问题是:
给定一系列数字(x,y)
,查找所有素数(仅计数),它们是两个数字的平方和,其限制为0<=x<y<=2*(10^8)
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)
但在某些情况下,这会增加时间复杂度和内存限制。
这是我的提交结果:
任何人都可以帮助我通过更好的算法降低时间复杂度和内存限制吗?
Problem Link(不是正在进行的比赛FYI)
答案 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
,但它是两个方格的总和,所以你可以保留原样......