用车牌

时间:2018-03-04 18:24:34

标签: python haskell

当我小的时候,我和堂兄弟一起玩下一场比赛:

"汽车牌照归零":使用汽车牌照号码中的数字并使用基本操作(总和,减法,产品和除法)一次每一个,你必须找到一个等于0的订单。

例如,如果我们有以下车牌:

2591 --> (2*5)-(9+1) = 0

2491 --> (2*4)+1 -9 = 0

我想在Haskell或Python中创建一个能够播放此游戏的程序,并打印出结果为0的步骤。

getZero 2491 =  2*4+1-9
getZero 2591 =  2*5-9+1

也许不可能做到这一点;我希望你能帮助我。

2 个答案:

答案 0 :(得分:1)

这可能不是主题,而是一个有趣的玩具拼图。

想到这个问题的一个简单方法可能是四个不同的部分:

  1. 参数的无聊管道并打印结果
  2. 构造给定数字的二进制运算的所有可能表达式。
  3. 解释这些表达式以获取数字文字并删除任何非零的结果。
  4. 相当打印表达。
  5. 我们可以执行额外的步骤,例如根据代数规则过滤掉重复项(a + bb + a在道德上是相同的),但我跳过了。

    无聊的管道只是得到我们的板号(每个参数一个),将它们分解为数字,运行我们的计算,然后打印解决方案。

    import Data.Foldable
    import Data.List
    import System.Environment
    
    main :: IO ()
    main =
      do ns <- getArgs
         for_ ns $ \n -> do
            let digitList = map (read . (:[])) (n :: String) :: [Int]
            putStrLn $ "---------- " ++ show n ++ " ----------"
            putStrLn $ unlines (map render (solutions digitList))
    

    构建真正的乐趣在于构造一个二元树操作和叶子与文字。首先我们定义表达式语言:

    data Expr = Add Expr Expr
              | Sub Expr Expr
              | Mul Expr Expr
              | Div Expr Expr
              | Lit Int
            deriving (Eq, Ord, Show)
    

    我们可以将这些表达式与Haskell列表monad一起使用来构建所有可能的表达式(假设非空列表作为输入):

    operations :: [Expr -> Expr -> Expr]
    operations = [Add, Sub, Mul, Div]
    
    exprsOf :: [Expr] -> [Expr]
    exprsOf [term] = [term]
    exprsOf xs =
      do x <- xs
         y <- (delete x xs)
         o <- operations
         exprsOf (o x y : delete y (delete x xs))
    

    也就是说,x是原始表达式集合中的元素之一。 y是另一个元素(但不是x)。 o是我们的合法操作之一(加法,减法等)。我们递归地减少这个列表大小,直到我们留下顶级表达式(变量名term)。如果您不了解可以操作的操作 - 那些让您感到困惑的特定部分会产生罚款(主题)问题。

    解释通过构建表达式,我们现在可以解释它们并过滤掉任何不会产生零的表达式。

    当我们看到+构造函数时,解释器只使用加法(Add),其他操作也是如此。我将所有内容都提升到了Maybe Applicative中,因为我不希望除以零或其余部分显示在我们的结果中。

    interp :: Expr -> Maybe Int
    interp (Lit n) = Just n
    interp (Add a b) = (+) <$> interp a <*> interp b
    interp (Sub a b) = (-) <$> interp a <*> interp b
    interp (Mul a b) = (*) <$> interp a <*> interp b
    interp (Div a b) | interp b == Just 0 = Nothing
                     | interp b == Nothing = Nothing
                     | otherwise          =
            case divMod <$> interp a <*> interp b of
                Nothing -> Nothing
                Just (x,0) -> Just x
                _ -> Nothing -- Ignore uneven division
    

    应用此解释只需过滤Just 0

    solutions :: [Int] -> [Expr]
    solutions xs = filter ((== Just 0) . interp) $ exprsOf (map Lit xs)
    

    渲染最后,有一个非常难看的渲染函数,只是为了发出正确的括号,以便我们看到正确的操作顺序:

    render :: Expr -> String
    render (Lit n) = show n
    render (Add a b) = "(" ++ render a ++ " + " ++ render b ++ ")"
    render (Sub a b) = "(" ++ render a ++ " - " ++ render b ++ ")"
    render (Mul a b) = "(" ++ render a ++ " * " ++ render b ++ ")"
    render (Div a b) = "(" ++ render a ++ " / " ++ render b ++ ")"
    

    运行示例

    *Main> :main 2591
    ---------- "2591" ----------
    (((2 * 5) - 9) - 1)
    (1 - ((2 * 5) - 9))
    (((2 * 5) - 1) - 9)
    (9 - ((2 * 5) - 1))
    ((9 - (2 * 5)) + 1)
    (1 + (9 - (2 * 5)))
    ((9 + 1) - (2 * 5))
    ((2 * 5) - (9 + 1))
    ((1 - (2 * 5)) + 9)
    (9 + (1 - (2 * 5)))
    ((1 + 9) - (2 * 5))
    ((2 * 5) - (1 + 9))
    (((5 * 2) - 9) - 1)
    (1 - ((5 * 2) - 9))
    (((5 * 2) - 1) - 9)
    (9 - ((5 * 2) - 1))
    ((9 - (5 * 2)) + 1)
    (1 + (9 - (5 * 2)))
    ((9 + 1) - (5 * 2))
    ((5 * 2) - (9 + 1))
    ((1 - (5 * 2)) + 9)
    (9 + (1 - (5 * 2)))
    ((1 + 9) - (5 * 2))
    ((5 * 2) - (1 + 9))
    (((9 + 1) / 2) - 5)
    (5 - ((9 + 1) / 2))
    (((9 + 1) / 5) - 2)
    (2 - ((9 + 1) / 5))
    ((2 * 5) - (9 + 1))
    ((9 + 1) - (2 * 5))
    ((5 * 2) - (9 + 1))
    ((9 + 1) - (5 * 2))
    (((1 + 9) / 2) - 5)
    (5 - ((1 + 9) / 2))
    (((1 + 9) / 5) - 2)
    (2 - ((1 + 9) / 5))
    ((2 * 5) - (1 + 9))
    ((1 + 9) - (2 * 5))
    ((5 * 2) - (1 + 9))
    ((1 + 9) - (5 * 2))
    

