在线性同余生成器和加密函数之间,有一种哈希函数可以将线性计数转换为可接受的伪随机数。
如果您恰好有128位整数类型(例如,在64位目标上构建时,GCC中的__int128),或者愿意手动实现这样的长乘法,则可以扩展
SplitMix64中使用的构造。我进行了相当肤浅的搜索,并得出了以下参数:
uint128_t mix(uint128_t x) {
uint128_t m0 = (uint128_t)0xecfb1b9bc1f0564f << 64
| 0xc68dd22b9302d18d;
uint128_t m1 = (uint128_t)0x4a4cf0348b717188 << 64
| 0xe2aead7d60f8a0df;
x ^= x >> 59;
x *= m0;
x ^= x >> 60;
x *= m1;
x ^= x >> 84;
return x;
}
以及它的反函数:
uint128_t unmix(uint128_t x) {
uint128_t im0 = (uint128_t)0x367ce11aef44b547 << 64
| 0x424b0c012b51d945;
uint128_t im1 = (uint128_t)0xef0323293e8f059d << 64
| 0x351690f213b31b1f;
x ^= x >> 84;
x *= im1;
x ^= x >> 60 ^ x >> (2 * 60);
x *= im0;
x ^= x >> 59 ^ x >> (2 * 59);
return x;
}
我不确定你是想要一个随机序列,还是想要一种混淆任意时间戳的方法(因为你说你想要解码这些值,所以它们必须比线性计数器更有趣),但是一个可以简单地从另一个中推导出来:
uint128_t encode(uint128_t time, uint128_t salt) {
return mix((time + 1) * salt);
}
uint128_t generate(uint128_t salt) {
static uint128_t t = 0;
return encode(t++, salt);
}
static uint128_t inv(uint128_t d) {
uint128_t i = d;
while (i * d != 1) {
i *= 2 - i * d;
}
return i;
}
uint128_t decode(uint128_t etime, uint128_t salt) {
return unmix(etime) * inv(salt) - 1;
}
请注意,
salt
会选择2的127次方个不重复的128位值序列(因为
salt
必须是奇数,所以我们失去了一位),但是可能生成的序列有2的128次方阶乘种。在其他地方,我正在考虑扩展参数化,以便可以访问更多这些序列,但我开始使用上述方法来增加序列的随机性,以隐藏参数可能选择的不太随机(但可证明是不同的)序列的任何问题。
显然,
uint128_t
不是标准类型,因此我的答案不是C语言,但您可以使用大数字库或编译器扩展使算术运算工作。为了清晰起见,我依赖于编译器扩展。所有操作都依赖于类似于C语言的无符号溢出行为(取任意精度结果的低位)。