优化除数筛

时间:2012-11-07 23:39:13

标签: python optimization factors sieve

我有两个我在python中编写过的sieves,如果可能的话,我想帮助优化它们。 divisorSieve计算所有数字的除数,最大为n。列表的每个索引都包含其除数列表。 numDivisorSieve只计算每个索引具有的除数的数量,但不存储除数本身。这些筛子的工作方式与您使用Eratosthenes筛子的方式类似,可以计算最多n的所有素数。

def divisorSieve(n):
    divs = [[1] for x in xrange(0, n + 1)]
    divs[0] = [0]
    for i in xrange(2, n + 1):
        for j in xrange(1, n / i + 1):
            divs[i * j] += [i]
    return divs

    def numDivisorSieve(n):
        divs = [1] * (n + 1)
        divs[0] = 0
        for i in xrange(2, n + 1):
            for j in xrange(1, n / i + 1):
                divs[i * j] += 1
        return divs

#Timer test for function
if __name__=='__main__':
    from timeit import Timer
    n = ...
    t1 = Timer(lambda: divisorSieve(n))
    print n, t1.timeit(number=1)

结果:

 -----n-----|--time(divSieve)--|--time(numDivSieve)--
    100,000 |  0.452978167569  |  0.187762331281
    200,000 |  0.98932170181   |  0.362314797537
    300,000 |  1.6338203645    |  0.55124339118
    400,000 |  2.17913634385   |  0.748340797412
    500,000 |  2.84024755862   |  0.959312993718
    600,000 |  3.46395907197   |  1.17777010636
    700,000 |  4.11840766312   |  1.38268800149
    800,000 |  4.77613733906   |  1.62560614543
    900,000 |  5.4634148162    |  1.83002270324
  1,000,000 |  6.11017136282   |  2.10247496423
  2,000,000 | 13.0507389438    |  4.59150618897
  3,000,000 | 20.6068050631    |  7.24799900479
  4,000,000 | 28.3432841948    |  10.1484527586
  5,000,000 | 36.4113970268    |  12.7670585308
  6,000,000 | 44.4017867139    |  15.4226118057
  7,000,000 | 52.4697580394    |  18.2902677738
  8,000,000 | 61.4056966474    |  21.1247001928
  9,000,000 | 69.7948510294    |  23.8988925173
 10,000,000 | 78.639434285     |  26.8588813211
 20,000,000 |      N/A         |  56.2527237669 (waited for a while but divisorSieve didn't finish)
 30,000,000 |   MemoryError    |  86.8917332214
 40,000,000 |   MemoryError    |  118.457179822
 50,000,000 |   MemoryError    |  149.526622815
 60,000,000 |   MemoryError    |  181.627320396
 70,000,000 |   MemoryError    |  214.17467749
 80,000,000 |   MemoryError    |  246.23677614
 90,000,000 |   MemoryError    |  279.53308422
100,000,000 |   MemoryError    |  314.813166014

结果非常好,我很高兴我能够做到这一点,但我希望得到它更快。如果可能的话,我希望使用divisorSieve以合理的速度获得 100,000,000 。虽然这也会导致问题, 30,000,000 + 的任何内容在divisorSieve中抛出MemoryError divs = [[1] for x in xrange(0, n + 1)]。 numDivisorSieve允许运行完整的 100,000,000

我尝试用divs = [1] * (n + 1)divs = array.array('i', [1] * (n + 1))替换numDivisorSieve的divs = numpy.ones((n + 1), dtype='int'),但两者都导致速度损失(数组略有差异,numpy差异大得多)。我希望由于numDivisorSieve的效率会有所下降,所以divisorSieve也是如此。当然,我总是有机会错误地使用其中一个或两个,因为我不习惯其中任何一个。

我很感激你能给我的任何帮助。我希望我提供了足够的细节。谢谢。

2 个答案:

答案 0 :(得分:1)

替换

 divs[i * j] += [i]

 divs[i * j].append(i)

答案 1 :(得分:0)

你可以替换:

divs = [[1] for x in xrange(0, n + 1)]

divs = [[1]] * (n + 1)

我发现第二个选项明显更快(在mem上不确定,请参阅下面的输出)

In [24]: t1=timeit.Timer(stmt='[[1]] * 1000')

In [25]: t2=timeit.Timer(stmt='[[1] for i in xrange(1000)]')

In [26]: t1.timeit(number=10000)
Out[26]: 0.06668901443481445

In [27]: t2.timeit(number=10000)
Out[27]: 1.5391979217529297