问题
Why does the Binet formula( O(LogN), but it is not exactly ) work worse in time than the iteration method( O(n) )?
static double SQRT5 = Math.Sqrt(5);
static double PHI = (SQRT5 + 1) / 2;
public static int Bine(int n)
{
return (int)(Math.Pow(PHI, n) / SQRT5 + 0.5);
}
static long[] NumbersFibonacci = new long[35];
public static void Iteracii(int n)
{
NumbersFibonacci[0] = 0;
NumbersFibonacci[1] = 1;
for (int i = 1; i < n - 1; i++)
{
NumbersFibonacci[i + 1] = NumbersFibonacci[i] + NumbersFibonacci[i - 1];
}
}
The time of the algorithms
回答1:
If arithmetic operations are assumed to be O(1) then using Binet's formula is O(1) and the typical iterative implementation is O(n).
However, if we assume arithmetic operations are O(1) then, even though fibo(n)
is a common interview and phone screen topic, it actually makes little sense to implement it in the typical way -- barring being told we are to ignore the finiteness of standard programming language integers and floating point numbers. The Fibonacci numbers grow exponentially. They overflow standard programming language types long before the particular algorithm chosen matters, as long as that is one did not choose the naive recursive implementation.
To get specific, here are two implementations of returning the nth Fibonacci numbers in C#. The top one implements Binet’s closed form solution on doubles and casts to a long, which in C# will be 64 bits wide. The second one is the iterative version:
static long constant_time_fibo(long n)
{
double sqrt_of_five = Math.Sqrt(5.0);
return (long) (
(Math.Pow(1.0 + sqrt_of_five, n) - Math.Pow(1.0 - sqrt_of_five, n)) /
(sqrt_of_five * Math.Pow(2.0, n))
);
}
static long linear_time_fibo(long n)
{
long previous = 0;
long current = 1;
for (int i = 1; i < n; i++)
{
long temp = current;
current = previous + current;
previous = temp;
}
return current;
}
static void Main(string[] args)
{
for (int i = 1; i < 100; i++)
Console.WriteLine("{0} => {1} {2}", i,
constant_time_fibo(i), linear_time_fibo(i) );
}
when I run this code I get the constant time algorithm failing to match the iterative implementation at around n = 72 due to floating point error and the iterative approach failing at n = 92 due to overflow. If I had used 32 bit types instead of 64 bits this would have happened even sooner.
Ninety-two items is nothing. If you need the nth fibonacci number in practice and only care about fibonacci numbers that fit in 64 bits, in a non-contrived situation -- not for a homework assignment or for a whiteboard question -- it should take O(1) time not because of the existence of Binet's formula but because you should use a lookup table with 92 items in it. In C++ you could even generate the 92 items at compile time with a constexpr
function.
If on the other hand if we are talking about arbitrarily large number arithmetic then the question is somewhat more interesting. The exponents in Binet’s formula are all integers. You can implement Binet’s formula using only arbitrarily large integer arithmetic — you do not need to compute any square roots of 5, just need to keep track of “where the square roots of five are” because they are going to cancel out in the end. You calculate in terms of a binomial form like (a+b√5)/c but because of the weird algebraic properties of ϕ all of the irrationality and all of the non-integer math cancels out by magic. You do not need to actually calculate any √5's while finding ϕ^n. If you use “exponentiation by squaring” this will lead to an O(log n) implementation -- O(log n) arithmetic operations anyway; the time complexity of the whole thing would depend on the time complexity of the arbitrary large arithmetic library you are using.
来源:https://stackoverflow.com/questions/59372841/smth-about-binet-formula