I have the following java function, to count common elements in two sorted arrays. Note the line with the question mark.
public static int overlap(int[] a, int[] b)
{
int i = 0, j = 0;
int res = 0;
while(i < a.length && j < b.length)
{
if(a[i] > b[j])
j ++;
else if(a[i] < b[j])
i ++;
else if(a[i] == b[j]) // ?
{
res ++;
i ++;
j ++;
}
}
return res;
}
Clearly, that last if-statement doesn't need to be there: at that point we know that the two values are equal. However, when I test the speed (I was checking whether the order of the checks made any difference), the method with the superfluous check is invariably faster than the one without (sometimes by a factor of two).
What is going on here? Some mysterious optimization? Am I overlooking something obvious? I'm using the stand compiler, version 1.8. I can't read bytecode, so I don't know what's going on under the hood.
Here is a full test class: https://gist.github.com/pbloem/1523283211454ec58ce9c5b45204eebd
Bytecode:
https://gist.github.com/pbloem/ce4f6758f0bb1424c155c26e83ca88a1
Possibly JIT swapping order of "if"s to get best performance but cannot swap order of just "else"(without an "if") with another "if" at the beginning, so when you added "if" after "else", it tried it as a first check, and if array overlapping is like %90, then it could keep that last "if" at the first place.
if(a[i] == b[j]) // say %70 probability after N iterations
{ // or less randomized branching
res ++;
i ++;
j ++;
}
else if(a[i] > b[j]) // %20 probability, medium branching
j ++;
else if(a[i] < b[j]) // %10, totally random branching, not predictable
i ++;
is possible when arrays ordering
a > b or b < a
are more randomized than arrays overlapping.
When there is if+if+else, JIT may not predict what you mean in that. By adding an equalty, you are elliminating many cases except the equation.
Could you please try with an ordered array and totally randomized array?
Or it is just helping cpu's branch predictor as #noahnu said in comments.
By using System.currentTimeMillis() you cant get exact elapsed time of program as sometimes it can be wrong due to JIT optimization. Try to use System.nanoTime().
Also make the variables used in time calculation as global variable for proper micro benchmark.
double sumWith = 0.0;
double sumWithout = 0.0;
Related
This method merges two sorted lists and I want to know the time complexity of it if the length of list a is n and the length of list b is m. I am confused with while loops because they also sort of act like if statements (are only executed when the condition is true), meaning they aren't necessarily executed, so how can I compute the time complexity of them?
ArrayList<Integer> union(ArrayList<Integer> a, ArrayList<Integer> b){
ArrayList<Integer> res = new ArrayList<>();
int i = 0, j = 0;
while (i<a.size() && j<b.size()){
if(a.get(i) < b.get(j)){
res.add(a.get(i));
i++;
} else if (a.get(i) > b.get(j)){
res.add(b.get(j));
j++;
} else {
res.add(b.get(j));
i++;
j++;
}
}
while (i < a.size()){
res.add(a.get(i));
i++;
}
while (j < b.size()){
res.add(b.get(j));
j++;
}
return res;
}
Well, each iteration of each while loop increments either i or j (or both) by 1.
i can grow from 0 to n - 1.
j can group from 0 to m - 1.
Hence the total number of iterations is bound by n + m.
Since each iteration of any of the 3 loops does constant amount of work (since both ArrayList's get and add take constant time),
the total time complexity in O(n+m).
BTW, is this method supposed to eliminate duplicates?
If it does, it only eliminates duplicates for elements that appear on both input lists. If a list already contains duplicates, they won't be eliminated.
If it's not supposed to eliminate duplicates, it has a bug, since if a.get(i) == b.get(j), both should be added to the output list (since both i and j are incremented in this case).
I'm studying this book since yesterday and after I've understood and applied the first algorithm, I tried to go on my own and look in a different way. Here's, in Java, the shown algorithm :
public static int[] sort(int[] array)
{
for(int i = 1; i < array.length; i++){
int value = array[i];
int j = i - 1;
while(j >= 0 && array[j] > value){
array[j + 1] = array[j];
j--;
}
array[j+1] = value;
}
return array;
}
And here is mine :
public static int[] sortb(int[] array)
{
for(int i = 0; i < array.length; i++){
int value = array[i];
int j = i;
while(j < array.length && value > array[j]){
array[j] = array[j + 1];
j++;
}
array[j] = value;
}
return array;
}
For 1 million of function call for each, I got 32 ms for the first and 25 ms for the second. I'm still beginning with algorithms, so I have no idea of the meaning.
I found why your sort is so much faster than original one: Because you are not doing sort at all.
In your code
int value = array[i];
int j = i;
while(j < array.length && value > array[j]) { ... }
Because j = i, so value == array[j] before you get into the while loop, and thus your while loop body will never execute. Your sort result will be wrong. That's the main reason why your code is extremely faster.
In my experience (read student's experience) this kind of different values have little meaning.
Maybe you had a background process that took / released a bit more of resources from one to another.
Maybe the specific case you tried to arrange was better for one of the algorythms than the other.
Maybe, if you used different random arrays, one of them was closer to be sorted than the other..
To have good measures, you usually have to do a lot of tests, not only one. For example, generate 1k arrays of 10k elements each and sort each of this array with both algorythms..
Anyway, sometimes specific features of a language or a compiler can generate different results for algorythms with theoretically the exact same complexity (one example: once I noticed in C++ if you traverse a 2-dimensional array first by columns and then by rows, you will have a very different speed than if you do it the other way around; but I don't remember which one was faster tbh).
I have a method in java that finds the longest non-decreasing segment in an array.
The method works, however, as part of the assignment, I need to find the running time of size n elements using the method in terms of O(f(n))(i.e upper bound) and Ω(g(n))(i.e lower bound). Can someone help me?
thanks in advance!!
public int maxAscent(int A[])
{
int num = 0;
int count = 1;
int i;
for(i = 0; i < A.length-1; i++)
if(A[i] <= A[i+1])
count++;
else
{
if(count > num)
num = count;
count = 1;
}
if(count > num)
num = count;
return num;
}
The upper and lower bound, as well as the Big O have to do with the loops. The questions to ask are which loops do you have in your code? How many times does each loop run in the worst case and best case scenarios? If a loop contains another loop, you multiply their best cases and their worst cases.
For your particular program, there is one loop, and it goes from 0 to n-1. There is no early out so your best case, worst case, and average case all have the same value.
That would be "n" operations, so O(n), Ω(n) and ø(n).
I am trying to figure out the run time of the following algorithm.
I argue it is O(n) because the inner loop does not depend on the outer loop.
So we could have O(n) + O(n) = O(2n) which equals O(n)
Is this correct? I'm not sure my logic is correct and I cannot figure out how to analyze is correctly.
The algorithm is finding the largest elements to the left of a list of elements.
Thanks!
public static void main(String[] args){
int[] a = {4,3,2,10,4,8,9,1};
int[] p = new int[a.length];
ArrayDeque<Integer> previousIndex = new ArrayDeque<Integer>();
for(int i = 0; i < a.length ; i++){
while (!previousIndex.isEmpty() && a[previousIndex.peek()] <= a[i]){
previousIndex.pop();
}
if (previousIndex.isEmpty()) {
p[i] = 0;
} else {
p[i] = previousIndex.peek();
}
previousIndex.push(i);
}
for(int i = 0; i < p.length ; i++){
System.out.println(p[i]);
}
}
}
This is O(N) for though you have a loop within a loop, the total number of times the inner loop will be executed can never be more than the total number of times that
previousIndex.push(i);
is called, which is a.length (or N)
To work out the order really you are looking at the worst case. You are correct that the nested loop is the cause for concern here:
for(int i = 0; i < a.length ; i++){
This is immediately order N
while (!previousIndex.isEmpty() && a[previousIndex.peek()] <= a[i]){
This could potentially also go nearly N times.
So the final order is N*N or N^2
You do have to keep in mind the usual case though. If it is likely that the while loop will in fact exit after only a couple of iterations you could get back down to O(N).
In fact, you are fine and have an O(N) algorithm - but it's harder to prove than most. This assumes, though, that .isEmpty(), .peek() etc. on the ArrayDeque are all constant-time operations. Consult the documentation to be sure.
The key is that your processing of the deque in the inner loop is destructive:
while (!previousIndex.isEmpty() && a[previousIndex.peek()] <= a[i]){
previousIndex.pop();
}
This removes an element from previousIndex each time, and can only run when there is one to remove. Therefore, the total number of times the while loop could run, across all indices, is the number of times that something is .pushed into deque. And since this only happens at one point - at the end of the first for loop - we can see that the number of items pushed is O(N).
I spend the last hour doing trial and error with this problem with no avail. We have to, using general coding guidelines (like scan.nextDouble) instead of actual numbers, find the max of a certain number of double values. The only catch is that we can only add code at a certain point. (where the ... is)
double value, valMax;
int n;
n = scan.nextInt();
for(int j = 0; j < n; j++)
{
value = scan.nextDouble();
...
}
Where the first value read in is an int and that is the amount of doubles to be entered.
It is difficult because I have to find a way to initialize valMax inside the loop without messing up anything else.
This is what I have been working with, but with nothing working for me.
for(int j = 0; j < n; j++)
{
value = scan.nextDouble();
if(j == 0)
{
valMax = scan.nextDouble();
j++;
}
else
{
continue;
}
if(value >= valMax)
{
valMax = value;
}
}
Example input:
5 -4.7 -9.2 -3.1 -8.6 -5.0
Where -3.1 is the max and 5 is the count of following numbers.
Your code seems like a good start.
To help solve your problem, consider:
Why did you put in the extra j++? Do you really need it? (Hint: no ;-) )
What will the loop do for j>0 (i.e. after the first iteration)?
That should quickly give you a working solution.
Are you allowed to set the valMax before the loop? Because in that case you can just do
valMax = Double.MIN_VALUE
and just forget about strange things by doing a normal comparison value > valMax.
If you are not your approach is how you should do but two things:
you shouldn't care about incrementing with j++ since the for loop will care about it by itself..
having a else { continue; } will make the body of the for jump to next iteration without caring about code that is after the continue. Are you sure that is what you want to do?
I think that you can initialize to Double.MIN_VALUE at first iteration (j == 0) and just behave normally afterwards: the only thing you need is that valMax is initialized before the first comparison with value, not before the scan from stdin..