Generating sorted list of all possible coprimes

前端 未结 4 1020
走了就别回头了
走了就别回头了 2021-01-22 07:07

I need to generate infinite sorted list of all coprimes. The first element in each pair must be less than the second. The sorting must be done in ascending order -- by the sum o

相关标签:
4条回答
  • 2021-01-22 07:30

    As @Bakuriu suggests, merging an infinite list of infinite lists is a solution, but the devil is in the details.

    The diagonal function from the universe-base package can do this, so you could write:

    import Data.Universe.Helpers
    
    coprimes = diagonal [ go n | n <- [2..] ]
      where go n = [ (n,k) | k <- [n+1..], gcd n k == 1 ]
    

    Note - this doesn't satisfy your sorted criteria, but I mention it because the functions in that package are useful to know about, and implementing a function like diagonal correctly is not easy.

    If you want to write your own, consider decomposing the infinite grid N x N (where N is the natural numbers) into diagonals:

    [ (1,1) ] ++ [ (1,2), (2,1) ] ++ [ (1,3), (2,2), (3,1) ] ++ ...
    

    and filtering this list.

    0 讨论(0)
  • 2021-01-22 07:44

    While probably not the most optimal way it should works if you first generate all possible pairs and then filter them.

    So using your criteria:

    pairs :: [(Integer,Integer)]
    pairs = [ (i,l-i) | l <- [1..], i <- [1..l-1] ]
    
    coprimes :: [(Integer,Integer)]
    coprimes = [ (i,j) | (i,j) <- pairs, 1 < i, i < j,gcd i j == 1]
    

    produces

    λ> take 10 coprimes
    [(2,3),(2,5),(3,4),(3,5),(2,7),(4,5),(3,7),(2,9),(3,8),(4,7)]
    

    now of course you can put some of the stuff 1 < i and i < j comes to mind into the pairs definition or even join them but I think here it's more obvious what's going on

    0 讨论(0)
  • 2021-01-22 07:46

    Here's a possible solution following Chapter 9 of Richard Bird's Thinking Functionally in Haskell:

    coprimes = mergeAll $ map coprimes' [2..]
    
    coprimes' n = [(n, m) | m <- [n+1..], gcd m n == 1]
    
    merge (x:xs) (y:ys)
        | s x < s y =  x:merge xs (y:ys)
        | s x == s y = x:y:merge xs ys
        | otherwise = y:merge (x:xs) ys
        where s (x, y) = x+y
    
    xmerge (x:xs) ys = x:merge xs ys
    
    mergeAll = foldr1 xmerge
    

    And the result is:

    > take 10 $ coprimes
    [(2,3),(2,5),(3,4),(3,5),(2,7),(4,5),(3,7),(2,9),(3,8),(4,7)]
    

    Note that the natural definition of mergeAll would be foldr1 merge, but this doesn't work because it will try to find the minimum of the first elements of all the list before returning the first element, and hence you end up in an infinite loop. However, since we know that the lists are in ascending order and the minimum is the first element of the first list xmerge does the trick.


    Note: this solution appears to be significantly (like 2 order of magnitudes) slower than Carsten "naive" answer. So I advise to avoid this if you are interested in performance. Yet it still is an interesting approach that might be effective in other situations.

    0 讨论(0)
  • 2021-01-22 07:47

    I need to generate infinite sorted list of all coprimes. The first element in each pair must be less than the second. The sorting must be done in ascending order -- by the sum of pair's elements; and if two sums are equal, then by the pair's first element.

    So, we generate ascending pairs of sum and first element, and keep only the coprimes. Easy cheesy!

    [ (first, second)
    | sum <- [3..]
    , first <- [2..sum `div` 2]
    , let second = sum-first
    , gcd first second == 1
    ]
    
    0 讨论(0)
提交回复
热议问题