Инициализация вектора C++ к случайным значениям … быстро

Эй, я хотел бы сделать это максимально быстро, потому что это называют МНОГО в программе, которую я пишу, так там любой более быстрый способ инициализировать вектор 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.

12
задан Flamewires 18 May 2010 в 20:13
поделиться

6 ответов

Сейчас это должно быть действительно быстрым, поскольку цикл не будет выполняться.

Лично я, вероятно, использовал бы что-то вроде этого:

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) . Остальные могли бы , но с включенной оптимизацией я бы не ожидал этого.

17
ответ дан 2 December 2019 в 03:38
поделиться
#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;
}

опоздал :(

3
ответ дан 2 December 2019 в 03:38
поделиться

Вы можете рассмотреть возможность использования генератора псевдослучайных чисел, который выдает выходные данные в виде последовательности. Поскольку большинство PRNG в любом случае просто предоставляют последовательность, это будет намного эффективнее, чем простой вызов rand () снова и снова.

Но тогда, думаю, мне действительно нужно больше узнать о вашей ситуации.

  • Почему этот фрагмент кода так много выполняется? Можете ли вы реструктурировать свой код, чтобы избежать повторной генерации случайных данных так часто?
  • Насколько велики ваши векторы?
  • Насколько «хорош» должен быть ваш генератор случайных чисел? Высококачественные дистрибутивы, как правило, дороже вычислять.
  • Если ваши векторы большие, вы повторно используете их буферное пространство или выбрасываете его и перераспределяете в другом месте? Создание новых векторов волей-неволей - отличный способ уничтожить ваш кеш.
2
ответ дан 2 December 2019 в 03:38
поделиться

Ответ @Jerry Coffin выглядит очень хорошо. Однако две другие мысли:

  1. Встраивание - весь ваш доступ к векторам будет очень быстрым, но если вызов rand () находится вне очереди, накладные расходы на вызов функции могут преобладать. В этом случае вам может потребоваться запустить собственный генератор псевдослучайных чисел .

  2. SIMD - Если вы собираетесь использовать собственный ГПСЧ, вы можете заставить его вычислять 2 двойных (или 4 числа с плавающей запятой) одновременно. Это уменьшит количество преобразований int-to-float, а также умножений. Я никогда не пробовал, но, видимо, есть SIMD-версия Mersenne Twister, которая неплохая. Простой линейный конгруэнтный генератор тоже может быть достаточно хорош (и, вероятно, это то, что rand () уже использует).

1
ответ дан 2 December 2019 в 03:38
поделиться

Да, тогда как x.at (i) выполняет проверку границ, x [i] этого не делает. Кроме того, ваш код неверен, поскольку вы не указали размер x заранее. Вам нужно использовать std :: vector x (n) , где 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());

Тем не менее, код генерации для другое приведенное решение проще, и, честно говоря, я бы просто явно заполнил вектор, не прибегая к чему-то вроде этого ... но об этом круто подумать.

5
ответ дан 2 December 2019 в 03:38
поделиться

Я думаю об этом как о подходе «резина встречает дорогу».
Другими словами, должны произойти определенные минимальные вещи, которые нельзя обойти, например:

  • функция rand () должна быть вызвана N раз.

  • результат rand () должен быть преобразован в удвоение, а затем умножен на что-то.

  • полученные числа должны быть сохранены в последовательных элементах массива.

Как минимум, цель состоит в том, чтобы выполнить эти вещи.

Другие проблемы, например, использовать ли std :: vector и итераторы, все в порядке, если они не добавляют дополнительных циклов. Самый простой способ узнать, добавляют ли они значительные дополнительные циклы, - это пошагово выполнить код на уровне языка ассемблера.

0
ответ дан 2 December 2019 в 03:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: