Python中的帕斯卡三角形

20

作为Python学习经验,我正在尝试编写自己版本的帕斯卡三角形代码。由于我刚开始学习,所以花了几个小时时间来编写以下代码:

pascals_triangle = []

def blank_list_gen(x):
    while len(pascals_triangle) < x:
        pascals_triangle.append([0])

def pascals_tri_gen(rows):
    blank_list_gen(rows)
    for element in range(rows):
        count = 1
        while count < rows - element:
            pascals_triangle[count + element].append(0)
            count += 1
    for row in pascals_triangle:
        row.insert(0, 1)
        row.append(1)
    pascals_triangle.insert(0, [1, 1])
    pascals_triangle.insert(0, [1])

pascals_tri_gen(6)

for row in pascals_triangle:
    print(row)

这将返回

[1]
[1, 1]
[1, 0, 1]
[1, 0, 0, 1]
[1, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 0, 1]

然而,我不知道该怎么做。我已经花费了数小时在这个问题上。我想强调的是,我并不希望你替我完成它;只需指导我正确的方向即可。就列表而言,我的代码返回

[[1], [1, 1], [1, 0, 1], [1, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 1]]

谢谢。

编辑:我听取了一些好建议,完全重写了我的代码,但现在遇到了另一个问题。以下是我的代码。

import math

pascals_tri_formula = []

def combination(n, r):
    return int((math.factorial(n)) / ((math.factorial(r)) * math.factorial(n - r)))

def for_test(x, y):
    for y in range(x):
        return combination(x, y)

def pascals_triangle(rows):
    count = 0
    while count <= rows:
        for element in range(count + 1):
            [pascals_tri_formula.append(combination(count, element))]
        count += 1

pascals_triangle(3)

print(pascals_tri_formula)

然而,我发现输出结果有些不理想:

[1, 1, 1, 1, 2, 1, 1, 3, 3, 1]

我该如何修复这个问题?

12个回答

28

好的代码审查:

import math

# pascals_tri_formula = [] # don't collect in a global variable.

def combination(n, r): # correct calculation of combinations, n choose k
    return int((math.factorial(n)) / ((math.factorial(r)) * math.factorial(n - r)))

def for_test(x, y): # don't see where this is being used...
    for y in range(x):
        return combination(x, y)

def pascals_triangle(rows):
    result = [] # need something to collect our results in
    # count = 0 # avoidable! better to use a for loop, 
    # while count <= rows: # can avoid initializing and incrementing 
    for count in range(rows): # start at 0, up to but not including rows number.
        # this is really where you went wrong:
        row = [] # need a row element to collect the row in
        for element in range(count + 1): 
            # putting this in a list doesn't do anything.
            # [pascals_tri_formula.append(combination(count, element))]
            row.append(combination(count, element))
        result.append(row)
        # count += 1 # avoidable
    return result

# now we can print a result:
for row in pascals_triangle(3):
    print(row)

打印:

[1]
[1, 1]
[1, 2, 1]

Pascal三角形的解释:

这是"n choose k"的公式(即从一个有序列表中选择k个项目(不考虑顺序)的不同方式数):

from math import factorial

def combination(n, k): 
    """n choose k, returns int"""
    return int((factorial(n)) / ((factorial(k)) * factorial(n - k)))

有评论者问这是否与itertools.combinations有关 - 的确如此。 "n选k"可以通过从组合中取元素的列表的长度来计算:

from itertools import combinations

def pascals_triangle_cell(n, k):
    """n choose k, returns int"""
    result = len(list(combinations(range(n), k)))
    # our result is equal to that returned by the other combination calculation:
    assert result == combination(n, k)
    return result

让我们看一下这个演示:

from pprint import pprint

ptc = pascals_triangle_cell

>>> pprint([[ptc(0, 0),], 
            [ptc(1, 0), ptc(1, 1)], 
            [ptc(2, 0), ptc(2, 1), ptc(2, 2)],
            [ptc(3, 0), ptc(3, 1), ptc(3, 2), ptc(3, 3)],
            [ptc(4, 0), ptc(4, 1), ptc(4, 2), ptc(4, 3), ptc(4, 4)]],
           width = 20)
[[1],
 [1, 1],
 [1, 2, 1],
 [1, 3, 3, 1],
 [1, 4, 6, 4, 1]]

我们可以使用嵌套列表推导式避免重复:
def pascals_triangle(rows):
    return [[ptc(row, k) for k in range(row + 1)] for row in range(rows)]

>>> pprint(pascals_triangle(15))
[[1],
 [1, 1],
 [1, 2, 1],
 [1, 3, 3, 1],
 [1, 4, 6, 4, 1],
 [1, 5, 10, 10, 5, 1],
 [1, 6, 15, 20, 15, 6, 1],
 [1, 7, 21, 35, 35, 21, 7, 1],
 [1, 8, 28, 56, 70, 56, 28, 8, 1],
 [1, 9, 36, 84, 126, 126, 84, 36, 9, 1],
 [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1],
 [1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1],
 [1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1],
 [1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1],
 [1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1]]

递归定义:

我们可以使用三角形所示的关系递归地定义它(这是一种不太高效但可能更数学优美的定义):

 def choose(n, k): # note no dependencies on any of the prior code
     if k in (0, n):
         return 1
     return choose(n-1, k-1) + choose(n-1, k)

为了好玩,你可以看到每一行的执行时间逐渐变长,因为每一行都必须重新计算前一行中几乎每个元素两次:

for row in range(40):
    for k in range(row + 1):
        # flush is a Python 3 only argument, you can leave it out,
        # but it lets us see each element print as it finishes calculating
        print(choose(row, k), end=' ', flush=True) 
    print()


1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
1 10 45 120 210 252 210 120 45 10 1
1 11 55 165 330 462 462 330 165 55 11 1
1 12 66 220 495 792 924 792 495 220 66 12 1
1 13 78 286 715 1287 1716 1716 1287 715 286 78 13 1
1 14 91 364 1001 2002 3003 3432 3003 2002 1001 364 91 14 1
1 15 105 455 1365 3003 5005 6435 6435 5005 3003 1365 455 105 15 1
1 16 120 560 1820 4368 8008 11440 12870 11440 8008 4368 1820 560 120 16 1
1 17 136 680 2380 6188 12376 19448 24310 24310 19448 12376 6188 2380 680 136 17 1
1 18 153 816 3060 8568 18564 31824 43758 48620 43758 31824 18564 8568 3060 816 ...

当你看腻了,按下Ctrl-C退出,它会变得非常慢...


12

我知道你想自己实现,但是最好的方式是通过演示一个实现过程来解释。这是我会怎么做,这个实现依赖于我对Python函数如何工作的相当完整的了解,所以你可能不想使用这个代码本身,但它可以让你找到正确的方向。

def pascals_triangle(n_rows):
    results = [] # a container to collect the rows
    for _ in range(n_rows): 
        row = [1] # a starter 1 in the row
        if results: # then we're in the second row or beyond
            last_row = results[-1] # reference the previous row
            # this is the complicated part, it relies on the fact that zip
            # stops at the shortest iterable, so for the second row, we have
            # nothing in this list comprension, but the third row sums 1 and 1
            # and the fourth row sums in pairs. It's a sliding window.
            row.extend([sum(pair) for pair in zip(last_row, last_row[1:])])
            # finally append the final 1 to the outside
            row.append(1)
        results.append(row) # add the row to the results.
    return results

使用方法:

>>> for i in pascals_triangle(6):
...     print(i)
... 
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]

10

不使用zip,但使用生成器:

def gen(n,r=[]):
    for x in range(n):
        l = len(r)
        r = [1 if i == 0 or i == l else r[i-1]+r[i] for i in range(l+1)]
        yield r

例子:

print(list(gen(15)))

输出:

[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1], [1, 6, 15, 20, 15, 6, 1], [1, 7, 21, 35, 35, 21, 7, 1], [1, 8, 28, 56, 70, 56, 28, 8, 1], [1, 9, 36, 84, 126, 126, 84, 36, 9, 1], [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1], [1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1], [1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1], [1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1], [1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1]]

绘制三角形

为了绘制一个美丽的三角形(仅适用于 n < 7,超过这个数字会变形。对于 n>7,请参考 draw_beautiful)

n < 7

def draw(n):
    for p in gen(n):
        print(' '.join(map(str,p)).center(n*2)+'\n')

例子:

draw(10)

输出:

      1       

     1 1      

    1 2 1     

   1 3 3 1    

  1 4 6 4 1   

1 5 10 10 5 1   

适用于任何尺寸

因为我们需要知道最大宽度,所以不能使用生成器。

