如何从1-10000数组中找出乘积为指定值的长度30子数组?解决内存溢出
问题分析与解决方案
你的核心问题是暴力枚举组合的方式完全不可行——itertools.combinations(1到10000, 30)对应的组合数是天文数字(C(10000,30)≈10^87),内存和计算资源根本无法支撑,直接枚举必然爆内存。同时你的代码存在语法错误(未定义变量y)、逻辑错误(迭代器不能用len()),且直接计算大数乘积会溢出。
正确思路:质因数分解+动态规划
要解决这个问题,必须把乘积相等的问题转化为质因数指数求和相等的问题,完全避免直接计算大数乘积,同时用动态规划统计符合条件的组合数。
步骤拆解
目标值的质因数分解
对于目标值(你这里是N!),用Legendre公式快速计算其质因数指数:对于质数p,N!中p的指数是sum_{i=1}^∞ floor(N/p^i),直到p^i > N。单个数字的质因数分解
对1到N的每个数字,分解其质因数,得到每个质数对应的指数(1没有质因数,指数全为0)。动态规划统计组合数
用动态规划数组dp[cnt]存储选cnt个数时,各质因数指数组合对应的组合数:- 初始状态:
dp[0][全0指数组合] = 1(选0个数的情况只有1种) - 遍历每个数字的指数组合,倒序更新
dp:对于每个已有的dp[cnt][当前指数],加上当前数字的指数得到新的指数组合,累加组合数 - 最终
dp[30][目标指数组合]就是答案
- 初始状态:
修正后的实现代码
import math from collections import defaultdict def legendre(n, p): # 计算n!中质数p的指数 count = 0 while n > 0: n = n // p count += n return count def factorize(num): # 分解单个数字的质因数,返回{质数:指数} factors = {} if num == 1: return factors # 处理2的情况 while num % 2 == 0: factors[2] = factors.get(2, 0) + 1 num = num // 2 # 处理奇数 i = 3 while i*i <= num: while num % i == 0: factors[i] = factors.get(i, 0) + 1 num = num // i i += 2 if num > 1: factors[num] = 1 return factors def count_valid_combinations(N, k): # 生成N以内的所有质数(埃氏筛) sieve = [True] * (N + 1) sieve[0] = sieve[1] = False for i in range(2, int(math.sqrt(N)) + 1): if sieve[i]: sieve[i*i : N+1 : i] = [False] * len(sieve[i*i : N+1 : i]) primes = [i for i, is_prime in enumerate(sieve) if is_prime] # 计算目标值N!的质因数指数元组 target_exps = [legendre(N, p) for p in primes] target_tuple = tuple(target_exps) # 把1~N的每个数转换成对应质数索引的指数元组 num_exp_tuples = [] for num in range(1, N + 1): factor_dict = factorize(num) exp_list = [factor_dict.get(p, 0) for p in primes] num_exp_tuples.append(tuple(exp_list)) # 动态规划初始化:dp[cnt]存储选cnt个数时的指数组合->组合数 dp = [defaultdict(int) for _ in range(k + 1)] dp[0][tuple([0]*len(primes))] = 1 # 遍历每个数的指数组合,倒序更新dp避免重复选同一个数 for exp_tuple in num_exp_tuples: for cnt in range(k-1, -1, -1): for curr_exp, curr_count in list(dp[cnt].items()): # 计算新的指数组合 new_exp = tuple(c + e for c, e in zip(curr_exp, exp_tuple)) # 剪枝:如果新指数超过目标,直接跳过 if any(ne > te for ne, te in zip(new_exp, target_tuple)): continue dp[cnt+1][new_exp] += curr_count return dp[k].get(target_tuple, 0) # 测试示例:N=10,k=3,统计乘积等于10!的长度3子数组数量 # print(count_valid_combinations(10, 3))
额外优化建议
- 提前过滤无效数字:如果某个数字的某质因数指数已经超过目标值的对应指数,直接排除该数字,减少计算量。
- 单独处理数字1:1的质因数指数全为0,可统计1的数量
m,拆分问题为选t个1(t从0到min(k,m)),剩下k-t个非1数字的指数和等于目标,分别计算后累加结果,能大幅减少动态规划的状态数。 - 使用更高效的状态存储:可以用数组代替字典存储指数组合(如果质数数量可控),进一步提升效率。
内容的提问来源于stack exchange,提问作者icebluered
相关产品推荐
相关产品推荐

