【问题标题】:How to divide every element in a list with each element in another list in haskell如何将列表中的每个元素与haskell中另一个列表中的每个元素分开
【发布时间】:2017-08-04 10:43:01
【问题描述】:

假设我有一个无限列表A = [1..] 我想将A 中的每个元素与列表B = [1..10] 中的所有元素相除如果列表A 中的任何元素可以被B 中的所有元素整除我需要打印它。 我需要继续这个,直到我得到 10 个这样的数字。

以下尝试无效:

print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))

【问题讨论】:

  • @Willem Van Onsem 我写了一个绝对不会给出正确答案的代码!打印(最小值([x | x

标签: list haskell list-comprehension


【解决方案1】:

你的尝试

你写道:

print(minimum([x | x <- [1..], y <- [1..10], rem x y == 0]))

由于以下几个原因,这将不起作用:

  1. 它会将x 添加到列表理解中的列表中,因为[1..10] 中有any 元素yx 分开。此外,对于任何这样的y,我们将添加一次x。所以给定x6,它会加三四次,因为6 可以被1、2、3 和6 整除;
  2. 结果将是一个无限列表,因此无法计算该列表的最小值。这是因为 Haskell 不知道列表已经排序,因此最小值只是第一个元素。您可以使用head 获取第一个元素;
  3. minimum 只会产生一个元素,但你想要前 10 个。

蛮力方法

首先我们可以使用列表推导来生成所有这些数字(对于任意列表asbs):

divide_all as bs = [a | a <- as, all ((0 ==) . mod a) bs]

所以这里的列表推导遍历as 并将每个元素分配给a。接下来我们有一个过滤器all ((0 ==) . mod a) bs,它是all (\b -&gt; mod a b == 0) bs 的紧凑形式。因此,它检查bsmod a b == 0 中的所有成员b 是否(所以a 可以被b 整除)。如果满足过滤器,那么我们将a(在列表理解的头部)添加到结果中。请注意,此类列表是延迟构建的,因此 as 具有无限数量的元素这一事实不是问题。

现在我们可以使用take :: Int -&gt; [a] -&gt; [a] 获取这些数字中的前 10 个,从而打印这些数字:

mapM_ print (take 10 $ divide_all [1..] [1..10])

哪个打印:

Prelude> mapM_ print (take 10 $ divide_all [1..] [1..10])
2520
5040
7560
10080
12600
15120
17640
20160
22680
25200

最小公倍数

上述方法效率不高:对于a的每个元素,我们需要检查它是否可以与b的每个元素整除。我的机器用了 2.16 秒来计算这个列表的第 1000 个元素,并用了 10.21 秒找到第 5000 个元素。

我们可以加快最后一个,通过计算b中所有元素的最小公倍数(lcm),并检查一个数是否可​​以被lcm整除:

divide_all as bs = [a | a <- as, mod a lcmb == 0] -- optimized version
    where lcmb = foldr1 lcm bs

所以现在我们只需要执行一项检查。现在计算第 1000 个元素需要 0.95 秒,计算第 5000 个元素需要 4.54 秒。

万一as = [1..]

如果已知as[1..],我们可以显着提升这段代码,因为我们知道a 的元素都是lcmb 的倍数。所以我们可以去掉as参数,然后使用:

divide_all bs = [lcmb*a | a <- [1..]] -- optimized version
    where lcmb = foldr1 lcm bs

现在计算第 1000 个元素需要 0.01 秒,计算第 5000 个元素需要 0.03 秒。但当然,这仅在假设条件下才有效。

【讨论】:

  • @Willem Van Onsem 谢谢你的详细解释:)
【解决方案2】:
let divcheck = (take 10 .) . filter . flip (all . ((0 ==) .) . mod)

divcheck [1..10] [1..]
-- [2520,5040,7560,10080,12600,15120,17640,20160,22680,25200]

divcheck [1,2,3] [1..]
-- [6,12,18,24,30,36,42,48,54,60]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-11
    相关资源
    最近更新 更多