检查数组在Haskell中sum(x_0,x_1 ... x_k)== sum(x_k + 1,.. x_n)是否相等

时间:2015-12-30 00:13:26

标签: haskell

我需要实现一个算法来解决Haskell中的以下问题:

给定一个数组,检查sum(x_0,x_1 ... x_k)== sum(x_k + 1,...,x_n)

如果数组没有元素,则总和为零 enter image description here

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
.....

有没有人知道如何在不将索引传递给函数的情况下实现新函数?

5 个答案:

答案 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)

使用内置函数的解决方案。

使用scanlscanr查找左右所有的总和。

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。