我有一个粒子系统,它通常创建新粒子,更新它们并破坏......
在发射器模块中,有重置粒子的 for 循环:
foreach p in particles
p.position = rand()
p.velocity = rand()
通常当使用 C 的 rand() 函数时,我们会得到均匀的分布,但是当我想使用其他一些分布(例如高斯)时呢?
如何更改该代码,以便它能够处理几种(或至少两种)生成新粒子参数的不同方式?
当然,你可以创建一些对象:比如RandomGenerator,并使用一些虚拟函数调用来处理这些不同的行为。但是这段代码应该非常快(当更新数千个粒子时),所以我认为使用虚函数并不好。
或者也许我不应该关心,只是写:
foreach p in particles
p.position = useGaussian ? gausRand() : UniRand()
p.velocity = useGaussian ? gausRand() : UniRand()
我们可以缩小不同分布的数量,只使用其中的两三个......
请注意,我的示例非常简单,但在实际代码中,您有几个粒子参数配置。
我想就这个问题得到一些一般性建议。
虽然@gavinb的答案是一个完全有效的方法,但我建议避免重新发明轮子并使用标准设施:如果您有 c++11 支持,请使用 std::normal_distribution
及其亲戚(例如,请参阅 C++ TR1:如何使用normal_distribution?否则,请使用boost::random
库。
由于这些是仅标头的(至少是 boost 版本),因此不涉及多态调用,因此您不必担心它们。当然,这并不能消除@Oli查尔斯沃思的建议的最大相关性。
编辑:如果多态调用引起的开销不可忽略,则始终可以在枚举类型的分布上模板化函数,并根据需要对其进行专用化。
简而言之,它就是这样简单:
#include<iostream>
// template on an int selector
template<int N> void foo(){ std::cout<<"42n"; }
template<> void foo<1>() {std::cout<<"1n";}
//now use an enum
enum distr_types {UNIF, NORMAL, UNKNOWN};
template<distr_types T> void bar() {std::cout<<"fourty twon";}
template<> void bar<UNIF>() {std::cout<<"UNIFn";}
template<> void bar<NORMAL>(){std::cout<<"NORMALn";}
int main(){
foo<3>();
foo<1>();
bar<UNIF>();
bar<NORMAL>();
bar<UNKNOWN>();
}
但是,如果你发现自己在做这种事情,那么值得一看一本C++好的书。
通常,当使用 C 的 rand() 函数时,我们会得到均匀的分布,但是当我想使用其他一些分布(例如高斯)时呢?
盒穆勒变换是一种非常聪明的算法,它使用三角函数从高斯分布生成随机数,使用均匀分布作为输入(即使用 rand()
)。 您可以指定均值和标准差,并调用此函数以生成新的变量。 唯一的缺点是它比简单地调用rand
更昂贵,因为它也调用sin()
和cos()
(尽管只有每秒调用一次)。
如何更改该代码,以便它能够处理几种(或至少两种)生成新粒子参数的不同方式?
我建议你从RandomGenerator
和虚拟方法开始。 这将是最容易维护的。 在尝试优化之前,先从最简单的方法和配置文件开始。
考虑到生成随机数的计算复杂性,生成变量的成本将远远超过虚拟方法调用与静态函数调用的开销。
如果这真的不够快,你总是可以生成一个随机数池,根据需要在后台生成更多。