简体   繁体   中英

Combining two list comprehensions Haskell

I'm trying to calculate the first triangle number with 501 divisorsin Haskell. I already made two list comprehensions, one listing all the triangle numbers and one listing all divisors of a given number. Now I want to make one big list with all values divisors of each triangle number. (eg [[1],[1,3],[1,2,3,6],[1,2,5,10] etc..)

How can I use my triangleNumbers list in my divisors list? My code is below.

triangleNumbers = [t | a <- [0..], let t = a*(a+1)/2] 
divisors triangleNumbers = [d | d <- [1..triangleNumbers], triangleNumbers 
`mod` d == 0]
numDivisors = takeWhile(<501) length . divisors
answer = divisors !! numDivisors

First of all, your triangleNumbers is a bit weird: it contains Fractional s, instead of Integrals s. This makes it more troublesome to perform accurate division calculations. So we better modify this:

triangleNumbers = [ div (a*(a+1)) 2 | a <- [0..]]

Note that we can write the expression in the head of the list comprehension. We also use div over / since div is an integer division. We know for sure that we will not lose data by performing an integer division, since either a or a+1 is even, and the multiplication of a number with an even number is always even. This results in the following list:

Prelude> take 10 triangleNumbers 
[0,1,3,6,10,15,21,28,36,45]

Now we want a function that maps numbers on the divisors. We can make a generic function:

divisors x = [d | d <- [1..x], mod x d == 0]

Now we can use map , to map a list of numbers to a list of lists where each list contains the divisors of the original number. So:

Prelude> map divisors [1,2,3,5,8,13,21]
[[1],[1,2],[1,3],[1,5],[1,2,4,8],[1,13],[1,3,7,21]]

We can however also give the map divisors the (infinite list) of triangleNumbers . For instance for the first 10 triangleNumbers :

Prelude> take 10 $ map divisors $ triangleNumbers 
[[],[1],[1,3],[1,2,3,6],[1,2,5,10],[1,3,5,15],[1,3,7,21],[1,2,4,7,14,28],[1,2,3,4,6,9,12,18,36],[1,3,5,9,15,45]]

Now we only need to filter the numbers that have 501 elements or more. We can do this by checking that if we drop 500 elements, we still have a list that contains at least one element. So with:

hasAtLeastLength :: Int -> [a] -> Bool
hasAtLeastLength n = not . null . drop (n-1)

So now we can filter all the elements where the hasAtLeastLengh 501 (divisors x) for a number x . This will thus produce the list of all these numbers:

filter (hasAtLeastLength 501 . divisors) triangleNumbers

This will produce an infinite list of all triangleNumbers that have at least 501 divisors. We can use head to finally obtain the first element:

head $ filter (hasAtLeastLengh 501 . divisors) triangleNumbers

This takes a large amount of time. The code works however quite fast if we work with 10 divisors:

Prelude> filter (hasAtLeastLength 10 . divisors) triangleNumbers
[120,210,276,300,378,496,528,630,666,780,820,990,1035,1128,1176,1275,1326,1485,1540,1596,1770,1830,1953,2016,2080,2145,2346,2415,2556,2628,2775,2850,2926,3003,3160,3240,3321,3486,3570,3828,3916,4005,4095,4186,4278,4560,4656,4851,4950,5050,5356,5460,5565,5778,5886,6105,6216,6328,6555,6670,6786,6903,7140,7260,7626,7750,7875,8001,8128,8256,8385,8646,8778,9045,9180,9316,9730,9870,10296,10440,10731,10878,11175,11325,11476,11628,11781,11935,12090,12246,12720,12880,13041,13203,13530,13695,14028,14196,14365,14535,14706,15225,15400,15576,16110,16290,16653,16836,17020,17205,17391,17578,17766,17955,18336,18528,18915,19110,19306,19503,19701,19900,20100,20706,20910,21321,21528,21736,21945,22155,22578,23220,23436,24090,24310,24531,24976,25200,25425,25878,26106,26565,26796,27028,27261,27495,27730,27966,28203,28680,28920,29403,29646,29890,30135,30381,30628,30876,31125,31626,31878,32385,32640,32896,33411,33670,33930,34191,34716,34980,35245,35511,35778,36315,36585,36856,37128,37401,37675,37950,38226,38781,39060,39340,40470,40755,41041,41328,41616,41905,42195,42486,43071,43365,43660,43956,44253,44850,45150,46056,46360,46665,46971,47278,47586,47895,48516,48828,49455,49770,50721,51040,51360,51681,52003,52326,52650,...

which means that it produces an answer. This means however that you will have to come up with something smarter than simply enumerating.

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