12硬币问题 python源码 可拓展
有12个硬币,其中有一个假币,其重量未知,称量三次就可找出假币并知道其重量。可拓展,
利用A*算法求解。代码如下
#%%
import copy
import queue
#状态定义和目标状态
#五个位置分别代表,没经过称重硬币数,,可能假币较轻,可能假币较重,已知是真币个数,剩余称重次数。
# Goal1=[0,1,0,11,0]
# Gaol2=[0,0,1,11,0]
node=[12,0,0,0,3]#lhs,ls,hs,s,t
str_key={
'lhs':0,
'ls':1,
'hs':2,
's':3,
't':4
}
coin_weight=[0,1,1,1,0,1,1,1,1,1,1,1,1]#硬币从下标1开始到12的重量。
coin_status=['lhs','lhs','lhs','lhs','lhs','lhs','lhs','lhs','lhs','lhs','lhs','lhs','lhs']#初始状态
#%%
def h(node):#[lhs,ls,hs,s,t] ,评价函数
return sum(node[:3]) -1
def isGoal(node):
if node[0]==0 and node[3]==11:
return True
else :
return False
#%%
def pikup(coin_status,left,right):#从当前状态nownode中取出left:[lhs1,ls1,hs1,s1]放在天平左边,取出right:[lhs2,ls2,hs2,s2]方右边
left_sum=0#左天平重量
left_select=[]
right_sum=0#左天平重量
right_select=[]
for coin,stat in enumerate(coin_status[1:]):
coin+=1
# print(coin)
if stat =='lhs':
if left[0]>0:
left[0]-=1
left_sum+= coin_weight[coin]
left_select.append(coin)
else:
if right[0]>0:
right[0]-=1
right_sum+= coin_weight[coin]
right_select.append(coin)
elif stat=='ls':
if left[1]>0:
left[1]-=1
left_sum+= coin_weight[coin]
left_select.append(coin)
else:
if right[1]>0:
right[1]-=1
right_sum+= coin_weight[coin]
right_select.append(coin)
elif stat=='hs':
if left[2]>0:
left[2]-=1
left_sum+= coin_weight[coin]
left_select.append(coin)
else:
if right[2]>0:
right[2]-=1
right_sum+= coin_weight[coin]
right_select.append(coin)
else:
if left[3]>0:
left[3]-=1
left_sum+= coin_weight[coin]
left_select.append(coin)
else:
if right[3]>0:
right[3]-=1
right_sum+= coin_weight[coin]
right_select.append(coin)
if left_sum==right_sum:
return 0,[left_select,right_select]
elif left_sum>right_sum:
return -1,[left_select,right_select]
else:
return 1,[left_select,right_select]#返回左右天平状态,和所选的cion
#%%
# temper,select=pikup([4,0,0,0],[4,0,0,0])
# print(temper,select)
#%%
def AStar(node,coin_status_in,left,right):#对当前选取,返回选取后的
temp_node=copy.deepcopy(node)
left_copy=copy.deepcopy(left)
right_copy=copy.deepcopy(right)
coin_status=copy.deepcopy(coin_status_in)
# print(node)
# print(coin_status)
temper,select=pikup(coin_status,left_copy,right_copy)#选取一次经过天平,状态,select:具体选的硬币
# print(temper,select)
# if sum(left)!=len(select[0]) or sum(right)!=len(select[1]):
# print("pick up error")
# print(left)
# print(select[0])
# print(right)
# print(select[1])
if temper==0:#天平左右平等,选取的所有硬币都为真
for item in select:
for j in item:
if coin_status[j]=='lhs':
temp_node[0]-=1
elif coin_status[j]=='ls':
temp_node[1]-=1
elif coin_status[j]=='hs':
temp_node[2]-=1
if coin_status[j]!='s':
temp_node[3]+=1
coin_status[j]='s'
temp_node[4]-=1
elif temper==-1:#左边重
for i in range(1,13):
if i in select[0]:#左天平
if coin_status[i]=='ls':
coin_status[i]='s'
temp_node[1]-=1
temp_node[3]+=1
elif coin_status[i]=='lhs':
coin_status[i]='hs'
temp_node[0]-=1
temp_node[2]+=1
elif i in select[1]:
if coin_status[i]=='hs':
coin_status[i]='s'
temp_node[2]-=1
temp_node[3]+=1
elif coin_status[i]=='lhs':
coin_status[i]='ls'
temp_node[0]-=1
temp_node[1]+=1
else:
# print(coin_status[i])
if coin_status[i]!='s':
temp_node[str_key[coin_status[i]]]-=1
coin_status[i]='s'
temp_node[3]+=1
temp_node[4]-=1
# print(temp_node)
else:#右边重
for i in range(1,13):
if i in select[0]:#左天平
if coin_status[i]=='hs':
coin_status[i]='s'
temp_node[2]-=1
temp_node[3]+=1
elif coin_status[i]=='lhs':
coin_status[i]='ls'
temp_node[0]-=1
temp_node[1]+=1
elif i in select[1]:#右天平
if coin_status[i]=='ls':
coin_status[i]='s'
temp_node[1]-=1
temp_node[3]+=1
elif coin_status[i]=='lhs':
coin_status[i]='hs'
temp_node[0]-=1
temp_node[2]+=1
else:
# print(coin_status[i])
if coin_status[i]!='s':
temp_node[str_key[coin_status[i]]]-=1
coin_status[i]='s'
temp_node[3]+=1
temp_node[4]-=1
# print(temp_node)
return temp_node,coin_status,select
#%%
frontier=queue.PriorityQueue()
frontier.put([h(node),node,coin_status,[],[[]]])
# frontier.put([100,[0, 1, 1, 10, 0],['lhs', 's', 's', 's', 's', 's', 's', 'hs', 'ls', 's', 's', 's', 's'],[[]]])
while not frontier.empty():
[v,node,coin_status,ch,select]=frontier.get()
while node[4]<0 or node[0]<0 or node[1]<0 or node[2]<0 or node[3]<0:
[v,node,coin_status,ch,select]=frontier.get()
print(v,node,coin_status,ch,select)
if isGoal(node):
# print(step)
print(coin_weight)
print(coin_status)
break
else:
# [v,node,coin_status,ch,select]=frontier.get()
# while node[4]<0 or node[0]<0 or node[1]<0 or node[2]<0 or node[3]<0:
# [v,node,coin_status,ch,select]=frontier.get()
# step.append([v,node,coin_status,ch,select])
# print('new,',v,node,coin_status,ch,select)
# 选取硬币的所有可能性,然后利用评估函数进行评估,并把数据储存在优先级队列中
all_select_lhs=[[0,0]]
for i in range(1,node[0]+1):
for j in range(1,node[0]+1):
if (i+j)<=node[0]:
all_select_lhs.append([i,j])
all_select_ls=[[0,0]]
for i in range(1,node[1]+1):
for j in range(1,node[1]+1):
if (i+j)<=node[1]:
all_select_ls.append([i,j])
all_select_hs=[[0,0]]
for i in range(1,node[2]+1):
for j in range(1,node[2]+1):
if (i+j)<=node[2]:
all_select_hs.append([i,j])
all_select_s=[[0,0]]
for i in range(1,node[3]+1):
for j in range(1,node[3]+1):
if (i+j)<=node[3]:
all_select_s.append([i,j])
# print(all_select_lhs,all_select_ls,all_select_hs,all_select_s)
left=[0,0,0,0]
right=[0,0,0,0]
for lhs in all_select_lhs:#穷举所有的选择
for ls in all_select_ls:
for hs in all_select_hs:
for s in all_select_s:
left[0]=lhs[0]
left[1]=ls[0]
left[2]=hs[0]
left[3]=s[0]
right[0]=lhs[1]
right[1]=ls[1]
right[2]=hs[1]
right[3]=s[1]
if sum(left)==sum(right) and sum(left)!=0:#选取的硬币,左右个数必须相等
# print('in')
# print(node,coin_status,left,right)
new_node,new_coin_status,select=AStar(node,coin_status,left,right)
# print('out')
# print(new_node,new_coin_status,select)
value=h(node)+h(new_node)#评估函数
# print("left",left,right)
# print('select',select)
# print('put',value,new_node,new_coin_status)
frontier.put([value,new_node,new_coin_status,[copy.deepcopy(left),copy.deepcopy(right)],select])
#%%
#%%
其称量过程:
优缺点分析
在评估时,为了穷举其所有的可能,采用 了暴力法,还需要进一步优化。
可以尝试求解所有的解。
参考论文
https://kns.cnki.net/kcms/detail/detail.aspx?dbcode=CJFD&dbname=CJFD2001&filename=JSGG200121038&uniplatform=NZKPT&v=MKhLKPWPegtKfgw8IcVPKJpZpuB10ecW9v_ddgX0GppMunPqaoerCKyHQYDNpnkf