使用递归的Erlang Prime分解

时间:2014-02-19 01:12:31

标签: recursion erlang factorization

我正在尝试使用素数因子分解数字。我想使用if语句测试条件,如果因子是一个因子,则创建一个列表。它不起作用,我想不出任何其他方式它可以工作,建议?

factor( N ) ->
    if
        N rem factor(N-1) == 0 ->
            [N|factor(N-1)];
        true -> false;
    end.

3 个答案:

答案 0 :(得分:1)

此代码执行分解,但我不使用if语句(在Erlang IMHO中不常见):

%% this function is here to hide the detail of parameters needed for the tail recursion
decomp(N) when is_integer(N), (N > 0) -> 
    lists:reverse(decomp(N,[],2)).

%% generally in recursion the first clause is the stop condition
decomp(N,R,I) when I*I > N -> [N|R];
%% this is what you put in your if statement, is N is divided by I, then I is a factor of N
%% so add I in the list of factors and continue with (N div I) and I (it can be a multiple factor
decomp(N,R,I) when (N rem I) =:= 0 -> decomp(N div I,[I|R],I);
%% this clause is reached if I does not divide N, so simply skip it and go to 3 or I+2
decomp(N,R,2) -> decomp(N,R,3);
decomp(N,R,I) -> decomp(N,R,I+2).

我记录了一个例子的执行跟踪。注意,decomp / 3是尾递归的,因此长返回阶段实际上是单个返回。

1> help_num:decomp(70610).
++ (3) <147> decomp(70610,[],2)
++ (4) <150> decomp(35305,[2],2)
++ (5) <151> decomp(35305,[2],3)
++ (6) <152> decomp(35305,[2],5)
++ (7) <150> decomp(7061,[5,2],5)
++ (8) <152> decomp(7061,[5,2],7)
++ (9) <152> decomp(7061,[5,2],9)
++ (10) <152> decomp(7061,[5,2],11)
++ (11) <152> decomp(7061,[5,2],13)
++ (12) <152> decomp(7061,[5,2],15)
++ (13) <152> decomp(7061,[5,2],17)
++ (14) <152> decomp(7061,[5,2],19)
++ (15) <152> decomp(7061,[5,2],21)
++ (16) <152> decomp(7061,[5,2],23)
++ (17) <150> decomp(307,[23,5,2],23)
-- (17) [307,23,5,2]
-- (16) [307,23,5,2]
-- (15) [307,23,5,2]
-- (14) [307,23,5,2]
-- (13) [307,23,5,2]
-- (12) [307,23,5,2]
-- (11) [307,23,5,2]
-- (10) [307,23,5,2]
-- (9) [307,23,5,2]
-- (8) [307,23,5,2]
-- (7) [307,23,5,2]
-- (6) [307,23,5,2]
-- (5) [307,23,5,2]
-- (4) [307,23,5,2]
-- (3) [307,23,5,2]
++ (3) <147> lists:reverse([307,23,5,2])
[2,5,23,307]
2>

答案 1 :(得分:0)

初看起来,我在代码中看到两件不起作用的东西:

  1. 该函数应该返回一个列表。因此,测试“N rem factor(N-1)== 0”将计算列表的剩余部分除以数字,这没有任何意义。您无法在列表中执行除法。

  2. if guard中有一个递归调用,不支持。

  3. 除非您更详细地解释代码应该如何运作,否则我认为我不能提供更好的帮助。

答案 2 :(得分:0)

有一个实现示例。您无需关注next_may_be_prime/2。您可以使用简单的Old + 2。这只是优化。

-module(factor).

-export([factor/1, make_wheels/0]).

factor(X) when is_integer(X), X > 0 ->
    factor(X, 2, first_primes_steps()).

factor(X, X, _) -> [X];
factor(X, Prime, Wheel) when X rem Prime =:= 0 ->
    [Prime|factor(X div Prime, Prime, Wheel)];
factor(X, Old, Wheel) when Old*Old < X ->
    {MayBePrime, Wheel2} = next_may_be_prime(Old, Wheel),
    factor(X, MayBePrime, Wheel2);
factor(X, _, _) -> [X].

%%% Prime candidates generator

next_may_be_prime(X, [Add|T]) ->
    {X+Add, case T of [] -> wheel(); _ -> T end}.

first_primes_steps() -> [1,2,2,4,2].

wheel() ->
    [4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,14,4,6,2,
     10,2,6,6,4,2,4,6,2,10,2,4,2,12,10,2,4,2,4,6,2,6,4,6,6,6,2,6,
     4,2,6,4,6,8,4,2,4,6,8,6,10,2,4,6,2,6,6,4,2,4,6,2,6,4,2,6,10,
     2,10,2,4,2,4,6,8,4,2,4,12,2,6,4,2,6,4,6,12,2,4,2,4,8,6,4,6,
     2,4,6,2,6,10,2,4,6,2,6,4,2,4,2,10,2,10,2,4,6,6,2,6,6,4,6,6,
     2,6,4,2,6,4,6,8,4,2,6,4,8,6,4,6,2,4,6,8,6,4,2,10,2,6,4,2,4,
     2,10,2,10,2,4,2,4,8,6,4,2,4,6,6,2,6,4,8,4,6,8,4,2,4,2,4,8,6,
     4,6,6,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10,2,6,4,6,2,6,4,2,4,
     6,6,8,4,2,6,10,8,4,2,4,2,4,8,10,6,2,4,8,6,6,4,2,4,6,2,6,4,6,
     2,10,2,10,2,4,2,4,6,2,6,4,2,4,6,6,2,6,6,6,4,6,8,4,2,4,2,4,8,
     6,4,8,4,6,2,6,6,4,2,4,6,8,4,2,4,2,10,2,10,2,4,2,4,6,2,10,2,
     4,6,8,6,4,2,6,4,6,8,4,6,2,4,8,6,4,6,2,4,6,2,6,6,4,6,6,2,6,6,
     4,2,10,2,10,2,4,2,4,6,2,6,4,2,10,6,2,6,4,2,6,4,6,8,4,2,4,2,
     12,6,4,6,2,4,6,2,12,4,2,4,8,6,4,2,4,2,10,2,10,6,2,4,6,2,6,4,
     2,4,6,6,2,6,4,2,10,6,8,6,4,2,4,8,6,4,6,2,4,6,2,6,6,6,4,6,2,
     6,4,2,4,2,10,12,2,4,2,10,2,6,4,2,4,6,6,2,10,2,6,4,14,4,2,4,
     2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,12,2,12].

%%% Auxiliary functions for making prime wheels

make_wheels() ->
    %%%%%%%%%%%%%%%%
    Primes = [2,3,5,7,11],
    Next = 13,
    %%%%%%%%%%%%%%%%
    Period = lists:foldl(fun(X, A) -> X*A end, 1, Primes),
    Max = Period + Next,
    F = fun(X, A) -> array:set(X, false, A) end,
    G = fun(X, A) -> lists:foldl(F, A, lists:seq(X, Max, X)) end,
    Sieve = lists:foldl(G, array:new([{default, true}]), Primes),
    {
        diffs(Primes ++ [Next]),
        diffs([ X || X <- lists:seq(Next, Max), array:get(X, Sieve)])
        }.

diffs([H|T]) ->
    diffs(H, T).

diffs(_, []) -> [];
diffs(A, [B|T]) -> [B-A|diffs(B, T)].