训练一下条件独立的直觉

为什么本站支持上传 zip, exe, apk, mp4, msi, deb, rpm 却不支持上传 html

Sorry, the file you are trying to upload is not authorized (authorized extensions: jpg, jpeg, png, gif, heic, heif, webp, avif, pdf, docx, xlsx, pptx, doc, xls, ppt, py, cs, cpp, c, rs, h, m, 7z, gz, xz, zstd, rar, zip, txt, zst, apk, mp4, mkv, webm, mp3, flac, wav, weba, psd, csv, xcf, db, exe, msi, appimage, deb, rpm).

复制以下内容,存储为 html 格式文件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>d-分离 (d-Separation) 练习</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            background-color: #f4f7f9;
            color: #333;
            line-height: 1.6;
            display: flex;
            justify-content: center;
            padding: 20px;
        }
        .container {
            max-width: 800px;
            width: 100%;
            background-color: #fff;
            padding: 20px 30px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
        }
        h1, h2 {
            text-align: center;
            color: #0056b3;
        }
        .introduction {
            background-color: #e7f3ff;
            border-left: 5px solid #0056b3;
            padding: 15px;
            margin: 20px 0;
            border-radius: 5px;
        }
        #quiz-area {
            margin-top: 20px;
        }
        #graph-container {
            text-align: center;
            margin-bottom: 20px;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 8px;
            background-color: #fafafa;
            min-height: 250px;
        }
        #graph-svg {
            max-width: 100%;
            height: auto;
        }
        #question {
            font-size: 1.3em;
            font-weight: bold;
            text-align: center;
            margin-bottom: 25px;
        }
        .answer-buttons {
            display: flex;
            justify-content: center;
            gap: 20px;
            margin-bottom: 20px;
        }
        .btn {
            padding: 12px 25px;
            font-size: 1em;
            cursor: pointer;
            border: none;
            border-radius: 5px;
            color: white;
            transition: background-color 0.3s, transform 0.1s;
        }
        .btn:hover {
            opacity: 0.9;
            transform: translateY(-2px);
        }
        .btn:disabled {
            cursor: not-allowed;
            opacity: 0.6;
        }
        #yes-btn { background-color: #28a745; }
        #no-btn { background-color: #dc3545; }
        #next-btn {
            display: block;
            margin: 20px auto 0;
            background-color: #007bff;
            display: none;
        }
        #feedback {
            padding: 15px;
            border-radius: 5px;
            margin-top: 20px;
            display: none;
        }
        #feedback.correct {
            background-color: #d4edda;
            border: 1px solid #c3e6cb;
            color: #155724;
        }
        #feedback.incorrect {
            background-color: #f8d7da;
            border: 1px solid #f5c6cb;
            color: #721c24;
        }
        #progress {
            text-align: center;
            font-size: 0.9em;
            color: #666;
            margin-top: 10px;
        }
        .explanation {
            margin-top: 10px;
            font-size: 0.95em;
            text-align: left;
        }
        .explanation ul {
            padding-left: 20px;
        }
        .explanation strong {
            color: #0056b3;
        }
        #final-score {
            text-align: center;
            font-size: 1.5em;
            padding: 40px 20px;
        }
    </style>
</head>
<body>

<div class="container">
    <h1>d-分离 (d-Separation) 练习</h1>
    <div class="introduction">
        <p><strong>欢迎来到 d-分离练习!</strong></p>
        <p>d-分离是判断贝叶斯网络中变量是否条件独立的重要工具。请根据下图和问题,判断两个节点集是否在给定条件下 d-分离(条件独立)。</p>
        <ul>
            <li><strong>d-分离 (d-separated)</strong>: 两个节点之间所有的路径都被**阻断 (blocked)**。</li>
            <li><strong>d-连接 (d-connected)</strong>: 两个节点之间至少存在一条**未被阻断 (unblocked)** 的路径。</li>
        </ul>
    </div>

    <div id="quiz-area">
        <div id="progress"></div>
        <div id="graph-container">
            <svg id="graph-svg" width="600" height="250" viewBox="0 0 600 250"></svg>
        </div>
        <p id="question"></p>
        <div class="answer-buttons">
            <button class="btn" id="yes-btn">是 (d-分离)</button>
            <button class="btn" id="no-btn">否 (d-连接)</button>
        </div>
        <div id="feedback"></div>
        <button class="btn" id="next-btn">下一题</button>
    </div>
    <div id="final-score" style="display: none;"></div>
