我正在C程序中执行以下模数除法运算:
(5 ^ 6)mod 23 = 8
(5 ^ 15)mod 23 = 19
为方便起见,我使用以下功能:
int mod_func(int p, int g, int x) {
return ((int)pow((double)g, (double)x)) % p;
}
但调用函数时的操作结果不正确:
mod_func(23, 5, 6) //returns 8
mod_func(23, 5, 15) //returns -6
模运算符是否对操作数的大小有一些限制?
答案 0 :(得分:3)
pow(5, 15)
的整数部分在int
中无法表示(假设int
的宽度为32位)。转换(从转换表达式中的double
到int
)在C和C ++中是未定义的行为。
为避免未定义的行为,您应该使用fmod
函数来执行浮点余数操作。
答案 1 :(得分:3)
5到15的功率是30,517,578,125
您可以在int
中存储的最大值是2,147,483,647
您可以使用64位整数,但要注意最终从double
转换时会出现精度问题。
从记忆中,数理论中有一个关于你正在进行的计算的规则,这意味着你不需要计算全功率扩展来确定模数结果。但我可能是错的。自从我学到这些东西以来已经太多年了。
阅读,然后停止使用double
和pow
=)
int mod_func(int p, int g, int x)
{
int r = g;
for( int i = 1; i < x; i++ ) {
r = (r * g) % p;
}
return r;
}
答案 2 :(得分:1)
我的猜测是问题是5 ^ 15 = 30517578125,它大于INT_MAX
(2147483647)。您目前正在将其投射到int
,这就是失败的原因。
答案 3 :(得分:0)
如前所述,你的第一个问题是
int mod_func(int p, int g, int x) {
return ((int)pow((double)g, (double)x)) % p;
}
pow(g,x)
经常超出int
范围,然后您有未定义的行为将结果转换为int
,无论结果int
是什么,都没有有理由相信它与所需的模数有关。
下一个问题是pow(g,x)
作为double
的结果可能不准确。除非g
是2的幂,否则对于足够大的指数,数学结果不能精确地表示为double
,即使它在范围内,但如果数学结果可以准确表示,它也可能发生(取决于pow
)的实现。
如果你进行数论计算 - 并且计算一个整数的幂的余数是一个 - 你应该只使用整数类型。
对于手头的情况,您可以通过重复平方使用取幂,计算所有中间结果的残差。如果模数p
足够小,(p-1)*(p-1)
永不溢出,
int mod_func(int p, int g, int x) {
int aux = 1;
g %= p;
while(x > 0) {
if (x % 2 == 1) {
aux = (aux * g) % p;
}
g = (g * g) % p;
x /= 2;
}
return aux;
}
做到了。如果p
可能更大,则需要使用更宽的类型进行计算。