【问题标题】:How to calculate modulus of large numbers?如何计算大数的模?
【发布时间】:2011-01-11 18:38:11
【问题描述】:

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

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

【问题讨论】:

  • 这里是一个解释:devx.com/tips/Tip/39012
  • devx 链接没有多大用处,数论中还有其他简单的方法可以解决此类问题,AFAIK。
  • @Priyank Bolia:别担心,这个问题不太可能被关闭。这是个好问题。如果关闭,会有很多人投票重新开放。
  • 是的,我们中的许多人都知道,有时计算机科学涉及数学。
  • @JB King:MathOverflow 适用于研究生及更高级别的数学;这个问题在那里会不受欢迎。

标签: math modulo


【解决方案1】:

好的,所以你要计算a^b mod m。首先,我们将采用一种简单的方法,然后看看我们如何改进它。

首先,减少a mod m。这意味着,找到一个数字a1,以便0 <= a1 < ma = 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 &lt;= p &lt; m0 &lt;= a1 &lt; 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

现在,我们可以使用exponentiation by squaring 来改进它;这是著名的技巧,我们将求幂减少到只需要 log b 乘法而不是 b。请注意,使用我上面描述的算法,通过平方改进求幂,您最终会得到right-to-left binary method

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等。

上面的算法形式化了这个想法。

【讨论】:

  • 嗯,大多数编程语言都有一个内置的运算符。例如,在 C 派生语言中,% 运算符是模运算符。因此,int p = 625 % 221 会将183 分配给p。您可以通过将625 除以221 作为整数除法并得到答案2 来实现相同的功能。然后你用625 - 2 * 221 得到剩余部分。在这种情况下,625 - 2 * 221 = 183 就是答案。
  • 是的,正如我在最后的段落中所描述的那样,你通过平方来求幂。
  • 您实际上可以通过平方比取幂做得更好,尤其是在大指数的情况下。请注意,您找到了5^16 == 1 (mod 221)。因此,5^k == 5^(k%16) (mod 221).
  • @Jason:你写过:首先,减少一个 mod m。这意味着,找到一个数字 a1 使得 0 看起来最后一个等式包含错字,不应该是 a1 = a mod m 而不是?
  • @Jason 大多数情况下,如果您刚刚添加了“;” (和一些其他字符)到你的伪代码,它会是 C.
【解决方案2】:

添加到杰森的答案:

您可以使用指数的二进制展开来加快处理速度(这可能对非常大的指数有帮助)。首先计算 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 中的线性,但不确定。)

【讨论】:

  • 这是更好的解释
  • 我怀疑通过平方来避免幂运算实际上要快得多(通常),而是直接搜索最小指数 $k$ 使得 $5^k == 5 (mod 221)$ .这当然取决于指数的大小与模数的关系,但是一旦你有了那个指数,你只需要一个计算(指数 mod k)和查找。请注意,如果您需要重复类似的计算,它也肯定会更好。 (你一般不能寻找 $a^k == 1 (mod 221)$ 因为这只有在 $a$ 和 221 是相对质数时才会发生)
  • 嗯,不,一般来说,找到具有该属性的最小指数比平方和乘法慢得多。但是,如果您知道模数的分解,那么您可以轻松计算 carmichael lambda 函数,它是您的 k 的倍数。
【解决方案3】:
/* 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;
}

【讨论】:

  • x * powerpower * powermod*mod &gt; UINT_MAX + 1 时会溢出。
  • 是的@chux 是对的,即使在 x * power 和 power * power 期间我们也应该使用 mod。
  • @jack_1729 代码可以使用更宽的整数类型和x * power 来避免OF。如果没有可用,代码可以使用this
【解决方案4】:
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

【讨论】:

  • 这比取幂的方式慢吗?
【解决方案5】:

您正在寻找的是模幂运算,特别是模二进制幂运算。这个wikipedia link 有伪代码。

【讨论】:

    【解决方案6】:

    Chinese Remainder Theorem 想到的初始点为 221 = 13 * 17。因此,将其分解为最后组合的两部分,一个用于 mod 13,一个用于 mod 17。其次,我相信那里是所有非零 a 的 a^(p-1) = 1 mod p 的一些证明,这也有助于减少您的问题,因为对于 mod 13 的情况,5^55 变为 5^3,即 13*4=52。如果您查看“有限域”主题,您可能会发现一些关于如何解决此问题的好结果。

    编辑:我提到这些因素的原因是,这创造了一种将零分解为非零元素的方法,就好像您尝试了 13^2 * 17^4 mod 221 之类的东西一样,答案为零,因为 13*17= 221.很多大数不会是素数,尽管有一些方法可以找到大素数,因为它们在密码学和数学的其他领域中被大量使用。

    【讨论】:

    • 好吧,我首先不知道阶乘,我试图证明这个数字是素数,使用米勒拉宾算法。所以,我在另一端。
    • 这里没有任何阶乘,但是有一个不同的因式分解。整数 n 的阶乘定义为所有小于 n 的正整数的乘积,例如2!=2、3!=6 等,通常使用 !象征。因式分解是不同的,没有一个通用的符号用来表示被分解的整数。
    【解决方案7】:

    这是我为 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);
        }
    

    【讨论】:

      【解决方案8】:

      Jason 在 Java 中的回答(注意 i &lt; 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);
          }
      
      }
      

      【讨论】:

        【解决方案9】:

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

        与同学讨论后,根据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;
        }
        

        【讨论】:

          【解决方案10】:

          这称为模幂运算(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 比较的情况下发现错误返回值不正确。

          【讨论】:

          • 非常感谢您的反馈。能否请您提供导致错误输出的输入数据?
          • 非常感谢您发现的错误。我已更正为 1 % N。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多