欧拉余数定理通解,求A的B次幂模C的值( A^B mod C)

#欧拉余数定理算法

对于 A^B mod C
(网上的算法基本上不见考虑 A ,C 不互质的情况,显然是残缺的)

1.如果 A ,C 不互质(互质:两个数做因式分解,公共因子只有1)
通过约分使得新的C1与A互质,如果C1与A不互质,则重复此步骤。
记录的约数积Y 和商S积

2 如果 A 大于 C ,则A = A %C

3 求C的欧拉函数值euler

4 如果B不小于euler, B=B%euler

5 计算 e=A^B ,此时A,B已经较小了

6 A^B mod C =(e*S %C)*Y

举个例子 :15 ^6mod12 = 9

  •   A=15
    
  •   B=6
    
  •   C=12
    

一,15,12有约数3:

  •   A=15
    
  •   B=B-1=5
    
  •   C=12/3=4
    
  •   Y=3
    
  •   S=15/3=5
    

二 A>C:

  •   A=A%C=15%4=3
    

三 求C的欧拉函数值:

  •   euler(4)=2
    

四 :

  •    B=B%euler=5/2=1
    

五 计算 :

  •    e=A^B  =3 ^ 1=3
    

六 :

  •   A^B  mod C =(e*S %C )*Y = 3* 5%4*3=15%4*3=3*3=9
    

代码

public class Mod {
	public static int  powerMod(int a,int pow,int n) {
		int factor=coprime(a,n);
		int multiplier=1;
		int quotient=1;
		int cya=a;
		int cyn=n;
		int cypow=pow;
		while(factor>0&&cypow>1) {
			multiplier*=factor;
			quotient*=cya/factor;
			cyn/=factor;
			cypow--;
			factor=coprime(cya,cyn);
		}
		if(cya>cyn) {
			cya%=cyn;
		}
		int euler = eulerFunction(cyn);
		if(cypow>euler&&euler>0) {
			cypow%=euler;
		}
		int ret=1;
		if(cya==0) {
			ret=0;
		}else if(cypow==0) {
			ret=1;
		}
		while(cypow>0) {
			ret*=cya;
			cypow--;
		}
		return (quotient*ret%cyn)*multiplier;
	}
	public static int  eulerFunction(int n) {
		if(isPrimeNumber(n)) {
			return n-1;
		}
		int count=0;
		for(int i=1;i<n;i++) {
			if(coprime(i,n)<0) {
				count+=1;
			}
		}
		return count;
	}
	public static int coprime(int a,int b) {
		int min = Mod.min(a,b);
		for(int i=2;i<=min;i++) {
			if(a%i==0) {
				if(b%i==0) {
					return i;
				}
			}
		}
		return -1;		
	}
	public static <E extends Comparable<E>> E min(E ... pars) {
		Objects.requireNonNull(pars);
		if(pars.length==0) {
			throw new NullPointerException();
		}
		E min=pars[0];
		for(int i=1;i<pars.length;i++) {
			if(min.compareTo(pars[i])>0) {
				min=pars[i];
			}
		}
		return min;		
	}
}

测试 欧拉函数

		for(int i=1;i<21;i++) {
			System.out.println(i+"->"+eulerFunction(i));
		}

1->0
2->1
3->2
4->2
5->4
6->2
7->6
8->4
9->6
10->4
11->10
12->4
13->12
14->6
15->8
16->8
17->16
18->6
19->18
20->8

测试模方法

手工单个检验无疑是不可取得,所以使用BigInteger 进行结果检验。
A C 的值一定要取小点,原因在下文。

		for(int i=1;i<10;i++) {
			int a=(int) (Math.random()*20);
			int b=(int) (Math.random()*1000+1);
			int c=(int) (Math.random()*20+1);
			System.out.println(a+" ^ "+b+" mod "+c);
			System.out.print("powerMod "+powerMod(a,b,c));
			BigInteger big=BigInteger.valueOf(a);
			System.out.println(" vs " +" big "+big.modPow(BigInteger.valueOf(b), BigInteger.valueOf(c)));
		}

13 ^ 214 mod 2
powerMod 1 vs big 1
12 ^ 178 mod 16
powerMod 0 vs big 0
6 ^ 73 mod 16
powerMod 0 vs big 0
3 ^ 890 mod 13
powerMod 9 vs big 9
6 ^ 572 mod 17
powerMod -5 vs big 13
8 ^ 805 mod 2
powerMod 0 vs big 0
15 ^ 665 mod 18
powerMod 9 vs big 9
10 ^ 290 mod 2
powerMod 0 vs big 0
17 ^ 149 mod 6
powerMod 5 vs big 5

很容易发现有个异常值:
6 ^ 572 mod 17
powerMod -5 vs big 13
不难想到原因定是整型溢出。
可以推敲一下。
euler(17)=16
572%16=12
6 ^ 12 = 3 ^ 24
int=2^31-1< 2 ^32
3 ^ 24 /2 ^ 32=(3/2) ^ 24 / (2 ^ 8 )
3/2=1.5>2 ^ (1/2)=1.414
(3/2) ^ 24 / (2 ^ 8 ) > ( 2 ^ (1/2) ) ^ 24 / (2 ^ 8 ) =2 ^ 12 / 2 ^8=16
至少大了16倍,所以必然出错。

解决方案:
将int 包装成 BigInteger 即可。

增加本算法与BigInteger.modPow()时间效率的对比,不禁感叹源码的神奇。

猜你喜欢

转载自blog.csdn.net/qq_39464369/article/details/89680789
今日推荐