矩陣快速冪——入門

題目鏈接POJ307

題意:求第n位斐波那契數(shù)mod 10000的大小。其中n的大小高達(dá)1000000000

由于我對(duì)矩陣快速冪也不是很了解颅停,所以只能淺談谓晌,不敢深談,希望對(duì)新手有點(diǎn)幫助癞揉。

先聊聊快速冪

首先纸肉,如果要你自己寫一個(gè)pow()函數(shù),需要求220喊熟,怎么求

#include <iostream>
using namespace std;
int quickPow(int a,int b){   //快速冪
    int ans = 1;
    while(b){
        if(b&1) ans*=a;
        a *= a;
        b /= 2;
    }
    return ans;
}
int pow(int a,int b){   //普通乘
    int ans = 1;
    for(int i=1; i<=b; i++)
        ans *= a;
    return ans;
}
int main(int argc, const char * argv[]) {
    cout<<quickPow(2, 20)<<endl;   //第一種方式
    cout<<pow(2,20)<<endl;    //第二種方式
    return 0;
}

上面的兩種方式柏肪,一種復(fù)雜度是O(logn),一種復(fù)雜度是O(n)芥牌。當(dāng)你數(shù)量級(jí)很大的時(shí)候预吆,比如10000000000時(shí),這個(gè)復(fù)雜度的時(shí)間差別就很大了

怎么得到這個(gè)快速冪的代碼的呢胳泉?我們假設(shè)需要求274

74轉(zhuǎn)化為二進(jìn)制為1001010,根據(jù)二進(jìn)制對(duì)應(yīng)位的1岩遗,可以把74拆分為64+8+2 扇商,所以
74 = 26 + 23 + 21
274 = 226 + 223 + 221

 while(b){    //快速冪核心代碼
      if(b&1) ans*=a;
      a *= a;
      b /= 2;
 }

下面看一下上面這段快速冪程序中的ans和a的用法 (74二進(jìn)制1001010為從右到左下面表格從1-7)

初始值 0 1(ans變了) 0 1(ans變了) 0 0 1(ans變了)
a = 2n 21 22 24 28 216 232 264
ans 20 20 20×22 20×22 20×22×28 20×22×28 20×22×28 ×264

這里有兩個(gè)變量,第一個(gè)變量a默認(rèn)用來存2n宿礁,案铺,變量ans存儲(chǔ)的是最后的結(jié)果,當(dāng)遇到第k位的二進(jìn)制位是1的時(shí)候梆靖,則ans ×= 2k

舉個(gè)例子控汉,74的二進(jìn)制表示是1001010,因?yàn)槭?code>除2運(yùn)算返吻,所以從右往左遍歷姑子,到右邊第二個(gè)時(shí),ans ×= 22测僵,到右邊第四個(gè)時(shí)街佑,ans ×= 28,到右邊第7個(gè)時(shí)捍靠,ans ×= 264沐旨,最后ans = 274,至于ans乘的過程中的那些22榨婆、28磁携、264怎么來?a變量不是一直在不斷增加嗎良风,ans乘的就是a變量對(duì)應(yīng)位的值谊迄,對(duì)應(yīng)上面的表格可能看的更清楚一點(diǎn)

不知道闷供,你看懂沒……如果還是沒懂,就給個(gè)面子裝懂好嘛……

矩陣鳞上?怎么過渡到斐波那契

  • 宏觀上看矩陣

上圖中这吻,乘號(hào)右邊是一系列的矩陣。我們稱為B1篙议、B2唾糯、B3、B4 …… Bn
該序列中的同行為一個(gè)斐波那契數(shù)列鬼贱。乘號(hào)左邊是一個(gè)轉(zhuǎn)移矩陣A移怯,Bn = A×Bn-1

  • 微觀上看矩陣


原始的序列是 [A,B],需要得到的序列是[A+B,A]这难,根據(jù)矩陣相乘定律舟误,可以得到轉(zhuǎn)移矩陣A

  • 總結(jié)一波
    現(xiàn)在需要求的是Bn%10000,Bn = B1 × An-1姻乓,B1[1,1]嵌溢,所以 An-1就是需要的答案。那把求Bn改成了求An-1有什么好處呢蹋岩?見下文

矩陣快速冪的實(shí)際運(yùn)用

上述矩陣代替斐波那契數(shù)列的方式赖草。已經(jīng)把斐波那契數(shù)列從原先的遞加變成了現(xiàn)在的遞乘。那變成遞乘有什么用?

如果斐波那契第一項(xiàng)是A剪个,第二項(xiàng)是A2秧骑,第三項(xiàng)是A3,第4項(xiàng)是A4 ……
那第8項(xiàng)的值是扣囊,A8 = A4×A4

換句話說乎折,第4項(xiàng)的值 × 第4項(xiàng)的值 = 第8項(xiàng)的值

求第16項(xiàng)的值
原來的方式是:1->2->3->4……->15->16需要經(jīng)過15次運(yùn)算
現(xiàn)在的方式是:1->2->4->8->16只需要經(jīng)過4次運(yùn)算

為什么只需要經(jīng)過4次?從A->A2得到第2項(xiàng)侵歇,我要求第4項(xiàng)不需要經(jīng)過第3項(xiàng)骂澄,直接A2 × A2 -> A4就可以得到第4項(xiàng)。這又回到了剛開始的快速冪

具體怎么得到惕虑,下面請(qǐng)看代碼解析

代碼拆分

