Java實現(xiàn)快速冪算法詳解
前言
此算法偶爾會出現(xiàn)在筆試以及面試中,特意花時間研究了下這題
題目:
求AB次方,并且保留最后幾位數(shù)字(此題保留最后3位數(shù))
例子:
21000,輸出的結果保留3位數(shù)字
在筆試或者面試中看到此題,第一思路可能通過遞歸或者while遍歷的想法,但細想一下,這么大的數(shù)字編程語言中任何一個變量或者計算機硬件機器也hold不住這么大的數(shù)字存儲(越往后冪次越大,總是會溢出)
此時想到了海量數(shù)據(jù)如何存儲:海量數(shù)據(jù)處理的高頻面試題分析
那我就選擇布隆過濾器:布隆過濾器的原理和實現(xiàn)詳細分析(全)。(但可能會有誤差)
硬件無法存儲,那我就分片切片,甚至二進制移位來對應計算(但是我是21000次方,每一次的冪算,我都整這么復雜,這計算一個數(shù)字要花大半天??)
冷靜思考后,我發(fā)現(xiàn)想復雜了,應該從數(shù)學推導公式下手,才能提高算法的優(yōu)化
以下章節(jié)對應算法復雜度的優(yōu)化
1. 暴力算法(fail)
算法如下:
/*
* base 為底數(shù)
* power 為指數(shù)
*/
public long slowPower(long base,long power){
long result = 1;
// 依次通過for循環(huán),將其對應的數(shù)字乘
for(int i = 1 ;i <= power;i++){
result *= base;
}
// 保留最后的3位數(shù)字,求余1000
return result % 1000;
}
或者使用while循環(huán)
public long slowPower(long base,long power){
long result = 1;
while(power--){
result *= base;
}
return result % 1000;
}
此處的代碼執(zhí)行的時候,就會出現(xiàn)數(shù)組越界
冪次運算,越到最后,爆炸式的增長:

對此求余是最好的想法(因為數(shù)值很大保留最后幾位即可)
但數(shù)值本身就已經(jīng)越界,而且爆炸增長也算不到最后的數(shù)值,更不用提及求余
2. 優(yōu)化取模運算(accept)
從上面的理論可得知,在求到某一步的時候,數(shù)值已經(jīng)越界,那可以提前求余在計算么
那就要從取模運算進行深入了解:取模運算百度百科
本身模運算與基本四則運算相似
- (a + b) % p = (a % p + b % p) % p
- (a - b) % p = (a % p - b % p ) % p
- (a * b) % p = (a % p * b % p) % p
- a ^ b % p = ((a % p)^b) % p
其他的重要的也可提前過一遍(哪天用得上)
結合律:
- ((a+b) % p + c) % p = (a + (b+c) % p) % p
- ((ab) % p * c)% p = (a * (bc) % p) % p
分配律:
- (a+b) % p = ( a % p + b % p ) %p
- ((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p
特別是這個公式:(a * b) % p = (a % p * b % p) % p
算法如下:
/*
* base 為底數(shù)
* power 為指數(shù)
*/
public long fastPower(long base,long power){
long result = 1;
// 依次通過for循環(huán),將其對應的數(shù)字乘
for(int i = 1 ;i <= power;i++){
result *= base;
result %= 1000;
}
// 保留最后的3位數(shù)字,求余1000
return result % 1000;
}3. 優(yōu)化時間復雜度(accept)
上面的計算是21000,如果是210000000000000000000000000000那時間復雜度隨著指數(shù)的增加而增加,而且迭代的次數(shù)也特別多,那優(yōu)化時間復雜度么?
通過冪次的巧妙處理,將其冪次計算的迭代減少
具體如下:(計算210)
pow(2,10)
= pow(4,5)
= pow(4,4) * pow(4,1)
= pow(16,2) * pow(4,1)
= pow(256,1) * pow(4,1)
- 冪次為偶數(shù),直接處理
- 冪次為奇數(shù),拆1 以及 偶數(shù)
知道所有的指數(shù)都變?yōu)?,將其相乘即為最終的結果
最終的算法:
/*
* base 為底數(shù)
* power 為指數(shù)
*/
public long fasterPower(long base,long power){
long result = 1;
// 保證指數(shù)大于0 遍歷
while (power > 0) {
// 偶數(shù)
if (power % 2 == 0) {
// 指數(shù)減半
power /= 2;
// 底數(shù)平方,記得 (模的運算優(yōu)化)
base = base * base % 1000;
} else {
// 指數(shù)為奇數(shù)
// 拆分為1 和 偶數(shù)
power -= 1;
// result乘底數(shù) 求余 并且放在result(提前存儲)
result = result * base % 1000;
// power偶數(shù),再操作一次
// 底數(shù)平方,記得 (模的運算優(yōu)化)
power /= 2;
base = base * base % 1000;
}
}
// 直接輸出結果,已經(jīng)不用求余了
return result;
}通過上面的算法發(fā)現(xiàn)冗余復雜了,提取公共部分合并
/*
* base 為底數(shù)
* power 為指數(shù)
*/
public long fasterPower(long base,long power){
long result = 1;
// 保證指數(shù)大于0 遍歷
while (power != 0) {
// 奇數(shù)特殊判斷
if (power % 2) {
result = result * base % 1000;
}
// 再次操作一次,底數(shù)平方,記得 (模的運算優(yōu)化)
// 此處之所以不用減1,是因為 power 會向下取整 ,5 / 2 = 2
power /= 2;
base = base * base % 1000;
}
// 直接輸出結果,已經(jīng)不用求余了
return result;
}4. 優(yōu)化 位運算(accept)
除2(右移),可以使用移位來計算
// 非遞歸
public long fastestPower(long base,long power){
long result = 1;
while (power != 0) {
// 奇數(shù)特殊判斷
if (power & 1) {
result = result * base % 1000;
}
power >>= 1;
base = base * base % 1000;
}
// 直接輸出結果,已經(jīng)不用求余了
return result;
}通過上面的層層遞進進行優(yōu)化,自然也可用遞歸
// 遞歸
public long fastestPower(long base,long power){
if(power == 0) {
return 1;
}
return fastestPower(base * base, power >> 1) * (power & 1 == 1 ? base : 1);
}以上就是Java實現(xiàn)快速冪算法詳解的詳細內(nèi)容,更多關于Java快速冪算法的資料請關注腳本之家其它相關文章!
相關文章
Java8?CompletableFuture?runAsync學習總結submit()?execute()等
這篇文章主要介紹了Java8?CompletableFuture?runAsync學習總結submit()?execute()等,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
詳解Java如何在業(yè)務代碼中優(yōu)雅的使用策略模式
這篇文章主要為大家介紹了Java如何在業(yè)務代碼中優(yōu)雅的使用策略模式,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的可以了解下2023-08-08

