Big-O for various Fibonacci Implementations - java

I just tried implementing code (in Java) for various means by which the nth term of the Fibonacci sequence can be computed and I'm hoping to verify what I've learnt.
The iterative implementation is as follows:
public int iterativeFibonacci(int n)
{
if ( n == 1 ) return 0;
else if ( n == 2 ) return 1;
int i = 0, j = 1, sum = 0;
for ( ; (n-2) != 0; --n )
{
sum = i + j;
i = j;
j = sum;
}
return sum;
}
The recursive implementation is as follows :-
public int recursiveFibonacci(int n)
{
if ( n == 1 ) return 0;
else if ( n == 2 ) return 1;
return recursiveFibonacci(n-1) + recursiveFibonacci(n-2);
}
The memoized implementation is as follows :-
public int memoizedFibonacci(int n)
{
if ( n <= 0 ) return -1;
else if ( n == 1 ) return 0;
else if ( n == 2 ) return 1;
if ( memory[n-1] == 0 )
memory[n-1] = memoizedFibonacci(n-1);
if ( memory[n-2] == 0 )
memory[n-2] = memoizedFibonacci(n-2);
return memory[n-1]+memory[n-2];
}
I'm having a bit of a doubt when trying to figure out the Big-O of these implementations. I believe the iterative implementation to be O(n) as it loops through N-2 times.
In the recursive function, there are values recomputed, hence I think it's O(n^2).
In the memoized function, more than half of the values are accessed based on memoization. I've read that an algorithm is O(log N) if it takes constant time to reduce the problem space by a fraction and that an algorithm is O(N) if it takes constant time to reduce the problem space by a constant amount. Am I right in believing that the memoized implementation is O(n) in complexity? If so, wouldn't the iterative implementation be the best among all three? (as it does not use the additional memory that memoization requires).

The recursive version is not polynomial time - it's exponential, tightly bounded at φn where φ is the golden ratio (≈ 1.618034). The recursive version will use O(n) memory (the usage comes from the stack).
The memoization version will take O(n) time on first run, since each number is only computed once. However, in exchange, it also take O(n) memory for your current implementation (the n comes from storing the computed value, and also for the stack on the first run). If you run it many times, the time complexity will become O(M + q) where M is the max of all input n and q is the number of queries. The memory complexity will become O(M), which comes from the array which holds all the computed values.
The iterative implementation is the best if you consider one run, as it also runs in O(n), but uses constant amount of memory O(1) to compute. For a large number of runs, it will recompute everything, so its performance may not be as good as memoization version.
(However, practically speaking, long before the problem of performance and memory, the number is likely to overflow even 64-bit integer, so an accurate analysis must take into account the time it takes to do addition if you are computing the full number).
As plesiv mentioned, the Fibonacci number can also be computed in O(log n) by matrix multiplication (using the same trick as fast exponentiation by halving the exponent at every step).

A java implementation to find Fibonacci number using matrix multiplication is as follows:
private static int fibonacci(int n)
{
if(n <= 1)
return n;
int[][] f = new int[][]{{1,1},{1,0}};
//for(int i = 2; i<=n;i++)
power(f,n-1);
return f[0][0];
}
// method to calculate power of the initial matrix (M = [][]{{1,1},{1,0}})
private static void power(int[][] f, int n)
{
int[][] m = new int[][]{{1,1},{1,0}};
for(int i = 2; i<= n; i++)
multiply(f, m);
}
// method to multiply two matrices
private static void multiply(int[][] f, int[][] m)
{
int x = f[0][0] * m[0][0] + f[0][1] * m[1][0];
int y = f[0][0] * m[0][1] + f[0][1] * m[1][1];
int z = f[1][0] * m[0][0] + f[1][1] * m[1][0];
int w = f[1][0] * m[0][1] + f[1][1] * m[1][1];
f[0][0] = x;
f[0][1] = y;
f[1][0] = z;
f[1][1] = w;
}
Even another faster method than Matrix exponentiation method to calculate Fibonacci number is Fast Doubling method. The amortized time complexity for both the methods is O(logn).
The method follows the following formula
F(2n) = F(n)[2*F(n+1) – F(n)]
F(2n + 1) = F(n)2 + F(n+1)2
One such java implementation for Fast Doubling method is as below:
private static void fastDoubling(int n, int[] ans)
{
int a, b,c,d;
// base case
if(n == 0)
{
ans[0] = 0;
ans[1] = 1;
return;
}
fastDoubling((n/2), ans);
a = ans[0]; /* F(n) */
b = ans[1]; /* F(n+1) */
c = 2*b-a;
if(c < 0)
c += MOD;
c = (a*c) % MOD; /* F(2n) */
d = (a*a + b*b) % MOD ; /* F(2n+1) */
if(n%2 == 0)
{
ans[0] = c;
ans[1] = d;
}
else
{
ans[0] = d;
ans[1] = (c+d);
}
}

