简体   繁体   English

打印给定范围内的所有素数,算法太慢..如何改进? (代码挑战)

[英]printing all prime numbers in given range, algorithm too slow.. how to improve? (code-challenge)

This is a question regarding a code challenge, please don't supply too much code.. I'd like to figure this out myself as much as possible.这是一个关于代码挑战的问题,请不要提供太多代码..我想尽可能自己解决这个问题。

I recently started getting into code challenges, and combined it with learning Python (I'm a frontend javascript developer by day ;) ).我最近开始接受代码挑战,并将其与学习 Python 结合起来(我白天是前端 javascript 开发人员;))。 All is going well so far and I'm convinced that this is the best way to learn a new language (for me at least).到目前为止一切进展顺利,我相信这是学习一门新语言的最佳方式(至少对我而言)。

I'm currently stuck on a challenge that requires me to print all prime numbers in a given range, this is all done by simple Stdin and Stdout.我目前面临一个挑战,需要我打印给定范围内的所有素数,这一切都是通过简单的 Stdin 和 Stdout 完成的。

I've tried two approaches so far, both are working but are too slow.. below is a link and the code of my fastest implementation so far.到目前为止,我已经尝试了两种方法,两种方法都在工作,但都太慢了……下面是一个链接和迄今为止我最快实现的代码。 Maybe I'm missing something super obvious that is causing my python script to slow down.也许我错过了一些导致我的 python 脚本变慢的非常明显的东西。 Currently it takes 1.76s for a single test case with a range of 1, 100000目前,单个测试用例需要1.76s ,范围为1, 100000

http://ideone.com/GT6Xxk (you can debug the script here as well) http://ideone.com/GT6Xxk (你也可以在这里调试脚本)

from sys import stdin
from math import sqrt, ceil

next(stdin) # skip unecessary line that describes the number of test cases

def is_prime(number):
    initial_divider = sqrt(number)

    if number == 2:
      return True
    elif number % 2 == 0 or int(initial_divider) == initial_divider:
      return False

    for divider in range(ceil(initial_divider), 1, -1):
        if number % divider == 0:
            return False

    return True

for line in stdin:
    low, high = [int(x) for x in line.split(' ')]
    primes = [number for number
                     in range(max(low, 2), high+1)
                     if is_prime(number)]

    for prime in primes:
        print (prime)
    print('')

The description of the 'assignment' / challenge is as follows: “作业”/挑战的描述如下:

Input输入

The input begins with the number t of test cases in a single line (t<=10).输入以单行中的测试用例数 t (t<=10) 开始。 In >each of the next t lines there are two numbers m and n (1 <= m <= n <= >1000000000, nm<=100000) separated by a space.在>接下来的每一行中,有两个数字 m 和 n (1 <= m <= n <= >1000000000, nm<=100000) 用空格隔开。

Output输出

For every test case print all prime numbers p such that m <= p <= n, one number >per line, test cases separated by an empty line.对于每个测试用例打印所有质数 p 使得 m <= p <= n,一个数字>每行,测试用例由空行分隔。

Update 1: I cleaned up the logic of the last block, where the gathering of primes and printing is done:更新1:我清理了最后一个块的逻辑,这里完成了素数的收集和打印:

for line in stdin:
    low, high = [int(x) for x in line.split(' ')]
    for number in range(max(low, 2), high+1):
        if is_prime(number):
            print (number)
    print('')

Change list comprehension to generator, the script will run faster.将列表理解更改为生成器,脚本将运行得更快。

for number in range(max(low, 2), high+1):
    if is_prime(number):
        yield number

1) It might be dominated by console IO, printing the output. 1) 它可能由控制台 IO 控制,打印输出。 I changed the output so it uses a generator to collect the primes, convert the numbers to strings, and join the numbers with newlines.我更改了输出,因此它使用生成器来收集素数,将数字转换为字符串,并将数字与换行符连接起来。 This should save some memory in list building and push some Python list iteration down into the Python runtime.这应该在列表构建中节省一些内存,并将一些 Python 列表迭代下推到 Python 运行时中。 That made it ~30% faster in unscientific rushed testing on my PC, doesn't make much difference on ideone.这使得在我的 PC 上进行不科学的匆忙测试时速度提高了约 30%,在 ideone 上没有太大区别。 (This might be because I bodged it to run in Python 2, which has some very different iteration/list/generator workings, but used Python 3 on ideone). (这可能是因为我希望它在 Python 2 中运行,它有一些非常不同的迭代/列表/生成器工作方式,但在 ideone 上使用了 Python 3)。

