diff --git a/solutions/0051.PrimeDigitReplace/euler_51.py b/solutions/0051.PrimeDigitReplace/euler_51.py new file mode 100644 index 0000000..070d3ca --- /dev/null +++ b/solutions/0051.PrimeDigitReplace/euler_51.py @@ -0,0 +1,194 @@ +""" +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) diff --git a/solutions/0051.PrimeDigitReplace/euler_51_better.py b/solutions/0051.PrimeDigitReplace/euler_51_better.py new file mode 100644 index 0000000..399fbda --- /dev/null +++ b/solutions/0051.PrimeDigitReplace/euler_51_better.py @@ -0,0 +1,105 @@ +""" +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 time +from itertools import combinations +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 primes_list(limit: int = 10**8) -> set[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} + + +@timer +def solve() -> list[int]: + primes = primes_list(10**6) + + # 遍历所有可能的模式 + for prime in sorted(primes): + if prime < 1000: # 从4位数开始 + continue + + s = str(prime) + + # 检查每个出现3次或以上的数字 + for d in set(s): + positions = [i for i, c in enumerate(s) if c == d] + if len(positions) < 3: + continue + + # 尝试替换3个位置 + for combo in combinations(positions, 3): + if 0 in combo and d == "0": + continue + + family = [] + for new_d in "0123456789": + new_num_str = "".join( + new_d if i in combo else c for i, c in enumerate(s) + ) + new_num = int(new_num_str) + + # 确保长度相同且是素数 + if len(str(new_num)) == len(s) and new_num in primes: + family.append(new_num) + + if len(family) == 8: + return sorted(family) + + return [-1] + + +if __name__ == "__main__": + result = solve() + print(result)