def draw_beautiful(n):
    ps = list(gen(n))
    max = len(' '.join(map(str,ps[-1])))
    for p in ps:
        print(' '.join(map(str,p)).center(max)+'\n')

示例(2): 适用于任何数字:

draw_beautiful(100)

n = 100 的示例


2
def pascal(n):
    if n==0:
        return [1]
    else:
        N = pascal(n-1)
        return [1] + [N[i] + N[i+1] for i in range(n-1)] + [1]


def pascal_triangle(n):
    for i in range(n):
        print pascal(i)

2
这里是我的尝试:
def generate_pascal_triangle(rows):
    if rows == 1: return [[1]]

    triangle = [[1], [1, 1]] # pre-populate with the first two rows

    row = [1, 1] # Starts with the second row and calculate the next

    for i in range(2, rows):
        row = [1] + [sum(column) for column in zip(row[1:], row)] + [1]
        triangle.append(row)

    return triangle

for row in generate_pascal_triangle(6):
    print row

讨论

  • 三角形的前两行是硬编码的
  • zip() 函数基本上将相邻的两个数字配对在一起
  • 我们仍然需要在开头添加1,在结尾再添加1,因为 zip() 函数只生成下一行的中间部分

1

我从流行的斐波那契数列解决方案中作弊。对我来说,帕斯卡三角形的实现与斐波那契的概念相同。在斐波那契中,我们一次使用一个数字并将其添加到前一个数字上。在帕斯卡三角形中,一次使用一行,并将其添加到前一行中。

这是完整的代码示例

>>> def pascal(n):
...     r1, r2 = [1], [1, 1]
...     degree = 1
...     while degree <= n:
...         print(r1)
...         r1, r2 = r2, [1] + [sum(pair) for pair in zip(r2, r2[1:]) ] + [1]
...         degree += 1

测试

>>> pascal(3)
[1]
[1, 1]
[1, 2, 1]
>>> pascal(4)
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
>>> pascal(6)
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]

注意:如果要将结果作为生成器返回,请将print(r1)更改为yield r1


1

这是一个优雅高效的递归解决方案。我正在使用非常方便的toolz库。

from toolz import memoize, sliding_window

@memoize
def pascals_triangle(n):
    """Returns the n'th row of Pascal's triangle."""
    if n == 0:
        return [1]
    prev_row = pascals_triangle(n-1)
    return [1, *map(sum, sliding_window(2, prev_row)), 1]

pascals_triangle(300) 在 MacBook Pro (2.9 GHz 英特尔 Core i5) 上大约需要 15 毫秒。请注意,如果不增加默认递归深度限制,您无法再提高更多。


惊人的速度! - ChoF

1
# combining the insights from Aaron Hall and Hai Vu,
# we get:

def pastri(n):
    rows = [[1]]
    for _ in range(1, n+1):
        rows.append([1] +
                    [sum(pair) for pair in zip(rows[-1], rows[-1][1:])] +
                    [1])
    return rows

# thanks! learnt that "shape shifting" data,
# can yield/generate elegant solutions.

1

我是一名初学者Python学生。这是我的尝试,采用了非常字面的方法,使用了两个For循环:

pascal = [[1]]
num = int(input("Number of iterations: "))
print(pascal[0]) # the very first row
for i in range(1,num+1):
    pascal.append([1]) # start off with 1
    for j in range(len(pascal[i-1])-1):
    # the number of times we need to run this loop is (# of elements in the row above)-1
        pascal[i].append(pascal[i-1][j]+pascal[i-1][j+1])
        # add two adjacent numbers of the row above together
    pascal[i].append(1) # and cap it with 1
    print(pascal[i])

1

这里是实现帕斯卡三角形的简单方法:

def pascal_triangle(n):
    myList = []
    trow = [1]
    y = [0]
    for x in range(max(n,0)):
        myList.append(trow)
        trow=[l+r for l,r in zip(trow+y, y+trow)]

    for item in myList:
        print(item)

pascal_triangle(5)

Python的zip()函数返回zip对象,它是元组的迭代器,其中每个传递的迭代器中的第一个项目被配对在一起,然后每个传递的迭代器中的第二个项目被配对在一起。Python zip是容纳实际数据的容器。
Python的zip()函数接受可迭代对象(可以是零个或多个),创建一个基于传递的可迭代对象聚合项的迭代器,并返回元组的迭代器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接