How many primitive operations in a simple loop? - algorithm

I have a bunch of code to find the primitive operations for. The thing is that there aren't really many detailed resources out on the web on the subject. In this loop:
for i:=0 to n do
print test
end
How many steps do we really have? In my first guess I would say n+1 considering n for the times looping and 1 for the print. Then I thought that maybe I am not precise enough. Isn't there an operation even to add 1 to i in every loop?
In that matter we have n+n+1=2n+1. Is that correct?

The loop can be broken down into its "primitive operations" by re-casting it as a while:
int i = 0;
while (i < n)
{
print test;
i = i + 1;
}
Or, more explicitly:
loop:
if (i < n) goto done
print test
i = i + 1
goto loop
done:
You can then see that for each iteration, there is a comparison, an increment, and a goto. That's just the loop overhead. You'd have to add to that whatever work is done in the loop. If the print is considered a "primitive operation," then you have:
n+1 comparisons
n calls to print
n increments
n+1 goto instructions (one to branch out of the loop when done)
Now, how that all gets converted to machine code is highly dependent on the compiler, the runtime library, the operating system, and the target hardware. And perhaps other things.

This question may be from a decade ago however since the core theme of the question is of algorithm related that is important even over such period of time I feel it is critical knowing it.
In Neutral Algorithm language let's look into the below example:
sum ← 0
for i ← 0 to n-1 do
  sum ← sum + 1
so, we already know primitive operations happens to exist when basic operations are computed by an algorithm.
Mainly when:
  A. When arithmetic operations are performed (e.g +, -, *, etc)
  B. When comparing two operands,
  C. When assigning a value to variable,
  D. When indexing an array with a value,
  E. When calling a method,
  F. When returning from a method and,
  G. When following object reference.
