Algorithm optimization: do not use for loop

3 views (last 30 days)
Hi, I have the following code which I'd like to optimize:
% Creation of all scenarios
AoA__ = [0 1 2 3];
Flight_Phase__ = [0 1];
AoA___ = allcomb(AoA__,AoA__,AoA__,Flight_Phase__)
s = size(AoA___);
n = s(1);
m = s(2);
% Calculation of sum and norm of each row
somme_ligne = sum(AoA___')';
norme_ligne = sqrt(sum(AoA___'.*AoA___'))';
somme_norme = [somme_ligne,norme_ligne];
unicite(1) = 1;
% Finding the redudancies
for i = 2:n
if ismember(somme_norme(i,:),somme_norme(1:i-1,:),'rows')
unicite(i,1) = 0;
else
unicite(i,1) = 1;
end
end
nbr_scenarios = sum(unicite);
% Saving the corresponding indices
for i = 1:n
if unicite(i,1) == 1
indices(i,1) = i;
end
end
index = indices(indices~=0);
AoA_without_order = AoA___(index,:)
Let me explain the interest. I am creating cell vectors AoA and Flight Phase. Then, I encode them in underlying values (Simulink objective here). Then we have two vectors: AoA = [0 1 2 3] and Flight Phase = [0 1];
I create all combinations with order and repetitions with 'allcomb' function. But I only need combinations with répétitions and without order. For that, I compute the sum and norm of my allcomb matrix and gather them in one matrix 'somme_norme' with 'somme_norme(:,1) = sum of each row' and 'somme_norme(:,2) = norm of each row'. If two lines have the same norm and sum, normally, the combination is redundant, then unicite = 0. Else, unicite = 1. Afterwards, I extract the rows number of the first apparition of one combination and run it in my allcomb matrix.
With this example, my all combination matrix is 124*4. Without considering the order, we must have only 40 combinations (formula used: C(n+p-1,p)).
I would like to find an algorithm which does not use a for loop. With that value of n, the total runtim is more than 256h, which is too much. Any ideas for that? I am not used to vectorize my algorithm, so bad knowledge of Matlab functions.
Thanks
  4 Comments
Jan
Jan on 24 Jul 2018
How many columns does your input have? Which type is it?
Antoine Guilleux
Antoine Guilleux on 24 Jul 2018
The input 'somme_norme' has two columns of type double.

Sign in to comment.

Accepted Answer

Walter Roberson
Walter Roberson on 24 Jul 2018
[~, ia] = unique(somme_norme, 'rows');
unicite = zeros(size(somme_norme,1),1);
unicite(ia) = 1;
  6 Comments
Walter Roberson
Walter Roberson on 24 Jul 2018
Different approach than what you describe:
Generate your data. sort it along the second dimension: since you do not care what order it is in, you can leave it sorted. Now you can use unique() like I showed, without bothering with the sum or norm.
Antoine Guilleux
Antoine Guilleux on 25 Jul 2018
Edited: Antoine Guilleux on 25 Jul 2018
Yes that's exactly what I found out yesterday and it works well. However, the problem was coming from the inputs: you cannot do this with my short example:
allcomb([0 1 2 3],[0 1 2 3],[0 1 2 3],[0 1])
Here physically the first three vectors represent one parameter three times, and the last vector another one. I cannot mix them if I want to have all combinations without order. I think there's no other way than use it on the first parameter and then build the matrix "with your hands":
A = sort(allcomb([0 1 2 3],[0 1 2 3],[0 1 2 3]),2)
[~, ia] = unique(A, 'rows');
A_without_order = A(ia,:)
m = size(A_without_order,1)
% Building final matrix
Combinations(1:m,:) = A_without_order(:,:);
Combinations(m+1:2*m,:) = A_without_order(:,:);
Combinations(1:m,4) = 0;
Combinations(m+1:2*m,4) = 1;
This is may be not pretty, but quite efficient and without for loops, and I have my 40 combinations!

Sign in to comment.

More Answers (1)

Jan
Jan on 24 Jul 2018
Edited: Jan on 24 Jul 2018
Creating the temporary arrays somme_norme(1:i-1,:) is cruel! Remember how much work this is for n=16384000. If I guess, that somme_norme has 10 columns and it is a double array, Matlab requests this number of bytes from the OS to create these arrays:
sum(1:16384000) * 10 * 8 Byte =
10737418895360000 Byte =
10'737 TByte
Of course this will take a long time, even without considering the work in ismember.
The same problem occurs for letting the output unicite grow iteratively: this requests sum(1:n)*8 bytes from the OS, because in each iteration a new vector is created and the old data are copied. Pre-allocation is the simple solution: Create the output before the loop with its final size.
It would be much more efficient to operate on columns instead of rows, because the element neighboring in columns are store side by side in the memory also.
S = somme_norme.';
unicite = ones(1, n); % Pre-allocate!!!
for i = 2:n
Si = S(:, i);
for k = 1:i-1
if all(Si == S(:, k))
unicite(i) = 0; % Stop the inner loop when the first match is found
break;
end
end
end
This is a brute-force attack. For a unique data set, the inner loop runs again sum(1:16384000) times.
There are smarter solutions: sortrows can sort the array and then you have to check the neighboring rows only. But this is exactly what Walter's suggestion does in unique('rows'). So please post a small example to demonstrate, what you want and what Walter's code does instead.
  2 Comments
Antoine Guilleux
Antoine Guilleux on 24 Jul 2018
Edited: Antoine Guilleux on 24 Jul 2018
Thanks for that. I obtain the same number of combinations than with Walter's, so definitely the problem is coming from somewhere else, my bad. I will put my whole code in the first post explaining what I want it to do. Thanks!
Antoine Guilleux
Antoine Guilleux on 24 Jul 2018
Edited: Antoine Guilleux on 24 Jul 2018
EDIT: I realized that computing the sum and norm of each row is not a good idea. Another example: [1 1 4] and [0 3 3] have the same sum = 6 and norm = sqrt(18). But there's no link between those combinations -> reason why I cannot find the right answer even with your codes... ><"

Sign in to comment.

Categories

Find more on Mathematics in Help Center and File Exchange

Products


Release

R2017b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!