I have a need to create a list of combinations of numbers. The numbers are quite small so I can use byte
rather than int
. However it requires many nested loops in order to get every possible combination. I'm wondering if there's a more efficient manner to do what I'm after. Code so far is:
var data = new List<byte[]>(); for (byte a = 0; a < 2; a++) for (byte b = 0; b < 3; b++) for (byte c = 0; c < 4; c++) for (byte d = 0; d < 3; d++) for (byte e = 0; e < 4; e++) for (byte f = 0; f < 3; f++) for (byte g = 0; g < 3; g++) for (byte h = 0; h < 4; h++) for (byte i = 0; i < 2; i++) for (byte j = 0; j < 4; j++) for (byte k = 0; k < 4; k++) for (byte l = 0; l < 3; l++) for (byte m = 0; m < 4; m++) { data.Add(new [] {a, b, c, d, e, f, g, h, i, j, k, l, m}); }
I was considering using something like a BitArray
but I'm not sure how I could incorporate it.
Any recommendations would be greatly appreciated. Alternatively, perhaps this is the fastest way of doing what I want?
EDIT Couple of quick points (and apologies I didn't put these in the original post):
byte
is faster than using int
, I guarantee it. It's also a lot better on memory usage to have 67m+ arrays of bytes rather than intsConcurrentBag
) - however I'm happy to be proved wrong :)CONCLUSION
Caramiriel has provided a good micro-optimisation which shaves some time off the loops, so I've marked that answer as correct. Eric also mentioned that it is faster to pre-allocate the List. But, at this stage it seems that the nested loops are in fact the fastest possible way of doing this (depressing, I know!).
If you want to try exactly what I was trying to benchmark with StopWatch
, go with 13 loops counting up to 4 in each loop - that makes about 67m+ lines in the list. On my machine (i5-3320M 2.6GHz) it takes about 2.2s to do the optimised version.
We can see that in the case of nested loops, list comprehensions are faster than the ordinary for loops, which are faster than while.
You can just simply use normal functions like this. Of course you CAN use recursion as others have mentioned, but there's no need of using recursion unless there's need for it. Moreover, using recursion in place of nested looping would become quite complex and you would have to think about it.
Originally Answered: How can I avoid nested "for loop" for optimize my code? Sort the array first. Then run once over it and count consecutive elements. For each count larger than 1, compute count-choose-2 and sum them up.
Nested loops can get difficult to understand relatively quickly, though some nesting of loops is fine - providing, as others point out, that doesn't mean you're creating a performance issue by using an extremely (and unnecessarily) slow algorithm.
As a reminder: you probably do not need this kind of code while developing your own solution. This can and should only used in very specific situations. Readability is often more important than speed.
You can use the properties of a struct and allocate the structure in advance. I cut off some levels in the sample below, but I'm sure you'll be able to figure out the specifics. Runs about 5-6 times faster than the original (release mode).
The block:
struct ByteBlock { public byte A; public byte B; public byte C; public byte D; public byte E; }
The loop:
var data = new ByteBlock[2*3*4*3*4]; var counter = 0; var bytes = new ByteBlock(); for (byte a = 0; a < 2; a++) { bytes.A = a; for (byte b = 0; b < 3; b++) { bytes.B = b; for (byte c = 0; c < 4; c++) { bytes.C = c; for (byte d = 0; d < 3; d++) { bytes.D = d; for (byte e = 0; e < 4; e++) { bytes.E = e; data[counter++] = bytes; } } } } }
It's faster because it doesn't allocate a new list every time you add it to the list. Also since it's creating this list, it needs a reference to every other value (a,b,c,d,e). You can assume each value is only modified once inside the loop, so we can optimize it to do so (data locality).
Also read the comments for side-effects.
Edited the answer to use an T[]
instead of a List<T>
.
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