十六进制Armstrong数查找算法优化问询:10位计算提速需求
针对你的十六进制Armstrong数计算优化需求,我整理了几个实用的方向,每个都能帮你大幅压缩运行时间,尤其是10位这种较大的位数场景:
1. 预计算幂值+批量求和,替代逐个累加
你当前的genPow可以简化为一个字典,提前缓存0-f每个字符的digits次方值,而且求和时不用逐个遍历组合里的字符,而是按字符出现的次数批量计算——比如组合里有3个5,直接用3 * pow_dict['5'],省去循环开销:
# 预计算幂字典,替代genPow pow_dict = {d: int(d, 16)**digits for d in '0123456789abcdef'} # 计算组合的幂和时,用计数批量计算 from collections import Counter comb_counter = Counter(comb) sum_val = sum(count * pow_dict[d] for d, count in comb_counter.items())
2. 提前剪枝无效组合,避免无用计算
在生成组合或计算幂和的过程中,直接跳过不可能符合条件的情况:
- 范围剪枝:先算出digits位十六进制数的范围:
lower = 16 ** (digits-1),upper = (16 ** digits) - 1。如果计算出的sum_val不在这个区间里,直接跳过,不用再转成十六进制字符串。 - 累加中途剪枝:如果在批量求和前,发现当前已选字符的幂和加上剩余位置全填最大字符
f的幂和仍小于lower,或者加上剩余位置全填最小字符0的幂和仍大于upper,直接跳过这个分支(递归实现时这一点更高效)。
3. 优化组合与幂和的匹配逻辑,抛弃Counter的低效操作
当前的check函数用Counter的减法比较,其实可以把字符计数转换成固定顺序的元组,直接做等值判断,速度快很多:
# 把字符序列的计数转换成元组(按0-f的顺序统计次数) def get_count_tuple(s): count = [0]*16 for c in s: count[int(c,16)] += 1 return tuple(count) # 组合的计数元组 comb_tuple = get_count_tuple(comb) # 幂和转十六进制后的计数元组 hex_str = format(sum_val, 'x') hex_tuple = get_count_tuple(hex_str) # 直接比较元组,比Counter操作快数倍 if comb_tuple == hex_tuple: results.append(hex_str)
这种方式是针对固定的16个字符做定向统计,没有通用Counter的额外开销,匹配效率会大幅提升。
4. 用递归生成组合+剪枝,替代itertools的全量生成
递归方案的核心优势是边生成边剪枝,而不是先生成所有组合再过滤。比如递归函数可以跟踪当前选到的字符索引、已选位数、当前幂和、计数元组,每一步都判断是否还有可能符合条件,不符合就直接终止分支:
def recursive_armstrong(digits): pow_vals = [i**digits for i in range(16)] # 预计算0-f的digits次方 lower = 16 ** (digits-1) upper = (16**digits) - 1 results = set() def backtrack(start, remaining, current_sum, count): if remaining == 0: if lower <= current_sum <= upper: hex_str = format(current_sum, 'x') # 生成hex_str的计数元组 hex_count = [0]*16 for c in hex_str: hex_count[int(c,16)] += 1 if tuple(hex_count) == tuple(count): results.add(hex_str) return # 从start开始选,避免生成重复组合(比如选1后不再选0) for i in range(start, 16): # 剪枝:即使剩下全选当前字符,总和仍不够下限,跳过 max_possible = current_sum + remaining * pow_vals[i] if max_possible < lower: continue # 尝试选k个当前字符,k从1到剩余位数 for k in range(1, remaining+1): new_sum = current_sum + k * pow_vals[i] if new_sum > upper: break # 再选更多会超上限,直接跳出循环 count[i] += k backtrack(i, remaining - k, new_sum, count) count[i] -= k backtrack(0, digits, 0, [0]*16) return sorted(results)
这个递归版本会在每一步跳过那些不可能满足范围的分支,大幅减少无效计算量,对于10位这种场景,效率提升会非常明显。
5. 用位运算判断十六进制位数,避免字符串转换开销
在判断sum_val的十六进制位数是否等于digits时,不用先转成字符串再取长度,可以用位运算快速计算:
# 十六进制位数 = ceil(二进制位数 /4) = (二进制位数 +3) //4 if (sum_val.bit_length() +3) //4 != digits: continue
这比len(format(sum_val, 'x'))快很多,因为字符串转换是相对耗时的操作,能省则省。
把这些优化结合起来,10位十六进制Armstrong数的计算时间应该能从60秒压缩到几秒以内。
内容的提问来源于stack exchange,提问作者Claypenguin
相关产品推荐
相关产品推荐

