python斐波那契数列的九种思路与多解

斐波那契数列介绍:

在这里插入图片描述


第一种方式:刚开始学python的程序员

 def fib(n):
     return nth fibonacci number

这个程序可以看出是一个伪代码,定义了函数后,将return翻译一下就是返回第n个斐波那契数列的数值,这也是做程序员必须掌握的吧,首先需要看懂伪码,然后写下自己的伪码,再最后构建真实的代码,这或许比直接写出能运行的代码更加真实,而大部分人却选择了跳过前面两步,最后造成结果就是只能做coder,而不是manager。


第二种方式:有其它编程语法经验的程序员

def fib(n):
     if n<=2 :
         return 1;
     else:
         return fib(n-1)+fib(n-2);

数列的前两项为1,这是后面能递归的充要条件,无法修改,所以我们必须分开来考虑,这个程序就是在原来的基础上,加了一个判断和C语言版的返回值。


第三种方式:聪明(懒惰)的程序员

fib=lambda n:1 if n<=2 else fib(n-1)+fib(n-2)

此处用了lambda匿名函数的使用方法,lambda函数是一个刷python题必备的函数,不用不知道,一用吓一跳,能将各种内置函数结合起来,大部分需要几行的程序,但在它这里,往往只要一行,整体来讲多花点时间看还是能看懂的,和前端中Vue的匿名有得一拼,不过还是没有前端那么恶心。十几行硬是写成一行。


第四种方式:继续耍小聪明的程序员

fib=lambda n,x=0,y=1:x if not n else f(n-1,y,x+y)

利用了python的三元操作符,比上面那种,相似但少了很多的步骤,更像是尾递归。


第五种方式:刚学完数据结构的程序员

def fib(n):
   x,y=0,1
       while(n):
   x,y,n=y,x+y,n-1  # 元组赋值特性
    return x

虽然不太清楚python中的递归为什么效率很低,可能是太多操作被简化导致了它的很多算法的缩水,我最近也正在学算法,发现市面上python的数据结构确实很少,但C++的数据结构因为基础问题又太难学了,还是需得做一番权衡了。
上面的代码是做了一些调整,因为之前的两个可以看出是一种树形递归结构,按递归理论来讲,这是一种比较低效的算法,所以我们改用迭代的思想,是可以提升效率的。

第六种方式:迭代器求解斐波那契

def __init__(self, n):
    self.n = n  # 记录要打印的斐波那契数列的数量
    self.a, self.b = 0, 1  # 记录数列的前两位值,初始值 0,1
    self.positon=0 # 记录当前迭代的位置,初始值是0

def __iter__(self):    # 定义一个可迭代对象,或者说产生一个可迭代协议
    return self

def __next__(self):     # 定义一个next方法,做手动迭代
    """返回迭代器指向的下一个值"""
    if self.positon<self.n:
        num=self.a
        self.a,self.b=self.b,self.a+self.b
        self.positon+=1 # 自加1,指向下一个位置
        return num
    else:
        raise StopIteration     # 抛出异常,完成一轮迭代

这个是根据迭代器的原理做的斐波那契数列,该解释的代码后的注释应该都很清楚了,这里我再来个整理一下,大概内容为:在这个手动模拟迭代求解斐波那契的过程中,先通过赋予其可迭代的协议,让对象具有迭代器,然后用next方法进行手动迭代,迭代到最后出现StopIteration异常退出。这里需要注意的一点就是,可迭代的对象并不都具有迭代器。


第七种方式:生成器求解斐波那契

def list(x):
    a = b = 1
    for i in range(x):    # 遍历i到x的值
        yield a     # 生成器,返回a,调用函数执行到这里就停止,因为它是一个懒惰函数。
        a , b = b, a + b    # 交换彼此的值,元组赋值特性

关于生成器,其继承了迭代器所有的功能,也即是对某些函数进行了封装,我没有看过源码,并不知道里面的具体实现流程是怎样的,上面的函数生成器版的斐波那契函数,该讲的也都在注释里提了,这里就不再做过多解释了。


第八种方式:刚刷完线代的程序员

def fib(n):
     def m1(a,b):
         m=[[],[]]
         m[0].append(a[0][0]*b[0][0]+a[0][1]*b[1][0])
         m[0].append(a[0][0]*b[0][1]+a[0][1]*b[1][1])
         m[1].append(a[1][0]*b[0][0]+a[1][1]*b[1][0])
         m[1].append(a[1][0]*b[1][0]+a[1][1]*b[1][1])
         return m
     def m2(a,b):
         m=[]
         m.append(a[0][0]*b[0][0]+a[0][1]*b[1][0])
         m.append(a[1][0]*b[0][0]+a[1][1]*b[1][0])
         return m
     return m2(reduce(m1,[[[0,1],[1,1]] for i in range(n)]),[[0],[1]])[0]

