编写Java代码实现求1001的803次方除以137的余数

问题

编写Java代码,求 1001803 mod 137 的值。

分析

求余运算是Java的基本功能,可以直接使用 (int)Math.power(a,b) % p 进行计算,但是由于指数运算增长太快,非常容易就超出 Integer 的数据类型,即便我们使用 Long,其最大值也不过是 264-1,约为 1.84X1019,远远小于 1001803,所以显然是无法直接求的。

为了解决问题,我们首先分析一下余数的性质。根据余数 性质3: 给定 a,b,c,rZa, b, c, r \in \mathbb{Z}c00rcc \neq 0,0 \leq r \leq c,则(a×c) mod b=(a mod b)×(b mod b)(a \times c) \ \mathrm{mod} \ b = (a \ \mathrm{mod} \ b) \times (b \ \mathrm{mod} \ b)。比如,(7 ×\times 9) mod 5 = 3,而((7 mod 5) ×\times (9 mod 5)) mod 5 = (2 ×\times 4) mod 5 = 3。

根据这个性质,我们可以把问题变为1001对137取余后的乘积再余,即:
1001803 mod 137=((1001 mod 137)(1001 mod 137)(1001 mod 137)803次取余再相乘) mod 137将乘积再对137取余 1001^{803} \ \mathrm{mod} \ 137 = \underbrace{\underbrace{ ((1001 \ \mathrm{mod} \ 137) * (1001 \ \mathrm{mod} \ 137) * \cdots * (1001 \ \mathrm{mod} \ 137) }_{\text{803次取余再相乘}} ) \ \mathrm{mod} \ 137}_\text{将乘积再对137取余}

实现

根据以上分析,我们可以根据这个思路编写以下代码:

public static void main(String[] args){
	int base = 1001;
	int x = 803;
	int p = 137;
	int result = 1;
	
	for (int i = 0; i < x; i++) 
		result *= base % p;
	result %= p;

	System.out.println("Result = " + result);
}

然而在执行时,仍然结果不正确,会报数据溢出的错误。经检查,发现溢出错误是发生在循环中的 result *= base % p 语句,即 result 仍然会超出数据范围,原因应该是多次乘法的值过大导致的,所以我们代码可以再优化一下,最终结果如下:

public static void main(String[] args){
	int base = 1001;
	int x = 803;
	int p = 137;
	int result = 1;
	
	for (int i = 0; i < x; i++){
		 // 防溢出,一定要写在 result *= base % p; 的上面,读者可以思考下这是为什么。
		if (result > 46340) // 注1
			result %= p;
		result *= base % p;
	}
	result %= p;
	System.out.println("Result = " + result);
}

注1:在程序中进行乘法运算时,两个数的乘积可能会超出其数据类型所能表示的范围,所以此处的取到此类型所能表示的最大值开根的值。当前类型为int,其最大的正值为 2^31-1,所以可选的最大值为 (int) Math.sqrt(2^31 - 1),即46,340。即底能取到最大值为 46,340,如果想取更大的值可以使用 long,计算范围的方法一样,其值为3,037,000,499。

运行计算后,结果为:

Result = 104

扩展

我们可以进一步对其进行扩展,将这个计算抽象出一个函数,以方便利用,其定义如下:

/**
 * Return base^x % p.
 * @param base The base of the exponential function.
 * @param x A variable of the exponential function.
 * @param p The divider.
 * @return
 */
public static int getMod(int base, int x, int p) {
	int r = 1;
	for (int i = 0; i < x; i++){
		if (result > 46340)
			result %= p;
		result *= base % p;
	}
	return r % p;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章