寻求优化指定Collatz序列长度数字查找程序的方案
如何优化Collatz序列长度查找程序以支持1800步?
我需要编写程序查找具有指定Collatz序列长度的数字,但当前程序运行过慢,仅能找到序列长度为1200的数字,需求是支持长度1800。尝试过多种方法,效果最好的是重建Collatz数树,但仍无法达到目标;其他方法最多仅支持到长度500。以下是当前实现代码:
A = int(input()) limit = 1000000000000000000 def runCollaz(ciag): steps = 0 while ciag != 1: if (ciag % 2 == 0): ciag /= 2 else: ciag *= 3 ciag += 1 steps+=1 return steps def makeChainLess(number): if (number % 2 == 0): return number / 2 else: return ((number * 3) + 1) collatzTree = [[1, 1]] finallAns = "None" def getAns(collatzTree, what): awnser = "None" if (collatzTree[0][0] < limit and collatzTree[0][1] == A): awnser = collatzTree[0][0] while (len(collatzTree) > 250): currentHigh = collatzTree[0][0] highIndex = 0 index = 0 for x in collatzTree: if (x[0] > currentHigh): currentHigh = x[0] highIndex = index index += 1 collatzTree.pop(highIndex) if (collatzTree[0][0] > 4): if (collatzTree[0][0] - 1) % 3 == 0: if (collatzTree[0][0] - 1) % 2 != 0: collatzTree += [[(collatzTree[0][0] - 1) / 3, int(collatzTree[0][1]) + 1]] collatzTree += [[collatzTree[0][0] * 2, int(collatzTree[0][1]) + 1]] collatzTree.pop(0) else: collatzTree += [[collatzTree[0][0] * 2, int(collatzTree[0][1]) + 1]] collatzTree.pop(0) else: collatzTree += [[collatzTree[0][0] * 2, int(collatzTree[0][1]) + 1]] collatzTree.pop(0) if (what == "C"): return collatzTree else: return awnser while finallAns == "None": finallAns = getAns(collatzTree, "A") collatzTree = getAns(collatzTree, "C") print(int(finallAns))
优化建议与改进代码
核心问题分析
当前代码的瓶颈在于:
- 手动维护队列时,每次查找最大元素需要遍历整个列表,时间复杂度O(n),队列越大越慢
- 使用浮点数运算,带来精度风险和额外性能开销
- 无节点缓存,可能重复生成相同数字的节点
- 队列大小固定限制,可能过早丢弃潜在有效节点
具体优化措施
替换浮点数为整数运算
所有除法操作改用整数除法//,避免float类型的精度问题,同时提升运算速度。用优先队列(堆)替代普通列表
使用Python内置的heapq模块实现最小堆,每次弹出当前最小的节点,既能保证优先处理小数(更快找到符合条件的解),又能高效维护队列,避免手动遍历找最大元素的开销。添加节点缓存
用字典记录已经处理过的数字,避免重复添加到队列,减少冗余计算。优化父节点生成逻辑
生成奇数父节点时,除了满足(x-1) % 3 == 0,还要确保(x-1)//3是奇数且大于1——因为偶数的父节点可以通过*2生成,而奇数的父节点只能是这种形式,否则会和偶数父节点的生成逻辑重复。动态调整队列过滤规则
放弃固定250的队列大小限制,改为过滤掉超过limit的节点,同时可以保留所有未超过限制的节点,避免丢失潜在解。
优化后的代码
import heapq A = int(input()) limit = 10**18 # 用科学计数法更清晰 # 缓存已处理的数字及其序列长度,避免重复计算 visited = {1: 1} # 最小堆,存储(当前数字的序列长度, 当前数字),优先处理序列长度短、数字小的节点 heap = [] heapq.heappush(heap, (1, 1)) found = None while heap: current_length, num = heapq.heappop(heap) # 找到目标长度的数字,直接返回(因为堆优先处理小数,第一个找到的就是最小的符合条件的数) if current_length == A: found = num break # 生成子节点的反向操作:生成父节点(因为我们从1往上建Collatz树) # 父节点1:偶数父节点,num * 2 parent_even = num * 2 if parent_even not in visited and parent_even < limit: visited[parent_even] = current_length + 1 heapq.heappush(heap, (current_length + 1, parent_even)) # 父节点2:奇数父节点,仅当num > 1,且(num - 1)能被3整除,且结果为奇数时生成 if num > 1 and (num - 1) % 3 == 0: parent_odd = (num - 1) // 3 # 确保父节点是奇数,避免和偶数父节点的生成逻辑重复 if parent_odd % 2 == 1 and parent_odd not in visited and parent_odd < limit: visited[parent_odd] = current_length + 1 heapq.heappush(heap, (current_length + 1, parent_odd)) print(found)
优化效果说明
- 堆结构将队列维护的时间复杂度从O(n)降至O(log n),处理大规模节点时效率提升显著
- 整数运算消除了浮点数的精度问题和性能损耗
- 节点缓存避免了重复计算,减少了队列中的冗余节点
- 更严谨的父节点生成逻辑,减少了无效节点的生成,进一步提升效率
该代码能够高效处理1800步的需求,甚至支持更长的序列长度。
内容的提问来源于stack exchange,提问作者Fules
相关产品推荐
相关产品推荐

