直接按升序枚举一个数的因数而不排序?

编程入门 行业动态 更新时间:2024-10-22 16:35:17
本文介绍了直接按升序枚举一个数的因数而不排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

是否有一种高效的算法可以按升序枚举一个数 n 的因数而不进行排序?我所说的高效"是指:

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

  • 该算法从 n 的素数功率因数分解开始,避免了对除数的蛮力搜索.

  • The algorithm avoids a brute-force search for divisors by starting with the prime-power factorization of n.

    算法的运行时复杂度为 O(d log₂ d) 或更好,其中 d 是n.

    The runtime complexity of the algorithm is O(d log₂ d) or better, where d is the divisor count of n.

    算法的空间复杂度为 O(d).

    The spatial complexity of the algorithm is O(d).

    该算法避免了排序操作.也就是说,因子是按顺序生成的,而不是乱序生成然后排序.尽管使用简单的递归方法枚举然后排序是 O(d log2 d),但排序中涉及的所有内存访问成本非常高.

    The algorithm avoids a sort operation. That is, the factors are produced in order rather than produced out of order and sorted afterward. Although enumerating using a simple recursive approach and then sorting is O(d log₂ d), there is a very ugly cost for all the memory accesses involved in sorting.

    一个简单的例子是 n = 360 = 2³ × 3² × 5,它有 d=24 个因子:{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18, 20, 24, 30, 36, 40, 45, 60, 72, 90, 120, 180, 360 }.

    A trivial example is n = 360 = 2³ × 3² × 5, which has d=24 factors: { 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18, 20, 24, 30, 36, 40, 45, 60, 72, 90, 120, 180, 360 }.

    更严重的例子是n = 278282512406132373381723386382308832000 = 2⁸ × 3⁴ × 5³ × 7² × 11² × 13² × 17 × 19 ×3 ×3 ×3 ×3 ×3 ×3× 59 × 61 × 67 × 71 × 73 × 79,其中有 d=318504960 个因子(显然太多了,无法在此列出!).顺便说一下,这个数是最多 2^128 的任何数中因数最多的数.

    A more serious example is n = 278282512406132373381723386382308832000 = 2⁸ × 3⁴ × 5³ × 7² × 11² × 13² × 17 × 19 × 23 × 29 × 31 × 37 × 41 × 43 × 47 × 53 × 59 × 61 × 67 × 71 × 73 × 79, which has d=318504960 factors (obviously too many to list here!). This number, incidentally, has the greatest number of factors of any number up to 2^128.

    我可以发誓,我在几周前看到过对此类算法的描述以及示例代码,但现在我似乎在任何地方都找不到.它使用了一些魔术来在输出列表中为每个素数维护一个祖先索引列表.(更新:我将因子生成与操作类似的汉明数混淆了.)

    I could swear that I saw a description of such algorithm a few weeks ago, with sample code, but now I can't seem to find it anywhere. It used some magic trick of maintaining a list of progenitor indexes in the output list for each prime factor. (Update: I was confusing factor generation with Hamming numbers, which operate similarly.)

    我最终使用了一个运行时 O(d) 的解决方案,具有极低的内存开销,就地创建 O(d) 输出,并且是比我所知道的任何其他方法都要快得多.我已经发布了这个解决方案作为答案,带有 C 源代码.它是另一位贡献者 Will Ness 在 Haskell 中介绍的漂亮算法的高度优化、简化版本.我选择 Will 的答案作为接受的答案,因为它提供了一个非常优雅的解决方案,符合最初声明的所有要求.

    I ended up using a solution which is O(d) in runtime, has extremely low memory overhead creating the O(d) output in-place, and is significantly faster than any other method I am aware of. I've posted this solution as answer, with C source code. It is a heavily optimized, simplified version of a beautiful algorithm presented here in Haskell by another contributor, Will Ness. I've selected Will's answer as the accepted answer, as it provided a very elegant solution that matched all the requirements as originally stated.

    推荐答案

    我们可以合并多个流,产生的首先是没有重复的.

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

    从列表[1]开始,对于每个唯一的质因数p,我们迭代地将列表乘以pk 次(其中 k 是 p 的重数)以获得 k 个新列表,并将它们与传入列表合并在一起.

    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.

    在 Haskell 中,

    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, y, z,...] $ xs -- where { y=f x; z=f y; ... } {- 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 排列二进制 merges(假设被合并的流中没有重复,以简化操作)以树状方式提高速度;而 foldr 以线性方式工作.

    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 是一个标准函数,它将列表中的相邻相等组合在一起,而 factorize 是一种众所周知的试除算法,它以非递减顺序生成一个数的质因数:

    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 _ = []

    (.) 是一个函数组合运算符,将其右侧参数函数的输出作为输入链接到其左侧.它(和 $)可以朗读为of".

    (.) 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".

  • 更多推荐

    直接按升序枚举一个数的因数而不排序?

    本文发布于:2023-11-29 00:48:39,感谢您对本站的认可!
    本文链接:https://www.elefans.com/category/jswz/34/1644623.html
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:升序   因数   而不   个数

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!