大家在学习信息学竞赛的过程中,总是会遇到要求计算结果对 \(10^9+7\) 或者 \(10007\) 这样的数取余数的结果,比如:http://codeforces.com/problemset/problem/1423/J
链接中的题目中就有一句话:
For each test case \(i\), print the answer on separate lines: number of polynomials \(P\) as described in statement such that \(P(2)=m_i\), modulo \(10^9+7\).
那么为什么要对 \(10^9+7\) 呢?
最主要的原因是一些运算的结果会很大,如果不在计算的过程中模 \(10^9+7\)(或者别的一些数),答案可能会超过 long long 的表示范围。而如果两个数 \(a,b\) 满足 \(0 \le a,b \lt 10^9+7\),则对其进行加、减、乘、除(整除或取余)操作,都不会让结果超过 long long 的表示范围(当然,除法运算会复杂一点,不能简单地除,除以 \(a\) 需要转变为乘以 \(a\) 的逆,这部分不作为这篇随笔的讨论范围),然后计算的结果再对 \(10^9+7\) 取余,又能保证结果小于 \(10^9+7\) 了。
同时,对于算术运算,我们能够证明在计算过程中不停地模一个数,和计算完了之后再模这个数的结果是一样的。这部分相信学过 同余 的同学应该也都能够理解。
所以最主要的目的就是:让我们的计算过程用 long long 就可以解决。
那么为什么一般都选择 \(10^9+7\) 或者 \(10007\) 这样的数呢?
我们会发现一般取余的数都是 素数 ,这也是有原因的。
假设我要输出的是答案模 \(m\) 的结果(即答案除以 \(m\) 的余数)。假设 \(m\) 是有一个有较多约数的数,同时在数据中存在 \(q\) 满足 \(gcd(m,q) = d \gt 1\) (即假设有一个数 \(q\) ,它和 \(m\) 的最大公约数是一个大于 \(1\) 的数 \(d\)),即存在 —— \(m = a \times d, q = b \times d\),则有以下等式:
(这里 \([x]\) 表示 \(x\) 向下取整的结果)其中 \(b/a\) 的结果是 \([0,b]\) 范围内的正整数,也就是说,\([b/a]\) 的结果只有 \(b+1\) 种可能,而 \(m\) 是一个预先确定的数。因此,上式的值就只有 \(b+1\) 种可能了。这样,虽然模运算之后的余数仍然在 \([0,m-1]\) 范围内,但是它的取值仅限于等式可能取到的那些值,也就是说余数的分布变得不均匀了。所以,\(m\) 的约数越多,发生这种余数不均匀的情况就越频繁,冲突的几率就越高。而素数的约数是最少的,因此一般选 \(m\) 为小于某个区域长度的最大素数。于是 \(10^9+7\) 就变成一个很合适的选择方案,一方面能保证记起来比较方便,另一方面也能保证模 \(10^9+7\) 的结果不会超出 long long 表示范围。