【问题标题】:Find all pairs with the given sum, in an unsorted array of numbers [closed]在未排序的数字数组中查找具有给定总和的所有对[关闭]
【发布时间】:2020-10-31 16:52:21
【问题描述】:

例如,函数将有两个输入:

  • 任意长度的数组,例如[1, 8, 5, 2, 5, 6, 7, 3, 9, 4].

  • 目标总和,例如10.

输出将是一个包含所有对目标求和的数字对的数组,例如[(1,9),(8,2),(5,5),(7,3),(6,4)].

我完全不知道如何创建此代码。如果有人可以提供一些见解,以下是我到目前为止的内容,但它不起作用:

sums :: (Num a, Num b) => [a] -> b -> [(a,a)]
sums ([x] i) = (x i)
sums (x:xs) i
    | x == [] = []
    | x + sums(xs) == i = [(x,xs)]
    | otherwise = []

【问题讨论】:

  • 欢迎来到 StackOverflow。我们在这里不回答没有表现出自己努力解决问题的问题。当你“卡住”时,你显然已经尝试了一些自己的想法,所以一定要展示它们。
  • 顺便说一句,Haskell 中的[1,8,5] 不是数组而是列表
  • 不幸的是,您的代码看起来像是在墙上扔了一堆语法和函数,希望有什么东西可以坚持。它与有效的 Haskell 完全不同,我无法理解它应该是什么意思。您能否先用清晰准确的语言解释您希望如何解决问题?这似乎是一个乏味的步骤,但它确实是让您的想法井然有序的好方法。
  • 我将从一个小提示开始:您的数字类型肯定应该相同。你的类型签名应该看起来像sums :: (Num a, Eq a) => [a] -> a -> [(a,a)]。更高效的函数版本需要更受约束的类型:sums :: (Num a, Ord a) => [a] -> a -> [(a,a)] 甚至sums :: Integral a => [a] -> a -> [(a,a)],但您应该先尝试更简单的Eq 版本。
  • 列表元素是否允许使用两次?也就是说,如果 5 在输入中只出现一次,你还能产生(5,5)吗?

标签: list haskell recursion sum combinations


【解决方案1】:

您的尝试在句法和语义上都存在一些问题。 这是一种可能的解决方案。它不是递归的,但我希望它能帮助你理解问题(请参阅最后关于递归的建议):

sums :: (Num a, Eq a) => [a] -> a -> [(a,a)]
sums l s = filter (\(x,y) -> x+y == s) [ (x,y) | x <- l, y <- l ]
  • [ (x,y) | x &lt;- l, y &lt;- l ] 定义了一个包含所有可能对的列表
  • \(x,y) -&gt; x+y == s 是一个函数,给定一对告诉它是否满足你的条件
  • filter 使用该函数过滤所有可能对的列表

