如何让这个Python代码更快地运行? [项目欧拉问题#7]

问题描述 投票:3回答:13

我正在尝试完成Project Euler挑战:

通过列出前六个素数:2,3,5,7,11和13,我们可以看到第6个素数是13。

什么是10 001主数?

我的代码似乎是正确的,因为它适用于小数字,例如6th prime是13。

我如何改进它,以便代码将更快地运行更大的数字,如10 001。

代码如下:

#Checks if a number is a prime
def is_prime(n):
    count = 0
    for i in range(2, n):
        if n%i == 0:
            return False
            break
        else:
            count += 1
    if count == n-2:
        return True

#Finds the value for the given nth term     
def term(n):
    x = 0
    count = 0
    while count != n:
        x += 1
        if is_prime(x) == True:
            count += 1
    print x


term(10001)

更新:

谢谢你的回复。我应该更清楚,我不是要加速翻译或找到更快的解释器,因为我知道我的代码不是很好,所以我正在寻找使我的代码更有效的方法。

python performance interpreter
13个回答
11
投票

需要思考的几个问题:

  • 你真的需要检查分区直到n-1?你多久能停下来?
  • 除了2,你真的需要按两个倍数检查除法吗?
  • 3的倍数怎么样? 5?有没有办法将这个想法扩展到以前测试的素数的所有倍数?

0
投票
import math

count = 0 <br/> def is_prime(n):
    if n % 2 == 0 and n > 2:
        return False
    for i in range(3, int(math.sqrt(n)) + 1, 2):
        if n % i == 0:
            return False
    return True

for i in range(2,2000000):
    if is_prime(i):
        count += 1
        if count == 10001:
            print i
            break

0
投票

我以不同的方式接近它。我们知道2的所有倍数都不是素数(2除外)我们也知道所有非素数都可以分解成素数。

12 = 3 x 4 = 3 x 2 x 2

30 = 5×6 = 5×3×2

因此,我遍历一个奇数列表,累积一个素数列表,并且只尝试在此列表中找到具有素数的奇数的模数。

#First I create a helper method to determine if it's a prime that 
#iterates through the list of primes I already have
def is_prime(number, list):
    for prime in list:
        if number % prime == 0:
            return False
    return True

编辑:最初我是递归写的,但我认为迭代的情况要简单得多

def find_10001st_iteratively():
    number_of_primes = 0
    current_number = 3
    list_of_primes = [2]
    while number_of_primes <= 10001:
        if is_prime(current_number, list_of_primes):
            list_of_primes.append(current_number)
            number_of_primes += 1
        current_number += 2
    return current_number

0
投票

不同的快速Python解决方案:

import math
prime_number = 4 # Because 2 and 3 are already prime numbers
k = 3 # It is the 3rd try after 2 and 3 prime numbers
milestone = 10001
while k <= milestone:
    divisible = 0
    for i in range(2, int(math.sqrt(prime_number)) + 1):
        remainder = prime_number % i
        if remainder == 0: #Check if the number is evenly divisible (not prime) by i
            divisible += 1
    if divisible == 0:
        k += 1
    prime_number += 1
print(prime_number-1)

-2
投票

基于论文中的haskell代码:The Genuine Sieve of Eratosthenes by Melissa E. O'Neill

from itertools import cycle, chain, tee, islice

wheel2357 = [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]

def spin(wheel, n):
    for x in wheel:
        yield n
        n = n + x

import heapq

def insertprime(p,xs,t):
    heapq.heappush(t,(p*p,(p*v for v in xs)))

def adjust(t,x):
    while True:
        n, ns = t[0]
        if n <= x:
            n, ns = heapq.heappop(t)
            heapq.heappush(t, (ns.next(), ns))
        else:
            break

def sieve(it):
    t = []
    x = it.next()
    yield x
    xs0, xs1 = tee(it)
    insertprime(x,xs1,t)
    it = xs0
    while True:
        x = it.next()
        if t[0][0] <= x:
            adjust(t,x)
            continue
        yield x
        xs0, xs1 = tee(it)
        insertprime(x,xs1,t)
        it = xs0

primes = chain([2,3,5,7], sieve(spin(cycle(wheel2357), 11)))

from time import time
s = time()
print list(islice(primes, 10000, 10001))
e = time()
print "%.8f seconds" % (e-s)

打印:

[104743]
0.18839407 seconds

from itertools import islice
from heapq import heappush, heappop

wheel2357 = [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,
             4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]

class spin(object):
    __slots__ = ('wheel','o','n','m')
    def __init__(self, wheel, n, o=0, m=1):
        self.wheel = wheel
        self.o = o
        self.n = n
        self.m = m
    def __iter__(self):
        return self
    def next(self):
        v = self.m*self.n
        self.n += self.wheel[self.o]
        self.o = (self.o + 1) % len(self.wheel)
        return v
    def copy(self):
        return spin(self.wheel, self.n, self.o, self.m)
    def times(self, x):
        return spin(self.wheel, self.n, self.o, self.m*x)

def adjust(t,x):
    while t[0][0] <= x:
        n, ns = heappop(t)
        heappush(t, (ns.next(), ns))

def sieve_primes():

    for p in [2,3,5,7]:
        yield p

    it = spin(wheel2357, 11)

    t = []
    p = it.next()
    yield p
    heappush(t, (p*p, it.times(p)))

    while True:
        p = it.next()
        if t[0][0] <= p:
            adjust(t,p)
            continue
        yield p
        heappush(t, (p*p, it.times(p)))

from time import time
s = time()
print list(islice(sieve_primes(), 10000, 10001))[-1]
e = time()
print "%.8f seconds" % (e-s)

打印:

104743
0.22022200 seconds

import time
from math import sqrt

wheel2357 = [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]

list_prime = [2,3,5,7]

def isprime(num):
    limit = sqrt(num)
    for prime in list_prime:
        if num % prime == 0: return 0
        if prime > limit: break
    return 1

def generate_primes(no_of_primes):
    o = 0
    n = 11
    w = wheel2357
    l = len(w)
    while len(list_prime) < no_of_primes:
        i = n
        n = n + w[o]
        o = (o + 1) % l
        if isprime(i): 
            list_prime.append(i)

t0 = time.time()
generate_primes(10001)
print list_prime[-1]        # 104743
t1 = time.time()
print t1-t0                 # 0.18 seconds

打印:

104743
0.307313919067

10
投票

Project Euler的目的不是考虑学习编程,而是考虑算法。在问题#10上,你的算法需要比#7等更快。所以你需要找到一种更好的方法来查找素数,而不是更快的方式来运行Python代码。人们通过考虑数学来解决这些问题,而这些问题是你现在使用的计算机速度要慢得多。

在那个问题上,如果你真的需要帮助来思考这个问题,也许可以询问https://math.stackexchange.com/上的素数算法。


3
投票

更快的解释器不会削减它。即使用C语言或汇编语言编写的实现也不够快(在项目Euler的“大约一秒钟”时间范围内)。坦率地说,你的算法是可悲的。一些研究和思考将帮助你编写一个算法,它在狗慢解释器中运行速度比在本机代码中实现的当前算法更快(我不会说出任何具体细节,部分原因是因为这是你的工作,部分是因为我无法分辨需要多少优化)。


3
投票

许多欧拉问题(包括这个问题)都设计成一个解决方案,可以在几乎任何给定的硬件和编译器上计算可接受的时间(好吧,不是PDP-11上的INTERCAL)。

您的算法有效,但它具有二次复杂性。使用更快的解释器将为您提供线性性能提升,但在计算10,000个素数之前,二次复杂性将使它相形见绌。算法的复杂度要低得多;找到他们(或谷歌他们,没有羞耻,你仍然会学到很多)并实施它们。


2
投票

在没有讨论你的算法的情况下,PyPy解释器可以比普通的CPython解释器更快,因为这样可以进行严格的数值计算。你可能想尝试一下。


1
投票

检查你不必运行的素数,直到n-1或n / 2 ....

要更快地运行它,您只能检查直到n的平方根

这是我所知道的最快的算法

def isprime(number):
        if number<=1:
            return False
        if number==2:
            return True
        if number%2==0:
            return False
        for i in range(3,int(sqrt(number))+1):
            if number%i==0:
                return False
        return True

0
投票

正如大多数人所说,所有这些都是为了提出正确的算法。你考虑过看看Sieve of Eratosthenes吗?


0
投票
import time
t=time.time()
def n_th_prime(n):

    b=[]
    b.append(2)
    while len(b)<n :
        for num in range(3,n*11,2):
            if all(num%i!=0 for i in range(2,int((num)**0.5)+1)):
                b.append(num)


    print list(sorted(b))[n-1]
n_th_prime(10001)
print time.time()-t

版画

104743

0.569000005722秒


0
投票

一个pythonic答案

import time
t=time.time()
def prime_bellow(n):
   b=[]
   num=2
   j=0
   b.append(2)
   while len(b)-1<n:
        if all(num%i!=0 for i in range(2,int((num)**0.5)+1)):
            b.append(num)
        num += 1
   print b[n]
 prime_bellow(10001)
 print time.time()-t

打印

104743

   0.702000141144 second
© www.soinside.com 2019 - 2024. All rights reserved.