我有一个工作实现来计算32位无符号int的模7但我遇到了64位实现的问题。 32位实现来自this blog post(有一些bug修复)。我能够得到64位版本的模数3,5,15和6但不是7.数学有点过头了。
供参考,here is a gist with the full code。
这是工作32位:
static public uint Mersenne7(uint a)
{
a = (a >> 24) + (a & 0xFFFFFF); // sum base 2**24 digits
a = (a >> 12) + (a & 0xFFF); // sum base 2**12 digits
a = (a >> 6) + (a & 0x3F); // sum base 2**6 digits
a = (a >> 3) + (a & 0x7); // sum base 2**2 digits
a = (a >> 2) + (a & 0x7); // sum base 2**2 digits
if (a > 5) a = a - 6;
return a;
}
我做了一个看似明显的扩展,它适用于模5,5和15,但是对于mod 7,结果都是没有明显模式的地方(除了结果都在7以下):
static public ulong Mersenne7(ulong a)
{
a = (a >> 48) + (a & 0xFFFFFFFFFFFF); // sum base 2**48 digits
a = (a >> 24) + (a & 0xFFFFFF); // sum base 2**24 digits
a = (a >> 12) + (a & 0xFFF); // sum base 2**12 digits
a = (a >> 6) + (a & 0x3F); // sum base 2**6 digits
a = (a >> 3) + (a & 0x7); // sum base 2**2 digits
a = (a >> 2) + (a & 0x7); // sum base 2**2 digits
if (a > 5) a = a - 6;
return a;
}
64位的相同技术显然不适用于mod 7.我一直在尝试一些变化,但我没有得到任何明显更好的东西,我不知道如何系统地完成它。
我已经进行了基准测试并证明,使用shift和add for Mersenne数计算模数比我环境中的内置模数运算符快,并且这是在热路径中的紧密循环中运行(索引到静态大小循环缓冲区) )。这些较低值的除数也比较大的缓冲区大小更常见。
答案 0 :(得分:2)
这背后的数学实际上非常简单。
(注意数学部分I' m使用a^b
表示" a表示电源b"不是
" a xor b"。这些数学部分不应该是C#代码)
关键技巧是你将a
分成两部分,以便
a = b * 2^3 + c
其中b = a / 2^3 = a >> 3
和c = a mod 2^3 = a & 0x7
然后
a mod 7 = ((b mod 7) * (2^3 mod 7) + c ) mod 7
但是2^3 mod 7 = 1
所以
a mod 7 = ( b mod 7 + c ) mod 7 = (b + c) mod 7
我们使用
多次应用此技巧1 = 2^3 mod 7 = 2^6 mod 7 = 2^12 mod 7 = 2^24 mod 7 = 2^48 mod 7
考虑到这一点,它看起来像你的工作" Mersene7不起作用。
我想这个:
static public uint Mersenne7(uint a)
{
a = (a >> 24) + (a & 0xFFFFFF); // sum base 2**24 digits
a = (a >> 12) + (a & 0xFFF); // sum base 2**12 digits
a = (a >> 6) + (a & 0x3F); // sum base 2**6 digits
a = (a >> 3) + (a & 0x7); // sum base 2**2 digits
a = (a >> 2) + (a & 0x7); // sum base 2**2 digits
if (a > 5) a = a - 6;
return a;
}
应该是
static public uint Mersenne7(uint a)
{
a = (a >> 24) + (a & 0xFFFFFF); // sum base 2**24 digits
a = (a >> 12) + (a & 0xFFF); // sum base 2**12 digits
a = (a >> 6) + (a & 0x3F); // sum base 2**6 digits
a = (a >> 3) + (a & 0x7); // sum base 2**3 digits
a = (a >> 3) + (a & 0x7); // sum base 2**3 digits
if (a >= 7) a = a - 7;
return a;
}
注意最终比较中值的变化,以及最终总和线的删除。
通过这些更改,单位和ulong版本应该会产生正确的结果。 (但是,Haven未经过测试)
我已经复制了第二次收缩 - 我不确定它是否真的需要。 (它是为了处理溢出 - 但可能不会发生你需要尝试一些值来检查)
在ulong案例中,您需要a=(a>>48) + a & 0xFFFFFFFFFFFFL
行,就像您已经实施的那样。