最近因爲項目任務較少,手上有很多空閒的時間,因此抽空研究了一下矩陣行列式的算法。java
先來講說行列式,如下摘自百度百科:算法
行列式在數學中,是由解線性方程組產生的一種算式。行列式的特性能夠被歸納爲一個屢次交替線性形式,這個本質使得行列式在歐幾里德空間中能夠成爲描述「體積」的函數。編程
[1] 其定義域爲nxn的矩陣A,取值爲一個標量,寫做det(A)或 | A | 。行列式能夠看做是有向面積或體積的概念在通常的歐幾里得空間中的推廣。或者說,在n維歐幾里得空間中,行列式描述的是一個線性變換對「體積」所形成的影響。不管是在線性代數、多項式理論,仍是在微積分學中(好比說換元積分法中),行列式做爲基本的數學工具,都有着重要的應用。 行列式概念最先出如今解線性方程組的過程當中。十七世紀晚期,關孝和與萊布尼茨的著做中已經使用行列式來肯定線性方程組解的個數以及形式。十八世紀開始,行列式開始做爲獨立的數學概念被研究。數組
十九世紀之後,行列式理論進一步獲得發展和完善。矩陣概念的引入使得更多有關行列式的性質被發現,行列式在許多領域都逐漸顯現出重要的意義和做用,出現了線性自同態和向量組的行列式的定義。less
n階行列式的計算公式:函數
設有n²個數,排列成n行n列的表工具
a11 a12 ... a1nspa
a21 a22 ... a2ncode
... ... ... ...遞歸
an1 an2 ... ann
做出表中位於不一樣行不一樣列的n個數的乘積,並冠以符號(-1)t,獲得形如 (-1)t a1p1*a2p2 ... anpn的項,其中p1,p2,....pn爲天然數1,2,...n的一個排列,t爲這個排列的逆序數,因爲這樣的排列共有n!個,所以形如上式的項共有n!項,全部這n!項的代數和。
關於逆序數:
在一個排列中,若是一對數的先後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱爲一個逆序。一個排列中逆序的總數就稱爲這個排列的逆序數。逆序數爲偶數的排列稱爲偶排列;逆序數爲奇數的排列稱爲奇排列。如2431中,21,43,41,31是逆序,逆序數是4,爲偶排列。
知道了算法,如今考慮其編程實現:
首先須要一個能計算逆序數的函數,將其命名爲 inverse
/** * Inverse number * * @param s * @return */ public static int inverse(int[] s) { int t = 0; for (int i = 0; i < s.length - 1; i++) for (int j = i + 1; j < s.length; j++) if (s[i] > s[j]) t++; return t; }
其實現相對簡單,對於數組中的每個數,判斷它以後比它小的數,累加以後的總數即爲逆序數。
其次須要一個能返回0~n-1的全部排列狀況的函數,一共有n!種排列。要返回全部的排列並不難,咱們但願的狀況是能把0~n!-1這n!個數和n!種排列對應起來方便在計算項的時候進行調用。爲了方便,先定義一個階乘函數factorial
public static int factorial(int n) { return n == 0 ? 1 : n * factorial(n - 1); }
再來考慮一下思路,階乘來源於排列,那麼排列的問題也能夠歸爲階乘的問題。如今有下面一種思路:對於1~n(這裏只是爲了方便說成1~n,實際中考慮0~n-1更好)的排列,先讓n在n個位置中挑選一個,將(1~n!)分爲n段,每一段有(n-1)!個數,給定的數index在哪一個段就挑選哪一個位置,以後考慮用遞歸挑選剩下的位置。到最後沒有位置的時候,就對應一種排列,每個給定的index都對應一種惟一的排列,這就完成了咱們願望。
/** * * @param n * the serial number size * @param index * index of all arrangements * @return A possible order from 1 to n by the given index that is from 0 * to n! - 1 */ public static int[] order(int n, int index) { if (n < 1) throw new IllegalArgumentException("The size of number array could not less than 1"); if (index >= factorial(n) || index < 0) throw new IllegalArgumentException("The index could not be reached"); int[] nums = new int[n];//java數組在初始化時自動置0,不須要手動置0 fillArray(n, nums, index); return nums; } private static void fillArray(int n, int[] nums, int index) { if (n == 0) return; int fac = factorial(n - 1); int p = index / fac + 1; int i = -1;//此處爲填充還未填充的位置,即值爲0的位置 while (p > 0) { if (nums[++i] == 0) p--; } nums[i] = n; fillArray(n - 1, nums, index % fac); }
最後是行列式算法,有了上面的基礎相對容易,注意此處的A矩陣應該通過驗證,每一行都含有相同數目的項,即列數相等,事實上在個人實現中它是一個包裝類。
public static float det(float[][] A) { float det = 0f; int m = A.length; int n = A[0].length; if (m != n) return det; int fac = factorial(n); for (int i = 0; i < fac; i++) { // 返回一個排列 int[] order = order(n, i); // 逆序數肯定項的正負性 float item = (inverse(order) & 1) == 0 ? 1 : -1; for (int j = 0; j < n; j++) item *= A[j][order[j]-1]; det += item; } return det; }