【问题标题】:how to calculate modulus division如何计算模除法
【发布时间】:2013-07-09 21:46:16
【问题描述】:

我在查找除法模数时卡在程序中。

比如说我有:

((a*b*c)/(d*e)) % n

现在,我不能简单地计算表达式,然后将它取模到 n,因为乘法和除法是在一个循环中进行的,并且值足够大,即使是 long long 也不能适应。

正如在 cmets 中阐明的,n 可以被认为是素数。

我发现,对于乘法,我可以很容易地计算为:

((a%n*b%n)%n*c%n)%n

但当时不明白如何计算除法部分。

我面临的问题是一个简单的例子:

((7*3*5)/(5*3)) % 11 

上述表达式的值为 7

但如果我计算乘法,取模,它会是这样的:

((7%11)*(3%11))%11 = 10
((10%11)*(5%11))%11 = 6

现在我只剩下 6/15,我无法生成正确答案。

谁能帮帮我。请通过上面的例子让我明白其中的逻辑。

【问题讨论】:

  • a%n = a - (n * int(a/n))
  • 使用大整数库。
  • (a/b)mod n = ((a mod n )( 1/b mod n) mod n )
  • @Randy 不能保证b 有一个逆模n。例如。 b=2, n=8。那么没有整数x 使得2*x % 8 == 1
  • @iamnotmaynard 正确 - 在这种情况下,我认为 mod 是未定义的

标签: c algorithm


【解决方案1】:

因为 11 是素数,所以 Z11 是一个字段。因为15 % 114,所以1/15 等于3(因为3 * 4 % 11 是1)。因此,6/156 * 3,即 7 mod 11。

在问题下方的 cmets 中,您阐明模数始终是素数。

要有效地生成乘法逆元表,您可以将2 提高到连续幂以查看它生成的值。请注意,在字段 Zp 中,p 是奇素数,2p-1 = 1。因此,对于 Z 11

 2^1 = 2
 2^2 = 4
 2^3 = 8
 2^4 = 5
 2^5 = 10
 2^6 = 9
 2^7 = 7
 2^8 = 3
 2^9 = 6

所以5 的乘法逆元(即 24)是 26(即 9)。

所以,你可以像这样生成上表:

power_of_2[0] = 1;
for (int i = 1; i < n; ++i) {
    power_of_2[i] = (2*power_of_2[i-1]) % n;
}

乘法逆表可以这样计算:

mult_inverse[1] = 1;
for (int i = 1; i < n; ++i) {
    mult_inverse[power_of_2[i]] = power_of_2[n-1-i];
}

【讨论】:

  • 但是n 不一定是素数。
  • @iamnotmaynard:我没有说这是一个通用的解决方案,他想了解如何从他的示例中生成正确的答案。
  • OP 正在寻找一个通用的解决方案。
  • @iamnotmaynard:一般的解大概可以从中国剩余定理推导出来,但是我的数论在这一点上太生疏了。
  • @PieterGeerkens: n 是质数,根据问题下的 cmets。
【解决方案2】:

在您的示例中,由于 15 = 4 mod 11,您实际上最终不得不评估 (6/4) mod 11。

为了找到一个精确的解决方案,将其重新排列为 6 = ( (x * 4) mod 11),这样可以更清楚地了解模除法的工作原理。

如果没有别的,如果模数总是很小,您可以从 0 迭代到模数-1 以获得解决方案。

注意,当模数不是素数时,约简问题可能有多种解决方案。例如,4 = ( ( x * 2) mod 8) 有两种解:2 和 6。这将发生在形式简化问题:

  a = ( (x * b) mod c)

只要 b 和 c 不是互质的(即当它们共享一个公约数时)。

类似地,当 b 和 c 不是互质时,可能无法解决约化问题。例如,3 = ( (x * 2) mod 8) 没有解。每当 b 和 c 的 最大公约数 不整除 a 时,就会发生这种情况。

后两种情况是从 0 到 n-1 的整数在 n 不是素数时在乘法下形成群(或等效地,在 + 和 * 下的域)的结果,而是简单地形成一个不太有用的结构。

【讨论】:

    【解决方案3】:

    我认为问题的提出方式,应该假设分子可以被分母整除。在这种情况下,素数 n 的有限域解以及关于非素数 n 的可能扩展和警告的推测基本上是矫枉过正。如果您将所有分子项和分母项都存储在数组中,则可以迭代测试(分子项,分母项)对并快速找到最大公约数(gcd),然后将分子项和分母项除以gcd . (寻找 gcd 是一个经典问题,你可以很容易地在网上找到一个简单的解决方案。)在最坏的情况下,你将不得不迭代所有可能的对,但在某些时候,如果分母确实除以分子,那么你最终会留下减少的分子项,所有分母项将为 1。然后您就可以按照您描述的方式应用乘法(避免溢出)。

    【讨论】:

      【解决方案4】:

      由于 n 是素数,除以整数 b 只是乘以 b 的倒数。那就是:

      (a / b) mod n = (a * inv(b)) mod n
      

      在哪里

      inv(b) = (b ^ (n - 2)) mod n
      

      使用Exponentiation by squaring 算法可以在 O(log(n)) 时间内完成计算 inv(b)。代码如下:

      int inv(int b, int n)
      {
          int r = 1, m = n - 2;
          while (m)
          {
              if (m & 1) r = (long long)r * b % n;
              b = (long long)b * b % n;
              m >>= 1;
          }
          return r;
      }
      

      为什么有效?根据费马小定理,如果 n 是素数,对于任何正整数 b,b ^ (n - 1) mod n = 1。因此我们有 inv(b) * b mod n = 1。

      查找 inv(b) 的另一个解决方案是 Extended Euclidean algorithm,它需要更多代码来实现。

      【讨论】:

      • 感谢您指出 O(log (n)) 方法。正在研究这个。
      【解决方案5】:

      我认为你可以像这样分配分区

      z = d*e/3
      (a/z)*(b/z)*(c/z) % n
      

      剩下的只是整数除法问题。

      【讨论】:

      • 为什么/3 是答案的一部分?
      • 不,你必须做立方根z = (d*e) ** (1/3)。 (或者无论如何它是在 C 中完成的。)
      【解决方案6】:

      我认为您遇到的问题是您选择了一个对于示例来说过于简单的问题。在那种情况下,答案是 7 ,但如果 a*b*c 不能被 c*d 整除怎么办?您可能应该先查看如何使用模数进行除法,您应该很清楚:)

      【讨论】:

        【解决方案7】:

        考虑乘法逆元,而不是除法。对于 mod-n 系统中的每个数字,如果满足某些条件,则应该有一个倒数。对于 d 和 e,找到那些倒数,然后一切都只是相乘。求逆不是通过除法完成的!那里有很多信息...

        【讨论】:

        • 不能保证 b 有一个反模 n。
        猜你喜欢
        • 1970-01-01
        • 2011-07-19
        • 1970-01-01
        • 2017-12-02
        • 2019-04-05
        • 2013-08-13
        • 2021-01-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多