</div>

<script>
const questions = [
    // --- 基本规则 ---
    { // 1. 链式结构:被观察
        graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:125}, {id:'C', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'B', to:'C'}] },
        query: { X: 'A', Y: 'C', Z: ['B'] },
        answer: 'yes',
        explanation: `路径 A → B → C 是一条链式结构。因为中间节点 B 在条件集 Z 中(被观察),所以该路径被阻断。这是图中唯一的路径,因此 A 和 C 在给定 B 的条件下是 d-分离的。`
    },
    { // 2. 链式结构:未被观察
        graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:125}, {id:'C', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'B', to:'C'}] },
        query: { X: 'A', Y: 'C', Z: [] },
        answer: 'no',
        explanation: `路径 A → B → C 是一条链式结构。因为中间节点 B 不在条件集 Z 中(未被观察),所以该路径是连通的。因此 A 和 C 在给定空集的条件下是 d-连接的。`
    },
    { // 3. 分叉结构:被观察
        graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:50}, {id:'C', x:500, y:125}], edges: [{from:'B', to:'A'}, {from:'B', to:'C'}] },
        query: { X: 'A', Y: 'C', Z: ['B'] },
        answer: 'yes',
        explanation: `路径 A ← B → C 是一条分叉结构。因为共同原因 B 在条件集 Z 中(被观察),所以该路径被阻断。这是唯一的路径,因此 A 和 C 在给定 B 的条件下是 d-分离的。`
    },
    { // 4. 分叉结构:未被观察
        graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:50}, {id:'C', x:500, y:125}], edges: [{from:'B', to:'A'}, {from:'B', to:'C'}] },
        query: { X: 'A', Y: 'C', Z: [] },
        answer: 'no',
        explanation: `路径 A ← B → C 是一条分叉结构。因为共同原因 B 不在条件集 Z 中(未被观察),所以该路径是连通的。因此 A 和 C 在给定空集的条件下是 d-连接的。`
    },
    { // 5. 对撞结构:未被观察
        graph: { nodes: [{id:'A', x:100, y:50}, {id:'B', x:300, y:125}, {id:'C', x:500, y:50}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}] },
        query: { X: 'A', Y: 'C', Z: [] },
        answer: 'yes',
        explanation: `路径 A → B ← C 是一条对撞结构。因为对撞节点 B 及其后代都不在条件集 Z 中,所以该路径默认被阻断。这是唯一的路径,因此 A 和 C 在给定空集的条件下是 d-分离的。`
    },
    { // 6. 对撞结构:被观察
        graph: { nodes: [{id:'A', x:100, y:50}, {id:'B', x:300, y:125}, {id:'C', x:500, y:50}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}] },
        query: { X: 'A', Y: 'C', Z: ['B'] },
        answer: 'no',
        explanation: `路径 A → B ← C 是一条对撞结构。因为对撞节点 B 在条件集 Z 中(被观察),所以该路径被打开(连通)。这被称为“解释消除效应”。因此 A 和 C 在给定 B 的条件下是 d-连接的。`
    },
    { // 7. 对撞结构:后代被观察
        graph: { nodes: [{id:'A', x:100, y:50}, {id:'B', x:300, y:50}, {id:'C', x:500, y:50}, {id:'D', x:300, y:200}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}, {from:'B', to:'D'}] },
        query: { X: 'A', Y: 'C', Z: ['D'] },
        answer: 'no',
        explanation: `路径 A → B ← C 是一条对撞结构。虽然对撞节点 B 不在条件集 Z 中,但它的后代 D 在 Z 中。观察对撞节点的后代同样会打开这条路径。因此 A 和 C 在给定 D 的条件下是 d-连接的。`
    },
    // --- 组合情况 ---
    { // 8. 链 + 对撞
        graph: { nodes: [{id:'A', x:50, y:125}, {id:'B', x:200, y:125}, {id:'C', x:350, y:50}, {id:'D', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}, {from:'C', to:'D'}] },
        query: { X: 'A', Y: 'D', Z: ['C'] },
        answer: 'yes',
        explanation: `存在唯一路径 A → B ← C → D。我们逐段分析:
        <ul>
            <li>在 B 节点:A → B ← C 是一个对撞结构。由于对撞节点 B 未被观察,这条路径在 B 处被阻断。</li>
            <li>在 C 节点:B ← C → D 是一个分叉结构。由于分叉点 C 被观察,这条路径在 C 处也被阻断。</li>
        </ul>
        因为这条路径上至少有一个点(实际上是两个点)阻断了信息流,所以 A 和 D 在给定 C 的条件下是 d-分离的。`
    },
    { // 9. 同上,但观察 B
        graph: { nodes: [{id:'A', x:50, y:125}, {id:'B', x:200, y:125}, {id:'C', x:350, y:50}, {id:'D', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}, {from:'C', to:'D'}] },
        query: { X: 'A', Y: 'D', Z: ['B'] },
        answer: 'no',
        explanation: `存在唯一路径 A → B ← C → D。我们分析这条路径:
        <ul>
            <li>在 B 节点:A → B ← C 是一个对撞结构。由于对撞节点 B 被观察,这条路径在 B 处被打开(连通)。</li>
            <li>在 C 节点:B ← C → D 是一个分叉结构。分叉点 C 未被观察,所以路径在 C 处也是连通的。</li>
        </ul>
        因为整条路径上没有被阻断的点,所以 A 和 D 在给定 B 的条件下是 d-连接的。`
    },
    { // 10. M 型图
        graph: { nodes: [{id:'A', x:150, y:50}, {id:'B', x:450, y:50}, {id:'C', x:150, y:200}, {id:'D', x:450, y:200}], edges: [{from:'A', to:'C'}, {from:'A', to:'D'}, {from:'B', to:'C'}, {from:'B', to:'D'}] },
        query: { X: 'A', Y: 'B', Z: ['C'] },
        answer: 'no',
        explanation: `这里有两条路径连接 A 和 B:
        <ul>
            <li>路径 1: A → D ← B。这是一个对撞结构。对撞节点 D 未被观察,所以此路径被<strong>阻断</strong>。</li>
            <li>路径 2: A → C ← B。这是一个对撞结构。对撞节点 C 在条件集 Z 中被观察,所以此路径被<strong>打开(连通)</strong>。</li>
        </ul>
        因为存在至少一条连通的路径(路径 2),所以 A 和 B 在给定 C 的条件下是 d-连接的。`
    },
    { // 11. M 型图,观察两个
        graph: { nodes: [{id:'A', x:150, y:50}, {id:'B', x:450, y:50}, {id:'C', x:150, y:200}, {id:'D', x:450, y:200}], edges: [{from:'A', to:'C'}, {from:'A', to:'D'}, {from:'B', to:'C'}, {from:'B', to:'D'}] },
        query: { X: 'A', Y: 'B', Z: ['C', 'D'] },
        answer: 'no',
        explanation: `这里有两条路径连接 A 和 B:
        <ul>
            <li>路径 1: A → D ← B。对撞结构。对撞节点 D 被观察,路径<strong>打开(连通)</strong>。</li>
            <li>路径 2: A → C ← B。对撞结构。对撞节点 C 被观察,路径<strong>打开(连通)</strong>。</li>
        </ul>
        因为两条路径都由于观察了对撞节点而打开,所以 A 和 B 在给定 C 和 D 的条件下是 d-连接的。`
    },
    { // 12. 复杂图 1
        graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:200,y:50},{id:'C',x:200,y:200},{id:'D',x:350,y:125},{id:'E',x:500,y:125}], edges: [{from:'A',to:'B'},{from:'A',to:'C'},{from:'B',to:'D'},{from:'C',to:'D'},{from:'D',to:'E'}] },
        query: { X: 'A', Y: 'E', Z: ['D'] },
        answer: 'yes',
        explanation: `要判断 A 和 E 是否 d-分离,我们需要检查所有从 A 到 E 的路径。所有路径都必须经过 D。
        <ul>
            <li>路径 1: A → B → D → E。这是一条链式结构。中间节点 D 被观察,所以路径在 D 处被<strong>阻断</strong>。</li>
            <li>路径 2: A → C → D → E。这也是一条链式结构。中间节点 D 被观察,所以路径在 D 处被<strong>阻断</strong>。</li>
        </ul>
        因为所有从 A 到 E 的路径都被 D 阻断,所以 A 和 E 在给定 D 的条件下是 d-分离的。`
    },
    { // 13. 复杂图 1, 观察 A
        graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:200,y:50},{id:'C',x:200,y:200},{id:'D',x:350,y:125},{id:'E',x:500,y:125}], edges: [{from:'A',to:'B'},{from:'A',to:'C'},{from:'B',to:'D'},{from:'C',to:'D'},{from:'D',to:'E'}] },
        query: { X: 'B', Y: 'C', Z: ['A'] },
        answer: 'yes',
        explanation: `这里有两条路径连接 B 和 C:
        <ul>
            <li>路径 1: B ← A → C。这是一个分叉结构。分叉点 A 在条件集 Z 中被观察,所以此路径被<strong>阻断</strong>。</li>
            <li>路径 2: B → D ← C。这是一个对撞结构。对撞节点 D 及其后代 E 都未被观察,所以此路径被<strong>阻断</strong>。</li>
        </ul>
        因为所有路径都被阻断,所以 B 和 C 在给定 A 的条件下是 d-分离的。`
    },
    { // 14. 复杂图 1, 观察 D
        graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:200,y:50},{id:'C',x:200,y:200},{id:'D',x:350,y:125},{id:'E',x:500,y:125}], edges: [{from:'A',to:'B'},{from:'A',to:'C'},{from:'B',to:'D'},{from:'C',to:'D'},{from:'D',to:'E'}] },
        query: { X: 'B', Y: 'C', Z: ['D'] },
        answer: 'no',
        explanation: `这里有两条路径连接 B 和 C:
        <ul>
            <li>路径 1: B ← A → C。这是一个分叉结构。分叉点 A 未被观察,所以此路径是<strong>连通的</strong>。</li>
            <li>路径 2: B → D ← C。这是一个对撞结构。对撞节点 D 被观察,所以此路径也被<strong>打开(连通)</strong>。</li>
        </ul>
        因为存在连通路径(实际上两条都连通),所以 B 和 C 在给定 D 的条件下是 d-连接的。`
    },
    { // 15. 经典诱导依赖图
        graph: { nodes: [{id:'Rain',x:150,y:50},{id:'Sprinkler',x:450,y:50},{id:'Grass Wet',x:300,y:150}], edges: [{from:'Rain',to:'Grass Wet'},{from:'Sprinkler',to:'Grass Wet'}] },
        query: { X: 'Rain', Y: 'Sprinkler', Z: [] },
        answer: 'yes',
        explanation: `路径 Rain → Grass Wet ← Sprinkler 是一个对撞结构。因为对撞节点 Grass Wet 及其后代(无)都未被观察,所以路径被阻断。因此,在不知道草地是否湿润的情况下,下雨和洒水器是独立的(d-分离)。`
    },
    { // 16. 经典诱导依赖图,观察结果
        graph: { nodes: [{id:'Rain',x:150,y:50},{id:'Sprinkler',x:450,y:50},{id:'Grass Wet',x:300,y:150}], edges: [{from:'Rain',to:'Grass Wet'},{from:'Sprinkler',to:'Grass Wet'}] },
        query: { X: 'Rain', Y: 'Sprinkler', Z: ['Grass Wet'] },
        answer: 'no',
        explanation: `路径 Rain → Grass Wet ← Sprinkler 是一个对撞结构。因为对撞节点 Grass Wet 被观察了,所以路径被打开(连通)。这意味着,如果我们观察到草地是湿的,那么下雨和洒水器就变得相关了(例如,如果没下雨,那很可能是洒水器开了)。因此它们是 d-连接的。`
    },
    { // 17. 更长的链
        graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:180,y:125},{id:'C',x:310,y:125},{id:'D',x:440,y:125},{id:'E',x:570,y:125}], edges: [{from:'A',to:'B'},{from:'B',to:'C'},{from:'C',to:'D'},{from:'D',to:'E'}] },
        query: { X: 'A', Y: 'E', Z: ['C'] },
        answer: 'yes',
        explanation: `唯一的路径是 A → B → C → D → E。这是一个链式结构。因为中间节点 C 在条件集 Z 中被观察,所以信息流在 C 点被阻断。因此,A 和 E 在给定 C 的条件下是 d-分离的。`
    },
    { // 18. 对撞与分叉混合
        graph: { nodes: [{id:'A',x:50, y:125}, {id:'B',x:200, y:125}, {id:'C',x:350, y:50}, {id:'D',x:350, y:200}, {id:'E', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'B',to:'C'}, {from:'B',to:'D'}, {from:'C',to:'E'}, {from:'D',to:'E'}] },
        query: { X: 'A', Y: 'E', Z: [] },
        answer: 'no',
        explanation: `我们检查从 A 到 E 的所有路径,给定 Z=∅。
        <ul>
            <li>路径 1: A → B → C → E。路径上的 B 和 C 都是链式节点,且都未被观察。路径<strong>连通</strong>。</li>
            <li>路径 2: A → B → D → E。路径上的 B 和 D 都是链式节点,且都未被观察。路径<strong>连通</strong>。</li>
        </ul>
        因为存在连通路径,所以 A 和 E 在给定空集的条件下是 d-连接的。注意,C→E←D 中的 E 是对撞节点,但它不是 A 到 E 路径上的中间节点,不影响判断。`
    },
    { // 19. 钻石图
        graph: { nodes: [{id:'A',x:100,y:125},{id:'B',x:250,y:50},{id:'C',x:400,y:125},{id:'D',x:250,y:200}], edges: [{from:'A',to:'B'},{from:'B',to:'C'},{from:'A',to:'D'},{from:'D',to:'C'}] },
        query: { X: 'B', Y: 'D', Z: ['A', 'C'] },
        answer: 'no',
        explanation: `这里有两条路径连接 B 和 D:
        <ul>
            <li>路径 1: B ← A → D。这是一个分叉结构。分叉点 A 在 Z 中被观察,所以此路径被<strong>阻断</strong>。</li>
            <li>路径 2: B → C ← D。这是一个对撞结构。对撞点 C 在 Z 中被观察,所以此路径被<strong>打开(连通)</strong>。</li>
        </ul>
        因为存在一条连通的路径(路径 2),所以 B 和 D 在给定 A 和 C 的条件下是 d-连接的。`
    },
    { // 20. 钻石图 (最终版)
        graph: { nodes: [{id:'A',x:100,y:125},{id:'B',x:250,y:50},{id:'C',x:400,y:125},{id:'D',x:250,y:200}], edges: [{from:'A',to:'B'},{from:'B',to:'C'},{from:'A',to:'D'},{from:'D',to:'C'}] },
        query: { X: 'B', Y: 'D', Z: ['A'] },
        answer: 'yes',
        explanation: `这里有两条路径连接 B 和 D:
        <ul>
            <li>路径 1: B ← A → D。这是一个分叉结构。因为分叉点 A 在条件集 Z 中(被观察),所以此路径被 <strong>阻断</strong>。</li>
            <li>路径 2: B → C ← D。这是一个对撞结构。因为对撞点 C 及其后代(无)都不在条件集 Z 中,所以此路径被 <strong>阻断</strong>。</li>
        </ul>
        因为所有从 B 到 D 的路径都被阻断了,所以 B 和 D 在给定 A 的条件下是 d-分离的。`
    }
];

