我需要实现一个算法来解决Haskell中的以下问题:
给定一个数组,检查sum(x_0,x_1 ... x_k)== sum(x_k + 1,...,x_n)
1:
arr = [1]
sum([]) = 0
sum([1]) = 1
sum([]) != sum([1])
there is no such k
return False
2:
arr = [1,2,3]
sum([1,2]) == sum([3])
there is such k
return True
到目前为止,这是我当前的实现:
checkSum::[Int]->Int->Bool
checkSum [] _ = True
checkSum [x] _ = x == 0
checkSum l inx | sum(take inx l) == sum(drop inx l) = True
| inx <= length l = checkSum l (inx+1)
| otherwise = False
它有效,但我需要更改函数原型如下:
checkSum::[Int]->Bool
.....
有没有人知道如何在不将索引传递给函数的情况下实现新函数?
答案 0 :(得分:3)
此功能出现了惊人的数量:
import Data.List (inits, tails)
splits :: [a] -> [([a],[a])]
splits xs = zip (inits xs) (tails xs)
使用示例:
ghci> splits [1,2,3]
[([],[1,2,3]),([1],[2,3]),([1,2],[3]),([1,2,3],[])]
您可能也对any
功能感兴趣。
我想你可以从这里开始。
答案 1 :(得分:3)
更原始的方法
checkSum :: [Int] -> Bool
checkSum a = go a 0 (sum a)
where go [] left right = left==right
go (x:xs) left right = left==right || go xs (left+x) (right-x)
在每一步中,您将元素添加到左侧值并从右侧减去,其中初始值为0和sum。在任何步骤或阵列耗尽时达到相等时终止。
答案 2 :(得分:3)
一种聪明的思考方式:如果我们需要
x[1] + ... + x[k] == x[k+1] + ... + x[n],
然后我们可以将左侧添加到两侧以获得
(x[1] + ... + x[k]) * 2 == sum(x).
因此,我们可以生成所有加倍的前缀和,然后在其中查找sum(x)
。
checkSum :: [Int] -> Bool
checkSum xs = sum xs `elem` doublePrefixSums
where doublePrefixSums = scanl (\acc x -> acc + 2*x) 0 xs
答案 3 :(得分:2)
使用内置函数的解决方案。
使用scanl
和scanr
查找左右所有的总和。
scanl (+) 0 [1,2,3]
给你[0,0 + 1,0 + 1 + 2,0 + 1 + 2 + 3]
λ> scanl (+) 0 [1,2,3]
[0,1,3,6]
scanr (+) 0 [1,2,3]
给你[1 + 2 + 3 + 0,2 + 3 + 0,3 + 0,0]
λ> scanr (+) 0 [1,2,3]
[6,5,3,0]
然后zipWith
确定列表具有相同元素的位置。
λ> zipWith (==) [0,1,3,6] [6,5,3,0]
[False,False,True,False]
然后or
检查此列表是否包含True。
λ> or [False,False,True,False]
True
完整的功能:
checkSum :: [Int] -> Bool
checkSum xs = or (zipWith (==) leftSums rightSums)
where
leftSums = scanl (+) 0 xs
rightSums = scanr (+) 0 xs
(这将在空列表中返回True。这似乎是合理的
我,但如果你想要它是假的,你可以写一个特例
它或只是将scanr
更改为scanr1
。)
答案 4 :(得分:0)
我对这个问题的解释是:对于某些Int列表 x ,确定∃k::Integer. sum[x₀,…,xₖ] == sum[xₖ,…,xₙ]
。合理地,0 ≤ k < length x
(和n = length x
)因为否则xₖ是⊥,我们对这种情况不感兴趣。
那么你如何证明这个命题呢?我们对无限列表感兴趣吗?可能不是,所以让我们假设列表是有限的。然后,如果列表是有限的,那么 k 也有有限的选择,因为0 ≤ k < length x
。然后可以通过检查 k 的每个选项来完成证明。如果 k 中至少有一个选择满足命题则为真,否则为假。
在将我们的方法转换为Haskell时,我们希望充分利用现有定义。我们可以专注于列表本身的拆分,将 k 留在实现之外。 luqui's answer精美地证明了这一点。请注意,抽象符号(数学,逻辑,规范等)并不总是表明良好的实现。
一旦我们知道列表的所有拆分,我们就可以求和并进行相等比较。如果至少一次比较成功则产生True,否则产生False。