python杨辉三角

最近一直在自学 Python ,学习过一段时间后,为了方便以后自己的记忆。写一些学习过程中的东西,用来来记录之前的学习过程,本篇通过杨辉三角来记录下之前的学习经历。

平时主要在廖雪峰老师的教程网站上进行学习,这里要感谢廖雪峰老师的无私分享。

廖雪峰的官方网址

杨辉三角:

前提:每行端点与结尾的数为1.

  1. 每个数等于它上方两数之和。
  2. 每行数字左右对称,由1开始逐渐变大。
  3. 第n行的数字有n项。
  4. 第n行数字和为2n-1。

还有其他各种特性。

这里我们主要利用第一点的特性来实现该算法。

  • 先贴代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def triangles():
    L = [1]
    while True:
    yield L
    L = [1] + [L[i] + L[i+1] for i in range(0,len(L)-1) if len(L) >= 2]
    L.append(1)
    n = 0
    for t in triangles():
    print(t)
    n = n + 1
    if n == 6:
    break

代码块说明:

  • def : 这在 Python 中代表一个方法,triangles()就是一个方法,可以在后面的程序用直接调用,如:

    1
    for t in triangles():

    该段直接调用了该方法,跟其他高级语言中调用,是大同小异。

  • yield : 带有 yield 的函数在 Python 中被称之为 generator(生成器),生成器的内容就不在此累赘,不了解的同学可以 google 一下。根据我自学过程,统一采用我 google 到的答案进行描述。这里将用如何生成斐波那契數列来进行一系列说明。

    斐波那契(Fibonacci)数列是一个非常简单的递归数列,除了第一个第二个数外,任意一个数都可由两个数相加得到。用计算机程序输出菲波那切数列的前 N 个数是一个非常简单的问题,许多初学者都可以轻易写出如下函数:

    • 方法1:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def fab(max):
    n, a, b = 0, 0 ,1
    while n < max:
    print(b)
    a, b = b, a + b
    n = n + 1
    # 执行fab(5):
    fab(5)

    得到:

    1
    2
    3
    4
    5
    1
    1
    2
    3
    5

    结果没问题,但是这里会有一个问题产生,直接在 fab 函数中用 print 打印数字会导致该函数可复用性较差,因为无法获取该函数生成的数列。

    而为了提高 fab 函数的可复用性,最好不要直接打印数列,而是返回一个list.

    • 方法2:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def fab(max):
    n, a, b = 0, 0 ,1
    L = []
    while n < max:
    L.append(b)
    a, b = b, a + b
    n = n + 1
    return L
    # 执行fab(5),并循环输出fab(5)
    for n in fab(5):
    print(n)

    得到结果:

    1
    2
    3
    4
    5
    6
    7
    1
    1
    2
    3
    5
    ***Repl Closed***

    改写后的 fab 函数通过返回的 List 能满足复用性的要求,但是这里又会出现另一个问题,该函数在运行中占用的内存会随着参数 max 的增大而增大,如果要控制内存占用,就最好不要用 List 来保存中间结果,而是用过 iterable 对象来迭代。

    • 方法3:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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()
    s = Fab(5);
    #通过执行next得到r每次r的值
    print(s.next())
    print(s.next())
    print(s.next())
    print(s.next())
    print(s.next())
    print(s.next())#因为只是Fab(5),所以这是第六次,会终止结果

    得到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1
    1
    2
    3
    5
    Traceback (most recent call last):
    File "fibonacci.py", line 25, in <module>
    print(s.next())
    File "fibonacci.py", line 16, in next
    raise StopIteration()
    StopIteration
    ***Repl Closed***

    然而,使用 calss 改写的这个版本,代码明显没有方法一使用 fab 函数来的简洁。这时候为了保证具有代码的简洁性的同时又可以获得 iterable 的效果,yield 就派上用场了。

    • 方法4:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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(5):
    print(n)

    得到:

    1
    2
    3
    4
    5
    6
    7
    1
    1
    2
    3
    5
    ***Repl Closed***

    结果跟上面的方法都是一样的。但是这里仅仅是把 方法一的 print 改成 yield 就实现了代码简洁和获取 iterable 的效果。

    总结:yield 的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数也不再是一个简单的函数,Python 解释器会将其视为一个 generator ,调用 fab(5) 不会执行 fab 函数内部的代码,而是返回一个额iterable 对象。

  • for i in range(0,len(L)-1) if len(L) >= 2: 这是一个列表生成式(List Conprehensions),是 Python 内置的非常简单却强大的可以用来创建 List 的生产式。举个栗子:要生成 list [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 可以:

    1
    2
    3
    4
    print(list(range(1,11)))
    结果:
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    但是如果要得到一个 [1x1, 2x2, 3x3, …, 10x10] 怎么做呢?不用列表生成器的话就是用循环完成:

    1
    2
    3
    4
    5
    6
    7
    8
    L=[]
    for x in range(1,11):
    L.append(x*x)
    print(L)
    结果:
    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

    但是这种循环一看就感觉比较繁琐,而用列表生成式就可以这样使用:

    1
    print([x*x for x in range(1,11)])

    结果和上面循环是一样的,同时我们还可以在后面加上 判断语句,比如现在只输出偶数的结果:

    1
    2
    3
    4
    print([x*x for x in range(1,11) if x%2==0])
    结果:
    [4, 16, 36, 64, 100]

    并且,这里面还可以进行两层循环:

    1
    2
    3
    4
    print([x*y for x in range(1,11) for y in range(2,6) if x%2==0])
    结果:
    [4, 6, 8, 10, 8, 12, 16, 20, 12, 18, 24, 30, 16, 24, 32, 40, 20, 30, 40, 50]

    在本次算法中,灵活运用到列表生成式,省去了很多代码,但是同时,代码的可读性变差了,当然熟悉 python 之后这点可读性可以去掉。

    输出说明

    算法代码片段的后面输出中,因为 L 定义是一个数组,而在 def 中没有加判断终结条件,所以在后面的 for 语句下,加入了 break ,终结代码。

    函数 triangles 执行后,在函数 triangles里面包含了很多个 L 数组,使用print(t) 则每次输出一个 L 数组,就会形成所以需要的杨辉三角。

本次算法学习到此结束。