【发布时间】:2020-08-20 04:35:44
【问题描述】:
为了在 C++ 中从泊松分布中抽取随机数,一般建议使用
RNG_type rng;
std::poisson_distribution<size_t> d(1e-6);
auto r = d(rng);
在每次调用 std::poisson_distribution 对象时,都会消耗整个随机位序列(例如,std::mt19937 为 32 位,std::mt19937_64 为 64 位)。令我震惊的是,在如此低的平均值 (mean = 1e-6) 下,绝大多数情况下,只有少数位足以确定要返回的值为 0。然后可以缓存其他位以供以后使用。
假设设置为真的位序列与泊松分布的高返回值相关联,当使用1e-6 的平均值时,任何不以 19 个真开始的序列都必然返回零!确实,
1 - 1/2^19 < P(0, 1e-6) < 1 - 1/2^20
,其中P(n, r) 表示从均值为r 的泊松分布中抽取n 的概率。不浪费比特的算法会使用一半的时间,四分之一的时间使用两个比特,八分之一的时间使用三个比特,......
有没有一种算法可以通过在绘制泊松数时消耗尽可能少的位来提高性能?当我们考虑低均值时,与std::poisson_distribution 相比,还有其他方法可以提高性能吗?
回应@Jarod42 的评论是谁说的
想知道如果使用更少的位不会破坏等概率...
我认为它不会破坏等概率性。在一次模糊的测试中,我用一个简单的伯努利分布考虑了同样的问题。我以1/2^4 的概率采样真,以1 - 1/2^4 的概率采样假。函数 drawWithoutWastingBits 在缓存中看到 true 后立即停止,函数 drawWastingBits 消耗 4 位,无论这些位是什么。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <random>
bool drawWithoutWastingBits(std::vector<bool>& cache, size_t& cache_index)
{
/*
Get a true with probability 1/2^4 (=1/16=0.0625) and a false otherwise
*/
size_t nbTrues = 0;
while (cache[cache_index])
{
++nbTrues;
++cache_index;
if (nbTrues == 4)
{
return true;
}
}
++cache_index;
return false;
}
bool drawWastingBits(std::vector<bool>& cache, size_t& cache_index)
{
/*
Get a true with probability 1/2^4 (=1/16=0.0625) and a false otherwise
*/
bool isAnyTrue = false;
for (size_t i = 0 ; i < 4; ++i)
{
if (cache[cache_index])
{
isAnyTrue = true;
}
++cache_index;
}
return !isAnyTrue;
}
int main()
{
/*
Just cache a lot of bits in advance in `cache`. The same sequence of bits will be used by both function.
I am just caching way enough bits to make sure they don't run out of bits below
I made sure to have the same number of zeros and ones so that any deviation is caused by the methodology and not by the RNG
*/
// Produce cache
std::vector<bool> cache;
size_t nbBitsToCache = 1e7;
cache.reserve(nbBitsToCache);
for (size_t i = 0 ; i < nbBitsToCache/2 ; ++i)
{
cache.push_back(false);
cache.push_back(true);
}
// Shuffle cache
{
std::mt19937 mt(std::random_device{}());
std::shuffle(cache.begin(), cache.end(), mt);
}
// Draw without wasting bits
{
size_t nbDraws = 1e6;
size_t cache_index = 0;
std::pair<size_t, size_t> outcomes = {0,0};
for (size_t r = 0 ; r < nbDraws ; ++r)
{
drawWithoutWastingBits(cache, cache_index) ? ++outcomes.first : ++outcomes.second;
assert(cache_index <= cache.size());
}
assert(outcomes.first + outcomes.second == nbDraws);
std::cout << "Draw Without Wasting Bits: prob true = " << (double)outcomes.first / nbDraws << "\n";
}
// Draw wasting bits
{
size_t nbDraws = 1e6;
size_t cache_index = 0;
std::pair<size_t, size_t> outcomes = {0,0};
for (size_t r = 0 ; r < nbDraws ; ++r)
{
drawWastingBits(cache, cache_index) ? ++outcomes.first : ++outcomes.second;
assert(cache_index <= cache.size());
}
assert(outcomes.first + outcomes.second == nbDraws);
std::cout << "Draw Wit Wasting Bits: prob true = " << (double)outcomes.first / nbDraws << "\n";
}
}
可能的输出
Draw Without Wasting Bits: prob true = 0.062832
Draw Wit Wasting Bits: prob true = 0.062363
【问题讨论】:
-
想知道如果使用更少的位不会破坏等概率......
-
@rustyx 这篇文章表明 Mersenne Twister 的表现优于 LCG(Talk wikipedia page 也是如此)。我正在使用
std::mt19937_64(我已经在缓存用于采样等概率布尔值的位)并且还没有真正尝试过任何 LCG 或 xorshift 或任何其他可能更快的 RNG。在所有情况下,虽然随机数的产生很慢,但std::poisson_disribution本身也很慢。我希望一旦它知道平均值非常低,这也可以改善。 -
平均值在应用程序中是固定值吗?
-
在一般意义上谈论
std::poisson_disribution毫无意义,因为它是由实现定义的。据我们所知,可以有一个按照您建议的方式执行的实现。我会检查它是如何在不同的工具链中实现的(boost 也有)。 -
@PeterO。是的,平均值是一个固定值。
标签: c++ performance random probability poisson