最近一直看到诸如
的问题,闲的无聊,便写了段代码来自动解答这类问题。
初步只能支持标准的数字变化,即9可以变成3,5,6等,但是不能变成13因为一根火柴棍形成不了1(1需要两根火柴棍).另外数字间不能插入符号,例如132不能变成13-2,因为没有空间可以插入。
允许更为灵活的变换需要更多的代码,在此不过多深入思考了
思路如下:
1)首先求出每个字符“0~9 - +”增加一根火柴,减少一根火柴,自身移动一根火柴,所能形成的新的字符。
例如9,如果增加一根火柴,那么可以变成8,如果9在等式的最左边,也可以变成-9;如果减少一根火柴,那么可以变成5或者3,如果自身移动一根火柴,那么可以变成6.
2)解析问题串,譬如 3+6=3,解析为:
q = [
Item("3",PositionType.FrontEmpty,Side.Left),
Item("+",PositionType.Default,Side.Left),
Item("6",PositionType.Default,Side.Left),
Item("3",PositionType.Default,Side.Right)
]
3)对于已解析的q,对其进行如下变换。
.1 对任一字符进行自身移动操作,对于新产生的q,计算其是否是等式,若是则输出答案。
.2 对任一字符进行减少一根火柴操作,对于新产生的q,对其任一字符进行增加一根火柴操作,生成新的q1.对于q1,计算其是否是等式,若是则输出答案。
以下是完整的源码:
其中, 使用7位数组标定一个数字(1为有边,0位无边),大意如图:
class ActionType:
Dec = 0 #将火柴移出
Inc = 1 #将火柴移入
SelfTrans = 2
class PositionType:
Default = 0
FrontEmpty = 1
class Side:
Left = 0
Right = 1
class Item:
def __init__(self,value,positionType,side):
self.value = value
self.positionType = positionType
self.side = side
class Match:
def __init__(self):
self.G = {
0: [1, 1, 1, 1, 1, 1, 0],
1: [0, 1, 1, 0, 0, 0, 0],
2: [1, 1, 0, 1, 1, 0, 1],
3: [1, 1, 1, 1, 0, 0, 1],
4: [0, 1, 1, 0, 0, 1, 1],
5: [1, 0, 1, 1, 0, 1, 1],
6: [1, 0, 1, 1, 1, 1, 1],
7: [1, 1, 1, 0, 0, 0, 0],
8: [1, 1, 1, 1, 1, 1, 1],
9: [1, 1, 1, 1, 0, 1, 1]
}
self.M = {}
self.image_cache = {}
def _makeImage(self,c):
image = None
if c == "+":
image = [
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "@", "*", "*"],
["*", "*", "@", "*", "*"],
["@", "@", "@", "@", "@"],
["*", "*", "@", "*", "*"],
["*", "*", "@", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"]
]
elif c == "-":
image = [
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["@", "@", "@", "@", "@"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"]
]
elif c == "=":
image = [
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["@", "@", "@", "@", "@"],
["*", "*", "*", "*", "*"],
["@", "@", "@", "@", "@"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"],
["*", "*", "*", "*", "*"]
]
else:
d = ord(c) - ord('0')
if 0 <= d <= 9:
image = [["*" for j in range(5)] for i in range(9)]
if self.G[d][0] == 1:
image[0][0] = "@"
image[0][1] = "@"
image[0][2] = "@"
image[0][3] = "@"
image[0][4] = "@"
if self.G[d][1] == 1:
image[0][4] = "@"
image[1][4] = "@"
image[2][4] = "@"
image[3][4] = "@"
image[4][4] = "@"
if self.G[d][2] == 1:
image[4][4] = "@"
image[5][4] = "@"
image[6][4] = "@"
image[7][4] = "@"
image[8][4] = "@"
if self.G[d][3] == 1:
image[8][0] = "@"
image[8][1] = "@"
image[8][2] = "@"
image[8][3] = "@"
image[8][4] = "@"
if self.G[d][4] == 1:
image[4][0] = "@"
image[5][0] = "@"
image[6][0] = "@"
image[7][0] = "@"
image[8][0] = "@"
if self.G[d][5] == 1:
image[0][0] = "@"
image[1][0] = "@"
image[2][0] = "@"
image[3][0] = "@"
image[4][0] = "@"
if self.G[d][6] == 1:
image[4][0] = "@"
image[4][1] = "@"
image[4][2] = "@"
image[4][3] = "@"
image[4][4] = "@"
return image
def setup(self):
for c in "=+-0123456789":
self.image_cache[c] = self._makeImage(c)
self._makeM()
def draw(self,str):
all_image = [[] for i in range(9)]
for c in str:
if c not in self.image_cache:
raise Exception("输入串不合法.")
image = self.image_cache[c]
for i in range(9):
a = []
for j in range(5):
if image[i][j] == "@":
a.append("*")
else:
a.append(" ")
all_image[i].append(" ".join(a))
for i in range(9):
print(" ".join(all_image[i]))
def _findMatch(self, d, edge, actionType):
g = self.G[d].copy()
if actionType == ActionType.Inc:
if g[edge] == 1:
return None
g[edge] = 1
for i in range(10):
if g == self.G[i]:
return i
elif actionType == ActionType.Dec:
if g[edge] == 0:
return None
g[edge] = 0
for i in range(10):
if g == self.G[i]:
return i
def _findSelfTransMatch(self, d):
re = []
for i in range(7):
for j in range(i + 1, 7):
g = self.G[d].copy()
if g[i] != g[j]:
t = g[i]
g[i] = g[j]
g[j] = t
for z in range(10):
if g == self.G[z] and d != z:
re.append(z)
return re
def _makeM(self):
self.M["+"] = {
ActionType.Inc: {
PositionType.Default: []
},
ActionType.Dec: {
PositionType.Default: ["-"]
},
ActionType.SelfTrans: {
PositionType.Default: []
}
}
self.M["-"] = {
ActionType.Inc: {
PositionType.Default: ["+"]
},
ActionType.Dec: {
# PositionType.Default: [""]
PositionType.Default: []
},
ActionType.SelfTrans: {
PositionType.Default: []
}
}
for i in range(10):
self.M[i] = {
ActionType.Inc: {
PositionType.Default: [],
PositionType.FrontEmpty: [-i]
},
ActionType.Dec: {
PositionType.Default: [],
},
ActionType.SelfTrans: {
PositionType.Default: []
}
}
for j in range(7):
r = self._findMatch(i, j, ActionType.Inc)
if r is not None:
self.M[i][ActionType.Inc][PositionType.Default].append(r)
r = self._findMatch(i, j, ActionType.Dec)
if r is not None:
self.M[i][ActionType.Dec][PositionType.Default].append(r)
self.M[i][ActionType.SelfTrans][PositionType.Default] = self._findSelfTransMatch(i)
def _getMFor(self,c):
if c == "+" or c == "-":
return self.M[c]
d = ord(c) - ord('0')
if 0 <= d <= 9:
return self.M[d]
def _ifBalance(self,q):
exp = ""
eqReached = False
for item in q:
if not eqReached and item.side == Side.Right:
eqReached = True
exp += "=="
exp += str(item.value)
#print(exp)
return eval(exp)
def _parseToQ(self,qStr):
q = []
bEqFound = False
bFirst = True
for c in qStr:
if bFirst:
q.append(Item(c, PositionType.FrontEmpty, Side.Left))
bFirst = False
continue
if c == "=":
bEqFound = True
continue
if bEqFound:
q.append(Item(c,PositionType.Default,Side.Right))
else:
q.append(Item(c, PositionType.Default, Side.Left))
return q
def _printAnswer(self,q):
s = ""
eqReached = False
for item in q:
if not eqReached and item.side == Side.Right:
eqReached = True
s += "="
s += str(item.value)
print(s)
def solve(self,qStr):
print(qStr," ==>")
# q = [
# Item("3",PositionType.FrontEmpty,Side.Left),
# Item("+",PositionType.Default,Side.Left),
# Item("6",PositionType.Default,Side.Left),
# Item("3",PositionType.Default,Side.Right)
# ]
q = self._parseToQ(qStr)
for (i,item) in enumerate(q):
m = self._getMFor(item.value)
# try self-trans
for t in m[ActionType.SelfTrans][PositionType.Default]:
newQ = q.copy()
newQ[i] = Item(t,item.positionType,item.side)
if self._ifBalance(newQ):
self._printAnswer(newQ)
#dec => inc
for t0 in m[ActionType.Dec][PositionType.Default]:
newQ0 = q.copy()
newQ0[i] = Item(t0, item.positionType, item.side)
for(i1,item1) in enumerate(newQ0):
if i == i1:
continue
m1 = self._getMFor(item1.value)
t1List = []
if item1.positionType == PositionType.FrontEmpty:
t1List.extend(m1[ActionType.Inc][PositionType.FrontEmpty])
t1List.extend(m1[ActionType.Inc][PositionType.Default])
for t1 in t1List:
newQ1 = newQ0.copy()
newQ1[i1] = Item(t1,item1.positionType,item1.side)
if self._ifBalance(newQ1):
self._printAnswer(newQ1)
print("\n")
if __name__ == "__main__":
match = Match()
match.setup()
# match.draw("20-3+9=25")
print(match.M[9])
match.solve("3+6=3")
match.solve("9+5=9")
match.solve("19-3=6")
输出为:
3+6=3 ==>
9-6=3
3+0=3
9+5=9 ==>
3+6=9
3+5=8
19-3=6 ==>
15-9=6
但是对于经典问题14-1+1=3,则输出无解(正确答案为114-111=3),那是因为1+1移掉+号上的一横,算法也并不认为它是1,(1需要两根火柴棍,这只有一根)。当然,这个问题也很好修改,只要对于字符变换规则进行精心设置即可。
另外,自动生成问题也很简单,只要先随机生成一个等式,然后移动一根火柴形成新的式子即可。