I have a question regarding this SO post: Understanding How Many Times Nested Loops Will Run
In there the general formula for 3 nested for loops is: n(n+1)(n+2)/3. I don't really know why the 2nd inner loop runs n+1 times while the outer loop runs n times (wouldn't the inner loop run once more before it exits out of the for loop? Either way...the general formula is
n(n+1)(n+2)...(n+r-1)
---------------------
r!
Here, r is the number of nested loops.
I am wondering if this formula is always the same for nested loops or if it changes based on what the comparison is inside the for loops...If it is based on comparison then how can I determine the formula if on an exam I am given some different for loops? How can I generate or come up with this formula if the comparison for the for loops is not the same as the comparison in the SO post which creates that formula?
You'll have to train your mind to recognize and follow the patterns in execution, and come up with a formula for specific situations. The general rule of thumb is that if one for loop will run the code inside of it x times, and it has a loop inside of it that will run y times, then the code inside the inner loop will run x*y times.
The most common type of for loop starts at zero and increments by 1 until it reaches a certain number, like so:
for(int i = 0; i < x; i++)
for(int j = 0; j < y; j++)
for(int k = 0; k < z; k++)
// code here runs x * y * z times
To answer your question, if you change any part of any of the for loops, it will change the number of times the inner code is executed. You need to identify how many times that will be by thinking about the logical code execution.
for(int i = 1; i < x; i++)
for(int j = 0; j < y * 2; j++)
for(int k = 0; k < z; k += 2)
// code here runs (x - 1) * (y * 2) * (z / 2) times
In the above example, each for loop is tweaked in a different way. Notice how the overall formula for the number of times run stays pretty much the same, but now each loop is running a different number of times each time it gets hit.
Things become more complicated when the loops' variables affect more than one loop.
for(int i = 0; i < x; i++)
for(int j = i; j < y; j++) // notice how `j` starts as `i`
// Code here runs `y` times the first time through the outer loop,
// then `y - 1` times,
// then `y - 2` times,
// ...
// if x < y, the pattern continues until the xth time,
// when this loop runs `y - x` times.
// if x > y, the pattern will stop when y == x, and
// code here will run 0 times for the remainder of
// the loops.
So in this last example, assuming x < y, the loop will run y + (y-1) + (y-2) ... + (y-x) times.
It changes based on the inner values.
Example.
for (int i = 0; i < 100; i++)
{
//this loop will run 100 times.
for (int i1 = 0; i1 < 100; i++)
{
// This will run 100 times every outer loop int.
// This means that for each index i in the outer loop this will run 100 times.
// The outer loop runs 100 time and this runs 10,000 times in total.
}
}
for (int i = 0; i < 100; i++)
{
//this loop will run 100 times.
for (int i1 = 0; i1 < 10; i++)
{
// This will run 10 times every outer loop int.
// This means that for each index i in the outer loop this will run 10 times.
// The outer loop runs 100 time and this runs 1,000 times in total.
}
}
An easier way to look at it may be this.
for (int i = 0; i < 10; i++)
{
//this loop will run 10 times.
Console.WriteLine("int i = " + i.ToString()");
for (int i1 = 0; i1 < 10; i++)
{
// This will run 10 times every outer loop int.
// This means that for each index i in the outer loop this will run 10 times.
// The outer loop runs 10 time and this runs 100 times.
Console.WriteLine("int i2 = " + i2.ToString()");
}
}
That will output this.
int i = 0
int i2 = 0
int i2 = 1
int i2 = 2
int i2 = 3
int i2 = 4
int i2 = 5
int i2 = 6
int i2 = 7
int i2 = 8
int i2 = 9
int i = 1
int i2 = 0
int i2 = 1
int i2 = 2
int i2 = 3
int i2 = 4
int i2 = 5
int i2 = 6
int i2 = 7
int i2 = 8
int i2 = 9
and so on.
The formula is based on the inner loop numbers. (On when the loop ends.)
Related
I have this for loop. TicketList starts with 109 tickets. nColumns = 100. I calculate the number of rows I will need depending on the number of tickets. So in this case I need 2 rows. Row one will be full and row two will only have 9 entries. I have the loop below. It only runs one time for the NumOfRows and fills the first 100 and never loops.
What am I missing?
for (int j = 0; j < NumOfRows; j++)
{
for (int i = 0; i < nColumns; i++)
{
if (TicketList.Count() > 0)
{
t = rand.Next(0, TicketList.Count() - 1);
numbers[i, j] = TicketList[t];
TicketList.Remove(TicketList[t]);
}
}
}
Try changing your code to use a more LINQ-like, functional approach. If might make the logic easier. Something like this:
TicketList
.OrderBy(x => rand.Next())
.Select((ticket, n) => new
{
ticket,
j = n / NumOfRows,
i = n % NumOfRows
})
.ToList()
.ForEach(x =>
{
numbers[x.i, x.j] = x.ticket;
});
You may need to flip around x.i & x.j or use nColumns instead of NumOfRows - I wasn't sure what your logic was looking for - but this code might work better.
Other than a few poor choices, your loops appear to be fine. I would venture that NumOfRows is not being calculated correctly.
The expression NumOfRows = (TotalTickets + (Columns - 1)) / Columns; should calculate the correct number of rows.
Also, you should use the property version of Count rather than the Linq extension method and use IList<T>.RemoveAt() or List<T>.RemoveAt rather than Remove(TicketList[T]).
Using Remove() requires that the list be enumerated to locate the element to remove, which may not be the same index that you are targeting. Not to mention that you will scan 50% (on average) of the list for each Remove call, when you already know the correct index to remove.
The functional approach listed earlier seems like overkill.
I've attempted to replicate your issue, assuming certain facts about the various variables in use. The loop repeats the expected number of times.
static void TestMe ()
{
List<object> TicketList = new List<object>();
for (int index = 0; index < 109; index++)
TicketList.Add(new object());
var rand = new Random();
int nColumns = 100;
int NumOfRows = (TicketList.Count + (nColumns - 1)) / nColumns;
object[,] numbers;
int t;
numbers = new object[nColumns, NumOfRows];
for (int j = 0; j < NumOfRows; j++)
{
Console.WriteLine("OuterLoop");
for (int i = 0; i < nColumns; i++)
{
if (TicketList.Count > 0)
{
t = rand.Next(0, TicketList.Count - 1);
numbers[i, j] = TicketList[t];
TicketList.RemoveAt(t);
}
}
}
}
The problem that you are seeing must be the result of something that you have not included in your sample.
Friends
I have got an error which tells "Index was outside the bounds of the array" I dont know y it was happening since after completeting the for loop and entering the loop fresh again it was showing the variable value when exited from the loop before.
int[,] arrScr = new int[lstTest.Count, cnt2 + 3];
string[,] arrName = new string[lstTest.Count, cnt2 + 3];
int p;
for (i = 0; i < lstTest.Count; i++)
{
using (DataTableReader dtr3 = ds.Tables["scord_mark_table" + (i + 1).ToString()].CreateDataReader())
{
p = 0;
while (dtr3.Read())
{
arrName[i, 2 + p] = dtr3[15].ToString();
for (int k = 2; k < 12; k++)
{
arrScr[i, 2 + p] += Convert.ToInt32(dtr3[k].ToString());
}
p++;
}
}
What is in dtr3[12]? does it return null?
What is the value of cnt2?
Change k <= 12 to k < 12.
If this is not the cause of your problem you should rewrite k <= 12 to k < 13 as that is the convention most coders are used to read and write.
This means that your array does not contain as many elements as you are trying to access.
It's going to exit from the loop as k = 13 because the last valid value of k is 12, then it went through the loop, executed k++ (making it 13). At which point it fails the condition because 13 > 12 and that's when it actually exits.
Will this loop execute exactly N Times?
for (int i = 0; i < N; i++)
{
//statement
someMethodCall();
}
Will this loop execute at most N Times?
for (int i = 1; i < N; i++)
{
someMethodCall();
}
Will this loop execute at least N Times?
for (int i = 0; i <= N; i++)
{
//statement
someMethodCall();
}
What should I do if I need to execute the statement between m and n times, e.g. calling a method?
The answers to your three questions are yes, no, and yes, I suppose, although that third answer is a bit deceptive; it'll execute N times, no more and no less (unless there's an exception which terminates the loop abnormally.) You can write a for loop to loop a definite number of times, or until some condition becomes true, and then you have the ability to use break or return to terminate the loop early.
But there's no notion of executing "at least N times;" it's simply not part of this -- or any other -- computer language.
A loop formula can be calculated as
Math.Round (condition - initialization )/increment
In first case its
(N-0)/1 which evaluates to N times
In second case its
(N-1)/1 which evaluates to N-1 times
In third case its
(N-0+1)/1 which evaluates to N+1 times
How would I do if I need to execute statement between m and n times? For instance I want to call one method between m and n times?
then check it in condition
for (int i = m; i < n; i++)
{
someMethodCall();
}
In for loop you can have these code,
N Time
for (int i = 0; i < N; i++) {
}
N+1 Time
for (int i = 0; i <= N; i++) {
}
And if there isn't any time and you want handle it your self you can use this,
for (;;) {
//do something and don't forget use break or return !
}
OR
while(x>10){
}
and loop in loop is good as Ernest Friedman-Hill said
for (int i = 0; i <=10; i++) {
for (int i = 0; i < length; i++) {
}
}
use 2xTab for visual studio help.
look at these 2 loops
const int arrayLength = ...
Version 0
public void RunTestFrom0()
{
int sum = 0;
for (int i = 0; i < arrayLength; i++)
for (int j = 0; j < arrayLength; j++)
for (int k = 0; k < arrayLength; k++)
for (int l = 0; l < arrayLength; l++)
for (int m = 0; m < arrayLength; m++)
{
sum += myArray[i][j][k][l][m];
}
}
Version 1
public void RunTestFrom1()
{
int sum = 0;
for (int i = 1; i < arrayLength; i++)
for (int j = 1; j < arrayLength; j++)
for (int k = 1; k < arrayLength; k++)
for (int l = 1; l < arrayLength; l++)
for (int m = 1; m < arrayLength; m++)
{
sum += myArray[i][j][k][l][m];
}
}
Version 2
public void RunTestFrom2()
{
int sum = 0;
for (int i = 2; i < arrayLength; i++)
for (int j = 2; j < arrayLength; j++)
for (int k = 2; k < arrayLength; k++)
for (int l = 2; l < arrayLength; l++)
for (int m = 2; m < arrayLength; m++)
{
sum += myArray[i][j][k][l][m];
}
}
Results for arrayLength=50 are (average from multiple sampling compiled X64):
Version 0: 0.998s (Standard error of the mean 0.001s) total loops: 312500000
Version 1: 1.449s (Standard error of the mean 0.000s) total loops: 282475249
Version 2: 0.774s (Standard error of the mean 0.006s) total loops: 254803968
Version 3: 1.183s (Standard error of the mean 0.001s) total loops: 229345007
if we make arrayLength=45 then
Version 0: 0.495s (Standard error of the mean 0.003s) total loops: 184528125
Version 1: 0.527s (Standard error of the mean 0.001s) total loops: 164916224
Version 2: 0.752s (Standard error of the mean 0.001s) total loops: 147008443
Version 3: 0.356s (Standard error of the mean 0.000s) total loops: 130691232
why:
loop start from 0 is faster than loop start from 1 though more loops
why loop start from 2 behaves weird?
Update:
I did each run 10 times, (that's where standard error of the mean comes from)
I also switched the order of version tests a couple of time. No big difference.
The length of myArray of each dimension = arrayLength, I initialized it in the beginning and the time taken is excluded. The value is 1. So sum gives the total loops.
The complied version is Released mode, and I run it from Outside VS. (Closed VS)
Update2:
Now I discard myArray completely, sum++ instead, and added GC.Collect()
public void RunTestConstStartConstEnd()
{
int sum = 0;
for (int i = constStart; i < constEnd; i++)
for (int j = constStart; j < constEnd; j++)
for (int k = constStart; k < constEnd; k++)
for (int l = constStart; l < constEnd; l++)
for (int m = constStart; m < constEnd; m++)
{
sum++;
}
}
Update
This appears to me to be a result of an unsuccessful attempt at optimization by the jitter, not the compiler. In short, if the jitter can determine the lower bound is a constant it will do something different which turns out to actually be slower. The basis for my conclusions takes some proving, so bear with me. Or go read something else if you're not interested!
I concluded this after testing four different ways to set the lower bound of the loop:
Hard coded in each level, as in colinfang's question
Use a local variable, assigned dynamically through a command line argument
Use a local variable but assign it a constant value
Use a local variable and assign it a constant value, but first pass the value through a goofy sausage-grinding identity function. This confuses the jitter enough to prevent it from applying its constant-value "optimization".
The compiled intermediate language for all four versions of the looping section is almost identical. The only difference is that in version 1 the lower bound is loaded with the command ldc.i4.#, where # is 0, 1, 2, or 3. That stands for load constant. (See ldc.i4 opcode). In all other versions, the lower bound is loaded with ldloc. This is true even in case 3, where the compiler could infer that lowerBound is really a constant.
The resulting performance is not constant. Version 1 (explicit constant) is slower than version 2 (run-time argument) along similar lines as found by the OP. What is very interesting is that version 3 is also slower, with comparable times to version 1. So even though the IL treats the lower bound as a variable, the jitter appears to have figured out that the value never changes, and substitutes a constant as in version 1, with the corresponding performance reduction. In version 4 the jitter can't infer what I know -- that Confuser is actually an identity function -- and so it leaves the variable as a variable. The resulting performance is the same as the command line argument version (2).
My theory on the cause of the performance difference: The jitter is aware and makes use of the fine details of actual processor architecture. When it decides to use a constant other than 0, it has to actually go fetch that literal value from some storage which is not in the L2 cache. When it is fetching a frequently used local variable it instead reads its value from the L2 cache, which is insanely fast. Normally it doesn't make sense to be taking up room in the precious cache with something as dumb as a known literal integer value. In this case we care more about read time than storage, though, so it has an undesired impact on performance.
Here is the full code for the version 2 (command line arg):
class Program {
static void Main(string[] args) {
List<double> testResults = new List<double>();
Stopwatch sw = new Stopwatch();
int upperBound = int.Parse(args[0]) + 1;
int tests = int.Parse(args[1]);
int lowerBound = int.Parse(args[2]); // THIS LINE CHANGES
int sum = 0;
for (int iTest = 0; iTest < tests; iTest++) {
sum = 0;
GC.Collect();
sw.Reset();
sw.Start();
for (int lvl1 = lowerBound; lvl1 < upperBound; lvl1++)
for (int lvl2 = lowerBound; lvl2 < upperBound; lvl2++)
for (int lvl3 = lowerBound; lvl3 < upperBound; lvl3++)
for (int lvl4 = lowerBound; lvl4 < upperBound; lvl4++)
for (int lvl5 = lowerBound; lvl5 < upperBound; lvl5++)
sum++;
sw.Stop();
testResults.Add(sw.Elapsed.TotalMilliseconds);
}
double avg = testResults.Average();
double stdev = testResults.StdDev();
string fmt = "{0,13} {1,13} {2,13} {3,13}"; string bar = new string('-', 13);
Console.WriteLine();
Console.WriteLine(fmt, "Iterations", "Average (ms)", "Std Dev (ms)", "Per It. (ns)");
Console.WriteLine(fmt, bar, bar, bar, bar);
Console.WriteLine(fmt, sum, avg.ToString("F3"), stdev.ToString("F3"),
((avg * 1000000) / (double)sum).ToString("F3"));
}
}
public static class Ext {
public static double StdDev(this IEnumerable<double> vals) {
double result = 0;
int cnt = vals.Count();
if (cnt > 1) {
double avg = vals.Average();
double sum = vals.Sum(d => Math.Pow(d - avg, 2));
result = Math.Sqrt((sum) / (cnt - 1));
}
return result;
}
}
For version 1: same as above except remove lowerBound declaration and replace all lowerBound instances with literal 0, 1, 2, or 3 (compiled and executed separately).
For version 3: same as above except replace lowerBound declaration with
int lowerBound = 0; // or 1, 2, or 3
For version 4: same as above except replace lowerBound declaration with
int lowerBound = Ext.Confuser<int>(0); // or 1, 2, or 3
Where Confuser is:
public static T Confuser<T>(T d) {
decimal d1 = (decimal)Convert.ChangeType(d, typeof(decimal));
List<decimal> L = new List<decimal>() { d1, d1 };
decimal d2 = L.Average();
if (d1 - d2 < 0.1m) {
return (T)Convert.ChangeType(d2, typeof(T));
} else {
// This will never actually happen :)
return (T)Convert.ChangeType(0, typeof(T));
}
}
Results (50 iterations of each test, in 5 batches of 10):
1: Lower bound hard-coded in all loops:
Program Iterations Average (ms) Std Dev (ms) Per It. (ns)
-------- ------------- ------------- ------------- -------------
Looper0 345025251 267.813 1.776 0.776
Looper1 312500000 344.596 0.597 1.103
Looper2 282475249 311.951 0.803 1.104
Looper3 254803968 282.710 2.042 1.109
2: Lower bound supplied at command line:
Program Iterations Average (ms) Std Dev (ms) Per It. (ns)
-------- ------------- ------------- ------------- -------------
Looper 345025251 269.317 0.853 0.781
Looper 312500000 244.946 1.434 0.784
Looper 282475249 222.029 0.919 0.786
Looper 254803968 201.238 1.158 0.790
3: Lower bound hard-coded but copied to local variable:
Program Iterations Average (ms) Std Dev (ms) Per It. (ns)
-------- ------------- ------------- ------------- -------------
LooperX0 345025251 267.496 1.055 0.775
LooperX1 312500000 345.614 1.633 1.106
LooperX2 282475249 311.868 0.441 1.104
LooperX3 254803968 281.983 0.681 1.107
4: Lower bound hard-coded but ground through Confuser:
Program Iterations Average (ms) Std Dev (ms) Per It. (ns)
-------- ------------- ------------- ------------- -------------
LooperZ0 345025251 266.203 0.489 0.772
LooperZ1 312500000 241.689 0.571 0.774
LooperZ2 282475249 219.533 1.205 0.777
LooperZ3 254803968 198.308 0.416 0.778
That is an enourmous array. For all practical purposes you are testing how long it takes your operating system to fetch the values of each element from memory, not to compare whether j, k, etc are less than arrayLength, to increment the counters, and increment your sum. The latency to fetch those values has little to do with the runtime or jitter per se and a lot to do with whatever else happens to be running on your system as a whole and the current compression and organization of the heap.
In addition, because your array is taking up so much room and being accessed frequently it's quite possible that garbage collection is running during some of your test iterations, which would completely inflate the apparent CPU time.
Try doing your test without the array lookup -- just add 1 (sum++) and then see what happens. To be even more thorough, call GC.Collect() just before each test to avoid a collection during the loop.
I think Version 0 is faster because the compiler generates a special code without range checking in that case. See http://msdn.microsoft.com/library/ms973858.aspx (section Range Check Elimination)
Just an Idea :
Maybe there are bitshifting optimizations in the loops so it will take longer when beginning in an uneven count.
I also don't know if your processor might be an indicator for the different results
Off the top of my head, perhaps there's a compiler optimization for 0 -> length. Check your build settings (release vs debug).
Beyond that, unless it's just an issue of your computer doing other work that is influencing the benchmarks, I'm not sure. Perhaps you should alter your benchmark to run each test multiple times and average the results.
I am looping through an array of strings, such as (1/12/1992 apple truck 12/10/10 orange bicycle). The array's length will always be divisible by 3. I need to loop through the array and grab the first 3 items (I'm going to insert them into a DB) and then grab the next 3 and so on and so forth until all of them have been gone through.
//iterate the array
for (int i = 0; i < theData.Length; i++)
{
//grab 3 items at a time and do db insert, continue until all items are gone. 'theData' will always be divisible by 3.
}
Just increment i by 3 in each step:
Debug.Assert((theData.Length % 3) == 0); // 'theData' will always be divisible by 3
for (int i = 0; i < theData.Length; i += 3)
{
//grab 3 items at a time and do db insert,
// continue until all items are gone..
string item1 = theData[i+0];
string item2 = theData[i+1];
string item3 = theData[i+2];
// use the items
}
To answer some comments, it is a given that theData.Length is a multiple of 3 so there is no need to check for theData.Length-2 as an upperbound. That would only mask errors in the preconditions.
i++ is the standard use of a loop, but not the only way. Try incrementing by 3 each time:
for (int i = 0; i < theData.Length - 2; i+=3)
{
// use theData[i], theData[i+1], theData[i+2]
}
Not too difficult. Just increment the counter of the for loop by 3 each iteration and then offset the indexer to get the batch of 3 at a time:
for(int i=0; i < theData.Length; i+=3)
{
var item1 = theData[i];
var item2 = theData[i+1];
var item3 = theData[i+2];
}
If the length of the array wasn't garuanteed to be a multiple of three, you would need to check the upper bound with theData.Length - 2 instead.
Your for loop doesn't need to just add one. You can loop by three.
for(int i = 0; i < theData.Length; i+=3)
{
string value1 = theData[i];
string value2 = theData[i+1];
string value3 = theData[i+2];
}
Basically, you are just using indexes to grab the values in your array. One point to note here, I am not checking to see if you go past the end of your array. Make sure you are doing bounds checking!
This should work:
//iterate the array
for (int i = 0; i < theData.Length; i+=3)
{
//grab 3 items at a time and do db insert, continue until all items are gone. 'theData' will always be divisible by 3.
var a = theData[i];
var b = theData[i + 1];
var c = theData[i + 2];
}
I've been downvoted for this answer once. I'm pretty sure it is related to the use of theData.Length for the upperbound. The code as is works fine because array is guaranteed to be a multiple of three as the question states. If this guarantee wasn't in place, you would need to check the upper bound with theData.Length - 2 instead.
Here is a more general solution:
int increment = 3;
for(int i = 0; i < theData.Length; i += increment)
{
for(int j = 0; j < increment; j++)
{
if(i+j < theData.Length) {
//theData[i + j] for the current index
}
}
}
string[] friends = new string[4];
friends[0]= "ali";
friends[1]= "Mike";
friends[2]= "jan";
friends[3]= "hamid";
for (int i = 0; i < friends.Length; i++)
{
Console.WriteLine(friends[i]);
}Console.ReadLine();