const quizArea = document.getElementById('quiz-area');
const finalScoreDiv = document.getElementById('final-score');
const progressEl = document.getElementById('progress');
const questionEl = document.getElementById('question');
const svgEl = document.getElementById('graph-svg');
const feedbackEl = document.getElementById('feedback');
const yesBtn = document.getElementById('yes-btn');
const noBtn = document.getElementById('no-btn');
const nextBtn = document.getElementById('next-btn');

let currentQuestionIndex = 0;
let score = 0;
const NODE_RADIUS = 25; // Define node radius as a constant

function loadQuestion() {
    if (currentQuestionIndex >= questions.length) {
        showFinalScore();
        return;
    }

    const currentQuestion = questions[currentQuestionIndex];
    const { graph, query } = currentQuestion;

    feedbackEl.style.display = 'none';
    feedbackEl.className = '';
    nextBtn.style.display = 'none';
    yesBtn.disabled = false;
    noBtn.disabled = false;

    progressEl.textContent = `问题 ${currentQuestionIndex + 1} / ${questions.length}`;

    let z_str = query.Z.length > 0 ? `{${query.Z.join(', ')}}` : '∅ (空集)';
    questionEl.innerHTML = `在下图中,给定条件集 Z = ${z_str},请问节点 <strong>${query.X}</strong> 和 <strong>${query.Y}</strong> 是否 d-分离?`;

    drawGraph(graph, query);
}

