問題
編寫Java代碼,求 1001803 mod 137 的值。
分析
求餘運算是Java的基本功能,可以直接使用 (int)Math.power(a,b) % p
進行計算,但是由於指數運算增長太快,非常容易就超出 Integer 的數據類型,即便我們使用 Long,其最大值也不過是 264-1,約爲 1.84X1019,遠遠小於 1001803,所以顯然是無法直接求的。
爲了解決問題,我們首先分析一下餘數的性質。根據餘數 性質3: 給定 且 ,則。比如,(7 9) mod 5 = 3,而((7 mod 5) (9 mod 5)) mod 5 = (2 4) mod 5 = 3。
根據這個性質,我們可以把問題變爲1001對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;
}