简体   繁体   中英

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. I need to continue this till I get 10 such numbers.

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 . Furthermore for any such y , we will add x one time. So given x is 6 , it will add it three four times, since 6 is dividable by 1, 2, 3 and 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. You could have taken the first element with head ;
  3. minimum will only produce one element, but you want the first 10.

Brute force approach

First we can use list comprehension to generate all these numbers (for arbitrary lists as and bs ):

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 . Next we have a filter all ((0 ==) . mod a) bs , which is a compact form of 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 ). If the filter is satisfied, then we add a (in the head of the list comprehension) to the result. Note that such lists are build lazily, so the fact that as has an infinite number of elements, is not a problem.

Now we can use take :: Int -> [a] -> [a] to take the first 10 of these numbers, and thus print these:

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 . It took my machine 2.16 seconds to calculate the 1000th element of this list, and 10.21 seconds to find the 5000th element.

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:

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.

In case 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 . So we can drop the as parameter, and use:

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. 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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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