野生前端的數據結構練習(11)動態規划算法

来源:https://www.cnblogs.com/dashnowords/archive/2018/12/05/10073292.html
-Advertisement-
Play Games

一.動態規划算法 被認為是一種與遞歸相反的技術,遞歸是從頂部開始分解,通過解決掉所有分解出的問題來解決整個問題,而動態規劃是從問題底部開始,解決了小問題後合併為整體的解決方案,從而解決掉整個問題。 動態規劃在實現上基本遵循如下思路,根據邊界條件得到規模較小時的解,小規模問題合併時依據 遞推關係式 進 ...


一.動態規划算法

dynamic programming被認為是一種與遞歸相反的技術,遞歸是從頂部開始分解,通過解決掉所有分解出的問題來解決整個問題,而動態規劃是從問題底部開始,解決了小問題後合併為整體的解決方案,從而解決掉整個問題。

動態規劃在實現上基本遵循如下思路,根據邊界條件得到規模較小時的解,小規模問題合併時依據遞推關係式進行,也就是說較大規模的問題解可以由較小問題的解合併計算得到。最經典易懂的就是使用遞歸演算法和動態規划算法兩種不同的方式來計算斐波那契數列或求階乘的對比,動態規划算法的特性相當於對計算過程進行了緩存,避免了不必要的重覆計算。

本篇通過幾個典型的相關實例來學習和練習動態規划算法。

二.尋找最長公共子串

題目不難理解,例如在單詞“raven”“havoc”的最長公共子串就是av。最容易想到的演算法就是暴力求解,也稱為貪心演算法,在下一篇中會提供貪心演算法暴力求解最長公共子串的示例代碼。

演算法描述如下:

字元串1長為m,字元串2長為n,先生成一個m*n的矩陣L並將其中都填充為0,矩陣中的值L[x,y]表示如果存在公共字元串,那麼該字元串在字元串1中的位置為從x-L[x,y]到x,在字元串2中的位置為從y-L[x,y]到y,換句話說:L[x,y]記錄瞭如果當前位置為公共子串的截止點時公共子串的長度

遞推關係式如下:

str1[x] === str2[y], L[x,y] = L[x-1,y-1] + 1;

str1[x] !== str2[y], L[x,y] = 0;

從圖中可以更清晰地看到動態規划算法在尋找公共子串時的過程:

該表從左上角開始填空,迴圈遍歷每個格子,如果str1中的字元和str2中的某個字元相同,則需要找到其左上角格子的數字,然後加1填在自己的格子里,如果不相等則填0,最終記錄表中最大的值即為最長公共子串的結束位置,列印出最長公共子串也就很容易實現了。

參考代碼:

/**
 * 動態規劃求解最長公共子串
 */
function lcs(str1,str2) {
    let record = [];
    let max = 0;
    let pos = 0;
    let result = '';
    //初始化記錄圖
    for(let i = 0; i < str1.length; i++){
        record[i] = [];
        for(let j = 0; j < str2.length; j++){
            record[i][j] = 0;
        }
    }
    //動態規劃遍歷
    for(let i = 0; i < str1.length; i++){
        for(let j = 0; j < str2.length; j++){
            if (i === 0 || j === 0) {
                record[i][j] = 0;
            }else{
                if (str1[i] === str2[j]) {
                     record[i][j] = record[i-1][j-1] + 1;
                } else {
                     record[i][j] = 0;
                }
            }
            //更新最大值指針
            if (record[i][j] > max) {
                max = record[i][j];
                pos = [i];
            }
        }
    }
    //拼接結果
    if (!max) {
        return '';
    } else {
        for(let i = pos ; i > pos - max ; i--){
            result = str1[i] + result;
        }
        return result;
    }
}

console.log(lcs('havoc','raven'))
console.log(lcs('abbcc','dbbcc'))

三.0-1背包問題遞歸求解

0-1背包問題用遞歸或動態規劃都可以求解,通過本例和下一節的例子就可以對比兩種演算法相反的求解過程。

背包問題是演算法中一個經典的大類,從簡單到複雜一本書都講不完,本例中僅實現簡單的0-1背包問題,這類問題是指被放入背包的物品只能選擇放或者不放,不能只放入一部分。

0-1背包問題的描述是這樣的,假設保險箱中有n個物品,他們的尺寸存放在size[ ]數組中,每一個的價值存放在values[ ]數組中,你現在擁有一個背包,其容量為capacity,問應該裝哪些東西進背包,使得被裝入的物品總價值最大。

演算法描述和遞推關係式如下:

  1. 如果第n個物品無法放入背包,則最大價值等同於將其他物品放入背包時能得到的最大價值:

    maxValue = knapsack(capacity,size,values,n-1)

  2. 如果第n個物品能夠放入背包,則最大價值等同於下列兩種情況中較大的一個:

    2.1 放入物品n,maxValue = knapsack(capacity - size[n], size, values, n-1)+values[n]

    2.2 不放物品n,maxValue = knapsack(capacity, size, values, n-1)

代碼實現如下:

/**
 * 遞歸求解0-1背包問題
 * 演算法:
 * 1.如果單個物品體積超出背包容量,則肯定不拿
 * 2.如果單個物品體積未超出背包容量,則問題變為在下列兩種情況中取較大的值
 * 2.1 放入當前物品 knapsack(capacity - size[n-1], size, value, n-1) + value[n-1])
 * 2.2 不放入當前物品 knapsack(capacity, size, value, n-1) 
 */
function max(a,b) {
    return a>b?a:b;
}

/**
 * 
 * @param  {[type]} capacity 背包容量
 * @param  {[type]} size     物品體積數組
 * @param  {[type]} value    物品價值數組
 * @param  {[type]} n        物品個數
 * @return {[type]}          最大價值
 */
