2014-01-09 3 views
10

При ответе на другой, я наткнулся на вопрос, как я на самом деле можно было найти все факторы целого числа безСимволическое Math Toolbox.факторизации целого числа

Например:

factor(60) 

возвращается: поэтому

2  2  3  5 

unique(factor(60)) 

бы вернуть все прайм-факторы, "1" отсутствует.

2  3  5 

И я ищу функцию, которая будет возвращать все факторы (и сам номер не важны, но они бы неплохо)

Предназначенный выход для 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(:))' 

Кроме того, я думаю, это решение будет не в состоянии для некоторых больших чисел, так как завивка требует длины вектора < 11.

+0

Ваш код не генерирует 12 и 20, просто говоря. – Mahm00d

+0

@ Mahm00d: ты прав, еще хуже. Я исправил это. – thewaywewalk

ответ

11

Вот сравнение шести различных реализаций для нахождения коэффициентов целого числа:

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 

Хотя первая векторная версия является самой быстрой, эквивалентная реализация на основе цикла (factors2) не сильно отстает, благодаря автоматической оптимизации JIT.

Обратите внимание, что мне пришлось отключить реализацию грубой силы (factors5()), потому что она выдает ошибку вне памяти (для хранения вектора 1:3491888400 с двойной точностью требуется более 26 ГБ памяти!). Этот метод, очевидно, невозможен для больших целых чисел, не пространственно-временного.

Вывод: использовать следующую векторизованную реализацию :)

n = 3491888400; 
f = find(rem(n, 1:floor(sqrt(n))) == 0); 
f = unique([1, n, f, fix(n./f)]); 
+0

приятная мысль! Знаете ли вы, что вы можете сократить вдвое время с помощью 'mod' вместо' rem'? (Вот почему я никогда не слышал о 'rem', я всегда использовал' mod', что в большинстве случаев практически одинаково) PS: Я бы предпочел сделать ваше заключение сверху, я не знаю, сколько людей ухаживает о других 5 подходах;) – thewaywewalk

+0

@thewaywewalk: Я действительно не вижу разницы во времени между ['rem'] (http://www.mathworks.com/help/matlab/ref/rem.html) и [ 'mod'] (http://www.mathworks.com/help/matlab/ref/mod.html), как вы сказали, они практически одинаковы, за исключением перечисленных особых случаев. Я запускаю последнюю версию MATLAB Windows 64-бит. – Amro

+0

Я проверил его снова с вашей функцией синхронизации, я либо получаю одинаковое время для обоих, либо (в большинстве случаев) один из них до 5 раз быстрее. Однако это не имеет большого значения. – thewaywewalk

13

Вы можете найти все факторы ряда n путем деления его вектором, содержащим целые числа 1 по n, а затем найти где остаток после деления на 1 точно равен нулю (то есть, целые результаты):

>> n = 60; 
>> find(rem(n./(1:n), 1) == 0) 

ans = 

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

Я думал о чем-то подобном, но никогда не слышал о 'rem' раньше. Это очень элегантно! – thewaywewalk

+1

Нет необходимости в разделении. «rem» в одиночку достаточно (см. мой ответ) –

+1

этот метод грубой силы медленный для больших целых чисел, не говоря уже о том, что он может легко выкидывать ошибки из памяти (например, «n = 1e9» потребует около 8 ГБ Память). – Amro

10

улучшение по сравнению с @gnovice's answer является С.К. внутрибрюшинна операция деления: rem сами по себе достаточно:

n = 60; 
find(rem(n, 1:n)==0) 
+0

Хорошая добыча! Я полностью пропустил это. – gnovice

+0

Я думаю, было бы справедливо держать gnovices ответ выбранным, как принято, поскольку у него была идея;), но +1 в любом случае! – thewaywewalk

+0

@thewaywewalk Согласен. И спасибо! –

Смежные вопросы