如何计算大数模数?


72

在不使用计算器的情况下如何计算5 ^ 55模数221的模数?

我猜想密码学中的数论中有一些简单的原理可以计算这些东西。


1
这是一个解释:devx.com/tips/Tip/39012
tur1ng 2010年

devx链接用处不大,数字理论中还有其他一些简单的方法,即AFAIK。
Priyank Bolia'2

1
@Priyank Bolia:不用担心,这个问题不太可能解决。这是一个好问题。如果关闭,将会有很多人投票要求重新开放。
杰森

4
是的,我们许多人都知道有时计算机科学涉及数学。
卡斯卡贝尔

13
@JB King:MathOverflow适用于研究生及更高级别的数学;这个问题将在那里皱眉。
杰森2010年

Answers:


100

好的,所以您要计算a^b mod m。首先,我们将采用幼稚的方法,然后看看如何对其进行改进。

首先,减少a mod m。也就是说,找到一个数字,a1使0 <= a1 < mand a = a1 mod m。然后在一个循环中反复乘以a1并再次减少mod m。因此,用伪代码:

a1 = a reduced mod m
p = 1
for(int i = 1; i <= b; i++) {
    p *= a1
    p = p reduced mod m
}

通过这样做,我们避免了大于的数字m^2。这是关键。之所以我们避免使用大于的数字,m^2是因为每一步0 <= p < m0 <= a1 < m

例如,让我们计算一下5^55 mod 221。首先,5已经减少了mod 221

  1. 1 * 5 = 5 mod 221
  2. 5 * 5 = 25 mod 221
  3. 25 * 5 = 125 mod 221
  4. 125 * 5 = 183 mod 221
  5. 183 * 5 = 31 mod 221
  6. 31 * 5 = 155 mod 221
  7. 155 * 5 = 112 mod 221
  8. 112 * 5 = 118 mod 221
  9. 118 * 5 = 148 mod 221
  10. 148 * 5 = 77 mod 221
  11. 77 * 5 = 164 mod 221
  12. 164 * 5 = 157 mod 221
  13. 157 * 5 = 122 mod 221
  14. 122 * 5 = 168 mod 221
  15. 168 * 5 = 177 mod 221
  16. 177 * 5 = 1 mod 221
  17. 1 * 5 = 5 mod 221
  18. 5 * 5 = 25 mod 221
  19. 25 * 5 = 125 mod 221
  20. 125 * 5 = 183 mod 221
  21. 183 * 5 = 31 mod 221
  22. 31 * 5 = 155 mod 221
  23. 155 * 5 = 112 mod 221
  24. 112 * 5 = 118 mod 221
  25. 118 * 5 = 148 mod 221
  26. 148 * 5 = 77 mod 221
  27. 77 * 5 = 164 mod 221
  28. 164 * 5 = 157 mod 221
  29. 157 * 5 = 122 mod 221
  30. 122 * 5 = 168 mod 221
  31. 168 * 5 = 177 mod 221
  32. 177 * 5 = 1 mod 221
  33. 1 * 5 = 5 mod 221
  34. 5 * 5 = 25 mod 221
  35. 25 * 5 = 125 mod 221
  36. 125 * 5 = 183 mod 221
  37. 183 * 5 = 31 mod 221
  38. 31 * 5 = 155 mod 221
  39. 155 * 5 = 112 mod 221
  40. 112 * 5 = 118 mod 221
  41. 118 * 5 = 148 mod 221
  42. 148 * 5 = 77 mod 221
  43. 77 * 5 = 164 mod 221
  44. 164 * 5 = 157 mod 221
  45. 157 * 5 = 122 mod 221
  46. 122 * 5 = 168 mod 221
  47. 168 * 5 = 177 mod 221
  48. 177 * 5 = 1 mod 221
  49. 1 * 5 = 5 mod 221
  50. 5 * 5 = 25 mod 221
  51. 25 * 5 = 125 mod 221
  52. 125 * 5 = 183 mod 221
  53. 183 * 5 = 31 mod 221
  54. 31 * 5 = 155 mod 221
  55. 155 * 5 = 112 mod 221

