繁体   English   中英

如何在haskell中将列表中的每个元素与另一个列表中的每个元素相除

[英]How to divide every element in a list with each element in another list in haskell

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

以下尝试不起作用:

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

你的尝试

你写了:

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

现在,由于以下几个原因,这将无法正常工作:

  1. 这将增加x给出有任何元素到列表中列表解析, y[1..10]其将x 此外,对于任何这样的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 -> mod ab == 0) bs的紧凑形式。 因此,它检查bs所有成员b是否为mod ab == 0 (因此a可被b整除)。 如果满足过滤条件,则在结果中添加a (在列表理解的开头)。 请注意,这样的列表是延迟构建的,因此as具有无限数量的元素这一事实不是问题。

现在我们可以使用take :: Int -> [a] -> [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秒。 但是当然,这仅在给定假设的情况下有效。

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]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM