我的矢量化xorshift +不是很随机

时间:2017-07-02 14:13:57

标签: c++ sse avx

我有以下代码(http://plnkr.co/edit/kTe4jKUnypfe6SbDECHi?p=preview已修改为使用矢量类型):

body

输出

#include <immintrin.h>
#include <climits>

__v8si rand_si() {
    static auto s0 = __v4du{4, 8, 15, 16},
        s1 = __v4du{23, 34, 42, 69};
    auto x = s0, y = s1;
    s0 = y;
    x ^= x << 23;
    s1 = x ^ y ^ (x >> 17) ^ (y >> 26);
    return (__v8si)(s1 + y);
}

#include <iostream>
#include <iomanip>
void foo() {
    //Shuffle a bit. The result is much worse without this.
    rand_si(); rand_si(); rand_si(); rand_si();
    auto val = rand_si();

    for (auto it = reinterpret_cast<int*>(&val);
         it != reinterpret_cast<int*>(&val + 1);
         ++it)
        std::cout << std::hex << std::setfill('0') << std::setw(8) << *it << ' ';
    std::cout << '\n';
}

其他每个数字都很小,没有一个具有前导位设置。另一方面,使用xorshift *代替:

09e2a657 000b8020 1504cc3b 00110040 1360ff2b 00150078 2a9998b7 00228080

我得到了更好的输出

__v8si rand_si() {
    static auto x = __v4du{4, 8, 15, 16};
    x ^= x >> 12;
    x ^= x << 25;
    x ^= x >> 27;
    return x * (__v4du)_mm256_set1_epi64x(0x2545F4914F6CDD1D);
}

但根据维基百科的说法,xorshift +是一个很好的PRNG,并且比xorshift *产生更好的伪随机性。那么,我的RNG代码中是否有错误,或者我使用错了?

3 个答案:

答案 0 :(得分:3)

我认为你不应该通过查看它生成的8个数字来判断一个随机生成器。此外,发电机通常需要良好的播种(你的播种可能被认为是糟糕的 - 你的种子几乎都是从零开始。仅仅调用rand_si()几次就不足以让这些比特传播&#34;)

因此,我建议您使用适当的播种(例如,一个简单的解决方案是再次呼叫rand_si())。

xorshift*因为最终的乘法而表现得更好,所以由于播种不足,它不容易发现不良行为。

提示:将您的代码生成的数字与原始实现进行比较。这样您就可以确保您的实施是正确的。

答案 1 :(得分:2)

格扎的答案是完全正确的,播种是罪魁祸首。使用标准的64位PRNG来播种它会更好:

void seed(uint64_t s) {
    std::mt19937_64 e(s);
    s0 = __v4du{e(), e(), e(), e()};
    s1 = __v4du{e(), e(), e(), e()};
}

答案 2 :(得分:1)

以上两个人都错了。即使初始基数(种子)为1、2、3,...和其他最简单的值,xorshift +生成器也可以正常工作。生成器仅对零值种子失败。检查您的64位变量表示形式和二进制运算符的正确工作。