While reading essential java item45, there are three different simple loops as shown in the code below with timing. The first and the third is preferred. But when I time them,
for N < 1,000,000:
length of the list: 1000000
sum is: 499999500000
fast method time: 25
sum is: 499999500000
slower method time: 5
sum is: 499999500000
range method time: 21
the second method is actually faster, I think this may because java compiler is smart enough to substitute the a.size() with the actual number.
However, when N grows, the second method indeed becomes slower. I am aware this experiment is naive and machine specific. I wonder if there is an explanation for the second method outperform the other two when N is small. (the program have been run multiple times)
length of the list: 10000000
sum is: 49999995000000
fast method time: 44
sum is: 49999995000000
slower method time: 48
sum is: 49999995000000
range method time: 37
The code:
public static void main(String [] args){
// test the speed of initialize 1 million elements
// timing two different loops.
// int N = 10000000;
int N = 1000000;
List<Integer> a = new ArrayList<Integer>();
for(int i = 0; i < N; ++i){
a.add(i);
}
System.out.println("length of the list: " + a.size());
long t1 = System.currentTimeMillis();
long sum = 0;
for(int i = 0, n = a.size(); i < n; ++i){
sum += a.get(i);
}
long t2 = System.currentTimeMillis();
System.out.println("sum is: " + sum);
System.out.println("fast method time: " + (t2 - t1));
t1 = System.currentTimeMillis();
sum = 0;
for(int i = 0; i < a.size(); ++i){
sum += a.get(i);
}
t2 = System.currentTimeMillis();
System.out.println("sum is: " + sum);
System.out.println("slower method time: " + (t2 - t1));
t1 = System.currentTimeMillis();
sum = 0;
for(int i: a){
sum += i;
}
t2 = System.currentTimeMillis();
System.out.println("sum is: " + sum);
System.out.println("range method time: " + (t2 - t1));
}
I was indeed having the same results than you:
length of the list: 1000000
sum is: 499999500000
fast method time: 32
sum is: 499999500000
slower method time: 12
sum is: 499999500000
range method time: 24
So I used javap -c to dissasemble the bytecode, and I saw that javac was not making any kind of optimization when seeing that N was small, and in deed, no optimization was being done.
So, I tried exchanging the order of the first two statements, and here is the result:
length of the list: 1000000
sum is: 499999500000
slower method time: 30
sum is: 499999500000
fast method time: 8
sum is: 499999500000
range method time: 25
So the difference between those two methods is not the method itself, but which one happens first will be the slower.
As for why it happens, it is still puzzling me (maybe deferred loading of some class? hot-code native compilation?)
Related
What is the difference between these 2? They both give me the execution-times with slighlty different values and I can tell they are written differently. But since the outcome is nearly identical, what does code 2 do that code 1 does not? What actually is the function of "sum" and is it even executed?
Code 1:
for (int i = 0; i < 10; i++)
{
long n0 = System.nanoTime();
long n1 = System.nanoTime();
System.out.println(" resolution " + (n1 - n0) + " nanoseconds");
}
Code 2:
int[] given = {1,2,3,4,5,6,7,8,9,0};
int sum = 0;
for (int i = 0; i < 10; i++)
{
long t0 = System.nanoTime();
sum += given[i];
long t1 = System.nanoTime();
System.out.println(" resolution " + (t1 - t0) + " nanoseconds");
}
Here is the output for those who asked:
Code 1:
Code 2:
It is simply code to try out System.nanoTime(). Doing something or nothing between two calls does not make a discernible difference. The resolution of the clock is about 100 ns.
As sum is not printed, the compiler might have optimized the code by removing just that extra code.
Furthermore it seems that nanoTime alone already requires ~100 ns.
Note
The code is primarily written for ones own curiosity.
This question already has answers here:
How do I write a correct micro-benchmark in Java?
(11 answers)
Closed 4 years ago.
I am totally lost for words, i have no idea what's going on...
//import java.util.Random;
public class TestInProgress {
public static void main(String[] args) {
long start, end;
long average;
// Random random= new Random();
for(int j=0; j<5; j++) {
average= 0L;
for(int i=0; i<100; i++) {
start= System.nanoTime();
double temp= fastComputeSigm(0.0);
end= System.nanoTime();
average+= (end - start);
}
average= average/ 100L;
System.out.println("AVERAGE FASTCOMPUTE: "+average);
average= 0L;
for(int i=0; i<100; i++) {
start= System.nanoTime();
double temp= Math.exp(0.0)/(1.0 + Math.exp(0.0));
end= System.nanoTime();
average+= (end - start);
}
average= average / 100L;
System.out.println("AVERAGE SIGMOID: "+average);
}
}
static double fastComputeSigm(double value) {
return 0.0;
}
}
The most surprising is the output:
AVERAGE FASTCOMPUTE: 98 AVERAGE SIGMOID: 38625 AVERAGE FASTCOMPUTE:
106 AVERAGE SIGMOID: 65 AVERAGE FASTCOMPUTE: 299 AVERAGE SIGMOID: 201
AVERAGE FASTCOMPUTE: 36 AVERAGE SIGMOID: 65 AVERAGE FASTCOMPUTE: 53
AVERAGE SIGMOID: 57
see that? I uncommented Random (and its import)
What would you expect? output:
AVERAGE FASTCOMPUTE: 90 AVERAGE SIGMOID: 324 AVERAGE FASTCOMPUTE: 131
AVERAGE SIGMOID: 73 AVERAGE FASTCOMPUTE: 217 AVERAGE SIGMOID: 36
AVERAGE FASTCOMPUTE: 53 AVERAGE SIGMOID: 12 AVERAGE FASTCOMPUTE: 53
AVERAGE SIGMOID: 69
I tried this on eclipse Oxygen.3a Release (4.7.3a)
I'm using java 10.0.2
My computer has a Intel Core i5-7300HQ
with those cache memories : L1 256, L2 1024, L3 6M
I run on windows 10
note: I tried to make the forloop with j<1,2,3,... several times no helping the first sigmoid always has absurd time length
edit: tried launching it with windows commandline (javac and java), same result
In general comments pointing you at the trickiness of micro-benchmarking are correct and you should be using JMH. In this particular case it looks like your timing / benchmarking code introduces latency that wasn't there in the first place.
Here is a compacted version of your code that demonstrates that the issue is triggered not by the use of Random but of the use of System.nanoTime() (which Random() calls internally)
public static void main(String[] args) {
long start, end, average;
double temp = 0;
// start = System.nanoTime();
average = 0L;
for (int i = 0; i < 100; i++) {
start = System.nanoTime();
temp += Math.exp(0.0) / (1.0 + Math.exp(0.0));
end = System.nanoTime();
average += (end - start);
// System.out.println(i + " " + (end - start));
}
System.out.println("AVERAGE: " + (average / 100));
System.out.println("temp: " + temp);
}
When I uncomment start = System.nanoTime() I observer about x100 speedup - at this point it is important to remember that the units here are nanoseconds so we are very sensitive to whatever it is the runtime is doing in the background. If you uncomment System.out.println(i + " " + (end - start)); you will see an occasional hiccup that is responsible for the entire slowdown. While it may be interesting to chase the reason for that hiccup the following version of the code indicates that it is due to the measurement rather than the core functionality so you may want to make sure that this is something you want to spend your own time on [1]:
public static void main(String[] args) {
double temp = 0;
long start = System.nanoTime();
for (int i = 0; i < 100; i++) {
temp += Math.exp(0.0) / (1.0 + Math.exp(0.0));
}
System.out.println("AVERAGE: " + ((System.nanoTime() - start) / 100));
System.out.println("temp: " + temp);
}
The AVERAGE values here are similar to the previous version with start = System.nanoTime() uncommented.
[1] If you are interested in digging deeper into this particular behavior, try timing your test while running with the -Xint VM option which disables the compiler.
I was just wondering how many times a nested loop like this would run
int sum = 0;
for(int i = 0; i < total; i++) {
for(int j = i + 1; j < total; j++) {
for(int k = j; k < total; k++) {
sum++;
}
}
}
System.out.println(sum);
I can easily see the output of sum, but I would like to be able to mathematically calculate the total of sum with any number for total.
TL;DR
The loop will be executed for ((total ^ 3) - total) / 6 times and hence that will be the value of sum at the end of the loop.
int sum = 0;
for(int i = 0; i < total; i++) {
for(int j = i + 1; j < total; j++) {
for(int k = j; k < total; k++) {
sum++;
}
}
}
It is easy to see that the outer loop runs for a total times. The second loop is the trickier one
Let's try to work this out
i = 0, j runs from 1..total - 1
i = 1, j runs from 2..total - 1
i = 2, j runs from 3..total - 1
...
i = total - 2, j runs from total - 1 ..total - 1 (will only run once)
i = total - 1, inner loop does not execute as the loop termination condition is true.
The third loop is dependent on the second inner loop - k runs from j..total - 1
Let us take total as 6
j runs from 1..5 -> k runs for 5 times (j = 1) + 4 times(j = 2) + 3 times(j = 3)+ 2 times(j = 4) + 1 time(j = 4)
(Showing a minified version for others)
2..5 -> 4+3+2+1
3..5 3+2+1
4..5 2+1
5..5 1
Which is
1 + 2 + 3 + 4 + 5+
1 + 2 + 3 + 4 +
1 + 2 + 3 +
1 + 2 +
1
Generally,
1 + 2 + 3 + .. n +
1 + 2 + 3 +..n - 1+
1 + 2 + 3 +..n - 2+
1 + 2 + 3 +
1 + 2 +
1
This boils down to the sum
n * (n - 1)) / 2
For all values of n ranging from 1 to total
This can be verified with the below
int res = 0;
for (int i = 1; i <= total; i++) {
res += (i * (i - 1))/2;
}
res will be equal to your sum.
Mathematically, the above is
((total ^ 3) - total) / 6
Derivation:
References:
Sums of the First n Natural Numbers
Sum of the Squares of the First n Natural Numbers
The first iteration of the middle loop adds
total-1 + total-2 + ... + 1
to the sum.
The second iteration of the middle loop adds
total-2 + total - 3 + ... + 1
to the sum
The last iteration of the middle loop adds
1
to the sum.
If you sum all of these terms, you get
(total - 1) * 1 + (total - 2) * 2 + (total - 3) * 3 + ... + 2 * (total - 2) + 1 * (total - 1)
It's been a while since I studied math, so I don't remember if there's a simpler formula for this expression.
For example, if total is 10, you get:
9 * 1 + 8 * 2 + 7 * 3 + 6 * 4 + 5 * 5 + 4 * 6 + 3 * 7 + 2 * 8 + 1 * 9 =
9 + 16 + 21 + 24 + 25 + 24 + 21 + 16 + 9 =
165
It needs only a little bit knowledge of programming.Actually logic that is running behind is only computational kind of thing.
let's say:
total=10,sum=0
- when i is 0:
That time j is initialised with 1(i+1) and k as well. So k will lead us to execute the loop 9 times and and as j is incremented ,it will lead us to execute sum statement 8 times and 7 times and further 6 times till 1 time. (9+8+7+6+5+4+3+2+1=45 times.)
- when i is 1:
That time j is initialised with 2 and k as well.So sum statement is going to execute 8 times and then 7 times and then 6 times till 1.
(8+7+6+5+4+3+2+1=36 times).
- when i is 2:
Same thing happens repeatedly but starting with difference number ,so this time (7+6+5+4+3+2+1=28)
So this sequence continues until there is significance of occuring the condition with trueness.
This happens till i is 9.
So the final answer is 1+3+6+10+15+21+28+36+45=165.
The equation is like below
and the k is equal to total :
outermost loop runs 'total' number of times.
for each outer loop , middle loop runs 'total-i' times.
i.e total * total+total * (total-1)+total * (total-2)....total * 1
= total*(total+total-1+total-2...1)
= total*(1+2+3....total)
= total*(sum of first 'total' natural numbers)
= total*(total*(total+1)/2)
now the innermost loop also runs 'total-j' times for each middle loop
i.e
total*(total*(total+1)/2)*(total+(total-1)+(total-2)....+1)
= total*(total*(total+1)/2)*(1+2+3....+total)
= total*(total*(total+1)/2)* (sum of first 'total' natural numbers)
= total*(total*(total+1)/2) * (total*(total+1)/2)..
So finally you will get something close to
total * (total*(total+1)/2) * (total*(total+1)/2).
Sorry there's a correction as #andreas mentioned innermost and middle loops run only till total-i-1 times
in which case it will be the sum of first (total-1) no.s which should be (total-1)*total/2 so the final output should be
total * (total*(total-1)/2) * (total*(total-1)/2) .
As we know, the sum of an arithmetic progression is:
The inner-most loop will loop for
times, which is a function to j.
You sum it up and get a function to i, aka:
You sum it up again and get a function to total, aka:
For Mathematica users, the result is:
f[x_]:=Sum[Sum[x-j,{j,i+1,x-1}],{i,0,x-1}]
From here, we can see it more clearly, and the FINAL result is:
where x is total.
That function will loop (total/6)*(total*total - 1) times
The snippet bellow just verifies that
var total = 100
var sum = 0;
for(var i = 0; i < total; i++) {
for(var j = i + 1; j < total; j++) {
for(var k = j; k < total; k++) {
sum++;
}
}
}
function calc(n) {
return n*(n-1)*(n+1)/6
}
console.log("sum: "+sum)
console.log("calc: "+calc(total))
If we run this loop 100 times and generate a data set, then graph it, we get this:
Now, this graph is clearly a cubic. So we can do a solve using the cubic equation of ax^3+bx^2+cx+d.
Using 4 points, the values of them all are:
So the full equation is
y=x^3/6-x/6
y=x(x^2/6-1/6)
y=(x/6)(x^2-1)
Interactive Graph:
<iframe src="https://www.desmos.com/calculator/61whd83djd?embed" width="500px" height="500px" style="border: 1px solid #ccc" frameborder=0></iframe>
A simple loop like this:
for (i = a; i < b; i ++) {
....
}
runs b-a iterations (i takes the values: a, a+1, a+2... b-2, b-1) if a < b and 0 iterations otherwise. We will assume below that a < b always.
Its number of iterations can be compute using the simple maths formula:
Applying the formula to your code
We start with the innermost loop:
for(int k = j; k < t; k++) {
sum++;
}
Its number of iterations is:
Using the formula above, the value of U is (t-1)-j+1 which means:
U = t - j
Adding the middle loop
Adding the middle loop, the number of iterations becomes:
The terms of the second sum are t-(i+1), t-(i+2), ... t-(t-2), t-(t-1).
By solving the parentheses and putting them in the reverse order they can be written as:
1, 2, ... t-i-2, t-i-1.
Let p = t - j. The second sum now becomes:
It is the sum of the first t-i-1 natural numbers and its value is:
Adding the outer loop
Adding the outer loop the sum becomes:
On the last sum, the expression (t - i) starts with t (when i = 0), continues with t-1 (when i = 1) and it keeps decreasing until it reaches 1 (when i = t - 1). By replacing q = t - i, the last sum becomes:
The last expression subtracts the sum of the first n natural numbers from the sum of the first n square numbers. Its value is:
Now it's easy to simplify the expression:
The final answer
The number of iterations of the posted code is:
I have an exception in my Java program. When I run this code:
ArrayList<Integer> sum = new ArrayList<Integer>();
sum.add(10);
sum.add(15);
sum.add(20);
int total = 0;
int avg;
for(int i = 0; i < sum.size(); i++)
{
total += sum.get(i);
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
}
It prints each ArrayList index element then prints the average, but when I run this code:
for(int i = 0; i<sum.size(); i++)
total = total+sum.get(i);
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
It prints the average, but I have not given the bracket of the for loop.
How does the code compare?
Lambda stream method in Java 8 can solve this in a easy way:
int myArray[] = { 1, 2, 3 };
Arrays.stream(myArray).average();
The brackets are use to define block of statement
By default, a loop or a condition only read one statement. A statement could be one line or a block of statement
So here is a line
total=total+sum.get(i);
and here is the block of statement
{
total += sum.get(i);
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
}
NOTE : You speak about exception but also said that there is an output in both cases, so I guess your exception is not a Java Exception but just some misunderstanding in this behavior.
EDIT : You should change avg type to accept decimal values and you are going to change a bit the line, the easier is to add a static value of float to convert the value :
float avg = 1.0f * total / sum.size();
Because there is a float here (1.0f), the result will be a float, if you only use integers, the result will be rounded in integer (even if you store it in a float).
From your question, I guess that you are learning Java.
If you are in Java 8, you might use Stream (see link for a better explanation):
The new Stream API allows to transform (map), filter values, etc.
It allows to collect them (see Collectors), regrouping them by key (groupingBy), and in your case to compute a summary statistics.
The example below shows you how to do that using either an IntStream (a Stream tailored for int) or a standard Stream:
IntSummaryStatistics stats = Arrays.asList(10, 15, 20)
.stream()
.mapToInt(Integer::intValue)
.summaryStatistics()
;
// alternative
// IntSummaryStatistics stats2 = Arrays.asList(10, 15, 20)
// .stream()
// .collect(Collectors.summarizingInt(Integer::intValue))
// ;
System.out.println("average: " + stats.getAverage());
System.out.println("count: " + stats.getCount());
System.out.println("sum: " + stats.getSum());
See the javadoc for Collectors.summarizingInt.
In java curly braces are used to group the line of code. In first block of code
ArrayList<Integer> sum = new ArrayList<Integer>();
sum.add(10);
sum.add(15);
sum.add(20);
int total = 0;
int avg;
for(int i = 0; i < sum.size(); i++)
{
total += sum.get(i);
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
}
in this code you are adding element to total and same time you are calculating average. Let us see each iteration
iteration 1:
total = 10
avg = 10/3 = 3
iteration 2:
total = 25
avg = 25/3 = 8
iteration 3:
total = 45
avg = 45/3 = 15
But in case of second code block
for(int i = 0; i<sum.size(); i++)
total = total+sum.get(i);
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
here code is equivalent to
for(int i = 0; i<sum.size(); i++){
total = total+sum.get(i);
}
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
so in for loop, it calculates total only as
iteration 1: total = 10
iteration 2: total = 15
iteration 2: total = 45
after completion of block value of total is 45
and after block, actual average is calculated as:
avg = 45/3 = 15
In java if we don't provide curly braces to group block of code inside for, if and while by default considered only single line inside the block and execute it repeatedly based on condition.
Exception is according to you Is not achieving the expected behaviour for an average on the elements of the collections.
So, as the earlier answer it boiles down to the Java syntax for working with the Loops/conditions/statements that how we use the { // code
}
By defaults a single line of code statement followed after Loops/conditions does not need to wrap in the braces {}
Here the first snippet uses a block of statement to derive average on each element by collecting it in total and dividing with size of collection.
Whereas, the second snippet does the collection of total for all element at first and then go for finding average.
You need to account for the data precisions when deriving mathematical values like avg and use the appropriate primitive data type.
If you remove the braces, the for loop header refers to the (one) very next statement, so the following two examples are equal:
for(int i=0; i<sum.size(); i++)
total=total+sum.get(i);
avg=total/sum.size();
System.out.println("The Average IS:" + avg);
for(int i=0; i<sum.size(); i++) {
total=total+sum.get(i);
}
avg=total/sum.size();
System.out.println("The Average IS:" + avg);
When you leave the brackets for the loop away, then just the first following line will be part of the loop. That means:
for(int i=0; i<sum.size(); i++)
total=total+sum.get(i);
avg=total/sum.size();
System.out.println("The Average IS:" + avg);
Is equivalent to
for(int i=0; i<sum.size(); i++){
total=total+sum.get(i);
}
avg=total/sum.size();
System.out.println("The Average IS:" + avg);
Same counts also for e.g. if/else.
1st
for(int i = 0; i < sum.size(); i++)
{
total += sum.get(i);
}
2nd
for(int i = 0; i < sum.size(); i++)
total += sum.get(i);
//but in this for loop it considers only one statement as inside for loop
3rd
for(int i = 0; i < sum.size(); i++)
total += sum.get(i);//only this statement is considered as inside the loop
System.out.println("hello");//this statements is not considered inside the loop
1st and 2nd for loops are same
You need to insert bracket in 2nd code of for loop the following.
for(int i = 0; i<sum.size(); i++) {
total = total+sum.get(i);
avg = total / sum.size();
System.out.println("The Average IS:" + avg);
}
Because if you don't insert bracket in for loop, only one line under the for loop will execute for looping process. So you need to make bracket for starting line to ending line that you want to execute in looping process.
I am trying to figure out what the time complexity of this simple program is, but I can't seem to understand what would be the best way to do this.
I have written down the time complexity side by side for each line
1 public int fnA (int n) {
2 int sum = 0; O(1)
3 for (int i = 0; i < n; i++) { O(n)
4 int j = i; O(n)
5 int product = 1; O(1)
6
7 while (j > 1) { O(n)
8 product ∗= j; O(log n)
9 j = j / 2; O(log n)
10 }
11 sum += product; O(1)
12 }
13 return sum; O(1)
14 }
Am I correct to assume these running times and that the final running time is: O(n)
If not, would somebody be able to explain where it is I am going wrong?
Overall:
1 + n + n + 1 + n + logn + logn + 1 + 1
= 3n + 2logn + 4
Final: O(n)
Time complexity for that algorithm is O(NlogN).
The for loop is executed N times (from 0 to N).
The while loop is executed logN times since your are dividing the number to half each time.
Since your are executing the while inside the for, your are executing a logN operation N times, from there it is the O(NlogN).
All remaining operations (assign, multiplication, division, sum) you can assume that takes O(1)
The crux of the above program is the while loop and it is the defining factor and rest of the lines will not have complexity more than O(n) and assuming that arithmetic operations will run in O(1) time.
while (j > 1) {
product ∗= j;
j = j / 2;
}
The above loop will have a run time of O(log(j)) and j is varying from 1 to n, so its the series...
-> O(log(1) + log(2) + log(3) + log(4).....log(n))
-> O(log(1*2*3*4...*n))
-> O(log(n!))
and O(log(n!)) is equal to O(n log(n))
For the proof for above refer this
No for every i, there is logn loop running and hence for n elements the total complexity is nlogn.
Since you know that the following loop takes logn .
while (j > 1) {
product ∗= j;
j = j / 2;
}
Now this particular loop is executed for every i. And so this will be executed n times. So it becomes nlogn.
To start with, you could count all operations. For example:
1 public int fnA (int n) {
2 int sum = 0; 1
3 for (int i = 0; i < n; i++) {
4 int j = i; n
5 int product = 1; n
6
7 while (j > 1) {
8 product ∗= j; ?
9 j = j / 2; ?
10 }
11 sum += product; n
12 }
13 return sum; 1
14 }
Now we could do the counting: which sums up to: 2 + 3n + nlog(n)
In a lot of programs the counting is more complex and usually has one outstanding higher order term, for example: 2+3n+2n2. When talking about performance we really care about when n is large, because when n is small, the sum is small anyway. When n is large, higher order term drawf the rest, so in this example 2n2 is really the term that matters. So that's the concept of tilde approximation.
With that in mind, usually one could quickly identify the portion of code that gets executed most often and use its count to represent overall time complexity. In example given by OP, it would look like this:
for (int i = 0; i < n; i++) {
for (int j = i; j > 1; j /= 2)
product *= j;
}
which gives ∑log2n. Usually the counting involves discrete mathamatics, one trick I have learned is to just replace with it integral and do caculus: ∫ log2n = nlog(n)