Подтвердить что ты не робот

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

Эй, id хотел бы сделать это как можно быстрее, потому что он получил название LOT в программе, которую я пишу, так что есть ли более быстрый способ инициализировать вектор С++ для случайных значений, чем:

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;
}

EDIT: Исправлен x инициализатор.

4b9b3361

Ответ 1

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

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

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

Ответ 2

Я использую метод функтора Джерри Коффина в течение некоторого времени, но с приходом С++ 11 у нас есть множество классных новых функций случайных чисел. Чтобы заполнить массив со случайными значениями float, мы можем теперь сделать что-то вроде следующего.,

const size_t elements = 300;
std::vector<float> y(elements);    
std::uniform_real_distribution<float> distribution(0.0f, 2.0f); //Values between 0 and 2
std::mt19937 engine; // Mersenne twister MT19937
auto generator = std::bind(distribution, engine);
std::generate_n(y.begin(), elements, generator); 

См. соответствующий раздел Wikipedia для большего количества движков и дистрибутивов

Ответ 3

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

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

Что касается реализации iterator, вот мой непроверенный код:

 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());

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

Ответ 4

#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;
}

опоздал: (

Ответ 5

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

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

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

Ответ 6

@Ответ Джерри Коффина выглядит очень хорошо. Две другие мысли:

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

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

Ответ 7

int main() {
  int size = 10;
  srand(time(NULL));
  std::vector<int> vec(size);
  std::generate(vec.begin(), vec.end(), rand);

  std::vector<int> vec_2(size);
  std::generate(vec_2.begin(), vec_2.end(), [](){ return rand() % 50;})
}

Вам нужно включить вектор, алгоритм, время, cstdlib.

Ответ 8

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

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

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

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

Объект, как минимум, должен выполнить это.

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