剛剛接觸算法的初學者第一次記錄關於算法的理解,如果有什么不正確的地方各位大佬請指正。
最開始遇到一些關於求a^n次方取模的題目最開始的我想法無非是(可能是我比較笨)一次次的乘過去了
如下所示:
1 int a,b,mod,ans=1; 2 cin>>a>>b>>mod; 3 for(int i=1;i<=b;i++) 4 { 5 ans=ans*a; 6 } 7 cout<<ans%mod<<end;
但是想法僅僅是個天真的想法而已 比如如果要求9^1234次方這種算法太過於消耗時間
還有就是計算機最后也不一定放得下這個答案 所以一般這種題目都有取模的要求 雖然我這個地方按照思路來最后答案也取模了 但是可能在乘的過程中這個答案就溢出了..
所以首先我們要知道關於取模的運算 如下的公式是成立的
(a * b) % mod = (a % mod * b % mod) % mod
(a + b) % mod = (a % mod + b % mod) % mod
然后我們再來說快速冪的核心思想
如果我們要求2^7按照我以前的思維就是 ans=1*7*7*7*7*7*7*7 但是他的時間復雜度是O(n)
我們一般手算這種東西都是比較小的所以感覺不到 但是題目一般都是不會讓你這么好過的 1000ms的時間限制擺在那里。。
所以我們可以知道 2^7我們可以拆成 2^7=2^4*2^2*2^1 這樣是不是發現就只要計算三次了呢!
如果這個不明顯我們可以看看2^63按照一般方法我們要計算65次,但是2^63=2^32*2^16^2^8^2^4*2^2*2^1 這樣只計算了6次! 差別就明顯了
現在我們就要想辦法如何實現這個過程了 我們可以發現其實我們是把指數看成了二進制數
7=111 63=111111 我們要判斷指數在二進制的時候 每一位上是否為1 就知道是否要乘了
比如2^5 5的二進制為101 也就是只需要2^5=2^4*2^1就可以了 因為中間是0 所以我們不用乘2^2
這個地方我們要判斷指數的二進制每一位是否為1
我們可以很快的想到用&(按位與)運算符 如果&1等於1的話就說明最右邊這一位是1了
那么如果我們判斷完這一位之后 這一位肯定就不需要再判斷了 我們得舍去他 我們也能很快想到用>>(右位移)運算符 把7>>1 其實就是(111>1)當然就變成11了
當每一位都判斷完后 這個指數每一位也都被右移了 當然最后就變成了0 也就是運算完成了! 下面上代碼理解。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int quickchen(long long a,long long b,long long mod) 5 { 6 long long ans=1; //ans用來存放最終答案 注意是乘法運算 所以初始化的值應該是1 7 8 while(b) //這個地方就是我們的指數 按照上面的想法 如果這個數為0了也就是位移完了 答案也就出來了 循環就可以結束了 9 { 10 if(b&1) //判斷最右位是否是1 11 12 ans=(ans*a)%mod; //如果是1 那么這一位就應該乘上 13 //注意這里兩個取模 都應該乘了之后再取模 而不是ans*=a%mod; 如果是這樣 那其實是ans=ans*a%mod;
//按照上面我列出來的取模公式 這就不對了 15 a=(a*a)%mod; //注意這句話 是不需要寫在判斷里面的 就比如2^5=2^4*2^1(5的二進制數是101)第二次循環時候那一位是不需要乘上的 16 //但是下一次你使用的是2^4而第一次乘的時候a是2^1次方
//所以第二次不管需不需要乘 這個a都應該乘以自己變成2^2以便下次使用 17 18 b>>=1; //指數右移一位 19 } 20 return ans%mod; 21 } 22 23 int main() 24 { 25 long long a,b,mod,sum; //a^b次方 mod模數 sum是求模后結果 26 cin>>a>>b>>mod; 27 sum=quickchen(a,b,mod); //調用自定義的快速冪函數 28 cout<<a<<"^"<<b<<" mod "<<mod<<"="<<sum; 29 return 0; 30 }
講到這里快速冪算法就講完了 他的時間復雜度是O(logn) 是不是快了許多!
當然其實我也只是會用了而已 按照我自己的腦子是想不出來這種東西的(菜)
如果大佬看見覺得哪里很可笑希望能指教我 畢竟我也是初學容易忘 只是記錄下自己的思路
下一篇博客我會在這個基礎上寫一個關於矩陣快速冪的算法理解(其實自己也不太理解 只能寫下來給自己看了)