大数乘法的模[重复]

Posted

技术标签:

【中文标题】大数乘法的模[重复]【英文标题】:Modulo of multiplication of large numbers [duplicate] 【发布时间】:2014-01-09 20:10:33 【问题描述】:

是的,我知道这个问题可能看起来很幼稚,但我在谷歌和这个网站上搜索了很多,但找不到令人满意的答案。 我只想计算 (A*B)%MOD,前提是 a 很长,b 和 MOD 也是。 假设 MOD 大于 A 和 B 使得 A%MOD = A 和 B%MOD = B 但 A*B 大于 64 位。 (A*B)%MOD的正确值如何计算?

【问题讨论】:

马特:我不同意这个骗局。 unrealsoul: AB = (A-X)*B + XB,你总是可以用这种方式把 A 拆分成更小的数字。例如。设置 X = 地板(A/2)。然后,如果子结果仍然太大,您可以应用相同的过程。 这是回答被接受的人的评论“如果long的最大值是2^63 - 1,那么只要x 换句话说,分而治之 @Cheersandhth.-Alf 好伙伴。这种方法似乎很有希望。我一定会调查的。 不是 mod 1000000007 问题的欺骗,正如 OP 所述。其他问题的答案都不完全合适,因为它们都建议只使用 GMP 或做一些缓慢、错误或特定于 x86 的事情。你们都应该打开这个。 【参考方案1】:

这里的基本思想是首先定义一个非溢出的addmod 函数,该函数在其算术中利用负数。然后根据它定义timesmod,也使用位操作。时间复杂度为O(N),其中 N 是使用的位数(本例中为 64)。

#include <iostream>
using namespace std;

typedef long long BigInt; // must be signed, to detect overflow

BigInt A = 0x7fffffffffffff01;
BigInt B = 0x7fffffffffffff02;
BigInt M = 0x7fffffffffffff03;

// For simplicity it is assumed x, y, and m are all positive.
BigInt addmod( BigInt x, BigInt y, BigInt m )

  x %= m;
  y %= m;
  BigInt sum = x-m+y; // -m <= sum < m-1
  return sum < 0 ? sum + m : sum;


BigInt timesmod( BigInt x, BigInt y, BigInt m )

  x %= m;
  y %= m;
  BigInt a = x < y ? x : y; // min
  BigInt b = x < y ? y : x; // max
  BigInt product = 0;
  for (; a != 0; a >>= 1, b = addmod(b,b,m) )
    if (a&1) product = addmod(product,b,m);
  return product;


int main()

  cout << "A = " << A << endl;
  cout << "B = " << B << endl;
  cout << "M = " << M << endl;
  cout << "A*B mod M = " << timesmod(A,B,M) << endl;
  return 0;

输出:

A = 9223372036854775553
B = 9223372036854775554
M = 9223372036854775555
A*B mod M = 2

这很容易确认,因为 A=-2B=-1 mod M

注意:此代码未优化。

【讨论】:

问题是您使用BigInt 的签名类型,因此您无法可靠地检测溢出(它会导致未定义的行为)。您需要使用无符号类型来获得可靠的溢出行为。 @ChrisDodd 你完全正确;感谢您指出了这一点。我已经更新了addMod 函数来解决这个问题。 你的方法和我写的最后一个方法几乎一样:long long int multiply(long long a, long long b, long long mod) long long result; if(b==0) return 0LL; result = multiply(a,b&gt;&gt;1,mod); result = (result+result)%mod; if(b&amp;1) result = (result+a)%mod; return result; @unrealsoul007 我得到multiply(0x7fffffffffffff01,0x7fffffffffffff02,0x7fffffffffffff03) 返回-9223372036854711038。你得到了什么? (我相信答案应该是2。) @Matt 稍微编辑一下代码并将 long long 替换为 unsigned long long ,您将得到答案 2。【参考方案2】:

我认为您可以将 128 位产品分成两部分(高 64 位和低 64 位),并以 p 为模减少每一部分。假设p4^k 附近,那么您可以通过除以hi64 / (p&gt;&gt;k) 来大致计算出这个数字中有多少个p;这应该会给你大约k-1 位的正确答案。从整件事中减去那么多p,现在hi64 大约有k-1 更少的位。再次执行此操作,但计算 (hi64 &lt;&lt; k-1) / (p &gt;&gt; k)。然后再做一次,计算(hi64 &lt;&lt; k+k-2) / (p &gt;&gt; k)

另一张海报建议的施拉格的把戏听起来更划算,但我不明白。希望发帖人回来并完成他的回答!

【讨论】:

以上是关于大数乘法的模[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Karatsuba乘法--实现大数相乘

(转)大数运算——大数乘法

大数乘法(C语言实现)

大数加法大数乘法

大数运算——大数乘法

大数加法和大数乘法