<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://sethome2.github.io</id>
    <title>sethome的橱窗 个人博客</title>
    <updated>2024-10-27T17:21:14.028Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://sethome2.github.io"/>
    <link rel="self" href="https://sethome2.github.io/atom.xml"/>
    <subtitle>sethome的橱窗 个人博客</subtitle>
    <logo>https://sethome2.github.io/images/avatar.png</logo>
    <icon>https://sethome2.github.io/favicon.ico</icon>
    <rights>All rights reserved 2024, sethome的橱窗 个人博客</rights>
    <entry>
        <title type="html"><![CDATA[奇思妙想之超超临界机组蒸汽泄漏]]></title>
        <id>https://sethome2.github.io/post/qi-si-miao-xiang-zhi-chao-chao-lin-jie-ji-zu-shi-gu/</id>
        <link href="https://sethome2.github.io/post/qi-si-miao-xiang-zhi-chao-chao-lin-jie-ji-zu-shi-gu/">
        </link>
        <updated>2024-10-27T11:08:48.000Z</updated>
        <content type="html"><![CDATA[<p>我曾经无聊的时候看过一个日本动画电影《蒸汽男孩 スチームボーイ》，其中背景设定有个非常有意思的点，人们采用__高温高压__的蒸汽作为动力。如果里面的蒸汽泄露到外面，就会瞬间生成冰晶，制造出恐怖而美丽的冰刺或者雾气。</p>
<p>这样的设定有没有科学依据呢？我们手持油漆罐，防晒喷雾喷涂的时候，也会感觉到手冰凉冰凉的，气体减压吸收热量，带来了凉意，所以这个设定是有一定道理的。</p>
<p>我想到了目前最常见高温高压的场合，超超临界机组内部会存在着最大30MPa，温度约为600左右的高温蒸汽[1]。如果出现泄漏，会瞬间减压到大气压强度，即0.1MPa。</p>
<p>如果不涉及相变，整个过程应该很丝滑，单单理想气体状态方程就解决了。一旦考虑相变就太复杂了，超超临界机组里面的水是均匀相，此时此刻水会同时具备气体和液体的性质，减压降温就到气相了，不满足理想气体状态方程。水的相图如下所示。<br>
<img src="https://sethome2.github.io/post-images/1730048642553.png" alt="水的相图" loading="lazy"></p>
<p>为了验证动画的严谨性，那么就先假设350摄氏度（623.15开尔文），10MPa的高压蒸汽进行减压到大气压强度，看看原著是否满足这一条件。</p>
<p>利用理想气体状态方程<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>P</mi><mi>V</mi><mo>=</mo><mi>n</mi><mi>R</mi><mi>T</mi></mrow><annotation encoding="application/x-tex">PV = nRT</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault">n</span><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="mord mathdefault" style="margin-right:0.13889em;">T</span></span></span></span>，其中P是压力，V 是体积，n 是摩尔数， R是气体常数，<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>R</mi><mo>=</mo><mn>8.314</mn><mfrac><mi mathvariant="normal">J</mi><mrow><mi mathvariant="normal">K</mi><mo>⋅</mo><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">l</mi></mrow></mrow></mfrac></mrow><annotation encoding="application/x-tex">R = 8.314\frac{\mathrm{J}}{\mathrm{K}\cdot\mathrm{mol}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.217331em;vertical-align:-0.345em;"></span><span class="mord">8</span><span class="mord">.</span><span class="mord">3</span><span class="mord">1</span><span class="mord">4</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.872331em;"><span style="top:-2.6550000000000002em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathrm mtight">K</span></span><span class="mbin mtight">⋅</span><span class="mord mtight"><span class="mord mathrm mtight">m</span><span class="mord mathrm mtight">o</span><span class="mord mathrm mtight">l</span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathrm mtight">J</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>，T是温度（开尔文）。</p>
<p>假设1mol水从隔热等容容器中减压，计算前后的温度变化。可以得到方程组如下，其中下标1为起始状态，下标2为终态：<br>
<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>P</mi><mn>1</mn></msub><msub><mi>V</mi><mn>1</mn></msub><mo>=</mo><mi>n</mi><mi>R</mi><msub><mi>T</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">P_1V_1 = nRT_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord mathdefault">n</span><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br>
<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>P</mi><mn>2</mn></msub><msub><mi>V</mi><mn>2</mn></msub><mo>=</mo><mi>n</mi><mi>R</mi><msub><mi>T</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">P_2V_2 = nRT_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord mathdefault">n</span><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br>
对于等容过程，<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>V</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">V_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>始终等于<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>V</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">V_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，那么联立可得<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mfrac><msub><mi>P</mi><mn>1</mn></msub><msub><mi>T</mi><mn>1</mn></msub></mfrac><mo>=</mo><mfrac><msub><mi>P</mi><mn>2</mn></msub><msub><mi>T</mi><mn>2</mn></msub></mfrac></mrow><annotation encoding="application/x-tex">\frac{P_1}{T_1}=\frac{P_2}{T_2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3335309999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8884309999999999em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.13889em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.4101em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.13889em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.3335309999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8884309999999999em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.13889em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.4101em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.13889em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>，因此，<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>T</mi><mn>2</mn></msub><mo>=</mo><mfrac><mrow><mn>0.01</mn><mo>×</mo><mn>1</mn><msup><mn>0</mn><mn>6</mn></msup><mtext> </mtext><mrow><mi mathvariant="normal">P</mi><mi mathvariant="normal">a</mi></mrow><mo>×</mo><mn>623.15</mn><mtext> </mtext><mi mathvariant="normal">K</mi></mrow><mrow><mn>10</mn><mo>×</mo><mn>1</mn><msup><mn>0</mn><mn>6</mn></msup><mtext> </mtext><mrow><mi mathvariant="normal">P</mi><mi mathvariant="normal">a</mi></mrow></mrow></mfrac><mo>=</mo><mn>6.23</mn><mtext> </mtext><mi mathvariant="normal">K</mi></mrow><annotation encoding="application/x-tex">T_2=\frac{0.01\times10^{6}\,\mathrm{Pa}\times623.15\,\mathrm{K}}{10\times10^{6}\,\mathrm{Pa}}=6.23\,\mathrm{K}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">T</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.4212509999999998em;vertical-align:-0.403331em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.01792em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mord mtight">0</span><span class="mbin mtight">×</span><span class="mord mtight">1</span><span class="mord mtight"><span class="mord mtight">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7463142857142857em;"><span style="top:-2.786em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">6</span></span></span></span></span></span></span></span></span><span class="mspace mtight" style="margin-right:0.19516666666666668em;"></span><span class="mord mtight"><span class="mord mathrm mtight">P</span><span class="mord mathrm mtight">a</span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mord mtight">.</span><span class="mord mtight">0</span><span class="mord mtight">1</span><span class="mbin mtight">×</span><span class="mord mtight">1</span><span class="mord mtight"><span class="mord mtight">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913142857142857em;"><span style="top:-2.931em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">6</span></span></span></span></span></span></span></span></span><span class="mspace mtight" style="margin-right:0.19516666666666668em;"></span><span class="mord mtight"><span class="mord mathrm mtight">P</span><span class="mord mathrm mtight">a</span></span><span class="mbin mtight">×</span><span class="mord mtight">6</span><span class="mord mtight">2</span><span class="mord mtight">3</span><span class="mord mtight">.</span><span class="mord mtight">1</span><span class="mord mtight">5</span><span class="mspace mtight" style="margin-right:0.19516666666666668em;"></span><span class="mord mtight"><span class="mord mathrm mtight">K</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.403331em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord">6</span><span class="mord">.</span><span class="mord">2</span><span class="mord">3</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathrm">K</span></span></span></span></span>，6.23开尔文为-266.92摄氏度，因此动画中的情况是可以实现的！至少纸面上似乎是实现了。</p>
<p>但是现实中的泄漏肯定伴随着膨胀，实际肯定没那么低，也是难以计算的。现实生活中也有利用这种方案制冷的机构，常常用于航空发动机中给客舱制冷，或者是工业场景。当然空调也是其中一种。</p>
<p>但还是没有解决一开始的问题——超超临界机组的蒸汽泄漏了怎么办。毕竟这玩意可不能真的在现实漏了，还是得通过计算来解决，后续等我想起来再更新。</p>
<h1 id="参考文献">参考文献：</h1>
<ol>
<li>杨美, 周云龙, 杨金福, 王迪, 韩东江, &amp; 包佳鑫. (2021). 700℃ 超超临界一次再热发电系统优化. 发电技术, 42(4), 509.</li>
</ol>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[PX4&Ardupoilt鼓捣的碎碎念]]></title>
        <id>https://sethome2.github.io/post/px4andardupoilt-gu-dao-de-sui-sui-nian/</id>
        <link href="https://sethome2.github.io/post/px4andardupoilt-gu-dao-de-sui-sui-nian/">
        </link>
        <updated>2024-10-27T11:08:17.000Z</updated>
        <content type="html"><![CDATA[<p>sada</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[齐鲁工业大学Adam战队2024年RM AWAR青工会分享]]></title>
        <id>https://sethome2.github.io/post/qi-lu-gong-ye-da-xue-adam-zhan-dui-2024-nian-rm-awar-qing-gong-hui-fen-xiang/</id>
        <link href="https://sethome2.github.io/post/qi-lu-gong-ye-da-xue-adam-zhan-dui-2024-nian-rm-awar-qing-gong-hui-fen-xiang/">
        </link>
        <updated>2024-09-10T12:04:31.000Z</updated>
        <content type="html"><![CDATA[<p>如下是所使用的PPT，后续文件（包括Simlink文件后续也会在这发布）<br>
<a href="https://blog.sethome.cc/Ver1.1%E6%9D%8E%E6%9D%B0%E7%9B%B8-12%E5%8F%B7%E9%9D%92%E5%B7%A5%E4%BC%9A-%E5%B5%8C%E5%85%A5%E5%BC%8F.pptx">PPT下载链接</a><br>
<a href="https://blog.sethome.cc/rmwarwd.slx">Matlab文件</a></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASC24 OpenCAEPoro Problem Solve Plan (Qilu University of Technology)]]></title>
        <id>https://sethome2.github.io/post/asc24-opencaeporo-problem-solve-plan-qilu-university-of-technology/</id>
        <link href="https://sethome2.github.io/post/asc24-opencaeporo-problem-solve-plan-qilu-university-of-technology/">
        </link>
        <updated>2024-06-02T15:59:06.000Z</updated>
        <content type="html"><![CDATA[<p>This is a translated essay from the original Chinese text in this blog.</p>
<p>The essay was finished one day after the end of ASC24, potentially exist with some inaccuracies due to my limited ablility. And this is the first time I try to write MPI program and HPC application. Should there be any inaccuracies, your understanding is greatly appreciated. While my previous areas of study may intersect only minimally with this realm, I warmly welcome any corrections and enhancements from all.</p>
<p>The background of OpenCAEPoro, I believe, requires no explain again. To cut to the chase, it has become apparent to all that the dgemm function stands out as a focal point. Enhancing this function could achieve notably effects, thereby constituting our primary optimization target. Besides, in OpenCAEPoro, the algorithm for searching left and right neighbors exhibits particularly high time complexity, predominantly residing in O(N) or O(N^2) time complexity, so this is one of our points for optimization. The least point is about PETSc, by optimized the memory access speed, decoup function, we can reach greater speed. Meanwhile, we are trying to use asynchronous computing, which allows the program to fully maximize both CPU computational performance and remote access capabilities.</p>
<p>Next, this is our score list in finial competition. (Because the IB verbs drive problem, we got longer time than we expect.)<br>
<img src="https://sethome2.github.io/post-images/1713172673923.JPG" alt="OpenCAEPoro成绩单" loading="lazy"><br>
Accelration ratio in preliminary competition is show below.<br>
<img src="https://sethome2.github.io/post-images/1713174821342.png" alt="初赛加速比" loading="lazy"></p>
<hr>
<h1 id="dgemm-algorithm">dgemm algorithm</h1>
<p>First and foremost is the dgemm algorithm. In the unofficial ASC communication group (a QQ group number: 775322734), an expert has provided what appears to be an optimal solution, a technological marvel from Intel. we dare not claim that our code is faster. Here is the direct link:<a href="https://www.intel.com/content/www/us/en/developer/articles/technical/onemkl-improved-small-matrix-performance-using-just-in-time-jit-code.html">https://www.intel.com/content/www/us/en/developer/articles/technical/onemkl-improved-small-matrix-performance-using-just-in-time-jit-code.html</a>。<br>
In contrast, our approach utilizing block caching and compile-time calculations with C++ templates may seem somewhat inadequate. Some may argue that why we do not employ the dgemm algorithm form MKL. Through our testing, we have observed that MKL performs poorly on small matrices. The matrices calculated in testcase1 within OpenCAEPoro are relatively small, possibly within the range of 20x20 (the exact size in the finial competition is unknown). Furthermore, I provide the code we referenced, which stems from a tutorial by an NVIDIA engineer, serving as an exemplary guide for HPC beginners:<a href="https://github.com/yzhaiustc/Optimizing-DGEMM-on-Intel-CPUs-with-AVX512F">https://github.com/yzhaiustc/Optimizing-DGEMM-on-Intel-CPUs-with-AVX512F</a><br>
In our rendition of the code, the matrix multiplication for small matrices offers an approximate 30% performance enhancement compared to the original version by the author (due to certain scaling operations not being compiled).</p>
<pre><code class="language-cpp">#define A(i, j) A[(i) * LDA + (j)]
#define B(i, j) B[(i) * LDB + (j)]
#define C(i, j) C[(i) * LDC + (j)]
#include &quot;immintrin.h&quot;
#include &lt;stdint.h&gt;
#include &lt;cstring&gt;

void scale_c_k4(double *C, int M, int N, int LDC, double scalar)
{
    int i, j;
    for (i = 0; i &lt; M; i++)
    {
        for (j = 0; j &lt; N; j++)
        {
            C(i, j) *= scalar;
        }
    }
}

void mydgemm_cpu_opt_k4(int M, int N, int K, double alpha, double *A, int LDA, double *B, int LDB, double beta, double *C, int LDC)
{
    int i, j, k;
    if (beta != 1.0)
        scale_c_k4(C, M, N, LDC, beta);
    for (i = 0; i &lt; M; i++)
    {
        for (j = 0; j &lt; N; j++)
        {
            double tmp = C(i, j);
            for (k = 0; k &lt; K; k++)
            {
                tmp += alpha * A(i, k) * B(k, j);
            }
            C(i, j) = tmp;
        }
    }
}

template &lt;typename BETA, typename ALPHA&gt;
void mydgemm_cpu_v4(int M, int N, int K, ALPHA alpha, double *A, int LDA, double *B, int LDB, BETA beta, double *C, int LDC)
{
    int i, j, k;
    if constexpr (beta == 0) // optimize for beta == 0
        memset(C, 0, sizeof(double) * M * N);
    else
    {
        if constexpr (beta != 1.0)
            scale_c_k4(C, M, N, LDC, beta);
    }

    int M4 = M &amp; -4, N4 = N &amp; -4;
    for (i = 0; i &lt; M4; i += 4)
    {
        for (j = 0; j &lt; N4; j += 4)
        {
            double c00 = C(i, j);
            double c01 = C(i, j + 1);
            double c02 = C(i, j + 2);
            double c03 = C(i, j + 3);
            double c10 = C(i + 1, j);
            double c11 = C(i + 1, j + 1);
            double c12 = C(i + 1, j + 2);
            double c13 = C(i + 1, j + 3);
            double c20 = C(i + 2, j);
            double c21 = C(i + 2, j + 1);
            double c22 = C(i + 2, j + 2);
            double c23 = C(i + 2, j + 3);
            double c30 = C(i + 3, j);
            double c31 = C(i + 3, j + 1);
            double c32 = C(i + 3, j + 2);
            double c33 = C(i + 3, j + 3);
            for (k = 0; k &lt; K; k++)
            {
                double a0, a1, a2, a3;
                if constexpr (alpha != 1.0)
                {
                    a0 = alpha * A(i, k);
                    a1 = alpha * A(i + 1, k);
                    a2 = alpha * A(i + 2, k);
                    a3 = alpha * A(i + 3, k);
                }
                else
                {
                    a0 = A(i, k);
                    a1 = A(i + 1, k);
                    a2 = A(i + 2, k);
                    a3 = A(i + 3, k);
                }

                double b0 = B(k, j);
                double b1 = B(k, j + 1);
                double b2 = B(k, j + 2);
                double b3 = B(k, j + 3);
                c00 += a0 * b0;
                c01 += a0 * b1;
                c02 += a0 * b2;
                c03 += a0 * b3;
                c10 += a1 * b0;
                c11 += a1 * b1;
                c12 += a1 * b2;
                c13 += a1 * b3;
                c20 += a2 * b0;
                c21 += a2 * b1;
                c22 += a2 * b2;
                c23 += a2 * b3;
                c30 += a3 * b0;
                c31 += a3 * b1;
                c32 += a3 * b2;
                c33 += a3 * b3;
            }
            C(i, j) = c00;
            C(i, j + 1) = c01;
            C(i, j + 2) = c02;
            C(i, j + 3) = c03;
            C(i + 1, j) = c10;
            C(i + 1, j + 1) = c11;
            C(i + 1, j + 2) = c12;
            C(i + 1, j + 3) = c13;
            C(i + 2, j) = c20;
            C(i + 2, j + 1) = c21;
            C(i + 2, j + 2) = c22;
            C(i + 2, j + 3) = c23;
            C(i + 3, j) = c30;
            C(i + 3, j + 1) = c31;
            C(i + 3, j + 2) = c32;
            C(i + 3, j + 3) = c33;
        }
    }
    if (M4 == M &amp;&amp; N4 == N)
        return;
    // boundary conditions
    if (M4 != M)
        mydgemm_cpu_opt_k4(M - M4, N, K, alpha, A + M4, LDA, B, LDB, 1.0, &amp;C(M4, 0), LDC);
    if (N4 != N)
        mydgemm_cpu_opt_k4(M4, N - N4, K, alpha, A, LDA, &amp;B(0, N4), LDB, 1.0, &amp;C(0, N4), LDC);
}

</code></pre>
<h1 id="optimize-for-searching-neighbor">Optimize for searching neighbor</h1>
<p>Vtune Profile can not give useful infomation expect the dgemm algorithm is a hotpot. Fortunately, the code being relatively compact that allows us to initiate optimization efforts from the most time-consuming areas. In the Partition section, we found a weird code in the partion of search and construct connections between different components. This approach, accomplished through array traversal, results in the need to run 24 times to obtain connection information for finding left and right children in the 2x2x2 components. Is it feasible to employ a hash table?</p>
<p>Subsequently, the two individuals responsible for this competition topic diligently inspected the source code for two weeks and eventually uncovered areas amenable to straightforward optimization. Found within the SetDistribution function in the Partition.cpp file, the following represents a mere excerpt of the code, with two instances suitable for such modifications. In this section, the performance was enhanced by a factor of 7.</p>
<pre><code class="language-cpp">/// Get elements and their neighbors belonging to current process,
	/// Also, process of them are needed.
	/// Setup elements those will be sent to corresponding process
	//  target process, num of elements, num of edges, global index of elements, xadj, adjncy, adjproc
	vector&lt;vector&lt;idx_t&gt;&gt;  send_buffer;
	vector&lt;vector&lt;idx_t&gt;&gt;&amp; recv_buffer = elementCSR;
	recv_buffer.push_back(vector&lt;idx_t&gt; {myrank, 0, 0});

	// euphoria
	unordered_map&lt;idx_t, idx_t&gt; ump_send_buffer;

	// Calculate necessary memory first
	for (idx_t i = 0; i &lt; numElementLocal; i++) {
		if (part[i] == myrank) {
			recv_buffer[0][1] ++;
			recv_buffer[0][2] += (xadj[i + 1] - xadj[i]);
		}
		else {
			// euphoria
			if(ump_send_buffer[part[i]]){
				idx_t k = ump_send_buffer[part[i]] - 1;
				send_buffer[k][1] ++;
				send_buffer[k][2] += (xadj[i + 1] - xadj[i]);
			}
			else{
				send_buffer.push_back(vector&lt;idx_t&gt;{part[i], 1, xadj[i + 1] - xadj[i]});
				ump_send_buffer[part[i]] = send_buffer.size();
			}

			// idx_t k;
			// for (k = 0; k &lt; send_buffer.size(); k++) {
			// 	if (part[i] == send_buffer[k][0]) {
			// 		// existing process
			// 		send_buffer[k][1] ++;
			// 		send_buffer[k][2] += (xadj[i + 1] - xadj[i]);
			// 		break;
			// 	}
			// }
			// if (k == send_buffer.size()) {
			// 	// new process
			// 	send_buffer.push_back(vector&lt;idx_t&gt;{part[i], 1, xadj[i + 1] - xadj[i]});
			// }
		}
	}
</code></pre>
<h1 id="optimization-in-petsc-linersovler">Optimization in PETSc LinerSovler</h1>
<p>The first optimization we try was rewrite the decoup function. This is one of its subsidiary functions are show below. We modified the repeated memory deallocation operations to static memory, thereby alleviating the pressure on the memory pool from repeated allocate and free operations. Simultaneously, we allocated memory with alignment, thus improving access efficiency.</p>
<pre><code class="language-cpp">void inverse1(double *A, int N)
{
    static int *IPIV = static_cast&lt;int *&gt;(aligned_alloc(64, N * sizeof(int)));
    int LWORK = N * N;
    static int last_N = N;
    static double *WORK = static_cast&lt;double *&gt;(aligned_alloc(64, LWORK * sizeof(double)));
    int INFO;

    if (last_N &lt; N)
    {
        last_N = N;

        std::free(IPIV);
        std::free(WORK);

        IPIV = static_cast&lt;int *&gt;(aligned_alloc(64, N * sizeof(int)));
        WORK = static_cast&lt;double *&gt;(aligned_alloc(64, LWORK * sizeof(double)));
    }

    dgetrf_(&amp;N, &amp;N, A, &amp;N, IPIV, &amp;INFO);

    if (INFO &gt; 0)
    {
        printf(&quot;WARNING: The factorization has been completed, but the factor U is exactly singular&quot;);
    }
    else if (INFO &lt; 0)
    {
        printf(&quot;WARNING: There is an illegal value&quot;);
    }

    dgetri_(&amp;N, A, &amp;N, IPIV, WORK, &amp;LWORK, &amp;INFO);
}
</code></pre>
<p>Subsequently, we realized that the same approach could be applied to the entire LS solving function, starting from minor variables like &quot;args&quot; to relatively larger ones such as &quot;nNDCount&quot;. These modifications are estimated to yield a 10% enhancement in Object Time. Furthermore, during our documentation reading, we surprisely found PETSc's own implementation of memory pool operations, promising an additional 10% performance boost.<br>
<img src="https://sethome2.github.io/post-images/1713175347615.png" alt="PetSc内存操作" loading="lazy"></p>
<p>After consulting the PetSc documentation, we confirmed that variables such as Mat, Vector, and Ksp in PetSc could be reused. With this understanding, we converted them into static type variables, aiming for further performance gains. Unfortunately, we did not observe any significant improvements, speculating that the compiler might have concealed any potential enhancements through magical optimizations.</p>
<p>Within the solving function, we noticed functions like <code>MatAssemblyBegin</code> and <code>MatAssemblyEnd</code>, indicating asynchronous operations. Upon consulting the PETSc documentation, this was affirmed. Despite injecting computations into these areas, there were no discernible changes in Object Time, possibly due to the small scale of the test case. Similarly, drawing inspiration from this concept, we utilized C++ 20 features for asynchronous data copying during result transfer (While the operation is outside of Object Time, only for save time in finial competition).</p>
<p>Next, I would like to discuss some unsuccessful optimization attempts. We experimented with altering matrix indexing, matrix types, modifying preconditioning methods to accelerate least squares solving, and even attempting to accelerate linear solving processes using CUDA (though outside Object Time, serving to effectively reduce runtime during the finial competition). These endeavors were abandoned due to a lack of noticeable performance enhancements or computational errors. These endeavors consumed over half of our preparation time, yet regrettably did not yield any fruitful results.</p>
<h1 id="modify-file-readwrite-io-in-the-summary">Modify file read/write IO in the summary</h1>
<p>** Importance Notice: There is a bug in my implemented code. The logic is sound, but the main thread's output is corrupted, causing the SUMMAY.out file is disappear. I made this modification on my own and only discovered it during the competition. I take full responsibility and have already pleaded for forgiveness from my teammates. Unfortunately, due to this error, almost all cases were run twice during the final competition. I offer my sincere public apology for my mistake. I am truly sorry for my error. **</p>
<p>In OpenCAEPoro, during the summarization phase, the program writes &quot;SUMMAY_proc_xxx&quot; and &quot;Fast_proc_xxx&quot; files to the disk, which are then read and processed by the main thread to generate the final data. This approach limits scalability and may lead to performance degradation when dealing with hundreds of parallel cores. After reading the code, I decided to modify this section by avoiding disk writes and instead summarizing the data through MPI communication. This resulted in a 10% performance improvement, with even greater enhancements as the number of threads increases. It's estimated that with thousands of threads, the performance gains could reach several hundredfold.</p>
<p>The original process looked something like this:：<br>
<img src="https://sethome2.github.io/post-images/1713177578180.png" alt="原版汇总方法" loading="lazy"></p>
<p>I changed the process to have each thread send its data to the main thread by MPI Communication(first sending the length, then the content). As I am not very familiar with MPI writing, it seems that using the Gather method would be more appropriate.<br>
<img src="https://sethome2.github.io/post-images/1713177662038.png" alt="新版汇总方法" loading="lazy"></p>
<h1 id="other-little-optimizations">Other little Optimizations</h1>
<p>Some methods are come form OI competition.</p>
<ol>
<li>Input&amp;output acceleration<br>
Removing the synchronization with the C language library for input and output can enhance the I/O streams to reach the level of Printf:</li>
</ol>
<pre><code class="language-cpp">std::ios::sync_with_stdio(false);
std::cin.tie(0); 
std::cout.tie(0);
</code></pre>
<ol start="2">
<li>
<p>Preallocate space for the vector<br>
Origin code prefer not to preallocate space using the &quot;reserve&quot; function for vectors. However, some parts can be computed in advance to reserve an appropriate space. If calculated incorrectly, it can have a negative impact. By default, a vector automatically doubles its size when it needs to expand.</p>
</li>
<li>
<p>More beautiful acceleration ratio<br>
After multiple tests, it has been found that the fewer cores there are, the higher the acceleration ratio (higher communication and memory overhead). This code has an acceleration ratio of approximately 1.68 on a single 48 core machine.</p>
</li>
<li>
<p>Manual unfolding<br>
We manually expanded a portion of the loops in our code. When comparing the manually expanded code to the non-expanded code at the assembly level, the manually expanded code will be longer, but there is a performance improvement of 1-2% (which can be considered within the margin of error). We seek an explanation from experts as we ourselves do not have a clear conclusion.</p>
</li>
</ol>
<h1 id="self-assessment">Self-assessment</h1>
<p>If the input and output are not incorrect, I am satisfied with my own modifications. Any more advanced techniques are beyond my current capabilities and represent areas for further learning. This achievement is the result of the collaborative efforts of our team of five. While we believe we have done our best, we are all convinced that there are likely even better methods out there. We welcome the insights and corrections of experts in the field.</p>
<h1 id="perhaps-points-for-optimization-could-include">Perhaps points for optimization could include:</h1>
<ol>
<li>PetSc matrix types</li>
<li>PetSc matrix indexing</li>
<li>The reason for assembling the matrix twice in OpenCAEPoro? (Not certain if this is a valid point)</li>
</ol>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASC24 OpenCAEPoro 齐鲁工业大学优化方案]]></title>
        <id>https://sethome2.github.io/post/asc24-opencaeporo-ge-ren-andtuan-dui-you-hua-fang-an/</id>
        <link href="https://sethome2.github.io/post/asc24-opencaeporo-ge-ren-andtuan-dui-you-hua-fang-an/">
        </link>
        <updated>2024-04-13T16:30:12.000Z</updated>
        <content type="html"><![CDATA[<p>本文写于ASC24结束后一天，可能存在诸多错误，能力有限。同时这是我第一次接触MPI程序和HPC，还请谅解，之前学习的领域有重合但不多，非常欢迎大家指出错误斧正。</p>
<p>OpenCAEPoro的背景我想不需要赘述。开门见山的说，大家都发现了dgemm函数是一个热点，如果优化了这个函数，加速效果会非常明显，这也是我们所优化的地方。此外，CAE对左右邻居查找的算法时间复杂度特别高，基本上都是O(N)或O(N^2)的时间复杂度，这也是我们所优化的重点之一。最后PETSc有非常多可以优化调节的点，通过优化内存访问，decoup函数优化，我们可以达到更快的速度。同时合理利用异步计算，可以使程序同时榨干CPU运算性能和远端访问性能。</p>
<p>接下来，我们给出我们在决赛中的成绩作为参考，后续我会将初赛提交的文件上传到Github：<br>
<img src="https://sethome2.github.io/post-images/1713172673923.JPG" alt="OpenCAEPoro成绩单" loading="lazy"><br>
以下是初赛的加速比：<br>
<img src="https://sethome2.github.io/post-images/1713174821342.png" alt="初赛加速比" loading="lazy"></p>
<hr>
<h1 id="dgemm算法">dgemm算法</h1>
<p>首先是dgemm算法，ASC非官方交流群有大佬给出了应该是更优解，是Intel黑科技，我们不敢说我的代码更快，直达连接：<a href="https://www.intel.com/content/www/us/en/developer/articles/technical/onemkl-improved-small-matrix-performance-using-just-in-time-jit-code.html">https://www.intel.com/content/www/us/en/developer/articles/technical/onemkl-improved-small-matrix-performance-using-just-in-time-jit-code.html</a>。相比之下我们的利用block缓存和cpp temple编译期间计算的方法就显得有些无力。这里可能会有人好奇，为什么不使用MKL的dgemm算法呢？经过我们的测试，发现MKL在对小矩阵中性能不佳，CAE中testcase1所计算的矩阵是相当小的，20x20以内（比赛中的case不得而知）。同时，我们还给出我们参考的代码，是来自于英伟达工程师的小教程，简直就是HPC入门最好的指南：<a href="https://github.com/yzhaiustc/Optimizing-DGEMM-on-Intel-CPUs-with-AVX512F">https://github.com/yzhaiustc/Optimizing-DGEMM-on-Intel-CPUs-with-AVX512F</a><br>
在我们的这份代码，小矩阵乘法相比作者的原版，能提供约30%的性能提升（因为部分缩放操作不会被编译）。</p>
<pre><code class="language-cpp">#define A(i, j) A[(i) * LDA + (j)]
#define B(i, j) B[(i) * LDB + (j)]
#define C(i, j) C[(i) * LDC + (j)]
#include &quot;immintrin.h&quot;
#include &lt;stdint.h&gt;
#include &lt;cstring&gt;

void scale_c_k4(double *C, int M, int N, int LDC, double scalar)
{
    int i, j;
    for (i = 0; i &lt; M; i++)
    {
        for (j = 0; j &lt; N; j++)
        {
            C(i, j) *= scalar;
        }
    }
}

void mydgemm_cpu_opt_k4(int M, int N, int K, double alpha, double *A, int LDA, double *B, int LDB, double beta, double *C, int LDC)
{
    int i, j, k;
    if (beta != 1.0)
        scale_c_k4(C, M, N, LDC, beta);
    for (i = 0; i &lt; M; i++)
    {
        for (j = 0; j &lt; N; j++)
        {
            double tmp = C(i, j);
            for (k = 0; k &lt; K; k++)
            {
                tmp += alpha * A(i, k) * B(k, j);
            }
            C(i, j) = tmp;
        }
    }
}

template &lt;typename BETA, typename ALPHA&gt;
void mydgemm_cpu_v4(int M, int N, int K, ALPHA alpha, double *A, int LDA, double *B, int LDB, BETA beta, double *C, int LDC)
{
    int i, j, k;
    if constexpr (beta == 0) // optimize for beta == 0
        memset(C, 0, sizeof(double) * M * N);
    else
    {
        if constexpr (beta != 1.0)
            scale_c_k4(C, M, N, LDC, beta);
    }

    int M4 = M &amp; -4, N4 = N &amp; -4;
    for (i = 0; i &lt; M4; i += 4)
    {
        for (j = 0; j &lt; N4; j += 4)
        {
            double c00 = C(i, j);
            double c01 = C(i, j + 1);
            double c02 = C(i, j + 2);
            double c03 = C(i, j + 3);
            double c10 = C(i + 1, j);
            double c11 = C(i + 1, j + 1);
            double c12 = C(i + 1, j + 2);
            double c13 = C(i + 1, j + 3);
            double c20 = C(i + 2, j);
            double c21 = C(i + 2, j + 1);
            double c22 = C(i + 2, j + 2);
            double c23 = C(i + 2, j + 3);
            double c30 = C(i + 3, j);
            double c31 = C(i + 3, j + 1);
            double c32 = C(i + 3, j + 2);
            double c33 = C(i + 3, j + 3);
            for (k = 0; k &lt; K; k++)
            {
                double a0, a1, a2, a3;
                if constexpr (alpha != 1.0)
                {
                    a0 = alpha * A(i, k);
                    a1 = alpha * A(i + 1, k);
                    a2 = alpha * A(i + 2, k);
                    a3 = alpha * A(i + 3, k);
                }
                else
                {
                    a0 = A(i, k);
                    a1 = A(i + 1, k);
                    a2 = A(i + 2, k);
                    a3 = A(i + 3, k);
                }

                double b0 = B(k, j);
                double b1 = B(k, j + 1);
                double b2 = B(k, j + 2);
                double b3 = B(k, j + 3);
                c00 += a0 * b0;
                c01 += a0 * b1;
                c02 += a0 * b2;
                c03 += a0 * b3;
                c10 += a1 * b0;
                c11 += a1 * b1;
                c12 += a1 * b2;
                c13 += a1 * b3;
                c20 += a2 * b0;
                c21 += a2 * b1;
                c22 += a2 * b2;
                c23 += a2 * b3;
                c30 += a3 * b0;
                c31 += a3 * b1;
                c32 += a3 * b2;
                c33 += a3 * b3;
            }
            C(i, j) = c00;
            C(i, j + 1) = c01;
            C(i, j + 2) = c02;
            C(i, j + 3) = c03;
            C(i + 1, j) = c10;
            C(i + 1, j + 1) = c11;
            C(i + 1, j + 2) = c12;
            C(i + 1, j + 3) = c13;
            C(i + 2, j) = c20;
            C(i + 2, j + 1) = c21;
            C(i + 2, j + 2) = c22;
            C(i + 2, j + 3) = c23;
            C(i + 3, j) = c30;
            C(i + 3, j + 1) = c31;
            C(i + 3, j + 2) = c32;
            C(i + 3, j + 3) = c33;
        }
    }
    if (M4 == M &amp;&amp; N4 == N)
        return;
    // boundary conditions
    if (M4 != M)
        mydgemm_cpu_opt_k4(M - M4, N, K, alpha, A + M4, LDA, B, LDB, 1.0, &amp;C(M4, 0), LDC);
    if (N4 != N)
        mydgemm_cpu_opt_k4(M4, N - N4, K, alpha, A, LDA, &amp;B(0, N4), LDB, 1.0, &amp;C(0, N4), LDC);
}

</code></pre>
<h1 id="查找左右邻接组分的优化">查找左右邻接组分的优化</h1>
<p>哎呀，接下来其实Vtune Profile就很难给出什么有用的信息了，好在程序代码不多，我们可以从最花费时间的地方优化起。Partion部分，我们人肉顶针出了OpenCAEPoro奇怪的地方，不同组分直接的连接是通过***遍历数组***实现的。这就导致查找左右孩子（不同组分的连接），在2x2x2的组分中，就有可能有24个链接，获得连接信息就要跑24次。有没有可能用哈希表啊？来自O(1)时间算法的自信。<br>
然后，我们两位负责这个赛题的人，对着源代码肉眼检查了两周，终于发现可以简易优化的地方，位于Partition.cpp文件中的SetDistribution函数，以下仅为代码摘要，能这样修改的一共有两处，在这个部分，性能提高了7倍。</p>
<pre><code class="language-cpp">/// Get elements and their neighbors belonging to current process,
	/// Also, process of them are needed.
	/// Setup elements those will be sent to corresponding process
	//  target process, num of elements, num of edges, global index of elements, xadj, adjncy, adjproc
	vector&lt;vector&lt;idx_t&gt;&gt;  send_buffer;
	vector&lt;vector&lt;idx_t&gt;&gt;&amp; recv_buffer = elementCSR;
	recv_buffer.push_back(vector&lt;idx_t&gt; {myrank, 0, 0});

	// euphoria
	unordered_map&lt;idx_t, idx_t&gt; ump_send_buffer;

	// Calculate necessary memory first
	for (idx_t i = 0; i &lt; numElementLocal; i++) {
		if (part[i] == myrank) {
			recv_buffer[0][1] ++;
			recv_buffer[0][2] += (xadj[i + 1] - xadj[i]);
		}
		else {
			// euphoria
			if(ump_send_buffer[part[i]]){
				idx_t k = ump_send_buffer[part[i]] - 1;
				send_buffer[k][1] ++;
				send_buffer[k][2] += (xadj[i + 1] - xadj[i]);
			}
			else{
				send_buffer.push_back(vector&lt;idx_t&gt;{part[i], 1, xadj[i + 1] - xadj[i]});
				ump_send_buffer[part[i]] = send_buffer.size();
			}

			// idx_t k;
			// for (k = 0; k &lt; send_buffer.size(); k++) {
			// 	if (part[i] == send_buffer[k][0]) {
			// 		// existing process
			// 		send_buffer[k][1] ++;
			// 		send_buffer[k][2] += (xadj[i + 1] - xadj[i]);
			// 		break;
			// 	}
			// }
			// if (k == send_buffer.size()) {
			// 	// new process
			// 	send_buffer.push_back(vector&lt;idx_t&gt;{part[i], 1, xadj[i + 1] - xadj[i]});
			// }
		}
	}
</code></pre>
<h1 id="petsc-linersovler的优化">PETSc LinerSovler的优化</h1>
<p>重写decoup函数是我们首先应用的优化，这是其中的一个子函数，先把重复释放的内存操作给修改了，改为静态内存，避免反复申请释放对内存池的压力，同时申请对齐的内存，提高访问效率。</p>
<pre><code class="language-cpp">void inverse1(double *A, int N)
{
    static int *IPIV = static_cast&lt;int *&gt;(aligned_alloc(64, N * sizeof(int)));
    int LWORK = N * N;
    static int last_N = N;
    static double *WORK = static_cast&lt;double *&gt;(aligned_alloc(64, LWORK * sizeof(double)));
    int INFO;

    if (last_N &lt; N)
    {
        last_N = N;

        std::free(IPIV);
        std::free(WORK);

        IPIV = static_cast&lt;int *&gt;(aligned_alloc(64, N * sizeof(int)));
        WORK = static_cast&lt;double *&gt;(aligned_alloc(64, LWORK * sizeof(double)));
    }

    dgetrf_(&amp;N, &amp;N, A, &amp;N, IPIV, &amp;INFO);

    if (INFO &gt; 0)
    {
        printf(&quot;WARNING: The factorization has been completed, but the factor U is exactly singular&quot;);
    }
    else if (INFO &lt; 0)
    {
        printf(&quot;WARNING: There is an illegal value&quot;);
    }

    dgetri_(&amp;N, A, &amp;N, IPIV, WORK, &amp;LWORK, &amp;INFO);
}
</code></pre>
<p>然后，我们想到同样的操作也也可以用在整个LS求解函数中，从微小的args，到相对较大的nNDCount等变量开始改起，这些操作约可以带来了10%的Object Time提升。在查阅文档的时候，还意外发现PETSc自己实现了一个内存池的操作，这个又可以带来10%的性能提升。<br>
<img src="https://sethome2.github.io/post-images/1713175347615.png" alt="PETSc内存操作" loading="lazy"></p>
<p>在查阅PETSc文档后，确定了Mat, Vector, Ksp等PETSc变量可以被重复利用的前提下，我们将其改变为静态类型变量，企图得到进一步的性能提升，但是可惜的是并没有观察到有效的变化，估计是编译器做了一些神奇的优化掩盖掉了。</p>
<p>在求解函数中，还可以发现存在<code>MatAssemblyBegin</code>和<code>MatAssemblyBegin</code>的函数，这些字眼有效暗示了我们这个是异步操作函数，查阅PETSc文档后得到了肯定。我们在其中塞入了一些计算过程，但是Object Time也没有肉眼可以观察到的变化，或许case太小了没法体现出来。同样，我们复用了这一思想，在结果拷贝的过程中使用C++ 20 feature特性，异步拷贝了数据（但这不在Object time内，只是为了比赛求解更快）。</p>
<p>接下来我要介绍一些我们失败的优化，尝试过更改矩阵搜引，矩阵类型，修改预求解器的方法，加速LS求解过程，亦或者使用Cuda加速线性求解过程（即使它并不在Object Time其中，但是在比赛时可以有效减少题目运行时间），都因为没有明显的性能提升或者求解错误而作罢。在这里面，我们花费了超过一半以上的备赛时间，却很遗憾没有收获。</p>
<h1 id="修改汇总时的文件读写io">修改汇总时的文件读写IO</h1>
<p><strong>重点：我这个实现的代码有Bug，思路没问题，但是最后主线程输出改炸了，会导致SUMMAY.out文件输出到虚空里面。这个修改是我自己完成的，在比赛时才发现，我本人难逃其咎，已经向队友祈求原谅了，很遗憾因为这个错误，导致几乎所有的case在决赛时都跑了两次，在此处公开道歉了QAQ。</strong></p>
<p>在OpenCAEPoro中，汇总结果阶段会向磁盘中写入<code>SUMMAY_proc_xxx</code>和<code>Fast_proc_xxx</code>文件，再由主线程读取，整理出最终的数据，这样会导致可拓展性不高，对于上百核的并行，可能会造成性能损失，在研读代码后，我决定修改这一部分，不写入磁盘而通过MPI通讯汇总数据，最终得到10%的性能提升（线程越多性能提升越高，上千线程估计能跑到几百倍？）。</p>
<p>原来的过程像是这样：<br>
<img src="https://sethome2.github.io/post-images/1713177578180.png" alt="原版汇总方法" loading="lazy"></p>
<p>我改成每个线程都发送给主线程（先发送长度，在发送内容），初次编写MPI不太会，应该用Gather方法会比较合适。<br>
<img src="https://sethome2.github.io/post-images/1713177662038.png" alt="新版汇总方法" loading="lazy"></p>
<p>因为改炸了，实在不想说什么，居然能犯下这种错误，就说到这里吧。</p>
<h1 id="其他七七八八的小优化">其他七七八八的小优化</h1>
<p>接下来的优化就开始玄学了，来自OI选手的快乐。</p>
<ol>
<li>输入输出加速<br>
取消掉与C语言库输入输出的同步，可以使得输入输出流达到Printf的水平（当然还有更快的）：</li>
</ol>
<pre><code class="language-cpp">std::ios::sync_with_stdio(false);
std::cin.tie(0); 
std::cout.tie(0);
</code></pre>
<ol start="2">
<li>
<p>给vector提前分配空间<br>
代码中，不喜欢给vector模板reserve提前分配空间，但是有部分是可以被计算出来的，可以提前reserve分配好合适的空间。但是算不好会有反效果，默认下vector自动扩增的大小是当前大小的两倍。</p>
</li>
<li>
<p>更好看的加速比<br>
经过我们的多次测试，核心数越少，加速比越高（通讯的开销大了），这个代码在单机48核心的加速比约为1.68。</p>
</li>
<li>
<p>手动展开<br>
我们把自己修改的部分，手动展开了一部分循环。从汇编上对比手动展开的和没有手动展开的，手动展开的代码会更长，但是性能会有1-2%的提升（可以认为是误差了），求大佬解释，我们自己也没有一个很清晰的定论。</p>
</li>
</ol>
<h1 id="自我总结">自我总结</h1>
<p>如果没有改错的输入输出的话，我对我自己的修改是满意的。其他再厉害的方法，已经超出了我的能力范围，是还有待学习的领域。同时，这也是我们团队五个人齐心协力的结果，自问已经做到了最好，但是我们一致相信肯定还有更好的方法存在，也还希望大佬点评斧正。</p>
<h1 id="或许可以优化的点">或许可以优化的点？</h1>
<ol>
<li>PetSc矩阵类型</li>
<li>PetSc矩阵搜引</li>
<li>为什么OpenCAEPoro中要组装两次矩阵？（不确定是否正确的点）</li>
</ol>
<blockquote>
<p>Hi，此外我有幸作为打杂&amp;后勤队员，能参加这个比赛倍感荣幸，我还在寻找RA，PhD或者其他工作机会，希望聚焦于控制，航空航天，机器人学，如果您愿意向我伸出橄榄枝，我感激不尽。</p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[晶体培养手册]]></title>
        <id>https://sethome2.github.io/post/jing-ti-pei-yang-shou-ce/</id>
        <link href="https://sethome2.github.io/post/jing-ti-pei-yang-shou-ce/">
        </link>
        <updated>2023-11-21T15:57:52.000Z</updated>
        <content type="html"><![CDATA[<p>我其实从初中就开始想要培养晶体了，这个可能会动态更新一下。</p>
<p>图多杀猫！</p>
<p>我正式的开始学习化学则是从初中开始的，初中也是我化学最好的时候，后面上了高中感觉化学突然就变了样子，很多理论都是局限在一个圈子里面，后面就放弃化学了。<br>
除了考试，化学还是挺有趣的。</p>
<p>下面都会给出自己的方法和见解，建议看完所有步骤再行动。<br>
记得部分晶体防潮解。</p>
<h1 id="硫酸铜晶体">硫酸铜晶体</h1>
<p>化学式：不是<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>C</mi><mi>u</mi><mi>S</mi><msub><mi>O</mi><mn>4</mn></msub></mrow><annotation encoding="application/x-tex">CuSO_4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="mord mathdefault">u</span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.02778em;">O</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 而是<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>C</mi><mi>u</mi><mi>S</mi><msub><mi>O</mi><mn>4</mn></msub><mo separator="true">⋅</mo><mn>5</mn><msub><mi>H</mi><mn>2</mn></msub><mi>O</mi></mrow><annotation encoding="application/x-tex">CuSO_4·5H_2O</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="mord mathdefault">u</span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.02778em;">O</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">⋅</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">5</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.08125em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault" style="margin-right:0.02778em;">O</span></span></span></span>（五水硫酸铜）<br>
常见的似乎只有五水硫酸铜结构，晶胞结构有点复杂，还是看图吧。而且还有无水硫酸铜，呈现白色，加热到102℃失去两个结晶水；113℃失去三个结晶水；258℃失去全部结晶水；当加热温度达653℃时，开始分解生成<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>C</mi><mi>u</mi><mi>O</mi></mrow><annotation encoding="application/x-tex">CuO</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="mord mathdefault">u</span><span class="mord mathdefault" style="margin-right:0.02778em;">O</span></span></span></span>和<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>S</mi><msub><mi>O</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">SO_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.02778em;">O</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，在720℃时分解结束。</p>
<p>晶胞结构如下图，来源<a href="https://upload.wikimedia.org/wikipedia/commons/thumb/c/ce/Copper%28II%29-sulfate-unit-cell-3D-balls.png/300px-Copper%28II%29-sulfate-unit-cell-3D-balls.png">Wikipedia</a><br>
<img src="https://sethome2.github.io/post-images/1700586869077.jpg" alt="硫酸铜晶胞结构" loading="lazy"></p>
<p>步骤：</p>
<ol>
<li>
<p>准备五水硫酸铜试剂，纯水，烧杯，玻璃棒，一个小称，酒精灯或者水浴加热（可选）。<br>
稍微解释一下为什么准备这些东西，纯水是为了防止有杂质在水中，影响结晶的过程，不介意用白开水我估计也行。没有纯水或者家里没有反渗透净水器的，可以购买哇哈哈或者屈臣氏蒸馏水替代（有点用力过猛），就我个人是让舍友从实验室打了超纯水回来。加热硫酸铜溶液可以溶解更多的硫酸铜。在温度下降时，就会变成饱和溶液，从而结晶。</p>
</li>
<li>
<p>配置硫酸铜的饱和溶液，不停用玻璃杯搅拌直到全部溶解为止。如果有50ML水，加入至少15g的硫酸铜。如果选择后续加热溶液，则建议加入30g以上的五水硫酸铜。<br>
硫酸铜可以溶解31.6 g/100 ml在0度的水中，而在100度的水中，我暂时还没去查阅详细的资料，但是溶解100g/100 ml应该是没问题的。<br>
（图为氯化钠的）<br>
<img src="https://sethome2.github.io/post-images/1700585801683.JPG" alt="称量晶体" loading="lazy"></p>
</li>
<li>
<p>可选步骤，加热，加热可以溶解更多硫酸铜，还可以显著加速实验过程，基本上一天搞定。</p>
</li>
<li>
<p>静置，找一个没有晃动的地方，将烧杯盖好，静静等待就行。<br>
<img src="https://sethome2.github.io/post-images/1700586490673.JPG" alt="静置饱和溶液" loading="lazy">]</p>
</li>
</ol>
<p>有需要可以从里面找到一个比较完美的，作为结晶核再次培养。一般第一次都养不大。<br>
<img src="https://sethome2.github.io/post-images/1700586532827.JPG" alt="硫酸铜结晶" loading="lazy"></p>
<p>成品：（不如其他大佬的好，但是还挺透明的）<br>
<img src="https://sethome2.github.io/post-images/1700586554430.JPG" alt="硫酸铜结晶" loading="lazy"></p>
<h1 id="氯化钠晶体">氯化钠晶体</h1>
<p>化学式：<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>N</mi><mi>a</mi><mi>C</mi><mi>l</mi></mrow><annotation encoding="application/x-tex">NaCl</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span><span class="mord mathdefault">a</span><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="mord mathdefault" style="margin-right:0.01968em;">l</span></span></span></span><br>
步骤基本和硫酸铜一样，养出来就是一个四方的晶体，很好看，里面有点模糊，外面透明，缺点是养不大。</p>
<p>重要的事情说在前面：<br>
氯化钠溶解度：36.0 g/100ml (25 °C)。其受温度的影响不大，加热可以轻微溶解一部分，一定要有耐心。<br>
据说可以在培养的时候加入一点谷氨酸（味精）,使其看起来更透明。我就没有加，但是感觉还行。</p>
<p>晶胞结构如下图，来源<a href="(https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/NaCl-estructura_cristalina.svg/240px-NaCl-estructura_cristalina.svg.png)">Wikipedia</a>：<br>
<img src="https://sethome2.github.io/post-images/1700586907735.jpg" alt="氯化钠晶体结构" loading="lazy"></p>
<p>培养4天左右就开始生成奇怪的东西了。<br>
<img src="https://sethome2.github.io/post-images/1700586641725.JPG" alt="培养氯化钠晶体中间的样子" loading="lazy"></p>
<p>部分成品：有点像明日方舟的转置结晶？！<br>
<img src="https://sethome2.github.io/post-images/1700586673937.JPG" alt="氯化钠晶体" loading="lazy"><br>
<img src="https://sethome2.github.io/post-images/1700586676704.JPG" alt="氯化钠晶体" loading="lazy"></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Paddle OCR 跨平台编译部署]]></title>
        <id>https://sethome2.github.io/post/paddle-ocr-kua-ping-tai-bian-yi-bu-shu/</id>
        <link href="https://sethome2.github.io/post/paddle-ocr-kua-ping-tai-bian-yi-bu-shu/">
        </link>
        <updated>2022-09-27T15:50:21.000Z</updated>
        <content type="html"><![CDATA[<p>出于某种原因，我需要在某平台上打包编译paddleocr的组件，中间踩了很多坑。<br>
简单在这里总结一下吧。有其他问题可以留言。<br>
文章末尾我给出已经编译好的文件，有需要自取。</p>
<p>环境不用多说，CMake和Git必备。GCC建议自备10+，官方推荐8和5，看个人，不使用官方推荐的版本，还是建议全自己编译一下。</p>
<h1 id="编译opencv">编译openCV</h1>
<p>首先编译openCV，openCV似乎3和4都可以（不作确定与保证），在官方的readme里面写得很清楚，openCV毕竟是大项目，基本上不会出现问题。<br>
我用的是官方推荐的3.4.7，似乎4.5.5也能在paddle中使用。<br>
编译sh如下。</p>
<pre><code class="language-shell"># 建议使用绝对路径
root_path=&quot;/xxxxxxx/deployCpp/opencv-3.4.7&quot;
install_path=${root_path}/opencv3
build_dir=${root_path}/build

rm -rf ${build_dir}
mkdir ${build_dir}
cd ${build_dir}

cmake .. \
    -DCMAKE_INSTALL_PREFIX=${install_path} \
    -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_SHARED_LIBS=OFF \
    -DWITH_IPP=OFF \
    -DBUILD_IPP_IW=OFF \
    -DWITH_LAPACK=OFF \
    -DWITH_EIGEN=OFF \
    -DCMAKE_INSTALL_LIBDIR=lib64 \
    -DWITH_ZLIB=ON \
    -DBUILD_ZLIB=ON \
    -DWITH_JPEG=ON \
    -DBUILD_JPEG=ON \
    -DWITH_PNG=ON \
    -DBUILD_PNG=ON \
    -DWITH_TIFF=ON \
    -DBUILD_TIFF=ON

make -jxx
make install
</code></pre>
<p>接下来没什么好说的，等着滚动条滚动就行，openCV毕竟是国际大项目，没啥问题。</p>
<p>编译完成后得到build文件，Linux系统接下来的OPENCV_DIR变量指向这个文件夹就行了，Windows参考VS版本，一般为指向build文件夹下的VC15文件夹。</p>
<h1 id="编译paddlepaddle">编译paddlepaddle</h1>
<p>clone paddlepaddle仓库，建议直接最新版本梭哈。<br>
先说说Linux下的编译，在deploy/cpp_infer/tools内有编译脚本，我没用CUDA和CUDNN，纯CPU MKL部署。我是在英特尔CPU下部署的，据说openBLAS对AMD友好，就这样。<br>
官方的预编译仓库地址：<a href="https://paddleinference.paddlepaddle.org.cn/user_guides/download_lib.html#linux">https://paddleinference.paddlepaddle.org.cn/user_guides/download_lib.html#linux</a><br>
官方的手册：<a href="https://paddleinference.paddlepaddle.org.cn/user_guides/source_compile.html">https://paddleinference.paddlepaddle.org.cn/user_guides/source_compile.html</a><br>
吐槽一下，现在越来越喜欢docker了，编译怎么也用docker。</p>
<pre><code class="language-shell">sudo apt-get install gcc g++ make cmake git vim unrar python3 python3-dev python3-pip swig wget patchelf libopencv-dev
pip3 install numpy protobuf wheel setuptools

rm -rf build
mkdir build
cd build

# 注意需要修改ulimit -n nofile属性
# 在高版本ubuntu不起作用，需要手动修改/etc/systemd/system.conf
# /etc/systemd/user.conf,添加 DefaultLimitNOFILE=65535
# 在 /etc/security/limits.conf 添加以下两行
# hard nofile 65535
# soft nofile 65535
ulimit -n 4096

cmake  .. \
    -DWITH_CONTRIB=OFF \
    -DWITH_MKL=ON \
    -DWITH_MKLDNN=ON  \
    -DWITH_TESTING=OFF \
    -DCMAKE_BUILD_TYPE=Release \
    -DWITH_INFERENCE_API_TEST=OFF \
    -DON_INFER=ON \
    -DWITH_PYTHON=ON

# 可在此手动修改，针对高版本GCC
# 在thr_party/protobuf 中的报错，将函数修改为const类型
# paddle自己的报错，取消迭代器为const的限制
# 再手动make clean
# make -jx 即可

make -j48
make inference_lib_dist
</code></pre>
<p>等等，先别着急跑，有坑，paddle亲自给你挖的。<br>
paddle里面所有的编译优化参数-O （IO戏称氧气）全部写成了小写-o3，-o其实是重命名的代码，于是乎会导致Cmake找不到。</p>
<pre><code class="language-shell">nano CMakeList.txt 
</code></pre>
<p>把所有的-o3都改成-O3，就没问题了。</p>
<p>paddle本体编译一般不会出现什么问题，Windows有官方的预编译包，直接下就好了，别折腾了。如果要折腾也一样，Windows有好用的CMake GUI，参数一填，也一样。Windows建议使用VS编译。</p>
<h1 id="编译paddleocr-cpp_infer">编译paddleOCR cpp_infer</h1>
<p>OK，现在我们有paddle本体和openCV了，开始编译paddleOCR里面的cpp_infer吧。<br>
主要讲讲Linux，Windows有非常详细的官方教程，都别忘了改CMakeList.txt里面的-o3。<br>
Windows 参考链接：<a href="https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.6/deploy/cpp_infer/docs/windows_vs2019_build.md">https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.6/deploy/cpp_infer/docs/windows_vs2019_build.md</a><br>
Windows 没啥坑可以踩，别忘了拷贝DLL哦。</p>
<p>Linux编译脚本如下</p>
<pre><code class="language-shell">OPENCV_DIR=&quot;/xxxx/deployCpp/opencv-3.4.7/opencv3&quot;
LIB_DIR=&quot;/xxxx/deployCpp/paddlepaddle/Paddle/build&quot;
paddle_inference_install_dir
CUDA_LIB_DIR=your_cuda_lib_dir
CUDNN_LIB_DIR=your_cudnn_lib_dir

BUILD_DIR=build
rm -rf ${BUILD_DIR}
mkdir ${BUILD_DIR}
cd ${BUILD_DIR}
cmake .. \
    -DPADDLE_LIB=${LIB_DIR} \
    -DWITH_MKL=ON \
    -DWITH_GPU=OFF \
    -DWITH_STATIC_LIB=OFF \
    -DWITH_TENSORRT=OFF \
    -DOPENCV_DIR=${OPENCV_DIR} \
    -DCUDNN_LIB=${CUDNN_LIB_DIR} \
    -DCUDA_LIB=${CUDA_LIB_DIR} \
    -DTENSORRT_DIR=${TENSORRT_DIR} \

make -jxx
</code></pre>
<p>到此，编译结束了。可以看到build文件夹内生成的ppocr文件。Windows拷贝DLL就可以运行，Linux似乎包含了所有内容（仅限于完全自己编译），使用官方预编译似乎不行，需要手动拷贝一些.so文件。</p>
<p>文件下载列表：<br>
（懒得加，有时间先）</p>
<p>洗出结果的正则：（适合摆烂用，到时候一起补全）</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[第十三届蓝桥杯C/C++ B组个人比赛提交代码（摆烂杯]]></title>
        <id>https://sethome2.github.io/post/di-shi-san-jie-lan-qiao-bei-cc-b-zu-ge-ren-bi-sai-ti-jiao-dai-ma-bai-lan-bei/</id>
        <link href="https://sethome2.github.io/post/di-shi-san-jie-lan-qiao-bei-cc-b-zu-ge-ren-bi-sai-ti-jiao-dai-ma-bai-lan-bei/">
        </link>
        <updated>2022-04-09T14:53:33.000Z</updated>
        <content type="html"><![CDATA[<h1 id="填空题">填空题</h1>
<p>有一说一，今年没只有两个没想到</p>
<p>T1 题A: 九进制转十进制<br>
感觉没啥好说的，逢9进1。<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>a</mi><mi>n</mi><mi>s</mi><mo>=</mo><msup><mn>9</mn><mn>3</mn></msup><mo>×</mo><mn>2</mn><mo>+</mo><msup><mn>9</mn><mn>2</mn></msup><mo>×</mo><mn>0</mn><mo>+</mo><msup><mn>9</mn><mn>1</mn></msup><mo>×</mo><mn>2</mn><mo>+</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">ans = 9^3 \times 2 + 9^2 \times 0 + 9^1 \times 2 + 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathdefault">a</span><span class="mord mathdefault">n</span><span class="mord mathdefault">s</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord">9</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.72777em;vertical-align:-0.08333em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord">9</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.72777em;vertical-align:-0.08333em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord">9</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.72777em;vertical-align:-0.08333em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span></span></span></span></p>
<pre><code>1478 // should 1458 + 18 + 2 = 1,478
</code></pre>
<p>T2 题B: 顺子日期<br>
蓝桥杯歧义，建议冲锋</p>
<pre><code>4
</code></pre>
<hr>
<h1 id="大题">大题</h1>
<p>我犯了很多错误，已自裁</p>
<p>T1 题C: 刷题统计<br>
送分题，不多说，不开ULL，见祖宗。</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
using namespace std;

#define ULL unsigned long long

ULL a, b, n;

int main()
{
  cin &gt;&gt; a &gt;&gt; b &gt;&gt; n;

  ULL week = 0;
  week = a * 5 + b * 2;

  ULL week_cnt = n / week;
  n = n % week;

  ULL day = 0;
  while (n &gt; 0)
  {
    if (day &lt; 5)
    {
      n -= a;
      day++;
    }
    else
    {
      n -= b;
      day++;
    }
  }
  day += week_cnt * 7;
  cout &lt;&lt; day;

  return 0;
}
</code></pre>
<p>T2 题D: 修剪灌木<br>
找规律，不会的话可以像我一样，多写点数据，就出来了</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
using namespace std;

#define ULL unsigned long long

ULL tree[10001] = {};

ULL n;

int main()
{
  cin &gt;&gt; n;
  ULL index = n;
  if ((index % 2) == 1)
    index = index / 2 + 1;
  else
  {
    index = index / 2;
  }

  for (int i = 1; i &lt;= index; i++)
  {
    tree[i] = (n - i) * 2;
  }

  for (int i = 1; i &lt;= index; i++)
  {
      cout &lt;&lt; tree[i] &lt;&lt; endl;
  }

  if ((n % 2) == 1)
    index--;

  for (int i = index; i &gt; 0; i--)
  {
    cout &lt;&lt; tree[i] &lt;&lt; endl;
  }

  return 0;
}
</code></pre>
<p>T3 题E: X 进制减法<br>
看样例，主要是要理解，我手写了120 对应11 5 2 进制的数，就稍微理解了。<br>
求出值一减就行。</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#define FOREACH(a, b) for (int a = 0; i &lt; b; a++)
#define ULL unsigned long long 
using namespace std;

int N = 0;

int aLen = 0;
int a[1001] = {};

int bLen = 0;
int b[1001] = {};

int base[1001] = {};

ULL an;
ULL bn;

int main()
{
  //freopen(&quot;in.txt&quot;, &quot;r+&quot;, stdin);

  cin &gt;&gt; N;
  cin &gt;&gt; aLen;
  for (int i = 0; i &lt; aLen; i++)
  {
    cin &gt;&gt; a[i];
  }

  cin &gt;&gt; bLen;
  for (int i = 0; i &lt; bLen; i++)
  {
    cin &gt;&gt; b[i];
  }

  //找进制
  for (int i = 0; i &lt; aLen; i++)
  {
    if (a[i] &gt; b[i])
    {
      if (a[i] == 0 || a[i] == 1)
        base[i] = 2;
      else
        base[i] = a[i] + 1;
    }
    else
    {
      if (b[i] == 0 || b[i] == 1)
        base[i] = 2;
      else
        base[i] = b[i] + 1;
    }
  }
  int max = 0, max_index = 0;
  for (int i = 0; i &lt; aLen; i++)
  {
    if (a[i] &gt; max)
    {
      max = a[i];
      max_index = i;
    }
    if (b[i] &gt; max)
    {
      max = b[i];
      max_index = i;
    }
  }
  base[max_index] = N;

  //计算值
  ULL jin = 1;
  for (int i = aLen - 1; i &gt;= 0; i--)
  {

    if (i - (aLen - 1) == 0)
      an += a[i];
    else if (i - (aLen - 1) == 1)
    {
      jin *= base[i + 1];
      an += a[i] * base[i + 1];
    }
    else
    {
      jin *= base[i + 1];
      an += (a[i] * jin) % 1000000007;
    }
  }
  // cout &lt;&lt; an &lt;&lt; endl;

  jin = 1;
  for (int i = bLen - 1; i &gt;= 0; i--)
  {

    if (i - (bLen - 1) == 0)
      bn += b[i];
    else if (i - (bLen - 1) == 1)
    {
      jin *= base[i + 1];
      bn += b[i] * base[i + 1];
    }
    else
    {
      jin *= base[i + 1];
      bn += (b[i] * jin) % 1000000007;
    }
  }
  // cout &lt;&lt; bn &lt;&lt; endl;

  cout &lt;&lt; (an - bn) % 1000000007 &lt;&lt; endl;

  return 0;
}
</code></pre>
<p>T4 题F: 统计子矩阵<br>
摆烂没写(实际上是时间不够了），四重for循环也能写出来，但是感觉不是正解。<br>
理论上可以前缀和优化？蓝桥杯必出前缀和来着。</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#define ULL unsigned long long
using namespace std;

int x, y, N;

int arr[501][501] = {};

ULL cnt = 0;

int main()
{
    cin &gt;&gt; x;
    cin &gt;&gt; y;
    cin &gt;&gt; N;

    for (int i = 0; i &lt; y; i++)
        for (int j = 0; j &lt; x; j++)
        {
            cin &gt;&gt; arr[y][x];
            if(arr[y][x] &lt;= N)
                cnt++;
        }

    // 四重for暴力找
    
    return 0;
}
</code></pre>
<p>T5 题G: 积木画<br>
不会，想着可能是DP，找规律恶心我了，写出2*4知道大概有11个？就没写，严格来说就写了读入。<br>
按理来说应该是有规律的，2 5 11。</p>
<p>大佬说是二重前缀和，和大佬贴贴（lianyi超强的，他在友链里面，快去看看</p>
<p>T6 题H: 扫雷<br>
有点长hhh，现场学的map。<br>
爆搜挂着机，可能出现在找圆的时候TIME OUT，我人应该是魔怔了，应该读入的时候做优化的<br>
也就是说。。。优化了炸弹爆炸，应该就可以AC，应该还是遍历MAP树的方式，或者说队列，我是xxx</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#include &quot;vector&quot;
#include &quot;map&quot;

#define ULL unsigned long long
using namespace std;

int n, m;

typedef struct node
{
  /* data */
  ULL x, y;
  ULL range;
};

map&lt;pair&lt;ULL, ULL&gt;, int&gt; BOOM_map;
map&lt;pair&lt;ULL, ULL&gt;, int&gt; FIRE_map;

node boom[50000];
node fire[50000];

ULL cnt;

//检查是不是炸弹
inline bool cheak(int x, int y)
{
  auto res = BOOM_map.find(pair&lt;ULL, ULL&gt;(x, y));

  if (res == BOOM_map.end())
    return false;
  else
    return true;
}

//以圆圈方式查找炸弹，已经炸的-1
void dfs(int x, int y, int range)
{
  int i = 0, j = 0;
  while (1)
  {
    if (i * i + j * j &lt;= range * range)
    {
      if (cheak(i, j))
      {
        //开炸 dfs寻找
        int range_n = BOOM_map.at(pair&lt;ULL, ULL&gt;(i + x, j + y));
        if (range_n != -1)
        {
          cnt++;
          BOOM_map.at(pair&lt;ULL, ULL&gt;(i + x, j + y)) = -1;
          dfs(i, j, range_n);
        }
      }
      if (cheak(-i, j))
      {
        int range_n = BOOM_map.at(pair&lt;ULL, ULL&gt;(-i + x, j + y));
        if (range_n != -1)
        {
          cnt++;
          BOOM_map.at(pair&lt;ULL, ULL&gt;(-i + x, j + y)) = -1;
          dfs(-i, j, range_n);
        }
      }
      if (cheak(i, -j))
      {
        int range_n = BOOM_map.at(pair&lt;ULL, ULL&gt;(i + x, -j + y));
        if (range_n != -1)
        {
          cnt++;
          BOOM_map.at(pair&lt;ULL, ULL&gt;(i + x, -j + y)) = -1;
          dfs(i, -j, range_n);
        }
      }
      if (cheak(-i, -j))
      {
        int range_n = BOOM_map.at(pair&lt;ULL, ULL&gt;(-i + x, -j + y));
        if (range_n != -1)
        {
          cnt++;
          BOOM_map.at(pair&lt;ULL, ULL&gt;(-i + x, -j + y)) = -1;
          dfs(-i, -j, range_n);
        }
      }

      i++;
      j++;
    }
    else
      break;
  }
}

int main()
{
  cin &gt;&gt; n &gt;&gt; m;
  for (int i = 0; i &lt; n; i++)
  {
    cin &gt;&gt; boom[i].x &gt;&gt; boom[i].y &gt;&gt; boom[i].range;
    BOOM_map.insert(pair&lt;pair&lt;ULL, ULL&gt;, int&gt;(pair&lt;ULL, ULL&gt;(boom[i].x, boom[i].y), boom[i].range));
  }
  for (int i = 0; i &lt; m; i++)
  {
    cin &gt;&gt; fire[i].x &gt;&gt; fire[i].y &gt;&gt; fire[i].range;
    FIRE_map.insert(pair&lt;pair&lt;ULL, ULL&gt;,int&gt;(pair&lt;ULL, ULL&gt;(fire[i].x, fire[i].y), fire[i].range));
  }

  // 遍历火箭map
  for (auto iter : FIRE_map)
  {
    int x = iter.first.first;
    int y = iter.first.second;

    int range = iter.second;
    dfs(x, y, range);
  }

  cout &lt;&lt; cnt;

  return 0;
}
</code></pre>
<p>T7 题 I: 李白打酒加强版<br>
最后10分钟，没写完，就这样吧，退出有问题，别看了，放着凑字数</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#define ULL unsigned long long
using namespace std;

ULL N, M; // double sub1

ULL cnt;

ULL jiu = 0;

void dfs()
{
  if (N == 0 &amp;&amp; M == 0)
  {
    cnt++;
    cnt %= 1000000007;
    return;
  }
  if (N &lt; 0 || jiu &lt; 0 || M &lt; 0)
    return;

  N = N - 1;
  jiu *= 2;
  dfs();
  N++;
  jiu /= 2;

  M--;
  jiu--;
  dfs();
  jiu++;
  M++;
}

int main()
{
  cin &gt;&gt; N &gt;&gt; M;
  dfs();
  cout &lt;&lt; cnt;
  return 0;
}
</code></pre>
<p>T8 题J: 砍竹子<br>
有思路，没时间，后面补了注释，就这样吧，有点暴力，但我觉得是对的。<br>
STL不够熟，哈希模板不备，就是这个下场</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#include &quot;map&quot;
#include &quot;cmath&quot;
#include &quot;vector&quot;
#define ULL unsigned long long
using namespace std;

map&lt;ULL, int&gt; arr; // 高度 数量

unsigned int num;

ULL cntMAX;

void dfs(ULL cnt)
{
  // 如果都为 0 跳出循环
  // 根据cnt 剪枝
  for (auto iter = arr.rbegin(); iter != arr.rend(); iter++)// 问题在于，迭代器失效 似乎砍之后会给有效的迭代器
  {
    // 逆序读取高度 这样才是最高的
    // 砍第一个
    // 没砍到比第二个小的？继续砍
    // dfs
    // 还原第一个
  }
}

int main()
{
  cin &gt;&gt; num;
  for (int i = 0; i &lt; num; i++)
  {
    ULL high;
    cin &gt;&gt; high;
    if (arr.find(high) == arr.end())
      arr.insert(pair&lt;ULL, int&gt;(high, (int)1));
    else
      arr.at(high)++;
  }

  dfs(0);

  cout &lt;&lt; cntMAX;

  return 0;
}
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[常用的算法技巧]]></title>
        <id>https://sethome2.github.io/post/chang-yong-de-suan-fa-ji-qiao/</id>
        <link href="https://sethome2.github.io/post/chang-yong-de-suan-fa-ji-qiao/">
        </link>
        <updated>2022-04-09T11:48:53.000Z</updated>
        <content type="html"><![CDATA[<p>稍微和骗分导论分开一下，这里主要是搜集算法竞赛一些常用的代码模板和使用小技巧之类的，欢迎收藏~</p>
<p>长期更新哦！</p>
<h1 id="常用的宏定义">常用的宏定义</h1>
<pre><code class="language-c++">#include &quot;bits/stdc++.h&quot;
#define THREE_MAX(a, b, c) (a &gt; b ? (a &gt; c ? a : c) : (b &gt; c ? b : c)) 
#define THREE_MIN(a, b, c) (a &gt; b ? (b &gt; c ? c : b) : (a &gt; c ? c : a)) 
#define DEBUG freopen(&quot;in.txt&quot;,&quot;r+&quot;,stdin);
#define MAKE freopen(&quot;out.txt&quot;,&quot;w+&quot;,stdout);
#define FOREACH(a,b) for(int a = 0;a &lt; b;a++)
#define INIT(a,b) memset(a,b,sizeof(a));
#define ULL unsigned long long
#define LL long long
#define endl '\n'
using namespace std;

int main()
{
    // DEBUG
    // MAKE

    return 0;
}
</code></pre>
<h2 id="稍微解释一些宏定义的作用">稍微解释一些宏定义的作用</h2>
<p>DEBUG，可以很方便的调试输入样例，重新定向输入流到文件（再也不需要从文本框粘贴啦）。<br>
MAKE，打表用。<br>
FOREACH可以节约打for的时间，还更加简洁，当然这个得看个人习惯，也做不到Qt的Foreach智能</p>
<h1 id="线性素数筛">线性素数筛</h1>
<pre><code class="language-c++">bool is_prime[1000001] = {};

void produce()
{
  memset(&amp;is_prime, 1, sizeof(is_prime));
  is_prime[0] = is_prime[1] = 0;
  for (unsigned long long i = 2; i &lt;= 1000000; i++)
  {
    if (i * i &lt;= 1000000)
      for (unsigned long long j = i * i; j &lt;= 1000000; j += i)
        is_prime[j] = 0;
  }
}
</code></pre>
<p>素数打表（遇到就可以直接cv了wwwww,1000001个）<br>
<a href="https://blog.sethome.cc/algorithmFiles/prime.txt">素数打表文件</a></p>
<p>利用小费马定理的玄学素数筛（因为太玄学又不大用得到就删去了，知道有这个东西就行）</p>
<pre><code class="language-c++">
</code></pre>
<p>关闭C++的cout cin刷新与绑定，加快读取读入速度</p>
<pre><code class="language-c++">std::ios::sync_with_stdio(false);
std::cin.tie(0); 
std::cout.tie(0);
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[背包DP问题+动态规划 保姆级教程 包教包会]]></title>
        <id>https://sethome2.github.io/post/bei-bao-dp/</id>
        <link href="https://sethome2.github.io/post/bei-bao-dp/">
        </link>
        <updated>2022-03-25T14:13:54.000Z</updated>
        <content type="html"><![CDATA[<p>嘶，突然想起来，高中的时候其实没有认真学过DP（动态规划）的，记忆化搜索和DP只能说是异曲同工之妙，真正的DP核心在于状态转移方程。</p>
<p>之前遇到DP问题，都是直接恩算，尝试找出二维网格里面的一些关系。今天想了想，是应该从背包DP——这最简单的DP开始，一点一点理解动态规划了。</p>
<p><strong>动态规划是一种通过把原问题分解为更简单的子问题，求解子问题来解决复杂问题的方法</strong></p>
<p>长期更新~人菜瘾大。</p>
<h1 id="什么是背包dp">什么是背包DP？</h1>
<p>背包问题简洁的表述就是</p>
<blockquote>
<p>有N个物品，第i个物品的价值为V<sub>i</sub>，同时它的重量为W<sub>i</sub>，有一个背包能装总重为M的物品，<br>
如何取物品，才能使背包内所有物品的价值最大？</p>
</blockquote>
<p><strong>背包DP可以被分为以下几个类型</strong></p>
<table>
<thead>
<tr>
<th>背包类型</th>
<th>特殊条件</th>
</tr>
</thead>
<tbody>
<tr>
<td>01背包</td>
<td>每个物品只能被拿取一次</td>
</tr>
<tr>
<td>完全背包</td>
<td>每个物品物品都可以被重复拿取</td>
</tr>
<tr>
<td>多重背包</td>
<td>现在有两个以上的独立背包用于拿取了</td>
</tr>
<tr>
<td>混合背包</td>
<td>上面三种的混合，物品可以被拿(1, k, ∞)次，同时又有两个以上的背包</td>
</tr>
</tbody>
</table>
<h1 id="01背包问题">01背包问题</h1>
<p>先讲01背包。<br>
参考题目：<a href="https://www.luogu.com.cn/problem/P2871">P2871 [USACO07DEC]Charm Bracelet S</a></p>
<blockquote>
<p>有N个物品，第i个物品的价值为V<sub>i</sub>，同时它的重量为W<sub>i</sub>且只能取一次，<br>
有一个背包能装总重为M的物品，如何取物品，才能使背包内所有物品的价值最大？</p>
</blockquote>
<p>不难想出，我们可以试试每个物品拿或者不拿，可能的组合非常大，为时间复杂度为O(2<sub>N</sub>)。<br>
哪怕如果遇到超过背包装不下的状态而跳过，也是不能接受的复杂度。</p>
<p>先把问题简化一下吧，考虑在三个物品的情况下，背包问题如何解决。</p>
<p>构造一下数据<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>，假设我们的背包能装总重量4的物品（M=4）。</p>
<table>
<thead>
<tr>
<th>物品序号N<sub>i</sub></th>
<th>重量W<sub>i</sub></th>
<th>价值V<sub>i</sub></th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
<td>15</td>
</tr>
<tr>
<td>1</td>
<td>3</td>
<td>20</td>
</tr>
<tr>
<td>2</td>
<td>4</td>
<td>30</td>
</tr>
</tbody>
</table>
<p>肉眼观察法，一眼定真，很明显拿取 物品0 和 物品1 是最优的选择。<br>
但是，如何求解呢？</p>
<p>某种程度上我们可以求性价比<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msub><mi>P</mi><mi>i</mi></msub><mo>=</mo><mfrac><msub><mi>V</mi><mi>i</mi></msub><msub><mi>W</mi><mi>i</mi></msub></mfrac></mrow><annotation encoding="application/x-tex">P_i=\frac{V_i}{W_i}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.3335309999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8884309999999999em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.13889em;">W</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3280857142857143em;"><span style="top:-2.357em;margin-left:-0.13889em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.4101em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3280857142857143em;"><span style="top:-2.357em;margin-left:-0.22222em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>，,他完美符合我们上述的数据，可我们很容易就举出一个反例。<br>
假设背包能装总重为10的物品。</p>
<table>
<thead>
<tr>
<th>物品序号N<sub>i</sub></th>
<th>重量W<sub>i</sub></th>
<th>价值V<sub>i</sub></th>
<th>性价比P<sub>i</sub></th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>7</td>
<td>14</td>
<td>2</td>
</tr>
<tr>
<td>1</td>
<td>6</td>
<td>11</td>
<td>1.83</td>
</tr>
<tr>
<td>2</td>
<td>4</td>
<td>7</td>
<td>1.75</td>
</tr>
</tbody>
</table>
<p>如果按性价比贪心（只拿最高性价比的）就不能比拿取物品1和2的拥有更高价值，也就不能用于处理背包问题。某种意义上可以帮助简化搜索的过程，但是搜索不是正解（记忆化搜索除外，但也属于一种DP）。</p>
<p>回到最初的哪个例子，我们把问题再次简化一下，<strong>将问题分解为情况更小更简单的子问题</strong>。如果我们有能装总重量为M的背包，考虑容量更小的背包，以及更少的物品的情况下呢。</p>
<p>根据上面的假设，我们可以列出这样的一个表格，我们把问题拆分为 <strong>只拿前i个物品</strong> 和 <strong>更小的背包</strong> 的情况。</p>
<table>
<thead>
<tr>
<th></th>
<th>背包容量为0</th>
<th>背包容量为1</th>
<th>背包容量为2</th>
<th>背包容量为3</th>
<th>背包容量为4</th>
</tr>
</thead>
<tbody>
<tr>
<td>只拿前1个物品</td>
<td>0</td>
<td>15</td>
<td>15</td>
<td>15</td>
<td>15</td>
</tr>
<tr>
<td>只拿前2个物品</td>
<td>0</td>
<td>?</td>
<td>?</td>
<td>?</td>
<td>?</td>
</tr>
<tr>
<td>只拿前3个物品</td>
<td>0</td>
<td>?</td>
<td>?</td>
<td>?</td>
<td>?</td>
</tr>
</tbody>
</table>
<p>不难理解，如果只拿前1个（包括第1个，后面也是）物品，<strong>我们在背包能装得下的情况下（第一行可能不全是物品一的价值）</strong>，就是最优解。这个子问题，解决。</p>
<p>那么只拿前2个呢？现在我们尝试通过子问题来求解新的问题。<br>
我们可以知道分别在背包为0-4的重量时，只考虑拿物品0的最优解。对于我们的只拿前2个的情况，<strong>就是考虑在拿1个的基础上，拿或不拿物品1</strong>。<br>
我们假设当前背包容量为M<sub>i</sub>，以下为重点。</p>
<ul>
<li>拿取了物品1 ：总价值 = 物品0在背包容量为(M<sub>i</sub>减物品1的重量)的价值 + N<sub>1</sub>的价值</li>
<li>放弃物品1 : 总价值 = 物品0在背包容量为M<sub>i</sub>的价值</li>
</ul>
<blockquote>
<p>为什么拿取物品0时，背包容量是原容量减去物品1的重量？<br>
因为拿取物品0时，背包空间就相当于少了物品1的重量，自然要去找子问题的最优解，也就是物品0在此背包容量大小的最大值</p>
<p>假如物品装不下怎么办？<br>
不拿。</p>
</blockquote>
<p>选取 <strong>拿取与放弃物品1的总价值</strong> 中最大的值，我们就可以知道在背包容量为M<sub>i</sub>的情况下的最优解。</p>
<table>
<thead>
<tr>
<th></th>
<th>背包容量为0</th>
<th>背包容量为1</th>
<th>背包容量为2</th>
<th>背包容量为3</th>
<th>背包容量为4</th>
</tr>
</thead>
<tbody>
<tr>
<td>只拿前1个物品</td>
<td>15</td>
<td>15</td>
<td>15</td>
<td>15</td>
<td></td>
</tr>
<tr>
<td>只拿前2个物品</td>
<td>15</td>
<td>15</td>
<td>20</td>
<td>20</td>
<td></td>
</tr>
<tr>
<td>只拿前3个物品</td>
<td>?</td>
<td>?</td>
<td>?</td>
<td>?</td>
<td></td>
</tr>
</tbody>
</table>
<blockquote>
<p>以背包容量为3的情况举例，物品1价值20，重量3。</p>
<ul>
<li>拿取物品1的总价值 = 物品0在背包容量为0的价值 + 物品1价值 = 20</li>
<li>放弃物品1的总价值 = 物品0在背包容量为3的价值 = 15<br>
所以应拿取物品1，总价值为20</li>
</ul>
</blockquote>
<p>是不是有点思路了？类似数学归纳法，我们可以发现，只拿前3个物品的情况下，也就是只拿前2个物品的情况下，考虑拿或不拿物品2的总价值，谁更高。<br>
在前三个物品且背包容量为4的状态下就是我们需要的解。</p>
<p>接下来我们用符号来解释，刚才我们的表就是DP数组，假设选取i为序号≧i的物品，j就是当前背包的容量。<br>
物品序号从0开始哦。<br>
W<sub>i</sub>为序号i的物品重量，V<sub>i</sub>为序号i的物品价值。</p>
<strong>
DP[i][j] = max(不拿N~i~的总价值, 拿取N~i~的总价值)
<p>DP[i][j] = max(DP[i - 1][j],  DP[i - 1][j - W<sub>i</sub>] + V<sub>i</sub>)<br>
</strong></p>
<blockquote>
<p>不能理解的可以去看看前面通过子问题计算某个状态的最大值。使用了符号来表达而已。</p>
</blockquote>
<p>最终得到的结果如下，结果就是右下角的DP[2][5] = 35，即为背包的最大价值。</p>
<table>
<thead>
<tr>
<th></th>
<th>j = 0</th>
<th>j = 1</th>
<th>j = 2</th>
<th>j = 3</th>
<th>j = 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>i = 0</td>
<td>0</td>
<td>15</td>
<td>15</td>
<td>15</td>
<td>15</td>
</tr>
<tr>
<td>i = 1</td>
<td>0</td>
<td>15</td>
<td>15</td>
<td>20</td>
<td>20</td>
</tr>
<tr>
<td>i = 2</td>
<td>0</td>
<td>15</td>
<td>15</td>
<td>30</td>
<td>35</td>
</tr>
</tbody>
</table>
<p>现在我们可以尝试解上文题目了<br>
参考题目：<a href="https://www.luogu.com.cn/problem/P2871">P2871 [USACO07DEC]Charm Bracelet S</a></p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#include &quot;cmath&quot;
using namespace std;

int DP[3402][99999] = {0}; // 数组DP
int N = 0, M = 0;		   // 物品数量与背包容量

int N_W[3500] = {0}; // 物品重量
int N_V[3500] = {0}; // 物品价值

int main()
{
	cin &gt;&gt; N &gt;&gt; M; // 读取数据
	for (int i = 0; i &lt; N; i++)
		cin &gt;&gt; N_W[i] &gt;&gt; N_V[i];

	for (int i = 0; i &lt;= M; i++) // 初始化第一个物品的情况
		if (i &gt;= N_W[0])
			DP[0][i] = N_V[0];

	for (int i = 1; i &lt; N; i++) // DP循环
		for (int j = 1; j &lt;= M; j++)
			if (j &gt;= N_W[i])												  // 背包容量要大于物品容量才能拿取
				DP[i][j] = max(DP[i - 1][j], DP[i - 1][j - N_W[i]] + N_V[i]); // 比较拿取与不拿取谁更大
			else
				DP[i][j] = DP[i - 1][j];

	cout &lt;&lt; DP[N - 1][M]; // 输出结果
	return 0;
}
</code></pre>
<p>__ HOLD SHIT, MLE(Memory Limit Exceed)! 二维数组需要的数据量还是太大了__，导致了内存过大，要AC这个题，我们需要接近2GB的内存容量(逃</p>
<h2 id="优化dp数组">优化DP数组</h2>
<p>分析代码，可以发现DP数组是导致内存膨胀的主要原因。<br>
还是以刚才的DP数组为例，我们发现，当我们更新i = 2这一列的时候，i = 1以前的数据是完全不需要的。<br>
也就是说，前 i - 2 的物品数据是完全不会被使用到的，这样我们就可以用一维数组存储数据了。</p>
<table>
<thead>
<tr>
<th>j = 0</th>
<th>j = 1</th>
<th>j = 2</th>
<th>j = 3</th>
<th>j = 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>15</td>
<td>15</td>
<td>30</td>
<td>35</td>
</tr>
</tbody>
</table>
<p>同时我们再对背包容量的循环做些优化，可以得到以下代码。</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#include &quot;cmath&quot;
using namespace std;

int DP[12882] = {0}; // 数组DP
int N = 0, M = 0;	 // 物品数量与背包容量

int N_W[3500] = {0}; // 物品重量
int N_V[3500] = {0}; // 物品价值

int main()
{
	cin &gt;&gt; N &gt;&gt; M; // 读取数据
	for (int i = 0; i &lt; N; i++)
		cin &gt;&gt; N_W[i] &gt;&gt; N_V[i];

	for (int i = 0; i &lt; N; i++) // DP循环
		for (int j = M; j &gt;= N_W[i]; j--)
			DP[j] = max(DP[j], DP[j - N_W[i]] + N_V[i]);

	cout &lt;&lt; DP[M]; // 输出结果

	return 0;
}
</code></pre>
<blockquote>
<p>注：此时第二个for的遍历顺序必须从大往小，因为在二维DP数组中，我们拿取第i个物品时，DP[i - 1][j]是不会被更新的。<br>
但是在一维数组下，物品i在被拿取后，DP[j]就被立即更新，在后续随着j的更新，max(DP[j], DP[j - N_W[i]] + N_V[i])会取到之前被更新的值，也就可以被重复拿取了。<br>
<strong>事实上，调换更新顺序就是完全背包问题的解法</strong></p>
</blockquote>
<h1 id="完全背包问题">完全背包问题</h1>
<p>仔细阅读01背包问题的一维解法，你会发现，交换第二层for的顺序就可以完成了。提供代码如下。<br>
这里就不啰嗦了（其实是不知道怎么在md里面链接自己的段落）。</p>
<pre><code class="language-C++">#include &quot;iostream&quot;
#include &quot;cmath&quot;
using namespace std;

int DP[12882] = {0}; // 数组DP
int N = 0, M = 0;	 // 物品数量与背包容量

int N_W[3500] = {0}; // 物品重量
int N_V[3500] = {0}; // 物品价值

int main()
{
	cin &gt;&gt; N &gt;&gt; M; // 读取数据
	for (int i = 0; i &lt; N; i++)
		cin &gt;&gt; N_W[i] &gt;&gt; N_V[i];

	for (int i = 0; i &lt; N; i++) // DP循环
		for (int j = M; j &gt;= N_W[i]; j--)
			DP[j] = max(DP[j], DP[j - N_W[i]] + N_V[i]);

	cout &lt;&lt; DP[M]; // 输出结果

	return 0;
}
</code></pre>
<h1 id="在重新理解一次理解dp有点对不起这个标题">在重新理解一次理解DP（有点对不起这个标题</h1>
<p>DP(Dynamic programming)，动态规划。（我并不认为它的名字和思想有什么关联）</p>
<blockquote>
<p><strong>动态规划是一种通过把原问题分解为更简单的子问题，求解子问题来解决复杂问题的方法</strong></p>
</blockquote>
<p>通常我们解题的过程如下：</p>
<ol>
<li>我们在尝试解决背包问题的时候，最先开始制定了一种可能的规则，把原问题分解为不同的子问题。</li>
<li>尝试寻找从i - 1状态（或者更前的状态），更新到当前i状态的规则与方程。（也叫 <strong>动态转移方程</strong> ）</li>
<li>确定状态的 <strong>边界</strong>（前面的解释中我们提前了一步）</li>
<li>根据动态转移方程，得出最终的答案</li>
</ol>
<p>一步一步来解释吧。<br>
在第一步中，我们将问题分解的过程中需要考虑__最优子结构__ 和 <strong>无后效性</strong>  以及  <strong>子问题的重叠性</strong>。</p>
<blockquote>
<p>最优子结构：原问题的最优解所包含的子问题的解也能得到最优解<br>
无后效性：子问题的最优解一旦确定，就不再改变。（即使后面有更大更复杂的问题）<br>
子问题的重叠性: 在使用模拟或者递归求解时，总是会重复的求解某些子问题</p>
<p>如果我们构造的子问题有任一不满足，则很可能不能使用DP思想。</p>
</blockquote>
<p>在我们成功构造子问题后，需要查找子问题与原问题的关系规则，也就是 <strong>动态转移方程</strong>。这个也是DP最难的问题，在下也没用很好的技巧帮助你。<br>
个人解题习惯会展开子问题与原问题的解（类似我们构造二维DP数组），构造部分数据手算答案，再寻找可能的关系与规则。</p>
<p>接下来是 <strong>边界</strong>，其实就是动态转移中的第一个状态。<br>
一个问题总不能有无限的子问题，至少在求解的时候不能，这就需要我们自己构造合理的数据填入，然后再开始进行动态转移。在背包DP的例子中，我们的边界就是背包容量为0和前一个物品的时候。</p>
<p>最后还是要啰嗦的是，一个问题满足以下条件后，才可能使用DP思想解决，<strong>最优子结构</strong> 和 <strong>无后效性</strong>  以及  <strong>子问题的重叠性</strong>。</p>
<hr>
<p>终于写完啦，写了5h，希望还能对你有所帮助。如果可以的话还请收藏我的blog哦。</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>这里参考了<a href="https://zhuanlan.zhihu.com/p/345364527">这篇文章</a>的数据，感谢作者的付出。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></content>
    </entry>
</feed>