1至15之间的随机数

时间:2019-07-10 23:17:44

标签: c++ random bit-manipulation

在C ++中,我生成许多随机数,这些随机数需要在1到15(包括)之间。当然,我可以产生 git reset --hard 但这是浪费,因为此mersenn扭曲器生成32位(或使用mt19937_64甚至生成64位)随机值,而我只保留4位并丢弃所有其余值,就我而言,性能是一个问题并且生成随机数是一个重要的贡献者。

因此,我的想法是生成例如0到2 ^ 64-1之间的单个64位随机值,并从中选择4位。问题是我找不到在1到15之间生成值的方法。 示例:

std::uniform_int_distribution<std::mt19937::result_type> random(1, 15);

这里,此版本当然不起作用:尽管+1,生成的值仍在0到15之间(因为如果unsigned long long int r = uniform(generator); // between 0 and 2^64-1 unsigned int r1 = (r+1)&15; // first desired random value unsigned int r2 = ((r>>4)+1)&15; //second desired random value unsigned int r3 = ((r>>8)+1)&15; //third desired random value ... 恰好是r&15,则加1会产生结果0xb1111

此外,我希望分布保持一致(例如,我不想使最低有效位更频繁地出现,例如0xb0000就是这种情况,因为值{ {1}}会发生两次)。

2 个答案:

答案 0 :(得分:3)

代替

std::mt19937 gen(seed);
std::uniform_int_distribution<> dis(1, 15);

auto r1 = dis(gen);
auto r2 = dis(gen);
auto r3 = dis(gen);
auto r4 = dis(gen);

您可以这样做:

std::mt19937 gen(seed);
std::uniform_int_distribution<> dis(0, 15 * 15 * 15 * 15 - 1); // Assuming int at least 16 bits

auto r = dis(gen);

auto r1 = r % 15 + 1; r /= 15;
auto r2 = r % 15 + 1; r /= 15;
auto r3 = r % 15 + 1; r /= 15;
auto r4 = r + 1;

Quick benchmark(第二个版本比第一个版本快2.5倍)

答案 1 :(得分:1)

David Schwartz在评论部分中提到

  

您可以简单地丢弃任何零值。平均每个64位随机输入可为您提供约15个随机值。

为此特定用例实施简单的拒绝采样技术,而不是使用更通用的std::uniform_int_distribution应该会更有效率(例如,参见Quick Bench将标准类与以下内容进行比较)。

class uniform_1_to_15_distribution
{
    uint64_t value_{};        // So that one could use std::mt19937_64 too
public:
    uniform_1_to_15_distribution() = default;

    template <class Gen> int operator() (Gen &g)
    {
        for (;;)
        {
            // When all the bits have been used (or only useless zeroes remain)
            if( value_ == uint64_t{} )
            {
                // Get a new value from the generator
                value_ = g();
                continue;
            }

            // Pick the 4 LS bits
            int ret = value_ & 0xF;

            // "Consume" the state
            value_ >>= 4;

            // Reject 0. Only value in range [1, 15] are accepted.
            if ( ret == 0 )
            {
                continue;
            }        
            return ret;
        }
    }
};

可以测试分发HERE