Related

Can someone explain this code to me intuitively (finding nCr without overflow error)?

https://www.geeksforgeeks.org/program-to-calculate-the-value-of-ncr-efficiently/
this is the code I want to understand. Here is a video that explains it more in-depth https://www.youtube.com/watch?v=lhXwT7Zm3EU -> however, I still don't understand a certain aspect of it.
Here is the code:
// Java implementation to find nCr
class GFG {
// Function to find the nCr
static void printNcR(int n, int r)
{
// p holds the value of n*(n-1)*(n-2)...,
// k holds the value of r*(r-1)...
long p = 1, k = 1;
// C(n, r) == C(n, n-r),
// choosing the smaller value
if (n - r < r) {
r = n - r;
}
if (r != 0) {
while (r > 0) {
p *= n;
k *= r;
// gcd of p, k
long m = __gcd(p, k);
// dividing by gcd, to simplify
// product division by their gcd
// saves from the overflow
p /= m;
k /= m;
n--;
r--;
}
// k should be simplified to 1
// as C(n, r) is a natural number
// (denominator should be 1 ) .
}
else {
p = 1;
}
// if our approach is correct p = ans and k =1
System.out.println(p);
}
static long __gcd(long n1, long n2)
{
long gcd = 1;
for (int i = 1; i <= n1 && i <= n2; ++i) {
// Checks if i is factor of both integers
if (n1 % i == 0 && n2 % i == 0) {
gcd = i;
}
}
return gcd;
}
// Driver code
public static void main(String[] args)
{
int n = 50, r = 25;
printNcR(n, r);
}
}
Specifically, why does this code work:
if (n - r < r)
r = n - r;
Why, by doing this simple operation, produce the right answer eventually after going through and exiting the main while loop? I don't understand why this is necessary or why it makes sense to do. Like, why would not having this code make the nCr calculation fail or not work the way it's intended???? If someone can either explain this or point me to somewhere that does explain it or the math concept or something that would be great :) Maybe another way of coding the same thing would help. I just want to understand why this produces the right answer as a math and coding student.
To give a bit of perspective on my abilities (so you know what level I'm at), I am learning object-oriented programming, and have completed high school maths and basic computer science. I am by no means an expert.
The nCr operation has a speciality and it is mentioned in the comment above the if condition : // C(n, r) == C(n, n-r). Now, the while loop iterates when r>0 and with each iteration the value of r is decremented by 1. So in order to reduce the number of times the loop is executed, we need to reduce the value of r (if possible). Since C(n, r) == C(n, n-r), we take the smaller value among r and n-r so that the number of iterations are minimized but the result remains the same.
Assume that n = 100 and r=99. In this case if we skip the if condition, then the loop would be executed 99 times, whereas using the if condition we could update r as r = n-r so that r=1, then the loop would have been executed only once. Thus we are saving 98 unwanted iterations.
So there is a big performance improvement if we include the if condition.

What is the time complexity of calculating factorial n! using Java's BigInteger

Suppose the algorithm is as below:
public static BigInteger getFactorial(int num) {
BigInteger fact = BigInteger.valueOf(1);
for (int i = 1; i <= num; i++)
fact = fact.multiply(BigInteger.valueOf(i)); // ? time complexity
return fact;
}
It seems hard to calculate number of digits of fact.
Optimized version:
public BigInteger getFactorial2(long n) {
return subFactorial(1, n);
}
private BigInteger subFactorial(long a, long b) {
if ((b - a) < 10) {
BigInteger res = BigInteger.ONE;
for (long i = a; i <= b; i++) {
res = res.multiply(BigInteger.valueOf(i));
}
return res;
} else {
long mid = a + (b - a) / 2;
return subFactorial(a, mid).multiply(subFactorial(mid + 1, b));
}
}
The number of digits contained in fact is log(fact). It can be shown that O(log(n!)) == O(nlogn), so the number of digits in n! grows proportionally to nlogn. Since your algorithm piles values on to a partial product without splitting them into smaller intermediate values (divide and conquer fashion), we can assert that one of the numbers being multiplied will be less than n for calculating n!. Using grade school multiplication, we have O(logn * nlogn) time to multiply each of these numbers, and we have n-1 multiplies, so this is O(n * logn * nlogn) == O((nlogn)^2). I do believe this is a tight upper bound for grade-school multiplication, because even though the beginning multiplications are far smaller, the latter half are all larger than O((n/2)log^2(n/2)), and there are (n/2) of them, so O((n/2)^2 *log^2(n/2)) == O((nlogn)^2).
However, it is entirely possible that BigInteger uses Karatsuba multiplication, Toom-Cook multiplication, or maybe even the Schönhage–Strassen algorithm. I do not know how these perform on integers of such drastically varying sizes (logn vs nlogn), so I cannot give a tight upper bound on those. The best I can do is speculate that it will be less than that of the O(n*F(nlogn)), where F(x) is the time to multiply two numbers of length x using a specific algorithm.

Unknown issue converting python code to java to calculate number of simple connected graphs

The goal of this code: Number of simple connected graphs with N labeled vertices and K unlabeled edges.
Note: This might be considered as a Code Review problem, but after repeated tries, I think that the python and java code have the same functionality. I'm not sure if there is something wrong with the code, or something to do with language intricacies, or my error in overlooking something.
This was for a Google Foobar challenge. I completed it using the above method. I have posted links to all the source code, that tests all possible cases.
The first method works completely. Only issue - it makes O(NK) recursive calls and K is on average quadratic in N. [Full source]
A friend came up with an algorithm to do the same thing with a bottom-up approach. The main functionalities:
def answerHelper(n,k):
totalGraphs = 0
for s in range(1,n):
graphs = 0
for t in range(0,k+1):
graphs += answer(s, t) * answer(n - s, k - t)
graphs = choose(n, s)*s*(n - s) * graphs
totalGraphs+= graphs
return totalGraphs/2
F = {}
def answer(n, k):
if (n, k) in F:
return F[n, k]
N = n * (n - 1)/2
if k is n - 1:
return int(n ** (n-2))
if k < n or k > N:
return 0
if k == N:
return 1
result = ((N - k + 1) * answer(n, k - 1) + answerHelper(n, k - 1)) / k
F[n, k] = result
return result
The python fails in 4 cases in comparison with the original working Java code [diffchecker]. I presume this is because of some sort of overflow(?). [Full python source]
I am trying to convert this python code to Java. This is what I have come up with.
static Map<List<Integer>, String> resultMap = new HashMap<List<Integer>, String>();
public static String answer(int N, int K) {
/* for the case where K > N-1 */
// check if key is present in the map
List<Integer> tuple = Arrays.asList(N, K);
if( resultMap.containsKey(tuple) )
return resultMap.get(tuple);
// maximum number of edges in a simply
// connected undirected unweighted graph
// with n nodes = |N| * |N-1| / 2
int maxEdges = N * (N-1) / 2;
/* for the case where K < N-1 or K > N(N-1)/2 */
if(K < N-1 || K > maxEdges)
return BigInteger.ZERO.toString();
/* for the case where K = N-1 */
// Cayley's formula applies [https://en.wikipedia.org/wiki/Cayley's_formula].
// number of trees on n labeled vertices is n^{n-2}.
if(K == N-1)
return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();
/* for the case where K = N(N-1)/2 */
// if K is the maximum possible
// number of edges for the number of
// nodes, then there is only one way is
// to make a graph (connect each node
// to all other nodes)
if(K == maxEdges)
return BigInteger.ONE.toString();
// number of edges left from maxEdges if I take away K-1 edges
BigInteger numWays = BigInteger.valueOf(maxEdges - K + 1);
// number of graphs possible for each of the numWays edges for a graph that has 1 less edge
BigInteger numGraphsWithOneLessEdge = new BigInteger( answer(N, K-1) );
// number of all possible subgraphs with K-1 edges
BigInteger subGraphs = answerHelper(N, K-1);
// numWays*numGraphsWithOneLessEdge + subGraphs
BigInteger result = subGraphs.add(numWays.multiply(numGraphsWithOneLessEdge));
// this contains repeats for each of the K edges
result = result.divide(BigInteger.valueOf(K));
// add to cache
resultMap.put(Collections.unmodifiableList(Arrays.asList(N, K)), result.toString());
return resultMap.get(tuple);
}
private static BigInteger answerHelper(int N, int K) {
BigInteger totalGraphs = BigInteger.ZERO;
for(int n = 1 ; n < N ; n++) {
BigInteger graphs = BigInteger.ZERO;
for(int k = 0 ; k <= K ; k++) {
// number of graphs with n nodes and k edges
BigInteger num = new BigInteger( answer(n, k) );
// number of graphs with N-n nodes and K-k edges
BigInteger num2 = new BigInteger( answer(N-n, K-k) );
graphs = graphs.add( num.multiply(num2) );
}
// number of ways to choose n nodes from N nodes
BigInteger choose = choose(N, n);
// this is repeated for each of the n chosen nodes
// and the N-n unchosen nodes
choose = choose.multiply(BigInteger.valueOf(n)).multiply(BigInteger.valueOf(N-n));
totalGraphs = totalGraphs.add( choose.multiply(graphs) );
}
// now, consider the case where N = 20
// when n = 2, we compute for N-n = 18
// when n = 18, we do the same thing again
// hence, contents of totalGraphs is 2 times
// of what it should be
return totalGraphs.divide(BigInteger.valueOf(2));
}
[Full source]
This code, that I intended to function the same as Python, fails multiple cases with respect to the working java code [diffchecker]
I would be very grateful if I can get some guidance.
The issue was in the Java code, not the Python code (I had suspected an overflow; some meticulous debugging proved otherwise. Its not the easiest comparing numbers with 20 odd digits).
The erroneous code:
/* for the case where K = N-1 */
// Cayley's formula applies [https://en.wikipedia.org/wiki/Cayley's_formula].
// number of trees on n labeled vertices is n^{n-2}.
if(K == N-1)
return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();
For N>=17, (long)Math.pow(N, N-2) was not accurate. This happened because with greater double values, the gap between consecutive values increases. A double can't represent every integer value within its range, and that's what's going wrong here. It's returning the closest double value to the exact result. Moreover, for double values, the mantissa is 52 bits, which roughly equals 16(?) places of decimals. Hence the overflowiness (not really a word).
So, the value being returned was smaller than it should have been. Had to replace this with the following code block.
if(K == N-1) {
if(N < 2)
return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();
// multiply N to itself N-2 times
BigInteger val = BigInteger.ONE;
int count = 0;
while(count++ != N-2)
val = val.multiply( BigInteger.valueOf( (long)N ) );
return val.toString();
}

Analyze runtime for polynomial and exponential function

So I have been trying to implement 2 functions:
Method to perform integer polynomial evaluation in O(d) time where d is the degree of the polynomial.
Calculate exponentiation. I'd need it to perform in 0(log b) time
Here is what I've come up so far:
public static int polynomialEvaluation(int[] coefficients, int x){
int n = coefficients.length -1;
int y = coefficients[n];
for (int i = n-1; i >= 0; i--){
y = coefficients[i] + (x*y);
}
return y;
}
public static int exponentiation(int a, int b) {
int res = 1;
while (b > 0) {
res = res * a;
b--;
}
return res;
}
}
Does any of those 2 meet the time complexity requirement? I think I had the exponent function but not sure the cost of the 1st one.
Edited: I rewrote exponential function trying avoid iteration loop as following. It might compute more efficiently now in my opinion
public static int exponentiation(int a, int b) {
if ( b == 0) return 1;
int res = exponentiation(a, b/2);
if (a % 2 == 0)
return res * res;
else
return (a * res * res);
}
Basic algebraic operations (such as addition and multiplication), array lookups, and assignments are all considered to take constant time. Since your code only consists of these operations in a loop, the complexity is the number of iterations of the loop (plus a constant for the operations outside, but that disappears in the O notation). How many iterations do each of your loops perform?
This will hopefully tell you that the polynomial calculation has the desired complexity while the exponential one does not. Hint for a better algorithm: If you have already computed b2, what is the fastest way to use that answer to compute b4? And if you have that result, how can you compute b8? If you have computed e.g. b2, b4, b8, b16, b32, and b64 in this manner (and, of course, you still have the original b1), how can you use these results to compute e.g. b93?