因此,5^55 = 112 mod 221

现在,我们可以通过平方求幂来改善此问题;这是著名的技巧,其中我们将幂运算减少到只需要log b乘法而不是乘法b。请注意,使用我上面描述的算法(通过平方改进来求幂),您最终得到了从右到左的二进制方法

a1 = a reduced mod m
p = 1
while (b > 0) {
     if (b is odd) {
         p *= a1
         p = p reduced mod m
     }
     b /= 2
     a1 = (a1 * a1) reduced mod m
}

因此,由于55 = 110111(二进制)

  1. 1 * (5^1 mod 221) = 5 mod 221
  2. 5 * (5^2 mod 221) = 125 mod 221
  3. 125 * (5^4 mod 221) = 112 mod 221
  4. 112 * (5^16 mod 221) = 112 mod 221
  5. 112 * (5^32 mod 221) = 112 mod 221

因此答案是5^55 = 112 mod 221。之所以有效,是因为

55 = 1 + 2 + 4 + 16 + 32

以便

5^55 = 5^(1 + 2 + 4 + 16 + 32) mod 221
     = 5^1 * 5^2 * 5^4 * 5^16 * 5^32 mod 221
     = 5 * 25 * 183 * 1 * 1 mod 221
     = 22875 mod 221
     = 112 mod 221

在我们计算5^1 mod 2215^2 mod 221等等的步骤中,我们注意到5^(2^k)=,5^(2^(k-1)) * 5^(2^(k-1))因为2^k = 2^(k-1) + 2^(k-1)这样我们可以首先计算5^1和归约mod 221,然后mod 221求平方并归约以获得5^2 mod 221等。

上面的算法使这个想法正式化。


1
好吧,大多数编程语言都为此内置了一个运算符。例如,在C衍生语言中,%运算符是模数运算符。因此,int p = 625 % 221将分配183p。您可以通过将实现相同的功能625221作为整数除法和得到了答案2。然后您拿走625 - 2 * 221剩下的。在这种情况下625 - 2 * 221 = 183,这就是答案。
杰森

2
是的,正如我在最后的段落中所述,您通过平方来求幂。
杰森

6
实际上,通过平方,您可以做的比求幂要好得多,尤其是在指数较大的情况下。请注意,您发现了5^16 == 1 (mod 221)。因此,5^k == 5^(k%16) (mod 221)
卡斯卡贝尔

2
@Jason:您已经写过:首先,减少mod m。这就是说,找到一个数字a1,使0 <= a1 <m和a = a1 mod m。看起来最后一个等式包含一个错字,不是应该是a1 = mod m吗?
Timofey

2
如果您只添加了“;”,@ Jason大部分情况下 (和其他几个字符)到你的伪代码,这将是C.
haneefmubarak

28

要添加到杰森的答案:

您可以使用指数的二进制扩展来加快处理速度(这对于非常大的指数可能会有帮助)。首先计算5、5 ^ 2、5 ^ 4、5 ^ 8 mod 221-您可以通过重复平方来实现:

 5^1 = 5(mod 221)
 5^2 = 5^2 (mod 221) = 25(mod 221)
 5^4 = (5^2)^2 = 25^2(mod 221) = 625 (mod 221) = 183(mod221)
 5^8 = (5^4)^2 = 183^2(mod 221) = 33489 (mod 221) = 118(mod 221)
5^16 = (5^8)^2 = 118^2(mod 221) = 13924 (mod 221) = 1(mod 221)
5^32 = (5^16)^2 = 1^2(mod 221) = 1(mod 221)

现在我们可以写

55 = 1 + 2 + 4 + 16 + 32

so 5^55 = 5^1 * 5^2 * 5^4 * 5^16 * 5^32 
        = 5   * 25  * 625 * 1    * 1 (mod 221)
        = 125 * 625 (mod 221)
        = 125 * 183 (mod 183) - because 625 = 183 (mod 221)
        = 22875 ( mod 221)
        = 112 (mod 221)

