首先,每个人都指出了一个主要问题:您实际上并没有对阶乘求和,甚至没有返回可以传递给的值,sum
您只是打印出阶乘。
但即使你解决了这个问题,你实际上也不会计算sum(math.factorial(i) for i in range(1000))
,而是sum(math.factorial(i) for i in range(1, 1001))
。如果你想要前者,你需要从 0! 开始,而不是 1!。
正如 mgilson 指出的那样,您不需要对每个阶乘重新开始。进行累积阶乘的全部意义在于您可以同步累积阶乘和总和,因此您只需要生成每个阶乘一次:
import itertools
def factorials():
fact = 1
for i in itertools.count(1):
yield fact
fact *= i
def cum_factorials():
cum = 0
for fact in factorials():
cum += fact
yield cum
def cum_factorial(n):
cf = cum_factorials()
consume(cf, 1000)
return next(cf)
(这需要来自recipesconsume
的函数,或者从…导入的函数,或者应该很明显如何自己编写它。)itertools
more-itertools
显然,您不需要通过生成无限的累积阶乘列表并选择第 n 个来做到这一点,但这会阻止 Haskell 程序员嘲笑您选择的语言,这不是真正重要的吗?
更严重的是,想象一下你将如何在纸上做到这一点。首先,你要写下一个阶乘列表:
1
1 * 1 = 1
1 * 2 = 2
2 * 3 = 6
6 * 4 = 24
...
然后,您将在旁边写另一列,总结到目前为止的阶乘。
1 1
1 * 1 = 1 1 + 1 = 2
1 * 2 = 2 2 + 2 = 4
2 * 3 = 6 4 + 6 = 10
6 * 4 = 24 10 + 24 = 34
24 * 5 = 120 34 + 120 = 154
... ...
那么,你如何在 Python 中做同样的事情呢?
好吧,一种选择是将算法重组为可以按顺序编写的内容,如下所示:
def cum_fact(n):
cumulative_sum = 1
latest_factorial = 1
for i in range(1, n):
latest_factorial *= i
cumulative_sum += latest_factorial
return cumulative_sum
但这实际上比你在纸上做的更难理解,也更容易出错。
另一种方法是弄清楚如何让 Python 完成你在纸上所做的事情:取一个无限序列(1, 2, 3, 4, …)
,对那个序列迭代地应用一个简单的变换*=
来得到一个新的序列(1, 1, 2, 6, 24, …)
,然后对那个序列迭代地应用另一个简单的变换+=
来得到一个新的序列(1, 2, 4, 10, 34, …)
. 然后,您只需要该序列中的第 1001 个值,所以……扔掉前 1000 个并取下一个。
itertools.count(1)
给你第一个序列(1, 2, 3, …)
。你可以这样写:
def count(n):
while True:
yield n
n += 1
那是一个生成器函数。我不认为我可以在一段中解释整个概念,但这是基本思想:它不是返回一个值,而是产生一个值序列。每次点击yield
,调用者都会得到一个值。每次你要求下一个值时,它都会从它剩下的地方拾起,直到下一个yield
。如果您在交互式解释器中使用这个想法一段时间,也许它会立即到位 - 但是,更好的是,您可能想在谷歌上搜索教程。
接下来,虽然您可以使用折叠函数应用每个转换,但这可能有点太高级了,无法考虑,所以我使用另外两个生成器函数明确地写出来。
最后,它consume
本身有点不同——它不是一个生成器,而是一个接受和修改迭代器的函数,如下所示:
def consume(iter, n):
for i in range(n):
next(iter)
所以,如果有[1, 2, 3, 4, 5, 6, …]
,而你consume
是前 3 个元素,你会得到[4, 5, 6, …]
.