Binary search for square root [homework]

For an assignment I must create a method using a binary search to find the square root of an integer, and if it is not a square number, it should return an integer s such that s*s <= the number (so for 15 it would return 3). The code I have for it so far is
public class BinarySearch {
/**
* Integer square root Calculates the integer part of the square root of n,
* i.e. integer s such that s*s <= n and (s+1)*(s+1) > n
* requires n >= 0
*
* #param n number to find the square root of
* #return integer part of its square root
*/
private static int iSqrt(int n) {
int l = 0;
int r = n;
int m = ((l + r + 1) / 2);
// loop invariant
while (Math.abs(m * m - n) > 0) {
if ((m) * (m) > n) {
r = m;
m = ((l + r + 1) / 2);
} else {
l = m;
m = ((l + r + 1) / 2);
}
}
return m;
}
public static void main(String[] args) {
//gets stuck
System.out.println(iSqrt(15));
//calculates correctly
System.out.println(iSqrt(16));
}
}
And this returns the right number for square numbers, but gets stick in an endless loop for other integers. I know that the problem lies in the while condition, but I can't work out what to put due to the gap between square numbers getting much bigger as the numbers get bigger (so i can't just put that the gap must be below a threshold). The exercise is about invariants if that helps at all (hence why it is set up in this way). Thank you.
Think about it: Math.abs(m*m-n) > 0 is always true non-square numbers, because it is never zero, and .abs cannot be negative. It is your loop condition, that's why the loop never ends.
Does this give you enough info to get you going?
You need to change the while (Math.abs(m * m - n) > 0) to allow for a margin of error, instead of requiring it be exactly equal to zero as you do right now.
Try while((m+1)*(m+1) <= n || n < m * m)
#define EPSILON 0.0000001
double msqrt(double n){
assert(n >= 0);
if(n == 0 || n == 1){
return n;
}
double low = 1, high = n;
double mid = (low+high)/2.0;
while(abs(mid*mid - n) > EPSILON){
mid = (low+high)/2.0;
if(mid*mid < n){
low = mid+1;
}else{
high = mid-1;
}
}
return mid;}
As you can see above , you should simply apply binary search (bisection method)
and you can minimize Epsilon to get more accurate results but it will take more time to run.
Edit: I have written code in c++ (sorry)
As Ken Bloom said you have to have an error marge, 1. I've tested this code and it runs as expected for 15. Also you'll need to use float's, I think this algorithm is not possible for int's (although I have no mathematical proof)
private static int iSqrt(int n){
float l = 0;
float r = n;
float m = ((l + r)/2);
while (Math.abs(m*m-n) > 0.1) {
if ((m)*(m) > n) {
r=m;
System.out.println("r becomes: "+r);
} else {
l = m;
System.out.println("l becomes: "+l);
}
m = ((l + r)/2);
System.out.println("m becomes: "+m);
}
return (int)m;
}

Categories