求2的n次方对1e9+7的模

有如下问题:
求2n mod (1e9+7),其中1< n < 10100000

首先明确一下此类问题的几种算法,首先朴素算法,即暴力循环求解,是O(N)复杂度,适用范围应该是n小于1000000;然后是快速幂算法,效率是O(log N),适用于n小于2100000级别的;然后就是上面的例题了,由于范围是10100000,即使是快速幂也不可能在期望时间内解决,这时候需要另辟蹊径。

费马小定理: 对于质数P,任意的整数a,若满足GCD(a,P) = 1,则有: a p ≡ a ( m o d   p ) a^p \equiv a (mod \,p) apa(modp)

主要思路:
回到上述例题,可知 P 即1e9+7,这是个素数。而 a 是 2 ,很明显 2 也是素数,所以GCD(a , P ) = 1,满足费马小定理的条件。那么我们想提高2^n计算的效率,就绕不开减小n的大小,我们接下来就利用费马小定理来减少幂n的大小。

首先费马小定理的一个小变形: a ( P − 1 ) ≡ 1 ( m o d   P ) a^{(P-1)} \equiv 1 (mod \,P) a(P1)1(modP)

接下来对2^n进行变形: 2 n = 2 x ∗ ( P − 1 ) ∗ 2 k 2^n = 2^{x*(P-1)}* 2^k 2n=2x(P1)2k,而根据费马小定理,显然 2 x ∗ ( P − 1 ) m o d   P = 1 m o d   P 2^{x*(P-1)} mod\,P = 1 mod \,P 2x(P1)modP=1modP,于是 2 n m o d   P = 2 k 2^n mod\,P= 2^k 2nmodP=2k,其中k = n%(P-1)。此时k小于P,即小于1e9+7,可以使用快速幂解决。

注意: 值得注意的是,10^100000 最大是1后面有100000个0,需要用字符串存,在转化为整数时利用模运算规则,边转化边取模。

代码示例:

#include
#include
#include
using namespace std;
const int M = 1e9+7;
typedef long long ll;
ll qpow(ll a,ll b){
	ll res = 1;
	while(b){
		if(b&1) res = res*a%M;
		a = a*a%M;
		b >>= 1;
	}
	return res;	
}
ll solve(string x){
	ll M1 = M-1;
	ll k = x[0]-'0';
	for(int i = 1;x[i];i++) k = (k*10+x[i]-'0')%M1;
	ll ans = qpow(2,k); 
	return ans%M;
}
int main(){
	string n;
	while(cin >> n && n != "0"){
		printf("%lld\n",solve(n));
	}
	return 0;
}

你可能感兴趣的:(Algorithm,数学知识)