Fibonacci Sequence in Java taking too long? - java

I'm trying to find the sum of the Fibonacci sequence in Java, but the run time is taking way too long (or is it suppose to?). This slows down anytime I use an integer past 40.
Note: At 50, a negative value is returned which boggles my mind.
Any advice?
public static void main(String[] args) {
//Find Fibonacci sequence
int sum=getSum(50);
System.out.println("Sum of Fibonacci Numbers is " + sum);
}
static int getSum(int n){
if (n==0) return 0;
if (n==1 || n==2) return 1;
else return getSum(n-1) + getSum(n-2);
}

For n > 2, an invocation of your getSum(n) recursively invokes itself twice. Each of those invocations may recurse further. The total number of method invocations scales as 2^n, and 2^50 is a very large number. This poor scaling reflects the fact that the simple-minded recursive approach ends up needlessly recomputing the same results (e.g. fib(4)) a great many times, and it is why your program slows down so rapidly as you increase n.
The negative return value you get after a certain point arises from exceeding the limits of data type int. You could get a larger limit with a wider data type, presumably long. If that's not enough then you would need to go to something like BigInteger, at a substantial performance penalty.

You need to use long instead of int if you want to calculate the 50th Fibonacci number. The 50th Fibonacci number is 12586269025 and exceeds the maximum value of int (see http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.html).
A non-recursive algorithm is likely going to be faster, see http://planet.jboss.org/post/fibonacci_sequence_with_and_without_recursion for the different implementations.

As the others already stated you should use long for the calculated fibonacci value, as the number will get very long very fast.
If your formost priority is performance you could use the following formula:
with
(Idea taken from Linear Algebra lecture, actual formula taken from Wikipedia.)
That way you will get the n-th fibonacci number in constant time (depending on the calculation of the n-th powers in the formula).
The following code calculates the fibonacci sequenc of the first 93 numbers with no waiting time (on my machine):
private static final double SQRT_FIVE = Math.sqrt(5);
private static final double GOLDEN_RATIO = (1 + SQRT_FIVE) / 2;
public static void main(String[] args) {
for(int i = 0; i <= 92; i++) {
System.out.println("fib(" + i + ") = " + calculateFibonacci(i));
}
}
public static long calculateFibonacci(int n) {
double numerator = Math.pow(GOLDEN_RATIO, n) - Math.pow(1-GOLDEN_RATIO, n);
double denominator = SQRT_FIVE;
// This cast should in general work, as the result is always an integer.
// Floating point errors may occur!
return (long)(numerator/denominator);
}
From the 94-th number on the long is no longer sufficent and you need to use BigInteger and fitting math operations, as the double calculations may produce calculation errors with such big numbers.

first, use a long instead of an int, to avoid overflow.
Secondly, use a non-recursive algorithm, as a recursive one exists in exponential time I think. A well designed non-recursive one will solve in linear time (I think).
Example non-recursive
static long getSum(int n){
long[] fibonacci = new long[n];
fibonacci[0] = 1;
fibonacci[1] = 1;
if (n==0) return 0;
if (n==1 || n==2) return 1;
for(int i = 2; i < n;i++){
fibonacci[i] = fibonacci[i-1]+ finonacci[i-2];
}
return fibonacci[n-1];
}
I haven't tested this, but it should work.
If you plan to call this method frequently, it might be prudent to store the array outside of the method, so that it is a simple lookup when doing this. This would provide a constant time solution for numbers that have already been calculated at least once. an example of that is below.
static long[] fibonacci= {1,1};
static long getSum(int n){
if (n==0) return 0;
if (n==1 || n==2) return 1;
int old_length = fibonacci.length;
if(fibonacci.length < (n-1)){
fibonacci = Arrays.copyOf(fibonacci,n);
}else{
return fibonacci[n-1];
}
for(int i = old_length; i < n;i++){
fibonacci[i] = fibonacci[i-1]+ finonacci[i-2];
}
return fibonacci[n-1];
}
Again, the example is untested, so a bit of debugging might be required.
Here is a linear time implementation of the algorithm that uses a constant overhead, instead of linear overhead.
static long getSum(int n){
long currentNum = 0;
long previousNum = 1;
long previousNum2 = 1;
if (n==0) return 0;
if (n==1 || n==2) return 1;
for(int i = 2; i < n;i++){
currentNum = previousNum+ previousNum2;
previousNum2 = previousNum;
previousNum = currentNum;
}
return currentNum;
}

