【问题标题】:How to find the nearest Fibonacci Series number?如何找到最近的斐波那契数列数?
【发布时间】:2017-04-02 15:37:18
【问题描述】:

我的下一步是,如果输入不在斐波那契数列中,程序必须给出一个输出,其中的数字是最接近输入的数列。我不知道该怎么做,谁能帮帮我?

def fibs():
    a,b = 0,1
    yield a
    yield b
    while True:
        a,b = b,a+b
        yield b

n = int(input("please, enter a number: "))
for fib in fibs():
    if n == fib:
        print("Yes! Your number is a Fibonacci number!")
        break
    if fib > n:
        print("No! Your number is not a Fibonacci number!")
        break

【问题讨论】:

  • 你可以有一个保留旧数字的变量,并将它与input 比较,看看旧变量是否更接近输入或新生成的变量更接近....跨度>
  • 如果 (5*n^2 + 4) 或 (5*n^2 – 4) 是一个完美的正方形,那么 n 是一个斐波那契数。您可以使用它来快速计算非常大的斐波那契数。
  • 我在答案中添加了更快的算法,以及一些比较各种主要算法速度的计时代码。
  • 是否可以找到我输入的位置或系列中最近的斐波那契数?我通过输入第 n 个找到斐波那契数的值的方法,但不是相反

标签: python fibonacci


【解决方案1】:

这是一种使用生成器的简单方法,可用于测试小数字。

def fibs():
    a,b = 0,1
    yield a
    yield b
    while True:
        a,b = b,a+b
        yield b

def nearest_fib(n):
    ''' If n is a Fibonacci number return True and n
        Otherwise, return False and the nearest Fibonacci number
    '''
    for fib in fibs():
        if fib == n:
            return True, n
        elif fib < n:
            prev = fib
        else:
            # Is n closest to prev or to fib?
            if n - prev < fib - n:
                return False, prev
            else:
                return False, fib

# Test
for i in range(35):
    print(i, nearest_fib(i))

输出

0 (True, 0)
1 (True, 1)
2 (True, 2)
3 (True, 3)
4 (False, 5)
5 (True, 5)
6 (False, 5)
7 (False, 8)
8 (True, 8)
9 (False, 8)
10 (False, 8)
11 (False, 13)
12 (False, 13)
13 (True, 13)
14 (False, 13)
15 (False, 13)
16 (False, 13)
17 (False, 21)
18 (False, 21)
19 (False, 21)
20 (False, 21)
21 (True, 21)
22 (False, 21)
23 (False, 21)
24 (False, 21)
25 (False, 21)
26 (False, 21)
27 (False, 21)
28 (False, 34)
29 (False, 34)
30 (False, 34)
31 (False, 34)
32 (False, 34)
33 (False, 34)
34 (True, 34)

更新

这是一种更有效的方法,它使用Binet's formula 来首先近似 y:F(y) = n。然后它使用一对与matrix form 相关的身份(可以在 O(log(n)) 时间内计算 F(n))递归地找到最接近 n 的斐波那契数。递归非常快,因为它使用缓存来保存已经计算过的值。如果没有缓存,此算法的速度与 Rockybilly 的大致相同。

from math import log, sqrt

def fast_fib(n, cache={0: 0, 1: 1}):
    if n in cache:
        return cache[n]
    m = (n + 1) // 2
    a, b = fast_fib(m - 1), fast_fib(m)
    fib = a * a + b * b if n & 1 else (2 * a + b) * b
    cache[n] = fib
    return fib

logroot5 = log(5) / 2
logphi = log((1 + 5 ** 0.5) / 2)

def nearest_fib(n):
    if n == 0:
        return 0
    # Approximate by inverting the large term of Binet's formula
    y = int((log(n) + logroot5) / logphi)
    lo = fast_fib(y)
    hi = fast_fib(y + 1)
    return lo if n - lo < hi - n else hi

for i in range(35):
    print(i, nearest_fib(i))

输出

0 0
1 1
2 2
3 3
4 5
5 5
6 5
7 8
8 8
9 8
10 8
11 13
12 13
13 13
14 13
15 13
16 13
17 21
18 21
19 21
20 21
21 21
22 21
23 21
24 21
25 21
26 21
27 21
28 34
29 34
30 34
31 34
32 34
33 34
34 34

请注意,fast_fib 使用 default mutable argument 作为缓存,但这没关系,因为我们希望缓存记住它之前的内容。

在我的速度测试中,默认的可变参数缓存比任何其他形式的缓存都快,但它的缺点是无法从函数外部清除缓存,并且如果您向函数添加逻辑以清除缓存当您不想想要清除缓存时,会影响大多数调用的性能。