so, when justify the above scenario with ultimate primitive operations we find that:
  I. ONE basic operation when assigning to sum.
  II. n+1 comparisons in the simple for loop (mind you have compared n times from 0 to n-1 and the other 1 comparison is which failed checking i < m. so total n+1 comparisons.
  III. The third line sum has two primitive operations; 1 assigning to sum and another 1 performing arithmetic operations. which is since it is checked n times in the simple for loop it becomes 2*n= 2n;
  IV. Last one but that is shadowed by the pseudocode is the increment which can be explicitly represented as i ← i+1 which has two primitive operation that is gone n times 2*n= 2n;
Overall the above example has a total of 1 + 1 + n + 2n + 2n = 5n+2 primitive operations

Related

How is this nested loop algorithm analyzed?

So i have this algorithm analysis from my lecturer i need some help why the outer loop is n - 1 , isn't it should be n - 2? and the inner loop should be log3 (n) instead of log3(n) + 1
for(int a=3; a<=n; a++) n+1-2 = n-1
for(int a=1; a<n; a=a*3) log3 (n) +1
System.out.println(a); log3 (n)
Total =(n - 1)* (log3 (n)+1+log3 (n))
=(n-1)*(2 log3(n) + 1)
=2(n log3(n))+n -1 – 2 log3(n)
=n log3(n) + n – log3(n)
Is this correct answer for algorithmn analysis? thats what my lecturer showed me. Anyone can explain to me?
If you want a very precise count of the operations, it's important to specify: what operations are you counting? Number of increments a++? Number of comparisons a<=n? Number of executions of the loop body?
If you don't specify which operation you're counting, then there is not much sense in worrying about an extra +1 or -1.
Note that the variable used as counter for the outer loop is called a and the variable used as counter for the inner loop is also called a. While this is possible to do in C++, I strongly recommend not doing it. It's confusing and a good source of errors.
The outer loop is going to run for n-2 iterations. The inner loop is going to run for ceil(log3(n)) iterations (where ceil is the ceiling function). The line System.out.println(a) is not a loop, it's just one line of code, so you could write "1" on that line if you wanted; there is not much sense in writing "log3(n)" here.
The total number of times the line System.out.println(a) is executed is thus:
(n - 2) ceil(log3(n)).
It is possible that you might want to count the exact number of characters written. Again, we're coming back to the fact that you didn't specify what it is that you were counting. The number of characters depends on the exact value of a, so it changes at each iteration of the loop. But all in all, each call to System.out.println(a) prints about log10(n) characters, since we're writing in decimal.

For loop - big-O

I am trying to do this problem out of a book and am struggling to understand the answer.
for (i = 0; i < N; ++i) {
if ((i % 2) == 0) {
outVal[i] = inVals[i] * i
}
}
here's how I was breaking it down:
I=0 -> executes 1 time
I < n and ++I each execute once every iteration. so 1n+1n = 2n.
the if statement contains 2 operands, so now we are at 4n+1.
the contents of the if statement only executes n/2 times, so we are at 4n+1+n/2
however, big O drops those terms off, leaving us with N as the answer
Here's what I don't get: the explanation for the answer of my problem says this:
outVal[i] = inVals[i] * i; executes every other loop iteration, so the total number of operations include: 1 operation at the start of the loop, 3 operations every loop iteration, 1 operation every other loop iteration, and 1 operation for the final loop condition check.
how are there only 3 operations in the loop? I counted 4 as stated above. Please let me know the rationale behind this.
The complexity is measured by the time/space you take to accomplish a task. i<N and ++i do not take time dependant of your space variable N (the length of the loop).
You must not add how many times an operation is done and sum all of them - you must, instead, choose the one who takes more time or space, as that's the algorithm bottleneck. In a loop, msot of the operations run equal times, so we use the length of the loop as its complexity space or time.
The loop will run N times, so that's its complexity -> O(n)
Inside the loop, the if scope will run N/2 times, as you correctly said -> O(n/2)
But those runs are already added to the first loop iterations. You will not add it since there are no external iterations.
So, the complexity of the algorithm is O(n).
Regarding the operations, the 3 are:
Checking I
Adding 1 to I
The if condition
All of them are done in every iteration.

how i can find the loop invariant and prove correctness?

i am learning correctness and struggling to find the appropriate loop invariant and prove its correctness.
I think the loop invariant should be the t=sum of the positive value but i don't know how to write it correctly or is there any other loop invariant?
SumPos(A[0..n - 1])
// Returns the sum of the positive numbers in an array A of length n. We
// assume that the sum of the numbers in an array of length zero is
zero.
t = 0
i = 0
while i != n do
if A[i] > 0
t = t + A[i]
i = i + 1
return t
Before getting formal, it is sometimes helpful to think in terms of "what stays the same" and "what changes" as regards the loop.* For the loop written, we have these variables of interest:
A - the array of numbers to sum
n - the integral number of elements in A.
t - as written, I assume intended to be the eventual sum-of-positives
i - the index variable; sometimes called the variant
So what changes each iteration? The array A does not change. The number of elements n in the array does not change. As written, the sum t might change. The index variable i will change.
As pertains to the loop, then, folks generally say that i is the variant. It increments every iteration and a comparison of it against n is what exits the loop.
The invariant of interest to me is that t will always represent the calculated-so-far sum-of-positives. For example, on the first iteration:
Before the iteration, i == 0 and t is also correctly 0
After the iteration, i == 1 and t will be correct with respect to the first element.
However, as written, the return statement precludes any processing beyond the first element of the array. Moving from theory to practice, how then might you fix the implementation?
* For the pedantic, the qualifier is important because strictly speaking an "invariant" is something that does not vary -- does not change, or always holds true -- for every iteration of the loop. So? Lots of statements are invariant with respect to the loop! For example, my mother's name is invariant for the loop!

How should I count the number of operations in my algorithm?

After searching web, I found following solution for step count method.
int mean(int a[], size_t n)
{
int sum = 0; // 1 step * 1
for (int i = 0; i < n; i++) // 1 step * (N+1)
sum += a[i]; // 1 step * N
return sum; // 1 step * 1
}
Hence, the step count is 2N+3.
However, in a YouTube video, I saw that, for the same situation, they obtained a count of 6N+4. In that video, they count assignment, addition operations as steps.
Which is the correct method? The Big-O value remains the same, but, if someone ask me to give them a step count for the algorithm, what should I answer?
I don't know what you count there but, if you count the primitive operations involved then the calculation is a bit different.
The for() line contains the following operations:
i = 0 - 1 operation;
i < n - 1 operation, N times;
i ++ - 2 operations, N times; i ++ is a shortcut for i = i + 1 and this involves an addition and an assignment, therefore 2 operations;
Next, sum += a[i] is a shortcut for sum = sum + a[i] and that contains the computation of address of a[i] which is an addition (1 operation), the addition (sum + a[i]) and the assignment (2 operations). And all these happen N times;
Summing all up there are 6 * N + 2 operations but, as other posters noticed, it depends a lot of what you count. If you analyze the assembler code generated by the compiler for this source code you'll notice there are also read instructions and they should be counted too.
All in all, the algorithm is linear, its complexity is O(n) and this is the most important fact about it. This notation washes out the 4 or 6 or whatever value one puts in front of N because it is irrelevant.
Which is the correct method? Big O value is same. But if someone ask to give step count for the algorithm how do I answer?
It's a matter of convention:
some people count additions and multiplications together,
others just multiplications, because they would typically be more demanding operations than additions (although that's less true on modern architectures), and
yet other keep separate counts of additions and multiplications,
etc.
As long as you're crystal-clear as to which convention you're adopting, anything goes. The choice of convention has typically no impact on the asymptotics (the "Big-O value") anyway , although it would affect the constant factors.
Edit: As pointed out by dwn in his comment, what goes on "close to the metal" may end up having a different algorithmic complexity to that of your source code, because of compiler/interpreter optimization tricks.

How to calculate worst case analysis of this algorithm?

sum = 0;
for(int i = 0; i < N; i++)
for(int j = i; j >= 0; j--)
sum++;
From what I understand, the first line is 1 operation, 2nd line is (i+1) operations, 3rd line is (i-1) operations, and 4th line is n operations. Does this mean that the running time would be 1 + (i+1)(i-1) + n? It's just these last steps that confuse me.
To analyze the algorithm you don't want to go line by line asking "how much time does this particular line contribute?" The reason is that each line doesn't execute the same number of times. For example, the innermost line is executed a whole bunch of times, compared to the first line which is run just once.
To analyze an algorithm like this, try identifying some quantity whose value is within a constant factor of the total runtime of the algorithm. In this case, that quantity would probably be "how many times does the line sum++ execute?", since if we know this value, we know the total amount of time that's spent by the two loops in the algorithm. To figure this out, let's trace out what happens with these loops. On the first iteration of the outer loop, i == 0 and so the inner loop will execute exactly once (counting down from 0 to 0). On the second iteration of the outer loop, i == 1 and the inner loop executes exactly twice (first with j == 1, once with j == 0. More generally, on the kth iteration of the outer loop, the inner loop executes k + 1 times. This means that the total number of iterations of the innermost loop is given by
1 + 2 + 3 + ... + N
This quantity can be shown to be equal to
N (N + 1) N^2 + N N^2 N
--------- = ------- = --- + ---
2 2 2 2
Of these two terms, the N^2 / 2 term is the dominant growth term, and so if we ignore its constant factors we get a runtime of O(N2).
Don't look at this answer as something you should memorize - think of all of the steps required to get to the answer. We started by finding some quantity to count, and then saw how that quantity was influenced by the execution of the loops. From this, we were able to derive a mathematical expression for that quantity, which we then simplified. Finally, we took the resulting expression and determined the dominant term, which serves as the big-O for the overall function.
Work from inside-out.
sum++
This is a single operation on it's own, as it doesn't repeat.
for(int j = i; j >= 0; j--)
This loops i+1 times. There are several operations in there, but you probably don't mean to count the number of asm instructions. So I'll assume for this question this is a multiplier of i+1. Since the loop contents is a single operation, the loop and its block perform i+1 operations.
for(int i = 0; i < N; i++)
This loops N times. So as before, this is a multiplier of N. Since the block performs i+1 operations, this loop performs N(N+1)/2 operations in total. And that's your answer! If you want to consider big-O complexity, then this simplifies to O(N2).
It's not additive: the inner loop happens once for EACH iteration of the outer loop. So it's O(n2).
By the way, this is a good example of why we use asymptotic notation for this kind of thing -- depending on the definition of "operation" the exact details of the count could vary pretty widely. (Like, is sum++ a single operation, or is it add sum to 1 giving temp; load temp to sum?) But since we know that all that can be hidden in a constant factor, it's still going to be O(n2).
No; you don't count a specific number of operations for each line and then add them up. The entire point of constructions like 'for' is to make it possible for a given line of code to run more than once. You're supposed to use thinking and logic skills to figure out how many times the line 'sum++' will run, as a function of N. Hint: it runs once for every time that the third line is encountered.
How many times is the second line encountered?
Each time the second line is encountered, the value of 'i' is set. How many times does the third line run with that value of i? Therefore, how many times will it run overall? (Hint: if I give you a different amount of money on several different occasions, how do you find out the total amount I gave you?)
Each time the third line is encountered, the fourth line happens once.
Which line happens most often? How often does it happen, in terms of N?
So guess what interest you is the sum++ and how many time you execute it.
The final stat of sum would give you that answer.
Actually your loop is just:
Sigma(n) n goes from 1 to N.
Which equal to: N*(N+1) / 2 This give you in big-o-notation O(N^2)
Also beside the name of you question there is no worst case in you algorithm.
Or you could say that the worst case is when N goes to infinity.
Using Sigma notation to represent your loops:

Resources