10

別の質問に答えているときに、Symbolic Math Toolboxを使用せずに整数のすべての因数を実際に見つけるにはどうすればよいかという質問に出くわしました。

例えば:

factor(60)

戻り値:

 2     2     3     5

unique(factor(60))

したがって、 「1」が欠落しているすべての素因数を返します。

 2     3     5

そして、すべての要素を返す関数を探しています( 1数値自体は重要ではありませんが、それらはいいでしょう)

の意図した出力x = 60:

 1     2     3     4     5     6    10    12    15    20    30    60     

おそらくベクトル化できることを除けば、かなりかさばるソリューションを思いつきましたが、エレガントなソリューションはありませんか?

x = 60;

P = perms(factor(x));
[n,m] = size(P);
Q = zeros(n,m);
for ii = 1:n
    for jj = 1:m
        Q(ii,jj) = prod(P(ii,1:jj));
    end
end

factors = unique(Q(:))'

また、perms には 11 未満のベクトル長が必要なため、このソリューションは特定の大きな数では失敗すると思います。

4

3 に答える 3

11

以下は、整数の因数を見つけるための 6 つの異なる実装の比較です。

function [t,v] = testFactors()
    % integer to factor
    %{45, 60, 2059, 3135, 223092870, 3491888400};
    n = 2*2*2*2*3*3*3*5*5*7*11*13*17*19;

    % functions to compare
    fcns = {
        @() factors1(n);
        @() factors2(n);
        @() factors3(n);
        @() factors4(n);
        %@() factors5(n);
        @() factors6(n);
    };

    % timeit
    t = cellfun(@timeit, fcns);

    % check results
    v = cellfun(@feval, fcns, 'UniformOutput',false);
    assert(isequal(v{:}));
end

function f = factors1(n)
    % vectorized implementation of factors2()
    f = find(rem(n, 1:floor(sqrt(n))) == 0);
    f = unique([1, n, f, fix(n./f)]);
end

function f = factors2(n)
    % factors come in pairs, the smaller of which is no bigger than sqrt(n)
    f = [1, n];
    for k=2:floor(sqrt(n))
        if rem(n,k) == 0
            f(end+1) = k;
            f(end+1) = fix(n/k);
        end
    end
    f = unique(f);
end

function f = factors3(n)
    % Get prime factors, and compute products of all possible subsets of size>1
    pf = factor(n);
    f = arrayfun(@(k) prod(nchoosek(pf,k),2), 2:numel(pf), ...
        'UniformOutput',false);
    f = unique([1; pf(:); vertcat(f{:})])'; %'
end

function f = factors4(n)
    % http://rosettacode.org/wiki/Factors_of_an_integer#MATLAB_.2F_Octave
    pf = factor(n);                    % prime decomposition
    K = dec2bin(0:2^length(pf)-1)-'0'; % all possible permutations
    f = ones(1,2^length(pf));
    for k=1:size(K)
      f(k) = prod(pf(~K(k,:)));        % compute products 
    end; 
    f = unique(f);                     % eliminate duplicates
end

function f = factors5(n)
    % @LuisMendo: brute-force implementation
    f = find(rem(n, 1:n) == 0);
end

function f = factors6(n)
    % Symbolic Math Toolbox
    f = double(evalin(symengine, sprintf('numlib::divisors(%d)',n)));
end

結果:

>> [t,v] = testFactors();
>> t
t =
    0.0019        % factors1()
    0.0055        % factors2()
    0.0102        % factors3()
    0.0756        % factors4()
    0.1314        % factors6()

>> numel(v{1})
ans =
        1920

最初のベクトル化されたバージョンが最速ですが、自動 JIT 最適化のおかげで、同等のループベースの実装 ( factors2) もそれほど遅れていません。

ブルートフォース実装 ( factors5()) を無効にする必要があったことに注意してください。これは、メモリ不足エラーをスローするためです (ベクトル1:3491888400を倍精度で格納するには、26GB 以上のメモリが必要です!)。この方法は、空間的にも時間的にも、大きな整数には明らかに適していません。

結論:次のベクトル化された実装を使用してください:)

n = 3491888400;
f = find(rem(n, 1:floor(sqrt(n))) == 0);
f = unique([1, n, f, fix(n./f)]);
于 2014-01-10T14:50:32.293 に答える