python 八数码_实践多种搜索算法求解八数码问题python实现

哎,好久没写博文了,其实仔细想来,时间还是蛮多的,以后还是多写写吧!

之前看过经典的搜索路径方法,印象较深的也就BFS(广度优先),DFS(深度优先)以及A*搜索,但没实践过,就借八数码问题,来通通实现遍,观察下现象呗~~~

首先,怎么说也得把数码这玩意基本操作实现了呗!上代码~class puzzled:

def __init__(self,puzzled):

self.puzzled=puzzled

self.__getPuzzledInfo()

def __getPuzzledInfo(self):

self.puzzledWid=len(self.puzzled[0])

self.puzzledHei=len(self.puzzled)

self.__f1=False

for i in range(0,self.puzzledHei):

for j in range(0,self.puzzledWid):

if(self.puzzled[i][j]==0):

self.zeroX=j

self.zeroY=i

self.__f1=True

break

if(self.__f1):

break

def printPuzzled(self):

for i in range(0,len(self.puzzled)):

print self.puzzled[i]

print ""

def isRight(self):

if(self.puzzled[self.puzzledHei-1][self.puzzledWid-1]!=0):

return False

for i in range(0,self.puzzledHei):

for j in range(0,self.puzzledWid):

if(i*self.puzzledWid+j+1!=self.puzzled[i][j]):

if(i!=self.puzzledHei-1 or j!=self.puzzledWid-1):

return False

return True

def move(self,dere):#0 up,1 down,2 left,3 right

if(dere==0 and self.zeroY!=0):

self.puzzled[self.zeroY-1][self.zeroX],self.puzzled[self.zeroY][self.zeroX] = self.puzzled[self.zeroY][self.zeroX],self.puzzled[self.zeroY-1][self.zeroX]

self.zeroY-=1

return True

elif(dere==1 and self.zeroY!=self.puzzledHei-1):

self.puzzled[self.zeroY+1][self.zeroX],self.puzzled[self.zeroY][self.zeroX] = self.puzzled[self.zeroY][self.zeroX],self.puzzled[self.zeroY+1][self.zeroX]

self.zeroY+=1

return True

elif(dere==2 and self.zeroX!=0):

self.puzzled[self.zeroY][self.zeroX-1],self.puzzled[self.zeroY][self.zeroX] = self.puzzled[self.zeroY][self.zeroX],self.puzzled[self.zeroY][self.zeroX-1]

self.zeroX-=1

return True

elif(dere==3 and self.zeroX!=self.puzzledWid-1):

self.puzzled[self.zeroY][self.zeroX+1],self.puzzled[self.zeroY][self.zeroX] = self.puzzled[self.zeroY][self.zeroX],self.puzzled[self.zeroY][self.zeroX+1]

self.zeroX+=1

return True

return False

def getAbleMove(self):

a=[]

if(self.zeroY!=0):

a.append(0)

if(self.zeroY!=self.puzzledHei-1):

a.append(1)

if(self.zeroX!=0):

a.append(2)

if(self.zeroX!=self.puzzledWid-1):

a.append(3)

return a

def clone(self):

a=copy.deepcopy(self.puzzled)

return puzzled(a)

def toString(self):

a=""

for i in range(0,self.puzzledHei):

for j in range(0,self.puzzledWid):

a+=str(self.puzzled[i][j])

return a

def isEqual(self,p):

if(self.puzzled==p.puzzled):

return True

return False

def toOneDimen(self):

a=[]

for i in range(0,self.puzzledHei):

for j in range(0,self.puzzledWid):

a.append(self.puzzled[i][j])

return a

def getNotInPosNum(self):

t=0

for i in range(0,self.puzzledHei):

for j in range(0,self.puzzledWid):

if(self.puzzled[i][j]!=i*self.puzzledWid+j+1):

if(i==self.puzzledHei-1 and j==self.puzzledWid-1 and self.puzzled[i][j]==0):

continue

t+=1

return t

def getNotInPosDis(self):

t=0

it=0

jt=0

for i in range(0,self.puzzledHei):

for j in range(0,self.puzzledWid):

if(self.puzzled[i][j]!=0):

it=(self.puzzled[i][j]-1)/self.puzzledWid

jt=(self.puzzled[i][j]-1)%self.puzzledWid

else:

it=self.puzzledHei-1

jt=self.puzzledWid-1

t+=abs(it-i)+abs(jt-j)

return t

@staticmethod

def generateRandomPuzzle(m,n,ran):

tt=[]

for i in range(0,m):

t=[]

for j in range(0,n):

t.append(j+1+i*n)

tt.append(t)

tt[m-1][n-1]=0

a=puzzled(tt)

i=0

