【问题标题】:Making a list of divisors in Haskell在 Haskell 中制作除数列表
【发布时间】:2009-09-26 06:18:14
【问题描述】:

我在 eulerproject 中做问题 21。 一部分需要找到一个数字的正确除数列表。即有剩余的n 和一些小于n 的数量。所以我做了这个 Haskell,但是 GHCI 生我的气。

divisors n =[ n | n <- [1..(n-1)], n `rem` [1..(n-1)] ==0 ]

问题是我不知道怎么做:

n `rem` [1..(n-1)]

因此它只返回小于n 的数字,该数字平均分为n。

【问题讨论】:

    标签: haskell math


    【解决方案1】:

    你只需要一个单独的变量。

    Prelude> let divisors n = [x | x <- [1..(n-1)], n `rem` x == 0]
    Prelude> divisors 20
    [1,2,4,5,10]
    Prelude> divisors 30
    [1,2,3,5,6,10,15]
    

    现在,如果您想让它更高效一点,我们已经知道除数不会超过 n 的一半,而且我们知道 1 是所有事物的除数。而且,让我们继续让它更像 Haskell-y 启动,避免列表理解:

    Prelude> let divisors n = 1 : filter ((==0) . rem n) [2 .. n `div` 2]
    Prelude> divisors 20
    [1,2,4,5,10]
    Prelude> divisors 30
    [1,2,3,5,6,10,15]
    Prelude> divisors 31
    [1]
    

    【讨论】:

    • 为什么列表理解不是很Haskell?另外,一个小问题,有没有办法找到一个列表中所有列表之和的总和?
    • 无论如何,我都不是 Haskell 的老手,但我还没有真正看到任何 Haskell 库中使用的任何列表推导,例如。小答案:sum $ map sum x,如sum $ map sum [ [1,2,3], [4,5,6], [7,8,9] ] 得到45。
    • 或sum . concat,它首先列出了一个大列表。
    【解决方案2】:

    如果除数列表的顺序不重要,您可以通过仅检查 [2..sqrt n] 范围内的除数来显着提高效率。

    类似这样的东西(如果你想得更多,你可能会做一些局部优化):

    divisors' n = (1:) $ nub $ concat [ [x, div n x] | x <- [2..limit], rem n x == 0 ]
         where limit = (floor.sqrt.fromIntegral) n
    

    divisors 是之前的实现,divisors' 是新的实现:

    *Main> (sum.divisors) 10000000
    14902280
    (4.24 secs, 521136312 bytes)
    
    *Main> (sum.divisors') 10000000
    14902280
    (0.02 secs, 1625620 bytes)
    

    注意: 我们使用 nub 删除任何重复,而实际上,如果 n 是平方数,则唯一可能的重复将是限制。您可以通过更好地处理这种情况来提高效率,但我发现这种方式更具可读性(如果运行时间不重要的话)。

    【讨论】:

    • nub 可以删除将 [2 .. limit] 替换为 [2 .. limit-1],然后将 limit 视为特殊情况,如果 rem n limit == 0 如果是则添加限制除数
    猜你喜欢
    • 2016-05-17
    • 2018-01-14
    • 2012-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 2012-08-30
    相关资源
    最近更新 更多