简体   繁体   English

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

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

Suppose I have an infinite list A = [1..] I want to divide each element in A with all elements in list B = [1..10] If any element in list A is divisible by all the elements in B I need to print it. 假设我有一个无限列表A = [1..]我想将A每个元素除以列表B = [1..10]所有元素B = [1..10]如果列表A任何元素都可以被B所有元素整除,我需要打印。 I need to continue this till I get 10 such numbers. 我需要继续进行直到得到10个这样的数字。

The following attempt did not work: 以下尝试不起作用:

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

Your attempt 你的尝试

You wrote: 你写了:

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

Now this will not work for several reasons: 现在,由于以下几个原因,这将无法正常工作:

  1. It will add x to the list in the list comprehension, given there is any element y in [1..10] that divides x . 这将增加x给出有任何元素到列表中列表解析, y[1..10]其将x Furthermore for any such y , we will add x one time. 此外,对于任何这样的y ,我们将x加一次。 So given x is 6 , it will add it three four times, since 6 is dividable by 1, 2, 3 and 6; 所以给定x6 ,它将乘以三四次,因为6可以被1、2、3和6分开;
  2. The result will be an infinite list, and it is thus impossible to calculate the minimum of that list. 结果将是一个无限的列表,因此不可能计算该列表的最小值。 This is because Haskell does not know that the list is already ordered, and thus the minimum is simply the first element. 这是因为Haskell不知道列表已经排序,因此最小值只是第一个元素。 You could have taken the first element with head ; 你可以用head理解第一个要素;
  3. minimum will only produce one element, but you want the first 10. minimum只会产生一个元素,但是您想要第一个10。

Brute force approach 蛮力法

First we can use list comprehension to generate all these numbers (for arbitrary lists as and bs ): 首先,我们可以使用列表推导生成所有这些数字(对于asbs任意列表):

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

So here the list comprehension iterates over as and assigns every element to a . 所以这里的列表理解遍历as环节都分配给a Next we have a filter all ((0 ==) . mod a) bs , which is a compact form of all (\\b -> mod ab == 0) bs . 接下来我们有一个过滤器all ((0 ==) . mod a) bs ,这是all (\\b -> mod ab == 0) bs的紧凑形式。 So it checks whether for all members b in bs , mod ab == 0 (so a is dividable by b ). 因此,它检查bs所有成员b是否为mod ab == 0 (因此a可被b整除)。 If the filter is satisfied, then we add a (in the head of the list comprehension) to the result. 如果满足过滤条件,则在结果中添加a (在列表理解的开头)。 Note that such lists are build lazily, so the fact that as has an infinite number of elements, is not a problem. 请注意,这样的列表是延迟构建的,因此as具有无限数量的元素这一事实不是问题。

Now we can use take :: Int -> [a] -> [a] to take the first 10 of these numbers, and thus print these: 现在我们可以使用take :: Int -> [a] -> [a]来获取这些数字的前10个,并打印出:

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

which prints: 打印:

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

Least common multiple 最小公倍数

The above approach is not very efficient: for every element of a , we need to check whether it is dividable with every element of b . 上述方法是不是很有效:对的每一个元素a ,我们需要检查它是否是可分的与每一个元素b It took my machine 2.16 seconds to calculate the 1000th element of this list, and 10.21 seconds to find the 5000th element. 我的机器花了2.16秒来计算此列表的第1000个元素,花了10.21秒才找到了第5000个元素。

We can speed up the last one, by calculating the least common multiple (lcm) of all the elements in b , and check if a number is dividable by the lcm: 我们可以通过计算b中所有元素的最小公倍数(lcm)来加快最后一个的速度,并检查数字是否可被lcm整除:

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

So now we have to perform only one check. 所以现在我们只需要执行一项检查。 Calculating the 1000th element now takes 0.95 seconds, and calculating the 5000th element, takes 4.54 seconds. 现在计算第1000个元素需要0.95秒,而计算第5000个元素需要4.54秒。

In case as = [1..] 如果as = [1..]

In case as is known to be [1..] we can boost this code dramatically, since we know that the elements of a are all multiples of lcmb . 如果as是已知的[1..]我们可以显着提升该代码,因为我们知道的元素a是所有的倍数lcmb So we can drop the as parameter, and use: 因此,我们可以删除as参数,并使用:

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

Now calculating the 1000th element takes 0.01 seconds, and calculating the 5000th element 0.03 seconds. 现在计算第1000个元素需要0.01秒,计算第5000个元素需要0.03秒。 But of course this only works given the assumption. 但是当然,这仅在给定假设的情况下有效。

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