1亿以内素数的个数_算法|找出给定范围的所有素数
給定n,我們如何輸出n以內(不含n)的所有素數?
使用Python,完成函數體,要求返回n以內的素數個數和這些素數。
def countPrimes(n):"""itype -> intrtype -> intrtype -> list"""關于評價算法速度:如何計算時間復雜度1.直覺算法
按部就班地遍歷2到n-1,依次判斷素數。
def countPrimes(n):primes = []if n < 2:return 0, primesfor i in range(2,n):if isPrime(i):primes.append(i)return len(primes), primesdef isPrime(k):"""itype: int, >=2rtype: int"""r_isPrime = Truefor i in range(2,k):if k%i == 0:r_isPrime = Falsereturn r_isPrime from time import process_time t1 = process_time() n = int(input("Input a positive integer: ")) count_primes, primes = countPrimes(n) t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes)) print("Elapsed time: {}".format(t2-t1)) Input a positive integer: 10000The number of primes within 10000 is 1229: Elapsed time: 2.875評價
直接地,得到時間復雜度(時間復雜讀不考慮系數):
但是這樣地算法存在冗余,太耗時了。 對n=20,我們只需要檢查
。- 20=2*10
- 20=4*5
- 20=
- 20=5*4
- 20=10*2 觀察到只需要檢查20在小于 中的正整數中是否找能被整除的因數,找不到那么后面一半也找不到,找到了就直接判斷為素數。
2.平方根算法
檢查數k時,遍歷因數i范圍為
。def isPrime(k):"""itype: int, >=2rtype: int"""r_isPrime = Truefor i in range(2,int(k**0.5)+1):if k%i == 0:r_isPrime = Falsereturn r_isPrime from time import process_time t1 = process_time() n = int(input("Input a positive integer: ")) count_primes, primes = countPrimes(n) t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes)) print("Elapsed time: {}".format(t2-t1)) Input a positive integer: 10000The number of primes within 10000 is 1229: Elapsed time: 2.5625評價
發現優化得效果也沒好多少,用時減少一丁點。函數isPrime()時間復雜度為
。 發現其實還有冗余,如果已知2為素數,那么就不應該多余地去檢查4,6,8這些2的倍數。按照這樣的原理,演化到下一個算法。素數篩-Sieve of Eratosthenes
篩掉已知素數的倍數。
def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, n):if prime_sieve[i]:for j in range(i*2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1] from time import process_time t1 = process_time() n = int(input("Input a positive integer: ")) count_primes, primes = countPrimes(n) t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes)) print("Elapsed time: {}".format(t2-t1)) Input a positive integer: 1000000The number of primes within 1000000 is 78498: Elapsed time: 0.28125篩掉倍數后,算法表現提升顯著,還有可以提升的空間。 - for i in range(2, n):是在遍歷所有小于n大于等于2的正整數,當n=20,我們不需要遍歷所有的大于
的正整數,因為不是素數的肯定通過遍歷前面一半的時候篩掉了,是素數的素數篩值保持不變。那么可以將遍歷范圍變為 。def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, int(n**0.5)+1):if prime_sieve[i]:for j in range(i*2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]from time import process_time t1 = process_time() n = int(input("Input a positive integer: ")) count_primes, primes = countPrimes(n) t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes)) print("Elapsed time: {}".format(t2-t1)) Input a positive integer: 1000000The number of primes within 1000000 is 78498: Elapsed time: 0.1875OHHHHHHHHHHHHHHHH 外層的循環范圍優化了,現在考慮內層循環for j in range(i*2, n, i):。 當n=20,i=5時會篩掉10,15,20,25...,但是這10,15,20已經被素數2,3篩掉。小于當前素數的因數,要么是更小的素數,要么是更小的素數的倍數,當前素數與這些因數相乘,肯定被篩過了,所以只需要從
開始檢查。def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, int(n**0.5)+1):if prime_sieve[i]:for j in range(i**2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]from time import process_time t1 = process_time() n = int(input("Input a positive integer: ")) count_primes, primes = countPrimes(n) t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes)) print("Elapsed time: {}".format(t2-t1)) Input a positive integer: 1000000The number of primes within 1000000 is 78498: Elapsed time: 0.15625時間復雜度為:
.評價
以此來看,本文截至此處目前最好的素數算法為Sieve of Eratosthenes。代碼:
def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, int(n**0.5)+1):if prime_sieve[i]:for j in range(i**2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]Euler's Sieve
現在介紹一個有線性時間復雜度$mathcal{O(n)}$的算法:歐拉篩法。
算法:
整個過程為:
def countPrimes(n):if n < 2:return 0, primescur_list = list(range(2, n))primes = []while True:cur_prime = cur_list[0]backup_list = cur_list.copy()for j in backup_list:if j*cur_prime not in cur_list:breakcur_list.remove(j*cur_prime)primes.append(cur_prime)cur_list.pop(0)if cur_prime**2 > n:primes.extend(cur_list)breakreturn len(primes), primesfrom time import process_time t1 = process_time() n = int(input("Input a positive integer: ")) count_primes, primes = countPrimes(n) t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes)) print("Elapsed time: {}".format(t2-t1))Input a positive integer: 1000 The number of primes within 1000 is 168: Elapsed time: 0.013890345999996612Input a positive integer: 10000 The number of primes within 10000 is 1229: Elapsed time: 0.36447122299999535
Input a positive integer: 100000 The number of primes within 100000 is 9592: Elapsed time: 49.40181045400001
根據對區區10000的輸入,我寫的算法就表現得不太行,實際上輸入1000000就接近崩潰了,算法本身沒問題,我實現的代碼不夠還原,肯定有很多冗余。這兩天折騰夠了,以后再看看。
Changelog
- 2020/04/03: 感謝評論區 @天空和云 的提醒,修改了兩處代碼。
- 2020/02/15: 更新了歐拉篩法,思路正確但是代碼有缺陷。
總結
以上是生活随笔為你收集整理的1亿以内素数的个数_算法|找出给定范围的所有素数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GridView 自写分页 存储过程
- 下一篇: python导入文件-如何导入其他Pyt