<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>KYRIOTA</title>
  
  
  <link href="https://kyriota.github.io/atom.xml" rel="self"/>
  
  <link href="https://kyriota.github.io/"/>
  <updated>2024-04-09T22:17:36.558Z</updated>
  <id>https://kyriota.github.io/</id>
  
  <author>
    <name>KYRIOTA</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>美国PhD申请杂谈与我的本科四年回顾</title>
    <link href="https://kyriota.github.io/2024/02/02/US_PhD_Share/"/>
    <id>https://kyriota.github.io/2024/02/02/US_PhD_Share/</id>
    <published>2024-02-02T17:00:00.000Z</published>
    <updated>2024-04-09T22:17:36.558Z</updated>
    
    <content type="html"><![CDATA[<p>关于我被美国山村博导收留这件事 ᕕ( ᐛ )ᕗ</p><p><img src='/images/clemson.png' style="zoom:60%;"></p><span id="more"></span><h1 id="美国phd申请杂谈与我的本科四年回顾">美国PhD申请杂谈与我的本科四年回顾</h1><p>作为本科生的学习和生活即将迎来尾声，迎面走来的是来自Clemson的Computer Engineering PhD Program</p><blockquote><p>写在前面：</p><p>我的申请范围主要集中在CS Ranking 30~100的学校，且几乎不考虑竞争激烈的名校，相比起各位优秀的大哥我甘拜下风并主动退出竞争，人外有人，I just wanna be happy。</p><p>相比起城里，我更倾向于像我交换过的UC Santa Barbara一样的比较村的学校，这是因为我的娱乐需求很低且村里更亲近自然，在陕西的尘和霾里当了3年空气净化器（相比起我的家乡来说真的受不了一点），我希望从优美的自然环境中找补。</p></blockquote><h2 id="关于phd申请">关于PhD申请</h2><p>开门见山，对于看见标题点进来的读者，PhD申请应该是最受关注的话题。</p><blockquote><p>我的三维：IELTS 7，無GRE，GPA 3.4，一篇期刊在投，很多CTF经历</p></blockquote><p>从可量化的指标上来看（如GPA，GRE，有多少篇paper），不难看出我是一名条件很弱的PhD申请者。</p><h3 id="phd申请的评价体系">PhD申请的评价体系</h3><p>国人常陷入的惯性思维是：只有成绩好，应试能力强的学生才可以升学，并且进入更好的大学。这样的思维方式在国内的大环境下的形成是必然的，我们所有普通人家的孩子自小学到本科，从来都是以分数论英雄：分数高的人进入更好的学校，分数低的人次之，分数很低的人没有学上。但这种评价体系其实自国内本科阶段后便不再适用，也不应继续被使用。</p><p>PhD是一个学位的同时，也是一份工作和职业，而其本职工作便是深入了解某一个大领域（如：计算机中的AI、计算机架构、图形学，物理中的固体力学、流体力学、凝聚态）中的一个细分方向（对于AI，如：Trustworthy AI，NLP，CV，ML System），并尝试在这个细分方向中的一个更细的某一个点上尝试进行突破。我认为下图对于“人类在不同学位时对应的知识范围”总结得非常到位（图片来源：<a href="https://twitter.com/robertoglezcano/status/864895659392077826">Twitter</a>）：</p><p><img src='/images/phd_knowledge.jpg' style="zoom:50%;"></p><p>既然已知PhD的工作与应试能力关系不大，成绩与分数唯一能反映的便是学生对于其知识掌握的牢固程度，而这其实并不是PhD这一位置最需要的能力。一名PhD导师并不希望招来的学生<strong>仅仅</strong>是一群无情的刷题机器，这也是校方对于PhD学生的GPA要求都非常低（大多是高于3.0即可）的原因；相反，学校和导师更希望学生拥有对科研本身以及导师的研究方向有十足的热情与自驱力，并且有较强的科研能力，这也是我们在浏览导师们的招生条件时常看见要求学生“self-motivated”的原因。</p><blockquote><p>已知：导师希望招收科研能力强并且自驱力强的学生，问：如何展示你的科研能力以及自驱力？</p></blockquote><ul><li><strong>科研能力</strong><ul><li>这一方面其实非常简单，参与科研即可。作为本科生，你可以尝试联系本校中你感兴趣的课题组，作为”本科科研实习生“加入课题组，做一些力所能及的工作，运气好人缘好的话还能挂几篇论文的名，可以作为申请PhD时的噱头。为什么我说”噱头“？那是因为多数本科生就算成功加入了课题组，通常也难以获得接触核心工作的机会（如写关键实验的代码，提出或修改实验的核心思路与方法），我听说的最多的工作就是洗洗数据之类的打杂工作。适量的打杂对本科生而言其实非常有益，但前提是你自己不能把自己限定在”我只能打杂“的框架内，积极读paper并思考，多找与教授交流的机会，你的目标是”让教授对你刮目相看，并有把你当成研究生培养的意识“。</li><li>我在这方面比较幸运，因为我是实验班的学生，校方对我们都算是重视和给面子，所以在大二中间给了我们一次参与导师/企业的双向选择的机会，即根据自己毕业后的发展规划（深造or就业）提前参与相关工作。我有幸与<a href="https://web.xidian.edu.cn/clpeng/">彭老师</a>进行了一次合作，虽然没有进组，但得到了一个相对简单的课题一个人单干。现在看来我很庆幸：<ul><li>选择了一位年轻且没有特别特别忙的老师合作，这使我得到了更多与老师单独谈话与获得指导的机会；</li><li>有一个独立的课题并且全部活儿都由我一个人做（读paper调研，做实验，写paper等），这使我拥有了体验科研中的每一个部分并且走一遍完整科研流程的机会，也避免了进组天天被老师手下的博士生带然后还要求写读paper笔记的压迫感（真人真事，在我与其他实验班同学的闲聊中了解到）</li><li>我大二下选择的导师居然在我大三下学期带我们实验班的一门必修课？这下想逃避也不行咯</li></ul></li></ul></li><li><strong>自驱力</strong><ul><li>这一部分就非常抽象了，我个人认为，为了展示你的“自驱力”，你应该尝试向你的申请对象传达出”我对这个领域真的很感兴趣“这件事，那么如何提供相应的事实依据呢？你总不能期待教授在看到你邮件中的”我有很强的自驱力“或”我对XX领域很感兴趣“就相信你吧？自驱力和兴趣保证了你在PhD期间不会因为学术上面临巨大困难和挫折就放弃和消极应对，是保证你顺利攻克课题的重要内驱力，因此，越是关键的命题就越需要你提供强而有力的证据来证明其真实性。但证明”我对XX感兴趣“对于广大国内普通学生来说其实还是挺难的，退一步说，你真的有一个”兴趣“吗？在应试教育中高压烹饪初、高中6年后，很难想象大多数学生还能对”深入学习XX领域“保持兴趣。</li><li>每个人对于“兴趣”的理解应该都有所差异，在这里就聊聊我理解的“兴趣”。打游戏、刷视频等娱乐对于我这一代的年轻人来说应该都是“兴趣”之一了，虽然这不是什么好的兴趣（相比起体育运动或者艺术类活动之类的），但这可以非常好地引出“兴趣”的特征，我个人将其狭义地总结为：当你做这件事时你沉浸其中，当你做这件事时或这件事后你感受到正向积极的情感（满足感、快乐、成就感），这件事不是生理必须的（如吃东西，性，睡觉），这件事最好需要一定程度的付出与代价（这也是娱乐难以作为一种“兴趣”被社会认可的原因，因为娱乐中的成本与代价是人为设计的或是没有成本的）。对我而言，码代码大概就是我非常朴素的兴趣之一了，对我来说可以被称为新型“电子海洛因”了，属于是沉浸进去之后就根本停不下来，特别是后来还染上了CTF，因为比赛是限时的，经常既被迫又主动地通宵码代码解题（健康杀手，还是少这么做）。所以对我来说，我的科研课题和paper、令我印象深刻的AI安全相关的CTF题目都是我重要的给我申请的老师们展示的我拥有“强自驱力”的证据。</li></ul></li></ul><h3 id="phd攻读时间计算">PhD攻读时间计算</h3><p>在美国，直博（direct entry）相比于其他地区而言是更加常见的。例如我的项目（CE PhD），项目描述中的预期毕业年限是4~5年（通常对于很能发paper的直博学生和有硕士科研的学生而言是4年，对于普通直博学生是5年或以上），而美国的硕士（master）毕业年限通常为2年。因此，如果已经决定了要读PhD，那么最好就尽早准备并直接直博，否则读博前先读硕士将让你多花两年学费并且还通常比起直博的学生而言晚一年毕业；尽管如此，先读一个master也并非没有好处，对于本科没有科研经历的学生，先读一个master并尝试做做RA（research assistant），既能获得审视自己是否真正热爱科研的机会，还能有效提高自己的竞争力，在master结束后，通常可以在PhD阶段申请到更好的院校；同时，master由于读书时间短，所以相较于PhD而言也更加灵活，读完两年master后可能觉得经济好、行情好就直接进入市场求职。但值得注意的是：美国，包括欧洲的master大多数都是“授课型”硕士，也就是说不像国内必须参与科研并有几篇paper才能毕业，所以如果你在海外master期间不主动寻找RA机会，你的竞争力将不比你本科毕业时高多少。</p><h3 id="我的phd申请过程">我的PhD申请过程</h3><blockquote><p>一句话总结：学会自我营销并保持真诚</p></blockquote><p>其实我的PhD申请开始时间算是很晚的，由于之前提到的我与彭老师合作的课题在11月中旬才完全结束（写paper太痛苦了），所以我为了在我套磁时有完整的科研经历和“在投（under review）”的噱头（为了能快速证明我的科研能力和自驱力），直到我完成了论文投稿我才开始套磁与申请。如果你有条件，那你最好是可以在10月左右就开始套磁（因为美国很多学校的申请DDL在12月中旬），当然，对于DDL相对靠后的学校（比如也有一些学校会在1月甚至2月DDL），你可以适当延后套磁。通常招人的教授会在他计划的招人DDL（这个DDL通常是教授参考学校的DDL和自己这边的实际情况而定的，所以很看缘分）截止前先等待一段时间，然后分批次把所有他感兴趣的申请者都聊一遍（因此如果你没有被教授单独邀请非正式的面试，那可以说大多数情况下你被offer砸中的概率会很渺茫），最后对申请者进行排序并开始逐一发offer（关于强committee，排名靠前的名校通常采取这种录取方式，即你需要先通过committee的严格审核后才能被教授选择，这种学校的套磁的主要作用就不是admission了，而是确保你通过了committee后能进到意向中的组）。</p><p>由于我是网络安全背景并且打CTF的学生，尽管我有一篇关于CV应用的paper且我的CTF背景其实做的都是AI安全的题，我在AI的其他方向中（如NLP、CV等）还是会显得非常非常弱，所以我一开始尝试套磁的一些NLP、CV，以及multi-modal的组的反馈都很弱。因此，毫不避讳地说，我一开始的套磁时间都花在了一些我很难得到青睐的方向上（但这也并非全是坏处，当时并没有期待在这些方向上首战告捷，主要的想法是迈出实验性的第一步，为后续的套磁积累经验）。虽然在此过程中也得到了一些普普通通的反馈和面试，但我也立刻明白了：就算我进入了这些教授的waiting list，我的排位也一定是相对靠后的。因此我开始寻找我自己的核心竞争力，毕竟你为什么要用你的弱项去碰别人的强项呢？很显然，1. 我拥有安全方面的背景，2. 我希望参与AI方面的研究，既然是AI+安全，那么答案呼之欲出：trustworthy AI（考虑到我的CTF经验，如果教授组里有adversarial ML相关的课题或者项目，则我的竞争力将更上一层楼）。</p><p>于是我开始针对性地寻找做trustworthy AI并且明确说明对AI的security和privacy感兴趣的教授。不得不说由于这个方向并不热门，有的学校我就是把他的CS和ECE的faculty list都翻烂了也找不到一个打着这些tag的教授。但最终我套磁的做AI安全相关的教授也都给我安排了面谈并对我给予了高度肯定，可见对自己竞争力的正确认知（即尽量把重心放在自己最相关的方向的套磁上）以及对教授招人心理和目的的把控（即分析你的能力在进了该教授的组后是否能直接立刻开始干活？这个教授的个人主页中展示的课题和项目中有没有你一看就知道你可以帮上忙的内容？）至关重要。</p><blockquote><p>所以这个迴圈大概是：</p><ol type="1"><li>通过信息源获悉招生信息（如一亩三分地上有很多华人教授，或是直接去学校官网faculty list翻找）</li><li>仔细浏览教授的个人主页，重点关注教授的research interest、funding（如果有展示），以及学生水平（如果附上了学生的信息或个人网站）（我记得我在找教授时看到过一个毕业于复旦的教授手下的学生也全是复旦毕业的，对于这种组如果你不是复旦的那最好就别考虑了）</li><li>写出你的第一封套磁信或是基于你的上一份套磁信进行针对性修改，并在发出套磁信之前多读几遍（我曾经有一封套磁信因为改完之后懒得仔细读，导致教授的姓氏忘了改了哈哈哈哈）并尝试改良，每次都改良一点点，你的套磁信就会变得越来越好</li><li>等待教授回复，通常有以下几种结果：石沉大海（两周后仍然不回则可以考虑重发或放弃），象征性回复（只告诉你你真棒，然后鼓励申请），约面谈时间（恭喜你这表示你成功引起了教授的注意和兴趣）</li><li>循环往复，注意这个流程可以多线程并行</li></ol></blockquote><h3 id="套磁信的写作">套磁信的写作</h3><p>因人、因项目、因方向而异，我的评价是：真诚且条理清晰就好。不要海投完全没有针对性的邮件，这毫无诚意，哪怕你只是稍微花个十分钟点进教授的主页了解一下，并针对性地修改一下你的套磁信模板，也会显得真诚得多，核心思想就是让教授认为你有用心了解过他的组（不一定要读paper，但你可以浏览一下paper标题了解一下该教授的组大致的方向，我从来没有在面试结束前去详细读教授的任何一篇paper，时间不允许，也没必要）；不要写一大堆无法分开阅读的文字，教授的时间有限，他不是来看你展示你的文学功底和听你讲小时候的梦想的，你需要通过如小标题、序号等hint来引导教授进行高效的阅读，让教授在最短的时间内就能获取到他最需要的信息。以下是其中一篇我用于套磁的冷邮件，仅供参考（通常还需要附上各种成绩单以及简历的附件）：</p><p><img src="/images/taoci.png" /></p><h3 id="关于中介与代写">关于中介与代写</h3><p>别指望你请的中介比你更了解你的field和你的research经历。</p><p>在强导师的情况下，PS（或者说SOP）我怀疑甚至可能没人会在意，这种情况下文书一定不是你申请是否成功的决定性因素（强committee和申请master中文书则非常关键），你不如多打磨一下简历，毕竟这是要随着你的套磁信一起发出去的。</p><p>关于论文代写以及买科研经历：如果你能买，别人也能买。教授很有可能会从你意想不到的角度提出关于你科研经历的问题，是不是切身参与、是不是做了实事、你到底清楚哪些内容，只要教授想，通过30分钟的面谈都能给你翻个底朝天。所以，请保持真诚。</p><h2 id="思考与回顾">思考与回顾</h2><p>感觉本科四年回顾的内容放在PhD申请杂谈中怪合理的，毕竟本科毕业和下一步的升学是几乎同时发生的，且本科阶段的经历决定了我的PhD申请，所以我大胆假设可能会有读者也会接着浏览关于我的本科回顾部分的内容吧。</p><h3 id="教育杂谈">教育杂谈</h3><h4 id="教育体制">教育体制</h4><p>对于大多数普通的国内青少年，在高中毕业之前，几乎没有除了努力之外的选择和做出相应选择需要的能力与眼界。在家长们还在认为高考考取一个好本科就能获取一个很好的发展时，中国社会已经进入硕士都找工难的版本了，于是乎，当代父母不得不违背当初“考上大学了你就轻松了”的“诺言”，并开始叮嘱孩子在本科卷出好的GPA保研或者在考研前尽早做好准备。但同时也有部分家长和大部分年轻人终于看透澈了：无休无止的卷，卷到什么地步是个头？我们都被要求相信延迟满足，延迟有了，而且延迟的时间已经很长了，那满足什么时候来？中国大学生现在面临的是大家都留在学校读硕士的时代，那等到真的大家都有硕士学历之后，又和曾经的大家都有本科学历的状况有什么区别呢？我们就算接受了再高等的教育，大多数人也还是难逃成为打工人的命运，毕竟教育从来就不是通向如财富自由、商业成功的道路，教育给普通人带来的经济利好仅是提高了受教育者的薪酬中位数。在欧美，你不见得需要进入一个好大学才能获得一份好工作，欧美大学的学费高是一方面，但更多的是大多数人对于“高等教育”有着更加清晰的认知，这是为了科研而生的一套教育体系，而不是主要为了就业而服务的。因此我的评价是，应润尽润，在国内深造发展不一定能赶上国外的技术突破，但你在国外读完了书却可以根据毕业时的世界形势决定你未来的发展（回国or留下来），所以尽可能把路走宽，提高全球意识，多给自己留些选择，避免梭哈。</p><h4 id="卷还是躺">卷还是躺</h4><p>那我们还要努力、还要内卷吗？对于普通人而言，我的观点是：必须努力，但别内卷。虽然现在有把内卷和努力划等号的趋势，但我认为其两者仍然能从内在动机层面上区分开来：当作兴趣的努力可以认为是正面意义的努力，为了赢过别人的努力可以认为是负面意义的内卷。举个例子，你非常享受绘画的过程，且你绘画的根本目的不是为了画得比某人或某个市场平均值画得更好，这样的努力通常是幸福且快乐的；相反，在我这个年龄段上过大学的人都理解“青轴喊麦我安然入睡，深夜翻书我彻夜难眠”的感受，便不需要我再对“内卷”一词做过多解释了。我自从上了大学就不是一个认真上课的人，我当时这么做有以下几个原因：</p><ol type="1"><li>我刚进大学时的打算是就业为导向的，所以与其每天预习、复习，并在期末的时候把你本来就已经基本掌握的东西花很多时间在边际效益递减明显的情况下刷题到滚瓜烂熟，我单纯认为不值得；多学点语言特性、多折腾一下自己博客对我来说性价比更高，更何况我还发自内心地觉得这些事儿非常有趣。不是高分拿不起，而是做自己喜欢的事更有性价比。</li><li>从直觉上反感内卷，卷是一种恶性竞争，且不是以人为本的。举个例子，在一个保研率非常清晰的学院中，当某一门课的期末考试时间提前了，而你这门课的好哥们同学因为某些原因对此浑然不知，那么此时你是为了排除掉一个竞争对手而不主动告知，还是看在好兄弟的情谊上告诉他？你如何回答取决于你的人格和品性，但这个体制和大环境本身的最优解却只能是前者。有的人也许会想，所有竞赛都是这样的筛选机制，那么这些机制也都如此不堪吗？但我认为本质的不同就在于中国的问题是这样的竞争氛围是贯穿年轻人十几二十年的，量变引起质变了。对于良性的竞争，咱就说我接触最多的CTF，从我管中窥豹的观察来看，那真是复古地回到了最朴素的原初互联网精神：开放、平等、协作、分享。</li><li>我不太认可大部分国内本科的课程安排和教学质量，这话在我大四来UC Santa Barbara交换体验了美国本科教育之后更加肯定了。这个问题的根本我认为有两个方面，一是学生层面，很多学生压根就没有他感兴趣的学科，你教得再好他也只是被动地接受而不是积极思考并渴望知识；二是教授层面，这个问题简直就是养老问题的一个克隆，即本科扩招了，但其实无法掏出那么大量的优秀师资来承担本科教学，因为在之前培养出来的教授总量只有那么多，要找补则只能横向往不那么优秀的师资伸手。</li></ol><p>因此，再次申明，我提倡努力但不提倡内卷，应该将精力更多地放到自己的兴趣上，而对于GPA这东西，别被退学就行。你可能会对你努力的方向产生怀疑，但可以看看乔布斯在斯坦福的演讲，尤其是对于他提到的“书法课”一事（大学退学前修了一门书法课，多年后贡献到苹果的字体设计中），即当下的付出可能会对未来的发展产生意想不到的作用，也算是机会总是留给有准备的人的一种解释和一个案例吧。</p><h4 id="概率与采样">概率与采样</h4><p>总的来说，在人生的尺度上，一切未发生的事都有一个概率分布，当我们的人生来到那一个特定的节点时，我们便根据这个概率分布进行了一次随机采样。小明努力准备考研，每次自测都能稳在很高的分数，也架不住考研那天因为疫情感冒发烧；小红努力准备高考，他的模拟考分数就像是在一个区间内均匀分布的随机数<code>rand(580, 650)</code>，那么他将会非常焦虑，因为他无法控制高考时这个随机数会取到什么值。抗压能力和对概率本身的认知非常重要，我们能做的也只有：</p><ol type="1"><li>尽可能广泛地获取信息并根据信息构建对环境的认知</li><li>赌一个通过分析得出的高回报率的长期目标，也就是找到回报的期望最大的那个方向，没必要过度分析，除非您是拉普拉斯妖</li><li>开始为了这个目标做实际的事情，切分成很多短期目标分而治之也好，多线展开也好，总之开始做事</li><li>当认知发生重大改变时返回①</li></ol><p>由于任何事情都是一次采样，我们可以尽情调整在②中的“分析”的深刻程度：深刻的分析意味着我们相信通过我们的分析，我们可以较为精确地对环境进行建模；浅显的分析或者甚至不进行分析意味着我们相信作为人类的个体完全无法预测未来，无法对环境哪怕建立一个粗糙的模型。因此在不同的建模尺度下，应该针对认知和建模对象对此参数进行调整。</p><h3 id="回顾">回顾</h3><h4 id="大学之前">大学之前</h4><p>由于我在高中毕业前为数不多的“合法”（在父母眼中合法）长时间使用电脑的时间仅为在校内的时间，我全权包揽了12年的高中毕业前的班里的多媒体管理职务，为我自己争取到了每天都能捣鼓捣鼓电脑的机会。高考结束后第一件事就是买一台笔记本了，由于受到视游戏为洪水猛兽的思潮限制，最终只能选择了轻薄本才能有合家欢的happy ending，现在看来我推荐任何准大学生购买自己的第一台笔电时考虑全能本或性能再往上的选择。高考结束之后我直接解除封印在高中毕业的那个假期把C#稍微学了一下，做了一个非常简陋的以本地excel作为数据库的类似墨墨背单词的窗体应用，做这个的主要动机是当时有使用墨墨背高考的单词，觉得他这一套交互逻辑和使用体验都挺不错的，于是希望把这样的背书模式扩展到其他科目，并支持一下如图片之类的显示。结局是好不容易像搭积木一样堆了大概1k+行代码之后发现放在PC端完全不能利用上碎片化的时间，同时也不如手机上使用着那么顺手，总之最后是作为黑历史吃灰了。</p><h4 id="大一">大一</h4><p>卡线进了西电，当时大多数人能选的西电就三个大类：自动化、电子信息、计算机，于是不出意外录了自动化，当时自我安慰说这是“万金油”，但这也属于是将古早的刻板印象带到新的时代，刻舟求剑了。虽然西电入学后在大一开始的时候有一个卓越班筛选考试，但不出意外地竞争不过河北之类的内卷大省，河北省室友确实强，进了电子信息，而我们剩余的三人便只有等到大一结束时专业分流的份了，跨大类是很难的了（当时确实是这么想的）。进校第一件事当然是广泛参加一些校内的活动和组织了，一开始主要参与了浪潮俱乐部和学生会，浪潮俱乐部挺好，对我也产生了不小的影响，但关于学生会，只能说这种无实体价值交换的社交在我看来还是太魔幻了。在技术导向的组织（如浪潮俱乐部，网安协会）中，如果一名新成员真的有很强的技术实力，那么他就真的大概率会成为整个组织的焦点，但是对于学生会吧，在我的视角中，熟人圈内的继承各种部长会长，熟人圈外的只有幸苦打杂的份，评价为起码不适合我。在大一上学期快结束时，我因为偶然去听了一次浪潮中一名网安实验班学长的分享会，了解到了大一结束时居然还有少量实验班选拔可以作为转专业机会，我印象中只有网安实验班和空间科学实验班是这个时期选拔的，每个班招40人左右。而在后来和这位学长的深入交流中，我得知了CTF这种网安方向的赛事活动以及网安实验班的选拔流程，也属于是在大多数人得知这一转专业机会前就了解到了这些信息，打了个信息差。除此之外，这些浪潮的分享会还启发了我建立自己的博客页面，这也是作为读者的您能在此时此刻阅读到这些文字的原因。</p><p>于是大一的寒假我开始天天刷bugku（一个提供CTF赛题的国内平台），主攻web题（当时记录的<a href="https://kyriota.com/2021/03/16/WebBasicRequirement/">web学习笔记</a>）。直到大一下开学时，感觉自己已经多少有了一些CTF的能力，才去给XDSEC（西电网安协会）发了邮件申请加入。最终在校方准备的像狗屎一样的CTF机试中侥幸存活，并成功加入了实验班换了专业。在大一结束之前，XDSEC还办了一个校内CTF，mini L CTF，用于筛选一批日活高和有潜力的玩家加入"里世界"，也就是L team，一个代表西电参加更大规模、更正式的CTF的专业团队（貌似是否受官方认可一事还存在争议，但这无疑已经是西电唯一的CTF正规军了）。所以意料之中地，既然我都已经写了这些内容了，我便的确被邀请进入了L team这个里世界。不仅在L team这边活跃，我在浪潮俱乐部，作为代码能力在一众新生中还算稍微有点基础的，被学长（或者更准确地说是当时的浪潮俱乐部社长或者主席，现在去了清华图形学直博，之前看了一眼貌似是在做虚拟人物的骨骼控制相关的机器学习模型，很强的学长）邀请加入由他亲自指挥部署的机器学习小组参与学习（大概是渴望体验一把带学弟学妹的快乐，并将自己学到的知识分享出去吧）。当时主要的学习模式就是暑假保持看吴恩达的机器学习公开课，并将算法（线性回归、逻辑回归、如何正则化等）不调库地用Python实现然后push到小组的github仓库中去。说实话整个小组的学习效果只能用惨淡来形容，虽然学长计划了每周的腾讯会议线上交流学习成果和向他提问的时间，但只能说果然大家放假了还是更想开摆，收效甚微，参与的人越来越少。最后的结果是只有加上我不超过3个同学在他的带领下完成了这些学习，不得不说这个假期的学习为后来我深入这一领域埋下了种子。</p><p>对于我其他自动化室友的专业分流（我们虽然入学时室友是可以自主选择的，但寝室楼层是按照入学时的大类划分的，因此我能一块住的室友都是入学时就是自动化大类的同学），一名比我更卷、GPA更高的同学进入了机械，另一名非常摆、GPA比我更低的同学也进入了机械，上界和下界都已确定，夹在中间的我如果没有成功转专业，那多半也是一起进了机械（不是，你自动化大类为什么机械200多人，自动化才五六十人？你怎么不直接叫机械大类？）。我的大一便在不用担心专业分流分到自己不喜欢的细分专业中的愉悦心情中结束了（同时还没少和室友开黑打游戏哈哈哈哈，Overwatch、The Forest、L4D2等等，太快乐了）。</p><h4 id="大二">大二</h4><p>虽然加入了XDSEC，也进入了里世界L team，但我在不断的CTF赛事中逐渐意识到我的能力根本就无法和团队中的旗舰级web选手比较，当我还在纠结某个题的出题点到底是什么时，我们的web一哥二哥已经把题目给AK了（all clear）。于是我便开始消极摆烂了，这还真不是我不思进取，而是我知道我和他们之间的技术力差距过大，我在学习的时候他们也在学习，除非我能以非常快的速度学习并达到接近团队中旗舰选手的水平，否则永远被大哥压一头的感觉还是很难受的，我将很难得到正反馈并将“学习-&gt;做出题目-&gt;得到成就感并激励学习”的循环启动起来。</p><p>于是我开始做一些奇奇怪怪但对我来说非常有趣的东西，毕竟我渴望正反馈嘛。比如<a href="https://kyriota.com/2021/04/24/RenderHighDimensionalObjectInTerminal/">关于怎么在命令行中用字符绘制超立方体</a>、玩玩Unity 3D这个游戏引擎并做做和Unity相关的程序（<a href="https://kyriota.com/2021/10/02/AntColonyOptimization/">蚁群算法</a>、<a href="https://kyriota.com/2021/09/23/ComputeShaderAndBoid/">鱼群模拟</a>），甚至是根据B站的教程拼一台自动写字机来帮我抄物理实验报告和一些水课笔记，属于是梦回机械了哈哈哈（后来证实这其实是相比手写更慢的，这玩意儿老故障，很折腾，但作为一个上手把玩的玩具它是合格的）。下面请看VCR：</p><video src="/images/writing_mach.mp4" style="max-height: 60vh; display: block; margin-left: auto; margin-right: auto;" controls></video><p>在此期间，我还正好看到了Nova独游社（大概就是个热衷于游戏开发的社团）的Game Jam宣传（game jam是一种关于游戏开发的活动形式，参加者需要在很短时间内，通常是两三天，针对组织者发布的一个特定主题攒出一个可以入得了眼的游戏出来），但由于这个Game Jam是针对新人开放的，好多人甚至还不太会写代码就来参加活动试水了，所以Nova组织方很人性化地将这个Game Jam的制作时间拉长到了大约是两三个周的长度。但KY的我作为大二“老人”没有意识到这一点，直接跟我高中时的一好哥们对这个活动一起进行了996高强度爆肝，可以说这个活动是我当时挂大学物理的原因之一（临近期中考试，然而相比起预习物理我选择继续捣鼓Unity；后来期末又因为在捣鼓神经网络也没好好预习；好孩子别模仿），当然后来补考过了（这一次之后一想到根本不用考虑保研了就更加放飞自我了），这用心程度，那些新人哪受得了（捂脸）。所以不出所料地成为了Game Jam展会时全场唯一的画面乍一看还像那么回事的3D游戏（起码多了全局光照烘焙、正确使用材质和模型、写出类似起源引擎移动手感的运动脚本，虽然最后比起正统起源引擎完全不像就是了），尽管当时还有很多创意、关卡设计，或是机制设计比我优秀的游戏被展示，但大家也许是看到画面层面的噱头，也可能是对工作量的认可，最终我与我的好哥们（远程参与）荣幸地获得了由Nova颁发的一等奖。</p><p><img src='/images/nova_game_jam.jpg' style="zoom:50%;"></p><center><small>我在Nova Game Jam上的游戏展示</small></center><p><br></p><p>大概也就是在相近的时间，在作为web选手摸鱼潜水了几个月后，我在L team中的定位也迎来了转折。首先是在TCTF 2021 Rising Star（也就是马化腾家给高校办的CTF）的杂项题（杂项题，Miscellaneous，简称MISC，一般是一些隐写术、编码、或者奇奇怪怪的知识点组成的一组题目）中混进了一个AI题，他用了一个人脸识别的包，并部署了一个后端，然后选手的目标貌似是构造一张能骗过这个人脸识别后端的人脸图像。我一看，啪一下啊：“我去这么有意思的题目，太黑阔辣！”然后当时只会一些浪潮俱乐部那边学的回归算法的我就兴致勃勃地开始狂日这个题，结果自然是只能以<code>random</code>随机加噪音把这个题提交了（好歹是做出来了，虽然非常不优雅，甚至有点暴力）。后来，在2021的西湖论剑CTF中，我又在杂项里看到了<a href="https://kyriota.com/2021/11/25/%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91GlobalNoise%5BAI%E5%AF%B9%E6%8A%97%E5%90%AF%E8%92%99%5D/">AI题</a>，感觉是个缘分，就上手试了一下。但这次的题就不是我这种三脚猫功夫的半吊子能用随机数爆出来的了，他这大概可以算个论文题，即你需要根据题目要求抽象出问题后通过查资料发现之前有人发过paper解决过类似的问题，于是你的任务就变成复现论文了，故曰“论文题”（论文题一般在AI和密码方向比较常见）。最终，很遗憾地，这个题对于当时连神经网络都捋不清楚的我可以说是完全做不了一点，也正是这个题激发了我一定要把神经网络弄明白的斗志和决心。此时大二上的期中应该刚过，于是为了弄懂这个题目到底在干嘛，我又开始了在神经网络中的梭哈式学习（看博客的时间记录貌似是一直持续到了大二上的期末，GPA低这件事看来是破案了），并最终总算从里到外地浅显地理解了神经网络的方方面面（<a href="https://kyriota.com/2022/01/16/%E5%85%A8%E8%BF%9E%E6%8E%A5%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%5B%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%85%A5%E9%97%A8%5D/">全连接神经网络学习笔记</a>，可惜当时不会LaTeX，公式写得很抽象），学习成果大概就是只调用<code>numpy</code>和一些基础库手搓一个全连接神经网络。这便是我们推崇CTF to learn的原因，即，CTF选手多半可以陷入下面这样积极的循环中：</p><ol type="1"><li>你遇到一个你不会的题目</li><li>你在比赛前因为没有系统地学习过相关的知识，没能在比赛中完全把这个题弄明白</li><li>你的好奇心和好胜心被激发，在比赛后激情四射地把这个领域知识点给拿下</li><li>也许在下一场CTF中（大大小小的CTF赛事举办密度非常高），你又遇到了相似的知识点，你熟练地把题目拿下，赢得队友夸夸和吹捧</li><li>至此，一个人在自主（不受约束，可以自由选择，在CTF中体现为你可以选择任何一个题目、任何一个方向下手）、关联（与他人产生联系，获得他人的肯定）、胜任（掌握技能，克服挑战）三个方面的需求都得到了充分的满足</li><li>你终将会又遇到一个你不会的题目，然后回到①</li></ol><p>至于大二下，我继续参与了各式各样的CTF，并如预期地遇到了更多的AI相关的CTF题目，属于是在AI题这一个板块顺利成为咱们小团队里的第一人了，虽然有很多CTF根本就没AI题就对了哈哈哈哈，所以我其实基本属于是当某个CTF赛事中出现了AI题时才被call的AI题专员。当然，由于我也有Unity 3D的开发经验，所以有的和游戏相关的题也会向我呼叫增援。虽然我们这个CTF圈天天同行互吹，但是被吹到点子上还是忍不住截了一个图（这个Star CTF应该是在大三的时候的，当时说实话科研做实验很忙，要不是大白兔会长这彩虹屁放得这么香，我是真不会来帮忙的，最后的结果就是我把我该做的game题和AI题都拿下咯）：</p><p><img src='/images/kyr_god.jpg' style="zoom:25%;"></p><p>在大二下，学院还为我们实验班学生创造了和导师进行一次双向选择的机会（说实话这双选来得太晚了，人家人工智能卓越班大一下就搞完双选了），我有幸与我们网信院的彭老师进行合作，关于这部分内容可以回到“关于PhD申请-&gt;PhD申请的评价体系-&gt;科研能力”查看细节。</p><h4 id="大三">大三</h4><p>大三就真的没多少可以叙述的内容了，基本都沉浸在更多的CTF和科研中。由于轻薄本跑不动CNN的训练，于是在大三上结束时攒了一台4080的台式用来跑模型，说实话那段时间寝室电费感觉确实掉得比以往快一些，老是因为电费欠费了被迫停电，但由于我自己也不知道到底是不是我天天训练模型的原因，所以我也不敢说。</p><p>大三下的时候我报名了我们学校国际交流部宣传的一个UC Santa Barbara的GAPP交流项目，也就是大四最后一年出去读，体验一下美国的本科教育，并尝试着争取一些推荐信和RA机会。</p><h4 id="大四">大四</h4><p>也就是现在了，我写这篇博客文章的时候。正如我之前所说的，我和彭老师合作的课题直到11月才做完，所以为了方便补一些实验，我其实把那台4080的台式机也给托运到美国来了。美国大多数学校貌似采用学季制，即一个学年分成三个学季上课，但上课的周数仍然和学期制保持一致。学季制的好处是，因为每门课的持续时间相对更短了，所以同样是一年过去，学季制上的课的要比学期制的课种类更加丰富，数量上也更多。</p><p>UCSB的交流项目中，学生所属的部门是UCSB Extension，所以说实话参加交流项目的同学在选课上还是低人一等的，需要等正规军选完了后我们才能选；但好在UCSB每学期开设的课程都足够多样且都很优秀，所以对我来说，我总能找到没被选满的且我感兴趣的课程。</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        点击此处展开/收起一些UCSB的照片    </div>    <div class='spoiler-content'>        <p><img src='/images/UCSB_parking.jpg' style="zoom:15%;"></p><center><small>从UCSB的教学楼远眺</small></center><p><br></p><p><img src='/images/UCSB_mountain.jpg' style="zoom:15%;"></p><center><small>蓝调时间在上学路上拍摄的圣莫妮卡山脉</small></center><p><br></p><p><img src='/images/UCSB_beach.jpg' style="zoom:15%;"></p><center><small>在UCSB的海滩上可以望见的海上石油钻探平台</small></center><p><br></p>    </div></div><p>然后，关于PhD申请之类的话题，本篇博客之前也已经说过啦，在这里就不重复了。</p><h4 id="杂七杂八画廊">杂七杂八画廊</h4><div class='spoiler collapsed'>    <div class='spoiler-title'>        点击此处展开/收起杂七杂八的照片    </div>    <div class='spoiler-content'>        <p><img src='/images/garbage_PC.jpg' style="zoom:25%;"></p><center><small>由于父母的电脑管控，在初中攒钱参考图吧大佬经验攒的第一台机子</small></center><p><br></p><p><img src='/images/dorm_0.JPG' style="zoom:10%;"></p><center><small>到西电后早期的寝室床位布置</small></center><p><br></p><p><img src='/images/XD_show.JPG' style="zoom:7%;"></p><center><small>机电院迎新表演，上台稍微玩了一下www</small></center><p><br></p><p><img src='/images/zhujiaofan.JPG' style="zoom:10%;"></p><center><small>被室友安利了我在学校吃得最多最爽的潮汕猪脚饭，很好的辣椒</small></center><p><br></p><p><img src='/images/XD_hammer.JPG' style="zoom:10%;"></p><center><small>自动化大类在大一时要求的金工实习，从一坨铁中磨出了个锤子，但转专业后仍然得在大二接着做计科大类下要求的电装实习去焊电路板，不能直接用之前磨的锤子置换学分，难绷</small></center><p><br></p><p><img src='/images/dorm_1.JPG' style="zoom:10%;"></p><center><small>写字机和显示器都就位的留念</small></center><p><br></p><p><img src='/images/XDSEC_discussion.jpg' style="zoom:20%;"></p><center><small>给网安协会中对AI安全感兴趣的成员做点科普和宣讲</small></center><p><br></p><p><img src='/images/XD_DTFRY.JPG' style="zoom:10%;"></p><center><small>西安的大唐芙蓉园，貌似是元旦前后去的，刚好在办灯会，非常值的门票钱</small></center><p><br></p><p><img src='/images/XD_WU_OR_MAI.jpg' style="zoom:10%;"></p><center><small>疫情期间某次来到操场做核酸</small></center><p><br></p><p><img src='/images/XD_HUA_SHAN.JPG' style="zoom:10%;"></p><center><small>国庆假期和一个室友一起在华山山顶睡了一晚帐篷，然后看了日出（冷得根本睡不着）</small></center><p><br></p><p><img src='/images/XD_Square.jpg' style="zoom:60%;"></p><center><small>学校广场两边的银杏</small></center><p><br></p><p><img src='/images/XD_wtf.jpg' style="zoom:30%;"></p><center><small>寝室舍友群的群头像（没错就是我\呲牙）</small></center><p><br></p>    </div></div><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">仅代表个人观点，如有异议欢迎留言探讨与指正</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>CISCN2023初赛 AdvDetPatch WriteUp</title>
    <link href="https://kyriota.github.io/2023/08/07/CISCN2023%E5%88%9D%E8%B5%9B_AdvDetPatch_WriteUp/"/>
    <id>https://kyriota.github.io/2023/08/07/CISCN2023%E5%88%9D%E8%B5%9B_AdvDetPatch_WriteUp/</id>
    <published>2023-08-07T22:30:00.000Z</published>
    <updated>2023-08-07T12:27:11.714Z</updated>
    
    <content type="html"><![CDATA[<p>之前一起打了CISCN 2023初赛，因为周末睡懒觉晚到了两三个小时导致做这个题的时间不充裕了，最后在比赛结束后半个小时出了，主要原因还是因为没有玩过Yolo，当时记得这个题的解题数是很低的（或者好像是零解的？反正截止至我开始写这篇WP的时间[2023-08-07 18:30]，我在网上并没有搜到WP），本题在选手机器学习底子坚实的前提下主要考验选手入手新的模型和领域的速度以及将对抗样本训练应用上来的速度。</p><span id="more"></span><h1 id="ciscn2023初赛-advdetpatch-writeup">CISCN2023初赛 AdvDetPatch WriteUp</h1><h2 id="题干">题干</h2><blockquote><p>本次竞赛要求选手针对目标检测模型进行对抗攻击。题目提供了1张样例图片（/home/adv/images/stop.png），要求选手在给定的图片中添加一些对抗性补丁，上传带有补丁的图片和代表补丁位置的mask，要求：</p><ol type="1"><li>使得模型无法正确地检测出图片中的所有物体</li><li>并且补丁的面积不能超过图像面积的5%（例如对于640 * 640的图像，patch面积不超过20480 ），就能得到flag。</li><li>图像的尺寸、命名、文件格式（<code>.png</code>）和原始图像保持一致。</li><li>mask图像只存在0或255两种像素值，其中255代表patch像素占据的位置。文件名保存为<code>mask.png</code>，mask的白色部分代表补丁部分，黑色是未修改部分。</li></ol></blockquote><p>以下为需要选手攻击的图片</p><p><img src="/images/CISCN_2023_stop.png" /></p><h2 id="解题过程">解题过程</h2><h3 id="初步验证">初步验证</h3><p>由于我是第一次玩Yolo，先自己跑一下看看能否得到正确的输出，通过在<code>fool_me.py</code>中<code>print(pred)</code>得到如下输出</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">tensor([[3.38821e+02, 1.65334e+02, 5.22038e+02, 4.83467e+02, 9.34208e-01, 1.10000e+01],</span><br><span class="line">        [4.66704e+02, 2.09673e+02, 6.39832e+02, 5.45919e+02, 9.33491e-01, 1.10000e+01],</span><br><span class="line">        [2.00159e+02, 2.04222e+02, 3.79344e+02, 5.11239e+02, 9.25105e-01, 1.10000e+01],</span><br><span class="line">        [7.07512e+01, 2.37424e+02, 2.40440e+02, 5.33154e+02, 9.14894e-01, 1.10000e+01],</span><br><span class="line">        [7.70359e-01, 2.53848e+02, 1.03618e+02, 5.43242e+02, 9.01769e-01, 1.10000e+01],</span><br><span class="line">        [1.41904e+01, 3.93319e+02, 4.42650e+01, 4.40926e+02, 6.95912e-01, 2.40000e+01],</span><br><span class="line">        [1.30602e+01, 3.96193e+02, 4.70077e+01, 4.42493e+02, 4.05268e-01, 1.10000e+01]])</span><br></pre></td></tr></table></figure><p>对此我们需要确定此Tensor中每个Dimension的含义</p><p>我们查阅关于Yolo的资料可知，每个向量中前四个元素代表了Bounding Box的范围，第五维代表了置信度，第六维代表了Class(Label)</p><p>据此我们可以写出可视化脚本来可视化模型的输出结果，可视化如下</p><p><img src="/images/CISCN_2023_Yolo_Example.png" /></p><p>可见根据出题人给出的代码跑出的输出可以正确地被用于在原始图像上标记出所有Stop Sign的范围</p><p>接下来我们需要做的就是攻击这个模型，使得没有物体被检测到了</p><h3 id="查找资料">查找资料</h3><p>根据题目标题，我们正在做的是Adversarial Patch，对抗性补丁，按照我的理解，这与之前看过的在一个图像上贴一个不透明的同时只覆盖部分原始图像的技术是一个东西</p><p>目前这项技术比较流行的是在现实世界中的Adv Patch，如下图，挂有Adv Patch的人无法被模型识别出是人类</p><p><img src="/images/CISCN_2023_Yolo_Adv_Patch_Example.png" /></p><p>根据中英文关键词（Yolo Adversarial Attack; Yolo Adversarial Patch; Yolo 对抗补丁; Yolo 对抗攻击），查找到了一篇题为Sparse Adversarial Attack to Object Detection<a href="https://arxiv.org/abs/2012.13692">预印本文章</a>，这篇文章正好提供了我们需要的信息，我们只需要看文章中的两张图就知道如何攻击了</p><p><img src="/images/CISCN_2023_Paper_Fig_1.png" /></p><p>上图介绍了攻击的流程，比如确定Mask的形状，通过反向传播获取增加到图像上的扰动等。其中他有两个Preprocess我没太明白，但这不影响我们理解攻击的方式</p><p><img src="/images/CISCN_2023_Paper_Fig_2.png" /></p><p>上图展示了被攻击成功的图像，请注意图像中类似杂讯的十字，这可能需要对图像进行放大才能清晰地看见。我们可以由此认识到：其实Adv Patch就是在最基础的对抗攻击中限制了可以攻击的图像面积，并且被攻击的像素是替换而不是叠加</p><h3 id="exp">Exp</h3><p>首先需要获取一个Mask，在调试题目的代码后，我发现题目对修改像素的数量其实比较宽松，所以我们采用米字形的Mask以更好地对需要攻击的部分进行扰动，这个Mask可以通过PS绘图后使用Python将灰色像素给去掉（因为题目限制了Mask的像素必须非0即255，所以需要去掉灰色的像素）</p><p><img src="/images/CISCN_2023_mask.png" /></p><p>有了这个Mask我们就可以开始攻击了，现在移步<code>fool_me.py</code>进行代码修改</p><p>首先我们使用随机噪声填充这个Mask</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Get Noise    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">gen_noise</span>(<span class="params">mask_path, source</span>):</span></span><br><span class="line">    mask = Image.<span class="built_in">open</span>(mask_path)</span><br><span class="line">    mask = np.array(mask)</span><br><span class="line">    noise = torch.rand(<span class="number">640</span>, <span class="number">640</span>, <span class="number">3</span>)</span><br><span class="line">    noise = noise * <span class="number">255</span></span><br><span class="line">    noise = noise.<span class="built_in">int</span>()</span><br><span class="line">    noise = noise.numpy() * mask</span><br><span class="line">    noise = Image.fromarray(noise.astype(np.uint8))</span><br><span class="line">    noise.save(source)</span><br></pre></td></tr></table></figure>    </div></div><p>然后我们需要修改原本验证图像是否攻击成功的代码，其实就是对输入图像求个梯度，然后累加这个梯度，直到攻击成功</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Edited Code    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Run inference</span></span><br><span class="line"></span><br><span class="line">   grad = <span class="literal">None</span></span><br><span class="line">   crackFlag = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">for</span> path, im, im0s, vid_cap, s <span class="keyword">in</span> dataset:</span><br><span class="line"></span><br><span class="line">       dt = [<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>]</span><br><span class="line">       </span><br><span class="line">       t1 = time_sync()</span><br><span class="line">       im = torch.from_numpy(im).to(device)</span><br><span class="line">       im = im.half() <span class="keyword">if</span> half <span class="keyword">else</span> im.<span class="built_in">float</span>()  <span class="comment"># uint8 to fp16/32</span></span><br><span class="line">       im = im / <span class="number">255</span> <span class="comment"># 0 - 255 to 0.0 - 1.0</span></span><br><span class="line">       im.requires_grad = <span class="literal">True</span></span><br><span class="line">       <span class="keyword">if</span> <span class="built_in">len</span>(im.shape) == <span class="number">3</span>:</span><br><span class="line">           im = im[<span class="literal">None</span>]  <span class="comment"># expand for batch dim</span></span><br><span class="line">       t2 = time_sync()</span><br><span class="line">       dt[<span class="number">0</span>] += t2 - t1</span><br><span class="line">       <span class="keyword">assert</span> im.shape == clean_img.shape, <span class="string">&quot;Image shapes must match.&quot;</span></span><br><span class="line">       </span><br><span class="line">       <span class="keyword">while</span> <span class="keyword">not</span> crackFlag:</span><br><span class="line"></span><br><span class="line">           <span class="keyword">if</span> grad:</span><br><span class="line">               im = im - grad[<span class="number">0</span>]</span><br><span class="line">               <span class="comment"># ensure im is in [0, 1]</span></span><br><span class="line">               im = torch.clamp(im, <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">           new_im = im * mask + clean_img * (<span class="number">1</span> - mask) <span class="comment"># eval patch</span></span><br><span class="line">           <span class="comment"># Inference</span></span><br><span class="line">           print(<span class="string">&#x27;---------------------Start detecting----------------------&#x27;</span>)</span><br><span class="line">           pred = model(new_im, augment=augment, visualize=visualize)</span><br><span class="line">           t3 = time_sync()</span><br><span class="line">           dt[<span class="number">1</span>] += t3 - t2</span><br><span class="line"></span><br><span class="line">           <span class="comment"># NMS</span></span><br><span class="line">           <span class="comment"># pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)</span></span><br><span class="line">           pred = non_max_suppression(pred, <span class="number">0.2</span>, iou_thres, classes, agnostic_nms, max_det=max_det) <span class="comment"># make conf_thres smaller</span></span><br><span class="line">           print(pred[<span class="number">0</span>])</span><br><span class="line">           print(pred[<span class="number">0</span>].requires_grad, im.requires_grad)</span><br><span class="line">           <span class="keyword">try</span>:</span><br><span class="line">               grad = torch.autograd.grad(pred[<span class="number">0</span>][<span class="number">0</span>][<span class="number">4</span>], im)</span><br><span class="line">               <span class="comment"># print(grad)</span></span><br><span class="line">               print(grad[<span class="number">0</span>].<span class="built_in">max</span>(), grad[<span class="number">0</span>].<span class="built_in">min</span>())</span><br><span class="line">           <span class="keyword">except</span>:</span><br><span class="line">               <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">           dt[<span class="number">2</span>] += time_sync() - t3</span><br><span class="line">           <span class="keyword">if</span> <span class="built_in">len</span>(pred[<span class="number">0</span>].cpu().detach().numpy()) == <span class="number">0</span> <span class="keyword">and</span> check_patchsize(mask):</span><br><span class="line">               crackFlag = <span class="literal">True</span></span><br><span class="line">               <span class="comment"># save im</span></span><br><span class="line">               im = im.cpu().detach().numpy()</span><br><span class="line">               im = im[<span class="number">0</span>].transpose(<span class="number">1</span>,<span class="number">2</span>,<span class="number">0</span>)</span><br><span class="line">               im = im * <span class="number">255</span></span><br><span class="line">               im = im.astype(np.uint8)</span><br><span class="line">               im = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)</span><br><span class="line">               cv2.imwrite(<span class="string">&quot;adv_images/stop.png&quot;</span>, im)</span><br><span class="line">               print(<span class="string">&quot;[*]Cracked, now run fool_me.py&quot;</span>)</span><br><span class="line">               <span class="comment"># print(getflag())</span></span><br><span class="line">           <span class="keyword">elif</span> <span class="keyword">not</span> check_patchsize(mask):</span><br><span class="line">               print(<span class="string">&quot;[*]Too many adversarial pixels!&quot;</span>)</span><br><span class="line">           <span class="keyword">else</span>:</span><br><span class="line">               print(<span class="string">&quot;[*]Please remove the object!&quot;</span>)</span><br></pre></td></tr></table></figure>    </div></div><p>至此，通常来说题目应该已经被Mark为Solved了，但我们得到的输出并不这么认为：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">------------------Initializing detector-------------------</span><br><span class="line">Fusing layers... </span><br><span class="line">Model Summary: 367 layers, 46533693 parameters, 0 gradients</span><br><span class="line">libpng warning: iCCP: known incorrect sRGB profile</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.31950e+02, 1.63722e+02, 6.37381e+02, 5.43151e+02, 5.21886e-01, 1.10000e+01],</span><br><span class="line">        [4.71159e+02, 2.11284e+02, 6.39552e+02, 5.44185e+02, 4.40381e-01, 1.10000e+01],</span><br><span class="line">        [1.06963e+00, 2.29302e+02, 2.94475e+02, 5.46162e+02, 4.39895e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">C:\Users\Kyriota\AppData\Local\Programs\Python\Python39\lib\site-packages\torch\autograd\__init__.py:276: UserWarning: Error detected in struct torch::autograd::CopySlices. Traceback of forward call that caused the error:</span><br><span class="line">  File &quot;C:\Users\Kyriota\AppData\Local\Programs\Python\Python39\lib\runpy.py&quot;, line 197, in _run_module_as_main</span><br><span class="line">    return _run_code(code, main_globals, None,</span><br><span class="line">  File &quot;C:\Users\Kyriota\AppData\Local\Programs\Python\Python39\lib\runpy.py&quot;, line 87, in _run_code</span><br><span class="line">    exec(code, run_globals)</span><br><span class="line">  File &quot;c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\__main__.py&quot;, line 39, in &lt;module&gt;</span><br><span class="line">    cli.main()</span><br><span class="line">  File &quot;c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy&#x2F;..\debugpy\server\cli.py&quot;, line 430, in main</span><br><span class="line">    run()</span><br><span class="line">  File &quot;c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy&#x2F;..\debugpy\server\cli.py&quot;, line 284, in run_file        </span><br><span class="line">    runpy.run_path(target, run_name&#x3D;&quot;__main__&quot;)</span><br><span class="line">  File &quot;c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py&quot;, line 321, in run_path</span><br><span class="line">    return _run_module_code(code, init_globals, run_name,</span><br><span class="line">  File &quot;c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py&quot;, line 135, in _run_module_code</span><br><span class="line">    _run_code(code, mod_globals, init_globals,</span><br><span class="line">  File &quot;c:\Users\Kyriota\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py&quot;, line 124, in _run_code</span><br><span class="line">    exec(code, run_globals)</span><br><span class="line">  File &quot;C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\exp.py&quot;, line 181, in &lt;module&gt;</span><br><span class="line">    main(opt)</span><br><span class="line">  File &quot;C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\exp.py&quot;, line 176, in main</span><br><span class="line">    run(**vars(opt))</span><br><span class="line">  File &quot;C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\exp.py&quot;, line 117, in run</span><br><span class="line">    pred &#x3D; non_max_suppression(pred, 0.2, iou_thres, classes, agnostic_nms, max_det&#x3D;max_det) # make conf_thres smaller</span><br><span class="line">  File &quot;C:\Users\Kyriota\Desktop\CISCN\ciscn2023_AdvDetPatch\adv\utils\general.py&quot;, line 697, in non_max_suppression</span><br><span class="line">    x[:, 5:] *&#x3D; x[:, 4:5]  # conf &#x3D; obj_conf * cls_conf</span><br><span class="line"> (Triggered internally at  C:\actions-runner\_work\pytorch\pytorch\builder\windows\pytorch\torch\csrc\autograd\python_anomaly_mode.cpp:104.)</span><br><span class="line">  return Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass</span><br><span class="line">[*]Please remove the object!</span><br></pre></td></tr></table></figure><p>在这份报错中，我们关注的是<code>adv\utils\general.py</code>的报错</p><blockquote><p>x[:, 5:] <em>= x[:, 4:5] # conf = obj_conf </em> cls_conf</p></blockquote><p>见到了<code>*=</code>操作符以及<code>[]</code>直接对元素的操作，都表明了这是一个inplace操作，inplace操作是PyTorch为了节省内存，提高运行速度而直接在原始变量上进行修改和计算的一种操作方式，详情可以自行Google：<code>Inplace PyTorch</code>，这种操作方式有一个很大的问题就是无法进行AutoGrad自动求梯度，而此处我们恰好需要进行AutoGrad，所以我们需要将此处的Inplace操作修改为非Inplace的操作，具体需要修改的是<code>adv\utils\general.py</code>的<code>Line 696-739</code>，需要将原本的<code>x</code>进行<code>clone()</code>，在clone出的<code>new_x</code>上进行操作，并避免对<code>new_x</code>、<code>x</code>的任意一方进行inplace操作，<code>new_x[:, 5:] = x[:, 5:] * x[:, 4:5]</code>就顺利地避免了这件事</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Edited Code    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Compute conf</span></span><br><span class="line">      new_x = x.clone() <span class="comment"># If not clone, it will be an inplace operation</span></span><br><span class="line">      new_x[:, <span class="number">5</span>:] = x[:, <span class="number">5</span>:] * x[:, <span class="number">4</span>:<span class="number">5</span>]  <span class="comment"># conf = obj_conf * cls_conf</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># Box (center x, center y, width, height) to (x1, y1, x2, y2)</span></span><br><span class="line">      box = xywh2xyxy(new_x[:, :<span class="number">4</span>])</span><br><span class="line"></span><br><span class="line">      <span class="comment"># Detections matrix nx6 (xyxy, conf, cls)</span></span><br><span class="line">      <span class="keyword">if</span> multi_label:</span><br><span class="line">          i, j = (new_x[:, <span class="number">5</span>:] &gt; conf_thres).nonzero(as_tuple=<span class="literal">False</span>).T</span><br><span class="line">          xnew_x = torch.cat((box[i], xnew_x[i, j + <span class="number">5</span>, <span class="literal">None</span>], j[:, <span class="literal">None</span>].<span class="built_in">float</span>()), <span class="number">1</span>)</span><br><span class="line">      <span class="keyword">else</span>:  <span class="comment"># best class only</span></span><br><span class="line">          conf, j = new_x[:, <span class="number">5</span>:].<span class="built_in">max</span>(<span class="number">1</span>, keepdim=<span class="literal">True</span>)</span><br><span class="line">          new_x = torch.cat((box, conf, j.<span class="built_in">float</span>()), <span class="number">1</span>)[conf.view(-<span class="number">1</span>) &gt; conf_thres]</span><br><span class="line"></span><br><span class="line">      <span class="comment"># Filter by class</span></span><br><span class="line">      <span class="keyword">if</span> classes <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">          new_x = new_x[(new_x[:, <span class="number">5</span>:<span class="number">6</span>] == torch.tensor(classes, device=new_x.device)).<span class="built_in">any</span>(<span class="number">1</span>)]</span><br><span class="line"></span><br><span class="line">      <span class="comment"># Apply finite constraint</span></span><br><span class="line">      <span class="comment"># if not torch.isfinite(x).all():</span></span><br><span class="line">      <span class="comment">#     x = x[torch.isfinite(x).all(1)]</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># Check shape</span></span><br><span class="line">      n = new_x.shape[<span class="number">0</span>]  <span class="comment"># number of boxes</span></span><br><span class="line">      <span class="keyword">if</span> <span class="keyword">not</span> n:  <span class="comment"># no boxes</span></span><br><span class="line">          <span class="keyword">continue</span></span><br><span class="line">      <span class="keyword">elif</span> n &gt; max_nms:  <span class="comment"># excess boxes</span></span><br><span class="line">          new_x = new_x[new_x[:, <span class="number">4</span>].argsort(descending=<span class="literal">True</span>)[:max_nms]]  <span class="comment"># sort by confidence</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># Batched NMS</span></span><br><span class="line">      c = new_x[:, <span class="number">5</span>:<span class="number">6</span>] * (<span class="number">0</span> <span class="keyword">if</span> agnostic <span class="keyword">else</span> max_wh)  <span class="comment"># classes</span></span><br><span class="line">      boxes, scores = new_x[:, :<span class="number">4</span>] + c, new_x[:, <span class="number">4</span>]  <span class="comment"># boxes (offset by class), scores</span></span><br><span class="line">      i = torchvision.ops.nms(boxes, scores, iou_thres)  <span class="comment"># NMS</span></span><br><span class="line">      <span class="keyword">if</span> i.shape[<span class="number">0</span>] &gt; max_det:  <span class="comment"># limit detections</span></span><br><span class="line">          i = i[:max_det]</span><br><span class="line">      <span class="keyword">if</span> merge <span class="keyword">and</span> (<span class="number">1</span> &lt; n &lt; <span class="number">3E3</span>):  <span class="comment"># Merge NMS (boxes merged using weighted mean)</span></span><br><span class="line">          <span class="comment"># update boxes as boxes(i,4) = weights(i,n) * boxes(n,4)</span></span><br><span class="line">          iou = box_iou(boxes[i], boxes) &gt; iou_thres  <span class="comment"># iou matrix</span></span><br><span class="line">          weights = iou * scores[<span class="literal">None</span>]  <span class="comment"># box weights</span></span><br><span class="line">          new_x[i, :<span class="number">4</span>] = torch.mm(weights, new_x[:, :<span class="number">4</span>]).<span class="built_in">float</span>() / weights.<span class="built_in">sum</span>(<span class="number">1</span>, keepdim=<span class="literal">True</span>)  <span class="comment"># merged boxes</span></span><br><span class="line">          <span class="keyword">if</span> redundant:</span><br><span class="line">              i = i[iou.<span class="built_in">sum</span>(<span class="number">1</span>) &gt; <span class="number">1</span>]  <span class="comment"># require redundancy</span></span><br><span class="line"></span><br><span class="line">      output[xi] = new_x[i]</span><br></pre></td></tr></table></figure>    </div></div><p>在修改了<code>adv\utils\general.py</code>中的inplace操作后，我们的exp就可以正常运行啦</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Log    </div>    <div class='spoiler-content'>        <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br></pre></td><td class="code"><pre><span class="line">------------------Initializing detector-------------------</span><br><span class="line">Fusing layers... </span><br><span class="line">Model Summary: 367 layers, 46533693 parameters, 0 gradients</span><br><span class="line">libpng warning: iCCP: known incorrect sRGB profile</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.99440e+02, 1.65098e+02, 6.39046e+02, 5.46643e+02, 5.57359e-01, 1.10000e+01],</span><br><span class="line">        [9.87518e-01, 2.29487e+02, 2.95075e+02, 5.46320e+02, 4.48890e-01, 1.10000e+01],</span><br><span class="line">        [4.67540e+02, 2.11631e+02, 6.39664e+02, 5.44510e+02, 4.05359e-01, 1.10000e+01],</span><br><span class="line">        [2.26281e+02, 2.03809e+02, 3.72906e+02, 5.13177e+02, 2.52862e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02568) tensor(-0.02769)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.64544e+02, 2.11324e+02, 6.39780e+02, 5.44572e+02, 4.44466e-01, 1.10000e+01],</span><br><span class="line">        [2.97909e+02, 1.64988e+02, 6.39148e+02, 5.46905e+02, 4.07725e-01, 1.10000e+01],</span><br><span class="line">        [1.25335e+01, 2.16234e+02, 3.21994e+02, 5.42364e+02, 3.96188e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02159) tensor(-0.01982)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.97461e+02, 1.65155e+02, 6.39086e+02, 5.46568e+02, 4.47543e-01, 1.10000e+01],</span><br><span class="line">        [1.27674e+01, 2.16266e+02, 3.21484e+02, 5.42356e+02, 3.95687e-01, 1.10000e+01],</span><br><span class="line">        [4.66559e+02, 2.11551e+02, 6.39689e+02, 5.44489e+02, 3.58109e-01, 1.10000e+01],</span><br><span class="line">        [2.24259e+02, 2.03079e+02, 3.75759e+02, 5.14603e+02, 2.00155e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02328) tensor(-0.02582)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.29764e+02, 1.63141e+02, 6.36678e+02, 5.44628e+02, 3.93021e-01, 1.10000e+01],</span><br><span class="line">        [1.20015e+01, 2.09785e+02, 3.94054e+02, 5.39255e+02, 3.92304e-01, 1.10000e+01],</span><br><span class="line">        [4.63676e+02, 2.11226e+02, 6.39796e+02, 5.44551e+02, 3.89934e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.03840) tensor(-0.02972)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[5.46739e+00, 2.09377e+02, 4.15404e+02, 5.39420e+02, 4.52883e-01, 1.10000e+01],</span><br><span class="line">        [4.61235e+02, 2.10750e+02, 6.39857e+02, 5.44667e+02, 4.21564e-01, 1.10000e+01],</span><br><span class="line">        [3.17969e+02, 1.63581e+02, 5.48315e+02, 5.35918e+02, 2.31240e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01583) tensor(-0.01487)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.61415e+02, 2.10783e+02, 6.39854e+02, 5.44658e+02, 4.19019e-01, 1.10000e+01],</span><br><span class="line">        [6.71468e+00, 2.09764e+02, 4.11786e+02, 5.39314e+02, 4.15713e-01, 1.10000e+01],</span><br><span class="line">        [2.33962e+02, 1.62835e+02, 6.32862e+02, 5.44387e+02, 2.50291e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02130) tensor(-0.02102)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.74260e+00, 2.09793e+02, 4.12019e+02, 5.39302e+02, 4.13334e-01, 1.10000e+01],</span><br><span class="line">        [3.02754e+02, 1.65323e+02, 6.38845e+02, 5.46732e+02, 3.41682e-01, 1.10000e+01],</span><br><span class="line">        [4.70861e+02, 2.11502e+02, 6.39273e+02, 5.45185e+02, 2.92887e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01749) tensor(-0.01496)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[9.69278e+00, 2.10208e+02, 4.03334e+02, 5.39157e+02, 3.75298e-01, 1.10000e+01],</span><br><span class="line">        [3.01532e+02, 1.65306e+02, 6.38896e+02, 5.46712e+02, 3.43980e-01, 1.10000e+01],</span><br><span class="line">        [4.70989e+02, 2.11515e+02, 6.39274e+02, 5.45182e+02, 2.92170e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01465) tensor(-0.01859)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.28244e+02, 1.63378e+02, 6.38103e+02, 5.44877e+02, 3.58895e-01, 1.10000e+01],</span><br><span class="line">        [8.05008e-01, 2.31701e+02, 2.91779e+02, 5.46677e+02, 3.49028e-01, 1.10000e+01],</span><br><span class="line">        [4.64350e+02, 2.11181e+02, 6.39744e+02, 5.44553e+02, 3.31244e-01, 1.10000e+01],</span><br><span class="line">        [2.21834e+02, 2.02153e+02, 3.78507e+02, 5.16345e+02, 2.02959e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.03296) tensor(-0.02410)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.93478e+00, 2.10221e+02, 4.13514e+02, 5.39214e+02, 3.81639e-01, 1.10000e+01],</span><br><span class="line">        [4.61900e+02, 2.10729e+02, 6.39779e+02, 5.44686e+02, 3.52377e-01, 1.10000e+01],</span><br><span class="line">        [2.35689e+02, 1.62994e+02, 6.33016e+02, 5.44957e+02, 2.14085e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01133) tensor(-0.01336)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.62111e+02, 2.10766e+02, 6.39778e+02, 5.44674e+02, 3.50585e-01, 1.10000e+01],</span><br><span class="line">        [9.42287e+00, 2.10610e+02, 4.06254e+02, 5.39079e+02, 3.47936e-01, 1.10000e+01],</span><br><span class="line">        [2.33012e+02, 1.63025e+02, 6.34475e+02, 5.44863e+02, 2.41192e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02165) tensor(-0.01778)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[3.04119e+02, 1.65405e+02, 6.38794e+02, 5.46639e+02, 3.50666e-01, 1.10000e+01],</span><br><span class="line">        [9.52650e+00, 2.10631e+02, 4.06190e+02, 5.39065e+02, 3.46781e-01, 1.10000e+01],</span><br><span class="line">        [4.71491e+02, 2.11652e+02, 6.39239e+02, 5.45020e+02, 2.50843e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02018) tensor(-0.01902)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.92395e+00, 2.10629e+02, 4.16032e+02, 5.39267e+02, 3.49018e-01, 1.10000e+01],</span><br><span class="line">        [4.61676e+02, 2.10669e+02, 6.39771e+02, 5.44667e+02, 3.10069e-01, 1.10000e+01],</span><br><span class="line">        [2.31911e+02, 1.63185e+02, 6.34352e+02, 5.45076e+02, 2.31361e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01170) tensor(-0.01174)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[9.05428e+00, 2.11022e+02, 4.08999e+02, 5.39135e+02, 3.22089e-01, 1.10000e+01],</span><br><span class="line">        [4.61794e+02, 2.10688e+02, 6.39771e+02, 5.44658e+02, 3.09146e-01, 1.10000e+01],</span><br><span class="line">        [2.30330e+02, 1.63200e+02, 6.35278e+02, 5.44961e+02, 2.54768e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01150) tensor(-0.01293)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.21933e-01, 2.33057e+02, 2.87871e+02, 5.46809e+02, 3.21319e-01, 1.10000e+01],</span><br><span class="line">        [4.61861e+02, 2.10696e+02, 6.39771e+02, 5.44652e+02, 3.08678e-01, 1.10000e+01],</span><br><span class="line">        [2.29001e+02, 1.63199e+02, 6.36153e+02, 5.44797e+02, 2.82797e-01, 1.10000e+01],</span><br><span class="line">        [2.19337e+02, 2.01094e+02, 3.80560e+02, 5.18359e+02, 2.04086e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.03610) tensor(-0.02654)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[5.68347e+00, 2.10769e+02, 4.25982e+02, 5.39259e+02, 3.29447e-01, 1.10000e+01],</span><br><span class="line">        [4.62003e+02, 2.10736e+02, 6.39775e+02, 5.44642e+02, 3.09927e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00983) tensor(-0.01211)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.62217e+02, 2.10771e+02, 6.39774e+02, 5.44629e+02, 3.09151e-01, 1.10000e+01],</span><br><span class="line">        [6.18793e+00, 2.11071e+02, 4.24154e+02, 5.39151e+02, 3.06230e-01, 1.10000e+01],</span><br><span class="line">        [2.32386e+02, 1.63424e+02, 6.33786e+02, 5.45476e+02, 2.04663e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02187) tensor(-0.01586)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.22729e+00, 2.11085e+02, 4.24123e+02, 5.39140e+02, 3.06370e-01, 1.10000e+01],</span><br><span class="line">        [3.05818e+02, 1.65497e+02, 6.38405e+02, 5.46541e+02, 2.79065e-01, 1.10000e+01],</span><br><span class="line">        [4.71523e+02, 2.11698e+02, 6.39244e+02, 5.44901e+02, 2.21405e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01001) tensor(-0.01309)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.87506e+00, 2.11398e+02, 4.21776e+02, 5.39026e+02, 2.85813e-01, 1.10000e+01],</span><br><span class="line">        [3.05309e+02, 1.65486e+02, 6.38404e+02, 5.46548e+02, 2.75543e-01, 1.10000e+01],</span><br><span class="line">        [4.71635e+02, 2.11703e+02, 6.39247e+02, 5.44903e+02, 2.21985e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00883) tensor(-0.01158)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[3.04795e+02, 1.65479e+02, 6.38402e+02, 5.46556e+02, 2.72046e-01, 1.10000e+01],</span><br><span class="line">        [7.72601e+00, 2.11700e+02, 4.18948e+02, 5.38914e+02, 2.68070e-01, 1.10000e+01],</span><br><span class="line">        [4.71737e+02, 2.11707e+02, 6.39250e+02, 5.44905e+02, 2.22759e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02012) tensor(-0.01559)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.63252e+02, 2.10883e+02, 6.39755e+02, 5.44547e+02, 2.69398e-01, 1.10000e+01],</span><br><span class="line">        [6.81540e+00, 2.11608e+02, 4.23172e+02, 5.39060e+02, 2.64795e-01, 1.10000e+01],</span><br><span class="line">        [2.27875e+02, 1.63346e+02, 6.36536e+02, 5.45941e+02, 2.24023e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02240) tensor(-0.01346)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.87582e+00, 2.11620e+02, 4.22965e+02, 5.39048e+02, 2.65191e-01, 1.10000e+01],</span><br><span class="line">        [3.04492e+02, 1.65518e+02, 6.38154e+02, 5.46432e+02, 2.46447e-01, 1.10000e+01],</span><br><span class="line">        [4.66261e+02, 2.11266e+02, 6.39693e+02, 5.44395e+02, 2.22511e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00883) tensor(-0.00965)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.27861e+02, 1.63791e+02, 6.36813e+02, 5.46048e+02, 2.51087e-01, 1.10000e+01],</span><br><span class="line">        [7.54276e+00, 2.11909e+02, 4.20730e+02, 5.38950e+02, 2.49378e-01, 1.10000e+01],</span><br><span class="line">        [4.66363e+02, 2.11283e+02, 6.39697e+02, 5.44387e+02, 2.23032e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02562) tensor(-0.01938)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[5.88934e+00, 2.11406e+02, 4.31660e+02, 5.39174e+02, 2.55222e-01, 1.10000e+01],</span><br><span class="line">        [4.41733e+02, 1.98434e+02, 6.40785e+02, 5.46315e+02, 2.47780e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01241) tensor(-0.01126)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.42144e+02, 1.98801e+02, 6.40774e+02, 5.46288e+02, 2.44499e-01, 1.10000e+01],</span><br><span class="line">        [6.19185e+00, 2.11646e+02, 4.30635e+02, 5.39053e+02, 2.36687e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01627) tensor(-0.01399)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[3.05759e+02, 1.65554e+02, 6.38039e+02, 5.46447e+02, 2.41751e-01, 1.10000e+01],</span><br><span class="line">        [6.22633e+00, 2.11659e+02, 4.30684e+02, 5.39050e+02, 2.34810e-01, 1.10000e+01],</span><br><span class="line">        [4.68911e+02, 2.11582e+02, 6.39671e+02, 5.44300e+02, 2.08955e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01791) tensor(-0.01338)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.11099e+00, 2.11577e+02, 4.32366e+02, 5.39178e+02, 2.29949e-01, 1.10000e+01],</span><br><span class="line">        [4.67445e+02, 2.11376e+02, 6.39696e+02, 5.44351e+02, 2.16738e-01, 1.10000e+01],</span><br><span class="line">        [3.06311e+02, 1.65559e+02, 6.37805e+02, 5.46486e+02, 2.09929e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00953) tensor(-0.01247)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.67489e+02, 2.11380e+02, 6.39702e+02, 5.44346e+02, 2.17577e-01, 1.10000e+01],</span><br><span class="line">        [6.39536e+00, 2.11776e+02, 4.31496e+02, 5.39074e+02, 2.14570e-01, 1.10000e+01],</span><br><span class="line">        [3.06059e+02, 1.65553e+02, 6.37798e+02, 5.46512e+02, 2.05941e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01595) tensor(-0.01078)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.40094e+00, 2.11787e+02, 4.31503e+02, 5.39063e+02, 2.15568e-01, 1.10000e+01],</span><br><span class="line">        [3.05005e+02, 1.65564e+02, 6.37797e+02, 5.46392e+02, 2.13278e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00856) tensor(-0.01144)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[5.03036e-01, 2.35432e+02, 2.68015e+02, 5.47268e+02, 2.10955e-01, 1.10000e+01],</span><br><span class="line">        [3.04768e+02, 1.65558e+02, 6.37798e+02, 5.46419e+02, 2.08983e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01639) tensor(-0.01730)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.37762e+01, 1.74297e+02, 6.26394e+02, 5.36317e+02, 2.15803e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.02076) tensor(-0.01633)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[3.05007e+02, 1.65532e+02, 6.37837e+02, 5.46464e+02, 2.16731e-01, 1.10000e+01],</span><br><span class="line">        [6.43428e+00, 2.11935e+02, 4.29547e+02, 5.39050e+02, 2.12161e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01565) tensor(-0.01634)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.28447e+00, 2.11858e+02, 4.31250e+02, 5.39170e+02, 2.08235e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00794) tensor(-0.01184)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.56436e-01, 2.35380e+02, 2.68908e+02, 5.47319e+02, 2.03724e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01649) tensor(-0.01712)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[2.37818e+01, 1.73652e+02, 6.25408e+02, 5.35924e+02, 2.07998e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01791) tensor(-0.01575)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[6.22389e+00, 2.11991e+02, 4.29703e+02, 5.39186e+02, 2.05615e-01, 1.10000e+01],</span><br><span class="line">        [4.66568e+02, 2.11206e+02, 6.39723e+02, 5.44350e+02, 2.01893e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.00740) tensor(-0.01135)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.66580e+02, 2.11211e+02, 6.39728e+02, 5.44346e+02, 2.02418e-01, 1.10000e+01],</span><br><span class="line">        [4.12628e-01, 2.35314e+02, 2.70160e+02, 5.47328e+02, 2.00482e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01451) tensor(-0.01116)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([[4.10477e-01, 2.35314e+02, 2.70265e+02, 5.47322e+02, 2.01308e-01, 1.10000e+01]], grad_fn&#x3D;&lt;IndexBackward0&gt;)</span><br><span class="line">True True</span><br><span class="line">tensor(0.01699) tensor(-0.01811)</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Please remove the object!</span><br><span class="line">---------------------Start detecting----------------------</span><br><span class="line">tensor([], size&#x3D;(0, 6))</span><br><span class="line">False True</span><br><span class="line">Mask sum is: tensor(53862.)</span><br><span class="line">[*]Cracked, now run fool_me.py</span><br></pre></td></tr></table></figure>    </div></div><p>至此，本题被Mark为Solved</p><p>下面的两张图分别为Patch以及被攻击后的图像的可视化检测结果</p><p><img src="/images/CISCN_2023_patch.png" /></p><p><img src="/images/CISCN_2023_Yolo_Patched_Example.png" /></p><p>可以看见被攻击后的图像中检测到的物体置信度都在0.2以下，已经满足题目要求</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前一起打了CISCN 2023初赛，因为周末睡懒觉晚到了两三个小时导致做这个题的时间不充裕了，最后在比赛结束后半个小时出了，主要原因还是因为没有玩过Yolo，当时记得这个题的解题数是很低的（或者好像是零解的？反正截止至我开始写这篇WP的时间[2023-08-07 18:30]，我在网上并没有搜到WP），本题在选手机器学习底子坚实的前提下主要考验选手入手新的模型和领域的速度以及将对抗样本训练应用上来的速度。&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>攒机小技巧</title>
    <link href="https://kyriota.github.io/2023/01/16/%E6%94%92%E6%9C%BA%E5%B0%8F%E6%8A%80%E5%B7%A7/"/>
    <id>https://kyriota.github.io/2023/01/16/%E6%94%92%E6%9C%BA%E5%B0%8F%E6%8A%80%E5%B7%A7/</id>
    <published>2023-01-16T17:45:00.000Z</published>
    <updated>2023-01-16T16:08:55.044Z</updated>
    
    <content type="html"><![CDATA[<p>迫于目前自用笔电GPU性能过于不足，连一些基础的DL模型都放不下或者跑的太慢，而最近刚好Nvidia和AMD都在发布新GPU，所以准备高度定制化地配一台机子</p><p>本文具有较强的时效性，部分内容可能不适用与未来的硬件市场</p><span id="more"></span><h1 id="攒机小技巧">攒机小技巧</h1><p>首先明确一点，<strong>按需购买</strong>，先明确自己的用途会跑满不同硬件的百分之多少，然后在保证不出现木桶效应的情况下把需求不高的硬件水平尽量拉低</p><h2 id="显卡">显卡</h2><p>对于目前的硬件市场，显卡是水最深的一块，推荐在攒机中优先确定显卡，具体到某一款的型号，如：七彩虹4090火神</p><p>虽然40系已经发布，但碍于老黄30系库存没清完，苏妈7900xt表现欠佳，显卡价格居高不下，等等党在矿潮时期待的40系发布后显卡价格迎来断崖式下跌的愿景并未完全达成，且根据目前的市场形势，4070ti与4080虽然定价合理性欠缺，但仍会成为很多“默认30全矿”以及刚需的选择，40系的降价条件主要还是在于30系的库存积压情况，而不是市场供需关系（毕竟这就是个垄断的行业，Nvidia百分之八九十的市场占有率，根本没有什么供需问题），所以在短时间内既然无法改变这些现状，那我还是推荐现在<em>早买早享受</em>，虽然购买后显卡会持续缓慢掉价，但这也正是电子产品本身因为摩尔定律不可避免的属性；而<em>晚买享折扣</em>的事实是：虽然确实享受到了晚买的折扣，但这期间你的GPU需求如何解决，这段等待的时间是否对你造成了实质性的影响，你是否确定在未来的某一个时间点显卡价格会达到你现在的预期，都是需要考虑进来的隐性成本，见仁见智吧</p><p><strong>重要硬件参数</strong></p><ul><li>显存大小：在游戏中主要决定了能上多细节的材质，在DL中决定了能放的模型规模与<code>batchSize</code></li><li>显卡架构：不同的架构对于各种运算的速度是不同的，通常来说越新的显卡架构优化越好速度越快，能耗比也好</li><li>显存位宽：影响显卡与其他硬件的交互速度，游戏中体现在会影响可以使用的分辨率上限，比如这次的4070ti（or 4080 12G）位宽192bit太低，导致4K高刷新率在这张显卡上几乎成为不可能的事情，在DL中影响了数据写入显存的速度，低的位宽下训练速度更慢</li><li>显存频率：每秒交互次数，性能不够的时候进行显卡超频就是指超显存频率，显存的默认频率在同一GPU的不同型号下略有不同，但通常不构成致命差距，所以这一指标通常不会被重点吐槽</li></ul><p><strong>非硬件指标的关注点</strong></p><ul><li>显卡尺寸：随着显卡迭代，尺寸越做越大，看好显卡尺寸以防止装不进机箱或者与CPU风冷散热器鳍片冲突，ITX或MATX机箱尤其需要考虑这一点</li><li>显卡品牌：尽量选择支持<strong>个人送保</strong>的显卡品牌，尤其是在非官方的淘宝店购买显卡时。对于目前最便宜的万丽的显卡，虽然具有超高性价比，但是国内不支持个人送保，仅海外支持，所以对于这个品牌的选择就见仁见智了，但就我个人而言不推荐</li><li>是否吊装：显卡吊装是指：将显卡IO朝上，竖直吊装（IO朝下叫竖装不叫吊装）。部分立式ITX机箱或一些有个性的ATX机箱会做这样的操作，吊装最大的问题就是会导致热管中的冷却液回流时需要额外克服重力，虽然逆重力热管不是非常昂贵的技术，但市面上的显卡中还是有相当多的显卡在吊装后温度直接飙升10℃，更可恶的是几乎找不到关于“主流型号显卡吊装对温度影响”的全面评测，产品说明中也不会对吊装做单独说明，能确定下来吊装无影响的都是少数，所以在选择吊装时要么找官方商家问清楚，要么刚好找到对应型号的吊装测试结果，否则全靠自己摸奖</li></ul><p><strong>40系的注意事项</strong></p><ul><li>4080和4090用的是同等规格的散热，90都散热冗余了，80更是压得死死的，如果是4080或者4090则完全不需要担心显卡的散热</li><li>4080和4090超频能力都较低，小超就会功耗飙升</li></ul><p><strong>30系的注意事项</strong></p><ul><li><p>30系默认全矿是非常激进的观点，目前我知道的是3060ti有几款型号发布日期较晚，基本可以确定无矿，3090ti发布日期也较晚，基本无矿（但由于已经停产，同时是上代旗舰，除了显存优势以外性价比极低），研究一下应该还是可以找到一些无矿型号，目前最主流的显卡仍然是GTX 1650，RTX 3060的占比也在快速上升，所以目前购入3060ti的无矿型号的话，几乎可以吊打市面上所有游戏，也因为在未来会成为下一代主流显卡，各大游戏厂商将会以3060ti左右的标准去优化游戏，同时，3060ti还是目前所有N卡中性价比最高的型号，我实在想不出在3060ti性能够用且无矿的情况下购买更高性能显卡的理由</p></li><li><p>3090ti在40系发布之前是炼丹师的好朋友，但请看如下参数</p><table><thead><tr class="header"><th>型号</th><th>目前最低价格</th><th>显存</th><th>位宽</th><th>CUDA</th></tr></thead><tbody><tr class="odd"><td>4080</td><td>8299</td><td>16G</td><td>256bit</td><td>9728</td></tr><tr class="even"><td>3090ti</td><td>10499</td><td>24G</td><td>384bit</td><td>10752</td></tr></tbody></table><p>我推荐如下解决方案：</p><p>4080在大多数基本的炼丹上都已经可以handle，高级大规模炼丹建议直接上云</p><p>3090ti通常双卡走NV Link，30系相比起40系而言，利用其高速NV Link加上大显存也是一种解决方案，但就目前极度缺货以及性价比的问题，3090ti很可能在不久后从市场上基本消失</p></li></ul><p><strong>A卡注意事项</strong></p><ul><li>AMD不适合炼丹，没有CUDA</li><li>AMD本次旗舰7900XTX以及7900XT的性能和价格都让人😅</li></ul><h2 id="cpu">CPU</h2><p>目前CPU的红蓝之争还算激烈，所以红蓝两家CPU都可以选择，就普通用户而言，目前的CPU性能就算是打游戏也是过剩40%~60%的，但为了匹配显卡的计算能力，也不能上过于低端的U，否则CPU烤肉和CPU瓶颈都不是梦</p><p>目前对于蓝方阵营，13600KF搭配4080或4070ti（or 4080 12G），13700KF搭配4090是比较主流的选择；红方我没有太做了解，但对于老款型号，红方阵营的综合性价比确实非常高，可以考虑自行了解</p><p>对CPU需求高的工作，我只听说过视频剪辑；对CPU单核性能要求高的，我还听说过Minecraft服务器。所以了解清楚你的CPU Usage，在主流的搭配上选择性升级</p><p>CPU不像显卡那么需要纠结，认准需要的型号就可以了</p><p><strong>CPU后缀含义</strong></p><ul><li>K后缀：可超频不锁频</li><li>F后缀：不带核显，需独立显卡</li><li>T或S后缀：节能版</li></ul><p><strong>盒装与散片</strong></p><ul><li>盒装有保修，散片最多店保</li><li>盒装更贵</li><li>通常intel的U买散片就可以了，翻车率极低，精神洁癖者愿意多掏几百块就买盒装</li></ul><h2 id="主板">主板</h2><p>主板大小主要有ATX，MATX，ITX版型，通常一代U对应一代板，所以会有很多板U组合在售</p><p><strong>注意事项</strong></p><ul><li>对于Intel，大多数12代板可以支持13代U，但需要刷BIOS，否则无法点亮，所以个人装机还是建议哪一代U就套哪一代板，会比较稳妥</li><li>有的主板型号或整个系列都无法对CPU或内存进行超频，所以了解清楚后，如果不能超频，那就尽量选择不带K的U，还可以选择超频空间不高的内存</li><li>通常带WIFI模块的主板会比同型号不带WIFI的主板贵几十块，如果有无线需求还是建议直接加这几十块</li><li>关注主板风扇接口数量，如果机箱风扇太多则需要额外购买集线器</li></ul><h2 id="内存">内存</h2><p>现在内存价格无论是DDR4还是DDR5都比较便宜，根据以下几点选出合适的即可</p><p><strong>注意事项</strong></p><ul><li><p>DDR4还是DDR5主要看主板的要求，不能DDR5主板上DDR4内存</p></li><li><p>对于内存的品质，品牌是一方面，但更重要的是内存颗粒的品质，这个可以去自行了解</p></li><li><p>内存与主板、CPU密切挂钩，因为主板与CPU都会多少影响到内存能上到的频率，举点例子</p><ul><li>CPU最大支持2666MHz，主板最高支持3200MHz内存且<strong>不</strong>支持内存超频，内存为3200MHz，由于CPU最高只到2666MHz，所以内存也只能跑到这个频率</li><li>CPU最大支持2666MHz，主板最高支持3200MHz内存且支持内存超频，内存为3200MHz，虽然CPU最高只到2666MHz，但是主板支持内存超频，所以内存可以跑到3200MHz</li></ul></li><li><p>建议购买套条，因为体质相似，稳定性好且超频性能更接近，不会出现木桶效应</p></li><li><p>建议组成双通道，即8G*2套条而不是16G单条，可以获得更大的带宽</p></li><li><p>插内存的时候，如果主板有4个插槽且为双通道，需要插13槽或者24槽（推荐24槽），如果插12槽或者34槽则只使用了单通道</p></li></ul><h2 id="硬盘">硬盘</h2><p>现在固态的价格非常实惠，绝大多数人已经不会在PC中配机械硬盘了，建议还是买大厂的中高端货，毕竟数据无价，更何况一块优秀的固态可以极大提升电脑的使用体验，所以就不要太抠了</p><h2 id="散热">散热</h2><p>CPU散热分为风冷和水冷，水冷又分为一体水和分体水</p><p><strong>注意事项</strong></p><ul><li>同等价位来说，通常风冷的散热效果优于水冷，但是水冷的散热上限更高</li><li>水冷直接将CPU热量通过冷却液带到冷排，再从冷排直接吹出机箱外，而风冷只是将CPU热量从CPU转移到机箱内的空气中，所以如果机箱通风效果不好，建议直接水冷，或者上几把机箱风扇辅助散热</li><li>机箱风道的构建通常遵循前进后出，下进上出的原则，进风大于出风称为正压，反之称为负压</li><li>水冷冷排通常规格越大散热效果越好，但选择冷排时需要考虑机箱兼容性问题</li></ul><h2 id="电源">电源</h2><p>根据硬件功耗选择合适瓦数的电源即可，尽量选择一线品牌（这你也敢省？</p><p>电源金银铜牌就是帮你省电费，不能完全代表电源品质</p><p>模组电源中，电源和线材完全分离；半模组电源中，部分常用线材与电源为一体，其他线材完全分离；非模组电源中，所有线材都与电源一体，用不到的线材只能让他吊着</p><p>电源根据大小分为ATX，SFX-L，SFX电源，通常使用最小的SFX电源与ITX主板搭配构建ITX PC</p><h2 id="机箱">机箱</h2><p>机箱根据兼容的主板类型不同，分为ATX，MATX，ITX机箱，根据支持的风冷散热器高度不同，分为全塔、中塔机箱</p><p>机箱是门大学问，因为机箱的设计影响了风道的设计，也决定了你能塞下多大的硬件，在考虑本身美观性的同时，也应该考虑其兼容性以及散热表现等实用性问题</p><ul><li><p>对于ITX机箱，我最推荐的是曾今被封神的meshlicious，来自SSUPD，这是一款A4机箱，意思是显卡与主板背靠背的设计模式，这款机箱的致敬产品很多，但说实话还是这款机箱本身最值得青睐：超长显卡支持，显卡竖装，支持240冷排，合理的走管空间，全方位网板通风，可以说是把ITX的限制去除到极限了，售价也仅七八百元左右，非常香</p><p>在A4机箱中还会经常用到PCIe延长线，分为PCIe3.0延长线和PCIe4.0延长线，高端显卡需要上4.0的延长线，否则可能会被延长线卡带宽</p><p>但注意ITX机箱意味着通常需要上ITX主板与SFX小电源，这意味着需要花更高的价钱去买这些小尺寸的东西；同时，ITX对显卡的尺寸也有较大限制，有的只能上迷你卡甚至不能上独显</p></li><li><p>至于ATX机箱，只要避开了显卡吊装陷阱，其实可以随便选顺眼的</p></li></ul><p>但不管是什么机箱，都要注意现在流行的海景房玻璃侧板有较大可能对散热造成影响，如果追求散热，尽量选择带开孔的玻璃侧板或者直接换成开孔的铝钢侧板</p><h2 id="其他">其他</h2><ul><li>白色物品通常有白色税，即同样型号的白色款价格更贵</li><li>更详细的硬件知识可以参考b站<span class="citation" data-cites="硬件茶谈">[@硬件茶谈]</span>(https://space.bilibili.com/14871346)的系列视频，建议要自己攒机的朋友都先看一下，再去规划购买计划<ul><li><a href="https://www.bilibili.com/video/BV1Ca4y1W73e">风冷水冷原理</a></li><li><a href="https://www.bilibili.com/video/BV1UJ411B7mD">内存双通道原理</a></li><li><a href="https://www.bilibili.com/video/BV12J411G7Tj">内存频率由谁决定</a></li><li><a href="https://www.bilibili.com/video/BV1BG4y137mG">长达一小时的装机教程</a></li><li>......</li></ul></li></ul><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">按需购买，不买立省100%</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>Selective Repeat ARQ Simulation</title>
    <link href="https://kyriota.github.io/2023/01/03/SelectiveRepeatARQ-Simulation/"/>
    <id>https://kyriota.github.io/2023/01/03/SelectiveRepeatARQ-Simulation/</id>
    <published>2023-01-04T00:25:00.000Z</published>
    <updated>2023-11-24T01:13:26.032Z</updated>
    
    <content type="html"><![CDATA[<p>My homework in a course about computer network, coded in javascript in order to put it on my blog and give it a HTML UI to show the detailed process.</p><p>Just a simple version of selective repeat ARQ, but maybe helpful for you to understand the algorithm further.</p><p><img src='/images/ARQ_preview.png' style="zoom:40%;" ></p><span id="more"></span><!--toc--><h1 id="selective-repeat-arq-simulation">Selective Repeat ARQ Simulation</h1><p>First of all, you may wanna check the final result of the simulation: <a href="https://kyriota.com/html/ARQ">Portal to the final result</a></p><h2 id="theory">Theory</h2><p>To understand how selective repeat ARQ algorithm was implemented in my program, I recommend you understand the algorithm itself first, so that the following content can be much easier to understand.</p><blockquote><p>I'll show this algorithm in the following example of two host communicating</p></blockquote><p><code>Host A</code> and <code>Host B</code> are connected with each other, and <code>A</code> has some data that needs to send to <code>B</code></p><h3 id="framing">Framing</h3><ul><li>Because the data is too long to be sent at one time (just assume so), <code>A</code> slice the data into several pieces, and give those data sequence numbers in order to tell the proper order to combine the data, in case that the datum are not received in original order</li><li>Because the message could be corrupted when transferring, the message should contain some verification code related to the message content like CRC (Cyclic Redundancy Check)</li><li>Because <code>B</code> only have information in binary, <code>B</code> needs to know where is the start of the message and so does the end. There should be flags wrapping the whole message and the flag never appear in the middle of the message</li><li>Because <code>B</code> would like to know the sender of the message in order to do some further communication, the sender information should be contained in the message</li><li>Because <code>B</code> maybe not the receiver of the message but only the router to transmit the message, the true receiver information should be contained in the message</li></ul><p>So far, the message should be something like this:</p><center><code>| flag | destination | source | sequence | data | CRC | flag |</code></center><p>After such process, the message should be called as <em>frame</em>, which is a unit of communication in data link layer.</p><blockquote><p>As for how to make flags only appear in the start and the end of a frame:</p><p>In my case, flag is 01111110 that occupies one byte of the frame. To deal with such type of flag, since there are six successive '1's in the flag, we can append a '0' every time the content of the frame appears five successive '1's. And in the decoding part, when five successive '1's was found, remove the '0' behind them, so makes the flag only appears in the start and the end of a frame</p></blockquote><h3 id="window-control-frame">Window &amp; Control Frame</h3><p>But the sequence number could increase to infinite if <code>A</code> keeps sending messages, so sequence number should be limited in a specific range (usually in the range of 2^n, which makes best use of all bits).</p><p>However, when sequence numbers are in a specific range, imagine such situation:</p><ul><li><p><code>A</code> send a frame with sequence <code>X</code> to <code>B</code></p></li><li><p>frame corrupted when transmitting</p></li><li><p><code>B</code> found it corrupted by doing CRC and dropped it</p></li><li><p><code>A</code> keeps sending following frames, and comes with another frame with sequence <code>X</code></p></li><li><p>Under such situation, <code>B</code> could take this frame and think it the frame that <code>B</code> found corrupted</p></li></ul><p>In order to make the algorithm work properly, control frame and window are introduced. Here's how these things make <code>A</code> stop sending frames when frames are corrupted:</p><ul><li><p>With window and ACK (Acknowledge character) frame implemented, pretend sequence number takes 3 bits, and windows size is 3. The following text indicates sequence of sender(L) and receiver(R), contents in brackets indicates frames that are in current window:</p><ul><li><p>At the beginning:</p><center><p><code>&#123; |0|1|2| &#125; |3|4|5|6|7|0|1|2|3|... =&gt; &#123; |?|?|?| &#125;</code></p></center></li><li><p><code>A</code> sends frame 0 and starts a timer for frame 0</p></li><li><p><code>B</code> received it properly</p><center><p><code>&#123; |0|1|2| &#125; |3|4|5|6|7|0|1|2|3|... =&gt; &#123; |0|?|?| &#125;</code></p></center></li><li><p>Because frame 0 is in the correct order, <code>B</code> transmits frame 0 to upper layer to do further process and removes frame 0 from its buffer (in order to make the process clearer, the frame 0 is still texted in the buffer), at the same time, <code>B</code> shifts its window forward and sends ACK frame 0 to <code>A</code></p><center><p><code>&#123; |0|1|2| &#125; |3|4|5|6|7|0|1|2|3|... =&gt; |0| &#123; |?|?|?| &#125;</code></p></center></li><li><p><code>A</code> received ACK frame 0, so <code>A</code> removes frame 0 from its buffer, stops timer for frame 0, and shifts the window forward</p><center><p><code>|0| &#123; |1|2|3| &#125; |4|5|6|7|0|1|2|3|... =&gt; |0| &#123; |?|?|?| &#125;</code></p></center></li><li><p><code>A</code> sends frame 1 and starts a timer for frame 1</p></li><li><p>frame 1 corrupted when transmitting</p></li><li><p><code>B</code> received corrupted frame and drops it</p></li><li><p><code>A</code> sent frame 2, frame3, and <code>B</code> received them properly and sent ACK frame 2, 3 to <code>A</code>. But due to the first element in the window is not received, <code>B</code> could not shift the window</p><center><p><code>|0| &#123; |1|2|3| &#125; |4|5|6|7|0|1|2|3|... =&gt; |0| &#123; |?|2|3| &#125;</code></p></center></li><li><p>Also, because the first element in the window didn't receive an ACK frame, <code>A</code> cannot move the window neither. Because <code>A</code> finished sending mission in the window, <code>A</code> stops sending the next frame, which is frame 4, and waits for <code>B</code> to send ACK frame 1</p></li><li><p>Timer for frame 1 found frame 1 overtime, so <code>A</code> sends frame 1 again</p></li><li><p><code>B</code> received it properly</p><center><p><code>|0| &#123; |1|2|3| &#125; |4|5|6|7|0|1|2|3|... =&gt; |0| &#123; |1|2|3| &#125;</code></p></center></li><li><p><code>B</code> shifts the window, transmits frame 1, 2, 3 to upper layer, and sends ACK frame 1 to <code>A</code></p><center><p><code>|0| &#123; |1|2|3| &#125; |4|5|6|7|0|1|2|3|... =&gt; |0|1|2|3| &#123; |?|?|?| &#125;</code></p></center></li><li><p><code>A</code> removes frame 1, 2, 3 from buffer and shifts the window</p><center><p><code>|0|1|2|3| &#123; |4|5|6| &#125; |7|0|1|2|3|... =&gt; |0|1|2|3| &#123; |?|?|?| &#125;</code></p></center></li></ul></li><li><p>Under such implementation, there is nearly no problems for <code>A</code> and <code>B</code> can communicate with each other. But the overtime machinic seems too time consuming if ever time a frame is corrupted <code>A</code> will wait for a certain period. So we can make <code>B</code> send a NAK (Negative Acknowledgment) frame to make <code>A</code> resend faster (if <code>B</code> can recognize the sender and sequence number of the corrupted frame)</p></li></ul><p>We call ACK frames and NAK frames as control frames, and normal frames that sending data are called data frames</p><p>I think you may have a brief knowledge to selective repeat ARQ after my explanation, now I propose following principles to enhance your knowledge:</p><ul><li>Sender will not shift window until the first frames in the window receives ACK frame</li><li>Receiver will not transmit data to upper layer nor shift the window until the frames in the window are received in the right order</li><li>Control frames are dropped once confirmed</li><li>Every frame has its timer, they are started once the frame is sent or resent</li></ul><h3 id="details">Details</h3><ul><li><p>Notice that the control frames should be sent with the <strong>highest priority</strong>. Imaging the frame sending buffer has a lot of data frames queuing to be sent. If let the control frames queue behind them, they will never get sent due to the window limit.</p></li><li><p>In order to tell the frame type, it's better to add a segment in frame. In my program, the frame is coded as following:</p><center><p><code>|  flag  | type  | destination | source | sequence |      data       |  CRC   |  flag  |</code></p></center><center><p><code>| 1 byte | 1 bit |   2 bits    | 2 bits |  4 bits  | at least 1 byte | 1 byte | 1 byte |</code></p></center><p>So the minimal length of the frame is 41 bits. And in order to make timeout a short time to wait, the maximal length shouldn't be too long. In my program, I set max length to 65 bits, which means the data has max length of 4 bytes. When the data size is bigger than max length, the data is sliced into pieces.</p></li><li><p>When corruption rate of the channel is rather a big number, the max length of the frame should be shorter in order to avoid resending.</p></li></ul><h3 id="error-control">Error Control</h3><p>In order to deal with flipping bit that may occur in any part of the frame, discuss every possible situation is necessary for robustness of the program</p><ul><li><p><strong>How receiver's binary buffer deal with flags</strong></p><p>If everything goes right, the binary buffer should receive: <code>01111110 ... 01111110</code>, then receiver will take it out from the buffer as a frame and delete those bits in buffer</p><p>When the binary buffer receives following stuff: <code>... 01111110</code>, which means flag appears after some data. In such situation. The bin buffer should drop all the data before the flag and wait for following data</p><p>When the binary buffer receives following stuff:<code>01111110 ...(only a few bits)... 01111110</code>, which means the second flag appears before the frame reaches it's minimal length. The bin buffer should drop all bits before the second flag and wait for following data</p></li><li><p><strong>Start Flag</strong></p><p>When a bit was flipped in the start flag, receiver cannot recognize a frame anymore, the related frame drops, only waiting for timeout</p></li><li><p><strong>End Flag</strong></p><p>When a bit was flipped in the end flag, receiver will take the start flag of the next frame as the end flag of the former one, which will break the current frame and the next frame</p><p>However, the receiver can still get the sender address and the sequence of the first frame, so receiver will send NAK frame about the first frame to sender, but directly drop the next frame without sending NAK frame, only waiting for timeout</p></li><li><p><strong>Destination</strong></p><p>When a bit was flipped in the destination, the receiver could not receive the frame at all, only waiting for timeout</p></li><li><p><strong>Source</strong></p><p>When a bit was flipped in the source, the receiver will find CRC doesn't match and may send a NAK frame to a wrong host. If the wrong host happen to have connection to the receiver, it's also not a big deal since the worst situation is that the wrong host will send a frame to the receiver</p></li><li><p><strong>Sequence</strong></p><p>When a bit was flipped in the sequence, the receiver will find CRC doesn't match and send a NAK frame about a wrong frame to the sender. Not a big deal since the worst situation is that the sender will repeat a frame and wait for the flipped frame until timeout</p></li><li><p><strong>Data</strong></p><p>When a bit was flipped in the sequence, the receiver will find CRC doesn't match and send a NAK frame to the sender</p></li><li><p><strong>CRC</strong></p><p>When a bit was flipped in the sequence, the receiver will find CRC doesn't match and send a NAK frame to the sender</p></li></ul><h2 id="source">Source</h2><p>For <code>ARQ_Simulator.js</code></p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Utilities    </div>    <div class='spoiler-content'>        <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> START_TIME = <span class="built_in">Date</span>.now();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FrameSenderStatus = &#123;</span><br><span class="line">    Queuing: <span class="number">0</span>,</span><br><span class="line">    Sending: <span class="number">1</span>,</span><br><span class="line">    WaitingAck: <span class="number">2</span>,</span><br><span class="line">    Timeout: <span class="number">3</span>,</span><br><span class="line">    Acked: <span class="number">4</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FrameType = &#123;</span><br><span class="line">    Data: <span class="number">0</span>,</span><br><span class="line">    Control: <span class="number">1</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bin2bytes</span>(<span class="params">bin</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (bin.length % <span class="number">8</span> != <span class="number">0</span>) <span class="comment">// Padding 0 to the first byte</span></span><br><span class="line">        bin = <span class="built_in">Array</span>(<span class="number">8</span> - bin.length % <span class="number">8</span>).fill(<span class="number">0</span>).concat(bin);</span><br><span class="line">    <span class="keyword">var</span> bytes = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; bin.length; i += <span class="number">8</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> byte = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; <span class="number">8</span>; j++) &#123;</span><br><span class="line">            byte = byte &lt;&lt; <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span> (i + j &lt; bin.length)</span><br><span class="line">                byte += bin[i + j];</span><br><span class="line">        &#125;</span><br><span class="line">        bytes.push(byte);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bytes;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bytes2bin</span>(<span class="params">bytes</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> (bytes) == <span class="string">&quot;number&quot;</span>)</span><br><span class="line">        bytes = [bytes];</span><br><span class="line">    <span class="keyword">var</span> bin = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; bytes.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (bytes[i] &gt; <span class="number">255</span> || bytes[i] &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="string">&quot;Byte out of range&quot;</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; <span class="number">8</span>; j++)</span><br><span class="line">            bin.push((bytes[i] &gt;&gt; (<span class="number">7</span> - j % <span class="number">8</span>)) &amp; <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bin;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">str2bytes</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// Convert a string to an array of bytes</span></span><br><span class="line">    <span class="comment">// UTF-8 Supported</span></span><br><span class="line">    <span class="keyword">var</span> bytes = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; str.length; i++) &#123;</span><br><span class="line">        <span class="keyword">var</span> charCode = str.charCodeAt(i);</span><br><span class="line">        <span class="keyword">if</span> (charCode &gt; <span class="number">0xFFFF</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="string">&quot;Character out of range&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (charCode &lt; <span class="number">0x80</span>) &#123;</span><br><span class="line">            bytes.push(charCode);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (charCode &lt; <span class="number">0x800</span>) &#123;</span><br><span class="line">            bytes.push(<span class="number">0xC0</span> | (charCode &gt;&gt; <span class="number">6</span>), <span class="number">0x80</span> | (charCode &amp; <span class="number">0x3F</span>));</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            bytes.push(<span class="number">0xE0</span> | (charCode &gt;&gt; <span class="number">12</span>), <span class="number">0x80</span> | ((charCode &gt;&gt; <span class="number">6</span>) &amp; <span class="number">0x3F</span>), <span class="number">0x80</span> | (charCode &amp; <span class="number">0x3F</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bytes;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bytes2str</span>(<span class="params">bytes</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// Convert an array of bytes to a string</span></span><br><span class="line">    <span class="comment">// UTF-8 Supported</span></span><br><span class="line">    <span class="keyword">var</span> str = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; bytes.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (bytes[i] &lt; <span class="number">0x80</span>) &#123;</span><br><span class="line">            str += <span class="built_in">String</span>.fromCharCode(bytes[i]);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (bytes[i] &lt; <span class="number">0xE0</span>) &#123;</span><br><span class="line">            str += <span class="built_in">String</span>.fromCharCode(((bytes[i] &amp; <span class="number">0x1F</span>) &lt;&lt; <span class="number">6</span>) | (bytes[i + <span class="number">1</span>] &amp; <span class="number">0x3F</span>));</span><br><span class="line">            i++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            str += <span class="built_in">String</span>.fromCharCode(((bytes[i] &amp; <span class="number">0x0F</span>) &lt;&lt; <span class="number">12</span>) | ((bytes[i + <span class="number">1</span>] &amp; <span class="number">0x3F</span>) &lt;&lt; <span class="number">6</span>) | (bytes[i + <span class="number">2</span>] &amp; <span class="number">0x3F</span>));</span><br><span class="line">            i += <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">binArray2CubeText</span>(<span class="params">bin</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> str = bin.join(<span class="string">&quot;&quot;</span>);</span><br><span class="line">    <span class="comment">// replace 0 with ▢ and 1 with ▩</span></span><br><span class="line">    str = str.replace(<span class="regexp">/0/g</span>, <span class="string">&quot;▢&quot;</span>).replace(<span class="regexp">/1/g</span>, <span class="string">&quot;▩&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CubeText2binArray</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// replace ▢ with 0 and ▩ with 1 and make it bin array</span></span><br><span class="line">    str = str.replace(<span class="regexp">/▢/g</span>, <span class="string">&quot;0&quot;</span>).replace(<span class="regexp">/▩/g</span>, <span class="string">&quot;1&quot;</span>).replace(<span class="regexp">/ /g</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line">    <span class="keyword">var</span> bin = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; str.length; i++) &#123;</span><br><span class="line">        bin.push(<span class="built_in">parseInt</span>(str[i]));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bin;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">crc8</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// CRC-8-CCITT</span></span><br><span class="line">    <span class="comment">// data: array of bytes</span></span><br><span class="line">    <span class="keyword">var</span> POLY = <span class="number">0x07</span>,</span><br><span class="line">        INIT = <span class="number">0</span>,</span><br><span class="line">        XOROUT = <span class="number">0x55</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> crc = INIT, i = <span class="number">0</span>; i &lt; data.length; i++) &#123;</span><br><span class="line">        crc = crc ^ data[i];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; <span class="number">8</span>; j++) &#123;</span><br><span class="line">            crc = crc &amp; <span class="number">0x80</span> ? crc &lt;&lt; <span class="number">1</span> ^ POLY : crc &lt;&lt; <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (crc ^ XOROUT) &amp; <span class="number">0xFF</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">BitStuffing</span>(<span class="params">binArr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> stuffed = [];</span><br><span class="line">    <span class="keyword">var</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; binArr.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (binArr[i] == <span class="number">1</span>) &#123;</span><br><span class="line">            count++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        stuffed.push(binArr[i]);</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="number">5</span>) &#123;</span><br><span class="line">            stuffed.push(<span class="number">0</span>);</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> stuffed;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">BitDestuffing</span>(<span class="params">binArr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> destuffed = [];</span><br><span class="line">    <span class="keyword">var</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; binArr.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (binArr[i] == <span class="number">1</span>) &#123;</span><br><span class="line">            count++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        destuffed.push(binArr[i]);</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="number">5</span>) &#123;</span><br><span class="line">            i++;</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> destuffed;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FrameHelper</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="function"><span class="title">CheckFrame</span>(<span class="params">frame</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Check if the frame is valid</span></span><br><span class="line">        <span class="comment">// frame: binary array</span></span><br><span class="line">        <span class="comment">// return: boolean</span></span><br><span class="line">        <span class="keyword">if</span> (frame.slice(<span class="number">0</span>, <span class="number">8</span>).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span> || frame.slice(-<span class="number">8</span>).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> payload = BitDestuffing(frame.slice(<span class="number">8</span>, -<span class="number">8</span>));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> crc = payload.slice(-<span class="number">8</span>);</span><br><span class="line">        <span class="keyword">var</span> crcCal = crc8(bin2bytes(payload.slice(<span class="number">0</span>, -<span class="number">8</span>)));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (crcCal != bin2bytes(crc)[<span class="number">0</span>]) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="function"><span class="title">MakeFrame</span>(<span class="params">destination, source, seq, byteData, type = FrameType.Data</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// return: binary array</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (destination &gt; <span class="number">3</span> || destination &lt; <span class="number">0</span> || source &gt; <span class="number">3</span> || source &lt; <span class="number">0</span> || seq &gt; <span class="number">15</span> || seq &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="string">&quot;Invalid destination, source or sequence&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span> (byteData.length == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="string">&quot;Data cannot be empty&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span> (type &gt; FrameType.MAX || type &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="string">&quot;Invalid frame type&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> frame = [];</span><br><span class="line">        <span class="keyword">var</span> flag = bytes2bin(<span class="number">0x7E</span>)</span><br><span class="line">        <span class="keyword">var</span> control = [type, (destination &lt;&lt; <span class="number">6</span>) | (source &lt;&lt; <span class="number">4</span>) | seq];</span><br><span class="line">        <span class="keyword">var</span> binData = bytes2bin(byteData);</span><br><span class="line">        <span class="keyword">if</span> (binData.length &lt; <span class="number">8</span>)</span><br><span class="line">            binData = <span class="built_in">Array</span>(<span class="number">8</span> - binData.length).fill(<span class="number">0</span>).concat(binData);</span><br><span class="line">        <span class="keyword">var</span> crc = crc8(control.concat(byteData));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> payload = bytes2bin(control).slice(<span class="number">7</span>).concat(binData).concat(bytes2bin(crc));</span><br><span class="line">        payload = BitStuffing(payload);</span><br><span class="line"></span><br><span class="line">        frame = frame.concat(flag);</span><br><span class="line">        frame = frame.concat(payload);</span><br><span class="line">        frame = frame.concat(flag);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> frame;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="function"><span class="title">MakeFrameFromeText</span>(<span class="params">destination, source, startSeq, text, maxFrameSize</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// return: 2D array, each element is a binary array frame</span></span><br><span class="line">        <span class="keyword">var</span> frames = [];</span><br><span class="line">        <span class="keyword">var</span> data = str2bytes(text);</span><br><span class="line">        <span class="keyword">var</span> seq = startSeq;</span><br><span class="line">        <span class="keyword">var</span> maxDataSize = maxFrameSize - <span class="number">33</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; data.length; i += maxDataSize) &#123;</span><br><span class="line">            <span class="keyword">var</span> frameData = data.slice(i, i + maxDataSize);</span><br><span class="line">            frames.push(FrameHelper.MakeFrame(destination, source, seq, frameData));</span><br><span class="line">            seq = (seq + <span class="number">1</span>) % <span class="number">16</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> frames;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="function"><span class="title">ExtractFrame</span>(<span class="params">frame, checkFrame = <span class="literal">true</span></span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Extract the frame into destination, source, sequence and data</span></span><br><span class="line">        <span class="comment">// frame: binary array</span></span><br><span class="line">        <span class="comment">// return: object &#123;destination, source, sequence, data&#125;</span></span><br><span class="line">        <span class="keyword">if</span> (checkFrame &amp;&amp; !FrameHelper.CheckFrame(frame))</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> payload = BitDestuffing(frame.slice(<span class="number">8</span>, -<span class="number">8</span>));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> type = payload[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">var</span> destination = bin2bytes(payload.slice(<span class="number">1</span>, <span class="number">3</span>))[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">var</span> source = bin2bytes(payload.slice(<span class="number">3</span>, <span class="number">5</span>))[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">var</span> sequence = bin2bytes(payload.slice(<span class="number">5</span>, <span class="number">9</span>))[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">var</span> data = bin2bytes(payload.slice(<span class="number">9</span>, -<span class="number">8</span>));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            type: type,</span><br><span class="line">            destination: destination,</span><br><span class="line">            source: source,</span><br><span class="line">            sequence: sequence,</span><br><span class="line">            data: data</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="function"><span class="title">DisplayFrameInText</span>(<span class="params">frame, split = <span class="string">&quot;&lt;br&gt;&quot;</span>, end = <span class="string">&quot;&lt;br&gt;&lt;br&gt;&quot;</span></span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Display the frame in text</span></span><br><span class="line">        <span class="comment">// frame: binary array</span></span><br><span class="line">        <span class="comment">// return: string</span></span><br><span class="line">        <span class="keyword">var</span> frameObj = FrameHelper.ExtractFrame(frame);</span><br><span class="line">        <span class="keyword">if</span> (frameObj == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (frameObj.type == FrameType.Control) &#123;</span><br><span class="line">            <span class="keyword">if</span> (frameObj.data[<span class="number">0</span>] == <span class="number">0x01</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="string">&quot; x Sequence: &quot;</span> + frameObj.sequence + split + <span class="string">&quot;ACK Frame&quot;</span> + end;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (frameObj.data[<span class="number">0</span>] == <span class="number">0x00</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="string">&quot; x Sequence: &quot;</span> + frameObj.sequence + split + <span class="string">&quot;NAK Frame&quot;</span> + end;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot; @ Sequence: &quot;</span> + frameObj.sequence + split + <span class="string">&quot; + Data: &quot;</span> + bytes2str(frameObj.data).slice(<span class="number">0</span>, <span class="number">25</span>) + (bytes2str(frameObj.data).length &gt; <span class="number">25</span> ? <span class="string">&quot; ... &quot;</span> : <span class="string">&quot;&quot;</span>) + end;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="function"><span class="title">FrameFinder</span>(<span class="params">buffer, minFrLen</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Find the first frame in the buffer by searching for the flags</span></span><br><span class="line">        <span class="comment">// buffer: binary array</span></span><br><span class="line">        <span class="comment">// return: &#123;first frame in the buffer, start, end&#125; or null</span></span><br><span class="line">        <span class="keyword">if</span> (buffer.length &lt; minFrLen)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Find the start of the frame</span></span><br><span class="line">        <span class="keyword">var</span> start = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (</span><br><span class="line">            buffer.slice(start, start + <span class="number">8</span>).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span> &amp;&amp;</span><br><span class="line">            start + minFrLen &lt; buffer.length</span><br><span class="line">        )</span><br><span class="line">            start++;</span><br><span class="line">        <span class="keyword">if</span> (start + minFrLen &gt; buffer.length || buffer.slice(start, start + <span class="number">8</span>).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Find the end of the frame</span></span><br><span class="line">        <span class="keyword">var</span> end = buffer.length;</span><br><span class="line">        <span class="keyword">while</span> (</span><br><span class="line">            buffer.slice(end - <span class="number">8</span>, end).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span> &amp;&amp;</span><br><span class="line">            end - minFrLen &gt; start</span><br><span class="line">        )</span><br><span class="line">            end--;</span><br><span class="line">        <span class="keyword">if</span> (end - minFrLen &lt; start || buffer.slice(end - <span class="number">8</span>, end).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123;</span><br><span class="line">            frame: buffer.slice(start, end),</span><br><span class="line">            start: start,</span><br><span class="line">            end: end</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        Host Class    </div>    <div class='spoiler-content'>        <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Host</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="title">constructor</span>(<span class="params">simulator, id, UpperLayerFunc = () =&gt; &#123; <span class="keyword">return</span>; &#125;</span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sim = simulator;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.UpperLayerFunc = UpperLayerFunc;</span><br><span class="line">        <span class="built_in">this</span>.tickFlag = <span class="literal">false</span>;</span><br><span class="line">        <span class="built_in">this</span>.ui = <span class="literal">null</span>;</span><br><span class="line">        <span class="built_in">this</span>.sendingFrame = <span class="literal">false</span>;</span><br><span class="line">        <span class="built_in">this</span>.tickCnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.sentBitCnt = <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">this</span>.processedBitCnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.receiveBinBuffer = []; <span class="comment">// binary array</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.sendWinSeq = <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">this</span>.frameSender = [];</span><br><span class="line">        <span class="comment">// array of &#123;frame, receiverID, binIndex, sequence, feedback, controlFrame, timeout, status&#125;</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.receiveFrameBuffer = <span class="built_in">Array</span>(<span class="built_in">this</span>.sim.para.winLen).fill(<span class="literal">null</span>);</span><br><span class="line">        <span class="built_in">this</span>.receiveWinSeq = <span class="number">0</span>; <span class="comment">// sequence number of the first frame in the receive window</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.sim.hostList.push(<span class="built_in">this</span>);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.Ticker = <span class="built_in">setInterval</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (!<span class="built_in">this</span>.tickFlag)</span><br><span class="line">                <span class="built_in">this</span>.Tick();</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; is still ticking&quot;</span>)</span><br><span class="line">        &#125;, <span class="built_in">this</span>.sim.para.tick);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">Tick</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.tickFlag = <span class="literal">true</span>;</span><br><span class="line">        <span class="built_in">this</span>.SendListener();</span><br><span class="line">        <span class="built_in">this</span>.ReceiveListener();</span><br><span class="line">        <span class="built_in">this</span>.tickCnt++;</span><br><span class="line">        <span class="built_in">this</span>.tickFlag = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">PushToReceiveFrameBuffer</span>(<span class="params">extractedFrame</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// push the extracted frame to the receive frame buffer</span></span><br><span class="line">        <span class="keyword">var</span> sequence = extractedFrame.sequence;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> distance = <span class="built_in">Math</span>.min((<span class="built_in">this</span>.receiveWinSeq - sequence + <span class="built_in">this</span>.sim.para.seq) % <span class="built_in">this</span>.sim.para.seq, (sequence - <span class="built_in">this</span>.receiveWinSeq + <span class="built_in">this</span>.sim.para.seq) % <span class="built_in">this</span>.sim.para.seq);</span><br><span class="line">        <span class="keyword">if</span> (distance == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.receiveFrameBuffer[<span class="number">0</span>] = extractedFrame;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">var</span> expected = []</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i &lt; <span class="built_in">this</span>.sim.para.winLen; i++)</span><br><span class="line">                expected.push((<span class="built_in">this</span>.receiveWinSeq + i) % <span class="built_in">this</span>.sim.para.seq);</span><br><span class="line">            <span class="keyword">if</span> (expected.indexOf(sequence) == -<span class="number">1</span>) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; repeated frame &quot;</span> + sequence + <span class="string">&quot; as now is already to frame &quot;</span> + <span class="built_in">this</span>.receiveWinSeq);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">this</span>.receiveFrameBuffer[expected.indexOf(sequence) + <span class="number">1</span>] = extractedFrame;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;👉Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; Added frame &quot;</span> + sequence + <span class="string">&quot; to receive buffer &quot;</span> + <span class="string">&quot;as &quot;</span> + expected.indexOf(sequence) + <span class="number">1</span>);</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="built_in">this</span>.receiveFrameBuffer);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">this</span>.receiveFrameBuffer[<span class="number">0</span>] != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.UpperLayerFunc(<span class="built_in">this</span>.receiveFrameBuffer[<span class="number">0</span>].data);</span><br><span class="line"></span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁&quot;</span>)</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;👇Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; Removed frame &quot;</span> + <span class="built_in">this</span>.receiveWinSeq + <span class="string">&quot; from receive buffer&quot;</span>);</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;ASCII: &quot;</span> + bytes2str(<span class="built_in">this</span>.receiveFrameBuffer[<span class="number">0</span>].data));</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔&quot;</span>)</span><br><span class="line">            <span class="built_in">this</span>.receiveFrameBuffer.shift();</span><br><span class="line">            <span class="built_in">this</span>.receiveFrameBuffer.push(<span class="literal">null</span>);</span><br><span class="line">            <span class="built_in">this</span>.receiveWinSeq = (<span class="built_in">this</span>.receiveWinSeq + <span class="number">1</span>) % <span class="built_in">this</span>.sim.para.seq;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;👀 Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; Now receive window is at &quot;</span> + <span class="built_in">this</span>.receiveWinSeq);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">HandleControlFrame</span>(<span class="params">extractedFrame</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.sim.para.winLen; i++) &#123;</span><br><span class="line">            <span class="keyword">var</span> fs = <span class="built_in">this</span>.frameSender[i];</span><br><span class="line">            <span class="keyword">if</span> (fs == <span class="literal">null</span> || fs.sequence != extractedFrame.sequence)</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            fs.timeout = <span class="literal">null</span>;</span><br><span class="line">            fs.feedback = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (extractedFrame.data[<span class="number">0</span>] == <span class="number">0x01</span>) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;    👮🟢Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; received ACK frame &quot;</span> + extractedFrame.sequence + <span class="string">&quot; from host &quot;</span> + extractedFrame.source);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;👮‍🟢 Received ACK frame&quot;</span> + extractedFrame.sequence, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                fs.status = FrameSenderStatus.Acked;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;    👮🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; received NAK frame &quot;</span> + extractedFrame.sequence + <span class="string">&quot; from host &quot;</span> + extractedFrame.source);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;👮🔴 Received NAK frame &quot;</span> + extractedFrame.sequence, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            fs.binIndex = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">HandleDataFrame</span>(<span class="params">extractedFrame</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Check if the frame is in the receive window</span></span><br><span class="line">        <span class="keyword">var</span> distance = <span class="built_in">Math</span>.min((<span class="built_in">this</span>.receiveWinSeq - extractedFrame.sequence + <span class="built_in">this</span>.sim.para.seq) % <span class="built_in">this</span>.sim.para.seq, (extractedFrame.sequence - <span class="built_in">this</span>.receiveWinSeq + <span class="built_in">this</span>.sim.para.seq) % <span class="built_in">this</span>.sim.para.seq);</span><br><span class="line">        <span class="keyword">if</span> (distance &gt;= <span class="built_in">this</span>.sim.para.winLen + <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;❌Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; received frame &quot;</span> + extractedFrame.sequence + <span class="string">&quot; from host &quot;</span> + extractedFrame.source + <span class="string">&quot; out of window&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Send ACK frame to the source</span></span><br><span class="line">        <span class="keyword">var</span> fbFrame = FrameHelper.MakeFrame(</span><br><span class="line">            extractedFrame.source,</span><br><span class="line">            <span class="built_in">this</span>.id,</span><br><span class="line">            extractedFrame.sequence,</span><br><span class="line">            [<span class="number">0x01</span>],</span><br><span class="line">            FrameType.Control,</span><br><span class="line">        );</span><br><span class="line">        <span class="built_in">this</span>.LoadFrameToSender(fbFrame);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Save the frame to the receive buffer</span></span><br><span class="line">        <span class="built_in">this</span>.PushToReceiveFrameBuffer(extractedFrame);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">HandleCorruptedFrame</span>(<span class="params">extractedFrame</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// check if the sender id is in the host list, if so, send NAK frame to the source</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.sim.FindHostById(extractedFrame.source) != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> fbFrame = FrameHelper.MakeFrame(</span><br><span class="line">                extractedFrame.source,</span><br><span class="line">                <span class="built_in">this</span>.id,</span><br><span class="line">                extractedFrame.sequence,</span><br><span class="line">                [<span class="number">0x00</span>],</span><br><span class="line">                FrameType.Control,</span><br><span class="line">            );</span><br><span class="line">            <span class="built_in">this</span>.LoadFrameToSender(fbFrame);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;✋🏻 Sent NAK to &quot;</span> + extractedFrame.source + <span class="string">&quot; about frame &quot;</span> + extractedFrame.sequence, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">            <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;❌ Source info corrupted, dropped&quot;</span>, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">ReceiveListener</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Listen to the receive bin buffer, extract frame and push to receive frame buffer</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// Check if the first 8 bytes are a valid flag</span></span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBuffer.length &gt;= <span class="number">8</span> &amp;&amp;</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBuffer.slice(<span class="number">0</span>, <span class="number">8</span>).join(<span class="string">&quot;&quot;</span>) != <span class="string">&quot;01111110&quot;</span></span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="comment">// If not, remove the first byte</span></span><br><span class="line">            <span class="built_in">this</span>.receiveBinBuffer.shift();</span><br><span class="line">            <span class="built_in">this</span>.processedBitCnt++;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.receiveBinBuffer.length &lt; <span class="built_in">this</span>.sim.para.minFrLen)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// Check if there is valid flag at the middle of the buffer</span></span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBuffer.length &gt;= <span class="number">40</span> &amp;&amp;</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBuffer.slice(<span class="number">1</span>, <span class="built_in">this</span>.receiveBinBuffer.length - <span class="number">1</span>).join(<span class="string">&quot;&quot;</span>).indexOf(<span class="string">&quot;01111110&quot;</span>) != -<span class="number">1</span></span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="comment">// If yes, remove the bytes before the flag</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;♊ Two or more flag in first &quot;</span> + <span class="built_in">this</span>.sim.para.minFrLen + <span class="string">&quot; bits&quot;</span>, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">var</span> flagIndex = <span class="built_in">this</span>.receiveBinBuffer.slice(<span class="number">1</span>, <span class="built_in">this</span>.receiveBinBuffer.length - <span class="number">1</span>).join(<span class="string">&quot;&quot;</span>).indexOf(<span class="string">&quot;01111110&quot;</span>);</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBuffer = <span class="built_in">this</span>.receiveBinBuffer.slice(flagIndex);</span><br><span class="line">            <span class="built_in">this</span>.processedBitCnt += flagIndex;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// if there is enough data to extract a frame</span></span><br><span class="line">        <span class="keyword">var</span> fFinder = FrameHelper.FrameFinder(<span class="built_in">this</span>.receiveBinBuffer, <span class="built_in">this</span>.sim.para.minFrLen);</span><br><span class="line">        <span class="keyword">if</span> (!fFinder)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> frame = fFinder.frame;</span><br><span class="line">        <span class="built_in">this</span>.receiveBinBuffer = <span class="built_in">this</span>.receiveBinBuffer.slice(fFinder.end);</span><br><span class="line">        <span class="built_in">this</span>.processedBitCnt += fFinder.end;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Extract the frame</span></span><br><span class="line">        <span class="keyword">if</span> (FrameHelper.CheckFrame(frame)) &#123; <span class="comment">// if the frame is valid</span></span><br><span class="line">            <span class="keyword">var</span> extractedFrame = FrameHelper.ExtractFrame(frame);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (extractedFrame.destination != <span class="built_in">this</span>.id) &#123;</span><br><span class="line">                <span class="built_in">console</span>.warn(<span class="string">&quot;[DESTINATION] Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; received frame for host &quot;</span> + extractedFrame.destination);</span><br><span class="line">                <span class="built_in">console</span>.warn(<span class="string">&quot;[DESTINATION] frame: &quot;</span>, frame);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;🤷‍♂️ Not frame for this host, dropped&quot;</span>, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (extractedFrame.type == FrameType.Control) &#123;</span><br><span class="line">                <span class="comment">// If it&#x27;s a ACK or NAK frame, check frame sender and update the buffer</span></span><br><span class="line">                <span class="comment">// This also shows the host is the sender in this conversation</span></span><br><span class="line">                <span class="built_in">this</span>.HandleControlFrame(extractedFrame);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.HandleDataFrame(extractedFrame);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;📧 Valid data frame &quot;</span> + extractedFrame.sequence + <span class="string">&quot; found, sent it to Frame Receive Buffer&quot;</span>, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123; <span class="comment">// if the frame is corrupted, try to extract and send NAK</span></span><br><span class="line">            <span class="built_in">console</span>.warn(<span class="string">&quot;[CORRUPTED] Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; received corrupted frame&quot;</span>);</span><br><span class="line">            <span class="built_in">console</span>.warn(<span class="string">&quot;[CORRUPTED] frame: &quot;</span>, frame);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;❓ Corrupted frame received, trying to handle&quot;</span>, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">var</span> extractedFrame = FrameHelper.ExtractFrame(frame, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (extractedFrame.destination != <span class="built_in">this</span>.id) &#123;</span><br><span class="line">                <span class="built_in">console</span>.warn(<span class="string">&quot;[DESTINATION] Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; received frame for host &quot;</span> + extractedFrame.destination);</span><br><span class="line">                <span class="built_in">console</span>.warn(<span class="string">&quot;[DESTINATION] frame: &quot;</span>, frame);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;🤷‍♂️ Not frame for this host, dropped&quot;</span>, <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="built_in">this</span>.HandleCorruptedFrame(extractedFrame);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">LoadFrameToSender</span>(<span class="params">frame</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> extractedFrame = FrameHelper.ExtractFrame(frame);</span><br><span class="line">        <span class="built_in">this</span>.frameSender.push(&#123;</span><br><span class="line">            frame: frame.slice(),</span><br><span class="line">            receiverID: extractedFrame.destination,</span><br><span class="line">            binIndex: <span class="number">0</span>,</span><br><span class="line">            sequence: extractedFrame.sequence,</span><br><span class="line">            feedback: <span class="literal">false</span>,</span><br><span class="line">            controlFrame: extractedFrame.type == FrameType.Control,</span><br><span class="line">            timeout: <span class="literal">null</span>, <span class="comment">// timeout = null means the frame is not sent yet</span></span><br><span class="line">            status: FrameSenderStatus.Queuing,</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">SendListener</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Listen to the send frame buffer, send the frame to the destination using Selective Repeat ARQ protocol</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.frameSender.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// when not sending frame, check if there is any control frame, if so, make the first control frame found the top of the buffer</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.frameSender.length &amp;&amp; !<span class="built_in">this</span>.sendingFrame; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.frameSender[i].controlFrame == <span class="literal">true</span>) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;👆👆👆Contorl frame has moved to the top of the buffer&quot;</span>)</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;👆👆👆 Contorl frame has moved to the top of the buffer&quot;</span>, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="built_in">this</span>.frameSender.unshift(<span class="built_in">this</span>.frameSender.splice(i, <span class="number">1</span>)[<span class="number">0</span>]);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (</span><br><span class="line">            <span class="built_in">this</span>.frameSender.length &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">            <span class="built_in">this</span>.frameSender[<span class="number">0</span>].status == FrameSenderStatus.Acked</span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;      🚮 Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; Removed frame &quot;</span> + <span class="built_in">this</span>.frameSender[<span class="number">0</span>].sequence + <span class="string">&quot; from sender buffer&quot;</span>);</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;👀 Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; Now the send window is at &quot;</span> + <span class="built_in">this</span>.frameSender[<span class="number">0</span>].sequence)</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;🚮 Removed frame &quot;</span> + <span class="built_in">this</span>.frameSender[<span class="number">0</span>].sequence, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">this</span>.frameSender.shift();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Find the frame that is being sending or frist frame waiting to be sent</span></span><br><span class="line">        <span class="keyword">var</span> currentFrameIndex = -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.sim.para.winLen &amp;&amp; i &lt; <span class="built_in">this</span>.frameSender.length; i++) &#123;</span><br><span class="line">            <span class="keyword">var</span> currentFS = <span class="built_in">this</span>.frameSender[i];</span><br><span class="line">            <span class="keyword">if</span> (</span><br><span class="line">                currentFS.timeout == <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                (currentFS.feedback == <span class="literal">false</span> || currentFS.binIndex != currentFS.frame.length)</span><br><span class="line">            ) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!currentFS.feedback) &#123;</span><br><span class="line">                    currentFrameIndex = i;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (!<span class="built_in">this</span>.sendingFrame) &#123;</span><br><span class="line">                    currentFrameIndex = i;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (currentFrameIndex == -<span class="number">1</span>) &#123;</span><br><span class="line">            <span class="comment">// When all frames in the window are sent</span></span><br><span class="line">            <span class="comment">// Check time out in the window range</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.sim.para.winLen &amp;&amp; i &lt; <span class="built_in">this</span>.frameSender.length; i++) &#123;</span><br><span class="line">                <span class="keyword">var</span> currentFS = <span class="built_in">this</span>.frameSender[i];</span><br><span class="line">                <span class="keyword">if</span> (currentFS.timeout != <span class="literal">null</span> &amp;&amp; (<span class="built_in">this</span>.tickCnt &gt; currentFS.timeout)) &#123;</span><br><span class="line">                    <span class="comment">// If time out, reset the frame and send it again</span></span><br><span class="line"></span><br><span class="line">                    <span class="built_in">console</span>.log(<span class="string">&quot;⏰Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; time out frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                    <span class="built_in">console</span>.log(currentFS.timeout)</span><br><span class="line">                    <span class="built_in">console</span>.log(<span class="built_in">this</span>.tickCnt)</span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                        <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;⏰ Time out frame &quot;</span> + currentFS.sequence, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    currentFS.status = FrameSenderStatus.Timeout;</span><br><span class="line">                    currentFS.binIndex = <span class="number">0</span>;</span><br><span class="line">                    currentFS.timeout = <span class="literal">null</span>;</span><br><span class="line">                    currentFrameIndex = i;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (currentFrameIndex == -<span class="number">1</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.sendingFrame = <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Send a bit of the frame to the destination</span></span><br><span class="line">        <span class="keyword">var</span> destination = <span class="built_in">this</span>.sim.FindHostById(<span class="built_in">this</span>.frameSender[currentFrameIndex].receiverID);</span><br><span class="line">        <span class="keyword">var</span> currentFS = <span class="built_in">this</span>.frameSender[currentFrameIndex];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Update the frameSenderInfo</span></span><br><span class="line">        <span class="keyword">if</span> (currentFS.binIndex != currentFS.frame.length) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (currentFS.binIndex == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.sendingFrame = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">var</span> frameContent = bytes2str(FrameHelper.ExtractFrame(currentFS.frame).data);</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;📡Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; sending frame:  \&quot;&quot;</span> + frameContent + <span class="string">&quot;\&quot;  to host &quot;</span> + currentFS.receiverID + <span class="string">&quot; (sequence: &quot;</span> + currentFS.sequence);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;📡 Sending frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                currentFS.status = FrameSenderStatus.Sending;</span><br><span class="line">                currentFS.feedback = <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            destination.receiveBinBuffer.push(currentFS.frame[currentFS.binIndex])</span><br><span class="line"></span><br><span class="line">            <span class="keyword">var</span> destHost = <span class="built_in">this</span>.sim.hostList[<span class="built_in">this</span>.id == <span class="number">0</span> ? <span class="number">1</span> : <span class="number">0</span>];</span><br><span class="line">            <span class="built_in">this</span>.sentBitCnt++;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// ================================================================================</span></span><br><span class="line">            <span class="comment">// Mannually corrupting the bit randomly</span></span><br><span class="line">            <span class="comment">// ================================================================================</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">Math</span>.random() &lt; <span class="built_in">this</span>.sim.para.corRate) &#123;</span><br><span class="line">                destination.receiveBinBuffer[destination.receiveBinBuffer.length - <span class="number">1</span>] = <span class="number">1</span> - destination.receiveBinBuffer[destination.receiveBinBuffer.length - <span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Judge which part of the frame is corrupted</span></span><br><span class="line">                <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">8</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the header of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">9</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the type of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">11</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the destination of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">13</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the source of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">17</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the sequence of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; currentFS.frame.length - <span class="number">16</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the data of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; currentFS.frame.length - <span class="number">8</span>) &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the checksum of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="built_in">console</span>.warn(<span class="string">&quot;🔴Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; corrupted the end of frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">var</span> anotherUI = <span class="built_in">this</span>.sim.hostList[<span class="built_in">this</span>.id == <span class="number">0</span> ? <span class="number">1</span> : <span class="number">0</span>].ui;</span><br><span class="line">                <span class="keyword">if</span> (anotherUI) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">8</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; header &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">9</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; type &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">11</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; destination &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">13</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; source &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; <span class="number">17</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; sequence &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; currentFS.frame.length - <span class="number">16</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; data &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentFS.binIndex &lt; currentFS.frame.length - <span class="number">8</span>) &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; checksum &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        anotherUI.LogInfo(<span class="string">&quot;🔴 Manually corrupted a bit &#123; end &#125; of frame &quot;</span> + currentFS.sequence + (currentFS.controlFrame ? <span class="string">&quot; (control frame)&quot;</span> : <span class="string">&quot;&quot;</span>), <span class="string">&quot;receiveBinBufferLog&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (destHost.ui) &#123;</span><br><span class="line">                    destHost.ui.errorArr.push(<span class="built_in">this</span>.sentBitCnt - <span class="number">1</span>)</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// ================================================================================</span></span><br><span class="line"></span><br><span class="line">            currentFS.binIndex++;</span><br><span class="line">            <span class="keyword">if</span> (currentFS.controlFrame == <span class="literal">true</span> &amp;&amp; currentFS.binIndex == currentFS.frame.length) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;  🔵Host&quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; sent control frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;🔵 Sent control frame &quot;</span> + currentFS.sequence, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// remove current frame from the buffer due to it&#x27;s control frame and has been sent</span></span><br><span class="line">                <span class="built_in">this</span>.frameSender.splice(currentFrameIndex, <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="built_in">this</span>.sendingFrame = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (currentFS.timeout == <span class="literal">null</span> &amp;&amp; currentFS.feedback == <span class="literal">false</span>) &#123;</span><br><span class="line">                currentFS.timeout = <span class="built_in">this</span>.tickCnt + <span class="built_in">this</span>.sim.para.timeout;</span><br><span class="line">                <span class="comment">// log a message to say that timeout is set</span></span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;  ⌚Host &quot;</span> + <span class="built_in">this</span>.id + <span class="string">&quot; timer started for frame &quot;</span> + currentFS.sequence + <span class="string">&quot; to host &quot;</span> + currentFS.receiverID);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.ui) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.ui.LogInfo(<span class="string">&quot;&amp;nbsp;&amp;nbsp;⌚ Timer started for frame &quot;</span> + currentFS.sequence, <span class="string">&quot;frameSenderBufferLog&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                currentFS.status = FrameSenderStatus.WaitingAck;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">SendMessage</span>(<span class="params">message, receiverID</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Generate frames and add it to the frameSender</span></span><br><span class="line">        <span class="keyword">var</span> frameList = FrameHelper.MakeFrameFromeText(receiverID, <span class="built_in">this</span>.id, <span class="built_in">this</span>.sendWinSeq, message, <span class="built_in">this</span>.sim.para.maxFrLen);</span><br><span class="line">        <span class="built_in">this</span>.sendWinSeq = (<span class="built_in">this</span>.sendWinSeq + frameList.length) % <span class="built_in">this</span>.sim.para.seq;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; frameList.length; i++) &#123;</span><br><span class="line">            <span class="built_in">this</span>.LoadFrameToSender(frameList[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        Simulator Class    </div>    <div class='spoiler-content'>        <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Simulator</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="title">constructor</span>(<span class="params">initHostNum, para</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// JS grammar trap: even these parameters have default values, you can&#x27;t use foo(a,c=xxx) to escape parameter b</span></span><br><span class="line">        <span class="comment">// so why don&#x27;t I use &#123;&#125; to wrap the parameters</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.hostList = [];</span><br><span class="line">        <span class="keyword">var</span> timeout = para.timeout;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&quot;Timeout is: &quot;</span> + timeout + <span class="string">&quot;ms according to the tick and frame length&quot;</span>)</span><br><span class="line">        <span class="built_in">this</span>.para = &#123;</span><br><span class="line">            seq: para.seq, <span class="comment">// Squence has 4 bits if SEQ = 16</span></span><br><span class="line">            winLen: para.windowlen, <span class="comment">// Window length</span></span><br><span class="line">            tick: para.tick, <span class="comment">// Tick in milliseconds</span></span><br><span class="line">            minFrLen: para.minFrameLen, <span class="comment">// Minimum frame length</span></span><br><span class="line">            maxFrLen: para.maxFrameLen, <span class="comment">// Maximum frame length</span></span><br><span class="line">            corRate: para.corruptionRate, <span class="comment">// Corruption rate of the channel</span></span><br><span class="line">            timeout: timeout <span class="comment">// Timeout in tick, meaning after how many ticks the sender will resend the frame</span></span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; initHostNum; i++) &#123;</span><br><span class="line">            <span class="keyword">var</span> _ = <span class="keyword">new</span> Host(<span class="built_in">this</span>, i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">FindHostById</span>(<span class="params">id</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.hostList.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.hostList[i].id == id)</span><br><span class="line">                <span class="keyword">return</span> <span class="built_in">this</span>.hostList[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        HostUI Class    </div>    <div class='spoiler-content'>        <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HostUI</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="title">constructor</span>(<span class="params">host, simulator</span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.host = host;</span><br><span class="line">        <span class="built_in">this</span>.host.ui = <span class="built_in">this</span>;</span><br><span class="line">        <span class="built_in">this</span>.sim = simulator;</span><br><span class="line">        <span class="built_in">this</span>.messageBuffer = [];</span><br><span class="line">        <span class="built_in">this</span>.errorArr = [];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// hostX_userInput, input textare</span></span><br><span class="line">        <span class="built_in">this</span>.userInputUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + host.id + <span class="string">&quot;_userInput&quot;</span>);</span><br><span class="line">        <span class="comment">// hostX_sendButton, button</span></span><br><span class="line">        <span class="built_in">this</span>.sendButtonUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + host.id + <span class="string">&quot;_sendButton&quot;</span>);</span><br><span class="line">        <span class="comment">// hostX_frameSenderBuffer, div</span></span><br><span class="line">        <span class="built_in">this</span>.frameSenderUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + host.id + <span class="string">&quot;_frameSenderBuffer&quot;</span>);</span><br><span class="line">        <span class="comment">// hostX_receiveBinBuffer, div</span></span><br><span class="line">        <span class="built_in">this</span>.receiveBinBufferUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + host.id + <span class="string">&quot;_receiveBinBuffer&quot;</span>);</span><br><span class="line">        <span class="comment">// hostX_frameReceiverBuffer, div</span></span><br><span class="line">        <span class="built_in">this</span>.frameReceiverUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + host.id + <span class="string">&quot;_frameReceiverBuffer&quot;</span>);</span><br><span class="line">        <span class="comment">// hostX_message, textarea</span></span><br><span class="line">        <span class="built_in">this</span>.messageUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + host.id + <span class="string">&quot;_message&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.sendButtonUI.onclick = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.userInputUI.value != <span class="string">&quot;&quot;</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.host.SendMessage(<span class="built_in">this</span>.userInputUI.value, <span class="built_in">this</span>.host.id == <span class="number">0</span> ? <span class="number">1</span> : <span class="number">0</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        host.UpperLayerFunc = <span class="built_in">this</span>.ShowInMessageUI.bind(<span class="built_in">this</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// set interval to update the UI</span></span><br><span class="line">        <span class="built_in">this</span>.uiTicker = <span class="built_in">setInterval</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.Update();</span><br><span class="line">        &#125;, <span class="built_in">this</span>.sim.para.tick / <span class="number">2</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">LogInfo</span>(<span class="params">message, logger</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// log a message to the host_&#123;logger&#125;</span></span><br><span class="line">        <span class="comment">// logger: frameSenderBufferLog, receiveBinBufferLog</span></span><br><span class="line">        <span class="keyword">var</span> logUI = <span class="built_in">document</span>.getElementById(<span class="string">&quot;host&quot;</span> + <span class="built_in">this</span>.host.id + <span class="string">&quot;_&quot;</span> + logger);</span><br><span class="line">        <span class="keyword">if</span> (logUI != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">            <span class="keyword">var</span> time = date.toLocaleTimeString() + <span class="string">&quot;: &quot;</span>;</span><br><span class="line">            logUI.innerHTML += message + <span class="string">&quot;&lt;br&gt;&quot;</span>;</span><br><span class="line">            logUI.scrollTop = logUI.scrollHeight;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">ShowInMessageUI</span>(<span class="params">message</span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.messageUI.value = <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">var</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">        <span class="keyword">var</span> time = date.toLocaleTimeString();</span><br><span class="line">        <span class="built_in">this</span>.messageBuffer.push(&#123; <span class="attr">message</span>: message, <span class="attr">time</span>: time &#125;);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.messageBuffer.length; i++) &#123;</span><br><span class="line">            <span class="built_in">this</span>.messageUI.value += <span class="built_in">this</span>.messageBuffer[i].time + <span class="string">&quot; | &quot;</span> + bytes2str(<span class="built_in">this</span>.messageBuffer[i].message) + <span class="string">&quot;\n&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// scroll to the bottom</span></span><br><span class="line">        <span class="built_in">this</span>.messageUI.scrollTop = <span class="built_in">this</span>.messageUI.scrollHeight;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">Update</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.UpdateFrameSenderUI();</span><br><span class="line">        <span class="built_in">this</span>.UpdateReceiveBinBufferUI();</span><br><span class="line">        <span class="built_in">this</span>.UpdateFrameReceiveBufferUI();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">UpdateFrameSenderUI</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.frameSenderUI.innerHTML = <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="comment">// remove all child</span></span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">this</span>.frameSenderUI.firstChild) &#123;</span><br><span class="line">            <span class="built_in">this</span>.frameSenderUI.removeChild(<span class="built_in">this</span>.frameSenderUI.firstChild);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.host.frameSender.length; i++) &#123;</span><br><span class="line">            <span class="keyword">var</span> frSenderInfo = <span class="built_in">this</span>.host.frameSender[i];</span><br><span class="line">            <span class="keyword">var</span> frameUI = <span class="built_in">document</span>.createElement(<span class="string">&quot;div&quot;</span>);</span><br><span class="line">            frameUI.className = <span class="string">&quot;frame&quot;</span>;</span><br><span class="line">            frameUI.innerHTML = FrameHelper.DisplayFrameInText(frSenderInfo.frame);</span><br><span class="line">            <span class="comment">// adjust text size</span></span><br><span class="line">            frameUI.style.fontSize = <span class="number">3</span> + <span class="string">&quot;px&quot;</span>;</span><br><span class="line">            <span class="comment">// adjust text color</span></span><br><span class="line">            <span class="keyword">if</span> (frSenderInfo.status == FrameSenderStatus.Sending) &#123;</span><br><span class="line">                frameUI.style.color = <span class="string">&quot;rgb(51, 204, 255)&quot;</span>; <span class="comment">// blue</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (frSenderInfo.status == FrameSenderStatus.Acked) &#123;</span><br><span class="line">                frameUI.style.color = <span class="string">&quot;rgb(51, 255, 0)&quot;</span>; <span class="comment">// green</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (frSenderInfo.status == FrameSenderStatus.Queuing) &#123;</span><br><span class="line">                frameUI.style.color = <span class="string">&quot;white&quot;</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (frSenderInfo.status == FrameSenderStatus.Timeout) &#123;</span><br><span class="line">                frameUI.style.color = <span class="string">&quot;rgb(255, 51, 51)&quot;</span>; <span class="comment">// red</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (frSenderInfo.status == FrameSenderStatus.WaitingAck) &#123;</span><br><span class="line">                frameUI.style.color = <span class="string">&quot;rgb(255, 236, 139)&quot;</span>; <span class="comment">// yellow</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">this</span>.frameSenderUI.appendChild(frameUI);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">UpdateReceiveBinBufferUI</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="comment">// Update errorArr, drop expired errors</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.errorArr.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.errorArr[i] &lt; <span class="built_in">this</span>.host.processedBitCnt) &#123;</span><br><span class="line">                <span class="built_in">this</span>.errorArr.splice(i, <span class="number">1</span>);</span><br><span class="line">                i--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">var</span> binText = binArray2CubeText(<span class="built_in">this</span>.host.receiveBinBuffer);</span><br><span class="line">        <span class="built_in">this</span>.receiveBinBufferUI.innerHTML = <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="comment">// remove all child</span></span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">this</span>.receiveBinBufferUI.firstChild) &#123;</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBufferUI.removeChild(<span class="built_in">this</span>.receiveBinBufferUI.firstChild);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; binText.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.errorArr.indexOf(i + <span class="built_in">this</span>.host.processedBitCnt) == -<span class="number">1</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.receiveBinBufferUI.innerHTML += binText[i];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// append span child</span></span><br><span class="line">                <span class="keyword">var</span> span = <span class="built_in">document</span>.createElement(<span class="string">&quot;span&quot;</span>);</span><br><span class="line">                span.className = <span class="string">&quot;error&quot;</span>;</span><br><span class="line">                span.innerHTML = binText[i];</span><br><span class="line">                <span class="built_in">this</span>.receiveBinBufferUI.appendChild(span);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (i % <span class="number">8</span> == <span class="number">7</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.receiveBinBufferUI.innerHTML += <span class="string">&quot; &quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// scroll to bottom</span></span><br><span class="line">        <span class="built_in">this</span>.receiveBinBufferUI.scrollTop = <span class="built_in">this</span>.receiveBinBufferUI.scrollHeight;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.errorArr.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBufferUI.style.border = <span class="string">&quot;3px solid green&quot;</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">this</span>.errorArr.length &gt; <span class="number">0</span> &amp;&amp; <span class="built_in">this</span>.errorArr.length &lt; <span class="number">8</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.receiveBinBufferUI.style.border = <span class="string">&quot;3px solid red&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="title">UpdateFrameReceiveBufferUI</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.frameReceiverUI.innerHTML = <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="comment">// remove all child</span></span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">this</span>.frameReceiverUI.firstChild) &#123;</span><br><span class="line">            <span class="built_in">this</span>.frameReceiverUI.removeChild(<span class="built_in">this</span>.frameReceiverUI.firstChild);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.host.receiveFrameBuffer.length; i++) &#123;</span><br><span class="line">            <span class="keyword">var</span> extractedFrame = <span class="built_in">this</span>.host.receiveFrameBuffer[i];</span><br><span class="line">            <span class="keyword">var</span> frameUI = <span class="built_in">document</span>.createElement(<span class="string">&quot;div&quot;</span>);</span><br><span class="line">            frameUI.className = <span class="string">&quot;tempFrame&quot;</span>;</span><br><span class="line">            <span class="keyword">if</span> (extractedFrame == <span class="literal">null</span>) &#123;</span><br><span class="line">                frameUI.innerHTML = <span class="string">&quot;&#123; ----------------------------------- &#125;&quot;</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">var</span> dataStr = bytes2str(extractedFrame.data);</span><br><span class="line">                <span class="keyword">if</span> (dataStr.length &gt; <span class="number">25</span>) &#123;</span><br><span class="line">                    dataStr = dataStr.slice(<span class="number">0</span>, <span class="number">25</span>) + <span class="string">&quot;...&quot;</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                frameUI.innerHTML = <span class="string">&quot;&#123; &quot;</span> + dataStr + <span class="string">&quot; &#125;&quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            frameUI.style.textAlign = <span class="string">&quot;center&quot;</span>;</span><br><span class="line">            frameUI.style.fontSize = <span class="number">5</span> + <span class="string">&quot;px&quot;</span>;</span><br><span class="line">            <span class="built_in">this</span>.frameReceiverUI.appendChild(frameUI);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><p>For <code>index.html</code></p><div class='spoiler collapsed'>    <div class='spoiler-title'>        index.html    </div>    <div class='spoiler-content'>        <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Selective Repeat ARQ<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- make background black --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="css">        <span class="selector-tag">body</span> &#123;</span></span><br><span class="line"><span class="css">            <span class="attribute">background-color</span>: <span class="number">#23252F</span>;</span></span><br><span class="line"><span class="css">            <span class="attribute">color</span>: <span class="number">#AAC5FF</span>;</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="css">        <span class="selector-tag">span</span> &#123;</span></span><br><span class="line"><span class="css">            <span class="attribute">color</span>: <span class="built_in">rgb</span>(<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>);</span></span><br><span class="line">        &#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;main&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:80%;margin: 0 auto;&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">center</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;border: 2px dotted #575757;&quot;</span>&gt;</span>Selective Repeat ARQ Simulation<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;https://kyriota.com/2023/01/03/SelectiveRepeatARQ-Simulation/&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;color: #F378BF;&quot;</span>&gt;</span>click here for help and detail<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">center</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host0&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:50%;float:left;&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">center</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>User Input<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span><span class="tag">&lt;<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">&quot;host0_userInput&quot;</span> <span class="attr">cols</span>=<span class="string">&quot;60&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">rows</span>=<span class="string">&quot;4&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;background-color: #191b20;color:white;&quot;</span>&gt;</span>@kyriota My name is kira yoshikake, 33 years old. Living in the villa area northeast of duwangting, unmarried. I work in Guiyou chain store. Every day I have to work overtime until 8 p.m. to go home. I don&#x27;t smoke. The wine is only for a taste. Sleep at 11 p.m. for 8 hours a day. Before I go to bed, I must drink a cup of warm milk, then do 20 minutes of soft exercise, get on the bed, and immediately fall asleep. Never leave fatigue and stress until the next day. Doctors say I&#x27;m normal. JOJO, people&#x27;s ability is limited. I learned one thing from my short life... The more scheming I am, the more I will find that human ability is limited... Unless I surpass human. I&#x27;m going to give up my human identity! JOJO! &quot;I don&#x27;t want violent happiness Instead, there is no deep despair To pursue a plant like life and live a peaceful life is my goal... &quot;<span class="tag">&lt;/<span class="name">textarea</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">button</span> <span class="attr">id</span>=<span class="string">&quot;host0_sendButton&quot;</span>&gt;</span>Send<span class="tag">&lt;/<span class="name">button</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Frame Sender Buffer<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host0_frameSenderBuffer&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:68px;border:1px solid white;overflow-y:scroll;text-align: left;padding: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host0_frameSenderBufferLog&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:72px;border:1px dotted rgb(63, 63, 63);text-align: left;padding: 2px;overflow-y: auto;font-size: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Receive Bin Buffer<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host0_receiveBinBuffer&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:72px;border:1px solid white;overflow-y:scroll;text-align: left;padding: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host0_receiveBinBufferLog&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:72px;border:1px dotted rgb(63, 63, 63);text-align: left;padding: 2px;overflow-y: auto;font-size: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Frame Receiver Buffer<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host0_frameReceiverBuffer&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:90%;height:40px;text-align: left;padding: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Message<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">br</span>&gt;</span><span class="tag">&lt;<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">&quot;host0_message&quot;</span> <span class="attr">cols</span>=<span class="string">&quot;60&quot;</span> <span class="attr">rows</span>=<span class="string">&quot;5&quot;</span> <span class="attr">disabled</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;background-color: #23252F;color:white;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">center</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host1&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:50%;float:left;&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">center</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>User Input<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span><span class="tag">&lt;<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">&quot;host1_userInput&quot;</span> <span class="attr">cols</span>=<span class="string">&quot;60&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">rows</span>=<span class="string">&quot;4&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;background-color: #191b20;color:white;&quot;</span>&gt;</span>@kyriota &quot;When I was in primary school, there were dozens of signatures on my classmates&#x27; graduation album, but I didn&#x27;t have them. My mother had them, my father had them, but I didn&#x27;t. There were tens of thousands of fans of pop stars and movie stars, but I didn&#x27;t have them.&quot; &quot;In my life, it&#x27;s impossible to meet real friends. It&#x27;s impossible for me to connect with people who can&#x27;t see the green emperor.&quot; &quot;It&#x27;s 5:15 in Cairo now. Japan has jet lag. It should be midnight. What are my parents doing? Have you slept? I want you to worry, sorry... &quot; &quot;The last emerald spray... I have... Tried my best... This is me, the last rumor... Mr. josda... Please accept it.&quot; ------Hua Jing Yuan Dian Ming Hua Jing Yuan Dian Ming Death. With a regretless smile on his face, he ended his life at the age of 17<span class="tag">&lt;/<span class="name">textarea</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">button</span> <span class="attr">id</span>=<span class="string">&quot;host1_sendButton&quot;</span>&gt;</span>Send<span class="tag">&lt;/<span class="name">button</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Frame Sender Buffer<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host1_frameSenderBuffer&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:68px;border:1px solid white;overflow-y:scroll;text-align: left;padding: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host1_frameSenderBufferLog&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:72px;border:1px dotted rgb(63, 63, 63);text-align: left;padding: 2px;overflow-y: auto;font-size: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Receive Bin Buffer<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host1_receiveBinBuffer&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:72px;border:1px solid white;overflow-y:scroll;text-align: left;padding: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host1_receiveBinBufferLog&quot;</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;width:90%;height:72px;border:1px dotted rgb(63, 63, 63);text-align: left;padding: 2px;overflow-y: auto;font-size: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Frame Receiver Buffer<span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;host1_frameReceiverBuffer&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:90%;height:40px;text-align: left;padding: 2px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;height: 0px;color: yellow;&quot;</span>&gt;</span>Message<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">br</span>&gt;</span><span class="tag">&lt;<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">&quot;host1_message&quot;</span> <span class="attr">cols</span>=<span class="string">&quot;60&quot;</span> <span class="attr">rows</span>=<span class="string">&quot;5&quot;</span> <span class="attr">disabled</span></span></span><br><span class="line"><span class="tag">                    <span class="attr">style</span>=<span class="string">&quot;background-color: #23252F;color:white;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">center</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;ARQ_Simulator.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">        <span class="comment">// resize the receive bin buffer divs when the window is resized</span></span></span><br><span class="line"><span class="javascript">        <span class="function"><span class="keyword">function</span> <span class="title">ResizeUI</span>(<span class="params"></span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">            <span class="keyword">var</span> temp = <span class="number">121</span>;</span></span><br><span class="line"><span class="javascript">            <span class="keyword">var</span> base = <span class="built_in">window</span>.innerWidth / <span class="number">3</span>;</span></span><br><span class="line"><span class="javascript">            <span class="comment">// resize all the divs</span></span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_receiveBinBuffer&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_receiveBinBuffer&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_frameSenderBuffer&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_frameSenderBuffer&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_frameReceiverBuffer&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_frameReceiverBuffer&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_frameSenderBufferLog&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_frameSenderBufferLog&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_receiveBinBufferLog&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_receiveBinBufferLog&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_userInput&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_userInput&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host0_message&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">document</span>.getElementById(<span class="string">&quot;host1_message&quot;</span>).style.width = (base - base % temp) + <span class="string">&quot;px&quot;</span>;</span></span><br><span class="line">        &#125;</span><br><span class="line"><span class="javascript">        <span class="built_in">window</span>.addEventListener(<span class="string">&quot;resize&quot;</span>, ResizeUI);</span></span><br><span class="line">        ResizeUI();</span><br><span class="line">    <span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>    </div></div><h2 id="postscript">Postscript</h2><p>I highly recommend using emoji or something intuitive in your log text when possible, which can increase your reading speed of log text significantly</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;My homework in a course about computer network, coded in javascript in order to put it on my blog and give it a HTML UI to show the detailed process.&lt;/p&gt;
&lt;p&gt;Just a simple version of selective repeat ARQ, but maybe helpful for you to understand the algorithm further.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#39;/images/ARQ_preview.png&#39; style=&quot;zoom:40%;&quot; &gt;&lt;/p&gt;</summary>
    
    
    
    <category term="课内" scheme="https://kyriota.github.io/categories/%E8%AF%BE%E5%86%85/"/>
    
    
  </entry>
  
  <entry>
    <title>One Pixel Attack With Differential Evolution</title>
    <link href="https://kyriota.github.io/2022/12/12/OnePixelAttackWithDifferentialEvolution/"/>
    <id>https://kyriota.github.io/2022/12/12/OnePixelAttackWithDifferentialEvolution/</id>
    <published>2022-12-13T03:30:00.000Z</published>
    <updated>2024-01-10T01:20:11.247Z</updated>
    
    <content type="html"><![CDATA[<p>RCTF出了个OnePixelAttack的题，很早就看见过这个攻击了，于是尝试一下</p><span id="more"></span><!--toc--><h1 id="one-pixel-attack-with-differential-evolution">One Pixel Attack With Differential Evolution</h1><p>OnePixelAttack顾名思义，只修改一个像素来骗过Classfier，玩的非常极限，一开始我以为这样的攻击一定离不开梯度，但居然是用了差分演化这一优化算法，可以完全做到BlackBox攻击，只需要知道Classfier给出的Probability与Label即可</p><p>幸运的是，使用差分演化的OnePixelAttack在李宏毅的Channel上有过讲解，<a href="https://www.youtube.com/watch?v=tfpKIZIWidA">视频在此</a>，看两遍差分演化的部分后就可以尝试自己搓一个来解题了</p><h2 id="题目复述">题目复述</h2><p>本次RCTF给出的被攻击的model是ResNet50，分类结果的类别数量高达1000个，需要进行扰动的样本是一张60x60的猫咪图片，需要对其进行一个像素的扰动使其被分类为不是猫咪的类别，以下为原题前端代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, render_template, request</span><br><span class="line"><span class="keyword">from</span> torchvision.models <span class="keyword">import</span> resnet50, ResNet50_Weights</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">from</span> flag <span class="keyword">import</span> flag</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">checkImg</span>(<span class="params">Img</span>):</span></span><br><span class="line">    im = Image.<span class="built_in">open</span>(<span class="string">&#x27;static/start.png&#x27;</span>).convert(<span class="string">&#x27;RGB&#x27;</span>)</span><br><span class="line">    Img = Img.convert(<span class="string">&#x27;RGB&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> Img.size != (<span class="number">60</span>, <span class="number">60</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">60</span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">60</span>):</span><br><span class="line">            <span class="keyword">if</span> im.getpixel((i,j)) != Img.getpixel((i,j)):</span><br><span class="line">                count += <span class="number">1</span></span><br><span class="line">    <span class="keyword">if</span> count == <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">divide</span>(<span class="params">img</span>):</span></span><br><span class="line">  <span class="comment"># Step 1: Initialize model with the best available weights</span></span><br><span class="line">  weights = ResNet50_Weights.DEFAULT</span><br><span class="line">  model = resnet50(weights=weights)</span><br><span class="line">  model.<span class="built_in">eval</span>()</span><br><span class="line"></span><br><span class="line">  <span class="comment"># Step 2: Initialize the inference transforms</span></span><br><span class="line">  preprocess = weights.transforms()</span><br><span class="line"></span><br><span class="line">  <span class="comment"># Step 3: Apply inference preprocessing transforms</span></span><br><span class="line">  batch = preprocess(img).unsqueeze(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">  <span class="comment"># Step 4: Use the model and print the predicted category</span></span><br><span class="line">  prediction = model(batch).squeeze(<span class="number">0</span>).softmax(<span class="number">0</span>)</span><br><span class="line">  class_id = prediction.argmax().item()</span><br><span class="line">  score = prediction[class_id].item()</span><br><span class="line">  category_name = weights.meta[<span class="string">&quot;categories&quot;</span>][class_id]</span><br><span class="line">  <span class="keyword">return</span> category_name,score</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>, <span class="string">&#x27;GET&#x27;</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">welcome</span>():</span></span><br><span class="line">    <span class="keyword">return</span> render_template(<span class="string">&quot;index.html&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/upload&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>, <span class="string">&#x27;GET&#x27;</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload</span>():</span></span><br><span class="line">    <span class="keyword">if</span> request.method == <span class="string">&#x27;POST&#x27;</span>:</span><br><span class="line">        f = request.files[<span class="string">&#x27;file&#x27;</span>]</span><br><span class="line">        im = Image.<span class="built_in">open</span>(f)</span><br><span class="line">        <span class="keyword">if</span> checkImg(im) == <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">return</span> render_template(<span class="string">&#x27;upload.html&#x27;</span>, error=<span class="string">&quot;image format error! the image size must be 60 x 60 and you can only change one pixel!&quot;</span>)</span><br><span class="line">        category_name,score = divide(im)</span><br><span class="line">        <span class="keyword">if</span> category_name == <span class="string">&#x27;tabby&#x27;</span> <span class="keyword">or</span> <span class="string">&quot;cat&quot;</span> <span class="keyword">in</span> category_name:</span><br><span class="line">            <span class="keyword">return</span> render_template(<span class="string">&#x27;upload.html&#x27;</span>, res=category_name + <span class="string">&quot;  &quot;</span> + <span class="built_in">str</span>(score))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> render_template(<span class="string">&#x27;upload.html&#x27;</span>, flag=flag)</span><br><span class="line">    <span class="keyword">return</span> render_template(<span class="string">&#x27;upload.html&#x27;</span>,error=<span class="string">&#x27;please start attack!&#x27;</span>)</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    app.run(host=<span class="string">&#x27;0.0.0.0&#x27;</span>, port=<span class="number">5000</span>)</span><br></pre></td></tr></table></figure><p>需要进行扰动的样本：</p><p><img src="/images/OnePixelAttackWithDE_Sample.png" /></p><h2 id="差分演化">差分演化</h2><p>首先认识差分演化，简称DE</p><center><code>DE(func, cutoff, popSize, mut, CR, maxIter)</code></center><p>观察上述函数参数：</p><ul><li><code>func</code>：目标函数</li><li><code>cutoff</code>：阈值</li><li><code>popSize</code>：族群大小</li><li><code>mut</code>：进化参数</li><li><code>CR</code>：杂交参数</li><li><code>maxIter</code>：最大迭代次数</li></ul><p>我们期望上述函数在<code>maxIter</code>内找到一个向量<code>X</code>，使<code>func(X)</code>的值大于或小于（这取决于结果的需求）<code>cutoff</code></p><h3 id="具体流程">具体流程</h3><p>在此简述DE的流程，以免遗忘</p><blockquote><p>现有一名为<code>func</code>的黑盒/白盒函数，其接受向量X作为参数</p><p>现使用<code>DE()</code>求使得<code>func()</code>尽可能小的向量</p></blockquote><ul><li>随机初始化<code>popSize</code>个向量作为族群<code>pop</code>，这些向量符合<code>func</code>对输入向量的要求</li><li>当族群<code>pop</code>中不存在任何一个向量<code>x</code>满足<code>func(x)&lt;cutoff</code>：<ul><li>使族群<code>pop</code>中的每一个向量<code>x</code>作为杂交父向量之一：<ul><li>随机选取3个<code>pop</code>中的向量<code>a,b,c</code>，且他们与<code>x</code>不相等</li><li>令另一杂交父向量<code>mutant=a+mut*(b-c)</code></li><li>随机生成长度与<code>x</code>相同的仅包含<code>True,False</code>的向量，每个元素为<code>True</code>的可能性为<code>CR</code>，称此01向量为<code>crossOver</code></li><li>使<code>mutant</code>与<code>x</code>进行杂交，生成的杂交子向量为<code>child=x*(1-crossOver)+mutant*crossOver</code></li><li>如果<code>child</code>中的元素越界，有几种处理办法：<ul><li>Clip（较为保守的办法，会一定程度降低多样性）</li><li>取余（较为中庸的办法）</li><li>取随机数（较为激进的办法，增加多样性但可能导致混乱程度增加，更难收敛）</li></ul></li><li>当<code>func(child)&lt;func(x)</code><ul><li>更新族群<code>x=child</code></li></ul></li></ul></li></ul></li><li>返回满足<code>func(x)&lt;cutoff</code>的向量<code>x</code></li></ul><h2 id="调参">调参</h2><p>可以先参考这篇<a href="https://www.docin.com/p-2278988320.html">挂豆丁的中文论文</a>，里面介绍了各个参数的效果以及如何改进，但我觉得implement起来性价比最高的就是动态调整的<code>F</code>和随机生成的<code>CR</code></p><p>在此总结一下：</p><ul><li><code>F</code>∈[0,2]，最好设置成动态的</li><li><code>CR</code>∈(0,1)</li><li><code>popSize</code>：通常在50以内，但理论上越大则越有可能找到全局最优解，但太大了会导致收益不足以弥补计算速度过慢带来的损失</li><li><code>maxIter</code>：如果采用动态<code>F</code>，则需要斟酌一下，例如我的<code>F</code>就与<code>maxIter</code>有关<code>F=alpha*(np.exp(maxIter/(maxIter+iterCnt))-1)</code></li></ul><h2 id="copilot带来的问题">Copilot带来的问题</h2><p>在最开始用魔改版Copilot生成的DE解决这个题目时，经常遇到的一个问题：在某一时刻（通常是演化的初期），突然整个族群都进行了一次演化，即在一次进化中，整个族群都更新了，但在这之后，就不再有任何一个向量在演化中改变</p><blockquote><p>后记：对于上述现象，其实就是Copilot写的比较拉，在多次魔改后此bug凭空消失。感觉是因为Copilot写的代码中，对于待优化的函数定义模糊，变量关系混乱，参考的变量过于单一。详细地说，就是ResNet中的类别数量高达1000个，其Label中带有"cat"的就有十来个（不同品种的猫），也许在某一时刻，一个噪音成功更改了Label，但只是把Label从最初的Persian Cat改成了另一种Cat，这个成功的噪音顺利成为了最佳噪音；因为需要计算最佳噪音的fitness和预测结果，导致原本是Persian Cat的classID被改成另一种猫的classID；这一更改使得族群的fitness值整体大幅下降，整个族群或者绝大多数向量都接受了新的child，导致我们看到的突然大规模更新的现象，而因为另一种猫的概率本身就不高，梯度过于平缓甚至消失，DE在一片大平地上难以发挥作用，导致我们看到的在大规模更新后再无更新的现象</p><p>（找这个bug真的给我找麻了，虽然不是有意修复了bug，但我觉得bug成因应该是这样的</p></blockquote><p>虽然这个bug是Copilot造成的，但我之前也google了相关问题，找到了<a href="https://www.researchgate.net/publication/2645446_On_Stagnation_Of_The_Differential_Evolution_Algorithm">On_Stagnation_Of_The_Differential_Evolution_Algorithm</a>，解释了一下为什么DE算法中存在族群停止演化的现象</p><p><img src="/images/OnePixelAttackWithDE_Stagnant.png.png" /></p><p>感觉他说了挺多的，又感觉他什么也没说，总之在没有我这个bug的情况下，就是扩大种群规模，提供更广泛的多样性，就能使得停滞的概率减小，而停滞的成因则是陷入局部最优解（乐</p><p>我还去问了ChatGPT，都是AI应该比较互相了解（乐：</p><p><img src="/images/OnePixelAttackWithDE_GPT1.png" /></p><p><img src="/images/OnePixelAttackWithDE_GPT2.png" /></p><p>虽然点了很多次Try Again也没找出族群突然全体突变的bug，但倒是给我把另一个Copilot写的bug给我找出来了，就是target vector居然是随机取的而不是挨个儿来，毕竟这一堆东西里面<code>random</code>用的挺多的，我还真没看出来，于是立刻改掉然后做成标准的DE了</p><h2 id="输出">输出</h2><p>说实话，特别是这种看运气的程序，盯着输出终端等success就很紧张，所以记录几段输出，见证一下</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        alpha=0.4，popSize=20，maxIter=100    </div>    <div class='spoiler-content'>        <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><span class="line">(&#39;Persian cat&#39;, 0.265370637178421)</span><br><span class="line"></span><br><span class="line"> ### Starting DE Attack 1 ###</span><br><span class="line"></span><br><span class="line">Initializing Differential Evolution</span><br><span class="line">Initializing fitness:  20</span><br><span class="line"></span><br><span class="line"> # iter:  1 best fitness:  0.2845600592554547 best solution:  [ 29   4 202 195  42]</span><br><span class="line">    - F:  0.6766003777597516</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  11</span><br><span class="line">    🔵 new best fitness:  0.26558117862441577  new best solution:  [ 10  37   4 244  71]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.16487646102905273)</span><br><span class="line">    - mean fitness:  0.31238916044676446</span><br><span class="line"> # iter:  2 best fitness:  0.26558117862441577 best solution:  [ 10  37   4 244  71]</span><br><span class="line">    - F:  0.6662005326545324</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    - mean fitness:  0.3056630621358636</span><br><span class="line"> # iter:  3 best fitness:  0.26558117862441577 best solution:  [ 10  37   4 244  71]</span><br><span class="line">    - F:  0.656100186679759</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    🔵 new best fitness:  0.24916027527069673  new best solution:  [ 24  31 134 197  94]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.16068297624588013)</span><br><span class="line">    - mean fitness:  0.2983871838674531</span><br><span class="line"> # iter:  4 best fitness:  0.24916027527069673 best solution:  [ 24  31 134 197  94]</span><br><span class="line">    - F:  0.6462870241393159</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.2935906742452062</span><br><span class="line"> # iter:  5 best fitness:  0.24916027527069673 best solution:  [ 24  31 134 197  94]</span><br><span class="line">    - F:  0.6367493783259498</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.28915020487766013</span><br><span class="line"> # iter:  6 best fitness:  0.24916027527069673 best solution:  [ 24  31 134 197  94]</span><br><span class="line">    - F:  0.6274761901752574</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.2849028466225718</span><br><span class="line"> # iter:  7 best fitness:  0.24916027527069673 best solution:  [ 24  31 134 197  94]</span><br><span class="line">    - F:  0.6184569699902873</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.27997543959209</span><br><span class="line"> # iter:  8 best fitness:  0.24916027527069673 best solution:  [ 24  31 134 197  94]</span><br><span class="line">    - F:  0.6096817619774788</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    🔵 new best fitness:  0.2081426924560219  new best solution:  [ 26  28 207 212 232]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.13251474499702454)</span><br><span class="line">    - mean fitness:  0.27476091143325904</span><br><span class="line"> # iter:  9 best fitness:  0.2081426924560219 best solution:  [ 26  28 207 212 232]</span><br><span class="line">    - F:  0.6011411113590921</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.27236858464457325</span><br><span class="line"> # iter:  10 best fitness:  0.2081426924560219 best solution:  [ 26  28 207 212 232]</span><br><span class="line">    - F:  0.5928260338492048</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    🔵 new best fitness:  0.19687939991126768  new best solution:  [ 26  28 246 212 232]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.12056729197502136)</span><br><span class="line">    - mean fitness:  0.2706069266641862</span><br><span class="line"> # iter:  11 best fitness:  0.19687939991126768 best solution:  [ 26  28 246 212 232]</span><br><span class="line">    - F:  0.5847279872999954</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    🔵 new best fitness:  0.167548724275548  new best solution:  [  9  32  14 119 170]</span><br><span class="line">    🔵 prediction:  (&#39;Siamese cat&#39;, 0.06791895627975464)</span><br><span class="line">    - mean fitness:  0.2661146746744635</span><br><span class="line"> # iter:  12 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.5768388453426972</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.2630609781524981</span><br><span class="line"> # iter:  13 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.5691508728634869</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.2571228217886528</span><br><span class="line"> # iter:  14 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.561656703168862</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.25257750129821943</span><br><span class="line"> # iter:  15 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.5543493167079622</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.25171417870151347</span><br><span class="line"> # iter:  16 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.5472220212308999</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.25043370262428655</span><br><span class="line"> # iter:  17 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.5402684332726887</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.24717705660732464</span><br><span class="line"> # iter:  18 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.533482460861836</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  0</span><br><span class="line">    - mean fitness:  0.24717705660732464</span><br><span class="line"> # iter:  19 best fitness:  0.167548724275548 best solution:  [  9  32  14 119 170]</span><br><span class="line">    - F:  0.5268582873612809</span><br><span class="line">    - vec:  20</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    🔵 new best fitness:  0.12234273354988545  new best solution:  [  9  32  14  50 170]</span><br><span class="line">    🔵 prediction:  (&#39;goldfish&#39;, 0.06539428979158401)</span><br><span class="line">    - mean fitness:  0.24463605969795027</span><br><span class="line"></span><br><span class="line">🟢 Holy shit, solution found! Now it&#39;s a  goldfish with probability 0.06539428979158401</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        alpha=0.4，popSize=50，maxIter=100    </div>    <div class='spoiler-content'>        <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br></pre></td><td class="code"><pre><span class="line">(&#39;Persian cat&#39;, 0.265370637178421)</span><br><span class="line"></span><br><span class="line"> ### Starting DE Attack 1 ###</span><br><span class="line"></span><br><span class="line">Initializing Differential Evolution</span><br><span class="line">Initializing fitness:  40</span><br><span class="line"></span><br><span class="line"> # iter:  1 best fitness:  0.2792421304038726 best solution:  [ 39  15  57 207  64]</span><br><span class="line">    - F:  0.6766003777597516</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  15</span><br><span class="line">    🔵 new best fitness:  0.24962108858744614  new best solution:  [ 13  37  12  55 187]</span><br><span class="line">    🔵 prediction:  (&#39;Siamese cat&#39;, 0.1373663693666458)</span><br><span class="line">    - mean fitness:  0.3119236789265415</span><br><span class="line"> # iter:  2 best fitness:  0.24962108858744614 best solution:  [ 13  37  12  55 187]</span><br><span class="line">    - F:  0.6662005326545324</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  14</span><br><span class="line">    - mean fitness:  0.30659447693833497</span><br><span class="line"> # iter:  3 best fitness:  0.24962108858744614 best solution:  [ 13  37  12  55 187]</span><br><span class="line">    - F:  0.656100186679759</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  18</span><br><span class="line">    🔵 new best fitness:  0.2070837609935552  new best solution:  [  8  30  91  66 239]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.09145677834749222)</span><br><span class="line">    - mean fitness:  0.2969437881693011</span><br><span class="line"> # iter:  4 best fitness:  0.2070837609935552 best solution:  [  8  30  91  66 239]</span><br><span class="line">    - F:  0.6462870241393159</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  12</span><br><span class="line">    - mean fitness:  0.29271303782225006</span><br><span class="line"> # iter:  5 best fitness:  0.2070837609935552 best solution:  [  8  30  91  66 239]</span><br><span class="line">    - F:  0.6367493783259498</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  10</span><br><span class="line">    - mean fitness:  0.2910425961643341</span><br><span class="line"> # iter:  6 best fitness:  0.2070837609935552 best solution:  [  8  30  91  66 239]</span><br><span class="line">    - F:  0.6274761901752574</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  7</span><br><span class="line">    - mean fitness:  0.2881202471602592</span><br><span class="line"> # iter:  7 best fitness:  0.2070837609935552 best solution:  [  8  30  91  66 239]</span><br><span class="line">    - F:  0.6184569699902873</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  8</span><br><span class="line">    🔵 new best fitness:  0.1985895600519143  new best solution:  [  8  30  83  43 246]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.0852014571428299)</span><br><span class="line">    - mean fitness:  0.2845774589579378</span><br><span class="line"> # iter:  8 best fitness:  0.1985895600519143 best solution:  [  8  30  83  43 246]</span><br><span class="line">    - F:  0.6096817619774788</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  10</span><br><span class="line">    🔵 new best fitness:  0.1980369116354268  new best solution:  [ 19  31 221  60 219]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.12450581043958664)</span><br><span class="line">    - mean fitness:  0.2800736110439175</span><br><span class="line"> # iter:  9 best fitness:  0.1980369116354268 best solution:  [ 19  31 221  60 219]</span><br><span class="line">    - F:  0.6011411113590921</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  9</span><br><span class="line">    - mean fitness:  0.2778595529278391</span><br><span class="line"> # iter:  10 best fitness:  0.1980369116354268 best solution:  [ 19  31 221  60 219]</span><br><span class="line">    - F:  0.5928260338492048</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    - mean fitness:  0.27586468316367246</span><br><span class="line"> # iter:  11 best fitness:  0.1980369116354268 best solution:  [ 19  31 221  60 219]</span><br><span class="line">    - F:  0.5847279872999954</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  7</span><br><span class="line">    - mean fitness:  0.2737868920994515</span><br><span class="line"> # iter:  12 best fitness:  0.1980369116354268 best solution:  [ 19  31 221  60 219]</span><br><span class="line">    - F:  0.5768388453426972</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  10</span><br><span class="line">    - mean fitness:  0.27152785387224865</span><br><span class="line"> # iter:  13 best fitness:  0.1980369116354268 best solution:  [ 19  31 221  60 219]</span><br><span class="line">    - F:  0.5691508728634869</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    🔵 new best fitness:  0.19654489058302715  new best solution:  [  8  30  83  43 232]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.08681343495845795)</span><br><span class="line">    - mean fitness:  0.26959856572939317</span><br><span class="line"> # iter:  14 best fitness:  0.19654489058302715 best solution:  [  8  30  83  43 232]</span><br><span class="line">    - F:  0.561656703168862</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  8</span><br><span class="line">    - mean fitness:  0.26641814589238494</span><br><span class="line"> # iter:  15 best fitness:  0.19654489058302715 best solution:  [  8  30  83  43 232]</span><br><span class="line">    - F:  0.5543493167079622</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.2645324606302893</span><br><span class="line"> # iter:  16 best fitness:  0.19654489058302715 best solution:  [  8  30  83  43 232]</span><br><span class="line">    - F:  0.5472220212308999</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  9</span><br><span class="line">    🔵 new best fitness:  0.18593154309201054  new best solution:  [ 13  32  12  10 187]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.08130055665969849)</span><br><span class="line">    - mean fitness:  0.25822097945274436</span><br><span class="line"> # iter:  17 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.5402684332726887</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.25611360947732464</span><br><span class="line"> # iter:  18 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.533482460861836</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.2554245814695605</span><br><span class="line"> # iter:  19 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.5268582873612809</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.25354099731121094</span><br><span class="line"> # iter:  20 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.52039035635713</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    - mean fitness:  0.25284817432111595</span><br><span class="line"> # iter:  21 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.5140733575177235</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  7</span><br><span class="line">    - mean fitness:  0.24981836683436995</span><br><span class="line"> # iter:  22 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.507902213351963</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.2459833693370456</span><br><span class="line"> # iter:  23 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.5018720668016673</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.24333137386493037</span><br><span class="line"> # iter:  24 best fitness:  0.18593154309201054 best solution:  [ 13  32  12  10 187]</span><br><span class="line">    - F:  0.49597826960801883</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    🔵 new best fitness:  0.17529693761025555  new best solution:  [ 19  33 107 202  87]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.11591507494449615)</span><br><span class="line">    - mean fitness:  0.23850348221239984</span><br><span class="line"> # iter:  25 best fitness:  0.17529693761025555 best solution:  [ 19  33 107 202  87]</span><br><span class="line">    - F:  0.49021637139698715</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    🔵 new best fitness:  0.16616633877856657  new best solution:  [ 8 30 91 31 67]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.09061149507761002)</span><br><span class="line">    - mean fitness:  0.23500263207024547</span><br><span class="line"> # iter:  26 best fitness:  0.16616633877856657 best solution:  [ 8 30 91 31 67]</span><br><span class="line">    - F:  0.48458210943301233</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    🔵 new best fitness:  0.1616513744520489  new best solution:  [ 19  28   5 102  29]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.10139482468366623)</span><br><span class="line">    - mean fitness:  0.23261223865411013</span><br><span class="line"> # iter:  27 best fitness:  0.1616513744520489 best solution:  [ 19  28   5 102  29]</span><br><span class="line">    - F:  0.479071398994239</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    🔵 new best fitness:  0.14602759646368213  new best solution:  [ 16  34   1   3 205]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.07026080787181854)</span><br><span class="line">    - mean fitness:  0.22903343415091512</span><br><span class="line"> # iter:  28 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.4736803243262472</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.22653961733085454</span><br><span class="line"> # iter:  29 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.46840513013457596</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.22535609000551632</span><br><span class="line"> # iter:  30 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.46324221357937834</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.2251134257450758</span><br><span class="line"> # iter:  31 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.45818811673835536</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.22436150974099292</span><br><span class="line"> # iter:  32 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.4532395195066663</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.2225118848582497</span><br><span class="line"> # iter:  33 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.4483932329048621</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.2216588508694258</span><br><span class="line"> # iter:  34 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.44364619276804285</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.22057820814152365</span><br><span class="line"> # iter:  35 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.43899545379140953</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.21989876355437446</span><br><span class="line"> # iter:  36 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.4344381839091998</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.2197925534543174</span><br><span class="line"> # iter:  37 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.4299716589856594</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.218737456479721</span><br><span class="line"> # iter:  38 best fitness:  0.14602759646368213 best solution:  [ 16  34   1   3 205]</span><br><span class="line">    - F:  0.42559325779824</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    🔵 new best fitness:  0.14172421832336113  new best solution:  [ 19  33  30 202  87]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.08732357621192932)</span><br><span class="line">    - mean fitness:  0.21789813849754863</span><br><span class="line"> # iter:  39 best fitness:  0.14172421832336113 best solution:  [ 19  33  30 202  87]</span><br><span class="line">    - F:  0.4213004572946206</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.21529108281320078</span><br><span class="line"> # iter:  40 best fitness:  0.14172421832336113 best solution:  [ 19  33  30 202  87]</span><br><span class="line">    - F:  0.4170908281064568</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.21383756285431446</span><br><span class="line"> # iter:  41 best fitness:  0.14172421832336113 best solution:  [ 19  33  30 202  87]</span><br><span class="line">    - F:  0.4129620303039511</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    🔵 new best fitness:  0.13738378579728305  new best solution:  [ 9 34  0 26 64]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.07210958003997803)</span><br><span class="line">    - mean fitness:  0.20868932269731885</span><br><span class="line"> # iter:  42 best fitness:  0.13738378579728305 best solution:  [ 9 34  0 26 64]</span><br><span class="line">    - F:  0.40891180937645544</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.2078212516120402</span><br><span class="line"> # iter:  43 best fitness:  0.13738378579728305 best solution:  [ 9 34  0 26 64]</span><br><span class="line">    - F:  0.4049379924253286</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.20408854874112875</span><br><span class="line"> # iter:  44 best fitness:  0.13738378579728305 best solution:  [ 9 34  0 26 64]</span><br><span class="line">    - F:  0.40103848455621577</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  0</span><br><span class="line">    - mean fitness:  0.20408854874112875</span><br><span class="line"> # iter:  45 best fitness:  0.13738378579728305 best solution:  [ 9 34  0 26 64]</span><br><span class="line">    - F:  0.39721126545879254</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.20378129609016468</span><br><span class="line"> # iter:  46 best fitness:  0.13738378579728305 best solution:  [ 9 34  0 26 64]</span><br><span class="line">    - F:  0.39345438616281747</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    🔵 new best fitness:  0.1299925823986996  new best solution:  [ 19  28   5 102  82]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.07040903717279434)</span><br><span class="line">    - mean fitness:  0.20264634452323663</span><br><span class="line"> # iter:  47 best fitness:  0.1299925823986996 best solution:  [ 19  28   5 102  82]</span><br><span class="line">    - F:  0.3897659659600848</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.20154688280672417</span><br><span class="line"> # iter:  48 best fitness:  0.1299925823986996 best solution:  [ 19  28   5 102  82]</span><br><span class="line">    - F:  0.3861441894825582</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    🔵 new best fitness:  0.1224242995667737  new best solution:  [ 19  28  42  26 126]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.055999837815761566)</span><br><span class="line">    - mean fitness:  0.19660467324938508</span><br><span class="line"> # iter:  49 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.38258730392760887</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.1962674061607686</span><br><span class="line"> # iter:  50 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.37909361642187034</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  4</span><br><span class="line">    - mean fitness:  0.1943786095136602</span><br><span class="line"> # iter:  51 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3756614915157772</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    - mean fitness:  0.19027078175422502</span><br><span class="line"> # iter:  52 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.37228934880136033</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.18975930064043495</span><br><span class="line"> # iter:  53 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3689756606463516</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.18946195252865436</span><br><span class="line"> # iter:  54 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3657189500380862</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.18909707230777711</span><br><span class="line"> # iter:  55 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.36251778853110245</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.1873159199443762</span><br><span class="line"> # iter:  56 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.359370794292723</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.18381733766582328</span><br><span class="line"> # iter:  57 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.35627663024124995</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.1830537809233647</span><br><span class="line"> # iter:  58 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3532340022717441</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.18271953661824228</span><br><span class="line"> # iter:  59 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3502416575646581</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.18242792503733654</span><br><span class="line"> # iter:  60 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.34729838297288895</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  6</span><br><span class="line">    - mean fitness:  0.18039965996067622</span><br><span class="line"> # iter:  61 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3444030034830755</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.18006657009755145</span><br><span class="line"> # iter:  62 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.34155438074722033</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.179565512255067</span><br><span class="line"> # iter:  63 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3387514116809485</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.17945913075454883</span><br><span class="line"> # iter:  64 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3359930271249306</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    - mean fitness:  0.17832086661219365</span><br><span class="line"> # iter:  65 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3332781905662051</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  0</span><br><span class="line">    - mean fitness:  0.17832086661219365</span><br><span class="line"> # iter:  66 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3306058969163208</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  2</span><br><span class="line">    - mean fitness:  0.1771787651014165</span><br><span class="line"> # iter:  67 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3279751713434023</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.17541003143414854</span><br><span class="line"> # iter:  68 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.3253850681554063</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  5</span><br><span class="line">    - mean fitness:  0.17219932818989037</span><br><span class="line"> # iter:  69 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.32283466973198904</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.17194998578561355</span><br><span class="line"> # iter:  70 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.32032308550255917</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  1</span><br><span class="line">    - mean fitness:  0.1713867742641014</span><br><span class="line"> # iter:  71 best fitness:  0.1224242995667737 best solution:  [ 19  28  42  26 126]</span><br><span class="line">    - F:  0.31784945096821904</span><br><span class="line">    - vec:  40</span><br><span class="line">    - changed vec num:  3</span><br><span class="line">    🔵 new best fitness:  0.1114595357212238  new best solution:  [  9  30  15  26 105]</span><br><span class="line">    🔵 prediction:  (&#39;goldfish&#39;, 0.07046791166067123)</span><br><span class="line">    - mean fitness:  0.1694240938144503</span><br><span class="line"></span><br><span class="line">🟢 Holy shit, solution found! Now it&#39;s a  goldfish with probability 0.07046791166067123</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        alpha=0.4，popSize=200，maxIter=100    </div>    <div class='spoiler-content'>        <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line">(&#39;Persian cat&#39;, 0.265370637178421)</span><br><span class="line"></span><br><span class="line"> ### Starting DE Attack 1 ###      </span><br><span class="line"></span><br><span class="line">Initializing Differential Evolution</span><br><span class="line">Initializing fitness:  200</span><br><span class="line"></span><br><span class="line"> # iter:  1 best fitness:  0.24225685829878785 best solution:  [ 12  39  95 221 124]</span><br><span class="line">    - F:  0.6766003777597516</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  106</span><br><span class="line">    🔵 new best fitness:  0.14112568460404873  new best solution:  [ 22  32 143 101   2]</span><br><span class="line">    🔵 prediction:  (&#39;Persian cat&#39;, 0.08040182292461395)</span><br><span class="line">    - mean fitness:  0.3115253244334599</span><br><span class="line"> # iter:  2 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6662005326545324</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  70</span><br><span class="line">    - mean fitness:  0.3050887870359293</span><br><span class="line"> # iter:  3 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.656100186679759</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  63</span><br><span class="line">    - mean fitness:  0.30018534842150985</span><br><span class="line"> # iter:  4 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6462870241393159</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  61</span><br><span class="line">    - mean fitness:  0.2930651634036622</span><br><span class="line"> # iter:  5 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6367493783259498</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  48</span><br><span class="line">    - mean fitness:  0.2894190900228568</span><br><span class="line"> # iter:  6 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6274761901752574</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  45</span><br><span class="line">    - mean fitness:  0.2862749499891652</span><br><span class="line"> # iter:  7 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6184569699902873</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  34</span><br><span class="line">    - mean fitness:  0.283543661233125</span><br><span class="line"> # iter:  8 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6096817619774788</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  34</span><br><span class="line">    - mean fitness:  0.28058190911469866</span><br><span class="line"> # iter:  9 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.6011411113590921</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  34</span><br><span class="line">    - mean fitness:  0.27765514108512435</span><br><span class="line"> # iter:  10 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.5928260338492048</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  27</span><br><span class="line">    - mean fitness:  0.275277993111813</span><br><span class="line"> # iter:  11 best fitness:  0.14112568460404873 best solution:  [ 22  32 143 101   2]</span><br><span class="line">    - F:  0.5847279872999954</span><br><span class="line">    - vec:  200</span><br><span class="line">    - changed vec num:  25</span><br><span class="line">    🔵 new best fitness:  0.12863419004133902  new best solution:  [  8  31  29  26 118]</span><br><span class="line">    🔵 prediction:  (&#39;goldfish&#39;, 0.0704464316368103)</span><br><span class="line">    - mean fitness:  0.27182799993403023</span><br><span class="line"></span><br><span class="line">🟢 Holy shit, solution found! Now it&#39;s a  goldfish with probability 0.0704464316368103</span><br></pre></td></tr></table></figure>    </div></div><p>通过以上各个参数的迭代次数可见，族群大小对这个算法的收效甚微，只要不是太小的族群，在算法正确的情况下一般是没有问题的，比如上述的不同族群大小中，<code>popSize=20</code>时两三秒就可以进行一次迭代，而<code>popSize=200</code>时一次迭代就要几分钟，故而盲目追求大族群是得不偿失的，不如用小族群多跑几遍</p><p>然后是这个输出的结果，每次基本都是在猫耳朵的位置修改像素，得到goldfish的label，意味着Persian Cat的决策范围边上就是Goldfish，而且距离之近或者说梯度之大只需要一个像素就可以扰动成功</p><p><img src="/images/OnePixelAttackWithDE_Out1.png" /></p><h2 id="性能比较">性能比较</h2><p>copy了一份论文link的github上的<a href="https://github.com/Hyperparticle/one-pixel-attack-keras">代码</a>，进行了一下性能的对比，这种带运气成分的算法需要大量统计才能做出比较，直观感觉上来说，相同种群大小下，他的一次迭代比我的慢得多，他的代码和我的一样有运气好几次迭代直接出和运气差迭代半天一直不出的情况，但是从原理上说，只要一直挂着跑，失败了自动重启继续跑，都肯定可以跑出来的</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        popSize=15，maxIter=100    </div>    <div class='spoiler-content'>        <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">0.33761785909882747</span><br><span class="line"></span><br><span class="line">differential_evolution step 1: f(x)&#x3D; 0.177579</span><br><span class="line">Current solution:  [  7.89136557  29.78098292   0.66838417  26.92418736 187.89591321] Current Prediction:  (&#39;Persian cat&#39;, 0.09029071778059006)</span><br><span class="line"></span><br><span class="line">differential_evolution step 2: f(x)&#x3D; 0.177579</span><br><span class="line">Current solution:  [  7.89136557  29.78098292   0.66838417  26.92418736 187.89591321] Current Prediction:  (&#39;Persian cat&#39;, 0.09029071778059006)</span><br><span class="line"></span><br><span class="line">differential_evolution step 3: f(x)&#x3D; 0.177579</span><br><span class="line">Current solution:  [  7.89136557  29.78098292   0.66838417  26.92418736 187.89591321] Current Prediction:  (&#39;Persian cat&#39;, 0.09029071778059006)</span><br><span class="line"></span><br><span class="line">differential_evolution step 4: f(x)&#x3D; 0.177579</span><br><span class="line">Current solution:  [  7.89136557  29.78098292   0.66838417  26.92418736 187.89591321] Current Prediction:  (&#39;Persian cat&#39;, 0.09029071778059006)</span><br><span class="line"></span><br><span class="line">differential_evolution step 5: f(x)&#x3D; 0.164309</span><br><span class="line">Current solution:  [  8.16682606  31.87408249  35.702065    66.00663295 147.64079443] Current Prediction:  (&#39;Persian cat&#39;, 0.07545000314712524)</span><br><span class="line"></span><br><span class="line">differential_evolution step 6: f(x)&#x3D; 0.156934</span><br><span class="line">Current solution:  [  7.63761519  32.62248531  17.25317435   1.4109334  124.63028139] Current Prediction:  (&#39;Persian cat&#39;, 0.07208419591188431)</span><br><span class="line"></span><br><span class="line">differential_evolution step 7: f(x)&#x3D; 0.156934</span><br><span class="line">Current solution:  [  7.63761519  32.62248531  17.25317435   1.4109334  124.63028139] Current Prediction:  (&#39;Persian cat&#39;, 0.07208419591188431) </span><br><span class="line"></span><br><span class="line">differential_evolution step 8: f(x)&#x3D; 0.125927</span><br><span class="line">Solution found:  [ 8.58543511 29.28549154 35.04627611 30.05577958 80.01537897] </span><br><span class="line"></span><br><span class="line">     fun: 0.12592693569604307</span><br><span class="line"> message: &#39;callback function requested stop early by returning True&#39;</span><br><span class="line">    nfev: 681</span><br><span class="line">     nit: 8</span><br><span class="line"> success: False</span><br><span class="line">       x: array([ 8.58543511, 29.28549154, 35.04627611, 30.05577958, 80.01537897])</span><br><span class="line"></span><br><span class="line">(&#39;goldfish&#39;, 0.07302950322628021)</span><br></pre></td></tr></table></figure>    </div></div><h2 id="exp">exp</h2><div class='spoiler collapsed'>    <div class='spoiler-title'>        poc.py    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> torchvision.models <span class="keyword">import</span> resnet50, ResNet50_Weights</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="comment"># from DE import differential_evolution</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;Differential Evolution Test&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># def fooCallBack(x, convergence):</span></span><br><span class="line"><span class="comment">#     if foo(x) &lt; 5:</span></span><br><span class="line"><span class="comment">#         print(&quot;Solution found: &quot;, x, &quot;\n&quot;)</span></span><br><span class="line"><span class="comment">#         return True</span></span><br><span class="line"><span class="comment">#     else:</span></span><br><span class="line"><span class="comment">#         print(&quot;Current solution: &quot;, x, &quot;Current fitness: &quot;, foo(x), &quot;\n&quot;)</span></span><br><span class="line"><span class="comment">#         return False</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># def foo(x):</span></span><br><span class="line"><span class="comment">#     if x.ndim == 1:</span></span><br><span class="line"><span class="comment">#         x = np.array([x])</span></span><br><span class="line"><span class="comment">#     return np.sum(np.square(x), axis=1)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># solution = differential_evolution(</span></span><br><span class="line"><span class="comment">#     foo, bounds=[(-255, 255), (-255, 255), (-255, 255)], disp=True, callback=fooCallBack)</span></span><br><span class="line"><span class="comment"># print(solution)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;One Pixel Attack&#x27;&#x27;&#x27;</span></span><br><span class="line">weights = ResNet50_Weights.DEFAULT</span><br><span class="line">model = resnet50(weights=weights)</span><br><span class="line">model.<span class="built_in">eval</span>()</span><br><span class="line">preprocess = weights.transforms()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">startPath = <span class="string">&#x27;E:/DL/_media_file_task_0dddc5b6-cb34-4468-b20c-abb5d7d0befa/RCTF_catspy/static/start.png&#x27;</span></span><br><span class="line">outPath = <span class="string">&#x27;E:/DL/_media_file_task_0dddc5b6-cb34-4468-b20c-abb5d7d0befa/RCTF_catspy/out.png&#x27;</span></span><br><span class="line">startImg = Image.<span class="built_in">open</span>(startPath).convert(<span class="string">&#x27;RGB&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ChangeOnePixel</span>(<span class="params">img, pert</span>):</span></span><br><span class="line">    img = np.array(img)</span><br><span class="line">    img[<span class="built_in">int</span>(pert[<span class="number">0</span>])][<span class="built_in">int</span>(pert[<span class="number">1</span>])] = pert[<span class="number">2</span>:]</span><br><span class="line">    <span class="keyword">return</span> img</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">GetScore</span>(<span class="params">img</span>):</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(img, np.ndarray):</span><br><span class="line">        img = Image.fromarray(img.astype(<span class="string">&#x27;uint8&#x27;</span>)).convert(<span class="string">&#x27;RGB&#x27;</span>)</span><br><span class="line">    batch = preprocess(img).unsqueeze(<span class="number">0</span>)</span><br><span class="line">    prediction = model(batch).squeeze(<span class="number">0</span>).softmax(<span class="number">0</span>)</span><br><span class="line">    score = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> [<span class="number">143</span>, <span class="number">282</span>, <span class="number">283</span>, <span class="number">284</span>, <span class="number">285</span>, <span class="number">358</span>, <span class="number">383</span>, <span class="number">484</span>]:</span><br><span class="line">        score += prediction[i].item()</span><br><span class="line">    <span class="keyword">return</span> score</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">Predict</span>(<span class="params">img</span>):</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(img, np.ndarray):</span><br><span class="line">        img = Image.fromarray(img.astype(<span class="string">&#x27;uint8&#x27;</span>)).convert(<span class="string">&#x27;RGB&#x27;</span>)</span><br><span class="line">    batch = preprocess(img).unsqueeze(<span class="number">0</span>)</span><br><span class="line">    prediction = model(batch).squeeze(<span class="number">0</span>).softmax(<span class="number">0</span>)</span><br><span class="line">    class_id = prediction.argmax().item()</span><br><span class="line">    score = prediction[class_id].item()</span><br><span class="line">    category_name = weights.meta[<span class="string">&quot;categories&quot;</span>][class_id]</span><br><span class="line">    <span class="keyword">return</span> category_name, score</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">GetPertedScore</span>(<span class="params">perts</span>):</span></span><br><span class="line">    <span class="keyword">if</span> perts.ndim == <span class="number">1</span>:</span><br><span class="line">        perts = np.array([perts])</span><br><span class="line">    res = []</span><br><span class="line">    <span class="keyword">for</span> pert <span class="keyword">in</span> perts:</span><br><span class="line">        img = ChangeOnePixel(startImg, pert)</span><br><span class="line">        img = Image.fromarray(img.astype(<span class="string">&#x27;uint8&#x27;</span>)).convert(<span class="string">&#x27;RGB&#x27;</span>)</span><br><span class="line">        res.append(GetScore(img))</span><br><span class="line">    <span class="keyword">return</span> np.array(res)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">OPACallBack</span>(<span class="params">x, convergence</span>):</span></span><br><span class="line">    prediction = Predict(ChangeOnePixel(startImg, x))</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;cat&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> prediction[<span class="number">0</span>]:</span><br><span class="line">        print(<span class="string">&quot;Solution found: &quot;</span>, x, <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        print(<span class="string">&quot;Current solution: &quot;</span>, x, <span class="string">&quot;Current Prediction: &quot;</span>, prediction, <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">Solution for Catspy Using Differential Evolution From</span></span><br><span class="line"><span class="string">https://github.com/Hyperparticle/one-pixel-attack-keras/tree/8261f976be922bd9b470040367a34d82fcd506d4</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># print(GetScore(startImg), &quot;\n&quot;)</span></span><br><span class="line"><span class="comment"># solution = differential_evolution(GetPertedScore, bounds=[(0,59), (0,59), (0,255), (0,255), (0,255)], maxiter=100, popsize=15, disp=True, callback=OPACallBack)</span></span><br><span class="line"><span class="comment"># print(solution)</span></span><br><span class="line"><span class="comment"># print(Predict(ChangeOnePixel(startImg, solution.x)))</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;Hand Made Differential Evolution Attack&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">OPA</span>(<span class="params">img, pop_size, maxIter, alpha=<span class="number">0.4</span></span>):</span></span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    img: the image to be attacked</span></span><br><span class="line"><span class="string">    pop_size: the size of the population</span></span><br><span class="line"><span class="string">    maxIter: the maximum number of iterations</span></span><br><span class="line"><span class="string">    alpha: the mutation factor, [0.2, 0.6]</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line">    <span class="keyword">global</span> attackCnt</span><br><span class="line">    attackCnt = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">init</span>():</span></span><br><span class="line">        <span class="keyword">global</span> attackCnt</span><br><span class="line">        attackCnt += <span class="number">1</span></span><br><span class="line">        print(<span class="string">&quot;\n ### Starting DE Attack&quot;</span>, attackCnt, <span class="string">&quot;###\n&quot;</span>)</span><br><span class="line">        <span class="comment"># Step 5.1: Initialize the population</span></span><br><span class="line">        print(<span class="string">&quot;Initializing Differential Evolution&quot;</span>)</span><br><span class="line">        pop = np.concatenate((np.random.randint(<span class="number">0</span>, <span class="number">60</span>, (pop_size, <span class="number">2</span>)), np.random.randint(<span class="number">0</span>, <span class="number">256</span>, (pop_size, <span class="number">3</span>))), axis=<span class="number">1</span>)</span><br><span class="line">        pop = np.array(pop, dtype=np.uint8)</span><br><span class="line">        <span class="comment"># Step 5.2: Initialize the fitness</span></span><br><span class="line">        fitness = np.zeros(pop_size)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(pop_size):</span><br><span class="line">            <span class="keyword">if</span> i % <span class="number">10</span> == <span class="number">9</span>:</span><br><span class="line">                print(<span class="string">&quot;\rInitializing fitness: &quot;</span>, i+<span class="number">1</span>, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line">            fitness[i] = GetScore(ChangeOnePixel(img, pop[i]))</span><br><span class="line">        print(<span class="string">&quot;\n&quot;</span>)</span><br><span class="line">        <span class="comment"># Step 5.3: Initialize the best fitness</span></span><br><span class="line">        best_fitness = np.<span class="built_in">min</span>(fitness)</span><br><span class="line">        <span class="comment"># Step 5.4: Initialize the best solution</span></span><br><span class="line">        best_solution = pop[fitness.argmin()]</span><br><span class="line">        best_prediction = Predict(ChangeOnePixel(img, best_solution))</span><br><span class="line">        <span class="comment"># Step 5.5: Initialize the iteration</span></span><br><span class="line">        iterCnt = <span class="number">0</span></span><br><span class="line">        noVecChangedCnt = <span class="number">0</span></span><br><span class="line">        <span class="keyword">return</span> pop, fitness, best_fitness, best_solution, iterCnt, noVecChangedCnt, best_prediction, [], []</span><br><span class="line"></span><br><span class="line">    print(Predict(img))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Step 5: Differential Evolution Attack</span></span><br><span class="line">    pop, fitness, best_fitness, best_solution, iterCnt, noVecChangedCnt, best_prediction, popHistory, fitnessHistory = init()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> noVecChangedCnt == <span class="number">5</span> <span class="keyword">or</span> iterCnt == maxIter:</span><br><span class="line">            pop, fitness, best_fitness, best_solution, iterCnt, noVecChangedCnt, best_prediction, popHistory, fitnessHistory = init()</span><br><span class="line"></span><br><span class="line">        iterCnt += <span class="number">1</span></span><br><span class="line">        lastPop = np.array(pop)</span><br><span class="line">        popHistory.append(np.array(pop))</span><br><span class="line">        fitnessHistory.append(np.array(fitness))</span><br><span class="line">        changedVec = <span class="number">0</span></span><br><span class="line">        print(<span class="string">&quot; # iter: &quot;</span>, iterCnt, <span class="string">&quot;best fitness: &quot;</span>, best_fitness, <span class="string">&quot;best solution: &quot;</span>, best_solution)</span><br><span class="line">        F = alpha * (np.exp(maxIter / (maxIter + iterCnt)) - <span class="number">1</span>)</span><br><span class="line">        print(<span class="string">&quot;    - F: &quot;</span>, F)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(pop_size):</span><br><span class="line">            <span class="keyword">if</span> i % <span class="number">10</span> == <span class="number">9</span>:</span><br><span class="line">                print(<span class="string">&quot;\r    - vec: &quot;</span>, i+<span class="number">1</span>, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line">            <span class="comment"># Step 5.5.2: Initialize the mutation vector</span></span><br><span class="line">            mutation = np.random.randint(<span class="number">0</span>, pop_size, <span class="number">3</span>)</span><br><span class="line">            <span class="keyword">while</span> i <span class="keyword">in</span> mutation:</span><br><span class="line">                mutation = np.random.randint(<span class="number">0</span>, pop_size, <span class="number">3</span>)</span><br><span class="line">            <span class="comment"># Step 5.5.3: Initialize the crossover vector</span></span><br><span class="line">            CR = <span class="number">0.25</span> + np.random.rand() * <span class="number">0.5</span></span><br><span class="line">            crossover = np.random.rand(<span class="number">5</span>) &lt; CR</span><br><span class="line">            <span class="comment"># Step 5.5.4: Initialize the trial vector</span></span><br><span class="line">            trial = np.array(lastPop[i] * (<span class="number">1</span> - crossover) + (lastPop[mutation[<span class="number">0</span>]] + F * (lastPop[mutation[<span class="number">1</span>]] - lastPop[mutation[<span class="number">2</span>]])) * crossover, dtype=np.uint8)</span><br><span class="line">            <span class="keyword">for</span> trialIndex <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>):</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> <span class="number">0</span> &lt;= trial[trialIndex] &lt;= <span class="number">59</span>:</span><br><span class="line">                    trial[trialIndex] = np.random.randint(<span class="number">0</span>, <span class="number">60</span>)</span><br><span class="line">            <span class="keyword">for</span> trialIndex <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, <span class="number">5</span>):</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> <span class="number">0</span> &lt;= trial[trialIndex] &lt;= <span class="number">255</span>:</span><br><span class="line">                    trial[trialIndex] = np.random.randint(<span class="number">0</span>, <span class="number">256</span>)</span><br><span class="line">            <span class="comment"># Step 5.5.5: Update the population</span></span><br><span class="line">            newFitness = GetScore(ChangeOnePixel(img, trial))</span><br><span class="line">            <span class="keyword">if</span> newFitness &lt; fitness[i]:</span><br><span class="line">                pop[i] = np.array(trial, dtype=np.uint8)</span><br><span class="line">                fitness[i] = newFitness</span><br><span class="line">                changedVec += <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> changedVec &gt; pop_size * <span class="number">2</span> / <span class="number">3</span>:</span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line">        print()</span><br><span class="line">        </span><br><span class="line">        print(<span class="string">&#x27;    - changed vec num: &#x27;</span>, changedVec)</span><br><span class="line">        noVecChangedCnt += <span class="number">1</span> <span class="keyword">if</span> changedVec == <span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">        <span class="keyword">if</span> changedVec != <span class="number">0</span>:</span><br><span class="line">            noVecChangedCnt = <span class="number">0</span></span><br><span class="line">            </span><br><span class="line">        <span class="comment"># Step 5.7: Update the best fitness</span></span><br><span class="line">        <span class="keyword">if</span> np.<span class="built_in">min</span>(fitness) &lt; best_fitness:</span><br><span class="line">            best_fitness = np.<span class="built_in">min</span>(fitness)</span><br><span class="line">            best_solution = pop[fitness.argmin()]</span><br><span class="line">            print(<span class="string">&#x27;    🔵 new best fitness: &#x27;</span>, best_fitness, <span class="string">&#x27; new best solution: &#x27;</span>, best_solution)</span><br><span class="line">            best_prediction = Predict(ChangeOnePixel(img, best_solution))</span><br><span class="line">            print(<span class="string">&#x27;    🔵 prediction: &#x27;</span>, Predict(ChangeOnePixel(img, best_solution)))</span><br><span class="line">        print(<span class="string">&#x27;    - mean fitness: &#x27;</span>, np.mean(fitness))</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;cat&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> best_prediction[<span class="number">0</span>]:</span><br><span class="line">            print(<span class="string">&quot;\n🟢 Holy shit, solution found! Now it&#x27;s a&quot;</span>, best_prediction[<span class="number">0</span>], <span class="string">&quot;with probability&quot;</span>, best_prediction[<span class="number">1</span>])</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        </span><br><span class="line">    <span class="keyword">return</span> best_solution, popHistory, fitnessHistory</span><br><span class="line"></span><br><span class="line">best_solution, popHistory, fitnessHistory = OPA(startImg, <span class="number">20</span>, <span class="number">200</span>, alpha=<span class="number">0.5</span>)</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        DE.py    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br><span class="line">683</span><br><span class="line">684</span><br><span class="line">685</span><br><span class="line">686</span><br><span class="line">687</span><br><span class="line">688</span><br><span class="line">689</span><br><span class="line">690</span><br><span class="line">691</span><br><span class="line">692</span><br><span class="line">693</span><br><span class="line">694</span><br><span class="line">695</span><br><span class="line">696</span><br><span class="line">697</span><br><span class="line">698</span><br><span class="line">699</span><br><span class="line">700</span><br><span class="line">701</span><br><span class="line">702</span><br><span class="line">703</span><br><span class="line">704</span><br><span class="line">705</span><br><span class="line">706</span><br><span class="line">707</span><br><span class="line">708</span><br><span class="line">709</span><br><span class="line">710</span><br><span class="line">711</span><br><span class="line">712</span><br><span class="line">713</span><br><span class="line">714</span><br><span class="line">715</span><br><span class="line">716</span><br><span class="line">717</span><br><span class="line">718</span><br><span class="line">719</span><br><span class="line">720</span><br><span class="line">721</span><br><span class="line">722</span><br><span class="line">723</span><br><span class="line">724</span><br><span class="line">725</span><br><span class="line">726</span><br><span class="line">727</span><br><span class="line">728</span><br><span class="line">729</span><br><span class="line">730</span><br><span class="line">731</span><br><span class="line">732</span><br><span class="line">733</span><br><span class="line">734</span><br><span class="line">735</span><br><span class="line">736</span><br><span class="line">737</span><br><span class="line">738</span><br><span class="line">739</span><br><span class="line">740</span><br><span class="line">741</span><br><span class="line">742</span><br><span class="line">743</span><br><span class="line">744</span><br><span class="line">745</span><br><span class="line">746</span><br><span class="line">747</span><br><span class="line">748</span><br><span class="line">749</span><br><span class="line">750</span><br><span class="line">751</span><br><span class="line">752</span><br><span class="line">753</span><br><span class="line">754</span><br><span class="line">755</span><br><span class="line">756</span><br><span class="line">757</span><br><span class="line">758</span><br><span class="line">759</span><br><span class="line">760</span><br><span class="line">761</span><br><span class="line">762</span><br><span class="line">763</span><br><span class="line">764</span><br><span class="line">765</span><br><span class="line">766</span><br><span class="line">767</span><br><span class="line">768</span><br><span class="line">769</span><br><span class="line">770</span><br><span class="line">771</span><br><span class="line">772</span><br><span class="line">773</span><br><span class="line">774</span><br><span class="line">775</span><br><span class="line">776</span><br><span class="line">777</span><br><span class="line">778</span><br><span class="line">779</span><br><span class="line">780</span><br><span class="line">781</span><br><span class="line">782</span><br><span class="line">783</span><br><span class="line">784</span><br><span class="line">785</span><br><span class="line">786</span><br><span class="line">787</span><br><span class="line">788</span><br><span class="line">789</span><br><span class="line">790</span><br><span class="line">791</span><br><span class="line">792</span><br><span class="line">793</span><br><span class="line">794</span><br><span class="line">795</span><br><span class="line">796</span><br><span class="line">797</span><br><span class="line">798</span><br><span class="line">799</span><br><span class="line">800</span><br><span class="line">801</span><br><span class="line">802</span><br><span class="line">803</span><br><span class="line">804</span><br><span class="line">805</span><br><span class="line">806</span><br><span class="line">807</span><br><span class="line">808</span><br><span class="line">809</span><br><span class="line">810</span><br><span class="line">811</span><br><span class="line">812</span><br><span class="line">813</span><br><span class="line">814</span><br><span class="line">815</span><br><span class="line">816</span><br><span class="line">817</span><br><span class="line">818</span><br><span class="line">819</span><br><span class="line">820</span><br><span class="line">821</span><br><span class="line">822</span><br><span class="line">823</span><br><span class="line">824</span><br><span class="line">825</span><br><span class="line">826</span><br><span class="line">827</span><br><span class="line">828</span><br><span class="line">829</span><br><span class="line">830</span><br><span class="line">831</span><br><span class="line">832</span><br><span class="line">833</span><br><span class="line">834</span><br><span class="line">835</span><br><span class="line">836</span><br><span class="line">837</span><br><span class="line">838</span><br><span class="line">839</span><br><span class="line">840</span><br><span class="line">841</span><br><span class="line">842</span><br><span class="line">843</span><br><span class="line">844</span><br><span class="line">845</span><br><span class="line">846</span><br><span class="line">847</span><br><span class="line">848</span><br><span class="line">849</span><br><span class="line">850</span><br><span class="line">851</span><br><span class="line">852</span><br><span class="line">853</span><br><span class="line">854</span><br><span class="line">855</span><br><span class="line">856</span><br><span class="line">857</span><br><span class="line">858</span><br><span class="line">859</span><br><span class="line">860</span><br><span class="line">861</span><br><span class="line">862</span><br><span class="line">863</span><br><span class="line">864</span><br><span class="line">865</span><br><span class="line">866</span><br><span class="line">867</span><br><span class="line">868</span><br><span class="line">869</span><br><span class="line">870</span><br><span class="line">871</span><br><span class="line">872</span><br><span class="line">873</span><br><span class="line">874</span><br><span class="line">875</span><br><span class="line">876</span><br><span class="line">877</span><br><span class="line">878</span><br><span class="line">879</span><br><span class="line">880</span><br><span class="line">881</span><br><span class="line">882</span><br><span class="line">883</span><br><span class="line">884</span><br><span class="line">885</span><br><span class="line">886</span><br><span class="line">887</span><br><span class="line">888</span><br><span class="line">889</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> scipy._lib._util <span class="keyword">import</span> check_random_state</span><br><span class="line"><span class="keyword">from</span> scipy.optimize._optimize <span class="keyword">import</span> _status_message</span><br><span class="line"><span class="keyword">from</span> scipy.optimize <span class="keyword">import</span> OptimizeResult, minimize</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">A slight modification to Scipy&#x27;s implementation of differential evolution. To speed up predictions, the entire parameters array is passed to `self.func`, where a neural network model can batch its computations and execute in parallel. Search for `CHANGES` to find all code changes.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Dan Kondratyuk 2018</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Original code adapted from</span></span><br><span class="line"><span class="string">https://github.com/scipy/scipy/blob/70e61dee181de23fdd8d893eaa9491100e2218d7/scipy/optimize/_differentialevolution.py</span></span><br><span class="line"><span class="string">----------</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">differential_evolution: The differential evolution global optimization algorithm</span></span><br><span class="line"><span class="string">Added by Andrew Nelson 2014</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">_MACHEPS = np.finfo(np.float64).eps</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">differential_evolution</span>(<span class="params">func, bounds, args=(<span class="params"></span>), strategy=<span class="string">&#x27;best1bin&#x27;</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                           maxiter=<span class="number">1000</span>, popsize=<span class="number">15</span>, tol=<span class="number">0.01</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                           mutation=(<span class="params"><span class="number">0.5</span>, <span class="number">1</span></span>), recombination=<span class="number">0.7</span>, seed=<span class="literal">None</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                           callback=<span class="literal">None</span>, disp=<span class="literal">False</span>, polish=<span class="literal">True</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                           init=<span class="string">&#x27;latinhypercube&#x27;</span>, atol=<span class="number">0</span></span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Finds the global minimum of a multivariate function.</span></span><br><span class="line"><span class="string">    Differential Evolution is stochastic in nature (does not use gradient</span></span><br><span class="line"><span class="string">    methods) to find the minimium, and can search large areas of candidate</span></span><br><span class="line"><span class="string">    space, but often requires larger numbers of function evaluations than</span></span><br><span class="line"><span class="string">    conventional gradient based techniques.</span></span><br><span class="line"><span class="string">    The algorithm is due to Storn and Price [1]_.</span></span><br><span class="line"><span class="string">    Parameters</span></span><br><span class="line"><span class="string">    ----------</span></span><br><span class="line"><span class="string">    func : callable</span></span><br><span class="line"><span class="string">        The objective function to be minimized.  Must be in the form</span></span><br><span class="line"><span class="string">        ``f(x, *args)``, where ``x`` is the argument in the form of a 1-D array</span></span><br><span class="line"><span class="string">        and ``args`` is a  tuple of any additional fixed parameters needed to</span></span><br><span class="line"><span class="string">        completely specify the function.</span></span><br><span class="line"><span class="string">    bounds : sequence</span></span><br><span class="line"><span class="string">        Bounds for variables.  ``(min, max)`` pairs for each element in ``x``,</span></span><br><span class="line"><span class="string">        defining the lower and upper bounds for the optimizing argument of</span></span><br><span class="line"><span class="string">        `func`. It is required to have ``len(bounds) == len(x)``.</span></span><br><span class="line"><span class="string">        ``len(bounds)`` is used to determine the number of parameters in ``x``.</span></span><br><span class="line"><span class="string">    args : tuple, optional</span></span><br><span class="line"><span class="string">        Any additional fixed parameters needed to</span></span><br><span class="line"><span class="string">        completely specify the objective function.</span></span><br><span class="line"><span class="string">    strategy : str, optional</span></span><br><span class="line"><span class="string">        The differential evolution strategy to use. Should be one of:</span></span><br><span class="line"><span class="string">            - &#x27;best1bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;best1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;randtobest1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;currenttobest1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;best2exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand2exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;randtobest1bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;currenttobest1bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;best2bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand2bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand1bin&#x27;</span></span><br><span class="line"><span class="string">        The default is &#x27;best1bin&#x27;.</span></span><br><span class="line"><span class="string">    maxiter : int, optional</span></span><br><span class="line"><span class="string">        The maximum number of generations over which the entire population is</span></span><br><span class="line"><span class="string">        evolved. The maximum number of function evaluations (with no polishing)</span></span><br><span class="line"><span class="string">        is: ``(maxiter + 1) * popsize * len(x)``</span></span><br><span class="line"><span class="string">    popsize : int, optional</span></span><br><span class="line"><span class="string">        A multiplier for setting the total population size.  The population has</span></span><br><span class="line"><span class="string">        ``popsize * len(x)`` individuals (unless the initial population is</span></span><br><span class="line"><span class="string">        supplied via the `init` keyword).</span></span><br><span class="line"><span class="string">    tol : float, optional</span></span><br><span class="line"><span class="string">        Relative tolerance for convergence, the solving stops when</span></span><br><span class="line"><span class="string">        ``np.std(pop) &lt;= atol + tol * np.abs(np.mean(population_energies))``,</span></span><br><span class="line"><span class="string">        where and `atol` and `tol` are the absolute and relative tolerance</span></span><br><span class="line"><span class="string">        respectively.</span></span><br><span class="line"><span class="string">    mutation : float or tuple(float, float), optional</span></span><br><span class="line"><span class="string">        The mutation constant. In the literature this is also known as</span></span><br><span class="line"><span class="string">        differential weight, being denoted by F.</span></span><br><span class="line"><span class="string">        If specified as a float it should be in the range [0, 2].</span></span><br><span class="line"><span class="string">        If specified as a tuple ``(min, max)`` dithering is employed. Dithering</span></span><br><span class="line"><span class="string">        randomly changes the mutation constant on a generation by generation</span></span><br><span class="line"><span class="string">        basis. The mutation constant for that generation is taken from</span></span><br><span class="line"><span class="string">        ``U[min, max)``. Dithering can help speed convergence significantly.</span></span><br><span class="line"><span class="string">        Increasing the mutation constant increases the search radius, but will</span></span><br><span class="line"><span class="string">        slow down convergence.</span></span><br><span class="line"><span class="string">    recombination : float, optional</span></span><br><span class="line"><span class="string">        The recombination constant, should be in the range [0, 1]. In the</span></span><br><span class="line"><span class="string">        literature this is also known as the crossover probability, being</span></span><br><span class="line"><span class="string">        denoted by CR. Increasing this value allows a larger number of mutants</span></span><br><span class="line"><span class="string">        to progress into the next generation, but at the risk of population</span></span><br><span class="line"><span class="string">        stability.</span></span><br><span class="line"><span class="string">    seed : int or `np.random.RandomState`, optional</span></span><br><span class="line"><span class="string">        If `seed` is not specified the `np.RandomState` singleton is used.</span></span><br><span class="line"><span class="string">        If `seed` is an int, a new `np.random.RandomState` instance is used,</span></span><br><span class="line"><span class="string">        seeded with seed.</span></span><br><span class="line"><span class="string">        If `seed` is already a `np.random.RandomState instance`, then that</span></span><br><span class="line"><span class="string">        `np.random.RandomState` instance is used.</span></span><br><span class="line"><span class="string">        Specify `seed` for repeatable minimizations.</span></span><br><span class="line"><span class="string">    disp : bool, optional</span></span><br><span class="line"><span class="string">        Display status messages</span></span><br><span class="line"><span class="string">    callback : callable, `callback(xk, convergence=val)`, optional</span></span><br><span class="line"><span class="string">        A function to follow the progress of the minimization. ``xk`` is</span></span><br><span class="line"><span class="string">        the current value of ``x0``. ``val`` represents the fractional</span></span><br><span class="line"><span class="string">        value of the population convergence.  When ``val`` is greater than one</span></span><br><span class="line"><span class="string">        the function halts. If callback returns `True`, then the minimization</span></span><br><span class="line"><span class="string">        is halted (any polishing is still carried out).</span></span><br><span class="line"><span class="string">    polish : bool, optional</span></span><br><span class="line"><span class="string">        If True (default), then `scipy.optimize.minimize` with the `L-BFGS-B`</span></span><br><span class="line"><span class="string">        method is used to polish the best population member at the end, which</span></span><br><span class="line"><span class="string">        can improve the minimization slightly.</span></span><br><span class="line"><span class="string">    init : str or array-like, optional</span></span><br><span class="line"><span class="string">        Specify which type of population initialization is performed. Should be</span></span><br><span class="line"><span class="string">        one of:</span></span><br><span class="line"><span class="string">            - &#x27;latinhypercube&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;random&#x27;</span></span><br><span class="line"><span class="string">            - array specifying the initial population. The array should have</span></span><br><span class="line"><span class="string">              shape ``(M, len(x))``, where len(x) is the number of parameters.</span></span><br><span class="line"><span class="string">              `init` is clipped to `bounds` before use.</span></span><br><span class="line"><span class="string">        The default is &#x27;latinhypercube&#x27;. Latin Hypercube sampling tries to</span></span><br><span class="line"><span class="string">        maximize coverage of the available parameter space. &#x27;random&#x27;</span></span><br><span class="line"><span class="string">        initializes the population randomly - this has the drawback that</span></span><br><span class="line"><span class="string">        clustering can occur, preventing the whole of parameter space being</span></span><br><span class="line"><span class="string">        covered. Use of an array to specify a population subset could be used,</span></span><br><span class="line"><span class="string">        for example, to create a tight bunch of initial guesses in an location</span></span><br><span class="line"><span class="string">        where the solution is known to exist, thereby reducing time for</span></span><br><span class="line"><span class="string">        convergence.</span></span><br><span class="line"><span class="string">    atol : float, optional</span></span><br><span class="line"><span class="string">        Absolute tolerance for convergence, the solving stops when</span></span><br><span class="line"><span class="string">        ``np.std(pop) &lt;= atol + tol * np.abs(np.mean(population_energies))``,</span></span><br><span class="line"><span class="string">        where and `atol` and `tol` are the absolute and relative tolerance</span></span><br><span class="line"><span class="string">        respectively.</span></span><br><span class="line"><span class="string">    Returns</span></span><br><span class="line"><span class="string">    -------</span></span><br><span class="line"><span class="string">    res : OptimizeResult</span></span><br><span class="line"><span class="string">        The optimization result represented as a `OptimizeResult` object.</span></span><br><span class="line"><span class="string">        Important attributes are: ``x`` the solution array, ``success`` a</span></span><br><span class="line"><span class="string">        Boolean flag indicating if the optimizer exited successfully and</span></span><br><span class="line"><span class="string">        ``message`` which describes the cause of the termination. See</span></span><br><span class="line"><span class="string">        `OptimizeResult` for a description of other attributes.  If `polish`</span></span><br><span class="line"><span class="string">        was employed, and a lower minimum was obtained by the polishing, then</span></span><br><span class="line"><span class="string">        OptimizeResult also contains the ``jac`` attribute.</span></span><br><span class="line"><span class="string">    Notes</span></span><br><span class="line"><span class="string">    -----</span></span><br><span class="line"><span class="string">    Differential evolution is a stochastic population based method that is</span></span><br><span class="line"><span class="string">    useful for global optimization problems. At each pass through the population</span></span><br><span class="line"><span class="string">    the algorithm mutates each candidate solution by mixing with other candidate</span></span><br><span class="line"><span class="string">    solutions to create a trial candidate. There are several strategies [2]_ for</span></span><br><span class="line"><span class="string">    creating trial candidates, which suit some problems more than others. The</span></span><br><span class="line"><span class="string">    &#x27;best1bin&#x27; strategy is a good starting point for many systems. In this</span></span><br><span class="line"><span class="string">    strategy two members of the population are randomly chosen. Their difference</span></span><br><span class="line"><span class="string">    is used to mutate the best member (the `best` in `best1bin`), :math:`b_0`,</span></span><br><span class="line"><span class="string">    so far:</span></span><br><span class="line"><span class="string">    .. math::</span></span><br><span class="line"><span class="string">        b&#x27; = b_0 + mutation * (population[rand0] - population[rand1])</span></span><br><span class="line"><span class="string">    A trial vector is then constructed. Starting with a randomly chosen &#x27;i&#x27;th</span></span><br><span class="line"><span class="string">    parameter the trial is sequentially filled (in modulo) with parameters from</span></span><br><span class="line"><span class="string">    `b&#x27;` or the original candidate. The choice of whether to use `b&#x27;` or the</span></span><br><span class="line"><span class="string">    original candidate is made with a binomial distribution (the &#x27;bin&#x27; in</span></span><br><span class="line"><span class="string">    &#x27;best1bin&#x27;) - a random number in [0, 1) is generated.  If this number is</span></span><br><span class="line"><span class="string">    less than the `recombination` constant then the parameter is loaded from</span></span><br><span class="line"><span class="string">    `b&#x27;`, otherwise it is loaded from the original candidate.  The final</span></span><br><span class="line"><span class="string">    parameter is always loaded from `b&#x27;`.  Once the trial candidate is built</span></span><br><span class="line"><span class="string">    its fitness is assessed. If the trial is better than the original candidate</span></span><br><span class="line"><span class="string">    then it takes its place. If it is also better than the best overall</span></span><br><span class="line"><span class="string">    candidate it also replaces that.</span></span><br><span class="line"><span class="string">    To improve your chances of finding a global minimum use higher `popsize`</span></span><br><span class="line"><span class="string">    values, with higher `mutation` and (dithering), but lower `recombination`</span></span><br><span class="line"><span class="string">    values. This has the effect of widening the search radius, but slowing</span></span><br><span class="line"><span class="string">    convergence.</span></span><br><span class="line"><span class="string">    .. versionadded:: 0.15.0</span></span><br><span class="line"><span class="string">    Examples</span></span><br><span class="line"><span class="string">    --------</span></span><br><span class="line"><span class="string">    Let us consider the problem of minimizing the Rosenbrock function. This</span></span><br><span class="line"><span class="string">    function is implemented in `rosen` in `scipy.optimize`.</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; from scipy.optimize import rosen, differential_evolution</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; bounds = [(0,2), (0, 2), (0, 2), (0, 2), (0, 2)]</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; result = differential_evolution(rosen, bounds)</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; result.x, result.fun</span></span><br><span class="line"><span class="string">    (array([1., 1., 1., 1., 1.]), 1.9216496320061384e-19)</span></span><br><span class="line"><span class="string">    Next find the minimum of the Ackley function</span></span><br><span class="line"><span class="string">    (http://en.wikipedia.org/wiki/Test_functions_for_optimization).</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; from scipy.optimize import differential_evolution</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; import numpy as np</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; def ackley(x):</span></span><br><span class="line"><span class="string">    ...     arg1 = -0.2 * np.sqrt(0.5 * (x[0] ** 2 + x[1] ** 2))</span></span><br><span class="line"><span class="string">    ...     arg2 = 0.5 * (np.cos(2. * np.pi * x[0]) + np.cos(2. * np.pi * x[1]))</span></span><br><span class="line"><span class="string">    ...     return -20. * np.exp(arg1) - np.exp(arg2) + 20. + np.e</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; bounds = [(-5, 5), (-5, 5)]</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; result = differential_evolution(ackley, bounds)</span></span><br><span class="line"><span class="string">    &gt;&gt;&gt; result.x, result.fun</span></span><br><span class="line"><span class="string">    (array([ 0.,  0.]), 4.4408920985006262e-16)</span></span><br><span class="line"><span class="string">    References</span></span><br><span class="line"><span class="string">    ----------</span></span><br><span class="line"><span class="string">    .. [1] Storn, R and Price, K, Differential Evolution - a Simple and</span></span><br><span class="line"><span class="string">           Efficient Heuristic for Global Optimization over Continuous Spaces,</span></span><br><span class="line"><span class="string">           Journal of Global Optimization, 1997, 11, 341 - 359.</span></span><br><span class="line"><span class="string">    .. [2] http://www1.icsi.berkeley.edu/~storn/code.html</span></span><br><span class="line"><span class="string">    .. [3] http://en.wikipedia.org/wiki/Differential_evolution</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    solver = DifferentialEvolutionSolver(func, bounds, args=args,</span><br><span class="line">                                         strategy=strategy, maxiter=maxiter,</span><br><span class="line">                                         popsize=popsize, tol=tol,</span><br><span class="line">                                         mutation=mutation,</span><br><span class="line">                                         recombination=recombination,</span><br><span class="line">                                         seed=seed, polish=polish,</span><br><span class="line">                                         callback=callback,</span><br><span class="line">                                         disp=disp, init=init, atol=atol)</span><br><span class="line">    <span class="keyword">return</span> solver.solve()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DifferentialEvolutionSolver</span>(<span class="params"><span class="built_in">object</span></span>):</span></span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;&quot;This class implements the differential evolution solver</span></span><br><span class="line"><span class="string">    Parameters</span></span><br><span class="line"><span class="string">    ----------</span></span><br><span class="line"><span class="string">    func : callable</span></span><br><span class="line"><span class="string">        The objective function to be minimized.  Must be in the form</span></span><br><span class="line"><span class="string">        ``f(x, *args)``, where ``x`` is the argument in the form of a 1-D array</span></span><br><span class="line"><span class="string">        and ``args`` is a  tuple of any additional fixed parameters needed to</span></span><br><span class="line"><span class="string">        completely specify the function.</span></span><br><span class="line"><span class="string">    bounds : sequence</span></span><br><span class="line"><span class="string">        Bounds for variables.  ``(min, max)`` pairs for each element in ``x``,</span></span><br><span class="line"><span class="string">        defining the lower and upper bounds for the optimizing argument of</span></span><br><span class="line"><span class="string">        `func`. It is required to have ``len(bounds) == len(x)``.</span></span><br><span class="line"><span class="string">        ``len(bounds)`` is used to determine the number of parameters in ``x``.</span></span><br><span class="line"><span class="string">    args : tuple, optional</span></span><br><span class="line"><span class="string">        Any additional fixed parameters needed to</span></span><br><span class="line"><span class="string">        completely specify the objective function.</span></span><br><span class="line"><span class="string">    strategy : str, optional</span></span><br><span class="line"><span class="string">        The differential evolution strategy to use. Should be one of:</span></span><br><span class="line"><span class="string">            - &#x27;best1bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;best1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;randtobest1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;currenttobest1exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;best2exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand2exp&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;randtobest1bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;currenttobest1bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;best2bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand2bin&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;rand1bin&#x27;</span></span><br><span class="line"><span class="string">        The default is &#x27;best1bin&#x27;</span></span><br><span class="line"><span class="string">    maxiter : int, optional</span></span><br><span class="line"><span class="string">        The maximum number of generations over which the entire population is</span></span><br><span class="line"><span class="string">        evolved. The maximum number of function evaluations (with no polishing)</span></span><br><span class="line"><span class="string">        is: ``(maxiter + 1) * popsize * len(x)``</span></span><br><span class="line"><span class="string">    popsize : int, optional</span></span><br><span class="line"><span class="string">        A multiplier for setting the total population size.  The population has</span></span><br><span class="line"><span class="string">        ``popsize * len(x)`` individuals (unless the initial population is</span></span><br><span class="line"><span class="string">        supplied via the `init` keyword).</span></span><br><span class="line"><span class="string">    tol : float, optional</span></span><br><span class="line"><span class="string">        Relative tolerance for convergence, the solving stops when</span></span><br><span class="line"><span class="string">        ``np.std(pop) &lt;= atol + tol * np.abs(np.mean(population_energies))``,</span></span><br><span class="line"><span class="string">        where and `atol` and `tol` are the absolute and relative tolerance</span></span><br><span class="line"><span class="string">        respectively.</span></span><br><span class="line"><span class="string">    mutation : float or tuple(float, float), optional</span></span><br><span class="line"><span class="string">        The mutation constant. In the literature this is also known as</span></span><br><span class="line"><span class="string">        differential weight, being denoted by F.</span></span><br><span class="line"><span class="string">        If specified as a float it should be in the range [0, 2].</span></span><br><span class="line"><span class="string">        If specified as a tuple ``(min, max)`` dithering is employed. Dithering</span></span><br><span class="line"><span class="string">        randomly changes the mutation constant on a generation by generation</span></span><br><span class="line"><span class="string">        basis. The mutation constant for that generation is taken from</span></span><br><span class="line"><span class="string">        U[min, max). Dithering can help speed convergence significantly.</span></span><br><span class="line"><span class="string">        Increasing the mutation constant increases the search radius, but will</span></span><br><span class="line"><span class="string">        slow down convergence.</span></span><br><span class="line"><span class="string">    recombination : float, optional</span></span><br><span class="line"><span class="string">        The recombination constant, should be in the range [0, 1]. In the</span></span><br><span class="line"><span class="string">        literature this is also known as the crossover probability, being</span></span><br><span class="line"><span class="string">        denoted by CR. Increasing this value allows a larger number of mutants</span></span><br><span class="line"><span class="string">        to progress into the next generation, but at the risk of population</span></span><br><span class="line"><span class="string">        stability.</span></span><br><span class="line"><span class="string">    seed : int or `np.random.RandomState`, optional</span></span><br><span class="line"><span class="string">        If `seed` is not specified the `np.random.RandomState` singleton is</span></span><br><span class="line"><span class="string">        used.</span></span><br><span class="line"><span class="string">        If `seed` is an int, a new `np.random.RandomState` instance is used,</span></span><br><span class="line"><span class="string">        seeded with `seed`.</span></span><br><span class="line"><span class="string">        If `seed` is already a `np.random.RandomState` instance, then that</span></span><br><span class="line"><span class="string">        `np.random.RandomState` instance is used.</span></span><br><span class="line"><span class="string">        Specify `seed` for repeatable minimizations.</span></span><br><span class="line"><span class="string">    disp : bool, optional</span></span><br><span class="line"><span class="string">        Display status messages</span></span><br><span class="line"><span class="string">    callback : callable, `callback(xk, convergence=val)`, optional</span></span><br><span class="line"><span class="string">        A function to follow the progress of the minimization. ``xk`` is</span></span><br><span class="line"><span class="string">        the current value of ``x0``. ``val`` represents the fractional</span></span><br><span class="line"><span class="string">        value of the population convergence.  When ``val`` is greater than one</span></span><br><span class="line"><span class="string">        the function halts. If callback returns `True`, then the minimization</span></span><br><span class="line"><span class="string">        is halted (any polishing is still carried out).</span></span><br><span class="line"><span class="string">    polish : bool, optional</span></span><br><span class="line"><span class="string">        If True, then `scipy.optimize.minimize` with the `L-BFGS-B` method</span></span><br><span class="line"><span class="string">        is used to polish the best population member at the end. This requires</span></span><br><span class="line"><span class="string">        a few more function evaluations.</span></span><br><span class="line"><span class="string">    maxfun : int, optional</span></span><br><span class="line"><span class="string">        Set the maximum number of function evaluations. However, it probably</span></span><br><span class="line"><span class="string">        makes more sense to set `maxiter` instead.</span></span><br><span class="line"><span class="string">    init : str or array-like, optional</span></span><br><span class="line"><span class="string">        Specify which type of population initialization is performed. Should be</span></span><br><span class="line"><span class="string">        one of:</span></span><br><span class="line"><span class="string">            - &#x27;latinhypercube&#x27;</span></span><br><span class="line"><span class="string">            - &#x27;random&#x27;</span></span><br><span class="line"><span class="string">            - array specifying the initial population. The array should have</span></span><br><span class="line"><span class="string">              shape ``(M, len(x))``, where len(x) is the number of parameters.</span></span><br><span class="line"><span class="string">              `init` is clipped to `bounds` before use.</span></span><br><span class="line"><span class="string">        The default is &#x27;latinhypercube&#x27;. Latin Hypercube sampling tries to</span></span><br><span class="line"><span class="string">        maximize coverage of the available parameter space. &#x27;random&#x27;</span></span><br><span class="line"><span class="string">        initializes the population randomly - this has the drawback that</span></span><br><span class="line"><span class="string">        clustering can occur, preventing the whole of parameter space being</span></span><br><span class="line"><span class="string">        covered. Use of an array to specify a population could be used, for</span></span><br><span class="line"><span class="string">        example, to create a tight bunch of initial guesses in an location</span></span><br><span class="line"><span class="string">        where the solution is known to exist, thereby reducing time for</span></span><br><span class="line"><span class="string">        convergence.</span></span><br><span class="line"><span class="string">    atol : float, optional</span></span><br><span class="line"><span class="string">        Absolute tolerance for convergence, the solving stops when</span></span><br><span class="line"><span class="string">        ``np.std(pop) &lt;= atol + tol * np.abs(np.mean(population_energies))``,</span></span><br><span class="line"><span class="string">        where and `atol` and `tol` are the absolute and relative tolerance</span></span><br><span class="line"><span class="string">        respectively.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># Dispatch of mutation strategy method (binomial or exponential).</span></span><br><span class="line">    _binomial = &#123;<span class="string">&#x27;best1bin&#x27;</span>: <span class="string">&#x27;_best1&#x27;</span>,</span><br><span class="line">                 <span class="string">&#x27;randtobest1bin&#x27;</span>: <span class="string">&#x27;_randtobest1&#x27;</span>,</span><br><span class="line">                 <span class="string">&#x27;currenttobest1bin&#x27;</span>: <span class="string">&#x27;_currenttobest1&#x27;</span>,</span><br><span class="line">                 <span class="string">&#x27;best2bin&#x27;</span>: <span class="string">&#x27;_best2&#x27;</span>,</span><br><span class="line">                 <span class="string">&#x27;rand2bin&#x27;</span>: <span class="string">&#x27;_rand2&#x27;</span>,</span><br><span class="line">                 <span class="string">&#x27;rand1bin&#x27;</span>: <span class="string">&#x27;_rand1&#x27;</span>&#125;</span><br><span class="line">    _exponential = &#123;<span class="string">&#x27;best1exp&#x27;</span>: <span class="string">&#x27;_best1&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;rand1exp&#x27;</span>: <span class="string">&#x27;_rand1&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;randtobest1exp&#x27;</span>: <span class="string">&#x27;_randtobest1&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;currenttobest1exp&#x27;</span>: <span class="string">&#x27;_currenttobest1&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;best2exp&#x27;</span>: <span class="string">&#x27;_best2&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;rand2exp&#x27;</span>: <span class="string">&#x27;_rand2&#x27;</span>&#125;</span><br><span class="line"></span><br><span class="line">    __init_error_msg = (<span class="string">&quot;The population initialization method must be one of &quot;</span></span><br><span class="line">                        <span class="string">&quot;&#x27;latinhypercube&#x27; or &#x27;random&#x27;, or an array of shape &quot;</span></span><br><span class="line">                        <span class="string">&quot;(M, N) where N is the number of parameters and M&gt;5&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, func, bounds, args=(<span class="params"></span>),</span></span></span><br><span class="line"><span class="function"><span class="params">                 strategy=<span class="string">&#x27;best1bin&#x27;</span>, maxiter=<span class="number">1000</span>, popsize=<span class="number">15</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 tol=<span class="number">0.01</span>, mutation=(<span class="params"><span class="number">0.5</span>, <span class="number">1</span></span>), recombination=<span class="number">0.7</span>, seed=<span class="literal">None</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 maxfun=np.inf, callback=<span class="literal">None</span>, disp=<span class="literal">False</span>, polish=<span class="literal">True</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 init=<span class="string">&#x27;latinhypercube&#x27;</span>, atol=<span class="number">0</span></span>):</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> strategy <span class="keyword">in</span> self._binomial:</span><br><span class="line">            self.mutation_func = <span class="built_in">getattr</span>(self, self._binomial[strategy])</span><br><span class="line">        <span class="keyword">elif</span> strategy <span class="keyword">in</span> self._exponential:</span><br><span class="line">            self.mutation_func = <span class="built_in">getattr</span>(self, self._exponential[strategy])</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;Please select a valid mutation strategy&quot;</span>)</span><br><span class="line">        self.strategy = strategy</span><br><span class="line"></span><br><span class="line">        self.callback = callback</span><br><span class="line">        self.polish = polish</span><br><span class="line"></span><br><span class="line">        <span class="comment"># relative and absolute tolerances for convergence</span></span><br><span class="line">        self.tol, self.atol = tol, atol</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Mutation constant should be in [0, 2). If specified as a sequence</span></span><br><span class="line">        <span class="comment"># then dithering is performed.</span></span><br><span class="line">        self.scale = mutation</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">not</span> np.<span class="built_in">all</span>(np.isfinite(mutation)) <span class="keyword">or</span></span><br><span class="line">                np.<span class="built_in">any</span>(np.array(mutation) &gt;= <span class="number">2</span>) <span class="keyword">or</span></span><br><span class="line">                np.<span class="built_in">any</span>(np.array(mutation) &lt; <span class="number">0</span>)):</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&#x27;The mutation constant must be a float in &#x27;</span></span><br><span class="line">                             <span class="string">&#x27;U[0, 2), or specified as a tuple(min, max)&#x27;</span></span><br><span class="line">                             <span class="string">&#x27; where min &lt; max and min, max are in U[0, 2).&#x27;</span>)</span><br><span class="line"></span><br><span class="line">        self.dither = <span class="literal">None</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">hasattr</span>(mutation, <span class="string">&#x27;__iter__&#x27;</span>) <span class="keyword">and</span> <span class="built_in">len</span>(mutation) &gt; <span class="number">1</span>:</span><br><span class="line">            self.dither = [mutation[<span class="number">0</span>], mutation[<span class="number">1</span>]]</span><br><span class="line">            self.dither.sort()</span><br><span class="line"></span><br><span class="line">        self.cross_over_probability = recombination</span><br><span class="line"></span><br><span class="line">        self.func = func</span><br><span class="line">        self.args = args</span><br><span class="line"></span><br><span class="line">        <span class="comment"># convert tuple of lower and upper bounds to limits</span></span><br><span class="line">        <span class="comment"># [(low_0, high_0), ..., (low_n, high_n]</span></span><br><span class="line">        <span class="comment">#     -&gt; [[low_0, ..., low_n], [high_0, ..., high_n]]</span></span><br><span class="line">        self.limits = np.array(bounds, dtype=<span class="string">&#x27;float&#x27;</span>).T</span><br><span class="line">        <span class="keyword">if</span> (np.size(self.limits, <span class="number">0</span>) != <span class="number">2</span> <span class="keyword">or</span> <span class="keyword">not</span></span><br><span class="line">                np.<span class="built_in">all</span>(np.isfinite(self.limits))):</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&#x27;bounds should be a sequence containing &#x27;</span></span><br><span class="line">                             <span class="string">&#x27;real valued (min, max) pairs for each value&#x27;</span></span><br><span class="line">                             <span class="string">&#x27; in x&#x27;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> maxiter <span class="keyword">is</span> <span class="literal">None</span>:  <span class="comment"># the default used to be None</span></span><br><span class="line">            maxiter = <span class="number">1000</span></span><br><span class="line">        self.maxiter = maxiter</span><br><span class="line">        <span class="keyword">if</span> maxfun <span class="keyword">is</span> <span class="literal">None</span>:  <span class="comment"># the default used to be None</span></span><br><span class="line">            maxfun = np.inf</span><br><span class="line">        self.maxfun = maxfun</span><br><span class="line"></span><br><span class="line">        <span class="comment"># population is scaled to between [0, 1].</span></span><br><span class="line">        <span class="comment"># We have to scale between parameter &lt;-&gt; population</span></span><br><span class="line">        <span class="comment"># save these arguments for _scale_parameter and</span></span><br><span class="line">        <span class="comment"># _unscale_parameter. This is an optimization</span></span><br><span class="line">        self.__scale_arg1 = <span class="number">0.5</span> * (self.limits[<span class="number">0</span>] + self.limits[<span class="number">1</span>])</span><br><span class="line">        self.__scale_arg2 = np.fabs(self.limits[<span class="number">0</span>] - self.limits[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line">        self.parameter_count = np.size(self.limits, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">        self.random_number_generator = check_random_state(seed)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># default population initialization is a latin hypercube design, but</span></span><br><span class="line">        <span class="comment"># there are other population initializations possible.</span></span><br><span class="line">        <span class="comment"># the minimum is 5 because &#x27;best2bin&#x27; requires a population that&#x27;s at</span></span><br><span class="line">        <span class="comment"># least 5 long</span></span><br><span class="line">        self.num_population_members = <span class="built_in">max</span>(<span class="number">5</span>, popsize * self.parameter_count)</span><br><span class="line"></span><br><span class="line">        self.population_shape = (self.num_population_members,</span><br><span class="line">                                 self.parameter_count)</span><br><span class="line"></span><br><span class="line">        self._nfev = <span class="number">0</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(init, <span class="built_in">str</span>):</span><br><span class="line">            <span class="keyword">if</span> init == <span class="string">&#x27;latinhypercube&#x27;</span>:</span><br><span class="line">                self.init_population_lhs()</span><br><span class="line">            <span class="keyword">elif</span> init == <span class="string">&#x27;random&#x27;</span>:</span><br><span class="line">                self.init_population_random()</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">raise</span> ValueError(self.__init_error_msg)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            self.init_population_array(init)</span><br><span class="line"></span><br><span class="line">        self.disp = disp</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">init_population_lhs</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Initializes the population with Latin Hypercube Sampling.</span></span><br><span class="line"><span class="string">        Latin Hypercube Sampling ensures that each parameter is uniformly</span></span><br><span class="line"><span class="string">        sampled over its range.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        rng = self.random_number_generator</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Each parameter range needs to be sampled uniformly. The scaled</span></span><br><span class="line">        <span class="comment"># parameter range ([0, 1)) needs to be split into</span></span><br><span class="line">        <span class="comment"># `self.num_population_members` segments, each of which has the following</span></span><br><span class="line">        <span class="comment"># size:</span></span><br><span class="line">        segsize = <span class="number">1.0</span> / self.num_population_members</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Within each segment we sample from a uniform random distribution.</span></span><br><span class="line">        <span class="comment"># We need to do this sampling for each parameter.</span></span><br><span class="line">        samples = (segsize * rng.random_sample(self.population_shape)</span><br><span class="line"></span><br><span class="line">                   <span class="comment"># Offset each segment to cover the entire parameter range [0, 1)</span></span><br><span class="line">                   + np.linspace(<span class="number">0.</span>, <span class="number">1.</span>, self.num_population_members,</span><br><span class="line">                                 endpoint=<span class="literal">False</span>)[:, np.newaxis])</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Create an array for population of candidate solutions.</span></span><br><span class="line">        self.population = np.zeros_like(samples)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Initialize population of candidate solutions by permutation of the</span></span><br><span class="line">        <span class="comment"># random samples.</span></span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.parameter_count):</span><br><span class="line">            order = rng.permutation(<span class="built_in">range</span>(self.num_population_members))</span><br><span class="line">            self.population[:, j] = samples[order, j]</span><br><span class="line"></span><br><span class="line">        <span class="comment"># reset population energies</span></span><br><span class="line">        self.population_energies = (np.ones(self.num_population_members) *</span><br><span class="line">                                    np.inf)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># reset number of function evaluations counter</span></span><br><span class="line">        self._nfev = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">init_population_random</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Initialises the population at random.  This type of initialization</span></span><br><span class="line"><span class="string">        can possess clustering, Latin Hypercube sampling is generally better.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        rng = self.random_number_generator</span><br><span class="line">        self.population = rng.random_sample(self.population_shape)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># reset population energies</span></span><br><span class="line">        self.population_energies = (np.ones(self.num_population_members) *</span><br><span class="line">                                    np.inf)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># reset number of function evaluations counter</span></span><br><span class="line">        self._nfev = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">init_population_array</span>(<span class="params">self, init</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Initialises the population with a user specified population.</span></span><br><span class="line"><span class="string">        Parameters</span></span><br><span class="line"><span class="string">        ----------</span></span><br><span class="line"><span class="string">        init : np.ndarray</span></span><br><span class="line"><span class="string">            Array specifying subset of the initial population. The array should</span></span><br><span class="line"><span class="string">            have shape (M, len(x)), where len(x) is the number of parameters.</span></span><br><span class="line"><span class="string">            The population is clipped to the lower and upper `bounds`.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># make sure you&#x27;re using a float array</span></span><br><span class="line">        popn = np.asfarray(init)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (np.size(popn, <span class="number">0</span>) &lt; <span class="number">5</span> <span class="keyword">or</span></span><br><span class="line">                popn.shape[<span class="number">1</span>] != self.parameter_count <span class="keyword">or</span></span><br><span class="line">                <span class="built_in">len</span>(popn.shape) != <span class="number">2</span>):</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&quot;The population supplied needs to have shape&quot;</span></span><br><span class="line">                             <span class="string">&quot; (M, len(x)), where M &gt; 4.&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># scale values and clip to bounds, assigning to population</span></span><br><span class="line">        self.population = np.clip(self._unscale_parameters(popn), <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">        self.num_population_members = np.size(self.population, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">        self.population_shape = (self.num_population_members,</span><br><span class="line">                                 self.parameter_count)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># reset population energies</span></span><br><span class="line">        self.population_energies = (np.ones(self.num_population_members) *</span><br><span class="line">                                    np.inf)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># reset number of function evaluations counter</span></span><br><span class="line">        self._nfev = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">x</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        The best solution from the solver</span></span><br><span class="line"><span class="string">        Returns</span></span><br><span class="line"><span class="string">        -------</span></span><br><span class="line"><span class="string">        x : ndarray</span></span><br><span class="line"><span class="string">            The best solution from the solver.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> self._scale_parameters(self.population[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">convergence</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        The standard deviation of the population energies divided by their</span></span><br><span class="line"><span class="string">        mean.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> (np.std(self.population_energies) /</span><br><span class="line">                np.<span class="built_in">abs</span>(np.mean(self.population_energies) + _MACHEPS))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">solve</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Runs the DifferentialEvolutionSolver.</span></span><br><span class="line"><span class="string">        Returns</span></span><br><span class="line"><span class="string">        -------</span></span><br><span class="line"><span class="string">        res : OptimizeResult</span></span><br><span class="line"><span class="string">            The optimization result represented as a ``OptimizeResult`` object.</span></span><br><span class="line"><span class="string">            Important attributes are: ``x`` the solution array, ``success`` a</span></span><br><span class="line"><span class="string">            Boolean flag indicating if the optimizer exited successfully and</span></span><br><span class="line"><span class="string">            ``message`` which describes the cause of the termination. See</span></span><br><span class="line"><span class="string">            `OptimizeResult` for a description of other attributes.  If `polish`</span></span><br><span class="line"><span class="string">            was employed, and a lower minimum was obtained by the polishing,</span></span><br><span class="line"><span class="string">            then OptimizeResult also contains the ``jac`` attribute.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        nit, warning_flag = <span class="number">0</span>, <span class="literal">False</span></span><br><span class="line">        status_message = _status_message[<span class="string">&#x27;success&#x27;</span>]</span><br><span class="line"></span><br><span class="line">        <span class="comment"># The population may have just been initialized (all entries are</span></span><br><span class="line">        <span class="comment"># np.inf). If it has you have to calculate the initial energies.</span></span><br><span class="line">        <span class="comment"># Although this is also done in the evolve generator it&#x27;s possible</span></span><br><span class="line">        <span class="comment"># that someone can set maxiter=0, at which point we still want the</span></span><br><span class="line">        <span class="comment"># initial energies to be calculated (the following loop isn&#x27;t run).</span></span><br><span class="line">        <span class="keyword">if</span> np.<span class="built_in">all</span>(np.isinf(self.population_energies)):</span><br><span class="line">            self._calculate_population_energies()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># do the optimisation.</span></span><br><span class="line">        <span class="keyword">for</span> nit <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, self.maxiter + <span class="number">1</span>):</span><br><span class="line">            <span class="comment"># evolve the population by a generation</span></span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="built_in">next</span>(self)</span><br><span class="line">            <span class="keyword">except</span> StopIteration:</span><br><span class="line">                warning_flag = <span class="literal">True</span></span><br><span class="line">                status_message = _status_message[<span class="string">&#x27;maxfev&#x27;</span>]</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> self.disp:</span><br><span class="line">                print(<span class="string">&quot;differential_evolution step %d: f(x)= %g&quot;</span></span><br><span class="line">                      % (nit,</span><br><span class="line">                         self.population_energies[<span class="number">0</span>]))</span><br><span class="line"></span><br><span class="line">            <span class="comment"># should the solver terminate?</span></span><br><span class="line">            convergence = self.convergence</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (self.callback <span class="keyword">and</span></span><br><span class="line">                    self.callback(self._scale_parameters(self.population[<span class="number">0</span>]),</span><br><span class="line">                                  convergence=self.tol / convergence) <span class="keyword">is</span> <span class="literal">True</span>):</span><br><span class="line"></span><br><span class="line">                warning_flag = <span class="literal">True</span></span><br><span class="line">                status_message = (<span class="string">&#x27;callback function requested stop early &#x27;</span></span><br><span class="line">                                  <span class="string">&#x27;by returning True&#x27;</span>)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">            intol = (np.std(self.population_energies) &lt;=</span><br><span class="line">                     self.atol +</span><br><span class="line">                     self.tol * np.<span class="built_in">abs</span>(np.mean(self.population_energies)))</span><br><span class="line">            <span class="keyword">if</span> warning_flag <span class="keyword">or</span> intol:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            status_message = _status_message[<span class="string">&#x27;maxiter&#x27;</span>]</span><br><span class="line">            warning_flag = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">        DE_result = OptimizeResult(</span><br><span class="line">            x=self.x,</span><br><span class="line">            fun=self.population_energies[<span class="number">0</span>],</span><br><span class="line">            nfev=self._nfev,</span><br><span class="line">            nit=nit,</span><br><span class="line">            message=status_message,</span><br><span class="line">            success=(warning_flag <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">True</span>))</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> self.polish:</span><br><span class="line">            result = minimize(self.func,</span><br><span class="line">                              np.copy(DE_result.x),</span><br><span class="line">                              method=<span class="string">&#x27;L-BFGS-B&#x27;</span>,</span><br><span class="line">                              bounds=self.limits.T,</span><br><span class="line">                              args=self.args)</span><br><span class="line"></span><br><span class="line">            self._nfev += result.nfev</span><br><span class="line">            DE_result.nfev = self._nfev</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> result.fun &lt; DE_result.fun:</span><br><span class="line">                DE_result.fun = result.fun</span><br><span class="line">                DE_result.x = result.x</span><br><span class="line">                DE_result.jac = result.jac</span><br><span class="line">                <span class="comment"># to keep internal state consistent</span></span><br><span class="line">                self.population_energies[<span class="number">0</span>] = result.fun</span><br><span class="line">                self.population[<span class="number">0</span>] = self._unscale_parameters(result.x)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> DE_result</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_calculate_population_energies</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Calculate the energies of all the population members at the same time.</span></span><br><span class="line"><span class="string">        Puts the best member in first place. Useful if the population has just</span></span><br><span class="line"><span class="string">        been initialised.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line">        <span class="comment"># CHANGES: self.func operates on the entire parameters array</span></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line">        itersize = <span class="built_in">max</span>(<span class="number">0</span>, <span class="built_in">min</span>(<span class="built_in">len</span>(self.population),</span><br><span class="line">                       self.maxfun - self._nfev + <span class="number">1</span>))</span><br><span class="line">        candidates = self.population[:itersize]</span><br><span class="line">        parameters = np.array([self._scale_parameters(c)</span><br><span class="line">                              <span class="keyword">for</span> c <span class="keyword">in</span> candidates])  <span class="comment"># <span class="doctag">TODO:</span> can be vectorized</span></span><br><span class="line">        energies = self.func(parameters, *self.args)</span><br><span class="line">        self.population_energies = energies</span><br><span class="line">        self._nfev += itersize</span><br><span class="line"></span><br><span class="line">        <span class="comment"># for index, candidate in enumerate(self.population):</span></span><br><span class="line">        <span class="comment">#     if self._nfev &gt; self.maxfun:</span></span><br><span class="line">        <span class="comment">#         break</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     parameters = self._scale_parameters(candidate)</span></span><br><span class="line">        <span class="comment">#     self.population_energies[index] = self.func(parameters,</span></span><br><span class="line">        <span class="comment">#                                                 *self.args)</span></span><br><span class="line">        <span class="comment">#     self._nfev += 1</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line"></span><br><span class="line">        minval = np.argmin(self.population_energies)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># put the lowest energy into the best solution position.</span></span><br><span class="line">        lowest_energy = self.population_energies[minval]</span><br><span class="line">        self.population_energies[minval] = self.population_energies[<span class="number">0</span>]</span><br><span class="line">        self.population_energies[<span class="number">0</span>] = lowest_energy</span><br><span class="line"></span><br><span class="line">        self.population[[<span class="number">0</span>, minval], :] = self.population[[minval, <span class="number">0</span>], :]</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> self</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__next__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Evolve the population by a single generation</span></span><br><span class="line"><span class="string">        Returns</span></span><br><span class="line"><span class="string">        -------</span></span><br><span class="line"><span class="string">        x : ndarray</span></span><br><span class="line"><span class="string">            The best solution from the solver.</span></span><br><span class="line"><span class="string">        fun : float</span></span><br><span class="line"><span class="string">            Value of objective function obtained from the best solution.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># the population may have just been initialized (all entries are</span></span><br><span class="line">        <span class="comment"># np.inf). If it has you have to calculate the initial energies</span></span><br><span class="line">        <span class="keyword">if</span> np.<span class="built_in">all</span>(np.isinf(self.population_energies)):</span><br><span class="line">            self._calculate_population_energies()</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> self.dither <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">            self.scale = (self.random_number_generator.rand()</span><br><span class="line">                          * (self.dither[<span class="number">1</span>] - self.dither[<span class="number">0</span>]) + self.dither[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line">        <span class="comment"># CHANGES: self.func operates on the entire parameters array</span></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line"></span><br><span class="line">        itersize = <span class="built_in">max</span>(<span class="number">0</span>, <span class="built_in">min</span>(self.num_population_members,</span><br><span class="line">                       self.maxfun - self._nfev + <span class="number">1</span>))</span><br><span class="line">        trials = np.array([self._mutate(c)</span><br><span class="line">                          <span class="keyword">for</span> c <span class="keyword">in</span> <span class="built_in">range</span>(itersize)])  <span class="comment"># <span class="doctag">TODO:</span> can be vectorized</span></span><br><span class="line">        <span class="keyword">for</span> trial <span class="keyword">in</span> trials:</span><br><span class="line">            self._ensure_constraint(trial)</span><br><span class="line">        parameters = np.array([self._scale_parameters(trial)</span><br><span class="line">                              <span class="keyword">for</span> trial <span class="keyword">in</span> trials])</span><br><span class="line">        energies = self.func(parameters, *self.args)</span><br><span class="line">        self._nfev += itersize</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> candidate, (energy, trial) <span class="keyword">in</span> <span class="built_in">enumerate</span>(<span class="built_in">zip</span>(energies, trials)):</span><br><span class="line">            <span class="comment"># if the energy of the trial candidate is lower than the</span></span><br><span class="line">            <span class="comment"># original population member then replace it</span></span><br><span class="line">            <span class="keyword">if</span> energy &lt; self.population_energies[candidate]:</span><br><span class="line">                self.population[candidate] = trial</span><br><span class="line">                self.population_energies[candidate] = energy</span><br><span class="line"></span><br><span class="line">                <span class="comment"># if the trial candidate also has a lower energy than the</span></span><br><span class="line">                <span class="comment"># best solution then replace that as well</span></span><br><span class="line">                <span class="keyword">if</span> energy &lt; self.population_energies[<span class="number">0</span>]:</span><br><span class="line">                    self.population_energies[<span class="number">0</span>] = energy</span><br><span class="line">                    self.population[<span class="number">0</span>] = trial</span><br><span class="line"></span><br><span class="line">        <span class="comment"># for candidate in range(self.num_population_members):</span></span><br><span class="line">        <span class="comment">#     if self._nfev &gt; self.maxfun:</span></span><br><span class="line">        <span class="comment">#         raise StopIteration</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     # create a trial solution</span></span><br><span class="line">        <span class="comment">#     trial = self._mutate(candidate)</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     # ensuring that it&#x27;s in the range [0, 1)</span></span><br><span class="line">        <span class="comment">#     self._ensure_constraint(trial)</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     # scale from [0, 1) to the actual parameter value</span></span><br><span class="line">        <span class="comment">#     parameters = self._scale_parameters(trial)</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     # determine the energy of the objective function</span></span><br><span class="line">        <span class="comment">#     energy = self.func(parameters, *self.args)</span></span><br><span class="line">        <span class="comment">#     self._nfev += 1</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     # if the energy of the trial candidate is lower than the</span></span><br><span class="line">        <span class="comment">#     # original population member then replace it</span></span><br><span class="line">        <span class="comment">#     if energy &lt; self.population_energies[candidate]:</span></span><br><span class="line">        <span class="comment">#         self.population[candidate] = trial</span></span><br><span class="line">        <span class="comment">#         self.population_energies[candidate] = energy</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#         # if the trial candidate also has a lower energy than the</span></span><br><span class="line">        <span class="comment">#         # best solution then replace that as well</span></span><br><span class="line">        <span class="comment">#         if energy &lt; self.population_energies[0]:</span></span><br><span class="line">        <span class="comment">#             self.population_energies[0] = energy</span></span><br><span class="line">        <span class="comment">#             self.population[0] = trial</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line">        <span class="comment">##############</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> self.x, self.population_energies[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">next</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        Evolve the population by a single generation</span></span><br><span class="line"><span class="string">        Returns</span></span><br><span class="line"><span class="string">        -------</span></span><br><span class="line"><span class="string">        x : ndarray</span></span><br><span class="line"><span class="string">            The best solution from the solver.</span></span><br><span class="line"><span class="string">        fun : float</span></span><br><span class="line"><span class="string">            Value of objective function obtained from the best solution.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># next() is required for compatibility with Python2.7.</span></span><br><span class="line">        <span class="keyword">return</span> self.__next__()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_scale_parameters</span>(<span class="params">self, trial</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        scale from a number between 0 and 1 to parameters.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> self.__scale_arg1 + (trial - <span class="number">0.5</span>) * self.__scale_arg2</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_unscale_parameters</span>(<span class="params">self, parameters</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        scale from parameters to a number between 0 and 1.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> (parameters - self.__scale_arg1) / self.__scale_arg2 + <span class="number">0.5</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_ensure_constraint</span>(<span class="params">self, trial</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        make sure the parameters lie between the limits</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">for</span> index <span class="keyword">in</span> np.where((trial &lt; <span class="number">0</span>) | (trial &gt; <span class="number">1</span>))[<span class="number">0</span>]:</span><br><span class="line">            trial[index] = self.random_number_generator.rand()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_mutate</span>(<span class="params">self, candidate</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        create a trial vector based on a mutation strategy</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        trial = np.copy(self.population[candidate])</span><br><span class="line"></span><br><span class="line">        rng = self.random_number_generator</span><br><span class="line"></span><br><span class="line">        fill_point = rng.randint(<span class="number">0</span>, self.parameter_count)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> self.strategy <span class="keyword">in</span> [<span class="string">&#x27;currenttobest1exp&#x27;</span>, <span class="string">&#x27;currenttobest1bin&#x27;</span>]:</span><br><span class="line">            bprime = self.mutation_func(candidate,</span><br><span class="line">                                        self._select_samples(candidate, <span class="number">5</span>))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            bprime = self.mutation_func(self._select_samples(candidate, <span class="number">5</span>))</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> self.strategy <span class="keyword">in</span> self._binomial:</span><br><span class="line">            crossovers = rng.rand(self.parameter_count)</span><br><span class="line">            crossovers = crossovers &lt; self.cross_over_probability</span><br><span class="line">            <span class="comment"># the last one is always from the bprime vector for binomial</span></span><br><span class="line">            <span class="comment"># If you fill in modulo with a loop you have to set the last one to</span></span><br><span class="line">            <span class="comment"># true. If you don&#x27;t use a loop then you can have any random entry</span></span><br><span class="line">            <span class="comment"># be True.</span></span><br><span class="line">            crossovers[fill_point] = <span class="literal">True</span></span><br><span class="line">            trial = np.where(crossovers, bprime, trial)</span><br><span class="line">            <span class="keyword">return</span> trial</span><br><span class="line"></span><br><span class="line">        <span class="keyword">elif</span> self.strategy <span class="keyword">in</span> self._exponential:</span><br><span class="line">            i = <span class="number">0</span></span><br><span class="line">            <span class="keyword">while</span> (i &lt; self.parameter_count <span class="keyword">and</span></span><br><span class="line">                   rng.rand() &lt; self.cross_over_probability):</span><br><span class="line"></span><br><span class="line">                trial[fill_point] = bprime[fill_point]</span><br><span class="line">                fill_point = (fill_point + <span class="number">1</span>) % self.parameter_count</span><br><span class="line">                i += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> trial</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_best1</span>(<span class="params">self, samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        best1bin, best1exp</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        r0, r1 = samples[:<span class="number">2</span>]</span><br><span class="line">        <span class="keyword">return</span> (self.population[<span class="number">0</span>] + self.scale *</span><br><span class="line">                (self.population[r0] - self.population[r1]))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_rand1</span>(<span class="params">self, samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        rand1bin, rand1exp</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        r0, r1, r2 = samples[:<span class="number">3</span>]</span><br><span class="line">        <span class="keyword">return</span> (self.population[r0] + self.scale *</span><br><span class="line">                (self.population[r1] - self.population[r2]))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_randtobest1</span>(<span class="params">self, samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        randtobest1bin, randtobest1exp</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        r0, r1, r2 = samples[:<span class="number">3</span>]</span><br><span class="line">        bprime = np.copy(self.population[r0])</span><br><span class="line">        bprime += self.scale * (self.population[<span class="number">0</span>] - bprime)</span><br><span class="line">        bprime += self.scale * (self.population[r1] -</span><br><span class="line">                                self.population[r2])</span><br><span class="line">        <span class="keyword">return</span> bprime</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_currenttobest1</span>(<span class="params">self, candidate, samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        currenttobest1bin, currenttobest1exp</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        r0, r1 = samples[:<span class="number">2</span>]</span><br><span class="line">        bprime = (self.population[candidate] + self.scale *</span><br><span class="line">                  (self.population[<span class="number">0</span>] - self.population[candidate] +</span><br><span class="line">                   self.population[r0] - self.population[r1]))</span><br><span class="line">        <span class="keyword">return</span> bprime</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_best2</span>(<span class="params">self, samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        best2bin, best2exp</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        r0, r1, r2, r3 = samples[:<span class="number">4</span>]</span><br><span class="line">        bprime = (self.population[<span class="number">0</span>] + self.scale *</span><br><span class="line">                  (self.population[r0] + self.population[r1] -</span><br><span class="line">                   self.population[r2] - self.population[r3]))</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> bprime</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_rand2</span>(<span class="params">self, samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        rand2bin, rand2exp</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        r0, r1, r2, r3, r4 = samples</span><br><span class="line">        bprime = (self.population[r0] + self.scale *</span><br><span class="line">                  (self.population[r1] + self.population[r2] -</span><br><span class="line">                   self.population[r3] - self.population[r4]))</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> bprime</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_select_samples</span>(<span class="params">self, candidate, number_samples</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        obtain random integers from range(self.num_population_members),</span></span><br><span class="line"><span class="string">        without replacement.  You can&#x27;t have the original candidate either.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        idxs = <span class="built_in">list</span>(<span class="built_in">range</span>(self.num_population_members))</span><br><span class="line">        idxs.remove(candidate)</span><br><span class="line">        self.random_number_generator.shuffle(idxs)</span><br><span class="line">        idxs = idxs[:number_samples]</span><br><span class="line">        <span class="keyword">return</span> idxs</span><br></pre></td></tr></table></figure>    </div></div><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;RCTF出了个OnePixelAttack的题，很早就看见过这个攻击了，于是尝试一下&lt;/p&gt;</summary>
    
    
    
    <category term="AI" scheme="https://kyriota.github.io/categories/AI/"/>
    
    
  </entry>
  
  <entry>
    <title>网课 Auto Play</title>
    <link href="https://kyriota.github.io/2022/08/28/%E7%BD%91%E8%AF%BEAutoPlay/"/>
    <id>https://kyriota.github.io/2022/08/28/%E7%BD%91%E8%AF%BEAutoPlay/</id>
    <published>2022-08-28T16:25:00.000Z</published>
    <updated>2022-08-28T05:52:43.082Z</updated>
    
    <content type="html"><![CDATA[<p>最近油猴上超星尔雅的挂课脚本不好使，自己弄点基础的功能先用着。脚本直接从网上Copy后发现不能直接用，应该是平台更新的缘故，经过一些列调整之后成功跑起来了，放出来造福社会</p><span id="more"></span><h1 id="网课-auto-play">网课 Auto Play</h1><blockquote><p>哪些课该用脚本挂哪些课不该用脚本取决于使用者自己的认知，慎用</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> body = $(<span class="string">&quot;body&quot;</span>)</span><br><span class="line"><span class="keyword">var</span> button = $(<span class="string">&quot;&lt;li id=&#x27;set&#x27;&gt;&lt;/li&gt;&quot;</span>)</span><br><span class="line">button.html(<span class="string">&quot;&lt;span id=&#x27;start&#x27;&gt;开启自动播放模式&lt;/span&gt;&quot;</span>)</span><br><span class="line"><span class="keyword">var</span> json = &#123;</span><br><span class="line">    <span class="string">&quot;background&quot;</span>: <span class="string">&quot;#36f&quot;</span>,</span><br><span class="line">    <span class="string">&quot;height&quot;</span>: <span class="string">&quot;16px&quot;</span>,</span><br><span class="line">    <span class="string">&quot;padding&quot;</span>: <span class="string">&quot;5px&quot;</span>,</span><br><span class="line">    <span class="string">&quot;z-index&quot;</span>: <span class="number">999</span>, <span class="comment">// Stay top</span></span><br><span class="line">    <span class="string">&quot;cursor&quot;</span>: <span class="string">&quot;pointer&quot;</span>,</span><br><span class="line">    <span class="string">&quot;bottom&quot;</span>: <span class="string">&quot;0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;left&quot;</span>: <span class="string">&quot;0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;color&quot;</span>: <span class="string">&quot;#fff&quot;</span>,</span><br><span class="line">    <span class="string">&quot;position&quot;</span>: <span class="string">&quot;fixed&quot;</span></span><br><span class="line">&#125;;</span><br><span class="line">button.css(json);</span><br><span class="line">body.append(button)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Index of current video</span></span><br><span class="line"><span class="keyword">var</span> index = <span class="number">0</span>;</span><br><span class="line">$(<span class="string">&quot;.posCatalog_select&quot;</span>).each(<span class="function">(<span class="params">i, item</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> ($(item).hasClass(<span class="string">&quot;posCatalog_active&quot;</span>)) &#123;</span><br><span class="line">        index = i;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">&quot;第&quot;</span> + (index + <span class="number">1</span>) + <span class="string">&quot;个视频开始播放&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Blacklist of course id, courses in this list will be skipped</span></span><br><span class="line"><span class="keyword">var</span> blacklist = [];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">autoNext</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    index++;</span><br><span class="line">    <span class="keyword">var</span> selectID = $(<span class="string">&quot;.posCatalog_select&quot;</span>)[index].id;</span><br><span class="line">    <span class="keyword">while</span> (selectID.indexOf(<span class="string">&#x27;cur&#x27;</span>) == -<span class="number">1</span> || blacklist.indexOf(selectID) != -<span class="number">1</span>)</span><br><span class="line">        inedx++;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&quot;第&quot;</span> + (index + <span class="number">1</span>) + <span class="string">&quot;个视频开始播放&quot;</span>);</span><br><span class="line">    <span class="keyword">var</span> next = <span class="built_in">document</span>.querySelectorAll(<span class="string">&quot;.posCatalog_select&quot;</span>)[index].querySelector(<span class="string">&quot;.posCatalog_name&quot;</span>);</span><br><span class="line">    next.click();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> progressRecord = -<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">button.click(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">setInterval</span>(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// Get iframe</span></span><br><span class="line">        <span class="keyword">var</span> video = $(<span class="string">&quot;iframe&quot;</span>).contents().find(<span class="string">&quot;iframe&quot;</span>).contents();</span><br><span class="line">        <span class="comment">// Get video progress 0~100 float</span></span><br><span class="line">        <span class="keyword">var</span> spans = video.find(<span class="string">&quot;#video &gt; div.vjs-control-bar &gt; div.vjs-progress-control.vjs-control &gt; div&quot;</span>).attr(<span class="string">&quot;aria-valuenow&quot;</span>);</span><br><span class="line">        <span class="keyword">var</span> play = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (spans == progressRecord) &#123;</span><br><span class="line">                video.find(<span class="string">&quot;#video &gt; button&quot;</span>).click();</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;视频被暂停，已继续&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            progressRecord = spans;</span><br><span class="line">            <span class="keyword">var</span> slience = video.find(<span class="string">&quot;#video &gt; div.vjs-control-bar &gt; div.vjs-volume-panel.vjs-control.vjs-volume-panel-vertical &gt; button&quot;</span>);</span><br><span class="line">            <span class="comment">// Mute</span></span><br><span class="line">            <span class="keyword">if</span> (slience.attr(<span class="string">&quot;title&quot;</span>) != <span class="string">&quot;取消静音&quot;</span>) &#123;</span><br><span class="line">                slience.click();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">var</span> load = video.find(<span class="string">&quot;#loading&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (load.css(<span class="string">&quot;visibility&quot;</span>) != <span class="string">&quot;hidden&quot;</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (spans != <span class="number">100</span>) &#123;</span><br><span class="line">            play();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;第&quot;</span> + (index + <span class="number">1</span>) + <span class="string">&quot;个视频播放完成&quot;</span>);</span><br><span class="line">            autoNext();</span><br><span class="line">        &#125;</span><br><span class="line">        $(<span class="string">&quot;#start&quot;</span>).html(<span class="string">&quot;自动模式已开启,本视频进度:&quot;</span> + spans + <span class="string">&quot;%&quot;</span>);</span><br><span class="line">    &#125;, <span class="number">2000</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="更多注解">更多注解</h2><ul><li><p><code>Line20</code>：找正在播放的视频的<code>index</code>的思路：</p><ul><li><p>元素审查观察右侧课程列表</p><p><img src="/images/MOOC_Element.png" /></p><p>选择中的选项指向两个类，<code>posCatalog_select</code>，<code>posCatalog_active</code>，通过观察，前者是一般的选项的类，后者主要就负责显示一个蓝色的选中样式，由此，含有<code>posCatalog_active</code>这个类的就是被选中的视频</p></li></ul></li><li><p><code>Line32</code>：播放下一个视频的思路：</p><ul><li>一样的元素审查，见上图，虽然正常情况下只需要<code>index++</code>即可，但是目录的类也是<code>posCatalog_select</code>（比如上图中的<code>2 劳动与人生</code>就是一个folder，点了也就是折叠一下），观察目录类，发现其<code>id</code>中不含<code>cur</code>，可以以此和视频选卡区分开来</li><li>在某些网课中，还存在除了目录类之外的二级目录，这些选卡里面没有需要刷的点，也没有视频之类的，点开就是一个空白页，而且<code>id</code>中还带有<code>cur</code>，懒得想办法，直接用黑名单，每次手动设置一下需要跳过的选卡即可</li></ul></li><li><p><code>Line75</code>：一旦操作过于频繁就会弹出验证码，所以<code>SetInterval</code>间隔稍微大一些</p></li><li><p>关于倍速设置，如果直接获取html5播放器然后设置<code>playbackRate</code>会导致跳验证码以及设置不上，正常速度挂网课对我来说也足够了，懒得弄了就</p></li></ul><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近油猴上超星尔雅的挂课脚本不好使，自己弄点基础的功能先用着。脚本直接从网上Copy后发现不能直接用，应该是平台更新的缘故，经过一些列调整之后成功跑起来了，放出来造福社会&lt;/p&gt;</summary>
    
    
    
    <category term="课内" scheme="https://kyriota.github.io/categories/%E8%AF%BE%E5%86%85/"/>
    
    
  </entry>
  
  <entry>
    <title>HLSL Note</title>
    <link href="https://kyriota.github.io/2022/08/12/HLSL-Note/"/>
    <id>https://kyriota.github.io/2022/08/12/HLSL-Note/</id>
    <published>2022-08-13T03:30:00.000Z</published>
    <updated>2024-07-18T14:59:58.253Z</updated>
    
    <content type="html"><![CDATA[<p>A post to record HLSL stuff related to unity shader graph programing.</p><span id="more"></span><h1 id="hlsl-note">HLSL Note</h1><h2 id="custom-function-node">Custom Function Node</h2><p>When coding the HLSL include file, I found the custom function node in the shader graph never stops throwing out errors that I can't understand like this:</p><p><img src="/images/HLSLNote_CustomFuctionError.png" /></p><p>Any programmer can never allow any red or yellow stuff in his code, so I tried really hard to locate the error, but find nothing useful at the end. There's little information about how I can get rid of this error, until I accidently folded the preview in the node by referring an unsolved post on unity forum questioning that "why is my custom function node keeps throwing out errors though <strong>THE OVERALL SHADER IS WORKING FINE</strong> ?" and the error disappears. My feeling was so mixed and in chaos at that moment.</p><p>It seems that you cannot call functions or use structs in URP Library directly, that's why <code>SHADERGRAPH_PREVIEW</code> is defined when node tries to draw preview image.</p><p>Referring some other shader graph custom functions like the one below, we can see that they hard code the variables that should use functions in URP Library to get (such as <code>GetMainLight()</code>) to get a preview from the node (maybe also for performance in node preview).</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">void MainLight_float(float3 WorldPos, out float3 Direction, out float3 Color, out float DistanceAtten, out float ShadowAtten)</span><br><span class="line">&#123;</span><br><span class="line">    #if SHADERGRAPH_PREVIEW</span><br><span class="line">        Direction &#x3D; float3(-0.5, 0.5, 0);</span><br><span class="line">        Color &#x3D; float3(1, 0.95, 0.8);</span><br><span class="line">        DistanceAtten &#x3D; 1;</span><br><span class="line">        ShadowAtten &#x3D; 1;</span><br><span class="line">    #else</span><br><span class="line">        #if SHADOWS_SCREEN</span><br><span class="line">            float4 clipPos &#x3D; TransformWorldToHClip(WorldPos);</span><br><span class="line">            float4 shadowCoord &#x3D; ComputeScreenPos(clipPos);</span><br><span class="line">        #else</span><br><span class="line">            float4 shadowCoord &#x3D; TransformWorldToShadowCoord(WorldPos);</span><br><span class="line">        #endif</span><br><span class="line">        Light mainLight &#x3D; GetMainLight(shadowCoord);</span><br><span class="line">        Direction &#x3D; mainLight.direction;</span><br><span class="line">        Color &#x3D; mainLight.color;</span><br><span class="line">        DistanceAtten &#x3D; mainLight.distanceAttenuation;</span><br><span class="line">        ShadowAtten &#x3D; mainLight.shadowAttenuation;</span><br><span class="line">    #endif</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>However, the final preview (usually at right bottom corner) is not the same as the preview in nodes, so when you see errors from your custom node, check the final preview out to see whether your code actually works.</p><p>In case you didn't notice, the function name in the custom function node should be the same as the name in your HLSL file. For example, if you put the code above to the node, you should type in <code>MainLight</code> in the function name input box. The <code>_float</code> after the function name is describing the precision of your data, it's either <code>_float</code> or <code>_half</code> in shader graph, and you should also change the precision select box in the graph setting to match your precision in the function.</p><h2 id="static-variable">Static Variable</h2><p>When I was coding the outline HLSL file, I declared a float parameter outside the main function without marking it <code>static</code>. In HLSL, if you use the variable out of function without marking it static, then:</p><p><img src="/images/HLSLNote_Static.png" /></p><p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-syntax">Official document</a> is good, but sometimes I just don't know if it's a feature of the language or a bug.</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">The date of this post will be upgraded once it updates</summary>
    
    
    
    <category term="GameDev" scheme="https://kyriota.github.io/categories/GameDev/"/>
    
    
  </entry>
  
  <entry>
    <title>Unity Pixelated Art Style In URP</title>
    <link href="https://kyriota.github.io/2022/08/02/UnityPixelatedArtStyleInURP/"/>
    <id>https://kyriota.github.io/2022/08/02/UnityPixelatedArtStyleInURP/</id>
    <published>2022-08-02T17:47:00.000Z</published>
    <updated>2024-07-18T15:00:27.715Z</updated>
    
    <content type="html"><![CDATA[<p>I'm considering diving into game dev for a rather long period from now on, and was managing to find a possible way to build a traditional RPG that can be handled only by myself from any aspect such as assets like models, animation, sound design, and system. I think I should write down my progress and tech details in the future, so that I can feel the speed of my progress and share my experience to help those who are struggling like I was.</p><p><img height="333" width="515" src="/images/Unity_Pixelated_Art_Style_Final.png" ></p><span id="more"></span><h1 id="unity-pixelated-art-style-in-urp">Unity Pixelated Art Style In URP</h1><p>Currently I'm trying to find a suitable and simple art style for this project to work with, and I saw some pixelated 3D art style created by <a href="https://twitter.com/t3ssel8r">t3ssel8r</a> by coincidence. Though he didn't offer any tutorial for beginners to work with, <del>however I am not beginner at all,</del> <small>(Nope)</small> I can still get some information from the comments below his video or twitter especially on how he managed all those amazing works.</p><p><img src="/images/Unity_Pixelated_Art_Style_t3ssel8r.png" /></p><center><small><span class="citation" data-cites="t3ssel8r">@t3ssel8r</span>'s recent work</small></center><p>In order to get a harmonious pixelated art style like t3ssel8r's, I tried to do some breakdown to his final result like the one above.</p><ul><li>Cel shading (also called toon shading, which is more familiar to most developers), a necessity since it can reduce the color range, which is quite important for pixel art in its definition.</li><li>Down sampling, but I find his outcome differ from those pixelated style made by down sampling from camera output, because his method keeps pixel moving smoothly in screen rather than making the whole screen low resolution, so I think he may used some custom render pileline, which is still an unknown area for me to conquer. <strong>This part will not be included in this post</strong>, maybe a relevant post later.</li><li>Outliner, also a significant element in most pixel art. In traditional pixel art, the outline should be seamless, and outline should appear in any sharp edges as if it's some kind of highlights. So seamless outlines drawn by combining depth, normal, and scene color information is desired.</li></ul><h2 id="cel-shading">Cel Shading</h2><p>I started to work with cel shading first, and found a developer <a href="https://www.youtube.com/c/RobinSeibold">Robin Seibold</a> uploaded a cel shader graph with great expansibility in this <a href="https://www.youtube.com/watch?v=gw31oF9qITw">video</a> (BTW his video editing is absolutely fabulous). So I copied one from his video (great thanks to Robin) and modified the shader to suit my own needs (just added more borders to the shadow rather than only one border between lit area and unlit area).</p><p>I've also tried to implement gooch shading, but eventually failed to make it support multiple light sources. So I'd implement this later and put these content in another post if I succeed.</p><p>To follow the tutorial by Robin, I found my URP version so old that doesn't include some file that the tutorial may use. Mine is only about URP v10.x.x because that's already the highest version Unity 2020 can support according to this <a href="https://github.com/Unity-Technologies/Graphics">official GitHub repo</a>. So I upgraded my Unity to the newest LST 2021.3, which supports URP 12.x.x and dynamic additional light shadows, and made my environment as close to the tutorial as possible.</p><h3 id="outcome">Outcome</h3><p>For a basic cel material like this one I made below:</p><p><img height="300" width="300" src="/images/Unity_Pixelated_Art_Style_CelMatPreview.png" ></p><p>It is coded by following <a href="https://www.youtube.com/c/RobinSeibold">Robin Seibold</a>'s tutorial I mentioned before, and added an effect that create a certain number of shadow edges, which can be done by just tweaking the diffuse stuff.</p><p>I found a <a href="https://sketchfab.com/3d-models/higokumaru-honkai-impact-3rd-0e903387170846f5939adaa0c277b91b">free model</a> of Honkai Impact 3rd on Sketchfab to test out the cel shading:</p><p><img src="/images/Unity_Pixelated_Art_Style_CelTest.png" /></p><p>And I think the effect reaches my desire. This model contains too much details which I can never create in limited time. Currently I just care that whether the shadows, rim, specular looks nice after pixelate, which seems not bad at least.</p><h2 id="seamless-outline">Seamless Outline</h2><p>I used to think outline easy and handy, but this time, it takes me 3 or 4 days to get everything nearly right.</p><p><img height="200" width="200" src="/images/Unity_Pixelated_Art_Style_Outline.png"></p><p>Outline is not edge case like "Pixel Perfect Cam", so I found quite a lot sources as references:</p><ul><li><a href="https://www.youtube.com/c/RobinSeibold">Robin Seibold</a> again, he built a clean outline and taught how to write a renderer feature in URP. Although not using his edge detection method nor using renderer feature to generate normal texture, I learnt a lot about renderer feature by following his video.</li><li><a href="https://www.youtube.com/channel/UCEk8HlMSyA-nkZgFDf5kDEQ">James King</a>, he tried to recreate <a href="https://twitter.com/t3ssel8r">t3ssel8r</a>'s outline and pixelated art style in his Three JS project and put it on GitHub.</li></ul><p>This <a href="https://omar-shehata.medium.com/how-to-render-outlines-in-webgl-8253c14724f9">article</a> explained how to draw outline with depth texture and normal texture like the following image does (image from that article)</p><p><img src="/images/Unity_Pixelated_Art_Style_DepthNormalProcess.png" /></p><p>To get start with edge detection algorithm, we need the references to a few textures first.</p><h3 id="textures">Textures</h3><h4 id="depth-texture">Depth Texture</h4><p>Depth texture is much easier to get than normal texture. In order to make URP render a depth texture, check "Depth Texture" in URP Assets. After so, the <code>_CameraDepthTexture</code> should be rendered and can be seen through <code>Frame Debugger</code>. In shader graph, <code>Scene Depth</code> node is also provided to access depth conveniently.</p><p><img src="/images/Unity_Pixelated_Art_Style_URPAsset.png" /></p><p>To know more about depth in game, make sure to check this <a href="https://www.cyanilux.com/tutorials/depth/">article</a>.</p><p>It's noticeable that in orthographic cam, raw depth is already linear01, so you shouldn't use <code>linear01</code> mode nor <code>eye</code> mode in shader graph when using depth node.</p><h4 id="normal-texture">Normal Texture</h4><p>There're several ways to get a normal texture:</p><ul><li><code>G-Buffer</code> in deferred rendering</li><li><code>_CameraNormalsTexture</code> requested by <code>ConfigureInput(ScriptableRenderPassInput.Normal)</code></li><li>Draw your own normal texture with <code>ScriptableRendererFeature</code> by overriding material on every objects (another version of replacement shader)</li></ul><p>It's worth notice that <code>G-Buffer</code> and <code>_CameraNormalsTexture</code> stores <strong>world normal</strong> since those data are usually used to calculate shading / lighting stuff. Drawing your own texture with <code>ScriptableRendererFeature</code> may give you more customizable result, and of course you can draw view normal directly by using this method. However, since override material / replacement shader will override your original shaders, vertex displacement resulted by vertex shader will not be applied, nor normal map or any stuff to do with your original material. As screenshot shows below, <code>_CameraNormalsTexture</code> and <code>G-Buffer</code> hold details and vertex modifications which <code>ScriptableRendererFeature</code> doesn't.</p><p><img src="/images/Unity_Pixelated_Art_Style_NormalMap.png" /></p><center><small>L: world normal from G-Buffer<br>R: view normal by override material</small></center><h5 id="g-buffer">G-Buffer</h5><p>The easiest one is selecting <code>Rendering Path</code> as <code>Deferred</code> in <code>URP renderer data</code>, and set camera depth texture mode to <code>DepthNormal</code> in script.</p><p><img src="/images/Unity_Pixelated_Art_Style_URPRendererData.png" /></p><p>Because deferred rendering generate <code>G-Buffer</code>, which is used to save scene info in GPU to assist shading computations. The normal texture of the scene can be read from it. With URP 12, it is stored in <code>_GBuffer2</code>.</p><p><img src="/images/Unity_Pixelated_Art_Style_GBuffer2.png" /></p><p>The normal texture in G-Buffer is world normal, and it stores negative values, which means even though some pixels are pitch black visually, it still stores normal information which can be transformed to view normal by matrix.</p><p><img src="/images/Unity_Pixelated_Art_Style_CameraNormal.png" /></p><center><small>L: world normal from G-Buffer<br>R: view normal converted</small></center><h5 id="scriptable-render-pass-input">Scriptable Render Pass Input</h5><p>World normal texture can be get not only in deferred rendering, but also in forward rendering if you requested in a renderer feature. Only one line of code can do it: <code>ConfigureInput(ScriptableRenderPassInput.Normal);</code></p><p>After adding this renderer feature to the URP renderer data, <code>_CameraNormalsTexture</code> will be rendered just the same as the normal texture in <code>G-Buffer</code>.</p><h5 id="override-material">Override Material</h5><p>In <code>Built-in</code> pipeline, you can use <code>Replacement Shader</code> to render the whole scene in a single material. But you can't use it to control the camera in URP since this function is not contained in <code>Camera</code> class, instead you can use override material to achieve same result.</p><p>To do so, a basic understanding to renderer feature work flow is required. Here is a <a href="https://zhuanlan.zhihu.com/p/115080701">Chinese reference</a> about it.</p><p>In a word, you may wanna use a view normal material as override material and render your scene into a render texture you created. I'm not that familiar with these currently, so just some ambiguous guidance below.</p><ul><li><p>To create a render target:</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">readonly</span> RenderTargetHandle normals;</span><br><span class="line">normals.Init(<span class="string">&quot;_SceneViewSpaceNormals&quot;</span>);</span><br></pre></td></tr></table></figure></p></li><li><p>A customized <code>RenderTextureDescriptor</code> can offer options like color format, texture size, etc. It contains all the information required to create a render texture.</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Copy from cam settings</span></span><br><span class="line">RenderTextureDescriptor normalsTextureDescriptor = cameraTextureDescriptor;</span><br><span class="line"><span class="comment">// Customization</span></span><br><span class="line">normalsTextureDescriptor.colorFormat = RenderTextureFormat.ARGB32;</span><br></pre></td></tr></table></figure></p></li><li><p>Create a temporary render texture with given parameters, and set it up as a global shader property with nameID. Then redirect render target to the temporary RT, and clear the attachment into the color or depth/stencil values (depending on the format of this attachment)</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cmd.GetTemporaryRT(normals.id, normalsTextureDescriptor, FilterMode.Bilinear);</span><br><span class="line">ConfigureTarget(normals.Identifier());</span><br><span class="line">ConfigureClear(ClearFlag.All, Color.black);</span><br></pre></td></tr></table></figure></p></li><li><p>Create drawing settings and set override material</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">DrawingSettings drawSettings = CreateDrawingSettings(shaderTagIdList, <span class="keyword">ref</span> renderingData, renderingData.cameraData.defaultOpaqueSortFlags);</span><br><span class="line">drawSettings.overrideMaterial = normalsMaterial;</span><br><span class="line">FilteringSettings filteringSettings = <span class="keyword">new</span> FilteringSettings(RenderQueueRange.opaque, layerMask);</span><br><span class="line">context.DrawRenderers(renderingData.cullResults, <span class="keyword">ref</span> drawSettings, <span class="keyword">ref</span> filteringSettings);</span><br></pre></td></tr></table></figure></p></li><li><p>Release temporary RT after usage</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">OnCameraCleanup</span>(<span class="params">CommandBuffer cmd</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cmd.ReleaseTemporaryRT(normals.id);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="opaque-texture">Opaque Texture</h4><p><code>_CameraOpaqueTexture</code> provides a snapshot of the scene right before URP renders any transparent meshes. In shader graph, <code>SceneColor</code> node would sample this texture as return value.</p><p>By enabling the opaque texture, we can draw outline with reference to the original pixel color. This texture can be also used to create effects like frosted glass, water refraction, or heat waves.</p><p>To enable this texture, check the checkbox under depth texture checkbox in URP asset.</p><h3 id="edge-detection">Edge Detection</h3><p>After preparations of view normal texture, depth texture, opaque texture, it's time to do real edge detections with these references.</p><p>Edge detection in game is using basically the same tech in image edge detection. You sample a pixel and its neighbors, comparing their difference. However, since we got both normal and depth textures, it's possible to achieve more complicated effect by combining them together. <a href="https://www.youtube.com/channel/UCEk8HlMSyA-nkZgFDf5kDEQ">James King</a> is doing a really nice job on this, and made the outline only one pixel wide, which is a significant factor contributing to the overall appearance of the scene. So I'd explain his logic and talk about issues I encountered when implementing his method in Unity.</p><h4 id="depth-edge">Depth Edge</h4><ul><li><p><strong>Preprocess</strong></p><p>As I said before, raw depth in orthographic cam is already linear01, but the issue is that when far plane and near plane are far away from each other, the difference between two depth value in the texture becomes too tiny to work with. After my experimenting, it would be clear and reasonable when <code>far plane - near plane = 20</code>, so I made a multiplier by referencing far plane and near plane to change my depth scale making it reasonable.</p><p>Depth values would be difference depending on the platform:</p><ul><li>Direct3D-like, Reversed Z Buffer : <strong>1</strong> at the near plane, <strong>0</strong> at the far plane</li><li>OpenGL-like, Z Buffer : <strong>0</strong> at the near plane, <strong>1</strong> at the far plane</li></ul><p>This reverse flag is stored in <code>_ProjectionParams.x</code>, it can also be accessed in <code>Camera</code> node in shader graph.</p></li><li><p><strong>Process</strong></p><p>Now sample a few points around the original pixel, and get the bias by <code>bias = neighbor - center</code>. If using this bias directly, both edges inside the object and outside the object will be drawn, which results in at least 2 pixel wide depth outline. But this can be fixed by clamping bias to [0, 1] or [-1, 0], leaving only one pixel wide outline that either nearer or farther one. After so, use <code>smoothstep</code> to control the visibility of edges with different biases.</p></li></ul><p>Then we are done with it.</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Pseudo Code</span></span><br><span class="line"><span class="function"><span class="built_in">float</span> <span class="title">GetDepthStrength</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">float</span> difference = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">foreach</span> (<span class="built_in">float</span> bias <span class="keyword">in</span> depthBiases)</span><br><span class="line">        difference += clamp(bias, <span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> smoothstep(<span class="number">0.01f</span>, <span class="number">0.02f</span>, difference);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="normal-edge">Normal Edge</h4><ul><li><p><strong>Preprocess</strong></p><p>If you generate view normal texture by overriding materials, you should remap your view normal material from [-1, 1] to [0, 1] since when rendering to the render texture, negative values will be set to 0. By doing so you won't lose any normal information. You can remap it back to [-1, 1] when sampling your view normal texture.</p></li><li><p><strong>Edge Sharpness</strong></p><p>The sharpness is the easiest one to understand in the following parts. <code>sharpness = 1 - dot (normal, neighborNormal)</code></p></li><li><p><strong>Normal Indicator</strong></p><p>Similar to depth edges, we'd like to get normal biases between it's neighbors: <code>bias = normal - neighborNormal</code>. Since the bias is a <code>float3</code>, it can't be simply clamped to cut one side of outline (the normal outline is also 2 pixels wide as shown below)</p><p><img height="200" width="200" src="/images/Unity_Pixelated_Art_Style_RobinOutlineNormal.png"></p><p>To make the normal outline also one pixel wide, James declared a direction vector3 (e.g. float3(1, 1, 1)) as parameter and get the dot product of which and normal bias: <code>dot(bias, direction vector)</code>. Since the normal texture is in view space, such dot product can cull edges on faces in opposite direction of the direction parameter.</p><p><img src="/images/Unity_Pixelated_Art_Style_ViewNormal.png" /></p><center><p><small>View Space Normal</small></p></center><p>This dot product can then be passed in <code>smoothstep</code> to get control of edge strength and cull the undesired edges. For example, if the direction parameter is <code>float3(1, 1, 1)</code>, then only edges on faces towards up-right and pointing out of screen will be drawn.</p></li><li><p><strong>Depth Indicator</strong></p><p>Applying one pixel outline though, it's unsurprised to find that normal edges shows up outside the object, and it also draws outlines on concave edges.</p><p><img src="/images/Unity_Pixelated_Art_Style_DepthIndicator.png" /></p><center><p><small>Even two cubes have no contact<br>The normal texture indeed has difference there<br>And happen to match the requirement of the direction parameter</small></p></center><p><img src="/images/Unity_Pixelated_Art_Style_ConcaveEdge.png" /></p><center><p><small>The concave edges in red box are not desired in most pixel art</small></p></center><p>To eliminate these outlines, we can make only the shallower pixel detects the normal edge. So depth bias is required here to calculate the depth indicator. Get the average depth bias of the original pixel, if the value is negative, it means the pixel is farther than its neighbors, which should be culled when detecting normal edges; otherwise it counts.</p></li></ul><p>So the final normal edge strength can be represented as follows:</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Pseudo Code</span></span><br><span class="line"><span class="function"><span class="built_in">float</span> <span class="title">GetNormalStrength</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">float</span> normalIndicator = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">foreach</span> (float3 neighborNormal <span class="keyword">in</span> neighborNormals)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">float</span> sharpness = (<span class="number">1</span> - dot(normal, neighborNormal));</span><br><span class="line">        <span class="built_in">float</span> normalIndicator = smoothstep(<span class="number">-0.01f</span>, <span class="number">0.01f</span>, dot(normalBias, directionPara));</span><br><span class="line">        normalIndicator += sharpness * normalIndicator;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">float</span> avgDepthBias = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">foreach</span> (<span class="built_in">float</span> bias <span class="keyword">in</span> depthBiases)</span><br><span class="line">        avgDepthBias += bias;</span><br><span class="line">    avgDepthBias /= neighborPointCount;</span><br><span class="line">    <span class="keyword">return</span> step(<span class="number">0.1f</span>, normalIndicator * clamp(sign(avgDepthBias), <span class="number">0</span>, <span class="number">1</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="outcome-1">Outcome</h4><p>Two parameters to control depth and normal edge strength may be wanted. Usually, our brain would like the outline of an object darker, and the normal edges brighter:</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">float</span> strength =</span><br><span class="line">    depthEdgeStrength &gt; <span class="number">0</span> ?</span><br><span class="line">    (<span class="number">1</span> - _depthStrengthPara * depthEdgeStrength) :</span><br><span class="line">    (<span class="number">1</span> + _normalStrengthPara * normalEdgeStrength);</span><br><span class="line">_color = float3(<span class="number">.5</span>f, <span class="number">.5</span>f, <span class="number">.5</span>f) * strength;</span><br></pre></td></tr></table></figure><p><img src="/images/Unity_Pixelated_Art_Style_FinalOutline.png" /></p><p>To add scene color to the outline, I analysed the pros and cons of <code>_color = _texel * strength</code> and <code>_color = pow(_texel, strength)</code>, and found the method below is my favorite.</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">_color =</span><br><span class="line">    dei &gt; <span class="number">0</span> ?</span><br><span class="line">    _texel * (<span class="number">1</span> - _depthStrength * dei) :</span><br><span class="line">    pow(_texel, <span class="number">1</span> - _normalStrength * nei);</span><br></pre></td></tr></table></figure><p><img src="/images/Unity_Pixelated_Art_Style_Final.gif" /></p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;I&#39;m considering diving into game dev for a rather long period from now on, and was managing to find a possible way to build a traditional RPG that can be handled only by myself from any aspect such as assets like models, animation, sound design, and system. I think I should write down my progress and tech details in the future, so that I can feel the speed of my progress and share my experience to help those who are struggling like I was.&lt;/p&gt;
&lt;p&gt;&lt;img height=&quot;333&quot; width=&quot;515&quot; src=&quot;/images/Unity_Pixelated_Art_Style_Final.png&quot; &gt;&lt;/p&gt;</summary>
    
    
    
    <category term="GameDev" scheme="https://kyriota.github.io/categories/GameDev/"/>
    
    
  </entry>
  
  <entry>
    <title>Convolutional Neural Network</title>
    <link href="https://kyriota.github.io/2022/06/27/ConvolutionalNeuralNetwork/"/>
    <id>https://kyriota.github.io/2022/06/27/ConvolutionalNeuralNetwork/</id>
    <published>2022-06-27T18:06:00.000Z</published>
    <updated>2023-03-30T11:35:14.191Z</updated>
    
    <content type="html"><![CDATA[<p>I roughly learnt CNN recently and was about to code one just like the FCNN I coded before. However this may be too costly to time, so I'll use TensorFlow in this case and learn more about TF conveniently.</p><span id="more"></span><!--toc--><h1 id="convolutional-neural-network">Convolutional Neural Network</h1><p>(This is my first English blog, and maybe there would be more blogs in EN in the future if I'd like to practice my writing in English. My English sucks, so feel free to raise issues if any error is found)</p><h2 id="cnn-introduction">CNN Introduction</h2><p>Actually the core feature of convolutional neural network is the convolutional layer and maybe pooling layer. But actually pooling layer is usually treated as a part of convolutional layer since it does linear calculations.</p><p>A convolutional layer can be concluded as following image, which was drawn by myself.</p><p><img src="/images/CNN_summary.png" /></p><h3 id="structure">Structure</h3><p>A convolutional layer has two parameter matrices we need to train, which are <code>Filter</code> and <code>Bias</code>. Being different from FC layer, in a convolutional layer, <code>Bias</code> matrix cannot be blended into <code>Filter</code> matrix due to the cross correlation operation.</p><p>Comparing with FC layers, the biggest change in convolutional layers is that convolutional layers take tensors as inputs, keeping the form of tensor from the beginning to the end. On the contrary, in the FCNN, we would usually reshape the input to a vector at first.</p><p>Mathematically, we can treat a FC layer as a special case of convolutional layer, and the convolutional layer is the generalization of the FC layer. To prove this, just making all matrices in convolutional layer one dimension. The following image can explain the conclusion further.</p><p><img src="/images/CNN_CNN2FCNN.png" /></p><h3 id="working-flow">Working Flow</h3><p>A convolutional layer feeds its input to convolutional kernels. The kernels will do cross correlation operation and add the bias accordingly. At the end, collect all outputs from all kernels and make it a new tensor by just stacking those outputs. The output of a convolutional layer is called feature map.</p><h3 id="goal">Goal</h3><p>A convolutional layer has a rather different goal from the FC layer.</p><p>When using FC layers, we are expecting a humanoid brain neuron like model can learn from generic inputs. However, when doing image processing, this particular topic leads to huge amount of trainable parameters in FCNN. In which case, we can do some pre-process to the input image since we can easily see that not every pixel in the input is important for the task, and this pre-process is called convolution. From this aspect, we can treat convolutional layers in the network as feature detectors, the convolutional layers learn to extract meaningful features from the image using the filter and bias tensor, and use pooling layers to compress / emphasis the feature it extracted, making fewer trainable parameters.</p><p>But as I mentioned, convolutional layers are only good at feature extraction. So when doing classification with multiple features, it's still better to use FC layers. In which case, a CNN usually end up with FC layers to give the final outputs.</p><p>In conclusion, in CNN, the goal of a convolutional layer is extracting features from a rather huge input. The goal of a pooling layer is compressing data without damaging the main features. The goal of a FC layer in the end of CNN is to learn classification by giving features just as normal AI things would do.</p><h3 id="application">Application</h3><p>We can apply CNN in not only image recognition but also other area that shares the basic features with image recognition. For example, go and mahjong playing used CNN a lot. Especially in go playing, we can treat the go board as a 19x19 image with 48 channels (the reason why there should be 48 channels was raised by pros). and we can process every local part separately to get features of current situation. However, pooling layers are NOT used in go AI since the data loss in pooling operation is deadly to describe a go game. So design your CNN structure thoughtfully before putting it into training.</p><h3 id="back-prop">Back Prop</h3><p>I used to be quite curios about the back propagation in convolutional layer, and pooling layers also made me confused. But actually they are basically the same as back prop in FCNN if you expand all formulas.</p><p>This <a href="https://www.youtube.com/watch?v=Lakz2MoHy6o&amp;t=1097s">Video</a> explained how back prop in convolutional layer works.</p><p><img src="/images/CNN_BackProp.png" /></p><p>As for max pooling, since it's just doing linear calculation, when doing back prop, just implement the gradient to the max <code>A</code> of the previous one if there is a max pooling layer between them, and others' gradients are 0.</p><h2 id="cnn-example">CNN Example</h2><p>Following this <a href="https://www.youtube.com/watch?v=eMMZpas-zX0">video</a>, I coded a simple CNN example with <code>TensorFlow</code> to classify <code>CIFAR10</code>, a dataset with 10 classes as following.</p><blockquote><p>'airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'</p></blockquote><p>However, in my opinion, some images are too blur to classify even for human, so it's not surprised that AI performs poorly.</p><p><img src="/images/CNN_dataset.jpg" /></p><p>Though there's somebody made accuracy 90%+ AI with <code>ResNet</code>, that should be the content of another blog then. I made it 86.0% in 40 epochs, which is enough for experimenting CNN.</p><h3 id="structure-1">Structure</h3><p>Referring <code>LeNet</code>, <code>VGG</code>, and so on, we can see that the width and length of the tensor tend to go down, whereas the number of channels does increase as we go deeper into the layers of the network. Another pattern that still often repeated today is that we might have some one or more conv layers followed by a pooling layer.</p><p><img src="/images/CNN_LeNet.png" /></p><center><small>LeNet</small></center><p>So I came up with this structure:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">Model: &quot;sequential&quot;</span><br><span class="line">_________________________________________________________________</span><br><span class="line"> Layer (type)                Output Shape              Param #   </span><br><span class="line">&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;</span><br><span class="line"> conv2d (Conv2D)             (None, 32, 32, 64)        1792      </span><br><span class="line">                                                                 </span><br><span class="line"> max_pooling2d (MaxPooling2D  (None, 16, 16, 64)       0         </span><br><span class="line"> )                                                               </span><br><span class="line">                                                                 </span><br><span class="line"> conv2d_1 (Conv2D)           (None, 16, 16, 128)       73856     </span><br><span class="line">                                                                 </span><br><span class="line"> max_pooling2d_1 (MaxPooling  (None, 8, 8, 128)        0         </span><br><span class="line"> 2D)                                                             </span><br><span class="line">                                                                 </span><br><span class="line"> conv2d_2 (Conv2D)           (None, 8, 8, 256)         295168    </span><br><span class="line">                                                                 </span><br><span class="line"> max_pooling2d_2 (MaxPooling  (None, 4, 4, 256)        0         </span><br><span class="line"> 2D)                                                             </span><br><span class="line">                                                                 </span><br><span class="line"> flatten (Flatten)           (None, 4096)              0         </span><br><span class="line">                                                                 </span><br><span class="line"> dense (Dense)               (None, 256)               1048832   </span><br><span class="line">                                                                 </span><br><span class="line"> dense_1 (Dense)             (None, 10)                2570      </span><br><span class="line">                                                                 </span><br><span class="line">&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;</span><br><span class="line">Total params: 1,422,218</span><br><span class="line">Trainable params: 1,422,218</span><br><span class="line">Non-trainable params: 0</span><br><span class="line">_________________________________________________________________</span><br><span class="line">None</span><br></pre></td></tr></table></figure><p>However, after a few epochs, I found the accuracy too low to convince me that I have reached CNN's full potential, which was only around 70~75%, and was heavily overfitted since the training set accuracy is around 90%.</p><h3 id="training-tricks">Training Tricks</h3><p>Actually, even though you've come up with a fantastic structure, or just copied a successful classic structure from Internet, without some tricky training approaches, you can barely make the network works well.</p><h4 id="dropout">Dropout</h4><p>Dropout is a great way to avoid overfitting. I've implemented this method in my FCNN project and found it useful, so I'd recommend it also to CNN. We can add dropout layer into convolutional layers or FC layers, the dropout rate should be between 0.1 and 0.5. In detail, you should make the network receive all input information or most of it, so the dropout rate of the input layer should be like 0.1 or 0, then feel free about the dropout rate in hidden layers.</p><p>The reason why dropout helps, in my personal belief, is that randomly disabling neurons makes your single network more likely to be made up with tons of smaller networks, which improves robustness of your model evidently.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">model.add(layers.Dropout(<span class="number">0.4</span>))</span><br></pre></td></tr></table></figure><p>The code above means it will disable 40% neurons in the previous layer randomly.</p><p>In CNN, it's said adding dropout layer before or after pooling layer are both OK. I personally think adding a dropout layer after a pooling layer maximizes the effect of a dropout layer, so I'd prefer this method in most cases.</p><p>Adding a dropout layer will also make your validation accuracy higher than your training accuracy at the beginning. The difference between validation accuracy and training accuracy should be smaller and smaller as model learns.</p><h4 id="regularization">Regularization</h4><p>Doing regularization to weights and biases can also limit the level of overfitting because it's minimizing the complexity of your model. This can be done by setting <code>kernel_regularizer</code> to some <code>regularizers</code> when creating a layer. The regularization parameter, or <code>weight_decay</code>, is usually set to <code>1e-4</code>.</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">model.add(layers.Dense(<span class="number">256</span>, activation=<span class="string">&#x27;relu&#x27;</span>, kernel_regularizer=regularizers.l2(<span class="number">1e-4</span>)))</span><br></pre></td></tr></table></figure><h4 id="learning-rate-decay">Learning Rate Decay</h4><p>When we're training the model, we're expecting the model to learn more 'carefully' while it's getting more accurate. This demand leads us to learning rate decay.</p><p>Firstly, we can do a rather large decay to change the stage of training manually. For example, we can start the training with <code>learning_rate=0.002</code> for 10 epochs, and reduce the learning rate to 0.001 manually for another 10 epochs, by doing which, we can make our model to learn faster and more accurate at the same time.</p><p>Secondly, we can do very tiny decay in every stage of training. For example, when starting with <code>learning_rate=0.002</code>, we can reduce learning rate a bit after every epoch ,usually 1e-6, which also helps. To implement this method, just set <code>decay</code> parameter when initializing your optimizer.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">optim = keras.optimizers.Adam(learning_rate=<span class="number">0.0003</span>, decay=<span class="number">1e-6</span>)</span><br></pre></td></tr></table></figure><h4 id="batch-normalization">Batch Normalization</h4><p>Before feeding data to the model, we'd like to preprocess the data to make it close to Gaussian distribution to make the "landscape" of loss function smoother, in extreme cases, a model may unable to be trained or stay a low accuracy if normalization of input data is not applied. Since we can use this normalization in the input layer, we can also apply this method to hidden layers. We can collect the output of a hidden layer and make it Gaussian distribution. According to researches, doing batch normalization before and after activation are usually both OK. But we'd like to do it before activation when using sigmoid as activation function because sigmoid is happy to take input close to 0, from which we can get rather big gradient. So it's wise to make your decision after considering the actual conditions, features of your model, and the underlying principles of the actual processes.</p><p>When testing the model, it would use the delta and sigma at training stage to process the testing data.</p><p>I forgot to implement this kind of layers at first, and got accuracy to 84.46%. After implementing batch normalization layers, the accuracy increased by 1.5%, which is an evident improvement at such accuracy. So batch normalization is really a simple and effective way to improve your network.</p><h4 id="summary">Summary</h4><p>Training a ANN is very similar to alchemy. You need to tweak your network hundred times and try every little trick including but not limited to what I mentioned above to improve it. So you need enough patience and knowledge to become a really powerful AI alchemist.</p><h3 id="source">Source</h3><p>After implemented all tricks to prevent overfitting and normal optimizations such as input standardization, mini-batch, validation dataset, etc. I finally made the testing accuracy 86.03%, which is enough for me.</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Source    </div>    <div class='spoiler-content'>        <ul><li><p><strong>Importing</strong></p><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> regularizers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> random</span><br></pre></td></tr></table></figure></p></li><li><p><strong>Preprocess</strong></p><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">dataset = keras.datasets.cifar10</span><br><span class="line">(data_images, train_labels), (test_images, test_labels) = dataset.load_data()</span><br><span class="line"></span><br><span class="line">class_names = [<span class="string">&#x27;airplane&#x27;</span>, <span class="string">&#x27;automobile&#x27;</span>, <span class="string">&#x27;bird&#x27;</span>, <span class="string">&#x27;cat&#x27;</span>, <span class="string">&#x27;deer&#x27;</span>,</span><br><span class="line">              <span class="string">&#x27;dog&#x27;</span>, <span class="string">&#x27;frog&#x27;</span>, <span class="string">&#x27;horse&#x27;</span>, <span class="string">&#x27;ship&#x27;</span>, <span class="string">&#x27;truck&#x27;</span>]</span><br><span class="line">dataset_size = <span class="built_in">len</span>(data_images)</span><br><span class="line">train_images, test_images = data_images / <span class="number">255.0</span>, test_images / <span class="number">255.0</span></span><br><span class="line"></span><br><span class="line">mean = np.mean(train_images, axis=<span class="built_in">tuple</span>(<span class="built_in">range</span>(train_images.ndim-<span class="number">1</span>)))</span><br><span class="line">std = np.std(train_images, axis=<span class="built_in">tuple</span>(<span class="built_in">range</span>(train_images.ndim-<span class="number">1</span>)))</span><br><span class="line">train_images = (train_images - mean) / std</span><br><span class="line">test_images = (test_images - mean) / std</span><br><span class="line"></span><br><span class="line">test_labels = np.squeeze(np.array(tf.one_hot(test_labels, <span class="number">10</span>)))</span><br><span class="line">train_labels = np.squeeze(np.array(tf.one_hot(train_labels, <span class="number">10</span>)))</span><br><span class="line"></span><br><span class="line">valid_images = train_images[-<span class="number">1000</span>:]</span><br><span class="line">train_images = train_images[:-<span class="number">1000</span>]</span><br><span class="line">valid_labels = train_labels[-<span class="number">1000</span>:]</span><br><span class="line">train_labels = train_labels[:-<span class="number">1000</span>]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>Modeling</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">weight_decay = <span class="number">1e-4</span></span><br><span class="line"></span><br><span class="line">model = keras.models.Sequential()</span><br><span class="line"></span><br><span class="line">model.add(layers.Conv2D(<span class="number">64</span>, <span class="number">3</span>, activation=<span class="string">&#x27;relu&#x27;</span>, padding=<span class="string">&#x27;same&#x27;</span>, input_shape=(<span class="number">32</span>, <span class="number">32</span>, <span class="number">3</span>),</span><br><span class="line">                        kernel_regularizer=regularizers.l2(weight_decay)))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.MaxPool2D((<span class="number">2</span>, <span class="number">2</span>)))</span><br><span class="line">model.add(layers.Dropout(<span class="number">0.1</span>))</span><br><span class="line"></span><br><span class="line">model.add(layers.Conv2D(<span class="number">128</span>, <span class="number">3</span>, activation=<span class="string">&#x27;relu&#x27;</span>, padding=<span class="string">&#x27;same&#x27;</span>,</span><br><span class="line">          kernel_regularizer=regularizers.l2(weight_decay)))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.MaxPool2D((<span class="number">2</span>, <span class="number">2</span>)))</span><br><span class="line">model.add(layers.Dropout(<span class="number">0.4</span>))</span><br><span class="line"></span><br><span class="line">model.add(layers.Conv2D(<span class="number">256</span>, <span class="number">3</span>, activation=<span class="string">&#x27;relu&#x27;</span>, padding=<span class="string">&#x27;same&#x27;</span>,</span><br><span class="line">          kernel_regularizer=regularizers.l2(weight_decay)))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.MaxPool2D((<span class="number">2</span>, <span class="number">2</span>)))</span><br><span class="line">model.add(layers.Dropout(<span class="number">0.4</span>))</span><br><span class="line"></span><br><span class="line">model.add(layers.Flatten())</span><br><span class="line"></span><br><span class="line">model.add(layers.Dense(<span class="number">256</span>, activation=<span class="string">&#x27;relu&#x27;</span>, kernel_regularizer=regularizers.l2(weight_decay)))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dropout(<span class="number">0.4</span>))</span><br><span class="line"></span><br><span class="line">model.add(layers.Dense(<span class="number">10</span>, activation=<span class="string">&#x27;softmax&#x27;</span>, kernel_regularizer=regularizers.l2(weight_decay)))</span><br></pre></td></tr></table></figure></li><li><p><strong>Training</strong></p><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">batch_size = <span class="number">64</span></span><br><span class="line">loss = keras.losses.CategoricalCrossentropy()</span><br><span class="line">metrics = [<span class="string">&quot;accuracy&quot;</span>]</span><br><span class="line">history = []</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">Train</span>(<span class="params">lr, epochs</span>):</span></span><br><span class="line">    optim = keras.optimizers.Adam(learning_rate=lr, decay=<span class="number">1e-6</span>)</span><br><span class="line">    model.<span class="built_in">compile</span>(optimizer=optim, loss=loss, metrics=metrics)</span><br><span class="line">    history.append(model.fit(train_images, train_labels, batch_size=batch_size, epochs=epochs,</span><br><span class="line">                             verbose=<span class="number">1</span>, validation_data=(valid_images, valid_labels),</span><br><span class="line">                             validation_batch_size=batch_size))</span><br><span class="line"></span><br><span class="line">Train(<span class="number">0.001</span>, <span class="number">10</span>)</span><br><span class="line">model.save(<span class="string">&#x27;Cifar10_CNN0&#x27;</span>, save_format=<span class="string">&#x27;tf&#x27;</span>)</span><br><span class="line"></span><br><span class="line">Train(<span class="number">0.0005</span>, <span class="number">10</span>)</span><br><span class="line">model.save(<span class="string">&#x27;Cifar10_CNN1&#x27;</span>, save_format=<span class="string">&#x27;tf&#x27;</span>)</span><br><span class="line"></span><br><span class="line">Train(<span class="number">0.0003</span>, <span class="number">10</span>)</span><br><span class="line">model.save(<span class="string">&#x27;Cifar10_CNN2&#x27;</span>, save_format=<span class="string">&#x27;tf&#x27;</span>)</span><br><span class="line"></span><br><span class="line">Train(<span class="number">0.0002</span>, <span class="number">10</span>)</span><br><span class="line">model.save(<span class="string">&#x27;Cifar10_CNN3&#x27;</span>, save_format=<span class="string">&#x27;tf&#x27;</span>)</span><br></pre></td></tr></table></figure></p></li></ul>    </div></div><h3 id="review">Review</h3><p><img src="/images/CNN_loss_rec.png" /></p><center><small>Accuracy Figure</small></center><p><img src="/images/CNN_acc_rec.png" /></p><center><small>Loss Figure</small></center><p>We can see that when changing to a smaller learning rate, the loss drops rapidly (Look at 10, 20, 30 epochs in the loss figure). Which proves that learning rate should match the stage of the model when training.</p><p>Finally, the most 喜闻乐见 (interesting and funny) part: Error prediction reviewing. I've labeled these data by <code>prediction / correct label</code></p><p><img src="/images/CNN_Wrong_Predictions.png" /></p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;I roughly learnt CNN recently and was about to code one just like the FCNN I coded before. However this may be too costly to time, so I&#39;ll use TensorFlow in this case and learn more about TF conveniently.&lt;/p&gt;</summary>
    
    
    
    <category term="AI" scheme="https://kyriota.github.io/categories/AI/"/>
    
    
  </entry>
  
  <entry>
    <title>Friend Links Setup</title>
    <link href="https://kyriota.github.io/2022/06/03/FriendLinksSetup/"/>
    <id>https://kyriota.github.io/2022/06/03/FriendLinksSetup/</id>
    <published>2022-06-03T20:52:30.000Z</published>
    <updated>2022-06-03T09:15:09.383Z</updated>
    
    <content type="html"><![CDATA[<p>记录一下友链Page的Setup过程，非常无脑，搬砖就完了，感谢前辈的经验</p><span id="more"></span><h1 id="friend-links-setup">Friend Links Setup</h1><h2 id="reference">Reference</h2><p>代码来自<a href="https://finisky.github.io/simplestblogroll/">这篇博客</a></p><p>关于获取QQ头像的<a href="https://zhuanlan.zhihu.com/p/415485011">知乎专栏</a></p><h2 id="code">Code</h2><div class='spoiler collapsed'>    <div class='spoiler-title'>        CSS Source    </div>    <div class='spoiler-content'>        <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;post-body&quot;</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;links&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">style</span>&gt;</span></span><br><span class="line">         .links-content&#123;</span><br><span class="line"><span class="css">         <span class="attribute">margin-top</span>:<span class="number">1rem</span>;</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.link-navigation</span><span class="selector-pseudo">::after</span> &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">content</span>: <span class="string">&quot; &quot;</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">display</span>: block;</span></span><br><span class="line"><span class="css">         <span class="attribute">clear</span>: both;</span></span><br><span class="line">         &#125;</span><br><span class="line">         .card &#123;</span><br><span class="line"><span class="css">         <span class="attribute">width</span>: <span class="number">45%</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">font-size</span>: <span class="number">1rem</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">padding</span>: <span class="number">10px</span> <span class="number">20px</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">border-radius</span>: <span class="number">4px</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">transition-duration</span>: <span class="number">0.15s</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">margin-bottom</span>: <span class="number">1rem</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">display</span>:flex;</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.card</span><span class="selector-pseudo">:nth-child</span>(odd) &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">float</span>: left;</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.card</span><span class="selector-pseudo">:nth-child</span>(even) &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">float</span>: right;</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.card</span><span class="selector-pseudo">:hover</span> &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">1.1</span>);</span></span><br><span class="line"><span class="css">         <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">2px</span> <span class="number">6px</span> <span class="number">0</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.12</span>), <span class="number">0</span> <span class="number">0</span> <span class="number">6px</span> <span class="number">0</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.04</span>);</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.card</span> <span class="selector-tag">a</span> &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">border</span>:none;</span></span><br><span class="line">         &#125;</span><br><span class="line">         .card .ava &#123;</span><br><span class="line"><span class="css">         <span class="attribute">width</span>: <span class="number">3rem</span><span class="meta">!important</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">height</span>: <span class="number">3rem</span><span class="meta">!important</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">margin</span>:<span class="number">0</span><span class="meta">!important</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">margin-right</span>: <span class="number">1em</span><span class="meta">!important</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">border-radius</span>:<span class="number">4px</span>;</span></span><br><span class="line">         &#125;</span><br><span class="line">         .card .card-header &#123;</span><br><span class="line"><span class="css">         <span class="attribute">font-style</span>: italic;</span></span><br><span class="line"><span class="css">         <span class="attribute">overflow</span>: hidden;</span></span><br><span class="line"><span class="css">         <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.card</span> <span class="selector-class">.card-header</span> <span class="selector-tag">a</span> &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">font-style</span>: normal;</span></span><br><span class="line"><span class="css">         <span class="attribute">color</span>: <span class="number">#2bbc8a</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">font-weight</span>: bold;</span></span><br><span class="line"><span class="css">         <span class="attribute">text-decoration</span>: none;</span></span><br><span class="line">         &#125;</span><br><span class="line"><span class="css">         <span class="selector-class">.card</span> <span class="selector-class">.card-header</span> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> &#123;</span></span><br><span class="line"><span class="css">         <span class="attribute">color</span>: <span class="number">#d480aa</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">text-decoration</span>: none;</span></span><br><span class="line">         &#125;</span><br><span class="line">         .card .card-header .info &#123;</span><br><span class="line"><span class="css">         <span class="attribute">font-style</span>:normal;</span></span><br><span class="line"><span class="css">         <span class="attribute">color</span>:<span class="number">#a3a3a3</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">font-size</span>:<span class="number">14px</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">min-width</span>: <span class="number">0</span>;</span></span><br><span class="line"><span class="css">         <span class="attribute">overflow</span>: hidden;</span></span><br><span class="line"><span class="css">         <span class="attribute">white-space</span>: nowrap;</span></span><br><span class="line">         &#125;</span><br><span class="line">      <span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;links-content&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;link-navigation&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;card&quot;</span>&gt;</span></span><br><span class="line">               <span class="tag">&lt;<span class="name">img</span> <span class="attr">class</span>=<span class="string">&quot;ava&quot;</span> <span class="attr">src</span>=<span class="string">&quot;https://q.qlogo.cn/g?b=qq&amp;k=7PD14jYzQNoQ0KvkgCT3Xw&amp;s=640&quot;</span> /&gt;</span></span><br><span class="line">               <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;card-header&quot;</span>&gt;</span></span><br><span class="line">                  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">                     <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;https://blog.woooo.tech/&quot;</span>&gt;</span>Reverier<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">                  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">                  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;info&quot;</span>&gt;</span>XDSEC2019[Reverse、开发]<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">               <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;card&quot;</span>&gt;</span></span><br><span class="line">               <span class="tag">&lt;<span class="name">img</span> <span class="attr">class</span>=<span class="string">&quot;ava&quot;</span> <span class="attr">src</span>=<span class="string">&quot;https://q.qlogo.cn/g?b=qq&amp;k=UDUVtLNg8WvYmkaibG4wtIg&amp;s=640&quot;</span> /&gt;</span></span><br><span class="line">               <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;card-header&quot;</span>&gt;</span></span><br><span class="line">                  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">                     <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;https://d33b4t0.com/&quot;</span>&gt;</span>Deebato<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">                  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">                  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;info&quot;</span>&gt;</span>XDSEC2020[Crypto]<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">               <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>    </div></div><p>获取Key：https://ptlogin2.qq.com/getface?&amp;imgtype=1&amp;uin=123456</p><p>通过Key获取头像：https://q.qlogo.cn/g?b=qq&amp;k=xyOnRe5ML3Aw96iaaQ1hh6w&amp;s=640</p><p>通过QQ号获取头像：https://q.qlogo.cn/g?b=qq&amp;nk=123456&amp;s=640</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;记录一下友链Page的Setup过程，非常无脑，搬砖就完了，感谢前辈的经验&lt;/p&gt;</summary>
    
    
    
    <category term="Other" scheme="https://kyriota.github.io/categories/Other/"/>
    
    
  </entry>
  
  <entry>
    <title>L2 Targeted Attack</title>
    <link href="https://kyriota.github.io/2022/04/09/L2-TargetedAttack/"/>
    <id>https://kyriota.github.io/2022/04/09/L2-TargetedAttack/</id>
    <published>2022-04-09T05:57:20.000Z</published>
    <updated>2023-06-19T07:27:00.083Z</updated>
    
    <content type="html"><![CDATA[<p>终于是把这玩意儿自己实现了一遍，恰逢miniL CTF，这文章虽然是22.04.09写的，但是估计博客得等到五月多才会更新，届时将也再补充一些在miniL CTF中本题的情况</p><span id="more"></span><h1 id="l2-targeted-attack">L2 Targeted Attack</h1><p><strong>前置知识：<a href="https://kyriota.com/2022/01/16/%E5%85%A8%E8%BF%9E%E6%8E%A5%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%5B%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%85%A5%E9%97%A8%5D/">全连接神经网络</a></strong></p><p><strong>参考文献：<a href="https://arxiv.org/abs/1608.04644">Towards Evaluating the Robustness</a></strong></p><blockquote><p>碎碎念：这段时间又在捣鼓<a href="https://kyriota.com/2021/11/17/Uncertainty/">Uncertainty</a>，加点内容想去参加吉比特的Jam，等Jam完了可能的研究对象应该是CNN，UE5之类的（神经细胞自动机暂时就先鸽了，但确实很有趣，以后应该回去看看的</p></blockquote><h2 id="minil-ctf">miniL CTF</h2><p>有幸在校内的miniL CTF出一次题（一个L2TargetedAttack，一个CheatEngine改坐标），本来想做一个CNN的L2 Targeted Attack，这样可以把情景包装得有趣一些，但是还是太懒了，弄完这个就不想弄CNN了，就先出到这里了</p><p>题目名为<code>NEXT</code>，顾名思义就是让你对MNIST中的几个Samples加扰动，使得如下的8个Samples被模型识别成其原本lable的下一个数，如1→2，2→3...</p><p>为了让题目有个情景，所以把原本的weight给轮了一下顺序，原lable本应是[0,1,2,3,4,5,6,7,8,9]，被改成了[1,2,3,4,5,6,7,8,9,0]，于是乎题目就有了一个虽然不合理但可以忽悠人的情景</p><p><img src="/images/L2TarAtt_Original.png"></p><p>模型为带DropOut的FCNN，虽然带DropOut，但是整体梯度比较明显，对扰动的鲁棒性欠佳，很适合作为攻击对象</p><p>源码上对原本的函数做了一点混淆，比如<code>softMax</code>缩写成<code>SM</code>之类的，主要是为了选手深入了解神经网络之后再入手题目，不要底层没摸清楚做纯纯的TFboy</p><p>限制了L2和Linf的大小，是为了让做题人明确这是一个L2 Attack</p><p>task中不包含<code>torch</code>，<code>TF</code>之类的，但可以把weight手动导入一下，然后使用如<code>tf.gradientTape</code>之类的方法自动求梯度，我自己解是用比较土的手搓<code>BackProp</code></p><p><a href="https://kyriota.com/html/DLfiles/NEXT_ALL.zip">下载题目及exp</a></p><p>以下是题目代码</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        task.py    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment"># -*- coding:utf8 -</span></span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> secret <span class="keyword">import</span> flag</span><br><span class="line"></span><br><span class="line">L2CONSTRAIN = <span class="number">5.6789</span></span><br><span class="line">LINFCONSTRAIN = <span class="number">1.234</span></span><br><span class="line">CONFIDENCE = <span class="number">0.8</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">RL</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> np.maximum(x, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">SM</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> np.array(np.exp(x) / np.<span class="built_in">sum</span>(np.exp(x), axis=<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">AB</span>(<span class="params">x</span>):</span></span><br><span class="line">    x = np.matrix(x)</span><br><span class="line">    <span class="keyword">return</span> np.c_[np.ones((x.shape[<span class="number">0</span>], <span class="number">1</span>)), x]</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ANN</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, uNum</span>):</span></span><br><span class="line">        self.uNum = uNum</span><br><span class="line">        self.lNum = <span class="built_in">len</span>(uNum)</span><br><span class="line">        self.w = [<span class="number">0</span>]</span><br><span class="line">        self.Z = [<span class="number">0</span>]</span><br><span class="line">        self.A = [<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">FP</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, self.lNum - <span class="number">1</span>):</span><br><span class="line">            self.Z.append(AB(self.A[-<span class="number">1</span>]) * self.w[i])</span><br><span class="line">            self.A.append(RL(self.Z[-<span class="number">1</span>]))</span><br><span class="line">        self.Z.append(AB(self.A[-<span class="number">1</span>]) * self.w[-<span class="number">1</span>])</span><br><span class="line">        self.A.append(SM(self.Z[-<span class="number">1</span>]))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">PRED</span>(<span class="params">self, x</span>):</span></span><br><span class="line">        self.Z[<span class="number">0</span>] = np.matrix(x)</span><br><span class="line">        self.A[<span class="number">0</span>] = np.matrix(x)</span><br><span class="line">        self.FP()</span><br><span class="line">        <span class="keyword">return</span> np.array(self.A[-<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">LOAD</span>(<span class="params">self, fileName</span>):</span></span><br><span class="line">        f = <span class="built_in">open</span>(fileName, <span class="string">&quot;rb&quot;</span>)</span><br><span class="line">        wt = np.frombuffer(f.read(), np.float64)</span><br><span class="line">        f.close()</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="built_in">len</span>(self.uNum)):</span><br><span class="line">            data = wt[: (self.uNum[i - <span class="number">1</span>] + <span class="number">1</span>) * self.uNum[i]]</span><br><span class="line">            shape = (self.uNum[i - <span class="number">1</span>] + <span class="number">1</span>, self.uNum[i])</span><br><span class="line">            self.w.append(np.matrix(data).reshape(shape))</span><br><span class="line">            wt = wt[(self.uNum[i - <span class="number">1</span>] + <span class="number">1</span>) * self.uNum[i]:]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">CHECK</span>(<span class="params">mask: np.matrix, myNN: ANN, x</span>):</span></span><br><span class="line">    l2 = np.<span class="built_in">sum</span>(np.linalg.norm(np.array(mask), axis=<span class="number">1</span>)) / <span class="number">8</span></span><br><span class="line">    linf = np.<span class="built_in">max</span>(np.linalg.norm(np.array(mask), <span class="built_in">ord</span>=np.inf, axis=<span class="number">1</span>))</span><br><span class="line">    <span class="keyword">if</span> l2 &gt; L2CONSTRAIN:</span><br><span class="line">        print(<span class="string">&quot;Huge L2 &quot;</span> + <span class="built_in">str</span>(l2))</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    <span class="keyword">if</span> linf &gt; LINFCONSTRAIN:</span><br><span class="line">        print(<span class="string">&quot;Huge Linf&quot;</span> + <span class="built_in">str</span>(linf))</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    res = myNN.PRED(x + mask)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>):</span><br><span class="line">        lable = np.argmax(res[i])</span><br><span class="line">        <span class="keyword">if</span> lable != i + <span class="number">1</span>:</span><br><span class="line">            print(<span class="string">&quot;Wrong Lable &quot;</span> + <span class="built_in">str</span>(lable) + <span class="string">&quot;, &quot;</span> + <span class="built_in">str</span>(i + <span class="number">1</span>))</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">if</span> res[i][lable] &lt; CONFIDENCE:</span><br><span class="line">            print(<span class="string">&quot;Unconfident &quot;</span> + <span class="built_in">str</span>(lable) + <span class="string">&quot;, &quot;</span> + <span class="built_in">str</span>(i + <span class="number">1</span>) + <span class="string">&quot;, &quot;</span> + <span class="built_in">str</span>(res[i][lable]))</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">    print(flag)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    uNum = [<span class="number">784</span>, <span class="number">512</span>, <span class="number">256</span>, <span class="number">10</span>]</span><br><span class="line">    myNN = ANN(uNum)</span><br><span class="line">    myNN.LOAD(<span class="string">&quot;weight.dat&quot;</span>)</span><br><span class="line">    data = pd.read_csv(<span class="string">r&#x27;picData.csv&#x27;</span>)</span><br><span class="line">    x = np.matrix(data.iloc[:, <span class="number">1</span>:])</span><br><span class="line">    mask = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>):</span><br><span class="line">        print(<span class="string">&quot;&gt;&gt; Mask &quot;</span> + <span class="built_in">str</span>(i + <span class="number">1</span>) + <span class="string">&quot; :&quot;</span>)</span><br><span class="line">        maskTemp = []</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">784</span>):</span><br><span class="line">            maskTemp.append(<span class="built_in">float</span>(<span class="built_in">input</span>()))</span><br><span class="line">        mask.append(np.array(maskTemp, dtype=np.float64).ravel())</span><br><span class="line">    mask = np.matrix(mask)</span><br><span class="line">    CHECK(mask, myNN, x)</span><br></pre></td></tr></table></figure>    </div></div><h2 id="attack">Attack</h2><h3 id="loss">Loss</h3><p>参考论文，在<code>Loss</code>中包含了对L2的惩罚项，我implement的时候使用的是类似于参数正则化一样的方法，简单但有效，在每次迭代后：根据对L2的限制，让最终的噪声<code>Pert</code>自减一点点</p><p>而对于如何在<code>Loss</code>中体现出逼近Target，直接借鉴一下Carlini大神的结论，即参考文献中的<code>f6</code>，毕竟这些东西就是凭经验凭感觉弄出来的，他们基本也就是选了一些自己觉得有可能可行的<code>Loss</code>然后全部跑一遍，找一个效果最好的</p><p><img src="/images/L2TarAtt_f6.png" width=350></p><p>其中加号上标表示对括号内的参数<code>x</code>执行<code>max(x, 0)</code></p><p>大概意会一下，首先这玩意儿得从<code>logits</code>层开始回归，然后至于他这个<code>Loss</code>的思想也就字面意思：打压当前概率最高的，同时扶持target的概率</p><p>我没有完全按照他的<code>loss</code>来（主要是为了方便），但是思想都是一样的，我implement的就是一个对无关项置零的交叉熵，代码如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">d_crossEntPert</span>(<span class="params">self</span>):</span></span><br><span class="line">    mat = np.matrix(self.A[-<span class="number">1</span>] - self.y)</span><br><span class="line">    <span class="keyword">if</span> np.argmax(np.array(self.y).ravel()) != np.argmax(np.array(self.A[-<span class="number">1</span>]).ravel()):</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">            <span class="keyword">if</span> i != np.argmax(np.array(self.y).ravel()) <span class="keyword">and</span> i != np.argmax(np.array(self.A[-<span class="number">1</span>]).ravel()):</span><br><span class="line">                mat[<span class="number">0</span>, i] = <span class="number">0</span></span><br><span class="line"><span class="keyword">return</span> mat</span><br></pre></td></tr></table></figure><p>注1：此处的<code>Loss</code>还并非最终形态，因为没有加入对<code>Pert</code>的惩罚项</p><p>注2：关于为什么要在最终label是target的情况下直接return，则是因为这样在已经找到属于target的决策区域后的下降速度更快，还可以防止梯度消失</p><h3 id="get-gradient">Get Gradient</h3><p>由于是自己搓的FCNN，获取梯度直接<code>backProp</code>就行了，比如想要倒数第二层的dZ，<code>backProp</code>之后就直接<code>FCNN.dZ[-2]</code>，非常方便，这个手搓<code>BP</code>的梯度也拿去和<code>tensorflow</code>中的<code>gradientTape</code>求出的梯度做了对比，保证梯度正确</p><p>简化剔除了一些常规<code>BP</code>在此情境下不需要的内容，得到以下代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">backProp</span>(<span class="params">self, d_lossFunc</span>):</span></span><br><span class="line">    self.dZ[-<span class="number">1</span>] = d_lossFunc()</span><br><span class="line">    dw = self.A[-<span class="number">2</span>].T * self.dZ[-<span class="number">1</span>]</span><br><span class="line">    db = np.<span class="built_in">sum</span>(self.dZ[-<span class="number">1</span>], axis=<span class="number">0</span>)</span><br><span class="line">    self.dw[-<span class="number">1</span>] = np.r_[db, dw]</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">reversed</span>(<span class="built_in">range</span>(<span class="number">1</span>, self.layerNum - <span class="number">1</span>)):</span><br><span class="line">        self.dA[i] = RemoveBias(self.dZ[i + <span class="number">1</span>] * self.w[i + <span class="number">1</span>].T)</span><br><span class="line">        self.dZ[i] = np.multiply(d_relu(self.Z[i]), self.dA[i])</span><br><span class="line">        dw = self.A[i - <span class="number">1</span>].T * self.dZ[i]</span><br><span class="line">        db = np.<span class="built_in">sum</span>(self.dZ[i], axis=<span class="number">0</span>)</span><br><span class="line">        self.dw[i] = np.r_[db, dw]</span><br><span class="line">self.dA[<span class="number">0</span>] = RemoveBias(self.dZ[<span class="number">1</span>] * self.w[<span class="number">1</span>].T)</span><br><span class="line">self.dZ[<span class="number">0</span>] = self.dA[<span class="number">0</span>]</span><br></pre></td></tr></table></figure><h3 id="fool">Fool</h3><p>现在关键成分都已经齐全了，就可以开始生成AdversarialPerturbation了</p><p>基本的迭代过程大致如下：</p><ul><li>把target设置成<code>y</code>，因为<code>loss</code>函数中会用到（比如要把<code>6</code>糊弄成<code>7</code>，则<code>target=7</code>）</li><li>ForwardProp()</li><li>判断target的confidence是否符合要求，符合则return，不符合则继续</li><li>BackProp()</li><li>对梯度加入关于<code>Pert</code>的惩罚项</li></ul><p>在此基础上，还进行了几点优化：</p><ul><li>因为这个方法肉眼可见的容易出现惩罚项与逼近Target的方向相反的情况，所以加入了一个在Stuck时进行随机扰动的功能</li><li>对起点进行比较小的随机扰动，稍微差异化每次下降过程</li></ul><p>（在输出中加emoji主要是为了快速浏览运行结果，而且事实证明Jupyter对此支持是没问题的，看着很直观</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fool</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, myNetwork</span>):</span></span><br><span class="line">        self.Network = myNetwork</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">GetGrad</span>(<span class="params">self, X</span>):</span></span><br><span class="line">        pred = self.Network.predict(X)</span><br><span class="line">        self.Network.backProp(self.Network.d_crossEntPert)</span><br><span class="line">        <span class="keyword">return</span> self.Network.dA[<span class="number">0</span>], pred.ravel()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">Fool</span>(<span class="params">self, X, y, tar, foolRate=<span class="number">0.5</span>, maxIter=<span class="number">500</span>, step=<span class="number">0.05</span>, minGrad=<span class="number">0.05</span>, maxGrad=<span class="number">10</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">             constrain=<span class="number">10</span>, stuckRandL2=<span class="number">1</span>, initRandL2=<span class="number">1</span></span>):</span></span><br><span class="line">        rand = np.matrix(np.random.randn(<span class="number">1</span>, <span class="number">784</span>))</span><br><span class="line">        pert = np.zeros((<span class="number">1</span>, <span class="number">784</span>)) + initRandL2 * rand / np.linalg.norm(rand)</span><br><span class="line">        loss = []</span><br><span class="line">        L2Rec = []</span><br><span class="line">        cnt = <span class="number">0</span></span><br><span class="line">        stuckCnt = <span class="number">0</span></span><br><span class="line">        stuckJudgeCnt = <span class="number">0</span></span><br><span class="line">        success = <span class="literal">False</span></span><br><span class="line">        self.Network.predict(X + pert)</span><br><span class="line">        print(<span class="string">&quot;init: &quot;</span> + <span class="built_in">str</span>(y) + <span class="string">&quot; prob: &quot;</span> +</span><br><span class="line">              <span class="built_in">str</span>(self.Network.A[-<span class="number">1</span>].ravel()[y]))</span><br><span class="line">        self.Network.y = OneHot(</span><br><span class="line">            <span class="number">1</span>, self.Network.unitNum[-<span class="number">1</span>], y) <span class="keyword">if</span> tar == -<span class="number">1</span> <span class="keyword">else</span> OneHot(<span class="number">1</span>, self.Network.unitNum[-<span class="number">1</span>], tar)</span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">True</span>):</span><br><span class="line">            print(<span class="string">&quot;\rcnt: &quot;</span> + <span class="built_in">str</span>(cnt) + <span class="string">&quot;  stuck: &quot;</span> + <span class="built_in">str</span>(stuckCnt), end=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">            grad, pred = self.GetGrad(X + pert)</span><br><span class="line">            loss.append(np.linalg.norm(self.Network.A[-<span class="number">1</span>]) <span class="keyword">if</span> tar == -<span class="number">1</span> <span class="keyword">else</span></span><br><span class="line">                        -np.<span class="built_in">sum</span>(np.array(self.Network.y) * np.log(self.Network.A[-<span class="number">1</span>])</span><br><span class="line">                        + np.array(<span class="number">1</span> - self.Network.y) * np.log(<span class="number">1</span> - self.Network.A[-<span class="number">1</span>])))</span><br><span class="line">            <span class="keyword">if</span> ((self.Network.A[-<span class="number">1</span>].ravel()[y] &lt; foolRate <span class="keyword">if</span> tar == -<span class="number">1</span> <span class="keyword">else</span> self.Network.A[-<span class="number">1</span>].ravel()[tar] &gt; foolRate)</span><br><span class="line">                    <span class="keyword">and</span> stuckJudgeCnt &gt; <span class="number">5</span>):</span><br><span class="line">                print(<span class="string">&quot;\n⭕ OK:      &quot;</span> + <span class="built_in">str</span>(np.argmax(pred)) + <span class="string">&quot;    &quot;</span> +</span><br><span class="line">                      <span class="built_in">str</span>(self.Network.A[-<span class="number">1</span>].ravel()[np.argmax(pred)]) + <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">                success = <span class="literal">True</span></span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">if</span> np.linalg.norm(pert):</span><br><span class="line">                para = <span class="built_in">max</span>((np.linalg.norm(pert) - constrain) ** <span class="number">3</span>, <span class="number">0</span>)</span><br><span class="line">                grad -= np.<span class="built_in">sum</span>(np.array(pert) * np.array(grad)) * \</span><br><span class="line">                    (pert / np.linalg.norm(pert)) * para</span><br><span class="line">            L2 = np.linalg.norm(grad)</span><br><span class="line">            L2Rec.append(np.array(L2).ravel())</span><br><span class="line">            <span class="keyword">if</span> L2 &gt; maxGrad:</span><br><span class="line">                grad *= maxGrad / L2</span><br><span class="line">            <span class="keyword">if</span> L2 &lt; minGrad:</span><br><span class="line">                <span class="keyword">if</span> L2 == <span class="number">0</span>:</span><br><span class="line">                    print(<span class="string">&quot;\nERR: grad is zero&quot;</span>)</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                grad *= minGrad / L2</span><br><span class="line">            pert -= grad * step</span><br><span class="line">            cnt += <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> cnt == maxIter - <span class="number">1</span>:</span><br><span class="line">                print(<span class="string">&quot;\n❌ nope:   &quot;</span> + <span class="built_in">str</span>(y <span class="keyword">if</span> tar == -<span class="number">1</span> <span class="keyword">else</span> tar) + <span class="string">&quot;      &quot;</span> +</span><br><span class="line">                      <span class="built_in">str</span>(self.Network.A[-<span class="number">1</span>].ravel()[y <span class="keyword">if</span> tar == -<span class="number">1</span> <span class="keyword">else</span> tar]) + <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">if</span> cnt &gt; <span class="number">1</span> <span class="keyword">and</span> <span class="built_in">abs</span>(loss[-<span class="number">1</span>] - loss[-<span class="number">2</span>]) &lt; <span class="number">0.1</span>:</span><br><span class="line">                stuckJudgeCnt += <span class="number">1</span></span><br><span class="line">                <span class="keyword">if</span> stuckJudgeCnt &gt; <span class="number">10</span>:</span><br><span class="line">                    rand = np.matrix(np.random.randn(</span><br><span class="line">                        pert.shape[<span class="number">0</span>], pert.shape[<span class="number">1</span>]))</span><br><span class="line">                    pert += rand / np.linalg.norm(rand) * stuckRandL2</span><br><span class="line">                    stuckJudgeCnt = <span class="number">0</span></span><br><span class="line">                    stuckCnt += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> success, pert, loss, L2Rec</span><br></pre></td></tr></table></figure><h3 id="exp">Exp</h3><p>于是我们只需要基于以上的基础再对每个数字计算一遍即可</p><p>考虑到要使得L2尽可能小，我使用动态的Constrain，使得搜索结果的L2快速减小到一个可观的范围</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> KyNetExp</span><br><span class="line">X = np.matrix(pd.read_csv(<span class="string">r&#x27;picData.csv&#x27;</span>).iloc[:, <span class="number">1</span>:])</span><br><span class="line">unitNum = [<span class="number">784</span>, <span class="number">512</span>, <span class="number">256</span>, <span class="number">10</span>]</span><br><span class="line">FCNN = KyNetExp.Network()</span><br><span class="line">FCNN.Init(unitNum)</span><br><span class="line">FCNN.LoadParameters(<span class="string">&quot;weight.dat&quot;</span>)</span><br><span class="line">Fool = KyNetExp.Fool(FCNN)</span><br><span class="line">pert = []</span><br><span class="line">deltaConstrain = <span class="number">0.1</span></span><br><span class="line">maxUnsuccessCnt = <span class="number">10</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>):</span><br><span class="line">    print(<span class="string">&quot;\n-------------------- &quot;</span> + <span class="built_in">str</span>(i + <span class="number">1</span>) + <span class="string">&quot; --------------------\n&quot;</span>)</span><br><span class="line">    bestPert = np.matrix(np.ones((<span class="number">1</span>,<span class="number">784</span>)) * np.inf)</span><br><span class="line">    constrain = <span class="number">30</span></span><br><span class="line">    UnsuccessCnt = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span>(UnsuccessCnt &lt; maxUnsuccessCnt):</span><br><span class="line">        success = <span class="literal">False</span></span><br><span class="line">        success, pert_i, loss, L2Rec = Fool.Fool(X[i], i+<span class="number">1</span>, i+<span class="number">2</span>, maxIter=<span class="number">500</span>, foolRate=<span class="number">0.8</span>,</span><br><span class="line">                                                 constrain=constrain, stuckRandL2=<span class="number">1</span>, initRandL2=<span class="number">1</span>,</span><br><span class="line">                                                 minGrad=<span class="number">0.001</span>)</span><br><span class="line">        <span class="keyword">if</span> success:</span><br><span class="line">            <span class="keyword">if</span> np.linalg.norm(pert_i) &lt; np.linalg.norm(bestPert):</span><br><span class="line">                constrain = <span class="built_in">min</span>(np.linalg.norm(pert_i) - deltaConstrain, constrain)</span><br><span class="line">                bestPert = pert_i</span><br><span class="line">                UnsuccessCnt = <span class="number">0</span></span><br><span class="line">                print(<span class="string">&quot;----------------------------------&quot;</span>)</span><br><span class="line">                print(<span class="string">&quot;# New Constrain: &quot;</span> + <span class="built_in">str</span>(constrain))</span><br><span class="line">                print(<span class="string">&quot;----------------------------------\n&quot;</span>)</span><br><span class="line">            constrain -= deltaConstrain / <span class="number">2</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            UnsuccessCnt += <span class="number">1</span></span><br><span class="line">    pert.append(bestPert)</span><br></pre></td></tr></table></figure><p>最终生成的Pert数据如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># L2</span></span><br><span class="line">np.linalg.norm(np.array([np.array(i).ravel() <span class="keyword">for</span> i <span class="keyword">in</span> pert]), axis=<span class="number">1</span>)</span><br><span class="line">&gt;&gt; array([<span class="number">4.09481434</span>, <span class="number">4.2946097</span> , <span class="number">6.64396102</span>, <span class="number">5.90332629</span>, <span class="number">6.05583615</span>,</span><br><span class="line">       <span class="number">5.71162228</span>, <span class="number">6.26226203</span>, <span class="number">3.06447416</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 均值</span></span><br><span class="line">np.<span class="built_in">sum</span>(np.linalg.norm(np.array([np.array(i).ravel() <span class="keyword">for</span> i <span class="keyword">in</span> pert]), axis=<span class="number">1</span>)) / <span class="number">8</span></span><br><span class="line">&gt;&gt; <span class="number">5.2538632468131485</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Linf</span></span><br><span class="line">np.linalg.norm(np.array([np.array(i).ravel() <span class="keyword">for</span> i <span class="keyword">in</span> pert]), <span class="built_in">ord</span>=np.inf, axis=<span class="number">1</span>)</span><br><span class="line">&gt;&gt; array([<span class="number">0.51915417</span>, <span class="number">0.6969855</span> , <span class="number">0.94458608</span>, <span class="number">0.8020708</span> , <span class="number">0.99483219</span>,</span><br><span class="line">       <span class="number">0.70698208</span>, <span class="number">1.12368025</span>, <span class="number">0.41853863</span>])</span><br></pre></td></tr></table></figure><p>宏观感受如下</p><p><img src="/images/L2TarAtt_Attacked.png"></p><p>对比Carlini的论文中的L2数据，我认为这样的结果勉强可以接受，但鉴于视觉效果仍然不佳，之后可能会考虑在更大规模的model和dataset上做adversarial attack方面的种种实验</p><h2 id="dumbass">DumbAss</h2><p>一开始还想着靠单纯的梯度下降直接，硬着提高target的自信度，但是导致的问题就是各种梯度消失梯度过缓之类的，比如我当时的<code>loss</code>是这样下降的</p><p><img src="/images/L2TarAtt_fig.png"></p><p>具体是把<code>6</code>识别成<code>7</code>的情景，范数如下，吃力不讨好了属于是</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">L2  : <span class="number">8.161735079209715</span></span><br><span class="line">Linf: <span class="number">1.188402119264267</span></span><br></pre></td></tr></table></figure><p>在做这个L2 Targeted Attack的时候，最多的时间应该是浪费在以下这几件事上：</p><ul><li><code>float64</code>与<code>float32</code>在加载与储存的时候没有注意类型，导致不能及时与<code>tensorFlow</code>对答案</li><li><code>softMax</code>偏导的函数错误，导致最终偏导错误</li><li>文献查找，找到Carlini的这篇文章花了挺多时间的</li></ul><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;终于是把这玩意儿自己实现了一遍，恰逢miniL CTF，这文章虽然是22.04.09写的，但是估计博客得等到五月多才会更新，届时将也再补充一些在miniL CTF中本题的情况&lt;/p&gt;</summary>
    
    
    
    <category term="AI" scheme="https://kyriota.github.io/categories/AI/"/>
    
    
    <category term="CTF" scheme="https://kyriota.github.io/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>HandleCrack</title>
    <link href="https://kyriota.github.io/2022/03/23/HandleCrack/"/>
    <id>https://kyriota.github.io/2022/03/23/HandleCrack/</id>
    <published>2022-03-23T22:00:00.000Z</published>
    <updated>2023-01-03T11:04:09.493Z</updated>
    
    <content type="html"><![CDATA[<p>虎符签到题handle，连官方wp都叫你直接随便选一个看起来过得去的就行，确实是handle里对于开头的成语基本没啥讲究，但是反正做都做了不如做完做好，于是就又花了些时间跟着3b1b的wordle视频做了个这，主要是<b>信息论</b>的应用和对C++的顺便实操</p><span id="more"></span><h1 id="handlecrack信息论">HandleCrack[信息论]</h1><p>为了文章长度尽量短点（懒），以下是我略过但读者应该知道的内容：</p><ul><li><a href="https://handle.antfu.me/">handle</a>和wordle的规则</li><li>E = ∑ -p_i * log(p_i)</li></ul><p>（直接去看一遍<a href="www.bilibili.com/video/BV1zZ4y1k7Jw">3b1b视频</a>多好，以及另一个<a href="www.bilibili.com/video/BV1A3411p7Xv">3b1b的补充说明</a>）</p><h2 id="qa">Q&amp;A</h2><p>&gt;&gt; 问：原题不是py吗怎么要扯cpp？</p><p>答：不会优化py所以我的cpp跑起来应该比py快（?），而且cpp的位操作和手动内存管理我看着要放心一点（执念（不要学我（呜</p><p>&gt;&gt; 问：不是随便选一个当开头词就可以了吗，为什么还要费这么大力气整这些？</p><p>答：一，看了wordle的视频自己也想实操一下；二，反正比赛都结束了不如做完善点；三，不求出来我会睡不着觉的。总之就是虽然确实根据最后的结果看来，只要选一个比较正常的成语，或者多试几个开头词，就能达到很好的效果了，但是我就是想做一下，因为挺有趣的</p><h2 id="题面">题面</h2><p>Handle在4轮内找到答案，重复512次，每次的选词由<code>random.choice()</code>产生</p><p>然后就是他这个交互有点难受，由于你输入词语后它返回的拼音对错信息是以颜色标注的，虽然听群里说字节流下颜色可以比较方便的获取，但是反正后面我复现的时候是没有管这个的</p><p><a href="https://kyriota.com/html/DLfiles/%E8%99%8E%E7%AC%A6handle.zip">下载链接(github源)</a></p><h2 id="handle杂谈">Handle杂谈</h2><p>wordle是固定5个字母，每个字母框有三种情况，所以理论上总共的可能性是3^5种</p><p>handle是固定4个拼音，每个字的拼音分为声母，韵母，声调，所以可以看作是12个字母的wordle，也是每个拼音框有三种情况，理论可能性有3^12种</p><h3 id="特殊韵母">特殊韵母</h3><p>但是中文中包含特殊韵母，比如 en，er，ei 等，一个韵母就是一个注音的字。那比如这一轮猜了读音为 en 的一个字，则不管才没猜对，都损失了一个位置上声母的信息，特别是 en，ei 这些可以和声母搭配，又可以自己来的特殊韵母，对于 er 而言，由于 er 不能和声母组合，会更特殊一点（顺便一提，py的拼音库得出的拼音不知为何对“了”这个字也没有音调，所以有些时候不止会在声母上为空，声调也有可能为空）</p><p>但是我已经不想思考了，所以我就把 er 和 en，ei 之类的同等处理的，也许把 er 再单独分出来又会稍微不一样一捏捏，但是我真的已经停止思考了</p><h3 id="词库">词库</h3><p>其实我当时以为他题目没给代码，查了一下常用成语貌似也就几千个，我就去百度汉语扒了一个百度的成语词库，那也才4000个词啊，好家伙结果他给的那个列表有将近两万六千个词（？），看了一下是从<a href="https://github.com/antfu/handle">这里面</a>搬的，里面甚至有<b>“可口可乐”</b>之类的特别奇怪的东西，哈哈 (^ ^)</p><p><img src="/images/memes/TMDbloodpresure.jpg" height="100" width="100" /></p><h2 id="exp">exp</h2><p>这个exp里不包含对上面提到的特殊韵母的特殊处理，同时直接用的函数return的信息来反馈的，而<strong>不是题目环境下的颜色信息</strong></p><h3 id="吐槽">吐槽</h3><p>exp的思路很简单，的确只需要比较随便的选择开局的词语就可以了，甚至第一个词“一丁不识”在最后算出来的信息熵排名里面都很靠前，只要不是“可口可乐”这种奇怪的词，就可以在运气的加持下直接出flag，说实话这个flag是必须要运气的，因为就算是我最后算出来的信息熵最高的词他也不能保证4轮出结果，我得到的平均轮次大概是2.62轮（和题目条件一样随机抽取测试512次），而被我目击到的最大轮次是6轮，虽然我写的这个脚本优化比较烂，但是还是不影响我说：这个题他应该看你到最后的平均轮次是否合格，而不是看最大轮次 = =</p><p>在的<a href="https://mp.weixin.qq.com/s?__biz=MzU5Njg1NzMyNw==&amp;mid=2247484500&amp;idx=1&amp;sn=35e43e11b29119544045fa7dfd56c10a&amp;chksm=fe5d1cd4c92a95c208c93921a0b8f328cb92daf0630bc8a5ed7eaaafc30500fc44768beb0b6f&amp;mpshare=1&amp;scene=23&amp;srcid=0324Ct2tOV94yqxYEa60Rbey&amp;sharer_sharetime=1648094683578&amp;sharer_shareid=c69a4128c20e54a8749107dca4adf514#rd">官方wp</a>里对他自己的exp表示：</p><blockquote><p>这个脚本也不是必成功，但是体感成功率非常高，平均猜词次数 2.72，也许换个起始词可以把最大次数压到 3</p></blockquote><p>哥你得有数据啊不要凭感觉啊，出题人可能是觉得他随便选了个词就可以把平均猜词数压倒2.72，所以优化一下应该能轻松压得很低，但是问题就是其实这些词语的信息熵的分布总体来看都算是比较高的了，总共不到2.6w个词，2.1w个词的信息熵在10以上，最高的信息熵也就12.8755，意思是你随便选中那2.1w个信息熵在10以上的词的时候，理论上剩下的词数的期望就只有25.3个，但3b1b讲wordle的视频里有句话说得好，就，“期望是期望，实际是实际”（大概是这个意思），所以还是那句话，最好就存一下选手的轮次信息，最后算一下平均轮次够不够小</p><p>在我抽的512抽里面大概是这么个分布</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">猜中轮次 -| 一发入魂 |    2    |    3    |    4    |    5    |    6    |</span><br><span class="line">-----------------------------------------------------------------------</span><br><span class="line">发生次数 -|    1    |   230   |   247   |   32    |    1    |    1    |</span><br></pre></td></tr></table></figure><p><img src="/images/handle_bar.png"/></p><h3 id="source">Source</h3><p>挺无脑的，py水平有限，有些地方不太优雅</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        exp    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">initials, finals, tones = [], [], []</span><br><span class="line"><span class="comment"># 预先存了一下拼音</span></span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> idioms:</span><br><span class="line">    pinyin = get_pinyin(word)</span><br><span class="line">    initials.append(pinyin[<span class="number">0</span>])</span><br><span class="line">    finals.append(pinyin[<span class="number">1</span>])</span><br><span class="line">    tones.append(pinyin[<span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">GetWord</span>(<span class="params">words</span>):</span></span><br><span class="line">    <span class="comment"># 根据 E 得出当前最优词</span></span><br><span class="line">    wordLen = <span class="built_in">len</span>(words)</span><br><span class="line">    <span class="keyword">if</span> wordLen &gt; <span class="number">25000</span>: <span class="comment"># 开头词固定</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;研经铸史&quot;</span></span><br><span class="line">    E = []</span><br><span class="line">    <span class="keyword">for</span> guessIndex <span class="keyword">in</span> <span class="built_in">range</span>(wordLen): <span class="comment"># 无脑遍历算 E</span></span><br><span class="line">        partern, count, prob = [], [], []</span><br><span class="line">        <span class="keyword">for</span> ansIndex <span class="keyword">in</span> <span class="built_in">range</span>(wordLen):</span><br><span class="line">            result = []</span><br><span class="line">            result.append(check_part(initials[guessIndex],initials[ansIndex]))</span><br><span class="line">            result.append(check_part(finals[guessIndex],finals[ansIndex]))</span><br><span class="line">            result.append(check_part(tones[guessIndex],tones[ansIndex]))</span><br><span class="line">            findFlag = <span class="literal">False</span></span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(partern)):</span><br><span class="line">                <span class="keyword">if</span> partern[i] == result:</span><br><span class="line">                    count[i] += <span class="number">1</span></span><br><span class="line">                    findFlag = <span class="literal">True</span></span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> findFlag:</span><br><span class="line">                partern.append(result)</span><br><span class="line">                count.append(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(partern)):</span><br><span class="line">            p = count[i] / wordLen</span><br><span class="line">            prob.append(-p * log2(p))</span><br><span class="line">        E.append(<span class="built_in">sum</span>(prob))</span><br><span class="line">        print(<span class="string">&quot;\r&gt;&gt; E: &quot;</span> + <span class="built_in">str</span>(guessIndex), end=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    print(<span class="string">&quot;\n&gt;&gt; max E: &quot;</span> + <span class="built_in">str</span>(<span class="built_in">max</span>(E)))</span><br><span class="line">    <span class="keyword">return</span> words[argmax(E)]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">DeleteWord</span>(<span class="params">words, result, guess</span>):</span></span><br><span class="line">    <span class="comment"># 把不符合反馈 partern 的词删掉</span></span><br><span class="line">    certain = []</span><br><span class="line">    uncertain = []</span><br><span class="line">    wrong = []</span><br><span class="line">    pinyin = get_pinyin(guess)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">            <span class="keyword">if</span> result[i][j] == status.OK:</span><br><span class="line">                certain.append([pinyin[i][j],(i,j)])</span><br><span class="line">            <span class="keyword">elif</span> result[i][j] == status.MISS:</span><br><span class="line">                uncertain.append([pinyin[i][j],(i,j)])</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                wrong.append([pinyin[i][j],(i,j)])</span><br><span class="line">    newWords = []</span><br><span class="line">    <span class="keyword">for</span> wordIndex <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(words)):</span><br><span class="line">        pinyin = get_pinyin(words[wordIndex])</span><br><span class="line">        flag = <span class="literal">True</span> <span class="keyword">and</span> words[wordIndex] != guess</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> certain:</span><br><span class="line">            <span class="keyword">if</span> pinyin[i[<span class="number">1</span>][<span class="number">0</span>]][i[<span class="number">1</span>][<span class="number">1</span>]] != i[<span class="number">0</span>]:</span><br><span class="line">                flag = <span class="literal">False</span></span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> flag:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> uncertain:</span><br><span class="line">            <span class="keyword">if</span> i[<span class="number">0</span>] <span class="keyword">not</span> <span class="keyword">in</span> pinyin[i[<span class="number">1</span>][<span class="number">0</span>]] <span class="keyword">or</span> i[<span class="number">0</span>] == pinyin[i[<span class="number">1</span>][<span class="number">0</span>]][i[<span class="number">1</span>][<span class="number">1</span>]]:</span><br><span class="line">                flag = <span class="literal">False</span></span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> flag:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> wrong:</span><br><span class="line">            <span class="keyword">if</span> i[<span class="number">0</span>] <span class="keyword">in</span> pinyin[i[<span class="number">1</span>][<span class="number">0</span>]]:</span><br><span class="line">                wrongCnt = pinyin[i[<span class="number">1</span>][<span class="number">0</span>]].count(i[<span class="number">0</span>])</span><br><span class="line">                certainCnt, uncertainCnt = <span class="number">0</span>, <span class="number">0</span></span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> certain:</span><br><span class="line">                    certainCnt += j.count(i[<span class="number">0</span>])</span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> uncertain:</span><br><span class="line">                    uncertainCnt += j.count(i[<span class="number">0</span>])</span><br><span class="line">                <span class="keyword">if</span> wrongCnt &lt;= certainCnt + uncertainCnt:</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                flag = <span class="literal">False</span></span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> flag:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        newWords.append(words[wordIndex])</span><br><span class="line">    <span class="keyword">return</span> newWords</span><br></pre></td></tr></table></figure>    </div></div><h2 id="最优开头">最优开头</h2><p>我其实之前想过能不能直接构造一个最优开头，但是没什么思路，就决定从他给出的2.6w个词里面找一个最优开头</p><p>由于文章一开头说的优化问题，所以我就<code>py2cpp()</code>了</p><h3 id="准备工作">准备工作</h3><p>首先我把所有词的拼音都通过py的拼音库导出到了硬盘，无韵母无声调的地方用“#”进行了占位，然后为了之后不再重复统计一个词另一个词比较得出的pattern，我把所有partern导出，这样之后查表就完事了</p><p>这一块值得注意的应该也就是pattern的<strong>存储格式</strong>了</p><p>因为每一个拼音位有4种情况：确定存在且位置正确，确定存在但位置错误，确定不存在，不确定是否存在（特殊韵母）；而总共4个汉字，每个汉字3个拼音位，由于状态共4种，两位就能表示完（00，01，10，11），总共需要<code>4 * 3 * 2 = 24(bit)</code>，刚好3字节，所以用3个<code>char</code>就可以塞下1个pattern，这部分详见下方折叠代码中的<code>CalPattern()</code></p><div class='spoiler collapsed'>    <div class='spoiler-title'>        crackGen.cpp    </div>    <div class='spoiler-content'>        <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;fstream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAXSIZE 26352</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ZHword</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="built_in">string</span> pinyin[<span class="number">12</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">InitZHWords</span><span class="params">(<span class="built_in">list</span>&lt;ZHword&gt; &amp;words)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 读取 py 生成的拼音</span></span><br><span class="line">    ifstream f_ini(&quot;initials.txt&quot;), f_fin(&quot;finals.txt&quot;), f_ton(&quot;tones.txt&quot;);</span><br><span class="line">    <span class="built_in">string</span> str_ini, str_fin, str_ton; <span class="comment">// 声母 韵母 音调</span></span><br><span class="line">    <span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">    ZHword wordTemp;</span><br><span class="line">    <span class="keyword">while</span> (f_ini &gt;&gt; str_ini &amp;&amp; f_fin &gt;&gt; str_fin &amp;&amp; f_ton &gt;&gt; str_ton)</span><br><span class="line">    &#123;</span><br><span class="line">        wordTemp.pinyin[cnt % <span class="number">4</span> * <span class="number">3</span>] = str_ini;</span><br><span class="line">        wordTemp.pinyin[cnt % <span class="number">4</span> * <span class="number">3</span> + <span class="number">1</span>] = str_fin;</span><br><span class="line">        wordTemp.pinyin[cnt % <span class="number">4</span> * <span class="number">3</span> + <span class="number">2</span>] = str_ton;</span><br><span class="line">        <span class="keyword">if</span> (!(++cnt % <span class="number">4</span>))</span><br><span class="line">            words.push_back(wordTemp);</span><br><span class="line">    &#125;</span><br><span class="line">    f_ini.close();</span><br><span class="line">    f_fin.close();</span><br><span class="line">    f_ton.close();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">CalPattern</span><span class="params">(ZHword guess, ZHword answer, <span class="keyword">char</span> *pattern)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 计算 guess 与 answer 比对的 pattern, 并存入参数中的 pattern</span></span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="built_in">string</span>&gt; miss;</span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="built_in">string</span>&gt;::iterator iterMiss;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">12</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (guess.pinyin[i] == <span class="string">&quot;#&quot;</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            pattern[i % <span class="number">3</span>] |= <span class="number">3</span> &lt;&lt; (<span class="number">3</span> - i / <span class="number">3</span>) * <span class="number">2</span>; <span class="comment">// unknown =&gt; 11</span></span><br><span class="line">            miss.push_back(answer.pinyin[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (guess.pinyin[i] != answer.pinyin[i])</span><br><span class="line">            miss.push_back(answer.pinyin[i]);</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            pattern[i % <span class="number">3</span>] |= <span class="number">2</span> &lt;&lt; (<span class="number">3</span> - i / <span class="number">3</span>) * <span class="number">2</span>; <span class="comment">// certain =&gt; 10</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (miss.empty())</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">12</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> marked = <span class="keyword">int</span>(pattern[i % <span class="number">3</span>] &gt;&gt; ((<span class="number">3</span> - i / <span class="number">3</span>) * <span class="number">2</span>) &amp; <span class="keyword">char</span>(<span class="number">0x3</span>));</span><br><span class="line">        <span class="keyword">if</span> (marked)</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        <span class="keyword">for</span> (iterMiss = miss.begin(); iterMiss != miss.end(); iterMiss++)</span><br><span class="line">            <span class="keyword">if</span> (guess.pinyin[i] == *iterMiss)</span><br><span class="line">            &#123;</span><br><span class="line">                pattern[i % <span class="number">3</span>] |= <span class="number">1</span> &lt;&lt; (<span class="number">3</span> - i / <span class="number">3</span>) * <span class="number">2</span>; <span class="comment">// uncertain =&gt; 01</span></span><br><span class="line">                miss.erase(iterMiss);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="comment">// wrong =&gt; 00</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> *patternsHead[MAXSIZE];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; MAXSIZE; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">char</span> *patterns = <span class="keyword">new</span> <span class="keyword">char</span>[<span class="number">3</span> * MAXSIZE]();</span><br><span class="line">        patternsHead[i] = patterns;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">list</span>&lt;ZHword&gt; zh_words;</span><br><span class="line">    <span class="built_in">list</span>&lt;ZHword&gt;::iterator guess, answer;</span><br><span class="line">    InitZHWords(zh_words);</span><br><span class="line">    <span class="keyword">int</span> cntGuess = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (guess = zh_words.begin(); guess != zh_words.end() &amp;&amp; cntGuess &lt; MAXSIZE; guess++, <span class="built_in">cout</span> &lt;&lt; cntGuess++ &lt;&lt; <span class="built_in">endl</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> cntAns = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (answer = zh_words.begin(); answer != zh_words.end(); answer++, cntAns++)</span><br><span class="line">            CalPattern(*guess, *answer, &amp;patternsHead[cntGuess][cntAns * <span class="number">3</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function">ofstream <span class="title">f</span><span class="params">(<span class="string">&quot;patterns.dat&quot;</span>, ios::out | ios::binary)</span></span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> guessIndex = <span class="number">0</span>; guessIndex &lt; MAXSIZE; guessIndex++, <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;SAVE: &quot;</span> &lt;&lt; guessIndex &lt;&lt; <span class="built_in">endl</span>)</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> ansIndex = <span class="number">0</span>; ansIndex &lt; MAXSIZE; ansIndex++)</span><br><span class="line">            f.write(&amp;patternsHead[guessIndex][ansIndex * <span class="number">3</span>], <span class="number">3</span>);</span><br><span class="line">    f.close();</span><br><span class="line">    zh_words.clear();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><h3 id="main">main</h3><p>做好了patterns的准备工作，就可以开始求E了</p><p>对于<code>GetEntropy()</code>总共就三步：</p><ul><li>根据最大E猜词</li><li>求新的E</li><li>更新词库并回到第一步</li></ul><p>存储结构上要提一下，如果直接创建超大数组多半会segFault，所以我用的拉链法</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> patterns[MAXSIZE][MAXSIZE * <span class="number">3</span>]; <span class="comment">// =&gt; Segment Fault</span></span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> *patternsHead[MAXSIZE];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; MAXSIZE; i++)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">char</span> *patterns = <span class="keyword">new</span> <span class="keyword">char</span>[<span class="number">3</span> * MAXSIZE];</span><br><span class="line">patternsHead[i] = patterns;</span><br><span class="line">&#125; <span class="comment">// =&gt; 彳亍</span></span><br></pre></td></tr></table></figure><p>以及递归深度方面，由于深度只要不为0就算的真的太慢了，所以我也和3b1b一样，计算深度为1的数据时只拿在深度为0时排名靠前的数据去计算，以减少对获取最优开头词的无效计算</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        crack.cpp    </div>    <div class='spoiler-content'>        <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;fstream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstring&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAXSIZE 26352</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TOP 100</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> *patternsHead[MAXSIZE];</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">PatternRecord</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">char</span> pattern[<span class="number">3</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">EntropyRecord</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">float</span> E = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> index;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">cmp</span><span class="params">(EntropyRecord A, EntropyRecord B)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> A.E &gt; B.E;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">InitWords</span><span class="params">(<span class="built_in">string</span> *words)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">ifstream <span class="title">f_word</span><span class="params">(<span class="string">&quot;idioms.txt&quot;</span>)</span></span>;</span><br><span class="line">    <span class="built_in">string</span> str_word;</span><br><span class="line">    <span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (f_word &gt;&gt; str_word)</span><br><span class="line">        words[cnt++] = str_word;</span><br><span class="line">    f_word.close();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">SamePattern</span><span class="params">(<span class="keyword">char</span> *guessPattern, <span class="keyword">char</span> *answerPattern)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (guessPattern[<span class="number">0</span>] == answerPattern[<span class="number">0</span>] &amp;&amp;</span><br><span class="line">        guessPattern[<span class="number">1</span>] == answerPattern[<span class="number">1</span>] &amp;&amp;</span><br><span class="line">        guessPattern[<span class="number">2</span>] == answerPattern[<span class="number">2</span>])</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">GetRecords</span><span class="params">(<span class="keyword">char</span> *patterns, <span class="built_in">list</span>&lt;PatternRecord&gt; &amp;records, <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; enabled)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 将词按照其 pattern 分类</span></span><br><span class="line">    <span class="built_in">list</span>&lt;PatternRecord&gt;::iterator iterGuessRec;</span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt;::iterator iterEn = enabled.begin();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; MAXSIZE; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (!enabled.empty() &amp;&amp; i != *iterEn)</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        PatternRecord ansRecord;</span><br><span class="line">        <span class="built_in">memcpy</span>(ansRecord.pattern, &amp;patterns[i * <span class="number">3</span>], <span class="number">3</span>);</span><br><span class="line">        <span class="keyword">bool</span> match = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">for</span> (iterGuessRec = records.begin(); iterGuessRec != records.end(); iterGuessRec++)</span><br><span class="line">            <span class="keyword">if</span> (SamePattern((*iterGuessRec).pattern, ansRecord.pattern))</span><br><span class="line">            &#123;</span><br><span class="line">                (*iterGuessRec).count++;</span><br><span class="line">                match = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">if</span> (!match)</span><br><span class="line">            records.push_back(ansRecord);</span><br><span class="line">        iterEn++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; <span class="title">UpdateEnabled</span><span class="params">(<span class="keyword">char</span> *patterns, <span class="keyword">char</span> *pattern, <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; lastEnabled)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 更新词库</span></span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; enabled;</span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt;::iterator lastPtr;</span><br><span class="line">    <span class="keyword">for</span> (lastPtr = lastEnabled.begin(); lastPtr != lastEnabled.end(); lastPtr++)</span><br><span class="line">        <span class="keyword">if</span> (SamePattern(&amp;patterns[(*lastPtr) * <span class="number">3</span>], pattern))</span><br><span class="line">            enabled.push_back(*lastPtr);</span><br><span class="line">    <span class="keyword">return</span> enabled;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function">EntropyRecord <span class="title">GetEntropy</span><span class="params">(<span class="keyword">int</span> depth, <span class="keyword">int</span> guessIndex, <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; &amp;enabled)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">list</span>&lt;PatternRecord&gt; patternRecords; <span class="comment">// 记录 pattern 数量以及样式</span></span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="keyword">float</span>&gt; patternERecs;           <span class="comment">// 记录上述每种 pattern 对应的 E</span></span><br><span class="line">    GetRecords(patternsHead[guessIndex], patternRecords, enabled);</span><br><span class="line">    <span class="keyword">if</span> (depth)</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">list</span>&lt;PatternRecord&gt;::iterator iterPRec = patternRecords.begin();</span><br><span class="line">             iterPRec != patternRecords.end();</span><br><span class="line">             iterPRec++) <span class="comment">// 遍历表层patterns</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 根据表层pattern获取newEnabled</span></span><br><span class="line">            <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; newEnabled;</span><br><span class="line">            <span class="keyword">if</span> (enabled.empty())</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; MAXSIZE; i++)</span><br><span class="line">                    <span class="keyword">if</span> (SamePattern(&amp;patternsHead[guessIndex][i * <span class="number">3</span>], (*iterPRec).pattern))</span><br><span class="line">                        newEnabled.push_back(i);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt;::iterator iterEn = enabled.begin();</span><br><span class="line">                     iterEn != enabled.end();</span><br><span class="line">                     iterEn++)</span><br><span class="line">                    <span class="keyword">if</span> (SamePattern(&amp;patternsHead[guessIndex][(*iterEn) * <span class="number">3</span>], (*iterPRec).pattern))</span><br><span class="line">                        newEnabled.push_back(*iterEn);</span><br><span class="line"></span><br><span class="line">            <span class="built_in">list</span>&lt;<span class="keyword">float</span>&gt; localERecords; <span class="comment">// 记录里层 E</span></span><br><span class="line">            <span class="keyword">if</span> (newEnabled.size() &lt; <span class="number">2</span>)</span><br><span class="line">                localERecords.push_back(<span class="number">1</span>);</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt;::iterator newGuessIndex = newEnabled.begin();</span><br><span class="line">                     newGuessIndex != newEnabled.end();</span><br><span class="line">                     newGuessIndex++) <span class="comment">// 遍历里层词库</span></span><br><span class="line">                    <span class="comment">// 递归入口</span></span><br><span class="line">                    localERecords.push_back(GetEntropy(depth - <span class="number">1</span>, *newGuessIndex, newEnabled).E);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">float</span> localMax = <span class="number">0</span>; <span class="comment">// 里层 E 最大值</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">list</span>&lt;<span class="keyword">float</span>&gt;::iterator iterERec = localERecords.begin();</span><br><span class="line">                 iterERec != localERecords.end();</span><br><span class="line">                 iterERec++)</span><br><span class="line">                <span class="keyword">if</span> (localMax &lt; *iterERec)</span><br><span class="line">                    localMax = *iterERec;</span><br><span class="line">            patternERecs.push_back(localMax);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    EntropyRecord entropyRecord;</span><br><span class="line">    entropyRecord.index = guessIndex;</span><br><span class="line">    <span class="built_in">list</span>&lt;PatternRecord&gt;::iterator iterP = patternRecords.begin();</span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="keyword">float</span>&gt;::iterator iterE = patternERecs.begin();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (; iterP != patternRecords.end(); iterE++, iterP++) <span class="comment">// E(p)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">float</span> prob = <span class="keyword">float</span>((*iterP).count) / <span class="keyword">float</span>(enabled.empty() ? MAXSIZE : enabled.size());</span><br><span class="line">        entropyRecord.E += -prob * log2(prob);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (depth)</span><br><span class="line">    &#123;</span><br><span class="line">        iterP = patternRecords.begin();</span><br><span class="line">        iterE = patternERecs.begin();</span><br><span class="line">        <span class="keyword">for</span> (; iterE != patternERecs.end(); iterE++, iterP++) <span class="comment">// E(E&#x27;,p) 其中 E&#x27; 为上一层的 E</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">float</span> prob = <span class="keyword">float</span>((*iterP).count) / <span class="keyword">float</span>(enabled.empty() ? MAXSIZE : enabled.size());</span><br><span class="line">            entropyRecord.E += prob * (*iterE);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> entropyRecord;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//  &#123; 表层词库 &#125;  -&gt;  &#123; 表层patterns &#125;  -&gt;  &#123; 里层词库 &#125;  -&gt;  &#123; 里层patterns &#125;</span></span><br><span class="line"><span class="comment">//                       E1 = ∑p*E0                            E0 = ∑-p*log2(p)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//========================================================================</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// Init</span></span><br><span class="line">    <span class="built_in">string</span> words[MAXSIZE];</span><br><span class="line">    InitWords(words);</span><br><span class="line">    <span class="function">ifstream <span class="title">f_in</span><span class="params">(<span class="string">&quot;patterns.dat&quot;</span>, ios::in | ios::binary)</span></span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; MAXSIZE; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">char</span> *patterns = <span class="keyword">new</span> <span class="keyword">char</span>[<span class="number">3</span> * MAXSIZE];</span><br><span class="line">        patternsHead[i] = patterns;</span><br><span class="line">        f_in.read(patterns, MAXSIZE * <span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    f_in.close();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// E calculation with depth=0 for all data</span></span><br><span class="line">    <span class="built_in">list</span>&lt;<span class="keyword">int</span>&gt; enabled; <span class="comment">// 形式 enabled，除了参数占位无实际作用</span></span><br><span class="line">    <span class="built_in">list</span>&lt;EntropyRecord&gt; entropyRecords, topERecs;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> guessIndex = <span class="number">0</span>; guessIndex &lt; MAXSIZE; guessIndex++)</span><br><span class="line">    &#123;</span><br><span class="line">        EntropyRecord myEntRec = GetEntropy(<span class="number">0</span>, guessIndex, enabled);</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&gt;&gt; D0-No.&quot;</span> &lt;&lt; guessIndex &lt;&lt; <span class="string">&quot; &quot;</span> &lt;&lt; words[guessIndex]</span><br><span class="line">             &lt;&lt; <span class="string">&quot; complete with E of &quot;</span> &lt;&lt; myEntRec.E &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        entropyRecords.push_back(myEntRec);</span><br><span class="line">    &#125;</span><br><span class="line">    entropyRecords.sort(cmp);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Save TOP ranks at depth 0 with E value</span></span><br><span class="line">    <span class="built_in">list</span>&lt;EntropyRecord&gt;::iterator iterERec = entropyRecords.begin();</span><br><span class="line">    ofstream f_word(&quot;d0rankedWord.txt&quot;), f_E(&quot;d0rankedE.txt&quot;);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> cnt = <span class="number">0</span>; iterERec != entropyRecords.end(); iterERec++, cnt++)</span><br><span class="line">    &#123;</span><br><span class="line">        f_word &lt;&lt; words[(*iterERec).index] &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        f_E &lt;&lt; (*iterERec).E &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    f_word.close();</span><br><span class="line">    f_E.close();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// E calculation with depth=1 for TOP</span></span><br><span class="line">    iterERec = entropyRecords.begin();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> cnt = <span class="number">0</span>; cnt &lt; TOP; iterERec++, cnt++)</span><br><span class="line">    &#123;</span><br><span class="line">        EntropyRecord myEntRec = GetEntropy(<span class="number">1</span>, (*iterERec).index, enabled);</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&gt;&gt; D1-No.&quot;</span> &lt;&lt; cnt &lt;&lt; <span class="string">&quot; &quot;</span> &lt;&lt; words[(*iterERec).index]</span><br><span class="line">             &lt;&lt; <span class="string">&quot; complete with E of &quot;</span> &lt;&lt; myEntRec.E &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        topERecs.push_back(myEntRec);</span><br><span class="line">    &#125;</span><br><span class="line">    topERecs.sort(cmp);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Save TOP ranks at depth 1 with E value</span></span><br><span class="line">    f_word.open(<span class="string">&quot;d1rankedWord.txt&quot;</span>);</span><br><span class="line">    f_E.open(<span class="string">&quot;d1rankedE.txt&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (iterERec = topERecs.begin(); iterERec != topERecs.end(); iterERec++)</span><br><span class="line">    &#123;</span><br><span class="line">        f_word &lt;&lt; words[(*iterERec).index] &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        f_E &lt;&lt; (*iterERec).E &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    f_word.close();</span><br><span class="line">    f_E.close();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><h3 id="其他">其他</h3><p>其实我是准备吧handle完整搬到c++上来的，结果代码弄完了发现我不知道怎么让c++支持中文输入（？</p><p>但是不准备继续日了，放个之前的代码</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        crackGen.cpp    </div>    <div class='spoiler-content'>        <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">GetRand</span><span class="params">(<span class="keyword">int</span> seed=<span class="number">0</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    srand(seed ? seed : (<span class="keyword">int</span>)time(<span class="literal">NULL</span>));</span><br><span class="line">    <span class="keyword">return</span> rand() % MAXSIZE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ShowMatch</span><span class="params">(<span class="keyword">char</span> *partern, ZHword guess)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++, <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot; &quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; <span class="number">3</span>; j++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">int</span> index = j + <span class="number">3</span> * i;</span><br><span class="line">            <span class="keyword">if</span> ((<span class="keyword">int</span>)partern[index] == <span class="number">2</span>)</span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;[&quot;</span> &lt;&lt; guess.pinyin[index] &lt;&lt; <span class="string">&quot;]&quot;</span>;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((<span class="keyword">int</span>)partern[index] == <span class="number">1</span>)</span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&lt;&quot;</span> &lt;&lt; guess.pinyin[index] &lt;&lt; <span class="string">&quot;&gt;&quot;</span>;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; guess.pinyin[index];</span><br><span class="line">        &#125;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">StartRound</span><span class="params">(<span class="keyword">int</span> roundLimit, <span class="built_in">list</span>&lt;ZHword&gt; words)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">list</span>&lt;ZHword&gt;::iterator answer = words.begin();</span><br><span class="line">    advance(answer, GetRand() - <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> round = <span class="number">0</span>; round &lt; roundLimit; round++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">list</span>&lt;ZHword&gt;::iterator guess;</span><br><span class="line">        <span class="built_in">string</span> guessIn;</span><br><span class="line">        <span class="keyword">char</span> results[<span class="number">12</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&gt;&gt; Round: &quot;</span> &lt;&lt; round &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ====================================================== //</span></span><br><span class="line">        <span class="built_in">cin</span> &gt;&gt; guessIn; <span class="comment">// TNND C++不能输入中文 QAQ</span></span><br><span class="line"><span class="comment">// ====================================================== //</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (guess = words.begin(); guess != words.end(); guess++)</span><br><span class="line">            <span class="keyword">if</span> ((*guess).word == guessIn)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">if</span> ((*guess).word != guessIn)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&gt;&gt; Unkown word&quot;</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (CheckPart(*guess, *answer, results))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&gt;&gt; Correct&quot;</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ShowMatch(results, *guess);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;&gt;&gt; Failed&quot;</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><p>还有就是这令人发指的运行速度，不会多核多线程优化所以没办法</p><blockquote><p>学了OS后的补充：现在会了</p></blockquote><p>但是可以加入计时函数量化我的痛苦 :)</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;time.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">CheckTime</span><span class="params">(<span class="keyword">clock_t</span> &amp;start, <span class="keyword">clock_t</span> end)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">double</span> delta = <span class="keyword">double</span>(end - start) / CLOCKS_PER_SEC;</span><br><span class="line">    start = end;</span><br><span class="line">    <span class="keyword">return</span> delta;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">clock_t</span> clockStart;</span><br><span class="line">    clockStart = clock();</span><br><span class="line">    <span class="comment">// ... sth.</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;time length: &quot;</span> &lt;&lt; CheckTime(clockStart, clock()) &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>windows下g++编译，<code>depth=0</code>跑完<code>16</code>个大约是<code>12s</code>，<code>depth=1</code>跑完<code>4</code>个大约是<code>32s</code>，我大概是花了4~5h跑完所有数据</p><p><del>以及千万不要让测速度的脚本能覆盖结果，像我直接把之前算完的给覆盖了，全部重来</del></p><h3 id="运行结果">运行结果</h3><p>经过排序发现</p><center><b>研经铸史</b></center><p><br></p><p>是handle的最优开头，且在depth=1的情况下E=14.8701，以ΔE=0.03的巨大差值（笑）和第二名拉开距离</p><h2 id="结果统计">结果统计</h2><p>基本全部正常的词在d0的E都在10以上，少数如“可口可乐”，d0E=6.55192，这样的词拉低了整体下限，整体d0E分布如下</p><p><img src="/images/handle_d0.png"/></p><p>对于前d0E排名top100的词进行d1E的计算和排序，分布如下</p><p><img src="/images/handle_d1.png"/></p><p>日不动了，再见 UwU</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;虎符签到题handle，连官方wp都叫你直接随便选一个看起来过得去的就行，确实是handle里对于开头的成语基本没啥讲究，但是反正做都做了不如做完做好，于是就又花了些时间跟着3b1b的wordle视频做了个这，主要是&lt;b&gt;信息论&lt;/b&gt;的应用和对C++的顺便实操&lt;/p&gt;</summary>
    
    
    
    <category term="Misc" scheme="https://kyriota.github.io/categories/Misc/"/>
    
    
    <category term="CTF" scheme="https://kyriota.github.io/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>全连接神经网络[神经网络入门]</title>
    <link href="https://kyriota.github.io/2022/01/16/%E5%85%A8%E8%BF%9E%E6%8E%A5%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C[%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%85%A5%E9%97%A8]/"/>
    <id>https://kyriota.github.io/2022/01/16/%E5%85%A8%E8%BF%9E%E6%8E%A5%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C[%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%85%A5%E9%97%A8]/</id>
    <published>2022-01-16T17:40:00.000Z</published>
    <updated>2023-11-24T01:14:23.371Z</updated>
    
    <content type="html"><![CDATA[<p>全连接神经网络识别MNIST手写数字集，AI中的HelloWorld</p><p><img src='/images/FCNN_cover.png' style="zoom:60%;" ></p><span id="more"></span><!--toc--><h1 id="全连接神经网络神经网络入门">全连接神经网络[神经网络入门]</h1><h2 id="前言">前言</h2><p>现在的AI库诸如<code>TensorFlow</code>,<code>Keras</code>,<code>Pytorch</code>等，都可以快捷方便地在几行代码之内就构建好一个网络模型，然后开始训练等后续的事情，但把这些库当成黑盒来用的话，反正我是晚上睡不着觉的</p><p>虽然说全连接神经网络（Fully Connected Neural Network，以下简称<code>FCNN</code>）是人造神经网络（Artificial Neural Network，以下简称<code>ANN</code>）的入门，但在这之前也可以先做一些线性回归，逻辑回归，以及逻辑回归的多分类来加深理解和降低入门的难度，这些也是我之前做过的东西，但因为写的比较烂而且本身也不难，就不放出来了</p><p>因为在这篇文章之前只有<a href="https://kyriota.com/2021/11/25/%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91GlobalNoise%5BAI%E5%AF%B9%E6%8A%97%E5%90%AF%E8%92%99%5D/">这篇讲对抗扰动的文章</a>是有关机器学习的，所以我也尽量在这篇文章中细🔒一些概念和理解，最好能让吃瓜的也能吃的舒服</p><h2 id="ann概述">ANN概述</h2><p>对于神经网络的认识和理解，强烈推荐<a href="https://www.bilibili.com/video/BV1bx411M7Zx">3b1b的系列视频</a>（共四集），从概念到公式一条龙服务，讲的肯定比我清楚和直观</p><p>对于系统学习机器学习的相关知识（包括线性回归，逻辑回归等），可以看<a href="https://www.bilibili.com/video/BV164411b7dx">吴恩达系列视频</a>（这个就有点多了）</p><h3 id="ann是什么">ANN是什么</h3><p>神经网络不是玄学，全是数学，具体点，我目前做的东西涉及到的概念其实也就：偏导数，线性代数，链式法则，都是大一就学过的东西</p><p>本文会讲解如何训练一个识别MNIST手写数字集的神经网络，MNIST手写数字集是包含了数万张28x28的手写数字图像的数据集（以下简称MNIST，但其实还有MNIST衣物图片的数据集等），每个数字对应了一个<code>lable</code>，指明图像对应的数字，如下图就是一份数据集中包含的数据样本，而他对应的<code>lable</code>是<code>1</code></p><p><img src="/images/FCNN_sample.png"/></p><p>抽象的说，神经网络就是一个函数（这样思考对于CTF中构造对抗样本很有帮助），你给他一个输入，他给你一个输出</p><p>记这个网络为<code>h</code>（hypothesis），这个输入的<code>1</code>的数字图片样本记为<code>X</code>，则我们所期待的是模型输出一个向量来描述各个数字的概率<code>h(X)=&#123;0,1,0,0,0,0,0,0,0,0&#125;</code>（从左到右是0至9的概率，意思模型认为图片是<code>1</code>的概率是100%，是除了<code>1</code>以外的数字的概率是0%，这样的格式称为<code>OneHot Encoding</code>，好处是便于矩阵计算）</p><h3 id="ann为何表现出智能">ANN为何表现出智能</h3><p>在3b1b的视频中，他在Part1的5:40提到</p><blockquote><p>Why it's even reasonable to expect a layered structure like this to behave intelligently</p></blockquote><p>他给出了一个比较让人容易接受的理解，即在多层次的结构中，上一层处理出图像的一些细微特征，然后在下一层对这些特征进行组合</p><p>但显然真正的网络并不是这样工作的，在3b1b的视频中，他在Part2的14:25展示了一些神经网络的权重可视化，而直观上看可视化后的权重，基本就是稍微有序一些的噪音，所以关于这个网络怎么能够得到我们所期待的功能那是这个网络自己的事情（“你已经是一个成熟的神经网络了”），只要他找到了一个还算不错的局部最优解，那其实就已经足够了</p><p>总结一下就是，把ANN看成一个数学模型就行，千万别想玄乎了，在生物上的神经科学发展完全之前，对“智能”的定义都会是比较模糊的，我们就算感觉AI好像拥有智能，那也只不过是一个结构比较复杂的函数</p><h2 id="一个神经元">一个神经元</h2><p>我很喜欢的一句话：简单的规则可以组成复杂的系统，这句话在神经网络中同样适用，先理解单个神经元的工作原理，才能理解他们组成的网络是如何工作的，在后续对神经网络的数学推导中，我也会先对单个神经元组成的网络推导，然后再推广开来，方便我这样🧠不太好的人理解（这也是3b1b的做法，太照顾人了</p><p>以下内容均以下图所示网络为例</p><p><img src="/images/FCNN_cover.png"/></p><p>首先明确，FCNN是一个多层次结构，包含了输入层，隐藏层，输出层</p><p>单个神经元的机理很简单，观察隐藏层<code>Dense #2</code>中的第0个神经元，它也是一个函数，记作<code>Z[2][0]</code>，其中，<code>Z</code>表示神经元函数本身，<code>[2]</code>表示其在第二层，<code>[0]</code>表示其是第一个神经元，由图可知，它的输入是其上一层的所有神经元的输出，它的输出会传递到下一层的每一个神经元，这也是“全连接”的直接体现（你要是看到图里面他没有全连接起来，那多半是我抠图的时候魔棒不小心扣没了）</p><p>而神经元之间的信号传递其实是一个线性的过程，类比<code>y=kx+b</code>，即</p><center><code>Z[2][0]=∑w[2][i][0]*Z[1][i] for i in range(0,6)</code></center><br><center><b>WARNING: 这个式子是错的，但暂时先这样理解</b></center><p><br></p><p>在<code>w[2][i][0]</code>中，<code>w</code>表示权重（类比k，具体点，可以把w相成是神经元之间连接的线），<code>[2]</code>表示该权重是第一层与第二层之间的，<code>[i]</code>表示上一层中的第i个，<code>[0]</code>表示下一层中的第0个（表达上有点繁琐，但并不难理解）</p><h3 id="偏置">偏置</h3><p>显然相对于<code>y=kx+b</code>，还少了其中的<code>b</code>，为了补上<code>b</code>，在FCNN中，会给除了输出层之外的每一层补上一个神经元作为<code>bias</code>，又称偏置，这是一个特殊的神经元，其不接受任何输入，然后对下一层中的每一个神经元都稳定输出一个<code>+1</code>，然后下一层中偏置对每个神经元影响的大小再由对应的权重来调整，具体的连接方式如图白色部分所示</p><p><img src="/images/FCNN_bias.png"/></p><p>现在可以试着举一个例子了，比如考虑如下情况<code>?</code>的值</p><p><img src="/images/FCNN_neuronExample.png"/></p><p>而问号的下面那个神经元的值，是<code>-0.3+0.05+(-0.12)+(-0.4)=-0.77</code></p><h3 id="激活函数">激活函数</h3><p>如果直接把线性计算得到的<code>Z</code>作为该神经元的输出传递到下一层，那么得到的网络也将是一个线性网络，这样的网络不管是十层还是百层都只能相当于一层</p><p>为了得到一个非线性的网络，在除了输入层之外的每一层都会在<code>Z</code>的基础上再套一个激活函数<code>A</code>，激活函数以<code>Z</code>作为输入，即神经元的输出其实是<code>A(Z)</code></p><p>常见的激活函数有：<code>ReLU</code>，<code>tanh</code>，<code>sigmoid</code>等，就不附图了搜一下就有（懒</p><p>那么，更新一以下之前提到的<code>Z</code>的式子，应该是</p><center><code>Z[2][0]=∑w[2][i][0]*A[2][i], i for i in range(0,6)</code></center><p><br></p><p>（由于偏置作为一个神经元包含进上一层的输出，就不在式子中单独加上一个<code>b</code>了）</p><p>对于输出层，激活函数则要根据具体问题来选择，例如MNIST手写数字集是分类0至9的数字，有多个类别，则会选用<code>softMax</code>作为输出的激活函数，其特征是累加之和为1，这符合多分类的问题的期望解，更具体的东西，比如<code>softMax</code>的求偏导，具体公式之类的，就不赘述了（懒</p><h3 id="流程梳理">流程梳理</h3><p>以下梳理一下数据传递的整体流程</p><ul><li>输入的数据为<code>X</code>（输入层），直接输入到隐藏层</li><li>隐藏层第一层的输出为<code>A1(Z1)</code>，其中<code>Z1=w1*X</code></li><li>隐藏层第二层的输出为<code>A2(Z2)</code>，其中<code>Z2=w2*A1</code></li><li>......以此类推</li></ul><h2 id="训练过程">训练过程</h2><p>主要就是梯度下降，一句话，求梯度，然后往负梯度的方向行进，可以求得一个函数的极小值，但是：为什么梯度下降这个方法可以训练一个模型，具体是怎么implement的，大概就到了大多数人的认知边界了/滑稽。考虑梯度下降之前，先了解求梯度的应用对象：损失函数</p><h3 id="损失函数">损失函数</h3><p>loss/cost function，记作<code>J</code>，是描述模型的预测结果对于真实值的差异的函数，举个例子，给了模型<code>1</code>的图片输入，模型的输出却是<code>&#123;0.5,0.5,0,0,0,0,0,0,0,0&#125;</code>，这与应该出现的结果产生了偏差，要描述这个偏差，可以直接求实际输出向量和期望的输出向量之差的L2范数，即求他们的均方误差，记期望的向量为<code>y</code>，实际输出的向量为<code>h(x)</code>，则<code>J=∑((y-h(x))*(y-h(x)))</code>，此处乘积为对应元素相乘，则计算可得loss大约是0.71</p><p>通过损失函数，就可以量化表示模型预测结果的准确度了，而大多数时候会使用较复杂度更高的带<code>log</code>的交叉熵函数作为损失函数，公式如下</p><p><img src="/images/FCNN_crossEntropy.png"/></p><p>看起来比较复杂，但画一个抛物线来表示均方误差函数，再画一下log的图像理解交叉熵函数就差不多了</p><p>交叉熵好处都有啥：收敛快，局部最优点少，知道就行</p><h3 id="梯度下降">梯度下降</h3><blockquote><p>除了单纯的梯度下降，还有如momentum，RmsProp，Adam等优化算法，但本文使用<code>mini-batch</code>梯度下降法来训练</p></blockquote><p>首先明确常量：训练时用的样本<code>X</code>是固定不变的，样本对应的<code>lable</code>，或记作<code>y</code>，也是不变的，唯一变化的就只有网络中间连接各个层级的权重<code>w</code>（偏置<code>b</code>包含在权重里边，因为偏置的具体大小由连接的权重控制），以及因为权重变化而跟着一起变化的预测结果，还有中间量<code>A</code>和<code>Z</code>，一切变化都来自于权重<code>w</code>的变化</p><p>问题现在则应该理解成：找到合适的<code>w</code>，使得<code>J</code>最小</p><p>具体点，就是：对<code>J(w)</code>求关于<code>w</code>的偏导，获取<code>J(w)</code>的梯度，然后更新权重<code>w</code>为<code>w-=rate*dw</code>，其中，<code>dw</code>为<code>J(w)</code>的梯度，<code>rate</code>为学习率，控制了一次下降多少，学习率过低会导致训练缓慢，学习率过高则容易各种<code>NAN</code>或者反复横跳无法收敛，需要一定的试错成本来确定合适的学习率，一般在10e-6到1之间</p><p>由于这部分内容网上资源挺多的，就不赘述了（懒</p><p>（后面会有反向传播的推导，这才是重点</p><h2 id="具体实现">具体实现</h2><p>以上就是一些重要的前置（因为我怕把理解和代码融为一体会导致逻辑混乱），以下内容就正式开始涉及码代码了，坐好扶稳</p><h3 id="预处理">预处理</h3><p>在<code>kaggle</code>下载到他们提供的MNIST手写数字数据集后，不能上手即用</p><ul><li>颜色范围0至255这个区间太大了，应该压缩到0至1，整体除以255</li><li>再者数据的分布也不自然，应该套一个标准分数公式，处理成正态分布</li></ul><p>关于应该对单个像素做处理还是对整体做处理（例如求均值，是对每一个像素求出对应像素位置的均值，还是整体处理，直接求出所有像素的均值），在本例中应该整体处理更好</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Source    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 处理数据集</span></span><br><span class="line">dsTrain = pd.read_csv(<span class="string">r&#x27;mnist.csv&#x27;</span>)</span><br><span class="line"><span class="comment"># 原本kaggle上的测试集是没有lable分类的，在kaggle的leaderBoard上找到正确的lable后pd.concat到原本的测试集</span></span><br><span class="line">dsTest = pd.read_csv(<span class="string">r&#x27;mnist_test.csv&#x27;</span>)</span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string"># 获取训练集的特征</span></span><br><span class="line"><span class="string">ds_std = np.std(dsTrain.iloc[:, 1:], axis = 0) # 训练集标准差</span></span><br><span class="line"><span class="string">ds_mean = np.mean(dsTrain.iloc[:, 1:], axis = 0) # 训练集均值</span></span><br><span class="line"><span class="string"># 标准化数据，此方法是针对训练集中的同一位置的像素，效果一般，故未采用</span></span><br><span class="line"><span class="string">for i in range(len(ds.T) - 1):</span></span><br><span class="line"><span class="string">    if ds_std[i] != 0: # 边角上就很可能出现标准差为0的像素</span></span><br><span class="line"><span class="string">        dsR.iloc[:, i + 1] = (dsR.iloc[:, i + 1] - ds_mean[i]) / ds_std[i] # 标准分数公式，基本呈正态分布</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">trainX = dsTrain.iloc[:, <span class="number">1</span>:]</span><br><span class="line">trainy = dsTrain.iloc[:, :<span class="number">1</span>]</span><br><span class="line"><span class="comment"># 排列测试集，方便在测试之后绘图</span></span><br><span class="line">testX = pd.concat([dsTest[dsTest[<span class="string">&quot;label&quot;</span>] == i].iloc[:, <span class="number">1</span>:] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)])</span><br><span class="line">testy = pd.concat([dsTest[dsTest[<span class="string">&quot;label&quot;</span>] == i].iloc[:, :<span class="number">1</span>] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)])</span><br><span class="line"><span class="comment"># 归一化</span></span><br><span class="line">trainX = trainX / <span class="number">255</span></span><br><span class="line">testX = testX / <span class="number">255</span></span><br><span class="line"><span class="comment"># 获取训练集特征</span></span><br><span class="line">mean = np.mean(np.array(trainX).ravel())  <span class="comment"># 训练集均值</span></span><br><span class="line">std = np.std(np.array(trainX).ravel())  <span class="comment"># 训练集标准差</span></span><br><span class="line">trainX = (trainX - mean) / std  <span class="comment"># 标准分数公式，基本呈正态分布</span></span><br><span class="line">testX = (testX - mean) / std  <span class="comment"># 用训练集的特征处理测试集数据，才能证明模型是否能识别陌生数据</span></span><br></pre></td></tr></table></figure>    </div></div><h3 id="数学函数">数学函数</h3><p>之前提到了一堆数学函数，实现起来主要就是<code>softMax</code>和<code>sigmoid</code>会比较耽误时间</p><p><code>leaky ReLU</code>是<code>ReLU</code>的实验性变种，通常用于在实验的时候避免因为使用<code>ReLU</code>导致大量神经元死亡，梯度消失的问题</p><blockquote><p>后记：之前的SoftMax偏导求错了，其应该是一个方阵，但估计是因为一开始写的时候为了方便，没有理解清楚就去找了一段标称是derivative of softmax的代码，结果他求的只是在<code>i=j</code>情况下的的softmax<br>虽然事后证明这一谬误只影响了我一个百分点的正确率，但给我后来复用这段代码来找对于输入向量的梯度，并用自己找到的梯度和<code>tensorflow</code>中<code>gradientTape</code>求得的梯度比较时怀疑人生</p></blockquote><div class='spoiler collapsed'>    <div class='spoiler-title'>        Source    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># d_func 是func的导数/偏导 derivative</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">relu</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> np.maximum(x, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">d_relu</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> x &gt; <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">lrelu</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="comment"># leaky relu</span></span><br><span class="line">    <span class="keyword">return</span> np.maximum(x, <span class="number">0.1</span> * x)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">d_lrelu</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> (x &gt; <span class="number">0</span>) * <span class="number">0.9</span> + <span class="number">0.1</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">softMax</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> np.array(np.exp(x) / np.<span class="built_in">sum</span>(np.exp(x), axis=<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">d_softMax</span>(<span class="params">x</span>):</span></span><br><span class="line">    x = softMax(x)</span><br><span class="line">    r = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(x.shape[<span class="number">0</span>]):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(x.shape[<span class="number">1</span>]):</span><br><span class="line">            r.append([])</span><br><span class="line">            <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">range</span>(x.shape[<span class="number">1</span>]):</span><br><span class="line">                r[-<span class="number">1</span>].append(-x[i][j] * x[i][k] <span class="keyword">if</span> j != k <span class="keyword">else</span> x[i][j] * (<span class="number">1</span> - x[i][k]))</span><br><span class="line">    <span class="keyword">return</span> np.array(r)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sigmoid</span>(<span class="params">x</span>):</span></span><br><span class="line">    x_ravel = np.array(x).ravel()</span><br><span class="line">    length = <span class="built_in">len</span>(x_ravel)</span><br><span class="line">    y = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(length):</span><br><span class="line">        <span class="keyword">if</span> x_ravel[i] &gt;= <span class="number">0</span>:</span><br><span class="line">            x_ravel[i] = <span class="built_in">min</span>(<span class="number">19</span>, x_ravel[i])  <span class="comment"># 限制范围，精度不够，np.exp()也易爆</span></span><br><span class="line">            y.append(<span class="number">1.0</span> / (<span class="number">1</span> + np.exp(-x_ravel[i])))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            x_ravel[i] = <span class="built_in">max</span>(-<span class="number">744</span>, x_ravel[i])</span><br><span class="line">            y.append(np.exp(x_ravel[i]) / (np.exp(x_ravel[i]) + <span class="number">1</span>))</span><br><span class="line">    <span class="keyword">return</span> np.array(y).reshape(x.shape)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">d_sigmoid</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="keyword">return</span> sigmoid(x) * (<span class="number">1</span> - sigmoid(x))</span><br></pre></td></tr></table></figure>    </div></div><h3 id="数据处理函数">数据处理函数</h3><p>之前提到的<code>OneHot</code>和插入<code>bias</code></p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Source    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">OneHot</span>(<span class="params">length, width, y</span>):</span></span><br><span class="line">    <span class="comment"># OneHot encoding</span></span><br><span class="line">    y = np.array(y).ravel()</span><br><span class="line">    r = np.zeros((length, width))</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(length):</span><br><span class="line">        r[i][<span class="built_in">int</span>(y[i])] = <span class="number">1.0</span></span><br><span class="line">    <span class="keyword">return</span> r</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">AddBias</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="comment"># 在第0列插入全为1的列</span></span><br><span class="line">    x = np.matrix(x)</span><br><span class="line">    <span class="keyword">return</span> np.c_[np.ones((x.shape[<span class="number">0</span>], <span class="number">1</span>)), x]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">RemoveBias</span>(<span class="params">x</span>):</span></span><br><span class="line">    <span class="comment"># 去掉第一列</span></span><br><span class="line">    <span class="keyword">return</span> np.matrix(x)[:, <span class="number">1</span>:]</span><br></pre></td></tr></table></figure>    </div></div><h3 id="fcnn-class">FCNN class</h3><p>以下开始填充<code>FCNN</code>类</p><p>明确一下类中的基本函数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FCNN</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">Init</span>(<span class="params">self, unitNum</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">PrepareBatch</span>(<span class="params">self, X, y, batchSize=<span class="number">0</span></span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">fit</span>(<span class="params">self, epoch, rate</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">gradDes</span>(<span class="params">self, rate</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">backProp</span>(<span class="params">self</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">forwardProp</span>(<span class="params">self</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">ClrTempResult</span>(<span class="params">self</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">predict</span>(<span class="params">self, X, y</span>)</span></span><br></pre></td></tr></table></figure><p>明确一些概念</p><ul><li>batchSize：如果整个过完一遍训练集中的数据才更新一次权重，则更新速度过慢，故将数据集拆分为多批来处理，batchSize就是一批数据有多少份样本</li><li>epoch：总共要过几遍完整的数据，所以权重的更新次数就是<code>epoch*N/batchSize</code>，其中<code>N</code>是训练集样本总量</li><li>backward Propagation：后向传播，指通过梯度来调整权重的过程<strong>（重点）</strong></li><li>forward Propagation：前向传播，指传入样本后得到输出结果的过程</li></ul><p>以上概念虽然名词是在本文中第一次出现，但结合上文都不难理解</p><p>而训练过程中还有一个称作<code>DropOut</code>的优化方法，指的是在训练中随机地掐死一些神经元，前向传播的时候不经过他们，后向传播的时候也不更新他们，从而使得模型更具鲁棒性，也可以有效避免模型过拟合（过拟合：模型泛化不好，训练集和测试集的识别准确率相差过大），关于<code>DropOut</code>为什么能增强模型鲁棒性、避免过拟合的说法有很多，建议自己搜（懒</p><p>然后我们肯定希望模型可以保存下来，至少训练出来的权重得能保存吧，不然每次都得重来一遍</p><p>所以再补几个函数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FCNN</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">GetDropOut</span>(<span class="params">self</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">SaveParameters</span>(<span class="params">self</span>)</span></span><br><span class="line"><span class="function">    <span class="title">def</span> <span class="title">LoadParameters</span>(<span class="params">self</span>)</span></span><br></pre></td></tr></table></figure><p>其中<code>backProp</code>的公式推导这个重点问题放到下面的模块来说，这里则再提一些小一点的问题</p><ul><li><p>权重的初始化方法</p><p>选择错误的权重初始化方法会导致梯度消失等严重问题，可能导致训练根本无法开始，由于我选择的是<code>ReLU</code>作为隐藏层的激活函数，使用了<code>He initialization</code>，具体为什么这样的初始化对<code>ReLU</code>会更友好则没有深入探究</p></li><li><p>正则项</p><p>有一种防止过拟合的方法是：在损失函数中加入有关权重<code>w</code>的二次项，这样在<code>dw</code>中就体现在每次更新权重时权重都会自减一点点</p></li><li><p>DropOut的具体实现</p><p>创建与<code>w</code>同样size的矩阵，将本轮中掐死的神经元的行与列置<code>0</code>，其他置<code>1</code>，传递时把<code>w</code>换成<code>np.multiply(w,dropOut)</code>即可</p></li></ul><div class='spoiler collapsed'>    <div class='spoiler-title'>        Source    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FCNN</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">Init</span>(<span class="params">self, unitNum, dropOutProb=[]</span>):</span></span><br><span class="line">        <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">        完成一些初始化</span></span><br><span class="line"><span class="string">        unitNum: list 每一层的神经元数量</span></span><br><span class="line"><span class="string">        dropOutProb: list 除了输出层以外每一层的dropOut概率(0,1)</span></span><br><span class="line"><span class="string">        &#x27;&#x27;&#x27;</span></span><br><span class="line">        self.unitNum = unitNum</span><br><span class="line">        self.layerNum = <span class="built_in">len</span>(unitNum)</span><br><span class="line">        self.dropOutProb = dropOutProb <span class="keyword">if</span> dropOutProb <span class="keyword">else</span> [<span class="number">0</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.layerNum - <span class="number">1</span>)]</span><br><span class="line">        self.outLength = unitNum[-<span class="number">1</span>]</span><br><span class="line">        self.w = [<span class="number">0</span>]  <span class="comment"># 0 的作用仅为占位</span></span><br><span class="line">        self.Z = [<span class="number">0</span>]</span><br><span class="line">        self.A = [<span class="number">0</span>]</span><br><span class="line">        self.cost = []</span><br><span class="line">        self.costTemp = []</span><br><span class="line">        self.dwNorm = []</span><br><span class="line">        self.dwNormTemp = []</span><br><span class="line">        <span class="comment"># He initialization，针对隐藏层是reLu激活函数的权重初始化方法</span></span><br><span class="line">        <span class="comment"># 正确的权重初始化方法能一定程度避免梯度消失</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, self.layerNum - <span class="number">1</span>):</span><br><span class="line">            np.random.seed(i)  <span class="comment"># 设置随机数种子</span></span><br><span class="line">            self.w.append(np.matrix(np.random.randn(unitNum[i - <span class="number">1</span>] + <span class="number">1</span>, unitNum[i])) * np.sqrt(<span class="number">2</span> / unitNum[i]))</span><br><span class="line">            self.w[-<span class="number">1</span>][<span class="number">0</span>] = np.matrix(np.zeros((<span class="number">1</span>, unitNum[i]))) <span class="comment"># bias偏置初始化为0</span></span><br><span class="line">        <span class="comment"># 最后的权重初始化和之前的不太一样，需要更加接近0，这是针对sigmoid/softMax</span></span><br><span class="line">        np.random.seed(self.layerNum - <span class="number">1</span>)</span><br><span class="line">        self.w.append(np.matrix(np.random.rand(unitNum[-<span class="number">2</span>] + <span class="number">1</span>, unitNum[-<span class="number">1</span>])))</span><br><span class="line">        self.w[-<span class="number">1</span>] = (self.w[-<span class="number">1</span>] - np.mean(self.w[-<span class="number">1</span>])) / np.std(self.w[-<span class="number">1</span>]) / <span class="number">100</span></span><br><span class="line">        self.w[-<span class="number">1</span>][<span class="number">0</span>] = np.matrix(np.zeros((<span class="number">1</span>, unitNum[-<span class="number">1</span>])))</span><br><span class="line">        self.ClrTempResult()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">PrepareBatch</span>(<span class="params">self, X, y, batchSize=<span class="number">0</span></span>):</span></span><br><span class="line">        <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">        分割训练集并完成一些初始化</span></span><br><span class="line"><span class="string">        batchSize: 每一批样本的数量，需能被样本总数整除</span></span><br><span class="line"><span class="string">        &#x27;&#x27;&#x27;</span></span><br><span class="line">        N = X.shape[<span class="number">0</span>]</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> batchSize:</span><br><span class="line">            batchSize = N</span><br><span class="line">        <span class="keyword">if</span> N % batchSize:</span><br><span class="line">            print(<span class="string">&quot;# ERR: illegal batchSize&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        self.batchSize = batchSize</span><br><span class="line">        self.batchTime = <span class="built_in">int</span>(N / batchSize)</span><br><span class="line">        self.X = np.matrix(X)</span><br><span class="line">        self.y = OneHot(X.shape[<span class="number">0</span>], unitNum[-<span class="number">1</span>], y)</span><br><span class="line">        self.batchX = []</span><br><span class="line">        self.batchy = []</span><br><span class="line">        self.batchTime = <span class="built_in">int</span>(N / batchSize)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.batchTime):</span><br><span class="line">            <span class="comment"># mini-batch可以加快训练速度</span></span><br><span class="line">            self.batchX.append(np.array(self.X[i * batchSize : (i + <span class="number">1</span>) * batchSize]))</span><br><span class="line">            self.batchy.append(self.y[i * batchSize : (i + <span class="number">1</span>) * batchSize])</span><br><span class="line">            print(<span class="string">&quot;\rPreparing: %.2f%%&quot;</span> % ((i + <span class="number">1</span>) / N * batchSize * <span class="number">100</span>), end=<span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">fit</span>(<span class="params">self, epoch, rate</span>):</span></span><br><span class="line">        <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">        epoch: int 训练轮次</span></span><br><span class="line"><span class="string">        rate: float 学习率</span></span><br><span class="line"><span class="string">        &#x27;&#x27;&#x27;</span></span><br><span class="line">        N = self.X.shape[<span class="number">0</span>]</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epoch):</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.batchTime):</span><br><span class="line">                self.Z[<span class="number">0</span>] = self.batchX[j]</span><br><span class="line">                self.A[<span class="number">0</span>] = self.batchX[j]</span><br><span class="line">                self.y = self.batchy[j]</span><br><span class="line">                self.gradDes(rate)</span><br><span class="line">                self.ClrTempResult()</span><br><span class="line">                print(</span><br><span class="line">                    <span class="string">&quot;\rTotalProgress: %.2f%% BatchProgress: %.2f%%  &quot;</span></span><br><span class="line">                    % (((i + <span class="number">1</span>) / epoch * <span class="number">100</span>), ((j + <span class="number">1</span>) / N * self.batchSize * <span class="number">100</span>)),</span><br><span class="line">                    end=<span class="string">&quot;&quot;</span>,</span><br><span class="line">                )</span><br><span class="line">            <span class="comment"># 记录一个轮次下的平均损失函数</span></span><br><span class="line">            self.cost.append(<span class="built_in">sum</span>(self.costTemp) / self.batchTime)</span><br><span class="line">            self.costTemp.clear()</span><br><span class="line">            <span class="comment"># 记录一个轮次下的平均dw的L2，即模长</span></span><br><span class="line">            self.dwNorm.append(np.<span class="built_in">sum</span>(self.dwNormTemp, axis=<span class="number">0</span>) / self.batchTime)</span><br><span class="line">            self.dwNormTemp.clear()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">gradDes</span>(<span class="params">self, rate</span>):</span></span><br><span class="line">        self.backProp()</span><br><span class="line">        <span class="comment"># 没有加入正则项惩罚，因为未发现过拟合现象，同时样本足够多时可以不加入正则项</span></span><br><span class="line">        <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">        # 一种较为简单的损失函数 J(θ)</span></span><br><span class="line"><span class="string">        J = np.sum(np.multiply(self.y-self.A[-1],self.y-self.A[-1]))/self.batchSize</span></span><br><span class="line"><span class="string">        # 这里使用更加复杂的带有log的交叉熵函数</span></span><br><span class="line"><span class="string">        &#x27;&#x27;&#x27;</span></span><br><span class="line">        J = (-np.<span class="built_in">sum</span>(np.array(self.y) * np.log(self.A[-<span class="number">1</span>]) + np.array(<span class="number">1</span> - self.y) * np.log(<span class="number">1</span> - self.A[-<span class="number">1</span>])) / self.batchSize)</span><br><span class="line">        self.costTemp.append(J)</span><br><span class="line">        dwNt = []</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="built_in">len</span>(self.w)):</span><br><span class="line">            dwNt.append(np.linalg.norm(self.dw[i]))</span><br><span class="line">            self.w[i] -= rate * self.dw[i]</span><br><span class="line">        self.dwNormTemp.append(dwNt)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">GetDropOut</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="comment"># 获取dropOut矩阵，根据给定概率随机去掉神经元</span></span><br><span class="line">        index = []</span><br><span class="line">        prob = self.dropOutProb[<span class="number">0</span>]</span><br><span class="line">        num = <span class="number">0</span> <span class="keyword">if</span> <span class="keyword">not</span> prob <span class="keyword">else</span> <span class="built_in">int</span>(np.clip(np.random.randn() / <span class="number">50</span> + prob, <span class="number">0</span>, <span class="number">0.75</span>) * self.unitNum[<span class="number">0</span>])</span><br><span class="line">        index.append(np.random.choice(self.unitNum[<span class="number">0</span>], num, <span class="literal">False</span>))</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, self.layerNum - <span class="number">1</span>):</span><br><span class="line">            prob = self.dropOutProb[i]</span><br><span class="line">            num = <span class="number">0</span> <span class="keyword">if</span> <span class="keyword">not</span> prob <span class="keyword">else</span> <span class="built_in">int</span>(np.clip(np.random.randn() / <span class="number">50</span> + prob, <span class="number">0</span>, <span class="number">0.75</span>) * self.unitNum[i])</span><br><span class="line">            index.append(np.random.choice(self.unitNum[i], num, <span class="literal">False</span>))</span><br><span class="line">            dropOutMat = np.ones((self.unitNum[i - <span class="number">1</span>], self.unitNum[i]))</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> index[i - <span class="number">1</span>]:</span><br><span class="line">                dropOutMat[j] = np.zeros((<span class="number">1</span>, self.unitNum[i]))</span><br><span class="line">            dropOutMat = np.transpose(dropOutMat)</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> index[i]:</span><br><span class="line">                dropOutMat[j] = np.zeros((<span class="number">1</span>, self.unitNum[i - <span class="number">1</span>]))</span><br><span class="line">            dropOutMat = np.r_[np.ones((<span class="number">1</span>, self.unitNum[i])), np.transpose(dropOutMat)]</span><br><span class="line">            self.dropOut.append(np.matrix(dropOutMat))</span><br><span class="line">        dropOutMat = np.ones((self.unitNum[-<span class="number">2</span>], self.unitNum[-<span class="number">1</span>]))</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> index[-<span class="number">1</span>]:</span><br><span class="line">            dropOutMat[j] = np.zeros((<span class="number">1</span>, self.unitNum[-<span class="number">1</span>]))</span><br><span class="line">        dropOutMat = np.r_[np.ones((<span class="number">1</span>, self.unitNum[-<span class="number">1</span>])), dropOutMat]</span><br><span class="line">        self.dropOut.append(np.matrix(dropOutMat))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">backProp</span>(<span class="params">self, d_lossFunc, forward=<span class="literal">True</span>, dX=<span class="literal">False</span></span>):</span></span><br><span class="line">        <span class="comment"># 有详细推导过程</span></span><br><span class="line">        <span class="keyword">if</span> forward:</span><br><span class="line">            self.forwardProp()</span><br><span class="line">        self.dA[-<span class="number">1</span>] = d_lossFunc()</span><br><span class="line">        <span class="comment"># --- appaly derivative of SoftMax</span></span><br><span class="line">        self.dZ[-<span class="number">1</span>] = []</span><br><span class="line">        Zt = np.array(self.Z[-<span class="number">1</span>])</span><br><span class="line">        dAt = np.array(self.dA[-<span class="number">1</span>])</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.batchSize):</span><br><span class="line">            self.dZ[-<span class="number">1</span>].append(np.array(d_softMax(np.matrix(Zt[i])) *</span><br><span class="line">                                   np.matrix(dAt[i]).reshape((self.unitNum[-<span class="number">1</span>], <span class="number">1</span>))).ravel())</span><br><span class="line">        self.dZ[-<span class="number">1</span>] = np.matrix(self.dZ[-<span class="number">1</span>])</span><br><span class="line">        <span class="comment"># ---</span></span><br><span class="line">        dw = self.A[-<span class="number">2</span>].T * self.dZ[-<span class="number">1</span>]</span><br><span class="line">        db = np.<span class="built_in">sum</span>(self.dZ[-<span class="number">1</span>], axis=<span class="number">0</span>)</span><br><span class="line">        self.dw[-<span class="number">1</span>] = np.r_[db, dw] / self.batchSize</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">reversed</span>(<span class="built_in">range</span>(<span class="number">1</span>, self.layerNum - <span class="number">1</span>)):</span><br><span class="line">            self.dA[i] = RemoveBias(self.dZ[i + <span class="number">1</span>] * np.multiply(self.w[i + <span class="number">1</span>], self.dropOut[i]).T)</span><br><span class="line">            self.dZ[i] = np.multiply(d_relu(self.Z[i]), self.dA[i])</span><br><span class="line">            dw = self.A[i - <span class="number">1</span>].T * self.dZ[i]</span><br><span class="line">            db = np.<span class="built_in">sum</span>(self.dZ[i], axis=<span class="number">0</span>)</span><br><span class="line">            self.dw[i] = np.multiply(np.r_[db, dw] / self.batchSize, self.dropOut[i - <span class="number">1</span>])</span><br><span class="line">        <span class="keyword">if</span> dX:</span><br><span class="line">            self.dA[<span class="number">0</span>] = RemoveBias(self.dZ[<span class="number">1</span>] * np.multiply(self.w[<span class="number">1</span>], self.dropOut[<span class="number">0</span>]).T)</span><br><span class="line">            self.dZ[<span class="number">0</span>] = self.dA[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">forwardProp</span>(<span class="params">self</span>):</span></span><br><span class="line">        self.GetDropOut()</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, self.layerNum - <span class="number">1</span>):</span><br><span class="line">            self.Z.append(AddBias(self.A[-<span class="number">1</span>]) * np.multiply(self.w[i], self.dropOut[i - <span class="number">1</span>]))</span><br><span class="line">            self.A.append(relu(self.Z[-<span class="number">1</span>]))</span><br><span class="line">        self.Z.append(AddBias(self.A[-<span class="number">1</span>]) * np.multiply(self.w[-<span class="number">1</span>], self.dropOut[-<span class="number">1</span>]))</span><br><span class="line">        self.A.append(softMax(self.Z[-<span class="number">1</span>]))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">ClrTempResult</span>(<span class="params">self</span>):</span></span><br><span class="line">        self.Z = self.Z[<span class="number">0</span>:<span class="number">1</span>]</span><br><span class="line">        self.A = self.A[<span class="number">0</span>:<span class="number">1</span>]</span><br><span class="line">        self.dw = <span class="built_in">list</span>(np.repeat(<span class="number">1</span>, self.layerNum, axis=<span class="number">0</span>))</span><br><span class="line">        self.dZ = <span class="built_in">list</span>(np.repeat(<span class="number">1</span>, self.layerNum, axis=<span class="number">0</span>))</span><br><span class="line">        self.dA = <span class="built_in">list</span>(np.repeat(<span class="number">1</span>, self.layerNum, axis=<span class="number">0</span>))</span><br><span class="line">        self.dropOut = []</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">predict</span>(<span class="params">self, X, y</span>):</span></span><br><span class="line">        self.ClrTempResult()</span><br><span class="line">        self.dropOutProb = [<span class="number">0</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.layerNum - <span class="number">1</span>)]</span><br><span class="line">        self.Z[<span class="number">0</span>] = np.matrix(X)</span><br><span class="line">        self.A[<span class="number">0</span>] = np.matrix(X)</span><br><span class="line">        self.forwardProp()</span><br><span class="line">        r_p = self.A[-<span class="number">1</span>]</span><br><span class="line">        self.r = np.zeros((r_p.shape[<span class="number">0</span>], <span class="number">1</span>))</span><br><span class="line">        correct = <span class="number">0</span></span><br><span class="line">        self.wrong = []</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(r_p.shape[<span class="number">0</span>]):</span><br><span class="line">            self.r[i] = np.argmax(r_p[i])</span><br><span class="line">            <span class="keyword">if</span> self.r[i] == np.array(y).ravel()[i]:</span><br><span class="line">                correct += <span class="number">1</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                self.wrong.append(i) <span class="comment"># 储存错误样本的index</span></span><br><span class="line">        print(<span class="string">&quot;correct: &quot;</span> + <span class="built_in">str</span>(correct / r_p.shape[<span class="number">0</span>]))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">SaveParameters</span>(<span class="params">self</span>):</span></span><br><span class="line">        f = <span class="built_in">open</span>(<span class="string">&quot;FCNNweight.dat&quot;</span>, <span class="string">&quot;wb&quot;</span>)</span><br><span class="line">        wt = []</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> self.w[<span class="number">1</span>:]:</span><br><span class="line">            wt.extend(<span class="built_in">list</span>(np.array(i).ravel()))</span><br><span class="line">        f.write(np.array(wt).tobytes())</span><br><span class="line">        f.close()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">LoadParameters</span>(<span class="params">self</span>):</span></span><br><span class="line">        f = <span class="built_in">open</span>(<span class="string">&quot;FCNNweight.dat&quot;</span>, <span class="string">&quot;rb&quot;</span>)</span><br><span class="line">        wt = np.frombuffer(f.read(), np.float64)</span><br><span class="line">        f.close()</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="built_in">len</span>(self.unitNum)):</span><br><span class="line">            self.w[i] = np.matrix(wt[: (self.unitNum[i - <span class="number">1</span>] + <span class="number">1</span>) * self.unitNum[i]]).reshape((self.unitNum[i - <span class="number">1</span>] + <span class="number">1</span>, self.unitNum[i]))</span><br><span class="line">            wt = wt[(self.unitNum[i - <span class="number">1</span>] + <span class="number">1</span>) * self.unitNum[i] :]</span><br></pre></td></tr></table></figure>    </div></div><h3 id="后向传播推导">后向传播推导</h3><p>以下为我梳理代码的时候顺便写的过程</p><p>链式法则推单个神经元，然后扩展到矩阵</p><p>强烈建议搭配3b1b系列part4（或者是part3[下]？）食用，附上一张3b1b视频的截图</p><p><img src="/images/FCNN_formula.png"/></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line">layerNum = <span class="number">4</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string"> input -&gt; hidden 1 -&gt; hidden 2 -&gt; output</span></span><br><span class="line"><span class="string"> 784+1 -&gt;   H1+1   -&gt;   H2+1   -&gt;   10</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> Z0(X)  ┌───&gt;Z1     ┌───&gt;Z2     ┌──&gt;Z3</span></span><br><span class="line"><span class="string">   │    │     ↓     │     ↓     │    ↓</span></span><br><span class="line"><span class="string">   │  [w1] [relu] [w2] [relu] [w3][softmax]</span></span><br><span class="line"><span class="string">   ↓    │     ↓     │     ↓     │    ↓</span></span><br><span class="line"><span class="string"> A0(X)──┘    A1─────┘    A2─────┘   A3</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">unityNum = [<span class="number">784</span>, H1, H2, <span class="number">10</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">### Init ###</span></span><br><span class="line">w = [ <span class="number">0</span>, [w1(<span class="number">784</span>+<span class="number">1</span>,H1)], [w2(H1+<span class="number">1</span>,H2)], [w3(H2+<span class="number">1</span>,<span class="number">10</span>)] ]</span><br><span class="line">Z = [ [X(N,<span class="number">784</span>)] ]</span><br><span class="line"><span class="comment"># A has same structure as Z</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### forwardProp ###</span></span><br><span class="line">Z = [ [X(N,<span class="number">784</span>)], [Z1(N,H1)], [Z2(N,H2)], [Z3(N,<span class="number">10</span>)] ]</span><br><span class="line"><span class="comment"># A has same structure as Z</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### backProp ###</span></span><br><span class="line"><span class="comment"># dX means δJ/δX</span></span><br><span class="line"><span class="comment"># aka partial derivative of parameter X with respect to cost function J</span></span><br><span class="line"><span class="comment"># to get formulas as below, apply chain rule</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string"># u[i] indicates i.th layer of neural network</span></span><br><span class="line"><span class="string"># and each layer has only 1 neuron</span></span><br><span class="line"><span class="string"># L.th layer indicates output layer</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    ....    &#123;u[L-1]&#125;────&#123;u[L]&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># Deduction (lossFunc: CrossEntropy):</span></span><br><span class="line"><span class="string">- layer L :</span></span><br><span class="line"><span class="string">    dA[L] = δJ/δA[L] = A[L] - y</span></span><br><span class="line"><span class="string">    dZ[L] = δA[L]/δZ[L] * dA[L] = sigmoidDeriv(Z[L]) * dA[L]</span></span><br><span class="line"><span class="string">    dw[L] = δw[L]/δZ[L] * dZ[L] = A[L-1] * dZ[L]</span></span><br><span class="line"><span class="string">    db[L] = δb[L]/δZ[L] * dZ[L] = dZ[L]</span></span><br><span class="line"><span class="string">- layer L-1 :</span></span><br><span class="line"><span class="string">    dA[L-1] = δZ[L]/δA[L-1] * dZ[L] = w[L] * dZ[L]</span></span><br><span class="line"><span class="string">    dZ[L-1] = δA[L-1]/δZ[L-1] * dA[L-1] = reluDeriv(Z[L-1]) * dA[L-1]</span></span><br><span class="line"><span class="string">    ... etc.</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># extra process making formulas can be applied to matrices or layers</span></span><br><span class="line"><span class="comment"># no dot product is applied below, just multiplying elements by elements</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string"># when each layer has more than 1 neuron</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">            &#123;u[L-1][0]&#125;</span></span><br><span class="line"><span class="string">                         ┌─&#123;u[L][0]&#125;</span></span><br><span class="line"><span class="string">    ....    &#123;u[L-1][1]&#125;══╡</span></span><br><span class="line"><span class="string">                         └─&#123;u[L][1]&#125;</span></span><br><span class="line"><span class="string">            &#123;u[L-1][2]&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># for a certain neuron in hidden layer, e.g. as for u[L-1][1]</span></span><br><span class="line"><span class="string"># w[L][1][0] indicates weight between &#123;u[L-1][1]&#125; and &#123;u[L][0]&#125;</span></span><br><span class="line"><span class="string"># Deduction :</span></span><br><span class="line"><span class="string">- layer L :</span></span><br><span class="line"><span class="string">    dA[L][0] = A[L][0] - y[0]</span></span><br><span class="line"><span class="string">    dA[L][1] = A[L][1] - y[1]</span></span><br><span class="line"><span class="string">    dZ[L][0] = sigmoidDeriv(Z[L][0]) * dA[L][0]</span></span><br><span class="line"><span class="string">    dZ[L][1] = sigmoidDeriv(Z[L][1]) * dA[L][1]</span></span><br><span class="line"><span class="string">    dw[L][1][0] = A[L-1][1] * dZ[L][0]</span></span><br><span class="line"><span class="string">    dw[L][1][1] = A[L-1][1] * dZ[L][1]</span></span><br><span class="line"><span class="string">    db[L][1][0] = dZ[L][0]</span></span><br><span class="line"><span class="string">    db[L][1][1] = dZ[L][1]</span></span><br><span class="line"><span class="string">- layer L-1 :</span></span><br><span class="line"><span class="string">    dA[L-1][1] = w[L][1][0] * dZ[L][0] + w[L][1][1] * dZ[L][1]</span></span><br><span class="line"><span class="string">    dZ[L-1][1] = reluDeriv(Z[L-1][1]) * dA[L-1][1]</span></span><br><span class="line"><span class="string">    ... etc</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># e.g. as for 3.rd layer, which has index of 2 :</span></span><br><span class="line"><span class="string">    dA2(N,H2) = RemoveBias(dZ3(N,10) * w3.T(10,H2+1))</span></span><br><span class="line"><span class="string">    dZ2(N,H2) = np.multiply(reluDeriv(Z2)(N,H2),dA2(N,H2))</span></span><br><span class="line"><span class="string">    dw2(H1,H2) = A1.T(H1,N) * dZ2(N,H2)</span></span><br><span class="line"><span class="string">    db2(1,H2) = np.sum(dZ2(N,H2),axis=0)</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">dA = [ <span class="number">1</span>, [dA1(N,H1)], [dA2(N,H2)], [dA3(N,<span class="number">10</span>)] ]</span><br><span class="line">dw = [ <span class="number">1</span>, [dw0(<span class="number">784</span>+<span class="number">1</span>,H1)], [dw1(H1+<span class="number">1</span>,H2)], [dw2(H2+<span class="number">1</span>,<span class="number">10</span>)] ]</span><br></pre></td></tr></table></figure><h3 id="main">main</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">unitNum = [<span class="number">784</span>, <span class="number">512</span>, <span class="number">256</span>, <span class="number">10</span>]</span><br><span class="line">dropOutProb = [<span class="number">0.4</span>, <span class="number">0.2</span>, <span class="number">0.1</span>]</span><br><span class="line">batchSize = <span class="number">120</span></span><br><span class="line">epoch = <span class="number">50</span></span><br><span class="line">rate = <span class="number">0.08</span></span><br><span class="line"></span><br><span class="line">FCNN = FCNN()</span><br><span class="line">FCNN.Init(unitNum, dropOutProb)</span><br><span class="line">FCNN.PrepareBatch(trainX, trainy, batchSize)</span><br><span class="line">FCNN.fit(epoch, rate)</span><br><span class="line">FCNN.SaveParameters()</span><br></pre></td></tr></table></figure><h3 id="后续验证">后续验证</h3><p>在训练完只会应当绘制<code>loss</code>图像，用于确保梯度下降是没有问题的（<code>dw</code>模长单纯是我想看一下而已）</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        Source    </div>    <div class='spoiler-content'>        <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ShowCostFig</span>(<span class="params">epoch, cost</span>):</span></span><br><span class="line">    <span class="comment"># 绘制损失函数在迭代中的变化图像</span></span><br><span class="line">    ax = plt.figure().gca()</span><br><span class="line">    ax.xaxis.set_major_locator(MaxNLocator(integer=<span class="literal">True</span>))  <span class="comment"># 使坐标刻度为整数</span></span><br><span class="line">    plt.plot(<span class="built_in">range</span>(<span class="number">0</span>, epoch), cost[<span class="number">0</span>:], <span class="string">&quot;go-&quot;</span>)</span><br><span class="line">    plt.show()</span><br><span class="line">    print(<span class="string">&quot;Final Cost: &quot;</span> + <span class="built_in">str</span>(cost[epoch - <span class="number">1</span>]))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ShowdwFig</span>(<span class="params">epoch, dw, layerNum</span>):</span></span><br><span class="line">    <span class="comment"># 绘制dw的L2在迭代中的变化图像</span></span><br><span class="line">    camp = getCmap(layerNum)</span><br><span class="line">    ax = plt.figure(figsize=(<span class="number">15</span>, <span class="number">5</span>)).gca()</span><br><span class="line">    ax.xaxis.set_major_locator(MaxNLocator(integer=<span class="literal">True</span>))</span><br><span class="line">    <span class="keyword">for</span> i, data <span class="keyword">in</span> <span class="built_in">enumerate</span>(np.transpose(dw)):</span><br><span class="line">        plt.plot(data[<span class="number">1</span>:], c=camp(i), label=i)</span><br><span class="line">    plt.legend()  <span class="comment"># 加上图例</span></span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getCmap</span>(<span class="params">n, name=<span class="string">&quot;hsv&quot;</span></span>):</span></span><br><span class="line">    <span class="comment"># 从网上copy了一个调色盘来选颜色</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Returns a function that maps each index in 0, 1, ..., n-1 to a distinct</span></span><br><span class="line"><span class="string">    RGB color; the keyword argument name must be a standard mpl colormap name.&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> plt.cm.get_cmap(name, n)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ShowNorm</span>(<span class="params">x</span>):</span></span><br><span class="line">    print(<span class="string">&quot;Norm: &quot;</span> + <span class="built_in">str</span>(np.linalg.norm(x)))</span><br></pre></td></tr></table></figure>    </div></div><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">FCNN.predict(trainX, trainy)</span><br><span class="line">&gt;&gt; correct: <span class="number">0.9768571428571429</span></span><br><span class="line">FCNN.predict(testX, testy)</span><br><span class="line">&gt;&gt; correct: <span class="number">0.9675</span></span><br><span class="line">ShowNorm(FCNN.w[-<span class="number">1</span>])</span><br><span class="line">&gt;&gt; Norm: <span class="number">4.553835257428929</span></span><br><span class="line">ShowCostFig(epoch, FCNN.cost)</span><br><span class="line">&gt;&gt; Final Cost: <span class="number">0.2906157343621473</span></span><br><span class="line">ShowdwFig(epoch, FCNN.dwNorm, FCNN.layerNum)</span><br></pre></td></tr></table></figure><p><img src="/images/FCNN_costFig.png"/></p><p><img src="/images/FCNN_dwFig.png"/></p><p>可见<code>loss</code>的确在以肉眼可见速度收敛，且模型对训练集和测试集的预测准确度相差不大，都保持在96%~97%，对我来说我已经满意了</p><p>最后当然是喜闻乐见的错误样本抽样（只能说相当一部分的错误样本也都不是什么善茬</p><p><img src="/images/FCNN_wrong.png"/></p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;全连接神经网络识别MNIST手写数字集，AI中的HelloWorld&lt;/p&gt;
&lt;p&gt;&lt;img src=&#39;/images/FCNN_cover.png&#39; style=&quot;zoom:60%;&quot; &gt;&lt;/p&gt;</summary>
    
    
    
    <category term="AI" scheme="https://kyriota.github.io/categories/AI/"/>
    
    
  </entry>
  
  <entry>
    <title>西湖论剑GlobalNoise[AI对抗启蒙]</title>
    <link href="https://kyriota.github.io/2021/11/25/%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91GlobalNoise[AI%E5%AF%B9%E6%8A%97%E5%90%AF%E8%92%99]/"/>
    <id>https://kyriota.github.io/2021/11/25/%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91GlobalNoise[AI%E5%AF%B9%E6%8A%97%E5%90%AF%E8%92%99]/</id>
    <published>2021-11-25T20:15:15.000Z</published>
    <updated>2023-01-03T10:53:57.405Z</updated>
    
    <content type="html"><![CDATA[<p>近期CTF赛事里面AI对抗题出现频率逐渐升高，作为一个摸了好久的web人，正好之前自己有一点MachineLearning的基础，准备尝试一下这个领域</p><p><img src="/images/GlobalNoise_Universal.png" /></p><span id="more"></span><!--toc--><h1 id="西湖论剑globalnoiseai对抗启蒙">西湖论剑GlobalNoise[AI对抗启蒙]</h1><blockquote><p>2022/01/16补充：发了关于<a href="https://kyriota.com/2022/01/16/%E5%85%A8%E8%BF%9E%E6%8E%A5%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%5B%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%85%A5%E9%97%A8%5D/">全连接神经网络的文章</a>，手搓了一个模型之后再回来看这篇文章，发现很多地方有失偏颇(已基本修正)，正好之前有一个想把样本偏移到任意class的心愿没有完成，后续会发一篇新的文章<a href="https://kyriota.com/2022/04/09/L2-TargetedAttack/">L2 Targeted Attack</a></p></blockquote><p>本文面向的对象是：</p><ul><li>对Machine Learning原理有基本理解的入门AI人</li><li>吃瓜群众</li></ul><p>（机器学习相关的前置知识建议看吴恩达，然后不调库自己写一点逻辑回归线性回归的基本分类器就够了（至少我在这之前只有这点基础，技术拉跨</p><h2 id="题面">题面</h2><p>概述：提供了一个训练好的model和一份mnist数据集，要求对其中的100份样本进行一个全局性的较小扰动（对向量的范数进行了限制），使得这100份样本的分类正确率低于5%</p><p><a href="https://kyriota.com/html/DLfiles/%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91GlobalNoise.zip">下载链接(github源)</a></p><p>（当时没有存题面啊啊啊啊啊啊啊啊啊啊啊啊啊，并非完整原题，只是我根据回忆和需要的条件摸出来的一个题面</p><h3 id="题面分析">题面分析</h3><p>将题目给出的100个样本过一个目会发现是7和9的______</p><p><img src="/images/GlobalNoise_testSet.png" /></p><p>如果你想的是<code>二分类</code>就不对了，因为是不可能用一个噪音把大部分7糊弄到9，然后把大部分9糊弄到7的（好多人一看到都说二分类</p><p>本来就是有10个类别（0~9），可以把9和7糊弄到除他俩之外的classes里面去</p><p>此题很明显关键在于Global，也正是这个Global和对范数的限制拦住了很多像我这样的半吊子</p><blockquote><p>之前做过0CTF-final中的boyNextDoor，是一个人脸识别的题，正解是用梯度构造noise，还需要Expectation over Transformation (EOT) 来绕过dlib的随机抖动，当时属于是无知者无畏，直接随机改单像素给爆出来了，由于只需要构造人脸图片，所以对于噪音范数和噪音鲁棒性没有要求，只要他能识别到人脸位置即可</p></blockquote><p>但是当我想在这个题用半吊子手法去解决时，很明显在范数限制和鲁棒性上都遇到了问题，通常来说根据梯度构造的噪音会比满足条件的随机噪音的L2(模长)小10~100倍，而且随机噪音只对单个样本有效，完全不能Global</p><p>所以人还是要进步，必须得理解正确构造噪音的方法</p><h2 id="一篇论文">一篇论文</h2><p>这是一篇关于全局扰动的<a href="https://arxiv.org/pdf/2005.08087.pdf">文章</a>，看一半大概就能知道构造全局扰动的原理了，其实也qs不难理解，但可能需要先理解模型的训练过程以及使用</p><ul><li><p>在训练时：这个过程就是在寻找一个最优的 线性(or非线性?) 的变换，使得输入经过这个变换之后能够落到集中的位置，这样就可以根据一些界限(决策边界)来划分的输入，并给出模型的判断</p></li><li><p>在预测时：输入/数据 交给一个训练好的模型进行分类的时候，其实也就进行了一个映射，比如此处的mnist数据集是28x28的图片，那其实就是长度为784的向量，假设模型是f，那么f(x)就是模型对输入向量x预测的分类结果</p></li><li><p>决策边界：其实在我个人目前的理解中，决策边界其实就指</p><blockquote><p>在这个闭合的边界内，所有落到这里面的f(x)都会被判定为某一个分类</p></blockquote></li></ul><p>所以现在再回来看文中的图：</p><p><img src="/images/GlobalNoise_Universal.png" /></p><p>他描述的其实就是：</p><ul><li>在红，绿，蓝，三个决策边界内（二维扁平化的决策空间只是为了方便演示和理解），分别有三个样点x1（在红色边界内）,x2（在蓝色边界内）,x3（在绿色边界内）</li><li>为了视觉上方便理解，把这三个点以及他们的决策空间重合在一起（原本的决策空间绝一般不会相互重叠，因为边界上正是预测结果改变的临界处，如果有一个模型存在太多这样的临界状况，那说明他这个模型不太行）</li><li>对f(x)，使用梯度下降法，获取一个noise，这个noise可以最快地把x1送到红色的决策边界</li><li>这时noise可以把原本落在红色这个决策空间内的x1直接送出到决策边界外去，但对于蓝色决策空间中的x2来说则不太好说</li><li>故重复这一过程，但x2应更新为<code>x2+=noise</code>，对更新后的x2求一个能把x2送出决策边界的新noise，然后更新noise为<code>noise+=newNoise</code></li><li>对x3重复这一过程</li></ul><p>这样就获得了一个可以把x1,x2,x3都送出决策空间的噪音（当然，求这个噪音的顺序会对最终得到的噪音有影响</p><h2 id="具体实现">具体实现</h2><h3 id="预处理">预处理</h3><p>原题中的预处理是调库直接用的接口，我因为没有接触过太多机器学习的库所以手动处理</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">from</span> numpy.linalg <span class="keyword">import</span> norm</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> copy</span><br><span class="line"></span><br><span class="line">model = keras.models.load_model(<span class="string">&#x27;model.h5&#x27;</span>)</span><br><span class="line">test_set=[<span class="number">13616</span>, <span class="number">4189</span>, <span class="number">3206</span>, <span class="number">51839</span>, <span class="number">30560</span>, <span class="number">45045</span>, <span class="number">51389</span>, <span class="number">59638</span>, <span class="number">10487</span>, <span class="number">5233</span>, <span class="number">48889</span>, <span class="number">10247</span>, <span class="number">1547</span>, <span class="number">3914</span>, <span class="number">44182</span>, <span class="number">9990</span>, <span class="number">26276</span>, <span class="number">28283</span>, <span class="number">52481</span>, <span class="number">46609</span>, <span class="number">18757</span>, <span class="number">26954</span>, <span class="number">26322</span>, <span class="number">18819</span>, <span class="number">29463</span>, <span class="number">34489</span>, <span class="number">51248</span>, <span class="number">53986</span>, <span class="number">25872</span>, <span class="number">42855</span>, <span class="number">49719</span>, <span class="number">31176</span>, <span class="number">38292</span>, <span class="number">48100</span>, <span class="number">52763</span>, <span class="number">3904</span>, <span class="number">46879</span>, <span class="number">9810</span>, <span class="number">51583</span>, <span class="number">39760</span>, <span class="number">21245</span>, <span class="number">13728</span>, <span class="number">33834</span>, <span class="number">23968</span>, <span class="number">28781</span>, <span class="number">33134</span>, <span class="number">35277</span>, <span class="number">18562</span>, <span class="number">21344</span>, <span class="number">8396</span>, <span class="number">36000</span>, <span class="number">43427</span>, <span class="number">24477</span>, <span class="number">36678</span>, <span class="number">56218</span>, <span class="number">32945</span>, <span class="number">17707</span>, <span class="number">36763</span>, <span class="number">611</span>, <span class="number">12668</span>, <span class="number">31312</span>, <span class="number">28053</span>, <span class="number">35696</span>, <span class="number">9876</span>, <span class="number">33329</span>, <span class="number">56107</span>, <span class="number">19929</span>, <span class="number">35636</span>, <span class="number">21704</span>, <span class="number">35807</span>, <span class="number">28645</span>, <span class="number">16522</span>, <span class="number">15192</span>, <span class="number">43890</span>, <span class="number">14710</span>, <span class="number">11805</span>, <span class="number">4754</span>, <span class="number">33660</span>, <span class="number">13270</span>, <span class="number">25465</span>, <span class="number">20267</span>, <span class="number">4141</span>, <span class="number">40391</span>, <span class="number">14287</span>, <span class="number">15545</span>, <span class="number">56458</span>, <span class="number">6121</span>, <span class="number">19663</span>, <span class="number">15709</span>, <span class="number">52825</span>, <span class="number">25933</span>, <span class="number">4091</span>, <span class="number">17861</span>, <span class="number">37773</span>, <span class="number">22450</span>, <span class="number">8669</span>, <span class="number">4447</span>, <span class="number">22022</span>, <span class="number">40046</span>, <span class="number">32738</span>]</span><br><span class="line"></span><br><span class="line">dict_mnist = np.load(<span class="string">&quot;mnist.npz&quot;</span>)</span><br><span class="line">x_train = dict_mnist[<span class="string">&quot;x_train&quot;</span>]</span><br><span class="line">y_train = dict_mnist[<span class="string">&quot;y_train&quot;</span>]</span><br><span class="line">x_test = dict_mnist[<span class="string">&quot;x_test&quot;</span>]</span><br><span class="line">y_test = dict_mnist[<span class="string">&quot;y_test&quot;</span>]</span><br><span class="line">dict_mnist.close()</span><br><span class="line">x_train = np.expand_dims(x_train, axis=<span class="number">3</span>)</span><br><span class="line">x_test = np.expand_dims(x_test, axis=<span class="number">3</span>)</span><br><span class="line"><span class="comment">#normalize</span></span><br><span class="line">x_train = (x_train-np.amin(x_train))/(np.amax(x_train)-np.amin(x_train))</span><br><span class="line">x_test = (x_test-np.amin(x_test))/(np.amax(x_test)-np.amin(x_test))</span><br><span class="line"></span><br><span class="line">test_x_data = []</span><br><span class="line">test_y_data = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> test_set:</span><br><span class="line">    test_x_data.append(x_train[i])</span><br><span class="line">    test_y_data.append(y_train[i])</span><br><span class="line">test_x_data = np.array(test_x_data)</span><br><span class="line">test_y_data = np.array(test_y_data)</span><br><span class="line">test_x_data = test_x_data.astype(np.float32)</span><br><span class="line">test_y_data = test_y_data.astype(np.float32)</span><br></pre></td></tr></table></figure><h3 id="获取梯度">获取梯度</h3><p>tensorFlow提供了很好用的求梯度的接口gradientTape</p><blockquote><p>2022/01/16补充：这个函数在之前写wp的时候对我来说就是一个黑盒，对模型求输入的梯度的具体原理请看开头提到的新文章</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">GetGrad</span>(<span class="params">data, classIndex=-<span class="number">1</span></span>):</span></span><br><span class="line">    <span class="comment"># 当 classIndex=-1，求样本到最近的最高概率点</span></span><br><span class="line">    <span class="comment"># 当指定 classIndex，求样本到指定的classIndex的最高概率点</span></span><br><span class="line">    data = data.reshape(-<span class="number">1</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>)</span><br><span class="line">    data = tf.cast(data, tf.float32)</span><br><span class="line">    <span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> tape:</span><br><span class="line">        tape.watch(data)</span><br><span class="line">        pred = model(data)</span><br><span class="line">        <span class="comment"># loss = tf.norm(pred, axis=1, ord=2) if classIndex == -1 else pred[0][classIndex]  # 有的文章把loss这么写</span></span><br><span class="line">        loss = pred[<span class="number">0</span>][np.argmax(pred)] <span class="keyword">if</span> classIndex == -<span class="number">1</span> <span class="keyword">else</span> pred[<span class="number">0</span>][classIndex]</span><br><span class="line">    grad = tape.gradient(loss, data)</span><br><span class="line">    <span class="keyword">return</span> np.array(grad).reshape(<span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>)</span><br></pre></td></tr></table></figure><h3 id="梯度下降求noise">梯度下降求noise</h3><p>这样求出来的noise通常能以最小的Lp将样本送出去</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">GradDes</span>(<span class="params">data, foolRate=<span class="number">0.5</span>, step=<span class="number">0.05</span>, maxIteration=<span class="number">500</span></span>):</span></span><br><span class="line">    minGrad = <span class="number">0.05</span></span><br><span class="line">    arg = np.argmax(model.predict(data.reshape(-<span class="number">1</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>)))</span><br><span class="line">    print(<span class="string">&quot;GetPert init arg : &quot;</span> + <span class="built_in">str</span>(arg))</span><br><span class="line">    cnt = <span class="number">0</span></span><br><span class="line">    pert = copy.deepcopy(data / np.inf)</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        cnt += <span class="number">1</span></span><br><span class="line">        rate = model.predict(data.reshape(-<span class="number">1</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>) + pert.reshape(-<span class="number">1</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>))[<span class="number">0</span>][arg]</span><br><span class="line">        print(<span class="string">&quot;\r - Iteration : &quot;</span> + <span class="built_in">str</span>(cnt) + <span class="string">&quot; rate : &quot;</span> + <span class="built_in">str</span>(rate), end = <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> rate &lt; foolRate <span class="keyword">or</span> cnt == maxIteration:</span><br><span class="line">            result = model.predict(data.reshape(-<span class="number">1</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>) + pert.reshape(-<span class="number">1</span>, <span class="number">28</span>, <span class="number">28</span>, <span class="number">1</span>))[<span class="number">0</span>]</span><br><span class="line">            print(<span class="string">&quot;\n - pert result : &quot;</span> + <span class="built_in">str</span>(np.argmax(result)) + <span class="string">&quot; : &quot;</span> + <span class="built_in">str</span>(np.amax(result)))</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        grad = GetGrad(data + pert)</span><br><span class="line">        <span class="keyword">if</span> norm(grad) &lt; minGrad:</span><br><span class="line">            <span class="keyword">if</span> norm(grad) == <span class="number">0</span>:</span><br><span class="line">                print(<span class="string">&quot;ERROR: grad is zero&quot;</span>)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            grad *= minGrad / norm(grad)</span><br><span class="line">        pert -= grad * step</span><br><span class="line">    <span class="keyword">return</span> pert</span><br></pre></td></tr></table></figure><h3 id="main">main</h3><p>这样求算数平均值得方法其实有点问题，如果恰好有一个反向的pert，那不直接抵消哩，而且我用到的样本里面还有一个是一开始本来就被分类器误分类的点= =...但是既然他得出的结果能用那就先在这里这么写，然后再讨论更好的解法嗯</p><p>用了算数平均值来求noise，没有鲁棒性</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">pert = [<span class="number">0</span>] * <span class="number">6</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    pert[i] = GradDes(test_x_data[np.argwhere(test_y_data == <span class="number">9</span>)[i][<span class="number">0</span>]])</span><br><span class="line">    pert[i] = GradDes(test_x_data[np.argwhere(test_y_data == <span class="number">7</span>)[i][<span class="number">0</span>]] + pert[i])</span><br><span class="line">    pert[<span class="number">2</span> * i] = GradDes(test_x_data[np.argwhere(test_y_data == <span class="number">7</span>)[<span class="number">2</span> * i][<span class="number">0</span>]])</span><br><span class="line">    pert[<span class="number">2</span> * i] = GradDes(test_x_data[np.argwhere(test_y_data == <span class="number">9</span>)[<span class="number">2</span> * i][<span class="number">0</span>]] + pert[<span class="number">2</span> * i])</span><br><span class="line">pertSum = pert[<span class="number">0</span>]/np.inf</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> pert:</span><br><span class="line">    pertSum += i</span><br><span class="line">pert = pertSum/<span class="number">6</span></span><br></pre></td></tr></table></figure><h2 id="exp优化">exp优化</h2><p>目前的想法是：最好是通过求：将样本带到不同的边界的noise（比如本题中就是求出将样本7和9带到0,1,2,3,4,5,6,8的noise），然后比较这些noise在方向与范数上的差异，获得最优的noise，此时求算数平均值就没有问题了</p><p>但是在具体实现上遇到的问题就是：在梯度上升求noise的时候会陷入局部最优解（尤其是相差较大的class），然后目前虽然知道什么退🔥遗传🐜群啥的，但还没有想过具体到底怎么弄</p><p>但：对每一个class都求一个noise，使得某一特定样本偏移到其他的任意class是可行的</p><blockquote><p>2022.4.6补充：根据最近了解的信息看来，陷入局部最优解大部分原因是因为卡在鞍点，换成更好的优化算法，如Adam等即可</p></blockquote><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;近期CTF赛事里面AI对抗题出现频率逐渐升高，作为一个摸了好久的web人，正好之前自己有一点MachineLearning的基础，准备尝试一下这个领域&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/GlobalNoise_Universal.png&quot; /&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="AI" scheme="https://kyriota.github.io/categories/AI/"/>
    
    
    <category term="CTF" scheme="https://kyriota.github.io/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>Uncertainty download available!</title>
    <link href="https://kyriota.github.io/2021/11/17/Uncertainty/"/>
    <id>https://kyriota.github.io/2021/11/17/Uncertainty/</id>
    <published>2021-11-18T01:00:00.000Z</published>
    <updated>2024-07-18T15:00:19.351Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to BadAperture.Inc！</p><p><img src='/images/Uncertainty_cover.png' style="zoom:30%;" ></p><span id="more"></span><h1 id="uncertainty">Uncertainty</h1><p>Uncertainty是一款由BadAperture团队在2021NovaGameJam期间制作的活动作品，其中主要包含了解谜、跑酷两种类型的关卡，更多有关信息请您查看游戏主菜单中的ABOUT选卡！</p><blockquote><p>提取链接：https://pan.baidu.com/s/1DVbqyM2yhpzKt932_LWzEw 提取码：BDAP</p><h2 id="后记">后记</h2><p>本作品中仍然存在诸多不足，还请玩家多多包容，另外，由于时间不足，本作不包含任何音频，文字部分还请您耐心阅读，其中也许包含了线索及提示，我们对所有未处理得当的地方深表歉意</p></blockquote><p>这是我们第一次尝试游戏制作（之前是一块整音乐的😂），之所以会在jam中选择制作一个小体量的3D游戏，主要还是因为相对于正常的gameJam，他们给的时间实在是太多了（貌似是三个周，然后在线下展示了之后又继续完善了一个周），于是我们就做出了这么一个大胆的尝试</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;Welcome to BadAperture.Inc！&lt;/p&gt;
&lt;p&gt;&lt;img src=&#39;/images/Uncertainty_cover.png&#39; style=&quot;zoom:30%;&quot; &gt;&lt;/p&gt;</summary>
    
    
    
    <category term="GameDev" scheme="https://kyriota.github.io/categories/GameDev/"/>
    
    
  </entry>
  
  <entry>
    <title>Ant Colony Optimization</title>
    <link href="https://kyriota.github.io/2021/10/02/AntColonyOptimization/"/>
    <id>https://kyriota.github.io/2021/10/02/AntColonyOptimization/</id>
    <published>2021-10-02T18:48:47.000Z</published>
    <updated>2024-07-18T15:00:39.393Z</updated>
    
    <content type="html"><![CDATA[<p>蚁群优化算法，最直观的一个示例用途是解决TPS（TravelingSealsmanProblem）问题，快速寻找一个可观的解，但需要你调的一手好参</p><p><img src="/images/ACO_cover.gif" /></p><span id="more"></span><h1 id="ant-colony-optimization">Ant Colony Optimization</h1><blockquote><p>蚁群，退火，遗传，三大知名优化算法</p></blockquote><p>蚁群优化算法，启发在于蚂蚁觅食的行为，即一段时间后蚂蚁能够找到最短的路径到达食物的位置；核心在于对信息素的抽象，蚁群算法之所以能够快速优化，得益于算法已有的成果积累；一句话说明白这个算法，那就是在当前解的基础上去寻找更优解</p><p>本文记录一下写这个算法的unity可视化实现过程，但是我还是建议您自己写一遍👼，体验一下令人非常不愉快的调参体验💢；如果你决定自己写一遍，可以在看了<strong>Getting Start</strong>理解算法原理后直接开干，卡住了再来参考一下后面的具体实现方法</p><p>之后应该会先去学一些shader相关的内容，以免在很多之后想做的工程中受限</p><h2 id="getting-start">Getting Start</h2><h3 id="tps">TPS</h3><p>旅行商人问题，一句话概括就是</p><blockquote><p>找到一条长度最短的线串联起一些固定的点</p></blockquote><p>假设有n个点，那么将有<code>(n-1)!/2</code>种可能性，如果遍历所有结果，n稍微一大即刻计算不可行，蚁群优化算法的作用就是在短时间内找到一个可观的解，虽然这个解不一定会是最优解，但相对于算法消耗的算力与时间来说性价比高</p><h3 id="process-outline">Process Outline</h3><p>在用蚁群算法解决TPS问题时，有两个可以提高效率与结果质量的关键，这两个点不易被注意，所以放在开头就说</p><ul><li>一、将蚂蚁按组统计：不要在一只蚂蚁结束搜索后立即更新数据，而需要等待一定数量的蚂蚁均完成后再更新</li><li>二、随机选取每只蚂蚁的起点：多数时候，蚂蚁多绕的路程均来自于蚂蚁在走到最后一个点后返回起点的那一条线段，如果设定每只蚂蚁的起点不变，效率会更低，多次迭代后的结果也还是会产生交叉路线（which is obviously not the best solution）</li></ul><p>接下来是每轮（每组）蚂蚁跑的基本流程：</p><ul><li>随机选择起点</li><li>用距离与信息素对路径<strong>加权后随机</strong>选择路径</li><li>更新已选择过的点，使得蚂蚁不会返回已经到达过的点</li><li>当走到最后一个点，直接返回起点</li></ul><p>一组蚂蚁跑完后更新信息素矩阵</p><ul><li>对信息素矩阵乘以一个系数使得已有的信息素浓度降低<ul><li>根据当前组蚂蚁跑出来的路径距离长度，当前组中路径最短的蚂蚁留下的信息素最多，而路径长的蚂蚁信息素少</li></ul></li></ul><h2 id="process">Process</h2><p>由于算法的计算过程比较零碎，就不拿出来说了，在最后会直接放源码，这里主要解释一些参数</p><h3 id="misc">Misc</h3><p>点之间距离的表格是对称的</p><p><img src="/images/ACO_disGraph.png" style="zoom:50%;" /></p><p>为了只读写一个数据，只取表格的一半就够了，然后写一个函数，输入两个点的index，返回对应的表格中路径的index</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">int</span> <span class="title">GetPathIndex</span>(<span class="params"><span class="built_in">int</span> cur1, <span class="built_in">int</span> cur2</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (cur1 &gt; cur2)&#123;</span><br><span class="line">        <span class="built_in">int</span> tmp = cur1;</span><br><span class="line">        cur1 = cur2;</span><br><span class="line">        cur2 = tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">int</span> index = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">1</span>; i &lt;= cur1; i++) index += numPoint - i;</span><br><span class="line">    index += cur2 - cur1 - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> index;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="parameters">Parameters</h3><p>首先需要介绍一下算法涉及的几个重要参数</p><h4 id="ant">Ant</h4><p><code>numInGroup</code>规定了一个轮次中的蚂蚁数量，一轮中的蚂蚁越少迭代频率越高，蚂蚁越多则在更短路径上留下信息素的可能越大</p><blockquote><p>Recommended Value：5~25</p></blockquote><h4 id="pheromone">Pheromone</h4><p>信息素的必要参数有：<strong>最小值，初始值，增量，衰减值</strong>，但我为了防止浓度过高也弄了个最大值</p><ul><li><p><code>deltaPhe</code>规定了信息素在迭代时的增量</p><blockquote><p>Recommended Value：10</p></blockquote></li><li><p><code>pheMaxTimes</code>调整了信息素最大值，由于我的最大值还决定了初始值以及后面的一些对算法的tweak，所以我给出的推荐值会比较大，以制造出不同路径的差异性，从而更快地收敛到可观的解</p><blockquote><p>Recommended Value：25~50</p></blockquote></li><li><p><code>pheDrop</code>即每次寻路后衰减的百分比</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pheromone[i] *= pheDrop;</span><br><span class="line"><span class="keyword">if</span> (pheromone[i] &lt; pheMin) pheromone[i] = pheMin;</span><br></pre></td></tr></table></figure></p><blockquote><p>Recommended Value：0.7</p></blockquote></li><li><p>其他几个参数为了省事就通过自己估计的公式计算了嗯</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// calculate pheromone parameters</span></span><br><span class="line">pheMax = deltaPhe * pheMaxTimes * numInGroup;</span><br><span class="line">pheInit = pheMax / <span class="number">5</span>;</span><br><span class="line">pheMin = pheInit * Mathf.Pow(pheDrop, <span class="number">5</span>);</span><br></pre></td></tr></table></figure></p></li><li><p><code>emphasizeBest</code>因为在一轮迭代中可能存在没有蚂蚁按照当前最优解行进的情况，从而导致目前已发现的最优解路径上的信息素浓度降低，故需要在每轮迭代中都假设有一定数量的蚂蚁经过了最优路线，此参数就是用于规定这一具体数量</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pheromone[i] += deltaPhe * emphasizeBest;</span><br></pre></td></tr></table></figure></p><blockquote><p>Recommended Value：15</p></blockquote></li></ul><h4 id="weight">Weight</h4><p>蚁群优化算法的核心就在于调整每条路径的概率，而概率由下面的公式决定</p><p><img src="/images/ACO_formula.png" alt="ACO_formula" style="zoom: 50%;" /></p><p>其中，<code>τ</code>=信息素浓度，<code>η</code>=1/距离，<code>α</code>与<code>β</code>就是俩权重的参数</p><p><code>phePow</code>，即<code>α</code></p><blockquote><p>Recommended Value：3.5</p></blockquote><p><code>disPow</code>即<code>β</code></p><blockquote><p>Recommended Value：6</p></blockquote><h2 id="improvement">Improvement</h2><h3 id="crossing">Crossing</h3><p>最开始说过：交叉的路线明显不是最优解</p><p><img src="/images/ACO_crossing.png" /></p><p>但通常在最开始的时候都会出现交叉路线，而且随着点的数目增加，交叉路线被消除的概率逐步减小。所以我每次在看到最优解的图中一直有一个交叉线半天不动时就会非常难受，所以我们需要考虑如何让蚂蚁自动规避交叉</p><ul><li>在寻路时规避交叉：降低选择到 会与已选择的路径交叉的 点的概率</li><li>在强调最优解时规避交叉：不强调交叉路线的最优解，甚至反手将交叉的路线的信息素浓度降低，并提升不交叉线路的信息素浓度来引导</li></ul><p>应用以上两条，可以使路径快速迭代至一个没有交叉的状态</p><h3 id="small-angle">Small Angle</h3><p>路线中出现极小的角度也是非常不理想的一件事</p><p><img src="/images/ACO_smallAngle.png" alt="ACO_smallAngle" style="zoom:67%;" /></p><p>说实话目前没有什么很好的解决方案，目前我做的也仅仅是不去强化这个路径之类的，但不能快速处理掉这种情形</p><h3 id="annealing">Annealing</h3><p>调参时会发现许多参数需要动态调整，如初期应增加信息素浓度的权重，而后期却需要信息素与距离的平衡下进行优化，否则就容易自闭收敛，停滞不前；再比如初期应使信息素衰减速率较大，以促进收敛，而后期却需要额外的信息素来减少自闭的概率</p><p>这些需求与另一优化算法：<strong>模拟退火算法</strong> 相呼应，在此我没有进行这已实现，但若将这两个算法相结合，则肯定能够使得效率与结果质量都得到提升</p><h2 id="source-code">Source Code</h2><div class='spoiler collapsed'>    <div class='spoiler-title'>        Main    </div>    <div class='spoiler-content'>        <figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AntColonyOpt</span> : <span class="title">MonoBehaviour</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// min angle that the min path can have</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> angleOffset;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> crossTweakForce;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> crossDiv;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">bool</span> bParticle;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">bool</span> bDrawLine;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> emphasizeBest;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> numInGroup;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> preTimes;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> disPow;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> weightPow;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> dropPow;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> deltaPhe;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> pheMaxTimes;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> pheDrop;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> numPoint;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> xRange;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> yRange;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> minDis;</span><br><span class="line">    <span class="keyword">public</span> GameObject pointPrefab;</span><br><span class="line">    <span class="keyword">public</span> GameObject particalPrefab;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> preTimeLeft;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> Vector2[] pPos;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span>[] dis;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span>[] pheromone;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> pheMax;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> numPath;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> minPathDis;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span>[] minPath;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">bool</span> bSpace;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">bool</span> bAuto;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">bool</span> bFirstRound;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> pheInit;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> pheMin;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> NowPath nowPath;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> minCrossCnt;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> CrossPath crossPath;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> LineRenderer lineRenderer;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Particle[] particles;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> turnCnt;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="built_in">int</span> <span class="title">C2</span>(<span class="params"><span class="built_in">int</span> n</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="built_in">int</span> result = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = n - <span class="number">1</span>; i &gt; <span class="number">0</span>; i --) result += i;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// passing the index of two point</span></span><br><span class="line">    <span class="comment">// return the index of the path connecting them</span></span><br><span class="line">    <span class="function"><span class="built_in">int</span> <span class="title">GetPathIndex</span>(<span class="params"><span class="built_in">int</span> cur1, <span class="built_in">int</span> cur2</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="keyword">if</span> (cur1 &gt; cur2)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">int</span> tmp = cur1;</span><br><span class="line">            cur1 = cur2;</span><br><span class="line">            cur2 = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">int</span> index = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">1</span>; i &lt;= cur1; i++) index += numPoint - i;</span><br><span class="line">        index += cur2 - cur1 - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> index;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Awake</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        nowPath = FindObjectOfType&lt;NowPath&gt;();</span><br><span class="line">        crossPath = FindObjectOfType&lt;CrossPath&gt;();</span><br><span class="line">        <span class="comment">//numPath = (int)C(numPoint, 2);</span></span><br><span class="line">        numPath = C2(numPoint);</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPath &amp;&amp; bParticle; i ++)</span><br><span class="line">            Instantiate(particalPrefab);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Start</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        turnCnt = <span class="number">0</span>;</span><br><span class="line">        preTimeLeft = <span class="number">0</span>;</span><br><span class="line">        bAuto = <span class="literal">false</span>;</span><br><span class="line">        bFirstRound = <span class="literal">true</span>;</span><br><span class="line">        pPos = <span class="keyword">new</span> Vector2[numPoint];</span><br><span class="line">        minPath = <span class="keyword">new</span> <span class="built_in">int</span>[numPoint];</span><br><span class="line">        <span class="built_in">float</span> sqrMinDis = minDis * minDis;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// gen random points</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            minPath[i] = i;</span><br><span class="line"></span><br><span class="line">            Vector2 pos = <span class="keyword">new</span> Vector2(Random.Range(-xRange, xRange), Random.Range(-yRange, yRange));</span><br><span class="line">            <span class="built_in">bool</span> tooClose = <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> j = <span class="number">0</span>; j &lt; i; j++)</span><br><span class="line">                <span class="keyword">if</span> ((pos - pPos[j]).sqrMagnitude &lt; sqrMinDis)</span><br><span class="line">                &#123;</span><br><span class="line">                    i--;</span><br><span class="line">                    tooClose = <span class="literal">true</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            <span class="keyword">if</span> (tooClose) <span class="keyword">continue</span>;</span><br><span class="line">            GameObject point = Instantiate(pointPrefab);</span><br><span class="line">            point.transform.position = pos;</span><br><span class="line">            point.name = i.ToString();</span><br><span class="line">            pPos[i] = pos;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// calculate pheromone parameters</span></span><br><span class="line">        pheMax = deltaPhe * pheMaxTimes * numInGroup;</span><br><span class="line">        pheInit = pheMax / <span class="number">5</span>;</span><br><span class="line">        pheMin = pheInit * Mathf.Pow(pheDrop, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// initialization of arrays</span></span><br><span class="line">        pheromone = <span class="keyword">new</span> <span class="built_in">float</span>[numPath];</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPath; i++)</span><br><span class="line">            pheromone[i] = pheInit;</span><br><span class="line">        dis = <span class="keyword">new</span> <span class="built_in">float</span>[numPath];</span><br><span class="line">        particles = FindObjectsOfType&lt;Particle&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// calculate distance</span></span><br><span class="line">        <span class="comment">// set rotation and length of particles</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint - <span class="number">1</span>; i++)</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> j = i + <span class="number">1</span>; j &lt; numPoint; j++)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">int</span> nowIndex = GetPathIndex(i, j);</span><br><span class="line">                dis[nowIndex] = (pPos[j] - pPos[i]).magnitude;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span>(bParticle)</span><br><span class="line">                &#123;</span><br><span class="line">                    particles[nowIndex].transform.position = pPos[i];</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">float</span> angle = Vector2.Angle(Vector2.up, pPos[j] - pPos[i]);</span><br><span class="line">                    <span class="keyword">if</span> (Vector3.Cross(Vector2.up, pPos[j] - pPos[i]).z &gt; <span class="number">0</span>) angle = -angle;</span><br><span class="line">                    particles[nowIndex].SetParticleRot(angle);</span><br><span class="line"></span><br><span class="line">                    particles[nowIndex].SetParticleDis(dis[nowIndex]);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// initialize line renderer</span></span><br><span class="line">        lineRenderer = GetComponent&lt;LineRenderer&gt;();</span><br><span class="line">        lineRenderer.positionCount = numPoint + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Update</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        bSpace = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (Input.GetKeyDown(KeyCode.W))</span><br><span class="line">        &#123;</span><br><span class="line">            preTimeLeft = preTimes;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (preTimeLeft &gt; <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            preTimeLeft--;</span><br><span class="line">            Opt();</span><br><span class="line">            <span class="keyword">if</span> (preTimeLeft == <span class="number">0</span>) print(<span class="string">&quot;preTimeFinished&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (Input.GetKeyDown(KeyCode.Space))</span><br><span class="line">        &#123;</span><br><span class="line">            bSpace = <span class="literal">true</span>;</span><br><span class="line">            Opt();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (Input.GetKeyDown(KeyCode.S))</span><br><span class="line">        &#123;</span><br><span class="line">            GameObject[] points = GameObject.FindGameObjectsWithTag(<span class="string">&quot;Point&quot;</span>);</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">            &#123;</span><br><span class="line">                Destroy(points[i]);</span><br><span class="line">                nowPath.ClearPath();</span><br><span class="line">            &#125;</span><br><span class="line">            Start();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (Input.GetKeyDown(KeyCode.A))</span><br><span class="line">        &#123;</span><br><span class="line">            print(<span class="string">&quot;AUTO&quot;</span>);</span><br><span class="line">            bAuto = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (Input.GetKeyDown(KeyCode.D))</span><br><span class="line">        &#123;</span><br><span class="line">            print(<span class="string">&quot;MANUAL&quot;</span>);</span><br><span class="line">            bAuto = <span class="literal">false</span>;</span><br><span class="line">            print(turnCnt);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (bAuto) Opt();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Opt</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        turnCnt++;</span><br><span class="line">        <span class="built_in">int</span>[] path = <span class="keyword">new</span> <span class="built_in">int</span>[numPoint * numInGroup];</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> antCnt = <span class="number">0</span>; antCnt &lt; numInGroup; antCnt++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// offset of starting point index</span></span><br><span class="line">            <span class="built_in">int</span> offset = Random.Range(<span class="number">0</span>, numPoint);</span><br><span class="line">            <span class="built_in">int</span> nowP = offset;</span><br><span class="line">            <span class="built_in">int</span> numVisited = <span class="number">0</span>;</span><br><span class="line">            <span class="built_in">bool</span>[] visited = <span class="keyword">new</span> <span class="built_in">bool</span>[numPoint];</span><br><span class="line">            visited[offset] = <span class="literal">true</span>;</span><br><span class="line">            path[numPoint * antCnt] = offset;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> turn = <span class="number">0</span>; turn &lt; numPoint - <span class="number">1</span>; turn++)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">float</span>[] tempDis = <span class="keyword">new</span> <span class="built_in">float</span>[numPoint];</span><br><span class="line">                <span class="built_in">float</span>[] tempPhe = <span class="keyword">new</span> <span class="built_in">float</span>[numPoint];</span><br><span class="line">                <span class="built_in">float</span>[] weight = <span class="keyword">new</span> <span class="built_in">float</span>[numPoint];</span><br><span class="line"></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    <span class="keyword">if</span> (!visited[i])</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="built_in">int</span> nowIndex = GetPathIndex(nowP, i);</span><br><span class="line">                        <span class="built_in">float</span> nowDis = Mathf.Pow(<span class="number">1f</span> / dis[nowIndex], disPow);</span><br><span class="line">                        <span class="comment">// weight will be diveded by total then</span></span><br><span class="line">                        tempPhe[i] = pheromone[nowIndex];</span><br><span class="line">                        tempDis[i] = nowDis;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// calculate weight</span></span><br><span class="line">                <span class="built_in">float</span> totalWeight = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    <span class="keyword">if</span> (!visited[i])</span><br><span class="line">                    &#123;</span><br><span class="line">                        weight[i] = Mathf.Pow(tempDis[i] * tempPhe[i], weightPow);</span><br><span class="line">                        <span class="comment">// reduce chance of getting cross</span></span><br><span class="line">                        <span class="keyword">for</span> (<span class="built_in">int</span> j = <span class="number">0</span>; j &lt; numVisited - <span class="number">1</span>; j ++)</span><br><span class="line">                            <span class="keyword">if</span>(DetectCross(pPos[i], pPos[nowP], pPos[path[j + numPoint * antCnt]], pPos[path[j + <span class="number">1</span> + numPoint * antCnt]]))</span><br><span class="line">                                weight[i] /= crossDiv;</span><br><span class="line"></span><br><span class="line">                        totalWeight += weight[i];</span><br><span class="line">                    &#125;</span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    <span class="keyword">if</span> (!visited[i])</span><br><span class="line">                        weight[i] /= totalWeight;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// Random.value returns in range [0,1]</span></span><br><span class="line">                <span class="built_in">float</span> choice = Random.<span class="keyword">value</span>;</span><br><span class="line"></span><br><span class="line">                <span class="built_in">int</span> decideIndex = offset;</span><br><span class="line">                <span class="built_in">int</span> maxCnt = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">while</span> (maxCnt &lt; <span class="number">2</span> * numPoint)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">int</span> judgingIndex = decideIndex % numPoint;</span><br><span class="line">                    choice -= weight[judgingIndex];</span><br><span class="line">                    <span class="keyword">if</span> (choice &lt;= <span class="number">0</span> &amp;&amp; !visited[judgingIndex])</span><br><span class="line">                    &#123;</span><br><span class="line">                        numVisited++;</span><br><span class="line">                        path[numVisited + numPoint * antCnt] = judgingIndex;</span><br><span class="line">                        nowP = judgingIndex;</span><br><span class="line">                        visited[judgingIndex] = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    decideIndex++;</span><br><span class="line">                    maxCnt++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// update when a turn is ending</span></span><br><span class="line">            <span class="keyword">if</span> (antCnt == numInGroup - <span class="number">1</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">float</span> localMinPathDis = <span class="built_in">float</span>.MaxValue;</span><br><span class="line">                <span class="built_in">float</span>[] disGroup = <span class="keyword">new</span> <span class="built_in">float</span>[numInGroup];</span><br><span class="line">                <span class="built_in">int</span> localMinIndex = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// get localMinPathDis and its index</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> antIndex = <span class="number">0</span>; antIndex &lt; numInGroup; antIndex++)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                        disGroup[antIndex] += dis[GetPathIndex(path[i + numPoint * antIndex],</span><br><span class="line">                            (i + <span class="number">1</span>) % numPoint == <span class="number">0</span> ? path[numPoint * antIndex] : path[i + <span class="number">1</span> + numPoint * antIndex])];</span><br><span class="line">                    <span class="keyword">if</span> (disGroup[antIndex] &lt; localMinPathDis)</span><br><span class="line">                    &#123;</span><br><span class="line">                        localMinPathDis = disGroup[antIndex];</span><br><span class="line">                        localMinIndex = antIndex;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                </span><br><span class="line">                <span class="built_in">int</span>[] localMinPath = <span class="keyword">new</span> <span class="built_in">int</span>[numPoint];</span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    localMinPath[i] = path[i + localMinIndex * numPoint];</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span>(bFirstRound)</span><br><span class="line">                &#123;</span><br><span class="line">                    minPathDis = localMinPathDis;</span><br><span class="line">                    minCrossCnt = CountCross(localMinPath);</span><br><span class="line">                    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="keyword">if</span>(bDrawLine) lineRenderer.SetPosition(i, pPos[localMinPath[i]]);</span><br><span class="line">                        minPath[i] = localMinPath[i];</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span>(bDrawLine) lineRenderer.SetPosition(numPoint, pPos[localMinPath[<span class="number">0</span>]]);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// pheromone drop</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPath; i++)</span><br><span class="line">                &#123;</span><br><span class="line">                    pheromone[i] *= pheDrop;</span><br><span class="line">                    <span class="keyword">if</span> (pheromone[i] &lt; pheMin) pheromone[i] = pheMin;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// add pheromone of the turn</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> antIndex = <span class="number">0</span>; antIndex &lt; numInGroup; antIndex++)</span><br><span class="line">                    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="built_in">int</span> nowIndex = GetPathIndex(path[i + numPoint * antIndex], (i + <span class="number">1</span>) % numPoint == <span class="number">0</span></span><br><span class="line">                            ? path[numPoint * antIndex] : path[i + <span class="number">1</span> + numPoint * antIndex]);</span><br><span class="line">                        <span class="comment">// shorter path impacts pheromone more</span></span><br><span class="line">                        pheromone[nowIndex] += deltaPhe * Mathf.Pow(localMinPathDis / disGroup[antIndex], dropPow);</span><br><span class="line">                        <span class="keyword">if</span> (pheromone[nowIndex] &gt; pheMax) pheromone[nowIndex] = pheMax;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// emphasize best solution but avoid its crossing parts and small angle</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">int</span> cur1 = minPath[i];</span><br><span class="line">                    <span class="built_in">int</span> cur2 = (i + <span class="number">1</span>) % numPoint == <span class="number">0</span> ? minPath[<span class="number">0</span>] : minPath[i + <span class="number">1</span>];</span><br><span class="line">                    <span class="built_in">int</span> nowIndex = GetPathIndex(cur1, cur2);</span><br><span class="line">                    pheromone[nowIndex] += deltaPhe * emphasizeBest;</span><br><span class="line">                    <span class="keyword">if</span> (pheromone[nowIndex] &gt; pheMax) pheromone[nowIndex] = pheMax;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// avoid crossing</span></span><br><span class="line">                    <span class="keyword">for</span> (<span class="built_in">int</span> j = <span class="number">0</span>; j &lt; i - <span class="number">1</span>; j++)</span><br><span class="line">                        <span class="keyword">if</span> (DetectCross(pPos[cur1], pPos[cur2], pPos[minPath[j]], pPos[minPath[j + <span class="number">1</span>]]))</span><br><span class="line">                        &#123;</span><br><span class="line">                            <span class="built_in">int</span> anoIndex = GetPathIndex(minPath[j], minPath[j + <span class="number">1</span>]);</span><br><span class="line">                            pheromone[nowIndex] = pheMin;</span><br><span class="line">                            pheromone[anoIndex] = pheMin;</span><br><span class="line"></span><br><span class="line">                            pheromone[GetPathIndex(cur1, minPath[j])] += deltaPhe * crossTweakForce;</span><br><span class="line">                            <span class="keyword">if</span> (pheromone[GetPathIndex(cur1, minPath[j])] &gt; pheMax) pheromone[nowIndex] = pheMax;</span><br><span class="line">                            pheromone[GetPathIndex(cur2, minPath[j + <span class="number">1</span>])] += deltaPhe * crossTweakForce;</span><br><span class="line">                            <span class="keyword">if</span> (pheromone[GetPathIndex(cur2, minPath[j + <span class="number">1</span>])] &gt; pheMax) pheromone[nowIndex] = pheMax;</span><br><span class="line">                        &#125;</span><br><span class="line">                    <span class="comment">// avoid small angle</span></span><br><span class="line">                    <span class="keyword">if</span> (i != <span class="number">0</span> &amp;&amp; DetectSmallAngle(pPos[cur1] - pPos[cur2], pPos[cur1] - pPos[minPath[i - <span class="number">1</span>]], angleOffset))</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="built_in">int</span> anoIndex = GetPathIndex(minPath[i - <span class="number">1</span>], cur1);</span><br><span class="line"></span><br><span class="line">                        pheromone[nowIndex] -= deltaPhe * emphasizeBest;</span><br><span class="line">                        pheromone[anoIndex] -= deltaPhe * emphasizeBest;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// set particle size based on pheromone</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint &amp;&amp; bParticle; i++)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">int</span> mostPheIndex = i + <span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="built_in">int</span> j = i + <span class="number">1</span>; j &lt; numPoint; j++)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="built_in">int</span> nowIndex = GetPathIndex(i, j);</span><br><span class="line">                        <span class="built_in">float</span> sizeRatio = (pheromone[nowIndex] - pheMin) / (pheMax - pheMin);</span><br><span class="line">                        particles[nowIndex].SetParticleSize(sizeRatio);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                    </span><br><span class="line">                <span class="comment">// update minPathDis and display</span></span><br><span class="line">                <span class="keyword">if</span> (localMinPathDis &lt; minPathDis)</span><br><span class="line">                &#123;</span><br><span class="line">                    minCrossCnt = CountCross(localMinPath);</span><br><span class="line">                    minPathDis = localMinPathDis;</span><br><span class="line">                    print(minPathDis);</span><br><span class="line">                    print(minCrossCnt);</span><br><span class="line">                    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numPoint; i++)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="keyword">if</span>(bDrawLine) lineRenderer.SetPosition(i, pPos[localMinPath[i]]);</span><br><span class="line">                        minPath[i] = localMinPath[i];</span><br><span class="line">                    &#125;</span><br><span class="line">                <span class="keyword">if</span>(bDrawLine) lineRenderer.SetPosition(numPoint, pPos[localMinPath[<span class="number">0</span>]]);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// show Recent Path</span></span><br><span class="line">                <span class="keyword">if</span> (bSpace &amp;&amp; !bParticle) nowPath.DrawNowPath(path, pPos, numPoint, localMinIndex);</span><br><span class="line"></span><br><span class="line">                bFirstRound = <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="built_in">int</span> <span class="title">CountCross</span>(<span class="params"><span class="built_in">int</span>[] path</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="built_in">int</span> crossCnt = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">2</span>; i &lt; path.Length; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            Vector3 pA = pPos[path[i]];</span><br><span class="line">            Vector3 pB = pPos[i + <span class="number">1</span> == path.Length ? path[<span class="number">0</span>] : path[i + <span class="number">1</span>]];</span><br><span class="line">            <span class="keyword">for</span> (<span class="built_in">int</span> j = <span class="number">0</span>; j &lt; i - <span class="number">1</span>; j++)</span><br><span class="line">            &#123;</span><br><span class="line">                Vector3 pC = pPos[path[j]];</span><br><span class="line">                Vector3 pD = pPos[path[j + <span class="number">1</span>]];</span><br><span class="line">                <span class="keyword">if</span>(DetectCross(pA, pB, pC, pD)) crossCnt++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> crossCnt;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="built_in">bool</span> <span class="title">DetectCross</span>(<span class="params">Vector3 pA, Vector3 pB, Vector3 pC, Vector3 pD</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="keyword">if</span> (Vector3.Dot(Vector3.Cross(pA - pD, pC - pD), Vector3.Cross(pB - pD, pC - pD)) &lt; <span class="number">0</span></span><br><span class="line">            &amp;&amp; Vector3.Dot(Vector3.Cross(pC - pB, pA - pB), Vector3.Cross(pD - pB, pA - pB)) &lt; <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="built_in">bool</span> <span class="title">DetectSmallAngle</span>(<span class="params">Vector2 vec1, Vector2 vec2, <span class="built_in">float</span> angleOffset</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="keyword">if</span> (Vector2.Angle(vec1, vec2) &lt; angleOffset)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><div class='spoiler collapsed'>    <div class='spoiler-title'>        Particles    </div>    <div class='spoiler-content'>        <figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Particle</span> : <span class="title">MonoBehaviour</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">float</span> sizePow;</span><br><span class="line">    [<span class="meta">HideInInspector</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">new</span> ParticleSystem particleSystem;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Awake</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        particleSystem = GetComponent&lt;ParticleSystem&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">SetParticleRot</span>(<span class="params"><span class="built_in">float</span> angle</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="keyword">var</span> sh = particleSystem.shape;</span><br><span class="line">        sh.enabled = <span class="literal">true</span>;</span><br><span class="line">        sh.shapeType = ParticleSystemShapeType.Rectangle;</span><br><span class="line">        sh.rotation = <span class="keyword">new</span> Vector3(<span class="number">0f</span>, angle, <span class="number">0f</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">SetParticleDis</span>(<span class="params"><span class="built_in">float</span> dis</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="keyword">var</span> main = particleSystem.main;</span><br><span class="line">        <span class="built_in">float</span> speed = main.startSpeed.constant;</span><br><span class="line">        main.startLifetime = dis / speed;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// max Size = 2.5, min Size = 0.5</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">SetParticleSize</span>(<span class="params"><span class="built_in">float</span> sizeRatio</span>)</span></span><br><span class="line"><span class="function"></span>    &#123;</span><br><span class="line">        <span class="keyword">var</span> main = particleSystem.main;</span><br><span class="line">        sizeRatio = Mathf.Pow(sizeRatio, sizePow);</span><br><span class="line">        <span class="built_in">float</span> size = sizeRatio * <span class="number">2.5f</span>;</span><br><span class="line">        main.startSize = size &gt; <span class="number">0.5f</span>? size : <span class="number">0f</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;蚁群优化算法，最直观的一个示例用途是解决TPS（TravelingSealsmanProblem）问题，快速寻找一个可观的解，但需要你调的一手好参&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/ACO_cover.gif&quot; /&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="GameDev" scheme="https://kyriota.github.io/categories/GameDev/"/>
    
    
  </entry>
  
  <entry>
    <title>Compute Shader and Boid</title>
    <link href="https://kyriota.github.io/2021/09/23/ComputeShaderAndBoid/"/>
    <id>https://kyriota.github.io/2021/09/23/ComputeShaderAndBoid/</id>
    <published>2021-09-23T14:28:20.000Z</published>
    <updated>2023-11-24T01:24:01.395Z</updated>
    
    <content type="html"><![CDATA[<p>最近刷油管看到一个关于生物群模拟的Unity小项目，其中提到了使用ComputeShader来实现多线程的并行计算，感觉很适合入门，就做了一些尝试和改良</p><p><img src='/images/Boid_Cover.png' style="zoom:50%;" ></p><span id="more"></span><h1 id="compute-shader-and-boid">Compute Shader and Boid</h1><p><img src="/images/Boid_WholeFinish.png" /></p><ul><li>首先，什么是<code>ComputeShader</code>？</li></ul><blockquote><p>A <strong>Compute Shader</strong> is a Shader Stage that is used entirely for computing arbitrary information. While it can do rendering, it is generally used for tasks not <em>directly</em> related to drawing triangles and pixels.</p></blockquote><p>简单地说，就是一个把任务从CPU移到GPU计算的shader就是ComputeShader。关于为什么要这么做，GPU是善于处理高度统一，重复性高的计算的，可以一次性把大量数据写入buffer，然后进行并发的计算，在实际表现中，使用了ComputeShader后的性能提升比纯C#脚本的效率提升了至少两倍</p><p>具体说说并发计算，其实也就是多线程，GPU进行多线程计算时，会等到当前buffer中的最后一项操作执行完成后再返回，这就导致类似短板效应的“长板效应”，我的粗略理解为执行一轮计算的时间是需要操作次数最多的一个线程，于是很明显，ComputeShader不能用于分支多，运行时间差异大的算法</p><ul><li>其次，什么是<code>Boid</code>？</li></ul><p>对于生物群的模拟，根据 <a href="http://www.cs.toronto.edu/~dt/siggraph97-course/cwr87/">这篇文章</a>，可以简单把生物群的行为抽象为以下三条</p><blockquote><ol type="1"><li>Collision Avoidance: avoid collisions with nearby flockmates</li><li>Velocity Matching: attempt to match velocity with nearby flockmates</li><li>Flock Centering: attempt to stay close to nearby flockmates</li></ol></blockquote><p>也就是：避免碰撞，速度匹配，位置居中。还有不同的抽象方法，越细致的规则能塑造的群体必然也会越复杂，但其实只要应用了这三个规则，就已经很够看了</p><hr /><p>本工程将群落明确为鱼群，接下来将会从鱼群的抽象规则进行小规模模拟，到应用ComputeShader模拟大规模鱼群，最后使用ShaderGraph制作鱼的摆动以及水面来完善整个工程，为其润色</p><blockquote><p>其实当真的敲代码的时候会发现约束鱼群的规则都非常简单，但是多个个体所组成的系统确实复杂的</p></blockquote><h2 id="getting-start">Getting Start</h2><p>因为我自己也是Unity入门，对于里面的Rigibody之类的内置力学系统不太了解，也不知道通常的处理这类力学问题的解决方案，恰好我手上拿到的油管dalao的示例代码用的是公式法，即将速度，加速度都保存在变量里，我就照搬他的做法了</p><p>为了后期的调参便利（调参真的是一个很麻烦的事情），我将根据三条规则所产生的加速度捆绑为一种类型的加速度，再将规避障碍物的加速度捆绑为另一种加速度，然后对每种类型的加速度分别进行更细致的权重分配</p><p>为了便于管理鱼群的行为，将 处理当前的鱼与其他剩余的鱼的代码 与 鱼自身Update的代码 分开编辑是很重要的，不管是编辑的便利还是运行的效率，每次查找其他鱼的时候都<code>FindObjectsOfType&lt;Boid&gt;()</code>一个新的列表是完全没有必要的，所以把这部分与其他鱼产生联系的代码抽象为一个<code>BoidManager</code>类，把更新自身速度等信息的代码放在<code>Boid</code>自己的类里面作为成员才是合理的</p><ul><li><p>关键函数：</p><p><code>FindObjectsOfType&lt;Boid&gt;()</code> 按类查找</p><p><code>Awake()</code> 与 <code>Start()</code> 注意<code>Awake</code>方法比<code>Start</code>先执行就行</p></li></ul><p><img src="/images/Boid_UnityLifeCircle.webp" alt="Boid_UnityLifeCircle" style="zoom: 80%;" /></p><h2 id="rules">Rules</h2><h3 id="collision-avoidance">Collision Avoidance</h3><p>当前对象与每条鱼位置向量之差即为偏移量，用偏移量的单位向量除以偏移量的模以获得一个基于位置远近的加速度即可</p><center><code>avoidAcceleration -= posOffest.normalized / posOffset.magnitude</code></center><h3 id="velocity-matching">Velocity Matching</h3><p>当前对象与每条鱼朝向向量之差即为偏移量，因为其实实际运行中速度的大小差异不会很大，速度方向却会有很大差异，所以只用匹配一下朝向即可</p><center><code>headingAcceleration += forwardOffest</code></center><h3 id="flock-centering">Flock Centering</h3><p>这其实就是第一条规则去掉距离远近的加权</p><h3 id="result">Result</h3><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="built_in">int</span> anoBoidIndex = <span class="number">0</span>; anoBoidIndex &lt; boids.Length; anoBoidIndex++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (boidIndex != anoBoidIndex) &#123;</span><br><span class="line">        Vector3 offest = boids[boidIndex].transform.position - boids[anoBoidIndex].transform.position;</span><br><span class="line">        <span class="built_in">float</span> sqrDis = offest.sqrMagnitude;</span><br><span class="line">        <span class="keyword">if</span> (sqrDis &lt; viewRadius * viewRadius) &#123;</span><br><span class="line">            headingAcceleration += boids[anoBoidIndex].transform.forward - boids[boidIndex].transform.forward;</span><br><span class="line">            centerAcceleration -= offest;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (sqrDis &lt; avoidRadius * avoidRadius) avoidAcceleration += offest.normalized / sqrDis;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">boids[boidIndex].acceleration = avoidAcceleration * avoidWeight + headingAcceleration * headingWeight + centerAcceleration * centerWeight;</span><br></pre></td></tr></table></figure><p>其中引入了几个Radius作为是否影响当对象的阈值以限制鱼的视力范围</p><p>应用了三个规则后可以如下GIF的效果</p><p><img src="/images/Boid_Rules.gif" /></p><center><small>（红色的那条是0号鱼(初号机)，为的是便于从个体的视角观察动向）</small></center><p>我在一个圆内在随机的位置以随机的朝向生成了一些鱼，不难发现他们很快就会根据自己的位置与朝向形成几个鱼群，但这些鱼群都十分稳定，这是因为虽然他们受群聚规则的限制，但没有一个外力来破坏这一规则限制出的稳定结构，我们想看到的必然不是一个稳定结构的鱼群</p><p>对于外力，我们可以选择增加一个驱逐者来追赶鱼群，类似于 <a href="www.bilibili.com/video/BV1tq4y1j7TW">这个视频</a>，也可以增加障碍物来阻挡鱼群，在此为了场景的多样性，我选择后者</p><h2 id="obstacle-avoidance">Obstacle Avoidance</h2><p>其实上述的鱼群行动规则非常简单，不管是字面上看起来还是代码上敲起来，这也使得在空旷环境中的鱼群看起来很没意思，所以加入一个避障的function来使它更有趣吧</p><p>要使鱼避开前方的障碍物，首先想到的便是<code>Physics.Raycast()</code>射线检测，但此函数对CPU貌似不怎么友好，大量运行此函数卡成PPT，而单个的射线检测也是检测到物体时就返回了，均具有执行时间具有高度不确定性的特征，不适宜使用ComputeShader来提速，故唯一的解决方案就是尽可能地减少射线数量</p><p>在示例工程中，这位老哥使用了 <a href="https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere/44164075#44164075">这种方法</a> 来使点阵在球面上近似均匀分布，然后对每个点做射线检测，大致是对黄金螺线做极径开个根号，使得螺线上的点均匀摊开，BEAUTIFUL</p><p><img src="/images/Boid_DIvideSphere1.png" /></p><p>而从圆面到球面，把极径换成φ就过来了</p><p><img src="/images/Boid_DIvideSphere2.png" /></p><p>表达可能不太清楚，但只要动手调一下参，模拟一下螺线的变化就很明白了</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">const</span> <span class="built_in">int</span> numDivide = <span class="number">15</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">const</span> <span class="built_in">float</span> viewAngleRatio = <span class="number">4f</span> / <span class="number">5f</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Vector3[] viewVec3;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">int</span> numViewDirections = Mathf.CeilToInt(numDivide * viewAngleRatio);</span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="title">BoidHelper</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    viewVec3 = <span class="keyword">new</span> Vector3[BoidHelper.numDivide];</span><br><span class="line"></span><br><span class="line">    <span class="built_in">float</span> goldenRatio = (<span class="number">1</span> + Mathf.Sqrt(<span class="number">5</span>)) / <span class="number">2f</span>;</span><br><span class="line">    <span class="built_in">float</span> angleIncrement = Mathf.PI * <span class="number">2</span> * goldenRatio;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; numViewDirections; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">float</span> t = (<span class="built_in">float</span>)i / numDivide;</span><br><span class="line">        <span class="built_in">float</span> inclination = Mathf.Acos(<span class="number">1</span> - <span class="number">2</span> * t);</span><br><span class="line">        <span class="built_in">float</span> azimuth = angleIncrement * i;</span><br><span class="line"></span><br><span class="line">        viewVec3[i].x = Mathf.Sin(inclination) * Mathf.Cos(azimuth);</span><br><span class="line">        viewVec3[i].y = Mathf.Sin(inclination) * Mathf.Sin(azimuth);</span><br><span class="line">        viewVec3[i].z = Mathf.Cos(inclination);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>示例工程中这位老哥应该也是怕射线太多导致卡顿，方案比较暴力，就是从前方开始检测，一旦检测到空旷处就直接加个距离的权重然后应用到加速度上并直接<code>break</code>掉循环了，也就是说鱼会朝着第一个检测到的空旷处游去。</p><p><img src="/images/Boid_Close2Ground.png" /></p><p>这不免导致了一个问题，那就是鱼最后会和障碍物表面相切，然后贴着障碍物表面移动，先不说这样会导致在下一次受到扰动时穿模的问题，这种款式的鱼群简直像极了一群大耗子</p><p><img src="/images/Boid_StickToGround.gif" /></p><p>所以既然在场景不会很复杂的时候使用这么多射线属实没有必要，经过实际测试，使用几条射线分辨侧面环境并且不<code>break</code>射线检测的循环，使鱼充分认知到周围环境，将此与上述等分检测的方法组合便是一个更优解，此处我选择的是12条射线，即xy平面上60°等分，再在φ=45°的圆周上也60°等分</p><p><img src="/images/Boid_Close2GroundFix.png" /></p><p>避障的相关代码因为不涉及其他鱼，选择把代码放在了<code>Boid</code>类下，但这会导致调参的时候需要在Unity中频繁切换Inspector，解决方案是把参数全部放到一个<code>Setting</code>类中，此类继承<code>ScriptableObject</code>，这样不仅解决了Inspector的问题，还使得Prefab在被拷贝时通过reference访问数据，避免了多次拷贝造成的内存浪费，也提升了在运行中调参时的整体性，运行中进行的调参也得以直接保存而不用Copy Component Values了</p><h3 id="front">Front</h3><p>在进行正面避障时，如果提供的射线少了则会使得鱼的速度方向变化过大，不够平滑，所以正面避障直接采用黄金螺旋法在球面上等距取点，从正面开始遍历这些点，一旦取到一个可转向的范围就<code>break</code>即可</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Vector3 <span class="title">AvoidObstacleDir</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; BoidHelper.numViewDirections; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        Vector3 viewDir = transform.TransformDirection(BoidHelper.viewVec3[i]);</span><br><span class="line">        Ray ray = <span class="keyword">new</span> Ray(transform.position, viewDir);</span><br><span class="line">        <span class="keyword">if</span> (!Physics.Raycast(ray, obstacleRadius, obstacleMask)) <span class="keyword">return</span> viewDir;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Vector3.zero;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="around">Around</h3><p>示例工程中因为缺少了对周围障碍物的检测，导致了大量因为贴近物体表面移动而穿过Collider的行为，所以在周围加入少量的<code>Raycast</code>，使得鱼也不会过于靠近四周的障碍</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Vector3 <span class="title">KeepDstWithObstDir</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    Vector3 keepDstDir = Vector3.zero;</span><br><span class="line">    <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; circleFrac; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        Vector3 viewDir = Vector3.zero;</span><br><span class="line">        <span class="built_in">float</span> angle = i / (<span class="built_in">float</span>)circleFrac * <span class="number">2</span> *Mathf.PI;</span><br><span class="line">        viewDir.x = Mathf.Sin(angle);</span><br><span class="line">        viewDir.y = Mathf.Cos(angle);</span><br><span class="line">        viewDir = transform.TransformDirection(viewDir);</span><br><span class="line">        Ray ray = <span class="keyword">new</span> Ray(transform.position, viewDir);</span><br><span class="line">        RaycastHit hit;</span><br><span class="line">        <span class="keyword">if</span> (Physics.Raycast(ray,<span class="keyword">out</span> hit, keepDstWithObstRadius, obstacleMask)) keepDstDir -= viewDir / hit.distance;</span><br><span class="line">        <span class="comment">// cos(PI/4) is close to 0.7071</span></span><br><span class="line">        <span class="built_in">float</span> angleOffset = <span class="number">1</span> / (<span class="built_in">float</span>)circleFrac * Mathf.PI;</span><br><span class="line">        viewDir.x = Mathf.Sin(angle + angleOffset) * <span class="number">0.7071f</span>;</span><br><span class="line">        viewDir.y = Mathf.Cos(angle + angleOffset) * <span class="number">0.7071f</span>;</span><br><span class="line">        viewDir.z = <span class="number">0.7071f</span>;</span><br><span class="line">        viewDir = transform.TransformDirection(viewDir);</span><br><span class="line">        ray = <span class="keyword">new</span> Ray(transform.position, viewDir);</span><br><span class="line">        <span class="keyword">if</span> (Physics.Raycast(ray,<span class="keyword">out</span> hit, keepDstWithObstRadius, obstacleMask)) keepDstDir -= viewDir / hit.distance;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> keepDstDir.normalized;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="result-1">Result</h3><p>至此鱼群的模拟已经基本完成，接下来要做的便是将<code>Rules</code>部分的代码移植到ComputeShader中计算，以减轻CPU的负担</p><p><img src="/images/Boid_BasicResult.gif" /></p><p>除了使用障碍物破坏鱼群的稳定结构，加入捕食者、食物之类的机制也可以为鱼群系统增添复杂性与趣味性，不妨自己尝试一下</p><h2 id="compute-shader">Compute Shader</h2><h3 id="wtf-is-this">WTF is this</h3><p>不妨使用文档中的示例来解释一下ComputeShader，下面这个ComputeShader将一个材质涂红</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">#pragma kernel FillWithRed</span><br><span class="line">RWTexture2D&lt;float4&gt; res;</span><br><span class="line">[numthreads(1,1,1)]</span><br><span class="line">void FillWithRed (uint3 dtid : SV_DispatchThreadID)</span><br><span class="line">&#123;</span><br><span class="line">    res[dtid.xy] &#x3D; float4(1,0,0,1);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可见ComputeShader使用<code>HLSL</code>语言，也就是<code>High Level Shader Language</code>，因为通常只是拿来做数学计算，所以基本上只要装一个hlsl的代码提示的extension就可以直接开始写了</p><p><code>#pragma kernel FillWithRed</code>：首先定义了一个核函数，函数名默认是CSMain，这是compute shader的函数入口，你也可以定义多个入口，因为本工程只需要一个入口，多个入口的ComputeShader暂时没有多做了解</p><p><code>[numthreads(1,1,1)]</code>：规定了使用的线程组大小，这里使用的是单线程，默认是<code>(8,8,1)</code>，线程数量为<code>8x8x1</code>，也就是64线程作为一个线程组。<a href="https://blog.csdn.net/weixin_38884324/article/details/80570160">更多地了解numthreads</a> 会发现里面东西挺多，涉及到显卡的硬件</p><p><img src="/images/Boid_ThreadGroup.png" alt="Boid_ThreadGroup" style="zoom:50%;" /></p><p>如上图，<code>numthreads</code>规定了一个Group(线程组)中的线程数量，<code>Dispatch()</code>作为C#脚本调用ComputeShader的交互方式，与<code>numthread</code>有一些联系，在介绍完Unity的官方ComputeShader示例后会提到。至于为什么大小由一个三维数组确定，是为了在访问2D或者3D的数据结构的时候更加方便操作(大概是吧)</p><blockquote><p>The ability to specify the size of the thread group across three dimensions allows individual threads to be accessed in a manner that logically 2D and 3D data structures</p><p align="right"><a herf="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-numthreads">--Microsoft Doc</a></p></blockquote><p>在实际使用中，一般不会使<code>numthreads</code>小于32，这会达不到最低线程数导致多核围观；但也不能超过1024，应该也是硬件层面的限制。根据之前提到的CSDN上的建议：</p><blockquote><p>AMD：ThreadSize 使用 64 的倍數 ( wavefront 架構 ) NVIDIA：ThreadSize 使用 32 的倍數 ( SIMD32 (Warp) 架構 )</p></blockquote><p>这么一看官方初始线程数选择64还挺合理的，但我们仍然需要根据自己的实际需求以及预期的线程组数来选择具体的<code>numthread</code></p><p><code>ID</code>：在声明方法时提到了<code>ID</code>的概念，即<code>void FillWithRed (uint3 dtid : SV_DispatchThreadID)</code>中的<code>SV_DispatchThreadID</code>，其实这些三维的<code>ID</code>基本上就是<code>index</code>了</p><blockquote><p>SV_GroupID : 線程組 ID SV_GroupThreadID : 線程組內的線程 ID (三維，你可以理解為 Group 內的座標) SV_GroupIndex : 線程組內的線程 ID (一維) SV_DispatchThreadID : 唯一ID (你可以理解成整張圖片座標)</p></blockquote><p><img src="/images/Boid_ID.png" alt="Boid_ID" style="zoom:50%;" /></p><p><code>Dispatch(kernelIndex,x,y,z)</code>：用于在C#脚本中调用ComputeShader，第一个参数为核函数的index，如果是CSMain就直接是0，如果不确定index到底是多少，可以通过<code>FindeKernel(String kernelName)</code>获取index。关于后面的xyz，需要满足<code>Dispatch*numthread&gt;=numData</code>，否则既然你的输入都不全，怎么获得一个完整的输出呢？</p><p>举几个栗子：</p><ul><li><p>Eg1.输出ComputeShader的默认代码</p><p>ComputeShader在Unity中创建时初始化代码如下</p><p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; Each #kernel tells which function to compile; you can have many kernels</span><br><span class="line">#pragma kernel CSMain</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; Create a RenderTexture with enableRandomWrite flag and set it</span><br><span class="line">&#x2F;&#x2F; with cs.SetTexture</span><br><span class="line">RWTexture2D&lt;float4&gt; Result;</span><br><span class="line"></span><br><span class="line">[numthreads(8,8,1)]</span><br><span class="line">void CSMain (uint3 id : SV_DispatchThreadID)</span><br><span class="line">&#123;</span><br><span class="line">    &#x2F;&#x2F; TODO: insert actual code here!</span><br><span class="line"></span><br><span class="line">    Result[id.xy] &#x3D; float4(id.x &amp; id.y, (id.x &amp; 15)&#x2F;15.0, (id.y &amp; 15)&#x2F;15.0, 0.0);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>可以使用一个C#脚本调用它</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Tester</span> : <span class="title">MonoBehaviour</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> ComputeShader cs;</span><br><span class="line">    <span class="keyword">public</span> RenderTexture tex;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Start</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建一个材质查看结果</span></span><br><span class="line">        tex = <span class="keyword">new</span> RenderTexture(<span class="number">256</span>,<span class="number">256</span>,<span class="number">24</span>);</span><br><span class="line">        <span class="comment">// 一定要记得打开写入权限</span></span><br><span class="line">        tex.enableRandomWrite = <span class="literal">true</span>;</span><br><span class="line">        tex.Create();</span><br><span class="line">        <span class="comment">// 在Dispatch前需要先传入参数</span></span><br><span class="line">        cs.SetTexture(<span class="number">0</span>, <span class="string">&quot;Result&quot;</span>, tex);</span><br><span class="line">        <span class="comment">// 注意到numthread为(8,8,1)，有numthread*Dispatch=Resolution</span></span><br><span class="line">        cs.Dispatch(<span class="number">0</span>, <span class="number">256</span>/<span class="number">8</span>, <span class="number">256</span>/<span class="number">8</span>, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>得到一个好康的分形图案</p><p><img src="/images/Boid_CSdefault.png" /></p></li><li><p>Eg2.将参数改小</p><p><code>Dispatch(0,64/8,128/8,1);</code></p><p><code>numthread[8,8,1];</code></p><p><img src="/images/Boid_CSeg2.png" /></p><p>观察到图像不全且只有64x128，与Dispatch中传入的量吻合</p></li><li><p>Eg3.将参数改大</p><p><code>tex = new RenderTexture(256,300,24);</code></p><p><code>Dispatch(0,256/8,(int)Mathf.Ceil(300/8),1);</code></p><p><code>numthread[8,8,1];</code></p><p><img src="/images/Boid_CSeg3.png" /></p><p>注意到<code>(int)Mathf.Ceil(300/8) * 8 &gt; 300</code>，如果此处不用<code>Ceil</code>向上取整则会少一部份没有被计算</p></li></ul><h3 id="translation">Translation</h3><p><code>C#</code>翻译到<code>hlsl</code>的工作流程如下</p><p>首先是C#脚本上</p><ul><li><p>创建结构体保存一些属性，其中<code>acceleration</code>作为计算结果，等待在ComputeShader中被赋值</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> BoidData &#123;</span><br><span class="line">    <span class="keyword">public</span> Vector3 position;</span><br><span class="line">    <span class="keyword">public</span> Vector3 forward;</span><br><span class="line">    <span class="keyword">public</span> Vector3 acceleration;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">int</span> Size &#123;</span><br><span class="line">        <span class="keyword">get</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">sizeof</span> (<span class="built_in">float</span>) * <span class="number">3</span> * <span class="number">3</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>实例化，赋值，创建Buffer</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> boidData = <span class="keyword">new</span> BoidData[boids.Length];</span><br><span class="line"><span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; boids.Length; i++) &#123;</span><br><span class="line">    boidData[i].position = boids[i].transform.position;</span><br><span class="line">    boidData[i].forward = boids[i].transform.forward;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> boidBuffer = <span class="keyword">new</span> ComputeBuffer (boids.Length, BoidData.Size);</span><br><span class="line">boidBuffer.SetData (boidData);</span><br></pre></td></tr></table></figure></p></li><li><p>传入ComputeShader计算需要的变量信息</p><p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">computeShader.SetBuffer(<span class="number">0</span>, <span class="string">&quot;boids&quot;</span>, boidBuffer);</span><br><span class="line">computeShader.SetInt(<span class="string">&quot;numBoids&quot;</span>, boids.Length);</span><br><span class="line">computeShader.SetFloat(<span class="string">&quot;viewRadius&quot;</span>, viewRadius);</span><br><span class="line">computeShader.SetFloat(<span class="string">&quot;avoidRadius&quot;</span>, avoidRadius);</span><br><span class="line">computeShader.SetFloat(<span class="string">&quot;avoidWeight&quot;</span>, avoidWeight);</span><br><span class="line">computeShader.SetFloat(<span class="string">&quot;headingWeight&quot;</span>, headingWeight);</span><br><span class="line">computeShader.SetFloat(<span class="string">&quot;centerWeight&quot;</span>, centerWeight);</span><br></pre></td></tr></table></figure></p></li><li><p>通过<code>Dispatch()</code>开始执行ComputeShader</p><p><code>computeShader.Dispatch(0, threadGroups, 1, 1);</code></p></li></ul><p>现在把视角切换到ComputeShader上</p><ul><li><p>把需要传入的变量都定义一遍，这里注意结构体使用了<code>RWStructuredBuffer</code>作为容器</p><blockquote><p>A structured buffer is a buffer that contains elements of equal sizes. Use a structure with one or more member types to define an element. Here is a structure with three members.</p></blockquote><p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">struct Boid &#123;</span><br><span class="line">    float3 position;</span><br><span class="line">    float3 forward;</span><br><span class="line">    float3 acceleration;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">RWStructuredBuffer&lt;Boid&gt; boids;</span><br><span class="line">int numBoids;</span><br><span class="line">float viewRadius;</span><br><span class="line">float avoidRadius;</span><br><span class="line">float avoidWeight;</span><br><span class="line">float headingWeight;</span><br><span class="line">float centerWeight;</span><br></pre></td></tr></table></figure></p></li><li><p>翻译一遍C#代码，只需要改一点语法就可以了</p><p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">void CSMain (uint3 id : SV_DispatchThreadID)</span><br><span class="line">&#123;</span><br><span class="line">    float3 zero3 &#x3D; &#123;0, 0, 0&#125;;</span><br><span class="line">    float3 avoidAcceleration &#x3D; zero3;</span><br><span class="line">    float3 headingAcceleration &#x3D; zero3;</span><br><span class="line">    float3 centerAcceleration &#x3D; zero3;</span><br><span class="line">    int mateCount;</span><br><span class="line">    int avoidCount;</span><br><span class="line">    for(int i &#x3D; 0; i &lt; numBoids; i ++)&#123;</span><br><span class="line">        if((int)id.x !&#x3D; i)&#123;</span><br><span class="line">            float3 offset &#x3D; boids[id.x].position - boids[i].position;</span><br><span class="line">            float sqrDst &#x3D; offset.x * offset.x + offset.y * offset.y + offset.z * offset.z;</span><br><span class="line">            if(sqrDst &lt; viewRadius * viewRadius)&#123;</span><br><span class="line">                mateCount +&#x3D; 1;</span><br><span class="line">                headingAcceleration +&#x3D; boids[i].forward - boids[id.x].forward;</span><br><span class="line">                centerAcceleration -&#x3D; offset;</span><br><span class="line">            &#125;</span><br><span class="line">            if(sqrDst &lt; avoidRadius * avoidRadius)&#123;</span><br><span class="line">                avoidCount++;</span><br><span class="line">                avoidAcceleration +&#x3D; normalize(offset) &#x2F; sqrDst;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    avoidAcceleration &#x3D; avoidCount &#x3D;&#x3D; 0 ? zero3 : normalize(avoidAcceleration);</span><br><span class="line">    headingAcceleration &#x3D; mateCount &#x3D;&#x3D; 0 ? zero3 : normalize(headingAcceleration);</span><br><span class="line">    centerAcceleration &#x3D; mateCount &#x3D;&#x3D; 0 ? zero3 : normalize(centerAcceleration);</span><br><span class="line">    boids[id.x].acceleration &#x3D; avoidAcceleration * avoidWeight + headingAcceleration * headingWeight + centerAcceleration * centerWeight;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ul><p>关于<code>numthread</code>，示例工程直接设了<code>[1024,1,1]</code>：使用一维向量是因为需要处理的数据没有类似材质、体积之类的三维结构体；<code>1024</code>是为了直接把线程拉满</p><h3 id="result-2">Result</h3><p>将原本在CPU中进行的大量重复计算迁移至GPU后可模拟的鱼群规模瞬间提升了不少，现在你可以模拟一些较大规模的鱼群了，在此就不附新的图片了</p><h2 id="polish">Polish</h2><p>就算为其换上一个真正的鱼🐟的模型，现在的鱼群整体还比较单调，虽然有了一些规则使得其在宏观动态的表现力上有了那么一捏捏，但还缺少一些更加直观的细节，说白了就是有一个有趣的灵魂但没有华丽的外表，所以我们不妨做两个shader来为其润色，一个shader负责鱼的摆动，另一个负责水面材质</p><p>我使用的是HDRP作为渲染管线，虽然我不会用它，但我听说他很强，所以我希望能迈出第一步</p><p>由于目前使用的是ShaderGraph，不便于附代码，只能讲一下思路并附带一些资料了</p><h3 id="waving-fish">Waving Fish</h3><p>其实可以用一个<code>sin(t)</code>函数就能获得很能接受的效果了，但是鉴于鱼都是成群出现，以时间为变量的函数肯定是不行的，我当前的做法是将鱼的世界坐标作为变量，输入到<code>Gradient Noise</code>，对鱼的顶点坐标的某一个轴向应用这个噪声，这样得到的鱼会根据自身位置而决定摇摆，缺点是没有和鱼的转向、运动等联系起来，在设想中，能根自身加速度选择摇摆方式的Shader才是最好的，但目前不清楚这样做的可行性以及具体方法</p><p>接下来还需要将鱼头的摇摆幅度降低，因为一般来说鱼头不会像尾巴和身体那样剧烈摆动。在此推荐了解一下有关<code>UV</code>的概念，这很重要，比如此处可以将UV的x作为变量，在噪声和原本的顶点位置之间做线性插值(LERP)，也可以对UV.x取平方之类的以让摆动的过度更加自然</p><p>综上，便得到了一条欢快的小鱼🐟</p><p><img src="/images/Boid_WavingFish.gif" /></p><h3 id="water-surface-shader">Water Surface Shader</h3><p>鱼得到了升级，还需要一个容器来使得整个场景在水中，这里选择制作一个水面的材质贴在平面上，做成开头那样的水立方，Unity官方提供了 <a href="https://www.youtube.com/watch?v=gRq-IdShxpU">基本的水面ShdaerGraph教程</a>，由于处处碰壁，我也就基本是抄了一遍官方的成果</p><p>主要的思想是使<code>Scene Depth</code>与<code>Screen Position</code>相减，由于前者的检测不包含透明物体，而后者包含，则可以获取穿过一个透明平面后的空间深度信息</p><p><img src="/images/Boid_Depth.png" alt="Boid_Depth" style="zoom: 67%;" /></p><p>之后只需要在这个平面的表面做两层不同细节程度的水面Normal Map的反向滚动，再加入由噪声控制的平面顶点坐标的起伏即可</p><h2 id="final-result">Final Result</h2><p>Check the final result if you want, a gif about 10 Mb</p><p><img src="/images/Boid_Final.gif" /></p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近刷油管看到一个关于生物群模拟的Unity小项目，其中提到了使用ComputeShader来实现多线程的并行计算，感觉很适合入门，就做了一些尝试和改良&lt;/p&gt;
&lt;p&gt;&lt;img src=&#39;/images/Boid_Cover.png&#39; style=&quot;zoom:50%;&quot; &gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Unity" scheme="https://kyriota.github.io/categories/Unity/"/>
    
    
  </entry>
  
  <entry>
    <title>Render High Dimensional Object In Terminal</title>
    <link href="https://kyriota.github.io/2021/04/24/RenderHighDimensionalObjectInTerminal/"/>
    <id>https://kyriota.github.io/2021/04/24/RenderHighDimensionalObjectInTerminal/</id>
    <published>2021-04-24T21:28:20.000Z</published>
    <updated>2023-08-07T02:19:59.942Z</updated>
    
    <content type="html"><![CDATA[<p>Meet Hypercubes~ O(∩_∩)O</p><p>🦎语言渲染n维立方体至终端，那真是非常amazing啊</p><p><img src="/images/4Hypercube-transparent.gif" height="200" width="200" /></p><span id="more"></span><h1 id="render-high-dimensional-object-in-terminal">Render High Dimensional Object In Terminal</h1><p align="right"><small>So all this just to render a cube on your terminal ?</small></p><blockquote><p>后来做成工程概论大作业的<a href="https://kyriota.com/html/hypercube/">网页版本</a>(手机慎点)，使用了<code>threejs</code>这个图形库来绘制和打光，比起控制台来说虽然不那么geek，但是稍微更直观那么亿点，应该还存在一些bug，但是3，4，5维下应该是没什么问题</p></blockquote><p>封面gif为超立方体绕xw与xz轴的旋转，至于结果的正确性，我参照了wiki且自认为无误，例如下图就是一些与超立方体相关的图片</p><center><table><tr><td><center><img src="/images/HypercubeStructure.gif" width=200 height=200>超立方体解构</center></td><td><center><img src="/images/wikiHypercube.gif" width=200 height=200>超立方体绕xw轴旋转</center></td></tr></table></center><p>不妨先来看看我的代码都渲染出了些什么：</p><table><colgroup><col style="width: 49%" /><col style="width: 50%" /></colgroup><thead><tr class="header"><th>图像</th><th>说明</th></tr></thead><tbody><tr class="odd"><td><img src="/images/4HypercubeOpen.gif" width=500 height=500></td><td>此为在<a href="https://kyriota.com/2021/04/19/Render3D-ObjectInTerminal/">Render 3D Object In Terminal</a>基础上修改的代码，限于模型生成原理与渲染原理写的稀烂而无法拓展到更高的五维，且由于法向量并非实时计算而是手动设置，导致旋转时的”四维面“渲染错误（就是gif中看见明显是个正方体但是却没有正确的打光的一坨吞过来吐回去的东西）</td></tr><tr class="even"><td><img src="/images/4Hypercube.gif" width=500 height=500></td><td>后来对模型生成原理与渲染原理大改，引入顶点模型并实时计算法向量，正确渲染出了这个超立方体</td></tr><tr class="odd"><td><img src="/images/5Hypercube.gif" width=500 height=500></td><td>在四维升五维时又出现了代码通用性的问题，发现五维时存在多处三角形的各顶点几乎位于同一直线的情况，对判断质心与直线关系的方程引入一个很小的Δoffest解决此问题</td></tr></tbody></table><p>最后写了透明材质，为了方便和实际效果没有对透明材质计算光照，透明物体代码比不透明的简单，但确实便利了理解内部结构变化</p><center><table><tr><td><center><img src="/images/4Hypercube-transparent.gif" width=200 height=200>透明四维</center></td><td><center><img src="/images/5Hypercube-transparent.gif" width=200 height=200>透明五维</center></td></tr></table></center><div class='spoiler collapsed'>    <div class='spoiler-title'>        对渲染与模型的改进大纲    </div>    <div class='spoiler-content'>        <ul><li><p>三角形：先前的正方体和球体模型都是实际存在于数组中的非常多的点，每个点与屏幕上的点对应，不仅空间占用多，算力跟不上，法向量也是针对正方体和球体的几何性质为其手动安排的，缺乏普适性</p><ul><li>解决：对于正方体，只需要求出正方体的顶点以及顶点之间的位置关系（哪些顶点相邻），即可由此推得三角形、四棱锥等独立的局部图形，这带来的好处有：<ul><li>普适性增强，即使物体不是正方体也能以此方式计算、渲染</li><li>性能提升，维度得以升的更高，计算速度快了亿点点</li><li>可以通过计算局部质心准确区分内外法向量，但是此处的正方体是凸几何体，直接以原点（正方体质心）区分内外法向量也是足够的</li></ul></li></ul></li><li><p>内外法向量：现有一模型的相邻三点确定的平面ABC，如何区分内外法向量？</p><ul><li><p>解决：因为我面对的对象是正方体，所以只要对任意顶点向量加上法向量，将作此偏移后的向量与其对应顶点向量比较模长大小，即可判断内外法向量</p><p><img src="/images/HelloDimension_外法向量.jpg" width=200 height=200></p><p>但这样会在凹几何体上有误</p><p><img src="/images/HelloDimension_全局质心.jpg" width=200 height=200></p><p>所以普适的做法应是再寻找一个相邻顶点，确定一个四面体，计算此四面体质心，然后以此质心为基准作同样的偏移后的模长比较</p></li></ul></li><li><p>透视投影：人眼观察物体是小孔成像，透视投影，正投影会缺少近大远小的特征，且只有在降维时透视投影才能使高维几何体投影出令人满意的效果（否则就是黏成一坨扭来扭曲，没有大小变化），透视投影的投影矩阵通过一个相似三角形就出来了，点到为止</p></li><li><p>半透明材质：就简不就繁，再精致的透明材质在终端也看不出来，我的做法是不再计算法向量，发现投影到屏幕上的面有几个，该点的alpha就加到几，然后应用下面这个式子把alpha换回 [0,1) 内（整个幂主要是想让曲线平滑一点，去掉也可以）</p><center><p><code>alpha=1-1/(k*alpha+1)^3/2</code></p></center></li></ul>    </div></div><p>以下是最终的源码，重要参数全部在define或全局变量中</p><blockquote><p>后记：突然发现有一处用了<code>public</code>做了变量名/捂脸，但是C语言没有class，所以那里的public是指模型的公共顶点</p></blockquote><div class='spoiler collapsed'>    <div class='spoiler-title'>        简洁版    </div>    <div class='spoiler-content'>        <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> a 8.0</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> pi 3.1415926</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> size 55</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> delta 0.5</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> deltaA 0.02</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> eOffest 0.001</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> dimension 5</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> transparent 0.6</span></span><br><span class="line"><span class="keyword">char</span> shade[]=<span class="string">&quot;.,:;!om0&amp;%@&quot;</span>;</span><br><span class="line"><span class="keyword">float</span> lightSrcDis=<span class="number">5</span>*a;</span><br><span class="line"><span class="keyword">float</span> light[<span class="number">3</span>]=&#123;<span class="number">-2</span>,<span class="number">5</span>,<span class="number">0.8</span>&#125;;</span><br><span class="line"><span class="keyword">int</span> rotateAround[]=&#123;<span class="number">3</span>,<span class="number">4</span>,<span class="number">0</span>,<span class="number">2</span>&#125;;</span><br><span class="line"><span class="keyword">float</span> vAxis[]     =&#123; <span class="number">3</span> , <span class="number">1</span> &#125;;</span><br><span class="line"><span class="keyword">float</span> initialCam[<span class="number">3</span>]=&#123;<span class="number">0</span>,pi*<span class="number">5</span>/<span class="number">24</span>,<span class="number">0</span>&#125;;</span><br><span class="line"><span class="function"><span class="keyword">float</span> <span class="title">calLength</span><span class="params">(<span class="keyword">float</span> *vec,<span class="keyword">int</span> len)</span></span>&#123;</span><br><span class="line">    <span class="keyword">float</span> result=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;len;i++) result+=vec[i]*vec[i];</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sqrt</span>(result);&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">float</span> lightLength=calLength(&amp;light[<span class="number">0</span>],<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) light[i]/=lightLength;</span><br><span class="line">    <span class="keyword">float</span> out_f[size][size],dis[size][size]=&#123;(<span class="keyword">float</span>)<span class="number">4</span>*size&#125;;</span><br><span class="line">    <span class="keyword">float</span> move=(size<span class="number">-1</span>)/<span class="number">2</span>;</span><br><span class="line">    <span class="keyword">int</span> face=((<span class="keyword">int</span>)(<span class="built_in">pow</span>(<span class="number">2</span>,dimension<span class="number">-3</span>)*dimension*(dimension<span class="number">-1</span>)));</span><br><span class="line">    <span class="keyword">int</span> vexNum=(<span class="keyword">int</span>)(<span class="built_in">pow</span>(<span class="number">2</span>,dimension))*dimension;</span><br><span class="line">    <span class="keyword">int</span> vexLink[vexNum],normalLink[<span class="number">6</span>*face];</span><br><span class="line">    <span class="keyword">float</span> vex[vexNum],vexO[vexNum],vexProjected[vexNum],vexTemp[vexNum];</span><br><span class="line">    <span class="keyword">float</span> normalResultSet[<span class="number">6</span>*face];</span><br><span class="line">    <span class="built_in">memset</span>(vexLink,<span class="number">0xff</span>,<span class="keyword">sizeof</span>(vexLink));</span><br><span class="line">    <span class="built_in">memset</span>(normalLink,<span class="number">0xff</span>,<span class="keyword">sizeof</span>(normalLink));</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;vexNum;i++) vex[i]=a;</span><br><span class="line">    vex[dimension]=-a;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">2</span>,difference,sign,i;vexIndex*dimension&lt;vexNum;vexIndex++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(sign=<span class="number">0</span>;sign&lt;dimension;sign++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;vexIndex<span class="number">-1</span>;i++)&#123;difference=<span class="number">0</span>;</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> k=<span class="number">0</span>;k&lt;dimension&amp;&amp;!difference;k++)</span><br><span class="line">                    <span class="keyword">if</span>(sign==k?vex[i*dimension+k]!=<span class="number">-1</span>*vex[(vexIndex<span class="number">-1</span>)*dimension+k]:vex[i*dimension+k]!=vex[(vexIndex<span class="number">-1</span>)*dimension+k]) difference=<span class="number">1</span>;</span><br><span class="line">                <span class="keyword">if</span>(!difference) <span class="keyword">break</span>;</span><br><span class="line">            &#125;<span class="keyword">if</span>(difference) <span class="keyword">break</span>;</span><br><span class="line">        &#125;<span class="keyword">for</span>(<span class="keyword">int</span> k=<span class="number">0</span>;k&lt;dimension;k++) vex[vexIndex*dimension+k]=k==sign?<span class="number">-1</span>*vex[(vexIndex<span class="number">-1</span>)*dimension+k]:vex[(vexIndex<span class="number">-1</span>)*dimension+k];&#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>,n=<span class="number">0</span>;vexIndex*dimension&lt;vexNum;vexIndex++,n=<span class="number">0</span>)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> linkIndex=<span class="number">0</span>;linkIndex&lt;vexIndex;linkIndex++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension;i++)</span><br><span class="line">                <span class="keyword">if</span>(vexLink[linkIndex*dimension+i]==vexIndex) vexLink[vexIndex*dimension+n++]=linkIndex;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=vexIndex+<span class="number">1</span>,difference=<span class="number">0</span>,tooManyDiff,sign;i*dimension&lt;vexNum&amp;&amp;n!=dimension;i++,difference=<span class="number">0</span>)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> k=<span class="number">0</span>;k&lt;dimension&amp;&amp;difference&lt;=<span class="number">1</span>;k++)</span><br><span class="line">                <span class="keyword">if</span>(vex[i*dimension+k]!=vex[vexIndex*dimension+k]) difference++;</span><br><span class="line">            <span class="keyword">if</span>(difference==<span class="number">1</span>) vexLink[vexIndex*dimension+n++]=i;&#125;&#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> cur1=<span class="number">0</span>,normalIndex=<span class="number">0</span>;cur1&lt;vexNum/dimension&amp;&amp;normalIndex!=<span class="number">2</span>*face;cur1+=<span class="number">2</span>)</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> cur2=<span class="number">0</span>,pDiag=<span class="number">0</span>;cur2&lt;vexNum/dimension&amp;&amp;normalIndex!=<span class="number">2</span>*face;cur2++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(cur1==cur2) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension&amp;&amp;!pDiag;i++)</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;dimension&amp;&amp;!pDiag;j++)</span><br><span class="line">                    <span class="keyword">if</span>(vexLink[vexLink[cur1*dimension+i]*dimension+j]==cur2) pDiag=<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span>(pDiag)&#123;</span><br><span class="line">                <span class="keyword">int</span> <span class="keyword">public</span>[dimension<span class="number">-1</span>];</span><br><span class="line">                <span class="built_in">memset</span>(<span class="keyword">public</span>,<span class="number">0xff</span>,<span class="keyword">sizeof</span>(<span class="keyword">public</span>));</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>,k=<span class="number">0</span>;i&lt;dimension&amp;&amp;k&lt;dimension<span class="number">-1</span>;i++)</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;dimension&amp;&amp;k&lt;dimension<span class="number">-1</span>;j++)</span><br><span class="line">                        <span class="keyword">if</span>(vexLink[cur1*dimension+i]==vexLink[cur2*dimension+j]) <span class="keyword">public</span>[k++]=vexLink[cur1*dimension+i];</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;=normalIndex;i++)</span><br><span class="line">                    <span class="keyword">if</span>  (   (cur1==normalLink[i*<span class="number">3</span>]||cur1==normalLink[i*<span class="number">3</span>+<span class="number">1</span>]||cur1==normalLink[i*<span class="number">3</span>+<span class="number">2</span>])</span><br><span class="line">                        &amp;&amp;  (cur2==normalLink[i*<span class="number">3</span>]||cur2==normalLink[i*<span class="number">3</span>+<span class="number">1</span>]||cur2==normalLink[i*<span class="number">3</span>+<span class="number">2</span>]))</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;dimension<span class="number">-1</span>;j++)</span><br><span class="line">                            <span class="keyword">if</span>(<span class="keyword">public</span>[j]==normalLink[i*<span class="number">3</span>]||<span class="keyword">public</span>[j]==normalLink[i*<span class="number">3</span>+<span class="number">1</span>]||<span class="keyword">public</span>[j]==normalLink[i*<span class="number">3</span>+<span class="number">2</span>]) <span class="keyword">public</span>[j]=<span class="number">-1</span>;</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension<span class="number">-1</span>;i++)</span><br><span class="line">                    <span class="keyword">if</span>(<span class="keyword">public</span>[i]!=<span class="number">-1</span>)&#123;</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;<span class="number">3</span>;j++)normalLink[normalIndex*<span class="number">3</span>+j]=j==<span class="number">0</span>?cur1:j==<span class="number">1</span>?cur2:<span class="keyword">public</span>[i];</span><br><span class="line">                        normalIndex++;&#125;&#125;&#125;</span><br><span class="line">    <span class="built_in">memcpy</span>(vexTemp,vex,<span class="keyword">sizeof</span>(vex[<span class="number">0</span>])*vexNum);</span><br><span class="line">    <span class="built_in">memcpy</span>(vexO,vex,<span class="keyword">sizeof</span>(vex[<span class="number">0</span>])*vexNum);</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">float</span> angle=<span class="number">0</span>;;angle+=deltaA)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;size;i++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                out_f[i][j]=<span class="number">-1</span>;</span><br><span class="line">                dis[i][j]=(<span class="keyword">float</span>)(<span class="number">-4.0</span>*size);&#125;</span><br><span class="line">        <span class="built_in">memcpy</span>(vex,vexO,<span class="keyword">sizeof</span>(vexO));</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> rotationIndex=<span class="number">0</span>,r_i=<span class="number">0</span>;rotationIndex&lt;<span class="keyword">sizeof</span>(rotateAround)/<span class="keyword">sizeof</span>(rotateAround[<span class="number">0</span>])/<span class="number">2</span>;rotationIndex++,r_i=<span class="number">0</span>)&#123;</span><br><span class="line">                <span class="built_in">memcpy</span>(vexTemp,vex,<span class="keyword">sizeof</span>(vex));</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension;i++)</span><br><span class="line">                    <span class="keyword">if</span>(i==rotateAround[rotationIndex*<span class="number">2</span>+r_i])</span><br><span class="line">                        vex[vexIndex*dimension+i]=vexTemp[vexIndex*dimension+i]*<span class="built_in">cos</span>(vAxis[rotationIndex]*angle)+vexTemp[vexIndex*dimension+rotateAround[rotationIndex*<span class="number">2</span>+!r_i]]*<span class="built_in">sin</span>(vAxis[rotationIndex]*angle)*(<span class="number">1</span><span class="number">-2</span>*r_i++);&#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;</span><br><span class="line">            <span class="built_in">memcpy</span>(vexTemp,vex,<span class="keyword">sizeof</span>(vex));</span><br><span class="line">            <span class="keyword">int</span> c=!(i^<span class="number">1</span>),b=i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++) vex[vexIndex*dimension+!(i^<span class="number">1</span>)] = vexTemp[vexIndex*dimension+!(i^<span class="number">1</span>)]*<span class="built_in">cos</span>(initialCam[i]) + vexTemp[vexIndex*dimension+(i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>)]*<span class="built_in">sin</span>(initialCam[i]);</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++) vex[vexIndex*dimension+(i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>)] = vexTemp[vexIndex*dimension+(i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>)]*<span class="built_in">cos</span>(initialCam[i]) - vexTemp[vexIndex*dimension+!(i^<span class="number">1</span>)]*<span class="built_in">sin</span>(initialCam[i]);&#125;</span><br><span class="line">        <span class="keyword">float</span> xyBefore[<span class="number">2</span>*vexNum];</span><br><span class="line">        <span class="built_in">memcpy</span>(vexProjected,vex,<span class="keyword">sizeof</span>(vex[<span class="number">0</span>])*vexNum);</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> d=dimension<span class="number">-1</span>;d&gt;=<span class="number">2</span>;d--)&#123;</span><br><span class="line">            <span class="keyword">if</span>(d==<span class="number">2</span>)&#123;</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;vexNum/dimension;i++) <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;=<span class="number">1</span>;j++) xyBefore[i*<span class="number">2</span>+j]=vexProjected[i*dimension+j];</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> normalIndex=<span class="number">0</span>;normalIndex&lt;<span class="number">2</span>*face;normalIndex++)&#123;</span><br><span class="line">                    <span class="keyword">float</span> vec1[<span class="number">3</span>],vec2[<span class="number">3</span>];</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;</span><br><span class="line">                        vec1[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension+i]-vexProjected[normalLink[normalIndex*<span class="number">3</span>+<span class="number">1</span>]*dimension+i];</span><br><span class="line">                        vec2[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension+i]-vexProjected[normalLink[normalIndex*<span class="number">3</span>+<span class="number">2</span>]*dimension+i];&#125;</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) normalResultSet[<span class="number">3</span>*normalIndex+i]=vec1[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">0</span>]*vec2[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">1</span>]-vec1[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">1</span>]*vec2[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">0</span>];</span><br><span class="line">                    <span class="keyword">float</span> normalLength=calLength(&amp;normalResultSet[<span class="number">3</span>*normalIndex],<span class="number">3</span>);</span><br><span class="line">                    <span class="keyword">if</span>(normalLength!=<span class="number">0</span>)</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) normalResultSet[<span class="number">3</span>*normalIndex+i]/=normalLength;</span><br><span class="line">                    <span class="keyword">if</span>(!transparent)&#123;</span><br><span class="line">                        <span class="keyword">float</span> slidedVec[<span class="number">3</span>],slideLength,vexLength=calLength(&amp;vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension],<span class="number">3</span>);</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) slidedVec[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension+i]+normalResultSet[<span class="number">3</span>*normalIndex+i];</span><br><span class="line">                        slideLength=calLength(slidedVec,<span class="number">3</span>);</span><br><span class="line">                        <span class="keyword">if</span>(slideLength&gt;vexLength)</span><br><span class="line">                            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) normalResultSet[<span class="number">3</span>*normalIndex+i]*=<span class="number">-1</span>;&#125;&#125;&#125;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++)</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> axis=<span class="number">0</span>;axis&lt;d;axis++) vexProjected[vexIndex*dimension+axis]=vexProjected[vexIndex*dimension+axis]*lightSrcDis/(lightSrcDis-vexProjected[vexIndex*dimension+d]);&#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> sx=<span class="number">0</span>;sx&lt;size;sx++) <span class="keyword">for</span>(<span class="keyword">int</span> sy=<span class="number">0</span>;sy&lt;size;sy++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> normalIndex=<span class="number">0</span>,inside=<span class="number">1</span>;normalIndex&lt;<span class="number">2</span>*face;normalIndex++,inside=<span class="number">1</span>)&#123;</span><br><span class="line">                <span class="keyword">float</span> tCenter[<span class="number">2</span>]=&#123;<span class="number">0</span>&#125;;</span><br><span class="line">                <span class="keyword">float</span> x[<span class="number">3</span>],y[<span class="number">3</span>],z[<span class="number">3</span>],xb[<span class="number">3</span>],yb[<span class="number">3</span>];</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;</span><br><span class="line">                    x[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>+i]*dimension];</span><br><span class="line">                    tCenter[<span class="number">0</span>]+=x[i];</span><br><span class="line">                    y[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>+i]*dimension+<span class="number">1</span>];</span><br><span class="line">                    tCenter[<span class="number">1</span>]+=y[i];</span><br><span class="line">                    z[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>+i]*dimension+<span class="number">2</span>];</span><br><span class="line">                    xb[i]=xyBefore[normalLink[normalIndex*<span class="number">3</span>+i]*<span class="number">2</span>];</span><br><span class="line">                    yb[i]=xyBefore[normalLink[normalIndex*<span class="number">3</span>+i]*<span class="number">2</span>+<span class="number">1</span>];&#125;</span><br><span class="line">                tCenter[<span class="number">0</span>]/=<span class="number">3</span>;tCenter[<span class="number">1</span>]/=<span class="number">3</span>;</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>&amp;&amp;inside;i++)&#123;</span><br><span class="line">                    <span class="keyword">if</span>(     ((tCenter[<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])&lt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(tCenter[<span class="number">0</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])</span><br><span class="line">                            ?(sy-move-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])&gt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(sx-move-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])</span><br><span class="line">                            :(sy-move-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])&lt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(sx-move-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>]))</span><br><span class="line">                            ||((tCenter[<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])+eOffest&gt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(tCenter[<span class="number">0</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])</span><br><span class="line">                            &amp;&amp;(tCenter[<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])-eOffest&lt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(tCenter[<span class="number">0</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])))</span><br><span class="line">                        inside=<span class="number">0</span>;&#125;</span><br><span class="line">                <span class="keyword">if</span>(inside)&#123;<span class="keyword">float</span> alpha,currentDis,p[<span class="number">4</span>];</span><br><span class="line">                    <span class="keyword">if</span>(transparent) out_f[sy][sx]=out_f[sy][sx]&lt;<span class="number">0</span>?<span class="number">1</span>:out_f[sy][sx]+<span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">else</span>&#123;</span><br><span class="line">                        p[<span class="number">0</span>]= yb[<span class="number">0</span>]*z[<span class="number">1</span>] -yb[<span class="number">0</span>]*z[<span class="number">2</span>] -yb[<span class="number">1</span>]*z[<span class="number">0</span>] +yb[<span class="number">1</span>]*z[<span class="number">2</span>] +yb[<span class="number">2</span>]*z[<span class="number">0</span>] -yb[<span class="number">2</span>]*z[<span class="number">1</span>];</span><br><span class="line">                        p[<span class="number">1</span>]=-xb[<span class="number">0</span>]*z[<span class="number">1</span>] +xb[<span class="number">0</span>]*z[<span class="number">2</span>] +xb[<span class="number">1</span>]*z[<span class="number">0</span>] -xb[<span class="number">1</span>]*z[<span class="number">2</span>] -xb[<span class="number">2</span>]*z[<span class="number">0</span>] +xb[<span class="number">2</span>]*z[<span class="number">1</span>];</span><br><span class="line">                        p[<span class="number">2</span>]= xb[<span class="number">0</span>]*yb[<span class="number">1</span>] -xb[<span class="number">0</span>]*yb[<span class="number">2</span>] -xb[<span class="number">1</span>]*yb[<span class="number">0</span>] +xb[<span class="number">1</span>]*yb[<span class="number">2</span>] +xb[<span class="number">2</span>]*yb[<span class="number">0</span>] -xb[<span class="number">2</span>]*yb[<span class="number">1</span>];</span><br><span class="line">                        p[<span class="number">3</span>]=-xb[<span class="number">0</span>]*yb[<span class="number">1</span>]*z[<span class="number">2</span>] +xb[<span class="number">0</span>]*yb[<span class="number">2</span>]*z[<span class="number">1</span>] +xb[<span class="number">1</span>]*yb[<span class="number">0</span>]*z[<span class="number">2</span>] -xb[<span class="number">1</span>]*yb[<span class="number">2</span>]*z[<span class="number">0</span>] -xb[<span class="number">2</span>]*yb[<span class="number">0</span>]*z[<span class="number">1</span>] +xb[<span class="number">2</span>]*yb[<span class="number">1</span>]*z[<span class="number">0</span>];</span><br><span class="line">                        currentDis=lightSrcDis*(p[<span class="number">0</span>]*(sx-move)+p[<span class="number">1</span>]*(sy-move)+p[<span class="number">3</span>])/(p[<span class="number">0</span>]*(sx-move)+p[<span class="number">1</span>]*(sy-move)-p[<span class="number">2</span>]*lightSrcDis);</span><br><span class="line">                        <span class="keyword">if</span>(currentDis&gt;dis[sy][sx])&#123;</span><br><span class="line">                            alpha=<span class="number">0</span>;</span><br><span class="line">                            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) alpha+=normalResultSet[normalIndex*<span class="number">3</span>+i]*light[i];</span><br><span class="line">                            dis[sy][sx]=currentDis;</span><br><span class="line">                            out_f[sy][sx]=alpha&gt;<span class="number">1</span>||alpha&lt;<span class="number">-1</span>?alpha&gt;<span class="number">1</span>?<span class="number">10</span>:<span class="number">0</span>:(alpha*<span class="number">10</span>+<span class="number">10</span>)*<span class="number">0.5</span>;&#125;&#125;&#125;&#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>,outIndex;i&lt;size;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                <span class="keyword">if</span>(!transparent) outIndex=(<span class="keyword">int</span>)out_f[i][j];</span><br><span class="line">                <span class="keyword">else</span> outIndex=(<span class="keyword">int</span>)(<span class="built_in">pow</span>((<span class="number">1</span><span class="number">-1</span>/(transparent*out_f[i][j]+<span class="number">1</span>)),<span class="number">2</span>)*<span class="number">10</span>);</span><br><span class="line">                out_f[i][j]!=<span class="number">-1</span>?<span class="built_in">printf</span>(<span class="string">&quot;%c%c&quot;</span>,shade[outIndex],shade[outIndex]):<span class="built_in">printf</span>(<span class="string">&quot;  &quot;</span>);</span><br><span class="line">            &#125;<span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        &#125;<span class="built_in">printf</span>(<span class="string">&quot;\x1b[2J&quot;</span>);<span class="built_in">printf</span>(<span class="string">&quot;\x1b[H&quot;</span>);usleep(<span class="number">30000</span>);&#125;&#125;</span><br></pre></td></tr></table></figure>    </div></div><p>以上代码只是看起来比较好看，以下是以下是带注释与测试代码的源码，环境是win，linux的话把清屏部分的代码稍微改改即可，可以参考前文提到的之前的博客<a href="https://kyriota.com/2021/04/19/Render3D-ObjectInTerminal/">Render 3D Object In Terminal</a></p><div class='spoiler collapsed'>    <div class='spoiler-title'>        注释版    </div>    <div class='spoiler-content'>        <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> a 8.0<span class="comment">//立方体边长</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> pi 3.1415926</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> size 55<span class="comment">//荧幕大小</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> delta 0.5<span class="comment">//遍历方块的步长</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> deltaA 0.02<span class="comment">//物体旋转速度</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> eOffest 0.001<span class="comment">//判定点是否在平面内时的方程阈值</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> dimension 5<span class="comment">//维数</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> transparent 0<span class="comment">//是否使用透明材质，同时是透明的倍率，四维推荐0.75，五维推荐0.6</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//坐标系说明：x向右，y向下，z向前</span></span><br><span class="line"><span class="keyword">char</span> shade[]=<span class="string">&quot;.,:;!om0&amp;%@&quot;</span>;<span class="comment">//阴影字符集十一个，便于选取</span></span><br><span class="line"><span class="keyword">float</span> lightSrcDis=<span class="number">5</span>*a;<span class="comment">//透视投影时的光源距离</span></span><br><span class="line"><span class="keyword">float</span> light[<span class="number">3</span>]=&#123;<span class="number">-2</span>,<span class="number">5</span>,<span class="number">0.8</span>&#125;;<span class="comment">//平行光向量，选取了一个比较舒服的打光</span></span><br><span class="line"><span class="keyword">int</span> rotateAround[]=&#123;<span class="number">3</span>,<span class="number">4</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">0</span>,<span class="number">2</span>&#125;;<span class="comment">//确定旋转基准，指定参与旋转的方向，两个一组，一组中小的在前大的在后</span></span><br><span class="line"><span class="keyword">float</span> vAxis[]     =&#123; <span class="number">3</span> , <span class="number">2</span> , <span class="number">1</span> &#125;;<span class="comment">//对应上边的配速倍率</span></span><br><span class="line"><span class="keyword">float</span> initialCam[<span class="number">3</span>]=&#123;<span class="number">0</span>,pi*<span class="number">5</span>/<span class="number">24</span>,<span class="number">0</span>&#125;;<span class="comment">//水平旋转 前后倾斜 左右倾斜，注意用弧度角，如 pi/4</span></span><br><span class="line"><span class="comment">/*initialObj[已删除]：正方体初始摆放的角度；initialObj：摄像机初始摆放的角度</span></span><br><span class="line"><span class="comment">旋转顺序：initialObj → rotateAround → initialCam</span></span><br><span class="line"><span class="comment">initialObj感觉用处不大，删了算了*/</span></span><br><span class="line"><span class="comment">/*计算向量模长</span></span><br><span class="line"><span class="comment">--------------------</span></span><br><span class="line"><span class="comment">Par:</span></span><br><span class="line"><span class="comment">    vec[]：向量</span></span><br><span class="line"><span class="comment">    len：元素个数</span></span><br><span class="line"><span class="comment">--------------------*/</span></span><br><span class="line"><span class="function"><span class="keyword">float</span> <span class="title">calLength</span><span class="params">(<span class="keyword">float</span> *vec,<span class="keyword">int</span> len)</span></span>&#123;</span><br><span class="line">    <span class="keyword">float</span> result=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;len;i++) result+=vec[i]*vec[i];</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sqrt</span>(result);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    COORD pos=&#123;<span class="number">0</span>,<span class="number">0</span>&#125;;<span class="comment">//光标回零</span></span><br><span class="line">    <span class="keyword">float</span> lightLength=calLength(&amp;light[<span class="number">0</span>],<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) light[i]/=lightLength;<span class="comment">//单位化平行光向量</span></span><br><span class="line">    <span class="keyword">float</span> out_f[size][size],dis[size][size]=&#123;(<span class="keyword">float</span>)<span class="number">4</span>*size&#125;;<span class="comment">//记录亮度，映射到字符集 | 记录三维投影后到屏幕最小距离，由此判断遮罩关系</span></span><br><span class="line">    <span class="keyword">float</span> move=(size<span class="number">-1</span>)/<span class="number">2</span>;<span class="comment">//从坐标原点(屏幕左上角)偏移到屏幕中心所需的偏移量</span></span><br><span class="line">    <span class="keyword">int</span> face=((<span class="keyword">int</span>)(<span class="built_in">pow</span>(<span class="number">2</span>,dimension<span class="number">-3</span>)*dimension*(dimension<span class="number">-1</span>)));</span><br><span class="line">    <span class="comment">/*  面数公式解释</span></span><br><span class="line"><span class="comment">        每个 n 维正方体中的每个 m 维边界都会被 n-m 个 m+1 维边界共用。比如正六面体中，每条棱被两个面共用，而每个顶点被三条棱共用。</span></span><br><span class="line"><span class="comment">        这样算来，对于任意一个 n 维的正方体，计算其不重复的二维正方形表面的数量应该为：</span></span><br><span class="line"><span class="comment">          6x8x10x...x(2n)/(2*3*...*(n-2))</span></span><br><span class="line"><span class="comment">        =(2^(n-2))*(n!)/2/(n-2)!</span></span><br><span class="line"><span class="comment">        =(2^(n-3))*n*(n-1)*/</span></span><br><span class="line">    <span class="keyword">int</span> vexNum=(<span class="keyword">int</span>)(<span class="built_in">pow</span>(<span class="number">2</span>,dimension))*dimension;<span class="comment">//顶点个数*维数</span></span><br><span class="line">    <span class="keyword">int</span> vexLink[vexNum],normalLink[<span class="number">6</span>*face];<span class="comment">//相邻顶点集，如：三维中0号顶点与1，3，7相邻，则前dimension个元素为1，3，7 | 同理，法向量对应三角形面的三个vex的index</span></span><br><span class="line">    <span class="keyword">float</span> vex[vexNum],vexO[vexNum],vexProjected[vexNum],vexTemp[vexNum];<span class="comment">//当前顶点坐标 | 原始顶点坐标 | 投影后顶点坐标 | 临时</span></span><br><span class="line">    <span class="keyword">float</span> normalResultSet[<span class="number">6</span>*face];<span class="comment">//法向量结果集</span></span><br><span class="line">    <span class="built_in">memset</span>(vexLink,<span class="number">0xff</span>,<span class="keyword">sizeof</span>(vexLink));<span class="comment">//初始化</span></span><br><span class="line">    <span class="built_in">memset</span>(normalLink,<span class="number">0xff</span>,<span class="keyword">sizeof</span>(normalLink));</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;vexNum;i++) vex[i]=a;</span><br><span class="line">    vex[dimension]=-a;<span class="comment">//这个算法需要有前车之鉴</span></span><br><span class="line">    <span class="comment">/*得到顶点坐标</span></span><br><span class="line"><span class="comment">    用上一向量一个元素变号，使其与之前任一向量都不同，该向量即要找的下一个顶点</span></span><br><span class="line"><span class="comment">    向量不同 = 元素至少有一个不相同*/</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">2</span>,difference,sign,i;vexIndex*dimension&lt;vexNum;vexIndex++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(sign=<span class="number">0</span>;sign&lt;dimension;sign++)&#123;<span class="comment">//遍历尝试改变vexIndex-1向量的每一单个元素正负号，执行条件：d为假</span></span><br><span class="line">            <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;vexIndex<span class="number">-1</span>;i++)&#123;difference=<span class="number">0</span>;<span class="comment">//遍历每个向量，执行条件：d为真，每次初始化d为假</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> k=<span class="number">0</span>;k&lt;dimension&amp;&amp;!difference;k++)<span class="comment">//遍历比较vexIndex-1向量与之前向量的元素</span></span><br><span class="line">                    <span class="keyword">if</span>(sign==k?vex[i*dimension+k]!=<span class="number">-1</span>*vex[(vexIndex<span class="number">-1</span>)*dimension+k]:vex[i*dimension+k]!=vex[(vexIndex<span class="number">-1</span>)*dimension+k]) difference=<span class="number">1</span>;</span><br><span class="line">                <span class="keyword">if</span>(!difference) <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(difference) <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> k=<span class="number">0</span>;k&lt;dimension;k++) vex[vexIndex*dimension+k]=k==sign?<span class="number">-1</span>*vex[(vexIndex<span class="number">-1</span>)*dimension+k]:vex[(vexIndex<span class="number">-1</span>)*dimension+k];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/*测试代码：打印vex</span></span><br><span class="line"><span class="comment">    for(int i=0;i&lt;vexNum/dimension;i++)&#123;</span></span><br><span class="line"><span class="comment">        if(i&lt;10) printf(&quot; %d= &quot;,i);</span></span><br><span class="line"><span class="comment">        else printf(&quot;%d= &quot;,i);</span></span><br><span class="line"><span class="comment">        for(int j=0;j&lt;dimension;j++)&#123;</span></span><br><span class="line"><span class="comment">            if(vex[i*dimension+j]&gt;=0)printf(&quot; %.0f,&quot;,vex[i*dimension+j]);</span></span><br><span class="line"><span class="comment">            else printf(&quot;%.0f,&quot;,vex[i*dimension+j]);</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        printf(&quot;\n&quot;);</span></span><br><span class="line"><span class="comment">    &#125;</span></span><br><span class="line"><span class="comment">    printf(&quot;Vex Above\n&quot;);</span></span><br><span class="line"><span class="comment">    //*/</span></span><br><span class="line">    <span class="comment">/*得到相邻顶点，相邻顶点在vex中的index存储到vexLink中</span></span><br><span class="line"><span class="comment">    先在vexLink中已经建立link的部分找有没有之前link到过当前正在建立link的点，有则直接加入当前的link</span></span><br><span class="line"><span class="comment">    linkIndex：这是第几个已经建立link的点</span></span><br><span class="line"><span class="comment">    在link数量达到dimension之前，用当前向量与之前的向量作比较，如果_只_更_改_一_个_符_号_就可以匹配，则加入link，直到当前向量的link满dimension个*/</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>,n=<span class="number">0</span>;vexIndex*dimension&lt;vexNum;vexIndex++,n=<span class="number">0</span>)&#123;<span class="comment">//vexIndex：当前正在建立link的点 | n：已经建立到第几个link了</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> linkIndex=<span class="number">0</span>;linkIndex&lt;vexIndex;linkIndex++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension;i++)</span><br><span class="line">                <span class="keyword">if</span>(vexLink[linkIndex*dimension+i]==vexIndex) vexLink[vexIndex*dimension+n++]=linkIndex;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=vexIndex+<span class="number">1</span>,difference=<span class="number">0</span>,tooManyDiff,sign;i*dimension&lt;vexNum&amp;&amp;n!=dimension;i++,difference=<span class="number">0</span>)&#123;<span class="comment">//遍历当前向量之后的每个向量 || difference：存在不一样的元素的数量</span></span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> k=<span class="number">0</span>;k&lt;dimension&amp;&amp;difference&lt;=<span class="number">1</span>;k++)<span class="comment">//遍历被比较向量中的每个元素</span></span><br><span class="line">                <span class="keyword">if</span>(vex[i*dimension+k]!=vex[vexIndex*dimension+k]) difference++;</span><br><span class="line">            <span class="keyword">if</span>(difference==<span class="number">1</span>) vexLink[vexIndex*dimension+n++]=i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/*测试代码：打印vexLink</span></span><br><span class="line"><span class="comment">    for(int i=0;i&lt;vexNum/dimension;i++)&#123;</span></span><br><span class="line"><span class="comment">        if(i&lt;10) printf(&quot; %d= &quot;,i);</span></span><br><span class="line"><span class="comment">        else printf(&quot;%d= &quot;,i);</span></span><br><span class="line"><span class="comment">        for(int j=0;j&lt;dimension;j++)&#123;</span></span><br><span class="line"><span class="comment">            if(vexLink[i*dimension+j]&gt;=10)printf(&quot;%d, &quot;,vexLink[i*dimension+j]);</span></span><br><span class="line"><span class="comment">            else printf(&quot; %d ,&quot;,vexLink[i*dimension+j]);</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        printf(&quot;\n&quot;);</span></span><br><span class="line"><span class="comment">    &#125;</span></span><br><span class="line"><span class="comment">    printf(&quot;vexLink Above\n&quot;);</span></span><br><span class="line"><span class="comment">    //*/</span></span><br><span class="line">    <span class="comment">//得到面对应的三个点</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> cur1=<span class="number">0</span>,normalIndex=<span class="number">0</span>;cur1&lt;vexNum/dimension&amp;&amp;normalIndex!=<span class="number">2</span>*face;cur1+=<span class="number">2</span>)<span class="comment">//cur n 表示指向第几个顶点，normalIndex表这是已经找到的第几个三角形</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> cur2=<span class="number">0</span>,pDiag=<span class="number">0</span>;cur2&lt;vexNum/dimension&amp;&amp;normalIndex!=<span class="number">2</span>*face;cur2++)&#123;<span class="comment">//pDiag(Plane Diagonal)表是否为平面对角点，即cur1通过Link索引，在第二次可以索引到cur2</span></span><br><span class="line">            <span class="keyword">if</span>(cur1==cur2) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension&amp;&amp;!pDiag;i++)<span class="comment">//第一次索引的偏移</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;dimension&amp;&amp;!pDiag;j++)<span class="comment">//第二次索引的偏移</span></span><br><span class="line">                    <span class="keyword">if</span>(vexLink[vexLink[cur1*dimension+i]*dimension+j]==cur2) pDiag=<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span>(pDiag)&#123;</span><br><span class="line">                <span class="keyword">int</span> <span class="keyword">public</span>[dimension<span class="number">-1</span>];</span><br><span class="line">                <span class="built_in">memset</span>(<span class="keyword">public</span>,<span class="number">0xff</span>,<span class="keyword">sizeof</span>(<span class="keyword">public</span>));</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>,k=<span class="number">0</span>;i&lt;dimension&amp;&amp;k&lt;dimension<span class="number">-1</span>;i++)</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;dimension&amp;&amp;k&lt;dimension<span class="number">-1</span>;j++)</span><br><span class="line">                        <span class="keyword">if</span>(vexLink[cur1*dimension+i]==vexLink[cur2*dimension+j]) <span class="keyword">public</span>[k++]=vexLink[cur1*dimension+i];</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;=normalIndex;i++)<span class="comment">//i指向normalLink中的向量</span></span><br><span class="line">                    <span class="keyword">if</span>  (   (cur1==normalLink[i*<span class="number">3</span>]||cur1==normalLink[i*<span class="number">3</span>+<span class="number">1</span>]||cur1==normalLink[i*<span class="number">3</span>+<span class="number">2</span>])<span class="comment">//cur1与第i个normalLink中存在元素重合？</span></span><br><span class="line">                        &amp;&amp;  (cur2==normalLink[i*<span class="number">3</span>]||cur2==normalLink[i*<span class="number">3</span>+<span class="number">1</span>]||cur2==normalLink[i*<span class="number">3</span>+<span class="number">2</span>]))<span class="comment">//cur2与第i个normalLink中存在元素重合？</span></span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;dimension<span class="number">-1</span>;j++)</span><br><span class="line">                            <span class="keyword">if</span>(<span class="keyword">public</span>[j]==normalLink[i*<span class="number">3</span>]||<span class="keyword">public</span>[j]==normalLink[i*<span class="number">3</span>+<span class="number">1</span>]||<span class="keyword">public</span>[j]==normalLink[i*<span class="number">3</span>+<span class="number">2</span>]) <span class="keyword">public</span>[j]=<span class="number">-1</span>;<span class="comment">//public[j]与第i个normalLink中存在元素重合？</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension<span class="number">-1</span>;i++)</span><br><span class="line">                    <span class="keyword">if</span>(<span class="keyword">public</span>[i]!=<span class="number">-1</span>)&#123;</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;<span class="number">3</span>;j++)normalLink[normalIndex*<span class="number">3</span>+j]=j==<span class="number">0</span>?cur1:j==<span class="number">1</span>?cur2:<span class="keyword">public</span>[i];</span><br><span class="line">                        normalIndex++;</span><br><span class="line">                    &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    <span class="comment">/*测试代码：打印normalLink</span></span><br><span class="line"><span class="comment">    for(int i=0;i&lt;2*face;i++)&#123;</span></span><br><span class="line"><span class="comment">        //if(i&lt;10) printf(&quot; %d= &quot;,i);</span></span><br><span class="line"><span class="comment">        //else printf(&quot;%d= &quot;,i);</span></span><br><span class="line"><span class="comment">        for(int j=0;j&lt;3;j++)&#123;</span></span><br><span class="line"><span class="comment">            if(normalLink[i*3+j]&gt;=10) printf(&quot;%d, &quot;,normalLink[i*3+j]);</span></span><br><span class="line"><span class="comment">            else printf(&quot;0%d, &quot;,normalLink[i*3+j]);</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        printf(&quot;\n&quot;);</span></span><br><span class="line"><span class="comment">    &#125;</span></span><br><span class="line"><span class="comment">    printf(&quot;normalLink Above\n&quot;);</span></span><br><span class="line"><span class="comment">    //*/</span></span><br><span class="line">    <span class="built_in">memcpy</span>(vexTemp,vex,<span class="keyword">sizeof</span>(vex[<span class="number">0</span>])*vexNum);</span><br><span class="line">    <span class="built_in">memcpy</span>(vexO,vex,<span class="keyword">sizeof</span>(vex[<span class="number">0</span>])*vexNum);</span><br><span class="line">    <span class="comment">//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">float</span> angle=<span class="number">0</span>;;angle+=deltaA)&#123;<span class="comment">//大循环</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;size;i++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                out_f[i][j]=<span class="number">-1</span>;</span><br><span class="line">                dis[i][j]=(<span class="keyword">float</span>)(<span class="number">-4.0</span>*size);</span><br><span class="line">            &#125;<span class="comment">//初始化</span></span><br><span class="line">        <span class="built_in">memcpy</span>(vex,vexO,<span class="keyword">sizeof</span>(vexO));<span class="comment">//还原vex到初始状态，否则带角加速度</span></span><br><span class="line">        <span class="comment">//计算向量与旋转矩阵相乘，0太多，没必要搞真正的矩阵乘法</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> rotationIndex=<span class="number">0</span>,r_i=<span class="number">0</span>;rotationIndex&lt;<span class="keyword">sizeof</span>(rotateAround)/<span class="keyword">sizeof</span>(rotateAround[<span class="number">0</span>])/<span class="number">2</span>;rotationIndex++,r_i=<span class="number">0</span>)&#123;<span class="comment">//限定旋转次数；rotateAround每行2个元素，j的作用是选取他们</span></span><br><span class="line">                <span class="built_in">memcpy</span>(vexTemp,vex,<span class="keyword">sizeof</span>(vex));</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;dimension;i++)<span class="comment">//遍历所有的轴</span></span><br><span class="line">                    <span class="keyword">if</span>(i==rotateAround[rotationIndex*<span class="number">2</span>+r_i])<span class="comment">//当前轴是被旋转轴时，结果符合旋转矩阵计算结果</span></span><br><span class="line">                        vex[vexIndex*dimension+i]=vexTemp[vexIndex*dimension+i]*<span class="built_in">cos</span>(vAxis[rotationIndex]*angle)+vexTemp[vexIndex*dimension+rotateAround[rotationIndex*<span class="number">2</span>+!r_i]]*<span class="built_in">sin</span>(vAxis[rotationIndex]*angle)*(<span class="number">1</span><span class="number">-2</span>*r_i++);</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;<span class="comment">//摄像机旋转</span></span><br><span class="line">            <span class="built_in">memcpy</span>(vexTemp,vex,<span class="keyword">sizeof</span>(vex));</span><br><span class="line">            <span class="keyword">int</span> c=!(i^<span class="number">1</span>),b=i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++) vex[vexIndex*dimension+!(i^<span class="number">1</span>)] = vexTemp[vexIndex*dimension+!(i^<span class="number">1</span>)]*<span class="built_in">cos</span>(initialCam[i]) + vexTemp[vexIndex*dimension+(i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>)]*<span class="built_in">sin</span>(initialCam[i]);</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++) vex[vexIndex*dimension+(i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>)] = vexTemp[vexIndex*dimension+(i&lt;=<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>)]*<span class="built_in">cos</span>(initialCam[i]) - vexTemp[vexIndex*dimension+!(i^<span class="number">1</span>)]*<span class="built_in">sin</span>(initialCam[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//以透视投影降维</span></span><br><span class="line">        <span class="keyword">float</span> xyBefore[<span class="number">2</span>*vexNum];<span class="comment">//备份x，y轴用于计算平面方程</span></span><br><span class="line">        <span class="built_in">memcpy</span>(vexProjected,vex,<span class="keyword">sizeof</span>(vex[<span class="number">0</span>])*vexNum);</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> d=dimension<span class="number">-1</span>;d&gt;=<span class="number">2</span>;d--)&#123;<span class="comment">//d+1 = 当前维数</span></span><br><span class="line">            <span class="keyword">if</span>(d==<span class="number">2</span>)&#123;<span class="comment">//趁还在三维的时候先把法向量算了，不然反光的面就也透视了</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;vexNum/dimension;i++) <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;=<span class="number">1</span>;j++) xyBefore[i*<span class="number">2</span>+j]=vexProjected[i*dimension+j];</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> normalIndex=<span class="number">0</span>;normalIndex&lt;<span class="number">2</span>*face;normalIndex++)&#123;</span><br><span class="line">                    <span class="keyword">float</span> vec1[<span class="number">3</span>],vec2[<span class="number">3</span>];</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;</span><br><span class="line">                        vec1[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension+i]-vexProjected[normalLink[normalIndex*<span class="number">3</span>+<span class="number">1</span>]*dimension+i];</span><br><span class="line">                        vec2[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension+i]-vexProjected[normalLink[normalIndex*<span class="number">3</span>+<span class="number">2</span>]*dimension+i];</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) normalResultSet[<span class="number">3</span>*normalIndex+i]=vec1[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">0</span>]*vec2[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">1</span>]-vec1[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">1</span>]*vec2[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">0</span>];</span><br><span class="line">                    <span class="keyword">float</span> normalLength=calLength(&amp;normalResultSet[<span class="number">3</span>*normalIndex],<span class="number">3</span>);</span><br><span class="line">                    <span class="keyword">if</span>(normalLength!=<span class="number">0</span>)</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) normalResultSet[<span class="number">3</span>*normalIndex+i]/=normalLength;</span><br><span class="line">                    <span class="keyword">if</span>(!transparent)&#123;<span class="comment">//以全局质心计算，使法向量向外</span></span><br><span class="line">                        <span class="keyword">float</span> slidedVec[<span class="number">3</span>],slideLength,vexLength=calLength(&amp;vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension],<span class="number">3</span>);</span><br><span class="line">                        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) slidedVec[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>]*dimension+i]+normalResultSet[<span class="number">3</span>*normalIndex+i];</span><br><span class="line">                        slideLength=calLength(slidedVec,<span class="number">3</span>);</span><br><span class="line">                        <span class="keyword">if</span>(slideLength&gt;vexLength)</span><br><span class="line">                            <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) normalResultSet[<span class="number">3</span>*normalIndex+i]*=<span class="number">-1</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> vexIndex=<span class="number">0</span>;vexIndex&lt;vexNum/dimension;vexIndex++)<span class="comment">//vexIndex：第几个向量，axis：该向量的第几个元素</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> axis=<span class="number">0</span>;axis&lt;d;axis++) vexProjected[vexIndex*dimension+axis]=vexProjected[vexIndex*dimension+axis]*lightSrcDis/(lightSrcDis-vexProjected[vexIndex*dimension+d]);</span><br><span class="line">            <span class="comment">/*测试代码：打印vexProjected</span></span><br><span class="line"><span class="comment">            for(int i=0;i&lt;vexNum/dimension&amp;&amp;d&gt;2;i++)&#123;</span></span><br><span class="line"><span class="comment">                if(i&lt;10) printf(&quot; %d= &quot;,i);</span></span><br><span class="line"><span class="comment">                else printf(&quot;%d= &quot;,i);</span></span><br><span class="line"><span class="comment">                for(int j=0;j&lt;dimension;j++)&#123;</span></span><br><span class="line"><span class="comment">                    if(vexProjected[i*dimension+j]&gt;=0)printf(&quot; %.2f,&quot;,vexProjected[i*dimension+j]);</span></span><br><span class="line"><span class="comment">                    else printf(&quot;%.2f,&quot;,vexProjected[i*dimension+j]);</span></span><br><span class="line"><span class="comment">                &#125;</span></span><br><span class="line"><span class="comment">                printf(&quot;\n&quot;);</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">            if(d&gt;2) printf(&quot;VexPorjected Above , dimension = %d\n&quot;,d);</span></span><br><span class="line"><span class="comment">            //*/</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//输出到out_f[]</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> sx=<span class="number">0</span>;sx&lt;size;sx++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> sy=<span class="number">0</span>;sy&lt;size;sy++)</span><br><span class="line">                <span class="keyword">for</span>(<span class="keyword">int</span> normalIndex=<span class="number">0</span>,inside=<span class="number">1</span>;normalIndex&lt;<span class="number">2</span>*face;normalIndex++,inside=<span class="number">1</span>)&#123;</span><br><span class="line">                    <span class="keyword">float</span> tCenter[<span class="number">2</span>]=&#123;<span class="number">0</span>&#125;;<span class="comment">//当前三角形质心</span></span><br><span class="line">                    <span class="keyword">float</span> x[<span class="number">3</span>],y[<span class="number">3</span>],z[<span class="number">3</span>],xb[<span class="number">3</span>],yb[<span class="number">3</span>];</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;</span><br><span class="line">                        x[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>+i]*dimension];</span><br><span class="line">                        tCenter[<span class="number">0</span>]+=x[i];</span><br><span class="line">                        y[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>+i]*dimension+<span class="number">1</span>];</span><br><span class="line">                        tCenter[<span class="number">1</span>]+=y[i];</span><br><span class="line">                        z[i]=vexProjected[normalLink[normalIndex*<span class="number">3</span>+i]*dimension+<span class="number">2</span>];</span><br><span class="line">                        xb[i]=xyBefore[normalLink[normalIndex*<span class="number">3</span>+i]*<span class="number">2</span>];</span><br><span class="line">                        yb[i]=xyBefore[normalLink[normalIndex*<span class="number">3</span>+i]*<span class="number">2</span>+<span class="number">1</span>];</span><br><span class="line">                    &#125;</span><br><span class="line">                    tCenter[<span class="number">0</span>]/=<span class="number">3</span>;</span><br><span class="line">                    tCenter[<span class="number">1</span>]/=<span class="number">3</span>;</span><br><span class="line">                    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>&amp;&amp;inside;i++)&#123;<span class="comment">//判断当前屏幕的点是否在三角形范围内</span></span><br><span class="line">                        <span class="keyword">if</span>(     ((tCenter[<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])&lt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(tCenter[<span class="number">0</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])</span><br><span class="line">                                ?(sy-move-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])&gt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(sx-move-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])</span><br><span class="line">                                :(sy-move-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])&lt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(sx-move-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>]))</span><br><span class="line">                                ||((tCenter[<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])+eOffest&gt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(tCenter[<span class="number">0</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])</span><br><span class="line">                                &amp;&amp;(tCenter[<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(x[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])-eOffest&lt;(y[i==<span class="number">0</span>?<span class="number">2</span>:i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>]-y[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])*(tCenter[<span class="number">0</span>]-x[i==<span class="number">0</span>?<span class="number">1</span>:i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>])))</span><br><span class="line">                            inside=<span class="number">0</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span>(inside)&#123;</span><br><span class="line">                        <span class="comment">/*测试代码：方程阈值检验</span></span><br><span class="line"><span class="comment">                        if(sx==0)&#123;</span></span><br><span class="line"><span class="comment">                            for(int i=0;i&lt;3&amp;&amp;inside;i++)&#123;</span></span><br><span class="line"><span class="comment">                                double b=(tCenter[1]-y[i==0?1:i==1?0:0])*(x[i==0?2:i==1?2:1]-x[i==0?1:i==1?0:0]);</span></span><br><span class="line"><span class="comment">                                double c=(y[i==0?2:i==1?2:1]-y[i==0?1:i==1?0:0])*(tCenter[0]-x[i==0?1:i==1?0:0]);\</span></span><br><span class="line"><span class="comment">                                double d=b-c;</span></span><br><span class="line"><span class="comment">                            &#125;</span></span><br><span class="line"><span class="comment">                        &#125;</span></span><br><span class="line"><span class="comment">                        //*/</span></span><br><span class="line">                        <span class="keyword">float</span> alpha,currentDis,p[<span class="number">4</span>];</span><br><span class="line">                        <span class="keyword">if</span>(transparent) out_f[sy][sx]=out_f[sy][sx]&lt;<span class="number">0</span>?<span class="number">1</span>:out_f[sy][sx]+<span class="number">1</span>;</span><br><span class="line">                        <span class="keyword">else</span>&#123;<span class="comment">//求三角形平面方程算出点到屏幕距离</span></span><br><span class="line">                            p[<span class="number">0</span>]= yb[<span class="number">0</span>]*z[<span class="number">1</span>] -yb[<span class="number">0</span>]*z[<span class="number">2</span>] -yb[<span class="number">1</span>]*z[<span class="number">0</span>] +yb[<span class="number">1</span>]*z[<span class="number">2</span>] +yb[<span class="number">2</span>]*z[<span class="number">0</span>] -yb[<span class="number">2</span>]*z[<span class="number">1</span>];</span><br><span class="line">                            p[<span class="number">1</span>]=-xb[<span class="number">0</span>]*z[<span class="number">1</span>] +xb[<span class="number">0</span>]*z[<span class="number">2</span>] +xb[<span class="number">1</span>]*z[<span class="number">0</span>] -xb[<span class="number">1</span>]*z[<span class="number">2</span>] -xb[<span class="number">2</span>]*z[<span class="number">0</span>] +xb[<span class="number">2</span>]*z[<span class="number">1</span>];</span><br><span class="line">                            p[<span class="number">2</span>]= xb[<span class="number">0</span>]*yb[<span class="number">1</span>] -xb[<span class="number">0</span>]*yb[<span class="number">2</span>] -xb[<span class="number">1</span>]*yb[<span class="number">0</span>] +xb[<span class="number">1</span>]*yb[<span class="number">2</span>] +xb[<span class="number">2</span>]*yb[<span class="number">0</span>] -xb[<span class="number">2</span>]*yb[<span class="number">1</span>];</span><br><span class="line">                            p[<span class="number">3</span>]=-xb[<span class="number">0</span>]*yb[<span class="number">1</span>]*z[<span class="number">2</span>] +xb[<span class="number">0</span>]*yb[<span class="number">2</span>]*z[<span class="number">1</span>] +xb[<span class="number">1</span>]*yb[<span class="number">0</span>]*z[<span class="number">2</span>] -xb[<span class="number">1</span>]*yb[<span class="number">2</span>]*z[<span class="number">0</span>] -xb[<span class="number">2</span>]*yb[<span class="number">0</span>]*z[<span class="number">1</span>] +xb[<span class="number">2</span>]*yb[<span class="number">1</span>]*z[<span class="number">0</span>];</span><br><span class="line">                            currentDis=lightSrcDis*(p[<span class="number">0</span>]*(sx-move)+p[<span class="number">1</span>]*(sy-move)+p[<span class="number">3</span>])/(p[<span class="number">0</span>]*(sx-move)+p[<span class="number">1</span>]*(sy-move)-p[<span class="number">2</span>]*lightSrcDis);</span><br><span class="line">                            <span class="keyword">if</span>(currentDis&gt;dis[sy][sx])&#123;</span><br><span class="line">                                alpha=<span class="number">0</span>;</span><br><span class="line">                                <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++) alpha+=normalResultSet[normalIndex*<span class="number">3</span>+i]*light[i];</span><br><span class="line">                                dis[sy][sx]=currentDis;</span><br><span class="line">                                out_f[sy][sx]=alpha&gt;<span class="number">1</span>||alpha&lt;<span class="number">-1</span>?alpha&gt;<span class="number">1</span>?<span class="number">10</span>:<span class="number">0</span>:(alpha*<span class="number">10</span>+<span class="number">10</span>)*<span class="number">0.5</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">        <span class="comment">///*输出到终端</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>,outIndex;i&lt;size;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                <span class="keyword">if</span>(!transparent) outIndex=(<span class="keyword">int</span>)out_f[i][j];</span><br><span class="line">                <span class="keyword">else</span> outIndex=(<span class="keyword">int</span>)(<span class="built_in">pow</span>((<span class="number">1</span><span class="number">-1</span>/(transparent*out_f[i][j]+<span class="number">1</span>)),<span class="number">2</span>)*<span class="number">10</span>);</span><br><span class="line">                out_f[i][j]!=<span class="number">-1</span>?<span class="built_in">printf</span>(<span class="string">&quot;%c%c%c&quot;</span>,shade[outIndex],shade[outIndex],shade[outIndex]):<span class="built_in">printf</span>(<span class="string">&quot;   &quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        Sleep(<span class="number">15</span>);</span><br><span class="line">        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;Meet Hypercubes~ O(∩_∩)O&lt;/p&gt;
&lt;p&gt;🦎语言渲染n维立方体至终端，那真是非常amazing啊&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/4Hypercube-transparent.gif&quot; height=&quot;200&quot; width=&quot;200&quot; /&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="摸鱼" scheme="https://kyriota.github.io/categories/%E6%91%B8%E9%B1%BC/"/>
    
    
  </entry>
  
  <entry>
    <title>Render 3D Object In Terminal</title>
    <link href="https://kyriota.github.io/2021/04/19/Render3D-ObjectInTerminal/"/>
    <id>https://kyriota.github.io/2021/04/19/Render3D-ObjectInTerminal/</id>
    <published>2021-04-19T05:24:55.000Z</published>
    <updated>2023-01-03T16:53:17.242Z</updated>
    
    <content type="html"><![CDATA[<p>逛b站看到会转的甜甜圈，有点上头，自己拿蜥语言做个立方体玩玩</p><p><img src="/images/图形学HelloWorld.gif" height="200" width="200" /></p><span id="more"></span><h1 id="render-3d-object-in-terminal">Render 3D Object In Terminal</h1><p>直接看代码，注释比较详细</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> size 51<span class="comment">//规定屏幕大小</span></span></span><br><span class="line"><span class="comment">////cal_r用来算个模长</span></span><br><span class="line"><span class="function"><span class="keyword">float</span> <span class="title">cal_r</span><span class="params">(<span class="keyword">float</span> x,<span class="keyword">float</span> y,<span class="keyword">float</span> z)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sqrt</span>(x*x+y*y+z*z);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">char</span> shade[]=<span class="string">&quot;.,;!o*m0&amp;%@&quot;</span>;<span class="comment">//阴影字符集</span></span><br><span class="line"><span class="keyword">int</span> out_i[size][size];<span class="comment">//记录亮度，映射到字符集</span></span><br><span class="line"><span class="keyword">float</span> dyn=<span class="number">4.0</span>;<span class="comment">//开合幅度</span></span><br><span class="line"><span class="keyword">float</span> a=<span class="number">8.0</span>;<span class="comment">//立方体边长</span></span><br><span class="line"><span class="keyword">float</span> delta=<span class="number">0.75</span>;<span class="comment">//遍历立方体的步长</span></span><br><span class="line"><span class="keyword">float</span> move=(size<span class="number">-1</span>)/<span class="number">2</span>;<span class="comment">//从坐标原点偏移到屏幕中心所需的偏移量</span></span><br><span class="line"><span class="keyword">float</span> dis[size][size];<span class="comment">//记录最小距离，由此判断遮罩关系</span></span><br><span class="line"><span class="comment">//cal_shade用来算个阴影</span></span><br><span class="line"><span class="comment">//Par&#123;n_i是被选中相对不变的方向,相当于此方向穿过平面;n_j作为判定是上平面还是下平面的值，只可能是1或-1&#125;</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">cal_shade</span><span class="params">(<span class="keyword">float</span> *n,<span class="keyword">int</span> n_i,<span class="keyword">int</span> n_j,<span class="keyword">float</span> *ro_a,<span class="keyword">float</span> *ro_b,<span class="keyword">float</span> *ro_c,<span class="keyword">float</span> *light,<span class="keyword">float</span> rotate,<span class="keyword">int</span> *out_i)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)n[i]=i==n_i?n_j:<span class="number">0</span>;<span class="comment">//确定原始法向量n[]</span></span><br><span class="line">    <span class="keyword">float</span> n_a[<span class="number">3</span>]=&#123;n[<span class="number">0</span>]*ro_a[<span class="number">0</span>]+n[<span class="number">1</span>]*ro_a[<span class="number">3</span>]+n[<span class="number">2</span>]*ro_a[<span class="number">6</span>],</span><br><span class="line">                  n[<span class="number">0</span>]*ro_a[<span class="number">1</span>]+n[<span class="number">1</span>]*ro_a[<span class="number">4</span>]+n[<span class="number">2</span>]*ro_a[<span class="number">7</span>],</span><br><span class="line">                  n[<span class="number">0</span>]*ro_a[<span class="number">2</span>]+n[<span class="number">1</span>]*ro_a[<span class="number">5</span>]+n[<span class="number">2</span>]*ro_a[<span class="number">8</span>]&#125;;<span class="comment">//法向量第一次旋转</span></span><br><span class="line">    <span class="keyword">float</span> n_b[<span class="number">3</span>]=&#123;n_a[<span class="number">0</span>]*ro_b[<span class="number">0</span>]+n_a[<span class="number">1</span>]*ro_b[<span class="number">3</span>]+n_a[<span class="number">2</span>]*ro_b[<span class="number">6</span>],</span><br><span class="line">                  n_a[<span class="number">0</span>]*ro_b[<span class="number">1</span>]+n_a[<span class="number">1</span>]*ro_b[<span class="number">4</span>]+n_a[<span class="number">2</span>]*ro_b[<span class="number">7</span>],</span><br><span class="line">                  n_a[<span class="number">0</span>]*ro_b[<span class="number">2</span>]+n_a[<span class="number">1</span>]*ro_b[<span class="number">5</span>]+n_a[<span class="number">2</span>]*ro_b[<span class="number">8</span>]&#125;;<span class="comment">//法向量第二次旋转</span></span><br><span class="line">    <span class="keyword">float</span> n_r[<span class="number">3</span>]=&#123;n_b[<span class="number">0</span>]*ro_c[<span class="number">0</span>]+n_b[<span class="number">1</span>]*ro_c[<span class="number">3</span>]+n_b[<span class="number">2</span>]*ro_c[<span class="number">6</span>],</span><br><span class="line">                  n_b[<span class="number">0</span>]*ro_c[<span class="number">1</span>]+n_b[<span class="number">1</span>]*ro_c[<span class="number">4</span>]+n_b[<span class="number">2</span>]*ro_c[<span class="number">7</span>],</span><br><span class="line">                  n_b[<span class="number">0</span>]*ro_c[<span class="number">2</span>]+n_b[<span class="number">1</span>]*ro_c[<span class="number">5</span>]+n_b[<span class="number">2</span>]*ro_c[<span class="number">8</span>]&#125;;</span><br><span class="line">    <span class="comment">//n_r[]作为绕三轴旋转rotate角度后的法向量</span></span><br><span class="line">    <span class="keyword">float</span> result=(n_r[<span class="number">0</span>]*light[<span class="number">0</span>]+n_r[<span class="number">1</span>]*light[<span class="number">1</span>]+n_r[<span class="number">2</span>]*light[<span class="number">2</span>]+<span class="number">1.0</span>)*<span class="number">0.5</span>;<span class="comment">//点积求反光强度(亮度)，并映射到[0,1]</span></span><br><span class="line">    <span class="keyword">float</span> co[<span class="number">3</span>];<span class="comment">//正方体的原始坐标，co意为coordinate</span></span><br><span class="line">    co[n_i]=dyn*<span class="built_in">cos</span>(rotate*<span class="number">2</span>);<span class="comment">//make it 20% cooler</span></span><br><span class="line">    <span class="keyword">int</span> co_a=n_i==<span class="number">0</span>?<span class="number">1</span>:n_i==<span class="number">1</span>?<span class="number">0</span>:<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> co_b=n_i==<span class="number">0</span>?<span class="number">2</span>:n_i==<span class="number">1</span>?<span class="number">2</span>:<span class="number">1</span>;<span class="comment">//确定了index为n_i的坐标是不需要被遍历的</span></span><br><span class="line">    co[n_i]+=move/<span class="number">2</span>;<span class="comment">//无需遍历的方向偏移1/4个屏幕边长</span></span><br><span class="line">    <span class="keyword">for</span>(co[co_a]=-a;co[co_a]&lt;=a;co[co_a]+=delta)</span><br><span class="line">        <span class="keyword">for</span>(co[co_b]=-a;co[co_b]&lt;=a;co[co_b]+=delta)&#123;</span><br><span class="line">            <span class="keyword">int</span> <span class="keyword">final</span>=(<span class="keyword">int</span>)(result*<span class="number">10.0</span>);<span class="comment">//把亮度int化</span></span><br><span class="line">            <span class="keyword">final</span>=(<span class="keyword">final</span>&gt;<span class="number">10</span>||<span class="keyword">final</span>)&lt;<span class="number">0</span>?<span class="keyword">final</span>&gt;<span class="number">10</span>?<span class="number">10</span>:<span class="number">0</span>:<span class="keyword">final</span>;<span class="comment">//保险，防止一些刁钻情况</span></span><br><span class="line">            <span class="keyword">float</span> x_a=co[<span class="number">0</span>]*ro_a[<span class="number">0</span>]+co[<span class="number">1</span>]*ro_a[<span class="number">3</span>]+co[<span class="number">2</span>]*ro_a[<span class="number">6</span>];</span><br><span class="line">            <span class="keyword">float</span> y_a=co[<span class="number">0</span>]*ro_a[<span class="number">1</span>]+co[<span class="number">1</span>]*ro_a[<span class="number">4</span>]+co[<span class="number">2</span>]*ro_a[<span class="number">7</span>];</span><br><span class="line">            <span class="keyword">float</span> z_a=co[<span class="number">0</span>]*ro_a[<span class="number">2</span>]+co[<span class="number">1</span>]*ro_a[<span class="number">5</span>]+co[<span class="number">2</span>]*ro_a[<span class="number">8</span>];<span class="comment">//坐标第一次旋转</span></span><br><span class="line">            <span class="keyword">float</span> x_b=x_a*ro_b[<span class="number">0</span>]+y_a*ro_b[<span class="number">3</span>]+z_a*ro_b[<span class="number">6</span>];</span><br><span class="line">            <span class="keyword">float</span> y_b=x_a*ro_b[<span class="number">1</span>]+y_a*ro_b[<span class="number">4</span>]+z_a*ro_b[<span class="number">7</span>];</span><br><span class="line">            <span class="keyword">float</span> z_b=x_a*ro_b[<span class="number">2</span>]+y_a*ro_b[<span class="number">5</span>]+z_a*ro_b[<span class="number">8</span>];<span class="comment">//坐标第二次旋转</span></span><br><span class="line">            <span class="keyword">float</span> x_r=x_b*ro_c[<span class="number">0</span>]+y_b*ro_c[<span class="number">3</span>]+z_b*ro_c[<span class="number">6</span>];</span><br><span class="line">            <span class="keyword">float</span> y_r=x_b*ro_c[<span class="number">1</span>]+y_b*ro_c[<span class="number">4</span>]+z_b*ro_c[<span class="number">7</span>];</span><br><span class="line">            <span class="keyword">float</span> z_r=x_b*ro_c[<span class="number">2</span>]+y_b*ro_c[<span class="number">5</span>]+z_b*ro_c[<span class="number">8</span>];</span><br><span class="line">            <span class="comment">//co_r作为绕三轴旋转rotate角度后的空间坐标</span></span><br><span class="line">            <span class="keyword">int</span> cox=(<span class="keyword">int</span>)(n_j*x_r+move);</span><br><span class="line">            <span class="keyword">int</span> coy=(<span class="keyword">int</span>)(n_j*z_r+move);<span class="comment">//找到投影到屏幕上的点在哪</span></span><br><span class="line">            <span class="keyword">if</span>(n_j*y_r&lt;dis[cox][coy])&#123;<span class="comment">//只有发现更近的点才替换亮度</span></span><br><span class="line">                out_i[cox*size+coy]=<span class="keyword">final</span>;</span><br><span class="line">                dis[cox][coy]=n_j*y_r;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">float</span> n[<span class="number">3</span>];<span class="comment">//原始法向量n[]</span></span><br><span class="line">    <span class="keyword">float</span> light[<span class="number">3</span>]=&#123;<span class="number">-1</span>,<span class="number">-1</span>,<span class="number">1</span>&#125;;<span class="comment">//平行光向量</span></span><br><span class="line">    <span class="keyword">float</span> length=cal_r(light[<span class="number">0</span>],light[<span class="number">1</span>],light[<span class="number">2</span>]);</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)light[i]/=length;<span class="comment">//单位化平行光向量</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">float</span> rotate=<span class="number">0.0</span>;;rotate+=<span class="number">0.05</span>)&#123;<span class="comment">//rotate为旋转角度，增量即旋转速度</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;size;i++)</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                out_i[i][j]=<span class="number">-1</span>;</span><br><span class="line">                dis[i][j]=(<span class="keyword">float</span>)(<span class="number">4.0</span>*size);</span><br><span class="line">                <span class="comment">//初始化亮度与距离</span></span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">float</span> cos_ro=<span class="built_in">cos</span>(rotate),sin_ro=<span class="built_in">sin</span>(rotate);</span><br><span class="line">        <span class="keyword">float</span> ro_a[<span class="number">3</span>][<span class="number">3</span>]=&#123;&#123;<span class="number">1.0</span>,<span class="number">0</span>,<span class="number">0</span>&#125;,&#123;<span class="number">0</span>,cos_ro,-sin_ro&#125;,&#123;<span class="number">0</span>,sin_ro,cos_ro&#125;&#125;;</span><br><span class="line">        <span class="keyword">float</span> ro_b[<span class="number">3</span>][<span class="number">3</span>]=&#123;&#123;cos_ro,<span class="number">0</span>,sin_ro&#125;,&#123;<span class="number">0</span>,<span class="number">1.0</span>,<span class="number">0</span>&#125;,&#123;-sin_ro,<span class="number">0</span>,cos_ro&#125;&#125;;</span><br><span class="line">        <span class="keyword">float</span> ro_c[<span class="number">3</span>][<span class="number">3</span>]=&#123;&#123;cos_ro,-sin_ro,<span class="number">0</span>&#125;,&#123;sin_ro,cos_ro,<span class="number">0</span>&#125;,&#123;<span class="number">0</span>,<span class="number">0</span>,<span class="number">1.0</span>&#125;&#125;;</span><br><span class="line">        <span class="comment">//旋转矩阵，ro表rotate</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;<span class="comment">//算个阴影，第三个参数控制对应面</span></span><br><span class="line">            cal_shade(&amp;n[<span class="number">0</span>],i,<span class="number">1</span>,&amp;ro_a[<span class="number">0</span>][<span class="number">0</span>],&amp;ro_b[<span class="number">0</span>][<span class="number">0</span>],&amp;ro_c[<span class="number">0</span>][<span class="number">0</span>],&amp;light[<span class="number">0</span>],rotate,&amp;out_i[<span class="number">0</span>][<span class="number">0</span>]);</span><br><span class="line">            cal_shade(&amp;n[<span class="number">0</span>],i,<span class="number">-1</span>,&amp;ro_a[<span class="number">0</span>][<span class="number">0</span>],&amp;ro_b[<span class="number">0</span>][<span class="number">0</span>],&amp;ro_c[<span class="number">0</span>][<span class="number">0</span>],&amp;light[<span class="number">0</span>],rotate,&amp;out_i[<span class="number">0</span>][<span class="number">0</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;size;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++) out_i[i][j]!=<span class="number">-1</span>?<span class="built_in">printf</span>(<span class="string">&quot;%c%c&quot;</span>,shade[out_i[i][j]],shade[out_i[i][j]]):<span class="built_in">printf</span>(<span class="string">&quot;  &quot;</span>);</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;\x1b[2J&quot;</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;\x1b[H&quot;</span>);</span><br><span class="line">        usleep(<span class="number">30000</span>);</span><br><span class="line">        <span class="comment">//跟着甜甜圈抄的，这样才能稳定住图像</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Dev C++下则调用Windows.h来解决图像刷新的问题</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">goto_O</span><span class="params">()</span><span class="comment">//CSDN上抄的函数，刷新一快光标就飘来飘去，看起来像噪点一样</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    HANDLE hOut;</span><br><span class="line">    hOut = GetStdHandle(STD_OUTPUT_HANDLE);</span><br><span class="line">    COORD pos = &#123;<span class="number">0</span>,<span class="number">0</span>&#125;;</span><br><span class="line">    SetConsoleCursorPosition(hOut, pos);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/images/图形学HelloWorld.gif" /></p><p>最开始先写了个光源移动球</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">goto_xy</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;   </span><br><span class="line">   HANDLE hOut;</span><br><span class="line">    hOut = GetStdHandle(STD_OUTPUT_HANDLE);</span><br><span class="line">   COORD pos = &#123;<span class="number">0</span>,<span class="number">0</span>&#125;;</span><br><span class="line">   SetConsoleCursorPosition(hOut, pos);</span><br><span class="line">&#125;</span><br><span class="line">main()&#123;</span><br><span class="line">    <span class="keyword">int</span> size=<span class="number">45</span>;</span><br><span class="line">    <span class="keyword">double</span> g=<span class="number">0.01</span>;</span><br><span class="line">    <span class="keyword">double</span> move=(size<span class="number">-1</span>)/<span class="number">2</span>;</span><br><span class="line">    <span class="keyword">double</span> r=(<span class="keyword">double</span>(size)<span class="number">-4</span>)/<span class="number">2.0</span>;</span><br><span class="line">    <span class="keyword">double</span> light[<span class="number">3</span>]=&#123;<span class="number">-1.3</span>,<span class="number">1</span>,<span class="number">1</span>&#125;;</span><br><span class="line">    <span class="keyword">double</span> length=<span class="built_in">sqrt</span>(light[<span class="number">0</span>]*light[<span class="number">0</span>]+light[<span class="number">1</span>]*light[<span class="number">1</span>]+light[<span class="number">2</span>]*light[<span class="number">2</span>]);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++)&#123;</span><br><span class="line">        light[i]=light[i]/length;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">char</span> shade[]=<span class="string">&quot;.;!o*m0&amp;%@&quot;</span>;</span><br><span class="line">    <span class="keyword">int</span> out_i[size][size]=&#123;&#125;;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">double</span> rotate=<span class="number">0.0</span>;;rotate+=<span class="number">0.05</span>)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;size;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                out_i[i][j]=<span class="number">-1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">double</span> ro[<span class="number">3</span>][<span class="number">3</span>]=&#123;&#123;<span class="number">1.0</span>,<span class="number">0</span>,<span class="number">0</span>&#125;,&#123;<span class="number">0</span>,<span class="built_in">cos</span>(rotate),-<span class="built_in">sin</span>(rotate)&#125;,&#123;<span class="number">0</span>,<span class="built_in">sin</span>(rotate),<span class="built_in">cos</span>(rotate)&#125;&#125;;</span><br><span class="line">        <span class="keyword">double</span> light_r[<span class="number">3</span>]=&#123;light[<span class="number">0</span>]*ro[<span class="number">0</span>][<span class="number">0</span>]+light[<span class="number">1</span>]*ro[<span class="number">1</span>][<span class="number">0</span>]+light[<span class="number">2</span>]*ro[<span class="number">2</span>][<span class="number">0</span>],</span><br><span class="line">                           light[<span class="number">0</span>]*ro[<span class="number">0</span>][<span class="number">1</span>]+light[<span class="number">1</span>]*ro[<span class="number">1</span>][<span class="number">1</span>]+light[<span class="number">2</span>]*ro[<span class="number">2</span>][<span class="number">1</span>],</span><br><span class="line">                           light[<span class="number">0</span>]*ro[<span class="number">0</span>][<span class="number">2</span>]+light[<span class="number">1</span>]*ro[<span class="number">1</span>][<span class="number">2</span>]+light[<span class="number">2</span>]*ro[<span class="number">2</span>][<span class="number">2</span>]&#125;;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">double</span> a=<span class="number">0.0</span>;a&lt;<span class="number">3.15</span>;a+=g)&#123;<span class="comment">//alpha </span></span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">double</span> b=<span class="number">0.0</span>;b&lt;<span class="number">3.15</span>;b+=g)&#123;<span class="comment">//beta </span></span><br><span class="line">                <span class="keyword">double</span> x=r*<span class="built_in">sin</span>(b)*<span class="built_in">cos</span>(a);</span><br><span class="line">                <span class="keyword">double</span> y=r*<span class="built_in">sin</span>(b)*<span class="built_in">sin</span>(a);</span><br><span class="line">                <span class="keyword">double</span> z=r*<span class="built_in">cos</span>(b);</span><br><span class="line">                <span class="keyword">double</span> result=(x/r*light_r[<span class="number">0</span>]+y/r*light_r[<span class="number">1</span>]+z/r*light_r[<span class="number">2</span>]+<span class="number">1.0</span>)*<span class="number">0.5</span>;</span><br><span class="line">                <span class="keyword">int</span> <span class="keyword">final</span>=<span class="keyword">int</span>(result*<span class="number">10.0</span><span class="number">-1</span>);</span><br><span class="line">                <span class="comment">/*</span></span><br><span class="line"><span class="comment">                if(final&gt;10||final&lt;0)&#123;</span></span><br><span class="line"><span class="comment">                    if(final&gt;10)final=10;</span></span><br><span class="line"><span class="comment">                    else if(final&lt;0)final=0;</span></span><br><span class="line"><span class="comment">                &#125;</span></span><br><span class="line"><span class="comment">                */</span></span><br><span class="line">                <span class="keyword">int</span> cx=<span class="keyword">int</span>(x+move);</span><br><span class="line">                <span class="keyword">int</span> cy=<span class="keyword">int</span>(z+move);</span><br><span class="line">                <span class="keyword">if</span>(<span class="keyword">final</span>&gt;out_i[cx][cy])<span class="comment">//这种取最大亮度的写法明显是不好的，但这个例子特殊，用起来没问题</span></span><br><span class="line">                    out_i[cx][cy]=<span class="keyword">final</span>;</span><br><span class="line">            &#125; </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;size;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">0</span>;j&lt;size;j++)&#123;</span><br><span class="line">                <span class="keyword">if</span>(out_i[i][j]!=<span class="number">-1</span>)</span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;%c%c&quot;</span>,shade[out_i[i][j]],shade[out_i[i][j]]);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    <span class="built_in">printf</span>(<span class="string">&quot;  &quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        goto_xy(); </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>附上甜甜圈的代码，写方块之前没仔细看过，所以我写的方块非常冗余（主要是想先自己试试</p><div class='spoiler collapsed'>    <div class='spoiler-title'>        doughnut    </div>    <div class='spoiler-content'>        <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">float</span> A = <span class="number">0</span>, B = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">float</span> i, j;</span><br><span class="line">    <span class="keyword">int</span> k;</span><br><span class="line">    <span class="keyword">float</span> z[<span class="number">1760</span>];</span><br><span class="line">    <span class="keyword">char</span> b[<span class="number">1760</span>];</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\x1b[2J&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span>(;;) &#123;</span><br><span class="line">        <span class="built_in">memset</span>(b,<span class="number">32</span>,<span class="number">1760</span>);</span><br><span class="line">        <span class="built_in">memset</span>(z,<span class="number">0</span>,<span class="number">7040</span>);</span><br><span class="line">        <span class="keyword">for</span>(j=<span class="number">0</span>; j &lt; <span class="number">6.28</span>; j += <span class="number">0.07</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span>(i=<span class="number">0</span>; i &lt; <span class="number">6.28</span>; i += <span class="number">0.02</span>) &#123;</span><br><span class="line">                <span class="keyword">float</span> c = <span class="built_in">sin</span>(i);</span><br><span class="line">                <span class="keyword">float</span> d = <span class="built_in">cos</span>(j);</span><br><span class="line">                <span class="keyword">float</span> e = <span class="built_in">sin</span>(A);</span><br><span class="line">                <span class="keyword">float</span> f = <span class="built_in">sin</span>(j);</span><br><span class="line">                <span class="keyword">float</span> g = <span class="built_in">cos</span>(A);</span><br><span class="line">                <span class="keyword">float</span> h = d + <span class="number">2</span>;</span><br><span class="line">                <span class="keyword">float</span> D = <span class="number">1</span> / (c * h * e + f * g + <span class="number">5</span>);</span><br><span class="line">                <span class="keyword">float</span> l = <span class="built_in">cos</span>(i);</span><br><span class="line">                <span class="keyword">float</span> m = <span class="built_in">cos</span>(B);</span><br><span class="line">                <span class="keyword">float</span> n = <span class="built_in">sin</span>(B);</span><br><span class="line">                <span class="keyword">float</span> t = c * h * g - f * e;</span><br><span class="line">                <span class="keyword">int</span> x = <span class="number">40</span> + <span class="number">30</span> * D * (l * h * m - t * n);</span><br><span class="line">                <span class="keyword">int</span> y= <span class="number">12</span> + <span class="number">15</span> * D * (l * h * n + t * m);</span><br><span class="line">                <span class="keyword">int</span> o = x + <span class="number">80</span> * y;</span><br><span class="line">                <span class="keyword">int</span> N = <span class="number">8</span> * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n);</span><br><span class="line">                <span class="keyword">if</span>(<span class="number">22</span> &gt; y &amp;&amp; y &gt; <span class="number">0</span> &amp;&amp; x &gt; <span class="number">0</span> &amp;&amp; <span class="number">80</span> &gt; x &amp;&amp; D &gt; z[o]) &#123;</span><br><span class="line">                    z[o] = D;</span><br><span class="line">                    b[o] = <span class="string">&quot;.,-~:;=!*#$@&quot;</span>[N &gt; <span class="number">0</span> ? N : <span class="number">0</span>];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;\x1b[H&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span>(k = <span class="number">0</span>; k &lt; <span class="number">1761</span>; k++) &#123;</span><br><span class="line">            <span class="built_in">putchar</span>(k % <span class="number">80</span> ? b[k] : <span class="number">10</span>);</span><br><span class="line">            A += <span class="number">0.00004</span>;</span><br><span class="line">            B += <span class="number">0.00002</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        usleep(<span class="number">30000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>    </div></div><p>为了防止你们想看视频找不到传送门，<a href="https://www.youtube.com/watch?v=sW9npZVpiMI">Click Here</a></p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;逛b站看到会转的甜甜圈，有点上头，自己拿蜥语言做个立方体玩玩&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/图形学HelloWorld.gif&quot; height=&quot;200&quot; width=&quot;200&quot; /&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="摸鱼" scheme="https://kyriota.github.io/categories/%E6%91%B8%E9%B1%BC/"/>
    
    
  </entry>
  
  <entry>
    <title>i春秋虎符2021 web</title>
    <link href="https://kyriota.github.io/2021/04/10/i%E6%98%A5%E7%A7%8B%E8%99%8E%E7%AC%A62021/"/>
    <id>https://kyriota.github.io/2021/04/10/i%E6%98%A5%E7%A7%8B%E8%99%8E%E7%AC%A62021/</id>
    <published>2021-04-10T04:35:02.000Z</published>
    <updated>2022-08-02T06:50:24.774Z</updated>
    
    <content type="html"><![CDATA[<p>记录题目扫盲，只有前三题，包含以下points：</p><p>​ ①3.28 php.net后门</p><p>​ ②3F(FatFreeFramework)框架</p><p>​ ③SQL中md5()产生hex的'or'进行的注入</p><p>​ ④SSRF与gopher协议</p><span id="more"></span><h1 id="i春秋虎符2021_web">i春秋虎符2021_web</h1><h2 id="签到">签到</h2><p>2021.3.28出了php官方git仓库git.php.net被插入恶意代码的事，若使用此源码进行开发，网站就会执行user_agentt头中以zerodium开始的任意代码，注意double t，没有打错</p><p><img src="/images/虎符-签到github.png" /></p><center><a href="https://github.com/php/php-src/commit/c730aa26bd52829a49f2ad284b181b7e82a68d7d">view on github</a></center><p>题目环境是一个blog，最后一篇文章描述了博客搭建过程，在没有hint的情况下这是入手点</p><p><img src="/images/虎符-签到blog.png" /></p><h2 id="unsetme">unsetme</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Kickstart the framework</span></span><br><span class="line"><span class="variable">$f3</span> = <span class="keyword">require</span>(<span class="string">&#x27;1ib/base.php&#x27;</span>);</span><br><span class="line"><span class="variable">$f3</span>-&gt;set( <span class="string">&#x27;DEBUG&#x27;</span> ,<span class="number">1</span>);</span><br><span class="line"><span class="keyword">if</span> ((<span class="keyword">float</span>)PCREVERSION &lt;<span class="number">8.0</span>)</span><br><span class="line">    trigger_error(<span class="string">&#x27;PCREversion is out date&#x27;</span>);</span><br><span class="line"><span class="comment">//Load configuration</span></span><br><span class="line">highlight_file(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="variable">$a</span> = <span class="variable">$GET</span> [<span class="string">&#x27;a&#x27;</span>];</span><br><span class="line"><span class="keyword">unset</span>(<span class="variable">$f3</span>-&gt;<span class="variable">$a</span>);</span><br><span class="line"><span class="variable">$f3</span>-&gt;run();</span><br></pre></td></tr></table></figure><p align="right"><small>关键字：framework，f3</small></p><p>此为基于f3框架编写的代码，拿到手时应先本地搭建此框架，对于本题，这样你才有了base.php，才可以解题，各种不熟悉导致找不到本题入手点</p><p><img src="/images/虎符-unsetme-f3Framework.png" /></p><p>把题目代码copy过来，加上route指定路径后本地运行（也就是说要先设置<code>route</code>再<code>run</code>，不然框架跑都跑不起来，怎么设置查一下文档就知道了¯\_(ツ)_/¯）</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$f3</span>-&gt;route(<span class="string">&#x27;GET /test.php&#x27;</span>,</span><br><span class="line">    <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;route set successfully&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">);</span><br><span class="line"><span class="variable">$f3</span>-&gt;run();</span><br></pre></td></tr></table></figure><p><img src="/images/虎符-unsetme-f3run.png" /></p><p>直接报错了，看看530行eval的情况</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clear</span>(<span class="params"><span class="variable">$key</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">#...</span></span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$val</span>=preg_replace(<span class="string">&#x27;/^(\$hive)/&#x27;</span>,<span class="string">&#x27;$this-&gt;hive&#x27;</span>,</span><br><span class="line">                          <span class="keyword">$this</span>-&gt;compile(<span class="string">&#x27;@hive.&#x27;</span>.<span class="variable">$key</span>, <span class="literal">FALSE</span>));</span><br><span class="line">        <span class="keyword">eval</span>(<span class="string">&#x27;unset(&#x27;</span>.<span class="variable">$val</span>.<span class="string">&#x27;);&#x27;</span>);    <span class="comment">#第530行</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$parts</span>[<span class="number">0</span>]==<span class="string">&#x27;SESSION&#x27;</span>) &#123;</span><br><span class="line">            session_commit();</span><br><span class="line">            session_start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>追一下代码验证一下是不是<code>unset</code>触发的<code>clear()</code>，发现调用链条是<code>__unset($key)</code>→<code>offsetunset($key)</code>→<code>clear($key)</code>实锤</p><p>见里面有一个<code>compile()</code>，康康</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">compile</span>(<span class="params"><span class="variable">$str</span>, <span class="variable">$evaluate</span>=<span class="literal">TRUE</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (!<span class="variable">$evaluate</span>)</span><br><span class="line">        ? preg_replace_callback(</span><br><span class="line">            <span class="string">&#x27;/^@(\w+)((?:\..+|\[(?:(?:[^\[\]]*|(?R))*)\])*)/&#x27;</span>,</span><br><span class="line">            <span class="function"><span class="keyword">function</span>(<span class="params"><span class="variable">$expr</span></span>) </span>&#123;</span><br><span class="line">                <span class="variable">$str</span>=<span class="string">&#x27;$&#x27;</span>.<span class="variable">$expr</span>[<span class="number">1</span>];</span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$expr</span>[<span class="number">2</span>]))</span><br><span class="line">                    <span class="variable">$str</span>.=preg_replace_callback(</span><br><span class="line">                        <span class="string">&#x27;/\.([^.\[\]]+)|\[((?:[^\[\]\&#x27;&quot;]*|(?R))*)\]/&#x27;</span>,</span><br><span class="line">                        <span class="function"><span class="keyword">function</span>(<span class="params"><span class="variable">$sub</span></span>) </span>&#123;</span><br><span class="line">                            <span class="variable">$val</span>=<span class="keyword">isset</span>(<span class="variable">$sub</span>[<span class="number">2</span>]) ? <span class="variable">$sub</span>[<span class="number">2</span>] : <span class="variable">$sub</span>[<span class="number">1</span>];</span><br><span class="line">                            <span class="keyword">if</span> (ctype_digit(<span class="variable">$val</span>))</span><br><span class="line">                                <span class="variable">$val</span>=(<span class="keyword">int</span>)<span class="variable">$val</span>;</span><br><span class="line">                            <span class="variable">$out</span>=<span class="string">&#x27;[&#x27;</span>.<span class="keyword">$this</span>-&gt;export(<span class="variable">$val</span>).<span class="string">&#x27;]&#x27;</span>;</span><br><span class="line">                            <span class="keyword">return</span> <span class="variable">$out</span>;</span><br><span class="line">                        &#125;,</span><br><span class="line">                        <span class="variable">$expr</span>[<span class="number">2</span>]</span><br><span class="line">                    );</span><br><span class="line">                <span class="keyword">return</span> <span class="variable">$str</span>;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="variable">$str</span></span><br><span class="line">        )</span><br><span class="line"><span class="comment">#------------------下面这段都无关，毕竟clear()里传了FALSE------------------</span></span><br><span class="line">        : preg_replace_callback(</span><br><span class="line">        <span class="string">&#x27;/(?&lt;!\w)@(\w+(?:(?:\-&gt;|::)\w+)?)&#x27;</span>.</span><br><span class="line">        <span class="string">&#x27;((?:\.\w+|\[(?:(?:[^\[\]]*|(?R))*)\]|(?:\-&gt;|::)\w+|\()*)/&#x27;</span>,</span><br><span class="line">        <span class="function"><span class="keyword">function</span>(<span class="params"><span class="variable">$expr</span></span>) </span>&#123;</span><br><span class="line">            <span class="variable">$str</span>=<span class="string">&#x27;$&#x27;</span>.<span class="variable">$expr</span>[<span class="number">1</span>];</span><br><span class="line">        <span class="comment">#...</span></span><br></pre></td></tr></table></figure><p>看样子对输入做了改动，但其实不想分析<code>complie()</code>的话，直接<code>echo</code>即可知道到底eval了什么代码</p><blockquote>当a=hello<br>[Out] unset('$this-&gt;hive['hello']');</blockquote><p>但现在既然比赛已经结束那还是稍微深入看一下<code>complie()</code>，遇到了我没见过的php：回调，匿名函数</p><ul><li><p>回调：在函数执行的过程中，一般是不能去干预他的行为的，当函数被设计成带有回调功能时，我们就有可能在函数的执行过程中，通过回调函数去干预他</p><p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"><span class="variable">$n</span>, <span class="variable">$f</span>=<span class="string">&#x27;&#x27;</span></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="variable">$n</span> &lt; <span class="number">1</span>) <span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="variable">$i</span>=<span class="number">0</span>; <span class="variable">$i</span>&lt;<span class="variable">$n</span>; <span class="variable">$i</span>++) &#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="variable">$f</span> ? <span class="variable">$f</span>(<span class="variable">$i</span>) : <span class="variable">$i</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//无回调时</span></span><br><span class="line">foo(<span class="number">5</span>); <span class="comment">//01234</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">//有回调时</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"><span class="variable">$v</span></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="variable">$v</span> + <span class="variable">$v</span>;</span><br><span class="line">&#125;</span><br><span class="line">foo(<span class="number">5</span>, <span class="string">&#x27;f1&#x27;</span>); <span class="comment">//02468</span></span><br></pre></td></tr></table></figure></p></li><li><p>匿名函数：允许临时创建一个没有指定名称的函数。最经常用作回调函数<code>callable</code>回调参数的值</p></li></ul><p>而<code>preg_replace_callback</code>除了可以指定一个 <code>callback</code> 替代 <code>replacement</code> 进行替换字符串的计算，其他方面等同于<code>preg_replace</code></p><p>至于什么<code>$this-&gt;hive</code>之类的杂七杂八的都是后面才加上去的，和<code>compile()</code>无关</p><p>因为传参<code>a</code>会被<code>compile</code>处理为<code>['xxx']</code>的形式，所以需要打破这种格式，然后点号连接并闭合<code>unset</code>，最后<code>eval</code>执行任意代码</p><p>看看这个正则<code>'/\.([^.\[\]]+)|\[((?:[^\[\]\'"]*|(?R))*)\]/'</code>，并没有匹配换行符，所以出了换行符这个正则就会被打断，于是便超度成功</p><p><img src="/images/虎符-unsetme-换行符.png" /></p><h2 id="慢慢做管理系统">慢慢做管理系统</h2><blockquote>描述：这个sql吧，有点ssrf的样子，首页是一个很普通的sql注入，没有什么花样，但是我的admin.php是一个内网的管理系统，只要你用“真-admin”的密码登录了，就可以拿到flag</blockquote><blockquote>hint：第一步登录的sql语句是"SELECT * FROM users WHERE password = '".md5($password,true)."' limit 0,1";</blockquote><p>这个题没有怎么动，目前也没找到复现，暂时只能面向wp扫盲</p><h3 id="登录">登录</h3><p><img src="/images/虎符-慢慢做-登录.png" /></p><p>关键字是<code>md5($password,true)</code>，第二个参数<code>raw=true</code>即转md5后又做一次<code>hex2str</code></p><table><thead><tr class="header"><th style="text-align: left;">参数</th><th style="text-align: left;">描述</th></tr></thead><tbody><tr class="odd"><td style="text-align: left;"><em>string</em></td><td style="text-align: left;">必需。规定要计算的字符串。</td></tr><tr class="even"><td style="text-align: left;"><em>raw</em></td><td style="text-align: left;">可选。规定十六进制或二进制输出格式：TRUE - 原始 16 字符二进制格式FALSE - 默认。32 字符十六进制数</td></tr></tbody></table><p>所以hint描述的这一步其实是md5万能密码：<code>ffifdyop</code>与<code>129581926211651571912466741651878684928</code>在经过md5并转字符后均包含有<code>'or'</code>，只要<code>'or'</code>右边非零，即会判定整个表达式为true，以此便绕过了登录</p><h3 id="gopher协议">gopher协议</h3><p><img src="/images/虎符-慢慢做-gopher.png" /></p><blockquote>gopher协议支持发出GET、POST请求：可以先截获get请求包和post请求包，在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议</blockquote><p>SSRF：Server-side Request Forge，服务端请求伪造；说白了就是：<i>因为是由服务端发起，从而能够请求到与它相连而与外网隔离的内部系统</i> 的一种攻击方式，比如通过file://，gopher://之类的协议来读本地文件什么的</p><p>本题是屏蔽了<code>file://</code>的，只能用<code>gopher://</code></p><p>Gopher协议格式：<code>URL:gopher://&lt;host&gt;:&lt;port&gt;/&lt;gopher-path&gt;_TCP数据流</code></p><ul><li>gopher的默认端口是70</li><li>如果发起POST请求，请求体需要进行url编码，回车换行需要使用<code>%0d</code>或<code>%0a</code>代替</li></ul><p>payload：/ssrf.php?way=127.0.0.1/admin.php，发现admin.php不再302了</p><p><img src="/images/虎符-慢慢做-admin.png" /></p><p>然后通过gopher协议给admin.php发POST数据，HTTP协议默认端口是80，所以是往<code>127.0.0.1:80</code>发包：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gopher:&#x2F;&#x2F;127.0.0.1:80&#x2F;_POST%20%2Fadmin.php%20HTTP%2F1.1%0aHost%3A%20127.0.0.1%0aContent-Type%3A%20application%2Fx-www-form-urlencoded%0aContent-Length%3A%2027%0ausername%3Dtest%26password%3Dtest%0a</span><br></pre></td></tr></table></figure><blockquote>gopher://127.0.0.1:80/<br>_POST /admin.php HTTP/1.1<br> Host: 127.0.0.1<br> Content-Type: application/x-www-form-urlencoded<br> Content-Length: 27<br> username=test&amp;password=test</blockquote><h3 id="改表名">改表名</h3><p>通过改表名获取数据@2019强网杯，也可以通过<code>prepare</code>预处理<code>concat</code>过滤的<code>select</code>，预处理的方法之前就记录过，不再重复</p><p>当过滤了<code>select</code>，就不能在查出表名列名后选中数据，但若查询的回显就是表中的字段，则可以通过<code>rename</code>将默认查询的表替换为目标表，从而直接从回显获取字段</p><p>例如本题中<code>fake_admin</code>是默认被查询的表，<code>real_admin_here_do_you_find</code>是目标表</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$payload &#x3D; &quot;username&#x3D;admin&#39;&#x2F;**&#x2F;or&#x2F;**&#x2F;1&#x3D;2;RENAME TABLE &#96;fake_admin&#96; TO &#96;fake_admin1&#96;;RENAME TABLE &#96;real_admin_here_do_you_find&#96; TO &#96;fake_admin&#96;;##&amp;password&#x3D;129581926211651571912466741651878684928&quot;;</span><br></pre></td></tr></table></figure><p>顺便记一下 [2019强网杯-随便注] 的payload</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;?inject&#x3D;1&#39;;RENAME TABLE &#96;words&#96; TO &#96;words1&#96;;RENAME TABLE &#96;1919810931114514&#96; TO &#96;words&#96;;ALTER TABLE &#96;words&#96; CHANGE &#96;flag&#96; &#96;id&#96; VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;show columns from words;#</span><br></pre></td></tr></table></figure><p>此处改列名<code>flag</code>为<code>id</code>是因为目标表中没有<code>id</code>列，而查询是根据id查询，避免了一开始无法查询的情况</p><p><code>CHANGE</code>的语法为：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE t1 CHANGE c_old c_new INTEGER ;#把名为c_old的列名改为c_new，类型为整数型</span><br></pre></td></tr></table></figure><h2 id="internal-system">Internal System</h2><p>超出能力范围的部分有点多，积累起来了再说</p><link rel="stylesheet" href="/css/spoiler.css" type="text/css"><script src="/js/spoiler.js" type="text/javascript" async></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;记录题目扫盲，只有前三题，包含以下points：&lt;/p&gt;
&lt;p&gt;​ ①3.28 php.net后门&lt;/p&gt;
&lt;p&gt;​ ②3F(FatFreeFramework)框架&lt;/p&gt;
&lt;p&gt;​ ③SQL中md5()产生hex的&#39;or&#39;进行的注入&lt;/p&gt;
&lt;p&gt;​ ④SSRF与gopher协议&lt;/p&gt;</summary>
    
    
    
    <category term="Web" scheme="https://kyriota.github.io/categories/Web/"/>
    
    
    <category term="CTF" scheme="https://kyriota.github.io/tags/CTF/"/>
    
  </entry>
  
</feed>
