高效的比特重新映射算法

时间:2012-09-11 13:05:42

标签: c# random mapping bit-manipulation shuffle

我有一个用例,我需要以这样的方式加扰输入:

  1. 每个特定输入始终映射到特定的伪随机输出。
  2. 输出必须充分改变输入,以便递增输入映射到伪随机输出。
  3. 例如,如果输入是64位,则必须有2 ^ 64个唯一输出,这些输出必须尽可能地减少递增输入(任意要求)。

    我将在C#中编写代码,但只要没有SIMD内在函数,就可以从Java或C进行转换。我正在寻找的是一些已经存在的代码,而不是重新发明轮子。

    我查看了Google,但没有发现任何执行1:1映射的内容。

2 个答案:

答案 0 :(得分:1)

从我的头顶开始:

  1. 移动输入:确保保留每一位,即在不同方向上使用两个移位操作并将结果一起移动。

  2. 应用静态异或。

  3. 我想到的其他一切都不是双射的。然而,寻找双射可能会带来一些有用的东西; D

答案 1 :(得分:1)

这似乎运作得相当好:

const long multiplier = 6364136223846793005;
const long mulinv_multiplier = -4568919932995229531;
const long offset = 1442695040888963407;

static long Forward(long x)
{
    return x * multiplier + offset;
}

static long Reverse(long x)
{
    return (x - offset) * mulinv_multiplier;
}

只要multiplier为奇数,mulinv_multiplier是模数乘法逆(参见wiki:modular multiplicative inverse或黑客喜悦10-15精确除以常数),就可以将常数更改为任何值。 multiplier(模2 ^ 64,显然 - 这就是为什么multiplier必须是奇数,否则它没有反转。

偏移可以是任何东西,但为了安全起见,使其成为2 ^ 64的相对素数。

这些特定常数来自Knuths线性同余生成器。

有一件小事:它将输入的LSB的补码放在结果的LSB中。如果这是一个问题,你可以将其旋转任何非零数量。


对于32位,常量可以是multiplier = 0x4c957f2doffset = 0xf767814fmulinv_multiplier = 0x329e28a5

对于64位,multiplier = 12790229573962758597mulinv_multiplier = 16500474117902441741可能效果更好。


或者,对于CRC64,您可以使用CRC reversible(即输入与CRC的大小相同),当然需要进行一些修改。