所以,我的妻子在Steam上玩Hammerwatch。她遇到了一个谜题,我决定尝试编写解决方案。
拼图的工作方式如下:
激活开关可以打开或关闭该开关,也可以切换其相邻的开关。
以下是游戏中拼图的YouTube视频:
http://www.youtube.com/watch?v=OM1XD7IZ0cg
我想出了如何让拼图的机制正常工作。我终于意识到我有两个选择让电脑解决这个问题:
A)通过随机选择开关让计算机解决
......或者......
B)创建一种算法,让计算机更有效地解决难题。
作为一名新程序员(在CodeAcademy教程的中途,在LPTHW中途,目前正在通过麻省理工学院edX计算机科学Python课程),我觉得我的能力有限,无法解决这个问题。我来学习了!请帮忙!
我需要帮助找出一种更好的方法来随机解决这个问题,甚至更好,有一个算法可以让计算机系统地解决难题。
我唯一可以做的就是让计算机将拼图状态存储在列表或字典中,通过跳过那些存储的状态来协助程序,指向程序新的可能解决方案
我打算允许用户使用前9个raw_input输入拼图板的当前状态。然后它进入一个循环,随机切换拼图板的开关,直到它们全部打开。
P.S。:当我注册StackOverflow帐户并输入此消息时,我的计算机一直在后台运行该程序以找到解决方案。大约一个小时,仍然没有找到解决方案,它目前正在进行~92,000,000次迭代。我觉得它不起作用......
import random
def switcheroo(x):
"""
switches 'x' to 1 if it's a 0 and vice-versa
"""
if x == 0:
x = 1
else:
x = 0
return x
# original input variables
a1 = 0
a2 = 0
a3 = 0
b1 = 0
b2 = 0
b3 = 0
c1 = 0
c2 = 0
c3 = 0
# puzzleboard
print "\n\n"
print " 1 2 3 "
print " -------------"
print "a |",a1,"|",a2,"|",a3,"|"
print " -------------"
print "b |",b1,"|",b2,"|",b3,"|"
print " -------------"
print "c |",c1,"|",c2,"|",c3,"|"
print " -------------"
print "\n\n"
print "What's ON/OFF? (type 0 for OFF, 1 for ON)"
a1 = int(raw_input("a1: "))
a2 = int(raw_input("a2: "))
a3 = int(raw_input("a3: "))
b1 = int(raw_input("b1: "))
b2 = int(raw_input("b2: "))
b3 = int(raw_input("b3: "))
c1 = int(raw_input("c1: "))
c2 = int(raw_input("c2: "))
c3 = int(raw_input("c3: "))
# for counting the iterations within the loop
iteration = 0
# to stop loop if all switches are ON
ans = a1 and a2 and a3 and b1 and b2 and b3 and c1 and c2 and c3
while ans == False:
# randomly generates number, flipping random switches
counter = random.randint(1,9)
if counter == 1:
switch = "a1"
elif counter == 2:
switch = "a2"
elif counter == 3:
switch = "a3"
elif counter == 4:
switch = "b1"
elif counter == 5:
switch = "b2"
elif counter == 6:
switch = "b3"
elif counter == 7:
switch = "c1"
elif counter == 8:
switch = "c2"
elif counter == 9:
switch = "c9"
# PUZZLE MECHANICES #
if switch == "a1":
a1 = switcheroo(a1)
a2 = switcheroo(a2)
b1 = switcheroo(b1)
if switch == "a2":
a2 = switcheroo(a2)
a1 = switcheroo(a1)
a3 = switcheroo(a3)
b2 = switcheroo(b2)
if switch == "a3":
a3 = switcheroo(a3)
a2 = switcheroo(a2)
b3 = switcheroo(b3)
if switch == "b1":
b1 = switcheroo(b1)
b2 = switcheroo(b2)
a1 = switcheroo(a1)
c1 = switcheroo(c1)
if switch == "b2":
b2 = switcheroo(b2)
a2 = switcheroo(a2)
b1 = switcheroo(b1)
b3 = switcheroo(b3)
c2 = switcheroo(c2)
if switch == "b3":
b3 = switcheroo(b3)
b1 = switcheroo(b1)
b2 = switcheroo(b2)
c3 = switcheroo(c3)
# Edit 1
if switch == "c1":
c1 = switcheroo(c1)
c2 = switcheroo(c2)
b1 = switcheroo(b1)
if switch == "c2":
c2 = switcheroo(c2)
c1 = switcheroo(c1)
c3 = switcheroo(c3)
b2 = switcheroo(b2)
if switch == "c3":
c3 = switcheroo(c3)
c2 = switcheroo(c2)
b3 = switcheroo(b3)
if switch == "stop":
break
# prints puzzle-board state at end of loop iteration
print "\n\n"
print " 1 2 3 "
print " -------------"
print "a |",a1,"|",a2,"|",a3,"|"
print " -------------"
print "b |",b1,"|",b2,"|",b3,"|"
print " -------------"
print "c |",c1,"|",c2,"|",c3,"|"
print " -------------"
print "\n\n"
# prints which # was randomly generated
print "random #: ", counter
# tracks loop iteration
iteration += 1
print "iteration", iteration
if ans == True:
print "I figured it out!"
答案 0 :(得分:1)
有一种众所周知的方法可以解决这个问题。设x_1,...,x_n是对应于是否按下第n个按钮作为解决方案的一部分的变量,并让a_1,...,a_n为初始状态。
假设您正在解决3x3问题,并且变量设置如下:
x_1 x_2 x_3
x_4 x_5 x_6
x_7 x_8 x_9
,这个初始状态是:
a_1 a_2 a_3
a_4 a_5 a_6
a_7 a_8 a_9
现在,您可以写下解决方案必须满足的一些方程式(在算术模2中)。它基本上编码了关于哪些开关导致特定灯光切换的规则。
a_1 = x_1 + x_2 + x_4
a_2 = x_1 + x_2 + x_3 + x_5
...
a_5 = x_2 + x_4 + x_5 + x_6 + x_8
...
a_9 = x_6 + x_8 + x_9
现在你可以使用高斯消元法来解决这组联立方程。因为你在算术模2中工作,它实际上比实数上的联立方程更容易。例如,要摆脱第二个等式中的x_1,只需将第一个等式添加到其中即可。
a_1 + a_2 = (x_1 + x_2 + x_4) + (x_1 + x_2 + x_3 + x_5) = x_3 + x_4 + x_5
具体来说,这是算术模2中的高斯消元算法:
现在,E_n是仅包含x_n的等式。您可以将从此处获得的x_n值替换为先前的等式。重复E_ {n-1},...,E_1。
总的来说,这解决了O(n ^ 3)操作中的问题。
这是一些代码。
class Unsolvable(Exception):
pass
def switches(n, m, vs):
eqs = []
for i in xrange(n):
for j in xrange(m):
eq = set()
for d in xrange(-1, 2):
if 0 <= i+d < n: eq.add((i+d)*m+j)
if d != 0 and 0 <= j+d < m: eq.add(i*m+j+d)
eqs.append([vs[i][j], eq])
N = len(eqs)
for i in xrange(N):
for j in xrange(i, N):
if i in eqs[j][1]:
eqs[i], eqs[j] = eqs[j], eqs[i]
break
else:
raise Unsolvable()
for j in xrange(i+1, N):
if i in eqs[j][1]:
eqs[j][0] ^= eqs[i][0]
eqs[j][1] ^= eqs[i][1]
for i in xrange(N-1, -1, -1):
for j in xrange(i):
if i in eqs[j][1]:
eqs[j][0] ^= eqs[i][0]
eqs[j][1] ^= eqs[i][1]
return [(i//m,i%m) for i, eq in enumerate(eqs) if eq[0]]
print switches(4, 3, ([1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]))
您可以为其指定开关阵列的高度和宽度,以及一次一行的初始状态。它会返回您需要按下的开关以关闭所有灯。
答案 1 :(得分:0)
首先,您只需设置一次。你需要在循环中评估它。 (虽然这可能是因为缩进的缩进)。这可能就是为什么你目前的方法没有完成的原因。
顺便说一句,更“自然”的表示可能是使用bool数组(甚至是0-511之间的数字),因此切换器成为
a1 = not a1
随机方法也会给你很多打字。我怀疑你想从解决方案走向当前配置的角度考虑这个问题。把它想象成一个迷宫。有2 ^ 9 = 512种可能的配置(或迷宫中的位置)。每次你按下开关就像在迷宫中迈出一步。现在你真正想要的是找到从初始配置到解决方案的最短路径。那么看看Dijkstra的算法: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
答案 2 :(得分:0)
我认为你应该能够找到一个算法。不应该太难。
首先,没有必要多次按下任何开关。由于第二次只取消你在第一次击中时所做的操作并返回初始状态(无论你在此期间对其他开关做了什么)。
然后我们打电话
A11, A12, A13
A21, A22, A23
A31, A32, A33
启动时开关的状态。
使用相同的坐标系,让我们使用T作为触摸次数。 根据我们之前所说的,每个T都是0或1。
如果你可以做一点数学运算,并且你知道代数在一个域中进行操作,其中值只取0或1然后我认为你应该能够将问题减少到该空间中的问题类似的东西:
A11 + T11 + T12 + T21 = 1
A12 + T11 + T12 + T13 + T22 = 1
A13 + T12 + T13 + T23 = 1
...
如果你把它作为矩阵运算,你可能只能反转一个矩阵,然后根据As找到Ts。
也许这更适合于另一个比Stackoverflow更具数学导向性的网站...