Recursive solutions don't necessarily have to be slow. If you were to use this tail-recursive solution, you'd save up a lot of memory and still achieve great speed (e.g. Fib(10000) runs in 1.1s on my machine).
Here n is the sequence number for which you're calculating Fibonacci number, while f0 and f1 are two accumulators, for previous and current Fibonacci numbers respectively.
public class FibonacciRec {
public static int fib(int n, int f0, int f1) {
if (n == 0) {
return f0;
} else if (n == 1){
return f1;
} else {
return fib(n-1, f1, f0+f1);
}
}
public static void main(String[] args) {
System.out.println(fib(10, 0, 1));
}
}

If you want to keep the recursive approach as is, cache results of calculation in an array or map. When you have calculated one Fibonacci for n, save that result. Then, in your method first see if you have the result and return that if you do. Otherwise, make the recursive call(s). Here's an example: recursion is still used and it is quite fast:
public static Map<Long,Long> cache = null;
public static void main(String[] args) {
cache = new HashMap<Long,Long>();
cache.put(0L,0L);
cache.put(1L,1L);
cache.put(2L,1L);
Long sum=getSum(50L);
System.out.println("Sum of Fibonacci Numbers is " + sum);
}
static Long getSum(Long n){
if (cache.containsKey(n)) { return cache.get(n); }
else {
Long fib = getSum(n-1) + getSum(n-2);
cache.put(n, fib);
return fib;
}
}

Related

What is the largest prime factor of the number 600851475143(Java)? What could be my mistake?

The code works just fine for the int data type, but 600851475143 seems to be too big of a number. How can I make it work? It just keeps running and never gives me an answer.
public class Main {
public static void main(String[] args) {
long a = 600851475143L;
boolean prime = false;
long big = 0L;
for (long i = 1L; i < a; i++){
if (a % i == 0){
for (int j = 2; j < i/(float)2; j++){
if (i % j == 0){
prime = true;
break;
}
}
if(!prime){
big = i;
}
}
}
System.out.println(big);
}
}
You need to use a long also for j.
Your variable naming is also a bit misleading: prime is true when the number is not a prime ...
Your code has a lot of problems. My first advice would be to write clean and concise code with well-named variables. Secondly, analyze the runtime complexity of your program even if it works fast for large inputs. The fact that you run an inner loop inside if(a % i == 0) condition, makes your program extremely inefficient.
Here I provide a refactored version of your code with runtime complexity and good variable names in mind:
public static void main(String[] args) {
System.out.println(largestPrimeFactorOf(600851475143L));
}
public static long largestPrimeFactorOf(long input)
{
List<Long> factors = new ArrayList<>();
// You start from 2, as 1 is not a prime number and also using 1 will cause an infinite loop
for (long i = 2L; i < input; i++) {
if (input % i == 0) {
factors.add(i);
while (input % i == 0) {
input /= i;
}
}
}
// As we always add a bigger number to the factor list, the last element is the greatest factor.
return factors.get(factors.size() - 1);
}
Denote that this program will still be slow when the input is a large prime number, e.g. 100000000019. To handle such cases efficiently, it's better to use an efficient primality test algorithm. You can search the web for that.
https://en.wikipedia.org/wiki/Primality_test

Need explanation of looping java recursion code

