Enumerate factors of a number directly in ascending order without sorting?

前端 未结 4 1258
野趣味
野趣味 2020-11-27 20:00

Is there an efficient algorithm to enumerate the factors of a number n, in ascending order, without sorting? By “efficient” I mean:

  1. The algorithm a

4条回答
  •  自闭症患者
    2020-11-27 20:46

    We can merge streams of multiples, produced so there are no duplicates in the first place.

    Starting with the list [1], for each unique prime factor p, we multiply the list by p iteratively k times (where k is the multiplicity of p) to get k new lists, and merge them together with that incoming list.

    In Haskell,

    ordfactors n =          --   <----------------------<---<---<-----\
      foldr g [1]           -- g (19,1) (g (7,1) (g (3,2) (g (2,3) [1])))
        . reverse           -- [(19,1),(7,1),(3,2),(2,3)]              ^
        . primePowers       -- [(2,3),(3,2),(7,1),(19,1)]              |
        $ n                 -- 9576                    --->--->--->----/
           where
             g (p,k) xs = mergeAsTree 
                            . take (k+1)          -- take 3 [a,b,c,d..] = [a,b,c]
                            . iterate (map (p*))  -- iterate f x = [x, f x, f(f x),...]
                            $ xs
    
    {-  g (2,3) [1] = let a0 = [1]        
                          a1 = map (2*) a0               -- [2]
                          a2 = map (2*) a1               -- [4]
                          a3 = map (2*) a2               -- [8]
                      in mergeAsTree [ a0, a1, a2, a3 ]  -- xs2 = [1,2,4,8]
    
        g (3,2) xs2 = let b0 = xs2                       -- [1,2,4,8]
                          b1 = map (3*) b0               -- [3,6,12,24]
                          b2 = map (3*) b1               -- [9,18,36,72]
                      in mergeAsTree [ b0, b1, b2 ]      -- xs3 = [1,2,3,4,6,8,9,12,...] 
    
        g (7,1) xs3 = mergeAsTree [ xs3, map (7*) xs3 ]  -- xs4 = [1,2,3,4,6,7,8,9,...]
    
        g (19,1) xs4 = mergeAsTree [ xs4, map (19*) xs4 ]  
    -}
    mergeAsTree xs   = foldi merge [] xs    -- [a,b]     --> merge a b
                                            -- [a,b,c,d] --> merge (merge a b) (merge c d)
    foldi f z []     = z
    foldi f z (x:xs) = f x (foldi f z (pairs f xs))
    pairs f (x:y:t)  = f x y : pairs f t
    pairs _ t        = t
    

    foldi arranges the binary merges (which presuppose that there's no duplicates in the streams being merged, for streamlined operation) in a tree-like fashion for speed; whereas foldr works in linear fashion.

    primePowers n | n > 1 =           -- 9576 => [(2,3),(3,2),(7,1),(19,1)]
      map (\xs -> (head xs,length xs)) --                                ^
        . group                       -- [[2,2,2],[3,3],[7],[19]]        |
        . factorize                   -- [2,2,2,3,3,7,19]                |
        $ n                           -- 9576             --->--->--->---/
    

    group is a standard function that groups adjacent equals in a list together, and factorize is a well-known trial-division algorithm that produces prime factors of a number in non-decreasing order:

    factorize n | n > 1 = go n (2:[3,5..])   -- 9576 = 2*2*2*3*3*7*19
       where                                 -- produce prime factors only
         go n (d:t)
            | d*d > n    = [n]
            | r == 0     =  d : go q (d:t)
            | otherwise  =      go n t
                where 
                  (q,r)  = quotRem n d
    factorize _ = []
    

    (.) is a functional composition operator, chaining the output of its argument function on its right as input into the one on its left. It (and $) can be read aloud as "of".

提交回复
热议问题