2. 故障树最小割集求解

任务说明

给定一棵已构建的故障树(含顶事件、中间事件与底事件),使用下行法求解全部最小割集。输入采用结构化文本描述故障树逻辑关系,输出为按事件编号排序的最小割集列表。

故障树输入定义

故障树以如下格式描述:每行表示一个逻辑门,GATE 门编号 输出事件 门类型 输入事件列表。门类型仅支持AND与OR。底事件为叶子节点,不出现在任何门的输出位置。顶事件为第一个门(G1)的输出事件。

GATE G1 TOP AND M1 M2
GATE G2 M1 OR X1 X2
GATE G3 M2 OR X2 X3

对应故障树:TOP = M1 ∩ M2,M1 = X1 ∪ X2,M2 = X2 ∪ X3。底事件:X1, X2, X3。

下行法求解算法

下行法从顶事件开始,逐层向下:遇到OR门将输入事件纵向展开(各自成行),遇到AND门将输入事件横向扩展(同行追加)。处理完所有门后,每行是一个割集;删除包含其他割集的超集,剩余即为最小割集。

Python实现

def solve_minimal_cuts(gate_lines):
    gates = {}
    for line in gate_lines:
        parts = line.strip().split()
        if not parts or parts[0] != "GATE":
            continue
        _, gid, out, gtype, *inputs = parts
        gates[gid] = {"out": out, "type": gtype, "inputs": inputs}

    # 收集底事件(不出现在任何门输出中)
    outputs = {g["out"] for g in gates.values()}
    all_events = set()
    for g in gates.values():
        all_events.update(g["inputs"])
    basic_events = all_events - outputs

    # 下行法:从顶事件开始逐层替换
    top_gate = "G1"
    rows = [[top_gate]]
    processed = set()

    while True:
        expanded = False
        new_rows = []
        for row in rows:
            new_row = []
            for item in row:
                # 若item是门且未处理,则展开
                if item in gates and item not in processed:
                    gate = gates[item]
                    if gate["type"] == "OR":
                        # OR门:纵向展开,每行保留其他项+单个输入
                        for inp in gate["inputs"]:
                            new_rows.append([x for x in row if x != item] + [inp])
                    else:  # AND
                        # AND门:横向扩展,替换为所有输入
                        new_row.extend(gate["inputs"])
                    expanded = True
                else:
                    new_row.append(item)
            if not any(item in gates and item not in processed for item in row):
                new_rows.append(new_row)
        if not expanded:
            break
        rows = new_rows
        processed.add(top_gate)  # 简化处理:逐层标记已展开的门
        # 重新检查哪些门已展开
        processed = set()
        for row in rows:
            for item in row:
                if item in gates:
                    processed.add(item)
        # 若所有行都不含门,则结束
        if all(all(item not in gates for item in row) for row in rows):
            break

    # 去重与删除超集
    cut_sets = set()
    for row in rows:
        cut_sets.add(tuple(sorted(row)))

    # 删除超集
    minimal = []
    for cs in sorted(cut_sets, key=lambda x: (len(x), x)):
        if not any(set(cs) > set(other) for other in cut_sets if other != cs):
            minimal.append(list(cs))
    return minimal, sorted(basic_events)

# 输入数据
gate_lines = [
    "GATE G1 TOP AND M1 M2",
    "GATE G2 M1 OR X1 X2",
    "GATE G3 M2 OR X2 X3"
]

cuts, basics = solve_minimal_cuts(gate_lines)
print("底事件:", basics)
print("最小割集:")
for c in cuts:
    print("{" + ", ".join(c) + "}")

预期输出

底事件: ['X1', 'X2', 'X3']
最小割集:
{X1, X2}
{X2, X3}

推导:TOP = (X1∪X2) ∩ (X2∪X3) = (X1∩X2) ∪ (X1∩X3) ∪ (X2∩X2) ∪ (X2∩X3)。其中X2∩X2 = X2,但X2是{X1,X2}和{X2,X3}的子集,因此{X2}不是割集(因单事件无法同时满足两个AND分支)。实际最小割集为{X1,X2}与{X2,X3}。

验证方法

运行上述代码,观察输出是否与预期一致。判定标准:程序输出的最小割集与手工推导结果完全相同,且每个割集均为原故障树的割集(即割集中所有底事件同时发生时顶事件必然发生),且不存在任一割集是另一个割集的子集。

TOP AND M1 M2 OR OR X1 X2 X3 最小割集:{X1, X2} 与 {X2, X3}

运行与验证

将代码保存为minimal_cuts.py,在Python 3环境中执行python minimal_cuts.py。程序输出底事件列表与全部最小割集。验证判定标准:输出结果与上述预期输出完全一致,且每个最小割集均满足割集定义(割集中所有底事件发生时顶事件必然发生),同时不存在任何割集是另一个割集的子集。