Files
SolutionEuler/solutions/0051.PrimeDigitReplace/euler_51.py
2026-03-18 18:14:55 +08:00

195 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
By replacing the 1st digit of the 2-digit number *3,
it turns out that six of the nine possible values: 13, 23, 43, 53, 73, and 83, are all prime.
By replacing the 3rd and 4th digits of 56**3 with the same digit,
this 5-digit number is the first example having seven primes among the ten generated numbers,
yielding the family: 56003, 56113, 56333, 56443, 56663, 56773, and 56993.
Consequently 56003, being the first member of this family, is the smallest prime with this property.
Find the smallest prime which,
by replacing part of the number (not necessarily adjacent digits) with the same digit,
is part of an eight prime value family.
-----
*.000*.、*.111*.、*.222*.、*.333*.、*.444*.、*.555*.、*.666*.、*.777*.、*.888*.、*.999*.
数字的形式就是这样,最多十个数,那么,就是要在质数序列上搜索这样的数列:
1. 用欧拉筛生成质数表上限约100万足够
2. 对每个质数p
a. 检查是否有≥3个相同数字且该数字∈{0,1,2}
b. 生成所有C(n,3)种3位替换组合
c. 对每种组合用0-9替换统计质数个数
d. 若得到8个质数返回p首个即为答案
"""
import itertools
import time
from collections import Counter
from math import isqrt
from bitarray import bitarray
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"{func.__name__} time: {elapsed_time:.6f} seconds")
return result
return wrapper
def replace_digit(
number: str | int,
target_digit: str | int,
replace_count: int = 3,
replacement_digit: str | int = "0123456789",
return_positions: bool = False,
) -> list[str] | list[tuple]:
"""
使用单个替换数字,替换指定数量的目标数字
参数:
number: 输入的数字(字符串或整数)
target_digit: 要被替换的目标数字(如 '1'
replace_count: 要替换的数量(必须 <= 目标数字出现的次数)
replacement_digit: 用于替换的数字(单个字符)
return_positions: 是否同时返回被替换的位置信息
返回:
如果return_positions为False返回替换后的数字列表
如果return_positions为True返回[(替换后的数字, 被替换的位置), ...]
"""
# 转换为字符串处理
num_str = str(number)
target = str(target_digit)
replacement = str(replacement_digit)
# 找出所有目标数字的位置
target_positions = [i for i, digit in enumerate(num_str) if digit == target]
# 检查是否有足够的目标数字
if len(target_positions) < replace_count:
return [
f"错误:目标数字 '{target}' 只出现了 {len(target_positions)} 次,少于要替换的数量 {replace_count}"
]
results = []
# 遍历所有可能的要替换的位置组合
for positions in itertools.combinations(target_positions, replace_count):
# 创建新数字的列表
num_list = list(num_str)
# 替换指定位置
for pos in positions:
num_list[pos] = replacement
new_number = "".join(num_list)
if return_positions:
results.append((new_number, positions))
else:
results.append(new_number)
return results
def replace_muldigits(
number: str | int,
target_digit: str | int,
replace_count: int = 3,
replacement_digits: str | list[str] = "0123456789",
return_positions: bool = False,
) -> list:
"""
使用多个替换数字,每个数字单独进行替换
参数:
number: 输入的数字
target_digit: 目标数字
replace_count: 要替换的数量
replacement_digits: 多个替换数字(字符串或列表)
return_positions: 是否返回位置信息
返回:
字典,键为替换数字,值为对应的结果列表
"""
# 转换替换数字为列表
if isinstance(replacement_digits, str):
replacement_list = list(replacement_digits)
else:
replacement_list = replacement_digits
results = {}
for rep_digit in replacement_list:
results[rep_digit] = replace_digit(
number, target_digit, replace_count, rep_digit, return_positions
)
results = list(results.values())
res = [[] for _ in range(len(results[0]))]
for r in results:
for i, val in enumerate(r):
if val[0] != "0":
res[i].append(int(val))
return [sorted(list(set(val))) for val in res]
def primes_list(limit: int = 10**8) -> list[int]:
is_prime = bitarray(limit + 1)
is_prime.setall(True)
is_prime[:2] = False # 0和1不是素数
# 只需筛到 sqrt(n)
imax = isqrt(limit) + 1
for i in range(2, imax):
if is_prime[i]:
# 步长i从i*i开始小于i*i的已被更小的素数筛过
is_prime[i * i : limit + 1 : i] = False
return [i for i, isp in enumerate(is_prime) if isp]
def have_n_primes(primes: list[int], target: list[int]) -> int:
count = 0
for n in target:
if n in primes:
count += 1
return count
@timer
def solve() -> list[int]:
primes = primes_list()
for p in primes:
if p < 1000:
continue
digits = [int(d) for d in str(p)]
digit_counts = Counter(digits)
if max(digit_counts.values()) < 3:
continue
digs = [k for k, v in digit_counts.items() if v >= 3 and k in [0, 1, 2]]
for dig in digs:
testList = replace_muldigits(p, dig)
for xList in testList:
if have_n_primes(primes, xList) == 8:
return [x for x in xList if x in primes]
return []
if __name__ == "__main__":
result = solve()
print(result)