<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Wulnut`s space</title>
  <icon>https://www.wulnut.top/img/main/panda.png</icon>
  <subtitle>垃圾桶轮到你介绍了</subtitle>
  <link href="https://www.wulnut.top/atom.xml" rel="self"/>
  
  <link href="https://www.wulnut.top/"/>
  <updated>2023-02-06T15:15:21.444Z</updated>
  <id>https://www.wulnut.top/</id>
  
  <author>
    <name>Wulnut</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>记流浪地球Ⅱ的思考</title>
    <link href="https://www.wulnut.top/2023/02/01/%E6%B5%81%E6%B5%AA%E5%9C%B0%E7%90%832/"/>
    <id>https://www.wulnut.top/2023/02/01/%E6%B5%81%E6%B5%AA%E5%9C%B0%E7%90%832/</id>
    <published>2023-02-01T08:50:09.630Z</published>
    <updated>2023-02-06T15:15:21.444Z</updated>
    
    <content type="html"><![CDATA[<p>过年的时候看了两遍流浪地球，这两天把大刘的《流浪地球》小说看完了简单的说一下自己的思考</p><a id="more"></a><p>因为之前看过流浪地球Ⅰ，所以这次流浪地球Ⅱ出来的时候。我也就立刻买票去看。在买票之前我看到网上说流浪地球Ⅱ是Ⅰ的前传。所以我也挺好奇流浪地球计划是怎么来的。</p><h2 id="先说说为什么看两遍吧"><a href="#先说说为什么看两遍吧" class="headerlink" title="先说说为什么看两遍吧"></a>先说说为什么看两遍吧</h2><p>其实看第一遍的时候，我一直没看懂电影中一直闪烁的摄像头是怎么来的。还有就是图恒宇和马兆两个两人在最后启动跟服务器的时候，为什么图恒宇最后阵亡进入和女儿图丫丫统一数字空间。再就是图恒宇那条数字生命线到底是什么意思。再加上电影的场面很震撼，就去刷了第二遍。再就是电影不断受到来自未来的提示，这个地方理解还有点难度。</p><h2 id="简单说一下电影"><a href="#简单说一下电影" class="headerlink" title="简单说一下电影"></a>简单说一下电影</h2><p>这个电影感觉看第一遍的时候，因为内容太多了有点没看明白。后来在网上看了一些人影评之后我在理解电影里面的一些细节和情节设计，才明白其中的一些情节目的。也可能是我看电影的理解能力有点差。但可以说这部电影的情节还是很紧凑的，就是内容太多了给人一种应该没了吧，然后看手表发现才放了一半的感觉。再就是因为这部电影的拍摄大多为实体布景加特效的方法，所以给人之种很真实的感觉。再就是感觉电影中的设定给人一种好像现实世界也能实现的感觉。</p><h2 id="说说电影的两条线"><a href="#说说电影的两条线" class="headerlink" title="说说电影的两条线"></a>说说电影的两条线</h2><p>这个电影应该是双主线，双主教。一条线讲的是吴京的流浪地球派。他们根据联合政府的指定的计划推进流浪地球计划，这是一条明线。还有一条暗线就是，马兆和图恒宇所研究的数字生命计划。但是这个计划在研发到中途的时候被人们禁止了。但是图恒宇在马兆的帮助一下私下继续推进数字生命的研究。从而在550w被研制出来后，图恒宇用丫丫的数字生命卡。通过550w离线版本实现了真正的数字生命。</p><h2 id="说说我比较感兴趣的暗线"><a href="#说说我比较感兴趣的暗线" class="headerlink" title="说说我比较感兴趣的暗线"></a>说说我比较感兴趣的暗线</h2><p>电影里面，马兆和图恒宇这两哥角色是我比较喜欢的角色。先说说马兆吧，电影中马兆和图恒宇都是数字生命研究所的研究员。但是从图恒宇遇到车祸后，马老师愿意帮助图恒宇把丫丫最后的生命存入数字生命卡中开始。虽然马老师说自己不是数字生命派，不愿意以后自己的数字生命被别人当电子宠物养着，但马老师似乎一直都在帮助图恒宇进行数字生命的研究。先是允许图恒宇在月球上使用550A对图丫丫的生命卡进行反复的迭代，再就是同意图恒宇加入550系列电脑的后续研究，最后图恒宇在面试房间使用550W离线版本对图丫丫进行迭代的时候，马兆也是在房间另一边疯狂嘴炮给图恒宇拖延时间让他把迭代完成，算是证明了550W是可以实现数字生命的全部的。再到最后马兆把图恒宇从监狱里面捞出来，让他最终在重启互联网的时候使用让数字生命进入互联网从而进入550W。</p><p>最后550W的彩蛋有点想《星际穿越》的4维空间，在4维空间中时间也是一个维度可以去过去和未来。这似乎也解释了，图恒宇和图丫丫在数字生命中不断的给人们发送危机发生的时间让大家知道。这似乎也要我感觉马兆其实一个数字生命最坚持的信徒，是他一直在引领图恒宇进行不断坚定的对数字生命进行研究事件，他在遗书上画出的无穷似乎相互印证。</p><h2 id="说说原著"><a href="#说说原著" class="headerlink" title="说说原著"></a>说说原著</h2><p>大刘的《流浪地球》其实是一篇短篇小说。内容十分紧凑，基本上勾勒了一个世界观。和电影比起来，可以说是给电影提供了一个大纲和一个宏大的中心。可以说电影算是给这部短篇小说补上了细节，这也要我对《流浪地球》电影的整个剧组肃然起立。电影中的每一部分似乎都很合理，在不久的将来似乎都能够实现。<br>但是我还是觉得电影的描述，还是把人类描述的太好了。在原著中的末尾，当流浪地球已经启程，人们中对计划的质疑声也逐渐起来，因为有部分人们通过受限的观测方法认为认为太阳是不会发生氦闪的，最终起义冲进了驾驶室处刑了流浪地球的领导者。当起义者为他们拯救地球的行动而感到高兴的时候，太阳发生了氦闪。最终一少部分人继续执行着流浪地球计划。</p><p>我觉得在原著小说中，我似乎感觉这更应该会发生的。就拿疫情来说，当病毒的毒性逐渐减弱的时候，人们开始认为一开始的决策是错误的、是浪费时间的。只不过我们还没有遇到最后的恶果反噬。这也是我们应该庆幸的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;过年的时候看了两遍流浪地球，这两天把大刘的《流浪地球》小说看完了简单的说一下自己的思考&lt;/p&gt;</summary>
    
    
    
    <category term="半月谈" scheme="https://www.wulnut.top/categories/%E5%8D%8A%E6%9C%88%E8%B0%88/"/>
    
    
    <category term="回忆" scheme="https://www.wulnut.top/tags/%E5%9B%9E%E5%BF%86/"/>
    
    <category term="杂谈" scheme="https://www.wulnut.top/tags/%E6%9D%82%E8%B0%88/"/>
    
  </entry>
  
  <entry>
    <title>2022</title>
    <link href="https://www.wulnut.top/2022/12/31/2022%E6%80%BB%E7%BB%93/"/>
    <id>https://www.wulnut.top/2022/12/31/2022%E6%80%BB%E7%BB%93/</id>
    <published>2022-12-30T16:00:00.000Z</published>
    <updated>2023-02-06T15:08:41.913Z</updated>
    
    <content type="html"><![CDATA[<p>2022总结<br><a id="more"></a></p><!-- 一、简单总结一下今年二、略写一下今年上半年三、重点说说今年最后两个月的复习 --><h2 id="这是悲喜交加的一年"><a href="#这是悲喜交加的一年" class="headerlink" title="这是悲喜交加的一年"></a>这是悲喜交加的一年</h2><p>今年上半年是大起大落，从成绩出来到准备复试，再到今年复试线猛增落榜，开始准备调剂，到最后复试被刷。似乎暗示了这一年注定将在起伏中度过。</p><p>虽然调剂失败了，但是又遇到毕业。我知道我和我的室友、我的朋友在一起无厘头的时间也没有多少了。还是带着大家到我家一起玩了一个星期，算是毕业旅行也算是让自己从失落中走出来。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-imgxiangyang_travel.webp" alt="毕业旅行"></p><h2 id="选择从新上路"><a href="#选择从新上路" class="headerlink" title="选择从新上路"></a>选择从新上路</h2><p>在目标学校租好房子，因为那个十分油头的房东一直把我拖到7月份才要我住进去。本以为住进去就可以开始每天去书图书馆认真复习的是日子了，但是生活总是喜欢和我开玩笑，图书馆分配位置、疫情、限制出校等等7月份我基本上都在适应在新环境。不过因为徐老师和袁学长的帮助，很多问题才得以解决。当然这中稳定的学习生活并没有我想象的那么久，基本上每个月都会遇到一些奇奇怪怪的问题，但这些问题基本上都和疫情有关。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-imgpostgraduated_feel.webp" alt="研究生的感觉"></p><p>什么校外疫情，图书馆闭馆、进出学校受限制。基本上每个月都会从图书馆会到自己的房间学个两三天。特别是9月份那次疫情，听说是学校的联通营业厅老板的儿子感染了，恰好计院的一个领导刚好去过，整个学校直接进入封闭状态，因为我住在教师公寓，学校保卫处直接把教师公寓到学校的为一的一条路给封了。那天一大早我以为和往常一样，我准备去食堂吃饭，发现他们把路给封了。有个老师和我说你们学生应该也过不去，只有开车的老师可以过去你还是回去吧。感谢完老师之后我直接去教师公寓的中百，中百直接防止我们囤货，直接说不对外营业。最后是找房东给我们买了点泡面和面包准备抗过一个星期。不过挺搞笑的是，外卖还是可以点。能点外卖基本上买的泡面和面包就没有意义了。也算是第一次遇到这种事，没能考虑周全。选择了这种比较蠢的解决方式。不过还好危机解决的比较快，也就过来两三天，我们就能去食堂正常买饭了，大概过来四五天图书馆才正常开放。（下图为出去散心的时候拍的）</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-imgpostgraduated_feel.webp" alt="出去放松"></p><p>后来基本上平稳学习了两个月，正常的去食堂，去图书馆，周末去打球，晚上睡觉。直到11月中下旬，疫情又来了，我记得很清楚那是个周五我和647(和我一起租房的同学)两个人正常去打球，我俩坐在公交上一路上没有一个人上车，因为球馆在一个修车城里，往球馆走的一路上发现基本上一路上所有的店都是门窗紧闭。只有球馆里面还是很多人，我们一路上都在讨论是不是这周围又疫情我们大家都不出来了。果不其然第二天学校就通知严格管理进出校园。本以为只是简单的校外有疫情而已，谁知道没过两天情况急转直下647也害怕因为疫情太严重鄂州封城导致不能回去参加考试，也只能提前回去了。我本以为我可以一个人清静清静，本来一切都和我预想的差不多，按部就班的三点一线复习。他妈的，没过两天图书馆又封了，这一封就是三天又三天。我到最后也摆烂，就在房间的里坐着吧，就在房间里面复习，就不出去了。这也就是我的12月在房间里面度过了最后一个月的复习。因为在一个人在房间里面，周围的环境一直都不会改变，容易让自己感觉学了一会就过了很久的感觉，而实际自己也就学了十几分钟而已。所以我也是想方设法的提升自己的专注力，比如拿出便签贴在墙上做好一天的任务，打印倒计时日历，每天在日历上签到，让自己感到紧迫感。(还好有猫猫陪伴)</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-imgcute_cat.webp" alt="猫猫真的好可爱"></p><p>后来的事情大家都应该知道了，放开了感染率也就上去。我也是无意间看到了大概武汉会在12月18号达到感染的最高峰，我本以为我在房间里不出去就不会有事，每天戴好口罩出门买饭就能一切正常的熬到最后的考试。所以我也就没有提前买药，没有准别一些应急用品。很不幸17号我感染了，就很离谱。我什么人也没有接触，就是那天下午我养的那只小野猫，跑回来找我。我把它抱出去玩了一下我就开始头晕难受，直到晚上我真的晕受不了。因为没有温度计，也无法判断自己发烧到了多少度。但是我发现我小米手环上显示我心率一直都在107上下，我判断我应该是发烧了。</p><p>并且烧的还比较厉害，实在是没办法了。背了一会政治就躺下睡觉了。晚上睡着的时候像走马灯一样，我考试那几天各种霉运的画面浮现在我眼前，那简直是一个个噩梦在眼前浮现。不过还好，第二天我烧就退了，虽然有点反复但是只有头疼我就去找在附近住的同学跑腿搞到点布洛芬缓释胶囊。基本上三天后我就剩喉咙痛了，但是我还是背不了书，这是最难受的。就剩最后两三天了我背不了书。我感觉我基本就交代在这里了。不过还好老宋帮我花了一下重点感觉还是挽回点损失。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-imghit_me.webp" alt="给我两拳吧"></p><p>回想起那几天确实十分后怕，一是得亏我父母、爷爷奶奶没有感染。发烧的感觉就像水猴子把你往水下拉。二是得亏没有在考试那几天感染，感觉基本上没有潜伏期，要是考试第二天还挺好第二天直接发烧去考试感觉基本上可以不用做了。因为第二天的数学和专业课是最拉分且最需要思考的科目。</p><p>幸亏我在考前已经感染并且恢复了，虽然考场里面此起彼伏的咳嗽声在考前我已经预料到了。但是并没有我想象的那么烦人，也没有在考试中影响到我。这也算是不幸中的万幸把。</p><h2 id="考完后的回想"><a href="#考完后的回想" class="headerlink" title="考完后的回想"></a>考完后的回想</h2><p>这几天我回顾我从5月分决定拒绝老宋和他去考研机构并在学校租房子住开始，一直到现在考试结束。我是不是做出了一个错误的决定，因为在和他的交流中似乎在机构环境相对固定，受到疫情的影响也不会导致封闭主要的学习场所。虽然他们到后期也是大面积感染，但是我也最后也是感染了。似乎在很多方面要是听取他当时的建议在那边进行学习也不是一个很不错的选择。不会因为我在这边每个月都会遇到一些烦人的事情而浪费一些无畏的时间。是不是我当时考虑太多我想出去玩方便自己，而没有坚定信心全身心投入第二年的复习而导致做出这种错误决定？</p><p>其次在这半年的复习中，我感觉相比较去年我真的松懈了很多，我对我自己的要求也下降了很多。在最后想调动自己认真复习也没有坚持很久。就草草了事，返回了之前的状态。特别是最后一个人在房间复习的时候起的很晚，睡的也很晚。时间感觉没有很高效的被利用起来。想到去年给自己做的展望，要学会做长远的计划，要更加自律。似乎两者我都没有很好的完成。说是完事开头难，我感觉我这个起头的事情一直都没有做的很好。时常放纵自己，拖延问题似乎成了最后一段时间我最大的问题。我却束手无策。</p><h2 id="给自己制定一下目标吧"><a href="#给自己制定一下目标吧" class="headerlink" title="给自己制定一下目标吧"></a>给自己制定一下目标吧</h2><p>我觉得我还是得给自己上紧发条，严格要求自己。严格的在孤独总要求自己，让自己即便是独处也能严格要求自己。我对那种独处的生活十分向往，我本以为自己能在那种环境中继续严格要求自己。但是发现我不能，即便在压力面前我也不能很好的控制自己。再就是考研两年，我感觉我已经被我学过的技术给遗忘了，很多知识和技术我感觉我都忘的七七八八。真得应该抽时间给拾起来了。也希望我能再新的一年能从新回到正轨，从新回答我对自己的发问。再就是考虑自己考复试或者调剂之前能不能找一个工作？找个事情锻炼一下自己，我总觉得我可以处理好我周围的事情但是这种能力我真的有么，真的成熟么。感觉还是需要找个事情来正真检验一下自己。再就是我觉得我的英语学习不能停下，虽然后买你我暂时用不到英语了，但是我感觉我还是需要提升自己的英语能力，为我以后学习技术打下基础。</p><p>如果说今年的生活轨迹是一篇散文的话，那么疫情绝对是这篇散文的中心词。如果说今年是给去年展望的一份答卷的话那么我觉得今年顶多也只能是个不及格，因为我没有完成我去年的期待，让我失望了。虽然失败总是贯穿着人的一生，但是我不希望失望总是环绕在我周围。我现在还记得当时调剂那段日子的晚上，我整夜整夜的睡不着，希望能有学校可以给查看我的信息，能给我发调剂复试通知。但是每当我告诉自己睡吧，说不定我醒来就有学校给我复试通知了呢。但是每次醒来的结果都差不都，什么操作都没有。最多就是查看个人信息，再就是被学校拒绝。唯独一个学校给我发复试通知，只给了我一个小时的准备时间，11点接到复试通知，12点开始复试。匆忙开始复试，以为自己稳了谁知道，他们也只是为了能名正言顺的选择自己学校的学生而匆匆走的流程罢了。</p><p>希望23年能有个好的结果把。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;2022总结&lt;br&gt;</summary>
    
    
    
    <category term="day" scheme="https://www.wulnut.top/categories/day/"/>
    
    
    <category term="回忆" scheme="https://www.wulnut.top/tags/%E5%9B%9E%E5%BF%86/"/>
    
    <category term="杂谈" scheme="https://www.wulnut.top/tags/%E6%9D%82%E8%B0%88/"/>
    
  </entry>
  
  <entry>
    <title>动手学——预备知识</title>
    <link href="https://www.wulnut.top/2022/05/18/%E5%8A%A8%E6%89%8B%E5%AD%A6%E7%AC%AC%E4%BA%8C%E7%AB%A0/"/>
    <id>https://www.wulnut.top/2022/05/18/%E5%8A%A8%E6%89%8B%E5%AD%A6%E7%AC%AC%E4%BA%8C%E7%AB%A0/</id>
    <published>2022-05-18T03:51:54.204Z</published>
    <updated>2023-02-06T15:21:59.179Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是张量"><a href="#什么是张量" class="headerlink" title="什么是张量"></a>什么是张量</h2><p>&emsp;&emsp;张量这个名词原先起源于一个力学概念，然后被数学抽象出来成了一个分支。简单来说张量是用来表示在一个n维空间中某个位置的线性映射</p><p>也就是说n维矢量空间，可以通过线性表达映射到各个维度的坐标上，其坐标就是$n$维空间的$n$个分量也就是$n$个向量。也就是说这些分量也按照某些规则进行线性变换。</p><p>从而改变也就是改变这个矢量空间。那么来回来看深度学习好像就是就是一个高维度的矩阵进行运算从而得出结果，这么看似乎好像Tensor还挺符合这个模式。</p><p>&emsp;&emsp;但是要注意标量不是与之对应的概念，标量又称纯量，只有大小、没有方向、可用实数表示的一个量。</p><p>目的是为区别与向量。张量似乎可以理解成向量与举证的结合体。</p><h2 id="TensorFlow这个名字还挺有意思"><a href="#TensorFlow这个名字还挺有意思" class="headerlink" title="TensorFlow这个名字还挺有意思"></a>TensorFlow这个名字还挺有意思</h2><p>&emsp;&emsp;如果说从数学角度理解张量很抽象的话，来看看Tensorflow似乎不一样的发现。</p><ul><li>TensorFlow使用Tensor来表示数据</li><li>TensorFlow在内部将张量表示为<strong>基本数据类型的n维数组</strong></li></ul><p>&emsp;&emsp;连起来似乎就是<strong>TensorFlow所有数据都是一个n维的数组，只是给我们找了在数学中与其对应的概念叫张量（Tensor）</strong></p><h2 id="Pytorch基本函数"><a href="#Pytorch基本函数" class="headerlink" title="Pytorch基本函数"></a>Pytorch基本函数</h2><h3 id="torch-arange"><a href="#torch-arange" class="headerlink" title="torch.arange()"></a>torch.arange()</h3><pre><code class="hljs py">torch.arange(start=<span class="hljs-number">0</span>, end, step=<span class="hljs-number">1</span>, *, out=<span class="hljs-literal">None</span>, dtype=<span class="hljs-literal">None</span>,             layout=torch.strided, device=<span class="hljs-literal">None</span>, requires_grad=<span class="hljs-literal">False</span>)</code></pre><p>&emsp;&emsp;arange函数与numpy中的arange类似，torch.arange返回一个大小为$\lceil\frac{\text{end}-\text{start}}{step}\rceil$的一维张量，</p><p>并使用从start开始区间为[start, end)的公差step。</p><h3 id="torch-cat"><a href="#torch-cat" class="headerlink" title="torch.cat()"></a>torch.cat()</h3><pre><code class="hljs py">torch.cat(tensors, dim=<span class="hljs-number">0</span>, *, out=<span class="hljs-literal">None</span>)</code></pre><p>&emsp;&emsp;连接给定维度中给定的张量序列<code>seq</code>。所有张量必须具有相同的形状（连接维度除外）或为空。<code>torch.cat()</code>可以看作<code>torch.split()</code>和<code>torch.chunk()</code>的逆运算</p><ul><li>tensors (张量序列)任何相同类型的张量的python序列。提供的非空张量必须具有相同的形状，cat维度除外</li><li>dim(int, 选项)张量连接的维度</li><li>out(Tensor, 选项)输出张量</li></ul><pre><code class="hljs py"><span class="hljs-meta">&gt;&gt;&gt; </span>x = torch.randn(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>)<span class="hljs-meta">&gt;&gt;&gt; </span>xtensor([[ <span class="hljs-number">0.6580</span>, -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>],        [-<span class="hljs-number">0.1034</span>, -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>]])<span class="hljs-meta">&gt;&gt;&gt; </span>torch.cat((x, x, x), <span class="hljs-number">0</span>)tensor([[ <span class="hljs-number">0.6580</span>, -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>],        [-<span class="hljs-number">0.1034</span>, -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>],        [ <span class="hljs-number">0.6580</span>, -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>],        [-<span class="hljs-number">0.1034</span>, -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>],        [ <span class="hljs-number">0.6580</span>, -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>],        [-<span class="hljs-number">0.1034</span>, -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>]])<span class="hljs-meta">&gt;&gt;&gt; </span>torch.cat((x, x, x), <span class="hljs-number">1</span>)tensor([[ <span class="hljs-number">0.6580</span>, -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>,  <span class="hljs-number">0.6580</span>, -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>,  <span class="hljs-number">0.6580</span>,         -<span class="hljs-number">1.0969</span>, -<span class="hljs-number">0.4614</span>],        [-<span class="hljs-number">0.1034</span>, -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>, -<span class="hljs-number">0.1034</span>, -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>, -<span class="hljs-number">0.1034</span>,         -<span class="hljs-number">0.5790</span>,  <span class="hljs-number">0.1497</span>]])</code></pre><p>可以看出上述例子，如果dim为0时结果为x直接按行拼接，如果dim为1时就按照列拼接也可以理解为按照一维进行拼接。也就是说二维张量dim最大为1， 三维张量dim最大为2。</p><h3 id="torch-zeros-与torch-zeros-like"><a href="#torch-zeros-与torch-zeros-like" class="headerlink" title="torch.zeros()与torch.zeros_like()"></a>torch.zeros()与torch.zeros_like()</h3><ul><li>torch.zeros_like(tensor)：是根据<strong>给定张量</strong>，生成与其形状相同的全0张量</li><li>torch.zeros(size)：其形状由变量参数<strong>size定义</strong>，返回一个由标量值0填充的张量</li></ul><h2 id="张量计算"><a href="#张量计算" class="headerlink" title="张量计算"></a>张量计算</h2><p>&emsp;&emsp;在张量计算中我们要注意，只有具有相同形状的张量才能进行计算。</p><h3 id="广播机制"><a href="#广播机制" class="headerlink" title="广播机制"></a>广播机制</h3><p>&emsp;&emsp;广播机制说白了就是为了解决不同形状的张量进行相加，注意矩阵是无法做到这一点的。通过下面的例子可</p><p>可以说明这一点。</p><pre><code class="hljs py">a = torch.arange(<span class="hljs-number">3</span>).reshape((<span class="hljs-number">3</span>, <span class="hljs-number">1</span>))b = torch.arange(<span class="hljs-number">2</span>).reshape((<span class="hljs-number">1</span>, <span class="hljs-number">2</span>))a, ba + b</code></pre><p>运行结果为：</p><pre><code class="hljs py">(tensor([[<span class="hljs-number">0</span>],         [<span class="hljs-number">1</span>],         [<span class="hljs-number">2</span>]]), tensor([[<span class="hljs-number">0</span>, <span class="hljs-number">1</span>]]))tensor([[<span class="hljs-number">0</span>, <span class="hljs-number">1</span>],        [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>],        [<span class="hljs-number">2</span>, <span class="hljs-number">3</span>]])</code></pre><h2 id="内存节省"><a href="#内存节省" class="headerlink" title="内存节省"></a>内存节省</h2><p>&emsp;&emsp;在Cpp中对变量运算进行操作访问的实质是其地址。所以操作时变量地址是不会改变的。</p><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-keyword">int</span> a = <span class="hljs-number">2</span>, b = <span class="hljs-number">3</span>;    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d\n&quot;</span>, &amp;a);    a = a + b;    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d&quot;</span>, &amp;a);    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;&#125;</code></pre><p>运行结果为：</p><pre><code class="hljs cpp"><span class="hljs-number">-197368776</span><span class="hljs-number">-197368776</span></code></pre><p>&emsp;&emsp;但是在Python中由于操作的变量相加或者其他数值运算时是会开辟新的地址进行存储</p><pre><code class="hljs py">before = <span class="hljs-built_in">id</span>(Y) <span class="hljs-comment"># id()函数表示查看对象内存地址</span>Y = Y + X<span class="hljs-built_in">id</span>(Y) == before <span class="hljs-comment"># 说明Y的对象地址已经改变</span></code></pre><p>结果为：</p><pre><code class="hljs py"><span class="hljs-literal">False</span></code></pre><p>&emsp;&emsp;为了解决这个问题我们可以采用不同的语法来解决：</p><pre><code class="hljs py">X[:] = X + YX += Y</code></pre><p>&emsp;&emsp;当然这种方法解决内存问题只是从语法层面对我们进行了规范。但是当程序执行过程中RAM中有大量对象处于</p><p>活动状态时，可能会出现内存问题，特别是在对可用内存总量有限制的情况下。下面概述了一些减小对象大小的方法，这些</p><p>方法可以显著减少纯Python程序所需的RAM数量。</p><h3 id="Dict"><a href="#Dict" class="headerlink" title="Dict"></a>Dict</h3><p>&emsp;&emsp;在小程序中，特别是在脚本中，使用内置的dict来表示结构信息是非常简单方便的:</p><pre><code class="hljs py"><span class="hljs-meta">&gt;&gt;&gt; </span>ob = &#123;<span class="hljs-string">&#x27;x&#x27;</span>:<span class="hljs-number">1</span>, <span class="hljs-string">&#x27;y&#x27;</span>:<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;z&#x27;</span>:<span class="hljs-number">3</span>&#125;<span class="hljs-meta">&gt;&gt;&gt; </span>x = ob[<span class="hljs-string">&#x27;x&#x27;</span>]<span class="hljs-meta">&gt;&gt;&gt; </span>ob[<span class="hljs-string">&#x27;y&#x27;</span>] = y</code></pre><p>&emsp;&emsp;随着Python 3.6中使用一组有序键的更紧凑实现方式的出现，dict变得更有吸引力。但是，让我们看看它在</p><p>RAM中的内存大小:</p><pre><code class="hljs py"><span class="hljs-meta">&gt;&gt;&gt; </span>print(sys.getsizeof(ob))<span class="hljs-number">240</span></code></pre><p>它需要大量内存，特别是当你突然需要创建大量实例时:</p><div class="table-container"><table><thead><tr><th>实例数量</th><th>对象大小</th></tr></thead><tbody><tr><td>1000000</td><td>240Mb</td></tr><tr><td>10000000</td><td>2.40Gb</td></tr><tr><td>100000000</td><td>24Gb</td></tr></tbody></table></div><h3 id="实例类"><a href="#实例类" class="headerlink" title="实例类"></a>实例类</h3><p>&emsp;&emsp;对于那些喜欢将所有东西放置在类中的人来说，最好将结构定义为一个可以通过属性名访问的类</p><pre><code class="hljs py"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>:</span>        <span class="hljs-comment">#</span>        <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, x, y, z</span>):</span>                self.x = x                self.y = y                self.z = z<span class="hljs-meta">&gt;&gt;&gt; </span>ob   = Point(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>)<span class="hljs-meta">&gt;&gt;&gt; </span>x    = ob.x<span class="hljs-meta">&gt;&gt;&gt; </span>ob.y = y</code></pre><p>&emsp;&emsp;类实例的结构很有趣：</p><div class="table-container"><table><thead><tr><th>实例数量</th><th>对象大小</th></tr></thead><tbody><tr><td>1000000</td><td>240Mb</td></tr><tr><td>10000000</td><td>2.40Gb</td></tr><tr><td>100000000</td><td>24Gb</td></tr></tbody></table></div><p>&emsp;&emsp;这里的<strong>weakref</strong>是对这个对象的所谓弱引用列表的一个引用，<strong>dict</strong>字段是对类实例字典的引用，它包含实例</p><p>属性的值(注意64位的引用平台会占用8个字节)。从Python 3.3开始，共享空间用于在字典中存储类的所有实例的键。这减</p><p>少了RAM中实例堆栈的大小:</p><pre><code class="hljs py"><span class="hljs-meta">&gt;&gt;&gt; </span>print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))<span class="hljs-number">56</span> <span class="hljs-number">112</span></code></pre><p>因此，大量的类实例占用的内存比一个普通字典(dict)占用的要小:</p><div class="table-container"><table><thead><tr><th>实例数量</th><th>对象大小</th></tr></thead><tbody><tr><td>1000000</td><td>168Mb</td></tr><tr><td>10000000</td><td>1.68Gb</td></tr><tr><td>100000000</td><td>16.8Gb</td></tr></tbody></table></div><p>很容易看出，由于实例字典的大小，RAM中实例的大小仍然很大。</p><h3 id="带有slots的类实例"><a href="#带有slots的类实例" class="headerlink" title="带有slots的类实例"></a>带有slots的类实例</h3><p>&emsp;&emsp;通过消除 dict和weakref，可以显著减小RAM中的类实例的大小。这通过一个带有slots的小“技巧”是可能实现的：</p><pre><code class="hljs py"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>:</span>        __slots__ = <span class="hljs-string">&#x27;x&#x27;</span>, <span class="hljs-string">&#x27;y&#x27;</span>, <span class="hljs-string">&#x27;z&#x27;</span>        <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, x, y, z</span>):</span>                self.x = x, self.y = y, self.z = z<span class="hljs-meta">&gt;&gt;&gt; </span>ob = Point(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>)<span class="hljs-meta">&gt;&gt;&gt; </span>print(sys.getsizeof(ob))<span class="hljs-number">64</span></code></pre><p>RAM中对象大小明显减少：</p><div class="table-container"><table><thead><tr><th>字段</th><th>大小（字节）</th></tr></thead><tbody><tr><td>PyGC_Head</td><td>24</td></tr><tr><td>PyObject_HEAD</td><td>16</td></tr><tr><td>x</td><td>8</td></tr><tr><td>y</td><td>8</td></tr><tr><td>z</td><td>8</td></tr><tr><td>总数：</td><td>64</td></tr></tbody></table></div><p>在类定义中使用slots可以显著减少大量实例对内存空间的占用:</p><div class="table-container"><table><thead><tr><th>实例数量</th><th>对象大小</th></tr></thead><tbody><tr><td>1000000</td><td>64Mb</td></tr><tr><td>10000000</td><td>640Mb</td></tr><tr><td>100000000</td><td>6.4Gb</td></tr></tbody></table></div><p>要自动化使用 slots创建一个类的过程，有一个库<a href="https://pypi.org/project/namedlist">namedlist</a>可以使用。</p><p>namedlist.namedlist函数会创建一个带有slots的类:</p><pre><code class="hljs py">Point = namedlist(<span class="hljs-string">&#x27;Point&#x27;</span>, (<span class="hljs-string">&#x27;x&#x27;</span>, <span class="hljs-string">&#x27;y&#x27;</span>))</code></pre><p>另一个包<a href="https://pypi.org/project/attrs">attrs</a>允许你使用和不使用slots自动创建类。</p><!-- https://www.jianshu.com/p/027f44b2b108 --><h2 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h2><p>&emsp;&emsp;Pandas的用途十分广泛，Pandas与张量也可以兼容。下面主要介绍使用pandas预处理原始数据，并将原</p><p>始数据转换为张量格式的步骤。</p><h3 id="os-makedirs-与os-mkdir"><a href="#os-makedirs-与os-mkdir" class="headerlink" title="os.makedirs()与os.mkdir"></a>os.makedirs()与os.mkdir</h3><p>&emsp;&emsp;首先说os.mkdir(path)，他的功能是一级一级的创建目录，前提是前面的目录已存在，如果不存在会报异</p><p>常，比较麻烦，但是存在即有他的道理，当你的目录是根据文件名动态创建的时候，你会发现他虽然繁琐但是很有保障，不</p><p>会因为你的一时手抖，创建而创建了双层或者多层错误路径.</p><pre><code class="hljs py"><span class="hljs-keyword">import</span> os os.mkdir(<span class="hljs-string">&#x27;d:\hello&#x27;</span>)    <span class="hljs-comment">#  正常</span>os.mkdir(<span class="hljs-string">&#x27;d:\hello\hi&#x27;</span>) <span class="hljs-comment">#  正常</span> <span class="hljs-comment">#  如果d:\hello目录不存在</span><span class="hljs-comment">#  则os.mkdir(&#x27;d:\hello\hi&#x27;)执行失败</span></code></pre><p>&emsp;&emsp;然后是os.makedirs(path),单从写法上就能猜出他的区别，他可以一次创建多级目录，哪怕中间目录不存</p><p>在也能正常的（替你）创建.</p><pre><code class="hljs py"><span class="hljs-keyword">import</span> os os.makedirs(<span class="hljs-string">&#x27;d:\hello&#x27;</span>)    <span class="hljs-comment">#  正常</span>os.makedirs(<span class="hljs-string">&#x27;d:\hello\hi&#x27;</span>) <span class="hljs-comment">#  正常</span> <span class="hljs-comment">#  如果d:\hello目录不存在</span><span class="hljs-comment">#  则os.mkdir(&#x27;d:\hello\hi&#x27;)  #  仍然正常</span></code></pre><h2 id="自动积分框架"><a href="#自动积分框架" class="headerlink" title="自动积分框架"></a>自动积分框架</h2><!-- 不错的博客：https://www.cnblogs.com/yanqiang/p/12771928.html --><p>grad_fn: 记录的是创建该张量时所用的方法</p><p>例如：MulBackward0、AddBackward0、MeanBackward1等等</p><h2 id="matplotlib基础用法"><a href="#matplotlib基础用法" class="headerlink" title="matplotlib基础用法"></a>matplotlib基础用法</h2><h3 id="Matplotlib-绘图基础"><a href="#Matplotlib-绘图基础" class="headerlink" title="Matplotlib 绘图基础"></a>Matplotlib 绘图基础</h3><h4 id="xlim-和ylim-在Matplotlib种设置轴的限制"><a href="#xlim-和ylim-在Matplotlib种设置轴的限制" class="headerlink" title="xlim()和ylim()在Matplotlib种设置轴的限制"></a>xlim()和ylim()在Matplotlib种设置轴的限制</h4><p>&emsp;&emsp;<code>matplotlib.pyplot.xlim()</code>和<code>matplotlib.pyplot.ylim()</code>可用于分别设置或获取 X 轴和 Y 轴的</p><p>范围限制。如果在这些方法中传递参数，则它们将设置各个轴的极限，如果不传递任何参数，则将获得各个轴的范围。例如</p><p>绘制$\sin(2 \pi x) + 1$的图像。</p><pre><code class="hljs py"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> pltx = np.linespace(<span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">500</span>)y = np.sin(<span class="hljs-number">2</span> * np.pi * x) + <span class="hljs-number">1</span>fig = plt.figure(figsize=(<span class="hljs-number">8</span>, <span class="hljs-number">6</span>))plt.plot(x, y)plt.title(<span class="hljs-string">&quot;Setting range of Axes&quot;</span>, fontsize=<span class="hljs-number">25</span>)plt.xlabel(<span class="hljs-string">&quot;x&quot;</span>,fontsize=<span class="hljs-number">18</span>)plt.ylabel(<span class="hljs-string">&quot;1+sinx&quot;</span>,fontsize=<span class="hljs-number">18</span>)plt.xlim(<span class="hljs-number">4</span>,<span class="hljs-number">8</span>)plt.ylim(-<span class="hljs-number">0.5</span>,<span class="hljs-number">2.5</span>)plt.show()</code></pre><h4 id="set-xlim-和-set-ylim-方法来设置轴限制"><a href="#set-xlim-和-set-ylim-方法来设置轴限制" class="headerlink" title="set_xlim() 和 set_ylim() 方法来设置轴限制"></a>set_xlim() 和 set_ylim() 方法来设置轴限制</h4><p>&emsp;&emsp;<code>matplotlib.axes.Axes.set_xlim</code> 和 <code>matplotlib.axes.Axes.set_ylim</code> 也用于设置在结果图上查</p><p>看的数字范围的限制。</p><pre><code class="hljs py"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> pltx=np.linspace(<span class="hljs-number">0</span>,<span class="hljs-number">10</span>,<span class="hljs-number">500</span>)y=np.sin(<span class="hljs-number">2</span> * np.pi * x)+<span class="hljs-number">1</span>fig = plt.figure(figsize=(<span class="hljs-number">8</span>, <span class="hljs-number">6</span>))axes = plt.axes()axes.set_xlim([<span class="hljs-number">4</span>, <span class="hljs-number">8</span>])axes.set_ylim([-<span class="hljs-number">0.5</span>, <span class="hljs-number">2.5</span>])plt.plot(x, y)plt.title(<span class="hljs-string">&quot;Setting range of Axes&quot;</span>,fontsize=<span class="hljs-number">25</span>)plt.xlabel(<span class="hljs-string">&quot;x&quot;</span>,fontsize=<span class="hljs-number">18</span>)plt.ylabel(<span class="hljs-string">&quot;1+sinx&quot;</span>,fontsize=<span class="hljs-number">18</span>)plt.show()</code></pre><h4 id="使用-axis-方法在-Matplotlib-中设置轴的限制"><a href="#使用-axis-方法在-Matplotlib-中设置轴的限制" class="headerlink" title="使用 axis() 方法在 Matplotlib 中设置轴的限制"></a>使用 axis() 方法在 Matplotlib 中设置轴的限制</h4><p>&emsp;&emsp;也可以使用 matplotlib.pyplot.axis() 来设置轴的范围限制。语法如下：</p><pre><code class="hljs py">plt.axis([xmin, xmax, ymin, ymax])</code></pre><p>该方法消除了用于控制 X 轴和 Y 轴的单独功能的需要。</p><pre><code class="hljs py"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> pltx=np.linspace(<span class="hljs-number">0</span>,<span class="hljs-number">10</span>,<span class="hljs-number">50</span>)y=np.sin(<span class="hljs-number">2</span> * np.pi * x)+<span class="hljs-number">1</span>fig = plt.figure(figsize=(<span class="hljs-number">8</span>, <span class="hljs-number">6</span>))plt.axis([<span class="hljs-number">4</span>, <span class="hljs-number">9</span>, -<span class="hljs-number">0.5</span>, <span class="hljs-number">2.5</span>])plt.plot(x, y)plt.title(<span class="hljs-string">&quot;Setting range of Axes&quot;</span>,fontsize=<span class="hljs-number">25</span>)plt.xlabel(<span class="hljs-string">&quot;x&quot;</span>,fontsize=<span class="hljs-number">18</span>)plt.ylabel(<span class="hljs-string">&quot;1+sinx&quot;</span>,fontsize=<span class="hljs-number">18</span>)plt.show()</code></pre><h2 id="axse-plot绘制"><a href="#axse-plot绘制" class="headerlink" title="axse.plot绘制"></a>axse.plot绘制</h2><p>&emsp;&emsp;Format Setting分别是标记、线条、颜色</p><pre><code class="hljs py">fmt = <span class="hljs-string">&#x27;[marker][line][color]&#x27;</span></code></pre><p><strong>Markers</strong><br>标记设置</p><div class="table-container"><table><thead><tr><th>character</th><th>description</th></tr></thead><tbody><tr><td><code>&#39;.&#39;</code></td><td>point marker</td></tr><tr><td><code>&#39;,&#39;</code></td><td>pixel marker</td></tr><tr><td><code>&#39;o&#39;</code></td><td>circle marker</td></tr><tr><td><code>&#39;v&#39;</code></td><td>triangle_down marker</td></tr><tr><td><code>&#39;^&#39;</code></td><td>triangle_up marker</td></tr><tr><td><code>&#39;&lt;&#39;</code></td><td>triangle_left marker</td></tr><tr><td><code>&#39;&gt;&#39;</code></td><td>triangle_right marker</td></tr><tr><td><code>&#39;1&#39;</code></td><td>tri_down marker</td></tr><tr><td><code>&#39;2&#39;</code></td><td>tri_up marker</td></tr><tr><td><code>&#39;3&#39;</code></td><td>tri_left marker</td></tr><tr><td><code>&#39;4&#39;</code></td><td>tri_right marker</td></tr><tr><td><code>&#39;8&#39;</code></td><td>octagon marker</td></tr><tr><td><code>&#39;s&#39;</code></td><td>square marker</td></tr><tr><td><code>&#39;p&#39;</code></td><td>pentagon marker</td></tr><tr><td><code>&#39;P&#39;</code></td><td>plus (filled) marker</td></tr><tr><td><code>&#39;*&#39;</code></td><td>star marker</td></tr><tr><td><code>&#39;h&#39;</code></td><td>hexagon1 marker</td></tr><tr><td><code>&#39;H&#39;</code></td><td>hexagon2 marker</td></tr><tr><td><code>&#39;+&#39;</code></td><td>plus marker</td></tr><tr><td><code>&#39;x&#39;</code></td><td>x marker</td></tr><tr><td><code>&#39;X&#39;</code></td><td>x (filled) marker</td></tr><tr><td><code>&#39;D&#39;</code></td><td>diamond marker</td></tr><tr><td><code>&#39;d&#39;</code></td><td>thin_diamond marker</td></tr><tr><td><code>&#39;_&#39;</code></td><td>hline marker</td></tr></tbody></table></div><p><strong>Line Styles</strong><br>曲线颜色</p><div class="table-container"><table><thead><tr><th>character</th><th>description</th></tr></thead><tbody><tr><td><code>&#39;-&#39;</code></td><td>solid line style</td></tr><tr><td><code>&#39;--&#39;</code></td><td>dashed line style</td></tr><tr><td><code>&#39;-.&#39;</code></td><td>dash-dot line style</td></tr><tr><td><code>&#39;:&#39;</code></td><td>dotted line style</td></tr></tbody></table></div><p>例如：</p><pre><code class="hljs py"><span class="hljs-string">&#x27;b&#x27;</span>    <span class="hljs-comment"># blue markers with default shape</span><span class="hljs-string">&#x27;or&#x27;</span>   <span class="hljs-comment"># red circles</span><span class="hljs-string">&#x27;-g&#x27;</span>   <span class="hljs-comment"># green solid line</span><span class="hljs-string">&#x27;--&#x27;</span>   <span class="hljs-comment"># dashed line with default color</span><span class="hljs-string">&#x27;^k:&#x27;</span>  <span class="hljs-comment"># black triangle_up markers connected by a dotted line</span></code></pre><p><strong>Colors</strong><br>支持的颜色缩写是单字母代码</p><div class="table-container"><table><thead><tr><th>character</th><th>color</th></tr></thead><tbody><tr><td><code>&#39;b&#39;</code></td><td>blue</td></tr><tr><td><code>&#39;g&#39;</code></td><td>green</td></tr><tr><td><code>&#39;r&#39;</code></td><td>red</td></tr><tr><td><code>&#39;c&#39;</code></td><td>cyan</td></tr><tr><td><code>&#39;m&#39;</code></td><td>magenta</td></tr><tr><td><code>&#39;y&#39;</code></td><td>yellow</td></tr><tr><td><code>&#39;k&#39;</code></td><td>black</td></tr><tr><td><code>&#39;w&#39;</code></td><td>white</td></tr></tbody></table></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;什么是张量&quot;&gt;&lt;a href=&quot;#什么是张量&quot; class=&quot;headerlink&quot; title=&quot;什么是张量&quot;&gt;&lt;/a&gt;什么是张量&lt;/h2&gt;&lt;p&gt;&amp;emsp;&amp;emsp;张量这个名词原先起源于一个力学概念，然后被数学抽象出来成了一个分支。简单来说张量是用来表示在</summary>
      
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="学习" scheme="https://www.wulnut.top/tags/%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="机器学习" scheme="https://www.wulnut.top/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>两个月健身总结</title>
    <link href="https://www.wulnut.top/2022/03/03/%E6%80%BB%E7%BB%93%E4%B8%A4%E4%B8%AA%E6%9C%88%E5%81%A5%E8%BA%AB/"/>
    <id>https://www.wulnut.top/2022/03/03/%E6%80%BB%E7%BB%93%E4%B8%A4%E4%B8%AA%E6%9C%88%E5%81%A5%E8%BA%AB/</id>
    <published>2022-03-03T14:05:45.315Z</published>
    <updated>2023-02-06T15:23:18.334Z</updated>
    
    <content type="html"><![CDATA[<p>马上要返校健身房的老板和我们聊了一下</p><a id="more"></a><h2 id="说两句"><a href="#说两句" class="headerlink" title="说两句"></a>说两句</h2><p>&emsp;&emsp;两个月下来除去春节的一个星期和中间的休息大概有个一个半月左右。效果还是有的，但是</p><p>说是否明显就很难说了。但是这两个月来说，我收获还是很多的，因为健身其实也算是一个系统工程。</p><p>需要从基础动作开始打好基础，然后通过熟悉动作训练大肌群和多肌群的用力，才能上重量。我基本上</p><p>已经完成了第一步，三个基础动作我基本没得什么问题了，就是硬拉我还是不行，动作发力点还是没找好。</p><p>但是总体练下来我觉得还是很有乐趣的。</p><p>&emsp;&emsp;其实那个教练还是很厉害的，他说了句解剖决定功能功能决定训练，这也说明了为什么我们要</p><p>这么练，为什么练没有用。我觉得这些东西就像是我们学习某种技术，一开始先学会怎么用，然后要吃透为什么这么用</p><p>就需要学习源码。我觉得这其实也是一种从浅入深的过程。首选需要我们对一种新的知识会用，然后通过练习从而熟习，</p><p>熟习之后就应该学习为什么，为什么要这样做而这个过程就是把知识变成自己的过程。当然还有更高级的把不同章节的知识学会融汇贯通，这个应该需要我们不断的复习重复才能实现。</p><p>但是这也是最难的。</p><h2 id="关于健身"><a href="#关于健身" class="headerlink" title="关于健身"></a>关于健身</h2><p>&emsp;&emsp;我觉得健身是一个很枯燥的运动，因为他需要我们把简单事情重复做，困难的事情一步一步做。并且需要长期坚持才能出成果，和我平时学习也有点类似，要把基础的简单知识学会</p><p>并重复练习，然后把困难的东西一步一步做，逐步提升难度，并长期坚持。我还记得一句话，往往上私教课的人看不出他能练成什么样子，因为我只是把动作的样子和一些发力的要领交给他了。</p><p>但是要领会这些东西还需要学员自己课后的不断练习熟悉发力技巧才能正真掌握一个动作。我觉得这确实很真实，因为在做数学概念题的时候就是这样，虽然知道概念是什么但是在实际运用的过程中还是频频出错</p><p>需要反复的总结才能熟悉并掌握一个概念，也就突出了要总结的和反思的重要性。</p><p>&emsp;&emsp;我一开始觉得健身就是一个简单的事情，因为没有难度只需要反复练习不断加重量就能有效果，虽然说这确实可以但是在相同时间下没有那些有详细规划，正确掌握动作的人来的效率高提升快。这就是为什么</p><p>凡事都要讲究一个技巧，才能真正在一定时间内达到目的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;马上要返校健身房的老板和我们聊了一下&lt;/p&gt;</summary>
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="健身" scheme="https://www.wulnut.top/tags/%E5%81%A5%E8%BA%AB/"/>
    
    <category term="学习" scheme="https://www.wulnut.top/tags/%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>健身的浅见</title>
    <link href="https://www.wulnut.top/2022/02/13/%E5%81%A5%E8%BA%AB%E6%B5%85%E8%B0%88/"/>
    <id>https://www.wulnut.top/2022/02/13/%E5%81%A5%E8%BA%AB%E6%B5%85%E8%B0%88/</id>
    <published>2022-02-13T14:31:43.678Z</published>
    <updated>2023-02-06T15:25:21.290Z</updated>
    
    <content type="html"><![CDATA[<p>健身了快一个寒假，有了一些自己的理解简单记录一下。</p><a id="more"></a><h2 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h2><p>&emsp;&emsp;对于健身来说，全部的动作总体可以分为三部分。推、拉、腿。三部分，因为这三部分是我们日常生活中最为常见的用力动作。</p><p>通过这三个方面也对应着三个基础动作，卧推、硬拉、深蹲。这三个动作不仅仅是锻炼单一肌群，而是身体一些部分的所有肌群。</p><p>这是我这种小白刚入门容易误入的一个误区，直接去练习一些针对性的动作从而达到一些心理上所为的正确目的。但是要是这样，</p><p>就是去了我来健身房的目的，要是按照这种错误的想法，其实通过囚徒的方法似乎更加高效和明显。</p><p>&emsp;&emsp;通过这三个基础动作，可以衍生出其他一些器械和哑铃动作。这也是为什么一上来就要先练习这三个动作，通过这些动作的不足</p><p>我们再对其他一些肌群进行补强和针对性练习。也是我的大体思路，以总体带动局部肌群。而不是单一刺激局部肌群。</p><h3 id="推"><a href="#推" class="headerlink" title="推"></a>推</h3><p>&emsp;&emsp;bench pass(杠铃卧推)，可以理解成胸前水平推举，主要刺激的是胸大肌、肱三头肌、三角肌。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/%E5%8D%A7%E6%8E%A8%E8%82%8C%E7%BE%A4.webp" alt="卧推肌群"></p><p>也就是说通过卧推，可以总体提升自己的上肢力量。也就是说我们再练习完卧推后，可以进一步专项增强胸大肌、肱三头肌、三角肌。</p><p>从而达到全面刺激的目的。拉和腿也是一样</p><h3 id="拉"><a href="#拉" class="headerlink" title="拉"></a>拉</h3><p>&emsp;&emsp;Barbell deadlift(杠铃硬拉)，相当于我们从下向上抬起东西，主要刺激斜方肌、竖脊肌、背阔肌、核心肌群、臀大肌、腘绳肌、股四头肌。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/deadlift.webp" alt="Barbell deadlift"></p><p>这个动作锻炼的部位很多，但是发力也相对难掌握。必须通过小重量理解发力点和发力细节不然对腰的损伤是十分明显的。</p><h3 id="腿"><a href="#腿" class="headerlink" title="腿"></a>腿</h3><p>&emsp;&emsp;Barbell squat(杠铃深蹲),蹲起是我们日常是生活种的常用动作，主要锻炼的是两侧肌肉以及臀部肌肉。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/squat.webp" alt="Barbell squat"></p><p>深蹲是对腿部肌群总体锻炼的一个动作，但是这个动作对膝盖的压力会比较大，所以需要我们标准做好每次蹲起</p><h2 id="训练规划"><a href="#训练规划" class="headerlink" title="训练规划"></a>训练规划</h2><p>&emsp;&emsp;训练规划是我认为最为重要的一个环节，仅此与标准的动作。我认为如果没有个科学的训练计划，那么训练起来</p><p>出效果的周期就会很长，一些需要的练习到的肌肉锻炼不够充分等等。</p><p>&emsp;&emsp;目前来说我认为时间充裕的话，应该一周七天，需要六练一休息并且中间穿插两次有氧训练。这应该是我目前来说最为</p><p>满意的训练节奏，但是懂得都懂有氧实在是太难受了，想坚持下来确实有点不太容易。如果再细致一点我是这样规划的，周一到周三</p><p>分别锻炼推、拉、腿。周四休息一次，然后剩下的三天同理。因为在周三和周日连腿，所以有氧连起来会比较困难，一般有氧应该放在周二</p><p>和周六。</p><p>&emsp;&emsp;当然周一到周三期间，每天练习的动作其实我觉得也是有一些关系的，应该在一些地方相互避开。周一的练习卧推时主要时练</p><p>胸、肩、三头肌。那我们就可以再着重练习完卧推之后对剩下的肌群进行加强。周二练习硬拉后可以再加强背和腰，例如：引体向上。</p><p>周三练腿之后可以加强对腹部肌肉的训练，这样的基本三天下来，可以把身体上的各个肌肉锻炼到。从而往复训练，可以达到不错的效果。</p><p>健身其实就是简单的事情重复做，总体来说是比较枯燥的。如果不搭配其他动作混合训练，一次训练课来有点难坚持枯燥不说，肌肉耐受强度</p><p>达到最大后，效率就会下降。所以我们需要制定好训练计划这样才能更好的达到自己的训练效果。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;健身了快一个寒假，有了一些自己的理解简单记录一下。&lt;/p&gt;</summary>
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="健身" scheme="https://www.wulnut.top/tags/%E5%81%A5%E8%BA%AB/"/>
    
    <category term="学习" scheme="https://www.wulnut.top/tags/%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>折腾旁路由</title>
    <link href="https://www.wulnut.top/2022/02/11/%E6%97%81%E8%B7%AF%E7%94%B1/"/>
    <id>https://www.wulnut.top/2022/02/11/%E6%97%81%E8%B7%AF%E7%94%B1/</id>
    <published>2022-02-11T10:06:10.395Z</published>
    <updated>2023-02-06T15:28:50.152Z</updated>
    
    <content type="html"><![CDATA[<p>浅谈如何设置旁路由器</p><a id="more"></a><h2 id="序"><a href="#序" class="headerlink" title="序"></a>序</h2><p>由于市面上的主流路由器例如：小米、荣耀、华为、TP-Link、水星、华三等等路由器，厂家为了方便用户使用，</p><p>往往会是使用定制系统，让用户尽量简单且只管的设置路由器，从而帮助用户在家庭、寝室等环境内快速使用无线</p><p>路由功能。但是这也会给一些用于带来困扰，比如国内对家用路由器的功率大小有严格的限制，所以在一定条件下无</p><p>法充分发挥路由器的信号的强度。从而就慢慢了解到了openwrt这样的主流路由器系统，但是由于主流路由器的厂商</p><p>为了避免用户修改路由器本身系统就会通过各种方式来限制用户从新刷写路由器系统，所以国内也有一种独特的路由</p><p>方式：旁路由。旁路由准确来说是旁路网关，也就说在整个家庭网络环境中，我们通过统一更改设备网关来达到目的。</p><h2 id="设置"><a href="#设置" class="headerlink" title="设置"></a>设置</h2><h3 id="主路由"><a href="#主路由" class="headerlink" title="主路由"></a>主路由</h3><p>对于一些路由器厂商例如华为、荣耀、TP-Link需要设置WAN网口我们用荣耀路由器为例</p><ol><li><p>首先去荣耀路由器关闭WAN/LAN自动识别： 在更多功能-系统设置<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/honor1.webp" alt="设置WAN口"></p></li><li><p>然后关闭荣耀路由的DHCP功能：更多设置-网络设置-局域网-关闭DHCP<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/honor2.webp" alt="关闭DHCP"></p></li><li><p>如果你家是电信的宽带，一般会分配ipv6地址给你，我想你都点开这篇博客了肯定是想用旁路由干点啥，我建议你关闭ipv6，因为这可能会对你的openwrt旁路由产生影响<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/honor3.webp" alt="关闭ipv6"></p></li></ol><p>好到这里你的主路由已经设置好了然后我们再来设置旁路由</p><h3 id="旁路由"><a href="#旁路由" class="headerlink" title="旁路由"></a>旁路由</h3><p>首先你需要有个已经刷好openwrt固件的路由器，我用的是一个树莓派。</p><p>如果还没有刷入固件可以参照下表根据自己的需求来进行下载相关固件</p><div class="table-container"><table><thead><tr><th><strong>支持设备</strong></th><th><strong>支持平台</strong></th><th><strong>固件下载</strong></th></tr></thead><tbody><tr><td>树莓派 1B</td><td>bcm27xx/bcm2708</td><td><a href="https://openwrt.cc/releases/targets/bcm27xx/bcm2708/">🔗</a></td></tr><tr><td>树莓派 2B</td><td>bcm27xx/bcm2709</td><td><a href="https://openwrt.cc/releases/targets/bcm27xx/bcm2709/">🔗</a></td></tr><tr><td>树莓派 3B</td><td>bcm27xx/bcm2710</td><td><a href="https://openwrt.cc/releases/targets/bcm27xx/bcm2710/">🔗</a></td></tr><tr><td>树莓派 4B</td><td>bcm27xx/bcm2711</td><td><a href="https://openwrt.cc/releases/targets/bcm27xx/bcm2711/">🔗</a></td></tr><tr><td>ipq40xx 设备</td><td>ipq40xx/generic</td><td><a href="https://openwrt.cc/releases/targets/ipq40xx/generic/">🔗</a></td></tr><tr><td>rockchip 设备</td><td>rockchip/armv8</td><td><a href="https://openwrt.cc/releases/targets/rockchip/armv8/">🔗</a></td></tr><tr><td>x86_64 设备</td><td>x86/64</td><td><a href="https://openwrt.cc/releases/targets/x86/64/">🔗</a></td></tr></tbody></table></div><h4 id="固件选择"><a href="#固件选择" class="headerlink" title="固件选择"></a>固件选择</h4><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/openwrt.webp" alt="openwrt固件"></p><p>我们会发现很多固件不知道选择哪个一个，我们只需要关注ext4和squashfs这两个版本就够了。ext4代表可以使用swap进行空间交换，</p><p>适合一些内存比较的设备，可以扩容一些存储空间给内存提升性能，但是没有重置系统的功能。squashfs代表可以进行系统重置也就是恢复出厂设置。</p><p>然后有个factory.img和sysupgrade。factory.img就是我们需要刷写的系统，sysupgrade是当内核更新时系统升级的版本。</p><h4 id="设置旁路有"><a href="#设置旁路有" class="headerlink" title="设置旁路有"></a>设置旁路有</h4><p>我们把树莓派插到路由器上</p><p>主路由器(192.168.3.1)—-&gt;树莓派网关(192.168.3.2)</p><p>设置openwrt 网络-接口-基本设置<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/openwrt2.webp" alt="openwrt设置"></p><p>设置传输协议为静态地址，ip地址要和主路由在一个网段内，子网掩码设置默认，ipv4网关设置为主路由的ipv4地址，和ipv6相关设置全部关闭。</p><p><strong>这里有个坑需要注意</strong>,我们设置好这些最好填写一些自定义DNS服务器比如：114.114.114.114。这是为了防止出现在路由器终端内ping的通公网ip但是</p><p>ping不通域名的尴尬情况。</p><p>在高级设置内打开动态DHCP和强制、开机自运行、强制链路，物理设置内关闭桥接、接口选择eth0就行了。最后就保存并运用<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/openwrt3.webp" alt="openwrt设置"></p><p>最后需要设置防火墙，在自定义规则内填入</p><pre><code class="hljs shell">iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE</code></pre><p>重启防火墙即可。</p><p>最后重启路由器和旁路由，我们检查自己的设备是否网关都是旁路由地址就可以了。</p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>旁路由模式其实是整个网络环境下有两个网关，主路由是第一个网关，旁路由是第二个网关所有网络流量会先到主路由再到旁路由。</p><p>这也就会带来一个问题，如果你搞到了公网ipv4的地址想做端口转发应该怎么做？其实有个原则，在哪个设备上进行拨号，DHCP我们就</p><p>在哪个设备上进行端口转发。</p><p>但是旁路由模式相对比较特殊，在设置DDNS时我们可以在openwrt上进行设置，因为主路由对与DDNS提供商可能会由限制。荣耀、华为就只能</p><p>使用oary(贝瑞)的域名。所以我们使用openwrt的ddns插件，选择自己现有的域名进行设置，这个时候因为我们不是网口拨号，只能使用url查询。</p><p>我建议使用<a href="https://ip.3322.net/">https://ip.3322.net/</a>这个网站。</p><p>然后设置好动态DNS后，设置端口转发时就会发现我们无论怎么设置都不能通过网址访问除了openwrt以外的所有设备，这可能就是我最开始说的</p><p>旁路由相对独特的网络的特点。我们就需要先在主路由上设置端口转发给openwrt然后我们再通过openwrt上的端口转发再进行转发到相应的设备端口上。</p><p>有人可能会有疑问我们需不需要设置openwrt防火墙里面的转发为接受，我测试的结果是不需要，无论是拒绝和接受都不影响。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/openwrt4.webp" alt="主路由设置"></p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/openwrt5.webp" alt="旁路由设置"></p><p>对了，可能你为了省事打开了DMZ服务，那你可能会面临一个问题，你可能会无法从域名访问你的设备，具体为什么我也不太清楚。</p><h2 id="最最后"><a href="#最最后" class="headerlink" title="最最后"></a>最最后</h2><p>可能会有人担心树莓派的行能问题，目前来说我通过梯子上网，开启的有广告过滤，smartDNS，DDNS，基本上每天的cpu性能使用率不到2%。家里的设备大概是</p><p>8台300M的网速基本无压力，可以大胆放心使用。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;浅谈如何设置旁路由器&lt;/p&gt;</summary>
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="学习" scheme="https://www.wulnut.top/tags/%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="openwrt" scheme="https://www.wulnut.top/tags/openwrt/"/>
    
  </entry>
  
  <entry>
    <title>Debian编译CMake遇到的坑</title>
    <link href="https://www.wulnut.top/2022/01/10/Debian%E8%A3%85CMake%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91/"/>
    <id>https://www.wulnut.top/2022/01/10/Debian%E8%A3%85CMake%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91/</id>
    <published>2022-01-10T13:43:36.711Z</published>
    <updated>2023-02-06T15:32:06.355Z</updated>
    
    <content type="html"><![CDATA[<p>踩坑记录<br><a id="more"></a></p><p>事情是这样的这两天我准备搭一个CLion的远程开发环境来给我的毕业设计做一下准备，在配置Toolchains的时候因为是一个远程环境。<br>需要确认远程主机上是否有相应的开发环境。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/%E8%BF%9C%E7%A8%8B%E9%85%8D%E7%BD%AE.webp" alt=""><br>大概就是上图那个样子，我的树莓派上没有CMake所以就有了后来的这些坑。</p><h1 id="第一个问题"><a href="#第一个问题" class="headerlink" title="第一个问题"></a>第一个问题</h1><p>&nbsp;&nbsp;当时忘记截图了，大概就是我通过<code>bootstrap</code>引导<code>cmake</code>安装的时候报了一个它找不到openssl和openssl所对应的库。</p><p>我当时第一反应以为是我的机器上没有装openssl然后我就尝试用了一下<code>openssl version</code>的命令看看能不能有结果。</p><p>发现嘿！我机器上有这玩意，然后我以为是我的版本太低了，网上有些帖子也有这种观点。我就尝试去更行一下openssl。</p><p>但是操作下来发现问题其实不是这个openssl版本的问题，因为报错信息里面说的是它找不到库函数。</p><p>我更行了之后通过<code>find</code>命令也找不到那个东西安装哪里去了。这个坑现在我也没有解决emm，后面再研究一下。</p><h1 id="第二个问题"><a href="#第二个问题" class="headerlink" title="第二个问题"></a>第二个问题</h1><p>&nbsp;&nbsp;当然CMake的报错还给了我另一条路可以选，就是编译的时候忽略<code>openssl</code>，提示是-DCMAKE_USE_OPENSSL=OFF，我试了一下是不是在使用bootstrap命令的时候后面加一句，发现这样没什么卵用，又因为是CMake的编译是由CMakeList.txt控制的和makefile差不多，想是不是改CMakeList.txt来实现。</p><p>就在CMakeList.txt的头部加上set(CMAKE_USE_OPENSSL OFF)然后再试一下bootstrap命令，唉!就成了然后就是一些基本流程。</p><p>还有个很重要的问题，我的机器是树莓派编译CMake的时间长大概编译了快两个小时，所以性能差的设备就被干等了。乘早挂在那里睡觉吧。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/CMake%E7%BC%96%E8%AF%91.webp" alt=""></p><p>不要学我在那傻等。</p><h1 id="第三个问题"><a href="#第三个问题" class="headerlink" title="第三个问题"></a>第三个问题</h1><p>&nbsp;&nbsp;当我把CMake配置好之后，我准备把CLion的最后一部分配置完成一切都正常，但是还是有个问题就是CLion找不到gmake。</p><p>gamke其实就是make，因为在别的平台上面make这个名词一直被占用就叫做gmake了这个相对来说好解决，只需要建立一个链接就好了。</p><pre><code class="hljs shell">cd /usr/binln -s make gamke</code></pre><h1 id="第四个问题"><a href="#第四个问题" class="headerlink" title="第四个问题"></a>第四个问题</h1><p>&nbsp;&nbsp;vim好久没用了，vim的搜索关键字的还是很重要的有两种方法</p><ul><li>方法1: /content 默认从上往下查找<ul><li>只读模式下输入 /content 后回车</li><li>按 n 向下查找</li><li>按 N 向上查找</li></ul></li><li>方法2：?content 默认从下往上查找<ul><li>只读模式下输入 ?content 后回车</li><li>按 n 向上查找</li><li>按 N 向下查找</li></ul></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;踩坑记录&lt;br&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://www.wulnut.top/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://www.wulnut.top/tags/Linux/"/>
    
    <category term="Debian" scheme="https://www.wulnut.top/tags/Debian/"/>
    
  </entry>
  
  <entry>
    <title>2021</title>
    <link href="https://www.wulnut.top/2021/12/31/2021%E6%80%BB%E7%BB%93/"/>
    <id>https://www.wulnut.top/2021/12/31/2021%E6%80%BB%E7%BB%93/</id>
    <published>2021-12-31T14:50:45.241Z</published>
    <updated>2023-02-06T15:08:21.665Z</updated>
    
    <content type="html"><![CDATA[<p>2021总结<br><a id="more"></a></p><h1 id="序-简述一下这一年"><a href="#序-简述一下这一年" class="headerlink" title="序-简述一下这一年"></a>序-简述一下这一年</h1><p>&nbsp;&nbsp;2021年是关键一年，因为我选择了考研。正因我从年初选择了考研，这一年我一开始想想必是单一而枯燥的一年，但是到年终我才发现我只猜中了开始但是没有猜中结尾。</p><p>我记得我在刚去自习室的时候发现，自习室厕所窗户前有课水杉，我当时就想起来高中毕业的时候有个老兄发了四张教室窗户前水杉四个季节的照片，印象特别深刻也正应为这个我也决定用这种方法来记录一下生活，</p><p>我也用这种方法记录了这9个月来的生活。我本以为学习见坚持的过程是很痛苦的，但是我有一群志同道合的朋友这使我减轻了不少的压力。但是我在学习中好方法的保持上还是没能做到一如既往，这确实我的一个遗憾。</p><p>正因为这群朋友让我对每个月的月底感到非常的期待，因为我们可以一起出去吃烧烤也是我感到最为放松的时候。</p><p>&nbsp;&nbsp;这一年下来最痛苦的时期也是最让我成长的时期我觉得应该就是考前的三个月的那段时间，因为那段时间查漏补缺问题层次不穷的冒出来，也导致了自己状态的起伏，心态的变化也在那个时期最为频繁。</p><p>“焦虑”算是那段时间的关键词吧，睡不着见，吃不进饭，学习效率底下，基本上都是焦虑的基本病状。这也要我逐渐失去了把题目做对的信心。</p><h1 id="有的放矢"><a href="#有的放矢" class="headerlink" title="有的放矢"></a>有的放矢</h1><p>&nbsp;&nbsp;有的放矢这四个字应该是我这一路下来领悟最深的成语。因为在最后几个月里面我真真真切切的明白了人的精力是有限的。也正因为感觉自己可以抓住整个面，但是到最后每个点都很平庸。在适当的时候放弃，</p><p>并不会影响整个事情的发展，反而会有好的结果。但是对我来说要是能做到这点只有我想明白放过自己才能做到，不然我就会感到不甘心，觉得自己还是能全都要，但是实际上这对我来说是种非常愚蠢的想法。</p><h1 id="蚌埠住了"><a href="#蚌埠住了" class="headerlink" title="蚌埠住了"></a>蚌埠住了</h1><p>&nbsp;&nbsp;最后一个月的背书应该是我以后再也忘不掉的记忆了，真的是太痛苦了。每天晚睡早起，躲到人少的隔间里背书，我本以为我早点开始记忆可以缓解冲刺阶段的压力，但是实际并没有什么作用，每天需要背诵的东西基本上</p><p>不可能完成自己的任务，而我确没有意识到这个问题，这也使我在冲刺的时候埋下了隐患，直接导致了直到考试前一天知识我还没背完。再加上不让别的学科状态下滑，就得额外抽出时间来弥补这些可能出现的问题。</p><p>不过这也是个普遍的现象，坚持不下去的也在最后这段时间摆烂了。真的太不容易了，如果要我再来一次我感觉我真的坚持不下去。</p><h1 id="谈谈未来"><a href="#谈谈未来" class="headerlink" title="谈谈未来"></a>谈谈未来</h1><p>&nbsp;&nbsp;现在来说在等成绩这段时间还是需要好好准备一下，复试的科目。万一我要是我进复试了，也不至于应付不过来。</p><p>其二就是通过考试我发现我对长期目标的规划和完成能力缺陷还是很大很多事情缺乏长期规划,由于没有长期的规划从而无法完成一些需要长期完成的事情。虽然有能力对短期目标有明确规划，但是对宏观总体无法做出有效的把握。<br>因此需要尝试对长期目标做出尝试性规划来提升相关能力。<br>其三是毕业设计的问题，Linux的相关内容还是不熟悉，再就是网络编程。这两块对我来说可能都比较生疏了还是需要快速的把内容过一篇。毕竟在家里面整个人都是懒散的…..根本没什么效率可说。还是得想办法改变一下才行。<br>其四就是要长期练习英语，单词外刊，英文书籍。感觉英语这玩意就是得多用多练，不然就没办法提高。什么语法，什么长难句，可能就是为了考试能更快理解吧。但是练的多成习惯了就不会在意这些了。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;2021总结&lt;br&gt;</summary>
    
    
    
    <category term="day" scheme="https://www.wulnut.top/categories/day/"/>
    
    
    <category term="回忆" scheme="https://www.wulnut.top/tags/%E5%9B%9E%E5%BF%86/"/>
    
    <category term="杂谈" scheme="https://www.wulnut.top/tags/%E6%9D%82%E8%B0%88/"/>
    
  </entry>
  
  <entry>
    <title>判断互质数的五种方法</title>
    <link href="https://www.wulnut.top/2021/02/19/%E5%88%A4%E6%96%AD%E4%BA%92%E8%B4%A8%E6%95%B0%E7%9A%84%E4%BA%94%E7%A7%8D%E6%96%B9%E6%B3%95/"/>
    <id>https://www.wulnut.top/2021/02/19/%E5%88%A4%E6%96%AD%E4%BA%92%E8%B4%A8%E6%95%B0%E7%9A%84%E4%BA%94%E7%A7%8D%E6%96%B9%E6%B3%95/</id>
    <published>2021-02-19T07:52:34.573Z</published>
    <updated>2023-02-06T15:32:57.529Z</updated>
    
    <content type="html"><![CDATA[<p>一. 概念判断法</p><p>公约数只有1的两个数叫做互质数。根据互质数的概念可以对一组数是否互质进行判断。如：9和11的公约数只有1，则它们是互质数。</p><p>二. 规律判断法</p><p>根据互质数的定义，可总结出一些规律，利用这些规律能迅速判断一组数是否互质。</p><p>（1）两个不相同的质数一定是互质数。如：7和11、17和31是互质数。</p><p>（2）两个连续的自然数一定是互质数。如：4和5、13和14是互质数。</p><p>（3）相邻的两个奇数一定是互质数。如：5和7、75和77是互质数。</p><p>（4）1和其他所有的自然数一定是互质数。如：1和4、1和13是互质数。</p><p>（5）两个数中的较大一个是质数，这两个数一定是互质数。如：3和19、16和97是互质数。</p><p>（6）两个数中的较小一个是质数，而较大数是合数且不是较小数的倍数，这两个数一定是互质数。如：2和15、7和54是互质数。</p><p>（7）较大数比较小数的2倍多1或少1，这两个数一定是互质数。如：13和27、13和25是互质数。</p><p>三. 分解判断法</p><p>如果两个数都是合数，可先将两个数分别分解质因数，再看两个数是否含有相同的质因数。如果没有，这两个数是互质数。如：130和231，先将它们分解质因数：130＝2×5×13，231＝3×7×11。分解后，发现它们没有相同的质因数，则130和231是互质数。</p><p>四. 求差判断法</p><p>如果两个数相差不大，可先求出它们的差，再看差与其中较小数是否互质。如果互质，则原来两个数一定是互质数。如：194和201，先求出它们的差，201－194＝7，因7和194互质，则194和201是互质数。</p><p>五. 求商判断法</p><p>用大数除以小数，如果除得的余数与其中较小数互质，则原来两个数是互质数。如：317和52，317÷52＝6……5，因余数5与52互质，则317和52是互质数。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;一. 概念判断法&lt;/p&gt;
&lt;p&gt;公约数只有1的两个数叫做互质数。根据互质数的概念可以对一组数是否互质进行判断。如：9和11的公约数只有1，则它们是互质数。&lt;/p&gt;
&lt;p&gt;二. 规律判断法&lt;/p&gt;
&lt;p&gt;根据互质数的定义，可总结出一些规律，利用这些规律能迅速判断一组数是否互质</summary>
      
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="数学" scheme="https://www.wulnut.top/tags/%E6%95%B0%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>数学学习的思考</title>
    <link href="https://www.wulnut.top/2021/02/18/%E6%95%B0%E5%AD%A6%E5%AD%A6%E4%B9%A0/"/>
    <id>https://www.wulnut.top/2021/02/18/%E6%95%B0%E5%AD%A6%E5%AD%A6%E4%B9%A0/</id>
    <published>2021-02-18T03:35:43.451Z</published>
    <updated>2023-02-07T03:14:42.654Z</updated>
    
    <content type="html"><![CDATA[<p>值得思考的数学，看到一篇很不错的数学分享</p><a id="more"></a><p>学好数学，要先建立知识网络，再训练解题路径搜索能力</p><p>1、在理解的基础上牢记知识点，并记住这些知识点的来龙去脉，形成一个知识网络是高效学习的第一步。</p><p>2、解题就是从你的知识网络中独立自主的搜索出一条有效的解题路径。</p><p>3、不独立钻研难题，只听老师讲题，轻易看答案，相当于走哪儿都带着导航走。 丢了导航，立马抓瞎！</p><p>在我教过的学生中，我见过两类自称数学学的不好的。</p><p>一类学生，是以下这类情况：比如我问他，什么是对数，他说知道，我们课上讲过对数。但是我问他对数运算规律，他知道几个，但不全知道。 问他换底公式，他说，书上写着呢。但我让他写出来，又写的磕磕巴巴。 我继续问，换底公式还有几个变形，知道吗？ 答曰，老师上课讲过。 我让他写出来，不会。 我给他写出来，他做恍然大悟状。</p><p>这样的学生，我要求他系统的复述知识点都常常是半生不熟， 要让他把这些知识点用在具体的题目上，谈何容易。这种基本可以判定是基础问题，知识点太生了，不要说形成自己的知识网络了，知识点都是模糊的，点与点的连线就更模糊脆弱了。 所以当务之急是理解并记忆知识点。 为了记住知识点，刷最基础的简单题。</p><p>还有一类学生，知识点公式背的挺熟练，也刷过很多题。对于已经做过几遍的题型，基本能做出来。 但如果往细节处问，经不起问。 问着问着，就变成我看例题就是这么做的。这样的学生还有一个特点，比如你问他对数的换底公式，他记住了好几个，但你问这几个之间什么关系，怎么来的？ 他们有时候不知道。这类学生知识点已经记忆的很好了，但是知识点之间的连线是模糊的，所以也没有形成自己的知识网络。</p><p>这样的学生如果面对一道没见过的题型，常常一看没见过，试了三五分钟就放弃了。这样的学生，是勤动手，懒动脑的。 他们刷过很多题， 但常常是听过老师讲过这类题型之后，勤动手刷同类型题，对解题过程形成肌肉记忆。 这样的学生，考试还是能保证基本分数的。 可越学越吃力，因为越往上学，需要记忆的题型越来越多了。</p><p>所以在理解的基础上牢记知识点，并记住这些知识点的来龙去脉，形成一个知识网络是高效学习的第一步。有了牢固的知识网络。还要有下面这个能力：一道题拿到你面前， 你要从你的知识网络中搜索出能够用于解决这道题目的，用到这个题目上去。</p><p>我觉得这个能力是最难的，也是所谓的“思维能力”的体现。 这个能力是受益终身的能力，掌握了这个能力，即使学过的知识忘的差不多了，需要的时候也能快速重建知识网络、寻找解决问题的途径。</p><p>有些学生常常会抱怨说，数学太难。说，总是老师一讲就会，答案一看就懂，可是就是自己想不出来。 听老师讲，看答案， 这个独立搜索的过程，你是没有的。 长期欠缺这个能力，自己做题的时候， 就不知道怎么搜索。</p><p>解决的办法是，遇到新的题目、难的题目，要沉得住气去理解、分析，尝试求解。比较难的题目， 其解题路径常常由好几个知识点串起来的。 拿到一个难题， 最好还是尽可能多的去独立思考怎么做这个题目。 习惯了这样的主动搜索， 你离学好理科就不远了。看答案，或等着老师讲解，你把这其中最艰难，最有价值，最能体现智力的主动搜索能力给省了。</p><p>而思考了很久，实在不会做，再去看答案，就好像你去一个地方， 到处找路， 到处碰壁，突然有个人告诉你， 前面红色的门那里左拐。 这时候，这个信息就在你的记忆中非常的深刻。</p><p>看完了答案（或听完课）， 过一两天，必须要独自做一遍。 这就是你把之前你不能主动搜索知识点的过程补上。 就相当于， 有人带着你走了一遍路之后， 你要检查一下你到底记住路了没有， 还必须过两天再自己独立走一遍。简单的路， 拐一两个弯， 甚至是直路，一般独立走一遍你就记住了。 复杂的路， 左拐， 几个路口后右拐， 然后几个路口以后进了小区， 又七拐八拐，才能到。 你让别人带着走一遍， 就能完全记住的， 说明你的方向感（数学直觉） 很好。 不好怎么办？ 笨办法， 多练！</p><p>所以， 不独立钻研难题，只听老师讲题，轻易看答案，相当于走哪儿都带着导航走。 丢了导航，立马抓瞎！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;值得思考的数学，看到一篇很不错的数学分享&lt;/p&gt;</summary>
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="数学" scheme="https://www.wulnut.top/tags/%E6%95%B0%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>2020年</title>
    <link href="https://www.wulnut.top/2020/12/27/2020%E6%80%BB%E7%BB%93/"/>
    <id>https://www.wulnut.top/2020/12/27/2020%E6%80%BB%E7%BB%93/</id>
    <published>2020-12-27T01:10:40.840Z</published>
    <updated>2023-02-06T15:08:15.319Z</updated>
    
    <content type="html"><![CDATA[<p>2020年的总结<br><a id="more"></a></p><h1 id="序"><a href="#序" class="headerlink" title="序"></a>序</h1><p>&emsp;&emsp;时间总是过的太快，天天和我玩着逗你玩的游戏。一晃一年又过去了，回想这一年也是十分魔幻了。从年初的疫情在家学习，等着杳无音讯的开学时间，到直接上半年在家里修完了大二下学期的整个课程。再到暑假去做实习体验，去南京穷游。再到下半年，直接回到学校开始了大三的学校，然后就是现在这个时刻。<br>&emsp;&emsp;没想到2020年过的格外之快，快到我能记清楚每个时间节点我都在做什么。快到我根本来不及好好感受生活，感知这个神奇的年份，我现在还依稀记得，在2019年年末我回到家，从阳台可以看到前面那块块荒地，从荒芜生息，到春天生机盎然。再到夏天茂盛高长，漫过我的身高。窗前的景色的变化不仅丈量这时间的长度，也要我渐渐感受到了时间的流逝。在家头一次呆那么久，我也头一次感受到我想去外面走走的意愿有多么的强烈。我的妈妈也义无反顾的答应我要我出去逛逛，这便有那次神奇的南京之旅。如果要我总结这一年我愿意把这一年分为三部分“上中下”。</p><h1 id="上"><a href="#上" class="headerlink" title="上"></a>上</h1><p>&emsp;&emsp;上半年的魔幻程度我觉得我有必要再说一说，疫情在家由于爸爸的工作原因…，但是我觉得是我爸的正义感。他擅做主张的上了前线，当时一连一两个月不在家，我正真感受到了我爸的那种众人为己任，大任为我担当，我将义不容辞的感觉。虽然我和时常想起这件事我都会打趣的说这是我爸有远见，想到抗疫成功之后能捞到好处hhh，不过正常人谁会把自己的生命和担当至于整个家庭之外冒这个险呢。也正是这样我和我妈两个人在家里相依为命的呆了两个月，我也深入了解到了我家这几年的得与失，逐渐感受到了他们的真实想法。疫情虽然在家度过了看似毫无意义的半年，因为我觉得在家上课就和玩差不多，好事靠自己自学了。但是我真正感受到了父母在一起的难道，可能这次疫情之后我很难再有和爸妈一起生活这么就的日子了。<br>&emsp;&emsp;不过还好，这个疫情还算有始有终，天气渐渐转暖。可能是当时在家太过于颓废把，天天除了睡觉就是吃饭了hhh，到了5月份把我重新做起了计划，也正是这样吧。把难以丈量的时间分为了，一段又一段的时区，我也渐渐养成的习惯，每天的生活也开始充实起来，每天起床背单词，每天看多少书，每天学多少东西，在什么时间学完，短期的有长期的也有。当实现短期目的开始无法满足我的时候我也渐渐可以完成长期目标。也是因为这些， 我每天晚上跑步，锻炼身体，也开始成为了习惯，每天坚持的3公里。也使我渐渐找到了跑步放松的乐趣和自虐坚持的爽快感Hhh有点抖M内味道了嗷。</p><h1 id="中"><a href="#中" class="headerlink" title="中"></a>中</h1><p>&emsp;&emsp;因为暑假的临近，社会复工复产的也主键恢复到了往日的生机。我也知道我不喜欢在家呆着，希望能出去玩玩或者说换种生活方式。就去参加了实习计划嘛，然后我就去了规划院哈哈哈，那里确实是一个学习的好去处里面大哥哥姐姐都是大吼人呀！！各处都很照顾我，也就没事帮他们看看电脑里面的问题,虽然我说我是学计算机，但是win的疑难杂症我也的解决方法也只能是去网上搜一下利索我能及的帮他们解决一下问题。院里面的徐工(徐国什么什么哈哈其实我还是记得名字的)，王竹燕姐姐，王院长，宋院长，张爽哥等等，大家都狠照顾我。当时在院里面的两个事我还是很清楚的虽然都是和吃饭有关哈哈哈，第一次是王院长要我和徐工一起出外勤，去某乡镇看他们测绘并了解一下现场情况，我当时嫌热就穿了一套清凉装，不过在上班之前我妈和我说过要我尽量穿短袖长裤上班，一个是职场礼节把，另一个就是防止在出外勤的时候被蚊虫叮咬。不过我也没想到我回出外勤，那次也是被咬的挺惨的然后就是中午的饭菜确实挺不错，就是酒桌上，说的话不太事宜哈哈，跟着一个哥哥喊50多岁的叔叔“哥”，有点尴尬。还有次就是王竹燕姐姐带我出去吃饭(主要是那个姐姐张的太好看了hhh)那次也比较好玩，一起去吃饭的业主姐姐的弟弟刚好是我同学，不然气氛挺尴尬的。也是疫情之后第一次去吃火锅，饭后去又去了一清吧(第一次去嗷！！)，我一个人干了四瓶鸡尾酒把，也好像是啤酒不过我感觉我还挺能喝的走路不飘一切正常，回家幸亏我爹不在家，我身上也没啥酒味脸也不红，肯能我妈没看出来把，就赶快去洗澡了哈哈哈有点刺激。<br>&emsp;&emsp;实习过后我就萌生了想出去逛逛的想法，也不知道为什么一开始第一反应就是去成都，或者重庆。不过因为别的原因嘛，就没敲定选择去了南京。也不知道为什么我一直又想去南京发展的想法，无论是学习还是工作生活。可惜现在学习肯定是去不了了，只能渴求以后了。不过这次去南京给我的感觉就是路很窄，最求梦想的人很多。地铁修很有旅游特点。第一感觉并没有我想象的那样，不过我很喜欢满街梧桐树，把道路的天空遮蔽起来的感觉。有点都市小城的感觉，不过我玩是真的没有玩舒服，因为我想去的想吃的都被一些省钱的建议打乱了。我也不知道说什么好，我还是想再去一次南京，好好感受一下古城的魅力，因为我认为南京的魅力不在于此。不过还是像吐槽一下老门东那边是真的无聊，硬是就是无聊的小吃街。</p><h1 id="下"><a href="#下" class="headerlink" title="下"></a>下</h1><p>&emsp;&emsp;上半年的过的太安逸，但是时间长了我也难免会想起我和我室友一起再周么开黑游戏到深夜的快乐，一起出去玩相互吹牛逼的欢乐场景。可能是几个男生一起生活久了吧，难免会有点想念吧，毕竟这也算是我的青春吧。回到学校的愿望也逐渐开始强烈起来，可能是在家呆时间长了吧。毕竟在学校自在一些，可以随意的干一些自己愿意的事情。哦对了其实我在暑假还有一件事，对我今年还蛮重要的，就是我实习的时候上下班都是开车上下班，这也要我这个每次开车都要我爸在旁边看着的小司机有了可以单干的机会。然后又是在南京租车带的我那俩小兄弟跑了个南京城。要我更有自信一个人开车了。现在想想还蛮刺激的，自己开车去学校，开车去接同学又在外面玩了两天，两天一直在开车Hhhh也是绝了。感觉当时是玩开了，真正的疫情之后的放风吧哈哈哈。不过今年开车也有翻车的时候，就是有次我去接我民族大学的发小去汉口吃饭，有个路口也不知道为啥，我可能刹车没踩死吧就撞轻轻的碰了一下前车，不过还好没什么问题，没有坑之类的，那个东北的女司机可能看我是小孩就放了我一马，但是我要交代在这了哈哈哈。<br>&emsp;&emsp;下半年的在校学习因为有几个时间节点显得还蛮有秩序，但是因为疫情的原因，学校压缩了课时，取消了一些活动，课程安排也十分紧凑。不过我还是在一开始几个月抽了点时间去搞了一下实习hhh，还挺有意思的。起码补贴了我的日常生活，要我后面的过的有点潇洒。不过还好幸亏我离职的早，后面课程紧张的时候我相对我室友有更多时间去学习和折腾还是意见蛮好的事情，有舍有得嘛。到期末vsbf带着大家复习，我也把我室友拉了进来，帮着他一起给期末困难户解决问题。从中间我明白了一件事，就是要是一件事重复了多遍，在什么情况你都能运用自足。在详细一下，就是给室友讲解问题，围绕着一个一些知识点反复的给别人讲，再不熟练都能很熟练，然后就应该是自己练习与巩固了。而我感觉我这次复习只有前面没有后面，不过可能是时间原因把，复习时间确实不多，我的有些课程也算是速成的。希望以后不会这样了。然后就是最后的这个该死的项目，其实组员都挺不错的。我感觉我做的也挺不错的，起码每个人都才项目组里面做了自己该做的事情，基本达到了我的目的，就是锻炼他们的个人意识这块感觉不济我的期望。<br><!---这才是正篇，其实这样的我真的不想吐槽那个Acmer，我不清楚是为什么，是不上心么。为什么我每次的布置的任务我都说的非常明确了，但是还是那个样子，我真的搞不懂了，要是能做了就和我说清楚嘛，我就提前计划好，还好我有预料到，提前把事情搞完，不至于一直拖到最后赶集赶忙的，为了完成任务而完成任务，我当然知道我自己的能力，我能单干虽然可能不会非常优秀，但是带着大家一起拿个优秀还是很稳的--><br>&emsp;&emsp;就先说这么多吧hhh</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;2020年的总结&lt;br&gt;</summary>
    
    
    
    <category term="day" scheme="https://www.wulnut.top/categories/day/"/>
    
    
    <category term="回忆" scheme="https://www.wulnut.top/tags/%E5%9B%9E%E5%BF%86/"/>
    
    <category term="杂谈" scheme="https://www.wulnut.top/tags/%E6%9D%82%E8%B0%88/"/>
    
  </entry>
  
  <entry>
    <title>JDBC ResultSet结果通过java反射赋值给java对象</title>
    <link href="https://www.wulnut.top/2020/12/25/JDBC%E8%BF%94%E5%9B%9EResultSet/"/>
    <id>https://www.wulnut.top/2020/12/25/JDBC%E8%BF%94%E5%9B%9EResultSet/</id>
    <published>2020-12-25T03:00:45.983Z</published>
    <updated>2023-02-07T03:16:26.472Z</updated>
    
    <content type="html"><![CDATA[<p>使用反射</p><a id="more"></a><p>我们先来写一个不用框架，用JDBC来操作呢数据库，下面写一个简单的例子：</p><pre><code class="hljs java"><span class="hljs-comment">// 加载驱动程序</span>Class.forName(driver);conn = DriverManager.getConnection(url, user, password);Statement statement = conn.createStatement();String sql = <span class="hljs-string">&quot;select * from user&quot;</span>;ResultSet rs = statement.executeQuery(sql);String name  = <span class="hljs-keyword">null</span>;String phone = <span class="hljs-keyword">null</span>;<span class="hljs-keyword">while</span> (rs.next()) &#123;    <span class="hljs-comment">//获取stuname这列数据</span>    name  = rs.getString(<span class="hljs-string">&quot;user_name&quot;</span>);    phone = rs.getString(<span class="hljs-string">&quot;user_phone&quot;</span>);    System.out.println(name + <span class="hljs-string">&quot;\t&quot;</span> + phone);&#125;</code></pre><p>就是这么简单的一段代码，然后就是想从数据库中获取相应的字段，就用get和set方法来处理，返回一个对象，写起来就是麻烦，不通用</p><p>因为java有反射的功能，所以可以抽取出来，写个通用的方法。</p><pre><code class="hljs java"><span class="hljs-comment">/**</span><span class="hljs-comment"> * 把ResultSet的结果放到java对象中</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> &lt;T&gt;</span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> rs</span><span class="hljs-comment"> *            ResultSet</span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> obj</span><span class="hljs-comment"> *            java类的class</span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><span class="hljs-comment"> */</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;T&gt; <span class="hljs-function">ArrayList&lt;T&gt; <span class="hljs-title">putResult</span><span class="hljs-params">(ResultSet rs, Class&lt;T&gt; obj)</span> </span>&#123;<span class="hljs-keyword">try</span> &#123;ArrayList&lt;T&gt; arrayList = <span class="hljs-keyword">new</span> ArrayList&lt;T&gt;();ResultSetMetaData metaData = rs.getMetaData();<span class="hljs-comment">/**</span><span class="hljs-comment"> * 获取总列数</span><span class="hljs-comment"> */</span><span class="hljs-keyword">int</span> count = metaData.getColumnCount();<span class="hljs-keyword">while</span> (rs.next()) &#123;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 创建对象实例</span><span class="hljs-comment"> */</span>T newInstance = obj.newInstance();<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt;= count; i++) &#123;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 给对象的某个属性赋值</span><span class="hljs-comment"> */</span>String name = metaData.getColumnName(i).toLowerCase();name = toJavaField(name);<span class="hljs-comment">// 改变列名格式成java命名格式</span>String substring = name.substring(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);<span class="hljs-comment">// 首字母大写</span>String replace = name.replaceFirst(substring, substring.toUpperCase());Class&lt;?&gt; type = obj.getDeclaredField(name).getType();<span class="hljs-comment">// 获取字段类型</span>Method method = obj.getMethod(<span class="hljs-string">&quot;set&quot;</span> + replace, type);<span class="hljs-comment">/**</span><span class="hljs-comment"> * 判断读取数据的类型</span><span class="hljs-comment"> */</span><span class="hljs-keyword">if</span>(type.isAssignableFrom(String.class))&#123;method.invoke(newInstance, rs.getString(i));&#125;<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(type.isAssignableFrom(<span class="hljs-keyword">int</span>.class) || type.isAssignableFrom(Integer.class))&#123;method.invoke(newInstance, rs.getInt(i));&#125;<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(<span class="hljs-keyword">boolean</span>.class))&#123;method.invoke(newInstance, rs.getBoolean(i));&#125;<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(type.isAssignableFrom(Date.class))&#123;method.invoke(newInstance, rs.getDate(i));&#125;&#125;arrayList.add(newInstance);&#125;<span class="hljs-keyword">return</span> arrayList; &#125; <span class="hljs-keyword">catch</span> (InstantiationException | IllegalAccessException | SQLException | SecurityException| NoSuchMethodException | IllegalArgumentException | InvocationTargetException| NoSuchFieldException e) &#123;<span class="hljs-comment">// TODO Auto-generated catch block</span>e.printStackTrace();&#125; <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 数据库命名格式转java命名格式</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> str</span><span class="hljs-comment"> *            数据库字段名</span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> java字段名</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">toJavaField</span><span class="hljs-params">(String str)</span> </span>&#123;  String[] split = str.split(<span class="hljs-string">&quot;_&quot;</span>);StringBuilder builder = <span class="hljs-keyword">new</span> StringBuilder();builder.append(split[<span class="hljs-number">0</span>]);<span class="hljs-comment">// 拼接第一个字符</span>  <span class="hljs-comment">// 如果数组不止一个单词</span><span class="hljs-keyword">if</span> (split.length &gt; <span class="hljs-number">1</span>) &#123;<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt; split.length; i++) &#123;<span class="hljs-comment">// 去掉下划线，首字母变为大写</span>String string = split[i];String substring = string.substring(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);split[i] = string.replaceFirst(substring, substring.toUpperCase());builder.append(split[i]);&#125;&#125;  <span class="hljs-keyword">return</span> builder.toString();&#125;</code></pre><p>这里先简单处理这几种常用类型，其他的其实可以后续再加上。这里主要依赖反射obj.getDeclaredField(name).getType()获取字段类型，然后通过obj.getMethod(“set” + replace, type)来找到数据库java对象的这个set方法，最终method.invoke(newInstance, rs.getString(i))来附值，这么一想，如果查出来是一个很大的list的，那么该怎么优化呢？</p><p>先来使用一下，这个写的小工具，就可以不用繁琐的get和set方法：</p><pre><code class="hljs java">ArrayList&lt;userDO&gt; userDOS = ResultSetPropertiesHelper.transferResult(rs, userDO.class);<span class="hljs-keyword">for</span> (UserDD userDO : userDOS) &#123;    System.out.println(userDO.toString());&#125;</code></pre><p>上面是通过对象里面的set和get方法来设置变量的值，这样写起来感觉比较麻烦，直接用Field的set方法是不是更加简单一点，但是这样写，访问的是public对象，所以</p><pre><code class="hljs `java">Field field &#x3D; obj.getField(name);if (type.isAssignableFrom(String.class)) &#123;method.invoke(newInstance, rs.getString(i));&#125; else if (type.isAssignableFrom(byte.class) || type.isAssignableFrom(Byte.class)) &#123;method.invoke(newInstance, rs.getByte(i));&#x2F;&#x2F; byte 数据类型是8位、有符号的，以二进制补码表示的整数&#125; else if (type.isAssignableFrom(short.class) || type.isAssignableFrom(Short.class)) &#123;method.invoke(newInstance, rs.getShort(i));&#x2F;&#x2F; short 数据类型是 16 位、有符号的以二进制补码表示的整数&#125; else if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) &#123;method.invoke(newInstance, rs.getInt(i));&#x2F;&#x2F; int 数据类型是32位、有符号的以二进制补码表示的整数&#125; else if (type.isAssignableFrom(long.class) || type.isAssignableFrom(Long.class)) &#123;method.invoke(newInstance, rs.getLong(i));&#x2F;&#x2F; long 数据类型是 64 位、有符号的以二进制补码表示的整数&#125; else if (type.isAssignableFrom(float.class) || type.isAssignableFrom(Float.class)) &#123;method.invoke(newInstance, rs.getFloat(i));&#x2F;&#x2F; float 数据类型是单精度、32位、符合IEEE 754标准的浮点数&#125; else if (type.isAssignableFrom(double.class) || type.isAssignableFrom(Double.class)) &#123;method.invoke(newInstance, rs.getDouble(i));&#x2F;&#x2F; double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数&#125; else if (type.isAssignableFrom(BigDecimal.class)) &#123;method.invoke(newInstance, rs.getBigDecimal(i));&#125; else if (type.isAssignableFrom(boolean.class) || type.isAssignableFrom(Boolean.class)) &#123;method.invoke(newInstance, rs.getBoolean(i));&#x2F;&#x2F; boolean数据类型表示一位的信息&#125; else if (type.isAssignableFrom(Date.class)) &#123;method.invoke(newInstance, rs.getDate(i));&#125;</code></pre><p>这里补充一下ResultSetMetaData接口都有哪些信息</p><p>可以使用此对象获得列的数目和类型以及每一列的名称：</p><ul><li>getColumnCount(); 返回 ResultSet 中的列数。 </li><li>getColumnName(int); 返回列序号为 int 的列名。 </li><li>getColumnLabel(int); 返回此列暗含的标签。 </li><li>isCurrency(int); 如果此列包含带有货币单位的一个数字，则返回 true。 </li><li>isReadOnly(int); 如果此列为只读，则返回 true。 </li><li>isAutoIncrement(int); 如果此列自动递增，则返回 true。这类列通常为键，而且始终是只读的。 </li><li>getColumnType(int); 返回此列的 SQL 数据类型。这些数据类型包括 </li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;使用反射&lt;/p&gt;</summary>
    
    
    
    <category term="Java" scheme="https://www.wulnut.top/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>简述数据库连接池</title>
    <link href="https://www.wulnut.top/2020/12/23/%E8%BF%9E%E6%8E%A5%E6%B1%A0/"/>
    <id>https://www.wulnut.top/2020/12/23/%E8%BF%9E%E6%8E%A5%E6%B1%A0/</id>
    <published>2020-12-23T13:06:28.840Z</published>
    <updated>2023-02-07T03:17:56.449Z</updated>
    
    <content type="html"><![CDATA[<p>简介数据库连接池</p><a id="more"></a><h1 id="一般怎么链接数据库"><a href="#一般怎么链接数据库" class="headerlink" title="一般怎么链接数据库"></a>一般怎么链接数据库</h1><p>一般来说，Java应用程序访问数据库的过程是：</p><ol><li>装载数据库驱动程序</li><li>通过Jdbc链接数据库链接</li><li>访问数据库，执行sql语句</li><li>断开数据库连接</li></ol><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.sql.*;<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> </span>&#123;    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;        <span class="hljs-comment">// 声明Connection对象</span>        Connection connection;        <span class="hljs-comment">// 驱动程序名</span>        String driver = <span class="hljs-string">&quot;com.mysql.cj.jdbc.Driver&quot;</span>;        <span class="hljs-comment">// URL指向要访问的数据库名也就是那张表 student</span>        String url  = <span class="hljs-string">&quot;jdbc:mysql://localhost:3306/student?serverTimezone=UTC&quot;</span>;        <span class="hljs-comment">// MySQL配置时的用户名</span>        String user = <span class="hljs-string">&quot;root&quot;</span>;        <span class="hljs-comment">// MySQL配置时的密码</span>        String pwd  = <span class="hljs-string">&quot;233333333&quot;</span>;        <span class="hljs-comment">// 加载驱动</span>        <span class="hljs-comment">// 防止没有该驱动</span>        <span class="hljs-keyword">try</span> &#123;            <span class="hljs-comment">// 1.加载驱动</span>            Class.forName(driver);            System.out.println(<span class="hljs-string">&quot;驱动连接成功&quot;</span>);            <span class="hljs-comment">// 2.创建连接</span>            connection = DriverManager.getConnection(url, user, pwd);            <span class="hljs-keyword">if</span> (!connection.isClosed()) &#123;                System.out.println(<span class="hljs-string">&quot;\n\t\t成功以 &quot;</span> + user + <span class="hljs-string">&quot; 身份连接到数据库！！！&quot;</span>);            &#125;            <span class="hljs-comment">// 3.创建Statement</span>            Statement statement = connection.createStatement();            <span class="hljs-comment">// PreparedStatement 安全性更高</span><span class="hljs-comment">//            PreparedStatement st = connection.createStatement();</span>            <span class="hljs-comment">// 4.执行sql</span>            String sql_1 = <span class="hljs-string">&quot;insert into student.info values(&#x27;6&#x27;, &#x27;ash-bin&#x27;, &#x27;数学&#x27;, 40000)&quot;</span>;            <span class="hljs-keyword">int</span> n = statement.executeUpdate(sql_1);            System.out.println(n);            String sql_2 = <span class="hljs-string">&quot;select * from student.info&quot;</span>;            <span class="hljs-comment">// 5.ResultSet类，用来存放获取的结果集！！</span>            ResultSet rs = statement.executeQuery(sql_2);            System.out.println(<span class="hljs-string">&quot;\n\t\t执行结果如下所示:&quot;</span>);            System.out.println(<span class="hljs-string">&quot;\t\t-----------------------------------------------------&quot;</span>);            System.out.println(<span class="hljs-string">&quot;\t\t|\t&quot;</span> + <span class="hljs-string">&quot;ID&quot;</span> + <span class="hljs-string">&quot;\t&quot;</span> + <span class="hljs-string">&quot;姓名&quot;</span> + <span class="hljs-string">&quot;\t&quot;</span> + <span class="hljs-string">&quot;专业&quot;</span> + <span class="hljs-string">&quot;\t&quot;</span> + <span class="hljs-string">&quot;薪水\t|&quot;</span>);            System.out.println(<span class="hljs-string">&quot;\t\t-----------------------------------------------------&quot;</span>);            <span class="hljs-keyword">int</span> ID  = <span class="hljs-number">0</span>;            String name      = <span class="hljs-keyword">null</span>;            String salary    = <span class="hljs-keyword">null</span>;            String dept_name = <span class="hljs-keyword">null</span>;            <span class="hljs-comment">// 查询</span>            <span class="hljs-keyword">while</span> (rs.next()) &#123;            <span class="hljs-comment">// 获取 ID 这列数据</span>            ID = rs.getInt(<span class="hljs-string">&quot;ID&quot;</span>);            <span class="hljs-comment">// 获取 Name 这列数据</span>            name = rs.getString(<span class="hljs-string">&quot;Name&quot;</span>);            <span class="hljs-comment">// 获取 dept_name 这列数据</span>            dept_name = rs.getString(<span class="hljs-string">&quot;dept_name&quot;</span>);            <span class="hljs-comment">// 获取 salary 这列数据</span>            salary = rs.getString(<span class="hljs-string">&quot;salary&quot;</span>);            <span class="hljs-comment">// 输出结果</span>            System.out.println(<span class="hljs-string">&quot;\t\t|\t&quot;</span> + ID + <span class="hljs-string">&quot;\t&quot;</span> + name + <span class="hljs-string">&quot;\t&quot;</span> + dept_name + <span class="hljs-string">&quot;\t&quot;</span> + salary + <span class="hljs-string">&quot;\t|\t\t&quot;</span>);            &#125;        &#125; <span class="hljs-keyword">catch</span> (ClassNotFoundException e) &#123; <span class="hljs-comment">// 这个异常是驱动异常</span>            e.printStackTrace();            System.out.println(<span class="hljs-string">&quot;没有该驱动&quot;</span>);        &#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123; <span class="hljs-comment">// 这个异常是getConnection的异常</span>            e.printStackTrace();            System.out.println(<span class="hljs-string">&quot;创建数据库连接失败&quot;</span>);        &#125; <span class="hljs-keyword">finally</span> &#123;            System.out.println(<span class="hljs-string">&quot;\t\t\t\t获取数据库数据完毕！&quot;</span>);        &#125;    &#125;&#125;</code></pre><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/%E8%BF%9E%E6%8E%A5%E6%B1%A0.webp" alt="连接池"></p><p>程序开发过程中，存在很多问题：</p><ol><li>首先，每一次web请求都要建立一次数据库连接。建立连接是一个费时的活动，每次都得花费0.05s～1s的时间，而且系统还要分配内存资源。这个时间对于一次或几次数据库操作，或许感觉不出系统有多大的开销。</li><li><p>可是对于现在的web应用，尤其是大型电子商务网站，同时有几百人甚至几千人在线是很正常的事。在这种情况下，频繁的进行数据库连接操作势必占用很多的系统资源，网站的响应速度必定下降，严重的甚至会造成服务器的崩溃。不是危言耸听，这就是制约某些电子商务网站发展的技术瓶颈问题。其次，对于每一次数据库连接，使用完后都得断开。否则，如果程序出现异常而未能关闭，将会导致数据库系统中的内存泄漏，最终将不得不重启数据库</p><p>通过上面的分析，我们可以看出来，“数据库连接”是一种稀缺的资源，为了保障网站的正常使用，应该对其进行妥善管理。其实我们查询完数据库后，如果不关闭连接，而是暂时存放起来，当别人使用时，把这个连接给他们使用。就避免了一次建立数据库连接和断开的操作时间消耗。</p><p>数据库连接池的基本思想：就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接，当需要建立数据库连接时，只需从“缓冲池”中取出一个，使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。</p><p>创建数据库连接池大概有3个步骤：</p><ol><li>创建ConnectionPool实例，并初始化创建10个连接，保存在Vector中（线程安全）</li><li>实现getConnection()从连接库中获取一个可用的连接</li><li>returnConnection(conn) 提供将连接放回连接池中方法</li></ol></li></ol><p> 数据库连接池类 ConnectionPool.java<br> <pre><code class="hljs java"><span class="hljs-keyword">import</span> java.sql.Connection;<span class="hljs-keyword">import</span> java.sql.DatabaseMetaData;<span class="hljs-keyword">import</span> java.sql.Driver;<span class="hljs-keyword">import</span> java.sql.DriverManager;<span class="hljs-keyword">import</span> java.sql.SQLException;<span class="hljs-keyword">import</span> java.sql.Statement;<span class="hljs-keyword">import</span> java.util.Enumeration;<span class="hljs-keyword">import</span> java.util.Vector; <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConnectionPool</span> </span>&#123; <span class="hljs-keyword">private</span> String jdbcDriver = <span class="hljs-string">&quot;&quot;</span>; <span class="hljs-comment">// 数据库驱动</span><span class="hljs-keyword">private</span> String dbUrl = <span class="hljs-string">&quot;&quot;</span>; <span class="hljs-comment">// 数据 URL</span><span class="hljs-keyword">private</span> String dbUsername = <span class="hljs-string">&quot;&quot;</span>; <span class="hljs-comment">// 数据库用户名</span><span class="hljs-keyword">private</span> String dbPassword = <span class="hljs-string">&quot;&quot;</span>; <span class="hljs-comment">// 数据库用户密码</span><span class="hljs-keyword">private</span> String testTable = <span class="hljs-string">&quot;&quot;</span>; <span class="hljs-comment">// 测试连接是否可用的测试表名，默认没有测试表</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> initialConnections = <span class="hljs-number">10</span>; <span class="hljs-comment">// 连接池的初始大小</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> incrementalConnections = <span class="hljs-number">5</span>;<span class="hljs-comment">// 连接池自动增加的大小</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> maxConnections = <span class="hljs-number">50</span>; <span class="hljs-comment">// 连接池最大的大小</span><span class="hljs-keyword">private</span> Vector connections = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// 存放连接池中数据库连接的向量 , 初始时为 null</span><span class="hljs-comment">// 它中存放的对象为 PooledConnection 型</span> <span class="hljs-comment">/**</span><span class="hljs-comment"> * 构造函数</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> jdbcDriver</span><span class="hljs-comment"> *            String JDBC 驱动类串</span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> dbUrl</span><span class="hljs-comment"> *            String 数据库 URL</span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> dbUsername</span><span class="hljs-comment"> *            String 连接数据库用户名</span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> dbPassword</span><span class="hljs-comment"> *            String 连接数据库用户的密码</span><span class="hljs-comment"> * </span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ConnectionPool</span><span class="hljs-params">(String jdbcDriver, String dbUrl, String dbUsername,</span></span><span class="hljs-function"><span class="hljs-params">String dbPassword)</span> </span>&#123;<span class="hljs-keyword">this</span>.jdbcDriver = jdbcDriver;<span class="hljs-keyword">this</span>.dbUrl = dbUrl;<span class="hljs-keyword">this</span>.dbUsername = dbUsername;<span class="hljs-keyword">this</span>.dbPassword = dbPassword;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 返回连接池的初始大小</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 初始连接池中可获得的连接数量</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getInitialConnections</span><span class="hljs-params">()</span> </span>&#123;<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.initialConnections;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 设置连接池的初始大小</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> 用于设置初始连接池中连接的数量</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setInitialConnections</span><span class="hljs-params">(<span class="hljs-keyword">int</span> initialConnections)</span> </span>&#123;<span class="hljs-keyword">this</span>.initialConnections = initialConnections;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 返回连接池自动增加的大小 、</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 连接池自动增加的大小</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getIncrementalConnections</span><span class="hljs-params">()</span> </span>&#123;<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.incrementalConnections;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 设置连接池自动增加的大小</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> 连接池自动增加的大小</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setIncrementalConnections</span><span class="hljs-params">(<span class="hljs-keyword">int</span> incrementalConnections)</span> </span>&#123;<span class="hljs-keyword">this</span>.incrementalConnections = incrementalConnections;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 返回连接池中最大的可用连接数量</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 连接池中最大的可用连接数量</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getMaxConnections</span><span class="hljs-params">()</span> </span>&#123;<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.maxConnections;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 设置连接池中最大可用的连接数量</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> 设置连接池中最大可用的连接数量值</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setMaxConnections</span><span class="hljs-params">(<span class="hljs-keyword">int</span> maxConnections)</span> </span>&#123;<span class="hljs-keyword">this</span>.maxConnections = maxConnections;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 获取测试数据库表的名字</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 测试数据库表的名字</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getTestTable</span><span class="hljs-params">()</span> </span>&#123;<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.testTable;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 设置测试表的名字</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> testTable</span><span class="hljs-comment"> *            String 测试表的名字</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setTestTable</span><span class="hljs-params">(String testTable)</span> </span>&#123;<span class="hljs-keyword">this</span>.testTable = testTable;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * 创建一个数据库连接池，连接池中的可用连接的数量采用类成员 initialConnections 中设置的值</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">createPool</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;<span class="hljs-comment">// 确保连接池没有创建</span><span class="hljs-comment">// 如果连接池己经创建了，保存连接的向量 connections 不会为空</span><span class="hljs-keyword">if</span> (connections != <span class="hljs-keyword">null</span>) &#123;<span class="hljs-keyword">return</span>; <span class="hljs-comment">// 如果己经创建，则返回</span>&#125;<span class="hljs-comment">// 实例化 JDBC Driver 中指定的驱动类实例</span>Driver driver = (Driver) (Class.forName(<span class="hljs-keyword">this</span>.jdbcDriver).newInstance());DriverManager.registerDriver(driver); <span class="hljs-comment">// 注册 JDBC 驱动程序</span><span class="hljs-comment">// 创建保存连接的向量 , 初始时有 0 个元素</span>connections = <span class="hljs-keyword">new</span> Vector();<span class="hljs-comment">// 根据 initialConnections 中设置的值，创建连接。</span>createConnections(<span class="hljs-keyword">this</span>.initialConnections);<span class="hljs-comment">// System.out.println(&quot; 数据库连接池创建成功！ &quot;);</span>&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 创建由 numConnections 指定数目的数据库连接 , 并把这些连接 放入 connections 向量中</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> numConnections</span><span class="hljs-comment"> *            要创建的数据库连接的数目</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">createConnections</span><span class="hljs-params">(<span class="hljs-keyword">int</span> numConnections)</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;<span class="hljs-comment">// 循环创建指定数目的数据库连接</span><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>; x &lt; numConnections; x++) &#123;<span class="hljs-comment">// 是否连接池中的数据库连接的数量己经达到最大？最大值由类成员 maxConnections</span><span class="hljs-comment">// 指出，如果 maxConnections 为 0 或负数，表示连接数量没有限制。</span><span class="hljs-comment">// 如果连接数己经达到最大，即退出。</span><span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.maxConnections &gt; <span class="hljs-number">0</span>&amp;&amp; <span class="hljs-keyword">this</span>.connections.size() &gt;= <span class="hljs-keyword">this</span>.maxConnections) &#123;<span class="hljs-keyword">break</span>;&#125;<span class="hljs-comment">// add a new PooledConnection object to connections vector</span><span class="hljs-comment">// 增加一个连接到连接池中（向量 connections 中）</span><span class="hljs-keyword">try</span> &#123;connections.addElement(<span class="hljs-keyword">new</span> PooledConnection(newConnection()));&#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;System.out.println(<span class="hljs-string">&quot; 创建数据库连接失败！ &quot;</span> + e.getMessage());<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> SQLException();&#125;<span class="hljs-comment">// System.out.println(&quot; 数据库连接己创建 ......&quot;);</span>&#125;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 创建一个新的数据库连接并返回它</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 返回一个新创建的数据库连接</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">private</span> Connection <span class="hljs-title">newConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;<span class="hljs-comment">// 创建一个数据库连接</span>Connection conn = DriverManager.getConnection(dbUrl, dbUsername,dbPassword);<span class="hljs-comment">// 如果这是第一次创建数据库连接，即检查数据库，获得此数据库允许支持的</span><span class="hljs-comment">// 最大客户连接数目</span><span class="hljs-comment">// connections.size()==0 表示目前没有连接己被创建</span><span class="hljs-keyword">if</span> (connections.size() == <span class="hljs-number">0</span>) &#123;DatabaseMetaData metaData = conn.getMetaData();<span class="hljs-keyword">int</span> driverMaxConnections = metaData.getMaxConnections();<span class="hljs-comment">// 数据库返回的 driverMaxConnections 若为 0 ，表示此数据库没有最大</span><span class="hljs-comment">// 连接限制，或数据库的最大连接限制不知道</span><span class="hljs-comment">// driverMaxConnections 为返回的一个整数，表示此数据库允许客户连接的数目</span><span class="hljs-comment">// 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大</span><span class="hljs-comment">// 连接数目为数据库允许的最大数目</span><span class="hljs-keyword">if</span> (driverMaxConnections &gt; <span class="hljs-number">0</span>&amp;&amp; <span class="hljs-keyword">this</span>.maxConnections &gt; driverMaxConnections) &#123;<span class="hljs-keyword">this</span>.maxConnections = driverMaxConnections;&#125;&#125;<span class="hljs-keyword">return</span> conn; <span class="hljs-comment">// 返回创建的新的数据库连接</span>&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 , 如果当前没有可用的数据库连接，并且更多的数据库连接不能创</span><span class="hljs-comment"> * 建（如连接池大小的限制），此函数等待一会再尝试获取。</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 返回一个可用的数据库连接对象</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> Connection <span class="hljs-title">getConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;<span class="hljs-comment">// 确保连接池己被创建</span><span class="hljs-keyword">if</span> (connections == <span class="hljs-keyword">null</span>) &#123;<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>; <span class="hljs-comment">// 连接池还没创建，则返回 null</span>&#125;Connection conn = getFreeConnection(); <span class="hljs-comment">// 获得一个可用的数据库连接</span><span class="hljs-comment">// 如果目前没有可以使用的连接，即所有的连接都在使用中</span><span class="hljs-keyword">while</span> (conn == <span class="hljs-keyword">null</span>) &#123;<span class="hljs-comment">// 等一会再试</span><span class="hljs-comment">// System.out.println(&quot;Wait&quot;);</span>wait(<span class="hljs-number">250</span>);conn = getFreeConnection(); <span class="hljs-comment">// 重新再试，直到获得可用的连接，如果</span><span class="hljs-comment">// getFreeConnection() 返回的为 null</span><span class="hljs-comment">// 则表明创建一批连接后也不可获得可用连接</span>&#125;<span class="hljs-keyword">return</span> conn;<span class="hljs-comment">// 返回获得的可用的连接</span>&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 本函数从连接池向量 connections 中返回一个可用的的数据库连接，如果 当前没有可用的数据库连接，本函数则根据</span><span class="hljs-comment"> * incrementalConnections 设置 的值创建几个数据库连接，并放入连接池中。 如果创建后，所有的连接仍都在使用中，则返回 null</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 返回一个可用的数据库连接</span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">private</span> Connection <span class="hljs-title">getFreeConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;<span class="hljs-comment">// 从连接池中获得一个可用的数据库连接</span>Connection conn = findFreeConnection();<span class="hljs-keyword">if</span> (conn == <span class="hljs-keyword">null</span>) &#123;<span class="hljs-comment">// 如果目前连接池中没有可用的连接</span><span class="hljs-comment">// 创建一些连接</span>createConnections(incrementalConnections);<span class="hljs-comment">// 重新从池中查找是否有可用连接</span>conn = findFreeConnection();<span class="hljs-keyword">if</span> (conn == <span class="hljs-keyword">null</span>) &#123;<span class="hljs-comment">// 如果创建连接后仍获得不到可用的连接，则返回 null</span><span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;&#125;&#125;<span class="hljs-keyword">return</span> conn;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 查找连接池中所有的连接，查找一个可用的数据库连接， 如果没有可用的连接，返回 null</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 返回一个可用的数据库连接</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">private</span> Connection <span class="hljs-title">findFreeConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;Connection conn = <span class="hljs-keyword">null</span>;PooledConnection pConn = <span class="hljs-keyword">null</span>;<span class="hljs-comment">// 获得连接池向量中所有的对象</span>Enumeration enumerate = connections.elements();<span class="hljs-comment">// 遍历所有的对象，看是否有可用的连接</span><span class="hljs-keyword">while</span> (enumerate.hasMoreElements()) &#123;pConn = (PooledConnection) enumerate.nextElement();<span class="hljs-keyword">if</span> (!pConn.isBusy()) &#123;<span class="hljs-comment">// 如果此对象不忙，则获得它的数据库连接并把它设为忙</span>conn = pConn.getConnection();pConn.setBusy(<span class="hljs-keyword">true</span>);<span class="hljs-comment">// 测试此连接是否可用</span><span class="hljs-keyword">if</span> (!testConnection(conn)) &#123;<span class="hljs-comment">// 如果此连接不可再用了，则创建一个新的连接，</span><span class="hljs-comment">// 并替换此不可用的连接对象，如果创建失败，返回 null</span><span class="hljs-keyword">try</span> &#123;conn = newConnection();&#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;System.out.println(<span class="hljs-string">&quot; 创建数据库连接失败！ &quot;</span> + e.getMessage());<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;&#125;pConn.setConnection(conn);&#125;<span class="hljs-keyword">break</span>; <span class="hljs-comment">// 己经找到一个可用的连接，退出</span>&#125;&#125;<span class="hljs-keyword">return</span> conn;<span class="hljs-comment">// 返回找到到的可用连接</span>&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 测试一个连接是否可用，如果不可用，关掉它并返回 false 否则可用返回 true</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> conn</span><span class="hljs-comment"> *            需要测试的数据库连接</span><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 返回 true 表示此连接可用， false 表示不可用</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">testConnection</span><span class="hljs-params">(Connection conn)</span> </span>&#123;<span class="hljs-keyword">try</span> &#123;<span class="hljs-comment">// 判断测试表是否存在</span><span class="hljs-keyword">if</span> (testTable.equals(<span class="hljs-string">&quot;&quot;</span>)) &#123;<span class="hljs-comment">// 如果测试表为空，试着使用此连接的 setAutoCommit() 方法</span><span class="hljs-comment">// 来判断连接否可用（此方法只在部分数据库可用，如果不可用 ,</span><span class="hljs-comment">// 抛出异常）。注意：使用测试表的方法更可靠</span>conn.setAutoCommit(<span class="hljs-keyword">true</span>);&#125; <span class="hljs-keyword">else</span> &#123;<span class="hljs-comment">// 有测试表的时候使用测试表测试</span><span class="hljs-comment">// check if this connection is valid</span>Statement stmt = conn.createStatement();stmt.execute(<span class="hljs-string">&quot;select count(*) from &quot;</span> + testTable);&#125;&#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;<span class="hljs-comment">// 上面抛出异常，此连接己不可用，关闭它，并返回 false;</span>closeConnection(conn);<span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;&#125;<span class="hljs-comment">// 连接可用，返回 true</span><span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 此函数返回一个数据库连接到连接池中，并把此连接置为空闲。 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> 需返回到连接池中的连接对象</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">returnConnection</span><span class="hljs-params">(Connection conn)</span> </span>&#123;<span class="hljs-comment">// 确保连接池存在，如果连接没有创建（不存在），直接返回</span><span class="hljs-keyword">if</span> (connections == <span class="hljs-keyword">null</span>) &#123;System.out.println(<span class="hljs-string">&quot; 连接池不存在，无法返回此连接到连接池中 !&quot;</span>);<span class="hljs-keyword">return</span>;&#125;PooledConnection pConn = <span class="hljs-keyword">null</span>;Enumeration enumerate = connections.elements();<span class="hljs-comment">// 遍历连接池中的所有连接，找到这个要返回的连接对象</span><span class="hljs-keyword">while</span> (enumerate.hasMoreElements()) &#123;pConn = (PooledConnection) enumerate.nextElement();<span class="hljs-comment">// 先找到连接池中的要返回的连接对象</span><span class="hljs-keyword">if</span> (conn == pConn.getConnection()) &#123;<span class="hljs-comment">// 找到了 , 设置此连接为空闲状态</span>pConn.setBusy(<span class="hljs-keyword">false</span>);<span class="hljs-keyword">break</span>;&#125;&#125;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 刷新连接池中所有的连接对象</span><span class="hljs-comment"> * </span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">refreshConnections</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;<span class="hljs-comment">// 确保连接池己创新存在</span><span class="hljs-keyword">if</span> (connections == <span class="hljs-keyword">null</span>) &#123;System.out.println(<span class="hljs-string">&quot; 连接池不存在，无法刷新 !&quot;</span>);<span class="hljs-keyword">return</span>;&#125;PooledConnection pConn = <span class="hljs-keyword">null</span>;Enumeration enumerate = connections.elements();<span class="hljs-keyword">while</span> (enumerate.hasMoreElements()) &#123;<span class="hljs-comment">// 获得一个连接对象</span>pConn = (PooledConnection) enumerate.nextElement();<span class="hljs-comment">// 如果对象忙则等 5 秒 ,5 秒后直接刷新</span><span class="hljs-keyword">if</span> (pConn.isBusy()) &#123;wait(<span class="hljs-number">5000</span>); <span class="hljs-comment">// 等 5 秒</span>&#125;<span class="hljs-comment">// 关闭此连接，用一个新的连接代替它。</span>closeConnection(pConn.getConnection());pConn.setConnection(newConnection());pConn.setBusy(<span class="hljs-keyword">false</span>);&#125;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 关闭连接池中所有的连接，并清空连接池。</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">closeConnectionPool</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException </span>&#123;<span class="hljs-comment">// 确保连接池存在，如果不存在，返回</span><span class="hljs-keyword">if</span> (connections == <span class="hljs-keyword">null</span>) &#123;System.out.println(<span class="hljs-string">&quot; 连接池不存在，无法关闭 !&quot;</span>);<span class="hljs-keyword">return</span>;&#125;PooledConnection pConn = <span class="hljs-keyword">null</span>;Enumeration enumerate = connections.elements();<span class="hljs-keyword">while</span> (enumerate.hasMoreElements()) &#123;pConn = (PooledConnection) enumerate.nextElement();<span class="hljs-comment">// 如果忙，等 5 秒</span><span class="hljs-keyword">if</span> (pConn.isBusy()) &#123;wait(<span class="hljs-number">5000</span>); <span class="hljs-comment">// 等 5 秒</span>&#125;<span class="hljs-comment">// 5 秒后直接关闭它</span>closeConnection(pConn.getConnection());<span class="hljs-comment">// 从连接池向量中删除它</span>connections.removeElement(pConn);&#125;<span class="hljs-comment">// 置连接池为空</span>connections = <span class="hljs-keyword">null</span>;&#125; <span class="hljs-comment">/**</span><span class="hljs-comment"> * 关闭一个数据库连接</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> 需要关闭的数据库连接</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">closeConnection</span><span class="hljs-params">(Connection conn)</span> </span>&#123;<span class="hljs-keyword">try</span> &#123;conn.close();&#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;System.out.println(<span class="hljs-string">&quot; 关闭数据库连接出错： &quot;</span> + e.getMessage());&#125;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * 使程序等待给定的毫秒数</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> 给定的毫秒数</span><span class="hljs-comment"> */</span> <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">wait</span><span class="hljs-params">(<span class="hljs-keyword">int</span> mSeconds)</span> </span>&#123;<span class="hljs-keyword">try</span> &#123;Thread.sleep(mSeconds);&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;&#125;&#125;<span class="hljs-comment">/**</span><span class="hljs-comment"> * </span><span class="hljs-comment"> * 内部使用的用于保存连接池中连接对象的类 此类中有两个成员，一个是数据库的连接，另一个是指示此连接是否 正在使用的标志。</span><span class="hljs-comment"> */</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PooledConnection</span> </span>&#123;Connection connection = <span class="hljs-keyword">null</span>;<span class="hljs-comment">// 数据库连接</span><span class="hljs-keyword">boolean</span> busy = <span class="hljs-keyword">false</span>; <span class="hljs-comment">// 此连接是否正在使用的标志，默认没有正在使用</span> <span class="hljs-comment">// 构造函数，根据一个 Connection 构告一个 PooledConnection 对象</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PooledConnection</span><span class="hljs-params">(Connection connection)</span> </span>&#123;<span class="hljs-keyword">this</span>.connection = connection;&#125; <span class="hljs-comment">// 返回此对象中的连接</span><span class="hljs-function"><span class="hljs-keyword">public</span> Connection <span class="hljs-title">getConnection</span><span class="hljs-params">()</span> </span>&#123;<span class="hljs-keyword">return</span> connection;&#125; <span class="hljs-comment">// 设置此对象的，连接</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setConnection</span><span class="hljs-params">(Connection connection)</span> </span>&#123;<span class="hljs-keyword">this</span>.connection = connection;&#125; <span class="hljs-comment">// 获得对象连接是否忙</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isBusy</span><span class="hljs-params">()</span> </span>&#123;<span class="hljs-keyword">return</span> busy;&#125; <span class="hljs-comment">// 设置对象的连接正在忙</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setBusy</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span> busy)</span> </span>&#123;<span class="hljs-keyword">this</span>.busy = busy;&#125;&#125; &#125;</code></pre></p><p>ConnectionPoolUtils.java</p><pre><code class="hljs java"><span class="hljs-comment">/*连接池工具类，返回唯一的一个数据库连接池对象,单例模式*/</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConnectionPoolUtils</span> </span>&#123;<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">ConnectionPoolUtils</span><span class="hljs-params">()</span></span>&#123;&#125;;<span class="hljs-comment">//私有静态方法</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ConnectionPool poolInstance = <span class="hljs-keyword">null</span>;<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ConnectionPool <span class="hljs-title">GetPoolInstance</span><span class="hljs-params">()</span></span>&#123;<span class="hljs-keyword">if</span>(poolInstance == <span class="hljs-keyword">null</span>) &#123;poolInstance = <span class="hljs-keyword">new</span> ConnectionPool( <span class="hljs-string">&quot;com.mysql.jdbc.Driver&quot;</span>, <span class="hljs-string">&quot;jdbc:mysql://localhost:3306/student?serverTimezone=UTC&quot;</span>, <span class="hljs-string">&quot;root&quot;</span>, <span class="hljs-string">&quot;233333333&quot;</span>);<span class="hljs-keyword">try</span> &#123;poolInstance.createPool();&#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<span class="hljs-comment">// TODO Auto-generated catch block</span>e.printStackTrace();&#125;&#125;<span class="hljs-keyword">return</span> poolInstance;&#125;&#125;</code></pre><p>ConnectionPoolTest.java<br><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.sql.Connection;<span class="hljs-keyword">import</span> java.sql.DriverManager;<span class="hljs-keyword">import</span> java.sql.ResultSet;<span class="hljs-keyword">import</span> java.sql.SQLException;<span class="hljs-keyword">import</span> java.sql.Statement;  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConnectionTest</span> </span>&#123; <span class="hljs-comment">/**</span><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> args</span><span class="hljs-comment"> * <span class="hljs-doctag">@throws</span> Exception </span><span class="hljs-comment"> */</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>&#123; <span class="hljs-keyword">try</span> &#123;      <span class="hljs-comment">/*使用连接池创建100个连接的时间*/</span>        <span class="hljs-comment">/*// 创建数据库连接库对象</span><span class="hljs-comment">       ConnectionPool connPool = new ConnectionPool(&quot;com.mysql.jdbc.Driver&quot;,&quot;jdbc:mysql://localhost:3306/test&quot;, &quot;root&quot;, &quot;123456&quot;);</span><span class="hljs-comment">       // 新建数据库连接库</span><span class="hljs-comment">           connPool.createPool();*/</span>       ConnectionPool  connPool=ConnectionPoolUtils.GetPoolInstance();<span class="hljs-comment">//单例模式创建连接池对象</span>            <span class="hljs-comment">// SQL测试语句</span>       String sql = <span class="hljs-string">&quot;Select * from pet&quot;</span>;       <span class="hljs-comment">// 设定程序运行起始时间</span>       <span class="hljs-keyword">long</span> start = System.currentTimeMillis();             <span class="hljs-comment">// 循环测试100次数据库连接</span>              <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++) &#123;                  Connection conn = connPool.getConnection(); <span class="hljs-comment">// 从连接库中获取一个可用的连接</span>                  Statement stmt = conn.createStatement();                  ResultSet rs = stmt.executeQuery(sql);                  <span class="hljs-keyword">while</span> (rs.next()) &#123;                      String name = rs.getString(<span class="hljs-string">&quot;name&quot;</span>);                   <span class="hljs-comment">//  System.out.println(&quot;查询结果&quot; + name);</span>                  &#125;                  rs.close();                  stmt.close();                  connPool.returnConnection(conn);<span class="hljs-comment">// 连接使用完后释放连接到连接池</span>              &#125;              System.out.println(<span class="hljs-string">&quot;经过100次的循环调用，使用连接池花费的时间:&quot;</span>+ (System.currentTimeMillis() - start) + <span class="hljs-string">&quot;ms&quot;</span>);              <span class="hljs-comment">// connPool.refreshConnections();//刷新数据库连接池中所有连接，即不管连接是否正在运行，都把所有连接都释放并放回到连接池。注意：这个耗时比较大。</span>             connPool.closeConnectionPool();<span class="hljs-comment">// 关闭数据库连接池。注意：这个耗时比较大。</span>              <span class="hljs-comment">// 设定程序运行起始时间</span>              start = System.currentTimeMillis();                            <span class="hljs-comment">/*不使用连接池创建100个连接的时间*/</span>             <span class="hljs-comment">// 导入驱动</span>              Class.forName(<span class="hljs-string">&quot;com.mysql.jdbc.Driver&quot;</span>);              <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++) &#123;                  <span class="hljs-comment">// 创建连接</span>                 Connection conn = DriverManager.getConnection(                          <span class="hljs-string">&quot;jdbc:mysql://localhost:3306/test&quot;</span>, <span class="hljs-string">&quot;root&quot;</span>, <span class="hljs-string">&quot;123456&quot;</span>);                  Statement stmt = conn.createStatement();                  ResultSet rs = stmt.executeQuery(sql);                 <span class="hljs-keyword">while</span> (rs.next()) &#123;                  &#125;                 rs.close();                 stmt.close();                 conn.close();<span class="hljs-comment">// 关闭连接</span>             &#125;             System.out.println(<span class="hljs-string">&quot;经过100次的循环调用，不使用连接池花费的时间:&quot;</span>                     + (System.currentTimeMillis() - start) + <span class="hljs-string">&quot;ms&quot;</span>);         &#125; <span class="hljs-keyword">catch</span> (SQLException e) &#123;            e.printStackTrace();         &#125; <span class="hljs-keyword">catch</span> (ClassNotFoundException e) &#123;             e.printStackTrace();        &#125;&#125;</code></pre></p><p>注：关于对连接池深度的思考，对于一般的连接池最多10个就够了，基本可以应付一般的小项目。但是连接池开大过大，是不是就可以理解成没有连接池，因为缓存太多了就和没开连接池一样呢？</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;简介数据库连接池&lt;/p&gt;</summary>
    
    
    
    <category term="Java" scheme="https://www.wulnut.top/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>浅谈script标签的defer和async</title>
    <link href="https://www.wulnut.top/2020/11/28/%E6%B5%85%E8%B0%88script%E6%A0%87%E7%AD%BE/"/>
    <id>https://www.wulnut.top/2020/11/28/%E6%B5%85%E8%B0%88script%E6%A0%87%E7%AD%BE/</id>
    <published>2020-11-28T12:44:47.862Z</published>
    <updated>2023-02-07T03:20:51.188Z</updated>
    
    <content type="html"><![CDATA[<p>有点意思</p><a id="more"></a><h1 id="1-什么鬼"><a href="#1-什么鬼" class="headerlink" title="1.什么鬼"></a>1.什么鬼</h1><p>今天在做一个小玩意的时候看到一句吊炸天的代码<br><pre><code class="hljs js">&lt;script src=<span class="hljs-string">&quot;#link(&quot;</span>xxxx/xx/home/home.js<span class="hljs-string">&quot;)&quot;</span> type=<span class="hljs-string">&quot;text/javascript&quot;</span> <span class="hljs-keyword">async</span> defer&gt;&lt;/script&gt;</code></pre></p><p>卧槽，竟然同时有async和defer属性，这尼玛是什么操作，赶快学习一下</p><h1 id="2-调查一番"><a href="#2-调查一番" class="headerlink" title="2.调查一番"></a>2.调查一番</h1><p>先看看<code>async</code>和<code>defer</code>各自的定义吧，翻开红宝书望远镜，是这么介绍的</p><h2 id="2-1-defer"><a href="#2-1-defer" class="headerlink" title="2.1 defer"></a>2.1 defer</h2><blockquote><p>这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说，脚本会被延迟到整个页面都解析完毕后再运行。因此，在<code>&lt;script&gt;</code>元素中设置<code>defer</code>属性，相当于告诉浏览器立即下载，但延迟执行。<br>HTML5规范要求脚本按照它们出现的先后顺序执行，因此第一个延迟脚本会先于第二个延迟脚本执行，而这两个脚本会先于<code>DOMContentLoaded</code>事件执行。在现实当中，延迟脚本并不一定会按照顺序执行，也不一定会在<code>DOMContentLoad</code>时间触发前执行，因此最好只包含一个延迟脚本。</p></blockquote><h2 id="2-2-async"><a href="#2-2-async" class="headerlink" title="2.2 async"></a>2.2 async</h2><blockquote><p>这个属性与defer类似，都用于改变处理脚本的行为。同样与defer类似，async只适用于外部脚本文件，并告诉浏览器立即下载文件。但与defer不同的是，标记为async的脚本并不保证按照它们的先后顺序执行。<br>第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行，从而异步加载页面其他内容。</p></blockquote><p>概括来讲，就是这两个属性都会使script标签异步加载，然而执行的时机是不一样的。引用segmentfault上的一个回答中的一张图<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/1443517782-57c6928b20b56_articlex.webp" alt=" "></p><p>蓝色线代表网络读取，红色线代表执行时间，这俩都是针对脚本的；绿色线代表 HTML 解析。</p><p>也就是说<code>async</code>是乱序的，而defer是顺序执行，这也就决定了<code>async</code>比较适用于百度分析或者谷歌分析这类不依赖其他脚本的库。从图中可以看到一个普通的<code>&lt;script&gt;</code>标签的加载和解析都是同步的，会阻塞DOM的渲染，这也就是我们经常会把<code>&lt;script&gt;</code>写在<code>&lt;body&gt;</code>底部的原因之一，为了防止加载资源而导致的长时间的白屏，另一个原因是js可能会进行DOM操作，所以要在DOM全部渲染完后再执行。</p><h2 id="2-3-really？"><a href="#2-3-really？" class="headerlink" title="2.3 really？"></a>2.3 really？</h2><p><strong>然而</strong>，这张图（几乎是百度搜到的唯一答案）是不严谨的，这只是规范的情况，大多数浏览器在实现的时候会作出优化。</p><p>来看看chrome是怎么做的:</p><blockquote><p>《WebKit技术内幕》：</p><ol><li>当用户输入网页URL的时候，WebKit调用其资源加载器加载该URL对应的网页。</li><li>加载器依赖网络模块建立连接，发送请求并接受答复。</li><li>WebKit接收到各种网页或者资源的数据，其中某些资源可能是同步或异步获取的。</li><li>网页被交给HTML解释器转变成一系列的词语（Token）。</li><li>解释器根据词语构建节点（Node），形成DOM树。</li><li>如果节点是JavaScript代码的话，调用JavaScript引擎解释并执行。</li><li>JavaScript代码可能会修改DOM树的结构。</li><li>如果节点需要依赖其他资源，例如图片、CSS、视频等，调用资源加载器来加载他们，但是他们是异步的，不会阻碍当前DOM树的继续创建；如果是JavaScript资源URL（没有标记异步方式），则需要停止当前DOM树的创建，直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。</li></ol></blockquote><p>所以，通俗来讲，chrome浏览器首先会请求HTML文档，然后对其中的各种资源调用相应的资源加载器进行异步网络请求，同时进行DOM渲染，直到遇到<code>&lt;script&gt;</code>标签的时候，主进程才会停止渲染等待此资源加载完毕然后调用V8引擎对js解析，继而继续进行<code>DOM</code>解析。我的理解如果加了async属性就相当于单独开了一个进程去独立加载和执行，而defer是和将<code>&lt;script&gt;</code>放到<code>&lt;body&gt;</code>底部一样的效果。</p><h1 id="3-实验一发"><a href="#3-实验一发" class="headerlink" title="3. 实验一发"></a>3. 实验一发</h1><h2 id="3-1-demo"><a href="#3-1-demo" class="headerlink" title="3.1 demo"></a>3.1 demo</h2><p>为了验证上面的结论我们来测试一下</p><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.css&quot;</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;http://cdn.staticfile.org/foundation/6.0.1/css/foundation.css&quot;</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;http://lib.sinaapp.com/js/angular.js/angular-1.2.19/angular.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;http://libs.baidu.com/backbone/0.9.2/backbone.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;http://libs.baidu.com/jquery/2.0.0/jquery.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>    ul&gt;li&#123;这是第$个节点&#125;*1000<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></code></pre><p>一个简单的demo，从各个CDN上引用了2个CSS3个JS，在body里面创建了1000个li。通过调整外部引用资源的位置和加入相关的属性利用chrome的Timeline进行验证。</p><h2 id="放置在内"><a href="#放置在内" class="headerlink" title="放置在内"></a>放置在<head>内</h2><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/2699686611-57c6929ae4e60_articlex.webp" alt=""><br>异步加载资源，但会阻塞<code>&lt;body&gt;</code>的渲染会出现白屏，按照顺序立即执行脚本</p><h2 id="3-3-放置在底部"><a href="#3-3-放置在底部" class="headerlink" title="3.3 放置在底部"></a>3.3 放置在<body>底部</h2><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/2025377632-57c692a8e1d80_articlex.webp" alt=""><br>异步加载资源，等<body>中的内容渲染完毕后且加载完按顺序执行JS</p><h2 id="3-3-放置在头部并使用async"><a href="#3-3-放置在头部并使用async" class="headerlink" title="3.3 放置在头部并使用async"></a>3.3 放置在<head>头部并使用async</h2><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/1609828464-57c692b36943c_articlex.webp" alt=""><br>异步加载资源，且加载完JS资源立即执行，并不会按顺序，谁快谁先上</p><h2 id="3-4-放置在头部并使用defer"><a href="#3-4-放置在头部并使用defer" class="headerlink" title="3.4 放置在头部并使用defer"></a>3.4 放置在<head>头部并使用defer</h2><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/3039840796-57c692be0062f_articlex.webp" alt=""><br>异步加载资源，在DOM渲染后之后再按顺序执行JS</p><h2 id="3-5-放置在头部并同时使用async和defer"><a href="#3-5-放置在头部并同时使用async和defer" class="headerlink" title="3.5 放置在头部并同时使用async和defer"></a>3.5 放置在<head>头部并同时使用async和defer</h2><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/3802019618-57c692c9d447e_articlex.webp" alt=""><br>表现和async一致，开了个脑洞，把这两个属性交换一下位置，看会不会有覆盖效果，结果发现是一致的 = =、</p><p>综上，在webkit引擎下，建议的方式仍然是把<code>&lt;script&gt;</code>写在<code>&lt;body&gt;</code>底部，如果需要使用百度谷歌分析或者不蒜子等独立库时可以使用async属性，若你的<code>&lt;script&gt;</code>标签必须写在<code>&lt;head&gt;</code>头部内可以使用defer属性</p><h1 id="4-兼容性"><a href="#4-兼容性" class="headerlink" title="4. 兼容性"></a>4. 兼容性</h1><p>那么，同时写上的原因是什么呢，兼容性?</p><p>上caniuse，async在IE&lt;=9时不支持，其他浏览器OK；defer在IE&lt;=9时支持但会有bug，其他浏览器OK；现象在这个issue里有描述，这也就是“望远镜”里建议只有一个defer的原因。所以两个属性都指定是为了在async不支持的时候启用defer，但defer在某些情况下还是有bug。</p><blockquote><p>The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the synchronous blocking behavior that is the default.</p></blockquote><h1 id="5-结论"><a href="#5-结论" class="headerlink" title="5. 结论"></a>5. 结论</h1><p>其实这么讲来，最稳妥的办法还是把<code>&lt;script&gt;</code>写在<code>&lt;body&gt;</code>底部，没有兼容性问题，没有白屏问题，没有执行顺序问题，高枕无忧，不要搞什么defer和async的花啦~<br>目前只研究了chrome的webkit的渲染机制，Firefox和IE的有待继续研究，图片和CSS以及其他外部资源的渲染有待研究</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;有点意思&lt;/p&gt;</summary>
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="学习" scheme="https://www.wulnut.top/tags/%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="js" scheme="https://www.wulnut.top/tags/js/"/>
    
  </entry>
  
  <entry>
    <title>Cache地址映射与计算方式</title>
    <link href="https://www.wulnut.top/2020/11/01/cache%E6%98%A0%E5%B0%84%E6%96%B9%E5%BC%8F/"/>
    <id>https://www.wulnut.top/2020/11/01/cache%E6%98%A0%E5%B0%84%E6%96%B9%E5%BC%8F/</id>
    <published>2020-11-01T11:38:54.289Z</published>
    <updated>2023-02-06T15:10:32.712Z</updated>
    
    <content type="html"><![CDATA[<p>Cache地址映射</p><a id="more"></a><h1 id="什么是Cache地址映射"><a href="#什么是Cache地址映射" class="headerlink" title="什么是Cache地址映射"></a>什么是Cache地址映射</h1><p>&emsp;&emsp;主存的容量比Cache要大的多，所以我们得采用多个主存块映射到同一个Cache行中的方法，将要访问的局部主存区域取到Cache中。映射方法有：直接映射，全相联映射，组相链映射</p><h2 id="直接映射"><a href="#直接映射" class="headerlink" title="直接映射"></a>直接映射</h2><p>直接映射是最简单粗暴的办法：</p><blockquote><p>(块地址)mod(cache中的块数)</p></blockquote><p>&emsp;&emsp;一个内存块地址始终映射到一个固定的Cache 地址。下图中主存被分为了0-2047个内存块，缓存块或者说cache line有16块。那么第0，16，n*16块因为mod16都为0，所以他们对应到的Cache行号都为0。如果我们要访问第16号内存块(内存块从0开始计数的)，只要它在缓存块里面，那么它必定是在0号缓存块，也就是行号为0.<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162229663-269157985.webp" alt=""></p><p>&emsp;&emsp;知道了映射方法，那么如何规定主存地址呢？其实对于取模运算，我们只需要取低位字节就可以了。在十进制里面如果对16取余，那么结果定是两位数以内，并且不会大于15。比如说Cache有16行，16是2的4次方，那么我们就可以直接取主存块号的低四位作为Cache行号。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162127959-1713738037.webp" alt=""></p><p>&emsp;&emsp;17对应的cache行号就是1.但当我们读取某一个缓存行时，我们怎么知道他是0块群的还是其他块群的呢？其实正如主存块号中包含了Cache行号一样，其低四位之前的高位就可以作为区分的Tag(主存标记)使用。最后一点就是，CPU读取数据只是要读取它需要的字(Word)而已，那么这个字具体是在Cache line的哪里，我们还需要一个偏移量来纪录它。所以直接映射的主存地址应该由三部分组成：主存子块标记，Cache子块标记，字块内地址。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162153262-921590979.webp" alt=""></p><blockquote><p>现在我们来自己动手做一做：假设数据在主存和Cache间的传送单位为512B，Cache大小为$2^{13}$B，主存大小为$2^{20}$B。</p></blockquote><p>&emsp;&emsp;因为主存大小为$2^{20}$B,且以512B为传送单位。那么$2^{20}$B=2048块$ \times $512B/块，主存可以划分为2048块，主存地址为20位二进制数。因为我们需要确定要取的是块中的哪个字，又$512=2^9$，所以需要9位作为偏移量，或者说字块内地址。 Cache可以划分出16行($2^{13}$=16行 $\times$ 512B/行)，也就是说划出4位作为行号标记，或者说Cache字块地址。剩下的7位自然就作为主存字块标记啦。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162153262-921590979.webp" alt=""></p><p>优缺点：</p><ul><li>电路实现简单，命中时间短</li><li>无需考虑替换问题</li><li>Cache的存储空间没有得到充分使用，命中率低</li></ul><h2 id="全相联映射"><a href="#全相联映射" class="headerlink" title="全相联映射"></a>全相联映射</h2><p>&emsp;&emsp;针对直接映射Cache空间利用率低的问题，我们有一种简单粗暴的办法提升空间的利用率。那就是主存中的任意一块都可以映射到Cache中的任意一个位置。有空位置你就坐下，随意，映射位置不在固定。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162153262-921590979.webp" alt=""></p><p>&emsp;&emsp;那么我们唯一要做的就是知道Cache中是对应主存中的哪一块和字块内地址就行。因为是随便映射，所以我们把直接映射中的Cache字块标记合并到主存字块标记中。</p><p><code>全相联映射主存地址只有两部分：主存字块标记，字块内地址。</code></p><p>优缺点：</p><ul><li>不存在冲突缺失，命中率高</li><li>电路复杂，速度慢</li></ul><h2 id="组相联映射"><a href="#组相联映射" class="headerlink" title="组相联映射"></a>组相联映射</h2><p>&emsp;&emsp;综合前两种方法的就是组相联映射，具体做法是：将Cache中的行分组，主存块映射到固定的行中，但行中的位置可以随意。也就是组间直接映射，组内全相联映射。</p><blockquote><p>Cache组号=主存块号 mod Cache组数</p></blockquote><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162251696-1669819464.webp" alt=""></p><p>&emsp;&emsp;那么问题来了怎么确定Cache中的字块是对应主存的那一块呢？首先我们仍需要字块内地址，需要区分组号，那么剩下的地址就可以作为主存字块标记使用。</p><blockquote><p>容量为64块的Cache，采用组相联方式映像，字块大小为128字，每4块为一组。若主存容量为4096块，且以字编址，那么主存地址该如何划分？</p></blockquote><p>&emsp;&emsp;因为$4096=2^{19}$，所以主存地址应该为19位，Cache总共有16组，所以需要4位确定组号。又字块大小为$128$字，$128=2^7$，所有字块内地址为7位，剩下的$19-7-4=8$位作为主存字块标记。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img1932912-20200702162305856-386316918.webp" alt=""></p><p>优缺点：</p><ul><li>电路较简单，速度较快，命中率较高，属于比较理想的方式</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;Cache地址映射&lt;/p&gt;</summary>
    
    
    
    <category term="Notes" scheme="https://www.wulnut.top/categories/Notes/"/>
    
    
  </entry>
  
  <entry>
    <title>虚拟地址和物理地址</title>
    <link href="https://www.wulnut.top/2020/10/16/virtual_address/"/>
    <id>https://www.wulnut.top/2020/10/16/virtual_address/</id>
    <published>2020-10-16T12:38:23.976Z</published>
    <updated>2023-02-07T03:22:35.527Z</updated>
    
    <content type="html"><![CDATA[<p>计算机中虚拟地址与物理地址</p><a id="more"></a><h1 id="一、了解基本概念"><a href="#一、了解基本概念" class="headerlink" title="一、了解基本概念"></a>一、了解基本概念</h1><h2 id="1-地址空间"><a href="#1-地址空间" class="headerlink" title="1.地址空间"></a>1.地址空间</h2><p>&emsp;&emsp;我们最开始接触地址的时候，应该是在我们学习数组的时候。地址空间其实是一个比较抽象的概念，我们可以把它想象成一个长的数组，每个数组元素占一个字节。那么这个数组的长度就由地址空间长度来决定。例如：我们32位的系统的地址空间就是我们的$2^{32}$字节(4GB),而64位的地址空间大小就是$2^{64}$个字节。这也就解释了在我们32位的操作系统，为什么最大只能支持4GB的有效内存。</p><p><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/15774762-3a7d9d5ef4e0d886.webp" alt=" "></p><h2 id="2-虚拟地址的由来"><a href="#2-虚拟地址的由来" class="headerlink" title="2.虚拟地址的由来"></a>2.虚拟地址的由来</h2><p>&emsp;&emsp;在早期的计算机中，程序是直接运行到物理内存（可以理解为内存条上的内存）上的。也就是说，程序运行的时候直接访问的就是物理地址。如果，我们的一个计算机只运行一个程序，那么只有这个程序所需要的内存空间不超过物理内存空间的大小，就不会有问题。但是，我们正在希望的是在某个时候同时运行多个程序。那么这个时候，就会有个一个问题，计算机如何把有限的物理内存分配给多个程序使用呢？<br>&emsp;&emsp;某台计算机总的内存大小是128M，现在同时运行两个程序A和B，A需占用内存10M，B需占用内存110。计算机在给程序分配内存时会采取这样的方法：先将内存中的前10M分配给程序A,接着再从内存中剩余的118M中划分出110M分配给程序B。这种分配方法可以保证程序A和程序B都能运行，但是这种简单的内存分配策略问题很多。<br>&emsp;&emsp;问题1：进程地址空间不隔离。由于程序都是直接访问物理内存，所以恶意程序可以随意修改别的进程的内存数据，以达到破坏的目的。有些非恶意的，但是有bug的程序也可能不小心修改了其它程序的内存数据，就会导致其它程序的运行出现异常。这种情况对用户来说是无法容忍的，因为用户希望使用计算机的时候，其中一个任务失败了，至少不能影响其它的任务。<br>&emsp;&emsp;问题2：内存使用效率低。在A和B都运行的情况下，如果用户又运行了程序C，而程序C需要20M大小的内存才能运行，而此时系统只剩下8M的空间可供使用，所以此时系统必须在已运行的程序中选择一个将该程序的数据暂时拷贝到硬盘上，释放出部分空间来供程序C使用，然后再将程序C的数据全部装入内存中运行。可以想象得到，在这个过程中，有大量的数据在装入装出，导致效率十分低下。<br>&emsp;&emsp;问题3：程序运行的地址不确定。当内存中的剩余空间可以满足程序C的要求后，操作系统会在剩余空间中随机分配一段连续的20M大小的空间给程序C使用，因为是随机分配的，所以程序运行的地址是不确定的。但是我们的某些硬件是需要在固定的地址上去开始运行的，但是如果这个地址后边被我们的程序占有，那么我们对这块内存的修改，就可能导致某些硬件不可用了。<br>&emsp;&emsp;为了解决上述问题，人们想到了一种变通的方法，就是增加一个中间层，利用一种间接的地址访问方法访问物理内存。按照这种方法，程序中访问的内存地址不再是实际的物理内存地址，而是一个虚拟地址，然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。这样，只要操作系统处理好虚拟地址到物理内存地址的映射，就可以保证不同的程序最终访问的内存地址位于不同的区域，彼此没有重叠，就可以达到内存地址空间隔离的效果。</p><h1 id="二、虚拟地址和物理地址映射"><a href="#二、虚拟地址和物理地址映射" class="headerlink" title="二、虚拟地址和物理地址映射"></a>二、虚拟地址和物理地址映射</h1><p>&emsp;&emsp;了解了我们的虚拟地址和物理地址的由来，下面我们来总结一下，他们的概念.<br>&emsp;&emsp;物理地址：物理地址空间是实在的存在于计算机中的一个实体，在每一台计算机中保持唯一独立性。我们可以称它为物理内存；如在32位的机器上，物理空间的大小理论上可以达到$2^{32}$字节(4GB)，但如果实际装了512的内存，那么其物理地址真正的有效部分只有$512MB = 512 \times 1024 KB = = 512 \times 1024 \times 1024 B (0x00000000\sim0x1fffffff)$。其他部分是无效的。<br>&emsp;&emsp;虚拟地址：虚拟地址并不真实存在于计算机中。每个进程都分配有自己的虚拟空间，而且只能访问自己被分配使用的空间。理论上，虚拟空间受物理内存大小的限制，如给有4GB内存，那么虚拟地址空间的地址范围就应该是$0x00000000\sim0xFFFFFFFF$.每个进程都有自己独立的虚拟地址空间。这样每个进程都能访问自己的地址空间，<br>这样做到了有效的隔离。<br>&emsp;&emsp;在上面我们提到了合理的内存管理机制。我们这里虚拟地址和物理地址之间的映射是通过MMU（内存管理单元）来完成的。<br><img src="https://wulnut-blog.oss-cn-shanghai.aliyuncs.com/blog-img/15774762-b0ec5de9cf127f27.webp" alt=" "><br>&emsp;&emsp;我们平时操作的内存其实都是通过操作虚拟地址的内存单元。通过通过MMU的映射来间接的操作我们的物理地址。</p><h1 id="对虚地址的理解"><a href="#对虚地址的理解" class="headerlink" title="对虚地址的理解"></a>对虚地址的理解</h1><h2 id="一"><a href="#一" class="headerlink" title="一"></a>一</h2><ol><li>每个进程都有自己独立的4G内存空间，各个进程的内存空间具有类似的结构。</li><li>一个新进程建立的时候，将会建立起自己的内存空间，此进程的数据，代码等从磁盘拷贝到自己的进程空间，哪些数据在哪里，都由进程控制表中的task_struct记录，task_struct中记录中一条链表，记录中内存空间的分配情况，哪些地址有数据，哪些地址无数据，哪些可读，哪些可写，都可以通过这个链表记录。</li><li>每个进程已经分配的内存空间，都与对应的磁盘空间映射。<br><br><br>问题：计算机明明没有那么多内存（n个进程的话就需要$n\times4$G）内存建立一个进程，就要把磁盘上的程序文件拷贝到进程对应的内存中去，对于一个程序对应的多个进程这种情况，浪费内存！</li></ol><h2 id="二"><a href="#二" class="headerlink" title="二"></a>二</h2><ol><li>每个进程的4G内存空间只是虚拟内存空间，每次访问内存空间的某个地址，都需要把地址翻译为实际物理内存地址。</li><li>所有进程共享同一物理内存，每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。</li><li>进程要知道哪些内存地址上的数据在物理内存上，哪些不在，还有在物理内存上的哪里，需要用页表来记录。</li><li>页表的每一个表项分两部分，第一部分记录此页是否在物理内存上，第二部分记录物理内存页的地址（如果在的话）。</li><li>当进程访问某个虚拟地址，去看页表，如果发现对应的数据不在物理内存中，则缺页异常。</li><li>缺页异常的处理过程，就是把进程需要的数据从磁盘上拷贝到物理内存中，如果内存已经满了，没有空地方了，那就找一个页覆盖，当然如果被覆盖的页曾经被修改过，需要将此页写回磁盘。</li></ol><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><ol><li>既然每个进程的内存空间都是一致而且固定的，所以链接器在链接可执行文件时，可以设定内存地址，而不用去管这些数据最终实际的内存地址，这是有独立内存空间的好处。</li><li>当不同的进程使用同样的代码时，比如库文件中的代码，物理内存中可以只存储一份这样的代码，不同的进程只需要把自己的虚拟内存映射过去就可以了，节省内存。</li><li>在程序需要分配连续的内存空间的时候，只需要在虚拟内存空间分配连续空间，而不需要实际物理内存的连续空间，可以利用碎片。</li></ol><p>&emsp;&emsp;另外，事实上，在每个进程创建加载时，内核只是为进程“创建”了虚拟内存的布局，具体就是初始化进程控制表中内存相关的链表，实际上并不立即就把虚拟内存对应位置的程序数据和代码<br>（比如.text .data段）.拷贝到物理内存中，只是建立好虚拟内存和磁盘文件之间的映射就好（叫做存储器映射）,等到运行到对应的程序时，才会通过缺页异常，来拷贝数据。还有进程运行过程中，<br>要动态分配内存，比如malloc时，也只是分配了虚拟内存，即为这块虚拟内存对应的页表项做相应设置，当进程真正访问到此数据时，才引发缺页异常。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;计算机中虚拟地址与物理地址&lt;/p&gt;</summary>
    
    
    
    <category term="study" scheme="https://www.wulnut.top/categories/study/"/>
    
    
    <category term="杂谈" scheme="https://www.wulnut.top/tags/%E6%9D%82%E8%B0%88/"/>
    
  </entry>
  
  <entry>
    <title>超级源点与超级汇点</title>
    <link href="https://www.wulnut.top/2020/09/27/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84(3)/"/>
    <id>https://www.wulnut.top/2020/09/27/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84(3)/</id>
    <published>2020-09-27T15:57:36.269Z</published>
    <updated>2020-10-04T12:10:36.968Z</updated>
    
    <content type="html"><![CDATA[<p>超级源点与超级汇点<br><a id="more"></a></p><h1 id="背景1"><a href="#背景1" class="headerlink" title="背景1"></a>背景1</h1><p>给出题目，在一张图中有多个点起点，一个终点，求所有起点到终点的最短距离。</p><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><ol><li>跑N边单源最短路，但是这样是不行的肯定超时。</li><li>floyd求出所有最短路，枚举每个起点到终点的距离，这个似乎比法1更慢。</li><li>反向建边，反向跑一遍Dijkstra，或者SPFA，这样就能求到终点到起点的距离，在枚举最小的一个即可，时间复杂度为一遍最短路加枚举N。</li><li>建立超级源点，虚拟出一个点作为源点，源点到所有起点的距离都是0，那么这样求超级源点到终点的最短距离就是所有起点到终点的距离的最短一个，时间复杂度为一遍最短路。</li></ol><h1 id="背景2"><a href="#背景2" class="headerlink" title="背景2"></a>背景2</h1><p>给出一张图中有一个起点，有多个终点，求一个起点到所有终点的最短距离。</p><h2 id="方法-1"><a href="#方法-1" class="headerlink" title="方法"></a>方法</h2><ol><li>直接忽略floyd</li><li>一遍最短路（SPFA或Dijkstra），枚举N。</li><li>建立超级汇点，所有终点到汇点的距离为0，一遍最短路即可的出答案。</li></ol><h1 id="背景3"><a href="#背景3" class="headerlink" title="背景3"></a>背景3</h1><p>给出一张图，图中有若干起点与若干终点，在所有终点到起点的距离中的最短距离。</p><h2 id="方法-2"><a href="#方法-2" class="headerlink" title="方法"></a>方法</h2><ol><li>跑若干遍最短路，找到所有最短距离，比较得出最小值</li><li>建立超级源点，建立超级汇点，一遍Dijkstra或SPFA即可。</li></ol><p>通过上面我们大致知道超级源点超级汇点的建立的条件，而且通过超级源点（汇点）可以极大的减少题目的时间复杂度，在图论中用的比较多。最后我们用图的方式表示源点及汇点的建立。</p><p><img src="/img/short-path/super1.png" alt="如图"></p><p><img src="/img/short-path/super2.png" alt="如图"></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;超级源点与超级汇点&lt;br&gt;</summary>
    
    
    
    <category term="图论" scheme="https://www.wulnut.top/categories/%E5%9B%BE%E8%AE%BA/"/>
    
    
    <category term="C++" scheme="https://www.wulnut.top/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>最短路径之Dijkstra</title>
    <link href="https://www.wulnut.top/2020/09/20/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84(2)/"/>
    <id>https://www.wulnut.top/2020/09/20/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84(2)/</id>
    <published>2020-09-20T14:50:12.851Z</published>
    <updated>2020-09-27T15:58:50.063Z</updated>
    
    <content type="html"><![CDATA[<p>最短路径算法</p><a id="more"></a><p>&emsp;&emsp;在上一篇文章当中我们讲解了bellman-ford算法和spfa算法，其中spfa算法是我个人比较常用的算法，比赛当中几乎没有用过其他的最短路算法。但是spfa也是有缺点的，我们之前说过它的复杂度是<br>$O(kE)$，这里的E是边的数量。但有的时候边的数量很多，E最多能够达到$V^2$，这会导致超时，所以我们会更换其他的算法。这里说的其他的算法就是Dijkstra。</p><h1 id="Dijkstra算法的前提："><a href="#Dijkstra算法的前提：" class="headerlink" title="Dijkstra算法的前提："></a>Dijkstra算法的前提：</h1><ol><li>首先，Dijkstra处理的是带正权值的<code>有权图</code>，那么，就需要一个二维数组（如果空间大用list数组）存储各个点到达(边)的权值大小。<code>(邻接矩阵或者邻接表存储)</code></li><li>其次，还需要一个<code>bool数组</code>判断那些点已经确定最短长度，那些点没有确定。<code>int数组</code>记录距离<code>(在算法执行过程可能被多次更新)</code>。</li><li>需要<code>优先队列</code>加入<code>已经确定点的周围点</code>。每次抛出确定最短路径的那个并且确定最短，直到所有点路径确定最短为止。(优化后)</li></ol><h1 id="算法思想"><a href="#算法思想" class="headerlink" title="算法思想"></a>算法思想</h1><p>&emsp;&emsp;在上一篇文章当中我们曾经说过Bellman-ford算法本质上其实是动态规划算法，我们的状态就是每个点的最短距离，策略就是可行的边，由于一共最多要松弛V-1次，所以整体的算法复杂度很高。当我们用队列维护可以松弛的点之后，就将复杂度降到了<br>$O(kE)$，也就是spfa算法。<br>&emsp;&emsp;Dijkstra算法和Bellman-ford算法虽然都是最短路算法，但是核心的逻辑并不相同。Dijkstra算法的底层逻辑是贪心，也可以理解成贪心算法在图论当中的使用。<br>&emsp;&emsp;其实Dijstra算法和Bellman-ford算法类似，也是一个松弛的过程。即一开始的时候除了源点s之外，其他的点的距离都设置成无穷大，我们需要遍历这张图对这些距离进行松弛。所谓的松弛也就是要将这些距离变小。假设我们已经求到了两个点u和v的距离，我们用<br>dis[u]表示u到s的距离，dis[v]表示v的距离。</p><p>&emsp;&emsp;假设我们有dis[u] &lt; dis[v]，也就是说u离s更近，那么我们接下来要用一个新的点去搜索松弛的可能，u和v哪一个更有可能获得更好的结果呢？当然是u，所以我们选择u去进行新的松弛，这也就是贪心算法的体现。如果这一层理解了，算法的整个原理也就差不多<br>了。</p><p>我们来整理一下思路来看下完整的算法流程：</p><ol><li>我们用一个数组dis记录源点s到其他点的最短距离，起始时dis[s] = 0，其他值设为无穷大</li><li>我们从未访问过的点当中选择距离最小的点u，将它标记为已访问</li><li>遍历u所有可以连通的点v，如果dis[v] &lt; dis[u] + l[u] [v]，那么更新dis[v]</li><li>重复上述2，3两个步骤，直到所有可以访问的点都已经访问过</li></ol><p>怎么样，其实核心步骤只有两步，应该很好理解吧？我找到了一张不错的动图，大家可以根据上面的流程对照一下动图加深一下理解。</p><p><img src="/img/short-path/dijkstra.gif" alt="如图"><br>我们根据原理不难写出代码：<br>C++版本：<br><pre><code class="hljs C++"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;cstdio&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;cstring&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;algorithm&gt;</span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> N = <span class="hljs-number">510</span>;<span class="hljs-keyword">int</span> g[N][N];<span class="hljs-keyword">int</span> dis[N];<span class="hljs-keyword">int</span> st[N];<span class="hljs-keyword">int</span> n, m;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dijkstra</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-built_in">memset</span>(dis, <span class="hljs-number">0x3f</span>, <span class="hljs-keyword">sizeof</span> dis);        dis[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>;        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; n; ++ i) &#123;        <span class="hljs-keyword">int</span> t = <span class="hljs-number">-1</span>;        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">1</span>; j &lt;= n; ++ j) &#123;            <span class="hljs-keyword">if</span> (!st[j] &amp;&amp;(t == <span class="hljs-number">-1</span> || dis[t] &gt; dis[j])) &#123;                t = j;            &#125;        &#125;        st[t] = <span class="hljs-literal">true</span>;                <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">1</span>; j &lt;= n; ++ j) &#123;            dis[j] = min(dis[j], dis[t] + g[t][j]);        &#125;    &#125;        <span class="hljs-keyword">if</span>(dis[n] == <span class="hljs-number">0x3f3f3f3f</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;    <span class="hljs-keyword">return</span> dis[n];&#125;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d&quot;</span>, &amp;n, &amp;m);        <span class="hljs-built_in">memset</span>(g, <span class="hljs-number">0x3f</span>, <span class="hljs-keyword">sizeof</span> g);    <span class="hljs-keyword">while</span> ( m -- ) &#123;        <span class="hljs-keyword">int</span> a, b, c;        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d%d&quot;</span>, &amp;a, &amp;b, &amp;c);        g[a][b] = min(g[a][b], c);    &#125;        <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d&quot;</span>, dijkstra());        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;&#125;</code></pre></p><p>Pyhon版本：<br><pre><code class="hljs Python">INF = sys.maxsizeedges = [[]] <span class="hljs-comment"># 邻接表存储边</span>dis = [] <span class="hljs-comment"># 记录s到其他点的距离</span>visited = &#123;&#125; <span class="hljs-comment"># 记录访问过的点</span><span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:    mini = INF    u = <span class="hljs-number">0</span>    flag = <span class="hljs-literal">False</span>    <span class="hljs-comment"># 遍历所有未访问过点当中距离最小的</span>    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(V):        <span class="hljs-keyword">if</span> i <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> visited <span class="hljs-keyword">and</span> dis[i] &lt; mini:            mini, u = dis[i], i            flag = <span class="hljs-literal">True</span>                <span class="hljs-comment"># 如果没有未访问的点，则退出</span>    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> flag:        <span class="hljs-keyword">break</span>         visited[u] = <span class="hljs-literal">True</span>        <span class="hljs-keyword">for</span> v, l <span class="hljs-keyword">in</span> edges[u]:        dis[v] = <span class="hljs-built_in">min</span>(dis[v], dis[u] + l)</code></pre></p><p>&emsp;&emsp;虽然我们已经知道算法没有反例了，但是还是可以思考一下。主要的点在于我们每次都选择未访问的点进行松弛，有没有可能我们松弛了一个已经访问的点，由于它已经被松弛过了，导致后面没法拿来松弛其他的点呢？<br>&emsp;&emsp;其实是不可能的，因为我们每次选择的都是距离最小的未访问过的点。假设当前的点是u，我们找到了一个已经访问过的点v，是不可能存在dis[u] + l &lt; dis[v]的，因为dis[v]必然要小于dis[u]，v才有可能先于u访问。但是这有一个前提，就是每条边的长度不<br>能是负数。</p><h1 id="算法优化"><a href="#算法优化" class="headerlink" title="算法优化"></a>算法优化</h1><p>&emsp;&emsp;和Bellman-ford算法一样，Dijkstra算法最大的问题同样是复杂度。我们每次选择一个点进行松弛，选择的时候需要遍历一遍所有的点，显然这是非常耗时的。复杂度应该是$O(V^2 + E)$，这里的E是边的数量，Dijkstra中每个点只会松弛一次，也就意味着每条<br>边最多遍历一次。<br>&emsp;&emsp;我们观察一下会发现，外面这层循环也就算了，里面这层循环很没有必要，我们只是找了一个最值而已。完全可以使用数据结构来代替循环查询，维护最值的场景我们也已经非常熟悉了，当然是使用优先队列。</p><h1 id="算法流程"><a href="#算法流程" class="headerlink" title="算法流程"></a>算法流程</h1><ul><li>一般从选定点开始抛入优先队列。（路径一般为0），<code>bool数组</code>标记0的位置(最短为0) , 然后0周围连通的点抛入优先队列中（可能是node类），并把各个点的距离记录到对应数组内<code>(如果小于就更新，大于就不动，初始第一次是无穷肯定会更新)</code>，第一次就结束了</li><li>从队列中抛出距离最近的那个点B（第一次就是0周围邻居）。这个点距离一定是最近的（所有权值都是正的，点的距离只能越来越长。）标记这个点为true，<code>并且将这个点的邻居加入队列</code>(下一次确定的最短点在前面未确定和这个点邻居中产生),并更新通过B点计算各个位置的<br>长度，如果小于则更新！<br><img src="/img/short-path/dijkstra1.png" alt="如图"></li><li>重复二的操作，直到所有点都确定。<br><img src="/img/short-path/dijkstra2.png" alt="如图"></li></ul><p><a class="btn" href="https://www.acwing.com/problem/content/description/852/" title="title">参考题目</a></p><p>C++版本：</p><pre><code class="hljs C++"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;cstring&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;algorithm&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;queue&gt;</span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;<span class="hljs-keyword">typedef</span> <span class="hljs-built_in">pair</span>&lt;<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>&gt; PII;<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> N = <span class="hljs-number">1e6</span> + <span class="hljs-number">10</span>;<span class="hljs-keyword">int</span> n, m;<span class="hljs-keyword">int</span> h[N], w[N], e[N], ne[N], idx;<span class="hljs-keyword">int</span> dist[N], heap[N];<span class="hljs-keyword">bool</span> st[N];<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b, <span class="hljs-keyword">int</span> c)</span></span><span class="hljs-function"></span>&#123;    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;&#125;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dijkstra</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>&#123;    <span class="hljs-built_in">memset</span>(dist, <span class="hljs-number">0x3f</span>, <span class="hljs-keyword">sizeof</span> dist);    dist[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>;    <span class="hljs-built_in">priority_queue</span>&lt;PII, <span class="hljs-built_in">vector</span>&lt;PII&gt;, greater&lt;PII&gt;&gt; heap;    heap.push(&#123;<span class="hljs-number">0</span>, <span class="hljs-number">1</span>&#125;);    <span class="hljs-keyword">while</span> (heap.size())    &#123;        PII t = heap.top();        heap.pop();        <span class="hljs-keyword">int</span> ver = t.second; <span class="hljs-comment">//distance = t.first;</span>        <span class="hljs-keyword">if</span> (st[ver]) <span class="hljs-keyword">continue</span>;        st[ver] = <span class="hljs-literal">true</span>;        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = h[ver]; i != <span class="hljs-number">-1</span>; i = ne[i])        &#123;            <span class="hljs-keyword">int</span> j = e[i];            <span class="hljs-keyword">if</span> (dist[j] &gt; dist[ver] + w[i])            &#123;                dist[j] = dist[ver] + w[i];                heap.push(&#123;dist[j], j&#125;);            &#125;        &#125;    &#125;    <span class="hljs-keyword">if</span> (dist[n] == <span class="hljs-number">0x3f3f3f3f</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;    <span class="hljs-keyword">return</span> dist[n];&#125;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><span class="hljs-function"></span>&#123;    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d&quot;</span>, &amp;n, &amp;m);    <span class="hljs-built_in">memset</span>(h, <span class="hljs-number">-1</span>, <span class="hljs-keyword">sizeof</span> h);    <span class="hljs-keyword">while</span> (m -- )    &#123;        <span class="hljs-keyword">int</span> a, b, c;        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d%d&quot;</span>, &amp;a, &amp;b, &amp;c);        add(a, b, c);    &#125;    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d\n&quot;</span>, dijkstra());    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;&#125;</code></pre><p>Pyhon版本：</p><pre><code class="hljs Python"><span class="hljs-keyword">import</span> heapq<span class="hljs-keyword">import</span> sys<span class="hljs-comment"># 优先队列</span><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PriorityQueue</span>:</span>      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>        self._queue = []        self._index = <span class="hljs-number">0</span>    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">push</span>(<span class="hljs-params">self, item, priority</span>):</span>        <span class="hljs-comment"># 传入两个参数，一个是存放元素的数组，另一个是要存储的元素，这里是一个元组。</span>        <span class="hljs-comment"># 由于heap内部默认由小到大排，所以对priority取负数</span>        heapq.heappush(self._queue, (-priority, self._index, item))        self._index += <span class="hljs-number">1</span>    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pop</span>(<span class="hljs-params">self</span>):</span>        <span class="hljs-keyword">return</span> heapq.heappop(self._queue)[-<span class="hljs-number">1</span>]    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">empty</span>(<span class="hljs-params">self</span>):</span>        <span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(self._queue) == <span class="hljs-number">0</span>que = PriorityQueue()INF = sys.maxsizeedges = [[], [[<span class="hljs-number">2</span>, <span class="hljs-number">7</span>], [<span class="hljs-number">3</span>, <span class="hljs-number">9</span>], [<span class="hljs-number">6</span>, <span class="hljs-number">14</span>]], [[<span class="hljs-number">1</span>, <span class="hljs-number">7</span>], [<span class="hljs-number">3</span>, <span class="hljs-number">10</span>], [<span class="hljs-number">4</span>, <span class="hljs-number">15</span>]], [[<span class="hljs-number">1</span>, <span class="hljs-number">9</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">10</span>], [<span class="hljs-number">6</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">4</span>, <span class="hljs-number">11</span>]], [[<span class="hljs-number">3</span>, <span class="hljs-number">11</span>], [<span class="hljs-number">5</span>, <span class="hljs-number">6</span>]], [[<span class="hljs-number">4</span>, <span class="hljs-number">6</span>], [<span class="hljs-number">6</span>, <span class="hljs-number">9</span>]], [[<span class="hljs-number">3</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">5</span>, <span class="hljs-number">9</span>]]] <span class="hljs-comment"># 邻接表存储边</span>dis = [sys.maxsize <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">8</span>)] <span class="hljs-comment"># 记录s到其他点的距离</span>s = <span class="hljs-number">1</span>que.push(s, <span class="hljs-number">0</span>)dis[s] = <span class="hljs-number">0</span>visited = &#123;&#125;<span class="hljs-keyword">while</span> <span class="hljs-keyword">not</span> que.empty():    u, d = que.pop()    <span class="hljs-keyword">if</span> d != dis[u]:        <span class="hljs-keyword">continue</span>    <span class="hljs-keyword">for</span> v, l <span class="hljs-keyword">in</span> edges[u]:        <span class="hljs-keyword">if</span> dis[u] + l &lt; dis[v]:            dis[v] = dis[u] + l            que.push(v, dis[v])print(dis)</code></pre><p>Java版本：<br><pre><code class="hljs Java"><span class="hljs-keyword">import</span> java.util.ArrayDeque;<span class="hljs-keyword">import</span> java.util.Comparator;<span class="hljs-keyword">import</span> java.util.PriorityQueue;<span class="hljs-keyword">import</span> java.util.Queue;<span class="hljs-keyword">import</span> java.util.Scanner;<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">dijkstra</span> </span>&#123;<span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">node</span></span><span class="hljs-class"></span>&#123;<span class="hljs-keyword">int</span> x; <span class="hljs-comment">//节点编号</span><span class="hljs-keyword">int</span> lenth;<span class="hljs-comment">//长度</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">node</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x,<span class="hljs-keyword">int</span> lenth)</span> </span>&#123;<span class="hljs-keyword">this</span>.x=x;<span class="hljs-keyword">this</span>.lenth=lenth;&#125;&#125;<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123; <span class="hljs-keyword">int</span>[][] map = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">6</span>][<span class="hljs-number">6</span>];<span class="hljs-comment">//记录权值，顺便记录链接情况，可以考虑附加邻接表</span>initmap(map);<span class="hljs-comment">//初始化</span><span class="hljs-keyword">boolean</span> bool[]=<span class="hljs-keyword">new</span> <span class="hljs-keyword">boolean</span>[<span class="hljs-number">6</span>];<span class="hljs-comment">//判断是否已经确定</span><span class="hljs-keyword">int</span> len[]=<span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">6</span>];<span class="hljs-comment">//长度</span><span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i&lt;<span class="hljs-number">6</span>;i++)&#123;len[i]=Integer.MAX_VALUE;&#125;Queue&lt;node&gt;q1=<span class="hljs-keyword">new</span> PriorityQueue&lt;node&gt;(com);len[<span class="hljs-number">0</span>]=<span class="hljs-number">0</span>;<span class="hljs-comment">//从0这个点开始</span>q1.add(<span class="hljs-keyword">new</span> node(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>));<span class="hljs-keyword">int</span> count=<span class="hljs-number">0</span>;<span class="hljs-comment">//计算执行了几次dijkstra</span><span class="hljs-keyword">while</span> (!q1.isEmpty()) &#123;node t1=q1.poll();<span class="hljs-keyword">int</span> index=t1.x;<span class="hljs-comment">//节点编号</span><span class="hljs-keyword">int</span> length=t1.lenth;<span class="hljs-comment">//节点当前点距离</span>bool[index]=<span class="hljs-keyword">true</span>;<span class="hljs-comment">//抛出的点确定</span>count++;<span class="hljs-comment">//其实执行了6次就可以确定就不需要继续执行了  这句可有可无，有了减少计算次数</span><span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i&lt;map[index].length;i++)&#123;<span class="hljs-keyword">if</span>(map[index][i]&gt;<span class="hljs-number">0</span>&amp;&amp;!bool[i])&#123;node node=<span class="hljs-keyword">new</span> node(i, length+map[index][i]);<span class="hljs-keyword">if</span>(len[i]&gt;node.lenth)<span class="hljs-comment">//需要更新节点的时候更新节点并加入队列</span>&#123;len[i]=node.lenth;q1.add(node);&#125;&#125;&#125;&#125;<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i&lt;<span class="hljs-number">6</span>;i++)&#123;System.out.println(len[i]);&#125;&#125;<span class="hljs-keyword">static</span> Comparator&lt;node&gt;com=<span class="hljs-keyword">new</span> Comparator&lt;node&gt;() &#123;<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(node o1, node o2)</span> </span>&#123;<span class="hljs-keyword">return</span> o1.lenth-o2.lenth;&#125;&#125;;<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initmap</span><span class="hljs-params">(<span class="hljs-keyword">int</span>[][] map)</span> </span>&#123;map[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]=<span class="hljs-number">2</span>;map[<span class="hljs-number">0</span>][<span class="hljs-number">2</span>]=<span class="hljs-number">3</span>;map[<span class="hljs-number">0</span>][<span class="hljs-number">3</span>]=<span class="hljs-number">6</span>;map[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]=<span class="hljs-number">2</span>;map[<span class="hljs-number">1</span>][<span class="hljs-number">4</span>]=<span class="hljs-number">4</span>;map[<span class="hljs-number">1</span>][<span class="hljs-number">5</span>]=<span class="hljs-number">6</span>;map[<span class="hljs-number">2</span>][<span class="hljs-number">0</span>]=<span class="hljs-number">3</span>;map[<span class="hljs-number">2</span>][<span class="hljs-number">3</span>]=<span class="hljs-number">2</span>;map[<span class="hljs-number">3</span>][<span class="hljs-number">0</span>]=<span class="hljs-number">6</span>;map[<span class="hljs-number">3</span>][<span class="hljs-number">2</span>]=<span class="hljs-number">2</span>;map[<span class="hljs-number">3</span>][<span class="hljs-number">4</span>]=<span class="hljs-number">1</span>;map[<span class="hljs-number">3</span>][<span class="hljs-number">5</span>]=<span class="hljs-number">3</span>;map[<span class="hljs-number">4</span>][<span class="hljs-number">1</span>]=<span class="hljs-number">4</span>;map[<span class="hljs-number">4</span>][<span class="hljs-number">3</span>]=<span class="hljs-number">1</span>;map[<span class="hljs-number">5</span>][<span class="hljs-number">1</span>]=<span class="hljs-number">6</span>;map[<span class="hljs-number">5</span>][<span class="hljs-number">3</span>]=<span class="hljs-number">3</span>;&#125;&#125;</code></pre></p><p>&emsp;&emsp;这里用visited来判断是否之前访问过的主要目的是为了防止负环的产生，这样程序会陷入死循环，如果确定程序不存在负边的话，其实可以没必要判断。因为先出队列的一定更优，不会存在之后还被更新的情况。如果想不明白这点加上判断也没有关系。<br>&emsp;&emsp;我们最后分析一下复杂度，每个点最多用来松弛其他点一次，加上优先队列的调整耗时，整体的复杂度是$O(V \log V+E)$，比之前$O(V^2+E)$的复杂度要提速了很多，非常适合边很多，点相对较少的图。有时候spfa卡时间了，我们会选择Dijkstra。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最短路径算法&lt;/p&gt;</summary>
    
    
    
    <category term="图论" scheme="https://www.wulnut.top/categories/%E5%9B%BE%E8%AE%BA/"/>
    
    
    <category term="C++" scheme="https://www.wulnut.top/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>最短路径之bellman-ford&amp;SPFA</title>
    <link href="https://www.wulnut.top/2020/09/17/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84(1)/"/>
    <id>https://www.wulnut.top/2020/09/17/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84(1)/</id>
    <published>2020-09-17T10:33:31.039Z</published>
    <updated>2020-09-27T15:58:46.618Z</updated>
    
    <content type="html"><![CDATA[<p>最短路径算法</p><a id="more"></a><h1 id="Start"><a href="#Start" class="headerlink" title="Start"></a>Start</h1><p>&emsp;&emsp;最短路问题也属于图论算法之一，解决的是在一张有向图当中点与点之间的最短距离问题。最短路算法有很多，比较常用的有bellman-ford、dijkstra、floyd、spfa等等。这些算法当中主要可以分成两个分支，其中一个是<br>bellman-ford及其衍生出来的spfa，另外一个分支是dijkstra以及其优化版本。floyd复杂度比较高，一般不太常用。<br>&emsp;&emsp;这次就先简单说一下bellman-ford和SPFA算法。</p><h1 id="First"><a href="#First" class="headerlink" title="First"></a>First</h1><h2 id="存图"><a href="#存图" class="headerlink" title="存图"></a>存图</h2><p>我们要对一张有向图计算最短路，那么我们首先要做的就是将一张图存储下来。关于图的存储的数据结构，常用的方法有很多种。最简单的是邻接矩阵，所谓的邻接矩阵就是用一个二维矩阵存储每两个点之间的距离。如果两个点之间没有边相连，那么设为无穷大。</p><p><img src="/img/short-path/save-Graph.png" alt="如图"></p><p>&emsp;&emsp;这种方法的好处就是<code>非常直观</code>，实现也很简单，但是这中方法所消耗的时间复杂度也是很高的 $V^2$,这里的V指的是顶点的数量当顶点的数量稍稍大一些之后，带来的开销是非常庞大的。一般情况下我们的图的边的密集程度是不高<br>的，也就是说大量点和点之间没有边相连，我们浪费了很多空间。<code>一般情况下当边是顶点的10倍时(也就是稠密图)我们选择这种存图方式,此外我们就选择邻接表的方式来存储稀疏图</code></p><p>&emsp;&emsp;所谓的邻接表也就是说我们把顶点一字排开存入数组当中，每个顶点对应一条链表。这条链表当中存储了这个点可以到达的其他点的信息。邻接表的好处是可以最大化利用空间，有多少条边存储多少信息。但是也有缺点，除了实现稍稍复<br>杂一点之外，另外一个明显的缺点就是我们<code>没办法直接判断两点之间是否有边存在</code>，必须要遍历链表才可以。除了邻接矩阵和邻接表之外，还有一些其他的数据结构可以完成图的存储。比如前向星、边集数组、链式前向星等等。这些数据结构并没有<br>比邻接表有质的突破，对于非算法竞赛同学来说，能够熟练用好邻接表也就足够了。</p><h2 id="bellman-ford算法"><a href="#bellman-ford算法" class="headerlink" title="bellman-ford算法"></a>bellman-ford算法</h2><p>&emsp;&emsp;刚才上面描述当中提到的算法除了floyd算法是计算的所有点对之间的最短距离之外，其他算法解决的都是单源点最短路的问题。所谓的单源点可以简单理解成单个的出发点，也就是说我们求的是从图上的一个点出发去往其他每个点的最短<br>距离。既然如此，我们的出发点以及到达点都是确定的，不确定的只是它们之间的距离而已。</p><p>&emsp;&emsp;为什么我们会将bellman-ford算法和dijkstra算法区分开呢？因为两者的底层逻辑是不同的，bellman-ford算法的底层逻辑是动态规划， 而dijkstra算法的底层逻辑是贪心。</p><p>&emsp;&emsp;bellman-ford算法的得名也和人有关，我们之前在介绍KMP算法的时候曾经说过。由于英文表意能力不强，所以很多算法和公式都是以人名来取名。bellman-ford是Richard Bellman 和 Lester Ford 分别发表的，实际上还有一个<br>叫Edward F. Moore也发表了这个算法，所以有的地方会称为是Bellman-Ford-Moore 算法。</p><p>&emsp;&emsp;算法的原理非常简单，利用了动态规划的思想来维护源点出发到各个点的最短距离。</p><p>&emsp;&emsp;它的核心思维是松弛，所谓的松弛可以理解成找到了更短的路径对原路径进行更新。对于一个有V个节点的有向图进行V-1轮松弛，从而找到源点到所有点的最短距离。</p><p>&emsp;&emsp;初始的时候我们会用一个数组记录源点到其他所有点的距离，对于与源点直接相连的点来说，这个距离就是它和源点的距离否则则是无穷大。对于第一轮松弛来说，我们寻找的是源点出发经过一个点到达其他点的最短距离。我们用s代表源<br>点，我们寻找的就是s经过点a到达点b，使得距离小于s直接到b的距离。</p><p>&emsp;&emsp;第二轮松弛就是寻找的s经过两个点到达第三个点的最短距离，同理，对于一个有V个点的图来说，两个点之间最多经过V-1个点，所以我们需要V-1轮松弛操作。</p><pre><code class="hljs python"><span class="hljs-keyword">for</span> (var i = <span class="hljs-number">0</span>; i &lt; n - <span class="hljs-number">1</span>; i++) &#123;    <span class="hljs-keyword">for</span> (var j = <span class="hljs-number">0</span>; j &lt; m; j++) &#123;//对m条边进行循环      var edge = edges[j];      // 松弛操作      <span class="hljs-keyword">if</span> (distance[edge.to] &gt; distance[edge.<span class="hljs-keyword">from</span>] + edge.weight )&#123;         distance[edge.to] = distance[edge.<span class="hljs-keyword">from</span>] + edge.weight;      &#125;    &#125;&#125;</code></pre><p>&emsp;&emsp;Bellman-ford的算法很好理解，实现也不难，但是它有一个缺点就是复杂度很高。我们前面说了一共需要V-1轮松弛，每一轮松弛我们都需要遍历E条边，所以整体的复杂度是$O(VE)$，E指的是边的数量。想想看，假设对于一个有1w个顶点，10w条边的<br>图来说，这个算法是显然无法得出结果的。</p><p>&emsp;&emsp;所以为了提高算法的可用性，我们必须对这个算法进行优化。我们来分析一下复杂度巨大的原因，主要在两个地方，一个地方是我们松弛了V-1次，另外一个地方是我们枚举了所有的边。松弛V-1次是不可避免的，因为可能存在极端的情况需要V-1次松弛<br>才可以达成。但我们每次都枚举了所有的边感觉有点浪费，因为其中大部分的边是不可能达成新的松弛的。那有没有办法我们筛选出来可能构成新的松弛的边呢？</p><p>&emsp;&emsp;针对这个问题的思考和优化引出了新的算法——spfa。</p><h2 id="SPFA算法"><a href="#SPFA算法" class="headerlink" title="SPFA算法"></a>SPFA算法</h2><p>&emsp;&emsp;SPFA算法的英文全称是Shortest Path Faster Algorithm，从名字上我们就看得出来这个算法的最大特点就是快。它比Bellman-ford要快上许多倍，它的复杂度是$O(kE)$，这里的k是一个小于等于2的常数。</p><p>&emsp;&emsp;SPFA的核心原理和Bellman-ford算法是一样的，也是对点的松弛。只不过它优化了复杂度，优化的方法也很简单，用一个队列维护了可能存在新的松弛的点。这样我们每次从这些点出发去寻找其他可以松弛的点加入队列，这里面的原理<br>很简单，只有被松弛过的点才有可能去松弛其他的点。</p><p>&emsp;&emsp;SPFA的代码也很短，实现起来难度很低，单单从代码上来看和普通的宽搜区别并不大。</p><p>Python版本：<br><pre><code class="hljs Python"><span class="hljs-keyword">import</span> sys<span class="hljs-keyword">from</span> queue <span class="hljs-keyword">import</span> Queueque = Queue()<span class="hljs-comment"># 邻接表存储边</span>edges = [[]]<span class="hljs-comment"># 维护是否在队列当中</span>visited = [<span class="hljs-literal">False</span> <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(V)]dis = [sys.maxsize <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(V)]dis[s] = <span class="hljs-number">0</span>que.put(s)visited[s] = <span class="hljs-literal">True</span><span class="hljs-keyword">while</span> <span class="hljs-keyword">not</span> que.emtpy():    u = que.get()    <span class="hljs-keyword">for</span> v, l <span class="hljs-keyword">in</span> edges[u]:        <span class="hljs-keyword">if</span> dis[u] + l &lt; dis[v]:            dis[v] = dis[u] + l            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> visited[v]:                que.add(v)                visited[v] = <span class="hljs-literal">True</span>                    visited[u] = <span class="hljs-literal">False</span></code></pre></p><p><a class="btn" href="https://www.acwing.com/problem/content/853/" title="title">参考题目</a></p><p>C++版本：<br><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;queue&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;cstdio&gt;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;cstring&gt;</span></span><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> N = <span class="hljs-number">100010</span>;<span class="hljs-keyword">int</span> n, m;<span class="hljs-keyword">int</span> h[N], e[N], ne[N], w[N], idx;<span class="hljs-keyword">int</span> dis[N];<span class="hljs-keyword">bool</span> st[N];<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b, <span class="hljs-keyword">int</span> c)</span> </span>&#123;    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;&#125;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">spfa</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-built_in">queue</span>&lt;<span class="hljs-keyword">int</span>&gt; q;    q.push(<span class="hljs-number">1</span>);    <span class="hljs-built_in">memset</span>(dis, <span class="hljs-number">0x3f</span>, <span class="hljs-keyword">sizeof</span> dis);    dis[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>;    st[<span class="hljs-number">1</span>] = <span class="hljs-literal">true</span>;        <span class="hljs-keyword">while</span> ( !q.empty() ) &#123;        <span class="hljs-keyword">int</span> t = q.front();        q.pop();                st[t] = <span class="hljs-literal">false</span>;                <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = h[t]; i != <span class="hljs-number">-1</span>; i = ne[i]) &#123;            <span class="hljs-keyword">int</span> j = e[i];                        <span class="hljs-keyword">if</span> (dis[j] &gt; dis[t] + w[i]) &#123;                dis[j] = dis[t] + w[i];                st[j] = <span class="hljs-literal">true</span>;                q.push(j);            &#125;        &#125;    &#125;        <span class="hljs-keyword">if</span> (dis[n] == <span class="hljs-number">0x3f3f3f3f</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;    <span class="hljs-keyword">else</span> <span class="hljs-keyword">return</span> dis[n];    &#125;<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d&quot;</span>, &amp;n, &amp;m);    <span class="hljs-built_in">memset</span>(h, <span class="hljs-number">-1</span>, <span class="hljs-keyword">sizeof</span> h);        <span class="hljs-keyword">while</span> ( m -- ) &#123;        <span class="hljs-keyword">int</span> a, b, c;        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">&quot;%d%d%d&quot;</span>, &amp;a, &amp;b, &amp;c);        add(a, b, c);    &#125;        <span class="hljs-keyword">int</span> t = spfa();        <span class="hljs-keyword">if</span> (t == <span class="hljs-number">-1</span>) <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;impossible&quot;</span>);    <span class="hljs-keyword">else</span> <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%d&quot;</span>, t);&#125;</code></pre></p><p>Python版；<br><pre><code class="hljs python">n, m, k = <span class="hljs-built_in">map</span>(<span class="hljs-built_in">int</span>, <span class="hljs-built_in">input</span>().split())<span class="hljs-comment">## 用一个list 记录所有边的信息即可</span>g = []dist = [<span class="hljs-built_in">float</span>(<span class="hljs-string">&quot;inf&quot;</span>) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(n+<span class="hljs-number">1</span>)]<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(m):    a, b, w = <span class="hljs-built_in">map</span>(<span class="hljs-built_in">int</span>, <span class="hljs-built_in">input</span>().split())    g.append([a, b, w])<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bellman_ford</span>():</span>    dist[<span class="hljs-number">1</span>] = <span class="hljs-number">0</span>    <span class="hljs-comment">## 循环几次代表最多用几条边</span>    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(k):        <span class="hljs-comment">## 必须backup 防止在过程中会更新dist里面的数据导致并不是最多k条边</span>        backup = dist[:]        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(m):            a, b, w = g[i][<span class="hljs-number">0</span>], g[i][<span class="hljs-number">1</span>], g[i][<span class="hljs-number">2</span>]            dist[b] = <span class="hljs-built_in">min</span>(dist[b], backup[a] + w)bellman_ford()print(dist[n] <span class="hljs-keyword">if</span> dist[n] != <span class="hljs-built_in">float</span>(<span class="hljs-string">&quot;inf&quot;</span>) <span class="hljs-keyword">else</span> <span class="hljs-string">&quot;impossible&quot;</span>)</code></pre></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最短路径算法&lt;/p&gt;</summary>
    
    
    
    <category term="图论" scheme="https://www.wulnut.top/categories/%E5%9B%BE%E8%AE%BA/"/>
    
    
    <category term="C++" scheme="https://www.wulnut.top/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>公告 | 🔊 置顶</title>
    <link href="https://www.wulnut.top/2020/09/15/%E7%BD%AE%E9%A1%B6/"/>
    <id>https://www.wulnut.top/2020/09/15/%E7%BD%AE%E9%A1%B6/</id>
    <published>2020-09-14T16:00:00.000Z</published>
    <updated>2023-02-01T09:04:33.617Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;</p><a id="more"></a><style>section {    width: 100%;    height: 100px;    /*border: 1px solid red;*/    display: flex;    justify-content: space-around;    align-items: center;}section .section_item {    /*border: 1px solid blue;*/    position: relative;    display: flex;    justify-content: center;    align-items: center;    transition: filter 0.5s, transform 0.5s;}section .section_item .a {    color: #ffffff;    font-size: 2vw;    top: 0;    opacity: 1;    transition: top 0.5s, opacity 0.5s;}section .section_item .a:nth-child(1) {    position: absolute;    clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 0% 50%);}section .section_item .a:nth-child(2) {    position: relative;    clip-path: polygon(0% 50%, 100% 50%, 100% 100%, 0% 100%);}/*出发景深效果*/section:hover .section_item {    filter: blur(10px);    transform: scale(0.8);    transition: filter 0.5s, transform 0.5s;}/*对应取消景深效果*/section .section_item:hover {    filter: blur(0px);    transform: scale(1.1);    transition: filter 0.5s, transform 0.5s;}section .section_item:hover .a:nth-child(1) {    top: -40px;    opacity: 0.5;    transition: top 0.5s, opacity 0.5s;}section .section_item:hover .a:nth-child(2) {    top: 40px;    opacity: 0.5;    transition: top 0.5s, opacity 0.5s;}section .section_item a {    position: absolute;    color: #fff;    text-decoration: none;    opacity: 0;    transition: opacity 0.5s;}section .section_item a:hover {    text-decoration: underline;}section .section_item:hover a {    opacity: 1;    transition: opacity 0.5s;}.Photo img{    border-radius:25px;    box-shadow: 10px 10px 10px rgba(0,0,0,.5);    -moz-box-shadow: 10px 10px 10px rgba(0,0,0,.5) outset;    -webkit-box-shadow: 10px 10px 10px rgba(0,0,0,.5);}</style><p><div class = "Photo"></p><center>    <p class="count"></p>    <img src = "https://fastly.jsdelivr.net/gh/Wulnut/Figure/img/Top-2.jpg" alt = "photo" /></center><br><br><br><center><!-- 生老病死，衣食住行，这是我的奋斗，也是你的奋斗 -->一旦你决定好职业，你必须全心投入工作中，你必须爱你的工作，千万不要有怨言，你必须穷尽一生磨练技能，这就是成功的秘诀，也是让大家尊敬你的关键。</center>]]></content>
    
    
    <summary type="html">&lt;p&gt;&amp;emsp;&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
</feed>
