程序的概要設計思想
爬山算法是一種局部貪婪算法,每次更新一次狀態,都對相鄰狀態的衝突狀態進行排序,選擇最好的相鄰狀態進行更新,因此易陷入局部極值,從而無法得到最優解。
初始狀態
爲了方便對狀態序列進行操作,爬山法程序採用一維數組x[i]進行編碼,只是x[i]的範圍變成0~7。一維數組中各元素互不相同。每個編碼元素由隨機函數產生。
衝突函數
衝突函數與遺傳算法中的適應函數類似,只是在本程序中修改爲終止條件爲0。
尋找鄰居狀態
每更新一次狀態,即每挪動一個皇后,都有56個鄰居狀態。計算這些新鄰居狀態的衝突值,選擇衝突值最小的鄰居狀態作爲下一個狀態。如果本次的初始狀態可以由爬山法尋找到最優解,那麼爬山法的收斂速度是很快的,因此尋找新鄰居的次數如果大於100次,可以認爲在這個初始狀態下,爬山法尋找不到解。
尋找全部解集
解決八皇后問題的全部解集,在爬山法的程序中可以實現。多次進行爬山法求解,然後進行答案去重操作。隨着爬山法求解次數的增多,解集中的解答數量上升。當爬山法求解次數大於800次時,解集中解答數量不在變化,爲92,因此可以認爲爬山法求解八皇后問題的解集數量收斂於92。
最終得到的結果爲,八皇后問題有92個解。
注:
這裏求得的92並非嚴格意義上的完全不同的解,即如果一對解是對稱的,那麼在本程序中認爲它們是兩個解。
N·沃思在他的名著《算法+數據結構=程序》一書中給出了求解八皇后問題全部解的Pascal源程序。沃思同時指出,該程序不能識別對稱的解,雖然一切可能的解有92個,但是隻有12個真正不同的解。
這裏給出求出全部不同的八皇后問題的解的C語言程序的刊登在淮南師範學院學報上的論文。
有點不應該的是,這篇論文上有個無關痛癢的錯別字。
尹星雲. 八皇后問題的全部12個不同的解[J]. 淮南師範學院學報, 1997(1):72-74.
此外,補充上次遺傳算法解決八皇后問題,因爲我採用8列單算子的編碼表示個體,本來以爲遺傳算法雖然全局收斂性比爬山法稍好,但其效率不高且性能不穩定,不大可能在有限的時間內求解出所有92種解。但這篇刊登在華中科技大學學報上的論文采用3種基本算子複合表示一個個體的方式大大提高了計算效率,節約了片內資源,並且有嚴謹的數學建模過程和嚴密的結論證明。
周康, 魏傳佳, 劉朔, et al. 八皇后問題所有解的模擬DNA算法[J]. 華中科技大學學報(自然科學版), 2009(6).
程序主要函數的作用
初始化皇后狀態,每行只有一個皇后:
def initiate(status)
定義衝突函數,主要排查對角線上有多個皇后的情況,返回衝突的皇后對數:
def conflict
定義相鄰元素:
def neighbour(status)t(status)
爬山法函數:
def climbing(status)
八皇后問題求解函數:
def qeen():
輸入異常處理
def default():
運行結果截圖
Python源代碼
import random
from time import sleep
import matplotlib.pyplot
#初始化皇后狀態,每行只有一個皇后
def initiate(status):
while len(status)<8:
r=random.randint(0,7)
if not (r in status):
status.append(r)
return status
#定義衝突函數,主要排查對角線上有多個皇后的情況,返回衝突的皇后對數
def conflict(status):
n=0
for i in range(8):
for j in range(i+1,8):
if status[i]==status[j]:
n += 1
if status[j]-status[i]==i-j or status[j]-status[i]==j-i:
n += 1
return n
#定義相鄰元素
def neighbour(status):
next = {}
for i in range(8):
for j in range(8):
if status[i] == j:
continue
copy = list(status)
copy[i] = j
next[(i, j)] = conflict(copy)
return next
#爬山法函數
def climbing(status):
#當前互相沖突的皇后對數
conflictnow=conflict(status)
#最佳後繼集合
ans=[]
#尋找最佳後繼
next=neighbour(status)
for key,value in next.items():
if value < conflictnow:
conflictnow=value
for key,value in next.items():
if value == conflictnow:
ans.append(key)
#若後繼元素大於一個,則隨機選擇一個
if len(ans)>0:
rnd=random.randint(0,len(ans)-1)
i, j = ans[rnd][0], ans[rnd][1]
status[i]=j
return status
def qeen():
#若找不到解,循環的最大次數
max=100
total=0
status=[]
status=initiate(status)
#print("The initial status is {}".format(status))
climbing(status)
while conflict(status)>0:
status=climbing(status)
total+=1
if total==max:
#print("Climbinghill algorithm cannot find the answer in {} times".format(max))
return []
if total < max:
#可行解的可視化
'''print("The answer is {} which is found in {} times".format(status,total))
x=[0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5]
y=[status.index(0)+0.5,status.index(1)+0.5,status.index(2)+0.5,status.index(3)+0.5,status.index(4)+0.5,status.index(5)+0.5,status.index(6)+0.5,status.index(7)+0.5]
matplotlib.pyplot.title("Using ClimbingHill Algorithm to Solve 8-Queens' Problem")
matplotlib.pyplot.axis([0,8,0,8])
matplotlib.pyplot.grid()
matplotlib.pyplot.plot(x, y, '*')
matplotlib.pyplot.show()'''
return status
#輸入異常處理
def default():
try:
global tests
tests=eval(input("Please Enter Testing Times(Enter 0 To Complete This Program):"))
except:
print("Please Enter integer As Testing Times!")
return False
return True
#多次重複求解,尋找不同的解集
while True:
t, failed=0,0
solve=[]
#測試次數
try:
tests=eval(input("Enter Testing Times(Enter 0 To Complete This Program):"))
except:
print("Please Enter integer As Testing Times!")
boolean=False
while boolean!=True:
boolean=default()
if tests==0:
break
#去掉重複的解,保留不同的解
for i in range(tests):
status = qeen()
if status==[]:
failed+=1
continue
elif not(status in solve):
solve.append(status)
t+=1
else:
t+=1
print("Climbinghill algorithm failed times:{}".format(failed))
print("Climbinghill algorithm succeeded times:{}".format(t))
print("Climbinghill algorithm's succeeded rate:{:.2f}%".format((t-failed)/t*100))
print("Different solutions' number:{}".format(len(solve)))
#選擇是否輸出全部不同的解集
ifprint=input("Do you wanna print all the different solutions? Y(y)/N(n)")
while ifprint!='Y' and ifprint!="N" and ifprint!='y' and ifprint!='n':
ifprint = input("Please Enter Y/y or N/n :")
if ifprint=='y' or ifprint=='Y':
s,t=0,0
for i in solve:
s+=1
if t==5:
print("{:3}:{}".format(s,i))
t=0
else:
t+=1
print("{:2}:{}".format(s,i),end="")
print("")
print("----------------Testing times:{}----------------".format(tests))