Эй, я хотел бы сделать это максимально быстро, потому что это называют МНОГО в программе, которую я пишу, так там любой более быстрый способ инициализировать вектор C++ к случайным значениям, чем:
double range;//set to the range of a particular function i want to evaluate.
std::vector<double> x(30, 0.0);
for (int i=0;i<x.size();i++) {
x.at(i) = (rand()/(double)RAND_MAX)*range;
}
Править: Инициализатор фиксированного x.
Сейчас это должно быть действительно быстрым, поскольку цикл не будет выполняться.
Лично я, вероятно, использовал бы что-то вроде этого:
struct gen_rand {
double range;
public:
gen_rand(double r=1.0) : range(r) {}
double operator()() {
return (rand()/(double)RAND_MAX) * range;
}
};
std::vector<double> x(num_items);
std::generate_n(x.begin(), num_items, gen_rand());
Изменить: это чисто микрооптимизация, которая может вообще не иметь никакого значения, но вы можете подумать о том, чтобы изменить порядок вычислений, чтобы получить что-то вроде:
struct gen_rand {
double factor;
public:
gen_rand(double r=1.0) : factor(range/RAND_MAX) {}
double operator()() {
return rand() * factor;
}
};
Конечно , есть большая вероятность, что компилятор уже сделает это (или что-то подобное), но все равно не помешает попробовать (хотя на самом деле это поможет только с отключенной оптимизацией).
Edit2: "sbi" (как обычно) правильно: вы можете немного выиграть, сначала зарезервировав место, а затем используя итератор вставки для размещения данных:
std::vector<double> x;
x.reserve(num_items);
std::generate_n(std::back_inserter(x), num_items, gen_rand());
Как и раньше, мы в такая микроскопическая оптимизация, я вовсе не уверен, что действительно ожидал вообще увидеть разницу. В частности, поскольку все это делается с помощью шаблонов, велика вероятность того, что большая часть (если не весь) код будет сгенерирована встроенно. В этом случае оптимизатор, вероятно, заметит, что все исходные данные будут перезаписаны, и пропустит их инициализацию.
В конце концов, однако, почти единственная часть, которая действительно , вероятно, будет иметь существенное значение, - это избавиться от .at (i)
. Остальные могли бы , но с включенной оптимизацией я бы не ожидал этого.
#include <iostream>
#include <vector>
#include <algorithm>
struct functor {
functor(double v):val(v) {}
double operator()() const {
return (rand()/(double)RAND_MAX)*val;
}
private:
double val;
};
int main(int argc, const char** argv) {
const int size = 10;
const double range = 3.0f;
std::vector<double> dvec;
std::generate_n(std::back_inserter(dvec), size, functor(range));
// print all
std::copy(dvec.begin(), dvec.end(), (std::ostream_iterator<double>(std::cout, "\n")));
return 0;
}
опоздал :(
Вы можете рассмотреть возможность использования генератора псевдослучайных чисел, который выдает выходные данные в виде последовательности. Поскольку большинство PRNG в любом случае просто предоставляют последовательность, это будет намного эффективнее, чем простой вызов rand () снова и снова.
Но тогда, думаю, мне действительно нужно больше узнать о вашей ситуации.
Ответ @Jerry Coffin выглядит очень хорошо. Однако две другие мысли:
Встраивание - весь ваш доступ к векторам будет очень быстрым, но если вызов rand () находится вне очереди, накладные расходы на вызов функции могут преобладать. В этом случае вам может потребоваться запустить собственный генератор псевдослучайных чисел .
SIMD - Если вы собираетесь использовать собственный ГПСЧ, вы можете заставить его вычислять 2 двойных (или 4 числа с плавающей запятой) одновременно. Это уменьшит количество преобразований int-to-float, а также умножений. Я никогда не пробовал, но, видимо, есть SIMD-версия Mersenne Twister, которая неплохая. Простой линейный конгруэнтный генератор тоже может быть достаточно хорош (и, вероятно, это то, что rand () уже использует).
Да, тогда как x.at (i) выполняет проверку границ, x [i] этого не делает. Кроме того, ваш код неверен, поскольку вы не указали размер x заранее. Вам нужно использовать std :: vector
, где n - количество элементов, которые вы хотите использовать; в противном случае ваш цикл никогда не будет выполнен.
В качестве альтернативы вы можете захотеть создать собственный итератор для генерации случайных значений и заполнения их с помощью итератора; потому что конструктор std :: vector в любом случае инициализирует свои элементы, поэтому, если у вас есть собственный класс итератора, который генерирует случайные значения, вы можете исключить проход по элементам.
Что касается реализации собственного итератора , вот мой непроверенный код:
class random_iterator
{
public:
typedef std::input_iterator_tag iterator_category;
typedef double value_type;
typedef int difference_type;
typedef double* pointer;
typedef double& reference;
random_iterator() : _range(1.0), _count(0) {}
random_iterator(double range, int count) :
_range(range), _count(count) {}
random_iterator(const random_iterator& o) :
_range(o._range), _count(o._count) {}
~random_iterator(){}
double operator*()const{ return ((rand()/(double)RAND_MAX) * _range); }
int operator-(const random_iterator& o)const{ return o._count-_count; }
random_iterator& operator++(){ _count--; return *this; }
random_iterator operator++(int){ random_iterator cpy(*this); _count--; return cpy; }
bool operator==(const random_iterator& o)const{ return _count==o._count; }
bool operator!=(const random_iterator& o)const{ return _count!=o._count; }
private:
double _range;
int _count;
};
С приведенным выше кодом можно будет использовать:
std::vector<double> x(random_iterator(range,number),random_iterator());
Тем не менее, код генерации для другое приведенное решение проще, и, честно говоря, я бы просто явно заполнил вектор, не прибегая к чему-то вроде этого ... но об этом круто подумать.
Я думаю об этом как о подходе «резина встречает дорогу».
Другими словами, должны произойти определенные минимальные вещи, которые нельзя обойти, например:
функция rand ()
должна быть вызвана N раз.
результат rand ()
должен быть преобразован в удвоение, а затем умножен на что-то.
полученные числа должны быть сохранены в последовательных элементах массива.
Как минимум, цель состоит в том, чтобы выполнить эти вещи.
Другие проблемы, например, использовать ли std :: vector
и итераторы, все в порядке, если они не добавляют дополнительных циклов.
Самый простой способ узнать, добавляют ли они значительные дополнительные циклы, - это пошагово выполнить код на уровне языка ассемблера.