Scope
Matlab does include a function, named dvbs2ldpc, to construct a parity check matrix to be used at LDPC encoding stage in DVB-S2 standard.
This standard counts with two different transmission modes (SHORT and NORMAL), depending on the size of the resulting codeword. However dvbs2ldpc function only applies for NORMAL one. Thus I am trying to build up a function to be used in SHORT transmission mode.
Code description
You may find below all the related code in functions dvbs2ldpcShort.m, where I am to construct a parity check matrix for SHORT transmission mode, and LDPC.m, where I perform some BER simulations to check out the results.
You may see that dvbs2ldpcShort resembles quite a lot to dvbs2ldpc, which appears in Matlab Communication Toolbox. The two only differences that I included were changing the length of the codeword and the accumulator bits of the parity check matrix that it may correspond (please see annexes B and C from this link for further information).
Code
dvbs2ldpcShort.m
function H = dvbs2ldpcShort(R)
if R = 1/2
Rreal = 4/9; % Actual rate (as k = 7200 and N = 16200)
end
lenCodeWord = 16200; % Length of codeword for DVB-S.2
NB = 360; % Node indices parameter for DVB-S.2.
numInfoBits = lenCodeWord * Rreal;
numParityBits = lenCodeWord - numInfoBits;
[ct1, ct2] = getchecknodetable(R);
ck1 = nodeindices(ct1, numParityBits, NB);
ck2 = nodeindices(ct2, numParityBits, NB);
d = [size(ck1,2) size(ck1,1) size(ck2,2) size(ck2,1) numParityBits-1 2 1 1];
r = [ck1(:); ck2(:); 0; reshape(ones(2,1)*(1:numParityBits-1),[],1)];
S = zeros(length(r),1);
numGroup = length(d)/2;
n = 0;
ncol = 1;
for i = 1:numGroup
p = d(2*i-1)*d(2*i);
S(n+1:n+p) = reshape(ones(d(2*i),1)*(ncol:ncol+d(2*i-1)-1),p,1);
ncol = ncol + d(2*i-1);
n = n + p;
end
% Parity-check matrix (sparse) for DVB-S.2
outputFormat = 'sparse'; % Sparse matrix by default
if nargin == 2
if ~strcmp(varargin{1}, 'sparse') && ~strcmp(varargin{1}, 'indices')
error(message('comm:dvbs2ldpc:InvalidOutputFormat'));
end
outputFormat = varargin{1};
end
if strcmp(outputFormat, 'sparse')
H = logical(sparse(double(r+1), S, 1));
else
H = [double(r+1), double(S)];
end
%--------------------------------------------------------------------------
function ck = nodeindices(ct, M, NB)
% ct: check node table (single group)
% M: number of parity bits
% NB: block size
[N, D] = size(ct);
q = (M/NB);
b = (1:NB);
bq = (b-1).'*q;
ck = zeros(D, NB*N);
for r=1:N
ck(:, NB*(r-1)+1:NB*r) = mod(addcr(bq, ct(r,:)), M)';
end
%--------------------------------------------------------------------------
function A = addcr(c, r)
M = length(c);
N = length(r);
A = zeros(M, N);
for m = 1:M
A(m, :) = r + c(m);
end
%--------------------------------------------------------------------------
function [ct1, ct2] = getchecknodetable(R)
switch R
case 1/2 % There are all cases, but here I only include the R=1/2 one
ct1 = [20 712 2386 6354 4061 1062 5045 5158
21 2543 5748 4822 2348 3089 6328 5876
22 926 5701 269 3693 2438 3190 3507
23 2802 4520 3577 5324 1091 4667 4449
24 5140 2003 1263 4742 6497 1185 6202];
ct2 = [0 4046 6934
1 2855 66
2 6694 212
3 3439 1158
4 3850 4422
5 5924 290
6 1467 4049
7 7820 2242
8 4606 3080
9 4633 7877
10 3884 6868
11 8935 4996
12 3028 764
13 5988 1057
14 7411 3450];
end
LDPC.m
r = 1/2;
k = 7200;
ldpcEnc = comm.LDPCEncoder(dvbs2ldpcShort(r));
psk4Mod = comm.PSKModulator(4, 'BitInput',true);
EsNo = 0.2 : 0.1 : 1.2;
BER = zeros(size(EsNo));
for k = 1 : 1 : length(EsNo)
awgnChan = comm.AWGNChannel(...
'NoiseMethod','Signal to noise ratio (Es/No)','EsNo',EsNo(k));
psk4Demod = comm.PSKDemodulator(4, 'BitOutput',true,...
'DecisionMethod','Approximate log-likelihood ratio', ...
'Variance', 1/(2*10^(awgnChan.EsNo/10)));
ldpcDec = comm.LDPCDecoder(dvbs2ldpcShort(r));
ber = comm.ErrorRate;
for counter = 1:100
data = logical(randi([0 1], k, 1));
encodedData = ldpcEnc(data);
modSignal = psk4Mod(encodedData);
receivedSignal = awgnChan(modSignal);
demodSignal = psk4Demod(receivedSignal);
receivedBits = ldpcDec(demodSignal);
errorStats = ber(data, receivedBits);
end
BER(k) = errorStats(1);
end
Question
The corresponding BER curve does not resemble at all to how it is for NORMAL transmission mode (these represent the BER as function of SNR. and mine are function of EbNo, but the difference shouldn't be really big at all). Instead, results seem to be unexpectedly good. Can you see anything wrong with my code?
What could be wrong in my code?
Many thanks in advance, and may you have a nice weekend!
Thanks for the note on LDPC code identifier and Effective LDPC rate.
Your example-performance should be better, because you use more redundancy: 5/9 (0.56) of code word whereas in the MATLAB example they use (1 - 2/3) = 1/3 (0.33) of code word as redundancy.
I would like to add a note also: In the ETSI standard they also use a parameter q, that is equal to q = (M/NB); (M -- num of parity bits, NB = 360) when the n_ldpc = 64800, but if n_ldpc = 16200 the q should be used according a table in the ETSI standard.
Let's look at my solution: dvbs2ldpc_custom
Related
I want to write this matlab code in python but I do not know what LEV(1:n+1:n^2) = 0; or LEV(i,:) means. Can anyone explain me what are this notation? Thank you!
function A = ILU_p(A,p)
n = length(A);
LEV = inf(n);
LEV(find(A)) = 0;
LEV(1:n+1:n^2) = 0;
for i = 2:n
for k = 1:i-1
if LEV(i,k) > p
continue
end
A(i,k) = A(i,k) / A(k,k);
A(i,k+1:n) = A(i,k+1:n) - A(i,k) * A(k,k+1:n);
LEV(i,k+1:n) = min([LEV(i,k+1:n); LEV(i,k) + LEV(k,k+1:n) + 1]);
end
A(i,find(LEV(i,:)>p)) = 0;
end
The below sets up a vector of values to be used in an index. If n=10 then the below would yield a row vector of [1 12 23 34 45 56 67 78 89 100]
1:n+1:n^2
Since LEV is set up as an nxn matrix and the above row vector picks up the diagonal elements, i.e., LEV(1) = LEV(1,1), LEV(12) = LEV(2,2), etc.
LEV(i,:) is MATLAB's shorthand for referencing all columns in row i.
I am struggling to plot the PDF and CDF graphs of where
Sn=X1+X2+X3+....+Xn
using central limit theorem where n = 1; 2; 3; 4; 5; 10; 20; 40
I am taking Xi to be a uniform continuous random variable for values between (0,3).
Here is what i have done so far -
close all
%different sizes of input X
%N=[1 5 10 50];
N = [1 2 3 4 5 10 20 40];
%interval (1,6) for random variables
a=0;
b=3;
%to store sum of differnet sizes of input
for i=1:length(N)
%generates uniform random numbers in the interval
X = a + (b-a).*rand(N(i),1);
S=zeros(1,length(X));
S=cumsum(X);
cd=cdf('Uniform',S,0,3);
plot(cd);
hold on;
end
legend('n=1','n=2','n=3','n=4','n=5','n=10','n=20','n=40');
title('CDF PLOT')
figure;
for i=1:length(N)
%generates uniform random numbers in the interval
X = a + (b-a).*rand(N(i),1);
S=zeros(1,length(X));
S=cumsum(X);
cd=pdf('Uniform',S,0,3);
plot(cd);
hold on;
end
legend('n=1','n=2','n=3','n=4','n=5','n=10','n=20','n=40');
title('PDF PLOT')
My output is nowhere near what I am expecting any help is much appreciated.
This can be done with vectorization using rand() and cumsum().
For example, the code below generates 40 replications of 10000 samples of a Uniform(0,3) distribution and stores in X. To meet the Central Limit Theorem (CLT) assumptions, they are independent and identically distributed (i.i.d.). Then cumsum() transforms this into 10000 copies of the Sn = X1 + X2 + ... where the first row is n = 10000copies of Sn = X1, the 5th row is n copies of S_5 = X1 + X2 + X3 + X4 + X5. The last row is n copies of S_40.
% MATLAB R2019a
% Setup
N = [1:5 10 20 40]; % values of n we are interested in
LB = 0; % lowerbound for X ~ Uniform(LB,UB)
UB = 3; % upperbound for X ~ Uniform(LB,UB)
n = 10000; % Number of copies (samples) for each random variable
% Generate random variates
X = LB + (UB - LB)*rand(max(N),n); % X ~ Uniform(LB,UB) (i.i.d.)
Sn = cumsum(X);
You can see from the image that the n = 2 case, the sum is indeed a Triangular(0,3,6) distribution. For the n = 40 case, the sum is approximately Normally distributed (Gaussian) with mean 60 (40*mean(X) = 40*1.5 = 60). This shows the convergence in distribution for both the probability density function (PDF) and the cumulative distribution function (CDF).
Note: The CLT is often stated with convergence in distribution to a Normal distribution with zero mean as it has been shifted. Shifting the results by subtracting mean(Sn) = n*mean(X) = n*0.5*(LB+UB) from Sn gets this done.
Code below isn't the gold standard but it produced the image.
figure
s(11) = subplot(6,2,1) % n = 1
histogram(Sn(1,:),'Normalization','pdf')
title(s(11),'n = 1')
s(12) = subplot(6,2,2)
cdfplot(Sn(1,:))
title(s(12),'n = 1')
s(21) = subplot(6,2,3) % n = 2
histogram(Sn(2,:),'Normalization','pdf')
title(s(21),'n = 2')
s(22) = subplot(6,2,4)
cdfplot(Sn(2,:))
title(s(22),'n = 2')
s(31) = subplot(6,2,5) % n = 5
histogram(Sn(5,:),'Normalization','pdf')
title(s(31),'n = 5')
s(32) = subplot(6,2,6)
cdfplot(Sn(5,:))
title(s(32),'n = 5')
s(41) = subplot(6,2,7) % n = 10
histogram(Sn(10,:),'Normalization','pdf')
title(s(41),'n = 10')
s(42) = subplot(6,2,8)
cdfplot(Sn(10,:))
title(s(42),'n = 10')
s(51) = subplot(6,2,9) % n = 20
histogram(Sn(20,:),'Normalization','pdf')
title(s(51),'n = 20')
s(52) = subplot(6,2,10)
cdfplot(Sn(20,:))
title(s(52),'n = 20')
s(61) = subplot(6,2,11) % n = 40
histogram(Sn(40,:),'Normalization','pdf')
title(s(61),'n = 40')
s(62) = subplot(6,2,12)
cdfplot(Sn(40,:))
title(s(62),'n = 40')
sgtitle({'PDF (left) and CDF (right) for Sn with n \in \{1, 2, 5, 10, 20, 40\}';'note different axis scales'})
for tgt = [11:10:61 12:10:62]
xlabel(s(tgt),'Sn')
if rem(tgt,2) == 1
ylabel(s(tgt),'pdf')
else % rem(tgt,2) == 0
ylabel(s(tgt),'cdf')
end
end
Key functions used for plot: histogram() from base MATLAB and cdfplot() from the Statistics toolbox. Note this could be done manually without requiring the Statistics toolbox with a few lines to obtain the cdf and then just calling plot().
There was some concern in comments over the variance of Sn.
Note the variance of Sn is given by (n/12)*(UB-LB)^2 (derivation below). Monte Carlo simulation shows our samples of Sn do have the correct variance; indeed, it converges to this as n gets larger. Simply call var(Sn(40,:)).
% with n = 10000
var(Sn(40,:)) % var(S_40) = 30 (will vary slightly depending on random seed)
(40/12)*((UB-LB)^2) % 29.9505
You can see the convergence is very good by S_40:
step = 0.01;
Domain = 40:step:80;
mu = 40*(LB+UB)/2;
sigma = sqrt((40/12)*((UB-LB)^2));
figure, hold on
histogram(Sn(40,:),'Normalization','pdf')
plot(Domain,normpdf(Domain,mu,sigma),'r-','LineWidth',1.4)
ylabel('pdf')
xlabel('S_n')
Derivation of mean and variance for Sn:
For the expectation (mean), the second equality holds by linearity of expectation. The third equality holds since X_i are identically distributed.
The discrete version of this is posted here.
I am interested in modifying the code below to find the parameters I_1, I_2, I_3 and I_4, to be used in another code. Every time I run the code, it throws up
In an assignment A(:) = B, the number of elements in A and B must be the same
on this line " mult(mult == 0) = B;".
I have spent eternity figuring out what the problem could be. Here is the code:
%%% Some Parameters %%
delta = 0.6; % Blanked subframe ratio
B = [0 0.2 0.4 0.6 0.8 1]; %Power splitting factor
k = 2.3; %Macro BS density
f = k*5; %Small cell density
j = 300; %users density
P_m = 46; %Macro BS transmission power
P_s = 23; %SC transmit power
Zm = -15;
Zs = -15;
iter = 30; %Iteration run
h = 500; %Simulation area
hu = 0.8*h; %users simulation area
Vm = round(k*h); %Macro BS average no in h
Vs = round(f*h); %SC average no in h
Vu = round(j*hu); %%users average no in hu
Pm = 10^(P_m/10)/1000*10^(Zm/10);
Ps = 10^(P_s/10)/1000*10^(Zs/10);
for i = iter;
%% XY coodinates for Macrocell, small cells and users.
Xm = sqrt(h)*(rand(Vm,1)-0.5);
Ym = sqrt(h)*(rand(Vm,1)-0.5);
Xs = sqrt(h)*(rand(Vs,1)-0.5);
Ys = sqrt(h)*(rand(Vs,1)-0.5);
Xu = sqrt(hu)*(rand(Vu,1)-0.5);
Yu = sqrt(hu)*(rand(Vu,1)-0.5);
%Total coordinates for MBS and small cells
Total_Coord = [Xs Ys ones(size(Xs)) Xm Ym 2*ones(size(Xm))];
%Distance between BSs and users
[Xsm_mat, Xu_mat] = meshgrid(Total_Coord(:,1),Xu);
[Ysm_mat, Yu_mat] = meshgrid(Total_Coord(:,2),Yu);
Distance = sqrt((Xsm_mat-Xu_mat).^2 + (Ysm_mat-Yu_mat).^2);
%% To determine serving BS for each user
[D_m,idx_m] = min(Distance(:,(length(Xs)+1):end),[],2);
idx_m = idx_m + length(Xs);
[D_s,idx_s] = min(Distance(:,1:length(Xs)),[],2);
%% Power received by users from each BS
Psm_mat = [Ps*ones(length(Xu),length(Xs))
Pm*ones(length(Xu),length(Xm))]; % Transmit power of MBS and small cells
Pr_n = Psm_mat.*exprnd(1,size(Psm_mat))./(Distance*1e3).^4;
mult = binornd(1,delta,1,length(Xm)); % Full transmission power of each
interfering MBS for delta
mult(mult == 0) = B; % Reduced transmission power for (1-delta)
Pr = Pr_n.*[ones(length(Xu),length(Xs)) repmat(mult,length(Xu),1)];%
Interference from each BS
%% Power received by each user from serving BSs
Prm = Pr(sub2ind(size(Pr),(1:length(idx_m))',idx_m));
Prs = Pr(sub2ind(size(Pr),(1:length(idx_s))',idx_s));
P_m_n = Pr_n(sub2ind(size(Pr_n),(1:length(idx_m))',idx_m));
%% Total interference for each UE
I_T = sum(Pr,2) - Prm - Prs;
I_1 = P_m_n./(Prs + I_T);
I_2 = Prs./(P_m_n + I_T);
I_3 = B*I_1;
I_4 = Prs./(B*P_m_n + I_T);
end
The error appeared on this line "mult(mult == 0) = B;".
I know it to be assignment problem which requires equality in both the left and right dimensions. Suggestions for correction will be appreciated.
Your vector mult has length Vm (number of macro BS?). Assigning to mult(mult==0) will assign to a subset of this vector (those that have a value equal to zero). What you are assigning is your variable B which you define as B = [0 0.2 0.4 0.6 0.8 1], i.e., it is a length-6 vector. The assignment fails unless mult has exactly 6 zeros.
I highly doubt that this is what you want. It looks like you are trying to assign the same value ("Reduced transmission power"). Then your B should be scalar though.
Since we have no idea what you are trying to do (and your code is not exactly an MCVE), we can only guess.
I've had problems with my code as I've tried to make an integral compute, but it will not for the power, P2.
I've tried using anonymous function handles to use the integral() function on MATLAB as well as just using int(), but it will still not compute. Are the values too small for MATLAB to integrate or am I just missing something small?
Any help or advice would be appreciated to push me in the right direction. Thanks!
The problem in the code is in the bottom of the section labelled "Power Calculations". My integral also gets quite messy if that makes a difference.
%%%%%%%%%%% Parameters %%%%%%%%%%%%
n0 = 1; %air
n1 = 1.4; %layer 1
n2 = 2.62; %layer 2
n3 = 3.5; %silicon
L0 = 650*10^(-9); %centre wavelength
L1 = 200*10^(-9): 10*10^(-9): 2200*10^(-9); %lambda from 200nm to 2200nm
x = ((pi./2).*(L0./L1)); %layer phase thickness
r01 = ((n0 - n1)./(n0 + n1)); %reflection coefficient 01
r12 = ((n1 - n2)./(n1 + n2)); %reflection coefficient 12
r23 = ((n2 - n3)./(n2 + n3)); %reflection coefficient 23
t01 = ((2.*n0)./(n0 + n1)); %transmission coefficient 01
t12 = ((2.*n1)./(n1 + n2)); %transmission coefficient 12
t23 = ((2.*n2)./(n2 + n3)); %transmission coefficient 23
Q1 = [1 r01; r01 1]; %Matrix Q1
Q2 = [1 r12; r12 1]; %Matrix Q2
Q3 = [1 r23; r23 1]; %Matrix Q3
%%%%%%%%%%%% Graph of L vs R %%%%%%%%%%%
R = zeros(size(x));
for i = 1:length(x)
P = [exp(j.*x(i)) 0; 0 exp(-j.*x(i))]; %General Matrix P
T = ((1./(t01.*t12.*t23)).*(Q1*P*Q2*P*Q3)); %Transmission
T11 = T(1,1); %T11 value
T21 = T(2,1); %T21 value
R(i) = ((abs(T21./T11))^2).*100; %Percent reflectivity
end
plot(L1,R)
title('Percent Reflectance vs. wavelength for 2 Layers')
xlabel('Wavelength (m)')
ylabel('Reflectance (%)')
%%%%%%%%%%% Power Calculation %%%%%%%%%%
syms L; %General lamda
y = ((pi./2).*(L0./L)); %Layer phase thickness with variable Lamda
P1 = [exp(j.*y) 0; 0 exp(-j.*y)]; %Matrix P with variable Lambda
T1 = ((1./(t01.*t12.*t23)).*(Q1*P1*Q2*P1*Q3)); %Transmittivity matrix T1
I = ((6.16^(15))./((L.^(5)).*exp(2484./L) - 1)); %Blackbody Irradiance
Tf11 = T1(1,1); %New T11 section of matrix with variable Lambda
Tf2 = (((abs(1./Tf11))^2).*(n3./n0)); %final transmittivity
P1 = Tf2.*I; %Power before integration
L_initial = 200*10^(-9); %Initial wavelength
L_final = 2200*10^(-9); %Final wavelength
P2 = int(P1, L, L_initial, L_final) %Power production
I've refactored your code
to make it easier to read
to improve code reuse
to improve performance
to make it easier to understand
Why do you use so many unnecessary parentheses?!
Anyway, there's a few problems I saw in your code.
You used i as a loop variable, and j as the imaginary unit. It was OK for this one instance, but just barely so. In the future it's better to use 1i or 1j for the imaginary unit, and/or m or ii or something other than i or j as the loop index variable. You're helping yourself and your colleagues; it's just less confusing that way.
Towards the end, you used the variable name P1 twice in a row, and in two different ways. Although it works here, it's confusing! Took me a while to unravel why a matrix-producing function was producing scalars instead...
But by far the biggest problem in your code is the numerical problems with the blackbody irradiance computation. The term
L⁵ · exp(2484/L) - 1
for λ₀ = 200 · 10⁻⁹ m will require computing the quantity
exp(1.242 · 10¹⁰)
which, needless to say, is rather difficult for a computer :) Actually, the problem with your computation is two-fold. First, the exponentiation is definitely out of range of 64 bit IEEE-754 double precision, and will therefore result in ∞. Second, the parentheses are wrong; Planck's law should read
C/L⁵ · 1/(exp(D) - 1)
with C and D the constants (involving Planck's constant, speed of light, and Boltzmann constant), which you've presumably precomputed (I didn't check the values. I do know choice of units can mess these up, so better check).
So, aside from the silly parentheses error, I suspect the main problem is that you simply forgot to rescale λ to nm. Changing everything in the blackbody equation to nm and correcting those parentheses gives the code
I = 6.16^(15) / ( (L*1e+9)^5 * (exp(2484/(L*1e+9)) - 1) );
With this, I got a finite value for the integral of
P2 = 1.052916498836486e-010
But, again, you'd better double-check everything.
Note that I used quadgk(), because it's one of the better ones available on R2010a (which I'm stuck with), but you can just as easily replace this with integral() available on anything newer than R2012a.
Here's the code I ended up with:
function pwr = my_fcn()
% Parameters
n0 = 1; % air
n1 = 1.4; % layer 1
n2 = 2.62; % layer 2
n3 = 3.5; % silicon
L0 = 650e-9; % centre wavelength
% Reflection coefficients
r01 = (n0 - n1)/(n0 + n1);
r12 = (n1 - n2)/(n1 + n2);
r23 = (n2 - n3)/(n2 + n3);
% Transmission coefficients
t01 = (2*n0) / (n0 + n1);
t12 = (2*n1) / (n1 + n2);
t23 = (2*n2) / (n2 + n3);
% Quality factors
Q1 = [1 r01; r01 1];
Q2 = [1 r12; r12 1];
Q3 = [1 r23; r23 1];
% Initial & Final wavelengths
L_initial = 200e-9;
L_final = 2200e-9;
% plot reflectivity for selected lambda range
plot_reflectivity(L_initial, L_final, 1000);
% Compute power production
pwr = quadgk(#power_production, L_initial, L_final);
% Helper functions
% ========================================
% Graph of lambda vs reflectivity
function plot_reflectivity(L_initial, L_final, N)
L = linspace(L_initial, L_final, N);
R = zeros(size(L));
for ii = 1:numel(L)
% Transmission
T = transmittivity(L(ii));
% Percent reflectivity
R(ii) = 100 * abs(T(2,1)/T(1,1))^2 ;
end
plot(L, R)
title('Percent Reflectance vs. wavelength for 2 Layers')
xlabel('Wavelength (m)')
ylabel('Reflectance (%)')
end
% Compute transmittivity matrix for a single wavelength
function T = transmittivity(L)
% Layer phase thickness with variable Lamda
y = pi/2 * L0/L;
% Matrix P with variable Lambda
P1 = [exp(+1j*y) 0
0 exp(-1j*y)];
% Transmittivity matrix T1
T = 1/(t01*t12*t23) * Q1*P1*Q2*P1*Q3;
end
% Power for a specific wavelength. Note that this function
% accepts vector-valued wavelengths; needed for quadgk()
function pwr = power_production(L)
pwr = zeros(size(L));
for ii = 1:numel(L)
% Transmittivity matrix
T1 = transmittivity(L(ii));
% Blackbody Irradiance
I = 6.16^(15) / ( (L(ii)*1e+9)^5 * (exp(2484/(L(ii)*1e+9)) - 1) );
% final transmittivity
Tf2 = abs(1/T1(1))^2 * n3/n0;
% Power before integration
pwr(ii) = Tf2 * I;
end
end
end
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)]);
An improvement over #gnovice's answer is to skip the division operation: rem alone is enough:
n = 60;
find(rem(n, 1:n)==0)