【问题标题】:Bulk updating a slice of a Python list批量更新 Python 列表的一部分
【发布时间】:2015-08-15 03:30:57
【问题描述】:

我已经编写了埃拉托色尼筛法的简单实现,我想知道是否有更有效的方法来执行其中一个步骤。

def eratosthenes(n):
    primes = [2]
    is_prime = [False] + ((n - 1)/2)*[True]
    for i in xrange(len(is_prime)):
        if is_prime[i]:
            p = 2*i + 1
            primes.append(p)
            is_prime[i*p + i::p] = [False]*len(is_prime[i*p + i::p])
    return primes

我正在使用 Python 的列表切片来更新我的布尔值列表 is_prime。每个元素is_prime[i]对应一个奇数2*i + 1

is_prime[i*p + i::p] = [False]*len(is_prime[i*p + i::p])

当我找到一个素数p 时,我可以标记与该素数False 的倍数相对应的所有元素,并且由于所有小于p**2 的倍数也是较小素数的倍数,因此我可以跳过标记这些。 p**2的索引是i*p + i

我担心计算 [False]*len(is_prime[i*p + 1::p]) 的成本,我试图将它与其他两种我无法工作的策略进行比较。

由于某种原因,公式(len(is_prime) - (i*p + i))/p(如果为正)并不总是等于len(is_prime[i*p + i::p])。是因为我算错了切片的长度,还是因为切片的一些微妙之处我没有抓住?

当我在函数中使用以下行时:

print len(is_prime[i*p + i::p]), ((len(is_prime) - (i*p + i))/p)
is_prime[i*p + i::p] = [False]*((len(is_prime) - (i*p + i))/p)

我得到以下输出(案例n = 50):

>>> eratosthenes2(50)
7 7
3 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in eratosthenes2
ValueError: attempt to assign sequence of size 2 to extended slice of size 3

我还尝试将批量更新行替换为以下内容:

for j in xrange(i*p + i, len(is_prime), p):
    is_prime[j] = False

但是对于较大的 n 值,这会失败,因为 xrange 不会占用任何大于 long 的值。我放弃了试图将itertools.count 争取到我需要的东西。

是否有更快、更优雅的方式来批量更新列表切片?我可以做些什么来修复我尝试过的其他策略,以便我可以将它们与有效的策略进行比较?谢谢!

【问题讨论】:

    标签: python list slice sieve-of-eratosthenes


    【解决方案1】:

    使用itertools.repeat():

    is_prime[i*p + 1::p] = itertools.repeat(False, len(is_prime[i*p + 1::p]))
    

    切片语法将遍历您放在右侧的任何内容;它不需要是一个完整的序列。

    所以让我们修正这个公式。我只会borrow the Python 3 formula,因为我们知道这是可行的:

    1 + (hi - 1 - lo) / step
    

    由于step &gt; 0hi = stoplo = start,所以我们有:

    1 + (len(is_prime) - 1 - (i*p + 1))//p
    

    // 是整数除法;这为我们的 Python 3 代码提供了未来证明,但需要 2.7 才能运行)。

    现在,把它们放在一起:

    slice_len = 1 + (len(is_prime) - 1 - (i*p + 1))//p
    is_prime[i*p + 1::p] = itertools.repeat(False, slice_len)
    

    Python 3 用户:请不要直接使用这个公式。相反,只需写len(range(start, stop, step))。这给出了具有相似性能的相同结果(即 O(1))并且更容易阅读。

    【讨论】:

    • 不错的尝试。使用 timeit: >>> timeit.timeit('eratosthenes0(10000000)', setup="from main import eratosthenes0, count", number=1) 3.1473147869110107 >>> timeit.timeit('eratosthenes1( 10000000)', setup="from main import eratosthenes1; from itertools import repeat, count", number=1) 4.1984710693359375
    • @castle-bravo:你发布的长度公式对我来说很合适。请提供一个错误的具体示例。
    • 使用我发布的公式:>>> eratosthenes2(50) Traceback(最近一次调用最后一次):文件“”,第 1 行,在 文件“”中,第 9 行,在eratosthenes2 ValueError:尝试将大小为 2 的序列分配给大小为 3 的扩展切片
    • @castle-bravo:好的,我修好了。
    • 到目前为止,我的原始版本仍然是最快的。对于素数
    猜你喜欢
    • 2018-05-25
    • 2012-11-22
    • 2015-07-30
    • 1970-01-01
    • 2019-02-16
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多