While answering another, I stumbled over the question how I actually could find all factors of an integer number without the Symbolic Math Toolbox.
For example:
factor(60)
returns:
2 2 3 5
unique(factor(60))
would therefore return all prime-factors, "1" missing.
2 3 5
And I'm looking for a function which would return all factors (1 and the number itself are not important, but they would be nice)
Intended output for x = 60
:
1 2 3 4 5 6 10 12 15 20 30 60
I came up with that rather bulky solution, apart from that it probably could be vectorized, isn't there any elegant solution?
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(:))'
Also I think, this solution will fail for certain big numbers, because perms requires a vector length < 11.
You can find all factors of a number n
by dividing it by a vector containing the integers 1 through n
, then finding where the remainder after division by 1 is exactly zero (i.e., the integer results):
>> n = 60;
>> find(rem(n./(1:n), 1) == 0)
ans =
1 2 3 4 5 6 10 12 15 20 30 60
Here is a comparison of six different implementations for finding factors of an integer:
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
The results:
>> [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
Although the first vectorized version is the fastest, the equivalent loop-based implementation (factors2
) is not far behind, thanks to automatic JIT optimization.
Note that I had to disable the brute-force implementation (factors5()
) because it throws an out-of-memory error (storing the vector 1:3491888400
in double-precision requires over 26GB of memory!). This method is obviously not feasible for large integers, neither space- or time-wise.
Conclusion: use the following vectorized implementation :)
n = 3491888400;
f = find(rem(n, 1:floor(sqrt(n))) == 0);
f = unique([1, n, f, fix(n./f)]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With