答案 1 :(得分:0)

这不是最好的实现,因为有很多重复,但确实得到了结果。

使用itertools,我生成了数字和操作可能存在的每种不同状态(排列),例如:

[('1', '2', '3', '4'), ('1', '2', '4', '3'), ('1', '3', '2', '4'), ('1', '3', '4', '2'), ('1', '4', '2', '3'), ....... ('4', '1', '3', '2'), ('4', '2', '1', '3'), ('4', '2', '3', '1'), ('4', '3', '1', '2'), ('4', '3', '2', '1')]

[('+', '-', '*'), ('+', '*', '-') ...... ('/', '-', '*'), ('/', '*', '-')]

...然后,我创建了一个函数,为每个可能的计算状态添加括号:

[1,'+',2,'-',3,'*',4]变为:

1 + 2 - 3 * 4
( 1 + 2 ) - ( 3 * 4 )
( 1 + 2 - 3 ) * 4
1 + ( 2 - 3 * 4 )
( 1 + 2 ) - 3 * 4
1 + 2 - ( 3 * 4 )
1 + ( 2 - 3 ) * 4 

...然后,评估每种不同的可能性,如果它为零,我将其打印出来。

下面是所有代码:

from itertools import permutations, combinations, chain
import sys

operations = ['+', '-', '*', '/'] # basic operations

# add every combination of brackets to the calculation
def addBrackets(calc):
    possibilities = []
    possibilities.append(" ".join(calc))
    possibilities.append(" ".join(str(s) for s in list(chain(["("], calc[:3], [")"], [calc[3]], ["("], calc[4:], [")"]))))
    possibilities.append(" ".join(str(s) for s in list(chain(["("], calc[:-2], [")"], calc[-2:]))))
    possibilities.append(" ".join(str(s) for s in list(chain(calc[:2], ["("], calc[2:], [")"]))))
    possibilities.append(" ".join(str(s) for s in list(chain(["("], calc[:3], [")"], calc[3:]))))
    possibilities.append(" ".join(str(s) for s in list(chain(calc[:4], ["("], calc[4:], [")"]))))
    return possibilities

def getZero(n): # 2491
    nums = [x for x in str(n)] # [2, 4, 9, 1]
    while len(nums) < 4:
        nums = ['0'] + nums # add zeroes if the input was less than 1000 e.g. [5, 3, 1] -> [0, 5, 3, 1]
    possible_number_order = list(permutations(nums)) # get every number order
    possible_op_order = list(combinations(operations, 3)) # get combinations of 3 of the operations
    possible_op_permutations = list(chain(list(permutations(p)) for p in possible_op_order)) # chain together all the permutations of each combination
    possible_op_order = []
    for perms in possible_op_permutations: 
        possible_op_order.extend(perms) # add all the lists into one
    for n in possible_number_order: # for each number order
        for op in possible_op_order: # for each operation order
            calculation = [n[0],op[0],n[1],op[1],n[2],op[2],n[3]] # create a list with the order of the operation
            for calc in addBrackets(calculation): # for each bracket position
                try:
                    result = eval(calc) # evaluate
                    if abs(result) <= 0.001: # to check for float comparison
                        print("{} = {}".format(calc, 0))
                except: # ZeroDivisionError
                    continue

