矩陣連乘

問題定義

  • 輸入:給定n個矩陣,矩陣間連乘

    a1 ... an : ai 與 ai + 1 可乘

    令a1:p0 * p1

      ...
    

    an:pn-1 * pn

    則:輸入用一維數(shù)組表示,P:{p0p1p2...pn}

  • 輸出:求最少乘法次數(shù)的計算方法

    • 特征:無論在哪里斷開嫂拴,分開部分的矩陣作為一個整體不影響整個矩陣的連乘

    • 對于矩陣序列,不同的乘次序?qū)?dǎo)致不同的乘法次數(shù)

    • 例如:a1 * a2 * a3的乘法次數(shù)

      (a1 * a2) * a3 :(p0 * p1 * p2) + (p0 * p2 * p3)

      a1 * (a2 * a3):(p1 * p2 * p3) + (p0 * p1 * p3)

    • 找出最優(yōu)計算次序以使得矩陣連乘所需要的計算次數(shù)最少

蠻力法

枚舉每一種劃分括號的情況晒旅,計算出他們的乘法次數(shù),取最小的劃分情況

A1A2A3...An:添加括號汪诉,直到只有一個矩陣可以加括號废恋,然后計算他們的連乘次數(shù)

A1(A2(A3...An))的連乘次數(shù):p2 * ... * pn + p1 * p3 * pn + p0 * p1 * pn

遞歸分治

優(yōu)化暴力枚舉(用k表示分割的位置)

每次調(diào)用遞歸式時:傳入分割后的矩陣列

遞歸出口:當(dāng)傳入的矩陣列中只有一個矩陣,返回0

回溯處理:計算兩邊分割乘法次數(shù)的和扒寄,計算兩邊合并的乘法次數(shù)再相加鱼鼓,取某個分割位置k時的最小值
遞歸式: F(1,n) = \begin{cases} \sum_{k=1}^n(min{F(1,k)} + F(k+1, n) + P_0 \times P_k \times P_n), &\ n\ \gt\ 1\\ 0, &\ n\ = 1\\ \end{cases}

  • 優(yōu)點:存在子問題最優(yōu)解
  • 缺點:存在重復(fù)計算部分
    • 為什么
      1. 遞歸使用幀無法保存太多的信息
      2. 子問題重疊,因為不同的部分劃分可能由相同的子問題組成该编,那么遞歸調(diào)用中出現(xiàn)重復(fù)計算的子問題是隨著遞歸深度變化的

動態(tài)規(guī)劃法

  • 使用空間換時間迄本,將子問題記錄下來

    • 那么就將遞歸分治的缺點改為優(yōu)點,這下子课竣,解決方法就是利用了動態(tài)規(guī)劃的思想
  • 令 m(i,j) :為i嘉赎,j之間的矩陣列的最小乘法次數(shù),在這之間使用k作為劃分的位置

    • m(i,j) = \begin{cases} \sum_{k=i}^j(min{F(i,k)} + F(k+1, j) + P_{i-1} \times P_k \times P_n), &\ i\ \lt\ j\\ 0, &\ i\ = j\\ \end{cases}

    • 根據(jù)上述定義于樟,二維表m的對角線上都是0公条,m(1,n)是我們需要的最少乘法次數(shù)

  • 令s(i,j):為i,j之間矩陣列的最優(yōu)分割位置

  • 時間復(fù)雜度:

    • for(1 to n):枚舉序列長度

      • for(0 to r):枚舉矩陣子列的起點

        • j = r

          for(k = i + 1 to j):枚舉矩陣子序列分割點

          s表記錄最優(yōu)的分割位置迂曲、m表最少乘法次數(shù)
          
    • 線性時間讀s表靶橱,構(gòu)造最優(yōu)矩陣連乘的計算步驟

import java.util.Arrays;

public class MatrixChainTest {

    /**
     * @param p:矩陣維數(shù)序列,其長度減一為連乘矩陣的個數(shù)
     * @param memory:ai*...*aj的最小數(shù)乘次數(shù)
     * @param s:最佳括號斷開位置
     * 操作數(shù)組時路捧,從下標(biāo)開始
     */
    public static void matrixChain(int[] p, int[][] memory, int[][] s) {
        int n = p.length - 1;//矩陣個數(shù)
        //初始狀態(tài)對角線置為0
        for(int i = 0; i < memory.length; i++) {
            memory[i][i] = 0;
        }
        //矩陣長度從二(下標(biāo)0关霸,1)開始,計算不同子矩陣的數(shù)乘次數(shù)
        for(int r = 1; r < n; r++) {//r+1:矩陣列長度
            for(int i = 0; i < n - r; i++) {//從第一個矩陣開始杰扫,枚舉r+1長度的矩陣列(的起點)
                int j = i + r;//矩陣列:A_(i+1)*...*A_(j+1)
                //矩陣列長度:j - i + 1
                //斷開位置(矩陣列的第幾個(從1開始算)位置)與下標(biāo)關(guān)系:x_斷 = x_(下+1)
                //設(shè)最先斷開的位置是A_(i+1)
                memory[i][j] = memory[i][i] + memory[i+1][j] + p[i] * p[i + 1] * p[j + 1];
                s[i][j] = i + 1;
                //枚舉斷開位置队寇,在矩陣列:A_(i+1)*...*A_(j+1)的從 i+2 到 j+1
                for(int k = i + 1; k < j; k++) {//斷開位置從 k + 1開始
                    int temp = memory[i][k] + memory[k+1][j] + p[i] * p[k + 1] * p[j + 1];
                    if(temp < memory[i][j]) {
                        memory[i][j] = temp;
                        s[i][j] = k + 1;
                    }
                }
            }
        }
        System.out.println("矩陣連乘的最少乘法次數(shù)為" + memory[0][n - 1]);
    }

