C# 使用移位和加法对64位整数进行模7运算(32位有效,但64位无效';t)

C# 使用移位和加法对64位整数进行模7运算(32位有效,但64位无效';t),c#,math,optimization,bit-manipulation,modulo,C#,Math,Optimization,Bit Manipulation,Modulo,我有一个工作实现来计算32位无符号整数的模7,但我在64位实现上遇到了问题。32位的实现来自(有一些bug修复)。我能够得到适用于模3、模5、模15和模6的64位版本,但不适用于模7。这道数学题我有点搞不懂 供参考 以下是32位工作模式: static public uint Mersenne7(uint a) { a = (a >> 24) + (a & 0xFFFFFF); // sum base 2**24 digits a = (a >>

我有一个工作实现来计算32位无符号整数的模7,但我在64位实现上遇到了问题。32位的实现来自(有一些bug修复)。我能够得到适用于模3、模5、模15和模6的64位版本,但不适用于模7。这道数学题我有点搞不懂

供参考

以下是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;
}
我做了看起来很明显的扩展,它适用于模3、5和15,但对于模7,结果到处都是,没有明显的模式(除了结果都在7以下):

64位的相同技术显然不适用于mod 7。我一直在尝试一些变化,但我没有得到任何明显的改善,我不知道如何系统地工作

我已经进行了基准测试并证明,在我的环境中,使用shift和add计算Mersenne数的模比内置的模运算符更快,并且这是在热路径(索引到静态大小的循环缓冲区)中的紧密循环中运行的。这些较小的值除数也比较大的缓冲区大小更常见


这背后的数学原理其实很简单

(注意,对于数学部分,我用
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

然后

但是
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;
}
注意最终比较中值的变化,以及最终总和行的删除

有了这些变化,我认为unit和ulong版本都应该产生正确的结果。(但尚未测试)

我复制了第二次收缩——我不确定是否真的需要它。(这是为了处理溢出-但可能不会发生这种情况,您需要尝试一些值来检查)


在ulong案例中,您将需要
a=(a>>48)+a&0xffffffffffffffffl
行,就像您已经实现的一样。

您是对的,我设法错过了运行mod 7 32位的测试用例。您的更正似乎也有一些错误:14%7=7、7、21和28。4294967286%7=2(应该是1)和多个大于等于2的数字,当它们应该是1Ah时,
a>7
应该是
a>=7
,这将修复较低的数字。应该是a-7而不是a-6。希望这也能解决大量的问题。太好了!那个博客的作者有不少错误。感谢你的解释,我能够推导出形式为2^n-1的其他除数的模,因为@BenVoigt-公平点-添加了一个免责声明,即数学部分是数学,而不是代码。。。
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;
}