请花点时间了解一些主题:

  • 如何编写函数签名
  • 列表和最常用的函数(例如filter
  • 理解语法 ([ (x,y) | x &lt;- l, y &lt;- l ])
  • Lambda 表达式 (\(x,y) -&gt; x+y == s)
  • 延迟评估。您尝试在一个函数中完成所有操作:遍历列表、配对、检查它们是否满足条件。 上面的解决方案首先定义了所有可能的组合,然后过滤,只选择那些总和为s 的组合。如果您来自命令式编程,从概念上讲更简单,也很有效,直观地思考它可能看起来很慢。

正如 cmets 中所指出的,表达这一点的最紧凑的方式是使用理解“即时”将条件应用于生成的组合的能力:

sums l s = [ (x,y) | x <- l, y <- l, x+y == s ]

但是,尝试使用递归解决问题会更有指导意义。

建议:保持结构filter (\(x,y) -&gt; x+y == s) (all_pairs l),但尝试自己想出一个递归函数all_pairs,它从列表中生成所有可能的对,而不是像我那样使用理解语法。 您需要编写的函数比您之前尝试过的函数要简单得多。只需从列表中创建所有对,没有条件,没有检查。

【讨论】:

  • 你也可以在列表理解中进行过滤,所以[(x, y) | x &lt;- l, y &lt;- l, x+y == s]
  • @WillemVanOnsem 绝对。我的目标不是最好/最短的解决方案,而是一个可以帮助 OP 将原始问题视为子问题组合的解决方案。
  • @luqui 从答案的最后一段看来,回答者的意图是为了简化手动递归重写。不会让提问者分心。
【解决方案2】:

当您得到一个未排序的列表时,有时最好的第一步就是对其进行排序。那么让我们开始吧:

import Data.List (sort)
sums :: (Num a, Ord a) => [a] -> a -> [(a,a)]
sums xs s = ...
  where
    sorted = sort xs

酷。下一步是什么?让我们同时从双方处理排序列表。列表对此不好,所以让我们在这里使用序列。

import Data.List (sort)
import Data.Sequence (Seq (..), fromList)

sums :: (Num a, Ord a) => [a] -> a -> [(a,a)]
sums xs s = go sorted
  where
    sorted = fromList (sort xs)
    go Empty = []
    go (_ :<| Empty) = []
    go (a :<| asb@(as :|> b)) = case compare (a + b) s of
      EQ -> (a,b) : go as
      LT -> go asb
      GT -> go (a :<| as)

这将以与您的示例不同的顺序给出结果,但它的工作时间为 O(n log n) 而不是 O(n^2) 时间——对于长列表来说要快得多。

不幸的是,这里仍然存在效率低下的问题:我们可能会从序列中取出一个元素,然后多次将其放回。这没什么大不了的,但很容易解决。

import Data.List (sort)
import Data.Sequence (Seq (..), fromList)

sums :: (Num a, Ord a) => [a] -> a -> [(a,a)]
sums xs s = start sorted
  where
    sorted = fromList (sort xs)
    start Empty = []
    start (_ :<| Empty) = []
    start (a :<| (as :> b)) = go a as b

    go a as b = case compare (a + b) s of
      EQ -> (a,b) : start as
      LT
        | a' :<| as' <- as
        -> go a' as' b
        | otherwise -> []
      GT
        | as' :|> b' <- as
        -> go a as' b'
        | otherwise -> []

正如现在已删除的评论中所指出的,Data.Sequence 在这里真的是矫枉过正。各种各样的类似双端队列、类似多集和双端优先级队列结构都可以工作,其中许多比Data.Sequence 更简单和/或更快。

【讨论】:

  • @leftaroundabout,这当然是一种选择,但您必须仔细跟踪摘要长度。这样也有一点空间泄漏。在整个结果被消耗之前,您不会释放任何元素。当然Seq 是矫枉过正。您只需要fromListpopLpopR,而这些只需要快速用于临时使用。但是我不想为这个问题想出一个专用的数据结构。
  • 列表非常适合headtail;列表也非常适合反向构建。考虑输入targetSum 10 $ [1,2,9,8,6]++[100..10000]
  • 也许我会回答,但我不能,因为它现在因“缺乏细节”而关闭。 (我看到了我需要的所有细节。) ----- 上面我的 cmt 中的情况,我想知道快速排序是否会比那里的合并排序更好......
  • @WillNess,我不太明白你在说什么。您的具体示例适用于 GHC 的自适应归并排序。
  • 这是两件事。首先,我们不需要序列。在对我的示例数据进行排序后,我们甚至不需要反转整个列表。访问其take 6 前缀就足够了。我们会在构建反向前缀时沿着列表前进,在第 6 个元素处,我们会发现它就足够了,目标总和为 10。第二件事不是我的具体示例,它主要是排序的(是的,这个会在 O(n)) 中被发现,但有一个类似的,比如一开始有 10 个元素小于 10,然后有 10000 个元素都大于 10。我们首先在 10 处进行分区,等等。
猜你喜欢
  • 2016-04-03
  • 2022-06-14
  • 2017-02-08
  • 2017-01-12
  • 2015-09-18
  • 2021-09-28
  • 1970-01-01
  • 2020-02-22
  • 2014-11-15
相关资源
最近更新 更多