【问题标题】:Python itertools: Combining predicatesPython itertools:组合谓词
【发布时间】:2015-12-17 16:57:26
【问题描述】:

这是 Project Euler 问题 2:

斐波那契数列中的每个新项都是通过添加 前两期。从 1 和 2 开始,前 10 个术语将 是:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

通过考虑斐波那契数列中的项,其值不 超过四百万,求偶数项之和。

一种可能的直接方法是:

def fib_even(n):
    previous, current = 0, 1
    sum_fib = 0
    while current <= n:
        previous, current = current, previous + current
        if current % 2 == 0:
            sum_fib += current
    return sum_fib

然后我尝试使用“纯”生成器以一种功能性的方式来完成它:

def fibonacci_generator():
    curr_fib, next_fib = 0, 1
    while True:
        yield curr_fib
        curr_fib, next_fib = next_fib, curr_fib + next_fib

print(sum(itertools.filterfalse(lambda x: x % 2 == 0, itertools.takewhile(lambda x: x < n, fibonacci_generator()))))

这个版本效率低下,因为同一个工作(屈服)被多次完成。

我的问题:生成斐波那契数一次的功能性方法是什么,直到达到某个限制并同时过滤奇数。 (不改变第二个版本的生成器。)

作为参考,第一个版本 timeit for 10 给出:

100000 loops, best of 3: 10 µs per loop

十亿的第二个版本timeit给出:

10000 loops, best of 3: 28.8 µs per loop

【问题讨论】:

  • 这是函数式的方式。 Python 只是不支持具有与其他语言相同效率的函数式编程风格。
  • 你已经有了函数式方式,而你的非函数式方式基本上是函数式方式,但是内联。也许如果这是 PyPy 或 Jython 之类的,系统可以 JIT 内联第二个版本以加快速度,但在 CPython 上,调用这些 lambda 对速度没有好处。
  • 一个开箱即用的解决方案是注意每隔三个斐波那契数是偶数 (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...)。这意味着您可以只对序列进行切片,而不是测试每个值的均匀度。

标签: python python-3.x functional-programming


【解决方案1】:

在生成器中进行过滤会更快:

def my_fibonacci_generator():
    curr_fib, next_fib = 0, 1
    while True:
        if not curr_fib % 2:
            yield curr_fib
        curr_fib, next_fib = next_fib, curr_fib + next_fib


n = 4000000
sum(takewhile(lambda i: i < n, my_fibonacci_generator())

在不到一半的时间内运行:

In [10]: timeit (sum(filterfalse(lambda x: x % 2 == 0, takewhile(lambda x: x < n, fibonacci_generator()))))
100000 loops, best of 3: 16.1 µs per loop

In [11]: timeit sum(takewhile(lambda i: i < n, my_fibonacci_generator()))
100000 loops, best of 3: 7.65 µs per loop

In [12]: sum(filterfalse(lambda x: x % 2 == 0, takewhile(lambda x: x < n, fibonacci_generator())))
Out[12]: 4613732

In [13]:  sum(takewhile(lambda i: i < n, my_fibonacci_generator()))
Out[13]: 4613732

正是 lambda 的成本正在减慢您自己的功能。除非您可以使用内置行 map(operator.add 而不是等效的 lambda,否则函数式方法总是会受到影响。

在不修改的情况下接近相同运行时的唯一方法是先进行过滤:

In [57]: timeit sum(takewhile(lambda x: x < n, filter(lambda x: x & 1, fibonacci_generator())))
100000 loops, best of 3: 9.93 µs per loop

【讨论】:

  • 谢谢。我正在尝试在不改变第二个版本的生成器的情况下找到解决方案(如果可能的话)。
  • @blackened,这是第二个杀死你的 lambda
  • 是的。这就是我把它带到这里的原因。
  • @blackened,如果您不在函数中进行过滤,则必须使用 lambda 进行过滤,而不是 ifilterfalse 调用杀死您。 sum(filter(None, takewhile(lambda x: x &lt; n, fibonacci_generator()))) 也会高效运行`
猜你喜欢
  • 2016-03-15
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
相关资源
最近更新 更多