求解方程a * b = c,其中a,b和c是自然数

时间:2015-04-13 12:21:06

标签: haskell equation brute-force

我有一些自然数c。我想找到所有成对的自然数a and b,其中a < b,例如a * b = c

我有一个解决方案:

solve c = do solveHelper [1..c] c where
                   solveHelper xs c = do
                       x <- xs
                       (division, modulo ) <- return (c `divMod` x)
                       True <- return (modulo  == 0)
                       True <- return (x <= division)
                       return (x, division)

示例:

*Main> solve 10
[(1,10),(2,5)]

有没有办法加速我的代码,或者我应该使用更好的算法?

2 个答案:

答案 0 :(得分:5)

你可以做得更好,更好。基本思路是这样的:首先,将数字分解;然后枚举分解的分区。每个分区的产品都是一个解决方案。有快速的分解算法,但即使是天真的算法也是对你的代码的改进;这样:

factorize :: Integer -> [Integer]
factorize n
    | n < 1 = error "no. =("
    | otherwise = go 2 n
    where
    go p n | p * p > n = [n]
    go p n = case quotRem n p of
        (q, 0) -> p:go p q
        _      -> go (p+1) n

我将使用非常好的multiset-comb包来计算这组因子的分区。它不支持开箱即用的Foldable / Traversable内容,因此我们必须推出自己的product操作 - 但实际上这可能会更多一些比使用标准界面给我们的product更有效率。

import Math.Combinatorics.Multiset

productMS :: Multiset Integer -> Integer
productMS (MS cs) = product [n^p | (n, p) <- cs]

divisors :: Integer -> [(Integer, Integer)]
divisors n =
    [ (a, b)
    | (aMS, bMS) <- splits (fromList (factorize n))
    , let a = productMS aMS; b = productMS bMS
    , a <= b
    ]

对于不公平的时间安排,我们可以用ghci进行比较:

*Main> :set +s
*Main> length $ solve (product [1..10])
135
(3.55 secs, 2,884,836,952 bytes)
*Main> length $ divisors (product [1..10])
135
(0.00 secs, 4,612,104 bytes)
*Main> length $ solve (product [1..15])
^CInterrupted. [after several minutes, I gave up]
*Main> length $ divisors (product [1..15])
2016
(0.03 secs, 33,823,168 bytes)

此处solve是您的解决方案,divisors是我的解决方案。为了公平比较,我们应该编译;我用过这个程序:

main = print . last . solve . product $ [1..11]

(与divisors代替solve类似。)我使用-O2编译;你的总共使用了1.367s,总共使用了0.002s。

答案 1 :(得分:4)

您不使用一种优化:您不必尝试从0c的所有值。

a < ba * b = ca * a < c,这意味着您只需要尝试从0sqrt c的数字。或者,如果您不想计算c的平方根,则可以在a * a >= c后立即停止。

为此,您可以将[1..c]替换为(takeWhile (\x -> x * x < c) [1..])