<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Learning_rate</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://learningrate1.com/</id>
  <link href="https://learningrate1.com/" rel="alternate"/>
  <link href="https://learningrate1.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Learning_rate</rights>
  <subtitle>Learning_rate 的个人博客：大模型强化学习（LLM RL）、深度研究智能体（Deep Research Agent）方向的研究笔记，以及清华求学路上的总结与随想。</subtitle>
  <title>Learning_rate</title>
  <updated>2025-07-23T03:56:22.000Z</updated>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="论文" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%BA%E6%96%87/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<p>不定期更新最近读到的 paper （主要关注 reason, LLM RL, LLM Agent,Browse Agent）</p><h1 id="月">6月</h1><h2id="the-entropy-mechanism-of-reinforcement-learning-for-reasoning-language-models">TheEntropy Mechanism of Reinforcement Learning for Reasoning LanguageModels</h2><ul><li>熵 entropy 在训练过程中掉得很快，其主要作用是来源于 <spanclass="math inline">\(-\log \pi_\theta\)</span> 和优势 <spanclass="math inline">\(A\)</span>的协方差，也就是两者如果越不线性相关，模型就越不确定，相反如果模型确信的那些动作给了正优势，不确信的动作给了负优势，模型就很快达到确定的状态</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDdjZDMxNzZjMThkZTBhNDY5NzkzMDRlY2ZkOWE1NGZfTmQzVmJwN0JsM0FJWUVtZ0EyT2lPd0M0eFE5RVRUMmlfVG9rZW46QUZVcmI5b1pkb0p3VDB4RnlHNWNpY3FTbk1oXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>计算 token-level 的 advantage，将比较高的 <spanclass="math display">\[(\log p-\overline{\log p})\cdot(A-\overline{A})\]</span>的那些 token mask 掉，或者给这些 token 加上 KL约束，从而抑制 entropy 掉</li></ul><h2 id="truncated-proximal-policy-optimization-t-ppo">Truncated ProximalPolicy Optimization (T-PPO)</h2><table><colgroup><col style="width: 28%" /><col style="width: 35%" /><col style="width: 35%" /></colgroup><thead><tr><th style="text-align: left;">组件</th><th style="text-align: left;">关键思想</th><th style="text-align: left;">数学公式</th></tr></thead><tbody><tr><td style="text-align: left;">EGAE（Extended Generalized AdvantageEstimation）</td><td style="text-align: left;">只要生成到一半，就开始估算优势函数 <spanclass="math inline">\(A_t\)</span> 。</td><td style="text-align: left;"><spanclass="math inline">\(\begin{aligned}&amp;\widehat{A}_t=\delta_t+(\gamma \lambda) \delta_{t+1}+\cdots+ \\&amp;(\gamma \lambda)^{\ell-t-1} \delta_{\ell-1} \\&amp; \text { 其中 } \ell\text { 是截断长度 }\end{aligned}\)</span></td></tr><tr><td style="text-align: left;">截断—批处理策略</td><td style="text-align: left;">把长回应按固定窗口 <spanclass="math inline">\(\ell\)</span>切块，没写完的序列留到下一步；写完的立即换新 prompt。</td><td style="text-align: left;"><spanclass="math inline">\(\uparrow\)</span> GPU 吞吐：始终保持 batch满员；个学习信号密度。</td></tr><tr><td style="text-align: left;">独立更新 Actor／Critic</td><td style="text-align: left;">－Actor（策略）：用本窗口内的新token－Critic（价值）：等完整序列结束再用 Monte －Carlo 真实回报 <spanclass="math inline">\(R_t\)</span> 。</td><td style="text-align: left;"><spanclass="math inline">\(\begin{aligned}&amp; J_{\text {value }}(\phi)=\\&amp; \frac{1}{2} \mathbb{E}\left[\max\left(\left(V_\phi-R\right)^2,\left(V_{\text {clip}}-R\right)^2\right)\right]\end{aligned}\)</span></td></tr><tr><td style="text-align: left;">整体效率分析</td><td style="text-align: left;">如果实际最大长度 <spanclass="math inline">\(L\)</span> 是窗口 <spanclass="math inline">\(\ell\)</span> 的 <span class="math inline">\(k=L /\ell\)</span> 倍，则端到端训练最快提速 <spanclass="math inline">\(\approx \mathrm{k}\)</span> 倍</td><td style="text-align: left;">理论 + 实测最高 <spanclass="math inline">\(2.5 \times\)</span> 加速</td></tr></tbody></table><h2id="the-unreasonable-effectiveness-of-entropy-minimization-in-llm-reasoning">TheUnreasonable Effectiveness of Entropy Minimization in LLM Reasoning</h2><table><colgroup><col style="width: 22%" /><col style="width: 41%" /><col style="width: 36%" /></colgroup><thead><tr><th style="text-align: left;">阶段</th><th style="text-align: left;">方法</th><th style="text-align: left;">数学目标／奖励</th></tr></thead><tbody><tr><td style="text-align: left;">EM－FT（Fine－Tuning）</td><td style="text-align: left;">直接最小化 <spanclass="math inline">\(\widehat{H}_{\mathrm{tok}}\)</span></td><tdstyle="text-align: left;">与指令微调形式相同，但用模型自采样的输出作无标签数据</td></tr><tr><td style="text-align: left;">EM－RL（Reinforcement Learning）</td><td style="text-align: left;">把负熵当唯一奖励：<spanclass="math inline">\(r(y)=-\widehat{H}_{\mathrm{traj}}\)</span>或 <spanclass="math inline">\(-\widehat{H}_{\text {tok }}\)</span></td><td style="text-align: left;">采用 REINFORCE／GRPO 框架；加小系数 KL回正则防漂移</td></tr><tr><td style="text-align: left;">EM－INF（Inference Scaling）</td><td style="text-align: left;">解码时对 logits 做小步优化减少 <spanclass="math inline">\(\widehat{H}_{\mathrm{tok}}\)</span> （给 logits加上一层神经网新的 logits）</td><td style="text-align: left;">无需调参、只改每步logits；也探讨简单温度自适应</td></tr></tbody></table><h2 id="半监督学习思想应用于-rl">半监督学习思想应用于 RL</h2><h3id="seed-grpo-semantic-entropy-enhanced-grpo-for-uncertainty-aware-policy-optimization">SEED-GRPO:Semantic Entropy Enhanced GRPO for Uncertainty-Aware PolicyOptimization</h3><ul><li>针对采样出来的 <span class="math inline">\(N\)</span>条样本，语义聚类为 <span class="math inline">\(\left\{C_1, \cdots,C_k\right\}\)</span> ，计算优势，定义采样公式（从 shannon 熵 <spanclass="math inline">\(-\sum p_i \log \left(p_i\right)\)</span>Rao－Blackwell 化）</li></ul><p><span class="math display">\[\operatorname{SE}(q)=-\frac{1}{K} \sum_{k=1}^K \mathbb{P}\left(C_k \midq\right)\]</span></p><ul><li>其中 <span class="math inline">\(\mathrm{SE}_{\text {max }}\)</span>为所有答案语义都不一样，代表熵最大 <span class="math inline">\(\log(N)\)</span></li></ul><p><span class="math display">\[\hat{A}_i=A_i \cdot f\left(\alpha \cdot \operatorname{SE}(q) /\operatorname{SE}_{\max }(q)\right)\]</span></p><ul><li>F 是线性函数，使得熵越大，更新越保守</li></ul><h3 id="learning-to-reason-without-external-rewards">Learning to Reasonwithout External Rewards</h3><ul><li>将每个 token 的词表概率分布与均匀分布计算 KL散度，如果越远说明越自信 <spanclass="math inline">\(\operatorname{Self}\)</span>certainty <spanclass="math inline">\((o \mid q):=\frac{1}{|o|} \sum_{i=1}^{|o|}\operatorname{KL}\left(U| | p_{\pi_\theta}\left(\cdot \mid q,o_{&lt;i}\right)\right)=-\frac{1}{|o| \cdot|\mathcal{V}|}\sum_{i=1}^{|o|} \sum_{j=1}^{|\mathcal{V}|} \log \left(|\mathcal{V}|\cdot p_{\pi_\theta}\left(j \mid q,o_{&lt;i}\right)\right)\)</span></li><li>Self-certainty 更能区分 correct 和 incorrect</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZmMwMzkwNzUyMmNjMjg3NzE0YmYxZDc3ZmQzOGJmZGNfZXZqVDJmVGx4WGNpVjR2MGpjbDNTdUFYTm9XbnpTSDdfVG9rZW46QmxISGI4aXJwb2IyOEJ4WTJ3bGNGVzhNbnhoXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>Borda Voting：不同排名赋予不同得分，定义 <spanclass="math display">\[v(r)=(N-r+1)^p\]</span>例如下图的例子 <spanclass="math display">\[p=1\]</span>分别是 6, 5, 4, 3, 2, 1分，然后按照对应答案的一致性进行加权</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=NzZhMzZlMTg0MDRhNGRmYzFlNmEyODhmZGEzMDczOTVfQUxaQ3hVMjNCZ0tjTTU3RURYdXNSTVZSb04xYjlrc0dfVG9rZW46RlNVQ2JNVFVLb1RVYmR4N0VHdWN1N3dlbmNLXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>使用排名赋予投票挑选</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjlkNTcxOTg0ZTU0YjVmNGNlNjYyNGJlYmJiN2IyNjhfaUt6RmdVZzZCcXlvWWNmcXk0emtiRVY3STZnQU5nb0RfVG9rZW46WnRLS2J2U3lObzlia2Z4MlhQNWNuZEhNbktkXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>Learning to Reason without External Rewards这篇文章就在上述文章基础使用这个指标作为 reward</li></ul><h1 id="月-1">3月</h1><h2 id="hot">HoT</h2><ul><li>给事实加上标签</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=NzA5MzAzNjFlM2RjMDE5YmQ1NTEwZDQzMzY4MjRkYzRfMkw3TTJjcVA5akVMUmNyS3NaQnFLa0VvT2wyTGdFSFdfVG9rZW46VUU5Q2JqSFZNb0RKU2l4eDZXNmNDY21vbjdkXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h2id="satori-reinforcement-learning-with-chain-of-action-thought-enhances-llm-reasoning-via-autoregressive-search">Satori:Reinforcement Learning with Chain-of-Action-Thought Enhances LLMReasoning via Autoregressive Search</h2><ul><li>先用错误 =&gt; 正确构建一些 reflection 的数据，强制给 step 加上&lt;|reflect|&gt;，reward 计算加上 refecltion bonus</li></ul><p><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDk3MzkzYmJiMDRkMmRkNzI4YmE2Mjg3ZTY5MjNkNzBfMGZBWTZXWm9UOUd2QU9WM3lFZlBSQUFnajFabktPc1RfVG9rZW46V2xnMGJScmlHb2hMUWF4UzBRWWNwYjl3bnJiXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZWEzNGM5ZjRhMjgzMDIwOTMzOTE3MzMzNTVhODNiYzVfdzROSWc2aDdBTER4YkNDeDFkd0FIZ1p3TVMxZGpRZlRfVG9rZW46TVpacWJYMzZsb0dyTE14R0JZS2NsQTZLbmliXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /></p><h2 id="mrt">MRT</h2><ul><li>拼接 </think> 强制回答的 rollout 作为当前这步的baseline，当前这步直接 rollout 减去 baseline 作为 progress reward</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=Y2FkYzQ5N2JjOGNkMmRmYWI3NTgwYmQ2YjA3NzIwYzRfd0J2WVJFNWNFRUFHYnlzQ1Y5ZXNWM1lyYlYwY3F1d0lfVG9rZW46SFFMSGJCcFRSb2NqVnF4dVY1WWNkWmRKbjRnXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h2 id="think-twice">Think Twice</h2><ul><li>尝试两次，效果 qwq AIME 80.3-&gt;82.1</li></ul><p>Original question prompt</p><p>The assistant’s previous answer is: <answer> last round answer</answer>, and please re-answer.</p><h2id="exploring-data-scaling-trends-and-effects-in-reinforcement-learning-from-human-feedback">ExploringData Scaling Trends and Effects in Reinforcement Learning from HumanFeedback</h2><ul><li>使用 generative model 打分并删去 &gt; 0.5 的数据</li><li>将数学代码类型的数据放在前面</li></ul><h2id="sleep-time-compute-beyond-inference-scaling-at-test-time">Sleep-timeCompute: Beyond Inference Scaling at Test-time</h2><ul><li>将问题拆分为 (context, query), 调用 function call 让模型不断更新context，function call 传入参数包含</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=MzY1YzcwMjAwYjAwNTlhMzc4ODFhZjRmY2NkZTJmZTRfOGhPdW1EWFlnN1NjZzUySFJVRXdiQUxKb0FYdFM1d0RfVG9rZW46R1BBemJhRkhnb1RaRDl4UkM2N2NPYXphbkJhXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDhjOTBiYWY0MDllZmMzOTFiOGJkODcyMDk3NTM3MzdfcjEyZUVmNHpyQVBGS0w5ajhiZXRHVmdZV0FhYUhWcWZfVG9rZW46RHJ2QmJld3Rlb05Ud0x4a2g0cGNQNEhIblRoXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h2 id="retool">ReTool</h2><p><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=Njc2ODg2NDZjYzVjMDBmNTZkMzhiZDgyM2FlMTk1NWNfeTk5Tnk5MDRjdEMydTNFckVHcVlxOXRTZjI2am1pOExfVG9rZW46RktFTGJraGFIb1Nka0h4M1JpTmNFdnFmblJnXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=Mjc3ZjgyODEyYjE1YzE4NTg0MTRlYjUyMjkzNzU5NDFfb0dTYzFOaHF2aFVNeVBaWEM1RlBtWUkzUzNUWmUwMDBfVG9rZW46VUhzOWJUUDllb3B2S054dEl4Y2M0aE16bmlnXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /></p><h1 id="月-2">2月</h1><h2id="can-1b-llm-surpass-405b-llm-rethinking-compute-optimal-test-time-scaling">Can1B LLM Surpass 405B LLM? Rethinking Compute-Optimal Test-TimeScaling</h2><ul><li>小模型适合 BoN，大模型适合 diverse Beam Search</li></ul><h2 id="s-inference-scaling-for-code">S*: Inference scaling forCode</h2><ul><li>第一步使用公开样例和多轮对话，第二步 prompt语言模型生成能区分两个代码的测试，然后选择最好的 sample</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjhhNGU3OTdkNzRiN2Q0ZThkOGUwNDU4ZjY4ODc5MmJfSlBhR1o0dUdtR3NDWDU2RHR2NEg1MFROOGFLU1MwMEZfVG9rZW46UUlWV2JYMnlxb1dvWXV4Mm5Hd2NSOUN3bmtmXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h1 id="月-3">1月</h1><h2 id="forking-paths-in-neural-text-generation">Forking Paths in NeuralText Generation</h2><ul><li>采样的不同位置更换 token 后结果的概率分布变化，使用 Change PointDetection 统计学方法找到突变位置<ul><li><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=NjJkNTVlMDczYTczZDNiZjZmZDA3MzljNTQ3MjEzYzVfOUZJakl2SkpnZ3N4YVlQU2E1RFFVTUVlcUdxNGpOOEJfVG9rZW46TndOMmJvWnZJb1ZqejV4b3pHWWNJSnJobkt0XzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure></li><li>中间有个突变点能让答案分布收敛到同一个点</li><li>https://forking-paths.streamlit.app/</li></ul></li><li>不同 token 更换形成的概率分布</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=YTU2NzViNGRiYmRlMzA0OTRmZDJjOWJkZGU3NTk0YjlfWVVDR1gyWlNvUUVkeldlYVRFOE05WkJwVE1rdXd0U0dfVG9rZW46SjFMYWIxd0JOb0Z2SGl4dkNvR2NHZUxIbmhlXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>思考<ul><li>这里每个位置 token的更换只考虑了所有生成答案的分布，并没有考虑更换不更换这个 token对答案分布的影响，理论上强化学习应该鼓励那些能生成更多正确答案的token</li></ul></li></ul><h2 id="free-process-rewards-without-process-labels">Free ProcessRewards Without Process Labels</h2><ul><li>使用 DPO 训练好的模型直接给到每一步的 reward <spanclass="math inline">\(q_{\theta}(y_{&lt;t},y_t)=\beta \log\dfrac{\pi_\theta(y_t|y_{&lt;t})}{\pi_{ref}(y_t|y_{&lt;t})}\)</span><ul><li>评测 boN 时使用最小的 step-reward 来选，DPO 训练之后最适合做implicit reward</li><li>能促进 SC 的提升</li><li>但训练后的 policy model 性能下降</li></ul></li></ul><h2id="understanding-chain-of-thought-in-llms-through-information-theory">Understandingchain-of-thought in LLMs through information theory</h2><ul><li>定义信息增益 <spanclass="math display">\[\mathcal{I}=E[\log(Y|X_{j})]-E[\log(Y|X_{j-1})]\geq0\]</span>，也就是做出这一步之后对信息熵减少的程度，如果这一步让熵减少了，说明这一步是前进的，否则这一步是“扰乱”的</li><li>训练一个直接得到答案的模型 <span class="math inline">\(g\)</span>来估计 <span class="math inline">\(\log p(Y|X_{j-1})\approxl_{CE}(Y,g(X_{j-1}))\)</span></li><li>思考：<ul><li>为什么还要用一个模型来模拟这一步通向的答案的概率分布？直接 rollout不行吗</li><li>那这样可以在不知道答案的时候选择那些通向答案概率分布不混乱的步骤，那就是一个reward-free的解码算法，因为只用答案概率分布的大小比较来确定是不是选这一步（其实就是某种意义上的step-maj ？）</li></ul></li></ul><h2id="rstar-math-small-llms-can-master-math-reasoning-with-self-evolved-deep-thinking">rStar-Math:Small LLMs Can Master Math Reasoning with Self-Evolved DeepThinking</h2><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=OTRjMjcwMDVjMDY2MjQzMjFhZTQ1NmUyM2M1ZmVlODNfTEpaNVJYNkt0b2piQ3VxN05yOURzbkZlaFA4R0tsNjdfVG9rZW46SmtJWWJuVXlXb0dpMzF4VWl3NWNmMm9SbkFlXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>Round 1：DeepSeek-Coder-V2-Instruct (236B) 构建 MCTS 初始SFT（rollout 为 8，仅看 final answer），用最高 top-2 SFT 得到 SLM-r1 /PRM-r1</li><li>Round 2：SLM-r1 policy 运行 MCTS（模型更小，采样 rollout 16多一些），增量训练得到 SLM-r2 PRM-r2</li><li>Round 3/4：PRM-ri 指导下 SLM-ri policy MCTS（训练数据难度持续增加），增量训练得到 SLM-r{i+1} PRM-r{i+1} i=2,3</li></ul><h2id="process-supervision-guided-policy-optimization-for-code-generation">ProcessSupervision-Guided Policy Optimization for Code Generation</h2><ul><li>单行代码使用二分查找得到分界线（rollout如果有对的认为该步正确），然后训练 PRM 用于 RL</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ODVlMDZlMWRmZmE4NzVkMTU0OGMwMjIzN2QwNDA1NzVfdXJaWUdyOW9meWpiU3dtNFhlalh1VnVBMGpNNkJhaVBfVG9rZW46Qzl0TmJLVXdOb2VXVkJ4SEJQVWNHM0VBbmFmXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h2 id="entropy-regularized-process-reward-model">Entropy-RegularizedProcess Reward Model</h2><p>对于每一步进行 rollout，令 p_i 为给定当前步新采样出多个 trajactory时每个 trajactory 的分配概率，定义用熵正则化后的奖励函数为</p><p><span class="math display">\[f(\eta)=\dfrac{\ln (\sum_i p_i \exp(\etar(a_i,x)))}{\eta}\]</span></p><p>可以证明该函数为单调递增的凸函数，从 Soft-Label <spanclass="math inline">\(\sum p_i r_i\)</span> 增加到 Hard-Label <spanclass="math inline">\(\max_i(r_i)\)</span></p><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=NTg4ZThlYjFmMTE1NTllYTM2Y2MwODdkMzBmYjI5NzRfVkltVkl5RUZkU3FwUXB1aEtKS1ljeGJqcUR1VjFKZW9fVG9rZW46UE01bmJjV2VFb2FXanB4MGlTSWNGY3dYbndnXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>gsm8k 的最佳 <span class="math inline">\(\eta\)</span>比较高，更偏向结果导向；math 的最佳 <spanclass="math inline">\(\eta\)</span>要低一些，更希望每一步都有不错的正确率</li></ul><h2id="the-lessons-of-developing-process-reward-models-in-mathematical-reasoning">TheLessons of Developing Process Reward Models in MathematicalReasoning</h2><ul><li>结论：<ul><li>MC 性能和泛化性小于 LLM-as-Judge 以及人工标注</li><li>PRM 偏向于给末尾的 step 更低的分数，从而违背了 PRM原本用于过程监督的性质</li></ul></li><li>实验<ul><li>hard label 用分类任务训练，soft label 用回归任务训练</li><li>人工统计“蒙对”比例（答案正确，但过程错误），更难的数据更高（例如omni-math 为 43.4% ）</li><li>MC 训出来的 PRM 当 ORM 用效果最好</li></ul></li><li>方法<ul><li>过滤掉 MC 和 LLM-as-judge 差异比较大的数据，保证一致性，训了个更好的PRM</li></ul></li></ul><blockquote><p>本组实验结论：Point-wise 训练 PRM &gt; regression 训练</p></blockquote><h2id="rewarding-progress-scaling-automated-process-verifiers-for-llm-reasoning">RewardingProgress: Scaling Automated Process Verifiers for LLM Reasoning</h2><ul><li>用原始的 <span class="math inline">\(\pi\)</span> 模型计算 <spanclass="math inline">\(Q\)</span>，更强的 <spanclass="math inline">\(\mu\)</span> 模型计算 Adv</li></ul><p><span class="math display">\[\left.\nabla_\pi\ell_{\mathrm{PAV}-\mathrm{RL}}^{\pi^{\prime}}(\pi)\right|_{\pi^{\prime}=\pi}=\sum_{h=1}^H\nabla_\pi \log \pi\left(a_h \mid s_h\right) \cdot\underbrace{\left(Q^\pi\left(s_h, a_h\right)+\alpha \cdotA^\mu\left(s_h, a_h\right)\right)}_{\text {effective reward }}\]</span></p><ul><li>实验表明：ORM + <span class="math inline">\(\alpha\)</span>* PAV&gt; PAV &gt; ORM</li><li>使用 best-of-k 来代替这个更强的模型</li></ul><blockquote><p>思考：优势的鼓励需要借助更强的模型，和上一篇文章类似，使用qwen-2.5-math 更强的数学模型来构建 label （或许是一种扩大 label数值的方式）</p></blockquote><h2id="self-generated-critiques-boost-reward-modeling-for-language-models">Self-GeneratedCritiques Boost Reward Modeling for Language Models</h2><ul><li>x 是问题，y+/y- 是 chosen / reject，z+/z- 是 critique-cot，s+/s-是标量分熟</li><li>多次采样 critique 然后过滤，通过总结和 top-k (prompt) 产生更 diverse和更高质量的 critique</li><li><spanclass="math display">\[l_r=-\log(r_{\phi}(x,y^+,z^+)&gt;r_{\phi}(x,y^-,z^-))\]</span>即优化给定原始回答和对应critique 下 reward 预测</li><li><span class="math display">\[-\frac{1}{K} \sum_{z \in \mathcal{Z}}\log q_\phi(z \mid y, x)\]</span> 作为 generative-critique的损失函数</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=ZmM0OTliZTdmZGNlOTJkYzZlM2NlNDcyOGVmZTE5ZGFfZjdzRmpUZ20wUWdTNkhsVW9kcjNJcDVlVjRqYlhNNVdfVG9rZW46V1F0U2JnU3cyb0FCVjF4dHp2ZWN4NXVMbnFnXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h2 id="warp-on-the-benefits-of-weight-averaged-rewarded-policies">WARP:On the Benefits of Weight Averaged Rewarded Policies</h2><ul><li>Stage 1: 使用模型参数的 EMA 来计算 KL 惩罚，重复 m 遍</li><li>Stage 2: 上述模型使用球形插值 merge 起来</li><li>Stage 3: 和初始模型线性求和（迭代式的）</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=YjIxYTkyYzY0YjZhZGM3NzI1YzlhYjQ5NzQyZWJhZmVfcmVHYlZZMDJFbzExUEQzSHBYbjRpWHpQU0hQRUIzM3RfVG9rZW46UmFRTGJtaFZrb1lzNGx4b3lhdGNHRmdKbkFlXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>蓝线更好（在 <span class="math inline">\(\beta=0.1\)</span>下）</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=YzU1Yjg3MWI4MzAwODE2ZmU0MjJmOTY5ZDQ0YmI4OTJfcTVYMGJqeGtUR3pkTDVhMHlUTHBjbE5pUTVEQ1hwdlBfVG9rZW46UTBYcWJwMm1Kb01UdTB4OUJJNWN6Q2lBbnJiXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><h2id="deepseek-r1-incentivizing-reasoning-capability-in-llms-via-reinforcement-learning">DeepSeek-R1:Incentivizing Reasoning Capability in LLMs via ReinforcementLearning</h2><ul><li>Deepseek-Zero：基于 Base model 采用以下格式直接 GRPO 训练，只有rule-RM 以及 format-RM 两个奖励函数</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=N2NjM2QzN2YwNWExOGM1ZjMzNDg5OTgyYzg2YjcwZGRfSnFucG01NkFTRU5GZzlwVHlGMkNFeUs3eWxXdTZUdHBfVG9rZW46QVFBbmJVZWtDb1NPeld4VGlMSGNiclRCbmxmXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure><ul><li>LCB 66 AIME 79.8 Codeforce 2000 rate SWE-Bench-Verified 2000</li></ul><h2 id="s1-simple-test-time-scaling">s1: Simple test-time scaling</h2><ul><li>1000 条高质量、多样化的数据，强制添加 token 思考</li></ul><figure><imgsrc="https://zhipu-ai.feishu.cn/space/api/box/stream/download/asynccode/?code=NzYxMWMyNzRkZjliYTc3YWNiYjRlNGUyNGFhNmQwMWNfQjVtT2ppclJaWjNwcGFLMzhDcFMxRXp0cVZ1cHV6ZnhfVG9rZW46SVdTb2JuVmRNbzU1M0Z4cFlnY2NvakZhbkhmXzE3NTMyNDI5NjA6MTc1MzI0NjU2MF9WNA"alt="img" /><figcaption aria-hidden="true">img</figcaption></figure>]]>
    </content>
    <id>https://learningrate1.com/2025/07/23/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/reason_paper/</id>
    <link href="https://learningrate1.com/2025/07/23/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/reason_paper/"/>
    <published>2025-07-23T03:56:22.000Z</published>
    <summary>大模型推理相关论文阅读</summary>
    <title>LLM Reason Paper</title>
    <updated>2025-07-23T03:56:22.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="强化学习" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0/"/>
    <category term="机器学习" scheme="https://learningrate1.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="概念">概念</h2><p><strong>有监督学习任务：</strong>找到一个最优的模型函数。使其在训练数据集上最小化一个给定的损失函数。<span class="math display">\[\text { 最优模型 }=\arg_{\text { 模型 }} \mathbb{E}_{\text {(特征,标签)}\sim \text{数据分布 }}[\text { 损失函数 }(\text { 标签, 模型}(\text { 特征 }))]\]</span><strong>强化学习任务：</strong>最大化智能体策略在和动态环境交互过程中的价值。根据1.5节的分析，策略的价值可以等价转换成奖励函数在策略的占用度量上的期望，即:<span class="math display">\[\text { 最优策略 }=\arg \max _{\text {策略 }} \mathbb{E}_{\text{(状态。动作) 策略的占用度量 }}[\text { 奖励函数 }(\text { 状态, 动作)]}\]</span></p><ul><li>二者优化的途径是不同的，有监督学习直接通过优化模型对于数据特征的输出来优化目标，即<strong>修改目标函数而数据分布不变</strong>；强化学习则通过改变策略来调整智能体和环境交互数据的分布，进而优化目标，即<strong>修改数据分布而目标函数不变</strong>。</li></ul><h2 id="mab---多臂老虎机">MAB - 多臂老虎机</h2><p>多臂老虎机问题可以表示为一个元组 <spanclass="math inline">\(\langle\mathcal{A}, \mathcal{R}\rangle\)</span>,其中: - <span class="math inline">\(\mathcal{A}\)</span>为动作集合，其中一个动作表示拉动一个拉杆。若多臂老虎机一共有 <spanclass="math inline">\(K\)</span> 根拉杆，那动作空间就是集合 <spanclass="math inline">\(\left\{a_1, \ldots, a_K\right\}\)</span>，我们用<span class="math inline">\(a_t \in \mathcal{A}\)</span>表示任意一个动作; - <span class="math inline">\(\mathcal{R}\)</span>为奖励概率分布，拉动每一根拉杆的动作 <spanclass="math inline">\(a\)</span> 都对应一个奖励概率分布 <spanclass="math inline">\(\mathcal{R}(r \mid a)\)</span>,不同拉杆的奖励分布通常是不同的。</p><p>假设每个时间步只能拉动一个拉杆，多臂老虎机的目标为最大化一段时间步<span class="math inline">\(T\)</span> 内累积的奖励: <spanclass="math inline">\(\max \sum_{t=1}^T r_t, r_t \sim\mathcal{R}\left(\cdot \mid a_t\right)\)</span> 。其中 <spanclass="math inline">\(a_t\)</span> 表示在第 <spanclass="math inline">\(t\)</span> 时间步拉动某一拉杆的动作, <spanclass="math inline">\(r_t\)</span> 表示动作 <spanclass="math inline">\(a_t\)</span> 获得的奖励。</p><p><strong>探索和利用</strong>两者之间的平衡</p><ul><li><strong><span class="math inline">\(\epsilon\)</span>贪心算法：</strong><span class="math inline">\(a_t= \begin{cases}\arg\max _{a \in \mathcal{A}} \hat{Q}(a), &amp; \text { 采样概率: }1-\epsilon \\ \text { 从 } \mathcal{A} \text { 中随机选择, } &amp; \text{ 采样概率: } \epsilon\end{cases}\)</span></li><li><strong>上置信界算法：</strong>给定一个概率 <spanclass="math inline">\(p=e^{-2 N_t(a) U_t(a)^2}\Longrightarrow\hat{U}_t(a)=\sqrt{\dfrac{-\log p}{2N_t(a)}}\)</span>，根据霍夫丁不等式，<spanclass="math inline">\(Q_t(a)&lt;\hat{Q}_t(a)+\hat{U}_t(a)\)</span>至少以概率 <span class="math inline">\(1-p\)</span>成立，在每次拉杆前计算奖励期望的上界</li><li><strong>汤普森采样算法：</strong>例如对二项分布的老虎机，其参数的概率分布为beta 分布</li></ul><h2 id="马尔科夫">马尔科夫</h2><h3 id="马尔科夫过程">马尔科夫过程</h3><p>用元组 <span class="math inline">\(\langle\mathcal{S},\mathcal{P}\rangle\)</span> 描述一个<strong>马尔可夫过程</strong>，其中<span class="math inline">\(\mathcal{S}\)</span>是有限数量的状态集合，<span class="math inline">\(\mathcal{P}\)</span>是状态转移矩阵（state transition matrix）。假设一共有 <spanclass="math inline">\(n\)</span> 个状态，此时 <spanclass="math inline">\(\mathcal{S}=\left\{s_1, s_2, \ldots,s_n\right\}\)</span>。状态转移矩阵 <spanclass="math inline">\(\mathcal{P}\)</span>定义了所有状态对之间的转移概率 <span class="math display">\[\mathcal{P}=\left[\begin{array}{ccc}P\left(s_1 | s_1\right) &amp; \cdots &amp; P\left(s_n | s_1\right) \\\vdots &amp; \ddots &amp; \vdots \\P\left(s_1 | s_n\right) &amp; \cdots &amp; P\left(s_n | s_n\right)\end{array}\right]\]</span> 当前状态是未来的充分统计量</p><h3 id="mrp---马尔科夫奖励过程">MRP - 马尔科夫奖励过程</h3><p>由 <span class="math inline">\(\langle\mathcal{S}, \mathcal{P}, r,\gamma\rangle\)</span> 构成： - <spanclass="math inline">\(\mathcal{S}\)</span> - 有限状态 - <spanclass="math inline">\(\mathcal{P}\)</span> - 状态转移矩阵。 - <spanclass="math inline">\(r\)</span> - 奖励函数，某个状态 <spanclass="math inline">\(s\)</span> 的奖励 <spanclass="math inline">\(r(s)\)</span> 指转移到该状态时可以获得奖励的期望。- <span class="math inline">\(\gamma\)</span> 是折扣因子 (discountfactor) , <span class="math inline">\(\gamma\)</span> 的取值范围为 <spanclass="math inline">\([0,1)\)</span> 。远期利益有一定不确定性</p><p>定义<strong>回报</strong>如下 <span class="math display">\[G_t=R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+\cdots=\sum_{k=0}^{\infty}\gamma^k R_{t+k}\]</span> 定义<strong>价值函数</strong> <spanclass="math inline">\(V(s)=\mathbb{E}\left[G_t \midS_t=s\right]\)</span>，迭代法推出 <spanclass="math inline">\(V(s)=r(s)+\gamma \sum_{s^{\prime} \in S}p\left(s^{\prime} \mid s\right)V\left(s^{\prime}\right)\)</span>，为<strong>贝尔曼方程</strong>，记<span class="math display">\[ \mathcal{V}=\left[V\left(s_1\right),V\left(s_2\right), \ldots, V\left(s_n\right)\right]^T,\mathcal{R}=\left[r\left(s_1\right), r\left(s_2\right), \ldots,r\left(s_n\right)\right]^T\]</span> 矩阵形式以及解，该解法复杂度为 <spanclass="math inline">\(\mathcal{O}(n^3)\)</span> <spanclass="math display">\[\begin{aligned} \mathcal{V} &amp; =\mathcal{R}+\gamma \mathcal{P}\mathcal{V} \\ \mathcal{V} &amp; =(I-\gamma \mathcal{P})^{-1}\mathcal{R}\end{aligned}\]</span></p><h3 id="mdp---马尔科夫决策过程">MDP - 马尔科夫决策过程</h3><p>由 <spanclass="math inline">\(\langle\mathcal{S},\mathcal{A},\gamma,r(s,a),P\rangle\)</span>构成：</p><ul><li><span class="math inline">\(\mathcal{S}\)</span> - 状态集合</li><li><span class="math inline">\(\mathcal{A}\)</span> - 动作集合</li><li><span class="math inline">\(\gamma\)</span> -折扣因子（大多数情况下介于 0.95 和 0.99 之间）</li><li><span class="math inline">\(r(s, a)\)</span> -奖励函数，此时奖励可以同时取决于状态 <spanclass="math inline">\(s\)</span> 和动作 <spanclass="math inline">\(a\)</span></li><li><span class="math inline">\(P\left(s^{\prime} | s, a\right)\)</span>- 状态转移函数，表示在状态 <span class="math inline">\(s\)</span>执行动作 <span class="math inline">\(a\)</span> 之后到达状态 <spanclass="math inline">\(s^{\prime}\)</span>的概率（为三维数组或者连续的）</li></ul><p>定义<strong>策略</strong>，可以有确定性策略和随机性策略（根据概率分布）<span class="math display">\[\pi(a | s)=P\left(A_t=a | S_t=s\right)\]</span> 定义<strong>状态价值函数</strong>，代表基于某种策略 <spanclass="math inline">\(\pi\)</span> 下从状态 <spanclass="math inline">\(s\)</span> 出发的期望回报 <spanclass="math display">\[V^\pi(s)=\mathbb{E}_\pi\left[G_t | S_t=s\right]\]</span> 定义<strong>动作价值函数</strong>，代表基于某种策略 <spanclass="math inline">\(\pi\)</span> 下从状态 <spanclass="math inline">\(s\)</span> 执行动作 <spanclass="math inline">\(a\)</span> 的期望回报 <spanclass="math display">\[Q^\pi(s, a)=\mathbb{E}_\pi\left[G_t | S_t=s, A_t=a\right]\]</span> 全概率公式得到 <span class="math display">\[V^\pi(s)=\sum_{a \in A} \pi(a | s) Q^\pi(s, a)\]</span> 类似 MRP，在给定当前状态 <spanclass="math inline">\(s\)</span> 和执行动作 <spanclass="math inline">\(a\)</span> 下 <span class="math display">\[\begin{aligned} Q^\pi(s, a) &amp; =\mathbb{E}_\pi\left[R_t+\gammaQ^\pi\left(S_{t+1}, A_{t+1}\right) \mid S_t=s, A_t=a\right] \\ &amp;=r(s, a)+\gamma \sum_{s^{\prime} \in S} p\left(s^{\prime} \mid s,a\right) \sum_{a^{\prime} \in A} \pi\left(a^{\prime} \mids^{\prime}\right) Q^\pi\left(s^{\prime}, a^{\prime}\right)\end{aligned}\]</span><strong>最优状态价值</strong>是选择使最优动作价值最大的那个动作得到的状态价值<span class="math display">\[V^*(s)=\max _{a \in \mathcal{A}} Q^*(s, a)\]</span> 于是得到<strong>贝尔曼最优方程</strong> <spanclass="math display">\[\begin{aligned} V^*(s) &amp; =\max _{a \in \mathcal{A}}\left\{r(s,a)+\gamma \sum_{s^{\prime} \in \mathcal{S}} p\left(s^{\prime} \mid s,a\right) V^*\left(s^{\prime}\right)\right\} \\ Q^*(s, a) &amp; =r(s,a)+\gamma \sum_{s^{\prime} \in \mathcal{S}} p\left(s^{\prime} \mid s,a\right) \max _{a^{\prime} \in \mathcal{A}} Q^*\left(s^{\prime},a^{\prime}\right)\end{aligned}\]</span> ## 动态规划算法</p><h3 id="价值策略迭代">价值策略迭代</h3><p>随便初始化一个 <span class="math inline">\(V_0 \equiv0\)</span>，在<strong>最优贝尔曼方</strong>程上迭代，此时 <spanclass="math inline">\(V_{+\infty}=V^{\star}\)</span> <spanclass="math display">\[V_{k+1}(s) \leftarrow \max _{a \in \mathcal{A}}\left[\sum_{s^{\prime}\in \mathcal{S}} \mathbb{P}_{\mathcal{E}}\left(s^{\prime} \mid s,a\right)\left[r\left(s, a, s^{\prime}\right)+\gammaV_k\left(s^{\prime}\right)\right]\right]\]</span></p><h3 id="策略提升">策略提升</h3><p>得知状态价值函数的基础上优化策略，有<strong>完全贪心策略</strong>，只选择价值最大的那一个动作<span class="math display">\[\pi(a \mid s):= \begin{cases}1 &amp; a=\operatorname{argmax}_{a^{\prime}\in \mathcal{A}} Q\left(s, a^{\prime}\right) \\ 0 &amp; \text {otherwise }\end{cases}\]</span></p><h2 id="时序差分算法">时序差分算法</h2><p><strong>无模型的强化学习</strong>：环境的奖励函数和状态转移函数是未知的，只有不断地交互采样得到数据才能学习</p><h3 id="sarsa-算法在线策略">Sarsa 算法（在线策略）</h3><p><strong><spanclass="math inline">\(\epsilon\)</span>-贪心策略</strong>，有 <spanclass="math inline">\(1-\epsilon\)</span>的概率采用动作价值最大的那个动作，<spanclass="math inline">\(\epsilon\)</span> 概率从动作空间中随机采取一个动作<span class="math display">\[\pi(a \mid s)= \begin{cases}\epsilon /|\mathcal{A}|+1-\epsilon &amp;\text{ 如果 } a=\arg \max _{a^{\prime}} Q\left(s, a^{\prime}\right) \\\epsilon /|\mathcal{A}| &amp; \text { 其他动作 }\end{cases}\]</span> 蒙特卡洛方法直接拿到奖励然后更新，SARSA算法考虑下一个状态的价值估计，其中使用 <spanclass="math inline">\(\epsilon\)</span>-贪心策略估计动作函数 <spanclass="math display">\[Q\left(s_t, a_t\right) \leftarrow Q\left(s_t,a_t\right)+\alpha\left[r_t+\gamma Q\left(s_{t+1},a_{t+1}\right)-Q\left(s_t, a_t\right)\right]\]</span> 其中 <span class="math inline">\(r_t+\gammaV(s_{t+1})-V(s_t)\)</span> 被称为<strong>时序差分</strong>（temporaldifference, TD），也可以多走几步，为<strong>多步 Sarsa 算法</strong><span class="math display">\[Q\left(s_t, a_t\right) \leftarrow Q\left(s_t,a_t\right)+\alpha\left[r_t+\gamma r_{t+1}+\cdots+\gamma^nQ\left(s_{t+n}, a_{t+n}\right)-Q\left(s_t, a_t\right)\right]\]</span></p><h3 id="q-learning-算法离线策略">Q-learning 算法（离线策略）</h3><p>更新方式变为 <span class="math display">\[Q\left(s_t, a_t\right) \leftarrow Q\left(s_t,a_t\right)+\alpha\left[R_t+\gamma \max _a Q\left(s_{t+1},a\right)-Q\left(s_t, a_t\right)\right]\]</span> 本质上 Sarsa 算法是有了下一个状态后用当前策略（这里是 <spanclass="math inline">\(\epsilon\)</span>-贪心策略）去找到下一个或者多个状态的动作，从而更新相应的动作状态价值，Q-learning算法有了下一个状态后不用当前策略，而是选取最好的那个状态动作，这是由贝尔曼最优方程保证的</p><blockquote><p>采样数据的策略为<strong>行为策略</strong>，利用这些数据来更新的策略为<strong>目标策略</strong>，两者一样是<strong>在线策略</strong>，两者不一样是<strong>离线策略</strong>，</p></blockquote><h2 id="dyna-q-算法">Dyna-Q 算法</h2><p>每次选取一个曾经访问过的状态 <spanclass="math inline">\(s\)</span>，采取曾经在该状态下执行过的动作 <spanclass="math inline">\(a\)</span>，通过模型得到转移后的状态 <spanclass="math inline">\(s^{\prime}\)</span> 以及奖励 <spanclass="math inline">\(r\)</span>，并根据这个模拟数据 <spanclass="math inline">\((s,a,r,s^{\prime})\)</span> 用 Q-learning的方式来更新动作价值函数。</p><h2 id="dqn-算法深度-q-网络">DQN 算法（深度 Q 网络）</h2><h3 id="原始算法">原始算法</h3><p>Q-learning 的更新规则 <span class="math display">\[Q(s, a) \leftarrow Q(s, a)+\alpha\left[r+\gamma \max _{a^{\prime} \in\mathcal{A}} Q\left(s^{\prime}, a^{\prime}\right)-Q(s, a)\right]\]</span> 神经网络参数 <spanclass="math inline">\(\omega\)</span>，<spanclass="math inline">\(Q_{\omega}(s,a)\)</span> 表征 <spanclass="math inline">\(Q\)</span> 值，将 Q网络的损失函数构造为均方误差的形式 <span class="math display">\[\omega^*=\arg \min _\omega \frac{1}{2 N}\sum_{i=1}^N\left[Q_\omega\left(s_i, a_i\right)-\left(r_i+\gamma \max_{a^{\prime}} Q_\omega\left(s_i^{\prime},a^{\prime}\right)\right)\right]^2\]</span> 实际训练中使用 Buffer 池中抽样尽量使数据同分布，使用固定的target 模型更新原始模型，一定时间间隔之后将 target模型更新为原始模型。</p><h3 id="double-dqn">Double DQN</h3><p>训练过程中高估 <span class="math inline">\(Q\)</span> 值，改写为<span class="math display">\[r+\gamma Q_{\omega^{-}}\left(s^{\prime}, \underset{a^{\prime}}{\arg \max} Q_\omega\left(s^{\prime}, a^{\prime}\right)\right)\]</span>即不选取目标网络中最大价值动作，而是选择原始网络中的最大价值动作，这样某些不可能超过<span class="math inline">\(0\)</span> 的 <spanclass="math inline">\(Q\)</span> 值高估数量减少</p><h3 id="dueling-dqn">Dueling DQN</h3><p>价值函数 <span class="math inline">\(Q\)</span> 减去状态价值函数<span class="math inline">\(V\)</span>的结果定义为<strong>优势函数</strong> <spanclass="math inline">\(A\)</span>, 即 <span class="math inline">\(A(s,a)=Q(s, a)-V(s)\)</span> 。在同一个状态下, 所有动作的优势值之和为0。因为所有动作的动作价值的期望就是这个状态的状态价值。在 Dueling <spanclass="math inline">\(D Q N\)</span> 中，<spanclass="math inline">\(Q\)</span> 网络被建模为 <spanclass="math display">\[Q_{\eta, \alpha, \beta}(s, a)=V_{\eta, \alpha}(s)+A_{\eta, \beta}(s, a)\]</span> 由于这样对于相同的 <span class="math inline">\(Q\)</span>值，两者的值可以你增我减，可以固定最优动作的优势函数为 <spanclass="math inline">\(0\)</span> <span class="math display">\[Q_{\eta, \alpha, \beta}(s, a)=V_{\eta, \alpha}(s)+A_{\eta, \beta}(s,a)-\max _{a^{\prime}} A_{\eta, \beta}\left(s, a^{\prime}\right)\]</span> 也可以优势之和固定为 <span class="math inline">\(0\)</span><span class="math display">\[Q_{\eta, \alpha, \beta}(s, a)=V_{\eta, \alpha}(s)+A_{\eta, \beta}(s,a)-\frac{1}{|\mathcal{A}|} \sum_{a^{\prime}} A_{\eta, \beta}\left(s,a^{\prime}\right)\]</span> 这样更加稳定</p><h2 id="策略梯度算法">策略梯度算法</h2><p>假设策略是关于参数 <span class="math inline">\(\theta\)</span>处处可微的函数，定义策略学习的目标函数为 <span class="math display">\[J(\theta)=\mathbb{E}_{s_0}\left[V^{\pi_\theta}\left(s_0\right)\right]\]</span> 代表所有状态下以 <spanclass="math inline">\(\pi_{\theta}\)</span> 策略获得价值的期望，可以推导<span class="math display">\[\begin{aligned}\nabla J(\theta)&amp;=\sum_{s\in\mathcal{S}}\nu^{\pi_{\theta}}(s)\sum_{a\in\mathcal{A}}\nabla_{\theta}\pi_{\theta}(a|s)Q^{^{\pi_{\theta}}}(s,a)\\&amp;=\sum_{s\in \mathcal{S}}\nu^{\pi_{\theta}(s)}\sum_{a\in\mathcal{A}}\pi_{\theta}(a|s)Q^{\pi_{\theta}}(s,a)\nabla_{\theta}\log\pi_{\theta}(a|s)\\&amp;=\mathbb{E}_{\pi_{\theta}}\left[Q^{\pi_{\theta}}(s,a)\nabla_{\theta}\log\pi_{\theta}(a|s)\right]\end{aligned}\]</span> <strong>Reinforce算法</strong>：有限步数环境下采样轨迹计算相应的价值，这是通过蒙特卡洛采样，估计是无偏的，对每个时间<span class="math inline">\(t\)</span>往后采样序列得到轨迹并计算回报并求和乘以策略对数的梯度 <spanclass="math display">\[\nabla_\thetaJ(\theta)=\mathbb{E}_{\pi_\theta}\left[\sum_{t=0}^T\left(\sum_{t^{\prime}=t}^T\gamma^{t^{\prime}-t} r_{t^{\prime}}\right) \nabla_\theta \log\pi_\theta\left(a_t \mid s_t\right)\right]\]</span></p><p>使用当前策略采样，计算当前轨迹每个时刻 <spanclass="math inline">\(t\)</span> 往后的回报，记为 <spanclass="math inline">\(\psi_t\)</span>，然后梯度上升 <spanclass="math display">\[\theta\leftarrow \theta+\alpha \sum_{t}^{T}\psi_t\nabla_{\theta}\log\pi_{\theta}(a_t|s_t)\]</span></p><blockquote><p>[!NOTE]</p><p>如果定义深度神经网络的所有操作为 <spanclass="math inline">\(F:I\rightarrow O\)</span>​，DQN 算法本质是用 <spanclass="math inline">\(F(s)[a]\)</span>​ 去拟合 <spanclass="math inline">\(Q(s,a)\)</span>​ ，与 Q-learning 的 <spanclass="math inline">\(Q^{*}\)</span>​两者的平方误差均值作损失函数，然后梯度下降；策略梯度算法本质是用 <spanclass="math inline">\(\text{Softmax}(F(s))[a]\)</span>​ 去拟合 <spanclass="math inline">\(\pi(a|s)\)</span>​​，梯度用公式推导，使用采样的样本梯度上升。</p></blockquote><h2 id="actor-critic-算法">Actor-Critic 算法</h2><p>一个既学习价值函数，又学习策略函数的方法</p><ul><li><p>Actor 学习策略，使用策略梯度算法更新策略 <spanclass="math display">\[\theta_{\text{act}}=\theta_{\text{act}}+\alpha_\theta \sum_t \delta_t\nabla_\theta \log \pi_\theta\left(a_t \mid s_t\right)\]</span></p></li><li><p>Critic 学习价值函数，类似 DQN，把下一个时刻的目标 <spanclass="math inline">\(r+\gamma V_{\omega}(s_{t+1})\)</span> 作为target，优化损失函数，梯度更新只有 <spanclass="math inline">\(V_{\omega}(s_{t})\)</span> 原始价值函数更新 <spanclass="math display">\[\mathcal{L}(\omega)=\dfrac{1}{2}(r+\gammaV_{\omega}(s_{t+1})-V_{\omega}(s_{t}))^2\\\nabla \mathcal{L}(\omega)=-(r+\gammaV_{\omega}(s_{t+1})-V_{\omega})\nabla V_{\omega}(s_{t})\]</span></p></li></ul><h2 id="trpo-算法">TRPO 算法</h2><h3 id="如何更新策略">如何更新策略</h3><p>新旧策略之间的差距为 <span class="math display">\[\begin{aligned}J(\theta^{\prime})-J(\theta)&amp;=\mathbb{E}_{\pi_{\theta}^{\prime}}\left[\sum_{t=0}^{\infty}\gamma^{t}[r(s_t,a_t)+\gammaV^{\pi_{\theta}}(s_{t+1})-V^{\pi_{\theta}}(s_t))]\right]\\&amp;:=\mathbb{E}_{\pi_{\theta^{\prime}}}\left[\sum_{t=0}^{\infty}\gamma^{t}A^{\pi_{\theta}}(s_t,a_t)\right]\\&amp;=\frac{1}{1-\gamma} \mathbb{E}_{s \sim \nu^\pi\pi_{\theta^{\prime}}} \mathbb{E}_{a \sim \pi_{\theta^{\prime}}\cdot(\cdot \mid s)}\left[A^{\pi_\theta}(s, a)\right]\end{aligned}\]</span> 其中 <spanclass="math inline">\(A^{\pi_{\prime}}(s_t,a_t)\)</span>为优势函数，<spanclass="math inline">\(\nu^{\pi}(s)=(1-\gamma)\sum_{t=0}^{\infty}\gamma^{t}P_{t}^{\pi}(s)\)</span>为状态访问分布，保证对所有的 <span class="math inline">\(s\)</span>求和为 <span class="math inline">\(1\)</span>。</p><ul><li>假设1：相邻两策略之间的状态访问分布变化不大，第一个期望不变</li><li>假设2：相邻两策略之间的 KL 散度相近，信任区域为策略空间的一个球</li></ul><p>则需要优化的策略目标为 <span class="math display">\[\dfrac{\pi_{\theta^{\prime}}(a\mid s)}{\pi_{\theta}(a\mids)}A^{\pi_{\theta_k}}(s,a)\]</span> 同时还要保证优化后的策略尽可能与原来相近</p><p>在 <span class="math inline">\(\theta_{k}\)</span>​​附近一阶近似和二阶近似 <span class="math display">\[\begin{gathered}\mathbb{E}_{s \sim \nu^{\pi_\theta}} \mathbb{E}_{a \sim\pi_{\theta_k}(\cdot \mid s)}\left[\frac{\pi_{\theta^{\prime}}(a \mids)}{\pi_{\theta_k}(a \mid s)} A^{\pi_{\theta_k}}(s, a)\right] \approxg^T\left(\theta^{\prime}-\theta_k\right) \\\mathbb{E}_{s \sim \nu^{\pi_{\theta_k}}}\left[D_{KL}\left(\pi_{\theta_k}(\cdot \mid s), \pi_{\theta^{\prime}}(\cdot \mids)\right)\right] \approx\frac{1}{2}\left(\theta^{\prime}-\theta_k\right)^TH\left(\theta^{\prime}-\theta_k\right)\end{gathered}\]</span> 转换为优化问题 <span class="math display">\[\theta_{k+1}=\underset{\theta^{\prime}}{\arg \max }g^T\left(\theta^{\prime}-\theta_k\right) \quad \text { s.t. } \quad\frac{1}{2}\left(\theta^{\prime}-\theta_k\right)^TH\left(\theta^{\prime}-\theta_k\right) \leq \delta\]</span> <strong>KKT 条件</strong>，构造拉格朗日函数 <spanclass="math inline">\(L\left(\theta^{\prime},\lambda\right)=g^T\left(\theta^{\prime}-\theta_k\right)+\lambda\left(\delta-\dfrac{1}{2}\left(\theta^{\prime}-\theta_k\right)^TH\left(\theta^{\prime}-\theta_k\right)\right)\)</span></p><ol type="1"><li>定长方程式</li></ol><p><span class="math display">\[\begin{aligned}&amp; \nabla_{\theta^{\prime}} L\left(\theta^{\prime},\lambda\right)=g-\lambda H\left(\theta^{\prime}-\theta_k\right)=0 \\&amp; \Rightarrow \theta^{\prime}=\theta_k+\frac{1}{\lambda} H^{-1} g\end{aligned}\]</span> 2. 原始可行性</p><p><span class="math display">\[\frac{1}{2}\left(\theta^{\prime}-\theta_k\right)^TH\left(\theta^{\prime}-\theta_k\right) \leq \delta\]</span> 3. 对偶可行性</p><p><span class="math display">\[\lambda \geq 0\]</span> 4. 互补松弛型 <span class="math display">\[   \lambda\left(\delta-\frac{1}{2}\left(\theta^{\prime}-\theta_k\right)^TH\left(\theta^{\prime}-\theta_k\right)\right)=0   \]</span></p><p>可以解得 <span class="math display">\[\theta_{k+1}=\theta_{k}+\sqrt{\dfrac{2\delta}{g^{T}H^{-1}g}}H^{-1}g\]</span> 使用<strong>共轭梯度法</strong>求解 <spanclass="math inline">\(x=H^{-1}g\)</span>，代入 <spanclass="math display">\[\Delta\theta=\sqrt{\dfrac{2\delta}{x^{T}Hx}}x\]</span> <strong>线性搜索</strong>，找到最小的非负整数 <spanclass="math inline">\(i\)</span>，其中 <spanclass="math inline">\(\alpha\in(0,1)\)</span> <spanclass="math display">\[\theta_{k+1}=\theta_k+\alpha^i \sqrt{\frac{2 \delta}{x^T H x}} x\]</span> 使得新的 <span class="math inline">\(\theta_{k+1}\)</span>满足策略目标</p><h3 id="广义优势估计gae">广义优势估计（GAE）</h3><p>定义多步时序差分 <span class="math display">\[A_{t}^{(i)}=\sum_{j=0}^{i}\gamma^{j}\delta_{t+j}\\\delta_t=-V(s_t)+r_t+\gammaV(s_{t+1})\]</span> 使用数学归纳法可证明 <span class="math display">\[A_{t}^{(k)}=-V(s_t)+\sum_{i=0}^{k-1}\gamma^{i}r_{t+i}+\gamma^{k}V(s_{t+k})\]</span> 使用指数加权平均，有 <spanclass="math inline">\((1-\lambda)\sum_{i=0}^{\infty}\lambda^i=1\)</span><span class="math display">\[\begin{aligned}A_{t}^{GAE}&amp;=(1-\lambda)(A_t^{(1)}+\lambda A_{t}^{(2)}+\lambda^2A_{t}^{(3)}+\cdots)\\&amp; =(1-\lambda)\left(\delta_t \frac{1}{1-\lambda}+\gamma \delta_{t+1}\frac{\lambda}{1-\lambda}+\gamma^2 \delta_{t+2}\frac{\lambda^2}{1-\lambda}+\cdots\right) \\ &amp;=\sum_{l=0}^{\infty}(\gamma \lambda)^l \delta_{t+l}\end{aligned}\]</span> 当 <span class="math inline">\(\lambda=0\)</span>时，为第一个差分 <spanclass="math inline">\(A_{t,\lambda=0}^{GAE}=\delta_t\)</span>，当 <spanclass="math inline">\(\lambda=1\)</span> 时，<spanclass="math inline">\(A_t^{G A E}=\sum_{l=0}^{\infty} \gamma^l\delta_{t+l}=\sum_{l=0}^{\infty} \gamma^lr_{t+l}-V\left(s_t\right)\)</span></p><h2 id="ppo-算法"><font color='red'>PPO 算法</font></h2><p>使用拉格朗日乘子法将 KL 散度前面的系数也放进目标函数中，定义如下<span class="math display">\[\underset{\theta}{\arg \max } \mathbb{E}_{s \sim \nu^{\pi_{\theta_k}}}\mathbb{E}_{a \sim \pi_{\theta_k}(\cdot \mids)}\left[\dfrac{\pi_{\theta}(s|a)}{\pi_{\theta_k}(s|a)}A^{\pi_{\theta}}(s,a)-\betaD_{KL}[\pi_{\theta_k}(\cdot|s),\pi_{\theta}(\cdot|s)]\right]\]</span></p><h3 id="ppo-惩罚">PPO-惩罚</h3><p>令 <spanclass="math inline">\(d_k=D_{KL}^{\nu^{\pi_{\theta_k}}}(\pi_{\theta_k},\pi_\theta)\)</span>，<spanclass="math inline">\(\beta\)</span>​ 更新如下 <spanclass="math display">\[\beta_{k+1}= \begin{cases}\frac{\beta_k}{2}, &amp; \text { 如果 }d_k&lt;\frac{\delta}{1.5} \\ \beta_k \times 2, &amp; \text { 如果 }d_k&gt;\delta \times 1.5 \\ \beta_k, &amp; \text { 否则 }\end{cases}\]</span> 也就是散度太大的话权重调大，散度太低的话权重调小</p><h3 id="ppo-截断">PPO-截断</h3><p>根据优势函数在 <spanclass="math inline">\([1-\epsilon,1+\epsilon]\)</span> 前后截断 <spanclass="math display">\[\underset{\theta}{\arg \max } \mathbb{E}_{s \sim \nu}{}^{\pi_{\theta_k}} \mathbb{E}_{a \sim \pi_{\theta_k}(\cdot \mids)}\left[\min \left(\frac{\pi_\theta(a \mid s)}{\pi_{\theta_k}(a \mids)} A^{\pi_{\theta_k}}(s, a),\operatorname{clip}\left(\frac{\pi_\theta(a \mid s)}{\pi_{\theta_k}(a\mid s)}, 1-\epsilon, 1+\epsilon\right) A^{\pi_{\hat{\sigma}_k}}(s,a)\right)\right.\]</span> 如果 <span class="math inline">\(A&gt;0\)</span> 两者之比被<span class="math inline">\(1+\epsilon\)</span> 截断，如果 <spanclass="math inline">\(A&lt;0\)</span>，则被 <spanclass="math inline">\(1-\epsilon\)</span> 截断，为语言模型主流算法。</p>]]>
    </content>
    <id>https://learningrate1.com/2024/02/01/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <link href="https://learningrate1.com/2024/02/01/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <published>2024-02-01T05:17:04.000Z</published>
    <summary>2024 年寒假期间学习强化学习笔记，主要参考《动手学习强化学习》这本书</summary>
    <title>Foundation for Reinforcement Learning</title>
    <updated>2024-02-01T05:17:04.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="总结" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/"/>
    <category term="大三" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%B8%89/"/>
    <category term="总结" scheme="https://learningrate1.com/tags/%E6%80%BB%E7%BB%93/"/>
    <content>
      <![CDATA[<h2 id="学业">学业</h2><p>大三老狗了，已经对学业厌倦了，不过还是先康康这学期学了些啥</p><p><strong>数值分析</strong>，数学系 bcl老师的，老师人非常好，课间经常到座位上一个一个问我们上课情况，每章留一次作业，还有两次写代码的大作业，分别是解线性方程组和插值，都不算很难。期末复习的时候把《数值分析基础》整本书刷完，其实书上讲得蛮详细的，看完的话问题也不是很大，期末最难的一道使用拉格朗日插值证明恒等式的题目也就是书后的练习题。</p><p>期末复习的时候发现 GPT-4-All-Tools基本能解决大部分问题，这里写了一份<ahref="/2023/10/14/数理基础/数值分析期末复习/#数值分析期末考试">数值分析笔记以及往年题解析</a>。据说这是老师第一次开课时的卷子，平均分惨不忍睹，但GPT-4 十拿九稳，<del>说明这门课就应该取消，LLM能办的事情就不用人来操心了</del>，这次期末考完回来用 GPT-4验算的时候发现自己还算错一道题。。。</p><p><strong>计算机组成原理</strong>，软院 yz老师的，老师人也非常好，喜欢对同一个知识点过很多epoch，属于慢热型，上课内容和作业参考CSAPP，主要讲了信息是怎么编码在计算机中的、编译器是如何操纵运行代码的，Y86指令架构是如何设计并运行的、高速缓存机制是什么、如何优化代码增加运行速度这几方面。</p><p>这课的确是原理课，上课过程中回想起大一大二犯的一些错误：</p><ul><li><strong>程序设计基础：</strong>编译大作业最终代码的时候一直不懂报一百多个有关<code>vector</code> <code>size()</code> 的<code>warning</code>，现在才知道这是一个 <code>unsigned_int</code>类型，和其余 <code>int</code> 作用时会全部转为<code>unsigned_int</code>，编译器会警告这类转换</li><li><strong>数据结构：</strong>写 kd 树以 <spanclass="math inline">\(\mathcal{O}(\sqrt{n})\)</span>返回所在区间，判断两个 <code>int</code> 变量大小关系写成<code>return (a - b) &gt;= 0</code>，没有考虑到溢出的情况，在最后一次提交十成测试时才发现修改过来</li><li><strong>算法分析与设计基础：</strong>在编写各种字符串匹配算法，如KMP、BM 算法时，加入 不同 <code>-O1</code>优化等级能出现完全不同的测试结果，编译器还是很聪明的</li></ul><p>课程有几次小作业，以及三个 CSAPP的大作业，分别涉及位级编码运算、Buflab、Y-86 框架实现，Y-86的框架比较老了，环境配置还挺麻烦，不过作业都还算好写。</p><p><strong>统计推断</strong>，数理基础课组中的课，如果不上这门课的话，就只能上泛函分析、微分几何、数理方程这些硬课，不是课程上不起，而是科研更有性价比。不过这课还是挺有趣的，介绍了充分统计量、完全统计量，学会了如何估计一个点，估计区间，以及使用各种检验接受或者拒绝原假设。课程还是非常有应用价值，之前有个验证GPT-4变懒的文章就用了假设检验，以及孟德尔🫛实验数据也可以检验性状比是否符合<span class="math inline">\(9:3:3:1\)</span>。</p><p><strong>英语进阶读写</strong>，phm 老师的课程，老师人非常nice，考核对我这种英语不好的比较友好，虽然到期末背的比较多，体验还不错，老师上课喜欢讲许多其他领域的知识，很有趣。或许是之前发生过什么事吧，老师上课有点过于谨慎了哈哈。</p><p><strong>软件工程探索与实践(1)</strong>，书院专属课程，软件学院四个研究所每个所出一个老师介绍自己的研究方向，这学期是zxb 老师和 wjl 老师。</p><p>前半学期学习网络安全，在模拟平台上复现一句话木马、系统漏洞、中间人攻击等等，还是很有趣的。期中拿着其余同学攻击的数据进行分析，不得不说手工分析还是很难的，日志文件太多了而且相互之间联系紧密，其实到最后就直接搜索可疑指令还原攻击场景了hh，向那些防守or溯源的“红客”们致敬！所以当黑客还是难，想干点坏事还是挺容易的，但干了坏事不留痕迹难如登天。</p><p>后半学期学习物联网，讨论了目前网络编码信息、发送信息、解码信息中间的技术细节，有关傅立叶变换、正交复用、卷积等内容，以及老师上课讲了降噪耳机的原理，非常妙！期末大作业是写一组程序把一个长度为50 的随机字符串编码成音频，用手机播放，在距离 1m处的电脑接受音频并解码对。调试的过程非常曲折，如果所有声音的振幅一样，那么由于噪声就很难精准定位第一个比特对应的位置，并且只要错位1个比特整个字符串就崩溃了。开始想使用纠错码，但发现错位后根本无法纠错，因为0 1 概率几乎是随机的，之后想到使用不同的振幅并切分batch，每次相当于前后“夹击”定位中间的信息片段，经过不断地调试，最终发送并解码字符串匹配率<span class="math inline">\(100\%\)</span>。</p><h2 id="科研">科研</h2><p>欢迎关注我们在 2023 年暑假期间的工作 AgentTuning～😊（以下是朋友圈原文）</p><hr /><p>欢迎关注 star ⭐️ 我们最新的 Agent 方向工作 AgentTuning</p><p>现有 Agent 系统大多基于 GPT-3.5/4 开发，开源模型即便是 Llama2-70b也几乎无法胜任大多数 Agent 任务。</p><p>AgentTuning 旨在提升 LLM 的 Agent 能力，通过结合 6 个 Agent任务的高质量交互轨迹和通用指令数据对 Llama 2 进行微调，我们得到 AgentLM系列模型。AgentLM 展现出强大的泛化性，AgentLM-70B 在分布外 Agent任务可与 GPT-3.5-turbo 媲美（+176% vsLlama2-70b），同时依然保留通用对话能力。我们开源所有数据和模型，希望AgentLM 能成为 Agent 任务开发的有力开源替代。</p><p>网站：<ahref="https://thudm.github.io/AgentTuning">https://thudm.github.io/AgentTuning</a></p><p>论文：<ahref="https://arxiv.org/abs/2310.12823">https://arxiv.org/abs/2310.12823</a></p><p>代码：<ahref="https://github.com/THUDM/AgentTuning">https://github.com/THUDM/AgentTuning</a></p><p>模型：<ahref="https://huggingface.co/THUDM/agentlm-70b">https://huggingface.co/THUDM/agentlm-70b</a></p><p>数据：<ahref="https://huggingface.co/datasets/THUDM/AgentInstruct">https://huggingface.co/datasets/THUDM/AgentInstruct</a></p><h2 id="社工">社工</h2><p>本学期末开始负责软件学院科协相关工作，希望为学院创造更加良好的科研、科创、技术交流的环境，也辛苦各部门<del>履行职责</del>分好来自各个地方的锅！</p><h2 id="志愿">志愿</h2><p>继续在答疑坊干活，答疑坊最近在搞 AI &amp;大模型，帮忙整理/提供了一些数据</p>]]>
    </content>
    <id>https://learningrate1.com/2024/01/20/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%B8%89%E4%B8%8A%E6%80%BB%E7%BB%93/</id>
    <link href="https://learningrate1.com/2024/01/20/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%B8%89%E4%B8%8A%E6%80%BB%E7%BB%93/"/>
    <published>2024-01-20T13:20:41.000Z</published>
    <summary>今年北京的雪是真的大！南方人被深深震撼，趁着雪还没有完全融化，写写大三上学期的总结～</summary>
    <title>Summary of autumn term in the third college</title>
    <updated>2024-01-20T13:20:41.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="网络" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E7%BD%91%E7%BB%9C/"/>
    <category term="计算机科学" scheme="https://learningrate1.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <content>
      <![CDATA[<h1 id="网络学习">网络学习</h1><h2 id="网络桥接">网络桥接</h2><p><code>docker</code>使用<strong>网络桥接</strong>（bridge）来通信容器与宿主机进行通信，<code>docker0</code>是默认网络桥，其 IP 地址通常是<code>172.17.0.1</code>，可以在命令行中输入以下命令显示</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ ip addr show docker0<br>13: docker0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default <br>......<br>    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0<br>......<br></code></pre></td></tr></table></figure><h2 id="网络检测">网络检测</h2><p>检查端口是否开放并能够访问，<code>nc</code> 是 <code>netcat</code>的缩写，<code>-z</code> 只扫描端口，<code>-v</code> 工作在冗余模式</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ nc -zv 127.0.0.1 4000<br>Connection to 127.0.0.1 port 4000 [tcp/terabase] succeeded!<br><span class="hljs-comment"># 代表可以连接该端口</span><br></code></pre></td></tr></table></figure><p>检测集群上的网络状况，安装 <code>nethogs</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">sudo</span> apt install nethogs<br>$ <span class="hljs-built_in">sudo</span> nethogs<br><span class="hljs-comment"># 显示当前网路状况</span><br></code></pre></td></tr></table></figure><h2 id="nccl">NCCL</h2><p><strong>NCCL：</strong>在多机多卡训练过程中，作为面向GPU提供的集合通信库，能有效提高训练速度。</p><p><strong>DMA(Direct MemoryAccess)</strong>：允许外部设备直接访问计算机内存，无需通过 CPU进行数据传输，减少 CPU 负担。</p><p><strong>RDMA(Remote Direct MemoryAccess)</strong>：允许计算机在网络中从另一台计算机内存中读取或写入数据，而不需要远程系统的CPU 干预，这样能减少网络通信延迟和 CPU 负担。</p><p><strong>InfiniBand</strong>：一个高性能、低延迟的网络通信架构，常用于高性能计算集群，支持RDMA 技术，</p><p>显示当前 <code>InfiniBand</code> 网路设备状态工具，其中<code>state</code> 中的 <code>ACTIVE</code> 代表可以进行通信，而<code>DOWN</code> 代表不活动。</p><p><code>400Gb/sec</code> 中的 <strong>b</strong> 代表<strong>bit</strong>，其等于 <strong>50GB/sec</strong>。</p><p><code>Ethernet</code>代表以太网，与日常网络相同，在大模型训练过程中速度很慢。</p>]]>
    </content>
    <id>https://learningrate1.com/2023/08/18/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E9%9B%86%E7%BE%A4%E7%BD%91%E7%BB%9C/</id>
    <link href="https://learningrate1.com/2023/08/18/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E9%9B%86%E7%BE%A4%E7%BD%91%E7%BB%9C/"/>
    <published>2023-08-18T10:44:19.000Z</published>
    <summary>集群训练过程中有关网络知识的学习</summary>
    <title>Network learning for training models</title>
    <updated>2023-08-18T10:44:19.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="编程" scheme="https://learningrate1.com/categories/%E7%BC%96%E7%A8%8B/"/>
    <category term="Docker" scheme="https://learningrate1.com/categories/%E7%BC%96%E7%A8%8B/Docker/"/>
    <category term="编程" scheme="https://learningrate1.com/tags/%E7%BC%96%E7%A8%8B/"/>
    <content>
      <![CDATA[<h1 id="docker-学习">Docker 学习</h1><h2 id="简介">简介</h2><p>Docker 的吉祥物是 Moby Dock，最初是 dotCloud 公司的内部项目，在 2013年 3 月以 Apache 2.0 协议开源，开放容器联盟 OCl (Open ContainerInitiative)。</p><p>其使用 Go 语言实现，基于 Linux 内核的 cgroup、namespace 以 及 UnionFS等技术实现对进程的封装隔离，最初基于 LXC (LinuxContainers)，后来转向自行开发的 libcontainer</p><h2 id="环境">环境</h2><p>检查 Linux 系统版本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">uname</span> -a<br></code></pre></td></tr></table></figure><p>列出本地 Docker 环境中所有的 Docker 镜像</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker images<br></code></pre></td></tr></table></figure><p>列出当前正在运行的 Docker 容器，ps = process status，加上 -a则列出所有 Docker 容器</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker ps<br></code></pre></td></tr></table></figure><h2 id="docker-入门">Docker 入门</h2><h3 id="交互式容器">交互式容器</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 运行容器</span><br>$ docker run -it ubuntu bash<br><span class="hljs-comment"># 进入类似 Ubuntu 的”新环境“</span><br>root@f7cbdac22a02:/# <br></code></pre></td></tr></table></figure><p><code>-i</code> 保证 <code>STDIN</code> 是开启的，持久的标准输入是<code>shell</code> 的半边天，<code>-t</code> 则是另外一个半边天，让<code>docker</code> 将容器“打”到一个伪 <code>tty</code> 终端（该例子为<code>bash</code>。这里没有给定名字，<code>docker</code>会创建一个随机名，例如 <code>gray_cat</code>，加上名字的方法为<code>--name [NAME]</code>。</p><p>创建之后如同在新的 <code>bash</code> 环境下交互，输入<code>exit</code> 或者 <code>Ctrl+D</code> 退出。输入<code>docker start</code> 加上名字或者容器 ID启动已经停止的容器，如要重新启动为<code>docker restart</code>，但这都只是“启动”容器，要重新回到交互界面需要输入<code>docker attach</code>。</p><h3 id="守护式容器">守护式容器</h3><p>交互式容器适合运行应用程序和服务，例如</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># -d 代表放置在后台运行</span><br>$ docker run --name hello_world -d ubuntu bash -c <span class="hljs-string">&quot;while true: do echo hello world; sleep 1; done&quot;</span><br></code></pre></td></tr></table></figure><p>该容器则没有 <code>shell</code> 会话可供交互。以下命令查看日志，其中<code>-f</code> 与 <code>tail -f</code> 中的 <code>-f</code>相似，用来监控日志的变化，而 <code>-t</code>表示显示时间戳，可以看到如下输出</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker logs -ft hello_world<br>2023-07-29T15:03:44.879386240Z hello world<br>2023-07-29T15:03:45.880515741Z hello world<br>2023-07-29T15:03:46.882173546Z hello world<br></code></pre></td></tr></table></figure><blockquote><p>时间间隔不是严格 1s 因素：系统调度、写入日志、获取时间耗时等。</p></blockquote><h3 id="容器信息">容器信息</h3><p>查看容器进程、显示一个或者多个容器状态信息（整理为 markdown格式）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker top hello_world<br></code></pre></td></tr></table></figure><table><colgroup><col style="width: 4%" /><col style="width: 4%" /><col style="width: 7%" /><col style="width: 4%" /><col style="width: 5%" /><col style="width: 4%" /><col style="width: 8%" /><col style="width: 60%" /></colgroup><thead><tr><th>UID</th><th>PID</th><th>PPID</th><th>C</th><th>STIME</th><th>TTY</th><th>TIME</th><th>CMD</th></tr></thead><tbody><tr><td>root</td><td>2282</td><td>1922281</td><td>7520</td><td>23:09</td><td>?</td><td>00:00:00</td><td>bash -c "while true; do echo hello world; sleep 1; done"</td></tr><tr><td>root</td><td>2282</td><td>3542282</td><td>1920</td><td>23:09</td><td>?</td><td>00:00:00</td><td>sleep 1</td></tr></tbody></table><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 显示所有正在运行容器，-q 代表显示容器 ID</span><br>$ docker stats <span class="hljs-string">&#x27;docker ps -a -q&#x27;</span><br></code></pre></td></tr></table></figure><table style="width:100%;"><colgroup><col style="width: 12%" /><col style="width: 24%" /><col style="width: 5%" /><col style="width: 19%" /><col style="width: 5%" /><col style="width: 11%" /><col style="width: 16%" /><col style="width: 4%" /></colgroup><thead><tr><th>CONTAINER ID</th><th>NAME</th><th>CPU %</th><th>MEM USAGE / LIMIT</th><th>MEM %</th><th>NET I/O</th><th>BLOCK I/O</th><th>PIDS</th></tr></thead><tbody><tr><td>5639808ea117</td><td>hello_world</td><td>0.16%</td><td>3.277MiB / 1007GiB</td><td>0.00%</td><td>1.16kB / 0B</td><td>0B / 0B</td><td>2</td></tr><tr><td>4127b9416379</td><td>affectionate_cartwright</td><td>2.39%</td><td>16.29GiB / 1007GiB</td><td>1.62%</td><td>0B / 0B</td><td>24.6kB / 9.21MB</td><td>191</td></tr><tr><td>79bbf82e8628</td><td>pedantic_williams</td><td>0.42%</td><td>360.6MiB / 1007GiB</td><td>0.03%</td><td>0B / 0B</td><td>0B / 106kB</td><td>83</td></tr><tr><td>cfb008147738</td><td>musing_newton</td><td>0.00%</td><td>2.407GiB / 1007GiB</td><td>0.24%</td><td>0B / 0B</td><td>27.6MB / 38.7MB</td><td>1</td></tr></tbody></table><p>输入如下命令则可以停止容器，但容器仍在 <code>docker ps -a</code>里，要想彻底删除，输入 <code>docker rm [NAME_OR_ID]</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 使用名称或者容器 ID</span><br>$ docker stop hello_world<br></code></pre></td></tr></table></figure><p>自动重启容器加入 <code>--restart=always</code>参数，深入查看容器信息命令 <code>docker inspect hello_world</code>，加入<code>--format</code> 得到想要的指令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 获取容器的 IP 地址</span><br>$ docker inspect --format <span class="hljs-string">&#x27;&#123;&#123; .NetworkSettings.IPAddress&#125;&#125;&#x27;</span> hello_world<br></code></pre></td></tr></table></figure><p>删除所有名为 <code>&lt;none&gt;</code> 的镜像</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">docker rmi $(docker images -f <span class="hljs-string">&quot;dangling=true&quot;</span> -q)<br></code></pre></td></tr></table></figure><h2 id="镜像构建">镜像构建</h2><p>列出存在宿主机某个 <code>/docker</code> 目录下的镜像<code>docker images</code>；拉取镜像<code>docker pull 用户名:仓库名</code>，Docker Hub中有两类仓库，用户仓库和顶层仓库，后者是 Docker公司选定的优质基础镜像；查找镜像 <code>docker search</code>；</p><p>可以在镜像与 <code>bash</code> 交互的时候对镜像更改，退出之后<code>docker commit 容器ID</code>，但这样做会导致镜像到最后臃肿，不推荐这样做，采用<code>DockerFile</code> 编写构建脚本，例如在 <code>docker</code>中换源且安装</p><p>注意 <code>COPY</code> 指令只能在当前 <code>DockerFile</code>目录以及子目录下进行，因为其描述的是一个可重复构建的、独立于任何特定主机的过程</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs dockerfile"><span class="hljs-comment"># 使用 NVIDIA PyTorch镜像作为基础镜像，这是一个基于PyTorch（一个流行的深度学习库）的镜像</span><br><span class="hljs-keyword">FROM</span> nvcr.io/nvidia/pytorch:<span class="hljs-number">23.06</span>-py3<br><br><span class="hljs-comment"># 更改 apt 的源为清华大学的镜像源，可以加快下载速度</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> sed -i <span class="hljs-string">&#x27;s/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g&#x27;</span> /etc/apt/sources.list</span><br><br><span class="hljs-comment"># 更新 apt 的软件源列表</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> apt-get update</span><br><br><span class="hljs-comment"># 设置 SHELL 为 bash</span><br><span class="hljs-keyword">SHELL</span><span class="language-bash"> [ <span class="hljs-string">&quot;/bin/bash&quot;</span>, <span class="hljs-string">&quot;-c&quot;</span> ]</span><br><br><span class="hljs-comment"># 安装一些实用工具和开发工具</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> DEBIAN_FRONTEND=noninteractive apt-get -y install \</span><br><span class="language-bash">        gcc \   <span class="hljs-comment"># GCC编译器</span></span><br>        g++ \   <span class="hljs-comment"># G++编译器</span><br>        zlib1g-dev \  <span class="hljs-comment"># zlib库和头文件，用于文件压缩</span><br>        <span class="hljs-comment"># ...（省略部分注释）</span><br>        software-properties-common   <span class="hljs-comment"># 一个用于管理软件源的工具</span><br><br><span class="hljs-comment"># 更改 pip 的源为中国科技大学的镜像源，可以加快下载速度</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip config <span class="hljs-built_in">set</span> global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip config <span class="hljs-built_in">set</span> global.extra-index-url https://mirrors.ustc.edu.cn/pypi/web/simple</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip config <span class="hljs-built_in">set</span> global.trusted-host mirrors.ustc.edu.cn</span><br><br><span class="hljs-comment"># 更新 pip 至最新版本</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip install --upgrade pip</span><br><br><span class="hljs-comment"># 将当前目录（即包含这个Dockerfile的目录）下的所有文件和文件夹复制到容器的 /workspace 目录下</span><br><span class="hljs-keyword">COPY</span><span class="language-bash"> . /workspace</span><br><br><span class="hljs-comment"># 设置容器中的当前工作目录为 /workspace</span><br><span class="hljs-keyword">WORKDIR</span><span class="language-bash"> /workspace</span><br><br><span class="hljs-comment"># 安装 /workspace 目录下的 Python 包，&quot;-e&quot; 选项表示以&quot;可编辑&quot;模式安装，即在开发环境下安装</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip install -e .</span><br></code></pre></td></tr></table></figure><h2 id="docker-compose">Docker compose</h2><p>Docker Compose 可以在单个主机上定义和运行多个 <code>docker</code>的工具，使用 YAML 文件来配置应用程序的服务</p><h3 id="基本结构">基本结构</h3><p>创建 <code>docker compose.yml</code> 如下所示</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">&#x27;3&#x27;</span><br><span class="hljs-attr">services:</span><br>  <span class="hljs-attr">web:</span><br>    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span><br>    <span class="hljs-attr">ports:</span><br>     <span class="hljs-bullet">-</span> <span class="hljs-string">&quot;5000:5000&quot;</span><br>  <span class="hljs-attr">redis:</span><br>    <span class="hljs-attr">image:</span> <span class="hljs-string">&quot;redis:alpine&quot;</span><br></code></pre></td></tr></table></figure><p>这个文件定义了两个服务：web 和 redis。web 服务将当前目录构建为 Docker镜像，并绑定宿主机和容器的 5000 端口。redis 服务使用 Docker Hub 上的公共Redis 镜像。</p><h3 id="常见命令">常见命令</h3><ul><li><p><code>docker compose up</code>：根据 <code>compose.yml</code>文件启动服务。</p></li><li><p><code>docker compose down</code>：停止并删除由 <code>up</code>命令启动的所有容器。</p></li><li><p><code>docker compose build</code>：构建服务镜像。</p></li><li><p><code>docker compose ps</code>：列出由 <code>up</code>命令启动的所有容器。</p></li><li><p><code>docker compose logs</code>：显示服务的日志。</p></li></ul><h3 id="docker-compose-的使用实例">Docker Compose 的使用实例</h3><p>下面是一个使用 Flask 和 Redis 的简单应用的示例。</p><p>首先，创建一个 <code>app.py</code> 文件，内容如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask<br><span class="hljs-keyword">from</span> redis <span class="hljs-keyword">import</span> Redis<br><br>app = Flask(__name__)<br>redis = Redis(host=<span class="hljs-string">&#x27;redis&#x27;</span>, port=<span class="hljs-number">6379</span>)<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>():<br>    count = redis.incr(<span class="hljs-string">&#x27;hits&#x27;</span>)<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">f&quot;&quot;&quot;Hello World! This page is checked by <span class="hljs-subst">&#123;count&#125;</span> times&quot;&quot;&quot;</span><br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:<br>    app.run(host=<span class="hljs-string">&quot;0.0.0.0&quot;</span>, debug=<span class="hljs-literal">True</span>)<br></code></pre></td></tr></table></figure><p>创建一个 <code>requirements.txt</code> 文件，内容如下：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs txt">flask<br>redis<br></code></pre></td></tr></table></figure><p>创建一个 <code>Dockerfile</code> 文件，内容如下：</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs Dockerfile"><span class="hljs-comment"># 使用官方 Python 运行时作为父镜像</span><br><span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.7</span>-slim<br><br><span class="hljs-comment"># 将工作目录设置为 /app</span><br><span class="hljs-keyword">WORKDIR</span><span class="language-bash"> /app</span><br><br><span class="hljs-comment"># 将当前目录内容复制到容器的 /app 中</span><br><span class="hljs-keyword">ADD</span><span class="language-bash"> . /app</span><br><br><span class="hljs-comment"># 安装 requirements.txt 中指定的所有必需软件</span><br><span class="hljs-keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span><br><br><span class="hljs-comment"># 让 80 端口可以让外界访问</span><br><span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">5000</span><br><br><span class="hljs-comment"># 定义环境变量</span><br><span class="hljs-keyword">ENV</span> NAME World<br><br><span class="hljs-comment"># 在容器启动时运行 app.py</span><br><span class="hljs-keyword">CMD</span><span class="language-bash"> [<span class="hljs-string">&quot;python&quot;</span>, <span class="hljs-string">&quot;app.py&quot;</span>]</span><br></code></pre></td></tr></table></figure><p>在 <code>docker-compose.yml</code> 文件的 <code>web</code>服务中，使用了 <code>build: .</code> 来指定使用当前目录下的 Dockerfile构建镜像，依次执行以下指令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker compose build<br>$ docker compose up<br> ✔ Network docker_instruction_default    Created           0.1s <br> ✔ Container docker_instruction-redis-1  Created           0.0s <br> ✔ Container docker_instruction-web-1    Created           0.0s     <br></code></pre></td></tr></table></figure><p>在浏览器中看到应用程序，每次刷新页面，页面上的数字都会增加。</p><blockquote><p>注意如果想修改代码，必须先 build 后再起(up)。</p></blockquote><h2 id="实战要点">实战要点</h2><p>添加环境变量，用于设置 <code>OpenAI</code> API 相关参数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker run -e MY_VARIABLE=my_value -e ANOTHER_VARIABLE=another_value<br></code></pre></td></tr></table></figure>]]>
    </content>
    <id>https://learningrate1.com/2023/08/01/%E7%BC%96%E7%A8%8B/Docker%20%E5%AD%A6%E4%B9%A0/</id>
    <link href="https://learningrate1.com/2023/08/01/%E7%BC%96%E7%A8%8B/Docker%20%E5%AD%A6%E4%B9%A0/"/>
    <published>2023-08-01T11:29:14.000Z</published>
    <summary>环境配置一直是 cs 的门槛，而 Docker 使用虚拟化技术将应用本身与环境隔离开，保证环境一致性</summary>
    <title>Introduction to Docker</title>
    <updated>2023-08-01T11:29:14.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="总结" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/"/>
    <category term="大二" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%BA%8C/"/>
    <category term="总结" scheme="https://learningrate1.com/tags/%E6%80%BB%E7%BB%93/"/>
    <content>
      <![CDATA[<h2 id="学业">学业</h2><p>这学期按照培养方案按部就班地学习课程</p><p><strong>毛泽东思想和中国特色社会主义</strong>，马院 fwz老师的，老师在寒假就发了课程作业，是个很敬业的老头，经常凌晨在微信群发消息，上课感觉比之前的思政课有趣一些。作业有<strong>课程十问、读书报告、课程总结</strong>三个部分。课程十问主要是介绍自己和简历，之前一直维护着简历，还算比较轻松；读书报告看了《邓小平时代》，问了一下父母对那个年代的记忆，感叹没有那个年代的改革开放，老一辈可能没法活下来，某种意义上小平同志也是我的恩人；课程总结就结合上课讲的内容阐释了一下。</p><p><strong>习近平新时代中国特色社会主义</strong>，也是 fwz老师的，由于是同一个老师，后八周好多课内容跟之前相似。作业是<strong>思考题、读书报告、课程总结</strong>，读书报告阅读了《习近平的七年知青岁月》，总书记的人生也不是完美的，他在前途未卜时没有消极应对，而是磨砺内心，这种面对逆境的人生态度值得我们学习。</p><p>fwz老师不延期作业截止时间，结果自己申请提交成绩缓录。不过想来他一学期要上毛概、习概、中国式现代化，每门课有两个班次，课程过于繁重，期末批作业合计要看两三千篇文章（按照他的说法他会一篇一篇地看并批注）。毛概成绩是7 月 3 日凌晨四点提交的，习概是 7 月 4日下午四点提交的，实在是辛苦了。</p><p><strong>基础物理学(3)</strong>，选的pyk，一学期照着赵凯华的《电磁学》和《热学》讲，而且也有很多有趣的内容没有涉及，总之一学期下来不怎么花时间。平时挑出课件问题或者是写自己思考都可以加分，就连课程建议也能加分，但期末比较忙也不想写这些没有意义的东西，差不多是微积分cjl 老师的翻版了。</p><p>期末期间由于赶习概论文时间比较紧迫，没怎么复习，期末考试考得一般。不过这也是本科最后一门物理课了，想来除了物理当中的一些思想，之后也不会做和物理相关的事情，无论多好的白月光都会在现实世界的冲击下消散……</p><p><strong>离散数学(2)</strong>，软院 cl 和 lsx老师合开的计算机基础课，前四分之三是图论，后四分之一是代数结构，图论知识还是蛮有趣的，知乎上相关<ahref="https://www.zhihu.com/question/40771687/answer/592938088">问题</a>也挺有意思的，很多技巧比较烧脑，从图变换层面有<strong>极长回路、路径扩散、对偶转化</strong>，从方法论层面有<strong>数学归纳法、反证法、重合法</strong>，从算法层面介绍了<strong>Dijkstra、Warshall、Ford、匈牙利</strong>等一些经典算法，还讲了<strong>旅行商问题的近似解法</strong>，和算法分析课中的部分内容重合。群论讲了判定群的口诀“凤姐咬你”（<strong>封</strong>闭、<strong>结</strong>合律、<strong>幺</strong>元、<strong>逆</strong>元），然后简要介绍了<strong>置换群、左右陪集、正规子群</strong>，但没有抽象代数那么深入。</p><p>平时作业比较难，好多题不能直接想出来，只好参考祖传答案。还有一些上机作业，主要是基本算法的实现，也不难。期末考试不难，解答题基本都能做，填空题有些不太确定，不过分值不大。总之，这门课强化了一些计算机基础知识，很多问题也可以用图论的方式去建模，方便计算机离散处理。</p><p><strong>操作系统</strong>，软院 wlj开设的核心专业课，课堂没那么吸引人，到后面出勤率只有 <spanclass="math inline">\(50\%\)</span>。授课内容基本按照谌卫军《操作系统》一书，有<strong>进程调度、死锁、存储管理、I/O设备管理、文件系统</strong>这些内容。学完这些显然我造不了一个操作系统，不过让我对电脑进程调度有进一步的认识，一个很显著的改观是我尽可能减少了同时打开的进程，之前实在是不懂事亏待了自己的电脑。</p><p>这门课还穿插软院经典私货——Petri网，具体来说就是用两种状态类型描述系统资源的调度情况，可以形式化为一种自动机，还可以证明<strong>着色Petri 网</strong>和 <strong>Petri网</strong>表达能力是等价的。不过除了理论漂亮之外没啥可用之处，用来绘制着色Petri 网的软件只支持 Windows，最新版还是 <spanclass="math inline">\(2013\)</span>年的，这个领域可以说被全世界淘汰了。。</p><p>期末考试听学长说只用看谌卫军《操作系统》这本书就问题不大，结果今年大改，完全没涉及这本书上哪怕任何一句话，还好平时有几节课去听了，每道<span class="math inline">\(18\)</span>分的三道大题都磕磕绊绊地求解出来了，最后拿了 <spanclass="math inline">\(89\)</span> 分。考试之后是大作业，在教学操作系统xv6-riscv 上实现新的文件系统，组队 <spanclass="math inline">\(4\)</span> 人，我负责哈希表用来检索文件和 md5信息摘要算法。 期末展示前学习了用 <spanclass="math inline">\(\LaTeX\)</span> 编写 thu 主题的beamer，整体答辩效果不错，助教直接给我们整个组满分。</p><p><img src="/img/大二下总结/操作系统汇报展示.svg" alt="操作系统汇报展示" width="400px" /></p><p><strong>形式语言与自动机</strong>，之前是 lgm 老师开设课程，今年由 gy老师第一次接替授课，相比之前增加手写实现正则表达式的大作业。实验倒是让我深刻理解正则表达式的运行流程，这可是数据清洗的重要技术。不过对每种情况逐一构建非确定自动机是真的会写吐，还要考虑各种边界情况，有de 不完的 bug，最后隐藏样例评测也没能全过，可想而知 <code>regex.h</code>库和 <code>python</code> 的 <code>re</code>库都是相当有水平的，不是我能驾驭的，我还是安安心心调包吧……</p><p>和离散数学 (2)一样，平时作业也有好多不太能独立解答，参考了许多资料。期末考试不是很理想，大题考了AC 自动机、PDA构造、带约束图灵机，不是很会。这门课主要教会了我如何去形式化一些“自然语言”，以及看Turing如何秀翻理论计算机领域，要说用处的话估计就是会一些正则表达式的编写规则吧。</p><p><strong>算法分析与设计基础</strong>，软院 wb老师的，整体内容非常丰富，的确能学到很多东西，对应《算法导论》<strong>基础知识、排序与次序统计量、动态规划、贪心算法、摊还分析、字符串匹配、NP完全性、近似算法、多线程算法</strong>，老师人很好，讲课也清晰。既有数学层面的分析，也有很多算法实验，每年都有经典作业——seam-curving。这门课平时作业任务量是真的重，感觉不像是一门两学分的课。不过《算法导论》这本书是写得好，通俗易懂，零基础也能理解。</p><ul><li>主定理的证明非常严谨</li><li>之前不太能理解动规和贪心，这本书从抽象的角度分析了这两种高级技术背后的逻辑，前者是<strong>重叠子问题和最优子结构</strong>，后者则是<strong>拟阵</strong>这一数学结构。</li><li>NP 完全问题从规约的角度展示了 <span class="math inline">\(7\)</span>个经典的 NP 完全问题</li><li>多线程的调度突然让我回忆起 Transformer 中 <spanclass="math inline">\(d=1024\)</span>的用意，矩阵加速运算需要二分，当有无限多的资源时，两个 <spanclass="math inline">\(n\times n\)</span> 的矩阵乘法能优化到 <spanclass="math inline">\(\Theta(\log n)\)</span> 的时间复杂度</li></ul><p><strong>二年级男生手球</strong>，体育部 lyb老师的，老师年事已高，腿部还受了伤，不能像之前那样跟我们一起打手球比赛了。这学期难点是引体向上，从第十二周起，每隔一天在紫荆东侧单杠处练习，两只手的虎口全都磨破了，有段时间敲键盘特别痛。破茧成蝶，出了血结疤之后，结合腹部力量推动重心上移法，引体向上终于能稳定输出。十五周考核做了<span class="math inline">\(14\)</span>个，对我来说很满意了。手球专项给分也不差，最后拿到自己满意的成绩。</p><h2 id="实习">实习</h2><p><strong>商汤公司</strong>，<strong>NLP见习算法实习生</strong>（语言模型设计方向），去工业界看看，的确大开眼界。很难想象能在企业做科研，而且使用的是世界最前沿的技术。</p><ul><li><p>工作地点在中关村，骑车三四十分钟，不过打车可以企业报销，一般选择打车过去，公司能从窗户向外看到北京大学的博雅塔，每周去三到四天（一般是周末和平常一两天的晚上）</p></li><li><p>公司资源非常丰富，只要是从研究的角度出发，基本想有什么就有什么</p></li><li><p>公司的算力是海量的，对我而言，可以随时调用 <spanclass="math inline">\(2^7\)</span> 张 A100并行训练几百亿参数语言模型</p></li><li><p>发布会之旅</p><ul><li><p>4 月 8日：到达上海商汤科技人工智能计算中心，也是商汤科技算力之源，旁边就是特斯拉工厂</p></li><li><p>4 月 9日：接连几天在计算中心调参刷分，发布会展示的八个项目当中有两个项目和我们组相关</p></li><li><p>4 月 10日：发布会现场演示我们训练好的语言模型，还跟许多投资人交流一些技术细节，当晚打车到外滩，遇到好多热情的上海人帮我们拍照，直到凌晨两点才找到企业能预定的酒店</p></li><li><p>4 月 11 日：发布会很成功，股价大涨，mentor带着我们组在上海外滩附近坐游轮，吃了好几顿正宗上海菜，期间在出租车上听荷塘雨课堂，晚上飞回北京返校</p></li></ul></li></ul><p><img src="/img/大二下总结/交流日.jpg" alt="交流日" width="400px" /></p><p><img src="/img/大二下总结/徐立合影.jpg" alt="CEO合影" width="400px" /></p><p><img src="/img/大二下总结/东方明珠.jpg" alt="东方明珠" width="400px" /></p><h2 id="科研">科研</h2><p>这学期主要是把之前软件学院 SSRT的项目结题，报名参加今年的挑战杯，由于没什么亮点，就按照一些既有的压缩算法汇报展示，今年校科协提交材料特别早，导致很多内容特别仓促，最后“挑战杯”拿了三等奖。</p><p><img src="/img/大二下总结/时序数据编码压缩.svg" alt="时序数据编码压缩" width="400px" /></p><h2 id="社工">社工</h2><p><strong>校科协</strong>，“挑战杯“展览现场工作人员，在各个展区到处转，软院的展区实在是比较冷清，lfh的特奖项目是一个软体机器爪，很有意思。今年行健书院的挑战杯项目质量特别高，遇到了高中陪我一块学物理竞赛的qyy 学长，他们做了一个轮椅。</p><p>总览全场，我至少找到三个项目在整体框架上雷同，不过应该是各书院和原院系重合的原因。在展览过程中，我在公司老板的老板还来了，他是自动化系的三字班学长，人称“鼎爷”（CEO也这样叫他），跟他聊了一会儿科创经历，不过他一个月之后从公司离职了，想必他有自己的想法。</p><p><strong>软院科协</strong>负责 <ahref="https://ssast-readme.github.io/">ReadMe项目</a>，开学期间写了一个征集推送，号召软院的同学多投稿，分享学习经验，接着熟悉了一下<code>mkdocs</code> 框架，往仓库里更新了一些投稿内容。</p><h2 id="志愿">志愿</h2><p><strong>答疑坊</strong>，本学期继续在答疑坊工作，进入答疑坊队委，协助管理答疑坊。原本每两周去一次，由于周二其余队委都没空，改到每周二晚上都在答疑坊值班。</p><p>“温故而知新”，我在给别人讲授的时候也对知识有更新的理解，比如<strong>克罗内可内积、几何平均矩阵、刚体转动动能合成方法、世界线与狭义相对论、oop工程细节 debug、配置 vscode编译环境</strong>等等，每次走出答疑坊，相信和各位来答疑的同学一样，我也收获不少。</p><p>这学期学生发展中心也积极组织和各位基础学科授课教师开展座谈会</p><ul><li><strong>微积分教学组</strong>那帮老师尤其是 wxf语出惊人，对教育界指点江山，不过目前来看高中和大学之间教育的脱节一时半会儿也不太好解决</li><li><strong>线性代数教学组</strong>老师都很好的，许多线代中文教材的编者出席，他们对整个线性代数课程的设计颇有研究</li><li><strong>大学物理教学组</strong>比较抽象，在问卷“希望了解什么情况”一栏里面填写的“任何情况”。物理是一门实验科学，有个物理老师利用详实的“签到组”和“非签到组”实验数据，最后论证出“物理课的学习还是需要依靠自身”的结论，懂了，学得不好跟老师无关……</li></ul>]]>
    </content>
    <id>https://learningrate1.com/2023/07/08/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%BA%8C%E4%B8%8B%E6%80%BB%E7%BB%93/</id>
    <link href="https://learningrate1.com/2023/07/08/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%BA%8C%E4%B8%8B%E6%80%BB%E7%BB%93/"/>
    <published>2023-07-08T05:48:43.000Z</published>
    <summary>烈日，热浪，树荫，军歌，蝉鸣，汗水……军训生活告一段落，回首充实的大二下生活，对未来满怀希望，会一直热爱学习和生活</summary>
    <title>Summary of spring term in the second college</title>
    <updated>2023-07-08T05:48:43.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="讨论" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%A8%E8%AE%BA/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<h1 id="并行计算">并行计算</h1><h2 id="并行技术综述">并行技术综述</h2><h3 id="模型加速">🚀—— 模型加速</h3><ul><li><strong>数据并行：</strong>切分数据，调用 AllReduce计算梯度均值</li><li><strong>模型并行：</strong>将大模型 <code>Tensor</code>切分计算，矩阵线性连接特性</li><li><strong>流水并行：</strong>将任务分段，每个阶段在不同的设备上，前后阶段流水</li></ul><h3 id="减少存储">🍴—— 减少存储</h3><ul><li><strong>重计算：</strong><ul><li>重新计算激活值</li><li>转移到 CPU 上优化显存大小</li></ul></li><li><strong>ZeRO优化：</strong>分割模型和优化器，利用数据并行和模型并行的方式，减少单个设备上的内存占用。</li><li><strong>1F1B：</strong>解决缓存 activation 的份数问题，使得activation 的缓存数量只跟 stage 数相关。</li></ul><h2 id="模型加速-1">模型加速</h2><h3 id="bsp-ssp">BSP / SSP</h3><p>反向传播所需要的时间大约是前向传播的两倍（原因：反向传播需要访问当前输出值和梯度值，访问内存时间长，偏导数计算消耗大）</p><ul><li>BSP：每个 batch 的前向计算需要使用最新模型</li><li>SSP：异步模型更新，后者收敛性没有被严格论证，不采用</li></ul><p>以下为未改进 BSP 版本的运行方式，相比单机的改进仅仅是在最后 update参数时可以并行更新</p><p><img src="/img/DIstribution/image-20230215132632861.png" alt="image-20230215132632861" style="zoom:20%;" /></p><p>BSP 可以通过<strong>梯度累加和检查点</strong>加以改进，</p><h3 id="梯度累加">梯度累加</h3><p>数据并行是空间上的，数据拆分为多个 tensor，micro-batch则是时间上的</p><p><img src="/img/DIstribution/image-20230215131624762.png" alt="image-20230215131624762" style="zoom: 20%;" /></p><p>把前向传播和后向传播中的数据切分在时间序列上计算，记 stage 的数量为<span class="math inline">\(p\)</span>，切分数量为 <spanclass="math inline">\(s\)</span>，可以推导<strong>气泡率</strong> <spanclass="math inline">\(\eta\)</span> 为 <span class="math display">\[\begin{equation}\eta=\frac{p}{p+s-1}+\epsilon(\epsilon&gt;0)\end{equation}\]</span> 可以理解为卡就在每两个 stage 之间的地方“等着"，micro-batch鱼贯而入之后又鱼贯而出，每张卡把经过的每份子数据的前向激活值都存下来，子数据回传的时候把相应的前后值用作梯度计算并保存，当一张卡中所有的梯度计算完成之后更新参数。</p><p>利用率增加，但每张卡都要保存所有 micro-batch的激活值，显存占用太大。</p><h2 id="减少内存">减少内存</h2><h3 id="后向重计算">后向重计算</h3><p>又名“亚线性内存优化”</p><ul><li><strong>Checkpoint：</strong>前向计算时只保留标记的Tensor，其余通过反向传播时临时重新计算一遍前向。通过额外的<strong>计算开销减少显存</strong>。</li><li><strong>CPU offload：</strong>把暂时用不到的 activation 临时缓存到CPU 上。通过额外的<strong>传输开销换显存</strong>。</li></ul><h3 id="f1b">1F1B</h3><p>由公式 (1) 为减少气泡率，通常 <span class="math inline">\(p\sim2s\)</span>，但此时缓存数量较大，一种策略是在所有数据的前向传播未完成时开始反向传播，这样计算完之后的激活值可提早丢弃，其过程图如下，对每张卡实现前向后向交替使用</p><p><img src="/img/DIstribution/image-20230215150757258.png" alt="image-20230215150757258" style="zoom:40%;" /></p><h3 id="zero">ZeRO</h3><p>模型状态 (model states）：模型参数（fp16）、模型梯度（fp16）和 Adam状态（fp32 的模型参数备份，fp32 的 momentum 和 fp32 的 variance )。假设模型参数量 <span class="math inline">\(\Phi\)</span>，则共需要<span class="math inline">\(2 \Phi+2 \Phi+(4 \Phi+4 \Phi+4 \Phi)=4\Phi+12 \Phi=16 \Phi\)</span> 字节存储, 可以看到，Adam 状态的参数量占比<span class="math inline">\(75 \%\)</span> 。</p><p>使用不同分割策略如下，Adam 的占用量最大，先从 Adam 下手，分别对应ZERO_1, ZERO_2, ZERO_3</p><p><img src="/img/DIstribution/DeepSpeed-Image-1.png" alt="img" style="zoom:66%;" /></p><h3 id="zero-offload">ZeRO-Offload</h3><p>显存不够、内存来凑，不过需要消耗更多的通信时间</p><h1 id="梯度累积次数">梯度累积次数</h1><p>TP * PP 张卡去共用 MICRO_BATCH 样本，一共有 N 张卡，则每一次计算<span class="math inline">\(\dfrac{\text{N}}{\text{TP}\cdot \text{PP}}\cdot\text{MICRO\_BATCH}\)</span> 个样本，当梯度积累 <spanclass="math inline">\(\text{gradient}\)</span> 次之后为 GLOBAL_BATCH个样本时，进行一次梯度更新，从而 <span class="math display">\[\text{gradient}=\dfrac{\text{GLOBAL\_BATCH}}{\dfrac{\text{N}}{\text{TP}\cdot\text{PP}}\cdot\text{MICRO\_BATCH}}=\dfrac{\text{TP}\cdot\text{PP}}{\text{N}}\cdot\dfrac{\text{GLOBAL\_BATCH}}{\text{MICRO\_BATCH}}\]</span> 要求 <span class="math display">\[\boxed{\text{TP}\cdot\text{PP}\mid\text{N}}\]</span> 保证这 <span class="math inline">\(\text{N}\)</span> 张卡以<span class="math inline">\(\text{TP}\cdot\text{PP}\)</span>作切分，而本实验环境 <span class="math inline">\(\text{N}\)</span> 是<span class="math inline">\(8\)</span> 的倍数，所以设置 <spanclass="math inline">\(\text{TP}\)</span> 和 <spanclass="math inline">\(\text{PP}\)</span> 等于 <spanclass="math inline">\(8\)</span>是理论可行的，但如果对于小模型仍旧让两者乘积等于 <spanclass="math inline">\(8\)</span>，反而没能利用好显存</p>]]>
    </content>
    <id>https://learningrate1.com/2023/03/02/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%A1%E7%AE%97%E6%A6%82%E8%BF%B0/</id>
    <link href="https://learningrate1.com/2023/03/02/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%A1%E7%AE%97%E6%A6%82%E8%BF%B0/"/>
    <published>2023-03-02T02:27:42.000Z</published>
    <summary>训练大模型期间并行计算相关知识，为后续训练任务作准备</summary>
    <title>Overview of Distributed Computing</title>
    <updated>2023-03-02T02:27:42.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="机器学习" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="机器学习" scheme="https://learningrate1.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="lis-machine-learning">Li's Machine-Learning</h1><h2 id="model-bias-optimazition-issue">Model bias &amp; Optimazitionissue</h2><p>训练过程中，先对 <code>training data</code> 的 <code>loss</code>进行判定，如果<strong>大</strong>，则考虑两种情况</p><ul><li>模型不够弹性（Model bias）</li><li>优化器做得不够好（Optimization）</li></ul><p>搜索范围不够广还是优化器不能达到全局最优（SVM）？</p><p>如果发生下述情况，则说明优化器做得不够好 <spanclass="math display">\[\begin{array}{|c|c|c|c|c|c|}\hline &amp; \text { 1 layer } &amp; \text { 2 layer } &amp; \text { 3layer } &amp; \text { 4 layer } &amp; \text { 5 layer } \\\hline 2017-2020 &amp; 0.28 \mathrm{k} &amp; 0.18 \mathrm{k} &amp; 0.14\mathrm{k} &amp; 0.10 \mathrm{k} &amp; 0.34 \mathrm{k} \\\hline\end{array}\]</span></p><p>优化过程中 <code>Loss</code> 趋于不变，<spanclass="math inline">\(\nabla f\to 0\)</span>，可能在<code>Local minima</code> 或者<code>saddle point</code>，区别方法：Hesse matrix分别正定、既不正定也不负定 <span class="math display">\[L(\theta) \approxL\left(\theta^{\prime}\right)+\frac{1}{2}\left(\theta-\theta^{\prime}\right)^TH\left(\theta-\theta^{\prime}\right)\]</span></p><blockquote><p>在<strong>鞍点</strong>处往 <span class="math inline">\(\lambda&lt;0\)</span> 对应的特征向量方向 <spanclass="math inline">\(\vec{u}\)</span> 优化，<spanclass="math inline">\(H\vec{u}=\lambda \vec{u}\LongrightarrowL(\theta^{\prime})&lt;L(\theta)\)</span>，但实际中涉及二阶导和求解矩阵特征值运算，几乎不用。</p></blockquote><p>维度高代表参数空间有更多的路可以走，实验给出，Hesse matrix中特征值大于 <span class="math inline">\(0\)</span> 的比例大多数集中在<span class="math inline">\(0.5\)</span> 附近，即多数情况下是卡在<code>saddle point</code>。</p><p><img src="/img/李宏毅机器学习笔记/image-20230207205526588.png" alt="image-20230207205526588" style="zoom:33%;" /></p><h2 id="critical-point">Critical point</h2><h3 id="batch">Batch</h3><p>不用所有的训练资料计算梯度，随机混合分组，每组 Batch更新同一组参数，能够节约时间，虽然噪声影响大，但轮数多。（Batch大不一定计算梯度时间长，现代硬件有平行运算能力）</p><p>大的 Batch-size 往往得到比较差的模型，在 Training data 和 Test data上正确率都比较低，这是 Optimize 问题，因为不同的 Batch形成的噪声能更好地跨过 <code>saddle point</code>。小的 Batch-size 在Test data 上也表现好（一种解释是 Small batch 更容易进入“平原”）</p><p><img src="/img/李宏毅机器学习笔记/image-20230207204504569.png" alt="image-20230207204504569" style="zoom:20%;" /></p><h3 id="momentum">Momentum</h3><p>在原始梯度下降（Vanilla）中加入惯性，引入 <spanclass="math inline">\(m^{i}\)</span> 为每次改变中的质量，<spanclass="math inline">\(g^i\)</span> 为每次计算的梯度 <spanclass="math display">\[\begin{aligned}m^2&amp;=\lambda m^1-\eta g^1\\\theta^2&amp;=\theta^1+m^2\end{aligned}\]</span> 容易跨过鞍点</p><h2 id="learning_rate">Learning_rate</h2><blockquote><p>Adam means <strong>Adaptive momentum</strong>.</p></blockquote><h3 id="root-mean-squareadagrad">Root Mean Square(Adagrad)</h3><p>在一个等高线椭圆狭长的优化问题上优化</p><p><img src="/img/李宏毅机器学习笔记/image-20230207205820987.png" alt="image-20230207205820987" style="zoom:33%;" /></p><p>更新参数 <span class="math display">\[\theta_i^{t+1} \leftarrow \theta_i^{t}-\frac{\eta}{\sigma_i^t}g_i^{t}\]</span> 对每个参数 <spanclass="math inline">\(i\)</span>，使用前面梯度的方均根给参数 <spanclass="math inline">\(\sigma_i^t\)</span>，如下 <spanclass="math display">\[\theta_i^{t+1} \leftarrow \theta_i^{t}-\frac{\eta}{\sigma_i^t} g_i^{t}\quad \sigma_i^t=\sqrt{\frac{1}{t+1}\sum_{i=0}^t\left(g_{\dot{i}}^{t}\right)^2}\]</span> 即如果当前更新速度一直很慢的话可调大学习率，否则降低学习率</p><h3 id="rmsprop">RMSProp</h3><blockquote><p>Google 使用的优化器</p></blockquote><p>上述要求梯度基本一样，加入超参数 <spanclass="math inline">\(\alpha\)</span> 表征前者调整的比例 <spanclass="math display">\[\theta_i^{t+1} \leftarrow \theta_i^{t}-\frac{\eta}{\sigma_i^t} g_i^{t}\quad\sigma_i^t=\sqrt{\alpha\left(\sigma_i^{t-1}\right)^2+(1-\alpha)\left(g_i^{t}\right)^2}\]</span> <strong>Adam</strong>：RMSProp + Momentum</p><h3 id="learning-rate-scheduling">Learning Rate Scheduling</h3><p><img src="/img/李宏毅机器学习笔记/image-20230207211811638.png" alt="image-20230207211811638" style="zoom:33%;" /></p><p>随着训练进行，将学习率本身不断减小</p><p>Warm-up 黑科技，在训练初期逐渐增加<code>Learning_rate</code>，代表初期探索</p><h2 id="overfitting">Overfitting</h2><p>上述过程均检测正常，即 <code>training data</code> 中<code>loss</code> 较小，但 <code>testing data</code> 中<code>loss</code> 较大，一个极端的例子是</p><p>Training data: <span class="math inline">\(\left\{\left(x^1,\hat{y}^1\right),\left(x^2, \hat{y}^2\right), \ldots,\left(x^N,\hat{y}^N\right)\right\}\)</span> <span class="math display">\[f(x)=\left\{\begin{array}{cl}\hat{y}^i &amp; \exists x^i=x \\\text { random } &amp; \text { otherwise }\end{array} \quad\right.\]</span> model 越大，overfitting 的概率越大（另一种为mismatch，就算有更多的资料，训练集和测试集的分布不一样）</p><p><strong>解决办法：</strong></p><ul><li><p>Data augmentation，将图片水平翻转或者放大让训练集变大</p></li><li><p>Constrain，让模型限制在某些函数上</p></li><li><p>共用参数，限制参数 <span class="math display">\[\text{CNN}\in \text{Fully-Connected model}\]</span></p></li><li><p>N-fold Cross Validation，提高 private-data 准确率</p></li></ul><p>L2 正则化和 weight decay 是一样的，L2 正则就是权重衰减。</p>]]>
    </content>
    <id>https://learningrate1.com/2023/02/21/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E6%9D%8E%E5%AE%8F%E6%AF%85%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <link href="https://learningrate1.com/2023/02/21/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E6%9D%8E%E5%AE%8F%E6%AF%85%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <published>2023-02-21T15:19:36.000Z</published>
    <summary>mentor 觉得我机器学习知识不太够，听听李宏毅机器学习课程</summary>
    <title>Hongyi Li's Machine learning course notes</title>
    <updated>2023-02-21T15:19:36.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="论文" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%BA%E6%96%87/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<h1 id="t5">T5</h1><p>T5: <strong>Transfer Text-to-Text Transformer</strong></p><p>C4: <strong>Colossal Clean Crawled Corpus</strong></p><h2 id="架构择优">架构择优</h2><p><img src="/img/T5/image-20230212182549309.png" alt="image-20230212182549309" style="zoom:20%;" /></p><ul><li><strong>Encoder-Decoder</strong>（Seq2Seq，Encoder 看全体，Decoder看之前的，效果最好）</li><li><strong>Decoder</strong>（看之前）</li><li><strong>Prefix-LM</strong>（一部分全体一部分过去）</li></ul><p>其中两层数据之间的关联如下</p><p><img src="/img/T5/image-20230212185102519.png" alt="image-20230212185102519" style="zoom:25%;" /></p><h2 id="训练择优">训练择优</h2><p>顺次在以下并列选项中找到“最优解”，<font color='blue'>蓝色</font>代表“胜出”</p><p><img src="/img/T5/image-20230212194432962.png" alt="image-20230212194432962" style="zoom:20%;" /></p><h3 id="训练方式">训练方式</h3><ul><li><strong>语言模型式</strong>：从左到右预测</li><li><font color='blue'><strong>BERT 式</strong></font>：还原 MASK</li><li><strong>Deshuffling 式</strong>：还原打乱文本</li></ul><h3 id="掩盖策略">掩盖策略</h3><ul><li><strong>Mask</strong>：盖住 token</li><li><font color='blue'><strong>Replace spans</strong></font>：相邻 Mask合成一个特殊字符</li><li><strong>Drop</strong>：直接丢弃一些字符</li></ul><h3 id="掩盖比例">掩盖比例</h3><p>10%, <font color='blue'>15%</font>, <font color='blue'>25%</font>,<font color='blue'>50%</font> 的 MASK 比例</p><h3 id="掩盖长度">掩盖长度</h3><p>2, <font color='blue'>3</font>, 5, 10 的长度</p>]]>
    </content>
    <id>https://learningrate1.com/2023/02/09/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/T5/</id>
    <link href="https://learningrate1.com/2023/02/09/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/T5/"/>
    <published>2023-02-09T06:19:27.000Z</published>
    <summary>语言大模型开山之作，定海神针之三</summary>
    <title>Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer</title>
    <updated>2023-02-09T06:19:27.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="讨论" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%A8%E8%AE%BA/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<h1 id="discussion">2.1_Discussion</h1><h2 id="attention-is-all-you-need">Attention is all you need</h2><p><img src="/img/2.1_Discussion/image-20230202135855274.png" alt="image-20230202135855274" style="zoom:15%;" /></p><center><figcaption>The Transformer - model architecture</figcaption></center><h3 id="q-k-v-具体的理解以及在物理中的对应">Q, K, V具体的理解以及在物理中的对应</h3><p>key-value 为一本字典，query 为用户查询，通过和 key的<strong>匹配程度</strong>（不同注意力机制，加性注意力可以处理不等长的情况）去给value 分配相应的权重。 <span class="math display">\[\begin{array}{|c|c|}\hline \text { 加性模型 } &amp; \alpha\left(q, k_i\right)=v^T \tanh\left(W_k k_i+W_q q\right) \\\hline \text { 点积模型 } &amp; \alpha\left(q, k_i\right)=q k_i^T \\\hline \text { 缩放点积模型 } &amp; \alpha\left(q, k_i\right)=q k_i^T /\sqrt{d} \\\hline\end{array}\]</span><strong>认知层面</strong>：大脑带着<strong>目标</strong>（query）去理解一段信息时，不同<strong>信息片段</strong>（key）引起的<strong>脑电波</strong>（value）不同，注意力会更多地分配给关联程度更高的信息，反之亦然，最后得到的总脑电波在注意力分配下脑电波的加权求和。（做英语阅读文章）</p><p><strong>心理层面</strong>：在复杂环境下人类有效关注值得注意的点。</p><p><strong>量子层面</strong>：一个定态量子体系，不同<strong>定态波函数</strong>（key）对应的<strong>能级</strong>（value）不同，当其<strong>归一的体系状态</strong>（query）确定后，通过与不同的定态波函数作内积得到相应的比例分配，则该体系总能量则是按照比例分配下能级的加权求和。</p><h3 id="网络中的-add-的作用">网络中的 Add 的作用？</h3><p>Add 表示<strong>残差连接</strong>（ResidualConnection）用于防止<strong>梯度退化</strong>（由于回传通过链式法则，梯度在连乘的作用下趋于<span class="math inline">\(0\)</span>），每一层的输出为 <spanclass="math display">\[\text{LayerNorm} (x+\operatorname{Sublayer}(x))\]</span> 将模型从学习 <span class="math inline">\(x\longrightarrowf(x)\)</span> 到学习 <span class="math inline">\(x\longrightarrowf(x)-x\)</span>，深度神经网络在<strong>恒等映射</strong>上当层数变大时表现不好，该操作将其降为<span class="math inline">\(0\)</span> 使得效果变好。</p><h3 id="encoder-中-masked-的作用">Encoder 中 MASKED 的作用</h3><p>在编码的时候能看到整个句子，但是在解码的时候预测第 <spanclass="math inline">\(t\)</span> 个词时前面的词不能被看到，为<strong>auto-regressive</strong>，具体操作为矩阵计算之后把时刻 <spanclass="math inline">\(t\)</span> 之后的权重调整为 <spanclass="math inline">\(-\infty\)</span>，经过 softmax 之后概率趋于 <spanclass="math inline">\(0\)</span>。</p><h3 id="编码器和解码器的数据传递">编码器和解码器的数据传递</h3><p>编码器的输出作为 key 和 value 、解码器的输出作为 query进入解码器的第二个多头注意力层。</p><h3 id="multi-的作用">Multi 的作用</h3><p>投影 <span class="math inline">\(h\)</span>次到不同的子空间，增加权重用来学习，相当于给出 <spanclass="math inline">\(h\)</span>次学习的方式，允许模型在不同的子空间中学习相关的信息。 <spanclass="math display">\[f(X)=\operatorname{softmax}\left(\dfrac{X W_Q X^TW_K}{\sqrt{d_k}}\right) X W_V\]</span></p><h3 id="使用-layer-norm-的原因">使用 Layer-norm 的原因</h3><p>Batch-norm 是针对每个<strong>特征</strong>下的所有 batch（<strong>样本</strong>）正则化</p><p>Layer-norm 是针对每个 batch（<strong>样本</strong>）的所有<strong>特征</strong>正则化</p><p>而语言模型输入的样本<strong>长度不一样</strong>，如果对每个特征下做正则化，则长短不一不稳定，而对每个样本正则化则长短固定，更稳定。</p><h3 id="ffn-的作用">FFN 的作用</h3><p>单隐藏层，把 <span class="math inline">\(512\)</span> 维的向量投影到<span class="math inline">\(2048\)</span> 维向量，过一个 ReLU激活函数，再投影回 <span class="math inline">\(512\)</span>维向量，代表语义空间的转换。</p><h3 id="总参数">总参数</h3><p>词典大小为 <span class="math inline">\(V\)</span>，记录隐藏层维度为<span class="math inline">\(H\)</span>，嵌入层参数大小为 <spanclass="math inline">\(V\cdot H\)</span>，对 <spanclass="math inline">\(Q,K,V\)</span> 连接在一起的总投影矩阵大小 <spanclass="math inline">\(3H^2\)</span>，计算完之后还有一个线性投影 <spanclass="math inline">\(H^2\)</span>，再连上一个 MLP，参数为 <spanclass="math inline">\(4H\cdot H\)</span>，前后两个共 <spanclass="math inline">\(8H^2\)</span>，则总参数量为 <spanclass="math inline">\(\boxed{VH+12H^2}\)</span>。</p><h3 id="稀疏注意力sparse-attention的分类">稀疏注意力（<strong>SparseAttention</strong>）的分类</h3><p><img src="/img/2.1_Discussion/image-20230203211311915.png" alt="image-20230203211311915" style="zoom:33%;" /></p><p><strong>Longformer</strong>：处理超长文本，将<strong>局部性</strong>和<strong>全局</strong>的影响结合起来</p><ol type="1"><li>Sliding window attention (band attention)</li><li>Dilated sliding window attention</li><li>Global+Sliding attention</li></ol><p><img src="/img/2.1_Discussion/image-20230203212030556.png" alt="image-20230203212030556" style="zoom:30%;" /></p><p><strong>Big Bird</strong>：Random attention + Window attention +Global attention</p><p><strong>Routing Transformer</strong>：使用 K-means 聚类 query 和keys</p><p><strong>Reformer</strong>：Multi-round Locality SensitiveHashing，<span class="math inline">\(O(n\log n)\)</span></p><p>稀疏性的缺点</p><ul><li>Softmax 无法很好的稀疏化</li><li>没有严格的理论证明稀疏注意力有足够强的表达能力</li></ul><h2 id="bert">BERT</h2><p>增加一个输出，ELMo基于<strong>双向</strong>和基于特征，但使用的架构是循环神经网路 ，GPT使用 <strong>Transformer</strong> 架构</p><p><strong>迁移学习</strong>：在一个比较大的数据集上训练，然后拿到其他地方用（常用在计算机视觉）</p><h3 id="cls-有什么作用">[CLS] 有什么作用？</h3><p>代表 Classification，该词在分类任务能在 Attention之后对所有词作加权平均，能够更加准确地综合整个句子的信息，方便在分类等任务上进行微调。</p><h3 id="位置编码和-transformer-的不同">位置编码和 Transformer的不同</h3><p>使用训练出来的绝对位置编码</p><h3 id="mask-比例">Mask 比例</h3><p>在掩码预测任务中，输入是句子中遮掩掉几个词经过 BERT网络输出每个字的向量表征。训练中 <strong>15%</strong> 的词全被 Mask掉，由于需要微调的时候新输入的数据和之前的数据不同，从而使用 0.8:0.1:0.1进行替换并做 Ablation study。</p><h1 id="discussion-1">2.9 discussion</h1><h2 id="bert-后续问题">BERT 后续问题</h2><h3 id="l-和-h-在小模型和大模型上的依赖关系">L 和 h在小模型和大模型上的依赖关系</h3><h3 id="eisum-的作用">eisum() 的作用</h3><p>爱因斯坦求和，在 <code>model</code> 中叠加位置编码时使用，为<code>layer-multiplication</code> 和词向量 <code>dot-product</code>的综合，代码和理解如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">relative_position_scores = torch.einsum(<span class="hljs-string">&quot;bhld,lrd-&gt;bhlr&quot;</span>, query_layer, positional_embedding)<br></code></pre></td></tr></table></figure><p><span class="math display">\[\begin{aligned}Q(K)&amp;: \text{batch}\times \text{head} \times \text{layer}\times\text{dimension}\\P&amp;: \text{layer}\times \text{relative} \times \text{dimension}\\R&amp;: \text{batch}\times \text{head} \times \text{layer} \times\text{realative}\end{aligned}\]</span></p><p>固定 <span class="math inline">\(l\)</span>层，在词向量维度上作<strong>内积</strong>，得到在每个样本的每个头上的词向量关联四维张量<span class="math display">\[R_{\text{bhlr}}=\sum_{d}Q_{bhld}P_{lrd}\]</span></p><h3 id="mask-的打法">Mask 的打法</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">attention_mask = torch.cat([attention_mask, attention_mask.new_zeros((attention_mask.shape[<span class="hljs-number">0</span>], <span class="hljs-number">1</span>))], dim=-<span class="hljs-number">1</span>)<br></code></pre></td></tr></table></figure><p>给 attention_mask 每个维度补上 <spanclass="math inline">\(0\)</span>，这里主要是 <code>padding</code>操作。</p><p>上述操作在构建输入，预测下一个词的输入</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">extended_attention_mask = (<span class="hljs-number">1.0</span> - extended_attention_mask) * torch.finfo(dtype).<span class="hljs-built_in">min</span><br></code></pre></td></tr></table></figure><p>上述操作为打 [MASK] 的方法，其中 <code>torch.finfo(dtype).min</code>代表最小值，在 <code>pytorch</code> 中源码如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python">In [*]: torch.finfo().<span class="hljs-built_in">min</span><br>Out[*]: -<span class="hljs-number">3.4028234663852886e+38</span><br></code></pre></td></tr></table></figure><h3 id="adam-原理">Adam 原理</h3><p>其基于<strong>随机梯度下降</strong>和<strong>指数加权平均数</strong></p><p>Adam在 SGD基础上，为每个参数梯度增加了<strong>一阶动量</strong>（momentum）和<strong>二阶动量</strong>（variance）。</p><p><strong>Require</strong>: <span class="math inline">\(\alpha\)</span>: Stepsize</p><p><strong>Require</strong>: <span class="math inline">\(\beta_1,\beta_2 \in[0,1)\)</span> : Exponential decay rates for the momentestimates</p><p><strong>Require</strong>: <spanclass="math inline">\(f(\theta)\)</span> : Stochastic objective functionwith parameters <span class="math inline">\(\theta\)</span></p><p><strong>Require</strong>: <spanclass="math inline">\(\theta_0\)</span> : Initial parameter vector</p><p>​ <span class="math inline">\(m_0 \leftarrow 0\)</span> (Initialize<span class="math inline">\(1^{\text {st }}\)</span> moment vector)</p><p>​ <span class="math inline">\(v_0 \leftarrow 0\)</span> (Initialize<span class="math inline">\(2^{\text {nd }}\)</span> moment vector)</p><p>​ <span class="math inline">\(t \leftarrow 0\)</span> (Initializetimestep)</p><p>​ <strong>while</strong> <span class="math inline">\(\theta_t\)</span>not converged <strong>do</strong></p><p>​ <span class="math inline">\(t \leftarrow t+1\)</span></p><p>​ <span class="math inline">\(g_t \leftarrow \nabla_\thetaf_t\left(\theta_{t-1}\right)\)</span> (Get gradients w.r.t. stochasticobjective at timestep <span class="math inline">\(t\)</span> )</p><p>​ <span class="math inline">\(m_t \leftarrow \beta_1 \cdotm_{t-1}+\left(1-\beta_1\right) \cdot g_t\)</span> (Update biased firstmoment estimate)</p><p>​ <span class="math inline">\(v_t \leftarrow \beta_2 \cdotv_{t-1}+\left(1-\beta_2\right) \cdot g_t^2\)</span> (Update biasedsecond raw moment estimate)</p><p>​ <span class="math inline">\(\widehat{m}_t \leftarrow m_t/\left(1-\beta_1^t\right)\)</span> (Compute bias-corrected first momentestimate)</p><p>​ <span class="math inline">\(\widehat{v}_t \leftarrow v_t/\left(1-\beta_2^t\right)\)</span> (Compute bias-corrected second rawmoment estimate)</p><p>​ <span class="math inline">\(\theta_t \leftarrow \theta_{t-1}-\alpha\cdot \widehat{m}_t /\left(\sqrt{\hat{v}_t}+\epsilon\right)\)</span>(Update parameters)</p><p>​ <strong>end while</strong></p><p>​ <strong>return</strong> <spanclass="math inline">\(\theta_t\)</span> (Resulting parameters)</p>]]>
    </content>
    <id>https://learningrate1.com/2023/02/09/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%BC%80%E5%B1%B1%E4%B9%8B%E4%BD%9C%E8%AE%A8%E8%AE%BA%E7%AC%94%E8%AE%B0/</id>
    <link href="https://learningrate1.com/2023/02/09/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E5%BC%80%E5%B1%B1%E4%B9%8B%E4%BD%9C%E8%AE%A8%E8%AE%BA%E7%AC%94%E8%AE%B0/"/>
    <published>2023-02-09T02:27:42.000Z</published>
    <summary>讨论过程中从不同角度深入理解 Transformers 和 BERT</summary>
    <title>NLP introductory discussion notes</title>
    <updated>2023-02-09T02:27:42.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="论文" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%BA%E6%96%87/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<h1id="mathbfbtextidirectional-mathbfetextncoder-mathbfrtextepresentationstextfrom-mathbfttextransformers"><spanclass="math inline">\(\mathbf{B}\text{idirectional}\\mathbf{E}\text{ncoder}\ \mathbf{R}\text{epresentations}\text{from}\\mathbf{T}\text{ransformers}\)</span></h1><h2 id="key-points">Key-points</h2><p><span class="math inline">\((1)\)</span><strong>双向</strong>预训练；<span class="math inline">\((2)\)</span>统一模型解决任务；<span class="math inline">\((3)\)</span>无监督训练方式。</p><p><span class="math display">\[\begin{cases}\mathbf{MLM} \text{: masked language model}\\\mathbf{NSP} \text{: next sentence prediction}\end{cases}\]</span></p><ul><li>从 feature-based 到 fine-tuned，模型参数从不可变到可变。</li><li>大规模数据集上无监督预训练加目标数据集微调，<strong>上下文</strong>均可获取。</li><li>采用 Wordpiece 算法得到 <span class="math inline">\(30000\)</span>个词汇</li><li>embedding 有 Token, Segment, Position 三种。</li></ul><p>在 <span class="math inline">\(11\)</span> 个 NLP 任务上表现好。</p><hr /><h2 id="ablation-study">Ablation study</h2><p>该实验主要通过对比实验说明不同关键点融合的好处，本质思想是奥卡姆剃刀原理。</p><ul><li><p><strong>预训练</strong>：没有 NSP 以及没有 NSP 且加上 LTR都会使结果变差</p></li><li><p><strong>大小</strong>：原模型当大小变大时表现未必好，而该模型效果随模型大小增加而增加</p></li><li><p><strong>训练步数</strong>：收敛速度较慢</p></li><li><p><strong>Mask比例</strong>：概率 0.8:0.1:0.1 [MASK], 替代,保持不变时最好</p></li></ul>]]>
    </content>
    <id>https://learningrate1.com/2023/01/24/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/BERT_Pre-training_of_Deep_Bidirectional_Transformers_for_Language_Understanding/</id>
    <link href="https://learningrate1.com/2023/01/24/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/BERT_Pre-training_of_Deep_Bidirectional_Transformers_for_Language_Understanding/"/>
    <published>2023-01-24T15:17:22.000Z</published>
    <summary>语言大模型开山之作，定海神针之二</summary>
    <title>BERT, Pre-training of Deep Bidirectional Transformers for Language Understanding</title>
    <updated>2023-01-24T15:17:22.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="论文" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%BA%E6%96%87/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<h1 id="开山之作text-mathbfattention-is-all-you-need"><spanclass="math inline">\(开山之作\text{:}\ \mathbf{Attention\ is\ all\ you\need}\)</span></h1><ol type="1"><li><p><strong>CNN</strong>并行但只能学习有限信息（<strong>时间短、效果差</strong>）</p><p>学习远程句子困难，例如 making ... more difficult，空间受限。</p></li><li><p><strong>RNN</strong>学习全局信息但不能并行（<strong>时间长、效果好</strong>）</p><p>输入顺序来分解计算 <span class="math inline">\(h^{(t)}=f(h^{(t-1)},x^{(t)})\)</span>，时间受限。</p></li><li><p><strong>Transformer</strong> 使用 self-attention 结合 Multi机制，并行化+学习全局信息（<strong>时间短、效果好</strong>）</p></li></ol><hr /><h2 id="mathbfscaled-dot-product-流程"><spanclass="math inline">\(\mathbf{Scaled-dot\ product}\)</span> 流程</h2><p>令 <span class="math inline">\(I\)</span> 为 <spanclass="math inline">\(\begin{bmatrix}a^1 &amp;a^2 &amp; \cdots &amp;a^n\end{bmatrix}\)</span>，权重 <spanclass="math inline">\(I=WX\)</span>。每个 <spanclass="math inline">\(\text{query, key and value}\)</span> 分别为 <spanclass="math inline">\(q^i, k^i, v^i\)</span>，组装为 <spanclass="math inline">\(Q,K,V\)</span>，对应权重 <spanclass="math inline">\(Q=W^{q}I,K=W^{k}I,V=W^{v}I\)</span>，<spanclass="math inline">\(\text{query}\)</span> 和 <spanclass="math inline">\(\text{key}\)</span> 点积并正则化得到</p><p><span class="math display">\[\alpha_{i j}=\frac{\left\langle q^i,k^j\right\rangle}{\sqrt{d_k}}\tag{1}\]</span></p><p>并计算 <spanclass="math inline">\(\text{softmax}_{ij}=\hat{\alpha}_{ij}=\dfrac{\exp{\alpha_{ij}}}{\sum_{j} \exp(\alpha_{ij})}\)</span>，最后计算 <spanclass="math inline">\(B=\hat{A}V\)</span> 得到输出。</p><p>如果有 <span class="math inline">\(h\)</span>层，由于为线性空间变换，可以拼接矩阵然后作相同计算允许模型在<strong>不同的子空间</strong>中学习相关的信息，主要目的是抽取更加丰富的特征信息。</p><p>这样就避免了循环神经网络中运算有先后的问题，同时复杂度没有卷积神经网络那么大（矩阵乘法运算也可以优化，论文中维度取为<span class="math inline">\(512,1024\)</span> 等 <spanclass="math inline">\(2\)</span>的幂次便于分治算法，详见参考算法导论中对贪心调度器的讨论）</p><hr /><h2 id="时间复杂度降低">时间复杂度降低</h2><p>在卷积神经网络中，由于要保证输出矩阵为方阵（每个词表示向量间的权重关系，一共<span class="math inline">\(n\)</span> 个词，从而 <spanclass="math inline">\(n\cdot d^2\)</span>个元素），而方阵中的每个元素都是通过卷积核运算得到的，令卷积核的大小为<span class="math inline">\(k\)</span>，则每个元素的浮点运算次数（$$）为 <span class="math inline">\(k\cdot n\cdot d^2\)</span></p><p>在注意力机制中，各矩阵维数 <span class="math inline">\(Q, K, V: n\times d\)</span></p><ul><li><span class="math inline">\(Q K^T: n \times d\)</span> 与 <spanclass="math inline">\(d \times n\)</span> 运算，得到 <spanclass="math inline">\(n \times n\)</span> 矩阵，复杂度为 <spanclass="math inline">\(O\left(n^2 d\right)\)</span></li><li><span class="math inline">\(\text{softmax}\)</span>计算:对每行复杂度为 <span class="math inline">\(O(n)\)</span>，则 <spanclass="math inline">\(n\)</span> 行的复杂度为 <spanclass="math inline">\(O\left(n^2\right)\)</span></li><li>加权和: <span class="math inline">\(n \times n\)</span> 与 <spanclass="math inline">\(n \times d\)</span> 运算，得到 <spanclass="math inline">\(n \times d\)</span> 矩阵，复杂度为 <spanclass="math inline">\(O\left(n^2 d\right)\)</span></li></ul><p>在本文中将三者矩阵拆成 <span class="math inline">\(h=8\)</span>个头用于并行计算，相当于第一个 <span class="math inline">\(n\)</span>减少为 <span class="math inline">\(r\)</span>，复杂度为 <spanclass="math inline">\(O(rnd)\)</span>。<spanclass="math inline">\(\text{Multi}\)</span> 由此而来，不同的 <spanclass="math inline">\(\text{Multi}\)</span> 可以学习到不同的状态。</p><hr /><h2 id="正则化系数推导">正则化系数推导</h2><p>在论文中结合其注释可以推导，由公式 <spanclass="math inline">\((1)\)</span> 中为两个维度为 <spanclass="math inline">\(d_k\)</span> 的向量点积，从线性空间的角度从 <spanclass="math inline">\(\mathbb{R}^n\longrightarrow\mathbb{R}\)</span>，其误差会放大，假设 <spanclass="math inline">\(q^{\prime}=q-\overline{q}\)</span><spanclass="math inline">\(,v^{\prime}=v-\overline{v}\)</span>，可以理解为中心偏差计算两者点积的期望和方差，假设两者方差相同</p><p><span class="math display">\[\begin{cases}\displaystyle E\left[\sum_{i}q_{i}v_{i}\right]=\sum_{i}E[q_i]E[v_i]=0\\\displaystyleE\left[\left(\sum_{i}q_{i}v_{i}\right)^2\right]=\sum_{i}E\left[q_{i}^2\right]E\left[v_{i}^2\right]=d_{k}\text{Var}(q_i)^2\end{cases}\tag{2}\]</span></p><p>如果除以 <spanclass="math inline">\(\sqrt{d_{k}}\)</span>，则能保证方差不变，从而噪声不会展平，效果会更好。（这里保证初始的方差为<span class="math inline">\(1\)</span>，见后面的预处理）</p><hr /><h2 id="mathbfposition-编码增加位置信息"><spanclass="math inline">\(\mathbf{position}\)</span> 编码增加位置信息</h2><p>论文中对输入向量增加了位置编码张量</p><p><span class="math display">\[\begin{aligned}P E_{(p o s, 2 i)} &amp; =\sin \left(p o s / 10000^{2 i / d_{\text{model }}}\right) \\P E_{(p o s, 2 i+1)} &amp; =\cos \left(p o s / 10000^{2 i / d_{\text{model }}}\right)\end{aligned}\tag{3}\]</span></p><p>在 NLP 任务中能学习到以 10000个分词为跨度的权重已经达到极限了，相当于这些平面波构成词向量所有组合的“完备空间”。至于分奇偶是要求当位置变化k 时，由三角函数公式均可被线性表出。</p><p>位置编码也可以通过训练获得，两者效果相似。</p><hr /><h2 id="隐藏层的正则化处理">隐藏层的正则化处理</h2><p>这里运用中心极限定理将数据正则化，其中分布中添加的 <spanclass="math inline">\(\epsilon&gt;0\)</span> 是为了防止分母为 <spanclass="math inline">\(0\)</span>。</p><p><span class="math display">\[\begin{aligned}\mu_L&amp;=\frac{1}{m} \sum_{i=1}^m x_i \\\delta^2&amp;=\frac{1}{m} \sum_{i=1}^m\left(x_i-\mu\right)^2 \\ LN\left(x_i\right)&amp;=\alpha\frac{x_i-\mu_L}{\sqrt{\delta^2+\epsilon}}+\beta\end{aligned}\tag{4}\]</span></p><p>这样就能保证公式 <span class="math inline">\((2)\)</span>中的方差不变。</p>]]>
    </content>
    <id>https://learningrate1.com/2023/01/19/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/Attention_is_all_you_need_notes/</id>
    <link href="https://learningrate1.com/2023/01/19/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/Attention_is_all_you_need_notes/"/>
    <published>2023-01-19T02:27:42.000Z</published>
    <summary>语言大模型开山之作，定海神针之一</summary>
    <title>Attention is all you need</title>
    <updated>2023-01-19T02:27:42.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="NLP" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/"/>
    <category term="讨论" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/NLP/%E8%AE%A8%E8%AE%BA/"/>
    <category term="NLP" scheme="https://learningrate1.com/tags/NLP/"/>
    <content>
      <![CDATA[<h1 id="hugging-face-笔记">Hugging face 笔记</h1><h2 id="transformer-models">Transformer models</h2><p><code>pipeline()</code>第一个参数为模型用途，第二个参数为社区中的模型名字，从而获取模型各种接口</p><p><img src="/img/Hugging face 笔记/transformers_chrono.svg" alt="A brief chronology of Transformers models." style="zoom:30%;" /></p><p>GPT, BERT, BART/T5 使用不同策略，回归、编码、seq2seq</p><p><em>causal/masked language modeling</em> 预测后面/中间</p><p>预训练白手起家，微调增加限定，领域适应</p><p>Pretrained -- fine-tuning the knowledge inside the model<strong>transfer</strong></p><p>Transferring the knowledge of a pretrained model to a new model byinitializing the second model <strong>with the first model'sweight</strong>s.</p><p>编码（理解语句）解码（输出语句）对应不同需求</p><ul><li>仅编码（预训练遮盖词语）：词句分类、命名识别</li><li>仅解码（只知道前面的词语）：文本生成</li><li>编码+解码（T5单一单词随机遮盖不同跨度单词）：翻译、总结</li></ul><p>语言模型通常是自监督的（通过预测下一个单词或者完形填空）</p><p>BERT 编码架构，mask words，擅长事实性问题</p><p>GPT 解码架构，自回归模型</p><p>T5 解码-编码架构，多语言架构</p><h2 id="architecture-of-transformers">Architecture of Transformers</h2><p>Tokenizers 将自然语言转换成 IDTensor，将参数传递给模型（一般维度很大）做预测，Tokenizers 再次将 IDs转换为自然语言</p><p>分词三种：<strong>单词、字母、子单词</strong></p><p>padding 需要结合 attention-masking 保证输出的稳定性</p><p>model-head 将预测结果转换为目标输出</p><h2 id="finetune-models">Finetune models</h2><p>分批次使用 <code>Dataset.map(model, batch=true)</code>增加缓存，在多线程下以及多次访问下加速</p><p>若训练前类型和模型不匹配，训练头部会随机增加权重</p><h2 id="dataset-library">Dataset library</h2><p><code>Dataset.map()</code> 操作数据，使用 <code>lambda</code>函数省去较小函数的构造</p><p>常用 <code>filter()</code> 和 <code>map()</code> 函数</p><p><strong>内存映</strong>射是指将一个文件映射成应用程序地址空间内某个地址范围</p><h2 id="tokenizer-library">Tokenizer library</h2><p>使用 <code>(example['train'] for example in dataset)</code>获得生成器，减少内存消耗</p><p>DistilBERT，使用知识蒸馏技术</p><p>tokenizer 不同</p><p><strong>规范化</strong>（初始处理多空格、Unicode）、<strong>预标记化</strong>（拆分为单词）、<strong>后处理</strong>（掩码）</p><table><colgroup><col style="width: 34%" /><col style="width: 30%" /><col style="width: 34%" /></colgroup><thead><tr><th>BERT</th><th>GPT</th><th>T5</th></tr></thead><tbody><tr><td>忽略双空格</td><td>不忽略双空格</td><td>不忽略双空格</td></tr><tr><td>空格删除，不可逆</td><td>空格替换为 Ġ，可逆</td><td>空格替换为 _，可逆</td></tr><tr><td><strong>Wordpiece 算法</strong></td><td><strong>BPE 算法</strong></td><td><strong>Unigram 算法（Viterbi 算法）</strong></td></tr><tr><td><span class="math inline">\(\displaystyle\max_{i}\left(\dfrac{N_{i,\text{merge}}}{N_{i,\text{left}}\cdotN_{i,\text{right}}}\right)\to \{V\}\)</span></td><td><span class="math inline">\(\displaystyle\max_{i}(N_{i,\text{merge}})\to \{V\}\)</span></td><td><span class="math inline">\(\displaystyle\min\left[ -N\log \left(\prod_{\bigsqcup_{j}\text{Sub}_j\in\{\text{Words}\}}P(\text{Sub}_j)\right)\right]\)</span></td></tr><tr><td>子串</td><td>子串和合并规则</td><td>子串</td></tr><tr><td>选取最长有效子串</td><td>合并有效分词</td><td>选取损失最小有效子串</td></tr><tr><td>词汇量小</td><td>词汇量小</td><td>词汇量大</td></tr></tbody></table><h2 id="main-nlp-tasks">Main NLP tasks</h2><p><strong>翻译任务</strong>：BLEU 分数衡量（双语互译质量评估辅助工具）①precision生成答案中有多大比例重合，存在<strong>常用词干扰</strong>，例如 <spanclass="math inline">\(\text{the the the the the the the}\)</span> 分数为<span class="math inline">\(100\%\)</span> ② modified precision考虑词语在参考翻译中出现次数截断计数 ③ 加入惩罚，避免词语短得分高 ③n-gram 增加翻译流畅性，与前者求几何平均 ④ Sentence brevity penalty短惩罚 <span class="math display">\[\mathrm{BP}=\begin{cases}1 &amp; \text { if } c&gt;r \\e^{(1-r / c)} &amp; \text { if } c \leq r\end{cases} ,\mathrm{BLEU}=\mathrm{BP} \cdot \exp \left(\sum_{n=1}^N w_n \logp_n\right) .\]</span> <strong>摘要</strong>：ROUGE 分数，recall标准答案中有多大比例重合，precision生成答案中有多大比例重合，两者综合考量，取调和平均 <spanclass="math inline">\(F_1=\dfrac{2P_1P_2}{P_1+P_2}\)</span>。</p><p><strong>因果推断模型</strong>：数据集中设置<code>max_length</code></p><p><strong>问答系统</strong>：合并为 [CLS] question [SEP] context [SEP]作为训练输入</p><h2 id="ask-for-questions">Ask for questions</h2><p>CUDA 出 bug 在 CPU 上跑，因为前者是异步的</p><p>过拟合用于 debug，损失函数降为 0</p><h1 id="hugging-face-course-问题">Hugging face course 问题</h1><h2 id="一bertgptt5-的架构和区别">一、BERT/GPT/T5 的架构和区别</h2><p><span class="math display">\[\begin{array}{|c|c|c|}\hline \text { BERT } &amp; \text { GPT } &amp; \text { T5 } \\\hline \text { incoder 架构 } &amp; \text { decoder 架构 } &amp; \text {incoder-decoder 架构 } \\\hline \text { 编码模型 } &amp; \text { 自回归模型 } &amp; \text {Seq2seq 模型 } \\\hline \text { 获取所有可见词 } &amp; \text { 获取前面词语 } &amp; \text{ 获取前面词语 } \\\hline \text { 分类, Full-mask } &amp; \text { 文本生成 } &amp; \text {跨多语言, 翻译总结 } \\\hline\end{array}\]</span></p><p>tokenizer 预训练方法不同（子词算法不同）</p><hr /><h2id="二不同架构一定对应不同任务吗">二、不同架构一定对应不同任务吗？</h2><p>不同架构可以用来训练非匹配任务，但效果欠佳；当架构与任务类型匹配时，效果好。</p><h2 id="三tokenizer-怎么拿到词语和数字的关系">三、Tokenizer怎么拿到词语和数字的关系？</h2><ol type="1"><li><strong>规范化</strong>（初始处理多空格、Unicode）、<strong>预标记化</strong>（拆分为单词）</li><li>使用不同字词<strong>切分算法</strong>：按<strong>字词</strong>划分，按<strong>字符</strong>划分，按<strong>子串</strong>划分（例如Wordpiece, BPE, Unigram 算法）结合语料训练构建得到词汇表（一般不超过<span class="math inline">\(50000\)</span> 个词汇）</li><li>依据词汇表以及对应策略分词得到<strong>切片</strong>（##前缀处理）</li><li><strong>后处理</strong>：标记掩码、偏移映射（每个标记的文本跨度）等索引</li></ol><hr /><h2id="四掩码模型的微调方法以及种类">四、掩码模型的微调方法以及种类</h2><ol type="1"><li>导入数据</li><li>切片张量化（小的 <code>max_length</code>占用内存小，有重叠划分）</li><li>随机屏蔽（设定屏蔽比例，<strong>标记掩码</strong>和<strong>全词掩码</strong>）</li></ol><p>衡量方法：perplexity，交叉熵指数，数值越小意味着语法正确的句子概率更高</p><p><span class="math display">\[\operatorname{perplexity}(S)=p\left(w_1, w_2, w_3, \ldots,w_m\right)^{-1 / m}\]</span></p><blockquote><p>困惑度 perplexity可以理解为，如果每个时间步都根据语言模型计算的概率分布随机挑词，那么平均情况下，挑多少个词才能挑到正确的那个。</p></blockquote><hr /><h2 id="五各种衡量指标">五、各种衡量指标</h2><p><span class="math display">\[\begin{array}{|c|c|c|}\hline \text { BLEU } &amp; \text { ROUGE } &amp; \text { perlexity } \\\hline \text { 翻译 } &amp; \text { 摘要总结 } &amp; \text { 掩码模型 }\\\hline \text { 常用词干扰、长度短惩罚 } &amp; \text { recall + precision} &amp; \text { 交叉熵指数 } \\\hline \mathrm{BP} \cdot \exp \left(\sum_{n=1}^N w_n \log p_n\right)&amp; \dfrac{2 P_1 P_2}{P_1+P_2} &amp; p\left(w_1, \cdots,w_m\right)^{-1 / m} \\\hline \text { 高代表准确、流畅 } &amp; \text { 高代表准确、精炼 } &amp;\text { 高代表偏离统计模型 } \\\hline\end{array}\]</span></p>]]>
    </content>
    <id>https://learningrate1.com/2023/01/14/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/Hugging%20face%20%E7%AC%94%E8%AE%B0/</id>
    <link href="https://learningrate1.com/2023/01/14/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/Hugging%20face%20%E7%AC%94%E8%AE%B0/"/>
    <published>2023-01-14T12:37:29.000Z</published>
    <summary>寒假期间学习 Hugging face 所作笔记以及 mentor 提出问题的解答</summary>
    <title>Notes of Hugging face</title>
    <updated>2023-01-14T12:37:29.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="总结" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/"/>
    <category term="大二" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%BA%8C/"/>
    <category term="总结" scheme="https://learningrate1.com/tags/%E6%80%BB%E7%BB%93/"/>
    <content>
      <![CDATA[<h2 id="学业">学业</h2><p>还是照例谈谈这学期的学业情况，大二上学期一共上了 <spanclass="math inline">\(12\)</span> 门课程，共 <spanclass="math inline">\(31\)</span>学分（按课序号排序，不分先后），记了两门 <spanclass="math inline">\(PF\)</span>，一门多音，一门马原，实际只有 <spanclass="math inline">\(25\)</span> 学分。</p><p><strong>Matlab与科学计算</strong>，一门随便报的任选课，主要是想学一下Matlab，期末写了一个随机游走的模拟程序并对曲线进行模拟，曲线形状=为反比例函数，查找了很多资料提到平均自由场理论，但由于时间原因，没能深入了解。交了报告之后助教批改了，私聊之后助教说我大作业写得不错。这门课平常主要是对Matlab的各种功能加以介绍，开课老师是航院的，非常典型的工科院系了，说来惭愧，一学期只去听了第一节课，后面基本就是面向作业学习，不过还是学到Matlab 很多东西，之后也用 Matlab 处理实验报告，也算是有一定收获吧。</p><p><strong>多元文化中的音乐现象</strong>，听说是艺术课组中非常好的课了，任选要砸一志愿，而我当时砸的刘慧凝老师的沟通表达与基础，结果掉了，白给。秋季开学的时候跟lrf 一起手选，结果有半天的时间差吧，导致只有我手选上了，lrf比较遗憾没有选上。后来上课遇到 qqc 和lfh，之后课堂展示的时候就跟他们组了队，算是一种比较特殊的组队方式了，一般都是随机分配。我们以jazz为载体，结合疫情放开编了一个小话剧。线上播放提前录制的视频效果欠佳，还好有lzc 公子哥打了个圆场，总体印象应该不错。期间我跟 qqc由于对艺术一窍不通，就主要负责联系淘宝商家剪辑视频，给视频当中的画面加上微信发送信息。</p><p>前八周的时候罗老师讲卡农的时候还是比较有感触，加上忙的时候自己平常也喜欢听古典音乐集中精力，就以罗老师结尾“<ahref="/2022/12/28/生活/多元文化中的音乐现象课程报告/">你眼中的卡农</a>”为题，写了一篇报告。由于上学期被诗词这种偏艺术创作的课程背刺，这学期疫情可以有两门课程记PF ，我就把这门课记了PF。结果前几天上树洞发现可以在网络学堂已批改作业里面查看分数，原来罗老师设置了不公布分数，其实在HTML 里面就是加了一句<code>style='display:none'</code>，内容还是可以在网页源代码里面看到，结果就看到自己拿了<span class="math inline">\(97\)</span>分，有点小遗憾吧。写报告的时候我已经记了PF，也许就是比较真诚的心里话打动了罗老师吧，后来她又调整为 <spanclass="math inline">\(93\)</span>分，不知道是计算了课堂展示，还是单纯的调分，反正 P 了一个 4.0……</p><p><strong>复变函数</strong>，物理系 lys老师开设的前八周课程，老师上课挺幽默的，最后几节课还给我们透露了期末考题方向，半期考试相当于就是这门课的期末考试，五个大题，<ahref="/pdf/复变函数期末考试回忆版.pdf">回忆版</a>在此。考试的时候还是有一点小慌，毕竟题量小、分值大，稍不注意容易翻车。当时留数定理第二个题差点没有考虑到挖去两个点，不过总体还行。考完期末之后还可以提交小论文加分，听说之前疫情期间松神挂过将近三分之一的同学，还是比较恐怖。</p><p>这门课学到的知识倒是基本快忘了，毕竟计算居多，不过松神教会我们如何使用Mathematica 软件，这确实是工科专业必备软件了，djl 他们大一上费曼物理学(1)就要用这个数学专业软件，还是挺有趣的。关于松神还有一些人物轶事，可以看看这篇<ahref="https://news.ifeng.com/c/7fYQffQwnln">新闻</a>。他还教大学物理、分析力学、数理方程、基础物理学(3)，是个比较顶的老师，江湖有“四大松学"之说。他的研究方向为粒子物理和量子计算，算是理想中真正搞物理的大佬了。</p><p><strong>基础物理实验(2)</strong>，未央专属课程，老师跟基物实验 (1)一样，总之大家都是怨声载道的。有了上学期 <spanclass="math inline">\(\LaTeX\)</span> 经验，这学期找 gha 要了 typora的仿论文样式，写实验报告快了很多，加上这学期有一定的 Matlab知识，绘图什么的不在话下。前半学期最忙的时候可以当天做完实验当天交实验报告，报告均在<span class="math inline">\(90\)</span>分以上。塞曼效应那次实验遇到环境学院的lhgg，挺好说话的助教，在跟他交流了有关磁场和电流的变化特性之后，他提示我往磁化曲线方向去考虑，由此实验报告有了亮点，第一次在基础物理实验中取得<span class="math inline">\(95+\)</span> 的成绩。这门课也罕见地拿到了<span class="math inline">\(A\)</span> 的等级，也很欣慰。</p><p><strong>基础物理实验(3)</strong>，未央电的专属课程，当时看自己还有学分就顺便上了，这个实验主要是电磁学相关，老师是gxb。老师给分超好，次次 <spanclass="math inline">\(95+\)</span>，最后一次实验报告由于涉及计算机编程模拟同轴光子晶体，给了$100 $ 分，报告详细内容可以看<ahref="/2022/12/15/数理基础/同轴光子晶体实验报告/">这篇</a>，最后当然顺利拿到<spanclass="math inline">\(A\)</span>，这学期两门基物实验课双开，从第四周开始几乎每周都有实验，尤其是中午一点半的实验真的犯困，在巨大的压力之下还是顶住了，本科物理实验就此结束咧。</p><p><strong>二年级男生乒乓球</strong>，wx老师的的乒乓球课，由于自己从小学到乒乓球打到现在，对弧圈、左右旋、上下旋有一定概念，对我来说还算比较轻松。不过还是有些大佬，他们发球我需要一定时间才能破解。之后疫情转为线上授课了，之后的比赛就取消了，转为录制乒乓操视频。这学期要测试<span class="math inline">\(3\text{km}\)</span>以及立定跳远，成绩没有去年好了，主要是今年学业压力太重了。。</p><p><strong>量子力学(1)</strong>，cx老师的，主要是看到培养方案大三下这门课必修，就提前学习，反正如果半期考试考差了就退课，结果半期考试巨简单，考了<spanclass="math inline">\(99\)</span>，就还是继续撑下来。量子力学说实话国内没有讲得很好的教材，这门课用的曾谨言《量子力学教程》，书如作者其名，写得跟数学书有得一拼，用公理-引理-定理-例题书写逻辑，门槛很高，不过认真读下来还是有很多收获。</p><p>不过赵凯华的《量子物理》简直就是玄学，各种描述性的文字，我们学习量子力学又不是去调用量子力学API，而是学习背后的逻辑，整本书甚至都称不上量子力学的说明文档，连调用层级都是乱的，总之这本书骂声一片。赵凯华的《热学》那更是玄学，个人觉得热学的公理化不是很能让人信服，热学是更偏向唯象的一门学科，不过深入到统计部分后会好一些。扯远了，总之国内的教材一言难尽，还是多参考国外的教材吧。</p><p>前半期主要是涉及薛定谔方程以及相关算符的计算，半期考试难度上甚至达不到基物。后半期收获很多，本质上是物理学家绞尽脑汁去求解薛定谔方程，<strong>有氢原子能级、朗道规范、<spanclass="math inline">\(J-S\)</span>耦合</strong>，思想上有<strong>表象变换和微扰论</strong>两种很深受启发的处理技术，按照老师上课的说法，表象变换就是“换皮肤”，确实很形象，对应线代里面的相似型。微扰论本质上是物理学家常用技能了，江湖称“放小量”，跟机器学习用线性函数模拟任何函数有点类似。期末考试跟半期一样，<spanclass="math inline">\(7\)</span>个大题，开始前两个题没有思路，还好考试前听助教习题课说可能涉及上半学期知识，没想到两个题的最终落点都是在不确定性关系，还是顺利解答，期末<span class="math inline">\(J-S\)</span> 变换没理解题意，拿了 <spanclass="math inline">\(93\)</span> 分。</p><p>寒假期间向老师提交了关于束缚态量子数可数性质的证明，主要是结合离散数学(1)中的无穷基数，陈老师赞许了我的工作，并提出量子力学目前仍待完善的观点。的确，总感觉量子力学没有出现一种像分析力学那样对整个经典力学系统比较系统的描述，当然也有可能我学得不够深入，量子场论对这方面应该有更加抽象的叙述。原意是想申请<spanclass="math inline">\(A+\)</span>，但陈老师貌似没有正面回答这个问题。</p><p><strong>基础物理学(2)</strong>，csm老师的，讲授内容是光学和量子物理，第四周有个challenge-test，就报名参加了，当时跟初等概率论和奖学金评定三天连着考，还是比较麻。challenge-test还考察了上学期相对论的相关内容，当时对不确定关系还没有深入理解，脑子里只有<span class="math inline">\(\Delta p\cdot \Delta x\geq\dfrac{\hbar}{2}\)</span> 这一个公式。challenge-test 考过了，直接给<span class="math inline">\(A\)</span> 的成绩。半期的时候 zcy让我去开一个半期小班辅导，对自己的 reputation 有益，当时编写的<ahref="/2022/10/29/数理基础/基础物理学2小班辅导讲义/">讲义</a>在这里，使用<span class="math inline">\(\text{Q-book}\)</span> <spanclass="math inline">\(\LaTeX\)</span> 框架，现场到了 <spanclass="math inline">\(20\)</span> 余人，加上线上有 <spanclass="math inline">\(50\)</span> 来人，还算是比较令人满意。</p><p>不得不吐槽一下基础物理学的上课逻辑：力学-光学-量子力学-电磁学-热学，请问这是物理学的发展逻辑吗？学量子力学的时候使用部分分析力学的知识暂且不提，涉及到粒子在电场磁场中咋办？例如氢原子磁矩如何解释？真的为教学质量担忧，学生其实最后就只学到了如何调用物理的API，跟高中学习导体棒切割磁场一样，学生根本不知道来龙去脉，物理学成为了公式学。。</p><p><strong>离散数学(1)</strong>，软院 lsx老师开的，内容是数理逻辑和集合论，前面数理逻辑学了一堆感觉翻来覆去还不如真值表快，期末考试唯一需要列真值表的题目结果没看到用字典序，后面集合论学了很多定义，有<strong>自反、反自反、对称、反对称、传递</strong>，以及满足一些条件下的逆序、偏序、全序等关系，还学了集合论的公理化系统，在量子力学中有关能级分离的证明中发挥了重要作用，期末考试寄了，客观题错太多题了，还希望sxjj 捞捞我[可怜]。</p><p>这门课 sxjj捞得厉害，期末考试客观题做得比较急，主观题反而做得好一些。客观题犯了很多低级错误，实在是不应该。下学期开始对计算机相关的专业课要走心了，不然容易酿成惨剧……</p><p><strong>数据结构</strong>，软院 dgg老师开的，学习了各种数据结构基础知识，有平常小作业、oj作业和期末考试，oj 最难写的就是 kd-tree，用完了所有次数（共 <spanclass="math inline">\(5\)</span>次）才过，耗费了好几天时间，平常小作业感觉无理取闹，没啥意思，但是总是犯些低级错误，每次要被扣许多分。</p><p>期末考试个跟考研中的数据结构题目差不多，这里有一份数据结构复习<ahref="/2022/12/25/计算机科学/数据结构期末考试复习笔记/">笔记</a>，主要还是理解各种<strong>逻辑结构、存储结构以及相应的数据操作</strong>。考完之后第二天跟大师对了一波，客观题问题不大，代码填空题没能很好理解题意，有几个空不对，后面大题就比较放水了，就是些归并排序、插入排序、最小生成树、拓扑排序比较基本的内容。</p><p><strong>初等概率论</strong>，统计学中心 dwl老师开设的前八周课，课程前期讲了一些很偏分析的内容，比如 <spanclass="math inline">\(\sigma-\)</span>代数、Boral 集合，后期就是一些经典的期望、期望、协方差、大数定律以及结合各种分布下的应用。考试有期中和期末，最难的还是概念判断，毕竟我们这种学工科的各种近似放到嗨起的比不过理科人的严谨，错了几个判断题。半期考得还行<span class="math inline">\(94\)</span>，期末不知道咋样，dwl老师说不透露，的确，如果提前让我们知道自己情况，课程替代对于其他同学不公平。期末自我感觉还行，没有完全不会的题，后半期课程替代原培养方案中“概率论”一门课，最后顺利过关。</p><p><strong>马克思主义原理</strong>，马原院长 zad老师的，任务量比较重，但挺适合我这种不怎么懂政治的。有慕课、小班讨论、线下上课、读书报告、期末论文。zad老师还是我们四川乐山人，在仕途上很不错了。他上课还给我们讲怎么写好读书报告和期末论文，多写一些人生经历和个人感悟，少一些空话套话。读书报告看了《共产党宣言》和《帝国主义论》，对马克思主义萌芽初期的历史和发展有一定了解。期末报告写了一下全球形势，自己感觉也没分析啥出来。。最后反正也<span class="math inline">\(P\)</span> 了。</p><h2 id="志愿">志愿</h2><p><strong>软院迎新</strong>，暑假的时候报名成为软院迎新志愿者，在新生报道现场负责给新生登记学籍档案，顺便认识认识软院学生会的一帮人。有肯德基早饭，忙了一上午，当天太阳还比较毒辣，还吃了好几个雪糕，看着各种活蹦乱跳的吉祥物，青涩的学弟学妹们，还是很高兴的。</p><p><strong>答疑坊</strong>，这是大学以来最走心的志愿，开学初期答疑坊招新面试通过，从第四周开始双周坐班，这个志愿充分体现“费曼学习法”的好处，来答疑的主要是微积分A(1)和线性代数的知识，把自己“学会”的知识重新给别人讲一遍之后的收获是不一样的、在答疑过程中，我经常对知识有更加新奇的认知。当时学这门课的时候毕竟也是抱着通过考试的想法，有些知识没有深入消化，尤其是线性代数，这门大一的公共基础课内容是真的丰富。答疑坊这个地方真的是高手云集，在志愿者群里面问问题基本都有人能求解。</p><p>期中的时候收到来自学姐、学妹、学弟的喜报，大四学姐微积分 C 半期80、大一学妹线性代数半期很不错、大一学弟微积分 A(1) 半期99、幸甚至哉。他们分享的喜悦让我在匆忙的期中周也很开心。</p><p>后半期我拓宽了疆域，开始接离散数学、数据结构等计算机方向订单。不得不说，在答疑坊这个地方，我能够即时地了解到各大院系培养方案的差别，比如电子系大杂烩离散数学，经管学院各种错误代码满天飞的数据结构，计算机系程序设计基础跟数据结构难度有的一拼的算法部分。成外几个学弟学妹也经常问我程设的问题，他们像极了当年的我，代码风格脏乱差，代码习惯还得从小培养。</p><h2 id="科研">科研</h2><p>这学期 SRT 报龙明盛的课题，结果被拒绝了，主要是报的同学都太强了。不是129奖学金就是国家奖学金，我一个非软院血统的排不上号，不过也有可能有其他因素吧，进龙组还是比较难的。</p><p>然后听 zcy 的建议选个贵系的SRT，然后就报了关于因果推断的，这学期下来熟悉了一下知识图谱和 PC算法，做的主要内容是 AI forScience，还有个医学院的哥们儿，有很多的生物医学名词搞不太懂。不过这方向上的paper 发表在机器学习会议上，最重要的还是 AI的设计，说大点叫机器学习领域的落地，说小点叫医药蹭蹭 AI 的热度。</p><p>不过我们只有三个人，大家都太忙了，毕竟没有target，也不知道这个项目最后会以什么方式呈现结果。</p><h2 id="社工">社工</h2><p><strong>校科协</strong>这学期貌似没干啥。</p><p><strong>贵系科协</strong>组织了一次见面会，大家相互介绍了一下，高老板阐述了相关工作，之后开展了几次AI 学习小组，不过在预料之中，这个项目自然而然地烂尾了，讨论 AI最后变成讨论作业。</p><p><strong>软院科协</strong>负责 <ahref="https://ssast-readme.github.io/">ReadMe项目</a>，传了很多大一下学期记的电子版笔记，志愿工时真的丰厚，知识就是力量。这学期每隔几周就举办技术部例会这种形式还很好的，大家聊聊最近学习中遇到的问题，吐槽一下，顺便分享一些trick，学习到很多 mac terminal 相关插件，直接一个<code>brew install</code> 就装好了，至今也还很有用。</p><p>这种举办方式能让组员凝聚起来，大家唠唠嗑挺好的，毕竟大家课业都很繁重，这种方式既能放松心情，也能学习到更新的技术，一举两得。</p>]]>
    </content>
    <id>https://learningrate1.com/2023/01/09/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%BA%8C%E4%B8%8A%E6%80%BB%E7%BB%93/</id>
    <link href="https://learningrate1.com/2023/01/09/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%BA%8C%E4%B8%8A%E6%80%BB%E7%BB%93/"/>
    <published>2023-01-09T11:58:22.000Z</published>
    <summary>乘坐 1 月 9 日 Z49 次列车慢悠悠地回家，路上写了一些这学期的总结，大年三十闲来无聊，总结一下这一学期叭~</summary>
    <title>Summary of autumn term in the second college</title>
    <updated>2023-01-09T11:58:22.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="总结" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/"/>
    <category term="大一" scheme="https://learningrate1.com/categories/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%B8%80/"/>
    <category term="总结" scheme="https://learningrate1.com/tags/%E6%80%BB%E7%BB%93/"/>
    <content>
      <![CDATA[<p><div id="dplayer0" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer0"),"theme":"#FADFA3","loop":true,"lang":"zh-cn//语言","screenshot":true,"hotkey":true,"preload":"auto","volume":0.9,"video":{"url":"/video/亲爱的旅人啊.mp4","pic":"/img/亲爱的旅人啊.jpg"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script></p><p>之所以写这个大一学年总结，是因为看到很多学 CS的前辈们都喜欢在稍微放松的节点总结一下。之前高中的时候没有这个习惯，感觉时间过了就过了，忙完这件事情总有下一件事情要忙，权当写一个像样的大一总结吧，也希望看到这些的你有所启发。</p><h2 id="暑假">暑假</h2><p><strong>高考</strong>，也许上天还是看得起我的努力（如果之后有时间我可以详细叙述一下我高中的破防时光，不过现在觉得也是一笔财富），高考成绩误打误撞地考上了华子的（半个？）CS，有欣喜，也有忐忑，不知道在这条全新的道路上自己究竟会发展成什么样子。</p><p><strong>学车</strong>，高考完后几天在一个露天坝花了一上午的时间学会骑自行车（手动捂脸），接着暑假先是和绝大多数毕业生一样报名学车，在闷热的夏天里每天重复着同样的动作，个人觉得还没有在华子脱靶骑自行车好玩，学车就和几个在县城读书的高三毕业生一起学，之间也很少有交流，教练也经常刷短视频，不得不再次评价短视频推荐算法的厉害，反正感觉好多时间就在这种无聊透顶的驾驶中消逝。科一科二顺利一把过，但暑假后期由于开学比较早，科三没预约成功，就只有等着寒假再说。</p><p><strong>游泳</strong>，八月份联系上教育园区的游泳班，想的是毕竟在华子毕业游泳不过不行啊，就去学了个蛙泳，小学的时候我还有好几次去游泳馆被水呛到发烧的经历，心理上不是很能放松。跟一堆5、6岁的小孩一起学游泳，真的是搞不懂，明明我都知道阿基米德原理，结果自己的平均密度很容易变高，老是减少排水量，那些小朋友在水里面都开始到处耍了我还不得要领……反正前半期还学得比较难受，后面掌握到技巧后慢慢的就会了，后面几次就在1.4m的水池游来游去，反正至少淹不死了……游泳的话前面由于刚学会骑自行车比较兴奋，每天骑一个小时到郊区学游泳，后面发现晒黑了，就改乘公交车。</p><p><strong>编程，</strong>在母亲大人认识到的一位从业者的建议下，开始啃《C++primerplus》。虽然现在看来这种入门方式非常不友好，并且当时啥也不知道，拿着破windows 电脑（其实在后面我的<del>调教</del>下还蛮不错的），安装 Vs6.0，画风如下图。真的搞不懂，那个时候我自己的鲁棒性堪称极高，在这样的界面以及各种过时C语言规范折磨下，我还能把那本将近七百页的书看个八九不离十，做完大部分现在看起来几乎没什么意思的习题，虽然大部分忘了……然后我承认是看到python 语言各种天花乱坠的宣传，也搞了一点点 python，主要也就是熟悉python 语法。</p><p><strong>夏令营，</strong>严格来说不算是一个夏令营，就是一个线上的讲座吧，主讲人是华子宣传片《追光少年》里面的角色原型，研究立方卫星的，主要是讲他们实验室的工作，反正也听不懂就报了名。最终有两个作业，一个是弄一个单片机，一个是用matplotlib结合数据绘制卫星的轨迹图。当时觉得前面那个挺有趣的，就买了个单片机，配置了一堆硬件软件，最后把硬件语言烧在芯片上，大概跟zcy 的数字电路实验比较相似，只不过我们这个是超级低配版的。好在有 C语言基础，照着 PPT上的代码结合网上的参考资料，还把进阶内容也做了出来（呼吸闪光灯），感觉效果还行。</p><p>暑假大概就这样，当然免不了毕业典礼、宴席、经验分享以及杂七杂八之类的，主要大部分时间生活在县城，同学之间来往很少，就这样，在教练车的兜兜转转中，在阳光下聒噪的蝉鸣中，在跳来跳去的黑框框，暑假就这样过去了。</p><p>其实现在想来，那个暑假有点封闭，跟之后寒假生活亦或是今年的暑假相比，感觉自己懵懵懂懂，也没跟外界或者是其他人有交集。</p><h2 id="秋季">秋季</h2><p>初高中那时幻想大学的生活会很丰富，但是上了华子之后，还是真的感觉事多。学业上写沟微积分线性代数程设每门课都比较硬，每周都要花很多时间，尤其是那个时候乖到所有课都到教室，所有习题课都去听，所有作业都独立认真完成。</p><h3 id="学业">学业</h3><p>秋季学习一共上了 <span class="math inline">\(8\)</span> 门课程，共<span class="math inline">\(24\)</span> 学分。</p><p><strong>军事理论</strong>，考前认真复习了，主要是成外学长们有齐全的资料，感想提前写在参考资料上，感谢这些资料让我大学开局<span class="math inline">\(A-\)</span>。</p><p><strong>写作与沟通</strong>，主题是<strong>数字化生存</strong>，其实现在看起来这个主题还蛮适合的，至少我的博客是这样的。ZYY人美心善，是个宝藏老师，让这门课的痛苦减轻了不少。短文写了一个贴吧现象，自认为比较烂，长文总体框架选择得比较好，从媒介发展视角探讨了微信“拍一拍”流行的原因。虽然这些传媒学方面的知识之后应该是用不到了，但至少学到的文献查找以及一些写作技巧还是很有帮助的。</p><p><strong>微积分A(1)</strong>，WXF 的课对我来说真的nice，他从数的起源开始讲起，自然数、有理数、实数、良序关系、绝对值、指数对数、三角函数，一切被中学数学公式化（或者说功利化）的符号在几条公理的基础上就能建立起来。</p><p>最有印象的是 WXF第一次开放线下答疑，同学们“蜂拥而至”，当时好像是用阿基米德公理证明选择公理什么的，我们思考了好久都解不出来，WXF直接口述证明过程，赞叹不已。前几次作业，每次作业花上十个小时都不过分，有的open question 想不出来，还让我不得不去看实分析教材……</p><p>但是不足的是，从功利的角度，WXF上课对考试帮助不大，毕竟平常都深入数学的深刻本质去了，哪有时间讲怎么算对考试的那些题……好在当时维持了高中刷题的好习惯，提前做了一些往年题，保持考试手感，这门课至少没寄掉……按照WXF 的说法，期末解答出附加题的同学且总评 <span class="math inline">\(\in[90,100]\)</span> 的同学总评是 <spanclass="math inline">\(A+\)</span>，当时发下试卷看了一下附加题，好像是用一个积分中值第二定理再处理一下就能证明，就先做了附加题，最后拿到人生第一个<span class="math inline">\(A+\)</span> 。（手动狗头）</p><p><strong>线性代数</strong>，JY 姐姐的课，比较 nice，讲课 ppt非常数学，满篇都是公式，不像 WXF 还放点 Geogebra作的图，但思路确实比较清晰，至少对于我这种代数基础比较薄弱的很友好。还有JY 每次作业都有些补充题需要多动一些脑筋，而且他们作业都是手批，比 WXF的助教认真多了（ WXF 助教基本到期末才改平常的作业） ，可见如果是 JY的助教工作量是有多大。</p><p>比较有印象的一节课，她讲到正定二次型的时候，举了一个薯片形状为马鞍面抗压能力大的例子，还挺有趣的，JY姐姐难得在课堂上笑了一次。虽然有时候下雨的冬天坐在五教会犯困，甚至有次把笔记本落在教室了，还好一位日新的好心人帮忙送还，但是JY 的课也很推荐，至少学到了很多东西。</p><p><strong>程序设计基础</strong>，软院 LYS老师的大硬课，这门课来年春天的时候被评为优秀课堂。其实个人觉得这种课其实上课啥的都不重要，最重要的是动手实践。经常在LYS 老师课上写 JY 的线代作业，然后 LYS老师讲到他在美国读书什么什么的就认真听一下……</p><p>每周四都要上机，在东配楼，机房电脑上全是灰尘，随着课程的进行，后面和链表相关的题真的让人头疼，感觉用数组做呢，感觉过不去，用链表呢，链表要手写，太麻烦……然后当时助教让我当组长，结果就成了PPT 制作者 + PPT 报告者……虽然增加了工作量，不过也能锻炼自己。</p><p>然后临近期末就是头疼的大作业了，助教给了一个大约 800 行用 WIN32编写的框架，要求实现一个横版闯关游戏，那段时间是真的肝，而且当时一点模块化思想都没有，一个文件写到底，一旦调不出来直接塞到全局变量去，到后面加个按钮都非常炸裂。当时的启蒙书是毛星云的《逐梦旅程：Windows游戏编程之从零开始》，哎，没想到浅墨竟然就在我大作业交后两天辞别人世，我一直记得《逐梦旅程》中的一句话：</p><blockquote><p>谨以此书献给所有怀揣游戏开发梦想的人们，因为，你们不是一个人在战斗。</p></blockquote><p>愿浅墨在天堂实现他的梦想，逝者安息。</p><p>期末考试五个题，一堆<strong>码农题</strong>，写起来还挺难受的，当时不知道一些技巧，有个题要讨论八个方向，直接复制粘贴改数据八次……然后最后一个算法题，还以为自己没法做出来，但当时还有将近两个小时的时间（手动狗头），就拿出纸笔开始写思路，当时想测一下穷举法能过几个，然后写了一堆for，提交，结果就神奇般的过了，对，除了处理初始数据有个优化之外，其他啥都没优化，直接就给过了，然后三小时考试一个半小时提前交卷……LYS老师是图形所的，之后还会提到他。</p><p><strong>体育(1)</strong>，WJH老师，是一个和蔼的老头，经常口出一些顺口溜，<strong>“祖国好，清华好，无体育，不清华，全面发展我最好”</strong>，清华的<strong>3000米</strong>是真的难受，对于我这种初高中跑1km都难受得要死的体育差生简直是五雷轰顶。不过跟着清华阳光长跑的氛围，经常在紫荆操场上跑步，逐渐在耐力方面有所提升，长跑项目还是可以练的嘛，贵在坚持。反正最后体育成绩也算可以接受。</p><p><strong>思想道德与法律</strong>，ZXM老师，这门课任务量挺大的，分组小组展示。个人其实很讨厌这种小组活动，感觉跟生产队一样，分组也全看运气，如果分到摆烂组是真的难受。当时还选了一个组长，结果我实在看不下去，好多事情被我一个人包办了……</p><p>然后不得不说，有次上课一个同班好朋友拉我翘课去写微积分作业，结果那堂课就签到了……也没有怪罪好朋友的意思，当时我自己也没有定力，思修教室人很多很闷，特别难受，觉是睡过好几次了，课也没听进去几个字，思修群倒是特别热闹，好像老师也不怎么管，都让大家畅所欲言，唯一的收获是一堆表情包……就这样咯，本来对这种从小到大都不感冒的课程无所谓。</p><p><strong>英语听说讨论</strong>，Matthew的课，一个新聘请的外教老师，本来对华子的英语课抱有希望拯救我这垃圾英语，没想到还是失望了……前面几节课认真听讲，后面结果摆烂，上英语课的时候当天程设小作业就放出来了，于是在英语课堂上写代码，然后个人presentation 还行，结果又是 group presentation随机分组难受，一堆人摆烂，好多事情都是我做的，结果最后展示的时候组员忘词是真的无语了好吧……反正跟zcy 学长说的一样，华子英语课上是学不到东西的……</p><p><strong>未央工程导论</strong>，究极大屑课，跟贵系的<strong>信息技术科学概论</strong>有的一拼，未央十二个专业方向“百饼齐放”。而且这课还要签到，签到学号十分随机，时序数据预测很难。有次讲座是副校长，直接让半个未央的同学全来签到，还记得那次室友在群里一发，我们宿舍剩下三个人马上骑车飞奔到现场，真的是有惊无险。好在那堂课讲课还行，用一个动画展现了排队中的统筹策略，授课比较风趣。不过这门课是真的难受，还要写两篇报告，照着PPT 修修补补，总算弄完了这个烂摊子。</p><h3 id="srt">SRT</h3><p>未央工程导论中间有几堂课是导师引领相关研究方向，然后 SEY老师开始画大饼大法把我们一半的同学<del>骗</del>到 SRT项目中（包括我），项目题目叫<strong>二次曲线曲面函数求交库</strong>，是关于数学方面很底层的求交算法实现，前期对git 命令不是很熟悉，以及对整个 SRT项目抱有太多期待，投入了过量的时间……总之，顺着 GEMS 项目迁移了许多函数到GMT中，得到相应的测试，但感觉浪费了不少时间……不过，大一需要尝试，也算是一个经历吧……</p><h2 id="寒假">寒假</h2><p><strong>高中助教</strong>，年前回了趟成外当一个培训机构的助教，顺便和lsq在成外附近把好吃的又都吃了一遍，新开那家翘脚牛肉店味道不错，反正每顿饭都可以报销，就还算吃得痛快。还有一次吃了简阳羊肉汤，之前我家楼下有一家羊肉汤店，烟囱直上，出口基本就我们家窗户附近（我们家顶楼），每天中午都可以闻到非常浓郁的羊肉汤味道，可能开始觉得有点腥，后来闻惯了真觉得这是独特的家乡味道。确实吃着比较爽，还记得当时为了报销，把票分成了两次。</p><p><strong>“给我卷”</strong>，期间给 zcy说了目前的成绩，他特别兴奋，直接打来一个电话，我记得当时这个电话打完，我绕着成外不知道走了多少圈，他对我很是一番鼓励，还专门拉了一个群，跟罗哥一起提前卷卷下学期的课程。也算是那次真正认识了zcy吧，之前高一的时候跟他一起在东五楼学竞赛，现在他确实混得风生水起，本来刚刚开始大学生活比较迷茫，他算是让我少走了好多弯路。</p><p><strong>约饭</strong>，时期间还约了高中的英语老师在猫爱上鱼这家店吃烤鱼，她送了我一个笔记本，然后互相聊了聊近况，高中低迷期间她经常鼓励我，我还是很感激她。在四处逛教室的时候还见到了高二期间的高中班主任，我们在外面吃富顺豆花，他疯狂安利上海这座城市的好，他也感觉是个有趣的人，每天乐呵呵的。心态确实好，当年被各种竞赛教练“围攻”，还是安心教书，跟学生关系也挺不错的，对其他老师都没有怨言，我们也聊得开，高考前经常找他聊天，也很感激他。</p><p>说起来，高中过去小半年了，怀念的还是最纯真的那些老师，而不是各种以功利为导向、用奖金刺激、搞特殊化的竞赛老师。也有可能与竞赛上的失利有关吧，或者说是另一种形式上的PUA？总感觉竞赛这段岁月很不真实，很难想起那些年中存在温情的时候了，倒是留在普通班各位老师同学的鼓励让我觉得自己经历过高中的青春岁月。有次晚上考试监考还碰见了现任副校长的物理竞赛教练，他问我回来想干嘛，我直接回怼了一句“挣钱”，直接结束话题，后来他也就没来问我了。</p><p><strong>过年</strong>期间，当年还是要享受过年的气氛，虽然还是到处都有疫情，大年初一乐山大佛一日游，大年初六青城山一日游，还是感谢老爸，都是当天去当天回来，路上我基本都在睡觉，而我爸不仅跟我们走了全程（微信步数20000 步+），还开了往返的车。</p><p>中间几天就是到各种亲戚家串门，各种吃吃吃、耍耍耍，老屋里那个平板上的植物大战僵尸2从来都是过年的时候我推进进度，寒假期间貌似又通了两个地图，这种塔防游戏设计确实有意思，尤其是能量豆的合理使用，增加了非常多的不确定性。</p><p>之后就是 <strong>SRT</strong>那边推进度，主要问题还是有一大半的人摆烂，基本就三个人在做事情，推进过程中也耗费了不少时间。间有次去考科目三，结果两次都是最后靠边停车没到位，还是有点气，不过也没法。最后跟ly 一起从简阳坐飞机回到北京，开始大一下的生活。</p><h2 id="春季">春季</h2><h3 id="学业-1">学业</h3><p>春季学习一共上了 <span class="math inline">\(16\)</span> 门课程，共<span class="math inline">\(26\)</span> 学分（不算 PF 的）。</p><p><strong>微积分A(2)</strong>，一如既往地选择了wxf，前半学期听课出勤率挺高的，能有 50-60%吧，后半期大多时候就没怎么听了。前半期 wxf依然是一如既往地天马行空，讲了许多数学上比较抽象的概念，比如<strong>范数、凸集、双线性、梯度下降法、勒让德变换、Gamma函数</strong>等一系列知识，不完全统计的话涉及到<strong>高等代数、泛函分析、机器学习数学部分、数学物理方程</strong>等各种课程。</p><p>一如既往地，认为讲的这些和期中考试没有半毛钱关系，结果挺难的，印象中压轴题是平常一次作业题，结果没做出来，就没上90分，还挺亏的。后半期就基本没怎么听了，讲的都是些电磁学里面基本都会用到的东西，主要熟悉了一下数学意义上的缠绕数，没记错的话后半期几乎就没听过课。</p><p>作业的话每次都使用的 markdown格式，跟大师学的，各种数学方程式的排版技巧基本都掌握了，作业以及全套 pdf链接可以点击<ahref="/2022/06/18/数理基础/微积分A(2)作业合集（1-10）/">此处</a>，也算是为之后的论文排版提供一定基础？这门课还开设了周末“高等习题课”，上来就讲了一堆实变函数的东西，根本听不懂，后来去了几次也就开摆了。期末的时候想到可以合影留恋就去听了一下，讲的常微分方程幂级数解法，也属于数学物理方程范畴吧，这部分内容听着还行，毕竟氢原子波函数就是靠这解出来的。</p><p>期末的时候在计算机开放实验室考的，最后总排班上第三（第一还是成外的巨神zjk ，信息集训队预科班恐怖如斯），加了些周天习题课的证据。又获得了一个<span class="math inline">\(A+\)</span>，这样的话对我来说 wxf在给分方面还行（再次狗头）。</p><p><strong>基础物理实验(1)</strong>，这是我上大学以来最无语的课了，实验内容跟玩具一样，只要会认字就能做完实验拿到实验数据，然后评分仅仅只看实验报告，简直没有比这更sb 的课了。然后就想着怎么卷报告呗， 好家伙在 windows 上 <spanclass="math inline">\(\LaTeX\)</span>就配置了一天，然后各种拼接模板，关键是编译花的时间太长了，一篇报告基本要花好几天。学了python 用来画图，结果时间上还是消耗得比较多，就应该用 excel水水，没必要事事都做得特别精细。而且无论报告写得多精致，基本就在 88-92之间徘徊，最后拿了 <spanclass="math inline">\(A-\)</span>，属于是大一唯一 <spanclass="math inline">\(A-\)</span>的理科了。以后吸取了教训，这种课就不应该花过多时间。</p><p><strong>大学化学(B)</strong>，让人不理解的课，但未央大半部分都要学这个，想想也就算了，之后还要学生物导论，是真的理综都被打牢了。选的cc 老师的课，期中巨水，可以带 A4纸，就在一个比较小的教室考，基本可以看周围同学的，反正也只占一次作业分。期中考完之后北京就爆发疫情了，本来可以查卷，结果试卷送不进来，到了期末的时候才能查卷。</p><p>我一般很少查卷的，那天可能期末周比较闲，就去何添楼查卷，结果助教就说了一句很可疑的话，查卷的时候不能询问正确答案，我就觉得很奇怪。然后就把半期试卷上的内容基本都背了下来，我还记得我坐在楼梯上打开电脑开始敲markdown，果不其然，期末就考了好几道一样的题，好在通过百度我已经提前得到了标准答案，最后还意外地拿了<span class="math inline">\(A\)</span>，比较对得起自己的努力。</p><p><strong>中国近代史纲要</strong>，选课经历海淀区最大赌场选上了sw，据说按照字数线性给分，快到截止日期的时候跟 yyj 到清华学堂 rush期末论文，在 zcy 的建议下写了觉醒年代的观后感，水了 1.4w字，期末考试的时候抄 ppt手都抄麻了，尤其是中间有个题，连着十几页貌似都是答案，抄到后面实在是抄不完了，就偷工减料了一些，问题不大。第一次在政治课上拿<span class="math inline">\(A\)</span>，最离谱的还是 sw经常在群聊以及朋友圈里转发各种公众号，然后每次点击去都是违规无法访问，也没什么，课水给分好的老师也不多了。。</p><p><strong>形式与政策</strong>，lzd后八周，赶上疫情，有几次线上，中途校长来上了一次课，开始听着还有意思，后面就比较无聊了。期末写了一篇有关乡村振兴的论文，3k字也还行，也拿了 <span class="math inline">\(A\)</span>。</p><p><strong>诗词格律与创作</strong>，最无语的一门课了，老师上课照着自己提前写好的word文档念，后面去的人越来越少，然后这个外校聘请的老师不知道清华的打分制度，我交了几首高中期间写的一些诗词，格律方面也都查过了。可能是不符合老师的口味吧，然后吃了一个<span class="math inline">\(B-\)</span>，是真的难受。最难绷的是本来有 PF机会的，结果问了助教，他说问题不大，然后就这样了。。开校的时候老的师可能也有点内疚吧，希望给我们申请成绩复议，结果人文学院的教务就一直通不过，我了解到有<span class="math inline">\(4\)</span> 位同学吃 3.0，我也有同学拿了4.0，不过也就这样咯，这位老师应该也只有这一次失误了，相信后面的同学也都会好些吧。</p><p><strong>体育(2)</strong>，zsl老师，他其实算是半个体育老师，搞数据分析的，自己说 python 和 R语言玩得贼溜，依据统计模型给运动员提供科学策略。的确，现代的体育项目都是在玩高科技，专业团队和运动员都是相辅相成的。zsl老师人特好，当时引体向上我们班好多男生都菜（包括我），他专门单独教我们怎么练习，然后有次模拟测试的时候我从单杠上摔下来了，虎口磨破了皮，他就把花名册给我让我代替原来的班长。之后疫情教职工不能进来，然后我就成了组织者，笑死，自己三步篮都悬还组织大家考试，反正考试应水尽水，数据也是我在统计，除了有位同学记了PF，其余基本都 4.0 吧，拿了 <spanclass="math inline">\(A\)</span>，百分制98，应该是担任临时班长加了些分。</p><p><strong>游泳测试</strong>，十二周左右吧，游 50m就合格，旁边站着若干个拿着竹竿准备物理意义上的捞人，蛙泳过去的，比较慢，我记得还被后面一位同学反超了，还被狠狠地踢了一脚，最后上岸的时候我大口地喘气，有惊无险地<span class="math inline">\(P\)</span> 过了。还记得测完试去东主楼听 rls将如何科学上网，整个教室窗帘紧闭，周围好几个贵系的老师看守，阴森恐怖，也教了怎么搭建博客，hexo那一套，也算见识了大佬。</p><p><strong>英语阅读写作(B)</strong>，zzp老师的，这门课是真的任务量大，每周都要写预习作业，然后写短文长文。那个老师长得还真像我爸，语气态度什么的都很温和，上课都很死气沉沉，感觉大家都很水。期末考试可以使用各种电子词典，最后混了个<spanclass="math inline">\(A-\)</span>，貌似啥也没学到，自己的英语水平实在是堪忧。</p><p><strong>科技与人文研讨课</strong>，属于是未央的专属课程了，两个学期一共<span class="math inline">\(1\)</span> 学分，期间写了 <spanclass="math inline">\(6\)</span>篇读书报告。主要是选择的文章可读性比较差，那些田野调查和事实相比不能说有理有据吧，只能说毫无关系。上学期和这学期一共汇报过两次，一次是菊儿胡同，吴良镛工匠的杰作，不过现在都成为商业街泯然于南锣鼓巷了……另一次则是有关大厂的田野调查，汇报完之后，班主任对这本书的各种细节利用事实狠狠地反驳了一番。的确，一个针对大厂的社会学研究听着就不像是反映真实情况的。当然薪资不想书中反映的那么少，只不过996的情况确实比较普遍。班主任说我们这个行业一般三种途径，国内大厂、外资企业和体制内，对应三种不同的生活方式，主要看个人的喜好。最后，他还给我们展示了他们组SRT 的成果，发了一篇 CCFA（实际是还没正式登刊），作者是他的博士生 xjz还有软院大一学生 hyx。他还向我们推荐了cs-Rank，也算是让我们提前了解一些目前科研的现状。由于我们班代表 ljf上学期展示得分最高，助教也认可我们读书报告的美观，拿了 <spanclass="math inline">\(A\)</span>。</p><p><strong>基础物理学(1)</strong>，wsy老师，内容涵盖力学和相对论，没啥难的，只不过每周都有作业，期中还考得不理想，一去查卷发现自己真的想当然了，期末就认真准备，心想还是混个<span class="math inline">\(A-\)</span>吧，没想到期末异常简单，好几道跟作业一模一样的题，最后 <spanclass="math inline">\(A\)</span>。有意思的是，期末做了 js 老师之前的<ahref="/2022/06/20/数理基础/基物1期末试卷解答/">两套往年题</a>，结果他们更水，可以带A4 纸，稳定的 <span class="math inline">\(2-3\)</span>道作业原题，还有加上这学期一共连着考了 <spanclass="math inline">\(3\)</span> 次的题。题目是关于 16年那次震惊世界的引力波探测实验，数据都不改是认真的嘛。。当然也跟当时的疫情有关，据说物理教学组的说法也是应水尽水。</p><p><strong>面向对象程序设计基础</strong>，yjh老师，软院图形学所长，知乎上评价蛮高的，也许是年轻的时候太刻苦了没注意身体，这门课一直是录播，就听着广播音。后来大家都没去了，课程作业也很无聊，给一个话题代码、测试、报告啥都自己写。我评价自己的作业质量是单调递减，结果还一直是100 分，是真的离谱。期间 yyj 问过我很多贵系的 oop作业，感觉他们内容非常丰富，主要是有oj，各种题目涵盖调<strong>试技巧、STL、数据结构、现代 C++体系</strong>，蛮有意思的，只不过他们期末考试貌似 oj 系统挂了。。</p><p>说回来，期末写了个大作业，写了个函数绘图器，我在 github 上找了一个用qt写的，我还征求了作者的同意，魔改了一波，现在看来中缀表达式处理得不是优雅，当时用的很慢的regex 正则表达式库，应该用栈和运算符优先级来转换的，或者 python 里面自带<code>eval()</code>，不过当时不太懂，还是年少无知。。最后提交到算法挑战平台上可以加分，最后拿了<span class="math inline">\(A+\)</span>。</p><p><strong>计算机系统研讨</strong>，很水的一门自带 PF的课程，作业有一些杂七杂八关于体系结构的 task，顺便练习 wsl各种操作，反正作业交了都是 <spanclass="math inline">\(P\)</span>，算是混了个科学课组的学分。</p><p><strong>初等数论</strong>，跟着室友一起报的，他之前学数竞，这些内容对他不算难，作业只占<spanclass="math inline">\(20\%\)</span>，半期的时候担心寄，就记了任选的PF。期末还算比较认真，考场上只有最后一个题的第二种情况没想出来，后来发现思路没跳回到前面。结果总评<span class="math inline">\(94\)</span>，得了一个 <spanclass="math inline">\(P\)</span>。虽然有点小遗憾，不过倒也没啥，毕竟也学到了很多知识，掌握了<strong>孙子定理、剩余系、原根、勒让德符号</strong>这些概念，在数论层面至少也算是入了门吧，之后学习散列表的时候发现还挺有用处的。</p><p><strong>高等代数选讲(英)</strong>，杨 SIr的课，课上得比较好，属于是基本旁听下来的一门课，主要是跟 yyj一起讨论，学到了很多代数方面上的知识。不过感觉数学的提升是永无止境的，现在就算再学一遍线性代数都有不一样的收获，之后有空我也多复习一下这方面的内容，尤其是关于张量那一块，据说研究上经常用得到，毕竟杨Sir 也说过万物都是 Tensor。</p><h3 id="srt-1">SRT</h3><p><strong>大学生研究训练项目(SRT)</strong>，期中周准备挑战杯答辩，熬夜赶进度，我去参观了其中一个信息赛道专场的全过程，挑战杯真的可以叫做PPT杯，内容各种天花乱坠，只有一个超声波定点传声有实物展示，挑战杯不让说院系也是有道理的，毕竟很难从一个6分钟汇报能区分各种项目的优劣。最后我们项目获得了信息赛道二等奖，还是对得起期中周之前好几天的熬夜付出。最后应该是挑战杯获奖的原因吧，这门课拿了<span class="math inline">\(A+\)</span>，虽然不知道评分写个 <spanclass="math inline">\(96\)</span> 是什么意思。。</p><h3 id="竞赛">竞赛</h3><p>期中周的时候去参加了一下求真书院举办的<strong>新生数理基础大赛</strong>，具体经历可以看<ahref="/2022/04/10/数理基础/新生数理基础大赛/">这里</a>，考试的时候有“无限量”的士力架，题目和食物都不错，体验感很好，亚赛风格。当天晚上就出了成绩，大师拿了第一92，我第二 88，笑死，比微积分半期还高。可以报销 400 元，不亏。</p><p>5月份跟大师他们组队参加<strong>清华大学人工智能大赛（Thuai）</strong>，大师一波操作，指出赛事方平台shit代码中的各种通讯问题，不得不佩服大师对代码的理解能力。比赛主要比拼的是“英雄”和策略的选择，大师码力一个顶仨，不过中期因为比较忙大家比较摆，DDL前其余队都隐匿实力，结果我们中招了，想了一个策略看着还行就提交上去了，结果被暴虐，混了个三等奖。</p><h3 id="社工">社工</h3><p>在室友的邀请下，进入<strong>校科协活动策划部</strong>，这学期的主要工作是筹办第四十届挑战杯学生课外科技作品展，担任工作人员，在展览区里待了一天，看到了很多有趣的玩意儿。比如贵系有个根据盲打显示单词的，估计是用的隐马尔科夫模型训练了一些样本，本质上跟拼音输入法也差不多。还有生命学院用VR眼睛可视化他们的生物大分子成果，挺逼真的。航院还是什么具体忘了，下载一个软件来控制无人机飞行，然后照了一张照片。现场也有Thuai的解说，整个展览吸引了很多同学，很棒的一次体验，说是工作人员，倒不如说也是参观人员。</p><p>总之，这学期在学业方面也算是学到了更多的东西，逐渐适应了大学生活，也许有一些成绩上的小遗憾，不过长远来看到也没啥。生活也需要留一点遗憾嘛，这学期圈子也扩展了一些，相比之下，还是心满意足了。</p><h2 id="小学期">小学期</h2><h3 id="学业-2">学业</h3><p>小学期一共上了 <span class="math inline">\(4\)</span> 门课程，共<span class="math inline">\(9\)</span>学分，一路上下来我自己也感到震惊。</p><p><strong>程序设计实训</strong>，这门课是培养方案里面的课程，软院的 xf老师上的，讲课确实挺不错的，跟之前的课形成鲜明对比，不过 PPT内容其实比较单一，基本就是照着 more effective C++讲的，感觉好多东西也不怎么用。当然也有可能是我 C++学得不够深入。。期间写 24点属实伤了一些脑筋，后来才发现可以用递归的方式写，当时硬刚了一个递推的写法，其余都是些Qt的练习。大作业是写一个视频剪辑软件，知道消息之后提早写了一个出来，后来发现与需求有点出入，不得不评价助教还是有点摆，md需求文档不是很清晰，文档最后一句话还缺少宾语。。在二教答辩，期间还出了一个bug，后来联系助教说没事，顺利拿下 <spanclass="math inline">\(A\)</span>。</p><p><strong>程序设计训练</strong>，这门课是贵系的 hwt 老师开的，在 zcy的建议下软院贵系双开，同时推进两个大作业，h 老师 Rust语言的设计理念讲得挺不错的，这儿有一份<ahref="/2022/09/11/编程/Rust%20程序设计课堂笔记/">笔记</a>。Oj上的作业也蛮有意思的，还记得有一个二叉搜索树的题目，感觉贵系的风格就是你不会，总有人会。还好Copilot 基本都会（狗头），不会的就写注释”询问“它。不过 Rust编译器是真的智能，各种容易触发安全问题的代码片段全都提示出来，基本扫光所有error 和 warning 之后，代码能出的问题就只有运行逻辑问题了。Rust的生态圈也很友好，虽然赶不上python，不过应对需求应该也够了，况且其速度优势不再话下。后来也许是跟贵系另外一门爬虫小学期相比，Rust这门课的任务量实在是太大了，取消了两次小作业，的确，只有经历小学期的人才能体会到什么是真正的工程。这门课有个课程贡献加分，我就把课程笔记传了上去，之前帮助教过了一遍环境配置，加了一些分，也拿下了<span class="math inline">\(A\)</span>。</p><p><strong>量子计算研讨课</strong>，贵系 yms 教授开的，zcy让我跟他一起上，没想到这门课对全校学生开放。不过这门课的门槛是真的太高了，上来就从希尔伯特空间、量子力学五大公设的数学表述开始讲起，属实有些劝退，后来是jzf教授讲授的关于量子算法、量子电路相关部分。他自己也提到，许多做量子计算方向的博士生也难以掌握Shor算法，的确，非常前沿的课题是相当困难的，量子计算目前最难以跨越的还是物理实现，各种算法、理论已经满天飞，也许跟早年计算机模型的提出差不多吧，还是希望这棵令人无限遐想的科技树被点亮。</p><p>考核方式是论文调研，去年是读书报告。和 hsh、zcy组队，写了一篇关于量子纠错编码的综述论文，工作量比较大吧，答辩的时候 jsf教授只提了一个关于子空间编码的问题，hsh还在机场，刚好我可以回答，也算是比较幸运的。答辩结束的时候，jsf教授也很感慨，最初 70 多人，到最后只有 20多人参加答辩，的确这门课对于本科生来说难度过高。极为不友好。最后给了<spanclass="math inline">\(A-\)</span>，也算是比较中肯的评价了，毕竟本科生也很难在量子计算领域提出新的想法。</p><p><strong>毛泽东思想和中国特色社会主义概论(2)</strong>，自带 PF的实践课，主要是希望大家参与清华实践中。暑假 7月末去了趟武汉调研物流行业的情况，各种物流企业各有所长，顺丰主要针对中高端用户，中国邮政国营企业dddd，中通比较牛马，老总还非常得意地说解决许多就业问题，山农绿产品背后有供销社，也算是半个国营企业，京东非常神秘，还不让进工厂，害怕科技资料泄露吧，九州通专注于医疗产品，科技创新方面也是走在世界前列。前面几天都在繁忙地访谈记录整理，最后一天去了趟长江大桥附近，吹了一下江边的晚风，可惜没有白天登黄鹤楼，不过时间也不等人，还是踏上了高铁回到了家乡。这门课给分还有个小插曲，开始lzd 老师以为不是 PF 属性，结果我还吃了一个 <spanclass="math inline">\(B+\)</span>，后来还是改成了 <spanclass="math inline">\(P\)</span>。</p><h3 id="科研">科研</h3><p>参与班主任数据压缩相关工作，主要是参与一个江苏省太湖实验室举办的比赛，组队队员有之前提到的xjz、hyx、yyj，初赛当时有个编码一直没能破解，后来帮他们手写了 (51, 63)BCH编码的算法实现，大幅提高了压缩率，反正前三十名都进入决赛，最终以第三名的成绩进入决赛。</p><p>决赛是针对一个庞大的张量进行压缩，现在想来，我们当时一直走偏了方向，一直按照论文中比较传统的编码+压缩方式对数据进行压缩，最后直接细分到每种文件采用不同的方式进行压缩，后来发现应该基于预测的方式进行压缩，比如可以通过数据的一个子集来推断整个数据集，如果预测得当的话残差就很小，这样就能提高压缩比率。还记得中秋节在软院402绞尽脑汁，我们队反应过来的时候已经很晚了，只能说长一个教训。当然中途出现了<code>sizeof()</code> 和 <code>strlen()</code>不分的低级错误，我也犯了不少错，最后决赛没能入围答辩，还是得进一步提升自己的实力。</p><p>回想大一，大一上主要在适应，大一下在学业方面还是找到了方法，暑假三门硬课的磨炼也提升了我的能力，会<strong>永远热爱生活，热爱学习</strong>。</p>]]>
    </content>
    <id>https://learningrate1.com/2022/12/29/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%B8%80%E5%AD%A6%E5%B9%B4%E6%80%BB%E7%BB%93/</id>
    <link href="https://learningrate1.com/2022/12/29/%E6%80%BB%E7%BB%93/%E5%A4%A7%E4%B8%80%E5%AD%A6%E5%B9%B4%E6%80%BB%E7%BB%93/"/>
    <published>2022-12-29T15:38:42.000Z</published>
    <summary>趁着大二上的期末周比较无聊，更新了大一的学年总结，也算是对过去的一种纪念吧~</summary>
    <title>Summary of freshman year</title>
    <updated>2022-12-29T15:38:42.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="计算机科学" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <category term="数据结构" scheme="https://learningrate1.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    <category term="计算机科学" scheme="https://learningrate1.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/"/>
    <content>
      <![CDATA[<h1 id="数据结构总复习笔记">数据结构总复习笔记</h1><h2 id="绪论">绪论</h2><p>数据结构三元素：<strong>逻辑结构、存储结构、数据在运算上的实现</strong>。</p><p>逻辑结构：<strong>集合、线性、树、图</strong></p><p>存储结构：<strong>顺序、链式、索引、散列</strong></p><blockquote><p>Note.逻辑结构为树但存储结构可以为顺序，例如使用数组存储一棵完全二叉树。</p></blockquote><h2 id="向量与列表">向量与列表</h2><h3 id="二分查找">二分查找</h3><p>性质1：折半查找判定树是平衡二叉树，其<strong>高度差要么为 <spanclass="math inline">\(0,1\)</span>，要么为 <spanclass="math inline">\(0,-1\)</span></strong>。</p><blockquote><p>Prof.当前序列为奇数个时，一定能保证两侧数量相同；当前序列为偶数个时，要么为<spanclass="math inline">\(\left\lceil\dfrac{l+r}{2}\right\rceil\)</span>，要么为<spanclass="math inline">\(\left\lfloor\dfrac{l+r}{2}\right\rfloor\)</span>，对应高度差为<span class="math inline">\(1\)</span> 和 <spanclass="math inline">\(-1\)</span>，递归调用时一般不会更改中位数的选取，从而高度差要么非负、要么非正。</p></blockquote><p>性质2：二分查找次数形成的树高为 <spanclass="math inline">\(\left\lceil\log _{2}(n+1)\right\rceil\)</span></p><blockquote><p>Prof. <span class="math inline">\(2^0+\dots+2^{h-1}\leqn\leq2^0+\dots+2^{h}\Longrightarrow h\in[\log _2(n+1)-1,\log_{2}(n+1)]\)</span></p></blockquote><h2 id="栈与队列">栈与队列</h2><p><strong>（括号）匹配操作</strong>需要借助栈。</p><h3 id="栈模拟队列">栈模拟队列</h3><p>使用两个栈模拟队列，记“大小栈”容量为 <spanclass="math inline">\(M,N(M&gt;N)\)</span>，能够模拟的队列最大容量为<span class="math inline">\(2N+1\)</span>。</p><blockquote><p>Prof. <span class="math inline">\(1,\cdots,N\)</span>入大栈，依次弹出进入小栈，大栈加入 <spanclass="math inline">\(N+1,\cdots,2N+1\)</span>，因为还可以保留一个。输出队列时，小栈先依次弹出为<span class="math inline">\(1,\cdots,N\)</span>，大栈弹出 <spanclass="math inline">\(2N+1,\cdots N+2\)</span> 进入小栈，剩下一个 <spanclass="math inline">\(N+1\)</span>，先弹出 <spanclass="math inline">\(N+1\)</span>，再弹出小栈中的元素，恰好形成一个队列。</p></blockquote><h3 id="表达式求值">表达式求值</h3><p>前后缀表达式求值仅需维护<strong>一个数字栈</strong>。</p><p>后缀表达式：<strong>从左往右</strong>依次数字入栈，遇到操作符将栈顶的两个元素计算后结果入栈。</p><p>前缀表达式：<strong>从右往左</strong>依次数字入栈，遇到操作符将栈顶的两个元素计算后结果入栈。</p><blockquote><p>Note. 如果有括号将内部计算均做完后左右括号出栈。</p></blockquote><p>性质1：将表达式转换为二叉🌲，前缀、中缀、后缀表达式依次对应二叉树的前序、中序、后序遍历。</p><blockquote><p>Note. 在构建时 <span class="math inline">\(\neg\neg P\)</span> 前序为<span class="math inline">\(\neg\neg P\)</span>，后序则为 <spanclass="math inline">\(P\neg\neg\)</span>。</p></blockquote><h2 id="树的结构">树的结构</h2><p>以下讨论皆规定根节点🌲高为 <spanclass="math inline">\(1\)</span>，<span class="math inline">\(V\)</span>为🌲的节点个数，度为 <span class="math inline">\(i\)</span> 的数目为<span class="math inline">\(n_i\)</span>，🌲的度 <spanclass="math inline">\(d\)</span> 定义为所有节点度的最大值，度为 <spanclass="math inline">\(d\)</span> 且有左右树之分的树称作 <spanclass="math inline">\(d\)</span> 叉🌲，满足 <spanclass="math display">\[\begin{equation}V=\sum_{i=0}^{d} n_i\end{equation}\]</span></p><h3 id="基本特性">基本特性</h3><p>对任何🌲 可以看成最小连通图，顶点的数目比度的总数多 <spanclass="math inline">\(1\)</span>，即 <span class="math display">\[\begin{equation}1+\sum_{i=1}^{d} in_i=V\end{equation}\]</span> 给定一棵树的部分 <spanclass="math inline">\(n_i\)</span>，使用公式 (1)(2)可以获取许多信息。</p><h3 id="二叉树">二叉树</h3><p>对一棵二叉树度为 <span class="math inline">\(2\)</span> 的点有 <spanclass="math inline">\(n_2\)</span> 个，叶节点（度为 <spanclass="math inline">\(0\)</span>）的点有 <spanclass="math inline">\(n_0\)</span>，则 <span class="math display">\[\begin{equation}n_0=n_2+1\end{equation}\]</span></p><blockquote><p>Prof. 二叉树代入公式(1) <span class="math inline">\(1+0\cdotn_0+1\cdot n_1+2\cdot n_2=n_0+n_1+n_2\)</span> 即证。</p></blockquote><p>具有 <span class="math inline">\(n\)</span>个节点的二叉树的最小深度为 <spanclass="math inline">\(\log_2(n+1)\)</span>。</p><p>二叉树遍历方式</p><ol type="1"><li><p>广度优先遍历（层次遍历，使用<strong>队列</strong>模拟，记所有层中数量最大的为<span class="math inline">\(F\)</span>，队列最大容量为 <spanclass="math inline">\(F\)</span> 中的节点数）</p></li><li><p>深度优先遍历</p><p><strong>先序遍历</strong><strong>根</strong>左右（可以看成绕🌲外围一圈，图的深度优先遍历来源）</p><p><img src="/img/数据结构总复习笔记/image-20221222142445872.png" alt="image-20221222142445872" width=400 /></p><p><strong>中序遍历</strong>左<strong>根</strong>右（可以看成顺次掉落，其为有序的，无论平衡与否）</p><p><img src="/img/数据结构总复习笔记/image-20221222143306555.png" alt="image-20221222143306555" width=400; /></p><p><strong>后序遍历</strong>左右<strong>根</strong>（可以看成从左往右依次剪葡萄）</p><p><img src="/img/数据结构总复习笔记/image-20221222143738244.png" alt="image-20221222143738244" width=400 /></p></li></ol><p>使用递推的方式遍历二叉树需要<strong>栈</strong></p><p>性质1：<strong>完全确定二叉树至少需要中序遍历以及另外一种遍历，先序遍历和后序遍历不能完全确定一棵二叉树，除非保证每个节点的度为偶数。</strong></p><blockquote><p>Prof.中序遍历为左根右，前序或者后序遍历能给出根节点，从而能够区分出左右子树节点集合，对左右子树递归操作，能构建完整树。而先序和后序当存在度为<span class="math inline">\(1\)</span>的节点时叶节点可左可右，当每个节点的度为偶数时保证可重建二叉树。</p></blockquote><p>性质2：<strong>若一棵二叉树的前序遍历和后序遍历相反，则该二叉树为链状。</strong></p><blockquote><p>Prof. 反证法，若有个节点有左右子树，则前序和后序遍历不互为逆序。</p></blockquote><p>据此可求出已知前序遍历和后序遍历（记作 <spanclass="math inline">\(A\)</span> 和 <spanclass="math inline">\(B\)</span>）条件下构建二叉树的方法：<spanclass="math inline">\(A\)</span> 的第一位和 <spanclass="math inline">\(B\)</span>的最后一位均为根节点，去掉这两位，判断剩下 <spanclass="math inline">\(A\)</span> 首位和 <spanclass="math inline">\(B\)</span>的末尾是否相同，若相同，则该位与根节点形成<strong>“链表”</strong>，依此类推，若不同，则分别为左右子树的根节点，以两者的位置分别确定每棵子树的集合。剩下子树同样按照上述流程，同理，<strong>只要存在逆序字符串，该字符串对应的拓扑结构一定为链状</strong>。</p><p>性质3：<strong>若一个🍃为二叉树中序遍历最后一个节点，则该节点也为前序遍历最后一个节点，反之不成立。</strong></p><blockquote><p>Prof.中序遍历为左根右，最后一个节点为🍃，则该叶子一定在右子树，依次递推下去，该叶子一定为若干个右子树最后的叶子，在前序遍历时一定作为最后一个节点。反之，若🍃节点为前序遍历最后一个节点，其可以为左子树的🍃，此时中序遍历最后一个节点可以为根节点，即反过来不成立。在🍃时成立中序最后<span class="math inline">\(\Longrightarrow\)</span> 前序最后。</p></blockquote><p><strong>子集树：</strong><span class="math inline">\(n\)</span>个元素集合的子集可以看成是一颗<strong>叶子节点数为 <spanclass="math inline">\(2^{n}\)</span>的满二叉树</strong>，左和右分别代表选择或者不选。</p><p><strong>二叉排序树</strong>：左子树所有元素值小于根节点，右子树所有元素值大于根节点。（二叉树<strong>不要求平衡</strong>）</p><p>性质4：<strong>二叉排序树插入节点的复杂度为 <spanclass="math inline">\(O(n)\)</span>。</strong></p><blockquote><p>Prof. 最坏情况退化成链表，复杂度为 <spanclass="math inline">\(O(n)\)</span>。</p></blockquote><p>性质5：<strong>若排列组合随机，二叉树高度为 <spanclass="math inline">\(O(\log n)\)</span>；若形状随机，高度则为 <spanclass="math inline">\(O(\sqrt{n})\)</span>。</strong></p><blockquote><p>Prof.前者证明参考算法导论二叉搜索树最后一节，后者证明参考论文，过于复杂，</p></blockquote><h3 id="完全二叉树">完全二叉树</h3><p><strong>定义：除了最后一层外，其他各层全是满的，并且最后一层的节点尽可能往左靠</strong>。</p><p>性质6：<strong>总点数为 <span class="math inline">\(n\)</span>的完全二叉树的非叶子节点如下</strong> <span class="math display">\[\begin{equation}完全二叉树,\sum _{叶子节点}1=\lceil \dfrac{n}{2}\rceil\end{equation}\]</span></p><blockquote><p>Prof. 完全二叉树只有最后一层度为 <spanclass="math inline">\(1\)</span> 的点为 <spanclass="math inline">\(0,1\)</span>，即 <spanclass="math inline">\(n_1=0,1\)</span>，代入 <spanclass="math inline">\(n_0=n_2+1,n=n_0+n_1+n_2\)</span>，解得 <spanclass="math inline">\(n_0=\dfrac{n}{2},\dfrac{n+1}{2}=\lceil\dfrac{n}{2}\rceil\)</span>。</p></blockquote><p>性质7：<strong>🍃节点数为 <span class="math inline">\(n\)</span>的完全二叉树节点总数为 <span class="math inline">\(2n-1,2n\)</span>，共<span class="math inline">\(2\)</span> 种。</strong></p><blockquote><p>Prof. 注意完全二叉树度为 <span class="math inline">\(1\)</span>的节点个数为 <spanclass="math inline">\(n_1=0,1\)</span>，有两种情况。</p></blockquote><p>完全二叉树的🍃可能在最后一层，可能在倒数第二层，对应两种情况。例如一棵高度为<span class="math inline">\(h\)</span> 的完全二叉树第 <spanclass="math inline">\(i\)</span> 层有 <spanclass="math inline">\(x\)</span>个叶子，计算完全二叉树的节点数需要考虑该层为倒数第二层或者倒数第一层。</p><p>性质8：<strong>完全二叉树从 <span class="math inline">\(1\)</span>开始顺次标号，则标为 <span class="math inline">\(n\)</span>的节点左右节点为 <span class="math inline">\(2n\)</span> 和 <spanclass="math inline">\(2n+1\)</span>。</strong></p><blockquote><p>Note. 一般的二叉树没有这样的性质，其放在数组中有许多元素为空。</p></blockquote><h3 id="平衡二叉树">平衡二叉树</h3><p><strong>定义：<span class="math inline">\(\forall\节点,|h_{l}-h_{r}|\leq 1\)</span>，即任意节点左右子树的高度差不大于<span class="math inline">\(1\)</span>。</strong></p><p>性质9：令数列 <span class="math inline">\(\text{Fib}(n)\)</span> 满足<spanclass="math inline">\(\text{Fib(1)}=\text{Fib(2)}=1,\text{Fib(n+2)}=\text{Fib(n+1)}+\text{Fib(n)}\)</span>，<strong>则总高度为<span class="math inline">\(h\)</span> 的平衡二叉树节点数量满足</strong><span class="math display">\[\begin{equation}V_h \in\left[\operatorname{Fib}(h+2)-1,2^h-1\right]\end{equation}\]</span></p><blockquote><p>Prof. 对树高为 <span class="math inline">\(h\)</span>平衡二叉树，构造左右子树分别为 <span class="math inline">\(h-1\)</span>和 <span class="math inline">\(h-2\)</span>对应的最少平衡二叉树，满足平衡二叉树条件，且任意少一个节点要么让树高小于<spanclass="math inline">\(h\)</span>，要么不平衡，该构造为充分必要的，从而<spanclass="math inline">\(V_{\min}(h)=V_{\min}(h-1)+V_{\min}(h-2)+1\)</span>，则左边得证。右边为满二叉树情形<span class="math inline">\(2^0+2^1+\cdots+2^{h-1}=2^h-1\)</span>。</p></blockquote><h3 id="真二叉树">真二叉树</h3><p>定义：<strong>所有节点的度为 <span class="math inline">\(0\)</span>或 <span class="math inline">\(2\)</span>的二叉树，如哈夫曼树。</strong></p><h2 id="树的应用">树的应用</h2><h3 id="森林转换">森林转换</h3><p>有序树定义为<strong>兄弟节点有次序的树</strong>。</p><p><strong>有序树转换为二叉树</strong>：先将每棵多叉树转换为二叉树，每个节点而言，<strong>“左孩子右兄弟”</strong>，即初始左孩子仍为其左孩子，初始（右侧）兄弟转换为右孩子。</p><p><strong>二叉树转换为有序树</strong>：作为上述过程的逆过程，将右孩子变为兄弟即可。</p><p>性质1：<strong>一棵森林中树的数目等于总节点数减去总边数。</strong></p><p>性质2：<strong>有序树的遍历仅有前序和后序，分别等于转换后二叉树的前序和中序。</strong></p><blockquote><p>Prof. 构造基本有序树 <span class="math inline">\(\begin{gathered}b\\newline/\quad \backslash\\a\quad\ \c\end{gathered}\)</span>，其转换为二叉树 <spanclass="math inline">\(\begin{gathered}b\ \\/\quad \newlinea\longrightarrowc\end{gathered}\)</span>，满足前者前序序列等于后者前序序列 <spanclass="math inline">\(bac\)</span>，前者后序序列等于后者中序序列 <spanclass="math inline">\(acb\)</span>。</p></blockquote><p>性质3：<strong>给定有序树的前序和后序遍历能完全确定该有序树。</strong></p><blockquote><p>Prof. 由性质2和重构二叉树的性质即证。</p></blockquote><p><strong>森林转换为二叉树：</strong>先将每棵多叉树转换为二叉树，然后将所有二叉树根看成兄弟（假定有隐藏根节点），转换为一棵二叉树。</p><p><img src="/img/数据结构总复习笔记/image-20221221162312328.png" alt="image-20221221162312328" style="zoom: 20%;" /></p><p>性质4：<strong>原森林中叶节点数量等于二叉树中无左孩子节点数量。</strong></p><blockquote><p>Prof.原森林叶节点可能有兄弟导致在二叉树中有右孩子，但由于其没有孩子，其在二叉树中一定没有左孩子，两者一一映射，数量相同。</p></blockquote><p>性质5：<strong>一棵树非🍃节点有 <spanclass="math inline">\(m\)</span> 个，其转换为二叉树后无右孩子的节点有<span class="math inline">\(m+1\)</span> 个。</strong></p><blockquote><p>Prof. <span class="math inline">\(m\)</span>也为有孩子的节点数量，每个有孩子的节点，其有唯一的最右孩子，该孩子转换为二叉树之后无右孩子，其余孩子由于有右边的兄弟则有右孩子。还要加上根节点，其无兄弟，转换后无右孩子。</p></blockquote><p><strong>二叉树转换为森林</strong>：左孩子不变，把右孩子转换为自己的兄弟，然后从根节点开始，最长右孩子序列每个元素对应森林中每棵🌲的根。</p><h3 id="哈夫曼树">哈夫曼树</h3><p><strong>哈夫曼树（</strong>也叫<strong>最优编码树）</strong>：每个🍃带权，使得带权路径长度最小的二叉树，构建方法：森林<span class="math inline">\(F\)</span> 初始化为 <spanclass="math inline">\(n\)</span> 个权值的节点，重复下述操作直到 <spanclass="math inline">\(F\)</span> 仅含一棵二叉🌲：</p><ol type="1"><li>在 <span class="math inline">\(F\)</span>中选取两棵根节点的权值最小的树作为左右子树构造一个新的二叉树，且置新的二叉树的根节点的权值为左右子树上根节点的权值之和；</li><li>在 <span class="math inline">\(F\)</span>中删除这两棵树，同时将新得到的二叉树加入 <spanclass="math inline">\(F\)</span> 中。</li></ol><p>对二叉🌲，每个🍃标定“路线”（例如：<spanclass="math inline">\(\{0110,10,1110,1111,110,00,0111,010\}\)</span>），保证不是前缀码，为最优编码。</p><p><strong>最小带权路径长度（WPL）</strong>：构建的哈夫曼树对每个🍃节点以及路径长度加权求和，满足等于使用二进制编码字符串的最小长度。</p><p>性质6： <strong><span class="math inline">\(n\)</span>个节点的哈夫曼树对应 <span class="math inline">\(\dfrac{n+1}{2}\)</span>个叶子节点。</strong></p><blockquote><p>Prof. 由 <span class="math inline">\(n_0+n_2=n,n_0=n_2+1\)</span>即得。</p></blockquote><p>性质7：<strong>出现次数最多和最少的字符哈夫曼编码中位数一定为最短和最长。</strong></p><blockquote><p>Prof. 使用反证法易证。</p></blockquote><p><strong>多叉树的带权最短路径</strong>：要求严格 <spanclass="math inline">\(m\)</span> 叉树，通过 <spanclass="math inline">\(mn_{m}+1=n_0+n_{m}=n\)</span> 补若干个 <spanclass="math inline">\(0\)</span> （即不影响权的情况下适当增加 <spanclass="math inline">\(n\)</span>）使得上述方程有整数解，之后仿造哈夫曼编码的方式，每次选择<span class="math inline">\(m\)</span> 棵树进行合并。</p><h3 id="avl-树">AVL 树</h3><p>保证搜索树平衡，防止其退化为链式树。</p><p><strong>选择 <span class="math inline">\(g, p, v\)</span></strong>：<span class="math inline">\(g\)</span>为<strong>最小失衡子树根节点</strong> ，<spanclass="math inline">\(p\)</span> 和 <spanclass="math inline">\(v\)</span> 分别为 <spanclass="math inline">\(g\)</span> 和 <spanclass="math inline">\(p\)</span> 孩子中的<strong>较高者</strong>。</p><p><strong>调整：</strong>从左往右依次为 <spanclass="math inline">\(\text{zig-zig,zag-zag,zag-zig,zig-zag}\)</span>四种旋转方式。</p><p><img src="/img/数据结构总复习笔记/image-20221223160212730.png" alt="image-20221223160212730" style="zoom: 20%;" /></p><p>例如顺次加入 <span class="math inline">\(7, 6, 4, 10, 8, 11\)</span>六个节点，形成的 AVL 树如下</p><p><img src="/img/数据结构总复习笔记/image-20221226210942607.png" alt="image-20221226210942607" style="zoom: 20%;" /></p><p>性质8：AVL🌲插入结点之后若不平衡，则调整之后与插入之前的🌲高相同。</p><blockquote><p>Prof.如果添加之后发生旋转，一定能让多出的一层通过旋转减少，AVL🌲仅有不旋转的时候“增高”。</p></blockquote><p><strong>删除：</strong>如果删除的节点 <spanclass="math inline">\(P\)</span> 在根节点上，用 <spanclass="math inline">\(P\)</span> 的中序直接前驱 <spanclass="math inline">\(O\)</span> 替代，进而替代删除 <spanclass="math inline">\(O\)</span>，由此进行递归删除</p><p><img src="/img/数据结构总复习笔记/image-20221223160646898.png" alt="image-20221223160646898" style="zoom:25%;" /></p><h2 id="高级搜索树">高级搜索树</h2><h3 id="b-树">B 树</h3><p><span class="math inline">\(m\)</span> 阶B🌲又称 <spanclass="math inline">\(\left(\left\lceil\dfrac{m}{2}\right\rceil,m\right)\)</span>🌲，代表其分支数的取值范围，满足以下几种约束</p><ol type="1"><li><p>🍃的深度相同，根节点度数 <spanclass="math inline">\(\in[2,m]\)</span>；</p></li><li><p>非根节点度数 <span class="math inline">\(\in \left[\left\lceil\dfrac{m}{2}\right\rceil,m\right]\)</span>；</p></li><li><p>非🍃节点度数比关键码多 <spanclass="math inline">\(1\)</span>。</p></li></ol><p><img src="/img/数据结构总复习笔记/image-20221221215435138.png" alt="image-20221221215435138" style="zoom: 25%;" /></p><p>性质1：<strong>B🌲下层与上层分支数之差等于本层关键码数</strong>，令<span class="math inline">\(L_{h}\)</span> 为第 <spanclass="math inline">\(h\)</span> 层下层分支数，<spanclass="math inline">\(L_{h-1}\)</span> 为第 <spanclass="math inline">\(h\)</span> 层上层分支数，<spanclass="math inline">\(N_{h}\)</span> 为第 <spanclass="math inline">\(h\)</span> 层关键码数，则有 <spanclass="math display">\[\begin{equation}L_{h-1}+N_h=L_h\end{equation}\]</span></p><blockquote><p>Prof. 对上一层的每个分支，假设关键码有 <spanclass="math inline">\(n_i\)</span> 个，其下一层假设有 <spanclass="math inline">\(l_i\)</span> 个分支，由约束(2)，可知 <spanclass="math inline">\(1+n_i=l_i\)</span>，对上层每个分支求和即得。</p></blockquote><p>该性质可以结合边界条件 <span class="math inline">\(L_{-1}=1\)</span>得 <span class="math display">\[\begin{equation}L_h=1+\sum_{i=1}^h N_i\end{equation}\]</span> 性质2：<span class="math inline">\(m\)</span> 阶 B🌲总高度满足<span class="math display">\[\begin{equation}\log _m(N+1) \leq h \leq \log_{\left\lceil\frac{m}{2}\right\rceil}\left\lfloor\frac{N+1}{2}\right\rfloor+1\end{equation}\]</span></p><blockquote><p>Prof. 由约束(2)(3)可知度数满足条件 <spanclass="math inline">\(d_{i}\in\left[\lceil\dfrac{m}{2}\rceil,m\right]\)</span>，🌲最矮的情况对应每个节点都填满<span class="math inline">\(m-1\)</span> 个值，有 <spanclass="math inline">\((m-1)\cdot(1+m+\cdots+m^{h-1})=m^{h}-1=N\)</span>，左边得证；🌲最高的情况对应根节点度数为<span class="math inline">\(2\)</span>，每个节点填满 <spanclass="math inline">\(\lceil \dfrac{m}{2}\rceil-1\)</span> 个值，第<span class="math inline">\(i\)</span> 层有 <spanclass="math inline">\(2\cdot \left( \left\lceil\dfrac{m}{2}\right\rceil\right)^{i}\)</span> 组，<spanclass="math inline">\(1+\left(\lceil\dfrac{m}{2}\rceil-1\right)\cdot2\cdot\left[ \left( \left\lceil\dfrac{m}{2}\right\rceil\right)+\cdots+ \left( \left\lceil\dfrac{m}{2}\right\rceil\right)^{h-1}\right]=1+2\left[\left( \left\lceil\dfrac{m}{2}\right\rceil\right)^{h-1}-1\right]=N\)</span>，右边得证。</p></blockquote><p>另一种写法为，给定总高度 <spanclass="math inline">\(h\)</span>，<span class="math inline">\(m\)</span>阶 B🌲总节点数的取值范围为 <span class="math display">\[\begin{equation}\left[2 \cdot\left(\left\lceil\frac{m}{2}\right\rceil\right)^{h-1}-1,m^h-1\right]\end{equation}\]</span></p><blockquote><p>Note. B🌲总节点数和关键码数不同，后者可能不考虑叶子节点，对应 <spanclass="math inline">\(h\)</span> 减 <spanclass="math inline">\(1\)</span>。</p></blockquote><p><strong>插入节点</strong>：</p><p><strong>①</strong>根据性质查找插入的位置，如果<code>overflow</code>，就在中间关键码处<code>split</code>（这就是每节点的分支数大于<span class="math inline">\(m/2\)</span>的原因），即：把中间关键码放到它的父亲节点中，并把它原本的左右键值看成它的左右孩子。（<strong>找medieum 之后上溢</strong>）</p><p><strong>②</strong> 如果父亲节点此时也<code>overflow</code>，就不断分裂上溢。</p><p><strong>③</strong> 如果一直 <code>split</code>到根节点，就为根节点创造一个新的<code>root</code>。（允许根节点最少只有两个分支，仅有这种方法能长高，<strong>有爸求爸，没爸造爸</strong>）</p><p><strong>删除节点</strong>：</p><p><strong>①</strong>找到要删的节点，如果不是叶子，交换其与后继的位置，转换为删除其后继，直到删除节点为叶子。</p><p><strong>②</strong> 删除叶子之后如果<code>underflow</code>，先看左右兄弟是否能“借”它一个节点。如果可以，就通过<strong>三角债</strong>的方式从父节点处<code>rotate</code>。</p><p><strong>③</strong>如果不可以，也就是说它和它的兄弟都恰好到下确界，就把它和它的右兄弟以及它们的父亲合并成一个孩子节点。这会导致父亲节点数-1.如果这样，父亲需要重复上述操作。如果已经到了根节点，就意味着树的高度减<span class="math inline">\(1\)</span>。（对树高的改变，和<code>Insert</code>一样，是很少出现的）</p><p>性质3：<strong>如果某个非🍃节点中所有关键码都存在直接后继，则这些后继都是🍃。</strong></p><blockquote><p>Prof. 观察下面这棵B-树，第一层的关键码的直接后继需要迭代两层才能找到，仅有倒数第二层的关键码存在直接后继，后继均为🍃。</p><p><img src="/img/数据结构总复习笔记/image-20221229234821971.png" alt="image-20221229234821971" style="zoom:25%;" /></p></blockquote><h3 id="kd-树">KD 树</h3><p>对于搜索范围 <span class="math inline">\(R\)</span>的每条边，每个节点的 <span class="math inline">\(4\)</span>个孙子中不超过 <span class="math inline">\(2\)</span>个会与它相交，这意味着递归，如下图 (c) 中粗线最多只能交出 <spanclass="math inline">\(2\)</span> 条线，则 <span class="math display">\[Q(n) = 2 + 2Q(n/4), Q(1) = O(1) \Longrightarrow Q(n) = O(\sqrt{n})\]</span><img src="/img/数据结构总复习笔记/image-20221223162515054.png" alt="image-20221223162515054" style="zoom: 25%;" /></p><h2 id="卡特兰数">卡特兰数</h2><p>定义：<span class="math inline">\(n\)</span>个节点可构造的不同二叉树数目，显然其满足递推式 <spanclass="math display">\[\begin{equation}H_n=\sum_{i=0}^{n-1} H_i H_{n-i-1} \quad(n \geq 2)\end{equation}\]</span></p><blockquote><p>Prof. <span class="math inline">\(n\)</span> 个节点的二叉树根节点有<span class="math inline">\(1\)</span> 个，左右分别为 <spanclass="math inline">\(i\)</span> 个和 <spanclass="math inline">\(n-i-1\)</span> 个节点，两者相乘并求和为 <spanclass="math inline">\(H_n\)</span>。</p></blockquote><p>性质1：<spanclass="math inline">\(H_n=\dfrac{(2n)!}{n!(n+1)!}=\dfrac{\begin{pmatrix}2n\\n\end{pmatrix}}{n+1}=1,1,2,5,14,42\cdots\\\!\)</span>其渐进于<spanclass="math inline">\(\sim\dfrac{4^n}{\sqrt{\pi}n^{3/2}}\)</span></p><blockquote><p>Prof. 令生成函数 <spanclass="math inline">\(H(x)=\displaystyle\sum_{n \geq 0} H_nx^n\)</span>，由边界条件 <spanclass="math inline">\(H(0)=H(1)=1\)</span>，由递推式<spanclass="math inline">\(H(x)=1+\displaystyle\sum_{n\geq1}\sum_{i=0}^{n-1}H_{i}H_{n-i-1}x^n=1+x\sum_{n\geq1}\sum_{i=0}^{n-1}H_ix^{i}H_{n-i-1}x^{n-i-1}\)</span>，更换求和顺序，先对<span class="math inline">\(i\)</span> 求和有 <spanclass="math inline">\(\displaystyle\sum_{n\geq1}\sum_{i=0}^{n-1}H_ix^{i}H_{n-i-1}x^{n-i-1}=\sum_{i=0}^{+\infty}H_ix^i\sum_{j=0}^{+\infty}H_{j}x^{j}=H(x)^2\)</span>，则该函数满足方程<span class="math inline">\(H(x)=1+xH(x)^2,H(0)=1\)</span>，取极限下<span class="math inline">\(H(x)=\dfrac{1-\sqrt{1-4x}}{2x}\)</span>，由二项式定理 <spanclass="math inline">\(H(x)=\dfrac{1-\left(1+\displaystyle\sum_{n=1}^{+\infty}\begin{pmatrix}1/2\\n\end{pmatrix}(-4x)^{n}\right)}{2x},\begin{pmatrix}1/2\\n\end{pmatrix}=(-1)^{n-1}\dfrac{(2n-3)!!}{2^nn!}=\dfrac{(-1)^{n-1}(2n-2)!}{2^{2n-1}n!(n-1)!}\)</span>代入得<spanclass="math inline">\(H(x)=\dfrac{\displaystyle\sum_{n=1}^{+\infty}\dfrac{2(2n-2)!}{n!(n-1)!}x^n}{2x}=\displaystyle\sum_{n=1}^{\infty}\dfrac{(2n-2)!}{n!(n-1)!}x^{n-1}=\sum_{n=0}^{\infty}\dfrac{(2n)!}{n!(n+1)!}x^{n}\)</span>，得证。</p></blockquote><p>性质2：<strong><span class="math inline">\(n\)</span>个元素的栈混洗个数有 <span class="math inline">\(H_n\)</span>个。</strong></p><blockquote><p>Prof. 考虑最后一个出栈元素，设其为 <spanclass="math inline">\(k\)</span>，其必须满足前面 <spanclass="math inline">\(k-1\)</span> 个元素入栈且出栈以及原序列中后面<span class="math inline">\(n-k\)</span>个元素入栈且出栈，当两个过程全部完成 <spanclass="math inline">\(k\)</span> 最后出栈，求和得<spanclass="math inline">\(\displaystyle \sum h(k-1)\cdoth(n-k)\)</span>，不重不漏且满足递推式。</p></blockquote><p>性质3：<strong>给定长度为 <span class="math inline">\(n\)</span>的前序遍历，中序遍历数量为 <spanclass="math inline">\(H_n\)</span>。</strong></p><blockquote><p>Prof. 由性质1给出 <span class="math inline">\(H_n\)</span>种二叉树的形态，按照这些形态“输入”前序遍历获得完整的二叉树，每棵二叉树的中序遍历均不同，因为如果相同，由前序遍历和中序遍历完全确定一棵二叉树从而矛盾，故在前序遍历确定的情况下，每个二叉树形态与每个中序遍历一一对应。</p></blockquote><p>性质4：<strong>一个凸 <span class="math inline">\(n+2\)</span>边形，用直线连接若干对顶点使之分成多个三角形，要求每条直线不能相交，共有<span class="math inline">\(H_n\)</span> 种划分方案。</strong></p><blockquote><p>Prof. 任意选取其中一条边以及连接中间第 <spanclass="math inline">\(k\)</span> 和 <spanclass="math inline">\(k+1\)</span> 个顶点，满足递推关系。</p></blockquote><p>性质5：<strong>律师每天从 <span class="math inline">\(A\)</span> 到<span class="math inline">\(B\)</span> 走 <spanclass="math inline">\(2n\)</span>个街区上班，要求不能穿越（但可以碰到）从家到办公室的对角线（只能经过<font color='green'>绿色点</font>），一共有<span class="math inline">\(H_n\)</span> 条可能路线。</strong></p><p><img src="/img/数据结构总复习笔记/image-20221227192655173.png" alt="image-20221227192655173" style="zoom:25%;" /></p><blockquote><p>Prof. 每一次向上走看作入栈，向左走看作出栈，则等价于 <spanclass="math inline">\(n\)</span> 元素的栈混洗个数。</p></blockquote><h2 id="堆">堆</h2><h3 id="堆排序">堆排序</h3><p><strong>Floyd堆合并算法</strong>，将所有<strong>非叶子节点下滤</strong>，由公式(5)，筛选次数为 <spanclass="math inline">\(\sim\dfrac{n}{2}\)</span>。</p><p>性质1：<strong>建堆时比较次数为 <spanclass="math inline">\(O(n)\)</span></strong></p><blockquote><p>Prof. 设高度为 <span class="math inline">\(h\)</span>（ <spanclass="math inline">\(1\)</span> 个节点高度为 <spanclass="math inline">\(0\)</span>）相较之前的逐个插入法 <spanclass="math display">\[\sum _{i=0}^{h}i2^i\Longrightarrow\sum _{i=0}^{h}(h-i)2^i\\(h-1)2^{h+1}+2=O(n\log n)\Longrightarrow2^{h+1}-h-2=n-\log _2(n+1)=O(n)\]</span></p></blockquote><p>性质2：代码实现上<strong>从序列末尾开始向前遍历</strong>，比如从倒数第二层的最后一个节点开始上滤，但同层节点的下滤次序对算法的正确性和效率都没有影响，其互不相干。</p><p>性质3：<strong>合并</strong>两个一般堆与建堆的时间复杂度相同，时间为<span class="math inline">\(O(n)\)</span>。</p><blockquote><p>Prof. 两个一般堆的合并使用 Floyd 算法目前为最快的 <spanclass="math inline">\(O(n)\)</span>，使用左式堆可以降到 <spanclass="math inline">\(O(\log n)\)</span>。</p></blockquote><blockquote><p>程序中的堆是自己生成自己回收，栈是操作系统来分配。</p></blockquote><p>性质4：<strong>堆排序时间复杂度为 <span class="math inline">\(O(n\logn)\)</span>，其中建堆次数共 <span class="math inline">\(n\)</span>次</strong></p><blockquote><p>Prof. 建堆时间 <spanclass="math inline">\(O(n)\)</span>，之后每次移去最值并下滤，操作时间为<span class="math inline">\(O(n\cdot \log n)\)</span>。初始 <spanclass="math inline">\(1\)</span> 次，之后仅用下滤并取极值 <spanclass="math inline">\(n-1\)</span> 次，共 <spanclass="math inline">\(n\)</span> 次。</p></blockquote><h2 id="图">图</h2><h3 id="基本性质">基本性质</h3><p><strong>无向图</strong>中描述<strong>两顶点</strong>之间的关系可以用<code>(v1, v2)</code> 来表示。</p><p><strong>有向图</strong>中描述<strong>两顶点</strong>的单向关系用<code>&lt;v1,v2&gt;</code> 来表示。</p><p><strong>简单路径</strong>：经过的点互相不重复的路径。（回路<strong>不是</strong>简单路径）</p><p><strong>连通分量</strong>：<strong>无向图</strong>中，若 <spanclass="math inline">\(v_i\)</span> 到 <spanclass="math inline">\(v_j\)</span> 有路径，则称 <spanclass="math inline">\(v_i\)</span> 和 <spanclass="math inline">\(v_j\)</span>是连通的，<strong>无向图极大连通子图的集合为连通分量。</strong></p><p><strong>强连通分量</strong>：<strong>有向图</strong>中，若从 <spanclass="math inline">\(v_i\)</span> 到 <spanclass="math inline">\(v_j\)</span> 以及从 <spanclass="math inline">\(v_j\)</span> 到 <spanclass="math inline">\(v_i\)</span> 有路径，则称 <spanclass="math inline">\(v_i\)</span> 和 <spanclass="math inline">\(v_j\)</span>是连通的，<strong>有向图极大强连通子图的集合为强连通分量</strong>。</p><p><strong>平面图</strong>：可以画在平面上且不同的边互不交叠的图，判定<spanclass="math inline">\(V-E+F=C+1\)</span>，分别对应顶点、边、面、连通数目，满足<span class="math inline">\(e \leq 3 \times v-6=O(v) \ll v^2\)</span>。</p><p>有 <span class="math inline">\(e\)</span>条边的无向图，在邻接表中共有 <span class="math inline">\(n+e\cdot2=n+2e\)</span> 个节点（注意对称性）。</p><p>邻接表的空间复杂度为 <spanclass="math inline">\(O(n+e)\)</span>，邻接矩阵的空间复杂度为 <spanclass="math inline">\(O(n^2)\)</span></p><p>性质1：<strong>无向图的邻接矩阵为对称矩阵 <spanclass="math inline">\(A=A^{T}\)</span>，点为 <spanclass="math inline">\(i\)</span> 的度为 <spanclass="math inline">\(\displaystyle \sum_{j=1}^n A[i, j]=\sum_{i=1}^nA[i, j]\)</span>。</strong></p><blockquote><p>Prof. 对无向图中的一个点而言，其入度等于出度，</p></blockquote><p>性质2：<strong><span class="math inline">\(n\)</span>节点的连通无向图至少有 <span class="math inline">\(n-1\)</span>条边（邻接矩阵有 <span class="math inline">\(2(n-1)\)</span>个元素），连通有向图至少有 <span class="math inline">\(n\)</span>条边。</strong></p><blockquote><p>Prof. 对无向图而言，<span class="math inline">\(n-2\)</span>条边无法连接所有顶点，顺次连接 <span class="math inline">\(n\)</span>个点最少只需要 <span class="math inline">\(n-1\)</span>条边；对有向图形成一个大环为最少的边数。</p></blockquote><p>性质3：<strong>要保证在任意连接方式下都能连通 <spanclass="math inline">\(n\)</span> 个点，至少需要 <spanclass="math inline">\(\dfrac{n(n-1)}{2}+1\)</span> 个点。</strong></p><blockquote><p>Prof. 最坏情况为 <span class="math inline">\(n-1\)</span>个点构成完全图，最后连接剩下 <span class="math inline">\(1\)</span>个点。</p></blockquote><p>性质4：<strong>深度优先搜索生成的序列与图是否有向密切相关。</strong></p><blockquote><p>E.g. 对边集 <span class="math inline">\(E={&lt;v_0,v_1&gt;,&lt;v_0,v_2&gt;, &lt;v_0,v_3&gt;, &lt;v_1,v_3&gt;}\)</span> 从 <spanclass="math inline">\(v_0\)</span> 开始的深度优先搜索一共有 <spanclass="math inline">\(5\)</span> 种，其中 <spanclass="math inline">\(v_0\rightarrow v_3\rightarrow v_2\rightarrowv_1\)</span> 容易遗漏。</p></blockquote><h3 id="拓扑排序">拓扑排序</h3><p><strong>步骤：每次选择入度为 <span class="math inline">\(0\)</span>的点入队，剪去出队元素的所有边，重复操作，出队序列为拓扑序列。</strong>（拓扑排序得到的序列不唯一）</p><p>性质5：<strong>如果一个有向图的邻接矩阵对角线以下的元素非 <spanclass="math inline">\(0\)</span>，则该图存在拓扑排序。</strong></p><blockquote><p>Prof. 说明任意边 <span class="math inline">\(v_{i}\longrightarrowv_j\)</span> 中 <span class="math inline">\(i&lt;j\)</span>，若存在环<span class="math inline">\(v_i,v_j,v_k\)</span>，则满足 <spanclass="math inline">\(i&lt;j,j&lt;k,k&lt;i\)</span>矛盾，从而该有向图无环，存在拓扑排序。</p></blockquote><h3 id="最小生成树算法">最小生成树算法</h3><p>图的深度优先搜索能给出图的一棵<strong>支撑树</strong>，对应二叉树的<strong>先序遍历</strong>。</p><p><img src="/img/数据结构总复习笔记/image-20221228204231261.png" alt="image-20221228204231261" style="zoom:15%;" /></p><p><strong>生成树：</strong>连通图 <spanclass="math inline">\(G\)</span> 的某个子图是一颗包含 <spanclass="math inline">\(G\)</span> 所有顶点的树。</p><p><strong>Cayley 公式</strong>：<span class="math inline">\(n\)</span>个顶点的完全图的生成树总数为 <spanclass="math inline">\(n^{n-2}\)</span>。</p><blockquote><p>Prof. 设结果为 <spanclass="math inline">\(T(n)\)</span>，考虑生成树序列的个数<strong>算两次</strong>，第一种计算方式为每棵树的排列为<span class="math inline">\(T(n)\cdot n!\)</span>，第二种方式为考虑第<span class="math inline">\(k\)</span> 次加入的边，当前有 <spanclass="math inline">\((n-k+1)\)</span>棵树，边的一端任选，另一端不能为前者所在的树，每次有 <spanclass="math inline">\(n\cdot (n-k+1-1)=n(n-k)\)</span> 种选法，求积为<span class="math inline">\(\displaystyle\prod_{k=1}^{n-1}n(n-k)=n^{n-1}\cdot (n-1)!\)</span>，两者相等 <spanclass="math inline">\(T(n)n!=n^{n-1}\cdot (n-1)!\LongrightarrowT(n)=n^{n-2}\)</span>。</p></blockquote><p>性质1：<span class="math inline">\(n\)</span> 个顶点、<spanclass="math inline">\(n\)</span> 条边的连通图至少有 <spanclass="math inline">\(3\)</span> 棵生成树。</p><blockquote><p>Prof. 连通图一定有 <span class="math inline">\(n-1\)</span>条边的生成树，多一条边一定能通过其共同祖先形成环，该环至少有 <spanclass="math inline">\(3\)</span> 条边，任一去掉一条边均为生成树，至少<span class="math inline">\(3\)</span> 棵生成树，</p></blockquote><p><strong>最小生成树（MST）：</strong>所有生成树中权值之和最小的树，最小生成树有两种算法。</p><h4 id="kruskal-算法">Kruskal 算法</h4><p>算法流程：<strong>按权值递增顺序依次选取 <spanclass="math inline">\(n-1\)</span>条边，始终保证不构成回路。</strong></p><p>设图的边数量为 <span class="math inline">\(e\)</span>，前者需要排序<span class="math inline">\(O(e\log e)=O(e\log v)\)</span>，判断是否属于同一个树的时间为 <span class="math inline">\(O((v+e)\cdot\alpha(v))\)</span>，其中 <span class="math inline">\(\alpha(v)\)</span>为阿克曼函数，对目前规模数据 <span class="math inline">\(\alpha(v)\leq4\)</span>，从而总的复杂度为 <span class="math inline">\(O(e\logv)\)</span> ，适用于稀疏图。</p><h4 id="prim-算法">Prim 算法</h4><p>算法流程：<strong>重复加入当前节点与剩余节点距离最近的边，即一棵树以极短跨边生长。</strong></p><p>使用<strong>优先级队列</strong>取出最小边以及加入当前可跨边进行下滤和上滤操作，判定边是否使用可以使用一组布尔变量<span class="math inline">\(O(1)\)</span> 时间内判定，则消耗时间 <spanclass="math inline">\(O(e+(v+e)\log v)=O(e\log v)\)</span>。</p><p>可以看出，两种算法在渐进时间复杂度上是相同的。</p><blockquote><p>Note. 上述两种算法更改选择方式可以得到“最大生成树”。</p></blockquote><p>带权连通图的任意一个环中包含的边的权值均不相同时，其MST是唯一的（充分条件）</p><h3 id="最短路径算法">最短路径算法</h3><h4 id="dijkstra-算法">Dijkstra 算法</h4><p>求解<strong>权值非负单源最短路径</strong>问题。</p><p>算法流程：<strong>每次考虑当前点集的最短路径对应点的邻边，对所有点集的最短路径更新最短路径以及前驱，层层遍历，得到所有最短路径。</strong></p><p>算法共三组与顶点一一对应的数组，<strong>空间复杂度</strong>为 <spanclass="math inline">\(O(v)\)</span>。</p><p><strong>时间复杂度</strong>，时间主要消耗在寻找当前点集的最短路径，次数为<span class="math inline">\(O(1+\cdots+(v-1)+e)\)</span>，由于 <spanclass="math inline">\(O(e)=O(v^2)\)</span>，则不加优化为 <spanclass="math inline">\(O(v^2)\)</span>。</p><p>使用优先级队列在每次加入当前遇到的顶点时上滤，取出最小值之后下滤，每次操作<span class="math inline">\(O(\log v)\)</span>，共计 <spanclass="math inline">\(O(v+e)\)</span> 次操作，故可以优化为 <spanclass="math inline">\(O((v+e)\logv)\)</span>，可以看出<strong>对稀疏图较好</strong>，对稠密图反而不好。</p><h4 id="bellman-ford-算法">Bellman-Ford 算法</h4><p>求解<strong>权值任意单源最短路径</strong>问题</p><p>算法流程：<strong>设源点为 <spanclass="math inline">\(u\)</span>，构造 <spanclass="math inline">\(\text{dist}^{k}[v]\)</span> 代表源点最多经过 <spanclass="math inline">\(k\)</span> 次到 <spanclass="math inline">\(v\)</span>点的最短路径，记遍历当前有效点的邻边求最小值更新 <spanclass="math inline">\(\text{dist}\)</span> 数组为一次操作，重复操作<span class="math inline">\(v\)</span> 次。</strong></p><blockquote><p>Prof. 正确性，如果没有负环，每条最短路径最多只需要 <spanclass="math inline">\(n-1\)</span> 次便能确定，前 <spanclass="math inline">\(n-1\)</span>次计算最短路径，最后一次如果还有更新，则代表有负环，不存在最短路径。</p></blockquote><p>算法时间复杂度为 <span class="math inline">\(O(ve)\)</span>，共进行<span class="math inline">\(v\)</span>次循环，每次循坏最大可能遍历所有边。</p><h4 id="floyd-warshall-算法">Floyd-Warshall 算法</h4><p>求解<strong>权值任意多源最短路径</strong>：标定 <spanclass="math inline">\(\text{dist}[u][v]\)</span> 为从 <spanclass="math inline">\(u\)</span> 到 <spanclass="math inline">\(v\)</span> 的最短路径，对所有边进行初始化，对<span class="math inline">\(k:0\to v\)</span>，遍历 <spanclass="math inline">\(u\)</span> 和 <spanclass="math inline">\(v\)</span>，通过 <spanclass="math inline">\(\text{dist}[u][k]\)</span> 和 <spanclass="math inline">\(\text{dist}[u][k]\)</span> 对 <spanclass="math inline">\(\text{dist}[u][v]\)</span> 松弛。</p><p>时间复杂度 <span class="math inline">\(O(v^3)\)</span>，空间复杂度为<span class="math inline">\(O(v^2)\)</span>。</p><h2 id="散列">散列</h2><p>评判一个散列函数的四个维度</p><ol type="1"><li>确定：同一关键码总是被映射到同一地址</li><li>快速：计算时间复杂度为 <spanclass="math inline">\(O(1)\)</span></li><li>满射：尽可能充分地覆盖整个散列空间</li><li>均匀：映射到各位置的概率尽量接近，避免聚集</li></ol><h3 id="散列函数">散列函数</h3><ol type="1"><li>直接寻址法，直接取关键字的某个线性映射</li><li>MAD ( <strong>Multiply-Add-Divide</strong> )法，防止相邻关键码依然相邻</li></ol><p><span class="math display">\[\text{Hash}(\text {key})=(\mathbf{a} \times \mathbf{k e y}+\mathbf{b})\% \mathbf{M}\]</span></p><ol start="2" type="1"><li><p>数字分析法，例如手机后四位</p></li><li><p>平方取中法，例如f(1234)=<del>15</del>227<del>56</del>=227</p></li><li><p>折叠法，取若干位数相同的部分求和</p></li><li><p>伪随机数法，<spanclass="math inline">\(\text{Rand(A)}\)</span></p></li></ol><h3 id="散列冲突解决方法">散列冲突解决方法</h3><ol type="1"><li><p>多槽位法，预先几个槽</p></li><li><p>独立链法，链表</p></li><li><p>公共溢出法，冲突元素放置在单独一个地方</p></li><li><p>线性试探法，冲突之后顺次放置，第 <spanclass="math inline">\(k\)</span> 次试探地址 <spanclass="math inline">\(\left(\text{hash}(\text{key})+{k}\right) \%M\)</span>，使用 <strong>lazyRemoval</strong> 防止删除之后断链</p></li><li><p>平方试探法，第 <span class="math inline">\(k\)</span> 次试探地址<span class="math inline">\(\left(\text{hash}(\text{key})+{k^2}\right)\%M\)</span>，能够更快地跳出聚集区</p></li><li><p>双向平方试探法，第 <span class="math inline">\(k\)</span>次试探地址 <spanclass="math inline">\(\left(\text{hash}(\text{key})+{(-1)^{k-1}\left\lceil\dfrac{k}{2}\right\rceil^2}\right)\% M\)</span>。</p></li></ol><p>性质1：若表长为素数 <span class="math inline">\(p\equiv3\pmod{4}\)</span> ，使用双向平方试探法前 <spanclass="math inline">\(\text{p}\)</span> 次试探能遍历所有桶。</p><blockquote><p>Prof. 反证若两次试探 <span class="math inline">\(k_1,k_2\)</span>同时属于偶数、奇数集合 <spanclass="math inline">\(\{2,4,\cdots,p-1\},\{1,3,\cdots,p\}\)</span>中的某个，则<span class="math inline">\(k_1^2\equivk_2^2\pmod{p},(k_1+k_2)(k_1-k_2)\equiv 0\pmod{p}\)</span><spanclass="math inline">\(\Longleftrightarrow p\mid k_1+k_2,k_1-k_2\)</span>，后者均不能满足（之和为偶数）。则必须属于不同集合，此时<span class="math inline">\(k_1^2+k_2^2\equiv0\pmod{p}\)</span>，而二平方和问题只能为 <spanclass="math inline">\(4k+1\)</span> 型素数或者 <spanclass="math inline">\(2\)</span>，这是因为 <spanclass="math inline">\(2\nmid x\Longrightarrow x\equiv1\pmod{4}\)</span>。则对任何情况均无解，不存在冲突，能够在 <spanclass="math inline">\(p\)</span> 次试探中遍历所有桶。</p><p>Note. 当 <span class="math inline">\(M=2^s,s\in \mathbb{Z}\)</span>，第 <span class="math inline">\(k\)</span> 次试探地址为 <spanclass="math inline">\(\left(\text{hash}(\text{key})+\dfrac{k(k+1)}{2}\right)\% M\)</span> 时也能遍历所有桶。</p></blockquote><p><strong>线性探测失败和成功问题</strong>：成功查找次数等于线性探测加入<span class="math inline">\(\text{Hash}\)</span> 值时移动次数加 <spanclass="math inline">\(1\)</span> 的累加。</p><p>性质2：在线性探测策略下，当插入节点的数量小于总数量时，对给定的节点序列，无论插入顺序如何，最后平均成功查找次数相同。</p><blockquote><p>Prof.</p></blockquote><p><strong>组合索引：</strong>多个指标时“拼接”在一起计算哈希值。</p><h2 id="串">串</h2><h3 id="kmp-算法">KMP 算法</h3><p>改进前：<span class="math inline">\(\text{Next}(P, j)=\{0 \leq t&lt;j\mid P[0, t)=P[j-t, j)\}\)</span></p><p>改进后：<span class="math inline">\(\text{Next}(P, j)=\{0 \leq t&lt;j\mid P[0, t)=P[j-t, j)\wedge P(t) \neq P(j)\}\)</span></p><p>算法复杂度为 <spanclass="math inline">\(O(m+n)\)</span>，在最坏情况下依然是该复杂度。</p><h2 id="排序">排序</h2><p>直接插入排序需要挪位置。</p><p>性质1：共 <span class="math inline">\(4\)</span>种不稳定排序，<strong>“快些选堆”</strong>（快速排序、希尔排序、选择排序、堆排序）</p><blockquote><p>Prof.快速排序轴点的选择影响相同值元素的先后顺序；希尔排序由于间隔的存在，相同值元素可能在不同间隔序列中打乱；选择排序将<span class="math inline">\(\text{rank(i)}\)</span> 的元素与第 <spanclass="math inline">\(i\)</span>个元素交换，交换的元素可能与后面某元素相同，顺序打乱；堆排序在不同分支的维护堆操作顺序可能发生改变。</p></blockquote><h3 id="基数排序">基数排序</h3><p>算法：关键码由多个字段组成，可按照优先级从低到高进行桶排序，<strong>低位字段优先</strong>策略<span class="math display">\[\begin{array}{|c|c|c|c|c|c|c|c|}\hline \text { 输入序列 } &amp; 441 &amp; 276 &amp; 320 &amp; 214 &amp;698 &amp; 280 &amp; 112 \\\hline \text { 以个位排序 } &amp; 32{0} &amp; 28{0} &amp; 44{1} &amp;11{2} &amp; 21{4} &amp; 27{6} &amp; 69{8} \\\hline \text { 以十位排序 } &amp; 1{1}2 &amp; 2{1}4 &amp; 3{2}0 &amp;4{4}1 &amp; 2{7}6 &amp; 2{8}0 &amp; 6{9}8 \\\hline \text { 以百位排序 } &amp; {1}12 &amp; {2}14 &amp; {2}76 &amp;{2}80 &amp; {3}20 &amp; {4}41 &amp; {6}98 \\\hline\end{array}\]</span></p><blockquote><p>Prof. 正确性由数学归纳法给出，假设 <spanclass="math inline">\(i-1\)</span> 趟后前 <spanclass="math inline">\(i-1\)</span> 次序的数字排好序，第 <spanclass="math inline">\(i\)</span> 趟如果第 <spanclass="math inline">\(i\)</span>优先级不同，则按照该优先级排序，如果相同，前面工作保证排好序，则总能排好序。</p></blockquote><p><strong>复杂度</strong>： 各字段取值范围 <spanclass="math inline">\(\left[0, M_i\right), i&lt;=t\)</span>，令 <spanclass="math inline">\(M=\max \left(M_i\right)\)</span> <spanclass="math display">\[O\left(n+M_1\right)+O\left(n+M_2\right)+\cdots+O\left(n+M_t\right)=O\left(t\cdot(n+M)\right)\]</span></p><blockquote><p>Note. 基数排序需要的操作次数为定值，桶排序可以看成是基数为 <spanclass="math inline">\(1\)</span> 的基数排序。</p></blockquote><h3 id="快速排序">快速排序</h3><p>性质2：<strong>快速排序的平均时间复杂度为 <spanclass="math inline">\(O(1.386\cdot n\log n)\)</span></strong> <spanclass="math display">\[T(n)=(n-1)+\frac{1}{n} \cdot\sum_{k=0}^{n-1}[T(k)+T(n-k-1)]=(n-1)+\frac{2}{n} \cdot \sum_{k=0}^{n-1}T(k)\]</span> <span class="math inline">\(\begin{cases} n \cdot T(n)-(n-1)\cdot T(n-1)=2 \cdot(n-1)+2 \times T(n-1) \\n \cdot T(n)-(n+1) \cdotT(n-1)=2 \cdot(n-1)\end{cases}\Longrightarrow\dfrac{T(n)}{n+1}-\dfrac{T(n-1)}{n}=\dfrac{4}{n+1}-\dfrac{2}{n}\)</span></p><p>解得 <span class="math inline">\(T(n)=(n+1)\cdot\left(2 \displaystyle\cdot \sum_{k=1}^{n+1} \frac{1}{k}+\frac{2}{n+1}-4\right)\approx 2\ln2\cdot n\log n\)</span></p>]]>
    </content>
    <id>https://learningrate1.com/2022/12/25/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E6%9C%9F%E6%9C%AB%E8%80%83%E8%AF%95%E5%A4%8D%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <link href="https://learningrate1.com/2022/12/25/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E6%9C%9F%E6%9C%AB%E8%80%83%E8%AF%95%E5%A4%8D%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <published>2022-12-24T16:09:13.000Z</published>
    <summary>准备数据结构期末考试的复习笔记，数据结构期末考试已结束，现分享该篇万字笔记。</summary>
    <title>Data structure review</title>
    <updated>2022-12-24T16:09:13.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="数理基础" scheme="https://learningrate1.com/categories/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/"/>
    <category term="物理实验报告" scheme="https://learningrate1.com/categories/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/%E7%89%A9%E7%90%86%E5%AE%9E%E9%AA%8C%E6%8A%A5%E5%91%8A/"/>
    <category term="物理" scheme="https://learningrate1.com/tags/%E7%89%A9%E7%90%86/"/>
    <content>
      <![CDATA[<h1 id="同轴光子晶体实验报告">同轴光子晶体实验报告</h1><center><div style="height:2mm;"></div><div style="font-family:华文楷体;font-size:14pt;">learning_rate 2022.12.12<div style="height:2mm;"></div></center><div><div style="width:54px;float:left; font-family:方正公文黑体;"><b>摘要: </b></div><div style="overflow:hidden; font-family:华文楷体;">本实验使用信号发生器和示波器测试电路传输效率和相位，推导同轴电缆的特征阻抗、传播常数、衰减系数参数，理解相速度和群速度的概念。使用同一种特征阻抗的同轴电缆交替连接组成同轴光子晶体，测量并计算传输效率、等效折射率、群速度等物理量。在同轴晶体中引入缺陷后，观察透射峰并同时测试特性。使用MATLAB 编程计算上述物理量的理论值。实验结果与理论吻合度较好。<div style="height:4mm;"></div></div><div><div style="width:64px;float:left; font-family:方正公文黑体;"><b>关键词: </b></div><div style="overflow:hidden;line-height:16pt">同轴光子晶体; 能带理论; 色散关系; 衰减关系; "超光速"现象</div></div><br></div><center><div style="height:2mm;"></div><div style="font-family:黑体;font-size:14pt;">目录<div style="height:2mm;"></div></center><h2 id="实验仪器">实验仪器</h2><p>信号发生器、示波器、测试电缆（约 <spanclass="math inline">\(1\text{m}\)</span> 长）、同轴电缆（约 <spanclass="math inline">\(5\text{m}\)</span> 长）、三通接头、<spanclass="math inline">\(50\Omega\)</span> 电阻。</p><h2 id="实验原理">实验原理</h2><h3 id="同轴电缆电学参数">同轴电缆电学参数</h3><p>对于两种特征阻抗的同轴电览交替连接构成的同轴光子晶体，当两种电缆的特征阻抗之比<span class="math inline">\(Z_{0 H} / Z_{0 L}\)</span>越大、连接的电缆的数量越多，电磁带隙特性以及快慢光效应越明显。</p><p>常用同轴电缆的特征阻抗为 <span class="math inline">\(75\Omega\)</span> 和 <span class="math inline">\(50 \Omega\)</span>。使用这两种电缆的话阻抗比偏小，且两种电缆内外导体间介质的介电常数 <spanclass="math inline">\(\varepsilon_r\)</span>通不同，对应的半波长以及电缆长度也不同。</p><p>本实验<strong>只使用一种特征阻抗的同轴电缆</strong>。通过并联两根电缆的方法获得另一特征阻抗以及更高的阻抗比。</p><p><img src="/img/同轴光子晶体实验报告/image-20221208210228754.png" alt="image-20221208210228754" style="zoom: 50%;" /></p><center><figcaption><b>图1</b> 单根同轴电缆的等效电路模型</figcaption></center><p>同轴电缆的等效电路如图 <span class="math inline">\(5\)</span>所示，其中 <span class="math inline">\(R,L,G\)</span>分别为单位长度同轴电缆的电阻、电感、电容，且结合电磁学知识可知 <spanclass="math display">\[\begin{equation}\begin{cases}C &amp; =\dfrac{2 \pi \varepsilon_0 \varepsilon_r}{\ln (b / a)} \\L &amp; =\dfrac{\mu_0 \mu_r}{2 \pi} \ln \dfrac{b}{a}\end{cases}\end{equation}\]</span> 其中 <span class="math inline">\(a,b\)</span>为同轴电缆的内外径，<spanclass="math inline">\(\epsilon_r,\mu_r\)</span>分别为同轴电缆内外的相对介电常数以及相对磁导率，沿同轴电缆方向共有两个方向叠加，对应通解为<span class="math display">\[\begin{equation}\begin{aligned}&amp; v=v^{+}(x)+v^{-}(x)=V_0^{+} e^{-\gamma x}+V_0^{-} e^{\gamma x} \\&amp; i=i^{+}(x)+i^{-}(x)=\frac{1}{Z_0}\left(V_0^{+} e^{-\gammax}-V_0^{-} e^{\gamma x}\right)\end{aligned}\end{equation}\]</span> 对应电压波，其中 <spanclass="math inline">\(\gamma=\alpha+j\beta\)</span> 为电缆的传播常数，其表达式如下 <span class="math display">\[\begin{equation}\alpha+j \beta=\sqrt{(R+j \omega L)\left(G+j \omega C\right)}\end{equation}\]</span> 且定义<strong>特征阻抗</strong> <span class="math display">\[\begin{equation}Z_0=\sqrt{\dfrac{R+j \omega L}{G+j \omega C}}\end{equation}\]</span> 本实验中，电缆的损耗很小，有 <spanclass="math inline">\(R\ll\omega L,G\ll\omega C\)</span>，做近似有 <spanclass="math display">\[\begin{equation}\left\{\begin{array}{l}Z_0=\sqrt{\dfrac{L}{C}}=\dfrac{\ln (b / a)}{2 \pi} \sqrt{\dfrac{\mu_0\mu_r}{\epsilon_0 \epsilon_r}} \\\beta=\omega \sqrt{L C}=\omega \sqrt{\epsilon_0 \mu_0 \epsilon_r \mu_r}\end{array}\right.\end{equation}\]</span></p><h3 id="透射与反射">透射与反射</h3><p>对于半无限长的同轴电缆，信号从同轴电缆始端输入后，只有沿 <spanclass="math inline">\(+\)</span>方向传输的行波。实际同轴电缆都是有限长的，此时需考虑同轴电缆末端对信号的影响。</p><p>两根特征阻抗分别为 <span class="math inline">\(Z_0\)</span> 和 <spanclass="math inline">\(Z_1\)</span>的同轴电缆连接在一起，两根同轴电缆之间的连接线很短，远小于信号波长和传输线长度。输入信号通过阻抗为<span class="math inline">\(Z_0\)</span> 的同轴电缆到达连接处的电压为<span class="math inline">\(V_i\)</span>，透射信号在连接处的电压为 <spanclass="math inline">\(V_t\)</span> ，反射信号在连接处的电压为 <spanclass="math inline">\(V_r\)</span> ，满足如下方程 <spanclass="math display">\[\begin{equation}\left\{\begin{array}{l}V_i+V_r=V_t \\V_i / Z_0=V_r / Z_0+V_t / Z_1\end{array}\right.\end{equation}\]</span></p><h3 id="测量同轴光子晶体特性">测量同轴光子晶体特性</h3><p>由群速度公式 <span class="math display">\[\begin{equation}v_g=\dfrac{c} {n+\omega \dfrac{d n}{d \omega}}\end{equation}\]</span></p><p>可测得某一频率下示波器 <span class="math inline">\(\mathrm{Ch}2\)</span> 相对于 <span class="math inline">\(\mathrm{Ch} 1\)</span>的相位差 <spanclass="math inline">\(\varphi\)</span>，投输入待测电路的信号为 <spanclass="math inline">\(\cos \omega t\)</span>，初始相位为 0。输出信号为<span class="math inline">\(\cos (\omega t+\varphi)\)</span>，相位变化为<span class="math inline">\(\varphi\)</span> <spanclass="math display">\[\begin{equation}\cos (\omega t+\varphi)=\cos \omega\left(t-t_p\right) \Rightarrow\varphi=-\omega t_p\end{equation}\]</span> 将同轴光子晶体效为一根折射率为 <spanclass="math inline">\(n\)</span> 的均匀同轴电线，则延时 <spanclass="math inline">\(t_p\)</span> 与等效折射率 <spanclass="math inline">\(n\)</span> 的关系为 <span class="math display">\[\begin{equation}t_p=\dfrac{Ln}{c}\end{equation}\]</span> 公式 (10)(11) 联立计算得到 <span class="math display">\[\begin{equation}n=-\frac{\varphi c}{\omega L}=-\frac{\varphi c}{2 \pi f L}\end{equation}\]</span></p><h2 id="电学参数理论计算mathbfa部分">电学参数理论计算<spanclass="math inline">\((\,\mathbf{A}\,\)</span>部分<spanclass="math inline">\()\)</span></h2><h3 id="衰减系数和幅度">衰减系数和幅度</h3><p>由电压幅度值<strong>每米减少</strong> <spanclass="math inline">\(\mathbf{0.571\%}\ll1\)</span>，从而由衰减幅度公式<span class="math display">\[\begin{equation}V(x)=V_0 e^{-\alpha x}\end{equation}\]</span> 取近似 <span class="math inline">\(V_0e^{-\alpha x}\approxV_0(1-\alpha x)\)</span>，故 <span class="math inline">\(\alpha \approx0.00571\)</span>，或者严格计算有 <span class="math display">\[\alpha =-\ln (1-0.571\%)=0.0057264\text{m}^{-1}\]</span> 代入公式 (1) 计算 <spanclass="math inline">\(10\text{m},20\text{m},30\text{m}\)</span>处的幅度列表如下（保留三位小数）</p><table><thead><tr><th>距离/m</th><th>0</th><th>10</th><th>20</th><th>30</th></tr></thead><tbody><tr><td>幅度/V</td><td>1.000</td><td>0.944</td><td>0.892</td><td>0.842</td></tr></tbody></table><center><figcaption><b>表1</b> 信号幅度随距离的变化</figcaption></center><h3 id="电缆并联">电缆并联</h3><p>由图 1 可知，记每一小段 <span class="math inline">\(\Delta x\)</span>对应的复阻抗为 <span class="math inline">\(\Delta Z\)</span>，总复阻抗为<span class="math inline">\(N\DeltaZ\)</span>，则始末并联和每一小段处并联是等效的 <spanclass="math display">\[\sum_{i}\dfrac{\Delta Z\cdot \Delta Z}{\Delta Z+\DeltaZ}=\dfrac{1}{2}\sum_{i}\Delta Z=\dfrac{\displaystyle \sum_{i}\DeltaZ\cdot \displaystyle \sum_{i}\Delta Z}{\displaystyle \sum_{i}\DeltaZ+\displaystyle \sum_{i}\Delta Z}\]</span> 由并联性质 <span class="math display">\[R^{\prime}=\dfrac{R}{2},L^{\prime}=\dfrac{L}{2},G^{\prime}=2G,C^{\prime}=2C\]</span> 代入公式 (5)(6) 得 <span class="math display">\[\begin{equation}Z_0^{\prime}=\frac{1}{2} Z_0, \alpha^{\prime}=\alpha,\beta^{\prime}=\beta\end{equation}\]</span>一种更简便的想法是电压相同时，电流变成原来的两倍，从而<strong>复阻抗折半</strong>，而每个电缆上的电流衰减幅度以及相位变化与原来相同，总比例也不变，从而传播系数不变。</p><h3 id="高低阻抗交替连接">高低阻抗交替连接</h3><p>由上问可知，高特征阻抗使用单根电缆，低特征阻抗使用并联电缆，且有<span class="math display">\[\begin{equation}\frac{Z_{0 H}}{Z_{0 L}}=2\end{equation}\]</span> 可以通过<strong>多次并联</strong>的方法得到更高的阻抗比。</p><h3 id="频率计算">频率计算</h3><p>由电磁波公式 <span class="math display">\[\begin{equation}f=\frac{v}{\lambda}=\frac{c}{\sqrt{\epsilon_r \mu_r} \lambda}\end{equation}\]</span> 代入计算得 <span class="math display">\[f_0=\frac{c}{\sqrt{\epsilon_r \mu_r} \lambda} \approx \frac{299792458\mathrm{~m} / s}{\sqrt{2.354} \cdot 10 \mathrm{~m}}=19.540\mathrm{~}\mathrm{MHz},\dfrac{1}{2}f_0=9.770 \mathrm{~}\mathrm{MHz}\]</span></p><h2 id="间接测量色散关系mathbfb部分">间接测量色散关系<spanclass="math inline">\((\,\mathbf{B}\,\)</span>部分<spanclass="math inline">\()\)</span></h2><h3 id="正常光子晶体">正常光子晶体</h3><h4 id="测量数据">测量数据</h4><p>以 <span class="math inline">\(1 \mathrm{MHz}\)</span> 为间隔，在<span class="math inline">\(1 \mathrm{MHz}\)</span> 至 <spanclass="math inline">\(20 \mathrm{MHz}\)</span>范围内改变频率，观察示波器 <span class="math inline">\(\mathrm{Ch}1\)</span> 和 <span class="math inline">\(\mathrm{Ch} 2\)</span>的峰峰值及其变化情况，测试电路图如下</p><p><img src="/img/同轴光子晶体实验报告/image-20221210120748918.png" alt="image-20221210120748918" style="zoom: 33%;" /></p><center><figcaption><b>图2</b> 同轴光子晶体测试电路</figcaption></center><p>测量数据整理如下</p><table><thead><tr><th>f (MHz)</th><th>V1pp (V)</th><th>V2pp (V)</th><th>φ (°)</th></tr></thead><tbody><tr><td>1</td><td>10.250</td><td>9.220</td><td>-72.10</td></tr><tr><td>2</td><td>10.260</td><td>9.380</td><td>-139.80</td></tr><tr><td>3</td><td>10.270</td><td>8.750</td><td>147.70</td></tr><tr><td>4</td><td>10.280</td><td>8.730</td><td>80.80</td></tr><tr><td>5</td><td>10.270</td><td>8.250</td><td>2.70</td></tr><tr><td>6</td><td>10.320</td><td>7.590</td><td>-66.40</td></tr><tr><td>7</td><td>10.480</td><td>5.890</td><td>-166.90</td></tr><tr><td>8</td><td>10.510</td><td>2.320</td><td>138.10</td></tr><tr><td>9</td><td>10.570</td><td>1.450</td><td>112.60</td></tr><tr><td>10</td><td>10.640</td><td>1.360</td><td>92.10</td></tr><tr><td>11</td><td>10.750</td><td>1.850</td><td>68.20</td></tr><tr><td>12</td><td>10.860</td><td>4.160</td><td>27.00</td></tr><tr><td>13</td><td>10.980</td><td>8.000</td><td>-63.80</td></tr><tr><td>14</td><td>11.110</td><td>8.160</td><td>-145.60</td></tr><tr><td>15</td><td>11.280</td><td>8.770</td><td>140.40</td></tr><tr><td>16</td><td>11.450</td><td>8.700</td><td>71.10</td></tr><tr><td>17</td><td>11.630</td><td>9.190</td><td>2.40</td></tr><tr><td>18</td><td>11.810</td><td>8.670</td><td>-67.30</td></tr><tr><td>19</td><td>12.060</td><td>9.340</td><td>-131.90</td></tr><tr><td>20</td><td>12.310</td><td>9.030</td><td>155.50</td></tr></tbody></table><center><figcaption><b>表2</b> 正常光子晶体Ch1 和 Ch2 峰峰值和相位差随频率变化</figcaption></center><h4 id="传输效率和等效折射率">传输效率和等效折射率</h4><p>同轴晶体未接入、阻抗匹配状态下 <spanclass="math inline">\(50\Omega\)</span> 电阻上消耗的功率为 <spanclass="math inline">\(𝑃_1\)</span>，接入同轴光子晶体、阻抗匹配状态下<span class="math inline">\(50\Omega\)</span> 电阻上消耗的有功功率为<span class="math inline">\(P_2\)</span>，故计算公式如下 <spanclass="math display">\[\begin{equation}P_i=\frac{V_i^2}{R_0} \quad i=1,2\end{equation}\]</span> 则传输效率 <span class="math display">\[\begin{equation}\eta=\frac{P_2}{P_1}=\left(\frac{V_2}{V_1}\right)^2\end{equation}\]</span> 代入公式 (18)(12)计算效率以及折射率，其中相位差相对小频率来说需要减去 <spanclass="math inline">\(360^{\circ}\)</span>的整数倍，保证相位差是单调递减的，计算以及列表如下</p><table><thead><tr><th style="text-align: center;">f (MHz)</th><th style="text-align: center;">φ' (°)</th><th style="text-align: center;">η</th><th style="text-align: center;">n</th></tr></thead><tbody><tr><td style="text-align: center;">1</td><td style="text-align: center;">-72.10</td><td style="text-align: center;">80.91%</td><td style="text-align: center;">1.71548</td></tr><tr><td style="text-align: center;">2</td><td style="text-align: center;">-139.80</td><td style="text-align: center;">83.58%</td><td style="text-align: center;">1.66313</td></tr><tr><td style="text-align: center;">3</td><td style="text-align: center;">-212.30</td><td style="text-align: center;">72.59%</td><td style="text-align: center;">1.68375</td></tr><tr><td style="text-align: center;">4</td><td style="text-align: center;">-279.20</td><td style="text-align: center;">72.12%</td><td style="text-align: center;">1.66076</td></tr><tr><td style="text-align: center;">5</td><td style="text-align: center;">-357.30</td><td style="text-align: center;">64.53%</td><td style="text-align: center;">1.70025</td></tr><tr><td style="text-align: center;">6</td><td style="text-align: center;">-426.40</td><td style="text-align: center;">54.09%</td><td style="text-align: center;">1.69089</td></tr><tr><td style="text-align: center;">7</td><td style="text-align: center;">-526.90</td><td style="text-align: center;">31.59%</td><td style="text-align: center;">1.79094</td></tr><tr><td style="text-align: center;">8</td><td style="text-align: center;">-581.90</td><td style="text-align: center;">4.87%</td><td style="text-align: center;">1.73065</td></tr><tr><td style="text-align: center;">9</td><td style="text-align: center;">-607.40</td><td style="text-align: center;">1.88%</td><td style="text-align: center;">1.60577</td></tr><tr><td style="text-align: center;">10</td><td style="text-align: center;">-627.90</td><td style="text-align: center;">1.63%</td><td style="text-align: center;">1.49397</td></tr><tr><td style="text-align: center;">11</td><td style="text-align: center;">-651.80</td><td style="text-align: center;">2.96%</td><td style="text-align: center;">1.40985</td></tr><tr><td style="text-align: center;">12</td><td style="text-align: center;">-693.00</td><td style="text-align: center;">14.67%</td><td style="text-align: center;">1.37405</td></tr><tr><td style="text-align: center;">13</td><td style="text-align: center;">-783.80</td><td style="text-align: center;">53.09%</td><td style="text-align: center;">1.43454</td></tr><tr><td style="text-align: center;">14</td><td style="text-align: center;">-865.60</td><td style="text-align: center;">53.95%</td><td style="text-align: center;">1.47109</td></tr><tr><td style="text-align: center;">15</td><td style="text-align: center;">-939.60</td><td style="text-align: center;">60.45%</td><td style="text-align: center;">1.49040</td></tr><tr><td style="text-align: center;">16</td><td style="text-align: center;">-1008.90</td><td style="text-align: center;">57.73%</td><td style="text-align: center;">1.50030</td></tr><tr><td style="text-align: center;">17</td><td style="text-align: center;">-1077.60</td><td style="text-align: center;">62.44%</td><td style="text-align: center;">1.50820</td></tr><tr><td style="text-align: center;">18</td><td style="text-align: center;">-1147.30</td><td style="text-align: center;">53.89%</td><td style="text-align: center;">1.51654</td></tr><tr><td style="text-align: center;">19</td><td style="text-align: center;">-1211.90</td><td style="text-align: center;">59.98%</td><td style="text-align: center;">1.51762</td></tr><tr><td style="text-align: center;">20</td><td style="text-align: center;">-1284.50</td><td style="text-align: center;">53.81%</td><td style="text-align: center;">1.52811</td></tr></tbody></table><center><figcaption><b>表3</b> 正常光子晶体传输效率与等效折射率计算表格</figcaption></center><p>其效率和频率关系如下</p><p><img src="/img/同轴光子晶体实验报告/eta-f.svg" alt="eta-f" style="zoom:67%;" /></p><center><figcaption><b>图3</b> 正常光子晶体传输效率与频率关系曲线</figcaption></center><p>等效折射率与频率的变化如下</p><p><img src="/img/同轴光子晶体实验报告/n-f.svg" alt="n-f" style="zoom:67%;" /></p><center><figcaption><b>图4</b> 正常光子晶体等效折射率与频率关系曲线</figcaption></center><p>由图表可得 <spanclass="math inline">\(f=1,2,3,4,5,13,14,15,16,17,18,19,20\mathrm{MHz}\)</span> 为通带, <span class="math inline">\(f=8,9,10,11\mathrm{MHz}\)</span> 为带隙，在 <spanclass="math inline">\(f=7,8,9,10,11 M H Z\)</span> 出现反常色散。</p><h4 id="群速度">群速度</h4><p>代入公式 (9) ，其中离散化处理 <spanclass="math inline">\(\dfrac{dn}{d\omega}\approx\dfrac{n_{k+1}-n_k}{\omega_{k+1}-\omega_{k}},k=1,\cdots,19\)</span>，列表如下（取<span class="math inline">\(\dfrac{v_g}{c}\)</span> 为纵坐标）</p><table><thead><tr><th>f (MHz)</th><th>ωdn/dω</th><th>vg/c</th></tr></thead><tbody><tr><td>1</td><td>-0.05234</td><td>0.60127</td></tr><tr><td>2</td><td>0.04124</td><td>0.58673</td></tr><tr><td>3</td><td>-0.06900</td><td>0.61929</td></tr><tr><td>4</td><td>0.15799</td><td>0.54983</td></tr><tr><td>5</td><td>-0.04679</td><td>0.60479</td></tr><tr><td>6</td><td>0.60026</td><td>0.43646</td></tr><tr><td>7</td><td>-0.42203</td><td>0.73051</td></tr><tr><td>8</td><td>-0.99904</td><td>1.36686</td></tr><tr><td>9</td><td>-1.00621</td><td>1.66789</td></tr><tr><td>10</td><td>-0.84119</td><td>1.53193</td></tr><tr><td>11</td><td>-0.39378</td><td>0.98418</td></tr><tr><td>12</td><td>0.72587</td><td>0.47621</td></tr><tr><td>13</td><td>0.47518</td><td>0.52364</td></tr><tr><td>14</td><td>0.27029</td><td>0.57426</td></tr><tr><td>15</td><td>0.14856</td><td>0.61015</td></tr><tr><td>16</td><td>0.12638</td><td>0.61475</td></tr><tr><td>17</td><td>0.14183</td><td>0.60605</td></tr><tr><td>18</td><td>0.01941</td><td>0.65106</td></tr><tr><td>19</td><td>0.19927</td><td>0.58245</td></tr><tr><td>20</td><td>N/A</td><td>N/A</td></tr></tbody></table><center><figcaption><b>表4</b> 正常光子晶体群速度计算表格</figcaption></center><p>将群速度和频率作图如下</p><p><img src="/img/同轴光子晶体实验报告/vg_c-f.svg" alt="vg:c-f" style="zoom:67%;" /></p><center><figcaption><b>图5</b> 正常光子晶体群速度与频率关系曲线</figcaption></center><p>可以在处于反常色散频率时群速度可能超光速（对应 <spanclass="math inline">\(f=8 ,9 ,10\text{MHz}\)</span>）。</p><h3 id="缺陷光子晶体">缺陷光子晶体</h3><h4 id="测量数据-1">测量数据</h4><p>在同轴光子晶体中增加一根同轴电缆，形成有缺陷的同轴电缆，按照<strong>4.1</strong> 节内容测量数据结果如下</p><table><thead><tr><th>f (MHz)</th><th>V1pp (V)</th><th>V2pp (V)</th><th>φ (°)</th></tr></thead><tbody><tr><td>1</td><td>10.250</td><td>9.280</td><td>-81.60</td></tr><tr><td>2</td><td>10.260</td><td>9.210</td><td>-163.30</td></tr><tr><td>3</td><td>10.270</td><td>7.860</td><td>119.80</td></tr><tr><td>4</td><td>10.280</td><td>8.570</td><td>44.90</td></tr><tr><td>5</td><td>10.260</td><td>8.540</td><td>-45.00</td></tr><tr><td>6</td><td>10.310</td><td>6.110</td><td>-148.40</td></tr><tr><td>7</td><td>10.380</td><td>2.660</td><td>150.00</td></tr><tr><td>8</td><td>10.430</td><td>1.980</td><td>118.80</td></tr><tr><td>9</td><td>10.490</td><td>2.950</td><td>84.30</td></tr><tr><td>10</td><td>10.560</td><td>4.990</td><td>-34.40</td></tr><tr><td>11</td><td>10.670</td><td>2.340</td><td>-90.10</td></tr><tr><td>12</td><td>10.790</td><td>2.340</td><td>-122.70</td></tr><tr><td>13</td><td>10.910</td><td>4.550</td><td>-172.50</td></tr><tr><td>14</td><td>11.030</td><td>7.790</td><td>96.50</td></tr><tr><td>15</td><td>11.190</td><td>8.470</td><td>4.30</td></tr><tr><td>16</td><td>11.370</td><td>7.760</td><td>-76.20</td></tr><tr><td>17</td><td>11.530</td><td>8.570</td><td>-151.80</td></tr><tr><td>18</td><td>11.720</td><td>8.490</td><td>126.90</td></tr><tr><td>19</td><td>11.960</td><td>8.930</td><td>51.80</td></tr><tr><td>20</td><td>12.220</td><td>8.670</td><td>-27.30</td></tr></tbody></table><center><figcaption><b>表5</b> 缺陷光子晶体Ch1 和 Ch2 峰峰值和相位差随频率变化</figcaption></center><h4 id="传输效率和等效折射率-1">传输效率和等效折射率</h4><p>同理代入公式 (16)(12) 列表如下</p><table><thead><tr><th>f (MHz)</th><th>φ' (°)</th><th>η</th><th>n</th></tr></thead><tbody><tr><td>1</td><td>-81.60</td><td>81.97%</td><td>1.94151</td></tr><tr><td>2</td><td>-163.30</td><td>80.58%</td><td>1.94270</td></tr><tr><td>3</td><td>-240.20</td><td>58.57%</td><td>1.90503</td></tr><tr><td>4</td><td>-315.10</td><td>69.50%</td><td>1.87430</td></tr><tr><td>5</td><td>-405.00</td><td>69.28%</td><td>1.92724</td></tr><tr><td>6</td><td>-508.40</td><td>35.12%</td><td>2.01606</td></tr><tr><td>7</td><td>-570.00</td><td>6.57%</td><td>1.93743</td></tr><tr><td>8</td><td>-601.20</td><td>3.60%</td><td>1.78805</td></tr><tr><td>9</td><td>-635.70</td><td>7.91%</td><td>1.68058</td></tr><tr><td>10</td><td>-754.40</td><td>22.33%</td><td>1.79495</td></tr><tr><td>11</td><td>-810.10</td><td>4.81%</td><td>1.75225</td></tr><tr><td>12</td><td>-842.70</td><td>4.70%</td><td>1.67087</td></tr><tr><td>13</td><td>-892.50</td><td>17.39%</td><td>1.63348</td></tr><tr><td>14</td><td>-983.50</td><td>49.88%</td><td>1.67146</td></tr><tr><td>15</td><td>-1075.70</td><td>57.29%</td><td>1.70628</td></tr><tr><td>16</td><td>-1156.20</td><td>46.58%</td><td>1.71935</td></tr><tr><td>17</td><td>-1231.80</td><td>55.25%</td><td>1.72402</td></tr><tr><td>18</td><td>-1313.10</td><td>52.48%</td><td>1.73570</td></tr><tr><td>19</td><td>-1388.20</td><td>55.75%</td><td>1.73840</td></tr><tr><td>20</td><td>-1467.30</td><td>50.34%</td><td>1.74558</td></tr></tbody></table><center><figcaption><b>表6</b> 缺陷光子晶体传输效率与等效折射率计算表格</figcaption></center><p>其效率和频率关系如下，其中透射峰使用 <spanclass="math inline">\(\textcolor{red}{\swarrow}\)</span>标出，两侧由于多项式拟合的边界效应其峰可以排除</p><p><img src="/img/同轴光子晶体实验报告/eta-f-2.svg" alt="eta-f-2" style="zoom:67%;" /></p><center><figcaption><b>图6</b> 缺陷光子晶体传输效率与频率关系曲线</figcaption></center><p><img src="/img/同轴光子晶体实验报告/n-f-2.svg" alt="n-f-2" style="zoom:67%;" /></p><center><figcaption><b>图7</b> 缺陷光子晶体等效折射率与频率关系曲线</figcaption></center><p>计算<strong>表 6</strong>和<strong>表3</strong>计算的传输效率之比，列表如下</p><table><thead><tr><th>f (MHz)</th><th>η1</th><th>η2</th><th>η2/η1</th></tr></thead><tbody><tr><td>1</td><td>80.91%</td><td>81.97%</td><td>1.013</td></tr><tr><td>2</td><td>83.58%</td><td>80.58%</td><td>0.964</td></tr><tr><td>3</td><td>72.59%</td><td>58.57%</td><td>0.807</td></tr><tr><td>4</td><td>72.12%</td><td>69.50%</td><td>0.964</td></tr><tr><td>5</td><td>64.53%</td><td>69.28%</td><td>1.074</td></tr><tr><td>6</td><td>54.09%</td><td>35.12%</td><td>0.649</td></tr><tr><td>7</td><td>31.59%</td><td>6.57%</td><td>0.208</td></tr><tr><td>8</td><td>4.87%</td><td>3.60%</td><td>0.740</td></tr><tr><td>9</td><td>1.88%</td><td>7.91%</td><td>4.202</td></tr><tr><td>10</td><td>1.63%</td><td>22.33%</td><td>13.667</td></tr><tr><td>11</td><td>2.96%</td><td>4.81%</td><td>1.624</td></tr><tr><td>12</td><td>14.67%</td><td>4.70%</td><td>0.321</td></tr><tr><td>13</td><td>53.09%</td><td>17.39%</td><td>0.328</td></tr><tr><td>14</td><td>53.95%</td><td>49.88%</td><td>0.925</td></tr><tr><td>15</td><td>60.45%</td><td>57.29%</td><td>0.948</td></tr><tr><td>16</td><td>57.73%</td><td>46.58%</td><td>0.807</td></tr><tr><td>17</td><td>62.44%</td><td>55.25%</td><td>0.885</td></tr><tr><td>18</td><td>53.89%</td><td>52.48%</td><td>0.974</td></tr><tr><td>19</td><td>59.98%</td><td>55.75%</td><td>0.929</td></tr><tr><td>20</td><td>53.81%</td><td>50.34%</td><td>0.935</td></tr></tbody></table><center><figcaption><b>表7</b> 缺陷光子晶体传输效率与正常光子晶体传输效率之比</figcaption></center><p>将两者的传输效率之比绘图如下</p><p><img src="/img/同轴光子晶体实验报告/eta2_eta1-f.svg" alt="eta2:eta1-f" style="zoom:67%;" /></p><center><figcaption><b>图8</b> 两种光子晶体传输效率之比随频率的变化曲线</figcaption></center><p>可以看出在 <span class="math inline">\(f=9,10,11\text{MHz}\)</span>处的效率提升非常显著，其余基本在 <span class="math inline">\(1\)</span>附近波动。</p><h4 id="群速度-1">群速度</h4><p>同理离散化代入公式 (9) 计算群速度，列表如下</p><table><thead><tr><th>f (MHz)</th><th>ωdn/dω</th><th>vg/c</th></tr></thead><tbody><tr><td>1</td><td>0.00119</td><td>0.51475</td></tr><tr><td>2</td><td>-0.07534</td><td>0.53552</td></tr><tr><td>3</td><td>-0.09220</td><td>0.55162</td></tr><tr><td>4</td><td>0.21176</td><td>0.47937</td></tr><tr><td>5</td><td>0.44414</td><td>0.42170</td></tr><tr><td>6</td><td>-0.47178</td><td>0.64755</td></tr><tr><td>7</td><td>-1.04570</td><td>1.12142</td></tr><tr><td>8</td><td>-0.85972</td><td>1.07721</td></tr><tr><td>9</td><td>1.02929</td><td>0.36902</td></tr><tr><td>10</td><td>-0.42698</td><td>0.73101</td></tr><tr><td>11</td><td>-0.89521</td><td>1.16681</td></tr><tr><td>12</td><td>-0.44859</td><td>0.81815</td></tr><tr><td>13</td><td>0.49371</td><td>0.47010</td></tr><tr><td>14</td><td>0.48744</td><td>0.46320</td></tr><tr><td>15</td><td>0.19600</td><td>0.52569</td></tr><tr><td>16</td><td>0.07474</td><td>0.55739</td></tr><tr><td>17</td><td>0.19867</td><td>0.52011</td></tr><tr><td>18</td><td>0.04846</td><td>0.56049</td></tr><tr><td>19</td><td>0.13645</td><td>0.53338</td></tr><tr><td>20</td><td>N/A</td><td>N/A</td></tr></tbody></table><center><figcaption><b>表8</b> 缺陷光子晶体群速度计算表格</figcaption></center><p>其群速度和频率的曲线绘制如下</p><p><img src="/img/同轴光子晶体实验报告/vg_c-f-2.svg" alt="vg:c-f-2" style="zoom:67%;" /></p><center><figcaption><b>图9</b> 正常光子晶体和缺陷光子晶体群速度对比曲线</figcaption></center><p>由图像可知，在 <span class="math inline">\(f=9\text{MHz}\)</span>处出现强正常色散，该处的 <span class="math inline">\(v_g\)</span>显著小于 <span class="math inline">\(c\)</span>，为慢光速现象。</p><h2 id="色散关系直接测量mathbfc部分">色散关系直接测量<spanclass="math inline">\((\,\mathbf{C}\,\)</span>部分<spanclass="math inline">\()\)</span></h2><h3 id="特征频率">特征频率</h3><p>在 50 kHz 至 150 kHz范围内改变信号发生器频率，观察脉冲信号的波形、幅度和延时变化情况。从上到下分别为<span class="math inline">\(114、130、160 \text{ kHz}\)</span>时的波形，前两者为幅度最小的两个频率，后者为频率之间信号幅度最大且延时最长的频率。</p><p><img src="/img/同轴光子晶体实验报告/image-20221210211706993.png" alt="image-20221210211706993" style="zoom: 40%;" /></p><center><figcaption><b>图10</b> 频率为 114kHz 脉冲信号波形</figcaption></center><p><img src="/img/同轴光子晶体实验报告/image-20221210212548917.png" alt="image-20221210212548917" style="zoom:40%;" /></p><center><figcaption><b>图11</b> 频率为 130kHz 脉冲信号波形</figcaption></center><p><img src="/img/同轴光子晶体实验报告/image-20221210212612934.png" alt="image-20221210212612934" style="zoom:40%;" /></p><center><figcaption><b>图12</b> 频率为 160kHz 脉冲信号波形</figcaption></center><h3 id="群速度计算">群速度计算</h3><p>对图片截图计算中心波腹的延迟像素与示波器背景单位格点像素比例，例如对第一张图两者像素为333 和 543，故其延迟时间为 <span class="math display">\[t_1=\frac{333 \mathrm{~pix}}{543 \mathrm{~pix}} \cdot 500\mathrm{~ns}=306.6\text{~ns}\]</span> 同理对三幅图片处理列表如下，其中由于设置 <spanclass="math inline">\(100T\)</span>脉冲波形，高频正弦波信号的频率为原始频率的 <spanclass="math inline">\(100\)</span> 倍，使用 <spanclass="math inline">\(\text{MHz}\)</span> 单位方便统一</p><table><thead><tr><th>f (MHz)</th><th>基准像素</th><th>波腹像素</th><th>Δt/ns</th></tr></thead><tbody><tr><td>11.4</td><td>543</td><td>333</td><td>306.6</td></tr><tr><td>13.0</td><td>493</td><td>99</td><td>100.4</td></tr><tr><td>16.0</td><td>498</td><td>164</td><td>164.7</td></tr></tbody></table><center><figcaption><b>表9</b> 像素法延时长度计算</figcaption></center><p>由群速度代表波包的运动，<spanclass="math inline">\(v_{g}=\dfrac{l}{\Delta t}\)</span>，计算 <spanclass="math inline">\(\dfrac{v_g}{c}=\dfrac{l}{c\Delta t}\)</span>列表如下</p><table><thead><tr><th>f (MHz)</th><th>vg/c</th></tr></thead><tbody><tr><td>11.4</td><td>0.3807</td></tr><tr><td>13.0</td><td>1.1628</td></tr><tr><td>16.0</td><td>0.7090</td></tr></tbody></table><center><figcaption><b>表10</b> 群速度直接测量结果</figcaption></center><p>将该结果标注在<strong>图5</strong>中如下，其中 <spanclass="math inline">\(\textcolor{red}{\bullet}\)</span> 代表测量点</p><p><img src="/img/同轴光子晶体实验报告/vg_c-f-2-scatter.svg" alt="vg:c-f-2-scatter" style="zoom:67%;" /></p><center><figcaption><b>图13</b> 利用脉冲直接测量和间接测量群速度比对</figcaption></center><p>可以发现，除中间点外，测量结果基本符合预期。</p><h2 id="传输特性理论计算mathbfd.1-d.4部分">传输特性理论计算<spanclass="math inline">\((\,\mathbf{D.1-D.4}\,\)</span>部分<spanclass="math inline">\()\)</span></h2><h3 id="叠加电压求解">叠加电压求解</h3><p>由公式 (6) 的两个方程反解得 <span class="math display">\[\begin{equation}\begin{cases}V_0^{+} =\dfrac{1}{2}\left(v_2+Z_0 i_2\right) e^{\gamma l} \\V_0^{-} =\dfrac{1}{2}\left(v_2-Z_0 i_2\right) e^{-\gamma l}\end{cases}\end{equation}\]</span></p><h3 id="传递矩阵计算">传递矩阵计算</h3><p>取 <span class="math inline">\(x=0\)</span> 的点对应 <spanclass="math inline">\(v_1,i_1\)</span> <span class="math display">\[\begin{equation}\begin{cases}v_1=v^{+}(0)+v^{-}(0)=V_0^{+}+V_0^{-} \\i_1=i^{+}(0)+i^{-}(0)=\dfrac{1}{Z_0}\left(V_0^{+}-V_0^{-}\right)\end{cases}\end{equation}\]</span> 代入公式 (17) 得 <span class="math display">\[\begin{equation}\begin{aligned}v_1 &amp; =\frac{1}{2}\left(v_2+Z_0 i_2\right) e^{\gammal}+\frac{1}{2}\left(v_2-Z_0 i_2\right) e^{-\gamma l} \\i_1 &amp; =\frac{1}{Z_0}\left(\frac{1}{2}\left(v_2+Z_0 i_2\right)e^{\gamma l}-\frac{1}{2}\left(v_2-Z_0 i_2\right) e^{-\gamma l}\right)\end{aligned}\end{equation}\]</span> 写成矩阵的形式如下 <span class="math display">\[\begin{equation}\left[\begin{array}{l}v_1 \\i_1\end{array}\right]=\left[\begin{array}{cl}\cosh (\gamma l) &amp; Z_0 \sinh (\gamma l) \\\dfrac{1}{Z_0} \sinh (\gamma l) &amp; \cosh (\gamma l)\end{array}\right] \cdot\left[\begin{array}{l}v_2 \\i_2\end{array}\right]\end{equation}\]</span> 则其中 <span class="math inline">\(\left[\begin{array}{cl}\cosh (\gamma l) &amp; Z_0 \sinh (\gamma l) \\\dfrac{1}{Z_0} \sinh (\gamma l) &amp; \cosh (\gamma l)\end{array}\right]\)</span> 为传递矩阵。</p><h3 id="传递矩阵性质">传递矩阵性质</h3><p>将两个同轴电缆串联连接如下</p><p><img src="/img/同轴光子晶体实验报告/image-20221211111629563.png" alt="image-20221211111629563" style="zoom: 33%;" /></p><center><figcaption><b>图14</b> 同轴光子晶体的其中两段级联同轴电缆</figcaption></center><p>由上节传递矩阵的性质 <span class="math display">\[\begin{equation}\left[\begin{array}{l}v_1 \\i_1\end{array}\right]=A_1\left[\begin{array}{l}v_2 \\i_2\end{array}\right],\left[\begin{array}{l}v_2 \\i_2\end{array}\right]=A_2\left[\begin{array}{l}v_3 \\i_3\end{array}\right]\end{equation}\]</span> 后者代入前者可得 <span class="math display">\[\begin{equation}\left[\begin{array}{l}v_1 \\i_1\end{array}\right]=A_1 A_2\left[\begin{array}{l}v_3 \\i_3\end{array}\right]\end{equation}\]</span> 故总的传递矩阵为 <span class="math inline">\(A=A_1A_2\)</span>，从而通过传递矩阵相乘的方法可以得到若干个同轴光子晶体连接形成的等效传递矩阵。</p><h3 id="负载电压表达式">负载电压表达式</h3><p>令总传递矩阵 <span class="math inline">\(A_T=\left[\begin{array}{cc}A&amp; B \\ C &amp; D\end{array}\right]\)</span>，信号源电动势 <spanclass="math inline">\(e\)</span> 与内阻 <spanclass="math inline">\(Z_0\)</span>，联立方程 <spanclass="math display">\[\begin{equation}\left\{\begin{array}{l}{\left[\begin{array}{l}v_1 \\i_1\end{array}\right]=\left[\begin{array}{cc}A &amp; B \\ C &amp;D\end{array}\right]\cdot\left[\begin{array}{l}v_L \\i_L\end{array}\right]} \\v_L=i_L \cdot Z_0 \\e=v_1+i_1 \cdot Z_0\end{array}\right.\end{equation}\]</span> 解得负载电压的表达式 <span class="math display">\[\begin{equation}V_L=\frac{e}{A+\dfrac{1}{Z_0} B+Z_0 C+D}\end{equation}\]</span></p><h2 id="编程计算mathbfd.5部分">编程计算<spanclass="math inline">\((\,\mathbf{D.5}\,\)</span>部分<spanclass="math inline">\()\)</span></h2><p>使用 matlab 对给定数据进行模拟，已知单根同轴电缆的特征阻抗 <spanclass="math inline">\(Z_0=50 \Omega\)</span>，介质参数 <spanclass="math inline">\(\varepsilon_r=2.354 、 \mu_r \approx1\)</span>，衰减系数 <span class="math inline">\(\alpha=\)</span> <spanclass="math inline">\(1.810 \times 10^{-6} \sqrt{f}m^{-1}\)</span>，其中 <span class="math inline">\(f\)</span> 的单位为<span class="math inline">\(\mathrm{Hz}\)</span>。</p><p>由连接图可知，令 <span class="math inline">\(A_1\)</span>为原始同轴电缆，<span class="math inline">\(A_2\)</span>为并联之后的同轴电缆，其总传递矩阵如下 <span class="math display">\[\begin{equation}A_T=A_2 A_1 A_2 A_1 A_2 A_1 A_2\end{equation}\]</span></p><h3 id="程序设计">程序设计</h3><p>将转移矩阵对应的元素设计在函数中，例如第一个元素获取函数<code>A1()</code> 如下</p><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs matlab"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">[a]</span> = <span class="hljs-title">A1</span><span class="hljs-params">(f)</span></span><br>    A1=[<span class="hljs-built_in">cosh</span>(ga(f)*<span class="hljs-number">5</span>) <span class="hljs-number">50</span>*<span class="hljs-built_in">sinh</span>(ga(f)*<span class="hljs-number">5</span>);<span class="hljs-number">1</span>/<span class="hljs-number">50</span>*<span class="hljs-built_in">sinh</span>(ga(f)*<span class="hljs-number">5</span>) <span class="hljs-built_in">cosh</span>(ga(f)*<span class="hljs-number">5</span>)];<br>    A2=[<span class="hljs-built_in">cosh</span>(ga(f)*<span class="hljs-number">5</span>) <span class="hljs-number">25</span>*<span class="hljs-built_in">sinh</span>(ga(f)*<span class="hljs-number">5</span>);<span class="hljs-number">1</span>/<span class="hljs-number">25</span>*<span class="hljs-built_in">sinh</span>(ga(f)*<span class="hljs-number">5</span>) <span class="hljs-built_in">cosh</span>(ga(f)*<span class="hljs-number">5</span>)];<br>    <span class="hljs-comment">% A=A2*A1*A2*A1*A2*A1*A2; % 缺陷光子晶体</span><br>    A=A2*A2*A2*A1*A2*A1*A2;   <span class="hljs-comment">% 正常光子晶体</span><br>    a=A(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>) ;<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure><p>主函数针对<strong>传输效率、等效折射率、群速度</strong>计算，纵坐标标注不同结果</p><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs matlab">f = <span class="hljs-number">0</span>:<span class="hljs-number">0.1</span>:<span class="hljs-number">80</span>;<br>x = f;<br>angs = f;<br><span class="hljs-comment">% 计算效率</span><br><span class="hljs-keyword">for</span> <span class="hljs-built_in">i</span>=<span class="hljs-number">1</span>:<span class="hljs-number">801</span><br>    x(<span class="hljs-built_in">i</span>) = eta(f(<span class="hljs-built_in">i</span>) * <span class="hljs-number">1e6</span>);<br><span class="hljs-keyword">end</span><br><span class="hljs-comment">% 计算相位差以及使用 unwrap() 函数</span><br><span class="hljs-keyword">for</span> <span class="hljs-built_in">i</span>=<span class="hljs-number">1</span>:<span class="hljs-number">801</span><br>   angs(<span class="hljs-built_in">i</span>) = ang(f(<span class="hljs-built_in">i</span>)*<span class="hljs-number">1e6</span>);<br><span class="hljs-keyword">end</span><br>angs = <span class="hljs-built_in">unwrap</span>(angs);<br><span class="hljs-comment">% 计算等效等效折射率</span><br><span class="hljs-keyword">for</span> <span class="hljs-built_in">i</span>=<span class="hljs-number">1</span>:<span class="hljs-number">801</span><br>   x(<span class="hljs-built_in">i</span>) = n(f(<span class="hljs-built_in">i</span>)*<span class="hljs-number">1e6</span>, angs(<span class="hljs-built_in">i</span>));<br><span class="hljs-keyword">end</span><br><span class="hljs-comment">% 计算群速度</span><br>vg = x(<span class="hljs-number">1</span>:<span class="hljs-number">800</span>);<br><span class="hljs-keyword">for</span> <span class="hljs-built_in">i</span>=<span class="hljs-number">1</span>:<span class="hljs-number">800</span><br>   vg(<span class="hljs-built_in">i</span>) = <span class="hljs-number">1</span> / (x(<span class="hljs-built_in">i</span>) + f(<span class="hljs-built_in">i</span>)/<span class="hljs-number">0.1</span>*(x(<span class="hljs-built_in">i</span>+<span class="hljs-number">1</span>)-x(<span class="hljs-built_in">i</span>)));<br><span class="hljs-keyword">end</span><br><span class="hljs-comment">% 对不同情况绘制图像，选定不同的 y 轴标注</span><br><span class="hljs-comment">% plot(f, x);</span><br><span class="hljs-built_in">plot</span>(f(<span class="hljs-number">1</span>:<span class="hljs-number">800</span>), vg);<br>xlabel(<span class="hljs-string">&#x27;$f/MHz$&#x27;</span>, <span class="hljs-string">&#x27;interpreter&#x27;</span>, <span class="hljs-string">&#x27;latex&#x27;</span>, <span class="hljs-string">&#x27;FontSize&#x27;</span>, <span class="hljs-number">15</span>);<br><span class="hljs-comment">% ylabel(&#x27;$\eta$&#x27;, &#x27;interpreter&#x27;, &#x27;latex&#x27;, &#x27;FontSize&#x27;, 20);</span><br><span class="hljs-comment">% ylabel(&#x27;$n$&#x27;, &#x27;interpreter&#x27;, &#x27;latex&#x27;, &#x27;FontSize&#x27;, 20);</span><br>ylabel(<span class="hljs-string">&#x27;$\frac&#123;v_g&#125;&#123;c&#125;$&#x27;</span>, <span class="hljs-string">&#x27;interpreter&#x27;</span>, <span class="hljs-string">&#x27;latex&#x27;</span>, <span class="hljs-string">&#x27;FontSize&#x27;</span>, <span class="hljs-number">20</span>);<br></code></pre></td></tr></table></figure><p>其中使用 <code>unwrap()</code>函数对相位差有跃变的情况加以处理，自动增加 <spanclass="math inline">\(n\pi\)</span>。</p><h3 id="正常光子晶体-1">正常光子晶体</h3><p>取定 <span class="math inline">\(f\in [0,8]\text{~MHz}\)</span>步长为 <span class="math inline">\(0.1\text{~MHZ}\)</span>进行程序模拟，绘制图像如下</p><h4 id="传输效率">传输效率</h4><p><img src="/img/同轴光子晶体实验报告/eta-f-theory.svg" alt="eta-f-theory" style="zoom:67%;" /></p><center><figcaption><b>图15</b> 正常光子晶体效率与频率关系曲线（程序模拟）</figcaption></center><p>从左往右依次为<strong>通带、带隙、通带</strong>交替产生，并且随着频率地增加，多次反射和透射产生的损耗增加，从而引起效率<strong>极值点下降</strong>。</p><h4 id="等效折射率">等效折射率</h4><p><img src="/img/同轴光子晶体实验报告/n-f-theory.svg" alt="n-f-theory" style="zoom:67%;" /></p><center><figcaption><b>图16</b> 正常光子晶体等效折射率与频率关系曲线（程序模拟）</figcaption></center><p>可以看出在频率逐渐增大的过程中存在多个反常色散区域，这与反常色散曲线吻合。</p><h4 id="群速度-2">群速度</h4><p><img src="/img/同轴光子晶体实验报告/vg-f-theory.svg" alt="vg-f-theory" style="zoom:67%;" /></p><center><figcaption><b>图17</b> 正常光子晶体群速度与频率关系曲线（程序模拟）</figcaption></center><p>可以看出在反常色散区域群速度超过光速 <spanclass="math inline">\(c\)</span>，与预期吻合。</p><h3 id="缺陷光子晶体-1">缺陷光子晶体</h3><p>假定增加的电缆位于左侧，其总传递矩阵更改为 <spanclass="math display">\[\begin{equation}A_T^{\prime}=A_2 A_2 A_2 A_1 A_2 A_1 A_2\end{equation}\]</span></p><h4 id="传输效率-1">传输效率</h4><p>仅仅更改矩阵，其余代码不变，传输效率关系图如下</p><p><img src="/img/同轴光子晶体实验报告/eta-f-theory-2.svg" alt="eta-f-theory-2" style="zoom:67%;" /></p><center><figcaption><b>图18</b> 缺陷光子晶体效率与频率关系曲线（程序模拟）</figcaption></center><p>与<strong>图6</strong>对比，在每个通带和带隙之间增加了若干个透射峰（确切来说有 <spanclass="math inline">\(4\)</span> 个），与实测结论完全相符。</p><h4 id="等效折射率-1">等效折射率</h4><p><img src="/img/同轴光子晶体实验报告/n-f-theory-2.svg" alt="n-f-theory-2" style="zoom:67%;" /></p><center><figcaption><b>图19</b> 缺陷光子晶体等效折射率与频率关系曲线（程序模拟）</figcaption></center><h4 id="群速度-3">群速度</h4><p><img src="/img/同轴光子晶体实验报告/vg-f-theory-2.svg" alt="vg-f-theory-2" style="zoom:67%;" /></p><center><figcaption><b>图20</b> 缺陷光子晶体群速度与频率关系曲线（程序模拟）</figcaption></center><p>有缺陷的情形均与实际情况吻合，同时符合能带理论。</p><h2 id="原始数据">原始数据</h2><p><img src="/img/同轴光子晶体实验报告/image-20221212001029570.png" alt="image-20221212001029570" style="zoom: 25%;" /></p><center><figcaption><b>图21</b> 原始数据截图1</figcaption></center><p><img src="/img/同轴光子晶体实验报告/image-20221212003217593.png" alt="image-20221212003217593" style="zoom:25%;" /></p><center><figcaption><b>图22</b> 原始数据截图2</figcaption></center></div></div>]]>
    </content>
    <id>https://learningrate1.com/2022/12/15/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/%E5%90%8C%E8%BD%B4%E5%85%89%E5%AD%90%E6%99%B6%E4%BD%93%E5%AE%9E%E9%AA%8C%E6%8A%A5%E5%91%8A/</id>
    <link href="https://learningrate1.com/2022/12/15/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/%E5%90%8C%E8%BD%B4%E5%85%89%E5%AD%90%E6%99%B6%E4%BD%93%E5%AE%9E%E9%AA%8C%E6%8A%A5%E5%91%8A/"/>
    <published>2022-12-15T03:13:42.000Z</published>
    <summary>本科阶段最后一篇物理实验报告，也是唯一一次拿满分的实验报告，基物实验一路做下来，感觉除了熟练运用 Matlab 、各种排版技巧之外没学个啥。。</summary>
    <title>Last report for Lab of Physics</title>
    <updated>2022-12-15T03:13:42.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="数理基础" scheme="https://learningrate1.com/categories/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/"/>
    <category term="基础物理学" scheme="https://learningrate1.com/categories/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/%E5%9F%BA%E7%A1%80%E7%89%A9%E7%90%86%E5%AD%A6/"/>
    <category term="物理" scheme="https://learningrate1.com/tags/%E7%89%A9%E7%90%86/"/>
    <content>
      <![CDATA[<p>访问 <span class="math inline">\(\LaTeX\)</span> 源代码<ahref="https://github.com/lr-tsinghua11/Fundamental_Physics_2">仓库</a>，如果觉得有帮助，麻烦点个Star ，以下为<strong>讲义 PDF 文件</strong>：</p><p><ahref="/pdf/基础物理学2半期小班辅导讲义——学生版.pdf">基础物理学2半期小班辅导讲义——学生版</a></p><p><ahref="/pdf/基础物理学2半期小班辅导讲义——教师版.pdf">基础物理学2半期小班辅导讲义——教师版</a></p><p>授课过程中对学生版笔记进行相关批注，点击此处可以下载<strong>批注版</strong>：</p><p><ahref="/pdf/基础物理学2半期小班辅导讲义——批注版.pdf">基础物理学2半期小班辅导讲义——批注版</a></p>]]>
    </content>
    <id>https://learningrate1.com/2022/10/29/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/%E5%9F%BA%E7%A1%80%E7%89%A9%E7%90%86%E5%AD%A62%E5%B0%8F%E7%8F%AD%E8%BE%85%E5%AF%BC%E8%AE%B2%E4%B9%89/</id>
    <link href="https://learningrate1.com/2022/10/29/%E6%95%B0%E7%90%86%E5%9F%BA%E7%A1%80/%E5%9F%BA%E7%A1%80%E7%89%A9%E7%90%86%E5%AD%A62%E5%B0%8F%E7%8F%AD%E8%BE%85%E5%AF%BC%E8%AE%B2%E4%B9%89/"/>
    <published>2022-10-29T07:00:00.000Z</published>
    <summary>基础物理学 2 小班辅导讲义</summary>
    <title>Fundamental Physics 2</title>
    <updated>2022-10-29T07:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="编程" scheme="https://learningrate1.com/categories/%E7%BC%96%E7%A8%8B/"/>
    <category term="Rust" scheme="https://learningrate1.com/categories/%E7%BC%96%E7%A8%8B/Rust/"/>
    <category term="编程" scheme="https://learningrate1.com/tags/%E7%BC%96%E7%A8%8B/"/>
    <content>
      <![CDATA[<h1 id="lecture-1">Lecture 1</h1><h2 id="语言特性">语言特性</h2><p><strong>高效：</strong>Python 解释器，java 虚拟机，而 rust<strong>没有运行时</strong>，在 bare metal（裸机）上运行。</p><p>安卓基于 java，苹果 swift，有相应的垃圾回收机制，易卡顿，而 rust没有垃圾收集机制。</p><p><strong>可靠：</strong>用<strong>类型系统和所有权模型</strong>来确保内存安全性和线程安全性，在编译时消除各种潜在的问题。</p><p><strong>好用：</strong>文档丰富，编译器（提供更改方法）。</p><h2 id="rust-语言应用">Rust 语言应用</h2><ul><li><p>Servo 浏览器引擎，Redox 操作系统，Linux操作系统驱动和模块的支持</p></li><li><p>清华大学：操作系统教学 rCore，性能所 MadFS 文件系统，IO 500遥遥领先</p></li><li><p>Cargo 能够大规模添加依赖（第三方库），不需要像 C++花时间去寻找并下载源码</p></li></ul><h2 id="rust-基础语法">rust 基础语法</h2><p>C++ cin cout 读取失败时，将流转换为非法，而 rust则会显式地处理异常</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust">io::<span class="hljs-title function_ invoke__">stdin</span>()<br>  .<span class="hljs-title function_ invoke__">read_line</span>(&amp;<span class="hljs-keyword">mut</span> guess)<br>  .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Failed to read line&quot;</span>);<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;You guessed: &#123;guess&#125;&quot;</span>);<br></code></pre></td></tr></table></figure><p>猜数获取数字以及猜测语句</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// (1..=100) 代表 1-100 左闭右闭</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">secret_number</span> = rand::<span class="hljs-title function_ invoke__">thread_rng</span>().<span class="hljs-title function_ invoke__">gen_range</span>(<span class="hljs-number">1</span>..=<span class="hljs-number">100</span>);<br><span class="hljs-comment">// trim()前后处理空格，parse() 解析（转换类型）</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">guess</span>: <span class="hljs-type">u32</span> = guess.<span class="hljs-title function_ invoke__">trim</span>().<span class="hljs-title function_ invoke__">parse</span>().<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Please type a number!&quot;</span>);<br></code></pre></td></tr></table></figure><p>变量绑定</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">17</span>; <span class="hljs-comment">// 变量绑定，且隐式推断类型</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span>: <span class="hljs-type">i16</span> = <span class="hljs-number">17</span>; <span class="hljs-comment">// 显式绑定类型</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;<br>x += <span class="hljs-number">1</span>; <span class="hljs-comment">// error: re-assignment of immutable variable x</span><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">y</span> = <span class="hljs-number">5</span>;<br>y += <span class="hljs-number">1</span>; <span class="hljs-comment">// OK!</span><br></code></pre></td></tr></table></figure><p>变量类型</p><ul><li>布尔 bool：两个值 true/false。</li><li>字符 char：用单引号，例如 'R'、' 计', 是 Unicode 的。</li><li>数值：分为整数和浮点数，有不同的大小和符号属性。<ul><li>i8、i16、i32、i64、isize</li><li>u8、u16、u32、u64、usize</li><li>f32、f64</li></ul></li><li>其中 isize 和 usize是指针大小的整数，因此它们的大小与机器架构相关。</li><li>字面值 (literals) 写为 10i8、10u16、10.0f32、10usize 等。</li><li>字面值如果不指定类型，则默认整数为 i32，浮点数为 f64。</li><li>数组 (arrays)、切片 (slices)、str 字符串 (strings)、元组(tuples)。</li></ul><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// 数组</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">arr1</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]; <span class="hljs-comment">// (array of 3 elements)</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">arr2</span> = [<span class="hljs-number">2</span>; <span class="hljs-number">32</span>]; <span class="hljs-comment">// (array of 32 `2`s)</span><br><span class="hljs-comment">// 切片</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">arr</span> = [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">total_slice</span> = &amp;arr; <span class="hljs-comment">// Slice all of `arr`</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">total_slice</span> = &amp;arr[..]; <span class="hljs-comment">// Same, but more explicit</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">partial_slice</span> = &amp;arr[<span class="hljs-number">2</span>..<span class="hljs-number">5</span>]; <span class="hljs-comment">// [2, 3, 4]</span><br><span class="hljs-comment">// 字符 String 和 &amp;str，可以分别当做 C++ 中的 string 和 const char*</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">s</span>: &amp;<span class="hljs-type">str</span> = <span class="hljs-string">&quot;galaxy&quot;</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">s2</span>: <span class="hljs-type">String</span> = <span class="hljs-string">&quot;galaxy&quot;</span>.<span class="hljs-title function_ invoke__">to_string</span>();<br><span class="hljs-keyword">let</span> <span class="hljs-variable">s3</span>: <span class="hljs-type">String</span> = <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;galaxy&quot;</span>);<br><span class="hljs-keyword">let</span> <span class="hljs-variable">s4</span>: &amp;<span class="hljs-type">str</span> = &amp;s3;<br><span class="hljs-comment">// 向量</span><br><span class="hljs-comment">// Explicit typing</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">v0</span>: <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt; = <span class="hljs-type">Vec</span>::<span class="hljs-title function_ invoke__">new</span>();<br><span class="hljs-comment">// v1 and v2 are equal</span><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">v1</span> = <span class="hljs-type">Vec</span>::<span class="hljs-title function_ invoke__">new</span>();<br>v1.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-number">1</span>);<br>v1.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-number">2</span>);<br>v1.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-number">3</span>);<br><span class="hljs-keyword">let</span> <span class="hljs-variable">v2</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-comment">// v3 and v4 are equal</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">v3</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>; <span class="hljs-number">4</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">v4</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>];<br><span class="hljs-comment">// 输出向量中的所有元素</span><br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Task 10: The array is &#123;:?&#125;&quot;</span>, v2);<br></code></pre></td></tr></table></figure><p>类型转换</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// 使用 as 进行类型转换 (cast)</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span>: <span class="hljs-type">i32</span> = <span class="hljs-number">100</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">y</span>: <span class="hljs-type">u32</span> = x <span class="hljs-keyword">as</span> <span class="hljs-type">u32</span>;<br><span class="hljs-comment">// 一般来说只能在可以安全转换的类型之间进行转换操作</span><br></code></pre></td></tr></table></figure><p>引用</p><ul><li>在类型前面写 &amp; 表示引用类型： &amp;i32。</li><li>用 &amp; 来取引用（和 C++ 类似）。</li><li>用 * 来解引用（和 C++ 类似）。</li><li>rust 中引用和一般意义的指针是不一样的。</li></ul><p>条件语句</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">if</span> x &gt; <span class="hljs-number">0</span> &#123;<br><span class="hljs-number">10</span><br>&#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> x == <span class="hljs-number">0</span> &#123;<br><span class="hljs-number">0</span><br>&#125; <span class="hljs-keyword">else</span> &#123;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Not greater than zero!&quot;</span>);<br>-<span class="hljs-number">10</span><br>&#125;<br></code></pre></td></tr></table></figure><p>循环语句，三种循环 <spanclass="math inline">\(\begin{cases}\text{while}\\\text{loop = while true} \\\text{for}\end{cases}\)</span></p><p>迭代器</p><ul><li>n..m 创建一个从 n 到 m 半闭半开区间的迭代器。</li><li>n..=m 创建一个从 n 到 m 闭区间的迭代器。</li><li>很多数据结构可以当做迭代器来使用，比如数组、切片，还有向量 Vec等等。</li></ul><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">xs</span> = [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];<br><span class="hljs-comment">// Loop through elements in a slice of `xs`.</span><br><span class="hljs-keyword">for</span> <span class="hljs-variable">x</span> <span class="hljs-keyword">in</span> &amp;xs &#123;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, x);<br>&#125;<br></code></pre></td></tr></table></figure><p>匹配语句，其中 <code>_</code> 匹配所有情况</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// 单变量版本</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">3</span>;<br><span class="hljs-keyword">match</span> x &#123;<br>  <span class="hljs-number">1</span> =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;one fish&quot;</span>), <span class="hljs-comment">// &lt;- comma required</span><br>  <span class="hljs-number">2</span> =&gt; &#123;<br>    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;two fish&quot;</span>);<br>    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;two fish&quot;</span>);<br>  &#125;, <span class="hljs-comment">// &lt;- comma optional when using braces</span><br>  _ =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;no fish for you&quot;</span>), <span class="hljs-comment">// &quot;otherwise&quot; case</span><br>&#125;<br><span class="hljs-comment">// 元组版本</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">3</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = -<span class="hljs-number">3</span>;<br><span class="hljs-keyword">match</span> (x, y) &#123;<br>  (<span class="hljs-number">1</span>, <span class="hljs-number">1</span>) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;one&quot;</span>),<br>  (<span class="hljs-number">2</span>, j) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;two, &#123;&#125;&quot;</span>, j),<br>  (_, <span class="hljs-number">3</span>) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;three&quot;</span>),<br>  (i, j) <span class="hljs-keyword">if</span> i &gt; <span class="hljs-number">5</span> &amp;&amp; j &lt; <span class="hljs-number">0</span> =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;On guard!&quot;</span>),<br>  (_, _) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;:&lt;&quot;</span>),<br>&#125;<br></code></pre></td></tr></table></figure><p>模式绑定</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> (a, b) = (<span class="hljs-string">&quot;foo&quot;</span>, <span class="hljs-number">12</span>);<br></code></pre></td></tr></table></figure><p>函数</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// 函数头</span><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">foo</span>(x: T, y: U, z: V) <span class="hljs-punctuation">-&gt;</span> T &#123;<br><span class="hljs-comment">// ...</span><br>&#125;<br><span class="hljs-comment">// T 类型参数 x ，U 类型参数 y ，V 类型参数 z，返回 T 类型</span><br><span class="hljs-comment">// Rust 必须显式定义函数的参数和返回值的类型。</span><br><span class="hljs-comment">// 实际上编译器是可以推断函数的参数和返回值的类型的，但是 Rust 的设计者认为显式指定更好</span><br><br><span class="hljs-comment">// 函数返回</span><br><span class="hljs-comment">// 函数最后一个表达式是其返回值，可以使用 return 提前返回</span><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">square</span>(n: <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> &#123;<br>n * n<br>&#125;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">squareish</span>(n: <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> &#123;<br><span class="hljs-keyword">if</span> n &lt; <span class="hljs-number">5</span> &#123; <span class="hljs-keyword">return</span> n; &#125;<br>n * n<br>&#125;<br></code></pre></td></tr></table></figure><p>print! 和 println!</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-string">&quot;foo&quot;</span>;<br><span class="hljs-built_in">print!</span>(<span class="hljs-string">&quot;&#123;&#125;, &#123;&#125;, &#123;&#125;&quot;</span>, x, <span class="hljs-number">3</span>, <span class="hljs-literal">true</span>);<br><span class="hljs-comment">// =&gt; foo, 3, true</span><br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;, &#123;:?&#125;&quot;</span>, x, [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);<br><span class="hljs-comment">// =&gt; &quot;foo&quot;, [1, 2, 3]</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = <span class="hljs-number">1</span>;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;0&#125;, &#123;y&#125;, &#123;0&#125;&quot;</span>, x);<br><span class="hljs-comment">// =&gt; foo, 1, foo</span><br><br></code></pre></td></tr></table></figure><p>format!</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">fmted</span> = <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;&#123;&#125;, &#123;:x&#125;, &#123;:?&#125;&quot;</span>, <span class="hljs-number">12</span>, <span class="hljs-number">155</span>, <span class="hljs-title function_ invoke__">Some</span>(<span class="hljs-string">&quot;Hello&quot;</span>));<br><span class="hljs-comment">// fmted == &quot;12, 9b, Some(&quot;Hello&quot;)&quot;</span><br></code></pre></td></tr></table></figure><p>panic! 处理错误的方式，并不优雅</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">if</span> x &lt; <span class="hljs-number">0</span> &#123;<br><span class="hljs-built_in">panic!</span>(<span class="hljs-string">&quot;Kaboom!&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>assert! 和 assert_eq!</p><ul><li>如果条件 condition 不成立， assert!(condition)会导致<strong>恐慌</strong>。</li><li>如果 left != right， assert_eq!(left, right)会导致<strong>恐慌</strong>。</li></ul><p>unreachable!用于表达不会达到的分支，如果达到就会导致<strong>恐慌</strong></p><p>unimplemented! 标注没有实现的功能，panic!("not yet implemented")的简写</p><h1 id="lecture-2">lecture 2</h1><p>Rust 语言最 core 的语法，语言 = 核心语法 + 标准库</p><h2 id="所有权">所有权</h2><ul><li><p>资源管理的需求：内存使用的安全和性能</p><p>内存资源：<spanclass="math inline">\(\begin{cases}全局对象：事先分配的内存空间段，启动时分配，结束时回收\\局部对象：分配在栈上，进入函数时分配，退出函数时回收\\动态对象：分配在堆上，需要时分配，不需要时回收\end{cases}\)</span></p></li><li><p>对于小型程序，<code>new</code> 之后不 <code>delete</code>无所谓，程序结束之后会自动删除；但是对于大型 24h网络服务端程序，容易出现问题，总有分配内存失败的时候</p></li><li><p>内存管理方式，<strong>用户指定和垃圾回收</strong>，前者要求编写者的严谨，后者分为小回收和大回收，</p><p>安卓手机卡的原因：处于大回收状态，逻辑不明确，性能差</p></li><li><p>C艹 将构造和分配集成在一起：</p><ul><li><p>拷贝构造：在语义上实现一个对象变两个对象（二进制串的拷贝）。</p></li><li><p>移动构造：在语义上实现将一个对象的资源转移给另一个对象。</p></li></ul></li><li><p>空指针、悬垂指针（指针所指对象被释放，但指针没有做修改）、双重释放（两个对象的指针指向同一块内存空间，两个对象均释放）等问题导致运行时错误。</p></li></ul><p>计算机技术本质上是实现一个 <code>Trade-off</code></p><ul><li><p>Rust 中的每个值都有所有者 (owner)。</p></li><li><p>同一时刻<strong>只有一个</strong>所有者。</p></li><li><p>当所有者失效，值也将被丢弃。</p></li></ul><p>这是</p><p>一份数据只有一个所有者，如果超出作用域，其绑定数据自动释放</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">foo</span>() &#123;<br>  <span class="hljs-comment">// Creates a Vec object.</span><br>  <span class="hljs-comment">// Gives ownership of the Vec object to v1.</span><br>  <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">v1</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br>  v1.<span class="hljs-title function_ invoke__">pop</span>();<br>  v1.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-number">4</span>);<br>  <span class="hljs-comment">// At the end of the scope, v1 goes out of scope.</span><br>  <span class="hljs-comment">// v1 still owns the Vec object, so it can be cleaned up.</span><br>&#125;<br></code></pre></td></tr></table></figure><p>以下代码在编译过程中出错，所有权的转移</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs rust">  <span class="hljs-keyword">let</span> <span class="hljs-variable">v1</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br>  <span class="hljs-comment">// Ownership of the Vec object moves to v2.</span><br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">v2</span> = v1;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, v1[<span class="hljs-number">2</span>]); <span class="hljs-comment">// error: use of moved value `v1`</span><br>----------------------Compile Error-----------------------<br>error[E0382]: borrow of moved value: `v1`<br>  |<br><span class="hljs-number">2</span> |     <span class="hljs-keyword">let</span> <span class="hljs-variable">v1</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br>  |         -- <span class="hljs-keyword">move</span> occurs because `v1` has <span class="hljs-keyword">type</span> `<span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt;`, which does not implement the `<span class="hljs-built_in">Copy</span>` <span class="hljs-keyword">trait</span><br><span class="hljs-number">3</span> |     <span class="hljs-comment">// Ownership of the Vec object moves to v2.</span><br><span class="hljs-number">4</span> |     <span class="hljs-keyword">let</span> <span class="hljs-variable">v2</span> = v1;<br>  |              -- value moved here<br><span class="hljs-number">5</span> |     <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, v1[<span class="hljs-number">2</span>]); <span class="hljs-comment">// error: use of moved value `v1`</span><br>  |                    ^^ value borrowed here after <span class="hljs-keyword">move</span><br></code></pre></td></tr></table></figure><p>在函数调用的时候，如果传入参数过多，还要将所有权还回去，比较麻烦</p><p>使用<strong>借用</strong>，所有权本身没有变化，相当于是借用一下数据</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">v</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-comment">// v_ref is a reference to v.</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">v_ref</span> = &amp;v;<br><span class="hljs-comment">// Moving ownership to v_new would invalidate v_ref.</span><br><span class="hljs-comment">// error: cannot move out of `v` because it is borrowed</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">v_new</span> = v;<br><span class="hljs-comment">// Cancel the effect of NLL (non-lexical lifetime)</span><br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;&quot;</span>, v_ref);<br></code></pre></td></tr></table></figure><p>rust语言是一门<strong>面向编译器语言</strong>，可以认为写不出运行有问题的代码</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// 借用与函数</span><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">length</span>(vec_ref: &amp;<span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt;) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">usize</span> &#123;<br>  <span class="hljs-comment">// vec_ref is auto-dereferenced when you call methods on it.</span><br>  vec_ref.<span class="hljs-title function_ invoke__">len</span>()<br>&#125;<br><span class="hljs-comment">// 可变借用</span><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">push</span>(vec_ref: &amp;<span class="hljs-keyword">mut</span> <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt;, x: <span class="hljs-type">i32</span>) &#123;<br>vec_ref.<span class="hljs-title function_ invoke__">push</span>(x);<br>&#125;<br><span class="hljs-comment">// Copy (特型)</span><br><span class="hljs-comment">// i32、f64、char、bool 可以拷贝</span><br><span class="hljs-comment">// 生命周期检查</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">y</span>: &amp;<span class="hljs-type">i32</span>;<br>&#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;<br>  y = &amp;x; <span class="hljs-comment">// error: `x` does not live long enough</span><br>&#125;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, *y);<br></code></pre></td></tr></table></figure><p>向量的三种迭代方式，不可变借用 <code>&amp;V</code>、可变借用<code>&amp;mut V</code>、所有权 <code>V</code></p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">vs</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>];<br><span class="hljs-comment">// Borrow immutably</span><br><span class="hljs-keyword">for</span> <span class="hljs-variable">v</span> <span class="hljs-keyword">in</span> &amp;vs &#123; <span class="hljs-comment">// Can also write `for v in vs.iter()`</span><br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;I&#x27;m borrowing &#123;&#125;.&quot;</span>, v);<br>&#125;<br><span class="hljs-comment">// Borrow mutably</span><br><span class="hljs-keyword">for</span> <span class="hljs-variable">v</span> <span class="hljs-keyword">in</span> &amp;<span class="hljs-keyword">mut</span> vs &#123; <span class="hljs-comment">// Can also write `for v in vs.iter_mut()`</span><br>  *v = *v + <span class="hljs-number">1</span>;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;I&#x27;m mutably borrowing &#123;&#125;.&quot;</span>, v);<br>&#125;<br><span class="hljs-comment">// Take ownership of the whole vector</span><br><span class="hljs-keyword">for</span> <span class="hljs-variable">v</span> <span class="hljs-keyword">in</span> vs &#123; <span class="hljs-comment">// Can also write `for v in vs.into_iter()`</span><br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;I now own &#123;&#125;! AHAHAHAHA!&quot;</span>, v);<br>&#125;<br></code></pre></td></tr></table></figure><p>切片是一种特殊的引用，代表序列中的一个指定片段</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">a</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">slice</span> = &amp;a[<span class="hljs-number">1</span>..<span class="hljs-number">3</span>];<br></code></pre></td></tr></table></figure><h2 id="结构化数据">结构化数据</h2><p>有两种 struct 和 enum，mod 相当于 C艹 中的 namespace</p><p>结构体用 CamelCase 命名方式，里面的域用 snake_case 命名方式。</p><p>语法糖，对别的方法进行一种实现，写起来简便</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Foo</span> &#123; a: <span class="hljs-type">i32</span>, b: <span class="hljs-type">i32</span>, c: <span class="hljs-type">i32</span>, d: <span class="hljs-type">i32</span>, e: <span class="hljs-type">i32</span> &#125;<br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">x</span> = Foo &#123; a: <span class="hljs-number">1</span>, b: <span class="hljs-number">1</span>, c: <span class="hljs-number">2</span>, d: <span class="hljs-number">2</span>, e: <span class="hljs-number">3</span> &#125;;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">x2</span> = Foo &#123; e: <span class="hljs-number">4</span>, .. x &#125;;<br><span class="hljs-comment">// Useful to update multiple fields of the same struct:</span><br>x = Foo &#123; a: <span class="hljs-number">2</span>, b: <span class="hljs-number">2</span>, e: <span class="hljs-number">2</span>, .. x &#125;;<br></code></pre></td></tr></table></figure><p>Rust的枚举要强很多，是<strong>和类型</strong>，用来表示多选一的数据（<strong>代数数据类型</strong>，如笛卡尔坐标系）</p><p>变体 <spanclass="math inline">\(无数据、有命名的数据域(结构体)、无命名的数据域(元组变体)\)</span>，如</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Resultish</span> &#123;<br>  <span class="hljs-literal">Ok</span>,<br>  Warning &#123; code: <span class="hljs-type">i32</span>, message: <span class="hljs-type">String</span> &#125;,<br>  <span class="hljs-title function_ invoke__">Err</span>(<span class="hljs-type">String</span>)<br>&#125;<br><span class="hljs-comment">// 使用 Resultish::each 来访问并匹配数据</span><br><span class="hljs-keyword">match</span> <span class="hljs-title function_ invoke__">make_request</span>() &#123;<br>  Resultish::<span class="hljs-literal">Ok</span> =&gt;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Success!&quot;</span>),<br>  Resultish::Warning &#123; code, message &#125; =&gt;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Warning: &#123;&#125;!&quot;</span>, message),<br>  Resultish::<span class="hljs-title function_ invoke__">Err</span>(s) =&gt;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Failed with error: &#123;&#125;&quot;</span>, s),<br>&#125;<br></code></pre></td></tr></table></figure><p>枚举类型还可以递归</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">List</span> &#123;<br>Nil,<br><span class="hljs-title function_ invoke__">Cons</span>(<span class="hljs-type">i32</span>, List),<br>&#125;<br><span class="hljs-comment">// 但上述枚举类型会报错，会趋于无穷大，使用 Box 加以限制</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">boxed_five</span> = <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-number">5</span>);<br><span class="hljs-keyword">enum</span> <span class="hljs-title class_">List</span> &#123;<br>  Nil,<br>  <span class="hljs-title function_ invoke__">Cons</span>(<span class="hljs-type">i32</span>, <span class="hljs-type">Box</span>&lt;List&gt;), <span class="hljs-comment">// OK!</span><br>&#125;<br></code></pre></td></tr></table></figure><p>方法与所有权</p><p>方法的第一个参数（名字为self）决定这个方法需要的所有权种类，分类更加细致：</p><ul><li>&amp;self：方法借用对象的值。 一般情况下尽量使用这种方式，类似于 C++中的常成员函数。</li><li>&amp;mut self：方法可变地借用对象的值。在方法需要修改对象时使用，类似于 C++ 中的普通成员函数。</li><li>self：方法获得对象的所有权。方法会消耗掉对象，同时可以返回其他的值。</li></ul><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Point</span> &#123;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">distance</span>(&amp;<span class="hljs-keyword">self</span>, other: Point) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">f32</span> &#123;<br>    <span class="hljs-keyword">let</span> (dx, dy) = (<span class="hljs-keyword">self</span>.x - other.x, <span class="hljs-keyword">self</span>.y - other.y);<br>    ((dx.<span class="hljs-title function_ invoke__">pow</span>(<span class="hljs-number">2</span>) + dy.<span class="hljs-title function_ invoke__">pow</span>(<span class="hljs-number">2</span>)) <span class="hljs-keyword">as</span> <span class="hljs-type">f32</span>).<span class="hljs-title function_ invoke__">sqrt</span>()<br>  &#125;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">translate</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, x: <span class="hljs-type">i32</span>, y: <span class="hljs-type">i32</span>) &#123;<br>    <span class="hljs-keyword">self</span>.x += x;<br>    <span class="hljs-keyword">self</span>.y += y;<br>  &#125;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">mirror_y</span>(<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> Point &#123;<br>    Point &#123; x: -<span class="hljs-keyword">self</span>.x, y: <span class="hljs-keyword">self</span>.y &#125;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><ul><li><p>一般会创建一个名为 new 的关联函数起到构造函数的作用。</p><p>Rust 没有内置的构造函数语法，也不会自动构造。</p></li><li><p>方法、关联函数不能重载、方法不能继承</p></li></ul><h2 id="模式匹配">模式匹配</h2><p>对结构体进行解构</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Point</span> &#123;<br>  x: <span class="hljs-type">i32</span>,<br>  y: <span class="hljs-type">i32</span>,<br>&#125;<br><span class="hljs-keyword">match</span> p &#123;<br>  Point &#123; x, y &#125; =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;(&#123;&#125;, &#123;&#125;)&quot;</span>, x, y)<br>&#125;<br></code></pre></td></tr></table></figure><p>使用引用的方式匹配</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">17</span>;<br><span class="hljs-comment">// 打印数值或者修改值</span><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">x</span> = <span class="hljs-number">17</span>;<br><span class="hljs-keyword">match</span> x &#123;<br>  <span class="hljs-keyword">ref</span> r <span class="hljs-keyword">if</span> x == <span class="hljs-number">5</span> =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, r),<br>  <span class="hljs-keyword">ref</span> <span class="hljs-keyword">mut</span> r =&gt; *r = <span class="hljs-number">5</span><br>&#125;<br></code></pre></td></tr></table></figure><p>内部绑定（使用 <code>@</code> ）</p><p>模式匹配的穷尽性，否则会报错（使用 <code>_</code> 表示其他情况）</p><p><code>for</code> 循环的模式匹配</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">v</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-keyword">for</span> (i, x) <span class="hljs-keyword">in</span> v.<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">enumerate</span>() &#123;<br>  <span class="hljs-built_in">print!</span>(<span class="hljs-string">&quot;v[&#123;i&#125;] = &#123;x&#125; &quot;</span>);<br>&#125;<br><span class="hljs-comment">// v[0] = 1 v[1] = 2 v[2] = 3 </span><br></code></pre></td></tr></table></figure><h1 id="lecture-3标准库">lecture 3（标准库）</h1><h2 id="编码">编码</h2><p>C艹 语言 11 比 98 增加 unordered_map</p><p>C 里面的 string 为 <code>\0</code> 操作，即使是访问字符串长度也需要<span class="math inline">\(O(n)\)</span> 的空间</p><p>而 C艹 使用 <code>std::string</code>更加方法，对负数进行补码操作，便于加法</p><ul><li>Rust 的字符串处理机制比较复杂。<ul><li>主要是用 UTF-8 编码的 Unicode 字符序列。</li><li>不是空字符 '\0' 结尾的 C 风格字符串，可以包含空字符。</li></ul></li><li>主要有两大类： &amp;str 和 String。</li></ul><p>字符的标识，与信息论有关：</p><p>模拟电路（信号是连续变化的，模拟类型更多，但不抗干扰，教室里的钟表）</p><p>数字电路（低电位和高电位，0V 和 5V，能抗干扰，数字手表）</p><p>ASCII 码 0 是 48，A 是 65，a 是 97</p><ul><li><p>编码：字符在计算机内部的表示方式</p></li><li><p>早期： ASCII 码，以英文字符为主， 7 位二进制</p></li><li><p>中文： GB 2312-1980《信息交换用汉字编码字符集》， 6,763个汉字，两个字节</p><ul><li>GB 18030-2005《信息技术中文编码字符集》， 70,244个汉字，两个字节或四个字节</li></ul></li><li><p>Unicode：试图把全世界的文字都纳入进来，收集了 144,697个字符，四个字节</p><ul><li>常用 UTF-8 的形式来表示，<strong>变长</strong>一到四个字节，rust便使用这种编码</li></ul></li><li><p>会出现乱码问题</p></li></ul><h3 align="center">Unicode 中文乱码速查表</h3><table><colgroup><col style="width: 3%" /><col style="width: 26%" /><col style="width: 32%" /><col style="width: 36%" /></colgroup><thead><tr><th>xxxxxx</th><th>示例</th><th>特点</th><th>产生原因</th></tr></thead><tbody><tr><td>古文码</td><td>鐢辨湀瑕佸ソ濂藉涔犲ぉ澶╁悜涓?</td><td>大都为不认识的古文，并加杂日韩文</td><td>以 GBK 方式读取 UTF-8 编码的中文</td></tr><tr><td>口字码</td><td>����Ҫ�¨2�ѧϰ������</td><td>大部分字符为小方块</td><td>以 UTF-8 的方式读取 GBK 编码的中文</td></tr><tr><td>符号码</td><td>ç”±æœˆè|å￥½å￥½å-|ä1 å¤©å¤©å‘ä¸Š</td><td>大部分字符为各种符号</td><td>以 ISO8859-1 方式读取 UTF-8 编码的中文</td></tr><tr><td>拼音码</td><td>óéÔÂòaoÃoÃÑ§Ï°ììììÏòéÏ</td><td>大部分字符为头顶带有各种类似声调符号的字母</td><td>以 ISO8859-1 方式读取 GBK 编码的中文</td></tr><tr><td>问句码</td><td>由月要好好学习天天向??</td><td>字符串长度为偶数时正确，长度为奇数时最后的字符变为问号</td><td>以 GBK 方式读取 UTF-8 编码的中文，然后又用 UTF-8 的格式再次读取</td></tr><tr><td>锟拷码</td><td>锟斤拷锟斤拷要锟矫猴拷学习锟斤拷锟斤拷锟斤拷</td><td>全中文字符，且大部分字符为“锟斤拷”这几个字符</td><td>以 UTF-8 方式读取 GBK 编码的中文，然后又用 GBK 的格式再次读取</td></tr><tr><td>烫烫烫</td><td>烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫</td><td>字符显示为“烫烫烫”这几个字符</td><td>VC Debug 模式下，栈内存未初始化</td></tr><tr><td>屯屯屯</td><td>屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯</td><td>字符显示为“屯屯屯”这几个字符</td><td>VC Debug 模式下，堆内存未初始化</td></tr></tbody></table><h2 id="str-和-string">&amp;str 和 String</h2><p>&amp;str</p><ul><li>&amp;str 是字符串切片，是切片的一种。</li><li>形如 "string literals" 的字符串字面值是 &amp;str 类型的1。</li><li>&amp;str 是静态分配空间的，且固定大小。</li><li>不能用方括号来做形如 some_str[i] 的索引，因为每个 Unicode字符可能有多个字节。</li><li>正确的做法是在 chars() 中迭代：<ul><li><code>for c in "1234".chars() &#123; ... &#125;</code></li></ul></li></ul><p>String</p><ul><li>String 是分配在堆上的，可以动态增长。<ul><li>和 Vec 类似，实际上就是在 Vec&lt;u8&gt; 外面包了一层。</li></ul></li><li>也不能用下标来索引。<ul><li>可以通过 s.nth(i) 来访问某个字符。</li></ul></li><li>通过取引用的方式可以获得 &amp;str。</li></ul><h2 id="option-枚举类型">Option 枚举类型</h2><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Option</span>&lt;T&gt; &#123;<br>  <span class="hljs-literal">None</span>,<br>  <span class="hljs-title function_ invoke__">Some</span>(T),<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>Option<T> 是一个枚举类型，同时也是泛型类型。</li><li>为某种已有类型提供了<strong>表示没有或者空值的概念</strong>。</li><li>在 Rust 中，在需要返回空值时，推荐使用 Option<T>。<ul><li>而不是返回诸如 NaN、 -1、 null 等特殊的值。</li></ul></li><li>类型 T 可以是任何类型，没有限制。</li></ul><p>一个处理除数为 0 的情况：</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">divide</span>(numerator: <span class="hljs-type">f64</span>, denominator: <span class="hljs-type">f64</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Option</span>&lt;<span class="hljs-type">f64</span>&gt; &#123;<br><span class="hljs-keyword">if</span> denominator == <span class="hljs-number">0.0</span> &#123;<br>    <span class="hljs-literal">None</span><br>  &#125; <span class="hljs-keyword">else</span> &#123;<br>    <span class="hljs-title function_ invoke__">Some</span>(numerator / denominator)<br>  &#125;<br>&#125;<br><span class="hljs-comment">// The return value of the function is an option</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-title function_ invoke__">divide</span>(<span class="hljs-number">2.0</span>, <span class="hljs-number">3.0</span>);<br><span class="hljs-comment">// Pattern match to retrieve the value</span><br><span class="hljs-keyword">match</span> result &#123;<br>  <span class="hljs-comment">// The division was valid</span><br>  <span class="hljs-title function_ invoke__">Some</span>(x) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Result: &#123;x&#125;&quot;</span>),<br>  <span class="hljs-comment">// The division was invalid</span><br>  <span class="hljs-literal">None</span> =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Cannot divide by 0&quot;</span>),<br>&#125;<br></code></pre></td></tr></table></figure><p>典型用途：</p><p>初始值（求列表最大值）、函数定义域不是全集、表示简单的错误情况（未定义）、结构体的可选域或者可拿走的域、可选的函数参数、空指针</p><h2 id="错误处理">错误处理</h2><ul><li>对于不可恢复的错误，使用恐慌 panic!。<ul><li>数组越界、栈越界、算术运算溢出……</li></ul></li><li>对于可恢复的错误，使用 Result。<ul><li>文件操作、网络操作、字符串解析……</li></ul></li></ul><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Result</span>&lt;T, E&gt; &#123;<br>  <span class="hljs-title function_ invoke__">Ok</span>(T),<br>  <span class="hljs-title function_ invoke__">Err</span>(E)<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>Result 与 Option 类似，除了正常结果外，还可以表示错误状态。</li><li>也定义了 unwrap 和 expect 等方法。</li><li>可以通过 ok 或 err 等方法转换成 Option。<ul><li>把 Ok 或者 Err 的值作为 Some，另一种变成 None。</li></ul></li><li>也可以进行类似 Option 的操作。<ul><li>and、 or……</li></ul></li></ul><p>其处理原则，对返回值为 Result 的函数，一定要显式地处理（否则编译器报<code>warning</code> ）</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::io::Error;<br><span class="hljs-keyword">type</span> <span class="hljs-title class_">Result</span>&lt;T&gt; = <span class="hljs-type">Result</span>&lt;T, Error&gt;;<br></code></pre></td></tr></table></figure><h3 id="操作符">?操作符</h3><p>配合 Result 类型</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">read_username_from_file</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;<span class="hljs-type">String</span>, io::Error&gt; &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">username</span> = <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">new</span>();<br>  File::<span class="hljs-title function_ invoke__">open</span>(<span class="hljs-string">&quot;hello.txt&quot;</span>)?.<span class="hljs-title function_ invoke__">read_to_string</span>(&amp;<span class="hljs-keyword">mut</span> username)?;<br>  <span class="hljs-title function_ invoke__">Ok</span>(username)<br>&#125;<br></code></pre></td></tr></table></figure><p>配合 Opition 类型</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">last_char_of_first_line</span>(text: &amp;<span class="hljs-type">str</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Option</span>&lt;<span class="hljs-type">char</span>&gt; &#123;<br>  text.<span class="hljs-title function_ invoke__">lines</span>().<span class="hljs-title function_ invoke__">next</span>()?.<span class="hljs-title function_ invoke__">chars</span>().<span class="hljs-title function_ invoke__">last</span>()<br>&#125;<br></code></pre></td></tr></table></figure><p>相当于可以提前传播错误，对上述两种类型对于 <code>Err</code> 和<code>None</code> 就可以提前返回</p><p><strong>究竟是恐慌还是不恐慌？</strong>就看能否给调用代码恢复的机会。</p><p><strong>unwrap/expect的场合：</strong>作为原型代码中的错误处理占位符</p><h2 id="容器">容器</h2><p><strong>Vec&lt;T&gt;</strong>：连续空间、可增长的序列，末尾可以高效增删、会发生增长和收缩</p><p><strong>VecDeque&lt;T&gt;</strong>：双端向量，两端可以高效增删，用环状缓冲区</p><p><strong>LinkedList&lt;T&gt;</strong>：双向链表，不能随机索引</p><p><strong>HashMap&lt;K, V&gt; / BTreeMap&lt;K,V&gt;</strong>：字典（映射）类型，一般使用 <strong>HashMap&lt;K,V&gt;</strong>，需要满足 K: Hash + Eq，需要有序的时候用<strong>BTreeMap&lt;K, V&gt;</strong> ，需要满足 K: Ord</p><p>两者访问复杂度分别为 <span class="math inline">\(O(1)\)</span> 和<span class="math inline">\(O(\log n)\)</span> ，哈希表的使用举例</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::collections::HashMap;<br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">scores</span> = HashMap::<span class="hljs-title function_ invoke__">new</span>();<br><span class="hljs-comment">// 添加元素</span><br>scores.<span class="hljs-title function_ invoke__">insert</span>(<span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Blue&quot;</span>), <span class="hljs-number">10</span>);<br>scores.<span class="hljs-title function_ invoke__">insert</span>(<span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Yellow&quot;</span>), <span class="hljs-number">50</span>);<br><span class="hljs-comment">// 访问字典</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">team_name</span> = <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Blue&quot;</span>);<br><span class="hljs-keyword">let</span> <span class="hljs-variable">score</span> = scores.<span class="hljs-title function_ invoke__">get</span>(&amp;team_name);<br><span class="hljs-comment">// 遍历元素</span><br><span class="hljs-keyword">for</span> (key, value) <span class="hljs-keyword">in</span> &amp;scores &#123;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;: &#123;&#125;&quot;</span>, key, value);<br>&#125;<br><span class="hljs-comment">// 用于统计字母出现次数</span><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">count</span>: BTreeMap&lt;<span class="hljs-type">char</span>, <span class="hljs-type">usize</span>&gt; = BTreeMap::<span class="hljs-title function_ invoke__">new</span>();<br><span class="hljs-keyword">for</span> <span class="hljs-variable">ch</span> <span class="hljs-keyword">in</span> <span class="hljs-string">&quot;abcbcddef&quot;</span>.<span class="hljs-title function_ invoke__">chars</span>() &#123;<br>  <span class="hljs-comment">// &#123;&#x27;a&#x27;: 1, &#x27;b&#x27;: 2, &#x27;c&#x27;: 2, &#x27;d&#x27;: 2, &#x27;e&#x27;: 1, &#x27;f&#x27;: 1&#125;</span><br>  count.<span class="hljs-title function_ invoke__">entry</span>(ch).<span class="hljs-title function_ invoke__">and_modify</span>(|e| *e += <span class="hljs-number">1</span>).<span class="hljs-title function_ invoke__">or_insert</span>(<span class="hljs-number">1</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>collect() 的使用</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// 将数据从列表转化为 BTreeSet</span><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">data</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">0</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">set</span>: BTreeSet = data.<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">collect</span>();<br><span class="hljs-comment">// 将数据中每个数乘以原来的两倍</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">input</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>: <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt; = input.<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">map</span>(|x| x * <span class="hljs-number">2</span>).<span class="hljs-title function_ invoke__">collect</span>();<br><span class="hljs-comment">// 使用 zip() 将两个数据叠加</span><br><span class="hljs-keyword">let</span> <span class="hljs-variable">a</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">b</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];<br><span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>: <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt; = a.<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">zip</span>(b.<span class="hljs-title function_ invoke__">iter</span>()).<span class="hljs-title function_ invoke__">map</span>(|(x, y)| x + y).<span class="hljs-title function_ invoke__">collect</span>();<br></code></pre></td></tr></table></figure><p>early，向量 lazy</p><p>B树外存，二叉树内存</p><h2 id="迭代器">迭代器</h2><p>对<strong>序列</strong>的一种抽象</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">trait</span> <span class="hljs-title class_">Iterator</span> &#123;<br>  <span class="hljs-keyword">type</span> <span class="hljs-title class_">Item</span>;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">next</span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Option</span>&lt;<span class="hljs-keyword">Self</span>::Item&gt;;<br>  <span class="hljs-comment">// More fields omitted</span><br>&#125;<br></code></pre></td></tr></table></figure><p>大数据方法 map revuse</p><h2 id="自动测试">自动测试</h2><p>软件工程：<strong>回归测试</strong>（列出对所有的情况，每次开发判断能否通过）</p><p><strong>评测系统</strong>是独立于程序的系统，用于测试；<strong>单元测试</strong>嵌入程序当中，在内部进行测试</p><p>cargo 提供了相应测试 test，在函数前面加上 <code>#[test]</code>以标注这是一个测试函数</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[test]</span><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">it_works</span>() &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-number">2</span> + <span class="hljs-number">2</span>;<br>  <span class="hljs-built_in">assert_eq!</span>(result, <span class="hljs-number">4</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>习惯每写一个函数，就在文件后面实现对它的单元测试，也可以调换过来，测试驱动编程</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">vector_length</span>(data: &amp;<span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt;) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">usize</span> &#123;<br>  vector_length.<span class="hljs-title function_ invoke__">len</span>()<br>&#125;<br><span class="hljs-meta">#[test]</span><br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">test_vector_length</span>() &#123;<br>  <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-title function_ invoke__">vector_length</span>(&amp;<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">3</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>持续集成，CICD，每次 push 一次就会自动跑脚本，判断测试是否失败</p><h1 id="tutorial">Tutorial</h1><h2 id="习题评讲">习题评讲</h2><ul><li><p>使用元组实现相当于解包压包</p></li><li><p>使用 <code>a.inter().map(|x| x * 2).collect()</code> 等价于</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">ret</span> - <span class="hljs-built_in">vec!</span>[];<br><span class="hljs-keyword">for</span> <span class="hljs-variable">x</span> <span class="hljs-keyword">in</span> &amp;a&#123;<br>  rec.<span class="hljs-title function_ invoke__">push</span>(x * <span class="hljs-number">2</span>);<br>&#125;<br></code></pre></td></tr></table></figure></li><li><p>随机数的选取，如果不希望抽重，使用随机种子打乱然后顺序取</p></li><li><p><code>json</code>是传输数据格式中非常重要的格式：字符串、字典、数字，标准中没有注释，最后没有逗号。</p></li><li><p>general 的工作一定有人写，合并命令行参数第三方库<code>merge</code></p></li><li><p>f64 没有实现偏序关系 Ord ，这是因为 NaN 不满足全序关系，从而 NaN与所有数比较都是 false</p></li></ul><h2 id="oj-相关知识">OJ 相关知识</h2><p>请求和响应，前端属于客户端，不涉及跨主机访问</p><h3 id="http-请求">HTTP 请求</h3><p>例子：https://www/baidu.com/</p><p>GET:</p><p>HOST: www.baidu.com</p><p>Content-Type: html</p><h3 id="http-响应">HTTP 响应</h3><p>一个例子：</p><p>HTTP 200 OK</p><p>Content-Type: application</p><h3 id="json-序列化与反序列化">json 序列化与反序列化</h3><p>#[derive: deserialize]</p><p>result 转成 json 文件</p><h3 id="互斥锁">互斥锁</h3><p>yse std::sync::{Arc, Mutex};</p><p>保证数据只能被一个线程加以修改，但要防止死锁（情况如下）</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">lock_a</span> = A.<span class="hljs-title function_ invoke__">luck</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br><span class="hljs-keyword">let</span> <span class="hljs-variable">lock_b</span> = B.<span class="hljs-title function_ invoke__">luck</span>().<span class="hljs-title function_ invoke__">unwrap</span>();<br></code></pre></td></tr></table></figure><p>在上锁的时候，所有错误不要出现恐慌</p><h3 id="不同提交隔离">不同提交隔离</h3><p>一个小段子：C艹中的 <deque>中分配内存出错时，没有出现内存错误的异常</p><h1 id="lecture-4">Lecture 4</h1><h2 id="泛型">泛型</h2><p>C 语言中没有泛型，如 <code>quicksort() bisearch()</code>没有对数据类型进行泛化，而是交给程序员进行处理（手动传入<code>compare()</code> 函数）</p><p>Rust 中第一个泛型，将类型作为参数，变成泛型枚举类型</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Result</span>&lt;T, E&gt; &#123;<br>  <span class="hljs-title function_ invoke__">Ok</span>(T),<br>  <span class="hljs-title function_ invoke__">Err</span>(E),<br>&#125;<br></code></pre></td></tr></table></figure><p>python 不需要泛型，其有底层 <code>Object</code>类型，并且是动态语言</p><p>对上述泛型枚举类型，在实现相应方法的时候其函数返回值也是<code>&lt;T, E&gt;</code>，其也可以使用参数作为传入</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">foo</span>&lt;T, U&gt;(x: T, y: U)&#123;<br>  <span class="hljs-comment">// ...</span><br>&#125;<br></code></pre></td></tr></table></figure><h2 id="特型trait">特型(trait)</h2><p>一定程度上对应<strong>面向对象编程的多态性</strong>，对于下一段美观打印、同时比较多个参数结构体实现代码：</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Point</span> &#123; <br>  x: <span class="hljs-type">i32</span>, <br>  y: <span class="hljs-type">i32</span>, <br>&#125;<br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Point</span> &#123;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">format</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span> &#123;<br><span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;(&#123;&#125;, &#123;&#125;)&quot;</span>, <span class="hljs-keyword">self</span>.x, <span class="hljs-keyword">self</span>.y)<br>&#125;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">equals</span>(&amp;<span class="hljs-keyword">self</span>, other: Point) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">bool</span> &#123;<br><span class="hljs-keyword">self</span>.x == other.x &amp;&amp; <span class="hljs-keyword">self</span>.y == other.y<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>可以抽象出共同特点(trait)，相当于 C艹 中的虚函数</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// write trait</span><br><span class="hljs-keyword">trait</span> <span class="hljs-title class_">PrettyPrint</span> &#123;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">format</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span>;<br>&#125;<br><span class="hljs-comment">// write actual function</span><br><span class="hljs-keyword">impl</span> <span class="hljs-title class_">PrettyPoint</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Point</span> &#123;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">format</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span> &#123;<br>    <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;(&#123;&#125;, &#123;&#125;)&quot;</span>, <span class="hljs-keyword">self</span>.x, <span class="hljs-keyword">self</span>.y)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>C++ 中标准库由快速排序和插入排序混合版实现</p><p>python java 使用 Tim-Sort 归并排序</p><p>C++ 背上了很大的历史包袱，每次遇到问题都需要加入新的概念</p><p>特型约束的泛型类型示例</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Result</span>&lt;T, E&gt; &#123;<br><span class="hljs-title function_ invoke__">Ok</span>(T),<br><span class="hljs-title function_ invoke__">Err</span>(E), <br>&#125;<br><span class="hljs-keyword">trait</span> <span class="hljs-title class_">PrettyPrint</span> &#123;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">format</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span>; <br>&#125;<br><span class="hljs-keyword">impl</span>&lt;T: PrettyPrint, E: PrettyPrint&gt; PrettyPrint <span class="hljs-keyword">for</span> <span class="hljs-title class_">Result</span>&lt;T, E&gt; &#123;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">format</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span> &#123;<br>    <span class="hljs-keyword">match</span> *<span class="hljs-keyword">self</span> &#123;<br>      <span class="hljs-title function_ invoke__">Ok</span>(t) =&gt; <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;Ok(&#123;&#125;)&quot;</span>, t.format()),<br>      <span class="hljs-title function_ invoke__">Err</span>(e) =&gt; <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;Err(&#123;&#125;)&quot;</span>, e.format()), <br>    &#125;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>特型可以拿到其“子特型”的方法</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">trait</span> <span class="hljs-title class_">Parent</span> &#123;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">foo</span>(&amp;<span class="hljs-keyword">self</span>) &#123;<br>    <span class="hljs-comment">// ...</span><br>  &#125;<br>&#125;<br><span class="hljs-keyword">trait</span> <span class="hljs-title class_">Child</span>: Parent &#123;<br>  <span class="hljs-keyword">fn</span> <span class="hljs-title function_">bar</span>(&amp;<span class="hljs-keyword">self</span>) &#123;<br>    <span class="hljs-keyword">self</span>.<span class="hljs-title function_ invoke__">foo</span>();<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><code>#[derive(Debug)]</code>能够让对应的数据结构获得相应实现，不用重新编写，共有以下自动<strong>核心特性</strong></p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-built_in">Clone</span>, <span class="hljs-built_in">Copy</span>, <span class="hljs-built_in">Debug</span>, <span class="hljs-built_in">Default</span>, <span class="hljs-built_in">Eq</span><br>Hash, <span class="hljs-built_in">Ord</span>, PatialEq, <span class="hljs-built_in">PartialOrd</span><br></code></pre></td></tr></table></figure><p>特型的自动获得需要满足其所有成员都能自动获得指定的特型，如 Eq不能在包含 f32 的结构体类型上自动获得，因为 f32 不是 Eq 的（浮点数中的NAN与任意数比较都是错误的，<strong>不满足全序关系中的自反性</strong>）</p><p><strong>Debug</strong> 特型用于输出调试信息，如</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(Debug)]</span><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Point</span> &#123; x: <span class="hljs-type">i32</span>, y: <span class="hljs-type">i32</span>, &#125;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">origin</span> = Point &#123; x: <span class="hljs-number">0</span>, y: <span class="hljs-number">0</span> &#125;;<br><span class="hljs-comment">// println!(&quot;The origin is: &#123;:?&#125;&quot;, origin);</span><br></code></pre></td></tr></table></figure><p><strong>Default</strong> 特型用于定义一个默认值，如 0 或者 ""</p><p><strong>Eq 和 PartialEq</strong>等价关系和部分等价关系，都有对称性和传递性，前者还有自反性</p><p><strong>Hash</strong> 表示可哈希的类型，H类型是抽象的哈希状态，可以计算哈希值，而如果同时出现了 Eq特型，需要满足以下重要性质</p><p>x == y -&gt; hash(x) == hash(y)</p><p><strong>PartialOrd 和 Ord</strong>表示偏序和全序，都有反对称性和传递性，前者还要满足完全性（对所有的 a 和b，有 a &lt;= b 或者 b &lt;= a 成立），后者可以按照字典序排序</p><p><strong>关联类型的需求</strong>：例如，图的表示：邻接矩阵/链表</p><p><strong>Sized 和 ?Sized</strong>前者表示在编译时固定大小，后者大小是动态的（如 [T],str），一般跟指针相关的泛型才会出现后者（如 Box<T>）</p><p>特型甚至可以为所有类型写，如 i32，但不推荐。为了写一个特型实现的 impl代码段，要么拥有该特性，要么拥有该类型。</p><p><strong>Drop</strong> 表示可以销毁的特型，但一般情况下不需要手动实现Drop</p><h2 id="特型对象">特型对象</h2><p>考虑以下特型和实现</p><p><strong>静态</strong>分发：在编译的时候给定了相应特性的函数</p><p><strong>动态</strong>分发：在运行的时候决定相应特性的函数，但只有运行之后才能使用，其他情况只能当成一个特型来使用，编译器不知道对应的类型信息（已经被抹去）</p><p><strong>对象安全性</strong>，需要满足一定条件，关联函数要求除接收方之外，其他地方都不能出现Self 类型（否则获取到对应的类型），不能以 Sized为超特型，接收方是引用或者指针形式的类型（Self, Box&lt;Self&gt;）</p><blockquote><p>课件上问题：不可变的引用是可以 Clone 的。</p></blockquote><h2 id="生命周期">生命周期</h2><p>考虑以下情况：</p><ol type="1"><li>获取了一项资源。</li><li>乙方通过引用借用了甲方的这项资源。</li><li>甲方对这项资源使用完毕，对它进行释放。</li><li>乙方还保留着对这项资源的引用，并开始使用它。</li><li>乙方挂了……</li></ol><p>如何保证第 3 步和第 4步的顺序关系？一般情况下，引用具有隐式的生命周期，不需要额外关注，但也可以<strong>显式</strong>地指定生命周期</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">bar</span>&lt;<span class="hljs-symbol">&#x27;a</span>&gt;(x: &amp;<span class="hljs-symbol">&#x27;a</span> <span class="hljs-type">i32</span>) &#123;<br><span class="hljs-comment">// ...</span><br>&#125;<br></code></pre></td></tr></table></figure><p><code>fn borrow_x_or_y&lt;'a&gt;(x: &amp;'a str, y: &amp;'a str) -&gt; &amp;'a str;</code>保证引用 x 和 y的生命周期至少会和返回的引用生命周期一样长，若只需要前者和返回值的生命周期一样长，则可以分开为'a 与 'b<code>fn borrow_p&lt;'a, 'b&gt;(p: &amp;'a str, q: &amp;'b str) -&gt; &amp;'a str;</code>，如以下编译期间会报错</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Pizza</span>(<span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt;);<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">PizzaSlice</span>&lt;<span class="hljs-symbol">&#x27;a</span>&gt; &#123;<br>  pizza: &amp;<span class="hljs-symbol">&#x27;a</span> Pizza, <span class="hljs-comment">// &lt;- references in structs must</span><br>  index: <span class="hljs-type">u32</span>, <span class="hljs-comment">// ALWAYS have explicit lifetimes</span><br>&#125;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">s2</span>; &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">p2</span> = <span class="hljs-title function_ invoke__">Pizza</span>(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]);<br>  s2 = PizzaSlice &#123; pizza: &amp;p2, index: <span class="hljs-number">2</span> &#125;;<br>  <span class="hljs-comment">// no good - why?</span><br>&#125;<br><span class="hljs-title function_ invoke__">drop</span>(s2); <span class="hljs-comment">// to undo NLL</span><br></code></pre></td></tr></table></figure><p>如果结构体或者枚举类型的成员是引用，那么就需要显式地指定生命周期</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Foo</span>&lt;<span class="hljs-symbol">&#x27;a</span>, <span class="hljs-symbol">&#x27;b</span>&gt; &#123;<br>  v: &amp;<span class="hljs-symbol">&#x27;a</span> <span class="hljs-type">Vec</span>&lt;<span class="hljs-type">i32</span>&gt;,<br>s: &amp;<span class="hljs-symbol">&#x27;b</span> <span class="hljs-type">str</span>, <br>&#125;<br></code></pre></td></tr></table></figure><h1 id="lecture-5">Lecture 5</h1><blockquote><p><strong>所有权</strong>是 rust语言<strong>资源管理</strong>的灵魂，<strong>特型</strong>是 rust语言<strong>灵活运用</strong>的灵魂。</p></blockquote><blockquote><p><strong>共享不修改，修改不共享——rust 设计哲学</strong></p></blockquote><h2 id="项目管理">项目管理</h2><h3 id="模块系统">模块系统</h3><ul><li>包 (packages)：Cargo 的一项功能，可以让用户构建、测试、分享箱。</li><li>箱(crates)：也叫单元包，是由<strong>模块构成的一棵树</strong>，能够产生一个库或者可执行文件。</li><li>模块 (modules)：与 use配合，<strong>控制路径</strong>的组织结构、作用域和访问权限。</li><li>路径(paths)：命名项目的方式，这里的项目可以指结构体、函数、模块等。</li></ul><p>在模块中加上 <code>pub</code> 关键字便可以让其他用户访问，模块相当于C艹 中的 namespace，模块之间可以嵌套，如下</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">mod</span> english &#123;<br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">mod</span> greetings &#123; <span class="hljs-comment">/* ... */</span> &#125;<br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">mod</span> farewells &#123; <span class="hljs-comment">/* ... */</span> &#125;<br>&#125;<br><span class="hljs-keyword">mod</span> chinese &#123;<br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">mod</span> greetings &#123; <span class="hljs-comment">/* ... */</span> &#125;<br>  <span class="hljs-keyword">pub</span> <span class="hljs-keyword">mod</span> farewells &#123; <span class="hljs-comment">/* ... */</span> &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>可以把模块写成单独的文件 <code>lib.rs</code>，用于整合所有的模块</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// lib.rs</span><br><span class="hljs-keyword">mod</span> english;<br><span class="hljs-comment">// english.rs</span><br><span class="hljs-keyword">pub</span> <span class="hljs-keyword">mod</span> greetings &#123; <span class="hljs-comment">/*...*/</span> &#125;<br></code></pre></td></tr></table></figure><p>也可以用目录来组织模块，把模块当做目录名使用</p><p>还可以在 Cargo 中使用自己编写的箱</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust">[dependencies]<br>myfoo = &#123; git = <span class="hljs-string">&quot;https://github.com/me/foo-rs&quot;</span> &#125;<br>mybar = &#123; path = <span class="hljs-string">&quot;../rust-bar&quot;</span> &#125;<br></code></pre></td></tr></table></figure><h3 id="cargo-相关">Cargo 相关</h3><p><strong>单元测试</strong>直接附着在源代码中，<code>#[test]</code>，<strong>集成测试</strong>放在<code>tests/*.rs</code> 中，<strong>基准测试程序</strong>放在<code>benches/*.rs</code>（类似作坊中的基准模块）</p><p><code>feature</code> 是在构建时做选择性的开关（与 <code>bug</code>不同）</p><p>使用 rust 语言，Cargo 编写脚本</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust">[package]<br>build = <span class="hljs-string">&quot;build.rs&quot;</span><br></code></pre></td></tr></table></figure><p>可以将自己写的软件包发布到 crate.io ，原子性的库。</p><h2 id="语法补充">语法补充</h2><h3 id="属性">属性</h3><p>#! [no_std] 禁用标准库，#[derive(Debug)] 自动获得特型</p><p>#[inline(always)] 提示编译器内联优化，#[cfg(target_os = "linux")]定义条件编译。</p><p>inter procedure o</p><h3 id="操作符-1">操作符</h3><p>运算类 &gt; 操作类 &gt; 位运算类 &gt; 逻辑类，其背后的原因是 a + b ==c 应该被理解为 (a + b) ==c，后者是源于逻辑二元运算存在<strong>短路情况</strong></p><p>使用特型来重载操作符，定义在 std::ops 下，有如下重载操作符：Neg, Not,Deref, DerefMut | Mul, Div, Mod | Add, Sub...</p><h3 id="类型转换">类型转换</h3><p>使用 <strong>From 和 Into实现自定义类型转换</strong>，前者实现之后后者会自动实现，例如实现<strong>对数转换</strong></p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span> <span class="hljs-title class_">Into</span>&lt;<span class="hljs-type">f64</span>&gt; <span class="hljs-keyword">for</span> <span class="hljs-title class_">Log2</span> &#123;<br>    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">into</span>(<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">f64</span> &#123;<br>        <span class="hljs-comment">// return log_2 of the value</span><br>        <span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>.<span class="hljs-title function_ invoke__">ln</span>() / std::<span class="hljs-type">f64</span>::consts::LN_2<br>    &#125;<br>&#125;<br><span class="hljs-comment">// 调用取得对数</span><br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">log2_4</span>: <span class="hljs-type">f64</span> = <span class="hljs-title function_ invoke__">Log2</span>(<span class="hljs-number">4.0</span>).<span class="hljs-title function_ invoke__">into</span>();<br>    <span class="hljs-keyword">let</span> <span class="hljs-variable">log2_8</span>: <span class="hljs-type">f64</span> = <span class="hljs-title function_ invoke__">Log2</span>(<span class="hljs-number">8.0</span>).<span class="hljs-title function_ invoke__">into</span>();<br></code></pre></td></tr></table></figure><h3 id="命名规范">命名规范</h3><h4 id="标识符">标识符</h4><ul><li><p>CamelCase：类型、特型</p></li><li><p>snake_case：箱、模块、函数、方法、变量</p></li><li><p>SCREAMING_SNAKE_CASE：常量和静态变量</p></li><li><p>T（单个大写字母）：类型参数</p></li><li><p>'a（撇 + 短的小写名字）：生命周期参数</p></li></ul><h4 id="构造函数和转换函数">构造函数和转换函数</h4><ul><li><p>new, new_with_stuff：构造函数</p></li><li><p>from_foo：转换构造函数</p></li><li><p>as_foo：低开销非消耗性转换</p></li><li><p>to_foo：高开销非消耗性转换</p></li><li><p>into_foo：消耗性转换</p></li></ul><h2 id="智能指针">智能指针</h2><h3 id="boxt">Box&lt;T&gt;</h3><p>用于在堆上分配空间存放数据，其拥有 T 类型的对象，其指针是唯一的，类似C艹 中的 <code>std::unique_ptr</code>，是动态分配</p><h3 id="stdrcrct">std::rc::Rc&lt;T&gt;</h3><p>是 <strong>Referenced counted</strong> 的缩写，代表指针的别名个数</p><p>共享所有权的指针类型，相当于 C艹 中的<code>std::shared_ptr</code>，并且其一直符合 rust的借用规则，当且仅当引用计数为 1 时才能修改</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">shared</span> = Rc::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-number">6</span>);<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;&quot;</span>, Rc::<span class="hljs-title function_ invoke__">get_mut</span>(&amp;<span class="hljs-keyword">mut</span> shared)); <span class="hljs-comment">// ==&gt; Some(6)</span><br><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">cloned</span> = shared.<span class="hljs-title function_ invoke__">clone</span>(); <span class="hljs-comment">// ==&gt; Another reference</span><br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;&quot;</span>, Rc::<span class="hljs-title function_ invoke__">get_mut</span>(&amp;<span class="hljs-keyword">mut</span> shared)); <span class="hljs-comment">// ==&gt; None</span><br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;&quot;</span>, Rc::<span class="hljs-title function_ invoke__">get_mut</span>(&amp;<span class="hljs-keyword">mut</span> cloned)); <span class="hljs-comment">// ==&gt; None</span><br></code></pre></td></tr></table></figure><p>gc垃圾回收机制，如果有各种变量相互引用形成<strong>一种环</strong>，就不能释放，导致空间的浪费：</p><ul><li><p>A 有一个 B 的 Rc，B 也有一个 A 的 Rc，两者的引用计数都是1。</p></li><li><p>由于构成了环，两个对象都不会被释放，从而引起内存泄露。</p></li></ul><p>可以使用<strong>弱引用</strong>来避免（与 C艹 中的<code>weak_ptr</code> 类似） ，Rc::downgrade() 降级成 Weak。</p><blockquote><p>对于图 (V,E)，对顶点拥有<strong>所有权</strong>，但是对于边来说，不能拥有对顶点的所有权，可以使用<strong>弱引用</strong>来实现</p></blockquote><p>但这样会引入双重计数，增加开销。</p><h3 id="stdcellcellt">std::cell::Cell&lt;T&gt;</h3><p>为 Copy 类型提供内部可变性的格子类型，用 get() 从 Cell 中取值，用set() 更新 Cell 的值。</p><h3 id="stdcellrefcellt">std::cell::RefCell&lt;T&gt;</h3><p>可为任意类型提供内部可变性，当 borrow() 一个 RefCell&lt;T&gt;时，得到的是 Ref&lt;T&gt;，而不是 &amp;T。</p><h3 id="const-t-和-mut-t">const T 和 *mut T</h3><p>相当于 C 语言的裸指针。</p><h2 id="常用库">常用库</h2><ul><li>正则表达式: reges</li><li>日志: log （源于航海，各种级别分开，error, warning）</li><li>日期: chrono</li><li>HTTP 客户端: reqwest</li><li>增强错误处理: thiserror , anyhow</li><li>数据库: rusqlite, r2d2</li></ul><h2 id="数据库">数据库</h2><h3 id="分类">分类</h3><p><strong>数据库</strong>是以一定方式存储在一起、能够给多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。</p><ul><li>关系数据库：<strong>创建在关系模型基础上的数据库</strong>，给予集合代数<ul><li>Oracle 国外数据库，早些年中国各大银行使用，现在国产化</li><li>ProsgreSQL<ul><li>MySQL</li><li>SQLite</li></ul></li></ul></li></ul><p><img src="/img/imgdb_6310b37c16f2c2beb17decd6.jpg" style="zoom:50%;" /></p><ul><li>非关系型数据库<ul><li>文档数据库（json 转换为二进制文件），如 MongoDB</li><li>键值数据库（类似 HashMap），如 LevelDB</li></ul></li></ul><h3 id="操作">操作</h3><p>数据查询：<strong>选择、投影、连接、并、交、差</strong></p><p>Excel 表中 vlookup 函数用于合并数据，指定键值</p><p>数据操作：<strong>新增、删除、修改、查询</strong></p><h3 id="sql-简介">SQL 简介</h3><p>常用命令：</p><ul><li>创建表格 CREATE TABLE</li><li>查询数据 SELECT</li><li>插入数据 INSERT</li><li>更新数据 UPDATE</li><li>删除数据 DELETE</li><li>删除表格 DROP TABLE</li></ul><p>DBA IT 认证，Oracle 数据管理库职业</p><h3 id="在-rust-中使用-sql">在 rust 中使用 SQL</h3><p>软件包 rusqlite</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Result</span>&lt;()&gt; &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">conn</span> = Connection::<span class="hljs-title function_ invoke__">open_in_memory</span>()?;<br>  conn.<span class="hljs-title function_ invoke__">execute</span>(<br>    <span class="hljs-string">&quot;CREATE TABLE person (</span><br><span class="hljs-string">    id INTEGER PRIMARY KEY,</span><br><span class="hljs-string">    name TEXT NOT NULL,</span><br><span class="hljs-string">    data BLOB</span><br><span class="hljs-string">    )&quot;</span>,<br>    (), <span class="hljs-comment">// empty list of parameters.</span><br>  )?;<br>&#125;<br></code></pre></td></tr></table></figure><p>还可以与 Web 框架联合使用</p><p>创建数据库连接池，将连接池作为 Data&lt;T&gt; 传给请求处理代码</p><h1 id="lecture-6">Lecture 6</h1><blockquote><p>当今许多设备都是多核的，并发是现代语言必须具备的特性，</p></blockquote><h2 id="闭包">闭包</h2><h3 id="概念与类型推导">概念与类型推导</h3><p>闭包的概念和匿名函数、lambda 函数相似，其可以绑定在变量上：</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">square</span> = |x: <span class="hljs-type">i32</span>| <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> &#123; x * x &#125;;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, <span class="hljs-title function_ invoke__">square</span>(<span class="hljs-number">3</span>));<br><span class="hljs-comment">// =&gt; 9</span><br></code></pre></td></tr></table></figure><p>类型可以推导，参数类型和返回值类型都可以不显示地给出</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">square_v4</span> = |x: <span class="hljs-type">u32</span>| &#123; (x * x) <span class="hljs-keyword">as</span> <span class="hljs-type">i32</span> &#125;;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">square</span> = |x| &#123; x * x &#125;;<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, <span class="hljs-title function_ invoke__">square</span>(-<span class="hljs-number">2.4</span>));<br><span class="hljs-comment">// =&gt; 5.76</span><br></code></pre></td></tr></table></figure><h3 id="捕捉">捕捉</h3><p>闭包还可以包含其所在的环境（可以调用外面的参数，称为<strong>捕捉</strong>）</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">magic_num</span> = <span class="hljs-number">5</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">magic_johnson</span> = <span class="hljs-number">32</span>;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">plus_magic</span> = |x: <span class="hljs-type">i32</span>| x + magic_num;<br></code></pre></td></tr></table></figure><p>闭包绑定后如果尝试借用，如果在上述代码后面加入以下代码，借用编译器则会报错</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-variable">more_magic</span> = &amp;<span class="hljs-keyword">mut</span> magic_num; <span class="hljs-comment">// Err!</span><br></code></pre></td></tr></table></figure><h3 id="移动闭包">移动闭包</h3><p>可以使用 <code>&#123;...&#125;</code>让闭包超过作用域来恢复，调用函数和被调用函数 Caller, Callee，也可以使用<code>move</code>关键字强制闭包获得环境变量的所有权，为<strong>移动闭包</strong></p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">make_closure</span>(x: <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Box</span>&lt;<span class="hljs-keyword">dyn</span> <span class="hljs-title function_ invoke__">Fn</span>(<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span>&gt; &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">f</span> = <span class="hljs-keyword">move</span> |y| x + y;<br>  <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new</span>(f)<br>&#125;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">f</span> = <span class="hljs-title function_ invoke__">make_closure</span>(<span class="hljs-number">2</span>);<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, <span class="hljs-title function_ invoke__">f</span>(<span class="hljs-number">3</span>));<br><span class="hljs-comment">// =&gt; 5</span><br></code></pre></td></tr></table></figure><h3 id="特型闭包">特型闭包</h3><p>闭包与所有权，只能调用一次，满足 rust借用规则。与所有权相似，闭包也具有闭包特型，<code>Fn, FnMut, FnOnce</code>分别代表借用、可变借用、所有权</p><p>如何正确地返回闭包？如下，使用动态的生命周期以及移动语义解决</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">box_up_your_closure_and_move_out</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Box</span>&lt;<span class="hljs-keyword">dyn</span> <span class="hljs-title function_ invoke__">Fn</span>(<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span>&gt; &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-variable">local</span> = <span class="hljs-number">2</span>;<br>  <span class="hljs-type">Box</span>::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-keyword">move</span> |x| x * local)<br>&#125;<br></code></pre></td></tr></table></figure><p>Lambda 函数本质上是 C++/Rust在调用处创建一个未知名字的类/结构体，然后传入环境的相关值，最后调用一个未知名字的函数。</p><h2 id="并发">并发</h2><h3 id="线程进程并发并行概念">线程进程、并发并行概念</h3><p>二进制可执行文件在执行之后，成为进程，在 CPU中存放：<strong>寄存器，堆，栈，操作系统指令</strong>。</p><p><strong>Program Point</strong> 指向进程下一个指令（在X86 中称为PC）</p><p>线程是<strong>轻量级</strong>，有 CPU存放的寄存器、堆、栈、操作系统指令单元，但内存是相互共享的，但不引入通信的开销（网络、进程通信）</p><p>并发是程序同时有多个正在运行的线程，而并行是指多个处理单元，要求更高，真正意义的同时处理。</p><h3 id="并发执行">并发执行</h3><p>考虑下面代码，假设两个线程，一个执行 <code>foo()</code>，一个执行<code>bar()</code></p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">x</span> = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">foo</span>() &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">y</span> = &amp;<span class="hljs-keyword">mut</span> x; *y = <span class="hljs-number">1</span>;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, *y); <span class="hljs-comment">// foo expects 1</span><br>&#125;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">bar</span>() &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">z</span> = &amp;<span class="hljs-keyword">mut</span> x; *z = <span class="hljs-number">2</span>;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;&#125;&quot;</span>, *z); <span class="hljs-comment">// bar expects 2</span><br>&#125;<br></code></pre></td></tr></table></figure><p>这两个线程的执行顺序不是每次都能保证的，如果将两个函数当做两台 ATM机，则会发生严重的后果。</p><p>并发编程的难点：<strong>数据共享</strong>、<strong>数据竞争</strong>、<strong>同步</strong>（保证所有线程都有正确的世界观，共享缓冲区）、<strong>死锁</strong></p><p>死锁发生有四个条件：<strong>互斥、持有资源、非抢占、等待成环</strong></p><p>一个形象的例子：</p><p><em>N</em>个哲学家坐在一张圆桌周围，交替地进行吃饭和思考。每个哲学家需要一双筷子用来吃饭，但是一共只有<em>N</em> 根筷子，每两个哲学家之间有一根。</p><p>哲学家的行为用算法描述如下：</p><ul><li>拿起他左侧的那根筷子（获取一个资源的锁）。</li><li>拿起他右侧的那根筷子（获取一个资源的锁）。</li><li>吃饭（使用资源）。</li><li>将两根筷子放回原处（释放资源的锁）。</li></ul><p>对所有哲学家来说，依据算法，所有人都拿到左侧的筷子，而此时桌上没有筷子，从而所有人卡在第二步</p><h2 id="线程">线程</h2><p>Rust 标准库提供了线程<code>std::thread</code>，每个线程有自己的栈和状态，使用闭包来指定线程的行为</p><h3 id="线程句柄">线程句柄</h3><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::thread;<br><span class="hljs-keyword">let</span> <span class="hljs-variable">handle</span> = thread::<span class="hljs-title function_ invoke__">spawn</span>(|| &#123;<br>  <span class="hljs-string">&quot;Hello, world!&quot;</span><br>&#125;);<br><span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;&#123;:?&#125;&quot;</span>, handle.<span class="hljs-title function_ invoke__">join</span>());<br></code></pre></td></tr></table></figure><p><img src="/img/video_editor/blackboard.png" /></p><p><code>join()</code>会阻塞当前线程的执行，直到句柄对应的线程终止，其返回 <code>Ok</code>或者 <code>Err</code></p><p><code>thread::park()</code> 可以暂停自己的执行，之后可以通过现成的<code>unpark()</code> 来继续执行</p><h3 id="线程与所有权">线程与所有权</h3><p>线程的创建也要满足所有权的规则（包括闭包和所有权的规则），例如使用<code>move</code> 来创建移动闭包，获得所有权</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::thread;<br><span class="hljs-keyword">for</span> <span class="hljs-variable">i</span> <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-number">10</span> &#123;<br>  thread::<span class="hljs-title function_ invoke__">spawn</span>(<span class="hljs-keyword">move</span> || &#123;<br>  <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;I&#x27;m #&#123;&#125;!&quot;</span>, i);<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="共享线程状态">共享线程状态</h2><p>Rust 类型系统包含要求满足并发承诺的特型</p><ul><li><p>Send 表示可以在线程间安全转移</p></li><li><p>Sync 表示可以在线程间（通过引用）安全共享</p></li></ul><p>Send 类型可以将它的所有权在线程间转移，如果一种类型没有实现Send，那么它只能留在原来的线程里。</p><p>Sync 类型在多个线程使用时不会引发内存安全问题，基本所有类型都是 Sync的。以下为一个共享线程状态示例</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::thread;<br><span class="hljs-keyword">use</span> std::time::Duration;<br><span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() &#123;<br>  <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">data</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];<br>  <span class="hljs-keyword">for</span> <span class="hljs-variable">i</span> <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-number">3</span> &#123;<br>    thread::<span class="hljs-title function_ invoke__">spawn</span>(<span class="hljs-keyword">move</span> || &#123;<br>      data[i] += <span class="hljs-number">1</span>;<br>    &#125;);<br>  &#125;<br>  thread::<span class="hljs-title function_ invoke__">sleep</span>(Duration::<span class="hljs-title function_ invoke__">from_millis</span>(<span class="hljs-number">50</span>));<br>&#125;<br></code></pre></td></tr></table></figure><p>此时 data 有多个所有者，使用 <code>Arc&lt;T&gt;</code>，代表原子性的引用计数指针（AtomicReference-Counted），但如果只是在初始化加入 Arc，编译也不通过。</p><p>Arc 也不具有内部可变性，需要添加<strong>互斥锁</strong>（MutualExclusion），保证它包含的值只有一个线程能够访问；如果一个线程锁定了互斥锁，但是发生了恐慌，此时该互斥锁进入中毒状态，该锁不会被释放</p><p>高并发任务、超算比赛主要资源共享，Open Np，消息传递 npi</p><h2 id="通道">通道</h2><p>通道（channels）可以用来<strong>同步线程之间的状态</strong>，用于在线程之间传递消息，也可以用来提醒其他现成关于数据就绪、事件已经发行的情况</p><p><code>std::sync::mpsc</code> 实现多生产者、单消费者的通信功能</p><p><strong>同步：</strong>不同进程之间是需要等待的，<strong>异步：</strong>发送的东西放入（相当于无限大的）缓冲区，相互之间不需要等待</p><p>使用 <code>channel&lt;T&gt;()</code> 函数创建一对连接的<code>(Sender&lt;T&gt;, Receiver&lt;T&gt;)</code></p><p><code>go</code> 语言有 <code>GC</code> 机制，导致编程开销大</p><p>对哲学家筷子问题，可以使用最后一个哲学家用相反方向拿筷子或者传递令牌规定拿筷子的哲学家。</p>]]>
    </content>
    <id>https://learningrate1.com/2022/09/11/%E7%BC%96%E7%A8%8B/Rust%20%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%BE%E5%A0%82%E7%AC%94%E8%AE%B0/</id>
    <link href="https://learningrate1.com/2022/09/11/%E7%BC%96%E7%A8%8B/Rust%20%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%BE%E5%A0%82%E7%AC%94%E8%AE%B0/"/>
    <published>2022-09-11T11:33:27.000Z</published>
    <summary>2022 年夏季学期 rust 语言课堂笔记</summary>
    <title>Rust Notes</title>
    <updated>2022-09-11T11:33:27.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Learning_rate</name>
    </author>
    <category term="早期科研" scheme="https://learningrate1.com/categories/%E6%97%A9%E6%9C%9F%E7%A7%91%E7%A0%94/"/>
    <category term="求交问题" scheme="https://learningrate1.com/categories/%E6%97%A9%E6%9C%9F%E7%A7%91%E7%A0%94/%E6%B1%82%E4%BA%A4%E9%97%AE%E9%A2%98/"/>
    <category term="科研" scheme="https://learningrate1.com/tags/%E7%A7%91%E7%A0%94/"/>
    <content>
      <![CDATA[<h1 id="特殊曲线曲面">特殊曲线曲面</h1><h2 id="bernstein-多项式">$Bernstein $ 多项式</h2><p>定义 <spanclass="math inline">\(B_n(t)=\dfrac{n!}{i!(n-i)!}(1-t)^{n-i}t^{i}\ \ \ \\ i=0\cdots n\)</span></p><p>性质 ① 非负：<span class="math inline">\(B_{i,n}(t)\geq 0,\ \ 1\leqt\leq 1\)</span>；② 归一：<spanclass="math inline">\(\displaystyle\sum_{i=0}^n{B_{i,n}(t)}=(1-t+t)^{n}=1\)</span>（二项式定理）</p><p>③ 对称性：<spanclass="math inline">\(B_{i,n}(t)=B_{n-i,n}(1-t)\)</span>； ④ 递归：<spanclass="math inline">\(B_{i,n}(t)=(1-t)B_{i,n-1}(t)+tB_{i-1,n-1}(t)\)</span>；</p><p>⑤ 升阶：<spanclass="math inline">\(B_{i,n}(t)=(1-\dfrac{i}{n+1})B_{i,n+1}(t)+\dfrac{i+1}{n+1}B_{i+1,n+1}(t)\)</span>；</p><p>⑥ 线性精度：<span class="math inline">\(t=\displaystyle\sum_{i=0}^n\dfrac{i}{n}B_{i,n}(t)\)</span>，表明单项式 <spanclass="math inline">\(t\)</span> 可以表示为 <spanclass="math inline">\(n\)</span> 次 <spanclass="math inline">\(Bernstein\)</span> 多项式的加权组合</p><p>⑦ 求导：<spanclass="math inline">\(\dfrac{dB_{i,n}(t)}{dt}=n(B_{i-1,n-1}(t)-B_{i,n-1}(t))\)</span>；</p><p>对于一般多项式 <spanclass="math inline">\(f(t)=a_nt^n+a_{n-1}t^{n-1}+\cdotsa_1t+a_0\)</span> 可以看作 <spanclass="math inline">\(\{t^n,t^{n-1},\cdots,1\}\)</span> 的线性组合</p><p>若换成基向量 <span class="math inline">\(\{B_{i,n}(t)\},0\leq i\leqn\)</span> 称为 <span class="math inline">\(Bernstein\)</span>基，注意：用 <span class="math inline">\(Bernstein\)</span>基做展开不唯一</p><p><spanclass="math inline">\(f(t)+g(t)=\begin{cases}m=n,f+g=\displaystyle\sum_{i=0}^m(f_i^m+g_i^m)B_{i,m}(t)\\m\neqn,f+g=\displaystyle\sum_{i=0}^m(\largef_i^m+\normalsize\sum_{j=\max\{0,i-m+n\}}^{\min\{n,i\}}\frac{\begin{pmatrix}n\\j\end{pmatrix}\begin{pmatrix}m-n\\i-j\end{pmatrix}}{\begin{pmatrix}m\\i\end{pmatrix}}g_j^{n})B_{i,m}(t)\end{cases}\)</span></p><p>意义：以 <span class="math inline">\(Berstein\)</span>基作展开虽然更复杂，但是保证了数值稳定性</p><h2 id="bacuteezier-曲线"><spanclass="math inline">\(B\acute{e}zier\)</span> 曲线</h2><p>以 <span class="math inline">\(Berstein\)</span>基函数为参数函数，给定"控制顶点" <spanclass="math inline">\(\{\vec{b_0},\vec{b_1}\cdots\vec{b_n}\}\)</span>，则空间曲线方程 <span class="math display">\[\vec{r}(t)=\displaystyle\sum_{i=0}^{n}\vec{b_i}B_{i,n}(t),t\in[0,1]\]</span> 性质：① 几何不变性：对 <spanclass="math inline">\(\vec{b_i}\)</span> 平移旋转，<spanclass="math inline">\(\vec{r}(t)\)</span> 的形状保持不变；</p><p>​ <span class="math inline">\(\Large\ \)</span>② 对称性：<spanclass="math inline">\(\displaystyle\sum_{i=0}^n\vec{b_i}B_{i,n}(t)=\sum_{i=0}^n\vec{b_{n-i}}B_{i,n}(1-t)\)</span>（反向拟合曲线）</p><p>​ ② <spanclass="math inline">\(\dot{\vec{r}(t)}=n\displaystyle\sum_{i=0}^{n-1}(\vec{b}_{i+1}-\vec{b}_{i})B_{i,n-1}(t)\)</span>，也为<span class="math inline">\(B\acute{e}zier\)</span>曲线（向量相应改变）</p><p>​ ③ <span class="math inline">\(\vec{r}(t)\)</span> 为包围 <spanclass="math inline">\(\{\vec{b_i}\}^n\)</span>的最小凸区域（曲线凸包，<span class="math inline">\(\largeconvex\)</span>），进而对于直线和 <spanclass="math inline">\(B\acute{e}zier\)</span> 曲线求交问题有：</p><p><strong>变差缩减</strong>：直线与 <spanclass="math inline">\(B\acute{e}zier\)</span> 曲线的交点 <spanclass="math inline">\(\leq\)</span>直线与凸包边界的交点（凸包应用：求交算法，光顺检测）</p><p>​ ④不是所有的向量组都有 <spanclass="math inline">\(B\acute{e}zier\)</span> 曲线，有 <spanclass="math inline">\(\dot{r(0)}=n(\vec{b_1}-\vec{b_0}),\dot{r(1)}=n(\vec{b_n}-\vec{b_{n-1}})\)</span>如果初始绕原点转</p><p>动后不能在一圈之内得到末态方向，由于凸包的性质则不能构成 <spanclass="math inline">\(B\acute{e}zier\)</span> 曲线</p><h2 id="bacuteezier-曲面"><spanclass="math inline">\(B\acute{e}zier\)</span> 曲面</h2><p>定义 <spanclass="math inline">\(\vec{r}(u,v)=\displaystyle\sum_{i=0}^m\sum_{j=0}^n\vec{b_{ij}}B_{i,m}(u)B_{i,n}(v)\\ u,v\in[0,1]\)</span>，与 <spanclass="math inline">\(B\acute{e}zier\)</span>曲线性质相似：几何不变、端点边界、凸包</p><h2 id="b-样条曲线面"><spanclass="math inline">\(B-\)</span>样条曲线/面</h2><p>给定节点向量 <span class="math inline">\(T=(t_0,t_1,t_2\cdotst_n)\)</span> 其中 <span class="math inline">\(t_0\leq t_1\leq t_2\cdotst_n\)</span>，对区间划分，定义 <spanclass="math inline">\(DB-\)</span>样条基函数 <spanclass="math inline">\(N_{i,k}(t)\)</span></p><p><span class="math inline">\(N_{i,1}(t)=\begin{cases}1,t_i\leq t\leqt_{i+1}\\0,\mbox{otherwise}\end{cases}\)</span> ，显然在节点处不连续，当<span class="math inline">\(k&gt;1,i=0,1\cdots n\)</span>时，有递推式</p><p><spanclass="math inline">\(N_{i,k}(t)=\dfrac{t-t_i}{t_{i+k-1}-t_i}N_{i,k-1}(t)+\dfrac{t_{i+k}-t}{t_{i+k}-t_{i+1}}N_{i+1,k-1}(t)\)</span>，代入计算有</p><p><spanclass="math inline">\(N_{0,2}=(\dfrac{t-t_0}{t_1-t_0})N_{0,1}(t)+(\dfrac{t_2-t}{t_2-t_1})N_{1,1}(t)\)</span>，为锯齿状，在节点处连续，导数不连续</p><p><spanclass="math inline">\(N_{0,3}(t)=\dfrac{(t-t_0)^2}{(t_2-t_0)(t_1-t_0)}N_{0,1}(t)+((\dfrac{t-t_0}{t_2-t_0})(\dfrac{t_2-t}{t_2-t_1})+(\dfrac{t-t_1}{t_2-t_1})(\dfrac{t_3-t}{t_3-t_1}))N_{1,1}\)</span>，拱形状</p><p>在节点处导数连续，但二阶导不连续，一般地有 <spanclass="math inline">\(N_{i,k}(t)\)</span> 在节点处 <spanclass="math inline">\((k-2)\)</span> 阶导连续，<spanclass="math inline">\((k-1)\)</span> 阶导不连续</p><p>性质：① 正值：<span class="math inline">\(N_{i,k}(t)&gt;0,t\in(t_i,t_{i+k})\)</span>；</p><p><span class="math inline">\(\Large\ \)</span> ② 局部支撑（<spanclass="math inline">\(\mbox{support}\)</span>）：<spanclass="math inline">\(N_{i,k}(t)=0,t\leq t_i\ or \ t\geqt_{i+k}\)</span>；</p><p><span class="math inline">\(\Large\ \)</span> ③ 单位分割：<spanclass="math inline">\(\displaystyle\sum_{i=0}^{n}N_{i,k}(t)=1,t\in[t_0,t_n]\)</span>；</p><p><span class="math inline">\(\Large\ \)</span> ④ 连续性：<spanclass="math inline">\(N_{i,k}(t)\)</span> 在单节点处是 <spanclass="math inline">\(C^{k-2}\)</span> 阶连续的</p><p>有 <span class="math inline">\(\mbox{de\ Boor}\)</span>算法（求值与剖分）得到 <span class="math inline">\(B-\)</span>样条曲线<span class="math display">\[r(t)=\sum_{i=0}^{n+j}\boldsymbol{p}_i^jN_{i,k-j}(t),\ \boldsymbolp_i^j=(1-\alpha_i^j)\boldsymbol p_{i-1}^{j-1}+\alpha_i^j\boldsymbolp_{i}^{j-1},\alpha_i^j=\dfrac{\bar{t}-t_i}{t_{i+k-j}-t_i},\boldsymbolp_j^0=\boldsymbol p_j\]</span> <span class="math inline">\(B\)</span> 样条曲线是一种特殊的<span class="math inline">\(B\acute{e}zier\)</span> 曲线，具体来说端点为<span class="math inline">\(k\)</span> 重节点</p><p><span class="math inline">\(B-\)</span>样条曲面定义为 <spanclass="math inline">\(\vec{r}(u,v)=\displaystyle\sum_{i=0}^{m}\sum_{j=0}^{n}\vec{P_{i,j}}N_{i,k}(u)N_{j,k}(v)\)</span></p><h2 id="nurbs-曲线"><span class="math inline">\(NURBS\)</span> 曲线</h2><p><span class="math inline">\(NURBS\)</span> 曲线非均匀有理 <spanclass="math inline">\(B-\)</span>样条，定义： <spanclass="math display">\[\displaystyle\vec{r}(t)=\dfrac{\displaystyle\sum_{i=0}^n\omega_i\vec{p_i}N_{i,k}(t)}{\displaystyle\sum_{i=0}^n\omega_iN_{i,k}(t)},\mbox{where}\  \begin{cases}N_{i,1}(t)=\begin{cases}1,t_i\leqt\leqt_{i+1}\\0,\mbox{otherwise}\end{cases}\\N_{i,k}(t)=\dfrac{t-t_i}{t_{i+k-1}-t_i}N_{i,k-1}(t)+\dfrac{t_{i+k}-t}{t_{i+k}-t_{i+1}}N_{i+1,k-1}(t)\end{cases}\]</span> 圆锥曲线等二次曲线都可以写成 <spanclass="math inline">\(NURB\)</span> 曲线的形式</p><p>例：<span class="math inline">\(\dfrac{1}{4}\)</span> 椭圆：<spanclass="math inline">\(\begin{cases}x=a\cos \theta\\y=b\sin\theta\end{cases}\ ,\theta\in[0,\dfrac{\pi}{2}]\)</span>，若令 <spanclass="math inline">\(t=\tan \dfrac{\theta}{2}\)</span>，有 <spanclass="math inline">\(\begin{cases}x=a\cdot\cfrac{1-t^2}{1+t^2}\\y=b\cdot \dfrac{2t}{1+t^2}\end{cases},\t\in[0,1]\)</span> <span class="math display">\[\vec{r}(t)=(a\cdot\dfrac{1-t^2}{1+t^2},b\cdot\dfrac{2t}{1+t^2})=\dfrac{\omega_0(1-t)^2\vec{b_0}+2\omega_1t(1-t)\vec{b_1}+\omega_2t^2\vec{b_2}}{\omega_0(1-t)^2+2\omega_1t(1-t)+\omega_2t^2}\]</span> 代入对应系数相等解得 <spanclass="math inline">\(\omega_2=2\omega_0=2\omega_1,\vec{b_0}=(a,0),\vec{b_1}=(a,b),\vec{b_2}=(0,b)\)</span></p><p>定义 <span class="math inline">\(NURBS\)</span> 曲面 <spanclass="math inline">\(\displaystyle\vec{r}(t)=\dfrac{\displaystyle\sum_{i=0}^m\sum_{j=0}^n\omega_{ij}\vec{p_i}N_{i,k}(u)N_{j,k}(v)}{\displaystyle\sum_{i=0}^m\sum_{j=0}^n\omega_{ij}N_{i,k}(u)N_{j,k}(v)}\)</span>，在当今建模主流应用曲面</p><p>其中当 <span class="math inline">\(u=u_0\)</span> 时为 <spanclass="math inline">\(NURBS\)</span> 二次曲面，例如圆环面（ <spanclass="math inline">\(\mbox{Torus}\)</span> ），旋转面</p><p>当今学术界提出实现 <span class="math inline">\(C^2\)</span>连续的样条</p><h1 id="曲线和曲面的表示">曲线和曲面的表示</h1><h2 id="平面曲线奇异点性质">平面曲线奇异点性质</h2><p>对隐式曲线<spanclass="math inline">\(f(x,y)=0\)</span>，奇异点判据<spanclass="math inline">\(\dfrac{\partial f}{\partial x}\big |_{x=x_0}=0\and\ \dfrac{\partial f}{\partial y}\big|_{y=y_0}=0\)</span>，奇异点为<spanclass="math inline">\((x_0,y_0)\)</span></p><p>①二次曲线<spanclass="math inline">\(a_{11}x^2+2a_{12}xy+a_{22}y^2+2a_{13}x+2a_{23}y+a_{33}=0\)</span>存在奇异点；</p><p>②退化为两条相交直线，或者退化为单个点；</p><p>③<span class="math display">\[ \begin{vmatrix}  a_{11} &amp; a_{12}&amp; a_{13}\\a_{21} &amp; a_{22} &amp; a_{23}\\a_{31} &amp; a_{32}&amp; a_{33}\end{vmatrix}=0 \]</span></p><p>条件①②③等价</p><h2 id="空间曲面">空间曲面</h2><p>①<span class="math inline">\(f(x,y,z)=0\)</span> 的法向量为 <spanclass="math inline">\(n=(\dfrac{\partial f}{\partial x},\dfrac{\partialf}{\partial y},\dfrac{\partial f}{\partialz})\)</span>，其切平面点法式方程为 <span class="math display">\[\dfrac{\partial f}{\partial x}\big|_{x=x_0}(x-x_0)+\dfrac{\partialf}{\partial y}\big |_{y=y_0}(y-y_0)+\dfrac{\partial f}{\partial z}\big|_{z=z_0}(z-z_0)=0\]</span> 空间曲面奇异点存在的充要条件： <spanclass="math inline">\(\dfrac{\partial f}{\partial x}\big|_{x=x_0}=\dfrac{\partial f}{\partial y}\big |_{y=y_0}=\dfrac{\partialf}{\partial z}\big |_{z=z_0}=0\)</span></p><p>对一般二次曲面<spanclass="math inline">\(Ax^2+By^2+Cz^2+Dxy+Eyz+Fxz+Gx+Hy+Iz+J=0\)</span></p><p>一般情况下是：椭球面、双曲面、抛物面，有圆锥面，相交双平面等均有奇异点</p><p>例1: <span class="math inline">\(z^2=x^2+y^2\)</span>在<spanclass="math inline">\((0,0,0)\)</span>处法向量为 <spanclass="math inline">\(n=(0,0,0)\)</span>，不可导，无切平面方程</p><p>例2: <span class="math inline">\(z=xy\)</span> 换参数 <spanclass="math inline">\(x=u+v,y=u-v\)</span>，得到<spanclass="math inline">\(z=u^2-v^2\)</span>为双曲面方程</p><p>隐函数定理：<spanclass="math inline">\(f(x,y,z)=0\)</span>，只要<spanclass="math inline">\(\dfrac{\partial f}{\partial z}\ne 0\)</span></p><p>②空间曲面参数表示 <spanclass="math inline">\(x=x(t),y=y(t),z=z(t)\)</span>，切线矢量<spanclass="math inline">\(r=(\dfrac{dx}{dt},\dfrac{dy}{dt},\dfrac{dz}{dt})\)</span></p><p>对球坐标下 <span class="math inline">\(x=r\cdot cos\alpha \cdotsin\theta\ \ \ y=r\cdot sin\alpha \cdot sin\theta\ \ \ z=r\cdotcos\theta\)</span> <span class="math inline">\(ds^2=r^2sin\thetadrd\theta d\alpha\)</span></p><h2 id="空间曲线">空间曲线</h2><p>①使用两个空间曲面相交 <span class="math inline">\(f(x,y,z)=0\ and \g(x,y,z)=0\)</span>，其在某点处的切线向量与两个空间曲面的法向量均垂直</p><p><span class="math inline">\(n_1=(\dfrac{\partial f}{\partial x}\big|_{x=x_0},\dfrac{\partial f}{\partial y}\big |_{y=y_0},\dfrac{\partialf}{\partial z}\big |_{z=z_0})\ \ \ n_2=(\dfrac{\partial g}{\partialx}\big |_{x=x_0},\dfrac{\partial g}{\partial y}\big|_{y=y_0},\dfrac{\partial g}{\partial z}\big |_{z=z_0})\)</span>，从而<span class="math inline">\(l=n_1\times n_2\)</span></p><p>②弧长微元$ds=|r(t+dt)-r(t)| <spanclass="math inline">\(，空间曲线有奇异点的条件\)</span>=0<spanclass="math inline">\(，例：\)</span>y<sup>2=x</sup>3<spanclass="math inline">\(有\)</span>x$轴上下两段</p><p>参数方程 <span class="math inline">\(x=t^3,y=t^2\)</span>，在<spanclass="math inline">\(t=0\)</span> 处 <spanclass="math inline">\(\dfrac{dr}{dt}=(0,0)\)</span>，则称<spanclass="math inline">\((0,0)\)</span>为奇异点（又称Cusp点），计算其弧长积分为<span class="math display">\[s(t_1,t_2)=\int_{t_1}^{t_2}\sqrt{4t^2+9t^4}dt=\int_{t_1}^{t_2}\sqrt{1+\frac{9}{4}t^2}d(t^2)=\frac{2}{3}(1+\frac{9}{4}t^2)^{\frac{3}{2}}\big|_{t_1}^{t_2}\]</span> 在 <span class="math inline">\(t=0\)</span>处积分处不连续，需要分段</p><h1 id="曲线的微分几何">曲线的微分几何</h1><h2 id="位矢弧长参数-_vecr-vecrs">位矢（弧长参数）<spanclass="math inline">\(\ \ _{\vec{r}\ =\ \vec{r}(s)}\)</span></h2><p>对 <span class="math inline">\(\vec{r}=\vec{r}(t)\)</span> ，中 <spanclass="math inline">\(t\)</span> 为任意参数，如果令 <spanclass="math inline">\(t=s\)</span> ，则代入弧长微元 <spanclass="math inline">\(ds=|\dot{r}(t)|dt\)</span> 有<spanclass="math inline">\(|\dot{\vec{r}(s)}|=1\)</span></p><p>写成点乘形式有 <span class="math inline">\(\dot{\vec{r}(s)}\cdot\dot{\vec{r}(s)}=0\)</span>，两边求导有 <spanclass="math inline">\(\ddot{\vec{r}(s)}\cdot\dot{\vec{r}(s)}=0\)</span></p><p>即若用弧长作为参数，则位矢对弧长的一阶导和位矢对弧长的二阶导相互正交</p><p><strong>例</strong>：对于圆周运动，使用弧长参数 <spanclass="math inline">\(s=R\cdot \omega t\)</span> <spanclass="math display">\[\begin{equation}\left\{        \begin{array}{}        x=R\cdot cos(\omega t)&amp; \\        y=R\cdot sin(\omega t)&amp; \\        \end{array}\right.\end{equation}\]</span> 得 <span class="math inline">\(\vec{r}(s)=(R\cdotcos(\dfrac{s}{R}),R\cdot sin(\dfrac{s}{R}))\)</span>，有<spanclass="math inline">\(\dot{\vec{r}(s)}=(-sin(\dfrac{s}{R}),cos(\dfrac{s}{R}))\)</span></p><p><span class="math inline">\(\ddot{\vec{r}(s)}=(-\dfrac{1}{R}\cdotcos(\dfrac{s}{R}),-\dfrac{1}{R}\cdotsin(\dfrac{s}{R}))\)</span>，后两者相互正交，且 <spanclass="math inline">\(|\ddot{\vec{r}(s)}|=\dfrac{1}{R}\)</span></p><p>另一种理解方式：位矢对弧长的导数相当于是一个正则化的速度矢量，该速度矢量的大小不</p><p>变，其变化一定沿其法向，两者相互正交</p><p>曲率的计算方式：有 <spanclass="math inline">\(\rho=\dfrac{ds}{d\theta}=\dfrac{\dot{\vec{r}}(s+ds)-\dot{\vec{r}}(s)}{\ddot{\vec{r}(s)}\cdotd\theta}\)</span>，由分子两矢量与其差构成等腰三角形</p><p><span class="math inline">\(ds\to 0\)</span> 时，<spanclass="math inline">\(\dot{\vec{r}}(s+ds)-\dot{\vec{r}}(s)=|\dot{\vec{r}(s)}|\cdotd\theta=1\cdot d\theta\)</span>，从而消去有 <spanclass="math inline">\(\rho=\dfrac{1}{|\ddot{\vec{r}(s)}|}\)</span>，即<span class="math inline">\(|\ddot{\vec{r}(s)}|=\kappa\)</span></p><p>定义，单位主法向量 <spanclass="math inline">\(\vec{n}(s)=\dfrac{\ddot{\vec{r}(s)}}{|\ddot{\vec{r}(s)}|}\)</span>，单位切向量<span class="math inline">\(\vec{t}=\dot{\vec{r}(s)}\)</span></p><p>对任意一点处， <span class="math inline">\(\vec{n}\)</span> 和 <spanclass="math inline">\(\vec{t}\)</span>两者张成的平面就是密切平面，若为平面曲线，则密切平面不变</p><p>对于空间曲线，密切平面就随着点的运动变化而变化</p><p>定义副法向量 $= $，有 <spanclass="math inline">\((\vec{t},\vec{n},\vec{b})\)</span> 为以 <spanclass="math inline">\(P\)</span> 点为原点的一组坐标架，有 <spanclass="math inline">\(\vec{b}=\dot{\vec{r}(s)}\times\dfrac{\ddot{\vec{r}(s)}}{\kappa}\)</span></p><p>考虑 $(s) $ 的变化有 <span class="math inline">\(\vec{n} \cdot\vec{n}=1\)</span>，求导有 <span class="math inline">\(\dot{\vec{n}(s)}\\bot\  \ddot{\vec{n}(s)}\)</span>，从而令 <spanclass="math inline">\(\dot{\vec{n}(s)}=\mu\vec{t}+\tau\vec{b}\)</span>，则<span class="math inline">\(\vec{b}\)</span> 变化</p><p><spanclass="math inline">\(\dot{\vec{b}(s)}=\dfrac{d}{ds}(\vec{t}\times\vec{n})=\ddot{\vec{r}(s)}\times\dfrac{\ddot{\vec{r}(s)}}{|\ddot{\vec{r}(s)}|}+\vec{t}\times\dfrac{d\vec{n}}{ds}=0+\vec{t}\times(\mu\vec{t}+\tau\vec{b})=-\tau\vec{n}\)</span>（右手螺旋）</p><p>定义 <span class="math inline">\(\tau\)</span>为曲线的挠率，几何意义：<span class="math inline">\(\vec{b}\)</span>为密切平面中单位切向量和单位法向量的叉乘，即为密切</p><p>平面的法向量，<span class="math inline">\(\dot{\vec{b}(s)}\)</span>代表密切平面法向量方向的变化，即曲线局部摆脱密切平面的趋势，若挠</p><p>率处处为0，则该曲线为平面曲线</p><p>计算挠率的方法：对 <spanclass="math inline">\(\dot{\vec{b}(s)}=-\tau\cdot\vec{n}(s)\)</span>，两边点乘 <spanclass="math inline">\(\vec{n}(s)\)</span> 计算得<spanclass="math inline">\(\tau=-\vec{n}(s)\cdot\dot{\vec{b}(s)}=\)</span></p><p><span class="math inline">\(-\dfrac{\ddot{\vec{r}(s)}}{\kappa}\cdot(\dfrac{\dot{\vec{r}(s)}\times\ddot{\vec{r}(s)}}{\kappa})&#39;=\dfrac{(\dot{\vec{r}},\ddot{\vec{r}},\dddot{\vec{r}})}{\ddot{\vec{r}\cdot}\ddot{\vec{r}}}\)</span>，其中 <spanclass="math inline">\((a,b,c)\)</span>表示混合积（平行六面体有向体积）</p><p><strong>例</strong>：等距螺线 <spanclass="math inline">\(\vec{r}(t)=(acos(t),asin(t),bt)\)</span>，弧长参数：<spanclass="math inline">\(\vec{r}(s)=(acos(\dfrac{s}{c}),asin(\dfrac{s}{c}),\dfrac{bs}{c})\)</span></p><p>代入公式有曲率半径 <spanclass="math inline">\(\rho=\dfrac{1}{|\ddot{\vec{r}}(s)|}=\dfrac{c^2}{a}\)</span>，计算挠率<span class="math inline">\(\tau=\dfrac{b}{c^2}\)</span></p><h2 id="位矢一般参数-_vecr-vecrt">位矢（一般参数）<spanclass="math inline">\(\ \ _{\vec{r}\ =\ \vec{r}(t)}\)</span></h2><p>要求 <span class="math inline">\(s=s(t)\)</span>是单调的，对一般参数求导有</p><p><spanclass="math inline">\(\dot{\vec{r}(t)}=\dfrac{d\vec{r}}{ds}\dfrac{ds}{dt}\)</span>，令速率<span class="math inline">\(v=|\dfrac{ds}{dt}|\)</span>，有 <spanclass="math inline">\(\dot{\vec{r}(t)}=\dfrac{d\vec{r}}{ds}|v|\)</span>，为一阶导公式</p><p><spanclass="math inline">\(\ddot{\vec{r}(t)}=\dfrac{d}{dt}(\dot{\vec{r}(s)}\cdot|v|)=\ddot{\vec{r}(s)}|v|^2+\dot{\vec{r}(s)}\dfrac{d|v|}{dt}=\dfrac{|v|^2}{\rho}\vec{n}+\dfrac{d|v|}{dt}\vec{t}\)</span></p><p>称 <span class="math inline">\(\kappa\)</span> 变号的点为拐点，<spanclass="math inline">\(ds\)</span> 是不依赖参数的</p><h2 id="fernet-serret-公式">Fernet-Serret 公式</h2><p>将上述产生的坐标架 <spanclass="math inline">\((\vec{t},\vec{n},\vec{b})\)</span>对弧长参数的导数写成矩阵形式： <span class="math display">\[\begin{pmatrix}\boldsymbol t&#39;\\\boldsymbol n&#39;\\\boldsymbol b&#39;\end{pmatrix}=\begin{pmatrix}0&amp;\kappa&amp;0\\-\kappa&amp;0&amp;\tau\\0&amp;-\tau&amp;0\end{pmatrix}\begin{pmatrix}\boldsymbol t\\\boldsymbol n\\\boldsymbol b\end{pmatrix}\]</span> 则 <span class="math inline">\(\kappa=\kappa(s)\)</span> 和<span class="math inline">\(\tau=\tau(s)\)</span>称为曲线的内在方程。</p><p>若两者以及初始条件 <spanclass="math inline">\(\vec{r}(0),\dot{\vec{r}(0)},\ddot{\vec{r}(0)},\ddot{\vec{r}(0)}\)</span>均已知，则该空间曲线可以确定</p><h1 id="曲面的微分几何">曲面的微分几何</h1><h2 id="切平面和法向量">切平面和法向量</h2><p>对某些变换后的一组参数坐标满足其中一个参数为定值时形成的曲线簇和另一组曲线簇相互正交</p><p>如 <spanclass="math inline">\((x,y),(r,\theta),(s,t)_{(x=s+t,y=s-t)},(r,\theta,\phi)\)</span>等等</p><p>定义曲面 <span class="math inline">\(S\)</span>的方程满足其每一个点的位矢 <spanclass="math inline">\(\vec{r}=\vec{r}(u,v)\)</span>，考虑参数空间中的一条空间曲线</p><p><span class="math inline">\(u=u(t),v=v(t)\)</span> 在该参数空间条件下<span class="math inline">\(\vec{r}=\vec{r}(u(t),v(t))\)</span>也为空间曲线，其切向量全微分为</p><p><span class="math inline">\(\dot{\vec{r}(t)}=\dfrac{\partial\vec{r}}{\partial u}\dot{u}+\dfrac{\partial \vec{r}}{\partialv}\dot{v}\)</span>，其法向量为两者的叉乘 <spanclass="math inline">\(\vec{N}=\dfrac{\dfrac{\partial r}{\partialu}\times \dfrac{\partial r}{\partial v}}{|\dfrac{\partial r}{\partialu}\times \dfrac{\partial r}{\partial v}|}\)</span></p><p>当 <span class="math inline">\(\dfrac{\partial r}{\partial u}\times\dfrac{\partial r}{\partial v}=0\)</span>时对应的点为奇异点，如圆锥面的顶点，椭圆圆锥的顶点</p><h2 id="曲面的第一基本齐式度量">曲面的第一基本齐式（度量）</h2><p><spanclass="math inline">\(ds=|\dfrac{d\vec{r}}{dt}|dt=|\dfrac{\partial\vec{r}}{\partial u}\cdot \dfrac{du}{dt}+\dfrac{\partial\vec{r}}{\partial v}\cdot\dfrac{dv}{dt}|=\sqrt{Edu^2+2Fdudv+Gdv^2}\)</span></p><p>其中定义 <span class="math inline">\(E=\dfrac{\partial\vec{r}}{\partial u}\cdot \dfrac{\partial \vec{r}}{\partialu}\)</span>，<span class="math inline">\(F=\dfrac{\partial\vec{r}}{\partial u}\cdot \dfrac{\partial \vec{r}}{\partialv}\)</span>，<span class="math inline">\(G=\dfrac{\partial\vec{r}}{\partial v}\cdot \dfrac{\partial \vec{r}}{\partialv}\)</span>，三者为第一基本齐式系数</p><p>有第一基本齐式 <spanclass="math inline">\(I=Edu^2+2Fdudv+Gdv^2=\dfrac{1}{E}(Edu+Fdv)^2+\dfrac{EG-F^2}{E}dv^2\)</span></p><p><strong>例</strong>：对球面 <spanclass="math inline">\(\vec{r}=(Rsin\theta cos\phi,Rsin\thetasin\phi,Rcos\theta)\)</span> 计算有 <spanclass="math inline">\(I=Rd\theta^2+R^2sin^2\theta d\phi^2\)</span></p><p>对 <spanclass="math inline">\(\vec{r}=\vec{r}(u(t),v(t))\)</span>，计算两个切矢之间的夹角有<span class="math inline">\(cos\omega=\dfrac{\dfrac{\partial\vec{r}}{\partial u}\cdot \dfrac{\partial \vec{r}}{\partialv}}{|\dfrac{\partial \vec{r}}{\partial u}||\dfrac{\partial\vec{r}}{\partial v}|}=\dfrac{F}{\sqrt{EG}}\)</span></p><p>从而有面积微元 <span class="math inline">\(\delta A=sinw\cdot\sqrt{EG}\delta u\delta v=\sqrt{EG-F^2}\delta u \delta v\)</span></p><p>应用：①计算面积；②计算弧长</p><p><strong>例</strong>：对柱面 <spanclass="math inline">\(x^2+y^2=R^2\)</span> 使用柱坐标有 <spanclass="math inline">\((Rcos\theta,Rsin\theta,z)\)</span>，有 <spanclass="math inline">\((\theta,z)\)</span> 相当于 <spanclass="math inline">\((u,v)\)</span>，</p><p>求导有 <span class="math inline">\(\dfrac{\partial\vec{r}}{\partial\theta}=(-Rsin\theta,Rcos\theta,0),\dfrac{\partial \vec{r}}{\partialz}=(0,0,1)\)</span>，则 <spanclass="math inline">\(E=R^2,F=0,G=1\)</span>，代入有</p><p><span class="math inline">\(I=R^2d\theta^2+dz\)</span></p><p><strong>例</strong>：求双曲抛物面 <spanclass="math inline">\(\vec{r}=(u,v,uv)\)</span> 当 <spanclass="math inline">\(u,v\)</span> 取正半轴以及 <spanclass="math inline">\(u^2+v^2\leq1\)</span> 围成的区域面积</p><p><span class="math inline">\(\dfrac{\partial \vec{r}}{\partialu}=(1,0,v)\)</span>，<span class="math inline">\(\dfrac{\partial\vec{r}}{\partial v}=(0,1,u)\)</span>，则有 <spanclass="math inline">\(E=1+v^2,F=uv,G=1+u^2\)</span>，代入面积微元</p><p><spanclass="math inline">\(dA=\sqrt{(1+v^2)(1+u^2)-u^2v^2}dudv=\sqrt{1+v^2+u^2}dudv\)</span>，换成极坐标积分有</p><p><spanclass="math inline">\(S=\displaystyle\int_0^{\frac{\pi}{2}}d\theta\int_0^1\sqrt{1+r^2}rdr=\dfrac{\pi}{6}(2\sqrt{2}-1)\)</span></p><h2 id="曲面的第二基本齐式曲率">曲面的第二基本齐式（曲率）</h2><p>量化曲面的曲率考虑曲面 <span class="math inline">\(S\)</span>上通过点 <span class="math inline">\(P\)</span> 的一条曲线 <spanclass="math inline">\(C\)</span> 在 <spanclass="math inline">\(P\)</span> 处的单位法向量关系有： <spanclass="math display">\[\vec{k}=\dfrac{d\vec{t}}{ds}=\kappa\vec{n}=\vec{k}_n+\vec{k}_g\]</span> 其中，<span class="math inline">\(\vec{k}_n\)</span>为法曲率向量为曲率向量在曲面法向的投影，称为法曲率向量；<spanclass="math inline">\(\vec{k}_g\)</span>为曲率向量在曲面切平面的投影，称为测地曲率向量（当 <spanclass="math inline">\(\vec{k_n}=0\)</span>时曲线在该点朝“前”走，若该曲线处处法向量都等于测地曲率向量，则该曲线为测地线，代表连接起点和终点最短的曲线），对于 <span class="math inline">\(\vec{\kappa_n}\)</span> 的计算有<span class="math display">\[\because \boldsymbol N\cdot \boldsymbol t=0\\therefore\dfrac{d\boldsymbol t}{ds}\cdot \boldsymbolN+\dfrac{d\boldsymbol N}{ds}\cdot \boldsymbol t=0,\ \vec{\boldsymbol\kappa_n}=\dfrac{d\boldsymbol t}{ds}\cdot \boldsymbolN=-\dfrac{d\boldsymbol N}{ds}\cdot t=-\dfrac{d\boldsymbol r}{ds}\cdot\dfrac{d\boldsymbol N}{ds}=-\dfrac{d\boldsymbol r\cdot d\boldsymbolN}{d\boldsymbol r\cdot d\boldsymbol r}\]</span> 令 <span class="math inline">\(L=-\dfrac{\partial  r}{\partialu}\cdot \dfrac{\partial  N}{\partialu},M=-\dfrac{1}{2}(\dfrac{\partial  r}{\partial u}\cdot\dfrac{\partial  N}{\partial v}+\dfrac{\partial  r}{\partial v}\cdot\dfrac{\partial  N}{\partial u})=-\dfrac{\partial  r}{\partial u}\cdot\dfrac{\partial  N}{\partial v}=\dfrac{\partial  r}{\partial v}\cdot\dfrac{\partial  N}{\partial u},N=-\dfrac{\partial  r}{\partial v}\cdot\dfrac{\partial  N}{\partial v}\)</span></p><p><span class="math inline">\(\dfrac{\partial  r}{\partialu}\cdot  N=0,\dfrac{\partial  r}{\partial v}\cdot  N=0\)</span> ，均对<span class="math inline">\(u,v\)</span> 求导代入有 <spanclass="math inline">\(L=\dfrac{\partial^2  r}{\partialu^2}\cdot  N,M=\dfrac{\partial ^2 r}{\partial u\partialv}\cdot  N,N=\dfrac{\partial^2  r}{\partial v^2}\cdot  N\)</span> 有<span class="math display">\[\kappa_n=-\dfrac{d\boldsymbol r\cdot d\boldsymbol N}{d\boldsymbol r\cdotd\boldsymbol r}=\dfrac{Ldu^2+2Mdudv+Ndv^2}{Edu^2+2Fdudv+Gdv^2}\]</span> 称分子 <spanclass="math inline">\(H=Ldu^2+2Mdudv+Ndv^2\)</span>为第二基本齐式，若定义 <spanclass="math inline">\(\lambda=\dfrac{dv}{du}\)</span> 为曲线 <spanclass="math inline">\(C\)</span> 在 <spanclass="math inline">\(P\)</span> 点处的切线方向</p><p>则对给定参数 <span class="math inline">\((u,v)\)</span> 下，$_n===f()$，有 <spanclass="math inline">\(\kappa_{max},\kappa_{min}\)</span></p><p>即曲面上给定点 <span class="math inline">\(P\)</span>处的法曲率只依赖于切向 <span class="math inline">\(\lambda\)</span>，进而得到 <span class="math inline">\(Meusnier\)</span> 定理：曲面<span class="math inline">\(S\)</span> 上过点 <spanclass="math inline">\(P\)</span>且具有相同切向的所有曲线具有相同的法曲率</p><p>定义高斯曲率 <spanclass="math inline">\(K=\kappa_{min}\kappa_{max}\)</span>，平均曲率<spanclass="math inline">\(H=\dfrac{\kappa_{min}+\kappa_{max}}{2}\)</span></p><p>主曲率为 <span class="math inline">\(f(\lambda)\)</span>取得最大值最小值时对应的切线方向，<spanclass="math inline">\(Euler\)</span> 定理：选取 <spanclass="math inline">\(xy\)</span> 轴为 <spanclass="math inline">\(\kappa_{max},\kappa_{min}\)</span> 对应方向，设<span class="math inline">\(\vec{t}\)</span> 与 <spanclass="math inline">\(x\)</span> 轴方向夹角为 <spanclass="math inline">\(\theta\)</span> ，有法曲率 <spanclass="math inline">\(\large\kappa_n\small{(\vec{t})}=\large\kappa_{max}\cos^2\theta+\large\kappa_{min}\sin ^2\theta\)</span> ，当 <spanclass="math inline">\(\large\kappa_{max}=2\large\kappa_{min}\)</span>时图像如下</p><p><img src="/img/imgdb_62d0258ff54cd3f9371814f6.jpg" style="zoom:33%;" /></p>]]>
    </content>
    <id>https://learningrate1.com/2022/07/14/%E6%97%A9%E6%9C%9F%E7%A7%91%E7%A0%94/%E6%B1%82%E4%BA%A4%E9%97%AE%E9%A2%98%E6%95%B0%E5%AD%A6%E7%9F%A5%E8%AF%86/</id>
    <link href="https://learningrate1.com/2022/07/14/%E6%97%A9%E6%9C%9F%E7%A7%91%E7%A0%94/%E6%B1%82%E4%BA%A4%E9%97%AE%E9%A2%98%E6%95%B0%E5%AD%A6%E7%9F%A5%E8%AF%86/"/>
    <published>2022-07-14T14:12:22.000Z</published>
    <summary>寒假期间 SRT 项目数学授课，一些计算机图形学知识</summary>
    <title>Intersection Math knowledge</title>
    <updated>2022-07-14T14:12:22.000Z</updated>
  </entry>
</feed>
