完整格式連結:https://blog.imakiseki.cf/2022/03/11/acmoi/lanqiao/chun-zhi-shu/
藍橋杯 2021 年國賽真題《純質數》的 Python 解法。
藍橋杯 2021 年國賽真題:純質數。
題目連結:https://www.lanqiao.cn/problems/1561/learning/(需要登入)。
題目大意
輸出 1 到 20210605 之間(包括兩端)的“純質數”(指十進位制各數位皆為質數的質數,1 不視作質數)。
分析
Python
本題是填空題,原則上無需時間複雜度較低的程式也可被接受。當然,這裡將給出非填空題的解法。
Python 在大資料規模的迴圈上耗時較大,而本題顯然繞不開判斷質數這一話題——乍看本題給出的資料規模達到了千萬級,判斷其是否為質數最壞情況下需要千級別的迴圈,而我們要判斷 20210605 個數字是否為質數,這個耗時顯然是不可接受的(預測在 C/C++ 下也難以接受)。
因此我們可以考慮能否降低需要判斷是否為質數的數字的數量。注意到本題中所提到的“純質數”不僅要求其本身是質數,還要求十進位制各數位也為質數——這下我們可以先借此排除掉一定不是純質數的數字,繼而大大簡化運算量。
經過分析,我們得知如果一個數要滿足“十進位制各數位為質數”這一條件,必須滿足各個數位只可取 2、3、5、7 中的一個。粗略來看,我們省下了約五分之三的運算量。而更進一步,對於不小於 10 的數字,個位數若為 2 或 5,則一定是合數,故也可提前去除。
為了更好地實現上面的需求,我們最好是自行生成純質數的“候選”,而不是生成一個長度為 20210605 的列表再刪除不符合條件的。每一個數位有可以選擇的數字,而這些選擇都是互不干擾的,因此我們可以利用標準庫 itertools
裡的 product()
生成器生成笛卡爾積,以便於快速生成符合條件的數字。實現這一需求的主要程式碼如下:
from itertools import product
# (不小於 10 的數字)非個位可取的數字集合、個位可取的數字集合
_a, _b = (2, 3, 5, 7), (3, 7)
# 生成“候選”數字的生成器
def candidate_gen():
# 1 位數
for i in _a:
yield i
# 2~8 位數
for i in range(2, 9):
# star expression,表示生成 (i-1) 個 `_a`,一個 `_b`
for j in product(*([_a] * (i-1)), _b):
# `j` 是一個由各個數位組成的元組,需要先將其拼成一個整數
x = packtup(j)
# 超出範圍,終止生成
if x > 20210605:
return
yield x
判斷質數的程式碼較為簡單,在此不作詳述,注意設定迴圈時除數的上限略高於目標數字的算術平方根即可。
完整程式碼
Python
from itertools import product
_a, _b = (2, 3, 5, 7), (3, 7)
def packtup(t):
return sum(map(lambda i: t[::-1][i] * 10 ** i, range(len(t))))
def candidate_gen():
for i in _a:
yield i
for i in range(2, 9):
for j in product(*([_a] * (i-1)), _b):
x = packtup(j)
if x > 20210605:
return
yield x
def isprime(x):
if x == 1:
return False
if x == 2:
return True
if x % 2 == 0:
return False
for i in range(3, int(x ** 0.5) + 1):
if x % i == 0:
return False
return True
print(len(list(filter(isprime, candidate_gen()))))