Algorithm optimization: do not use for loop
3 次查看(过去 30 天)
显示 更早的评论
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 个评论
采纳的回答
Walter Roberson
2018-7-24
[~, ia] = unique(somme_norme, 'rows');
unicite = zeros(size(somme_norme,1),1);
unicite(ia) = 1;
6 个评论
Walter Roberson
2018-7-24
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.
更多回答(1 个)
Jan
2018-7-24
编辑:Jan
2018-7-24
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 个评论
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!