Fibonacci Memoized/Dynamic Programming in Java - 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?

Related

How to properly handle max/min int value edge case-Java

I am working on a problem from LeetCode (not an interview question just practicing) that asks the following:
Given a sorted integer array nums, where the range of elements are in the inclusive range [lower, upper], return its missing ranges.
The code that I came up with fails for inputs where the nums array is [-2147483648,2147483647] and lower/upper are -2147483648/2147483647 respectively. The part of my code that actually answers the question is:
if (nums[0]-lower > 1) {
String range = lower + "->" + (nums[0]-1);
ans.add(range);
}
else if (nums[0]-lower == 1) {
String range = new Integer(lower).toString();
ans.add(range);
}
for (int i = 1; i < nums.length; i++) {
if (nums[i] - nums[i-1] > 2) {
String range = nums[i-1]+1 + "->" + (nums[i]-1);
ans.add(range);
}
else if (nums[i] - nums[i-1] == 2) {
String range = new Integer(nums[i]-1).toString();
ans.add(range);
}
}
I was wondering how best to handle this edge case, not just for this question but generally. Do I just add extra if-statements to my code to specifically handle these two numbers (or if addition/subtraction of numbers causes the int value to overflow) or is there a more elegant way to handle this?
The maximum value of an int is 231-1 which is 2147483647, but the difference between that number and any negative number is larger than that number itself.
So all your subtraction expressions like nums[0]-lower overflow with [-2147483648,2147483647] (or [-1,2147483647]).
You can check it with this:
System.out.println(2147483647 - -1);
This prints out -2147483648 even though you would expect it to be 2147483648.
One easy fix is to do the calculations as a 64-bit long. Change all your subtractions like below to cast the expression to long.
if (nums[0] - (long)lower > 1) {
Take the above example and change it to:
System.out.println(2147483647 - (long) -1);
This will correctly print 2147483648.

Evaluation order in 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.

Determining the Big O of a recursive method with two recursive cases?

I am currently struggling with computing the Big O for a recursive exponent function that takes a shortcut whenever n%2 == 0. The code is as follows:
public static int fasterExponent(int x, int n){
if ( n == 0 ) return 1;
if ( n%2 == 0 ){
int temp = fasterExponent(x, n/2);
return temp * temp;
}
return x * fasterExponent(x, --n); //3
}
I understand that, without the (n%2 == 0) case, this recursive exponent function would be O(n). The inclusion of the (n%2 == 0) case speeds up the execution time, but I do not know how to determine neither its complexity nor the values of its witness c.
The answer is O(log n).
Reason: fasterExponent(x, n/2) this halfes the input in each step and when it reaches 0 we are done. This obviously needs log n steps.
But what about fasterExponent(x, --n);? We do this when the input is odd and in the next step it will be even and we fall back to the n/2 case. Let's consider the worst case that we have to do this every time we divide n by 2. Well then we do the second recursive step once for every time we do the first recursive step. So we need 2 * log n operations. That is still O(log n).
I hope my explanation helps.
It's intuitive to see that at each stage you are cutting the problem size by half. For instance, to find x4, you find x2(let's call this A), and return the result as A*A. Again x2 itself is found by dividing it into x and x.
Considering multiplication of two numbers as a primitive operation, you can see that the recurrence is:
T(N) = T(N/2) + O(1)
Solving this recurrence(using say the Master Theorem) yields:
T(N) = O(logN)

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.

Categories