2个斐波那契数的乘积

Product of 2 fibonacci numbers

我的作业:

Given a number, say prod (for product), we search two Fibonacci numbers F(n) and F(n+1) verifying

F(n) * F(n+1) = prod if F(n) * F(n+1) = prod

Your function productFib takes an integer (prod) and returns an array:

[F(n), F(n+1), True] else 

F(m) being the smallest one such as F(m) * F(m+1) > prod

[F(m), F(m+1), False] 

Examples:

productFib(714) # should return [21, 34, True], 
            # since F(8) = 21, F(9) = 34 and 714 = 21 * 34

productFib(800) # should return [34, 55, False], 
            # since F(8) = 21, F(9) = 34, F(10) = 55 and 21 * 34 < 800 < 34 * 55

我的代码:

def f(i):
    if i == 0 :
        return 0
    if i == 1 :
        return 1
    return f(i-2) + f(i-1)

def productFib(prod):
    i=1
    final1 = 0
    final2 = 0
    while(f(i)*f(i+1) != prod and f(i)*f(i+1) < prod):
        i += 1
        final1 = f(i)
        final2 = f(i+1)
    if(final1*final2 == prod):
        return [final1,final2,True]
    else:
        return [final1,final2,False]

我是编程新手;这对于大量运行非常慢。如何降低时间复杂度?

您的 fib 函数每次都从底部重新计算。您应该保存您已经知道的值。我的 python 生锈了,但我认为这样做就可以了:

dict = {}

def f(i):
    if dict.has_key(i):
        return dict[i]
    if i == 0 :
        return 0
    if i == 1 :
    return 1
    sum = f(i-2) + f(i-1)
    dict[i] = sum
    return sum

def productFib(prod):
    i=1
    final1 = 0
    final2 = 0
    while(f(i)*f(i+1) != prod and f(i)*f(i+1) < prod):
        i += 1
        final1 = f(i)
        final2 = f(i+1)
    if(final1*final2 == prod):
        return [final1,final2,True]
    else:
        return [final1,final2,False]

首先,您的 f 函数非常耗时:它计算 f(n) for low n 多次。 Memoize功能:将结果保存在一个列表中,再次计算时只需参考该列表即可。

memo = [1, 1]

def f(i):
    global memo

    if i >= len(memo):
        # Compute all f(k) where k < i
        f_of_i = f(i-2) + f(i-1)
        memo.append(f_of_i)

    return memo[i]

请注意,此序列仍然需要您按数字顺序填写 memo:f(i-2) 在 f(i- 1), 两者都在将 f(i) 添加到列表之前被调用。

用这个 returns 立即调用 f(100000000000000) (10^14)。我还没有尝试过更高的数字。

更新

我运行它以10的幂递增。在10^1650时,它还在全速打印输出,我打断了运行。

改进

通过直接从闭式方程计算 f(i),您可以做得更好(对于许多应用程序):

root5 = 5 ** 0.5
phi = (1 + root5) / 2
psi = (1 - root5) / 2

def f(i):
    return int(round((phi ** i - psi ** i) / root5))

更多改进

直接计算i的正确值。 f(i) * f(i+1) 非常接近 phi**(2i+1) / 5

def productFib(prod):
    power = math.log(prod*5) / log_phi
    i = int(round(power-1, 7)/2) + 1
    low  = f(i)
    high = f(i+1)
    # print i, low, high
    answer = [low, high, low*high == prod]
    return answer

print productFib(714)
print productFib(800)
print productFib(100000000000000000)

输出:

[21, 34, True]
[34, 55, False]
[267914296, 433494437, False]

您可以将以下代码用于您的 ProductFib 函数

def productFib2(prod):
    i=0
    final1 = f(i)
    final2 = f(i+1)
    while(final1 * final2 < prod):
        i += 1
        final1 = final2
        final2 = f(i+1)
    if(final1*final2 == prod):
        return [final1,final2,True]
    else:
        return [final1,final2,False]

您可以通过使用生成器大大提高性能。

def fib():
    # Generator that yields the last two fibonacci numbers on each iteration.
    # Initialize
    np = 0
    n = 1

    # Loop forever. The main function will break the loop.
    while True:
        # Calculate the next number
        nn = np + n

        # Yield the previous number and the new one.
        yield n, nn

        # Update the generator state.
        np = n
        n = nn

def product_fib(prod):
    # Loop through the generator.
    for f_0, f_1 in fib():
        # If the product is larger or equal to the product return.
        if f_0 * f_1 >= prod:
            # The last element in the list is the resut of the equality check.
            return [f_0, f_1, f_0 * f_1 == prod]


t0 = perf_counter()
res = productFib(8000000000)
t = perf_counter() - t0
print("Original:", t, res)

t0 = perf_counter()
res = product_fib(8000000000)  # 8000000000
t = perf_counter() - t0
print("New:", t, res)

这个的输出是

Original: 0.8113621789962053 [75025, 121393, False]
New: 1.3276992831379175e-05 [75025, 121393, False]

编辑

如果你想要单行。它有效,但不要使用它,它不是真正最实用的解决方案。无论如何第一个更快。

