You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化Python查找大型无向图(374节点)所有环的性能问题

无向图全环查找性能优化方案(针对374节点/1376边规模)

核心优化思路

针对递归实现的性能瓶颈,从去重逻辑、迭代替代递归、数据结构优化、内存控制四个方向入手,彻底降低无效计算和资源开销:


1. 关键优化点与代码实现

1.1 节点ID映射(字符串转整数)

加密货币名称为字符串,直接用于数组索引、比较会拖慢效率,先映射为连续整数ID:

# 假设原始边数据为 (加密货币A, 加密货币B) 格式的列表
edges = [("BTC", "ETH"), ("ETH", "BNB"), ("BNB", "BTC"), ...]

# 提取所有唯一节点并映射为整数ID
nodes = list({u for u, v in edges} | {v for u, v in edges})
node_to_id = {node: idx for idx, node in enumerate(nodes)}
id_to_node = {idx: node for node, idx in node_to_id.items()}

1.2 预处理邻接表

构建整数ID的邻接表并排序,提升遍历效率:

num_nodes = len(nodes)
adj = [[] for _ in range(num_nodes)]
for u, v in edges:
    u_id = node_to_id[u]
    v_id = node_to_id[v]
    adj[u_id].append(v_id)
    adj[v_id].append(u_id)

# 对每个节点的邻居排序,配合后续迭代回溯逻辑
for neighbors in adj:
    neighbors.sort()

1.3 迭代回溯+去重逻辑(核心优化)

替换递归为迭代回溯,同时通过固定环的起点为环中最小ID节点的策略,彻底避免重复枚举同一环(如A→B→C→A和B→C→A→B视为同一环):

def yield_all_cycles(adj, node_to_id, id_to_node):
    num_nodes = len(node_to_id)
    # 按ID顺序遍历每个可能的环起点
    for start in range(num_nodes):
        path = [start]
        visited = [False] * num_nodes
        visited[start] = True
        # 栈元素:(当前节点, 邻居迭代器, 是否处于回溯阶段)
        stack = [(start, iter(adj[start]), False)]
        
        while stack:
            current, neighbor_iter, is_backtracked = stack.pop()
            
            if is_backtracked:
                # 回溯:恢复路径和访问状态
                path.pop()
                visited[current] = False
                continue
            
            # 标记当前节点为待回溯状态
            stack.append((current, neighbor_iter, True))
            
            for neighbor in neighbor_iter:
                if neighbor == start:
                    # 找到有效环(长度≥3,排除两节点来回的无效环)
                    if len(path) >= 3:
                        yield [id_to_node[idx] for idx in path]
                elif not visited[neighbor] and neighbor >= start:
                    # 仅访问ID≥起点的节点,避免重复枚举环
                    visited[neighbor] = True
                    path.append(neighbor)
                    stack.append((neighbor, iter(adj[neighbor]), False))
                    break  # 暂停当前迭代,优先处理新节点的邻居

1.4 内存优化:生成器输出

当环数量极大时,使用生成器边生成边处理,避免一次性加载所有环到内存:

# 遍历所有环并处理(示例:打印环)
for cycle in yield_all_cycles(adj, node_to_id, id_to_node):
    print(f"找到环: {' → '.join(cycle)} → {cycle[0]}")
    # 可添加存储到文件、统计分析等逻辑

2. 进阶优化:并行处理

由于每个起点的环查找逻辑独立,可利用多进程并行加速:

from multiprocessing import Pool

def process_single_start(start):
    cycles = []
    path = [start]
    visited = [False] * num_nodes
    visited[start] = True
    stack = [(start, iter(adj[start]), False)]
    
    while stack:
        current, neighbor_iter, is_backtracked = stack.pop()
        if is_backtracked:
            path.pop()
            visited[current] = False
            continue
        stack.append((current, neighbor_iter, True))
        for neighbor in neighbor_iter:
            if neighbor == start:
                if len(path) >= 3:
                    cycles.append([id_to_node[idx] for idx in path])
            elif not visited[neighbor] and neighbor >= start:
                visited[neighbor] = True
                path.append(neighbor)
                stack.append((neighbor, iter(adj[neighbor]), False))
                break
    return cycles

if __name__ == "__main__":
    num_nodes = len(node_to_id)
    # 启动多进程池,按CPU核心数分配任务
    with Pool() as pool:
        results = pool.map(process_single_start, range(num_nodes))
    
    # 合并所有进程的结果
    all_cycles = []
    for res in results:
        all_cycles.extend(res)

3. 注意事项

  • 如果仅需要特定长度的环(如长度为3的三角环),可在代码中添加长度判断(如if len(path) == 2 and neighbor == start),大幅减少计算量。
  • 若内存仍然紧张,可将环直接写入文件而非存储在内存中。

内容的提问来源于stack exchange,提问作者nothingisme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:43:24