模数除法返回负数

时间:2012-12-19 21:56:53

标签: c++ c visual-studio-2010 modulo

我正在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

模运算符是否对操作数的大小有一些限制?

4 个答案:

答案 0 :(得分:3)

pow(5, 15)的整数部分在int中无法表示(假设int的宽度为32位)。转换(从转换表达式中的doubleint)在C和C ++中是未定义的行为。

为避免未定义的行为,您应该使用fmod函数来执行浮点余数操作。

答案 1 :(得分:3)

5到15的功率是30,517,578,125

您可以在int中存储的最大值是2,147,483,647

您可以使用64位整数,但要注意最终从double转换时会出现精度问题。

从记忆中,数理论中有一个关于你正在进行的计算的规则,这意味着你不需要计算全功率扩展来确定模数结果。但我可能是错的。自从我学到这些东西以来已经太多年了。

啊,这是:Modular Exponentiation

阅读,然后停止使用doublepow =)

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可能更大,则需要使用更宽的类型进行计算。