更新

实际上可以从函数外部清除默认的可变参数缓存。我们可以通过函数的.__default__ 属性访问函数的默认参数(或在旧版本的 Python 2 中为 .func_defaults.__default__ 在 Python 2.6 中有效,但在 2.5 中无效)。

例如,

d = fast_fib.__defaults__[0]
d.clear()
d.update({0: 0, 1: 1})

这里有一些代码(在 Python 2 和 Python 3 上运行)对为此问题提交的一些算法执行时序测试。 Rockybilly 与我的第一个版本非常相似,只是它避免了保存以前的值。我还使 OP 的 fibs 生成器更加紧凑。

Douglas 的代码适用于小数,或者当参数实际上是一个斐波那契数时,但对于大型非斐波那契数,它会变得非常慢,因为逐一缓慢一搜。通过避免重新计算各种数量,我已经能够对其进行一些优化,但这对运行速度并没有太大的影响。

在这个版本中,我的fast_fib() 函数使用全局缓存,以便可以在测试之间清除它以使时间更公平。

#!/usr/bin/env python3

""" Find the nearest Fibonacci number to a given integer

    Test speeds of various algorithms

    See https://stackoverflow.com/questions/40682947/fibonacci-in-python

    Written by PM 2Ring 2016.11.19
    Incorporating code by Rockybilly and Douglas
"""

from __future__ import print_function, division
from math import log, sqrt
from time import time

def fibs():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

def nearest_fib_Rocky(n):
    ''' Find the nearest Fibonacci number to n '''
    fibgen = fibs()
    for fib in fibgen:
        if fib == n:
            return n
        elif fib > n:
            next_fib = next(fibgen)
            return next_fib - fib if 2 * n < next_fib else fib

def nearest_fib_Doug(n):
    a = 5 * n * n
    if sqrt(a + 4)%1 == 0 or sqrt(a - 4)%1 == 0:
        return n
    c = 1
    while True:
        m = n + c
        a = 5 * m * m
        if sqrt(a + 4)%1 == 0 or sqrt(a - 4)%1 == 0:
            return m
        m = n - c
        a = 5 * m * m
        if sqrt(a + 4)%1 == 0 or sqrt(a - 4)%1 == 0:
            return m
        c += 1

cache={0: 0, 1: 1}
def fast_fib(n):
    if n in cache:
        return cache[n]
    m = (n + 1) // 2
    a, b = fast_fib(m - 1), fast_fib(m)
    fib = a * a + b * b if n & 1 else (2 * a + b) * b
    cache[n] = fib
    return fib

logroot5 = log(5) / 2
logphi = log((1 + 5 ** 0.5) / 2)

def nearest_fib_PM2R(n):
    if n == 0:
        return 0
    # Approximate by inverting the large term of Binet's formula
    y = int((log(n) + logroot5) / logphi)
    lo = fast_fib(y)
    hi = fast_fib(y + 1)
    return lo if n - lo < hi - n else hi

funcs = (
    nearest_fib_PM2R,
    nearest_fib_Rocky,
    nearest_fib_Doug,
)

# Verify that all the functions return the same result
def verify(lo, hi):
    for n in range(lo, hi):
        a = [f(n) for f in funcs]
        head, tail = a[0], a[1:]
        if not all(head == u for u in tail):
            print('Error:', n, a)
            return False
    else:
        print('Ok')
        return True

def time_test(lo, hi):
    print('lo =', lo, 'hi =', hi)
    for f in funcs:
        start = time()
        for n in range(lo, hi):
            f(n)
        t = time() - start
        print('{0:18}: {1}'.format(f.__name__, t))
    print()

verify(0, 1000)
cache={0: 0, 1: 1}
time_test(0, 1000)

funcs = funcs[:-1]
cache={0: 0, 1: 1}
time_test(1000, 50000)

典型输出

Ok
lo = 0 hi = 1000
nearest_fib_PM2R  : 0.005465507507324219
nearest_fib_Rocky : 0.02432560920715332
nearest_fib_Doug  : 0.45461463928222656

lo = 1000 hi = 50000
nearest_fib_PM2R  : 0.26880311965942383
nearest_fib_Rocky : 1.266334056854248

这些时间是在一台在 Linux 上运行 Python 3.6 的旧 2GHz 32 位机器上。 Python 2.6 给出了类似的时间安排。

FWIW,Rockybilly 和我的代码都可以轻松处理非常大的数字。这是time_test(10**1000, 10**1000 + 1000)的定时输出:

