2010-05-18 2 views
12

Эй, id хотел сделать это как можно быстрее, потому что он получил название LOT в программе, которую я пишу, так же есть более быстрый способ инициализировать вектор 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; 
} 

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

+0

Правильный тип доступа к элементу данных вектора по индексу: 'std :: vector <...> :: size_type', а не' int'. – sbi

+0

Я полагаю, что обычные компиляторы будут перемещать «диапазон/RAND_MAX» из цикла в своих оптимизаторах? – sbi

+3

Любопытно узнать, действительно ли профайлер идентифицировал это как проблему. – Puppy

ответ

17

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

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

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

Edit: Это чисто микро-оптимизации, которая не может сделать никакой разницы, но вы могли бы рассмотреть переставляя вычисления, чтобы получить что-то вроде :

struct gen_rand { 
    double factor; 
public: 
    gen_rand(double r=1.0) : factor(range/RAND_MAX) {} 
    double operator()() { 
     return rand() * factor; 
    } 
}; 

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

Edit2: «ИЙ» (как это обычно бывает) права: вы могли бы получить немного, первоначально резервирование места, затем с помощью вставки итератора, чтобы поместить данные на место:

std::vector<double> x; 
x.reserve(num_items); 
std::generate_n(std::back_inserter(x), num_items, gen_rand()); 

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

В конце концов, однако, почти единственная часть, которая действительно , вероятно,, чтобы сделать существенную разницу, заключается в том, чтобы избавиться от .at(i).Остальные могут, но с включенными оптимизациями я бы не ожидал их.

+1

Нехорошо, я видел это первым. Я просто слишком медленный, ты избил меня. – wheaties

+5

Это все еще предварительно инициализирует вектор с значениями «0.0», которые затем перезаписываются. Не будет ли исключение «reserve()» и вставить итератор? – sbi

+0

@Wheaties: это должно быть первое - с моей (плохой) типизацией, это, как правило, наоборот! :-) –

5

Да, тогда как 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()); 

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

+0

Хорошо. Идея создания итератора, создающего случайные значения, просто классная. –

+0

О, извините, я только что написал его здесь, Его больше для концепции, но я исправлю это. Не могли бы вы привести мне пример или направить меня туда, где я могу прочитать о создании своего собственного итератора? Я довольно новичок в C++. Спасибо – Flamewires

+0

@Flamewires, да ... вам нужно создать класс, соответствующий одному из понятий итератора. См. Http://www.sgi.com/tech/stl/Iterators.html. Если вы довольно новичок в C++, тогда создание собственного итератора может быть не такой замечательной идеей на данный момент, поскольку оно включает в себя такие понятия, как «черты типа», «шаблоны», «специализация шаблонов», «понятия» и «шаблоны», перегрузка оператора ". Если вы знакомы с ними, тогда это может быть разумным делом. –

2

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

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

  • Почему эта часть кода выполнена так много? Можете ли вы перестроить свой код, чтобы избежать повторного генерации случайных данных так часто?
  • Сколько у вас векторов?
  • Как «хороший» ваш генератор случайных чисел должен быть? Высококачественные дистрибутивы, как правило, более дороги для расчета.
  • Если ваши векторы большие, вы повторно используете их буферное пространство, или вы бросаете его и перераспределяете его в другом месте? Создание новых векторов волей-неволей - отличный способ уничтожить ваш кеш.
3
#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; 
} 

:(

опоздал
1
ответ

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

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

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

0

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

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

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

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

Целью является, как минимум, добиться этих целей.

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

11

Я использую метод-функтор Джерри Коффина в течение некоторого времени, но с приходом C++ 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 для более двигателей и распределения

1
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.

Смежные вопросы