if __name__ == "__main__":
    for user_input in [int(n) for n in sys.argv[1:]]:
        print("-------{0:04}-------".format(user_input)) # print 0 padded output
        getZero(user_input)

输入输入作为命令行参数:

$ python3.6 zerogame.py 2491 2591
-------2491-------
( 2 * 4 - 9 ) + 1 = 0
( 2 * 4 ) - 9 + 1 = 0
( 2 * 4 ) + ( 1 - 9 ) = 0
( 2 * 4 + 1 ) - 9 = 0
( 2 * 4 ) + 1 - 9 = 0
2 * 4 + ( 1 - 9 ) = 0
2 + ( 1 - 9 ) / 4 = 0
( 4 * 2 - 9 ) + 1 = 0
( 4 * 2 ) - 9 + 1 = 0
( 4 * 2 ) + ( 1 - 9 ) = 0
( 4 * 2 + 1 ) - 9 = 0
( 4 * 2 ) + 1 - 9 = 0
4 * 2 + ( 1 - 9 ) = 0
4 + ( 1 - 9 ) / 2 = 0
9 - ( 2 * 4 + 1 ) = 0
9 - ( 4 * 2 + 1 ) = 0
9 - ( 1 + 2 * 4 ) = 0
9 - ( 1 + 4 * 2 ) = 0
( 1 + 2 * 4 ) - 9 = 0
1 + ( 2 * 4 - 9 ) = 0
1 + ( 2 * 4 ) - 9 = 0
( 1 + 4 * 2 ) - 9 = 0
1 + ( 4 * 2 - 9 ) = 0
1 + ( 4 * 2 ) - 9 = 0
( 1 - 9 ) + ( 2 * 4 ) = 0
( 1 - 9 ) + 2 * 4 = 0
1 - 9 + ( 2 * 4 ) = 0
( 1 - 9 ) / 2 + 4 = 0
( 1 - 9 ) + ( 4 * 2 ) = 0
( 1 - 9 ) + 4 * 2 = 0
1 - 9 + ( 4 * 2 ) = 0
( 1 - 9 ) / 4 + 2 = 0
-------2591-------
( 2 * 5 ) - ( 9 + 1 ) = 0
2 * 5 - ( 9 + 1 ) = 0
( 2 * 5 ) - ( 1 + 9 ) = 0
2 * 5 - ( 1 + 9 ) = 0
2 - ( 9 + 1 ) / 5 = 0
2 - ( 1 + 9 ) / 5 = 0
( 5 * 2 ) - ( 9 + 1 ) = 0
5 * 2 - ( 9 + 1 ) = 0
( 5 * 2 ) - ( 1 + 9 ) = 0
5 * 2 - ( 1 + 9 ) = 0
5 - ( 9 + 1 ) / 2 = 0
5 - ( 1 + 9 ) / 2 = 0
( 9 - 2 * 5 ) + 1 = 0
9 - ( 2 * 5 ) + 1 = 0
( 9 - 5 * 2 ) + 1 = 0
9 - ( 5 * 2 ) + 1 = 0
( 9 + 1 ) - ( 2 * 5 ) = 0
9 + ( 1 - 2 * 5 ) = 0
( 9 + 1 ) - 2 * 5 = 0
9 + 1 - ( 2 * 5 ) = 0
( 9 + 1 ) / 2 - 5 = 0
( 9 + 1 ) - ( 5 * 2 ) = 0
9 + ( 1 - 5 * 2 ) = 0
( 9 + 1 ) - 5 * 2 = 0
9 + 1 - ( 5 * 2 ) = 0
( 9 + 1 ) / 5 - 2 = 0
( 1 - 2 * 5 ) + 9 = 0
1 - ( 2 * 5 ) + 9 = 0
( 1 - 5 * 2 ) + 9 = 0
1 - ( 5 * 2 ) + 9 = 0
( 1 + 9 ) - ( 2 * 5 ) = 0
1 + ( 9 - 2 * 5 ) = 0
( 1 + 9 ) - 2 * 5 = 0
1 + 9 - ( 2 * 5 ) = 0
( 1 + 9 ) / 2 - 5 = 0
( 1 + 9 ) - ( 5 * 2 ) = 0
1 + ( 9 - 5 * 2 ) = 0
( 1 + 9 ) - 5 * 2 = 0
1 + 9 - ( 5 * 2 ) = 0
( 1 + 9 ) / 5 - 2 = 0