您可以看到对于非常大的指数,这将更快(我相信这是对数,而不是b中的线性,但不确定)。


6
这甚至是更好的解释
Priyank Bolia's 2010年

1
我怀疑实际上,通过平方避免幂运算实际上要快得多(一般而言),而是直接搜索指数最小的$ k $,使得$ 5 ^ k == 5(mod 221)$。当然,这确实取决于指数与模数的大小,但是一旦有了该指数,就只需要一个计算(指数mod k)和查找。请注意,如果您需要重复类似的计算,它肯定也更好。(您通常无法寻找$ a ^ k == 1(mod 221)$,因为这仅在$ a $和221相对质数时发生)
Cascabel 2010年

1
好吧,不,总的来说,找到具有该特性的最小指数要比平方乘运算慢得多。但是,如果您知道模数的因式分解,则可以轻松计算出carmichael lambda函数,它是k的一个复数。
总统詹姆斯·波尔克(James K. Polk)2010年

12
/* The algorithm is from the book "Discrete Mathematics and Its
   Applications 5th Edition" by Kenneth H. Rosen.
   (base^exp)%mod
*/

int modular(int base, unsigned int exp, unsigned int mod)
{
    int x = 1;
    int power = base % mod;

    for (int i = 0; i < sizeof(int) * 8; i++) {
        int least_sig_bit = 0x00000001 & (exp >> i);
        if (least_sig_bit)
            x = (x * power) % mod;
        power = (power * power) % mod;
    }

    return x;
}

1
x * power并且power * power在时会溢出mod*mod > UINT_MAX + 1
chux-恢复莫妮卡

是的@chux是正确的,即使在x * power和power * power期间,我们也应该采用mod。
jack_1729 18-10-30

@ jack_1729代码可以使用更宽的整数类型x * power来避免OF。如果没有可用的代码,则可以使用this
chux-恢复莫妮卡

3
5^55 mod221

= (   5^10         * 5^10         * 5^10         * 5^10          * 5^10          * 5^5) mod221    

= ( ( 5^10) mod221 * 5^10         * 5^10         * 5^10          * 5^10          * 5^5) mod221 

= (   77           * 5^10         * 5^10         * 5^10          * 5^10          * 5^5) mod221   

= ( ( 77           * 5^10) mod221 * 5^10         * 5^10          * 5^10          * 5^5) mod221 

= (   183                         * 5^10         * 5^10          * 5^10          * 5^5) mod221 

= ( ( 183                         * 5^10) mod221 * 5^10          * 5^10          * 5^5) mod221 

= (   168                                        * 5^10          * 5^10          * 5^5) mod221 

= ( ( 168                                        * 5^10) mod 221 * 5^10          * 5^5) mod221 

= (   118                                                        * 5^10          * 5^5) mod221 

= ( ( 118                                                        * 5^10) mod 221 * 5^5) mod221 

= (   25                                                                         * 5^5) mod221 

=     112

这比做幂运算慢吗?
Pacerier's


2

中国剩余定理的初始点为221 = 13 *17。因此,将其分解为两个部分,最后将它们组合在一起,一个用于mod 13,一个用于mod17。第二,我相信有一些证据对于所有非零a的a ^(p-1)= 1 mod p,这也有助于减少问题,因为对于13 * 4 = 52,对于mod 13情况,5 ^ 55变为5 ^ 3。如果您在“有限字段”主题下查看,可能会发现一些有关如何解决此问题的良好结果。

编辑:我提到这些因素的原因是,这创建了一种将零分解为非零元素的方法,就好像您尝试了类似13 ^ 2 * 17 ^ 4 mod 221的操作一样,由于13 * 17 = 221,答案为零。尽管有很多方法可以找到大质数,但许多大数将不是质数,因为它们在密码学和数学中的其他领域中已得到广泛使用。


好吧,我首先不知道阶乘,我正在尝试使用Miller Rabin算法证明数字是素数。所以,我在另一端。
Priyank Bolia'2