2) You run the if number == 2: return True test every time; 2)你运行if number == 2: return True test 每次; out of the first 100,000 numbers, most of them aren't 2. I extracted that to print 2 before printing the rest of the primes.在前 100,000 个数字中,大多数都不是 2。我在打印其余的素数之前将其提取出来打印 2。 Very minor change, not really worth it.非常小的变化,不值得。

3) Your range counts down - range(ceil(initial_divider), 1, -1) - and that's really weird. 3)你的范围倒计时- range(ceil(initial_divider), 1, -1) -这是很奇怪。 It's very much more likely that a number will divide by 3 than by, say, 19. Every third number divides by 3, only every 19th number divides by 19. So for quick-return of the function, try the small dividers first, right?一个数除以 3 的可能性比除以 19 的可能性要大得多。每三个数除以 3,只有第 19 个数除以 19。因此,为了快速返回该函数,请先尝试小除法器,对吧? I set it to count up .我把它设置为向上计数。 Noticable speed improvement, I hope it's still working.显着的速度提升,我希望它仍然有效。

That's ~50% of the original runtime, in a casual and completely uncomparable situation.这是原始运行时的约 50%,在随意且完全无法比较的情况下。 Code now looks like this:代码现在看起来像这样:

from sys import stdin
from math import sqrt, ceil

next(stdin) # skip unecessary line

def is_prime(number):
    initial_divider = sqrt(number)

    if number % 2 == 0 or int(initial_divider) == initial_divider:
      return False

    for divider in range(2, ceil(initial_divider)+1):
        if number % divider == 0:
            return False

    return True

for line in stdin:
    low, high = [int(x) for x in line.split(' ')]
    primes = '\n'.join(str(number) for number
                     in range(max(low, 3), high+1)
                     if is_prime(number))

    if low <= 2: print(2)
    print (primes)
    print('')

In a language like C or C++ the SPOJ PRIME 1 problem can easily be solved by brute force, ie by writing code that sieves all numbers up to 1000,000,000 in less than a second and thus stays below the time limit.在像 C 或 C++ 这样的语言中, SPOJ PRIME 1问题可以很容易地通过蛮力解决,即通过编写代码,在不到一秒的时间内筛选出高达 1000,000,000 的所有数字,从而保持在时间限制之下。 Perhaps even in Java, C# or Delphi.甚至可能使用 Java、C# 或 Delphi。 But if it is possible in Python at all then it is probably bloody hard and requires some serious fu.但是,如果在 Python 中完全可行,那么它可能非常困难并且需要一些认真的帮助。

