<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>mbinary&#39;s blog</title>
  
  <subtitle>淡泊明志, 宁静致远</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://mbinary.xyz/"/>
  <updated>2021-04-23T08:02:23.262Z</updated>
  <id>https://mbinary.xyz/</id>
  
  <author>
    <name>mbinary</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>生活也要有诗和远方呀</title>
    <link href="https://mbinary.xyz/poems.html"/>
    <id>https://mbinary.xyz/poems.html</id>
    <published>2021-04-23T08:02:23.262Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>2021-4-10</strong></p><h2 id="渔歌子-春游雁栖湖"><a href="#渔歌子-春游雁栖湖" class="headerlink" title="渔歌子   春游雁栖湖"></a>渔歌子   春游雁栖湖</h2><p>雁栖湖边绿柳新，<br>晨露花丛蜜蜂勤，<br>红步道，白亭台，<br>煦日清风把歌吟。</p><h2 id="游泳"><a href="#游泳" class="headerlink" title="游泳"></a>游泳</h2><p>池清水浅几一米，<br>泳裤拖鞋俱备齐，<br>手脚划蹬鼻替换，<br>如鱼自在不知疲。</p><h2 id="夜写代码"><a href="#夜写代码" class="headerlink" title="夜写代码"></a>夜写代码</h2><p>灯熄人未寐，夜寂月犹明。<br>两手持黑键，双瞳映白屏。<br>逻辑归正误，万物汇一零。<br>翌日查结果，中途错误停。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
      <category term="生活" scheme="https://mbinary.xyz/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="生活" scheme="https://mbinary.xyz/tags/%E7%94%9F%E6%B4%BB/"/>
    
  </entry>
  
  <entry>
    <title>傅里叶变换</title>
    <link href="https://mbinary.xyz/dft.html"/>
    <id>https://mbinary.xyz/dft.html</id>
    <published>2019-06-10T23:46:00.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#定义">定义</a><ul><li><a href="#连续">连续</a></li><li><a href="#离散">离散</a></li></ul></li><li><a href="#性质">性质</a><ul><li><a href="#分离性">分离性</a></li><li><a href="#位移定理">位移定理</a></li><li><a href="#周期性">周期性</a></li><li><a href="#共轭对称性">共轭对称性</a></li><li><a href="#旋转性">旋转性</a></li><li><a href="#加法定理">加法定理</a></li><li><a href="#平均值">平均值</a></li><li><a href="#相似性定理">相似性定理</a></li><li><a href="#卷积定理">卷积定理</a></li><li><a href="#相关定理">相关定理</a></li><li><a href="#rayleigh-定理">Rayleigh 定理</a></li></ul></li><li><a href="#快速傅里叶变换">快速傅里叶变换</a><ul><li><a href="#复数中的单位根">复数中的单位根</a></li><li><a href="#快速傅里叶变换的计算">快速傅里叶变换的计算</a></li></ul></li><li><a href="#代码">代码</a></li><li><a href="#参考">参考</a></li></ul><!-- /TOC --><p>图像处理中, 为了方便处理，便于抽取特征，数据压缩等目的，常常要将图像进行变换。<br>一般有如下变换方法</p><ol><li>傅立叶变换Fourier Transform </li><li>离散余弦变换Discrete Cosine Transform </li><li>沃尔希-哈德玛变换Walsh-Hadamard Transform </li><li>斜变换Slant Transform </li><li>哈尔变换Haar Transform </li><li>离散K-L变换Discrete Karhunen-Leave Transform </li><li>奇异值分解SVD变换Singular-Value Decomposition </li><li>离散小波变换Discrete Wavelet Transform </li></ol><p>这篇文章介绍一下傅里叶变换</p><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><h3 id="连续"><a href="#连续" class="headerlink" title="连续"></a>连续</h3><p>积分形式<br>如果一个函数的绝对值的积分存在，即</p><script type="math/tex; mode=display">\int_{-\infty} ^\infty |h(t)|dt<\infty</script><p>并且函数是连续的或者只有有限个不连续点，则对于 x 的任何值， 函数的傅里叶变换存在</p><ul><li>一维傅里叶变换<script type="math/tex; mode=display">H(f)=\int_{-\infty} ^\infty h(t)e^{-j2\pi ft}dt</script></li><li>一维傅里叶逆变换<script type="math/tex; mode=display">H(f)=\int_{-\infty} ^\infty h(t)e^{j2\pi ft}dt</script>同理多重积分<h3 id="离散"><a href="#离散" class="headerlink" title="离散"></a>离散</h3>实际应用中，多用离散傅里叶变换 DFT.</li><li>一维傅里叶变换<script type="math/tex; mode=display">F(u)=\sum_{x=0} ^{N-1} f(x)e^{\frac{-2\pi j}{N} ux}</script></li><li>一维傅里叶逆变换<script type="math/tex; mode=display">f(x)=\frac{1}{N}\sum_{u=0} ^{N-1} F(u)e^{\frac{2\pi j}{N} ux}</script>需要注意的是， 逆变换乘以 $\frac{1}{N}$ 是为了<strong>归一化</strong>，这个系数可以随意改变， 即可以正变换乘以 $\frac{1}{N}$, 逆变换就不乘，或者两者都乘以$\frac{1}{\sqrt{N}}$等系数。</li><li>二维傅里叶变换<script type="math/tex; mode=display">F(u,v)=\frac{1}{N}\sum_{x=0}^{N-1}\sum_{y=0} ^{N-1} f(x,y)e^{\frac{-2\pi j}{N} (ux+vy)}</script></li><li>二维傅里叶逆变换</li></ul><script type="math/tex; mode=display">f(x,y)=\frac{1}{N}\sum_{u=0}^{N-1}\sum_{v=0} ^{N-1} F(u,v)e^{\frac{2\pi j}{N} (ux+vy)}</script><p>幅度</p><script type="math/tex; mode=display">|F(u,v)| = \sqrt{real(F)^2+imag(F)^2}</script><p>相位</p><script type="math/tex; mode=display">arctan{\frac{imag(F)}{real(F)}}</script><p>对于图像的幅度谱显示，由于 |F(u,v)| 变换范围太大，一般显示 $D= log(|F(u,v)+1)$</p><p>用 <code>&lt;=&gt;</code> 表示傅里叶变换对</p><script type="math/tex; mode=display">f(x)<=>F(u)\\f(x,y)<=>F(u,v)</script><p>f,g,h 对应的傅里叶变换 F,G,H</p><p>$F^*$ 表示 $F$ 的共轭</p><h2 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h2><h3 id="分离性"><a href="#分离性" class="headerlink" title="分离性"></a>分离性</h3><script type="math/tex; mode=display">\begin{aligned}&F(x,v)=\sum_{y=0} ^{N-1} f(x,y)e^{\frac{-2\pi j}{N} vy}\\&F(u,v)=\frac{1}{N}\sum_{x=0}^{N-1}F(x,v)e^{\frac{-2\pi j}{N}ux}\end{aligned}</script><p>进行多维变换时，可以依次对每一维进行变换。 下面在代码中就是这样实现的。</p><h3 id="位移定理"><a href="#位移定理" class="headerlink" title="位移定理"></a>位移定理</h3><script type="math/tex; mode=display">f(x,y)e^{\frac{2\pi j}{N}(u_0x+v_0y)} <=>F(u-u_0,v-v_0)</script><script type="math/tex; mode=display">f(x-x_0,y-y_0)<=>F(u,v)e^{\frac{-2\pi j}{N}(ux_0+vy_0)}</script><h3 id="周期性"><a href="#周期性" class="headerlink" title="周期性"></a>周期性</h3><script type="math/tex; mode=display">F(u,v) = F(u+N,v+N)</script><h3 id="共轭对称性"><a href="#共轭对称性" class="headerlink" title="共轭对称性"></a>共轭对称性</h3><script type="math/tex; mode=display">F(u,v) = F^*(-u,-v)</script><p>a)偶分量函数在变换中产生偶分量函数;<br>b)奇分量函数在变换中产生奇分量函数;<br>c)奇分量函数在变换中引入系数-j;<br>d)偶分量函数在变换中不引入系数. </p><h3 id="旋转性"><a href="#旋转性" class="headerlink" title="旋转性"></a>旋转性</h3><p>若有 </p><script type="math/tex; mode=display">f(r,\theta)<=>F(\omega,\phi)</script><p>则 </p><script type="math/tex; mode=display">f(r,\theta+t)<=>F(\omega,\phi+t)</script><h3 id="加法定理"><a href="#加法定理" class="headerlink" title="加法定理"></a>加法定理</h3><p>1.</p><script type="math/tex; mode=display">Fourier[f+g]=Fourier[f]+Fourier[g]</script><p>2.</p><script type="math/tex; mode=display">af(x,y)<=>aF[u,v]</script><h3 id="平均值"><a href="#平均值" class="headerlink" title="平均值"></a>平均值</h3><script type="math/tex; mode=display">\frac{1}{N^2}\sum_{x=0}^{N-1}\sum_{y=0} ^{N-1} f(x,y) = \frac{1}{N}F(0,0)</script><h3 id="相似性定理"><a href="#相似性定理" class="headerlink" title="相似性定理"></a>相似性定理</h3><p>尺度变换</p><script type="math/tex; mode=display">f(ax,by)<=>\frac{F(\frac{u}{a},\frac{v}{b})}{ab}</script><h3 id="卷积定理"><a href="#卷积定理" class="headerlink" title="卷积定理"></a>卷积定理</h3><p>卷积定义<br>1d</p><script type="math/tex; mode=display">f*g = \frac{1}{M}\sum_{m=0}^{M-1}f(m)g(x-m)</script><p>2d</p><script type="math/tex; mode=display">f(x,y)*g(x,y) = \frac{1}{MN}\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}f(m,n)g(x-m,y-n)</script><p>卷积定理</p><script type="math/tex; mode=display">f(x,y)*g(x,y) <=> F(u,v)G(u,v)</script><script type="math/tex; mode=display">f(x,y)g(x,y)<=>F(u,v)*G(u,v)</script><p>离散卷积<br>用</p><script type="math/tex; mode=display">\sum_{i=0}^{N-1}x(iT)h[(k-i)T] <=> X(\frac{n}{NT})H(\frac{n}{NT})</script><p>即两个周期为 N 的抽样函数， 他们的卷积的离散傅里叶变换等于他们的离散傅里叶变换的卷积</p><p>卷积的应用：<br>去除噪声， 特征增强<br>两个不同周期的信号卷积需要周期扩展的原因：如果直接进行傅里叶变换和乘积，会产生折叠误差(卷绕)。</p><h3 id="相关定理"><a href="#相关定理" class="headerlink" title="相关定理"></a>相关定理</h3><p>下面用$ \infty$ 表示相关。<br>相关函数描述了两个信号之间的相似性，其相关性大小有相关系数衡量</p><ul><li>相关函数的定义<br>离散<script type="math/tex; mode=display">f(x,y)\quad  \infty \quad g(x,y) =  \frac{1}{MN}\sum_{m=0}^{M-1}\sum_{n=0}^{N-1}f^*(m,n)g(x+m,y+n)</script>连续<script type="math/tex; mode=display">z(t) = \int_{-\infty}^{\infty}x^*(\tau) h(t+\tau)d\tau</script></li><li>定理<script type="math/tex; mode=display">f(x,y)\quad  \infty \quad g(x,y)<=>F^*(u,v)G(u,v)</script><h3 id="Rayleigh-定理"><a href="#Rayleigh-定理" class="headerlink" title="Rayleigh 定理"></a>Rayleigh 定理</h3>能量变换<br>对于有限区间非零函数 f(t), 其能量为<script type="math/tex; mode=display">E = \int_{-\infty}^{\infty}|f(t)|^2dt</script></li></ul><p>其变换函数与原函数有相同的能量</p><script type="math/tex; mode=display"> \int_{-\infty}^{\infty}|f(t)|^2dt =  \int_{-\infty}^{\infty}|F(u)|^2dt</script><h2 id="快速傅里叶变换"><a href="#快速傅里叶变换" class="headerlink" title="快速傅里叶变换"></a>快速傅里叶变换</h2><p>由上面离散傅里叶变换的性质易知，直接计算 1维 dft 的时间复杂度维 $O(N^2)$。</p><p>利用到单位根的对称性，快速傅里叶变换可以达到 $O(nlogn)$的时间复杂度。</p><h3 id="复数中的单位根"><a href="#复数中的单位根" class="headerlink" title="复数中的单位根"></a>复数中的单位根</h3><p>我们知道， 在复平面，复数 $cos\theta +i\ sin\theta$k可以表示成 $e^{i\theta}$， 可以对应一个向量。$\theta$即为幅角。<br> 在<strong>单位圆</strong>中 ，单位圆被分成 $\frac{2\pi}{\theta}$ 份， 由单位圆的对称性 </p><script type="math/tex; mode=display">e^{i\theta} = e^{i(\theta+2\pi)}</script><p>现在记 $ n =\frac{ 2\pi }{\theta}$ ， 即被分成 n 份，幅度角为正且最小的向量称为 n 次单位向量， 记为$\omega _n$，<br>其余的 n-1 个向量分别为 $\omega_{n}^{2},\omega_{n}^{3},\ldots,\omega_{n}^{n}$ ，它们可以由复数之间的乘法得来 $w_{n}^{k}=w_{n}^{k-1}\cdot w_{n}^{1}\ (2 \leq k \leq n) $。<br>单位根的性质</p><ol><li>这个可以用 e 表示出来证明<script type="math/tex; mode=display">\omega_{2n}^{2k}=\omega_{n}^{k}</script></li><li>可以写成三角函数证明<script type="math/tex; mode=display">\omega_{n}^{k+\frac{n}{2}}=-\omega_{n}^{k}</script></li></ol><p>容易看出 $w_{n}^{n}=w_{n}^{0}=1 $。</p><p>对于$ w_{n}^{k}$ , 它事实上就是 $e^{\frac{2\pi i}{n}k}$ 。</p><h3 id="快速傅里叶变换的计算"><a href="#快速傅里叶变换的计算" class="headerlink" title="快速傅里叶变换的计算"></a>快速傅里叶变换的计算</h3><p>下面的推导假设 $n=2^k$，以及代码实现 FFT 部分也是 如此。</p><p>利用上面的对称性，<br>将傅里叶计算进行奇偶分组</p><script type="math/tex; mode=display">\begin{aligned}F(u)&=\sum_{i=0}^{n-1}\omega_n ^{iu} a^i\\       &= \sum_{i=0}^{\frac{n}{2}-1}\omega_n ^{2iu} a^{2i}+\sum_{i=0}^{\frac{n}{2}-1}\omega_n ^{(2i+1)u} a^{2i+1}\\      &=\sum_{i=0}^{\frac{n}{2}-1}\omega_{\frac{n}{2}} ^{iu} a^{2i}+\omega_n^u\sum_{i=0}^{\frac{n}{2}-1}\omega_{\frac{n}{2}} ^{iu} a^{2i+1}\\      & = F_{even}(u)+\omega_n^u F_{odd}(u)\end{aligned}</script><p>$F_{even}$表示将 输入的次序中偶数点进行 Fourier 变换， $F_{odd}$ 同理，这样就形成递推公式。<br>现在还没有减少计算量，下面通过将分别计算的 奇项，偶项利用起来，只计算 前 $\frac{n}{2}-1$项，后面的一半可以利用此结果马上算出来。每一次可以减少一半的计算量。</p><p>对于 $\frac{n}{2}\leq i+\frac{n}{2}\leq n-1$</p><script type="math/tex; mode=display">\begin{aligned}F(\omega_{n}^{i+\frac{n}{2}})&=F_{even}(\omega_{n}^{2i+n})+\omega_{n}^{i+\frac{n}{2}}\cdot F_{odd}(\omega_{n}^{2i+n})\\ &=F_{even}(\omega_{\frac{n}{2}}^{i+\frac{n}{2}})+\omega_{\frac{n}{2}}^{i+\frac{n}{2}}\cdot F_{odd}(\omega_{\frac{n}{2}}^{i+\frac{n}{2}})\\     & =F_{even}(\omega_{\frac{n}{2}}^{i})-\omega_{\frac{n}{2}}^{i}\cdot F_{odd}(\omega_{\frac{n}{2}}^{i})\end{aligned}</script><p>现在很清楚了，在每次计算 a[0..n-1] 的傅里叶变换F[0..n-1]，分别计算出奇 odd[0..n/2-1]，偶even[0..n/2-1]（可以递归地进行），<br>那么傅里叶变换为：</p><script type="math/tex; mode=display">F[i] = \begin{cases}even[i]+ \omega^i \cdot odd[i], \quad i<\frac{n}{2}\\even[i]- \omega^i \cdot odd[i], \quad else\end{cases}</script><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p>下面是 python 实现<br>一维用 FFT 实现， 不过 只实现了 2 的幂。/ 对于非 2 的幂，用 FFT 实现有点困难，还需要插值，所以我 用$O(n^2)$ 直接实现。</p><p>二维的 DFT利用 分离性，直接调用 一维 FFT。<br><a href="https://github.com/mbinary/algorithm" target="_blank" rel="noopener">GitHub</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_fft</span><span class="params">(a, invert=False)</span>:</span></span><br><span class="line">    N = len(a)</span><br><span class="line">    <span class="keyword">if</span> N == <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> [a[<span class="number">0</span>]]</span><br><span class="line">    <span class="keyword">elif</span> N &amp; (N - <span class="number">1</span>) == <span class="number">0</span>:  <span class="comment"># O(nlogn),  2^k</span></span><br><span class="line">        even = _fft(a[::<span class="number">2</span>], invert)</span><br><span class="line">        odd = _fft(a[<span class="number">1</span>::<span class="number">2</span>], invert)</span><br><span class="line">        i = <span class="number">2j</span> <span class="keyword">if</span> invert <span class="keyword">else</span> <span class="number">-2j</span></span><br><span class="line">        factor = np.exp(i * np.pi * np.arange(N // <span class="number">2</span>) / N)</span><br><span class="line">        prod = factor * odd</span><br><span class="line">        <span class="keyword">return</span> np.concatenate([even + prod, even - prod])</span><br><span class="line">    <span class="keyword">else</span>:  <span class="comment"># O(n^2)</span></span><br><span class="line">        w = np.arange(N)</span><br><span class="line">        i = <span class="number">2j</span> <span class="keyword">if</span> invert <span class="keyword">else</span> <span class="number">-2j</span></span><br><span class="line">        m = w.reshape((N, <span class="number">1</span>)) * w</span><br><span class="line">        W = np.exp(m * i * np.pi / N)</span><br><span class="line">        <span class="keyword">return</span> np.concatenate(np.dot(W, a.reshape(</span><br><span class="line">            (N, <span class="number">1</span>))))  <span class="comment"># important, cannot use *</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fft</span><span class="params">(a)</span>:</span></span><br><span class="line">    <span class="string">'''fourier[a]'''</span></span><br><span class="line">    n = len(a)</span><br><span class="line">    <span class="keyword">if</span> n == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">"[Error]: Invalid length: 0"</span>)</span><br><span class="line">    <span class="keyword">return</span> _fft(a)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ifft</span><span class="params">(a)</span>:</span></span><br><span class="line">    <span class="string">'''invert fourier[a]'''</span></span><br><span class="line">    n = len(a)</span><br><span class="line">    <span class="keyword">if</span> n == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">"[Error]: Invalid length: 0"</span>)</span><br><span class="line">    <span class="keyword">return</span> _fft(a, <span class="keyword">True</span>) / n</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fft2</span><span class="params">(arr)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> np.apply_along_axis(fft, <span class="number">0</span>,</span><br><span class="line">                               np.apply_along_axis(fft, <span class="number">1</span>, np.asarray(arr)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ifft2</span><span class="params">(arr)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> np.apply_along_axis(ifft, <span class="number">0</span>,</span><br><span class="line">                               np.apply_along_axis(ifft, <span class="number">1</span>, np.asarray(arr)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test</span><span class="params">(n=<span class="number">128</span>)</span>:</span></span><br><span class="line">    print(<span class="string">'\nsequence length:'</span>, n)</span><br><span class="line">    print(<span class="string">'fft'</span>)</span><br><span class="line">    li = np.random.random(n)</span><br><span class="line">    print(np.allclose(fft(li), np.fft.fft(li)))</span><br><span class="line"></span><br><span class="line">    print(<span class="string">'ifft'</span>)</span><br><span class="line">    li = np.random.random(n)</span><br><span class="line">    print(np.allclose(ifft(li), np.fft.ifft(li)))</span><br><span class="line"></span><br><span class="line">    print(<span class="string">'fft2'</span>)</span><br><span class="line">    li = np.random.random(n * n).reshape((n, n))</span><br><span class="line">    print(np.allclose(fft2(li), np.fft.fft2(li)))</span><br><span class="line"></span><br><span class="line">    print(<span class="string">'ifft2'</span>)</span><br><span class="line">    li = np.random.random(n * n).reshape((n, n))</span><br><span class="line">    print(np.allclose(ifft2(li), np.fft.ifft2(li)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">1</span>, <span class="number">3</span>):</span><br><span class="line">        test(i * <span class="number">16</span>)</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://202.38.64.11/~wansh/image.htm" target="_blank" rel="noopener">万寿红老师课件</a></li><li><a href="https://zhuanlan.zhihu.com/p/31584464" target="_blank" rel="noopener">一小时学会快速傅里叶变换 Fast Fourier Transform</a></li><li><a href="https://www.cnblogs.com/ECJTUACM-873284962/p/6919424.html" target="_blank" rel="noopener">快速傅里叶变换（FFT）算法【详解】</a></li></ul>]]></content>
    
    <summary type="html">
    
      图像处理中, 为了方便处理，便于抽取特征，数据压缩等目的，常常要将图形进行变换,这篇文章介绍一下傅里叶变换
    
    </summary>
    
      <category term="数学" scheme="https://mbinary.xyz/categories/%E6%95%B0%E5%AD%A6/"/>
    
    
      <category term="数学" scheme="https://mbinary.xyz/tags/%E6%95%B0%E5%AD%A6/"/>
    
      <category term="图像处理" scheme="https://mbinary.xyz/tags/%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>WSL 中的 VIM 与 Windwos 剪切板通信</title>
    <link href="https://mbinary.xyz/wsl-vim-clipboard.html"/>
    <id>https://mbinary.xyz/wsl-vim-clipboard.html</id>
    <published>2019-05-28T13:52:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- vim-markdown-toc GFM --><ul><li><a href="#前言">前言</a></li><li><a href="#说明">说明</a></li><li><a href="#复制">复制</a><ul><li><a href="#寄存器">寄存器</a></li><li><a href="#新建文件">新建文件</a></li><li><a href="#write命令">write命令</a></li></ul></li><li><a href="#粘贴">粘贴</a><ul><li><a href="#pastetoggle">pastetoggle</a></li><li><a href="#windows-paste-程序">windows paste 程序</a></li></ul></li><li><a href="#参考">参考</a></li></ul><!-- vim-markdown-toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>WSL 使用的不是系统剪切板,与系统剪切板通信，进行复制粘贴，是一个很棘手的问题。</p><p>问题:</p><ul><li><code>vim --version | grep clipboard</code> 没有 加号（系统）寄存器， 重新编译 vim（开启 featured)太麻烦</li><li>用 ctrl+shift+c/v 进行复制粘贴,然而在粘贴 python 代码以及 vim 安装 indentpython 插件时，粘贴得到的代码缩进会出现错误。</li></ul><p>我尝试了很多种方法，从操作的<strong>舒适程度</strong>，以及实现效果来得到最终最优的解决方法： 通过运行 windows 的 <code>paste.exe</code>, <code>clip.exe</code>程序进行复制粘贴</p><h2 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h2><p>环境如下</p><ul><li>Windows 10: 1709</li><li>WSL: Ubuntu 1904</li></ul><blockquote><p>说明</p><ul><li><code>&lt;c-r&gt;</code> 代表组合键 <code>ctrl</code>+<code>r</code> </li><li><code>&lt;cr&gt;</code> 代表回车键</li><li><code>&lt;f1&gt;</code> 可以从 1到12， 代表<code>F1</code></li><li><code>^J</code> 代表换行的控制字符，而不是<code>^</code>,<code>J</code>的连接，在 linux 上 换行为 <code>^@</code>,<br>在 VIM 输入控制字符，比如<code>^M</code>，需要按下<code>&lt;c-r&gt;&lt;c-m&gt;</code></li></ul></blockquote><h2 id="复制"><a href="#复制" class="headerlink" title="复制"></a>复制</h2><p>从 VIM 中 复制文本到 Windoes 剪切板。</p><h3 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a>寄存器</h3><p>通过 VIM 寄存器实现： 将 visual 模式下选中的文本复制到 vim 寄存器，然后将寄存器内容通过 shell 处理进入到剪切板。</p><p>首先选中（在 visual 模式下），用<code>&quot;ay</code>将内容保存到 <code>a</code>寄存器，然后在命令行模式下 <code>!echo &lt;c-r&gt;a \| /mnt/c/Windows/System32/clip.exe</code>(执行 shell 命令。  a 寄存器的内容直接 作为参数文本传递（命令行模式下, <c-r>再接寄存器名字，可以将寄存器内容拷贝过来)</c-r></p><p>然而拷贝的文本很可能不能直接在shell 下作为参数，有特殊字符，比如<code>&quot;</code>,<code>$</code>等等。<br>所以要进行转义，用 vim 的 <code>escape</code>函数 （我试了<code>shellescape</code>, 效果不怎么好）</p><p>把上面的操作映射到按键下， 我映射的是 <code>;y</code>, 就得到如下的 vim 键盘映射<br>在 visual 模式下选中，依次按下 <code>;y</code>即可复制</p><p><code>vmap ;y  &quot;ay: let @a=&quot;&#39;&quot;.escape(@a,&quot;\\&#39;\&quot;&quot;).&quot;&#39;&quot; &lt;cr&gt;:!echo &lt;c-r&gt;a \|&quot;/mnt/c/Windows/System32/clip.exe&quot;&lt;cr&gt;</code></p><p>然而在复制多行时,寄存器中会包含换行控制字符<code>^J</code>或<code>^@</code>,<code>^M</code>，这在传递到shell 中时执行会截断这个参数（在参数还没有输入完全按下 enter 回车),所以有时不会成功。<br>而且有些字符 escape 也很难转换为 shell 的原文本参数</p><p>所以，这个方法行不通</p><h3 id="新建文件"><a href="#新建文件" class="headerlink" title="新建文件"></a>新建文件</h3><p>由于寄存器难以传到 shell 作为参数， 我就想到可以把寄存器的内容复制到一个新的 文件 buffer 中， 然后将文件内容拷贝到剪切板，然后删除文件。<br>如下，每个 <code>&lt;cr&gt;</code> 分隔开 两条 命令，</p><p>在 visual 模式下选中，依次按下 <code>;y</code>即可复制</p><p><code>vmap ;y &quot;ay: vs vim-copy&lt;cr&gt;&quot;aP:wq&lt;cr&gt;:call system(&quot;/mnt/c/Windows/System32/clip.exe &lt; vim-copy &amp;&amp; rm vim-copy&quot;)&lt;cr&gt;&lt;cr&gt;</code></p><p>各部分解释如下</p><ul><li><code>&quot;ay</code>: 复制选中区域到 <code>a</code>寄存器</li><li><code>vs vim-copy</code>: 新建文件 <code>vim-copy</code>到新窗口</li><li><code>&quot;aP:wq</code>: 拷贝 <code>a</code> 内容到 文件并保存退出</li><li><code>call system(&quot;...&quot;)</code>: 执行 shell 命令， shell 命令的内容就是复制 文件内容到剪切板，再删除文件</li></ul><p>这个办法可以很好地复制， 唯一的缺点就是打开新buffer 窗口，再关闭，屏幕画面变化大，看着不舒服<span class="github-emoji" title="astonished" data-src="https://github.githubassets.com/images/icons/emoji/unicode/1f632.png?v8">&#x1f632;</span></p><h3 id="write命令"><a href="#write命令" class="headerlink" title="write命令"></a>write命令</h3><p>write 命令缩写为 w， 直接使用就是 保存缓冲区</p><p>他后面可以接shell 命令与 shell 交互</p><p>如<br><code>:w !echo</code></p><p>这是对于整个文件，也可以选择一部分，<br>而进入 visual 模式下选中，再按下<code>:</code>， 则进入命令行且将选择的位置也输入进命令行<br>这是可以 直接 传递给 <code>clip.exe</code> 程序。 执行后，选中的部分备剪切掉了，可以按 <code>u</code>恢复</p><p>在 visual 模式下选中，依次按下 <code>;y</code>即可复制<br><code>vmap ;y : !/mnt/c/Windows/System32/clip.exe&lt;cr&gt;u&#39;&#39;</code><br>这也是最优的方法了，如果你有更好的方法，欢迎赐教。</p><h2 id="粘贴"><a href="#粘贴" class="headerlink" title="粘贴"></a>粘贴</h2><p>从 Windows 剪切板 粘贴到 VIM</p><h3 id="pastetoggle"><a href="#pastetoggle" class="headerlink" title="pastetoggle"></a>pastetoggle</h3><p>如果 VIM 没有设置<code>set mouse=a</code>, 那么可以直接右击粘贴，设置了之后要按住 <code>shift</code>再右击粘贴</p><p>然而这样存在问题，就是 vim 设置了autoindent,它会错误的将粘贴进的文本进行缩进， 而不是粘贴原文。</p><p>这个办法可以 <code>set paste</code>， <code>set nopaste</code>解决，设置了<code>paste</code> 后，就可以原文粘贴，</p><p>而这样输入命令切换很麻烦， 可以<code>set pastetoggle=&lt;f12&gt;</code>，或者其他按键，这样按一次就可以切换 paste 状态。</p><p>这样比平常的 paste 动作 要多一个<code>pastetoggle</code> 操作，所以不好</p><h3 id="windows-paste-程序"><a href="#windows-paste-程序" class="headerlink" title="windows paste 程序"></a>windows paste 程序</h3><p>在 了解到上面 复制时使用的 <code>clip.exe</code>程序，我就在想是不是 windows 有也专门<code>paste</code>的程序 （这个程序是和 cmd 交互的，加之， wsl 也可以执行 <code>exe</code>程序)</p><p>很遗憾，windows 没有</p><p>但是令人高兴的是，一个网站上有，<a href="https://www.c3scripts.com/tutorials/msdos/paste.zip" target="_blank" rel="noopener">点击这里下载</a>, 然后解压放到 <code>C:Windows/System32</code>目录下<br>使用 vim 的 read 命令进行与 shell 的交互， 即将 shell命令执行的输出 读到当前 buffer</p><p>映射如下</p><p>在任何模式下按下 <code>;p</code> 即可粘贴<br><figure class="highlight elixir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">map ;p <span class="symbol">:read</span> !<span class="regexp">/mnt/c</span><span class="regexp">/Windows/</span>System32/paste.exe &lt;cr&gt;</span><br><span class="line">map! ;p &lt;esc&gt;<span class="symbol">:read</span> !<span class="regexp">/mnt/c</span><span class="regexp">/Windows/</span>System32/paste.exe &lt;cr&gt;</span><br></pre></td></tr></table></figure></p><p>这个是在新行开始粘贴, 如果在行内粘贴，粘贴的内容在一行内，可以按下<code>i&lt;bs&gt;&lt;esc&gt;l</code>执行退格操作（同样可以映射一下）</p><p>综上所述，最终解决方案为:<br><a href="https://www.c3scripts.com/tutorials/msdos/paste.zip" target="_blank" rel="noopener">点击这里下载</a>, 然后解压放到 <code>C:Windows\System32</code>目录下</p><p>再在 <code>.vimrc</code>文件中增加如下映射<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">vmap ;y : !/mnt/c/Windows/System32/clip.exe&lt;cr&gt;<span class="string">u''</span></span><br><span class="line">map ;p :read !/mnt/c/Windows/System32/paste.exe &lt;cr&gt;</span><br><span class="line">map! ;p &lt;esc&gt;:read !/mnt/c/Windows/System32/paste.exe &lt;cr&gt;</span><br></pre></td></tr></table></figure></p><p>WSL 真香，强烈推荐入坑 <span class="github-emoji" title="grimacing" data-src="https://github.githubassets.com/images/icons/emoji/unicode/1f62c.png?v8">&#x1f62c;</span><br>还想起一个 瑕疵， WSL 不支持32 位的程序， 不过可以安装  qemu 等解决。</p><p>另外 windows terminal 在今年 6月中旬也会来到，值得期待。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.c3scripts.com/tutorials/msdos/paste.html" target="_blank" rel="noopener">MS-DOS-Tutorial</a></li><li><a href="http://vimcdoc.sourceforge.net/doc/eval.html#functions" target="_blank" rel="noopener">vim-doc-zh-CN</a></li><li><a href="https://www.cnblogs.com/mydomain/archive/2011/10/15/2213017.html" target="_blank" rel="noopener">shell转义，单引号，双引号，反撇号</a></li></ul>]]></content>
    
    <summary type="html">
    
      WSL(Windows Subsystem for Linux)真香，然而还是存在一些瑕疵。比如 WSL不是使用的系统剪切板,与系统剪切板通信，进行复制粘贴，是一个很棘手的问题。本文谈谈解决这个问题的一些方法。
    
    </summary>
    
      <category term="linux" scheme="https://mbinary.xyz/categories/linux/"/>
    
    
      <category term="操作系统" scheme="https://mbinary.xyz/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
      <category term="vim" scheme="https://mbinary.xyz/tags/vim/"/>
    
  </entry>
  
  <entry>
    <title>Number-Theory</title>
    <link href="https://mbinary.xyz/number-theory.html"/>
    <id>https://mbinary.xyz/number-theory.html</id>
    <published>2018-12-16T16:56:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#01-gcd-co-primes">0.1. gcd, co-primes</a><ul><li><a href="#011-bezouts-identity">0.1.1. Bezout’s identity</a></li></ul></li><li><a href="#02-primality_test">0.2. primality_test</a><ul><li><a href="#021-prime-sieve">0.2.1. Prime Sieve</a></li><li><a href="#022-miller-rabin">0.2.2. Miller-Rabin</a></li></ul></li><li><a href="#03-factorization">0.3. Factorization</a><ul><li><a href="#031-pollards-rho-algorithm">0.3.1. Pollard’s rho algorithm</a></li></ul></li><li><a href="#04-euler-function">0.4. Euler function</a></li><li><a href="#05-modulo-equation">0.5. Modulo equation</a></li></ul><!-- /TOC --><p><a id="markdown-01-gcd-co-primes" name="01-gcd-co-primes"></a></p><h2 id="0-1-gcd-co-primes"><a href="#0-1-gcd-co-primes" class="headerlink" title="0.1. gcd, co-primes"></a>0.1. gcd, co-primes</h2><p><code>gcd</code> is  short for  <code>greatest common divisor</code><br>If <code>a</code>,<code>b</code> are  co-primes, we denote as $(a,b)=1, \text{which means } gcd(a,b)=1 $<br>We can use <code>Euclid algorithm</code> to calculate <code>gcd</code> of two numbers.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">gcd</span><span class="params">(a,b)</span>:</span></span><br><span class="line">    <span class="keyword">while</span> b!=<span class="number">0</span>:</span><br><span class="line">        a,b=b,a%b</span><br><span class="line">    <span class="keyword">return</span> a</span><br></pre></td></tr></table></figure></p><p><a id="markdown-011-bezouts-identity" name="011-bezouts-identity"></a></p><h3 id="0-1-1-Bezout’s-identity"><a href="#0-1-1-Bezout’s-identity" class="headerlink" title="0.1.1. Bezout’s identity"></a>0.1.1. Bezout’s identity</h3><p> Let <em>a</em> and <em>b</em> be <a href="https://en.wikipedia.org/wiki/Integer" title="Integer" target="_blank" rel="noopener">integers</a> with <a href="https://en.wikipedia.org/wiki/Greatest_common_divisor" title="Greatest common divisor" target="_blank" rel="noopener">greatest common divisor</a> <em>d</em>. Then, there exist integers <em>x</em> and <em>y</em> such that <em>ax</em> + <em>by</em> = <em>d</em>. More generally, the integers of the form <em>ax</em> + <em>by</em> are exactly the multiples of <em>d</em>.</p><p>we can use extended euclid algorithm to calculate x,y,gcd(a,b)<br><figure class="highlight python"><figcaption><span>gcd.py</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">xgcd</span><span class="params">(a,b)</span>:</span></span><br><span class="line">    <span class="string">'''return gcd(a,b),  x,y  where  ax+by=gcd(a,b)'''</span></span><br><span class="line">    <span class="keyword">if</span> b==<span class="number">0</span>:<span class="keyword">return</span> a,<span class="number">1</span>,<span class="number">0</span></span><br><span class="line">    g,x,y = xgcd(b,a%b)</span><br><span class="line">    <span class="keyword">return</span> g,y,x-a//b*y</span><br></pre></td></tr></table></figure></p><p><a id="markdown-02-primality_test" name="02-primality_test"></a></p><h2 id="0-2-primality-test"><a href="#0-2-primality-test" class="headerlink" title="0.2. primality_test"></a>0.2. primality_test</h2><p><a id="markdown-021-prime-sieve" name="021-prime-sieve"></a></p><h3 id="0-2-1-Prime-Sieve"><a href="#0-2-1-Prime-Sieve" class="headerlink" title="0.2.1. Prime Sieve"></a>0.2.1. Prime Sieve</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">primeSieve</span>:</span></span><br><span class="line">    <span class="string">'''sieve of Eratosthenes, It will be more efficient when judging many times'''</span></span><br><span class="line">    primes = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">11</span>,<span class="number">13</span>]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">isPrime</span><span class="params">(self,x)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> x&lt;=primes[<span class="number">-1</span>]:</span><br><span class="line">            <span class="keyword">return</span> twoDivideFind(x,self.primes)</span><br><span class="line">        <span class="keyword">while</span> x&gt;self.primes[<span class="number">-1</span>]:</span><br><span class="line">            left = self.primes[<span class="number">-1</span>]</span><br><span class="line">            right = (left+<span class="number">1</span>)**<span class="number">2</span></span><br><span class="line">            lst = []</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> range(left,right):</span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> self.primes:</span><br><span class="line">                    <span class="keyword">if</span> i%j==<span class="number">0</span>:<span class="keyword">break</span></span><br><span class="line">                <span class="keyword">else</span>:lst.append(i)</span><br><span class="line">            self.primes+=lst</span><br><span class="line">        <span class="keyword">return</span> twoDivideFind(x,lst)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">twoDivideFind</span><span class="params">(x,li)</span>:</span></span><br><span class="line">    a,b = <span class="number">0</span>, len(li)</span><br><span class="line">    <span class="keyword">while</span> a&lt;=b:</span><br><span class="line">        mid = (a+b)//<span class="number">2</span></span><br><span class="line">        <span class="keyword">if</span> li[mid]&lt;x:a=mid+<span class="number">1</span></span><br><span class="line">        <span class="keyword">elif</span> li[mid]&gt;x: b= mid<span class="number">-1</span></span><br><span class="line">        <span class="keyword">else</span>:<span class="keyword">return</span> mid</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span></span><br></pre></td></tr></table></figure><p><a id="markdown-022-miller-rabin" name="022-miller-rabin"></a></p><h3 id="0-2-2-Miller-Rabin"><a href="#0-2-2-Miller-Rabin" class="headerlink" title="0.2.2. Miller-Rabin"></a>0.2.2. Miller-Rabin</h3><blockquote><p>Excerpted from <a href="https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test" target="_blank" rel="noopener">wikipedia:Miller_Rabin_primality_test</a></p></blockquote><p>Just like the Fermat and Solovay–Strassen tests, the Miller–Rabin test relies on an equality or set of equalities that hold true for prime values, then checks whether or not they hold for a number that we want to test for primality.</p><p>First, a <a href="https://en.wikipedia.org/wiki/Lemma_(mathematics" target="_blank" rel="noopener">lemma</a> “Lemma (mathematics)”) about square <a href="https://en.wikipedia.org/wiki/Root_of_unity" title="Root of unity" target="_blank" rel="noopener">roots of unity</a> in the <a href="https://en.wikipedia.org/wiki/Finite_field" title="Finite field" target="_blank" rel="noopener">finite field</a> <strong>Z</strong>/<em>p**</em>Z<em>*, where </em>p<em> is prime and </em>p<em> &gt; 2. Certainly 1 and −1 always yield 1 when squared modulo </em>p<em>; call these <a href="https://en.wikipedia.org/wiki/Trivial_(mathematics" target="_blank" rel="noopener">trivial</a> “Trivial (mathematics)”) <a href="https://en.wikipedia.org/wiki/Square_root" title="Square root" target="_blank" rel="noopener">square roots</a> of 1. There are no </em>nontrivial<em> square roots of 1 modulo </em>p<em> (a special case of the result that, in a field, a <a href="https://en.wikipedia.org/wiki/Polynomial" title="Polynomial" target="_blank" rel="noopener">polynomial</a> has no more zeroes than its degree). To show this, suppose that </em>x<em> is a square root of 1 modulo </em>p*. Then:</p><script type="math/tex; mode=display">x^2\equiv1\ (mod\ p)</script><script type="math/tex; mode=display">(x-1)(x+1) \equiv 0\ (mod\ p)</script><p>In other words, prime <em>p</em> divides the product (<em>x</em> − 1)(<em>x</em> + 1). By <a href="https://en.wikipedia.org/wiki/Euclid%27s_lemma" title="Euclid&#39;s lemma" target="_blank" rel="noopener">Euclid’s lemma</a> it divides one of the factors <em>x</em> − 1 or <em>x</em> + 1, implying that <em>x</em> is congruent to either 1 or −1 modulo <em>p</em>.</p><p>Now, let <em>n</em> be prime, and odd, with <em>n</em> &gt; 2. It follows that <em>n</em> − 1 is even and we can write it as 2<sup><em>s</em></sup>·<em>d</em>, where <em>s</em> and <em>d</em> are positive integers and <em>d</em> is odd. For each <em>a</em> in (<strong>Z</strong>/<em>n**</em>Z<em>*)</em>, either</p><script type="math/tex; mode=display">a^d\equiv 1\ (mod\ n)</script><p>or</p><script type="math/tex; mode=display">a^{2^r*d}\equiv -1\ (mod\ n), \text{where } 0\le r<s</script><p>To show that one of these must be true, recall <a href="https://en.wikipedia.org/wiki/Fermat%27s_little_theorem" title="Fermat&#39;s little theorem" target="_blank" rel="noopener">Fermat’s little theorem</a>, that for a prime number n:</p><script type="math/tex; mode=display">a^{n-1}\equiv1\ (mod\ n)</script><p>By the lemma above, if we keep taking square roots of <em>a</em><sup><em>n</em>−1</sup>, we will get either 1 or −1. If we get −1 then the second equality holds and it is done. If we never get −1, then when we have taken out every power of 2, we are left with the first equality.</p><p>The Miller–Rabin primality test is based on the <a href="https://en.wikipedia.org/wiki/Contrapositive" title="Contrapositive" target="_blank" rel="noopener">contrapositive</a> of the above claim. That is, if we can find an <em>a</em> such that</p><script type="math/tex; mode=display">a^d\not\equiv 1\ (mod\ n)</script><p>and</p><script type="math/tex; mode=display">a^{2^r*d}\not\equiv -1\ (mod\ n), \text{where } 0\le r<s</script><p> then <em>n</em> is not prime. We call <em>a</em> a <a href="https://en.wikipedia.org/wiki/Witness_(mathematics" target="_blank" rel="noopener">witness</a> “Witness (mathematics)”) for the compositeness of <em>n</em> (sometimes misleadingly called a <em>strong witness</em>, although it is a certain proof of this fact). Otherwise <em>a</em> is called a <em>strong liar</em>, and <em>n</em> is a <a href="https://en.wikipedia.org/wiki/Probable_prime" title="Probable prime" target="_blank" rel="noopener">strong probable prime</a> to base <em>a</em>. The term “strong liar” refers to the case where <em>n</em> is composite but nevertheless the equations hold as they would for a prime.</p><p>Every odd composite <em>n</em> has many witnesses <em>a</em>, however, no simple way of generating such an <em>a</em> is known. The solution is to make the test <a href="https://en.wikipedia.org/wiki/Primality_test#Probabilistic_tests" title="Primality test" target="_blank" rel="noopener">probabilistic</a>: we choose a non-zero <em>a</em> in <strong>Z</strong>/<em>n**</em>Z<em>* randomly, and check whether or not it is a witness for the compositeness of </em>n<em>. If </em>n<em> is composite, most of the choices for </em>a<em> will be witnesses, and the test will detect </em>n<em> as composite with high probability. There is, nevertheless, a small chance that we are unlucky and hit an </em>a<em> which is a strong liar for </em>n<em>. We may reduce the probability of such error by repeating the test for several independently chosen </em>a*.</p><p>For testing large numbers, it is common to choose random bases <em>a</em>, as, a priori, we don’t know the distribution of witnesses and liars among the numbers 1, 2, …, <em>n</em> − 1. In particular, Arnault <sup><a href="https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#cite_note-Arnault397Digit-4" target="_blank" rel="noopener">[4]</a></sup> gave a 397-digit composite number for which all bases <em>a</em>less than 307 are strong liars. As expected this number was reported to be prime by the <a href="https://en.wikipedia.org/wiki/Maple_(software" target="_blank" rel="noopener">Maple</a> “Maple (software)”) <code>isprime()</code> function, which implemented the Miller–Rabin test by checking the specific bases 2,3,5,7, and 11. However, selection of a few specific small bases can guarantee identification of composites for <em>n</em> less than some maximum determined by said bases. This maximum is generally quite large compared to the bases. As random bases lack such determinism for small <em>n</em>, specific bases are better in some circumstances.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/number-theory-1.png" alt="number-theory-1"></p><p>python implementation<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> sample</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">quickMulMod</span><span class="params">(a,b,m)</span>:</span></span><br><span class="line">    <span class="string">'''a*b%m,  quick'''</span></span><br><span class="line">    ret = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> b:</span><br><span class="line">        <span class="keyword">if</span> b&amp;<span class="number">1</span>:</span><br><span class="line">            ret = (a+ret)%m</span><br><span class="line">        b//=<span class="number">2</span></span><br><span class="line">        a = (a+a)%m</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">quickPowMod</span><span class="params">(a,b,m)</span>:</span></span><br><span class="line">    <span class="string">'''a^b %m, quick,  O(logn)'''</span></span><br><span class="line">    ret =<span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> b:</span><br><span class="line">        <span class="keyword">if</span> b&amp;<span class="number">1</span>:</span><br><span class="line">            ret =quickMulMod(ret,a,m)</span><br><span class="line">        b//=<span class="number">2</span></span><br><span class="line">        a = quickMulMod(a,a,m)</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">isPrime</span><span class="params">(n,t=<span class="number">5</span>)</span>:</span></span><br><span class="line">    <span class="string">'''miller rabin primality test,  a probability result</span></span><br><span class="line"><span class="string">        t is the number of iteration(witness)</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="keyword">if</span> n&lt;<span class="number">2</span>:</span><br><span class="line">        print(<span class="string">'[Error]: &#123;&#125; can\'t be classed with prime or composite'</span>.format(n))</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    <span class="keyword">if</span> n==<span class="number">2</span>: <span class="keyword">return</span> <span class="keyword">True</span></span><br><span class="line">    d = n<span class="number">-1</span></span><br><span class="line">    r = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> d%<span class="number">2</span>==<span class="number">0</span>:</span><br><span class="line">        r+=<span class="number">1</span></span><br><span class="line">        d//=<span class="number">2</span></span><br><span class="line">    t = min(n<span class="number">-3</span>,t)</span><br><span class="line">    <span class="keyword">for</span> a <span class="keyword">in</span> sample(range(<span class="number">2</span>,n<span class="number">-1</span>),t):</span><br><span class="line">        x= quickPowMod(a,d,n)</span><br><span class="line">        <span class="keyword">if</span> x==<span class="number">1</span> <span class="keyword">or</span> x==n<span class="number">-1</span>: <span class="keyword">continue</span>  <span class="comment">#success,</span></span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> range(r<span class="number">-1</span>):</span><br><span class="line">            x= quickMulMod(x,x,n)</span><br><span class="line">            <span class="keyword">if</span> x==n<span class="number">-1</span>:<span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">True</span></span><br></pre></td></tr></table></figure></p><p><a id="markdown-03-factorization" name="03-factorization"></a></p><h2 id="0-3-Factorization"><a href="#0-3-Factorization" class="headerlink" title="0.3. Factorization"></a>0.3. Factorization</h2><p><a id="markdown-031-pollards-rho-algorithm" name="031-pollards-rho-algorithm"></a></p><h3 id="0-3-1-Pollard’s-rho-algorithm"><a href="#0-3-1-Pollard’s-rho-algorithm" class="headerlink" title="0.3.1. Pollard’s rho algorithm"></a>0.3.1. Pollard’s rho algorithm</h3><blockquote><p>Excerpted from <a href="https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm" target="_blank" rel="noopener">wikipedia:Pollard’s rho algorithm</a></p></blockquote><p>Suppose we need to factorize a number<br>$n=pq$, where $p$ is a non-trivial factor. A polynomial modulo $n$, called</p><script type="math/tex; mode=display">g(x)=x^2+c\ mod\ n</script><p>where c is a chosen number ,eg 1.</p><p>is used to generate a <a href="https://en.wikipedia.org/wiki/Pseudo-random_sequence" title="Pseudo-random sequence" target="_blank" rel="noopener">pseudo-random sequence</a>: A starting value, say 2, is chosen, and the sequence continues as  </p><script type="math/tex; mode=display">x_1 = g(2),x_2=g(g(2)),\ldots, x_i =g^{(i)}(2) = g(x_{i-1})</script><p>,<br>The sequence is related to another sequence$\{x_k\ mod \ p\}$ . Since $p$ is not known beforehand, this sequence cannot be explicitly computed in the algorithm. Yet, in it lies the core idea of the algorithm.</p><p>Because the number of possible values for these sequences are finite, both the$\{x_n\}$ sequence, which is mod $n$ , and $\{x_n\ mod\ p\}$ sequence will eventually repeat, even though we do not know the latter. Assume that the sequences behave like random numbers. Due to the <a href="https://en.wikipedia.org/wiki/Birthday_paradox" title="Birthday paradox" target="_blank" rel="noopener">birthday paradox</a>, the number of$x_k$before a repetition occurs is expected to be $O(\sqrt{N})$ , where $N$  is the number of possible values. So the sequence  $\{x_n\ mod\ p\}$  will likely repeat much earlier than the sequence $x_k$. Once a sequence has a repeated value, the sequence will cycle, because each value depends only on the one before it. This structure of eventual cycling gives rise to the name “Rho algorithm”, owing to similarity to the shape of the Greek character ρ when the values  $x_i\ mod \ p$  are represented as nodes in a <a href="https://en.wikipedia.org/wiki/Directed_graph" title="Directed graph" target="_blank" rel="noopener">directed graph</a>.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/number-theory-2.png" alt="number-theory-2"><br>This is detected by the <a href="https://en.wikipedia.org/wiki/Floyd%27s_cycle-finding_algorithm" title="Floyd&#39;s cycle-finding algorithm" target="_blank" rel="noopener">Floyd’s cycle-finding algorithm</a>: two nodes$i,j$ are kept. In each step, one moves to the next node in the sequence and the other moves to the one after the next node. After that, it is checked whether $\text{gcd}(x_i-x_j,n)\neq 1$.<br>If it is not 1, then this implies that there ris a repetition in the $\{x_k\ mod\ p\}$ swquence</p><p>This works because if the $x_i\ mod\ p$is the same as$x_j\ mod\ p$, the difference between$x_i,x_j$ is necessarily a multiple of $p$. Although this always happens eventually, the resulting GCD is a divisor of $n$ other than 1. This may be$n$ itself, since the two sequences might repeat at the same time. In this (uncommon) case the algorithm fails, and can be repeated with a different parameter.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/number-theory-3.png" alt="number-theory-3"></p><p>python implementation<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line"><span class="keyword">from</span> isPrime <span class="keyword">import</span> isPrime</span><br><span class="line"><span class="keyword">from</span> gcd <span class="keyword">import</span> gcd</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">factor</span><span class="params">(n)</span>:</span></span><br><span class="line">    <span class="string">'''pollard's rho algorithm'''</span></span><br><span class="line">    <span class="keyword">if</span> n==<span class="number">1</span>: <span class="keyword">return</span> []</span><br><span class="line">    <span class="keyword">if</span> isPrime(n):<span class="keyword">return</span> [n]</span><br><span class="line">    fact=<span class="number">1</span></span><br><span class="line">    cycle_size=<span class="number">2</span></span><br><span class="line">    x = x_fixed = <span class="number">2</span></span><br><span class="line">    c = randint(<span class="number">1</span>,n)</span><br><span class="line">    <span class="keyword">while</span> fact==<span class="number">1</span>:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(cycle_size):</span><br><span class="line">            <span class="keyword">if</span> fact&gt;<span class="number">1</span>:<span class="keyword">break</span></span><br><span class="line">            x=(x*x+c)%n</span><br><span class="line">            <span class="keyword">if</span> x==x_fixed:</span><br><span class="line">                c = randint(<span class="number">1</span>,n)</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            fact = gcd(x-x_fixed,n)</span><br><span class="line">        cycle_size *=<span class="number">2</span></span><br><span class="line">        x_fixed = x</span><br><span class="line">    <span class="keyword">return</span> factor(fact)+factor(n//fact)</span><br></pre></td></tr></table></figure></p><p><a id="markdown-04-euler-function" name="04-euler-function"></a></p><h2 id="0-4-Euler-function"><a href="#0-4-Euler-function" class="headerlink" title="0.4. Euler function"></a>0.4. Euler function</h2><p>Euler function, denoted as $\phi(n)$, mapping  n as the number of number which is smaller than n and is the co-prime of n.</p><p>e.g.: $\phi(3)=2$ since 1,2 are coprimes of 3 and smaller than 3,   $\phi(4)=2$ ,(1,3)</p><p>Euler function is a kind of productive function and has two properties as follows:</p><ol><li>$\phi(p^k) = p^k-p^{k-1}$, where p is a prime</li><li>$\phi(mn) = \phi(m)*\phi(n)$ where $(m,n)=1$</li></ol><p>Thus, for every narural number <em>n</em>, we can evaluate $\phi(n)$ using the following method.</p><ol><li>factorize n:<br><script type="math/tex">n = \prod _{i=1}^{l}  p_i^{k_i}</script>, where $p_i$ is a prime and $k_i,l &gt; 0$ .</li><li>calculate $\phi(n) $ using the two properties.</li></ol><script type="math/tex; mode=display">\begin{aligned}\phi(n) &=\phi( \prod _{i=1}^{l}  p_i^{k_i}) \\           &=\prod _{i=1}^{l} \phi(  p_i^{k_i}) \\            &=\prod _{i=1}^{l} ( p_i^{k_i}-p_i^{ {k_i}-1})\\            &=\prod _{i=1}^{l}p_i^{k_i} \prod _{i=1}^{l} ( 1-\frac{1}{p_i})\\            &=n \prod _{i=1}^{l} ( 1-\frac{1}{p_i})\\ \end{aligned}</script><p>And , $\sigma(n)$ represents the sum of all factors of n.<br>e.g. : $\sigma(9) = 1+3+9 = 14$</p><script type="math/tex; mode=display">\begin{aligned}\sigma(n) &= \prod _{i=1}^{l} \sum_{j=0}^{k_i} p_i^j \\            &=\prod _{i=1}^{l} \frac{p_i^{k_i+1}-1}{p_i-1}\\ \end{aligned}</script><p>A <code>perfect number</code> _n_ is defined as $\sigma(n) = 2n$<br>The following is the implementation of this two functions.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> factor <span class="keyword">import</span> factor</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> Counter</span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> mul</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">phi</span><span class="params">(n)</span>:</span></span><br><span class="line">    st  = set(factor(n))</span><br><span class="line">    <span class="keyword">return</span> round(reduce(mul,(<span class="number">1</span><span class="number">-1</span>/p <span class="keyword">for</span> p <span class="keyword">in</span> st),n))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sigma</span><span class="params">(n)</span>:</span></span><br><span class="line">    ct = Counter(factor(n))</span><br><span class="line">    <span class="keyword">return</span> reduce(mul,(round((p**(ct[p]+<span class="number">1</span>)<span class="number">-1</span>)/(p<span class="number">-1</span>)) <span class="keyword">for</span> p <span class="keyword">in</span> ct),<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p><a id="markdown-05-modulo-equation" name="05-modulo-equation"></a></p><h2 id="0-5-Modulo-equation"><a href="#0-5-Modulo-equation" class="headerlink" title="0.5. Modulo equation"></a>0.5. Modulo equation</h2><p>The following codes can solve a linear, group modulo equation. More details and explanations will be supplied if I am not too busy.</p><p>Note that I use <code>--</code> to represent $\equiv$ in the python codes.<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/number-theory-4.png" alt="number-theory-4"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> gcd <span class="keyword">import</span> xgcd</span><br><span class="line"><span class="keyword">from</span> euler <span class="keyword">import</span> phi</span><br><span class="line"><span class="keyword">from</span> isPrime <span class="keyword">import</span> isPrime</span><br><span class="line"><span class="keyword">from</span> factor <span class="keyword">import</span> factor</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span>  <span class="title">ind</span><span class="params">(m,g)</span>:</span></span><br><span class="line">    <span class="string">''' mod m ,primary root g  -&gt;  &#123;n:indg n&#125;'''</span></span><br><span class="line">    <span class="keyword">return</span> &#123;j:i <span class="keyword">for</span> i <span class="keyword">in</span> range(m<span class="number">-1</span>) \</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> range(m) <span class="keyword">if</span> (g**i-j)%m==<span class="number">0</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">gs</span><span class="params">(m,num=<span class="number">100</span>)</span>:</span></span><br><span class="line">    <span class="string">'''return list of  m's  primary roots below num'''</span></span><br><span class="line">    p = phi(m)</span><br><span class="line">    mp = factor(p)</span><br><span class="line">    checkLst = [p//i <span class="keyword">for</span> i <span class="keyword">in</span> mp]</span><br><span class="line">    <span class="keyword">return</span> [i <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">2</span>,num) <span class="keyword">if</span> all((i**n<span class="number">-1</span>)%m !=<span class="number">0</span>  <span class="keyword">for</span> n <span class="keyword">in</span> checkLst)]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">minG</span><span class="params">(m)</span>:</span></span><br><span class="line">    p = phi(m)</span><br><span class="line">    mp = factor(p)</span><br><span class="line">    checkLst = [p//i <span class="keyword">for</span> i <span class="keyword">in</span> mp]</span><br><span class="line">    i=<span class="number">2</span></span><br><span class="line">    <span class="keyword">while</span>  <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">if</span> all((i**n<span class="number">-1</span>)%m !=<span class="number">0</span>  <span class="keyword">for</span> n <span class="keyword">in</span> checkLst):<span class="keyword">return</span> i</span><br><span class="line">        i+=<span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">solve</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,equ=None)</span>:</span></span><br><span class="line">        self.linearPat= re.compile(<span class="string">r'\s*(\d+)\s*--\s*(\d+)[\s\(]*mod\s*(\d+)'</span>)</span><br><span class="line">        self.sol  = []</span><br><span class="line">        <span class="comment">#self.m = m</span></span><br><span class="line">        <span class="comment">#self.ind_mp = ind(m,minG(m))</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">noSol</span><span class="params">(self)</span>:</span></span><br><span class="line">        print(<span class="string">'equation &#123;equ&#125; has no solution'</span>.format(equ=self.equ))</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">error</span><span class="params">(self)</span>:</span></span><br><span class="line">        print(<span class="string">"Error! The divisor m must be postive integer"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">solveLinear</span><span class="params">(self,a,b,m)</span>:</span></span><br><span class="line">        <span class="string">'''ax--b(mod m): solve linear equation with one unknown</span></span><br><span class="line"><span class="string">            return  ([x1,x2,...],m)</span></span><br><span class="line"><span class="string">        '''</span></span><br><span class="line">        a,b,m = self.check(a,b,m)</span><br><span class="line">        g,x,y=xgcd(a,m)</span><br><span class="line">        <span class="keyword">if</span> a*b%g!=<span class="number">0</span>:</span><br><span class="line">            self.noSol()</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">        sol=x*b//g</span><br><span class="line">        m0 = m//g</span><br><span class="line">        sols = [(sol+i*m0)%m <span class="keyword">for</span> i <span class="keyword">in</span> range(g)]</span><br><span class="line">        print(<span class="string">'&#123;&#125;x--&#123;&#125;(mod &#123;&#125;), solution: &#123;&#125; mod &#123;&#125;'</span>.format(a,b,m,sols,m))</span><br><span class="line">        <span class="keyword">return</span> (sols,m)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">check</span><span class="params">(self,a,b,m)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> m&lt;=<span class="number">0</span>:</span><br><span class="line">            self.error()</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">        <span class="keyword">if</span> a&lt;<span class="number">0</span>:a,b=-a,-b  <span class="comment">## important</span></span><br><span class="line">        <span class="keyword">if</span> b&lt;<span class="number">0</span>:b+= -b//m * m</span><br><span class="line">        <span class="keyword">return</span> a,b,m</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">solveHigh</span><span class="params">(self,a,n,b,m)</span>:</span></span><br><span class="line">        <span class="string">''' ax^n -- b (mod m)  ind_mp is a dict of  m's &#123;n: indg n&#125;'''</span></span><br><span class="line">        ind_mp = ind(m,minG(m))</span><br><span class="line">        tmp = ind_mp[b] - ind_mp[a]</span><br><span class="line">        <span class="keyword">if</span> tmp &lt; <span class="number">0</span>:tmp+=m</span><br><span class="line">        sol = self.solveLinear(n,tmp,phi(m))</span><br><span class="line">        re_mp = &#123;j:i <span class="keyword">for</span> i ,j <span class="keyword">in</span> ind_mp.items()&#125;</span><br><span class="line">        sols = [re_mp[i] <span class="keyword">for</span> i <span class="keyword">in</span> sol[<span class="number">0</span>]]</span><br><span class="line">        print(<span class="string">'&#123;&#125;x^&#123;&#125;--&#123;&#125;(mod &#123;&#125;),  solution: &#123;&#125; mod &#123;&#125;'</span>.format(a,n,b,m,sols,m))</span><br><span class="line">        <span class="keyword">return</span> sols,m</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">solveGroup</span><span class="params">(self,tups)</span>:</span></span><br><span class="line">        <span class="string">'''tups is a list of tongyu equation groups, like</span></span><br><span class="line"><span class="string">            [(a1,b1,m1),(a2,b2,m2)...]</span></span><br><span class="line"><span class="string">            and, m1,m2... are all primes</span></span><br><span class="line"><span class="string">        '''</span></span><br><span class="line">        mp = &#123;&#125;</span><br><span class="line">        print(<span class="string">'solving group of equations: '</span>)</span><br><span class="line">        <span class="keyword">for</span> a,b,m <span class="keyword">in</span> tups:</span><br><span class="line">            print(<span class="string">'&#123;&#125;x--&#123;&#125;(mod &#123;&#125;)'</span>.format(a,b,m))</span><br><span class="line">            <span class="keyword">if</span> m <span class="keyword">in</span> mp:</span><br><span class="line">                <span class="keyword">if</span> mp[m][<span class="number">0</span>]*b!=mp[m][<span class="number">1</span>]*a:</span><br><span class="line">                    self.noSol()</span><br><span class="line">                    <span class="keyword">return</span></span><br><span class="line">            <span class="keyword">else</span>:mp[m] = (a,b)</span><br><span class="line">        product = <span class="number">1</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> mp.keys():</span><br><span class="line">            product *=i</span><br><span class="line">        sol = [<span class="number">0</span>]</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> mp:</span><br><span class="line">            xs,m = self.solveLinear(product//i*mp[i][<span class="number">0</span>],<span class="number">1</span>,i)</span><br><span class="line">            new = []</span><br><span class="line">            <span class="keyword">for</span> x <span class="keyword">in</span> xs:</span><br><span class="line">                cur = x*product//i*mp[i][<span class="number">1</span>]</span><br><span class="line">                <span class="keyword">for</span> old <span class="keyword">in</span> sol:</span><br><span class="line">                    new.append(old+cur)</span><br><span class="line">            sol = new</span><br><span class="line">        sol= [i%product <span class="keyword">for</span> i <span class="keyword">in</span> sol]</span><br><span class="line">        print(<span class="string">'final solution: &#123;&#125; mod &#123;&#125;'</span>.format(sol,product))</span><br><span class="line">        <span class="keyword">return</span> sol,product</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self)</span>:</span></span><br><span class="line">        s=input(<span class="string">'输入同余方程，用--代表同于号，形如3--5(mod 7)代表3x模7同余于5'</span>)</span><br><span class="line">        li= self.linearPat.findall(s)</span><br><span class="line">        li = [(int(a),int(b),int(m)) <span class="keyword">for</span> a,b,m <span class="keyword">in</span> li]</span><br><span class="line">        print(self.solveLinear(li[<span class="number">0</span>]))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    solver  = solve()</span><br><span class="line">    res = solver.solveLinear(<span class="number">3</span>,<span class="number">6</span>,<span class="number">9</span>)</span><br><span class="line">    print()</span><br><span class="line">    res = solver.solveHigh(<span class="number">1</span>,<span class="number">8</span>,<span class="number">3</span>,<span class="number">11</span>)</span><br><span class="line">    print()</span><br><span class="line">    res = solver.solveGroup([(<span class="number">5</span>,<span class="number">11</span>,<span class="number">2</span>),(<span class="number">3</span>,<span class="number">8</span>,<span class="number">5</span>),(<span class="number">4</span>,<span class="number">1</span>,<span class="number">7</span>)])</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      bezouts-identity, primality_test, miller-rabin, prime-sieve, pollards-rho-algorithm, modulo equation
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="数学" scheme="https://mbinary.xyz/tags/%E6%95%B0%E5%AD%A6/"/>
    
      <category term="数论" scheme="https://mbinary.xyz/tags/%E6%95%B0%E8%AE%BA/"/>
    
  </entry>
  
  <entry>
    <title>String-Mathching-Algorithm</title>
    <link href="https://mbinary.xyz/string-matching.html"/>
    <id>https://mbinary.xyz/string-matching.html</id>
    <published>2018-12-11T15:21:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>See more on <a href="https://github.com/mbinary/algorithm" target="_blank" rel="noopener">github</a></p><p>In this article, I will show you some kinds of popular string matching algorithm and  dynamic programming  algorithm for wildcard matching.</p><a id="more"></a><h1 id="String-Matching-algorithm"><a href="#String-Matching-algorithm" class="headerlink" title="String Matching algorithm"></a>String Matching algorithm</h1><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/string-matching-1.png" alt="string-matching-1"></p><h2 id="Rabin-Karp"><a href="#Rabin-Karp" class="headerlink" title="Rabin-Karp"></a>Rabin-Karp</h2><p>We can view a string of k characters (digits) as a length-k decimal number.  E.g., the string “31425” corresponds to the decimal number 31,425.</p><ul><li>Given a pattern P [1..m], let p denote the corresponding decimal value.</li><li>Given a text T [1..n], let $t_s$ denote the decimal value of the length-m substring  T [(s+1)..(s+m)] for s=0,1,…,(n-m).</li><li>let <code>d</code> be the radix of num, thus $d = len(set(s))$</li><li>$t_s$ = p iff T [(s+1)..(s+m)] = P [1..m].</li><li>p can be computed in O(m) time. p = P[m] + d*(P[m-1] + d*(P[m-2]+…)).</li><li>$t_0$ can similarly be computed in O(m) time.</li><li>Other $t_1,\ldots,t_{n-m}$ can be computed in O(n-m) time since $t_{s+1} can be computed from ts in constant time.</li></ul><p>Namely, </p><script type="math/tex; mode=display">t_{s+1} = d*(t_s-d^{m-1} * T[s+1])+T[s+m+1]</script><p>However, it’s no need to calculate $t_{s+1}$ directly. We can use modulus operation to reduce the work of caculation.</p><p>We choose a small prime number. Eg 13 for radix( denoted as d) 10.<br>Generally, $d*q$ should fit within one computer word.</p><p>We firstly caculate $t_0$ mod q.<br>Then, for every $t_i (i&gt;1)$<br>assume</p><script type="math/tex; mode=display"> t_{i-1} = T[i+m-1] + d*T[i+m-2]+\ldots+d^{m-1}*T[i-1]</script><p>denote $ d’ = d^{m-1}\ mod\ q$<br>thus,</p><script type="math/tex; mode=display">\begin{aligned}t_i &= (t_{i-1} - d^{m-1}*T[i-1]) * d + T[i+m]\\&\equiv (t_{i-1} - d^{m-1}*T[i-1]) * d + T[i+m] (mod\ q)\\&\equiv (t_{i-1}- ( d^{m-1} mod \ q) *T[i-1]) * d + T[i+m] (mod\ q)\\&\equiv (t_{i-1}- d'*T[i-1]) * d + T[i+m] (mod\ q)\end{aligned}</script><p>So we can compare the modular value of each $t_i$ with p’s.<br>Only if they are the same, then we compare the origin chracters, namely </p><script type="math/tex; mode=display">T[i],T[i+1],\ldots,T[i+m-1]</script><p>and the pattern characters.<br>Gernerally, this algorithm’s time approximation is O(n+m), and the worst case is O((n-m+1)*m)</p><p><strong>Problem: this is assuming p and $t_s$ are small numbers. They may be too large to work with easily.</strong></p><p>python implementation<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#coding: utf-8</span></span><br><span class="line"><span class="string">''' mbinary</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string"># File : rabin_karp.py</span></span><br><span class="line"><span class="string"># Author: mbinary</span></span><br><span class="line"><span class="string"># Mail: zhuheqin1@gmail.com</span></span><br><span class="line"><span class="string"># Blog: https://mbinary.github.io</span></span><br><span class="line"><span class="string"># Github: https://github.com/mbinary</span></span><br><span class="line"><span class="string"># Created Time: 2018-12-11  00:01</span></span><br><span class="line"><span class="string"># Description: rabin-karp algorithm</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">isPrime</span><span class="params">(x)</span>:</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">2</span>,int(x**<span class="number">0.5</span>)+<span class="number">1</span>):</span><br><span class="line">        <span class="keyword">if</span> x%i==<span class="number">0</span>:<span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">True</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getPrime</span><span class="params">(x)</span>:</span></span><br><span class="line">    <span class="string">'''return a prime which is bigger than x'''</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(x,<span class="number">2</span>*x):</span><br><span class="line">        <span class="keyword">if</span> isPrime(i):<span class="keyword">return</span> i</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">findAll</span><span class="params">(s,p)</span>:</span></span><br><span class="line">    <span class="string">'''s: string   p: pattern'''</span></span><br><span class="line">    dic=&#123;&#125;</span><br><span class="line">    n,m = len(s),len(p)</span><br><span class="line">    d=<span class="number">0</span> <span class="comment">#radix</span></span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> s:</span><br><span class="line">        <span class="keyword">if</span> c <span class="keyword">not</span> <span class="keyword">in</span> dic:</span><br><span class="line">            dic[c]=d</span><br><span class="line">            d+=<span class="number">1</span></span><br><span class="line">    sm = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> p:</span><br><span class="line">        <span class="keyword">if</span> c <span class="keyword">not</span> <span class="keyword">in</span> dic:<span class="keyword">return</span> []</span><br><span class="line">        sm = sm*d+dic[c]</span><br><span class="line"></span><br><span class="line">    ret = []</span><br><span class="line">    cur = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(m): cur=cur*d + dic[s[i]]</span><br><span class="line">    <span class="keyword">if</span> cur==sm:ret.append(<span class="number">0</span>)</span><br><span class="line">    tmp = n-m</span><br><span class="line">    q = getPrime(m)</span><br><span class="line">    cur = cur%q</span><br><span class="line">    sm = sm%q</span><br><span class="line">    exp = d**(m<span class="number">-1</span>) % q</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(m,n):</span><br><span class="line">        cur = ((cur-dic[s[i-m]]*exp)*d+dic[s[i]]) % q</span><br><span class="line">        <span class="keyword">if</span> cur == sm <span class="keyword">and</span> p==s[i-m+<span class="number">1</span>:i+<span class="number">1</span>]:</span><br><span class="line">            ret.append(i-m+<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">randStr</span><span class="params">(n=<span class="number">3</span>)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> [randint(ord(<span class="string">'a'</span>),ord(<span class="string">'z'</span>)) <span class="keyword">for</span> i <span class="keyword">in</span> range(n)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ ==<span class="string">'__main__'</span>:</span><br><span class="line">    <span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line">    s = randStr(<span class="number">50</span>)</span><br><span class="line">    p = randStr(<span class="number">1</span>)</span><br><span class="line">    print(s)</span><br><span class="line">    print(p)</span><br><span class="line">    print(findAll(s,p))</span><br></pre></td></tr></table></figure></p><h2 id="FSM"><a href="#FSM" class="headerlink" title="FSM"></a>FSM</h2><p>A FSM can be represented as $(Q,q_0,A,S,C)$, where</p><ul><li>Q is the set of all states</li><li>$q_0$ is the start state</li><li>$A\in Q$ is a set of accepting states.</li><li>S is a finite input alphabet.</li><li>C is the set of transition functions: namely  $q_j = c(s,q_i)$.</li></ul><p>Given a pattern string S, we can build a FSM for string matching.<br>Assume S has m chars, and there should be m+1 states. One is for the begin state, and the others are for matching state of each position of S.</p><p>Once we have built the FSM, we can run it on any input string.</p><h2 id="KMP"><a href="#KMP" class="headerlink" title="KMP"></a>KMP</h2><blockquote><p>Knuth-Morris-Pratt method</p></blockquote><p>The idea is inspired by FSM. We can avoid computing the transition functions. Instead, we compute a prefix function P in O(m) time, which has only m entries.</p><blockquote><p>Prefix funtion stores info about how the pattern matches against shifts of itself.</p></blockquote><ul><li>String w is a prefix of string x, if x=wy for some string y</li><li>String w is a suffix of string x, if x=yw for some string y</li><li>The k-character prefix of the pattern P [1..m] denoted by Pk.</li><li>Given that pattern prefix P [1..q] matches text characters T [(s+1)..(s+q)], what is the least shift s’&gt; s such that P [1..k] = T [(s’+1)..(s’+k)] where s’+k=s+q?</li><li>At the new shift s’, no need to compare the first k characters of P with corresponding characters of T.<br>Method: For prefix $p_i$, find the longest proper prefix of $p_i$ that is also a suffix of $p_i$.<script type="math/tex; mode=display">pre[q] = max\{k|k<q , p_k \text{is a suffix of } p_q\}</script></li></ul><p>For example:  p = ababaca,<br>Then,<br>$p_5$ = ababa, pre[5] = 3.<br>Namely $p_3$=aba is the longest prefix of p that is also a suffix of $p_5$.</p><p>Time approximation: finding prefix function take O(m), matching takes O(m+n)</p><p>python implementation<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#coding: utf-8</span></span><br><span class="line"><span class="string">''' mbinary</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string"># File : KMP.py</span></span><br><span class="line"><span class="string"># Author: mbinary</span></span><br><span class="line"><span class="string"># Mail: zhuheqin1@gmail.com</span></span><br><span class="line"><span class="string"># Blog: https://mbinary.github.io</span></span><br><span class="line"><span class="string"># Github: https://github.com/mbinary</span></span><br><span class="line"><span class="string"># Created Time: 2018-12-11  14:02</span></span><br><span class="line"><span class="string"># Description:</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getPrefixFunc</span><span class="params">(s)</span>:</span></span><br><span class="line">    <span class="string">'''return the list of prefix function of s'''</span></span><br><span class="line">    length = <span class="number">0</span></span><br><span class="line">    i = <span class="number">1</span></span><br><span class="line">    n = len(s)</span><br><span class="line">    ret = [<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">while</span> i&lt;n:</span><br><span class="line">        <span class="keyword">if</span> s[i]==s[length]:</span><br><span class="line">            length +=<span class="number">1</span></span><br><span class="line">            ret.append(length)</span><br><span class="line">            i+=<span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">if</span> length==<span class="number">0</span>:</span><br><span class="line">                ret.append(<span class="number">0</span>)</span><br><span class="line">                i+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                length = ret[length<span class="number">-1</span>]</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">findAll</span><span class="params">(s,p)</span>:</span></span><br><span class="line">    pre = getPrefixFunc(p)</span><br><span class="line">    i = j  =<span class="number">0</span></span><br><span class="line">    n,m = len(s),len(p)</span><br><span class="line">    ret = []</span><br><span class="line">    <span class="keyword">while</span> i&lt;n:</span><br><span class="line">        <span class="keyword">if</span> s[i]==p[j]:</span><br><span class="line">            i+=<span class="number">1</span></span><br><span class="line">            j+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> j==m:</span><br><span class="line">                ret.append(i-j)</span><br><span class="line">                j=pre[j<span class="number">-1</span>]</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">if</span> j==<span class="number">0</span>: i+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">else</span>: j = pre[j<span class="number">-1</span>]</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">randStr</span><span class="params">(n=<span class="number">3</span>)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> [randint(ord(<span class="string">'a'</span>),ord(<span class="string">'z'</span>)) <span class="keyword">for</span> i <span class="keyword">in</span> range(n)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ ==<span class="string">'__main__'</span>:</span><br><span class="line">    <span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line">    s = randStr(<span class="number">50</span>)</span><br><span class="line">    p = randStr(<span class="number">1</span>)</span><br><span class="line">    print(s)</span><br><span class="line">    print(p)</span><br><span class="line">    print(findAll(s,p))</span><br></pre></td></tr></table></figure></p><h2 id="Boyer-Moore"><a href="#Boyer-Moore" class="headerlink" title="Boyer-Moore"></a>Boyer-Moore</h2><ul><li>The longer the pattern is, the faster it works.</li><li>Starts from the end of pattern, while KMP starts from the beginning.</li><li>Works best for character string, while KMP works best for binary string.</li><li>KMP and Boyer-Moore<ul><li>Preprocessing existing patterns.</li><li>Searching patterns in input strings.</li></ul></li></ul><h2 id="Sunday"><a href="#Sunday" class="headerlink" title="Sunday"></a>Sunday</h2><h3 id="features"><a href="#features" class="headerlink" title="features"></a>features</h3><ul><li>simplification of the Boyer-Moore algorithm;</li><li>uses only the bad-character shift;</li><li>easy to implement;</li><li>preprocessing phase in O(m+sigma) time and O(sigma) space complexity;</li><li>searching phase in O(mn) time complexity;</li><li>very fast in practice for short patterns and large alphabets.<h3 id="description"><a href="#description" class="headerlink" title="description"></a>description</h3>The Quick Search algorithm uses only the bad-character shift table (see chapter Boyer-Moore algorithm). After an attempt where the window is positioned on the text factor y[j .. j+m-1], the length of the shift is at least equal to one. So, the character y[j+m] is necessarily involved in the next attempt, and thus can be used for the bad-character shift of the current attempt.</li></ul><p>The bad-character shift of the present algorithm is slightly modified to take into account the last character of x as follows: for c in Sigma, qsBc[c]=min{i : 0  &lt; i leq m and x[m-i]=c} if c occurs in x, m+1 otherwise (thanks to Darko Brljak).</p><p>The preprocessing phase is in O(m+sigma) time and O(sigma) space complexity.</p><p>During the searching phase the comparisons between pattern and text characters during each attempt can be done in any order. The searching phase has a quadratic worst case time complexity but it has a good practical behaviour.</p><p>For instance,<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/string-matching-2.png" alt="string-matching-2"></p><p>In this example, t0, …, t4 =  a b c a b is the current text window that is compared with the pattern. Its suffix a b has matched, but the comparison c-a causes a mismatch. The bad-character heuristics of the Boyer-Moore algorithm (a) uses the “bad” text character c to determine the shift distance. The Horspool algorithm (b) uses the rightmost character b of the current text window. The Sunday algorithm (c) uses the character directly right of the text window, namely d in this example. Since d does not occur in the pattern at all, the pattern can be shifted past this position.</p><p>python implementation<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">''' mbinary</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string"># File : sunday.py</span></span><br><span class="line"><span class="string"># Author: mbinary</span></span><br><span class="line"><span class="string"># Mail: zhuheqin1@gmail.com</span></span><br><span class="line"><span class="string"># Blog: https://mbinary.github.io</span></span><br><span class="line"><span class="string"># Github: https://github.com/mbinary</span></span><br><span class="line"><span class="string"># Created Time: 2018-07-11  15:26</span></span><br><span class="line"><span class="string"># Description: 字符串模式匹配, sunday 算法, kmp 的改进</span></span><br><span class="line"><span class="string">#               pattern matching for strings using sunday algorithm</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getPos</span><span class="params">(pattern)</span>:</span></span><br><span class="line">    dic = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> i,j <span class="keyword">in</span> enumerate(pattern[::<span class="number">-1</span>]):</span><br><span class="line">        <span class="keyword">if</span> j <span class="keyword">not</span> <span class="keyword">in</span> dic:</span><br><span class="line">            dic[j]= i</span><br><span class="line">    <span class="keyword">return</span> dic</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find</span><span class="params">(s,p)</span>:</span></span><br><span class="line">    dic = getPos(p)</span><br><span class="line">    ps = pp = <span class="number">0</span></span><br><span class="line">    ns = len(s)</span><br><span class="line">    np = len(p)</span><br><span class="line">    <span class="keyword">while</span> ps&lt;ns <span class="keyword">and</span> pp&lt;np:</span><br><span class="line">        <span class="keyword">if</span> s[ps] == p[pp]:</span><br><span class="line">            ps,pp = ps+<span class="number">1</span>,pp+<span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            idx = ps+ np-pp</span><br><span class="line">            <span class="keyword">if</span> idx &gt;=ns:<span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line">            ch = s[idx]</span><br><span class="line">            <span class="keyword">if</span> ch <span class="keyword">in</span> dic:</span><br><span class="line">                ps += dic[ch]+<span class="number">1</span>-pp</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                ps = idx+<span class="number">1</span></span><br><span class="line">            pp = <span class="number">0</span></span><br><span class="line">    <span class="keyword">if</span> pp==np:<span class="keyword">return</span> ps-np</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">findAll</span><span class="params">(s,p)</span>:</span></span><br><span class="line">    ns = len(s)</span><br><span class="line">    np = len(p)</span><br><span class="line">    i = <span class="number">0</span></span><br><span class="line">    ret = []</span><br><span class="line">    <span class="keyword">while</span> s:</span><br><span class="line">        print(s,p)</span><br><span class="line">        tmp = find(s,p)</span><br><span class="line">        <span class="keyword">if</span> tmp==<span class="number">-1</span>: <span class="keyword">break</span></span><br><span class="line">        ret.append(i+tmp)</span><br><span class="line">        end = tmp+np</span><br><span class="line">        i +=end</span><br><span class="line">        s = s[end:]</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">randStr</span><span class="params">(n=<span class="number">3</span>)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> [randint(ord(<span class="string">'a'</span>),ord(<span class="string">'z'</span>)) <span class="keyword">for</span> i <span class="keyword">in</span> range(n)]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test</span><span class="params">(n)</span>:</span></span><br><span class="line">    s = randStr(n)</span><br><span class="line">    p = randStr(<span class="number">3</span>)</span><br><span class="line">    str_s = <span class="string">''</span>.join((chr(i) <span class="keyword">for</span> i <span class="keyword">in</span> s))</span><br><span class="line">    str_p = <span class="string">''</span>.join((chr(i) <span class="keyword">for</span> i <span class="keyword">in</span> p))</span><br><span class="line">    n1 = find(s,p)</span><br><span class="line">    n2 = str_s.find(str_p) <span class="comment"># 利用已有的 str find 算法检验</span></span><br><span class="line">    <span class="keyword">if</span> n1!=n2:</span><br><span class="line">        print(n1,n2,str_p,str_s)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">True</span></span><br><span class="line"><span class="keyword">if</span> __name__ ==<span class="string">'__main__'</span>:</span><br><span class="line">    <span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line">    n = <span class="number">1000</span></span><br><span class="line">    suc = sum(test(n) <span class="keyword">for</span> i <span class="keyword">in</span> range(n))</span><br><span class="line">    print(<span class="string">'test &#123;n&#125; times, success &#123;suc&#125; times'</span>.format(n=n,suc=suc))</span><br></pre></td></tr></table></figure></p><h1 id="WildCard-matching"><a href="#WildCard-matching" class="headerlink" title="WildCard matching"></a>WildCard matching</h1><p> <img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/divider.png" alt="divider"><br>wild card:</p><ul><li><code>*</code>  matches 0 or any chars</li><li><code>?</code> matches any single char.</li></ul><p>Given a string <code>s</code> which doesn’t include wild card,<br>    and a pattern <code>p</code> which includes wild card,</p><p>Judge if they are matching.</p><h2 id="Idea"><a href="#Idea" class="headerlink" title="Idea"></a>Idea</h2><p>Using dynamic programming.</p><p>n = length(s),  m = length(p)</p><p>dp[m+1][n+1]:  bool</p><p>i:n,  j:m<br>dp[j][i] represents if s[:i+1] matches p[:j+1]</p><p>initialzation :<br>dp[0][0] = True, dp[0][i],dp[j][0] = False, only if p startswith ‘*’,  dp[1][0] = True.</p><p>if   p[j] = ‘*’: dp[j][i] = dp[j-1][i] or dp[j][i-1]<br>elif p[j] = ‘?’: dp[j][i] = dp[j-1][i-1]<br>else           : dp[j][i] = dp[j-1][i-1] and s[i] == p[j]</p><h2 id="Code"><a href="#Code" class="headerlink" title="Code"></a>Code</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">isMatch</span><span class="params">(self, s, p)</span>:</span></span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    :type s: str</span></span><br><span class="line"><span class="string">    :type p: str   pattern str including wildcard</span></span><br><span class="line"><span class="string">    :rtype: bool</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    n,m = len(s),len(p)</span><br><span class="line">    last =  [<span class="keyword">False</span>]*(n+<span class="number">1</span>)</span><br><span class="line">    last[<span class="number">0</span>] = <span class="keyword">True</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> range(m):</span><br><span class="line">        <span class="keyword">if</span> p[j]==<span class="string">'*'</span>:</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">                last[i+<span class="number">1</span>] = last[i+<span class="number">1</span>] <span class="keyword">or</span> last[i]</span><br><span class="line">        <span class="keyword">elif</span> p[j]==<span class="string">'?'</span>:</span><br><span class="line">            last.pop()</span><br><span class="line">            last.insert(<span class="number">0</span>,<span class="keyword">False</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            li = [<span class="keyword">False</span>]</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">                li.append( last[i] <span class="keyword">and</span> p[j]==s[i])</span><br><span class="line">            last = li</span><br><span class="line">    <span class="keyword">return</span> last[<span class="number">-1</span>]</span><br></pre></td></tr></table></figure><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference:"></a>Reference:</h1><ol><li>Xuyun, ppt, String matching</li><li><a href="http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/sunday.htm" target="_blank" rel="noopener">Sunday-algorithm</a></li><li>GeeksforGeeks, <a href="https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/" target="_blank" rel="noopener">KMP Algorithm</a></li><li>Augustliang, csdn, <a href="https://blog.csdn.net/glDemo/article/details/47678159" target="_blank" rel="noopener">dynamic programming for wild card matching</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;See more on &lt;a href=&quot;https://github.com/mbinary/algorithm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this article, I will show you some kinds of popular string matching algorithm and  dynamic programming  algorithm for wildcard matching.&lt;/p&gt;
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="https://mbinary.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="字符串匹配" scheme="https://mbinary.xyz/tags/%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8C%B9%E9%85%8D/"/>
    
      <category term="KMP" scheme="https://mbinary.xyz/tags/KMP/"/>
    
      <category term="动态规划" scheme="https://mbinary.xyz/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
    
  </entry>
  
  <entry>
    <title>图算法</title>
    <link href="https://mbinary.xyz/graph.html"/>
    <id>https://mbinary.xyz/graph.html</id>
    <published>2018-09-06T19:10:00.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-图">1. 图</a><ul><li><a href="#11-概念">1.1. 概念</a><ul><li><a href="#111-性质">1.1.1. 性质</a></li></ul></li><li><a href="#12-图的表示">1.2. 图的表示</a></li><li><a href="#13-树">1.3. 树</a></li></ul></li><li><a href="#2-图的搜索">2. 图的搜索</a><ul><li><a href="#21-bfs">2.1. BFS</a></li><li><a href="#22-dfs">2.2. DFS</a><ul><li><a href="#221-dfs-的性质">2.2.1. DFS 的性质</a></li></ul></li><li><a href="#23-拓扑排序">2.3. 拓扑排序</a></li><li><a href="#24-强连通分量">2.4. 强连通分量</a></li></ul></li><li><a href="#3-最小生成树">3. 最小生成树</a><ul><li><a href="#31-kruskal-算法">3.1. Kruskal 算法</a></li><li><a href="#32-prim-算法">3.2. Prim 算法</a></li></ul></li><li><a href="#4-单源最短路">4. 单源最短路</a><ul><li><a href="#41-最短路的子路径也是最短路径">4.1. 最短路的子路径也是最短路径</a></li><li><a href="#42-负权重的边">4.2. 负权重的边</a></li><li><a href="#43-初始化">4.3. 初始化</a></li><li><a href="#44-松弛操作">4.4. 松弛操作</a></li><li><a href="#45-有向无环图的单源最短路问题">4.5. 有向无环图的单源最短路问题</a></li><li><a href="#46-bellman-ford-算法">4.6. Bellman-Ford 算法</a></li><li><a href="#47-dijkstra-算法">4.7. Dijkstra 算法</a></li></ul></li><li><a href="#5-所有结点对的最短路问题">5. 所有结点对的最短路问题</a><ul><li><a href="#51-矩阵乘法">5.1. 矩阵乘法</a></li><li><a href="#52-floyd-warshall-算法">5.2. Floyd-Warshall 算法</a></li><li><a href="#53-johnson-算法">5.3. Johnson 算法</a></li></ul></li><li><a href="#6-最大流">6. 最大流</a><ul><li><a href="#61-最大流最小截定理">6.1. 最大流最小截定理</a></li><li><a href="#62-多个源汇">6.2. 多个源,汇</a></li><li><a href="#63-ford-fulkerson-方法">6.3. Ford-Fulkerson 方法</a><ul><li><a href="#631-残存网络">6.3.1. 残存网络</a></li><li><a href="#632-增广路径">6.3.2. 增广路径</a></li><li><a href="#633-割">6.3.3. 割</a></li></ul></li><li><a href="#64-基本的-ford-fulkerson算法">6.4. 基本的 Ford-Fulkerson算法</a></li><li><a href="#65-tbd">6.5. TBD</a></li></ul></li><li><a href="#7-参考资料">7. 参考资料</a></li></ul><!-- /TOC --><p><a id="markdown-1-图" name="1-图"></a></p><h1 id="1-图"><a href="#1-图" class="headerlink" title="1. 图"></a>1. 图</h1><p><a id="markdown-11-概念" name="11-概念"></a></p><h2 id="1-1-概念"><a href="#1-1-概念" class="headerlink" title="1.1. 概念"></a>1.1. 概念</h2><ul><li>顶</li><li>顶点的度 d</li><li>边</li><li>相邻</li><li>重边</li><li>环</li><li>完全图: 所有顶都相邻</li><li>二分图: $V(G) = X \cup Y, X\cap Y = \varnothing$, X中, Y 中任两顶不相邻</li><li>轨道</li><li>圈<br><a id="markdown-111-性质" name="111-性质"></a></li></ul><h3 id="1-1-1-性质"><a href="#1-1-1-性质" class="headerlink" title="1.1.1. 性质"></a>1.1.1. 性质</h3><ul><li>$\sum_{v\in V} d(v) = 2|E|$</li><li>G是二分图 $\Leftrightarrow$ G无奇圈</li><li>树是无圈连通图</li><li>树中, $|E| = |V| -1$<br><a id="markdown-12-图的表示" name="12-图的表示"></a></li></ul><h2 id="1-2-图的表示"><a href="#1-2-图的表示" class="headerlink" title="1.2. 图的表示"></a>1.2. 图的表示</h2><ul><li>邻接矩阵</li><li>邻接链表<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-1.png" alt="graph-1"></li></ul><p><a id="markdown-13-树" name="13-树"></a></p><h2 id="1-3-树"><a href="#1-3-树" class="headerlink" title="1.3. 树"></a>1.3. 树</h2><p>无圈连通图, $E = V-1$, 详细见<a href="/tree.html">树</a>, </p><p><a id="markdown-2-图的搜索" name="2-图的搜索"></a></p><h1 id="2-图的搜索"><a href="#2-图的搜索" class="headerlink" title="2. 图的搜索"></a>2. 图的搜索</h1><p>Introduction to algorithm<sup><a href="#fn_1" id="reffn_1">1</a></sup><br><a id="markdown-21-bfs" name="21-bfs"></a></p><h2 id="2-1-BFS"><a href="#2-1-BFS" class="headerlink" title="2.1. BFS"></a>2.1. BFS</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> v <span class="keyword">in</span> V:</span><br><span class="line">    v.d = MAX</span><br><span class="line">    v.pre = <span class="keyword">None</span></span><br><span class="line">    v.isFind = <span class="keyword">False</span></span><br><span class="line">root. isFind = <span class="keyword">True</span></span><br><span class="line">root.d = <span class="number">0</span></span><br><span class="line">que = [root]</span><br><span class="line"><span class="keyword">while</span> que !=[]:</span><br><span class="line">    nd = que.pop(<span class="number">0</span>)</span><br><span class="line">    <span class="keyword">for</span> v <span class="keyword">in</span> Adj(nd):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> v.isFind :</span><br><span class="line">            v.d = nd.d+<span class="number">1</span></span><br><span class="line">            v.pre = nd</span><br><span class="line">            v.isFind = <span class="keyword">True</span></span><br><span class="line">            que.append(v)</span><br></pre></td></tr></table></figure><p>时间复杂度 $O(V+E)$<br><a id="markdown-22-dfs" name="22-dfs"></a></p><h2 id="2-2-DFS"><a href="#2-2-DFS" class="headerlink" title="2.2. DFS"></a>2.2. DFS</h2><p>$\Theta(V+E)$<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dfs</span><span class="params">(G)</span>:</span></span><br><span class="line">    time = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> v <span class="keyword">in</span> V:</span><br><span class="line">        v.pre = <span class="keyword">None</span></span><br><span class="line">        v.isFind = <span class="keyword">False</span></span><br><span class="line">    <span class="keyword">for</span> v <span class="keyword">in</span> V : <span class="comment"># note this, </span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> v.isFind:</span><br><span class="line">            dfsVisit(v)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">dfsVisit</span><span class="params">(G,u)</span>:</span></span><br><span class="line">        time =time+<span class="number">1</span></span><br><span class="line">        u.begin = time</span><br><span class="line">        u.isFind = <span class="keyword">True</span></span><br><span class="line">        <span class="keyword">for</span> v <span class="keyword">in</span> Adj(u):</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> v.isFind:</span><br><span class="line">                v.pre = u</span><br><span class="line">                dfsVisit(G,v)</span><br><span class="line">        time +=<span class="number">1</span></span><br><span class="line">        u.end = time</span><br></pre></td></tr></table></figure></p><p>begin, end 分别是结点的发现时间与完成时间<br><a id="markdown-221-dfs-的性质" name="221-dfs-的性质"></a></p><h3 id="2-2-1-DFS-的性质"><a href="#2-2-1-DFS-的性质" class="headerlink" title="2.2.1. DFS 的性质"></a>2.2.1. DFS 的性质</h3><ul><li>其生成的前驱子图$G_{pre}$ 形成一个由多棵树构成的森林, 这是因为其与 dfsVisit 的递归调用树相对应</li><li>括号化结构<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-2.png" alt="graph-2"></li><li>括号化定理:<br>  考察两个结点的发现时间与结束时间的区间 [u,begin,u.end] 与 [v.begin,v.end]<ul><li>如果两者没有交集, 则两个结点在两个不同的子树上(递归树)</li><li>如果 u 的区间包含在 v 的区间, 则 u 是v 的后代</li></ul></li></ul><p><a id="markdown-23-拓扑排序" name="23-拓扑排序"></a></p><h2 id="2-3-拓扑排序"><a href="#2-3-拓扑排序" class="headerlink" title="2.3. 拓扑排序"></a>2.3. 拓扑排序</h2><p>利用 DFS, 结点的完成时间的逆序就是拓扑排序</p><p>同一个图可能有不同的拓扑排序<br><a id="markdown-24-强连通分量" name="24-强连通分量"></a></p><h2 id="2-4-强连通分量"><a href="#2-4-强连通分量" class="headerlink" title="2.4. 强连通分量"></a>2.4. 强连通分量</h2><p>在有向图中, 强连通分量中的结点互达<br>定义 $Grev$ 为 $G$ 中所有边反向后的图</p><p>将图分解成强连通分量的算法<br>在 Grev 上根据 G 中结点的拓扑排序来 dfsVisit, 即<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">compute Grev</span><br><span class="line">initalization</span><br><span class="line"><span class="keyword">for</span> v <span class="keyword">in</span> topo-sort(G.V):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> v.isFind: dfsVisit(Grev,v)</span><br></pre></td></tr></table></figure></p><p>然后得到的DFS 森林(也是递归树森林)中每个树就是一个强连通分量</p><p><a id="markdown-3-最小生成树" name="3-最小生成树"></a></p><h1 id="3-最小生成树"><a href="#3-最小生成树" class="headerlink" title="3. 最小生成树"></a>3. 最小生成树</h1><p>利用了贪心算法,<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Generate-Minimum-spanning-tree(G)</span><br><span class="line">    A = []</span><br><span class="line">    <span class="keyword">while</span> len(A)!=len(G.V)<span class="number">-1</span>:</span><br><span class="line">        add a safe edge <span class="keyword">for</span> A to A</span><br><span class="line">    <span class="keyword">return</span> A</span><br></pre></td></tr></table></figure></p><p><a id="markdown-31-kruskal-算法" name="31-kruskal-算法"></a></p><h2 id="3-1-Kruskal-算法"><a href="#3-1-Kruskal-算法" class="headerlink" title="3.1. Kruskal 算法"></a>3.1. Kruskal 算法</h2><p>总体上, 从最开始 每个结点就是一颗树的森林中(不相交集合, 并查集), 逐渐添加不形成圈的(两个元素不再同一个集合),最小边权的边.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">edges=[]</span><br><span class="line"><span class="keyword">for</span>  edge <span class="keyword">as</span> u,v <span class="keyword">in</span> sorted(G.E):</span><br><span class="line">    <span class="keyword">if</span> find-set(u) != find-set(v):</span><br><span class="line">        edges.append(edge)</span><br><span class="line">        union(u,v)</span><br><span class="line"><span class="keyword">return</span> edges</span><br></pre></td></tr></table></figure></p><p>如果并查集的实现采用了 按秩合并与路径压缩技巧, 则 find 与 union 的时间接近常数<br>所以时间复杂度在于排序边, 即 $O(ElgE)$, 而 $E\lt V^2$, 所以 $lgE = O(lgV)$, 时间复杂度为  $O(ElgV)$<br><a id="markdown-32-prim-算法" name="32-prim-算法"></a></p><h2 id="3-2-Prim-算法"><a href="#3-2-Prim-算法" class="headerlink" title="3.2. Prim 算法"></a>3.2. Prim 算法</h2><p>用了 BFS, 类似 Dijkstra 算法<br>从根结点开始 BFS, 一直保持成一颗树<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> v <span class="keyword">in</span> V: </span><br><span class="line">    v.minAdjEdge = MAX</span><br><span class="line">    v.pre = <span class="keyword">None</span></span><br><span class="line">root.minAdjEdge = <span class="number">0</span></span><br><span class="line">que = priority-queue (G.V)  <span class="comment"># sort by minAdjEdge</span></span><br><span class="line"><span class="keyword">while</span> <span class="keyword">not</span> que.isempty():</span><br><span class="line">    u = que.extractMin()</span><br><span class="line">    <span class="keyword">for</span> v <span class="keyword">in</span> Adj(u):</span><br><span class="line">        <span class="keyword">if</span> v <span class="keyword">in</span> que <span class="keyword">and</span> v.minAdjEdge&gt;w(u,v):</span><br><span class="line">            v.pre = u</span><br><span class="line">            v.minAdjEdge = w(u,v)</span><br></pre></td></tr></table></figure></p><ul><li>建堆 $O(V)$ <code>//note it&#39;s v, not vlgv</code></li><li>主循环中<ul><li>extractMin:  $O(VlgV)$</li><li>in 操作 可以另设标志位, 在常数时间完成, 总共 $O(E)$</li><li>设置结点的 minAdjEdge, 需要$O(lgv)$, 循环 E 次,则 总共$O(ElgV)$</li></ul></li></ul><p>综上, 时间复杂度为$O(ElgV)$<br>如果使用的是 <a href="/fib-heap.html">斐波那契堆</a>, 在  设置 minAdjEdge时 调用 <code>decrease-key</code>, 这个操作摊还代价为 $O(1)$, 所以时间复杂度可改进到 $O(E+VlgV)$</p><p><a id="markdown-4-单源最短路" name="4-单源最短路"></a></p><h1 id="4-单源最短路"><a href="#4-单源最短路" class="headerlink" title="4. 单源最短路"></a>4. 单源最短路</h1><p>求一个结点到其他结点的最短路径, 可以用 Bellman-ford算法, 或者 Dijkstra算法.<br>定义两个结点u,v间的最短路 </p><script type="math/tex; mode=display">\delta(u,v) = \begin{cases}\min(w(path)),\quad u\xrightarrow{path} v\\\infty, \quad u\nrightarrow v\end{cases}</script><p>问题的变体</p><ul><li>单目的地最短路问题: 可以将所有边反向转换成求单源最短路问题</li><li>单结点对的最短路径</li><li>所有结点对最短路路径</li></ul><p><a id="markdown-41-最短路的子路径也是最短路径" name="41-最短路的子路径也是最短路径"></a></p><h2 id="4-1-最短路的子路径也是最短路径"><a href="#4-1-最短路的子路径也是最短路径" class="headerlink" title="4.1. 最短路的子路径也是最短路径"></a>4.1. 最短路的子路径也是最短路径</h2><p>$p=(v_0,v_1,\ldots,v_k)$为从结点$v_0$到$v_k$的一条最短路径,  对于任意$0\le i\le j \le k$, 记$p_{ij}=(v_i,v_{i+1},\ldots,v_j)$为 p 中 $v_i$到$v_j$的子路径, 则 $p_{ij}$为 $v_i$到$v_j$的一条最短路径</p><p><a id="markdown-42-负权重的边" name="42-负权重的边"></a></p><h2 id="4-2-负权重的边"><a href="#4-2-负权重的边" class="headerlink" title="4.2. 负权重的边"></a>4.2. 负权重的边</h2><p>Dijkstra 算法不能处理负值边, 只能用 Bellman-Ford 算法,<br>而且如果有负值圈, 则没有最短路,  bellman-ford算法也可以检测出来<br><a id="markdown-43-初始化" name="43-初始化"></a></p><h2 id="4-3-初始化"><a href="#4-3-初始化" class="headerlink" title="4.3. 初始化"></a>4.3. 初始化</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">initialaize</span><span class="params">(G,s)</span>:</span></span><br><span class="line">    <span class="keyword">for</span> v <span class="keyword">in</span> G.V:</span><br><span class="line">        v.pre = <span class="keyword">None</span></span><br><span class="line">        v.distance = MAX</span><br><span class="line">    s.distance = <span class="number">0</span></span><br></pre></td></tr></table></figure><p><a id="markdown-44-松弛操作" name="44-松弛操作"></a></p><h2 id="4-4-松弛操作"><a href="#4-4-松弛操作" class="headerlink" title="4.4. 松弛操作"></a>4.4. 松弛操作</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">relax</span><span class="params">(u,v,w)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> v.distance &gt; u.distance + w:</span><br><span class="line">        v.distance = u.distance + w:</span><br><span class="line">         v.pre = u</span><br></pre></td></tr></table></figure><p>性质</p><ul><li>三角不等式: $\delta(s,v) \leqslant \delta(s,u) +  w(u,v)$</li><li>上界: $v.distance \geqslant \delta(s,v)$</li><li>收敛: 对于某些结点u,v  如果s-&gt;…-&gt;u-&gt;v是图G中的一条最短路径，并且在对边，进行松弛前任意时间有 $u.distance=\delta(s,u)$则在之后的所有时间有 $v.distance=\delta(s,v)$</li><li>路径松弛性质: 如果$p=v_0 v_1 \ldots v_k$是从源结点下v0到结点vk的一条最短路径，并且对p中的边所进行松弛的次序为$(v_0,v_1),(v_1,v_2), \ldots ,(v_{k-1},v_k)$, 则 $v_k.distance = \delta(s,v_k)$<br>该性质的成立与任何其他的松弛操作无关，即使这些松弛操作是与对p上的边所进行的松弛操作穿插进行的。</li></ul><p>证明<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-3.png" alt="graph-3"></p><p><a id="markdown-45-有向无环图的单源最短路问题" name="45-有向无环图的单源最短路问题"></a></p><h2 id="4-5-有向无环图的单源最短路问题"><a href="#4-5-有向无环图的单源最短路问题" class="headerlink" title="4.5. 有向无环图的单源最短路问题"></a>4.5. 有向无环图的单源最短路问题</h2><p>$\Theta(V+E)$<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dag</span>-<span class="title">shortest</span>-<span class="title">path</span><span class="params">(G,s)</span>:</span></span><br><span class="line">    initialize(G,s)</span><br><span class="line">    <span class="keyword">for</span> u <span class="keyword">in</span> topo-sort(G.V):</span><br><span class="line">        <span class="keyword">for</span> v <span class="keyword">in</span> Adj(v):</span><br><span class="line">            relax(u,v,w(u,v))</span><br></pre></td></tr></table></figure></p><p><a id="markdown-46-bellman-ford-算法" name="46-bellman-ford-算法"></a></p><h2 id="4-6-Bellman-Ford-算法"><a href="#4-6-Bellman-Ford-算法" class="headerlink" title="4.6. Bellman-Ford 算法"></a>4.6. Bellman-Ford 算法</h2><p>$O(VE)$<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bellman</span>-<span class="title">ford</span><span class="params">(G,s)</span>:</span></span><br><span class="line">    initialize(G,s)</span><br><span class="line">    <span class="keyword">for</span> ct <span class="keyword">in</span> range(|V|<span class="number">-1</span>): <span class="comment"># v-1 times</span></span><br><span class="line">        <span class="keyword">for</span> u,v <span class="keyword">as</span> edge <span class="keyword">in</span> E:</span><br><span class="line">            relax(u,v,w(u,v))</span><br><span class="line">    <span class="keyword">for</span> u,v <span class="keyword">as</span> edge <span class="keyword">in</span> E:</span><br><span class="line">        <span class="keyword">if</span> v.distance &gt; u.distance + w(u,v):</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">True</span></span><br></pre></td></tr></table></figure></p><p>第一个 for 循环就是进行松弛操作, 最后结果已经存储在 结点的distance 和 pre 属性中了, 第二个 for 循环利用三角不等式检查有不有负值圈.</p><p>下面是证明该算法的正确性<img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-4.png" alt="graph-4"></p><p><a id="markdown-47-dijkstra-算法" name="47-dijkstra-算法"></a></p><h2 id="4-7-Dijkstra-算法"><a href="#4-7-Dijkstra-算法" class="headerlink" title="4.7. Dijkstra 算法"></a>4.7. Dijkstra 算法</h2><p>$O(ElogV)$, 要求不能有负值边</p><p>Dijkstra算法既类似于广度优先搜索（，也有点类似于计算最小生成树的Prim算法。它与广度优先搜索的类似点在于集合S对应的是广度优先搜索中的黑色结点集合：正如集合S中的结点的最短路径权重已经计算出来一样，在广度优先搜索中，黑色结点的正确的广度优先距离也已经计算出来。Dijkstra算法像Prim算法的地方是，两个算法都使用最小优先队列来寻找给定集合（Dijkstra算法中的S集合与Prim算法中逐步增长的树）之外的“最轻”结点，将该结点加入到集合里，并对位于集合外面的结点的权重进行相应调整。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dijkstra</span><span class="params">(G,s)</span>:</span></span><br><span class="line">    initialize(G,s)</span><br><span class="line">    paths=[]</span><br><span class="line">    q = priority-queue(G.V) <span class="comment"># sort by distance</span></span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">not</span> q.empty():</span><br><span class="line">        u = q.extract-min()</span><br><span class="line">        paths.append(u)</span><br><span class="line">        <span class="keyword">for</span> v <span class="keyword">in</span> Adj(u):</span><br><span class="line">            relax(u,v,w(u,v))</span><br></pre></td></tr></table></figure><p><a id="markdown-5-所有结点对的最短路问题" name="5-所有结点对的最短路问题"></a></p><h1 id="5-所有结点对的最短路问题"><a href="#5-所有结点对的最短路问题" class="headerlink" title="5. 所有结点对的最短路问题"></a>5. 所有结点对的最短路问题</h1><p><a id="markdown-51-矩阵乘法" name="51-矩阵乘法"></a></p><h2 id="5-1-矩阵乘法"><a href="#5-1-矩阵乘法" class="headerlink" title="5.1. 矩阵乘法"></a>5.1. 矩阵乘法</h2><p>使用动态规划算法, 可以得到最短路径的结构<br>设 $l_{ij}^{(m)}$为从结点i 到结点 j 的至多包含 m 条边的任意路径的最小权重,当m = 0, 此时i=j, 则 为0,<br>可以得到递归定义 </p><script type="math/tex; mode=display">l_{ij}^{(m)} =\min( l_{ij}^{(m-1)}, \min_{1\leqslant k\leqslant n}( l_{ik}^{(m-1)}+w_{kj})) =  \min_{1\leqslant k\leqslant n}( l_{ik}^{(m-1)}+w_{kj}))</script><p>由于对于所有 j, 有 $w_{jj}=0$,所以上式后面的等式成立.</p><p>由于是简单路径, 则包含的边最多为 |V|-1 条, 所以</p><script type="math/tex; mode=display">\delta(i,j) = l_{ij}^{(|V|-1)} = l_{ij}^{(|V|)} =l_{ij}^{(|V| + 1)}= ...</script><p>所以可以从自底向上计算, 如下<br>输入权值矩阵 $W(w_{ij})),  L^{(m-1)}$,输出$ L^{(m)}$,  其中 $L^{(1)} = W$,<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span>  <span class="title">f</span><span class="params">(L, W)</span>:</span></span><br><span class="line">  n = L.rows</span><br><span class="line">  L_new = new matrix(row=n ,col = n)</span><br><span class="line">  <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">      <span class="keyword">for</span> j <span class="keyword">in</span> range(n):</span><br><span class="line">          L_new[i][j] = MAX</span><br><span class="line">          <span class="keyword">for</span> k <span class="keyword">in</span> range(n):</span><br><span class="line">              L_new[i][j] = min(L_new[i][j], L[i][k]+w[k][j])</span><br><span class="line">  <span class="keyword">return</span> L_new</span><br></pre></td></tr></table></figure></p><p>可以看出该算法与矩阵乘法的关系<br>$L^{(m)} = W^m$,<br>所以可以直接计算乘法, 每次计算一个乘积是 $O(V^3)$, 计算 V 次, 所以总体 $O(V^4)$, 使用矩阵快速幂可以将时间复杂度降低为$O(V^3lgV)$<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f</span><span class="params">(W)</span>:</span></span><br><span class="line">    L = W</span><br><span class="line">    i = <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> i&lt;W.rows:</span><br><span class="line">        L = L*L</span><br><span class="line">        i*=<span class="number">2</span></span><br><span class="line">    <span class="keyword">return</span> L</span><br></pre></td></tr></table></figure></p><p><a id="markdown-52-floyd-warshall-算法" name="52-floyd-warshall-算法"></a></p><h2 id="5-2-Floyd-Warshall-算法"><a href="#5-2-Floyd-Warshall-算法" class="headerlink" title="5.2. Floyd-Warshall 算法"></a>5.2. Floyd-Warshall 算法</h2><p>同样要求可以存在负权边, 但不能有负值圈. 用动态规划算法:<br>设 $ d_{ij}^{(k)}$ 为 从 i 到 j 所有中间结点来自集合 ${\{1,2,\ldots,k\}}$ 的一条最短路径的权重. 则有</p><script type="math/tex; mode=display">d_{ij}^{(k)} = \begin{cases}w_{ij},\quad k=0\\min(d_{ij}^{(k-1)},d_{ik}^{(k-1)}+d_{kj}^{(k-1)}),\quad k\geqslant 1\end{cases}</script><p>而且为了找出路径, 需要记录前驱结点, 定义如下前驱矩阵 $\Pi$, 设 $ \pi_{ij}^{(k)}$ 为 从 i 到 j 所有中间结点来自集合 ${\{1,2,\ldots,k\}}$ 的最短路径上 j 的前驱结点<br>则</p><script type="math/tex; mode=display">\pi_{ij}^{(0)} = \begin{cases}nil,\quad i=j \ or \ w_{ij}=\infty \\i, \quad i\neq j\ and \ w_{ij}<\infty\end{cases}</script><p>对 $k\geqslant 1$</p><script type="math/tex; mode=display">\pi_{ij}^{(k)} = \begin{cases}\pi_{ij}^{(k-1)} ,\quad  d_{ij}^{(k-1)}\leqslant d_{ik}^{(k-1)}+d_{kj}^{(k-1)}\\\pi_{kj}^{(k-1)} ,\quad otherwise\end{cases}</script><p>由此得出此算法<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">floyd</span>-<span class="title">warshall</span><span class="params">(W)</span>:</span></span><br><span class="line">    n = len(W)</span><br><span class="line">    D= W</span><br><span class="line">    initialize pre</span><br><span class="line">    <span class="keyword">for</span> k <span class="keyword">in</span> range(n):</span><br><span class="line">        pre2 = pre.copy()</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> range(n)</span><br><span class="line">                <span class="keyword">if</span> d[i][j] &gt; d[i][k]+d[k][j]:</span><br><span class="line">                    d[i][j] =d[i][k]+d[k][j]</span><br><span class="line">                    pre2[i][j] = pre[k][j]</span><br><span class="line">        pre = pre2</span><br><span class="line"><span class="keyword">return</span> d,pre</span><br></pre></td></tr></table></figure></p><p><a id="markdown-53-johnson-算法" name="53-johnson-算法"></a></p><h2 id="5-3-Johnson-算法"><a href="#5-3-Johnson-算法" class="headerlink" title="5.3. Johnson 算法"></a>5.3. Johnson 算法</h2><p>思路是通过重新赋予权重, 将图中负权边转换为正权,然后就可以用 dijkstra 算法(要求是正值边)来计算一个结点到其他所有结点的, 然后对所有结点用dijkstra </p><ol><li>首先构造一个新图 G’<br>先将G拷贝到G’, 再添加一个新结点 s, 添加 G.V条边, s 到G中顶点的, 权赋值为 0</li><li>用 Bellman-Ford 算法检查是否有负值圈, 如果没有, 同时求出 $\delta(s,v) 记为 h(v)$</li><li>求新的非负值权, w’(u,v) = w(u,v)+h(u)-h(v)</li><li>对所有结点在 新的权矩阵w’上 用 Dijkstra 算法<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-5.png" alt="graph-5"></li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">JOHNSON (G, u) </span><br><span class="line"></span><br><span class="line">s = newNode</span><br><span class="line">G<span class="string">' = G.copy()</span></span><br><span class="line"><span class="string">G'</span>.addNode(s)</span><br><span class="line"><span class="keyword">for</span> v <span class="keyword">in</span> G.V: G<span class="string">'.addArc(s,v,w=0)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">if BELLMAN-FORD(G'</span> , w, s) ==FALSE </span><br><span class="line">    error <span class="string">"the input graph contains a negative-weight cycle"</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> v <span class="keyword">in</span> G<span class="string">'.V:</span></span><br><span class="line"><span class="string">    # computed by the bellman-ford algorithm, delta(s,v) is the shortest distance from s to v</span></span><br><span class="line"><span class="string">    h(v) = delta(s,v) </span></span><br><span class="line"><span class="string">for edge(u,v) in G'</span>.E:</span><br><span class="line">    w<span class="string">' = w(u,v)+h(u)-h(v)</span></span><br><span class="line"><span class="string">d = matrix(n,n)</span></span><br><span class="line"><span class="string">for u in G:</span></span><br><span class="line"><span class="string">    dijkstra(G,w'</span>,u) <span class="comment"># compute delta' for all v in G.V</span></span><br><span class="line">    <span class="keyword">for</span> v <span class="keyword">in</span> G.V:</span><br><span class="line">        d[u][v] = delta<span class="string">'(u,v) + h(v)-h(u)</span></span><br><span class="line"><span class="string">return d</span></span><br></pre></td></tr></table></figure><p><a id="markdown-6-最大流" name="6-最大流"></a></p><h1 id="6-最大流"><a href="#6-最大流" class="headerlink" title="6. 最大流"></a>6. 最大流</h1><p>G 是弱连通严格有向加权图, s为源, t 为汇, 每条边e容量 c(e), 由此定义了网络N(G,s,t,c(e)),</p><ul><li>流函数 $f(e):E \rightarrow R$<script type="math/tex; mode=display">\begin{aligned}(1)\quad & 0\leqslant f(e) \leqslant c(e),\quad e \in E\\(2)\quad & \sum_{e\in \alpha(v)} f(e)= \sum_{e\in \beta(v)}f(e),\quad  v \in V-\{s,t\}\end{aligned}</script>其中 $\alpha(v)$ 是以 v 为头的边集, $\beta(v)$是以 v 为尾的边集</li><li>流量: $F =  \sum_{e\in \alpha(t)} f(e)- \sum_{e\in -\beta(t)}f(e),$</li><li>截$(S,\overline S)$: $S\subset V,s\in S, t\in \overline S =V-S$</li><li>截量$C(S) = \sum_{e\in(S,\overline S)}c(e)$<br><a id="markdown-61-最大流最小截定理" name="61-最大流最小截定理"></a><h2 id="6-1-最大流最小截定理"><a href="#6-1-最大流最小截定理" class="headerlink" title="6.1. 最大流最小截定理"></a>6.1. 最大流最小截定理</h2>&lt;&lt;图论&gt;&gt; 王树禾<sup><a href="#fn_2" id="reffn_2">2</a></sup></li><li>对于任一截$(S,\overline S)$, 有 $F =  \sum_{e\in (S,\overline S)} f(e)- \sum_{e\in(\overline S,S)}f(e),$<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/prove.png" alt="prove"></li><li>$F\leqslant C(S)$<br>证明: 由上面定理<script type="math/tex; mode=display">F =  \sum_{e\in (S,\overline S)} f(e)- \sum_{e\in(\overline S,S)}f(e),</script>而 $0\leqslant f(e) \leqslant c(e)$, 则<script type="math/tex; mode=display">F\leqslant \sum_{e\in (S,\overline S)} f(e) \leqslant \sum_{e\in (S,\overline S)} c(e) = C(S)</script></li><li>最大流,最小截: 若$F= C(S) $, 则F’是最大流量, C(S) 是最小截量<br><a id="markdown-62-多个源汇" name="62-多个源汇"></a><h2 id="6-2-多个源-汇"><a href="#6-2-多个源-汇" class="headerlink" title="6.2. 多个源,汇"></a>6.2. 多个源,汇</h2>可以新增一个总的源,一个总的汇,<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-6.png" alt="graph-6"></li></ul><p><a id="markdown-63-ford-fulkerson-方法" name="63-ford-fulkerson-方法"></a></p><h2 id="6-3-Ford-Fulkerson-方法"><a href="#6-3-Ford-Fulkerson-方法" class="headerlink" title="6.3. Ford-Fulkerson 方法"></a>6.3. Ford-Fulkerson 方法</h2><p>由于其实现可以有不同的运行时间, 所以称其为方法, 而不是算法.<br>思路是 循环增加流的值, 在一个关联的”残存网络” 中寻找一条”增广路径”, 然后对这些边进行修改流量. 重复直至残存网络上不再存在增高路径为止.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ford</span>-<span class="title">fulkerson</span><span class="params">(G,s,t)</span>:</span></span><br><span class="line">    initialize flow f to <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> exists an augmenting path p <span class="keyword">in</span> residual network Gf:</span><br><span class="line">        augment flow f along p</span><br><span class="line">    <span class="keyword">return</span> f</span><br></pre></td></tr></table></figure></p><p><a id="markdown-631-残存网络" name="631-残存网络"></a></p><h3 id="6-3-1-残存网络"><a href="#6-3-1-残存网络" class="headerlink" title="6.3.1. 残存网络"></a>6.3.1. 残存网络</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-7.png" alt="graph-7"></p><p><a id="markdown-632-增广路径" name="632-增广路径"></a></p><h3 id="6-3-2-增广路径"><a href="#6-3-2-增广路径" class="headerlink" title="6.3.2. 增广路径"></a>6.3.2. 增广路径</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-8.png" alt="graph-8"><br><a id="markdown-633-割" name="633-割"></a></p><h3 id="6-3-3-割"><a href="#6-3-3-割" class="headerlink" title="6.3.3. 割"></a>6.3.3. 割</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/graph-9.png" alt="graph-9"><br><a id="markdown-64-基本的-ford-fulkerson算法" name="64-基本的-ford-fulkerson算法"></a></p><h2 id="6-4-基本的-Ford-Fulkerson算法"><a href="#6-4-基本的-Ford-Fulkerson算法" class="headerlink" title="6.4. 基本的 Ford-Fulkerson算法"></a>6.4. 基本的 Ford-Fulkerson算法</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ford</span>-<span class="title">fulkerson</span><span class="params">(G,s,t)</span>:</span></span><br><span class="line">    <span class="keyword">for</span> edge <span class="keyword">in</span> G.E: edge.f = <span class="number">0</span></span><br><span class="line">    while exists path p:s-&gt;t  in Gf:</span><br><span class="line">        cf(p) = min&#123;cf(u,v):(u,v) <span class="keyword">is</span> <span class="keyword">in</span> p&#125;</span><br><span class="line">        <span class="keyword">for</span> edge <span class="keyword">in</span> p:</span><br><span class="line">            <span class="keyword">if</span> edge  <span class="keyword">in</span> E:</span><br><span class="line">                edge.f +=cf(p)</span><br><span class="line">            <span class="keyword">else</span>: reverse_edge.f -=cf(p)</span><br></pre></td></tr></table></figure><p><a id="markdown-65-tbd" name="65-tbd"></a></p><h2 id="6-5-TBD"><a href="#6-5-TBD" class="headerlink" title="6.5. TBD"></a>6.5. TBD</h2><p><a id="markdown-7-参考资料" name="7-参考资料"></a></p><h1 id="7-参考资料"><a href="#7-参考资料" class="headerlink" title="7. 参考资料"></a>7. 参考资料</h1><blockquote id="fn_1"><sup>1</sup>. 算法导论<a href="#reffn_1" title="Jump back to footnote [1] in the text."> &#8617;</a></blockquote><blockquote id="fn_2"><sup>2</sup>. 图论, 王树禾<a href="#reffn_2" title="Jump back to footnote [2] in the text."> &#8617;</a></blockquote>]]></content>
    
    <summary type="html">
    
      算法导论上常用的图算法, 代码, 原理等
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="https://mbinary.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="图" scheme="https://mbinary.xyz/tags/%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>『数据结构』Fibonacci-heap</title>
    <link href="https://mbinary.xyz/fib-heap.html"/>
    <id>https://mbinary.xyz/fib-heap.html</id>
    <published>2018-09-06T19:09:00.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-结构">1. 结构</a></li><li><a href="#2-势函数">2. 势函数</a></li><li><a href="#3-最大度数">3. 最大度数</a></li><li><a href="#4-操作">4. 操作</a><ul><li><a href="#41-创建一个斐波那契堆">4.1. 创建一个斐波那契堆</a></li><li><a href="#42-插入一个结点">4.2. 插入一个结点</a></li><li><a href="#43-寻找最小结点">4.3. 寻找最小结点</a></li><li><a href="#44-合并两个斐波那契堆">4.4. 合并两个斐波那契堆</a></li><li><a href="#45-抽取最小值">4.5. 抽取最小值</a></li><li><a href="#46-关键字减值">4.6. 关键字减值</a></li><li><a href="#47-删除结点">4.7. 删除结点</a></li></ul></li><li><a href="#5-最大度数的证明">5. 最大度数的证明</a></li></ul><!-- /TOC --><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/fib-heap-1.png" alt="fib-heap-1"></p><p><a id="markdown-1-结构" name="1-结构"></a></p><h1 id="1-结构"><a href="#1-结构" class="headerlink" title="1. 结构"></a>1. 结构</h1><p>斐波那契堆是一系列具有最小堆序的有根树的集合, 同一代(层)结点由双向循环链表链接, <strong>为了便于删除最小结点, 还需要维持链表为升序, 即nd&lt;=nd.right(nd==nd.right时只有一个结点或为 None)</strong>, 父子之间都有指向对方的指针.</p><p>结点有degree 属性, 记录孩子的个数, mark 属性用来标记(为了满足势函数, 达到摊还需求的)</p><p>还有一个最小值指针 H.min 指向最小根结点<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/fib-heap-2.png" alt="fib-heap-2"></p><p><a id="markdown-2-势函数" name="2-势函数"></a></p><h1 id="2-势函数"><a href="#2-势函数" class="headerlink" title="2. 势函数"></a>2. 势函数</h1><p>下面用势函数来分析摊还代价, 如果你不明白, 可以看<a href="https://www.jianshu.com/p/052fbe9d92a4" target="_blank" rel="noopener">摊还分析</a></p><p>$\Phi(H) = t(H) + 2m(h)$<br>t 是根链表中树的数目,m(H) 表示被标记的结点数</p><p>最初没有结点<br><a id="markdown-3-最大度数" name="3-最大度数"></a></p><h1 id="3-最大度数"><a href="#3-最大度数" class="headerlink" title="3. 最大度数"></a>3. 最大度数</h1><p>结点的最大度数(即孩子数)$D(n)\leqslant \lfloor lgn \rfloor$, 证明放在最后<br><a id="markdown-4-操作" name="4-操作"></a></p><h1 id="4-操作"><a href="#4-操作" class="headerlink" title="4. 操作"></a>4. 操作</h1><p><a id="markdown-41-创建一个斐波那契堆" name="41-创建一个斐波那契堆"></a></p><h2 id="4-1-创建一个斐波那契堆"><a href="#4-1-创建一个斐波那契堆" class="headerlink" title="4.1. 创建一个斐波那契堆"></a>4.1. 创建一个斐波那契堆</h2><p>$O(1)$<br><a id="markdown-42-插入一个结点" name="42-插入一个结点"></a></p><h2 id="4-2-插入一个结点"><a href="#4-2-插入一个结点" class="headerlink" title="4.2. 插入一个结点"></a>4.2. 插入一个结点</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">nd = new node</span><br><span class="line">nd.prt = nd.chd = <span class="keyword">None</span></span><br><span class="line"><span class="keyword">if</span> H.min <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">    creat H <span class="keyword">with</span> nd</span><br><span class="line">    H.min = nd</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    insert nd into  H<span class="string">'s root list</span></span><br><span class="line"><span class="string">    if H.min&lt;nd: H.min = nd</span></span><br><span class="line"><span class="string">H.n +=1</span></span><br></pre></td></tr></table></figure><script type="math/tex; mode=display">\Delta \Phi = \Delta t(H) + 2\Delta m(H) = 1+0 = 1</script><p>摊还代价为$O(1)$<br><a id="markdown-43-寻找最小结点" name="43-寻找最小结点"></a></p><h2 id="4-3-寻找最小结点"><a href="#4-3-寻找最小结点" class="headerlink" title="4.3. 寻找最小结点"></a>4.3. 寻找最小结点</h2><p>直接用 H.min, $O(1)$<br><a id="markdown-44-合并两个斐波那契堆" name="44-合并两个斐波那契堆"></a></p><h2 id="4-4-合并两个斐波那契堆"><a href="#4-4-合并两个斐波那契堆" class="headerlink" title="4.4. 合并两个斐波那契堆"></a>4.4. 合并两个斐波那契堆</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">union</span><span class="params">(H1,H2)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> H1.min ==<span class="keyword">None</span> <span class="keyword">or</span> (H1.min <span class="keyword">and</span> H2.min <span class="keyword">and</span> H1.min&gt;H2.min):</span><br><span class="line">        H1.min = H2.min</span><br><span class="line">    link H2.rootList to H1.rootList </span><br><span class="line">    <span class="keyword">return</span> H1</span><br></pre></td></tr></table></figure><p>易知 $\Delta \Phi = 0$<br><a id="markdown-45-抽取最小值" name="45-抽取最小值"></a></p><h2 id="4-5-抽取最小值"><a href="#4-5-抽取最小值" class="headerlink" title="4.5. 抽取最小值"></a>4.5. 抽取最小值</h2><p>抽取最小值, 一定是在根结点, 然后将此根结点的所有子树的根放在 根结点双向循环链表中, 之后还要进行<strong>树的合并. 以使每个根结点的度不同,</strong><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">extract</span>-<span class="title">min</span><span class="params">(H)</span>:</span></span><br><span class="line">    z = H.min</span><br><span class="line">    <span class="keyword">if</span> z!=<span class="keyword">None</span>:</span><br><span class="line">        <span class="keyword">for</span> chd of z:</span><br><span class="line">            link chd to H.rootList</span><br><span class="line">            chd.prt = <span class="keyword">None</span></span><br><span class="line">        remove z <span class="keyword">from</span> the rootList of H</span><br><span class="line">        <span class="keyword">if</span> z==z.right:</span><br><span class="line">            H.min = <span class="keyword">None</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            H.min = z.right</span><br><span class="line">            consolidate(H)</span><br><span class="line">        H.n -=<span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> z</span><br></pre></td></tr></table></figure></p><p>consolidate 函数使用一个 辅助数组degree来记录所有根结点(不超过lgn)对应的度数, degree[i] = nd 表示.有且只有一个结点 nd 的度数为 i.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">consolidate</span><span class="params">(H)</span>:</span></span><br><span class="line">    initialize degree  <span class="keyword">with</span> <span class="keyword">None</span></span><br><span class="line">    <span class="keyword">for</span> nd <span class="keyword">in</span> H.rootList:</span><br><span class="line">        d = nd.degree</span><br><span class="line">        <span class="keyword">while</span> degree[d] !=<span class="keyword">None</span>:</span><br><span class="line">            nd2 = degree[d]</span><br><span class="line">            <span class="keyword">if</span> nd2.degree &lt; nd.degree:</span><br><span class="line">                nd2,nd = nd,nd2</span><br><span class="line"></span><br><span class="line">            make nd2 child of nd  </span><br><span class="line">            nd.degree = d+<span class="number">1</span></span><br><span class="line">            nd.mark = <span class="keyword">False</span> <span class="comment"># to balace the potential </span></span><br><span class="line"></span><br><span class="line">            remove nd2 <span class="keyword">from</span> H.rootList</span><br><span class="line">            degree[d] = <span class="keyword">None</span></span><br><span class="line">            d+=<span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>: degree[d] = nd</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> degree:</span><br><span class="line">        <span class="keyword">if</span> i!=<span class="keyword">None</span>: </span><br><span class="line">            link i to H.rootList</span><br><span class="line">            <span class="keyword">if</span> H.min ==<span class="keyword">None</span>: H.min = i</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> H.min&gt;i: H.min = i</span><br></pre></td></tr></table></figure></p><p>时间复杂度为$O(lgn)$ 即数组移动的长度, 而最多有 lgn个元素</p><p><a id="markdown-46-关键字减值" name="46-关键字减值"></a></p><h2 id="4-6-关键字减值"><a href="#4-6-关键字减值" class="headerlink" title="4.6. 关键字减值"></a>4.6. 关键字减值</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decrease</span>-<span class="title">key</span><span class="params">(H,x,k)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> k&gt;x.key: error </span><br><span class="line">    x.key = k</span><br><span class="line">    y=x.p</span><br><span class="line">    <span class="keyword">if</span> y!=<span class="keyword">None</span> <span class="keyword">and</span> x.key &lt; y.key:</span><br><span class="line">        cut(H,x,y)</span><br><span class="line">        cascading-cut(H,y)</span><br><span class="line">    <span class="keyword">if</span> x.key &lt; H.min.key:</span><br><span class="line">      H.min = x</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cut</span><span class="params">(H,x,y)</span>:</span></span><br><span class="line">    remove x <span class="keyword">from</span> the child list of y, decrementing y.degree</span><br><span class="line">    add x to H.rootList</span><br><span class="line">    x.prt = <span class="keyword">None</span></span><br><span class="line">     x.mark = <span class="keyword">False</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cascading</span>-<span class="title">cut</span><span class="params">(H,y)</span>:</span></span><br><span class="line">    z- y,prt</span><br><span class="line">    <span class="keyword">if</span> z !=<span class="keyword">None</span>:</span><br><span class="line">        <span class="keyword">if</span> y.mark ==<span class="keyword">False</span>:y.mark = <span class="keyword">True</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            cut(H,y,z)</span><br><span class="line">            cascading-cut(H,z)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/fib-heap-3.png" alt="fib-heap-3"></p><p><a id="markdown-47-删除结点" name="47-删除结点"></a></p><h2 id="4-7-删除结点"><a href="#4-7-删除结点" class="headerlink" title="4.7. 删除结点"></a>4.7. 删除结点</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">decrease(H,nd, MIN)</span><br><span class="line">extract-min(H)</span><br></pre></td></tr></table></figure><p><a id="markdown-5-最大度数的证明" name="5-最大度数的证明"></a></p><h1 id="5-最大度数的证明"><a href="#5-最大度数的证明" class="headerlink" title="5. 最大度数的证明"></a>5. 最大度数的证明</h1><p>这也是<code>斐波那契</code>这个名字的由来,<br>$D(n)\leqslant \lfloor lgn \rfloor$<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/fib-heap-4.png" alt="fib-heap-4"></p>]]></content>
    
    <summary type="html">
    
      介绍 fibnacci heap 的原理
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="数据结构" scheme="https://mbinary.xyz/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
      <category term="斐波那契堆" scheme="https://mbinary.xyz/tags/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E5%A0%86/"/>
    
  </entry>
  
  <entry>
    <title>『数据结构』B树(B-Tree)及其变体 B+树,B*树</title>
    <link href="https://mbinary.xyz/b-tree.html"/>
    <id>https://mbinary.xyz/b-tree.html</id>
    <published>2018-08-29T15:42:00.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-背景">1. 背景</a></li><li><a href="#2-定义">2. 定义</a></li><li><a href="#3-查找操作">3. 查找操作</a></li><li><a href="#4-插入操作">4. 插入操作</a></li><li><a href="#5-删除操作">5. 删除操作</a><ul><li><a href="#51-第一种方法">5.1. 第一种方法</a></li><li><a href="#52-第二种方法">5.2. 第二种方法</a></li></ul></li><li><a href="#6-b树">6. B+树</a></li><li><a href="#7-b树">7. B*树</a></li><li><a href="#8-代码实现与测试">8. 代码实现与测试</a><ul><li><a href="#81-测试">8.1. 测试</a></li><li><a href="#82-python-实现">8.2. python 实现</a></li></ul></li><li><a href="#9-参考资料">9. 参考资料</a></li></ul><!-- /TOC --><p><a id="markdown-1-背景" name="1-背景"></a></p><blockquote><blockquote><p>从此心里有了B数(●’◡’●)</p></blockquote></blockquote><h1 id="1-背景"><a href="#1-背景" class="headerlink" title="1. 背景"></a>1. 背景</h1><p>当有大量数据储存在磁盘时,如数据库的查找,插入, 删除等操作的实现,  如果要读取或者写入, 磁盘的寻道, 旋转时间很长, 远大于在 内存中的读取,写入时间. </p><p>平时用的二叉排序树搜索元素的时间复杂度虽然是 $O(log_2n)$的, 但是底数还是太小, 树高太高. </p><p>所以就出现了 B 树(英文为B-Tree, 不是B减树), 可以理解为多叉排序树.  一个结点可以有多个孩子, 于是增大了底数, 减小了高度, 虽然比较的次数多(关键字数多), 但是由于是在内存中比较, 相较于磁盘的读取还是很快的.<br><a id="markdown-2-定义" name="2-定义"></a></p><h1 id="2-定义"><a href="#2-定义" class="headerlink" title="2. 定义"></a>2. 定义</h1><p>度为 <strong>d</strong>(degree)的 B 树(阶(order) 为 2d)  定义如下, </p><ol><li><p>每个结点中包含有 n 个关键字信息： $(n,P_0,K_1,P_1,K_2,\ldots,K_n,P_n)$。其中：</p><pre><code>a)   $K_i$为关键字,且关键字按顺序升序排序 $K_{i-1}&lt; K_i$b)   $P_i$ 为指向子树根的接点, $K_{i-1}&lt;P(i-1) &lt; Ki$c)   关键字的数 n 满足(由此也确定了孩子结点的个数): $d-1\leqslant n \leqslant 2d-1$ (根节点可以少于d-1)</code></pre></li><li><p>树中每个结点最多含有 2d个孩子（d&gt;=2）；</p></li><li>除根结点和叶子结点外,其它每个结点至少有 d个孩子；</li><li>若根结点不是叶子结点,则至少有 2 个孩子（特殊情况：没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点）；</li><li><strong>所有叶子结点都出现在同一层</strong>,叶子节点没有孩子和指向孩子的指针</li></ol><p>性质:<br>$h\leq \left\lfloor \log _{d}\left({\frac {n+1}{2}}\right)\right\rfloor .$</p><p>如下是 度为2的 B 树, 每个结点可能有2,3或4 个孩子, 所以也叫 2,3,4树, 等价于<a href="/red-black-tree.html#more">红黑树</a><br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/b-tree-1.png" alt="b-tree-1"></p><p><a id="markdown-3-查找操作" name="3-查找操作"></a></p><h1 id="3-查找操作"><a href="#3-查找操作" class="headerlink" title="3. 查找操作"></a>3. 查找操作</h1><p>可以看成二叉排序树的扩展,二叉排序树是二路查找,B - 树是多路查找。<br>节点内进行查找的时候除了顺序查找之外,还可以用二分查找来提高效率。</p><p>下面是顺序查找的 python 代码<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">search</span><span class="params">(self,key,withpath=False)</span>:</span></span><br><span class="line">    nd = self.root</span><br><span class="line">    fathers = []</span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">True</span>:</span><br><span class="line">        i = nd.findKey(key)</span><br><span class="line">        <span class="keyword">if</span> i==len(nd): fathers.append((nd,i<span class="number">-1</span>,i))</span><br><span class="line">        <span class="keyword">else</span>: fathers.append((nd,i,i))</span><br><span class="line">        <span class="keyword">if</span> i&lt;len(nd) <span class="keyword">and</span> nd[i]==key:</span><br><span class="line">            <span class="keyword">if</span> withpath:<span class="keyword">return</span> nd,i,fathers</span><br><span class="line">            <span class="keyword">else</span>:<span class="keyword">return</span> nd,i</span><br><span class="line">        <span class="keyword">if</span> nd.isLeafNode():</span><br><span class="line">            <span class="keyword">if</span> withpath:<span class="keyword">return</span> <span class="keyword">None</span>,<span class="keyword">None</span>,<span class="keyword">None</span></span><br><span class="line">            <span class="keyword">else</span>:<span class="keyword">return</span> <span class="keyword">None</span>,<span class="keyword">None</span></span><br><span class="line">        nd = nd.getChd(i)</span><br></pre></td></tr></table></figure></p><p>我实现时让 fathers 记录查找的路径, 方便在实现 delete 操作时使用(虽然有种 delete 方法可以不需要, 直接 from up to down with no pass by),</p><p><a id="markdown-4-插入操作" name="4-插入操作"></a></p><h1 id="4-插入操作"><a href="#4-插入操作" class="headerlink" title="4. 插入操作"></a>4. 插入操作</h1><p>自顶向下地进行插入操作,  最终插入在叶子结点,<br>考虑到叶子结点如果有 2t-1 $(k_1,k_2,\ldots,k_{2t-1})$个 关键字, 则需要进行分裂, </p><p>一个有 2t-1$(k_1,k_2,\ldots,k_{2t-1})$个关键字 结点分裂是这样进行的:  此结点分裂为 两个关键字为 t-1个的结点, 分别为 $(k_1,k_2,\ldots,k_{t-1})$, $(k_{t+1},k_{t+2},\ldots,k_{2t-1})$, 然后再插入一个关键字$k_t$到父亲结点.</p><p>注意同时要将孩子指针移动正确.</p><p>所以自顶向下地查找到叶子结点, 中间遇到 2t-1个关键字的结点就进行分裂, 这样如果其子结点进行分裂,  上升来的一个关键字可以插入到父结点而不会超过2t-1</p><p>代码如下<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,key)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> len(self.root)== self.degree*<span class="number">2</span><span class="number">-1</span>:</span><br><span class="line">        self.root = self.root.split(node(isLeaf=<span class="keyword">False</span>),self.degree)</span><br><span class="line">        self.nodeNum +=<span class="number">2</span></span><br><span class="line">    nd = self.root</span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">True</span>:</span><br><span class="line">        idx = nd.findKey(key)</span><br><span class="line">        <span class="keyword">if</span> idx&lt;len(nd) <span class="keyword">and</span> nd[idx] == key:<span class="keyword">return</span></span><br><span class="line">        <span class="keyword">if</span> nd.isLeafNode():</span><br><span class="line">            nd.insert(idx,key)</span><br><span class="line">            self.keyNum+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            chd = nd.getChd(idx)</span><br><span class="line">            <span class="keyword">if</span> len(chd)== self.degree*<span class="number">2</span><span class="number">-1</span>: <span class="comment">#ensure its keys won't excess when its chd split and u</span></span><br><span class="line">                nd = chd.split(nd,self.degree)</span><br><span class="line">                self.nodeNum +=<span class="number">1</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                nd = chd</span><br></pre></td></tr></table></figure></p><p><a id="markdown-5-删除操作" name="5-删除操作"></a></p><h1 id="5-删除操作"><a href="#5-删除操作" class="headerlink" title="5. 删除操作"></a>5. 删除操作</h1><p>删除操作是有点麻烦的, 有两种方法<sup><a href="#fn_1" id="reffn_1">1</a></sup></p><blockquote><ol><li>Locate and delete the item, then restructure the tree to retain its invariants, OR</li><li>Do a single pass down the tree, but before entering (visiting) a node, restructure the tree so that once the key to be deleted is encountered, it can be deleted without triggering the need for any further restructuring</li></ol></blockquote><p><a id="markdown-51-第一种方法" name="51-第一种方法"></a></p><h2 id="5-1-第一种方法"><a href="#5-1-第一种方法" class="headerlink" title="5.1. 第一种方法"></a>5.1. 第一种方法</h2><p>有如下情况</p><ul><li><p>删除结点在叶子结点上</p><ol><li>结点内的关键字个数大于d-1,可以直接删除（大于关键字个数下限,删除不影响 B - 树特性）</li><li><p>结点内的关键字个数等于d-1（等于关键字个数下限,删除后将破坏 特性）,此时需观察该节点左右兄弟结点的关键字个数：<br>a. <strong>旋转</strong>: 如果其左右兄弟结点中存在关键字个数大于d-1 的结点,则从关键字个数大于 d-1 的兄弟结点中借关键字：<strong>(这里看了网上的很多说法, 都是在介绍关键字的操作,而没有提到孩子结点.  我实现的时候想了很久才想出来:  借关键字时, 比如从右兄弟借一个关键字(第一个$k_1$), 此时即为左旋, 将父亲结点对应关键字移到当前结点, 再将右兄弟的移动父亲结点(因为要满足排序性质, 类似二叉树的选择) 然后进行孩子操作, 将右兄弟的$p_0$ 插入到 当前结点的孩子指针末尾) 左兄弟类似, <mark>而且要注意到边界条件, 比如当前结点是第0个/最后一个孩子, 则没有 左兄弟/右兄弟</mark></strong>)</p><p>b. <strong>合并</strong>: 如果其左右兄弟结点中不存在关键字个数大于 t-1 的结点,进行结点合并：将其父结点中的关键字拿到下一层,与该节点的左右兄弟结点的所有关键字合并<br><mark><strong>同样要注意到边界条件, 比如当前结点是第0个/最后一个孩子, 则没有 左兄弟/右兄弟</strong></mark></p></li><li><p>自底向上地检查来到这个叶子结点的路径上的结点是否满足关键字数目的要求, 只要关键字少于d-1,则进行旋转(2a)或者合并(2b)操作</p></li></ol></li><li>删除结点在非叶子结点上</li></ul><ol><li>查到到该结点, 然后转化成 上述 叶子结点中情况</li><li>转化过程：<br> a. 找到相邻关键字：即需删除关键字的左子树中的最大关键字或右子树中的最小关键字<br> b. 用相邻关键字来覆盖需删除的非叶子节点关键字,再删除原相邻关键字(在;叶子上,这即为上述情况)。</li></ol><p>python 代码如下,  <code>delete</code>函数中, 查找到结点, 用 <code>fathers::[(父节点, 关键字指针, 孩子指针)]</code> 记录路径,  如果不是叶子结点, 就再进行查找, 并记录结点, 转换关键字.</p><p>rebalance 就是从叶子结点自底向上到根结点, 只要遇到关键字数少于 2d-1 的,就进行平衡操作(旋转, 合并)</p><p>实现时要很仔细, 考虑边界条件, 还有当是左孩子的时候操作的是父结点的 chdIdx 的前一个, 是右孩子的时候是 chdIdx 的关键字. 具体实现完整代码见文末.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(self,key)</span>:</span><span class="comment">#to do</span></span><br><span class="line">    <span class="string">'''search the key, delete it , and form down to up to rebalance it '''</span></span><br><span class="line">    nd,idx ,fathers= self.search(key,withpath=<span class="keyword">True</span>)</span><br><span class="line">    <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">None</span> : <span class="keyword">return</span></span><br><span class="line">    <span class="keyword">del</span> nd[idx]</span><br><span class="line">    self.keyNum-=<span class="number">1</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> nd.isLeafNode():</span><br><span class="line">        chd = nd.getChd(idx) <span class="comment"># find the predecessor key</span></span><br><span class="line">        <span class="keyword">while</span> <span class="keyword">not</span>  chd.isLeafNode():</span><br><span class="line">            fathers.append((chd,len(chd)<span class="number">-1</span>,len(chd)))</span><br><span class="line">            chd = chd.getChd(<span class="number">-1</span>)</span><br><span class="line">        fathers.append((chd,len(chd)<span class="number">-1</span>,len(chd)))</span><br><span class="line">        nd.insert(idx,chd[<span class="number">-1</span>])</span><br><span class="line">        <span class="keyword">del</span> chd[<span class="number">-1</span>]</span><br><span class="line">    <span class="keyword">if</span> len(fathers)&gt;<span class="number">1</span>:self.rebalance(fathers)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">rebalance</span><span class="params">(self,fathers)</span>:</span></span><br><span class="line">    nd,keyIdx,chdIdx = fathers.pop()</span><br><span class="line">    <span class="keyword">while</span> len(nd)&lt;self.degree<span class="number">-1</span>: <span class="comment"># rebalance tree from down to up</span></span><br><span class="line">        prt,keyIdx,chdIdx = fathers[<span class="number">-1</span>]</span><br><span class="line">        lbro = [] <span class="keyword">if</span> chdIdx==<span class="number">0</span> <span class="keyword">else</span> prt.getChd(chdIdx<span class="number">-1</span>)</span><br><span class="line">        rbro = [] <span class="keyword">if</span> chdIdx==len(prt) <span class="keyword">else</span> prt.getChd(chdIdx+<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> len(lbro)&lt;self.degree <span class="keyword">and</span> len(rbro)&lt;self.degree:  <span class="comment"># merge two deficient nodes</span></span><br><span class="line">            beforeNode,afterNode = <span class="keyword">None</span>,<span class="keyword">None</span></span><br><span class="line">            <span class="keyword">if</span> lbro ==[]:</span><br><span class="line">                keyIdx = chdIdx</span><br><span class="line">                beforeNode,afterNode = nd,rbro</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                beforeNode,afterNode = lbro,nd</span><br><span class="line">                keyIdx = chdIdx<span class="number">-1</span>      <span class="comment"># important, when choosing</span></span><br><span class="line">            keys = beforeNode[:]+[prt[keyIdx]]+afterNode[:]</span><br><span class="line">            children = beforeNode.getChildren() + afterNode.getChildren()</span><br><span class="line">            isLeaf = beforeNode.isLeafNode()</span><br><span class="line">            prt.delChd(keyIdx+<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">del</span> prt[keyIdx]</span><br><span class="line">            nd.update(keys,isLeaf,children)</span><br><span class="line">            prt.children[keyIdx]=nd</span><br><span class="line">            self.nodeNum -=<span class="number">1</span></span><br><span class="line">        <span class="keyword">elif</span> len(lbro)&gt;=self.degree:  <span class="comment"># rotate  when only one sibling is deficient</span></span><br><span class="line">            keyIdx = chdIdx<span class="number">-1</span></span><br><span class="line">            nd.insert(<span class="number">0</span>,prt[keyIdx])    <span class="comment"># rotate keys</span></span><br><span class="line">            prt[keyIdx] =  lbro[<span class="number">-1</span>]</span><br><span class="line">            <span class="keyword">del</span> lbro[<span class="number">-1</span>]</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> nd.isLeafNode():     <span class="comment"># if not leaf, move children</span></span><br><span class="line">                nd.insert(<span class="number">0</span>,nd=lbro.getChd(<span class="number">-1</span>))</span><br><span class="line">                lbro.delChd(<span class="number">-1</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            keyIdx = chdIdx</span><br><span class="line">            nd.insert(len(nd),prt[keyIdx])    <span class="comment"># rotate keys</span></span><br><span class="line">            prt[keyIdx] =  rbro[<span class="number">0</span>]</span><br><span class="line">            <span class="keyword">del</span> rbro[<span class="number">0</span>]</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> nd.isLeafNode():     <span class="comment"># if not leaf, move children</span></span><br><span class="line">                <span class="comment">#note that insert(-1,ele) will make the ele be the last second one</span></span><br><span class="line">                nd.insert(len(nd),nd=rbro.getChd(<span class="number">0</span>))</span><br><span class="line">                rbro.delChd(<span class="number">0</span>)</span><br><span class="line">        <span class="keyword">if</span> len(fathers)==<span class="number">1</span>:</span><br><span class="line">            <span class="keyword">if</span> len(self.root)==<span class="number">0</span>:</span><br><span class="line">                self.root = nd</span><br><span class="line">                self.nodeNum -=<span class="number">1</span></span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        nd,i,j = fathers.pop()</span><br></pre></td></tr></table></figure></p><p><a id="markdown-52-第二种方法" name="52-第二种方法"></a></p><h2 id="5-2-第二种方法"><a href="#5-2-第二种方法" class="headerlink" title="5.2. 第二种方法"></a>5.2. 第二种方法</h2><p>这是算法导论<sup><a href="#fn_2" id="reffn_2">2</a></sup>上的<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/b-tree-2.png" alt="b-tree-2"><br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/b-tree-3.png" alt="b-tree-3"></p><p>例如<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/b-tree-4.png" alt="b-tree-4"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line">B-TREE-DELETE(T,k)</span><br><span class="line"></span><br><span class="line"><span class="number">1</span>  r ← root[T]</span><br><span class="line"> <span class="number">2</span>  <span class="keyword">if</span> n[r] = <span class="number">1</span></span><br><span class="line"> <span class="number">3</span>    then DISK_READ(c1[r])</span><br><span class="line"> <span class="number">4</span>       DISK_READ(c2[r])</span><br><span class="line"> <span class="number">5</span>       y ←c1[r]</span><br><span class="line"> <span class="number">6</span>       z ←c2[r]</span><br><span class="line"> <span class="number">7</span>       <span class="keyword">if</span> n[y] = n[z] = t<span class="number">-1</span>                   ▹ Cases <span class="number">2</span>c <span class="keyword">or</span> <span class="number">3</span>b</span><br><span class="line"> <span class="number">8</span>         then  B-TREE-MERGE-CHILD(r, <span class="number">1</span>, y, z) </span><br><span class="line"> <span class="number">9</span>            root[T] ← y</span><br><span class="line"> <span class="number">10</span>           FREE-NODE(r)</span><br><span class="line"> <span class="number">11</span>           B-TREE-DELETE-NONONE(y, k)</span><br><span class="line"><span class="number">12</span>      <span class="keyword">else</span> B-TREE-DELETE-NONONE (r, k)</span><br><span class="line"><span class="number">13</span> <span class="keyword">else</span> B-TREE-DELETE-NONONE (r, k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">考虑到根结点的特殊性,对根结点为<span class="number">1</span>,并且两个子结点都是t<span class="number">-1</span>的情况进行了特殊的处理：</span><br><span class="line">先对两个子结点进行合并,然后把原来的根删除,把树根指向合并后的子结点y。</span><br><span class="line">这样B树的高度就减少了<span class="number">1</span>。这也是B树高度唯一会减少的情况。 </span><br><span class="line">除了这种情况以外,就直接调用子过程 B-TREE-DELETE-NONONE (x, k)。</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">B-TREE-DELETE-NONONE (x, k)</span><br><span class="line"></span><br><span class="line"><span class="number">1</span>  i ← <span class="number">1</span></span><br><span class="line"> <span class="number">2</span>  <span class="keyword">if</span> leaf[x]                                       ▹ Cases <span class="number">1</span></span><br><span class="line"> <span class="number">3</span>     then <span class="keyword">while</span> i &lt;= n[x] <span class="keyword">and</span> k &gt; keyi[x]</span><br><span class="line"> <span class="number">4</span>            do i ← i + <span class="number">1</span></span><br><span class="line"> <span class="number">5</span>               <span class="keyword">if</span> k = keyi[x]</span><br><span class="line"> <span class="number">6</span>                 then <span class="keyword">for</span> j ← i+<span class="number">1</span> to n[x]</span><br><span class="line"> <span class="number">7</span>                        do keyj<span class="number">-1</span>[x] ←keyj[x]</span><br><span class="line"> <span class="number">8</span>                      n[x] ← n[x] - <span class="number">1</span></span><br><span class="line"> <span class="number">9</span>                      DISK-WRITE(x)</span><br><span class="line"> <span class="number">10</span>              <span class="keyword">else</span> error:”the key does <span class="keyword">not</span> exist”</span><br><span class="line"> <span class="number">11</span>    <span class="keyword">else</span> <span class="keyword">while</span> i &lt;= n[x] <span class="keyword">and</span> k &gt; keyi[x]</span><br><span class="line"><span class="number">12</span>           do i ← i + <span class="number">1</span></span><br><span class="line"> <span class="number">13</span>              DISK-READ(ci[x])</span><br><span class="line"> <span class="number">14</span>              y ←ci[x]</span><br><span class="line"> <span class="number">15</span>              <span class="keyword">if</span> i &lt;= n[x]</span><br><span class="line"> <span class="number">16</span>                then DISK-READ(ci+<span class="number">1</span>[x])</span><br><span class="line"> <span class="number">17</span>                     z ←ci+<span class="number">1</span>[x]</span><br><span class="line"> <span class="number">18</span>              <span class="keyword">if</span> k = keyi[x]                          ▹ Cases <span class="number">2</span></span><br><span class="line"><span class="number">19</span>                then <span class="keyword">if</span> n[y] &gt; t<span class="number">-1</span>                   ▹ Cases <span class="number">2</span>a</span><br><span class="line"> <span class="number">20</span>                       then k′←B-TREE-SEARCH-PREDECESSOR(y)</span><br><span class="line"> <span class="number">21</span>                            B-TREE-DELETE-NONONE (y, k′)</span><br><span class="line"> <span class="number">22</span>                            keyi[x] ←k′</span><br><span class="line"> <span class="number">23</span>                     <span class="keyword">else</span> <span class="keyword">if</span> n[z] &gt; t<span class="number">-1</span>               ▹ Cases <span class="number">2</span>b</span><br><span class="line"> <span class="number">24</span>                       then k′←B-TREE-SEARCH-SUCCESSOR (z)</span><br><span class="line"> <span class="number">25</span>                            B-TREE-DELETE-NONONE (z, k′)</span><br><span class="line"> <span class="number">26</span>                            keyi[x] ←k′</span><br><span class="line"> <span class="number">27</span>                     <span class="keyword">else</span> B-TREE-MERGE-CHILD(x, i, y, z)▹ Cases <span class="number">2</span>c</span><br><span class="line"> <span class="number">28</span>                          B-TREE-DELETE-NONONE (y, k)</span><br><span class="line"> <span class="number">29</span>              <span class="keyword">else</span>                                   ▹ Cases <span class="number">3</span></span><br><span class="line"> <span class="number">30</span>                <span class="keyword">if</span> i &gt;<span class="number">1</span></span><br><span class="line"> <span class="number">31</span>                  then DISK-READ(ci<span class="number">-1</span>[x])</span><br><span class="line"> <span class="number">32</span>                       p ←ci<span class="number">-1</span>[x]</span><br><span class="line"> <span class="number">33</span>                <span class="keyword">if</span> n[y] = t<span class="number">-1</span> </span><br><span class="line"> <span class="number">34</span>                  then <span class="keyword">if</span> i&gt;<span class="number">1</span> <span class="keyword">and</span> n[p] &gt;t<span class="number">-1</span>               ▹ Cases <span class="number">3</span>a</span><br><span class="line"> <span class="number">35</span>                         then B-TREE-SHIFT-TO-RIGHT-CHILD(x,i,p,y)</span><br><span class="line"> <span class="number">36</span>                       <span class="keyword">else</span> <span class="keyword">if</span> i &lt;= n[x] <span class="keyword">and</span> n[z] &gt; t<span class="number">-1</span>    ▹ Cases <span class="number">3</span>a</span><br><span class="line"> <span class="number">37</span>                         then B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)</span><br><span class="line"> <span class="number">38</span>                       <span class="keyword">else</span> <span class="keyword">if</span> i&gt;<span class="number">1</span>                       ▹ Cases <span class="number">3</span>b</span><br><span class="line"> <span class="number">39</span>                         then B-TREE-MERGE-CHILD(x, i, p, y)  </span><br><span class="line"> <span class="number">40</span>                              y ← p</span><br><span class="line"> <span class="number">41</span>                       <span class="keyword">else</span> B-TREE-MERGE-CHILD(x, i, y, z)▹ Cases <span class="number">3</span>b</span><br><span class="line"> <span class="number">42</span>                B-TREE-DELETE-NONONE (y, k)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> 转移到右边的子结点</span><br><span class="line">B-TREE-SHIFT-TO-RIGHT-CHILD(x,i,y,z)</span><br><span class="line"><span class="number">1</span> n[z] ← n[z] +<span class="number">1</span></span><br><span class="line"><span class="number">2</span> j ← n[z]</span><br><span class="line"><span class="number">3</span> <span class="keyword">while</span> j &gt; <span class="number">1</span></span><br><span class="line"><span class="number">4</span>   do keyj[z] ←keyj<span class="number">-1</span>[z]</span><br><span class="line"><span class="number">5</span>      j ← j <span class="number">-1</span></span><br><span class="line"><span class="number">6</span> key1[z] ←keyi[x]</span><br><span class="line"><span class="number">7</span> keyi[x] ←keyn[y][y]</span><br><span class="line"><span class="number">8</span> <span class="keyword">if</span> <span class="keyword">not</span> leaf[z]</span><br><span class="line"><span class="number">9</span>   then j ← n[z]</span><br><span class="line"><span class="number">10</span>       <span class="keyword">while</span> j &gt; <span class="number">0</span></span><br><span class="line"><span class="number">11</span>         do cj+<span class="number">1</span>[z] ←cj[z]</span><br><span class="line"><span class="number">12</span>            j ← j <span class="number">-1</span></span><br><span class="line"><span class="number">13</span>       c1[z] ←cn[y]+<span class="number">1</span>[y]</span><br><span class="line"><span class="number">14</span> n[y] ← n[y] <span class="number">-1</span></span><br><span class="line"><span class="number">15</span> DISK-WRITE(y)</span><br><span class="line"></span><br><span class="line"><span class="number">16</span> DISK-WRITE(z)</span><br><span class="line"></span><br><span class="line"><span class="number">17</span> DISK-WRITE(x)</span><br><span class="line"></span><br><span class="line">转移到左边的子结点</span><br><span class="line">B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)</span><br><span class="line"><span class="number">1</span> n[y] ← n[y] +<span class="number">1</span></span><br><span class="line"><span class="number">2</span> keyn[y][y] ← keyi[x]</span><br><span class="line"><span class="number">3</span> keyi[x] ←key1[z]</span><br><span class="line"><span class="number">4</span> n[z] ← n[z] <span class="number">-1</span></span><br><span class="line"><span class="number">5</span> j ← <span class="number">1</span></span><br><span class="line"><span class="number">6</span> <span class="keyword">while</span> j &lt;= n[z]</span><br><span class="line"><span class="number">7</span>   do keyj[z] ←keyj+<span class="number">1</span>[z]</span><br><span class="line"><span class="number">8</span>      j ← j +<span class="number">1</span></span><br><span class="line"><span class="number">9</span> <span class="keyword">if</span> <span class="keyword">not</span> leaf[z]</span><br><span class="line"><span class="number">10</span>  then cn[y]+<span class="number">1</span>[y] ←c1[z]</span><br><span class="line"><span class="number">11</span>       j ← <span class="number">1</span></span><br><span class="line"><span class="number">12</span>       <span class="keyword">while</span> j &lt;= n[z]+<span class="number">1</span></span><br><span class="line"><span class="number">13</span>         do cj[z] ←cj+<span class="number">1</span>[z]</span><br><span class="line"><span class="number">14</span>            j ← j + <span class="number">1</span></span><br><span class="line"><span class="number">15</span> DISK-WRITE(y)</span><br><span class="line"></span><br><span class="line"><span class="number">16</span> DISK-WRITE(z)</span><br><span class="line"></span><br><span class="line"><span class="number">17</span> DISK-WRITE(x)</span><br></pre></td></tr></table></figure><p><a id="markdown-6-b树" name="6-b树"></a></p><h1 id="6-B-树"><a href="#6-B-树" class="headerlink" title="6. B+树"></a>6. B+树</h1><p> B+ 树<sup><a href="#fn_3" id="reffn_3">3</a></sup>是 B- 树的变体,与B树不同的地方在于:</p><ol><li>非叶子结点的子树指针与关键字个数相同；</li><li>非叶子结点的子树指针 $p_i$指向关键字值属于 $[k_i,k_{i+1})$ 的子树（B- 树是开区间）；</li><li>为所有叶子结点增加一个链指针；</li><li><p><strong>所有关键字都在叶子结点出现</strong></p><p>B+ 的搜索与 B- 树也基本相同,区别是 B+ 树只有达到叶子结点才命中（B- 树可以在非叶子结点命中）,其性能也等价于在关键字全集做一次二分查找；<br>下面摘自 wiki<sup><a href="#fn_4" id="reffn_4">4</a></sup><br>&gt;</p><blockquote><h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><p>查找以典型的方式进行,类似于<a href="https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91" title="二叉查找树" target="_blank" rel="noopener">二叉查找树</a>。起始于根节点,自顶向下遍历树,选择其分离值在要查找值的任意一边的子指针。在节点内部典型的使用是<a href="https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE" title="二分查找" target="_blank" rel="noopener">二分查找</a>来确定这个位置。</p><h3 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h3><p>节点要处于违规状态,它必须包含在可接受范围之外数目的元素。</p><ol><li>首先,查找要插入其中的节点的位置。接着把值插入这个节点中。</li><li>如果没有节点处于违规状态则处理结束。</li><li>如果某个节点有过多元素,则把它分裂为两个节点,每个都有最小数目的元素。在树上递归向上继续这个处理直到到达根节点,如果根节点被分裂,则创建一个新根节点。为了使它工作,元素的最小和最大数目典型的必须选择为使最小数不小于最大数的一半。</li></ol><h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><ol><li>首先,查找要删除的值。接着从包含它的节点中删除这个值。</li><li>如果没有节点处于违规状态则处理结束。</li><li>如果节点处于违规状态则有两种可能情况：<ol><li>它的兄弟节点,就是同一个父节点的子节点,可以把一个或多个它的子节点转移到当前节点,而把它返回为合法状态。如果是这样,在更改父节点和两个兄弟节点的分离值之后处理结束。</li><li>它的兄弟节点由于处在低边界上而没有额外的子节点。在这种情况下把两个兄弟节点合并到一个单一的节点中,而且我们递归到父节点上,因为它被删除了一个子节点。持续这个处理直到当前节点是合法状态或者到达根节点,在其上根节点的子节点被合并而且合并后的节点成为新的根节点。</li></ol></li></ol></blockquote></li></ol><p>由于叶子结点间有指向下一个叶子的指针, 便于遍历, 以及区间查找, 所以数据库的以及操作系统文件系统的实现常用 B+树,<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/b-tree-5.png" alt="b-tree-5"></p><p><a id="markdown-7-b树" name="7-b树"></a></p><h1 id="7-B-树"><a href="#7-B-树" class="headerlink" title="7. B*树"></a>7. B*树</h1><p>B<em>-tree <sup><a href="#fn_5" id="reffn_5">5</a></sup> 是 B+-tree 的变体,在 B+ 树的基础上 (所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针),B </em> 树中非根和非叶子结点再增加指向兄弟的指针；B<em> 树定义了非叶子结点关键字个数至少为 (2/3)</em>M,即块的最低使用率为 2/3（代替 B+ 树的 1/2）</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/b-tree-6.png" alt="b-tree-6"></p><p><a id="markdown-8-代码实现与测试" name="8-代码实现与测试"></a></p><h1 id="8-代码实现与测试"><a href="#8-代码实现与测试" class="headerlink" title="8. 代码实现与测试"></a>8. 代码实现与测试</h1><p><a href="https://github.com/mbinary/algorithm" target="_blank" rel="noopener">github地址</a></p><p><a id="markdown-81-测试" name="81-测试"></a></p><h2 id="8-1-测试"><a href="#8-1-测试" class="headerlink" title="8.1. 测试"></a>8.1. 测试</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ ==<span class="string">'__main__'</span>:</span><br><span class="line">    bt = bTree()</span><br><span class="line">    <span class="keyword">from</span> random <span class="keyword">import</span> shuffle,sample</span><br><span class="line">    n = <span class="number">20</span></span><br><span class="line">    lst = [i <span class="keyword">for</span> i <span class="keyword">in</span> range(n)]</span><br><span class="line">    shuffle(lst)</span><br><span class="line">    test= sample(lst,len(lst)//<span class="number">4</span>)</span><br><span class="line">    print(<span class="string">f'building b-tree with  <span class="subst">&#123;lst&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> lst:</span><br><span class="line">        bt.insert(i)</span><br><span class="line">        <span class="comment">#print(f'inserting &#123;i&#125;)</span></span><br><span class="line">        <span class="comment">#print(bt)</span></span><br><span class="line">    print(bt)</span><br><span class="line">    print(<span class="string">f'serching <span class="subst">&#123;test&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> test:</span><br><span class="line">        nd,idx = bt.search(i)</span><br><span class="line">        print(<span class="string">f'node: <span class="subst">&#123;repr(nd)&#125;</span>[<span class="subst">&#123;idx&#125;</span>]== <span class="subst">&#123;i&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> test:</span><br><span class="line">        print(<span class="string">f'deleting <span class="subst">&#123;i&#125;</span>'</span>)</span><br><span class="line">        bt.delete(i)</span><br><span class="line">        print(bt)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bTree.png" alt="bTree"></p><p><a id="markdown-82-python-实现" name="82-python-实现"></a></p><h2 id="8-2-python-实现"><a href="#8-2-python-实现" class="headerlink" title="8.2. python 实现"></a>8.2. python 实现</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,keys=None,isLeaf = True,children=None)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> keys <span class="keyword">is</span> <span class="keyword">None</span>:keys=[]</span><br><span class="line">        <span class="keyword">if</span> children <span class="keyword">is</span> <span class="keyword">None</span>: children =[]</span><br><span class="line">        self.keys = keys</span><br><span class="line">        self.isLeaf =  isLeaf</span><br><span class="line">        self.children = []</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self,i)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.keys[i]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__delitem__</span><span class="params">(self,i)</span>:</span></span><br><span class="line">        <span class="keyword">del</span> self.keys[i]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__setitem__</span><span class="params">(self,i,k)</span>:</span></span><br><span class="line">        self.keys[i] = k</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__len__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> len(self.keys)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> str(self.keys)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        children = <span class="string">','</span>.join([str(nd.keys) <span class="keyword">for</span> nd <span class="keyword">in</span> self.children])</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'keys:     <span class="subst">&#123;self.keys&#125;</span>\nchildren: <span class="subst">&#123;children&#125;</span>\nisLeaf:   <span class="subst">&#123;self.isLeaf&#125;</span>'</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getChd</span><span class="params">(self,i)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.children[i]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">delChd</span><span class="params">(self,i)</span>:</span></span><br><span class="line">        <span class="keyword">del</span> self.children[i]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setChd</span><span class="params">(self,i,chd)</span>:</span></span><br><span class="line">        self.children[i] = chd</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getChildren</span><span class="params">(self,begin=<span class="number">0</span>,end=None)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> end <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span> self.children[begin:]</span><br><span class="line">        <span class="keyword">return</span> self.children[begin:end]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">findKey</span><span class="params">(self,key)</span>:</span></span><br><span class="line">        <span class="keyword">for</span> i,k <span class="keyword">in</span> enumerate(self.keys):</span><br><span class="line">            <span class="keyword">if</span> k&gt;=key:</span><br><span class="line">                <span class="keyword">return</span> i</span><br><span class="line">        <span class="keyword">return</span> len(self)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">update</span><span class="params">(self,keys=None,isLeaf=None,children=None)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> keys <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:self.keys = keys</span><br><span class="line">        <span class="keyword">if</span> children <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:self.children = children</span><br><span class="line">        <span class="keyword">if</span> isLeaf <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>: self.isLeaf = isLeaf</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,i,key=None,nd=None)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> key <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:self.keys.insert(i,key)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.isLeaf <span class="keyword">and</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>: self.children.insert(i,nd)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">isLeafNode</span><span class="params">(self)</span>:</span><span class="keyword">return</span> self.isLeaf</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">split</span><span class="params">(self,prt,t)</span>:</span></span><br><span class="line">        <span class="comment"># form new two nodes</span></span><br><span class="line">        k = self[t<span class="number">-1</span>]</span><br><span class="line">        nd1 = node()</span><br><span class="line">        nd2 = node()</span><br><span class="line">        nd1.keys,nd2.keys = self[:t<span class="number">-1</span>], self[t:] <span class="comment"># note that t is 1 bigger than  key index</span></span><br><span class="line">        nd1.isLeaf = nd2.isLeaf = self.isLeaf</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.isLeaf:</span><br><span class="line">            <span class="comment"># note that  children index is one bigger than key index, and all children included</span></span><br><span class="line">            nd1.children, nd2.children = self.children[<span class="number">0</span>:t], self.children[t:]</span><br><span class="line">        <span class="comment"># connect them to parent</span></span><br><span class="line">        idx = prt.findKey(k)</span><br><span class="line">        <span class="keyword">if</span> prt.children !=[]: prt.children.remove(self) <span class="comment"># remove the original node</span></span><br><span class="line">        prt.insert(idx,k,nd2)</span><br><span class="line">        prt.insert(idx,nd = nd1)</span><br><span class="line">        <span class="keyword">return</span> prt</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">bTree</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,degree=<span class="number">2</span>)</span>:</span></span><br><span class="line">        self.root = node()</span><br><span class="line">        self.degree=degree</span><br><span class="line">        self.nodeNum = <span class="number">1</span></span><br><span class="line">        self.keyNum = <span class="number">0</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">search</span><span class="params">(self,key,withpath=False)</span>:</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        fathers = []</span><br><span class="line">        <span class="keyword">while</span> <span class="keyword">True</span>:</span><br><span class="line">            i = nd.findKey(key)</span><br><span class="line">            <span class="keyword">if</span> i==len(nd): fathers.append((nd,i<span class="number">-1</span>,i))</span><br><span class="line">            <span class="keyword">else</span>: fathers.append((nd,i,i))</span><br><span class="line">            <span class="keyword">if</span> i&lt;len(nd) <span class="keyword">and</span> nd[i]==key:</span><br><span class="line">                <span class="keyword">if</span> withpath:<span class="keyword">return</span> nd,i,fathers</span><br><span class="line">                <span class="keyword">else</span>:<span class="keyword">return</span> nd,i</span><br><span class="line">            <span class="keyword">if</span> nd.isLeafNode():</span><br><span class="line">                <span class="keyword">if</span> withpath:<span class="keyword">return</span> <span class="keyword">None</span>,<span class="keyword">None</span>,<span class="keyword">None</span></span><br><span class="line">                <span class="keyword">else</span>:<span class="keyword">return</span> <span class="keyword">None</span>,<span class="keyword">None</span></span><br><span class="line">            nd = nd.getChd(i)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,key)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> len(self.root)== self.degree*<span class="number">2</span><span class="number">-1</span>:</span><br><span class="line">            self.root = self.root.split(node(isLeaf=<span class="keyword">False</span>),self.degree)</span><br><span class="line">            self.nodeNum +=<span class="number">2</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        <span class="keyword">while</span> <span class="keyword">True</span>:</span><br><span class="line">            idx = nd.findKey(key)</span><br><span class="line">            <span class="keyword">if</span> idx&lt;len(nd) <span class="keyword">and</span> nd[idx] == key:<span class="keyword">return</span></span><br><span class="line">            <span class="keyword">if</span> nd.isLeafNode():</span><br><span class="line">                nd.insert(idx,key)</span><br><span class="line">                self.keyNum+=<span class="number">1</span></span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                chd = nd.getChd(idx)</span><br><span class="line">                <span class="keyword">if</span> len(chd)== self.degree*<span class="number">2</span><span class="number">-1</span>: <span class="comment">#ensure its keys won't excess when its chd split and u</span></span><br><span class="line">                    nd = chd.split(nd,self.degree)</span><br><span class="line">                    self.nodeNum +=<span class="number">1</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    nd = chd</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(self,key)</span>:</span><span class="comment">#to do</span></span><br><span class="line">        <span class="string">'''search the key, delete it , and form down to up to rebalance it '''</span></span><br><span class="line">        nd,idx ,fathers= self.search(key,withpath=<span class="keyword">True</span>)</span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">None</span> : <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">del</span> nd[idx]</span><br><span class="line">        self.keyNum-=<span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> nd.isLeafNode():</span><br><span class="line">            chd = nd.getChd(idx) <span class="comment"># find the predecessor key</span></span><br><span class="line">            <span class="keyword">while</span> <span class="keyword">not</span>  chd.isLeafNode():</span><br><span class="line">                fathers.append((chd,len(chd)<span class="number">-1</span>,len(chd)))</span><br><span class="line">                chd = chd.getChd(<span class="number">-1</span>)</span><br><span class="line">            fathers.append((chd,len(chd)<span class="number">-1</span>,len(chd)))</span><br><span class="line">            nd.insert(idx,chd[<span class="number">-1</span>])</span><br><span class="line">            <span class="keyword">del</span> chd[<span class="number">-1</span>]</span><br><span class="line">        <span class="keyword">if</span> len(fathers)&gt;<span class="number">1</span>:self.rebalance(fathers)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">rebalance</span><span class="params">(self,fathers)</span>:</span></span><br><span class="line">        nd,keyIdx,chdIdx = fathers.pop()</span><br><span class="line">        <span class="keyword">while</span> len(nd)&lt;self.degree<span class="number">-1</span>: <span class="comment"># rebalance tree from down to up</span></span><br><span class="line">            prt,keyIdx,chdIdx = fathers[<span class="number">-1</span>]</span><br><span class="line">            lbro = [] <span class="keyword">if</span> chdIdx==<span class="number">0</span> <span class="keyword">else</span> prt.getChd(chdIdx<span class="number">-1</span>)</span><br><span class="line">            rbro = [] <span class="keyword">if</span> chdIdx==len(prt) <span class="keyword">else</span> prt.getChd(chdIdx+<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">if</span> len(lbro)&lt;self.degree <span class="keyword">and</span> len(rbro)&lt;self.degree:  <span class="comment"># merge two deficient nodes</span></span><br><span class="line">                beforeNode,afterNode = <span class="keyword">None</span>,<span class="keyword">None</span></span><br><span class="line">                <span class="keyword">if</span> lbro ==[]:</span><br><span class="line">                    keyIdx = chdIdx</span><br><span class="line">                    beforeNode,afterNode = nd,rbro</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    beforeNode,afterNode = lbro,nd</span><br><span class="line">                    keyIdx = chdIdx<span class="number">-1</span>      <span class="comment"># important, when choosing</span></span><br><span class="line">                keys = beforeNode[:]+[prt[keyIdx]]+afterNode[:]</span><br><span class="line">                children = beforeNode.getChildren() + afterNode.getChildren()</span><br><span class="line">                isLeaf = beforeNode.isLeafNode()</span><br><span class="line">                prt.delChd(keyIdx+<span class="number">1</span>)</span><br><span class="line">                <span class="keyword">del</span> prt[keyIdx]</span><br><span class="line">                nd.update(keys,isLeaf,children)</span><br><span class="line">                prt.children[keyIdx]=nd</span><br><span class="line">                self.nodeNum -=<span class="number">1</span></span><br><span class="line">            <span class="keyword">elif</span> len(lbro)&gt;=self.degree:  <span class="comment"># rotate  when only one sibling is deficient</span></span><br><span class="line">                keyIdx = chdIdx<span class="number">-1</span></span><br><span class="line">                nd.insert(<span class="number">0</span>,prt[keyIdx])    <span class="comment"># rotate keys</span></span><br><span class="line">                prt[keyIdx] =  lbro[<span class="number">-1</span>]</span><br><span class="line">                <span class="keyword">del</span> lbro[<span class="number">-1</span>]</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> nd.isLeafNode():     <span class="comment"># if not leaf, move children</span></span><br><span class="line">                    nd.insert(<span class="number">0</span>,nd=lbro.getChd(<span class="number">-1</span>))</span><br><span class="line">                    lbro.delChd(<span class="number">-1</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                keyIdx = chdIdx</span><br><span class="line">                nd.insert(len(nd),prt[keyIdx])    <span class="comment"># rotate keys</span></span><br><span class="line">                prt[keyIdx] =  rbro[<span class="number">0</span>]</span><br><span class="line">                <span class="keyword">del</span> rbro[<span class="number">0</span>]</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> nd.isLeafNode():     <span class="comment"># if not leaf, move children</span></span><br><span class="line">                    <span class="comment">#note that insert(-1,ele) will make the ele be the last second one</span></span><br><span class="line">                    nd.insert(len(nd),nd=rbro.getChd(<span class="number">0</span>))</span><br><span class="line">                    rbro.delChd(<span class="number">0</span>)</span><br><span class="line">            <span class="keyword">if</span> len(fathers)==<span class="number">1</span>:</span><br><span class="line">                <span class="keyword">if</span> len(self.root)==<span class="number">0</span>:</span><br><span class="line">                    self.root = nd</span><br><span class="line">                    self.nodeNum -=<span class="number">1</span></span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            nd,i,j = fathers.pop()</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        head= <span class="string">'\n'</span>+<span class="string">'-'</span>*<span class="number">30</span>+<span class="string">'B  Tree'</span>+<span class="string">'-'</span>*<span class="number">30</span></span><br><span class="line">        tail= <span class="string">'-'</span>*<span class="number">30</span>+<span class="string">'the end'</span>+<span class="string">'-'</span>*<span class="number">30</span>+<span class="string">'\n'</span></span><br><span class="line">        lst = [[head],[<span class="string">f'node num: <span class="subst">&#123;self.nodeNum&#125;</span>,  key num: <span class="subst">&#123;self.keyNum&#125;</span>'</span>]]</span><br><span class="line">        cur = []</span><br><span class="line">        ndNum =<span class="number">0</span></span><br><span class="line">        ndTotal= <span class="number">1</span></span><br><span class="line">        que = [self.root]</span><br><span class="line">        <span class="keyword">while</span> que!=[]:</span><br><span class="line">            nd = que.pop(<span class="number">0</span>)</span><br><span class="line">            cur.append(repr(nd))</span><br><span class="line">            ndNum+=<span class="number">1</span></span><br><span class="line">            que+=nd.getChildren()</span><br><span class="line">            <span class="keyword">if</span> ndNum==ndTotal:</span><br><span class="line">                lst.append(cur)</span><br><span class="line">                cur = []</span><br><span class="line">                ndNum = <span class="number">0</span></span><br><span class="line">                ndTotal =len(que)</span><br><span class="line">        lst.append([tail])</span><br><span class="line">        lst = [<span class="string">','</span>.join(li) <span class="keyword">for</span> li <span class="keyword">in</span> lst]</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'\n'</span>.join(lst)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self,nd = None)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">None</span>: nd = self.root</span><br><span class="line">        que = [nd]</span><br><span class="line">        <span class="keyword">while</span> que !=[]:</span><br><span class="line">            nd = que.pop(<span class="number">0</span>) </span><br><span class="line">            <span class="keyword">yield</span> nd</span><br><span class="line">            <span class="keyword">if</span> nd.isLeafNode():<span class="keyword">continue</span></span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> range(len(nd)+<span class="number">1</span>):</span><br><span class="line">                que.append(nd.getChd(i))</span><br></pre></td></tr></table></figure><p><a id="markdown-9-参考资料" name="9-参考资料"></a></p><h1 id="9-参考资料"><a href="#9-参考资料" class="headerlink" title="9. 参考资料"></a>9. 参考资料</h1><blockquote id="fn_1"><sup>1</sup>. <a href="https://en.wikipedia.org/wiki/B-tree" target="_blank" rel="noopener">B树</a><a href="#reffn_1" title="Jump back to footnote [1] in the text."> &#8617;</a></blockquote><blockquote id="fn_2"><sup>2</sup>. 算法导论<a href="#reffn_2" title="Jump back to footnote [2] in the text."> &#8617;</a></blockquote><p><sup><a href="#fn_3" id="reffn_3">3</a></sup>:<a href="https://blog.csdn.net/u010842515/article/details/68487817" target="_blank" rel="noopener">B - 树特征及插入删除操作总结</a></p><blockquote id="fn_4"><sup>4</sup>. <a href="https://zh.wikipedia.org/wiki/B%2B%E6%A0%91" target="_blank" rel="noopener">B+树</a><a href="#reffn_4" title="Jump back to footnote [4] in the text."> &#8617;</a></blockquote><blockquote id="fn_5"><sup>5</sup>. <a href="https://blog.csdn.net/v_JULY_v/article/details/6530142" target="_blank" rel="noopener">从 B 树、B + 树、B * 树谈到 R 树</a><a href="#reffn_5" title="Jump back to footnote [5] in the text."> &#8617;</a></blockquote>]]></content>
    
    <summary type="html">
    
      B 树的原理与实现,b+ 树介绍
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="数据结构" scheme="https://mbinary.xyz/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
      <category term="B树" scheme="https://mbinary.xyz/tags/B%E6%A0%91/"/>
    
      <category term="数据库" scheme="https://mbinary.xyz/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>『比特币』概述</title>
    <link href="https://mbinary.xyz/introduction-to-bitcoin.html"/>
    <id>https://mbinary.xyz/introduction-to-bitcoin.html</id>
    <published>2018-08-02T11:04:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>本文整理自<a href="https://legacy.gitbook.com/book/wizardforcel/masterbitcoin2cn/details" target="_blank" rel="noopener">&lt;&lt;精通比特币&gt;&gt;</a><br>**<a id="more"></a></p><!-- TOC --><ul><li><a href="#1-概述">1. 概述</a><ul><li><a href="#11-钱包地址">1.1. 钱包地址</a></li><li><a href="#12-交易链">1.2. 交易链</a></li><li><a href="#13-找零">1.3. 找零</a></li><li><a href="#14-钱包">1.4. 钱包</a></li><li><a href="#15-挖矿的作用">1.5. 挖矿的作用</a></li></ul></li><li><a href="#2-bitcoin-core-客户端">2. bitcoin core 客户端</a></li><li><a href="#3-密钥与地址">3. 密钥与地址</a><ul><li><a href="#31-公钥加密">3.1. 公钥加密</a></li><li><a href="#32-椭圆曲线乘法">3.2. 椭圆曲线乘法</a></li><li><a href="#33-生成公钥">3.3. 生成公钥</a></li><li><a href="#34-生成比特币地址">3.4. 生成比特币地址</a></li><li><a href="#35-公钥格式">3.5. 公钥格式</a></li><li><a href="#36-私钥格式">3.6. 私钥格式</a></li><li><a href="#37-高级密钥和地址">3.7. 高级密钥和地址</a><ul><li><a href="#371-加密私钥">3.7.1. 加密私钥</a></li><li><a href="#372-p2shpay-to-script-hash">3.7.2. P2SH(Pay-to-Script Hash)</a></li></ul></li><li><a href="#38-纸钱包">3.8. 纸钱包</a></li></ul></li><li><a href="#4-钱包">4. 钱包</a><ul><li><a href="#41-钱包种类">4.1. 钱包种类</a><ul><li><a href="#411-非确定性钱包nondeterministic-wallet">4.1.1. 非确定性钱包(nondeterministic wallet)</a></li><li><a href="#412-确定性钱包deterministic-wallet">4.1.2. 确定性钱包(deterministic wallet)</a></li></ul></li><li><a href="#42-hd钱包">4.2. HD钱包</a></li><li><a href="#43-种子和助记词">4.3. 种子和助记词</a><ul><li><a href="#431-创建助记词">4.3.1. 创建助记词</a></li><li><a href="#432-从助记词得到种子">4.3.2. 从助记词得到种子</a></li></ul></li><li><a href="#44-可选密码短语">4.4. 可选密码短语</a><ul><li><a href="#441-功能">4.4.1. 功能</a></li><li><a href="#442-风险">4.4.2. 风险</a></li></ul></li><li><a href="#45-从种子中创造-hd-钱包">4.5. 从种子中创造 HD 钱包</a><ul><li><a href="#451-私有子密钥的衍生更">4.5.1. 私有子密钥的衍生更</a></li><li><a href="#452-扩展密钥">4.5.2. 扩展密钥</a></li><li><a href="#453-公共子密钥的推导">4.5.3. 公共子密钥的推导</a></li><li><a href="#454-硬化子密钥的衍生">4.5.4. 硬化子密钥的衍生</a></li><li><a href="#455-索引号码">4.5.5. 索引号码</a></li><li><a href="#456-钱包密钥识别符路径">4.5.6. 钱包密钥识别符(路径)</a></li><li><a href="#457-hd钱包树状结构的导航">4.5.7. HD钱包树状结构的导航</a></li></ul></li></ul></li><li><a href="#5-交易">5. 交易</a><ul><li><a href="#51-utxo">5.1. UTXO</a></li><li><a href="#52-币基交易coinbasetransaction">5.2. 币基交易(CoinbaseTransaction)</a></li><li><a href="#53-交易输出">5.3. 交易输出</a></li><li><a href="#54-交易输入">5.4. 交易输入</a></li><li><a href="#55-交易过程">5.5. 交易过程</a></li><li><a href="#56-交易费">5.6. 交易费</a></li><li><a href="#57-交易脚本语言">5.7. 交易脚本语言</a><ul><li><a href="#571-图灵非完备性">5.7.1. 图灵非完备性</a></li><li><a href="#572-去中心化验证">5.7.2. 去中心化验证</a></li><li><a href="#573-脚本构建锁定与解锁">5.7.3. 脚本构建(锁定与解锁)</a><ul><li><a href="#5731-锁定脚本locking-script">5.7.3.1. 锁定脚本(Locking Script)</a></li><li><a href="#5732-解锁脚本unlocking-script">5.7.3.2. 解锁脚本(Unlocking Script)</a></li></ul></li></ul></li><li><a href="#58-数字签名ecdsa">5.8. 数字签名(ECDSA)</a><ul><li><a href="#581-如何工作">5.8.1. 如何工作</a></li><li><a href="#582-作用">5.8.2. 作用</a></li><li><a href="#583-创建与验证">5.8.3. 创建与验证</a></li><li><a href="#584-签名序列化der">5.8.4. 签名序列化(DER)</a></li><li><a href="#585-签名哈希类型sighash">5.8.5. 签名哈希类型(SIGHASH)</a></li></ul></li></ul></li><li><a href="#6-高级交易和脚本">6. 高级交易和脚本</a><ul><li><a href="#61-多重签名">6.1. 多重签名</a></li><li><a href="#62-p2shpay-to-script-hash">6.2. P2SH(Pay-to-Script-Hash)</a><ul><li><a href="#621-多重签名的问题">6.2.1. 多重签名的问题</a></li><li><a href="#622-赎回脚本">6.2.2. 赎回脚本</a></li><li><a href="#623-p2sh地址">6.2.3. P2SH地址</a></li><li><a href="#624-优点">6.2.4. 优点</a></li></ul></li><li><a href="#63-数据记录输出return操作符">6.3. 数据记录输出(RETURN操作符)</a></li><li><a href="#64-时间锁timelocks">6.4. 时间锁(Timelocks)</a><ul><li><a href="#641-检查锁定时间验证checklocktimeverifycltv">6.4.1. 检查锁定时间验证CheckLockTimeVerify(CLTV)</a></li><li><a href="#642-相对时间锁">6.4.2. 相对时间锁</a><ul><li><a href="#6421-nsequence">6.4.2.1. nSequence</a></li><li><a href="#6422-checksequenceverifycsv">6.4.2.2. CHECKSEQUENCEVERIFY(CSV)</a></li></ul></li><li><a href="#643-median-time-past">6.4.3. Median-Time-Past</a></li></ul></li><li><a href="#65-条件子句conditional-clauses">6.5. 条件子句(Conditional Clauses)</a></li></ul></li><li><a href="#7-p2p-网络架构">7. P2P 网络架构</a><ul><li><a href="#71-扩展比特币网络">7.1. 扩展比特币网络</a></li><li><a href="#72-网络发现">7.2. 网络发现</a></li><li><a href="#73-同步区块链">7.3. 同步区块链</a></li><li><a href="#74-简单支付验证spv">7.4. 简单支付验证(SPV)</a></li><li><a href="#75-bloom-filter">7.5. Bloom filter</a></li><li><a href="#76-spv-节点如何使用-bloom-filter">7.6. SPV 节点如何使用 Bloom filter</a></li><li><a href="#77-加密和认证连接">7.7. 加密和认证连接</a></li><li><a href="#78-交易池">7.8. 交易池</a></li></ul></li><li><a href="#8-区块链">8. 区块链</a></li><li><a href="#9-挖矿与共识">9. 挖矿与共识</a><ul><li><a href="#91-交易验证">9.1. 交易验证</a></li><li><a href="#92-coinbase交易创币交易">9.2. coinbase交易(创币交易)</a></li><li><a href="#93-构造区块头">9.3. 构造区块头</a></li><li><a href="#94-校验新区块">9.4. 校验新区块</a></li><li><a href="#95-构建区块">9.5. 构建区块</a></li><li><a href="#96-区块链的组装与选择">9.6. 区块链的组装与选择</a></li><li><a href="#97-矿池">9.7. 矿池</a><ul><li><a href="#971-矿工加入">9.7.1. 矿工加入</a></li><li><a href="#972-托管矿池">9.7.2. 托管矿池</a></li><li><a href="#973-p2p矿池">9.7.3. P2P矿池</a></li></ul></li><li><a href="#98-共识攻击">9.8. 共识攻击</a></li><li><a href="#99-分叉">9.9. 分叉</a><ul><li><a href="#991-硬分叉">9.9.1. 硬分叉</a></li><li><a href="#992-软分叉">9.9.2. 软分叉</a></li></ul></li></ul></li><li><a href="#10-比特币安全">10. 比特币安全</a><ul><li><a href="#101-安全准则">10.1. 安全准则</a></li><li><a href="#102-比特币系统安全开发">10.2. 比特币系统安全开发</a></li><li><a href="#103-信任根root-of-trust">10.3. 信任根(ROOT-OF-TRUST)</a></li></ul></li></ul><!-- /TOC --><p><a id="markdown-1-概述" name="1-概述"></a></p><h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h1><p>每一笔交易可以分为 输入, 输出, 其他<br>输入&gt;=输出+其他(奖励给矿工),<br>而输入的比特币需要引用其来源(它作为上次交易的输出),称UTXO.<br>而且每笔交易作为输入不能分割.<br><a id="markdown-11-钱包地址" name="11-钱包地址"></a></p><h2 id="1-1-钱包地址"><a href="#1-1-钱包地址" class="headerlink" title="1.1. 钱包地址"></a>1.1. 钱包地址</h2><p>新生成的钱包, 即未交易过的比特币地址对于比特币网络来说是不知道的，或者是未经注册到比特币系统中。它只是一个数字，对应于一个可以用来控制资金访问的密钥。由钱包独立生成的，还没有参考或注册任何服务。<br>事实上，在大多数钱包中，比特币地址和任何外部可识别的信息(包括用户的身份)之间没有关联。在该地址被引用作为比特币总帐的交易中的接收者之前，比特币地址只是在比特币中有效的大量可能的地址的一部分。只有一旦与交易相关联才能成为网络中已知地址的一部分。<br><a id="markdown-12-交易链" name="12-交易链"></a></p><h2 id="1-2-交易链"><a href="#1-2-交易链" class="headerlink" title="1.2. 交易链"></a>1.2. 交易链</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-1.png" alt="introduction-to-bitcoin-1"><br><a id="markdown-13-找零" name="13-找零"></a></p><h2 id="1-3-找零"><a href="#1-3-找零" class="headerlink" title="1.3. 找零"></a>1.3. 找零</h2><p>许多比特币交易都会包括新所有者的地址(买方地址)和当前所有者的地址(称为找零地址)的输出。这是因为交易输入，就像纸币那样能够，不能再分割。如果您在商店购买了5美元的商品，但是使用20美元的美金来支付商品，您预计会收到15美元的找零。</p><p>相同的概念适用于比特币交易输入。如果您购买了一个价格为5比特币但只能使用20比特币输入的商品，那<br>么您可以将5个比特币的一个输出发送给商店所有者，并将一个15比特币的输出返回给您自己作为找零(减去任何适用的交易费用)。重要的是，找零地址不必与输入时提供的地址相同，出于隐私的原因，通常是所有者钱包中的新地址。</p><p>另外还有其他模式, 比如有一把零钱, 凑在一起,支付一次<br>还有就是一次有很大一笔钱, 分给很多人.<br>如下图所示<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-2.png" alt="introduction-to-bitcoin-2"><br><a id="markdown-14-钱包" name="14-钱包"></a></p><h2 id="1-4-钱包"><a href="#1-4-钱包" class="headerlink" title="1.4. 钱包"></a>1.4. 钱包</h2><p>钱包应用可以在完全离线时建立交易。就像在家里写张支票，    之后放到信封发给银行一样，比特币交易建立和签名时不用连接比特币网络。只有在执行交易时才需要将交易发送到网络。<br><a id="markdown-15-挖矿的作用" name="15-挖矿的作用"></a></p><h2 id="1-5-挖矿的作用"><a href="#1-5-挖矿的作用" class="headerlink" title="1.5. 挖矿的作用"></a>1.5. 挖矿的作用</h2><ul><li>挖矿节点通过参考比特币的共识规则验证所有交易。    因此，挖矿通过拒绝无效或畸形交易来提供比特币交易的安全性。</li><li>挖矿在构建区块时会创造新的比特币，和一个中央银行印发新的纸币很类似。每个区块创造的比特币数量是固定的，    随时间会渐渐减少</li></ul><p><a id="markdown-2-bitcoin-core-客户端" name="2-bitcoin-core-客户端"></a></p><h1 id="2-bitcoin-core-客户端"><a href="#2-bitcoin-core-客户端" class="headerlink" title="2. bitcoin core 客户端"></a>2. bitcoin core 客户端</h1><ul><li><p>下载</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">clone</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git  tag</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git checkout  TAG</span></span><br></pre></td></tr></table></figure></li><li><p>检查<code>./autogen.sh</code></p></li><li><p>配置</p><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">./configure</span><br><span class="line">--<span class="keyword">with</span>-gui=<span class="literal">no</span> <span class="string">\</span></span><br><span class="line">-<span class="keyword">with</span>-incompatible-bdb <span class="string">\</span></span><br><span class="line">-prefix=$HOME  <span class="string">\</span></span><br></pre></td></tr></table></figure></li><li><p>编译</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make</span><br><span class="line">sudo make install</span><br></pre></td></tr></table></figure></li><li><p>测试是否成功</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> <span class="built_in">which</span> bitcoind   <span class="comment">#/usr/local/bin/bitcoind</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> whic bitcoin-cli <span class="comment">#/usr/local/bitcoin-cli</span></span></span><br></pre></td></tr></table></figure></li><li><p>设置 API 访问的密码(首次运行):<br>编辑bitcoin/bitcoin.conf  内容如下.   rpc 即 remote procedure call</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">rpcuser</span>=bitcoinrpc</span><br><span class="line"><span class="attr">rpcpassword</span>=...</span><br></pre></td></tr></table></figure></li><li><p>启动守护进程后台运行 ,<br><code>$ bitcoind -daemon</code></p></li><li><p>监视状态<br><code>$ bitcoin-cli getinfo</code></p></li><li><p>RPC</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli getinfo  <span class="comment">#return json</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli getrawtransaction txid  <span class="comment">#return hash-cont</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli decoderawransaction   <span class="built_in">hash</span>-cont  <span class="comment">#ret json</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli gettransaction txid   <span class="comment"># json</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli getblockhash   bloack-id   <span class="comment"># ret block-hash</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli getblock  block-hash <span class="comment">#ret json</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli getnewaddress <span class="comment"># ret 64位 十六进制的数  addr   公钥</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> bitcoin-cli dumpprivkey addr <span class="comment"># ret 私钥</span></span></span><br></pre></td></tr></table></figure></li></ul><p><a id="markdown-3-密钥与地址" name="3-密钥与地址"></a></p><h1 id="3-密钥与地址"><a href="#3-密钥与地址" class="headerlink" title="3. 密钥与地址"></a>3. 密钥与地址</h1><p><a id="markdown-31-公钥加密" name="31-公钥加密"></a></p><h2 id="3-1-公钥加密"><a href="#3-1-公钥加密" class="headerlink" title="3.1. 公钥加密"></a>3.1. 公钥加密</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-3.png" alt="introduction-to-bitcoin-3"><br><a id="markdown-32-椭圆曲线乘法" name="32-椭圆曲线乘法"></a></p><h2 id="3-2-椭圆曲线乘法"><a href="#3-2-椭圆曲线乘法" class="headerlink" title="3.2. 椭圆曲线乘法"></a>3.2. 椭圆曲线乘法</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/例如.png" alt="例如"><br>比特币使用 secp256k1标准定义的一种特殊的椭圆曲线和一系列常数<br>secp256k1:</p><script type="math/tex; mode=display">y^2=(x^3+7) mod (p)</script><p>其中 $p=2^{256}-2^{32}-2^9-2^8-2^7-2^6-2^4-1$为素数</p><ul><li>无穷远点: 对应于 加法中的 0 ,  x=y=0(虽然不满足曲线方程,但可作为特殊情况进行检验)</li><li>椭圆曲线加法: 两个点 p1,p2的加法p1+p2 得到一个点 p3,  记 p3’(x,y) 是 直线p1p2与椭圆曲线不同于p1,p2的交点, 则 p3(x,-y)<br>注意p1=p2时, 直线p1p2定义为p1处的切线</li><li>椭圆曲线乘法: $kP = P+P+\ldots+P(k\ times)$<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-4.png" alt="introduction-to-bitcoin-4"></li></ul><p><a id="markdown-33-生成公钥" name="33-生成公钥"></a></p><h2 id="3-3-生成公钥"><a href="#3-3-生成公钥" class="headerlink" title="3.3. 生成公钥"></a>3.3. 生成公钥</h2><p>随机数私钥 k,<br>取椭圆曲线上的一点 G, 称为生成点,<br>公钥 $K = kG$, 注意是 mod p 的域内<br><strong><br>这个过程是不可逆的</strong></p><p><a id="markdown-34-生成比特币地址" name="34-生成比特币地址"></a></p><h2 id="3-4-生成比特币地址"><a href="#3-4-生成比特币地址" class="headerlink" title="3.4. 生成比特币地址"></a>3.4. 生成比特币地址</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-5.png" alt="introduction-to-bitcoin-5"></p><p>增加位权减少长度</p><ul><li>Base64: 26个小写字母、26个大写字母、10个数字以及两个符号(例    如“+”和“/”)，</li><li>Base58: 不包括(0，O，l，I)的大小写字母和数字组成。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-6.png" alt="introduction-to-bitcoin-6"><br><a id="markdown-35-公钥格式" name="35-公钥格式"></a><h2 id="3-5-公钥格式"><a href="#3-5-公钥格式" class="headerlink" title="3.5. 公钥格式"></a>3.5. 公钥格式</h2>一个公钥是一个椭圆曲线上的点(x,    y)。而椭圆曲线实际是一个数学方程，曲线上的点实际是该方程的一个解。因此，知道了公钥的x坐标,通过解方程<script type="math/tex; mode=display">y^2 \ mod \ p =(x^3+7)\ mod\ p</script>得到y坐标。这种方案可以让我们只存储公钥的x<br>坐标，略去y坐标，从而将公钥的大小和存储空间减少了256比特。</li></ul><p>使用二进制算术计算椭圆曲线的时候，y坐标可能是奇数或者偶数，分别对应前面所讲的y值的正负符号</p><p>压缩格式公钥和非压缩格式公钥看起来不同,但是对应着同样的一个私钥。更重要的是，如果我们使用双哈希函数(RIPEMD160(SHA256(K)))将压缩格式公钥转化成比特币地址，得到的地址将会不同于由非压缩格式公钥产生的地址。这种结果会让人迷惑，因为一个私钥可以生成两种不同格式的公钥——压缩格式和非压缩格式，而这两种格式的公钥可以生成两个不同的比特币地址。但是，这两个不同的比特币地址的私钥是一样的。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-7.png" alt="introduction-to-bitcoin-7"><br><a id="markdown-36-私钥格式" name="36-私钥格式"></a></p><h2 id="3-6-私钥格式"><a href="#3-6-私钥格式" class="headerlink" title="3.6. 私钥格式"></a>3.6. 私钥格式</h2><p>当一个私钥被使用WIF压缩格式导出时，不但没有压缩，而且比“非压缩格式”私钥长出一个字节。这个多出来的一个字节是私钥被加了后缀01，用以表明该私钥是来自于一个较新的钱包，    只能被用来生成压缩的公钥。<br><strong>私钥是非压缩的，也不能被压缩</strong>。“压缩的私钥”实际上只是表示“用于生成压缩格式公钥的私钥”，而“非压缩格式私钥”用来表明“用于生成非压缩格式公钥的私钥”。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/eg.png" alt="eg"></p><p>十六进制压缩私钥格式在末尾有一个额外的字节(十六进制为01)。虽然Base58编码版本前缀对于WIF和WIF压缩格式都是相同的(0x80)，但在数字末尾添加一个字节会导致Base58编码的第一个字符从5变为K或    L，考虑到对于Base58这是十进制编码100号和99号之间的差别。对于100是一个数字长于99的数字，它有一个前缀1，而不是前缀9。当长度变化，它会影响前缀。    在Base58中，前缀5改变为K或L，因为数字的长度增加一个字节。</p><p>要注意的是，这些格式并不是可互换使用的。在实现了压缩格式公钥的较新的钱包中，私钥只能且永远被导出为WIF压    缩格式(以K或L为前缀)。对于较老的没有实现压缩格式公钥的钱包，私钥将只能被导出为WIF格式(以5为前缀)导    出。这样做的目的就是为了给导入这些私钥的钱包一个信号：是否钱包必须搜索区块链寻找压缩或非压缩公钥和地址。</p><p><strong>最全面的比特币Python库是    Vitalik    Buterin写的    pybitcointools</strong></p><p><a id="markdown-37-高级密钥和地址" name="37-高级密钥和地址"></a></p><h2 id="3-7-高级密钥和地址"><a href="#3-7-高级密钥和地址" class="headerlink" title="3.7. 高级密钥和地址"></a>3.7. 高级密钥和地址</h2><p><a id="markdown-371-加密私钥" name="371-加密私钥"></a></p><h3 id="3-7-1-加密私钥"><a href="#3-7-1-加密私钥" class="headerlink" title="3.7.1. 加密私钥"></a>3.7.1. 加密私钥</h3><ul><li><p>加密标准— BIP0038: 使用一个口令加密私钥并使用Base58Check对加密的私钥进行编码，这样加密的私钥就可以安全地保存在备份介质里，安全地在钱包间传输，保持密钥在任何可能被暴露情况下的安全性。(使用了 AES)</p></li><li><p>BIP0038加密方案：输入一个比特币私钥，通常使用WIF编码过，base58chek字符串的前缀“5”。此外BIP0038加密方案需要一个长密码作为口令，通常由多个单词或一段复杂的数字字母字符串组成。BIP0038加密方案的结果是一个由    base58check编码过的加密私钥，前缀为6P。如果你看到一个6P开头的的密钥，这就意味着该密钥是被加密过，并需要一个口令来转换(解码)该密钥回到可被用在任何钱包WIF格式的私钥(前缀为5)</p></li></ul><p><a id="markdown-372-p2shpay-to-script-hash" name="372-p2shpay-to-script-hash"></a></p><h3 id="3-7-2-P2SH-Pay-to-Script-Hash"><a href="#3-7-2-P2SH-Pay-to-Script-Hash" class="headerlink" title="3.7.2. P2SH(Pay-to-Script Hash)"></a>3.7.2. P2SH(Pay-to-Script Hash)</h3><p>以数字3开头的比特币地址是P2SH地址，有时<strong>被错误的称谓多重签名或多重签名地址</strong>。他们指定比特币交易中受益人为哈希的脚本，而不是公钥的所有者</p><p>不同于P2PKH交易发送资金到传统1开头的比特币地址，资金被发送到3开头的地址时，需要的不仅仅是一个公钥的哈希值和一个私钥签名作为所有者证明。在创建地址的时候，这些要求会被指定在脚本中，所有对地址的输入都会被这些要求阻隔。</p><p>一个P2SH地址从交易脚本中创建，它定义谁能消耗这个交易输出<br><code>script hash =RIPEMD160(SHA256(script))</code></p><p>产生的脚本哈希由Base58Check编码前缀为5的版本、编码后得到开头为3的编码地址</p><p>P2SH函数最常见的实现是多重签名地址脚本。顾名思义，底层脚本需要多个签名来证明所有权，此后才能消费资金。设计比特币多重签名特性是需要从总共N个密钥中需要M个签名(也被称为“阈值”)，被称为M-N多签名，其    中M是等于或小于N。例如，第一章中提到的咖啡店主Bob使用多重签名地址需要1-2签名，一个是属于他的密钥和一个属于他同伴的密钥，以确保其中一方可以签署消费一笔锁定到这个地址的输出。这类似于传统的银行中的一个“联合账户”，其中任何一方配偶可以单独签单消费。</p><p><a id="markdown-38-纸钱包" name="38-纸钱包"></a></p><h2 id="3-8-纸钱包"><a href="#3-8-纸钱包" class="headerlink" title="3.8. 纸钱包"></a>3.8. 纸钱包</h2><p>将公钥和私钥(可以是加密过的)打印在纸上, 这期间都没有经过网络(直接用算法计算出), 所以又被称为冷钱包.<br><a id="markdown-4-钱包" name="4-钱包"></a></p><h1 id="4-钱包"><a href="#4-钱包" class="headerlink" title="4. 钱包"></a>4. 钱包</h1><p>广义上，钱包是一个应用程序，为用户提供交互界面。钱包控制用户访问权限，管理密钥和地址，跟踪余额以及创建和签名交易。    狭义上，即从程序员的角度来看，“钱包”是指用于存储和管理用户密钥的数据结构。</p><p>一个常见误解是，比特币钱包里含有比特币。    事实上，钱包里只含有钥匙。    “钱币”被记录在比特币网络的区块链中。    用户通过钱包中的密钥签名交易，从而来控制网络上的钱币。    在某种意义上，比特币钱包是密钥链。<br><a id="markdown-41-钱包种类" name="41-钱包种类"></a></p><h2 id="4-1-钱包种类"><a href="#4-1-钱包种类" class="headerlink" title="4.1. 钱包种类"></a>4.1. 钱包种类</h2><p>每个用户有一个包含多个密钥的钱包。根据包含的多个密钥是否相互关联,可以分为两类<br><a id="markdown-411-非确定性钱包nondeterministic-wallet" name="411-非确定性钱包nondeterministic-wallet"></a></p><h3 id="4-1-1-非确定性钱包-nondeterministic-wallet"><a href="#4-1-1-非确定性钱包-nondeterministic-wallet" class="headerlink" title="4.1.1. 非确定性钱包(nondeterministic wallet)"></a>4.1.1. 非确定性钱包(nondeterministic wallet)</h3><p>其中每个密钥都是从随机数独立生成的。密钥彼此无关。这种钱包也被称为“Just    a    Bunch    Of    Keys(一堆密钥)”，简称 <strong>JBOK</strong> 钱包。<br><a id="markdown-412-确定性钱包deterministic-wallet" name="412-确定性钱包deterministic-wallet"></a></p><h3 id="4-1-2-确定性钱包-deterministic-wallet"><a href="#4-1-2-确定性钱包-deterministic-wallet" class="headerlink" title="4.1.2. 确定性钱包(deterministic wallet)"></a>4.1.2. 确定性钱包(deterministic wallet)</h3><p>其中所有的密钥都是从一个主密钥派生出来，这个主密钥即为种子(seed)。该类型钱包中所有密钥都相互关联，如果有原始种子，则可以再次生成全部密钥。确定性钱包中使用了许多不同的密钥推导方法。最常用的推导方法是使用树状结构，称为分级确定性钱包或HD钱包。</p><p><a id="markdown-42-hd钱包" name="42-hd钱包"></a></p><h2 id="4-2-HD钱包"><a href="#4-2-HD钱包" class="headerlink" title="4.2. HD钱包"></a>4.2. HD钱包</h2><p>HD钱包包含以树状结构衍生的密钥<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-8.png" alt="introduction-to-bitcoin-8"></p><p>优点:</p><ul><li>树状结构可以被用来表达额外的组织含义。比如当一个特定分支的子密钥被用来接收交易收入并且有另一个分支的子密钥用来负责支付花费。不同分支的密钥都可以被用在企业环境中，这就可以支配不同的分支部门、子公司、具体功能以及会计类别。</li><li>允许使用者去建立一个公共密钥的序列而不需要访问相对应的私钥。这可允许HD钱包在不安全的服务器中使用或者在每笔交易中发行不同的公共钥匙。公共钥匙不需要被预先加载或者提前衍生，而在服务器中不需要可用来支付的私钥。</li></ul><p><a id="markdown-43-种子和助记词" name="43-种子和助记词"></a></p><h2 id="4-3-种子和助记词"><a href="#4-3-种子和助记词" class="headerlink" title="4.3. 种子和助记词"></a>4.3. 种子和助记词</h2><p>由一系列英文单词生成种子是个标准化的方<br>法，这样易于在钱包中转移、导出和导入。<br>这些英文单词被称为助记词，标准由BIP-39定义<br><a id="markdown-431-创建助记词" name="431-创建助记词"></a></p><h3 id="4-3-1-创建助记词"><a href="#4-3-1-创建助记词" class="headerlink" title="4.3.1. 创建助记词"></a>4.3.1. 创建助记词</h3><p>助记词是由钱包使用BIP-39中定义的标准化过程自动生成的。    钱包从熵源开始，增加校验和，然后将熵映射到单词列表：<br>1、创建一个128到256位的随机序列(熵)。<br>2、提出SHA256哈希前几位(熵长/    32)，就可以创造一个随机序列的校验和。<br>3、将校验和添加到随机序列的末尾。<br>4、将序列划分为包含11位的不同部分。<br>5、将每个包含11位部分的值与一个已经预先定义2048个单词的字典做对应。<br>6、生成的有顺序的单词组就是助记码。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-9.png" alt="introduction-to-bitcoin-9"><br><a id="markdown-432-从助记词得到种子" name="432-从助记词得到种子"></a></p><h3 id="4-3-2-从助记词得到种子"><a href="#4-3-2-从助记词得到种子" class="headerlink" title="4.3.2. 从助记词得到种子"></a>4.3.2. 从助记词得到种子</h3><p>助记词表示长度为128至256位的熵。    通过使用密钥延伸函数PBKDF2，熵被用于导出较长的<br>(512位)种子。将所得的种子用于构建确定性钱包并得到其密钥。<br>密钥延伸函数有两个参数：助记词和盐。其中盐的目的是增加构建能够进行暴力攻击的查找<br>表的困难度。</p><p>7、PBKDF2密钥延伸函数的第一个参数是从步骤6生成的助记符。<br>8、PBKDF2密钥延伸函数的第二个参数是盐。    由字符串常数“助记词”与可选的用户提供的密码字符串连接组成。<br>9、PBKDF2使用HMAC-SHA512算法，使用2048次哈希来延伸助记符和盐参数，产生一个512位的值作为其最终输出。    这个512位的值就是种子。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-10.png" alt="introduction-to-bitcoin-10"><br><strong><br>密钥延伸函数，使用2048次哈希是一种非常有效的保护，可以防止对助记词或密码短语的暴力攻击。    它使得攻击尝试非常昂贵(从计算的角度)，需要尝试超过几千个密码和助记符组合，而这样可能产生的种子的数量是巨大的(2^512)。</strong></p><p><a id="markdown-44-可选密码短语" name="44-可选密码短语"></a></p><h2 id="4-4-可选密码短语"><a href="#4-4-可选密码短语" class="headerlink" title="4.4. 可选密码短语"></a>4.4. 可选密码短语</h2><p>BIP-39标准允许在推导种子时使用可选的密码短语。    如果没有使用密码短语，助记词是用由常量字符串“助记词”构成的盐进行延伸，从任何给定的助记词产生一个特定的512位种子。    如果使用密码短语，密钥延伸函数使用同样的助记词也会产生不同的种子。</p><p><strong></strong>BIP-39中没有“错误的”密码短语。    每个密码都会导致一些钱包，只是未使用的钱包是空的。<strong></strong></p><p><a id="markdown-441-功能" name="441-功能"></a></p><h3 id="4-4-1-功能"><a href="#4-4-1-功能" class="headerlink" title="4.4.1. 功能"></a>4.4.1. 功能</h3><ul><li>(存储在大脑中的)密码短语成为第二个因素，使得助记词不能单独使用，避免了助记词备份盗取后被利用。    </li><li>起到掩人耳目的效果，把密码短语指向有小额资金的钱包，分散攻击者注意力，使其不在关注拥有大额资金的“真实”钱包。</li></ul><p><a id="markdown-442-风险" name="442-风险"></a></p><h3 id="4-4-2-风险"><a href="#4-4-2-风险" class="headerlink" title="4.4.2. 风险"></a>4.4.2. 风险</h3><p>如果钱包所有者无行为能力或死亡，没有人知道密码，种子是无用的，所有存储在钱包中的资金都将永远丢失。相反，如果所有者将密码短语与种子备份在相同的地方，则违反了上述第二个因素的目的。虽然密码是非常有用的，但它们只能与仔细计划的备份和恢复流程结合使用，考虑到所有者个人风险的可能性，应该允许其家人恢复加密资产。</p><p><a id="markdown-45-从种子中创造-hd-钱包" name="45-从种子中创造-hd-钱包"></a></p><h2 id="4-5-从种子中创造-HD-钱包"><a href="#4-5-从种子中创造-HD-钱包" class="headerlink" title="4.5. 从种子中创造 HD 钱包"></a>4.5. 从种子中创造 HD 钱包</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-11.png" alt="introduction-to-bitcoin-11"></p><p><a id="markdown-451-私有子密钥的衍生更" name="451-私有子密钥的衍生更"></a></p><h3 id="4-5-1-私有子密钥的衍生"><a href="#4-5-1-私有子密钥的衍生" class="headerlink" title="4.5.1. 私有子密钥的衍生"></a>4.5.1. 私有子密钥的衍生</h3><p>分层确定性钱包使用CKD(child    key    derivation)函数去从母密钥衍生出子密钥。<br>子密钥衍生函数是基于单项哈希函数。这个函数结合了：</p><ul><li>一个母私钥或者公共钥匙(ECDSA未压缩键)</li><li>一个叫做链码(256    bits)的种子</li><li>一个索引号(32    bits)</li></ul><p>链码是用来给这个过程引入确定性随机数据的，使得索引不能充分衍生其他的子密钥。因此，有了子密钥并不能让它发现自己的姊妹密钥，除非你已经有了链码。最初的链码种子(在密码树的根部)是用随机数据构成的，随后链码从各自的母链码中衍生出来。</p><p>母公共钥匙——链码——以及索引号合并在一起并且用HMAC-SHA512函数散列之后可以得到512位的散列。所得的散列可被拆分为两部分。散列右半部分的256位产出可以给子链当链码。左半部分256位散列以及索引码被加载在母私钥上来衍生子私钥。 如上图</p><p>改变索引可以让我们延长母密钥以及创造序列中的其他子密钥。比如子0，子1，子2等等。每一个母密钥可以有2,147,483,647    (2^31)    个子密钥。2^31是整个2^32范围可用的一半，因为另一半是为特定类型的推导而保留的.</p><p>如下则是扩展母公钥来衍生子公钥的传递机制。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-12.png" alt="introduction-to-bitcoin-12"></p><p><a id="markdown-452-扩展密钥" name="452-扩展密钥"></a></p><h3 id="4-5-2-扩展密钥"><a href="#4-5-2-扩展密钥" class="headerlink" title="4.5.2. 扩展密钥"></a>4.5.2. 扩展密钥</h3><p>密钥以及链码的结合，就叫做扩展密钥(extended    key). 可以简单地被储存并且表示为简单的将256位密钥与256位链码所并联的<strong>512</strong>位序列。</p><p>扩展密钥通过Base58Check来编码，从而能轻易地在不同的BIP-32兼容钱包之间导入导出。扩展密钥编码用的    Base58Check使用特殊的版本号，这导致在Base58编码字符中，出现前缀“xprv”和“xpub”。<br><a id="markdown-453-公共子密钥的推导" name="453-公共子密钥的推导"></a></p><h3 id="4-5-3-公共子密钥的推导"><a href="#4-5-3-公共子密钥的推导" class="headerlink" title="4.5.3. 公共子密钥的推导"></a>4.5.3. 公共子密钥的推导</h3><p>分层确定性钱包的一个很有用的特点就是可以不通过私钥而直接从公共母密钥派生出公共子密钥的能    力。所以有两种衍生子公钥的方法：通过子私钥，或者就是直接通过母公钥。</p><p>因此，扩展密钥可以在HD钱包结构的分支中，被用来衍生所有的公钥(且只有公钥)。</p><p><strong>应用</strong>: 用来创造非常保密的只有公钥配置。在配置中，服务器或者应用程序不管有没有私钥，都可以有扩展公钥的副本。这种配置可以创造出无限数量的公钥以及比特币地址。但是发送到这个地址里的任何比特币都不能使用。与此同时，在另一种更保险的服务器上，扩展私钥可以衍生出所有的对应的可签署交易以及花钱的私钥。</p><p>扩展的私钥可以被储存在纸质钱包中或者硬件设备中(比如    Trezor    硬件钱包)，与此同时扩展公钥可以在线保存。根据意愿创造“接收”地址而私钥可以安全地在线下被保存。为了支付资金，使用者可以使用扩展的私钥离线签署比特币客户或者通过硬件钱包设备(比如    Trezor)签署交易。</p><p>这种方案的常见应用是安装扩展公钥电商的网络服务器上。网络服务器可以使用这个公钥衍生函数去给每一笔交易(比如客户的购物车)创造一个新的比特币地址。但为了避免被偷，网络服务商不会有任何私钥。没有HD钱包的话，唯一的方法就是在不同的安全服务器上创造成千上万个比特币地址，之后就提前上传到电商服务器上。这种方法比较繁琐而且要求持续的维护来确保电商服务器不“用光”公钥。</p><p><a id="markdown-454-硬化子密钥的衍生" name="454-硬化子密钥的衍生"></a></p><h3 id="4-5-4-硬化子密钥的衍生"><a href="#4-5-4-硬化子密钥的衍生" class="headerlink" title="4.5.4. 硬化子密钥的衍生"></a>4.5.4. 硬化子密钥的衍生</h3><p><strong><br>访问扩展公钥并不能得到访问子私钥的途径。但是，因为扩展公钥包含有链码，如果子私钥被知道或者被泄漏的话，链码就可以被用来衍生所有的其他子私钥</strong></p><p>为了应对这种风险，HD钱包使用一种叫做硬化衍生(hardened    derivation)的替代衍生函数。</p><p>这就“打破”了母公钥以及子链码之间的关系。这个硬化衍生函数使用了母私钥去推导子链码，而不是母公钥。这就在母/子顺序中创造了一道“防火墙”——有链码但并不能够用来推算子链码或者姊妹私钥。强化衍生函数看起来几乎与一般的衍生的子私钥相同，不同的是母私钥被用来输入散列函数中而不是母公钥，<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-13.png" alt="introduction-to-bitcoin-13"></p><p><a id="markdown-455-索引号码" name="455-索引号码"></a></p><h3 id="4-5-5-索引号码"><a href="#4-5-5-索引号码" class="headerlink" title="4.5.5. 索引号码"></a>4.5.5. 索引号码</h3><p>用在衍生函数中的索引号码是32位的整数。为了区分密钥是从正常衍生函数中衍生出来还是从强化衍生函数中产出，这个索引号被分为两个范围。索引号在0和2^31–1(0x0    to0x7FFFFFFF)之间的是只被用在常规衍生。索引号在2^31和2^32–    1(0x80000000    to 0xFFFFFFFF)之间的只被用在强化衍生。</p><p><a id="markdown-456-钱包密钥识别符路径" name="456-钱包密钥识别符路径"></a></p><h3 id="4-5-6-钱包密钥识别符-路径"><a href="#4-5-6-钱包密钥识别符-路径" class="headerlink" title="4.5.6. 钱包密钥识别符(路径)"></a>4.5.6. 钱包密钥识别符(路径)</h3><p>HD钱包中的密钥是用“路径”命名的，且每个级别之间用斜杠(/)字符来表示。由主私钥衍生出的私钥起始以“m”打头。由主公钥衍生的公钥起始以“M“打头。因此，母密钥生成的第一个子私钥是m/0。第一个公钥是M/0。第一个子密钥的子密钥就是m/0/1，以此类推。</p><p><a id="markdown-457-hd钱包树状结构的导航" name="457-hd钱包树状结构的导航"></a></p><h3 id="4-5-7-HD钱包树状结构的导航"><a href="#4-5-7-HD钱包树状结构的导航" class="headerlink" title="4.5.7. HD钱包树状结构的导航"></a>4.5.7. HD钱包树状结构的导航</h3><p>HD钱包树状结构提供了极大的灵活性。每一个母扩展密钥有40亿个子密钥：20亿个常规子密钥和20亿个强化子密钥。    而每个子密钥又会有40亿个子密钥并且以此类推。</p><p>由此带来问题,对无限的树状结构进行导航就变得异常困难。尤其是对于在不同的HD钱包之间进行转移交易，因为内部组织到内部分支以及亚分支的可能性是无穷的。</p><p>两个比特币改进建议(BIPs)</p><ul><li>通过创建几个HD钱包树的提议标准。BIP-43提出使用第一个强化子索引作为特殊的标识符表示树状结构的“purpose”。</li><li>基于BIP-43，HD钱包应该使用且只用第一层级的树的分支，而且有索引号码去识别结构并且有命名空间来定义剩余的树的目的地。举个例子，HD钱包只使用分支m/i’/是    为了表明那个被索引号“i”定义的特殊为目地。</li></ul><p>BIP-44指定了包含5个预定义树状层级的结构：<br><code>m    /    purpose&#39;    /    coin_type&#39;    /    account&#39;    /    change    /    address_index</code></p><p>第一层的purpose总是被设定为44’。</p><p>第二层的“coin_type”特指币种并且允许多元货币HD钱包中的货币在第二个层级下有自己的亚树状结构。<br>目前有三种货币被定义：Bitcoin    is    m/44’/0’、Bitcoin    Testnet    is    m/44’/1’，以及 Litecoin    is    m/44’/2’。</p><p>第三层级是“account”，举个例子，一个HD钱包可能包含两个比特币“账户”：m/44’/0’/0’和    m/44’/0’/1’。每个账户都是它自己亚树的根。</p><p>第四层级就是“change”。注意无论先前的层级是否使用强化衍生，这一层级使用的都是常规衍生。这是为了允许这一层级的树可以在不安全环境下，输出扩展公钥。</p><p>被HD钱包衍生的可用的地址是第四层级的子级，就是第五层级的树的“address_index”</p><p>如<img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-14.png" alt="introduction-to-bitcoin-14"></p><p><a id="markdown-5-交易" name="5-交易"></a></p><h1 id="5-交易"><a href="#5-交易" class="headerlink" title="5. 交易"></a>5. 交易</h1><p>根据比特币系统的设计原理，系统中任何其他的部分都是为了确保比特币交易可以被生成、能在比特币网络中得以传播和通过验证，并最终添加入全球比特币交易总账簿(比特币区块链)。比特币交易的本质是数据结构，这些数据结构中含有比特币交易参与者价值转移的相关信息。比特币区块链是一本全球复式记账总账簿，每个比特币交易都是在比特币区块链上的一个公开记录。</p><p><a id="markdown-51-utxo" name="51-utxo"></a></p><h2 id="5-1-UTXO"><a href="#5-1-UTXO" class="headerlink" title="5.1. UTXO"></a>5.1. UTXO</h2><p>比特币交易中的基础构建单元是<strong>交易输出</strong>。    交易输出是比特币不可分割的基本组合，记录在区块上，并被整个网络识别为有效。    比特币完整节点跟踪所有可找到的和可使用的输出，称为    “未花费的交易输出”(unspent    transaction    outputs)，即UTXO。所有UTXO的集合被称为UTXO集。<strong>每一个交易都代表UTXO集的变化(状态转换)。</strong></p><p>用户的比特币“余额”是指用户钱包中可用的UTXO总和. 比特币钱包通过扫描区块链并聚集所有属于该用户的UTXO来计算该用户的余额    。大多数钱包维护一个数据库或使用数据库服务来存储所有UTXO的快速参考集，这些UTXO由用户所有的密钥来控制花费行为。</p><p><strong>一个UTXO只能在一次交易中作为一个整体被消耗。一定数量的比特币价值在不同所有者之间转移，并在交易链中消耗和创建UTXO。一笔比特币交易通过使用所有者的签名来解锁UTXO，并通过使用新的所有者的比特币地址来锁定并创建UTXO</strong><br><a id="markdown-52-币基交易coinbasetransaction" name="52-币基交易coinbasetransaction"></a></p><h2 id="5-2-币基交易-CoinbaseTransaction"><a href="#5-2-币基交易-CoinbaseTransaction" class="headerlink" title="5.2. 币基交易(CoinbaseTransaction)"></a>5.2. 币基交易(CoinbaseTransaction)</h2><p>它是每个区块中的第一笔交易，这种交易存在的原因是作为对挖矿的奖励，创造出全新的可花费比特币用来支付给“赢家”矿工。</p><p><strong></strong>输入和输出，哪一个是先产生的呢？先有鸡还是先有蛋呢？严格来讲，先产生输出，因为可以创造新比特币的    “币基交易”没有输入，但它可以无中生有地产生输出。<strong></strong><br><a id="markdown-53-交易输出" name="53-交易输出"></a></p><h2 id="5-3-交易输出"><a href="#5-3-交易输出" class="headerlink" title="5.3. 交易输出"></a>5.3. 交易输出</h2><p>交易输出包含两部分：</p><ul><li>一定量的比特币，面值为“聪”(satoshis),是最小的比特币单位；</li><li>确定花费输出所需条件的加密难题(cryptographic    puzzle)</li></ul><p>这个加密难题也被称为锁定脚本(locking    script),    见证脚本(witness    script),    或脚本公钥(scriptPubKey)。</p><p>如下面的交易包含两个输出,<br>每个输出包含 比特币的值(本身编码是以聪为单位, 以json解码后单位是 比特币), 以及锁定脚本<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"vout"</span>:[</span><br><span class="line">truetrue&#123;</span><br><span class="line">truetruetruetrue<span class="string">"value"</span>:<span class="number">0.01500000</span>,</span><br><span class="line">truetruetruetrue<span class="string">"scriptPubKey"</span>:<span class="string">"OP_DUPOP_HASH160ab68025513c3dbd2f7b92a94e0581f5d50f654e7OP_EQU</span></span><br><span class="line"><span class="string">ALVERIFY</span></span><br><span class="line"><span class="string">OP_CHECKSIG"</span></span><br><span class="line">truetrue&#125;,</span><br><span class="line">truetrue&#123;</span><br><span class="line">truetruetruetrue<span class="string">"value"</span>:<span class="number">0.08450000</span>,</span><br><span class="line">truetruetruetrue<span class="string">"scriptPubKey"</span>:<span class="string">"OP_DUPOP_HASH1607f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8OP_EQU</span></span><br><span class="line"><span class="string">ALVERIFYOP_CHECKSIG"</span>,</span><br><span class="line">truetrue&#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure></p><p><a id="markdown-54-交易输入" name="54-交易输入"></a></p><h2 id="5-4-交易输入"><a href="#5-4-交易输入" class="headerlink" title="5.4. 交易输入"></a>5.4. 交易输入</h2><p>包含</p><ul><li>一个指向UTXO的指针，通过指向UTXO被记录在区块链中所在的交易的哈希值和序列号来实现。    </li><li>解锁脚本，钱包构建它用以满足设定在UTXO中的支出条件。    大多数情况下，解锁脚本是一个证明比特币所有权的数字签名和公钥，但是并不是所有的解锁脚本都包含签名。    </li><li>序列号。</li></ul><p>如下面的交易包含一个输入</p><ul><li>txid: 引用的 UTXO 交易,</li><li>vout(输出索引) : 标识来自txid的交易的哪个输出被引用(0-indexed)</li><li>scriptSig(解锁脚本): 满足放置在 UTXO 上的条件,解锁它用于支出</li><li>sequence: 序列号</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"vin"</span>:[</span><br><span class="line">truetrue&#123;</span><br><span class="line">truetruetruetrue<span class="string">"txid"</span>:<span class="string">"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18"</span>,</span><br><span class="line">truetruetruetrue<span class="string">"vout"</span>:<span class="number">0</span>,</span><br><span class="line">truetruetruetrue<span class="string">"scriptSig"</span>:<span class="string">"3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c</span></span><br><span class="line"><span class="string">4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL]0484e</span></span><br><span class="line"><span class="string">cc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457</span></span><br><span class="line"><span class="string">eee41c04f4938de5cc17b4a10fa336a8d752adf"</span>,</span><br><span class="line">truetruetruetrue<span class="string">"sequence"</span>:<span class="number">4294967295</span></span><br><span class="line">truetrue&#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p><a id="markdown-55-交易过程" name="55-交易过程"></a></p><h2 id="5-5-交易过程"><a href="#5-5-交易过程" class="headerlink" title="5.5. 交易过程"></a>5.5. 交易过程</h2><p>首先检索引用的UTXO，检查其锁定脚本，然后使用它来构建所需的解锁脚本以满足此要求。</p><p><strong><br>除了对包含它引用的交易之外，我们无从了解这个UTXO的任何内容。我们不知道它的价值(多少satoshi金额)，我们不知道设置支出条件的锁定脚本。要找到这些信息，我们必须通过检索整个交易来检索被引用的UTXO。</strong></p><p>然后运行解锁脚本与锁定脚本, 检查结果是否为True</p><p><a id="markdown-56-交易费" name="56-交易费"></a></p><h2 id="5-6-交易费"><a href="#5-6-交易费" class="headerlink" title="5.6. 交易费"></a>5.6. 交易费</h2><p>大多数交易包含交易费(矿工费)，这是为了确保网络安全而给比特币矿工的一种补偿。费用本身也作为一个安全机制，使经济上不利于攻击者通过交易来淹没网络。</p><p>大多数钱包自动计算并计入交易费。但是，    如果你以编程方式构造交易，或者使用命令行界面，你必须手动计算并计入这些费用。</p><p>任何创建交易的比特币服务，包括钱包，交易所，零售应用等，都必须实现动态收费。动态费用可以通过第三方费用估算服务或内置的费用估算算法来实现</p><p>费用估算算法根据网络能力和“竞争”交易提供的费用计算适当的费用。大多数服务为用户提供高、中、低优先费用的选择。高优先级意味着用户支付更高交易费.</p><blockquote><p>交易费即输入总和减输出总和的余量：交易费    =    求和(所有输入)    -    求和(所有输出)</p></blockquote><p><strong><br>如果你忘记了在手动构造的交易中增加找零的输出，系统会把找零当作交易费来处理。“不用找了！”也许不是你的真实意愿。</strong></p><p>一般交易费是根据交易的数据正相关的, 而不是交易的比特币值,所以如果有很多个 输入(很多个 UTXO 零钱), 或很多输出, 造成数据量很大, 而使交易费很多.</p><p><a id="markdown-57-交易脚本语言" name="57-交易脚本语言"></a></p><h2 id="5-7-交易脚本语言"><a href="#5-7-交易脚本语言" class="headerlink" title="5.7. 交易脚本语言"></a>5.7. 交易脚本语言</h2><p><a id="markdown-571-图灵非完备性" name="571-图灵非完备性"></a></p><h3 id="5-7-1-图灵非完备性"><a href="#5-7-1-图灵非完备性" class="headerlink" title="5.7.1. 图灵非完备性"></a>5.7.1. 图灵非完备性</h3><p>有条件的流控制以外，没有循环或复杂流控制能力。这限制确保该语言不被用于创造无限循环或其它类型的逻辑炸弹，这样的炸弹可以植入在一笔交易中，引起针对比特币网络的“拒绝服务”攻击。因为每一笔交易都会被网络中的全节点验证，受限制的语言能防止交易验证机制被作为一个漏洞而加以利用。</p><p><a id="markdown-572-去中心化验证" name="572-去中心化验证"></a></p><h3 id="5-7-2-去中心化验证"><a href="#5-7-2-去中心化验证" class="headerlink" title="5.7.2. 去中心化验证"></a>5.7.2. 去中心化验证</h3><p>没有任何中心主体能凌驾于脚本之上，也没有中心主体会在脚本被执行后对其进行保存。所以执行脚本所需信息都已包含在脚本中。<strong>一个脚本能在任何系统上以相同的方式执行。</strong></p><p><a id="markdown-573-脚本构建锁定与解锁" name="573-脚本构建锁定与解锁"></a></p><h3 id="5-7-3-脚本构建-锁定与解锁"><a href="#5-7-3-脚本构建-锁定与解锁" class="headerlink" title="5.7.3. 脚本构建(锁定与解锁)"></a>5.7.3. 脚本构建(锁定与解锁)</h3><p><a id="markdown-5731-锁定脚本locking-script" name="5731-锁定脚本locking-script"></a></p><h4 id="5-7-3-1-锁定脚本-Locking-Script"><a href="#5-7-3-1-锁定脚本-Locking-Script" class="headerlink" title="5.7.3.1. 锁定脚本(Locking Script)"></a>5.7.3.1. 锁定脚本(Locking Script)</h4><p>一个放置在输出上面的花费条件.它指定了今后花费这笔输出必须要满足的条件。曾被称为脚本公钥(scriptPubKey) , 也被称为见证脚本(witness    script)，或者更一般地说，它是一个加密难题(cryptographic puzzle)。这些术语在不同的抽象层次上都意味着同样的东西。</p><p><a id="markdown-5732-解锁脚本unlocking-script" name="5732-解锁脚本unlocking-script"></a></p><h4 id="5-7-3-2-解锁脚本-Unlocking-Script"><a href="#5-7-3-2-解锁脚本-Unlocking-Script" class="headerlink" title="5.7.3.2. 解锁脚本(Unlocking Script)"></a>5.7.3.2. 解锁脚本(Unlocking Script)</h4><p>一个“解决”或满足被锁定脚本在一个输出上设定的花费条件,从而允许输出被消费的脚本。解锁脚本是每一笔比特币交易输入的一部分，而且<strong>往往</strong>含有一个由用户的比特币钱包(通过用户的私钥)生成的数字签名,曾被称作ScriptSig。</p><p>每一个比特币验证节点会通过同时执行锁定和解锁脚本来验证一笔交易。每个输入都包含一个解锁脚本，并引用了之前存在的UTXO。    验证软件将复制解锁脚本，检索输入所引用的UTXO，并从该UTXO复制锁定脚本。    然后依次执行解锁和锁定脚本。如果解锁脚本满足锁定脚本条件，则输入有效。所有输入都是独立验证的，作为交易总体验证的一部分。</p><p>形式上两个脚本拼接如下, 如后用栈的方式执行, 右边为栈顶. 最终结果为 TRUE 则 满足条件<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-15.png" alt="introduction-to-bitcoin-15"></p><p>如<br>锁定脚本:   <code>3    OP_ADD    5    OP_EQUAL</code><br>解锁脚本:  <code>2</code><br>拼接后为: <code>2  3    OP_ADD    5    OP_EQUAL</code>  </p><p>实际过程<br>使用堆栈执行引擎执行解锁脚本。如果解锁脚本在执行过程中未报错(例如：没有“悬挂”操作码)，则复制主堆栈(而不是备用堆栈)，并执行锁定脚本。如果从解锁脚本中复制而来的堆栈数据执行锁定脚本的结果为“TRUE”，那么解锁脚本就成功地满足了锁定脚本所设条件</p><p>如<br>锁定脚本: <code>OP_DUP    OP_HASH160    &lt;Cafe    Public    Key    Hash&gt;    OP_EQUALVERIFY    OP_CHECKSIG</code><br>解锁脚本: <code>&lt;Cafe    Signature&gt;    &lt;Cafe    Public    Key&gt;</code><br>验证过程<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-16.png" alt="introduction-to-bitcoin-16"><br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-17.png" alt="introduction-to-bitcoin-17"></p><p><a id="markdown-58-数字签名ecdsa" name="58-数字签名ecdsa"></a></p><h2 id="5-8-数字签名-ECDSA"><a href="#5-8-数字签名-ECDSA" class="headerlink" title="5.8. 数字签名(ECDSA)"></a>5.8. 数字签名(ECDSA)</h2><p><a id="markdown-581-如何工作" name="581-如何工作"></a></p><h3 id="5-8-1-如何工作"><a href="#5-8-1-如何工作" class="headerlink" title="5.8.1. 如何工作"></a>5.8.1. 如何工作</h3><ul><li>使用私钥(签名密钥)从消息(交易)创建签名</li><li>给定消息和公钥允许任何人验证签名</li></ul><p><a id="markdown-582-作用" name="582-作用"></a></p><h3 id="5-8-2-作用"><a href="#5-8-2-作用" class="headerlink" title="5.8.2. 作用"></a>5.8.2. 作用</h3><p>数字签名在不揭示私钥的情况下提供私钥的所有权证明。</p><p>数字签名在比特币中的作用:</p><ol><li>签名证明私钥的所有者，即资金所有者，已经授权支出这些资金。</li><li>授权证明是不可否认的(不可否认性)。</li><li>签名证明交易(或交易的具体部分)在签字之后没有也不能被任何人修改。</li></ol><p><strong><br>每个交易输入和它可能包含的任何签名完全独立于任何其他输入或签名。多方可以协作构建交易，并各自仅签一个输入。</strong></p><p><a id="markdown-583-创建与验证" name="583-创建与验证"></a></p><h3 id="5-8-3-创建与验证"><a href="#5-8-3-创建与验证" class="headerlink" title="5.8.3. 创建与验证"></a>5.8.3. 创建与验证</h3><p><strong>公钥是一个二维数组, 图形上是一个点</strong></p><p>比特币中使用的数字签名算法是椭圆曲线数字签名算法(Elliptic    Curve    Digital    SignatureAlgorithm ,  ECDSA)</p><p>签名算法首先生成一个 ephemeral(临时)私钥(即随机数 $k_{tmp}$), 记临时私钥生成的临时公钥的 x坐标为 $x$, p 是椭圆曲线的主要顺序, 记用户的私钥为k,公钥为K(是一个点) , G是椭圆曲线发生器点.<br>则</p><script type="math/tex; mode=display">y = k_{\text{tmp}}^{-1}(hash(transaction)+x*k )\ mod \ p</script><p>得到签名为  $signature = (x,y)$</p><p>验证过程如下, 计算</p><script type="math/tex; mode=display">(x_{verify},y_{verify}) =y^{-1}( hash(transaction)* G+ x*K)</script><p>如果 $x_{verify} = x$, 则签名有效<br><strong><br>如果在两个不同的交易中，在签名算法中使用相同的值    k，则私钥可以被计算并暴露给世界！</strong></p><p>重用    k    值的最常见原因是未正确初始化的随机数生成器。为了避免这个漏洞，业界最佳实践不是用熵播种的随机数生成器生成    k    值，而是使用交易数据本身播种的确定性随机进程。</p><p><a id="markdown-584-签名序列化der" name="584-签名序列化der"></a></p><h3 id="5-8-4-签名序列化-DER"><a href="#5-8-4-签名序列化-DER" class="headerlink" title="5.8.4. 签名序列化(DER)"></a>5.8.4. 签名序列化(DER)</h3><p>如用 DER((Distinguished    Encoding    Rules)编码后的签名为<br><code>3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301</code><br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-18.png" alt="introduction-to-bitcoin-18"><br>这里 数字签名记为(R,S)</p><p><a id="markdown-585-签名哈希类型sighash" name="585-签名哈希类型sighash"></a></p><h3 id="5-8-5-签名哈希类型-SIGHASH"><a href="#5-8-5-签名哈希类型-SIGHASH" class="headerlink" title="5.8.5. 签名哈希类型(SIGHASH)"></a>5.8.5. 签名哈希类型(SIGHASH)</h3><p>SIGHASH,指示交易数据的哪一部分.SIGHASH    标志是附加到签名的单个字节。每个签名都有一个SIGHASH标志，该标志在不同输入之间也可以不同。 有三个 标志 如下<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-19.png" alt="introduction-to-bitcoin-19"></p><p>另外还有一个修饰符标志SIGHASH_ANYONECANPAY，它可以与前面的每个标志组合。    当设置ANYONECANPAY时，只有一个输入被签名，其余的(及其序列号)打开以进行修改。ANYONECANPAY的值为0x80，并通过按位OR运算，得到如下所示的组合标志：<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-20.png" alt="introduction-to-bitcoin-20"></p><p>SIGHASH标志在签名和验证期间应用的方式是建立交易的副本和删节其中的某些字段(设置长度为零并清空)，继而生成的交易被序列化，SIGHASH标志被添加到序列化交易的结尾，并将结果哈希化    ，得到的哈希值本身即是被签名的“消息”。    基于SIGHASH标志的使用，交易的不同部分被删节。    所得到的哈希值取决于交易中数据的不同子集。</p><p>例如</p><ul><li>ALL | ANYONECANPAY可以用来发起众筹</li><li>NONE 可用于构建特定数量的”不记名支票”或”空白支票”</li><li>NONE | ANYONECANPAY 造可以用来建造一个“吸尘器”。在他们的钱包中拥有微小UTXO的用户无法花费这些费用，因为手续费用超过了这些微小UTXO的价值。借助这种类型的签名，微小UTXO可以为任何人捐赠，以便随时随地收集和消费。</li></ul><p><a id="markdown-6-高级交易和脚本" name="6-高级交易和脚本"></a></p><h1 id="6-高级交易和脚本"><a href="#6-高级交易和脚本" class="headerlink" title="6. 高级交易和脚本"></a>6. 高级交易和脚本</h1><p><a id="markdown-61-多重签名" name="61-多重签名"></a></p><h2 id="6-1-多重签名"><a href="#6-1-多重签名" class="headerlink" title="6.1. 多重签名"></a>6.1. 多重签名</h2><p>多重签名脚本设置了一个条件，其中N个公钥被记录在脚本中，并且至少有M个必须提供签名来解锁资金。这也称为M-N方案，其中N是密钥的总数，M是验证所需的签名的数量。例如，2-3的多重签名是三个公钥被列为潜在签名人，至少有2个有效的签名才能花费资金。</p><p>锁定脚本格式:<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">M <span class="tag">&lt;<span class="name">PubKey</span> <span class="attr">1</span>&gt;</span> <span class="tag">&lt;<span class="name">Pubkey</span> <span class="attr">2</span>&gt;</span> ... <span class="tag">&lt;<span class="name">Pubkey</span> <span class="attr">N</span>&gt;</span> N CHECKMULTISIG</span><br></pre></td></tr></table></figure></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">Signature</span> <span class="attr">1</span>&gt;</span> <span class="tag">&lt;<span class="name">Signature</span> <span class="attr">2</span>&gt;</span> ... <span class="tag">&lt;<span class="name">Signature</span> <span class="attr">M</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然而由于实施中 CHECKMULTISIG 的 bug: 会在弹出解锁脚本时从栈中多弹出一个, 所以<br>解锁脚本规定为<br><figure class="highlight basic"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">0 </span>&lt;Signature <span class="number">1</span>&gt; &lt;Signature <span class="number">2</span>&gt; ... &lt;Signature M&gt;</span><br></pre></td></tr></table></figure></p><p><a id="markdown-62-p2shpay-to-script-hash" name="62-p2shpay-to-script-hash"></a></p><h2 id="6-2-P2SH-Pay-to-Script-Hash"><a href="#6-2-P2SH-Pay-to-Script-Hash" class="headerlink" title="6.2. P2SH(Pay-to-Script-Hash)"></a>6.2. P2SH(Pay-to-Script-Hash)</h2><p><a id="markdown-621-多重签名的问题" name="621-多重签名的问题"></a></p><h3 id="6-2-1-多重签名的问题"><a href="#6-2-1-多重签名的问题" class="headerlink" title="6.2.1. 多重签名的问题"></a>6.2.1. 多重签名的问题</h3><p>P2SH 是针对 <strong>多重签名</strong> 以下问题提出的</p><ul><li>老板要在客户付款前将该脚本发送给每一位客户，而每一位顾客也必须使用特制的能产生客户交易脚本的比特币钱包软件，每位顾客还得学会如何利用脚本来完成交易。</li><li>由于脚本可能包含特别长的公钥，最终的交易脚本可能是最初交易脚本长度几倍。额外长度的脚本将给客户造成费用负担。</li><li>一个长的交易脚本将一直记录在所有节点的随机存储器的UTXO集中，直到该笔资金被使用。采用这种复杂输出脚本使得在实际交易中变得困难重重。</li></ul><p><a id="markdown-622-赎回脚本" name="622-赎回脚本"></a></p><h3 id="6-2-2-赎回脚本"><a href="#6-2-2-赎回脚本" class="headerlink" title="6.2.2. 赎回脚本"></a>6.2.2. 赎回脚本</h3><p>在P2SH    支付中锁定脚本由哈希运算后的20字节的散列值取代，被称为赎回脚本。当一笔交易试图支付UTXO时，要解锁支付脚本，它必须含有与哈希相匹配的脚本。<br>如下<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-21.png" alt="introduction-to-bitcoin-21"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-22.png" alt="introduction-to-bitcoin-22"></p><p>赎回脚本本身之后作为解锁脚本在输出花费时的一部分出现。    这使得给矿工的交易费用从发送方转移到收款方，复杂的计算工作也从发送方转移到收款方。<br><a id="markdown-623-p2sh地址" name="623-p2sh地址"></a></p><h3 id="6-2-3-P2SH地址"><a href="#6-2-3-P2SH地址" class="headerlink" title="6.2.3. P2SH地址"></a>6.2.3. P2SH地址</h3><p>P2SH旨在使复杂脚本的运用能与直接向比特币地址支付一样简单。</p><p>P2SH 能将脚本哈希编译为一个地址, 以“3”为前缀，该地址与一个脚本相对应而非与一个公钥相对应，但是它的效果与比特币地址支付别无二致。</p><p><a id="markdown-624-优点" name="624-优点"></a></p><h3 id="6-2-4-优点"><a href="#6-2-4-优点" class="headerlink" title="6.2.4. 优点"></a>6.2.4. 优点</h3><ul><li>在交易输出中，复杂脚本由简短电子指纹取代，使得交易代码变短。</li><li>脚本能被编译为地址，支付指令的发出者和支付者的比特币钱包不需要复杂工序就可以执行P2SH。</li><li>P2SH将构建脚本的重担转移至接收方，而非发送方。</li><li>P2SH将长脚本数据存储的负担从输出方(存储于UTXO集，影响内存)转移至输入方(存储在区块链里面)。</li><li>P2SH将长脚本数据存储的重担从当前(支付时)转移至未来(花费时)。</li><li>P2SH将长脚本的交易费成本从发送方转移至接收方，接收方在使用该笔资金时必须含有赎回脚本。</li></ul><p><strong><br>不能将P2SH植入P2SH赎回脚本，因为P2SH不能自循环。虽然在技术上可以将RETURN包含在赎回脚本中，但由于规则中没有策略阻止来, 因此在验证期间执行RETURN将导致交易被标记为无效.</strong></p><p><strong><br>P2SH锁定脚本脚本对于赎回脚本本身未提供任何描述。P2SH交易即便在赎回脚本无效的情况下也会被认为有效, 这时可能会被锁死在P2SH这个交易中，导致不能花费这笔比特币.</strong></p><p><a id="markdown-63-数据记录输出return操作符" name="63-数据记录输出return操作符"></a></p><h2 id="6-3-数据记录输出-RETURN操作符"><a href="#6-3-数据记录输出-RETURN操作符" class="headerlink" title="6.3. 数据记录输出(RETURN操作符)"></a>6.3. 数据记录输出(RETURN操作符)</h2><p>运用比特币的区块链技术存储与比特币支付不相关数据, 例如，为文件记录电子指纹，则任何人都可以通过该机制在特定的日期建立关于文档存在性的证明。</p><p>此类交易仅将比特币地址当作自由组合的20个字节而使用，进而会产生不能用于交易的UTXO。因为比特币地址只是被当作数据使用，并不与私钥相匹配，所以会导致UTXO不能被用于交易，因而是一种伪支付行为。因此，这些交易永远不会被花费，所以永远不会从UTXO集中删除，并导致UTXO数据库的大小永远增加或“膨胀”</p><p>Return允许开发者在交易输出上增加80字节的非交易数据。与伪交易型的UTXO不同，Return创造了一种明确的<strong>可复查的非交易型输出</strong>，此类数据无需存储于UTXO集。Return输出被记录在区块链上，会消耗磁盘空间，也会导致区块链规模的增加，但它们不存储在UTXO集中，因此也不会使得UTXO内存膨胀.</p><p>RETURN    不涉及可用于支付的解锁脚本的特点，    RETURN    不能使用其输出中所锁定的资金，因此没有必要记录在蕴含潜在成本的UTXO集中，所以    RETURN    实际是没有成本的。</p><p>RETURN    常为一个金额为0的比特币输出，    因为任何与该输出相对应的比特币都会永久消失。假如一笔    RETURN    被作为一笔交易的输入，脚本验证引擎将会阻止验证脚本的执行，将标记交易为无效</p><p><a id="markdown-64-时间锁timelocks" name="64-时间锁timelocks"></a></p><h2 id="6-4-时间锁-Timelocks"><a href="#6-4-时间锁-Timelocks" class="headerlink" title="6.4. 时间锁(Timelocks)"></a>6.4. 时间锁(Timelocks)</h2><p>时间锁是只允许在一段时间后才允许支出的交易,锁定时间也称为nLocktime.</p><ul><li>nLocktime=0，即时传播和执行</li><li>0&lt;nLocktime≤5e8，则将其解释为块高度，这意味着交易无效. </li><li>nLocktime＞5e8, 解释为Unix纪元时间戳,并且交易在指定时间之前无效。</li></ul><p>试想, 如果 A 支付 B 一个交易, nLocktime 为3个月后, 那么 B 3个月后才可用这个 UTXO , 如果 A这时再将原来输入的 UTXO 用于其他交易,那么 B 3个月后就不能用了. </p><p>因此,时间限制必须放在UTXO本身上，并成为锁定脚本的一部分，而不是交易。可通过时间锁定的一种形式<code>检查锁定时间验证(CLTV)</code>来实现.<br><a id="markdown-641-检查锁定时间验证checklocktimeverifycltv" name="641-检查锁定时间验证checklocktimeverifycltv"></a></p><h3 id="6-4-1-检查锁定时间验证CheckLockTimeVerify-CLTV"><a href="#6-4-1-检查锁定时间验证CheckLockTimeVerify-CLTV" class="headerlink" title="6.4.1. 检查锁定时间验证CheckLockTimeVerify(CLTV)"></a>6.4.1. 检查锁定时间验证CheckLockTimeVerify(CLTV)</h3><p>通过在输出的赎回脚本中添加CLTV操作码来限制输出，从而只能在指定的时间过后使用. CLTV不会取代nLocktime，而是限制特定的UTXO，并通过将nLocktim设置为更大或相等的值，从而达到在未来才能花费这笔钱的目的。</p><p>一个 P2SH 交易的赎回脚本如下: Alice 转给 Bob的钱, 3个月才到<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">now+3</span> <span class="attr">months</span>&gt;</span>CHECKLOCKTIMEVERIFYDROP DUP HASH160 <span class="tag">&lt;<span class="name">Bob's</span><span class="attr">Public</span><span class="attr">Key</span><span class="attr">Hash</span>&gt;</span> EQUALVERIFY CHECKSIG</span><br></pre></td></tr></table></figure></p><p>如果 Bob 尝试引用(花费)这个 UTXO,他使用他的签名和公钥在该输入的解锁脚本，并将交易nLocktime设置为等于或更大于Alice设置的CHECKLOCKTIMEVERIFY    时间锁。然后Bob在比特币网络上广播交易。</p><p>矿工对交易评估如下:<br>如果Alice设置的CHECKLOCKTIMEVERIFY参数小于或等于支出交易的nLocktime，脚本执行将继续(就好像执行“无操作”或NOP操作码一样). 否则,CHECKLOCKTIMEVERIFY失败并停止执行，标记交易无效:</p><ol><li>堆栈是空的要么</li><li>堆栈中的顶部项小于0;要么</li><li>顶层堆栈项和nLocktime字段的锁定时间类型(高度或者时间戳)不相同;要么</li><li>顶层堆栈项大于交易的nLocktime字段;要么</li><li>输入的nSequence字段为0xffffffff。</li></ol><p><a id="markdown-642-相对时间锁" name="642-相对时间锁"></a></p><h3 id="6-4-2-相对时间锁"><a href="#6-4-2-相对时间锁" class="headerlink" title="6.4.2. 相对时间锁"></a>6.4.2. 相对时间锁</h3><p>nLocktime和CLTV都是绝对时间锁定，它们指定绝对时间点。</p><p>它们允许将两个或多个相互依赖的交易链接在一起，同时对依赖于从先前交易的确认所经过的时间的一个交易施加时间约束。换句话说，在UTXO被记录在块状块之前，时钟不开始计数。这个功能在双向状态通道和闪电网络中特别有用</p><p>交易级相对时间锁定是作为对每个交易输入中设置的交易字段nSequence的值的共识规则实现的。脚本级相对时间锁定使用CHECKSEQUENCEVERIFY(CSV)操作码实现。</p><p><a id="markdown-6421-nsequence" name="6421-nsequence"></a></p><h4 id="6-4-2-1-nSequence"><a href="#6-4-2-1-nSequence" class="headerlink" title="6.4.2.1. nSequence"></a>6.4.2.1. nSequence</h4><p>在每个输入中加多一个nSequence字段来设置此类相对时间锁. ，如果输入的交易的序列值小于2^32    (0xFFFFFFFF)，就表示尚未“确定”的交易。</p><p>nSequence的原始含义从未被正确实现，并且在不利用时间锁定的交易中nSequence的值通常设置为$2^{32}$.  对于具有nLocktime或CHECKLOCKTIMEVERIFY的交易，nSequence值必须设置为小于$2^{32}$， 以使时间锁定器有效。通常设置为$2^{32}-1\<br>\ (0xFFFFFFFE)$。</p><p>一笔输入交易，当输入脚本中的nSequence值小于2^31时，就是相对时间锁定的输入交易。</p><p>交易可以包括时间锁定输入(nSequence    <2^31)和没有相对时间锁定(nsequence>    =2^31)的输入。    nSequence值以块或秒为单位, 类型标志用于区分计数块和计数时间(以秒为单位)的值。类型标志设置在第23个最低有效位(即值1    &lt;&lt;    22)。如果设置了类型标志，则nSequence值将被解释为512秒的倍数。如果未设置类型标志，则nSequence值被解释为块数。</2^31)和没有相对时间锁定(nsequence></p><p>当将nSequence解释为相对时间锁定时，只考虑16个最低有效位。一旦评估了标志(位32和23)，nSequence值通常用16位掩码(例如nSequence＆0x0000FFFF)“屏蔽”。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-23.png" alt="introduction-to-bitcoin-23"></p><p><a id="markdown-6422-checksequenceverifycsv" name="6422-checksequenceverifycsv"></a></p><h4 id="6-4-2-2-CHECKSEQUENCEVERIFY-CSV"><a href="#6-4-2-2-CHECKSEQUENCEVERIFY-CSV" class="headerlink" title="6.4.2.2. CHECKSEQUENCEVERIFY(CSV)"></a>6.4.2.2. CHECKSEQUENCEVERIFY(CSV)</h4><p>脚本操作码, 在UTXO的赎回脚本中评估时，CSV操作码仅允许在输入nSequence值大于或等于CSV参数的交易中进行消耗。<br><a id="markdown-643-median-time-past" name="643-median-time-past"></a></p><h3 id="6-4-3-Median-Time-Past"><a href="#6-4-3-Median-Time-Past" class="headerlink" title="6.4.3. Median-Time-Past"></a>6.4.3. Median-Time-Past</h3><p>在比特币中, 墙上时间(wall    time)和共识时间之间存在微妙但非常显著的差异。比特币是一个分散的网络，这意味着每个参与者都有自己的时间观。网络上的事件不会随时随地发生。网络延迟必须考虑到每个节点的角度。最终，所有内容都被同步，以创建一个共同的分类帐。</p><p>通过取最后11个块的时间戳并计算其中位数作为“中位时间过去”的值,作为共识时间，并被用于所有的时间计算.  通过这个方法，没有一个矿工可以利用时间戳从具有尚未成熟的时间段的交易中获取非法矿工费。</p><p><a id="markdown-65-条件子句conditional-clauses" name="65-条件子句conditional-clauses"></a></p><h2 id="6-5-条件子句-Conditional-Clauses"><a href="#6-5-条件子句-Conditional-Clauses" class="headerlink" title="6.5. 条件子句(Conditional Clauses)"></a>6.5. 条件子句(Conditional Clauses)</h2><p>可以控制流量.<br>由于比特币脚本语言是一种堆栈语言, 则其条件控制如下<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">truecondition</span><br><span class="line">IF</span><br><span class="line">truetruecodetorunwhencondition<span class="keyword">is</span>true</span><br><span class="line">ELSE</span><br><span class="line">truetruecodetorunwhencondition<span class="keyword">is</span>false</span><br><span class="line">ENDIF</span><br><span class="line">codetorun<span class="keyword">in</span>eithercase</span><br></pre></td></tr></table></figure></p><p>另外也有带有VERIFY操作码的条件子句</p><p>任何以VERIFY结尾的操作码。    VERIFY后缀表示如果评估的条件不为TRUE，脚本的执行将立即终止，并且该交易被视为无效。VERIFY后缀充当保护子句，只有在满足前提条件的情况下才会继续。<br>如<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HASH160<span class="tag">&lt;<span class="name">expected</span><span class="attr">hash</span>&gt;</span>EQUALVERIFY<span class="tag">&lt;<span class="name">Bob's</span><span class="attr">Pubkey</span>&gt;</span>CHECKSIG</span><br></pre></td></tr></table></figure></p><p>等同于<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">HASH160&lt;expectedhash&gt;EQUAL</span><br><span class="line">IF</span><br><span class="line">truetruetrue&lt;Bo<span class="string">b'sPubkey&gt;CHECKSIG</span></span><br><span class="line"><span class="string">ENDIF</span></span><br></pre></td></tr></table></figure></p><p>使用IF的脚本与使用具有VERIFY后缀的操作码相同;    他们都作为保护条款。    然而，VERIFY的构造更有效率，使用较少的操作码。</p><p>诸如EQUAL之类的操作码会将结果(TRUE    /    FALSE)推送到堆栈上，留下它用于后续操作码的评估。    相比之下，操作码EQUALVERIFY后缀不会在堆栈上留下任何东西。    在VERIFY中结束的操作码不会将结果留在堆栈上。</p><p>在多重签名, 赎回脚本中使用<br>赎回脚本<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">IF</span><br><span class="line">true&lt;Alice<span class="string">'sPubkey&gt;CHECKSIG</span></span><br><span class="line"><span class="string">ELSE</span></span><br><span class="line"><span class="string">&lt;Bob'</span>sPubkey&gt;CHECKSIG</span><br><span class="line">ENDIF</span><br></pre></td></tr></table></figure></p><p>而条件应该在解锁脚本中,<br>Alice用解锁脚本<code>&lt;Alice&#39;s    Sig&gt;    1</code>,<br>Bob 用解锁脚本<code>&lt;Bob&#39;s    Sig&gt;    0</code></p><p>一个复杂的例子</p><blockquote><p>多重签名的计划的参与者是Mohammed，他的两个合作伙伴Saeed和Zaira，以及他们的公司律师Abdul。三个合作伙伴根据多数规则作出决定，因此三者中的两个必须同意。然而，如果他们的钥匙有问题，他们希望他们的律师能够用三个合作伙伴签名之一收回资金。最后，如果所有的合作伙伴一段时间都不可用或无行为能力，他们希望律师能够直接管理该帐户。</p></blockquote><p>具有时间锁定(Timelock)变量的多重签名<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">IF</span><br><span class="line">truetrueIF</span><br><span class="line">truetruetruetrue<span class="number">2</span></span><br><span class="line">truetrueELSE</span><br><span class="line">truetruetruetrue&lt;<span class="number">30</span>days&gt;CHECKSEQUENCEVERIFYDROP</span><br><span class="line">truetruetruetrue&lt;AbdultheLawye<span class="string">r'sPubkey&gt;CHECKSIGVERIFY</span></span><br><span class="line"><span class="string">1</span></span><br><span class="line"><span class="string">ENDIF</span></span><br><span class="line"><span class="string">&lt;Mohammed'</span>sPubkey&gt;&lt;Saeed<span class="string">'sPubkey&gt;&lt;Zaira'</span>sPubkey&gt;<span class="number">3</span>CHECKMULTISIG</span><br><span class="line">ELSE</span><br><span class="line">truetrue&lt;<span class="number">90</span>days&gt;CHECKSEQUENCEVERIFYDROP</span><br><span class="line">truetrue&lt;AbdultheLawye<span class="string">r'sPubkey&gt;CHECKSIG</span></span><br><span class="line"><span class="string">ENDIF</span></span><br></pre></td></tr></table></figure></p><p>第二个执行路径只能在UTXO创建30天后才能使用。    那时候，它需要签署Abdul(律师)和三个合作伙伴之一(三分之一)。<br>解锁第二个执行路径的脚本(Lawyer    +    1-of-3)</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0</span>&lt;Saeed<span class="symbol">'s</span>Sig&gt;&lt;Abdul<span class="symbol">'s</span>Sig&gt;<span class="literal">FALSE</span><span class="literal">TRUE</span></span><br></pre></td></tr></table></figure><p>** </p><ul><li>此解锁脚本开头的0是因为CHECKMULTISIG中的错误从堆栈中弹出一个额外的值</li><li>先FALSE后TRUE, 分析栈的顺序</li></ul><p>**</p><p><a id="markdown-7-p2p-网络架构" name="7-p2p-网络架构"></a></p><h1 id="7-P2P-网络架构"><a href="#7-P2P-网络架构" class="headerlink" title="7. P2P 网络架构"></a>7. P2P 网络架构</h1><p>P2P是指位于同一网络中的每台计算机都彼此对等，各个节点共同提供网络服务，不存在任何“特殊”节点。每个网络节点以“扁平(flat)”的拓扑结构相互连通。    在P2P网络中不存在任何服务端(server)、中央化的服务、以及层级结构。P2P网络的节点之间交互运作、协同处理：每个节点在对外提供服务的同时也使用网络中其他节点所提供的服务。P2P网络也因此具有可靠性、去中心化，以    及开放性。</p><p>尽管比特币P2P网络中的各个节点相互对等，但是根据所提供的功能不同，各节点可能具有不同的角色。每个比特币节点都是路由、区块链数据库、挖矿、钱包服务的功能集合。</p><p>全节点含有 区块链的完整拷贝, 而轻量级结点只有一部分, 交易验证的方式是 <code>简单支付验证(SPV)</code></p><p>常见结点类型</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-24.png" alt="introduction-to-bitcoin-24"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-25.png" alt="introduction-to-bitcoin-25"></p><p><a id="markdown-71-扩展比特币网络" name="71-扩展比特币网络"></a></p><h2 id="7-1-扩展比特币网络"><a href="#7-1-扩展比特币网络" class="headerlink" title="7.1. 扩展比特币网络"></a>7.1. 扩展比特币网络</h2><p>运行比特币P2P协议的比特币主网络由大约5000-8000个运行着不同版本比特币核心客户端(Bitcoin    Core)的监听节    点、以及几百个运行着各类比特币P2P协议的应用(例如BitcoinClassic,    Bitcoin    Unlimited,    BitcoinJ,    Libbitcoin,    btcd,    and    bcoin等)的节点组成。比特币P2P网络中的一小部分节点也是挖矿节点，它们竞争挖矿、验证交易、并创建新的区块。许多连接到比特币网络的大型公司运行    着基于Bitcoin核心客户端的全节点客户端，它们具有区块链的完整拷贝及网络节点，但不具备挖矿及钱包功能。这些节点是网络中的边缘路由器(edgerouters)，通过它们可以搭建其他服务，例如交易所、钱包、区块浏览器、商家支付处理(merchant    payment    processing)等<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-26.png" alt="introduction-to-bitcoin-26"></p><p><a id="markdown-72-网络发现" name="72-网络发现"></a></p><h2 id="7-2-网络发现"><a href="#7-2-网络发现" class="headerlink" title="7.2. 网络发现"></a>7.2. 网络发现</h2><p>当新的网络节点启动后，为了能够参与协同运作，它必须发现网络中的其他比特币节点。新的网络节点必须发现至少一个网络中存在的节点并建立连接。由于比特币网络的拓扑结构并不基于节点间的地理位置，因此各个节点之间的地理信息完全无关。在新节点连接时，可以随机选择网络中存在的比特币节点与之相连。</p><p>节点通常采用TCP协议、使用8333端口. 。在建立连接时，该节点会通过发送一条包含基本认证内容的version消息开始“握手”通信过    程. 包括如下内容:</p><ul><li>nVersion: 比特币P2P协议所采用的版本</li><li>nLocalServices: 一组该节点支持的本地服务列表，当前仅支持NODE_NETWORK▷</li><li>nTime: 当前时间</li><li>addrYou: 当前节点可见的远程节点的IP地址</li><li>addrMe: 本地节点所发现的本机IP地址</li><li>subver: 指示当前节点运行的软件类型的子版本号</li><li>BaseHeight: 当前节点区块链的区块高度    </li></ul><p>接收版本消息的本地对等体将检查远程对等体报告的nVersion，并确定远端对等体是否兼容。    如果远程对等体兼容，则本地对等体将确认版本消息，并通过发送一个verack建立连接。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-27.png" alt="introduction-to-bitcoin-27"></p><p><strong>当建立一个或多个连接后</strong>，新节点将一条包含自身IP地址的addr消息发送给其相邻节点。相邻节点再将此条addr消息依    次转发给它们各自的相邻节点，从而保证新节点信息被多个节点所接收、保证连接更稳定。然后，新接入的节点可以向    它的相邻节点发送getaddr消息，要求它们返回其已知对等节点的IP地址列表。</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-28.png" alt="introduction-to-bitcoin-28"></p><p>节点必须连接到若干不同的对等节点才能在比特币网络中建立通向比特币网络的种类各异的路径(path)。由于节点可以随时加入和离开，通讯路径是不可靠的。因此，节点必须持续进行两项工作：在失去已有连接时发现新节点，并在其他节点启动时为其提供帮助。节点启动时只需要一个连接，因为第一个节点可以将它引荐给它的对等节点，而这些节点又会进一步提供引荐。一个节点，如果连接到大量的其他对等节点，这既没必要，也是对网络资源的浪费。在启动完成    后，节点会记住它最近成功连接的对等节点；因此，当重新启动后它可以迅速与先前的对等节点网络重新建立连接。如果先前的网络的对等节点对连接请求无应答，该节点可以使用种子节点进行重启动。</p><p>如果已建立的连接没有数据通信，所在的节点会定期发送信息以维持连接。如果节点持续某个连接长达90分钟没有任何通信，它会被认为已经从网络中断开，网络将开始查找一个新的对等节点</p><p><a id="markdown-73-同步区块链" name="73-同步区块链"></a></p><h2 id="7-3-同步区块链"><a href="#7-3-同步区块链" class="headerlink" title="7.3. 同步区块链"></a>7.3. 同步区块链</h2><p>对于全节点, 需要同步备份整个区块链..<br>此过程从发送version消息开始，这是因为该消息中含有的BestHeight字段标示了一个节点当前的区块链高度(区块数量)。对等节点们会<strong>交换</strong>一个getblocks消息，其中包含他们本地区块链的顶端区块哈希值(指纹)。如果某个对等节点识别出它接收到的哈希值并不属于顶端区块，而是属于一个非顶端区块的旧区块，那么它就能推断出：其自身的本地区块链比其他对等节点的区块链更长。</p><p>拥有更长区块链的对等节点,识别出第    一批可供分享的500个区块，通过使用inv(inventory)消息把这些区块的哈希值传播出去。缺少这些区块的节点便可以    通过各自发送的getdata消息来请求得到全区块信息，用包含在inv消息中的哈希值来确认是否为正确的被请求的区块，    从而读取这些缺失的区块。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-29.png" alt="introduction-to-bitcoin-29"></p><p><a id="markdown-74-简单支付验证spv" name="74-简单支付验证spv"></a></p><h2 id="7-4-简单支付验证-SPV"><a href="#7-4-简单支付验证-SPV" class="headerlink" title="7.4. 简单支付验证(SPV)"></a>7.4. 简单支付验证(SPV)</h2><p>SPV节点只需下载区块头，而不用下载包含在每个区块中的交易信息。由此产生的不含交易信息的区块链，大小只有完整区块链的1/1000。SPV节点不能构建所有可用于消费的UTXO的全貌. SPV节点验证交易时依赖对等节点“按需”提供区块链相关部分的局部视图。</p><p>如要检查第300000号区块的某个交易, SPV节点会在该交易信息和它所在区块之间用merkle路径建立一条链接。然后SPV节点一直等待，直到序号从300,001到300,006的六个区块堆叠在该交易所在的区块之上，并通过确立交易的深度是在第300,006区块~第300,001区块之下来验证交易的有效性。</p><p>SPV节点可以证实某个交易的存在性，但它不能验证某个交易(譬如同一个UTXO的双重支付)不存在，这是因为SPV节点没有一份关于所有交易的记录。这个漏洞会被针对SPV节点的拒绝服务攻击或双重支付型攻击所利用。为了防御这些攻击，SPV节点需要随机连接到多个节点，以增加与至少一个可靠节点相连接的概率。这种随机连接的需求意味着SPV节    点也容易受到网络分区攻击或Sybil攻击。在后者情况中，SPV节点被连接到虚假节点或虚假网络中，没有通向可靠节点或真正的比特币网络的连接。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-30.png" alt="introduction-to-bitcoin-30"></p><p><a id="markdown-75-bloom-filter" name="75-bloom-filter"></a></p><h2 id="7-5-Bloom-filter"><a href="#7-5-Bloom-filter" class="headerlink" title="7.5. Bloom filter"></a>7.5. Bloom filter</h2><p>SPV节点对特定数据的请求可能无意中透露了钱包里的地址信息。例如，监控网络的第三方可以跟踪某个SPV节点上的钱包所请求的全部交易信息，并且利用这些交易信息把比特币地址和钱包的用户关联起来，从而损害了用户的隐私。</p><p>Bloom过滤器通过一个采用概率而不是固定模式的过滤机制，允许SPV节点只接收<strong>交易信息的子集</strong>，同时不会精确泄露哪些是它们感兴趣的地址。</p><p>Bloom过滤器可以让SPV节点指定交易的搜索模式，该搜索模式可以基于准确性或私密性的考虑被调节。如果过滤器只包含简单的关键词，更多相应的交易会被搜索出来，在包含若干无关交易的同时有着更高的私密性。</p><p>构成:<br>一个可变长(N)的 二进制数组, 数组初始值为0, . 一组数量可变(M)的哈希函数,  哈希函数输出为 1—-N, 对应数组,且为确定性函数.</p><p>算法如下<br>记数组 arr[N] , M个hash函数 $hs=\{h_1,h_2,\ldots,h_M\}$<br>关键字 $keys = \{k_1,k_2,\ldots,k_i\}$</p><p>过滤器记录关键字过程<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">arr[N]=&#123;<span class="number">0</span>&#125;  <span class="comment"># initialization</span></span><br><span class="line"><span class="keyword">for</span> key <span class="keyword">in</span> keys:</span><br><span class="line">    <span class="keyword">for</span> hash  <span class="keyword">in</span>   hs:</span><br><span class="line">        arr[hash(key)] = <span class="number">1</span></span><br></pre></td></tr></table></figure></p><p>判断一个关键字是否被过滤器记录: 将关键字分别代入各hash 函数 计算对比 arr 对应的值, 如果有0, 则没有被记录, 如果全为1, 则 <strong>可能</strong> 被记录.(基于概率)</p><p><a id="markdown-76-spv-节点如何使用-bloom-filter" name="76-spv-节点如何使用-bloom-filter"></a></p><h2 id="7-6-SPV-节点如何使用-Bloom-filter"><a href="#7-6-SPV-节点如何使用-Bloom-filter" class="headerlink" title="7.6. SPV 节点如何使用 Bloom filter"></a>7.6. SPV 节点如何使用 Bloom filter</h2><p>数组置0, 然后SPV节点将列出所有感兴趣的地址，密钥和散列，它将通过从其钱包控制的任何UTXO中提取公钥哈希和脚本哈希和交易ID来实现。    SPV节点然后将其中的每一个添加到Bloom过滤器，以便如果这些模式存在于交易中，则Bloom过滤器将“匹配”，而不会自动显示模式。</p><p>然后，SPV节点将向对等体发送一个过滤器加载消息，其中包含在连接上使用的bloom过滤器。在对等体上，针对每个传入交易检查Bloom过滤器。完整节点根据bloom过滤器检查交易的几个部分，寻找匹配，</p><p>只有与过滤器匹配的交易才会发送到节点。响应于来自节点的getdata消息，对等体将发送一个merkleblock消息，该消息仅包含与过滤器匹配的块和每个匹配交易的merkle路径。然后，对等体还将发送包含由过滤器匹配的交易的tx消息。</p><p><a id="markdown-77-加密和认证连接" name="77-加密和认证连接"></a></p><h2 id="7-7-加密和认证连接"><a href="#7-7-加密和认证连接" class="headerlink" title="7.7. 加密和认证连接"></a>7.7. 加密和认证连接</h2><p>Tor传输和P2P认证和加密<br><a id="markdown-78-交易池" name="78-交易池"></a></p><h2 id="7-8-交易池"><a href="#7-8-交易池" class="headerlink" title="7.8. 交易池"></a>7.8. 交易池</h2><p>比特币网络中几乎每个节点都会维护一份未确认交易的临时列表，被称为内存池或交易池</p><p>有些节点的实现还维护一个单独的孤立交易池。如果一个交易的输入与某未知的交易有关，如与缺失的父交易相关，该    孤立交易就会被暂时储存在孤立交易池中直到父交易的信息到达。当一个交易被添加到交易池中，会同时检查孤立交易池，看是否有某个孤立交易引用了此交易的输出(子交易)。</p><p>交易池和孤立交易池(如有实施)都是存储在本地内存中，并不是存储在永久性存储设备(如硬盘)里。</p><p><a id="markdown-8-区块链" name="8-区块链"></a></p><h1 id="8-区块链"><a href="#8-区块链" class="headerlink" title="8. 区块链"></a>8. 区块链</h1><p>平均每个区块至少包含超过500个交易<br>区块头由三组区块元数据组成:</p><ul><li>引用父区块哈希值的数据，用于与前一区块相连接。</li><li>难度、时间戳和nonce,与挖矿竞争相关</li><li>merkle树根</li></ul><p>因为<code>创世区块</code>被编入到比特币客户端软件里，所以每一个节点都始于至少包含一个区块的区块链，这能确保创世区块不会被改变。每一个节点都“知道”创世区块的哈希值、结构、被创建的时间和里面的一个交易。因此，每个节点都把该区块作为区块链的首区块，从而构建了一个安全的、可信的区块链。</p><p>在比特币网络中，<code>Merkle树</code>被用来归纳一个区块中的所有交易,同时生成整个交易集合的数字指纹，且提供了一种校验区块是否存在某交易的高效途径。。如果仅有奇数个交易需要归纳，那最后的交易就会被复制一份以构成偶数个叶子节点，</p><p>不需要下载整个区块而通过Merkle路径去验证交易的存在,又被称作简单支付验证.</p><p>一个SPV节点想知道它钱包中某个比特币地址即将到达的支付。该节点会在节点间的通信链接上建立起bloom过滤器，限制只接受含有目标比特币地址的交易。当对等体探测到某交易符合bloom过滤器，它将以Merkleblock消息的形式发送该区块。Merkleblock消息包含区块头和一条连接目标交易与Merkle根的Merkle路径。SPV节点能够使用该路径找到与该交易相关的区块，进而验证对应区块中该交易的有无。SPV节点同时也使用区块头去关联区块和区块链中的其余区块。这两种关联，交易与区块、区块和区块链，就可以证明交易存在于区块链。简而言之，SPV节点会收到少于1KB的有关区块头和Merkle路径的数据，其数据量比一个完整的区块(目前大约有1MB)少了一千多倍。\</p><p><strong>比特币的测试区块链</strong></p><ul><li>testnet—比特币的试验场: 实际上它和主网只有两个区别：testnet币是毫无价值的，挖掘难度足够低，任何人都可以相对容易地使用testnet币)。<br>任何打算在比特币主干网上用于生产的软件开发都应该首先在testnet上用测试币进行测试。免受由于软件错误而导致的金钱损失，也可以保护网络免受由于软件错误导致的意外攻击</li><li>regtest—本地区块链: regtest 代表回归测试, 是比特币的一中核心功能,可以创建本地区块链以进行测试.</li></ul><p>开发过程:首先在regtest上部署每个变更，然后在testnet上进行测试，最后实现生产,部署到比特币网络上。</p><p><a id="markdown-9-挖矿与共识" name="9-挖矿与共识"></a></p><h1 id="9-挖矿与共识"><a href="#9-挖矿与共识" class="headerlink" title="9. 挖矿与共识"></a>9. 挖矿与共识</h1><p>挖矿巩固了去中心化的清算交易机制，通过这种机制，交易得到验证和清算,实现去中心化的安全机制，是P2P数字货币的基础。</p><p>矿工们在挖矿过程中会得到两种类型的奖励：创建新区块的新币奖励，以及区块中所含交易的交易费。矿工们争相完成一种基于加密哈希算法的数学难题，这些难题的答案包括在新区块中，作为矿工的计算工作量的证明，被称为”“工作量证明”。该<strong>算法的竞争机制</strong>以及<strong>获胜者有权在区块链上进行交易记录的机制</strong>，这二者是比特币安全的基石。</p><p>比特币的去中心化共识由所有网络节点的4种独立过程相互作用而产生：</p><ul><li>每个全节点依据综合标准对每个交易进行独立验证</li><li>通过完成工作量证明算法的验算，挖矿节点将交易记录独立打包进新区块</li><li>每个节点独立的对新区块进行校验并组装进区块链</li><li>每个节点对区块链进行独立选择，在工作量证明机制下选择累计工作量最大的区块链。</li></ul><p>它们之间如何相互作用并达成全网的自发共识，从而使任意节点组合出    它自己的权威、可信、公开的总帐副本。</p><p><a id="markdown-91-交易验证" name="91-交易验证"></a></p><h2 id="9-1-交易验证"><a href="#9-1-交易验证" class="headerlink" title="9.1. 交易验证"></a>9.1. 交易验证</h2><p>在交易传递到临近的节点前，每一个收到交易的比特币节点将会首先验证该交易，这将确保只有有效的交易才会    在网络中传播，而无效的交易将会在第一个节点处被废弃。</p><ul><li>交易的语法和数据结构必须正确。</li><li>输入与输出列表都不能为空。</li><li>交易的字节大小是小于    MAX_BLOCK_SIZE    的。</li><li>每一个输出值，以及总量，必须在规定值的范围内    (小于2,100万个币，大于0)。</li><li>没有哈希等于0，N等于-1的输入(coinbase交易不应当被传递)。</li><li>nLockTime是小于或等于 INT_MAX 的。或者nLocktime    and    nSequence的值满足MedianTimePast</li><li>交易的字节大小是大于或等于100的。</li><li>交易中的签名数量(SIGOPS)应小于签名操作数量上限。</li><li>解锁脚本(    scriptSig    )只能够将数字压入栈中，并且锁定脚本(    scriptPubkey    )必须要符合isStandard的格式    (该格式将会拒绝非标准交易)。</li><li>池中或位于主分支区块中的一个匹配交易必须是存在的。</li><li>对于每一个输入，引用的输出是必须存在的，并且没有被花费。</li><li>对于每一个输入，如果引用的输出存在于池中任何别的交易中，该交易将被拒绝。</li><li>对于每一个输入，在主分支和交易池中寻找引用的输出交易。如果输出交易缺少任何一个输入，该交易将成为一个孤    立的交易。如果与其匹配的交易还没有出现在池中，那么将被加入到孤立交易池中。</li><li>对于每一个输入，如果引用的输出交易是一个coinbase输出，该输入必须至少获得COINBASE_MATURITY(100)个确认。</li><li>使用引用的输出交易获得输入值，并检查每一个输入值和总值是否在规定值的范围内    (小于2100万个币，大于0)。</li><li>如果输入值的总和小于输出值的总和，交易将被中止。</li><li>如果交易费用太低以至于无法进入一个空的区块，交易将被拒绝。</li><li>每一个输入的解锁脚本必须依据相应输出的锁定脚本来验证。</li></ul><p>验证交易后，比特币节点会将这些交易添加到自己的内存池中。内存池也称作交易池，用来暂存尚未被加入到区块的交    易记录。</p><p>例如:<br>Jing节点的区块链已经收集到了区块277,314，并继续监听着网络上的交易，在尝试挖掘新区块的同时，也监    听着由其他节点发现的区块。这时他从比特币网络收到了区块277,315, 标志着终结了产出区块277,315竞赛，与此同时也是产出区块277,316竞赛的开始。</p><p>在上一个10分钟内，当Jing的节点正在寻找区块277,315的解的同时，他也在收集交易记录为下一个区块做准备。目前    它已经收到了几百笔交易记录，并将它们放进了内存池。直到接收并验证区块277,315后，Jing的节点会检查内存池中    的全部交易，<strong>并移除已经在区块277,315中出现过的交易记录，确保任何留在内存池中的交易都是未确认的</strong>，等待被记    录到新区块中。</p><p>Jing的节点立刻构建一个新的空区块，做为区块277,316的候选区块。称作候选区块是因为它还没有包含有效的工作量证明(计算出合适的 nonce)，不是一个有效的区块，而只有在矿工成功找到一个工作量证明解之后，这个区块才生效。现在，Jing的节点从内存池中整合到了全部的交易，新的候选区块包含有418笔交易，总的矿工费为0.09094925个比特币。</p><p><a id="markdown-92-coinbase交易创币交易" name="92-coinbase交易创币交易"></a></p><h2 id="9-2-coinbase交易-创币交易"><a href="#9-2-coinbase交易-创币交易" class="headerlink" title="9.2. coinbase交易(创币交易)"></a>9.2. coinbase交易(创币交易)</h2><p>每个区块中的第一笔交易是笔特殊交易，称为创币交易或者coinbase交易<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-31.png" alt="introduction-to-bitcoin-31"></p><p>与常规交易不同，创币交易没有输入，不消耗UTXO。它只包含一个被称作coinbase的输入，仅仅用来创建新的比特    币。创币交易有一个输出，支付到这个矿工的比特币地址。</p><p>为了构造创币交易，矿工节点需要计算如下</p><ul><li>矿工费的总额: 输入总额减去输出总额</li><li>新区块奖励额: 是基于区块高度的，以每个区块50个比特币为开    始，每产生210,000个区块(10分钟一个区块,大约4年)减半一次。2016年7月为 12.5 bitcoin</li></ul><p><a id="markdown-93-构造区块头" name="93-构造区块头"></a></p><h2 id="9-3-构造区块头"><a href="#9-3-构造区块头" class="headerlink" title="9.3. 构造区块头"></a>9.3. 构造区块头</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-bitcoin-32.png" alt="introduction-to-bitcoin-32"></p><ul><li>merkle root: 将全部的交易组成一个merkle树。创币交易作为区块中的首个交易，后将余下的    418笔交易添至其后，这样区块中的交易一共有419笔。</li></ul><ul><li>Target<strong>(难度目标值)</strong>: <code>定义了所需满足的工作量证明的难度</code>。难度在区块中以“尾数-指数”的格式，编码并存储，这种格式称作target-bits(难度位)。首字节表示指数(exponent)，后面的3字节表示尾数(系数)(coefficient)。则$\text{difficulty} = cofficient<em> 2^{8</em>{(exponent-3)} }$<br>难度是可以调整的, 以保证不论计算力如何, 总要大约每十分钟产生一个区块. 因此, 新公式为 $\text{ newDifficulty} = \text{diffculty} * \frac{Time(last\ 2016\ blocks)}{20160\ min}$</li><li>nonce: 初始值为0</li></ul><p>构造区块 nonce 如下<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">nonce = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">    <span class="keyword">if</span> hash(blockHead)&lt;Target:<span class="keyword">break</span>   <span class="comment"># nonce in  blockHead</span></span><br><span class="line">    <span class="keyword">else</span>: CHANGE NONCE  <span class="comment"># eg  nonce+=1</span></span><br></pre></td></tr></table></figure></p><p>构造好之后, 挖矿节点立刻将这个区块发给它的所有相邻节点。这些节点在接收并验证这个新区块后，也会继续传播此区块。当这个新区块在网络中扩散时，每个节点都会将它加入自己的区块链副本中。其他挖矿结点就放弃之前对构建这个相    同高度区块的计算，并立即开始计算区块链中下一个区块的工作。</p><p><a id="markdown-94-校验新区块" name="94-校验新区块"></a></p><h2 id="9-4-校验新区块"><a href="#9-4-校验新区块" class="headerlink" title="9.4. 校验新区块"></a>9.4. 校验新区块</h2><p>前面清单列出了一些, 也可以通过 客户端的 CheckBlock, CheckBlockHead,查看</p><p>为什么矿工不为他们自己记录一笔交易去获得数以千计的比特币？这是因为每一个节点根据相同的规则对区块进行校验。一个无效的coinbase交易将使整个区块无效，这将导致该区块被拒    绝，因此，该交易就不会成为总账的一部分。矿工们必须构建一个完美的区块，基于所有节点共享的规则，并且根据正    确工作量证明的解决方案进行挖矿，他们要花费大量的电力挖矿才能做到这一点。如果他们作弊，所有的电力和努力都    会浪费。这就是为什么独立校验是去中心化共识的重要组成部分。</p><p><a id="markdown-95-构建区块" name="95-构建区块"></a></p><h2 id="9-5-构建区块"><a href="#9-5-构建区块" class="headerlink" title="9.5. 构建区块"></a>9.5. 构建区块</h2><p>构建了一个候选区块，然后求解工作量证明算法以使这个区块有效。</p><p>每次改变 nonce, 尝试产生一个随机的结果，但是任何可能的结果的概率可以预先计算。因此，指定特定难度(Target)的结果构成了具体的工作量证明。</p><p>验证nonce 哈希值只需要一次计算，而我们找到它却花了很多次。知道目标值后，任何人都可以用统计学来估算其难度，因此就能知道找到这个nonce需要多少工作。</p><p>按当前比特币系统的难度，矿工得试$10^15$次才能找到一个合适的nonce使区块头信息哈希值足够小。</p><p><a id="markdown-96-区块链的组装与选择" name="96-区块链的组装与选择"></a></p><h2 id="9-6-区块链的组装与选择"><a href="#9-6-区块链的组装与选择" class="headerlink" title="9.6. 区块链的组装与选择"></a>9.6. 区块链的组装与选择</h2><ul><li>连接到主链上的</li><li>从主链上产生分支的(备用链)，</li><li>在已知链中没有找到已知父区块的。</li></ul><p>在验证过程中，一旦发现有不符合标准的地方，验证就会失败，这样区块会被节点拒绝，所以也不    会加入到任何一条链中。</p><p>任何时候，主链都是累计了最多难度的区块链。在一般情况下，主链也是包含最多区块的那个链，除非有两个等长的链    并且其中一个有更多的工作量证明。主链也会有一些分支，这些分支中的区块与主链上的区块互为“兄弟”区块。<strong>这些区    块是有效的</strong>，但不是主链的一部分。</p><p>新区块所延长的区块链并不是主链,节点将新的区块添加到备用链，同时比较备用链与主链的难度。如果备用链比主链积累了更多的难度，节点将收敛于备用链，意味    着节点将选择备用链作为其新的主链，而之前那个老的主链则成为了备用链。</p><p>如果节点收到了一个有效的区块，而在现有的区块链中却未找到它的父区块，那么这个区块被认为是“孤块”。孤块会被    保存在孤块池中，直到它们的父区块被节点收到。</p><p>比特币将区块间隔设计为10分钟，是在更快速的交易确认和更低的分叉概率间作出的妥协。更短的区块产生间隔会让交    易清算更快地完成，也会导致更加频繁地区块链分叉。</p><p>难度增长后, nonce 值不够, 可以延后时间戳来解决, 但是如果延后太久, 可能导致区块无效,  更好的解决方案是利用 coinbase 这笔交易中的空间(coinbase 脚本可以存储2-100bytes 数据),  而且这笔交易会影响 merkle 根的变化.</p><p><a id="markdown-97-矿池" name="97-矿池"></a></p><h2 id="9-7-矿池"><a href="#9-7-矿池" class="headerlink" title="9.7. 矿池"></a>9.7. 矿池</h2><p><a id="markdown-971-矿工加入" name="971-矿工加入"></a></p><h3 id="9-7-1-矿工加入"><a href="#9-7-1-矿工加入" class="headerlink" title="9.7.1. 矿工加入"></a>9.7.1. 矿工加入</h3><p>个人矿工在建立矿池账号后，设置他们的矿机连接到矿池服务器。他们的挖矿设备在挖矿时保持和矿池服务器的连接，和其他矿工同步各自的工作。这样，矿池中的矿工分享挖矿任务，之后分享奖励。成功出块的奖励支付到矿池的比特币地址，而不是单个矿工的。一旦奖励达到一个特定的阈值，矿池服务器便会定期支    付奖励到矿工的比特币地址。<br><a id="markdown-972-托管矿池" name="972-托管矿池"></a></p><h3 id="9-7-2-托管矿池"><a href="#9-7-2-托管矿池" class="headerlink" title="9.7.2. 托管矿池"></a>9.7.2. 托管矿池</h3><p>大部分矿池是“托管的”，有一个公司或者个人经营一个矿池服务器。矿池服务器的所有者叫矿池管理员，同时他    从矿工的收入中收取一个百分比的费用。矿池服务器运行专业软件以及协调池中矿工们活动的矿池采矿协议。矿池服务器同时也连接到一个或更多比特币完全节点并直接访问一个块链数据库的完整副本。这使得矿池服务器可以代替矿池中的矿工验证区块和交易，缓解他们运行一个完整节点的负担.<br><a id="markdown-973-p2p矿池" name="973-p2p矿池"></a></p><h3 id="9-7-3-P2P矿池"><a href="#9-7-3-P2P矿池" class="headerlink" title="9.7.3. P2P矿池"></a>9.7.3. P2P矿池</h3><p>托管矿池存在管理人作弊的可能，管理人可以利用矿池进行双重支付或使区块无效,     此外，中    心化的矿池服务器代表着单点故障。如果因为拒绝服务攻击服务器挂了或者被减慢，池中矿工就不能采矿。</p><p>P2Pool是一个点对点的矿池，没有中心管理    人。P2Pool通过将矿池服务器的功能去中心化，实现一个并行的类似区块链的系统，名叫<code>份额链(share    chain)</code>。</p><p>一个份额链是一个难度低于比特币区块链的区块链系统。份额链允许池中矿工在一个去中心化的池中合作,采矿，并获得份额。份额链上的区块记录了贡献工作的矿工的份额，并且继承了之前份额区块上的份额记录。当一    个份额区块上还实现了比特币网络的难度目标时，它将被广播并包含到比特币的区块链上，并奖励所有已经在份额链区块中取得份额的池中矿工。</p><p><a id="markdown-98-共识攻击" name="98-共识攻击"></a></p><h2 id="9-8-共识攻击"><a href="#9-8-共识攻击" class="headerlink" title="9.8. 共识攻击"></a>9.8. 共识攻击</h2><p>比特币的共识机制的前提: 绝大多数的矿工，出于自己利益最大化的考虑，都会通过诚实地挖矿来维持整个比特币系统。</p><p>当一个或者一群拥有了整个系统中大量算力的矿工出现, 可以通过攻击比特币的共识机制来达到破坏比特币网络的安全性和可靠性的目的。</p><p>注意, ，共识攻击只能影响整个区块链未来的共识，即最多影响    过去10个块。而且随着时间的推移，整个比特币块链被篡改的可能性越来越低。</p><p>共识攻击也    不能从其他的钱包那里偷到比特币、不签名地支付比特币、重新分配比特币、改变过去的交易或者改变比特币持有纪    录。共识攻击能够造成的唯一影响是影响最近的区块(最多10个)并且通过拒绝服务来影响未来区块的生成。</p><p>区块链分叉/双重支付攻击指的是攻击者通过    不承认最近的某个交易，并在这个交易之前重构新的块，从而生成新的分叉，继而实现双重支付。双重支付只能在攻击者拥有的钱包所发生的交易上进行，因为只有钱包的拥有者才能生成一个合法的签名用    于双重支付交易。攻击者在自己的交易上进行双重支付攻击，如果可以通过使交易无效而实现对于不可逆转的购买行为不予付款，这种攻击就是有利可图的。</p><p>51%攻击并不是像它的命名里说的那样，攻击者需要至少51%的算力才能发起，实际上，即使其拥有不到51%的系统算力，依然可以尝试发起这种攻击。之所以命名为51％攻击，只是因为在攻击者的算力达到51%这个阈值的时候，其发起的攻击尝试几乎<strong>肯定</strong>会成功。</p><p><a id="markdown-99-分叉" name="99-分叉"></a></p><h2 id="9-9-分叉"><a href="#9-9-分叉" class="headerlink" title="9.9. 分叉"></a>9.9. 分叉</h2><p><a id="markdown-991-硬分叉" name="991-硬分叉"></a></p><h3 id="9-9-1-硬分叉"><a href="#9-9-1-硬分叉" class="headerlink" title="9.9.1. 硬分叉"></a>9.9.1. 硬分叉</h3><p>导致硬分叉：共识规则中的错误，以及对共识规则的故意修改。</p><p>对于硬分叉发生，必须是由于采取相互竞争的实施方案，并且规则需要由矿工，钱包和中间节点激活。相反，有许多比特币核心的替代实现方案，甚至还有软分叉，这些没有改变共识规则，阻止发生错误，可以在网络上共存并互操作，最终并未导致硬分叉。</p><p>可以将硬分叉子看成四个阶段：<code>软分叉，网络分叉，挖矿分叉和区块链分叉</code>。该过程开始于开发人员创建的客户端，这个客户端对共识规则进行了修改。当这种新版本的客户端部署在网络中时，一定百分比的矿工，钱包用户和中间节点可以采用并运行该版本客户端。得到的分叉将取决于新的共识规则是否适用于区块，交易或系统其他方面。如果新的共识规则与交易有关，那么当交易被挖掘成一个块时，根据新规则创建交易的钱包可能会产生出一个网络分叉，这就是一个硬分叉。如果新规则与区块有关，那么当一个块根据新规则被挖掘时，硬分叉进程将开始。</p><p>一些开发商反对任何形式的硬叉，认为它太冒险了。另一些人认为硬分叉机制是提升共识规则的重要工具，避免了“技术债务”，并与过去提供了一个干净的了断<br><a id="markdown-992-软分叉" name="992-软分叉"></a></p><h3 id="9-9-2-软分叉"><a href="#9-9-2-软分叉" class="headerlink" title="9.9.2. 软分叉"></a>9.9.2. 软分叉</h3><p>共识规则的改变也能够让未修改的客户端仍然按照先前的规则对待交易或者区块</p><p>软分叉级只能用于增加共识规则约束，而不是扩展它们。软叉可以通过多种方式实现,方法的共同点是<strong>不要求所有节点升级或强制非升级节点必须脱离共识。</strong><br>如</p><ul><li>重新定义 NOP 操作码<br>Bitcoin脚本有10个操作码保留供将来使用，NOP1到NOP10。    根据共识规则，这些操作码在脚本中的存在被解释为无效的运算符。软叉可以修改NOP代码的语义给它新的含义。</li><li>segwit<br>是一个交易结构的体系结构变化，它将解锁脚本(见证)从交易内部移动到外部数据结构(将其隔离)</li></ul><p>对软分叉的批评</p><ul><li>技术性债务<br>指由于过去的设计权衡而增加代码维护的未来成本。代码复杂性又增加了错误和安全漏洞的可能性。(因为软叉在技术上比硬叉升级更复杂)</li><li>验证放松<br>未经修改的客户端将交易视为有效，而不评估修改的共识规则。实际上，未经修改的客户端不会使用全面的协商一致的规则来验证，因为它们对新规则无视。这适用于基于NOP的升<br>级，以及其他软分叉升级。</li><li>不可逆转升级<br>因为软分叉产生额外的共识约束的交易，所以它们在实践中成为不可逆转的升级。如果软分叉升级在被激活后被回退，根据新规则创建的任何交易都可能导致旧规则下的资金损失。因此，评论家认为，由于错误而不得不被回退的失败的软分叉几乎肯定会导致资金的流失。</li></ul><p><a id="markdown-10-比特币安全" name="10-比特币安全"></a></p><h1 id="10-比特币安全"><a href="#10-比特币安全" class="headerlink" title="10. 比特币安全"></a>10. 比特币安全</h1><p><a id="markdown-101-安全准则" name="101-安全准则"></a></p><h2 id="10-1-安全准则"><a href="#10-1-安全准则" class="headerlink" title="10.1. 安全准则"></a>10.1. 安全准则</h2><p>比特币的核心准则是去中心化,将责任和控制权都移交给了用户。由于网络的安全性是基于工作量证明而非访问控制，比特币网络可以对所有人开放，也无需对比特币传输进行加密。</p><p>一笔比特币交易只授权向指定接收方发送一个指定数额，并且不能被修改或伪造。它不会透露任何个人信息，例如当事人的身份，也不能用于权限外的支付。因此，比特币的支付网络并不需要加密或防窃听保护</p><p><a id="markdown-102-比特币系统安全开发" name="102-比特币系统安全开发"></a></p><h2 id="10-2-比特币系统安全开发"><a href="#10-2-比特币系统安全开发" class="headerlink" title="10.2. 比特币系统安全开发"></a>10.2. 比特币系统安全开发</h2><p>比特币的安全性依赖于密钥的分散性控制，并且需要矿工们各自独立地进行交易验证。如果想利用好比特币的安全性，确保自己处于比特币的安全模型里。简而言之，不要将用户的密钥控制权拿走，不要接受非区块链交易信息。一个常见的错误是接受区块链离线交易，妄图减少交易费或加速交易处理速度。一个“区块链离线交易”系统将交易数据记录在一个内部的中心化账本上，然后偶尔将它们同步到比特币区块链中。这种做法，再一次，用专制和集中的方式取    代比特币的去中心化安全模型。当数据处于离线的区块链上的时候，保护不当的中心化账本里的资金可能会不知不觉被    伪造、被挪用、被消耗。</p><p>除非你是准备大力投资运营安全，叠加多层访问控制，或(像传统的银行那样)加强审计，否则将资金从比特币的去中心化安全场景中抽离出来这样的设计也仅仅是复制了一个脆弱不堪，深受账户盗窃威胁、贪污和挪用公款困扰的传统金融网络而已。要想充分利用比特币特有的去中心化安全模型，必须避免中心化架构的常见诱惑，因它最终将摧毁比特币的安全性。<br><a id="markdown-103-信任根root-of-trust" name="103-信任根root-of-trust"></a></p><h2 id="10-3-信任根-ROOT-OF-TRUST"><a href="#10-3-信任根-ROOT-OF-TRUST" class="headerlink" title="10.3. 信任根(ROOT-OF-TRUST)"></a>10.3. 信任根(ROOT-OF-TRUST)</h2><p>传统的安全体系的基础，它指的总体系统或应用程序中一个可信赖的安全核心。安全体系像一圈同心圆一样围绕着信任根源来进行开发，像层层包裹的洋葱一样，信任从内至外依次延伸。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文整理自&lt;a href=&quot;https://legacy.gitbook.com/book/wizardforcel/masterbitcoin2cn/details&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&amp;lt;&amp;lt;精通比特币&amp;gt;&amp;gt;&lt;/a&gt;&lt;br&gt;**
    
    </summary>
    
      <category term="区块链" scheme="https://mbinary.xyz/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="区块链" scheme="https://mbinary.xyz/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="数字货币" scheme="https://mbinary.xyz/tags/%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81/"/>
    
      <category term="比特币" scheme="https://mbinary.xyz/tags/%E6%AF%94%E7%89%B9%E5%B8%81/"/>
    
  </entry>
  
  <entry>
    <title>『区块链』简介</title>
    <link href="https://mbinary.xyz/introduction-to-blockchain.html"/>
    <id>https://mbinary.xyz/introduction-to-blockchain.html</id>
    <published>2018-07-23T22:00:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>以下内容摘录自 &lt;&lt;区块链原理,设计与应用&gt;&gt;<br><a id="more"></a></p><!-- TOC --><ul><li><a href="#1-分布式记账">1. 分布式记账</a><ul><li><a href="#11-原理">1.1. 原理</a></li><li><a href="#12-重要性">1.2. 重要性</a></li></ul></li><li><a href="#2-区块链的特点">2. 区块链的特点</a></li><li><a href="#3-区块链的定义">3. 区块链的定义</a></li><li><a href="#4-区块链的原理">4. 区块链的原理</a><ul><li><a href="#41-例如比特币">4.1. 例如比特币</a></li></ul></li><li><a href="#5-区块链的演化">5. 区块链的演化</a></li><li><a href="#6-关键问题与挑战">6. 关键问题与挑战</a><ul><li><a href="#61-抗抵赖与隐私保护">6.1. 抗抵赖与隐私保护</a></li><li><a href="#62-分布式共识">6.2. 分布式共识</a></li><li><a href="#63-扩展性">6.3. 扩展性</a></li><li><a href="#64-数据库和存储系统">6.4. 数据库和存储系统</a></li><li><a href="#65-认识上的误区">6.5. 认识上的误区</a></li></ul></li><li><a href="#7-应用场景">7. 应用场景</a><ul><li><a href="#71-金融服务">7.1. 金融服务</a></li><li><a href="#72-征信管理">7.2. 征信管理</a></li><li><a href="#73-权属管理">7.3. 权属管理</a></li><li><a href="#74-资源共享">7.4. 资源共享</a></li><li><a href="#75-贸易管理">7.5. 贸易管理</a></li><li><a href="#76-物联网">7.6. 物联网</a></li></ul></li><li><a href="#8-分布式系统">8. 分布式系统</a><ul><li><a href="#81-一致性问题consistency">8.1. 一致性问题(Consistency)</a><ul><li><a href="#811-理想一致性">8.1.1. 理想一致性</a></li><li><a href="#812-带约束的一致性">8.1.2. 带约束的一致性</a></li></ul></li><li><a href="#82-共识算法">8.2. 共识算法</a><ul><li><a href="#821-paxos--raft">8.2.1. Paxos &amp; Raft</a></li><li><a href="#822-flp不可能原理">8.2.2. FLP不可能原理</a></li></ul></li><li><a href="#83-cap原理">8.3. CAP原理</a></li><li><a href="#84-acid-原则">8.4. ACID 原则</a></li></ul></li><li><a href="#9-密码学技术">9. 密码学技术</a><ul><li><a href="#91-hash算法">9.1. hash算法</a></li><li><a href="#92-加解密算法">9.2. 加解密算法</a><ul><li><a href="#921-对称">9.2.1. 对称</a></li><li><a href="#922-非对称加密">9.2.2. 非对称加密</a></li><li><a href="#923-混合加密机制">9.2.3. 混合加密机制</a></li></ul></li><li><a href="#93-数字签名">9.3. 数字签名</a></li><li><a href="#94-数字证书">9.4. 数字证书</a></li><li><a href="#95-merkle-树">9.5. Merkle 树</a><ul><li><a href="#951-定义">9.5.1. 定义</a></li><li><a href="#952-特点">9.5.2. 特点</a></li><li><a href="#953-应用场景">9.5.3. 应用场景</a></li></ul></li><li><a href="#96-同态加密">9.6. 同态加密</a></li></ul></li><li><a href="#10-比特币">10. 比特币</a><ul><li><a href="#101-从实体货币到数字货币">10.1. 从实体货币到数字货币</a></li><li><a href="#102-去中心化实现数字货币的难题">10.2. 去中心化实现数字货币的难题</a></li><li><a href="#103-原理与设计">10.3. 原理与设计</a><ul><li><a href="#1031-基本交易过程">10.3.1. 基本交易过程</a></li><li><a href="#1032-概念">10.3.2. 概念</a></li></ul></li><li><a href="#104-闪电网络">10.4. 闪电网络</a></li><li><a href="#105-侧链">10.5. 侧链</a></li></ul></li><li><a href="#11-以太坊">11. 以太坊</a><ul><li><a href="#111-特点">11.1. 特点</a></li><li><a href="#112-概念">11.2. 概念</a></li><li><a href="#113-主要设计">11.3. 主要设计</a></li></ul></li><li><a href="#12-超级账本-hyperldger">12. 超级账本-Hyperldger</a></li></ul><!-- /TOC --><p><a id="markdown-1-分布式记账" name="1-分布式记账"></a></p><h1 id="1-分布式记账"><a href="#1-分布式记账" class="headerlink" title="1. 分布式记账"></a>1. 分布式记账</h1><p><a id="markdown-11-原理" name="11-原理"></a></p><h2 id="1-1-原理"><a href="#1-1-原理" class="headerlink" title="1.1. 原理"></a>1.1. 原理</h2><p>商业活动参与者首先要寻找一个多方均信任的第三方来记账, 确保交易的准确.</p><p>可以很容易设计出一个简单粗暴的分布式记账结构，如下图。多方均允许对账本进行任意读写，一旦发生新的交易即追加到账本上。这种情况下，如果参与多方均诚实可靠，则该方案可以正常工作；但是一旦有参与方恶意篡改已发生过的记录，则无法确保账本记录的正确性。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-1.png" alt="introduction-to-blockchain-1"></p><p>为防止恶意篡改, 可以引入验证机制. 使用<code>数字摘要技术(digital digest)</code>. 每当有新交易记录被追加到账本上, 记录前面交易历史的 hash 值, 此后每个时刻, 参与者都可以重新计算 hash, 看是否与记录的 hash 匹配. 不匹配说明修改过, 也可以容易地定位修改的交易记录了</p><p>不必要每次都计算前面所有历史的 hash, 可以计算 上次的 hash 加上当前交易 的 内容的 hash<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-2.png" alt="introduction-to-blockchain-2"></p><p>这正是一个区块链结构.</p><p><a id="markdown-12-重要性" name="12-重要性"></a></p><h2 id="1-2-重要性"><a href="#1-2-重要性" class="headerlink" title="1.2. 重要性"></a>1.2. 重要性</h2><p>分布式记账问题为何重要？可以类比互联网出现后对社会带来的重大影响。<br>互联网是人类历史上最大的分布式互联系统。作为信息社会的基础设施，它很好地解决了传递信息的问题。然而，由于早期设计上的缺陷，互联网无法确保所传递信息的可靠性，这大大制约了人们利用互联网进行大规模协作的能力。而以区块链为基础的分布式账本科技则可能解决传递可信信息的问题。这意味着基于分布式账本科技的未来商业网络，将成为新一代的文明基础设施——大规模的协作网络。</p><p>分布式账本科技的核心价值在于为未来多方协同网络提供可信基础。区块链引发的记账科技的演进，将促使商业协作和组织形态发生变革。</p><p><a id="markdown-2-区块链的特点" name="2-区块链的特点"></a></p><h1 id="2-区块链的特点"><a href="#2-区块链的特点" class="headerlink" title="2. 区块链的特点"></a>2. 区块链的特点</h1><ul><li>分布式容错性：分布式网络极其 robust , 能够容忍部分节点的异常状态；</li><li>不可篡改性：一致提交后的数据会一直存在，不可被销毁或修改；</li><li>隐私保护性：密码学保证了数据隐私，即便数据泄露，也无法解析。</li></ul><p>可能带来的业务特性</p><ul><li>可信任性：区块链技术可以提供天然可信的分布式账本平台，不需要额外第三方中介机构参与；</li><li>降低成本：跟传统技术相比，区块链技术可能带来更短的时间、更少的人力，降低维护成本；</li><li>增强安全：区块链技术将有利于安全、可靠的审计管理和账目清算，减少犯罪风险。</li></ul><p><a id="markdown-3-区块链的定义" name="3-区块链的定义"></a></p><h1 id="3-区块链的定义"><a href="#3-区块链的定义" class="headerlink" title="3. 区块链的定义"></a>3. 区块链的定义</h1><p>狭义上，区块链是一种以区块为基本单位的链式数据结构，区块中利用数字摘要对之前的交易历史进行校验，适合分布式记账场景下防篡改和可扩展性的需求。<br>广义上，区块链还指代基于区块链结构实现的分布式记账技术，还包括分布式共识、隐私与安全保护、点对点通信技术、网络协议、智能合约等。</p><p><a id="markdown-4-区块链的原理" name="4-区块链的原理"></a></p><h1 id="4-区块链的原理"><a href="#4-区块链的原理" class="headerlink" title="4. 区块链的原理"></a>4. 区块链的原理</h1><ul><li>交易(transaction): 一次对账本的操作,导致账本状态的一次改变.</li><li>区块(block): 记录一段时间内发生的所有交易和状态结果.,是对当前账本状态的一次共识</li><li>链(chain): 由区块按照发生顺序串联而成,是整个版本状态变化的日志记录</li></ul><p>在实现上, 首先假设存在一个分布式的数据记录账本,只允许添加,不允许删除.</p><p><a id="markdown-41-例如比特币" name="41-例如比特币"></a></p><h2 id="4-1-例如比特币"><a href="#4-1-例如比特币" class="headerlink" title="4.1. 例如比特币"></a>4.1. 例如比特币</h2><p>首先，比特币客户端发起一项交易，广播到比特币网络中并等待确认。网络中的节点会将一些收到的等待确认的交易记录打包在一起（此外还要包括前一个区块头部的哈希值等信息），组成一个候选区块。然后，试图找到一个    nonce    串（随机串）放到区块里，使得候选区块的哈希结果满足一定条件（比如小于某个值）。这个nonce    串的查找需要一定的时间进行计算尝试。<br>一旦节点算出来满足条件的    nonce    串，这个区块在格式上就被认为是“合法”了，就可以尝试在网络中将它广播出去。其它节点收到候选区块，进行验证，发现确实符合约定条件了，就承认这个区块是一个合法的新区块，并添加到自己维护的区块链上。当大部分节点都将区块添加到自己维护的区块链结构上时，该区块被网络接受，区块中所包括的交易也就得到确认。</p><p>这种基于算力寻找 nonce 串的共识机制成为 PoW(Proof of Work). (还有很多其他共识机制 PoX, 如 PoS (stake)…) </p><p><a id="markdown-5-区块链的演化" name="5-区块链的演化"></a></p><h1 id="5-区块链的演化"><a href="#5-区块链的演化" class="headerlink" title="5. 区块链的演化"></a>5. 区块链的演化</h1><p>比特币区块链支持简单的脚本计算, 仅限于数字画笔相关的处理. 还可以将区块链上执行的处理过程进一步泛化,即提供 智能合约 Smart Contract. 由此提供除货币交易功能外更灵活的合约共功能,执行更为复杂的操作.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-3.png" alt="introduction-to-blockchain-3"></p><p><a id="markdown-6-关键问题与挑战" name="6-关键问题与挑战"></a></p><h1 id="6-关键问题与挑战"><a href="#6-关键问题与挑战" class="headerlink" title="6. 关键问题与挑战"></a>6. 关键问题与挑战</h1><p><a id="markdown-61-抗抵赖与隐私保护" name="61-抗抵赖与隐私保护"></a></p><h2 id="6-1-抗抵赖与隐私保护"><a href="#6-1-抗抵赖与隐私保护" class="headerlink" title="6.1. 抗抵赖与隐私保护"></a>6.1. 抗抵赖与隐私保护</h2><ul><li>怎么防止交易记录被篡改</li><li>怎么证明交易双方的身份</li><li>怎么保护交易双方的隐私</li></ul><p><a id="markdown-62-分布式共识" name="62-分布式共识"></a></p><h2 id="6-2-分布式共识"><a href="#6-2-分布式共识" class="headerlink" title="6.2. 分布式共识"></a>6.2. 分布式共识</h2><p>指标: 容错的结点比例, 决策收敛速度, 出错后的恢复,动态特性等.<br><a id="markdown-63-扩展性" name="63-扩展性"></a></p><h2 id="6-3-扩展性"><a href="#6-3-扩展性" class="headerlink" title="6.3. 扩展性"></a>6.3. 扩展性</h2><p>不能简单得增加结点来扩展整个系统的处理能力.</p><p>对于比特币和以太坊区块链而言，网络中每个参与维护的核心节点都要保持一份完整的存储，并且进行智能合约的处理。此时，整个网络的总存储和计算能力，取决于单个节点的能力。甚至当网络中节点数过多时，可能会因为一致性的达成过程延迟降低整个网络的性能。尤其在公有网络中，由于大量低性能处理节点的存在，问题将更加明显。</p><p>要解决这个问题，根本上是放松对每个节点都必须参与完整处理的限制（当然，网络中节点要能合作完成完整的处理），这个思路已经在超级账本中得到应用；同时尽量减少核心层的处理工作。</p><p>在联盟链模式下，还可以专门采用高性能的节点作为核心节点，用相对较弱的节点仅作为代理访问节点。</p><p><a id="markdown-64-数据库和存储系统" name="64-数据库和存储系统"></a></p><h2 id="6-4-数据库和存储系统"><a href="#6-4-数据库和存储系统" class="headerlink" title="6.4. 数据库和存储系统"></a>6.4. 数据库和存储系统</h2><p>区块链网络中的大量信息需要写到文件和数据库中进行存储。</p><p>预测将可能出现更具针对性的“块数据库（BlockDB）”，专门服务类似区块链这样的新型数据业务，其中每条记录将包括一个完整的区块信息，并天然地跟历史信息进行关联，一旦写入确认则无法修改。所有操作的最小单位将是一个块。为了实现这种结构，需要原生支持高效的签名和加解密处理。</p><p><a id="markdown-65-认识上的误区" name="65-认识上的误区"></a></p><h2 id="6-5-认识上的误区"><a href="#6-5-认识上的误区" class="headerlink" title="6.5. 认识上的误区"></a>6.5. 认识上的误区</h2><p>区块链不等于数据库。虽然区块链也可以用来存储数据，但它要解决的核心问题是多方的互信问题。单纯从存储数据角度，它的效率可能不高，不建议把大量的原始数据放到区块链系统上。</p><p><a id="markdown-7-应用场景" name="7-应用场景"></a></p><h1 id="7-应用场景"><a href="#7-应用场景" class="headerlink" title="7. 应用场景"></a>7. 应用场景</h1><p><a id="markdown-71-金融服务" name="71-金融服务"></a></p><h2 id="7-1-金融服务"><a href="#7-1-金融服务" class="headerlink" title="7.1. 金融服务"></a>7.1. 金融服务</h2><p>区块链带来的潜在优势包括降低交易成本、减少跨组织交易风险等。</p><p><a id="markdown-72-征信管理" name="72-征信管理"></a></p><h2 id="7-2-征信管理"><a href="#7-2-征信管理" class="headerlink" title="7.2. 征信管理"></a>7.2. 征信管理</h2><p>区块链平台将可能提供前所未有规模的相关性极高的数据，这些数据可以在时空中准确定位，并严格关联到用户。因此，基于区块链提供数据进行征信管理，将大大提高信用评估的准确率，同时降低评估成本</p><p>另外，跟传统依靠人工的审核过程不同，区块链中交易处理完全遵循约定自动化执行。基于区块链的信用机制将天然具备稳定性和中立性。</p><p><a id="markdown-73-权属管理" name="73-权属管理"></a></p><h2 id="7-3-权属管理"><a href="#7-3-权属管理" class="headerlink" title="7.3. 权属管理"></a>7.3. 权属管理</h2><p>区块链技术可以用于产权、版权等所有权的管理和追踪。其中包括汽车、房屋、艺术品等各种贵重物品的交易等，也包括数字出版物，以及可以标记的数字资源。<br>目前权属管理领域存在的几个难题是：</p><ul><li>物品所有权的确认和管理；</li><li>交易的安全性和可靠性保障；</li><li>必要的隐私保护机制。</li></ul><p>利用区块链技术，物品的所有权是写在数字链上的，谁都无法修改。并且一旦出现合同中约定情况，区块链技术将确保合同能得到准确执行。这能有效减少传统情况下纠纷仲裁环节的人工干预和执行成本</p><p><a id="markdown-74-资源共享" name="74-资源共享"></a></p><h2 id="7-4-资源共享"><a href="#7-4-资源共享" class="headerlink" title="7.4. 资源共享"></a>7.4. 资源共享</h2><p>相比于依赖于中间方的资源共享模式，基于区块链的模式有潜力更直接的连接资源的供给方和需求方，其透明、不可篡改的特性有助于减小摩擦。<br><a id="markdown-75-贸易管理" name="75-贸易管理"></a></p><h2 id="7-5-贸易管理"><a href="#7-5-贸易管理" class="headerlink" title="7.5. 贸易管理"></a>7.5. 贸易管理</h2><p>区块链技术可以帮助自动化国际贸易和物流供应链领域中繁琐的手续和流程。基于区块链设计的贸易管理方案会为参与的多方企业带来极大的便利。另外，贸易中销售和法律合同的数字化、货物监控与检测、实时支付等方向都可能成为创业公司的</p><p><a id="markdown-76-物联网" name="76-物联网"></a></p><h2 id="7-6-物联网"><a href="#7-6-物联网" class="headerlink" title="7.6. 物联网"></a>7.6. 物联网</h2><p>物联网络中每一个设备分配地址，给该地址所关联一个账户，用户通过向账户中支付费用可以租借设备，以执行相关动作，从而达到租借物联网的应用。</p><p>典型的应用包括    PM2.5    监测点的数据获取、温度检测服务、服务器租赁、网络摄像头数据调用等等。</p><p>另外，随着物联网设备的增多、边沿计算需求的增强，大量设备之间形成分布式自组织的管理模式，并且对容错性要求很高。区块链自身分布式和抗攻击的特点可以很好地融合到这一场景中。</p><p><a id="markdown-8-分布式系统" name="8-分布式系统"></a></p><h1 id="8-分布式系统"><a href="#8-分布式系统" class="headerlink" title="8. 分布式系统"></a>8. 分布式系统</h1><p><a id="markdown-81-一致性问题consistency" name="81-一致性问题consistency"></a></p><h2 id="8-1-一致性问题-Consistency"><a href="#8-1-一致性问题-Consistency" class="headerlink" title="8.1. 一致性问题(Consistency)"></a>8.1. 一致性问题(Consistency)</h2><p>对于系统中的多个服务结点,给定一系列操作, 在协议(某种共识算法)保障下, 使得它们对处理结果达成某种程度的一致存在的问题</p><ul><li>节点之间的网络通讯是不可靠的，包括任意延迟和内容故障；</li><li>节点的处理可能是错误的，甚至节点自身随时可能宕机；</li><li>同步调用会让系统变得不具备可扩展性。</li></ul><blockquote><blockquote><p>解决的基本思想: 将可能引发不一致的并行操作串行化</p></blockquote></blockquote><p><a id="markdown-811-理想一致性" name="811-理想一致性"></a></p><h3 id="8-1-1-理想一致性"><a href="#8-1-1-理想一致性" class="headerlink" title="8.1.1. 理想一致性"></a>8.1.1. 理想一致性</h3><p>分布式系统一致性应满足</p><ul><li>可终止性（Termination）：一致的结果在有限时间内能完成；</li><li>共识性（Consensus）：不同节点最终完成决策的结果应该相同；</li><li>合法性（Validity）：决策的结果必须是其它进程提出的提案。</li></ul><p><a id="markdown-812-带约束的一致性" name="812-带约束的一致性"></a></p><h3 id="8-1-2-带约束的一致性"><a href="#8-1-2-带约束的一致性" class="headerlink" title="8.1.2. 带约束的一致性"></a>8.1.2. 带约束的一致性</h3><p>理想情况的强一致性是很难达到的. 其实实际需求并没有那么强,可以适当放宽一致性要求.</p><p><a id="markdown-82-共识算法" name="82-共识算法"></a></p><h2 id="8-2-共识算法"><a href="#8-2-共识算法" class="headerlink" title="8.2. 共识算法"></a>8.2. 共识算法</h2><p>由于响应请求往往存在时延、网络会发生中断、节点会发生故障、甚至存在恶意节点故意要破坏系统, 不能简单地通过多播过程投票.</p><p>一般地，把故障（不响应）的情况称为“非拜占庭错误”，恶意响应的情况称为“拜占庭错误”（对应节点为拜占庭节点）。</p><p><a id="markdown-821-paxos--raft" name="821-paxos--raft"></a></p><h3 id="8-2-1-Paxos-amp-Raft"><a href="#8-2-1-Paxos-amp-Raft" class="headerlink" title="8.2.1. Paxos &amp; Raft"></a>8.2.1. Paxos &amp; Raft</h3><p>这种算法解决的是对于 分布式系统中存在故障(fault), 但不存在恶意(corrupt)结点场景(即可能消息丢失或重复, 但无错误信息)下的共识达成(consensus)问题.</p><p><a id="markdown-822-flp不可能原理" name="822-flp不可能原理"></a></p><h3 id="8-2-2-FLP不可能原理"><a href="#8-2-2-FLP不可能原理" class="headerlink" title="8.2.2. FLP不可能原理"></a>8.2.2. FLP不可能原理</h3><blockquote><p>在网络可靠，存在节点失效（即便只有一个）的最小化异步模型系统中，不存在一个可以解决一致性问题的确定性算法。</p></blockquote><p>即一个可扩展的分布式系统的共识问题的下限是无解</p><p>它告诉人们，不要浪费时间去为异步分布式系统设计在任意场景下都能实现共识的算法。</p><p>但是可以在付出一定代价下, 达到一定的目标.</p><p>下面的CAP原理告诉我们能做到多少<br><a id="markdown-83-cap原理" name="83-cap原理"></a></p><h2 id="8-3-CAP原理"><a href="#8-3-CAP原理" class="headerlink" title="8.3. CAP原理"></a>8.3. CAP原理</h2><p>分布式系统不可能同时确保一致性（Consistency）、可用性（Availablity）和分区容忍性（Partition），<strong>设计中往往需要弱化对某个特性的保证。</strong></p><ul><li>一致性（Consistency）：任何操作应该都是原子的，发生在后面的事件能看到前面事件发生导致的结果，注意这里指的是强一致性；</li><li>可用性（Availablity）：在有限时间内，任何非失败节点都能应答请求；</li><li>分区容忍性（Partition）：网络可能发生分区，即节点之间的通信不可保障。</li></ul><p>CAP 不能同时满足,设计系统时针对应用场景弱化对某个特性的支持</p><ul><li>弱化一致性: 如网站静态页面内容, 实时性较弱的查询类数据库</li><li>弱化可用性: 对结果一致性很敏感的应用. 如银行取款机</li><li>弱化分区容忍性: 如 某些关系型数据库, ZooKeeper</li></ul><p><a id="markdown-84-acid-原则" name="84-acid-原则"></a></p><h2 id="8-4-ACID-原则"><a href="#8-4-ACID-原则" class="headerlink" title="8.4. ACID 原则"></a>8.4. ACID 原则</h2><p>即    Atomicity（原子性）、Consistency（一致性）、Isolation（隔离性）、Durability（持久性）。<br>ACID    原则描述了对分布式数据库的一致性需求，同时付出了可用性的代价。<br>Atomicity：每次操作是原子的，要么成功，要么不执行；<br>Consistency：数据库的状态是一致的，无中间状态；<br>Isolation：各种操作彼此互相不影响；<br>Durability：状态的改变是持久的，不会失效。</p><p>一个与之相对的原则是    BASE（Basic    Availiability，Soft    state，Eventually    Consistency），牺牲掉对一致性的约束（最终一致性），来换取一定的可用性。</p><p><a id="markdown-9-密码学技术" name="9-密码学技术"></a></p><h1 id="9-密码学技术"><a href="#9-密码学技术" class="headerlink" title="9. 密码学技术"></a>9. 密码学技术</h1><p><a id="markdown-91-hash算法" name="91-hash算法"></a></p><h2 id="9-1-hash算法"><a href="#9-1-hash算法" class="headerlink" title="9.1. hash算法"></a>9.1. hash算法</h2><p>是一种信息摘要, 可以用于检验内容的完全性, 一致性等. 流行的有  md5, sha-1, sha-2(Secure Hash Algorithm), sha-1已被证明不具备”强抗碰撞性”<br><a id="markdown-92-加解密算法" name="92-加解密算法"></a></p><h2 id="9-2-加解密算法"><a href="#9-2-加解密算法" class="headerlink" title="9.2. 加解密算法"></a>9.2. 加解密算法</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-4.png" alt="introduction-to-blockchain-4"></p><p>组件包括: 加解密算法,加密密钥,解密密钥.<br>根据加解密的密钥是否相同,可以分成对称加密与非对称加密(asymmetrix cryptography).<br><a id="markdown-921-对称" name="921-对称"></a></p><h3 id="9-2-1-对称"><a href="#9-2-1-对称" class="headerlink" title="9.2.1. 对称"></a>9.2.1. 对称</h3><p>对称密码从实现原理上可以分为两种：分组密码和序列密码。前者将明文切分为定长数据块<br>作为加密单位，应用最为广泛。后者则只对一个字节进行加密，且密码不断变化，只用在一<br>些特定领域，如数字媒介的加密等。<br>代表算法</p><ul><li>DES（Data    Encryption    Standard）：经典的分组加密算法,将    64    位明文加密为    64    位的密文，其密钥长度为    56位    +    8    位校验。现在已经很容易被暴力破解。</li><li>3DES：三重    DES    操作：加密    —&gt;    解密    —&gt;    加密，处理过程和加密强度优于    DES，但现在也被认为不够安全。</li><li>AES（Advanced    Encryption    Standard）：分组算法，分组长度为    128、192、256    位三种。AES    的优势在于处理速度快，整个过程可以数学化描述，目前尚未有有效的破解手段。</li></ul><p>适用于大量数据的加解密；不能用于签名场景；需要提前分发密钥。<br><a id="markdown-922-非对称加密" name="922-非对称加密"></a></p><h3 id="9-2-2-非对称加密"><a href="#9-2-2-非对称加密" class="headerlink" title="9.2.2. 非对称加密"></a>9.2.2. 非对称加密</h3><p>非对称加密是现代密码学历史上最为伟大的发明，可以很好的解决对称加密需要的提前分发密钥问题。</p><p>一般比对称加解密算法慢两到三个数量级；同时加密强度相比对称加密要差。<br>非对称加密算法的安全性往往需要基于数学问题来保障，目前主要有基于大数质因子分解、离散对数、椭圆曲线等几种思路。<br>代表算法:</p><ul><li>RSA：经典的公钥算法。算法利用了对大数进行质因子分解困难的特性，但目前还没有数学证明两者难度等价，或许存在未知算法在不进行大数分解的前提下解密。</li><li>Diffie-Hellman    密钥交换：基于离散对数无法快速求解，可以在不安全的通道上，双方协商一个公共密钥。</li><li>ElGamal：利用了模运算下求离散对数困难的特性。被应用在PGP    等安全工具中。</li><li>椭圆曲线算法（Elliptic    curve    cryptography，ECC）：基于对加解密算法椭圆曲线上特定点进行特殊乘法逆运算难以计算的特性。一般适用于签名场景或密钥协商，不适于大量数据的加解密。</li></ul><p>RSA    算法等已被认为不够安全，一般推荐采用椭圆曲线系列算法。</p><p><a id="markdown-923-混合加密机制" name="923-混合加密机制"></a></p><h3 id="9-2-3-混合加密机制"><a href="#9-2-3-混合加密机制" class="headerlink" title="9.2.3. 混合加密机制"></a>9.2.3. 混合加密机制</h3><p>即先用计算复杂度高的非对称加密协商一个临时的对称加密密钥（会话密钥，一般相对内容<br>来说要短的多），然后双方再通过对称加密对传递的大量数据进行加解密处理。<br>典型的场景是现在大家常用的    HTTPS    机制。HTTPS    实际上是利用了    Transport    Layer<br>Security/Secure    Socket    Layer（TLS/SSL）来实现可靠的传输。TLS    为    SSL    的升级版本</p><p><a id="markdown-93-数字签名" name="93-数字签名"></a></p><h2 id="9-3-数字签名"><a href="#9-3-数字签名" class="headerlink" title="9.3. 数字签名"></a>9.3. 数字签名</h2><p>类似在纸质合同上签名确认合同内容，数字签名用于证实某数字内容的完整性（integrity）和来源（或不可抵赖，non-repudiation）。</p><p>一个典型的场景是，A    要发给    B    一份信息.<br>A    先对文件进行摘要，然后用自己的私钥进行加密，将文件和加密串都发给B。B    收到文件和加密串后，用    A    的公钥来解密加密串，得到原始的数字摘要，跟对文件进行摘要后的结果进行比对。</p><p><a id="markdown-94-数字证书" name="94-数字证书"></a></p><h2 id="9-4-数字证书"><a href="#9-4-数字证书" class="headerlink" title="9.4. 数字证书"></a>9.4. 数字证书</h2><p>数字证书用来证明某个公钥是谁的，并且内容是正确的。</p><p>数字证书内容可能包括版本、序列号、签名算法类型、签发者信息、有效期、被签发人、签发的公开密钥、CA    数字签名、其它信息等等，一般使用最广泛的标准为    ITU    和    ISO    联合制定的    X.509    规范。<br>其中，最重要的包括        签发的公开密钥    、    CA    数字签名        两个信息。因此，只要通过这个证书就能证明某个公钥是合法的，因为带有    CA    的数字签名。</p><p><a id="markdown-95-merkle-树" name="95-merkle-树"></a></p><h2 id="9-5-Merkle-树"><a href="#9-5-Merkle-树" class="headerlink" title="9.5. Merkle 树"></a>9.5. Merkle 树</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/merkle.png" alt="merkle"><br><a id="markdown-951-定义" name="951-定义"></a></p><h3 id="9-5-1-定义"><a href="#9-5-1-定义" class="headerlink" title="9.5.1. 定义"></a>9.5.1. 定义</h3><p>默克尔树（又叫哈希树）是一种二叉树，由一个根节点、一组中间节点和一组叶节点组成。最下面的叶节点包含存储数据或其哈希值，每个中间节点是它的两个孩子节点内容的哈希值，根节点也是由它的两个子节点内容的哈希值组成。</p><p><a id="markdown-952-特点" name="952-特点"></a></p><h3 id="9-5-2-特点"><a href="#9-5-2-特点" class="headerlink" title="9.5.2. 特点"></a>9.5.2. 特点</h3><p> 底层数据的任何变动，都会传递到其父亲节点，一直到树根。<br><a id="markdown-953-应用场景" name="953-应用场景"></a></p><h3 id="9-5-3-应用场景"><a href="#9-5-3-应用场景" class="headerlink" title="9.5.3. 应用场景"></a>9.5.3. 应用场景</h3><ul><li>快速比较大量数据：当两个默克尔树根相同时，则意味着所代表的数据必然相同。</li><li>快速定位修改：例如上例中，如果    D1    中数据被修改，会影响到    N1，N4    和    Root。因此，沿着    Root    —&gt;    N4    —&gt;    N1，可以快速定位到发生改变的    D1；</li><li>零知识证明：例如如何证明某个数据（D0……D3）中包括给定内容    D0，很简单，构造一个默克尔树，公布    N0，N1，N4，Root，D0    拥有者可以很容易检测    D0    存在，但不知道其它内容。</li></ul><p><a id="markdown-96-同态加密" name="96-同态加密"></a></p><h2 id="9-6-同态加密"><a href="#9-6-同态加密" class="headerlink" title="9.6. 同态加密"></a>9.6. 同态加密</h2><p>同态加密（Homomorphic    Encryption）是一种特殊的加密方法，允许对密文进行处理得到仍然是加密的结果，即对密文直接进行处理，跟对明文进行处理再加密，得到的结果相同。从代数的角度讲，即同态性。(保运算)</p><p><a id="markdown-10-比特币" name="10-比特币"></a></p><h1 id="10-比特币"><a href="#10-比特币" class="headerlink" title="10. 比特币"></a>10. 比特币</h1><p><a id="markdown-101-从实体货币到数字货币" name="101-从实体货币到数字货币"></a></p><h2 id="10-1-从实体货币到数字货币"><a href="#10-1-从实体货币到数字货币" class="headerlink" title="10.1. 从实体货币到数字货币"></a>10.1. 从实体货币到数字货币</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-5.png" alt="introduction-to-blockchain-5"></p><p><a id="markdown-102-去中心化实现数字货币的难题" name="102-去中心化实现数字货币的难题"></a></p><h2 id="10-2-去中心化实现数字货币的难题"><a href="#10-2-去中心化实现数字货币的难题" class="headerlink" title="10.2. 去中心化实现数字货币的难题"></a>10.2. 去中心化实现数字货币的难题</h2><ul><li>货币的防伪：谁来负责对货币的真伪进行鉴定；</li><li>货币的交易：如何确保货币从一方安全转移到另外一方；</li><li>避免双重支付：如何避免同一份货币支付给多个接收者。</li></ul><p><a id="markdown-103-原理与设计" name="103-原理与设计"></a></p><h2 id="10-3-原理与设计"><a href="#10-3-原理与设计" class="headerlink" title="10.3. 原理与设计"></a>10.3. 原理与设计</h2><p>比特币网络是一个分布式的点对点网络，网络中的矿工通过“挖矿”来完成对交易记录的记账过程，维护网络的正常运行。<br><a id="markdown-1031-基本交易过程" name="1031-基本交易过程"></a></p><h3 id="10-3-1-基本交易过程"><a href="#10-3-1-基本交易过程" class="headerlink" title="10.3.1. 基本交易过程"></a>10.3.1. 基本交易过程</h3><p>比特币中没有账户的概念。因此，每次发生交易，用户需要将交易记录写到比特币网络账本中，等网络确认后即可认为交易完成。</p><p>除了挖矿获得奖励的    coinbase    交易只有输出，正常情况下每个交易需要包括若干输入和输出，未经使用（引用）的交易的输出（Unspent    Transaction    Outputs，UTXO）可以被新的交易引用作为其合法的输入。被使用过的交易的输出（Spent    Transaction    Outputs，STXO），则无法被引用作为合法输入。</p><p><a id="markdown-1032-概念" name="1032-概念"></a></p><h3 id="10-3-2-概念"><a href="#10-3-2-概念" class="headerlink" title="10.3.2. 概念"></a>10.3.2. 概念</h3><ul><li>账户</li></ul><p>比特币采用了非对称的加密算法，用户自己保留私钥，对自己发出的交易进行签名确认，并公开公钥。<br>比特币的账户地址其实就是用户公钥经过一系列    Hash（HASH160，或先进行    SHA256，然后进行    RIPEMD160）及编码运算后生成的    160    位（20    字节）的字符串。</p><ul><li>交易</li></ul><p>易可能包括如下信息：<br>付款人地址：合法的地址，公钥经过    SHA256    和    RIPEMD160    两次    Hash，得到    160    位Hash    串；<br>付款人对交易的签字确认：确保交易内容不被篡改；<br>付款人资金的来源交易    ID：从哪个交易的输出作为本次交易的输入；<br>交易的金额：多少钱，跟输入的差额为交易的服务费；<br>收款人地址：合法的地址；<br>时间戳：交易何时能生效<br>节点收到交易信息后，将进行如下检查：<br>交易是否已经处理过；<br>交易是否合法。包括地址是否合法、发起交易者是否是输入地址的合法拥有者、是否是UTXO；<br>交易的输入之和是否大于输出之和。</p><ul><li>交易脚本: 保障交易的完成的核心机制</li><li>区块</li></ul><p>比特币区块链的一个区块不能超过    1    MB，将主要包括如下内容：<br>区块大小：4    字节；<br>区块头：80    字节：<br>交易个数计数器：1~9    字节；<br>所有交易的具体内容，可变长，匹配    Merkle    树叶子节点顺序。</p><ul><li>避免作恶: 在一个开放的网络中，无法通过技术手段保证每个人都是合作的。但可以通过经济博弈来让合作者得到利益，让非合作者遭受损失和风险 .  如共识机制PoW</li></ul><p><a id="markdown-104-闪电网络" name="104-闪电网络"></a></p><h2 id="10-4-闪电网络"><a href="#10-4-闪电网络" class="headerlink" title="10.4. 闪电网络"></a>10.4. 闪电网络</h2><p>比特币交易性能：全网每秒    7    笔左右的交易速度，远低于传统的金融交易系统</p><p>提出闪电网络的解决方法<br>主要通过引入智能合约的思想来完善链下的交易渠道。核心的概念主要有两个：RSMC（Recoverable    Sequence    Maturity    Contract）和    HTLC（Hashed    TimelockContract）。前者解决了链下交易的确认问题，后者解决了支付通道的问题。</p><p><a id="markdown-105-侧链" name="105-侧链"></a></p><h2 id="10-5-侧链"><a href="#10-5-侧链" class="headerlink" title="10.5. 侧链"></a>10.5. 侧链</h2><p>以比特币区块链作为主链（Parent    chain），其他区块链作为侧链，二者通过双向挂钩（Two-way    peg），可实现比特币从主链转移到侧链进行流通。<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-6.png" alt="introduction-to-blockchain-6"></p><p>侧链可以是一个独立的区块链，有自己按需定制的账本、共识机制、交易类型、脚本和合约的支持等。侧链不能发行比特币，但可以通过支持与比特币区块链挂钩来引入和流通一定数量的比特币。当比特币在侧链流通时，主链上对应的比特币会被锁定，直到比特币从侧链回到主链。可以看到，侧链机制可将一些定制化或高频的交易放到比特币主链之外进行，实现了比特币区块链的扩展。侧链的核心原理在于能够冻结一条链上的资产，然后在另一条链上产生，可以通过多种方式来实现, 如 SPV<br><a id="markdown-11-以太坊" name="11-以太坊"></a></p><h1 id="11-以太坊"><a href="#11-以太坊" class="headerlink" title="11. 以太坊"></a>11. 以太坊</h1><p>以太坊区块链底层也是一个类似比特币网络的    P2P    网络平台，智能合约运行在网络中的以太坊虚拟机里。网络自身是公开可接入的，任何人都可以接入并参与网络中数据的维护，提供运行以太坊虚拟机的资源。<br><a id="markdown-111-特点" name="111-特点"></a></p><h2 id="11-1-特点"><a href="#11-1-特点" class="headerlink" title="11.1. 特点"></a>11.1. 特点</h2><p>跟比特币项目相比</p><ul><li>支持图灵完备的智能合约，设计了编程语言    Solidity    和虚拟机    EVM；</li><li>选用了内存需求较高的哈希函数，避免出现强算力矿机、矿池攻击；</li><li>叔块（Uncle    Block）激励机制，降低矿池的优势，并减少区块产生间隔（10    分钟降低到15    秒左右）；</li></ul><p>*采用账户系统和世界状态，而不是    UTXO，容易支持更复杂的逻辑；</p><ul><li>通过    Gas    限制代码执行指令数，避免循环执行攻击；</li><li>支持    PoW    共识算法，并计划支持效率更高的    PoS    算法。</li></ul><p><a id="markdown-112-概念" name="112-概念"></a></p><h2 id="11-2-概念"><a href="#11-2-概念" class="headerlink" title="11.2. 概念"></a>11.2. 概念</h2><ul><li>智能合约:即以计算机程序的方式来缔结和运行各种合约。</li><li><p>账户: 比特币在设计中并没有账户（Account）的概念，而是采用了UTXO    模型记录整个系统的状态。任何人都可以通过交易历史来推算出用户的余额信息。而以太坊则直接用账户来记录系统状态。每个账户存储余额信息、智能合约代码和内部数据存储等。 以太坊账户分为两类</p><ul><li>合约账户：存储执行的智能合约代码，只能被外部账户来调用激活；</li><li>外部账户：以太币拥有者账户，对应到某公钥。账户包括    nonce、balance、storageRoot、codeHash    等字段，由个人来控制。</li></ul></li><li><p>交易: 是指从一个账户到另一个账户的消息数据</p></li></ul><p>包括如下字段：</p><pre><code>* to：目标账户地址。* value：可以指定转移的以太币数量。* nonce：交易相关的字串，用于防止交易被重放。* gasPrice：执行交易需要消耗的    Gas    价 格。* startgas：交易消耗的最大    Gas    值。* signature：签名信息。</code></pre><p>在发送交易时，用户需要缴纳一定的交易费用，通过以太币方式进行支付<br>和消耗。</p><ul><li>燃料（Gas），控制某次交易执行指令的上限。每执行一条合约指令会消耗固定的燃料。当某个交易还未执行结束，而燃料消耗完时，合约执行终止并回滚状态。</li></ul><p>Gas    可以跟以太币进行兑换。需要注意的是，以太币的价格是波动的，但运行某段智能合约的燃料费用可以是固定的，通过设定    Gas    价格等进行调节<br><a id="markdown-113-主要设计" name="113-主要设计"></a></p><h2 id="11-3-主要设计"><a href="#11-3-主要设计" class="headerlink" title="11.3. 主要设计"></a>11.3. 主要设计</h2><ul><li>运行环境: EVM</li></ul><p>以太坊虚拟机是一个隔离的轻量级虚拟机环境，运行在其中的智能合约代码无法访问本地网络、文件系统或其它进程。<br>对同一个智能合约来说，往往需要在多个以太坊虚拟机中同时运行多份，以确保整个区块链数据的一致性和高度的容错性。另一方面，这也限制了整个网络的容量。</p><ul><li>开发语言: Solidity, vyper…  智能合约编写完毕后，用编译器编译为以太坊虚拟机专用的二进制格式（EVM    bytecode），由客户端上传到区块链当中，之后在矿工的以太坊虚拟机中执行。</li><li>交易模型: 以太坊的账户模型与比特币的 UXTO 模型对比<img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/introduction-to-blockchain-7.png" alt="introduction-to-blockchain-7"></li><li>共识: 基于成熟的    PoW    共识的变种算法    Ethash    协议</li><li>客户端和开发库</li></ul><p>以太坊客户端可用于接入以太坊网络，进行账户管理、交易、挖矿、智能合约等各方面操<br>作。</p><p><a id="markdown-12-超级账本-hyperldger" name="12-超级账本-hyperldger"></a></p><h1 id="12-超级账本-Hyperldger"><a href="#12-超级账本-Hyperldger" class="headerlink" title="12. 超级账本-Hyperldger"></a>12. 超级账本-Hyperldger</h1><p>Hyperledger    项目是首个面向企业的开放区块链技术的重要探索.该项目试图打造一个透明、公开、去中心化的分布式账本项目，作为区块链技术的开源规范和标准，让更多的应用能更容易的建立在区块链技术之上。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;以下内容摘录自 &amp;lt;&amp;lt;区块链原理,设计与应用&amp;gt;&amp;gt;&lt;br&gt;
    
    </summary>
    
      <category term="区块链" scheme="https://mbinary.xyz/categories/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
    
      <category term="区块链" scheme="https://mbinary.xyz/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/"/>
    
      <category term="数字货币" scheme="https://mbinary.xyz/tags/%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81/"/>
    
  </entry>
  
  <entry>
    <title>『算法』概述</title>
    <link href="https://mbinary.xyz/algorithm-general.html"/>
    <id>https://mbinary.xyz/algorithm-general.html</id>
    <published>2018-07-21T18:20:00.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-算法">1. 算法</a></li><li><a href="#2-可以解决哪些类型的问题">2. 可以解决哪些类型的问题</a></li><li><a href="#3-算法分析">3. 算法分析</a></li><li><a href="#4-算法设计">4. 算法设计</a><ul><li><a href="#41-分治divide-and-conquer">4.1. 分治(divide and conquer)</a></li></ul></li><li><a href="#5-递归式">5. 递归式</a><ul><li><a href="#51-代换法">5.1. 代换法</a><ul><li><a href="#511-步骤">5.1.1. 步骤</a></li><li><a href="#512-例子">5.1.2. 例子</a></li><li><a href="#513-放缩">5.1.3. 放缩</a></li><li><a href="#514-改变变量">5.1.4. 改变变量</a></li></ul></li><li><a href="#52-递归树">5.2. 递归树</a></li><li><a href="#53-主方法master-method">5.3. 主方法(master method)</a><ul><li><a href="#531-记忆">5.3.1. 记忆</a></li><li><a href="#532-证明">5.3.2. 证明</a><ul><li><a href="#5321-证明当-n-为-b-的正合幂时成立">5.3.2.1. 证明当 n 为 b 的正合幂时成立</a></li><li><a href="#5322-分析扩展至所有正整数-n-都成立">5.3.2.2. 分析扩展至所有正整数 n 都成立</a></li></ul></li></ul></li></ul></li><li><a href="#6-随机算法">6. 随机算法</a><ul><li><a href="#61-随机排列数组shuffle">6.1. 随机排列数组(shuffle)</a><ul><li><a href="#611-permute-by-sorting">6.1.1. PERMUTE-BY-SORTING</a></li><li><a href="#612-randomize-in-place">6.1.2. RANDOMIZE-IN-PLACE</a></li></ul></li></ul></li><li><a href="#7-组合方程的近似算法">7. 组合方程的近似算法</a></li><li><a href="#8-概率分析与指示器变量例子">8. 概率分析与指示器变量例子</a><ul><li><a href="#81-球与盒子">8.1. 球与盒子</a></li><li><a href="#82-序列">8.2. 序列</a></li></ul></li><li><a href="#9-摊还分析">9. 摊还分析</a><ul><li><a href="#91-聚合分析aggregate-analysis">9.1. 聚合分析(aggregate analysis)</a></li><li><a href="#92-核算法-accounting-method">9.2. 核算法 (accounting method)</a></li><li><a href="#93-势能法potential-method">9.3. 势能法(potential method)</a></li></ul></li></ul><!-- /TOC --><p><a id="markdown-1-算法" name="1-算法"></a></p><h1 id="1-算法"><a href="#1-算法" class="headerlink" title="1. 算法"></a>1. 算法</h1><p>定义良好的计算过程,取输入,并产生输出. 即算法是一系列的计算步骤,将输入数据转化为输出结果</p><p>算法的特点:</p><ul><li>有穷性</li><li>确定性</li><li>可行性</li><li>0 或多个输入</li><li>1 或多个输出</li></ul><p><a id="markdown-2-可以解决哪些类型的问题" name="2-可以解决哪些类型的问题"></a></p><h1 id="2-可以解决哪些类型的问题"><a href="#2-可以解决哪些类型的问题" class="headerlink" title="2. 可以解决哪些类型的问题"></a>2. 可以解决哪些类型的问题</h1><ul><li>大数据的存储,以及开发出进行这方面数据分析的工具</li><li>网络数据的传输,寻路, 搜索</li><li>电子商务密码, (数值算法,数论)</li><li>资源分配,最大效益</li><li>…</li></ul><p><a id="markdown-3-算法分析" name="3-算法分析"></a></p><h1 id="3-算法分析"><a href="#3-算法分析" class="headerlink" title="3. 算法分析"></a>3. 算法分析</h1><p>衡量算法的优劣<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/algorithm-general-1.png" alt="algorithm-general-1"></p><ul><li>$\omicron,O,\Omega,\Theta$</li><li>最坏情况, 平均情况</li><li>增长的量级$ O(1), O(log^*n), O(logn), O(n), O(n^k), O(a^n) $</li></ul><p>$\log^{*}<em>(\log x) = log^{\</em>}x-1$</p><p><a id="markdown-4-算法设计" name="4-算法设计"></a></p><h1 id="4-算法设计"><a href="#4-算法设计" class="headerlink" title="4. 算法设计"></a>4. 算法设计</h1><p><a id="markdown-41-分治divide-and-conquer" name="41-分治divide-and-conquer"></a></p><h2 id="4-1-分治-divide-and-conquer"><a href="#4-1-分治-divide-and-conquer" class="headerlink" title="4.1. 分治(divide and conquer)"></a>4.1. 分治(divide and conquer)</h2><p>结构上是递归的,<br>步骤: 分解,解决, 合并<br>eg.  快排,归并排序, 矩阵乘法(Strassen $O(log_2 7)$</p><p><a id="markdown-5-递归式" name="5-递归式"></a></p><h1 id="5-递归式"><a href="#5-递归式" class="headerlink" title="5. 递归式"></a>5. 递归式</h1><p> $T(n) = aT(\frac{n} {b})+f(n)$</p><p><a id="markdown-51-代换法" name="51-代换法"></a></p><h2 id="5-1-代换法"><a href="#5-1-代换法" class="headerlink" title="5.1. 代换法"></a>5.1. 代换法</h2><p><a id="markdown-511-步骤" name="511-步骤"></a></p><h3 id="5-1-1-步骤"><a href="#5-1-1-步骤" class="headerlink" title="5.1.1. 步骤"></a>5.1.1. 步骤</h3><ul><li>猜测解的形式</li><li>用数学归纳法找出常数</li></ul><p><a id="markdown-512-例子" name="512-例子"></a></p><h3 id="5-1-2-例子"><a href="#5-1-2-例子" class="headerlink" title="5.1.2. 例子"></a>5.1.2. 例子</h3><p>$T(n) = 2T(\frac{n} {2})+n$<br>猜测$T(n) = O(nlogn)$<br>证明 $ T(n)\leqslant cnlogn$<br>归纳奠基 n=2,3<br>归纳假设 $T(\frac{n} {2}) \leqslant \frac{cn}{2}$<br>递归<br>$<br>\begin{aligned}<br>T(n) &amp;\leqslant  2c\frac{n}{2}log(\frac{n}{2}) + n \leqslant cnlog(\frac{n}{2})  \\<br>\end{aligned}<br>$</p><p><a id="markdown-513-放缩" name="513-放缩"></a></p><h3 id="5-1-3-放缩"><a href="#5-1-3-放缩" class="headerlink" title="5.1.3. 放缩"></a>5.1.3. 放缩</h3><p>对于 $T(n) = 2T(\frac{cn}{2}) + 1$<br>如果 直接猜测 $T(n) =  O (n)$ 不能证明,<br>而且不要猜测更高的界 $O (n^2)$<br>可以放缩为 n-b</p><p><a id="markdown-514-改变变量" name="514-改变变量"></a></p><h3 id="5-1-4-改变变量"><a href="#5-1-4-改变变量" class="headerlink" title="5.1.4. 改变变量"></a>5.1.4. 改变变量</h3><p>对于 $ T(n) = 2T(\sqrt{n})+logn $<br>可以 令 <code>m = logn</code>, 得到<br>$T(2^m) = 2T(m^{\frac{m}{2}}) + m $<br>令 $S(m) = T(2^m)$<br>得到 $ S(m) = 2S(\frac{m}{2}) + m $</p><script type="math/tex; mode=display">T(n)=T(2^m)=S(m)=\Theta(m\log m)=\Theta(\log n \log^2 n)</script><p><a id="markdown-52-递归树" name="52-递归树"></a></p><h2 id="5-2-递归树"><a href="#5-2-递归树" class="headerlink" title="5.2. 递归树"></a>5.2. 递归树</h2><p>例如 $T(n) = 3T(\frac{n}{4}) + c n^2$<br>不妨假设 n 为4的幂, 则有如下递归树<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/recursive-tree.jpg.png" alt="recursive-tree.jpg"></p><script type="math/tex; mode=display">T(n) = \sum_{i=0}^{ {\log_4 n}-1}cn^2*(\frac{3}{16})^i + \Theta(n^{\log4 3})</script><p>每个结点是代价, 将每层加起来即可</p><p><a id="markdown-53-主方法master-method" name="53-主方法master-method"></a></p><h2 id="5-3-主方法-master-method"><a href="#5-3-主方法-master-method" class="headerlink" title="5.3. 主方法(master method)"></a>5.3. 主方法(master method)</h2><p>对于 $T(n) = aT(\frac{n} {b})+f(n)$</p><script type="math/tex; mode=display">\begin{aligned}T(n)=\begin{cases}\Theta(n^{log_b a}),\quad f(n)=O(n^{ {log_b a}-\epsilon})   \\\Theta(n^{log_b a}logn),\quad  f(n)=\Theta(n^{log_b a})   \\\Theta(f(n)),\quad f(n)=\Omega(n^{ {log_b a}+ \epsilon}),af(\frac{n}{b})\leqslant cf(n)  \\\qquad \qquad \quad  \text{其中常数c<1,变量n任意大}    \\unknown, \quad others\end{cases}\end{aligned}</script><p><a id="markdown-531-记忆" name="531-记忆"></a></p><h3 id="5-3-1-记忆"><a href="#5-3-1-记忆" class="headerlink" title="5.3.1. 记忆"></a>5.3.1. 记忆</h3><p>直观上, 比较 $n^{log_b a}$ 和 $f(n)$, 谁大就是谁,<br>相等的话就是 $\Theta(f(n))\log n$<br>这里的大是多项式上的比较, 即比较次数, 而不是渐近上的<br>比如 $n$ 与 $nlogn$ 渐近上后者大, 但多项式上是不能比较的</p><p><a id="markdown-532-证明" name="532-证明"></a></p><h3 id="5-3-2-证明"><a href="#5-3-2-证明" class="headerlink" title="5.3.2. 证明"></a>5.3.2. 证明</h3><p><a id="markdown-5321-证明当-n-为-b-的正合幂时成立" name="5321-证明当-n-为-b-的正合幂时成立"></a></p><h4 id="5-3-2-1-证明当-n-为-b-的正合幂时成立"><a href="#5-3-2-1-证明当-n-为-b-的正合幂时成立" class="headerlink" title="5.3.2.1. 证明当 n 为 b 的正合幂时成立"></a>5.3.2.1. 证明当 n 为 b 的正合幂时成立</h4><ul><li>用递归树可以得到 总代价为 $\sum_{j=0}^{log_b n-1} a^j f(\frac{n}{b^j})$</li><li>决定上式的渐近界</li><li>结合前两点</li></ul><p><a id="markdown-5322-分析扩展至所有正整数-n-都成立" name="5322-分析扩展至所有正整数-n-都成立"></a></p><h4 id="5-3-2-2-分析扩展至所有正整数-n-都成立"><a href="#5-3-2-2-分析扩展至所有正整数-n-都成立" class="headerlink" title="5.3.2.2. 分析扩展至所有正整数 n 都成立"></a>5.3.2.2. 分析扩展至所有正整数 n 都成立</h4><p>主要是应用数学技巧来解决 floor, ceiling 函数的处理问题</p><p><a id="markdown-6-随机算法" name="6-随机算法"></a></p><h1 id="6-随机算法"><a href="#6-随机算法" class="headerlink" title="6. 随机算法"></a>6. 随机算法</h1><p><a id="markdown-61-随机排列数组shuffle" name="61-随机排列数组shuffle"></a></p><h2 id="6-1-随机排列数组-shuffle"><a href="#6-1-随机排列数组-shuffle" class="headerlink" title="6.1. 随机排列数组(shuffle)"></a>6.1. 随机排列数组(shuffle)</h2><p><a id="markdown-611-permute-by-sorting" name="611-permute-by-sorting"></a></p><h3 id="6-1-1-PERMUTE-BY-SORTING"><a href="#6-1-1-PERMUTE-BY-SORTING" class="headerlink" title="6.1.1. PERMUTE-BY-SORTING"></a>6.1.1. PERMUTE-BY-SORTING</h3><p>给出初始数组, eg A={1,2,3}, 选择随机的优先级 P={16,4,10}<br>则得出 B={2,3,1},因为第二个(2)优先级最小, 为4, 接着第三个,最后第1个.<br>优先级数组的产生, 一般在 RANDOM(1,n^3), 这样优先级各不相同的概率至少为 1-1/n</p><p>由于要排序优先级数组, 所以时间复杂度 $O(nlogn)$</p><p>如果优先级唯一,  则此算法可以 shuffle 数组<br>应证明 同样排列的概率是 $\frac{1}{n!}$</p><p><a id="markdown-612-randomize-in-place" name="612-randomize-in-place"></a></p><h3 id="6-1-2-RANDOMIZE-IN-PLACE"><a href="#6-1-2-RANDOMIZE-IN-PLACE" class="headerlink" title="6.1.2. RANDOMIZE-IN-PLACE"></a>6.1.2. RANDOMIZE-IN-PLACE</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">myshuffle</span><span class="params">(arr)</span>:</span></span><br><span class="line">    n = len(arr)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">        p = randint(i,n<span class="number">-1</span>)</span><br><span class="line">        arr[i],arr[p] = arr[p],arr[i]</span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure><p>时间复杂度 $O(n)$<br>证明<br>定义循环不变式: 对每个可能的 $A_n^{i-1}$ 排列, 其在 arr[1..i-1] 中的概率为 $\frac{1}{A_n^{i-1}}$<br>初始化: i=1 成立<br>保持 : 假设 在第 i-1 次迭代之前,成立, 证明在第 i 次迭代之后, 仍然成立,<br>终止: 在 结束后, i=n+1, 得到 概率为 $\frac{1}{n!}$</p><p><a id="markdown-7-组合方程的近似算法" name="7-组合方程的近似算法"></a></p><h1 id="7-组合方程的近似算法"><a href="#7-组合方程的近似算法" class="headerlink" title="7. 组合方程的近似算法"></a>7. 组合方程的近似算法</h1><ul><li>Stiring’s approximation: $ n! \approx \sqrt{2\pi n}\left(\frac{n}{e}\right)^n$</li><li>对于 $C_n^x=a$, 有 $x=\frac{ln^2 a}{n}$</li><li>对于 $C_x^n=a$, 有 $x=(a*n!)^{\frac{1}{n}}+\frac{n}{2}$</li></ul><p><a id="markdown-8-概率分析与指示器变量例子" name="8-概率分析与指示器变量例子"></a></p><h1 id="8-概率分析与指示器变量例子"><a href="#8-概率分析与指示器变量例子" class="headerlink" title="8. 概率分析与指示器变量例子"></a>8. 概率分析与指示器变量例子</h1><p><a id="markdown-81-球与盒子" name="81-球与盒子"></a></p><h2 id="8-1-球与盒子"><a href="#8-1-球与盒子" class="headerlink" title="8.1. 球与盒子"></a>8.1. 球与盒子</h2><p>把相同的秋随机投到 b 个盒子里,问在每个盒子里至少有一个球之前,平均至少要投多少个球?<br>称投入一个空盒为击中, 即求取得 b 次击中的概率<br>设投 n 次, 称第 i 个阶段包括第 i-1 次击中到 第 i 次击中的球, 则第 i 次击中的概率为 $p_i=\frac{b-i+1}{b}$<br>用 $n_i$表示第 i 阶段的投球数,则 $n=\sum_{i=1}^b n_i$<br>且 $n_i$服从几何分布, $E(n_i)=\frac{b}{b-i+1}$,<br>则由期望的线性性, </p><script type="math/tex; mode=display">E(n)=E(\sum_{i=1}^b n_i)=\sum_{i=1}^b E(n_i)=\sum_{i=1}^b \frac{b}{b-i+1}=b\sum_{i=1}^b \frac{1}{i}=b(lnb+O(1))</script><p>这个问题又被称为 赠券收集者问题(coupon collector’s problem),即集齐 b 种不同的赠券,在随机情况下平均需要买 blnb 张<br><a id="markdown-82-序列" name="82-序列"></a></p><h2 id="8-2-序列"><a href="#8-2-序列" class="headerlink" title="8.2. 序列"></a>8.2. 序列</h2><p>抛 n 次硬币, 期望看到的连续正面的次数<br>答案是 $\Theta(logn)$<br>记 长度至少为 k 的正面序列开始与第 i 次抛, 由于独立, 所有 k 次抛掷都是正面的 概率为<br>$P(A_{ik})=\frac{1}{2^k}$,对于 $k=2\lceil lgn\rceil$<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/coin1.jpg.png" alt="coin1.jpg"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/coin2.jpg.png" alt="coin2.jpg"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/coin3.jpg.png" alt="coin3.jpg"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/coin4.jpg.png" alt="coin4.jpg"></p><p><a id="markdown-9-摊还分析" name="9-摊还分析"></a></p><h1 id="9-摊还分析"><a href="#9-摊还分析" class="headerlink" title="9. 摊还分析"></a>9. 摊还分析</h1><p><a id="markdown-91-聚合分析aggregate-analysis" name="91-聚合分析aggregate-analysis"></a></p><h2 id="9-1-聚合分析-aggregate-analysis"><a href="#9-1-聚合分析-aggregate-analysis" class="headerlink" title="9.1. 聚合分析(aggregate analysis)"></a>9.1. 聚合分析(aggregate analysis)</h2><p> 一个 n 个操作的序列最坏情况下花费的总时间为$T(n)$, 则在最坏情况下, 每个操作的摊还代价为 $\frac{T(n)}{n}$</p><p>如栈中的 push, pop 操作都是 $O(1)$, 增加一个新操作 <code>multipop</code>,<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">multipop</span><span class="params">(stk,k)</span>:</span></span><br><span class="line">  <span class="keyword">while</span> <span class="keyword">not</span> stk.empty() <span class="keyword">and</span> k&gt;<span class="number">0</span>:</span><br><span class="line">    stk.pop()</span><br><span class="line">    k-=<span class="number">1</span></span><br></pre></td></tr></table></figure></p><p>multipop 的时间复杂度为 min(stk.size,k), 最坏情况为 $O(n)$, 则 n 个包含 push pop multipop 的操作列的最坏情况是 $O(n^2)$, 并不是这样, 注意到, 必须栈中有元素, 再 pop, 所以 push 操作与pop 操作(包含 multipop中的pop), 个数相当, 所以 实际上应为 $O(n)$, 每个操作的摊还代价 为$O(1)$</p><p><a id="markdown-92-核算法-accounting-method" name="92-核算法-accounting-method"></a></p><h2 id="9-2-核算法-accounting-method"><a href="#9-2-核算法-accounting-method" class="headerlink" title="9.2. 核算法 (accounting method)"></a>9.2. 核算法 (accounting method)</h2><p>对不同操作赋予不同费用 cost (称为摊还代价 $c_i’$), 可能多于或者少于其实际代价 $c_i$ </p><p>当 $c_i’&gt;c_i$, 将  $c_i’-c_i$( <code>credit</code>) 存入数据结构中的特定对象.. 对于后续 $c_i’&lt;c_i$时, 可以使用这些credit来 支付差额.. 有要求 </p><script type="math/tex; mode=display">\sum_{i}c_i' \geqslant \sum_{i}c_i</script><p>如栈</p><div class="table-container"><table><thead><tr><th style="text-align:center">op</th><th style="text-align:center">$c_i’$</th><th style="text-align:center">$c_i$</th></tr></thead><tbody><tr><td style="text-align:center">push</td><td style="text-align:center">2</td><td style="text-align:center">1</td></tr><tr><td style="text-align:center">pop</td><td style="text-align:center">0</td><td style="text-align:center">1</td></tr><tr><td style="text-align:center">multipop</td><td style="text-align:center">0</td><td style="text-align:center">min(s,k)</td></tr></tbody></table></div><p>由核算法, 摊还代价满足要求,  所以 n 个操作总代价 $O(n)$, 每个操作摊还代价为 $O(1)$</p><p><a id="markdown-93-势能法potential-method" name="93-势能法potential-method"></a></p><h2 id="9-3-势能法-potential-method"><a href="#9-3-势能法-potential-method" class="headerlink" title="9.3. 势能法(potential method)"></a>9.3. 势能法(potential method)</h2><p>势能释放用来支付未来操作的代价, 势能是整个数据结构的, 不是特定对象的(核算法是).</p><p>数据结构 $D_0$为初始状态, 依次 执行 n 个操作 $op_i$进行势能转换 $D_i =op_i(D_{i-1}), i=1,2,\ldots,n$ , 各操作代价为 $c_i$</p><p>势函数 $\Phi:D_i\rightarrow R$, $\Phi(D_i)$即为 $D_i$的势</p><p>则第 i 个操作的摊还代价 </p><script type="math/tex; mode=display">c_i'=c_i+\Phi(D_i)-\Phi(D_{i-1})</script><p>则</p><script type="math/tex; mode=display">\sum_{i=1}^{n}c_i'=\sum_{i=1}^{n}c_i+\Phi(D_n)-\Phi(D_0)</script><p>如果定义一个势函数$\Phi, st \ \Phi(D_i)\geqslant\Phi(D_0)$, 则总摊还代价给出了实际代价的一个上界<br>可以简单地以 $D_0 \text{为参考状态}, then \ \Phi(D_0)=0$</p><p>例如栈操作,<br>设空栈为 $D_0$, 势函数定义为栈的元素数<br>对于push, $ \Phi(D_i)-\Phi(D_{i-1})=1$<br>则 $c’ = c +\Phi(D_i)-\Phi(D_{i-1}) = c+1 = 2$</p><p>对于 multipop,  $ \Phi(D_i)-\Phi(D_{i-1})=- min(k,s)$<br>则 $c’ = c - min(k,s) = 0$</p><p>同理 pop  的摊还代价也是0, 则总摊还代价的上界(最坏情况) 为 $O(n)$</p>]]></content>
    
    <summary type="html">
    
      概述, 包括基本的算法分析与设计方法, (递归式, 递归树, 主方法), 以及随机算法, 概率分析, 摊还分析等
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="https://mbinary.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>『数据结构』红黑树(red-black tree)</title>
    <link href="https://mbinary.xyz/red-black-tree.html"/>
    <id>https://mbinary.xyz/red-black-tree.html</id>
    <published>2018-07-12T19:58:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-定义与性质">1. 定义与性质</a><ul><li><a href="#11-数据域">1.1. 数据域</a></li><li><a href="#12-红黑性质">1.2. 红黑性质</a></li><li><a href="#13-黑高度">1.3. 黑高度</a></li></ul></li><li><a href="#2-旋转">2. 旋转</a></li><li><a href="#3-插入">3. 插入</a><ul><li><a href="#31-二叉查找树的插入">3.1. 二叉查找树的插入</a></li><li><a href="#32-颜色调整与旋转">3.2. 颜色调整与旋转</a><ul><li><a href="#321-问题">3.2.1. 问题</a></li><li><a href="#322-情况">3.2.2. 情况</a><ul><li><a href="#3221-case1--x-的叔叔是红色的">3.2.2.1. case1:  x 的叔叔是红色的</a></li><li><a href="#3222-case2-x-的叔叔是黑色-xpx-ppx方向为-left-right-或者-right-left">3.2.2.2. case2: x 的叔叔是黑色, x,p(x), p(p(x)),方向为 left-right 或者 right-left</a></li><li><a href="#3223-case3-x-的叔叔是黑色-xpx-ppx方向为-left-left-或者-right-right">3.2.2.3. case3: x 的叔叔是黑色, x,p(x), p(p(x)),方向为 left-left 或者 right-right</a></li></ul></li><li><a href="#323-总体解决方案">3.2.3. 总体解决方案</a></li></ul></li></ul></li><li><a href="#4-删除">4. 删除</a><ul><li><a href="#41-二叉查找树删除结点">4.1. 二叉查找树删除结点</a></li><li><a href="#42-调整颜色与旋转">4.2. 调整颜色与旋转</a></li></ul></li><li><a href="#5-数据结构的扩张">5. 数据结构的扩张</a><ul><li><a href="#51-平衡树的扩张">5.1. 平衡树的扩张</a></li></ul></li><li><a href="#6-python-代码">6. python 代码</a></li><li><a href="#7-参考">7. 参考</a></li></ul><!-- /TOC --><p><a id="markdown-1-定义与性质" name="1-定义与性质"></a></p><h1 id="1-定义与性质"><a href="#1-定义与性质" class="headerlink" title="1. 定义与性质"></a>1. 定义与性质</h1><p>红黑树是一种平衡的二叉查找树<br><a id="markdown-11-数据域" name="11-数据域"></a></p><h2 id="1-1-数据域"><a href="#1-1-数据域" class="headerlink" title="1.1. 数据域"></a>1.1. 数据域</h2><p>每个结点有 5 个数据域 </p><ul><li>color: red or black</li><li>key: keyword</li><li>left: pointer to left child</li><li>right:pointer to right child</li><li>p: pointer to nil leaf</li></ul><p><a id="markdown-12-红黑性质" name="12-红黑性质"></a></p><h2 id="1-2-红黑性质"><a href="#1-2-红黑性质" class="headerlink" title="1.2. 红黑性质"></a>1.2. 红黑性质</h2><p>满足下面的 <code>红黑性质</code> 的二叉查找树就是红黑树:</p><ul><li>每个结点或是红色或是黑色</li><li>根是黑</li><li>nil leaf 是 黑</li><li>红结点的孩子是黑</li><li>从每个结点出发,通过子孙到达叶子结点的各条路径上 黑结点数相等</li></ul><p>如,叶子结点 是 nil, 即不存储任何东西, 为了编程方便,相对的,存有数据的结点称为内结点<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-1.png" alt="red-black-tree-1"></p><p>为了节省空间, 可以如下实现, 只需要一个 nil 结点<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/nil leaf.png" alt="nil leaf"></p><p><a id="markdown-13-黑高度" name="13-黑高度"></a></p><h2 id="1-3-黑高度"><a href="#1-3-黑高度" class="headerlink" title="1.3. 黑高度"></a>1.3. 黑高度</h2><p>从某个结点 x 到叶结点的黑色结点数,称为此结点的黑高度, 记为 $h_b(x)$<br>树的黑高度是根的黑高度</p><blockquote><ol><li>以 x 为 根的子树至少包含 $2^{h_b(x)}-1$个结点</li><li>一颗有 n 个内结点的红黑树高度至多为$2lg(n+1)$</li></ol></blockquote><p>可用归纳法证明1<br>证明 2:<br>设树高 h<br>由红黑性质4, 根结点到叶子路径上的黑结点数至少 $\frac{h}{2}$,即 $h_b(root)\geqslant \frac{h}{2}$<br>再由1, </p><script type="math/tex; mode=display">n \geqslant 2^{h_b(x)} -1 \geqslant 2^{\frac{h}{2}} -1</script><p>即 $ h\leqslant 2lg(n+1)$</p><p><a id="markdown-2-旋转" name="2-旋转"></a></p><h1 id="2-旋转"><a href="#2-旋转" class="headerlink" title="2. 旋转"></a>2. 旋转</h1><p>由于上面证明的红黑树高为 $O(logn)$,红黑树的 insert, delete, search 等操作都是, $O(logn)$.<br>进行了 insert, delete 后可能破坏红黑性质, 可以通过旋转来保持.</p><p>下面是对结点 x 进行 左旋与右旋.<br>注意进行左旋时, 右孩子不是 nil(要用来作为旋转后 x 的双亲), 同理 右旋的结点的左孩子不是nil<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/左旋与右旋.png" alt="左旋与右旋"><br>总结起来就是: 父亲旋转,顺时针就是右旋,逆时针就是左旋, 旋转的结果是儿子成为原来父亲的新父亲, 即旋转的结点下降一层, 它的一个儿子上升一层.</p><p><a id="markdown-3-插入" name="3-插入"></a></p><h1 id="3-插入"><a href="#3-插入" class="headerlink" title="3. 插入"></a>3. 插入</h1><p>插入的过程: </p><ul><li>先同二叉查找树那样插入, 做为叶子(不为空)</li><li>然后将新结点的 左右孩子设为 nil , 颜色设为红色</li><li>最后再进行颜色调整以及旋转(维持红黑性质)</li></ul><p>这是算法导论<sup><a href="#fn_1" id="reffn_1">1</a></sup>上的算法<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">RB-INSERT(T, z)  </span><br><span class="line"> y ← nil[T]                        // 新建节点“y”，将y设为空节点。</span><br><span class="line"> x ← root[T]                       // 设“红黑树T”的根节点为“x”</span><br><span class="line"> <span class="keyword">while</span> x ≠ nil[T]                  // 找出要插入的节点“z”在二叉树T中的位置“y”</span><br><span class="line">     do y ← x                      </span><br><span class="line">        <span class="keyword">if</span> key[z] &lt; key[x]  </span><br><span class="line">           then x ← left[x]  </span><br><span class="line">           <span class="keyword">else</span> x ← right[x]  </span><br><span class="line"> p[z] ← y                          // 设置 “z的父亲” 为 “y”</span><br><span class="line"> <span class="keyword">if</span> y = nil[T]                     </span><br><span class="line">    then root[T] ← z               // 情况<span class="number">1</span>：若y是空节点，则将z设为根</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> key[z] &lt; key[y]        </span><br><span class="line">            then left[y] ← z       // 情况<span class="number">2</span>：若“z所包含的值” &lt; “y所包含的值”，则将z设为“y的左孩子”</span><br><span class="line">            <span class="keyword">else</span> right[y] ← z      // 情况<span class="number">3</span>：(“z所包含的值” &gt;= “y所包含的值”)将z设为“y的右孩子” </span><br><span class="line"> left[z] ← nil[T]                  // z的左孩子设为空</span><br><span class="line"> right[z] ← nil[T]                 // z的右孩子设为空。至此，已经完成将“节点z插入到二叉树”中了。</span><br><span class="line"> color[z] ← RED                    // 将z着色为“红色”</span><br><span class="line"> RB-INSERT-FIXUP(T, z)             // 通过RB-INSERT-FIXUP对红黑树的节点进行颜色修改以及旋转，让树T仍然是一颗红黑树</span><br></pre></td></tr></table></figure></p><p><a id="markdown-31-二叉查找树的插入" name="31-二叉查找树的插入"></a></p><h2 id="3-1-二叉查找树的插入"><a href="#3-1-二叉查找树的插入" class="headerlink" title="3.1. 二叉查找树的插入"></a>3.1. 二叉查找树的插入</h2><p>可以用python 实现如下<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">    <span class="keyword">if</span>  <span class="keyword">not</span> isinstance(nd,node):</span><br><span class="line">        nd = node(nd)</span><br><span class="line">    <span class="keyword">elif</span> nd.isBlack: nd.isBlack = <span class="keyword">False</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> self.root <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">        self.root = nd</span><br><span class="line">        self.root.isBlack = <span class="keyword">True</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        parent = self.root</span><br><span class="line">        <span class="keyword">while</span> parent:</span><br><span class="line">            <span class="keyword">if</span> parent == nd : <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">            <span class="keyword">if</span> parent&gt;nd:</span><br><span class="line">                <span class="keyword">if</span> parent.left :</span><br><span class="line">                    parent = parent.left</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    parent.left  = nd</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">if</span> parent.right:</span><br><span class="line">                    parent = parent.right</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    parent.right = nd</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">        self.fixUpInsert(parent,nd)</span><br></pre></td></tr></table></figure></p><p><a id="markdown-32-颜色调整与旋转" name="32-颜色调整与旋转"></a></p><h2 id="3-2-颜色调整与旋转"><a href="#3-2-颜色调整与旋转" class="headerlink" title="3.2. 颜色调整与旋转"></a>3.2. 颜色调整与旋转</h2><p><a id="markdown-321-问题" name="321-问题"></a></p><h3 id="3-2-1-问题"><a href="#3-2-1-问题" class="headerlink" title="3.2.1. 问题"></a>3.2.1. 问题</h3><p>在插入后,可以发现后破坏的红黑性质只有以下两条(且互斥)</p><ol><li>root 是红 (这可以直接将root 颜色设为黑调整)</li><li>红结点的孩子是黑</li></ol><p>所以下面介绍如何保持 红结点的孩子是黑 , 即插入结点的双亲结点是红的情况.</p><p>下面记  结点 x 的 双亲为 p(x), 新插入的结点为 x, 记 uncle 结点 为 u(x)</p><p>由于 p(x) 是红色,  而根结点是黑色, 所以 p(x)不是根, p(p(x))存在<br><a id="markdown-322-情况" name="322-情况"></a></p><h3 id="3-2-2-情况"><a href="#3-2-2-情况" class="headerlink" title="3.2.2. 情况"></a>3.2.2. 情况</h3><p>有如下三种情况</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-2.png" alt="red-black-tree-2"></p><p>每种情况的解决方案如下</p><p><a id="markdown-3221-case1--x-的叔叔是红色的" name="3221-case1--x-的叔叔是红色的"></a></p><h4 id="3-2-2-1-case1-x-的叔叔是红色的"><a href="#3-2-2-1-case1-x-的叔叔是红色的" class="headerlink" title="3.2.2.1. case1:  x 的叔叔是红色的"></a>3.2.2.1. case1:  x 的叔叔是红色的</h4><p>这里只需改变颜色, 将 p(x)变为 黑, p(p(x))变为红, u(x) 变为黑色 (x为右孩子同样)<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-3.png" alt="red-black-tree-3"></p><p><a id="markdown-3222-case2-x-的叔叔是黑色-xpx-ppx方向为-left-right-或者-right-left" name="3222-case2-x-的叔叔是黑色-xpx-ppx方向为-left-right-或者-right-left"></a></p><h4 id="3-2-2-2-case2-x-的叔叔是黑色-x-p-x-p-p-x-方向为-left-right-或者-right-left"><a href="#3-2-2-2-case2-x-的叔叔是黑色-x-p-x-p-p-x-方向为-left-right-或者-right-left" class="headerlink" title="3.2.2.2. case2: x 的叔叔是黑色, x,p(x), p(p(x)),方向为 left-right 或者 right-left"></a>3.2.2.2. case2: x 的叔叔是黑色, x,p(x), p(p(x)),方向为 left-right 或者 right-left</h4><p>即 x,p(x), p(p(x)) 成折线状<br><a id="markdown-3223-case3-x-的叔叔是黑色-xpx-ppx方向为-left-left-或者-right-right" name="3223-case3-x-的叔叔是黑色-xpx-ppx方向为-left-left-或者-right-right"></a></p><h4 id="3-2-2-3-case3-x-的叔叔是黑色-x-p-x-p-p-x-方向为-left-left-或者-right-right"><a href="#3-2-2-3-case3-x-的叔叔是黑色-x-p-x-p-p-x-方向为-left-left-或者-right-right" class="headerlink" title="3.2.2.3. case3: x 的叔叔是黑色, x,p(x), p(p(x)),方向为 left-left 或者 right-right"></a>3.2.2.3. case3: x 的叔叔是黑色, x,p(x), p(p(x)),方向为 left-left 或者 right-right</h4><p>即 x,p(x), p(p(x)) 成直线状</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-4.png" alt="red-black-tree-4"></p><p>当 x 为右孩子时, 通过旋转变成p(x) 的双亲, 然后相当于 新插入 p(x)作为左孩子, 再进行转换.</p><p>即将新结点的双亲向上一层旋转,颜色变为黑色, 而新节点的祖父向下一层, 颜色变为红色</p><p><a id="markdown-323-总体解决方案" name="323-总体解决方案"></a></p><h3 id="3-2-3-总体解决方案"><a href="#3-2-3-总体解决方案" class="headerlink" title="3.2.3. 总体解决方案"></a>3.2.3. 总体解决方案</h3><p>我最开始也没有弄清楚, 有点绕晕的感觉, 后来仔细读了书上伪代码, 然后才发现就是一个状态机, 画出来就一目了然了.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-5.png" alt="red-black-tree-5"></p><p>现在算是知其然了, 那么怎样知其所以然呢? 即 为什么要分类这三个 case, 不重不漏了吗?</p><p>其实也简单, 只是太繁琐.<br>就是将各种情况枚举出来, 一一分析即可. 我最开始试过, 但是太多,写在代码里很容易写着写着就混了.<br>而算法导论上分成这三个case , 很简洁, 只是归纳了一下而已. 如果想看看枚举情况的图与说明,可以参考<sup><a href="#fn_2" id="reffn_2">2</a></sup> .</p><p>算法导论上的伪代码<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">RB-INSERT-FIXUP(T, z)</span><br><span class="line"><span class="keyword">while</span> color[p[z]] = RED                                                  // 若“当前节点(z)的父节点是红色”，则进行以下处理。</span><br><span class="line">    do <span class="keyword">if</span> p[z] = left[p[p[z]]]                                           // 若“z的父节点”是“z的祖父节点的左孩子”，则进行以下处理。</span><br><span class="line">          then y ← right[p[p[z]]]                                        // 将y设置为“z的叔叔节点(z的祖父节点的右孩子)”</span><br><span class="line">               <span class="keyword">if</span> color[y] = RED                                         // Case <span class="number">1</span>条件：叔叔是红色</span><br><span class="line">                  then color[p[z]] ← BLACK                    ▹ Case <span class="number">1</span>   //  (<span class="number">01</span>) 将“父节点”设为黑色。</span><br><span class="line">                       color[y] ← BLACK                       ▹ Case <span class="number">1</span>   //  (<span class="number">02</span>) 将“叔叔节点”设为黑色。</span><br><span class="line">                       color[p[p[z]]] ← RED                   ▹ Case <span class="number">1</span>   //  (<span class="number">03</span>) 将“祖父节点”设为“红色”。</span><br><span class="line">                       z ← p[p[z]]                            ▹ Case <span class="number">1</span>   //  (<span class="number">04</span>) 将“祖父节点”设为“当前节点”(红色节点)</span><br><span class="line">                  <span class="keyword">else</span> <span class="keyword">if</span> z = right[p[z]]                                // Case <span class="number">2</span>条件：叔叔是黑色，且当前节点是右孩子</span><br><span class="line">                          then z ← p[z]                       ▹ Case <span class="number">2</span>   //  (<span class="number">01</span>) 将“父节点”作为“新的当前节点”。</span><br><span class="line">                               LEFT-ROTATE(T, z)              ▹ Case <span class="number">2</span>   //  (<span class="number">02</span>) 以“新的当前节点”为支点进行左旋。</span><br><span class="line">                          color[p[z]] ← BLACK                 ▹ Case <span class="number">3</span>   // Case <span class="number">3</span>条件：叔叔是黑色，且当前节点是左孩子。(<span class="number">01</span>) 将“父节点”设为“黑色”。</span><br><span class="line">                          color[p[p[z]]] ← RED                ▹ Case <span class="number">3</span>   //  (<span class="number">02</span>) 将“祖父节点”设为“红色”。</span><br><span class="line">                          RIGHT-ROTATE(T, p[p[z]])            ▹ Case <span class="number">3</span>   //  (<span class="number">03</span>) 以“祖父节点”为支点进行右旋。</span><br><span class="line">       <span class="keyword">else</span> (same <span class="keyword">as</span> then clause <span class="keyword">with</span> <span class="string">"right"</span> <span class="keyword">and</span> <span class="string">"left"</span> exchanged)      // 若“z的父节点”是“z的祖父节点的右孩子”，将上面的操作中“right”和“left”交换位置，然后依次执行。</span><br><span class="line">color[root[T]] ← BLACK</span><br></pre></td></tr></table></figure></p><p>我用python 实现如下. 由于左右方向不同, 如果向上面伪代码那样实现, fixup 代码就会有两份类似的(即 right left 互换),  为了减少代码冗余, 我就定义了 <code>setChild</code>, <code>getChild</code> 函数, 传递左或是右孩子这个方向的数据(代码中是isLeft), 所以下面的就是完整功能的 fixup, 可以减少一般的代码量, haha😄,<br>(下文 删除结点同理)</p><p>其实阅读代码也简单, 可以直接当成 isLeft 取真值.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fixUpInsert</span><span class="params">(self,parent,nd)</span>:</span></span><br><span class="line">    <span class="string">''' adjust color and level,  there are two red nodes: the new one and its parent'''</span></span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">not</span> self.checkBlack(parent):</span><br><span class="line">        grand = self.getParent(parent)</span><br><span class="line">        isLeftPrt = grand.left <span class="keyword">is</span> parent </span><br><span class="line">        uncle = grand.getChild(<span class="keyword">not</span> isLeftPrt)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.checkBlack(uncle):</span><br><span class="line">            <span class="comment"># case 1:  new node's uncle is red</span></span><br><span class="line">            self.setBlack(grand, <span class="keyword">False</span>)</span><br><span class="line">            self.setBlack(grand.left, <span class="keyword">True</span>)</span><br><span class="line">            self.setBlack(grand.right, <span class="keyword">True</span>)</span><br><span class="line">            nd = grand</span><br><span class="line">            parent = self.getParent(nd)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="comment"># case 2: new node's uncle is black(including nil leaf)</span></span><br><span class="line">            isLeftNode = parent.left <span class="keyword">is</span> nd</span><br><span class="line">            <span class="keyword">if</span> isLeftNode ^ isLeftPrt:</span><br><span class="line">                <span class="comment"># case 2.1 the new node is inserted in left-right or right-left form</span></span><br><span class="line">                <span class="comment">#         grand               grand</span></span><br><span class="line">                <span class="comment">#     parent        or            parent</span></span><br><span class="line">                <span class="comment">#          nd                   nd</span></span><br><span class="line">                parent.setChild(nd.getChild(isLeftPrt),<span class="keyword">not</span> isLeftPrt)</span><br><span class="line">                nd.setChild(parent,isLeftPrt)</span><br><span class="line">                grand.setChild(nd,isLeftPrt)</span><br><span class="line">                nd,parent = parent,nd</span><br><span class="line">            <span class="comment"># case 2.2 the new node is inserted in left-left or right-right form</span></span><br><span class="line">            <span class="comment">#         grand               grand</span></span><br><span class="line">            <span class="comment">#      parent        or            parent</span></span><br><span class="line">            <span class="comment">#     nd                                nd</span></span><br><span class="line">            grand.setChild(parent.getChild(<span class="keyword">not</span> isLeftPrt),isLeftPrt)</span><br><span class="line">            parent.setChild(grand,<span class="keyword">not</span> isLeftPrt)</span><br><span class="line">            self.setBlack(grand, <span class="keyword">False</span>)</span><br><span class="line">            self.setBlack(parent, <span class="keyword">True</span>)</span><br><span class="line">            self.transferParent(grand,parent)</span><br><span class="line">    self.setBlack(self.root,<span class="keyword">True</span>)</span><br></pre></td></tr></table></figure></p><p><a id="markdown-4-删除" name="4-删除"></a></p><h1 id="4-删除"><a href="#4-删除" class="headerlink" title="4. 删除"></a>4. 删除</h1><p>算法导论上的算法</p><p>写的很简练👍<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/rb-delete.png" alt="rb-delete"></p><p><a id="markdown-41-二叉查找树删除结点" name="41-二叉查找树删除结点"></a></p><h2 id="4-1-二叉查找树删除结点"><a href="#4-1-二叉查找树删除结点" class="headerlink" title="4.1. 二叉查找树删除结点"></a>4.1. 二叉查找树删除结点</h2><p>下面  z 是要删除的结点,  y 是 其后继或者是它自己, x 是 y 的一个孩子(如果 y 的孩子为 nil,则为 nli, 否则 y 只有一个非 nil 孩子, 为 x)   </p><ul><li>当 z 孩子全是 nil (y==z): 直接让其双亲对应的孩子为 nil</li><li>当 z 只有一个非 nil 孩子 x  (y==z): <ol><li>如果 z 为根, 则让 x 为根.   </li><li>让 y 的双亲连接到 x</li></ol></li><li>当 z 有两个非nil孩子(y!=z): 复制其后继 y 的内容到 z (除了指针,颜色) ,  将其后继 y 的孩子(最多只有一个 非 nil ,不然就不是后继了)连接到其后继的双亲, 删除 其后继y, </li></ul><p>即<sup><a href="#fn_3" id="reffn_3">3</a></sup>  如果要删除有两个孩子的结点 z , 则找到它的后继y(前趋同理), 可以推断 y 一定没有左孩子, 右孩子可能有,可能没有. 也就是最多一个孩子.<br>所以将 y 的值复制到 x 位置, 现在相当于删除 y 处的结点.<br>这样就化为 删除的结点最多一个孩子的情况.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-6.png" alt="red-black-tree-6"></p><p><a id="markdown-42-调整颜色与旋转" name="42-调整颜色与旋转"></a></p><h2 id="4-2-调整颜色与旋转"><a href="#4-2-调整颜色与旋转" class="headerlink" title="4.2. 调整颜色与旋转"></a>4.2. 调整颜色与旋转</h2><p> 可以发现只有当 y 是黑色,才进行颜色调整以及旋转(维持红黑性质), 因为如果删除的是红色, 不会影响黑高度, 所有红黑性质都不会破坏<br>伪代码如下, (我的python代码见文末)<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-7.png" alt="red-black-tree-7"></p><p>如果被删除的结点 y 是黑色的, 有三种破坏红黑性质的情况</p><ol><li>y是根, 则 y 的一个红色孩子成为新根</li><li>进行删除结点过程中, p(y) 的孩子有 x, 两者都是红色</li><li>删除 y 导致包含y 的路径上的黑结点 少 1个</li></ol><p>修复3的思路:<br>如果可能,在兄弟一支,通过旋转,改变颜色修复<br>否则, 将红结点一直向上推(因为当前路径上少了一个黑结点,向上推的过程中使红结点所在的子树都少一个黑结点), 直到到达树根, 那么全部路径都少一个黑结点, 3就修复了, 这时只需将根设为黑就修复了  1</p><p>代码中的 while 循环的目的是将额外的黑色沿树上移,直到</p><ul><li>x 指向一个红黑结点</li><li>x 指向根,这时可以简单地消除额外的黑色</li><li>颜色修改与旋转</li></ul><p>在 while 中, x 总是指向具有双重黑色的那个非根结点, 在第 2 行中要判断 x 是其双亲的左右孩子<br>w 表示 x 的相抵. w 不能为 nil(因为 x 是双重黑色)</p><p>算法中的四种情况如图所示<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-8.png" alt="red-black-tree-8"></p><p>即</p><ul><li>x 的兄弟 w 是红色的<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-9.png" alt="red-black-tree-9"></li><li><p>x 的兄弟 w 是黑色的, w的两个孩子都是黑色的</p></li><li><p>x 的兄弟 w 是黑色的, w 的左孩子是红,右孩子是黑</p></li><li>x 的兄弟 w 是黑色的, w 的孩子是红色的</li></ul><blockquote><blockquote><p>注意上面都是先考虑的左边, 右边可以对称地处理.</p></blockquote></blockquote><p>同插入一样, 为了便于理解, 可以作出状态机.<br>而且这些情形都是归纳化简了的, 你也可以枚举列出基本的全部情形.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-10.png" alt="red-black-tree-10"></p><p><a id="markdown-5-数据结构的扩张" name="5-数据结构的扩张"></a></p><h1 id="5-数据结构的扩张"><a href="#5-数据结构的扩张" class="headerlink" title="5. 数据结构的扩张"></a>5. 数据结构的扩张</h1><p><a id="markdown-51-平衡树的扩张" name="51-平衡树的扩张"></a></p><h2 id="5-1-平衡树的扩张"><a href="#5-1-平衡树的扩张" class="headerlink" title="5.1. 平衡树的扩张"></a>5.1. 平衡树的扩张</h2><p>通过在平衡树(如红黑树上的每个结点 加上 一个数据域 size (表示以此结点为根的子树的结点数.) 可以使<code>获得第 i 大的数</code> 的时间复杂度为 $O(logn)$</p><p>在 $O(n)$ 时间内建立, python代码如下<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">setSize</span><span class="params">(root)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> root <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">    root.size = setSize(root.left) + setSize(root.right)+<span class="number">1</span></span><br></pre></td></tr></table></figure></p><p>在$O(logn)$时间查找,<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find</span><span class="params">(root,i)</span>:</span></span><br><span class="line">    r =  root.left.size +<span class="number">1</span></span><br><span class="line">    <span class="keyword">if</span> r==i:</span><br><span class="line">        <span class="keyword">return</span> root</span><br><span class="line">    <span class="keyword">if</span> r &gt; i:</span><br><span class="line">        <span class="keyword">return</span> find(root.left,i)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> find(root.right,i-r)</span><br></pre></td></tr></table></figure></p><p><a id="markdown-6-python-代码" name="6-python-代码"></a></p><h1 id="6-python-代码"><a href="#6-python-代码" class="headerlink" title="6. python 代码"></a>6. python 代码</h1><p><strong><a href="https://github.com/mbinary/algorithm.git" target="_blank" rel="noopener">github地址</a></strong></p><p>我用了 setChild, getChild 来简化代码量, 其他的基本上是按照算法导论上的伪代码提到的case 来实现的. 然后display 只是测试的时候,为了方便调试而层序遍历打印出来</p><p>效果如下<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/red-black-tree-11.png" alt="red-black-tree-11"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">''' mbinary</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string"># File : redBlackTree.py</span></span><br><span class="line"><span class="string"># Author: mbinary</span></span><br><span class="line"><span class="string"># Mail: zhuheqin1@gmail.com</span></span><br><span class="line"><span class="string"># Blog: https://mbinary.github.io</span></span><br><span class="line"><span class="string"># Github: https://github.com/mbinary</span></span><br><span class="line"><span class="string"># Created Time: 2018-07-14  16:15</span></span><br><span class="line"><span class="string"># Description:</span></span><br><span class="line"><span class="string">#########################################################################</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> total_ordering</span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint, shuffle</span><br><span class="line"></span><br><span class="line"><span class="meta">@total_ordering</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,val,left=None,right=None,isBlack=False)</span>:</span></span><br><span class="line">        self.val =val</span><br><span class="line">        self.left = left</span><br><span class="line">        self.right = right</span><br><span class="line">        self.parent= <span class="keyword">None</span></span><br><span class="line">        self.isBlack  = isBlack</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__lt__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val &lt; nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__eq__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">and</span> self.val == nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setChild</span><span class="params">(self,nd,isLeft)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> isLeft: self.left = nd</span><br><span class="line">        <span class="keyword">else</span>: self.right = nd</span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>: nd.parent = self</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getChild</span><span class="params">(self,isLeft)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> isLeft: <span class="keyword">return</span> self.left</span><br><span class="line">        <span class="keyword">else</span>: <span class="keyword">return</span> self.right</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        color = <span class="string">'B'</span> <span class="keyword">if</span> self.isBlack <span class="keyword">else</span> <span class="string">'R'</span></span><br><span class="line">        val = <span class="string">'-'</span> <span class="keyword">if</span> self.parent==<span class="keyword">None</span> <span class="keyword">else</span> self.parent.val</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'<span class="subst">&#123;color&#125;</span>-<span class="subst">&#123;self.val&#125;</span>'</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'node(<span class="subst">&#123;self.val&#125;</span>,isBlack=<span class="subst">&#123;self.isBlack&#125;</span>)'</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">redBlackTree</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,unique=False)</span>:</span></span><br><span class="line">        <span class="string">'''if unique is True, all node'vals are unique, else there may be equal vals'''</span></span><br><span class="line">        self.root = <span class="keyword">None</span></span><br><span class="line">        self.unique = unique</span><br><span class="line"></span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">checkBlack</span><span class="params">(nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> nd <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> nd.isBlack</span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setBlack</span><span class="params">(nd,isBlack)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">            <span class="keyword">if</span> isBlack <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> isBlack:</span><br><span class="line">                nd.isBlack = <span class="keyword">True</span></span><br><span class="line">            <span class="keyword">else</span>:nd.isBlack = <span class="keyword">False</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setRoot</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>: nd.parent=<span class="keyword">None</span></span><br><span class="line">        self.root= nd</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">find</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        <span class="keyword">while</span> nd:</span><br><span class="line">            <span class="keyword">if</span> nd.val ==val:</span><br><span class="line">                <span class="keyword">return</span> nd</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                nd = nd.getChild(nd.val&gt;val)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getSuccessor</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> nd:</span><br><span class="line">            <span class="keyword">if</span> nd.right:</span><br><span class="line">                nd = nd.right</span><br><span class="line">                <span class="keyword">while</span> nd.left:</span><br><span class="line">                    nd = nd.left</span><br><span class="line">                <span class="keyword">return</span> nd</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">while</span> nd.parent <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">and</span> nd.parent.right <span class="keyword">is</span> nd:</span><br><span class="line">                    nd = nd.parent</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">None</span> <span class="keyword">if</span> nd <span class="keyword">is</span> self.root <span class="keyword">else</span> nd.parent</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">rotate</span><span class="params">(self,prt,chd)</span>:</span></span><br><span class="line">        <span class="string">'''rotate prt with the center of chd'''</span></span><br><span class="line">        <span class="keyword">if</span> self.root <span class="keyword">is</span> prt:</span><br><span class="line">            self.setRoot(chd)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            prt.parent.setChild(chd, prt.parent.left <span class="keyword">is</span> prt)</span><br><span class="line">        isLeftChd = prt.left <span class="keyword">is</span> chd</span><br><span class="line">        prt.setChild(chd.getChild(<span class="keyword">not</span> isLeftChd), isLeftChd)</span><br><span class="line">        chd.setChild(prt,<span class="keyword">not</span> isLeftChd)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> nd.isBlack: nd.isBlack = <span class="keyword">False</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> self.root <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">            self.setRoot(nd)</span><br><span class="line">            self.root.isBlack = <span class="keyword">True</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            parent = self.root</span><br><span class="line">            <span class="keyword">while</span> parent:</span><br><span class="line">                <span class="keyword">if</span> parent == nd : <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">                isLeft = parent &gt; nd</span><br><span class="line">                chd  = parent.getChild(isLeft)</span><br><span class="line">                <span class="keyword">if</span> chd <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">                    parent.setChild(nd,isLeft)</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    parent = chd</span><br><span class="line">            self.fixUpInsert(parent,nd)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">fixUpInsert</span><span class="params">(self,parent,nd)</span>:</span></span><br><span class="line">        <span class="string">''' adjust color and level,  there are two red nodes: the new one and its parent'''</span></span><br><span class="line">        <span class="keyword">while</span> <span class="keyword">not</span> self.checkBlack(parent):</span><br><span class="line">            grand = parent.parent</span><br><span class="line">            isLeftPrt = grand.left <span class="keyword">is</span> parent</span><br><span class="line">            uncle = grand.getChild(<span class="keyword">not</span> isLeftPrt)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> self.checkBlack(uncle):</span><br><span class="line">                <span class="comment"># case 1:  new node's uncle is red</span></span><br><span class="line">                self.setBlack(grand, <span class="keyword">False</span>)</span><br><span class="line">                self.setBlack(grand.left, <span class="keyword">True</span>)</span><br><span class="line">                self.setBlack(grand.right, <span class="keyword">True</span>)</span><br><span class="line">                nd = grand</span><br><span class="line">                parent = nd.parent</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="comment"># case 2: new node's uncle is black(including nil leaf)</span></span><br><span class="line">                isLeftNode = parent.left <span class="keyword">is</span> nd</span><br><span class="line">                <span class="keyword">if</span> isLeftNode ^ isLeftPrt:</span><br><span class="line">                    <span class="comment"># case 2.1 the new node is inserted in left-right or right-left form</span></span><br><span class="line">                    <span class="comment">#         grand               grand</span></span><br><span class="line">                    <span class="comment">#     parent        or            parent</span></span><br><span class="line">                    <span class="comment">#          nd                   nd</span></span><br><span class="line">                    self.rotate(parent,nd)    <span class="comment">#parent rotate</span></span><br><span class="line">                    nd,parent = parent,nd</span><br><span class="line">                <span class="comment"># case 3  (case 2.2) the new node is inserted in left-left or right-right form</span></span><br><span class="line">                <span class="comment">#         grand               grand</span></span><br><span class="line">                <span class="comment">#      parent        or            parent</span></span><br><span class="line">                <span class="comment">#     nd                                nd</span></span><br><span class="line"></span><br><span class="line">                self.setBlack(grand, <span class="keyword">False</span>)</span><br><span class="line">                self.setBlack(parent, <span class="keyword">True</span>)</span><br><span class="line">                self.rotate(grand,parent)</span><br><span class="line">        self.setBlack(self.root,<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">copyNode</span><span class="params">(self,src,des)</span>:</span></span><br><span class="line">        <span class="string">'''when deleting a node which has two kids,</span></span><br><span class="line"><span class="string">            copy its succesor's data to his position</span></span><br><span class="line"><span class="string">            data exclude left, right , isBlack</span></span><br><span class="line"><span class="string">        '''</span></span><br><span class="line">        des.val = src.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        <span class="string">'''delete node in a binary search tree'''</span></span><br><span class="line">        <span class="keyword">if</span> isinstance(val,node): val = val.val</span><br><span class="line">        nd = self.find(val)</span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">None</span>: <span class="keyword">return</span></span><br><span class="line">        self._delete(nd)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_delete</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        y = <span class="keyword">None</span></span><br><span class="line">        <span class="keyword">if</span> nd.left <span class="keyword">and</span> nd.right:</span><br><span class="line">            y= self.getSuccessor(nd)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            y = nd</span><br><span class="line">        py = y.parent</span><br><span class="line">        x = y.left <span class="keyword">if</span> y.left <span class="keyword">else</span> y.right</span><br><span class="line">        <span class="keyword">if</span> py <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">            self.setRoot(x)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            py.setChild(x,py.left <span class="keyword">is</span> y)</span><br><span class="line">        <span class="keyword">if</span> y != nd:</span><br><span class="line">            self.copyNode(y,nd)</span><br><span class="line">        <span class="keyword">if</span> self.checkBlack(y): self.fixUpDel(py,x)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">fixUpDel</span><span class="params">(self,prt,chd)</span>:</span></span><br><span class="line">        <span class="string">''' adjust colors and rotate '''</span></span><br><span class="line">        <span class="keyword">while</span> self.root != chd <span class="keyword">and</span> self.checkBlack(chd):</span><br><span class="line">            isLeft =prt.left <span class="keyword">is</span> chd</span><br><span class="line">            brother = prt.getChild(<span class="keyword">not</span> isLeft)</span><br><span class="line">            <span class="comment"># brother is black</span></span><br><span class="line">            lb = self.checkBlack(brother.getChild(isLeft))</span><br><span class="line">            rb = self.checkBlack(brother.getChild(<span class="keyword">not</span> isLeft))</span><br><span class="line">            <span class="keyword">if</span>  <span class="keyword">not</span> self.checkBlack(brother):</span><br><span class="line">                <span class="comment"># case 1: brother is red.   converted to  case 2,3,4</span></span><br><span class="line"></span><br><span class="line">                self.setBlack(prt,<span class="keyword">False</span>)</span><br><span class="line">                self.setBlack(brother,<span class="keyword">True</span>)</span><br><span class="line">                self.rotate(prt,brother)</span><br><span class="line"></span><br><span class="line">            <span class="keyword">elif</span> lb <span class="keyword">and</span> rb:</span><br><span class="line">                <span class="comment"># case 2: brother is black and two kids are black.</span></span><br><span class="line">                <span class="comment"># conveted to the begin case</span></span><br><span class="line">                self.setBlack(brother,<span class="keyword">False</span>)</span><br><span class="line">                chd = prt</span><br><span class="line">                prt= chd.parent</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">if</span>  rb:</span><br><span class="line">                    <span class="comment"># case 3: brother is black and left kid is red and right child is black</span></span><br><span class="line">                    <span class="comment"># rotate bro to make g w wl wr in one line</span></span><br><span class="line">                    <span class="comment"># uncle's son is nephew, and niece for uncle's daughter</span></span><br><span class="line">                    nephew = brother.getChild(isLeft)</span><br><span class="line">                    self.setBlack(nephew,<span class="keyword">True</span>)</span><br><span class="line">                    self.setBlack(brother,<span class="keyword">False</span>)</span><br><span class="line"></span><br><span class="line">                    <span class="comment"># brother (not isLeft) rotate</span></span><br><span class="line">                    self.rotate(brother,nephew)</span><br><span class="line">                    brother = nephew</span><br><span class="line"></span><br><span class="line">                <span class="comment"># case 4: brother is black and right child is red</span></span><br><span class="line">                brother.isBlack = prt.isBlack</span><br><span class="line">                self.setBlack(prt,<span class="keyword">True</span>)</span><br><span class="line">                self.setBlack(brother.getChild(<span class="keyword">not</span> isLeft),<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line">                self.rotate(prt,brother)</span><br><span class="line">                chd = self.root</span><br><span class="line">        self.setBlack(chd,<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">sort</span><span class="params">(self,reverse = False)</span>:</span></span><br><span class="line">        <span class="string">''' return a generator of sorted data'''</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">inOrder</span><span class="params">(root)</span>:</span></span><br><span class="line">            <span class="keyword">if</span> root <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span></span><br><span class="line">            <span class="keyword">if</span> reverse:</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> inOrder(root.right)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> inOrder(root.left)</span><br><span class="line">            <span class="keyword">yield</span> root</span><br><span class="line">            <span class="keyword">if</span> reverse:</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> inOrder(root.left)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> inOrder(root.right)</span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">from</span> inOrder(self.root)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">display</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">getHeight</span><span class="params">(nd)</span>:</span></span><br><span class="line">            <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">            <span class="keyword">return</span> max(getHeight(nd.left),getHeight(nd.right)) +<span class="number">1</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">levelVisit</span><span class="params">(root)</span>:</span></span><br><span class="line">            <span class="keyword">from</span> collections <span class="keyword">import</span> deque</span><br><span class="line">            lst = deque([root])</span><br><span class="line">            level = []</span><br><span class="line">            h = getHeight(root)</span><br><span class="line">            ct = lv = <span class="number">0</span></span><br><span class="line">            <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">                ct+=<span class="number">1</span></span><br><span class="line">                nd = lst.popleft()</span><br><span class="line">                <span class="keyword">if</span> ct &gt;= <span class="number">2</span>**lv:</span><br><span class="line">                    lv+=<span class="number">1</span></span><br><span class="line">                    <span class="keyword">if</span> lv&gt;h:<span class="keyword">break</span></span><br><span class="line">                    level.append([])</span><br><span class="line">                level[<span class="number">-1</span>].append(str(nd))</span><br><span class="line">                <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">                    lst += [nd.left,nd.right]</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    lst +=[<span class="keyword">None</span>,<span class="keyword">None</span>]</span><br><span class="line">            <span class="keyword">return</span> level</span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">addBlank</span><span class="params">(lines)</span>:</span></span><br><span class="line">            width = <span class="number">1</span>+len(str(self.root))</span><br><span class="line">            sep = <span class="string">' '</span>*width</span><br><span class="line">            n = len(lines)</span><br><span class="line">            <span class="keyword">for</span> i,oneline <span class="keyword">in</span> enumerate(lines):</span><br><span class="line">                k  = <span class="number">2</span>**(n-i) <span class="number">-1</span></span><br><span class="line">                new = [sep*((k<span class="number">-1</span>)//<span class="number">2</span>)]</span><br><span class="line">                <span class="keyword">for</span> s <span class="keyword">in</span> oneline:</span><br><span class="line">                    new.append(s.ljust(width))</span><br><span class="line">                    new.append(sep*k)</span><br><span class="line">                lines[i] = new</span><br><span class="line">            <span class="keyword">return</span> lines</span><br><span class="line"></span><br><span class="line">        lines = levelVisit(self.root)</span><br><span class="line">        lines = addBlank(lines)</span><br><span class="line">        li = [<span class="string">''</span>.join(line) <span class="keyword">for</span> line <span class="keyword">in</span> lines]</span><br><span class="line">        length = <span class="number">10</span> <span class="keyword">if</span> li==[] <span class="keyword">else</span> max(len(i) <span class="keyword">for</span> i <span class="keyword">in</span> li)//<span class="number">2</span></span><br><span class="line">        begin =<span class="string">'\n'</span>+ <span class="string">'red-black-tree'</span>.rjust(length+<span class="number">14</span>,<span class="string">'-'</span>)  + <span class="string">'-'</span>*(length)</span><br><span class="line">        end = <span class="string">'-'</span>*(length*<span class="number">2</span>+<span class="number">14</span>)+<span class="string">'\n'</span></span><br><span class="line">        <span class="keyword">return</span>  <span class="string">'\n'</span>.join([begin,*li,end])</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.display()</span><br></pre></td></tr></table></figure><p>测试代码<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">genNum</span><span class="params">(n =<span class="number">10</span>)</span>:</span></span><br><span class="line">    nums =[]</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">        <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">            d = randint(<span class="number">0</span>,<span class="number">100</span>)</span><br><span class="line">            <span class="keyword">if</span> d <span class="keyword">not</span> <span class="keyword">in</span> nums:</span><br><span class="line">                nums.append(d)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">return</span> nums</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">buildTree</span><span class="params">(n=<span class="number">10</span>,nums=None,visitor=None)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> nums <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> nums ==[]: nums = genNum(n)</span><br><span class="line">    rbtree = redBlackTree()</span><br><span class="line">    print(<span class="string">f'build a red-black tree using <span class="subst">&#123;nums&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> nums:</span><br><span class="line">        rbtree.insert(node(i))</span><br><span class="line">        <span class="keyword">if</span> visitor:</span><br><span class="line">            visitor(rbtree,i)</span><br><span class="line">    <span class="keyword">return</span> rbtree,nums</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">testInsert</span><span class="params">(nums=None)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">visitor</span><span class="params">(t,val)</span>:</span></span><br><span class="line">        print(<span class="string">'inserting'</span>, val)</span><br><span class="line">        print(t)</span><br><span class="line">    rbtree,nums = buildTree(visitor = visitor,nums=nums)</span><br><span class="line">    print(<span class="string">'-'</span>*<span class="number">5</span>+ <span class="string">'in-order visit'</span> + <span class="string">'-'</span>*<span class="number">5</span>)</span><br><span class="line">    <span class="keyword">for</span> i,j <span class="keyword">in</span> enumerate(rbtree.sort()):</span><br><span class="line">        print(<span class="string">f'<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>: <span class="subst">&#123;j&#125;</span>'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">testSuc</span><span class="params">(nums=None)</span>:</span></span><br><span class="line">    rbtree,nums = buildTree(nums=nums)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> rbtree.sort():</span><br><span class="line">        print(<span class="string">f'<span class="subst">&#123;i&#125;</span>\'s suc is <span class="subst">&#123;rbtree.getSuccessor(i)&#125;</span>'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">testDelete</span><span class="params">(nums=None)</span>:</span></span><br><span class="line">    rbtree,nums = buildTree(nums = nums)</span><br><span class="line">    print(rbtree)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> sorted(nums):</span><br><span class="line">        print(<span class="string">f'deleting <span class="subst">&#123;i&#125;</span>'</span>)</span><br><span class="line">        rbtree.delete(i)</span><br><span class="line">        print(rbtree)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line">    lst =[<span class="number">45</span>, <span class="number">30</span>, <span class="number">64</span>, <span class="number">36</span>, <span class="number">95</span>, <span class="number">38</span>, <span class="number">76</span>, <span class="number">34</span>, <span class="number">50</span>, <span class="number">1</span>]</span><br><span class="line">    lst = [<span class="number">0</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">26</span>,<span class="number">25</span>,<span class="number">8</span>,<span class="number">19</span>,<span class="number">15</span>,<span class="number">16</span>,<span class="number">17</span>]</span><br><span class="line">    <span class="comment">#testSuc(lst)</span></span><br><span class="line">    <span class="comment">#testInsert(lst)</span></span><br><span class="line">    testDelete()</span><br></pre></td></tr></table></figure></p><p><strong>下面是利用红黑树进行扩展成区间树的代码</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> redBlackTree <span class="keyword">import</span> redBlackTree</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> total_ordering</span><br><span class="line"></span><br><span class="line"><span class="meta">@total_ordering</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,low,high,left=None,right=None,isBlack=False)</span>:</span></span><br><span class="line">        self.val =  low   <span class="comment"># self.val is the low</span></span><br><span class="line">        self.high = high</span><br><span class="line">        self.max = high</span><br><span class="line">        self.left = left</span><br><span class="line">        self.right = right</span><br><span class="line">        self.parent=<span class="keyword">None</span></span><br><span class="line">        self.isBlack = isBlack</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__lt__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val &lt; nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__eq__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">and</span> self.val == nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setChild</span><span class="params">(self,nd,isLeft = True)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> isLeft: self.left = nd</span><br><span class="line">        <span class="keyword">else</span>: self.right = nd</span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>: nd.parent = self</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getChild</span><span class="params">(self,isLeft)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> isLeft: <span class="keyword">return</span> self.left</span><br><span class="line">        <span class="keyword">else</span>: <span class="keyword">return</span> self.right</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        color = <span class="string">'B'</span> <span class="keyword">if</span> self.isBlack <span class="keyword">else</span> <span class="string">'R'</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'<span class="subst">&#123;color&#125;</span>[<span class="subst">&#123;self.val&#125;</span>,<span class="subst">&#123;self.high&#125;</span>]-<span class="subst">&#123;self.max&#125;</span>'</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'intervalNode(<span class="subst">&#123;self.val&#125;</span>,<span class="subst">&#123;self.high&#125;</span>,<span class="subst">&#123;self.max&#125;</span>,isBlack=<span class="subst">&#123;self.isBlack&#125;</span>)'</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">overlap</span><span class="params">(self,low,high)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val&lt;=high <span class="keyword">and</span> self.high&gt;=low</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setMax</span><span class="params">(self)</span>:</span></span><br><span class="line">        l = <span class="number">0</span> <span class="keyword">if</span> self.left <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">else</span> self.left.max</span><br><span class="line">        r = <span class="number">0</span> <span class="keyword">if</span> self.right <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">else</span> self.right.max</span><br><span class="line">        self.max = max(self.high, l, r)</span><br><span class="line">        <span class="keyword">return</span> self.max</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">intervalTree</span><span class="params">(redBlackTree)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">search</span><span class="params">(self,low,high)</span>:</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        <span class="keyword">while</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">and</span> <span class="keyword">not</span> nd.overlap(low,high):</span><br><span class="line">            <span class="keyword">if</span> nd.left <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">and</span> nd.left.max&gt;=low:</span><br><span class="line">                nd = nd.left</span><br><span class="line">            <span class="keyword">else</span>:nd = nd.right</span><br><span class="line">        <span class="keyword">return</span> nd</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        super(intervalTree,self).insert(nd)</span><br><span class="line">        <span class="keyword">while</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">            nd.setMax()</span><br><span class="line">            nd = nd.parent</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        nd = self.find(val)</span><br><span class="line">        <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">            nd.max = <span class="number">0</span></span><br><span class="line">            tmp = nd.parent</span><br><span class="line">            <span class="keyword">while</span> tmp <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">                tmp.setMax()</span><br><span class="line">                tmp = tmp.parent</span><br><span class="line">            super(intervalTree,self).delete(val)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">rotate</span><span class="params">(self,prt,chd)</span>:</span></span><br><span class="line">        <span class="string">'''rotate prt, and return new prt, namyly the original chd'''</span></span><br><span class="line">        super(intervalTree,self).rotate(prt,chd)</span><br><span class="line">        prt.setMax()</span><br><span class="line">        chd.setMax()</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">copyNode</span><span class="params">(self,src,des)</span>:</span></span><br><span class="line">        des.val = src.val</span><br><span class="line">        des.high = src.high</span><br><span class="line">        des.setMax()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint, shuffle</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">genNum</span><span class="params">(n =<span class="number">10</span>,upper=<span class="number">10</span>)</span>:</span></span><br><span class="line">    nums =&#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(n):</span><br><span class="line">        <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">            d = randint(<span class="number">0</span>,<span class="number">100</span>)</span><br><span class="line">            <span class="keyword">if</span> d <span class="keyword">not</span> <span class="keyword">in</span> nums:</span><br><span class="line">                nums[d] = (d,randint(d,d+upper))</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">return</span> nums.values()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">buildTree</span><span class="params">(n=<span class="number">10</span>,nums=None,visitor=None)</span>:</span></span><br><span class="line">    <span class="comment">#if nums is None or nums ==[]: nums = genNum(n)</span></span><br><span class="line">    tree = intervalTree()</span><br><span class="line">    print(<span class="string">f'build a red-black tree using <span class="subst">&#123;nums&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> nums:</span><br><span class="line">        tree.insert(node(*i))</span><br><span class="line">        <span class="keyword">if</span> visitor:</span><br><span class="line">            visitor(tree,i)</span><br><span class="line">    <span class="keyword">return</span> tree,nums</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">testInsert</span><span class="params">(nums=None)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">visitor</span><span class="params">(t,val)</span>:</span></span><br><span class="line">        print(<span class="string">'inserting'</span>, val)</span><br><span class="line">        print(t)</span><br><span class="line">    tree,nums = buildTree(visitor = visitor,nums=nums)</span><br><span class="line">    print(<span class="string">'-'</span>*<span class="number">5</span>+ <span class="string">'in-order visit'</span> + <span class="string">'-'</span>*<span class="number">5</span>)</span><br><span class="line">    <span class="keyword">for</span> i,j <span class="keyword">in</span> enumerate(tree.sort()):</span><br><span class="line">        print(<span class="string">f'<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>: <span class="subst">&#123;j&#125;</span>'</span>)</span><br><span class="line">    <span class="keyword">return</span> tree</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">testSuc</span><span class="params">(nums=None)</span>:</span></span><br><span class="line">    tree,nums = buildTree(nums=nums)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> tree.sort():</span><br><span class="line">        print(<span class="string">f'<span class="subst">&#123;i&#125;</span>\'s suc is <span class="subst">&#123;tree.getSuccessor(i)&#125;</span>'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">testDelete</span><span class="params">(nums=None)</span>:</span></span><br><span class="line">    tree,nums = buildTree(nums = nums)</span><br><span class="line">    print(tree)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> nums:</span><br><span class="line">        print(<span class="string">f'deleting <span class="subst">&#123;i&#125;</span>'</span>)</span><br><span class="line">        tree.delete(i[<span class="number">0</span>])</span><br><span class="line">        print(tree)</span><br><span class="line">    <span class="keyword">return</span> tree</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line">    lst = [(<span class="number">0</span>,<span class="number">3</span>),(<span class="number">5</span>,<span class="number">8</span>),(<span class="number">6</span>,<span class="number">10</span>),(<span class="number">26</span>,<span class="number">26</span>),(<span class="number">25</span>,<span class="number">30</span>),(<span class="number">8</span>,<span class="number">9</span>),(<span class="number">19</span>,<span class="number">20</span>),(<span class="number">15</span>,<span class="number">23</span>),(<span class="number">16</span>,<span class="number">21</span>),(<span class="number">17</span>,<span class="number">19</span>)]</span><br><span class="line">    <span class="comment">#lst = None</span></span><br><span class="line">    <span class="comment">#testSuc(lst)</span></span><br><span class="line">    tree = testInsert(lst)</span><br><span class="line">    <span class="comment">#tree,_= buildTree(lst)</span></span><br><span class="line">    <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">        a =int( input(<span class="string">'low:'</span>))</span><br><span class="line">        b =int( input(<span class="string">'high:'</span>))</span><br><span class="line">        res = tree.search(a,b)</span><br><span class="line">        print(res)<span class="keyword">from</span> redBlackTree <span class="keyword">import</span> redBlackTree</span><br></pre></td></tr></table></figure><p><a id="markdown-7-参考" name="7-参考"></a></p><h1 id="7-参考"><a href="#7-参考" class="headerlink" title="7. 参考"></a>7. 参考</h1><blockquote id="fn_1"><sup>1</sup>. 算法导论<a href="#reffn_1" title="Jump back to footnote [1] in the text."> &#8617;</a></blockquote><blockquote id="fn_2"><sup>2</sup>. <a href="https://www.jianshu.com/p/a5514510f5b9?utm_campaign=maleskine&amp;utm_content=note&amp;utm_medium=seo_notes&amp;utm_source=recommendation" target="_blank" rel="noopener">https://www.jianshu.com/p/a5514510f5b9?utm_campaign=maleskine&amp;utm_content=note&amp;utm_medium=seo_notes&amp;utm_source=recommendation</a><a href="#reffn_2" title="Jump back to footnote [2] in the text."> &#8617;</a></blockquote><blockquote id="fn_3"><sup>3</sup>. <a href="https://www.jianshu.com/p/0b68b992f688?utm_campaign=maleskine&amp;utm_content=note&amp;utm_medium=seo_notes&amp;utm_source=recommendation" target="_blank" rel="noopener">https://www.jianshu.com/p/0b68b992f688?utm_campaign=maleskine&amp;utm_content=note&amp;utm_medium=seo_notes&amp;utm_source=recommendation</a><a href="#reffn_3" title="Jump back to footnote [3] in the text."> &#8617;</a></blockquote>]]></content>
    
    <summary type="html">
    
      红黑树的原理与实现, 包括插入, 删除, 以及数据结构的扩张
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="数据结构" scheme="https://mbinary.xyz/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
      <category term="红黑树" scheme="https://mbinary.xyz/tags/%E7%BA%A2%E9%BB%91%E6%A0%91/"/>
    
  </entry>
  
  <entry>
    <title>『数据结构』树</title>
    <link href="https://mbinary.xyz/tree.html"/>
    <id>https://mbinary.xyz/tree.html</id>
    <published>2018-07-11T18:56:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-概念">1. 概念</a></li><li><a href="#2-二叉查找树">2. 二叉查找树</a><ul><li><a href="#21-随机构造的二叉查找树">2.1. 随机构造的二叉查找树</a></li><li><a href="#22-平均结点深度">2.2. 平均结点深度</a></li><li><a href="#23-不同的二叉树数目catalan-num">2.3. 不同的二叉树数目(Catalan num)</a></li><li><a href="#24-好括号列">2.4. 好括号列</a><a id="more"></a></li></ul></li><li><a href="#3-基数树radixtree">3. 基数树(radixTree)</a></li><li><a href="#4-字典树trie">4. 字典树(trie)</a><ul><li><a href="#41-ac-自动机">4.1. AC 自动机</a></li></ul></li><li><a href="#5-平衡二叉树">5. 平衡二叉树</a><ul><li><a href="#51-avl-tree">5.1. AVL Tree</a></li><li><a href="#52-splaytree">5.2. splayTree</a><ul><li><a href="#521-zig-step">5.2.1. Zig-step</a></li><li><a href="#522-zig-zig-step">5.2.2. Zig-zig step</a></li><li><a href="#523-zig-zag-step">5.2.3. Zig-zag step</a></li></ul></li><li><a href="#53-read-black-tree">5.3. read-black Tree</a></li><li><a href="#54-treap">5.4. treap</a></li></ul></li><li><a href="#6-总结">6. 总结</a></li><li><a href="#7-附代码">7. 附代码</a><ul><li><a href="#71-二叉树binarytree">7.1. 二叉树(binaryTree)</a></li><li><a href="#72-前缀树trie">7.2. 前缀树(Trie)</a></li><li><a href="#73-赢者树winnertree">7.3. 赢者树(winnerTree)</a></li><li><a href="#74-左斜堆">7.4. 左斜堆</a></li></ul></li></ul><!-- /TOC --><p><a id="markdown-1-概念" name="1-概念"></a></p><h1 id="1-概念"><a href="#1-概念" class="headerlink" title="1. 概念"></a>1. 概念</h1><ul><li>双亲</li><li>左右孩子</li><li>左右子树</li><li>森林</li><li>结点,叶子,边,路径</li><li>高度 h</li><li>遍历(前中后层)</li><li>结点数 n</li></ul><p><a id="markdown-2-二叉查找树" name="2-二叉查找树"></a></p><h1 id="2-二叉查找树"><a href="#2-二叉查找树" class="headerlink" title="2. 二叉查找树"></a>2. 二叉查找树</h1><p>又名排序二叉树,对于每个结点, 如果有,其左孩子不大于它,右孩子不小于它</p><p>通过前序遍历或者后序遍历就可以得到有序序列(升序,降序)</p><p>常用三种操作, 插入,删除,查找,时间复杂度是 $O(h)$<br>h是树高, 但是由于插入,删除而导致树不平衡, 即可能 $h\geqslant \lfloor logn \rfloor$</p><p><a id="markdown-21-随机构造的二叉查找树" name="21-随机构造的二叉查找树"></a></p><h2 id="2-1-随机构造的二叉查找树"><a href="#2-1-随机构造的二叉查找树" class="headerlink" title="2.1. 随机构造的二叉查找树"></a>2.1. 随机构造的二叉查找树</h2><p>下面可以证明,随机构造,即输入序列有 $n!$中, 每种概率相同的情况下, 期望的树高 $h=O(logn)$</p><p>(直接搬运算法导论上面的啦&gt;_&lt;)<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/tree-1.png" alt="tree-1"></p><p><a id="markdown-22-平均结点深度" name="22-平均结点深度"></a></p><h2 id="2-2-平均结点深度"><a href="#2-2-平均结点深度" class="headerlink" title="2.2. 平均结点深度"></a>2.2. 平均结点深度</h2><p>一个较 上面定理 弱的结论:</p><blockquote><p>一棵随机构造的二叉查找树,n 个结点的平均深度为 $O(logn)$</p></blockquote><p>类似 RANDOMIZED-QUICKSORT 的证明过程, 因为快排 递归的过程就是一个递归 二叉树.<br>随机选择枢纽元就相当于这里的某个子树的根结点 在所有结点的大小随机排名, 如 i. 然后根结点将剩下的结点划分为左子树(i-1)个结点, 右子树(n-i)个结点.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/tree-2.png" alt="tree-2"><br><a id="markdown-23-不同的二叉树数目catalan-num" name="23-不同的二叉树数目catalan-num"></a></p><h2 id="2-3-不同的二叉树数目-Catalan-num"><a href="#2-3-不同的二叉树数目-Catalan-num" class="headerlink" title="2.3. 不同的二叉树数目(Catalan num)"></a>2.3. 不同的二叉树数目(Catalan num)</h2><p>给定$\{1,2,\ldots,n\}$,组成二叉查找树的数目.<br>由上面的证明过程, 可以容易地分析得出, 任选第 i 个数作为根, 由于二叉查找树的性质, 其左子树<br>应该有 i-1个结点, 右子树有 n-i个结点.<br>如果记 n 个结点 的二叉查找树的数目为$b_n$<br>则有递推公式</p><script type="math/tex; mode=display">b_n=\begin{cases}1 &n=0 \\\sum_{i=1}^{n}b_{i-1}b_{n-i} & n\geqslant 1\end{cases}</script><p>然后我们来看<code>&lt;&lt;算法导论&gt;&gt;</code>(p162,思考题12-4)上怎么求的吧( •̀ ω •́ )y<br>设生成函数</p><script type="math/tex; mode=display">B(x)=\sum_{n=0}^{\infty}b_n x^n</script><p>下面证明$B(x)=xB(x)^2+1$<br>易得<script type="math/tex">xB(x)^2=\sum_{i=1}^{\infty}\sum_{n=i}^{\infty}b_{i-1}b_{n-i}x^n</script><br>对比$B(x), xB(x)^2+1$的 x 的各次系数,分别是 $b_k,a_{k}$<br>当 k=0, $a_k=1=b_k$<br>当 k&gt;0</p><script type="math/tex; mode=display">a_{k} = \sum_{i=1}^{k}b_{i-1}b_{k-i} = b_k</script><p>所以$B(x)=xB(x)^2+1$<br>由此解得</p><script type="math/tex; mode=display">B(x)=\frac{1-\sqrt{1-4x} }{2x}</script><p>在点 x=0 处,<br>用泰勒公式得</p><script type="math/tex; mode=display">\begin{aligned}\lim_{x\to 0}\sqrt{1-4x}&=1+\sum_{n=1}^{\infty}C_n^{\frac{1}{2}}{(-4)}^nx^n  \\&=1+\sum_{n=1}^{\infty}\frac{(2n-3)!!{(-4x)}^n}{n!}\end{aligned}</script><p>所以对应系数</p><script type="math/tex; mode=display">\begin{aligned}b_n&=\frac{1}{2}\frac{4^{n+1}(2n-1)!!}{2^{n+1}n!}  \\  &=\frac{C_{2n}^{n}}{n+1}\end{aligned}</script><p>这个数叫做 <code>Catalan 数</code><br><a id="markdown-24-好括号列" name="24-好括号列"></a></p><h2 id="2-4-好括号列"><a href="#2-4-好括号列" class="headerlink" title="2.4. 好括号列"></a>2.4. 好括号列</h2><p>王树禾的<code>&lt;&lt;图论&gt;&gt;</code>(p42)上用另外的方法给出Catalan数, 并求出n结点 二叉查找数的个数</p><p>首先定义好括号列,有:</p><ul><li>空列,即没有括号叫做好括号列</li><li>若A,B都是好括号列, 则串联后 AB是好括号列</li><li>若A是好括号列, 则 (A)是好括号列</li></ul><blockquote><p>充要条件: 好括号列 $\Longleftrightarrow$ 左右括号数相等, 且从左向右看, 看到的右括号数不超过左括号数</p><p>定理: 由 n个左括号,n个右括号组成的好括号列个数为$c(n)=\frac{C_{2n}^{n}}{n+1}$</p></blockquote><p>证明:<br>由 n左n右组成的括号列有 $\frac{2n}{n!n!}=C_{2n}^{n}$个.<br> 设括号列$a_1a_2\ldots a_{2n}$为坏括号列,<br>由充要条件, 存在最小的 j, 使得$a_1a_2\ldots a_{j}$中右括号比左括号多一个,<br>由于是最小的 j, 所以 $a_j$为右括号, $a_{j+1}$为右括号<br>把$a_{j+1}a_{j+2}\ldots a_{2n}$中的左括号变为右括号, 右变左,记为$\bar a_{j+1}\bar a_{j+2}\ldots \bar a_{2n}$</p><p>则括号列$a_1a_2\ldots a_{j}\bar a_{j+1}$为好括号列<br>$a_1a_2\ldots a_{j}\bar a_{j+1}\bar a_{j+2}\ldots \bar a_{2n}$可好可坏,且有n-1个右,n+1个左, 共有$\frac{2n}{(n+1)!(n-1)!}=C_{2n}^{n+1}$个.</p><p>所以坏括号列$a_1a_2\ldots a_{2n}$ 与括号列 $a_1a_2\ldots a_{j}\bar a_{j+1}\bar a_{j+2}\ldots \bar a_{2n}$, 有$\frac{2n}{(n+1)!(n-1)!}=C_{2n}^{n+1}$个</p><p>那么好括号列有</p><script type="math/tex; mode=display">c(n)=C_{2n}^{n} - C_{2n}^{n+1} =\frac{C_{2n}^{n}}{n+1}</script><blockquote><p>推论: n个字符,进栈出栈(出栈可以在栈不为空的时候随时进行), 则出栈序列有 c(n)种</p></blockquote><p>这种先入后出的情形都是这样<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/tree-3.png" alt="tree-3"></p><p><a id="markdown-3-基数树radixtree" name="3-基数树radixtree"></a></p><h1 id="3-基数树-radixTree"><a href="#3-基数树-radixTree" class="headerlink" title="3. 基数树(radixTree)"></a>3. 基数树(radixTree)</h1><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/tree-4.png" alt="tree-4"><br><a id="markdown-4-字典树trie" name="4-字典树trie"></a></p><h1 id="4-字典树-trie"><a href="#4-字典树-trie" class="headerlink" title="4. 字典树(trie)"></a>4. 字典树(trie)</h1><p>又叫<code>前缀树</code>(preifx tree).适用于储存有公共前缀的字符串集合. 如果直接储存, 而很多字符串有公共前缀, 会浪费掉存储空间.<br>字典树可以看成是基数树的变形, 每个结点可以有多个孩子, 每个结点存储的是一个字符, 从根沿着结点走到一个结点,走过的路径形成字符序列, 如果有合适的单词就可以输出.</p><p>当然,也可以同理得出后缀树<br><a id="markdown-41-ac-自动机" name="41-ac-自动机"></a></p><h2 id="4-1-AC-自动机"><a href="#4-1-AC-自动机" class="headerlink" title="4.1. AC 自动机"></a>4.1. AC 自动机</h2><p>Aho-Corasick automation,是在字典树上添加匹配失败边(失配指针), 实现字符串搜索匹配的算法.<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/tree-5.png" alt="tree-5"></p><p>图中蓝色结点 表示存在字符串, 灰色表示不存在.<br>黑色边是父亲到子结点的边, 蓝色边就是<code>失配指针</code>.</p><p>蓝色边(终点称为起点的后缀结点): 连接字符串<strong>终点</strong>到在<strong>图中存在的</strong>, <strong>最长</strong>严格后缀的结点. 如 caa 的严格后缀为 aa,a, 空. 而在图中存在, 且最长的是字符串  a, 则连接到这个字符串的终点 a.</p><p>绿色边(字典后缀结点): 终点是起点经过蓝色有向边到达的第一个蓝色结点.</p><p>下面摘自 <code>wiki</code></p><blockquote><p>在每一步中，算法先查找当前节点的 “孩子节点”，如果没有找到匹配，查找它的后缀节点(suffix) 的孩子，如果仍然没有，接着查找后缀节点的后缀节点的孩子, 如此循环, 直到根结点，如果到达根节点仍没有找到匹配则结束。</p><p>当算法查找到一个节点，则输出所有结束在当前位置的字典项。输出步骤为首先找到该节点的字典后缀，然后用递归的方式一直执行到节点没有字典前缀为止。同时，如果该节点为一个字典节点，则输出该节点本身。</p></blockquote><p><a id="markdown-5-平衡二叉树" name="5-平衡二叉树"></a></p><h1 id="5-平衡二叉树"><a href="#5-平衡二叉树" class="headerlink" title="5. 平衡二叉树"></a>5. 平衡二叉树</h1><p>上面的二叉查找树不平衡,即经过多次插入,删除后,  其高度变化大, 不能保持$\Theta(n)$的性能<br>而平衡二叉树就能.<br>平衡二叉树都是经过一些旋转操作, 使左右子树的结点高度相差不大,达到平衡<br> 有如下几种<br><a id="markdown-51-avl-tree" name="51-avl-tree"></a></p><h2 id="5-1-AVL-Tree"><a href="#5-1-AVL-Tree" class="headerlink" title="5.1. AVL Tree"></a>5.1. AVL Tree</h2><p><code>平衡因子</code>: 右子树高度 - 左子树高度<br>定义: 每个结点的平衡因子属于{0,-1,1}<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/AVL_Tree_Example.gif.png" alt="AVL_Tree_Example.gif"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/from wiki.png" alt="from wiki"></p><p><a id="markdown-52-splaytree" name="52-splaytree"></a></p><h2 id="5-2-splayTree"><a href="#5-2-splayTree" class="headerlink" title="5.2. splayTree"></a>5.2. splayTree</h2><p>伸展树, 它的特点是每次将访问的结点通过旋转旋转到根结点.<br>其实它并不平衡. 但是插入,查找,删除操作 的平摊时间是$O(logn)$<br>有三种旋转,下面都是将访问过的 x 旋转到 根部<br><a id="markdown-521-zig-step" name="521-zig-step"></a></p><h3 id="5-2-1-Zig-step"><a href="#5-2-1-Zig-step" class="headerlink" title="5.2.1. Zig-step"></a>5.2.1. Zig-step</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/zig.png" alt="zig"><br><a id="markdown-522-zig-zig-step" name="522-zig-zig-step"></a></p><h3 id="5-2-2-Zig-zig-step"><a href="#5-2-2-Zig-zig-step" class="headerlink" title="5.2.2. Zig-zig step"></a>5.2.2. Zig-zig step</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/zig-zig.png" alt="zig-zig"><br><a id="markdown-523-zig-zag-step" name="523-zig-zag-step"></a></p><h3 id="5-2-3-Zig-zag-step"><a href="#5-2-3-Zig-zag-step" class="headerlink" title="5.2.3. Zig-zag step"></a>5.2.3. Zig-zag step</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/zig-zag.png" alt="zig-zag"><br><a id="markdown-53-read-black-tree" name="53-read-black-tree"></a></p><h2 id="5-3-read-black-Tree"><a href="#5-3-read-black-Tree" class="headerlink" title="5.3. read-black Tree"></a>5.3. read-black Tree</h2><p>同样是平衡的二叉树, 以后单独写一篇关于红黑树的.</p><p><a id="markdown-54-treap" name="54-treap"></a></p><h2 id="5-4-treap"><a href="#5-4-treap" class="headerlink" title="5.4. treap"></a>5.4. treap</h2><p><a href="#21-随机构造的二叉查找树">前面提到</a>, 随机构造的二叉查找树高度为 $h=O(logn)$,以及在<a href="/alg-genral.html">算法 general</a> 中说明了怎样 随机化(shuffle)一个给定的序列.</p><p>所以,为了得到一个平衡的二叉排序树,我们可以将给定的序列随机化, 然后再进行构造二叉排序树.</p><p>但是如果不能一次得到全部的数据,也就是可能插入新的数据的时候,该怎么办呢? 可以证明,满足下面的条件构造的结构相当于同时得到全部数据, 也就是随机化的二叉查找树.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/treap.png" alt="treap"></p><p>这种结构叫 <code>treap</code>, 不仅有要排序的关键字 key, 还有随机生成的,各不相等的关键字<code>priority</code>,代表插入的顺序.</p><ul><li>二叉查找树的排序性质: 双亲结点的 key 大于左孩子,小于右孩子</li><li>最小(大)堆的堆序性质: 双亲的 prority小于(大于) 孩子的 prority</li></ul><p>插入的实现: 先进行二叉查找树的插入,成为叶子结点, 再通过旋转 实现 <code>上浮</code>(堆中术语).<br>将先排序 key, 再排序 prority(排序prority 时通过旋转保持 key 的排序)</p><p><a id="markdown-6-总结" name="6-总结"></a></p><h1 id="6-总结"><a href="#6-总结" class="headerlink" title="6. 总结"></a>6. 总结</h1><p>还有很多有趣的树结构,<br>比如斜堆, 竞赛树(赢者树,输者树,线段树, 索引树,B树, fingerTree(不知道是不是译为手指树233)…<br>这里就不详细介绍了, 如果以后有时间,可能挑几个单独写一篇文章</p><p><a id="markdown-7-附代码" name="7-附代码"></a></p><h1 id="7-附代码"><a href="#7-附代码" class="headerlink" title="7. 附代码"></a>7. 附代码</h1><p><strong><a href="https://github.com/mbinary/algorithm.git" target="_blank" rel="noopener">github地址</a></strong></p><p><a id="markdown-71-二叉树binarytree" name="71-二叉树binarytree"></a></p><h2 id="7-1-二叉树-binaryTree"><a href="#7-1-二叉树-binaryTree" class="headerlink" title="7.1. 二叉树(binaryTree)"></a>7.1. 二叉树(binaryTree)</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> total_ordering</span><br><span class="line"></span><br><span class="line"><span class="meta">@total_ordering</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,val,left=None,right=None,freq = <span class="number">1</span>)</span>:</span></span><br><span class="line">        self.val=val</span><br><span class="line">        self.left=left</span><br><span class="line">        self.right=right</span><br><span class="line">        self.freq = freq</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__lt__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val&lt;nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__eq__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val==nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">'node(&#123;&#125;)'</span>.format(self.val)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">binaryTree</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.root=<span class="keyword">None</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">add</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">_add</span><span class="params">(nd,newNode)</span>:</span></span><br><span class="line">            <span class="keyword">if</span> nd&lt;newNode:</span><br><span class="line">                <span class="keyword">if</span> nd.right <span class="keyword">is</span> <span class="keyword">None</span>:nd.right = newNode</span><br><span class="line">                <span class="keyword">else</span>:_add(nd.right,newNode)</span><br><span class="line">            <span class="keyword">elif</span> nd&gt;newNode:</span><br><span class="line">                <span class="keyword">if</span> nd.left <span class="keyword">is</span> <span class="keyword">None</span>:nd.left = newNode</span><br><span class="line">                <span class="keyword">else</span> : _add(nd.left,newNode)</span><br><span class="line">            <span class="keyword">else</span>:nd.freq +=<span class="number">1</span></span><br><span class="line">        _add(self.root,node(val))</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">find</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        prt= self._findPrt(self.root,node(val),<span class="keyword">None</span>)</span><br><span class="line">        <span class="keyword">if</span> prt.left <span class="keyword">and</span> prt.left.val==val:</span><br><span class="line">            <span class="keyword">return</span> prt.left</span><br><span class="line">        <span class="keyword">elif</span>  prt.right <span class="keyword">and</span> prt.right.val==val:<span class="keyword">return</span> prt.right</span><br><span class="line">        <span class="keyword">else</span> :<span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_findPrt</span><span class="params">(self,nd,tgt,prt)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> nd==tgt <span class="keyword">or</span> nd <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span> prt</span><br><span class="line">        <span class="keyword">elif</span> nd&lt;tgt:<span class="keyword">return</span> self._findPrt(nd.right,tgt,nd)</span><br><span class="line">        <span class="keyword">else</span>:<span class="keyword">return</span> self._findPrt(nd.left,tgt,nd)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">delete</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        prt= self._findPrt(self.root,node(val),<span class="keyword">None</span>)</span><br><span class="line">        <span class="keyword">if</span> prt.left <span class="keyword">and</span> prt.left.val==val:</span><br><span class="line">            l=prt.left</span><br><span class="line">            <span class="keyword">if</span> l.left <span class="keyword">is</span> <span class="keyword">None</span>:prt.left = l.right</span><br><span class="line">            <span class="keyword">elif</span> l.right <span class="keyword">is</span> <span class="keyword">None</span> : prt.left = l.left</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                nd = l.left</span><br><span class="line">                <span class="keyword">while</span> nd.right <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:nd = nd.right</span><br><span class="line">                nd.right = l.right</span><br><span class="line">                prt.left = l.left</span><br><span class="line">        <span class="keyword">elif</span>  prt.right <span class="keyword">and</span> prt.right.val==val:</span><br><span class="line">            r=prt.right</span><br><span class="line">            <span class="keyword">if</span> r.right <span class="keyword">is</span> <span class="keyword">None</span>:prt.right = r.right</span><br><span class="line">            <span class="keyword">elif</span> r.right <span class="keyword">is</span> <span class="keyword">None</span> : prt.right = r.left</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                nd = r.left</span><br><span class="line">                <span class="keyword">while</span> nd.right <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:nd = nd.right</span><br><span class="line">                nd.right = r.right</span><br><span class="line">                prt.left = r.left</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">preOrder</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">_p</span><span class="params">(nd)</span>:</span></span><br><span class="line">            <span class="keyword">if</span> nd <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">                print(nd)</span><br><span class="line">                _p(nd.left)</span><br><span class="line">                _p(nd.right)</span><br><span class="line">        _p(self.root)</span><br></pre></td></tr></table></figure><p><a id="markdown-72-前缀树trie" name="72-前缀树trie"></a></p><h2 id="7-2-前缀树-Trie"><a href="#7-2-前缀树-Trie" class="headerlink" title="7.2. 前缀树(Trie)"></a>7.2. 前缀树(Trie)</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,val = None)</span>:</span></span><br><span class="line">        self.val = val</span><br><span class="line">        self.isKey = <span class="keyword">False</span></span><br><span class="line">        self.children = &#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self,i)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.children[i]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> iter(self.children.keys())</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__setitem__</span><span class="params">(self,i,x)</span>:</span></span><br><span class="line">        self.children[i] = x</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.children!=&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">'val: '</span>+str(self.val)+<span class="string">'\nchildren: '</span>+<span class="string">' '</span>.join(self.children.keys())</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> str(self)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Trie</span><span class="params">(object)</span>:</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.root=node(<span class="string">''</span>)</span><br><span class="line">        self.dic =&#123;<span class="string">'insert'</span>:self.insert,<span class="string">'startsWith'</span>:self.startsWith,<span class="string">'search'</span>:self.search&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self, word)</span>:</span></span><br><span class="line">        <span class="string">"""</span></span><br><span class="line"><span class="string">        Inserts a word into the trie.</span></span><br><span class="line"><span class="string">        :type word: str</span></span><br><span class="line"><span class="string">        :rtype: void</span></span><br><span class="line"><span class="string">        """</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> word:<span class="keyword">return</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> word:</span><br><span class="line">            <span class="keyword">if</span> i <span class="keyword">in</span> nd:</span><br><span class="line">                nd = nd[i]</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                newNode= node(i)</span><br><span class="line">                nd[i] = newNode</span><br><span class="line">                nd = newNode</span><br><span class="line">        <span class="keyword">else</span>:nd.isKey = <span class="keyword">True</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">search</span><span class="params">(self, word,matchAll=<span class="string">'.'</span>)</span>:</span></span><br><span class="line">        <span class="string">"""support matchall function  eg,  'p.d' matchs 'pad' , 'pid'</span></span><br><span class="line"><span class="string">        """</span></span><br><span class="line">        self.matchAll = <span class="string">'.'</span></span><br><span class="line">        <span class="keyword">return</span> self._search(self.root,word)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_search</span><span class="params">(self,nd,word)</span>:</span></span><br><span class="line">        <span class="keyword">for</span> idx,i <span class="keyword">in</span> enumerate(word):</span><br><span class="line">            <span class="keyword">if</span> i==self.matchAll :</span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> nd:</span><br><span class="line">                    bl =self._search(nd[j],word[idx+<span class="number">1</span>:])</span><br><span class="line">                    <span class="keyword">if</span> bl:<span class="keyword">return</span> <span class="keyword">True</span></span><br><span class="line">                <span class="keyword">else</span>:<span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">            <span class="keyword">if</span> i  <span class="keyword">in</span> nd:</span><br><span class="line">                nd = nd[i]</span><br><span class="line">            <span class="keyword">else</span>:<span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">        <span class="keyword">else</span>:<span class="keyword">return</span> nd.isKey</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">startsWith</span><span class="params">(self, prefix)</span>:</span></span><br><span class="line">        <span class="string">"""</span></span><br><span class="line"><span class="string">        Returns if there is any word in the trie that starts with the given prefix.</span></span><br><span class="line"><span class="string">        :type prefix: str</span></span><br><span class="line"><span class="string">        :rtype: bool</span></span><br><span class="line"><span class="string">        """</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> prefix:</span><br><span class="line">            <span class="keyword">if</span> i <span class="keyword">in</span>  nd:</span><br><span class="line">                nd= nd[i]</span><br><span class="line">            <span class="keyword">else</span>:<span class="keyword">return</span> <span class="keyword">False</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">True</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">display</span><span class="params">(self)</span>:</span></span><br><span class="line">        print(<span class="string">'preOrderTraverse  data of the Trie'</span>)</span><br><span class="line">        self.preOrder(self.root,<span class="string">''</span>)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">preOrder</span><span class="params">(self,root,s)</span>:</span></span><br><span class="line">        s=s+root.val</span><br><span class="line">        <span class="keyword">if</span>  root.isKey:</span><br><span class="line">            print(s)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> root:</span><br><span class="line">            self.preOrder(root[i],s)</span><br></pre></td></tr></table></figure><p><a id="markdown-73-赢者树winnertree" name="73-赢者树winnertree"></a></p><h2 id="7-3-赢者树-winnerTree"><a href="#7-3-赢者树-winnerTree" class="headerlink" title="7.3. 赢者树(winnerTree)"></a>7.3. 赢者树(winnerTree)</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">winnerTree</span>:</span></span><br><span class="line">    <span class="string">'''if i&lt;lowExt    p = (i+offset)//2</span></span><br><span class="line"><span class="string">       else           p = (i+n-1-lowExt)//2</span></span><br><span class="line"><span class="string">       offset is a num 2^k-1 just bigger than n</span></span><br><span class="line"><span class="string">        p is the index of tree</span></span><br><span class="line"><span class="string">        i is the index of players</span></span><br><span class="line"><span class="string">        lowExt is the double node num of the lowest layer of the tree</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,players,reverse=False)</span>:</span></span><br><span class="line">        self.n=len(players)</span><br><span class="line">        self.tree = [<span class="number">0</span>]*self.n</span><br><span class="line">        players.insert(<span class="number">0</span>,<span class="number">0</span>)</span><br><span class="line">        self.players=players</span><br><span class="line">        self.reverse=reverse</span><br><span class="line">        self.getNum()</span><br><span class="line">        self.initTree(<span class="number">1</span>)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getNum</span><span class="params">(self)</span>:</span></span><br><span class="line">        i=<span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> <span class="number">2</span>*i&lt; self.n:i=i*<span class="number">2</span></span><br><span class="line">        <span class="keyword">if</span> <span class="number">2</span>*i ==self. n:</span><br><span class="line">            self.lowExt=<span class="number">0</span></span><br><span class="line">            self.s = <span class="number">2</span>*i<span class="number">-1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            self.lowExt = (self.n-i)*<span class="number">2</span></span><br><span class="line">            self.s = i<span class="number">-1</span></span><br><span class="line">        self.offset = <span class="number">2</span>*i<span class="number">-1</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">treeToArray</span><span class="params">(self,p)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span>*p-self.offset <span class="keyword">if</span> p&gt;self.s <span class="keyword">else</span> <span class="number">2</span>*p+self.lowExt-self.n+<span class="number">1</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">arrayToTree</span><span class="params">(self,i)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> (i+self.offset)//<span class="number">2</span> <span class="keyword">if</span> i&lt;=self.lowExt <span class="keyword">else</span> (i-self.lowExt+ self.n<span class="number">-1</span>)//<span class="number">2</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">win</span><span class="params">(self,a,b)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> a&lt;b <span class="keyword">if</span> self.reverse <span class="keyword">else</span> a&gt;b</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">initTree</span><span class="params">(self,p)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> p&gt;=self.n:</span><br><span class="line">            delta = p%<span class="number">2</span>  <span class="comment">#!!! good job  notice delta mark the lchild or rchlid</span></span><br><span class="line">            <span class="keyword">return</span> self.players[self.treeToArray(p//<span class="number">2</span>)+delta]</span><br><span class="line">        l = self.initTree(<span class="number">2</span>*p)</span><br><span class="line">        r = self.initTree(<span class="number">2</span>*p+<span class="number">1</span>)</span><br><span class="line">        self.tree[p] = l <span class="keyword">if</span> self.win(l,r) <span class="keyword">else</span> r</span><br><span class="line">        <span class="keyword">return</span> self.tree[p]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">winner</span><span class="params">(self)</span>:</span></span><br><span class="line">        idx = <span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> <span class="number">2</span>*idx&lt;self.n:</span><br><span class="line">            idx = <span class="number">2</span>*idx <span class="keyword">if</span> self.tree[<span class="number">2</span>*idx] == self.tree[idx] <span class="keyword">else</span> idx*<span class="number">2</span>+<span class="number">1</span></span><br><span class="line">        num = self.treeToArray(idx)</span><br><span class="line">        num = num+<span class="number">1</span> <span class="keyword">if</span> self.players[num] !=self.tree[<span class="number">1</span>] <span class="keyword">else</span> num</span><br><span class="line">        <span class="keyword">return</span> self.tree[<span class="number">1</span>],num</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getOppo</span><span class="params">(self,i,x,p)</span>:</span></span><br><span class="line">        oppo=<span class="keyword">None</span></span><br><span class="line">        <span class="keyword">if</span> <span class="number">2</span>*p&lt;self.n:oppo=self.tree[<span class="number">2</span>*p]</span><br><span class="line">        <span class="keyword">elif</span> i&lt;=self.lowExt:oppo=self.players[i<span class="number">-1</span>+i%<span class="number">2</span>*<span class="number">2</span>]</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            lpl= self.players[<span class="number">2</span>*p+self.lowExt-self.n+<span class="number">1</span>]</span><br><span class="line">            oppo = lpl <span class="keyword">if</span> lpl!=x <span class="keyword">else</span> self.players[<span class="number">2</span>*p+self.lowExt-self.n+<span class="number">2</span>]</span><br><span class="line">        <span class="keyword">return</span> oppo</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">update</span><span class="params">(self,i,x)</span>:</span></span><br><span class="line">        <span class="string">''' i is 1-indexed  which is the num of player</span></span><br><span class="line"><span class="string">            and x is the new val of the player '''</span></span><br><span class="line">        self.players[i]=x</span><br><span class="line">        p = self.arrayToTree(i)</span><br><span class="line">        oppo =self.getOppo(i,x,p)</span><br><span class="line">        self.tree[p] = x <span class="keyword">if</span> self.win(x,oppo) <span class="keyword">else</span> oppo</span><br><span class="line">        p=p//<span class="number">2</span></span><br><span class="line">        <span class="keyword">while</span> p:</span><br><span class="line">            l = self.tree[p*<span class="number">2</span>]</span><br><span class="line">            r = <span class="keyword">None</span></span><br><span class="line">            <span class="keyword">if</span> <span class="number">2</span>*p+<span class="number">1</span>&lt;self.n:r=self.tree[p*<span class="number">2</span>+<span class="number">1</span>]   <span class="comment">#notice this !!!</span></span><br><span class="line">            <span class="keyword">else</span>:r = self.players[<span class="number">2</span>*p+self.lowExt-self.n+<span class="number">1</span>]</span><br><span class="line">            self.tree[p] = l <span class="keyword">if</span> self.win(l,r) <span class="keyword">else</span> r</span><br><span class="line">            p=p//<span class="number">2</span></span><br></pre></td></tr></table></figure><p><a id="markdown-74-左斜堆" name="74-左斜堆"></a></p><h2 id="7-4-左斜堆"><a href="#7-4-左斜堆" class="headerlink" title="7.4. 左斜堆"></a>7.4. 左斜堆</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> total_ordering</span><br><span class="line"><span class="meta">@total_ordering</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">node</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,val,freq=<span class="number">1</span>,s=<span class="number">1</span>,left=None,right=None)</span>:</span></span><br><span class="line">        self.val=val</span><br><span class="line">        self.freq=freq</span><br><span class="line">        self.s=s</span><br><span class="line">        <span class="keyword">if</span> left <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> right <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">            self.left = left <span class="keyword">if</span> left <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">else</span> right</span><br><span class="line">            self.right =<span class="keyword">None</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">if</span> left.s&lt;right.s:</span><br><span class="line">                left,right =right, left</span><br><span class="line">            self.left=left</span><br><span class="line">            self.right=right</span><br><span class="line">            self.s+=self.right.s</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__eq__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val==nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__lt__</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.val&lt;nd.val</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">'node(val=%d,freq=%d,s=%d)'</span>%(self.val,self.freq,self.s)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">leftHeap</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,root=None)</span>:</span></span><br><span class="line">        self.root=root</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.root <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span></span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_merge</span><span class="params">(root,t)</span>:</span>  <span class="comment">#-&gt; int</span></span><br><span class="line">        <span class="keyword">if</span> root <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span> t</span><br><span class="line">        <span class="keyword">if</span> t <span class="keyword">is</span> <span class="keyword">None</span>:<span class="keyword">return</span> root</span><br><span class="line">        <span class="keyword">if</span> root&lt;t:</span><br><span class="line">            root,t=t,root</span><br><span class="line">        root.right = leftHeap._merge(root.right,t)</span><br><span class="line">        <span class="keyword">if</span> root.left <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> root.right <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">            root.s=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> root.left <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">                root.left,root.right = root.right,<span class="keyword">None</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">if</span> root.left.s&lt;root.right.s:</span><br><span class="line">                root.left,root.right = root.right,root.left</span><br><span class="line">            root.s = root.right.s+<span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> root</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">insert</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> isinstance(nd,node):nd = node(nd)</span><br><span class="line">        <span class="keyword">if</span> self.root <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">            self.root=nd</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">if</span> self.root==nd:</span><br><span class="line">            self.root.freq+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        prt =self. _findPrt(self.root,nd,<span class="keyword">None</span>)</span><br><span class="line">        <span class="keyword">if</span> prt <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">            self.root=leftHeap._merge(self.root,nd)</span><br><span class="line">        <span class="keyword">else</span> :</span><br><span class="line">            <span class="keyword">if</span> prt.left==nd:</span><br><span class="line">                prt.left.freq+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">else</span>:prt.right.freq+=<span class="number">1</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">remove</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> isinstance(nd,node):nd = node(nd)</span><br><span class="line">        <span class="keyword">if</span> self.root==nd:</span><br><span class="line">            self.root=leftHeap._merge(self.root.left,self.root.right)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            prt = self._findPrt(self.root,nd,<span class="keyword">None</span>)</span><br><span class="line">            <span class="keyword">if</span> prt <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</span><br><span class="line">                <span class="keyword">if</span> prt.left==nd:</span><br><span class="line">                    prt.left=leftHeap._merge(prt.left.left,prt.left.right)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    prt.right=leftHeap._merge(prt.right.left,prt.right.right)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">find</span><span class="params">(self,nd)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> isinstance(nd,node):nd = node(nd)</span><br><span class="line">        prt = self._findPrt(self.root,nd,self.root)</span><br><span class="line">        <span class="keyword">if</span> prt <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> prt==nd:<span class="keyword">return</span> prt</span><br><span class="line">        <span class="keyword">elif</span> prt.left==nd:<span class="keyword">return</span> prt.left</span><br><span class="line">        <span class="keyword">else</span>:<span class="keyword">return</span> prt.right</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_findPrt</span><span class="params">(self,root,nd,parent)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> isinstance(nd,node):nd = node(nd)</span><br><span class="line">        <span class="keyword">if</span> root <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">or</span> root&lt;nd:<span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">        <span class="keyword">if</span> root==nd:<span class="keyword">return</span> parent</span><br><span class="line">        l=self._findPrt(root.left,nd,root)</span><br><span class="line">        <span class="keyword">return</span>  l <span class="keyword">if</span> l <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span> <span class="keyword">else</span> self._findPrt(root.right,nd,root)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getTop</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.root</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">pop</span><span class="params">(self)</span>:</span></span><br><span class="line">        nd = self.root</span><br><span class="line">        self.remove(self.root.val)</span><br><span class="line">        <span class="keyword">return</span> nd</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">levelTraverse</span><span class="params">(self)</span>:</span></span><br><span class="line">        li = [(self.root,<span class="number">0</span>)]</span><br><span class="line">        cur=<span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> li:</span><br><span class="line">            nd,lv = li.pop(<span class="number">0</span>)</span><br><span class="line">            <span class="keyword">if</span> cur&lt;lv:</span><br><span class="line">                cur=lv</span><br><span class="line">                print()</span><br><span class="line">                print(nd,end=<span class="string">' '</span>)</span><br><span class="line">            <span class="keyword">else</span>:print(nd,end=<span class="string">' '</span>)</span><br><span class="line">            <span class="keyword">if</span> nd.left <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:li.append((nd.left,lv+<span class="number">1</span>))</span><br><span class="line">            <span class="keyword">if</span> nd.right <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:li.append((nd.right,lv+<span class="number">1</span>))</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;!-- TOC --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#1-概念&quot;&gt;1. 概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-二叉查找树&quot;&gt;2. 二叉查找树&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#21-随机构造的二叉查找树&quot;&gt;2.1. 随机构造的二叉查找树&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#22-平均结点深度&quot;&gt;2.2. 平均结点深度&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#23-不同的二叉树数目catalan-num&quot;&gt;2.3. 不同的二叉树数目(Catalan num)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#24-好括号列&quot;&gt;2.4. 好括号列&lt;/a&gt;
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="数据结构" scheme="https://mbinary.xyz/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
      <category term="树" scheme="https://mbinary.xyz/tags/%E6%A0%91/"/>
    
  </entry>
  
  <entry>
    <title>『数据结构』散列表</title>
    <link href="https://mbinary.xyz/hashTable.html"/>
    <id>https://mbinary.xyz/hashTable.html</id>
    <published>2018-07-08T23:25:00.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-关键字">1. 关键字</a></li><li><a href="#2-映射">2. 映射</a><ul><li><a href="#21-散列函数hash">2.1. 散列函数(hash)</a><ul><li><a href="#211-简单一致散列">2.1.1. 简单一致散列</a></li><li><a href="#212-碰撞collision">2.1.2. 碰撞(collision)</a></li><li><a href="#213-str2int-的方法">2.1.3. str2int 的方法</a></li></ul></li><li><a href="#22-直接寻址法">2.2. 直接寻址法</a></li><li><a href="#23-链接法">2.3. 链接法</a><ul><li><a href="#231-全域散列universal-hashing">2.3.1. 全域散列(universal hashing)</a><ul><li><a href="#2311-定义">2.3.1.1. 定义</a></li><li><a href="#2312-性质">2.3.1.2. 性质</a></li><li><a href="#2313-实现">2.3.1.3. 实现</a></li></ul></li></ul></li><li><a href="#24-开放寻址法">2.4. 开放寻址法</a><ul><li><a href="#241-不成功查找的探查数的期望">2.4.1. 不成功查找的探查数的期望</a><ul><li><a href="#2411-插入探查数的期望">2.4.1.1. 插入探查数的期望</a></li><li><a href="#2412-成功查找的探查数的期望">2.4.1.2. 成功查找的探查数的期望</a></li></ul></li></ul></li></ul></li></ul><!-- /TOC --><p>哈希表 (hash table) , 可以实现 $O(1)$ 的 read, write, update<br>相对应 python 中的 dict, c语言中的 map</p><p>其实数组也能实现, 只是数组用来索引的关键字是下标, 是整数.<br>而哈希表就是将各种关键字映射到数组下标的一种”数组”</p><p><a id="markdown-1-关键字" name="1-关键字"></a></p><h1 id="1-关键字"><a href="#1-关键字" class="headerlink" title="1. 关键字"></a>1. 关键字</h1><p>由于关键字是用来索引数据的, 所以要求它不能变动(如果变动,实际上就是一个新的关键字插入了), 在python 中表现为 immutable. 常为字符串.</p><p><a id="markdown-2-映射" name="2-映射"></a></p><h1 id="2-映射"><a href="#2-映射" class="headerlink" title="2. 映射"></a>2. 映射</h1><p><a id="markdown-21-散列函数hash" name="21-散列函数hash"></a></p><h2 id="2-1-散列函数-hash"><a href="#2-1-散列函数-hash" class="headerlink" title="2.1. 散列函数(hash)"></a>2.1. 散列函数(hash)</h2><p>将关键字 k 进行映射, 映射函数 $h$, 映射后的数组地址 $h(k)$.</p><p><a id="markdown-211-简单一致散列" name="211-简单一致散列"></a></p><h3 id="2-1-1-简单一致散列"><a href="#2-1-1-简单一致散列" class="headerlink" title="2.1.1. 简单一致散列"></a>2.1.1. 简单一致散列</h3><blockquote><ul><li>简单一致假设:元素散列到每个链表的可能性是相同的, 且与其他已被散列的元素独立无关.</li><li>简单一致散列(simple uniform hashing): 满足简单一致假设的散列</li></ul></blockquote><p>好的散列函数应 满足简单一致假设<br>例如</p><script type="math/tex; mode=display">\begin{aligned}&(1) \text{除法散列} \quad h(k) = k \ mod\ m \\&(2) \text{乘法散列} \quad h(k) = \lfloor {m(kA \ mod\  1)\rfloor}  \text{,(0< A<  1)}\\&\quad\text{任何 A 都适用,最佳的选择与散列的数据特征有关.}\\&\quad\text{  Knuth 认为,最理想的是黄金分割数}\frac{\sqrt{5} -1}{2} \approx 0.618\end{aligned}</script><p><a id="markdown-212-碰撞collision" name="212-碰撞collision"></a></p><h3 id="2-1-2-碰撞-collision"><a href="#2-1-2-碰撞-collision" class="headerlink" title="2.1.2. 碰撞(collision)"></a>2.1.2. 碰撞(collision)</h3><p> 由于关键字值域大于映射后的地址值域, 所以可能出现两个关键字有相同的映射地址</p><p><a id="markdown-213-str2int-的方法" name="213-str2int-的方法"></a></p><h3 id="2-1-3-str2int-的方法"><a href="#2-1-3-str2int-的方法" class="headerlink" title="2.1.3. str2int 的方法"></a>2.1.3. str2int 的方法</h3><p>可以先用 ascii 值,然后</p><ul><li>各位相加</li><li>两位叠加</li><li>循环移位</li><li>…</li></ul><p><a id="markdown-22-直接寻址法" name="22-直接寻址法"></a></p><h2 id="2-2-直接寻址法"><a href="#2-2-直接寻址法" class="headerlink" title="2.2. 直接寻址法"></a>2.2. 直接寻址法</h2><p>将关键字直接对应到数组地址, 即 $h(k)=k$</p><p>缺点: 如果关键字值域范围大, 但是数量小, 就会浪费空间, 有可能还不能储存这么大的值域范围.</p><p><a id="markdown-23-链接法" name="23-链接法"></a></p><h2 id="2-3-链接法"><a href="#2-3-链接法" class="headerlink" title="2.3. 链接法"></a>2.3. 链接法</h2><p>通过链接法来解决碰撞</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/hashTable-1.png" alt="hashTable-1"></p><p>记有 m 个链表, n 个元素 $\alpha = \frac{n}{m}$ 为每个链表的期望元素个数(长度)</p><p>则查找成功,或者不成功的时间复杂度为 $\Theta(1+\alpha)$<br>如果 $n=O(m), namely \quad \alpha=\frac{O(m)}{m}=O(1)$, 则上面的链接法满足 $O(1)$的速度</p><p><a id="markdown-231-全域散列universal-hashing" name="231-全域散列universal-hashing"></a></p><h3 id="2-3-1-全域散列-universal-hashing"><a href="#2-3-1-全域散列-universal-hashing" class="headerlink" title="2.3.1. 全域散列(universal hashing)"></a>2.3.1. 全域散列(universal hashing)</h3><p> 随机地选择散列函数, 使之独立于要存储的关键字<br><a id="markdown-2311-定义" name="2311-定义"></a></p><h4 id="2-3-1-1-定义"><a href="#2-3-1-1-定义" class="headerlink" title="2.3.1.1. 定义"></a>2.3.1.1. 定义</h4><p>设一组散列函数 $H=\{h_1,h_2,\ldots,h_i\}$, 将 关键字域 U 映射到 $\{0,1,\ldots,m-1\}$ , 全域的函数组, 满足</p><script type="math/tex; mode=display">for \ k \neq l \ \in U, h(k) = h(l), \text{这样的 h 的个数不超过}\frac{|H|}{m}</script><p>即从 H 中任选一个散列函数, 当关键字不相等时, 发生碰撞的概率不超过 $\frac{1}{m}$</p><p><a id="markdown-2312-性质" name="2312-性质"></a></p><h4 id="2-3-1-2-性质"><a href="#2-3-1-2-性质" class="headerlink" title="2.3.1.2. 性质"></a>2.3.1.2. 性质</h4><p>对于 m 个槽位的表, 只需 $\Theta(n)$的期望时间来处理 n 个元素的 insert, search, delete,其中  有$O(m)$个insert 操作<br><a id="markdown-2313-实现" name="2313-实现"></a></p><h4 id="2-3-1-3-实现"><a href="#2-3-1-3-实现" class="headerlink" title="2.3.1.3. 实现"></a>2.3.1.3. 实现</h4><p>选择足够大的 prime p, 记 $Z_p=\{0,1,\ldots,p-1\}$,  $Z_p^{<em>}=\{1,\ldots,p-1\}$<br>令$h_{a,b}(k) = ((ak+b)mod\ p) mod\ m$<br>则 $H_{p,m}=\{h_{a,b}|a\in Z_p^{</em>},b\in Z_p\}$</p><p>每一个散列函数 $h_{a,b}$ 都将 $Z_p$ 映射到 $Z_m$, m 可以是任意的, 不用是一个素数<br><a id="markdown-24-开放寻址法" name="24-开放寻址法"></a></p><h2 id="2-4-开放寻址法"><a href="#2-4-开放寻址法" class="headerlink" title="2.4. 开放寻址法"></a>2.4. 开放寻址法</h2><p>所有表项都在散列表中, 没有链表.<br>且散列表装载因子$\alpha=\frac{n}{m}\leqslant1$<br>这里散列函数再接受一个参数, 作为探测序号<br>逐一试探 $h(k,0),h(k,1),\ldots,h(k,m-1)$,这要有满足的,就插入, 不再计算后面的 hash值</p><p>探测序列一般分有三种</p><ul><li>线性$\ 0,1,\ldots,m-1$</li></ul><p>存在一次聚集问题</p><ul><li>二次$\ 0,1,\ldots,(m-1)^2$</li></ul><p>存在二次聚集问题</p><ul><li>双重探查</li></ul><p>$h(k,i) = (h_1(k)+i*h_2(k))mod\ m$<br>为了能查找整个表, 即要为模 m 的完系, 则 h_2(k)要与 m 互质.<br>如可以取 $h_1(k) = k\ mod \ m,h_2(k) = 1+(k\ mod\ {m-1})$</p><p>注意删除时, 不能直接删除掉(如果有元素插入在其后插入时探测过此地址,删除后就不能访问到那个元素了), 应该 只是做个标记为删除</p><p><a id="markdown-241-不成功查找的探查数的期望" name="241-不成功查找的探查数的期望"></a></p><h3 id="2-4-1-不成功查找的探查数的期望"><a href="#2-4-1-不成功查找的探查数的期望" class="headerlink" title="2.4.1. 不成功查找的探查数的期望"></a>2.4.1. 不成功查找的探查数的期望</h3><p>对于开放寻址散列表,且 $\alpha&lt;1$,一次不成功的查找,是这样的: 已经装填了 n 个, 总共有m 个,则空槽有 m-n 个.<br>不成功的探查是这样的: 一直探查到已经装填的元素(但是不是要找的元素),  直到遇到没有装填的空槽. 所以这服从几何分布, 即</p><script type="math/tex; mode=display">p(\text{不成功探查})=p(\text{第一次找到空槽})=\frac{m-n}{m}</script><p> 有</p><script type="math/tex; mode=display">E(\text{探查数})=\frac{1}{p}\leqslant \frac{1}{1-\alpha}</script><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/hashTable-2.png" alt="hashTable-2"></p><p><a id="markdown-2411-插入探查数的期望" name="2411-插入探查数的期望"></a></p><h4 id="2-4-1-1-插入探查数的期望"><a href="#2-4-1-1-插入探查数的期望" class="headerlink" title="2.4.1.1. 插入探查数的期望"></a>2.4.1.1. 插入探查数的期望</h4><p>所以, 插入一个关键字, 也最多需要 $\frac{1}{1-\alpha}$次, 因为插入过程就是前面都是被占用了的槽, 最后遇到一个空槽.与探查不成功是一样的过程<br><a id="markdown-2412-成功查找的探查数的期望" name="2412-成功查找的探查数的期望"></a></p><h4 id="2-4-1-2-成功查找的探查数的期望"><a href="#2-4-1-2-成功查找的探查数的期望" class="headerlink" title="2.4.1.2. 成功查找的探查数的期望"></a>2.4.1.2. 成功查找的探查数的期望</h4><p>成功查找的探查过程与插入是一样的. 所以查找关键字 k 相当于 插入它, 设为第 i+1 个插入的(前面插入了i个,装载因子$\alpha=\frac{i}{m}$. 那么期望探查数就是 </p><script type="math/tex; mode=display">\frac{1}{1-\alpha}=\frac{1}{1-\frac{i}{m}}=\frac{m}{m-i}</script><p>则成功查找的期望探查数为</p><script type="math/tex; mode=display">\begin{aligned}\frac{1}{n}\sum_{i=0}^{n-1}\frac{m}{m-i}=\frac{m}{n}\sum_{i=0}^{n-1}\frac{1}{m-i} &= \frac{m}{n}\sum_{i=m-n+1}^{m}\frac{1}{i}\\&\leqslant  \frac{1}{\alpha} \int_{m-n}^m\frac{1}{x}dx\\&=\frac{1}{\alpha}ln\frac{1}{1-\alpha}\end{aligned}</script><p>代码</p><p><strong><a href="https://github.com/mbinary/algorithm.git" target="_blank" rel="noopener">github地址</a></strong><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">item</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,key,val,nextItem=None)</span>:</span></span><br><span class="line">        self.key = key</span><br><span class="line">        self.val = val</span><br><span class="line">        self.next = nextItem</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">to</span><span class="params">(self,it)</span>:</span></span><br><span class="line">        self.next = it</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__eq__</span><span class="params">(self,it)</span>:</span></span><br><span class="line">        <span class="string">'''using  keyword &lt;in&gt; '''</span></span><br><span class="line">        <span class="keyword">return</span> self.key == it.key</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.key <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        li = []</span><br><span class="line">        nd = self</span><br><span class="line">        <span class="keyword">while</span> nd:</span><br><span class="line">            li.append(<span class="string">f'(<span class="subst">&#123;nd.key&#125;</span>:<span class="subst">&#123;nd.val&#125;</span>)'</span>)</span><br><span class="line">            nd = nd.next</span><br><span class="line">        <span class="keyword">return</span> <span class="string">' -&gt; '</span>.join(li)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">f'item(<span class="subst">&#123;self.key&#125;</span>,<span class="subst">&#123;self.val&#125;</span>)'</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">hashTable</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,size=<span class="number">100</span>)</span>:</span></span><br><span class="line">        self.size = size</span><br><span class="line">        self.slots=[item(<span class="keyword">None</span>,<span class="keyword">None</span>) <span class="keyword">for</span> i <span class="keyword">in</span> range(self.size)]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__setitem__</span><span class="params">(self,key,val)</span>:</span></span><br><span class="line">        nd = self.slots[self.myhash(key)]</span><br><span class="line">        <span class="keyword">while</span> nd.next:</span><br><span class="line">            <span class="keyword">if</span> nd.key ==key:</span><br><span class="line">                <span class="keyword">if</span> nd.val!=val: nd.val=val</span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line">            nd  = nd.next</span><br><span class="line">        nd.next = item(key,val)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">myhash</span><span class="params">(self,key)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> isinstance(key,str):</span><br><span class="line">            key = sum(ord(i) <span class="keyword">for</span> i <span class="keyword">in</span> key)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> isinstance(key,int):</span><br><span class="line">            key = hash(key)</span><br><span class="line">        <span class="keyword">return</span> key % self.size</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="string">'''when using keyword &lt;in&gt;, such as ' if key in dic',</span></span><br><span class="line"><span class="string">            the dic's  __iter__ method will be called,(if hasn't, calls __getitem__</span></span><br><span class="line"><span class="string">            then  ~iterate~  dic's keys to compare whether one equls to the key</span></span><br><span class="line"><span class="string">        '''</span></span><br><span class="line">        <span class="keyword">for</span> nd <span class="keyword">in</span> self.slots:</span><br><span class="line">            nd = nd.next</span><br><span class="line">            <span class="keyword">while</span> nd :</span><br><span class="line">                <span class="keyword">yield</span> nd.key</span><br><span class="line">                nd = nd.next</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self,key)</span>:</span></span><br><span class="line">        nd =self.slots[ self.myhash(key)].next</span><br><span class="line">        <span class="keyword">while</span> nd:</span><br><span class="line">            <span class="keyword">if</span> nd.key==key:</span><br><span class="line">                <span class="keyword">return</span> nd.val</span><br><span class="line">            nd = nd.next</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">f'[KeyError]: <span class="subst">&#123;self.__class__.__name__&#125;</span> has no key <span class="subst">&#123;key&#125;</span>'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__delitem__</span><span class="params">(self,key)</span>:</span></span><br><span class="line">        <span class="string">'''note that None item and item(None,None) differ with each other,</span></span><br><span class="line"><span class="string">            which means you should take care of them and correctly cop with None item</span></span><br><span class="line"><span class="string">            especially when deleting items</span></span><br><span class="line"><span class="string">        '''</span></span><br><span class="line">        n = self.myhash(key)</span><br><span class="line">        nd = self.slots[n].next</span><br><span class="line">        <span class="keyword">if</span> nd.key == key:</span><br><span class="line">            <span class="keyword">if</span> nd.next <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line">                self.slots[n] =  item(<span class="keyword">None</span>,<span class="keyword">None</span>) <span class="comment"># be careful</span></span><br><span class="line">            <span class="keyword">else</span>:self.slots[n] = nd.next</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">while</span> nd:</span><br><span class="line">            <span class="keyword">if</span> nd.next <span class="keyword">is</span> <span class="keyword">None</span>: <span class="keyword">break</span>  <span class="comment"># necessary</span></span><br><span class="line">            <span class="keyword">if</span> nd.next.key ==key:</span><br><span class="line">                nd.next = nd.next.next</span><br><span class="line">            nd = nd.next</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line">        li = [<span class="string">'\n\n'</span>+<span class="string">'-'</span>*<span class="number">5</span>+<span class="string">'hashTable'</span>+<span class="string">'-'</span>*<span class="number">5</span>]</span><br><span class="line">        <span class="keyword">for</span> i,nd <span class="keyword">in</span> enumerate(self.slots):</span><br><span class="line">            li.append(<span class="string">f'<span class="subst">&#123;i&#125;</span>: '</span>+str(nd.next))</span><br><span class="line">        <span class="keyword">return</span> <span class="string">'\n'</span>.join(li)</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      散列表的原理与实现, 包括直接寻址, 链接法, 开放寻址法等
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="数据结构" scheme="https://mbinary.xyz/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
      <category term="散列表" scheme="https://mbinary.xyz/tags/%E6%95%A3%E5%88%97%E8%A1%A8/"/>
    
  </entry>
  
  <entry>
    <title>『算法』排序</title>
    <link href="https://mbinary.xyz/sort.html"/>
    <id>https://mbinary.xyz/sort.html</id>
    <published>2018-07-06T00:00:00.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#1-希尔排序shellsort">1. 希尔排序(shellSort)</a></li><li><a href="#2-堆排序heapsort">2. 堆排序(heapSort)</a><ul><li><a href="#21-建堆">2.1. 建堆</a></li><li><a href="#22-访问最元">2.2. 访问最元</a></li><li><a href="#23-取出最元">2.3. 取出最元</a></li><li><a href="#24-堆排序">2.4. 堆排序</a></li></ul></li><li><a href="#3-快速排序quicksort">3. 快速排序(quickSort)</a><ul><li><a href="#31-partition的实现">3.1. partition的实现</a></li><li><a href="#32-选择枢纽元">3.2. 选择枢纽元</a></li><li><a href="#33-快速排序的性能">3.3. 快速排序的性能</a><ul><li><a href="#331-最坏情况">3.3.1. 最坏情况</a></li><li><a href="#332-最佳情况">3.3.2. 最佳情况</a></li><li><a href="#333-平衡的划分">3.3.3. 平衡的划分</a></li></ul></li><li><a href="#34-期望运行时间">3.4. 期望运行时间</a></li><li><a href="#35-堆栈深度">3.5. 堆栈深度</a></li><li><a href="#36-测试">3.6. 测试</a></li></ul></li><li><a href="#4-计数排序countsort">4. 计数排序(countSort)</a></li><li><a href="#5-基数排序radixsort">5. 基数排序(radixSort)</a><ul><li><a href="#51-原理">5.1. 原理</a></li><li><a href="#52-实现">5.2. 实现</a></li><li><a href="#53-扩展">5.3. 扩展</a></li><li><a href="#54-测试">5.4. 测试</a></li></ul></li><li><a href="#6-桶排序bucketsort">6. 桶排序(bucketSort)</a></li><li><a href="#7-选择问题select">7. 选择问题(select)</a></li></ul><!-- /TOC --><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/sort-1.png" alt="sort-1"></p><p>排序的本质就是减少逆序数, 根据是否进行比较,可以分为如下两类.</p><ul><li>比较排序</li></ul><p>如希尔排序,堆排序, 快速排序, 合并排序等<br>可以证明 比较排序的下界 是 $\Omega(nlogn)$</p><ul><li>非比较排序</li></ul><p>如 计数排序, 桶排序, 基数排序 不依靠比较来进行排序的, 可以达到 线性时间的复杂度</p><p><a id="markdown-1-希尔排序shellsort" name="1-希尔排序shellsort"></a></p><h1 id="1-希尔排序-shellSort"><a href="#1-希尔排序-shellSort" class="headerlink" title="1. 希尔排序(shellSort)"></a>1. 希尔排序(shellSort)</h1><p>希尔排序是选择排序的改进, 通过在较远的距离进行交换, 可以更快的减少逆序数. 这个距离即增量, 由自己选择一组, 从大到小进行, 而且最后一个增量必须是  1. 要选得到好的性能, 一般选择$2^k-1$<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">def shellSort(s,inc = None):</span><br><span class="line">    if inc is None: inc = [1,3,5,7,11,13,17,19]</span><br><span class="line">    num = len(s)</span><br><span class="line">    inc.sort(reverse=True)</span><br><span class="line">    for i in inc:</span><br><span class="line">        for j in range(i,num):</span><br><span class="line">            cur = j</span><br><span class="line">            while cur&gt;=i and s[j] &gt; s[cur-i]:</span><br><span class="line">                s[cur] = s[cur-i]</span><br><span class="line">                cur-=i</span><br><span class="line">            s[cur] = s[j]</span><br><span class="line">    return s</span><br></pre></td></tr></table></figure></p><p>可以证明 希尔排序时间复杂度可以达到$O(n^{\frac{4}{3}})$<br><a id="markdown-2-堆排序heapsort" name="2-堆排序heapsort"></a></p><h1 id="2-堆排序-heapSort"><a href="#2-堆排序-heapSort" class="headerlink" title="2. 堆排序(heapSort)"></a>2. 堆排序(heapSort)</h1><p><a id="markdown-21-建堆" name="21-建堆"></a></p><h2 id="2-1-建堆"><a href="#2-1-建堆" class="headerlink" title="2.1. 建堆"></a>2.1. 建堆</h2><p>是将一个数组(列表) heapify 的过程. 方法就是对每一个结点, 都自底向上的比较,然后操作,这个过程称为 上浮.<br>粗略的计算, 每个结点上浮的比较次数的上界是 层数, 即 logn, 则 n 个结点, 总的比较次数为 nlogn<br>但是可以发现, 不同高度 h 的结点比较的次数不同, 上界实际上应该是 $O(h)$,每层结点数上界 $\lfloor 2^h \rfloor$<br>则 总比较次数为 </p><script type="math/tex; mode=display">\begin{aligned}\sum_{h=1}^{\lfloor{log_2 n}\rfloor} O(h)\lceil 2^{h} \rceil  & = \sum_{h=0}^{ {log_2 n}-1} O(h\frac{n}{2^h})\\                                                          & = n*O(\sum_{h=0}^{log_2 n}\frac{h}{2^h}) \\                                                              & = n*O(1)   \\                                                          & = O(n)  \end{aligned}</script><p><a id="markdown-22-访问最元" name="22-访问最元"></a></p><h2 id="2-2-访问最元"><a href="#2-2-访问最元" class="headerlink" title="2.2. 访问最元"></a>2.2. 访问最元</h2><p>最大堆对应最大元,最小堆对于最小元, 可以 $O(1)$ 内实现<br><a id="markdown-23-取出最元" name="23-取出最元"></a></p><h2 id="2-3-取出最元"><a href="#2-3-取出最元" class="headerlink" title="2.3. 取出最元"></a>2.3. 取出最元</h2><p>最大堆取最大元,最小堆取最小元,由于元素取出了, 要进行调整.<br>从堆顶开始, 依次和其两个孩子比较, 如果是最大堆, 就将此结点(父亲)的值赋为较大的孩子的值,最小堆反之.<br>然后对那个孩子进行同样的操作,一直到达堆底,即最下面的一层. 这个过程称为 下滤.<br>最后将最后一个元素与最下面一层那个元素(与上一层交换的)交换, 再删除最后一个元素.<br>时间复杂度为 $O(logn)$<br><a id="markdown-24-堆排序" namie="24-堆排序"></a></p><h2 id="2-4-堆排序"><a href="#2-4-堆排序" class="headerlink" title="2.4. 堆排序"></a>2.4. 堆排序</h2><p>建立堆之后, 一直进行 <code>取出最元</code>操作, 即得有序序列</p><p>代码<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> partial</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">heap</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,lst,reverse = False)</span>:</span></span><br><span class="line">        self.data= heapify(lst,reverse)</span><br><span class="line">        self.cmp = partial(<span class="keyword">lambda</span> i,j,r:cmp(self.data[i],self.data[j],r),r=  reverse)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getTop</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.data[<span class="number">0</span>]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self,idx)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.data[idx]</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.data != []</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">popTop</span><span class="params">(self)</span>:</span></span><br><span class="line">        ret = self.data[<span class="number">0</span>]</span><br><span class="line">        n = len(self.data)</span><br><span class="line">        cur = <span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> cur * <span class="number">2</span>&lt;=n:</span><br><span class="line">            chd = cur<span class="number">-1</span></span><br><span class="line">            r_idx = cur*<span class="number">2</span></span><br><span class="line">            l_idx = r_idx<span class="number">-1</span></span><br><span class="line">            <span class="keyword">if</span> r_idx==n:</span><br><span class="line">                self.data[chd] = self.data[l_idx]</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            j = l_idx <span class="keyword">if</span> self.cmp(l_idx,r_idx)&lt;<span class="number">0</span> <span class="keyword">else</span> r_idx</span><br><span class="line">            self.data[chd] = self.data[j]</span><br><span class="line">            cur = j+<span class="number">1</span></span><br><span class="line">        self.data[cur<span class="number">-1</span>] = self.data[<span class="number">-1</span>]</span><br><span class="line">        self.data.pop()</span><br><span class="line">        <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">addNode</span><span class="params">(self,val)</span>:</span></span><br><span class="line">        self.data.append(val)</span><br><span class="line">        self.data = one_heapify(len(self.data)<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cmp</span><span class="params">(n1,n2,reverse=False)</span>:</span></span><br><span class="line">    fac = <span class="number">-1</span> <span class="keyword">if</span> reverse <span class="keyword">else</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">if</span> n1 &lt; n2: <span class="keyword">return</span> -fac</span><br><span class="line">    <span class="keyword">elif</span> n1 &gt; n2: <span class="keyword">return</span> fac</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">heapify</span><span class="params">(lst,reverse = False)</span>:</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(len(lst)):</span><br><span class="line">        lst = one_heapify(lst,i,reverse)</span><br><span class="line">    <span class="keyword">return</span> lst</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">one_heapify</span><span class="params">(lst,cur,reverse = False)</span>:</span></span><br><span class="line">    cur +=<span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> cur&gt;<span class="number">1</span>:</span><br><span class="line">        chd = cur<span class="number">-1</span></span><br><span class="line">        prt = cur//<span class="number">2</span><span class="number">-1</span></span><br><span class="line">        <span class="keyword">if</span> cmp(lst[prt],lst[chd],reverse)&lt;<span class="number">0</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        lst[prt],lst[chd] = lst[chd], lst[prt]</span><br><span class="line">        cur = prt+<span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> lst</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">heapSort</span><span class="params">(lst,reverse = False)</span>:</span></span><br><span class="line">    lst = lst.copy()</span><br><span class="line">    hp = heap(lst,reverse)</span><br><span class="line">    ret = []</span><br><span class="line">    <span class="keyword">while</span> hp:</span><br><span class="line">        ret.append(hp.popTop())</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    <span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line">    n = randint(<span class="number">10</span>,<span class="number">20</span>)</span><br><span class="line">    lst = [randint(<span class="number">0</span>,<span class="number">100</span>) <span class="keyword">for</span> i <span class="keyword">in</span> range(n)]</span><br><span class="line">    print(<span class="string">'random    : '</span>, lst)</span><br><span class="line">    print(<span class="string">'small-heap: '</span>, heapify(lst))</span><br><span class="line">    print(<span class="string">'big-heap  : '</span>, heapify(lst,<span class="keyword">True</span>))</span><br><span class="line">    print(<span class="string">'ascend    : '</span>, heapSort(lst))</span><br><span class="line">    print(<span class="string">'descend   : '</span>, heapSort(lst,<span class="keyword">True</span>))</span><br></pre></td></tr></table></figure></p><p><a id="markdown-3-快速排序quicksort" name="3-快速排序quicksort"></a></p><h1 id="3-快速排序-quickSort"><a href="#3-快速排序-quickSort" class="headerlink" title="3. 快速排序(quickSort)"></a>3. 快速排序(quickSort)</h1><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">quickSort</span><span class="params">(lst)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_sort</span><span class="params">(a,b)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> a&gt;=b:<span class="keyword">return</span> </span><br><span class="line">        CHOOSE PIVOT <span class="comment">#选取适当的枢纽元, 一般是三数取中值</span></span><br><span class="line">        pos = partition(a,b)</span><br><span class="line">        _sort(a,pos<span class="number">-1</span>)</span><br><span class="line">        _sort(pos+<span class="number">1</span>,b)</span><br><span class="line">    _sort(<span class="number">0</span>,len(lst))</span><br></pre></td></tr></table></figure><p>快排大体结构就是这样,使用分治的思想, 在原地进行排列.<br>关键就在于选择枢纽元.</p><p>这里的 partition 就是根据枢纽元,分别将 大于,小于或等于的枢纽元的元素放在列表两边, 分割开.<br><a id="markdown-31-partition的实现" name="31-partition的实现"></a></p><h2 id="3-1-partition的实现"><a href="#3-1-partition的实现" class="headerlink" title="3.1. partition的实现"></a>3.1. partition的实现</h2><p>partition 有不同的实现. 下面列出两种</p><ul><li>第一种实现</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">partition</span><span class="params">(a,b)</span>:</span></span><br><span class="line">    pivot = lst[a]</span><br><span class="line">    <span class="keyword">while</span> a!=b:</span><br><span class="line">        <span class="keyword">while</span> a&lt;b <span class="keyword">and</span> lst[b]&gt;pivot: b-=<span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> a&lt;b:</span><br><span class="line">            lst[a] = lst[b]</span><br><span class="line">            a+=<span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> a&lt;b <span class="keyword">and</span> lst[a]&lt;pivot: a+=<span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> a&lt;b:</span><br><span class="line">            lst[b] = lst[a]</span><br><span class="line">            b-=<span class="number">1</span></span><br><span class="line">    lst[a] = pivot</span><br><span class="line">    <span class="keyword">return</span> a</span><br></pre></td></tr></table></figure><ul><li>第二种实现</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">partition</span><span class="params">(a,b)</span>:</span></span><br><span class="line">    pivot = lst[b]</span><br><span class="line">    j = a<span class="number">-1</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(a,b):</span><br><span class="line">        <span class="keyword">if</span> lst[i]&lt;=pivot:</span><br><span class="line">            j+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> i!=j: lst[i], lst[j] = lst[j], lst[i]</span><br><span class="line">    lst[j+<span class="number">1</span>],lst[b] = lst[b],lst[j+<span class="number">1</span>]</span><br><span class="line">    <span class="keyword">return</span> j+<span class="number">1</span></span><br></pre></td></tr></table></figure><p>第二种是算法导论上的,可以发现,第二种交换赋值的次数比第一种要多,而且如果序列的逆序数较大,第二种一次交换减少的逆序数很少, 而第一种就比较多(交换的两个元素相距较远)<br>然后我用随机数测试了一下, 确实是第一种较快, 特别是要排序的序列较长时,如在 5000 个元素时, 第一种要比第二种快几倍, Amazing!</p><p>完整代码<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">quickSort</span><span class="params">(lst)</span>:</span></span><br><span class="line">    <span class="string">'''A optimized version of Hoare partition'''</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">partition</span><span class="params">(a,b)</span>:</span></span><br><span class="line">        pivot = lst[a]</span><br><span class="line">        <span class="keyword">while</span> a!=b:</span><br><span class="line">            <span class="keyword">while</span> a&lt;b <span class="keyword">and</span> lst[b]&gt;pivot: b-=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> a&lt;b:</span><br><span class="line">                lst[a] = lst[b]</span><br><span class="line">                a+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> a&lt;b <span class="keyword">and</span> lst[a]&lt;pivot: a+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> a&lt;b:</span><br><span class="line">                lst[b] = lst[a]</span><br><span class="line">                b-=<span class="number">1</span></span><br><span class="line">        lst[a] = pivot</span><br><span class="line">        <span class="keyword">return</span> a</span><br><span class="line">    <span class="function"><span class="keyword">def</span>  <span class="title">_sort</span><span class="params">(a,b)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> a&gt;=b:<span class="keyword">return</span> </span><br><span class="line">        mid = (a+b)//<span class="number">2</span></span><br><span class="line">        <span class="comment"># 三数取中值置于第一个作为 pivot</span></span><br><span class="line">        <span class="keyword">if</span> (lst[a]&lt;lst[mid]) ^ (lst[b]&lt;lst[mid]): lst[a],lst[mid] = lst[mid],lst[a]  <span class="comment"># lst[mid] 为中值</span></span><br><span class="line">        <span class="keyword">if</span> (lst[a]&lt;lst[b]) ^ (lst[b]&gt;lst[mid]): lst[a],lst[b] = lst[b],lst[a] <span class="comment"># lst[b] 为中值</span></span><br><span class="line">        i = partition(a,b)</span><br><span class="line">        _sort(a,i<span class="number">-1</span>)</span><br><span class="line">        _sort(i+<span class="number">1</span>,b)</span><br><span class="line">    _sort(<span class="number">0</span>,len(lst)<span class="number">-1</span>)</span><br><span class="line">    <span class="keyword">return</span> lst</span><br></pre></td></tr></table></figure></p><p><a id="markdown-32-选择枢纽元" name="32-选择枢纽元"></a></p><h2 id="3-2-选择枢纽元"><a href="#3-2-选择枢纽元" class="headerlink" title="3.2. 选择枢纽元"></a>3.2. 选择枢纽元</h2><ul><li>端点或中点</li><li>随机</li><li>三数取中(两端点以及中点)</li><li>五数取中</li></ul><p><a id="markdown-33-快速排序的性能" name="33-快速排序的性能"></a></p><h2 id="3-3-快速排序的性能"><a href="#3-3-快速排序的性能" class="headerlink" title="3.3. 快速排序的性能"></a>3.3. 快速排序的性能</h2><p>快速排序性能取决于划分的对称性(即枢纽元的选择), 以及partition 的实现. 如果每次划分很对称(大概在当前序列的中位数为枢纽元), 则与合并算法一样快, 但是如果不对称,在渐近上就和插入算法一样慢<br><a id="markdown-331-最坏情况" name="331-最坏情况"></a></p><h3 id="3-3-1-最坏情况"><a href="#3-3-1-最坏情况" class="headerlink" title="3.3.1. 最坏情况"></a>3.3.1. 最坏情况</h3><p>试想,如果每次划分两个区域分别包含 n-1, 1则易知时间复杂度为 $\Theta(n^2)$, 此外, 如果输入序序列已经排好序,且枢纽元没选好, 比如选的端点, 则同样是这样复杂, 而此时插入排序只需 $O(n)$.</p><p><a id="markdown-332-最佳情况" name="332-最佳情况"></a></p><h3 id="3-3-2-最佳情况"><a href="#3-3-2-最佳情况" class="headerlink" title="3.3.2. 最佳情况"></a>3.3.2. 最佳情况</h3><p>有 $T(n) = 2T(\frac{n}{2})+\Theta(n)$<br>则由主方法为$O(nlogn)$<br><a id="markdown-333-平衡的划分" name="333-平衡的划分"></a></p><h3 id="3-3-3-平衡的划分"><a href="#3-3-3-平衡的划分" class="headerlink" title="3.3.3. 平衡的划分"></a>3.3.3. 平衡的划分</h3><p>如果每次 9:1, $T(n) = T(\frac{9n}{10})+T(\frac{n}{10})+\Theta(n)$<br>用递归树求得在渐近上仍然是 $O(nlogn)$<br>所以任何比值 k:1, 都有如上的渐近时间复杂度</p><p>然而每次划分是不可能完全相同的</p><p><a id="markdown-34-期望运行时间" name="34-期望运行时间"></a></p><h2 id="3-4-期望运行时间"><a href="#3-4-期望运行时间" class="headerlink" title="3.4. 期望运行时间"></a>3.4. 期望运行时间</h2><p>对于 randomized-quicksort, 即随机选择枢纽元<br>设 n 个元素, 从小到大记为 $z_1,z_2,\ldots,z_n$,指示器变量 $X_{ij}$表示 $z_i,z_j$是否进行比较<br>即 </p><script type="math/tex; mode=display">X_{ij} = \begin{cases}1,\quad z_i,z_j\text{进行比较}\\0,\quad z_i,z_j\text{不进行比较}\end{cases}</script><p>考察比较次数, 可以发现两个元素进行比较, 一定是一个是枢纽元的情况, 两个元素间不可能进行两次比较.<br>所有总的比较次数不超过,$\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}X_{ij}$<br>求均值</p><script type="math/tex; mode=display">E(\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}X_{ij})=\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}E(X_{ij})=\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}P(z_i,z_j\text{进行比较})</script><p>再分析,$z_i,z_j$ 在$Z_{ij} =  \{z_i,z_{i+1},\ldots,z_j\} $中, 如果集合中的非此两元素,$z_k, i&lt; k&lt; j$作为了枢纽元, 则$z_k$将集合划分{z_i,z_{i+1},\ldots,z_{k-1}},{z_{k+1},\ldots,z_j}, 这两个集合中的元素都不会再和对方中的元素进行比较,<br>所以要使 $z_i,z_j$进行比较, 则两者之一(只能是一个,即互斥)是 $Z_{ij}$上的枢纽元<br>则</p><script type="math/tex; mode=display">\begin{aligned}P(z_i,z_j\text{进行比较}) & = P(z_i,z_j\text{做为}Z_{ij}\text{上的枢纽元})   \\                          & = P(z_j\text{做为}Z_{ij}\text{上的枢纽元})+P(z_i\text{做为}Z_{ij}\text{上的枢纽元})\\                          & = \frac{1}{j-i+1}+\frac{1}{j-i+1}   \\                          & = \frac{2}{j-i+1}\\\end{aligned}</script><p>注意第二步是因为两事件互斥才可以直接概率相加</p><p>然后就可以将此概率代入求期望比较次数了,<br>为 $O(nlogn)$ (由于是 O, 放缩一下就行)<br><a id="markdown-35-堆栈深度" name="35-堆栈深度"></a></p><h2 id="3-5-堆栈深度"><a href="#3-5-堆栈深度" class="headerlink" title="3.5. 堆栈深度"></a>3.5. 堆栈深度</h2><p>考察快速排序的堆栈深度,可以从递归树思考,实际上的堆栈变化过程就是前序访问二叉树, 所以深度为 $O(logn)$<br>为了减少深度, 可以进行 尾递归优化, 将函数返回前的递归通过迭代完成<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">QUICKSORT(A,a,b)</span><br><span class="line">    <span class="keyword">while</span> a&lt;b:</span><br><span class="line">        <span class="comment">#partition and sort left subarray</span></span><br><span class="line">        pos = partition(a,b)</span><br><span class="line">        QUICKSORT(A,a,pos<span class="number">-1</span>)</span><br><span class="line">        a = pos+<span class="number">1</span></span><br></pre></td></tr></table></figure></p><p><a id="markdown-36-测试" name="36-测试"></a></p><h2 id="3-6-测试"><a href="#3-6-测试" class="headerlink" title="3.6. 测试"></a>3.6. 测试</h2><p>这是上面三个版本的简单测试结果,<br>前面测试的是各函数用的时间, 后面打印出来的是体现正确性,用的另外的序列了<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/test.jpg.png" alt="test.jpg"></p><p><a id="markdown-4-计数排序countsort" name="4-计数排序countsort"></a></p><h1 id="4-计数排序-countSort"><a href="#4-计数排序-countSort" class="headerlink" title="4. 计数排序(countSort)"></a>4. 计数排序(countSort)</h1><p>需要知道元素的取值范围, 而且应该是有限的, 最好范围不大</p><p>不过需要额外的存储空间.<br><mark>计算排序是稳定的: 具有相同值的元素在输出中是原来的相对顺序.</mark><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">countSort</span><span class="params">(lst,mn,mx)</span>:</span></span><br><span class="line">    mark = [<span class="number">0</span>]*(mx-mn+<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> lst:</span><br><span class="line">        mark[i-mn]+=<span class="number">1</span></span><br><span class="line">    ret =[]</span><br><span class="line">    <span class="keyword">for</span> n,i <span class="keyword">in</span> enumerate(mark):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> range(i):</span><br><span class="line">            ret.append(n+mn)</span><br><span class="line">    <span class="keyword">return</span> ret</span><br></pre></td></tr></table></figure></p><p><a id="markdown-5-基数排序radixsort" name="5-基数排序radixsort"></a></p><h1 id="5-基数排序-radixSort"><a href="#5-基数排序-radixSort" class="headerlink" title="5. 基数排序(radixSort)"></a>5. 基数排序(radixSort)</h1><p><a id="markdown-51-原理" name="51-原理"></a></p><h2 id="5-1-原理"><a href="#5-1-原理" class="headerlink" title="5.1. 原理"></a>5.1. 原理</h2><p>由我们平时的直觉, 我们比较两个数时, 是从最高位比较起, 一位一位比较, 直到不相等时就能判断大小,或者相等(位数比完了).</p><p>基数排序有点不一样, 它是从低位比到高位, 这样才能把相同位有相同值的不同数排序.<br>对于 n 个数, 最高 d 位, 用下面的实现, 可时间复杂度为 $\Theta((n+d)*d)$</p><p><a id="markdown-52-实现" name="52-实现"></a></p><h2 id="5-2-实现"><a href="#5-2-实现" class="headerlink" title="5.2. 实现"></a>5.2. 实现</h2><p>下面是一个整数版本的基数排序,比较容易实现<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">radixSort</span><span class="params">(lst,radix=<span class="number">10</span>)</span>:</span></span><br><span class="line">    ls = [[] <span class="keyword">for</span> i <span class="keyword">in</span> range(radix)]</span><br><span class="line">    mx = max(lst)</span><br><span class="line">    weight =  <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> mx &gt;= weight:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> lst:</span><br><span class="line">            ls[(i // weight)%radix].append(i)</span><br><span class="line">        weight *= radix</span><br><span class="line">        lst =  sum(ls,[])</span><br><span class="line">        ls = [[] <span class="keyword">for</span>  i <span class="keyword">in</span> range(radix)]</span><br><span class="line">    <span class="keyword">return</span> lst</span><br></pre></td></tr></table></figure></p><p><a id="markdown-53-扩展" name="53-扩展"></a></p><h2 id="5-3-扩展"><a href="#5-3-扩展" class="headerlink" title="5.3. 扩展"></a>5.3. 扩展</h2><p>注意到如果有负数,要使用计数排序或者 基数排序,每个数需要加上最小值的相反数, 再排序, 最后再减去, 如果有浮点数, 就需要先乘以一个数, 使所有数变为整数.<br> 我想过用 str 得到一个数的各位, 不过 str 可能比较慢. str 的实现应该也是先算术计算, 再生成 str 对象, 对于基数排序, 生成str 对象是多余的.</p><p><a id="markdown-54-测试" name="54-测试"></a></p><h2 id="5-4-测试"><a href="#5-4-测试" class="headerlink" title="5.4. 测试"></a>5.4. 测试</h2><p>下面是 基数排序与快速排序的比较,测试代码<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> time <span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">timer</span><span class="params">(funcs,span,num=<span class="number">1000000</span>)</span>:</span></span><br><span class="line">    lst = [randint(<span class="number">0</span>,span) <span class="keyword">for</span> i <span class="keyword">in</span> range(num)]</span><br><span class="line">    print(<span class="string">'range(&#123;&#125;), &#123;&#125; items'</span>.format(span,num))</span><br><span class="line">    <span class="keyword">for</span> func <span class="keyword">in</span> funcs:</span><br><span class="line">        data = lst.copy()</span><br><span class="line">        t = time()</span><br><span class="line">        func(data)</span><br><span class="line">        t = time()-t</span><br><span class="line">        print(<span class="string">'&#123;&#125;: &#123;&#125;s'</span>.format(func.__name__,t))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    timer([quickSort,radixSort],<span class="number">1000000000</span>,<span class="number">100000</span>)</span><br><span class="line">    timer([quickSort,radixSort],<span class="number">1000000000000</span>,<span class="number">10000</span>)</span><br><span class="line">    timer([quickSort,radixSort],<span class="number">10000</span>,<span class="number">100000</span>)</span><br></pre></td></tr></table></figure></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/radixSort vs quickSort.png" alt="radixSort vs quickSort"></p><p><a id="markdown-6-桶排序bucketsort" name="6-桶排序bucketsort"></a></p><h1 id="6-桶排序-bucketSort"><a href="#6-桶排序-bucketSort" class="headerlink" title="6. 桶排序(bucketSort)"></a>6. 桶排序(bucketSort)</h1><p>适用于均匀分布的序列</p><p>设有 n 个元素, 则设立 n 个桶<br>将各元素通过数值线性映射到桶地址,<br>类似 hash 链表.<br>然后在每个桶内, 进行插入排序($O(n_i^2)$)<br>最后合并所有桶.<br>这里的特点是 n 个桶实现了 $\Theta(n)$的时间复杂度, 但是耗费的空间 为 $\Theta(n)$</p><p>证明</p><ul><li>线性映射部分: $\Theta(n)$</li><li>桶合并部分: $\Theta(n)$</li><li>桶内插入排序部分: 设每个桶内的元素数为随机变量 $n_i$, 易知 $n_i \sim  B(n,\frac{1}{n})$ 记 $p=\frac{1}{n}$</li></ul><script type="math/tex; mode=display">\begin{aligned}E(\sum_{i=1}^{n}n_i^2) &=\sum_{i=1}^{n}E(n_i^2)   \\&=\sum_{i=1}^{n}( Var(n_i)+E^2(n_i)  )  \\&= \sum_{i=1}^{n}( np(1-p)+ (np)^2 )\\&= \sum_{i=1}^{n}( 2-\frac{1}{n} )\\&= 2n-1\end{aligned}</script><p>将以上各部分加起来即得时间复杂度 $\Theta(n)$</p><p><a id="markdown-7-选择问题select" name="7-选择问题select"></a></p><h1 id="7-选择问题-select"><a href="#7-选择问题-select" class="headerlink" title="7. 选择问题(select)"></a>7. 选择问题(select)</h1><p>输入个序列 lst, 以及一个数 i, 输出 lst 中 第 i 小的数,即从小到大排列第 i</p><p>解决方法</p><ul><li>全部排序, 取第 i 个, $O(nlogn)$</li><li>长度为 i 的队列(这是得到 lst 中 前</li></ul><p>i 个元素的方法) 仍然 $O(nlogn)$</p><ul><li>randomized-select(仿造快排) 平均情况$O(n)$,最坏情况同上(快排), $\Theta(n^2)$</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">select</span><span class="params">(lst,i)</span>:</span></span><br><span class="line">    lst = lst.copy()</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">partition</span><span class="params">(a,b)</span>:</span></span><br><span class="line">        pivot = lst[a]</span><br><span class="line">        <span class="keyword">while</span> a&lt;b:</span><br><span class="line">            <span class="keyword">while</span> a&lt;b <span class="keyword">and</span> lst[b]&gt;pivot: b-=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> a&lt;b:</span><br><span class="line">                lst[a] = lst[b]</span><br><span class="line">                a+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> a&lt;b <span class="keyword">and</span> lst[a]&lt;pivot: a+=<span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> a&lt;b:</span><br><span class="line">                lst[b] = lst[a]</span><br><span class="line">                b-=<span class="number">1</span></span><br><span class="line">        lst[a]= pivot</span><br><span class="line">        <span class="keyword">return</span> a</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_select</span><span class="params">(a,b)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> a&gt;=b: <span class="keyword">return</span> lst[a]</span><br><span class="line">        <span class="comment"># randomized select</span></span><br><span class="line">        n = randint(a,b)</span><br><span class="line">        lst[a],lst[n] = lst[n],lst[a]</span><br><span class="line">        pos = partition(a,b)</span><br><span class="line">        <span class="keyword">if</span> pos&gt;i:</span><br><span class="line">            <span class="keyword">return</span> _select(a,pos<span class="number">-1</span>)</span><br><span class="line">        <span class="keyword">elif</span> pos&lt;i:</span><br><span class="line">            <span class="keyword">return</span> _select(pos+<span class="number">1</span>,b)</span><br><span class="line">        <span class="keyword">else</span>:<span class="keyword">return</span> lst[pos]</span><br><span class="line">    <span class="keyword">return</span> _select(<span class="number">0</span>,len(lst)<span class="number">-1</span>)</span><br></pre></td></tr></table></figure><p><strong><a href="https://github.com/mbinary/algorithm.git" target="_blank" rel="noopener">github地址</a></strong></p>]]></content>
    
    <summary type="html">
    
      七大排序算法
    
    </summary>
    
      <category term="数据结构与算法" scheme="https://mbinary.xyz/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="https://mbinary.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="排序" scheme="https://mbinary.xyz/tags/%E6%8E%92%E5%BA%8F/"/>
    
  </entry>
  
  <entry>
    <title>『计算机组成原理』总线</title>
    <link href="https://mbinary.xyz/bus.html"/>
    <id>https://mbinary.xyz/bus.html</id>
    <published>2018-06-18T20:57:27.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>总线</strong><br><!-- TOC --></p><ul><li><a href="#历史">历史</a></li><li><a href="#分类">分类</a></li><li><a href="#特性">特性</a></li><li><a href="#性能指标">性能指标</a></li><li><a href="#总线结构">总线结构</a></li><li><a href="#总线控制">总线控制</a></li><li><a href="#总线周期">总线周期</a></li><li><a href="#总线标准">总线标准</a><ul><li><a href="#pci">PCI</a></li><li><a href="#usb">USB</a></li></ul></li><li><a href="#通信方式">通信方式</a><ul><li><a href="#同步">同步</a></li><li><a href="#异步">异步</a></li><li><a href="#半同步">半同步</a></li><li><a href="#分离式">分离式</a><ul><li><a href="#引入">引入</a></li><li><a href="#原理">原理</a></li><li><a href="#特点">特点</a></li></ul></li></ul></li></ul><!-- /TOC --><p><a id="markdown-历史" name="历史"></a></p><h2 id="历史"><a href="#历史" class="headerlink" title="历史"></a>历史</h2><ul><li>以 ALU 为中心</li><li>以 MEM 为中心</li><li>总线</li></ul><p><a id="markdown-分类" name="分类"></a></p><h2 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h2><ul><li>根据数据传送方式: 串, 并</li><li>根据宽度: 8,16,32,64</li><li>根据使用范围: 计算机外设总线, 测控总线, 总线</li><li><p>根据连接部件</p><ul><li>片内总线(芯片内)</li><li>系统总线: CPU, MEM, IO之间信息传输<br>根据传递的信息功能分类<ul><li>数据总线</li><li>地址总线</li><li>控制总线</li></ul></li></ul></li></ul><p><a id="markdown-特性" name="特性"></a></p><h2 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h2><ul><li>机械特性: 规定几何尺寸,形状,引脚等</li><li>电器特性: 信号方向, 电平范围等</li><li>功能特性: 多跟线不同的功能</li><li>时间特性: 时序</li></ul><p><a id="markdown-性能指标" name="性能指标"></a></p><h2 id="性能指标"><a href="#性能指标" class="headerlink" title="性能指标"></a>性能指标</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-1.png" alt="bus-1"><br>总线宽度：通常指数据总线的根数；<br>总线带宽：总线的数据传输率，指单位时间内总线上传输数据的位数；<br>总线复用：指同一条信号线可以分时传输不同的信号。<br>总线的主设备（主模块）：指一次总线传输期间，拥有总线控制权的设备（模块）；<br>总线的从设备（从模块）：指一次总线传输期间，配合主设备完成数据传输的设备（模块），它只能被动接受主设备发来的命令；<br>总线的传输周期：指总线完成一次完整而可靠的传输所需时间；<br>总线的通信控制：指总线传送过程中双方的时间配合方式。</p><p><a id="markdown-总线结构" name="总线结构"></a></p><h2 id="总线结构"><a href="#总线结构" class="headerlink" title="总线结构"></a>总线结构</h2><ul><li>单总线</li><li>双总线(引入通道)</li></ul><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-2.png" alt="bus-2"></p><ul><li>三总线1</li></ul><p>IO总线, DMA 总线, 主存总线<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-3.png" alt="bus-3"></p><ul><li>三总线2</li></ul><p>局部总线,  系统总线, 扩展总线<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-4.png" alt="bus-4"></p><ul><li>四总线</li></ul><p>局部总线,  系统总线, 扩展总线, <mark>高速总线</mark> ( 适用高速 I/O 设备)<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/四总线.png" alt="四总线"></p><p><a id="markdown-总线控制" name="总线控制"></a></p><h2 id="总线控制"><a href="#总线控制" class="headerlink" title="总线控制"></a>总线控制</h2><ul><li>判优</li></ul><ul><li><p>仲裁逻辑</p><ul><li><p>链式查询 <img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-5.png" alt="bus-5"></p></li><li><p>计数器定时查询 <img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-6.png" alt="bus-6"></p></li></ul></li><li><p>独立请求 <img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-7.png" alt="bus-7"></p></li></ul><p>比较</p><div class="table-container"><table><thead><tr><th>方式</th><th>每个设备用的总线数</th><th>实现</th><th>特点</th><th>原理</th></tr></thead><tbody><tr><td>链式查询</td><td>2</td><td>简单</td><td>近的优先,对电路故障最敏感</td><td>BS总线忙,BR总线请求,BG总线同意.<br>BG信号串行地从近到远传递到下一个IO接口, 如果此接口有总线请求, 总 BG 不再向下传,此接口得到总线使用权</td></tr><tr><td>计数器定时查询</td><td>~log<sub>2</sub>n</td><td>稍复杂</td><td>平等,对故障不敏感</td><td>多了一组设备地址先,少了BG. 总线未被使用时,BS=0. 总线控制部件的计数器开始计数,然后通过设备地址先,向各设备发出一组地址信号. 到设备地址值与计数器值相同时,就获得总线使用权</td></tr><tr><td>独立请求</td><td>2n</td><td>很复杂</td><td>响应速度快,优先次序灵活(通过程序改变)</td><td>设备发出对应的请求信号,总线控制部件中有一个排队电路, 可根据优先次序确定响应设备.</td></tr></tbody></table></div><ul><li>通信</li></ul><p><a id="markdown-总线周期" name="总线周期"></a></p><h2 id="总线周期"><a href="#总线周期" class="headerlink" title="总线周期"></a>总线周期</h2><p>申请分配 -&gt; 寻址 -&gt; 传输/通信 -&gt; 结束 -&gt; 申请分配…<br><a id="markdown-总线标准" name="总线标准"></a></p><h2 id="总线标准"><a href="#总线标准" class="headerlink" title="总线标准"></a>总线标准</h2><p><a id="markdown-pci" name="pci"></a></p><h3 id="PCI"><a href="#PCI" class="headerlink" title="PCI"></a>PCI</h3><ul><li>高性能</li><li>不依附于 CPU</li><li>良好的兼容性</li><li>即插即用(Plug and Play)</li></ul><p>每个 PCI 设备配有此设备的 reg, 供 BIOS 自动获取, 无需手动设置</p><p><a id="markdown-usb" name="usb"></a></p><h3 id="USB"><a href="#USB" class="headerlink" title="USB"></a>USB</h3><ul><li>即插即用</li><li>通过 HUB 扩展连接</li><li>速度快</li><li>标准统一</li><li>有4根线, 两根信号, 两根电源</li></ul><p><a id="markdown-通信方式" name="通信方式"></a></p><h2 id="通信方式"><a href="#通信方式" class="headerlink" title="通信方式"></a>通信方式</h2><p><a id="markdown-同步" name="同步"></a></p><h3 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h3><p>通信双方由统一时标控制数据传送<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-8.png" alt="bus-8"><br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/bus-9.png" alt="bus-9"></p><p><a id="markdown-异步" name="异步"></a></p><h3 id="异步"><a href="#异步" class="headerlink" title="异步"></a>异步</h3><p>允许各模块速度不一致,更加灵活. 没有公共的时钟标准,不一颗球所有部件严格统一操作时间, 而是应用<code>应答方式</code>(又称 <code>握手方式</code>)</p><ul><li>不互锁</li><li>半互锁</li><li>全互锁<br><a id="markdown-半同步" name="半同步"></a><h3 id="半同步"><a href="#半同步" class="headerlink" title="半同步"></a>半同步</h3>半同步通信既能像同步通信那样由统一时钟控制，又能像异步通信那样允许传输时间不一致，因此工作效率介于两者之间。<br><a id="markdown-分离式" name="分离式"></a><h3 id="分离式"><a href="#分离式" class="headerlink" title="分离式"></a>分离式</h3><a id="markdown-引入" name="引入"></a><h4 id="引入"><a href="#引入" class="headerlink" title="引入"></a>引入</h4>以上三种通信方式都是从主模块发出地址和读写命令开始，直到数据传输结输周期中，系统总线的使用权完全由占有使用权的主模块和由它选中的从模块占据。<br>进一步分析读命令传输周期，<ul><li>主模块通过传输总线向从模块发送地址和命令。</li><li>从模块按照命令进行读数据的必要准备。</li><li>从模块经数据总线向主模块提供数据。．</li></ul></li></ul><p>由第二点可见，对系统总线而言，从模块内部读数据过程并无实质性的信息传输，总线空闲。为了克服和利用这种消极等待，尤其在大型刘算机系统中，总线的负载已处于饱和状态，<br>充分挖掘系统总线每瞬间的潜力，对提高系统性能起到极大作用。<br>提出了“分离式”的通信方式<br><a id="markdown-原理" name="原理"></a></p><h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>其基本思想是将一个传输周期（或总线周期）分解为两个子周期。在第一个子周期中，主模块A在得到总线使用权后将命令、地址以及其他有关信息，包括该主模块编号（当有多个主模块时，此号尤为重要）发到系统总线上，经总线传输后，由有关的从模块B接收下来。</p><p>主模块A向系统总线发布这些信息只占用总线很短的时间，一旦发送完，立即放弃总线使用权，<br>以便其他模块使用。在第二个子周期中，当B模块收到A模块发来的有关命令信号后，经选择、<br>译码、读取等一系列内部作，将A模块所需的数据准备好，使由B模块中请总线使用权，一旦<br>获准，B模块便将A模块的号、B模块的地址、A模块所需的数据等一系列信息送到总线上，供<br>A模块接收。很明显，上述两个传输子周期都只单方向的信息流，每个模块都变成了主模块。</p><p><a id="markdown-特点" name="特点"></a></p><h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h4><ul><li>各模块欲占用总线使用权都必须提出串请。</li><li>在得到总线使用权后，主模块在限定的间内向对力传送信息，采用同步方式传送，不再</li></ul><p>等待对方的回答信号。</p><ul><li>各模块在准备数据的过程中都不占用总线，使总线可接受其他模块的请求。</li><li>总线被占用，或者通过它发送命令，或通过它传送数据，不存在空闲等待时间，充分地利用了总线的有效占用，从而实现了总线在多个主、从模块间进行信息交叉重叠并行式传送.</li></ul><p>这种方式控制比较复杂，一般用于大型计算机系统</p>]]></content>
    
    <summary type="html">
    
      总线的结构, 控制, 周期, 标准, 通信方式等
    
    </summary>
    
      <category term="计算机基础" scheme="https://mbinary.xyz/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="计算机组成原理" scheme="https://mbinary.xyz/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>『现代操作系统』IO设备</title>
    <link href="https://mbinary.xyz/IO-device.html"/>
    <id>https://mbinary.xyz/IO-device.html</id>
    <published>2018-06-17T20:57:27.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#盘">盘</a><ul><li><a href="#硬件">硬件</a><ul><li><a href="#磁盘">磁盘</a></li><li><a href="#raid">RAID</a></li><li><a href="#cd-rom">CD-ROM</a></li></ul></li><li><a href="#磁盘格式化">磁盘格式化</a></li><li><a href="#磁盘臂调度算法">磁盘臂调度算法</a></li><li><a href="#错误处理">错误处理</a></li><li><a href="#稳定存储器">稳定存储器</a><ul><li><a href="#目标">目标</a></li><li><a href="#模型">模型</a></li><li><a href="#原理">原理</a></li></ul></li></ul></li><li><a href="#时钟">时钟</a><ul><li><a href="#时钟硬件">时钟硬件</a><ul><li><a href="#构成">构成</a></li><li><a href="#模式">模式</a></li></ul></li><li><a href="#时钟软件">时钟软件</a></li><li><a href="#软定时器">软定时器</a></li></ul></li><li><a href="#用户界面">用户界面</a><ul><li><a href="#键盘">键盘</a><ul><li><a href="#键盘软件">键盘软件</a></li><li><a href="#模式">模式,</a></li><li><a href="#回显">回显</a></li><li><a href="#规范模式下的特殊字符">规范模式下的特殊字符</a></li></ul></li><li><a href="#鼠标">鼠标</a><ul><li><a href="#硬件-1">硬件</a></li><li><a href="#原理-1">原理</a></li></ul></li><li><a href="#x-windows-system">X Windows System</a></li></ul></li><li><a href="#瘦客户机thin-client">瘦客户机(thin client)</a></li><li><a href="#电源管理">电源管理</a><ul><li><a href="#思路">思路</a></li><li><a href="#硬件问题">硬件问题</a></li><li><a href="#os-问题">OS 问题</a><ul><li><a href="#显示器">显示器</a></li><li><a href="#硬盘">硬盘</a></li><li><a href="#cpu">CPU</a></li><li><a href="#内存">内存</a></li></ul></li></ul></li></ul><!-- /TOC --><p><a id="markdown-盘" name="盘"></a></p><h1 id="盘"><a href="#盘" class="headerlink" title="盘"></a>盘</h1><p><a id="markdown-硬件" name="硬件"></a></p><h2 id="硬件"><a href="#硬件" class="headerlink" title="硬件"></a>硬件</h2><p>如磁盘, 硬盘, 软盘, 常作为辅助存储器.</p><p>磁记录, 根据每个<code>小磁针</code>的极性记录 0, 1. 写的时候, 改变电流方向利用电流的磁效应感性去磁性. 读的时候,利用电磁感应判断极性.</p><p><a id="markdown-磁盘" name="磁盘"></a></p><h3 id="磁盘"><a href="#磁盘" class="headerlink" title="磁盘"></a>磁盘</h3><p>磁盘被组织成柱面, 每个柱面包含若干磁道,磁道数与垂直堆叠的磁头个数相同. 磁道被分成若干扇区.</p><p>重叠寻道(overlapped seek): 控制器同时操控多个驱动器进行寻道.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-1.png" alt="IO-device-1"><br>大多数磁盘都有一个虚拟的几何规格呈现给 OS, 控制器可以将虚拟的几何规格映射到实际的物理位置</p><p><a id="markdown-raid" name="raid"></a></p><h3 id="RAID"><a href="#RAID" class="headerlink" title="RAID"></a>RAID</h3><p>(Redundant Array of Inexpensive Disk)</p><p>CPU 性能提升快于磁盘, 出现 <code>并行 I/O</code> 的思想—RAID ( 相对的,  SLED(single large expensive disk)<br>RAID 背后的思想是将一个装满了的磁盘盒子安装到计算机上, 用RAID 控制器替换磁盘控制器卡,将数据复制到整个RAID 上, 然后继续常规的操作</p><p>对 RAID 的并行操作, 目前有0级到7级 RAID. 层级这个名称或许用词不当, 这里没有分层结构,只是不同的组织形式而已</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-2.png" alt="IO-device-2"></p><ul><li><p>0</p><ul><li>组成: 将 RAID 模拟的虚拟单个磁盘划分成 Stripe , 每个 stripe 带有 K个扇区, <code>0</code>~ <code>k-1</code>扇区为 条带0, <code>k</code>~<code>2k-1</code>为条带1…  注意还未引入冗余, 实际上不是正真的 RAID</li><li>原理: 软件发出一条命令,如读取一个由四个连续 stripe 组成的数据块, 那么 RAID 控制器将命令分解为四个单独的命令, 每条命令对应每个磁盘块, 并行操作. 而软件并不知道这些</li><li>特点: 对于大量数据量的请求性能最好</li></ul></li><li>1</li></ul><p>真正的 RAID, 复制了所有的磁盘. 执行写, 每个 stripe 都被写了两次 执行读,可以任选一个副本. 具有很好的容错性(即时一个驱动器崩溃,有副本). 恢复简单:,安装一个新的驱动器复制到其上就可以了.</p><ul><li>2</li></ul><p>0和1操作的扇区条带, 而 2 是工作在字(甚至字节)的基础上. 如 将每个字节分割成 4位半字节对, 并形成 7 位的汉明码. 然后同步读写.<br>效率高,(即时损失一位, 汉明码可以轻松处理).<br>但是这要求所有驱动器旋转必须同步.</p><ul><li>3</li></ul><p>3 是 2 的简化, 为每个数据字计算奇偶校验位并写入即可.</p><ul><li>4,5</li></ul><p>使用stripe ,但是同样写有奇偶校验字., 然而计算 奇偶校验会降低效率.</p><p><a id="markdown-cd-rom" name="cd-rom"></a></p><h3 id="CD-ROM"><a href="#CD-ROM" class="headerlink" title="CD-ROM"></a>CD-ROM</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-3.png" alt="IO-device-3"></p><p><a id="markdown-磁盘格式化" name="磁盘格式化"></a></p><h2 id="磁盘格式化"><a href="#磁盘格式化" class="headerlink" title="磁盘格式化"></a>磁盘格式化</h2><p>在磁盘使用之前,每个盘片必须经由软件完成低级格式化(low-level format). 格式化的磁盘容量约为物理容量 70%.<br><a id="markdown-磁盘臂调度算法" name="磁盘臂调度算法"></a></p><h2 id="磁盘臂调度算法"><a href="#磁盘臂调度算法" class="headerlink" title="磁盘臂调度算法"></a>磁盘臂调度算法</h2><p>时间, 主要是寻到时间较长.</p><ul><li>寻道时间</li><li>旋转延迟</li><li>实际数据传输时间</li></ul><p>算法</p><ul><li>FCFS 先来先服务: 很难优化寻道时间,</li><li>SSF(Shortest Seek First) 最短寻道优先: 很可能在中间往返, 而不能处理靠边的请求, 响应时间很长</li><li>电梯算法(elevator algorithm): 电梯也是用的这种算法. 保存向一个方向移动直到向那个方向再没有服务请求到来.</li></ul><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/电梯算法.png" alt="电梯算法"></p><p>由于寻道和旋转延迟太影响性能了, 所以一次只读一两个扇区效率低下,. 许多磁盘控制器常读出多个扇区并进行高速缓存(独立于操作系统的 高速缓存).</p><p><a id="markdown-错误处理" name="错误处理"></a></p><h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2><p>制造时的瑕疵可能出现坏扇区, 厂商需要设置控制器来处理坏区, 有如下方法, 控制器需要维护一个映射表来替换坏区<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-4.png" alt="IO-device-4"></p><p>也可以由操作系统软件来处理, 首先要获得坏区列表,然后建立重映射表.</p><p>另外的问题: <code>备份</code><br>操作系统要隐藏坏块, 使对备份应用程序不可见.</p><p><strong>AV盘🙈😮</strong><br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-5.png" alt="IO-device-5"></p><p><a id="markdown-稳定存储器" name="稳定存储器"></a></p><h2 id="稳定存储器"><a href="#稳定存储器" class="headerlink" title="稳定存储器"></a>稳定存储器</h2><p><a id="markdown-目标" name="目标"></a></p><h3 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h3><p>为了不丢失或损坏数据,保持磁盘的移植性,稳定存储器有必要的</p><p>它们是: 在写操作到来时, 要么成功执行, 要么对现有数据没有影响, 即时发生了磁盘或者 CPU 错误.并且是<code>在软件中实现的</code></p><p><a id="markdown-模型" name="模型"></a></p><h3 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h3><ul><li>在磁盘写一个块时, 写操作要么正确,要么错误,并且该错误可以在随后的读操作中通过检查 ECC 域检查出来</li><li>一个被正确写入的扇区可能会自发地变坏并且变得不可读(但是概率很小, 可以忽略)</li><li>CPU 可能出现故障, 这时只能停机.</li></ul><p><a id="markdown-原理" name="原理"></a></p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>使用一对完全相同的磁盘,对应的块一同工作形成一个无差错的块. 定义如下三种操作<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-6.png" alt="IO-device-6"></p><p><a id="markdown-时钟" name="时钟"></a></p><h1 id="时钟"><a href="#时钟" class="headerlink" title="时钟"></a>时钟</h1><p>既不是块设备, 也不是字符设备<br><a id="markdown-时钟硬件" name="时钟硬件"></a></p><h2 id="时钟硬件"><a href="#时钟硬件" class="headerlink" title="时钟硬件"></a>时钟硬件</h2><p><a id="markdown-构成" name="构成"></a></p><h3 id="构成"><a href="#构成" class="headerlink" title="构成"></a>构成</h3><p>晶体震荡器, 计数器, 存储寄存器<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-7.png" alt="IO-device-7"></p><p><a id="markdown-模式" name="模式"></a></p><h3 id="模式"><a href="#模式" class="headerlink" title="模式"></a>模式</h3><ul><li>完成模式(one-shot mode): 当时钟启动时它把存储寄存器的值复制到计数器中，然后，来自晶体的每一个脉冲使计数器减1。当计數器变为0时．产生一个中断．并停止工作，直到软件再一次显式地启动它。</li><li>方波模式(square-wavemode): 当计数器变为0并且产生中断之后，存储寄存器的值自动复制到计数器中，并且整个过程无限期地再次重复下去。这些周期性的中断称为时忡滴答(clocktick).</li></ul><p>可编程时钟的优点是其中断頻率可以由软件控制。可编程时钟芯片通常包含两个或三个独立的可编程时钟.</p><p>为了防止计算机的电源被切断时丢失当前时间，大多数计算机具有一个由电池供电的备份时钟，它是由在数字手表中使用的那种类型的低功耗电路实現的。电池时钟可以在系统启动的时候读出，如果不存在备份时钟，软件可能会向用户询问当前日期和时f对于一个连人网络的系统而言还有一种从远程主机获取当前时间的标群方法。无论是哪种情况，当前时间都要像UNIX所做的那样转换成自 1970 年 1 月 1 日 12 时 的UTC滴答数.</p><p><a id="markdown-时钟软件" name="时钟软件"></a></p><h2 id="时钟软件"><a href="#时钟软件" class="headerlink" title="时钟软件"></a>时钟软件</h2><p>时间硬件所做的只是根据已知时间间隔产生中断,其他与时间有关的工作都是软件—时钟驱动程序完成的. 如</p><ul><li>维护日时间</li></ul><p>因为 32 位的寄存器以滴答计数最多计数 2 年<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-8.png" alt="IO-device-8"></p><ul><li>防止进程超时运行</li></ul><p>每启动一个进程,调度程序将一个计数器初始化为以<code>滴答</code>为单位的该进程的时间片取值. 每次时钟中断时,时钟驱动程序将时间片计数器减少1,当为 0 , 时钟驱动程序就 <code>调用调度程序</code>激活另一个进程</p><ul><li>对 CPU 的使用情况记账</li></ul><p>每启动一个程序, 需要对它使用的 CPU 时间计时,</p><ul><li>使用一个辅助定时器, </li><li>全局变量维护一个指针(不太精确),指向进程表中当前运行的进程的表项,每一个 滴答, 就加 1<ul><li>处理用户进程提出的 alarm 系统调用</li><li>为系统本身的各个部分提供监视定时器</li><li>完成概要剖析,监视和统计信息收集</li></ul></li></ul><p>注意: 在时钟中断期间, 时钟驱动程序需要做: </p><ul><li>将实际时间加 1</li><li>将时间片减 1 并检查是否为0</li><li>对 CPU 记账</li><li>将报警计数器减 1</li><li>…</li></ul><p>做很多事, 所以要仔细安排以加快速度</p><p><a id="markdown-软定时器" name="软定时器"></a></p><h2 id="软定时器"><a href="#软定时器" class="headerlink" title="软定时器"></a>软定时器</h2><p>大多数计算机有辅助可编程时钟, 可以设置它以程序需要的任何速率引发定时器中断.<br><a id="markdown-用户界面" name="用户界面"></a></p><h1 id="用户界面"><a href="#用户界面" class="headerlink" title="用户界面"></a>用户界面</h1><p><a id="markdown-键盘" name="键盘"></a></p><h2 id="键盘"><a href="#键盘" class="headerlink" title="键盘"></a>键盘</h2><p>键盘包含一个嵌入式微处理器. 每按下一个键, 产生一个中断,释放键, 也产生一个中断.<br><a id="markdown-键盘软件" name="键盘软件"></a></p><h3 id="键盘软件"><a href="#键盘软件" class="headerlink" title="键盘软件"></a>键盘软件</h3><p>I/O 端口中段数字是键标号, 称为 扫描码(scan code),而不是 ASCII 码.<br>键盘不超过128个, 所以只需7 个表示键编号, 第 8位为0 表示按下, 为 1 表示释放<br><a id="markdown-模式" name="模式"></a></p><h3 id="模式-1"><a href="#模式-1" class="headerlink" title="模式,"></a>模式,</h3><ul><li>非规范模式: 直接将扫描码传递给用户程序, 如 1<backspace>2  (这样过于原始, 且依赖机器</backspace></li><li>规范模式: 处理行内编辑, 如上面只需传递 2 </li></ul><p><a id="markdown-回显" name="回显"></a></p><h3 id="回显"><a href="#回显" class="headerlink" title="回显"></a>回显</h3><p>键盘和监视器本来是两种 I/O 设备, 但用户的使用将他们联系起来, 即用户习惯 键入 , 然后在监视器上显示出来, 称为 <code>echoing 回显</code> .<br>这里就需考虑回显带来的问题,</p><ul><li>一行超过 80 个字符, 换行?</li><li>制表符的处理</li></ul><p><a id="markdown-规范模式下的特殊字符" name="规范模式下的特殊字符"></a></p><h3 id="规范模式下的特殊字符"><a href="#规范模式下的特殊字符" class="headerlink" title="规范模式下的特殊字符"></a>规范模式下的特殊字符</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-9.png" alt="IO-device-9"></p><p><a id="markdown-鼠标" name="鼠标"></a></p><h2 id="鼠标"><a href="#鼠标" class="headerlink" title="鼠标"></a>鼠标</h2><p><a id="markdown-硬件-1" name="硬件-1"></a></p><h3 id="硬件-1"><a href="#硬件-1" class="headerlink" title="硬件"></a>硬件</h3><p>光学鼠标在其底部装备有一个或多个发光二极管和光电探瀏器。现代光学鼠标在其中有图像处理芯片并且获取处于它们下方的连续的低分辨率照片，寻找从图像到图像的变化。</p><p>鼠标可能具有一个，两个或者三个按钮,某些鼠标具有滚轮，可将额外的数据发送回计算机.</p><p>无线鼠标使用低功率无线电，例如使 Bluetooth将數据发这回计算机，而有线鼠标是通过导线将数据发送回。</p><p><a id="markdown-原理-1" name="原理-1"></a></p><h3 id="原理-1"><a href="#原理-1" class="headerlink" title="原理"></a>原理</h3><p>当鼠标在随便哪个方向移动了一个确定的最小距离，或者按钮被接下或释放时。都会有一条消息发送给计算机。最小距离大约是0.1mm（尽管它可以在软件中设置）。有些人将这一单位称为一个鼠标步(mickey) </p><p>发送的消息为 <code>(Δx,Δy,button)</code>. 通常，消息占3字节, 速度最多每秒40次．<br>注意，鼠标仅仅指出位上的变化，而不是绝对位置本身。如果轻轻地拿起鼠标并且轻轻地放下而<br>不导致橡皮球旋转．那么就不会有消息发出。<br>某些GUI 区分单击与双击鼠标接钮。如果两次点击在空间上〈鼠标步）足够接近，并且在时间上(亳秒)也足够接近，那么就会发出双击信号。最大的“足够接近”是软件的事情，并且这两个参数通常是用户可设置的。<br><a id="markdown-x-windows-system" name="x-windows-system"></a></p><h2 id="X-Windows-System"><a href="#X-Windows-System" class="headerlink" title="X Windows System"></a>X Windows System</h2><p>几乎所有 UNIX 系统的用户界面都以 X 为基础. 如 GNOME, KDE</p><p>当 X 在一台机器上运行时, 采集键盘与鼠标输入并且输出到屏幕上的软件称为 <code>X server</code><br>X server 常位于用户计算机的内部, 而 X 客户可能在远程计算服务器上.</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/Client and Server.png" alt="Client and Server"></p><p>注意 X 只是一个窗口系统, 不是一个完全的 GUI. 要获得完全的 GUI, 需要在器上运行其他软件层.</p><p><a id="markdown-瘦客户机thin-client" name="瘦客户机thin-client"></a></p><h1 id="瘦客户机-thin-client"><a href="#瘦客户机-thin-client" class="headerlink" title="瘦客户机(thin client)"></a>瘦客户机(thin client)</h1><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-10.png" alt="IO-device-10"></p><p><a id="markdown-电源管理" name="电源管理"></a></p><h1 id="电源管理"><a href="#电源管理" class="headerlink" title="电源管理"></a>电源管理</h1><p><a id="markdown-思路" name="思路"></a></p><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><ul><li>当计算机的某些部件(主要是 I/O 设备)不同的时候让 OS 关闭他们.</li><li>应用程序使用较少的能量</li></ul><p><a id="markdown-硬件问题" name="硬件问题"></a></p><h2 id="硬件问题"><a href="#硬件问题" class="headerlink" title="硬件问题"></a>硬件问题</h2><p>计算机状态: 工作, 睡眠, 休眠, 关闭<br>权衡: 消耗电量从左到右递减, 关闭状态不耗电量. 但是从睡眠, 休眠状态恢复到工作状态, 后者需要更多的时间和电量.</p><p><a id="markdown-os-问题" name="os-问题"></a></p><h2 id="OS-问题"><a href="#OS-问题" class="headerlink" title="OS 问题"></a>OS 问题</h2><p>通过算法或者试探, 让 OS 对关于关闭什么设备以及何时关闭能够作出良好的决策<br><a id="markdown-显示器" name="显示器"></a></p><h3 id="显示器"><a href="#显示器" class="headerlink" title="显示器"></a>显示器</h3><p>一段时间后可以关闭屏幕(是睡眠,可以立即唤醒))<br>改进: 将屏幕分成多个区域, 可以关闭当前窗口(或者用户自己定义)未覆盖的区域. 窗口管理器还可以使窗口与区域对齐, 进一步地, 部分照亮关闭的区域<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-device-11.png" alt="IO-device-11"><br><a id="markdown-硬盘" name="硬盘"></a></p><h3 id="硬盘"><a href="#硬盘" class="headerlink" title="硬盘"></a>硬盘</h3><p>即时不存在存取操作,硬盘也消耗大量的能量以保持高速旋转.(不然加速需要很长时间 呢(●ˇ∀ˇ●)). 但是注意 停止硬盘是休眠不是睡眠.<br>此外,重新硬盘启动会消耗更多的能量.<br>因此, 每个硬盘有一个特征时间 T, 为它的盈亏平衡点. 如果能预测将来多久才用到硬盘(可以基于存取历史), 那么如果将来时间讲个 ΔT &gt; T, 就可以关闭.<br><a id="markdown-cpu" name="cpu"></a></p><h3 id="CPU"><a href="#CPU" class="headerlink" title="CPU"></a>CPU</h3><p>睡眠中的 CPU 几乎不耗电, 只需等待中断的到来才唤醒.<br>CPU 电压可以用软件降低,但会降低时钟速度. 由于电能消耗与电压的平方成正比, 而电压与时钟速度成正比, 所以可以有降低的平衡点来盈利</p><p><a id="markdown-内存" name="内存"></a></p><h3 id="内存"><a href="#内存" class="headerlink" title="内存"></a>内存</h3><ul><li>刷新然后关闭高速缓存</li></ul><p>cache 可以重新加载且不损失信息, 而且速度快, </p><ul><li>将主存内容写到磁盘上, 然后关闭主存本身.</li></ul>]]></content>
    
    <summary type="html">
    
      IO 设备详细介绍, 包括盘, 时钟, 用户界面等组成以及原理
    
    </summary>
    
      <category term="操作系统" scheme="https://mbinary.xyz/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    
      <category term="操作系统" scheme="https://mbinary.xyz/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
  </entry>
  
  <entry>
    <title>『现代操作系统』IO硬件原理</title>
    <link href="https://mbinary.xyz/IO-hardware.html"/>
    <id>https://mbinary.xyz/IO-hardware.html</id>
    <published>2018-06-16T20:57:27.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>I/O 硬件原理</strong></p><!-- TOC --><ul><li><a href="#01-io-设备">0.1. I/O 设备</a><ul><li><a href="#011-块设备block-device">0.1.1. 块设备(block device)</a></li><li><a href="#012-字符设备character-device">0.1.2. 字符设备(character device)</a></li></ul></li><li><a href="#02-设备控制器device-controller--adapter">0.2. 设备控制器(device controller / adapter)</a></li><li><a href="#03-内存映射-io">0.3. 内存映射 I/O</a><ul><li><a href="#031-方案">0.3.1. 方案</a></li><li><a href="#032-工作原理">0.3.2. 工作原理</a></li><li><a href="#033-优点">0.3.3. 优点</a></li><li><a href="#034-缺点">0.3.4. 缺点</a></li></ul></li><li><a href="#04-dma直接存储器存取-direct-memory-access">0.4. DMA(直接存储器存取, Direct Memory Access)</a><ul><li><a href="#041-工作原理">0.4.1. 工作原理</a></li><li><a href="#042-对-cpu-的延迟">0.4.2. 对 CPU 的延迟</a><ul><li><a href="#0421-周期窃取cycle-stealing">0.4.2.1. 周期窃取(Cycle Stealing)</a></li><li><a href="#0422-突发模式burst-mode">0.4.2.2. 突发模式(burst mode)</a></li></ul></li></ul></li><li><a href="#05-中断">0.5. 中断</a><ul><li><a href="#051-问题">0.5.1. 问题</a><ul><li><a href="#0511-哪些信号需要保存">0.5.1.1. 哪些信号需要保存?</a></li><li><a href="#0512-保存在哪里">0.5.1.2. 保存在哪里?</a></li><li><a href="#0513-谁来保存">0.5.1.3. 谁来保存?</a></li><li><a href="#0514-考虑流水线超标量内部并行">0.5.1.4. 考虑流水线,超标量(内部并行)</a></li></ul></li></ul></li></ul><!-- /TOC --><p><a id="markdown-01-io-设备" name="01-io-设备"></a></p><h2 id="0-1-I-O-设备"><a href="#0-1-I-O-设备" class="headerlink" title="0.1. I/O 设备"></a>0.1. I/O 设备</h2><p><a id="markdown-011-块设备block-device" name="011-块设备block-device"></a></p><h3 id="0-1-1-块设备-block-device"><a href="#0-1-1-块设备-block-device" class="headerlink" title="0.1.1. 块设备(block device)"></a>0.1.1. 块设备(block device)</h3><p>把信息存储在固定大小的块中,每个块都有自己的地址. 每个块可以独立于其他块读写. 如 硬盘, CD-ROM , USB 盘 …</p><p><a id="markdown-012-字符设备character-device" name="012-字符设备character-device"></a></p><h3 id="0-1-2-字符设备-character-device"><a href="#0-1-2-字符设备-character-device" class="headerlink" title="0.1.2. 字符设备(character device)"></a>0.1.2. 字符设备(character device)</h3><p>字符设备以字符为单位发送或接收一个字符流, 而不考虑任何块结构. 它是不可寻址的.<br>如打印机,网络接口, 鼠标(用作指点设备)…</p><p><a id="markdown-02-设备控制器device-controller--adapter" name="02-设备控制器device-controller--adapter"></a></p><h2 id="0-2-设备控制器-device-controller-adapter"><a href="#0-2-设备控制器-device-controller-adapter" class="headerlink" title="0.2. 设备控制器(device controller / adapter)"></a>0.2. 设备控制器(device controller / adapter)</h2><p>I/O 设备一般由两部分组成: 机械部分和电子部分.<br>电子部分就是设备控制器. 常以插入(PCI)扩展槽中的印刷电路板的形式出现.</p><p>控制器与设备之间的接口是很低层次的接口. 它的任务就是把串行的位流转换为字节块,并进行必要的错误校正.</p><p><a id="markdown-03-内存映射-io" name="03-内存映射-io"></a></p><h2 id="0-3-内存映射-I-O"><a href="#0-3-内存映射-I-O" class="headerlink" title="0.3. 内存映射 I/O"></a>0.3. 内存映射 I/O</h2><p>每个控制器有几个寄存器, OS 可以读写来了解,更改设备的状态信息. 控制器还有 OS 可以读写的 <strong>数据缓冲区</strong>.</p><p>问题来了: CPU 如何与设备的控制寄存器和数据缓冲区通信.<br><a id="markdown-031-方案" name="031-方案"></a></p><h3 id="0-3-1-方案"><a href="#0-3-1-方案" class="headerlink" title="0.3.1. 方案"></a>0.3.1. 方案</h3><ul><li>方法一: 每个控制寄存器被分配一个 I/O   端口(所有端口形成端口空间,受保护不被普通用户访问).  然后可以设置指令来读写, </li></ul><p>如 <code>IN REG, PORT</code>将读取控制器寄存器 PORT 中的内容到 CPU 寄存器 REG</p><ul><li>方法二: 内存映射 I/O. 将所有控制寄存器映射到内存空间, 都被分配唯一的地址, 且这些内存地址不会再分配. </li></ul><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-hardware-1.png" alt="IO-hardware-1"></p><p><a id="markdown-032-工作原理" name="032-工作原理"></a></p><h3 id="0-3-2-工作原理"><a href="#0-3-2-工作原理" class="headerlink" title="0.3.2. 工作原理"></a>0.3.2. 工作原理</h3><p>CPU 读入一个字时, 不论是从内存还是 I/O 端口, 都将目的地址放在总线的地址线上, 总线控制线置 READ 信号看. 还要用一条线表明是 I/O 空间 还是内存空间. 如果是 I/O空间, I/O设备将响应请求.<br><a id="markdown-033-优点" name="033-优点"></a></p><h3 id="0-3-3-优点"><a href="#0-3-3-优点" class="headerlink" title="0.3.3. 优点"></a>0.3.3. 优点</h3><ul><li>如果需要特殊的 I/O 指令读写设备控制寄存器,那么访问这些寄存器需要使用汇编代码, 调用这样的过程需要增加开销,</li></ul><p>对于`内存映射 I/O  ,设备控制寄存器只是内存中的变量, 和其他变量一样寻址,可以用 C 语言编写驱动程序</p><ul><li>对于内存映射 I/O , 不需要特殊的保护机制来阻止用户进程执行 I/O 操作. 操作系统只需注意不要将内存映射的地址映射到用户虚拟地址空间. 更有利的是, 如果有多个设备, 可以将内存映射 I/O 映射到不同的页, 可以分配特定的页给用户,使其使用驱动程序, 而且不担心各驱动程序之间的影响</li></ul><p><a id="markdown-034-缺点" name="034-缺点"></a></p><h3 id="0-3-4-缺点"><a href="#0-3-4-缺点" class="headerlink" title="0.3.4. 缺点"></a>0.3.4. 缺点</h3><ul><li>不能对设备控制器的寄存器进行 cache, 因为设备的状态改变, 软件将没有办法发现. 所以硬件必须对每个页面具备选择性的禁用 chche. 增加了复杂性</li></ul><ul><li>在内存映射机器上, 具有单独的内存总线会使 I/O 设备没有办法查看内存地址,因为内存地址旁路到内存总线上, 没有办法响应.</li></ul><p><a id="markdown-04-dma直接存储器存取-direct-memory-access" name="04-dma直接存储器存取-direct-memory-access"></a></p><h2 id="0-4-DMA-直接存储器存取-Direct-Memory-Access"><a href="#0-4-DMA-直接存储器存取-Direct-Memory-Access" class="headerlink" title="0.4. DMA(直接存储器存取, Direct Memory Access)"></a>0.4. DMA(直接存储器存取, Direct Memory Access)</h2><p>独立于 CPU 访问系统总线</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-hardware-2.png" alt="IO-hardware-2"></p><p><a id="markdown-041-工作原理" name="041-工作原理"></a></p><h3 id="0-4-1-工作原理"><a href="#0-4-1-工作原理" class="headerlink" title="0.4.1. 工作原理"></a>0.4.1. 工作原理</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-hardware-3.png" alt="IO-hardware-3"></p><p>也就是不用浪费 CPU 处理缓冲区到内存的时间, 相当于另有一个” CPU “ 专门处理 磁盘 到 内存 的 I/O</p><p><a id="markdown-042-对-cpu-的延迟" name="042-对-cpu-的延迟"></a></p><h3 id="0-4-2-对-CPU-的延迟"><a href="#0-4-2-对-CPU-的延迟" class="headerlink" title="0.4.2. 对 CPU 的延迟"></a>0.4.2. 对 CPU 的延迟</h3><p><a id="markdown-0421-周期窃取cycle-stealing" name="0421-周期窃取cycle-stealing"></a></p><h4 id="0-4-2-1-周期窃取-Cycle-Stealing"><a href="#0-4-2-1-周期窃取-Cycle-Stealing" class="headerlink" title="0.4.2.1. 周期窃取(Cycle Stealing)"></a>0.4.2.1. 周期窃取(Cycle Stealing)</h4><p>注意 上面的操作是字模式传送,  在 DMA 请求传送一个字并且得到这个字时, CPU 不能使用总线,必须等待.</p><p><a id="markdown-0422-突发模式burst-mode" name="0422-突发模式burst-mode"></a></p><h4 id="0-4-2-2-突发模式-burst-mode"><a href="#0-4-2-2-突发模式-burst-mode" class="headerlink" title="0.4.2.2. 突发模式(burst mode)"></a>0.4.2.2. 突发模式(burst mode)</h4><p>上面是字传输模式, 对于块模式下的传送, DMA 会发起一连串的传送,然后才释放总线. 这比周期窃取效率更高.</p><p>上面 的模式是<code>飞越模式(fly-by mode)</code>, 即 DMA 控制器直接通知设备控制器将数据传送到 主存, 只请求一次总线</p><p>某些 DMA 使用其他模式. 让设备控制器将字发送到 DMA, 然后 DMA 再 请求总线将数据发送到其他地方(其他设备, 主存…), 这样会多消耗一个总线周期, 但是更加灵活: 可以 <code>设备-&gt;设备</code>, <code>内存-&gt;内存</code>(内存读, 然后 内存写)</p><p>不使用 DMA 的考虑:</p><ul><li>CPU 比 DMA 快得多,当限制因素不是  I/O 设备的读写速度时,没必要使用 DMA</li><li>去除 DMA 而用 CPU 使用软件做所有工作可以节省硬件的开销</li></ul><p><a id="markdown-05-中断" name="05-中断"></a></p><h2 id="0-5-中断"><a href="#0-5-中断" class="headerlink" title="0.5. 中断"></a>0.5. 中断</h2><p>当一个 I/O 设备完成它的工作后,它就产生一个中断, 通过在分配给它的一条总线信号线上置起信号.<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/中断.png" alt="中断"></p><p>如果有多个中断请求, 按优先级, 如果还没有被处理, 设备一直发出中断知道得到 CPU 服务</p><p>中断控制器通过在地址先上放置一个数字<code>(中断向量  interrupt vector)</code>表明哪个设备需要关注,同时向 CPU 发出中断</p><p>中断信号导致 CPU 停止当前工作, 并处理其他事情.  根据中断向量跳转到需要的中断服务程序</p><p><a id="markdown-051-问题" name="051-问题"></a></p><h3 id="0-5-1-问题"><a href="#0-5-1-问题" class="headerlink" title="0.5.1. 问题"></a>0.5.1. 问题</h3><p>开始中断服务之前, 硬件需要保存信息<br><a id="markdown-0511-哪些信号需要保存" name="0511-哪些信号需要保存"></a></p><h4 id="0-5-1-1-哪些信号需要保存"><a href="#0-5-1-1-哪些信号需要保存" class="headerlink" title="0.5.1.1. 哪些信号需要保存?"></a>0.5.1.1. 哪些信号需要保存?</h4><p>至少程序计数器, 至多可见的寄存器, 一些内部寄存器…<br><a id="markdown-0512-保存在哪里" name="0512-保存在哪里"></a></p><h4 id="0-5-1-2-保存在哪里"><a href="#0-5-1-2-保存在哪里" class="headerlink" title="0.5.1.2. 保存在哪里?"></a>0.5.1.2. 保存在哪里?</h4><ul><li>如果放在内部寄存器, 那么中断控制器之后无法得到应答,知道所有可能的相关信息被读出,以免第二个中断重写内部寄存器保存状态. 这样在中断被禁止时将导致长时间的死机,并可能丢失中断和数据</li><li><p>如果在堆栈中, 使用谁的堆栈? </p><ul><li>如果使用当前堆栈, 可能是用户进程的,堆栈指针可能是不合法的.  </li><li>可能指向一个页面的末端, 若干次内存写之后, 可能超出页面发生页面故障. 那么在何处保存状态以处理页面故障?</li><li>如果用内核堆栈. 切换到和心态可能要求改变 MMU 上下文, 并且可能使 cache 和 TLB 的大部分失效. 静态地或动态地重新状态所有东西将增加处理一个中断的时间,因而浪费 CPU 的时间<br><a id="markdown-0513-谁来保存" name="0513-谁来保存"></a><h4 id="0-5-1-3-谁来保存"><a href="#0-5-1-3-谁来保存" class="headerlink" title="0.5.1.3. 谁来保存?"></a>0.5.1.3. 谁来保存?</h4>对谁可见就谁来保存</li></ul></li></ul><p><a id="markdown-0514-考虑流水线超标量内部并行" name="0514-考虑流水线超标量内部并行"></a></p><h4 id="0-5-1-4-考虑流水线-超标量-内部并行"><a href="#0-5-1-4-考虑流水线-超标量-内部并行" class="headerlink" title="0.5.1.4. 考虑流水线,超标量(内部并行)"></a>0.5.1.4. 考虑流水线,超标量(内部并行)</h4><p>在流水线满的时候,如果出现一个中断, 由于许多指令处于不同的正在执行的截断. 程序计数器可能无法正确反应已经执行的指令和未执行之间的边界.<br>在超标量机器上, 指令可能分解成微操作, 为操作可能乱序执行</p><ul><li>精确中断(precise interrupt):将机器留在一个明确状态</li></ul><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-hardware-4.png" alt="IO-hardware-4"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-hardware-5.png" alt="IO-hardware-5"></p><ul><li>不精确中断(imprecise interrupt)</li></ul><p>不满足上面的条件</p>]]></content>
    
    <summary type="html">
    
      io 硬件原理, 设备控制器, 内存映射, DMA
    
    </summary>
    
      <category term="操作系统" scheme="https://mbinary.xyz/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    
      <category term="操作系统" scheme="https://mbinary.xyz/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
  </entry>
  
  <entry>
    <title>『现代操作系统』IO软件原理</title>
    <link href="https://mbinary.xyz/IO-software.html"/>
    <id>https://mbinary.xyz/IO-software.html</id>
    <published>2018-06-16T20:57:27.000Z</published>
    <updated>2021-04-23T08:02:23.258Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><!-- TOC --><ul><li><a href="#io软件目标">IO软件目标</a></li><li><a href="#io处理方式">IO处理方式</a></li><li><a href="#io软件层次">IO软件层次</a><ul><li><a href="#中断处理程序">中断处理程序</a></li><li><a href="#设备驱动程序">设备驱动程序</a><ul><li><a href="#位置">位置</a></li><li><a href="#功能">功能</a></li><li><a href="#运行">运行</a></li></ul></li><li><a href="#与设备无关的io软件">与设备无关的I/O软件</a></li><li><a href="#用户空间的io软件">用户空间的IO软件</a></li></ul></li></ul><!-- /TOC --><a id="more"></a><p><a id="markdown-io软件目标" name="io软件目标"></a></p><h1 id="IO软件目标"><a href="#IO软件目标" class="headerlink" title="IO软件目标"></a>IO软件目标</h1><ul><li>设备独立性(device independence): 编写的程序能够访问任何设备而无需事先指定. 即程序的通用性</li><li>统一命名(uniform naming): 一个文件或一个设备的名字应该是一个简单的字符串或数字. </li><li>错误处理(error handling): 错误应该尽可能接近硬件得到处理.  处理不了再上传</li><li>同步(synchronous) 和 异步(asynchronous)(即中断驱动): 大都数物理 I/O 是  异步的</li></ul><p><a id="markdown-io处理方式" name="io处理方式"></a></p><h1 id="IO处理方式"><a href="#IO处理方式" class="headerlink" title="IO处理方式"></a>IO处理方式</h1><ul><li>程序控制 I/O</li></ul><p>让 CPU 做全部 I/O工作,成为程序控制 I/O<br>CPU 要不断地查询设备, 这成为 <code>polling</code> 或 <code>busy waiting</code></p><ul><li>中断驱动 I/O</li></ul><p>缺点是 中断发生在每个事件上, 同样要花一些时间,<br>如打印一个缓冲区的字符, 每个字符都要中断一次</p><ul><li>使用DMA</li></ul><p>需要特殊的硬件 DMA 控制器, 每个缓冲区中断一次</p><p><a id="markdown-io软件层次" name="io软件层次"></a></p><h1 id="IO软件层次"><a href="#IO软件层次" class="headerlink" title="IO软件层次"></a>IO软件层次</h1><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-software-1.png" alt="IO-software-1"></p><p><a id="markdown-中断处理程序" name="中断处理程序"></a></p><h2 id="中断处理程序"><a href="#中断处理程序" class="headerlink" title="中断处理程序"></a>中断处理程序</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/中断发生时软件需要做的.png" alt="中断发生时软件需要做的"><br><a id="markdown-设备驱动程序" name="设备驱动程序"></a></p><h2 id="设备驱动程序"><a href="#设备驱动程序" class="headerlink" title="设备驱动程序"></a>设备驱动程序</h2><p>每个连接到计算机上的 I/O 设备都需要某些设备特定的代码来对其进行控制 , 注意 设备控制器是硬件上的, 驱动程序是软件上的. </p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-software-2.png" alt="IO-software-2"></p><p><a id="markdown-位置" name="位置"></a></p><h3 id="位置"><a href="#位置" class="headerlink" title="位置"></a>位置</h3><p>为了访问设备的硬件(即设备控制器的寄存器), 设备驱动程序需要是系统内核的一部分. </p><p>其实也可以构造运行在用户空间的驱动程序,使用系统调用来读写设备寄存器. 这样可以使内核与驱动程序,  驱动程序之间隔离, 消除驱动程序干扰内核造成的系统崩溃.</p><p><a id="markdown-功能" name="功能"></a></p><h3 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h3><ul><li>接收来自其上方与设备无关的软件发出的抽象的读写请求</li><li>如果需要, 驱动程序 必须对设备进行初始化,还可能对电源需求和日志事件进行管理</li></ul><p><a id="markdown-运行" name="运行"></a></p><h3 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h3><p>驱动程序在执行期间动态地装在到系统<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-software-3.png" alt="IO-software-3"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-software-4.png" alt="IO-software-4"></p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-software-5.png" alt="IO-software-5"></p><p><a id="markdown-与设备无关的io软件" name="与设备无关的io软件"></a></p><h2 id="与设备无关的I-O软件"><a href="#与设备无关的I-O软件" class="headerlink" title="与设备无关的I/O软件"></a>与设备无关的I/O软件</h2><p><strong>功能</strong></p><ul><li>缓冲</li></ul><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/IO-software-6.png" alt="IO-software-6"></p><ul><li>错误报告</li><li>分配与释放专用设备</li><li>提供与设备无关的块大小</li><li>设备驱动程序的统一接口</li></ul><p><a id="markdown-用户空间的io软件" name="用户空间的io软件"></a></p><h2 id="用户空间的IO软件"><a href="#用户空间的IO软件" class="headerlink" title="用户空间的IO软件"></a>用户空间的IO软件</h2><ul><li>C 语言中的 printf</li></ul><ul><li>假脱机(spoolilng)</li></ul><p>如果一个进程打开它, 然后很长时间不使用, 则其他进程都无法打印 .  另外一种方法是   创建一个 <code>守护进程(daemon)</code> 和<code>假脱机目录</code>. 一个进程要打印一个文件时, 首先生成要打印的整个文件, 并且放在假脱机目录, 由守护进程打印该目录下的文件, ,,守护进程是唯一允许使用打印机特殊文件的进程.</p>]]></content>
    
    <summary type="html">
    
      &lt;!-- TOC --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#io软件目标&quot;&gt;IO软件目标&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#io处理方式&quot;&gt;IO处理方式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#io软件层次&quot;&gt;IO软件层次&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#中断处理程序&quot;&gt;中断处理程序&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#设备驱动程序&quot;&gt;设备驱动程序&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#位置&quot;&gt;位置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#功能&quot;&gt;功能&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#运行&quot;&gt;运行&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#与设备无关的io软件&quot;&gt;与设备无关的I/O软件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#用户空间的io软件&quot;&gt;用户空间的IO软件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- /TOC --&gt;
    
    </summary>
    
      <category term="操作系统" scheme="https://mbinary.xyz/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    
      <category term="操作系统" scheme="https://mbinary.xyz/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
  </entry>
  
  <entry>
    <title>『现代操作系统』操作系统引论</title>
    <link href="https://mbinary.xyz/os-general.html"/>
    <id>https://mbinary.xyz/os-general.html</id>
    <published>2018-06-14T23:33:25.000Z</published>
    <updated>2021-04-23T08:02:23.262Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>操作系统引论</strong></p><a id="more"></a><!-- TOC --><ul><li><a href="#位置">位置</a></li><li><a href="#功能">功能</a><ul><li><a href="#向应用程序提供抽象--自顶向下">向应用程序提供抽象—自顶向下</a></li><li><a href="#资源管理--自底向上">资源管理—自底向上</a></li></ul></li><li><a href="#发展历史">发展历史</a><ul><li><a href="#第一代1945-1955--真空管和穿孔卡片">第一代(1945-1955)—真空管和穿孔卡片</a></li><li><a href="#第二代1955-1965--晶体管和批处理系统">第二代(1955-1965)—晶体管和批处理系统</a></li><li><a href="#第三代1965-1980--集成电路芯片和多道程序设计">第三代(1965-1980)—集成电路芯片和多道程序设计</a></li><li><a href="#第四代1980至今--个人计算机">第四代(1980至今)—个人计算机</a></li></ul></li><li><a href="#操作系统结构">操作系统结构</a><ul><li><a href="#单体结构">单体结构</a></li><li><a href="#层次式系统">层次式系统</a></li><li><a href="#微内核">微内核</a></li><li><a href="#client-server模式">Client-Server模式</a></li><li><a href="#虚拟机">虚拟机</a><ul><li><a href="#优点">优点</a></li><li><a href="#条件">条件</a></li><li><a href="#ⅰ型管理程序">Ⅰ型管理程序</a></li><li><a href="#ⅱ型管理程序">Ⅱ型管理程序</a></li><li><a href="#准虚拟化paravirtualized">准虚拟化(paravirtualized)</a></li></ul></li></ul></li></ul><!-- /TOC --><p><a id="markdown-位置" name="位置"></a></p><h2 id="位置"><a href="#位置" class="headerlink" title="位置"></a>位置</h2><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/os.png" alt="os"></p><p><a id="markdown-功能" name="功能"></a></p><h2 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h2><p><a id="markdown-向应用程序提供抽象--自顶向下" name="向应用程序提供抽象--自顶向下"></a></p><h3 id="向应用程序提供抽象—自顶向下"><a href="#向应用程序提供抽象—自顶向下" class="headerlink" title="向应用程序提供抽象—自顶向下"></a>向应用程序提供抽象—自顶向下</h3><p>抽象是管理复杂性的一个关键, 好的抽象可以把一个几乎不可能管理的任务划分为两个可管理的部分:</p><ul><li>有关抽象的定义和实现</li><li>用这些抽象解决问题</li></ul><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/os-general-1.png" alt="os-general-1"><br><a id="markdown-资源管理--自底向上" name="资源管理--自底向上"></a></p><h3 id="资源管理—自底向上"><a href="#资源管理—自底向上" class="headerlink" title="资源管理—自底向上"></a>资源管理—自底向上</h3><p>在相互竞争的程序之间有序地控制对处理器,  存储器以及其他 I/O 接口设备的分配</p><p><a id="markdown-发展历史" name="发展历史"></a></p><h2 id="发展历史"><a href="#发展历史" class="headerlink" title="发展历史"></a>发展历史</h2><p><a id="markdown-第一代1945-1955--真空管和穿孔卡片" name="第一代1945-1955--真空管和穿孔卡片"></a></p><h3 id="第一代-1945-1955-—真空管和穿孔卡片"><a href="#第一代-1945-1955-—真空管和穿孔卡片" class="headerlink" title="第一代(1945-1955)—真空管和穿孔卡片"></a>第一代(1945-1955)—真空管和穿孔卡片</h3><p>ENIAC ,  程序设计是用纯粹的机器语言,</p><p>使用: 程序员在墙上的机时表上预约一段时间,,然后到机房中将他的插件板接到计算机中,在接下来的几小时等待(计算的都是简单的数字运算, 如制作对数表)</p><p><a id="markdown-第二代1955-1965--晶体管和批处理系统" name="第二代1955-1965--晶体管和批处理系统"></a></p><h3 id="第二代-1955-1965-—晶体管和批处理系统"><a href="#第二代-1955-1965-—晶体管和批处理系统" class="headerlink" title="第二代(1955-1965)—晶体管和批处理系统"></a>第二代(1955-1965)—晶体管和批处理系统</h3><p>批处理系统(batch system), 在输入室中手机全部的作业 ,然后用一台相对便宜的计算机,读到磁带上. 磁带被送到机房里并装到磁带机上. 然后操作员装入一个特殊的程序(现代操作系统的前身), 它从磁带上读入第一个作业并运行. 如此反复</p><p><a id="markdown-第三代1965-1980--集成电路芯片和多道程序设计" name="第三代1965-1980--集成电路芯片和多道程序设计"></a></p><h3 id="第三代-1965-1980-—集成电路芯片和多道程序设计"><a href="#第三代-1965-1980-—集成电路芯片和多道程序设计" class="headerlink" title="第三代(1965-1980)—集成电路芯片和多道程序设计"></a>第三代(1965-1980)—集成电路芯片和多道程序设计</h3><p>多道程序设计(multiprogramming): 若当前作业因等待磁带或其他 I/O 操作而暂停时, 为了不让 CPU 一直等待这一个作业, 将<code>内存</code>分几个部分, 每一部分存放不同的作业, 在一个作业等待 I/O 时, 可以让另一个作业使用 CPU. </p><p>在内存中防止多个作业需要特殊的硬件来保护, 以免作业的信息被窃取或受到攻击.</p><p>程序员希望得到更快响应, 由此出现<code>分时系统(timesharing)</code>.</p><p><a id="markdown-第四代1980至今--个人计算机" name="第四代1980至今--个人计算机"></a></p><h3 id="第四代-1980至今-—个人计算机"><a href="#第四代-1980至今-—个人计算机" class="headerlink" title="第四代(1980至今)—个人计算机"></a>第四代(1980至今)—个人计算机</h3><p>大规模集成电路(LSI)的发展, 晶体管个数在单位平方厘米面积的芯片上可达数千个.</p><p>逐步发展处 网络操作系统, 分布式操作系统</p><p><a id="markdown-操作系统结构" name="操作系统结构"></a></p><h2 id="操作系统结构"><a href="#操作系统结构" class="headerlink" title="操作系统结构"></a>操作系统结构</h2><p><a id="markdown-单体结构" name="单体结构"></a></p><h3 id="单体结构"><a href="#单体结构" class="headerlink" title="单体结构"></a>单体结构</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/单体结构.png" alt="单体结构"></p><ul><li>主程序: 处理服务过程请求</li><li>服务过程: 执行系统调用</li><li>实用过程: 辅助服务过程</li></ul><p><a id="markdown-层次式系统" name="层次式系统"></a></p><h3 id="层次式系统"><a href="#层次式系统" class="headerlink" title="层次式系统"></a>层次式系统</h3><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/by Dijkstra.png" alt="by Dijkstra"></p><p><a id="markdown-微内核" name="微内核"></a></p><h3 id="微内核"><a href="#微内核" class="headerlink" title="微内核"></a>微内核</h3><p>传统上,所有的层都在内核中, 但是这样并没有必要. 尽可能减少内核态中功能的做法更好. 要知道, 代码量越大, bug 越多. </p><p><strong>思想</strong><br>为了实现高可靠性,将操作系统划分成更小的, 良好定义的模块. 只有其中一个模块—<code>微内核</code> 运行在内核态上. </p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/os-general-2.png" alt="os-general-2"></p><p><a id="markdown-client-server模式" name="client-server模式"></a></p><h3 id="Client-Server模式"><a href="#Client-Server模式" class="headerlink" title="Client-Server模式"></a>Client-Server模式</h3><p>将进程划分为两类,</p><ul><li>服务器: 提供服务</li><li>客户端: 使用服务</li></ul><p>通常在系统最底层是微内核</p><p><a id="markdown-虚拟机" name="虚拟机"></a></p><h3 id="虚拟机"><a href="#虚拟机" class="headerlink" title="虚拟机"></a>虚拟机</h3><p><a id="markdown-优点" name="优点"></a></p><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><ul><li>节省硬件成本, 如不同服务器(web, ftp, mail…)可以在一台宿主机上</li><li><strong>同时</strong>运行多个 OS 的需求(如软件开发过程, 运行在某些 OS 不支持的程序)</li><li>检查点, 虚拟机的迁移: 仅需移动内存映像</li></ul><p><a id="markdown-条件" name="条件"></a></p><h4 id="条件"><a href="#条件" class="headerlink" title="条件"></a>条件</h4><ul><li><code>敏感指令( sensitive instruction )</code>.:有<code>内核态</code>和<code>用户态</code>的 CPU 的一组只能在内核态执行的指令集, 比如 I/O 指令, 改变 MMU 状态的指令等,</li><li><code>特权指令( privileged insttruction)</code>: 在用户态下执行会引起陷入</li></ul><p>当 敏感指令是  特权指令的子集时, 机器才是可虚拟化的</p><p>解决的基本思想: 创建容器使得虚拟机在其内运行</p><p><a id="markdown-ⅰ型管理程序" name="ⅰ型管理程序"></a></p><h4 id="Ⅰ型管理程序"><a href="#Ⅰ型管理程序" class="headerlink" title="Ⅰ型管理程序"></a>Ⅰ型管理程序</h4><p>在其上的 OS 称为客户操作系统</p><p><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/virtual machine.png" alt="virtual machine"></p><p>虚拟机在用户态以用户进程的身份运行, 因此不允许执行敏感指令,否则崩溃</p><p>在支持VT技术的 CPU 上, 客户操作系统执行敏感指令会发生陷入. 管理程序分析指令</p><ul><li>来自客户操作系统: 管理程序调度将要执行的指令</li><li>来自虚拟机中用户程序: 仿真面对运行在用户态的敏感指令时真是硬件的行为</li></ul><p><a id="markdown-ⅱ型管理程序" name="ⅱ型管理程序"></a></p><h4 id="Ⅱ型管理程序"><a href="#Ⅱ型管理程序" class="headerlink" title="Ⅱ型管理程序"></a>Ⅱ型管理程序</h4><p>在其上的 OS 称为宿主操作系统,如上图 b</p><p>VMware就是采用的这种管理程序:<br> 当运行一个二进制文件, VMware 先浏览代码段以寻找<code>基本块(basic block)</code>. </p><p>所谓基本块就是以 jump, call, trap 等改变控制流的指令结束的可顺序执行的指令序列, 而基本块中就不含其他改变 程序计数器 的指令</p><p>如果基本块中含有敏感指令, VMware 将其替换为相应的 VMware 过程调用, 基本块的最后一条指令也被过程调用替换</p><p>这种 找出, 仿真敏感指令 的技术称为 <code>二进制翻译(binary translation)</code></p><p>所以, 即使在不可虚拟化的硬件上, Ⅱ型 也能正常: 因为所有的敏感指令被仿真, 不会被真正的硬件执行,管理程序的调用可以代替.</p><p><a id="markdown-准虚拟化paravirtualized" name="准虚拟化paravirtualized"></a></p><h4 id="准虚拟化-paravirtualized"><a href="#准虚拟化-paravirtualized" class="headerlink" title="准虚拟化(paravirtualized)"></a>准虚拟化(paravirtualized)</h4><p>前面介绍的两种, 在其上的客户操作系统都是没有修改过的. 可以更改客户操作系统的源码, 将其中的敏感指令都转换为 管理程序调用. 这就要给管理 程序 定义 过程调用集合, 从而形成 API, 虽然这个接口是供客户 OS, 而不是 应用程序. </p><p>其实就将管理程序变成了一个微内核.</p><p>这种方法就是准虚拟化, 这样会使得虚拟机技术更容易被支持和使用<br><img src="https://raw.githubusercontent.com/mbinary/mbinary.github.io/hexo/source/images/os-general-3.png" alt="os-general-3"></p><p>问题:</p><ul><li>如果所有敏感指令都被代替, 操作系统不能在物理机器上运行</li><li>如果市场下有很多管理程序, 有不同的 API 接口</li></ul><p>Amsden 的一个解决方案:<br>当内核需要执行一些敏感指令操作时会调用特殊的例程(称为 VMI 虚拟机接口), VMI 形成的底层与硬件或管理程序进行交互. 将 VMI 设计得通用化, 不依赖硬件或特定的管理程序</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;操作系统引论&lt;/strong&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="操作系统" scheme="https://mbinary.xyz/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    
      <category term="操作系统" scheme="https://mbinary.xyz/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
      <category term="读书笔记" scheme="https://mbinary.xyz/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
</feed>
