【问题标题】:list comprehension with multiple assignments具有多个作业的列表理解
【发布时间】:2015-01-27 18:44:23
【问题描述】:

我目前有这个代码来分解大数:

def f1(n):
    return [[i, n//i] for i in range(1 , int(n**0.5) + 1) if n % i == 0]

这是迄今为止我见过的最快的版本(如果有更快的方法我也很想知道),但我想要一个没有嵌套的所有因素的单一列表(所以我想要比如:[factor 1, factor 2, factor 3,..., factor n-3, factor n-2, factor n-1, factor n] 等等。顺序并不重要。

因此,我想知道是否有一种方法可以通过列表理解来分配多个作业。

def f1(n):
    return [i, n//i for i in range(1 , int(n**0.5) + 1) if n % i == 0]

这样我就没有嵌套列表。它会更快,速度至关重要。

我查看了文档,但找不到多个分配的单个示例。

【问题讨论】:

  • 那么,如果您只想要 n // i 因素,为什么还要在其中添加 i,
  • I和n/I都是n的因数

标签: python list python-2.7 nested-lists


【解决方案1】:

列表推导很棒,但有时它们并不是最佳解决方案,具体取决于对可读性和速度的要求。有时,只写出隐含的 for 循环(和 if 语句)更具可读性和更快。

def factors(n):
    l = []
    for i in range(1, int(n**0.5)+1):
        if n % i == 0:
            l.append(i)
            l.append(n//i)
    return l

对于小数字,上述函数比列表推导更快。在较大的数字(1,000,000 和更大)上,函数和列表理解在速度方面是相等的。

为了稍微提高速度,您还可以缓存列表的 append 方法,但这会使函数的可读性稍差。

def factors(n):
    l = []
    append = l.append
    for i in range(1, int(n**0.5)+1):
        if n % i == 0:
            append(i)
            append(n//i)
    return l

速度对比:

In [86]: %timeit factors_list_comprehension(1000)
100000 loops, best of 3: 7.57 µs per loop

In [87]: %timeit factors_function(1000)
100000 loops, best of 3: 6.24 µs per loop

In [88]: %timeit factors_optimised_function(1000)
100000 loops, best of 3: 5.81 µs per loop

In [89]: %timeit factors_list_comprehension(1000000)
10000 loops, best of 3: 111 µs per loop

In [90]: %timeit factors_function(1000000)
10000 loops, best of 3: 108 µs per loop

In [91]: %timeit factors_optimised_function(1000000)
10000 loops, best of 3: 106 µs per loop

【讨论】:

  • 太棒了...我不知道你可以缓存追加。
  • 这种在紧密循环之外存储点表达式是代码优化的标准形式 (compileroptimizations.com/category/hoisting.htm),其中循环中的不变量被“提升”在外部并且只计算一次而不是一次每次迭代。评估本地人比评估点表达式更便宜/更快,特别是如果表达式是 'x.y.z.f.g' 深度。
【解决方案2】:

使用itertools.chain

from itertools import chain

def f1(n):
    return list(chain.from_iterable([i, n//i] for i in xrange(1 , int(n**0.5) + 1) if not n % i))

如果您不需要列表,请删除链上的列表调用并遍历返回的链对象。

如果优化很重要,你应该使用 extend 和 xrange:

def f1(n):
    l = []
    for i in xrange(1, int(n**0.5)+1):
        if not n % i:
            l.extend((i,n//i))
    return l

【讨论】:

    【解决方案3】:

    您可以使用sum() 获得所需的结果。例如:

    >>> sum([[1,6],[2,3]],[])
    [1, 6, 2, 3]
    

    我们可以根据您现有的代码来定义答案:

    def f2(n):
        return sum(f1(n), [])
    

    但是,请注意,当 n 是完美平方时,您的代码会返回两次平方根:

    >>> f1(9)
    [[1, 9], [3, 3]]
    >>> f2(9)
    [1, 9, 3, 3]
    

    【讨论】:

    • 使用sum() 是扁平化列表的最糟糕的方式。
    • 什么是首选?
    • 这是扁平化列表最低效的方法之一。使用itertools.chain.from_iterable 代替以提高速度,或使用嵌套列表理解以提高可读性。
    • 当因子的数量与数字的大小近似成对数时,效率不是问题(例如,n = 10 亿将有大约 30 个因子)
    猜你喜欢
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-29
    • 2011-09-24
    • 1970-01-01
    • 2017-03-20
    • 2021-12-08
    • 1970-01-01
    相关资源
    最近更新 更多