nearest_fib_PM2R  : 0.011492252349853516
nearest_fib_Rocky : 7.556792497634888

【讨论】:

    【解决方案2】:

    为什么这种方法效果很好:

    这是一种不需要先前计算的方法,因此对于性能等检查非常大的数字时非常出色。


    程序:

    from math import *
    
    n = int(input("Enter a number:"))
    
    if sqrt(5*n**2+4)%1==0 or sqrt(5*n**2-4)%1==0:
        print("Your number is a Fibonacci number!")
    else:
        print("Your number is not a Fibonacci number.")
        c = 0
        while 1:
            c += 1
            if sqrt(5*(n+c)**2+4)%1==0 or sqrt(5*(n+c)**2-4)%1==0:
                print("%s is the closest Fibonacci number to your entry." % str(n+c))
                break
            if sqrt(5*(n-c)**2+4)%1==0 or sqrt(5*(n-c)**2-4)%1==0:
                print("%s is the closest Fibonacci number to your entry." % str(n-c))
                break
    

    解释:

    如果 (5*n^2 + 4) 或 (5*n^2 – 4) 是完全平方,则 n 是斐波那契数。


    程序输入/输出

    Enter a number: 9999999999
    Your number is not a Fibonacci number.
    9999816735 is the closest Fibonacci number to your entry.
    
    
    Enter a number: 9999816735
    Your number is a Fibonacci number!
    

    【讨论】:

    • 如果你能用多线程来做这件事,那将会非常快!
    • n 实际上是一个斐波那契数时,该算法非常出色,但是当它必须搜索时,它会真的变慢,尤其是对于较大的n。我在答案中添加了更快的算法,以及一些比较我们算法和 Rockybilly 的速度的计时代码。
    • 是否可以找到我输入的位置或系列中最近的斐波那契数?我通过输入第 n 个找到斐波那契数的值的方法,但不是相反
    • 这是一个非常有用的公式,可以找到第 n 个斐波那契数:F(n) = round( (Phi**n) / √5 ) 前提是 n ≥ 0。要求解 n,您只需使用代数交换方程:n = 1.67227594 + 2.07808692 ln(F(n))。您可以深入了解斐波那契数字,我建议您对它们进行一些研究,特别是如果您打算深入研究它。人们不知道斐波那契数列有很多很酷的东西。您是否知道自然界中的所有事物都显示斐波那契(或卢卡斯)数字,例如松果上的漩涡?
    • 您可能已经注意到我在更新的nearest_fib 函数中使用了该公式:y = int((log(n) + logroot5) / logphi)...
    【解决方案3】:

    您可以自行压缩fibs

    n = int(input("please, enter a number: "))
    for fib, next_fib in itertools.izip(fibs(), itertools.islice(fibs(), 1, None)):
        if n == fib:
            print("Yes! Your number is a Fibonacci number!")
            break
        if next_fib > n:
            closest = fib if n - fib < next_fib - n else next_fib
            print("The closest Fibonacci number is {}".format(closest))
            break
    

    您可以使用itertools.tee 对其进行一些优化。

    【讨论】:

      【解决方案4】:

      如果您不介意进行额外的生成器调用,则无需保留先前的斐波那契。

      首先将生成器存储在一个变量中。

      gen = fibs()
      
      n = int(input("please, enter a number: "))
      for fib in gen:
          if n == fib:
              print("Yes! Your number is a Fibonacci number!")
              break
          if fib > n:
              print("No! Your number is not a Fibonacci number!")
      
              next_fib = next(gen)
              prev = next_fib - fib 
              closest = prev if n - prev < fib - n else fib # Search for Python ternary operator
                                                            # If you are a stranger to this line of code. 
              print("The closest fibonacci number to your entry is %s" % closest)
      
              break
      

      编辑:我首先使用gen.next() 来获取产量的下一个值,但是我忘记了在Python 3 中,它被重命名为gen.__next__()。请小心使用。 next(gen) 是两个 Python 版本的预期用法。

      【讨论】:

      • 'generator' object has no attribute 'next' next = gen.next() 语句错误
      • 让我纠正一下,我实际上是用 Python 2 编写的。
      • 哦!那么 Python 3.x 的替代方案是什么?
      • FWIW,我在答案中添加了一个更快的算法,以及一些比较我们的算法速度和道格拉斯的速度的计时代码。
      • @user7043763 当然,你必须将它与你的代码结合起来。你的 fibs 是在哪里定义的。
      猜你喜欢
      • 1970-01-01
      • 2015-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-03
      • 2014-05-19
      相关资源
      最近更新 更多