function knapsack(capacity, size, value, n) {
    //如果沒有物品或者背包容量為0 則無法增值
    
    if (n == 0 || capacity == 0 ) {
        return 0;
    }
    if (size[n-1] > capacity) {
        //演算法步驟1 從最後一個物品開始,如果單個物品超出容量限制則不放入
        return knapsack(capacity, size, value, n-1);
    } else {
        //演算法步驟2
        return max(knapsack(capacity - size[n-1], size, value, n-1) + value[n-1],knapsack(capacity, size, value, n-1));
    }
}

let value = [4,5,10,11,13];
let size = [3,4,7,8,9];
let capacity = 16;
let n = 5;

let result = knapsack(capacity, size, value, n);
console.log('result:',result);

可以看到代碼基本只是用程式語言實現了演算法描述併進行了一些邊界條件的處理,並沒有進行任何實現方法上的優化,從它不斷調用自身就可以看出這是一個很明顯的遞歸方法。在遞歸方法下,由於重覆訪問計算的問題,很難列印出最終到底選擇了哪些物品,而在下麵的動態規劃演算法的解法中就相對容易實現。

四.0-1背包問題動態規劃求解

動態規划算法來求解0-1背包問題,核心遞推關係上並沒有什麼差異,但正如開頭所講,動態規劃的優勢就是對計算過的結果進行了緩存,所以採用這個演算法進行0-1背包問題求解得到最終結果後,可以再自頂向下反向查詢從緩存的表格中查出這個解的實現路徑,從而就可以判斷每個物品是否被選入當前解。

動態規划算法實現如下:

/**
 * 動態規劃求解0-1背包問題
 */
function max(a,b) {
    return a>b?a:b;
}

/**
 * 
 * @param  {[type]} capacity 背包容量
 * @param  {[type]} size     物品體積數組
 * @param  {[type]} value    物品價值數組
 * @param  {[type]} n        物品個數
 * @return {[type]}          最大價值
 */
function knapsack(capacity, size, value, n) {
    //K[n][capacity]表示0~n-1這n個物品入選時的最優值
    let K = [];
    let pick = [];
    let result = 0;
    for (let i = 0; i <= n ; i++){
       K[i] = [];
       for(let j = 0; j <= capacity; j++){
          if(j === 0 || i === 0){
            //i=0為防禦性編程,沒有實際意義
            //j=0表示背包容量為0,無法放入故結果為0
            K[i][j] = 0;
          } else if (size[i-1] > j){
            //如果背包容量比第i個物品的重量還小,則第i個物品必然無法放入,相當於前i-1個物品放入j容量背包時的最值
            K[i][j] = K[i-1][j];
          } else {
            //動態規劃解,當第i個物品可以放入時,K[i][j]等同於放入i時最值和不放i時的值取最大
            K[i][j] = max(K[i-1][j-size[i-1]] + value[i-1], K[i-1][j]);
          }
       }
    }
    result = K[n][capacity];
    
    //如何求解到底選了哪些物品?
    while(n > 0){
        if (K[n-1][capacity - size[n-1]] + value[n-1] > K[n-1][capacity]) {
            capacity -= size[n-1];
            n--;
            pick[n] = 1;
        } else {
            n--;
            pick[n] = 0;
        }
    }
    console.log('答案的選擇情況為:',pick);
    return result;
}

let value = [4,5,10,11,13];
let size = [3,4,7,8,9];
let capacity = 16;
let n = 5;

let result = knapsack(capacity, size, value, n);
console.log('結果:',result);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 參考代碼可見: "https://github.com/dashnowords/blogs/tree/master/Structure/GreedyAlogrithm" 一.貪心演算法 屬於比較簡單的演算法,它總是會選擇當下最優解,而不去考慮單次遞歸時是否會對未來造成影響,也就是說不考慮得到的解是否是 ...
  • 基本語法: 1、雙向數據綁定 vue react angular --ngMel(屬性型指令) 2、條件渲染: vue react angular *ngIf(結構型指令) 3、列表渲染: vue react angular angular小案例:Todos ...
  • 貪吃蛇 對不起,您的瀏覽器不支持canvas ...
  • 一、Object.creat()使用方法 Object.creat(對象); 功能:實現繼承,創建一個原型繼承自參數的對象。 什麼是原型式繼承:就是利用修改原型鏈的結構(增加一個節點中的成員,刪除一個節點中的成員,修改一個節點中的成員),來使得實例化對象可以使用整條鏈中的所有成員。 相容方式: fu ...
  • 一、需要實現的效果 這裡使用jQuery來實現。需要實現的效果如下:當下拉條改變時,單選框選中的值隨之變化。 二、註意 當設置單選框的checked屬性時,要使用jQuery的prop()方法,不能夠使用jQuery的attr()方法,attr()只適合簡單html元素設置。 jQuery的prop ...
  • CSS盒模型的概念與分類 CSS盒模型就是一個盒子,封裝周圍的HTML元素,它包括內容content、邊框border、內邊距padding、外邊距margin。 CSS盒模型分為標準模型和IE模型; 標準模型和IE模型的區別 標準模型的width是內容content 的寬度; 設置方式: box- ...
  • 項目中基於skyline的瀏覽器插件進行二次開發,基本的業務操作模式基本上是:點擊功能按鈕開啟操作模式,onFrame預選Feature,onLButtonUp選定Feature並執行業務操作,OnRButtonUp結束操作模式等。這裡封裝了這個基本操作模式,避免代碼重覆,便捷功能擴展和統一。 ...
  • 聲明 本系列文章內容全部梳理自以下幾個來源: 《JavaScript權威指南》 "MDN web docs" "Github:smyhvae/web" "Github:goddyZhao/Translation/JavaScript" 作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...