给定一个数字列表,使用它们之间的四个基本操作来找到另一个给定的数字

时间:2013-07-11 16:37:30

标签: haskell countdown

第一:我的英语不是很好,道歉:(

所以,这是我必须解决的问题:

-- Based on a simple math game: given a list of numbers use the four basic 
-- operations (+, -, /, *)  between them to find (or be as close as possible to) 
-- another given number

有我的问题的例子,但我需要更具体的解决,我不能在“haskell模式”中思考,我是一个C ++播放器:(

所以,我完成了这件事:

-- Find all possible 2-combinations of the elements of xs.
   pairs :: [Int] -> [(Int, Int)]
   pairs xs = [(x, y) | (x:ys) <- tails xs, y <- ys]

 operations :: (Int, Int) -> [(Int, Int, Char, Int)]
 operations (x, y) =
         [ (x, y, '+', x + y) ] ++
         [ (x, y, '*', x * y) ] ++
         [ (x, y, '-', x - y) | x > y ] ++
         [ (x, y, '/', x `div` y) | x >= y, x `mod` y == 0]

我必须实现另一个函数('solve')来执行以下操作:

'solve'函数返回一个列表,其中包含所有结果节点,以从可用数字列表中选择一对数字,并应用可能对所选伙伴执行的操作。

我必须更新可用号码列表(取消使用的号码并添加新号码)和操作列表(以反映最新操作)

示例:

solve ( 100 , [1,4,5] , [] )

[ ( 100 , [5,5] , [(1,4,'+',5)] ), -- take first tuple 1,4 add and subs into "new tuple"5,5
( 100 , [3,5] , [(4,1,'-',3)] ),
( 100 , [6,4] , [(1,5,'+',6)] ),
( 100 , [4,4] , [(5,1,'-',4)] ),
( 100 , [9,1] , [(4,5,'+',9)] ),
( 100 , [1,1] , [(5,4,'-',1)] ),
( 100 , [20,1] , [(4,5,'*',20)] ) ]

首先使用几个数字(使用对功能),

“操作”的第二个显示[number,number,'operation',result]可以做。

我有这样的事情:

solve(n,ns) = [ e | ns' <- pairs ns
                  , e   <- operations ns'] 

但我不能让它发挥作用,任何想法?


编辑:

我真的很感谢你的回答,非常感谢你,但如果我不能做我要求的功能,我无法理解你的发展,因为我真的是Haskell真的很新:(

正如我所说,我需要一个带有数字列表的函数,以及我在主帖(操作和对)中编写的2个操作创建另一个函数来执行此操作:

实施例)

solve ( 100 , [1,4,5] , [] )

[ ( 100 , [5,5] , [(1,4,'+',5)] ),
( 100 , [3,5] , [(4,1,'-',3)] ),
( 100 , [6,4] , [(1,5,'+',6)] ),
( 100 , [4,4] , [(5,1,'-',4)] ),
( 100 , [9,1] , [(4,5,'+',9)] ),
( 100 , [1,1] , [(5,4,'-',1)] ),
( 100 , [20,1] , [(4,5,'*',20)] ) ]

非常感谢您快速回答,但我的答案需要更具体。

1 个答案:

答案 0 :(得分:2)

这是一个非常简洁的问题,但比起初看起来有点棘手。

我们希望,对于数学运算'+''-''*''/'

data Op = Plus | Minus | Mult | Div deriving (Eq, Ord)

ops = [Op]
ops = [Plus, Minus, Div, Mult]

instance Show Op where
  show Plus  = "+"
  show Minus = "-"
  show Div   = "/"
  show Mult  = "*"

和一组数字ns,搜索所有可能的算术表达式,这些表达式使用每个数字一次最接近某个目标的表达式。为了解决这个问题,我们将抽象地表达问题,然后以明确正确的方式解决它,然后应用一些优化。


让我们考虑一下哪种数据类型可以代表我们的表达方式。每个术语,称为Term,需要 Op和另外两个Terms的组合,一个“纯”值,一个本身就是整数。

data Term = Ap Op Term Term | Pure Double

我们将(2 + (3 * 4))表示为App Plus (Pure 2) (App Mult (Pure 3) (Pure 4))。给定这样的Term,我们可以递归地遍历它以将其打印出来或计算结果。

instance Show Term where
  show (Pure x)    = show x
  show (Ap op l r) = "(" ++ show l ++ " " ++ show op ++ " " ++ show r ++ ")"

我们会在适当的时候对此进行更详细的调查,但现在我们需要专注于生成。


如果我们的数字列表n <- ns中有任意数字,我们可以构建一个Term,它以三种不同的方式涉及它,Pure nTerm op n other或{{ 1}}其中Term op other n是我们op之一,而ops是由otherData.List.delete n ns没有ns)中的数字构成的术语。这是您可以从n构造的所有Terms的完全有效的递归定义。让我们建立它。

首先,我们需要一种选择或“聚焦”ns中每个元素的方法。我们将通过ns形成一个拉链作为ns,将列表转换为三元组列表,一个列表用于原始列表中的每个元素。中间元素是“聚焦”值,左侧列表是我们聚焦值左侧的元素,右侧列表是右侧的值。这可能看起来有点矫枉过正,但稍后它会派上用场。

pares :: [a] -> [([a], a, [a])]

现在我们可以致电pare :: [a] -> Int -> ([a], a, [a]) pare xs n = pare' n ([], xs) -- accumulate the left and right sides where -- we end up storing the left list in reverse, which is the style of zippers -- it seems a little weird, but it makes the code very simple and it shouldn't -- affect our problem pare' 0 (left, x:right) = (left, x, right) pare' n (left, x:right) = pare' (n-1) (x:left, right) -- 'pare' is a little dangerous since it can have out of bounds errors, -- but 'pares' can not. pares :: [a] -> [([a], a, [a])] pares xs = map (pare xs) [0..length xs - 1] 来获取

pares [1,2,3]

从这里开始,使用[ ( [] , 1 , [2,3] ) , ( [1] , 2 , [3] ) , ( [2,1] , 3 , [] ) ] 理解就可以直截了当地定义allTerms :: [Double] -> [Term]

List

好的,所以不那么直截了当。由于我们总是想要“至少”返回allTerms ns = [ result | (left, n, right) <- pares ns , result <- Pure n : (concat [ [ Ap op (Pure n) term, Ap op term (Pure n) ] | op <- ops , term <- allTerms (left ++ right) ]) ] (Pure n),我们必须将我们的列表理解分开来处理递归项。否则,我们总是得到Term,因为列表无法返回[]的任何递归子项。

这种表示法有点困难,所以我们将其更改为allTerms [] ic表示法,因为所有Monad理解都可以转换为List List的使用。< / p>

Monad

allTerms ns = do (left, n, right) <- pares ns let branches = do op <- ops term <- allTerms (left ++ right) [ Ap op (Pure n) term, Ap op term (Pure n) ] Pure n : branches 表示法可以让我们删除一些不必要的括号,为以后的优化提供更好的理由。现在,我们可以测试一下。

do

应该很容易检查这是......好,全面。它也证明了我们定义的一个弱点 - 我们忽略了许多问题的对称性。例如,没有必要为我们已访问过的数字生成子标记(这些术语将再次由后面的术语生成。此外,当*Main> mapM_ print $ allTerms [1,2] 1.0 (1.0 + 2.0) (2.0 + 1.0) (1.0 - 2.0) (2.0 - 1.0) (1.0 / 2.0) (2.0 / 1.0) (1.0 * 2.0) (2.0 * 1.0) 2.0 (2.0 + 1.0) (1.0 + 2.0) (2.0 - 1.0) (1.0 - 2.0) (2.0 / 1.0) (1.0 / 2.0) (2.0 * 1.0) (1.0 * 2.0) opPlus时,我们可以利用交换性。这是一个快速重写来解决这个问题。

Mult

如果allTerms ns = do (left, n, right) <- pares ns let branches = do op <- ops term <- allTerms right -- we no longer visit the left terms -- this is the value of using 'pares' makeApps op (Pure n) term Pure n : branches where -- makeApps only applies symmetry when the operator is Div or -- Minus. makeApps Plus t1 t2 = [Ap Plus t1 t2] makeApps Mult t1 t2 = [Ap Mult t1 t2] makeApps op l r = [Ap op l r, Ap op r l] ,那么我们的第一个版本将生成195 ns = [1,2,3]秒。第二个利用对称性仅生成57 Term s。这有点合理,所以让我们继续前进。


现在我们已经生成了所有可能的Term,我们需要对它们进行评估。与我们的Term实例一样,这是一个相对简单的递归定义。

Show

假设我们正在查找calcTerm :: Term -> Double calcTerm (Pure x) = x calcTerm (Ap Plus l r) = calcTerm l + calcTerm r calcTerm (Ap Minus l r) = calcTerm l - calcTerm r calcTerm (Ap Mult l r) = calcTerm l * calcTerm r calcTerm (Ap Div l r) = calcTerm l / calcTerm r ,其值最接近Term。我们可以使用“错误”goal :: Double对每个Term t进行注释,然后按此排序。我们需要使用abs (goal - calcTerm t)中的专用sortBy :: (a -> a -> Ordering) -> [a] -> [a]

Data.List

或者,使用一些专门的函数import Data.List (sortBy) bestTerm :: Double -> [Term] -> (Double, Term) bestTerm g = minimumBy (\(a, _) (b, _) -> a `compare` b) . map (\t -> (abs (g - calcTerm t), t)) Control.Arrow.&&&,我们可以写得最快

Data.Ord.comparing

我们可以开始回答这个问题了

bestTerm :: Double -> [Term] -> (Double, Term)
bestTerm g =
  minimumBy (comparing fst) . map (first error) where error t = abs (g - calcTerm t)

但有时候,因为等待*Main> bestTerm 31 $ allTerms [1..5] (0.0,(1.0 + ((3.0 * (4.0 * 5.0)) / 2.0))) 会让我失去耐心。看起来length $ allTerms [1..10]个数字上有(7^n-1)/6个术语(请参阅Sloane),因此我们将等待它计算47,079,208 n s。


希望这能说明您如何计算问题的答案,并为您提供优势地点以寻找优化结果的方法。

我认为你的例子的答案是Term