模擬退火算法(Simulated Annealing)求解工作指派問題(Assignment Problem)的Python實現
1 簡單指派問題
2 Python實現
# -*- coding: utf-8 -*-
'''
basic SA algorithm on assignment problem.
Hint:number of workers equals that of jobs.
coder:yangsen @ UCAS.
reference:中國科學院大學2017春季課程《智能優化算法》chapter4
'''
import numpy as np
#instance 1: 4 workers and 4 jobs. Every row corresponds one worker.
d=np.array([[2,15,13,4],[10,4,14,15],[9,14,16,13],[7,8,11,9]])
#best solution:x=[4,2,1,3], that is saying, worker 1 do job 4, and
#worker 2 do job 2, etc.
#instance 2: 5 workers and 5 jobs
d=np.array([[7,2,8,11,12],[9,12,11,9,12],[8,5,6,8,12],[7,3,9,6,12],[4,6,5,11,12]])
#best solution:x=[2,5,3,4,1]
num=d.shape[0]#number of worker and job.
def cal_total_time(x):
total_time=0
for i in range(num):
total_time+=d[i,x[i]]
return total_time
T=100 #initial temperature
t=1# final temperature
alpha=0.9#annealing rate
markov_len=1000 #inner loop,aka,markov length
current_x=np.random.permutation(num)#initialize the solution
current_time=cal_total_time(current_x)
new_x=current_x.copy()
best_x=current_x.copy()
best_time=np.max
while T>t:
for i in range(markov_len):
while True:
k=np.random.randint(0,num,2)
if k[0]!=k[1]:
break
new_x[k[0]],new_x[k[1]]=new_x[k[1]],new_x[k[0]]
new_time=cal_total_time(new_x)
if new_time<current_time:#accept new solution with no condition.
current_time=new_time
current_x=new_x.copy()
if new_time<best_time:
best_time=new_time
best_x=new_x.copy()
else:
delta=new_time-current_time
if np.random.rand()<np.exp(-delta/T):#accept new solution with an probability.
current_time=new_time
current_x=new_x.copy()
else:
new_x=current_x.copy()#refuse new solution.
T=T*alpha
print current_x,current_time
程序中實例規模太小,無法體現SA算法的尋優能力,但一時找不到合適的實例。後面找到規模較大的實例後,補充到博客中去。
P.S:對於指派問題還有一種更一般的提法,就是工人數和工作數目不等的情況。針對這種情況,一種最容易想到的處理方式就是增加虛擬工人或虛擬工作,但會有很多問題需要處理。比如,如果工人數少,則必然有工人要做多個工作,而如果工人多,則有工人閒着。後者的處理比較簡單。
指派問題有經典的算法,匈牙利算法。