遺傳演算法解決揹包問題(python)

2021-10-03 17:38:33 字數 3863 閱讀 6364

1.問題重述

給定n個物品,價值分別是:v1,v2,…,vn,重量分別是:w1,w2,…,wn。在物品不可分割的情況下,挑選物品放入承重為w的揹包,使得揹包內物品的價值最大,且揹包內物品的總重量小於w.

2.解決方案

本問題可用多種方法解決,這裡採用遺傳演算法來求解,以下是遺傳演算法的流程圖:

要採用遺傳演算法,則首先確定以下幾個要素:染色體的編碼方法、適值函式、染色體交叉和變異採用的方案、選擇策略

2.1 染色體的編碼方法

基因編碼的方法都有:二進位制、整數編碼、順序編碼、 實數編碼等

對於01揹包問題,由於每乙個物體都有選或者不選兩種情況,即可以用0、1來表示選擇或是沒有選擇。故可使用二進位制的編碼方法來對染色體編碼,且染色體長度為物體個數。

def

initpopulation

(self)

:"""初始化種群"""

self.lives =

for i in

range

(self.lifecount)

: gene =

[random.randint(0,

1)for x in

range

(self.genelenght)

]# 初始化染色體

life = life(gene)

# 生成個體

# 擴充套件種群

2.2 適值函式的選擇

在確定適值函式時,首先要確定是求最小值優化問題還是求最大值優化問題,當求最大值優化問題時,可直接將目標函式當做適值函式,求最小值優化問題時,通過變換將其變為求最大值優化問題。

確定目標函式時,要先確定求解目標以及約束條件。此問題的目標是求乙個x=(x1,x2…xn)(xi為0或1),從而使得v(總價值)最大,而約束條件則是w(揹包承重)。以下為我的目標函式:

f (x

)=∑i

=1nx

i∗vi

∑i=1

nxi∗

wi⩽w

f(x)=\sum_^n x_i*v_i \ \ \ \ \ \ \ \ \ \ \ \ \sum_^n x_i*w_i\leqslant w

f(x)=i

=1∑n

​xi​

∗vi​

i=1∑

n​xi

​∗wi

​⩽wf(x

)=∑i

=1nx

i∗vi

∑i=1

nxi∗

wi+1

−w∑i

=1nx

i∗wi

>

wf(x)=\frac^n x_i*v_i} ^n x_i*w_i+1-w} \ \ \ \ \ \ \ \ \ \ \ \ \sum_^n x_i*w_i>w

f(x)=∑

i=1n

​xi​

∗wi​

+1−w

∑i=1

n​xi

​∗vi

​​i=

1∑n​

xi​∗

wi​>

w為了讓物體盡可能裝滿揹包,故又加入了罰值函式:

p (x

)=1−

∣∑i=

1nxi

∗wi−

w∣δp(x)=1-\frac^n x_i*w_i-w\rvert} δ

p(x)=1

−δ∣∑

i=1n

​xi​

∗wi​

−w∣​

其中δ=ma

xδ=max\^*w_i-w\rvert\}

δ=ma

x從中不難看出,當總重越接近w時,p(x)越接近1。

最終,適值函式確定為:

f (x

)=f(

x)∗p

(x)f(x)=f(x)*p(x)

f(x)=f

(x)∗

p(x)

**如下:

def

matchfun

(self)

:"""適值函式"""

max =

max(self.allgoods,

abs(

sum(self.goods[i][0

]for i in

range

(len

(self.goods)))

- self.allgoods)

)return

lambda life: self.price(life.gene)[1

]/(self.price(life.gene)[0

]+1-self.allgoods)

*\ (1-

abs(self.price(life.gene)[0

]-self.allgoods)

/max) \

if self.price(life.gene)[0

]>self.allgoods \

else self.price(life.gene)[1

]*(1

-abs

(self.price(life.gene)[0

]-self.allgoods)

/max)

此函式的返回值為乙個由lambda定義的函式,這樣可以將它作為形參傳遞到其他類中呼叫,有點類似於c++中的函式指標。

2.3 交叉

此處採取的交叉策略是隨機從其他染色體上擷取一段基因,與原染色體上的此段基因片段交換,來得到新染色體

def

cross

(self, parent1, parent2)

:"""交叉"""

index1 = random.randint(

0, self.genelenght -1)

index2 = random.randint(index1, self.genelenght -1)

newgene =

newgene.extend(parent1.gene[

:index1]

) newgene.extend(parent2.gene[index1:index2]

) newgene.extend(parent1.gene[index2:])

self.crosscount +=

1return newgene # 返回交叉後的parent1.gene

2.4 變異

此處採取的變異策略是隨機改變染色體中一處基因的值(有一定概率此染色體會繼續變異,故此處用了遞迴)

def

mutation

(self, gene)

:"""突變,兩個基因互換位置"""

index1 = random.randint(

0, self.genelenght -1)

newgene = gene[:]

# 產生乙個新的基因序列,以免變異的時候影響父種群

newgene[index1]

=(newgene[index1]+1

)%2if random.random(

)< self.mutationrate:

self.mutation(newgene)

self.mutationcount +=

1return newgene

3. 原始碼

原始碼見

遺傳演算法解決揹包問題

總體思想與之前的相似,評價函式就是物品的價值之和,但要注意一旦物品的重量大於揹包的重量,那麼該條染色體的倖存概率為0 基因就是每乙個物品是否選擇,這裡預設有10條染色體在比較,並且每一條染色體上的第i個基因就是代表第i個物品是否選擇 突變就是隨機選擇的染色體的隨機位置由0變1,由1變0 另外建議把變...

遺傳演算法 01揹包問題 C

include include include include include using namespace std const int pack max w 80 揹包最大承受重量 const int pack max v 75 揹包最大承受容積 const int num 32 物品數 con...

tsp問題 遺傳演算法解決

tsp問題最簡單的求解方法是列舉法。它的解是多維的 多區域性極值的 趨於無窮大的複雜解的空間,搜尋空間是n個點的所有排列的集合,大小為 n 1 可以形象地把解空間看成是乙個無窮大的丘陵地帶,各山峰或山谷的高度即是問題的極值。求解tsp,則是在此不能窮盡的丘陵地帶中攀登以達到山頂或谷底的過程。這一篇將...