闲的蛋疼:用算法解移动一根火柴问题

最近一直看到诸如


的问题,闲的无聊,便写了段代码来自动解答这类问题。

初步只能支持标准的数字变化,即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需要两根火柴棍,这只有一根)。当然,这个问题也很好修改,只要对于字符变换规则进行精心设置即可。

另外,自动生成问题也很简单,只要先随机生成一个等式,然后移动一根火柴形成新的式子即可。

猜你喜欢

转载自blog.csdn.net/lucytheslayer/article/details/80048788
今日推荐