This code is for finding the smallest number divisible by all num from 1 to 20
I dont understand how this code works on recursion can u pleases explain this it wil be helpful
static long gcd(long a, long b)
{
if(a%b != 0)
return gcd(b,a%b);
else
return b;
}
// Function returns the lcm of first n numbers
static long lcm(long n)
{
long ans = 1;
for (long i = 1; i <= n; i++)
ans = (ans * i)/(gcd(ans, i));
return ans;
}
// Driver program to test the above function
public static void main(String []args)
{
long n = 20;
System.out.println(lcm(n));
}
This is very mathy, but gcd stands for "greatest common denominator" and lcm stands for "lowest common multiple".
The main algorithm keeps track of the current lowest common multiple "ans", and iterates "i" from 1 to "n" which in this case is 20. It then multiplies the current value by each "i" and divides by the greatest common denominator between "ans" and "i".
The gcd() method uses Euclid's algorithm to calculate the greatest common denominator
The reason that algorithm works is more a question for https://math.stackexchange.com/

I need to print the 215th Lucas Numbers using an efficient algorithm. Using recursion takes way too long.

The purpose of this class is to calculate the nth number of the Lucas Sequence. I am using data type long because the problems wants me to print the 215th number. The result of the 215th number in the Lucas Sequence is: 855741617674166096212819925691459689505708239. The problem I am getting is that at some points, the result is negative. I do not understand why I am getting a negative number when the calculation is always adding positive numbers. I also have two methods, since the question was to create an efficient algorithm. One of the methods uses recursion but the efficiency is O(2^n) and that is of no use to me when trying to get the 215th number. The other method is using a for loop, which the efficiency is significantly better. If someone can please help me find where the error is, I am not sure if it has anything to do with the data type or if it is something else.
Note: When trying to get the 91st number I get a negative number and when trying to get the 215th number I also get a negative number.
import java.util.Scanner;
public class Problem_3
{
static long lucasNum;
static long firstBefore;
static long secondBefore;
static void findLucasNumber(long n)
{
if(n == 0)
{
lucasNum = 2;
}
if(n == 1)
{
lucasNum = 1;
}
if(n > 1)
{
firstBefore = 1;
secondBefore = 2;
for(int i = 1; i < n; i++)
{
lucasNum = firstBefore + secondBefore;
secondBefore = firstBefore;
firstBefore = lucasNum;
}
}
}
static long recursiveLucasNumber(int n)
{
if(n == 0)
{
return 2;
}
if(n == 1)
{
return 1;
}
return recursiveLucasNumber(n - 1) + recursiveLucasNumber(n - 2);
}
public static void main(String[] args)
{
System.out.println("Which number would you like to know from "
+ "the Lucas Sequence?");
Scanner scan = new Scanner(System.in);
long num = scan.nextInt();
findLucasNumber(num);
System.out.println(lucasNum);
//System.out.println(recursiveLucasNumber(num));
}
}
Two observations:
The answer you are expecting (855741617674166096212819925691459689505708239) is way larger than you can represent using a long. So (obviously) if you attempt to calculate it using long arithmetic you are going to get integer overflow ... and a garbage answer.
Note: this observation applies for any algorithm in which you use a Java integer primitive value to represent the Lucas numbers. You would run into the same errors with recursion ... eventually.
Solution: use BigInteger.
You have implemented iterative and pure recursion approaches. There is a third approach: recursion with memoization. If you apply memorization correctly to the recursive solution, you can calculate LN in O(N) arithmetical operations.
Java data type long can contain only 64-bit numbers in range -9223372036854775808 .. 9223372036854775807. Negative numbers arise due to overflow.
Seems you need BigInteger class for arbitrary-precision integer numbers
I wasn't aware of the lucas numbers before this thread, but from wikipedia it looks like they are related to the fibonacci sequence with (n = nth number, F = fibonacci, L = lucas):
Ln = F_(n-1) + F_(n+1)
Thus, if your algorithm is too slow, you could use the closed form fibonacci and than compute the lucas number from it, alternative you could also use the closed form given in the wikipedia article directly (see https://en.wikipedia.org/wiki/Lucas_number).
Example code:
public static void main(String[] args) {
long n = 4;
double fibo = computeFibo(n);
double fiboAfter = computeFibo(n + 1);
double fiboBefore = computeFibo(n - 1);
System.out.println("fibonacci n:" + Math.round(fibo));
System.out.println("fibonacci: n+1:" + Math.round(fiboAfter));
System.out.println("fibonacci: n-1:" + Math.round(fiboBefore));
System.out.println("lucas:" + (Math.round(fiboAfter) + Math.round(fiboBefore)));
}
private static double computeFibo(long n) {
double phi = (1 + Math.sqrt(5)) / 2.0;
double psi = -1.0 / phi;
return (Math.pow(phi, n) - Math.pow(psi, n)) / Math.sqrt(5);
}
To work around the long size limit you could use java BigDecimal (https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html). This is needed earlier in this approach as the powers in the formula will grow very quickly.

Different results from similar methods

In my class I tried to return the sum of: 2^n + 2^(n+1) + 2^(n+2) ... in 2 ways.
Iterative in the first method and recursive in the 2nd one.
This worked as long as numbers weren't too big. Can someone explain to me why those methods return different answers when used with high numbers?
public class Power
{
public static void main(String[] args)
{
System.out.println(iterativ(3));
System.out.println(rekursiv(3));
System.out.println(iterativ(40));
// The recursive one is lower by 10
System.out.println(rekursiv(40));
}
public static int iterativ(int x)
{
int sum = 0;
for (int i = 0; i <= x; i++) {
sum += Math.pow(2, i);
}
return sum;
}
public static int rekursiv(int x)
{
if (x > 0) {
return ((int) Math.pow(2, x) + rekursiv(x - 1));
}
return 1;
}
}
You are using functions that deal with double. You are casting your values to int. Casting values will sooner or later always lead to some inaccurate results, even more so, if you cast from double to int.
The number you were seeing when using 40 as your exponent, was 2147483647, which is in fact Integer.MAX_VALUE, but isn't 2^40. It is rather 2^31-1. The java tutorial has a chapter about the primitive datatypes, which shows you the ranges of each type.
Besides using double you may also want to look at BigDecimal instead.

Possible multiplications of k distinct factors with largest possible factor n

Let M(n,k) be the sum of all possible multiplications of k distinct factors with largest possible factor n, where order is irrelevant.
For example, M(5,3) = 225 , because:
1*2*3 = 6
1*2*4 = 8
1*2*5 = 10
1*3*4 = 12
1*3*5 = 15
1*4*5 = 20
2*3*4 = 24
2*3*5 = 30
2*4*5 = 40
3*4*5 = 60
6+8+10+12+15+20+24+30+40+60 = 225.
One can easily notice that there are C(n,k) such multiplications, corresponding to the number of ways one can pick k objects out of n possible objects. In the example above, C(5,3) = 10 and there really are 10 such multiplications, stated above.
The question can also be visualized as possible n-sized sets containing exactly k 0's, where each cell that does not contain 0 inside it, has the value of its index+1 inside it. For example, one possible such set is {0,2,3,0,5}. From here on, one needs to multiply the values in the set that are different than 0.
My approach is a recursive algorithm. Similiarly to the above definition of
M(n,k), I define M(n,j,k) to be the sum of all possible multiplications of exactly k distinct factors with largest possible factor n, AND SMALLEST possible factor j. Hence, my approach would yield the desired value if ran on
M(n,1,k). So I start my recursion on M(n,1,k), with the following code, written in Java:
public static long M (long n, long j, long k)
{
if (k==1)
return usefulFunctions.sum(j, n);
for (long i=j;i<=n-k+1+1;i++)
return i*M(n,i+1,k-1);
}
Explanation to the code:
Starting with, for example, n=5 , j=1, k=3, the algorithm will continue to run as long as we need more factors, (k>=1), and it is made sure to run only distinct factors thanks to the for loop, which increases the minimal possible value j as more factors are added. The loop runs and decreases the number of needed factors as they are 'added', which is achieved through applying
M(n,j+1,k-1). The j+1 assures that the factors will be distinct because the minimal value of the factor increases, and k-1 symbolizes that we need 1 less factor to add.
The function 'sum(j,n)' returns the value of the sum of all numbers starting from j untill n, so sum(1,10)=55. This is done with a proper, elegant and simple mathematical formula, with no loops: sum(j,n) = (n+1)*n/2 - (i-1)*i/2
public static long sum (long i, long n)
{
final long s1 = n * (n + 1) / 2;
final long s2 = i * (i - 1) / 2;
return s1 - s2 ;
}
The reason to apply this sum when k=1, I will explain with an example:
Say we have started with 1*2. Now we need a third factor, which can be either of 3,4,5. Because all multiplications: 1*2*3, 1*2*4, 1*2*5 are valid, we can return 1*2*(3+4+5) = 1*2*sum(3,5) = 24.
Similiar logic explains the coefficient "i" next to the M(n,j+1,k-1).
say we have now the sole factor 2. Hence we need 2 more factors, so we multiply 2 by the next itterations, which should result in:
2*(3*sum(4,5) + 4*sum(5,5))
However, for a reason I can't explain yet, the code doesn't work. It returns wrong values and also has "return" issues that cause the function not to return anything, don't know why.
This is the reason i'm posting this question here, in hope someone will aid me. Either by fixing this code or sharing a code of his own. Explaining where I'm going wrong will be most appreciable.
Thanks a lot in advance, and sorry for this very long question,
Matan.
-----------------------EDIT------------------------
Below is my fixed code, which solves this question. Posting it incase one should ever need it :) Have fun !
public static long M(long n, long j, long k)
{
if (k == 0)
return 0;
if (k == 1)
return sum(j,n);
else
{
long summation = 0;
for (long i=j; i<=n; i++)
summation += i*M(n, i+1, k-1);
return summation;
}
}
I see that u got ur answer and i really like ur algorithm but i cant control myself posting a better algorithm . here is the idea
M(n,k) = coefficient of x^k in (1+x)(1+2*x)(1+3*x)...(1+n*x)
u can solve above expression by divide and conquer algorithm Click Here to find how to multiply above expression and get polynomial function in the form of ax^n + bx^(n-1)....+c
overall algorithm time complexity is O(n * log^2 n)
and best part of above algorithm is
in the attempt of finding solution for M(n,k) , u will find solution for M(n,x) where 1<=x<=n
i hope it will be useful to know :)
I am not sure about your algorithm, but you certainly messed up with your sum function. The problem you have is connected to type casting and division of integer numbers. Try something like this:
public static long sum (long i, long n)
{
final long s1 = n * (n + 1) / 2;
final long s2 = (i * i - i) / 2;
return s1 - s2 ;
}
You have a problem with your sum function : here is the correct formula:
public static long sum (long i, long n)
{
double s1 = n*(n+1)/2;
double s2 = i*(i-1)/2;
return (long)(s1-s2);
}
Here the full solution :
static int n = 5;
static long k = 3;
// no need to add n and k them inside your M function cause they are fixed.
public static long M (long start) // start = 1
{
if(start > k) // if start is superior to k : like your example going from 1..3 , then you return 0
return 0;
int res = 0; // res of your function
for(long i=start+1;i<n;i++){
res+=start*i*sum(i+1,n); // here you take for example 1*2*sum(3,5) + 1*3*sum(4,5).... ect
}
return res+M(start+1); // return res and start again from start+1 wich would be 2.
}
public static long sum (long i, long n)
{
if(i>n)
return 0;
double s1 = n*(n+1)/2;
double s2 = i*(i-1)/2;
return (long)(s1-s2);
}
public static void main(String[] args) {
System.out.println(M(1));
}
Hope it helped

Categories