動態(tài)規(guī)劃: Subset Sum

這次我想給大家分享的內(nèi)容是動態(tài)規(guī)劃Subset Sum (子集之和)問題 。其實(shí)挺枯燥的,涉及到的是數(shù)學(xué)和編程方面的內(nèi)容,如果不感興趣請直接退出閱讀。

本文概要:

  • Subset Sum 問題描述
  • 問題求解思路
  • 遞歸法求解
  • 重點(diǎn):動態(tài)規(guī)劃法求解

Subset Sum 問題


假設(shè)有一串 { 1,2, 4,5,6} , 是否存在任意數(shù)字之和為 7 , 55 。
通過目測 { 1, 2,4} {5, 2} 之和為7,所以答案為存在,因?yàn)樗性刂托∮?5,所以答案為不存在。

問題:

假設(shè)有一個非負(fù)整數(shù)的集合 Set ,求是否存在一個組合使得其中任意元素之和為一個給定的整數(shù) sum ?

A = { X1,X2,....Xn}, sum = Y
subset(A,Y) = True or False ?

求解思路


  1. 我們立馬能想到第一個方法是先求解集合A 所有的排列組合,一共有2的5次方種可能,然后逐一對組合求和判斷是否等于6。這種思路屬于暴利求解法,當(dāng)集合元素非常多的時候,計(jì)算時間會指數(shù)級增長,該算法的時間復(fù)雜度為O(2^n)。

  2. 第二種求解方法的思想是 把大事化小,小事化無的。

如: A = { 1,2, 4,5,6} 可以簡化為 { 2, 4,5,6 } 這個子集合元素是否有sum = 6 的解,或者{ 2, 4,5,6 } 是否存在相加之和為5 的解 。依次類推可以將問題一層一層縮小,利用上篇文章提到的遞歸思想。

遞歸求解法


arr  = [1,2 ,4,5,6]

def  subset(arr , i , s):
    if i == 0:
        return s == arr[0]
    elif s == 0:
        return True
    elif arr[i] > s :
        return rec_subset(arr, i-1 , s)
    else:
        A =  subset(arr , i-1 ,s - arr[i])
        B =  subset(arr, i-1 , s)
        return A or B
    
print( subset(arr ,len(arr )-1, 7))
out: True

通過遞歸方式來求解必須遍歷所有的子問題,但其中子問題有重疊的部分,也就是說存在重復(fù)計(jì)算的情況,算法時間復(fù)雜度為O(2^n)。對于規(guī)模比較大的問題這是無法容忍的。接下來通過動態(tài)規(guī)劃的方法 ,來避免重復(fù)計(jì)算來優(yōu)化程序的性能。

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


動態(tài)規(guī)劃是從下往上(bottom-up)的求解方法,并且把中間結(jié)果緩存起來避免重復(fù)計(jì)算。
例如:把{ 1,2, 4,5,6} 分解成 空集 0 { } ,1 { 1 } ,2 { 1,2 } ,3 { 1,2, 4 }, ,4 { 1,2, 4,5 } ,5 { 1,2, 4,5,6} 分別來計(jì)算,具體流程如下。

  1. 構(gòu)建一個二維數(shù)組Subset[i,j] 來存放子集合相加和的所有可能情況,True(以下記為T)表示存在解,F(xiàn)alse(以下記為F)表示不存在解。
step1

如圖1: 行為子集相加為0-7所有的情況,列為0-5個元素的所有情況。
第一行表示: 除了第一列為0存在解標(biāo)記為T 之外,其余1-7都為 F 。

第一列表示:各個子集相加之和為0的情況。例如 {1 } 的情況顯然如果 不取元素1 空集{}之和存在0的解,類似{1,2} 也有存在0的解,因此第一列都為T。

第二行第二列:表示{1} 之和為1,顯然為T 。

第二行其余列:因?yàn)樽畲鬄?,所以余下都為F。

step2

中間任意一個格子計(jì)算方法,分兩種情況。

如果格子上方為T,當(dāng)前格子也為T。實(shí)際意義為:當(dāng)前集合的子集如果有解那么,當(dāng)前集合也有解,如{1,2,4} 之和3的情況,因?yàn)?{1,2} 存在之和為3的解。
因此得出結(jié)論,只要T下方的格子都可以設(shè)置為T。

如果格子上方為F, 則求 sum - A 余下子集合的解。例如上圖綠色標(biāo)記的位置 Subset[2,4] , 可以簡化為求解 { 1 } 是否有 1的解,查表值為T,當(dāng)前表格值則 為T。

用此算法,把表格填滿。

step3

表格最后一個元素就是 { 1,2, 4,5,6} 是否存在子集合之和為7 這個問題的解。

具體的代碼實(shí)現(xiàn):


import numpy as np

def dp_subset(arr,S):
    
    subset = np.zeros((len(arr),S+1),dtype=bool) #構(gòu)造二維數(shù)組
    subset[:,0] = True # 第一列 設(shè)為True
    subset[0,: ] = False #第一列 設(shè)為 False
    subset[0,arr[0]] = True
    
    for i in range(1,len(arr)):
        for s in range(1, S+1):
            if arr[i] > s:
                subset[i , s] = subset[i-1 , s]
            else:
                A = subset[i-1 , s ]
                B = subset[i-1 , s - arr[i]]
                subset[i,s] = A or B
                
                
    row ,cell = subset.shape
    return subset[row-1,cell-1]
            
arr  = [1,2, 4,5,6]            
dp_subset(arr,7) 
out:True

參考


  1. 視頻講解動態(tài)規(guī)劃

  2. Dynamic Programming – Subset Sum Problem

  1. Algorithm/Insights
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 分治策略 本文包括分治的基本概念二分查找快速排序歸并排序找出偽幣棋盤覆蓋最大子數(shù)組 源碼鏈接:https://gi...
    廖少少閱讀 1,992評論 0 7
  • 使用swift的Bundle來獲取項(xiàng)目的文件路徑 , 代碼如下: 一直打印獲取路徑失敗 解決方案: 打開build...
    紅姑娘閱讀 3,307評論 0 1
  • 炎熱的度假時光馬上就要開始了,一想到可以到海邊或者游泳池就覺得很興奮。不過,想要出門的話當(dāng)然也要專門準(zhǔn)備必備單品。...
    D3舍閱讀 639評論 0 1
  • 有時候我覺得自己很多事不能做,做不好,女兒還小很多不懂,在去往西安游玩的火車上,女兒與一個八歲男孩子的對話提醒了我...
    幼學(xué)瓊林閱讀 211評論 0 0
  • 每天依舊繁忙,從早到晚。一下班,人就精神抖擻,上班,人就沒有了精神。有點(diǎn)滑稽和可笑。 清早,...
    一簾思語閱讀 302評論 0 0

友情鏈接更多精彩內(nèi)容