我在C中实现了一个算法,它需要在无符号整数上快速进行模数加法和减法,并能正确处理溢出条件。这就是我现在所拥有的(它确实有效):
/* a and/or b may be greater than m */
uint32_t modadd_32(uint32_t a, uint32_t b, uint32_t m) {
uint32_t tmp;
if (b <= UINT32_MAX - a)
return (a + b) % m;
if (m <= (UINT32_MAX>>1))
return ((a % m) + (b % m)) % m;
tmp = a + b;
if (tmp > (uint32_t)(m * 2)) // m*2 must be truncated before compare
tmp -= m;
tmp -= m;
return tmp % m;
}
/* a and/or b may be greater than m */
uint32_t modsub_32(uint32_t a, uint32_t b, uint32_t m) {
uint32_t tmp;
if (a >= b)
return (a - b) % m;
tmp = (m - ((b - a) % m)); /* results in m when 0 is needed */
if (tmp == m)
return 0;
return tmp;
}
有人知道更好的算法吗?我发现模块化算法的库似乎都适用于大的任意精度数,这种方法有点过分。
编辑:我希望这能在32位机器上运行良好。此外,我现有的函数可以简单地转换为其他大小的无符号整数,这是一个很好的保留属性。
答案 0 :(得分:9)
模块化操作通常假设a和b小于m。这允许更简单的算法:
umod_t sub_mod(umod_t a, umod_t b, umod_t m)
{
if ( a>=b )
return a - b;
else
return m - b + a;
}
umod_t add_mod(umod_t a, umod_t b, umod_t m)
{
if ( 0==b ) return a;
// return sub_mod(a, m-b, m);
b = m - b;
if ( a>=b )
return a - b;
else
return m - b + a;
}
资料来源:Matters Computational,第39.1章。
答案 1 :(得分:1)
如果它适合,我会在uint32_t
中进行算术,否则在uint64_t
进行算术。
uint32_t modadd_32(uint32_t a, uint32_t b, uint32_t m) {
if (b <= UINT32_MAX - a)
return (a + b) % m;
else
return ((uint64_t)a + b) % m;
}
在具有64位整数类型的体系结构上,这应该几乎没有开销,您甚至可以考虑只在uint64_t
中执行所有操作。在合成uint64_t
的体系结构上
让编译器决定他认为最好的东西,然后查看生成的汇编程序并测量是否满意。
答案 2 :(得分:0)
溢出安全的模块化添加
首先使用通常a<m
确定b<m
和% m
。
添加更新的a
和b
。
如果a
(或b
)超过uintN_t
总和,那么数学上的和是uintN_t
溢出和减去m
将“mod”在数学上总和到uintN_t
的范围。
如果总和超过m
,那么就像上面的步骤一样,m
的单个减法将“修改”总和。
uintN_t modadd_N(uintN_t a, uintN_t b, uintN_t m) {
// may omit these 2 steps if a < b and a < m are known before the call.
a %= m;
b %= m;
uintN_t sum = a + b;
if (sum >= m || sum < a) {
sum -= m;
}
return sum;
}
最后非常简单。
溢出安全模块减法
@Evgeny Kluev良好答案的变化。
uintN_t modsub_N(uintN_t a, uintN_t b, uintN_t m) {
// may omit these 2 steps if a < b and a < m are known before the call.
a %= m;
b %= m;
uintN_t diff = a - b;
if (a < b) {
diff += m;
}
return diff;
}
请注意,此方法适用于各种N
,例如32, 64, 16
或unsigned
,unsigned long
等,而无需使用更广泛的类型。它也适用于比int/unsigned
窄的无符号类型。