<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>J的小站</title>
    <link>https://level0-jj.github.io/</link>
    
    <atom:link href="https://level0-jj.github.io/rss2.xml" rel="self" type="application/rss+xml"/>
    
    <description>是个懒惰的大一小灯，努力学习ing o(*￣▽￣*)ブ</description>
    <pubDate>Mon, 07 Sep 2026 13:00:32 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>做leetcode160.相交链表有感</title>
      <link>https://level0-jj.github.io/2026/09/07/%E5%81%9Aleetcode160-%E7%9B%B8%E4%BA%A4%E9%93%BE%E8%A1%A8%E6%9C%89%E6%84%9F/</link>
      <guid>https://level0-jj.github.io/2026/09/07/%E5%81%9Aleetcode160-%E7%9B%B8%E4%BA%A4%E9%93%BE%E8%A1%A8%E6%9C%89%E6%84%9F/</guid>
      <pubDate>Mon, 07 Sep 2026 12:35:16 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;缘由&quot;&gt;&lt;a href=&quot;#缘由&quot; class=&quot;headerlink&quot; title=&quot;缘由&quot;&gt;&lt;/a&gt;缘由&lt;/h2&gt;&lt;p&gt;鉴于本人长期使用ai写代码，手搓代码能力堪比成年草履虫，遂听从好友建议刷&lt;a href=&quot;(https://leetcode.cn/)&quot;&gt;l</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="缘由"><a href="#缘由" class="headerlink" title="缘由"></a>缘由</h2><p>鉴于本人长期使用ai写代码，手搓代码能力堪比成年草履虫，遂听从好友建议刷<a href="(https://leetcode.cn/)">leetcode</a>,然<strong>leetcode太难了QAQ</strong>，刷起来十分缓慢。今日写完jb物理实验报告打开<a href="https://leetcode.cn/studyplan/top-100-liked/">leetcode 热题 100</a>刷一下链表，发现了一道简单的链表题，即<a href="https://leetcode.cn/problems/intersection-of-two-linked-lists/description/?envType=study-plan-v2&envId=top-100-liked">160.相交链表</a>，然后便十分兴奋地打开开始做</p><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>给你两个单链表的头节点 <code>headA</code> 和 <code>headB</code> ，请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点，返回 <code>null</code> 。</p><p>题目数据<strong>保证</strong>整个链式结构中不存在环。</p><p>注意，函数返回结果后，链表必须<strong>保持其原始结构</strong>。</p><p>自定义评测：</p><p>评测系统 的输入如下（你设计的程序<strong>不适用</strong>此输入）：</p><p><code>intersectVa</code>· - 相交的起始节点的值。如果不存在相交节点，这一值为 <code>0</code></p><p><code>listA</code> - 第一个链表<code>listB</code> - 第二个链表<code>skipA</code> - 在 <code>listA</code> 中（从头节点开始）跳到交叉节点的节点数<code>skipB</code> - 在 <code>listB</code> 中（从头节点开始）跳到交叉节点的节点数评测系统将根据这些输入创建链式数据结构，并将两个头节点 <code>headA</code> 和 <code>headB</code> 传递给你的程序。如果程序能够正确返回相交节点，那么你的解决方案将被<strong>视作正确答案</strong></p><h2 id="我的想法"><a href="#我的想法" class="headerlink" title="我的想法"></a>我的想法</h2><p>在经过一番苦思冥想之后，我也是写出了和官方一样的答案</p><p>就是遍历一遍链表<code>headA</code>，并将其存入哈希表中，然后遍历链表<code>headB</code>,对于遍历的每一个节点，判断该节点是否在哈希表内。</p><p>代码如下</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">ListNode</span> <span class="token punctuation">&#123;</span>      <span class="token keyword">int</span> val<span class="token punctuation">;</span>      ListNode <span class="token operator">*</span>next<span class="token punctuation">;</span>      <span class="token function">ListNode</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">val</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span> <span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    ListNode <span class="token operator">*</span><span class="token function">getIntersectionNode</span><span class="token punctuation">(</span>ListNode <span class="token operator">*</span>headA<span class="token punctuation">,</span> ListNode <span class="token operator">*</span>headB<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        unordered_set<span class="token operator">&lt;</span>ListNode <span class="token operator">*</span><span class="token operator">></span> visit<span class="token punctuation">;</span>        ListNode <span class="token operator">*</span>temp<span class="token operator">=</span>headA<span class="token punctuation">;</span>        <span class="token keyword">while</span><span class="token punctuation">(</span>temp<span class="token operator">!=</span><span class="token keyword">nullptr</span><span class="token punctuation">)</span>        <span class="token punctuation">&#123;</span>            visit<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>temp<span class="token punctuation">)</span><span class="token punctuation">;</span>            temp<span class="token operator">=</span>temp<span class="token operator">-></span>next<span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        temp<span class="token operator">=</span>headB<span class="token punctuation">;</span>        <span class="token keyword">while</span><span class="token punctuation">(</span>temp<span class="token operator">!=</span><span class="token keyword">nullptr</span><span class="token punctuation">)</span>        <span class="token punctuation">&#123;</span>            <span class="token keyword">if</span><span class="token punctuation">(</span>visit<span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span>temp<span class="token punctuation">)</span><span class="token punctuation">)</span>            <span class="token punctuation">&#123;</span>                <span class="token keyword">return</span> temp<span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>            temp<span class="token operator">=</span>temp<span class="token operator">-></span>next<span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        <span class="token keyword">return</span> <span class="token keyword">nullptr</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="双指针解法"><a href="#双指针解法" class="headerlink" title="双指针解法"></a>双指针解法</h2><p>习惯性看题解，突然发现一个高赞题解</p><p><a href="https://leetcode.cn/problems/intersection-of-two-linked-lists/solutions/12624/intersection-of-two-linked-lists-shuang-zhi-zhen-l/?envType=study-plan-v2&envId=top-100-liked">160.相交链表（双指针，清晰图解）</a></p><p><strong>&lt;(O0O)&gt;</strong></p><p>题解这样说：</p><p>设「第一个公共节点」为 <code>node</code> ，「链表 headA」的节点数量为 <code>a</code> ，「链表 headB」的节点数量为 <code>b</code> ，「两链表的公共尾部」的节点数量为 <code>c</code> ，则有：</p><p>头节点 <code>headA</code> 到 <code>node</code> 前，共有 <code>a−c</code> 个节点；头节点 <code>headB</code> 到 <code>node</code> 前，共有 <code>b−c</code> 个节点；</p><p>考虑构建两个节点指针 <code>A​</code> , <code>B</code> 分别指向两链表头节点 <code>headA</code> , <code>headB</code> ，做如下操作：</p><p>指针 <code>A</code> 先遍历完链表 <code>headA</code> ，再开始遍历链表 <code>headB</code> ，当走到 <code>node</code> 时，共走步数为：<code>a+(b−c)</code>指针 <code>B</code> 先遍历完链表 <code>headB</code> ，再开始遍历链表 <code>headA</code> ，当走到 <code>node</code> 时，共走步数为：<code>b+(a−c)</code>如下式所示，此时指针 <code>A</code> , <code>B</code> 重合，并有两种情况：</p><p><code>a+(b−c)=b+(a−c)</code>若两链表 有 公共尾部 (即 <code>c&gt;0</code> ) ：指针 <code>A</code> , <code>B</code> 同时指向「第一个公共节点」<code>node</code> 。若两链表 无 公共尾部 (即 <code>c=0</code> ) ：指针 <code>A</code> , <code>B</code> 同时指向 <code>null</code> 。因此返回 <code>A</code> 即可。</p><p>代码：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    ListNode <span class="token operator">*</span><span class="token function">getIntersectionNode</span><span class="token punctuation">(</span>ListNode <span class="token operator">*</span>headA<span class="token punctuation">,</span> ListNode <span class="token operator">*</span>headB<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        ListNode <span class="token operator">*</span>A <span class="token operator">=</span> headA<span class="token punctuation">,</span> <span class="token operator">*</span>B <span class="token operator">=</span> headB<span class="token punctuation">;</span>        <span class="token keyword">while</span> <span class="token punctuation">(</span>A <span class="token operator">!=</span> B<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            A <span class="token operator">=</span> A <span class="token operator">!=</span> <span class="token keyword">nullptr</span> <span class="token operator">?</span> A<span class="token operator">-></span>next <span class="token operator">:</span> headB<span class="token punctuation">;</span>            B <span class="token operator">=</span> B <span class="token operator">!=</span> <span class="token keyword">nullptr</span> <span class="token operator">?</span> B<span class="token operator">-></span>next <span class="token operator">:</span> headA<span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        <span class="token keyword">return</span> A<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>&lt;(O0O)&gt;</strong></p><p>不知道是学得太少还是脑子是摆设，总之我是傻逼。</p><p>欸欸，还有很大进步空间……</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/leetcode/">leetcode</category>
      
      <category domain="https://level0-jj.github.io/tags/cpp/">cpp</category>
      
      
      <comments>https://level0-jj.github.io/2026/09/07/%E5%81%9Aleetcode160-%E7%9B%B8%E4%BA%A4%E9%93%BE%E8%A1%A8%E6%9C%89%E6%84%9F/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>关于我的小学期计算机大作业</title>
      <link>https://level0-jj.github.io/2026/07/21/%E5%85%B3%E4%BA%8E%E6%88%91%E7%9A%84%E5%B0%8F%E5%AD%A6%E6%9C%9F%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%A4%A7%E4%BD%9C%E4%B8%9A/</link>
      <guid>https://level0-jj.github.io/2026/07/21/%E5%85%B3%E4%BA%8E%E6%88%91%E7%9A%84%E5%B0%8F%E5%AD%A6%E6%9C%9F%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%A4%A7%E4%BD%9C%E4%B8%9A/</guid>
      <pubDate>Tue, 21 Jul 2026 01:01:57 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;碎碎念念&quot;&gt;&lt;a href=&quot;#碎碎念念&quot; class=&quot;headerlink&quot; title=&quot;碎碎念念&quot;&gt;&lt;/a&gt;碎碎念念&lt;/h2&gt;&lt;p&gt;小学期的大作业（为什么暑假要上课），再也不想写这种令人心情不愉快的作业了口牙！&lt;/p&gt;
&lt;p&gt;本文结合小规模示例介绍以下内容</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="碎碎念念"><a href="#碎碎念念" class="headerlink" title="碎碎念念"></a>碎碎念念</h2><p>小学期的大作业（为什么暑假要上课），再也不想写这种令人心情不愉快的作业了口牙！</p><p>本文结合小规模示例介绍以下内容：</p><ul><li>最小二乘线性回归</li><li>决策树回归</li><li>决策树分类</li><li>K-Means 聚类与 K-Means++ 初始化</li></ul><p>图形界面使用 EasyX 完成。EasyX 在项目中主要负责绘制散点、曲线、聚类中心和处理按钮点击，本文不对其接口展开介绍。</p><h2 id="回归、分类和聚类有什么区别"><a href="#回归、分类和聚类有什么区别" class="headerlink" title="回归、分类和聚类有什么区别"></a>回归、分类和聚类有什么区别</h2><p>机器学习任务可以先从“有没有标签”和“要预测什么”两个角度理解。</p><table><thead><tr><th>任务</th><th>数据特点</th><th>输出结果</th><th>简单例子</th></tr></thead><tbody><tr><td>回归</td><td>有标签</td><td>连续数值</td><td>根据学习时间预测成绩</td></tr><tr><td>分类</td><td>有标签</td><td>离散类别</td><td>判断鸢尾花属于哪个品种</td></tr><tr><td>聚类</td><td>无标签</td><td>自动形成的分组</td><td>将位置相近的数据点分成若干组</td></tr></tbody></table><p>回归和分类都需要已知答案来训练，因此属于监督学习；聚类没有预先提供类别，因此属于无监督学习。</p><h2 id="最小二乘线性回归"><a href="#最小二乘线性回归" class="headerlink" title="最小二乘线性回归"></a>最小二乘线性回归</h2><p>线性回归希望用一条直线描述输入 $x$ 与目标值 $y$ 之间的关系：</p><p>$$y&#x3D;ax+b$$</p><p>其中，$a$ 是斜率，$b$ 是截距。最小二乘法会选择一组 $a$ 和 $b$，使所有样本的残差平方和尽可能小：</p><p>$$\sum_{i&#x3D;1}^{n}\left[y_i-(ax_i+b)\right]^2$$</p><p>平方可以避免正误差和负误差相互抵消，同时会让较大的误差受到更明显的惩罚。</p><p>斜率和截距可以直接计算：</p><p>$$a&#x3D;\frac{n\sum x_i y_i-\sum x_i\sum y_i}{n\sum x_i^2-(\sum x_i)^2}$$</p><p>$$b&#x3D;\frac{\sum y_i-a\sum x_i}{n}$$</p><h3 id="一个简单例子"><a href="#一个简单例子" class="headerlink" title="一个简单例子"></a>一个简单例子</h3><p>假设有三个样本：</p><table><thead><tr><th align="right">$x$</th><th align="right">$y$</th></tr></thead><tbody><tr><td align="right">1</td><td align="right">2</td></tr><tr><td align="right">2</td><td align="right">3</td></tr><tr><td align="right">3</td><td align="right">5</td></tr></tbody></table><p>可以得到：</p><p>$$\sum x_i&#x3D;6,\quad \sum y_i&#x3D;10,\quad\sum x_i y_i&#x3D;23,\quad \sum x_i^2&#x3D;14$$</p><p>代入公式：</p><p>$$a&#x3D;\frac{3\times23-6\times10}{3\times14-6^2}&#x3D;1.5$$</p><p>$$b&#x3D;\frac{10-1.5\times6}{3}&#x3D;\frac{1}{3}$$</p><p>最终拟合直线约为：</p><p>$$y&#x3D;1.5x+0.33$$</p><p>当输入 $x&#x3D;4$ 时，模型预测：</p><p>$$y&#x3D;1.5\times4+0.33&#x3D;6.33$$</p><h3 id="C-实现思路"><a href="#C-实现思路" class="headerlink" title="C++ 实现思路"></a>C++ 实现思路</h3><p>程序只需遍历一次样本，累计四个统计量，再代入公式即可：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">LeastSquares</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>vector<span class="token operator">&lt;</span>DataPoint<span class="token operator">></span><span class="token operator">&amp;</span> points<span class="token punctuation">,</span>                  <span class="token keyword">double</span><span class="token operator">&amp;</span> a<span class="token punctuation">,</span> <span class="token keyword">double</span><span class="token operator">&amp;</span> b<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>points<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        a <span class="token operator">=</span> b <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>        <span class="token keyword">return</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">double</span> sumX <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>    <span class="token keyword">double</span> sumY <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>    <span class="token keyword">double</span> sumXY <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>    <span class="token keyword">double</span> sumX2 <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">auto</span><span class="token operator">&amp;</span> point <span class="token operator">:</span> points<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        sumX <span class="token operator">+=</span> point<span class="token punctuation">.</span>x<span class="token punctuation">;</span>        sumY <span class="token operator">+=</span> point<span class="token punctuation">.</span>y<span class="token punctuation">;</span>        sumXY <span class="token operator">+=</span> point<span class="token punctuation">.</span>x <span class="token operator">*</span> point<span class="token punctuation">.</span>y<span class="token punctuation">;</span>        sumX2 <span class="token operator">+=</span> point<span class="token punctuation">.</span>x <span class="token operator">*</span> point<span class="token punctuation">.</span>x<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">const</span> <span class="token keyword">double</span> n <span class="token operator">=</span> <span class="token generic-function"><span class="token function">static_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token keyword">double</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>points<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">const</span> <span class="token keyword">double</span> denominator <span class="token operator">=</span> n <span class="token operator">*</span> sumX2 <span class="token operator">-</span> sumX <span class="token operator">*</span> sumX<span class="token punctuation">;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">abs</span><span class="token punctuation">(</span>denominator<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">1e-12</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        a <span class="token operator">=</span> b <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span>        <span class="token keyword">return</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    a <span class="token operator">=</span> <span class="token punctuation">(</span>n <span class="token operator">*</span> sumXY <span class="token operator">-</span> sumX <span class="token operator">*</span> sumY<span class="token punctuation">)</span> <span class="token operator">/</span> denominator<span class="token punctuation">;</span>    b <span class="token operator">=</span> <span class="token punctuation">(</span>sumY <span class="token operator">-</span> a <span class="token operator">*</span> sumX<span class="token punctuation">)</span> <span class="token operator">/</span> n<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>该算法的时间复杂度为 $O(n)$，额外空间复杂度为 $O(1)$。如果所有样本的 $x$ 都相同，公式分母为零，此时无法求出唯一斜率，需要单独处理。</p><h2 id="决策树回归"><a href="#决策树回归" class="headerlink" title="决策树回归"></a>决策树回归</h2><p>线性回归只能拟合一条直线。当数据关系不是线性的，可以使用决策树回归。</p><p>决策树回归会不断选择阈值，把数据分成左右两部分。例如：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">x &lt;= 3.5 进入左子树x > 3.5 进入右子树<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>每个叶节点保存该区域内所有目标值的平均值。预测时，从根节点出发，根据输入与阈值的关系向左或向右移动，最后返回叶节点的均值。</p><h3 id="如何选择划分位置"><a href="#如何选择划分位置" class="headerlink" title="如何选择划分位置"></a>如何选择划分位置</h3><p>回归树通常用均方误差衡量一组数据是否集中：</p><p>$$\operatorname{MSE}(D)&#x3D;\frac{1}{|D|}\sum_{i&#x3D;1}^{|D|}(y_i-\bar y)^2$$</p><p>对一个候选阈值，需要计算左右子集的加权误差：</p><p>$$L&#x3D;\frac{|D_L|}{|D|}\operatorname{MSE}(D_L)+\frac{|D_R|}{|D|}\operatorname{MSE}(D_R)$$</p><p>算法选择 $L$ 最小的阈值。</p><h3 id="一个简单例子-1"><a href="#一个简单例子-1" class="headerlink" title="一个简单例子"></a>一个简单例子</h3><p>假设数据如下：</p><table><thead><tr><th align="right">$x$</th><th align="right">$y$</th></tr></thead><tbody><tr><td align="right">1</td><td align="right">2</td></tr><tr><td align="right">2</td><td align="right">2</td></tr><tr><td align="right">3</td><td align="right">8</td></tr><tr><td align="right">4</td><td align="right">8</td></tr></tbody></table><p>候选阈值可以取相邻 $x$ 的中点，即 $1.5$、$2.5$ 和 $3.5$。</p><p>如果按 $x&#x3D;2.5$ 划分：</p><ul><li>左侧目标值为 $\lbrace 2,2\rbrace$，均值为 $2$，MSE 为 $0$；</li><li>右侧目标值为 $\lbrace 8,8\rbrace$，均值为 $8$，MSE 为 $0$；</li><li>加权误差为 $0$。</li></ul><p>因此，$2.5$ 是一个非常合适的阈值。模型得到的规则是：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">x &lt;= 2.5 时预测 2x > 2.5 时预测 8<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>这也解释了为什么回归树的预测图像通常是阶梯形：同一个叶节点对应的输入区间会得到相同预测值。</p><h3 id="前缀和优化"><a href="#前缀和优化" class="headerlink" title="前缀和优化"></a>前缀和优化</h3><p>如果每测试一个阈值都重新遍历左右数据，计算开销会比较大。项目在排序后建立 $y$ 和 $y^2$ 的前缀和。</p><p>对任意区间，可以快速得到：</p><p>$$\operatorname{MSE}&#x3D;E[y^2]-E[y]^2$$</p><p>这样每个候选切分位置的误差可以在 $O(1)$ 时间内计算。单个节点中，排序复杂度为 $O(n\log n)$，扫描候选阈值为 $O(n)$。</p><h3 id="树深对结果的影响"><a href="#树深对结果的影响" class="headerlink" title="树深对结果的影响"></a>树深对结果的影响</h3><p>树的最大深度决定模型复杂度：</p><ul><li>深度较小，划分次数少，曲线简单，可能欠拟合；</li><li>深度较大，模型可以描述更多局部变化，但可能过拟合；</li><li>设置最小分裂样本数，可以避免节点在样本很少时继续划分。</li></ul><p>项目同时绘制浅层树和较深的树，可以直观看到预测曲线从粗略到细致的变化。</p><h2 id="决策树分类"><a href="#决策树分类" class="headerlink" title="决策树分类"></a>决策树分类</h2><p>决策树分类与决策树回归的整体结构相似，区别主要在于：</p><ul><li>回归树预测连续数值，叶节点保存平均值；</li><li>分类树预测离散类别，叶节点保存多数类别；</li><li>回归树可以用 MSE 选择阈值；</li><li>分类树可以用信息增益选择阈值。</li></ul><p>项目使用 Iris 鸢尾花数据集。每个样本包含四个特征：</p><ul><li>花萼长度</li><li>花萼宽度</li><li>花瓣长度</li><li>花瓣宽度</li></ul><p>目标类别包括山鸢尾、变色鸢尾和维吉尼亚鸢尾。</p><h3 id="信息熵"><a href="#信息熵" class="headerlink" title="信息熵"></a>信息熵</h3><p>信息熵用于描述类别的混乱程度：</p><p>$$H(D)&#x3D;-\sum_{k&#x3D;1}^{K}p_k\log_2p_k$$</p><p>其中，$p_k$ 是第 $k$ 类样本所占比例。</p><p>如果节点中的样本全部属于同一类，熵为 $0$；如果各类别数量接近，数据更加混乱，熵会更大。</p><h3 id="一个简单例子-2"><a href="#一个简单例子-2" class="headerlink" title="一个简单例子"></a>一个简单例子</h3><p>假设某节点中有四个样本：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">A, A, B, B<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>两类比例都为 $\frac{1}{2}$，因此：</p><p>$$H(D)&#x3D;-\frac{1}{2}\log_2\frac{1}{2}-\frac{1}{2}\log_2\frac{1}{2}&#x3D;1$$</p><p>现在用某个特征进行划分，得到：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">左侧：A, A右侧：B, B<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>左右子集的熵都是 $0$。信息增益为：</p><p>$$Gain(D)&#x3D;H(D)-\frac{|D_L|}{|D|}H(D_L)-\frac{|D_R|}{|D|}H(D_R)&#x3D;1$$</p><p>这次划分完全分开了两个类别，因此效果很好。</p><h3 id="建树过程"><a href="#建树过程" class="headerlink" title="建树过程"></a>建树过程</h3><p>分类树的训练过程可以概括为：</p><ul><li>遍历每个特征；</li><li>对特征值排序并去重；</li><li>将相邻特征值的中点作为候选阈值；</li><li>计算每个候选划分的信息增益；</li><li>选择信息增益最大的特征和阈值；</li><li>对左右子集递归执行相同操作。</li></ul><p>当样本已经属于同一类别、达到最大深度或找不到有效划分时停止递归，并将当前节点设置为叶节点。</p><h3 id="如何评价分类结果"><a href="#如何评价分类结果" class="headerlink" title="如何评价分类结果"></a>如何评价分类结果</h3><p>最常用的基础指标是准确率：</p><p>$$Accuracy&#x3D;\frac{\text{预测正确的样本数}}{\text{样本总数}}\times100%$$</p><p>项目先使用固定随机种子打乱 Iris 数据，再按照 $8:2$ 划分训练集和测试集。固定随机种子可以让每次运行得到相同的数据划分，方便比较和复现实验结果。</p><p>训练准确率很高并不一定代表模型效果好，还需要观察测试准确率。如果训练准确率明显高于测试准确率，模型可能出现了过拟合。</p><h2 id="K-Means-聚类"><a href="#K-Means-聚类" class="headerlink" title="K-Means 聚类"></a>K-Means 聚类</h2><p>K-Means 用于在没有标签的情况下，将数据自动分成 $K$ 个簇。它希望同一簇内的样本彼此接近，不同簇的中心尽量分开。</p><p>算法的基本过程是：</p><ul><li>选择 $K$ 个初始聚类中心；</li><li>将每个样本分配给距离最近的中心；</li><li>对每个簇中的样本求平均，得到新中心；</li><li>重复分配和更新，直到中心几乎不再移动。</li></ul><p>二维空间中常用欧几里得距离：</p><p>$$d(\mathbf{x},\boldsymbol{\mu})&#x3D;\sqrt{\sum_{j&#x3D;1}^{m}(x_j-\mu_j)^2}$$</p><h3 id="一个简单例子-3"><a href="#一个简单例子-3" class="headerlink" title="一个简单例子"></a>一个简单例子</h3><p>假设有六个二维点：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">(1, 1), (1, 2), (2, 1)(8, 8), (8, 9), (9, 8)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>设置 $K&#x3D;2$，初始中心选择 $(1,1)$ 和 $(8,8)$。</p><p>第一次分配后，前三个点更接近 $(1,1)$，后三个点更接近 $(8,8)$。随后计算两个簇的新中心：</p><p>$$\boldsymbol{\mu}_1&#x3D;\left(\frac{1+1+2}{3},\frac{1+2+1}{3}\right)&#x3D;(1.33,1.33)$$</p><p>$$\boldsymbol{\mu}_2&#x3D;\left(\frac{8+8+9}{3},\frac{8+9+8}{3}\right)&#x3D;(8.33,8.33)$$</p><p>再次分配时，各点所属簇不会改变，中心也保持稳定，算法便可以停止。</p><h3 id="为什么需要-K-Means"><a href="#为什么需要-K-Means" class="headerlink" title="为什么需要 K-Means++"></a>为什么需要 K-Means++</h3><p>普通 K-Means 如果随机选出的初始中心过于接近，可能需要更多次迭代，也可能得到较差结果。</p><p>K-Means++ 的初始化方式是：</p><ul><li>随机选择第一个中心；</li><li>计算每个样本到最近已有中心的距离平方；</li><li>按距离平方作为权重选择下一个中心；</li><li>重复以上过程，直到得到 $K$ 个中心。</li></ul><p>距离现有中心越远的点，被选为新中心的概率越大。因此，初始中心通常会分布得更分散。</p><p>需要注意，这里是“按概率选择”，而不是每次直接选择最远点。这样可以降低异常值被固定选中的风险。</p><h3 id="收敛与空簇处理"><a href="#收敛与空簇处理" class="headerlink" title="收敛与空簇处理"></a>收敛与空簇处理</h3><p>项目计算新旧中心之间的位移。如果最大位移不超过容差，就认为算法已经收敛：</p><p>$$\max_k\left\lVert\boldsymbol{\mu}_k^{new}-\boldsymbol{\mu}_k^{old}\right\rVert\leq\varepsilon$$</p><p>如果某个簇没有分配到任何样本，就无法计算平均值。一个简单的处理方式是暂时保留该簇原来的中心。也可以重新选择距离其他中心较远的样本作为中心。</p><p>K-Means 的局限也比较明显：需要提前指定 $K$，对初始中心敏感，更适合近似球状、大小接近的簇，而且容易受到异常值影响。</p><h2 id="可视化如何帮助理解算法"><a href="#可视化如何帮助理解算法" class="headerlink" title="可视化如何帮助理解算法"></a>可视化如何帮助理解算法</h2><p>图形界面不改变算法本身，但能把抽象结果转换为更直观的图像：</p><ul><li>在线性回归中，同时绘制原始散点和拟合直线；</li><li>在决策树回归中，用阶梯线展示分段常数预测；</li><li>在决策树分类中，用不同颜色表示不同类别；</li><li>在 K-Means 中，用颜色表示聚类标签，用特殊标记显示中心。</li></ul><p>从数据坐标转换到屏幕坐标时，横坐标可以按比例映射：</p><p>$$x_s&#x3D;x_{left}+\frac{x-x_{min}}{x_{max}-x_{min}}(x_{right}-x_{left})$$</p><p>由于屏幕纵坐标通常向下增大，纵坐标映射需要反向：</p><p>$$y_s&#x3D;y_{bottom}-\frac{y-y_{min}}{y_{max}-y_{min}}(y_{bottom}-y_{top})$$</p><p>项目使用 EasyX 完成这些基础绘制和交互。对于理解算法而言，重点仍然是数据如何训练、模型如何预测以及结果为什么呈现出当前形态。</p><h2 id="几种算法的对比"><a href="#几种算法的对比" class="headerlink" title="几种算法的对比"></a>几种算法的对比</h2><table><thead><tr><th>算法</th><th>任务</th><th>主要依据</th><th>结果特点</th></tr></thead><tbody><tr><td>最小二乘线性回归</td><td>回归</td><td>残差平方和最小</td><td>一条连续直线</td></tr><tr><td>决策树回归</td><td>回归</td><td>加权 MSE 最小</td><td>分段常数、阶梯形</td></tr><tr><td>决策树分类</td><td>分类</td><td>信息增益最大</td><td>一组可解释的判断规则</td></tr><tr><td>K-Means</td><td>聚类</td><td>样本到中心的距离</td><td>自动形成若干数据簇</td></tr></tbody></table><p>线性回归结构简单、计算速度快，但只能描述近似线性关系。决策树能够表达非线性规则，解释也比较直观，但树过深时容易过拟合。K-Means 不需要标签，实现简单，不过需要提前指定簇数，并且会受到数据分布和初始中心影响。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过手动实现这些基础算法，可以更清楚地看到机器学习的核心过程：</p><ul><li>回归是在寻找输入到连续数值的映射；</li><li>分类是在学习区分不同类别的规则；</li><li>聚类是在没有标签时发现数据内部的分组；</li><li>损失函数或评价指标决定了算法如何选择更好的参数；</li><li>停止条件和边界处理决定了程序是否可靠。</li></ul><p>相比只调用现成接口，小规模示例更适合观察每一步计算。把算法结果绘制出来后，拟合直线、树的阶梯预测和聚类中心都会变得容易理解，这也是该 C++ 可视化项目最主要的学习价值。</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/cpp/">cpp</category>
      
      <category domain="https://level0-jj.github.io/tags/%E5%86%B3%E7%AD%96%E6%A0%91/">决策树</category>
      
      <category domain="https://level0-jj.github.io/tags/KMeans/">KMeans</category>
      
      <category domain="https://level0-jj.github.io/tags/%E8%81%9A%E7%B1%BB/">聚类</category>
      
      
      <comments>https://level0-jj.github.io/2026/07/21/%E5%85%B3%E4%BA%8E%E6%88%91%E7%9A%84%E5%B0%8F%E5%AD%A6%E6%9C%9F%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%A4%A7%E4%BD%9C%E4%B8%9A/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>DLinear复现实验</title>
      <link>https://level0-jj.github.io/2026/05/28/DLinear%E5%A4%8D%E7%8E%B0%E5%AE%9E%E9%AA%8C/</link>
      <guid>https://level0-jj.github.io/2026/05/28/DLinear%E5%A4%8D%E7%8E%B0%E5%AE%9E%E9%AA%8C/</guid>
      <pubDate>Thu, 28 May 2026 14:43:36 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;最近我复现了时间序列预测领域的一篇经典论文：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Are Transformers Ef</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近我复现了时间序列预测领域的一篇经典论文：</p><blockquote><p><strong>Are Transformers Effective for Time Series Forecasting?</strong><br>AAAI 2023<br>对应代码仓库：LTSF-Linear</p></blockquote><p><a href="DLinear.pdf">DLinear</a>这篇论文提出了一个很有意思的问题：</p><blockquote><p>在长序列时间序列预测任务中，复杂的 Transformer 模型真的一定比简单线性模型好吗？</p></blockquote><p>论文的结论是：在很多长序列预测 benchmark 上，简单的线性模型反而可以取得非常有竞争力的结果，甚至超过一些 Transformer-based 方法。</p><p>这篇博客主要记录我本次复现实验的过程、结果和阶段性分析。相比单纯“跑通代码”，我更希望把这次复现整理成一个比较规范的实验过程，包括：</p><ol><li>跑哪些模型；</li><li>跑哪些数据集；</li><li>如何整理 MSE&#x2F;MAE；</li><li>如何比较 Linear 和 DLinear；</li><li>如何分析多次运行稳定性；</li><li>如何结合可视化结果进行解释；</li><li>当前复现还缺什么。</li></ol><hr><h2 id="论文和模型简介"><a href="#论文和模型简介" class="headerlink" title="论文和模型简介"></a>论文和模型简介</h2><p>LTSF-Linear 论文主要讨论的是 Long-term Time Series Forecasting，也就是长序列时间序列预测任务。</p><p>论文中提出了三个非常简单的线性模型：</p><table><thead><tr><th>模型</th><th>核心思想</th></tr></thead><tbody><tr><td>Linear</td><td>直接用一个线性层从历史序列预测未来序列</td></tr><tr><td>NLinear</td><td>在 Linear 基础上加入简单归一化，用于缓解分布偏移</td></tr><tr><td>DLinear</td><td>先将序列分解为 trend 和 seasonal，再分别用线性层预测</td></tr></tbody></table><p>其中，DLinear 是本文最核心的模型之一。</p><p>DLinear 的基本思想是：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">原始序列 = 趋势项 trend + 季节/残差项 seasonal<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后模型分别对趋势项和季节项进行线性预测，最后将两部分结果相加得到最终预测。</p><p>这看起来很简单，但它在很多长预测任务上表现不错。</p><hr><h2 id="实验环境与结果来源"><a href="#实验环境与结果来源" class="headerlink" title="实验环境与结果来源"></a>实验环境与结果来源</h2><p>本次实验主要在 WSL 环境中运行，然后将结果同步到 Windows 上整理。</p><p>实验结果主要来自以下文件：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">logs/LongForecasting/*.logresult.txttest_results/<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>其中：</p><table><thead><tr><th>文件或目录</th><th>作用</th></tr></thead><tbody><tr><td><code>logs/LongForecasting/*.log</code></td><td>保存每次实验的训练过程和最终 MSE&#x2F;MAE</td></tr><tr><td><code>result.txt</code></td><td>保存每次测试的汇总指标，但存在重复记录</td></tr><tr><td><code>test_results/</code></td><td>保存预测曲线 PDF，可用于可视化分析</td></tr><tr><td><code>results/</code></td><td>保存 <code>.npy</code> 预测数组，本阶段主要作为备份</td></tr></tbody></table><p>由于 <code>result.txt</code> 中可能出现重复记录，所以最终分析时我主要以：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">logs/LongForecasting/*.log<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>为准。</p><hr><h2 id="当前完成的实验"><a href="#当前完成的实验" class="headerlink" title="当前完成的实验"></a>当前完成的实验</h2><p>本阶段主要完成了以下实验。</p><h3 id="ETTh1-数据集"><a href="#ETTh1-数据集" class="headerlink" title="ETTh1 数据集"></a>ETTh1 数据集</h3><p>在 ETTh1 数据集上，我完成了：</p><table><thead><tr><th>Dataset</th><th>Model</th><th align="right">Seq Len</th><th>Pred Len</th></tr></thead><tbody><tr><td>ETTh1</td><td>Linear</td><td align="right">336</td><td>96 &#x2F; 192 &#x2F; 336 &#x2F; 720</td></tr><tr><td>ETTh1</td><td>DLinear</td><td align="right">336</td><td>96 &#x2F; 192 &#x2F; 336 &#x2F; 720</td></tr><tr><td>ETTh1</td><td>DLinear</td><td align="right">336</td><td>96 &#x2F; 192 &#x2F; 336 &#x2F; 720，多次运行</td></tr></tbody></table><p>也就是说，ETTh1 上已经可以比较：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">Linear vs DLinear<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>同时，DLinear 也有多次运行结果，可以做初步稳定性分析。</p><hr><h3 id="ETTm1-数据集"><a href="#ETTm1-数据集" class="headerlink" title="ETTm1 数据集"></a>ETTm1 数据集</h3><p>在 ETTm1 数据集上，我完成了：</p><table><thead><tr><th>Dataset</th><th>Model</th><th align="right">Seq Len</th><th>Pred Len</th></tr></thead><tbody><tr><td>ETTm1</td><td>DLinear</td><td align="right">336</td><td>96 &#x2F; 192 &#x2F; 336 &#x2F; 720</td></tr></tbody></table><p>这个实验主要用于观察 DLinear 在另一个 ETT 数据集上的预测长度变化趋势。</p><hr><h2 id="ETTh1：Linear-与-DLinear-对比"><a href="#ETTh1：Linear-与-DLinear-对比" class="headerlink" title="ETTh1：Linear 与 DLinear 对比"></a>ETTh1：Linear 与 DLinear 对比</h2><p>首先看 ETTh1 上 Linear 和 DLinear 的 MSE 对比。</p><p>这里 DLinear 使用多次运行的平均结果。</p><table><thead><tr><th align="right">Pred Len</th><th align="right">Linear MSE</th><th align="right">DLinear Mean MSE</th><th>结论</th></tr></thead><tbody><tr><td align="right">96</td><td align="right">0.3747</td><td align="right">0.3767</td><td>基本接近，Linear 略优</td></tr><tr><td align="right">192</td><td align="right">0.4183</td><td align="right">0.4201</td><td>基本接近，Linear 略优</td></tr><tr><td align="right">336</td><td align="right">0.4792</td><td align="right">0.4583</td><td>DLinear 更优</td></tr><tr><td align="right">720</td><td align="right">0.6250</td><td align="right">0.5087</td><td>DLinear 明显更优</td></tr></tbody></table><p>再看 MAE：</p><table><thead><tr><th align="right">Pred Len</th><th align="right">Linear MAE</th><th align="right">DLinear Mean MAE</th><th>结论</th></tr></thead><tbody><tr><td align="right">96</td><td align="right">0.3967</td><td align="right">0.3982</td><td>基本接近</td></tr><tr><td align="right">192</td><td align="right">0.4288</td><td align="right">0.4288</td><td>基本持平</td></tr><tr><td align="right">336</td><td align="right">0.4767</td><td align="right">0.4593</td><td>DLinear 更优</td></tr><tr><td align="right">720</td><td align="right">0.5930</td><td align="right">0.5159</td><td>DLinear 明显更优</td></tr></tbody></table><p>从结果可以看出：</p><ol><li>在短预测长度 <code>96</code> 和 <code>192</code> 下，Linear 和 DLinear 的表现非常接近；</li><li>在更长预测长度 <code>336</code> 和 <code>720</code> 下，DLinear 开始明显优于 Linear；</li><li>尤其是 <code>pred_len=720</code> 时，DLinear 相比 Linear 的 MSE 下降约 <strong>18.60%</strong>。</li></ol><p>这个结果说明：</p><blockquote><p>DLinear 的趋势&#x2F;季节分解机制在长预测任务中更有优势。</p></blockquote><p>这也符合论文中的直觉：当预测长度变长时，显式建模趋势和周期结构会更重要。</p><hr><h2 id="ETTh1：DLinear-多次运行稳定性分析"><a href="#ETTh1：DLinear-多次运行稳定性分析" class="headerlink" title="ETTh1：DLinear 多次运行稳定性分析"></a>ETTh1：DLinear 多次运行稳定性分析</h2><p>为了避免只看单次实验结果，我对 ETTh1 上的 DLinear 结果进行了多次运行整理。</p><p>当前有三组结果：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">default/rerunseed2025seed2026<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>其中 default&#x2F;rerun 是一次未单独标注 seed 的重跑记录，seed2025 和 seed2026 是两次带 seed 标记的实验。</p><h3 id="MSE-结果"><a href="#MSE-结果" class="headerlink" title="MSE 结果"></a>MSE 结果</h3><table><thead><tr><th align="right">Pred Len</th><th align="right">Default&#x2F;Rerun MSE</th><th align="right">Seed2025 MSE</th><th align="right">Seed2026 MSE</th><th align="right">Mean MSE</th><th align="right">Std</th></tr></thead><tbody><tr><td align="right">96</td><td align="right">0.3841</td><td align="right">0.3720</td><td align="right">0.3740</td><td align="right">0.3767</td><td align="right">0.0065</td></tr><tr><td align="right">192</td><td align="right">0.4435</td><td align="right">0.4070</td><td align="right">0.4099</td><td align="right">0.4201</td><td align="right">0.0203</td></tr><tr><td align="right">336</td><td align="right">0.4469</td><td align="right">0.4644</td><td align="right">0.4636</td><td align="right">0.4583</td><td align="right">0.0099</td></tr><tr><td align="right">720</td><td align="right">0.5042</td><td align="right">0.5393</td><td align="right">0.4827</td><td align="right">0.5087</td><td align="right">0.0286</td></tr></tbody></table><p>这里的 <code>Std</code> 是标准差，用于衡量多次运行结果的波动程度。</p><h3 id="如何理解标准差"><a href="#如何理解标准差" class="headerlink" title="如何理解标准差"></a>如何理解标准差</h3><p>标准差越小，说明多次运行结果越接近，模型越稳定。</p><p>标准差越大，说明不同运行之间差异越大，模型对随机性或训练过程更敏感。</p><p>从上表可以看到：</p><ul><li><code>pred_len=96</code> 的结果比较稳定；</li><li><code>pred_len=336</code> 的结果也相对稳定；</li><li><code>pred_len=720</code> 的波动较大；</li><li><code>pred_len=192</code> 中 default&#x2F;rerun 明显高于 seed2025&#x2F;2026，说明也存在一定训练波动。</li></ul><p>这说明：</p><blockquote><p>长预测任务本身更困难，模型结果更容易受到随机初始化、训练过程和 early stopping 的影响。</p></blockquote><p>如果后续要写得更严谨，应该继续补充更多随机种子，例如：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">seed2027seed2028<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>然后报告：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">mean ± std<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><hr><h2 id="ETTm1：DLinear-预测长度趋势分析"><a href="#ETTm1：DLinear-预测长度趋势分析" class="headerlink" title="ETTm1：DLinear 预测长度趋势分析"></a>ETTm1：DLinear 预测长度趋势分析</h2><p>接下来观察 ETTm1 上 DLinear 的结果。</p><table><thead><tr><th align="right">Pred Len</th><th align="right">MSE</th><th align="right">MAE</th></tr></thead><tbody><tr><td align="right">96</td><td align="right">0.3012</td><td align="right">0.3446</td></tr><tr><td align="right">192</td><td align="right">0.3356</td><td align="right">0.3661</td></tr><tr><td align="right">336</td><td align="right">0.3718</td><td align="right">0.3887</td></tr><tr><td align="right">720</td><td align="right">0.4266</td><td align="right">0.4227</td></tr></tbody></table><p>这个结果非常符合长序列预测任务的一般规律：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">预测长度越长，误差越大。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>从 96 到 720：</p><ul><li>MSE 从 0.3012 增加到 0.4266；</li><li>MAE 从 0.3446 增加到 0.4227。</li></ul><p>这说明实验结果整体合理，没有出现明显异常。</p><hr><h2 id="可视化结果怎么看"><a href="#可视化结果怎么看" class="headerlink" title="可视化结果怎么看"></a>可视化结果怎么看</h2><p>除了 MSE 和 MAE，项目还会在 <code>test_results/</code> 中保存预测曲线 PDF。</p><p>可视化图通常包含：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">历史输入 + 真实未来曲线 + 模型预测曲线<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>看这些图时，我主要从以下几个角度分析。</p><hr><h3 id="预测曲线是否跟随真实曲线趋势"><a href="#预测曲线是否跟随真实曲线趋势" class="headerlink" title="预测曲线是否跟随真实曲线趋势"></a>预测曲线是否跟随真实曲线趋势</h3><p>首先看预测曲线的大方向是否和真实曲线一致。</p><p>比如：</p><ul><li>真实曲线整体上升，预测曲线是否也上升；</li><li>真实曲线整体下降，预测曲线是否也下降；</li><li>真实曲线有周期性波动，预测曲线是否也有类似波动。</li></ul><p>如果趋势能跟上，说明模型捕捉到了主要变化规律。</p><p>如果趋势跟不上，说明模型可能只学到了平均水平，而没有学到时间变化结构。</p><hr><h3 id="长预测下是否出现过度平滑"><a href="#长预测下是否出现过度平滑" class="headerlink" title="长预测下是否出现过度平滑"></a>长预测下是否出现过度平滑</h3><p>长预测时，模型经常会出现一种现象：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">预测曲线越来越平，波动越来越小。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>也就是说：</p><ul><li>真实曲线有明显峰谷；</li><li>但预测曲线像一条比较平滑的线；</li><li>模型预测不到尖峰、突变、快速下降或快速上升。</li></ul><p>这说明模型对长期未来的不确定性处理不够好。</p><p>这个现象尤其应该在：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">pred_len=720<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>中观察。</p><hr><h3 id="Linear-和-DLinear-在长预测下是否有差异"><a href="#Linear-和-DLinear-在长预测下是否有差异" class="headerlink" title="Linear 和 DLinear 在长预测下是否有差异"></a>Linear 和 DLinear 在长预测下是否有差异</h3><p>可以对比同一个数据集、同一个预测长度下的图。</p><p>例如：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">ETTh1_336_720_Linear.../0.pdfETTh1_336_720_DLinear.../0.pdf<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>主要看：</p><ol><li>哪个模型预测曲线更接近真实曲线；</li><li>哪个模型趋势更准；</li><li>哪个模型更能保留波动；</li><li>哪个模型在预测后半段偏移更严重。</li></ol><p>如果 DLinear 图上更贴近真实曲线，同时 MSE&#x2F;MAE 也更低，就可以说：</p><blockquote><p>DLinear 不仅指标更好，可视化上也能更好地跟随长期趋势。</p></blockquote><hr><h3 id="是否存在峰值预测失败的样本"><a href="#是否存在峰值预测失败的样本" class="headerlink" title="是否存在峰值预测失败的样本"></a>是否存在峰值预测失败的样本</h3><p>峰值预测失败指的是：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">真实曲线突然升高或降低，但预测曲线没有跟上。<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>常见表现包括：</p><ul><li>真实值有尖峰，预测没有；</li><li>真实值有突然下降，预测还很平；</li><li>预测曲线滞后于真实曲线；</li><li>模型只预测出平均趋势，忽略极端变化。</li></ul><p>这类图很适合放进汇报，因为它能说明：</p><blockquote><p>模型虽然整体 MSE&#x2F;MAE 不错，但对突发变化仍然不足。</p></blockquote><p>这就是失败案例分析。</p><hr><h2 id="当前结果说明了什么"><a href="#当前结果说明了什么" class="headerlink" title="当前结果说明了什么"></a>当前结果说明了什么</h2><p>综合数值结果和可视化分析，本阶段可以得到几个结论。</p><h3 id="简单-Linear-已经是很强的-baseline"><a href="#简单-Linear-已经是很强的-baseline" class="headerlink" title="简单 Linear 已经是很强的 baseline"></a>简单 Linear 已经是很强的 baseline</h3><p>ETTh1 上，在 <code>pred_len=96</code> 和 <code>192</code> 时，Linear 和 DLinear 的结果非常接近，甚至 Linear 略优。</p><p>这说明：</p><blockquote><p>在一些短预测任务中，简单线性映射已经能捕捉主要模式。</p></blockquote><p>这也是 LTSF-Linear 论文想强调的核心观点之一：不要盲目认为复杂模型一定更好。</p><hr><h3 id="DLinear-在长预测上更有优势"><a href="#DLinear-在长预测上更有优势" class="headerlink" title="DLinear 在长预测上更有优势"></a>DLinear 在长预测上更有优势</h3><p>当预测长度增加到 336 和 720 时，DLinear 明显优于 Linear。</p><p>尤其在 <code>pred_len=720</code> 上：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">Linear MSE = 0.6250DLinear Mean MSE = 0.5087<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>DLinear 的优势很明显。</p><p>这说明：</p><blockquote><p>趋势&#x2F;季节分解结构更适合长预测任务。</p></blockquote><hr><h3 id="长预测任务稳定性更差"><a href="#长预测任务稳定性更差" class="headerlink" title="长预测任务稳定性更差"></a>长预测任务稳定性更差</h3><p>DLinear 在 <code>pred_len=720</code> 上的多次运行结果波动较大：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">0.5042 / 0.5393 / 0.4827<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这说明：</p><blockquote><p>长预测任务不仅误差更大，而且结果稳定性也更差。</p></blockquote><p>因此后续如果要做更规范的科研实验，需要更多 seed。</p><hr><h3 id="ETTm1-结果趋势合理"><a href="#ETTm1-结果趋势合理" class="headerlink" title="ETTm1 结果趋势合理"></a>ETTm1 结果趋势合理</h3><p>ETTm1 上 DLinear 的误差随预测长度增加而单调上升：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">96 &lt; 192 &lt; 336 &lt; 720<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这符合长序列预测任务的基本规律，说明实验结果整体可信。</p><hr><h2 id="当前复现还缺什么"><a href="#当前复现还缺什么" class="headerlink" title="当前复现还缺什么"></a>当前复现还缺什么</h2><p>虽然目前已经完成了阶段性复现，但还不能说完整复现论文。</p><p>当前还缺：</p><ol><li><p><strong>NLinear 实验</strong><br>LTSF-Linear 包含 Linear、NLinear、DLinear 三个模型，目前还缺 NLinear。</p></li><li><p><strong>Transformer baseline</strong><br>论文核心问题是“Transformer 是否适合长序列预测”，所以后续需要补 Transformer 或 Autoformer。</p></li><li><p><strong>更多数据集</strong><br>当前主要有 ETTh1 和 ETTm1，后续可以补 Weather、Exchange 等。</p></li><li><p><strong>更多随机种子</strong><br>尤其是 DLinear 在 <code>pred_len=720</code> 上波动较大，需要更多 seed 验证稳定性。</p></li><li><p><strong>更系统的可视化分析</strong><br>后续可以从 PDF 中挑选成功案例和失败案例，形成更完整的图表分析。</p></li></ol><hr><h2 id="下一步计划"><a href="#下一步计划" class="headerlink" title="下一步计划"></a>下一步计划</h2><p>如果继续推进，我认为优先级应该是：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">1. 补 ETTh1 + NLinear + pred_len=96/192/336/7202. 补 ETTh1 + DLinear + pred_len=720 + 更多 seed3. 补 Transformer 或 Autoformer baseline4. 整理典型可视化图5. 扩展到 Weather / Exchange 数据集<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这样可以从“代码复现”进一步升级为“系统实验复现”。</p><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本次复现实验完成了 LTSF-Linear 的阶段性实验整理，主要包括：</p><ul><li>ETTh1 上 Linear 和 DLinear 的对比；</li><li>ETTh1 上 DLinear 的多次运行稳定性分析；</li><li>ETTm1 上 DLinear 的预测长度趋势分析；</li><li>基于 <code>test_results</code> 的可视化分析思路。</li></ul><p>目前最重要的结论是：</p><blockquote><p>在短预测长度下，Linear 和 DLinear 表现接近；但随着预测长度变长，DLinear 的优势逐渐明显，尤其在 <code>pred_len=720</code> 上明显优于 Linear。这说明 DLinear 的趋势&#x2F;季节分解结构对长预测任务有帮助。</p></blockquote><p>同时也观察到：</p><blockquote><p>长预测任务下模型结果波动更大，因此后续需要更多随机种子和更多 baseline 来增强实验可信度。</p></blockquote><p>这次复现让我认识到，科研中的“复现”不只是把代码跑通，更重要的是把结果整理成表格、做对比、分析趋势、解释可视化现象，并明确下一步还缺什么。</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/%E6%97%B6%E5%BA%8F%E9%A2%84%E6%B5%8B/">时序预测</category>
      
      
      <category domain="https://level0-jj.github.io/tags/python/">python</category>
      
      <category domain="https://level0-jj.github.io/tags/%E6%97%B6%E5%BA%8F%E9%A2%84%E6%B5%8B/">时序预测</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/28/DLinear%E5%A4%8D%E7%8E%B0%E5%AE%9E%E9%AA%8C/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>基于 C 语言的图像旋转算法解析</title>
      <link>https://level0-jj.github.io/2026/05/24/%E5%9F%BA%E4%BA%8E-C-%E8%AF%AD%E8%A8%80%E7%9A%84%E5%9B%BE%E5%83%8F%E6%97%8B%E8%BD%AC%E7%AE%97%E6%B3%95%E8%A7%A3%E6%9E%90/</link>
      <guid>https://level0-jj.github.io/2026/05/24/%E5%9F%BA%E4%BA%8E-C-%E8%AF%AD%E8%A8%80%E7%9A%84%E5%9B%BE%E5%83%8F%E6%97%8B%E8%BD%AC%E7%AE%97%E6%B3%95%E8%A7%A3%E6%9E%90/</guid>
      <pubDate>Sun, 24 May 2026 08:49:30 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;p&gt;本文将深入解析一个基于 C 语言实现的图像旋转算法，核心函数为 &lt;code&gt;Image *Rotation(Image *image)&lt;/c</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>本文将深入解析一个基于 C 语言实现的图像旋转算法，核心函数为 <code>Image *Rotation(Image *image)</code>。该算法以灰度或彩色图像为输入，实现了逆时针 45 度旋转，并采用双线性插值保证旋转后图像的平滑性。代码结构清晰，适合学习图像处理基础与算法实现。</p><h2 id="代码结构"><a href="#代码结构" class="headerlink" title="代码结构"></a>代码结构</h2><p>包含如下核心部分：</p><ul><li>图像结构体与操作函数（通过 <code>proto.h</code> 引入）</li><li>图像读取与保存：<code>ReadPNMImage</code>、<code>SavePNMImage</code></li><li>图像旋转：<code>Rotation</code></li><li>新图像创建：<code>CreateNewImage</code></li><li>主流程：<code>main</code>、<code>TestReadImage</code></li></ul><h2 id="核心函数：Image-Rotation-Image-image"><a href="#核心函数：Image-Rotation-Image-image" class="headerlink" title="核心函数：Image *Rotation(Image *image)"></a>核心函数：<code>Image *Rotation(Image *image)</code></h2><h3 id="算法流程"><a href="#算法流程" class="headerlink" title="算法流程"></a>算法流程</h3><ol><li><p><strong>新图像创建</strong><br>调用 <code>CreateNewImage</code>，输出图像尺寸为原图的 1.5 倍，便于旋转后完整显示。</p></li><li><p><strong>中心坐标计算</strong><br>原图与输出图像分别计算中心点坐标，用于旋转变换。</p></li><li><p><strong>旋转参数计算</strong><br>旋转角度为 -45°（逆时针），计算对应的正弦、余弦值。</p></li><li><p><strong>逆向映射</strong><br>对输出图像每个像素，计算其对应原图位置（逆向旋转），避免空洞。</p></li><li><p><strong>双线性插值</strong><br>若映射点在原图范围内，采用双线性插值计算像素值，提升旋转后图像质量。</p></li><li><p><strong>边界处理</strong><br>映射点超出原图范围时，像素值设为 0（黑色）。</p></li></ol><h3 id="关键代码片段"><a href="#关键代码片段" class="headerlink" title="关键代码片段"></a>关键代码片段</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">double</span> rad <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">45.0</span> <span class="token operator">*</span> <span class="token number">3.1415926</span> <span class="token operator">/</span> <span class="token number">180.0</span><span class="token punctuation">;</span><span class="token keyword">double</span> cosA <span class="token operator">=</span> <span class="token function">cos</span><span class="token punctuation">(</span>rad<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">double</span> sinA <span class="token operator">=</span> <span class="token function">sin</span><span class="token punctuation">(</span>rad<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">int</span> inCx <span class="token operator">=</span> inW <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token keyword">int</span> inCy <span class="token operator">=</span> inH <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token keyword">int</span> outCx <span class="token operator">=</span> outW <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token keyword">int</span> outCy <span class="token operator">=</span> outH <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> y <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> y <span class="token operator">&lt;</span> outH<span class="token punctuation">;</span> y<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> x <span class="token operator">&lt;</span> outW<span class="token punctuation">;</span> x<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">double</span> tx <span class="token operator">=</span> x <span class="token operator">-</span> outCx<span class="token punctuation">;</span>        <span class="token keyword">double</span> ty <span class="token operator">=</span> y <span class="token operator">-</span> outCy<span class="token punctuation">;</span>        <span class="token keyword">double</span> srcX <span class="token operator">=</span> tx <span class="token operator">*</span> cosA <span class="token operator">-</span> ty <span class="token operator">*</span> sinA <span class="token operator">+</span> inCx<span class="token punctuation">;</span>        <span class="token keyword">double</span> srcY <span class="token operator">=</span> tx <span class="token operator">*</span> sinA <span class="token operator">+</span> ty <span class="token operator">*</span> cosA <span class="token operator">+</span> inCy<span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>srcX <span class="token operator">>=</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> srcX <span class="token operator">&lt;</span> inW <span class="token operator">-</span> <span class="token number">1</span> <span class="token operator">&amp;&amp;</span> srcY <span class="token operator">>=</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> srcY <span class="token operator">&lt;</span> inH <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            <span class="token comment">// 双线性插值</span>            <span class="token keyword">int</span> x1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span><span class="token function">floor</span><span class="token punctuation">(</span>srcX<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> y1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span><span class="token function">floor</span><span class="token punctuation">(</span>srcY<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> x2 <span class="token operator">=</span> x1 <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> y2 <span class="token operator">=</span> y1 <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>            <span class="token keyword">double</span> dx <span class="token operator">=</span> srcX <span class="token operator">-</span> x1<span class="token punctuation">;</span>            <span class="token keyword">double</span> dy <span class="token operator">=</span> srcY <span class="token operator">-</span> y1<span class="token punctuation">;</span>            <span class="token keyword">int</span> p1 <span class="token operator">=</span> in<span class="token punctuation">[</span>y1 <span class="token operator">*</span> inW <span class="token operator">+</span> x1<span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> p2 <span class="token operator">=</span> in<span class="token punctuation">[</span>y1 <span class="token operator">*</span> inW <span class="token operator">+</span> x2<span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> p3 <span class="token operator">=</span> in<span class="token punctuation">[</span>y2 <span class="token operator">*</span> inW <span class="token operator">+</span> x1<span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> p4 <span class="token operator">=</span> in<span class="token punctuation">[</span>y2 <span class="token operator">*</span> inW <span class="token operator">+</span> x2<span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">double</span> value <span class="token operator">=</span> p1 <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> dx<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> dy<span class="token punctuation">)</span> <span class="token operator">+</span>                           p2 <span class="token operator">*</span> dx <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> dy<span class="token punctuation">)</span> <span class="token operator">+</span>                           p3 <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> dx<span class="token punctuation">)</span> <span class="token operator">*</span> dy <span class="token operator">+</span>                           p4 <span class="token operator">*</span> dx <span class="token operator">*</span> dy<span class="token punctuation">;</span>            out<span class="token punctuation">[</span>y <span class="token operator">*</span> outW <span class="token operator">+</span> x<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">char</span><span class="token punctuation">)</span>value<span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>            out<span class="token punctuation">[</span>y <span class="token operator">*</span> outW <span class="token operator">+</span> x<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="算法特点"><a href="#算法特点" class="headerlink" title="算法特点"></a>算法特点</h3><ul><li><strong>逆向映射</strong>：避免旋转后像素空洞，保证每个输出像素都能找到原图对应点。</li><li><strong>双线性插值</strong>：提升旋转后图像质量，减少锯齿和模糊。</li><li><strong>中心化处理</strong>：以图像中心为旋转轴，旋转效果自然。</li></ul><h2 id="主流程与使用方法"><a href="#主流程与使用方法" class="headerlink" title="主流程与使用方法"></a>主流程与使用方法</h2><p>主函数 <code>main</code> 调用 <code>TestReadImage</code>，流程如下：</p><ol><li>读取原图像（如 <code>lena.pgm</code>）</li><li>调用 <code>Rotation</code> 进行旋转</li><li>保存旋转后图像为 <code>rotation.pgm</code></li></ol><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span><span class="token keyword">char</span> <span class="token operator">*</span><span class="token operator">*</span>argv<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_in<span class="token operator">=</span><span class="token string">"lena.pgm"</span><span class="token punctuation">;</span>    <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_out<span class="token operator">=</span><span class="token string">""</span><span class="token punctuation">;</span>    <span class="token function">TestReadImage</span><span class="token punctuation">(</span>file_in<span class="token punctuation">,</span>file_out<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">TestReadImage</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_in<span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_out<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    Image <span class="token operator">*</span>image<span class="token punctuation">;</span>    Image  <span class="token operator">*</span>rotation<span class="token punctuation">;</span>    image <span class="token operator">=</span> <span class="token function">ReadPNMImage</span><span class="token punctuation">(</span>file_in<span class="token punctuation">)</span><span class="token punctuation">;</span>    rotation <span class="token operator">=</span> <span class="token function">Rotation</span><span class="token punctuation">(</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">SavePNMImage</span><span class="token punctuation">(</span>rotation<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">char</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token string">"rotation.pgm"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文以 <code>Image *Rotation(Image *image)</code> 为核心，解析了 C 语言实现的图像旋转算法。该算法采用逆向映射与双线性插值，保证旋转后图像的完整性与质量。适合图像处理初学者深入理解旋转算法原理与实现细节。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://en.wikipedia.org/wiki/Digital_image_processing">数字图像处理基础</a></li><li><a href="https://en.wikipedia.org/wiki/Bilinear_interpolation">双线性插值算法</a></li><li>源代码：<code>main.c</code></li></ul>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/c/">c</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/24/%E5%9F%BA%E4%BA%8E-C-%E8%AF%AD%E8%A8%80%E7%9A%84%E5%9B%BE%E5%83%8F%E6%97%8B%E8%BD%AC%E7%AE%97%E6%B3%95%E8%A7%A3%E6%9E%90/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>数字图像处理中的图像滤波</title>
      <link>https://level0-jj.github.io/2026/05/23/%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%E4%B8%AD%E7%9A%84%E5%9B%BE%E5%83%8F%E6%BB%A4%E6%B3%A2/</link>
      <guid>https://level0-jj.github.io/2026/05/23/%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%E4%B8%AD%E7%9A%84%E5%9B%BE%E5%83%8F%E6%BB%A4%E6%B3%A2/</guid>
      <pubDate>Sat, 23 May 2026 02:24:45 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;图像滤波本质上是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;利用某个像素周围邻域的信息，对当前像素进行重新计算。&lt;/p&gt;
&lt;/blockqu</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>图像滤波本质上是：</p><blockquote><p>利用某个像素周围邻域的信息，对当前像素进行重新计算。</p></blockquote><p>常见用途：</p><ul><li>去除噪声</li><li>平滑图像</li><li>提高图像质量</li><li>为后续边缘检测做准备</li></ul><h2 id="项目简介"><a href="#项目简介" class="headerlink" title="项目简介"></a>项目简介</h2><ol><li>读取一个 PGM 灰度图像（P5 格式，如 <a href="(gitcode.com/open-source-toolkit/93a09)">lena.pgm</a>）, 得到一个二维像素矩阵（实际存在一维数组里）。</li><li>对图像分别应用 3×3 均值滤波 和 3×3 中值滤波。</li><li>将滤波后的图像保存为新的 PGM 文件（lena_avg.pgm 和 lena_med.pgm）。</li></ol><p>整个代码不依赖任何第三方图像库，完全使用标准 C 语言和自定义的 Image 结构体。</p><p>虽然整个项目包含多个函数，但真正的核心，其实是：<code>AverageImage()</code>和<code>MedianImage</code></p><p>程序执行流程如下：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">读取图像   ↓均值滤波   ↓中值滤波   ↓保存结果图像<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对应主函数：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span><span class="token keyword">char</span> <span class="token operator">*</span><span class="token operator">*</span>argv<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_in<span class="token operator">=</span> <span class="token string">"lena.pgm"</span><span class="token punctuation">;</span>    <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_out<span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span>    <span class="token function">TestReadImage</span><span class="token punctuation">(</span>file_in<span class="token punctuation">,</span>file_out<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>真正的处理在<code>TestReadImage</code>中完成</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> <span class="token function">TestReadImage</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_in<span class="token punctuation">,</span><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>file_out<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    Image <span class="token operator">*</span>image<span class="token punctuation">;</span>    Image <span class="token operator">*</span>outimage_average<span class="token punctuation">;</span>    Image <span class="token operator">*</span>outimage_median<span class="token punctuation">;</span>    image<span class="token operator">=</span><span class="token function">ReadPNMImage</span><span class="token punctuation">(</span>file_in<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//读取图像</span>    outimage_average<span class="token operator">=</span><span class="token function">AverageImage</span><span class="token punctuation">(</span>image<span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">//均值滤波</span>    outimage_median<span class="token operator">=</span><span class="token function">MedianImage</span><span class="token punctuation">(</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">//中值滤波</span>    <span class="token function">SavePNMImage</span><span class="token punctuation">(</span>outimage_average<span class="token punctuation">,</span><span class="token string">"lena_avg.pgm"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">SavePNMImage</span><span class="token punctuation">(</span>outimage_median<span class="token punctuation">,</span><span class="token string">"lena_med.pgm"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="程序整体结构"><a href="#程序整体结构" class="headerlink" title="程序整体结构"></a>程序整体结构</h2><p>整个项目中主要有几个函数：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">void</span> <span class="token function">SavePNMImage</span><span class="token punctuation">(</span>Image <span class="token operator">*</span><span class="token punctuation">,</span><span class="token keyword">char</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>Image <span class="token operator">*</span><span class="token function">AverageImage</span><span class="token punctuation">(</span>Image <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>Image <span class="token operator">*</span><span class="token function">MedianImage</span><span class="token punctuation">(</span>Image <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>Image <span class="token operator">*</span><span class="token function">ReadPNMImage</span><span class="token punctuation">(</span><span class="token keyword">char</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>Image <span class="token operator">*</span><span class="token function">CreateNewImage</span><span class="token punctuation">(</span>Image <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">char</span> <span class="token operator">*</span>comment<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>ReadPNMImage</td><td>读取 PGM&#x2F;PPM 图像</td></tr><tr><td>CreateNewImage</td><td>创建输出图像</td></tr><tr><td>AverageImage</td><td>均值滤波</td></tr><tr><td>MedianImage</td><td>中值滤波</td></tr><tr><td>SavePNMImage</td><td>保存图像</td></tr></tbody></table><ul><li><code>ReadPNMImage()</code>负责把图像读取到内存。</li><li><code>CreateNewImage()</code>负责创建输出图像，避免修改原图。<code>outimage-&gt;data</code>可以单独保存结果，算法独立，即使得多种滤波可同时进行，方便对比效果，避免污染数据。</li><li><code>SavePNMImage()</code>负责将结果保存为新的 PGM 文件。</li></ul><p>这些函数主要属于：<strong>图像输入输出模块</strong> </p><h2 id="图像的储存"><a href="#图像的储存" class="headerlink" title="图像的储存"></a>图像的储存</h2><p>程序中的图像数据：<code>unsigned char *data</code>实际上是一维数组，但图像本质上是二维的，因此需要进行二维转一维：<code>index = width * i + j</code></p><h2 id="均值滤波（Average-Filter）"><a href="#均值滤波（Average-Filter）" class="headerlink" title="均值滤波（Average Filter）"></a>均值滤波（Average Filter）</h2><h3 id="算法"><a href="#算法" class="headerlink" title="算法"></a>算法</h3><ul><li>对图像中每个像素（除边缘外），用自身和周围8个像素（共 9 个）的平均值替换原像素值。</li><li>公式$$ Pixel(i,j)&#x3D;\frac{1}{9}\sum_{x&#x3D;1}^{1}\sum_{y&#x3D;-1}^{1}origina(i+x,i+y) $$</li><li>边缘像素直接忽略（不处理）。</li></ul><p>结果：对lena.pgm处理后，图像变平滑（模糊）。</p><h3 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h3><ul><li><strong>优点</strong>：实现简单，计算速度快，能有效抑制<strong>高斯噪声</strong>。</li><li><strong>缺点</strong>：在去噪的同时会<strong>模糊图像的边缘和细节</strong>，使图像变得“朦胧”。</li></ul><h3 id="源代码"><a href="#源代码" class="headerlink" title="源代码"></a>源代码</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c">Image <span class="token operator">*</span><span class="token function">AverageImage</span><span class="token punctuation">(</span>Image <span class="token operator">*</span>image<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">unsigned</span> <span class="token keyword">char</span> <span class="token operator">*</span>tempin<span class="token punctuation">,</span><span class="token operator">*</span>tempout<span class="token punctuation">;</span>    Image <span class="token operator">*</span>outimage<span class="token punctuation">;</span>    <span class="token keyword">int</span> size<span class="token punctuation">;</span>    outimage<span class="token operator">=</span><span class="token function">CreateNewImage</span><span class="token punctuation">(</span>image<span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token string">"#testing Average Filter"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//保存滤波结果，不破坏原图</span>    tempin<span class="token operator">=</span>image<span class="token operator">-></span>data<span class="token punctuation">;</span>    tempout<span class="token operator">=</span>outimage<span class="token operator">-></span>data<span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Type<span class="token operator">==</span>GRAY<span class="token punctuation">)</span>size<span class="token operator">=</span>image<span class="token operator">-></span>Width <span class="token operator">*</span> image<span class="token operator">-></span>Height<span class="token punctuation">;</span>    <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Type<span class="token operator">==</span>COLOR<span class="token punctuation">)</span>size<span class="token operator">=</span>image<span class="token operator">-></span>Width <span class="token operator">*</span> image<span class="token operator">-></span>Height <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span>image<span class="token operator">-></span>Height<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator">&lt;</span>image<span class="token operator">-></span>Width<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            <span class="token keyword">int</span> sum<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>            <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>x<span class="token operator">&lt;=</span><span class="token number">1</span><span class="token punctuation">;</span>x<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> y<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>y<span class="token operator">&lt;=</span><span class="token number">1</span><span class="token punctuation">;</span>y<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                    sum<span class="token operator">+=</span>tempin<span class="token punctuation">[</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Width<span class="token punctuation">)</span><span class="token operator">*</span><span class="token punctuation">(</span>i<span class="token operator">+</span>x<span class="token punctuation">)</span><span class="token operator">+</span><span class="token punctuation">(</span>j<span class="token operator">+</span>y<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span>            <span class="token punctuation">&#125;</span>            tempout<span class="token punctuation">[</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Width<span class="token punctuation">)</span><span class="token operator">*</span>i<span class="token operator">+</span>j<span class="token punctuation">]</span><span class="token operator">=</span>sum<span class="token operator">/</span><span class="token number">9</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span><span class="token punctuation">(</span>outimage<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="外层循环"><a href="#外层循环" class="headerlink" title="外层循环"></a>外层循环</h4><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span>image<span class="token operator">-></span>Height<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator">&lt;</span>image<span class="token operator">-></span>Width<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>遍历图像中的每一个像素。</p><p>之所以从 1 开始，到 Height-1 结束：是为了避免访问越界。因为需要访问：</p><p><code>(i-1,j-1) (i+1,j+1)</code></p><p>边界像素无法完整获得 3×3 邻域。</p><h3 id="均值滤波的问题"><a href="#均值滤波的问题" class="headerlink" title="均值滤波的问题"></a>均值滤波的问题</h3><p>均值滤波虽然简单，但有一个非常明显的问题：<strong>会让图像变模糊</strong></p><p>因为：它会把周围像素“平均化”，边缘细节会丢失。</p><h2 id="中值滤波（Median-Filter）"><a href="#中值滤波（Median-Filter）" class="headerlink" title="中值滤波（Median Filter）"></a>中值滤波（Median Filter）</h2><h3 id="算法-1"><a href="#算法-1" class="headerlink" title="算法"></a>算法</h3><ul><li>与均值滤波类似，但替换为9个像素的中位数（中间值）。</li><li>实现方法：将 9 个像素值存入数组，用插入排序排序后取 <code>array[4]</code>作为中值。</li></ul><h3 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a>特点</h3><ul><li><strong>优点</strong>：对 <strong>椒盐噪声</strong>（随机出现的黑&#x2F;白点）抑制效果极佳，同时能够 <strong>较好地保留边缘信息</strong> 。</li><li><strong>缺点</strong>：对高斯噪声的去除能力不如均值滤波，且排序算法会增加计算量。</li></ul><h3 id="源代码-1"><a href="#源代码-1" class="headerlink" title="源代码"></a>源代码</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c">Image <span class="token operator">*</span><span class="token function">MedianImage</span><span class="token punctuation">(</span>Image <span class="token operator">*</span>image<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">unsigned</span> <span class="token keyword">char</span> <span class="token operator">*</span>tempin<span class="token punctuation">,</span><span class="token operator">*</span>tempout<span class="token punctuation">,</span>arr<span class="token punctuation">[</span><span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">;</span>    Image <span class="token operator">*</span>outimage<span class="token punctuation">;</span>    <span class="token keyword">int</span> size<span class="token punctuation">;</span>    outimage<span class="token operator">=</span><span class="token function">CreateNewImage</span><span class="token punctuation">(</span>image<span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token string">"#testing Average Filter"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    tempin<span class="token operator">=</span>image<span class="token operator">-></span>data<span class="token punctuation">;</span>    tempout<span class="token operator">=</span>outimage<span class="token operator">-></span>data<span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Type <span class="token operator">==</span> GRAY<span class="token punctuation">)</span> size <span class="token operator">=</span> image<span class="token operator">-></span>Width <span class="token operator">*</span> image<span class="token operator">-></span>Height<span class="token punctuation">;</span>    <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Type <span class="token operator">==</span> COLOR<span class="token punctuation">)</span> size <span class="token operator">=</span> image<span class="token operator">-></span>Width <span class="token operator">*</span> image<span class="token operator">-></span>Height <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span>image<span class="token operator">-></span>Height<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator">&lt;</span>image<span class="token operator">-></span>Width<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            <span class="token keyword">int</span> num<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>            <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> x<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>x<span class="token operator">&lt;=</span><span class="token number">1</span><span class="token punctuation">;</span>x<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> y<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>y<span class="token operator">&lt;=</span><span class="token number">1</span><span class="token punctuation">;</span>y<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                    arr<span class="token punctuation">[</span>num<span class="token operator">++</span><span class="token punctuation">]</span><span class="token operator">=</span>tempin<span class="token punctuation">[</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Width<span class="token punctuation">)</span><span class="token operator">*</span><span class="token punctuation">(</span>i<span class="token operator">+</span>x<span class="token punctuation">)</span><span class="token operator">+</span>j<span class="token operator">+</span>y<span class="token punctuation">]</span><span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span>            <span class="token punctuation">&#125;</span>            <span class="token comment">//插值排序</span>            <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> m<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>m<span class="token operator">&lt;</span><span class="token number">9</span><span class="token punctuation">;</span>m<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                <span class="token keyword">int</span> currNum<span class="token operator">=</span>arr<span class="token punctuation">[</span>m<span class="token punctuation">]</span><span class="token punctuation">;</span>                <span class="token keyword">int</span> n<span class="token operator">=</span>m<span class="token punctuation">;</span>                <span class="token keyword">while</span><span class="token punctuation">(</span>n<span class="token operator">>=</span><span class="token number">1</span><span class="token operator">&amp;&amp;</span>arr<span class="token punctuation">[</span>n<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token operator">></span>currNum<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                    arr<span class="token punctuation">[</span>n<span class="token punctuation">]</span><span class="token operator">=</span>arr<span class="token punctuation">[</span>n<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>                    n<span class="token operator">--</span><span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span>                arr<span class="token punctuation">[</span>n<span class="token punctuation">]</span><span class="token operator">=</span>currNum<span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>            tempout<span class="token punctuation">[</span><span class="token punctuation">(</span>image<span class="token operator">-></span>Width<span class="token punctuation">)</span><span class="token operator">*</span>i<span class="token operator">+</span>j<span class="token punctuation">]</span><span class="token operator">=</span>arr<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span><span class="token punctuation">(</span>outimage<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h3><p>其核心思想是：每次取一个元素，插入到前面已经有序的序列中。</p><h2 id="实验对比"><a href="#实验对比" class="headerlink" title="实验对比"></a>实验对比</h2><p>我们使用标准的 <a href="gitcode.com/open-source-toolkit/93a09">Lena 灰度图像</a>进行测试。因为下载到的是<code>bpm</code>和<code>jpg</code>格式，这里为了方便起见，我用<code>ffmpeg</code>将其转化成512×512的PGM格式</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">ffmpeg -i lena.bmp lena.pgm<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>分别应用均值滤波和中值滤波，得到以下结果。</p><p>原始图像：<img src="lena.png" alt="Lena原始图" loading="lazy"></p><p>均值滤波效果：<img src="lena_avg.png" alt="均值滤波" loading="lazy"></p><p>中值滤波效果：<img src="lena_med.png" alt="中值滤波" loading="lazy"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文基于 C 语言实现了：</p><ul><li>PGM 图像读取</li><li>均值滤波</li><li>中值滤波</li><li>图像保存</li></ul><p>后续还会有图像旋转（以参数标识旋转角度）以及X方向连续数字、Y方向连续数字、对角线方向连续数字等模式等对图像的几何变换处理。</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/c/">c</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/23/%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%E4%B8%AD%E7%9A%84%E5%9B%BE%E5%83%8F%E6%BB%A4%E6%B3%A2/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>基于BFS实现迷宫求解</title>
      <link>https://level0-jj.github.io/2026/05/21/%E5%9F%BA%E4%BA%8EBFS%E5%AE%9E%E7%8E%B0%E8%BF%B7%E5%AE%AB%E6%B1%82%E8%A7%A3/</link>
      <guid>https://level0-jj.github.io/2026/05/21/%E5%9F%BA%E4%BA%8EBFS%E5%AE%9E%E7%8E%B0%E8%BF%B7%E5%AE%AB%E6%B1%82%E8%A7%A3/</guid>
      <pubDate>Thu, 21 May 2026 13:21:50 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;在学习算法的过程中，广度优先搜索（BFS）是一个非常经典且实用的算法。今天我想分享一个使用C语言实现迷宫最短路径的例子，并附上完整代码。最好</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在学习算法的过程中，广度优先搜索（BFS）是一个非常经典且实用的算法。今天我想分享一个使用C语言实现迷宫最短路径的例子，并附上完整代码。最好用循环队列，防止假溢出，这里先使用普通数组队列。</p><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>假设我们有一个 <strong>n x n</strong> 的迷宫矩阵，矩阵中的 <code>1</code> 表示可走的路径，<code>0</code> 表示障碍。我们希望从起点 <code>(0,0)</code> 出发，找到终点 <code>(n-1,n-1)</code> 的最短路径，并打印出来。</p><p>示例迷宫：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">1 0 0 0 01 1 1 0 00 0 1 0 01 1 1 1 00 0 0 1 1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="BFS算法思路"><a href="#BFS算法思路" class="headerlink" title="BFS算法思路"></a>BFS算法思路</h2><p>BFS 的核心思想是 一层一层地扩展搜索，这样可以保证第一次到达终点时路径长度最短。实现步骤如下：</p><ul><li>使用队列存储待访问的节点。</li><li>用<code>visited</code>数组标记访问过的节点。</li><li>使用<code>parent</code>数组记录每个节点的前驱节点。</li><li>当到达终点时，通过回溯<code>parent</code>数组打印完整路径。</li></ul><h2 id="代码结构拆解"><a href="#代码结构拆解" class="headerlink" title="代码结构拆解"></a>代码结构拆解</h2><p>我们的代码可以拆解成以下几个模块：</p><ol><li>全局变量和结构体定义</li><li>BFS函数实现</li><li>路径回溯</li><li>主函数初始化与调用</li></ol><h3 id="全局变量和结构体定义"><a href="#全局变量和结构体定义" class="headerlink" title="全局变量和结构体定义"></a>全局变量和结构体定义</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">MAX_VERTICES</span> <span class="token expression"><span class="token number">100</span></span></span><span class="token keyword">int</span> visited<span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token punctuation">;</span>   <span class="token comment">// 标记节点是否已访问</span><span class="token keyword">int</span> queue<span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token punctuation">;</span>     <span class="token comment">// BFS队列</span><span class="token keyword">int</span> parent<span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token comment">// 记录每个节点的前驱节点</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> x<span class="token punctuation">,</span> y<span class="token punctuation">;</span><span class="token punctuation">&#125;</span> Point<span class="token punctuation">;</span> <span class="token comment">// 坐标结构体</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li><code>visited</code>防止重复访问节点，保证BFS正确性。</li><li><code>queue</code>保存带访问节点、保证BFS的层次顺序。</li><li><code>parent</code>用于回溯路径。</li><li><code>Point</code>结构体让代码更清晰。</li></ul><h3 id="BFS函数实现"><a href="#BFS函数实现" class="headerlink" title="BFS函数实现"></a>BFS函数实现</h3><p>BFS核心代码如下</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">void</span> <span class="token function">BFS</span><span class="token punctuation">(</span><span class="token keyword">int</span> maze<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token keyword">int</span> n<span class="token punctuation">,</span>Point start<span class="token punctuation">,</span>Point end<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> front <span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>   <span class="token comment">//</span>    <span class="token keyword">int</span> rear <span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>    queue<span class="token punctuation">[</span>rear<span class="token operator">++</span><span class="token punctuation">]</span><span class="token operator">=</span>start<span class="token punctuation">.</span>x<span class="token operator">*</span>n<span class="token operator">+</span>start<span class="token punctuation">.</span>y<span class="token punctuation">;</span>    visited<span class="token punctuation">[</span>start<span class="token punctuation">.</span>x<span class="token operator">*</span>n<span class="token operator">+</span>start<span class="token punctuation">.</span>y<span class="token punctuation">]</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>    parent<span class="token punctuation">[</span>start<span class="token punctuation">.</span>x<span class="token operator">*</span>n<span class="token operator">+</span>start<span class="token punctuation">.</span>y<span class="token punctuation">]</span><span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span>front<span class="token operator">&lt;</span>rear<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token keyword">int</span> current<span class="token operator">=</span>queue<span class="token punctuation">[</span>front<span class="token operator">++</span><span class="token punctuation">]</span><span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>current<span class="token operator">/</span>n <span class="token operator">==</span> end<span class="token punctuation">.</span>x <span class="token operator">&amp;&amp;</span> current<span class="token operator">%</span>n <span class="token operator">==</span> end<span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Successfully find the road!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>节点编码为 <code>index = x * n + y</code>，方便存储在一维数组。</p><h3 id="回溯路径"><a href="#回溯路径" class="headerlink" title="回溯路径"></a>回溯路径</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> path<span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> step<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> temp <span class="token operator">=</span> current<span class="token punctuation">;</span>            <span class="token keyword">while</span> <span class="token punctuation">(</span>temp <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>                path<span class="token punctuation">[</span>step<span class="token operator">++</span><span class="token punctuation">]</span> <span class="token operator">=</span> temp<span class="token punctuation">;</span>                temp <span class="token operator">=</span> parent<span class="token punctuation">[</span>temp<span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>            <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"The road is:\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span>step<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">>=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">--</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                <span class="token keyword">int</span> px<span class="token operator">=</span>path<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token operator">/</span>n<span class="token punctuation">;</span>                <span class="token keyword">int</span> py<span class="token operator">=</span>path<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token operator">%</span>n<span class="token punctuation">;</span>                <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"(%d,%d)"</span><span class="token punctuation">,</span>px<span class="token punctuation">,</span>py<span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token keyword">if</span><span class="token punctuation">(</span>i<span class="token operator">></span><span class="token number">0</span><span class="token punctuation">)</span><span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"->"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>            <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token keyword">return</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li>从终点开始，沿<code>parent</code>数组回溯到起点。</li><li>存储到<code>path</code>数组。</li><li>逆序打印</li></ul><h3 id="扩展邻居节点"><a href="#扩展邻居节点" class="headerlink" title="扩展邻居节点"></a>扩展邻居节点</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> moves<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token punctuation">&#123;</span>            <span class="token punctuation">&#123;</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>            <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>            <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>            <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span><span class="token number">4</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            <span class="token keyword">int</span> newX<span class="token operator">=</span>current<span class="token operator">/</span>n<span class="token operator">+</span>moves<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">int</span> newY<span class="token operator">=</span>current<span class="token operator">%</span>n<span class="token operator">+</span>moves<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>            <span class="token keyword">if</span><span class="token punctuation">(</span>newX<span class="token operator">>=</span><span class="token number">0</span><span class="token operator">&amp;&amp;</span>newY<span class="token operator">>=</span><span class="token number">0</span><span class="token operator">&amp;&amp;</span>newX<span class="token operator">&lt;</span>n<span class="token operator">&amp;&amp;</span>newY<span class="token operator">&lt;</span>n<span class="token operator">&amp;&amp;</span><span class="token operator">!</span>visited<span class="token punctuation">[</span>newX<span class="token operator">*</span>n<span class="token operator">+</span>newY<span class="token punctuation">]</span><span class="token operator">&amp;&amp;</span>maze<span class="token punctuation">[</span>newX<span class="token punctuation">]</span><span class="token punctuation">[</span>newY<span class="token punctuation">]</span><span class="token operator">==</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                <span class="token keyword">if</span><span class="token punctuation">(</span>rear <span class="token operator">>=</span> MAX_VERTICES<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Queue Overflow!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                    <span class="token keyword">return</span><span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span>                queue<span class="token punctuation">[</span>rear<span class="token operator">++</span><span class="token punctuation">]</span><span class="token operator">=</span>newX<span class="token operator">*</span>n<span class="token operator">+</span>newY<span class="token punctuation">;</span>                visited<span class="token punctuation">[</span>newX<span class="token operator">*</span>n<span class="token operator">+</span>newY<span class="token punctuation">]</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>                parent<span class="token punctuation">[</span>newX<span class="token operator">*</span>n<span class="token operator">+</span>newY<span class="token punctuation">]</span> <span class="token operator">=</span> current<span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="主函数"><a href="#主函数" class="headerlink" title="主函数"></a>主函数</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> n<span class="token operator">=</span><span class="token number">6</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> maze<span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token punctuation">[</span>MAX_VERTICES<span class="token punctuation">]</span><span class="token operator">=</span><span class="token punctuation">&#123;</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span>n<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>j<span class="token operator">&lt;</span>n<span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        visited<span class="token punctuation">[</span>i<span class="token operator">*</span>n<span class="token operator">+</span>j<span class="token punctuation">]</span><span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>        parent<span class="token punctuation">[</span>i<span class="token operator">*</span>n<span class="token operator">+</span>j<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token function">BFS</span><span class="token punctuation">(</span>maze<span class="token punctuation">,</span>n<span class="token punctuation">,</span><span class="token punctuation">(</span>Point<span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span><span class="token punctuation">(</span>Point<span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token number">5</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="运行结果"><a href="#运行结果" class="headerlink" title="运行结果"></a>运行结果</h3><pre class="line-numbers language-text" data-language="text"><code class="language-text">Successfully find the road!The road is:(0,0)->(1,0)->(2,0)->(2,1)->(2,2)->(3,2)->(4,2)->(4,3)->(4,4)->(4,5)->(5,5)Running time:1.000 ms<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li><code>BFS</code> 是按层搜索，保证第一次到达终点就是最短路径</li><li><code>queue</code> 保证访问顺序，<code>visited</code> 防止重复访问。</li><li><code>parent</code> 用于路径回溯，是打印路径的关键。</li><li>节点编码 <code>(x*n+y)</code> 方便存储和回溯。</li><li>可以扩展八方向、障碍更多、迷宫更大，或者改成权重图使用 <code>Dijkstra</code>。</li></ul><p>之后可能更新更多的东西（挖坑中）敬请期待……</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/c/">c</category>
      
      <category domain="https://level0-jj.github.io/tags/BFS/">BFS</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/21/%E5%9F%BA%E4%BA%8EBFS%E5%AE%9E%E7%8E%B0%E8%BF%B7%E5%AE%AB%E6%B1%82%E8%A7%A3/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>基于DFS实现迷宫求解</title>
      <link>https://level0-jj.github.io/2026/05/17/%E5%9F%BA%E4%BA%8EDFS%E5%AE%9E%E7%8E%B0%E8%BF%B7%E5%AE%AB%E6%B1%82%E8%A7%A3/</link>
      <guid>https://level0-jj.github.io/2026/05/17/%E5%9F%BA%E4%BA%8EDFS%E5%AE%9E%E7%8E%B0%E8%BF%B7%E5%AE%AB%E6%B1%82%E8%A7%A3/</guid>
      <pubDate>Sun, 17 May 2026 02:37:38 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;要求&quot;&gt;&lt;a href=&quot;#要求&quot; class=&quot;headerlink&quot; title=&quot;要求&quot;&gt;&lt;/a&gt;要求&lt;/h2&gt;&lt;p&gt;封面、目录、每个方法（算法原理说明、代码、运行结果截图）以及收获体会。&lt;/p&gt;
&lt;p&gt;打印运行时间&lt;/p&gt;
&lt;h2 id=&quot;算法原理说明&quot;&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="要求"><a href="#要求" class="headerlink" title="要求"></a>要求</h2><p>封面、目录、每个方法（算法原理说明、代码、运行结果截图）以及收获体会。</p><p>打印运行时间</p><h2 id="算法原理说明"><a href="#算法原理说明" class="headerlink" title="算法原理说明"></a>算法原理说明</h2><h3 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h3><p> <strong>栈(stack)</strong> 是一种特殊的线性表,是一种只允许在表的一端进行插入或删除操作的线性表。表中允许进行插入和删除操作的一端称为栈顶,最下面的那一端称为栈底。栈顶是动态的,它由一个称为栈顶指针的位置指示器指示。当栈中没有数据元素时,称为空栈。</p><p>栈的主要特点是 <strong>“后进先出”</strong> ,即后进栈的元素先处理。因此栈又称为后进先出(LastInFirstOut,LIFO)表。</p><p>迷宫探路问题中的若干可通位置形成的通路构成了栈,对栈的运算主要有以下几种。</p><ul><li>初始化栈<code>initStack(&amp;s)</code></li><li>入栈<code>push(&amp;s,Box)</code></li><li>出栈<code>pop(&amp;s)</code></li><li>读栈顶元素<code>peek(&amp;s)</code></li><li>判断栈满<code>isFull</code></li><li>判断栈空<code>isEmpty</code></li></ul><h3 id="DFS"><a href="#DFS" class="headerlink" title="DFS"></a>DFS</h3><p> <strong>深度优先搜索（DFS）</strong> 是一种用于遍历或搜索树或图的算法。在迷宫生成的上下文中，深度优先搜索可以用来探索从起点到终点的路径。其思想是从起点开始，向一个方向尽可能深入地走，直到无法继续为止（即到达死胡同），然后回溯到上一个岔路口，继续探索其他路径，直到所有的路径都已遍历。</p><p>深度优先搜索迷宫生成的特点是相对简单易实现，并且生成的迷宫通常具有长而窄的路径特征，适合于需要用户深思熟虑的迷宫游戏。</p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><h4 id="迷宫的打印"><a href="#迷宫的打印" class="headerlink" title="迷宫的打印"></a>迷宫的打印</h4><p>先不谈具体算法，首先我们需要实现迷宫的物理存储，可以采用二维数组来存储一个二维平面地图，使用0表示是通路，1表示是墙这里我们先自己创建一个，之后再用递归等算法自动创建迷宫。</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> maze<span class="token punctuation">[</span>ROW<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">[</span>COL<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token punctuation">&#123;</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里四周多出一圈1，是为了在每一个方向上都有四个方向可以选。</p><h4 id="定义栈的结构"><a href="#定义栈的结构" class="headerlink" title="定义栈的结构"></a>定义栈的结构</h4><p>我们用一个结构体来封装栈：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">MAX_STACK</span> <span class="token expression"><span class="token number">100</span></span></span><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> x<span class="token punctuation">;</span>          <span class="token comment">// 行坐标</span>    <span class="token keyword">int</span> y<span class="token punctuation">;</span>          <span class="token comment">// 列坐标</span>    <span class="token keyword">int</span> dir<span class="token punctuation">;</span>        <span class="token comment">// 方向（0~3 或 -1 表示未尝试）</span><span class="token punctuation">&#125;</span> Box<span class="token punctuation">;</span>              <span class="token comment">// 栈中存放的元素</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>    Box data<span class="token punctuation">[</span>MAX_STACK<span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> top<span class="token punctuation">;</span>        <span class="token comment">// 栈顶索引，-1 表示空栈</span><span class="token punctuation">&#125;</span> Stack<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="栈的基本操作"><a href="#栈的基本操作" class="headerlink" title="栈的基本操作"></a>栈的基本操作</h4><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">//初始化栈</span><span class="token keyword">void</span> <span class="token function">initStack</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    s<span class="token operator">-></span>top<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//判空</span>bool <span class="token function">isEmpty</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">return</span> s<span class="token operator">-></span>top<span class="token operator">==</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//判满</span>bool <span class="token function">isFull</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">return</span> s<span class="token operator">-></span>top<span class="token operator">==</span>MAX_STACK<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//压入</span><span class="token keyword">void</span> <span class="token function">push</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">,</span>Box b<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isFull</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        s<span class="token operator">-></span>top<span class="token operator">++</span><span class="token punctuation">;</span>        s<span class="token operator">-></span>data<span class="token punctuation">[</span>s<span class="token operator">-></span>top<span class="token punctuation">]</span><span class="token operator">=</span>b<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token comment">//弹出并返回</span>Box <span class="token function">pop</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    Box empty<span class="token operator">=</span><span class="token punctuation">&#123;</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">return</span> empty<span class="token punctuation">;</span>    Box b<span class="token operator">=</span>s<span class="token operator">-></span>data<span class="token punctuation">[</span>s<span class="token operator">-></span>top<span class="token punctuation">]</span><span class="token punctuation">;</span>    s<span class="token operator">-></span>top<span class="token operator">--</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> b<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="方向数组"><a href="#方向数组" class="headerlink" title="方向数组"></a>方向数组</h4><p>用结构体存储方向向量，配合循环尝试四个方向：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> incX<span class="token punctuation">;</span>   <span class="token comment">// 行增量</span>    <span class="token keyword">int</span> incY<span class="token punctuation">;</span>   <span class="token comment">// 列增量</span><span class="token punctuation">&#125;</span> Direction<span class="token punctuation">;</span>Direction direct<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">&#123;</span>    <span class="token punctuation">&#123;</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>   <span class="token comment">// 上</span>    <span class="token punctuation">&#123;</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>   <span class="token comment">// 下</span>    <span class="token punctuation">&#123;</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>  <span class="token comment">// 左</span>    <span class="token punctuation">&#123;</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">&#125;</span>    <span class="token comment">// 右</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="DFS寻路逻辑"><a href="#DFS寻路逻辑" class="headerlink" title="DFS寻路逻辑"></a>DFS寻路逻辑</h4><ul><li>将起点入栈，并标记为已访问（<code>maze[x][y] = -1</code>）。</li><li>循环直到栈空：<ol><li>弹出栈顶元素作为当前考察点 <code>(x, y)</code>。</li><li>从 <code>di</code> 记录的下一个方向开始尝试（<code>di = cur.di + 1</code>）。</li><li>依次尝试四个方向：<ul><li>计算邻居坐标 <code>(nx, ny)</code>。</li><li>若邻居可通（<code>maze[nx][ny] == 0</code>）：<ul><li>将当前点（更新方向索引为 <code>di</code>）压回栈，以便以后回溯。</li><li>移动到邻居点，标记已访问。</li><li>将邻居点（方向索引 <code>-1</code>）压入栈。</li><li>若邻居是终点，则成功返回。</li><li>否则跳出方向循环，下一次循环直接处理栈顶（邻居点）。</li></ul></li><li>若不可通，则 <code>di++</code> 继续尝试下一个方向。</li></ul></li><li>如果四个方向都试完仍无通路，则当前点被丢弃（未重新压回），自然实现回溯</li></ol></li></ul><p>代码：</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">//寻路函数</span>bool <span class="token function">findPath</span><span class="token punctuation">(</span><span class="token keyword">int</span> maze<span class="token punctuation">[</span>ROW<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">[</span>COL<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span>Direction direct<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    Box temp<span class="token punctuation">;</span>    <span class="token keyword">int</span> x<span class="token punctuation">,</span>y<span class="token punctuation">,</span>di<span class="token punctuation">;</span>    <span class="token keyword">int</span> line<span class="token punctuation">,</span>col<span class="token punctuation">;</span><span class="token comment">//下一步的坐标</span>    maze<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>    temp<span class="token operator">=</span><span class="token punctuation">(</span>Box<span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>    <span class="token function">push</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span>temp<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        temp<span class="token operator">=</span><span class="token function">pop</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">;</span>        x<span class="token operator">=</span>temp<span class="token punctuation">.</span>x<span class="token punctuation">;</span>        y<span class="token operator">=</span>temp<span class="token punctuation">.</span>y<span class="token punctuation">;</span>        di<span class="token operator">=</span>temp<span class="token punctuation">.</span>di<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span>        <span class="token keyword">while</span><span class="token punctuation">(</span>di<span class="token operator">&lt;</span><span class="token number">4</span><span class="token punctuation">)</span>        <span class="token punctuation">&#123;</span>            line<span class="token operator">=</span>x<span class="token operator">+</span>direct<span class="token punctuation">[</span>di<span class="token punctuation">]</span><span class="token punctuation">.</span>incX<span class="token punctuation">;</span>            col<span class="token operator">=</span>y<span class="token operator">+</span>direct<span class="token punctuation">[</span>di<span class="token punctuation">]</span><span class="token punctuation">.</span>incY<span class="token punctuation">;</span>            <span class="token keyword">if</span><span class="token punctuation">(</span>maze<span class="token punctuation">[</span>line<span class="token punctuation">]</span><span class="token punctuation">[</span>col<span class="token punctuation">]</span><span class="token operator">==</span><span class="token number">0</span><span class="token punctuation">)</span>            <span class="token punctuation">&#123;</span>                temp<span class="token operator">=</span><span class="token punctuation">(</span>Box<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>x<span class="token punctuation">,</span>y<span class="token punctuation">,</span>di<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>                <span class="token function">push</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span>temp<span class="token punctuation">)</span><span class="token punctuation">;</span>                x<span class="token operator">=</span>line<span class="token punctuation">;</span>                y<span class="token operator">=</span>col<span class="token punctuation">;</span>                maze<span class="token punctuation">[</span>x<span class="token punctuation">]</span><span class="token punctuation">[</span>y<span class="token punctuation">]</span><span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>                <span class="token keyword">if</span><span class="token punctuation">(</span>x<span class="token operator">==</span>ROW<span class="token operator">&amp;&amp;</span>y<span class="token operator">==</span>COL<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                    <span class="token function">push</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span><span class="token punctuation">(</span>Box<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>x<span class="token punctuation">,</span>y<span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                    <span class="token keyword">return</span> true<span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span><span class="token keyword">else</span> <span class="token punctuation">&#123;</span>                        di<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>                    <span class="token punctuation">&#125;</span>            <span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>                di<span class="token operator">++</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>     <span class="token keyword">return</span> false<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="完整代码示例"><a href="#完整代码示例" class="headerlink" title="完整代码示例"></a>完整代码示例</h4><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">//迷宫问题DFS</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdbool.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;time.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">ROW</span> <span class="token expression"><span class="token number">6</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">COL</span> <span class="token expression"><span class="token number">6</span></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">MAX_STACK</span> <span class="token expression"><span class="token number">100</span></span></span><span class="token comment">//方向结构体，用于存储每个方向移动的值</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span><span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> incX<span class="token punctuation">;</span>    <span class="token keyword">int</span> incY<span class="token punctuation">;</span><span class="token punctuation">&#125;</span>Direction<span class="token punctuation">;</span><span class="token comment">//当前位置</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span><span class="token punctuation">&#123;</span>    <span class="token keyword">int</span> x<span class="token punctuation">;</span>    <span class="token keyword">int</span> y<span class="token punctuation">;</span>    <span class="token keyword">int</span> di<span class="token punctuation">;</span><span class="token punctuation">&#125;</span>Box<span class="token punctuation">;</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span><span class="token punctuation">&#123;</span>    Box data<span class="token punctuation">[</span>MAX_STACK<span class="token punctuation">]</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> top<span class="token punctuation">;</span>                <span class="token comment">//固定数组+移动的top=动态的栈顶指针</span><span class="token punctuation">&#125;</span>Stack<span class="token punctuation">;</span><span class="token comment">//栈的基本操作</span><span class="token comment">//初始化栈</span><span class="token keyword">void</span> <span class="token function">initStack</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    s<span class="token operator">-></span>top<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//判空</span>bool <span class="token function">isEmpty</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">return</span> s<span class="token operator">-></span>top<span class="token operator">==</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//判满</span>bool <span class="token function">isFull</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">return</span> s<span class="token operator">-></span>top<span class="token operator">==</span>MAX_STACK<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//压入</span><span class="token keyword">void</span> <span class="token function">push</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">,</span>Box b<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isFull</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        s<span class="token operator">-></span>top<span class="token operator">++</span><span class="token punctuation">;</span>        s<span class="token operator">-></span>data<span class="token punctuation">[</span>s<span class="token operator">-></span>top<span class="token punctuation">]</span><span class="token operator">=</span>b<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token comment">//弹出并返回</span>Box <span class="token function">pop</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    Box empty<span class="token operator">=</span><span class="token punctuation">&#123;</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">return</span> empty<span class="token punctuation">;</span>    Box b<span class="token operator">=</span>s<span class="token operator">-></span>data<span class="token punctuation">[</span>s<span class="token operator">-></span>top<span class="token punctuation">]</span><span class="token punctuation">;</span>    s<span class="token operator">-></span>top<span class="token operator">--</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> b<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//寻路函数</span>bool <span class="token function">findPath</span><span class="token punctuation">(</span><span class="token keyword">int</span> maze<span class="token punctuation">[</span>ROW<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">[</span>COL<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span>Direction direct<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    Box temp<span class="token punctuation">;</span>    <span class="token keyword">int</span> x<span class="token punctuation">,</span>y<span class="token punctuation">,</span>di<span class="token punctuation">;</span>    <span class="token keyword">int</span> line<span class="token punctuation">,</span>col<span class="token punctuation">;</span><span class="token comment">//下一步的坐标</span>    maze<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>    temp<span class="token operator">=</span><span class="token punctuation">(</span>Box<span class="token punctuation">)</span><span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>    <span class="token function">push</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span>temp<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        temp<span class="token operator">=</span><span class="token function">pop</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">;</span>        x<span class="token operator">=</span>temp<span class="token punctuation">.</span>x<span class="token punctuation">;</span>        y<span class="token operator">=</span>temp<span class="token punctuation">.</span>y<span class="token punctuation">;</span>        di<span class="token operator">=</span>temp<span class="token punctuation">.</span>di<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">;</span>        <span class="token keyword">while</span><span class="token punctuation">(</span>di<span class="token operator">&lt;</span><span class="token number">4</span><span class="token punctuation">)</span>        <span class="token punctuation">&#123;</span>            line<span class="token operator">=</span>x<span class="token operator">+</span>direct<span class="token punctuation">[</span>di<span class="token punctuation">]</span><span class="token punctuation">.</span>incX<span class="token punctuation">;</span>            col<span class="token operator">=</span>y<span class="token operator">+</span>direct<span class="token punctuation">[</span>di<span class="token punctuation">]</span><span class="token punctuation">.</span>incY<span class="token punctuation">;</span>            <span class="token keyword">if</span><span class="token punctuation">(</span>maze<span class="token punctuation">[</span>line<span class="token punctuation">]</span><span class="token punctuation">[</span>col<span class="token punctuation">]</span><span class="token operator">==</span><span class="token number">0</span><span class="token punctuation">)</span>            <span class="token punctuation">&#123;</span>                temp<span class="token operator">=</span><span class="token punctuation">(</span>Box<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>x<span class="token punctuation">,</span>y<span class="token punctuation">,</span>di<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>                <span class="token function">push</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span>temp<span class="token punctuation">)</span><span class="token punctuation">;</span>                x<span class="token operator">=</span>line<span class="token punctuation">;</span>                y<span class="token operator">=</span>col<span class="token punctuation">;</span>                maze<span class="token punctuation">[</span>x<span class="token punctuation">]</span><span class="token punctuation">[</span>y<span class="token punctuation">]</span><span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>                <span class="token keyword">if</span><span class="token punctuation">(</span>x<span class="token operator">==</span>ROW<span class="token operator">&amp;&amp;</span>y<span class="token operator">==</span>COL<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                    <span class="token function">push</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span><span class="token punctuation">(</span>Box<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>x<span class="token punctuation">,</span>y<span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                    <span class="token keyword">return</span> true<span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span><span class="token keyword">else</span> <span class="token punctuation">&#123;</span>                        di<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>                    <span class="token punctuation">&#125;</span>            <span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>                di<span class="token operator">++</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>     <span class="token keyword">return</span> false<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>   <span class="token comment">//打印输出路径</span><span class="token keyword">void</span> <span class="token function">printPath</span><span class="token punctuation">(</span>Stack <span class="token operator">*</span>s<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"The path is empty.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Path found (from start to end):\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">&lt;=</span>s<span class="token operator">-></span>top<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"(%d,%d)"</span><span class="token punctuation">,</span>s<span class="token operator">-></span>data<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>x<span class="token punctuation">,</span>s<span class="token operator">-></span>data<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>i<span class="token operator">&lt;</span>s<span class="token operator">-></span>top<span class="token punctuation">)</span><span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"->"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token class-name">clock_t</span> start<span class="token punctuation">,</span> end<span class="token punctuation">;</span>    <span class="token keyword">double</span> cpu_time_used<span class="token punctuation">;</span>    start <span class="token operator">=</span> <span class="token function">clock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// 记录开始时刻</span>    <span class="token keyword">int</span> maze<span class="token punctuation">[</span>ROW<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">[</span>COL<span class="token operator">+</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token punctuation">&#123;</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>Direction direct<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token punctuation">&#123;</span>    <span class="token punctuation">&#123;</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token comment">//上</span>    <span class="token punctuation">&#123;</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>  <span class="token comment">//下</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token comment">//左</span>    <span class="token punctuation">&#123;</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>  <span class="token comment">//右</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>Stack stack<span class="token punctuation">;</span><span class="token function">initStack</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>stack<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">findPath</span><span class="token punctuation">(</span>maze<span class="token punctuation">,</span>direct<span class="token punctuation">,</span><span class="token operator">&amp;</span>stack<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token function">printPath</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>stack<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Path not found!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span>end <span class="token operator">=</span> <span class="token function">clock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>     <span class="token comment">// 记录结束时刻</span>    cpu_time_used <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">/</span> CLOCKS_PER_SEC<span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"The running time is: %f s\n"</span><span class="token punctuation">,</span> cpu_time_used<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="运行结果与复杂度"><a href="#运行结果与复杂度" class="headerlink" title="运行结果与复杂度"></a>运行结果与复杂度</h2><p> <strong>时间复杂度</strong> ：每个格子最多被访问一次，O(M*N)。</p><p> <strong>空间复杂度</strong> ：栈对多存储一条路径上的格子，O(M+N)。</p><p> 对于6*6迷宫，程序 <strong>瞬间</strong> 输出路径，例如：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">Path found (from start to end):(1,1)->(2,1)->(3,1)->(3,2)->(3,3)->(2,3)->(1,3)->(1,4)->(1,5)->(1,6)->(2,6)->(3,6)->(3,5)->(4,5)->(5,5)->(5,6)->(6,6)The running time is: 0.000000 s<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="总结扩展"><a href="#总结扩展" class="headerlink" title="总结扩展"></a>总结扩展</h2><p>通过数组手写栈并实现非递归 DFS，我们不仅加深了对栈“后进先出”的理解，也掌握了一种经典的回溯算法实现方式。栈的运算虽然简单（<code>push</code>、<code>pop</code>、<code>peek</code>、<code>isEmpty</code>），但组合起来就能解决迷宫寻路、括号匹配、表达式求值等许多实际问题。</p><p>之后可以 <strong>用链表实现栈</strong> ，动态分配节点，没有固定容量限制， <strong>求最短路径</strong> ，将栈改为队列，DFS改为BFS。</p><p>如果还有时间的话，会将迷宫可视化做出来。</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/c/">c</category>
      
      <category domain="https://level0-jj.github.io/tags/BFS/">BFS</category>
      
      <category domain="https://level0-jj.github.io/tags/DFS/">DFS</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/17/%E5%9F%BA%E4%BA%8EDFS%E5%AE%9E%E7%8E%B0%E8%BF%B7%E5%AE%AB%E6%B1%82%E8%A7%A3/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>cpp操纵像素在CPU上跑Shader</title>
      <link>https://level0-jj.github.io/2026/05/12/cpp%E6%93%8D%E7%BA%B5%E5%83%8F%E7%B4%A0%E5%9C%A8CPU%E4%B8%8A%E8%B7%91Shader/</link>
      <guid>https://level0-jj.github.io/2026/05/12/cpp%E6%93%8D%E7%BA%B5%E5%83%8F%E7%B4%A0%E5%9C%A8CPU%E4%B8%8A%E8%B7%91Shader/</guid>
      <pubDate>Tue, 12 May 2026 14:15:41 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;来自 &lt;a href=&quot;https://x.com/tsoding/status/1989446257882829</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>来自 <a href="https://x.com/tsoding/status/1989446257882829223">Tsoding 的视频《Graphics API is irrelevant》</a></p></blockquote><p>Tsoding 的观点非常直白：<strong>如果你想做的只是把像素画到屏幕上，你根本不需要任何图形 API</strong>。CPU 可以直接计算出每个像素的颜色，然后以一种最简单的图像格式（比如 PPM）保存下来，再用任何图片查看器打开即可。</p><p>这就是“用 CPU 跑 Shader”的最原始形态：没有 GPU，没有着色器语言，只有 <code>for</code> 循环和 <code>fputc</code>。</p><h2 id="PPM-格式：比-TXT-还简单的图像格式"><a href="#PPM-格式：比-TXT-还简单的图像格式" class="headerlink" title="PPM 格式：比 TXT 还简单的图像格式"></a>PPM 格式：比 TXT 还简单的图像格式</h2><ul><li><strong>P6</strong>（二进制）：直接写入每个像素的 RGB 字节，文件更小，也更容易用代码生成。</li></ul><p>我们要用的就是 <strong>P6 二进制格式</strong>。它的头部只有三行：<strong>P6</strong>、<strong>宽度 高度</strong>、<strong>255</strong>。</p><p>紧接着就是 <code>宽度 × 高度 × 3</code> 个字节，按 <code>R, G, B, R, G, B, ...</code> 顺序排列。</p><h2 id="纯红色的-PPM"><a href="#纯红色的-PPM" class="headerlink" title="纯红色的 PPM"></a>纯红色的 PPM</h2><h3 id="代码展示"><a href="#代码展示" class="headerlink" title="代码展示"></a>代码展示</h3><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    FILE <span class="token operator">*</span>f <span class="token operator">=</span> <span class="token function">fopen</span><span class="token punctuation">(</span><span class="token string">"output.ppm"</span><span class="token punctuation">,</span> <span class="token string">"wb"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> w <span class="token operator">=</span> <span class="token number">16</span> <span class="token operator">*</span> <span class="token number">60</span><span class="token punctuation">;</span>   <span class="token comment">// 960</span>    <span class="token keyword">int</span> h <span class="token operator">=</span> <span class="token number">9</span> <span class="token operator">*</span> <span class="token number">60</span><span class="token punctuation">;</span>    <span class="token comment">// 540</span>    <span class="token comment">// PPM 头部（P6 二进制格式）</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> <span class="token string">"P6\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> <span class="token string">"%d %d\n"</span><span class="token punctuation">,</span> w<span class="token punctuation">,</span> h<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> <span class="token string">"255\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 像素数据：全部写成 R=255, G=0, B=0</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> h<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> w<span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0xFF</span><span class="token punctuation">,</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// R</span>            <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// G</span>            <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">// B</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    <span class="token function">fclose</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">g<span class="token operator">++</span> ppm<span class="token punctuation">.</span>c <span class="token operator">-</span>o ppm<span class="token punctuation">.</span>exe<span class="token punctuation">.</span>\ppm<span class="token punctuation">.</span>exe<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>运行之后会生成一个ppm文件，我用ffmpeg转成了jpg格式</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">ffmpeg <span class="token operator">-</span>i output<span class="token punctuation">.</span>ppm <span class="token operator">-</span>pix_fmt yuvj420p output<span class="token punctuation">.</span>jpg<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>只是一个纯红色的无聊图案（？</p><p><img src="output1.jpg" alt="示例" loading="lazy"></p><h2 id="一个棋盘格"><a href="#一个棋盘格" class="headerlink" title="一个棋盘格"></a>一个棋盘格</h2><p>当然我们可以通过判断当前索引是奇数还是偶数，决定当前像素的颜色</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i<span class="token operator">/</span><span class="token number">60</span><span class="token operator">+</span>j<span class="token operator">/</span><span class="token number">60</span><span class="token punctuation">)</span><span class="token operator">%</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>           <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0xFF</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>           <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>           <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>           <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>           <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>           <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这样就可以得到一个黑红相间的棋盘格</p><p><img src="output2.jpg" alt="棋盘格" loading="lazy"></p><h2 id="一个棋盘格视频"><a href="#一个棋盘格视频" class="headerlink" title="一个棋盘格视频"></a>一个棋盘格视频</h2><p>我们还可以让棋盘格移动，只需要做这样的修改</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">if</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i<span class="token operator">+</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token number">60</span><span class="token operator">+</span><span class="token punctuation">(</span>j<span class="token operator">+</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token number">60</span><span class="token punctuation">)</span><span class="token operator">%</span><span class="token number">2</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p> 编译运行之后的棋盘格就会在X和Y方向偏移</p><p> 然后，我们可以重复60次，将上述过程从0-60进行迭代，每一次迭代我们都创建一个单独的帧文件，共生成60个，代码如下</p> <pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;stdio.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token keyword">char</span> buf<span class="token punctuation">[</span><span class="token number">256</span><span class="token punctuation">]</span><span class="token punctuation">;</span>   <span class="token comment">//在栈上分配生成的帧文件名</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> k<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>k<span class="token operator">&lt;</span><span class="token number">60</span><span class="token punctuation">;</span>k<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token function">snprintf</span><span class="token punctuation">(</span>buf<span class="token punctuation">,</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">"output-%02d.ppm"</span><span class="token punctuation">,</span>k<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>output_path<span class="token operator">=</span>buf<span class="token punctuation">;</span>    FILE <span class="token operator">*</span>f<span class="token operator">=</span><span class="token function">fopen</span><span class="token punctuation">(</span>output_path<span class="token punctuation">,</span><span class="token string">"wb"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> w<span class="token operator">=</span><span class="token number">16</span><span class="token operator">*</span><span class="token number">60</span><span class="token punctuation">;</span>    <span class="token keyword">int</span> h<span class="token operator">=</span><span class="token number">9</span><span class="token operator">*</span><span class="token number">60</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span><span class="token string">"P6\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span><span class="token string">"%d %d\n"</span><span class="token punctuation">,</span>w<span class="token punctuation">,</span>h<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">fprintf</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span><span class="token string">"255\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span>h<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>             <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> j<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>j<span class="token operator">&lt;</span>w<span class="token punctuation">;</span>j<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                     <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i<span class="token operator">+</span>k<span class="token punctuation">)</span><span class="token operator">/</span><span class="token number">60</span><span class="token operator">+</span><span class="token punctuation">(</span>j<span class="token operator">+</span>k<span class="token punctuation">)</span><span class="token operator">/</span><span class="token number">60</span><span class="token punctuation">)</span><span class="token operator">%</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                     <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0xFF</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>                     <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>                     <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>                 <span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>                    <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>                    <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>                    <span class="token function">fputc</span><span class="token punctuation">(</span><span class="token number">0x00</span><span class="token punctuation">,</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token punctuation">&#125;</span>               <span class="token punctuation">&#125;</span>        <span class="token punctuation">&#125;</span>     <span class="token function">fclose</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Generated %s\n"</span><span class="token punctuation">,</span>output_path<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后在终端进行编译</p><pre class="line-numbers language-none"><code class="language-none">ffmpeg -framerate 30 -i output-%02d.ppm -c:v libx264 -pix_fmt yuv420p output.mp4<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>即可看见视频</p><p><video src="cpp操纵像素在CPU上跑Shader/output.mp4" controls width="100%"></video></p><h2 id="纯红色到任意“CPU-Shader”"><a href="#纯红色到任意“CPU-Shader”" class="headerlink" title="纯红色到任意“CPU Shader”"></a>纯红色到任意“CPU Shader”</h2><p>生成纯红色当然没什么用，但你可以替换内层循环里的 <code>fputc</code>逻辑——比如根据 <code>(i, j)</code> 坐标计算渐变色、画圆、画棋盘格，甚至实现分形、噪声、光线步进（Ray Marching）……所有这些都不需要任何图形 API，纯粹是 CPU 上对每个像素执行一个数学函数。</p><p>同时更进一步的，如果要动画，可以在外层再加一维时间 t，每帧生成一个 PPM，然后用 FFmpeg 合成视频。</p><p>以上本人因时间问题还未实现（挖坑中）</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/cpp/">cpp</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/12/cpp%E6%93%8D%E7%BA%B5%E5%83%8F%E7%B4%A0%E5%9C%A8CPU%E4%B8%8A%E8%B7%91Shader/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>终端typing_game</title>
      <link>https://level0-jj.github.io/2026/05/12/%E7%BB%88%E7%AB%AFtyping-game/</link>
      <guid>https://level0-jj.github.io/2026/05/12/%E7%BB%88%E7%AB%AFtyping-game/</guid>
      <pubDate>Tue, 12 May 2026 04:05:36 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;游戏简介&quot;&gt;&lt;a href=&quot;#游戏简介&quot; class=&quot;headerlink&quot; title=&quot;游戏简介&quot;&gt;&lt;/a&gt;游戏简介&lt;/h2&gt;&lt;p&gt;这款打字小游戏会在终端中生成一个 &lt;code&gt;4x6&lt;/code&gt; 的随机字母矩阵，你需要按照&lt;strong&gt;行优先&lt;/st</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="游戏简介"><a href="#游戏简介" class="headerlink" title="游戏简介"></a>游戏简介</h2><p>这款打字小游戏会在终端中生成一个 <code>4x6</code> 的随机字母矩阵，你需要按照<strong>行优先</strong>的顺序依次输入正确的字母。每输入正确一个格子就会变成绿色，错误则变成红色并累积错误次数。当错误次数达到上限（默认 5 次）时游戏失败；全部正确输入后游戏胜利。</p><p><strong>核心特性</strong>：</p><ul><li>随机生成大写字母矩阵</li><li>彩色高亮显示当前输入位置（青色背景）、正确输入（绿色）、错误输入（红色）</li><li>实时错误计数，支持中途按 <code>ESC</code> 退出</li><li>跨平台支持 Windows &#x2F; Linux &#x2F; macOS</li></ul><hr><h2 id="代码结构解析"><a href="#代码结构解析" class="headerlink" title="代码结构解析"></a>代码结构解析</h2><h3 id="跨平台无缓冲输入"><a href="#跨平台无缓冲输入" class="headerlink" title="跨平台无缓冲输入"></a>跨平台无缓冲输入</h3><p>为了实现“按下一个键立即响应而不需要回车”，代码分别处理了 Windows 和 Unix-like 系统：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifdef</span> <span class="token expression">_WIN32  </span></span>    <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span><span class="token string">&lt;conio.h></span></span>    <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">GETCH</span> <span class="token expression">_getch</span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span>    </span>    <span class="token comment">// 使用 termios 关闭规范输入和回显</span>    <span class="token keyword">int</span> <span class="token function">GETCH</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">&#125;</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="启用-Windows-终端-ANSI-色彩"><a href="#启用-Windows-终端-ANSI-色彩" class="headerlink" title="启用 Windows 终端 ANSI 色彩"></a>启用 Windows 终端 ANSI 色彩</h3><p>Windows 10+ 的默认控制台支持 ANSI 转义序列，但需要手动启用。代码中定义了 enableAnsi() 函数：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token function">enableAnsi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    HANDLE hOut <span class="token operator">=</span> <span class="token function">GetStdHandle</span><span class="token punctuation">(</span>STD_OUTPUT_HANDLE<span class="token punctuation">)</span><span class="token punctuation">;</span>    DWORD dwMode <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>    <span class="token function">GetConsoleMode</span><span class="token punctuation">(</span>hOut<span class="token punctuation">,</span> <span class="token operator">&amp;</span>dwMode<span class="token punctuation">)</span><span class="token punctuation">;</span>    dwMode <span class="token operator">|=</span> ENABLE_VIRTUAL_TERMINAL_PROCESSING<span class="token punctuation">;</span>    <span class="token function">SetConsoleMode</span><span class="token punctuation">(</span>hOut<span class="token punctuation">,</span> dwMode<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这样在 Windows 下也能使用 \033[32m 这类颜色控制码。</p><h3 id="游戏数据结构"><a href="#游戏数据结构" class="headerlink" title="游戏数据结构"></a>游戏数据结构</h3><ul><li><p><strong>字母矩阵</strong>：<code>vector&lt;vector&lt;char&gt;&gt; letters(ROWS, vector&lt;char&gt;(COLS))</code><br>每个位置存储一个随机大写字母（<code>&#39;A&#39; + dis(engine)</code>）。</p></li><li><p><strong>状态矩阵</strong>：<code>vector&lt;vector&lt;int&gt;&gt; status(ROWS, vector&lt;int&gt;(COLS, 0))</code><br>0 &#x3D; 未输入，1 &#x3D; 正确，-1 &#x3D; 错误。</p></li><li><p><strong>当前指针</strong>：<code>int currentRow, currentCol</code> 表示下一个要输入的格子。</p></li></ul><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">const</span> <span class="token keyword">int</span> ROWS<span class="token operator">=</span><span class="token number">4</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token keyword">int</span> COLS<span class="token operator">=</span><span class="token number">6</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token keyword">int</span> MAX_ERRORS<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>   <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifdef</span> <span class="token expression">_WIN32</span></span>    <span class="token function">SetConsoleOutputCP</span><span class="token punctuation">(</span>CP_UTF8<span class="token punctuation">)</span><span class="token punctuation">;</span>              <span class="token function">SetConsoleCP</span><span class="token punctuation">(</span>CP_UTF8<span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span><span class="token function">enableAnsi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//开启ANSI转义码</span><span class="token comment">//用时间生成随机字母</span><span class="token keyword">auto</span> seed <span class="token operator">=</span> std<span class="token double-colon punctuation">::</span>chrono<span class="token double-colon punctuation">::</span>steady_clock<span class="token double-colon punctuation">::</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">time_since_epoch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>std<span class="token double-colon punctuation">::</span>mt19937 <span class="token function">engine</span><span class="token punctuation">(</span>seed<span class="token punctuation">)</span><span class="token punctuation">;</span>std<span class="token double-colon punctuation">::</span>uniform_int_distribution<span class="token operator">&lt;</span><span class="token operator">></span> <span class="token function">dis</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">25</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//一个vector，每一个元素是另一个vector&lt;char>，外层是行，内层是列</span><span class="token comment">//std::vector&lt;char>(COLS)一个临时内层vector，作为每一行的初始值</span><span class="token comment">//临时对象本身是 vector&lt;char>(COLS)，表示它创建一个大小为 COLS 的 vector&lt;char>，即每一行有 COLS 个字符。</span>std<span class="token double-colon punctuation">::</span>vector<span class="token operator">&lt;</span>std<span class="token double-colon punctuation">::</span>vector<span class="token operator">&lt;</span><span class="token keyword">char</span><span class="token operator">>></span> <span class="token function">letters</span><span class="token punctuation">(</span>ROWS<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token generic-function"><span class="token function">vector</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token keyword">char</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>COLS<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> r<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>r<span class="token operator">&lt;</span>ROWS<span class="token punctuation">;</span><span class="token operator">++</span>r<span class="token punctuation">)</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>c<span class="token operator">&lt;</span>COLS<span class="token punctuation">;</span><span class="token operator">++</span>c<span class="token punctuation">)</span>        letters<span class="token punctuation">[</span>r<span class="token punctuation">]</span><span class="token punctuation">[</span>c<span class="token punctuation">]</span><span class="token operator">=</span><span class="token char">'A'</span><span class="token operator">+</span><span class="token function">dis</span><span class="token punctuation">(</span>engine<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//记录每个位置的判定结果，0=为输入，1=正确，-1=错误</span>std<span class="token double-colon punctuation">::</span>vector<span class="token operator">&lt;</span>std<span class="token double-colon punctuation">::</span>vector<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">>></span><span class="token function">status</span><span class="token punctuation">(</span>ROWS<span class="token punctuation">,</span>std<span class="token double-colon punctuation">::</span><span class="token generic-function"><span class="token function">vector</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>COLS<span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">int</span> currentRow<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span>currentCol<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>      <span class="token comment">//当前待输入位置</span><span class="token keyword">int</span> error<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>            <span class="token comment">//错误次数</span><span class="token keyword">bool</span> gameOver<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">;</span><span class="token keyword">bool</span> victory<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="游戏主循环"><a href="#游戏主循环" class="headerlink" title="游戏主循环"></a>游戏主循环</h3><ol><li>清屏并重新绘制整个界面（使用 ANSI 码 <code>\033[2J\033[H</code>）。</li><li>根据 <code>status</code> 和当前光标位置设定不同颜色：<ul><li>当前格子 → 青色背景、黑色文字</li><li>正确格子 → 绿色文字</li><li>错误格子 → 红色文字</li><li>未输入格子 → 白色文字</li></ul></li><li>等待用户按键：<ul><li><code>ESC</code> → 退出游戏</li><li>其他字母 → 与 <code>letters[currentRow][currentCol]</code> 比较</li></ul></li><li>判断正确&#x2F;错误，更新状态、错误计数，并移动光标。</li><li>当错误次数 ≥ <code>MAX_ERRORS</code> 或所有格子输入完毕时，结束主循环。</li></ol><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>gameOver<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    <span class="token comment">//清屏并定位到左上角</span>    std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[2J\033[H"</span><span class="token punctuation">;</span>    <span class="token comment">//绘制矩阵</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> r<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>r<span class="token operator">&lt;</span>ROWS<span class="token punctuation">;</span>r<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>c<span class="token operator">&lt;</span>COLS<span class="token punctuation">;</span>c<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            <span class="token keyword">if</span><span class="token punctuation">(</span>r<span class="token operator">==</span>currentRow<span class="token operator">&amp;&amp;</span>c<span class="token operator">==</span>currentCol<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[46m\033[30m"</span><span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>            <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>status<span class="token punctuation">[</span>r<span class="token punctuation">]</span><span class="token punctuation">[</span>c<span class="token punctuation">]</span><span class="token operator">==</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[32m"</span><span class="token punctuation">;</span><span class="token comment">//正确输入绿色</span>            <span class="token punctuation">&#125;</span>            <span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>status<span class="token punctuation">[</span>r<span class="token punctuation">]</span><span class="token punctuation">[</span>c<span class="token punctuation">]</span><span class="token operator">==</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>                std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[31m"</span><span class="token punctuation">;</span><span class="token comment">//错误输入红色</span>            <span class="token punctuation">&#125;</span>            <span class="token keyword">else</span><span class="token punctuation">&#123;</span>                std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[37m"</span><span class="token punctuation">;</span><span class="token comment">//带输入白色</span>            <span class="token punctuation">&#125;</span>            std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token char">' '</span><span class="token operator">&lt;&lt;</span>letters<span class="token punctuation">[</span>r<span class="token punctuation">]</span><span class="token punctuation">[</span>c<span class="token punctuation">]</span><span class="token operator">&lt;&lt;</span><span class="token char">' '</span><span class="token punctuation">;</span>            std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[0m"</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token char">'\n'</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token comment">//显示错误计数和提示</span>    std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\n错误次数："</span><span class="token operator">&lt;&lt;</span>error<span class="token operator">&lt;&lt;</span><span class="token string">"/"</span><span class="token operator">&lt;&lt;</span>MAX_ERRORS            <span class="token operator">&lt;&lt;</span><span class="token string">"\t输入字母（退出按ESC）："</span><span class="token punctuation">;</span>    <span class="token comment">//读取一个键</span>    <span class="token keyword">int</span> ch<span class="token operator">=</span><span class="token function">GETCH</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>ch<span class="token operator">==</span><span class="token number">27</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        gameOver<span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">;</span>        <span class="token keyword">break</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token comment">//转成大写</span>    <span class="token keyword">char</span> inputChar<span class="token operator">=</span><span class="token generic-function"><span class="token function">static_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token keyword">char</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">toupper</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>inputChar<span class="token operator">==</span>letters<span class="token punctuation">[</span>currentRow<span class="token punctuation">]</span><span class="token punctuation">[</span>currentCol<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        status<span class="token punctuation">[</span>currentRow<span class="token punctuation">]</span><span class="token punctuation">[</span>currentCol<span class="token punctuation">]</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>        status<span class="token punctuation">[</span>currentRow<span class="token punctuation">]</span><span class="token punctuation">[</span>currentCol<span class="token punctuation">]</span><span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>        <span class="token operator">++</span>error<span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>error<span class="token operator">>=</span>MAX_ERRORS<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            gameOver<span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">;</span>            victory<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">;</span>            <span class="token keyword">break</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span>    <span class="token comment">//移动到下一个格子（行优先）</span>    <span class="token operator">++</span>currentCol<span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>currentCol<span class="token operator">>=</span>COLS<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>        currentCol<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>        <span class="token operator">++</span>currentRow<span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>currentRow<span class="token operator">>=</span>ROWS<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>            <span class="token comment">//全部输入完毕</span>            gameOver<span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">;</span>            victory<span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="结束画面"><a href="#结束画面" class="headerlink" title="结束画面"></a>结束画面</h3><ul><li>胜利时显示绿色祝贺信息，并附带“🎉恭喜喵”。</li><li>失败时显示红色错误信息，指出超出错误次数。</li><li>普通退出则显示提示。</li></ul><p>最后调用 <code>GETCH()</code> 等待任意键退出，避免控制台窗口一闪而过。</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[2J\033[H"</span><span class="token punctuation">;</span><span class="token comment">//清屏</span><span class="token keyword">if</span><span class="token punctuation">(</span>victory<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[32m🎉恭喜喵\033[0m\n"</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">else</span> <span class="token keyword">if</span><span class="token punctuation">(</span>error<span class="token operator">>=</span>MAX_ERRORS<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\033[31m💥错误达到"</span><span class="token operator">&lt;&lt;</span>MAX_ERRORS<span class="token operator">&lt;&lt;</span><span class="token string">",失败了喵\033[0m\n"</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">else</span><span class="token punctuation">&#123;</span>    std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"游戏已退出。\n"</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">//等待按键再退出</span>std<span class="token double-colon punctuation">::</span>cout<span class="token operator">&lt;&lt;</span><span class="token string">"\n按任意键退出……"</span><span class="token punctuation">;</span><span class="token function">GETCH</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><hr><h3 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h3><p>在终端输入</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">g<span class="token operator">++</span> typing_game<span class="token punctuation">.</span>cpp <span class="token operator">-</span>o typing_game<span class="token punctuation">.</span>exe<span class="token punctuation">.</span>\typing_game<span class="token punctuation">.</span>exe<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><img src="1.png" alt="游戏界面" loading="lazy"></p><h2 id="扩展与改进"><a href="#扩展与改进" class="headerlink" title="扩展与改进"></a>扩展与改进</h2><p>还在开发中……之后会作出一个GUI，然后再丰富玩法……</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/c/">c++</category>
      
      <category domain="https://level0-jj.github.io/tags/%E5%B0%8F%E6%B8%B8%E6%88%8F/">小游戏</category>
      
      
      <comments>https://level0-jj.github.io/2026/05/12/%E7%BB%88%E7%AB%AFtyping-game/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>手写数字识别（LeNet-5）</title>
      <link>https://level0-jj.github.io/2025/12/20/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88LeNet-5%EF%BC%89/</link>
      <guid>https://level0-jj.github.io/2025/12/20/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88LeNet-5%EF%BC%89/</guid>
      <pubDate>Sat, 20 Dec 2025 13:17:07 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;大一人工智能通识课（一个无用的水课？）上看到了一个深度学习项目，然后就想试一下这个经典项目——&lt;strong&gt;手写数字识别&lt;/strong&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>大一人工智能通识课（一个无用的水课？）上看到了一个深度学习项目，然后就想试一下这个经典项目——<strong>手写数字识别</strong>（MNIST）。选择了 LeNet-5 这个轻量级卷积神经网络，最终模型在测试集上达到了约 98.7% 的准确率，之后有时间还会完善（或许）。</p><hr><h2 id="项目背景"><a href="#项目背景" class="headerlink" title="项目背景"></a>项目背景</h2><ul><li><strong>数据集</strong>：<a href="http://yann.lecun.com/exdb/mnist/">MNIST</a> —— 6 万张 28×28 灰度训练图片，1 万张测试图片，共 10 个数字类别（0~9）。</li><li><strong>模型</strong>：LeNet-5（1998 年 Yann LeCun 提出），专为手写数字识别设计。</li></ul><hr><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>我使用 Python 3.10 和虚拟环境（venv）管理依赖。核心库安装命令：</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">pip <span class="token function">install</span> torch torchvision matplotlib Pillow numpy<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="LeNet-5-模型详解"><a href="#LeNet-5-模型详解" class="headerlink" title="LeNet-5 模型详解"></a>LeNet-5 模型详解</h2><h3 id="网络结构"><a href="#网络结构" class="headerlink" title="网络结构"></a>网络结构</h3><table><thead><tr><th>层名</th><th>操作</th><th>输入尺寸</th><th>输出尺寸</th></tr></thead><tbody><tr><td>Conv1</td><td>5×5 卷积，6 核</td><td>1×32×32</td><td>6×28×28</td></tr><tr><td>MaxPool1</td><td>2×2 池化（步长2）</td><td>6×28×28</td><td>6×14×14</td></tr><tr><td>Conv2</td><td>5×5 卷积，16 核</td><td>6×14×14</td><td>16×10×10</td></tr><tr><td>MaxPool2</td><td>2×2 池化</td><td>16×10×10</td><td>16×5×5</td></tr><tr><td>Flatten</td><td>展平</td><td>16×5×5 &#x3D; 400</td><td>400</td></tr><tr><td>FC1</td><td>全连接 400→120</td><td>400</td><td>120</td></tr><tr><td>FC2</td><td>全连接 120→84</td><td>120</td><td>84</td></tr><tr><td>FC3</td><td>全连接 84→10</td><td>84</td><td>10</td></tr><tr><td>LogSoftmax</td><td>对数概率</td><td>10</td><td>10</td></tr></tbody></table><hr><h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><h3 id="模型定义（lenet5-py）"><a href="#模型定义（lenet5-py）" class="headerlink" title="模型定义（lenet5.py）"></a>模型定义（lenet5.py）</h3><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">import</span> torch <span class="token keyword">from</span> torch <span class="token keyword">import</span> nn  <span class="token keyword">import</span> torch<span class="token punctuation">.</span>nn<span class="token punctuation">.</span>functional <span class="token keyword">as</span> F  <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="LeNet-5-网络模型类定义"><a href="#LeNet-5-网络模型类定义" class="headerlink" title="LeNet-5 网络模型类定义"></a>LeNet-5 网络模型类定义</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">MyLeNet5</span><span class="token punctuation">(</span>nn<span class="token punctuation">.</span>Module<span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>               <span class="token builtin">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__init__<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment"># 调用父类nn.Module的初始化方法</span>                <span class="token comment"># ========== 第一组：卷积层 + 池化层 ==========</span>        self<span class="token punctuation">.</span>conv1 <span class="token operator">=</span> nn<span class="token punctuation">.</span>Conv2d<span class="token punctuation">(</span>in_channels<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">,</span> out_channels<span class="token operator">=</span><span class="token number">6</span><span class="token punctuation">,</span> kernel_size<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token comment">#卷积层1 2d</span>                self<span class="token punctuation">.</span>maxpool1 <span class="token operator">=</span> nn<span class="token punctuation">.</span>MaxPool2d<span class="token punctuation">(</span>kernel_size<span class="token operator">=</span><span class="token number">2</span><span class="token punctuation">,</span> stride<span class="token operator">=</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token comment"># 最大池化层1 2d</span>                <span class="token comment"># ========== 第二组：卷积层 + 池化层 ==========</span>        <span class="token comment"># 卷积层2：第二个卷积层，提取更高级的特征</span>        self<span class="token punctuation">.</span>conv2 <span class="token operator">=</span> nn<span class="token punctuation">.</span>Conv2d<span class="token punctuation">(</span>in_channels<span class="token operator">=</span><span class="token number">6</span><span class="token punctuation">,</span> out_channels<span class="token operator">=</span><span class="token number">16</span><span class="token punctuation">,</span> kernel_size<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">)</span>                <span class="token comment"># 最大池化层2：进一步降低特征图尺寸</span>        self<span class="token punctuation">.</span>maxpool2 <span class="token operator">=</span> nn<span class="token punctuation">.</span>MaxPool2d<span class="token punctuation">(</span>kernel_size<span class="token operator">=</span><span class="token number">2</span><span class="token punctuation">,</span> stride<span class="token operator">=</span><span class="token number">2</span><span class="token punctuation">)</span>                <span class="token comment"># ========== 全连接层（全连接层用于分类） ==========</span>        <span class="token comment"># 全连接层1：将卷积特征图展平后连接到全连接层</span>        <span class="token comment"># Linear: 全连接层（线性层）</span>        self<span class="token punctuation">.</span>fc1 <span class="token operator">=</span> nn<span class="token punctuation">.</span>Linear<span class="token punctuation">(</span><span class="token number">16</span><span class="token operator">*</span><span class="token number">5</span><span class="token operator">*</span><span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">120</span><span class="token punctuation">)</span>                <span class="token comment"># 全连接层2：进一步压缩特征</span>        self<span class="token punctuation">.</span>fc2 <span class="token operator">=</span> nn<span class="token punctuation">.</span>Linear<span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">,</span> <span class="token number">84</span><span class="token punctuation">)</span>                <span class="token comment"># 全连接层3（输出层）：最终分类层</span>        self<span class="token punctuation">.</span>fc3 <span class="token operator">=</span> nn<span class="token punctuation">.</span>Linear<span class="token punctuation">(</span><span class="token number">84</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="前向传播函数：定义数据在网络中的流动过程"><a href="#前向传播函数：定义数据在网络中的流动过程" class="headerlink" title="前向传播函数：定义数据在网络中的流动过程"></a>前向传播函数：定义数据在网络中的流动过程</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">forward</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> x<span class="token punctuation">)</span><span class="token punctuation">:</span>        <span class="token comment"># ========== 第一组：卷积 + 激活 + 池化 ==========</span>    x <span class="token operator">=</span> self<span class="token punctuation">.</span>conv1<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># ReLU激活函数：增加非线性，使网络能够学习复杂模式</span>    <span class="token comment"># ReLU(x) = max(0, x)，将负值置为0，保留正值</span>    x <span class="token operator">=</span> F<span class="token punctuation">.</span>relu<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># 最大池化层1：下采样，减少计算量</span>    x <span class="token operator">=</span> self<span class="token punctuation">.</span>maxpool1<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># ========== 第二组：卷积 + 激活 + 池化 ==========</span>    x <span class="token operator">=</span> self<span class="token punctuation">.</span>conv2<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># ReLU激活函数</span>    x <span class="token operator">=</span> F<span class="token punctuation">.</span>relu<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># 最大池化层2：进一步下采样</span>    x <span class="token operator">=</span> self<span class="token punctuation">.</span>maxpool2<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># ========== 展平操作：将多维特征图展平为一维向量 ==========</span>    <span class="token comment"># view(-1, 16*5*5): 重塑张量形状</span>    <span class="token comment"># -1 表示自动计算该维度的大小（通常是batch_size）</span>    x <span class="token operator">=</span> x<span class="token punctuation">.</span>view<span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token operator">*</span><span class="token number">5</span><span class="token operator">*</span><span class="token number">5</span><span class="token punctuation">)</span>        <span class="token comment"># ========== 全连接层1 ==========</span>    <span class="token comment"># 全连接层1 + ReLU激活</span>    x <span class="token operator">=</span> F<span class="token punctuation">.</span>relu<span class="token punctuation">(</span>self<span class="token punctuation">.</span>fc1<span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span>        <span class="token comment"># ========== 全连接层2 ==========</span>    <span class="token comment"># 全连接层2 + ReLU激活</span>    x <span class="token operator">=</span> F<span class="token punctuation">.</span>relu<span class="token punctuation">(</span>self<span class="token punctuation">.</span>fc2<span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span>        <span class="token comment"># ========== 输出层 ==========</span>    <span class="token comment"># 全连接层3（输出层）：不应用激活函数，输出原始分数（logits）</span>    x <span class="token operator">=</span> self<span class="token punctuation">.</span>fc3<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># ========== 输出处理 ==========</span>    <span class="token comment"># 使用log_softmax而不是softmax的原因：</span>    <span class="token comment">#   1. 数值稳定性更好（避免溢出）</span>    <span class="token comment">#   2. 与NLLLoss（负对数似然损失）配合使用更高效</span>    <span class="token comment"># dim=1 表示在类别维度（第1维）上应用softmax</span>    <span class="token keyword">return</span> nn<span class="token punctuation">.</span>functional<span class="token punctuation">.</span>log_softmax<span class="token punctuation">(</span>x<span class="token punctuation">,</span> dim<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span>        <span class="token comment"># log_softmax: 对输出应用对数softmax函数</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>    x <span class="token operator">=</span> torch<span class="token punctuation">.</span>rand<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">]</span><span class="token punctuation">)</span>        <span class="token comment"># 模型实例化：创建MyLeNet5模型对象</span>    model <span class="token operator">=</span> MyLeNet5<span class="token punctuation">(</span><span class="token punctuation">)</span>        <span class="token comment"># 前向传播：将输入数据传入模型，得到输出</span>    <span class="token comment"># 模型会自动调用forward()方法</span>    y <span class="token operator">=</span> model<span class="token punctuation">(</span>x<span class="token punctuation">)</span>        <span class="token comment"># 打印输出结果</span>    <span class="token comment"># 输出形状：(1, 10)，包含10个类别的对数概率</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"模型输出形状："</span><span class="token punctuation">,</span> y<span class="token punctuation">.</span>shape<span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"模型输出（对数概率）："</span><span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\n说明：输出为对数概率，值越大表示该类别可能性越高"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="训练脚本（train-py）"><a href="#训练脚本（train-py）" class="headerlink" title="训练脚本（train.py）"></a>训练脚本（train.py）</h3><p>训练流程：</p><ol><li>数据预处理和加载</li><li>模型初始化</li><li>定义损失函数和优化器</li><li>训练循环（前向传播 → 计算损失 → 反向传播 → 更新参数）</li><li>验证模型性能</li><li>保存最佳模型</li></ol><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">import</span> torch <span class="token keyword">from</span> torch <span class="token keyword">import</span> nn <span class="token keyword">from</span> lenet5 <span class="token keyword">import</span> MyLeNet5 <span class="token keyword">from</span> torch<span class="token punctuation">.</span>optim <span class="token keyword">import</span> lr_scheduler<span class="token keyword">from</span> torchvision <span class="token keyword">import</span> datasets<span class="token punctuation">,</span> transforms<span class="token keyword">import</span> os  <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="定义数据预处理流程"><a href="#定义数据预处理流程" class="headerlink" title="定义数据预处理流程"></a>定义数据预处理流程</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python">data_transfrom_train <span class="token operator">=</span> transforms<span class="token punctuation">.</span>Compose<span class="token punctuation">(</span><span class="token punctuation">[</span>    transforms<span class="token punctuation">.</span>RandomHorizontalFlip<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>   <span class="token comment"># 随机水平翻转（数据增强，增加训练样本多样性）</span>        transforms<span class="token punctuation">.</span>Resize<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>    <span class="token comment"># Resize((32, 32))：将图片尺寸调整为32×32像素</span>    transforms<span class="token punctuation">.</span>ToTensor<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>    transforms<span class="token punctuation">.</span>Normalize<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">0.1307</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0.3081</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token punctuation">]</span><span class="token punctuation">)</span>data_transfrom_test <span class="token operator">=</span> transforms<span class="token punctuation">.</span>Compose<span class="token punctuation">(</span><span class="token punctuation">[</span>    transforms<span class="token punctuation">.</span>Resize<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>    transforms<span class="token punctuation">.</span>ToTensor<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>    transforms<span class="token punctuation">.</span>Normalize<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">0.1307</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0.3081</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="加载MNIST数据集"><a href="#加载MNIST数据集" class="headerlink" title="加载MNIST数据集"></a>加载MNIST数据集</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python">train_dataset <span class="token operator">=</span> datasets<span class="token punctuation">.</span>MNIST<span class="token punctuation">(</span>    root<span class="token operator">=</span><span class="token string">'./data'</span><span class="token punctuation">,</span>     train<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">,</span>     transform<span class="token operator">=</span>data_transfrom_train<span class="token punctuation">,</span>     download<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span class="token comment"># 创建训练集数据加载器（DataLoader）</span>train_dataloader <span class="token operator">=</span> torch<span class="token punctuation">.</span>utils<span class="token punctuation">.</span>data<span class="token punctuation">.</span>DataLoader<span class="token punctuation">(</span>    dataset<span class="token operator">=</span>train_dataset<span class="token punctuation">,</span>     batch_size<span class="token operator">=</span><span class="token number">64</span><span class="token punctuation">,</span>  <span class="token comment"># 可根据GPU显存调整</span>    shuffle<span class="token operator">=</span><span class="token boolean">True</span>   test_dataset <span class="token operator">=</span> datasets<span class="token punctuation">.</span>MNIST<span class="token punctuation">(</span>    root<span class="token operator">=</span><span class="token string">'./data'</span><span class="token punctuation">,</span>     train<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">,</span>     transform<span class="token operator">=</span>data_transfrom_test<span class="token punctuation">,</span>     download<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>test_dataloader <span class="token operator">=</span> torch<span class="token punctuation">.</span>utils<span class="token punctuation">.</span>data<span class="token punctuation">.</span>DataLoader<span class="token punctuation">(</span>    dataset<span class="token operator">=</span>test_dataset<span class="token punctuation">,</span>     batch_size<span class="token operator">=</span><span class="token number">32</span><span class="token punctuation">,</span>     shuffle<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="设置计算设备"><a href="#设置计算设备" class="headerlink" title="设置计算设备"></a>设置计算设备</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python">device <span class="token operator">=</span> torch<span class="token punctuation">.</span>device<span class="token punctuation">(</span><span class="token string">"cuda"</span> <span class="token keyword">if</span> torch<span class="token punctuation">.</span>cuda<span class="token punctuation">.</span>is_available<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token string">"cpu"</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"使用设备：</span><span class="token interpolation"><span class="token punctuation">&#123;</span>device<span class="token punctuation">&#125;</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>  <span class="token comment"># 打印当前使用的设备</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="初始化模型"><a href="#初始化模型" class="headerlink" title="初始化模型"></a>初始化模型</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python">model <span class="token operator">=</span> MyLeNet5<span class="token punctuation">(</span><span class="token punctuation">)</span>model <span class="token operator">=</span> model<span class="token punctuation">.</span>to<span class="token punctuation">(</span>device<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="定义损失函数"><a href="#定义损失函数" class="headerlink" title="定义损失函数"></a>定义损失函数</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># 交叉熵损失函数（CrossEntropyLoss）</span>loss_fn <span class="token operator">=</span> nn<span class="token punctuation">.</span>CrossEntropyLoss<span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="定义优化器"><a href="#定义优化器" class="headerlink" title="定义优化器"></a>定义优化器</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># 随机梯度下降优化器（SGD with Momentum）</span>optimizer <span class="token operator">=</span> torch<span class="token punctuation">.</span>optim<span class="token punctuation">.</span>SGD<span class="token punctuation">(</span>model<span class="token punctuation">.</span>parameters<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> lr<span class="token operator">=</span><span class="token number">1e-3</span><span class="token punctuation">,</span> momentum<span class="token operator">=</span><span class="token number">0.9</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="定义学习率调度器"><a href="#定义学习率调度器" class="headerlink" title="定义学习率调度器"></a>定义学习率调度器</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment"># StepLR：阶梯式学习率调度器</span>lr_scheduler <span class="token operator">=</span> lr_scheduler<span class="token punctuation">.</span>StepLR<span class="token punctuation">(</span>optimizer<span class="token punctuation">,</span> step_size<span class="token operator">=</span><span class="token number">10</span><span class="token punctuation">,</span> gamma<span class="token operator">=</span><span class="token number">0.1</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="定义训练函数"><a href="#定义训练函数" class="headerlink" title="定义训练函数"></a>定义训练函数</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">train</span><span class="token punctuation">(</span>dataloader<span class="token punctuation">,</span> model<span class="token punctuation">,</span> loss_fn<span class="token punctuation">,</span> optimizer<span class="token punctuation">)</span><span class="token punctuation">:</span>       loss <span class="token operator">=</span> <span class="token number">0.0</span>     current <span class="token operator">=</span> <span class="token number">0.0</span>     n <span class="token operator">=</span> <span class="token number">0</span>          model<span class="token punctuation">.</span>train<span class="token punctuation">(</span><span class="token punctuation">)</span>        <span class="token comment"># 遍历数据加载器中的每个批次</span>    <span class="token keyword">for</span> batch<span class="token punctuation">,</span> <span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>dataloader<span class="token punctuation">)</span><span class="token punctuation">:</span>        <span class="token comment"># ========== 前向传播 ==========</span>        x<span class="token punctuation">,</span> y <span class="token operator">=</span> x<span class="token punctuation">.</span>to<span class="token punctuation">(</span>device<span class="token punctuation">)</span><span class="token punctuation">,</span> y<span class="token punctuation">.</span>to<span class="token punctuation">(</span>device<span class="token punctuation">)</span>                <span class="token comment"># 前向传播：将输入数据传入模型，得到预测输出</span>        output <span class="token operator">=</span> model<span class="token punctuation">(</span>x<span class="token punctuation">)</span>                <span class="token comment"># 计算损失：比较预测输出和真实标签</span>        cur_loss <span class="token operator">=</span> loss_fn<span class="token punctuation">(</span>output<span class="token punctuation">,</span> y<span class="token punctuation">)</span>                <span class="token comment"># ========== 计算准确率 ==========</span>        <span class="token comment"># torch.max(input, dim): 在指定维度上找到最大值和对应的索引</span>        _<span class="token punctuation">,</span> pred <span class="token operator">=</span> torch<span class="token punctuation">.</span><span class="token builtin">max</span><span class="token punctuation">(</span>output<span class="token punctuation">,</span> dim<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span>                <span class="token comment"># 计算当前批次的准确率</span>        cur_acc <span class="token operator">=</span> torch<span class="token punctuation">.</span><span class="token builtin">sum</span><span class="token punctuation">(</span>y <span class="token operator">==</span> pred<span class="token punctuation">)</span> <span class="token operator">/</span> output<span class="token punctuation">.</span>shape<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>                <span class="token comment"># ========== 反向传播 ==========</span>        optimizer<span class="token punctuation">.</span>zero_grad<span class="token punctuation">(</span><span class="token punctuation">)</span>        cur_loss<span class="token punctuation">.</span>backward<span class="token punctuation">(</span><span class="token punctuation">)</span>        optimizer<span class="token punctuation">.</span>step<span class="token punctuation">(</span><span class="token punctuation">)</span>                <span class="token comment"># ========== 累计统计 ==========</span>        loss <span class="token operator">+=</span> cur_loss<span class="token punctuation">.</span>item<span class="token punctuation">(</span><span class="token punctuation">)</span>        current <span class="token operator">+=</span> cur_acc<span class="token punctuation">.</span>item<span class="token punctuation">(</span><span class="token punctuation">)</span>        n <span class="token operator">=</span> n <span class="token operator">+</span> <span class="token number">1</span>        <span class="token comment"># 计算整个epoch的平均损失和准确率</span>    train_loss <span class="token operator">=</span> loss <span class="token operator">/</span> n      train_acc <span class="token operator">=</span> current <span class="token operator">/</span> n          <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'训练损失 (train_loss): </span><span class="token interpolation"><span class="token punctuation">&#123;</span>train_loss<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'训练准确率 (train_acc): </span><span class="token interpolation"><span class="token punctuation">&#123;</span>train_acc<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="定义验证函数"><a href="#定义验证函数" class="headerlink" title="定义验证函数"></a>定义验证函数</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">val</span><span class="token punctuation">(</span>dataloader<span class="token punctuation">,</span> model<span class="token punctuation">,</span> loss_fn<span class="token punctuation">)</span><span class="token punctuation">:</span>    model<span class="token punctuation">.</span><span class="token builtin">eval</span><span class="token punctuation">(</span><span class="token punctuation">)</span>        <span class="token comment"># 初始化统计变量</span>    loss <span class="token operator">=</span> <span class="token number">0.0</span>      current <span class="token operator">=</span> <span class="token number">0.0</span>      n <span class="token operator">=</span> <span class="token number">0</span>          <span class="token keyword">with</span> torch<span class="token punctuation">.</span>no_grad<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>        <span class="token comment"># 遍历验证/测试集的每个批次</span>        <span class="token keyword">for</span> batch<span class="token punctuation">,</span> <span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>dataloader<span class="token punctuation">)</span><span class="token punctuation">:</span>            x<span class="token punctuation">,</span> y <span class="token operator">=</span> x<span class="token punctuation">.</span>to<span class="token punctuation">(</span>device<span class="token punctuation">)</span><span class="token punctuation">,</span> y<span class="token punctuation">.</span>to<span class="token punctuation">(</span>device<span class="token punctuation">)</span>                        <span class="token comment"># 前向传播</span>            output <span class="token operator">=</span> model<span class="token punctuation">(</span>x<span class="token punctuation">)</span>                          <span class="token comment"># 计算损失</span>            cur_loss <span class="token operator">=</span> loss_fn<span class="token punctuation">(</span>output<span class="token punctuation">,</span> y<span class="token punctuation">)</span>                        <span class="token comment"># 获取预测结果</span>            _<span class="token punctuation">,</span> pred <span class="token operator">=</span> torch<span class="token punctuation">.</span><span class="token builtin">max</span><span class="token punctuation">(</span>output<span class="token punctuation">,</span> dim<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span>             <span class="token comment"># 计算当前批次的准确率</span>            cur_acc <span class="token operator">=</span> torch<span class="token punctuation">.</span><span class="token builtin">sum</span><span class="token punctuation">(</span>y <span class="token operator">==</span> pred<span class="token punctuation">)</span> <span class="token operator">/</span> output<span class="token punctuation">.</span>shape<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>                        <span class="token comment"># 累计统计</span>            loss <span class="token operator">+=</span> cur_loss<span class="token punctuation">.</span>item<span class="token punctuation">(</span><span class="token punctuation">)</span>            current <span class="token operator">+=</span> cur_acc<span class="token punctuation">.</span>item<span class="token punctuation">(</span><span class="token punctuation">)</span>            n <span class="token operator">=</span> n <span class="token operator">+</span> <span class="token number">1</span>        <span class="token comment"># 计算整个验证集的平均损失和准确率</span>    val_loss <span class="token operator">=</span> loss <span class="token operator">/</span> n    val_acc <span class="token operator">=</span> current <span class="token operator">/</span> n        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'验证损失 (val_loss): </span><span class="token interpolation"><span class="token punctuation">&#123;</span>val_loss<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'验证准确率 (val_acc): </span><span class="token interpolation"><span class="token punctuation">&#123;</span>val_acc<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>        <span class="token comment"># 返回验证准确率，用于选择最佳模型</span>    <span class="token keyword">return</span> val_acc<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="训练循环"><a href="#训练循环" class="headerlink" title="训练循环"></a>训练循环</h4><pre class="line-numbers language-python" data-language="python"><code class="language-python">epoch <span class="token operator">=</span> <span class="token number">10</span>  min_acc <span class="token operator">=</span> <span class="token number">0</span><span class="token comment"># 开始训练循环</span><span class="token keyword">for</span> t <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>epoch<span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'\n</span><span class="token interpolation"><span class="token punctuation">&#123;</span><span class="token string">"="</span><span class="token operator">*</span><span class="token number">50</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'Epoch </span><span class="token interpolation"><span class="token punctuation">&#123;</span>t<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">&#125;</span></span><span class="token string">/</span><span class="token interpolation"><span class="token punctuation">&#123;</span>epoch<span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'</span><span class="token interpolation"><span class="token punctuation">&#123;</span><span class="token string">"="</span><span class="token operator">*</span><span class="token number">50</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>        <span class="token comment"># ========== 训练阶段 ==========</span>    train<span class="token punctuation">(</span>train_dataloader<span class="token punctuation">,</span> model<span class="token punctuation">,</span> loss_fn<span class="token punctuation">,</span> optimizer<span class="token punctuation">)</span>        <span class="token comment"># ========== 验证阶段 ==========</span>    val_acc <span class="token operator">=</span> val<span class="token punctuation">(</span>test_dataloader<span class="token punctuation">,</span> model<span class="token punctuation">,</span> loss_fn<span class="token punctuation">)</span>        <span class="token comment"># ========== 更新学习率 ==========</span>    lr_scheduler<span class="token punctuation">.</span>step<span class="token punctuation">(</span><span class="token punctuation">)</span>    current_lr <span class="token operator">=</span> optimizer<span class="token punctuation">.</span>param_groups<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">'lr'</span><span class="token punctuation">]</span>    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'当前学习率: </span><span class="token interpolation"><span class="token punctuation">&#123;</span>current_lr<span class="token punctuation">:</span><span class="token format-spec">.6f</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>        <span class="token comment"># ========== 保存最佳模型 ==========</span>    <span class="token keyword">if</span> val_acc <span class="token operator">></span> min_acc<span class="token punctuation">:</span>        <span class="token comment"># 设置模型保存目录</span>        folder <span class="token operator">=</span> <span class="token string">'save_model'</span>                <span class="token comment"># 如果保存目录不存在，则创建它</span>        <span class="token keyword">if</span> <span class="token keyword">not</span> os<span class="token punctuation">.</span>path<span class="token punctuation">.</span>exists<span class="token punctuation">(</span>folder<span class="token punctuation">)</span><span class="token punctuation">:</span>            os<span class="token punctuation">.</span>mkdir<span class="token punctuation">(</span><span class="token string">'save_model'</span><span class="token punctuation">)</span>            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'创建模型保存目录：</span><span class="token interpolation"><span class="token punctuation">&#123;</span>folder<span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>                min_acc <span class="token operator">=</span> val_acc                <span class="token comment"># 保存模型权重</span>        model_path <span class="token operator">=</span> <span class="token string">'save_model/best_model.pth'</span>        torch<span class="token punctuation">.</span>save<span class="token punctuation">(</span>model<span class="token punctuation">.</span>state_dict<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> model_path<span class="token punctuation">)</span>        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'✓ 保存最佳模型 (准确率: </span><span class="token interpolation"><span class="token punctuation">&#123;</span>min_acc<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">) → </span><span class="token interpolation"><span class="token punctuation">&#123;</span>model_path<span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span>    <span class="token keyword">else</span><span class="token punctuation">:</span>        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'当前模型准确率 (</span><span class="token interpolation"><span class="token punctuation">&#123;</span>val_acc<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">) 未超过最佳 (</span><span class="token interpolation"><span class="token punctuation">&#123;</span>min_acc<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">)，不保存'</span></span><span class="token punctuation">)</span><span class="token comment"># ==================== 训练完成 ====================</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'\n</span><span class="token interpolation"><span class="token punctuation">&#123;</span><span class="token string">"="</span><span class="token operator">*</span><span class="token number">50</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">'训练完成！'</span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'最佳验证准确率: </span><span class="token interpolation"><span class="token punctuation">&#123;</span>min_acc<span class="token punctuation">:</span><span class="token format-spec">.4f</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'最佳模型已保存到: save_model/best_model.pth'</span></span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'</span><span class="token interpolation"><span class="token punctuation">&#123;</span><span class="token string">"="</span><span class="token operator">*</span><span class="token number">50</span><span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">'Done!'</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="训练日志"><a href="#训练日志" class="headerlink" title="训练日志"></a>训练日志</h2><p><img src="3.png" alt="train.py运行日志" loading="lazy"><img src="2.png" alt="批量测试" loading="lazy"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>很多用了ai辅助，核心代码和原理由自己完成，欢迎指正（未完善……</p>]]></content:encoded>
      
      
      <category domain="https://level0-jj.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      
      
      <category domain="https://level0-jj.github.io/tags/python/">python</category>
      
      
      <comments>https://level0-jj.github.io/2025/12/20/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88LeNet-5%EF%BC%89/#disqus_thread</comments>
      
    </item>
    
  </channel>
</rss>
