这是一个基本的大学拼图问题,我虽然我会指出,但是当涉及到实施它时,我就被卡住了。
背景:4位路径寻找算法,因此例如给出目标号1001和目标号1040,使用BF,DF等等找到路径。
允许移动:按任意顺序从4位中的任何一位加1或减1,你不能加9,或从0减去。你不能在同一位数上加或减两次,即得到1000到3000,你不能做2000 - > 3000,因为你将在第一个数字上加两次。所以你必须做2000-> 2100-> 3100-> 3000。
我陷入困境:我无法弄清楚我将如何以编程方式表示问题,更不用说算法,但我将如何代表1001并向1004迈进。什么数据结构?我如何识别邻居?
有人可以把我推向正确的方向吗?
答案 0 :(得分:2)
如果我理解正确,您正在寻找一种方法来表示您的问题。也许你很难找到它,因为问题使用的是4位数。尝试缩小尺寸,并考虑2位数的相同问题(另请参阅this other answer)。假设您想使用问题规则从10移动到30。现在您有两个维度,您可以轻松地在10x10矩阵上表示您的问题(每个数字可以有10个不同的值):
:
在上图中,S
是您的来源位置(10),T
是您的目标位置(30)。现在您可以应用问题的规则。我不知道你想找到什么:有多少路径?只是一条路?最短的路径?
无论如何,处理动作的算法草图是:
如果你看T
你根据问题的规则看到,你可以到达的唯一可能的邻居(30)是(20),(31)和(40) )。
现在,我们假设您选择(20),那么您可以到达的唯一可能的邻居(20)是(21)。
现在,你被迫选择(21),你可以达到的唯一可能的邻居(21)是(11)和(31)。
最后,如果你选择(11),那么你可以达到的唯一可能的邻居(11)是(10)和(12)......和(10)是你的来源{{1} ,所以你已经完成了。
这只是算法的草图(它没有说明如何从可能的邻居列表中进行选择),但我希望它能给你一个想法。
最后,当您解决了二维空间中的问题时,您可以将其扩展到三维空间,并扩展到四维空间(您的原始问题),其中您有一个10x10x10x10矩阵。每个维度始终有10个插槽/位置。
我希望这会有所帮助。
答案 1 :(得分:1)
我想到的第一件事就是这只是一个额外维度的点。也就是说,而不是[x,y]来表示二维网格上的点,你有[w,x,y,z]来表示四维空间中的一个点。
我在你的问题中不确定的一件事是当你将1添加到9时会发生什么。是否有剩余部分被结转,或者这个数字是否被推翻到零?或者这是否代表了无法跨越的边缘?
在任何情况下,我都会将此视为一种具有额外维度的点,并适当地应用您的算法。
答案 2 :(得分:1)
我将从有向图开始,其中每个节点代表您的数字加上额外的自由度(因为约束)。例如。您可以将此视为添加到您的号码中的另一个数字,表示“最后更改的数字”。例如。从1001[0]
开始,此节点连接到
1001[0] -> 2001[1]
1001[0] -> 0001[1]
1001[0] -> 1101[2]
1001[0] -> 1011[3]
1001[0] -> 1002[4]
1001[0] -> 0000[4]
或2001[1]
到
2001[1] -> 2101[2]
2001[1] -> 2011[3]
2001[1] -> 2002[4]
2001[1] -> 2000[4]
请注意:????[0]
节点仅允许作为起始节点。每条边总是连接不同的最后一位数。
因此,对于每个数字xyzw[d]
,传出边是由
xyzw[d] -> (x+1)yzw[1] if x<9 && d!=1
xyzw[d] -> (x-1)yzw[1] if x>0 && d!=1
xyzw[d] -> x(y+1)zw[2] if y<9 && d!=2
xyzw[d] -> x(y-1)zw[2] if y>0 && d!=2
xyzw[d] -> xy(z+1)w[3] if z<9 && d!=3
xyzw[d] -> xy(z-1)w[3] if z>0 && d!=3
xyzw[d] -> xyz(w+1)[4] if w<9 && d!=4
xyzw[d] -> xyz(w-1)[4] if w>0 && d!=4
您的目标现在是四个节点1004[1]
.. 1004[4]
。如果您的算法需要单个目标节点,则可以为每个数字添加一个人工边????[?] -> ????[0]
,然后将1004[0]
作为最终结果。
答案 3 :(得分:1)
你可以将每个状态表示为长度为4的整数数组。然后有一个图形结构来连接状态(我不建议完全构建图形,因为这单独需要O(10 ^ 4)内存)。
然后使用A* algorithm遍历图表并到达目标节点。
或者只是使用BFS遍历例程,在处理状态时生成相邻状态。你必须跟踪已经访问过的状态(某种类似于数据结构的集合)。
答案 4 :(得分:0)
当访问节点时,你需要遍历节点的所有子节点并访问其中的一些节点,所以你应该只实现子生成器函数获取一个参数 - 当前数字如1001.它是算法的“核心”,然后你需要使用recoursion遍历一棵树。以下是不工作的代码,进入无限的再生,但是是一个解释我的想法的草图:
from random import random
def shouldVisit(digits):
#very smart function defining search strategy
return random() > 0.5
def traverse(source, target, prev):
if source == target:
print 'wow!'
return
for i in range(len(source)):
if i == prev:
continue
d = source[i]
if d > 0:
res = list(source)
res[i] = d - 1
if shouldVisit(res):
traverse(res, target, i)
if d < 9:
res = list(source)
res[i] = d + 1
if shouldVisit(res):
traverse(res, target, i)
def traverse0(source, target):
def tolist(num):
digits = []
while num:
digits.append(num % 10)
num /= 10
return digits
traverse(tolist(source), tolist(target), -1)
traverse0(1001, 1040)
非常智能的存根函数shouldVisit
可以检查例如是否已经访问过给定节点