问题陈述
生成尽可能多的不同质数P,使得反向(P)也是质数
并且不等于P。
输出:
每行打印一个整数(≤1015)。总共打印不超过
106个整数。
评分:
让N =正确的输出。 M =错误的输出。您的分数
将为最大值(0,N-M)。
注意:仅P和reverse(P)之一将被视为正确。如果两个文件都
,则将其视为不正确。
示例输出
107 13 31 17 2
说明
分数将为1。由于13,107,17是正确的。 31是不正确的,因为13已经存在。 2不正确。
时间限制2秒(时间限制是针对每个输入文件的。)
内存限制
源代码限制
25 KB原为:16.452秒
我的问题是,如果我们必须使用Python语言,是否可以进一步优化以下代码,是否可以将执行时间减少到2秒。
from time import time
start = time()
lsta=[] # empty list used to hold prime numbers created by primes function
LIMIT = pow(10,6)
# binary search function
def Bsearch(lsta,low,high,search):
if low>high:
return False
else:
mid=int((low+high)/2)
if search<lsta[mid]:
return(Bsearch(lsta,low,mid-1,search))
elif search>lsta[mid]:
return(Bsearch(lsta,mid+1,high,search))
elif search==lsta[mid]:
return True
else:
return False
# prime number creating function using sieve of Eras** algorithm
def primes(LIMIT):
dicta = {}
for i in range(2,LIMIT):
dicta[i]=1
for i in range(2,LIMIT):
for j in range(i,LIMIT):
if i*j>LIMIT:
break
dicta[i*j]=0
for i in range(2,LIMIT):
if(dicta[i]==1):
lsta.append(i)
final = [] # used to hold the final output values
primes(LIMIT)
for i in range(len(lsta)):
# prime number compared with reversed counterpart
if(int(str(lsta[i])[::-1])<=lsta[len(lsta)-1]):
if Bsearch(lsta,i+1,len(lsta)-1,int(str(lsta[i])[::-1])):
if not(int(str(lsta[i])[::-1])==lsta[i]):
final.append(str(lsta[i]))
for i in range(len(final)-1,0,-1):
print(final[i])
print(13)
end=time()
print("Time Taken : ",end-start)
#1 楼
通过有效地实现Eratosthenes筛子和@tobias_k概述的一般方法,我可以在0.5秒内运行它,包括打印。尝试以下操作:def primes(limit):
# Keep only odd numbers in sieve, mapping from index to number is
# num = 2 * idx + 3
# The square of the number corresponding to idx then corresponds to:
# idx2 = 2*idx*idx + 6*idx + 3
sieve = [True] * (limit // 2)
prime_numbers = set([2])
for j in range(len(sieve)):
if sieve[j]:
new_prime = 2*j + 3
prime_numbers.add(new_prime)
for k in range((2*j+6)*j+3, len(sieve), new_prime):
sieve[k] = False
return prime_numbers
评论
\ $ \ begingroup \ $
您提出了一种替代解决方案,但尚未检查代码。请对其进行编辑,以说明您的推理(解决方案的工作原理以及对原始解决方案的改进),以便所有人都可以从您的思考过程中学习。
\ $ \ endgroup \ $
– Toby Speight
18年4月19日在10:42
#2 楼
您可以优化一些事情:如果您(或分级服务器?)在Python 2.x上运行此程序,则绝对应该使用
xrange
而不是range
(大约2 在素数筛中,仅当当前数是素数(低至1s)时,才需要检查倍数;另外,不要乘以
i
s的倍数。使用规则数组而不是筛子中的字典(0.8s)
不要进行字符串转换和反转,而不必要的次数更多;另外,也可以使用
set
s快速查找(0.4s),而不是执行自己的二进制搜索。我的版本(已更新):
def primes(limit):
numbers = [0, 0] + [1] * (limit-2)
for i in xrange(2, limit):
if numbers[i]:
for k in xrange(i**2, limit, i):
numbers[k] = 0
return set(i for i, e in enumerate(numbers) if e)
ps = primes(LIMIT)
final = set()
for p in ps:
q = int(str(p)[::-1])
if p != q and q in ps and q not in final:
final.add(p)
>不确定您的情况是否将运行时间从16s缩短为2s,但这可能是一个开始。作为一种绝望的措施,您可以减少
LIMIT
-最好不要发现太多,而要超时。评论
\ $ \ begingroup \ $
谢谢您的建议,是的,我现在相信它的减少不能少于您在此处添加的内容,也许我应该用C或C ++编写此程序。
\ $ \ endgroup \ $
– Akash Rana
2014年3月31日17:17
\ $ \ begingroup \ $
在我看来,您的基本方法不错,但这并不是Erathostenes筛网的最佳实现。对于p!= q,检查p> 9是多余的,并且对于大多数数字来说放慢了速度。
\ $ \ endgroup \ $
– Jaime
2014年3月31日17:27
\ $ \ begingroup \ $
@Jaime感谢您的指导!我使用三参数范围(x)而不是乘以来改进筛子,但是为了提高可读性,并没有完全复制答案(+1),只剩下了奇数技巧。将时间再减少20%(您的时间甚至减少40%)。
\ $ \ endgroup \ $
–tobias_k
2014年3月31日在18:27
\ $ \ begingroup \ $
当i是素数时,要从筛子上移除的第一个非素数是i * i,而不是i * 2。我想你在哪里写i ** 2?这样可以节省更多时间,因为它避免了很多多余的筛分。
\ $ \ endgroup \ $
– Jaime
2014年3月31日在18:46
\ $ \ begingroup \ $
如果p!= ps中的q和q且q不是最终值:如果重写为p \ $ \ endgroup \ $
– Peter Taylor
18年4月19日在10:28
#3 楼
使用快速的素数生成器请参见以下该线程(python 3)中最好的纯python素数生成器:
def primes(n):
sieve = [True] * (n//2)
for i in range(3, int(n**0.5) + 1, 2):
if sieve[i//2]:
sieve[i*i//2::i] = [False] * ((n-i*i-1)//(2*i)+1)
return [2] + [2*i+1 for i in range(1, n//2) if sieve[i]]
使用一个python集
您可以使用本机python模块bisect执行二进制搜索。但是使用一组素数更简单,更快捷。有关时间复杂度的信息,请参见此页面:在原始素数上(以避免重复),然后在素数集中查找它:
prime_set = set(primes(upper_bound))
将运行逻辑与求解逻辑分开
它使参数化和测试程序(python 3)更加容易:
def palindromic_primes(upper_bound):
prime_set = set(primes(upper_bound))
for p in prime_set:
r = int(str(p)[::-1])
if r > p and r in prime_set:
yield p
多个上限的结果
if __name__ == '__main__':
t = time.time()
upper_bound = int(eval(sys.argv[1]))
generator = palindromic_primes(upper_bound)
for score, x in enumerate(generator, 1):
print(x)
delta = time.time() - t
status = "Score = {} in {:.3f} s".format(score, delta)
print(status, file=sys.stderr)
评论
\ $ \ begingroup \ $
我想知道使用int→str→int转换来反转数字是否有限制。显而易见的算术方法会更快吗?
\ $ \ endgroup \ $
– Toby Speight
18年4月20日在13:03
\ $ \ begingroup \ $
@TobySpeight我刚刚对其进行了基准测试,字符串方法的速度大约快了3倍。
\ $ \ endgroup \ $
– Vincent
18年4月20日在14:31
\ $ \ begingroup \ $
这让我感到惊讶-感谢您的演示!
\ $ \ endgroup \ $
– Toby Speight
18年4月20日在14:34
#4 楼
使用numba
甚至可以在不到一秒钟的时间内运行此代码。('Time Taken : ', 0.7115190029144287)
稍微基于@jaime版本的代码应该用
numpy.arrays
重写。在这里看到import numpy as np
import numba
from time import time
start = time()
LIMIT = pow(10,6)
# binary search function
def Bsearch(lsta,low,high,search):
if low>high:
return False
else:
mid=int((low+high)/2)
if search<lsta[mid]:
return(Bsearch(lsta,low,mid-1,search))
elif search>lsta[mid]:
return(Bsearch(lsta,mid+1,high,search))
elif search==lsta[mid]:
return True
else:
return False
def primes(limit):
# Keep only odd numbers in sieve, mapping from index to number is
# num = 2 * idx + 3
# The square of the number corresponding to idx then corresponds to:
# idx2 = 2*idx*idx + 6*idx + 3
sieve = [True] * (limit // 2)
prime_numbers = set([2])
for j in range(len(sieve)):
if sieve[j]:
new_prime = 2*j + 3
prime_numbers.add(new_prime)
for k in range((2*j+6)*j+3, len(sieve), new_prime):
sieve[k] = False
return list(prime_numbers)
@numba.jit('void(uint8[:])', nopython=True)
def primes_util(sieve):
ssz = sieve.shape[0]
for j in xrange(ssz):
if sieve[j]:
new_prime = 2*j + 3
for k in xrange((2*j+6)*j+3, ssz, new_prime):
sieve[k] = False
def primes_numba(limit):
sieve = np.ones(limit // 2, dtype=np.uint8)
primes_util(sieve)
return [2] + (np.nonzero(sieve)[0]*2 + 3).tolist()
final = [] # used to hold the final output values
lsta = primes_numba(LIMIT)
for i in xrange(len(lsta)):
# prime number compared with reversed counterpart
if(int(str(lsta[i])[::-1])<=lsta[len(lsta)-1]):
if Bsearch(lsta,i+1,len(lsta)-1,int(str(lsta[i])[::-1])):
if not(int(str(lsta[i])[::-1])==lsta[i]):
final.append(str(lsta[i]))
for i in xrange(len(final)-1,0,-1):
print(final[i])
print(13)
end=time()
print("Time Taken : ",end-start)
评论
\ $ \ begingroup \ $
您提出了一种替代解决方案,但尚未检查代码。请对其进行编辑,以说明您的推理(解决方案的工作原理以及对原始解决方案的改进),以便所有人都可以从您的思考过程中学习。
\ $ \ endgroup \ $
– Toby Speight
18年4月19日在10:43
#5 楼
这大约用了1350万个点,用了1350万个限制,在大约2秒钟内得到了基本上,我的策略是创建一个由布尔组成的查找列表,在主循环的每个循环结束时,保证下一个比索引值高的下一个真值是质数。
评估新质数时,我要做的第一件事是从列表的其余部分中消除其倍数。
此后,我得到当前质数的倒数并执行:
Case1如果反转的值小于非反转的值,我使用查找列表检查它是否也是素数。如果它也是一个素数,则仅添加原始值。
情况2如果反转的值高于我的整体限制,我将使用通用素数评估功能对其进行简单检查。如果是素数,我添加非反向素数
Case3如果反向值高于非反向素数且小于限制,我将忽略它,因为在Case1
from time import time
def is_prime(n):
for i in xrange(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
def DoMath(limit):
start = time()
lkup = [True,True,True] +[ bool(ii%2) for ii in xrange(3,limit)]
with open("text.txt", 'w') as file:
index = 3
r_index = 0
str_index = ''
while index < limit:
if lkup[index]:
for ii in xrange(index*2, limit, index):
lkup[ii] = False
str_index = str(index)
r_index = int(str_index[::-1])
if r_index >= limit and is_prime(r_index):
file.write(str_index+'\n')
elif r_index < index and lkup[r_index]:
file.write(str_index+'\n')
index += 1
end=time()
print("Time Taken : ",end-start)
评论
\ $ \ begingroup \ $
您提出了一种替代解决方案,但尚未检查代码。请对其进行编辑,以说明您的推理(解决方案的工作原理以及对原始解决方案的改进),以便所有人都可以从您的思考过程中学习。
\ $ \ endgroup \ $
– Toby Speight
18年4月19日在10:43
评论
有关生成素数的信息,请参见Eratosthenes筛-Python为什么要等到编译完整个列表才能开始输出?
@DavidHarkness:条件是P是素数,其反向是素数,并且其反向不等于P。2满足前两个条件,但不满足第三个条件。
@StijndeWitt:不,没有(广泛理解的)质数定义要求将质数强制为奇数。这似乎是一个误会。
@Oddthinking:有趣,因为我记得认为2不是一个素数真的很愚蠢...似乎我的第一个直觉毕竟是对的...但是我对一个也有同样的感觉,但是我肯定是错的。顺便说一句,您的用户名现在非常适合:)