function drawGraph(graph, query) {
    svgEl.innerHTML = '';

    const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
    const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
    marker.setAttribute('id', 'arrow');
    marker.setAttribute('viewBox', '0 -5 10 10');
    marker.setAttribute('refX', '10'); // Point of the arrow
    marker.setAttribute('refY', '0');
    marker.setAttribute('markerWidth', '6');
    marker.setAttribute('markerHeight', '6');
    marker.setAttribute('orient', 'auto');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M0,-5L10,0L0,5');
    path.setAttribute('fill', '#666');
    marker.appendChild(path);
    defs.appendChild(marker);
    svgEl.appendChild(defs);

    // Draw edges first, so nodes are rendered on top
    graph.edges.forEach(edge => {
        const fromNode = graph.nodes.find(n => n.id === edge.from);
        const toNode = graph.nodes.find(n => n.id === edge.to);

        // *** FIX: Calculate line end point on the circle's edge, not center ***
        const dx = toNode.x - fromNode.x;
        const dy = toNode.y - fromNode.y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        
        // Avoid division by zero
        if (dist === 0) return; 

        // Calculate the new endpoint (shortened by the radius)
        const endX = toNode.x - (dx / dist) * NODE_RADIUS;
        const endY = toNode.y - (dy / dist) * NODE_RADIUS;

        const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        line.setAttribute('x1', fromNode.x);
        line.setAttribute('y1', fromNode.y);
        line.setAttribute('x2', endX); // Use calculated end point
        line.setAttribute('y2', endY); // Use calculated end point
        line.setAttribute('stroke', '#666');
        line.setAttribute('stroke-width', 2);
        line.setAttribute('marker-end', 'url(#arrow)');
        svgEl.appendChild(line);
    });

    // Draw nodes and labels
    graph.nodes.forEach(node => {
        const isQueryNode = node.id === query.X || node.id === query.Y;
        const isGivenNode = query.Z.includes(node.id);

        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('cx', node.x);
        circle.setAttribute('cy', node.y);
        circle.setAttribute('r', NODE_RADIUS);
        circle.setAttribute('stroke', isQueryNode ? '#0056b3' : '#333');
        circle.setAttribute('stroke-width', 3);
        circle.setAttribute('fill', isGivenNode ? '#ffc107' : '#e7f3ff');

        const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        text.setAttribute('x', node.x);
        text.setAttribute('y', node.y + 7);
        text.setAttribute('text-anchor', 'middle');
        text.setAttribute('font-size', '18px');
        text.setAttribute('font-weight', 'bold');
        text.textContent = node.id;

        svgEl.appendChild(circle);
        svgEl.appendChild(text);
    });
}

