【问题标题】:Generating sorted list of all possible coprimes生成所有可能的互质数的排序列表
【发布时间】:2016-02-16 02:34:29
【问题描述】:

我需要生成所有互质数的无限排序列表。 每对中的第一个元素必须小于第二个。 排序必须按升序进行——按对元素的总和;如果两个和相等,则乘以该对的第一个元素。

所以,结果列表一定是

[(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 第一对。我意识到无法对无限列表进行排序。

但是我怎样才能以惰性的方式生成相同的序列呢?

【问题讨论】:

  • 诀窍可能是按升序为23 生成对,然后合并它们。

标签: list sorting haskell primes infinite


【解决方案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 &lt; ii &lt; j 放入 pairs 定义中,甚至加入它们,但我认为这里发生的事情更明显

【讨论】:

    【解决方案2】:

    在 Richard Bird 的 Thinking Functionally in 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 “天真的”答案慢。因此,如果您对性能感兴趣,我建议避免这种情况。然而,它仍然是一种有趣的方法,可能在其他情况下也有效。

    【讨论】:

    • 代替foldr1 xmerge,您可以使用data-ordlist 中的Data.List.Ordered.mergeAllBy (comparing (uncurry (+)))。它使用树状结构进行折叠,因此应该带来显着的算法(我的意思是,复杂性)优势(并因此加速)。比较返回 EQ 相等的总和,但该包中的所有函数都是左偏的,所以没关系。
    【解决方案3】:

    正如@Bakuriu 建议的那样,合并无限列表的无限列表是一种解决方案,但问题在于细节。

    universe-base 包中的 diagonal 函数可以做到这一点,所以你可以这样写:

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

    并过滤此列表。

    【讨论】:

    • @daniel-wagner 是 Universe-base 的作者。他的答案就在你之前(这个)。
    【解决方案4】:

    我需要生成所有互质数的无限排序列表。每对中的第一个元素必须小于第二个。排序必须按升序进行——按对元素的总和;如果两个和相等,则乘以该对的第一个元素。

    因此,我们生成和和第一个元素的升序对,并且只保留互质数。简单俗气!

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

    【讨论】:

      猜你喜欢
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-24
      • 2016-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多