这里没有任何阶乘,但是有一个不同的阶乘。整数n的阶乘定义为所有小于n的正整数的乘积,例如2!= 2、3!= 6,等等,通常用!表示。符号。因式分解是不同的,并且没有用于表示正因式分解的整数的通用符号。
JB King

2

这是我为IBAN验证编写的代码的一部分。随时使用。

    static void Main(string[] args)
    {
        int modulo = 97;
        string input = Reverse("100020778788920323232343433");
        int result = 0;
        int lastRowValue = 1;

        for (int i = 0; i < input.Length; i++)
        {
            // Calculating the modulus of a large number Wikipedia http://en.wikipedia.org/wiki/International_Bank_Account_Number                                                                        
            if (i > 0)
            {
                lastRowValue = ModuloByDigits(lastRowValue, modulo);
            }
            result += lastRowValue * int.Parse(input[i].ToString());
        }
        result = result % modulo;
        Console.WriteLine(string.Format("Result: {0}", result));            
    }

    public static int ModuloByDigits(int previousValue, int modulo)
    {
        // Calculating the modulus of a large number Wikipedia http://en.wikipedia.org/wiki/International_Bank_Account_Number                        
        return ((previousValue * 10) % modulo);
    }
    public static string Reverse(string input)
    {
        char[] arr = input.ToCharArray();
        Array.Reverse(arr);
        return new string(arr);
    }

2

Jason用Java回答(注i < exp)。

private static void testModulus() {
    int bse = 5, exp = 55, mod = 221;

    int a1 = bse % mod;
    int p = 1;

    System.out.println("1. " + (p % mod) + " * " + bse + " = " + (p % mod) * bse + " mod " + mod);

    for (int i = 1; i < exp; i++) {
        p *= a1;
        System.out.println((i + 1) + ". " + (p % mod) + " * " + bse + " = " + ((p % mod) * bse) % mod + " mod " + mod);
        p = (p % mod);
    }

}

1

这称为模块化幂运算(https://en.wikipedia.org/wiki/Modular_exponentiation)。

假设您具有以下表达式:

19 ^ 3 mod 7

您可以直接执行以下操作,而不是直接为19供电:

(((19 mod 7) * 19) mod 7) * 19) mod 7

但是由于大量的顺序乘法,这也可能花费很长时间,因此您可以乘以平方值:

x mod N -> x ^ 2 mod N -> x ^ 4 mod -> ... x ^ 2 |log y| mod N

模幂运算算法做出以下假设:

x ^ y == (x ^ |y/2|) ^ 2 if y is even
x ^ y == x * ((x ^ |y/2|) ^ 2) if y is odd

因此,递归模块化幂运算算法在Java中将如下所示:

/**
* Modular exponentiation algorithm
* @param x Assumption: x >= 0
* @param y Assumption: y >= 0
* @param N Assumption: N > 0
* @return x ^ y mod N
*/
public static long modExp(long x, long y, long N) {
    if(y == 0)
        return 1 % N;

    long z = modExp(x, Math.abs(y/2), N);

    if(y % 2 == 0)
        return (long) ((Math.pow(z, 2)) % N);
    return (long) ((x * Math.pow(z, 2)) % N);
}

特别感谢@chux在y和0比较的情况下发现错误并返回了错误的返回值。


非常感谢您的反馈。您能否提供导致错误输出的输入数据?
Stepan Pogosyan

1
非常感谢您发现的错误。我已经纠正了1%N
斯捷潘·波戈相

0

只需提供C对Jason答案的另一种实现即可。

在与同学讨论之后,根据杰森的解释,如果您不太在意性能,我会更喜欢递归版本:

例如:

#include<stdio.h>

int mypow( int base, int pow, int mod ){
    if( pow == 0 ) return 1;
    if( pow % 2 == 0 ){
        int tmp = mypow( base, pow >> 1, mod );
        return tmp * tmp % mod;
    }
    else{
        return base * mypow( base, pow - 1, mod ) % mod;
    }
}

int main(){
    printf("%d", mypow(5,55,221));
    return 0;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.