排課問題的本質是將課程、教師和學生在合適的時間段內分配到合適的教室中,涉及到的因素較多,是一個多目標的調度問題,在運籌學中被稱為時間表問題(Timetable Problem,TTP)。設一個星期有n個時段可排課,有m位教師需要參與排課,平均每位教師一個星期上k節課,在不考慮其他限制的情況下,能夠推出的可能組合就有 種,如此高的復雜度是目前計算機所無法承受的。因此眾多研究者提出了多種其他排課演算法,如模擬退火,列表尋優搜索和約束滿意等。
Github : https://github.com/xiaochus/GeneticClassSchele
遺傳演算法(Genetic Algorithm)是模擬達爾文生物進化論的自然選擇和遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜索最優解的方法。遺傳演算法的流程如下所示:
遺傳演算法首先針對待解決問題隨機生成一組解,我們稱之為種群(Population)。種群中的每個個體都是問題的解,在優化的過程中,演算法會計算整個種群的成本函數,從而得到一個與種群相關的適應度的序列。如下圖所示:
為了得到新的下一代種群,首先根據適應度對種群進行排序,從中挑選出最優的幾個個體加入下一代種群,這一個過程也被稱為精英選拔。新種群餘下的部分通過對選拔出來的精英個體進行修改得到。
對種群進行修改的方法參考了生物DAN進化的方法,一般使用兩種方法: 變異 和 交叉 。 變異 的做法是對種群做一個微小的、隨機的改變。如果解的編碼方式是二進制,那麼就隨機選取一個位置進行0和1的互相突變;如果解的編碼方式是十進制,那麼就隨機選取一個位置進行隨機加減。 交叉 的做法是隨機從最優種群中選取兩個個體,以某個位置為交叉點合成一個新的個體。
經過突變和交叉後我們得到新的種群(大小與上一代種群一致),對新種群重復重復上述過程,直到達到迭代次數(失敗)或者解的適應性達到我們的要求(成功),GA演算法就結束了。
演算法實現
首先定義一個課程類,這個類包含了課程、班級、教師、教室、星期、時間幾個屬性,其中前三個是我們自定義的,後面三個是需要演算法來優化的。
接下來定義cost函數,這個函數用來計算課表種群的沖突。當被測試課表沖突為0的時候,這個課表就是個符合規定的課表。沖突檢測遵循下面幾條規則:
使用遺傳演算法進行優化的過程如下,與上一節的流程圖過程相同。
init_population :隨機初始化不同的種群。
mutate :變異操作,隨機對 Schele 對象中的某個可改變屬性在允許范圍內進行隨機加減。
crossover :交叉操作,隨機對兩個對象交換不同位置的屬性。
evolution :啟動GA演算法進行優化。
實驗結果
下面定義了3個班,6種課程、教師和3個教室來對排課效果進行測試。
優化結果如下,迭代到第68次時,課程安排不存在任何沖突。
選擇1203班的課表進行可視化,如下所示,演算法合理的安排了對應的課程。
❷ 基於Python編程,使用遺傳演算法求解區間[0,31]上的二次函數y=x(x-1)的最大值
max_y=max(list(map(lambda x:[x,x*(x-1)], [x for x in range(0,32)])))
print(f"[x,y]={max_y=}")
'''python運行效果
[x,y]=max_y=[31, 930]
'''
❸ python 哪個包里有 遺傳演算法
scikit-opt調研過很多遺傳演算法庫,這個挺好用的。
#目標函數
defdemo_func(x):
x1,x2,x3=x
returnx1**2+(x2-0.05)**2+x3**2
fromgaimportGA
調用遺傳演算法求解:
ga=GA(func=demo_func,lb=[-1,-10,-5],ub=[2,10,2],max_iter=500)
best_x,best_y=ga.fit()
❹ python 遺傳演算法問題
遺傳演算法(GA)是最早由美國Holland教授提出的一種基於自然界的「適者生存,優勝劣汰」基本法則的智能搜索演算法。
遺傳演算法也是借鑒該基本法則,通過基於種群的思想,將問題的解通過編碼的方式轉化為種群中的個體,並讓這些個體不斷地通過選擇、交叉和變異運算元模擬生物的進化過程,然後利用「優勝劣汰」法則選擇種群中適應性較強的個體構成子種群,然後讓子種群重復類似的進化過程,直到找到問題的最優解或者到達一定的進化(運算)時間。
❺ python有沒有簡單的遺傳演算法庫
首先遺傳演算法是一種優化演算法,通過模擬基因的優勝劣汰,進行計算(具體的演算法思路什麼的就不贅述了)。大致過程分為初始化編碼、個體評價、選擇,交叉,變異。
以目標式子 y = 10 * sin(5x) + 7 * cos(4x)為例,計算其最大值
首先是初始化,包括具體要計算的式子、種群數量、染色體長度、交配概率、變異概率等。並且要對基因序列進行初始化
[python]view plain
pop_size=500#種群數量
max_value=10#基因中允許出現的最大值
chrom_length=10#染色體長度
pc=0.6#交配概率
pm=0.01#變異概率
results=[[]]#存儲每一代的最優解,N個二元組
fit_value=[]#個體適應度
fit_mean=[]#平均適應度
pop=geneEncoding(pop_size,chrom_length)
其中genEncodeing是自定義的一個簡單隨機生成序列的函數,具體實現如下
[python]view plain
defgeneEncoding(pop_size,chrom_length):
pop=[[]]
foriinrange(pop_size):
temp=[]
forjinrange(chrom_length):
temp.append(random.randint(0,1))
pop.append(temp)
returnpop[1:]
編碼完成之後就是要進行個體評價,個體評價主要是計算各個編碼出來的list的值以及對應帶入目標式子的值。其實編碼出來的就是一堆2進制list。這些2進制list每個都代表了一個數。其值的計算方式為轉換為10進制,然後除以2的序列長度次方減一,也就是全一list的十進制減一。根據這個規則就能計算出所有list的值和帶入要計算式子中的值,代碼如下
[python]view plain
#0.0coding:utf-80.0
#解碼並計算值
importmath
defdecodechrom(pop,chrom_length):
temp=[]
foriinrange(len(pop)):
t=0
forjinrange(chrom_length):
t+=pop[i][j]*(math.pow(2,j))
temp.append(t)
returntemp
defcalobjValue(pop,chrom_length,max_value):
temp1=[]
obj_value=[]
temp1=decodechrom(pop,chrom_length)
foriinrange(len(temp1)):
x=temp1[i]*max_value/(math.pow(2,chrom_length)-1)
obj_value.append(10*math.sin(5*x)+7*math.cos(4*x))
returnobj_value
有了具體的值和對應的基因序列,然後進行一次淘汰,目的是淘汰掉一些不可能的壞值。這里由於是計算最大值,於是就淘汰負值就好了
[python]view plain
#0.0coding:utf-80.0
#淘汰(去除負值)
defcalfitValue(obj_value):
fit_value=[]
c_min=0
foriinrange(len(obj_value)):
if(obj_value[i]+c_min>0):
temp=c_min+obj_value[i]
else:
temp=0.0
fit_value.append(temp)
returnfit_value
然後就是進行選擇,這是整個遺傳演算法最核心的部分。選擇實際上模擬生物遺傳進化的優勝劣汰,讓優秀的個體盡可能存活,讓差的個體盡可能的淘汰。個體的好壞是取決於個體適應度。個體適應度越高,越容易被留下,個體適應度越低越容易被淘汰。具體的代碼如下
[python]view plain
#0.0coding:utf-80.0
#選擇
importrandom
defsum(fit_value):
total=0
foriinrange(len(fit_value)):
total+=fit_value[i]
returntotal
defcumsum(fit_value):
foriinrange(len(fit_value)-2,-1,-1):
t=0
j=0
while(j<=i):
t+=fit_value[j]
j+=1
fit_value[i]=t
fit_value[len(fit_value)-1]=1
defselection(pop,fit_value):
newfit_value=[]
#適應度總和
total_fit=sum(fit_value)
foriinrange(len(fit_value)):
newfit_value.append(fit_value[i]/total_fit)
#計算累計概率
cumsum(newfit_value)
ms=[]
pop_len=len(pop)
foriinrange(pop_len):
ms.append(random.random())
ms.sort()
fitin=0
newin=0
newpop=pop
#轉輪盤選擇法
whilenewin<pop_len:
if(ms[newin]<newfit_value[fitin]):
newpop[newin]=pop[fitin]
newin=newin+1
else:
fitin=fitin+1
pop=newpop
選擇完後就是進行交配和變異,這個兩個步驟很好理解。就是對基因序列進行改變,只不過改變的方式不一樣
交配:
[python]view plain
#0.0coding:utf-80.0
#交配
importrandom
defcrossover(pop,pc):
pop_len=len(pop)
foriinrange(pop_len-1):
if(random.random()<pc):
cpoint=random.randint(0,len(pop[0]))
temp1=[]
temp2=[]
temp1.extend(pop[i][0:cpoint])
temp1.extend(pop[i+1][cpoint:len(pop[i])])
temp2.extend(pop[i+1][0:cpoint])
temp2.extend(pop[i][cpoint:len(pop[i])])
pop[i]=temp1
pop[i+1]=temp2
[python]view plain
#0.0coding:utf-80.0
#基因突變
importrandom
defmutation(pop,pm):
px=len(pop)
py=len(pop[0])
foriinrange(px):
if(random.random()<pm):
mpoint=random.randint(0,py-1)
if(pop[i][mpoint]==1):
pop[i][mpoint]=0
else:
pop[i][mpoint]=1
[python]view plain
#0.0coding:utf-80.0
importmatplotlib.pyplotasplt
importmath
fromselectionimportselection
fromcrossoverimportcrossover
frommutationimportmutation
frombestimportbest
print'y=10*math.sin(5*x)+7*math.cos(4*x)'
#計算2進制序列代表的數值
defb2d(b,max_value,chrom_length):
t=0
forjinrange(len(b)):
t+=b[j]*(math.pow(2,j))
t=t*max_value/(math.pow(2,chrom_length)-1)
returnt
pop_size=500#種群數量
max_value=10#基因中允許出現的最大值
chrom_length=10#染色體長度
pc=0.6#交配概率
pm=0.01#變異概率
results=[[]]#存儲每一代的最優解,N個二元組
fit_value=[]#個體適應度
fit_mean=[]#平均適應度
#pop=[[0,1,0,1,0,1,0,1,0,1]foriinrange(pop_size)]
pop=geneEncoding(pop_size,chrom_length)
foriinrange(pop_size):
obj_value=calobjValue(pop,chrom_length,max_value)#個體評價
fit_value=calfitValue(obj_value)#淘汰
best_indivial,best_fit=best(pop,fit_value)#第一個存儲最優的解,第二個存儲最優基因
results.append([best_fit,b2d(best_indivial,max_value,chrom_length)])
selection(pop,fit_value)#新種群復制
crossover(pop,pc)#交配
mutation(pop,pm)#變異
results=results[1:]
results.sort()
X=[]
Y=[]
foriinrange(500):
X.append(i)
t=results[i][0]
Y.append(t)
plt.plot(X,Y)
plt.show()
完整代碼可以在github查看
歡迎訪問我的個人博客
閱讀全文
❻ python遺傳演算法目標函數怎麼編
一、遺傳演算法介紹
遺傳演算法是通過模擬大自然中生物進化的歷程,來解決問題的。大自然中一個種群經歷過若干代的自然選擇後,剩下的種群必定是適應環境的。把一個問題所有的解看做一個種群,經歷過若干次的自然選擇以後,剩下的解中是有問題的最優解的。當然,只能說有最優解的概率很大。這里,我們用遺傳演算法求一個函數的最大值。
f(x) = 10 * sin( 5x ) + 7 * cos( 4x ), 0 <= x <= 10
1、將自變數x進行編碼
取基因片段的長度為10, 則10位二進制位可以表示的范圍是0到1023。基因與自變數轉變的公式是x = b2d(indivial) * 10 / 1023。構造初始的種群pop。每個個體的基因初始值是[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
2、計算目標函數值
根據自變數與基因的轉化關系式,求出每個個體的基因對應的自變數,然後將自變數代入函數f(x),求出每個個體的目標函數值。
3、適應度函數
適應度函數是用來評估個體適應環境的能力,是進行自然選擇的依據。本題的適應度函數直接將目標函數值中的負值變成0. 因為我們求的是最大值,所以要使目標函數值是負數的個體不適應環境,使其繁殖後代的能力為0.適應度函數的作用將在自然選擇中體現。
4、自然選擇
自然選擇的思想不再贅述,操作使用輪盤賭演算法。其具體步驟:
假設種群中共5個個體,適應度函數計算出來的個體適應性列表是fitvalue = [1 ,3, 0, 2, 4] ,totalvalue = 10 , 如果將fitvalue畫到圓盤上,值的大小表示在圓盤上的面積。在轉動輪盤的過程中,單個模塊的面積越大則被選中的概率越大。選擇的方法是將fitvalue轉化為[1 , 4 ,4 , 6 ,10], fitvalue / totalvalue = [0.1 , 0.4 , 0.4 , 0.6 , 1.0] . 然後產生5個0-1之間的隨機數,將隨機數從小到大排序,假如是[0.05 , 0.2 , 0.7 , 0.8 ,0.9],則將0號個體、1號個體、4號個體、4號個體、4號個體拷貝到新種群中。自然選擇的結果使種群更符合條件了。
5、繁殖
假設個體a、b的基因是
a = [1, 0, 0, 0, 0, 1, 1, 1, 0, 0]
b = [0, 0, 0, 1, 1, 0, 1, 1, 1, 1]
這兩個個體發生基因交換的概率pc = 0.6.如果要發生基因交換,則產生一個隨機數point表示基因交換的位置,假設point = 4,則:
a = [1, 0, 0, 0, 0, 1, 1, 1, 0, 0]
b = [0, 0, 0, 1, 1, 0, 1, 1, 1, 1]
交換後為:
a = [1, 0, 0, 0, 1, 0, 1, 1, 1, 1]
b = [0, 0, 0, 1, 0, 1, 1, 1, 0, 0]
6、突變
遍歷每一個個體,基因的每一位發生突變(0變為1,1變為0)的概率為0.001.突變可以增加解空間
二、代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def b2d(b): #將二進制轉化為十進制 x∈[0,10] t = 0 for j in range(len(b)): t += b[j] * (math.pow(2, j)) t = t * 10 / 1023 return tpopsize = 50 #種群的大小#用遺傳演算法求函數最大值:#f(x)=10*sin(5x)+7*cos(4x) x∈[0,10]chromlength = 10 #基因片段的長度pc = 0.6 #兩個個體交叉的概率pm = 0.001; #基因突變的概率results = [[]]bestindivial = []bestfit = 0fitvalue = []tempop = [[]]pop = [[0, 1, 0, 1, 0, 1, 0, 1, 0, 1] for i in range(popsize)]for i in range(100): #繁殖100代 objvalue = calobjvalue(pop) #計算目標函數值 fitvalue = calfitvalue(objvalue); #計算個體的適應值 [bestindivial, bestfit] = best(pop, fitvalue) #選出最好的個體和最好的函數值 results.append([bestfit,b2d(bestindivial)]) #每次繁殖,將最好的結果記錄下來 selection(pop, fitvalue) #自然選擇,淘汰掉一部分適應性低的個體 crossover(pop, pc) #交叉繁殖 mutation(pop, pc) #基因突變 results.sort() print(results[-1]) #列印函數最大值和對應的
來自CODE的代碼片
GA.py
1
2
3
4
5
6
7
8
9
def best(pop, fitvalue): #找出適應函數值中最大值,和對應的個體 px = len(pop) bestindivial = [] bestfit = fitvalue[0] for i in range(1,px): if(fitvalue[i] > bestfit): bestfit = fitvalue[i] bestindivial = pop[i] return [bestindivial, bestfit]
來自CODE的代碼片
best.py
1
2
3
4
5
6
7
8
9
10
11
def calfitvalue(objvalue):#轉化為適應值,目標函數值越大越好,負值淘汰。 fitvalue = [] temp = 0.0 Cmin = 0; for i in range(len(objvalue)): if(objvalue[i] + Cmin > 0): temp = Cmin + objvalue[i] else: temp = 0.0 fitvalue.append(temp) return fitvalue
來自CODE的代碼片
calfitvalue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import mathdef decodechrom(pop): #將種群的二進制基因轉化為十進制(0,1023) temp = []; for i in range(len(pop)): t = 0; for j in range(10): t += pop[i][j] * (math.pow(2, j)) temp.append(t) return tempdef calobjvalue(pop): #計算目標函數值 temp1 = []; objvalue = []; temp1 = decodechrom(pop) for i in range(len(temp1)): x = temp1[i] * 10 / 1023 #(0,1023)轉化為 (0,10) objvalue.append(10 * math.sin(5 * x) + 7 * math.cos(4 * x)) return objvalue #目標函數值objvalue[m] 與個體基因 pop[m] 對應
來自CODE的代碼片
calobjvalue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import randomdef crossover(pop, pc): #個體間交叉,實現基因交換 poplen = len(pop) for i in range(poplen - 1): if(random.random() < pc): cpoint = random.randint(0,len(pop[0])) temp1 = [] temp2 = [] temp1.extend(pop[i][0 : cpoint]) temp1.extend(pop[i+1][cpoint : len(pop[i])]) temp2.extend(pop[i+1][0 : cpoint]) temp2.extend(pop[i][cpoint : len(pop[i])]) pop[i] = temp1 pop[i+1] = temp2
來自CODE的代碼片
crossover.py
1
2
3
4
5
6
7
8
9
10
11
12
13
import randomdef mutation(pop, pm): #基因突變 px = len(pop) py = len(pop[0]) for i in range(px): if(random.random() < pm): mpoint = random.randint(0,py-1) if(pop[i][mpoint] == 1): pop[i][mpoint] = 0 else: pop[i][mpoint] = 1
來自CODE的代碼片
mutation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import randomdef sum(fitvalue): total = 0 for i in range(len(fitvalue)): total += fitvalue[i] return totaldef cumsum(fitvalue): for i in range(len(fitvalue)): t = 0; j = 0; while(j <= i): t += fitvalue[j] j = j + 1 fitvalue[i] = t;def selection(pop, fitvalue): #自然選擇(輪盤賭演算法) newfitvalue = [] totalfit = sum(fitvalue) for i in range(len(fitvalue)): newfitvalue.append(fitvalue[i] / totalfit) cumsum(newfitvalue) ms = []; poplen = len(pop) for i in range(poplen): ms.append(random.random()) #random float list ms ms.sort() fitin = 0 newin = 0 newpop = pop while newin < poplen: if(ms[newin] < newfitvalue[fitin]): newpop[newin] = pop[fitin] newin = newin + 1 else: fitin = fitin + 1 pop = newpop