2012-06-02 3 views
0

У меня есть система частиц, которые, как правило, создают новые частицы, обновляют их и разрушает ...Как эффективно управлять функциями распределения вероятностей?

В модуле эмиттера есть цикл, который сбрасывает частицу:

foreach p in particles 
    p.position = rand() 
    p.velocity = rand() 

обычно при использовании рандов языка C (), мы получаем равномерное распределение, но что, когда я хотел бы использовать какое-то другое распределение (например, гауссовское)?

Как изменить этот код так, чтобы он обрабатывал несколько (или, по крайней мере, два) разных способа генерации параметров новых частиц?

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

или, может быть, я не должен заботиться и просто написать:

foreach p in particles 
    p.position = useGaussian ? gausRand() : UniRand() 
    p.velocity = useGaussian ? gausRand() : UniRand() 

мы можем сократить количество различного распределения и использовать только два или три из них ...

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

Я хотел бы получить некоторые общие рекомендации по этому вопросу.

+0

Профилировали ли вы этот код, чтобы определить, что накладные расходы виртуального вызова являются проблемой? –

+0

Я не профиль, но я хотел получить некоторые общие мысли по этому делу. Когда есть тысячи частиц, вызов виртуального метода имеет значение, я думаю. – fen

+1

Общие мысли состоят в том, что вы не должны оптимизировать, пока профилирование не решит проблему. –

ответ

3

В то время как ответ @ gavinb является вполне допустимым способом, я предлагаю не изобретать колесо и идти со стандартными средствами: если у вас есть поддержка C++ 11, используйте std::normal_distribution и его родственников (см., Например, C++ TR1: how to use the normal_distribution?). В противном случае используйте boost::random library.

Поскольку они предназначены только для заголовка (по крайней мере, для версии с ускорением), в этом случае полиморфных вызовов не требуется, поэтому вам не нужно беспокоиться о них. Это, конечно, не устраняет предельную актуальность рекомендаций @Oli Charlesworth.

РЕДАКТИРОВАТЬ: если накладные расходы из-за полиморфных вызовов не являются незначительными, вы всегда можете создавать свои функции по перечисленным типам распределений и специализировать их по мере необходимости.

В двух словах, это так просто, как это:

#include<iostream> 

// template on an int selector 
template<int N> void foo(){ std::cout<<"42\n"; } 
template<> void foo<1>() {std::cout<<"1\n";} 

//now use an enum 
enum distr_types {UNIF, NORMAL, UNKNOWN}; 
template<distr_types T> void bar() {std::cout<<"fourty two\n";} 
template<> void bar<UNIF>() {std::cout<<"UNIF\n";} 
template<> void bar<NORMAL>(){std::cout<<"NORMAL\n";} 

int main(){ 
    foo<3>(); 
    foo<1>(); 

    bar<UNIF>(); 
    bar<NORMAL>(); 
    bar<UNKNOWN>(); 
} 

Но если вы окажетесь делать вещи такого рода, это стоит иметь взгляд на одном из good C++ books.

+0

Очень хорошая точка - кататься самостоятельно, как я описал, лучше всего делать, только если вы не можете добавить дополнительные зависимости. Использование 'std :: normal_distribution' или' boost :: random' должно, вероятно, быть первым рассмотренным вариантом. +1 :) – gavinb

+0

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

+0

@fen: см. Мой отредактированный ответ –

3

Обычно при использовании функции С rand() мы получаем равномерное распределение, но что, когда я хотел бы использовать другое распределение (например, гауссовское)?

The Box Muller Transform очень умный алгоритм, который использует тригонометрические функции для генерации случайных чисел из гауссовского распределения с использованием равномерного распределения в качестве входных данных (то есть. Используя rand()). Вы указываете среднее и стандартное отклонение и вызываете эту функцию для генерации новых вариаций. Единственный недостаток заключается в том, что он дороже, чем просто вызов rand, так как он также вызывает sin() и cos() (хотя и только каждый второй вызов).

Как изменить этот код так, чтобы он обрабатывал несколько (или не менее двух) разных способов генерации параметров новых частиц?

Предлагаю вам начать с подхода RandomGenerator и виртуальных методов. Это будет проще всего поддерживать. Начните с простейшего подхода и профиля, прежде чем пытаться оптимизировать ситуацию.

Учитывая вычислительную сложность генерации случайных чисел , стоимость создания вариации будет намного превышать накладные расходы на вызов виртуального метода по сравнению с вызовом статической функции.

Если это действительно действительно не так быстро, вы всегда можете создать пул случайных чисел, генерируя больше в фоновом режиме по мере необходимости.