Tuesday, March 1, 2016

Project Euler #25 1000-digit Fibonacci number

First I attempted to solve this problem using BigInteger & BigDecimal using bruteforce method, but come on! We know Euler didn't know how to import such classes.
Asymptotically, and precisely, we can get n-th term of Fibonacci number using following formular. Again, I am not going to talk about how to prove this, but for those of who are interested, please take a look at Wikipedia here and Wolfram Alpha here

Basic idea is as follows. I will use LaTex format for mathematical formula from now on.
  1. \(F_n = \lfloor\frac{\psi^n}{\sqrt{5}}\rfloor\), where \(\psi\) is \(\frac{1-\sqrt(5)}{2}\).
  2. To find minimum D-digit \(F_n\) we can set \(F_n = \lfloor\frac{phi^n}{\sqrt(5)}\rfloor < 10^D\).
  3. By taking log on both side, and we get \(n \times \log{\psi} - \log{\sqrt{5}} < D \times \log{10}\)
Using 3., we will iterate from digit 1 to 5000 to find minimum F_n which satisfies the above formula. That's it. O(n).
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

public class Solution3 {

    private static int DATA[] = new int[5002];
    private static int DATA2[] = new int[5002];

    private static void bruteForce() {
        BigInteger bi1 = BigInteger.valueOf(1);
        BigInteger bi2 = BigInteger.valueOf(1);
        BigInteger bi3;
        int term = 3;
        int digit = 1;
        int cur;
        DATA[1] = 1;
        while (digit <= 5000) {
            bi3 = bi1.add(bi2);
            cur = bi3.toString().length();
            if (cur > digit) {
                System.out.println(term);
                ;
                digit = cur;
                DATA[digit] = term;
            }
            bi1 = bi2;
            bi2 = bi3;
            term++;
        }

        int T, N;
        Scanner scanner = new Scanner(System.in);
        T = scanner.nextInt();
        while (T-- > 0) {
            N = scanner.nextInt();
            System.out.println(DATA[N]);
        }
        scanner.close();
    }

    public static void main(String[] args) {
        // bruteForce();
        approximate();
    }

    private static void approximate() {
        int T, N;
        final double OMEGA = (1 + Math.sqrt(5)) / 2;
        // F_n <= Omega^n / sqrt(5) + 0.5 < 10^digit
        // log (F_N) <= n * log(Omega) - log(sqrt(5)) < digit * log 10
        // For digit 1 ... 5000, find n argmax (n * log(Omega) - log(sqrt(5)))

        int term = 1;
        double fn;
        for (int i = 1; i <= 5000; i++) {
            double l = (i * Math.log(10));
            // System.out.println("L=" + l);
            do {
                fn = term * Math.log(OMEGA) - Math.log(Math.sqrt(5));
                // System.out.println("F_" + term + "= " + fn);
                term++;
            } while (l > fn);
            DATA2[i] = term - 1;
        }
        Scanner scanner = new Scanner(System.in);
        T = scanner.nextInt();
        while (T-- > 0) {
            N = scanner.nextInt();
            System.out.println(DATA2[N - 1]);
        }
        scanner.close();
    }
}