1

I have the following nested for loop program and it is taking forever to run. Is there a better way (more efficient) way to code this?

tic 
ndx = 0;
for i1 = 1 : 49 - 5
    for i2 = i1 + 1 : 49 - 4
        for i3 = i2 + 1 : 49 - 3
            for i4 = i3 + 1 : 49 - 2
                for i5 = i4 + 1 : 49 - 1
                    for i6 = i5 + 1 : 49 - 0
                        ndx = ndx +1;
                         number(ndx,1) = i1;
                         number(ndx,2) = i2;
                         number(ndx,3) = i3;
                         number(ndx,4) = i4;
                         number(ndx,5) = i5;
                         number(ndx,6) = i6;
                       %  display([int2str(number(ndx,1)), ' ',  int2str(number(ndx,2)), ...
                        %     ' ',  int2str(number(ndx,3)), ' ',  int2str(number(ndx,4)),...
                        %    ' ',  int2str(number(ndx,5)), ' ',  int2str(number(ndx,6)) ])
                    end
                end
            end
        end
    end
end
toc
Dan D.
  • 73,243
  • 15
  • 104
  • 123
user804903
  • 13
  • 3
  • 2
    Sure there is. You can preallocate `numbers` like this: `n=49;number=zeros((n-5)*(n-4)*(n-3)*(n-2)*(n-1)*n/(1*2*3*4*5*6),6);` (see also [this question](http://stackoverflow.com/questions/2151636/matlab-consider-preallocating-for-speed)). This will give you orders of magnitude faster evaluation. To actually efficiently achieve what you want, it would have been more helpful to tell what you want, instead of letting us guess and tell only what you do to achieve it. – arne.b Jun 18 '11 at 22:53
  • @user804903: oh my! that's quite a messy set of loops you got there... it might help if you explained in words (math would be even better) what you're trying to do. I don't think anyone here has the time to wade through the loops and follow the logic. – abcd Jun 18 '11 at 22:55
  • @user804903: Just following @arne.b's suggestion to preallocate should give you a damn quick answer, considering you're only making assignments and no other calculations. – abcd Jun 18 '11 at 22:59
  • 3
    @arne.b: you could even reduce space requirement eight times if you use unsigned integers: `number=zeros(13983816,6,'uint8');` – Amro Jun 18 '11 at 22:59
  • 2
    @yoda: What (s)he wants is `nchoosek(1:n,6)` (which, according to its documentation, "is only practical for situations where N is less than about 15") for n=49. (I figured this out running the code for sufficiently small n, not reading it). – arne.b Jun 18 '11 at 23:11
  • 3
    @arne.b: The loop seems to be reasonably fast once the matrix is pre-allocated. The entire thing took just a little over a second. You should post it as an answer. – abcd Jun 18 '11 at 23:16
  • @yoda: The last time I answered a "how to speed up nested for loops" question, my answer was accepted initially, then gnovice dropped by and gave a much better answer using a built-in function that I did not yet know about. Thus, I decided to wait and see first here ;-). – arne.b Jun 19 '11 at 07:49

1 Answers1

4

This answer is a digest of the above comments, as suggested:

To speed up nested for loops in MATLAB, the first things to consider include the following: Is an array/matrix growing inside a loop? Can the whole calculation be vectorized, possibly using an existing MATLAB function?

The answer to the first one is yes, number grows one line at a time in the innermost loop. The answer to the second becomes irrelevant in this case once the first problem has been solved.

If you run your above code with only the assignment of ndx, not number, in the innermost loop, you will find out that the total number of lines you will get is 13983816, and that this is calculated very quickly. To prevent number from growing inside the loop, which requires rather costly memory operations, you could precede your code with: number = zeros(13983816,6); This would create a double matrix of the size of the matrix you will get anyway, but with only 0 entries. Since you know all your entries will be from 1 to 49, the data type need not be double, uint8 suffices (for values from 0 to 255; HT:Amro).

So the first line of your code should be:

number = zeros(13983816,6,'uint8');

Then the execution of the whole code will take only seconds.

Note: As for the second question above, there is indeed an existing MATLAB function to do what the above code does, i.e. finding all combinations to choose 6 numbers out of 49 without repetition and without regards to order (German lottery?), but does it less efficiently and is thus only applicable to n much smaller than 49: nchoosek, which for two scalar inputs calculates the number of elements (i.e. nchoosek(49,6)==13983816) and for a vector (here: 1:49) and a scalar gives a matrix with the actual combinations.

arne.b
  • 4,212
  • 2
  • 25
  • 44