笔者是一枚准备找工作的新手,为了准备一些算法思想,开启了改善脑回路计划。记录我思考问题的过程,如果能启发一下其他道友就更好了。标题原创,各位大大别乱用哈~
简介
斐波那契(Fibonacci)數列是一个非常简单的递归数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到。形如:1, 1, 2, 3, 5, 8, 13…
要求就是:一个函数fibonacci(n),接受参数n为数列的长度,返回对象是长度为n的斐波那契数列
尝试
声明:程序用的是Python
第一步,写关键部分的代码。返回斐波那契数列中的数,基本规律是这一个数等于前两个数相加:result = late1 + late2
。其中,三个数的顺序是...late1, late2, result...
。由此可见,返回斐波那契数列中的一个数需要前面两个数已知。所以,不仅要为返回值设置变量result,还要为计算值设置两个变量late1和late2。
def fib(n, late1, late2):
if n < 0:
'''报错'''
elif n == 0 or n == 1:
return 1
elif n >= 2:
result = late1 + late2
return result
问题来了:哪儿来的late1和late2以供计算呢,毕竟整体而言,只有一个输入参数,那就是n。**第二步,解决第一步剩余的相关问题。**late1和late2也是数列中的数,是之前计算的result,那么,在每次计算result的时候,我们都应该保留两个值(当前计算出的返回值及其前一个数)以供计算下一个result。
def fib(n, late1, late2):
if n < 0:
'''报错'''
elif n == 0:
return 0, 1
elif n == 1:
return 1, 1
elif n >= 2:
result = late1 + late2
return late2, result
def fibonacci(n):
late, cur = 1, 1 # 初始当前值为1,前一个值为1
rslist = [] # 数列的存储列表
for i in range(n):
late, cur = fib(i, late, cur)
rslist.append(cur)
print rslist
'''产生一个长度为10的fibonacci数列'''
for x in fibonacci(10):
print x,
# 输出为[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
第三步,对代码中的特殊情况特殊处理。要求输入的n必须为大于0的整数。在这里,先默认输入的都是整数,只对n<0的情况进行处理。n<0时,不返回值并且抛出参数异常。
def fib(n, late1, late2):
if n == 0:
return 0, 1
elif n == 1:
return 1, 1
elif n >= 2:
result = late1 + late2
return late2, result
def fibonacci(n):
if n <= 0:
'''报错'''
print 'error'
raise ValueError, 'n must be an integer greater than zero!!'
late, cur = 1, 1 # 初始当前值为1,前一个值为1
rslist = [] # 数列的存储列表
for i in range(n):
late, cur = fib(i, late, cur)
rslist.append(cur)
return rslist
一般代码都有三个要求,基本要求是功能,进阶要求是效率,终极要求是可复用。到此我们已经完成了基本功能要求了,笔者当下心满意足,先去玩耍了~
更新
最近看到别人写的一个更简单的方式, 如下:
def fib(n):
n, late, result = 0, 0, 1
rslist = []
while n < max:
rslist.append(result)
late, result = result, late + result
n = n + 1
return rslist
def fibonacci(n):
if n <= 0:
'''报错'''
print 'error'
raise ValueError, 'n must be an integer greater than zero!!'
else:
return fib(n)
-
具体改进的关键点有三个:
- 我们的代码中需要存储当前返回值和它之前的值。斐波那契数列第一个数是1, 它前面并没有其他数了,但是,因为我们只是用存储的两个数进行相加来计算返回值,所以, 可以将第零个数设为0。
-
当输入为2时,函数需要返回[1, 1]。计算时, 第二个数通过上面的方法,计算为
result = late1 + late2 = 0 + 1
。但是第一个数的计算怎么办? 可以采用先保存结果在计算的方式(跟篱笆桩的情况不太一样),虽然最后会多算一个多余值,不过无伤大雅。 - 现在fib()函数的参数只要一个。两个函数的结构稍作了改变。
更新2
如果因为输入n特别大,可能导致输出的列表特别大,造成内存溢出。为了控制存储空间,我们应该将输出的列表用generator生成器来表示。
举个生成器和普通可迭代对象(list)的列子:
iter_list = range(10) # 普通可迭代对象
gene_list = xrange(10) # 生成器
print type(iter_list)
print type(gene_list)
for i in iter_list:
print i,
for i in gene_list:
print i,
print
print iter_list
print gene_list
---输出↓↓↓---
<type 'list'>
<type 'xrange'>
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
xrange(10)
可以看出:
- 生成器和普通迭代器的效果是一样的
- 但是他们的直接打印结果不一样,是因为普通迭代器是先产生了整个数组并保存在内存中;而生成器只是一个生成数据的方式,当有需要的时候才会产生下一个值,因此也更省内存空间。
用一个生成器来实现斐波那契额数列:
class Fab(object):
def __init__(self, max):
self.max = max
self.n, self.a, self.b = 0, 0, 1
def __iter__(self):
return self
def next(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n = self.n + 1
return r
raise StopIteration()
或者是利用Python的yield命令:
# 简介版,使用yield
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
结果都是一样的:
for n in fab(10):
print n,
---输出↓↓↓---
1 1 2 3 5 8 13 21 34 55
在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。