上面是我去百度到的一个大佬写得程序推导式,下面是他的见解还有我的一些小见解。

说明:
这段代码就不像之前的代码那样清晰了,所以先介绍下原理(需要一点线性代数知识):
首先看一下之前的迭代版本的Fibonacci函数,很容易可以发现存在一个变换:y->x, x+y->y。换一个角度,就是[x,y]->[y,x+y]。
在这里,我声明一个二元向量[x,y]T,它通过一个变换得到[y,x+y]T,可以很容易得到变换矩阵是[[1,0],[1,1]],也就是说:[[1,0],[1,1]]*[x,y]T=[y,x+y]T
令二元矩阵A=[[1,0],[1,1]],二元向量x=[0,1]T,容易知道Ax的结果就是下一个Fibonacci数值,即:
Ax=[fib(1),fib(2)]T
亦有:
Ax=[fib(2),fib(3)]T
………………
以此类推,可以得到:
Aⁿx=[fib(n),fib(n-1)]T
也就是说可以通过对二元向量[0,1]T进行n次A变换,从而得到[fib(n),fib(n+1)]T,从而得到fib(n)。
在这里定义了一个二元矩阵的相乘函数m1,以及一个在二元向量上的变换m2,然后利用reduce操作完成一个连乘操作得到Aⁿx,最后得到fib(n)。


第九种方式:准备参加ACM比赛的Python程序员

 def fib(n):
     lhm=[[0,1],[1,1]]
     rhm=[[0],[1]]
     em=[[1,0],[0,1]]
     #multiply two matrixes
     def matrix_mul(lhm,rhm):
         #initialize an empty matrix filled with zero
         result=[[0 for i in range(len(rhm[0]))] for j in range(len(rhm))]
         #multiply loop
         for i in range(len(lhm)):
             for j in range(len(rhm[0])):
                 for k in range(len(rhm)):
                     result[i][j]+=lhm[i][k]*rhm[k][j]
         return result
     
     def matrix_square(mat):
         return matrix_mul(mat,mat)
     #quick transform
     def fib_iter(mat,n):
         if not n:
             return em
         elif(n%2):
             return matrix_mul(mat,fib_iter(mat,n-1))
         else:
             return matrix_square(fib_iter(mat,n/2))
     return matrix_mul(fib_iter(lhm,n),rhm)[0][0]

说明:

看过上一个fib函数就比较容易理解这一个版本了,这个版本同样采用了二元变换的方式求fib(n)。不过区别在于这个版本的复杂度是lgn,而上一个版本则是线性的。
这个版本的不同之处在于,它定义了一个矩阵的快速求幂操作fib_iter,原理很简单,可以类比自然数的快速求幂方法,所以这里就不多说了。

衍生题:

这是我在pythontip上看到的有一个基础题,题目如下:

斐波那契数列为1,1,2,3,5,8…。数列从第三项起满足,该项的数是其前面两个数之和。现在给你一个正整数n(n < 10000), 请你求出第n个斐波那契数取模20132013的值(斐波那契数列的编号从1开始)。
例如:
n=1, 则输出:1
n=4, 则输出:3

这里有很多种解法,题目整体的意思很简单,就是当和为20132013问加的最后一个斐波那契数是第几个,这里我将我和我当初想的一样的一种解法放上,当然还有很多种其它更高大上的解法,这里也只是一种,其它的就不说了。

L = [1, 1]
while len(L) < n:
    L.append(L[-1] + L[-2])
print(L[-1] % 20132013)

总结:

其实这篇博文是我之前微信公众号上的,那个时候刚开始接触python写的一个问题,然后是在pythontip上刷的题,惊讶于斐波那契数列的解法多样化,所以自己也想通俗的捋顺里面的逻辑还有用法,当然,可能我上面总结得还不到位,因为题目的解法是多元的,在我查资料的时候,python就已经有十几种了,更何况还有Java、C等,并且有一些思路就是在通过其它语言衍生而来,所以,永远不要高估自己,低估题目,这是我越往后面走,反应越强烈的话语。以后可能也会多写一些这样的,即使是敲代码也需要调剂,我也需要通过学习更多的基础,才能不断警醒自己,不能舍本逐末。


另外这篇文章是我借鉴我下面发的参考目录里的第一篇博客中的文章,参考了几种解法,加上我用迭代器和生成器的解法以及理解,最后两种解法因为自身都不是很理解就直接加引用了,所以,如果想看原文,可以看下面的链接,而衍生题的题目我也放在第二个链接里。

1.http://www.cnblogs.com/figure9/archive/2010/08/30/1812927.html#undefined
2.http://www.pythontip.com/coding/code_oj_case/43

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

submarineas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值