素数可以写成两个数字x和y的平方和

时间:2014-04-09 08:16:11

标签: python algorithm primes

问题是:

给定一系列数字(x,y),查找所有素数(仅计数),它们是两个数字的平方和,其限制为0<=x<y<=2*(10^8)

根据Fermat's theorem

Fermat's theorem on sums of two squares asserts that an odd prime number p can be   
expressed as p = x^2 + y^2 with integer x and y if and only if p is congruent to 
1 (mod4).

我做过这样的事情:

import math
def is_prime(n):
    if n % 2 == 0 and n > 2:
        return False
    return all(n % i for i in range(3, int(math.sqrt(n)) + 1, 2))

a,b=map(int,raw_input().split())
count=0
for i in range(a,b+1):
    if(is_prime(i) and (i-1)%4==0):
        count+=1
print(count)

但在某些情况下,这会增加时间复杂度内存限制

这是我的提交结果:

enter image description here

任何人都可以帮助我通过更好的算法降低时间复杂度和内存限制吗?

Problem Link(不是正在进行的比赛FYI)

3 个答案:

答案 0 :(得分:5)

不要检查每个数字是否为素数。使用Sieve of Eratosthenes预先计算范围内的所有素数。这将大大降低复杂性。

由于您拥有最多200M的数字和256Mb的内存限制,并且每个数字至少需要4个字节,因此您需要一点点黑客。不要将所有数字的筛子初始化为y,但只能使用不能被2,3和5整除的数字。这样可以减小筛子的初始尺寸,使其足以适应内存限制。

UPD 正如Will Ness在评论中正确指出的那样,筛选仅包含标志,而不包含数字,因此每个元素需要不超过1个字节,您甚至不需要这个预先计算黑客。

答案 1 :(得分:1)

您可以通过将for i in range(a,b+1):更改为for i in xrange(a,b+1):来减少内存使用量,这样就不会在内存中生成整个列表。

你可以在下面的陈述中做同样的事情,但你是对的,它对时间没有帮助。

return all(n % i for i in xrange(3, int(math.sqrt(n)) + 1, 2))

一次性优化可能不会像其他答案一样在内存方面花费太多,而是使用Fermat's Little Theorem。它可能会帮助您尽早拒绝许多候选人。 更具体地说,您可以选择3或4个随机值来测试,如果其中一个拒绝,那么您可以拒绝。否则你可以做你目前正在进行的测试。

答案 2 :(得分:0)

首先,虽然它不会改变您的时间复杂度的顺序,但您仍然可以将检查的数字列表缩小6倍,因为您只需要检查等于1 mod 12或等于5 mod 12的数字(例如[1,5],[13,17],[25,29],[37,41]等)

由于您只需计算两个数字的平方和的素数,因此顺序并不重要。因此,您可以将range(a,b+1)更改为range(1,b+1,12)+range(5,b+1,12)

显然,您可以删除功能if n % 2 == 0 and n > 2中的is_prime条件,此外,将if is_prime(i) and (i-1)%4 == 0条件更改为if is_prime(i)

最后,您可以通过将除以与6的倍数相邻的数字(例如[5,7],[11,13],[]来检查每个数字的素数。 17,19],[23,25]等。

所以你可以改变这个:

range(3,int(math.sqrt(n))+1,2)

对此:

range(5,math.sqrt(n))+1,6)+range(7,math.sqrt(n))+1,6)

你也可以预先计算math.sqrt(n))+1

总结这一切,以下是如何改善计划的整体表现:

import math

def is_prime(n):
    max = int(math.sqrt(n))+1
    return all(n % i for i in range(5,max,6)+range(7,max,6))

count = 0
b = int(raw_input())
for i in range(1,b+1,12)+range(5,b+1,12):
    if is_prime(i):
        count += 1
print count

请注意1通常不被视为素数,因此您可能需要打印count-1。另一方面,2不等于1 mod 4,但它是两个方格的总和,所以你可以保留原样......