print((lambda prod: (lambda n: next(([a, b, (a * b) == prod] for a, b in ([n.append(n[0] + n[1]), n.pop(0), n][-1] for _ in iter(int, 1)) if (a * b) >= prod)))([0, 1]))(714))
def productFib(prod):
    a, b = 0, 1
    while prod > a * b:
        a, b = b, a + b
    return [a, b, prod == a * b]

这里有几个不同的算法,它们的优化程度越来越高(从原始算法开始)。

算法 3 使用带缓存的迭代斐波那契 以及猜测平方根(它应该在斐波那契乘积的两个倍数之间。

计时结果:

Original algorithm:                          4.43634369118898
Algorithm 2 (recursive fib):                 1.5160450420565503
Algorithm 2 (iter fib):                      0.03543769357344395
Algorithm 2 (iter fib + caching):            0.013537414072276377
Algorithm 3 (iter fib + caching + guessing): 0.0017255337946799898

设置:

import timeit

from random import randint
from math import sqrt
from bisect import bisect

cache = [0, 1]

第 N 斐波那契数算法:

def f(n):
    """Recursive solution: O(2^n)"""
    if n == 0:
        return 0
    if n == 1:
        return 1
    return f(n-2) + f(n-1)


def ff(n):
    """Iterative solution: O(n)"""
    a, b = 0, 1
    for _ in range(0, n):
        a, b = b, a + b
    return a


def fff(n):
    """Iterative solution + caching"""
    length = len(cache)
    if n >= length:
        for i in range(length, n+1):
            cache.append(cache[i-1] + cache[i-2])
    return cache[n]

斐波那契乘积算法:

def pf1(prod):
    """Original algorithm."""
    i=1
    final1 = 0
    final2 = 0
    while(f(i)*f(i+1) != prod and f(i)*f(i+1) < prod):
        i += 1
        final1 = f(i)
        final2 = f(i+1)
    if(final1*final2 == prod):
        return [final1,final2,True]
    else:
        return [final1,final2,False]


def pf2(prod, fib_func):
    """Algorithm 2.

    Removes extra logic and duplicate function calls."""
    i = 1
    guess = 0
    while guess < prod:
        final1 = fib_func(i)
        final2 = fib_func(i + 1)
        guess = final1 * final2
        i += 1

    return [final1, final2, guess == prod]


def pf3(prod, fib_func):
    """Algorithm 3.

    Implements square root as a guess."""
    guess = sqrt(prod)  # Numbers will be near square root

    i = 2
    while cache[-1] < guess:
        fff(i)
        i += 1

    insertion_spot = bisect(cache, guess)
    test1 = cache[insertion_spot-1]
    test2 = cache[insertion_spot]

    return [test1, test2, (test1 * test2) == prod]

测试与输出:

prods = [randint(3, 99999) for _ in range(1000)]  # 1000 random products
print("Original algorithm:                         ", timeit.timeit("for prod in prods: pf1(prod)", number=1, setup="from __main__ import pf1, prods"))
print("Algorithm 2 (recursive fib):                ", timeit.timeit("for prod in prods: pf2(prod, f)", number=1, setup="from __main__ import pf2, prods, f"))
print("Algorithm 2 (iter fib):                     ", timeit.timeit("for prod in prods: pf2(prod, ff)", number=1, setup="from __main__ import pf2, prods, ff"))
print("Algorithm 2 (iter fib + caching):           ", timeit.timeit("for prod in prods: pf2(prod, fff)", number=1, setup="from __main__ import pf2, prods, fff"))
print("Algorithm 3 (iter fib + caching + guessing):", timeit.timeit("for prod in prods: pf3(prod, fff)", number=1, setup="from __main__ import pf3, prods, fff, cache; cache = [0, 1]"))

Python"List"对象的有用时间复杂度:https://wiki.python.org/moin/TimeComplexity

两种方式:慢速方式和快速方式

慢:

def productFib(prod):
    i=0
    final1 = f(i)
    final2 = f(i+1)
    while(final1 * final2 < prod):
        i += 1
        final1 = final2 #this is the same as final1 = f(i) again but, the value is already in memory no need to recalc it
        final2 = f(i+1)
    if(final1*final2 == prod):
        return [final1,final2,True]
    else:
        return [final1,final2,False] #if non equivilant product
def f(i): #fib(0) == 0 fib(1) == 1
    if i == 0 :
        return 0
    if i == 1 :
        return 1
    return f(i-2) + f(i-1)

while 循环中产品的输出: 0 1个 2个 6个 15

快:

def productFib(prod):
    a, b = 0, 1
    while prod > a * b:

 1. List item

        a, b = b, a + b #a is equal to b and b is equal to the sum of a and b
    return [a, b, prod == a * b] #return a and b as well as the conditional if a times b is equal to the product

你不需要检查这里是否有 fib 系列的一部分,因为我们可以简单地在 while 循环中迭代系列,而不必递归调用另一个函数 fib 函数定义为:F(n) = F(n-1) + F(n-2),其中 F(0) = 0 且 F(1) = 1。因此,您可以使用以下算法生成此函数上面的 while 循环 接着 a = 0, b = 1 接下来 a = 1, b = 1 接下来 a = 1, b = 2 接下来 a = 2, b = 3 接下来 a = 3, b = 5 ...