【问题标题】:List comprehensions without filter没有过滤器的列表推导
【发布时间】:2018-05-12 10:54:52
【问题描述】:

最近在网上偶然发现了这段代码:

nonprime = [j for i in range(2, 8) for j in range(i*2, 50, i)]

上面的代码似乎在计算所有低于 50 的非素数,但我没有得到逻辑。 我研究了python中的列表理解,并观察到它使用过滤器根据条件进行过滤, 但我无法理解两个 for 循环是如何计算这些非素数的。

【问题讨论】:

  • 这只是两个嵌套循环。没有过滤。它的工作原理可能与 python 无关,但它是一些聪明的数字东西——我很肯定它对更大的数字停止工作。
  • @deets,你说得对,这是一个“聪明的数字”(见the proof)。但由于它已在数学上得到证明,它确实适用于更大的数字,尽管效率低下。
  • 确实是的.. 许多解决方案很有帮助,而且很难选择一个。非常感谢大家的详细解释。

标签: python python-3.x algorithm list list-comprehension


【解决方案1】:

这个列表推导与这两个嵌套的 for 循环相同:

nonprime=[]
for i in range(2,8):
    for j in range(i*2, 50,i):
        nonprime.append(j)

所以在外循环中,i 在每次迭代中只增加 1,并取值 i=2,3,4,5,6,7。 j 上的内部 for 循环取决于外部循环。它从2*i 开始,j 在每次迭代中增加i

所以对于ì=2,内部循环从j=4 开始,j 采用值j=4,6,8,...,48。 然后 ì 在外循环中增加到 3,j 取值 6,9,12,...48

请注意,nonprime 列表不会创建唯一元素,因为例如 6 多次出现。

正如 Bakuriu 正确指出的那样,这种方法利用了 Sieve of Eratosthenes

【讨论】:

    【解决方案2】:

    这个答案的关键在于数学:

    If a number n>1 is not prime, then it has a prime factor <= sqr(n)

    现在我们要解释逻辑了。注意sqrt(50) &lt; 8,这是第一个range的限制。

    我们可以将嵌套列表推导重写为常规循环。我们可以使用set 代替列表,列表中会有不必要的重复元素:

    res = set()
    
    for i in range(2, 8):
        for j in range(i*2, 50, i):
            res.add(j)
    

    外层循环遍历所有i &lt; sqrt(50)

    对于每个i,内部循环迭代小于50的i的所有倍数。

    根据前面提到的数学陈述,我们有所有小于 50 的非素数。这是真的,因为我们用尽了 所有 素因数

    【讨论】:

      【解决方案3】:

      循环基本上是Sieve of Erathostenes,但不是删除数字而是保留它们。

      在 Erathostenes 的筛子中,您删除“已知素数”的倍数。最有效的方法是,给定一个素数p,删除数字p*p,然后删除p*(p+1) 等,直到达到限制并继续下一个素数q,然后开始一个q*q

      列表推导是它的一个小变种。您可以将 i*2 替换为 i*i 并仍然获得相同的数字(尽管重复次数更少)。

      在 Erathostenes 的筛子中,您通常会删除除 2 之外的所有偶数,然后使用 2*i 作为范围的步长,因为您只需要考虑奇数作为素数。显然,如果您想获得所有非素数,那么“优化”就不起作用了。

      【讨论】:

        【解决方案4】:

        这是该代码的非列表理解版本:

        nonprime = []
        for i in range(2, 8):
            for j in range(i*2, 50, i):
                nonprime.append(j)
        

        一步一步:

        for i in range(2, 8):
        

        到目前为止,非常简单。这将返回数字 2 到 7,当它到达 8 时停止。在此循环的第一次迭代中,i = 2

            for j in range(i*2, 50, i):
        

        现在我们正在创建一个内部循环。让我们用 2 替换 i,这样我们就可以看到它在 outer 循环的第一次迭代中做了什么。

            for j in range(2*2, 50, 2):
        

            for j in range(4, 50, 2)
        

        这会返回一个从 4 开始的数字列表,每次(6、8、10)递增 2,并在达到 50 时停止。如果您查看nonprime 中的结果,您会看到这些数字.

        外循环的下一次迭代,i = 3。所以:

            for j in range(i*2, 50, i):
        

        变成:

            for j in range(3*2, 50, 3):
        

        或者:

            for j in range(6, 50, 3):
        

        从 6 开始,我们不断添加 3,并在达到 50 时停止。您将看到此序列出现在 nonprime 中存在的值列表中的下一个

        就是这样,这就是这段代码所做的一切。正如@Bakuriu 所指出的,这基本上是一个反向的Sieve of Erathostenes,但这是另一个主题。

        【讨论】:

          【解决方案5】:

          它基本上计算 range(2, 8) 中小于 50 的所有值的倍数。它将包含 重复 值。

          # iterates over values in this range [2, 7]
          for i in range(2, 8)
          
          # iterates over multiples of i lower than 50
          for j in range(i*2, 50, i)
          

          结果将如下所示:

          [ 4, 6, 8, ... # multiples of 2 lower than 50
           6, 9, 12, ... # multiples of 3 lower than 50
          (...)
          14, 21, 28, ... # multiples of 7 lower than 50
          ]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-06-17
            • 2022-01-13
            • 2020-09-12
            • 1970-01-01
            • 1970-01-01
            • 2012-05-28
            • 2014-12-23
            • 2017-04-23
            相关资源
            最近更新 更多