Evaluation order in Java - java

So I have this code for the Fibonacci sequence:
int fibonacci(int i, int[] memo) {
if (i == 0 || i == 1) return i;
if (memo[i] == 0) {
memo[i] = fibonacci(i - 1, memo) + fibonacci(i - 2, memo);
}
return(memo[i]);
}
My question is: fibonacci(i-1, memo) will always be evaluated before fibonacci(i-2, memo) correct?

Correct, from left to right.
First you will completely traverse the recursion with the left argument fibonacci(i - 1, memo) after that, when it moves the recursion tree up again, each time the right argument will get computed, again with a full recursive tree.
A quick search yield this image illustrating the process:
Note that many values are often computed multiple times. Your current approach tries to optimize this by caching results inside an array memo.

Related

Fibonacci Memoized/Dynamic Programming in Java

So this is some code to calculate the Fibonacci sequence with memoization. What confuses me is when we check if memo[i]==0. I understand that Java arrays are initialized to zero and thus if memo[i] == 0 this may mean that the computation for memo[i] has not yet occured. However, one of the return values for this fibonacci function is 0. So doesn't this mean that let's say if fib(3)=0 (I know it doesn't but just for the sake of arguement) then everytime we have fib(3) we would wind up recomputing fib(3) because the check is if(memo[i] == 0) right? And if that is the case why can we use if(memo[i] == 0) in this particular code and not wind up recomputing a bunch of values?
int fibonacci(int n){
return fibonacci(n, new int[n+1]);
}
int fibonacci(int i, int[] memo) {
if(i == 0 || i == 1) return i;
if(memo[i] == 0){ //This line does not make sense to me
memo[i] = fibonacci(i - 1, memo) + fibonacci(i - 2, memo);
}
return memo[i];
}
Since the only case where fib(i) should return 0 is when i = 0, then the test if (memo[i] == 0) is ok---it is never called for a value where 0 is an ambiguous result because of the first line of the function: if (i == 0.
Note what I think is more puzzling is why is the memoization array created in the wrapper call? Yes, the memoization saves computation for a given call, but all that optimization is lost between successive calls to the function.
if(memo[i]==0)
That if block implies that if fibonacci(n) has not already been computed (in which case the value in the i index will be 0), compute the value of fibonacci(n) and cache, but if it has already been computed and stored/cached (in which case the value in the i index would not be 0), do not compute again.
The reason for this is because, int array values are by default initialized to 0. You can see how here: Any shortcut to initialize all array elements to zero?

Check if a value in Array correspond to it place

I was confronted not so long ago to an algorithmic problem.
I needed to find if a value stored in an array was at it "place".
An example will be easier to understand.
Let's take an Array A = {-10, -3, 3, 5, 7}. The algorithm would return 3, because the number 3 is at A[2] (3rd place).
On the contrary, if we take an Array B = {5, 7, 9, 10}, the algorithm will return 0 or false or whatever.
The array is always sorted !
I wasn't able to find a solution with a good complexity. (Looking at each value individualy is not good !) Maybe it is possible to resolve that problem by using an approach similar to merge sorting, by cuting in half and verifying on those halves ?
Can somebody help me on this one ?
Java algorithm would be the best, but pseudocode would also help me a lot !
Here is an algorithm (based on binary search) to find all matching indices that has a best-case complexity of O(log(n)) and a worst case complexity of O(n):
1- Check the element at position m = array.length / 2
2- if the value array[m] is strictly smaller than m, you can forget about the left half of the array (from index 0 to index m-1), and apply recursively to the right half.
3- if array[m]==m, add one to the counter and apply recursively to both halves
4- if array[m]>m, forget about the right half of the array and apply recursively to the left half.
Using threads can accelerate things here. I suppose that there is no repetitions in the array.
Since there can be no duplicates, you can use the fact that the function f(x): A[x] - x is monotonous and apply binary search to solve the problem in O(log n) worst-case complexity.
You want to find a point where that function A[x] - x takes value zero. This code should work:
boolean binarySearch(int[] data, int size)
{
int low = 0;
int high = size - 1;
while(high >= low) {
int middle = (low + high) / 2;
if(data[middle] - 1 == middle) {
return true;
}
if(data[middle] - 1 < middle) {
low = middle + 1;
}
if(data[middle] - 1 > middle) {
high = middle - 1;
}
}
return false;
}
Watch out for the fact that arrays in Java are 0-indexed - that is the reason why I subtract -1 from the array.
If you want the find the first number in the array that is at its own place, you just have to iterate the array:
static int find_in_place(int[] a) {
for (int i=0; i<a.length; i++) {
if (a[i] == i+1) {
return a[i];
}
}
return 0;
}
It has a complexity of O(n), and an average cost of n/2
You can skip iterating if there is no such element by adding a special condition
if(a[0]>1 && a[a.length-1]>a.length){
//then don't iterate through the array and return false
return false;
} else {
//make a loop here
}
Using binary search (or a similar algorithm) you could get better than O(n). Since the array is sorted, we can make the following assumptions:
if the value at index x is smaller than x-1 (a[x] <= x), you know that all previous values also must be smaller than their index (because no duplicates are allowed)
if a[x] > x + 1 all following values must be greater than their index (again no duplicates allowed).
Using that you can use a binary approach and pick the center value, check for its index and discard the left/right part if it matches one of the conditions above. Of course you stop when a[x] = x + 1.
simply use a binary search for the 0 and use for compare the value in the array minus index of the array. O(log n)

Finding unique numbers from sorted array in less than O(n)

I had an interview and there was the following question:
Find unique numbers from sorted array in less than O(n) time.
Ex: 1 1 1 5 5 5 9 10 10
Output: 1 5 9 10
I gave the solution but that was of O(n).
Edit: Sorted array size is approx 20 billion and unique numbers are approx 1000.
Divide and conquer:
look at the first and last element of a sorted sequence (the initial sequence is data[0]..data[data.length-1]).
If both are equal, the only element in the sequence is the first (no matter how long the sequence is).
If the are different, divide the sequence and repeat for each subsequence.
Solves in O(log(n)) in the average case, and O(n) only in the worst case (when each element is different).
Java code:
public static List<Integer> findUniqueNumbers(int[] data) {
List<Integer> result = new LinkedList<Integer>();
findUniqueNumbers(data, 0, data.length - 1, result, false);
return result;
}
private static void findUniqueNumbers(int[] data, int i1, int i2, List<Integer> result, boolean skipFirst) {
int a = data[i1];
int b = data[i2];
// homogenous sequence a...a
if (a == b) {
if (!skipFirst) {
result.add(a);
}
}
else {
//divide & conquer
int i3 = (i1 + i2) / 2;
findUniqueNumbers(data, i1, i3, result, skipFirst);
findUniqueNumbers(data, i3 + 1, i2, result, data[i3] == data[i3 + 1]);
}
}
I don't think it can be done in less than O(n). Take the case where the array contains 1 2 3 4 5: in order to get the correct output, each element of the array would have to be looked at, hence O(n).
If your sorted array of size n has m distinct elements, you can do O(mlogn).
Note that this is going to efficient when m << n (eg m=2 and n=100)
Algorithm:
Initialization: Current element y = first element x[0]
Step 1: Do a binary search for the last occurrence of y in x (can be done in O(log(n)) time. Let it's index be i
Step 2: y = x[i+1] and go to step 1
Edit: In cases where m = O(n) this algorithm is going to work badly. To alleviate it you can run it in parallel with regular O(n) algorithm. The meta algorithm consists of my algorithm and O(n) algorithm running in parallel. The meta algorithm stops when either of these two algorithms complete.
Since the data consists of integers, there are a finite number of unique values that can occur between any two values. So, start with looking at the first and last value in the array. If a[length-1] - a[0] < length - 1, there will be some repeating values. Put a[0] and a[length-1] into some constant-access-time container like a hash set. If the two values are equal, you konow that there is only one unique value in the array and you are done. You know that the array is sorted. So, if the two values are different, you can look at the middle element now. If the middle element is already in the set of values, you know that you can skip the whole left part of the array and only analyze the right part recursively. Otherwise, analyze both left and right part recursively.
Depending on the data in the array you will be able to get the set of all unique values in a different number of operations. You get them in constant time O(1) if all the values are the same since you will know it after only checking the first and last element. If there are "relatively few" unique values, your complexity will be close to O(log N) because after each partition you will "quite often" be able to throw away at least one half of the analyzed sub-array. If the values are all unique and a[length-1] - a[0] = length - 1, you can also "define" the set in constant time because they have to be consecutive numbers from a[0] to a[length-1]. However, in order to actually list them, you will have to output each number, and there are N of them.
Perhaps someone can provide a more formal analysis, but my estimate is that this algorithm is roughly linear in the number of unique values rather than the size of the array. This means that if there are few unique values, you can get them in few operations even for a huge array (e.g. in constant time regardless of array size if there is only one unique value). Since the number of unique values is no grater than the size of the array, I claim that this makes this algorithm "better than O(N)" (or, strictly: "not worse than O(N) and better in many cases").
import java.util.*;
/**
* remove duplicate in a sorted array in average O(log(n)), worst O(n)
* #author XXX
*/
public class UniqueValue {
public static void main(String[] args) {
int[] test = {-1, -1, -1, -1, 0, 0, 0, 0,2,3,4,5,5,6,7,8};
UniqueValue u = new UniqueValue();
System.out.println(u.getUniqueValues(test, 0, test.length - 1));
}
// i must be start index, j must be end index
public List<Integer> getUniqueValues(int[] array, int i, int j) {
if (array == null || array.length == 0) {
return new ArrayList<Integer>();
}
List<Integer> result = new ArrayList<>();
if (array[i] == array[j]) {
result.add(array[i]);
} else {
int mid = (i + j) / 2;
result.addAll(getUniqueValues(array, i, mid));
// avoid duplicate divide
while (mid < j && array[mid] == array[++mid]);
if (array[(i + j) / 2] != array[mid]) {
result.addAll(getUniqueValues(array, mid, j));
}
}
return result;
}
}

Linked list recursion in Java

I have to code a recursive method that iterates through a linked list and returns the number of integers that are positive. Here is the question:
The method countPos below must be a recursive method that takes a Node head
as its argument, goes down the list headed by head, and counts the number of nodes which have a positive data field.
The code I have works however, I don't understand how it works.
public int countPos(Node head) {
int count = 0;
if (head == null) { return count; }
if (head.data > 0) {
count++;
return count + countPos(head.next);
} else {
return count + countPos(head.next);
}
}
The problem I'm having is I don't understand how count doesn't get set back to 0 every time the method is called. For some reason the statement int count = 0; is ignored the next time the method gets called. Is this because I'm returning count also? Any explanation would be greatly appreciated.
Thanks.
DON'T begin by tracing execution or debugging. The power of recursion is that it lets you reason about complicated programs with simple logic.
Your code works by chance. It reflects that whoever wrote it (you?) doesn't understand how recursion solves problems. It's more complex than necessary.
To exploit recursion, take the problem at hand and:
Define the function interface.
Split the problem into parts, at least one of which is a smaller version of the same problem.
Solve that (or those) smaller version(s) by calling the function interface itself.
Find the "base case" or cases that are solutions to very small instances of the same problem.
With all this done, the pseudocode for most recursive algorithms is:
function foo(args)
if args describe a base case
return the base case answer.
solve the smaller problem or problems by calling foo with
args that describe the smaller problem!
use the smaller problem solution(s) to get the answer for this set of args
return that answer
end
Let's apply this to your case:
PROBLEM: Count the number of positive items in a list.
Define the function interface: int countPos(Node head).
Split the problem up into parts: Get the number of positives in the list remaining after the head, then add one if the head is positive and nothing if the head is zero or negative.
The smaller version of the problem is finding the number of positives in the list with head removed: countPos(head.next).
Find the base case: The empty list has zero positives.
Put this all together:
int countPos(Node head) {
// Take care of the base case first.
if (head == null) return 0;
// Solve the smaller problem.
int positiveCountWithoutHead = countPos(head.next);
// Now the logic in step 2. Return either the positive count or 1+ the positive count:
return head.data > 0 ? positiveCountWithoutHead + 1 : positiveCountWithoutHead;
}
You might learn a little bit by tracing execution of something like this one time. But trying to write recursive code by reasoning about what's going on with the stack is a dead end. To be successful, you must think at a higher level.
Let's try one that doesn't quite follow the standard template: Recursive binary search. We have an array a of integers and are trying to find the index of x if it exists in the array and return -1 if not.
PROBLEM: Search the array between positions i0 and i1-1.
(The above is an example of how you must sometimes "specialize" the problem by adding parameters so that smaller subproblems can be described in the recursive call or calls. Here we are adding the new parameters i0 and i1 so that we can specify a subarray of a. Knowing how and when to do this is a matter of practice. The parameters needed can vary with language features.)
Function interface: int search(int [] a, int x, int i0, int i1)
Split the problem in parts: We'll pick a "middle" element index: mid = (i0 + i1) / 2. Then the subproblem is either searching the first half of the array up to but excluding mid or the second half of the array starting after mid and continuing to the end.
The calls are search(a, x, i0, mid) and search(a, x, mid + 1, i1).
The base cases are that 1) if i0 >= i1, there are no elements to search, so return -1 and 2) if we have a[mid] == x, then we've found x and can return mid.
Putting this all together
int search(int [] a, int x, int i0, int i1) {
// Take care of one base case.
if (i0 >= i1) return -1;
// Set up mid and take care of the other base case.
int mid = (i0 + i1) / 2;
if (a[mid] == x) return mid;
// Solve one or the other subproblems. They're both smaller!
return x < a[mid] ? search(a, x, i0, mid) : search(a, x, mid + 1, i1);
}
And to start the search:
int search(int [] a, int x) { return search(a, x, 0, a.length); }
Each time you call countPos(), a new version of that function starts. This function starts from a clean slate meaning all of the local variables (count) are its own, and no other "copy" of countPos can see or modify its local variables.
The only state that is passed between these "copies" or of countPos is the variables that are passed in as parameters (Node head).
So here's a rough workflow assuming the list [1, -2, 3]
countPos starts, and says number of positive nodes is equal to 1, since "1" is positive. The total number of positive nodes is equal to 1 + whatever the next function returns.
The next function says the number of positive nodes is equal to 0 + whatever the next function returns.
The next function says the number of positive nodes is equal to 1 + whatever the next function returns
The next function sees that head == null and so returns 0.
Now each recursive function returns one after another to the original function that called it, with the total number of positive nodes "snowballing" as we return.
The total number returned in the end will be 2.

Two different way of recursion

I calculate fibonachi row in two different ways. Why fib1 executes much longer then fib2?
public class RecursionTest {
#Test
public void fib1() {
long t = System.currentTimeMillis();
long fib = fib1(47);
System.out.println(fib + " Completed fib1 in:" + (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
fib = fib2(47);
System.out.println(fib + " Completed fib2 in:" + (System.currentTimeMillis() - t));
}
long fib1(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fib1(n - 1) + fib1(n - 2);
}
}
long fib2(int n) {
return n == 0 ? 0 : fib2x(n, 0, 1);
}
long fib2x(int n, long p0, long p1) {
return n == 1 ? p1 : fib2x(n - 1, p1, p0 + p1);
}
}
The output:
2971215073 Completed fib1 in:17414
2971215073 Completed fib2 in:0
Because both algorithms work entirely different. Let me show you this with fib(5).
if you call fib1(5), it internally calls fib1(4) und fib1(3), lets visualize that with a tree:
fib(5)
/ \
fib(4) fib(3)
now, fib(4) internally calls fib(3) and fib(2).
So now we have this:
fib(5)
/ \
fib(4) fib(3)
/ \ / \
fib(3) fib(2) fib(2) fib(1)
I think by now it is very obvious where this is going, you should be able to fill in the rest.
edit: Another thing you should notice here is, that it actually has to performe the same caclculation multiple times. In this picture, fib(2) und fib(3) are both called multiple times. And this gets worse if the starting number is bigger./edit
Now, let's take a look at fib2(5). It you call it with 0, it returns 0. Otherwise, it calls fib2x(n, 0,1)
So, we have a call to fib2x(5,0,1). fib2x(n, 0,1) now internally calls fib2x(n-1, p1, p0+p1) and so on.
So, lets see:
fib2x(5, 0,1) => fib2x(4, 1,1) => fib2x(3, 1, 2) => fib2x(2, 2, 3) => fib2x(1, 3, 5)
by then, it has reached the return condition and returns 5.
So, your algorithms work entirely different. The first one works recursively and from the top to the bottom.
The second one starts at 1 and works his way up. Actually, it is more iterative then recursive (it was probably written recursive to throw you off). It keeps the already calculated values instead of discarding them and therefore needs to invoke far less calculations.
The reason is two algorithms have different runtime complexities:
fib1 is in Ο(2n)
fib2 is in Ο(n)
Ultimately, it's because fib2 only uses tail end recursion. It only makes one recursive call at the end. Thus there isn't any "branching" associated with the recursion and leads to a linear time solution. The fact that it's a tail call also leads to certain compiler/VM optimizations where the recursion can be converted into an iterative procedure with lower overhead.
fib1 uses another recursive call in addition to the tail-call which causes the running time to be exponential.
fib1 is an algorithm with O(2^n) runtime. fib2 is an algorithm with O(n) runtime.
The reason for this is pretty cool -- it's a technique called memoization. The work the program does is saved at each step, avoiding any extraneous calculation.
You can see it happen by unrolling the loop a couple more steps:
long fib2(int n) {
return n == 0 ? 0 : fib2x(n, 0, 1);
}
long fib2x(int n, long p0, long p1) {
return n == 1 ? p1 : fib2xy(n - 1, 1, 1);
}
long fib2xy(int n, long p0, long p1) {
return n == 1 ? p1 : fib2xyz(n - 1, 1, 2);
}
long fib2xyz(int n, long p0, long p1) {
return n == 1 ? p1 : fib2xyz(n - 1, p1, p0 + p1);
}
You can unroll this loop to any arbitrary number in the fibonacci sequence; each step builds on the calculation stored previously in the stack until n is depleted. This is in contrast to the first algorithm, which must redo this work at every step. Nifty!

Categories