This commit is contained in:
krahets
2024-05-31 12:45:23 +08:00
parent 1a7a8ac395
commit 6abac5a2e7
39 changed files with 1316 additions and 453 deletions
@@ -3606,14 +3606,14 @@
</ol>
<p>也就是說,在能夠解決問題的前提下,演算法效率已成為衡量演算法優劣的主要評價指標,它包括以下兩個維度。</p>
<ul>
<li><strong>時間效率</strong>:演算法執行速度的快慢</li>
<li><strong>時間效率</strong>:演算法執行時間的長短</li>
<li><strong>空間效率</strong>:演算法佔用記憶體空間的大小。</li>
</ul>
<p>簡而言之,<strong>我們的目標是設計“既快又省”的資料結構與演算法</strong>。而有效地評估演算法效率至關重要,因為只有這樣,我們才能將各種演算法進行對比,進而指導演算法設計與最佳化過程。</p>
<p>效率評估方法主要分為兩種:實際測試、理論估算。</p>
<h2 id="211">2.1.1 &nbsp; 實際測試<a class="headerlink" href="#211" title="Permanent link">&para;</a></h2>
<p>假設我們現在有演算法 <code>A</code> 和演算法 <code>B</code> ,它們都能解決同一問題,現在需要對比這兩個演算法的效率。最直接的方法是找一臺計算機,執行這兩個演算法,並監控記錄它們的執行時間和記憶體佔用情況。這種評估方式能夠反映真實情況,但也存在較大的侷限性。</p>
<p>一方面,<strong>難以排除測試環境的干擾因素</strong>。硬體配置會影響演算法的效能。比如在某臺計算機中,演算法 <code>A</code> 的執行時間比演算法 <code>B</code> 短;但在另一臺配置不同的計算機中,可能得到相反的測試結果。這意味著我們需要在各種機器上進行測試,統計平均效率,而這是不現實的。</p>
<p>一方面,<strong>難以排除測試環境的干擾因素</strong>。硬體配置會影響演算法的效能表現。比如一個演算法的並行度較高,那麼它就更適合在多核 CPU 上執行,一個演算法的記憶體操作密集,那麼它在高效能記憶體上的表現就會更好。也就是說,演算法在不同的機器上的測試結果可能是不一致的。這意味著我們需要在各種機器上進行測試,統計平均效率,而這是不現實的。</p>
<p>另一方面,<strong>展開完整測試非常耗費資源</strong>。隨著輸入資料量的變化,演算法會表現出不同的效率。例如,在輸入資料量較小時,演算法 <code>A</code> 的執行時間比演算法 <code>B</code> 短;而在輸入資料量較大時,測試結果可能恰恰相反。因此,為了得到有說服力的結論,我們需要測試各種規模的輸入資料,而這需要耗費大量的計算資源。</p>
<h2 id="212">2.1.2 &nbsp; 理論估算<a class="headerlink" href="#212" title="Permanent link">&para;</a></h2>
<p>由於實際測試具有較大的侷限性,因此我們可以考慮僅透過一些計算來評估演算法的效率。這種估算方法被稱為<u>漸近複雜度分析(asymptotic complexity analysis</u>,簡稱<u>複雜度分析</u></p>
@@ -3623,8 +3623,9 @@
<li>“隨著輸入資料大小的增加”意味著複雜度反映了演算法執行效率與輸入資料體量之間的關係。</li>
<li>“時間和空間的增長趨勢”表示複雜度分析關注的不是執行時間或佔用空間的具體值,而是時間或空間增長的“快慢”。</li>
</ul>
<p><strong>複雜度分析克服了實際測試方法的弊端</strong>,體現在以下個方面。</p>
<p><strong>複雜度分析克服了實際測試方法的弊端</strong>,體現在以下個方面。</p>
<ul>
<li>它無需實際執行程式碼,更加綠色節能。</li>
<li>它獨立於測試環境,分析結果適用於所有執行平臺。</li>
<li>它可以體現不同資料量下的演算法效率,尤其是在大資料量下的演算法效能。</li>
</ul>
@@ -4318,7 +4318,7 @@
<ul>
<li><strong>時間複雜度能夠有效評估演算法效率</strong>。例如,演算法 <code>B</code> 的執行時間呈線性增長,在 <span class="arithmatex">\(n &gt; 1\)</span> 時比演算法 <code>A</code> 更慢,在 <span class="arithmatex">\(n &gt; 1000000\)</span> 時比演算法 <code>C</code> 更慢。事實上,只要輸入資料大小 <span class="arithmatex">\(n\)</span> 足夠大,複雜度為“常數階”的演算法一定優於“線性階”的演算法,這正是時間增長趨勢的含義。</li>
<li><strong>時間複雜度的推算方法更簡便</strong>。顯然,執行平臺和計算操作型別都與演算法執行時間的增長趨勢無關。因此在時間複雜度分析中,我們可以簡單地將所有計算操作的執行時間視為相同的“單位時間”,從而將“計算操作執行時間統計”簡化為“計算操作數量統計”,這樣一來估算難度就大大降低了。</li>
<li><strong>時間複雜度也存在一定的侷限性</strong>。例如,儘管演算法 <code>A</code><code>C</code> 的時間複雜度相同,但實際執行時間差別很大。同樣,儘管演算法 <code>B</code> 的時間複雜度比 <code>C</code> 高,但在輸入資料大小 <span class="arithmatex">\(n\)</span> 較小時,演算法 <code>B</code> 明顯優於演算法 <code>C</code>在這些情況,我們很難僅憑時間複雜度判斷演算法效率的高低。當然,儘管存在上述問題,複雜度分析仍然是評判演算法效率最有效且常用的方法。</li>
<li><strong>時間複雜度也存在一定的侷限性</strong>。例如,儘管演算法 <code>A</code><code>C</code> 的時間複雜度相同,但實際執行時間差別很大。同樣,儘管演算法 <code>B</code> 的時間複雜度比 <code>C</code> 高,但在輸入資料大小 <span class="arithmatex">\(n\)</span> 較小時,演算法 <code>B</code> 明顯優於演算法 <code>C</code>對於此類情況,我們時常難以僅憑時間複雜度判斷演算法效率的高低。當然,儘管存在上述問題,複雜度分析仍然是評判演算法效率最有效且常用的方法。</li>
</ul>
<h2 id="232">2.3.2 &nbsp; 函式漸近上界<a class="headerlink" href="#232" title="Permanent link">&para;</a></h2>
<p>給定一個輸入大小為 <span class="arithmatex">\(n\)</span> 的函式:</p>
@@ -6552,10 +6552,12 @@ O(\log_m n) = O(\log_k n / \log_k m) = O(\log_k n)
<a id="__codelineno-182-2" name="__codelineno-182-2" href="#__codelineno-182-2"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;線性對數階&quot;&quot;&quot;</span>
<a id="__codelineno-182-3" name="__codelineno-182-3" href="#__codelineno-182-3"></a> <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
<a id="__codelineno-182-4" name="__codelineno-182-4" href="#__codelineno-182-4"></a> <span class="k">return</span> <span class="mi">1</span>
<a id="__codelineno-182-5" name="__codelineno-182-5" href="#__codelineno-182-5"></a> <span class="n">count</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">linear_log_recur</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">linear_log_recur</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
<a id="__codelineno-182-6" name="__codelineno-182-6" href="#__codelineno-182-6"></a> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<a id="__codelineno-182-7" name="__codelineno-182-7" href="#__codelineno-182-7"></a> <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<a id="__codelineno-182-8" name="__codelineno-182-8" href="#__codelineno-182-8"></a> <span class="k">return</span> <span class="n">count</span>
<a id="__codelineno-182-5" name="__codelineno-182-5" href="#__codelineno-182-5"></a> <span class="c1"># 一分為二,子問題的規模減小一半</span>
<a id="__codelineno-182-6" name="__codelineno-182-6" href="#__codelineno-182-6"></a> <span class="n">count</span> <span class="o">=</span> <span class="n">linear_log_recur</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">linear_log_recur</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
<a id="__codelineno-182-7" name="__codelineno-182-7" href="#__codelineno-182-7"></a> <span class="c1"># 當前子問題包含 n 個操作</span>
<a id="__codelineno-182-8" name="__codelineno-182-8" href="#__codelineno-182-8"></a> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<a id="__codelineno-182-9" name="__codelineno-182-9" href="#__codelineno-182-9"></a> <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<a id="__codelineno-182-10" name="__codelineno-182-10" href="#__codelineno-182-10"></a> <span class="k">return</span> <span class="n">count</span>
</code></pre></div>
</div>
<div class="tabbed-block">