生成所有可能的互质的排序列表

时间:2015-11-14 16:57:19

标签: sorting haskell

我需要生成所有互班体的无限排序列表。 每对中的第一个元素必须小于第二个元素。 排序必须按升序进行 - 通过对的元素之和;如果两个总和相等,则由该对的第一个元素组成。

因此,结果列表必须是

[(2,3),(2,5),(3,4),(3,5),(2,7),(4,5),(3,7),(2,9),(3,8),(4,7)...`

这是我的解决方案。

coprimes :: [(Int, Int)]
coprimes = sortBy (\t1 t2 -> if uncurry (+) t1 <= uncurry (+) t2 then LT else GT) $ helper [2..]
    where helper xs = [(x,y) | x <- xs, y <- xs, x < y, gcd x y == 1]

问题是我不能先n次{my}次。我意识到排序不能在无限列表上完成。

但是如何以懒惰的方式生成相同的序列呢?

4 个答案:

答案 0 :(得分:1)

如果您首先生成所有可能的对然后过滤它们,那么它可能不是最佳方式。

所以使用你的标准:

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]

生成

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

现在当然你可以将1 < ii < j中的一些东西放到pairs定义中,或者甚至加入它们但我认为这里更明显发生了什么

答案 1 :(得分:1)

正如@Bakuriu建议的那样,合并一个无限列表的无限列表是一个解决方案,但魔鬼在细节中。

diagonal包中的universe-base函数可以执行此操作,因此您可以写:

import Data.Universe.Helpers

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

注意 - 这不符合您的排序标准,但我提到它是因为该包中的函数对于了解并且正确实现diagonal这样的函数并不容易。

如果你想自己编写,可以考虑将无限网格N x N(其中N是自然数)分解为对角线:

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

并过滤此列表。

答案 2 :(得分:1)

这是一个可能的解决方案,遵循Richard Bird的在Haskell中功能性思考的第9章:

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

结果是:

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

请注意mergeAll的自然定义是foldr1 merge,但这不起作用,因为它会在返回第一个元素之前尝试找到所有列表中第一个元素的最小值元素,因此你最终陷入无限循环。但是,因为我们知道列表是按升序排列的,并且最小值是第一个列表的第一个元素xmerge才有效。

注意:此解决方案似乎显着(比如2个数量级)慢于Carsten&#34;天真&#34;回答。因此,如果您对性能感兴趣,我建议您避免这种情况。然而,它仍然是一种有趣的方法,可能在其他情况下有效。

答案 3 :(得分:1)

  

我需要生成所有互班体的无限排序列表。每对中的第一个元素必须小于第二个元素。排序必须按升序进行 - 通过对的元素之和;如果两个总和相等,则由该对的第一个元素组成。

因此,我们生成上升和和第一元素的对,并且只保留互质。容易俗气!

[ (first, second)
| sum <- [3..]
, first <- [2..sum `div` 2]
, let second = sum-first
, gcd first second == 1
]