while(i

i+=1

a.move(random.randint(0,4))

return a

稍微注解一下,puzzled类表示一个数码类,初始化利用a=puzzled(  [1,2,3],

[4,5,6],

[7,8,0])

其中呢,0表示空格位置,上面初始化的便是一个正确的,未被打乱的位置~

其他的成员函数,看名称就很好理解了呗~

ok,基础打好了,接下来就该上节点类了:class node:

def __init__(self,p):

self.puzzled=p

self.childList=[]

self.father=None

def addChild(self,child):

self.childList.append(child)

child.setFather(self)

def getChildList(self):

return self.childList

def setFather(self,fa):

self.father=fa

def displayToRootNode(self):

t=self

tt=0

while(True):

tt+=1

t.puzzled.printPuzzled()

t=t.father

if(t==None):

break

print "it need "+str(tt)+ " steps!"

def getFn(self):

fn=self.getGn()+self.getHn() #A*

#fn=self.getHn() #贪婪

return fn

def getHn(self):

Hn=self.puzzled.getNotInPosDis()

return Hn

def getGn(self):

gn=0

t=self.father

while(t!=None):

gn+=1

t=t.father

return gn

对于节点类吧,也还是很好理解的,初始化方法a=node(

puzzled([1,2,3],

[4,5,6],

[7,8,0])

)

基础都搭好了,重点人物该闪亮登场了呗~class seartchTree:

def __init__(self,root):

self.root=root

def __search2(self,hlist,m):  #二分查找,经典算法,从大到小,返回位置

#若未查找到,则返回应该插入的位置

low = 0

high = len(hlist) - 1

mid=-1

while(low <= high):

mid = (low + high)/2

midval = hlist[mid]

if midval > m:

low = mid + 1

elif midval 

high = mid - 1

else:

return (True,mid)

return (False,mid)

def __sortInsert(self,hlist,m):#对于一个从大到小的序列,

#插入一个数,仍保持从大到小

t=self.__search2(hlist,m)

if(t[1]==-1):

hlist.append(m)

return 0

if(m

hlist.insert(t[1]+1, m)

return t[1]+1

else:

hlist.insert(t[1], m)

return t[1]

def breadthFirstSearch(self):#广度优先搜索

numTree=NumTree.NumTree()

numTree.insert(self.root.puzzled.toOneDimen())

t=[self.root]

flag=True

generation=0

while(flag):

print "it's the "+str(generation)+" genneration now,the total num of items is "+str(len(t))

tb=[]

for i in t:

if(i.puzzled.isRight()==True):

i.displayToRootNode()

flag=False

break

else:

for j in i.puzzled.getAbleMove():

tt=i.puzzled.clone()

tt.move(j)

a=node(tt)

if(numTree.searchAndInsert(a.puzzled.toOneDimen())==False):

i.addChild(a)

tb.append(a)

t=tb

generation+=1

def depthFirstSearch(self):#深度优先搜索

numTree=NumTree.NumTree()

numTree.insert(self.root.puzzled.toOneDimen())

t=self.root

flag=True

gen=0

while(flag):

bran=0

print "genneration: "+str(gen)

if(t.puzzled.isRight()==True):

t.displayToRootNode()

flag=False

break

else:

f1=True

for j in t.puzzled.getAbleMove():

tt=t.puzzled.clone()

tt.move(j)

a=node(tt)

if(numTree.searchAndInsert(a.puzzled.toOneDimen())==False):

t.addChild(a)

t=a

f1=False

gen+=1

break

if(f1==True):

t=t.father

gen-=1

def AStarSearch(self):#A*

numTree=NumTree.NumTree()

numTree.insert(self.root.puzzled.toOneDimen())

leaves=[self.root]

leavesFn=[0]

while True:

t=leaves.pop()  #open表

print leavesFn.pop()

if(t.puzzled.isRight()==True):

t.displayToRootNode()

break

for i in t.puzzled.getAbleMove():

tt=t.puzzled.clone()

tt.move(i)

a=node(tt)

if(numTree.searchAndInsert(a.puzzled.toOneDimen())==False):#close表

t.addChild(a)

fnS=self.__sortInsert(leavesFn,a.getFn())

leaves.insert(fnS, a)

注意到里面存在一个 NumTree,这个是个啥玩意了,这个吧,其实是一个closed表,啥是Closed表呢?

就是呀,在搜索的时候,得记录已经扩展的节点,不然的话很有可能成为不完备的搜索了(对于深度搜索),而且已经扩展的状态,也对于数码问题没必要在扩展一次,那么怎么来实现记录已经扩展的节点呢?

一个最简单的方法就是建立一个链表,将扩展的节点加进去,但这样存在一个问题,就是由于每次在扩展节点时都得遍历closed表,查找是否存在同样的节点已经被扩展,这样,复杂度就简直太高了,完全不行呗~

那么咋办呢?从上面代码注意到,node对象里面有一个Puzzled成员,实际上puzzled对象主要就由一个二维数组构成呗~这时候呀,我就把这个二维数组变为一维呗[1,2,3],

[4,5,6],         ->   [1,2,3,4,5,6,7,8,0]

[7,8,0]

然后呢,利用trie树存储该数组,这样一来,查找添加一步到位!

也就是上面代码中的这个啦~numTree.searchAndInsert(a.puzzled.toOneDimen())

贴上numtree代码:class NumTree:

def __init__(self):

self.root = Node()

def insert(self, key):      # key is of type string

# key should be a low-case string, this must be checked here!

node = self.root

for char in key:

if char not in node.children:

child = Node()

node.children[char] = child

node = child

else:

node = node.children[char]

node.value = key

def search(self, key):

node = self.root

for char in key:

if char not in node.children:

return None

else:

node = node.children[char]

return node.value

def display_node(self, node):

if (node.value != None):

print node.value

for char in node.children.keys():

if char in node.children:

self.display_node(node.children[char])

return

def display(self):

self.display_node(self.root)

def searchAndInsert(self,m):

if(self.search(m)==None):

self.insert(m)

return False

return True

"""test

trie = NumTree()

print trie.searchAndInsert([1,2,3,4,5,6,7,8])

print trie.searchAndInsert([1,2,3,4,5,6,7,8,9])

trie.display()

"""

好了,这样再看searchTree的代码就比较清晰了呗,

哎,好吧,我承认这等低劣之作也只有我等渣渣才能写出,其实再看时,可以发现很多地方都可以优化的,比如二维转一维,这个其实花费的不少时间,其实可以在内部以一维形式存在的~

你可能感兴趣的:(python,八数码)