function handleAnswer(userAnswer) {
    const currentQuestion = questions[currentQuestionIndex];
    const isCorrect = userAnswer === currentQuestion.answer;

    if (isCorrect) {
        score++;
        feedbackEl.className = 'correct';
        feedbackEl.innerHTML = `<strong>正确!</strong> <div class="explanation">${currentQuestion.explanation}</div>`;
    } else {
        feedbackEl.className = 'incorrect';
        feedbackEl.innerHTML = `<strong>错误。</strong> 正确答案是 "${currentQuestion.answer === 'yes' ? '是 (d-分离)' : '否 (d-连接)'}"。<div class="explanation">${currentQuestion.explanation}</div>`;
    }

    feedbackEl.style.display = 'block';
    yesBtn.disabled = true;
    noBtn.disabled = true;
    nextBtn.style.display = 'block';
}

function showFinalScore() {
    quizArea.style.display = 'none';
    finalScoreDiv.style.display = 'block';
    finalScoreDiv.innerHTML = `<h2>练习完成!</h2>
                             <p>你的最终得分是:${score} / ${questions.length}</p>
                             <p>${score > questions.length * 0.8 ? '太棒了!你对 d-分离的理解非常深入。' : (score > questions.length * 0.5 ? '不错!再多练习几次就能完全掌握了。' : '继续努力!建议回顾一下 d-分离的三条规则。')}</p>
                             <button class="btn" onclick="location.reload()" style="background-color:#007bff">再做一次</button>`;
}

yesBtn.addEventListener('click', () => handleAnswer('yes'));
noBtn.addEventListener('click', () => handleAnswer('no'));
nextBtn.addEventListener('click', () => {
    currentQuestionIndex++;
    loadQuestion();
});

document.addEventListener('DOMContentLoaded', loadQuestion);

</script>
</body>
</html>

网页设计和出题人都是 Gemini,人工 check 了一遍,题目都是没问题的。

附上三种基本结构的解释和证明

interesting

帮你挂在了 d-分离 (d-Separation) 练习

好快