Note, however, that SPOJ PRIME 1 does not ask for a billion numbers to be sieved;但是请注意,SPOJ PRIME 1 并不要求筛选十亿个数字; it asks for a couple of small ranges to be sieved which are no wider than 100001 numbers, and it likely queries only a handful of ranges.它要求筛选几个不超过 100001 数字的小范围,并且它可能只查询少数范围。 Let's say the number of ranges is 100 (it's probably much less) and the average width is 100000 (it's probably much less) then that's still only 10,000,000 numbers.假设范围数为 100(可能少得多),平均宽度为 100000(可能少得多),那么这仍然只有 10,000,000 个数字。 Sieving the full billion in that situation does two orders of magnitude too much work, which is why SPOJ PRIME 1 manages to weed out the chaff with such precision despite the wide range of languages employed by pundits.在这种情况下筛选全部 10 亿人需要做两个数量级的工作,这就是为什么 SPOJ PRIME 1 尽管专家使用的语言范围很广,但仍能如此精确地清除掉渣滓。

Hence the trick is to do only what's asked - to sieve the ranges provided as input.因此,诀窍是只做要求的事情 - 筛选作为输入提供的范围。 Even the most simple, straightforward code can do that with lots of time to spare (C++: about a millisecond total).即使是最简单、直接的代码也可以用大量空闲时间来做到这一点(C++:大约一毫秒)。 The principle is exactly the same as in my answer to the challenge of drawing 100 random primes from a range with an upper bound of 1,000,000,000 , and the same solution applies.原理与我对从上限为 1,000,000,000 的范围中抽取 100 个随机素数挑战的回答完全相同,并且适用相同的解决方案。 The key is writing a function that can sieve a given range (window) without having to sieve all numbers below as well.关键是编写一个函数,它可以筛选给定的范围(窗口),而不必筛选下面的所有数字。

Besides, the question of how to beat SPOJ PRIME 1 has been asked numerous times already and the answers given are still valid.此外,如何击败 SPOJ PRIME 1 的问题已经被问过无数次,给出的答案仍然有效。 A small selection:一个小选择:

def PrimesBelow(limit):
    np=set()
    p=[2]
    for i in xrange(1,limit+1,2):
            if i == 1: continue
            if i in np: continue
            beg=2 if i % 2 == 0 else 0
            for j in xrange(beg,int(limit)+1,i):
                    np.add(j)
            p.append(i)
    return i

LetzerWille was right. LetzerWille是对的。 Function above will return a list of prime numbers below (limit) .上面的函数将返回下面(limit)的素数列表。 I think that this function will run faster than checking each number is prime or not, because this function will remove multiplies of each number and add them to (np) .我认为这个函数会比检查每个数字是否为素数运行得更快,因为这个函数将删除每个数字的乘法并将它们添加到(np) Side note: this function will test odd numbers only.旁注:此功能将仅测试奇数。

Simple improvement.简单的改进。 It was embarrasing to see this simple code.看到这个简单的代码很尴尬。 Mine was much longer and slower :( ... but I learned a lot :)我的更长更慢:(...但我学到了很多:)

Adding also simple function for measuring time mt()还添加了测量时间的简单函数 mt()

def PrimesBelow(limit):
    np=set()
    p=[2]
    for i in range(3,limit+1,2):
            if i in np: continue
            beg = i*i
            for j in range(beg,int(limit)+1,i):
                    np.add(j)
            p.append(i)
    return p

def mt(n):
    import time
    t = time.time()
    pr = PrimesBelow(n)
    print("#-Primes: {}, Time: {}".format(len(pr), round(time.time()-t, 4)))
    return pr

pr = mt(100000000)

is about 49 secs on a i7-3770 with 16GB在 16GB 的 i7-3770 上大约需要 49 秒

This will be the optimized code with less number of executions, it can calculate and display 10000 prime numbers within a second.这将是执行次数较少的优化代码,它可以在一秒钟内计算和显示 10000 个素数。 it uses the prime number property that * if a number is not divisible by the numbers which are less than its square root then it is prime number.它使用质数属性,即 * 如果一个数不能被小于其平方根的数整除,那么它就是质数。 * instead of checking till the end(Means 1000 iteration to figure out 1000 is prime or not) we can end the loop within 35 iterations, * break the loop if it is divided by any number at the beginning(if it is even loop will break on first iteration, if it is divisible by 3 then 2 iteration) so we iterate till the end only for the prime numbers * 而不是检查到最后(意味着 1000 次迭代来确定 1000 是否为素数)我们可以在 35 次迭代内结束循环, * 如果在开始时除以任何数字则中断循环(如果是偶数循环将在第一次迭代时中断,如果它可以被 3 整除,则进行 2 次迭代)所以我们只对素数迭代到最后

remember one thing you can still optimize the iterations by using the property *if a number is not divisible with the prime numbers less than that then it is prime number but the code will be too large, we have to keep track of the calculated prime numbers, also it is difficult to find a particular number is a prime or not, so this will be the Best logic or code记住一件事,你仍然可以通过使用属性来优化迭代 *如果一个数字不能被质数整除,那么它就是质数,但代码会太大,我们必须跟踪计算出的质数,也很难找到一个特定的数字是否是素数,所以这将是最好的逻辑或代码

import math
number=1
count = 0



while(count<10000):
    isprime=1
    number+=1
    for j in range(2,int(math.sqrt(number))+1):
        if(number%j==0):
            isprime=0   
            break
    if(isprime==1):
        print(number,end=" ")
        count+=1    
print("\nCount "+str(count))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM