【问题标题】:Haskell: Find two related elements in a listHaskell:在列表中查找两个相关元素
【发布时间】:2021-01-05 18:57:20
【问题描述】:

我是 Haskell/FP 的新手。我想解决这个任务:

给出这个列表:[1721, 979, 366, 299, 675, 1456]

找出总和为2020 的两个元素并将它们相乘。解是 1721 * 299。

我认为在 Haskell 中,我可以用来解决这个问题的工具是列表理解和折叠(或它们的组合)。但我不明白如何编写一个列表理解,它考虑到同一列表的其他元素,而不仅仅是当时的一个元素。

这是我几个小时后想出的(ints 是列表):

print [(x,y, x*y) | x <- ints, y <- ints, x+y == 2020]

它实际上打印了正确的答案。但我认为我的解决方案很脏:

  • 我将输入列表两次输入到列表理解中。这个对吗?对我来说,这似乎是开销/重复。我相信有更好的方法。
  • 函数的返回是一个包含两个相同实体的列表(我假设这是因为我在上一个项目符号中描述的内容):[(1721,608,514579),(1721,608,514579)] - 当然我可以使用head 获得单个元素,但是没有解决问题的根源。

【问题讨论】:

  • 您将枚举每两个组合,因此这将使用 二次时间。通过先对列表进行排序,可以得到线性时间。
  • 你会得到每个答案两次,因为xy可以互换,x + y == y+x

标签: list haskell


【解决方案1】:

这将发出两次相同值的原因是因为您在列表上有两个迭代器。因此,这意味着在某些时候x 将取值为 1721,y 将取值为 299;但稍后在程序中会出现相反的情况:x 将占用 299; y 需要 1721。

我们可以使用tails :: [a] -&gt; [[a]] 轻松解决这个问题:

import Data.List(tails)

[(x,y, x*y) | (x:ys) <- tails ints, y <- ys, x+y == 2020]

这里对于ints的每个后缀,我们将x作为第一个元素,ys作为其余元素,然后我们枚举ys

但这仍然需要二次时间。这可以通过对列表进行排序在 O(n log n) 上完成,然后在我们枚举列表两端的地方使用递归,直到找到等于 2020 的值。另一种选择是使使用像HashSet 这样的集合。然后我们首先将元素存储在HashSet 中,然后对于列表中的每个元素x,我们检查2020 - x 是否在HashSet 中。我把这些留作执行。

【讨论】:

  • y &lt;- ys 中的 &lt; 需要转义。
  • 非常感谢 Willem,也感谢您的奖金练习。我理解为什么它会两次发出结果。我理解这个想法,但语法对我来说仍然很混乱。我对(x:ys) 感到困惑。但我认为这仅仅是因为我对 Haskell 缺乏了解。我希望几天后我能更容易地理解这些结构。非常感谢您的帮助。
  • @AndreasFröwis:现在已经解决了。
猜你喜欢
  • 1970-01-01
  • 2015-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-02
  • 2020-01-21
相关资源
最近更新 更多