遞歸算法之n階矩陣行列式求解

最近高等代數(shù)正好講到這里,此篇文章正好對所學(xué)知識做一個具體程序?qū)嵺`惕稻。

設(shè)計算法時使用遞歸的思想是一個程序員的基本素質(zhì)栋齿,遞歸可以把一個很龐大的問題轉(zhuǎn)化為規(guī)模縮小了的同類問題的子問題狡刘,通過這一思想享潜,我們編程時運用遞歸可以使用很少的代碼來處理很大的問題。這篇文章將會講到遞歸算法的運用嗅蔬。

在數(shù)學(xué)中剑按,很多數(shù)學(xué)函數(shù)都是由一個公式來表達(dá)的,比如 f(x) = 100x; 這個公式可以讓我們把長度米轉(zhuǎn)換成長度厘米澜术。 有了這個公式艺蝴,在程序中敲出一行代碼,寫出一個函數(shù)(function)來計算實在是太簡單方便了鸟废,就像這樣猜敢。

int convert(int m){
    return 100*m;
}

我們就寫好了一個函數(shù)來進(jìn)行米到厘米的單位換算。

但是有的時候盒延,數(shù)學(xué)函數(shù)會以不太標(biāo)準(zhǔn)的形式來定義缩擂,比如這個函數(shù),他滿足 f(0)=0而且 f(x) = 2f(x-1)+x; 從這個函數(shù)定義我們可以得出 f(1)=1;f(2)=3;等等添寺。當(dāng)一個函數(shù)用他自己來定義時就稱為這個函數(shù)是遞歸的胯盯。

通俗地講,就是從前有個山计露,山里有個廟,廟里有個老和尚再給小和尚講故事票罐,講的是:從前有個山叉趣,山里有個廟,廟里有個老和尚再給小和尚講故事胶坠。君账。。沈善。這就是遞歸乡数。

好了說了這么多你們肯定還是一頭霧水椭蹄,現(xiàn)在來實踐一下。

遞歸求階乘

剛開始學(xué)編程的同學(xué)一定會寫求階乘的函數(shù)净赴,使用for循環(huán)或者while循環(huán)都可以绳矩,但是遞歸卻完全用不上這兩個循環(huán)。

public static int factorial(int a){
 
    if (a==0 || a==1){
        return 1;
    }
 
    return a*factorial(a-1);
}

上面的代碼就是遞歸求階乘的方法玖翅,a是需要傳入的參數(shù)翼馆,比如我們要求5的階乘就傳入5這樣factorial函數(shù)最終的返回值為120;

分析這段代碼金度,他的第3行到第五行處理了 基準(zhǔn)情況(Base Case)应媚,在這個情況下,函數(shù)的值可以直接算出而不用求出遞歸猜极。就像上文提到的函數(shù)f(x) = 2f(x-1)+x;如果沒有f(0)=0這個事實在數(shù)學(xué)上沒有意義一樣中姜。 再編程中,如果沒有基準(zhǔn)情況也是無意義的跟伏。第7行執(zhí)行的是遞歸調(diào)用丢胚。

所以所,設(shè)計遞歸算法受扳,需要包含以下兩個基本法則:

1携龟、基準(zhǔn)情形(Base Case),必須總要有某些基準(zhǔn)的清醒勘高,在這個情形中峡蟋,不執(zhí)行遞歸就能求解。

2相满、不斷推進(jìn)(Making Progress)层亿,對于需要遞歸求解的情形,遞歸調(diào)用必須總能夠朝著一個基準(zhǔn)情形推進(jìn)立美。這樣的話,不斷推進(jìn)到基準(zhǔn)情形方灾,到達(dá)基準(zhǔn)情形的時候遞歸才會推出建蹄,得到返回值。

n階矩陣行列式的求解

有了剛在知識的鋪墊裕偿,現(xiàn)在我們可以動手寫一個程序來用遞歸計算n階矩陣的行列式了洞慎。

首先來看下二階矩陣的求法

也就是說2×2矩陣的元素交叉相乘再想減即可求出行列式。

接下來是3階矩陣

3×3矩陣求解中嘿棘,選擇任意行或者列劲腿,在那一行/列中,移除那個元素所在的行和列比如選擇a11,則移除第一行和第一列鸟妙,這樣矩陣就變成了2×2的焦人,再按照剛才的方法求2×2矩陣的行列式即可挥吵。之后整個行或列的3個元素進(jìn)行此類運算后相加就是3×3的行列式。

n x n矩陣

n階矩陣就和3階矩陣求解的方法一樣了花椭,使用3×3求解的方法忽匈,比如4階矩陣,將4階消除成3階矿辽,然后再變成2階來算丹允。但是矩陣每上升一個維度,計算量就會擴大很多袋倔。

知道了n階矩陣行列式的計算思路后雕蔽,就可以開始編寫算法了。

首先是數(shù)據(jù)結(jié)構(gòu)設(shè)計宾娜,我們需要設(shè)計一個矩陣類來提供便利萎羔,這個類有兩個成員,一個二維數(shù)組碳默,用來儲存矩陣贾陷,一個整數(shù),來儲存矩陣的行數(shù)或列數(shù)(行列式必須是方矩陣才可以求解所以行列無所謂)嘱根。

以下是整個Matrix類的設(shè)計:

static class Matrix{
 
    private int rowNum=0;
 
    private int[][] array;
 
    //Constructor
    public Matrix(int rowNum){
        this.rowNum = rowNum;
        array = new int[rowNum][rowNum];
    }
 
    public Matrix(int[][] array){
        this.array = array;
        this.rowNum = array.length;
    }
 
    int counter = 0; //For add Element
 
    public void addElement(int a){
        array[(counter)/rowNum][counter%rowNum] = a;
        counter++;
    }
 
    //Print the instance itself
    public void printMat(){
        for (int i=0;i < rowNum ;i++){
            for (int j=0;j < rowNum ;j++){
                 System.out.print(array[i][j]+\t;);
            }
            System.out.println();
        }
    }
 
    //Setter and Getter
    public int getRowNum() {
        return rowNum;
    }
 
    public int[][] getArray() {
        return array;
    }
 
    public void setArray(int[][] array) {
        this.array = array;
    }
 
}

Matrix類中有兩個構(gòu)造方法:傳入整數(shù)a會初始化一個axa大小的空矩陣髓废,傳入一個二維數(shù)組的話即可根據(jù)二維初始化一個Matrix對象。

Matrix類中有一個方法比較特殊:addElement方法该抒,通過不斷調(diào)用這個函數(shù)即可向一個Matrix實例進(jìn)行有順序的負(fù)值慌洪,第一次調(diào)用則會更改第第一行第一列位置上的值,第二次調(diào)用則會更改第一行第二列上的值凑保,以此類推冈爹。

接下來就是設(shè)計一個MatrixCalculator類的,這個類中的一個成員方法可以求出行列式欧引,我命名他為getDet(); 在計算行列式的時候需要移除元素所在的行和列频伤,生成一個減小了一個維度的矩陣,我們需要編寫一個方法來完成這個操作芝此,我命名他為removeRowAndCol()憋肖;還有一個方法,由于相加的時候會產(chǎn)生符號的改變婚苹,所以需要寫一個方法來計算矩陣中一個元素的cofactor岸更,命名為getCofactor。

以下就是removeRowAndCol方法的代碼:傳入需要移除的行和列和一個Matrix對象膊升,函數(shù)會返回消除了指定行和列的Matrix對象怎炊。

public static Matrix removeRowAndCol(int row,int col,Matrix mat){
 
    int matRowNum = mat.getRowNum();
    int[][] arr = mat.getArray();
 
    Matrix matrix = new Matrix(matRowNum-1);
 
    for (int i = 0;i < matRowNum; i++){
        for (int j = 0 < matRowNum; j++){
                if (i!=row && j!=col) {
                    matrix.addElement(arr[i][j]);
                }
        }
    }
 
    matrix.printMat();
    return matrix;
 
}

以下是getCofactor方法:由于我的算法只會去遍歷矩陣第一列來進(jìn)行求解,所以得到Cofactor的代碼就變得簡單很多。

public static int getCofactor(int colNum){
    if (colNum%2 == 0){
        return 1;
    }else {
        return -1;
    }
}

接下來就是核心的遞歸求解行列式的算法了评肆,先理一下思路债查,遞歸算法的兩個要素:基準(zhǔn)情形,不斷推進(jìn)糟港。

對于n階矩陣什么是基準(zhǔn)情形呢攀操?就是矩陣被降為2×2維度的時候,直接返回交叉相乘的差即可秸抚,不斷推進(jìn)速和,如果是一個4階矩陣,算法會先把4將為3×3矩陣剥汤,然后3×3再拆成3個2×2矩陣來達(dá)到基準(zhǔn)情形來算出答案颠放,就和我們手算行列式時用到的方法一樣,手算時候也遵循這一算法吭敢。

public static int getDet(Matrix targetMatrix){
    //Base (Finally reduced to 2 x 2 Matrix)
    if (targetMatrix.rowNum == 2){
        int[][] arr = targetMatrix.getArray();
        // a*d - b*c
        return arr[0][0]*arr[1][1] - arr[0][1]*arr[1][0];
    }
 
    //MARK- Recursion: to reduce dimension
    int det = 0;
    int colNum = targetMatrix.rowNum;
    for (int i = 0; < colNum;i++){
        det+= (targetMatrix.getArray()[i][0])*getCofactor(i)*getDet(removeRowAndCol(i,0,targetMatrix));
    }
 
    return det;
}

只有不到20行代碼碰凶,但是卻可以解決nxn的矩陣,是不是很神奇鹿驼,這就是遞歸的優(yōu)勢欲低,把一個很龐大的問題轉(zhuǎn)化為規(guī)模縮小了的同類問題的子問題來求解畜晰。n階矩陣最后被降為若干個2×2矩陣砾莱。

加上適當(dāng)?shù)妮敵稣Z句,來求解一個3階矩陣行列式試一下:

2   1    2 
-1   5   21 
13 -1  -17

Element: 2
Cofactor: 1
Removing Row 0 Col 0
A 2 x 2 Matrix is initialized 
5 21 
-1 -17 
END State Reached
Det: -64

Element: -1
Cofactor: -1
Removing Row 1 Col 0
A 2 x 2 Matrix is initialized 
1 2 
-1 -17 
END State Reached
Det: -15

Element: 13
Cofactor: 1
Removing Row 2 Col 0
A 2 x 2 Matrix is initialized 
1 2 
5 21 
END State Reached
Det: 11
Result: 0

但是有一個問題需要注意凄鼻,就是這個算法的復(fù)雜度腊瑟。

算法復(fù)雜度

這個算法的復(fù)雜度為O(n!), 這意味著他的運行速度很慢,隨著問題規(guī)模的增長块蚌,時間會大幅度增長闰非。在我的電腦上,計算3×3到7×7內(nèi)規(guī)模的矩陣峭范,電腦都可以秒算出來财松,但是如果是一個10×10的矩陣,電腦需要54秒鐘虎敦,到了11×11時間將會變得更長游岳,下圖是這個算法隨著問題規(guī)模增長對運行時產(chǎn)生影響的曲線。

可以看出7×7矩陣需要遞歸517次其徙,到了10×10需要大約260萬次遞歸運算才能得到結(jié)果∨缁В可見問題規(guī)模增長后時間的開銷是十分巨大的唾那。

原文:http://miketech.it/recursion_algorithm/ 感謝此篇文章的作者!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市闹获,隨后出現(xiàn)的幾起案子期犬,更是在濱河造成了極大的恐慌,老刑警劉巖避诽,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件龟虎,死亡現(xiàn)場離奇詭異,居然都是意外死亡沙庐,警方通過查閱死者的電腦和手機鲤妥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拱雏,“玉大人棉安,你說我怎么就攤上這事≈郑” “怎么了贡耽?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鹊汛。 經(jīng)常有香客問我蒲赂,道長,這世上最難降的妖魔是什么刁憋? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任滥嘴,我火速辦了婚禮,結(jié)果婚禮上职祷,老公的妹妹穿的比我還像新娘氏涩。我一直安慰自己,他們只是感情好有梆,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布是尖。 她就那樣靜靜地躺著,像睡著了一般泥耀。 火紅的嫁衣襯著肌膚如雪饺汹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天痰催,我揣著相機與錄音兜辞,去河邊找鬼。 笑死夸溶,一個胖子當(dāng)著我的面吹牛逸吵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缝裁,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扫皱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起韩脑,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤氢妈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后段多,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體首量,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年进苍,在試婚紗的時候發(fā)現(xiàn)自己被綠了加缘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡琅捏,死狀恐怖生百,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柄延,我是刑警寧澤蚀浆,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站搜吧,受9級特大地震影響市俊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滤奈,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一摆昧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜒程,春花似錦绅你、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至领炫,卻和暖如春偶垮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帝洪。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工似舵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人葱峡。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓砚哗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砰奕。 傳聞我的和親對象是個殘疾皇子频祝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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