Jekyll2024-01-10T08:32:33+00:00https://cynthiachuang.github.io/atom.xml辛西亞的技能樹自己整理過才是自己的辛西亞.CynthiaID驗證系列|公司統一編號驗2023-12-20T00:07:00+00:002023-12-20T00:07:00+00:00https://cynthiachuang.github.io/Check-Tax-ID-Number<p>又是不務正業的一篇 XDDD <br />
不過統編的資料有點少,所有的資料看來都出自同一個地方。</p>
<!--more-->
<p class="illustration">
<img src="https://i.imgur.com/GPyBBgm.jpg?1" alt="公司統一編號" />
公司統一編號
</p>
<div class="alert warning">
<div class="head">公司統一編號驗證檢查碼邏輯修正說明</div>
1. 統一編號預估空號將於 2024 年用罄,故擴增統一編號。<br />
2. 為相容新舊統一編號,檢查邏輯由可被『10』整除改為可被『5』整除。<br />
`Sum%10=0` -> `Sum%5=0` <br />
3. 預計 <b>2023 年 4 月</b>以後,逐步釋出新統一編號。<br />
4. 不過是說 2023 年才釋出的編號,我改這麼快幹嘛啦 XD<br />
</div>
<h2 id="編號規則">編號規則</h2>
<p>目前現行的統編是 <strong>8 個數字</strong>。在驗證時,會將其乘上相對應的權重分別得到每個位元乘積的十位與個位數的:</p>
<table>
<thead>
<tr>
<th>Index</th>
<th>$n_0$</th>
<th>$n_1$</th>
<th>$n_2$</th>
<th>$n_3$</th>
<th>$n_4$</th>
<th>$n_5$</th>
<th>$n_6$</th>
<th>$n_7$</th>
</tr>
</thead>
<tbody>
<tr>
<td>權重</td>
<td>1</td>
<td>2</td>
<td>1</td>
<td>2</td>
<td>1</td>
<td>2</td>
<td>4</td>
<td>1</td>
</tr>
<tr>
<td>乘積十位數</td>
<td>$t_0$</td>
<td>$t_1$</td>
<td>$t_2$</td>
<td>$t_3$</td>
<td>$t_4$</td>
<td>$t_5$</td>
<td>$t_6$</td>
<td>$t_7$</td>
</tr>
<tr>
<td>乘積個位數</td>
<td>$d_0$</td>
<td>$d_1$</td>
<td>$d_2$</td>
<td>$d_3$</td>
<td>$d_4$</td>
<td>$d_5$</td>
<td>$d_6$</td>
<td>$d_7$</td>
</tr>
</tbody>
</table>
<p>再將每個位元的乘積的十位與個位數兩兩相加,得到一新的 8 碼數字 $s_0s_1s_2s_3s_4s_5s_6s_7$:</p>
\[s_i = t_i + d_i,\text{ where } 0 \le i \le 7 \text{ and } 0 \le s_i < 10\]
<p><br class="big" /></p>
<p>最後新的 8 碼數字總和若為 <strong>10 的倍數</strong>,即為有效的驗證碼。</p>
\[Sum\%10=0,\text{ where } Sum = \sum_{i=0}^{7}s_i\]
<p>不過在 <strong>2023 年 4 月</strong>逐步釋出新的統一編號後,若為相容新舊統一編號,則應檢查數字總和是否為 <strong>5 的倍數</strong>:</p>
\[Sum\%5=0,\text{ where } Sum = \sum_{i=0}^{7}s_i\]
<h3 id="例外n_6--7">例外:$n_6 = 7$</h3>
<p>不過上述的計算過程中,會有一個例外情況,就是當 $n_6 = 7$ 時,當其乘上對應權重 $4$ 後,會得到:</p>
\[t_6 = 2 , d_6 = 8\]
<p>但,兩者相加後,其值會大於 10:</p>
\[s_6 = t_6 + d_6 = 2 + 8 = 10\]
<p><br class="big" /></p>
<p>當遇到這情況時,會採用一個折衷的方法,將 $10$ 的兩個位數分別當成:</p>
\[s_6 = 1, s_6' = 0\]
<p><br class="big" /></p>
<p>把這兩個數分別計算總和,得到 $Sum$ 與 $Sum’$ :</p>
\[Sum = \sum_{i=0}^{7}s_i \text{ , }
Sum' = \sum_{i=0, i \not = 6}^{7}s_i + s_6'\]
<p><br class="big" /></p>
<p>若其中一個和為 <strong>10 的倍數</strong>,即為有效的驗證碼。</p>
\[Sum\%10=0 \text{ or } Sum'\%10=0\]
<p>一樣若在新版統編釋出後,為相容兩者檢查邏輯則改為:其中一個和為 <strong>5 的倍數</strong></p>
\[Sum\%5=0 \text{ or } Sum'\%5=0\]
<h2 id="程式碼">程式碼</h2>
<p>這次驗證規則有點瑣碎,regexp 只能用來驗證是否為數字與長度。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>^\d{8}$
</code></pre></div></div>
<p><br class="big" /></p>
<p>至於其他的驗證只能靠程式了,regexp 派不上用場,對了這次是 C++。偷個懶直接把 checkNumber 拉成變數並設置為 5,就自己視需求要用 10 或 5。</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <regex>
#include <string>
#include <iostream>
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
<span class="kt">bool</span> <span class="nf">checkTaxId</span><span class="p">(</span><span class="n">string</span> <span class="n">taxId</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[]){</span>
<span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="o">++</span><span class="n">count</span><span class="p">]);</span>
<span class="k">if</span><span class="p">(</span><span class="n">count</span><span class="o">!=</span><span class="mi">2</span><span class="p">){</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"Give me an Tax-ID-Number!!!!"</span><span class="p">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">string</span> <span class="n">taxId</span> <span class="o">=</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
<span class="kt">bool</span> <span class="n">verification</span> <span class="o">=</span> <span class="n">checkTaxId</span><span class="p">(</span><span class="n">taxId</span><span class="p">);</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="n">boolalpha</span> <span class="o"><<</span> <span class="n">verification</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">bool</span> <span class="nf">checkTaxId</span><span class="p">(</span><span class="n">string</span> <span class="n">idStr</span><span class="p">){</span>
<span class="n">regex</span> <span class="n">reg</span><span class="p">(</span><span class="s">"^</span><span class="se">\\</span><span class="s">d{8}$"</span><span class="p">);</span>
<span class="n">smatch</span> <span class="n">m</span><span class="p">;</span>
<span class="n">ssub_match</span> <span class="n">sm</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">regex_match</span><span class="p">(</span><span class="n">idStr</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">reg</span><span class="p">)){</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"Fail, 長度錯誤"</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">8</span> <span class="p">;</span>
<span class="kt">int</span> <span class="n">idArray</span><span class="p">[</span><span class="n">len</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">weight</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">};</span>
<span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
<span class="c1">// conver char to int </span>
<span class="n">idArray</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">idStr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="sc">'0'</span> <span class="p">;</span>
<span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="n">idArray</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">weight</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="n">p</span><span class="o">/</span><span class="mi">10</span> <span class="o">+</span> <span class="n">p</span><span class="o">%</span><span class="mi">10</span><span class="p">;</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">==</span><span class="mi">10</span><span class="o">?</span><span class="mi">0</span><span class="o">:</span><span class="n">s</span><span class="p">;</span>
<span class="n">sum</span> <span class="o">+=</span> <span class="n">s</span> <span class="p">;</span>
<span class="p">}</span>
<span class="c1">// when s[6]==7, after adding the two, its value will be equal to 10, in this case, 1 or 0 should be used to calculate the sum. But here 10 is used directly to calculate the sum, because if 0 and 10 are used to take the remainder, both are 0; if 1 is taken to take the remainder as 0, it can be reversed that the remainder should be 9. </span>
<span class="kt">int</span> <span class="n">checkNumber</span> <span class="o">=</span> <span class="mi">5</span> <span class="p">;</span>
<span class="kt">bool</span> <span class="n">isLegal</span> <span class="o">=</span> <span class="n">sum</span><span class="o">%</span><span class="n">checkNumber</span><span class="o">==</span><span class="mi">0</span> <span class="o">||</span> <span class="p">((</span><span class="n">sum</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">%</span><span class="n">checkNumber</span><span class="o">==</span><span class="mi">0</span> <span class="o">&&</span> <span class="n">idArray</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span><span class="o">==</span><span class="mi">7</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">isLegal</span><span class="p">){</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"Fail, 不合法的統編驗證"</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">isLegal</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="參考資料">參考資料</h2>
<ol>
<li>林壽山 (2013-03-17)。<a href="https://superlevin.ifengyuan.tw/%E7%87%9F%E5%88%A9%E4%BA%8B%E6%A5%AD%E7%B5%B1%E4%B8%80%E7%B7%A8%E8%99%9F%E9%82%8F%E8%BC%AF%E6%AA%A2%E6%9F%A5%E6%96%B9%E6%B3%95/">營利事業統一編號邏輯檢查方法</a> 。檢自 Levin’s Blog-林壽山 (2020-10-23)。</li>
<li>hero (2018-11-07)。<a href="http://herolin.webhop.me/entry/is-valid-TW-company-ID/">營利事業統一編號驗證完全手冊(Javascript,Java,C#,PHP)</a> 。檢自 Hero Think~用手摀住我的嘴 (2020-10-23)。</li>
<li>beethobear (2006-10-30)。<a href="http://phorum.study-area.org/index.php/topic,11397.html">[商用軟體]統一編號檢查碼規則</a> 。檢自 酷!學園 (2020-10-23)。</li>
<li>電子發票組 (2021-12-22)。<a href="https://www.fia.gov.tw/singlehtml/3?cntId=c4d9cff38c8642ef8872774ee9987283">營利事業統一編號檢查碼邏輯修正說明</a> 。檢自 財政部財政資訊中心 (2022-05-01)。</li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-12-20</summary>
<ul>
<li>2023-12-20 更新:使計算過程與文件相符,但不影響判別是否為合法統編</li>
<li>2022-05-01 更新:檢查碼邏輯修正說明</li>
<li>2022-05-01 更新:檢查碼判別式錯誤修正</li>
<li>2020-12-31 發布</li>
<li>2020-10-24 完稿</li>
<li>2020-10-23 起稿</li>
</ul>
</details>辛西亞.Cynthia又是不務正業的一篇 XDDD 不過統編的資料有點少,所有的資料看來都出自同一個地方。Survey|Label Studio2023-12-20T00:00:00+00:002023-12-20T00:00:00+00:00https://cynthiachuang.github.io/Survey-Label-Studio<p>我竟然還是從草稿夾中把這篇給翻出來填坑…。上次搞這個都是一年前了,但最近又被重新 assign 了這個 project,也只能重新去把記憶給找回來了。嘖…要回想一年多前做了什麼事情真是難倒我了==</p>
<p>上次看的時候時候版本還是 1.0,現在都 1.6 啦…不過筆記應該沒有差多少?應該…</p>
<!--more-->
<p class="illustration">
<img src="https://i.imgur.com/rlEl5Z6.png" alt="Label Studio" />
Label Studio(圖片來源: <a href="https://github.com/heartexlabs/label-studio">GitHub</a>)
</p>
<h2 id="overview">Overview</h2>
<p>故事的開始好像是從 CVAT 開始,它是套是開源的影像標注工具,功能雖然完善但主要專注於影像方面,界面也稍嫌複雜。如果要標注文本或是 NLP 就需要另尋它法…雖然我拿 excel 也能標啦 XDDD</p>
<p>在找標注工具時,大概考慮了幾個點:</p>
<ol>
<li><strong>開源</strong>:<del>大概是因為老闆不想多付錢 XDDD</del></li>
<li><strong>友好界面,有 UI 支援更加</strong>:都得標文字資料,再不來點 UI 洗洗眼睛,應該很快就膩了。</li>
<li><strong>協作功能</strong>:相信我不會想要一個人標完整個資料集。</li>
<li><strong>管理機制</strong>:主要是看訪控權限跟 project 管理機制,免得有天兵砍我資料(也不是沒發生過==。</li>
</ol>
<p>我是有看到一套我還滿喜歡的 <a href="https://demo.prodi.gy/?=null&view_id=ner_manual">Prodigy</a>,不過它看起來只能標注文本,重點是它不是開源的。Survey 到最後上位的就是 Label Studio 啦~!</p>
<p class="illustration">
<img src="https://i.imgur.com/toUwkBX.png" alt="What is Label Studio?" />
What is Label Studio?(圖片來源: <a href="https://labelstud.io/">Label Studio 官網</a>)
</p>
<p>忽然意識到他們有換配色耶,尾巴的顏色也不太一樣了…不過好像舊版的顏色比較合我胃口?</p>
<h3 id="labeling-workflow">Labeling Workflow</h3>
<p>開始看 Label Studio 之前,先看下標注流程,因為無論是要進行人工標注(Human Labeling)或是半監督學習(Semi-supervised Labeling),第一步都是資料標注。</p>
<p>其中人機協作是常見的資料標注流程:</p>
<ol>
<li><strong>資料收集</strong>:<br />
通過爬蟲、程式或者其它工具,將資料保存到資料庫中。</li>
<li><strong>資料前處理/資料標注</strong>:<br />
因為收集回來的資料多是雜亂無章的,若要標注、甚至進行訓練前,都必需先將資料進行清洗。清洗完的資料可以直接用於無監督學習(Unsupervised Labeling)做預訓練模型,也可以將整理後的資料進行標注,變成有標籤資料。<br /><br />
標注的方式與目標,會依照訓練目的而有所不同。例如對於文本的標注有:文本分類、命名實體識別、文本摘要…等;對於音訊可以做講者分類、情緒識別或轉錄…等;對於圖片則可能有圖像分類、物件檢測、語義分割…等。<br /><br />
是說,如果是多人協作標注資料,最好在開始前確定標注原則…至少…確定要使用的標籤,別出現一人標貓、一人標 cat。</li>
<li><strong>模型訓練</strong>:<br />
有了標注完成的資料後,即可將資料導入,從無到有進行訓練;或者選擇在該領域或是其他領域的模型進行 Fine-Tune 或遷移學習(Transfer Learning)。</li>
<li><strong>模型評估</strong>:<br />
當訓練完成後,使用測試集或是新收集的資料進行檢驗,若是訓練結果無法有良好的表現,可能需要調整參數重新訓練。</li>
<li><strong>重新訓練 or 模型部署</strong>:<br />
不斷重複前 1~4 步,以優化模型和資料,提高模型性能。一旦取得合乎預期的表現後,就可將模型進一步部署在設備端或伺服器上。
<p class="illustration">
<img src="https://i.imgur.com/V7KOD9m.png" alt="Usually Labeling Workflow" />
Usually Labeling Workflow(圖片來源: <a href="https://towardsdatascience.com/introducing-label-studio-a-swiss-army-knife-of-data-labeling-140c1be92881">Towards Data Science</a>)
</p>
</li>
</ol>
<h3 id="what-is-label-studio">What is Label Studio?</h3>
<p>通常完成上述的步驟需耗時數週到數月的時間,所以 Heartex 的首席技術官希望能整合相關功能到自動化的平台,從而減少技術團隊的產品、機器學習的開發、實驗時間學習生命週期。</p>
<p><br class="big" /></p>
<p>Heartex 就是推出 Label Studio 的公司,而 Label Studio 是一套開源的標注工具,它可應用範圍很廣,包含圖片、音訊、文字、影片…等格式的資料。此外它還提供簡明 UI 可快速配置多種資料,能在 10 分鐘內準備好工具,用以標注文本、音訊或圖像…等資料格式。</p>
<p>它另外一個宣傳點則是可將 Label Studio 與機器學習模型進行集成。機器學習可以用來協助標注,以提供標籤預測(預標籤),並可執行持續的主動學習。</p>
<p>不過有些我希望的功能,例如:訪控權限,並不是在開源版本中,不過我這還能接受 XDDD</p>
<p class="illustration">
<img src="https://i.imgur.com/CzmobYV.png" alt="Usually Labeling Workflow" />
Usually Labeling Workflow(圖片來源: <a href="https://towardsdatascience.com/introducing-label-studio-a-swiss-army-knife-of-data-labeling-140c1be92881">Towards Data Science</a>)
</p>
<h3 id="components-and-architecture">Components and Architecture</h3>
<p>根據 Label Studio 的軟體架構圖,系統可以大致分成:<strong>Frontend</strong>、<strong>Backend</strong>、<strong>Task</strong>、<strong>ML Backend</strong> 4 個部份:</p>
<p class="illustration">
<img src="https://i.imgur.com/sE306vT.png" alt="Label Studio Architecture" />
Label Studio Architecture(圖片來源: <a href="https://labelstud.io/guide/index.html#Components-and-architecture">Label Studio Documentation</a>)
</p>
<p>顧名思義,各元件負責前端、執行標注、標籤資料與任務管理、以及串接 ML Backend 的部份。各個元件基本都有提供 <a href="https://labelstud.io/guide/index.html#Components-and-architecture">Source Code</a>,方便開發者自行客製化。</p>
<p>從表中也可以看到各元件的實做方法中,其中大概除了 <a href="https://github.com/mobxjs/mobx-state-tree">MST</a> 我可能找不到替罪同事外,其他應該都可以?而且授權看來都是 <strong>Apache 2.0 LICENSE</strong>,所以可以放心改 XDDD</p>
<p class="illustration">
<img src="https://i.imgur.com/wZU025g.png" alt="Label Studio Components" />
Label Studio Components(圖片來源: <a href="https://labelstud.io/guide/index.html#Components-and-architecture">Label Studio Documentation</a>)
</p>
<h4 id="label-studio-backend">Label Studio Backend</h4>
<ul>
<li><a href="https://github.com/heartexlabs/label-studio/">heartexlabs/label-studio</a></li>
</ul>
<p>基本上,Backend 是它主體了 XDDD 在這份 Source Code 中,你可以找到 frontend 跟 data_manager。安裝所用的 docker 與 docker-compose 都出自於這一份。</p>
<h4 id="label-studio-frontend">Label Studio Frontend</h4>
<ul>
<li>A. <a href="https://github.com/heartexlabs/label-studio/tree/master/label_studio/frontend">heartexlabs / label_studio/frontend</a></li>
<li>B. <a href="https://github.com/heartexlabs/label-studio-frontend">heartexlabs / label-studio-frontend</a></li>
</ul>
<p>A 混在 Backend 的 project 內,安裝 Backend 時會順便起起來,如果要直接起則是 B。但目前從 commit 看來兩邊專案似乎不齊頭?</p>
<p>這個專案所使用到的技術,就是我們在上表看到的 React 跟 MST(mobx-state-tree)。</p>
<h4 id="data-manager">Data Manager</h4>
<ul>
<li><a href="https://github.com/heartexlabs/dm2">heartexlabs/dm2</a></li>
</ul>
<p class="illustration">
<img src="https://i.imgur.com/tTP9yoD.png" alt="Data exploration tool for Label Studio." />
Data exploration tool for Label Studio.(圖片來源: <a href="https://github.com/heartexlabs/dm2">GitHub</a>)
</p>
<p>這專案可讓使用者輕鬆探索資料集,可以選擇如何查看與顯示的資料,如:網格和列表視圖…等。當然這個專案必須跟前端集成,因為 DataManager 使用 LabelStudio API 進行操作。</p>
<p class="illustration">
<img src="https://i.imgur.com/mIWHD8y.png" alt="LabelStudio API Workflow" />
LabelStudio API Workflow(圖片來源: <a href="https://github.com/HumanSignal/dm2/blob/master/docs/dm_architecture_diagram.pdf">dm2|GitHub</a>)
</p>
<h4 id="machine-learning-backends">Machine Learning Backends</h4>
<ul>
<li><a href="https://github.com/heartexlabs/label-studio-ml-backend">heartexlabs/label-studio-ml-backend</a></li>
</ul>
<p>Machine Learning Backends 是一個 SDK,可讓使用者封裝機器學習程式碼並將其變成換為 Web 服務器。最後將該服務器連接到 Label Studio Instance 以執行 2 個任務:</p>
<ol>
<li>基於模型推理結果動態預標注資料</li>
<li>根據最近注釋的資料重新訓練或微調模型</li>
</ol>
<h4 id="label-studio-enterprise-edition">Label Studio Enterprise Edition</h4>
<p>官網是建議,在客製化之前先參考下 Label Studio Enterprise Edition 咩,有了它就可以不用自己造輪子了呦~!不過我沒研究過他的<a href="https://labelstud.io/guide/billing.html">收費標準</a>,有考慮的可能得查一下。</p>
<p class="illustration">
<img src="https://i.imgur.com/MqJnisw.png" alt="Label Studio Features" />
Label Studio Features(圖片來源: <a href="https://labelstud.io/guide/index.html#Components-and-architecture">Label Studio Documentation</a>)
</p>
<p>是說…那個訪控權限跟標注 review 的功能,看起來就很棒!</p>
<h3 id="data-type">Data Type</h3>
<p>它們能支援的格式或說標注動作還不少,以資料類型來說大致包含了 <mark>Images、 Audio、 Text、 Time Series、 Multi-Domain、 Video</mark>:</p>
<ol>
<li><strong>Images</strong>
<ul>
<li>支援圖片分類、物體檢測、語義分割。</li>
</ul>
</li>
<li><strong>Audio</strong>
<ul>
<li>支援音源分類、講者分類、情緒識別、轉錄。</li>
</ul>
</li>
<li><strong>Text</strong>
<ul>
<li>支援文檔分類、NER、問答、情緒分析。</li>
</ul>
</li>
<li><strong>Time Series</strong>
<ul>
<li>支援時間序列分類、分割、事件識別。</li>
</ul>
</li>
<li><strong>Multi-Domain</strong>
<ul>
<li>支援對話處理(呼叫中心錄音可以同時轉錄和處理為文本)、OCR、將影像或音檔分割時間序列數。</li>
</ul>
</li>
<li><strong>Video</strong>
<ul>
<li>支援影像分類、物件追蹤、輔助標記。</li>
</ul>
</li>
</ol>
<p class="illustration">
<img src="https://i.imgur.com/1re3sxj.gif" alt="Label every data type." />
Label every data type.(圖片來源: <a href="https://labelstud.io/">Label Studio</a>)
</p>
<p>各資料所能接受的資料格式如下,不過我好像沒看到 video 的資料格式?是還沒更新上去嗎(video 是 1.6 版新增的)?</p>
<p class="illustration">
<img src="https://i.imgur.com/ALRMGe9.png" alt="Types of data you can import" />
Types of data you can import(圖片來源: <a href="https://labelstud.io/guide/tasks.html#Types-of-data-you-can-import-into-Label-Studio">Label Studio Documentation</a>)
</p>
<h3 id="pros-and-cons">Pros and Cons</h3>
<p>稍微玩了下這套工具,簡單列些優缺點。不過在開始前先看看官方給的優點:</p>
<ol>
<li>
<p><strong>簡單</strong>:<br />
沒有複雜的配置,並且易於集成到機器學習管道中。</p>
</li>
<li>
<p><strong>可針對多種資料類型快速配置</strong>:<br />
可在 10 分鐘內準備好工具,以在標記文本、音源與圖像之間切換。甚至可以同時標注三種類型,不過這種複合式的用法得自製 template 才可用。</p>
<p class="illustration">
<img src="https://i.imgur.com/lvZrPku.png" alt="同時標注三種類型" />
同時標注三種類型
</p>
</li>
<li>
<p><strong>機器學習集成</strong> <br />
它能與所有眾多的機器學習框架和模型集成。ML 有許多不同的約束條件應用程序,Label Studio 必須足夠靈活以處理它們並提供幫助,而不是使其複雜化。</p>
</li>
</ol>
<p><br class="big" /></p>
<p>玩完後,它確實可以稱得上它所宣稱的優點:</p>
<ol>
<li><strong>界面相對漂亮</strong><br />
這套算是我玩過工具中,界面比較漂亮的。</li>
<li><strong>部署方便</strong><br />
它提供了 2~3 種的部署方式,且每種方式的部署也都還滿簡單的。</li>
<li><strong>配置方便,且有多種內置模板與支持多種資料類型</strong><br />
這前面說過了,就不再提了。</li>
<li><strong>機器學習集成</strong><br />
可以串接訓練好的推論服務,達到輔助標注的目的。</li>
</ol>
<p>至於缺點的部份,除了權限機制跟 plug-in 的部份(好吧,這其實也不太算是缺點,是閹割的結果)外,我覺得比較麻煩的是的是它的<strong>錯誤訊息難以閱讀</strong>,這蟲要抓會很麻煩 XDDD</p>
<p class="illustration">
<img src="https://i.imgur.com/rmaUE5O.png" alt="Label Studio 界面" />
Label Studio 界面(圖片來源: <a href="https://labelstud.io/guide/tasks.html#Types-of-data-you-can-import-into-Label-Studio">Label Studio Documentation</a>)
</p>
<h2 id="quick-start">Quick start</h2>
<p>OK,來試著安裝它吧!先貼傳送門,等等應該用的上:</p>
<ul>
<li><a href="https://labelstud.io/guide/install.html">Install and upgrade Label Studio</a></li>
<li><a href="https://labelstud.io/guide/start.html">Start Label Studio</a></li>
<li><a href="https://labelstud.io/guide/storedata.html">Database setup</a></li>
</ul>
<h3 id="system-requirements">System requirements</h3>
<p>安裝前先確定下安裝環境,主要是儲存空間的部份,特別是如果是要作為 production 那空間需要特別注意:</p>
<ol>
<li><strong>軟體要求</strong>
<ul>
<li>Python 3.6 +(不過我覺得應該要 <strong>Python 3.7</strong> + 才對?)</li>
<li>Linux、Windows or MacOSX</li>
<li>PostgreSQL 11.5 版 or SQLite 3.35 +</li>
</ul>
</li>
<li><strong>空間</strong>
<ul>
<li>使用 SQLite 資料庫時,100 萬筆的標注任務佔用大約 2.3GB 的硬碟空間。若是要作為 production,建議使用 <strong>50GB</strong> 的硬碟空間。</li>
<li>至於記憶體的部份至少使用 8GB RAM,但<strong>建議使用 16GB RAM</strong>。</li>
</ul>
</li>
</ol>
<h3 id="install-label-studio--start-label-studio">Install Label Studio & Start Label Studio</h3>
<p>他的安裝方式有 4 種,可以依照需要的方式來安裝:</p>
<ol>
<li>
<p><strong>pip</strong><br />
這大概是最簡單的安裝方式,不過需要注意下 Python 版本,文件中有說需要 <strong>Python 3.7 +</strong> 的版本:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>pip <span class="nb">install</span> <span class="nt">-U</span> label-studio
<span class="nv">$ </span>label-studio
</code></pre></div> </div>
<p><br class="big" /></p>
<p>啟動時,默認 Web 瀏覽器會在 <code class="language-plaintext highlighter-rouge">http://localhost:8080</code>。 這個方式在啟動時是資料庫是使用 SQLite,如果要用 PostgreSQL 可以在啟動時設定:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>label-studio start my_project <span class="nt">--init</span> <span class="nt">-db</span> postgresql
</code></pre></div> </div>
<p>不過還需要設定點環境變數:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">DJANGO_DB</span><span class="o">=</span>default
<span class="nv">POSTGRE_NAME</span><span class="o">=</span>postgres
<span class="nv">POSTGRE_USER</span><span class="o">=</span>postgres
<span class="nv">POSTGRE_PASSWORD</span><span class="o">=</span>
<span class="nv">POSTGRE_PORT</span><span class="o">=</span>5432
<span class="nv">POSTGRE_HOST</span><span class="o">=</span>db
</code></pre></div> </div>
<p><br class="big" /></p>
<p>另外,還有個參數我覺得應該也能派上用場: <code class="language-plaintext highlighter-rouge">LABEL_STUDIO_BASE_DATA_DIR</code> / <code class="language-plaintext highlighter-rouge">--data-dir</code>,前者用在環境變數、後者用在命令列,不過目的是一樣,從文件中看來這個值應該可以讓每次啟動都讀到相同的資料庫…不然每次啟動都要重標資料?怎麼可能啦 XDDD</p>
</li>
<li><strong>Docker</strong><br />
這個大概是我比較常用的方法:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>docker run <span class="nt">-it</span> <span class="nt">-p</span> 8080:8080 <span class="nt">-v</span> <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/mydata:/label-studio/data heartexlabs/label-studio:latest
</code></pre></div> </div>
<p>在 mount 進去的 mydata 資料夾後,會看到存放資料的資料夾 media 跟與標籤存放的 <code class="language-plaintext highlighter-rouge">label_studio.sqlite3</code>。若要換成 PostgreSQL 或是指定資料庫的話可以用 <code class="language-plaintext highlighter-rouge">-e</code> 的參數搭配 <code class="language-plaintext highlighter-rouge">LABEL_STUDIO_DATABASE</code>、<code class="language-plaintext highlighter-rouge">LABEL_STUDIO_BASE_DATA_DIR</code> 這兩個環境變數使用。</p>
<p><br class="big" /></p>
<p>不過我這次在玩 1.6 時候會遇到,一連串 <code class="language-plaintext highlighter-rouge">OpenBLAS blas_thread_init</code> 的 error:</p>
<p class="illustration">
<img src="https://i.imgur.com/qUsoNyU.png" alt="Label Studio 界面" />
Label Studio 界面(圖片來源: <a href="https://labelstud.io/guide/tasks.html#Types-of-data-you-can-import-into-Label-Studio">Label Studio Documentation</a>)
</p>
<p>對於這個我有點莫名奇妙,想想看當你上一秒用 1.0 跑得很愉快的時候,下一秒發現有更新,就順手換到 1.6 版,然後就 GG 了…你能想像這有多崩潰阿 XDDD</p>
<p>而且最悲劇的是,我在 <a href="https://labelstud.io/guide/index.html">Label Studio Documentation</a> 找不到相關的訊息,還好最後 <a href="https://github.com/heartexlabs/label-studio/blob/6efe3b21aad7ba69ed04c28aa68314174c78bfdf/docs/source/guide/install.md#openblas-blas_thread_init-pthread_create-failed-for-thread-x-of-y-operation-not-permitted">git 的 docs</a> 有找到,謝天謝地!</p>
<p>簡而言之,就是 Docker Engine 的版本太低了,文件中要求 Docker Engine 需要 >= <code class="language-plaintext highlighter-rouge">20.10.12</code>。不過我實驗用的環境是 Ubuntu 16.04,Docker Engine 升不上 <code class="language-plaintext highlighter-rouge">20.10.12</code> (艹皿艹 )</p>
</li>
<li>
<p><strong>Docker Compose</strong> <br />
是說如果真要部署 production ,應該用 Docker Compose 比較適合</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/heartexlabs/label-studio.git
<span class="nv">$ </span><span class="nb">cd </span>label-studio
<span class="nv">$ </span>docker-compose up <span class="nt">-d</span>
Creating network <span class="s2">"label-studio_default"</span> with the default driver
Creating volume <span class="s2">"label-studio_static"</span> with default driver
...
Creating label-studio_db_1 ... <span class="k">done
</span>Creating label-studio_app_1 ... <span class="k">done
</span>Creating label-studio_nginx_1 ... <span class="k">done</span>
</code></pre></div> </div>
<p>完成後可以看到有 3 容器被啟動:</p>
<ul>
<li><strong>Label Studio</strong>:就是本體阿。</li>
<li><strong>Nginx</strong>:proxy web server 是用於加載各種靜態資料,包括上傳的音檔、圖像…等。</li>
<li><strong>PostgreSQL</strong>:這邊是用於替代性能較低的 SQLite3。</li>
</ul>
<p>各容器的詳細設定就直接看看 <a href="https://github.com/heartexlabs/label-studio/blob/develop/docker-compose.yml">docker-compose.yml</a> 吧。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fdd6ea6bb8b8 nginx:latest <span class="s2">"/docker-entrypoint.…"</span> About a minute ago Up About a minute 0.0.0.0:8080->80/tcp label-studio_nginx_1
de66bb2f5bfc heartexlabs/label-studio:latest <span class="s2">"./deploy/docker-ent…"</span> About a minute ago Up About a minute 8080/tcp label-studio_app_1
b4108f4d947f postgres:11.5 <span class="s2">"docker-entrypoint.s…"</span> About a minute ago Up About a minute 5432/tcp label-studio_db_1
</code></pre></div> </div>
</li>
<li>
<p><strong>Source Code</strong><br />
這邊通常用有需要改動程式碼的時候會用到。例如若要更改前端,可以進入修改 <code class="language-plaintext highlighter-rouge">frontend/</code> 文件夾,不過改完後後,記得重 build:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>label_studio/frontend/
npm ci
npx webpack
<span class="nb">cd</span> ../..
python label_studio/manage.py collectstatic <span class="nt">--no-input</span>
</code></pre></div> </div>
<p>是說,如果文件建議改這份前端的話,所以前端是以這份為主?</p>
<p><br class="big" /></p>
<p>改完後有兩種用法:</p>
<ol>
<li><strong>直接運行</strong>:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> git clone https://github.com/heartexlabs/label-studio.git
<span class="nb">cd </span>label-studio
<span class="c"># Install all package dependencies</span>
pip <span class="nb">install</span> <span class="nt">-e</span> <span class="nb">.</span>
<span class="c"># Run database migrations</span>
python label_studio/manage.py migrate
<span class="c"># Start the server in development mode at http://localhost:8080</span>
python label_studio/manage.py runserver
</code></pre></div> </div>
</li>
<li><strong>包成 Docker image</strong>:<br />
因為我要部署上伺服器,所以我傾向會把它包成 image。在 source code 中有現成的 dockerfile,所以可以直接 build 了,不過需要注意的是,跟 Docker 一樣 Docker Engine 太低會 build 不起來,不然你<a href="https://stackoverflow.com/questions/71941032/why-i-cannot-run-apt-update-inside-a-fresh-ubuntu22-04">會發現它 apt update 會一直 fail</a>。</li>
</ol>
</li>
</ol>
<p><br class="big" /></p>
<p>啟動完成後就可以看倒吊的…老鼠(!?)</p>
<p class="illustration">
<img src="https://i.imgur.com/6GL45uv.png" alt="Label Studio 界面" />
Label Studio 界面(圖片來源: <a href="https://labelstud.io/guide/tasks.html#Types-of-data-you-can-import-into-Label-Studio">Label Studio Documentation</a>)
</p>
<p><br class="big" /></p>
<p>喔,<a href="https://labelstud.io/blog/what-s-with-the-label-studio-opossums/">查了一下</a>原來牠是負鼠(opossums),而且人家是女孩子,名叫海蒂(Heidi)XDDD</p>
<p class="illustration">
<img src="https://i.imgur.com/5myGKmv.png" alt="Hi, Heidi" />
Hi, Heidi(圖片來源: <a href="https://labelstud.io/blog/what-s-with-the-label-studio-opossums/">Label Studio</a>)
</p>
<h3 id="labeling-workflow-1">Labeling workflow</h3>
<p>安裝完成並啟動 Label Studio 後,就可以開始嘗試標注:</p>
<h4 id="step1-create-accounts-for-label-studio"><strong>Step1: Create accounts for Label Studio.</strong></h4>
<ul>
<li><a href="https://labelstud.io/guide/signup.html">Set up user accounts</a></li>
</ul>
<p>開始前須先註冊並建立一個帳戶,以開始標記資料和設置專案。目前有兩種方式可以註冊:</p>
<ol>
<li>
<p><strong>UI</strong><br />
就會看到剛剛那隻爬爬走的負鼠,在頁面中有一個 sing up 的選項。</p>
</li>
<li>
<p><strong>terminal</strong><br />
另一種是在啟動 Label Studio 時在指令順便建立一個帳號:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>label-studio start <span class="nt">--username</span> <username> <span class="nt">--password</span> <password> <span class="o">[</span><span class="nt">--user-token</span> <token-at-least-5-chars>]
</code></pre></div> </div>
<p>如果是用 docker 則是加上 <code class="language-plaintext highlighter-rouge">LABEL_STUDIO_USERNAME</code> 與 <code class="language-plaintext highlighter-rouge">LABEL_STUDIO_PASSWORD</code> 兩個環境變數:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>docker run <span class="nt">-it</span> <span class="nt">-p</span> 8080:8080 <span class="nt">-v</span> <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/mydata:/label-studio/data <span class="nt">-e</span> <span class="nv">LABEL_STUDIO_USERNAME</span><span class="o">=</span><my-email> <span class="nt">-e</span> <span class="nv">LABEL_STUDIO_PASSWORD</span><span class="o">=</span><my-password> heartexlabs/label-studio:latest
</code></pre></div> </div>
<p><br class="big" /></p>
<p>如果要禁用註冊頁面,讓僅有人使用邀請鏈接人使用,在啟動 Label Studio 前加上環境變數:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">export </span><span class="nv">LABEL_STUDIO_DISABLE_SIGNUP_WITHOUT_LINK</span><span class="o">=</span><span class="nb">true</span>
</code></pre></div> </div>
<p>再用指令啟動並建立一個基礎帳戶,以用於登入 Label Studio:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>label-studio start <span class="nt">--username</span> <username> <span class="nt">--password</span> <password> <span class="o">[</span><span class="nt">--user-token</span> <token-at-least-5-chars>]
</code></pre></div> </div>
<p>或是直接在 docker 啟動指令上加上環境變數:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>docker run <span class="nt">-it</span> <span class="nt">-p</span> 8080:8080 <span class="nt">-v</span> <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/mydata:/label-studio/data <span class="nt">-e</span> <span class="nv">LABEL_STUDIO_DISABLE_SIGNUP_WITHOUT_LINK</span><span class="o">=</span><span class="nb">true</span> <span class="nt">-e</span> <span class="nv">LABEL_STUDIO_USERNAME</span><span class="o">=</span><my-email> <span class="nt">-e</span> <span class="nv">LABEL_STUDIO_PASSWORD</span><span class="o">=</span><my-password> heartexlabs/label-studio:latest
</code></pre></div> </div>
<p>登入 Label Studio 後,再邀請協作者。</p>
</li>
</ol>
<h4 id="step2-set-up-the-labeling-projectset-up-the-labeling-interface"><strong>Step2: Set up the labeling project/Set up the labeling interface.</strong></h4>
<ul>
<li><a href="https://labelstud.io/guide/setup_project.html">Set up your labeling project</a></li>
<li><a href="https://labelstud.io/guide/setup.html">Set up your labeling interface</a></li>
<li><a href="https://labelstud.io/guide/tasks.html">Get data into Label Studio</a></li>
<li><a href="https://labelstud.io/guide/labeling.html">Label and annotate data</a></li>
</ul>
<p>先針對要標記的資料,定義標記類型並配置專案設置:</p>
<ol>
<li>建立專案:
<p class="illustration">
<img src="https://i.imgur.com/a0Ox5mG.png" alt="建立專案" />
</p>
</li>
<li>導入資料以進行標注任務:
<ul>
<li><a href="https://labelstud.io/guide/storage.html">Sync data from external storage</a></li>
</ul>
<p>從文件看來資料來源可以是:<strong>雲端儲存體</strong>,如:Amazon S3、 Google Cloud Storage、 Microsoft Azure Blob storage…等, <strong>Redis database</strong>、 <strong>URL</strong> 與 <strong>Local storage</strong>。</p>
<p>其中從 Local 端上傳資料是比較簡單的做法:</p>
<p class="illustration">
<img src="https://i.imgur.com/GOniSBJ.png" alt="從 Local 端上傳資料" />
<img src="https://i.imgur.com/NkS4Dwf.png" alt="從 Local 端上傳資料" />
</p>
</li>
<li>labeling interface
<ul>
<li><a href="https://labelstud.io/templates/">Templates</a></li>
</ul>
<p>上傳完資料後,就可以開始配置標注模板,制定模板的方式有兩種。一是使用現成的模板,能選擇的方式還頗多,基本上就是參考 <a href="#Data-Type">Data Type</a> 的支援。</p>
<p>因為我手邊的是貓狗資料集,所以我這邊選擇影像分類:</p>
<p class="illustration">
<img src="https://i.imgur.com/HsEWU5w.png" alt="labeling interface" />
</p>
<p class="illustration">
<img src="https://i.imgur.com/GXYAQd4.png" alt="labeling interface" />
</p>
<p><br class="big" /></p>
<p>另一種起相同標注模板的方式是使用 Template 撰寫:</p>
<p class="illustration">
<img src="https://i.imgur.com/aLyHdLH.png" alt="labeling interface" />
</p>
<p class="illustration">
<img src="https://i.imgur.com/45uZECY.png" alt="labeling interface" />
</p>
<p><br class="big" /></p>
<p>如果現有的標注模板不符合使用需求,可以<a href="https://labelstud.io/guide/setup.html#Customize-a-template">自定義符合需求的 template</a>,像是之前提過的標注三種類型的 template:</p>
<details class="details-2">
<summary>自定義 template:</summary>
<pre>
<span><<span>View</span>></span>
<span class="hljs-comment"><!-- Image with Polygons --></span>
<span><<span>View</span> <span>style</span>=<span>"padding: 25px;
box-shadow: 2px 2px 8px #AAA"</span>></span>
<span><<span>Header</span> <span>value</span>=<span>"Label the image with polygons"</span>/></span>
<span><<span>Image</span> <span>name</span>=<span>"img"</span> <span>value</span>=<span>"$image"</span>/></span>
<span><<span>Text</span> <span>name</span>=<span>"text1"</span>
<span>value</span>=<span>"Select label, start to click on image"</span>/></span>
<span><<span>PolygonLabels</span> <span>name</span>=<span>"tag"</span> <span>toName</span>=<span>"img"</span>></span>
<span><<span>Label</span> <span>value</span>=<span>"Airbus"</span> <span>background</span>=<span>"blue"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Boeing"</span> <span>background</span>=<span>"red"</span>/></span>
<span></<span>PolygonLabels</span>></span>
<span></<span>View</span>></span>
<span class="hljs-comment"><!-- Text with multi-choices --></span>
<span><<span>View</span> <span>style</span>=<span>"margin-top: 20px; padding: 25px;
box-shadow: 2px 2px 8px #AAA;"</span>></span>
<span><<span>Header</span> <span>value</span>=<span>"Classify the text"</span>/></span>
<span><<span>Text</span> <span>name</span>=<span>"text2"</span> <span>value</span>=<span>"$text"</span>/></span>
<span><<span>Choices</span> <span>name</span>=<span>"choices1"</span> <span>toName</span>=<span>"img"</span> <span>choice</span>=<span>"multiple"</span>></span>
<span><<span>Choice</span> <span>alias</span>=<span>"wisdom"</span> <span>value</span>=<span>"Wisdom"</span>/></span>
<span><<span>Choice</span> <span>alias</span>=<span>"long"</span> <span>value</span>=<span>"Long"</span>/></span>
<span></<span>Choices</span>></span>
<span></<span>View</span>></span>
<span><<span>View</span> <span>style</span>=<span>"margin-top: 20px; padding: 25px;
box-shadow: 2px 2px 8px #AAA;"</span>></span>
<span><<span>Header</span> <span>value</span>=<span>"Named entity"</span>/></span>
<span><<span>Labels</span> <span>name</span>=<span>"label"</span> <span>toName</span>=<span>"text"</span>></span>
<span><<span>Label</span> <span>value</span>=<span>"Person"</span> <span>background</span>=<span>"red"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Organization"</span> <span>background</span>=<span>"darkorange"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Fact"</span> <span>background</span>=<span>"orange"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Money"</span> <span>background</span>=<span>"green"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Date"</span> <span>background</span>=<span>"darkblue"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Time"</span> <span>background</span>=<span>"blue"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Ordinal"</span> <span>background</span>=<span>"purple"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Percent"</span> <span>background</span>=<span>"#842"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Product"</span> <span>background</span>=<span>"#428"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Language"</span> <span>background</span>=<span>"#482"</span>/></span>
<span><<span>Label</span> <span>value</span>=<span>"Location"</span> <span>background</span>=<span>"rgba(0,0,0,0.8)"</span>/></span>
<span></<span>Labels</span>></span>
<span><<span>Text</span> <span>name</span>=<span>"text"</span> <span>value</span>=<span>"$text"</span>/></span>
<span></<span>View</span>></span>
<span></<span>View</span>></span>
</pre>
</details>
<p class="illustration">
<img src="https://i.imgur.com/lvZrPku.png" alt="同時標注三種類型" />
同時標注三種類型
</p>
</li>
<li>Label and annotate the data. <br />
設定完後就可以嚕貓了 XDDD
<p class="illustration">
<img src="https://i.imgur.com/nIFNsm8.png" alt="貓狗影像分類" />
</p>
<p><br class="big" /></p>
<p>順便試試物件偵測:</p>
<p class="illustration">
<img src="https://i.imgur.com/UKbrhZp.png" alt="物件偵測" />
</p>
<p><br class="big" /></p>
<p>跟車牌標注:</p>
<p class="illustration">
<img src="https://i.imgur.com/anuaKtP.png" alt="貓狗影像分類" />
</p>
</li>
<li>Export the labeled data or the annotations
<ul>
<li><a href="https://labelstud.io/guide/export.html">Export annotations and data from Label Studio</a></li>
</ul>
<p>因為只有企業版本才能從 UI 會出標注結果,所以我們只能用指令來匯出:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$label</span><span class="nt">-studio</span> <span class="nb">export</span> <project-id> <export-format> <span class="nt">--path</span><span class="o">=</span><output-path>
</code></pre></div> </div>
</li>
</ol>
<h3 id="machine-learning-backends-1">Machine Learning Backends</h3>
<p>這邊試著設置 ML Backends。<a href="https://labelstud.io/guide/ml.html">文件中</a>提供了兩種安裝方法:<code class="language-plaintext highlighter-rouge">docker-compose</code> 或是指令。</p>
<p>我這邊使用指令方式安裝:</p>
<ol>
<li>首先先 Clone the repo
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>git clone https://github.com/heartexlabs/label-studio-ml-backend
</code></pre></div> </div>
</li>
<li>Setup environment。建議使用venv,不過我在 label studio 的容器內執行,所以我就沒管它了。
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">cd </span>label-studio-ml-backend
<span class="nv">$ </span>pip <span class="nb">install</span> <span class="nt">-U</span> <span class="nt">-e</span> <span class="nb">.</span>
<span class="nv">$ </span>pip <span class="nb">install</span> <span class="nt">-r</span> label_studio_ml/examples/requirements.txt
</code></pre></div> </div>
</li>
<li>根據範例腳本初始化 ML 後端。
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>label-studio-ml init my_ml_backend <span class="nt">--script</span> label_studio_ml/examples/simple_text_classifier.py
</code></pre></div> </div>
<p>此 ML 後端是 Label Studio 提供的 <a href="https://github.com/heartexlabs/label-studio-ml-backend/blob/master/label_studio_ml/examples/simple_text_classifier/simple_text_classifier.py">example</a>。</p>
</li>
<li>Start ML 後端 server
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>label-studio-ml start my_ml_backend
</code></pre></div> </div>
</li>
<li>最後啟動 Label Studio 後,前往在專案設置頁面的機器學習部分,添加指向 <code class="language-plaintext highlighter-rouge">http://localhost:9090</code> 機器學習模型後端的連結。
<p class="illustration">
<img src="https://i.imgur.com/R7QpvJl.png" alt="連接 ML 後端" />
</p>
</li>
</ol>
<h3 id="demo-畫面">Demo 畫面</h3>
<p>最後附上幾張看起來超級酷的 demo 畫面。</p>
<p class="illustration">
<img src="https://i.imgur.com/LW3JD4E.gif" alt="Demo1" />
</p>
<p class="illustration">
<img src="https://i.imgur.com/cPPynxt.gif" alt="Demo1" />
</p>
<p class="illustration">
<img src="https://i.imgur.com/MV1naEb.gif" alt="Demo1" />
</p>
<p class="illustration">
<img src="https://i.imgur.com/drT44AA.gif" alt="Demo1" />
</p>
<h2 id="題外話">題外話</h2>
<p>是說…我真的覺得綠色那隻比較好看欸?你們覺得呢?</p>
<p class="illustration">
<img src="https://i.imgur.com/rLSg2Ef.gif" alt="題外話" />
</p>
<p><br class="big" /></p>
<p>海蒂小姐真的還滿可愛的 XDDD</p>
<p class="illustration">
<img src="https://i.imgur.com/xtFkeBt.png" alt="題外話" />
</p>
<h2 id="參考資料">參考資料</h2>
<ol>
<li>secsilm (2020-11-10)。<a href="https://blog.csdn.net/u010099080/article/details/104881167">试用开源标注平台 Label Studio_Alan Lee</a>。檢自 CSDN博客 (2021-08-04)。</li>
<li>協同撰寫 (2021-07-28)。<a href="https://github.com/heartexlabs/label-studio">heartexlabs/label-studio</a>。檢自 GitHub (2021-08-04)。</li>
<li>Nikolai Liubimov (2020-01-28)。<a href="https://towardsdatascience.com/introducing-label-studio-a-swiss-army-knife-of-data-labeling-140c1be92881">Introducing Label Studio, a swiss army knife of data labeling</a>。檢自 Towards Data Science (2021-08-04)。</li>
<li>Johnson7788 (2020-12-28)。<a href="https://zhuanlan.zhihu.com/p/339567115">安利一个开源的好工具Label Studio, 闭环数据标注和模型训练</a>。檢自 知乎 (2021-08-04)。</li>
<li><a href="https://labelstud.io/guide/index.html#Components-and-architecture">Components and Architecture</a>。檢自 Label Studio Documentation (2022-12-09)。</li>
<li><a href="https://labelstud.io/guide/label_studio_compare.html">Label Studio features</a>。檢自 Label Studio Documentation (2022-12-09)。</li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-12-20</summary>
<ul>
<li>2023-12-20 更新:更新 API Workflow 圖片來源。</li>
<li>2023-02-16 發布</li>
<li>2022-12-14 完稿</li>
<li>2021-08-04 起稿</li>
</ul>
</details>
<style>
.details-2 summary {
padding: 5px;
background-color: #f0f0f0;
}
.details-2 content {
display: block;
padding: 5px;
background-color: #f5f7f7;
}
.details-2 summary::after {
content: '';
position: absolute;
width: 1em; height: 1em;
margin: .2em 0 0 .5ch;
background: "^"
background-size: 100% 100%;
transition: transform .2s;
}
.details-2:not([open]) summary::after {
margin-top: .25em;
transform: rotate(90deg);
}
.details-2 ::-webkit-details-marker {
display: none;
}
.details-2 ::-moz-list-bullet {
font-size: 0;
}
</style>辛西亞.Cynthia我竟然還是從草稿夾中把這篇給翻出來填坑…。上次搞這個都是一年前了,但最近又被重新 assign 了這個 project,也只能重新去把記憶給找回來了。嘖…要回想一年多前做了什麼事情真是難倒我了== 上次看的時候時候版本還是 1.0,現在都 1.6 啦…不過筆記應該沒有差多少?應該…Survey|Source-To-Image2023-02-16T00:00:00+00:002023-02-16T00:00:00+00:00https://cynthiachuang.github.io/Survey-Source-to-Image<p>過年大掃除,又掃出一篇之前 Survey 過且寫到一半的草稿 XDDD 這應該是去年(2021)年 12 月份筆記,我這次只是把筆記整理到能見人的程度,如果後續有更新,麻煩告知我一下,我有空再來看看 <del>(雖然很有可能會一直沒空下去)</del></p>
<p>這篇的 Servey 目標是玩玩 S2I,順便看看 <mark>Openshift 利用 S2I 提供了哪些 image builder 相關功能</mark>。</p>
<!--more-->
<p class="illustration">
<img src="https://i.imgur.com/ZVTUloI.png" alt="openshift/source-to-image" />
openshift/source-to-image(圖片來源: <a href="https://github.com/openshift/source-to-image">GitHub</a>)
</p>
<h2 id="overview">Overview</h2>
<p>雖然我習慣使用 Dockerfile 從頭構建一個 image。但若你的應用不複雜,則可以考慮下 Image Builder 這些更快捷的方法。Image Builder 通常可藉由指定基礎 image 及提供要安裝工具、函式庫與自定義程式…等,為使用者提供客製化 image。</p>
<h3 id="source-to-images2i">Source-To-Image,S2I</h3>
<p>Source-To-Image(S2I),就是套由 <strong>OpenShift</strong> 所提供的 Image Builder Framework,它可以將你的程式碼編譯後,注入 Builder Image 中以產生新的 image。</p>
<p>這樣的開發方式,是為了方便軟體開發人員對程式碼進行改動,他們毋需了解 Dockerfile 的撰寫與構建,只需要專注在軟體的開發即可, <del>(當然還要學會用 S2I)</del>。其工作流程如下:</p>
<p class="illustration">
<img src="https://imgur.com/dE1zcpi.png" alt="S2I Developer Workflow" />
S2I Developer Workflow(圖片來源: <a href="https://cloud.redhat.com/blog/source-image-s2i-deep-dive-ben-parees-openshift-commons-briefing-43">Red Hat Blog</a>)
</p>
<p>SI2 是套相當便捷的工具,也是 OpenShift 生態系中相當重要的一個工具。我在網路上看到一個<a href="https://www.jianshu.com/p/c06b8ec92ed3">論點</a>,他認為 <mark>OpenShift 之所以能區別於 K8S 成為一個 PaaS 平台,其中一個主要環節就是 SI2</mark>。透過這套工具,在使用時僅需提供程式碼,就能生成 image,並利用 OpenShift 現有的體系架構(Deployment config,DC 和 Build config,BC)直接部署,完成所有的應用的生命週期管理。</p>
<h3 id="pros">Pros</h3>
<p>S2I 除可以讓開發者關注程式碼本身外,亦有以下好處:</p>
<ul>
<li>
<p><strong>關注點分離 SoC</strong><br />
這點有點贅述了,它的意思就是程式碼和 Docker image 是清楚的分離開的,簡而言之就是讓<mark>開發者關注程式碼本身</mark>,不過我覺得這小標看起來很厲害的樣子,所以我還是再貼了一次 XDDD</p>
</li>
<li>
<p><strong>速度 Speed</strong> <br />
這可以想像,依照前面所說的,不需要從 Dockerfile 從頭 build 起,改利用現有 image 的話確實會比較快。</p>
<p>是說我有個疑問,我習慣把程式碼的部份放在 Dockerfile 的最後面,好像也就重 build 最後一層?這樣的話,重 build 最後一層跟編譯注入程式碼會是哪個快? 🤔</p>
<p>嗯…好像少算了寫 Dockerfile 的時間,算了這先別管它好了。</p>
</li>
<li>
<p><strong>可修補性 Patchability</strong><br />
如果 Builder Image 基於安全問題需要修補,可以協助不斷 rebuild 應用的 image。</p>
</li>
<li>
<p><strong>操作安全性 Operational Safety</strong> <br />
這優點在 PaaS 平台上比較能展現。透過 S2I 進而對構建 image 的過程進行限制,可以避免意外或故意濫用構建系統。
<br /><br />
此外,讓使用者直接用 Dockerfile 構建 image,可能主機系統暴露於 root 特權提升的風險中,透過 S2I 限制可以 root 使用者的操作。</p>
</li>
<li>
<p><strong>使用者效率 User efficiency</strong> <br />
其實這跟上一條有點像,不過這是從使用者的角度出發。它可以阻止使用者員在應用構建期間執行任意的 yum 安裝類型操作,避免過度安裝太多的套件導致 image 太過肥大,拖慢開發迭代的效率。</p>
</li>
<li>
<p><strong>生態性 Ecosystem</strong> <br />
鼓勵共享 image 的生態系統,找到並利用最佳實踐的方式來為應用構建映像檔。</p>
</li>
</ul>
<p>上面我直接當搬運工,直接從這篇 <a href="https://access.redhat.com/documentation/zh-cn/openshift_container_platform/4.4/pdf/builds/OpenShift_Container_Platform-4.4-Builds-zh-CN.pdf">〈OpenShift Container Platform 4.4 构建(build)〉</a> 搬過來的 XDDD</p>
<h2 id="required-image-contents">Required Image Contents</h2>
<p class="illustration">
<img src="https://i.imgur.com/TeduiMV.png" height="450px" alt="S2I Developer Workflow" />
S2I Developer Workflow(圖片來源: <a href="https://cloudpak.info/openshift/что-такое-source-2-image/4811">Cloudpak Service</a>)
</p>
<p>在 S2I 中,image 的建構主要是由<strong>三個元素</strong>互動所構成,分別是:<mark>builder image、s2i scripts、source code</mark>。</p>
<h3 id="builder-image">Builder Image</h3>
<p>就是拿來當基底的 image,我在 <a href="https://github.com/sclorg?q=s2i&type=all&language=&sort=">Software Collections</a> 有找到一些支援 S2I framework 的 image,它是有含 Pyhton 的 container 啦,不過像是我們比較常用的 Jupyter、 Tensorfolw、 Pytorch 之類好像都沒有,所以還要來研究下怎麼自定義 S2I framework image。</p>
<p><br class="br" /></p>
<p><a href="https://github.com/openshift/source-to-image/blob/master/examples/nginx-centos7/README.md">研究</a>了下,感覺自定義 S2I framework image 不難,只是必須包含特定文件,也就是我們的第二個元素 <strong>s2i scripts</strong>:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>Required?</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Dockerfile</td>
<td><strong>Yes</strong></td>
<td>Defines the base builder image</td>
</tr>
<tr>
<td>s2i/bin/assemble</td>
<td><strong>Yes</strong></td>
<td>Script that builds the application</td>
</tr>
<tr>
<td>s2i/bin/usage</td>
<td>No</td>
<td>Script that prints the usage of the builder</td>
</tr>
<tr>
<td>s2i/bin/run</td>
<td><strong>Yes</strong></td>
<td>Script that runs the application</td>
</tr>
<tr>
<td>s2i/bin/save-artifacts</td>
<td>No</td>
<td>Script for incremental builds that saves the built artifacts</td>
</tr>
<tr>
<td>test/run</td>
<td>No</td>
<td>Test script for the builder image</td>
</tr>
<tr>
<td>test/test-app</td>
<td>No</td>
<td>Test application source code</td>
</tr>
</tbody>
</table>
<p>這些<a href="https://github.com/openshift/source-to-image/blob/master/docs/cli.md#sti-create">結構</a>不用特別去記憶,可以直接用相對應的指令產生目錄結構:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>s2i create <imageName> <destination> <span class="o">[</span>flags]
</code></pre></div></div>
<p>其中 <code class="language-plaintext highlighter-rouge"><imageName></code> 就是 image 的名字,<code class="language-plaintext highlighter-rouge"><destination></code> 則是要產生目錄的資料夾名稱。詳細的狀況稍後等我們實際玩指令時再來試試。</p>
<h3 id="s2i-scripts">S2I Scripts</h3>
<p>焦點在回到前面提到的 s2i scripts。在前述的表格中,除了 Dockerfile 外還有幾項必備的文件就都是屬於 s2i scripts:</p>
<ul>
<li><strong>[Required] assemble</strong><br />
它會涉及原始碼的編譯、構建。我們會建立一個 assemble scripts 來 build 我們的程式,如:build modules、
bundle install gems、設置應用程式 configuration…之類的。
<br />
預設情況下,build 時程式碼會放在 <code class="language-plaintext highlighter-rouge">/tmp/s2i/src</code> 下,你可依照需求搬移至指定位置。你也可以透過 <code class="language-plaintext highlighter-rouge">io.openshift.s2i.destination label</code>,或傳入 <code class="language-plaintext highlighter-rouge">--destination</code> 參數修改。</li>
<li><strong>[Required] run</strong><br />
生成的最終 image 將以這個 script 作為容器的啟動命令。</li>
<li><strong>[Optional] usage</strong><br />
印出協助資訊,以告知使用者 image 的用法,我理解就是我們平常看到 <code class="language-plaintext highlighter-rouge">--help</code> 所看到的資訊。</li>
<li><strong>[Optional] save-artifacts</strong> <br />
為了實現增量構建,在構建過程中會執行此腳本保存中間構建產物,以節省時間。(待會再詳細介紹)</li>
</ul>
<h3 id="source-code">Source Code</h3>
<p>Source code 顧名思義就是我們要放入 image 中的原始碼。原始碼的來源已有二:</p>
<ol>
<li><strong>遠端 Git Repository</strong> <br />
你可以從遠端的 Git Repository 抓取原始碼來構建映像檔,甚至可以從指定 repo 中的特定資料夾來建構映像檔。</li>
<li><strong>本地端</strong><br />
另一個可以取得原始碼的方法就是從本地端啦 XDDD</li>
</ol>
<h3 id="build-flow">Build Flow</h3>
<p>大概知道三個元素在構建過程中所扮演的角色後,我們可以來綜觀整個構建過程。</p>
<p>S2I 會將 sources 和 scripts 壓成 tar,並將其放入 builder image 中。在執行 assemble scripts 之前,S2I 會將壓縮檔解壓並放置指定位置,然後執行 S2I。完整的構建流程如下:</p>
<p class="illustration">
<img src="https://i.imgur.com/RIbvZ6i.png" alt="Build Workflow" />
Build Workflow(圖片來源: <a href="https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md">GitHub|source-to-image</a>)
</p>
<p>其中會按照下面的順序與位置搜索 scripts :</p>
<ol>
<li>at the <code class="language-plaintext highlighter-rouge">--scripts-url</code> URL</li>
<li>in the application source <code class="language-plaintext highlighter-rouge">.s2i/bin</code> directory(<code class="language-plaintext highlighter-rouge">/usr/libexec/s2i</code>)</li>
<li>at the default image URL(<code class="language-plaintext highlighter-rouge">io.openshift.s2i.scripts-url</code> label)</li>
</ol>
<p>並以下列形式提供位置資訊:</p>
<ol>
<li>image 內的絕對路徑:<code class="language-plaintext highlighter-rouge">image://path_to_scripts_dir</code></li>
<li>主機上的相對或絕對路徑:<code class="language-plaintext highlighter-rouge">file://path_to_scripts_dir</code></li>
<li>URL:<code class="language-plaintext highlighter-rouge">http(s)://path_to_scripts_dir</code></li>
</ol>
<p><br class="big" /></p>
<p>換個較簡單的方法來表示,基本上將 copying、 assemble 與 run 這三個步驟,對應回 Dockerfile 中,可以比較清楚這三個步驟對應的的執行時間。</p>
<p class="illustration">
<img src="https://i.imgur.com/pZrbq3x.png" alt="Build Workflow" />
</p>
<h3 id="save-artifacts">Save Artifacts</h3>
<p>不過像這樣的建構方式,一旦稍有有變動相依性就被打破,導致 assemble script 中相關安裝必須重來。</p>
<p>所以為了使用增量,我們需傳遞 <code class="language-plaintext highlighter-rouge">--incremental=true</code> 參數,並提供一個已構建好的 image 作為 cash,以獲取中間產物:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./s2i build <<span class="nb">source </span>code> <Builder Image> <New Image> <span class="nt">--incremental</span><span class="o">=</span><span class="nb">true</span>
</code></pre></div></div>
<p><br class="big" /></p>
<p>引入增量後,流程會變成這樣:</p>
<p class="illustration">
<img src="https://i.imgur.com/ieSfMSA.png" alt="Build Workflow" />
</p>
<h2 id="installation-and-operation">Installation and Operation</h2>
<p>好了,還是來看看怎麼使用這套軟體吧。</p>
<h3 id="installation">Installation</h3>
<p>首先,當然必須先將它安裝起來 XDDD</p>
<ol>
<li><strong>安裝 s2i</strong><br />
首先先建立一個資料夾,並目前目錄切換置資料夾:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">mkdir </span>s2i
<span class="nv">$ </span><span class="nb">cd </span>s2i
</code></pre></div> </div>
<p>前往 <a href="https://github.com/openshift/source-to-image/releases">releases 頁面</a>,挑選合適的發行版本並解壓縮:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>wget https://github.com/openshift/source-to-image/releases/download/v1.3.1/source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz
<span class="nv">$ </span><span class="nb">tar </span>zxvf source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz
</code></pre></div> </div>
<p><br class="big" /></p>
<p>解壓完成後,會多出兩個資料夾 <code class="language-plaintext highlighter-rouge">sti</code> 與 <code class="language-plaintext highlighter-rouge">s2i</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">ls
</span>s2i source-to-image-v1.3.1-a5a77147-linux-amd64.tar.gz sti
</code></pre></div> </div>
<p>將其中的 s2i 添加到 PATH 環境變數中,或移動到 PATH 的目錄中:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">cp </span>s2i /usr/local/bin
<span class="nv">$ </span>./s2i version
s2i v1.3.1
</code></pre></div> </div>
</li>
</ol>
<h3 id="operation">Operation</h3>
<p>我們從自定義 S2I builder image 開始,並以 builder image 為底構建新的 image。</p>
<h4 id="自定義-s2i-builder-image">自定義 S2I builder image</h4>
<ol>
<li><strong>建立一個 builder image</strong><br />
前面提過不用特別去<a href="https://github.com/openshift/source-to-image/blob/master/docs/cli.md#sti-create">記 builder image 的結構</a>,可以直接用相指令產生目錄結構:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>s2i create <imageName> <destination> <span class="o">[</span>flags]
</code></pre></div> </div>
<p><br class="big" /></p>
<p>實際試試指令的執行效果,將 <code class="language-plaintext highlighter-rouge"><imageName></code> 命名為 test-imag、要產生的目錄資料夾 <code class="language-plaintext highlighter-rouge"><destination></code> 則命名為 test-image-content:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>./s2i create test-image test-image-content
</code></pre></div> </div>
<p>建立目錄後,可以切進目錄以觀察目錄結構。基本上與先前介紹過得相呼應(廢話 XDDD):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">cd </span>test-image-content/
<span class="nv">$ </span>tree
<span class="nb">.</span>
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│ └── bin
│ ├── assemble
│ ├── run
│ ├── save-artifacts
│ └── usage
└── <span class="nb">test</span>
├── run,可以看到先前介紹過得內容:
4 directories, 9 files
</code></pre></div> </div>
</li>
<li>
<p><strong>修改 Dockerfile</strong><br />
這邊開始撰寫所需要 Dockerfile。在這份文件中會有許多提示,依照這些提示填入需要的安裝。</p>
</li>
<li><strong>修改 assemble / run</strong>
<ul>
<li>建立一個 assemble,主要是配置文件和靜態內容複製到目標容器中。<br />
不過這邊我沒做 XDDD</li>
<li>再稍微修改下 run
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">exec</span> ./app.sh
</code></pre></div> </div>
</li>
</ul>
</li>
<li><strong>建立 base image</strong>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>docker build <span class="nt">-t</span> test-image-base test-image-content/.
</code></pre></div> </div>
</li>
</ol>
<h4 id="構建-app-image">構建 app image</h4>
<ol>
<li><strong>撰寫個人程式</strong>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">mkdir </span>myproject
<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"echo 'Hello Word' "</span> <span class="o">></span> myproject/app.sh
</code></pre></div> </div>
</li>
<li><strong>構建 app image</strong>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>s2i build myproject/ test-image-base hello-world-app
</code></pre></div> </div>
<p>如果順利,就會做出新的 image。但若你的 builder image 不符合 s2i 架構,在構建 app image 時就會出現錯誤訊息:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> warning: Image sha256:37e4715d22fd3c00d8b51379e9e0eb556e842d4796d4c582fd4ff814e3d55edd does not contain a value <span class="k">for </span>the io.openshift.s2i.scripts-url label
Build failed
ERROR: An error occurred: failed to <span class="nb">install</span> <span class="o">[</span>assemble run]
ERROR: Suggested solution: <span class="nb">set </span>the scripts URL parameter with the location of the S2I scripts, or check <span class="k">if </span>the image has the <span class="s2">"io.openshift.s2i.scripts-url"</span> label <span class="nb">set
</span>ERROR: If the problem persists consult the docs at https://github.com/openshift/source-to-image/tree/master/docs. Eventually reach us on freenode <span class="c">#openshift or file an issue at https://github.com/openshift/source-to-image/issues providing us with a log from your build using log output level 3.</span>
</code></pre></div> </div>
</li>
</ol>
<p><br class="big" /></p>
<p>是說,我這邊偷懶用了比較簡單的例子。另外這邊有一個範例演示如何構建包含 Nginx 服務的 image builder:</p>
<ul>
<li><a href="https://kubesphere.io/zh/docs/v3.3/project-user-guide/image-builder/s2i-templates/">〈自定义 S2I 模板〉</a></li>
<li><a href="https://www.twblogs.net/a/5c4c35cebd9eee6e7e06f186">〈Openshift S2I構建流程〉</a></li>
</ul>
<h4 id="從-git-repository-抓取原始碼">從 Git Repository 抓取原始碼</h4>
<p>之前說過除了從本地端抓取原始碼外,也可以從遠端抓取原始碼。</p>
<ol>
<li>從遠端 Git Repository 抓取原始碼來構建 image
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>s2i build <git-repo> <S2I Builder Image Repository> <imageName>
</code></pre></div> </div>
</li>
<li>或是從指定 repo 中的特定資料夾抓取原始碼
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>s2i build <git-repo> <span class="nt">--context-dir</span><span class="o">=</span><Path/To/Context> <S2I Builder Image Repository> <imageName>
</code></pre></div> </div>
</li>
</ol>
<h2 id="openshift-s2i">OpenShift S2I</h2>
<p>阿,這篇一開始的目的是要看 OpenShift 利用 S2I 提供了哪些 image builder 相關功能。不過,我完全忘了截 OpenShift 的圖,只能附上幾篇有 UI 的 XDDD</p>
<ul>
<li><a href="https://programmersought.com/article/70153639307/">〈Get s2i image on Openshift〉</a></li>
<li><a href="https://www.cnblogs.com/ericnie/p/9677719.html">〈OpenShift应用镜像构建(1) S2I tomcat 镜像定制〉</a></li>
</ul>
<h2 id="epilogue-and-supplement">Epilogue and Supplement</h2>
<p>好吧,我承認我這篇寫的爛透了 XDDD 畢竟這篇是好久前的事情,希望下一篇不會這麼糟糕 Orz</p>
<p class="illustration">
<img src="https://i.imgur.com/cPqnoom.png" alt="爛透了" />
爛透了(圖片來源: <a href="https://memes.tw/">Meme 梗圖倉庫</a>)
</p>
<p><br class="big" /></p>
<p>手邊還有幾張在文中用不到的圖,但我覺得圖還不錯,所以我就把圖放在這邊提供大家參考了:</p>
<p class="illustration">
<img src="https://i.imgur.com/4sVwCth.png" alt="s2i flow" />
s2i flow(圖片來源: <a href="https://andyyoung01.github.io/2016/08/24/Source-to-image构建代码">Andy's Techblog</a>)
</p>
<p class="illustration">
<img src="https://i.imgur.com/b9Y8tjr.png" alt="s2i flow" />
s2i flow(圖片來源: <a href="https://osninja.io/source-to-image-getting-started-with-s2i-4554dc9daa68">The OpenShift Ninja|Medium </a>)
</p>
<h2 id="參考資料">參考資料</h2>
<ol>
<li><a href="https://v2-1.docs.kubesphere.io/docs/developer/s2i-introduction/">Introduction to S2I</a>。檢自 KubeSphere Documents (2022-12-31)。</li>
<li><a href="https://docs.openshift.com/container-platform/3.11/creating_images/s2i.html">S2I Requirements|OpenShift Container Platform 3.11</a>。檢自 Red Hat OpenShift (2022-12-31)。</li>
<li>Maciej Szulik (2015-07-21)。<a href="https://cloud.redhat.com/blog/create-s2i-builder-image">How to Create an S2I Builder Image</a>。檢自 Red Hat Hybrid Cloud (2022-12-31)。</li>
<li>Diane Mueller (2016-07-08)。<a href="https://cloud.redhat.com/blog/source-image-s2i-deep-dive-ben-parees-openshift-commons-briefing-43">Source-to-Image (S2I) Deep Dive with Ben Parees - OpenShift Commons Briefing #43</a>。檢自 Red Hat Hybrid Cloud (2022-12-31)。</li>
<li>(2021-03-11)。<a href="https://access.redhat.com/documentation/zh-cn/openshift_container_platform/4.4/pdf/builds/OpenShift_Container_Platform-4.4-Builds-zh-CN.pdf">OpenShift Container Platform 4.4 构建(build)</a>。檢自 Red Hat (2022-12-31)。</li>
<li>coreydaley et al. (2022-07-09)。<a href="https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md">s2i builder image requirements</a>。檢自 source-to-image|GitHub (2023-01-12)。</li>
<li>Nautilu, coreydaley (2021-10-18)。<a href="https://github.com/openshift/source-to-image/blob/master/examples/nginx-centos7/README.md">Creating a basic S2I builder imag</a>。檢自 source-to-image|GitHub (202-01-12)。</li>
<li>Vidyasagar Machupalli (2021-10-18)。<a href="https://www.ibm.com/cloud/blog/build-a-container-image-from-source-code-using-s2i">Build a Container Image from Source Code Using S2I and Push It to a Private Registry</a>。檢自 IBM (202-01-12)。</li>
<li>The OpenShift Ninja (2018-07-03)。<a href="https://osninja.io/source-to-image-getting-started-with-s2i-4554dc9daa68">Source-To-Image: Getting Started With s2i</a>。檢自 Medium (2022-12-31)。</li>
<li>elef (2018-11-08)。<a href="https://www.jianshu.com/p/c06b8ec92ed3">第5章 5.2 OpenShift Origin下快速部署JAVA应用</a>。檢自 简书 (2022-12-31)。</li>
<li>brandontsai (2020-09-26)。<a href="https://ithelp.ithome.com.tw/articles/10244344">免 Dockerfile 就可建構 Image 神器 - Source-To-Image (S2I)</a>。檢自 iT 邦幫忙 (2022-12-31)。</li>
<li>张雷 (2018-01-16)。<a href="https://zhuanlan.zhihu.com/p/33046277">Source to Image 工具介绍 - </a>。檢自 知乎 (2022-12-31)。</li>
<li>杨冬 (2016-08-24)。<a href="https://andyyoung01.github.io/2016/08/24/Source-to-image构建代码/">使用Source-to-image(S2I)构建镜像</a>。檢自 Andy’s Techblog (2022-12-31)。</li>
<li>我是读书人 (2020-06-15)。<a href="https://segmentfault.com/a/1190000022936004">使用S2I从源码构建镜像</a>。檢自 SegmentFault 思否 (2022-12-31)。</li>
<li>shenhonglei (2020-07-16)。<a href="https://blog.csdn.net/shenhonglei1234/article/details/107396708">快速学习Source-to-Image (S2I) (一)</a>。檢自 shenhonglei 的博客|CSDN博客 (2022-12-31)。</li>
<li>胡了了 (2017-09-27)。<a href="https://blog.csdn.net/huqigang/article/details/78110233?ops_request_misc=&request_id=&biz_id=102&utm_term=openshift/origin学习记录(9)——S2I镜像&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-78110233.142^v70^js_top,201^v4^add_ask&spm=1018.2226.3001.4187">openshift/origin学习记录(9)——S2I镜像定制(基于Git)</a>。檢自 胡了了的博客|CSDN博客 (2022-12-31)。</li>
<li>胡了了 (2017-10-25)。<a href="https://blog.csdn.net/huqigang/article/details/78338376?ops_request_misc=&request_id=&biz_id=102&utm_term=openshift/origin工作记录(1)——S2I镜像&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-78338376.142^v70^js_top,201^v4^add_ask&spm=1018.2226.3001.4187">openshift/origin工作记录(1)——S2I镜像定制(基于SVN)</a>。檢自 胡了了的博客|CSDN博客 (2022-12-31)。</li>
<li>echochio (2017-05-07)。<a href="https://echochio.pixnet.net/blog/post/44754745-source-to-image(s2i)測試一下">Source-to-image(S2I)測試一下</a>。檢自 echochio|痞客邦 (2022-12-31)。</li>
<li>qq_dao (2019-01-26)。<a href="https://www.twblogs.net/a/5c4c35cebd9eee6e7e06f186">Openshift S2I構建流程</a>。檢自 台部落 (2022-12-31)。</li>
<li><a href="https://programmersought.com/article/70153639307/">Get s2i image on Openshift</a>。檢自 Programmer Sought (2022-12-31)。</li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-16</summary>
<ul>
<li>2023-02-16 發布</li>
<li>2023-01-08 完稿</li>
<li>2022-12-29 起稿</li>
</ul>
</details>辛西亞.Cynthia過年大掃除,又掃出一篇之前 Survey 過且寫到一半的草稿 XDDD 這應該是去年(2021)年 12 月份筆記,我這次只是把筆記整理到能見人的程度,如果後續有更新,麻煩告知我一下,我有空再來看看 (雖然很有可能會一直沒空下去) 這篇的 Servey 目標是玩玩 S2I,順便看看 Openshift 利用 S2I 提供了哪些 image builder 相關功能。CSS 漸層背景2023-02-15T16:46:00+00:002023-02-15T16:46:00+00:00https://cynthiachuang.github.io/CSS-Gradient-Backgrounds<p>我還以為我這篇已經寫完了,結果一切都是我幻想 XDDD</p>
<p>這主要是我當初用在用 <a href="/Mark-Element-is-Used-to-Highlight-Content/">Mark Tag 實作細螢光筆</a>的技術,這邊稍微整理一下相關資料。</p>
<!--more-->
<p class="illustration">
<img src="https://i.imgur.com/ehVOu8l.png" alt="Basic gradients" />
Basic gradients(範例來源: <a href="https://easylogic.medium.com/gradient-tool-conic-gradient-e9dd198921cc">easylogic|Medium</a>)
</p>
<h2 id="gradient">Gradient</h2>
<p>漸層基本上就是一個顏色的漸變過程。既然是漸變,那至少需具備<strong>兩種顏色</strong>,或者說<mark>需要兩種以上的顏色</mark>,並從其中挑出兩種顏色分別設置為是起始色與終點色。</p>
<p>設定完成後,瀏覽器會計算出中間漸變的過程,進而產生一種流動感。除了顏色設定外,還可設定漸變角度、方向與尺寸…等,形成不同的流動感。</p>
<p>不過漸層這東西,如果顏色挑選的好的話,這顏色會非常漂亮,但…如果挑選的不好那會是場災難 XDDD</p>
<p>在 CSS 裡有 6 種漸層,分別是:</p>
<ol>
<li>linear-gradient:線性漸層</li>
<li>radial-gradient:徑向漸層</li>
<li>conic-gradient:錐形漸層</li>
<li>repeating-linear-gradient:線性重複漸層</li>
<li>repeating-radial-gradient:放射重複漸層</li>
<li>repeating-conic-gradient:重複錐形漸層</li>
</ol>
<h2 id="linear-gradient-線性漸層">Linear Gradient 線性漸層</h2>
<p>線性漸層大概是大家第一印象會浮出的樣式。線性漸層的特色就是沿著指定角度上放置數個顏色,並以直線的方式依照此角度逐漸漸變:</p>
<p class="illustration">
<img src="https://i.imgur.com/HEvHg0w.png" alt="紫色和黃色之間呈 45 度角的線性漸變。" />
紫色和黃色之間呈 45 度角的線性漸變。(圖片來源: <a href="https://www.quackit.com/css/functions/css_linear-gradient_function.cfm">Quackit</a>)
</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:linear-gradient</span><span class="o">(</span><span class="nt">direction</span><span class="o">,</span> <span class="nt">color-stop1</span><span class="o">,</span> <span class="nt">color-stop2</span><span class="o">,</span> <span class="o">...);</span>
</code></pre></div></div>
<ol>
<li><strong>direction</strong><br />
漸變的方向或角度,如果沒有設定,預設會是由上至下進行漸層;若是設定角度,則是以左下角為圓心,與正 y 軸的夾角作為角度的來設定。這細節我們等等來實驗會比較清楚。</li>
<li><strong>color-stop</strong><br />
它會依照依方向使用 color 1 → color 2 → … 依序漸變過去。在 color 後方會僅跟著定位參數,將每個顏色放置在漸層線上的特定位置。若沒有指定定位參數,則預設為將第一個顏色放置在 0%、最後一個顏色放置在 100%,其餘顏色則會自動等比例分配。其中 0% 表示起始邊界,100% 表示結束邊界。</li>
</ol>
<h3 id="用關鍵字設置漸變方向">用關鍵字設置漸變方向</h3>
<p>接下來我們就漸變方向的設置方式的不同來實驗。</p>
<p>若想用關鍵字設置漸變方向,這邊有八組關鍵字可用,剛好對應八個方位:<strong>上、下、左、右、上左、下左、上右、下右</strong>,分別為 <strong>to top、to bottom、to left、to right、to top left、to bottom left、to top right、to bottom right</strong>。需特別注意的是 <mark>to</mark> 是必須的,別省略了。</p>
<p>這邊設置柔紫色 <code class="language-plaintext highlighter-rouge">#c5d5fa</code> 為起始色、柔綠色 <code class="language-plaintext highlighter-rouge">#c3dc99</code> 為終點色,並依序設置漸層方向為上、下、左、右,其效果如下:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">top</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">bottom</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">left</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">right</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);;</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/KRyoNCw.png" alt="漸層方向依序為上、下、左、右" />
漸層方向依序為上、下、左、右
</p>
<p>若依序設置漸層方向為上左、下左、上右、下右,其效果如下:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">top</span> <span class="nt">left</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">bottom</span> <span class="nt">left</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">top</span> <span class="nt">right</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">bottom</span> <span class="nt">right</span> <span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/TDOTHk5.png" alt="漸層方向依序為上左、下左、上右、下右" />
漸層方向依序為上左、下左、上右、下右
</p>
<h3 id="用度數設置漸變方向">用度數設置漸變方向</h3>
<p>不過因為用關鍵字只能設定特定方向,若想設置其他角度只能用度數來設定。設定時是<strong>以左下角為圓心,夾角以 12 點鐘方向順時鐘轉動來表示</strong>。</p>
<p>這邊一樣設置柔紫色 <code class="language-plaintext highlighter-rouge">#c5d5fa</code> 為起始色、柔綠色 <code class="language-plaintext highlighter-rouge">#c3dc99</code> 為終點色,並依序設置漸層不同的度數,其效果如下:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="err">0</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="err">30</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="err">45</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="err">60</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="err">90</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/e911Iad.png" alt="漸層方向依序為0度、30度、45度、60度、90度" />
漸層方向依序為0度、30度、45度、60度、90度
</p>
<p>仔細觀察,可以看到漸層依順時鐘的方向在轉動。其中 45 倍數角度的渲染效果基本就跟關鍵字的渲染效果一致。這邊試著依每 30 度為一步旋轉 360 度,其效果如下:</p>
<p class="illustration">
<img src="https://i.imgur.com/125pGOh.png" alt="漸層方向依順時鐘旋轉一圈的效果" />
漸層方向依順時鐘旋轉一圈的效果
</p>
<h3 id="多種顏色漸層">多種顏色漸層</h3>
<p>除了兩色外,漸層的顏色還可以使用多色漸層。以三色為例 <code class="language-plaintext highlighter-rouge">#c5d5fa</code>、<code class="language-plaintext highlighter-rouge">#fcdef4</code>、<code class="language-plaintext highlighter-rouge">#c3dc99</code> ,因為沒有設定定位參數,所以三種顏色平均分配漸層位置,分別是 0%、50%、100%。</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://imgur.com/2cWDQex.png" alt="預設分配位置的三色漸層" />
預設分配位置的三色漸層
</p>
<p>這邊試著把粉紅色 <code class="language-plaintext highlighter-rouge">#fcdef4</code> 的位置由預設的 50%,上提到 15% 的位置。可以看到因為粉紅色 <code class="language-plaintext highlighter-rouge">#fcdef4</code> 的位置上提,導致柔紫色 <code class="language-plaintext highlighter-rouge">#c5d5fa</code> 的位置被壓縮。</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span> <span class="err">0</span><span class="o">%,</span> <span class="nf">#fcdef4</span> <span class="err">15</span><span class="o">%,</span> <span class="nf">#c3dc99</span> <span class="err">100</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/jx2ihat.png" alt="自訂分配位置的三色漸層" />
自訂分配位置的三色漸層
</p>
<p><br class="big" /></p>
<p>如果多一點顏色,還能做出彩虹的效果,就是出來的效果有點傷眼睛 XDDD</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">red</span><span class="o">,</span> <span class="nt">orange</span><span class="o">,</span> <span class="nt">yellow</span><span class="o">,</span> <span class="nt">green</span><span class="o">,</span>
<span class="nt">blue</span><span class="o">,</span> <span class="nt">indigo</span><span class="o">,</span> <span class="nt">purple</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/EkYuUvI.png" alt="彩虹漸層" />
彩虹漸層
</p>
<p>這時可以搭配定位參數的使用,畫出銳利的直線:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">red</span> <span class="err">0</span><span class="o">%,</span> <span class="nt">red</span> <span class="err">15</span><span class="o">%,</span>
<span class="nt">orange</span> <span class="err">15</span><span class="o">%,</span> <span class="nt">orange</span> <span class="err">30</span><span class="o">%,</span>
<span class="nt">yellow</span> <span class="err">30</span><span class="o">%,</span> <span class="nt">yellow</span> <span class="err">45</span><span class="o">%,</span>
<span class="nt">green</span> <span class="err">45</span><span class="o">%,</span> <span class="nt">green</span> <span class="err">60</span><span class="o">%,</span>
<span class="nt">blue</span> <span class="err">60</span><span class="o">%,</span> <span class="nt">blue</span> <span class="err">75</span><span class="o">%,</span>
<span class="nt">indigo</span> <span class="err">75</span><span class="o">%,</span> <span class="nt">indigo</span> <span class="err">90</span><span class="o">%,</span>
<span class="nt">purple</span> <span class="err">90</span><span class="o">%,</span> <span class="nt">purple</span> <span class="err">100</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/wOh7IbF.png" alt="彩虹" />
彩虹
</p>
<p>這效果主要是藉由將兩種顏色位置重疊,最終會直接畫出一條壁壘分明直線:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span> <span class="err">50</span><span class="o">%</span> <span class="o">,</span><span class="nf">#c3dc99</span> <span class="err">50</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/wtIhDIB.png" alt="壁壘分明直線" />
壁壘分明直線
</p>
<h3 id="patterns-with-gradients">Patterns with Gradients</h3>
<p>如果能熟練的使用線性漸層能拼出一些有趣的花樣。像是在 <a href="https://www.oxxostudio.tw/articles/202008/css-gradient.html">oxxo</a> 就弄出了個拼磚:</p>
<p class="illustration">
<img src="https://i.imgur.com/ZvmN70e.png" alt="彩色拼磚" />
彩色拼磚(範例來源: <a href="https://www.oxxostudio.tw/articles/202008/css-gradient.html">OXXO.STUDIO</a>)
</p>
<p>不過這個需要按照尺寸客製化去調整。使用上有點小麻煩。</p>
<p><br class="big" /></p>
<p>到是在 <a href="https://www.quackit.com/css/codes/patterns/">Quackitn</a> 跟 <a href="https://www.casper.tw/css/2013/09/24/css-background/">卡斯伯</a> 這邊找到些有趣的花樣:</p>
<p class="illustration">
<img src="https://i.imgur.com/D2yeUdU.png" alt="grid Patterns" />
<img src="https://i.imgur.com/BubAmOp.png" alt="grid Patterns" />
<img src="https://i.imgur.com/uTdE9Wu.png" alt="Zig-Zag Patterns" />
<img src="https://i.imgur.com/1aQ6wSG.png" alt="Box Patterns" />
<img src="https://i.imgur.com/xlHu2L0.png" alt="Zig-Zag Patterns" />
<img src="https://i.imgur.com/FodellA.png" alt="Pyramid" />
<img src="https://i.imgur.com/7TUeJIm.png" alt="Half-Rombes" />
<img src="https://i.imgur.com/7ABhKhK.png" alt="Zig-Zag Patterns" />
Box and Zig-Zag Background Patterns(範例來源: <a href="https://www.quackit.com/css/codes/patterns/">Quackitn</a>、<a href="https://www.casper.tw/css/2013/09/24/css-background/">卡斯伯 Blog</a>、<a href="https://medium.com/@lavanyaratnabala/css-patterns-870d65192f40">Medium</a>、<a href="https://css-tricks.com/background-patterns-simplified-by-conic-gradients/">CSS-Tricks</a>)
</p>
<p>分享個我比較喜歡樣式,也就是上面第一張圖的 CSS:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span>
<span class="nt">linear-gradient</span><span class="o">(</span><span class="err">135</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#ccc</span> <span class="err">25</span><span class="o">%,</span> <span class="nt">transparent</span> <span class="err">25</span><span class="o">%)</span> <span class="nt">-50px</span> <span class="err">0</span><span class="o">,</span>
<span class="nt">linear-gradient</span><span class="o">(</span><span class="err">225</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#eee</span> <span class="err">25</span><span class="o">%,</span> <span class="nt">transparent</span> <span class="err">25</span><span class="o">%)</span> <span class="nt">-50px</span> <span class="err">0</span><span class="o">,</span>
<span class="nt">linear-gradient</span><span class="o">(</span><span class="err">315</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#ccc</span> <span class="err">25</span><span class="o">%,</span> <span class="nt">transparent</span> <span class="err">25</span><span class="o">%),</span>
<span class="nt">linear-gradient</span><span class="o">(</span><span class="err">45</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#eee</span> <span class="err">25</span><span class="o">%,</span> <span class="nt">transparent</span> <span class="err">25</span><span class="o">%);</span>
<span class="nt">background-size</span><span class="o">:</span> <span class="err">100</span><span class="nt">px</span> <span class="err">100</span><span class="nt">px</span><span class="o">;</span>
</code></pre></div></div>
<h2 id="radial-gradient-徑向漸層">Radial Gradient 徑向漸層</h2>
<p>除了基礎的線性漸層,另一個常見的漸層方式徑向漸層。它與線性漸層一樣,會用設定的顏色填滿整個區域,但與之不同的是,徑向漸層是從單點為顏色起始點,並依圓形或橢圓的方式向外放射延伸。</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">shape</span> <span class="nt">size</span> <span class="nt">at</span> <span class="nt">position</span><span class="o">,</span> <span class="nt">color-stop1</span><span class="o">,</span> <span class="nt">color-stop2</span><span class="o">,</span> <span class="o">...);</span>
</code></pre></div></div>
<ol>
<li><strong>shape size at position</strong><br />
其實這邊有三個參數 shape、size 與 position。
<ul>
<li><strong>shape</strong> <br />
指的是向外放射延伸的形狀,<strong>預設是橢圓(ellipse)</strong>,另一個選項則是圓形(circle)。</li>
<li><strong>size</strong><br />
指的是橢圓/圓形的半徑,<strong>預設是以圓心到最遠角(farthest-corner)</strong> 為半徑進行漸變,其餘的值有最近邊(closest-side)、最近角(closest-corner)、最遠邊(farthest-side)。這邊一樣等等來做實驗。</li>
<li><strong>position</strong><br />
預設會將圓心設在中心點(center)。</li>
</ul>
</li>
<li><strong>color-stop</strong><br />
跟線性漸層一樣,有顏色與定位參數兩個參數,寫在前的顏色越接近圓心。</li>
</ol>
<h3 id="設置放射延伸形狀">設置放射延伸形狀</h3>
<p>剛剛提過延伸的形狀有兩種:<mark>橢圓(ellipse)與圓形(circle)兩種</mark>。這邊一樣柔紫色 <code class="language-plaintext highlighter-rouge">#c5d5fa</code> 為起始色、柔綠色 <code class="language-plaintext highlighter-rouge">#c3dc99</code> 為終點色,來觀察兩種形狀的漸層效果:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">ellipse</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/xvr87iW.png" alt="觀察圓形與橢圓的漸層效果" />
觀察圓形與橢圓的漸層效果
</p>
<p>一樣可以透過定位參數來調整它的渲染效果:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nf">#c5d5fa</span> <span class="err">25</span><span class="o">%,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nf">#c5d5fa</span> <span class="err">70</span><span class="o">%,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/orkcNCD.png" alt="不同定位參數的圓形漸層效果" />
不同定位參數的圓形漸層效果
</p>
<h3 id="設置放射中心">設置放射中心</h3>
<p>無論是橢圓還是圓形其放射中心,都是預設在區域中心。如果想更改放射中心位置有 3 種方式:</p>
<ol>
<li><strong>絕對位置</strong>:直接使用長度值來定義位置(如:10px)</li>
<li><strong>百分比值</strong>:使用百分比(例:10%)來定義位置</li>
<li><strong>關鍵字</strong>:使用 top、 bottom、 left、 right、 center 關鍵字來指明位置,也可將關鍵字使用,就是前面提到的 top left、 top right…等。</li>
</ol>
<p>對了關鍵字 <strong>at</strong> 別忘了:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">at</span> <span class="err">30</span><span class="nt">px</span> <span class="err">50</span><span class="nt">px</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">at</span> <span class="err">80</span><span class="o">%</span> <span class="err">20</span><span class="o">%,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">at</span> <span class="nt">left</span> <span class="nt">bottom</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/niOVS4G.png" alt="不同放射中心的圓形漸層效果" />
不同放射中心的圓形漸層效果
</p>
<h3 id="設置放射半徑">設置放射半徑</h3>
<p>若依照參數順序,這小節應該放在設置放射延伸形狀與設置放射中心的中間章節比較合適,不過因為效果圖必須搭配放射中心的調整來看會比較清楚,所以我把它移到這邊來看效果。</p>
<p>這邊總共有 4 個值可選最遠角(farthest-corner)、最近邊(closest-side)、最近角(closest-corner)、最遠邊(farthest-side):</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">closest-side</span> <span class="nt">at</span> <span class="nt">center</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="nd">:radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">farthest-side</span> <span class="nt">at</span> <span class="nt">center</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="nd">:radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">closest-corner</span> <span class="nt">at</span> <span class="nt">left</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="nd">:radial-gradient</span><span class="o">(</span><span class="nt">circle</span> <span class="nt">farthest-corner</span> <span class="nt">at</span> <span class="nt">left</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/zeLzbT8.png" alt="不同放射半徑的圓形漸層效果" />
不同放射半徑的圓形漸層效果
</p>
<h3 id="多種顏色漸層-1">多種顏色漸層</h3>
<p>至於多種顏色的設置跟線性漸層的一致,如果沒有設置定位參數,所有顏色會平均分配:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nt">red</span><span class="o">,</span> <span class="nt">orange</span><span class="o">,</span> <span class="nt">yellow</span><span class="o">,</span>
<span class="nt">green</span><span class="o">,</span> <span class="nt">blue</span><span class="o">,</span> <span class="nt">indigo</span><span class="o">,</span> <span class="nt">purple</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/GrePbTF.png" alt="徑向彩虹漸層" />
徑向彩虹漸層
</p>
<p>一樣透過設置定位參數,可以調整顏色的分配狀況,若直接將顏色重疊,會做出同心圓效果…雖然我的圖被截掉…不過應該還看得出是同心圓啦 XDDD</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span>
<span class="nt">red</span> <span class="err">0</span><span class="o">%,</span> <span class="nt">red</span> <span class="err">15</span><span class="o">%,</span>
<span class="nt">orange</span> <span class="err">15</span><span class="o">%,</span> <span class="nt">orange</span> <span class="err">30</span><span class="o">%,</span>
<span class="nt">yellow</span> <span class="err">30</span><span class="o">%,</span> <span class="nt">yellow</span> <span class="err">45</span><span class="o">%,</span>
<span class="nt">green</span> <span class="err">45</span><span class="o">%,</span> <span class="nt">green</span> <span class="err">60</span><span class="o">%,</span>
<span class="nt">blue</span> <span class="err">60</span><span class="o">%,</span> <span class="nt">blue</span> <span class="err">75</span><span class="o">%,</span>
<span class="nt">indigo</span> <span class="err">75</span><span class="o">%,</span> <span class="nt">indigo</span> <span class="err">90</span><span class="o">%,</span>
<span class="nt">purple</span> <span class="err">90</span><span class="o">%,</span> <span class="nt">purple</span> <span class="err">100</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/nPp3Pv2.png" alt="徑向彩虹漸層" />
彩虹同心圓
</p>
<h3 id="patterns-with-gradients-1">Patterns with Gradients</h3>
<p>每次看人家的範例,就覺得就覺得人家真超厲害,一個簡單的漸層都可以完出個花來:</p>
<p class="illustration">
<img src="https://i.imgur.com/asXOzkV.png" alt="圓圈背景" />
<img src="https://i.imgur.com/RmT8uXv.png" alt="圓圈背景" />
<img src="https://i.imgur.com/F5Lxk7c.png" alt="圓圈背景" />
<img src="https://i.imgur.com/xSpvlTa.png" alt="圓圈背景" />
圓圈背景(範例來源: <a href="https://www.quackit.com/css/codes/patterns/">Quackitn</a>、<a href="https://www.casper.tw/css/2013/09/24/css-background/">卡斯伯 Blog</a>、<a href="https://medium.com/@lavanyaratnabala/css-patterns-870d65192f40">Medium</a>)
</p>
<p><a href="https://www.oxxostudio.tw/articles/202008/css-gradient.html">oxxo</a> 還能畫出個球體:</p>
<p class="illustration">
<img src="https://i.imgur.com/nzHhNQy.png" alt="球體" />
球體(範例來源: <a href="https://www.oxxostudio.tw/articles/202008/css-gradient.html">OXXO.STUDIO</a>)
</p>
<p>橘黃色點點那個還滿漂亮的,留一下 CSS 好了:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background-color</span><span class="o">:</span> <span class="nt">orange</span><span class="o">;</span>
<span class="nt">background-image</span><span class="o">:</span>
<span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">gold</span> <span class="err">15</span><span class="o">%,</span> <span class="nt">transparent</span> <span class="err">15</span><span class="o">%),</span>
<span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">gold</span> <span class="err">40</span><span class="o">%,</span> <span class="nt">transparent</span> <span class="err">40</span><span class="o">%);</span>
<span class="nt">background-size</span><span class="o">:</span> <span class="err">60</span><span class="nt">px</span> <span class="err">60</span><span class="nt">px</span><span class="o">;</span>
<span class="nt">background-position</span><span class="o">:</span> <span class="err">0</span> <span class="err">0</span><span class="o">,</span> <span class="err">30</span><span class="nt">px</span> <span class="err">30</span><span class="nt">px</span><span class="o">;</span>
</code></pre></div></div>
<h2 id="conic-gradient-錐形漸層">Conic Gradient 錐形漸層</h2>
<p>錐形漸層語法有點類似徑向漸層,但漸變的方向確不太一樣。徑向漸層是由圓心向外漸變,而錐形漸層則是<mark>順時針繞圓心漸變</mark>:</p>
<p class="illustration">
<img src="https://i.imgur.com/FZQT7D8.png" alt="線性漸層、徑向漸層與錐形漸層的漸變差異" />
線性漸層、徑向漸層與錐形漸層的漸變差異(範例來源: <a href="https://followandrew.dev/css-conic-gradient-effects-tutorial/">FollowAndrew</a>)
</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background-image</span><span class="o">:</span>
<span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">from</span> <span class="nt">angle</span> <span class="nt">at</span> <span class="nt">position</span><span class="o">,</span> <span class="nt">color-stop1</span><span class="o">,</span> <span class="nt">color-stop2</span><span class="o">,</span> <span class="o">...);</span>
</code></pre></div></div>
<ol>
<li><strong>from angle</strong><br />
開始旋轉的角度,預設是從 <strong>0 度</strong> 開始順時鐘漸變。</li>
<li><strong>at position</strong><br />
錐形漸層的圓心,預設圓心是在圖片中心。</li>
</ol>
<p><br class="big" /></p>
<p>自己上點顏色來看看,</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/3zlx7Bq.png" alt="徑向漸層與錐形漸層的漸變差異" />
徑向漸層與錐形漸層的漸變差異
</p>
<h3 id="設置起始旋轉的角度">設置起始旋轉的角度</h3>
<p>這邊要特別注意的大概就是關鍵字 <mark>from</mark> 別丟了?</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">from</span> <span class="err">0</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">from</span> <span class="err">90</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">from</span> <span class="err">180</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">from</span> <span class="err">270</span><span class="nt">deg</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/fEyVauI.png" alt="不同起始旋轉的角度的錐形漸層效果" />
不同起始旋轉的角度的錐形漸層效果
</p>
<h3 id="設置錐形的圓心">設置錐形的圓心</h3>
<p>跟徑向漸層的放射中心設置方式一樣,可以透過設置<strong>絕對位置</strong>、<strong>百分比值</strong>與<strong>關鍵字</strong>的 3 種方式,一樣 <mark>at</mark> 別忘了:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">at</span> <span class="err">30</span><span class="nt">px</span> <span class="err">50</span><span class="nt">px</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">at</span> <span class="err">80</span><span class="o">%</span> <span class="err">20</span><span class="o">%,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">at</span> <span class="nt">left</span> <span class="nt">bottom</span><span class="o">,</span> <span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#fcdef4</span><span class="o">,</span> <span class="nf">#c3dc99</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/2tpVgCd.png" alt="不同圓心的錐形漸層效果" />
不同圓心的錐形漸層效果
</p>
<h3 id="多種顏色漸層-2">多種顏色漸層</h3>
<p>其實我前面已經用了三個顏色了,但…我想畫彩虹 :rainbow: 所以我畫了兩版,一版是漸層變色,一版是色塊版:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">width</span><span class="o">:</span><span class="err">200</span><span class="nt">px</span><span class="o">;</span>
<span class="nt">height</span><span class="o">:</span><span class="err">200</span><span class="nt">px</span><span class="o">;</span>
<span class="nt">border-radius</span><span class="o">:</span><span class="err">50</span><span class="o">%;</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">red</span><span class="o">,</span> <span class="nt">orange</span><span class="o">,</span> <span class="nt">yellow</span><span class="o">,</span> <span class="nt">green</span><span class="o">,</span> <span class="nt">blue</span><span class="o">,</span> <span class="nt">indigo</span><span class="o">,</span> <span class="nt">purple</span><span class="o">,</span> <span class="nt">red</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">conic-gradient</span><span class="o">(</span><span class="nt">red</span> <span class="err">0</span><span class="o">,</span> <span class="nt">red</span> <span class="err">25</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">orange</span> <span class="err">25</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">orange</span> <span class="err">75</span><span class="nt">deg</span> <span class="o">,</span>
<span class="nt">yellow</span> <span class="err">75</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">yellow</span> <span class="err">125</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">green</span> <span class="err">125</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">green</span> <span class="err">175</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">blue</span> <span class="err">175</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">blue</span> <span class="err">225</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">indigo</span> <span class="err">225</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">indigo</span> <span class="err">275</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">purple</span> <span class="err">275</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">purple</span> <span class="err">325</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">red</span> <span class="err">325</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">red</span> <span class="err">360</span><span class="nt">deg</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/uvHHuaL.png" alt="彩虹錐形漸變" />
彩虹錐形漸變
</p>
<h3 id="patterns-with-gradients-2">Patterns with Gradients</h3>
<p>錐形好像比較少見到各種花裡胡哨的圖案,到是比較常見各種圓餅圖:</p>
<p class="illustration">
<img src="https://i.imgur.com/gnT6EZJ.png" alt="圓餅圖" />
圓餅圖(範例來源: <a href="https://lenadesign.org/2021/05/15/css-conic-gradient/">Lena Design</a>)
</p>
<p>另一種比較常見的則是這種環形色票:</p>
<p class="illustration">
<img src="https://i.imgur.com/hXxMXQv.png" alt="色票" />
色票(範例來源: <a href="https://lenadesign.org/2021/05/15/css-conic-gradient/">Lena Design</a>)
</p>
<h2 id="repeating-linear-gradients-重複線性漸層">Repeating Linear Gradients 重複線性漸層</h2>
<p>前面的範例中,如果要做出重複的漸層效果,都會透過 <code class="language-plaintext highlighter-rouge">background-size</code> 與 <code class="language-plaintext highlighter-rouge">background-repeat</code> 來實現。但除此之外,還可以透過 <code class="language-plaintext highlighter-rouge">repeating-linear-gradient</code> 輕鬆實現重複效果:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:repeating-linear-gradient</span><span class="o">(</span><span class="nt">direction</span><span class="o">,</span> <span class="nt">color-stop1</span><span class="o">,</span> <span class="nt">color-stop2</span><span class="o">,</span> <span class="o">...);</span>
</code></pre></div></div>
<p>基本使用方法,與 <code class="language-plaintext highlighter-rouge">linear-gradient</code> 基本一致,但需要指定定位參數,瀏覽器會將需要重複的顏色放置在指定位置,其餘部份會自動計算補滿:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:repeating-linear-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span> <span class="err">15</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/O2uC0Yl.png" alt="repeating-linear-gradient 漸層效果" />
repeating-linear-gradient 漸層效果
</p>
<p>以這個例子為例,會將 <code class="language-plaintext highlighter-rouge">#c5d5fa</code> 與 <code class="language-plaintext highlighter-rouge">#c3dc99</code> 分別擺放在 0% 和 15%,其餘部份就會重複填滿了。不過我發現這個重複的交界處的過渡太過明顯,所以我在 30% 的地方加上起始色 <code class="language-plaintext highlighter-rouge">#c5d5fa</code>,讓它的產生平滑過渡的效果:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:repeating-linear-gradient</span><span class="o">(</span><span class="nf">#c5d5fa</span><span class="o">,</span> <span class="nf">#c3dc99</span> <span class="err">15</span><span class="o">%,</span> <span class="nf">#c5d5fa</span> <span class="err">30</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/Uka8hxp.png" alt="repeating-linear-gradient 漸層效果" />
repeating-linear-gradient 漸層效果
</p>
<p>若沒有指定定位參數,使用起來的效果基本跟 <code class="language-plaintext highlighter-rouge">linear-gradient</code> 相同:</p>
<p class="illustration">
<img src="https://i.imgur.com/ZTFefJ9.png" alt="不給定定位參數的 repeating-linear-gradient 漸層效果" />
不給定定位參數的 repeating-linear-gradient 漸層效果
</p>
<h3 id="patterns-with-gradients-3">Patterns with Gradients</h3>
<p>一樣找了些有趣花樣:</p>
<p class="illustration">
<img src="https://i.imgur.com/SdRNmr2.png" alt="網狀線" />
<img src="https://i.imgur.com/xEVPr40.png" alt="斑馬線" />
<img src="https://i.imgur.com/Duhv4Zt.png" alt="警戒線" />
網格與線狀(範例來源: <a href="https://www.quackit.com/css/codes/patterns/">Quackitn</a>、<a href="https://www.casper.tw/css/2013/09/24/css-background/">卡斯伯 Blog</a>、<a href="https://medium.com/@lavanyaratnabala/css-patterns-870d65192f40">Medium</a>)
</p>
<p>那個橘黑色的配色,有點像道路工程的警示色 XDDD</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-linear-gradient</span><span class="o">(</span><span class="err">45</span><span class="nt">deg</span><span class="o">,</span>
<span class="nt">black</span> <span class="err">0</span><span class="o">,</span> <span class="nt">black</span> <span class="err">5</span><span class="o">%,</span>
<span class="nt">orange</span> <span class="err">5</span><span class="o">%,</span> <span class="nt">orange</span> <span class="err">10</span><span class="o">%);</span>
</code></pre></div></div>
<h2 id="repeating-radial-gradient-重複徑向漸層">Repeating Radial Gradient 重複徑向漸層</h2>
<p>除線性漸層有重複效果外,徑向漸層也有:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="nd">:repeating-radial-gradient</span><span class="o">(</span><span class="nt">direction</span><span class="o">,</span> <span class="nt">color-stop1</span><span class="o">,</span> <span class="nt">color-stop2</span><span class="o">,</span> <span class="o">...);</span>
</code></pre></div></div>
<p>基本使用方法,與 radial-gradient 基本一致,但需要指定定位參數,剩下交由瀏覽器自動補滿:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-radial-gradient</span><span class="o">(</span><span class="nt">circle</span><span class="o">,</span> <span class="nf">#c5d5fa</span> <span class="err">5</span><span class="o">%,</span> <span class="nf">#c3dc99</span> <span class="err">10</span><span class="o">%);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/KHL7UqV.png" alt="重複徑向漸層" />
重複徑向漸層
</p>
<p>不曉得為啥,好像出現類似陰影的效果,我換個顏色感覺會更明顯:</p>
<p class="illustration">
<img src="https://i.imgur.com/Ukz4lEF.png" alt="重複徑向漸層有自帶陰影效果" />
重複徑向漸層有自帶陰影效果
</p>
<p>一樣若想讓過渡效果平滑點,可將起始色設為新的終點色,就是眼睛看起來有點花:</p>
<p class="illustration">
<img src="https://i.imgur.com/rbu1CdL.png" alt="平滑過渡效果的重複徑向漸層" />
平滑過渡效果的重複徑向漸層
</p>
<h3 id="patterns-with-gradients-4">Patterns with Gradients</h3>
<p>這個我就不找太多範例了,我眼睛好花阿 @@</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-radial-gradient</span><span class="o">(</span><span class="nt">closest-side</span> <span class="nt">at</span> <span class="err">25</span><span class="nt">px</span> <span class="err">35</span><span class="nt">px</span><span class="o">,</span> <span class="nt">orange</span> <span class="err">15</span><span class="o">%,</span> <span class="nt">gold</span> <span class="err">40</span><span class="o">%);</span>
<span class="nt">background-size</span><span class="o">:</span><span class="err">60</span><span class="nt">px</span> <span class="err">60</span><span class="nt">px</span><span class="o">;</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/iS4ABKV.png" alt="圈圈" />
圈圈(範例來源: <a href="https://www.quackit.com/css/codes/patterns/">Quackitn</a>)
</p>
<h2 id="repeating-conic-gradient-重複錐形漸層">Repeating Conic Gradient 重複錐形漸層</h2>
<p>如果用重複錐形漸層則可做出放射線的背景效果:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-conic-gradient</span><span class="o">(</span><span class="nt">from</span> <span class="nt">angle</span> <span class="nt">at</span> <span class="nt">position</span><span class="o">,</span> <span class="nt">color-stop1</span><span class="o">,</span> <span class="nt">color-stop2</span><span class="o">,</span> <span class="o">...);</span>
</code></pre></div></div>
<p>基本使用方法,與 conic-gradient 基本一致,一樣別忘了定位參數:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-conic-gradient</span><span class="o">(</span><span class="nt">red</span> <span class="err">0</span><span class="o">,</span> <span class="nt">yellow</span> <span class="err">15</span><span class="nt">deg</span><span class="o">);</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-conic-gradient</span><span class="o">(</span><span class="nt">red</span> <span class="err">0</span><span class="o">,</span> <span class="nt">red</span> <span class="err">15</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">yellow</span> <span class="err">15</span><span class="nt">deg</span><span class="o">,</span> <span class="nt">yellow</span> <span class="err">30</span><span class="nt">deg</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/v5mf4wi.png" alt="重複錐形漸層" />
重複錐形漸層
</p>
<h3 id="patterns-with-gradients-5">Patterns with Gradients</h3>
<p>如果能熟練是用漸層的話,可以試著疊加不同的漸層效果,例如漸淡融入的效果;也能取代之前一些像是棋盤實做,它的程式碼會更加的簡短。</p>
<p class="illustration">
<img src="https://i.imgur.com/j0H2hRP.png" alt="重複錐形漸層花樣" />
重複錐形漸層花樣(範例來源: <a href="https://www.oxxostudio.tw/articles/202008/css-gradient.html">OXXO.STUDIO</a>、<a href="https://css-tricks.com/background-patterns-simplified-by-conic-gradients/">CSS-Tricks</a>)
</p>
<p>最後記錄下棋盤的 CSS</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">border</span><span class="o">:</span><span class="err">7</span><span class="nt">px</span> <span class="nt">solid</span> <span class="err">#000</span><span class="o">;</span>
<span class="nt">background</span><span class="o">:</span> <span class="nt">repeating-conic-gradient</span><span class="o">(</span><span class="nt">black</span> <span class="err">0</span><span class="o">%</span> <span class="err">25</span><span class="o">%,</span> <span class="nt">white</span> <span class="err">0</span><span class="o">%</span> <span class="err">50</span><span class="o">%)</span> <span class="err">43</span><span class="o">%/</span><span class="err">45</span><span class="nt">px</span> <span class="err">50</span><span class="nt">px</span><span class="o">;</span>
</code></pre></div></div>
<h2 id="combine-background-image-with-gradient-overlay">Combine background image with gradient overlay</h2>
<p>原本我以為圖片也有漸層屬性可用,不過看來我多想了,它也是利用漸層效果疊加做出來的:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">background-image</span><span class="o">:</span>
<span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">bottom</span><span class="o">,</span> <span class="nt">rgba</span><span class="o">(</span><span class="err">195</span><span class="o">,</span> <span class="err">220</span><span class="o">,</span> <span class="err">153</span><span class="o">,</span> <span class="err">0</span><span class="o">.</span><span class="err">32</span><span class="o">),</span> <span class="nt">rgba</span><span class="o">(</span><span class="err">255</span><span class="o">,</span> <span class="err">255</span><span class="o">,</span> <span class="err">255</span><span class="o">,</span> <span class="err">0</span><span class="o">.</span><span class="err">73</span><span class="o">)</span> <span class="err">80</span><span class="o">%),</span>
<span class="nt">url</span><span class="o">(</span><span class="s2">'https://i.imgur.com/OzTiFeI.png'</span><span class="o">);</span>
</code></pre></div></div>
<p class="illustration">
<img src="https://i.imgur.com/b9FkiTd.png" alt="圖片漸層效果" />
圖片漸層效果(原圖片來源: <a href="https://699pic.com/tupian-401782443.html">摄图网</a>)
</p>
<h2 id="color-palettes-generator-and-color-gradient-tool">Color Palettes Generator and Color Gradient Tool</h2>
<p>這部份跟 CSS 比較沒關係,純粹就是給配色苦手用的參考資料。這些網站有現成的配色方案,能配出許多還不錯的視覺效果 XDDD</p>
<ol>
<li><a href="https://webgradients.com/">Fresh Background Gradients</a> <strong>推</strong> :star2:</li>
<li><a href="https://uigradients.com/">UI Gradients</a></li>
<li><a href="http://www.colorzilla.com/gradient-editor/">Colorzilla Gradients</a></li>
<li><a href="https://mycolor.space/">ColorSpace</a></li>
<li><a href="http://grabient.com">Grabient</a></li>
<li><a href="http://webkul.github.io/coolhue">coolhue</a></li>
<li><a href="https://gradients.cssgears.com/">CSS GEARS</a></li>
</ol>
<h2 id="參考資料">參考資料</h2>
<ol>
<li>卡斯伯 (2013-09-24)。<a href="https://www.casper.tw/css/2013/09/24/css-background/">CSS沒有極限 - CSS3的漸層</a>。檢自 卡斯伯 Blog (2022-12-22)。</li>
<li>oxxo (2022-08-30)。<a href="https://www.oxxostudio.tw/articles/202008/css-gradient.html">深入理解 CSS 漸層 ( CSS Gradient ) </a>。檢自 OXXO.STUDIO (2022-12-22)。</li>
<li>小艾 (2018-01-14)。<a href="https://ithelp.ithome.com.tw/articles/10197136">Day26:小事之 多重背景與漸層背景 CSS3 Gradients</a>。檢自 iT 邦幫忙 (2022-12-22)。</li>
<li>bdp (2017-12-07)。<a href="https://ithelp.ithome.com.tw/articles/10190867">CSS:background 雙色漸層</a>。檢自 iT 邦幫忙 (2022-12-22)。</li>
<li>逗點人 (2017-10-27)。<a href="https://blog.7netic.com/2017/10/27/三種絕美css-背景漸層語法產生器fresh-background-gradients)-適用firefox、ie/">三種絕美CSS 背景漸層語法產生器(FRESH BACKGROUND GRADIENTS)-適用FIREFOX、IE</a>。檢自 逗點人7netic設計插畫誌 (2022-12-22)。</li>
<li><a href="https://www.quackit.com/css/functions/css_linear-gradient_function.cfm">CSS linear-gradient() Function</a>。檢自 Quackit (2022-12-22)。</li>
<li><a href="https://www.w3schools.com/cssref/func_radial-gradient.php">CSS radial-gradient() function</a>。檢自 W3schools (2022-12-22)。</li>
<li>Lena Stanley (2021-05-15)。<a href="https://lenadesign.org/2021/05/15/css-conic-gradient/">CSS Conic-Gradient</a>。檢自 Lena Design (2022-12-22)。</li>
<li>Ana Tudor (May 28, 2020-05-28)。<a href="https://css-tricks.com/background-patterns-simplified-by-conic-gradients/">Background Patterns, Simplified by Conic Gradients</a>。檢自 CSS-Tricks (2022-12-26)。</li>
<li>(2021-06-16)。<a href="https://webdevetc.com/blog/how-to-add-a-gradient-overlay-to-a-background-image-using-just-css-and-html/">How to add a gradient overlay to a background image using just CSS and HTML</a>。檢自 Web dev etc (2022-12-26)。</li>
<li>沾醬油(2018-04-19)。<a href="https://spencer581026.pixnet.net/blog/post/402900452-色票-好用的9個漸層配色網站">色票-好用的9個漸層配色網站</a>。檢自 沾醬油的部落格|痞客邦 (2022-12-26)。</li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-15</summary>
<ul>
<li>2023-02-15 發布</li>
<li>2022-12-26 完稿</li>
<li>2022-12-22 起稿</li>
</ul>
</details>辛西亞.Cynthia我還以為我這篇已經寫完了,結果一切都是我幻想 XDDD 這主要是我當初用在用 Mark Tag 實作細螢光筆的技術,這邊稍微整理一下相關資料。【LeetCode】0000. 解題目錄2023-02-15T00:00:00+00:002023-02-15T00:00:00+00:00https://cynthiachuang.github.io/LeetCode-0000-Contents<p>最近開始刷 LeetCode,但雖然有些題目解了出來,但還是存在很多似懂非懂的模糊空間,因此想透過記錄的方式記下這些經驗與領略,並用自己的方式表達出來,以釐清這些模糊的空間。</p>
<p>是說才剛開始刷題,長路漫漫阿…
<!--more--></p>
<p><br class="big" /></p>
<table>
<thead>
<tr>
<th>NO.</th>
<th>題目</th>
<th>難度</th>
<th>備註</th>
</tr>
</thead>
<tbody>
<tr>
<td>0771.</td>
<td><a href="/LeetCode-0771-Jewels-and-Stones/">Jewels and Stones</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0226.</td>
<td><a href="/LeetCode-0226-Invert-Binary-Tree/">Invert Binary Tree</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0208.</td>
<td><a href="/problemset/2018/12/21/LeetCode-0208-Implement-Trie/">Implement Trie (Prefix Tree) </a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0189.</td>
<td><a href="/LeetCode-0189-Rotate-Array/">Rotate Array</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0175.</td>
<td><a href="/LeetCode-0175-Combine-Two-Tables/">Combine Two Tables</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0140.</td>
<td><a href="/LeetCode-0140-Word-Break-II/">Word Break II</a></td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0139.</td>
<td><a href="/LeetCode-0139-Word-Break/">Word Break</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0071.</td>
<td><a href="/LeetCode-0071-Simplify-Path/">Simplify Path</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0066.</td>
<td><a href="/LeetCode-0066-Plus-One/">Plus One</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0065.</td>
<td><a href="/LeetCode-0065-Valid-Number/">Valid Number</a></td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0044.</td>
<td><a href="/LeetCode-0044-Wildcard-Matching/">Wildcard Matching*</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0043.</td>
<td><a href="/LeetCode-0043-Multiply-Strings/">Multiply Strings</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0036.</td>
<td>Valid Sudoku</td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0035.</td>
<td>Search Insert Position</td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0034.</td>
<td>Find First and Last Position of Element in Sorted Array</td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0033.</td>
<td>Search in Rotated Sorted Array</td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0032.</td>
<td><a href="/LeetCode-0032-Longest-Valid-Parentheses/">Longest Valid Parentheses*</a></td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0031.</td>
<td><a href="/LeetCode-0031-Next-Permutation/">Next Permutation</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0030.</td>
<td>Substring with Concatenation of All Words</td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0029.</td>
<td>Divide Two Integers</td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0028.</td>
<td><a href="/LeetCode-0028-Find-the-Index-of-the-First-Occurrence-in-a-String">Find the Index of the First Occurrence in a String</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0027.</td>
<td><a href="/LeetCode-0027-Remove-Element">Remove Element</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0026.</td>
<td><a href="/LeetCode-0026-Remove-Duplicates-from-Sorted-Array">Remove Duplicates from Sorted Array</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0025.</td>
<td><a href="/LeetCode-0025-Reverse-Nodes-in-k-Group/">Reverse Nodes in k-Group</a></td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0024.</td>
<td><a href="/LeetCode-0024-Swap-Nodes-in-Pairs/">Swap Nodes in Pairs</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0023.</td>
<td><a href="/LeetCode-0023-Merge-k-Sorted-Lists">Merge k Sorted Lists</a></td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0022.</td>
<td><a href="/LeetCode-0022-Generate-Parentheses">Generate Parentheses</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0021.</td>
<td><a href="/LeetCode-0021-Merge-Two-Sorted-Lists/">Merge Two Sorted Lists</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0020.</td>
<td><a href="/LeetCode-0020-Valid-Parentheses/">Valid Parentheses</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0019.</td>
<td><a href="/LeetCode-0019-Remove-Nth-Node-From-End-of-List/">Remove Nth Node From End of List</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0018.</td>
<td><a href="/LeetCode-0018-4Sum/">4Sum*</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0017.</td>
<td><a href="/LeetCode-0017-Letter-Combinations-of-a-Phone-Number/">Letter Combinations of a Phone Number</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0016.</td>
<td><a href="/LeetCode-0016-3Sum-Closest/">3Sum Closest</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0015.</td>
<td><a href="/LeetCode-0015-3Sum/">3Sum</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0014.</td>
<td><a href="/LeetCode-0014-Longest-Common-Prefix/">Longest Common Prefix</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0013.</td>
<td><a href="/LeetCode-0013-Roman-to-Integer/">Roman to Integer</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0012.</td>
<td><a href="/LeetCode-0012-Integer-to-Roman/">Integer to Roman</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0011.</td>
<td><a href="/LeetCode-0011-Container-With-Most-Water/">Container With Most Water</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0010.</td>
<td><a href="/LeetCode-0010-Regular-Expression-Matching/">Regular Expression Matching</a></td>
<td>H</td>
<td> </td>
</tr>
<tr>
<td>0009.</td>
<td><a href="/LeetCode-0009-Palindrome-Number/">Palindrome Number</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0008.</td>
<td><a href="/LeetCode-0008-String-to-Integer-atoi/">String to Integer (atoi)</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0007.</td>
<td><a href="/LeetCode-0007-Reverse-Integer/">Reverse Integer</a></td>
<td>E</td>
<td> </td>
</tr>
<tr>
<td>0006.</td>
<td><a href="/LeetCode-0006-ZigZag-Conversion/">ZigZag Conversion</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0005.</td>
<td><a href="/LeetCode-0005-Longest-Palindromic-Substring/">Longest Palindromic Substring</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0003.</td>
<td><a href="/LeetCode-0003-Longest-Substring-Without-Repeating-Characters">Longest Substring Without Repeating Characters</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0002.</td>
<td><a href="/LeetCode-0002-Add-Two-Numbers/">Add Two Numbers</a></td>
<td>M</td>
<td> </td>
</tr>
<tr>
<td>0001.</td>
<td><a href="/LeetCode-0001-Two-Sum/">Two Sum</a></td>
<td>E</td>
<td> </td>
</tr>
</tbody>
</table>辛西亞.Cynthia最近開始刷 LeetCode,但雖然有些題目解了出來,但還是存在很多似懂非懂的模糊空間,因此想透過記錄的方式記下這些經驗與領略,並用自己的方式表達出來,以釐清這些模糊的空間。【LeetCode】0022.Generate Parentheses2023-02-15T00:00:00+00:002023-02-15T00:00:00+00:00https://cynthiachuang.github.io/LeetCode-0022-Generate-Parentheses<p>Given <code class="language-plaintext highlighter-rouge">n</code> pairs of parentheses, write a function to generate all combinations of well-formed parentheses.</p>
<!--more-->
<p><br /></p>
<p><strong>Example 1:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
</code></pre></div></div>
<p><br /></p>
<p><strong>Example 2:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: n = 1
Output: ["()"]
</code></pre></div></div>
<p><br /></p>
<p><strong>Constraints</strong>:</p>
<ul>
<li>1 <= n <= 8</li>
</ul>
<p><br /></p>
<p><strong>Related Topics:</strong> <code class="language-plaintext highlighter-rouge">String</code> <code class="language-plaintext highlighter-rouge">Dynamic Programming</code> <code class="language-plaintext highlighter-rouge">Backtracking</code></p>
<h2 id="解題邏輯與實作">解題邏輯與實作</h2>
<p>好久沒刷 LeetCode,所以先拿之前寫過,但還沒打成網誌的題目來練練手。</p>
<p>這題會給定一個數字 n,以生成包含 n 個括號的所有正確形式。若要列出所有結果,我第一個想到的會是用遞迴,幾個中止條件也在第一時間就浮出來了:</p>
<ol>
<li>
<p><strong>長度為 2n,且左括號(open parenthesis)個數等於右括號(close parenthesis)個數</strong><br />
這個條件應該是可以輸出正確形式的中止條件,所以這個終止時需要回傳正確的形式。但實作時,我不想多傳入常數 n(因為這個數在遞迴過程中不會變),所以我把條件給反過來使用倒數的方式:「當左括號剩餘個數為零,且右括號剩餘個數亦為零」時終止。</p>
</li>
<li>
<p><strong>右括號剩餘個數少於左括號剩餘個數</strong><br />
假設 n = 3,若目前字串為 <code class="language-plaintext highlighter-rouge">())</code> ,則左括號剩餘個數則為 2、右括號剩餘個數為 1,遇到這樣的非法形式就直接返回不處理了。</p>
</li>
</ol>
<p>如果兩種終止條件都不滿足就繼續往下走:</p>
<ol>
<li>若左括號個數仍有剩餘,即不為 0,則輸出一左括號後繼續向下遞迴。</li>
<li>若右括號剩餘個數大於左括號剩餘個數,則輸出一右括號後繼續向下遞迴。</li>
</ol>
<p>有這些基本條件後就能開始寫 code 了,完整程式碼如下:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">generateParenthesis</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">cur_str</span> <span class="o">=</span> <span class="s">""</span>
<span class="bp">self</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">cur_str</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">results</span><span class="p">)</span>
<span class="k">return</span> <span class="n">results</span>
<span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cur_str</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">open_remaining</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">close_remaining</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">results</span><span class="p">:</span><span class="nb">list</span><span class="p">):</span>
<span class="k">if</span> <span class="n">open_remaining</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">close_remaining</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">results</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">cur_str</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">close_remaining</span> <span class="o"><</span> <span class="n">open_remaining</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">open_remaining</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">cur_str</span> <span class="o">+</span> <span class="s">"("</span><span class="p">,</span> <span class="n">open_remaining</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">close_remaining</span><span class="p">,</span> <span class="n">results</span><span class="p">)</span>
<span class="k">if</span> <span class="n">close_remaining</span> <span class="o">></span> <span class="n">open_remaining</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">cur_str</span> <span class="o">+</span> <span class="s">")"</span><span class="p">,</span> <span class="n">open_remaining</span><span class="p">,</span> <span class="n">close_remaining</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">results</span><span class="p">)</span>
</code></pre></div></div>
<p>Runtime 大約 38 ms,約在 78.88% 左右,我再多加個終止條件試試應該可以加快速度:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">generateParenthesis</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">cur_str</span> <span class="o">=</span> <span class="s">""</span>
<span class="bp">self</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">cur_str</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">results</span><span class="p">)</span>
<span class="k">return</span> <span class="n">results</span>
<span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cur_str</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">open_remaining</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">close_remaining</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">results</span><span class="p">:</span><span class="nb">list</span><span class="p">):</span>
<span class="k">if</span> <span class="n">open_remaining</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">close_remaining</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">results</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">cur_str</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">open_remaining</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">close_remaining</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="n">cur_str</span> <span class="o">+=</span> <span class="s">')'</span><span class="o">*</span><span class="n">close_remaining</span>
<span class="n">results</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">cur_str</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">close_remaining</span> <span class="o"><</span> <span class="n">open_remaining</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">close_remaining</span> <span class="o"><</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">open_remaining</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">open_remaining</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">cur_str</span> <span class="o">+</span> <span class="s">"("</span><span class="p">,</span> <span class="n">open_remaining</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">close_remaining</span><span class="p">,</span> <span class="n">results</span><span class="p">)</span>
<span class="k">if</span> <span class="n">close_remaining</span> <span class="o">></span> <span class="n">open_remaining</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">cur_str</span> <span class="o">+</span> <span class="s">")"</span><span class="p">,</span> <span class="n">open_remaining</span><span class="p">,</span> <span class="n">close_remaining</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">results</span><span class="p">)</span>
</code></pre></div></div>
<p>Runtime 有稍微提升來到了 31 ms,約在 95.25% 左右。</p>
<h2 id="其他連結">其他連結</h2>
<ol>
<li><a href="/LeetCode-0000-Contents/">【LeetCode】0000. 解題目錄</a></li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-15</summary>
<ul>
<li>2023-02-15 發布</li>
<li>2023-01-09 完稿</li>
<li>2023-01-09 起稿</li>
</ul>
</details>辛西亞.CynthiaGiven n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.【LeetCode】0023. Merge k Sorted Lists2023-02-15T00:00:00+00:002023-02-15T00:00:00+00:00https://cynthiachuang.github.io/LeetCode-0023-Merge-k-Sorted-Lists<p>You are given an array of <code class="language-plaintext highlighter-rouge">k</code> linked-lists <code class="language-plaintext highlighter-rouge">lists</code>, each linked-list is sorted in ascending order.</p>
<p>Merge all the linked-lists into one sorted linked-list and return it.</p>
<!--more-->
<p><br /></p>
<p><strong>Example 1:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nput: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6
</code></pre></div></div>
<p><br /></p>
<p><strong>Example 2:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: lists = []
Output: []
</code></pre></div></div>
<p><br /></p>
<p><strong>Constraints</strong>:</p>
<ul>
<li>k == lists.length</li>
<li>0 <= k <= 104</li>
<li>0 <= lists[i].length <= 500</li>
<li>-104 <= lists[i][j] <= 104</li>
<li>lists[i] is sorted in ascending order.</li>
<li>The sum of lists[i].length will not exceed 104.</li>
</ul>
<p><br /></p>
<p><strong>Related Topics:</strong> <code class="language-plaintext highlighter-rouge">Linked List</code> <code class="language-plaintext highlighter-rouge">Divide and Conquer</code> <code class="language-plaintext highlighter-rouge">Heap (Priority Queue)</code> <code class="language-plaintext highlighter-rouge">Merge Sort</code></p>
<h2 id="解題邏輯與實作">解題邏輯與實作</h2>
<p>之前有解過寫過一篇 <a href="/LeetCode-0021-Merge-Two-Sorted-Lists">0021. Merge Two Sorted Lists</a>,這題就是它的進階。</p>
<p>我打算按照提示中的分而治之(Divide and Conquer)法與遞迴不斷將鏈結串列剖半對分,直到剩下一個或是兩個鏈結串列時,開始合併與回傳。</p>
<p>以長度為 6 的鏈結串列 [A, B, C, D, E, F] 為例,用遞迴去剖半對分的話最終會是 A、B 排,AB 排後再跟 C 排,再 D、E 排,DE 排後再跟 F 排,最後 ABC 與 DEF 再排一次,總共執行了 5 次。</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">mergeKLists</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lists</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]:</span>
<span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">merged</span><span class="p">(</span><span class="n">lists</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">lists</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">merged</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lists</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]],</span> <span class="n">start_idx</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">end_idx</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]:</span>
<span class="n">len_list</span> <span class="o">=</span> <span class="n">end_idx</span><span class="o">-</span><span class="n">start_idx</span><span class="p">;</span>
<span class="k">if</span> <span class="n">len_list</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="n">merged_result</span> <span class="o">=</span> <span class="n">lists</span><span class="p">[</span><span class="n">start_idx</span><span class="p">]</span>
<span class="k">if</span> <span class="n">len_list</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
<span class="n">k</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="n">len_list</span><span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
<span class="n">merged_result</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">mergeTwoLists</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">merged</span><span class="p">(</span><span class="n">lists</span><span class="p">,</span> <span class="n">start_idx</span><span class="p">,</span> <span class="n">start_idx</span><span class="o">+</span><span class="n">k</span><span class="p">),</span>
<span class="bp">self</span><span class="p">.</span><span class="n">merged</span><span class="p">(</span><span class="n">lists</span><span class="p">,</span> <span class="n">start_idx</span><span class="o">+</span><span class="n">k</span><span class="p">,</span> <span class="n">end_idx</span><span class="p">))</span>
<span class="k">return</span> <span class="n">merged_result</span>
<span class="k">def</span> <span class="nf">mergeTwoLists</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">list1</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">],</span> <span class="n">list2</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]:</span>
<span class="n">answer</span> <span class="o">=</span> <span class="n">ListNode</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">head</span> <span class="o">=</span> <span class="n">answer</span>
<span class="k">while</span> <span class="n">list1</span> <span class="ow">and</span> <span class="n">list2</span><span class="p">:</span>
<span class="k">if</span> <span class="n">list1</span><span class="p">.</span><span class="n">val</span> <span class="o"><</span> <span class="n">list2</span><span class="p">.</span><span class="n">val</span><span class="p">:</span>
<span class="n">answer</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">list1</span>
<span class="n">list1</span> <span class="o">=</span> <span class="n">list1</span><span class="p">.</span><span class="nb">next</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">answer</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">list2</span>
<span class="n">list2</span> <span class="o">=</span> <span class="n">list2</span><span class="p">.</span><span class="nb">next</span>
<span class="n">answer</span> <span class="o">=</span> <span class="n">answer</span><span class="p">.</span><span class="nb">next</span>
<span class="n">answer</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">list1</span> <span class="k">if</span> <span class="n">list1</span> <span class="k">else</span> <span class="n">list2</span>
<span class="k">return</span> <span class="n">head</span><span class="p">.</span><span class="nb">next</span>
</code></pre></div></div>
<p>Runtime 為 112 ms,Beats 是 78.87%。不過它效率比我想像中的還差。</p>
<p><br class="big" /></p>
<p>因此我打算換個寫法試試,一樣是分而治之法,不過把它改成直接兩兩抓來做排序,就是 A、B 排、C、D 排、C、D 排:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
</span><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">mergeKLists</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lists</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]:</span>
<span class="n">len_list</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">lists</span><span class="p">)</span>
<span class="k">if</span> <span class="n">len_list</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="n">resules</span> <span class="o">=</span> <span class="n">lists</span>
<span class="k">while</span> <span class="n">len_list</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
<span class="k">if</span> <span class="n">len_list</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">resules</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
<span class="n">len_list</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">new_resule</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">len_list</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span>
<span class="n">new_resule</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">mergeTwoLists</span><span class="p">(</span><span class="n">resules</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">resules</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]))</span>
<span class="n">resules</span> <span class="o">=</span> <span class="n">new_resule</span>
<span class="n">len_list</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">resules</span><span class="p">)</span>
<span class="k">return</span> <span class="n">resules</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">mergeTwoLists</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">list1</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">],</span> <span class="n">list2</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]:</span>
<span class="n">answer</span> <span class="o">=</span> <span class="n">ListNode</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">head</span> <span class="o">=</span> <span class="n">answer</span>
<span class="k">while</span> <span class="n">list1</span> <span class="ow">and</span> <span class="n">list2</span><span class="p">:</span>
<span class="k">if</span> <span class="n">list1</span><span class="p">.</span><span class="n">val</span> <span class="o"><</span> <span class="n">list2</span><span class="p">.</span><span class="n">val</span><span class="p">:</span>
<span class="n">answer</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">list1</span>
<span class="n">list1</span> <span class="o">=</span> <span class="n">list1</span><span class="p">.</span><span class="nb">next</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">answer</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">list2</span>
<span class="n">list2</span> <span class="o">=</span> <span class="n">list2</span><span class="p">.</span><span class="nb">next</span>
<span class="n">answer</span> <span class="o">=</span> <span class="n">answer</span><span class="p">.</span><span class="nb">next</span>
<span class="n">answer</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">list1</span> <span class="k">if</span> <span class="n">list1</span> <span class="k">else</span> <span class="n">list2</span>
<span class="k">return</span> <span class="n">head</span><span class="p">.</span><span class="nb">next</span>
</code></pre></div></div>
<p>效果出來出乎意料的不錯欸,Runtime 96 ms、Beats 95.64%。</p>
<p><br class="big" /></p>
<p>偷看了下 70ms 的答案,因為它直接用 sort 難怪效能不錯 XDDD</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">mergeKLists</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lists</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]])</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="n">ListNode</span><span class="p">]:</span>
<span class="n">head</span> <span class="o">=</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">ListNode</span><span class="p">()</span>
<span class="n">arr</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lists</span><span class="p">:</span>
<span class="k">while</span> <span class="n">l</span> <span class="o">!=</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">arr</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">val</span><span class="p">)</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">l</span><span class="p">.</span><span class="nb">next</span>
<span class="n">arr</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>
<span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">arr</span><span class="p">:</span>
<span class="n">temp</span><span class="p">.</span><span class="nb">next</span> <span class="o">=</span> <span class="n">ListNode</span><span class="p">()</span>
<span class="n">temp</span> <span class="o">=</span> <span class="n">temp</span><span class="p">.</span><span class="nb">next</span>
<span class="n">temp</span><span class="p">.</span><span class="n">val</span> <span class="o">=</span> <span class="n">a</span>
<span class="k">return</span> <span class="n">head</span><span class="p">.</span><span class="nb">next</span>
</code></pre></div></div>
<h2 id="其他連結">其他連結</h2>
<ol>
<li><a href="/LeetCode-0000-Contents/">【LeetCode】0000. 解題目錄</a></li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-15</summary>
<ul>
<li>2023-02-15 發布</li>
<li>2023-01-11 完稿</li>
<li>2023-01-11 起稿</li>
</ul>
</details>辛西亞.CynthiaYou are given an array of k linked-lists lists, each linked-list is sorted in ascending order.【LeetCode】0026. Remove Duplicates from Sorted Array2023-02-15T00:00:00+00:002023-02-15T00:00:00+00:00https://cynthiachuang.github.io/LeetCode-0026-Remove-Duplicates-from-Sorted-Array<p>Given an integer array nums sorted in <strong>non-decreasing order</strong>, remove the duplicates <a href="https://en.wikipedia.org/wiki/In-place_algorithm">in-place</a> such that each unique element appears only <strong>once</strong>. The <strong>relative order</strong> of the elements should be kept the <strong>same</strong>.</p>
<p>Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the <strong>first part</strong> of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.</p>
<p>Return k after placing the final result in the first k slots of nums.</p>
<p>Do <strong>not</strong> allocate extra space for another array. You must do this by <strong>modifying the input array</strong> <a href="https://en.wikipedia.org/wiki/In-place_algorithm">in-place</a> with O(1) extra memory.</p>
<!--more-->
<p><br /></p>
<p><strong>Custom Judge:</strong></p>
<p>The judge will test your solution with the following code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">int</span><span class="p">[]</span> <span class="n">nums</span> <span class="o">=</span> <span class="p">[...];</span> <span class="o">//</span> <span class="n">Input</span> <span class="n">array</span>
<span class="nb">int</span><span class="p">[]</span> <span class="n">expectedNums</span> <span class="o">=</span> <span class="p">[...];</span> <span class="o">//</span> <span class="n">The</span> <span class="n">expected</span> <span class="n">answer</span> <span class="k">with</span> <span class="n">correct</span> <span class="n">length</span>
<span class="nb">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">removeDuplicates</span><span class="p">(</span><span class="n">nums</span><span class="p">);</span> <span class="o">//</span> <span class="n">Calls</span> <span class="n">your</span> <span class="n">implementation</span>
<span class="k">assert</span> <span class="n">k</span> <span class="o">==</span> <span class="n">expectedNums</span><span class="p">.</span><span class="n">length</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="nb">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">k</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">assert</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">expectedNums</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If all assertions pass, then your solution will be <strong>accepted</strong>.</p>
<p><br /></p>
<p><strong>Example 1:</strong></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Input</span><span class="p">:</span> <span class="n">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
<span class="n">Output</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="n">_</span><span class="p">]</span>
<span class="n">Explanation</span><span class="p">:</span> <span class="n">Your</span> <span class="n">function</span> <span class="n">should</span> <span class="k">return</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="k">with</span> <span class="n">the</span> <span class="n">first</span> <span class="n">two</span> <span class="n">elements</span> <span class="n">of</span> <span class="n">nums</span> <span class="n">being</span> <span class="mi">1</span> <span class="ow">and</span> <span class="mi">2</span> <span class="n">respectively</span><span class="p">.</span>
<span class="n">It</span> <span class="n">does</span> <span class="ow">not</span> <span class="n">matter</span> <span class="n">what</span> <span class="n">you</span> <span class="n">leave</span> <span class="n">beyond</span> <span class="n">the</span> <span class="n">returned</span> <span class="n">k</span> <span class="p">(</span><span class="n">hence</span> <span class="n">they</span> <span class="n">are</span> <span class="n">underscores</span><span class="p">).</span>
</code></pre></div></div>
<p><br /></p>
<p><strong>Example 2:</strong></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Input</span><span class="p">:</span> <span class="n">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="n">Output</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="n">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="n">_</span><span class="p">,</span><span class="n">_</span><span class="p">,</span><span class="n">_</span><span class="p">,</span><span class="n">_</span><span class="p">,</span><span class="n">_</span><span class="p">]</span>
<span class="n">Explanation</span><span class="p">:</span> <span class="n">Your</span> <span class="n">function</span> <span class="n">should</span> <span class="k">return</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="k">with</span> <span class="n">the</span> <span class="n">first</span> <span class="n">five</span> <span class="n">elements</span> <span class="n">of</span> <span class="n">nums</span> <span class="n">being</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="ow">and</span> <span class="mi">4</span> <span class="n">respectively</span><span class="p">.</span>
<span class="n">It</span> <span class="n">does</span> <span class="ow">not</span> <span class="n">matter</span> <span class="n">what</span> <span class="n">you</span> <span class="n">leave</span> <span class="n">beyond</span> <span class="n">the</span> <span class="n">returned</span> <span class="n">k</span> <span class="p">(</span><span class="n">hence</span> <span class="n">they</span> <span class="n">are</span> <span class="n">underscores</span><span class="p">).</span>
</code></pre></div></div>
<p><br /></p>
<p><strong>Constraints:</strong></p>
<ul>
<li>1 <= <code class="language-plaintext highlighter-rouge">nums.length</code> <= 3 * 104</li>
<li>-100 <= <code class="language-plaintext highlighter-rouge">nums[i]</code> <= 100</li>
<li>nums is sorted in <strong>non-decreasing</strong> order.</li>
</ul>
<p><br /></p>
<p><strong>Related Topics:</strong> <code class="language-plaintext highlighter-rouge">Array</code> <code class="language-plaintext highlighter-rouge">Two Pointers</code></p>
<h2 id="解題邏輯與實作">解題邏輯與實作</h2>
<p>這題是要我們從 Array 中去除重複項。這邊我第一個想法是使用 for 迴圈,我另外用了兩個指標 <code class="language-plaintext highlighter-rouge">compar_idx</code> 與 <code class="language-plaintext highlighter-rouge">replace_idx</code> 以記錄目前的位置,並用 for 迴圈尋訪 Array 內容。一旦目前被尋訪到的值與比較值(<code class="language-plaintext highlighter-rouge">compar_idx</code> 所指的值)相同,就將該值指定給取代值(<code class="language-plaintext highlighter-rouge">replace_idx</code> 所指的值),完成後就將 <code class="language-plaintext highlighter-rouge">compar_idx</code> 移到目前位置且 <code class="language-plaintext highlighter-rouge">replace_idx</code> 也向前移動一步:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">removeDuplicates</span><span class="p">(</span><span class="n">nums</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">nums</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="n">length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span>
<span class="k">if</span> <span class="n">length</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="n">length</span>
<span class="n">compar_idx</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">replace_idx</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="n">length</span><span class="p">):</span>
<span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">compar_idx</span><span class="p">]</span> <span class="o">!=</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
<span class="n">nums</span><span class="p">[</span><span class="n">replace_idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">compar_idx</span> <span class="o">=</span> <span class="n">i</span>
<span class="n">replace_idx</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">replace_idx</span>
</code></pre></div></div>
<p>出來效果還行 <strong>83 ms,93.30%</strong>。</p>
<h2 id="其他連結">其他連結</h2>
<ol>
<li><a href="/LeetCode-0000-Contents/">【LeetCode】0000. 解題目錄</a></li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-15</summary>
<ul>
<li>2023-02-15 發布</li>
<li>2023-02-02 完稿</li>
<li>2023-02-02 起稿</li>
</ul>
</details>辛西亞.CynthiaGiven an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same.【LeetCode】0027. Remove Element2023-02-15T00:00:00+00:002023-02-15T00:00:00+00:00https://cynthiachuang.github.io/LeetCode-0027-Remove-Element<p>Given an integer array <code class="language-plaintext highlighter-rouge">nums</code> and an integer <code class="language-plaintext highlighter-rouge">val</code>, remove all occurrences of <code class="language-plaintext highlighter-rouge">val</code> in <code class="language-plaintext highlighter-rouge">nums</code> <a href="https://en.wikipedia.org/wiki/In-place_algorithm">in-place</a>. The relative order of the elements may be changed.</p>
<p>Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the <strong>first part</strong> of the array <code class="language-plaintext highlighter-rouge">nums</code>. More formally, if there are <code class="language-plaintext highlighter-rouge">k</code> elements after removing the duplicates, then the first <code class="language-plaintext highlighter-rouge">k</code> elements of <code class="language-plaintext highlighter-rouge">nums</code> should hold the final result. It does not matter what you leave beyond the first <code class="language-plaintext highlighter-rouge">k</code> elements.</p>
<p>Return <code class="language-plaintext highlighter-rouge">k</code> after placing the final result in the first <code class="language-plaintext highlighter-rouge">k</code> slots of <code class="language-plaintext highlighter-rouge">nums</code>.</p>
<p>Do not allocate extra space for another array. You must do this by <strong>modifying the input array</strong> <a href="https://en.wikipedia.org/wiki/In-place_algorithm">in-place</a> with O(1) extra memory.</p>
<!--more-->
<p><strong>Custom Judge:</strong></p>
<p>The judge will test your solution with the following code:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int[] nums = [...]; // Input array
int val = ...; // Value to remove
int[] expectedNums = [...]; // The expected answer with correct length.
// It is sorted with no values equaling val.
int k = removeElement(nums, val); // Calls your implementation
assert k == expectedNums.length;
sort(nums, 0, k); // Sort the first k elements of nums
for (int i = 0; i < actualLength; i++) {
assert nums[i] == expectedNums[i];
}
If all assertions pass, then your solution will be accepted.
</code></pre></div></div>
<p><strong>Example 1:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: nums = [3,2,2,3], val = 3
Output: 2, nums = [2,2,_,_]
Explanation: Your function should return k = 2, with the first two elements of nums being 2.
It does not matter what you leave beyond the returned k (hence they are underscores).
Example 2:
Input: nums = [0,1,2,2,3,0,4,2], val = 2
Output: 5, nums = [0,1,4,0,3,_,_,_]
Explanation: Your function should return k = 5, with the first five elements of nums containing 0, 0, 1, 3, and 4.
Note that the five elements can be returned in any order.
It does not matter what you leave beyond the returned k (hence they are underscores).
</code></pre></div></div>
<p><strong>Constraints:</strong></p>
<ul>
<li><code class="language-plaintext highlighter-rouge">0 <= nums.length <= 100</code></li>
<li><code class="language-plaintext highlighter-rouge">0 <= nums[i] <= 50</code></li>
<li><code class="language-plaintext highlighter-rouge">0 <= val <= 100</code></li>
</ul>
<p><strong>Related Topics:</strong> <code class="language-plaintext highlighter-rouge">Array</code> <code class="language-plaintext highlighter-rouge">Two Pointers</code></p>
<h2 id="解題邏輯與實作">解題邏輯與實作</h2>
<p>這題給我們一個未排序 List 並給定一個數字,要我們移除從 List 中移除該數字。因為題目要求是 in-place,所以必須將合法值全部移到前方後,回傳合法值的總個數。這題目跟感覺跟上一題 <a href="/LeetCode-0026-Remove-Duplicates-from-Sorted-Array">0026. Remove Duplicates from Sorted Array</a> 很相似,當然實做的目的並不相同,就只是操作很雷同。</p>
<p>因為涉及交換,所以我第一時間想到是左到右遍歷 List,並用一個指標指在最右邊,若遍歷到的值是要移除的值,則將該值與右邊指標的值交換,並將該指標向左移一位,直到兩指標指到同一位置為止。</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">removeElement</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
<span class="n">left</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">right</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">while</span><span class="p">(</span><span class="n">left</span> <span class="o"><=</span> <span class="n">right</span><span class="p">):</span>
<span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">==</span> <span class="n">val</span><span class="p">:</span>
<span class="n">tmp</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">right</span><span class="p">]</span>
<span class="n">nums</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">left</span><span class="p">]</span>
<span class="n">nums</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">=</span> <span class="n">tmp</span>
<span class="n">right</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">left</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">left</span>
</code></pre></div></div>
<h2 id="其他連結">其他連結</h2>
<ol>
<li><a href="/LeetCode-0000-Contents/">【LeetCode】0000. 解題目錄</a></li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-15</summary>
<ul>
<li>2023-02-15 發布</li>
<li>2023-02-03 完稿</li>
<li>2023-02-02 起稿</li>
</ul>
</details>辛西亞.CynthiaGiven an integer array nums and an integer val, remove all occurrences of val in nums in-place. The relative order of the elements may be changed.【LeetCode】0028. Find the Index of the First Occurrence in a String2023-02-15T00:00:00+00:002023-02-15T00:00:00+00:00https://cynthiachuang.github.io/LeetCode-0028-Find-the-Index-of-the-First-Occurrence-in-a-String<p>Given two strings <code class="language-plaintext highlighter-rouge">needle</code> and <code class="language-plaintext highlighter-rouge">haystack</code>, return the index of the first occurrence of <code class="language-plaintext highlighter-rouge">needle</code> in <code class="language-plaintext highlighter-rouge">haystack</code>, or <code class="language-plaintext highlighter-rouge">-1</code> if <code class="language-plaintext highlighter-rouge">needle</code> is not part of haystack.</p>
<!--more-->
<p><strong>Example 1:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: haystack = "sadbutsad", needle = "sad"
Output: 0
Explanation: "sad" occurs at index 0 and 6.
The first occurrence is at index 0, so we return 0.
</code></pre></div></div>
<p><br /></p>
<p><strong>Example 2:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: haystack = "leetcode", needle = "leeto"
Output: -1
Explanation: "leeto" did not occur in "leetcode", so we return -1.
</code></pre></div></div>
<p><br /></p>
<p><strong>Constraints:</strong></p>
<ul>
<li>1 <= haystack.length, needle.length <= 104</li>
<li>haystack and needle consist of only lowercase English characters.</li>
</ul>
<p><strong>Related Topics:</strong> <code class="language-plaintext highlighter-rouge">Two Pointers</code> <code class="language-plaintext highlighter-rouge">String</code> <code class="language-plaintext highlighter-rouge">String Matching</code></p>
<h2 id="解題邏輯與實作">解題邏輯與實作</h2>
<p>這題看來是要找子字串第一次出現的位置,若無,則回傳 <code class="language-plaintext highlighter-rouge">-1</code>。最直覺的方法就是從左到右一一比過去,若剩餘長度小於子字串長度就中止。對了,開始前可以加上兩判斷式,若 <code class="language-plaintext highlighter-rouge">haystack</code> 或 <code class="language-plaintext highlighter-rouge">needle</code> 長度為 0,直接回傳 -1;若 <code class="language-plaintext highlighter-rouge">needle</code> 長度大於 <code class="language-plaintext highlighter-rouge">haystack</code> 也直接回傳 -1。</p>
<p>先照這樣的想法去寫一版:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">strStr</span><span class="p">(</span><span class="n">haystack</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">needle</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
<span class="n">h_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">haystack</span><span class="p">)</span>
<span class="n">n_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">needle</span><span class="p">)</span>
<span class="k">if</span> <span class="n">h_len</span> <span class="o"><</span> <span class="n">n_len</span><span class="p">:</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">if</span> <span class="n">h_len</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">n_len</span> <span class="o">==</span><span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="n">end_idx</span> <span class="o">=</span> <span class="n">h_len</span><span class="o">-</span><span class="n">n_len</span><span class="o">+</span><span class="mi">1</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">end_idx</span><span class="p">):</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">n_len</span><span class="p">):</span>
<span class="k">if</span> <span class="n">haystack</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="n">needle</span><span class="p">[</span><span class="n">j</span><span class="p">]:</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">if</span> <span class="n">j</span> <span class="o">==</span> <span class="n">n_len</span><span class="o">-</span><span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="n">i</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</code></pre></div></div>
<p>是說,如果不是為了寫過程,我應該會直接呼叫 <a href="https://www.w3schools.com/python/ref_string_find.asp"><code class="language-plaintext highlighter-rouge">find</code></a> XDDD</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">strStr</span><span class="p">(</span><span class="n">haystack</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">needle</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
<span class="k">return</span> <span class="n">haystack</span><span class="p">.</span><span class="n">rfind</span><span class="p">(</span><span class="n">needle</span><span class="p">)</span>
</code></pre></div></div>
<p><br /></p>
<p>依稀記得除了暴力搜尋外,應該還有一個從後面比的演算法,到<a href="https://zh.wikipedia.org/zh-tw/字串搜尋演算法">維基百科</a>查了下,我印想中的應該是 <a href="https://zh.wikipedia.org/wiki/博耶-穆尔字符串搜索算法">Boyer-Moore字串搜尋演算法</a>。所以我根據 <a href="https://www.geeksforgeeks.org/boyer-moore-algorithm-for-pattern-searching/">〈Boyer Moore Algorithm for Pattern Searching〉</a>的教學實做了一次,但這份教學應該是 Boyer-Moore 演算法的精簡版本,因為它沒實做好字元位移規則的部份:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">badCharHeuristic</span><span class="p">(</span><span class="n">needle</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">NO_OF_CHARS</span> <span class="o">=</span> <span class="mi">256</span>
<span class="n">n_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">needle</span><span class="p">)</span>
<span class="n">badChar</span> <span class="o">=</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">NO_OF_CHARS</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n_len</span><span class="p">):</span>
<span class="n">badChar</span><span class="p">[</span><span class="nb">ord</span><span class="p">(</span><span class="n">needle</span><span class="p">[</span><span class="n">i</span><span class="p">])]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
<span class="k">return</span> <span class="n">badChar</span>
<span class="k">def</span> <span class="nf">strStr</span><span class="p">(</span><span class="n">haystack</span><span class="p">,</span> <span class="n">needle</span><span class="p">):</span>
<span class="n">h_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">haystack</span><span class="p">)</span>
<span class="n">n_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">needle</span><span class="p">)</span>
<span class="k">if</span> <span class="n">h_len</span> <span class="o"><</span> <span class="n">n_len</span><span class="p">:</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">if</span> <span class="n">h_len</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">n_len</span> <span class="o">==</span><span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="n">badChar</span> <span class="o">=</span> <span class="n">badCharHeuristic</span><span class="p">(</span><span class="n">needle</span><span class="p">)</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">end_idx</span> <span class="o">=</span> <span class="n">h_len</span><span class="o">-</span><span class="n">n_len</span>
<span class="k">while</span><span class="p">(</span><span class="n">i</span> <span class="o"><=</span> <span class="n">end_idx</span><span class="p">):</span>
<span class="n">j</span> <span class="o">=</span> <span class="n">n_len</span><span class="o">-</span><span class="mi">1</span>
<span class="k">while</span> <span class="n">j</span><span class="o">>=</span><span class="mi">0</span> <span class="ow">and</span> <span class="n">needle</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">==</span> <span class="n">haystack</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">]:</span>
<span class="n">j</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">j</span><span class="o"><</span><span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="n">i</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">i</span> <span class="o">+=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">j</span><span class="o">-</span><span class="n">badChar</span><span class="p">[</span><span class="nb">ord</span><span class="p">(</span><span class="n">haystack</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">])])</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</code></pre></div></div>
<h2 id="其他連結">其他連結</h2>
<ol>
<li><a href="/LeetCode-0000-Contents/">【LeetCode】0000. 解題目錄</a></li>
</ol>
<h2 id="更新紀錄">更新紀錄</h2>
<details class="update_stamp">
<summary>最後更新日期:2023-02-15</summary>
<ul>
<li>2023-02-15 發布</li>
<li>2023-02-03 完稿</li>
<li>2023-02-03 起稿</li>
</ul>
</details>辛西亞.CynthiaGiven two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.