    /**
     * 利用矩陣序列最優(yōu)的分割位置s,構(gòu)造最優(yōu)計算次序
     * @param s:最優(yōu)分割位置
     * @return:矩陣序列p的最優(yōu)計算次序
     */
    private static String matrixChainMake(int[][] s) {
        int n = s.length;
        String[] temp = new String[n];
        //矩陣序列初始化
        for(int i = 0; i < n; i++) {
            temp[i] = "A" + i;
        }
        //加括號
        for(int i = 0; i < n - 2; i++) {
            int k = s[i][n - 1];//獲得分割的位置
            if(k - i > 1) {//括號包住兩個以上的矩陣列
                temp[i] = "(" + temp[i];
                temp[k - 1] += ")";
            }
            if(n - k > 1) {
                temp[k] = "(" + temp[k];
                temp[n - 1] += ")";
            }
        }
        //最后答案處理
        StringBuilder rs = new StringBuilder();
        for(int i = 0; i < temp.length; i++) {
            rs.append(temp[i]);
        }
        System.out.println("最優(yōu)計算次序為:" + rs.toString());
        return rs.toString();
    }

    /**
     *外部調(diào)用入口
     * @param p:矩陣列的維數(shù)
     * @return 返回矩陣連乘的最優(yōu)計算次序
     */
    public static String matrixChain(int[] p) {
        int[][] m = new int[p.length - 1][p.length - 1];
        int[][] s = new int[p.length - 1][p.length - 1];
        MatrixChainTest.matrixChain(p, m, s);
        System.out.println("m:");
        array2dShow(m);
        System.out.println("s:");
        array2dShow(s);
        return matrixChainMake(s);
    }

    public static void main(String[] args) {
        int[] p = {30, 35, 15, 5, 10, 20, 25};
        matrixChain(p);
    }

    private static void array2dShow(int[][] s) {
        for(int i = 0; i <s.length; i++) {
            System.out.println(Arrays.toString(s[i]));
        }
    }

}

相關(guān)鏈接:

學(xué)習(xí)別人的思想窗声,總結(jié)自己的想法:

  • 分析一個問題的解決方法,很容易想到暴力辜纲,然后再想著優(yōu)化笨觅,發(fā)現(xiàn)問題可以被分割成獨立的子問題拦耐,例如這題就朝分治遞歸去了;再分析遞歸分治的過程见剩,發(fā)現(xiàn)存在重復(fù)計算的子問題杀糯,那么出現(xiàn)了動態(tài)規(guī)劃問題的特性:最優(yōu)子結(jié)構(gòu)、子問題重疊
  • 一步步的分析才能得到結(jié)果苍苞,不是觀測就來的

動態(tài)規(guī)劃基本步驟:
1.找出最優(yōu)解的性質(zhì)固翰,并刻劃其結(jié)構(gòu)特征。
2.遞歸地定義最優(yōu)值羹呵。
3.以自底向上的方式計算出最優(yōu)值骂际。
4.根據(jù)計算最優(yōu)值時得到的信息,構(gòu)造最優(yōu)解冈欢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末歉铝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子凑耻,更是在濱河造成了極大的恐慌太示,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件香浩,死亡現(xiàn)場離奇詭異先匪,居然都是意外死亡,警方通過查閱死者的電腦和手機弃衍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門呀非,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人镜盯,你說我怎么就攤上這事岸裙。” “怎么了速缆?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵降允,是天一觀的道長。 經(jīng)常有香客問我艺糜,道長剧董,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任破停,我火速辦了婚禮翅楼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘真慢。我一直安慰自己毅臊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布黑界。 她就那樣靜靜地躺著管嬉,像睡著了一般皂林。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚯撩,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天础倍,我揣著相機與錄音,去河邊找鬼胎挎。 笑死沟启,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呀癣。 我是一名探鬼主播美浦,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼项栏!你這毒婦竟也來了浦辨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤沼沈,失蹤者是張志新(化名)和其女友劉穎流酬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體列另,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡芽腾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了页衙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摊滔。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖店乐,靈堂內(nèi)的尸體忽然破棺而出艰躺,到底是詐尸還是另有隱情,我是刑警寧澤眨八,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布腺兴,位于F島的核電站,受9級特大地震影響廉侧,放射性物質(zhì)發(fā)生泄漏页响。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一段誊、第九天 我趴在偏房一處隱蔽的房頂上張望闰蚕。 院中可真熱鬧,春花似錦枕扫、人聲如沸陪腌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诗鸭。三九已至,卻和暖如春参滴,著一層夾襖步出監(jiān)牢的瞬間强岸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工砾赔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝌箍,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓暴心,卻偏偏與公主長得像妓盲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子专普,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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