指數(連乘)的快捷求法

平常我們用代碼求指數,一般是利用循環實現,例如

求2^10,用C語言可以寫爲:

int result=1;
for(i=0;i<10;i++){
    result*=2;
}
但是,當指數非常大時,用這種循環無疑會非常慢,例如
求2^10000000000(10個0),需要循環2^10000000000(10個0)次,無疑非常浪費時間,尤其在做ACM題目時,時間有限,往往無法達標。
怎麼辦?我們看下快速求指數(連乘)的原理。
以2^7爲例:(爲了方便看,把乘號省略)
2^7:2 2 2 2 2 2 2 ,總共需要6次乘法運算

2^7:2 2 2|2 2 2|2,(2^3)*(2^3) *2,而2^3需要2次連乘,因此總共需要4次乘法運算(沒看懂的看接下來的註釋,看懂的自覺跳過)
=====================上一步的註釋===========================
2^3:2*2*2,需要2次乘法運算,通過這兩次乘法運算得出2^3的結果
(2^3)*(2^3),由於2^3結果已知,因此是自乘運算(等效於result*=result;),算1次乘法運算
(2^3)*(2^3) *2,最後再乘以2,因此總共有4次乘法運算

=====================註釋結束==============================

再以2^8爲例:

2^2:2|2,(2^1)*(2^1),2*2一次運算求出2^2

2^4:2 2|2 2,(2^2)*(2^2),再一次自乘由2^2求出2^4

2^8:2 2 2 2|2 2 2 2 ,(2^4)*(2^4),再一次自乘由2^4求出2^8

因此快速指數需要3次乘法運算即可。

發現規律了沒?
採用二分的思想,每次拿次方數除2,直到商爲1終止,把餘數和2分次數加起來就可以了

再以2^7爲例點明規律
2^7:2 2 2|2 2 2|2,
把指數7二分(7個2相乘),7/2=3(分爲兩個2^3相乘),7%2=1(餘1個2)
2^3:2|2|2,把3均分,3/2=1(兩個2^1相乘),3%2=1(餘1個2)

2^1已經不能再分(此時3/2=1,即商爲1時終止),因此:
指數/2=1是劃分結束標誌
總的乘法運算次數=劃分次數(2)+餘數次數(1)

再以一道ACM題輔助理解:

描述

給你一個非零整數,讓你求這個數的n次方,每次相乘的結果可以在後面使用,求至少需要多少次乘。如24:2*2=22(第一次乘),22*22=24(第二次乘),所以最少共2次;             

輸入
第一行m表示有m(1<=m<=100)組測試數據;
每一組測試數據有一整數n(0<n<=10000);
輸出
輸出每組測試數據所需次數s;
樣例輸入
3
2
3
4
樣例輸出
1
2
2
我用的是二分的方法。每次拿次方數除2,直到商爲1終止,把餘數和2分次數加起來就可以了。例如:7次
原始:2 2 2 2 2 2 2 
1次:2 2 2 | 2 2 2 | 2 (分了1次,餘1,第二次二分只需要取3次方,7/2=3);
2次:2 |2| 2(又分了1次,餘1,此時3/2=1,商爲1,不用再二分了)
則共分了2次,兩次餘1,則2+2=4,需要分4次。
同理8次方分3次

代碼如下:
#include <stdio.h>

int main(){
<span style="white-space:pre">	</span>int length;
<span style="white-space:pre">	</span>int getnum;//輸入的次方<pre name="code" class="cpp">int mul(long num){//連乘的快捷算法
    unsigned long long temp;
    if(num==1)
        return 2;
    temp=mul(num/2);//遞歸調用
    temp=temp*temp*((num%2)?2:1); 
    if(temp>1000000)//題目只要求後6位數 
        temp%=1000000; 
    return temp;
}

int time=0;//記錄2分次數 int remainder=0;//餘數不爲0的次數 scanf("%d",&length); for(;length>0;length--){ scanf("%d",&getnum); while(getnum!=1){ if(getnum%2) remainder++; time++; getnum/=2; } printf("%d\n",time+remainder); time=0; remainder=0; }}

=====================================================================原理解釋到這,怎麼運用呢?以代碼展示:
<span style="font-size:18px;">int mul(long num){//連乘的快捷算法,求2^num
    unsigned long long temp;
    if(num==1)//遞歸結束條件,1個2相乘,即2^1,上一個指數/2=1的結果
        return 2;
    temp=mul(num/2);//遞歸調用
    temp=temp*temp*((num%2)?2:1); 
    return temp;
}</span>
由於每一次都是對指數按同樣原理劃分,以指數/2=1結束,因此採用遞歸的形式
精華在於
<span style="font-size:18px;">temp=temp*temp*((num%2)?2:1); //有餘數:(temp^2)*2,無餘數</span><span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif;">(temp^2)*1</span>
((num%2)?2:1)是對餘數的判斷,當num%2==1時,乘以1次餘下的2,當num%2==0時,使用1,即不變

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章