代碼不長(zhǎng)酗洒,先看下面這個(gè)函數(shù)。這里使用了引用傳遞枷遂,即a數(shù)組發(fā)生變化后樱衷,傳入的實(shí)參也會(huì)改變。例如傳入數(shù)組a數(shù)組b酒唉,得到的數(shù)組a = 數(shù)組a × 數(shù)組b矩桂。即Matrix(cot,temp)執(zhí)行的結(jié)果是cot *= temp,且cottemp都表示的是矩陣數(shù)組

void Matrix(int (&a)[2][2],int b[2][2]){   //矩陣乘法
    int tmp[2][2] = {0};
    for(int i = 0; i < 2; ++i)
        for(int j = 0; j < 2; ++j)
            for(int k = 0; k < 2; ++k)
                tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % N;  //矩陣相乘,注意mod
    for(int i = 0; i < 2; ++i)
        for(int j = 0; j < 2; ++j)  
            a[i][j] = tmp[i][j]; //賦值返回?cái)?shù)據(jù)
}

A1侄榴,也就是轉(zhuǎn)移矩陣雹锣,題中已經(jīng)給出了,用數(shù)組表示是[1,1,1,0]癞蚕。這里還需要一個(gè)A0蕊爵,A0其實(shí)就是單位矩陣E(E × 任意矩陣A = A)。單位矩陣用數(shù)組表示就是[1,0,0,1](對(duì)角線都是1的矩陣)

while(n){
       if(n & 1) Matrix(cot,temp);   //如果是奇數(shù)
       Matrix(temp,temp);   
       n /= 2;  //不斷除2
}

上述5行代碼是矩陣快速冪關(guān)鍵(至于怎么得到這5行代碼的桦山,在上述快速冪的解釋中應(yīng)該已經(jīng)可以得到了)攒射。cot數(shù)組一開始是A0temp數(shù)組一開始是A1恒水。我們來模擬下求第13項(xiàng)的斐波那契值是怎么求的会放。

第一步:n = 13,執(zhí)行Matrix(cot,temp)Matrix(temp,temp)钉凌,得到cot數(shù)組為A1咧最,t數(shù)組為A2n = 6

第二步:n = 6御雕,執(zhí)行Matrix(temp,temp)矢沿,得到cot數(shù)組為A1t數(shù)組為A4酸纲,n = 3

第三步:n = 3捣鲸,執(zhí)行Matrix(cot,temp)Matrix(temp,temp),得到cot數(shù)組為A5福青,t數(shù)組為A8n = 1

第四步:n = 1脓诡,執(zhí)行Matrix(cot,temp)Matrix(temp,temp)无午,得到cot數(shù)組為A13t數(shù)組為A16祝谚,n = 0

退出while循環(huán)并輸出cot數(shù)組對(duì)應(yīng)的值宪迟。一共進(jìn)行了4步

完整代碼

#include <iostream>
using namespace std;
int N = 10000,n;
void Matrix(int (&a)[2][2],int b[2][2]){
    int tmp[2][2] = {0};
    for(int i = 0; i < 2; ++i)
        for(int j = 0; j < 2; ++j)
            for(int k = 0; k < 2; ++k)
                tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % N;
    for(int i = 0; i < 2; ++i)
        for(int j = 0; j < 2; ++j)
            a[i][j] = tmp[i][j];
}
int main(){
    while(cin>>n && n!=-1){
        int temp[2][2] = {1, 1, 1, 0},cot[2][2] = {1, 0, 0, 1};
        while(n){
            if(n & 1) Matrix(cot,temp);
            Matrix(temp,temp);
            n /= 2;
        }
        cout<<cot[0][1]<<endl;
    }
    return 0;
}

這篇文章主要利用斐波那契數(shù)列來淺談矩陣快速冪的用法交惯,矩陣快速冪還有更大的使用空間次泽,后續(xù)有空會(huì)補(bǔ)上矩陣快速冪的其他用法,另外送上一篇文章 十個(gè)利用矩陣乘法解決的經(jīng)典題目

后續(xù)學(xué)習(xí)的傳送門

矩陣快速冪——進(jìn)階
矩陣快速冪——實(shí)戰(zhàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末席爽,一起剝皮案震驚了整個(gè)濱河市意荤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌只锻,老刑警劉巖玖像,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異齐饮,居然都是意外死亡捐寥,警方通過查閱死者的電腦和手機(jī)笤昨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來握恳,“玉大人瞒窒,你說我怎么就攤上這事∠缤荩” “怎么了崇裁?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)就珠。 經(jīng)常有香客問我寇壳,道長(zhǎng),這世上最難降的妖魔是什么妻怎? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任壳炎,我火速辦了婚禮,結(jié)果婚禮上逼侦,老公的妹妹穿的比我還像新娘匿辩。我一直安慰自己,他們只是感情好榛丢,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布铲球。 她就那樣靜靜地躺著,像睡著了一般晰赞。 火紅的嫁衣襯著肌膚如雪稼病。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天掖鱼,我揣著相機(jī)與錄音然走,去河邊找鬼。 笑死戏挡,一個(gè)胖子當(dāng)著我的面吹牛芍瑞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播褐墅,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼拆檬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了妥凳?” 一聲冷哼從身側(cè)響起竟贯,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逝钥,沒想到半個(gè)月后澄耍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年齐莲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了痢站。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡选酗,死狀恐怖阵难,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芒填,我是刑警寧澤呜叫,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站殿衰,受9級(jí)特大地震影響朱庆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闷祥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一娱颊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凯砍,春花似錦箱硕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至座泳,卻和暖如春惠昔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挑势。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工镇防, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薛耻。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓营罢,卻偏偏與公主長(zhǎng)得像赏陵,于是被迫代替她去往敵國(guó)和親饼齿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容