[{"categories":["530 - 大型語言模型 (LLM)"],"content":"前言 寫完上一篇 【LLM #1】什麼是 LLM 後，\n我腦中其實冒出一個蠢問題：\n欸，Transformer 原本不是有 encoder 跟 decoder 嗎？\n那現在 LLM 都去哪了？為什麼大家講 GPT 都說它是「decoder-only」？\n那 encoder 是不是失業了？ 🫠\n查了一下發現這題其實滿多人搞不清楚的，\n於是就順手整理成筆記給自己 XD。\n（我還是菜，如果有地方寫錯歡迎糾正！）\nTransformer 的原始長相（2017 版） 先來個老派的回憶。2017 年那篇著名的「Attention is All You Need」，\n原始 Transformer 架構其實不是為了 chatbot 設計的，它是為了機器翻譯。\n所以它天然長成這樣：\n[輸入句子] → Encoder → (壓縮後的語意表示) → Decoder → [輸出句子] 「I love cats」 → Encoder → [...向量...] → Decoder → 「我愛貓」 Encoder：把輸入句吃進去、理解它、壓成一堆向量 Decoder：參考 encoder 給的向量，一個字一個字生出來 用白話比喻：\nEncoder 像是「閱讀理解」的人：讀完整段，抓到意思 Decoder 像是「寫作」的人：參考理解好的結果，一邊寫一邊看自己已經寫了什麼 Encoder 跟 Decoder 的關鍵差異 兩邊都用 attention 機制，但注意力的「看法」不同：\n特性 Encoder Decoder 注意力方向 雙向（可看前後所有字） 單向（只能看已生成的前文） 典型任務 理解、分類、embedding 生成、續寫 訓練目標 填空題（mask 掉某個字讓它猜） 接龍（預測下一個字） 輸出結果 一堆向量（高維語意表示） 一連串 token（看得懂的文字） Decoder 只能看「過去」這件事非常重要，\n因為它要模擬「一邊寫一邊決定下一個字」的過程，\n不能作弊偷看答案（未來的字），才不會在真的 inference 時失靈。\n後來 Transformer 被拆成三個家族 慢慢地，大家發現「不一定要 encoder + decoder 一起用」，\n不同任務適合不同變形：\n家族 代表模型 擅長的事 Encoder-only BERT 理解類任務：分類、情感分析、命名實體辨識 (NER；從句子抓出人名、地名、公司名)、語意搜尋 Decoder-only GPT 系列、Claude、Llama、Qwen、Gemini 生成類任務：寫作、對話、程式碼 Encoder-Decoder T5、BART、原始 Transformer 明確的「輸入→輸出」轉換：翻譯、摘要 順帶一提（知道會更好，這篇不展開）：\nBERT 後來有 RoBERTa、DeBERTa 等變形，訓練策略微調後效果通常更好 T5 / BART 則是 encoder-decoder 在 LLM 時代的代表作，聊三大家族時常被一起提 Encoder-only：閱讀理解專家 BERT 就是代表，2018 出來那時超有名。\n這類模型不會自己生東西，你丟一段文字進去，它還你一組向量，\n你再接一個小小的 classifier 去做分類。\n適合：句子分類、情感分析、命名實體辨識 (NER)、語意搜尋 不適合：要生成一段話（要逼它做會很痛苦） Decoder-only：一直接龍的模型 這是 GPT、Claude、Llama 的家族。\n它的訓練方式很「暴力美學」：看到 N 個字，猜第 N+1 個字。\n就這樣用全世界的文字練上幾千億次後，它就學會了語言。\n適合：幾乎所有語言任務（只要會轉成「給我接下去寫」的形式） 不適合：（好像沒什麼不適合的？這就是它贏的原因 XD） Encoder-Decoder：原汁原味派 T5、BART 這類。明確區分「輸入」和「輸出」。\n適合：有明確 source → target 的任務，例如翻譯、摘要、文法糾正 不適合：開放式對話（因為它習慣「轉換」而不是「延續」） 為什麼現在主流 LLM 都是 decoder-only？ 這是我一開始最想不通的點。既然 encoder 這麼擅長理解，\n為什麼不用 encoder-decoder 來做 LLM？答案大概有三層：\n1. 一把刀切所有東西：prompt 就能扮演 encoder Decoder-only 的 prompt 前半段，其實就在做 encoder 的工作。\n舉個例子：\n請把下面這段英文翻成中文： I love cats. → 模型在讀「請把下面這段英文翻成中文」+「I love cats」的時候，\n就等於在做理解（等同 encoder 的工作）；\n讀完後開始輸出「我愛貓」，這才是生成（decoder 的工作）。\n換句話說，decoder-only 是把 encoder 跟 decoder 合併成同一個東西。\n一個模型就能做「理解 + 生成」，不用養兩套。\n2. Scaling 友善：參數集中在一邊 Encoder-decoder 要維持兩邊的連結（cross-attention），\n架構比較複雜，也不太好擴展到超大規模。\nDecoder-only 架構單純：一個 stack 堆到底，\nscaling law 的實驗也更好做，這在「大就是好」的 LLM 時代是硬優勢。\n3. In-context learning 是意外大禮 OpenAI 在 GPT-3 發現一件超威的事：\n只要在 prompt 裡塞幾個範例，\ndecoder-only 模型就能「臨時學會」新任務，\n不用重新訓練 — 這就是 few-shot / in-context learning。\n這個能力基本上只有 decoder-only 做得到，\n而這個能力又徹底改變了 LLM 的使用方式（prompt engineering 就是從這時開始熱門的）。\n那 encoder 是不是就失業了？ 並沒有 XD。Encoder 在 2026 年依然活得好好的，只是分工變了：\nSearch / RAG 系統裡的 embedding：用 encoder 把文件變向量做語意搜尋，這塊 encoder 還是主力（生產環境常見的 embedding 模型如 OpenAI 的 「text-embedding-3-large」、開源的 「bge-m3」，底層都是 encoder）。 輕量的分類任務：BERT 家族在生產環境依然很常見，因為便宜、快、夠用。 專門的翻譯 / 摘要系統：encoder-decoder 架構（T5、BART）依然有優勢。 只是在「通用對話 AI」這個最吸睛的戰場，decoder-only 勝出了。\n就像內燃機跟電動車 — 不是內燃機不會動，是電動車在主流乘用市場贏了。\n小結 整理完這題，我自己的一點小心得：\nEncoder 跟 decoder 是「工具差別」不是「好壞差別」 Decoder-only 贏了是因為它「夠泛用」，不是因為它「最強」 這讓我想起寫程式時的老話：\n能用少的組件解決的問題，就不要硬加架構。\nDecoder-only 用一個 stack 解決「理解 + 生成」兩件事，\n架構簡單、scaling 容易、prompt 就能切換任務 —\n在「什麼都要做」的大語言模型時代，這組特性真的太加分。\nReference Attention Is All You Need (Vaswani et al., 2017) The Illustrated Transformer — Jay Alammar BERT: Pre-training of Deep Bidirectional Transformers (Devlin et al., 2018) Language Models are Few-Shot Learners (GPT-3 paper, Brown et al., 2020) Hugging Face - Transformer models families ","date":"2026-04-22T14:00:00+08:00","permalink":"https://wongwongnotes.com/posts/ai/large-language-models/llm-encoder-decoder/","tags":["LLM","AI","Transformer","入門筆記"],"title":"【LLM #2】LLM 跟 Encoder / Decoder 的關係 — 為什麼主流 LLM 都是 decoder-only？"},{"categories":["530 - 大型語言模型 (LLM)"],"content":"前言 最近 AI 工具真的滿山滿谷，\nChatGPT、Claude、Gemini\u0026hellip; 感覺不用一下就會被時代丟在後面 🫠\n說實在自從 2023 年後，我自己也是大量在用這些工具，\n但我發現我好像都「會用」但沒有真的「理解」這些東西是什麼，\n於是就想來寫一篇給自己看的筆記 XD。\n這篇文章不會深入到數學公式或 Transformer 架構，\n純粹是從「使用者」角度，把我自己常被問的幾個關鍵字整理一下。\n如果你也是剛開始接觸 LLM 的朋友，希望對你有幫助！\n什麼是 LLM？ LLM = Large Language Model，中文翻作大型語言模型。\n一句話版本：\n一個「吃了超多文字」之後，學會「接下一個字」的機率模型。 然後這個「超多文字」真的是超多，\n通常是整個網路上能抓到的大部分公開文本 (書、論文、維基、Reddit、GitHub\u0026hellip; 什麼都有)，\n模型參數量動輒幾十億 (B) 到幾千億 (T)。\n所以它不是真的「理解」什麼事情，\n而是「統計上」知道下一個字最有可能是什麼 — 只是這個統計好到讓我們覺得它在思考 XD\n常見混淆：LLM ≠ encoder-decoder 架構 這邊順便釐清一個很多人會搞混（包括以前的我）的地方。\n有人問「LLM 是什麼？」時，反射動作常常會從架構角度答：\n「它是 Transformer，有 encoder 跟 decoder\u0026hellip;」\n這個回答其實不太精準。\nLLM 最核心的定義是它「做什麼」，不是「怎麼做」。 最乾淨的版本就是上面那句：「能一直接下一個字的大模型」。 現代主流 LLM（GPT、Claude、Llama 家族）架構上其實都是 decoder-only，沒有 encoder。\n「encoder-decoder」的印象來自 2017 年原始的 Transformer 論文（那篇是為機器翻譯設計的），\n但 LLM 後來演化成另一種樣子了。\n架構是實作細節，定義是它做什麼。\n講定義時先給「生下一個字」，架構的部分可以之後再補。\n這題其實滿值得展開，我寫在了系列第二篇：【LLM #2】LLM 跟 Encoder / Decoder 的關係。\nLLM 能做什麼 老實說目前大家也還在摸索邊界，但以我日常用到的來看：\n寫程式：寫 code、debug、解釋別人的 code、不同程式語言互轉 整理資訊：摘要長文、抓重點、翻譯、改寫 文字創作：寫文章初稿、email、slogan、社群貼文 閱讀助手：丟一份論文/文件問它裡面的內容 Agent 類應用：串工具 (檔案系統、瀏覽器、API) 幫你跑任務 對我個人影響最大的其實不是「產出」，\n而是「能隨時問一個不會不耐煩的對象」這件事 XDD\n幾個一定會遇到的關鍵字 Token LLM 不是用「字」或「word」在算，而是用 Token (子詞)。\n英文大約 1 個 token ≈ 0.75 個單字 中文常常 1 個字 ≈ 1~2 個 token (看 tokenizer) 為什麼要知道這個？\n因為 LLM 的計費、輸入長度限制，通通是按 token 算。 Context Window 又叫做「上下文視窗」，指的是模型一次最多可以「看進去」多少 token。\n常見的幾個數字：\n早期 GPT-3.5：4K tokens (差不多幾千字) 現在主流：128K ~ 200K tokens 部分旗艦：1M tokens (可以丟整本小說進去) 超過 context window 的內容，模型就「看不到」了，\n這也是為什麼長對話久了會覺得它開始「失憶」的原因。\nPrompt 就是你丟給模型的輸入。\n聽起來很簡單，但其實「怎麼寫 prompt」本身已經演化成一門學問，\n大家叫它 Prompt Engineering。\n簡單的心法：\n把「角色」、「任務」、「限制條件」、「輸出格式」講清楚 給 1~2 個範例 (few-shot) 通常會比空口白話好很多 Hallucination (幻覺) 這是 LLM 最有名的毛病：\n它會「一本正經地胡說八道」， 語氣非常自信，但內容是錯的、甚至是它自己編的。 所以重要的事情還是要驗證，尤其是：\n具體的人名、書名、論文、API 規格 法律、醫療、財務類建議 冷門領域的事實 用 LLM 的一個心態我覺得滿重要：\n把它當成「很強但有點唬爛的實習生」，\n能大幅提升效率，但不能全然信任。\n常見的幾家 LLM 快速流水帳版本 (2026 年初視角)：\n家族 公司 特色 GPT 系列 OpenAI 最有名，生態最大 Claude 系列 Anthropic 長文處理、寫作品質我自己最愛 Gemini 系列 Google 多模態強，整合 Google 產品 Llama 系列 Meta 開源大宗，能自己部署 Qwen / DeepSeek 阿里 / DeepSeek 中文開源模型代表 Mistral Mistral AI 歐洲開源，小而美 我的使用習慣是 Claude 寫東西、GPT 問一些快答案、\n本地小任務跑 Qwen 之類的開源模型，\n看你的需求與預算來選就好！\n小結：以「做筆記的站長」視角看 LLM 這個網站的初衷是「幫我自己整理筆記」，\nAI 崛起後我一度懷疑這件事還有沒有意義 (畢竟什麼都可以問 LLM 了 🫠)，\n但用了這麼多它之後，我反而覺得：\nLLM 給的是「即時但可能錯」的答案 自己的筆記是「慢慢累積但被自己驗證過」的答案 兩者並不衝突，甚至互補 —\n我現在常常把 LLM 當成「第一層草稿機」，\n然後再自己整理成能放上網站的筆記，效率高很多。\n佛系經營 + AI 輔助，似乎是個滿舒服的組合 🌿\nReference Wikipedia - Large language model Anthropic - What is a prompt? OpenAI - Tokenizer Hugging Face - Open LLM Leaderboard ","date":"2026-04-22T10:00:00+08:00","permalink":"https://wongwongnotes.com/posts/ai/large-language-models/llm-introduction/","tags":["LLM","AI","入門筆記"],"title":"【LLM #1】什麼是 LLM (Large Language Model)？給自己的一點入門筆記"},{"categories":["922 - Mac / MacOS"],"content":"前言 這篇是前文的進階版，前文是使用替換鍵來解消失的 touchbar，\n這篇用組合鍵來解\nhttps://wongwongnotes.com/posts/os-misc/os/macos/mac-karabiner-elements/\nKarabiner-Elements step 1. 下載 Karabiner-Elements 這邊我們就去下載鍵盤映射工具，連結在底下\nKarabiner-Elements step 2. 在隱私權與安全性中，把權限開啟 這邊我們把權限開啟，這樣才能去改系統的鍵盤映射。\nstep 3. 更改鍵盤設定 (組合鍵版，替換鍵請參考前言連結) 這裡是新方法，在 Complex modifications 新增以下內容，\n代表將 「cmd + `」 取代 「esc」 的作用。\n{ \u0026#34;description\u0026#34;: \u0026#34;cmd + ` -\u0026gt; esc\u0026#34;, \u0026#34;manipulators\u0026#34;: [ { \u0026#34;from\u0026#34;: { \u0026#34;key_code\u0026#34;: \u0026#34;grave_accent_and_tilde\u0026#34;, \u0026#34;modifiers\u0026#34;: { \u0026#34;mandatory\u0026#34;: [\u0026#34;command\u0026#34;], \u0026#34;optional\u0026#34;: [\u0026#34;any\u0026#34;] } }, \u0026#34;to\u0026#34;: [ { \u0026#34;key_code\u0026#34;: \u0026#34;escape\u0026#34;, \u0026#34;modifiers\u0026#34;: [] } ], \u0026#34;type\u0026#34;: \u0026#34;basic\u0026#34; } ] } 更多的範本可以參考其他網友的作品：https://ke-complex-modifications.pqrs.org/ Reference Karabiner-Elements Mac 神器系列 Karabiner-Elements ","date":"2024-10-09T09:59:36+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/%E6%88%AA%E5%9C%96-2023-05-27-%E4%B8%8B%E5%8D%8811.25.23.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/macbook-combination-key/","tags":["macOS"],"title":"【Mac】macbook touchbar 壞了沒 ESC 用怎麼辦? 用 Karabiner-Elements 新增 macbook 組合鍵！(附 script)"},{"categories":["936 – 原子閱讀計畫"],"content":"原子閱讀計畫週回顧 EP1 【習慣養成】\n要怎麼養成一個好習慣？ - #原子閱讀計畫EP1\n【學習力】\n如何讓自己的記憶力更長久？ - #原子閱讀計畫EP2\n如何讓自己的記憶力更長久？人工智慧如何讓我感覺更不智慧？ - #原子閱讀計畫EP3\n閒聊 依原子習慣開始執行的第一週，不給自己過多壓力，假日就好好休息，\n下週有想寫的一些書，不知道有沒有人會想點餐(然而我幾乎都看差不多了XD)\n📖《原子習慣》：養成習慣的經典之作，總共有4好習慣養成+4壞習慣除去，總之我還沒寫完XD\n📖《致富心態》：這本老實說我覺得不能被歸類在理財類，他更應該是心理面的書\n📖《最高學以致用法》：我現在很需要XD，我覺得我學習碰到了撞牆期\n📖《一週工作4小時》：欸\u0026hellip; 這本我看了覺得很微妙，心態可學，但實作面我覺得有距離\n📖《我可能錯了：森林智者的最後一堂人生課》：(想澄清一下，我不是只看很生產力取向的書好嗎🤣🤣)\n這本書真的好好看，自我覺察、反省、消除執念，我的過去與生活造成了現在的我，而也只有我有意識才能改變認知。\n","date":"2024-05-19T02:09:42+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/atomic-reading/atomic-reading-weekly-1/","tags":["原子閱讀計畫"],"title":"【原子閱讀計畫】#原子閱讀計畫週回顧\u0026閒聊 EP1"},{"categories":["936 – 原子閱讀計畫"],"content":"如何讓自己的記憶力更長久？人工智慧如何讓我感覺更不智慧？ - #原子閱讀計畫EP3 📍對記憶最有幫助的就是「失敗、錯誤」，一次慘痛的失敗經驗你可能一輩子都忘不掉。\n📍AI流行後，有一段時間的確他幫我很快的解決很多問題，帶來很多方便。\n📍但直到有次幫人解題，我發現求助AI成為我第一個想法，我驚覺不對。\n這就像面試時，對面試官說「讓我google、讓我問chatGPT，我一定能回答你這題的答案」，\n是面試的一種回答，但明顯不是好答案。\n📍當伸手變得容易，記憶也容易忘記。我認為什麼時機該使用工具非常重要，不然很可能會自廢武功。\n📍新的原則：進行專業工作，專業題目不該優先求助AI。但如果只是我興趣的攝影修圖剪片，那就盡量問，真的方便。\n📍整天都先問AI，我把多少的學習機會都給了AI？或許這才是該思考害怕被AI搶飯碗的理由。\n我也想學好英文，整天都問AI，很快得到答案了，現在回想好像什麼單字也都忘了，幸好發現的夠早。\n參考資料 出自《最高學以致用法》第一章，與很多我自己的感悟與觀點\n我也還在思考，怎麼樣好好「正確的」使用AI這個工具\n","date":"2024-05-17T02:07:45+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/atomic-reading/atomic-reading-ep3/","tags":["原子閱讀計畫"],"title":"【原子閱讀計畫】如何讓自己的記憶力更長久？人工智慧如何讓我感覺更不智慧？ - #原子閱讀計畫EP3"},{"categories":["936 – 原子閱讀計畫"],"content":"如何讓自己的記憶力更長久？ - #原子閱讀計畫EP2 📍人的記憶具有反芻的特性，「一次花很多時間」不如「分很多次反覆」讀同一個概念\n📍常聽到的「聽說讀寫」，「聽、讀」是輸入，「說、寫，還有行動」是輸出\n📍「輸出」本身是一種運動，講出來、寫下來、甚至動用全身，「身體」會幫助你進行記憶。\n📍「反覆+輸出」是培養長期記憶力的關鍵，輸出以「行動」最能印象深刻，就像騎腳踏車這件事通常不容易隨時間忘記\n📍最好的輸入:輸出=3:7，一直很努力多聽多看，卻沒有輸出，有可能只是從眼睛耳朵路過大腦，隔天忘的差不多，不信可以隔天嘗試描述看看昨天的內容。\n參考資料 出自《最高學以致用法》第一章，這本書我挺喜歡的。\n","date":"2024-05-16T02:06:46+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/atomic-reading/atomic-reading-ep2/","tags":["原子閱讀計畫"],"title":"【原子閱讀計畫】如何讓自己的記憶力更長久？ - #原子閱讀計畫EP2"},{"categories":["936 – 原子閱讀計畫"],"content":"要怎麼養成一個好習慣？ - #原子閱讀計畫EP1 📍作為新系列的第一篇，根據《原子習慣》1，「先至少跨出第一步，哪怕再小步」，是養成好習慣的關鍵。也是這系列的成因\n📍我要來處理已經囤積已久很多的書跟線上課程XDD\n📍根據《最高學以致用法》2，花時間看書, 文章, 影片如果不輸出，就很容易忘記。因此才會有這個把每天自己閱讀的一點東西做成簡易心得的計畫\n📍原子：很小的單元，也能表示零碎的時間、一個小章節、短的知識心得，對應我忙碌生活也能負擔的產出\n參考資料 *1：出自《原子習慣》養成好習慣的法則三，另外《先了解自己的性格，再來談培養習慣！4型性格習慣養成術》第五章：也有提到 baby step 的概念，與此法則概念雷同\n*2：出自《最高學以致用法》第一章，另外《化輸入為輸出》線上課程中，也有提到同樣概念\n","date":"2024-05-16T02:03:59+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/atomic-reading/atomic-reading-ep1/","tags":["原子閱讀計畫"],"title":"【原子閱讀計畫】要怎麼養成一個好習慣？ - #原子閱讀計畫EP1"},{"categories":["942 – 楓之谷M"],"content":"前言 總之就是最近我玩 楓之谷M 自動掛機都沒辦法順利放自動戰鬥後離線，\n重新連線有時候會顯示\n0x10010006\n已從其他地方登入\n無法連接伺服器\n然後登入後就會發現沒有在自動戰鬥的狀態，研究了一下發現是電信網路問題，\n不是遊戲問題 (差點錯怪他了?)\n解決方法 紀錄一下，參考巴友的留言，\n這個要找自己的電信商重置網路，以我來說就是中華電信\n撥打 800 後，等待客服回復，跟他說要重置網路，\n他會請你把手機關機後，等五分鐘再開，\n然後問題的確就順利解決了，\n這邊特別寫一篇文章出來，希望能幫助到有需要的人!\n(不然原先的解法藏在某一篇的留言底下\u0026hellip; 這該怎麼找XDD)\n參考資料 【問題】請問有人也有連線不穩的問題嗎? ","date":"2024-05-05T23:20:26+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/games/m/maplestorym-0x10010006/","tags":["Minecraft","楓之谷M"],"title":"【楓之谷 M】問題解決 - 已從其他地方登入 無法連接伺服器 0x10010006"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 Facebook 的 電子書閱讀器討論區 由於自己電子書很多, 一直都很想要有一個閱讀器來方便閱讀,\n以前都是用 ipad 但眼睛看久了還是會酸,\n於是之前嘗試購入閱讀器能改善這個現象。\n因此當時我有了人生第一台電子閱讀器\nhttps://wongwongnotes.com/posts/life-and-work/reviews/reviews/boox-page-7/\n然後，自從加入的 Facebook 的電子書閱讀器討論區，\n裡面有好多的推坑XDDD，看到裡面書友的建議，\nkobo 購買電子書能透過各種優惠活動疊加，長遠來說，能以最優惠的價格入手電子書，\n本來已經買書買到變成博客來白金會員的我，也依照建議漸漸的把自己的電子書收藏轉向 Kobo。\n(這社團勸敗的能力也很可怕)\n我現在都說是我投資自己的金額更高了！才不是什麼敗家！\nKobo Libra 2 就有防水很香，現在又有彩色，更香了 一直都很害怕電子閱讀器碰到水，之前買 BOOX Page 7 就是因為怕水一直用的很小心，\n但這台官方示範都直接整台泡在水裡給你看了，\nhttps://youtu.be/TihMGaXFYSM?si=s7M9XQV02xC4m21b\u0026t=23\n太讚了！這就是能帶去浴室閱讀的電子神器！\n早在 Kobo Libra 2 就一直很想衝這個防水特性去買，還好有忍住！居然後來還能變彩機！那我更喜歡了！\n前一台也沒浪費，開放式與封閉式各自有了定位，圖書館借書還是省錢王道 雖然說買了這台，但前一台其實也沒有浪費，BOOX Page 7 之後會變我「圖書館借書」與「博客來電子書」的主力機，\nKobo 是封閉式閱讀器，也代表只能看 Kobo 的書，\n但圖書館借電子書，才是真正能幫忙省不少錢、也能看好書的解決方案，而且現在 24 小時想借書就借，相比以前真的很方便，\n不論如何都很推薦先去把全台灣借閱證辦好辦滿，現在在家線上就能辦了！\n購入價 我是 2024/4/12 在 Rakuten 樂天市場 買的，\nKobo Libra Colour 7吋彩色電子書閱讀器 (白) 購入價 $7349\n$1100 (可以折的皮套) $1100 (附筆槽的皮套) $2099 (Kobo Stylus 2 觸控筆) 另外跟上活動有送 $800 購書金 + ＄200 KOL 推薦加碼購書金\n還有樂天點數回饋 10%，等值約 734 元 (只算本體，其他加購的 10% 我沒算)\n加購的配件都有 -100 元，但現在想想當初應該少買一個皮套\u0026hellip;，有點浪費錢了，\n再加上這皮套有個小缺點，他都沒有包覆到側邊，下面有照片，\n我覺得有點怕側邊刮到，算是設計上我覺得小可惜的地方。\n開箱囉！ (圖多，想看心得可以直接拉到文章結尾處) 寄來時的樣子 △ 打開箱子時，初次見面的樣子\n△ 聽說是預購時才有附贈的貼紙?\n開箱 Kobo Libra Colour 7吋彩色電子書閱讀器 (白) △ 外盒，彩色真的很好看，包裝也很有書的氣息\n△ 背面，從左邊的封條撕開，包裝簡單俐落\n△ 打開囉！螢幕就直接顯示彩色的給你看！\n△ 這個是待機畫面就有的樣子，不是貼紙貼上去的彩色！\n△ 本體背面\n△ 附贈的說明書兩張\n△ 底下打開就是充電線，有點可惜還不是兩頭都 type-C 的，不過也確實目前還沒有那麼普及\n△ 特別拉看上面來確認一下，上面還真的是沒有東西，卻有個空間XDDD\n△ Kobo Libra Colour 7吋彩色電子書閱讀器 (白)，全部內容物合照\n開機 △ 開機後會先要求設定語言\n△ 然後要等待一陣子進行更新\n△ 更新後，照著提示登入就能順利進到主畫面了\n△ 彩色果然後是真的比較賞心悅目！！！雖然也有很多人提過看純文字不需要彩色\u0026hellip; 但還是\u0026hellip; 一種內心的滿足(?)\n△ 開箱 Kobo Libra Colour 7吋彩色電子書閱讀器 (白) 就先到這邊啦~\n開箱 Kobo Stylus 2 觸控筆 △ 外盒的樣子\n△ 打開來就是說明書\n△ 說明書下面就是筆的本體了，下方還有兩個可以替換的筆頭\n△ 這邊就是筆的全部內容物了！\n△ 順便備份一下筆的指示說明書，我也是看了才知道「白燈是充飽電」、「按住劃線才是螢光筆的功能」\n開箱 保護殼 比較不是重點，稍微開一下\n△ 搶預購時沒想太多，就覺得好像需要折的，又好像需要一個放筆的，真的中計了\u0026hellip; 有點浪費，而且為什麼沒出一個又能放筆又能折的！！！\n△ 可以折的長這樣，當立架使用，但之前跟社團又買了飛行支架，感覺之後這個保護殼可能會很少用\u0026hellip; 或是出遠門不想帶筆跟飛行支架就會拿出來用一下(?)\n△ 皮套表面，選黑色單純就不怕髒，皮套就是拿來保護主機用，雖然這次有出一些顏色很漂亮，但畢竟就保護用，而且漂亮的顏色之後髒了，我擔心看到髒的地方就會影響心情(?)\n△ 有附筆槽的裡面長這樣，就是有一個可以卡住筆的地方\n△ 我覺得側邊沒包到是個缺點，這樣我會很怕側邊去括到，小可惜，但應該之後副廠有出自己的保護殼就能解決的問題\nKobo Stylus 2 觸控筆的用法 這支筆主要就兩種功能\n筆：可以作為寫字或螢光筆使用 橡皮擦 (筆的尾端)：可以擦掉寫的字與螢光筆 使用方法 筆有兩種模式 直接寫：就是一般的筆跡 按住寫(劃線)：變成螢光筆 剛開始使用時覺得不是很直覺，原來是按住才是螢光筆，我一開始以為畫過去就是螢光筆了\n筆的橡皮擦 螢光筆跟一般的筆跡都可以用橡皮擦擦掉，「輕輕擦過」就可以把「整個筆跡」刪除 我一開始還當作是真的橡皮擦的感覺，來回擦了好多次XD，\n後來發現根本不用，還有點擔心一開始擦拭來回磨來磨去，是不是有減損橡皮擦的壽命QQ\n背面的橡皮擦設計很有趣，螢光筆與筆跡的消除，都可以使用橡皮擦輕輕擦過就可以消除「整段筆跡」\n總結 - 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n一個彩色後，感覺缺點都看不見了(?) 我果然還是喜歡彩色多一些，人生就該是彩色的(?)，\n一個彩色就能忽略所有缺點了，不過這台體驗上也沒特別感受到什麼缺點。\n而且真的很順，雖然很多人都說晶片性能比不上手機，\n用到現在也沒有覺得卡過，這樣的性能對於電子書應該已經非常足夠了！\n然後有防水真的很讚！那種用起來的安心感，體驗真的好！\n這也是我當初一直差點就敗下去 Kobo Libra 2 的痛點，畢竟在蹲廁所時經常看書(?)\n再加上不到 8000 的價位，跟一台手機比，價格真的不是問題！\n不是主機的缺點 真的要說就是那個皮套沒保護側邊，讓我很怕側邊去刮到，\n還有可以折的皮套跟附筆的皮套沒整合成一個，有點可惜兩個優點沒有合在一起，\n不過這些之後應該靠其他廠商設計的保護套應該就能解決的，都是小問題。\n","date":"2024-05-03T00:34:54+08:00","image":"https://wongwongnotes.com/images/restored/2024/05/IMG_9945-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/kobo-libra-colour/","tags":[],"title":"【嗡嗡開箱 #24】Kobo – Kobo Libra Colour 7吋彩色電子書閱讀器 (白), Kobo Stylus 2 觸控筆, Kobo Libra Colour 保護殼 (折疊殼 or 附筆槽) (圖多) | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 由於自己電子書很多, 一直都很想要有一個閱讀器來方便閱讀,\n以前都是用 ipad 但眼睛看久了還是會酸,\n希望這次第一次嘗試購入閱讀器能改善這個現象。\n購入價 我是 2024/3/26 在 Pchome 羅技官網 買的，購入價 $7780 + $880 (加購皮套)\n開箱囉！ 外盒 △ 外盒的樣子\n△ 打開的樣子\n△ 包的好好的\n△ 商品外觀\n△ 總共有兩個東西，本體+皮套\n本體 △ 本體外觀\n△ 拆開外包裝\n△ 把內盒抽出來\n△ 本體出現囉\n△ 本體近拍\n△ 本體正面，原本以為這個是什麼貼紙，沒想到是關機時螢幕就這樣顯示！！！\n△ 本體背面，大家都說這個很有質感\n△ 開機選語言\n△ 進行一些初始的設定\n△ 進入主畫面囉！\n△ 支援 google play，基本上相關的東西應該都能用 (只是閱讀器還是拿來閱讀就好，效能沒得跟手機比XD)\n皮套 △ 皮套外觀\n△ 拆開包裝\n△ 內容物\n△ 皮套近拍\n△ 皮套內部\n合體 △ 最後的樣子！！！\n總結 - 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n暫時覺得看閱讀器的確有比看 ipad mini 舒服很多，\n不過本身畢竟定位就不同，所以也不會拿 ipad mini 要跑的遊戲拿來就跟這個比XD\n暫時覺得都很不錯，跟事前有做的功課一樣！\n","date":"2024-03-29T16:44:41+08:00","image":"https://wongwongnotes.com/images/restored/2024/03/IMG_9393-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/boox-page-7/","tags":[],"title":"【嗡嗡開箱 #23】文石 – BOOX Page 7 吋電子閱讀器 | 不專業開箱文"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n暖身 橋式：重點應該放在大腿內側，90度保持，另外一側抬起放下\n弓箭步：單膝下跪 9090 抬腿\n弓箭步旋轉：單膝下跪 9090 旋轉側身 抬腿\n側蹲：腿一個伸展一個內收 注意髖\n動作 腿推 腿要打開\n划船 注意前胸椎\n核心抬腿 (躺平, 抬腿) 下拉\n","date":"2024-03-07T16:05:31+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-47/","tags":["健身筆記"],"title":"【健身筆記 #47】2024/3/7 教練課47 - 橋式, 弓箭步, 弓箭步旋轉, 側蹲 | 腿推, 划船, 核心抬腿, 下拉"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n暖身 9090呼吸\n死蟲式\n側棒式 打開胸\n拉開瑜珈墊 打開胸\n動作 cable 划船\n下拉 肩膀打開 頭往後\ncable 夾胸 手伸直往前弓箭步 注意髖的發力\n三頭下壓機\n二頭彎舉機 肩膀打開\n","date":"2024-02-19T16:02:03+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-45/","tags":["健身筆記"],"title":"【健身筆記 #45】2024/2/19 教練課45 - cable划船, 下拉, cable 夾胸, 三頭下壓機, 二頭彎舉機"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n暖身 (要練背的時候不要做卷腹，核心可能會比較難維持)\n髖關節訓練\n坐在箱子上手伸直貼箱側，只透過髖去做抬腿放下(不可以外八)\n站立式髖關節訓練\n單腳夾瑜珈球 另外一側抬腿(髖屈）與往後(髖伸)\n輔助：拿一個竿子，撐住另外一側幫忙維持平衡 平板支撐(球) 手肘往前推, 頭部往前 頭往後時(手肘往前)維持張力 (吐氣)\n平板支撐(箱子) 顛腳往前 頭向前時(手肘往後拉) 維持張力 (吐氣) 硬舉\n肩、膝蓋、腳踝 一線，膝蓋不要內八，要往外\n注意下去時的胸椎(前側)有沒有伸展\n練腳的張力 在家拿抹布用腳掌抓過來看看\n動作 臉拉\n練外三角\n注意脊柱伸直 還有維持臉的高度\n","date":"2023-12-26T15:46:02+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-39/","tags":["健身筆記"],"title":"【健身筆記 #39】2023/12/26 教練課39 - 髖關節訓練, 平板支撐, 硬舉, 臉拉"},{"categories":["116 - Python 系統控制"],"content":"前言 這篇文章的問題來源於，\n我之前寫了一個程式會導致記憶體用完導致程式被 killed 的問題，\n那時候很困擾到底哪幾行導致的錯誤，\n因此那時候就研究了 memory_profiler 這個套件\n安裝 pip install memory_profiler 範例程式碼 import numpy as np from memory_profiler import profile @profile def foo(): d = np.ones((100, 100, 100)) return d if __name__ == \u0026#34;__main__\u0026#34;: foo() 結果 透過在指定的 func 增加 decorator 「@profile」，\n我們可以知道每一行程式碼導致的記憶體變化。\nReference https://pypi.org/project/memory-profiler/ https://www.youtube.com/watch?v=tIKo2uex8x4\u0026ab_channel=JCharisTech ","date":"2023-12-13T23:16:29+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2023-12-13-%E6%99%9A%E4%B8%8A11.13.44.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/python/python-memory-profiler/","tags":["Python","Python 系統偵測","系統控制"],"title":"【Python 系統相關 #3】python 利用 memory_profiler 監測程式執行時的記憶體變化"},{"categories":["043 - 個人作品"],"content":"ChatGPT-Linebot using python flask on vercel 作者註：本專案因為作者我的免費 API 額度已經用完了，然後我現在使用的是 ChatGPT Plus，\n如果之後要我本人更新可能要等我哪天改用 ChatGPT API 了\u0026hellip;\n現在最新的版本感謝網友 @willismax 提供的 PR：https://github.com/howarder3/GPT-Linebot-python-flask-on-vercel/pull/17\n但同樣的因為我沒有 ChatGPT API 的額度了\u0026hellip; 所以也沒辦法測就直接 merge 了，\n如果不能動再請大家幫忙發個 Issues/PR，感謝大大們🙏\nlast updated: 2023/10/9 更新說明 by @willismax，與修正的 repo (merged) vercel.json修正，改為第2版 修正單純安裝Flask 2.2.2 會與 Werkzeug 衝突的問題，在 requirements.txt 添加 Werkzeug 2.3.7 (參考stack overflow) 本篇教學無經驗的新手也可學習，無須寫任何程式。\n無經驗預計 15 ~ 20 分鐘都可以完成。老手最快可能 5 分鐘就搞定\n這是使用 python flask 套件撰寫的 gpt-linebot 不需寫 code，只需去網頁設定一些內容，新手 15 分鐘內也能建立自己的 gpt-linebot Why flask? 簡單好用，且支援 vercel Why vercel? 免費！！！免費額度就很夠一般使用，是 heroku 不再免費後的好選擇 註：ChatGPT 與 gpt 是同樣任務的模型，而目前透過 API 只能使用到 GPT-3 (本程式使用的方法)\n而非 ChatGPT 使用的 GPT-3.5\n已經改為 GPT-4，與最新的 ChatGPT 相同，只是改為使用 OpenAI API 的方式實現\n安裝步驟 主要會有四個地方要去：(這部份不看也沒關係，以下照著做就可以了！)\n我的 github repo：透過 python 串接 openai 的 API，並透過 linebot sdk 提供簡單的訊息回復 openai：申請 OpenAI 的 API KEY line developer：創建機器人 vercel：提供訊息回復，雖然是 serverless 但已經很符合我們的需求 step 1. 至 github fork 專案 去我這個專案的 github repo\n按下面的按鈕，\nfork 一份檔案到自己的帳號底下，等等我們會使用到\n可以的話，旁邊的 Star 也幫我按一下，是對創作者最大的鼓勵！\nstep 2. 申請 OpenAI 的 API KEY 可以直接去這裡，一直往下拉，找到這個按鈕，並生成一個 API KEY\n請務必複製下來，這個 KEY 我們取名為 OPENAI_API_KEY step 3. 去 line developer 建立一個新的機器人 這邊熟悉的人動作應該超快，可以略過，\n以下教學是針對完全沒經驗的新手\n我們先到 line developer 的首頁註冊一下，\n註冊完後，點選 Messaging API。\nstep 3-1. 創建新的 channel 第一次使用要創建一個新的 provider 與 channel，\n一個 provider 可以有很多 channel，\n「而一個 channel 對應的就是一個 chatbot」，\n這邊以下都照自己想要的名字跟事實填就好。\nstep 3-2. 在 Basic Settings 的分頁，取得 LINE_CHANNEL_SECRET 在 Basic Settings 的分頁，往下找到 channel secret\n請務必複製下來，這個 KEY 我們取名為 LINE_CHANNEL_SECRET step 3-3. 在 Messaging API 的分頁，進行一些機器人初始設定 (並加機器人好友) 再來我們去上方，選擇 Messaging API 的分頁，\n我們先關閉一些可能會吵的東西 (預設的自動回復之類的)，\n這邊可以順便掃一下 QR code 或透過 line ID ，加機器人的 line 好友！\n我自己是設定如下：\n允許加入群組要注意使用 openai 額度可能會超快 「自動回復訊息必關！！！」，那是 line 的自動回復，不是我們要的 歡迎訊息也可以關，這邊我是開著 step 3-4. 在 Messaging API 的分頁，取得 LINE_CHANNEL_ACCESS_TOKEN 最後，在 Messaging API 的分頁的最下面，\n找到 channel access token，點選右邊發行，並把他記下來。\n請務必複製下來，這個 KEY 我們取名為 LINE_CHANNEL_ACCESS_TOKEN 請不要把這金鑰分享給別人，別人可能會拿去作壞事！！！\n然後這邊網頁先不要關，等等還會用到！！！！\nstep 4. 去 vercel 設定相關的環境變數，完成啟動機器人！ step 4-1. 新增 project 去 vercel 首頁，add new project\nImport Git Repository，選擇你剛剛 fork 的專案 import\nstep 4-2. 設定環境變數 選擇「Environment Variables」，把我們剛剛紀錄的 OPENAI_API_KEY、LINE_CHANNEL_SECRET、LINE_CHANNEL_ACCESS_TOKEN 都設定至環境變數，\n按下 Deploy 等待一下就完成囉！\n完成後我們可以看到會有以下的三個環境變數 step 4-3. deploy 完成後，可以簡單確認是否有成功 去部屬完成的 vercel 頁面，紅框處可以拿到我們要用的網址，\n把這個網址複製下來，等等要用\n這個網址我們也可以點開，應該會出現我們在程式預先寫好的 Hello, World! (應該是一個只有 Hello, World! 的網頁)\n我們可以藉此確定程式有正常的被 Deploy\nstep 5. 設定 webhook 回到 line developer 的 Messaging API 分頁，\n將剛剛 step 4-3. 的網址填入，並在後面加上 「/webhook」，例如下圖\n可以用 Verify 看看有沒有問題，通常應該會是寫「Success」\n「記得開啟下面的使用 Use webhook 」 完成圖範例 此 linebot 的其他一些內建功能 機器人「說話開關」 這個本來是我除錯用的，因為有時候回復一些怪東西會很吵，\n意外得到好評，所以這個功能就被保留下來\n輸入「說話」：機器人開啟說話模式，預設是開啟的 輸入「閉嘴」：機器人暫停說話模式 (但一段時間會自動再起動)，閉嘴後將不會對任何對話有反應。輸入「說話」可再次開啟對話。 其他環境參數功能 參考自 memochou1993/gpt-ai-assistant 的作法，下列參數也可藉由設定 vercel 的環境變數來作調控。\n環境變數名稱 預設值 說明 OPENAI_MODEL text-davinci-003 請參考 OpenAI 對 [model](https://beta.openai.com/docs/api-reference/completions/create#completions/create-model) 的敘述 OPENAI_TEMPERATURE 0 請參考 OpenAI 對 [temperature](https://beta.openai.com/docs/api-reference/completions/create#completions/create-temperature) 的敘述 OPENAI_FREQUENCY_PENALTY 0 請參考 OpenAI 對 [frequency_penalty](https://beta.openai.com/docs/api-reference/completions/create#completions/create-frequency_penalty) 的敘述 OPENAI_PRESENCE_PENALTY 0.6 請參考 OpenAI 對 [presence_penalty](https://beta.openai.com/docs/api-reference/completions/create#completions/create-presence_penalty) 的敘述 OPENAI_MAX_TOKENS 240 請參考 OpenAI 對 [max_tokens](https://beta.openai.com/docs/api-reference/completions/create#completions/create-max_tokens) 的敘述 MSG_LIST_LIMIT 20 prompt 參數往回參照的句數 INIT_LANGUAGE zh 決定初始語言，可設置為 \"zh\" 或 \"en\" TODO List \u0026amp; Future Work 目前基本功能都已經有了，然後我比較忙可能沒空一直更新QQ\n還有很多可以優化的地方，歡迎提供 PR！\n(已調整完成) 回復文字感覺不是很順 (可能需要研究一下 API 使用方法) [x] (已調整完成) 記憶功能 ... 目前算法是「紀錄使用者與 AI 的前20句對話」，嘗試推論出下一句話應該要說什麼。以達成延續話題的效果。\n靈感來源 感謝 memochou1993/gpt-ai-assistant 提供的 node.js 版本串接 vercel 示範，讓我有了想把 python linebot 也串進 vercel 的靈感，(目前感覺下來，免費又好用(?)) 感謝 Lanznx/HealthLineBot 給了一個很好的 python Django 範例，然而我不會 Django XD，vercel 官方文件好像也沒有提到這部份，總之後來就改成了 flask 版本，也符合 linebot 推薦的範例。 參考資料 Line 官方提供的 python flask 製作 linebot 的 sample code line/line-bot-sdk-python Vercel 官方提供的 python runtime Flask 範例 Deploy an example with Flask ","date":"2023-12-10T00:01:29+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-12-%E4%B8%8B%E5%8D%8811.24.29.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/works/gpt-linebot-python-flask-for-vercel/","tags":["Python","作品集","個人作品"],"title":"【Side Project】(全圖文教學) 用 Python flask 實作 ChatGPT 的 Linebot，並部屬至 Vercel 上"},{"categories":["382 - Bash 檔案處理"],"content":"前言 透過指令來知道「單一」檔案最後的更新日期\n其實 「ls -l」(或 「ll」) 也可以做到，\n但缺點就是會印出全部的檔案資訊，\n而且如果我們要針對單一的檔案處理寫 script 也不好用，\n所以才會有這個需求\n範例 date -r ./test_opt.sh date -r ./test_opt.sh \u0026#34;+%m-%d-%Y %H:%M:%S\u0026#34; date -r ./test_opt.sh \u0026#34;+%Y%m%d_%H%M\u0026#34; 效果如下 ＃Reference\nhttps://stackoverflow.com/questions/16391208/print-a-files-last-modified-date-in-bash https://stackoverflow.com/questions/14842195/how-to-get-file-creation-date-time-in-bash-debian ","date":"2023-12-08T17:23:55+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/%E6%88%AA%E5%9C%96-2023-12-08-%E4%B8%8B%E5%8D%885.21.35.png","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash/bash-get-file-last-updated/","tags":["Bash","Linux","Ubuntu","檔案處理"],"title":"【Bash 檔案處理 #3】bash 確認「單一」檔案最後更新日期 (get file last updated)"},{"categories":["861 - html 語法"],"content":"前言 我本身不是個網頁工程師XD\n但我弄這個網站有些地方會需要語法，\n因此順便紀錄一下，內容應該都很雜XD\n透過 html 語法達到 highlight 效果 主要於側邊欄會使用\n範例程式碼 要 highlight 的文字！) 有試過用 css 做 highlight 效果，但好像不行，不知道是不是有什麼語法跟外掛衝突到。\nReference https://www.fooish.com/html/hyperlink-a-tag.html https://www2.lidicity.com/julebu/zhiwangle/wenzi_jiadise.html ","date":"2023-12-08T03:27:50+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/web-basics/html/html-highlight/","tags":["Bash","HTML","Linux","Ubuntu","語法"],"title":"【html #1】html highlight 語法 (強調重點語法)"},{"categories":["441 - VScode"],"content":"前言 又要介紹 vscode 常用的好功能，\n有沒有碰過 workspace 底下超級多的檔案！\n這種時候如果要跳到特定某一特定的檔案應該要怎麼辦呢？\n(通常很有可能是一層一層資料夾慢慢找對吧\u0026hellip;)\n「cmd + P (windows: ctrl + P)」這時就非常方便囉！\n效果 先使用 cmd + P 叫出這個視窗，開始輸入你想要找的檔名，\n過程中他都會自動配對，找到後 enter 就可以直接開啟 (已開啟的話會跳到對應視窗)，\n這樣就不容易迷失在檔案海之中囉！\n","date":"2023-12-08T02:04:00+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/%E6%88%AA%E5%9C%96-2023-12-08-%E5%87%8C%E6%99%A82.01.59.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-ctrl-p/","tags":["VSCode"],"title":"【VScode #7】VScode 快速開啟/跳到 workspace 中的特定文件 cmd + P / ctrl + P"},{"categories":["441 - VScode"],"content":"前言 又要介紹 vscode 常用的好功能，\n有沒有碰過那種程式碼超級多行的檔案！\n這種時候如果要跳到特定某一行應該要怎麼辦呢？\n「control + G (windows: ctrl + G)」這時就非常方便囉！\n效果 記法: G 我是記「go」\n在後面輸入自己要的行數，就可以直接跳到對應的那行了。\n","date":"2023-12-08T01:58:39+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/%E6%88%AA%E5%9C%96-2023-12-08-%E5%87%8C%E6%99%A81.55.58.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-cmd-g/","tags":["VSCode"],"title":"【VScode #6】VScode 快速跳到程式碼的某行 ctrl + G"},{"categories":["418 - Git 問題解決"],"content":"前言 這是我在 fork 別人專案的時候, clone 到自己 local 電腦修改後無法上傳所碰到的情況\nfatal: couldn\u0026#39;t find remote ref master 「注意: 只是記錄我碰到的問題, 並非所有碰到此訊息都是一樣的解法」\n個人解法 後來注意到 .git/config 裡面\n原來 origin branch name 是 「main」 而不是常見的 「master」 (也是常見啦沒有對錯)\n總之就是不能下\ngit push origin master 這種情況要改下\ngit push origin main Reference https://blog.csdn.net/ltstud/article/details/79935001 https://gitbook.tw/chapters/github/fail-to-push https://stackoverflow.com/questions/29297154/github-invalid-username-or-password ","date":"2023-12-08T01:18:10+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git/git-fatal-couldnt-find-remote-ref-master/","tags":["Git","Linux"],"title":"【Git】問題解決：fatal: couldn't find remote ref master. (git 無法 push)"},{"categories":["416 - Git 協作相關"],"content":"前言 在多人協作的時候，特別是在 github 這種可能會與陌生人合作的地方，\npull request 是一個讓自己的修改能合併到別人的修改的重要方法\n舉例 這裡以與合作別人的 github 專案為例\nstep 1. fork 別人的專案 找到 fork 按下去複製一份到自己的地方\nstep 2. fork 後, 視同於自己的專案, 可以自由修改後上傳 自行修改與 push 非本文重點，這裡就不贅述\nstep 3. 提交 pull request 至他人的 repo step 3-1. 建立新的 pull request (PR) step 3-2. 選擇自己的 repo branch, 提交到對方的 repo branch 這裡是以 branch 為單位去 merge, 如果專案小也許就是 master 對 master\n大的話可能就是 feature branch 或 master 對 feature branch 之類的\u0026hellip; 可以自行推敲\n剛進來可能會找不到自己的 repo, 因為目前的介面只有看的到對方的所有 branch\n如果你也是此專案的直接 contributor, 當然可以直接上傳到原先的 repo\n但我們不是, 我們是 fork 回來修改的,「非原 repo 的 contributor 」，\n這時候我們就要選以下的按鍵，「去找自己 fork 的 branch」\n剩下的就是把一些資訊填一填，越清楚越好，好讓原作者知道你這修改在幹嘛。然後就可以提交了！\n這裡也可以想想，如果真的想被 merge，如果是那種還要讓原作者自己慢慢 trace 你的 code 才能知道功能的\n那想必一定會被放很久，或被擱置吧，所以這裡就盡量用心寫吧！\nReference https://gitbook.tw/chapters/github/pull-request ","date":"2023-12-08T01:12:46+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/PR-2.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-collab/git-pull-request/","tags":["Git","協作相關"],"title":"【Git 協作相關 #4】git pull request (PR) - 合併 change 到別人的 change 中 (或反之)"},{"categories":["122 - Python conda (anaconda)"],"content":"前言 不知道 windows 上面要怎麼管理 python 環境 (mac 用太久了哈哈哈哈)\nanaconda 感覺可以嘗試看看用來管理 python 環境，這邊紀錄一下。\n產生 \u0026amp; 啟動 產生新的 python 環境 conda create -n myenv python=3.10 # or conda create --name myenv python=3.10 自己常記錯: conda init，初始化 shell 的時候才會用\n通常用不到。\n啟動 conda myenv activate 檢查有哪些 python 環境 conda env list 移除 移除 python 環境 conda remove --name myenv --all Reference https://www.freecodecamp.org/news/how-to-delete-an-environment-in-conda/ https://docs.conda.io/projects/conda/en/stable/dev-guide/deep-dives/activation.html https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#activating-an-environment ","date":"2023-12-07T21:13:39+08:00","permalink":"https://wongwongnotes.com/posts/python/environment/python-conda-anaconda/conda-create-env/","tags":["Python","conda"],"title":"【Python conda #3】conda 產生/移除 新的 python 環境"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 羅技家的產品，特別是滑鼠或鍵盤我也買了不少了，\n我自己都覺得非常耐用，目前手邊的鍵盤滑鼠都還沒有想要替換的感覺，\n真的不知道什麼時候才能壞掉好讓我換下一把呢\n這次要來開箱的是羅技家新出的「人體工學鍵盤」，\n這也算是我的第一把人體工學鍵盤，\n之前有使用過他們家的人體工學滑鼠 LIFT，\n當時印象就非常不錯， 甚至後來還買了兩隻加上這次買\nWave Keys 可以湊套裝的新的一隻 LIFT， 這是我的第三隻人體工學滑鼠了！！\n（家裡、公司、租屋處，剛剛好我沒有浪費😆）\n對於這次人體工學鍵盤的表現我也十分好奇會是怎麼樣子的，\n因此就買了這個套組。\n購入價 我是 2023/11/28 在 羅技官網 買的，購入價 $3980\n剛好有活動，剛開始賣 Wave Keys + LIFT 套組特價。\n開箱囉！ 開箱 △ 寄來的包裹，有羅技的標籤\n△ 開箱初次見面\n△ 看起來保護還行，裡面就滑鼠鍵盤 + 贈品\nWave Keys 人體工學鍵盤 △ 鍵盤外觀，第一眼看到外合就覺得，怎麼比想像中小這麼多！！！\n△ 拿另外一個 100％ 鍵盤來比較，怎麼盒子可以這麼小！\n△ 外盒背面\n△ 開箱！！ 看起來到處都有防撞保護\n△ 原來是少了方向鍵的獨立一區，剛剛開箱時沒有注意到\n△ 最後獨立來拍一張！ △ 跟 mac 來比較一下大小，做的稍微比 mac 大一點又不會太大！\n△ 放在 mac 上面的大小\nLIFT人體工學垂直滑鼠 其實之前就有開過了，這次就簡單開哈哈哈哈！\n單獨的詳細版可見：\nhttps://wongwongnotes.com/posts/life-and-work/reviews/reviews/logi-lift/\n△ 產品外觀\n△ 初開啟外盒與內容物\n△ 裡面就很單純的一顆滑鼠\n△ 全部的內容物\n贈品 - 頸枕開箱 既然都送了，就也來開一下\n△ 產品外觀\n△ 初打開\n△ 頸枕附贈袋子\n△ 頸枕的樣子，以贈品來說還不錯的，如果要好一點就要自己買了\n擺在桌上的樣子 最後來整理一下工作區吧！！\n△ 整個工作區\n△ 特寫產品\n優點 優點 1 - 人體工學有感，手托軟墊很也很舒適！ 用久了習慣會很舒服，人體工學真的有感，覺得打字輕鬆舒適很多\n這種舒適感比較建立在「手移動的距離」上，如果沒有使用到一定程度以上，可能差別就沒那麼明顯xd\n但我是覺得「以整天都會在辦公室打字的人來說，(習慣鍵盤配置後) 應該是真的會有感的程度」\n另外手托軟墊也很舒服！\n優點 2 - 多工連線！！ 多台裝置用戶真的一定要推這功能！！！ 雖然這不是這次鍵盤的專門功能，但也並非所有鍵盤都有的功能，\n因為我很常會有需要在「個人電腦/工作電腦」之間切換，\n如果沒有切換的功能，那我可能要準備兩組鍵盤、滑鼠，或是一直把線進行插拔的動作，非常的不方便！！！\n優點 3 - 一體成形，體積小、方便攜帶 照剛剛鍵盤的大小比對 mac，就知道他非常的小巧，\n而且他與手托一體成形！非常方便好攜帶！\n而建立在方向鍵區沒有獨立出來，鍵盤小巧很多，更好攜帶收納！\n優點 4 - (個人主觀) 羅技家的產品挺耐用的 我自己本身也有不少的羅技鍵盤滑鼠，目前還真的沒壞過幾個\u0026hellip;\n不讓人有機會換滑鼠鍵盤嗎\n總之我覺得挺耐用的，買了好像也不用太擔心用不久就壞掉的問題，\n產品品質也不錯，很少有買了會有故障的問題，當初買根本沒在擔心這些！\n缺點 缺點 1 - 鍵盤配置學習會有陣痛期 因為鍵盤配置特殊的關係，如果是長期使用一般鍵盤的人，突然換過來這個鍵盤一定超不習慣，\n甚至很有可能習慣前就放棄習慣了，\n這時候就要看個人有沒有想花時間跟他培養感情了，\n也要培養出感情(習慣鍵盤的配置)，才能享受人體工學的舒適哈哈哈哈。\n缺點 2 - 少了獨立方向鍵區、Page up, down 鍵區 這點特別寫出來，雖然跟前一點提到的鍵盤配置指的是同一件事情，\n但因為我自己有使用獨立方向鍵區、Page up, down 鍵甚至是 Home, end 鍵的習慣，\n這個是我在寫程式的時候突然發現「手很自動的想去按這些已經沒有的鍵的位置XD」\n總之如果真的像我一樣很經常可能會使用獨立方向鍵區或 Page up, down 鍵區，\n可能可以再多考慮一下XD\n另外因為上下左右區被往左移動，與英文鍵盤區的右側結合，\n這點也是絕對要去習慣的！！！\n缺點 3 - (其實還好) 對比 MX 系列，沒有自動喚醒 其實這個自己因為我自己使用 MX 系列使用太習慣，手一靠近鍵盤就會自動喚醒的感覺好棒！！！\n結果換成這個鍵盤沒有自動喚醒了，\n讓我有些微感覺的不習慣XD，但其實也只是差多按一個按鍵而已\n這真的是小事XD，比較像是「我覺得如果有會更棒，沒有也還好的那種XD」\n總結 - 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n人體工學的舒適感 (在習慣後) 體驗確實非常棒！可以打很久手指不太會疲勞！\n另外搭配 LIFT 滑鼠，可以讓整天滑滑鼠的手部摩擦降到最低！\n可惜為了鍵盤的配置，少了獨立的方向鍵區與 home 鍵區，\n如果是有使用這區習慣的人，那可能就要另外找辦法或是要取捨一下了！\n整體而言我覺得體驗是不錯的，唯一就是要能夠接受鍵盤配置的適應期 (滑鼠也是)，\n這個就是看個人的選擇了XD，願意習慣配置就會換得更舒適的體驗。\n","date":"2023-12-03T03:50:01+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/IMG_7543-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/logi-wave-keys/","tags":[],"title":"【嗡嗡開箱 #22】羅技 – Wave Keys 人體工學鍵盤 (+ LIFT人體工學垂直滑鼠 \u0026 贈品頸枕) 開箱 | 不專業開箱文"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n暖身 腳踩牆壁死蟲 夾滾筒 手伸直肩外旋 頭墊高\n進階手掌往後搬\n不踩牆壁再一次\n腋下緊是脊柱伸直頭往上\n趴 Y 畫圓，手伸直畫半圓後回來\n動作 Cable 划船\n支撐腳在前（因為如果重心的髖側可以旋轉拉更多）\n胸口打開、頭往上、注意手肘（二頭碰手臂）、肩膀不要動 Bench肩推\n跟前面一樣注意肩外旋支撐\n只有手肘動\n胸口打開、頭往上(後背弱要多練練） 單槓跨步蹲\n注意9090\n後腳往前，頭往前（往後會拱腰）\n重心的後腳 從膝蓋到髖到身體側到頭\n","date":"2023-11-30T15:41:23+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-36/","tags":["健身筆記"],"title":"【健身筆記 #36】2023/11/30 教練課36 - Cable 划船, Bench肩推, 單槓跨步蹲"},{"categories":["417 - Git LFS 大型檔案處理"],"content":"前言 git lfs 可以幫助我們 上傳/下載 比較大的檔案，通常單純使用 git 我們會盡量保持檔案不要過大，\n而使用 git lfs 另外處理比較大的檔案。\n這篇專注於下載大檔案時，避免全部都下載的情況 (如果要想要無腦全下載也不怕的可以不用看這篇)\n範例 方法一 直接使用 「-I」\n可以直接指定明確的檔案名稱，或使用正規表達式\ngit lfs pull -I ./* git lfs pull -I ./*.txt git lfs pull -I ./test.txt 方法二 可以使用組合的方式，先透過 git config lfs.fetchinclude，先過濾出要 fetch 的檔案\n再使用 git lfs pull，把有對應的檔案都拉下來\n# will found match files git config lfs.fetchinclude ./* # check found files git config lfs.fetchinclude git lfs pull 這種作法的話，就是直接設定 lfs pull 的檔案對象，也就是就算我們之後下 git lfs pull，也只會對我們「有指定過的」檔案處理。\n至於要確認目前有哪些檔案被包含，可以使用「git config lfs.fetchinclude」來確認。\n另外，如果要解除上面所指定的範圍：\ngit config --unset lfs.fetchinclude ","date":"2023-11-26T03:48:28+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-lfs/git-lfs-filter/","tags":["Git","LFS","大型檔案處理"],"title":"【Git LFS #1】git lfs pull - lfs.fetchinclude 過濾出自己要的檔案，以免全部都下載"},{"categories":["124 - jupyter notebook"],"content":"前言 jupyter notebook 本身其實就是建立一個 server，只是一開始初學我們通常透過 localhost 去連接，\n實際上我們也可以透過一些設定，讓我們可以透過區域網路也可以連線上。\n方法 在 container 中\njupyter notebook --ip 0.0.0.0 --no-browser --log-level=\u0026#34;DEBUG\u0026#34; 說明 \u0026ndash;ip 0.0.0.0 : 表示監聽所有 ip (jupyter notebook 預設就是只能使用 localhost) \u0026ndash;no-browser：反正是在 remote，也不需要打開 browser (預設為開啟) Reference https://stackoverflow.com/questions/42848130/why-i-cant-access-remote-jupyter-notebook-server ","date":"2023-11-26T03:12:26+08:00","permalink":"https://wongwongnotes.com/posts/python/environment/jupyter-notebook/jupyter-notebook-remote/","tags":["Ubuntu","jupyter","notebook"],"title":"【Python jupyter notebook #1】在 linux 電腦上建立 jupyter notebook 的 server，使區域網路可以透過網頁連線至 linux 主機的 notebook"},{"categories":["211 - C++ 基礎語法"],"content":"前言 當我們有時候想要在 C++ 使用第三方套件的時候，我們需要下 -I (大寫的 i)，\n來幫助我們使用套件，\n因為我是從 Python 學習過來的，\n所以一開始的時候，對這一點非常的不習慣，\n在 Python 的習慣的流程是先作類似 pip install 後，直接 import package 即可。(這邊講的是通常，並非全部)\n而 C++ 在這部份並不相同。\n這裡我們以我經常使用的 C++ OpenCV 與 C++ Tensorflow 為例子\nC++ OpenCV mac 安裝 OpenCV mac 安裝 OpenCV，需要在終端機輸入以下指令 brew install opencv C++ Tensorflow step 1. git clone - 把 tensorflow 的 Library 下載下來 git clone https://github.com/tensorflow/tensorflow step 2. 連同剛剛 git clone 的 repo，建立資料夾結構如下 因為檔案非常的多，這邊只舉部分的檔案作為例子\ntensorflow tensorflow c lite ... third_party ... - test_tensor_flow.cpp (我們測試用的檔案) step 3. 寫一些測試用的程式，以下舉個範例 #include \u0026lt;iostream\u0026gt; #include \u0026#34;tensorflow/lite/core/c/common.h\u0026#34; int main() { TfLiteTensor* tensor = nullptr; return 0; } step 4. compile cpp 檔案，使用 Tensorflow library，下 -I tensorflow (大寫的 i) g++ test_simple_tensor.cpp -I tensorflow # uppercase i 其他的一些延伸問題 比照上述路徑的方式不對 \u0026#39;tensorflow/lite/c/common.h\u0026#39; file not found 需要使用 -I 的方式，IWYU 代表的是 include what you use 的縮寫， 總之 include 方式錯誤，需要研究 -I 的方式怎麼用 #include \u0026#34;tensorflow/lite/core/c/c_api_types.h\u0026#34; // IWYU pragma: export Reference OpenCV Tutorial C++ ","date":"2023-11-25T23:54:58+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-third-party-library/","tags":["C++","基礎語法"],"title":"【C++ 基礎語法 #10】C++ 透過 -I (大寫的 i) 使用第三方 library (third party library), 與 Python import package 的流程差別"},{"categories":["432 - tmux"],"content":"前言 重新命名方便查詢與分類任務\n指令 最常用: 更改 pane 名稱 crtl+b , 提示用: 更改 session 名稱 (session 就是 pane 的 collection) crtl+b $ ","date":"2023-11-23T22:12:28+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/tmux/tmux-rename/","tags":[],"title":"【tmux #5】tmux rename - 對 tmux 內視窗進行重新命名"},{"categories":["416 - Git 協作相關"],"content":"前言 協作最重要的合併功能，不外乎 merge 或 rebase\n這一篇我們專注於 rebase\n說明 rebase 的使用情境就是如果我們很努力的在 dev branch 開發，\n但大家一起合作的 master 可能有很多的更新 (新的 merge)，\n我們也想要同步進行更新一些新的 change，就需要 rebase\n效果 同上面的說明，大家一起做的 master，與你在 a 時間點切出去的 dev\n後來 master 又更新了很多 bcd\u0026hellip;\n我也想要 bcd，且維持我繼續開發的狀態，這時候就需要 rebase，\n結果大概如下 (注意只發生在 dev)\n實際範例 step 1. 切到 dev git checkout \u0026lt;your_dev_branch\u0026gt; step 2. git rebase master，表示拉 master (從我們切出去後的所有更新) git rebase \u0026lt;your_master_branch\u0026gt; step 3-1. (運氣好就不用) 看有沒有 conflict 需要解 這裡提供一個要解 conflict 的範例\ndev 上： 123456 master 上：123abc 因為不知道要怎麼樣的順序自動合併，勢必要解 conflict\n基本上調整確定是自己想要的樣子就好了\nstep 3-2. 解完 conflict 後，記得 git rebase \u0026ndash;continue 表示告訴 rebase 我解完了請繼續\n這裡可能有的步驟 git add \u0026lt;confilct_file\u0026gt; git rebase --continue 就解完了，就會變成下面這樣圖的狀態\n(目前自己在最後一個 commit，master 的所有更新都合併到 dev branch)。\nReference https://gitbook.tw/chapters/branch/merge-with-rebase ","date":"2023-11-22T00:28:00+08:00","image":"https://wongwongnotes.com/images/restored/2023/11/IMG_0088.jpg","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-collab/git-rebase-2/","tags":["Git","協作相關"],"title":"【Git 協作相關 #3】git rebase - 實際操作，把在 dev branch 的 commit 與 master 同步更新，並解 conflict"},{"categories":["199 - Python 自用腳本"],"content":"Basic debug in code - print 大法 print.\n延伸 - f-string format print(f\u0026#34;{target=}\u0026#34;) 可以輸出 target=\u0026lt;traget實際值\u0026gt;\ndebug with IDE - VScode debugger debugger, variable, watch, call stack 都看\ndebug with IDE - VScode 中斷點 中斷點\nAdvanced Advanced - try except try: pass except: print(\u0026#34;stop\u0026#34;) 下中斷點於 gotcha, 當 pass 會經過很多輪時非常好用.\nAdvanced - magic (dunder) methods 改天會在寫 magic (dunder) methods 是什麼\n註：dunder = double under\nprint(f\u0026#34;[{__class__}]{a=}\u0026#34;) 在超級多檔案時，可以快速定位「哪個檔案 -\u0026gt; class」，超好用\n至於我也想定位到 function，但目前好像沒有簡單的方法\u0026hellip;\n另外 _ _ name _ _ 、 _ _ qualname _ _ 有時候 debug 也好用。\n只想要找檔案不想要找 class name (比較短)，就用 name，\nqualname 雖然可以顯示 function name，但他的 object 要是 function\u0026hellip;\n例如 obj.qualname (自行補上雙底線)，我覺得有點不是那麼方便，\n畢竟這樣我早就知道他的 function name 了，還需要問嗎\u0026hellip;\nAdvanced - conditional 中斷點 於中斷點按右鍵，可設定條件式\nTools Tools - logger 待補\nTools - file dump 當資訊過多導致 debugger 跑不太動時，直接存到檔案內\nhttps://wongwongnotes.com/posts/python/concepts/python/python-debug-dump/ def dump_debug_str(data, filename): print(f\u0026#34;saving {filename} with data...\u0026#34;) with open(filename, \u0026#34;w\u0026#34;) as f_write: f_write.write(str(data)) ","date":"2023-11-21T00:06:01+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/python/python-debug-note/","tags":["Python","自用腳本"],"title":"【Python】python debug 自用方法整理筆記"},{"categories":["199 - Python 自用腳本"],"content":"前言 裝飾器 (decorator)，可以幫助我們快速地用簡易的方式達到修飾 function 的效果，\n以本篇的例子來說，我們可以替原本沒有計時功能的 function，\n以一行的方式直接新增計時的功能。\n範例程式碼 from functools import wraps import time def WongWongTimer(func): @wraps(func) def wrap(*args, **kargs): time_start = time.time() value = func(*args, **kargs) time_end = time.time() time_spend = time_end - time_start #print(f\u0026#34;[{func.__module__}.{func.__name__}] cost time: {time_spend}\u0026#34;) print(f\u0026#34;[WongWongTimer][{func.__qualname__}] cost time: {time_spend}\u0026#34;) return value return wrap 使用方式 - 單純版，撰寫 wongwong_utils.py 個人是存成 wongwong_utils.py 後，\n要使用時，只需要在想要偵測的 function 前一行加上這個「@WongWongTimer」即可！\n範例如下： from wongwong_utils import WongWongTimer @WongWongTimer def test(): print(\u0026#34;Hello World!!!\u0026#34;) test() 使用方式 - 大型程式重複使用版 (放入指定資料夾並設定 __init__.py) 通常在比較大型的資料夾中，我們會將此類工具嘗試放入資料夾當中。\n個人放的方法為，將上面那段程式碼存在 wongwong_utils.py 中，\n並把這個檔案放在一個名為 「utils」 的資料夾當中。\n參考下圖架構：\nmain.py utils __init__.py wongwong_utils.py 我們去修改 __init__.py，更方便我們去 import 這支程式。\n「 「init」」.py 的功能，主要是讓一個資料夾內的所有內容可以被模組化，\n我們可以使用 import 資料夾名稱，來直接引入所有我們需要的、且在資料將底下的模組。\n撰寫 utils 資料夾底下的 init.py (使 utils 成為 package) from .wongwong_utils import * __all__ = [\u0026#39;WongWongTimer\u0026#39;\u0026#39;] 撰寫 utils 資料夾底下的 wongwong_utils.py from functools import wraps import time def WongWongTimer(func): @wraps(func) def wrap(*args, **kargs): time_start = time.time() value = func(*args, **kargs) time_end = time.time() time_spend = time_end - time_start #print(f\u0026#34;[{func.__module__}.{func.__name__}] cost time: {time_spend}\u0026#34;) print(f\u0026#34;[WongWongTimer][{func.__qualname__}] cost time: {time_spend}\u0026#34;) return value return wrap 實作範例 from utils import WongWongTimer @WongWongTimer def test(): print(\u0026#34;Hello World!!!\u0026#34;) test() 因為上面已經把 utils 包裝成一份 package，\n所以我們可以直接呼叫此 package 內的 WongWongTimer 模組來使用。\nfrom utils import WongWongTimer 要使用時，只需要在想要偵測的 function 前一行加上這個「@WongWongTimer」即可！\n注意：utils package 必須在「不同的資料夾」底下才可以使用。\n因為被打包的 package 不包含本身\n(例如說我們想在 utils 資料夾底下這樣 import WongWongTimer，會找不到不能使用)\n未使用 init.py 加工成 utils package 的方法也可以 如果是一般未經過資料夾 init.py 加工的方法，我們也可以使用\nfrom utils.wongwong_utils import WongWongTimer\n可以簡記為 「from [file] import [module]」\n前面的 「from utils.wongwong_utils」 代表是尋找 .py 的路徑\n後面的 「import WongWongTimer」 就是要 import 的 class/function\n延伸討論 有點小可惜的是，這個方便的 decorator 只能夠給 funciton 使用 (但也夠方便了)，\n不能給單一行程式碼使用 (需要先包進 function 當中)。\n","date":"2023-11-20T23:45:18+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/python/python-timer-decorator/","tags":["Python","自用腳本"],"title":"【Python 自用腳本 #6】實用模組 - python Timer decorator 計時用裝飾器模組，簡易增加 python 程式碼計時功能 (Timer module)"},{"categories":["199 - Python 自用腳本"],"content":"前言 python attach 是 VScode 官方也有說明的一種方法\n使用 debugpy 套件，\n讓我們可以透過 port 來進行 python 程式碼的 debug\nlaunch.json 新建 launch.json 大部分內容都可以參考官方文件\nhttps://code.visualstudio.com/docs/python/debugging 設定 launch.json 內容 總之就是要設定一個 launch.json，可以參考官方，也可以參考我的。 (也只是官方的微調)\n{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Python: Attach\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;python\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;attach\u0026#34;, \u0026#34;connect\u0026#34;: { \u0026#34;host\u0026#34;: \u0026#34;localhost\u0026#34;, \u0026#34;port\u0026#34;: 5678 }, } ] } 執行 debug 要開始 debug 時，依照官方文件的說明，\n我們可以下以下指令\npython -m debugpy --listen 5678 --wait-for-client 上面 5678 就是配合 launch.json 所寫的\n參考：https://code.visualstudio.com/docs/python/debugging Reference 官方文件：https://code.visualstudio.com/docs/python/debugging ","date":"2023-11-20T22:18:19+08:00","image":"https://wongwongnotes.com/images/restored/2023/11/%E6%88%AA%E5%9C%96-2023-11-20-%E6%99%9A%E4%B8%8A10.16.26.png","permalink":"https://wongwongnotes.com/posts/python/concepts/python/python-debugpy/","tags":["Python","自用腳本"],"title":"【Python 自用腳本 #5】python attach - 使用 debugpy 在 VScode 中實現 debug container 內程式的效果"},{"categories":["441 - VScode"],"content":"前言 python 排版有幾個常用的規範或常見的 formatter\n這邊可參考\nhttps://code.visualstudio.com/docs/python/formatting 透過 extension 幫助我們自動排版，存檔時自動排的好看好閱讀好方便！\nextension extension 裡面找這個安裝\n我自己習慣用 black，但更常用的應該是 pep8，特別是在大部分的企業中(?)\nblack 規定比較囉唆，但彈性小對我來說是好事。\n那用 black 的原因\u0026hellip; 改天在寫，有興趣自己研究\n可參考\nhttps://zhuanlan.zhihu.com/p/203307235 裝好後去 Perferences -\u0026gt; Settings，老樣子我們從右上角直接去改 settings.json，\n加上下面這一段，其實下載底下的說明裡也有。\n\u0026#34;[python]\u0026#34;: { \u0026#34;editor.defaultFormatter\u0026#34;: \u0026#34;ms-python.black-formatter\u0026#34;, \u0026#34;editor.formatOnSave\u0026#34;: true } 兩個功能會被開啟，一個是指定 black formatter，另外一個是於手動儲存時排版。\n","date":"2023-11-19T00:26:54+08:00","image":"https://wongwongnotes.com/images/restored/2023/11/%E6%88%AA%E5%9C%96-2023-11-19-%E5%87%8C%E6%99%A812.21.06.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-black-python/","tags":["VSCode"],"title":"【VScode #5】在 VSCode 實現讓 Python 自動排版的功能 (pep8, black, ...)"},{"categories":["340 - Linux 系統控制"],"content":"前言 我們可以用 du 來快速搜尋檔案的大小、df 來觀察硬碟容量\n範例 du - 查看檔案, 資料夾大小 du -sh ./* s: summary, 如果沒下這個會全部底下的檔案遞迴顯示 (看用途)\nh: human readable, 顯示 GB, MB\u0026hellip; 人比較看得懂\n./* 表示只有這一層，配合 -s 可以看出資料夾的大小\n查看隱藏檔 (全部檔案) du -ah ./* a 就是 all，所以隱藏檔都會顯示，但全部顯示太多了，\n我們也許有時候只想看隱藏資料夾，就用下面\n查看隱藏檔 (單層，含隱藏資料夾) du -sh $(ls -A) ls -A (注意大寫)，可以把這一層目錄都以路徑的方式顯示出來，\n配合前面的 du -sh，就可以達到顯示這一層，包含全部隱藏資料夾的檔案大小。\ndf - 查看所有目前硬碟的大小、使用量、所剩空間 df -h ","date":"2023-11-16T21:51:25+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-du/","tags":["Linux","系統控制"],"title":"【Linux 系統控制 #10】du, df - 查看檔案大小, 硬碟大小與所剩容量"},{"categories":["320 - Linux 搜尋內容"],"content":"前言 以搜尋檔案而言 (不包含檔案內容)，最好用的應該就是 find 指令了，\n透過搭配 exec，還可以多執行一些額外功能\n範例 基本搜尋 find ./ -name \u0026#34;test.txt\u0026#34; 檔案名稱可以用正規表達式，例如 *.txt 就是搜尋所有的 txt 檔案\n而 ./ 可以是相對或絕對路徑，\n搜尋檔案，並顯示絕對路徑 find ~+ -name \u0026#34;*.txt\u0026#34; ~+ 是一種表示式，總之可以讓結果顯示為絕對路徑\n搜尋檔案，並刪除 find ./ -name \u0026#34;*.txt\u0026#34; -delete 小心使用，刪了基本找不回來，等同於 rm 的效果\n搜尋檔案，並執行特定指令 find ./ -name \u0026#34;*.txt\u0026#34; -exec ls -l {} \\; 這裡表示對搜尋結果執行 ls -l，{} 可以取代前面的結果\n-exec 就是執行後面的指令，需要搭配 ; 結尾表示指令結束 -execdir 如果不要完整的相對路徑，只需要最後的檔名可以用這個，經常用於對於大量資料夾檔案的單一檔名替換 ","date":"2023-11-16T20:51:55+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-find/","tags":["Linux","搜尋內容"],"title":"【Linux 搜尋內容 #5】find - 搜尋檔案名稱，並進行進一步處理 (顯示相對路徑、絕對路徑、刪除、重新命名、執行特定指令)"},{"categories":["432 - tmux"],"content":"前言 tmux 搜尋功能紀錄\n搜尋 進入讀取模式 先進入讀取模式，可以用 Ctrl+b [\n搜尋 Ctrl+s (記法： search)\n向上/下搜尋 n: 向下搜尋 shift+n: 向上搜尋 Reference https://superuser.com/questions/231002/how-can-i-search-within-the-output-buffer-of-a-tmux-shell https://blog.karmacomputing.co.uk/how-to-search-backwards-on-tmux/ ","date":"2023-11-16T20:42:54+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/tmux/tmux-search/","tags":[],"title":"【tmux #4】tmux search - 對歷史紀錄進行搜尋"},{"categories":["434 - ps1"],"content":"前言 PS1 全名為 Prompt String 1\n通常每次查 PS1 都會查到某遊戲機\n總之如果不喜歡安裝很多東西來裝飾自己的 teminal，PS1 提供了非常輕量化的方式，\n只要輸入指令就可以直接完成裝飾了！\n幾個範例 以下製作為 script，可以放在 ~/.bashrc 裡面讓每次啟動終端機時自動執行 script\n#!/bin/bash export PS1=\u0026#39;🐧 \\[\\e[38;5;228;1m\\]\\T\\[\\e[0m\\] | \\[\\e[38;5;177;1m\\]\\u\\[\\e[0m\\] at \\[\\e[38;5;208;1m\\]\\h\\[\\e[0m\\] in \\[\\e[38;5;156;1m\\]\\w\\[\\e[0m\\] \\[\\e[38;5;159;1m\\]$(git branch 2\u0026gt;/dev/null | grep \u0026#39;\u0026#34;\u0026#39;\u0026#34;\u0026#39;*\u0026#39;\u0026#34;\u0026#39;\u0026#34;\u0026#39; | colrm 1 2) \\[\\e[0m\\]\u0026gt; \u0026#39; #!/bin/bash export PS1=\u0026#39;👻 \\[\\e[38;5;228;1m\\]\\T\\[\\e[0m\\] | \\[\\e[38;5;177;1m\\]\\u\\[\\e[0m\\] at \\[\\e[38;5;208;1m\\]\\h\\[\\e[0m\\] in \\[\\e[38;5;156;1m\\]\\w\\[\\e[0m\\] \\[\\e[38;5;159;1m\\]$(git branch 2\u0026gt;/dev/null | grep \u0026#39;\u0026#34;\u0026#39;\u0026#34;\u0026#39;*\u0026#39;\u0026#34;\u0026#39;\u0026#34;\u0026#39; | colrm 1 2) \\[\\e[0m\\]\u0026gt; \u0026#39; #!/bin/bash export PS1=\u0026#39;🐍 \\[\\e[38;5;228;1m\\]\\T\\[\\e[0m\\] | \\[\\e[38;5;177;1m\\]\\u\\[\\e[0m\\] at \\[\\e[38;5;208;1m\\]\\h\\[\\e[0m\\] in \\[\\e[38;5;156;1m\\]\\w\\[\\e[0m\\] \\[\\e[38;5;159;1m\\]$(git branch 2\u0026gt;/dev/null | grep \u0026#39;\u0026#34;\u0026#39;\u0026#34;\u0026#39;*\u0026#39;\u0026#34;\u0026#39;\u0026#34;\u0026#39; | colrm 1 2) \\[\\e[0m\\]\u0026gt; \u0026#39; 好用網站 自己如果想自製，也很推薦使用網路上的 bash prompt generator，\n相關的功能都已經視覺化做完了，只要視覺化的拉一拉就好\nhttps://robotmoon.com/bash-prompt-generator/ 裡面還有參考的代稱 - https://bash-prompt-generator.org/ https://ezprompt.net/ https://tecadmin.net/how-to-customize-bash-prompt-ps1-in-linux/ Reference https://www.maxivanov.io/add-docker-container-name-to-shell-prompt/ ","date":"2023-11-16T20:38:04+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/ps/ps1/","tags":["ps1"],"title":"【PS1】使用 ps1 裝飾自己的 terminal 吧！"},{"categories":["921 - iphone / iOS"],"content":"前言 IPhone 轉移並不是所有 app 與資料都無痛轉移，\n這裡留下一些筆記，是我在 iPhone 換機時，轉移資料還需要手動處理的部分。\n注意事項 1. 所有綁定手機類的要注意!!!! 綁定手機就代表他換機會需要從「原來的手機」設定才能夠順利轉換， 新的手機就算有轉移，打開 app 也會像是全新的 !!!\n例如：郵保鏢、微軟的 authenticator\n郵保鏢 之前有次太早恢復舊手機回原廠，他說要用舊機才能轉移認證，直接沒救\n(因為舊手機資料已經被清掉)\n微軟的 authenticator 另外還有微軟的 authenticator，他會要求使用「可以進行認證的 authenticator」，\n啊這個只有在舊手機才有，新手機要重新認證，如果舊手機資料清掉了，會很麻煩！！！\n2. 所有銀行類、金融類、證券類 app 都需要重登， 不會是換機後還可以照常用直接解鎖 換句話說銀行類要求重登也是種保險，不要以為 face ID 會直接被沿用過來！\n全部都要重新登入\n3. apple watch 記得要從舊手機登出！！！ 再去綁新手機 (不能直接轉) 如果不透過從舊手機登出的方式，那就會需要直接重置 Apple Watch\n會建議從舊手機登出啦\u0026hellip; 手錶重製等於資料都不見 (雖然登出好像也差不多)\n4. 遊戲類 app 有帳號的可能也要注意，可能帳號沒轉過來，打開 app 跟新的一樣 遊戲類 app 有帳號的可能也要注意，可能帳號沒轉過來，\napp 跟新的一樣，帳號就不見了\n雖然現在大多遊戲還會有綁定自己的伺服器，但大多都是因為有綁定帳號，\n沒綁定帳號的要注意了！\n5. apple pay, line pay 有綁定的全部都需要重新註冊 這個應該也很好理解，就是卡片全部都要重新登錄、重新綁定，\n需要重綁定而不是直接轉移也好，防止被盜\n我自己的 Line Pay 就是因為這樣子，\n好幾次都刷顯示錯誤，後來發現重新綁定卡片就好了。\n6. 冷錢包之類的也記得都要重新登入 有在玩加密貨幣項目的應該會懂意思\n7. 外部 3C 有連動手機，綁定裝置的 app 像是會連動手機並且要綁定手機的體重機、體脂機之類的 app (歐姆龍、小米\u0026hellip;)\n結語 有發現其他的再繼續補充，反正就是紀錄一些並不是換機後，\n直接 apple 內建完全無痛轉移，\n還需要注意或留意的一些 app。\n","date":"2023-11-14T20:22:53+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/iphone-transfer/","tags":["iOS"],"title":"【iphone】iphone 換機轉移資料時注意事項 (apple 轉移不是所有 app 都無痛轉移)，一些還需要手動處理的部分"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n暖身 放鬆小腿 腿要用力往下壓，速度放慢\n放鬆背 滾筒滾背/伸展內縮\n拉伸背 兩手扶牆90度，身體往前推 (手肘貼牆壁)\n髖外展 膝蓋跪地 腳尖碰腳尖 身體往後坐 記得脊柱要伸直\n箱上深蹲 注意髖的伸縮跟重心的移動\n動作 彈力帶下拉 脊椎要伸直 手肘向前 拉的時候頭微後傾讓胸去找頭\ncable 下拉搭配髖內縮 注意移動方向需要跟脊椎同方向\n拉伸髖外展/內縮 大腿90度放在bench上，另外一側腳尖支撐髖伸展\n進階是另外一側手放膝蓋 做脊椎旋轉\n","date":"2023-11-08T15:07:48+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-33/","tags":["健身筆記"],"title":"【健身筆記 #33】2023/11/8 教練課33 - 彈力帶下拉, cable 下拉, 拉伸髖外展/內縮"},{"categories":["381 - Bash 基本語法"],"content":"前言 getopts 寫 script 作為參數傳入好用，改天再來寫 getopt，\n功能更強，但基本的 getopt 已經很好用了。\n使用 getopts 來處理 input options 範本 (帶參數) #!/bin/bash optstr=\u0026#34;:m:t:\u0026#34; while getopts $optstr opt; do case $opt in m) mode=$OPTARG ;; t) target=$OPTARG ;; \\?) echo \u0026#34;Invalid option: -$OPTARG\u0026#34; \u0026gt;\u0026amp;2; exit 1 ;; esac done # check non zero if [[ -z \u0026#34;$mode\u0026#34; ]]; then echo \u0026#34;Error: -m (mode) flag is required.\u0026#34; exit 1 fi if [[ -z \u0026#34;$target\u0026#34; ]]; then echo \u0026#34;Error: -t (target) flag is required.\u0026#34; exit 1 fi echo mode=$mode echo target=$target 說明 getopts 是 bash 內建處理參數的工具\n用法如上，而 optstr 就是表示有哪些短的選項 (options)\n-z: 表示非零 (有存到東西)，這邊多出來的實作就是必須要傳入參數。 使用 getopts 來處理 input options 範本 (不帶參數，純選項) 其實差別就只在有沒有跟一個冒號\n範例程式碼 #!/bin/bash function usage() { cat \u0026lt;\u0026lt;EOF This is a $0 options usage. options: -h help -v verbose EOF } VAR=0 while getopts \u0026#34;hv\u0026#34; OPTION do case \u0026#34;$OPTION\u0026#34; in h) usage exit;; v) VAR=1;; ?) usage exit 1;; esac done 結果 -h, -v 是有被我們設定的選項 ? 表示任意的其他選項 沒給選項則不反應 說明 相比範例一，其實只要注意 optstr 的處理不同即可，\nhv後面不帶「:」，表示是一個不存參數的選項\ncat EOF cat \u0026lt;\u0026lt;EOF ... EOF 用於處理 multi-text\nexit, exit 0, exit 1 另外 exit, exit 0, 預設是表示返回沒有異常的成功執行完 script\nexit 1 通常會被使用於有例外狀況的時候，作為後續直行程式的判斷\n使用位置變數 ($N) 來處理 input options 範本 #!/bin/bash function usage() { cat \u0026lt;\u0026lt;EOF Usage: $(basename \u0026#34;$0\u0026#34;) [options] Options: -a VALUE Description for option a. -b VALUE Description for option b. -c VALUE Description for option c. -h, --help Display this help and exit. --debug Enable debug mode. EOF } function parse_option() { if [ -n \u0026#34;$2\u0026#34; ]; then echo \u0026#34;$2\u0026#34; else echo \u0026#34;Error: Argument for $1 is missing\u0026#34; \u0026gt;\u0026amp;2 usage exit 1 fi } DEBUG=0 while [[ $# -gt 0 ]]; do case \u0026#34;$1\u0026#34; in -a) A=$(parse_option \u0026#34;$1\u0026#34; \u0026#34;$2\u0026#34;) shift 2 ;; -b) B=$(parse_option \u0026#34;$1\u0026#34; \u0026#34;$2\u0026#34;) shift 2 ;; -c) C=$(parse_option \u0026#34;$1\u0026#34; \u0026#34;$2\u0026#34;) shift 2 ;; -h|--help) usage exit ;; --debug) DEBUG=1 shift ;; --) # End of all options shift break ;; -*) echo \u0026#34;Unknown option $1\u0026#34; usage exit 1 ;; *) echo \u0026#34;Unexpected argument: $1\u0026#34; usage exit 1 ;; esac done echo \u0026#34;A=$A, B=$B, C=$C\u0026#34; echo \u0026#34;debug=$DEBUG\u0026#34; 說明 「;;」: case 使用，表示結束一個特定的 case 分支 「 $# -gt 0」:判斷還有沒有參數，搭配 while, gt = greater than, 因為還有 shift 的關係，因此此數字會慢慢減少到 0 「-n $2」: n 為 non-zero, 判斷 $2 是否存在, 上面的例子就是必須存在 (不然 argparse 沒存東西) Reference Cat","date":"2023-11-06T03:14:34+08:00","image":"https://wongwongnotes.com/images/restored/2023/11/%E6%88%AA%E5%9C%96-2023-11-06-%E5%87%8C%E6%99%A82.08.55.png","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/bash-input-options/","tags":["Bash","基本語法"],"title":"【Bash 基本語法 #7】bash input options, 類似 argparse 的 bash script 實作範例程式碼"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n髖伸展 抱住一個膝蓋, 另外一隻腳 90 度呈現翹二郎腿, 一手抱腿另外一手壓另外一腿, 放鬆髖部\n(臀在髖對側)\n臀伸展 坐在滾筒/球上 重心放在上面, 一手往後撐, 重心全部放在要做那側的臀部, 順著球/滾筒前後滾動\n槓鈴深蹲 - 注意後面的脊椎段, 頭要往上\n高腳杯深蹲 - 身體要往前傾, 重心要往前\n徒手肩推 (YW) - 手比讚往前畫圓\n啞鈴肩推 - 教練的手(負重)是往下的, 手的移動一定要抵抗負重, 不能掉 (保持垂直)\ncable下拉 - 手肘先動不是肩膀 (會聳肩)\n闊背肌伸展 - 兩手臂方形的起手式, 保持手肘的彎曲度, 手肘往上畫圈\n","date":"2023-10-04T23:47:10+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-29/","tags":["健身筆記"],"title":"【健身筆記 #29】2023/10/4 教練課29 - 髖伸展, 臀伸展, 槓鈴深蹲, 高腳杯深蹲, 啞鈴肩推, 闊背肌伸展"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 一直想要一個相機來紀錄生活，而富士相機的膠卷模擬功能我很喜歡！\n我又是個懶得後製的人，如果能夠相機直出就是好看的照片，就是我喜歡的相機！\n購入價 我是 2023/9/2 在 Fujifilm Wonder Photo Shop 買的，購入價 $43000 (選的是 15-45 的鏡頭套組)\n另外還有加購 保護鏡、富士藍芽腳架 跟 SD卡，這邊價格就不特別去找了\n取貨驗機 △ 相機驗機\n△ 帶走了！這家 Fujifilm Wonder Photo Shop 我很喜歡，是專賣富士的一家店，還可以租借鏡頭去試 開箱 △ 印有 Fujifilm Wonder Photo Shop 的專屬紙袋，與相機盒子本體 △ 這次買的全部內容\n△ 開箱第一眼，有說明書跟一些資訊\n△ 下面就是相機本體，跟鏡頭\n相機本體 △ 位於左邊的相機本體\n△ 本體就長這樣！非常的小巧好攜帶！\n△ 本體背面\n鏡頭與配件 △ 鏡頭與一些其他的配件\n△ 這個就是 15-45 的鏡頭了！\n△ 一些其他的配件，鏡頭保護鏡\n完整的拍 △ 接上鏡頭的樣子\n△ 拿掉鏡頭蓋\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n不過我應該是會很喜歡XDD，非專業使用，單純紀錄生活，\n覺得是跟手機比相對更高級一些的畫質選擇！\n","date":"2023-09-02T03:03:34+08:00","image":"https://wongwongnotes.com/images/restored/2023/09/IMG_5391-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/fujifilm-x-s20/","tags":[],"title":"【嗡嗡開箱 #21】富士相機 - FUJIFILM X-S20 開箱 | 不專業開箱文"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n槓鈴深蹲 （槓鈴擺在上斜方肌的位置，前置：WY，夾緊腋下)\n壺鈴肩推 （手臂支撐壺鈴，不能掉）\n腳推機\nCable 反向划船 （角度改變重量）\n","date":"2023-08-16T02:54:44+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-22/","tags":["健身筆記"],"title":"【健身筆記 #22】2023/8/16 教練課22 - 槓鈴深蹲、壺鈴肩推、腳推機、Cable 反向划船"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 因為前一代的作品《曠野之息》非常喜歡，這次果斷就入手了很有紀念價值的豪華盒裝版了，\n結果讓我一路等玩等到了八月，這段期間還真的難熬啊！\n購入價 我是 2023/5/12 在 普雷伊 預購的，購入價 $3340\n人家都開始玩了，就我還在預購QQ\n最後是 8/10 才到貨，要等別人都玩三個月才拿到遊戲\u0026hellip;.\n真不容易啊！\n開箱 △ 外盒正面\n△ 外盒側面\n△ 打開內容物有好幾層呢！\n△ 全部拉出來大概的樣子\n磁碟海報的部分 △ 磁碟海報的部分\n△ 海報正面\n△ 背面就一個強力磁鐵\n海報的部分就這樣，其實雖然精緻但好像又有點空虛的感覺\u0026hellip;\n遊戲本體與金屬特製外盒 △ 遊戲本體\n△ 底下多放了一個金屬特製外盒\n金屬特製外盒 △ 金屬特製的遊戲外盒，有比較精緻的感覺\n△ 打開來就跟一般遊戲盒一樣了\n遊戲本體 △ 遊戲本體正面\n△ 遊戲本體背面\n△ 遊戲本體裡面，應該大家都看過了\n徽章 △ 豪華版附贈的徽章盒\n△ 徽章分別代表中文四個字，看得出來嗎？ (風、火、水、雷) 難得中文玩家能直接看得懂 XD 畫冊 △ 盒子最底下就是畫冊了\n△ 用 10 元比較一下厚度，那是真的滿厚的\n△ 怕可能有版權問題，稍微截個幾張大概的，真的很用心\n△ 可愛的呀哈哈與這次他的背包\n心得 聽說最近剛發售馬上就掉價，那我之前買貴了QQQQ\n我在猜可能是想玩的早買了，所以這個含遊戲就多了，\n而且太晚發售了，想玩要忍到現在真的太痛苦。\n至於 cp 值我覺得還不錯，但裡面東西是否實用就見仁見智了XD\n","date":"2023-08-10T03:29:21+08:00","image":"https://wongwongnotes.com/images/restored/2023/09/IMG_5132-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/the-legend-of-zelda-tears-of-the-kingdom/","tags":[],"title":"【嗡嗡開箱 #20】任天堂 - NS Switch 薩爾達傳說 王國之淚-豪華盒裝版 | 不專業開箱文"},{"categories":["931 - 健身筆記"],"content":"前言 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n橋式（手緊貼地板，腳踩穩）\n鳥狗式（頭向前，貼水管，背部與胸都要有支撐，四足跪姿，練肩伸穩定度，顛腳尖、膝蓋離地）\n徒手硬舉（手向外展，腋下夾緊，背跟胸椎要有力量對抗）\n硬舉 （硬舉水管，直上直下、折斷的感覺：手肘旋轉面向前，肩膀外展）\n划船機（一開始手伸直，背跟胸椎要有力量對抗，肩伸+肘曲） 肩伸（身體大字型伸展，拳頭往側邊）/曲\n肘伸/曲 有氧\n30分/坡8/速5/130（太低加速、呼吸跟不上減速）\n比讚往外展、手臂抬高、注意腋下張力\n","date":"2023-08-08T02:52:59+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-21/","tags":["健身筆記"],"title":"【健身筆記 #21】2023/8/8 教練課21 - 橋式、鳥狗式、徒手硬舉、硬舉、划船機"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n常時在家訓練動作：\n注意中立位\n死蟲式\n側棒式：髖外展不是旋轉，腋下快抽筋就是正確\n橋式：手支撐地板\n坐姿支撐抬腿：手伸直、手貼箱側，抬腿頂教練手\n徒手深蹲：手往外類似V但不要內夾\n箱上蹲：坐下去就是不對，沒有支撐\n站立式Y伸展：手往前不是往外\n","date":"2023-08-01T02:46:30+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-19/","tags":["健身筆記"],"title":"【健身筆記 #19】2023/8/1 教練課19 - (常時在家訓練動作)、死蟲式、側棒式、橋式、坐姿支撐抬腿、徒手深蹲、箱上蹲、站立式Y伸展"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n死蟲式\n超人式 抬腳\nWY\n彈力帶下拉\n不穩定藥球 cable下拉\n椅子下拉\n跨步蹲：後腳出力 前腳支撐\n","date":"2023-07-06T02:29:54+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-14/","tags":["健身筆記"],"title":"【健身筆記 #14】2023/7/6 教練課14 - 死蟲式、超人式、WY、彈力帶下拉、cable 下拉、跨步蹲"},{"categories":["017 - 修網路"],"content":"前言 修理我家裡壞掉網路的筆記\n檢查 router WAN 出現紅燈，重插 WAN 無效\n檢查 switch 因為 WAN 紅燈，所以檢查看看是不是大樓那邊出問題，\n換了一個插孔\n回來檢查 router 因為 WAN 重插、並「系統重新開機」後，WAN 紅燈消失，\n原本以為網路就這樣好了\u0026hellip; 但還是連不上\n但！\ngoogle 搜尋依然有用！ 只是其他網站連不上 (或一直轉)\n更換 DNS 從上面判斷可能是 DNS 導致的讀取網頁速度過慢，\n從預設 DNS 更換成 google DNS (更換於 router 內)，\n換成 8.8.8.8, 8.8.4.4\nrouter 重置 更換後，狀況一樣沒有明顯變化，\n但 google 搜尋依然有用，只是連不上其他網站 (轉很久沒東西)，\n沒想法了，只好最後手段「重製 router」，\n剛重置完，依然狀況沒變\n更新 router 韌體 因為重製，要更新韌體，重開機後，順利解決，(也順便重製 wifi ID 跟密碼)\n不確定具體原因，但解決了，這裡留下筆記作為以後參考。\n","date":"2023-06-23T02:56:08+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/networking-repair/fix-internet/","tags":["修網路","嗡嗡粗門玩","實境解謎"],"title":"【技術雜記】修理家中網路，過程筆記"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n複習：死蟲\n上身負重死蟲 雙手舉壺鈴，卷腹，雙手做 90\n硬舉 核心：沒出力\n背、胸：沒張力，沒挺起來\nWY 頭：面朝下、頭頂超前\n伸展深蹲 髖不要外展，全身都要有張力\n","date":"2023-06-12T23:46:30+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-11/","tags":["健身筆記"],"title":"【健身筆記 #11】2023/6/12 教練課11 - 複習：死蟲、上身負重死蟲、硬舉、WY、伸展深蹲"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n複習核心：死蟲式 可以開始多一點卷腹了，記得頭\n複習背：貓牛式 注意「拱背 (向下彎)」、「卷腹 (向內卷)」\n會有一種抬屁股的感覺\n複習腿：負重深蹲 注意重心在前！！(腳尖的重心)\n注意胸椎與頭的排列！\n(髖) 負重走路 訓練重心交換的感覺，前腳「伸直」，後腳出力\n(髖) 負重抬腿 (壺鈴, 彈力帶) 前腳「伸直」支撐，感受髖部的啟動，後腳出力，\n注意頭「向前」\n(髖) 壺鈴硬舉 與深蹲不同，\n深蹲：屁股往下\n硬舉：屁股往後 (膝蓋只需要微彎去啟動)\n用竿子矯正姿勢 如果還不習慣髖的運動的話，可以用竿子輔助來矯正姿勢，\n手擺在後側脖子與腰的位置，反手握竿，\n練習讓背彎曲時，同時身體貼竿子 (注意頭、下巴)，\n練習「用屁股推的感覺」\n壺鈴後握來矯正姿勢 一樣，這次把壺鈴放到後面，練習「用屁股推的感覺」\n","date":"2023-06-04T12:13:02+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-08/","tags":["健身筆記"],"title":"【健身筆記 #8】2023/6/3 教練課08 - 複習：死蟲式, 貓牛式, 負重深蹲 / 負重走路, 負重抬腿 (壺鈴, 彈力帶), 壺鈴硬舉"},{"categories":["198 - Python 問題解決"],"content":"前言 這是我自己在寫程式碼的時候，碰到以下問題的解決辦法\nTypeError: can\u0026#39;t multiply sequence by non-int of type \u0026#39;str\u0026#39; 問題原因 其實很簡單，應該是不小心拿了非數字的東西進行相乘，\n而更常見的情況就是，我們可能讀取了 \u0026ldquo;string type 的數字\u0026rdquo;，\n例如說我們從伺服器或從哪裡的 API 拿回資料時，很常都還會是 string type，\n這時我們應該進行轉型， 例如 int(\u0026ldquo;10\u0026rdquo;)，就可以當 10 計算了！\n解決方法 上面把原因講得很清楚了，所以以下以一個範例帶過\nReference 【python】算术运算报错can\u0026rsquo;t multiply sequence by non-int of type \u0026lsquo;float\u0026rsquo; ","date":"2023-05-27T23:47:58+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/%E6%88%AA%E5%9C%96-2023-05-27-%E4%B8%8B%E5%8D%8811.46.32.png","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/typeerror-cant-multiply-sequence-by-non-int-of-type-str/","tags":["Python"],"title":"【Python】問題解決：TypeError: can't multiply sequence by non-int of type 'str'"},{"categories":["922 - Mac / MacOS"],"content":"前言 在舊版的 macbook 中，讓人又愛又恨的就是 touchbar\u0026hellip;\n因為壞了就直接失去了 ESC 與 F1 - F12 的功能，\n又特別是工程師 ESC 鍵常用的跟命一樣重要！！！\n這邊紀錄一下我臨時找到的解決方案，當然修 touchbar 太貴是一點，\n另外是這只是救急用的，買外接鍵盤個人覺得是更好的解法！\n這篇就是個外出可以應急用的方案，外接鍵盤絕對是更好的解！\nKarabiner-Elements step 1. 下載 Karabiner-Elements 這邊我們就去下載鍵盤映射工具，連結在底下\nKarabiner-Elements step 2. 在隱私權與安全性中，把權限開啟 這邊我們把權限開啟，這樣才能去改系統的鍵盤映射。\nstep 3. 更改鍵盤設定 這裡就可以來思考該把哪個按鍵修改對應按鍵了！\n我自己是改右邊的 option，因為我要用 option 幾乎都只會用左邊，\n雖然右邊的 option 位置比原來的 ESC 位置鳥了一些，\n但總比沒有按鍵用都好！！！ (這邊也說了，這只是應急用)\n這樣以後我們需要 ESC 鍵的時候，我們就可以直接使用 右邊的 option 代替囉！\nReference Karabiner-Elements ","date":"2023-05-27T23:35:25+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/%E6%88%AA%E5%9C%96-2023-05-27-%E4%B8%8B%E5%8D%8811.25.18.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/mac-karabiner-elements/","tags":["macOS"],"title":"【Mac】macbook touchbar 壞了沒 ESC 用怎麼辦? 用 Karabiner-Elements 變更 macbook 的鍵盤映射！"},{"categories":["421 - Dockerfile"],"content":"前言 dockerfile 算是用來「文件」式的管理環境最好用的方法，\n當然我自己之前也用過 docker image 來管理環境過，\n但畢竟一個是「文字檔」，一個是「image」自然檔案大小也差了 N 倍，\n這次把握了一點時間來學習並整理一下 dockerfile 該怎麼寫！\n以下依照「常見的撰寫 dockerfile 順序排列」\n變數介紹 FROM 通常寫在最開頭，如果不想完全從 0 開始建立，\n可以透過 FROM 表示要從哪一個已經有的 image，去疊加自己的東西\nFROM ubuntu:20.04 ARGS 用來設定一些變數，經常與 ENV 搭配使用\nARGS MY_PATH=\u0026#34;/mypath/\u0026#34; ENV 設定環境變數，也就是說，當 container 經由 dockerfile 建立的時候，\n會一開始就設定好的環境變數。\n搭配 ARGS 使用可能會長這樣\n注意與 ARGS 差別就是在，「ARGS 沒有設定環境變數，只有設定變數而已」\nARGS MY_PATH=\u0026#34;/mypath/\u0026#34; ENV MY_HOME=\u0026#34;${MY_PATH}\u0026#34; RUN 建立 container 階段就執行的指令\n常見的安裝\nRUN apt-get update -y RUN apt-get upgrade -y 或例如我一開始就想安裝 python3 的 numpy package\nRUN pip3 install numpy COPY 因為 container 內部的檔案可能與外面的 Host (也就是你本身的電腦)獨立，\n我們可以在建立階段就從外部 copy 一份資料進來，\n這樣就不怕 container 內拿不到外面的檔案\n如果不懂概念的話，可以想像 container 是電腦中的電腦，但兩者資料存放的位置沒辦法互相溝通。\nCOPY \u0026lt;外部路徑\u0026gt; \u0026lt;目標 continer 內的檔案路徑\u0026gt; CMD RUN 跟 CMD 感覺很像，也很容易弄混，但其實搞清楚很簡單，\n思考下面的關係\ndockerfile -\u0026gt; docker image -\u0026gt; container\n從 dockerfile -\u0026gt; docker image，是 docker build，也就是 RUN 作用的時間 從 docker image -\u0026gt; container，是 docker run，也就是 CMD 作用的時間 (optional) MAINTAINER 就是作個紀錄，紀錄誰在維護這份檔案\nMAINTAINER Howard Weng 隨便寫一個 ubuntu 20.04 的範例 建立 Dockerfile (注意大小寫) 預設會要建立一個乾淨的資料夾，\n在裡面放上一個檔案名稱為 Dockerfile (注意大小寫)\n然後這邊我們就單純直接去拉乾淨的別人寫好的 image\nFROM ubuntu:20.04 docker build 在同一個資料夾下，去建立一個新的 docker image\ndocker build -t ubuntu_2004 . --no-cache 就完成了，結果類似以下這樣，我們建立了一個新的 docker image\nReference Docker Dockerfile Dockerfile中run、cmd和entrypoint之間的區別 Difference between RUN and CMD in a Dockerfile opencpu/ubuntu-20.04 Day5: 實作撰寫第一個 Dockerfile ","date":"2023-05-26T15:03:34+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/%E6%88%AA%E5%9C%96-2023-05-26-%E4%B8%8B%E5%8D%883.18.18.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/dockerfile/docker-dockerfile-1/","tags":["Docker","Dockerfile"],"title":"【Dockerfile #1】撰寫第一個自己的 Dockerfile ! dockerfile 學習整理筆記"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n(背舒緩) 滾筒上背, 前後 \u0026amp; 上下 [embed]https://www.youtube.com/watch?v=7QA6hof5BgA\u0026amp;ab_channel=SonyaLoPilates%26Posture[/embed]\n(背) 貓牛式 (\u0026amp;分解動作) 分解動作 先用雙手撐住，不作手部，\n屁股往後往上抬高\n(核心複習) 死蟲式 常常會有沒收下巴的問題\n分解動作 改成手腳分開做\n(背複習) 划船機 常常會有沒收下巴的問題，\n重點一樣在維持胸與頭成一直線，\n現在階段還沒有要感受背\n(注意單純手部彎曲，記得手肘往腰靠近)\n","date":"2023-05-25T01:18:24+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-06/","tags":["健身筆記"],"title":"【健身筆記 #6】2023/5/24 教練課06 - (核心複習) 死蟲式 (\u0026分解動作), (背複習) 划船機 / (背舒緩) 滾筒上背, 前後 \u0026 上下, (背) 貓牛式 (\u0026分解動作), (背伸展) 用椅子支撐"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n複習一: (核心) 死蟲式 忽略的點： 想像雙手與雙腳向內側抵抗教練的手，保持用力的狀態 可以的話，可以做小卷腹 (臀、腿) 深蹲 上半身只作為支撐，主要練腿、臀\n[embed]https://www.youtube.com/watch?v=EzDxwv3wASY\u0026amp;ab_channel=GladysFitLife[/embed]\n起始動作 雙腳開的位置，大約是「肩膀向下對齊」雙腳的內側 上半身要保持直線 一樣要保持腹內壓 手：手肘向內縮緊，要有夾緊腋下的感覺 動作中 呼吸：向下時吸氣 注意腋下要保持夾緊 注意雙腳不要越做越往外開 注意事項 注意是髖部去驅動膝蓋導致全身向下，不要「膝蓋先向下」 怎麼找到髖部，可以用手插腰去感受一下\n(臀、腿) 負重深蹲 [embed]https://youtu.be/7dT8bFGy5V0?t=294[/embed]\n雙手拿壺鈴，一樣要注意動作中要保持腋下夾緊，不可以向外\n(背肌) 划船機 [embed]https://www.youtube.com/watch?v=JC1CTwLZYvE\u0026amp;ab_channel=%E6%85%95%E8%B0%B7%E5%81%A5%E8%BA%AB%E6%88%BFMukuGym[/embed]\n坐穩，「注意腳一定要踩穩」，\n挺胸，要注意胸部會有把頭往上撐的感覺\n","date":"2023-05-20T02:17:02+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-04/","tags":["健身筆記"],"title":"【健身筆記 #4】2023/5/18 教練課04 - (核心複習) 死蟲式 / (臀、腿) 深蹲、負重深蹲、(背肌) 划船機"},{"categories":["413 - Git 遠端互動"],"content":"前言 這篇要記錄一下，我有時候會有開發在 local，\n而實際運作在遠端伺服器的情況，\n因此常常會需要進行 git push/pull，\n這篇簡單紀錄一下，我是怎麼處理這個問題。\n實現方法 我主要實現的位置分別在 mac, 與我遠端的 rpi3 上，\n不過步驟基本上大同小異，\n因為我懶，如果只需要第一次打密碼後有記憶就太好了！\nmac 在第一次登入後，這個憑證就會被保存下來，這樣就可以不用一直重新輸入\n(但也需要自行留意安全問題。)\ngit config --global credential.helper osxkeychain linux (rpi3) 我在 rpi3 使用以下指令，一樣就可以做到保存憑證的效果，\n之後 git 與 remote 操作不需要重新輸入密碼。\ngit config --global credential.helper store 檢查 git config --list 如果裡面有找到類似 credential.helper=store 或 credential.helper=osxkeychain，\n就表示目前已經有用憑證存儲器來儲存帳號和密碼。 (務必自己小心安全問題)\n","date":"2023-05-20T01:58:51+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-remote/git-push-pull-no-pwd/","tags":["Git","遠端互動"],"title":"【Git 遠端互動 #3】將 git 綁定裝置，進行無密碼 git push/pull 筆記"},{"categories":["921 - iphone / iOS"],"content":"前言 就有時候會想要把 google drive 的照片直接下載到 iphone 相簿裡面，\n然後就發現找不到我想要的選項，\n後來找到了，筆記一下。\n兩個步驟，只是要記得哪個按鍵 step 1. 右上角的，選擇傳送副本 step 2. 等一下後，就會找到儲存影像了 其他按鍵都沒辦法直接儲存到 iphone 相簿裡面，我都試過了。\n","date":"2023-05-17T23:34:01+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/img_2814-scaled.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/iphone-google-drive/","tags":["iOS"],"title":"【iphone】iphone 將 google 雲端硬碟 (google drive) 的照片下載至 iphone 相簿的筆記"},{"categories":["114 - Python 字串處理"],"content":"前言 我們會需要在字串中尋找特定子字串的功能，\n這裡我們使用 python 的 find 函數來幫助我們實現找子字串的功能\n範例程式碼 因為 find 功能相對單純，我們先直接來看例子，\n讀者可以自己觀察判斷看看能不能理解！\n可先觀察結果，下面再來慢慢說明 \u0026gt;\u0026gt;\u0026gt; main_str = \u0026#34;test_data\u0026#34; \u0026gt;\u0026gt;\u0026gt; print(main_str.find(\u0026#39;test\u0026#39;)) 0 \u0026gt;\u0026gt;\u0026gt; print(main_str.find(\u0026#39;data\u0026#39;)) 5 \u0026gt;\u0026gt;\u0026gt; print(main_str.find(\u0026#39;hello\u0026#39;)) -1 str.find(\u0026rsquo;test\u0026rsquo;) 會回傳找到的子字串所在的 「index 起始位置 (從 0 開始)」\n如果沒有會回傳 -1\n就是這樣，非常簡單XD\n應用 其實最重要的還是如何把上面的內容應用到自己的程式上，\n例如以下我舉一個我自己最常用的例子：\n如果找到「特定字串」，做 A 事情，如果沒有找到，做 B 事情。 main_str = \u0026#34;test_data\u0026#34; str_founded = main_str.find(\u0026#39;test\u0026#39;) if(str_founded == -1): # not found print(\u0026#34;DO if NOT found.\u0026#34;) else: # found print(\u0026#34;DO if found.\u0026#34;) Reference Python find()方法 ","date":"2023-05-17T11:58:42+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/string-find-python/","tags":["Python","字串處理"],"title":"【Python 字串處理 #6】python find 使用方法 — 在字串中找尋子字串"},{"categories":["843 - UML"],"content":"前言 UML 中的 class diagram 真的超常用，\n然後用到後來，箭頭、符號都亂用\u0026hellip;\n趁這個機會來整理一下 class diagram.\n箭頭的方向 箭頭的方向其實有明確的定義，\n簡單來說我們可以理解為「被箭頭指向方，通常都是比較大的角色」\n例如： (這裡的箭頭只有示意用)\n子 -\u0026gt; 父 個人 -\u0026gt; 群體 貓 -\u0026gt; 動物 箭頭的種類 箭頭種類一共用六種，然後我相信讀者可能跟我一樣只看英文或中文也完全看不懂差別 ? _ ?，\n所以我下面再來舉例，經典表格還是要上。\n我們先稍微給予一個讀者一個大概念，\n基本上箭頭的種類不同，只有「概念上強弱」的差別，並不一定錯誤\n聽不懂意思我這邊舉例：\n我可以說：\n我跟父親有關係 我跟父親有父子關係 上面兩種，上面的類似圖上的「關聯」，\n而下面就是類似圖上的「繼承」，\n「強化了雙方的關係，但沒有表示用比較不強烈的表示法是錯誤的」\n弄懂後，當然如果可以我們一定是希望雙方的關係越明確越好，\n如果全部都是箭頭也是對的，不過表現起來，就好像「差了那麼一點細節」\n上面圖中的藍色箭頭也是在表示這樣的意思，「組合」、「聚合」為「關聯」的子集，\n但並不代表其他剩下的箭頭沒有類似關係。\n最普通的箭頭 - 「關聯 —〉」與「依賴 - -〉」 關聯就是最普通的表示兩人之間有關係的表示，箭頭也是最普通的實線箭頭，\n而如果有更弱的關係 (沒有也沒差)，我們則用虛線箭頭表示。\n只靠敘述太難懂了不如我們直接看例子吧。\n「關聯」：農作物 —\u003e 天氣 想要種植出農作物，必須要有好的天氣，但兩者之間沒有什麼特別的直接關聯。 「依賴」：我 - -\u003e 股票 我擁有股票，但我也可以沒有，也不會怎麼樣。(沒有也沒差的關係) 可能是最好懂的兩個箭頭 - 「繼承—▷」與「實作 - -▷」 這裡就不多解釋了，有一定程式基礎的一定都知道什麼是「繼承」，\n這樣子特別的關係，我們就會用「—▷」表示，\n而相對繼承關係。另外就會有介面的實作，我們就會用「- -▷」表示。\n舉例： mycat - -▷ cat —▷ animal\ncat 是 animal 底下的一種，這個就是一種繼承關係 cat, animal 都是集合名詞，不一定有非常「個體化的」定義，真正的有個體化「實作」是 Mycat 題外話一些專有名詞： (中文翻譯不知道是翻什麼)\ngeneralization: 繼承關係中，從底部往上看 (我的貓 -\u0026gt; 貓 -\u0026gt; 動物)，有種越來越 general 一般般(?) 的感覺，我的記法\u0026hellip;\nspecialization: 繼承關係中，從上看往底部看 (動物 -\u0026gt; 貓 -\u0026gt; 我的貓)，有種越來越 special (?) 的感覺，我的記法啦\u0026hellip;\n跟「合」有關係的兩個箭頭 - 「組合一⬥」與「聚合 一◇」 既然都是跟「合」有關係的，想必有存在一種組合的概念，\n只是文字上實在是太難懂了，一樣也直接看例子吧。\n「聚合」：人 —◇ 團體 、 學生 —◇ 教室 (大者不存在，小者也可獨立存在) 人聚集起來成為了團體 就算沒有了團體，人還是可以各自生存 「組合」：房間 —⬥ 房子 、 心臟 —⬥ 人 (大者不存在，小者也不會存在) 房子裡面有很多房間 但如果沒有了房子，也會沒有房間 Reference 14 種 UML 圖的綜合指南 UML 圖摘要 UML Association vs Aggregation vs Composition Java 大黑话讲解设计模式 \u0026ndash; UML类图 ","date":"2023-05-16T00:11:00+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/img_0044.jpg","permalink":"https://wongwongnotes.com/posts/cloud-iot/software-engineering/uml/class-diagram/","tags":["Python","UML"],"title":"【UML #2】class diagram, 最常用於理解程式中有哪些重要物件 (class, 類別), 與他們之間的相互關係"},{"categories":["843 - UML"],"content":"前言 UML 那堆箭頭我看了就頭痛，意義還幾乎超像，\n看完一堆文章後，我還是覺得可能這輩子很難真的搞清楚了\u0026hellip;\n但想要搞清楚，總是得先嘗試整理才行！\n所以就來整理了！\nUML 總共會有哪些圖? UML 總共有 14 種，不同的情況下他們各自有擅長的目標。\n可以參考\n14 種 UML 圖的綜合指南 UML 圖摘要 而其中我最常用的是\nclass diagram: 用於快速理解一個大型程式，有哪些重要物件 (class)，與他們之間相依的關係 sequence diagram: 用於理解程式執行的「流程」 在後續的文章在針對這兩個分別介紹。\nReference 14 種 UML 圖的綜合指南 UML 圖摘要 ","date":"2023-05-15T23:24:20+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/software-engineering/uml/uml-14/","tags":["Python","UML"],"title":"【UML #1】我也不知道我這輩子到底搞不搞的清楚的 UML - UML 到底有幾種圖? 我常用的又有哪些?"},{"categories":["112 - Python 進階語法"],"content":"前言 因為雙底線會被 markdown 文字吃掉，以下文章「雙底線、雙等號」請自行補上。\n這篇屬於 python 要準備實作更複雜功能的時候才會碰到的狀況，\n通常一個簡單的 python 腳本不會需要用到 name = main 這個敘述，\n我們可以直接撰寫並執行。\n這邊只寫到我要用到的地方，觀念可能不是很清楚，有錯歡迎指正。\n說明 不論是 name 或是 main，\n用雙引號的表示方式，在 python 中都是屬於一種特殊標誌，\n思考問題 (重要) 如果我們透過 import 另外一支 python 程式，\n那請問那個被 import 的 python 程式，\n內容會不會直接被執行呢？\n範例：請回答下面的 python 程式，在執行 main.py 後，會輸出什麼內容？ # main.py import import_module print(\u0026#34;This is main\u0026#34;) # import_module.py print(\u0026#34;This is import module\u0026#34;) 思考問題解答 這邊強烈建議自己想一次、並寫一次！！！！\n會特別有感覺，而且會更有印象\n答案是：\n想一下，應該滿合理的對吧！\n其實不一定哦！！！\n思考 - 我們才剛使用別人的 module? 怎麼還沒做事就直接印東西了呢? 當然，這有可能是寫法的關係，\n不過你想想，如果今天 import XXXX 時，\n我根本還沒想對他作什麼事情，他就先印一個東西給我，\n這樣其實很詭異欸！！！ 還可能不知道誰在搞鬼\n不過一般來說不會這樣用啦，只是要知道這訊息可能從哪來的\n一般來說我們都會定義一些 function，也因為這個特性，\n這時就直接載入，就會是正常的行為。\n另外一個範例 我們稍微修改一下第二支程式，問題來了，我們執行一樣的 main.py，\n這次會印什麼呢？\n# import_module.py if __name__ == \u0026#34;__main__\u0026#34;: print(\u0026#34;This is import module\u0026#34;) 這段程式基本上沒啥用\u0026hellip;也不會有人這樣寫\n但我不知道為什麼考試好像會考\u0026hellip;\n位於 import 的程式，main 是沒有作用的，\n重點只要知道「main」就是作為程式的開始點，在執行的當下會被呼叫一個而已。\n常用範例程式碼 雖然上面講了那麼多觀念，實際上我們使用大概是這樣：\ndef main(): pass if __name__ == \u0026#34;__main__\u0026#34;: main() Reference https://www.tutorialsteacher.com/python/main-in-python ","date":"2023-05-14T03:25:27+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-09-%E4%B8%8B%E5%8D%887.07.20.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-name-main/","tags":["Python","進階語法"],"title":"【Python 進階語法 #9】python __name__ == \"__main__\" 筆記，內附常考問題"},{"categories":["112 - Python 進階語法"],"content":"前言 進階篇的語法，python 新手來說可以完全先不用碰，\n(碰了可能有比較厲害，但更有可能觀念大亂)，\n大部分的功能只需要靠基礎篇的內容就能實現了！\n建議會先把基礎的都學會再來看哦！\nargs, kwargs 是慣用名，使用時，我們不太會只講這個部分，會連前面的符號一起講\n因此以下都請看作「*args」、「**kwargs」\n小提示 因為開始講到比較細的東西，用詞上我會更小心一些，\n注意以下 parameters 與 arguments 位置的差別，\n雖然翻譯幾乎相同也很常混用就是\u0026hellip;. 我自己之前也常講錯\ndef foo(parameters): pass foo(arguments) args 與 kwargs python 裡面我們傳參數 (arguments) 給 function 時，\n我們更仔細的看，其實在 arguments 我們就可以預先指定給傳給特定的 key，\n這個順序有個嚴格的排列，必須先 args 再 kwargs\nargs: 位置參數 (不指定 key 的都必須先放在前面，且順序很重要) kwargs: key word (故意分開表示 kw) arguments 關鍵字參數，只要擺比 args 後面即可。 當我們指定 keywords, 我們就可以無視順序的傳入參數給 function，\n但如果我們沒有指定，就是一律按照順序\nargs 思考問題 請問以下會印出什麼呢？(建議先想過再往下看，如果答對了基本上就已經理解囉！)\ndef my_function(*args): for arg in args: print(arg) my_function(1, 2, 3, 4) 答案 我們所傳入所有的 args，其實如果是以 *args 的方式去接這些變數，就會依照順序。\n(args 只是習慣，實際上可以替換成別的名稱)\nkwargs 思考問題 請問以下會印出什麼呢？(建議先想過再往下看，如果答對了基本上就已經理解囉！)\ndef my_function(**kwargs): for key, value in kwargs.items(): print(f\u0026#34;{key}: {value}\u0026#34;) my_function(a=1, b=2, c=3) 答案 **kwargs: 可以讓我們以 dict 的方式把所有帶有 key 的變數儲存，\n一樣 kwargs 只是慣用名，不一定要用這個名字。\n同時出現的 args 與 kwargs 我們從上面先分別了解兩者的功能後，當一個 function 出現類似\ndef func(*args, **kwargs): pass 表示「無論傳入任何的變數，這個 func 都能吃的進去」 (當然也先必須符合 args 在前， kwargs 在後的前提)\n用途 從上面的敘述中，我們可以發現這種方式給予我們在撰寫 func 的時候有最大的彈性，\n所以我們會使用與一些可能還不確定傳入內容，或本身就需要彈性的設計 (對外給客戶的 API \u0026hellip;)\n思考問題 請問以下會印出什麼呢？(建議先想過再往下看，如果答對了基本上就已經理解囉！)\ndef foo(*args, **kwargs): print(type(args)) print(type(kwargs)) for ele in args: print(ele) for k, v in kwargs.items(): print(f\u0026#34;{k}: {v}\u0026#34;) foo(1, 2, 3, a=4, b=5, c=6) 答案 進階討論：關於「*」這個符號 「*」 在 python 裡面有 unpack (展開) tuple (list 也可以) 的意思\n而兩個的「**」，是可以 unpack dict\n最後我的理解是\n我們可以直接把「*args」，傳入 func 後，扣掉符號所剩下的 「args」當成是 tuple (list)，\n而直接把「**kwargs」，傳入 func 後，扣掉符號所剩下的 「kwargs」當成是 dict\n這是我目前認為最容易理解的方式了。\n題外話的嘗試 所以也有以下的用法：\nmylist = [1, 2, 3] print(*mylist) 會印出 1 2 3 (針對每個元素各別印出)，\n這段程式碼可翻譯為 print(1, 2, 3)\n而我們沒辦法同樣的印出 print(**mydict)\n因為這段程式碼會被翻譯為 print(a=4, b=5, c=6)，\n而這種帶有 keyword 的 print 本來就不能印出東西來\nReference ","date":"2023-05-13T00:53:49+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/%E6%88%AA%E5%9C%96-2023-05-13-%E4%B8%8A%E5%8D%881.29.51.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-arg-kwargs/","tags":["Python","進階語法"],"title":"【Python 進階語法 #8】args? kwargs? 也許可以不用完整的講清楚傳了什麼東西給 func?"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n複習一: (核心) 屈膝卷腹 [embed]https://www.youtube.com/watch?v=cx9bfLJRHBY\u0026amp;ab_channel=ONLINEBODYTRAINER[/embed]\n忽略的點： 起始動作：腳假設地面是牆 (或想像用力踩教練腳)，踩穩後，腳貼著地面上移 腳不夠直：一腳屈膝踩穩時 (不可以被手搬動)，另外一腳應該也要踩穩 腹部卷太多：不是在核心「訓練」，只是要練習「保持腹部張力」 複習二: (核心) 側棒式 [embed]https://youtu.be/xu0sLJ0nv7I?t=166[/embed]\n忽略的點： 手：往後傾斜不可，應該要往上，另外一手注意角度 90 度 (注意腋下部分是有被訓練到的感覺，那才是運練目標) 手部面向前 一氣合成的完成抬起動作 腳一樣先伸直，記得要一直線，然後下方角再 90 度 屁股：一樣，往後傾斜不可，跟手一樣，應該要跟身體呈現一個面 複習三: (核心) 死蟲式 [embed]https://youtu.be/jQrXmr8s8-Y?t=59[/embed]\n忽略的點： 手：向上兩手相對 腳起始動作：腳假設地面是牆 (或想像用力踩教練腳)，踩穩後，腳貼著地面上移 腳、臀部：都要 90 度，記得前置動作 90-90 呼吸法 屁股：要有挺起來的感覺 (腳如果有伸直想像踩地板會自動做到) 動作過程： 手：伸直，只靠肩膀的移動 90 度 (不可以歪掉，同一平面) 腳：伸直收回，維持 90 度 (不可以歪掉，同一平面) (臀肌訓練) 橋式 [embed]https://youtu.be/fRvUXUPNO0I[/embed]\n起始動作： 手：向兩側伸直 腳：一樣先向下踩地板，後往上移動，可以往內一點 (等等上升膝蓋要 90 度) 動作過程： 用臀部發力：呈現膝蓋跟背呈現 90 後，保持呼吸節奏循環。 注意事項： 注意背部不要貼地板！！！ ","date":"2023-05-09T17:55:57+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-03/","tags":["健身筆記"],"title":"【健身筆記 #3】2023/5/8 教練課03 - (核心複習) 屈膝卷腹, 側棒式, 死蟲式 / (臀肌) 橋式"},{"categories":["112 - Python 進階語法"],"content":"前言 python 中有個內建函數為「zip」，\n他能夠快速的幫助我們將「有順序的多組內容」完成「一一對應的打包」\n學會 zip 在特定情況下可以省掉大量的程式撰寫時間，\n如果是初學者的話，沒有學會 zip 也能用相對比較複雜的方法實現一樣的功能，\n但學會後也能體會到 zip 存在的便利。\n比較 \u0026amp; 範例程式碼 zip 基本用法，將每一個元素依照位置一個個拉出來對齊 以下面例子而言，我們宣告 3 個 list a, b, c，\n我們如果使用 zip，\n就會將 a,b,c 依照順序抽出來，\n從第一組 (1, 4, 7) -\u0026gt; (2, 5, 8) -\u0026gt; (3, 6, 9)\n範例程式碼 - zip 基本用法 a = [1,2,3] b = [4,5,6] c = [7,8,9] print(zip(a,b,c)) # \u0026lt;zip object at 記憶體位置\u0026gt; print(list(zip(a,b,c))) # [(1, 4, 7), (2, 5, 8), (3, 6, 9)] 詳細說明 - zip 基本用法 python3 之後，zip 的結果會成為一個 object，\n如下我們印出「print(zip(a,b,c)) 」\n\u0026lt;zip object at 記憶體位置\u0026gt; 我們經常需要搭配「list」才能夠得到我們可以使用的結果，\n如下我們印出「print(list(zip(a,b,c))) 」\n[(1, 4, 7), (2, 5, 8), (3, 6, 9)] 如果遇到組合元素不相等？ 直接舉例，以下兩個 list 長度不相等。\n結果會是「以比較短的 list 為主的全部組合」\na = [1, 2, 3] b = [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;] zipped = list(zip(a, b)) print(zipped) # 輸出: [(1, \u0026#39;a\u0026#39;), (2, \u0026#39;b\u0026#39;)] zip 搭配矩陣用法 我們先宣告一個矩陣 matrix = [ [1,2,3], [4,5,6], [7,8,9] ] # matrix[::] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] print(f\u0026#34;{matrix[::] = }\u0026#34;) zip 搭配矩陣用法，將每一個元素依照位置一個個拉出來對齊 (1) 藉由「matrix[::]」我們可以將矩陣元素一個個拉出來，\n例如：「matrix[::] = [1, 2, 3] [4, 5, 6] [7, 8, 9]」\n(2) 由 zip 將所有的元素「一個一個照順序」對應組在一起，\n(1, 4, 7) -\u0026gt; (2, 5, 8) -\u0026gt; (3, 6, 9)，\n所有我們就能有一個全新的組合！\n(3) 但是我們還需要靠 list 才能夠將 zip 的結果顯示成我們看得懂的樣子，\n最後我們得到 list(zip(*matrix[::])) = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]\n# *matrix[::] = [1, 2, 3] [4, 5, 6] [7, 8, 9], print(*matrix[::]) # list(zip(*matrix[::])) = [(1, 4, 7), (2, 5, 8), (3, 6, 9)] print(f\u0026#34;{list(zip(*matrix[::])) = }\u0026#34;) 【極為實用】zip = 轉置矩陣應用，用 zip 快速實作轉置矩陣 基本上這點就是 zip 最實用的功能！透過 zip 作轉置矩陣就是個「秒殺等級」的事情！\n範例與 (2) 相同，注意橫列 [1,2,3] 被換成了直行 (1, 4, 7)，\n範例程式碼 - 使用 zip，做「轉置矩陣」 matrix = [ [1,2,3], [4,5,6], [7,8,9] ] # *matrix[::] = [1, 2, 3] [4, 5, 6] [7, 8, 9], print(*matrix[::]) # list(zip(*matrix[::])) = [(1, 4, 7), (2, 5, 8), (3, 6, 9)] print(f\u0026#34;{list(zip(*matrix[::])) = }\u0026#34;) 範例程式碼 - 如果不使用 zip，要「轉置矩陣」我們可能就需要這麼做 import copy matrix = [ [1,2,3], [4,5,6], [7,8,9] ] new_matrix = copy.deepcopy(matrix) # 複製一樣的大小，內容一樣沒關係，等等就換掉了 # (需要使用deepcopy，不然值被改掉後，我們會去取到錯誤被改過的值。) for y_idx in range(len(matrix)): for x_idx in range(len(matrix[0])): new_matrix[x_idx][y_idx] = matrix[y_idx][x_idx] print(new_matrix) 補充 - 矩陣旋轉 90 度 (也是 leetcode 第 48 題) 然後我們先進行上下交換，透過 zip 進行轉置，\n可以快速又炫的解完矩陣 「旋轉 90 度」 這個難題。\n(或者也可以用先透過 zip 進行轉置，再進行左右交換，\n也能實現「矩陣旋轉 90 度」的功能)\nmatrix = [ [1,2,3], [4,5,6], [7,8,9] ] # matrix[::-1] = [[7, 8, 9], [4, 5, 6], [1, 2, 3]] print(f\u0026#34;{matrix[::-1] = }\u0026#34;) # list(zip(*matrix[::-1])) = [(7, 4, 1), (8, 5, 2), (9, 6, 3)] print(f\u0026#34;{list(zip(*matrix[::-1])) = }\u0026#34;) matrix[::] = zip(*matrix[::-1]) # 題目需求：change in-place print(matrix) 關於這題 (leetcode 第 48 題)，詳細解說可以參考我的另外一篇文章：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-48/\nReference https://www.runoob.com/python/python-func-zip.html ","date":"2023-05-04T11:41:58+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-zip/","tags":["Python","進階語法"],"title":"【Python 進階語法 #7】python zip - 使用方法 與 其他寫法比較整理"},{"categories":["931 - 健身筆記"],"content":" 健身筆記系列是我自己去上教練課做的筆記,\n正因為我有去上課, 教練「針對個人細節的調整」我能保證這是絕對不可能只靠筆記就能學會的\n上完後會鼓勵初學者不熟就先去上課, 自己練很容易姿勢「很多細節」沒注意，\n不會說沒練到，但就是「事倍功半」的效果。(也浪費時間)\n啟動：(複習\u0026amp;矯正) 90-90躺姿呼吸訓練 [embed]https://www.youtube.com/watch?v=rmkpsseNEK8\u0026amp;ab_channel=PhysiomotionLab%E5%8B%95%E4%BD%9C%E5%AF%A6%E9%A9%97%E5%AE%A4[/embed]\n強調下巴的問題 下巴、頭：注意收下巴頭往前（相對地板的上）看 - 是脊椎用腹部啟動彎曲，不是叫你動脖子！！！ (維持腹內壓的感覺！吸氣吐氣都要撐住，不會吐氣就躺下) - 手：向下方出力 (不可以被外力搬動) 核心一: 屈膝卷腹 [embed]https://www.youtube.com/watch?v=cx9bfLJRHBY\u0026amp;ab_channel=ONLINEBODYTRAINER[/embed]\n新手變化: 手：雙手收背後面，成稍息狀態 (或角度不對改用插腰) 腳：單腳屈膝，踩穩地板 (不可以被手搬動) / 另外一隻腳伸直，腳尖朝上 (左右交換) 腹：一樣要維持腹內壓！ (維持腹內壓的感覺！吸氣吐氣都要撐住，不會吐氣就躺下) 組數 以呼吸算組數，新手左右各 5 組\n核心二: 側棒式 [embed]https://youtu.be/xu0sLJ0nv7I?t=166[/embed]\n新手變化: 手：支撐側向內夾緊腋下，有點45度的感覺，另外一側手向上伸直，注意不要前後傾倒 腳：支撐腳屈膝 90度 向後 / 另外一隻腳向下方伸展 腹：一樣要維持腹內壓！ (維持腹內壓的感覺！吸氣吐氣都要撐住，不會吐氣就躺下) 組數 以呼吸算組數，新手左右各 5 組\n核心三: 死蟲式 [embed]https://youtu.be/jQrXmr8s8-Y?t=59[/embed]\n手：兩手手心皆向內側，夾緊腋下 腳：想像兩腳夾一個滾筒 腹：一樣要維持腹內壓！ (維持腹內壓的感覺！吸氣吐氣都要撐住，不會吐氣就躺下) 如果一開始做不起來或手忙腳亂 一手與斜對側腳夾住一個滾動，只做單邊 組數 以呼吸算組數，新手左右各 5 組\n","date":"2023-05-03T01:28:48+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/fitness/fitness-02/","tags":["健身筆記"],"title":"【健身筆記 #2】2023/5/2 教練課02 - (啟動複習) 90-90躺姿呼吸訓練 / (核心) 屈膝卷腹, 側棒式, 死蟲式"},{"categories":["740 - 面試問題收集"],"content":"前言 這裡收集一些我從朋友身上聽到一些不錯的面試問題，\n第一次碰到可能會比較慌亂，不見得能漂亮的直接答出來，\n這裡紀錄一下。\n問題 請說明 tree, graph 的差異?\n參考回答 tree 跟 graph 確實很像，但我們可以當作 tree 是一種特化後的 graph，\ngraph 可以包含更多種可能，而 tree 可以專門處理特定的問題\ntree，會有 parent, child 的上下概念，graph 沒有 (每一個 node 同地位) tree，有向性 (有上下關係)，graph 不一定有向 (單向雙向都可以) tree，有 root，graph 沒有 root graph，可以有循環，tree 那是不可能的XD 總之，graph 能廣泛處理各種種類的問題，而 tree 是專門處理有著特定關係的問題。\n","date":"2023-04-29T12:21:53+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/interview-prep/740---%E9%9D%A2%E8%A9%A6%E5%95%8F%E9%A1%8C%E6%94%B6%E9%9B%86/interview-tree-graph/","tags":["Git","面試問題收集"],"title":"【面試準備 #1】經典問題收集 - 說明 tree, graph 的差異?"},{"categories":["415 - Git 備份還原"],"content":"前言 這篇要記錄一下，我自己開發時遇到的情況題，\n如果忘記事先切 branch，卻有新的 commit 該怎麼辦?\n在多人協作時，通常我們會希望每一個功能都提交到指定的 branch 上，\n因此當如果 commit 沒有在 branch 上卻有 commit，的確有點困擾啊\u0026hellip;.\n解決方法 其實只要一些操作就沒有什麼困難的！ 重點在活用「git stash」!\nstep 1. git stash 暫存此 commit git stash save \u0026#34;some msgs\u0026#34; step 2. 切換回你要更新的 branch git checkout \u0026lt;your_branch\u0026gt; step 3. apply 剛剛的 stash git stash apply # 不移除 stash # or git stash pop # 移除 stash step 4. 可以回到在該 branch 上 commit 的動作啦！ git commit -m \u0026#34;your msg\u0026#34; Reference Difference between git stash pop and git stash apply ","date":"2023-04-25T02:04:22+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-backup/git-forget-to-checkout-branch/","tags":["Git","備份還原"],"title":"【Git 備份還原 #3】git stash - 情況題：如果忘記切 branch，卻有新的 commit 該怎麼辦?"},{"categories":["415 - Git 備份還原"],"content":"前言 git tag，算是我自己認為在管理大版本號中最重要的技巧\n透過 git tag，我們可以快速定義出 「v1.0.0 這種版本號所代表的特定 commit」\nGit tag 的基本概念 git tag 正如其名，就是一個 tag，也就是標籤，\n當我們下載任何一個專案，都可以透過以下查看所有的標籤 (通常也會看到許多版本號)\ngit tag 建立 git tag 如果確定現在的版本就是一個定版，那很簡單的我們只需要下\ngit tag v1.0.0 這版就是 \u0026ldquo;v1.0.0\u0026rdquo; 了！\n範例 我們只要下上面的指令！\n就可以看到多一個標籤了！\nCheckout 到某個 tag 要 checkout 到某個 tag，只要 「git checkout \u0026lt;tag_name\u0026gt;」 就可以囉！\ngit checkout v1.0.0 這樣，我們就切換到了 \u0026ldquo;v1.0.0\u0026rdquo; 這個 tag (版本)。\n其實就是對應到了這個 commit，只是比 commit id 更好記憶與切換了！\nReference GIT 如何 CHECKOUT 某個TAG ","date":"2023-04-24T02:26:29+08:00","image":"https://wongwongnotes.com/images/restored/2023/04/%E6%88%AA%E5%9C%96-2023-04-25-%E4%B8%8A%E5%8D%882.22.45.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-backup/git-tag/","tags":["Git","備份還原"],"title":"【Git 備份還原 #2】git tag - 透過幫現有的 commit 加個標籤，進行快速版本切換"},{"categories":["192 - Python 測試程式"],"content":"前言 幫我在撰寫 python 的時候，總會有一些程式碼是比較沒有把握的，\n或者有機率性的會發生錯誤，\n這時候我就會選擇使用 try-except 的方式來迴避掉可能會發生的錯誤。\n範例 try: pass # 有可能會發生錯誤的程式，例如：讀取檔案找不到檔案.... except: pass # 當 try 失敗時，進行的例外處理 也可以進一步的把錯誤訊息拉出來 上面的只是一般的例外處理情況，當然基本上那是我們很有把握會出錯的情況符合我們的想像，\n因此我們可以針對意外發生時，去作對應的處置。\n但不是所有錯誤的狀況都會符合我們的預期，\n這時候我們可以額外把錯誤訊息抓出來。\ntry: pass # 有可能會發生錯誤的程式，例如：讀取檔案找不到檔案.... except Exception as e: print(e) # 當 try 失敗時，印出錯誤訊息 e，方便工程師 debug 用 範例 print(\u0026#34;hello\u0026#34;) try: a = 1/0 print(a) except Exception as e: print(e) print(\u0026#34;world\u0026#34;) 結果 注意程式沒有死掉，反而執行完印出 world 的那一行。\n這邊只列舉我比較常用的而已，畢竟是我的筆記哈哈哈，\n更多的可以參考 Reference。\nReference 例外處理 ( try、except ) ","date":"2023-04-21T02:44:29+08:00","image":"https://wongwongnotes.com/images/restored/2023/04/%E6%88%AA%E5%9C%96-2023-04-21-%E4%B8%8A%E5%8D%882.42.10.png","permalink":"https://wongwongnotes.com/posts/python/concepts/testing/python-try-except/","tags":["Python"],"title":"【Python 測試程式 #2】使用 try-except 來測試或跳過 python 執行中可能會出錯的程式碼 (python 例外處理)"},{"categories":["441 - VScode"],"content":"前言 在安裝 powerlevel10k 後，會發現透過 VScode 開啟的 terminal 還沒有支援對應字體，\n這邊是我更換字體的一些紀錄\nVScode 設定的部分 step 1 打開「設定」，我們準備進行修改 我們打開並修改設定「settings.json」，\n點選「檔案」-\u0026gt;「喜好設定」中的「設定」。\nstep 2 在上方搜尋「settings.json」(不用全輸入)，打開「settings.json」，我們準備進行修改 或者也可以點擊右上角的符號，也可以叫出 「settings.json」 step 3 在「settings.json」中加入 terminal 字體的設定 在「settings.json」中加入這幾行，\n因為我們有使用 powerlevel10k 與 iTerm2，\n我們也把字體修改為 \u0026ldquo;MesloLGS NF\u0026rdquo;，並指定 terminal 為 \u0026ldquo;iTerm.app\u0026rdquo;\n\u0026#34;terminal.integrated.defaultProfile.osx\u0026#34;: \u0026#34;zsh\u0026#34;, \u0026#34;terminal.external.osxExec\u0026#34;: \u0026#34;iTerm.app\u0026#34;, \u0026#34;terminal.integrated.fontFamily\u0026#34;: \u0026#34;MesloLGS NF\u0026#34; 加入這幾行後，就可以比照 iTerm2 正常顯示 powerlevel10k 的相關內容了！\nReference How to change font for terminal in Visual Studio Code? ","date":"2023-04-21T02:19:32+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/vscode-remote-container-2-1-1024x512.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-terminal-powerlevel10k/","tags":["VSCode"],"title":"【VScode #4】修改 VScode terminal 字體，使得他能夠正常顯示 powerlevel10k (change vscode terminal font)"},{"categories":["114 - Python 字串處理"],"content":"前言 這是我在寫聊天機器人的時候，因為字串太長導致需要分段處理的情形，\n當時是一個超長的文章，然後我需要分段餵才能夠不超過字數上限。\n範例程式碼 照 fix_length 去分段\ns = \u0026#34;abcdefghijklmnop\u0026#34; s_list = [] fix_length = 3 for i in range(0, len(s), fix_length): s_list.append(s[i:i+fix_length]) print(s_list) 結果 個人關鍵字 how to split text by 100 words python Reference Split string by number of words with python Python split()方法 ","date":"2023-04-21T01:28:33+08:00","image":"https://wongwongnotes.com/images/restored/2023/04/%E6%88%AA%E5%9C%96-2023-04-21-%E4%B8%8A%E5%8D%881.26.54.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/python-split-string-by-fix-length/","tags":["Python","字串處理"],"title":"【Python 字串處理 #5】將字串依照固定長度分小段，split string by fix length (by number of words)"},{"categories":["198 - Python 問題解決"],"content":"前言 這是我在開發 chatbot 的時候，Python 時碰到以下問題的解決方式筆記\nImportError: cannot import name \u0026#39;Literal\u0026#39; from \u0026#39;typing\u0026#39; (/usr/lib/python3.7/typing.py) 問題原因 我們想要使用的 \u0026lsquo;Literal\u0026rsquo; 功能，是在 python3.7 以後才有的功能，\n在那之前我們需要用別的功能去代替他才能正常使用\n解決方法 透過下面的討論發現了以下的解決方法，\nCannot Import Literal 先安裝 typing_extensions\npip3 install typing_extensions 然後透過 typing_extensions import 即可在 python 3.7 以前的版本使用\nfrom typing_extensions import Literal Reference Cannot Import Literal ","date":"2023-04-20T20:28:41+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/importerror-cannot-import-name-literal-from-typing/","tags":["Python"],"title":"【Python】問題解決：ImportError: cannot import name 'Literal' from 'typing' (/usr/lib/python3.7/typing.py)"},{"categories":["112 - Python 進階語法"],"content":"前言 python 裡面有一個 repr() 的 function，使用方式與 str() 非常類似，\n不過大致上我們可以這樣簡單理解\nstr()：給人類看的 repr()：給電腦看的 我們來看簡單的例子吧！\n範例 範例一： 有特殊字元的時候 直接看一個簡單的例子：\nstr()，如果有寫一些程式經驗的，我們可以直接看到他印出如同我們想像的結果。\n而 repr()，本來就比較少用，他有點像是把原來的字串，還原成他當初被定義的形狀\n上面圖片很明顯可以說明這件事情，原先我們定義的 a，\n只有在 repr() 被好好的還原，\n但其實我們一般使用時，只會希望是 str() 的結果，\n特定時候我們才會使用 repr()。\n範例二： 純字串的時候 當純字串的時候，使用 repr() 其實也有細微的差別的，\n我們仔細看下面例子，會發現使用 repr() 時，\n印出的結果有反應他是個 \u0026ldquo;字串的本質\u0026rdquo; (注意多了前後的字串符號)，\n也表示對電腦來說，他是理解為字串的。\n應用在 replace 的情況? 當我們在使用原來的 replace 時，如果沒有特別指定完整的 \u0026ldquo;\\t\u0026rdquo; 而是指定 \u0026ldquo;t\u0026rdquo;，不會有任何反應，\n然而如果有使用 repr，使用 \u0026ldquo;t\u0026rdquo; 就會有反應了！\nReference Python repr() 函数 ","date":"2023-04-19T02:03:03+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-19-%E4%B8%8A%E5%8D%881.24.04.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-repr-str/","tags":["Python","字串處理","進階語法"],"title":"【Python 進階語法 #6】python repr() 用法筆記，還我 str 原形的 repr !"},{"categories":["112 - Python 進階語法"],"content":"前言 這篇我們要來研究 python counter 使用方法，\n在解 leetcode 相關的題目的時候，\n很常會有需要「計算數量」的時候，這時 counter() 就能發揮很大的功能。\n說明 counter 屬於 dict 的子資料型態，是專門設計用來計數的，\n我們將一個內容丟入 counter 中，\n會回傳一個相當於 dict 的資料型態，\n其中 key 代表的是「對應的元素」，而 value 代表的是「對應的數量」，\n我們可以直接把內容丟入 counter，並得到結果。\n注意 這邊要另外注意，因為這不算會預載入的 package，\n我們使用上需要另外從 collections import Counter，\n範例如下：\nfrom collections import Counter 完整內容可參考官方文件：\ncollections \u0026mdash; 容器資料型態 新增項目 (counter add) 既然都宣告了一個 counter, 我們當然會想要新增物件, 並累積數量。\n這時我們可以使用\nmy_counter = Counter() my_counter[key] += 1 就是上面這樣使用的方式，\n原來我們 dict 的用法就是結合 key, value\n而 value 被換成計數器，現在我們只需要宣告 Counter 後，\n在指定對應的 key (不需要特別初始化 key 為 0)，直接 += 1 即可，就會是從 1 開始了！\n應用 這邊先直接講我在 leetcode 的應用，\n如果我們將題目的 list 丟入 counter()、counter 會快速幫我們完成統計的動作，\n我們可以再根據回傳的 counter (相當於 dict 的資料型態)，\n進行後續的動作。\n我自己應用的經驗 - 1 from collections import Counter s = \u0026#34;leetcode\u0026#34; print(Counter(s)) print(type(Counter(s))) print(set(Counter(s))) 這邊的應用是，我想統計 “leetcode” 這個 string 裡面，\n每個英文單字各有幾個，\n我們可以看到丟入 Counter 後，我們快速的直接得到答案了！\n而下方的取 set 是另外一種應用，我們可以快速知道「不重複的字母」有哪些。\n不過這裡取 set 的這個用法只是因為我自己解題的過程剛好使用，\n如果通常要用我們會直接取 set()\n範例：\n結果 我的關鍵字 counter add Reference 更完整的 set 整理推薦以下連結，我這邊目前只寫到我自己需要用的部分XD：\ncollections \u0026mdash; 容器資料型態 ","date":"2023-04-18T11:44:14+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/%E6%88%AA%E5%9C%96-2022-03-02-%E4%B8%8A%E5%8D%8811.33.15.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-counter/","tags":["Python","進階語法"],"title":"【Python 進階語法 #5】python counter() 用法整理 - 快速計算資料內容的數量"},{"categories":["199 - Python 自用腳本"],"content":"前言 在開發過程中，我會經常需要記錄某些事件發生的時間。\n而 logging 模組雖然完全能符合我的需求，但有時候有些小專案又不想要設定那麼多。\n這篇文章簡單介紹用 Python 的 「time」 來快速製造我想要的事件紀錄 \u0026ldquo;%Y-%m-%d %H:%M:%S\u0026rdquo; 格式。\n獲取當前時間 要獲取當前時間，我們先 import 「time」 ，然後使用 time.localtime() 函數獲取當地時間。\nimport time now = time.localtime() 此時，「now」 會是一個 「time.struct_time」 對象，其中包含當前時間的各個組件（如年、月、日、時、分、秒等）。\n格式化時間 有了 「time.struct_time」 的物件後，\n我們可以使用 time.strftime() 將他轉換為我們所需的格式。\n在這個例子中，我們將使用 「\u0026quot;%Y-%m-%d %H:%M:%S\u0026quot;」 作為格式字符串，就會產生一個類似於 「2023-03-20 10:30:45」 的結果。\ntime_str = time.strftime(\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;, now) 範例程式碼 import time now = time.localtime() time_str = time.strftime(\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;, now) print(time_str) 這樣就能產生我想要又經常使用來紀錄時間的「\u0026quot;%Y-%m-%d %H:%M:%S\u0026quot; 」格式。\n","date":"2023-04-13T00:44:14+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/python/python-time-record/","tags":["Python","自用腳本"],"title":"【Python 自用腳本 #2】紀錄事件時間表示法 \"%Y-%m-%d %H:%M:%S\" (非 logging 模組)"},{"categories":["182 - Python 網頁爬蟲"],"content":"前言 之前有一個任務是我需要去爬取 Google 文件中的一些內容，\n因此就產生了這一篇程式碼，用這篇文章來作為紀錄。\n在本文中，我們將示範如何使用 「requests」 和 「BeautifulSoup」 來抓取 Google 文件中的內容。\n範例程式碼 import requests from bs4 import BeautifulSoup url = \u0026#34;google 文件網址, 記得改為 public 發布後, 才能讓程式爬取\u0026#34; def read_google_doc(url): response = requests.get(url) if response.status_code != 200: print(\u0026#34;Error fetching page\u0026#34;) exit() else: content = response.content soup = BeautifulSoup(response.content, \u0026#39;html.parser\u0026#39;) # print(content) all_results = str(soup.select(\u0026#39;#contents \u0026gt; div\u0026#39;)) replace_list = [\u0026#39;\u0026lt;p class=\u0026#34;c1\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;div class=\u0026#34;c3 doc-content\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;p class=\u0026#34;c0 c2\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;span class=\u0026#34;c0\u0026#34;\u0026gt;\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;\u0026lt;/span\u0026gt;\u0026#39;, \u0026#39;\u0026lt;p class=\u0026#34;c1 c3\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;/div\u0026gt;\u0026#39;, \u0026#39;\u0026lt;div class=\u0026#34;c2 doc-content\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;p class=\u0026#34;c0\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;span class=\u0026#34;c1\u0026#34;\u0026gt;\u0026#39;] for each_word in replace_list: all_results = all_results.replace(each_word, \u0026#34;\u0026#34;) all_results = all_results.replace(\u0026#34; \u0026#34;,\u0026#34;\u0026#34;) return text_spliter(all_results) 使用 requests 抓取網頁內容 def read_google_doc(url): response = requests.get(url) if response.status_code != 200: print(\u0026#34;Error fetching page\u0026#34;) exit() else: content = response.content 在這段程式碼中，我定義了一個 「read_google_doc」 函數，\n該函數接受一個 「google doc 公開文件的網址」 作為參數。\n接著，我們使用 requests.get() 函數發送 HTTP 請求，並檢查返回的狀態碼。\n如果狀態碼不是 200（200 表示成功），\n我們將輸出錯誤信息並退出。否則，我們將網頁內容保存到 「content」 變量中。\n使用 「BeautifulSoup」 解析網頁內容 soup = BeautifulSoup(response.content, \u0026#39;html.parser\u0026#39;) all_results = str(soup.select(\u0026#39;#contents \u0026gt; div\u0026#39;)) 我們再來使用 「BeautifulSoup」 解析網頁內容。\n首先，我們將剛剛從拿到的 response 裡面，取得 「content」 傳給 「BeautifulSoup」，\n並指定解析器為 「\u0026lsquo;html.parser\u0026rsquo;」。\n接著，我們使用 soup.select() 函數選擇所需的元素，保存到 「all_results」 中。\n這邊有個小技巧，我們可以使用網頁的開發工具 (F12) 來快速幫我們找到我們想要的解析路徑，\nF12 -\u0026gt; 找到對應的 html 片段 -\u0026gt; 右鍵「copy selector」\n處理網頁內容 在處理網頁內容時，我們需要將多餘的 HTML 標籤移除，以便得到乾淨的文字。\n我們可以通過遍歷需要替換的標籤列表來實現這一目標：\n以下內容 replace 很多很雜，基本上應該還有更簡單俐落的做法，不過這邊只是記錄用， 就沒有另外特別整理\nreplace_list = [\u0026#39;\u0026lt;p class=\u0026#34;c1\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;div class=\u0026#34;c3 doc-content\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;p class=\u0026#34;c0 c2\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;span class=\u0026#34;c0\u0026#34;\u0026gt;\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;\u0026lt;/span\u0026gt;\u0026#39;, \u0026#39;\u0026lt;p class=\u0026#34;c1 c3\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;/div\u0026gt;\u0026#39;, \u0026#39;\u0026lt;div class=\u0026#34;c2 doc-content\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;p class=\u0026#34;c0\u0026#34;\u0026gt;\u0026#39;, \u0026#39;\u0026lt;span class=\u0026#34;c1\u0026#34;\u0026gt;\u0026#39;] for each_word in replace_list: all_results = all_results.replace(each_word, \u0026#34;\u0026#34;) all_results = all_results.replace(\u0026#34; \u0026#34;,\u0026#34;\u0026#34;) replace_list 保存了所有我們要替換的內容，\n可以讓我們在後續處理時處理掉這些多餘的字元，\n最後我們再用 replace 進行替換。\n並且透過 replace(\u0026quot; \u0026ldquo;,\u0026rdquo;\u0026quot;)，快速的把文件內的空白全部消除掉。\n到這邊我們就已經完成了，可以把一份乾淨的 Google 文件裡面，裡面的文字內容，全部爬取出來的全部過程。\nReference BeautifulSoup Tutorial: Scraping Web Pages With Python How to read html from a url in python 3 ","date":"2023-04-13T00:14:37+08:00","permalink":"https://wongwongnotes.com/posts/python/web-automation/python/requests-beautifulsoup-google-doc/","tags":["Python","網頁爬蟲"],"title":"【Python 網頁爬蟲 #4】python 爬蟲筆記 - 使用 requests + BeautifulSoup 爬取 google 文件的內容"},{"categories":["141 - Python JSON處理"],"content":"前言 在 Python 中，dict 的格式與 json 的格式時常交互轉換，\n這篇文章將會介紹如何使用 json.loads() 函數將已經轉換為 str type 的 dict 如何轉換回 JSON 格式。\n轉換回 JSON 格式後，我們可以在透過 dict 的資料結構去進行操作。\n使用 json.loads() 轉換字符串為 JSON json.loads() 是 Python 標準庫中 JSON 模塊的一個函數，可以將 JSON 格式的字符串轉換為 Python 的字典對象。以下是一個簡單的範例：\nimport json json_str = \u0026#39;{\u0026#34;key\u0026#34;: \u0026#34;value\u0026#34;}\u0026#39; my_dict = json.loads(json_str) print(type(my_dict)) # \u0026lt;class \u0026#39;dict\u0026#39;\u0026gt; print(my_dict) # {\u0026#39;key\u0026#39;: \u0026#39;value\u0026#39;} 在這個例子中，我們定義了一個 JSON 格式的字符串 json_str (type 為 str, 儲存格式為 JSON)。\n接著，我們使用 json.loads() 函數將 json_str 轉換為字典對象 my_dict。\n注意事項 在使用 json.loads() 時，需要確保字符串符合 JSON 規範。\n例如，所有的 key 必須用雙引號（\u0026quot;）括起來，而不能用單引號（\u0026rsquo;）。\n如果字符串中有單引號，可以使用 replace() 函數將其替換為雙引號：\njson_str_with_single_quotes = \u0026#34;{\u0026#39;key\u0026#39;: \u0026#39;value\u0026#39;}\u0026#34; json_str_with_double_quotes = json_str_with_single_quotes.replace(\u0026#34;\\\u0026#39;\u0026#34;, \u0026#39;\u0026#34;\u0026#39;) # 前「\u0026#39;\u0026#39;」後「\u0026#39;」 my_dict = json.loads(json_str_with_double_quotes) Reference Python | Convert string dictionary to dictionary ","date":"2023-04-08T22:43:36+08:00","permalink":"https://wongwongnotes.com/posts/python/data-formats/python-json/python-json-to-dict/","tags":["JSON處理","Python","字串處理"],"title":"【Python JSON 處理 #2】Python 讀取 JSON 檔案並轉換為 dict，並可在程式碼中使用 (str to dict)"},{"categories":["198 - Python 問題解決"],"content":"前言 這篇是我在透過 request 發送請求時，拿回 json 時發生的問題，\n出現了以下錯誤：\nExpecting property name enclosed in double quotes: line 1 column 2 (char 1) 問題原因 其實後半段的 line 1 column 2 (char 1) 也明確的寫了，\n那讓我們仔細檢查一下內容，發現這個問題，如果是第一次碰到也有點難發現\u0026hellip;\n讀者可以先仔細看這問題，以下為範例：\n{ \u0026#39;key\u0026#39;:\u0026#39;value\u0026#39; } 與\n{ \u0026#34;key\u0026#34;:\u0026#34;value\u0026#34; } 有找到問題了嗎?\n最主要就是 「\u0026rsquo; \u0026lsquo;」、「\u0026quot; \u0026ldquo;」的差別導致了這個錯誤 (其實英文提示的 double quotes 也是這個意思啦)\n解法 因為要使用 json.loads 需要符合他規定的格式，\n因此我們在多做一步，「json_str.replace(\u0026rdquo;'\u0026quot;, \u0026ldquo;\u0026rdquo;\u0026quot;)」，主要是取代單引號為雙引號。\nimport json json_str.replace(\u0026#34;\\\u0026#39;\u0026#34;, \u0026#34;\u0026#34;\u0026#34;) my_dict = json.loads(json_str) Reference Python/Json:Expecting property name enclosed in double quotes ","date":"2023-04-08T14:49:00+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/expecting-property-name-enclosed-in-double-quotes/","tags":["Python"],"title":"【Python】問題解決：Expecting property name enclosed in double quotes"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 舊的 2018 macbook 最近已經不堪負荷，chrome 多開幾個分頁就超級當，\n趁 macbook pro M2 pro 最近剛發表就直接衝了！\n題外話：不知是幸還是不幸，在 macbook pro M2 pro 剛發表的前一週，\n剛好我耍蠢直接把飲料放在書包裡面，飲料不堪擠壓直接噴出在我書包裡面，\n連帶影響到我的電腦，保護了三年的 touch bar 就這樣壞掉了，真的太蠢了QQ\n要修也超級貴\u0026hellip; 然後就馬上發表了這台，難道是剛好聽到了我的需求？\n購入價 我是 2023/3/18 在 apple 直營店買的，購入價 $73500 (教育價)\n購買的配置是 12 核心 CPU, 19 核心 GPU\n16GB 記憶體, 1TB SSD\n選此配置的原因 選此配置的原因主要是我不想要再因為記憶體只有 8G 而 chrome 卡頓了QQ，\n然後 16G 很夠用了，可惜沒有像 Macbook air 有 24G 的選項，升級 32G 就太多也太貴了！\n然後我不需要太好的 GPU，但 CPU 一定要夠力 (其實 M2 最低配也很夠力了XD)，\n但電腦通常買了就會用超級久，所以就不如一次買貴一點的 CPU 最高配了！\n(沒更高等級的 CPU 了，剩下追求的都是 GPU)\n取貨 △ 在 apple 商店領到貨囉！\n△ 台灣剛到貨馬上就入手\u0026hellip; 當相比國外也快等了兩個月才過 NCC 啊\u0026hellip; 這段時間每天都在刷，超煎熬\n△ 結果很不幸的，拿回家的路上就下雨，QQ 紙袋就這樣爛掉了\n開箱 外觀 △ 本體正面\n△ 本體側面\n△ 本體背面\n拆封 △ 打開囉！\n△ 蘋果在包裝方面是真的很細心，我很喜歡這點\n△ 翻到背面，準備拆封模\n△ 剛撕下來，滿滿的質感\n△ 全部撕掉的背面\n△ 全部撕掉的正面，真的很有質感，這代蘋果也變大了！\n其他配件 △ 說明書與電源線\n△ 全部內容物\n充電線 △ 充電線部分，比起以前換成的編織材質，更有質感\n△ 充電線部分，這代採用跟主機一樣顏色的設計，這樣的顏色細節我也很喜歡\n△ 充電線換回了 MagSafe 設計，不知道是好是壞，畢竟現在 type c 充電可能大家也都習慣了\n變壓器 △ 這個到跟之前比沒什麼變化\n說明書與蘋果貼紙 △ 剩下的說明書與蘋果貼紙\n個人化時間！ △ 準備了 Dinotaeng 貼紙，來讓自己的電腦有個性一些\n△ 選這貼紙是要作什麼呢\u0026hellip;?\n△ 答案是要烤蘋果！\n△ 烤蘋果真的超好笑的XD\n△ 跟 ipad 一起烤\n開機 △ 最後截一個剛打開螢幕自動開機的蘋果樣子，登好大聲！！！\n使用心得 因為完全是符合我的使用需求，也沒有之前 2018 會卡頓的問題，\n再加上不小心因為飲料打翻導致 touch bar 壞掉導致不方便使用\n(這裡也想要吐槽一下設計，touch bar 壞掉鍵盤就要全換，蘋果根本搶錢啊！)\n哪有壞一個地方收零件全換的錢的道理！太搶錢了吧！\n","date":"2023-03-18T02:35:05+08:00","image":"https://wongwongnotes.com/images/restored/2023/05/IMG_2050-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/macbook-pro-m2-pro/","tags":[],"title":"【嗡嗡開箱 #19】Apple - MacBook Pro M2 Pro 開箱 | 不專業開箱文"},{"categories":["913 - Telegram"],"content":"前言 這個是我朋友在使用 telegram 嘗試加入群組時，\n碰到以下敘述：\n\u0026#34;Sorry, this group is private\u0026#34; or \u0026#34;Sorry This Group Is Not Accessible\u0026#34; 最後我自己的解決方法\n問題可能原因 經過各種網路上的資料查詢，各種方法也都試過了\u0026hellip;\n我都沒有試到可行的辦法\u0026hellip;.\n我自己覺得可能是 telegram 的 bug 啦\u0026hellip;\n而且再加上 telegram 現在又綁定手機號碼，變成幾乎大部分人只會有一個帳號\n我的解決辦法 總之，不能說是很好的解法，\n但最後我選擇「Delete account」後，\n直接重新申請一個，瞬間就解決了。\n我覺得可能是帳號有碰到什麼奇怪的 bug 就是\u0026hellip;\n但我也不是他們的人，所以只能說是我的猜測\n總之，就是這樣成功解決了這個無法加入群組的問題。\nReference 網路上的很多解法\u0026hellip; 但都沒效\n","date":"2023-03-15T01:29:55+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/telegram/sorry-this-group-is-private/","tags":["Telegram"],"title":"【Telegram】“Sorry, this group is private\" / “Sorry This Group Is Not Accessible” 解決方法"},{"categories":["899 - Debug / Error","299 - C++ 問題解決"],"content":"前言 當我們在寫 C++ 時，碰到以下問題，個人的解決辦法\nerror: no member named \u0026#39;cout\u0026#39; in namespace \u0026#39;std\u0026#39; 問題成因 我們在輸入 cout 的時候，並沒有 include ，\n導致 namespace 中找不到 std::cout 這個成員，\n解決方法 補上 「include 」即可。\nReference cout is not a member of std ","date":"2023-03-09T01:57:41+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/other/debug-error/no-member-named-cout-in-namespace-std/","tags":["C++","Debug","Error"],"title":"【C++】問題解決：error: no member named 'cout' in namespace 'std'"},{"categories":["231 - C++ OpenCV"],"content":"前言 OpenCV 是我們經常用來處理影像的一個套件，\n由於安裝的過程不是說非常容易， 有時候安裝好了，甚至還不能使用，\n因此在這邊做個筆記。\n在這篇文章中，我們簡單介紹一下如何在 mac 中安裝好 OpenCV，\n並撰寫第一支 OpenCV 程式。\n過程中是採用我自己的方法，因此不見得適用於每一個人。\n這一篇教學是依照 OpenCV 官方文件的教學，我把他部分重點翻譯成中文，有能力者建議直接去看原文，順便培養一些看文件能力：Installation in MacOS\nStep 1. 安裝 CMake 我們必須要先確認 CMake \u0026gt;= 3.9，\n我們可以在終端機輸入以下指令確認版本，\ncmake --version 如果還沒有 cmake 或是需要版本更新， 可以輸入以下指令：\nbrew install cmake Step 2. 安裝 CMake 設定好一個要安裝 opencv 的路徑 mkdir ./\u0026lt;opencv_install_dir\u0026gt; # 自己改安裝資料夾名稱 cd ./\u0026lt;opencv_install_dir\u0026gt; git clone https://github.com/opencv/opencv.git git clone https://github.com/opencv/opencv_contrib.git Step 3. 安裝 CMake 建立一個 cmake 用資料夾 mkdir build_opencv cd build_opencv cmake cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON ../opencv make make -j7 # runs 7 jobs in parallel Reference Installation in MacOS ","date":"2023-01-06T00:08:32+08:00","permalink":"https://wongwongnotes.com/posts/cpp/visual-apps/c-opencv/mac-cpp-opencv/","tags":["C++","C++ OpenCV"],"title":"【C++ OpenCV】在 mac 上安裝 OpenCV 筆記，與撰寫第一支 OpenCV 程式"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 這是我在使用 mac 嘗試執行 C++ 程式碼的時候，所碰到的問題\nfatal error: \u0026#39;opencv2/opencv.hpp\u0026#39; file not found 問題原因 簡單來說，就是 OpenCV 沒有裝好，\n至於實際問題可能是什麼，每個人可能會有不一樣的答案，\n因此這邊只舉例「我成功的方法」， 不代表每一個人都適用\n解決方法 這邊因為是沒有成功安裝好的問題，\n因此建議把安裝流程再重新跑一遍，\n安裝流程可以參考我的另一篇文章：\nReference Installation in MacOS ","date":"2023-01-06T00:07:47+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/opencv2-opencv-hpp-file-not-found/","tags":["C++"],"title":"【C++】問題解決：fatal error: 'opencv2/opencv.hpp' file not found"},{"categories":["291 - C++ 觀念理解"],"content":"前言 這一篇文章我們要來討論一個小東西，\n因為我是從 Python 過來的，在 Python 裡面如果沒有經過特別設計，\n我們通常在一個新的檔案都要重新 import package。\n但這個邏輯，在 C++ 似乎不是這個樣子\u0026hellip;\nPython 範例 我們直接來看例子吧！\n注意下面的 file1.py ，我們必須要重新 import os，程式才能夠正常的執行\n# file1.py import file2 os.system(\u0026#34;pwd\u0026#34;) # NameError: name \u0026#39;os\u0026#39; is not defined, need import os # file2.py import os C++ 範例 一樣的，我們直接來看例子吧！\n但 C++\u0026hellip; 似乎不是這樣\n注意下面的 file1.cpp ，我們「不必」重新 incluide ，而且甚至也不用重寫一次「using namespace std」\n程式都能夠正常的執行！！！\n// file1.cpp #include \u0026#34;file2.cpp\u0026#34; int main(){ cout \u0026lt;\u0026lt; \u0026#34;file1 with no include iostream and using namespace std.\u0026#34; \u0026lt;\u0026lt; endl; return 0; } // file2.cpp #include \u0026lt;iostream\u0026gt; using namespace std; 就是這一點，讓剛剛從 Python 回來 C++ 的我，非常的不習慣XDD\n雖然自己在撰寫 C++ 程式的時候，重複的 include 並不會影響程式運行\n但一方面是 trace code 的時候沒辦法直接從同一份檔案找到 library 有些不習慣 (有時候要找 class 定義)\n另外就是自己在寫 C++ 程式的時候，也要讓自己盡量去配合這個習慣，不然寫出來的程式，感覺會很囉唆許多？\nReference C/C++ #include directive with Examples ","date":"2022-12-29T02:27:51+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-include-file/","tags":["C++","觀念理解"],"title":"【C++ 觀念理解 #4】C++ include file 邏輯， 在子檔案已經被 include 的檔案，還需要再重新被 include 嗎？(對比 Python import)"},{"categories":["213 - C++ 檔案處理"],"content":"前言 開頭趁大家注意力還在的時候，\n我們先把最容易混淆的幾個 (我自己都非常經常混淆的)，\n趕快整理起來。\n我們今天要介紹的檔案讀取，\n是 C 語言底下的 istream 類別 (class)， std::ifstream，是他實際使用的名稱。 fstream，則是他的 header。 #include \u0026lt;fstream\u0026gt; 另外還有一個會一起來搗亂的是 iostream，\n在此我們使用，是為了 cout 使用，\n不要看名字很像就混在一起了，例如我 Q_Q\n#include \u0026lt;iostream\u0026gt; 範例程式碼 讓我們直接來看範例程式碼吧！\n// read a file into memory #include \u0026lt;iostream\u0026gt; // std::cout #include \u0026lt;fstream\u0026gt; // std::ifstream using namespace std; int main () { std::ifstream is (\u0026#34;test.txt\u0026#34;, std::ifstream::binary); if (is) { // get length of file: is.seekg (0, is.end); // set position to end int length = is.tellg(); // get length is.seekg (0, is.beg); // set position to start char * buffer = new char [length]; std::cout \u0026lt;\u0026lt; \u0026#34;Reading \u0026#34; \u0026lt;\u0026lt; length \u0026lt;\u0026lt; \u0026#34; characters... \u0026#34;; // read data as a block: is.read (buffer, length); if (is) std::cout \u0026lt;\u0026lt; \u0026#34;all characters read successfully.\u0026#34; \u0026lt;\u0026lt; endl; else std::cout \u0026lt;\u0026lt; \u0026#34;error: only \u0026#34; \u0026lt;\u0026lt; is.gcount() \u0026lt;\u0026lt; \u0026#34; could be read\u0026#34; \u0026lt;\u0026lt; endl; is.close(); // ...buffer contains the entire file... cout \u0026lt;\u0026lt; buffer \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; *buffer \u0026lt;\u0026lt; endl; // first word cout \u0026lt;\u0026lt; *(buffer+1) \u0026lt;\u0026lt; endl; // second word cout \u0026lt;\u0026lt; buffer[1] \u0026lt;\u0026lt; endl; // second word delete[] buffer; } return 0; } 結果 Reference std::istream::read ","date":"2022-12-27T11:49:10+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-27-%E4%B8%8A%E5%8D%8811.48.05.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-file/c-istream-fstream-ifstream/","tags":["C++","檔案處理"],"title":"【C++ 檔案處理 #1】C 語法的檔案讀取 istream (header: fstream, std::ifstream), 與 iostream 直接比較"},{"categories":["298 - Effective Modern C++ 閱讀筆記","999 - 【草稿區】"],"content":"ch4 - item 18: 使用 std::unique_ptr 於 exclusive-ownership (排他的-所有權) 資源管理 此筆記目前尚未整理完成，如需學習完整內容可參考隨附的 reference，或自行 google 搜尋\n但因為作者要整理的筆記太多，如果想早點看到整理後的文章，可以下方留言「期待整理」之類的\u0026hellip; 我會努力找時間優先整理！\nstd::unique_ptrs, raw pointers 有相同的 size，且大部分的功能都相同，\n(包括 dereferencing, 簡單來說指使用「*」 ，例如 *var 取得值)\n如果 raw pointers 能有效率的完成一件事情，那麼 std::unique_ptr 自然也是。\nstd::unique_ptr 介紹 std::unique_ptr 表現出「排他的 - 所有權」特徵，\n當然這段是我直翻的，所以聽起來一定超怪，\n總之 「std::unique_ptr 永遠擁有著他指向的 object ownership」，\n因此，我們就可以討論一下的這些操作：\nmove 使用 move，假如從 source 移動到 destination，\n會使得 destination 獲得這個 「object 的 ownership」，\n這也同時造成 source 在進行這樣的操作後會變為 nullptr\ncopy copy 對於 std::unique_ptr 是禁止操作的，\n因為如果你可以 copy std::unique_ptr，\n就會有兩個 std::unique_ptr 指向相同的 resource，\n但「object 的 ownership」只能有一個， 這也顯示了他的排他性 (exclusive)。\ndelete 從上述我們可以推斷 std::unique_ptr 是個 move-only type，\n當 std::unique_ptr 想要 delete resource 時，\n預設 std::unique_ptr 的 delete 方式，\n是已經被寫好的 raw pointer delete 內建在 std::unique_ptr 裡面\nstd::unique_ptr 使用情境 我們會常見 std::unique_ptr 使用於 factory function，\n特別像是同一個物件會被多個物件所取用的情況，\n例如：\nclass Investment{...}; // 投資 class Stock: // 股票 public Investment{...}; class Bond: //債券 public Investment{...}; class RealEstate: // 房產 public Investment{...}; 像上面的這種情況，我們要使用的是「同一個」Investment，\n這種時候 std::unique_ptr 就可以派上用場。\n上面的例子不好懂的話，可以想像是你的「總財產」，\n不管怎麼樣的消費，花費的都是你的「總財產」，\n因此，不應該產生新的物件，讓「總財產」都是使用同一個物件。\n另外 exclusive-ownership 的特性，也幫助了我們完成了這件「應該要唯一且排他」事情。\n此外，像是這樣子的結構，我們其實會需要稍微思考一下要在哪裡 delete 這個資源\n但因為 std::unique_pt 具有「唯一的 ownership」，\n當 factory 被 destory 時，這個物件也會隨之自動的被 delete。\n另外就像我們剛剛提到的，std::unique_ptr 具有可以被 move 的特性，\n就算這個 std::unique_ptr 被任意的 move，\n或是碰到 atypical (非典型的) control flow，例如：early function return, break\nstd::unique_ptr 也可以自動的判斷在合適的時間 destroy。\nCustom deleters 結論 std::unique_ptr 是一個小, 快速, move-only 的 smart pointer，用來管理 resources 具有 exclusive-ownership 的特性 預設 std::unique_ptr 使用 delete 作為 resource destruction，但我們也可以自己設定 custom deleter，不過這樣的操作有可能使用的 std::unique_ptr size 變大。 從 std::unique_ptr 轉換至 std::shared_ptr 是非常容易的 Reference Effective Modern C++, by Scott Meyers Ch4 智慧指標 ","date":"2022-12-26T02:41:03+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/effective-modern-c/effective-modern-unique_ptr/","tags":["C++","Effective","Modern","閱讀筆記"],"title":"【草稿】【Effective Modern C++ 閱讀筆記 #2】ch4 - Item 18: 使用 std::unique_ptr 於 exclusive-ownership (排他的-所有權) 資源管理, std::unique_ptr 介紹"},{"categories":["298 - Effective Modern C++ 閱讀筆記"],"content":"ch4 - Smart Pointers 前言 raw pointer 的缺點 1. 以 raw pointer 而言，我們不知道他指向的是 single object 還是 array (表示法相同) 都是 object*，我們沒有辦法直接區分出來。\n但為了簡化例子，我們以下使用 int 代替，但就沒辦法舉例到 single-object 的 delete。\n以下是 int array 的宣告 int* p = new int[0]; // accessing p[0] or *p is undefined delete[] p; // cleanup still required 以下是 int pointer 的宣告 #include \u0026lt;iostream\u0026gt; using namespace std; int main(){ int n = 1; int* p1 = \u0026amp;n; cout \u0026lt;\u0026lt; *p1 \u0026lt;\u0026lt; endl; int a[3] = {1, 2, 3}; int* p2 = a; cout \u0026lt;\u0026lt; p2[1] \u0026lt;\u0026lt; endl; int* p3 = new int[5]; // accessing p[0] or *p is undefined cout \u0026lt;\u0026lt; p3[1] \u0026lt;\u0026lt; endl; p3[1] = 10; cout \u0026lt;\u0026lt; p3[1] \u0026lt;\u0026lt; endl; delete[] p3; // cleanup still required return 0; } p1, p2 沒有使用到 new, 單純的拿地址，我們不需要寫 delete，\np3 使用了 new 宣告了 int array 這個 object，\n我們需要手動 delete p3\n結果 2. raw pointer 我們需要手動進行 destroy，換言之我們可能會忘記要 delete 這個很直覺，有 new，就必須要在另外寫一個 delete，\n這就不是很方便。\n3. raw pointer 可能會有專用的 destruction，我們也必須傳入 我們應該要使用 delete，或使用 dedicated (專用的) destruction，\n這是我們必須決定的。\n4. 與第一點相同的，raw pointer 我們在 delete 時，是 single-object 或是 array ? 與第一點相同的，raw pointer 我們在 delete 時，是 single-object 或是 array ?\nsingle-object 要使用 delete 而 array 要使用 delete[] 因為表示方法相同，所以我們沒辦法知道。\n5. (未知的行為) raw pointer 因為手動 delete 的關係，我們可能會有不小心多次 delete 的情況 這其實比想像中容易發生，對於同一個物件的多次 delete，可能會有未知的行為。\nobj* a; if(condition){ // Do something... delete a; } delete a; 我們不知道可能會發生什麼事。\n6. (未知的行為) raw pointer 可能會創造出 dangling pointer，也不知道會發生什麼事 我們先來定義 dangling pointer，\n通常我們會有一個 pointer 指向一個物件 (與其對應的記憶體位置)\npointer -\u0026gt; object(與對應的 memory 位置) 當 object 基於某些原因已經被 destroy，而這個仍然指向他的 pointer 就會被稱作 dangling pointer\ndangling pointer -\u0026gt; 已 destroy object(對應的 memory 位置) 這個 pointer 依然指向這個記憶體位置，如果這時候我們使用了對這個 pointer 取值，\n會發生什麼事情我們無法預期。\n總結上述，我們發現 raw pointer 有太多細節需要注意，\n雖然稍加注意就可以防範，但我們必須非常謹慎使用才行\nsmart pointer 的出現 C++11 後，smart pointer 的出現，\n基本上我們可以視為他是「原來 raw pointer 的 wrapper」，\n而這個 warpper，幫我們迴避掉了上面多數缺點，使我們可以減少犯下的錯誤。\n因此在 C++ 11 以後，\n我們大部分都會推薦優先使用 smart pointer，\nsmart pointer 不但可以做到幾乎全部 raw pointer 能做到的事情，\n還可以大幅減少因為不小心犯下的錯誤。\nsmart pointer 的種類 我們總共有四種 smart pointert 出現在 C++11 之後，分別有\nstd::auto_ptr std::unique_ptr std::shared_ptr std::weak_ptr std::auto_ptr 其中，std::auto_ptr 已經被棄用，在 C++98 時因為 move semantics 還沒有出現，\n因此他有使用 copy 的方式實現，總之就是還不完整設計，知道是個過渡性的東西就好，\n後來在 C++ 11 後我們都使用 std::unique_ptr\nstd::unique_ptr 幾乎做到所有 std::auto_ptr 能做到的事情，\n他還做得更多、更有效率，而且不是使用 copy 的方法\n目前唯一我們還使用 std::auto_ptr 的情境就是為了配合 C++ 98 的 compiler，\n不然建議一律改使用 std::unique_ptr\nReference Effective Modern C++, by Scott Meyers ","date":"2022-12-26T01:28:45+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-26-%E4%B8%8A%E5%8D%8812.44.22.png","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/effective-modern-c/effective-modern-smart-pointers/","tags":["C++","Effective","Modern","閱讀筆記"],"title":"【Effective Modern C++ 閱讀筆記 #1】ch4 - Smart Pointers 前言, 什麼是 dangling pointer?"},{"categories":["211 - C++ 基礎語法"],"content":"前言 這一篇我們要來介紹一個小東西，他的寫法很特別，\n出現在程式裡面也不知道能幹嘛的。\n他長得像這樣：\n(void)var; // var 可以是任意變數 然而實際上他也沒有幹嘛XD\n(void) 的用途 (void)var; 基本上放在程式，可以避免 compiler 跳出 unused variable warnings\n那你可能會想問既然都想避免 unused variable warnings，\n那不如就直接不要寫就好啦，為什麼要寫呢？\n這種用法通常是可能程式設計到一半，\n而這一個變數「未來可能會有功能需要增加，但目前尚未使用」，\n所以我們預先宣告了他，但還沒有使用他的這時候，就會出現這樣子的寫法。\n換句話說，你也可以當作這是一個 TODO 的概念\n並且他不會影響程式碼執行，也不會做任何事。\n範例程式碼 #include \u0026lt;iostream\u0026gt; using namespace std; int main() { cout \u0026lt;\u0026lt; \u0026#34;Hello\u0026#34; \u0026lt;\u0026lt; endl; int a; (void)a; cout \u0026lt;\u0026lt; \u0026#34;World\u0026#34; \u0026lt;\u0026lt; endl; return 0; } 結果 (對，就是沒有東西，但為了確保程式有執行，我還是印了一點東西)\nReference Significance of (void)variable [duplicate] ","date":"2022-12-25T02:43:35+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-25-%E4%B8%8A%E5%8D%882.43.05.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-void-var/","tags":["C++","基礎語法"],"title":"【C++ 基礎語法 #9】C++ (void) var 是什麼寫法? 會做什麼事情?"},{"categories":["291 - C++ 觀念理解"],"content":"前言 這邊要來整理長期我在 C++ 感到困惑的一個問題，\n就是為什麼有時候 for 迴圈 會有 \u0026amp;，有時候 for 迴圈 又沒有 \u0026amp;，\n然後有時候沒有 \u0026amp; 也跑得過，有時候看範例卻又不是這樣寫。\n直接看程式碼再來解釋 下面這一段程式碼是一個簡化後的例子，它可以用來解釋我目前困惑的所有問題。\n我們單純的讓程式印出從 0 到 9，\n也宣告了另外一個 arr 從 9 一路到 0。\n#include \u0026lt;iostream\u0026gt; using namespace std; int main() { for (int i = 0; i \u0026lt; 10; ++i) { cout \u0026lt;\u0026lt; i; } cout \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; \u0026#34;---------------------\u0026#34; \u0026lt;\u0026lt; endl; int arr[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; for (int i = 0; i \u0026lt; 10; ++i) { cout \u0026lt;\u0026lt; arr[i]; } cout \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; \u0026#34;---------------------\u0026#34; \u0026lt;\u0026lt; endl; for (int* it = std::begin(arr); it != std::end(arr); ++it){ cout \u0026lt;\u0026lt; it \u0026lt;\u0026lt; \u0026#34;, \u0026#34;; // it: address cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; // *it get value } cout \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; \u0026#34;---------------------\u0026#34; \u0026lt;\u0026lt; endl; for (int \u0026amp;ele : arr) { cout \u0026lt;\u0026lt; ele; } cout \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; \u0026#34;---------------------\u0026#34; \u0026lt;\u0026lt; endl; return 0; } 結果 解釋區 上面的程式碼大致分為四段，讓我理解了非常多的事情，還有觀念混淆的地方\n第一段 for loop 的例子，最單純的數值增加 for (int i = 0; i \u0026lt; 10; ++i) { cout \u0026lt;\u0026lt; i; } 第一段例子是每一位學 c++ 新手都一定最早學到的例子，\n他就是一個單純的數值增加，\n但大部分開始混亂，都是從下面那幾個例子才開始出現的，例如我XD。\n第二段 for loop 的例子，我們是在用 for 增加變數，透過變數去拿取物件 int arr[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; for (int i = 0; i \u0026lt; 10; ++i) { cout \u0026lt;\u0026lt; arr[i]; } 到這裡應該也還不算太混亂，就只是一個透過 array 的 index 去拿取對應的 arr 數值。\n第三段 for loop 的例子 (重要)，其實，我們是直接去地址拿值！ for (int* it = std::begin(arr); it != std::end(arr); ++it){ cout \u0026lt;\u0026lt; it \u0026lt;\u0026lt; \u0026#34;, \u0026#34;; // it: address cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; // *it get value } 讓我觀念開始混亂，就是從這個例子開始的，\n碰到 array 數值的拿取，第二種寫法或是第三種寫法都是主流，\n第二種寫法是透過「index」去拿取 arr 裡面的值 第三種寫法是，「我們直接去對應的記憶體位置，把值拿出來」 所以這邊我們看到 for 開頭宣告是指標的 int，\n因為我們是透過這個 int，作為 iterator 去遍歷我們的 array 的內容。\n而這裡還有一個重要的觀念，注意輸出結果的部分，我們發現儲存的記憶體位置，\n每一次都是移動 4 個 bytes，並且連續。\n我們唯有透過正確的一次移動 4 bytes 的記憶體位置，才能夠拿到正確的值。\n(而我們怎麼知道「記憶體位置正確的移動」是 4 bytes? 注意到我們 iterator 定義的是 int* 了嗎! )\n而這個例子，經常有的延伸應用 通常這樣子的寫法會被我們拿來遍歷各種更複雜的資料結構，例如像是 vector\u0026hellip;\n這也是為什麼我們在 for 的後面，必須要宣告一個指標的原因，\n實際上我們就是在不斷地移動記憶體位置，好讓我們去拿取對應的值。\n第四段 for loop 的例子 (重要)，其實，我們也是直接去地址拿值！ 第四種寫法出現後，更是讓我跟前面的第二、三個例子，整個大爆炸！！！\n這也是為什麼這次整理，一開始就在強調，\n現在就是要整理 for 迴圈後面出現的東西，\n為什麼有時候有 * ? 有 \u0026amp; ?\n他們一起出現的時候，我的觀念就爆了XDDD，\n開始懷疑人生，跟我是不是不適合寫程式XDDD\nfor (int \u0026amp;ele : arr) { cout \u0026lt;\u0026lt; ele; } 這個寫法其實還蠻新鮮，是在 C++ 11之後才出現，\n因此如果使用 C++ 98 的編譯器是沒有辦法成功執行的。\n關於這個寫法的關鍵字，可以搜尋「Range-based for loop」\nRange-based for loop Range-based for loop in C++ 他一開始出現的用意，是為了幫助我們簡化寫 for loop，\n但面對像我這種觀念不穩的人，這個東西出現之後整個就讓我觀念大爆炸XDDD，\n總之， 他做的事情非常簡單，就是去指定物件中去拿每一個地址的值。\n像上面的表示方法，因為我知道 arr 是 Int array，我就透過 int \u0026amp; 去拿每一個值。\n題外話，那不加 \u0026amp; 可以嗎? 為什麼有時候看到不加也可以？ 沒錯，不加也可以！\n但這也是我們另外今天要另外討論的另外一個主題！\n拿參考? 拿真實地址? 為了解決這個問題，我們另外加了一點點程式碼，\n這邊我們應該直接看結果就知道差別了。\n(先跟人家說，沒錯，印出結果會是一樣的，但注意細節！)\nfor (int ele : arr) { cout \u0026lt;\u0026lt; \u0026amp;ele \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; ele \u0026lt;\u0026lt; endl; } cout \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; \u0026#34;---------------------\u0026#34; \u0026lt;\u0026lt; endl; for (int \u0026amp;ele : arr) { cout \u0026lt;\u0026lt; \u0026amp;ele \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; ele \u0026lt;\u0026lt; endl; } cout \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; \u0026#34;---------------------\u0026#34; \u0026lt;\u0026lt; endl; 與他對應的結果 有注意到差別了嗎？仔細看上面「沒有 \u0026amp;」的例子，雖然記憶體位置好像感覺都一樣，\n但這並不好！！！\n這表示實際上我們多使用了一個 int ele 的記憶體空間。\n而且，再仔細觀察一下記憶體位置，\n你會發現下面的寫法跟第三個例子印出來的記憶體位址，結果完全一模一樣。\n因此上面的寫法實際上我們在 int ele 多跟記憶體要了一個 int 空間，\n但實際上這是完全沒有必要的，我們只是單純的要做指標的移動，\n不需要新的記憶體空間， 所以這邊使用「int \u0026amp;」的方法會更好。\nReference Range-based for loop Range-based for loop in C++ ","date":"2022-12-25T02:31:59+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-25-%E4%B8%8A%E5%8D%882.26.20.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-for-loop-pointer-reference/","tags":["C++","觀念理解"],"title":"【C++ 觀念理解 #3】C++ 理解 for loop (迴圈) 為什麼有時候會有 * ，為什麼有時候會有 \u0026"},{"categories":["630 - 演算法 Algorithm"],"content":"前言 這篇是我個人的筆記，因此不會是完整的教學，\n只紀錄個人認為重要的部分。\n推薦文章 在網路上剛好已經有看到非常不錯的介紹，推薦可以優先看這一篇：\n[資訊安全] 密碼存明碼，怎麼不直接去裸奔算了？淺談 Hash , 用雜湊保護密碼\nhash 的介紹與特性 我們在使用 hash 的時候，\n主要在做的事情是把 Input 經過 hash function 運算之後得到一個 output，\n可以表示為 input -\u0026gt; hash function -\u0026gt; output\nhash 最重要的特性 「不可逆」 hash 最重要的特性就是 「不可逆」\n我們在設計 hash function 時，要注意我們設計的 hash function 要有不可逆的特性，\n這樣才符合 hash 的精神，\n我們這裡簡單舉例怎麼設計不可逆的 hash function，但實際上設計只會更複雜。\n這裡只為解釋「不可逆的特性」舉例。\n例如，我們的密碼是 18，hash function 是 x mod 10，\n因此透過計算我們會得到 8。\n所謂不可逆的特性是指我們無法從 8 回推至 18，\n稍微思考之後我們能知道，回推的可能有太多種，8、18、28 都是有可能的結果。\n因此利用這個特性，我們可以設計一種密碼驗證機制，\n「使我們可以在不儲存真實的密碼情況下，進行密碼驗證」\ncollision 講完上面的例子後，來講個題外話，\n你可能會有疑惑，那如果猜密碼的人去猜 hash function 後的結果就好啦！\n甚至還比原來的 18 更好猜，因為 8, 18, 28 都算對耶！！\n這裡就會帶出一個新的概念，就是 hash 後的 collison (碰撞)，也代表著出現一樣結果的機率。\n一個好的 hash function，發生 collison 的機率也必須要低，\n例如我們可以透過增加 hash function output 的位元數，\n把所有可能的密碼對應到，全部可能有 2^256 種結果，\n不要小看這數字，「表示上」看起來很小，他可是「天文等級的數量級」，\n有興趣的話，推薦閱讀這篇：加密貨幣世界裡最強大的數字：2 的 256 次方\n而這個算法就可以提到超級有名的 hash 算法 SHA-256，\n細節就先不在此文章多提了，有興趣可以自己去查詢。\n而 SHA-256 發生 collision 的機率，雖然不為 0，\n但那機率超級無限接近 0 \u0026hellip; 不知道怎麼解釋好的數量級XDDD，\n因此我們就幾乎可以直接假定這件事情不會發生！\n(我是抓個感覺，比被雷劈到的機率更低? 比你所有認為發生機率超級低的事情，機率都還要更更低！)\n有興趣的話一樣可以參考其他網友們的分享！\nhash 在密碼上的應用 因為上述提到的不可逆特性，使得我們輸入的密碼，例如 password，\n經過 hash function 後可能變成一個特殊值，\n而 hash function 的另外一個特色是，不論我們進行多少次 hash function 計算，\n「hash function 出來的結果必須要是一樣的」，也就是必須都能算出一樣的結果\n而這樣我們就能安心的儲存密碼，因為就算駭客駭入主機拿得到密碼，\n拿到的密碼也是永遠沒辦法被還原成原始的樣子。\n而像我們作為使用者，只要我們每次輸入一樣的密碼，\n透過 hash function 都會得到一樣的特殊值， 進而比對密碼的正確性。\n比較：加/解密 與 hash 的不同 「加解密」：重點是可逆，可以還原出原始結果。 「Hash」：重點是不可逆，不能還原出原始結果，只用來單向計算，比對一致性。 Reference [資訊安全] 密碼存明碼，怎麼不直接去裸奔算了？淺談 Hash , 用雜湊保護密碼 Encryption 資安筆記-2 加密貨幣世界裡最強大的數字：2 的 256 次方 ","date":"2022-12-25T01:23:56+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/hash/","tags":["Algorithm","演算法"],"title":"【演算法筆記 #5】Hash 筆記，為什麼要使用 bash，與常見的密碼查詢驗證"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 這是我在宣告 unordered_map 時，所碰到的問題\ncannot be initialized with an initializer list 解決方法 這裡碰到的是 g++ 不支援 c++98 的標準問題，\n因此只要更換至 c++11 以上即可。\n原來： g++ unorder_map_test.cpp; ./a.out 修改後： g++ unorder_map_test.cpp -std=c++11; ./a.out Reference C++编译报错 non-aggregate cannot be initialized with an initializer list ","date":"2022-12-24T02:15:20+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/cannot-be-initialized-with-an-initializer-list/","tags":["C++"],"title":"【C++】問題解決：cannot be initialized with an initializer list"},{"categories":["211 - C++ 基礎語法"],"content":"前言 在 C++ 基礎語法 #7 的時候，我們介紹了 unorder_set()，\n今天我們要來介紹另外一個也是相關，但更實用的 unorder_map() ！！！\n快速破題 unorder_set()，就是 set 的一種，用來 hash 單一項目，\nunorder_map()，屬於 table 的一種 (Python 我們會說 dict)，用來 hash key-value pair 的項目，\n也因此，我們可以快速透過 unorder_map 的方式，\n快速地透過 hash key 來找到我們想要的 value。\n注意事項 (include header) 使用前，我必須要記得「#include \u0026lt;unordered_map\u0026gt;」\n#include \u0026lt;unordered_map\u0026gt; 使用方法 1 - 宣告 unorder_map 這裡舉例一個常見的用法，我們把 string 這個方法給 include 進來，\n然後我們就可以建立一個 unorder_map (如同 Python Dict 那樣)\n範例程式碼 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; #include \u0026lt;unordered_map\u0026gt; using namespace std; int main(){ std::unordered_map\u0026lt;std::string, std::string\u0026gt; u_map = { {\u0026#34;key1\u0026#34;,\u0026#34;value1\u0026#34;}, {\u0026#34;key2\u0026#34;,\u0026#34;value2\u0026#34;}, {\u0026#34;key3\u0026#34;,\u0026#34;value3\u0026#34;} }; cout \u0026lt;\u0026lt; u_map[\u0026#34;key1\u0026#34;] \u0026lt;\u0026lt; endl; return 0; } 結果 使用方法 2 - 遍歷 unorder_map (first, second) 另外一個我們也會經常用到的用法，\n我們會想要遍歷整個 unorder_map，\n我們可以透過 std::pair 的方式來遍歷 unorder_map 的內容\n範例程式碼 我們繼續接續上面那一段的程式碼，我們接下來加上下面這一段。\nfor(const std::pair\u0026lt;std::string, std::string\u0026gt;\u0026amp; ele: u_map){ cout \u0026lt;\u0026lt; \u0026#34;u_map[\u0026#34; \u0026lt;\u0026lt; ele.first \u0026lt;\u0026lt; \u0026#34;] = \u0026#34; \u0026lt;\u0026lt; ele.second \u0026lt;\u0026lt; endl; } 結果 或把上述的內容用 auto 簡化 for(const auto\u0026amp; ele: u_map){ cout \u0026lt;\u0026lt; \u0026#34;u_map[\u0026#34; \u0026lt;\u0026lt; ele.first \u0026lt;\u0026lt; \u0026#34;] = \u0026#34; \u0026lt;\u0026lt; ele.second \u0026lt;\u0026lt; endl; } 結果 一些細節 - 關於 const 這裡必須要加 const 的原因，因為透過 pair 拿出來的值，是臨時的值，\n我們無法進行修改，因此必須給予 const 屬性。\n這邊觀念不是很確定，有錯歡迎指正。\n一些細節 - 關於 \u0026amp; 這裡其實我們如果不使用 \u0026amp; 程式也是可以跑的，\n不過我們可以想像 std::pair 的時候，\n他是每一次都宣告一個新的記憶體空間「為了儲存我們遍歷拿出來的值」，\n但是我們在 for 迴圈的時候，其實沒有必要每一次都宣告一個新的記憶體空間，\n因此我們這邊加上 \u0026amp;，讓我們只要宣告一次的記憶體空間進行遍歷操作即可。\n這邊觀念不是很確定，有錯歡迎指正。\nReference std::unordered_map ","date":"2022-12-24T00:44:18+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-24-%E4%B8%8A%E5%8D%8812.46.14.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-unorder_map/","tags":["C++","基礎語法"],"title":"【C++ 基礎語法 #8】C++ unorder_map() 用法筆記 (類似 Python Dict)"},{"categories":["443 - vim"],"content":"前言 vim 用久了之後，看到一個方便的資料夾插件 NERDTree，\n可以直接在vim 當中看到資料夾結構。\n例圖： 安裝 - 設定 vim 管理插件 pathogen step 1. 新建資料夾 mkdir -p ~/.vim/autoload mkdir -p ~/.vim/bundle step 2. 下載 pathogen (管理 vim 插件使用) mac 個人試過可行的方法： cd ~/.vim/autoload wget https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim -o pathogen.vim 這裡可能有個常見錯誤，會不小心把 html header 拉下來，\n常見錯誤解決方法可參考：Unknown function: pathogen#infect\n或直接嘗試下方 Linux 的下載 pathogen.vim 的方法\nLinux, rpi3 個人試過可行的方法： curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim step 3. 修改 ~/.vimrc 先打開 ~/.vimrc，\nvim ~/.vimrc 加入以下內容，\n這樣就可以使用 pathogen 了！\nexecute pathogen#infect() 安裝 - 安裝 NERDTree 直接安裝至 ~/.vim/bundle 即可\ncd ~/.vim/bundle git clone https://github.com/scrooloose/nerdtree.git 使用 打開 vim 之後，\n輸入 :NERDTree (可以 tab，不用全部輸入完)\n就可以使用囉！\n開啟 vim 時，自動啟動 NERDTree 打開 ~/.vimrc ，加入以下指令\nau VimEnter * NERDTree 補充 - vim 快速鍵 既然都會自動打開 NERDTree 了！\n那快速鍵更是要先記好啊！\n快速保存並退出：ZZ (shift 按住)\n快速退出、不保存：ZQ (shift 按住)\n其他可以參考我的另外一篇文章：\nhttps://wongwongnotes.com/posts/dev-tools/editors/vim/linux-ubuntu-vim/\n個人搜尋用關鍵字 mac vim install nerdtree Reference 原作者 github : preservim / nerdtree vim插件推荐（持续更新） NERDTree — 好用的 vim 樹狀檔案管理 plugin Mac Install Vim 外掛 - NERDTree Unknown function: pathogen#infect Auto-open NERDTree in vim ","date":"2022-12-24T00:25:43+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-12-%E4%B8%8A%E5%8D%881.59.05.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vim/vim-nerdtree/","tags":["Bash","Linux","Ubuntu","Vim"],"title":"【vim #2】vim 顯示資料夾結構的插件 NERDTree 安裝筆記 (Mac, Linux, rpi3)"},{"categories":["432 - tmux"],"content":"前言 當我們在 tmux 使用久了之後，我們也會想要把一些歷史紀錄清空，\n讓我們在倒退回去看歷史紀錄的時候，顯得比較乾淨，\n因此這一篇我們就要來教學如何透過一些指令把 tmux 的歷史紀錄清空。\n範例 「C+b」，並輸入「:」進入指令模式，\n在冒號後繼續完成以下輸入\n:clear-history △ 在指令模式下這樣輸入\n結果 △ 清空紀錄的樣子\nReference How do I clear the scrollback buffer in tmux? ","date":"2022-12-23T01:30:58+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-23-%E4%B8%8A%E5%8D%881.25.48.png","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/tmux/tmux-clear-buffer/","tags":[],"title":"【tmux #3】tmux 進階 - 清空 scrollback buffer (tmux clear buffer)"},{"categories":["350 - Linux 終端機操作"],"content":"前言 熟悉操作終端機的人，有時候會不會有想要把目前正在跑的程序放到背景執行的時候呢?\n這裡我們介紹一些方法\n一開始就放在背景 「\u0026amp;」 例如我們可以這樣做：\npython example.py \u0026amp; # 或是 ./script.sh \u0026amp; 這樣預設一開始就會是背景執行了！\n「ctrl+Z」 執行過程中放到背景 如果程序已經先執行了，我們才想放到後台去跑呢？\n這種情況其實也很常見，因為有時候我們要看程式有沒有被正常的初始化。\n這時候我們只要按鍵盤快速鍵「ctrl+Z」就可以把程序放到後台囉！\n「fg」/ fore ground 放到背景的程式還原 我們剛剛把一堆程式放到背景了，那有沒有方法能夠把這些程序進行還原呢？\n當然是有的。我們只需要在終端機輸入「fg」即可。\nfg 記法：「fg」= fore-ground，把後台程序叫到前景。\n「bg」/ backend ground，把程序丟到背景去執行 有「fg」就會有「bg」，\n「bg」， 就是可以協助我們來查看目前在後台的所有程序囉！\n記法：「bg」= back-ground，顯示背景的程序。\njobs，查看所有的任務 可以透過 jobs 指令看現在所有存在的任務，最前面會有 id，\n只要使用 bg 就可以把他丟到後台去執行！\n綜合運用 例如說程式執行到一半想暫停，或是想丟到後台繼續跑，\n那就是可以先 Ctrl+z，jobs 看是哪一個 id，然後 bg ，就可以把未執行完的程式丟到後台去跑囉！\nReference How to restart some progress which is stopped by \u0026ldquo;ctrl+z\u0026rdquo;? JUN 12 2009 [轉]Linux 任務控制的幾個技巧( \u0026amp;, [ctrl]-z, jobs, fg, bg, kill) ","date":"2022-12-23T01:15:42+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/linux-ctrl-z-fg-bg/","tags":["Linux","終端機操作"],"title":"【Linux 終端機操作 #9】ctrl+Z and resume, jobs, \u0026, fg, bg - 終端機執行的指令移動到背景? 如何從後台還原程序? 如何把未執行完的程式丟到後台去跑？"},{"categories":["432 - tmux"],"content":"前言 當我們 tmux 使用久了之後，我們會一直感到很困惑的是，\n每次滑鼠好像都沒有辦法在 tmux 裡面使用，\n但其實有一個隱藏功能是可以讓我們「完全且很直覺地透過滑鼠在 tmux 進行操作」，\n當我發現這個功能的時候「甚至有點後悔太晚知道這件事情」，\n現在這篇文章就讓我們透過滑鼠來徹底解放使用滑鼠來操縱 tmux 吧！\n使用指令 一樣的我們先使用「C+b」，並輸入「:」， 呼叫出我們的指令模式，\n並且緊接在「:」完成輸入\n:setw -g mouse on 就完成了！！！ 非常簡單的設定！\n使用方式 現在開始，我們可以透過滑鼠進行各種簡單且直覺的操作！\n快速地 scrollback，並進行過去的歷史紀錄查看 這一點是最打中我的，因為以前在終端機呼叫出 tmux 的時候，\n很常會跟「原生終端機」的捲動「混在一起」，\n導致超出 tmux 限制的範圍 (也不是我們想看的)，\n現在直接透過滑鼠捲動，就只會在 tmux 的內部進行 scrollback ，不會再捲動「原生終端機的範圍」了。\n這一點真的太實用了！！！大推！！！\n快速複製「終端機內文字」 除了上一點提到的，我們可以快速查看過去顯示的歷史訊息，\n現在我們只要滑鼠直接反白文字，他就會直接複製文字完成。\n這一點實在是非常的方便！！！\n分割模式時，使用滑鼠來快速調整分割 這邊因為不太方便截圖的關係，我先用文字說明，\n如果我們是透過 tmux 直接進行畫面分割(C+b % 的垂直分割、C+b \u0026quot; 的水平分割)，\n現在我們可以透過滑鼠直接拖動分隔線，來重新分割畫面，\n不用像之前還要使用複雜的按鍵組合「c+b (按住) 上下左右」才能夠拖動。\n直接改為預設開啟 編輯「~/.tmux.conf」檔案，設定啟動時配置 vim ~/.tmux.conf 添加內容 set -g mouse on Reference Scroll shell output with mouse in tmux https://codimd.mcl.math.ncu.edu.tw/s/H1dRFHYyV ","date":"2022-12-22T02:10:59+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-23-%E4%B8%8A%E5%8D%882.01.19.png","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/tmux/tmux-mouse-set/","tags":[],"title":"【tmux #2】tmux 好用的滑鼠擴充功能，透過滑鼠徹底強化你的 tmux！(tmux scroll up with mouse)"},{"categories":["114 - Python 字串處理"],"content":"前言 這篇也是要介紹一個小東西，Python String strip()，\n沒什麼好多介紹的，直接進入正題。\n範例 範例程式碼 sample code \u0026amp; 結果 嘿對，就這樣，可以同時去掉頭尾的「空白」、「換行\n」、「\\t」，\n特定時候超好用的！ 不用特別小心 replace 還會不小心替換錯東西。\nReference ","date":"2022-12-20T00:47:08+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-20-%E4%B8%8A%E5%8D%8812.42.37.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/python-strip-trim/","tags":["Python","字串處理"],"title":"【Python 字串處理 #4】Python strip() — python 去頭去尾、去空白、去換行神器"},{"categories":["114 - Python 字串處理"],"content":"前言 這篇也是要介紹一個小東西，Python String startswith()，\n沒什麼好多介紹的，直接進入正題。\n範例 範例程式碼 sample code \u0026amp; 結果 嘿對，就這樣，可以判斷開頭，\n特定時候滿好用的！\n題外話：記得不要打錯字！！！ start\u0026quot;s\u0026quot;with，記法：「第三人稱單數」英文老師有沒有教好！！！\nReference Python String startswith() ","date":"2022-12-20T00:41:44+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-20-%E4%B8%8A%E5%8D%8812.40.01.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/python-string-startswith/","tags":["Python","字串處理"],"title":"【Python 字串處理 #3】Python String startswith()，判斷字串是不是什麼東西開頭的！"},{"categories":["411 - Git 觀念整理"],"content":"前言 其實這篇跟 Git 只有一點點關係 XD，\n因為也算是版本控制的內容，所以被我歸類在 Git 這邊。\n這篇要來筆記一個比較特別的小東西，\n也就是我們在各大程式常見的版本號「v X.X.X」是怎麼被訂出來的！\nSemantic Versioning 原文：Semantic Versioning 提供了一個我們在命名版本號的準則，\n通常會有三個數字，已經英文來說分別是，\n- MAJOR：通常是大更新，且會造成相容性問題的更新 - MINOR：同一版本內的小更新，功能上增加幾個，且能做到向下兼容。 - PATCH：單純 bug fixes, 修一些小東西的版本更新。 MAJOR 0? Major 0 在文件中有提到 0.y.z，通常代表的是初始開發 (inital development)，通常是 0 開頭的 API 版本，也會建議對於 public 不要將他視為穩定版 (stable)。\nreference 原文：Semantic Versioning ","date":"2022-12-19T23:47:16+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-concepts/git-semantic-versioning/","tags":["Git"],"title":"【Git 觀念整理 #3】關於版本號，v X.X.X 是怎麼被訂出來的? (Semantic Versioning, 版號控制)"},{"categories":["116 - Python 系統控制"],"content":"前言 python 有個非常實用的套件「argparse」，\n使我們的程式執行時可以攜帶參數，\n許多大型的 python 系統中都能夠看到使用這個套件的蹤影，\n內文中也提供我自己的程式模板，\n可以直接套用再更改成自己想要的樣子。\n詳細說明 下面的範例程式碼中，是將每一個變數寫得較為嚴謹的，\n實際上不需要每個都寫到這麼細節，但為了程式可讀性 (也方便其他工程師閱讀)，\n個人覺得是寫愈完整愈好。\nimport argparse 基本的「import argparse」是必要的，這個我就不多提了XD\nargparse.ArgumentParser() parser = argparse.ArgumentParser(prog=\u0026#39;argparse_template.py\u0026#39;, description=\u0026#39;Tutorial\u0026#39;) 這個是最一開始的宣告，內容可以不寫，\n這些裡面的內容都是在當我們下「-h」、「\u0026ndash;help」時才會出現的「提示內容」\n也就是說，最少只需要寫 「parser = argparse.ArgumentParser()」，不能再少了\nprog='argparse_template.py' prog 指的是「程式的名稱」 description='Tutorial' description 指的是「程式的功能敘述」 parser.add_argument() parser.add_argument(\u0026#39;--text\u0026#39;, \u0026#39;-t\u0026#39;, default=\u0026#39;test123456\u0026#39;, type=str, required=False, help=\u0026#39;Text for program\u0026#39;) parser.add_argument(\u0026#39;--nums\u0026#39;, \u0026#39;-n\u0026#39;, default=\u0026#39;10\u0026#39;, type=int, required=False, help=\u0026#39;Text for program\u0026#39;) 這邊就是「重點中的重點了」，\n所有的參數都是在這邊完成的！ 這邊提供兩個範例。\n'--text', '-t' （必給）就是指帶的參數，像這邊我們定義了「--text」、「-t」都可以，(長的是清楚、短的是方便)， 這邊定義完之後，我們之後就可以透過這邊的內容在程式執行時下參數。 註：不一定要「兩個」，可以一個或兩個以上都沒問題，直接接在後面即可。\n但一般建議「一長一短」就好 (目的：長的是清楚、短的是方便)，太多看起來就太雜亂了。\ndefault='10' （可以不給）預設這個變數的值。 type=str （可以不給）預設這個變數的型態，通常是限制型態或讓可讀性更好，例如我們可以判斷 1 應該是 bool, str 還是 int。 required=False （可以不給）如果這個值是 True，在執行 python 的時候會強制要你給這個參數 (不管你有沒有設 default)。 help='Text for program') （可以不給）這個變數的說明，會在輸入錯誤時、或是輸入「-h」、「–help」時出現提示。 parser.parse_args() 將這些帶入的參數，導入至一個變數裡面。\n習慣上我們都會直接寫\nargs = parser.parse_args() 呼叫變數 我們只要使用上面回傳的變數 args，\n我們可以透過物件呼叫的方式將我們得到的變數呼叫出來。\n例如： args.text、args.num (取決於：「你的 \u0026ndash; 後面接的東西」)\n寫完了，卻忘了自己設定怎麼使用嗎? 寫完了，卻忘了自己設定怎麼使用嗎?\nargparse 也提供了我們方便的功能，\n只要下 「-h」、「\u0026ndash;help」就可以快速查詢所有的說明。\n範例 範例程式碼 sample code 這邊我們將 argparse 做成一個 function，套用時\n只需要修改 get_args() 就能直接修改要帶入的參數 修改 main() ，然後看你想要幹嘛XD 像這邊的 main() ，我們就將輸入的字串 args.text 重複印出 args.nums 次\nimport argparse def get_args(): parser = argparse.ArgumentParser(prog=\u0026#39;argparse_template.py\u0026#39;, description=\u0026#39;Tutorial\u0026#39;) parser.add_argument(\u0026#39;--text\u0026#39;, \u0026#39;-t\u0026#39;, default=\u0026#39;test\u0026#39;, type=str, required=False, help=\u0026#39;Text for repeated\u0026#39;) parser.add_argument(\u0026#39;--nums\u0026#39;, \u0026#39;-n\u0026#39;, default=\u0026#39;10\u0026#39;, type=int, required=False, help=\u0026#39;Repeated times\u0026#39;) return parser.parse_args() def main(): for i in range(args.nums): print(i, args.text) if __name__ == \u0026#39;__main__\u0026#39;: args = get_args() main() 範例執行結果 可以看到我們下了 「-n 10 (表示重複10次)」，「 -t (表示輸入字串)」\n另外，因為我們有設定 default，所以就算有參數沒有下也能直接讀取 default 的值。\n進階使用 - 設定不帶參數的純 flag (store_true) 上面的例子當中，我們幾乎示範的都是「帶有參數的」，例如「\u0026ndash;num 10」，\n其實也有不帶參數的用法，作為一個「單純的 flag」。\n範例程式碼 在此，flag 作為單純的 flag 使用，\n而「action=\u0026ldquo;store_true\u0026rdquo;」，表示「有 flag 時為 True」。\nimport argparse def get_args(): parser = argparse.ArgumentParser(prog=\u0026#39;test.py\u0026#39;, description=\u0026#39;Tutorial\u0026#39;) parser.add_argument(\u0026#34;--flag\u0026#34;, help=\u0026#34;help\u0026#34;, action=\u0026#34;store_true\u0026#34;) return parser.parse_args() if __name__ == \u0026#39;__main__\u0026#39;: args = get_args() if args.flag: print(\u0026#34;got flag.\u0026#34;) else: print(\u0026#34;no flag.\u0026#34;) 結果 Reference Argparse 教學 python argparse 的介紹 (python 的引數) ","date":"2022-12-19T19:20:32+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/python-argparse-1.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/python/python-argparse/","tags":["Python","Python 系統偵測","系統控制"],"title":"【Python 系統相關 #2】python 利用 argparse 使程式執行時可帶參數"},{"categories":["922 - Mac / MacOS"],"content":"前言 本篇作為一個簡單的紀錄，用來紀錄當購買「非特別針對 mac 設計的鍵盤」時，\n可能需要修改的鍵盤配置。\n特別是針對那種 cmd 不在想像位置上的，很困擾\n解法 我們可以去設定裡面，「鍵盤」-\u0026gt; 「鍵盤快速鍵\u0026hellip;」-\u0026gt; 「變更鍵」，\n針對單個鍵盤做客製化的配置。\n我自己修改後的設定如下，更接近 mac 鍵盤原來的位置：\n最主要是交換 Command 與 Option ","date":"2022-12-19T11:05:09+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-19-%E4%B8%8A%E5%8D%883.49.18.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/mac-keyboard-alt-cmd/","tags":["macOS"],"title":"【Mac】mac 上修改鍵盤配置，以適用一般鍵盤 (Corsair K70 for mac alt, cmd)"},{"categories":["114 - Python 字串處理"],"content":"前言 python 裡面我們有時會碰到超長字串的排版，\n這時我們應該要怎麼處理才會讓排版好看呢？\n以下是我搜尋找到的結果，作一個紀錄。\n解法 \u0026amp; 範例 我們可以透過括弧 ()，中間擺上各種字串，最後會自動接在一起。\n原本的樣子 a = \u0026#34;This is a long long long long long long long long long long long long long long long long long long string.\u0026#34; print(a) 修改後 a = (\u0026#34;This is a long long long long long \u0026#34; \u0026#34;long long long long long \u0026#34; \u0026#34;long long long long long \u0026#34; \u0026#34;long long long string.\u0026#34; ) print(a) 實際執行結果 Reference Multiline f-string in Python ","date":"2022-12-19T02:38:20+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/%E6%88%AA%E5%9C%96-2022-12-19-%E4%B8%8A%E5%8D%882.37.08.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/python-f-string-two-line/","tags":["Python","字串處理"],"title":"【Python 字串處理 #2】python f-string 長字串的排版處理 / 多行排版 (f string two lines)"},{"categories":["198 - Python 問題解決"],"content":"前言 這是我在設定 python flask on vercel 發生的問題，\n錯誤訊息如下：\nif not issubclass(base, BaseHTTPRequestHandler) 解決方法 這是我個人的解決方法，不一定每個人都適用，\n最主要發生問題的原因是因為我在設定 linebot 的時候，\n有一個變數名稱 \u0026ldquo;handler\u0026rdquo; ，似乎會被認為是要使用 BaseHTTPRequestHandler 作為開發，\n而非使用 flask，\n只需要換掉此名稱 \u0026ldquo;handler\u0026rdquo; 就可以解決問題，我自己是換成 \u0026ldquo;line_handler\u0026rdquo;\nReference me\n","date":"2022-12-14T02:03:29+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/vercel-if-not-issubclass-basehttprequesthandler/","tags":["Python"],"title":"【Python】問題解決：if not issubclass(base, BaseHTTPRequestHandler) vercel"},{"categories":["443 - vim"],"content":"前言 vim 是傳統終端機 (terminal) 經常使用的編輯器，\n學習 vim 能幫助我們在不用安裝視覺化軟體的情況下修改檔案。\n試著想想：如果今天要修改的電腦只有終端機 (terminal) 介面，\n那我們平常覺得超好用的 VS code、Sublime、Notepad ++ 等等的就都不能用了\n這時候我們就只能依賴 vim 來快速解決問題。(甚至是唯一的最佳解)\n開始 vim 編輯、用 vim 開啟檔案 當我們透過 vim 開始編輯一個檔案\nvim text.txt 就可以用 vim 編輯器來開起「text.txt」這個檔案了！\n基本模式介紹、與儲存檔案 vim 本身有多種模式，新手/初學者建議只需要記得有：\n指令模式 (command mode) 編輯模式 (insert mode) 指令模式 (command mode) 預設進入是「指令模式 (command mode)」，此時按一下「i」，\n即可進入「編輯模式 (insert mode)」。\n在指令模式中的指令，就是我們此篇文章下面表格所要介紹的東西。\n存檔 (save) 或 退出 (quit) 在「指令模式 (command mode)」中，\n存檔並退出 (write and quit) : 輸入「:wq」 退出 (quit force) : 輸入「:q!」 (第一次使用 vim 被困在裡面的工程師，請看這邊XDDD) 當然也是可以多記「:w」只存檔，「:q」純離開 (需在沒有編輯過的情況)\n編輯模式 (insert mode) 當在「編輯模式 (insert mode)」時，想要退出編輯模式請按「ESC」，\n「編輯模式 (insert mode)」就與一般文字編輯器一樣，自由輸入自己想輸入的內容。\n個人常用功能整理，以下指令皆是在「指令模式 (command mode)」中使用 檔案相關，基本操作 (存檔、退出) 第一次使用 vim 被困在裡面的工程師，請看這邊XDDD\n指令 功能 備註 記法 :wq 儲存檔案並退出 鍵盤快速鍵「ZZ」(注意 shift 大寫) write quit :q! 強制退出 鍵盤快速鍵「ZQ」(注意 shift 大寫) quit, 「!」: 驚嘆號代表 force 顯示相關 指令 功能 記法 : set nu 顯示行號(因為會顯示表情符號，:與s之間並沒有空白) | set num :數字 到第「數字」行 編輯相關 指令 功能 記法 u 還原 undo ctrl + r 下一步 redo yy 複製一行 yank nyy 從此行往下複製 n 行 yank p 貼上 (所以我常用 yyp 組合) paste dd 剪下一行 delete 搜尋相關 指令 功能 記法 / 搜尋 Reference 很棒的「入門」影片：\nhttps://www.youtube.com/watch?v=Nav8vpUfS7A\n【Vim 編輯器 入門指南 (上)】用思維的速度寫程式\n大家來學VIM（一個歷久彌新的編輯器）[四]\nVim复制一整行和复制多行\nAre there any commands aside from ZZ and ZQ that start with Z?\n","date":"2022-12-11T18:02:13+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vim/linux-ubuntu-vim/","tags":["Bash","Linux","Ubuntu","Vim"],"title":"【vim #1】vim 的 新手/初學者 的基礎使用指令 與 個人常用功能總整理"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在玩 Twitch-Channel-Points-Miner-v2 自動挖礦時碰到的問題，\n最主要是我覺得我的電腦丟 24 小時很浪費電又燒錢，\n不如丟去我租的 ubunut server 跑時，所發生的問題。\nubuntu server 環境果然很惡劣啊XDD 重點是也沒瀏覽器能開XD\nNotImplementedError: Unknown TwitchAPI error code: 5027 個人解法 以下只是個人解法，只確定對「我」是有效，對別人有沒有效我不知道。\n先找一台可以正常登入的主機，\n登入後，將 cookies 資料夾打包，丟至不能跑的主機 (也就是出現上面 error code 的主機)，\n換句話說，我們雖然不能在特定主機打開瀏覽器 (像我使用雲端沒有 GUI 的 server) ，\n我們依然可以透過現有的 cookie 直接執行他。\n更新 (2022/12/10) 後來發現只可以登入，但依然不能 claim 的問題\n透過修改 login 的方式，可以透過其他備案方式 login，\n並去 twitch 後台修改帳號設定，\n並且透過 2FA 的方式登入，可以順利 claim 點數，\n原因不明，這邊只是分享我試過可行。\n修改 login 方式可以參考此 PR: 自己試過可行\nhttps://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/606#issuecomment-1256839239\n或是另外一位網友提供的修改版本，可以試試看 https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2\nReference login error error_code:5027 NotImplementedError: Unknown TwitchAPI error code: 5027 ","date":"2022-12-10T02:39:22+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/unknown-twitchapi-error-code/","tags":["Linux"],"title":"【Linux】問題解決：NotImplementedError: Unknown TwitchAPI error code: 5027"},{"categories":["921 - iphone / iOS"],"content":"前言 iphone, ipad 上有時候，我會需要即時對伺服器進行一些操作，\n這時候能夠有個在手機上也能執行 ssh 的 terminal 就很方便了！\n這裡我們要來介紹 Termius\n取得 Termius app store 直接下載\n使用設定 step 1. 新增一個 Host step 2. 新增一個 Key step 3. 填上必要資訊與私鑰 這邊我是因為電腦上已經生成了私鑰\n直接把私鑰內容複製過來，貼在 private 上。\nName 自己好記的 key name 就好\npassphrase 看自己有沒有設定，沒有設定就空著\nipad Termius 設定 step 1. 新增一個 Host 點擊這兩個地方都可以新增 連入資訊，必要的有 Hostname (domain name 或 ip)、Username 登入的帳號、Key 可能是從電腦剛剛創建好的私鑰 step 2. 新增一個 Key step 3. 填上必要資訊與私鑰 這邊我是因為電腦上已經生成了私鑰\n直接把私鑰內容複製過來，貼在 private 上。\nName 自己好記的 key name 就好\npassphrase 看自己有沒有設定，沒有設定就空著\n這樣就可以連線囉！\nReference me\n","date":"2022-12-09T10:41:39+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/IMG_0028.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/iphone-termius-ssh/","tags":["iOS"],"title":"【IOS】在 iphone, ipad 上透過 Termius 執行 ssh，連線至遠端的 terminal"},{"categories":["420 - Docker"],"content":"前言 mac (intel chip) 安裝 docker 的過程筆記\nmac 安裝 dokcer 過程 step 1. 下載 去這裡，https://docs.docker.com/desktop/install/mac-install/\n找到 mac (我自己還是 intel chip，M1 系列請下載另外一個)，\n下載 Docker.dmg\nstep 2. 安裝 下載後，直接安裝即可。\nstep 3. 安裝後開啟 安裝後開啟 Docker Desktop，\n這裡 mac 應該會有一些系統權限要授權，記得授權。\nstep 4. 等待 Docker start 這裡進到 Docker Desktop 後，基本上他的教學都寫很清楚了，\n這邊就不重複教學。\n此外，如果對 Docker 已經很熟悉的話，選擇 skip tutorial 也是可以的。\nstep 5. 使用 Docker !!! 如果到這裡， Docker Desktop start 都一切順利的話 (需要等待他啟動一下)，\n再來應該就可以直接使用 docker 了！\n到這裡安裝就完成囉！\nReference Install on Mac ","date":"2022-12-09T01:26:53+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/mac-install-docker/","tags":["Docker"],"title":"【Docker #7】mac (intel chip) 安裝 docker 的過程教學筆記"},{"categories":["580 – Quantization"],"content":"前言 Quantization 作為模型加速一個非常重要的演算法，\n這邊整理一下\nQuantization 用途 Quantization 就是一個把連續值變成離散的過程，\n2.123 變成 2 之類的\n優點 將 float 轉成 int，\n可以達到計算加速 需要的儲存空間減少 (例如：float32, 4 bytes 變 int8, 1 bytes) 功耗減少 但相對的當然也有缺點\n缺點 因為有轉換的過程，就會產生誤差需要處理 (這其中也包含著轉成 int 後的數字有沒有足夠的解釋原始 float 數據的能力，此與分布有關係) Quantization 分類 主要分為兩類\nPost-training Quantization Quantization aware training Post-training Quantization 看到 「post-」，就知道這個是後處理的部分，通常是拿已經訓練好的 model 直接進行 Quantization\nQuantization aware training (QAT) 對比上面，這個就是邊訓練邊做 Quantization\n粗略比較兩種 Quantization Post-training Quantization 當然是最泛用，可以直接現有的就直接轉，\n但考慮到裡面資料的分布，直接作 Quantization 有可能導致解釋力不足，\n(可以設想一個情況，資料分布非常不平均，在 Quantization 後可能導致資料都被過度的集中，變成反應不出原始資料的分布)\nQuantization aware training 基本上就是訓練時就邊做 Quantization，\n比較麻煩，但出來的結果相對品質會更好 (如果追求 Quantization 後還要高的 accuary，也許就會採取這個策略)\nQuantization 基本概念 分成 Symmetric Quantization 和 Asymmetric Quantization (也有中文翻譯作對稱/非對稱)\nSymmetric Quantization (對稱) Symmetric 重點在公式皆滿足\nQuantize 公式：「X_quant = round(X_原始 / scale)」 依照下圖公式 (Dequantize)：「X_原始 = X_quant * scale」 (這也表示 zero-point 被限制等於 0)\nzero-point 正如其名，用來解釋「原始的哪個數值」quant 後會到 0 的位置，\nSymmetric Quantization 的好處就是在「我們不用處理 offset, zero-point 等額外的運算」，相對就比較簡化\nReference: https://arxiv.org/pdf/2106.08295 這裡也分為 unsigned int8 (0-255) 與 signed int8（-128 ~ 127)\n不過我們應該是可以先不用管這麼多，基本上原文提到 unsigned 是專門用來處理 ReLU 那種只有單邊數值的數據。\n很明顯的缺點就是如果左右的數據一邊大一邊小，比較小那邊的解析度會被另外一邊害到 (因為 0 已經被固定)\nAsymmetric Quantization (非對稱，最常見，重點！！！) Quantize 公式：「X_quant = round((X_原始 / scale) + zero_point)」 依照下圖公式 (Dequantize)：「X_原始 = (X_quant - zero_point) * scale」 相比 Symmetric，從整個數據的分布去考量，zero point 不在限制為 0，\n因此整個數據的分佈可以更合理！\nReference: https://arxiv.org/pdf/2106.08295 這邊我們推算一下，\n如果 Quantize 公式是 「X_quant = round((X_原始 / scale) + zero_point)」，\n原始的最小值，在除以 scale 後加上 zero_point 應該要位於 quant 後最小的位置，也就是 0\n所以 0 = round((X_原始 / scale) + zero_point\n「X_原始_min = -zs」就這樣推算出來\n而我們也可以知道\n「zero_point= -round((X_原始 / scale) 」\n但這樣好像有點難看，於是又有了一個 offset 的詞，像是把原來的範圍移動的感覺，\n因此 offset = round((X_原始 / scale) = -(zero_point)\n名詞解釋 zero-point zero-point 正如其名，用來解釋「原始的哪個數值」quant 後會到 0 的位置，\nSymmetric，zero-point 被固定在 0\nAsymmetric，zero-point 可以反應在「X_原始 / scale」要加上的偏移量 (移動的感覺)\n留意 Asymmetric 公式：「X_quant = round((X_原始 / scale) + zero_point)」\noffset offset = -(zero-point) offset，中文是偏移量，但中文不重要我自己多了中文的解釋反而更亂，解釋是用作調整數據範圍「至新的中心點」\n說實在我也常被這個詞越搞越亂，現在我也只記他好像是 zero-point 的相反，\n然後把 zero-point 定義記好 「原始數據 quant 後 0 的位置！」\n然後 X_原始_min = -sz ，這樣 quant 後才會等於 0。\nbit-width 有幾個 bit 能用來表示，常見的 b = 8 (bit)\n能表示範圍 = 2^8 - 1 = 255 個數值\nscale 縮放的比例，因為概念相對直覺，這邊比較晚提到，\n通常計算是\nscale =(max - min) / (2^b -1)\nmax-min 可以算出全部的數值範圍 2^b -1 代表著用於表示的全部數字 除完就可以得到每一個間距大概要抓多少了。\ndynamic range 直翻可能是動態範圍，不過中文不重要，\n這表示範圍太大可能導致解析度差(表示力不足)，\n簡單想像：「超級大的範圍只用 0-255 表示？」\ndynamic range 與 scale 的關係 想一下就會發現 dynamic range 其實跟 scale 息息相關，\n因為 scale 大才能反應出大的 dynamic range，\n通常可以有個判斷的依據 (但實際還是要是情況而定)\nscale \u0026gt; 0.5 (dynamic range 太大了)\nscale \u0026lt; 0.01 (dynamic range 很小，有可能還滿精準的)\n(警告) 此區對觀念沒把握的不要亂看，是我自己的混淆區，亂看可能跟著混淆 Quantization 與 Normalization? Quantization 的結果是離散的 (discrete) Normalization 的結果依然是連續的 (continuous) Quantization 的目的是讓連續的值，轉換到「有限範圍的離散值」，目標是減少儲存空間、加速運算等 Normalization 的目的是調整數據的範圍，有可能是轉換到「0-1 或 -1~1」，目的是確保「在不同的維度上有一致性」，也可以是為了權重的相等 Quantization 的「離散」結果，會產生一些資訊上的誤差 Normalization 的結果只是比例上的縮放，讓資料範圍保持一致，資訊保持著一定的比例，很少會額外增加誤差 Reference https://docs.nvidia.com/deeplearning/tensorrt/tensorflow-quantization-toolkit/docs/docs/intro_to_quantization.html https://intellabs.github.io/distiller/algo_quantization.html https://chih-sheng-huang821.medium.com/ai%E6%A8%A1%E5%9E%8B%E5%A3%93%E7%B8%AE%E6%8A%80%E8%A1%93-%E9%87%8F%E5%8C%96-quantization-966505128365 https://arxiv.org/pdf/2106.08295 ","date":"2022-12-09T00:11:34+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/%E6%88%AA%E5%9C%96-2023-12-09-%E4%B8%8A%E5%8D%8812.24.13.png","permalink":"https://wongwongnotes.com/posts/ai/580-model-optimization/quantization/quantization-1/","tags":["Quantization"],"title":"【Quantization #1】Quantization 基本概念筆記 (Symmetric, Asymmetric, QAT, scale, zero points)"},{"categories":["310 - Linux 基礎指令"],"content":"前言 linux 中有個指令是 cut，\n透過活用 cut 我們可以，我們可以在終端機印出的 log 進行訊息剪裁，\n可以得到更乾淨、或我們想要看的結果。\ncut 這裡先舉例我自己會常用的方法，更完整的用法可以再去自行參考文件。\n保頭 (去尾) cut -c -N -N: 可以想像成 「～N」的樣子，表示從頭到 N 保尾 (去頭) cut -c N- N-: 可以想像成 「N～」的樣子，表示從 N 到尾 ","date":"2022-12-07T02:22:43+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-cut/","tags":["Linux","基礎指令"],"title":"【Linux 基礎指令 #5】cut - 擷取特定在 terminal output 輸出的段落"},{"categories":["360 - Linux 網路遠控"],"content":"前言 最近有個需求是我使用 python flask 開發一個網頁，\n我已經有租用一個網路的伺服器了，但現在不知道該如何把\n「localhost:8888」 轉移至 「\u0026lt;public_ip\u0026gt;:8888」\n或是「\u0026lt;domain_name\u0026gt;:8888」\nflask 的修改 我們在撰寫 flask 的時候，通常會指定 127.0.0.1，代表 localhost，\n我們現在要轉成 public 也可以使用，我們需要先取得 public ip。\n取得 server 的 public ip 這邊我要感謝 ChatGPT\u0026hellip;\n對於我這種不夠精確的問題還能正確回答\n一般的工程師可能也不見得那麼有耐心聽我敘述問題\u0026hellip;\n真的是太偉大的發明了！！！\ncurl http://checkip.dyndns.org/ Reference ChatGPT ","date":"2022-12-06T02:23:22+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/get-linux-server-public-ip/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #14】如何取得 linux server 的 pulibc ip，作為 port 使用"},{"categories":["360 - Linux 網路遠控"],"content":"前言 VNC 是一種透過協定可以遠端連線的軟體，\n我們可以透過 VNC 來進行遠端桌面的連線。\n透過 VNC 連線至雲端的 ubuntu server GUI 上 (remote 無法連線螢幕的狀況) ref: [How to Install and Configure VNC on Ubuntu 22.04](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-vnc-on-ubuntu-22-04) 由 DigitalOcean 出產的文件，非常詳細的優質說明文！！！ 裡面的步驟先照著做，\n最後我們要注意這個建立 ssh tunnel 的部分，\n上面的介紹中是寫\nssh -L 59000:localhost:5901 -C -N -l \u0026lt;remote_user_name\u0026gt; \u0026lt;remote_ip\u0026gt; -l: remote 的 user name 其中 5901，是因為 remote VNC 使用了 port 1，對應到 5901，\n而我們 local 使用 59000 去建立通道，\n因此下方針對 localhost 的連線，會看到我們都下「localhost:59000」\nmac 如何透過 VNC 連線到遠端主機 ref：Connecting using VNC from a Mac computer to a Linux server 我們可以透過 finder 上方的 Go，\n選擇 connect to server，就可以連線至 VNC 遠端桌面。\n伺服器輸入\nvnc://localhost:59000 Reference ","date":"2022-12-03T02:53:31+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/linux-vnc-remote/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #13】透過 VNC 連線至遠端桌面 (在遠端可能沒有螢幕的情況下)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在測試雲端 docker GUI 時，發生了無法在 container 打開 browser 的問題\n並出現以下錯誤\nFailed to execute default web browser. input/output error 問題成因 不是很清楚，總之就是透過 docker 建立一個含有 GUI container 感覺還是有很多 bug\n解決方法 執行 chrome 的時候，改為輸入\ngoogle-chrome --no-sandbox 附錄：安裝 chrome 的方式 # download chrome.deb wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb # install chrome sudo apt install ./google-chrome-stable_current_amd64.deb # use it google-chrome Reference Failed to execute default web browser. input/output error ","date":"2022-12-03T02:45:38+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/failed-to-execute-default-web/","tags":["Linux"],"title":"【Linux】問題解決：Failed to execute default web browser. input/output error"},{"categories":["340 - Linux 系統控制"],"content":"前言 htop 在 linux 是一個很好用的效能檢測工具。\n安裝 sudo apt-get install htop 結果範例 終端機輸入\nhtop (有些應用因為安全關係，下面的截圖就不截了，實際上不只這樣。)\nReference Linux 的 htop 系統狀態即時監控指令工具使用教學 ","date":"2022-12-03T01:56:53+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/%E6%88%AA%E5%9C%96-2021-10-10-%E4%B8%8B%E5%8D%882.36.28-1024x112.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-ubuntu-htop/","tags":["Linux","系統控制"],"title":"【Linux 系統控制 #9】htop - 在 Linux (ubuntu) 上查詢電腦的資源使用情況, 比內建的 top 更多資訊！"},{"categories":["420 - Docker"],"content":"前言 我們通常使用 container 來方便我們管理環境、並大量部署同樣的系統環境\n不過雖然我們大部分的操作只需要在 terminal 即可進行。\n有時候還是會覺得有些麻煩，畢竟 terminal 少了一些 GUI，\n操作上的直覺性就少了那麼一些 (對新手來說可能更是XD)\n這邊分享一個能夠建立有 GUI docker 的方式\n建立有 GUI 的 docker 桌面 ref：Ubuntu 20.04/18.04/16.04 Multi User Remote Desktop Server 基本上可以依照作者內文的方式建立好 container\n架構 在 docker run 時我們有注意到，port 的指定，\n我們可以分析成上圖，並觀察對應 port 的處理與功能。\n去 docker hub 下載並建立好環境 下面最直接去拉 danielguerra/ubuntu-xrdp:20.04 的這個 image，\n不需要額外去 github\ndocker run -d --name uxrdp --hostname terminalserver --shm-size 4g -p 14389:3389 -p 14322:22 danielguerra/ubuntu-xrdp:20.04 一些細節的設定可以自己調整\n\u0026ndash;shm-size 建議 1g 以上免得瀏覽器跑不動\nwindows 連線至 xrdp 可參考以下操作，解釋的很清楚了這邊先不另外截圖\nref: Ubuntu 安裝可讓 Windows 遠端桌面登入的 xrdp mac 連線至 xrdp 可參考以下操作，解釋的很清楚了這邊先不另外截圖\n其中使用的軟體「Microsoft Remote Desktop」可於 Appstore 找到，\n有點有趣的是\u0026hellip; 居然用 Microsoft 的XD，Apple 不考慮自己也出一個嗎XD\nref: Connecting to XRDP from a Mac 附錄 透過 terminal 替遠端裝 chrome # download chrome.deb wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb # install chrome sudo apt install ./google-chrome-stable_current_amd64.deb # use it google-chrome 註：如果出現以下問題 Failed to execute default web browser. input/output error 請往這邊走~\nhttps://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/failed-to-execute-default-web/\nReference Ubuntu 20.04/18.04/16.04 Multi User Remote Desktop Server Failed to execute default web browser. input/output error ","date":"2022-12-03T00:29:51+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/xrdp.drawio.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/docker-gui-xrdp/","tags":["Docker"],"title":"【Docker #6】建立一個有桌面 GUI 的 container, 透過 xrdp 建立桌面, ubuntu server GUI"},{"categories":["440 - 編輯器使用 (VSCode...)"],"content":"前言 linux 更換「預設文字編輯器」，這篇基本上是我快被 nano 逼瘋後的產物，\n編輯器基本上我覺得專精一套就好，一套用到最習慣，就不用學那麼多套，\n這邊示範從 nano 預設編輯器換成 vim 的技巧，\n反過來也是可以的！\n那就來把預設編輯器 nano 修改成 vim 吧! sudo update-alternatives --config editor 會出現一堆選單，選擇有 vim 的選項，就完成囉!\n(沒有 vim 請先安裝 vim)\nReference Change the Default Editor From Nano on Ubuntu Linux How do I change the default text editor? ","date":"2022-11-24T02:09:17+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode-linux/linux-change-default-editor/","tags":["Bash","Linux","Ubuntu","編輯器使用"],"title":"【Linux 編輯器使用 #1】linux 更換預設文字編輯器，change editor from nano to vim (change default editor)"},{"categories":["416 - Git 協作相關"],"content":"前言 當 branch 一多，我們自然的就會產生非常多不同的 branch，\n有時候雖然還沒有 merge，但別人寫了一個感覺很好用的 function，\n這時我們要怎麼在「自己開發到一半的情況」，「又直接去拿別人的東西來用呢」?\n自己的理解方式：跟 merge 很像，但 cherry-pick 目的不在 merge，而是臨時借東西來用。\n用法 我們可以透過以下指令，去任何一個 commit/branch 拿東西\ngit cherry-pick \u0026lt;commit id\u0026gt; # (小技巧：不用打完，只要沒有重複，至少打四位都能智慧配對) 實驗與示範 這裡我們來實驗一下\n我建立了一個文件 test.txt，並依序 commit 了三次，commit message 對應內容。\ncommit 1 commit 2 commit 3 commit message 如下： 假設今天換我在另外一個 test_branch 開發，\n我寫下了\ncommit 1 commit 4 此時是這樣 但我覺得 commit 2 很好用，想借來用一下，\n這時我們就可以 git cherry-pick \u0026lt;commit 2 的 id\u0026gt;，\n如果照我的例子而言，就是： 註：任何的 git log 都是越靠近現在越下面，\n自己的記法：「太久以前的 git 就算顯示會被卷上去，我們基本上也不太會在意，因為我們幾乎只看最近的。」\n借一下 commit 2 來用 git cherry-pick 8ab9 # (小技巧：不用打完，只要沒有重複，至少打四位都能智慧配對) 到這邊已經達到我們想要的效果囉！！！我們拿到了想要的東西！！！\n此時可能會 confilct 的原因也很好理解，\n你寫的 commit 4 跟 commit 2 都要搶第二行，\ngit 也不知道要怎麼幫你組合順序，請你自己組合。\n如果是借 function 來用，這時你應該已經拿到了！可以放到想放的地方！\n而且正常來說應該順序就會影響邏輯。\n組合後就可以 commit 新的組合版囉！\nreference 【狀況題】如果你只想要某個分支的某幾個 Commit？ ","date":"2022-11-23T19:19:29+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/git-remote.drawio-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-collab/git-cherry-pick/","tags":["Git","協作相關"],"title":"【Git 協作相關 #2】git cherry-pick | 你的那招好像很厲害，可以借我學一下嗎?"},{"categories":["415 - Git 備份還原"],"content":"前言 這其實也是個 git 的面試經典考題，git reset 與 git checkout 都可以幫我們移動到某一個版本紀錄 (commit)上，\n那他們兩個有什麼差別呢?\n沒事請用 git checkout，git reset 很好用，但也很危險 版本紀錄本身是有「時間相依的」，因此我們不能夠任意的修改過去與未來的因果關係，\n例如：因為下雨，才撐傘，\n因為撐傘，傘才被風吹壞。\n我們可以得到：下雨 -\u0026gt; 撐傘 -\u0026gt; 傘壞\n然後我們不可以把歷史中的過程進行修改，變成「下雨 -\u0026gt; 傘壞」，整個因果都亂了，\ngit reset 的危險類似這樣的概念，因為「雲端存在這樣的歷史」，而「本地的歷史已經亂了」，導致無法合併 (不能 push) 的狀態\n如果有讀過我的其他 git 筆記，我的老師一直都有在強調一個觀念，\n沒事不要「亂 push 上雲」，除非你很確定現在的版本順序非常正確，\n因為「在 Push 之前」，我們可以輕鬆的用 reset 搞定上面的問題，但碰到的「remote 歷史不一致問題」，\n就會變超級麻煩\u0026hellip; 甚至我都有點懶得慢慢的處理這種問題\n圖解 為了方便理解，這裡我們把 commit id 替換成時間，更突顯出時間軸的概念\n我們可以從下面看出 git reset 與 git checkout 從 11:00 回到 10:00 時的差別\ncommit 當下最新的狀態 我們可以注意我們的 HEAD 與 master 都在最新的狀態\ngit checkout 主要有兩種用法 commit 與 branch，但都是讓目前的工作目錄回到指定的狀態，\nbranch 會是 branch 目前的 head，\n而我自己最常用的是去指定 commit id。\n小技巧：commit id 很長還要慢慢打不好用？其實可以不用打完，打前幾個字就會智慧的自動判斷了。\n如果前幾個字有出現重複，他也會提示你，通常再多打幾個字就可以確定那「唯一的 commit id」\ngit checkout \u0026lt;branch name\u0026gt; git checkout \u0026lt;commit id\u0026gt; checkout 後，我們暫時移動到過去的版本 這是我們最經常用的操作\n留下了 master，我們可以當作一個旗標，\n我們之後想回去的時候，只需要 git checkout master 就能隨時回去，\n因此這個旗子非常重要，不能隨便亂改，\n不然我們可能就回不去了！！\ngit reset git reset \u0026ndash;soft 簡單來說，就是回到 git add 後、git commit 前的狀態，\n也因此，最新的 master 會被移動，我們沒辦法透過 git checkout master 回去！\n「事實上，使用 git reset 也沒必要回去了，我們用 git reset 就是要消除最新的 commit」\ngit reset (\u0026ndash;mixed): default 簡單來說，就是回到 git add 前的狀態，\n除了不要最新的 commit，連 modified 的狀態也回復了\ngit reset \u0026ndash;hard 個人也非常常用的功能，直接把檔案完全還原成自己想要的「當時狀態」\n完全回到某個版本的狀態，連檔案也修改，\n然而這個指令也是最危險的，建議在進行此操作的過程中，\n完全控制在 local 使用，不要牽扯到 remote\n注意到了嗎? 「如果是 reset，11:00 的紀錄會因為時空回溯而消失！！！」\n你應該會想問，那如果 reset 我還回的去嗎? 可以，但前提是你必須要有他的 commit id，\n這可以用 git reflog 查\n可是既然都要回去了，為何不當初就用 git checkout 移動就好呢?\ncheckout, reset 兩者都有必要性 上面我們似乎講了 reset 很不好、很危險，那他的必要性是什麼？\n我們偶爾會做錯事情 (不要以為你很聰明，就不會做錯事XDD)\n像是不小心把「很大的檔案」加到了 commit 裡面，導致後續同步時變超久\n這時候這種「黑歷史」，何必讓他留在時間軸上呢?\ngit reset 就是專門砍「黑歷史」的工具，而如果在 local 的資料夾使用得宜，\n其實會是個非常好用管理版本工具，「砍掉任何不是重要里程碑的 commit、砍黑歷史」，都非常好用！\n因此如果我們想要，我們甚至可以做到：\n我覺得 10：00 的紀錄沒有價值，或者 11：00 更具有代表性，\n也能代表 10：00，因此 10：00 沒必要成為一個版本\n就可以透過 reset 改成這樣：\n但一樣的老話，這種操作，在 local 還非常容易處理，一但 push 到了 remote，\n就請先放棄吧，乖乖的用 checkout 處理讓黑歷史留在那邊。(除非太嚴重，但處理也非常麻煩)\n我們會因為 remote 可能會產生「時空倒流的行為」，必須回到「更早的時間點」，去「重新理順時空的因果」。\nreference Git reset 的三種模式( soft mixed hard )比較 【Git教學】Git 時光機回復版本的 2 種方法 reset \u0026amp; checkout 細說git reset和git checkout的不同之處 ","date":"2022-11-22T10:41:24+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/%E6%88%AA%E5%9C%96-2022-11-05-%E4%B8%8A%E5%8D%881.39.21.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-backup/git-reset/","tags":["Git","備份還原","初學者"],"title":"【Git 備份還原 #1】git reset, git checkout | 萬能又危險的 git 復原神器?! 不論怎麼樣的修改都可以還原的版本方式!"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下內容，問題的解決方式\nCannot overload functions distinguished by return type alone 問題來源 我寫範例程式碰到的問題\n解決方法 簡單來說是 C++ function 使用了 overload，\n而這個 overload 造成了 compiler 混淆的行為，\n這樣講我也覺得很不好懂，不如我們直接來看例子XDD\n範例 int foo(float a); float foo(float a); 現在問題來了，請問以下的 function call 會使用上面的哪一個呢?\nfloat a = 1.0; foo(a); 很難對不對，實際上就是沒有答案XDDD，\n因為 complier 就是不知道要用哪一個。\n解決範例 解決方法：就不要 overload 了吧\u0026hellip;\n另外也建議可以在 function naming 多下功夫\n例如： int float_to_int(float a); float float_to_float(float a); Reference What does \u0026ldquo;Cannot overload functions distinguished by return type alone\u0026rdquo; mean? ","date":"2022-11-21T23:01:53+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/cannot-overload-functions-distinguished/","tags":["C++"],"title":"【C++】問題解決：Cannot overload functions distinguished by return type alone"},{"categories":["416 - Git 協作相關"],"content":"前言 當 commit 一多，有時候我們只是修改了一行，甚至只是為美觀加一個換行，\n這時候我們就會想要「整理我們的 commit」，\n雖然說是隨時都可以整理，但最常發生在我們完成一個功能時，想要整理我們的 commit。\n方法有很多種，這裡我們介紹最萬用的 git rebase，\n至於 git commit \u0026ndash;amend 或是 重開新 branch 後 cherry-pick 等一些其他的方式，\n這裡因為不是文章的重點，我就只先留關鍵字給大家自己找啦XD\ngit rebase 用途 git rebase 可以看到「從\u0026quot;過去某個commit\u0026quot;的到\u0026quot;現在\u0026quot;，過程中的所有 commit」，\n我們可以重新整理這段時間的 commit 內容，\ngit rebase 用法 個人最常使用的是\ngit rebase -i \u0026lt;過去某時間點的 commit id\u0026gt; 其中 i，代表 Interactive，我們會開啟一個互動的視窗，讓我們可以調整 commit\n範例 我們寫了三個 commit 1,2,3 也對應到他們分別的 commit message，\n我們可以用 git log \u0026ndash;oneline 快速看一下 (oneline 就是只顯示一行，也就是我們的重點內容)\ngit log --oneline 上面我們可以看到我們的三個 commit 對應到他們的 commit id\n重組 commit 我們針對 commit 1 輸入以下指令，\n我們就可以針對到現在的 commit 2,3 進行 commit 重組，不包含我們輸入的 commit 1\ngit rebase -i 72f1 # (不用打完整，也能智慧搜尋) 然後我們就會進到以下的互動式畫面，這裡其實下面都有很明顯的提示用法了，\n我自己最常用的操作是 pick, squash(與前一個組合)，\n在這個例子中就可以改成：\n原本進來看到都會是 pick，表示這些 commit 都有被使用。 時間的關係是越下面越新，記法：太舊的 commit 相對比較不重要了，因此被顯示上被卷上去也沒差\n我們把第三個 commit 寫 squash (也可以簡寫 s，參考下方提示)，這個時候我們就會把 commit 2,3 合併成一個，\n而因為變成了一個 commit，勢必要重新計算 commit id，\n但 「commit 對文件的改動」是已經被合併成一個的。\n於是原本是長這樣的 commit 修改後，會把 commit 3 squash(合併)至 commit 2，變成這樣 但因為內容有所改動，commit id 需要重算，\n保留文件的修改與 commit messge (commit 3 整併至 commit 2)\n後話，與一些比較進階，這裡沒提到的部分 當然這邊也有比較進階的操作，但因為也是容易出錯的操作，\n這裡不會太詳細介紹，(比較針對新手夠用就好XD，像目前的我)，\n例如 drop 可以丟棄一個 commit，這裡的 commit 順序也能夠直接更改，\n這有時候會造成時空的錯亂，導致無法順利完成，\n「真的要操作的話，請務必注意時間上的相依性」\n也就是說，因為先有 A 才有 B，先有 B 才有 C，\n如果我們把 B 拿掉，照理來說 C 就不應該出現。類似這樣的問題很多也很難處理\u0026hellip;\n因此我通常也不太會用這個指令來整理，頂多就是開一個新的 branch，\n把最後的結果 cherry-pick 過來後建立一個新的 combine 後的 commit，\n讓 commit 看起來就很乾淨了 XD\nreference 【狀況題】想要刪除某幾個 Commit 或是調整 Commit 的順序 【狀況題】把多個 Commit 合併成一個 Commit 【狀況題】修改歷史訊息 Git怎么跳开中间的commit合并头尾的commit? ","date":"2022-11-21T01:46:17+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/git-remote.drawio-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-collab/git-rebase/","tags":["Git","協作相關"],"title":"【Git 協作相關 #1】git rebase - 重新整理 commit 的好幫手, 合併 commit"},{"categories":["412 - Git 本地指令"],"content":"前言 這篇是一個小筆記，\n用於處理「當我們只想修改最後一次 commit message 的時候」，\n為什麼會有這種情況呢? 我們有時候可能會有需要修改 commit message 的情況，\n例如：內容寫不夠清楚、放了不適合的內容(例如不小心把密碼打在 commit message 裡面，不處理可是會永流傳的啊！！！)\n用法 我們可以透過以下指令，開啟預設文字編輯器\ngit commit --amend 或是透過以下指令，直接輸入內容 (比較簡短的話)\ngit commit --amend -m \u0026#34;fixed message\u0026#34; reference How to modify existing, unpushed commit messages? 【狀況題】修改 Commit 紀錄 ","date":"2022-11-18T14:49:02+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/git-remote.drawio-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-local/git-commit-amend/","tags":["Git","初學者","本地指令"],"title":"【Git 本地指令 #3】git commit --amend | 修改最後一次 commit 的內容"},{"categories":["013 - Switch"],"content":"前言 前陣子受到一些朋友在家運動的刺激，\n結果拿起了我那已經長了灰塵的健身環，\n意外開始激瘦之旅（誤⋯⋯\n原來的運動習慣？ 平日偶爾會跑健身房，\n之前跑了快一年效果都蠻慢步調的，\n最近想想覺得之前下班回家已經累累不想再出門，\n就算很勵志要一週七天也是太拼\n（除非我隔天不想上班XD，請個雞肉酸痛假\n所以之前一週大概運動四天摸魚三天運動這樣?\n寫在攻略的最開頭 - 個人遊戲小 tips - 【密技】：等級 \u003e 技能 \u003e 裝備，等級可以戰勝一切 - 【技能】：「初期」打王推薦帶2-3個補血技 (可以循環補血硬扛攻擊等機會)，平常帶1-2個 - 【技能】：先開技能欄位(多屬性x單多體攻擊/回血技)\u003e心心最大值(容錯高)\u003e防禦無損機率提升(製造可攻擊空檔)\u003e被動技\u003e剩下 - 【錢錢運用】：錢建議都存起來拿來點技能，不要買商店裝備果汁(除非真的缺)，點技能很花錢，因為沒錢不能點技能會想哭QQ - 【裝備】：為了省錢點技能，盡量城鎮任務解到的裝備為主，減少購買的花費 - 【密技】：靠近怪物前一段距離如果使用空氣砲怪物會變大，如果想跳怪，盡量不要太遠就在亂射空氣砲 - 【萊納】：會跑的寶箱(那個很逼機的臉(X)可以用空氣砲，請萊納坐下來... 【素材】： (這遊戲居然也要農XDD) 建議至少打每個世界的最終 boss 都吃一下經驗兩倍，畢竟長期奮戰後的兩倍經驗真的很香 (試過就知道)， 其它有多就是小王或經驗跳跳也可以吃一下 (W7拿到經驗食譜後我只有打 boss 都固定會吃，之後拿到W15柿子果汁食譜就只需要刷柿子，刷素材單純一些再來大量刷) - 【素材雙倍】 芝麻果汁 - 商店買 - 【連續回合】 奇亞籽果汁 - 商店買 - 【經驗雙倍】 柿子果汁 W19/42/65 - 刷衝衝峽谷 - 【金錢雙倍】 紅茶 W16/39/62 - 刷哈巴瀑布 初期 (第一輪的世界 W1~W23) 重點攻略 - 重要的遊戲攻略里程碑 因為初期還有太多遊戲要素要解鎖，因此暫時還沒有特別農素材的部分，\n以「重點要素解鎖為主」\n初期攻略分成：\n重要靈環的能力/技能樹 解鎖 重要食譜 重要素材 我們會優先以上面三點，較為重要的部分為攻略目標！！\n純文字，照關卡順序流程版 W : 代表第幾世界\nW2 屬性攻擊加成（戰鬥顏色的能力） W3 製作果汁（果汁的能力） W4 划船（渡河的能力） W6 到此大約40等（技能樹5x5解鎖） 到此開啟技能樹後，我覺得才算是真正體驗遊戲的開始XDDD\nW7 胡蘿蔔湯食譜(經驗x2，刷素材：W5伊奇莫克山路(洋蔥)，W5卡克爾迴廊(胡蘿蔔)) W9 到此大約65等（技能樹7x7解鎖）、二段跳（高度跳躍的能力） 開啟二段跳之後，遊戲進入另外一個階段，基本上小怪能跳過都會跳過XDD\n(官方公認的偷懶法，雖然也是要消耗體力做這件事XDD)\nW12 飛行（翅膀的能力） W15 柿子果汁食譜(第二種經驗x2，刷素材：W19衝衝峽谷) W17 到此大約130等（技能樹9x9解鎖，開始最精采的四轉人生） 到此之後，就比較單純的在堆等級 \u0026amp; 點技能 \u0026amp; 刷素材了\n整理成以下大表 世界 重要靈環的能力/技能樹 解鎖 重要食譜 重要素材 W2 屬性攻擊（戰鬥顏色的能力） W3 製作果汁（果汁的能力） W4 划船（渡河的能力） 芝麻果汁(素材x2) W5 卡克爾迴廊(刷素材x2) W6 到此大約40等（技能樹5x5） W7 胡蘿蔔湯(經驗x2)、南瓜湯(金錢x2) 搖擺大門 - 蘿蔔和洋蔥(經驗x2) W9 到此大約65等（技能樹7x7）、二段跳（高度跳躍的能力） W12 飛行（翅膀的能力） W14 捲心菜濃湯(素材x2) W15 紅茶(金錢x2)、柿子果汁(經驗x2) W16 哈巴瀑布 - 紅茶茶葉(金錢x2) W17 到此大約130等（技能樹9x9） W19 衝衝峽谷 - 柿子(經驗x2) 經驗衣服 (經驗系列直接到底！) 經驗衣服也是本遊戲衝等的關鍵！！！\n有機會的話務必去解！！！\n全遊戲基本上整套經驗服攻略就可以了！ (我自己是這樣啦XD，畢竟等級就是一切！)\n全部經驗都是 +5％，因此只需要去注意更新「攻擊/防禦」即可！\n衣服 上衣(攻擊) 取得方式 下褲(防禦) 取得方式 鞋子(防禦) 取得方式 冷酷機械學者 77 W10任務 58 W10任務 19 W10任務 閃耀機械學者 179 (W19) $1560 + 紫水晶x3 134 (W19) $1170 + 紫水晶x3 45 (W19) $390 + 紫水晶x3 銀河系慢跑者 219 W22通關 165 W22通關 55 W22通關 W24 開始 冷酷機械學者 II 302 (W33) $1320 + 黃寶石x2 227 (W33) $990 + 黃寶石x1 76 (W33) $330 + 黃寶石x1 閃耀機械學者 II 368 (W42) $1740 + 芙蓉石x1 276 (W42) $1310 + 芙蓉石x1 92 (W42) $440 + 芙蓉石x2 銀河系慢跑者 II 395 (W46) $1860 + 鋯石x1 296 (W46) $1400 + 鋯石x2 99 (W46) $470 + 鋯石x1 W47 開始 銀河系慢跑者 III 400 (W49) $2000 + 黃水晶x1 300 (W49) $1500 + 鑽石x3 100 (W49) $500 + 黃水晶x1 冷酷機械學者 III 400 $2000 300 $1500 100 $500 閃耀機械學者 III 400 $2000 300 $1500 100 $500 技能樹配點順序 這邊借用網路上找的圖片，個人稍微加工一下\n「雙紅圈」：最一開始的目標，想辦法先點就對了！！！ 「紅圈」：第二目標，想辦法先點就對了！！！ 「藍圈」：次優先目標，我覺得好用的技能 「綠圈」：最後的優先目標，還不錯的技能 後期 (第二輪的世界 W24~W46) 重點攻略 (農素材) 這遊戲玩到後來就是農素材，爬等級的天梯了\u0026hellip;\n所以後期基本上也沒什麼特別的事情要做XD\n乖乖養成運動的習慣吧！\n最高 cp 值的經驗果汁 - lv 280 以後 另外，在280等(含)之後，升等經驗固定都會是10000，\n這時喝經驗果汁的cp值是最高的哦！\n可以參考以下大表： 大表 / Ring Fit Adventure Resource Sheet (Comments Enabled)\n農素材個人推薦地點 效果 果汁 素材 地點 經驗x2 柿子果汁 柿子 W19/42/65 - 刷衝衝峽谷 金錢x2 紅茶 茶葉 W16/39/62 - 刷哈巴瀑布 素材x2 芝麻果汁 商店買「白芝麻(x2)」、「香蕉(x1)」合成「芝麻果汁」 連續回合 奇亞籽果汁 商店直接買「奇亞籽果汁」 刷柿子示範影片 https://youtu.be/h6K22e91kfQ\n技能樹點滿 lv 289 (不喝技能藥水) 如果不喝技能藥水的話，技能樹會在 lv 289 點滿\n點滿後的畫面 大後期 (第三輪的世界 W47~W69) 重點攻略 你可能很困惑? 為什麼不是「前、中、後期」，而是「前期、後期、大後期呢?」\n因為我覺得這遊戲大部分人應該玩到前期就放棄了\u0026hellip;\n再來可能好不容易通關的人，玩到後期(第二世界)，可能也沒過多久也會放棄XD\n因此真的能玩到第三世界 (大後期) 的人少之又少，真的很佩服能有這樣的毅力!\n這個階段會全技能解放第四階段，「攻擊力統一相同，全部 CD 1」，\n因為攻擊力都一樣，而且都 CD 1，就可以開始專注想練自己想練的內容了!\n遊戲心得 「實力不夠就靠毅力來湊」\n從出生到現在實力比我強的人很多，\n毅力比我強的(跟我一樣瘋子)還真的沒見過幾個\n健身環的心得之前已經說過了XD\n在家真的方便，衣服都不用打理很棒！（欸\n另外我必須說任天堂真的不會行銷遊戲，\n第二輪故事的《八點檔劇情》比第一輪的《王道邪不勝正》劇情好看太多了，\n但我相信正常世界上絕大部分的人都不會玩到第二輪XDD\n那邊的劇情應該是要先撐得住的人才看得到\u0026hellip;\n到底是沈迷遊戲還是沈迷健身??\n最後，分享一些健身環的優點？ 1. 在家運動可以不穿內衣內褲(不是，你聽我說)，但是去健身房一定要穿！ 出門真的很麻煩，在家運動沒人管，我考慮之後退掉月費了，要錢而且通常也不會到每天出門www(平時到家到家後出門真的很麻煩，特別是還要換運動服\n2. 在家運動沒有懶得出門的理由，特別現在又疫情加成 因為都在家，沒有再懶惰的理由，\n我就是懶，每天假設一定要耍廢個3個小時，\n原本去上班通勤回家耍廢完就沒有時間了，現在都在家，\n原本耍廢的時間一樣，但又多出了好多時間，自然就沒有理由懶下去了?\n3. 趣味性 如果標準的幾組幾下做得很無聊，加一點娛樂效果真的會好滿多的，而且沒有偷懶的理由（因為偷懶怪物會打不死?‍♂️\n4. 第二種成就感 除了健身本身就有身材變好的成就感之外，練等級本身也能製造額外的成就感，而且還是「即時回饋」的，立即收獲成就感就不會無感而堅持不下去?\n5. 強迫症福音 你懂玩遊戲經驗值99%的痛苦嗎？可惡當然要升等才關遊戲啊！\n健身房想休息就停了，為了遊戲就會多動一點（然後就發現不只一點，可惡的強迫症），平常只做30下棒式都能做50下\n回答大家的問題 1. 我所堅持的每天150~200kcal是什麼概念？ 我每天運動完瑜珈墊全部都是濕的，都是我的汗，我覺得不雅觀所以沒有照片XD，但我是運動到每天都要洗瑜珈墊的程度，不然那個汗味一定會發臭 (另外這個程度是我自己訂的，不要像我一樣沒事當抖M\n正常人一天運動50我覺得就差不多了 ^ ^\n2. 怎麼堅持下去的？ 跟您分享我的四個毒品：\n? 數字強迫症（沒有150, 200 不會停啦！）\n? 等級強迫症（再…再一等，我想要點那個技能！）\n? 抖M的心（不會因為累了就放棄，搭配強迫症效果更佳）\n? 每天發文吵人的心（大家在家都好無聊，好好的廢文不發一下娛樂大家嗎（並沒有\n? 還有一個久了體認到很沒用的，減肥的心（很沒用，因為大家都有這樣的心，然後也很容易淪為只是口號QQ，習慣了就會放縱了）\n? 還有就是上一篇都發了勵志廢文，總是要堅持完成的吧XDD\n3. 瘦了多少? 14 天裡面最高峰就是瘦了6kg，不過有時候灌一下水又回來了1-2kg，我的天ಥ_ಥ\n最後感謝大家被我洗版與提供問題? 我會繼續努力洗下去的? 但還是希望疫情大家都能平安，回歸正常的生活（體重就不用回歸了)\nReference [心得] 健身環大冒險Lv.999封頂+攻略+心得 文長 素材 / 【問題】健身環大冒險 食譜全收集疑問（內有全食譜英日文連結） 食譜 / 【心得】健身環大冒險：靈環沒告訴你的事Part2 大表 / Ring Fit Adventure Resource Sheet (Comments Enabled) 特別感謝其他玩家的紀錄，作為攻略的查詢很實用 健身環大冒險 # 37 冒險模式 19 機械人之國(前篇) 健身環大冒險 # 60 冒險模式 33 EXTRA健身10 以下網站內文撤掉了，要用 waybackmachine 去看\nスムージー一覧 素材別入手ステージ ","date":"2022-11-14T10:56:35+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/IMG_0138.png","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/switch/ring-fit-adventure/","tags":["Switch"],"title":"【Switch】 任天堂 - 健身環大冒險 全攻略重點筆記"},{"categories":["899 - Debug / Error"],"content":"前言 以下是個人解決問題的分享，不見得對每一個人都適用，\n如果有剛好幫助到您，歡迎留言讓更多人知道此方法是有用的!\n對於這個螢幕，我的開箱文： https://wongwongnotes.com/posts/life-and-work/reviews/reviews/s34a650uxc/\n問題說明 以下是我自己的三星螢幕碰到「眼睛保護模式不可用」，螢幕顯示的狀態\n解決方法 如果也是 windows 電腦，可能不小心調到 windows HDR 模式了，\n這樣會導致螢幕沒辦法照原本預期的方式成色，\n(這邊應該也可以推測，這個「護眼模式」是做在系統裡的\u0026hellip;? 不是做在螢幕上的?)\n請把下面的「使用 HDR」關閉 就修復完成囉! ","date":"2022-11-13T20:26:23+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/img_0708-scaled.jpg","permalink":"https://wongwongnotes.com/posts/cloud-iot/other/debug-error/s34a650uxc-eye-saver-error/","tags":["Debug","Error"],"title":"【問題解決】三星螢幕護眼模式無法使用、眼睛保護模式不可用 | S34A650UXC 34型 21:9 WQHD高解析度曲面顯示器 (螢幕S6) - SAMSUNG 三星"},{"categories":["122 - Python conda (anaconda)"],"content":"前言 anaconda-navigator 是 anaconda 提供的 GUI 介面，\n對於還不熟悉以終端機 (terminal) 操作的新手們來說是很方面的工具！\n但是 ubuntu 的環境中安裝 anaconda 不一定像 windows 或 mac 直接找得到 anaconda-navigator，\n這篇文章提供指令的方式可以將 anaconda-navigator 的介面呼叫出來。\n方法 如果是直接按照官方預設值安裝，沒有特別去修改安裝路徑，\n可以直接執行以下指令。\n(推薦寫成 bash 文件，並以 bash 執行，會方便很多。)\nsource ~/anaconda3/bin/activate root anaconda-navigator ","date":"2022-11-13T17:31:07+08:00","permalink":"https://wongwongnotes.com/posts/python/environment/python-conda-anaconda/linux-ubuntu-anaconda-navigator/","tags":["Python","Ubuntu","conda"],"title":"【Python conda #2】linux/ubuntu 下執行 anaconda-navigator 的方法"},{"categories":["120 - Python 環境管理"],"content":"前言 這篇是我在幫我弟解作業環境問題時，意外發現，\n普遍網路上的解法都「比較少提到」卻「非常重要的」python 環境問題\n因為這邊我只淺談，但會把重點都講清楚，\n在我們的電腦裡面，會有很多的 python 環境，\n而初學程式的人大部分應該都對 python 環境沒有概念，\n而且會「誤以為是自己程式碼打錯了！」，\n頓時就覺得程式太難了\u0026hellip; 失去了信心，這其實是很可惜的！\n但學校往往也不會教這東西，因為這不是教學的重點，\n這概念有點像，我要你學 photoshop，可是你處在一個連 photoshop 都裝不起來的狀況，\n這不能算是學生的問題，卻難以算上是「學校應該要教的重點部分」，\n因此此類大部分問題，最後都會淪為「助教來協助處理同學們的 python 環境」。\n而比較害羞的學生，可能誤以為自己學不會程式，太難了，也沒機會找助教詢問，\n照上面的比喻來說，連 photoshop 都還沒開始學，就因為 ps 裝不起來放棄了，這真的很可惜。\n問題內容 執行 python 時出現以下錯誤\nModuleNotFoundError: No module named \u0026#39;cv2\u0026#39; 常見解法 pip install opencv-python 結果還是解決不了? why? 難道網路上常見的解法都錯了嗎？\n其實他們都是對的，但這個問題是來自於「對 python 環境沒有概念」，\n導致「以為自己下的指令有用，其實沒有下在正確的地方」\npython 環境 每一台電腦裡面都可能有非常多的 python 環境，\n基本的電腦開啟終端機後，可能都會預設有裝一個，\n這時如果我們看一些教學，可能誤打誤撞去裝了 anaconda 之類的 python 環境管理工具，\n在不是清楚自己在幹嘛，而只是單純「照著教學做」，\n就會根本不知道自己會在哪個「python 環境裡面」，\n這會有什麼問題呢？ 問題可大了\n一個對於「python 環境」的比喻 假設 A 環境才會有 A 道具，B 環境才會有 B 道具\n我們想在 A 環境拿到 B 道具，想也知道這是不可能的。\n但我們上面卻就在做類似這樣的事情，\n我們安裝 (pip install) OpenCV 這個工具在「anaconda 環境」，\n卻嘗試在「系統的 python 環境」拿這個工具，怎麼可能拿的到呢？\n這也是為什麼，明明網路上的解法寫的都一樣，但你照著做不見得行得通的原因。\n沒錯，我弟就是照著老師的教學，老實的裝了 anaconda，\n也很認真的去網路上找到了上面的解法，結果搞了半天解不掉問題，\n甚至一度以為是自己的錯。但明明就不是，而且這還是應學習之外的東西。\n可以透過 VScode 快速檢查自己電腦裡面的「python 環境」 其實不一定要 VScode，這只是我個人習慣，\n我們的第一步，就是要先確認自己的電腦上可能有哪些「python 環境」\n我們先任意建立一個「.py」的檔案並儲存，\n這時候 VScode 會偵測到這是個 python 的程式，\n我們可以按右下角，「選擇直譯器」、或有可能是顯示「python 版本號」，\n點擊後，我們就可以看到目前系統所有的「python 環境」。\n點擊後，我們就可以看到目前系統可以選擇的「所有 python 環境」，\n就如同我上圖中顯示了我的很多「python 環境」，\n也分別安裝在我電腦中的不同位置，\n讀者 python 環境數量與我不同是正常的，因為這些都是我自己建立的。\n我們稍微觀察一下就會發現一些事情：\nglobal 顯示的路徑，與目前星星的路徑是一樣的。也都是 python 3.7.3，這就表示系統預設的「python 環境路徑」。 接下來有很多 「~/opt/anaconda3/\u0026hellip;」 開頭的，這都是透過「anaconda 這套 python 環境管理工具」所產生的 python 環境。 承上，我們再特別留意 base，這個是指「anaconda 中，最基本的環境」 我們接下來把上面圖示一下。\n用圖示的方式講解 python 環境 對照上圖，我的 python 環境在我電腦中大概長這樣\n看來我電腦裡面真的有很多「python 環境」呢，\n那麼問題來了，「請問你是把 pip install 裝到哪一個 python 環境呢？」\n如果答不出來也沒關係，這是幾乎每一位 python 初學者一定有碰過的問題，\n而且也不知道自己在幹嘛，可能就是照著做，\n而且通常學校、甚至公司也不太會教，因為也不知道該怎麼教\u0026hellip;\n所以現在讀者應該能比較清楚了，你下的 pip install 會沒有用，\n不是指令錯了，是因為裝錯了環境，\n你可以預設「每一個 python 環境預設都是各自獨立」的，\n裝錯了，就是找不到，就是會跳錯。\n該怎麼知道 python 環境 與 如何進入正確的 「python 環境」 這裡方法有太多種，我只想講一種我會比較推薦初學者使用的方式，\n畢竟這篇文章應該比較偏初學者向？\n終端機那些我覺得操作可能就比較複雜了，\n需要的人，應該也有能力自行另外找到資源學習了。\n首先一樣回到我們的 VScode，在剛剛的所跳出的所有環境選項中，\n直接選擇你想要的「python 環境」，\n選哪個環境比較好\u0026hellip; 這可以再細談，\n但注意： VScode 預設的「建議」我覺得並不是個「好建議」，\n可以的話我會建議選下圖紅色的 (如果有的話) 環境，\n因為「下圖紅色的環境，我們之後如果不需要開發的時候，砍掉是最不痛不癢的」，\n你可以想想如果裝在系統，之後要砍藍色的某一個東西，萬一不小心砍掉，\n但有某某程式要用，那個程式就不能用了，\n因此如果可以的話，我非常不建議安裝在藍色的地方。\n要刪除會有潛在的麻煩，但一直裝可能就會很佔空間。\n選擇 python 環境後，安裝指定的套件 這邊選好 python 環境後，我們點擊 VScode 上方，\n「終端機 -\u0026gt; 新增終端機」，\nVScode 很自動的會幫我們自動載入進去那個環境，\n這個時候下 pip install 才是裝在正確的 python 環境裡面。\n然後看到下面開啟的終端機，(可能會預先跑一些開啟 python 環境的指令，是正常的)，\n我們就可以輸入「pip install opencv-python」，\n才是把工具裝在「正確的 python 環境」裡面哦！\n這裡也要注意，以後每次開 VScode，都要記得要用一樣的方式「回到這個 python 環境」跑 python，\n不然就又是在「沒有 A 工具的環境，嘗試做 A 工具才能做的事」\n測試 這邊可以很簡單的測試，\n安裝好後，輸入 import cv2，能夠通過就是安裝完成了。\n預防萬一，怕有人不知道怎麼退出 python 模式，\n可以輸入「exit()」或快速鍵輸入「ctrl + z」，就可以退出囉！\nReference me\n","date":"2022-11-12T02:35:27+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/%E6%88%AA%E5%9C%96-2022-11-12-%E4%B8%8A%E5%8D%882.11.33.png","permalink":"https://wongwongnotes.com/posts/python/environment/python/python-environment/","tags":["Python","環境管理"],"title":"【Python 環境管理 #3】淺談 python 環境 與 python OpenCV 問題解決：No module named 'cv2'"},{"categories":["211 - C++ 基礎語法"],"content":"前言 unorder_set() 作為一種 hashset 是一個滿經典的用法，\n在 python 寫 set() 久了，換過來還真的有點不習慣，這裡稍微紀錄一下常見用法。\n這裡只紀錄個人會常用到的用法，因此這裡「不會是完整的使用教學哦」(要完整請直接看文件)\n初始化 unorder_set() 我們可以透過宣告一個 {}，代表建立一個空的 set()\nstd::unordered_set\u0026lt;int\u0026gt; myset{}; insert，插入值進 unorder_set() 這邊留意一下 set 的特性，重複的值插入是「不會產生第二個」的\nmyset.insert(1); myset.insert(2); myset.insert(2); myset.insert(3); count，\u0026ldquo;單純\u0026rdquo; 確認元素存在 這裡我覺得很神奇，居然是用 count，\n但 set 裡面最多就一個啊\u0026hellip;. 是要 count 啥?\n至少應該用 find? 不過 find 被另外拿去用了，下面會介紹， find 被拿去要來取出東西用的了\ncout \u0026lt;\u0026lt; \u0026#34;count 2 in myset = \u0026#34; \u0026lt;\u0026lt; myset.count(2) \u0026lt;\u0026lt; endl; // 1, 不會因為 insert 兩次變多 cout \u0026lt;\u0026lt; \u0026#34;count 5 in myset = \u0026#34; \u0026lt;\u0026lt; myset.count(5) \u0026lt;\u0026lt; endl; // 1 find，尋找元素 find 與 count 的不同，就是 find 能幫我們拿到元素存在 set 的 iterator 位置，\ncout \u0026lt;\u0026lt; \u0026#34;find 2 in myset = \u0026#34; \u0026lt;\u0026lt; *myset.find(2) \u0026lt;\u0026lt; endl; // 透過 find 取得 iterator，並用 * 取得值 遍歷 unorder_set 中的元素 遍歷也很常需要用到，通常都是我自己 debug 的時候要看看有什麼\u0026hellip;\n這時就要呼叫我們的 iterator 了，然後就會感覺到程式變成超長又不好讀\u0026hellip;\nfor (std::unordered_set\u0026lt;int\u0026gt;::iterator it = myset.begin(); it != myset.end(); it++) { cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; } C++ 11 以後的替代方案 謝天謝地的是，C++ 11 之後新增了 auto，可以幫我們處理掉前面那個看起來有點冗長的東西\u0026hellip;\n取而代之的就是，自己必須對 type 更加清楚，不然這會是個未來對 type 混亂災難性的開始\u0026hellip;\n把上面 type 的部分直接改成 auto 即可，\n個人是認為雖然程式碼更簡潔了，但更考驗工程師對 type 的理解程度\u0026hellip;\nfor (auto it = myset.begin(); it != myset.end(); it++) { cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; } 完整範例程式碼 void exp_unordered_set(){ std::unordered_set\u0026lt;int\u0026gt; myset{}; myset.insert(1); myset.insert(2); myset.insert(2); myset.insert(3); cout \u0026lt;\u0026lt; \u0026#34;find 2 in myset = \u0026#34; \u0026lt;\u0026lt; myset.count(2) \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;find 5 in myset = \u0026#34; \u0026lt;\u0026lt; myset.count(5) \u0026lt;\u0026lt; endl; for (std::unordered_set\u0026lt;int\u0026gt;::iterator it = myset.begin(); it != myset.end(); it++) { cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; } for (auto it = myset.begin(); it != myset.end(); it++) { cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; } } 結果： Reference std::unordered_set::find C++ std::unordered_set 用法與範例 unordered_set count() function in C++ STL ","date":"2022-11-11T02:55:31+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/%E6%88%AA%E5%9C%96-2022-11-11-%E4%B8%8A%E5%8D%882.34.00.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-unorder_set/","tags":["C++","基礎語法"],"title":"【C++ 基礎語法 #7】C++ unorder_set() 用法筆記"},{"categories":["212 - C++ 字串處理"],"content":"前言 當我們要比較兩個字串時，我們可以使用 strcmp\nstrcmp 用法 當我們有兩個 const char*，我們可以使用 std::strcmp 來比較兩者，\n依照第一個字開始比較，如果 apple 跟 book 會先比 a, b，\n前小：-1 相同：0 前大：1 範例 void exp_strcmp(){ const char *a = \u0026#34;apple\u0026#34;; const char *b = \u0026#34;book\u0026#34;; cout \u0026lt;\u0026lt; strcmp(a, b) \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; strcmp(b, a) \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; strcmp(a, a) \u0026lt;\u0026lt; endl; } 結果： Reference std::strcmp ","date":"2022-11-11T02:12:25+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/%E6%88%AA%E5%9C%96-2022-11-11-%E4%B8%8A%E5%8D%882.09.29.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-string/cpp-strcmp/","tags":["C++","C++ 字串處理","字串處理"],"title":"【C++ 字串處理 #5】C++ strcmp 字串比較 用法整理"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 對我來說個平板最理想的樣子，應該要是能夠攜帶出去的，\n仔細想想使用情境，在家裡明明就有「更大的電視」跟「電腦」兩種不同裝置，\n個人認為，一個人最多眼前就只能專心做一件事，並且背景放一件事差不多。\n所以基本上也不會想要在家裡用平板看東西 (有更大的螢幕了！)，\n在家的情境是這樣，外出時， 有時候手機正在使用，就沒有辦法做其他的事情，\n在外如果能同時有手機跟平板，這樣子分配「眼前工作」與「背景工作」，\n對我來說，就是最完美的分配了。\n因此這一台 iPad Mini 6，他的「便攜性」同時「兼具足夠的性能 (A15 !!!) 」，都是我心中認為平板最應該要有的樣子！\n購入價 我是 2022/11/6 在 apple 直營店買的，購入價 $19220\n(ipad mini 6 + Apple pencil 2, 不含殼)\n剛好是在漲價之後買到的啊\u0026hellip;QQ\n開箱 - 全部的樣子 一共有 iPad mini 6 + Apple Pencil 2 + UNIQ Camden 抗菌磁吸設計帶支架多功能極簡透明保護套，\n這是我們今天要開箱的內容！\n開箱 - iPad mini 6 外盒 △ ipad mini 6 的外盒\n△ 另外一個角度看看\n產品內容物 △ 把盒子打開囉！\n△ 包裝依然一貫頻果作風，精美又環保\n△ 背面的顏色，這個紫色很美，但有點難拍出來，建議想買的可以先去實體店看看\n△ 產品正面，這是第一個拿掉 home 鍵的版本，也是我認為平板最應該要有的樣子！\n產品其他內容物 △ 說明書\n△ 可能因為第一次換 type C，這次還有「附線跟充電頭」\n△ 這就是全部的產品內容物啦！！\n說明書 △ 讓我們把說明書裡面的內容拿出來看一下\n△ 圖文並茂的介紹，非常簡單易懂\n△ 最後再來拍一張他的背面！那神秘又好看的紫色！手機真的拍不出來！ 開箱 - Apple Pencil 2 △ 外盒的樣子\n△ 稍微打開一點，他是拉出來的\n△ 拉出來之後就長這樣啦！ △ 把說明書的內容都拿出來，全部的樣子大概是長這樣\n△ 筆上面還有多一層細心的保護，真不愧是蘋果一貫的細節\n開箱 - UNIQ Camden 抗菌磁吸設計帶支架多功能極簡透明保護套 △ 外盒的樣子\n△ 外盒拆開後，裡面的內容物 △ 產品的正面\n△ 打開來後，有一些簡單的基本保護\n△ 殼我是選透明的，這樣可以突顯 iPad 神秘的紫色\n組合起來囉！ △ 把保護殼跟筆都裝上去，並且開機\n△ 看到了新 iPad 的「你好！」\n△ 再拍一次背面的紫色，真的是很好看的紫色，雖然手機拍不太出來實體的感覺XD\n△ 正面的樣子\n△ 最後再來一張背後的樣子，紫色真好看！\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n「愛不釋手、相見恨晚」到現在才發文！\n這台是我第一台靠自己買的平板，是一個很神奇的產品\n「他真正符合我心中對平板的想像」\n發表後雖然討論一直都很少。在追求大、追求高效能的時代，\n我需要一台能陪我旅遊世界的「平板」兼「電子筆記本」\n這台就是「絕對夠用」且「目前唯一」的選項\n在這之上的大小，我完全不會想帶出門旅遊、上山下海，\n（用不到、帶不出門的平板對我而言就是 0，哪怕性能再好）\n而且超過這大小，我都覺得該叫做繪圖板不叫做平板XD\n還有在家我會用「電腦」跟「電視」，不用會用這個（買後也不愛奇藝\n不論是去爬山玩水，拿出來這樣的螢幕都比手機舒服，\n而且無可取代的 pencil 體驗，就是他無可取代手機的強項\n大概就是這樣，因為太少人討論，但用過，就是相見恨晚\n我之前腦袋也是 M1 來 M1 去的，果然蘋果洗腦真的很厲害\n不要自己創造需求，要知道自己真正的需求是什麼，\n「他也許不是最好，但他是最適合我的」\n大概是 John 的故事。之後遇到我，都會看到我與我的新小夥伴\n然後這紫色真的超美！但相機拍不出來，不同角度不同顏色都很好看！\n","date":"2022-11-06T19:05:11+08:00","image":"https://wongwongnotes.com/images/restored/2022/12/IMG_0632-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/ipad-mini-6/","tags":[],"title":"【嗡嗡開箱 #18】Apple - iPad mini 6 + Apple Pencil 2 + UNIQ Camden 抗菌磁吸設計帶支架多功能極簡透明保護套 | 不專業開箱文"},{"categories":["381 - Bash 基本語法"],"content":"前言 透過傳入參數，使我們的 bash script 更加具有彈性，\n能夠有更多客製化的設定\n傳入參數 我們在下指令時，後綴的內容依序為 $1 $2 $3\u0026hellip;\n我們可以妥善利用這件事情來撰寫 bash script\n範例 #!/bin/bash echo $1 echo $2 echo $3 結果： 我們在 script 後，直接輸入我們想要的變數\n./test_script.sh apple book cat Reference How to Use Command Line Arguments in a Bash Script ","date":"2022-11-05T01:04:35+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/%E6%88%AA%E5%9C%96-2022-11-05-%E4%B8%8A%E5%8D%881.02.24.png","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/bash-script-arguments/","tags":["Bash","基本語法"],"title":"【Bash 基本語法 #6】在 bash script 中加入可以從外部傳入的參數 (bash script arguments)"},{"categories":["411 - Git 觀念整理"],"content":"前言 我們來整理一些 Git 的基本知識\n檔案的狀態 討論 git 之前，我們可以先知道檔案在電腦中可以有四種狀態，\n我們可以用下面的圖作解釋\nuntracked files: 未追蹤的檔案 不在 git 控制範圍內的檔案，也就是說就算變化了，\n因為不在 git 所觀察的範圍，因此與我們無關\n例如：電腦中，與 git 無關的其他資料夾的檔案\nunmodified files: 未編輯的檔案 這個概念聽起來有點繞口，有兩種狀況會變成這個狀態，\n在 git 的範圍加入新的檔案，畢竟剛加入，就是個未編輯的狀態(也可以說已經編輯完了，成為一個定版的狀態) 當我們決定替現在的 git 目錄提交一個定版 (commit)，那麼現在的檔案狀態就是未編輯的狀態 (這裡「相對的基準點」就是定版的狀態，因此是未編輯的狀態) modified files: 編輯中、已編輯的檔案 一樣這個概念聽起來有點繞口，不過如果上面已經有理解了什麼是「未編輯的狀態」 (最難理解的點應該是在「相對基準點是定版」)，\n這裡就很好懂了，只要有修改，就是 modified files。\nstaged files: \u0026ldquo;等待\u0026quot;定版的檔案 這裡的概念與 modified files 類似，甚至有些重疊的部分，\n硬要分的話，一個檔案可以在定版前不斷的在 modified files 階段，也就是說我們可以一直修改他，\n直到我們想提交定版 (commit)，就會是 staged files。\n而在提交定版 (commit) 後，就會變回 unmodified files，因為「相對現在的定版狀態」，未編輯的檔案就是「沒有編輯的檔案」\ngit 中的版本控制 這裡與上面的概念有點重疊，但是又不太相同，\n上面我們提到了檔案的狀態，而這裡我們要看 git 怎麼樣的控制檔案的版本。\nrepository (常簡稱 repo) 我們先講 repository (常簡稱 repo)，基本上可以當作是檔案版本的保存庫\n暫時的定版 (git add) 與正式的定版 (git commit) 我們可以透過 git add，把檔案暫時都加入 staged files，\n注意我們上面有一個微妙的定義「\u0026quot;等待\u0026ldquo;定版的檔案」\n因此在 staged files 的這個階段，我們一直都沒有正式的上一個版本，一切都只是暫時(我們仍可修改)的狀態，\n「當我們確定現在的修改程度，值得作一個版本記號」，我們就會下 git commit 提交定版，\n也就是我們把 staged files 「正式的作為一個版本」保存在 repo 當中。\n與雲端交互 學習 git 的時候，可以先不用學習雲端相關的知識，\n因為在觀念還不穩時，加進雲端的概念只會更多更亂，\n然而，git 是完全可以在本地執行的，如果新手建議可以先嘗試在本地用 git 進行版本控制即可。\n與雲端交互的關鍵基本上就是 push 與 fetch (把本地推上雲端、把雲端拉回本地)，\n但在此操作之前「絕大部分的操作，都是要我們在本地先處理好，在上雲端的」，\n「請盡量不要上雲後再來處理，會比在 local 處理麻煩非常多！ 而 local 處理非常簡單！！！」\n上面圖片基本上可以反覆查看，而與雲端交互的部分基本上可以晚點看\n大原則就是「在 local 把一切都處理好，在一次 push 上雲。不要 push 上雲後再來後悔，會難處理很多」\nreference Git 版本控制 常用篇 Git 入門 (1)_創建版本庫及記錄修改 ","date":"2022-11-04T02:21:40+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/%E6%88%AA%E5%9C%96-2022-11-04-%E4%B8%8A%E5%8D%8810.18.32.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-concepts/git-file-concept/","tags":["Git","初學者"],"title":"【Git 觀念整理 #2】git 基本概念篇 | 讓我們來談談檔案的各種狀態與 git 的關係"},{"categories":["215 - Modern C++"],"content":"前言 這邊整理一下 C++ virtual, override 的筆記\noverride (C++ 11) 我們先講個在 C++ 11 新出現的小東西「override」，\n這個 override 語法上「可有可無」，\n但有的好處是\n- (重要) 當 Parent Class 沒有這個函數時，會報錯!!! - (提醒，可讀性增加) 提醒我們這個函數有覆寫 Parent Class 的功能，提醒我們要注意! 為什麼第一點重要呢?\n因為在 C++ 裡面有個功能叫做 overload (對，不是 override !!!)，\n白話文就是，我們可以有\nfoo(int) 我們在沒有寫 override 的情況下，如果在 Child class 寫了一個\nfoo(char) 是不會報錯的!!! 而且同時我們會有兩個函數可以使用\n來自繼承父類的: foo(int) 來自子類新增的: foo(char) 但如果寫了 override 就會報錯了!!!\n因為 foo(char) 在父類不存在!!! 因此就不會有上述的狀況!\n很棒吧!\nvirtual function: Parent 有實作, Child 可以覆蓋 or 直接用父類實作 在一般的情況下，我們可以在 Parent 有先實作一些功能，類似 default 的設定。\n如果子類有想要修改功能，可以直接 override 掉，也可以直接使用 Parent 的 default 設定來用。\n範例程式碼 #include\u0026lt;iostream\u0026gt; using namespace std; class Papa { public: virtual void foo() { cout \u0026lt;\u0026lt; \u0026#34;foo in class Mama\u0026#34; \u0026lt;\u0026lt; endl; } virtual void foo2() { cout \u0026lt;\u0026lt; \u0026#34;foo2 in class Mama\u0026#34; \u0026lt;\u0026lt; endl; } }; class Child : public Papa { public: void foo() override { cout \u0026lt;\u0026lt; \u0026#34;foo in class Child\u0026#34; \u0026lt;\u0026lt; endl; } }; int main() { Papa a; Child b; a.foo(); b.foo(); a.foo2(); b.foo2(); return 0; } 結果 Pure virtual function: Parent 沒實作, 並強制要求 child 實作 (常用於建立 interface) 通常用於建立 interface，我們不實作，但要求繼承的使用者實作。\n我們會用「= 0」作為函數宣告的內容。\n這裡要注意，含有 Pure virtual 的 Parent Class，不能建立 instance，會報錯。\n範例程式碼 #include\u0026lt;iostream\u0026gt; using namespace std; class Papa { public: virtual void foo() = 0; }; class Child : public Papa { public: void foo() override { cout \u0026lt;\u0026lt; \u0026#34;(Must do) foo in class Child\u0026#34; \u0026lt;\u0026lt; endl; } }; int main() { // Papa a; can not be instance Child b; // a.foo(); b.foo(); return 0; } 結果 Reference C++ virtual 的兩種用法 Virtual Function in C++ virtual function specifier C++中關於 virtual 的兩三事 Virtual Function in C++ ","date":"2022-11-01T15:41:18+08:00","image":"https://wongwongnotes.com/images/restored/2022/11/cpp-virtual-2.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/modern-c/cpp-virtual-override/","tags":["C++","C++ 系統偵測","Linux","Modern","Ubuntu"],"title":"【Modern C++ #3】 C++ virtual, override 筆記"},{"categories":["340 - Linux 系統控制"],"content":"前言 我們可以透過 xrandr 查看目前螢幕的解析度\nxrandr 範例 在終端機輸入 xrandr，我們可以看到目前螢幕的解析度，\n可以注意以下「current 的部分，就是目前螢幕的解析度」\nReference ","date":"2022-10-25T21:14:24+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/%E6%88%AA%E5%9C%96-2022-10-25-%E4%B8%8B%E5%8D%889.10.29.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-xrandr/","tags":["Bash","Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #8】xrandr - 查看目前螢幕的解析度，確認螢幕是否連接"},{"categories":["330 - Linux 檔案處理"],"content":"前言 取得路徑的筆記\npwd 作為取得「當前路徑」使用 只要在 terminal 輸入 pwd，就可以查看當前路徑\npwd realpath 可以查看絕對路徑 透過 realpath，我們可以取得檔案完整的「絕對路徑」\nrealpath \u0026lt;檔案\u0026gt; 範例 realpath ~/Desktop/note.txt # /home/ubuntu/Desktop/note.txt basename 可以查看檔名 basename \u0026lt;檔案\u0026gt; 範例 basename ~/Desktop/note.txt # note.txt Reference How to obtain the absolute path of a file via Shell (BASH/ZSH/SH)? ","date":"2022-10-24T16:44:40+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-realpath-pwd/","tags":["Linux","檔案處理"],"title":"【Linux 檔案處理 #8】realpath - 取得檔案完整「絕對路徑」，basename 取得檔名，用 pwd 取得現在 terminal 路徑"},{"categories":["433 - screen"],"content":"前言 我們有時候可能在某一台電腦 terminal 操作到一半，\n突然會有需要換一台電腦，但又想要接續操作同一個 terminal 的內容，\n這時候 screen 可以幫助我們無縫切換這件事情。\n(也就是說，在 A 電腦上面顯示的所有 terminal 內容，可以直接顯示在 B 電腦 terminal 上)\n只有 terminal 當下分頁的全部內容，並沒有其他的視窗哦XD\n要其它視窗，你想要找的應該是 anydesk, teamviewer 之類的XD\n另外一個個人覺得很大的優點 當如果是使用筆電操作遠端 terminal 的時候，\n如果當要移動，筆電合起來勢必「連線會中斷，導致任務中斷」\n使用 screen 就可以「讓任務在背景持續進行，就算要移動也不怕中斷任務」，\n而且可以「隨時再啟動同樣的畫面！！！」\n安裝 screen sudo apt-get install screen 使用 screen 啟動新的 screen 很簡單，輸入 screen 會自動啟動一個新的 screen\nscreen 啟動新的 screen - 並自定義 screen 名稱 我們可以使用「-S」，宣告 screen 的名稱，\n「-S」後面的字串，代表自己定義 screen 名稱，\n方便在 「screen -ls」 時查詢是哪一個 terminal\n記法：\u0026ldquo;S\u0026quot;creen (不過原意是 sockname)\nscreen -S \u0026lt;name\u0026gt; 暫時離開 (detach) 一個 screen 記法：ctrl + A：可以當作萬用功能 function key\nctrl + A, D # 記法：detach 顯示目前所有正在執行中的 screen screen -ls 重新進入 (回復工作) 一個 screen 重新進入最近離開的 screen session screen -R 重新進入指定的 screen session screen_id：可透過 \u0026ldquo;screen -ls\u0026rdquo; 取得，不用完全輸入，可以只輸入至 id 就找得到了\nscreen -r \u0026lt;screen_id\u0026gt; 砍掉 screen 砍掉單一 screen 進入某個 screen 後\nctrl + A, K # 記法：kill 砍掉所有 screen pkill screen 想要知道更多指令 這裡只有列出個人常用功能，如果想知道更多可以自行閱讀以下網站：\n延伸閱讀：使用 Screen 指令操控 UNIX/Linux 終端機的教學與範例\n延伸閱讀：IT｜作業系統｜Linux｜使用 screen 指令操控 unix/linux 終端機\nReference kill a screen session Setting a name for a screen session ","date":"2022-10-23T16:22:39+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/screen/linux-screen-terminal/","tags":["Bash","Docker","Linux","Ubuntu","screen"],"title":"【screen #1】透過 screen 讓 terminal 內的工作內容可以無縫接軌到任何電腦 (儲存當下 terminal 內容)，並可以繼續操作"},{"categories":["215 - Modern C++","999 - 【草稿區】"],"content":"前言 此文章目前尚未整理完成，如需學習完整內容可參考隨附的 reference，或自行 google 搜尋\n但因為作者要整理的筆記太多，如果想早點看到整理後的文章，可以下方留言「期待整理」之類的\u0026hellip; 我會努力找時間優先整理！\ncasting 就是轉型的意思，這篇文章讓我們來整理一些 C, C++ 一些相關的概念\nC-style cast (Regular Cast) C 語言的轉型，格式是 (int) a，\n透過 () 包住 type，\n可以將後者變數轉型成 () 中的 type。\n在 C++ 中，這種語法包含了 const_cast, static_cast, reinterpret_cast，不過也因此這種轉型不見得安全(下方有說明)，因為他沒有採取比較安全的 dynamic_cast，可能會出現不預期的轉型(或說是語意混淆 ambiguous)，因此使用時必須要小心。\n缺點? 其實是因為從 C 到了 C++ C 語言能夠轉型的方式只有一種，但到了 C++ 後，出現了物件導向的「繼承」、「模版」概念，\n這在 C 上單純轉型的行為，可能開始會有語意混淆 (ambiguous)，\n而這時 compiler 的預設行為是「照著某個順序嘗試」，天啊，嘗試聽起來就可能突然哪天會出事啊！\n當然，大部分情況可能沒差，但一個嚴謹的程式設計，\n又特別是撰寫 C++ (相比 python)，對這種小細節我們當然必須要求。\n也因此後來才會出現了我們以下要介紹的 static_cast, dynamic_cast, reinterpret_cast, const_cast\n介紹一下從 C++ 開始要處理的問題 如果我們把各種轉型的情況列出來，大致有以下幾種可能：\n此部分參考原文翻譯，翻譯能力有限，建議英文不錯的也可以閱讀原文：\nHow do you explain the differences among static_cast, reinterpret_cast, const_cast, and dynamic_cast to a new C++ programmer? Regular cast vs. static_cast vs. dynamic_cast in C++ When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used? 兩個可運算的 type 把運算 type 轉成 pointer type 兩個 pointer type 的轉換 C-style cast (Regular Cast) 範例程式碼 #include \u0026lt;iostream\u0026gt; int main(){ double a = 1.5; int b = (int) 1.5; std::cout \u0026lt;\u0026lt; b \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; \u0026amp;a \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; (int) a \u0026lt;\u0026lt; std::endl; std::cout \u0026lt;\u0026lt; (float) a \u0026lt;\u0026lt; std::endl; return 0; } 結果： 就是單純的轉型\nstatic_cast 最基本的型態轉換，屬於強制轉型\n常用於 float -\u0026gt; int，char -\u0026gt; int\ndynamic_cast dynamic_cast 最常被使用來處理多型 (polymorphism) 的問題，當我們要轉型至「被繼承的 class」才需要使用， dynamic_cast只被使用於 cast from \u0026ldquo;base class\u0026rdquo; to \u0026ldquo;derived class\u0026rdquo;.\nreinterpret_cast reinterpret_cast 是最危險的 casting，使用時最需要小心，他的強制轉型有可能會出現我們完全預期的結果。\n使用於重新解讀比較 low level 的情況，(舉例像是 bit patterns)，\n最主要的使用情況是將 raw data bit stream 轉換為實際可以使用的資料，或將資料儲存為 low bits，並使用 pointer 指向這些資料。\nconst_cast 當我們要對一個 variable 新增/移除 const 時使用，\n理解上，建議理解 「const type」 與 「type」 是不同的 type，\n會比較不容易出現 casting 的問題。\nReference How do you explain the differences among static_cast, reinterpret_cast, const_cast, and dynamic_cast to a new C++ programmer? Regular cast vs. static_cast vs. dynamic_cast in C++ When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used? C 語言強制轉型 (casting) [C++] 標準類型轉換：static_cast, dynamic_cast, reinterpret_cast, and const_cast ","date":"2022-10-23T03:23:41+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/%E6%88%AA%E5%9C%96-2022-10-23-%E4%B8%8A%E5%8D%882.30.29.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/modern-c/cpp-casting/","tags":["C++","Modern"],"title":"【草稿】【Modern C++ #2】 C++ 轉型 casting 整理 (C-style cast, static_cast, dynamic_cast, reinterpret_cast, const_cast)"},{"categories":["432 - tmux"],"content":"前言 在嘗試實現使用 F1 - F12 的 function key 控制 tmux 視窗的時候，\n有運用到的一些技術筆記。\nbash 的數學表示式 echo $((1+1)) 結果 使用兩層的括弧，就可以變成一個數學表示式，\n同樣的有變數也適用，寫法上不用在變數前面多加「$」號，\n統一使用一個就好。\nn = 10 echo $((n+1)) 結果 設定 tmux 視窗對應 f1-f12 熱鍵 因為 tmux 視窗從 0 開始編號，因此 f1 對應視窗 0\nfor n in 1 2 3 4 5 6 7 8 9 10 11 12; do tmux bind-key -T root \u0026#34;F$n\u0026#34; select-window -t \u0026#34;$((--n))\u0026#34; done Reference Math Arithmetic: How To Do Calculation In Bash? How to get the F1-F4 keys to behave normally in tmux https://unix.stackexchange.com/questions/594486/how-to-bind-tmux-window-selection-to-f1-f12-keys ","date":"2022-10-21T10:48:02+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/%E6%88%AA%E5%9C%96-2022-10-21-%E4%B8%8A%E5%8D%8810.38.44.png","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/tmux/f1-f12-control-tmux-window/","tags":["Bash","Linux","Ubuntu"],"title":"【tmux #1】使用 F1-F12 控制 tmux window，利用 bash 實現數學運算"},{"categories":["432 - tmux"],"content":"前言 screen 如果是基本版的終端機管理工具，\ntmux 就是進階版的，能為我們提供更多的功能，\n這篇稍微整理一下我會用到的東西。\n這邊只有整理我常用的功能，因此「不是完整的功能整理」。\n安裝 linux: sudo apt install tmux mac: brew install tmux 先講結論，一張圖整理完我常用功能 「開啟 / 關閉」 sessions 與 windows 我們在使用 screen 的時候，\n預設就是一個又一個的 terminal shell，\n但有時後我們會想針對這些 shell 做更多「不同任務上的管理」，\n例如：一些 shell 負責 A 任務，一些 shell 負責 B 任務\u0026hellip;\n這時候如果是 screen，我們會看到終端機完全混在一起，\n透過 tmux，我們可以直接進行上述更完整的管理。\n如果從上面的例子「一些 shell 負責 A 任務，一些 shell 負責 B 任務」\nsessions 就是可以負責幫我們開一個 A 任務的 session，\n而 windows 就是可以幫我們開多個 shell\n架構圖示大概是\ntmux sessions windows 個人常用指令 因為快速鍵「ctrl + B」太過常用，以下 「c+b」 皆等於 「ctrl + B」\n啟動 tmux tmux session 類 啟用新的 sessions 啟用新的 sessions 如果是在 tmux 外面，直接 tmux 即可\ntmux 啟用新的 session 並命名 tmux new -s \u0026lt;session name\u0026gt; 查看現有的 tmux sessions 查看現有的 sessions (windows 只會計算數量) tmux ls detach (暫時離開) sessions detach sessions c+b d # (detach) kill sessions (kill windows 也可以) 快速砍視窗\nc+b w # 先查看所有 windows t # tag, 想砍的打上記號 X # 砍掉 attach session 如果都不指定，那就是回到最近的 session (最方便) tmux attach # or tmux a attach 回指定的 session tmux attach \u0026lt;session-name\u0026gt; tmux attach -t \u0026lt;session-id\u0026gt; windows 類 「在一個 session」中啟用新的 windows 啟用新的 windows c+b c # (create) 查看現有的 windows 查看所有的 windows (非常好用！！！) c+b w # (select window) 切換 windows 我們一樣也可以用查看所有的 windows，同步做到切換的功能 (非常好用！！！)\nc+b w # (select window) 如果只是單純要切換到已知編號的視窗：\nc+b \u0026lt;number of window\u0026gt; 刪除 windows 也就是關掉 windows 與其包含的所有 panes\n方法1: 「c+b \u0026amp;」 c+b \u0026amp; 方法2: 透過「kill-window」指令 :kill-window -t \u0026lt;window-number\u0026gt; 「:kill-window -t 」等效於直接輸入 tmux kill-window -t \u0026lt;window-number\u0026gt; 查看歷史內容 倒退查看歷史內容 scroll tmux 因為 tmux 實際上顯示的內容並不是照著視窗在走，\n因此如果我們想要倒退查看之前的 log，\nc+b [ # [, 上括弧 我是記有種往前推的感覺? 按方向鍵 q # 退出 (進階) 修改最大歷史紀錄 因為預設的行數有限制 (也避免佔用系統容量)，如果要更大的空間我們需要手動修改。\n最主要是要新增「~/.tmux.conf」這份文件\necho \u0026#34;set -g history-limit 10000\u0026#34; \u0026gt;\u0026gt; ~/.tmux.conf 10000 可以改任意數值，越大可以有越多的歷史紀錄。 (進階) 切割畫面 (使用 pane) 切割畫面個人認為屬於比較進階的功能，\n我們可以透過 tmux 直接實現切割畫面\n有些 terminal app 設計上也已經有內建這樣的功能，不過 tmux 更好用的地方是在於他是「自身」就能夠進行畫面分割，無須下載新的 terminal app。\n這時我們的階層會變成像這樣\ntmux sessions windows panes 每一層都可以開啟多個下一層。\n這時候我們可以下「c+b ％」、「c+b \u0026ldquo;\u0026quot;」\n(我的記法：把 % 看的有點像 o|o ，所以是直的，另外「 \u0026quot; 」就記\u0026hellip;. 有兩個單撇XDDD 超爛我知道)\n在不同的 pane 之間切換，我們可以下「c+b 上下左右」，就可以切換了\n這邊要記得，因為可以連續切換，所以在「換到想要的 pane」之後記得先停頓一下。\npane 縮放 有時候我們會想要對於視窗進行調整，這時候我們可以下「c+b(按住) 上下左右」\n快速排版 pane 如果透過上面的方式，有時候我們會把版面弄得很亂，\n這時候只需要輸入「c+b space」，就可以快速整理成對齊的樣子。\n可以多按幾次，切換不同種預設的排版。\n關閉 pane 方法1: 「c+b x」 使用 x 他會再跟你確認一次，這時候按 y 即可刪除。\n方法2: 「c+d」 這方法不會再跟你確認一次，會直接刪掉 pane。\n方法3: 使用「c+b :」先叫出功能表後，輸入「:kill-pane」即可刪除。 範例：在特定的 pane 裡面「:kill-pane」，砍掉該 pane\n「:kill-pane」等效於直接輸入 tmux kill-pane 問題解決：關於 tmux 畫面出現切割的問題 (tmux resize window) 原因 主要發生原因是同時有多個 shell attach 至同一個 terminal，\n因為是「同一個 terminal」，會自動符合某一個 size\n解決方法 將 terminal detach 即可，不過有時候可能因為有「斷線」的情況，\n導致我們無法回去該 terminal，並進行 detach，\n此時我們可以使用「c+b shift+D」(detach target window，是以 window 為單位)，\n來快速的 detach 各個尚未 detach 的 terminal。\nReference Linux tmux 終端機管理工具使用教學 How to Rename a Session in Tmux How to close a tmux session How do I scroll in tmux? How to do a full length vertical split in tmux Is there any way to redraw tmux window when switching smaller monitor to bigger one? How do I increase the scrollback buffer size in tmux? Tmux常用功能总结 How to Use tmux for Remote \u0026amp; Local Development https://andyyou.github.io/2017/11/27/tmux-notes/ 分割畫面 https://opensource.com/article/20/5/split-terminal ","date":"2022-10-20T12:58:54+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/%E6%88%AA%E5%9C%96-2023-12-14-%E5%87%8C%E6%99%A812.58.40.png","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/tmux/linux-tmux/","tags":[],"title":"【tmux #0】好用的終端機管理工具 - tmux (類似 screen 的進階版)"},{"categories":["935 - 重症醫學"],"content":"\n前言 在加護病房中重症患者插管使用呼吸器是很常見的事情，然而對於許多剛進入重症加護領域的醫療同仁來說，呼吸器是令人感到害怕的存在，更不用說瞭解正壓通氣是如何去影響生理運作的，本文簡述正壓呼吸器對於人體心血管於中樞與周邊系統的影響。談論正壓通氣對心血管系統影響前，我們要先知道自發性呼吸下與心血管間的作用關係。\n自發性吸氣 自發性吸氣(inspiration)時: 肋間肌收縮 → 胸腔擴張 (肋膜壓↓) → 吸氣 → 胸內壓(intrathoracic pressure)↓ → 靜脈回流↑ → 心輸出↑\n而正壓通氣下又可以分為幾種情況\n無心臟疾病的正壓通氣 正壓吸氣期(inspiration)時: 機械提供正壓通氣 → 肺泡壓↑ → 肋膜壓↑ → 胸內壓(intrathoracic pressure) ↑ → 靜脈回流↓ → 心輸出↓\n左心衰竭 正壓吸氣期(inspiration)時: 機械提供正壓通氣 → 肺泡壓↑ → 肋膜壓↑ → 胸內壓(intrathoracic pressure)↑ → 靜脈回流↓ →〔左心衰竭 → 肺血管阻力上升 → 右心 afterload↑ → RV擴張 → 心中膈shift to left〕→ 左心輸出↓\n右心衰竭 正壓吸氣期(inspiration)時:\n機械提供正壓通氣 → 肺泡壓↑ → 肋膜壓↑ → 胸內壓(intrathoracic pressure)↑ → 靜脈回流↓ →\n〔左心衰竭 → 肺血管阻力上升 → 右心 afterload↑ → RV擴張 → 心中膈shift to left → 左心輸出↓ 〕\n〔肺血管阻力上升 → 右心 afterload↑ → RV擴張 → 右心輸出↓ 〕\n→ 心輸出↓\n前面講了這麼多正壓呼吸會帶來心血管的負面影響，後面來說說正壓呼吸會給生理帶來甚麼樣的好處\n正壓通氣帶給生理的好處 小節 最後總結一下幾個正壓通氣對心血管中樞系統的重點\n- 肋膜壓 ↑ - 心輸出量 ↓ - 肺血管阻力 ↑ - RV afterload ↑ - RV work ↑ - LV work ↓ - Coronary blood flow ↓ - Coronary perfusion pressure = Systemic diastolic pressure – LV end diastolic pressure 如何避免呼吸器對心血管系統的影響?? 減少Mean airway pressure, MAP (呼吸道平均壓)： 真正影響胸內壓的因素是MAP，因此降低MAP的方式，如: 降低flow、I:E、PEEP、PIP 等等，可以有效降低呼吸器所帶來的不良影響 - 升壓藥物支持：Epinephrine, Norepinephrine, Dopamine, Vasopressin - 適當的輸液：Normal saline, D5W, Lactate ringer 說完心血管中樞的影響，接下來說一下正壓通氣對周邊血管的影響吧！\n對腦內壓的影響 首先我們需要知道腦部灌流壓的公式與基礎知識：\n腦部灌流壓(CPP) = MAP(平均動脈壓)-顱內壓(ICP) CPP normal range 60-150mmHg ICP \u0026lt; 15mmHg 正壓通氣會增加Mean airway pressure, MAP (呼吸道平均壓)，而呼吸道平均壓會使得Mean arterial pressure, MAP (平均動脈壓) 下降，進而使腦部灌流壓下降，這會導致腦內血流下降(CBF)。對於正常人來說是可以代償這點壓力，但是在一些腦受損或腫瘤的病人身上發生IICP 的情形。過去許多文獻指出高PaCO2 可能導致腦內血管收縮，間接導致ICP 升高的情形，因此曾經有讓呼吸器過度通氣產生permissive hypocapnia (32–45 mmHg)的治療策略，但是這樣的作法僅能在短時間內影響腦壓，且無法顯著改善病人預後，因此近些年較不流行這樣的做法。目前比較主流的方式是透過調整適當的PEEP (\u0026lt; 5cmH2O)以降低Mean airway pressure, MAP (呼吸道平均壓) 對腦壓的影響。\n(Crit Care. 2018; 22: 76.)\n對腎臟的影響 正壓通氣會使得周邊血流整體的血流量下降，其中會包括腎臟血流，而腎絲球過濾血液需仰賴輸入的血流，而這樣的現象會導致體內的水分與電解質滯留於體內。上述狀況會使體內的血壓上升，拮抗調控體內的血壓。當血壓過高時，會啟動體內的 腎素-血管張力素-醛固酮系統（renin-angiotensin-aldosterone system, RAAS），藉此調控血壓的穩定。\n對肝臟與胃腸道的影響 如同腎臟血流會受到正壓通氣的影響，肝臟與胃腸道血流也會因正壓通氣而減少，導致缺血性的問題，如果發生缺血性腸疾 (Ischemia bowel)等問題，會使得重症病人死亡率上升。另外如果是使用非侵襲式呼吸器(Non-invasive ventilator, NIV) 且正壓通氣壓力高於20cmH2O，可能會使得氣體打入胃內，使得患者腹脹，因此建議這一類患者需要放置鼻胃管，以方便減壓。\n總結 延伸閱讀 重症病患的血流動力學監測 急性呼吸窘迫症候群(ARDS)的定義與歷史 ","date":"2022-10-10T00:15:31+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/15.png","permalink":"https://wongwongnotes.com/posts/os-misc/growth/critical-care/the-effect-of-ppv/","tags":["重症醫學"],"title":"【重症醫學】正壓通氣對心血管系統的影響 (The effect of positive pressure ventilation on the cardiovascular system)"},{"categories":["221 - C++ 程式編譯"],"content":"前言 這裡是一些我自己 debug 時做的筆記，並不是完整的教學，\n只會寫我會用到的東西，或有學到新東西在陸續補上。\n建立「launch.json」，產生 debug 我們需要參考的檔案 這邊我們會先需要建立「./.vscode/task.json」、「./.vscode/launch.json」，作為我們 debug 需要的設定檔。\nstep1. 開啟 debug extension，點擊執行並偵錯 step2. 「在開啟檔案的畫面」，選擇 C++ (GDB/LLDB)，再選擇 g++ (可以自己針對需求修改) 如果發現有下圖沒有出現的選項，有可能不是在「當前視窗就是想編譯的檔案」\nstep3. 我們會發現，我們右邊多出兩份檔案「./.vscode/task.json」、「./.vscode/launch.json」 step4. 先看我們的「./.vscode/task.json」，我們會在這裡決定我們要怎麼「編譯程式」 在「一開始是選擇開啟檔案的情況下」，我們自動建立的「./.vscode/task.json」，照理來說是可以直接跑的。\nstep5. 看我們的 「./.vscode/launch.json」，我們在這裡決定我們要怎麼「執行程式」 在「一開始是選擇開啟檔案的情況下」，我們自動建立的「./.vscode/launch.json」，照理來說是可以直接跑的。\n設定 debug 中斷點 step1. 在對應的行數前，點擊紅點，建立等等暫停位置 step2. 點擊左方執行，程式開始運作 注意可能會顯示的指令：「g++ -fdiagnostics-color=always -g test.cpp -o test」，注意「-g」、「-o」、「 -fdiagnostics-color=always」這幾個 flag\nstep3. 可以透過右方小工具，一步一步執行至下一個中斷點 debug 其他小技巧 「cmd + 點擊 function name」：進入 function / variable 查看 「alt + ←」：往前一步 (搭配上述使用，查看 function 後跳回去) Reference ","date":"2022-10-06T20:37:58+08:00","image":"https://wongwongnotes.com/images/restored/2022/10/%E6%88%AA%E5%9C%96-2022-10-06-%E4%B8%8B%E5%8D%888.26.25.png","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-debug-vscode/","tags":["C++","程式編譯"],"title":"【C++ 程式編譯 #2】透過 vscode 進行 C++ debug，設定 launch.json 與透過中斷點將程式暫停，觀察變數"},{"categories":["441 - VScode"],"content":"前言 這篇是小知識篇，\n近期有一個需求是我需要連接至遠端的 docker 中，\n並同時執行遠端 docker 的 html file，\n這時候會碰到一個問題，如果是本地的 html file，我們可以直接用自己的瀏覽器打開，但「遠端+ docker 內的 html」我們可以怎麼辦呢?\n這邊我紀錄一些實用的 extension\nLive Preview Live Preview 可以直接在 VScode 新的分頁顯示出格式化後的 html 內容，\n基本上就是我要的功能，可以直接把 docker 內的 html 文件顯示出來。\nLive Server Live Server 可以透過本地的網頁，把 docker 內的 heml 顯示出來，\n但目前使用上好像有時候會有 bug，自己更喜歡用上面那一個。\nReference Live Preview Visual Studio Code設定與插件 Live Server ","date":"2022-09-30T10:41:39+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-html-file-extension/","tags":["VSCode"],"title":"【VScode #3】把遠端的 html file 直接顯示在 local vscode 裡面的 extension 紀錄 (open html in vscode)"},{"categories":["381 - Bash 基本語法"],"content":"前言 這篇是小知識篇，\n我們在執行一個 bash 腳本的時候，可能會有幾種執行方法，\n但其實有很大的差別，\n以下我們進行比較。\n比較 我們要比較的執行方式有\n./script.sh bash ./script.sh source ./script.sh ./script.sh 直接執行腳本，使用的 shell 可以透過 \u0026ldquo;which $SHELL\u0026rdquo; 查詢\n例如我在 mac 上，查詢的結果會是 /bin/zsh\n因此這行 「./script.sh」，實際上等價於 「zsh ./script.sh」\nbash ./script.sh 直接指定用 bash 執行 script.sh，\n這裡有一個重點「我們在執行這指令後，其實是另外開一個 shell 去執行這個指令」\n簡單來說，這會有什麼差別呢? 如果我們在此 script 有定義一些環境變數，\n他會被執行在「暫時為了執行 script 而產生的 shell」，\n所有執行過程中產生的「變數定義不會被保存」。\n如果我們要做一些事情，同時「不希望暫時產生的變數不會修改到原本的 shell」，\n應該要使用 bash 或 直接執行。\nsource ./script.sh 與 bash 的最大差別是在，\n我們是「對當前的 shell」去執行腳本，\n因此「變數定義都會被保存」。\n因此如果我們要做一些「環境變數的定義」，\n應該要使用 source。\nReference ","date":"2022-09-30T10:28:35+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/linux-source-bash/","tags":["Bash","基本語法"],"title":"【Bash 基本語法 #5】小知識 | source 與 bash 執行 script 的差別"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 最近因為在外租屋，想要買一個可以當擺飾的充電盤來用，\n之前買比較便宜的，但想想都買這麼貴的手機、手錶、airpods，\n這次挑選後，選擇了蘋果官方認證的產品，而且同樣擺在居家也非常美觀。\n購入價 我是在 pchome 買的，購入價 $3790\n開箱 產品本體 △ 收到包裹的樣子\n△ 初開封！(裡面有一些我買的其他東西，先別管他)\n△ 商品本體！！ 外盒也一樣精緻呢！\n△ 產品側面，他開封的方式很特別\n△ 打開產品後，第一眼看到的樣子\n△ 產品的正面\n△ 底盤的樣子\n△ 把產品整個擺出來\n連接電源 △ 在產品底下，有一個說明書，教你怎麼把電源連接\n△ 來把電線連接進去\n△ 然後把線放置於收藏的地方\n使用 △ 說明書的背面，有展現使用的方式\n△ 透過 magsafe，手機可以直放，吸附在上面\n△ 透過 magsafe，手機可以橫放，吸附在上面\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n這算是我第一次體驗 magsafe，結果「手機裝了犀牛頓的殼，反而吸不起來」，\n這邊可以注意上面的照片，「我是拆掉殼之後才吸上去的」!\n只能說購買時要注意，自己的手機殼是否會造成 magsafe 無法順利吸附，\n不然就會像我一樣，每次要使用的時候都會碰到「需要拆掉殼才能用的窘境」囧，\n不過拆掉殼後的體驗真的沒話說，品質上設計也非常的優美，\n很適合擺放在居家的任何地方，都是一個非常有藝術感的裝飾品！\n","date":"2022-09-16T00:42:58+08:00","image":"https://wongwongnotes.com/images/restored/2022/09/IMG_0582-1024x768.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/belkin-boost-charge-pro-magsafe/","tags":[],"title":"【嗡嗡開箱 #17】Belkin - Belkin BOOST↑CHARGE™ PRO MagSafe 3 合 1 無線充電器(黑) | 不專業開箱文"},{"categories":["442 - Sublime Text"],"content":"前言 我在寫文章的時候，非常經常利用 home 與 end 這兩個按鍵，\n來快速前往行首與行尾，\n但 sublime 預設的 home、end 是到整頁的頭與尾，\n但我們還是可以透過修改的方式，\n達到「home(到行首)」，「end(到行尾)」的功能\n修改使用者自定義按鍵 從 Rreferences 打開 Key Bindings，\n並從個人的設定中加入以下設置，並儲存：\n[ { \u0026#34;keys\u0026#34;: [\u0026#34;home\u0026#34;], \u0026#34;command\u0026#34;: \u0026#34;move_to\u0026#34;, \u0026#34;args\u0026#34;: {\u0026#34;to\u0026#34;: \u0026#34;bol\u0026#34;, \u0026#34;extend\u0026#34;: false} }, { \u0026#34;keys\u0026#34;: [\u0026#34;end\u0026#34;], \u0026#34;command\u0026#34;: \u0026#34;move_to\u0026#34;, \u0026#34;args\u0026#34;: {\u0026#34;to\u0026#34;: \u0026#34;eol\u0026#34;, \u0026#34;extend\u0026#34;: false} } ] 如下圖：\n上面的指令內容，就是我們把 home、end 這兩個指令，\n去映射到 bol (begin of column，我猜的)、 eol (end of column，我猜的)、\n這樣就完成囉！\n我熟悉的快速行首行尾 home、end 鍵終於回來了！！！\nReference Key Bindings Sublime Windows [Ctrl Right] Doesn’t go To End Of Line. HELP ","date":"2022-09-15T21:34:00+08:00","image":"https://wongwongnotes.com/images/restored/2022/09/%E6%88%AA%E5%9C%96-2022-09-15-%E4%B8%8B%E5%8D%889.29.48-1024x429.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-home-end/","tags":[],"title":"【Sublime】透過修改指令，實現按下「home(到行首)」，「end(到行尾)」的功能"},{"categories":["922 - Mac / MacOS"],"content":"前言 這篇要來解決個人在 mac 注音輸入法上面碰到的問題，\n最主要麻煩的點是在「mac 預設的注音輸入，會是全形符號/數字混注音」，\n例如：『測試一下１２３』\n但我想要的是：『測試一下 123』\n大部分的情況下，我都不會用到全形輸入法，因此我想把他換掉！\n全形絕大部分的情況下我都用不到，\n因此這篇作一個筆記，把 mac 原生的注音輸入法替換成 「注音混半型」的輸入法！！\n安裝 小麥注音 (McBopomofo) 這篇幾乎上完全是參考以下的教學，只作為我自己想要搜尋的關鍵字使用，\n因此細節我不會交代太多，也感謝原作者的分享，內容可以參考：\nMac系統中切換全形數字與半形數字的方法 而小麥注音的安裝位置，開發者的網站：\n感謝開發者開發了這麼方便的東西！！！\nMcBopomofo 小麥注音輸入法 Reference Mac系統中切換全形數字與半形數字的方法 McBopomofo 小麥注音輸入法 ","date":"2022-09-15T21:09:06+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/mac-mcbopomofo/","tags":["macOS"],"title":"【Mac】透過小麥注音來「避免輸入全形數字、文字」，比原廠 mac 注音更好用的「注音混半型」輸入法"},{"categories":["899 - Debug / Error"],"content":"前言 這個是我嘗試開啟 windows 上 ssh 服務時，我執行指令：\nStart-Service sshd 碰到以下錯誤的個人解決方式：\nStart-Service : \u0026#39;OpenSSH SSH Server (sshd)\u0026#39; 服務因為下列錯誤而無法啟動: 無法開啟 sshd 服務 (於電腦 \u0026#39;.\u0026#39; 上)。 位於 線路:1 字元:1 + Start-Service sshd + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : OpenError: (System.ServiceProcess.ServiceController:ServiceController) [Start-Service], ServiceCommandException + FullyQualifiedErrorId : CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand 問題成因 因為權限不足，導致此問題發生\n問題解決 使用「系統管理員」身分，重新啟動 powershell 即可。\n","date":"2022-09-12T20:42:04+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/other/debug-error/start-service-openssh-ssh-server-sshd/","tags":["Debug","Error"],"title":"【Windows】問題解決：Start-Service : 'OpenSSH SSH Server (sshd)' 服務因為下列錯誤而無法啟動: 無法開啟 sshd 服務 (於電腦 '.' 上)。"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 前幾天購入 Chromecast 之後，\n畢竟也是有搭載 android 系統，\n所以自然我們也會想看看能不能透過一些 app，來大幅強化我們的體驗。\nchromecast 切換 youtube 帳號 (目前好像有 bug) 如果遇到無法切換 youtube 帳號的時候，\n從設定裡面，將 chromecast 重開機即可，\n(不是遙控器上的「開關電源」，是要將機器重新開機)\n讓 chromecast 可以使用 airplay 我們可以透過下載修改遙控器的 app 「AirScreen」，\n達到類似「使用 apple airplay」的投影效果。\n可參考：\n怎麼投影iPhone 畫面到Chromecast with Google TV？3種方法輕鬆搞定 修改 chromecast 的遙控器，更改按鍵配置 我們可以透過下載修改遙控器的 app 「Button Mapper」，\n達到「修改遙控器按鍵」的效果。\n可參考：\n第四代Google Chromecast如何截圖？更改遙控器快捷鍵、一鍵抓下影片畫面 Reference 第四代Google Chromecast如何截圖？更改遙控器快捷鍵、一鍵抓下影片畫面 怎麼投影iPhone 畫面到Chromecast with Google TV？3種方法輕鬆搞定 ","date":"2022-09-11T17:06:58+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/chromecast-settings/","tags":[],"title":"【Chromecast】一些自己強化 Chromecast 的個人筆記"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 在外租屋的時候，房東有時候給的電視會比較舊，\n通常都只有一些基本的功能，\n但只要有 hdmi 的接孔，現在只要搭配 chromecast，\n就可以直接把智障電視升級成智慧電視囉！\n這次是 chromecast 的第四代 Google Chromecast (支援Google TV)，\n多了 Google TV 的功能，更接近智慧電視的完整體\nfirst view 讓我們先看一下設定完成後的成品吧！\n購入價 我是在 pchome 買的，購入價 $1999\n開箱 △ 收到包裹的樣子\n△ 初開封！\n△ 全部就大概這個 size △ 拆開包膜！\n△ 背面也來一張！\n盒子內部 △ 打開盒子內部，分別是一個本體與一個遙控器\n△ 底下是說明書\n開箱本體 △ 本體拿出盒子\n△ 本體真正的樣子！出現！\n開箱遙控器 △ 遙控器拿出盒子\n△ 遙控器本體\n開箱配件 △ 說明書最底下就是插頭了\n△ 近拍一下插頭\n△ 全部的成員大概就是這樣囉！\n初次設定 安裝 △ 充電線這邊有個我很喜歡的小巧思，使用扣子的方式，可以重複利用！\n△ 充電線連接本體\n△ 盒子隨附電池，不用再另外買！我們把它裝到遙控器中\n△ 直接把本體插入電視的 hdmi 當中，並連接電源\n初次設定 基本上，這邊分兩條路可以開始設定：\n直接用遙控器設定 透過手機 (iphone, android 都可以) 設定 個人強烈推薦使用「透過手機 (iphone, android 都可以) 設定」，好操作非常多，提示也十分充足！\n△ 歡迎介面\n△ 手機下載 google home，透過 app 提示連線電視\n△ 大部分設定都會自動完成，有需要再操作一下就好\n△ 另外一個個人覺得不錯的功能，直接把 google 遙控器變成電視遙控器 (同步電視開關、音量、訊號源) 這些最常用的功能\n△ 選擇電視廠牌，基本上這裡各大家廠商都有了\n△ 最後就等他預先做一些初始設定，載一些app\n△ 設定完成！\n使用 △ 剛進來畫面，就是充滿著一堆能觀賞的內容，十分期待！\n△ 主畫面還會推薦各種內容，供你選擇\n△ 他可以載的 app 有很多，常見的 youtube, netflix, 愛奇藝, disney+ \u0026hellip;，更甚至有動畫瘋！ twitch！\n△ 在前一個步驟中，手機就已經可以先設定好要預裝哪些 app 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n買這個之前，我宿舍電視沒開過\u0026hellip;\n買回來這個第一天，我宿舍電視開整天\u0026hellip;\n實在是太好用了！在外租屋，房東通常會給第四台，\n但現代人幾乎都比較少在看電視了，\n而這樣的智慧電視比較滿足現代人的觀看需求，\n而且不分老少，覺得也是非常適合買給父母的禮物 (不用再看小螢幕了！！！)\n另外我也很喜歡他的設計小巧思，\n他把電視常用的功能鍵也整合進 google 的遙控器裡面，\n包含 (電視開關、音量、訊號源切換) ，\n真的幾乎可以變成一個遙控器達到所有的需求。\n△ 設計小巧思，常用的電視功能有放進來 (電視開關、音量、訊號源切換) 達到一個遙控器幾乎都滿足最主要的需求\n此外 google 語音助理也很方便，一個按鍵按住 (上方黑色按鍵)，對著他講想看什麼，\n就可以自動幫忙完成搜尋的功能了！實在是非常的便利！\n還可以特別注意他有個小巧思，上面有快速的 youtube、Netflix 按鍵XD，\n很符合現代人的需求，不過小可惜是不能自訂，\n畢竟我沒有訂閱 Netflix，有時候會很希望能自定義成 twitch，會更加的便利！\n小結 總之，買這個一點後悔都沒有，\n甚至買了這個從宿舍幾乎都不開電視到電視幾乎不會關XD，\n而且該有的功能幾乎都有了！\n還有很神奇的 YT 快速鍵、同步遙控器音量鍵/ 電源鍵 / 切換訊號鍵，\n對遙控器講話就能快速搜尋！\n我當初是買完使用不到半小時，就決定再下訂一個孝敬爸媽了！\n不到 2000 元，而且大約 1/3 apple tv 4K 的價格，\n卻該有的功能都有了！ 比起買一台 apple tv 4K，\n或許改成買 3 個 chromecast 搞不好更經濟實惠！\n應該是沒有長輩會不喜歡的！電視螢幕又大，\n可以把家裡的傳統電視直接大升級成智慧電視！\ncp 值跟體驗都非常棒！！！\n","date":"2022-09-08T14:10:17+08:00","image":"https://wongwongnotes.com/images/restored/2022/09/IMG_9582-1024x768.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/google-chromecast-google-tv/","tags":[],"title":"【嗡嗡開箱 #16】讓租屋處電視無痛升級吧！ Google - Google Chromecast (支援Google TV) | 不專業開箱文"},{"categories":["923 - Windows"],"content":"前言 這篇是寫給那些 mac 輸入法用久了，可能因為工作或其他的原因，\n被迫回去使用 windows 輸入法的人類XDDD\n(習慣 mac 輸入法後，回不去的請留言 +1 XDDD)\n這裡有一個微軟官方的小工具，可以直接更改「鍵盤的映射」，\n雖然一樣有「輸入標點符號」會必須使用特定組合鍵的問題，\n但比起 ctrl 跟 cmd 完全在不同位置的配置來說，應該已經算改善很多了XDDD\n當初我推薦這個給我的同事，照同事的說法他這樣的配置生產力能夠直接提升 50% !!!\n不過大部分人可能不一定有這樣的需求，所以這個才叫做「不一定實用系列」XD\nMicrosoft PowerToys 簡介 Microsoft PowerToys 是微軟開發用來強化 windows 功能的小工具，\n畢竟是官方開發的，因此相對比較不用擔心會有電腦被惡意程式攻擊的風險。(相對而已XD)\n更改鍵盤映射，達到仿 mac 鍵盤的效果 為了達到類似 mac 鍵盤的效果，我們主要要更改的內容有：\nctrl -\u0026gt; alt alt -\u0026gt; ctrl shift -\u0026gt; capslock 因此我們在鍵盤管理器中，進行以下鍵盤映射的操作：\n額外的小工具，滑鼠尋找器 與 簡報重點提示器 這個算是另外一個我覺得實用的功能，\n我們可以去「滑鼠公用程式」開啟「尋找我的滑鼠」功能，\n並且設定「搖動滑鼠」就能啟動。\n之後，我們只要找不到滑鼠時，搖一搖它就會幫我們強調了! (例如上面的小圖示範)\n此外也能當作另外一個功能使用，當我們在簡報時，\n能作為重點的強調，幫助聽眾更清楚現在在講哪個部分!\n更詳細的介紹，推薦 YT 更詳細的介紹，推薦 PAPAYA 電腦教室的 youtube !!\n裡面真的是介紹的非常詳細!!!\nhttps://www.youtube.com/watch?v=EAoIGJjWdbA\nReference 微軟竟給 Windows 量身打造了一套生產力小工具？看在這誠意的份上我只好安裝了 #PowerToys Microsoft PowerToys ：要自訂的公用程式 Windows ","date":"2022-09-01T00:55:51+08:00","image":"https://wongwongnotes.com/images/restored/2022/09/wp_editor_md_fce0ddd68d2a90f3c92434c26a60dcc6.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/os/windows/windows-microsoft-powertoys/","tags":[],"title":"【Windows】不一定實用系列：習慣 mac 鍵盤後回不去? 把 windows 鍵盤配置成 mac 的形狀 (Microsoft PowerToys 實用功能介紹)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在 mount Rclone 時，Mac 碰到以下的 error 的個人解決辦法：\nFatal error: failed to mount FUSE fs: mount stopped before calling Init: mount failed: cgofuse: cannot find FUSE 問題成因 ＆ 解決方式 那表示 Mac 需要去安裝 FUSE，\n安裝 macFUSE 可以去以下連結：\nmacFUSE Rclone mount 筆記 如果有 Rclone mount 相關的需求，可以參考我的這篇筆記：\nhttps://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/rclone-mount-google-drive/\n","date":"2022-08-27T00:58:04+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/failed-to-mount-fuse/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】問題解決：Fatal error: failed to mount FUSE fs: mount stopped before calling Init: mount failed: cgofuse: cannot find FUSE"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 因為現在外宿的原因，\n不知道為什麼我的浴室很容易就超臭！！！\n之前買了他牌的浴室擴香瓶，有好一點點點\u0026hellip; 但整體還是一樣臭\n因為想到以前不管去飯店或餐廳，總是會聞到很香的擴香瓶，\n因此最近，一直都有去留意外面餐廳與飯店所使用的擴香瓶！！！\n最後就找到了這款韓國的 cocod’or 室內擴香瓶！ (而且還真的不少餐廳廁所都有在使用，又意外的很便宜！！！)\n購入價 我是在 蝦皮 買的，購入價 $159 (200 ml)\n(我原本以為飯店餐廳那種用的都超貴的！！！ 這真是意外！！！)\n開箱 △ 收到包裹的樣子，有一種很扎實的感覺\n△ 裡面塞滿滿的，保護做得非常好！ 這個我給滿分！\n△ 找到我們的擴香瓶\n商品本體 △ 商品本體\n△ 商品上方拍攝，整個都是質感！\n△ 打開包裝，相當俐落的內容物\n△ 所有的東西，總共就一個擴香瓶，幾根擴香棒\n△ 商品背面，這次我買的是白茉莉的味道，這也是我在某間餐廳聞到還不錯的味道！\n商品組裝 △ 拆開瓶蓋\n△ 轉進我們要擴香的蓋子\n△ 放入擴香棒！為了除臭效果最強！直接全部都放進去！！！\n成果 △ 完成！等等放入我們想要除臭的地方！\n△ 拜託了！讓我的浴室變超級香吧！！！香起來！！！\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n先說我才剛開始使用XD，使用體驗的時間不多，\n大概剛放三小時後發這篇文，還不到整間浴室都超香，\n但已經比之前他牌的擴香能力好很多了！！！！\n然後因為這次挑的味道又是我很喜歡的味道！\n聞起來就是很舒服 好讚！！！ 愜意的感覺\n","date":"2022-08-26T01:55:47+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_9260-1024x768.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/cocodor-white-jasmine/","tags":[],"title":"‘【嗡嗡開箱 #15】cocod’or - 韓國 cocod’or 室內擴香瓶 (附擴香棒) | 不專業開箱文’"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 作為一個工程師，除了鍵盤之外，滑鼠也超級重要！\n難得看到羅技有出一款「人體工學滑鼠」，\n讓人也想體驗看看，到底「人體工學滑鼠」能帶給我多大的省力，\n畢竟「手也是一輩子的生產力工具」，花一點錢好好的愛護自己的手，希望是有用的！\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 除了看 youtube 之外，\n我覺得一定要「去現場體驗看看」，\n因為這麼特別的滑鼠，沒用過直接買，真的太衝動，\n我是賣場先試用過覺得還行，才買下來多體驗看看的！\n希望真的能夠幫忙減輕手部負擔！\n購入價 我是在 pchome 買的，購入價 $2190，原價 $2490\n開箱 △ 收到包裹的樣子，但裡面的東西搖起來就知道，沒塞滿空間，猜是運送過程中一直撞\u0026hellip;\n△ 開箱後，果然周圍都沒保護XDD 看來真的是運送時一直撞過來\u0026hellip; 好險邊邊沒有撞得太嚴重\n△ 商品正面\n△ 商品正面，多一點燈光拍\n內容物 △ 採用抽出滑鼠的設計，感覺好特別＠＠\n△ 不是很好打開的包裝方式，但優點就是這樣確實把本體保護的很好\n滑鼠本體 △ 滑鼠主要按鍵面\n△ 滑鼠副功能按鍵面\n△ 打開背蓋，這裡有一個小特色是磁吸式的蓋子！不過電池\u0026hellip; 就感覺不是很現代的設計了\n△ 不過裡面有附電池還是不錯的 (有些還要你自己買)，撕下來直接用吧！\n△ 完成的滑鼠背面！\n連結電腦 △ 說明書圖示得非常清楚，羅技的一貫特色，很讚！\n△ 使用藍牙的方式連接\n初次設定 △ 連接上了！開始初次設定！\n△ 羅技的 app 中也有提示，這一款是為了人體工學而設計的滑鼠！\n△ 使用的是 mac，個人習慣是把側鍵的兩個鍵，設定為桌面向左向右！\n△ 既然都用這麼高級的滑鼠了，我很習慣直接把敏感度與速度調到最高來使用！讓他精準定位！\n使用 \u0026amp; 握給你看 (握滑鼠的手指大概位置) 這邊給大家看握這個滑鼠的時候，手指大概的位置，僅供參考。\n△ 正面\n△ 主要功能鍵面\n△ 副功能鍵面\n△ 最後的鍵盤滑鼠特寫\n羅技神奇的多功能跨平台「黑科技flow」 這隻 LIFT 滑鼠，雖然不是 MX 系列，但一樣有 MX 系列主打的「黑科技flow」，\n(而且這滑鼠還比 MX 系列便宜XDD)\n以下介紹因為與其他篇是一樣的，就容我複製貼上了XDD\n這台電腦複製，那台電腦貼上，不管作業系統，「跨OS」還能「跨螢幕」無痛切換!\n其實我也不是什麼羅技的鐵粉，但我怎麼知道，\n我想要的功能，他剛好都做出來了XDDD，我也只好買下去了XD\n我們來看一次這個能解釋一切的 gif：\n因為這個無痛跨「windows」、「mac」電腦的滑鼠與鍵盤，\n完全解決我的痛點，甚至「還不用從滑鼠鍵盤底下切換」，\n我只能給滿分了，目前沒看過類似的產品，這完全就是我要的！！！\n題外話：使用的鍵盤與滑鼠組合為：MX Mechanical + MX master 3\n羅技「flow」技術，「鍵盤滑鼠一起」無痛地跨到另外一台電腦 羅技「flow」更誇張的是，滑鼠移動的同時，連鍵盤都可以「自動切換」過去。\n多台電腦的概念，直接變成「一組鍵盤滑鼠」，就能實現全部控制。\n目前這真的是我碰到最無痛的使用方式了，\n以往都還要「按鍵盤或滑鼠的切換按鍵」，這次「直接滑鼠移過去就好了」。\n好扯XDDD\n羅技「flow」技術，無痛「跨 OS」檔案傳輸 羅技「flow」還有另外一個主打功能，\n就是滑鼠拖曳檔案的同時，或是鍵盤複製貼上的時候，\n可以直接「無痛的在另外一台電腦上貼上」。\n讓我們來看看示範 gif，「不管作業系統，這台複製，那台貼上」： 當初我第一次試用這個功能的時候\u0026hellip;\n我心中全部都是問號．．．\n羅技你不是做滑鼠鍵盤的嗎！！！怎麼搞起檔案傳輸來了！！！\n太扯了太扯了！！！ (讚到不行的意味，好扯，真的好用)\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n先說我才剛開始使用XD，使用體驗的時間只有這篇文章，\n我必須說，「握感差很多！！！勢必需要再去另外習慣的那種」，\n但「使用上沒有太大的不同」，因此這邊就要再去習慣不一樣的滑鼠握法，\n至於有沒有比較省力，我覺得有！\n最主要的差別是，我們比較像是用「握手」的姿勢，取代傳統「趴在滑鼠上」的姿勢，\n這樣的差別，只要使用時間越長應該是一定更有感覺的！\n我自己使用一兩個小時後很明顯有感，猜是移動滑鼠的手腕施力方式改變，過程中也省了不少力。\n另外我想要特別提這一個「人體工學滑鼠的上一代 MX Vertical」，\n我在賣場體驗的時候，第一次就感覺，真的太大\u0026hellip;\n這一款 LIFT 有特別針對亞洲人的手型設計，猜是有得到不少亞洲用戶的回饋才誕生的，\n明明可以算是 MX Vertical 的下一代，但不知道爲什麼沒被擺在 MX 系列(?)\n總之，比起上一代 MX Vertical，個人覺得這款的手型更適合台灣人，\n但至於習不習慣與要不要買，還是一樣建議「一定要先去賣場體驗看看再說！！！」\n","date":"2022-08-26T01:08:41+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8875_4_MOV_AdobeExpress.gif","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/logi-lift/","tags":[],"title":"【嗡嗡開箱 #14】羅技 - LIFT人體工學垂直滑鼠 (石墨灰) | 不專業開箱文"},{"categories":["182 - Python 網頁爬蟲"],"content":"前言 這篇是我自己之前做「靜態網頁爬蟲」的筆記程式碼，\n順便簡介一下靜態跟動態的差別，\n靜態：可以當作是單純爬取 html 動態：有時會有與後端互動的網頁，純 html 無法處理到的部分 通常我們都可以對一個網頁按下右鍵「檢視網頁原始碼」，就可以看到網站原始的 html 了！\n在「靜態網頁爬蟲」的主題中，我們使用的就是這個 html 進行搜尋\n如果有找不到的內容，那很有可能就是「動態網頁爬蟲」的主題。\n範例程式碼 (以爬我自己的網站為例) 之前有一個需求，我想要快速大量的取得我網站 leetcode 主題的所有標題，因此我就自己寫了一個爬蟲來爬我的網站。\n但寫得比較複雜一點了，其實不是很適合新手學習，\n而且使用的套件也不單純，我只是留這邊給我作為筆記使用XD\n不過還是可以大概說明一下我在幹嘛：\nargparse：一個幫我處理 terminal 輸入的套件，有興趣歡迎在我網站搜尋 argparse 的用法，看完就會懂了。 (我也是拿我網站的範例去改出這支程式的)\nrequests：處理網路、網頁相關的請求用 BeautifulSoup：排版、取出特定 html 用，是當我們在拿回 requests 後好用的處理套件 (當然你懂了就知道，這個也可以不用，只是方便處理回傳的內容而已)\n其他就不先多說明了，要用別人的程式碼前也要先做一點功課XDD，有些段落想用也請先想想在幹嘛，直接複製交作業，真的很容易就知道你是抄的了XDD (畢竟無關的段落太多XD)\nimport requests from bs4 import BeautifulSoup import argparse def parse_args(): parser = argparse.ArgumentParser(prog=\u0026#39;argparse_template.py\u0026#39;, description=\u0026#39;Tutorial\u0026#39;) parser.add_argument(\u0026#39;-l\u0026#39;, default=\u0026#34;leet\u0026#34;, type=str, required=False, help=\u0026#39;leet or lint\u0026#39;) parser.add_argument(\u0026#39;-c\u0026#39;, default=\u0026#34;py\u0026#34;, type=str, required=False, help=\u0026#39;py or cpp\u0026#39;) parser.add_argument(\u0026#39;--nums\u0026#39;, \u0026#39;-n\u0026#39;, type=int, required=True, help=\u0026#39;no\u0026#39;) return parser.parse_args() def generate_string(url, language): print(f\u0026#34; Now searching for: {url}\u0026#34;) response = requests.get(url) # print(response) soup = BeautifulSoup(response.text, \u0026#34;html.parser\u0026#34;) # print(soup.prettify()) # 排版後 html result = soup.find(\u0026#34;h1\u0026#34;, class_=\u0026#34;page-title\u0026#34;) # print(f\u0026#34;{result}\u0026#34;) print(\u0026#34;find string: \u0026#34;) print(f\u0026#34;{result.text} \u0026#34;) if language == \u0026#34;cpp\u0026#34;: final_str = f\u0026#34;[C++]({url})\u0026#34; else: final_str = f\u0026#34;[Python]({url})\u0026#34; # print(final_str) return final_str def input_problem_number(n): if args.l == \u0026#34;lint\u0026#34;: problem_src = \u0026#34;lintcode\u0026#34; else: # default leetcode problem_src = \u0026#34;leetcode\u0026#34; if args.c == \u0026#34;cpp\u0026#34;: language = \u0026#34;cpp\u0026#34; else: # default python language = \u0026#34;python\u0026#34; if language == \u0026#34;cpp\u0026#34;: base_path = \u0026#34;algorithm-practice/cpp-solutions/c-leetcode\u0026#34; else: base_path = \u0026#34;algorithm-practice/data-structures/python-leetcode\u0026#34; url = f\u0026#34;https://wongwongnotes.com/posts/{base_path}/{problem_src}-{language}-{n}/\u0026#34; return generate_string(url, language) def main(): problem_number = args.nums print(input_problem_number(problem_number)) if __name__ == \u0026#39;__main__\u0026#39;: args = parse_args() main() Reference Day 20 - 網路爬蟲(1) [Python爬蟲教學]7個Python使用BeautifulSoup開發網頁爬蟲的實用技巧 ","date":"2022-08-24T03:19:34+08:00","permalink":"https://wongwongnotes.com/posts/python/web-automation/python/python-requests-beautifulsoup/","tags":["Python","網頁爬蟲"],"title":"【Python 網頁爬蟲 #3】python 爬蟲筆記 - 靜態網頁爬蟲，使用 requests + BeautifulSoup"},{"categories":["398 - Linux 問題解決"],"content":"前言 這篇文章主要說明當我想要 scp，卻碰到以下內容時，我是如何解決的。\nscp: xxxxxx : No such file or directory 問題成因 個人常見的可能原因，遠端的資料夾路徑沒有被完整的建立。\n例如：想要傳到遠端的路徑 ~/folder/file.txt\n但在遠端的資料夾，「~/」 底下並不存在「folder」這個資料夾就會跳錯。\n解決方式 個人是先去遠端，把對應資料夾路徑建好，\n以上面範例為例，我需要先連線至遠端，\n建立 folder 資料夾\nmkdir -p ~/folder/ 再來下 scp ~/folder/file.txt 指令就可以順利成功了！\nReference me\n","date":"2022-08-24T01:29:20+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/linux-scp-no-such-file-or-directory/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】scp 問題解決：scp: xxxxxx : No such file or directory"},{"categories":["291 - C++ 觀念理解"],"content":"前言 C/C++ 中，「class 類別」是到後來大型程式一定的必備知識，\n面試常考的問題自然也包含著 public, protected, private 的比較，\n我們可以用最簡單的方式概括這個觀念：\n沒有使用「繼承」時，protected = private\npublic 可以任意取用\nprivate 僅限類別內部成員存取，不可外部存取\nprotected 繼承時，雖然一樣不允許外部存取，但內部修改是允許的 (相比 private，即使繼承也是不能修改內部成員的值)\n範例 private 使用 private 時，我們通常會這樣使用\nprivate 即使繼承也不能去改。\nclass A: { private: int a_private; public: void set_a(){ a_private = 2; } } class B : public A{ public: void set_a_inheritance(){ // a_private = 3; 不能這樣改 a，會跳錯 } } protected 使用 protected 時，我們被允許可以在繼承的函數中修改成員的值。\nclass A: { protected: int a_protected; public: void get_a(){ a_protected = 2; } } class B : public A{ public: void set_a_inheritance(){ a_protected = 3; // 這樣改 a 沒有問題 } } public public 是最自由可以被使用的成員，通常都會拿來被外部取用，\n但也因為太過自由，如果要求是嚴謹的程式設計，例如設計「密碼」這類的東西，\n如果太容易就能夠被「外部取得」，則很有可能會有密碼洩漏的風險。\nclass A: { public: int a_public; } // 可被外部修改的意思如下 A obj_A; obj_A.a_public = 2; // 可以被外部修改，或取得 (如果是密碼類的，就很危險) A* ptr_A; ptr_A-\u0026gt;a_public = 2; // 可以被外部修改，或取得 (如果是密碼類的，就很危險) Reference 類別(Class) - private, protected, public 以C++為例 [C++] public, protected, private (1/2): 基礎篇 ","date":"2022-08-20T15:47:46+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/c-public-protected-private/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","觀念理解"],"title":"【C++ 觀念理解 #2】C++ public, protected, private 總和比較整理"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 因為現在外宿的關係，需要一個小的吸塵器方便清理居家環境，\n之前去小米實體店時，使用這個迷你吸塵器體驗還不錯，\n另外因為小巧輕便，也方便外宿族之後搬家移動。\n因此購入此商品。\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 自己親自體驗過，雖然吸力不可能到好用 (這樣就要買大台的了)，\n但對於外宿的人，這體積的小巧輕便，而且還算夠用，\n再加上價格 CP 值 也很不錯，因此整體來說對一個外宿族 CP 相當高。\n購入價 我是在 小米的實體店 現場買的，購入價 1095\n開箱 △ 產品的外包裝 △ 產品第一次打開後的樣子 △ 產品的全部內容 △ 產品的全部內容，拆封後 (線已經太多，我先另外收起來了) 組裝 \u0026amp; 使用 △ 把前面的管子接到本體上 △ 把零件組裝好後的樣子 △ 使用上的樣子，基本的吸力還不錯。雖然效果一定輸大台的，但整體來講還是夠用的 清潔 △ 在顯示紅色的時候，按下按鈕就可以打開清潔 △ 打開清潔的樣子 △ 與盒子合照，比較大小 △ 清潔濾心的注意事項 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n這個產品使用上優缺點都很明顯\n優點 迷你輕便，對外宿族來說相當方便 吸力夠用，基本清潔沒什麼問題 CP 值相當高，一千出頭的價格能夠有這樣的效果，我覺得非常可以XD 缺點 對於整個房間的清潔，可能吸力稍嫌不足，不過這個確實也反應在價格上 (畢竟這東西本來設計出來的目的，就是清潔車子或小地方XD) ","date":"2022-08-19T13:00:54+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8968-1024x768.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/mi-vacuum-cleaner-mini/","tags":[],"title":"【嗡嗡開箱 #13】小米 - 米家無線吸塵器 mini | 不專業開箱文"},{"categories":["291 - C++ 觀念理解"],"content":"前言 C/C++ 一個超級重要的觀念就是指標，\n沒學會指標基本上就還等於沒有玩出 C++ 的「精隨」，\n但指標這個概念真的超複雜又容易搞錯 (這邊是以作者而言，至少我混亂過好多次XD)\n這次打算來好好整理一下，以一個初學者到終於搞清楚的角度，\n重新整理這部分的內容。\n先釐清會常搞亂的根本原因 我到很後來才發現，自己指標學得很差的最主要原因，\n就是在「call by reference」加入之後，直接一片混亂，\n「call by reference」是 C++ 才有的「更方便」的東西，\n但也因為這個「方便」，導致觀念如果不穩，就會像我一樣把這個「方便」的功能，\n變成「符號意義大混亂」\n因此，如果這三個你現在很混亂，\n建議先從「call by value, call by address(pointer)」搞清楚再說!!!\ncall by value 在 C(C++) 裡面，「所有的東西基本上都是 call by value」，\n在這一開始還是很和平，「沒有指標」的世界，\n所有東西看起來都是非常好懂。\nvoid foo(int a){ a = 20; } int main(){ int a = 10; cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; foo(a); cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; return 0; } 結果 10 10 結果我們卻發現了一個神奇的問題，那就是 a 並沒有被修改到，\n如果以同樣的概念，除非我們改成這樣寫。\nvoid foo(){ return 20; } int main(){ int a = 10; cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; a = foo(); cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; return 0; } 結果 10 20 用「回傳」的方式，去修改 a 的值，a 才會被改到\n但其實這限制了所有修改的範圍，僅能侷限在 main() 當中。\n仔細觀察就發現，要達到我們的目的，所有的修改都必須要在 main 裡面\n但這並不是很實際。 隨著我們程式越寫越大，不可能要所有的東西都在 main() 處理。\n圖解一下 在沒有指標的觀念以前，我們不是很在乎這個值「是實際存在哪個記憶體位置」\n所以下面的圖，「Address 的部分」，在指標的概念出現前，\n我們都是省略不看的。\n但實際上，我們會發現在 void function 裡面的 a，\n「複製了 10 這個值，並儲存在新的記憶體位置，\n並用新的「只在 function 使用的 a」，指向這個記憶體位置。\nfunction 之內的 a，因為是另外一個記憶體位置的 a 被改過值， 所以不在 function 內的 a 就是另外一個 a，沒有被改過。 call by address (pointer) 在原本還是很簡單的只有 call by value 的世界，\n自從我們要開始學習「指標 (pointer)」這個章節後，\n一切開始複雜了起來。\n但其實，掌握「指標 (pointer)」，才是真正開始「能完全掌控系統參數的控制」\n上述的例子我們提到了一個問題，所有的修改僅在 main 裡面有效，\n那如果要實現真正的跨 function 修改，我們就必須使用 call by address (pointer) 的方式\n正如其名，call by address 就是直接呼叫他的記憶體位置進行修改，\n因為我們實際儲存值的地方，都是一個又一個的記憶體位置，\n我們所宣告的 a，只是暫時替我們指向這個記憶體位置而已。\n而之所以也可以叫做 call by pointer，因為我們傳的是記憶體位置，\n傳入 function 後，作為「指向記憶體位置的指標」，而非「參考值」，\n因此也有 call by pointer 之稱 (不過我個人覺得講 call by address 更好理解。)\n圖解一下 call by address，最重要的地方在於「傳了實際的記憶體位置進去function」，\n也因此，我們得以進行對應值的修改\n(改的是記憶體位置，原 a 也是指向同一個記憶體位置，造成 a 結果就可以從 function 內改變)\n也有一種理解方式是：其實這兩者本質都相同的是 call by value\n只是 call by value 指的是傳「值」\n而 call by address(pointer) 我們以「記憶體位置 (address)」作為我們的「value」並傳入 function\n(但如果這樣理解會混淆，我建議就還是分開想吧，以不混淆為主)\nvoid foo(int* a){ *a = 20; } int main(){ int a = 10; cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; foo(\u0026amp;a); cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; return 0; } 結果 10 20 call by reference (建議一定要先理解前面兩個的差別，再來理解這個以免混淆) call by reference 是 C++ 後新增的更「方便」 的使用方式，\n但也因為這個「方便」很容易造成「符號解釋大混亂」，\n這就是我當初嚴重混淆的主因。\n簡單來說，call by reference 與 call by address (pointer) 結果完全相同，\n只有差在使用符號上的撰寫不同而已!\n但因為「符號有重複的使用」，如果不熟如我，\n前面觀念又不穩，到此就會開始「大混亂」。\n寫法 C++ 的 call by reference 幫我們簡化了寫法，只需要在宣告 function 變數時，\n多新增「\u0026amp; 符號」，即可以表示「此值我們也要修改原值，而非只做參考用。」\nvoid foo(int\u0026amp; a){ a = 20; } int main(){ int a = 10; cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; foo(a); cout \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; return 0; } 與 call by value 比較，只多了一個 void foo(int\u0026amp; a) 的 「\u0026amp;」\n但因為也常因此跟「call by address 的取址 \u0026amp;」搞混，\n造成觀念不夠熟的(如我)，一開始就會大混亂。\n結論 我們只把最關鍵的部分拉出來看：\n// call by value void foo(int a){} call func: foo(a) // call by address (pointer)，傳入需使用 foo(\u0026amp;a) 取址，function 內使用時也需要使用 *a 從記憶體位置取得他的值。 void foo(int* a){} call func: foo(\u0026amp;a) // call 的時候可以找到 \u0026amp; 作為關鍵！！！ // call by reference void foo(int\u0026amp; a){} call func: foo(a) 我們可以直接觀察宣告 function 時的不同，\n而特別注意傳入時，因為 call by address (pointer)，\n是把「記憶體位置作為 value 傳入，因此需要傳入 \u0026amp;a」\n建議把兩種「\u0026amp;」分開理解，避免觀念混亂。\n實際使用上 實際使用上，「沒有哪一個是最萬用的，只能看情況覺得應該用哪個」，\n有時我們就是只需要傳入一個「參考值」，我們就需要用 call by value，\n有時我們就是要去「改原來的變數」，我們就需要用 call by address (pointer) 或 call by reference\n【面試常考題】 call by address (pointer) 與 call by reference 的優缺點? 其實這兩者所達到的效果是一樣的「都會改變原來傳入 function 的值」，\n但 C++ 中 call by reference 卻是一個更好的設計，\n在傳入 function 時，他會去「檢查這個記憶體位置是否合法」，\n如果為「不合法的記憶體位置」，則會跳錯，不去進行任何後續的動作\n這樣就可以避免「我們去改動到不應該去改的記憶體位置」，造成非預期的記憶體內容被修改。\n因此，這兩種寫法，在我們學會 C++ call by reference 之後，\n我們會更傾向使用 「call by reference」 \u0026gt; 「call by address (pointer) 」\n新補充：網路上找到更容易理解的方式 剛好有一次收集資料時剛好看到這個 youtube 的說明，\n覺得講解的滿不錯的，在此進行整理。\nWhat is the Difference Between a Pointer and a Reference C++ call by pointer (address) 只有宣告的指標，才能去取用「\u0026amp;」取得變數對應的位置。\nint* ptr; int var = 7; ptr = \u0026amp;var; call by reference 被宣告為 reference 的變數 (使用「\u0026amp;」，但這裡符號的意義不同，我自己搞混解釋就是在這裡。)\n我們比較好的理解方式是，我們可以視為有另外一個新的如果 var 指向 7 一樣的 ref，\n沒有「複製值」而「產生新的記憶體位置」，而是直接「讓 ref 指向同一個記憶體位置」\nint* ptr; int var = 7; ptr = \u0026amp;var; int\u0026amp; ref = var; 簡易圖解 ref 只是多宣告一個如同 var 指向同一個記憶體位置的變數。\n(這裡 int「\u0026amp;」不要用「取址」的概念去解釋，雖然看起來有點關係，但觀念真的很容易亂掉)\n宣告時的「\u0026amp;」，在觀念不熟的情況下，請先當作另外一回事。\nReference What is the Difference Between a Pointer and a Reference C++ 【教學】call by value, call by address, call by reference 差別在哪? C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別 C/C++之指標 (pointer)，參考 (reference) 觀念整理與常見問題 (轉貼) Diff Between Call By Reference And Call By Pointer [duplicate] Pointers ","date":"2022-08-19T12:03:19+08:00","image":"https://wongwongnotes.com/images/restored/2022/02/img_0261.jpg","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-value-address-pointer-reference/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","觀念理解"],"title":"【C++ 觀念理解 #1】C++ call by value, call by address (pointer), call by reference 總和比較整理"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 聽聞 OMRON 歐姆龍的體脂計已經很久了!\nOMRON 歐姆龍是來自日本的牌子，\n在網路上有非常多的推薦，這次趁難得特價，與外宿想要好好記錄自己體重變化的需求，就買了!!!\n(被公司一直餵食，感覺快扛不住了XDDD)\n為了自己在外宿的健康著想，必須要開始嚴格追蹤自己的飲食了!!!\n不能被公司養得太幸福XDDD\n因此就卯起來買了聽聞已久的 OMRON 歐姆龍 目前最頂規的體脂計 !!!\n要好好來監督自己有沒有好好地控制飲食!!!\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 OMRON 歐姆龍是來自日本的牌子，\n在網路上有非常多的推薦，\n然後我最主想要的功能是，「藍芽自動同步至手機」，方便我做後續的追蹤，\n因此在挑選上，這個條件是最必要的。\n結果\u0026hellip; 趁特價幾乎直接攻這牌子的頂了(?)\n購入價 我是在 pchome 買的，購入價 $5580，原價 $8880 (省了 3000 多耶!)\n此外還送一個小電風扇，跟小筋膜槍，\n實在是相當的划算 (但其實也滿貴的XDD)\n開箱 △ 收到包裹的樣子，包裹很大，但裡面的東西搖起來就知道，沒塞滿空間，猜是運送過程中一直撞\u0026hellip;\n△ 打開裡面，原來有一個防衝擊的氣墊\u0026hellip; 但這個還單邊XD 保護力真的夠嗎XD △ 裡面有體脂計本體，贈品的小風扇與小筋膜槍! 開箱 OMRON 歐姆龍 - 藍牙傳輸體重體脂計 (HBF-702T) △ 體脂計本體! △ 打開第一眼的樣子! △ 全部內容物，有本體、日文說明書、中文說明書、藍芽使用提示 (不愧是日本的公司\u0026hellip;) △ 日文說明書、中文說明書、藍芽使用提示，來自日本公司的產品 △ 近拍體脂計本體! △ 撕掉表面的保護膜 △ 可以分成兩段的本體，也就是為什麼他主打測的體脂會比別家準! 因為他通過身體的電流不單純只從腳底! △ 隨附四顆電池，直接裝進去吧! △ 裝好了! 準備來啟動囉! 初次設定 \u0026amp; OMRON connect app 他的「初次設定」方式有兩種，有本體直接設定，也有透過 app 設定，\n個人覺得透過 app 設定比較簡單也比較直覺，因此我們就從 app 開始設定吧!\n△ 可以直接去 appstore 下載 OMRON connect app △ 初啟動 app △ 找到我們的型號 HBF-702T △ 個人覺得使用者體驗做得非常好的 app，要做什麼都講得很清楚，完全沒有不知道該幹嘛的時候 △ 所以我們就照著她說的做 △ OK 就成功囉! OMRON connect app 簡介 這邊就不講太細了XD 實際體驗就知道XD\n我自己是覺得紀錄上很完美，但如果能多點健康類的提示會更棒! (例如：要改善XXX，建議「做什麼事情」之類的~)\n△ 下拉直接同步 △ 顏色提醒更新日期，與最後量測時間的差距 △ 可以自己調整想看的內容 △ app 可以直接同步到 apple 的健康功能，可以有一個更全面的健康資訊追蹤 △ app 內，可以自己設定提醒，記得每天都要量體重，方便觀測變化 (我自己剛開箱現在都量爆XDD 目前還不擔心，設開心的) 說明書的導讀 因為說明書很厚，這裡我給自己抓幾個重點\n(就是可能沒看說明書，就不會用的)\n△ 注意一下量測角度 90度，與手的位置，手握的方式 △ 建議量測時間 (不過我自己剛開箱\u0026hellip; 瘋狂量爆，想量就量，不管他了XD) △ 雖然它內建「智慧識別使用者」! 但他預測使用者後，還需要在測量完多按一次「體脂計上的設定鍵」，才算有正確紀錄 △ 自動辨識使用者是滿酷的\u0026hellip; 但實際使用上\u0026hellip;好像可以預設啟動直接是X號使用者會更好? (可能因為只有我自己用XD) 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n我必須說沒什麼好挑剔的XDD，都買到最貴最齊的版本了，\n基本的測量各方面都無可挑剔。\n另外我最想要的「藍芽同步到手機」方便後續追蹤，\n也做得非常的好，app 內建也有做出統計圖表，追蹤的曲線之類的\u0026hellip;\n(我不會提供我的給大家參考的XDD，因此沒圖，別想了XDD)\n目前體感的小缺點大概就只有我上面說的，\n雖然它內建「智慧識別使用者」，\n但他預測使用者後，還需要在測量完多按一次「體脂計上的設定鍵」，才算有正確紀錄\n像我只有一個人使用，這樣多按一個鍵反而有點小囉嗦，但也不能到算是缺點XD，\n畢竟那個「設定鍵」也在一個很好按的地方，\n只能說如果可以用「開機預設使用者」，取代這個「自動識別使用者後，還要確認」，感覺會更好?\nExtra: 贈品開箱 贈品小小開箱一下，畢竟是贈品就不多介紹了\n迷你筋膜槍 △ 迷你筋膜槍外盒 △ 拆開包裝後 △ 剛打開的樣子 △ 全部內容物 △ 滿意外的一點，充電居然是 type-C 小電風扇 △ 電風扇外盒 △ 電風扇全部內容物 ","date":"2022-08-19T02:12:06+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8941-1024x768.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/omron-hbf-702t/","tags":[],"title":"【嗡嗡開箱 #12】OMRON 歐姆龍 - 藍牙傳輸體重體脂計 (HBF-702T) | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 作為一個工程師，鍵盤超級重要！鍵盤就是生命！\n每天都在用！一個好用的鍵盤，\n真的除了保護自己的手指，更會影響自己開發的效率！\n我自己不是追求手感的那型XD，但對我來說「多功能」反而是我非常喜歡的！\n因為「多功能」也代表著更多方便的功能！\n因此今天我們就來開箱這個羅技的新鍵盤吧！\n這篇文我就是用新鍵盤完成的哦!!!\nQQ 結果我之前才剛敗一個類似的，但這個感覺難得出機械式又多功能，還是敗了！\n(對，花我自己的錢錢QQ，羅技哪天能給我業配嗎，都寫你們家的產品四篇了QQ)\n等等會分享羅技家對於「多功能」這件事情，有著「基本上可以被稱作黑科技」的功能！\n這把替換公司的鍵盤，我的前一支羅技鍵盤，一樣是「多功能」，目前仍在家服役中 (薄膜式的軸)，可參考： https://wongwongnotes.com/posts/life-and-work/reviews/reviews/mx-keys/\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 跟之前一樣，最強多工的鍵盤，這次又特別是「機械式鍵盤」！\n對於像我這種\n- 大部分時間都是在開發 - 不是開發的時間也幾乎都是在寫文章，重度鍵盤使用XD - 常常會在多電腦之間切換工作，特別還會跨 windows, mac, linux - 很少打遊戲，幾乎只看實況、YT 這鍵盤應該是非常符合我的需求的！\n購入價 我是在 pchome 買的，購入價 $4990，當時沒有折扣\n開箱 △ 收到包裹的樣子，包裹很大，但裡面的東西搖起來就知道，沒塞滿空間，猜是運送過程中一直撞\u0026hellip;\n△ 開箱後，果然周圍都沒保護XDD 看來真的是運送時一直撞過來\u0026hellip; 好險邊邊沒有撞得太嚴重\n△ 商品正面\n△ 商品外面\n內容物 △ 打開後，這個精簡的說明與俐落包裝的盒子，十分舒爽\n△ 鍵盤正面 與 隨附的小卡\n△ 再一張鍵盤正面\n△ 把所有的內容物拿出來\n△ 特寫一下接收器的部分，這次給的是新版(黃色的)，上次附的是舊版(紅色的)!\n使用 △ 側邊可以小墊高一下，打起來角度會更舒服!\n△ 測試一下鍵盤背光，暗中也是十分的具有質感\n△ 這種有質感又低調的設計，非常適合擺在辦公室!\n△ 最後的鍵盤特寫\n羅技神奇的多功能跨平台「黑科技flow」 註： 本功能需要搭配 MX Master 系列滑鼠使用!\n這台電腦複製，那台電腦貼上，不管作業系統，「跨OS」還能「跨螢幕」無痛切換!\n其實我也不是什麼羅技的鐵粉，但我怎麼知道，\n我想要的功能，他剛好都做出來了XDDD，我也只好買下去了XD\n我們來看一次這個能解釋一切的 gif：\n因為這個無痛跨「windows」、「mac」電腦的滑鼠與鍵盤，\n完全解決我的痛點，甚至「還不用從滑鼠鍵盤底下切換」，\n我只能給滿分了，目前沒看過類似的產品，這完全就是我要的！！！\n題外話：使用的鍵盤與滑鼠組合為：MX Mechanical + MX master 3\n羅技「flow」技術，「鍵盤滑鼠一起」無痛地跨到另外一台電腦 羅技「flow」更誇張的是，滑鼠移動的同時，連鍵盤都可以「自動切換」過去。\n多台電腦的概念，直接變成「一組鍵盤滑鼠」，就能實現全部控制。\n目前這真的是我碰到最無痛的使用方式了，\n以往都還要「按鍵盤或滑鼠的切換按鍵」，這次「直接滑鼠移過去就好了」。\n好扯XDDD\n羅技「flow」技術，無痛「跨 OS」檔案傳輸 羅技「flow」還有另外一個主打功能，\n就是滑鼠拖曳檔案的同時，或是鍵盤複製貼上的時候，\n可以直接「無痛的在另外一台電腦上貼上」。\n讓我們來看看示範 gif，「不管作業系統，這台複製，那台貼上」： 當初我第一次試用這個功能的時候\u0026hellip;\n我心中全部都是問號．．．\n羅技你不是做滑鼠鍵盤的嗎！！！怎麼搞起檔案傳輸來了！！！\n太扯了太扯了！！！ (讚到不行的意味，好扯，真的好用)\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n先說我才剛開始使用XD，我也只體驗打了這篇文章，\n第一個最直覺的感覺是，我超不習慣茶軸啊啊啊啊啊!!!!! (完全是個人問題XD) 之前的鍵盤都是薄膜或是青軸，茶軸原來這麼沒段落感\u0026hellip;\n打了這篇文章我深刻體會，沒有段落感打起來十分輕盈，但好像少了什麼回饋的感覺XD\n總之畢竟我之前都沒有用過類似的鍵盤，應該可以習慣一下XD (這不是缺點XD，但是我真實的體感心得!)\n因為我本來就是打算買一個 mac 為主的鍵盤，同時通用於 windows，\n因為桌子小的關係，不希望同時擺兩組鍵盤，\nMX Mechanical 的 快速在不同作業系統切換很讚，一個按鍵就能快速切換 windows 與 mac 了！\n而且反應感覺比 MX Keys 還要更快速! (這就是3000-\u0026gt;5000的金錢力量嗎)\n對於我這種雙修「mac 筆電 + windows 桌電」的人來說，真的是非常不錯的產品!\n特別是 flow ! 不管是在 windows/mac 上面複製，都可以直接在 mac/windows 上貼上檔案，\n這黑科技功能，真的是讓我這個跨平台開發者愛不釋手!!\n我覺得這產品對我來說真的讚！\n他成功滿足了我\n- 多電腦間切換，windows, mac - 多電腦切換後，且鍵盤能都適用 - 可以讓我以外接鍵盤的方式，適應 mac 上面鍵盤的配置 (可以說是把 mac 接螢幕當桌機用) - 無線，終於可以不用困擾於桌面的到處都有亂亂的線了 (還是有啦，只是有少了有感XD) - 羅技的黑科技「flow」，快速跨平台的複製貼上檔案，使用跨平台使用電腦更加的無痛。 ","date":"2022-08-16T23:50:31+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8875_4_MOV_AdobeExpress.gif","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/mx-mechanical/","tags":[],"title":"【嗡嗡開箱 #11】羅技 - MX Mechanical 鍵盤 (茶軸) | 不專業開箱文"},{"categories":["914 - Wordpress"],"content":"前言 這篇也是寫給有想要建立自己的網站，但還在猶豫要花多少錢 (成本) 的人們。\n還有我自己的心態，與做這件事情的心情分享。\n內文全部都是實際的金額，供想要建立自己的 wordpress 網站的人參考。\n先講結論：個人建立網站花費成本 註：本網站成立於 2020/10/13，蛤? 你說怎麼有這之前的文章?!\n你知道的太多了，沒有啦，我是依據我「作筆記當下的時間」決定發文日期的。\n再加上還有一些其他不好說的理由\u0026hellip;? 品項 花費 備註 SugarHosts 虛擬主機 (2020-2023) 6372(3年)+95(國外交易服務費) 主機的選擇，價格會有不同 SugarHosts 虛擬主機 (2023-2029) 7343(6年)+110(國外交易服務費) 註：主機商於 2025 年跑路，此消費僅用不到 2 年 購買網站主題 Avada 1739(永久)+26(國外交易服務費) (非必要) 免費的主題也很好用 購買網站主題 Blocksy 2905(永久) (非必要) 免費的主題也很好用 第一年網址 (2020-2021) wongwongnotes.com 0 與虛擬主機一同購買，第一年免費 第二年網址 (2021-2022) wongwongnotes.com 513(1年)+7(國外交易服務費) 第三年網址 (2022-2023) wongwongnotes.com 513(1年)+7(國外交易服務費) 第四年網址 (2023-2024) wongwongnotes.com 513(1年)+7(國外交易服務費) 第五年網址 (2024-2025) wongwongnotes.com 513(1年)+7(國外交易服務費) 合計成本 20670 (台幣) 好像看起來是一種還可以的投資?\n但其實我覺得這樣計算成本並不是很能反映現況\u0026hellip;\n我更喜歡與我朋友講這個版本：\n品項 花費 備註 SugarHosts 虛擬主機 (2020-2023) 6372(3年)+95(國外交易服務費) 主機的選擇，價格會有不同 SugarHosts 虛擬主機 (2023-2029) 7343(6年)+110(國外交易服務費) 註：主機商於 2025 年跑路，此消費僅用不到 2 年 購買網站主題 Avada 1739(永久)+26(國外交易服務費) (非必要) 免費的主題也很好用 購買網站主題 Blocksy 2905(永久) (非必要) 免費的主題也很好用 第一年網址 (2020-2021) wongwongnotes.com 0 與虛擬主機一同購買，第一年免費 第二年網址 (2021-2022) wongwongnotes.com 513(1年)+7(國外交易服務費) 第三年網址 (2022-2023) wongwongnotes.com 513(1年)+7(國外交易服務費) 第四年網址 (2023-2024) wongwongnotes.com 513(1年)+7(國外交易服務費) 第五年網址 (2024-2025) wongwongnotes.com 513(1年)+7(國外交易服務費) 無限的時間成本 無價 會犧牲休息時間、甚至睡眠時間，甚至減少與朋友出門遊樂的時間在經營網站、撰寫文章 合計成本 20670 (台幣) + 無價、無限的時間成本 實際上，我認為「無價、無限的時間成本」才是最貴的。\n先不考慮能帶來的收入多寡，需要「花費的時間成本」也必須列入考慮，\n我認為經營網站有沒有「興趣」非常的重要，這才是真正能堅持下去的關鍵。\nSugarHosts 虛擬主機 基本上建立一個自己的網站，一定需要租用一台虛擬主機 (或自己弄)，\n租用主機就像是去租一台永遠不會關機的電腦，(如果關機了那怎麼提供服務呢?)\n就算是 Medium 或是 Blogger，我們也是借用他們的主機，才能讓我們的文章一直上線哦！\n市面上虛擬主機服務有太多種類了，我自己也是選了很久，\n包含 BlueHost、SiteGround (現在退出亞洲市場了) 什麼的都有詳細查過資料，\n優點：選用 SugarHosts 最關鍵原因：支持「全中文服務」 最後選 SugarHost 的原因就是因為，他可以使用「完全中文的服務」，\n其實我也知道台灣人很多英文都很不錯，但說實在有時候專業的網頁問題，\n還要改用英文敘述，我整個頭都痛起來，「不是英文不行，只是真的太累了」XDD。\n服務示意圖: 抱歉...我一個人架網站真的問題很多XD 要不是可以中文我應該會先溝通到吐血(誤 感謝客服超親切又仔細的「中文」回覆! 缺點：比上不足，比下有餘 (?) 缺點我也老實講，我本身就是使用者，\n「你覺得這網站感覺怎麼樣，基本上就直接反應你覺得的好壞。」\n如果說要 CP 值最高，絕對有更好的選項。只是你碰到問題要用英文去問。 如果說要 網頁速度最快，絕對有更好的選項。只是你碰到問題要用英文去問。 不管怎麼比，絕對有更好的選項。我相信每個人都有比較注重的點。但我覺得 SugarHost 各方面都很夠用了，而且支援中文直接掩蓋其它的缺點 (其實也沒差多少) 了！(對我來說) (別以為自己不會碰到問題，我就超常碰到問題XDDD，\n而且身為工程師的我都覺得很難搞了，想要自己搞的慎思啊！)\nSugarHosts 參考網址：點我到 SugarHosts 官網 可以先多參考、多比較，畢竟我也是比較了許多虛擬主機後才做出了選擇XD。\n不過我主要只推薦他的中文服務，光這點就大勝了!!!(到底有多不想碰英文XDD)\n此連結也同時為聯盟行銷網址，如果您覺得我的文章不錯想要支持我，\n歡迎也從此連結點擊購買服務，您的鼓勵會經由廠商給予我一些分紅。\n我自己都直接買了3年，也因為覺得好用所以才推薦的！對，這就是我拿我自己來業配(喂)\n購買網站主題 這個就非必要花費了，有很多免費的主題也非常好看！\n不過為了美觀，與各種「客製化」功能，就可能需要有這樣的花費囉！\n無限的時間成本：「你願意花多少時間？」 建一個網站，最貴的絕對是「時間成本」，\n你願意花上多少時間，勢必相對的也就有所犧牲。\n這點我認為才是最大的考驗，能夠付出多少的時間，\n相對的，休息時間也會減少、睡眠時間可能也會減少、陪朋友的遊樂的時間、陪家人出門的時間\u0026hellip;\n這些都是一種變相的成本，建議想要好好建立網站的人，\n務必先考慮一下這一點：「你願意花多少時間？」\n個人建立網站心態分享 想想建立網站的目的是什麼？ 也許很多人建立網站的目的是「賺錢」為優先。\n畢竟是為了生活，誰不想靠網站賺錢呢？\n但如果真的要建立網站，以過來人的經驗，\n除非本身就已經很有名氣，建議第一時間不要先想「賺錢」 (除非你有毅力能堅持個好幾年的賠本經營)\n我一開始就知道，如果我一開始就把「賺錢」放在我目標的第一順位，\n那也許前幾年的賠本會先導致我喪失創作的熱情，認賠殺出。\n很自然的，「賺錢」絕對不是當初我建立網站目的第一順位，甚至連順位都沒有XD\n我想做這件事，到底是為了什麼？ 我對寫文章本身就很有興趣，在我的 google doc、 hackmd、icloud 裡面都有我的大量筆記，\n老實說跨程式搜尋筆記有點麻煩，而且很多筆記就是放在那裡了，也沒有生出額外的價值。\n但這些筆記應該能幫助到很多人，從我當助教的經驗我有了這樣的體會。\n於是我開始找尋能分享我筆記的方式。\n任何事情先為了自己而做，如果可以再順便幫助別人。 每個人的時間有限，總是要先做能幫助自己的事情，可以的話，再順便幫助別人。\n其實這個網站最大的受惠者就是「我自己」，他就是我「最大的筆記本」，\n所有我過去的筆記、過去的整理，全部都存在這個網站上。\n文章撰寫風格、網站定位，我真正在做的是「讓我自己更方便的事情」 也因為最大受眾是「我」，\n我認為「筆記」應該最優先符合「快速」與同時「我看得懂就好」，\n但為了不要太過只有「我看得懂就好」XDD，\n所以我有時文章也會「為了多一點看得懂的受眾」，稍微修改一些「讓大家也可以看得懂」。\n所以網站的文筆自然不會「太過專業」與「嚴謹」，畢竟最主要是我要看的XD\n也因此，我並不擔心沒有收益的問題。因為我就在做「能讓我更方便的事情」。\n我那時有個夢想，哪天我照著「我的想法」，在 google 輸入「有我特色的關鍵字」\n我就能找到我的文章，看著我曾經的筆記。\n(換句話說，我在 google 製作「屬於我的關鍵字」)\n(啊，不過這個夢想已經實現了XDDD)\n小結 老實說，剛開始架一個網站流量不多的時候(也是現在)，\n其實幾乎都是在燒自己的錢換興趣XDD，\n不過既然都是燒我自己的錢，文章只有我看得懂應該沒問題吧(誤)\n大概就是這樣囉，如果有新的花費也會持續更新在這裡，\n也算是給我自己一個紀錄！\n","date":"2022-08-14T15:48:05+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7755-1024x643.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/build-wordpress-cost/","tags":["WordPress","初學者"],"title":"【Wordpress #0】建立/架一個網站大概要花多少錢? 與個人建立網站心態分享 How Much Does It Cost to Build a WordPress Website?"},{"categories":["720 - C++ LeetCode"],"content":"題目出處 https://leetcode.com/problems/longest-palindromic-substring/\n難度 Medium\n個人範例程式碼 – 2022/8/12 一刷 class Solution { public: string longestPalindrome(string s) { string tmp_ans, final_ans = \u0026#34;\u0026#34;; for(int i = 0; i \u0026lt; s.length() ; i++){ tmp_ans = is_palindrome(s, i, i); if(tmp_ans.length() \u0026gt; final_ans.length()) final_ans = tmp_ans; tmp_ans = is_palindrome(s, i, i+1); if(tmp_ans.length() \u0026gt; final_ans.length()) final_ans = tmp_ans; } return final_ans; } string is_palindrome(string s, int start, int end){ while(0 \u0026lt;= start \u0026amp;\u0026amp; end \u0026lt; s.length() \u0026amp;\u0026amp; s[start] == s[end]){ start -= 1; end += 1; } // s.substr(start, len) // s[start+1:end] -\u0026gt; s.substr(start+1, end-(start+1)) string split_string = s.substr(start+1, end-(start+1)); // cout \u0026lt;\u0026lt; split_string \u0026lt;\u0026lt; endl; return split_string; } }; /* python solution class Solution: def longestPalindrome(self, s: str) -\u0026gt; str: ans = \u0026#34;\u0026#34; for i in range(len(s)): tmp_ans = self.is_palindrome(s, i, i) if len(tmp_ans) \u0026gt; len(ans): ans = tmp_ans tmp_ans = self.is_palindrome(s, i, i+1) if len(tmp_ans) \u0026gt; len(ans): ans = tmp_ans return ans def is_palindrome(self, s, start, end): while(0 \u0026lt;= start and end \u0026lt; len(s) and s[start] == s[end]): start -= 1 end += 1 # print(s[start+1:end]) return s[start+1:end] */ 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n個人是從 python 練熟後，把概念帶過來練習 C++，「python 解」可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-5/\n算法說明 以中間作為 palidrome 搜尋開始的起始位置，\n另外寫一個驗證 palindrome 用的 function。\ncorner case 特殊情況處理 一起在判斷 palindrome 的 function 中做掉。\nBoundary conditions/ Edge conditions 邊際情況處理 注意搜尋範圍 0 \u0026lt;= start, end \u0026lt; len(s) 注意離開迴圈後，回傳 s[start+1:end]，而不是直覺的 s[start:end+1] C++ substr 用法複習 在 python 裡面，我們可以比較輕鬆的使用 string split，\n我們在 C++ 需要使用 string.substr() 來代替，\n用法是 「string.substr(start, len)」，\n可以養成參考官方文件的習慣：\nstd::string::substr Reference std::string::substr Detailed analysis and explanation https://leetcode.com/problems/longest-palindromic-substring/discuss/751188/Simple-Easy-to-Follow-Python ","date":"2022-08-12T02:17:02+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/cpp-solutions/c-leetcode/leetcode-cpp-5/","tags":["C++","LeetCode"],"title":"【Leetcode】C++ - [5] Longest Palindromic Substring 個人筆記 | 內含 C++ string.substr() 用法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 https://leetcode.com/problems/longest-palindromic-substring/\n難度 Medium\n題目分類 String, DP\n個人範例程式碼 - 2022/8/12 三刷 class Solution: def longestPalindrome(self, s: str) -\u0026gt; str: ans = \u0026#34;\u0026#34; for i in range(len(s)): tmp_ans = self.is_palindrome(s, i, i) if len(tmp_ans) \u0026gt; len(ans): ans = tmp_ans tmp_ans = self.is_palindrome(s, i, i+1) if len(tmp_ans) \u0026gt; len(ans): ans = tmp_ans return ans def is_palindrome(self, s, start, end): while(0 \u0026lt;= start and end \u0026lt; len(s) and s[start] == s[end]): start -= 1 end += 1 # print(s[start+1:end]) return s[start+1:end] 算法說明 以中間作為 palidrome 搜尋開始的起始位置，\n另外寫一個驗證 palindrome 用的 function。\ncorner case 特殊情況處理 一起在判斷 palindrome 的 function 中做掉。\nBoundary conditions/ Edge conditions 邊際情況處理 注意搜尋範圍 0 \u0026lt;= start, end \u0026lt; len(s) 注意離開迴圈後，回傳 s[start+1:end]，而不是直覺的 s[start:end+1] 個人範例程式碼 - 2022/3/28 二刷 class Solution: def longestPalindrome(self, s: str) -\u0026gt; str: if not s: return \u0026#34;\u0026#34; final_ans = \u0026#34;\u0026#34; for index in range(len(s)): # odd case tmp_ans = self.find_max_palindrome(s, index, index) if len(final_ans) \u0026lt; len(tmp_ans): final_ans = tmp_ans # even case tmp_ans = self.find_max_palindrome(s, index, index+1) if len(final_ans) \u0026lt; len(tmp_ans): final_ans = tmp_ans return final_ans def find_max_palindrome(self, s, right_index, left_index): while(right_index \u0026gt;=0 and left_index \u0026lt; len(s) and s[right_index] == s[left_index]): right_index -= 1 left_index += 1 # when failed, get last ans return s[right_index+1:left_index] 算法說明 第二次解開始注重 coding 本身能夠自己解釋出邏輯的部分。\n另外還有注重邊界檢測與 input 異常處理。\n分成 odd case 與 even case 分開處理，\n可以同樣以中心出發，往兩側前進 (注意邊界)\n邊界檢測 檢查 for-loop, while-loop 是否有可能有越界問題\ninput 異常處理 主要處理以下：\ninput s 為 \u0026ldquo;\u0026quot;，回傳 \u0026quot;\u0026rdquo; 個人範例程式碼 - 2021/3/31 一刷 class Solution: def longestPalindrome(self, s: str) -\u0026gt; str: ans = \u0026#34;\u0026#34; for r in range(len(s)): word = self.checkPalindrome(s, r) # print(f\u0026#34;word = {word}\u0026#34;) if len(word) \u0026gt;= len(ans): ans = word return ans def checkPalindrome(self, s, idx): # case 1: i-1, i+1 k = 0 word1 = \u0026#34;\u0026#34; while(idx-k \u0026gt;= 0 and idx+k \u0026lt; len(s) and s[idx-k] == s[idx+k]): # record word1 = s[idx-k:idx+k+1] # print(f\u0026#34;idx = {idx}, k = {k}, word1 = {word1}\u0026#34;) # debug # move to next k += 1 # case 2: i, i+1 k = 0 word2 = \u0026#34;\u0026#34; while(idx-k \u0026gt;= 0 and (idx+1)+k \u0026lt; len(s) and s[idx-k] == s[(idx+1)+k]): # record word2 = s[idx-k:(idx+1)+k+1] # print(f\u0026#34;idx = {idx}, k = {k}, word2 = {word2}\u0026#34;) # debug # move to next k += 1 # return longest word(substring) if len(word1) \u0026gt;= len(word2): return word1 else: return word2 向前一個個搜尋 直覺的想，我們把每一個 index 都當作一個起點，進行各自的搜尋\n單一 index 搜尋最佳解 這裡我們發現可以分成兩種 case\ncase1: i-1, i, i+1 類型 (也就是有包含自己跟自己相同) case2: i-1, i, i+1, i+2 類型 (也就是自己跟自己+1相同) 注意：範圍的上下限，就是這題最需要細心的地方。\n回收 case1, case2 結果，更新最佳答案，直到搜尋結束。 Reference https://leetcode.com/problems/longest-palindromic-substring/discuss/751188/Simple-Easy-to-Follow-Python ","date":"2022-08-12T00:37:50+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/img_7733-1-1024x768.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-5/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [5] Longest Palindromic Substring 個人筆記"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 由於我買的 Mac 容量只有 256 GB，\n最近因為要剪環島的 vlog 影片，開始有了容量不夠的問題，\n因此決定買了一個 type C 的隨身碟！\n而且為了讀取速度，我還特別選了 SSD 的 USB！\n我自己事前查到的評價 其實買這個前沒有特別查，但很多 youtuber 有推薦 Mac 剪片的時候，\n建議買 SSD 的外接硬碟 (隨身碟)！ 因此就買了！\n購入價 PChome 購入，購入價 3199\n開箱 △ 包裝的外盒\n△ 拆開來後的樣子 使用 △ 插進電腦裡面的樣子，type C 直接插就能讀取非常的方便！另外一邊是常見的 USB，也能夠直接使用！非常方便！ △ 可以看到容量接近 500GB 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n能夠直接插的 type C USB 非常方便！\n特別是針對 Mac 的使用者，再加上 Mac 本身容量通常都會有限制，\n非常實用的周邊！\n再加上同樣一個 USB，可以直接換一個 USB 頭，\n照樣能直接讀取，真不愧是新時代的產物，就是這麼的方便！\n(雖然價格也是XDD 同樣容量的價格好像不是這樣的 誰叫我還要特別買SSD )\n","date":"2022-08-11T17:44:31+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_7895-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/transcend-jetflash930c-512gb/","tags":[],"title":"【嗡嗡開箱 #10】Transcend 創見 - JetFlash930C 512GB Type C 高速高耐用雙頭隨身碟 | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 最近由於外宿的關係，需要買一個新的喇叭，\n不是很追求音質，只希望之後搬家方便，就買了一個！\n我自己事前查到的評價 因為住宿處沒有音響，對音質不是很追求，\n但又想要有一個簡單便宜又夠用的 (方便之後搬家)，\n因此就挑了一個最便宜又好用的喇叭！\n(絕對不是追求音質的人的選項！但追求外宿方便很讚！)\n購入價 原價屋購入，購入價 429\n開箱 △ 產品外盒 △ 拆開包裝 △ 第一眼看內盒裡面的樣子 △ 把全部零件都拿出來看一下 △ 說明書在盒子上 △ 這就是我們的迷你喇叭囉 △ 把外面的貼膜撕掉 設置 △ 把它擺在主機的上方，很小很方便擺放 △ USB 供電的，因此也不用特別拉電源線 △ 最後拍一張合照 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n就是個簡單便宜的小喇叭，對於外宿的人來說非常方便！\n(之後如果還要搬家也很方便！)\n再加上不追求音質，因此這樣的價格我覺得還滿 OK 的！\n","date":"2022-08-11T17:14:34+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8035-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/logitech-z120/","tags":[],"title":"【嗡嗡開箱 #9】Logitech 羅技 - Z120 二件式 有線喇叭 | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 最近由於外宿的關係，需要買一個新的檯燈，\n於是就買了一個螢幕掛燈來使用！\n我自己事前查到的評價 朋友的推薦，由於自己使用電腦的需求比較多，\n因此能夠有搭配螢幕的檯燈就是最棒的了！\n因此就買了這個檯燈\n購入價 我是直接去小米的實體店買了，沒有特價，價格 995 (1000 有找哈)\n開箱 △ 包裝的外盒 △ 打開來後的樣子 △ 把全部零件拿出來看一下 組裝 △ 把電燈連接電源處 △ 把充電線插上去 △ 擺到螢幕上面的位置 △ 透過螢幕後面的 USB 供電 △ 設置完成！\n使用 △ 把電池放進去，可以用這個控制燈光大小與燈光色溫 △ 暖色燈光的狀態 △ 白色燈光的狀態 使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n螢幕上的電燈非常方便，特別對於「生活幾乎都在使用電腦的人」來說，\n幾乎直接對應到需求，而偶爾會有想要閱讀或寫字的時候，\n這樣的燈光也相當足夠，很適合搭配螢幕一起使用！\n題外話：放置於螢幕上方的電源供應支架其實很重，所以非常不容易掉下來哦！ ","date":"2022-08-11T16:55:34+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8024-scaled.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/xiaomi-lighting/","tags":[],"title":"【嗡嗡開箱 #8】小米 - 米家螢幕掛燈 | 不專業開箱文"},{"categories":["360 - Linux 網路遠控"],"content":"前言 注意：為了避免有心人拿去做壞事，這篇我不會講太多細節，只是給我自已留個筆記XDD\n主要是給需要幫助的人提供幫助。\n最近因為在外住宿，有些生活上的好多事情還需要習慣一下，\n其中突然發現房東忘記給 wifi 密碼 (X\n其實我是可以直接問啦XDD，但基於順便幫忙房東檢查網路安全問題，\n就順便去看看了一下後台，但「理論上我不應該看得到的XDDD」\n漏洞 簡單說就是利用大部分人設定 router 懶得改密碼的習慣，\n直接用預設帳號密碼登入後台 (嗯，雖然聽起來很不應該，但還真的很多XD)\n這篇的用意，主要是「提醒大家記得要改密碼」，不然真的很容易被有心人入侵\n前提 (事前準備) 能夠實現這件事情有幾個先決條件，\n首先你要有一條有線網路，直接插到 router 上，\n這時如果這台 router 有設定 DHCP (自動配給你 ip)，你應該就能連進去內網了。\n(另外一種 case 是我已經知道密碼，但想連進去改自己的 QoS，那就是題外話了。)\n登入後台 通常 router 後台設定，由 192.168.0.1 就可以登入\n可以自己試一下，或自己網路上找其他可以登入的入口\n一些常見的後台入口紀錄：\n可以先試：192.168.0.1 ASUS 後台：router.asus.com/index.asp 取得帳號密碼 這邊都是預設很多人不改帳號密碼的前提，\n如果嘗試登入失敗了，那也就不用嘗試了，\n那表示他們\u0026hellip; 有記得改密碼XD！\n但問題就出在一堆人都不改密碼啊XDDD\n如果 router 就在你附近 直接翻到 router 背面，你通常就會看到帳號密碼了，如果沒改，\n預設的這組就登的進去。\n如果 router 你不知道藏在哪裡 啊對，這邊的確有個 bug，如果你一開始就不知道密碼，確實你也連不進去，\n但在已經連進去區網的情況下 (已知道 wifi 密碼，想去改後台)，\n我們可以透過這個方式連進去改一些其他設定。\n你\u0026quot;也許\u0026quot;可以從上面的後台登入網站，知道 router 的型號或廠牌是哪一家的。\n上網找對應 router 型號的常見帳號密碼，也許有機會。\n登入後，找到 wifi 設定 找到 wifi 設定，直接按顯示密碼，\nOK！密碼 get！(所以就說真的要改密碼啊\u0026hellip;)\n一些進階的修改 警告：不懂或沒自信就不要亂改，改壞了\u0026hellip; 我救不了你 = = 修改 QoS：我建議不懂千萬不要亂改 QoS 是 Quality of Service 的縮寫，代表服務品質的意思，\n我們可以透過修改這個達到比較好的網路連線品質，\n(但效果非常有限，原本就慢的不會特別變飛快)\n總之，這裡我不會多介紹要怎麼改，而且也要 router 有提供 QoS 的功能可以設定，\n不然其實建議不要亂改XDD\n再次強調 本篇發出來主要用意是希望大家能養成「改密碼的好習慣」，\n免得密碼被駭都不知道，\n現在這篇直接示範給你看，希望能夠讓大家更有警覺才是最重要的！\n後續 我幫房東保護了他的密碼，房東該幫我優惠一點房租吧\nRefernce How to find your router IP on every device QoS：優化您的 Wi-Fi 網路 (針對家用路由器) ","date":"2022-08-10T22:36:03+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/wifi_backend.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/get-wifi-password/","tags":["Linux","網路遠控"],"title":"【生活駭客(?)系列】我的房東忘記給我 wifi 密碼，但我居然可以直接從 wifi 後台看得到?! (機會教育)"},{"categories":["015 - Final Cut Pro X"],"content":"前言 Final Cut Pro 是 Mac 上公認好用的剪輯軟體之一，\n上手後那更是如虎添翼，\n這邊記錄一些自己的學習筆記。\n新手教學 如何第一次自學 Final Cut Pro 就剪出一部專業級的作品？ ＃剪輯部分\n剪接快速鍵 - 單一 「快速切割」：cmd + B 剪接快速鍵 - 多段 按 B，滑鼠會變成剪刀 (刀片工具) 再按一次 A，還原成滑鼠 新增影片快速鍵 在素材區按 E (記法: end) (如果沒用，注意是不是不小心切到注音)\n時間軸做記號 M 做記號 Q 加入音效 P (Position) 模式 差別：保留原來的影片位置，移動影片\n沒有 P 模式時，移動影片會「自動往前遞補」\n群組 compound clip (群組功能)\n字幕相關 新手剪輯必看｜2022上字幕全宇宙最快方法｜超省時間 支援簡轉繁 SRT ｜Final Cut Pro \u0026amp; Premier￼ FCPX上質感黑底字幕的方法，間距超重要！｜Final Cut Pro教學第十集 https://github.com/howarder3/jy-srt-tools\nhttps://www.youtube.com/watch?v=td1EO3I1jJA\nHDR 顏色處理 去濾鏡那邊找，HDR 效果\n複製格式 複製格式 「cmd + c」 貼上格式 「shift + cmd + v」 FCPX 介面 部分 快速縮放至全影片預覽 「shift + Z」、觸控滑鼠點兩下 時間軸放大縮小 「cmd +」與「cmd -」時間軸放大/縮小 【軟體教學】Final Cut Pro 放大縮小 |時間軸 Timeline| 的五大方法 (超完整)\n預覽影片 「space」開始/暫停 聲音部分 控制聲音淡入淡出 final cut pro声音渐弱 淡入淡出 音频渐弱 fcpx 声音淡出final cut pro x zzLab mac 容量相關 不要複製一份檔案，只單純取用原始檔案位置進行剪接。\n【教學】FinalCutpPro新手必學五大內建功能｜字幕背景 降噪 穩定 和別的 vlog 相關 youtube 音樂資源庫 音樂庫 Reference 在 Final Cut Pro 中将片段剪切为两部分 音樂庫 新手剪輯必看｜2022上字幕全宇宙最快方法｜超省時間 支援簡轉繁 SRT ｜Final Cut Pro \u0026amp; Premier￼ 【教學】FinalCutpPro新手必學五大內建功能｜字幕背景 降噪 穩定 和別的 如何第一次自學 Final Cut Pro 就剪出一部專業級的作品？ FCPX上質感黑底字幕的方法，間距超重要！｜Final Cut Pro教學第十集 ","date":"2022-08-01T22:07:09+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/final-cut-pro-x/final-cut-pro-x/","tags":["Cut","Final","Pro"],"title":"【FCPX】Final Cut Pro X 影片剪輯 操作 \u0026 快速鍵 個人整理筆記"},{"categories":["198 - Python 問題解決"],"content":"前言 這是我在串接 Google Sheet API 時發生的問題\ngoogleapiclient.errors.HttpError: \u0026lt;HttpError 403 when requesting https://sheets.googleapis.com/v4/spreadsheets/1U-6cSK4qLwCnJUktHn1noUkDM7u5JvwjYCSIy4GbVkA?fields=properties%2Csheets%2Fproperties%2CspreadsheetId%2CnamedRanges\u0026amp;includeGridData=false\u0026amp;alt=json returned \u0026#34;The caller does not have permission\u0026#34;. Details: \u0026#34;The caller does not have permission\u0026#34;\u0026gt; 解決方法 這個問題的發生原因，以我的情況來說，\n是因為我還「沒有把 google sheet 的編輯權限給 API 的 email」，\n只要給編輯權限即可解決。\n解決範例 共用 google sheet 編輯者 (讓程式可以編輯) 跟我們通常使用 google sheet 邀請共同編輯的概念相同，我們一樣可以在 google sheet 中找到共享的部分，\n並且把要編輯的文件共享給上篇文章設定的開發者 email\n這邊只要按下確認就會自動開啟權限了，不用特別再去收信什麼的。\n不知道 email 在哪的，這張圖幫你回憶一下： Reference Google Sheets API returns \u0026ldquo;The caller does not have permission\u0026rdquo; when using server key ","date":"2022-07-01T14:58:47+08:00","image":"https://wongwongnotes.com/images/restored/2021/01/google_sheet_api_9.5.png","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/the-caller-does-not-have-permission/","tags":["Python"],"title":"【Python】Google Sheet API 問題解決：googleapiclient.errors.HttpError: \"The caller does not have permission\". Details: \"The caller does not have permission\""},{"categories":["182 - Python 網頁爬蟲"],"content":"前言 這篇文章是 【Python】設定 google sheet API 並取得 json 金鑰，讓我們的資料能同步更新至雲端 google sheet 表格 (內含完整圖片說明) 的下一篇，\nhttps://wongwongnotes.com/posts/python/web-automation/python/google-sheet-api/\n這篇我們專注再設定程式碼的部分。\n如果還有「設定 google sheet API 並取得 json 金鑰」的問題，請去上面的文章進行操作。\npygsheets 操作 pygsheets 已經幫我們把大部分的複雜功能簡化過了，因此我們可以很容易的使用 pygsheets 來操作 google sheets，\n重要的前置步驟，共用 google sheet 編輯者 (讓程式可以編輯) 跟我們通常使用 google sheet 邀請共同編輯的概念相同，我們一樣可以在 google sheet 中找到共享的部分，\n並且把要編輯的文件共享給上篇文章設定的開發者 email\n這邊只要按下確認就會自動開啟權限了，不用特別再去收信什麼的。\n不知道 email 在哪的，這張圖幫你回憶一下：\npygsheets 讀入 sheet 這邊我們要注意兩個地方，\n一個是我們剛剛下載下來的 json 金鑰，記得貼上對應的路徑位置 一個是你的 google sheet 網址 (看網址就知道了)，記得貼上 import pygsheets auth_file = \u0026#34;xxxxxxxxxxxxxxxxxxxxxxxxxx.json\u0026#34; gc = pygsheets.authorize(service_file = auth_file) # setting sheet sheet_url = \u0026#34;https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/\u0026#34; sheet = gc.open_by_url(sheet_url) 這樣做完後，sheet 就已經被我們讀進來了。\npygsheets 從 sheet 找對應 工作表 工作表就是我們 google sheet 下方會看到的工作表分頁，這邊我們要找一樣的名字。\n#選取by名稱 sheet_test01 = sheet.worksheet_by_title(\u0026#34;test01\u0026#34;) pygsheets 表格讀取 (read) # read A1 = sheet_test01.cell(\u0026#39;A1\u0026#39;) print(A1) print(A1.value) 範例 結果 pygsheets 表格寫入 (write) # write sheet_test01.update_value(\u0026#39;A2\u0026#39;, \u0026#34;test_A2\u0026#34;) # 單一個 sheet_test01.update_values(\u0026#39;B2\u0026#39;, [[\u0026#39;A\u0026#39;, \u0026#39;B\u0026#39;, \u0026#39;C\u0026#39;, \u0026#39;D\u0026#39;]]) # 橫的 sheet_test01.update_values(\u0026#39;A3\u0026#39;, [[\u0026#39;3\u0026#39;],[\u0026#39;4\u0026#39;],[\u0026#39;5\u0026#39;],[\u0026#39;6\u0026#39;]]) # 直的 結果 pygsheets 表格插入 (insert) 插入某一行，要注意邏輯是我們填入的數字的後一行\n(換句話說，我們如果填 1，「 1 不會動，會插入的是 2 」)\n# insert col = 1 sheet_test01.insert_cols(col, number=3) # add 2, 3, 4 (B, C, D) row = 1 sheet_test01.insert_rows(row, number=3) # add 2, 3, 4 結果 pygsheets 帶有值的表格插入 (insert with values) 內建的函數就有這個 keyword arguments，直接使用即可，\n(注意 values 必須是 matrix。)\n# insert with values values = [[\u0026#39;M\u0026#39;, \u0026#39;N\u0026#39;, \u0026#39;O\u0026#39;]] # matrix insert_rows = 1 # add start from 2 sheet_test01.insert_rows(insert_rows, number=1, values=values) 結果 pygsheets 表格結尾填入 (append) 會直接插在文件的最末尾，注意 values 必須是 matrix\n# append values = [[\u0026#39;X\u0026#39;, \u0026#39;Y\u0026#39;, \u0026#39;Z\u0026#39;]] # matrix sheet_test01.append_table(values=values) 結果 最後，把自己常用的功能封裝，供以後方便使用 留給我以後重複開發類似功能，實作快速使用，順便幫助有需要的人\nimport pygsheets auth_file = \u0026#34;xxxxxxxx.json\u0026#34; gc = pygsheets.authorize(service_file = auth_file) sheet_url = \u0026#34;https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxx/\u0026#34; global_sheet = gc.open_by_url(sheet_url) class google_sheet_helper: def __init__(self, sheet_name = \u0026#34;work01\u0026#34;): # setting sheet self.working_sheet = global_sheet.worksheet_by_title(sheet_name) def write(self, position = \u0026#34;A1\u0026#34;, content = \u0026#34;test\u0026#34;): self.working_sheet.update_value(position, content) def read(self, position = \u0026#34;A1\u0026#34;): content = self.working_sheet.cell(position) print(content) print(content.value) def append(self, values = []): self.working_sheet.append_table(values=values) def insert(self, insert_rows = 1, values = []): # insert_rows = 1, start from 2 self.working_sheet.insert_rows(insert_rows, number=1, values=values) Reference [資料庫筆記] Python 串接 GoogleSheet 新增、讀取、更新和刪除 Python 讀寫 Google Sheets 教學 ","date":"2022-07-01T14:47:16+08:00","image":"https://wongwongnotes.com/images/restored/2021/01/google_sheet_api_5.png","permalink":"https://wongwongnotes.com/posts/python/web-automation/python/python-pygsheets/","tags":["Python","網頁爬蟲"],"title":"【Python 網頁爬蟲 #2】透過 pygsheets 操作 google sheet API，讓我們的資料能同步更新至雲端 google sheet 表格"},{"categories":["182 - Python 網頁爬蟲"],"content":"前言 google sheet 算是在統計中最實用的工具了，\n有時候像是我們進行網路爬蟲，總會有大量的結果需要整理，\n因此，我們會想如果可以把我們爬蟲下來的資料，全部整理進 google sheet，\n再直接給別人看不是很好嗎? 因此這篇文章就是要來教學把資料透過 pygsheets 上傳至雲端。\n文章主要幾大部分：\nGCP (Google Cloud Platform) 專案建立部分 Google sheet API 設定部分 pygsheets 更新雲端 (因為篇幅已經太長，這裡特別專注在程式碼部分，我們下篇文再講) 這篇文章的下一篇文，只專注於 pygsheets 在程式碼上的使用，請見：\nhttps://wongwongnotes.com/posts/python/web-automation/python/python-pygsheets/\nstep 1. GCP (Google Cloud Platform) 專案 的建立 這邊我們要先建立一個 GCP 專案，讓我們可以往後的 google sheet 開發。\nstep 1-1. 我們先進入 Google Cloud Platform，我們要申請我們的 Google Sheet API Google Cloud Platform 傳送門：https://console.cloud.google.com/welcome?project=radiant-micron-354914\u0026amp;hl=zh-TW\nstep 1-2. 點擊左上角的專案名稱，透過此處找到新增專案的入口 step 1-3. 在右上角找到新增專案的地方 step 1-4. 新增專案，填寫相關內容 名稱可以自己取\nstep 1-5. 新增專案完成! 完成後，大概會看到以下畫面\nstep 2. google sheet API 的建立 剛剛我們在 step 1. 完成 GCP (Google Cloud Platform) 專案的建立，\n現在我們要透過這個專案來建立一個 google sheet 的 API，\nstep 2-1. 我們可以在上方搜尋 sheet，快速找到 Google sheets API 我們可以在上方搜尋 sheet，快速找到 Google sheets API\nstep 2-2. 建立 Google sheets API 建立 Google sheets API\nstep 2-3. 點選「建立憑證」，並選擇「服務帳戶」 點選「建立憑證」，並選擇「服務帳戶」\nstep 2-4. 填入相關設定資料 服務帳戶名稱：可以自己設定，為專案名稱 服務帳戶ID: 會透過此 ID 建立一個新的 email 帳戶，「之後 google sheets 共享編輯者要使用」 step 2-5. 點擊服務帳戶的名稱，接下來我們準備要來產生金鑰 點下方框起來的服務帳戶名稱\nstep 2-6. 建立 google sheet API 金鑰的 json file，給程式使用 建立 google sheet API 金鑰的 json file，給程式使用\n這裡建立完成後，會拿到一個「金鑰的 json file」，請務必好好保存!!!!!\n注意：「服務金鑰的 json 檔案」請勿外流!!! 不然你的資料可能有外流的風險\nstep 2-7. 新增金鑰完成! 寫到此，發現篇幅已經太長了，關於透過 Python - pygsheets 更新雲端 google sheet 表格，\n我們另外開一篇文章來說明。\n這篇文章的下一篇 - 程式碼部分 請見下篇\nhttps://wongwongnotes.com/posts/python/web-automation/python/python-pygsheets/\nReference [資料庫筆記] Python 串接 GoogleSheet 新增、讀取、更新和刪除 Python 讀寫 Google Sheets 教學 ","date":"2022-07-01T13:57:27+08:00","image":"https://wongwongnotes.com/images/restored/2021/01/google_sheet_api_5.png","permalink":"https://wongwongnotes.com/posts/python/web-automation/python/google-sheet-api/","tags":["Python","網頁爬蟲"],"title":"【Python 網頁爬蟲 #1】設定 google sheet API 並取得 json 金鑰，讓我們的資料能同步更新至雲端 google sheet 表格 (內含完整圖片說明)"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 最近因為各大學校把 G suite 的容量要開始緊縮，\n變得很多人開始要把資料另外尋覓一個資料儲存的地方\n大部分的人的選項大概都是:\n- 買個大外接硬碟，資料丟裡面 (冷儲存) - 買台 NAS 放家裡，資料丟裡面 (私有雲) - 買 google, apple 或微軟的 雲端硬碟空間，把資料丟裡面 (公有雲) 自己做的 NAS，完成後大概長這樣，可以自己再擴充硬碟，\n公有雲 與 私有雲 的介紹與定位 我們可以非常簡單的了解這件事情，\n你用 google drive，表示你的資料是「放在 google 那邊」，\n基本上他「硬要偷看你的資料」也是有可能的，只是他要不要這樣做而已。\n私有雲就是這樣產生的，特別最常被一般企業使用，\n你可以想像，企業那麼多「機密資料」，google, 微軟 「只要有心，就有機會看到」，這是一件多嚇人的事情!\n但我們還是「有公司內部的共用資料需求」，因此我們就建立一個「只限公司範圍內，可以共用的雲端硬碟」\n外接硬碟 我想看圖也知道，你的資料要被偷的難易度超級高，基本上除非小偷要闖入你家搶走你的硬碟(O\n公有雲 (這裡特別指 google drive, one drive\u0026hellip;) 雖然我這邊舉例的是 google drive, one drive\u0026hellip; 等，相對比較有「公信力」的公司，\n但萬一今天是一個「沒有那麼具有公信力」的公司，\n你所有的機密資料，真的「有可能」且「很容易」的被別人拿走。\n私有雲 (這裡特別指 NAS) 因此私有雲 (NAS) 的產生就非常重要，我們在自己的家中 (公司中)，放一台自己的 NAS，\n只限制在自己家中的網路範圍內 (下圖紅框的部分)，可以拿到這些資料，\n這樣資料就相對安全了!\n但只要「連網」都有可能有隱藏風險，硬要偷，都只是難度問題。\n正題 - 透過 rpi3 自製簡易低成本 NAS 結果我們到現在才在講正題嗎\u0026hellip;\n以下的 youtube 介紹的非常詳細，基本上完全照他的做就可以。\nBUILD A NAS with the RASPBERRY PI 3! [Easy Method]\nRaspberry Pi 3 / 樹莓派 的設定部分 step 1. 系統更新 基本上我們常用都常做這些事情，沒什麼好說明的\nsudo apt-get update -y sudo apt-get upgrade -y step 2. 安裝能將 rpi3 變 NAS 的相關套件 (進行資料串流) 就是安裝我們的重要功能。\nsudo apt-get intsall samba samba-common-bin step 3. 修改設定檔 我們設定一些資料權限相關的內容，這邊可以視自己需求調整。\n(這裡我們使用 vim 作為編輯器)\nsudo vim /etc/samba/smb.conf 在 vim 介面，輸入快速鍵 「shift + g」，可以快速到文件結尾，\n我們新增以下的設定內容\n[MyNAS] path = /media/pi/ writeable = yes create mask = 0777 directory mask = 0777 public = no 示範如下：\nstep 4. 註冊帳號密碼 設定作為登入 NAS 的帳號密碼\nsudo smbpasswd user -pi step 5. 重啟 smbd，依照我們的設定內容啟動 NAS sudo systemctl restart smbd step 6. 完成！現在只要插入新的硬碟，都會自動在 /media/pi/ 底下看到對應資料夾 完成啦~ 可以自己插入硬碟試試看，\n看插入的硬碟有沒有顯示在「/media/pi/」底下。\nwindows 連接 rpi3 NAS 的連接方法 step 1. 打開 windows 檔案總管，在上方找到「新增一個網路位置」 打開 windows 檔案總管，在上方找到「新增一個網路位置」，位置如下：\nstep 2. 連接上我們的 rpi3 NAS，登入剛剛註冊的帳號密碼 我們剛剛設定的網路位置，位於以下位置\naspberrypi\\MyNAS 之後登入帳號密碼，即可成功連線。\nstep 3. 連接成功後，就會在網路位置看到我們的 MyNAS 資料夾，裡面對應的資料，就是我們所插入 rpi3 的實體硬碟。 mac OS 連接 rpi3 NAS 的連接方法 如果上述有設定成功的話，在 finder 裡面，左側找到網路的部分，\n應該就會看到 raspberrypi 的一台新的網路位置。\n點進去後，就會看到我們的 MyNAS 資料夾，裡面對應的資料，就是我們所插入 rpi3 的實體硬碟。\nReference BUILD A NAS with the RASPBERRY PI 3! [Easy Method] 利用Raspberry Pi搭建一個簡易的NAS ","date":"2022-06-30T15:18:41+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/rpi_NAS_2.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/rpi3-nas/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 透過 rpi3 自製簡易低成本家用 NAS (家用雲端硬碟) | 內含 NAS (私有雲) 與 雲端硬碟 (公有雲) 的比較 | rpi3 NAS"},{"categories":["935 - 重症醫學"],"content":"\n前言 前幾天在交班的時候意外的發現許多剛進來的同仁不會判讀Flotrac，判讀血流動力學(Hemodynamic monitoring )是重症加護領域重要的評估數據，因此寫下這篇文章做資訊的整理。\n血流動力學監測工具 中心靜脈壓( Central venous pressure, CVP ) 中心靜脈壓泛指右心房與胸腔內部大靜脈的血壓，臨床意義上用以估計右心房壓力的值，可用來評估重症病患輸液的指標，也可以拿來當作心臟衰竭病人癒後指標。臨床上主要放置導管在右側的 internal jugular vein 和 external jugular vein的位置，正常值為4-12 cmH2O。然而作為輸液量是否足夠的根據其特異性又稍嫌太低，總是有一些病人無法單純透過灌水能解決臨床上心臟輸出不足問題。\n動脈導管 ( Arterial catheter, A line ) 可動態監測病患動脈血壓(Systolic pressure, Diastolic pressure, Mean arterial pressure)，小孩的 MAP 大約是 50mmHg 大人約 70-90mmHg。\n(Arterial Pressure Wave 總共分成4個部分，1號代表Systolic Pressure，2號是代表Dicrotic Pressure與主動脈瓣的關閉，3號是Diastolic Pressure，4號是兩個Pressure Wave 中間的接縫。)\n肺動脈導管 (Pulmonary arterial catheter, PAC) 肺動脈導管又有人稱之為Swan-Ganz 導管，放置後容易因感染導致死亡，被戲稱為死亡槍。經常放置在心因性休克或肺動脈高壓的患者身上，用作評估心輸出量與血管阻力的工具。過去心臟外科病人術後常規會放置肺動脈導管，除了易感染的問題外，隨著日新月異的血液動力學監測技術，這種導管漸漸地比較少在臨床上使用了。透過肺動脈導管可以直接或間接測量的數據如下：\n直接測量數據 中央靜脈壓力( Central venous pressure, CVP )： 正常值為4-12 cmH2O 右心房與右心室壓 ( Right atrium pressure and right ventricle pressure )： 舒張壓為 1-8 mmHg，收縮壓為 15-30 mmHg。 肺動脈壓 ( Pulmonary arterial pressure, Pap ) ： 正常肺動脈收縮壓為 15-30 mmHg，肺動脈舒張壓為 4-12 mmHg。臨床上常定義肺動脈高壓為，平均肺動脈壓在休息時大於25 mmHg或運動時大於30 mmHg 肺微血管嵌塞壓( Pulmonary capillary occlusion pressure, PCOP; pulmonary capillary wedge pressure, PCWP )： 正常值在4-12 mmHg，因測量位置與左心房內接近，可以視作左心房壓力。PCWP增高可以反映左心房壓力增高，可用於鑑別診斷急性肺水腫，此外，可以視作血容量指標，作為輸液的參考，且特異性比CVP高。 心輸出量 ( Cardiac output, CO )： 正常值　4-6 liters/min 間接測量數據 心輸出量指標 ( Cardiac output index, CO index)： 正常值　2.5-4 liters/m2 系統性血管阻力 ( Systemic vascular resistance, SVR)： 900-1440 dyn/s/cm-5 肺部血管阻力 ( Pulmonary vascular resistance, PVR)： 37-250 dyn/s/cm-5 心肺容積監測儀 (Pulse counter cardiac output, PiCCO) 放置中央靜脈導管與PiCCO動脈導管即可測量，屬於新世代的血流動力學監測工具。透過類似Swan-Ganz導管的經肺熱稀釋法及脈搏曲線分析法(微積分計算血流與時間下面積)來測得血流動力數據。測得的數據如下：\n心輸出量指標 ( Cardiac output index, CO index)：\n正常值　2.5-4 liters/m2 心臟舒張末期總血容積指標 ( Global end-diastolic Volume Index, GEDVI)：\n正常值　680-800ml/m2，可視作全心Preload 量化指標，與肺微血管嵌塞壓( PCWP )有相似的臨床意義，可作為病患輸液量是否足夠的參考，且較不受呼吸器吐氣末正壓 (PEEP)影響。 胸內血容積指標 ( Intrathoracic Blood Volume Index, ITBVI )：\n正常值　850-1000ml/m2，數值為GEDV+PBV(肺血管血容積)推算而來，其臨床意義近似GEDVI，屬於Preload量化指標。 血管外肺水容積 ( Extravascular lung water index, ELWI )： 正常值 3.0-7 ml/kg，肺部損傷指標，可用於量化肺水腫的程度，並可用於重症患者死亡率的預後指標。ELWI \u003e10 可以視為肺水腫。 心搏量變異率( Stroke volume variation, SVV )： 正常值12-15%表示每一次心搏輸出的差異程度，臨床上常用作判讀輸液量是否足夠的指標，心搏量易受正壓呼吸器使用而在吸氣期增加，在吐氣期減少，此數值可用做輸液指標，一般來說會以SVV 低於12%會被認為volume 足夠，高於12%被認為volume不夠。 肺血管通透性指數 (Pulmonary Vascular Permeability Index, PVPI )： 正常值1-3，用於判別肺水腫的類型。 PiCCO 臨床決策方式： 相信複雜的血流動力學參數可以讓不熟悉的人看了昏頭轉向，這邊提供臨床常用的決策樹狀圖供讀者參考。 Flotrac 類似於PiCCO屬於新世代的血流動力學監測工具，透過經肺熱稀釋法及脈搏曲線分析法，可測得數據與PiCCO基本一致，因此不重複贅述。\n血流動力監測儀器橫向比較 結語 許多經驗不足的臨床人員遇到心跳很快的病人，總是想要靠灌水來解決問題，然而臨床上的狀況是很複雜的，有些時候病人CVP偏低，然而Preload 已經過高了，透過一些進階的血流動力監測儀器，我們可以知道部分病人不僅僅是volume不足的問題，還可能有contractility不足的情形。此外，研究統計ARDS病患在加護病房盛行率約為10%，而實際臨床上有許多未被診斷出來的黑數，此部分除了仰賴呼吸治療師監測患者動脈血氧分析(ABG)的PF ratio外，判讀是否真的是心因性造常的肺水腫也是值得被評估的事項。\nReference - Jason Widrich; Mrin Shetty. Physiology, Pulmonary Vascular Resistance - Michard et al. AJRCCM. 1999; 159:935-9. ","date":"2022-06-30T03:43:38+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/table-1-2.png","permalink":"https://wongwongnotes.com/posts/os-misc/growth/critical-care/hemodynemic-monitoring/","tags":["重症醫學"],"title":"【重症醫學】重症病患的血流動力學監測 (Hemodynamic monitoring in critically ill patients)"},{"categories":["935 - 重症醫學"],"content":"\n前言 急性呼吸窘迫症候群 (Acute Respiratory Distress Syndrome, 簡稱ARDS)是常見於加護病房的重症疾病，其在加護病房的盛行率高達10%，根據嚴重度的不同，死亡率約為20-40%。因為其死亡率高，且不容易治療，所以各種不同的治療改善處置持續的被研究者們提出，時至1990年代早期ARDS治療有了肺保護策略(lung protect strategy)的概念後，死亡率才開始有顯著的減少。\nARDS的歷史 「A」RDS = Acute？ Adult？ ARDS是在1967年由Ashbaugh 等學者於Lancet首度提出，其描述了12例呼吸窘迫的病人，這些病人急性的發生呼吸急促、低血氧、肺部順應性下降、胸部X光浸潤，且對當時傳統療法反應不佳。因為這些病人的臨床與病理狀態與嬰兒的呼吸窘迫症(Respiratory Distress Syndrome)相似，因此以「Acute Respiratory Distress Syndrome in Adult」為題撰文描述這樣的疾病。四年後，Petty, Ashbaugh等人於CHEST上發表另一篇文章，並且以成人呼吸窘迫症候群(Adult Respiratory Distress Syndrome)來命名這種疾病。所以一開始ARDS的A是指Adult。\n戰爭促使ARDS廣為人知 ARDS在之後的越南戰爭開始廣為人知，這與當時美軍投入大量的醫療資源有關，受傷的軍人能即時的撤離前線，且因為直升機救援的投入，傷患能被運送至遠方的加護醫療單位。從結果看來，就是病患相較過去得以存活到足以發生ARDS的階段。在戰爭期間美國陸軍軍醫發現有些傷患一開始對於急救醫療是有效的，但是後續卻發生呼吸窘迫的症狀，緊接著發生持續性的低血氧，且這樣的狀況使用氧氣面罩是無效的，軍醫們覺得更奇怪的事，明明這些傷患沒有明顯胸部外傷(主要是燒燙傷、顱內出血、四肢創傷)，他們的胸部X光卻顯示肺水腫、左心室衰竭等徵候。雖然這些誘發ARDS危險因子在現在重症醫療場域已經是常識，然而對當時的人們來說確實是百思不得其解的狀況。\nARDS的定義 Murray 肺損傷分數 在Ashbaugh後近30年雖然有許多探討ARDS的文獻，但是後續的文獻針對疾病定沒有統一的定義。其中較為著名的為1988年Murray (他是胸腔暨重症教科書的作者，一代重症醫學大師今年3月死於COVID-19 QQ) 等學者提出肺損傷分數(Lung injury score)，依據CXR, PaO2/FiO2 ratio, PEEP, lung compliance，四個部分來替肺損傷做0-4分的評分，四部分加總後取平均值，如果大於2.5分就算是ARDS。即便有此篇著名的定義，仍舊沒有成為國際上廣為接受的定義。\nAECC 定義 直到1994年由美國ATS與歐洲ERS的醫學專家們招開歐美共識會議(The American-European Consensus Conference, AECC)，正式將此疾病定義為ARDS (Acute Respiratory Distress Syndrome)，不再侷限其於成人身上發生，同時也完整的定義急性肺損傷(Acute lung injury, ALI)與ARDS。\nAECC定義雖然一段時間成為國際統一的準則，但是很快的就受到了批評，其中包括：\n需要依靠CXR判讀：CXR判讀容易會有為判讀差異。 並未對急性的時間做定義 ALI 與ARDS僅為疾病進程上的嚴重度差異，另外命名並無益處 臨床上肺動脈楔壓(pulmonary capillary wedge pressure, PCWP)測量不易，須放置Swan-Ganz才能測量，且ARDS經常合併出現其他狀況導致PCWP\u0026gt;18mmHg 柏林定義 因此2011年歐洲與美國的醫師們又再度聯合重新定義了ARDS，因期間曾於柏林招開共識會議，因此被稱為柏林定義(Berlin definition)，定義如下：\n柏林定義改善了部分AECC定義的缺點外，也提升了ARDS於不同嚴重度死亡率的預測能力，改善的部分如下：\n- 急性定義：明確的定義急性為7天內 - 將原先ALI定義為mild ARDS - CXR雖維持與AECC相同，但是柏林定義新增文字說明「雙側肺陰影 (opacity)必須無法完全以積液、肺塌陷或肺結節可以解釋。」 - 移除不合用的PCWP - 新增常見危險因子：包含pneumonia, non-pulmonary sepsis, aspiration, trauma, lung contusion, pancreatitis, severe burn injury, non-cardiac shock, TRALI, 溺水等 結語 ARDS在過去歷經無統一定義的時期，後續出現AECC定義成為國際共識，幾年後又因為一些限制進而促使專家們修訂柏林定義。ARDS定義隨著時代的更迭而有所改變，距離2012發表的柏林定義已經過了8年的時間，相信柏林定義在許多面向會有所限制，不知何時又會更新下一代的ARDS定義？\nReference - Acute respiratory distress in adults, Lancet. 1967 Aug 12;2(7511):319-23 - An expanded definition of the adult respiratory distress syndrome, Am Rev Respir Dis. 1988 Sep;138(3):720-3 - The American-European Consensus Conference on ARDS, Am J Respir Crit Care Med. 1994 Mar;149(3 Pt 1):818-24. - Acute respiratory distress syndrome: the Berlin Definition, JAMA. 2012 Jun 20;307(23):2526-33 - Ventilation with lower tidal volumes as compared with traditional tidal volumes for acute lung injury and the acute respiratory distress syndrome, N Engl J Med. 2000 May 4;342(18):1301-8 - History of acute respiratory distress syndrome, Lancet Respir Med. 2016 Jul;4(7):547-548 - Fifty Years of Research in ARDS. The Epidemiology of Acute Respiratory Distress Syndrome. A 50th Birthday Review, Am J Respir Crit Care Med. 2017 Apr 1;195(7):860-870 ","date":"2022-06-29T17:31:36+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/3.png","permalink":"https://wongwongnotes.com/posts/os-misc/growth/critical-care/ards-definition-and-history/","tags":["重症醫學"],"title":"【重症醫學】 急性呼吸窘迫症候群(ARDS)的定義與歷史"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 因為我的 Raspberry Pi 3 使用的是 fish shell (我覺得比較方便一點)，\n但安裝 anaconda 時踩了不少雷，這裡記錄一下解決過程\n安裝 miniconda 推薦閱讀：Raspberry Pi安裝OpenCV與Jupyter(透過conda方法)\n上面的分享把安裝過程寫得非常清楚了，但因為我有些個人的問題 (誰叫我要使用 fish shell XDD)，因此重新整理在下方給自己。\nstep 1 - 下載並安裝 這邊沒什麼問題\nwget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-armv7l.sh sudo /bin/bash Miniconda3-latest-Linux-armv7l.sh 然後記得，安裝時\nlicense - yes 預設路徑 - 改成「/home/pi/miniconda3」 幫你自動加入環境變數 PATH - 「no」 我一開始有試著使用預設路徑安裝看看，但發現會碰到權限問題，\n不是不能處理，但處理起來太麻煩了，\n確實使用上方的路徑，處理起來方便很多。\n而幫你自動加入環境變數 PATH 也因為我們改了上述路徑，所以記得填「no」\nstep 2 - 安裝好了，來處理環境變數 這邊我碰到最大的麻煩就是因為我使用 fish shell，\n自己搞自己XDD，然後我又找不到設定檔在哪，\n最後暫時的解法，是去改以下檔案\nvim ~/.config/fish/conf.d/omf.fish 在中間找個適當的位置新增環境變數的修改： export PATH=/home/pi/miniconda3/bin:$PATH 然後就終於搞定了QQ ，我快哭了。\nReference Raspberry Pi安裝OpenCV與Jupyter(透過conda方法) Is there a way to install fish shell on raspberry pi? fish shell requires adding ~/miniconda3/bin/ to $PATH in conda 4.4.0rc1 (unlike bash-shell-derivatives) ","date":"2022-06-26T00:25:15+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/mini-conda-init-fish/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 設定 miniconda 在 fish shell 內順利運作筆記 (mini conda init fish)"},{"categories":["830 - Raspberry Pi 3"],"content":"問題來源 這是我在 Raspberry Pi 3 / 樹莓派 上安裝 miniconda 時，\n使用 fish shell 發現無法順利啟動的解決方法筆記。\n錯誤如下： fish: Unknown command conda 問題解決 最主要是 miniconda 的環境變數，在 Raspberry Pi 3 沒那麼好用，\n自己搞自己XDD，然後我又找不到設定檔在哪，\n最後暫時的解法，是去改以下檔案\nvim ~/.config/fish/conf.d/omf.fish 在中間找個適當的位置新增環境變數的修改： export PATH=/home/pi/miniconda3/bin:$PATH 然後就終於搞定了QQ ，我快哭了。\nReference Raspberry Pi安裝OpenCV與Jupyter(透過conda方法) Is there a way to install fish shell on raspberry pi? fish shell requires adding ~/miniconda3/bin/ to $PATH in conda 4.4.0rc1 (unlike bash-shell-derivatives) ","date":"2022-06-25T14:49:02+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/fish-unknown-command-conda/","tags":["Pi","Raspberry"],"title":"【Rpi3】問題解決：Raspberry Pi 3 / 樹莓派 fish: Unknown command conda"},{"categories":["399 – Bash 自用腳本"],"content":"前言 【嗡嗡精選】這系列是留給我自己用的，方便快速查詢的筆記。\nscreen - 無縫跨裝置回覆 terminal log 記法：ctrl + A：可以當作萬用功能 function key\n功能 指令/快速鍵 備註(記法) 啟動新的 screen screen 啟動新的 screen，並定義 session 的 screen -S \"-S\" : session (不過原意是 sockname) 暫時離開 (detach) 一個 screen ctrl + A, D 記法：detach 顯示目前所有正在執行中的 screen screen -ls 重新進入指定的 screen session screen -r screen_id：可透過 “screen -ls” 取得，不用完全輸入，可以只輸入至 id 就找得到了 重新進入指定的 screen session screen -r screen_id：可透過 “screen -ls” 取得，不用完全輸入，可以只輸入至 id 就找得到了 重新進入最近離開的 screen session screen -R 砍掉單一 screen (進入某個 screen 後)ctrl + A, K 記法：kill 砍掉所有 screen pkill screen Reference Setting a name for a screen session ","date":"2022-06-22T17:42:56+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/bash/wongwong-ubuntu-note/","tags":["Bash","Docker","Linux","Ubuntu","自用腳本"],"title":"【Linux】ubuntu 個人自用指令小抄"},{"categories":["Two pointers (同向雙指針 →→) / Sliding window","718 - Queue / Stack"],"content":"題目出處 239. Sliding Window Maximum\n難度 hard\n個人範例程式碼 class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -\u0026gt; List[int]: if len(nums) \u0026lt; k: return [] queue_idx = [] ans = [] # 1. if i-k == queue_idx[0] (3-3 = 0) oversize, pop oldest (0) # 2. if +1 nums[queue_idx[-1]] \u0026lt; num, remove smaller # 3. append new element # 4. nums[queue_idx[0]] will be max for i, num in enumerate(nums): # 1. if i-k == queue_idx[0] (3-3 = 0) oversize, pop oldest (0) if queue_idx and i - k == queue_idx[0]: queue_idx.pop(0) # 2. if +1 nums[queue_idx[-1]] \u0026lt; num, remove smaller while queue_idx and nums[queue_idx[-1]] \u0026lt; num: queue_idx.pop(-1) # 3. append new element queue_idx.append(i) # 4. nums[queue_idx[0]] will be max if i+1 \u0026gt;= k: # 2+1 \u0026gt;= 3 ans.append(nums[queue_idx[0]]) return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 暴力解的版本，會在每次的 find max 花太多時間，\n而我們會發現，sliding window 的特性，我們應該可以達成最小化的重複性更改。\n基本上我們可以拆成四大步驟：\n- if i-k queue_idx[0] (3-3 = 0) oversize, pop oldest (0) - if +1 nums[queue_idx[-1]] \u003c num, remove smaller - append new element - nums[queue_idx[0]] will be max input handling 如果 len(nums) \u0026lt; k，則不可能組出 window。\nBoundary conditions 用 for 控制範圍\n個人範例程式碼 - 暴力解，會 TLE class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -\u0026gt; List[int]: if len(nums) \u0026lt; k: return [] ans = [] slow, fast = 0, k counter = defaultdict(int) # init counter for i in range(k): counter[nums[i]] += 1 ans.append(self.get_counter_max(counter)) # sliding window while fast \u0026lt; len(nums): counter[nums[slow]] -= 1 counter[nums[fast]] += 1 ans.append(self.get_counter_max(counter)) slow += 1 fast += 1 return ans # O(n), if sorting O(nlogn) def get_counter_max(self, counter): get_max = float(\u0026#34;-inf\u0026#34;) for key in counter.keys(): if counter[key] \u0026gt; 0: get_max = max(get_max, key) return get_max 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 暴力解的版本，最主要花時間的地方在於找最大，\n如果使用 sorting 會是 O(nlogn)，單一次遍歷找最大會是 O(n)，\n結合最外層的 sliding window 移動則會是 O(n^2)，\n也還是會 TLE，需要另外想更快的辦法了。\ninput handling 如果 len(nums) \u0026lt; k，則不可能組出 window。\nBoundary conditions 用 for 控制範圍\nReference 滑动窗口的最大值 · Sliding Window Maximum ","date":"2022-06-21T18:51:23+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-sliding-window/leetcode-python-239/","tags":["Queue","Sliding","Stack","window"],"title":"【Leetcode】python - [239] Sliding Window Maximum 個人解法筆記"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 543. Diameter of Binary Tree\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def diameterOfBinaryTree(self, root: Optional[TreeNode]) -\u0026gt; int: return self.helper(root)[0] def helper(self, root): # end of recursion if not root: return 0, 0 # define and split left_longest, left_diameter = self.helper(root.left) right_longest, right_diameter = self.helper(root.right) longest = max(left_longest, right_longest, left_diameter + right_diameter) diameter = max(left_diameter, right_diameter) + 1 return longest, diameter 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 我們先來理解題意，這題目主要是在詢問在 Tree 中「最長可組成的邊 (diameter)」，\n主要有兩種情況：\n最長邊包含 root 最長邊不包含 root (最長存在子樹中) 因此我們需要同時記錄「子樹中，目前最長」與「目前最長的邊」。\n目前最長的邊 + 1 = 向上一層的最長邊 input handling 在 dfs 內處理\nBoundary conditions 在 dfs 內控制範圍，如果搜尋至 root = None，return 0, 0\nReference 二叉树的直径 · Diameter of Binary Tree ","date":"2022-06-21T01:43:54+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-543/","tags":["DFS"],"title":"【Leetcode】python - [543] Diameter of Binary Tree 個人解法筆記"},{"categories":["DFS (全部方案)","DP (記憶化搜索 memoization)"],"content":"題目出處 494. Target Sum\n難度 medium\n個人範例程式碼 class Solution: def findTargetSumWays(self, nums: List[int], target: int) -\u0026gt; int: memo = {} return self.dfs(nums, 0, target, memo) def dfs(self, nums, start_idx, target, memo): # end of recursion if start_idx \u0026gt;= len(nums): if target == 0: return 1 else: return 0 # memoization if (start_idx, target) in memo: return memo[(start_idx, target)] # define and split ans = self.dfs(nums, start_idx+1, target-nums[start_idx], memo) + self.dfs(nums, start_idx+1, target+nums[start_idx], memo) # memoization memo[(start_idx, target)] = ans return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 仔細看發現是很單純的「2 指數」的題目，\n每一次我們都只要多延伸出「+」或「-」的新情況，\nmemoization 純 DFS 會 TLE，需要搭配 DP 的記憶化搜索，\n我們紀錄的是，(目前 target 的值，目前的 idx)，\n當進行一樣的 target 與同 idx 搜尋時，我們可以直接查表。\ninput handling 在 dfs 內處理\nBoundary conditions 在 dfs 內控制範圍，我們在「搜尋 start_idx \u0026gt;= nums 總長」的情況下停止遞迴。\nReference 目标和 · Target Sum ","date":"2022-06-20T18:40:47+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-494/","tags":["DFS","DP"],"title":"【Leetcode】python - [494] Target Sum 個人解法筆記"},{"categories":["Two pointers (同向雙指針 →→) / slow, fast (快慢雙指針)"],"content":"題目出處 134. Gas Station\n難度 medium\n個人範例程式碼 class Solution: def canCompleteCircuit(self, gas: List[int], cost: List[int]) -\u0026gt; int: if sum(gas) \u0026lt; sum(cost): return -1 remain_gas = 0 start_station = 0 for i in range(len(gas)): remain_gas += gas[i] remain_gas -= cost[i] if remain_gas \u0026lt; 0: # change start station remain_gas = 0 start_station = i+1 return start_station 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 我們透過 0 不斷的 「+ gas[i]」，移動至下一個 station 「- cost[i]」，\n當發現 remain_gas 小於 0 時，表示這個加油站適合當最後一站。\n(用最後剩的 gas 回到此處。)\n更改 start station 位置時， start_station 的位置改為「i+1」\ninput handling 如果 sum(gas) \u0026lt; sum(cost)，不可能走完全程。\n反之，一定走得完全程，至少會有一個答案。 (題目也限制了，只會有一個答案)\nBoundary conditions 用 for 控制來範圍\nReference 加油站 · Gas Station ","date":"2022-06-20T18:00:21+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-slow-fast/leetcode-python-134/","tags":[],"title":"【Leetcode】python - [134] Gas Station 個人解法筆記"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 110. Balanced Binary Tree\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def isBalanced(self, root: Optional[TreeNode]) -\u0026gt; bool: return self.isBalanced_and_get_height(root)[0] def isBalanced_and_get_height(self, root): # end of recursion if not root: return True, 0 # split left_is_balanced, left_height = self.isBalanced_and_get_height(root.left) right_is_balanced, right_height = self.isBalanced_and_get_height(root.right) # define if not (left_is_balanced and right_is_balanced): return False, -1 if abs(left_height - right_height) \u0026gt; 1: return False, -1 return True, max(left_height, right_height)+1 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 正常直接的想法是，我們的「所有子樹都必須要是 balanced」，\n因此直覺會需要 recursion。\n但因為要決定這個結果，我們還需要知道「當前子樹的最大高度」，\n設計時，我們可以同時回傳「子樹的答案」+「當前子樹的最大高度」\ninput handling 一同在 dfs 內處理\nBoundary conditions 如果沒有 root，結束遞迴\nReference 平衡二叉树 · Balanced Binary Tree ","date":"2022-06-20T17:24:02+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-110/","tags":["DFS"],"title":"【Leetcode】python - [110] Balanced Binary Tree 個人解法筆記"},{"categories":["716 - Linked List","717 - Hash Table"],"content":"題目出處 146. LRU Cache\n難度 Medium\n個人範例程式碼 - 2022/6/19 二刷 class Node: def __init__(self,key, val): self.key = key self.val = val self.next = None self.prev = None class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.hash = {} self.head = Node(-1, -1) self.tail = Node(-1, -1) self.head.next = self.tail self.tail.prev = self.head def get(self, key: int) -\u0026gt; int: if key not in self.hash: return -1 node = self.hash[key] self.remove_node(node) self.move_to_tail(node) return node.val def put(self, key: int, value: int) -\u0026gt; None: if self.get(key) != -1: # old key self.hash[key].val = value return # new key if len(self.hash) \u0026gt;= self.capacity: self.pop_front() node = Node(key, value) self.move_to_tail(node) self.hash[key] = node def remove_node(self, node): node.prev.next = node.next # remove front-\u0026gt; node node.next.prev = node.prev # remove node -\u0026gt; next def move_to_tail(self, node): node.prev = self.tail.prev # tail.prev \u0026lt;- node node.next = self.tail # node -\u0026gt; tail node.prev.next = node # tail.prev -\u0026gt; node self.tail.prev = node # node \u0026lt;- tail def pop_front(self): del self.hash[self.head.next.key] self.head.next = self.head.next.next self.head.next.prev = self.head # Your LRUCache object will be instantiated and called as such # obj = LRUCache(capacity) # param_1 = obj.get(key) # obj.put(key,value) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 在標準的解答中，大概念是我們要設計一個 Linked List 配合 hashtable 進行 node 的快速查找，\n我們可以把功能進行拆分，大致上我們會需要：\nget：(取得新元素，題目需要) put：(加入新元素，題目需要) remove_node：移除一個元素 node move_to_tail：將一個元素新增至 tail pop_front：處理當 capacity 滿的時候，將最舊的元素移除 而為了快速且方便的能在 Linked List 中移動，\n我們使用雙向的 Linked List，這樣在「查找前面一個元素」時，會非常方便\ninput handling 此題為程式設計類題目，需要能夠保持不斷的運作\n(並非單純輸入一個 testcase 得到一個結果的類型。)\nBoundary conditions 此題為程式設計類題目，需要能夠保持不斷的運作\n(並非單純輸入一個 testcase 得到一個結果的類型。)\n個人範例程式碼 - 2022/5/18 一刷 有解出來，但不是題目要的 (queue + hashtable) class LRUCache: # recently def __init__(self, capacity: int): self.capacity = capacity self.hashtable = {} self.queue = deque() def get(self, key: int) -\u0026gt; int: if key not in self.queue: return -1 value = self.hashtable[key] self.queue.remove(key) self.queue.append(key) # print(self.queue) return value def put(self, key: int, value: int) -\u0026gt; None: if key in self.queue: self.queue.remove(key) self.queue.append(key) self.hashtable[key] = value if len(self.queue) \u0026gt; self.capacity: key = self.queue.popleft() # del self.hashtable[key] # print(self.queue) # Your LRUCache object will be instantiated and called as such # obj = LRUCache(capacity) # param_1 = obj.get(key) # obj.put(key,value) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 LRU cache 的設計方式，主要是由 linked list + hashtable\n概念就是透過 linked list 保持順序性，而透過 hashtable 去查詢。\ninput handling 此題為程式設計類題目，需要能夠保持不斷的運作\n(並非單純輸入一個 testcase 得到一個結果的類型。)\nBoundary conditions 此題為程式設計類題目，需要能夠保持不斷的運作\n(並非單純輸入一個 testcase 得到一個結果的類型。)\nReference LRU缓存策略 · LRU Cache ","date":"2022-06-19T01:49:10+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-146/","tags":[],"title":"【Leetcode】python - [146] LRU Cache 個人解法筆記 #重要題型"},{"categories":["923 - Windows"],"content":"問題來源 當我們購買新硬碟時，會想要分割新的磁碟區，\n但我們該如何分割完整的「1TB 磁碟區」，讓顯示看起來比較漂亮，如下：\n△ 注意上方右側寫的 1.00 TB\n但往往通常我們用數學計算的方式，會算出「1048576」，\n如果使用這個數字，我們最後會看到「0.99T」如下：\n△ 注意上方右側寫的 0.99 TB\n這裡留下問題的解決筆記。\n問題解決 這基本上是計算浮點數精度造成的誤差問題，\n我們在新增磁碟區時，把數字「多 + 1」即可。\n也就是說，我們在新增磁碟區時，輸入「1048577」MB\n留下一些我自己常用的大小 100 GB = 102401 MB 200 GB = 204801 MB 500 GB = 512001 MB 1 TB = 1048577 MB 2 TB = 2097153 MB Reference Windows 10 新增硬碟 及 分割區容量整數規劃 ","date":"2022-06-17T20:26:42+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/windows_new_drive_3.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/windows/windows-1tb-space/","tags":[],"title":"【Windows】問題解決：在 windows 新增 1TB 磁碟區，顯示 0.99 TB 的情況 解決筆記"},{"categories":["DP (座標型 - 一維)"],"content":"題目出處 122. Best Time to Buy and Sell Stock II\n難度 medium\n個人範例程式碼 class Solution: def maxProfit(self, prices: List[int]) -\u0026gt; int: max_profit = 0 for i, price in enumerate(prices): if i \u0026gt; 0: one_day_profit = prices[i] - prices[i-1] max_profit += one_day_profit if one_day_profit \u0026gt;= 0 else 0 return max_profit 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 可以一天內即時的進行買賣，我們最好的收益就是「從頭到尾都沒有損失的最佳情況」，\n因此我們只要計算\n當「今日 - 前日 受益 \u0026gt; 0」，我們就加上收益， 當「收益 \u0026lt;= 0」，我們就 +0 input handling x\nBoundary conditions 用 for 來控制搜尋範圍\nReference 买卖股票的最佳时机 II · Best Time to Buy and Sell Stock II ","date":"2022-06-17T18:28:56+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/dynamic-programming/dp/leetcode-python-122/","tags":["DP"],"title":"【Leetcode】python - [122] Best Time to Buy and Sell Stock II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 137. Single Number II\n難度 medium\n個人範例程式碼 - （非題目要求空間）解 class Solution: def singleNumber(self, nums: List[int]) -\u0026gt; int: counter = Counter(nums) for key, value in counter.items(): if value == 1: return key 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 先來簡介一下題目的要求，\n題目要求這題，希望能夠「在線性時間 O(n) 完成 」且「使用常數空間 O(1)」，\n使用常數空間的方法，我們直覺會想到用 sort() 線性時間的方法，我們直覺會想到用 hashtable() 但如果要同時滿足兩者，我們需要使用特別的方法，\n在這題會需要使用到 bit 的運算，但由於面試準備中這比較少見，\n因此這邊只點出思路，不是示範對應做法。\n而上方我們示範的是利用 hash 做出 counter 的做法，\n統計並找出 counter 中數量為 1 的結果。\ninput handling x\nBoundary conditions x\nReference 落单的数 II · Single Number II ","date":"2022-06-17T18:18:33+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-137/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [137] Single Number II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 621. Task Scheduler\n難度 medium\n個人範例程式碼 class Solution: def leastInterval(self, tasks: List[str], n: int) -\u0026gt; int: counter = Counter(tasks) collect_counts = sorted(counter.values(), reverse = True) # same_max_length + (max_count-1) * (1+n) same_max_length = collect_counts.count(collect_counts[0]) max_length = same_max_length + (collect_counts[0] - 1) * (n + 1) return max(max_length, len(tasks)) # min length 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題的答案可以直接用數學公式導出來，個人認為對面試準備的幫助比較不大。\n分析答案的分佈，我們可以知道「最大數量對於結果有一定程度的影響」，\n而繼續往下推，我們可以知道最小長度等於 = 「同樣的最大數量」+ 「(n+1) * (最大數量 -1)」\n不懂在說什麼的話，就看下圖吧：\ninput handling x\nBoundary conditions 直接數學運算出來\nReference 任务计划 · Task Scheduler ","date":"2022-06-17T17:19:38+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0378.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-621/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [621] Task Scheduler 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 846. Hand of Straights\n難度 medium\n個人範例程式碼 class Solution: def isNStraightHand(self, hand: List[int], groupSize: int) -\u0026gt; bool: if not len(hand) % groupSize == 0: return False counter = Counter(hand) # print(counter.items()) for key in sorted(counter.keys()): if counter[key] == 0: continue if counter[key] \u0026gt; 0: num_of_cards = counter[key] for delta_i in range(groupSize): # print(key + delta_i) counter[key + delta_i] -= num_of_cards if counter[key + delta_i] \u0026lt; 0: return False return True 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 先處理不整除的狀況，直接 return False\n如果是整除的情況，我們先進行排序，\n從小的卡片開始，往後「扣除最小卡片的數量」，\n搜尋卡片的過程中，我們順便判斷剩餘的卡片數量是否大於 0，\n等於 0 表示沒有剩餘，我們採取 continue 的動作。\ninput handling 先處理不整除的狀況，直接 return False\nBoundary conditions 用 for 控制搜尋範圍\nReference 一手顺子 · Hand of Straights ","date":"2022-06-17T16:21:35+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-846/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [846] Hand of Straights 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 867. Transpose Matrix\n難度 easy\n個人範例程式碼 class Solution: def transpose(self, matrix: List[List[int]]) -\u0026gt; List[List[int]]: m, n = len(matrix), len(matrix[0]) ans = [[0]*m for _ in range(n)] for i in range(m): for j in range(n): ans[j][i] = matrix[i][j] return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 單純做矩陣轉置，沒什麼特別的\ninput handling x\nBoundary conditions 用 for 控制範圍\n補充 - 系統思維加速 (面試題) 以系統思維來講，我們還可以加速：\n可以把 i, j 視為獨立任務，進行 multiprocessing 運算\n可以想像成把 n^2 的任務全部分出去算。\nReference ","date":"2022-06-16T19:12:22+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-867/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [867] Transpose Matrix 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 22. Generate Parentheses\n難度 medium\n個人範例程式碼 class Solution: def generateParenthesis(self, n: int) -\u0026gt; List[str]: ans = [] self.dfs(n, n, \u0026#34;\u0026#34;, ans) return ans def dfs(self, left, right, current_str, ans): # end of recursion if left == 0: for _ in range(right): current_str += \u0026#34;)\u0026#34; ans.append(current_str) return # define and split if left \u0026gt;= 0: self.dfs(left-1, right, current_str + \u0026#34;(\u0026#34;, ans) if right \u0026gt; left: # (3, 2), (3, 1) self.dfs(left, right-1, current_str + \u0026#34;)\u0026#34;, ans) return 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 用 dfs 把所有狀況窮舉出來，我們以 left, right 代替左右括弧剩餘的數量，\n可新增 left 的條件：left \u0026gt; 0\n可新增 right 的條件：right \u0026gt; left # (3, 2), (3, 1)\n終止條件：left = 0，放上所有剩餘的 right\ninput handling 一同在 dfs 內處理，基本上是處理 n = 0 的情況\nBoundary conditions 用 dfs 控制範圍，終止條件為當 left = 0\nReference ","date":"2022-06-16T14:38:15+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-22/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [22] Generate Parentheses 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 13. Roman to Integer\n難度 easy\n個人範例程式碼 - hashamp 與「照 Roman numerals 運算邏輯」推進 roman_table = { \u0026#34;I\u0026#34;:1, \u0026#34;V\u0026#34;:5, \u0026#34;X\u0026#34;:10, \u0026#34;L\u0026#34;:50, \u0026#34;C\u0026#34;:100, \u0026#34;D\u0026#34;:500, \u0026#34;M\u0026#34;:1000 } class Solution: def romanToInt(self, s: str) -\u0026gt; int: # \u0026#34;IV\u0026#34; 01 ans = 0 for i in range(len(s)-1, -1, -1): if i \u0026lt; len(s)-1 and roman_table[s[i]] \u0026lt; roman_table[s[i+1]]: # new i is smaller ans -= roman_table[s[i]] else: ans += roman_table[s[i]] return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 照 Roman numerals 的推算邏輯，我們從最小的位置開始往大的位置推進，\n例外情況為：當「小位置的字母 \u0026gt; 大位置的字母」，我們改採減運算，\n例如：\n\u0026ldquo;IV\u0026rdquo;:4, I \u0026lt; V, 5 - 1 \u0026ldquo;XL\u0026rdquo;:40, X \u0026lt; L, 50 - 10 而正常情況為：\n\u0026ldquo;VI\u0026rdquo;: 5 + 1 \u0026ldquo;LX\u0026rdquo;: 50 + 10 input handling x\nBoundary conditions 用 for 逆向控制範圍\n個人範例程式碼 - 雙 hashamp roman_table = { \u0026#34;I\u0026#34;:1, \u0026#34;V\u0026#34;:5, \u0026#34;X\u0026#34;:10, \u0026#34;L\u0026#34;:50, \u0026#34;C\u0026#34;:100, \u0026#34;D\u0026#34;:500, \u0026#34;M\u0026#34;:1000 } combination = { \u0026#34;IV\u0026#34;:4, \u0026#34;IX\u0026#34;:9, \u0026#34;XL\u0026#34;:40, \u0026#34;XC\u0026#34;:90, \u0026#34;CD\u0026#34;:400, \u0026#34;CM\u0026#34;:900 } class Solution: def romanToInt(self, s: str) -\u0026gt; int: ans = 0 i = 0 while i \u0026lt; len(s): if i+1 \u0026lt; len(s) and s[i:i+2] in combination: ans += combination[s[i:i+2]] i += 2 else: ans += roman_table[s[i]] i += 1 return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 先搜尋兩個字，如果兩個字的組合存在，就拿到結果，\n如果找不到兩個字，就去一個字的字典找。\ninput handling x\nBoundary conditions 用 while 控制範圍\nReference 罗马数字转整数 · Roman to Integer ","date":"2022-06-12T02:12:00+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-13/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [13] Roman to Integer 個人解法筆記"},{"categories":["Two pointers (背向雙指針 ←→)"],"content":"題目出處 9. Palindrome Number\n難度 easy\n個人範例程式碼 class Solution: def isPalindrome(self, x: int) -\u0026gt; bool: if x \u0026lt; 0: return False if x == 0: return True return self.is_palindrome(str(x)) def is_palindrome(self, s): start = 0 end = len(s) - 1 while(start \u0026lt; end): if s[start] == s[end]: start += 1 end -= 1 else: return False return True 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 先設計好一個判斷 palindrome 的 function，然後再處理特殊狀況\ninput handling 如果 = 0，回傳 True 如果 \u0026lt; 0，回傳 False Boundary conditions 用 start, end 來判斷 palindrome\nReference ","date":"2022-06-10T03:34:11+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers/leetcode-python-9/","tags":[],"title":"【Leetcode】python - [9] Palindrome Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 692. Top K Frequent Words\n難度 medium\n個人範例程式碼 - 單 hashtable + 自定義排序 key function class Solution: def topKFrequent(self, words: List[str], k: int) -\u0026gt; List[str]: word_to_counter = defaultdict(int) counter_to_word = defaultdict(list) # O(N) for word in words: word_to_counter[word] += 1 word_list = [(word, cnt) for word, cnt in word_to_counter.items()] word_list.sort(key = self.key_function) return [word_list[i][0] for i in range(k)] def key_function(self, word_tuple): # first compare: cnt (biggest need first, so we use -cnt: 4, 5 -\u0026gt; -5, -4) # second compare: word alphabetically first (lexicographical) word, cnt = word_tuple return (-cnt, word) return 那行使用的 list comprehension，同義於下方程式碼 return 那行優化後，我才寫成上面一行的形式，\n原本的內容長下面那樣，\n基本上面試時，建議以自己較能掌握的穩定寫法為主。\nans = [] for i in range(k): ans.append(word_list[i][0]) return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 另外一個方法是我們一樣用 hashtable 先把所有數量都數出來，\n可以想像做我們把所有的 word 變成 (word, count) 的形式，\n再來我們要使用自定義排序，排成我們要的順序。\n自定義排序 key function 一般來說，sort 都是幫我們單純直接照「數字大小排序」或是「照字母大小排序」，\n但在這題目當中，我們需要同時「先考慮數字大小，再考慮字母順序」，\n因此這樣的情況我們需要去特別定義排序的規則。\n我們可以自己寫一個 key_fuction 給 key 去使用，\n回傳一個 tuple 給 key 參考，\n依照 tuple 的順序，就是我們比較的順序。\n依照題目要的，數量越大的排序要越前面，因此我們排序結果應該要是\n「5,4,3,2,1」(反序)，再來「照字母順序 a,b,c 排」(升序)\n相對簡單的處理方式是，我們就直接讓數字前面加一個「負號」，\n這樣「-5,-4,-3,-2,-1」就是升序的排列了!\n因此我們回傳 (-cnt, word) 作為我們排序的依據。\nword_list.sort(key = self.key_function) def key_function(self, word_tuple): # first compare: cnt (biggest need first, so we use -cnt: 4, 5 -\u0026gt; -5, -4) # second compare: word alphabetically first (lexicographical) word, cnt = word_tuple return (-cnt, word) input handling x\nBoundary conditions 用 for 來控制範圍\n個人範例程式碼 - 雙 hashtable 紀錄 word_to_counter, counter_to_word class Solution: def topKFrequent(self, words: List[str], k: int) -\u0026gt; List[str]: word_to_counter = defaultdict(int) counter_to_word = defaultdict(list) # O(N) for word in words: word_to_counter[word] += 1 current_word_count = word_to_counter[word] if current_word_count \u0026gt; 1: # existed word counter_to_word[current_word_count-1].remove(word) counter_to_word[current_word_count].append(word) # O(nlogN) counter_keys = sorted(counter_to_word.keys(), reverse=True) ans = [] cnt = 0 for key in counter_keys: if cnt \u0026gt;= k: break ans += sorted(counter_to_word[key]) cnt += len(counter_to_word[key]) return ans[:k] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 第一次看到這題目，想說都要計數了，然後還要排序，直覺就想說用兩個 hashtable，\n分別存 word_to_counter, counter_to_word，\n當存到 k 個的時候，就輸出結果。\n排序算是純以\u0026quot;數字\u0026quot;為最優先儲存，再處理字母順序。\n但我們要注意，這題目有特別說明，取前 k 的時候，「有可能同數量，這時候我們就照字母順序取」\ninput handling x\nBoundary conditions 用 for 來控制範圍\nReference 最高频的K个单词 · Top K Frequent Words ","date":"2022-06-10T02:00:44+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-692/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [692] Top K Frequent Words 個人解法筆記 | 內含自定義排序 key function 的使用方法"},{"categories":["DFS (組合 combination)"],"content":"題目出處 216. Combination Sum III\n難度 medium\n個人範例程式碼 class Solution: def combinationSum3(self, k: int, n: int) -\u0026gt; List[List[int]]: if not k or not n: return [] if k \u0026gt; 9: # only 1-9 can use return [] ans = [] self.dfs(1, n, k, [], ans) return ans def dfs(self, start_num, target, k, combinations, ans): # end of recursion if k == 0: if target == 0: ans.append(combinations[:]) # deepcopy return else: return if k \u0026lt; 0: return # define and split for i in range(start_num, 10): # 1 - 9 combinations.append(i) self.dfs(i+1, target-i, k-1, combinations, ans) combinations.pop() # backtracking 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 本題是 Combinations 系列的第 4 題，其他的題目可以參考：\n第 1 題：不允許重複，給定數字範圍的全部組合，目標是指定組合內固定的數量。\n第 2 題：允許重複，順序不同視為相同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是一個結果」\n第 3 題：允許有限重複(題目指定上限數量)，求全部組合。\n第 4 題：不允許重複，給定數字範圍的全部組合，目標是求指定的和。\n第 5 題：允許重複，但順序不同視為不同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是兩個結果」。(這題已經可以當作排列的題目了。)\n組合類的問題，使用 dfs 搜尋出全部的組合\n我們能使用的數字只有 1-9，用 range 取範圍時要注意「range(1,10)」，才會取到 9\ninput handling 如果沒有 k 或 n，直接 return []\nBoundary conditions 用 dfs 來控制搜尋範圍，直到 「k = 0 時，進行結果判斷」 或 「k \u0026lt; 0 時，直接 return」\nReference ","date":"2022-06-08T07:52:42+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-216/","tags":["DFS"],"title":"【Leetcode】python - [216] Combination Sum III 個人解法筆記"},{"categories":["DFS (組合 combination)"],"content":"題目出處 77. Combinations\n難度 medium\n個人範例程式碼 class Solution: def combine(self, n: int, k: int) -\u0026gt; List[List[int]]: if not n or not k: return [[]] elements = [i for i in range(1, n+1)] ans = [] self.dfs(elements, k, [], ans) return ans def dfs(self, elements, k, combinations, ans): # end of recursion if k \u0026lt;= 0: ans.append(combinations[:]) # deepcopy return if not elements: return # define and split for i, element in enumerate(elements): combinations.append(element) self.dfs(elements[i+1:], k-1, combinations, ans) combinations.pop() # backtracking 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 本題是 Combinations 系列的第 1 題，其他的題目可以參考：\n第 1 題：不允許重複，給定數字範圍的全部組合，目標是指定組合內固定的數量。\n第 2 題：允許重複，順序不同視為相同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是一個結果」\n第 3 題：允許有限重複(題目指定上限數量)，求全部組合。\n第 4 題：不允許重複，給定數字範圍的全部組合，目標是求指定的和。\n第 5 題：允許重複，但順序不同視為不同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是兩個結果」。(這題已經可以當作排列的題目了。)\n組合類的問題，使用 dfs 搜尋出全部的組合，\n記得當 k = 4 時，實際上我們可用的數字是 「1,2,3,4」而非 「0,1,2,3」。\ninput handling 如果沒有 k 或 n，直接 return [[]]\nBoundary conditions 用 dfs 來控制搜尋範圍，直到 「k \u0026lt;= 0」 或 「找不到新的元素」，return 結果\nReference ","date":"2022-06-08T07:36:58+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-77/","tags":["DFS"],"title":"【Leetcode】python - [77] Combinations 個人解法筆記 #重要題型"},{"categories":["BFS (連通圖搜索)"],"content":"題目出處 431 · Connected Component in Undirected Graph\n難度 medium\n個人範例程式碼 - 2022/6/8 三刷 from typing import ( List, ) from lintcode import ( UndirectedGraphNode, ) \u0026#34;\u0026#34;\u0026#34; class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param nodes: a array of Undirected graph node @return: a connected set of a Undirected graph \u0026#34;\u0026#34;\u0026#34; def connectedSet(self, nodes: List[UndirectedGraphNode]) -\u0026gt; List[List[int]]: # write your code here if not nodes: return nodes visited = set() ans = [] for node in nodes: if node not in visited: ans.append(self.get_connected_parts(nodes, visited, node)) return ans def get_connected_parts(self, nodes, visited, start_node): queue = [start_node] ans = [] visited.add(start_node) while queue: node = queue.pop(0) ans.append(node.label) for neighbor in node.neighbors: if neighbor not in visited: queue.append(neighbor) visited.add(neighbor) return sorted(ans) 算法說明 BFS 搜尋圖，並把同一組的歸類再一起。\ninput handling x\nBoundary conditions 用 for 配合 visited 來尋找 start_node，\nbfs 內部用 queue 來掌控搜尋範圍。\n個人範例程式碼 - 2022/6/7 二刷 from typing import ( List, ) from lintcode import ( UndirectedGraphNode, ) \u0026#34;\u0026#34;\u0026#34; class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param nodes: a array of Undirected graph node @return: a connected set of a Undirected graph \u0026#34;\u0026#34;\u0026#34; def connectedSet(self, nodes: List[UndirectedGraphNode]) -\u0026gt; List[List[int]]: # write your code here visited = set() ans = [] for node in nodes: if node not in visited: ans.append(self.bfs(nodes, node, visited)) return ans def bfs(self, nodes, start_node, visited): ans = [] queue = [start_node] visited.add(start_node) while queue: node = queue.pop(0) ans.append(node.label) for neighbor in node.neighbors: if neighbor not in visited: queue.append(neighbor) visited.add(neighbor) return sorted(ans) 算法說明 BFS 搜尋圖，並把同一組的歸類再一起。\ninput handling x\nBoundary conditions 用 for 配合 visited 來尋找 start_node，\nbfs 內部用 queue 來掌控搜尋範圍。\n個人範例程式碼 - 2022/6/1 一刷 from typing import ( List, ) from lintcode import ( UndirectedGraphNode, ) \u0026#34;\u0026#34;\u0026#34; class UndirectedGraphNode: def __init__(self, x): self.label = x self.neighbors = [] \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param nodes: a array of Undirected graph node @return: a connected set of a Undirected graph \u0026#34;\u0026#34;\u0026#34; def connectedSet(self, nodes: List[UndirectedGraphNode]) -\u0026gt; List[List[int]]: # write your code here if not nodes: return [] ans = [] visited = set() for node in nodes: if node in visited: continue visited.add(node) ans.append(self.bfs(node, visited)) return ans def bfs(self, start_node, visited): ans = [] queue = [start_node] while(queue): node = queue.pop(0) ans.append(node.label) for neighbor in node.neighbors: # add all not visited neighbor if neighbor not in visited: queue.append(neighbor) visited.add(neighbor) return sorted(ans) 算法說明 經典的 bfs 問題，可以複製此架構變化的 BFS 題目很多。\n最外圈，先透過 for 確保所有 node 都會被搜尋，再搭配上 visited，確保不重複搜尋。\n內圈的 BFS，我們透過 while 不斷地新增 nighbor，直到 visited 全滿不能再新增，就結束回傳答案。\n由於題目有要求回傳的答案要是 sorted，請注意先 sorted 後再回傳。\ninput handling 處理沒有 input 的情況，return []\nBoundary conditions 用 for 配合 visited 來尋找 start node，\nbfs 內部用 queue 來掌控搜尋範圍。\nReference 找无向图的连通块 · Connected Component in Undirected Graph ","date":"2022-06-08T03:23:11+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/bfs-topological-sort/lintcode-python-431/","tags":["BFS"],"title":"【Lintcode】python - [431] Connected Component in Undirected Graph 個人解法筆記 | 內含 BFS 模板 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 202. Happy Number\n難度 easy\n個人範例程式碼 class Solution: def isHappy(self, n: int) -\u0026gt; bool: if not n: return False visited = set([n]) while True: n = self.get_square_sum(n) if n == 1 or n in visited: break visited.add(n) return n == 1 def get_square_sum(self, n): total_sum = 0 for each_n in str(n): total_sum += int(each_n)**2 return total_sum 算法說明 這題我覺得是題目還比較難懂\u0026hellip; 簡單解釋一下，\n一個數字能被稱作「Happy number」，\n表示這個數字在「無限重複”把每一個單一數字平方並加總“的過程中，最終可以到達 1 這個數字」\n舉例 - 成功的例子 (不斷的把每個單一數字平方並加總) 7 =\u0026gt;\n49 =\u0026gt;\n97 (16+81) =\u0026gt;\n130 (81+49) =\u0026gt;\n10 (1+9+0) =\u0026gt;\n1 (發現答案，是 Happy number)\n舉例 - 失敗的例子 (不斷的把每個單一數字平方並加總，會循環，永遠到不了 1) 2 =\u0026gt;\n4 =\u0026gt;\n16 =\u0026gt;\n37 (1+36) =\u0026gt;\n58 (9+49) =\u0026gt;\n89 (25+64) =\u0026gt;\n145 (64+81) =\u0026gt;\n42 (1+16+25) =\u0026gt;\n20 (16+4) =\u0026gt;\n4 (開始循環，不是 Happy number)\n看懂題目後，這題就很簡單了，我們可以簡單地用一個 hashset 來記錄我們已經計算過的數字。\ninput handling 如果沒有 n，return False\nBoundary conditions 一直循環，直到發現重複的數字\nReference 快乐数 · Happy Number ","date":"2022-06-07T19:29:12+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-202/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [202] Happy Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 155. Min Stack\n難度 easy\n個人範例程式碼 class MinStack: def __init__(self): self.stack = [] self.min_stack = [] def push(self, val: int) -\u0026gt; None: self.stack.append(val) if len(self.min_stack) == 0: # min stack empty self.min_stack.append(val) else: self.min_stack.append(min(val, self.min_stack[-1])) # \u0026#34;new smallest\u0026#34; or \u0026#34;copy last smallest\u0026#34; def pop(self) -\u0026gt; None: result = self.stack.pop() del self.min_stack[-1] return result def top(self) -\u0026gt; int: return self.stack[-1] def getMin(self) -\u0026gt; int: return self.min_stack[-1] # Your MinStack object will be instantiated and called as such # obj = MinStack() # obj.push(val) # obj.pop() # param_3 = obj.top() # param_4 = obj.getMin() 算法說明 設計類題目，我們使用兩個 stack 來處理，\n一個紀錄目前 stack 的狀況，另外一個用來記錄 min_stack 目前的排序。\ninput handling x (設計類問題)\nBoundary conditions x (設計類問題)\nReference 带最小值操作的栈 · Min Stack ","date":"2022-06-07T18:58:16+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-155/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [155] Min Stack 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 103. Binary Tree Zigzag Level Order Traversal\n難度 medium\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def zigzagLevelOrder(self, root: Optional[TreeNode]) -\u0026gt; List[List[int]]: if not root: return [] ans = [] this_layer = [root] reverse_flag = False while this_layer: this_layer_ans = [] next_layer = [] for node in this_layer: this_layer_ans.append(node.val) if node.left: next_layer.append(node.left) if node.right: next_layer.append(node.right) if reverse_flag: this_layer_ans.reverse() ans.append(this_layer_ans[:]) # deepcopy this_layer = next_layer reverse_flag = not reverse_flag return ans 算法說明 類似 level order traversal 的邏輯，只不過在 zigzag 中我們要做的是間隔 layer 反轉輸出的結果。\n至於要反轉的話，加一個反轉 flag 即可輕鬆完成\ninput handling 如果沒有 root，return []\nBoundary conditions 透過 layer 一層一層循環，直到下一層 layer 無東西為止。\nReference Python List reverse() ","date":"2022-06-07T18:42:04+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-103/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [103] Binary Tree Zigzag Level Order Traversal 個人解法筆記"},{"categories":["715 - Binary Serach"],"content":"題目出處 33. Search in Rotated Sorted Array\n難度 Medium\n題目分類 Array, BinarySearch\n難度 medium\n個人範例程式碼 - 2022/6/7 三刷 class Solution: def search(self, nums: List[int], target: int) -\u0026gt; int: start = 0 end = len(nums) - 1 while start + 1 \u0026lt; end: mid = (start + end) // 2 # print(start, mid, end) # print(nums[start], nums[mid], nums[end]) if nums[mid] == target: return mid if nums[start] \u0026lt; nums[mid]: # front: simple go up if nums[start] \u0026lt;= target \u0026lt; nums[mid]: end = mid else: start = mid else: # back: simple go up if nums[mid] \u0026lt; target \u0026lt;= nums[end]: start = mid else: end = mid else: if nums[start] == target: return start elif nums[end] == target: return end else: return -1 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 分成 2 種 case 討論，2 種 case 底下又有兩種 case，\n先看 mid 的落點，\n分成 「start \u0026lt; mid」 或 「mid \u0026lt; end」\n再來各自看 target 的落點，(看有沒有落在單純上升的範圍)。\n最後我們可以整理成： 「start \u003c mid」 「start \u0026lt; target \u0026lt; mid」：純上升範圍 「else」：亂的範圍 (依然是 rotated sorted array) 「mid \u003c end」 「mid \u0026lt; target \u0026lt; end」：純上升範圍 「else」：亂的範圍 (依然是 rotated sorted array) input handling 處理沒有輸入的時候，return -1\nBoundary conditions binary search 的結束條件\n個人範例程式碼 - 2022/4/5 二刷 class Solution: def search(self, nums: List[int], target: int) -\u0026gt; int: if not nums: return -1 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(nums[mid] \u0026gt;= nums[end]): if(nums[start] \u0026lt;= target \u0026lt;= nums[mid]): end = mid else: start = mid else: # (nums[mid] \u0026lt; nums[end]): if(nums[mid] \u0026lt;= target \u0026lt;= nums[end]): start = mid else: end = mid else: if(nums[start] == target): return start elif(nums[end] == target): return end else: return -1 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題可以說是 binary search 的最 #重要問題，\n主要因為要寫得出這題，基本上要對 binary search 的各種細節幾乎都已經非常清楚才寫得出來。\n注意幾個討論的重點，我們可以把整個題目拆成 2*2 個 case，\n比 start 還大的情況 (等同於 \u0026gt;= end 的情況) 比 end 還小的情況 然後再針對這兩個情況，再拆成兩種 mid 的情況：\n比 start 還大的情況 (等同於 \u003e= end 的情況) start \u0026lt; target \u0026lt; mid 的情況 else 比 end 還小的情況 else mid \u0026lt; target \u0026lt; end 的情況 我們要注意移動的時候要移動 start 還是 end，可以從圖上觀察而出。\n此外，邊界條件的處理也是此題相當重要的關鍵。\n「\u0026gt;= end」 這個觀念我們在 【Leetcode】python – [153] Find Minimum in Rotated Sorted Array 個人解法筆記\n已經有詳細討論過，如果不清楚可以去看看\n「邊界有沒有等於」? 這問題也非常重要，因為這會影響我們要移動 start 或是 end，\n而我們需要讓邊界「等於」，因為如果 mid 正好是解答，\n我們需要讓「移動側 = mid = target」，因此邊界條件中會有「包含等於的條件」\ninput handling 處理沒有輸入的時候，return -1\nBoundary conditions binary search 的結束條件\n個人解法筆記 (解法重點) - 2021/7/7 一刷 示意圖 注意事項 注意 corner case 的處理\n範例： [5, 1, 3]\n需要注意判斷非 ascending 時，是否該值會出現在對應的區間。\n個人範例程式碼 class Solution: def search(self, nums: List[int], target: int) -\u0026gt; int: l_idx , r_idx = 0, len(nums)-1 while l_idx \u0026lt;= r_idx: mid_idx = (l_idx + r_idx)//2 if nums[mid_idx] == target: return mid_idx # search left if nums[l_idx] \u0026lt;= nums[mid_idx]: if nums[l_idx] \u0026lt;= target and target \u0026lt; nums[mid_idx]: # ascending side r_idx = mid_idx - 1 else: l_idx = mid_idx + 1 # search right else: if nums[mid_idx] \u0026lt; target and target \u0026lt;= nums[r_idx]: # ascending side l_idx = mid_idx + 1 else: r_idx = mid_idx - 1 return -1 Reference 搜索旋转排序数组 · Search in Rotated Sorted Array https://leetcode.com/problems/remove-nth-node-from-end-of-list/discuss/8802/3-short-Python-solutions ","date":"2022-06-07T16:49:40+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0307.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/binary-search/leetcode-python-33/","tags":[],"title":"【Leetcode】python - [33] Search in Rotated Sorted Array 個人解法筆記 #重要題型"},{"categories":["730 - Leetcode 重要題型"],"content":"題目出處 424. Longest Repeating Character Replacement\n難度 medium\n個人範例程式碼 class Solution: def characterReplacement(self, s: str, k: int) -\u0026gt; int: ans = 0 window_counter = defaultdict(int) max_counter = 0 start = 0 for end in range(len(s)): # print(start, end, s[start:end+1]) new_word = s[end] window_counter[new_word] += 1 max_counter = max(max_counter, window_counter[new_word]) # end - start + 1 = window length (delete max duplicate \u0026lt;= k) if (end - start + 1) - max_counter \u0026lt;= k: ans = max(ans, end - start + 1) else: # wrong case, move window (first delete then move) delete_word = s[start] window_counter[delete_word] -= 1 start += 1 return ans 算法說明 讓我們先來理解題目，這題目主要是在詢問我們在「允許替換最多 k 個文字的情況下」，\n我們能夠組出的最長文字。\n我們使用「sliding window」的概念，配合 counter，計算最多重複的字元。\n我們可以藉由不斷移動 end，當發現 window 不滿足條件時，\n我們「不改變 window 的大小 (已經是最大)」，去不斷的 move start 直到 window 有機會變得更大。\nwindow 大小只會變大，「當發現不符合規則時，我們只會繼續移動 window，不會縮小 window」\ninput handling 一同在 for 內處理\nBoundary conditions 用 for 來控制範圍\nReference 替换后的最长重复字符 · Longest Repeating Character Replacement ","date":"2022-06-07T14:49:51+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0376.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/patterns/leetcode/leetcode-python-424/","tags":["LeetCode"],"title":"【Leetcode】python - [424] Longest Repeating Character Replacement 個人解法筆記 #重要題型"},{"categories":["BFS (連通圖搜索)"],"content":"題目出處 178 · Graph Valid Tree\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: An integer @param edges: a list of undirected edges @return: true if it\u0026#39;s a valid tree, or false \u0026#34;\u0026#34;\u0026#34; def valid_tree(self, n: int, edges: List[List[int]]) -\u0026gt; bool: # write your code here if len(edges) != n-1: return False # not completed graph graph = self.build_graph(edges) return self.bfs(graph, n) def bfs(self, graph, n): visited = {0} queue = [0] while queue: node = queue.pop(0) for neighbor in graph[node]: if neighbor not in visited: queue.append(neighbor) visited.add(neighbor) return len(visited) == n def build_graph(self, edges): graph = collections.defaultdict(list) for edge in edges: graph[edge[0]].append(edge[1]) graph[edge[1]].append(edge[0]) return graph 算法說明 這題的存在，直接地告訴了我們 graph 與 tree 的差別，\n簡單來說，tree 就等於「沒有環的 graph」，\n我們使用 bfs 來幫助我們搜尋，看是否全部搜尋完全即可。\n注意：這題題目中有提示我們，我們可以從 0 做為起點開始搜尋。\ninput handling 因為 n 的點至少需要有 n-1 條邊，且我們「tree 又必定沒有環」，\n因此我們可以先判斷 len(edges) 是否等於 n-1，沒有可以直接 return False\nBoundary conditions 用 while queue 搭配做出 bfs，把全部可找到的點都加入 visited 後，\n比較 len(visited) 是否等於 n，作為我們最後的答案。\nReference 图是否是树 · Graph Valid Tree ","date":"2022-06-07T02:26:49+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/bfs-topological-sort/lintcode-python-178/","tags":["BFS"],"title":"【Lintcode】python - [178] Graph Valid Tree 個人解法筆記 #重要題型"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 572. Subtree of Another Tree\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -\u0026gt; bool: # end of recursion if not root: return False # define if self.is_same_tree(root, subRoot): return True # split return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot) def is_same_tree(self, root, subroot): # end of recursion if not root and not subroot: return True if not root or not subroot: # only one side left return False if root.val != subroot.val: return False return self.is_same_tree(root.left, subroot.left) and self.is_same_tree(root.right, subroot.right) 算法說明 原本的 is_sub_tree，就已經可以幫助我們把大題目拆成小題目了，\n我們只需要再多補上一個，比較兩個 tree 是否相同的函數即可。\ninput handling if not root，return False (上層只要有一次 True 即可，因此用 or 比較其他答案)\nBoundary conditions 用 not root，來判斷 Tree 的結尾。\nReference 另一个树的子树 · Subtree of Another Tree ","date":"2022-06-06T18:21:27+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-572/","tags":["DFS"],"title":"【Leetcode】python - [572] Subtree of Another Tree 個人解法筆記"},{"categories":["DFS (全部方案)","DP (座標型 - 二維)","Two pointers (背向雙指針 ←→)"],"content":"題目出處 647. Palindromic Substrings\n難度 medium\n個人範例程式碼 - DFS (會 TLE) class Solution: def countSubstrings(self, s: str) -\u0026gt; int: ans = 0 return self.dfs(s, 0) def dfs(self, s, start): # end of recursion if start \u0026gt;= len(s): return 0 ans = 0 # define for end in range(start, len(s)): if self.is_palindromic(s[start:end+1]): ans += 1 # split return ans + self.dfs(s, start+1) def is_palindromic(self, s): # print(s) start = 0 end = len(s)-1 while start \u0026lt;= end: if s[start] == s[end]: start += 1 end -= 1 else: return False return True 算法說明 DFS 的作法，應該會是最直覺地把所有可能性都舉出來，\n總共會花大約 O(2^n) 的時間，會造成 TLE\ninput handling 一同在 DFS 內處理\nBoundary conditions 在 DFS 內處理，發現 start \u0026gt;= end 時 return\n個人範例程式碼 - DP class Solution: def countSubstrings(self, s: str) -\u0026gt; int: ans = 0 length = len(s) dp = [[0 for start in range(length)] for end in range(length)] for start in range(length): for end in range(start, length): # print(start, end) if dp[start][end]: # already recorded ans += 1 continue if self.is_palindromic(s[start:end+1]): ans += 1 tmp_start, tmp_end = start, end while tmp_start \u0026lt;= tmp_end: dp[tmp_start][tmp_end] = 1 tmp_start += 1 tmp_end -= 1 return ans def is_palindromic(self, s): # print(s) start = 0 end = len(s)-1 while start \u0026lt;= end: if s[start] == s[end]: start += 1 end -= 1 else: return False return True 算法說明 在上方，我們知道 DFS 因為需要 O(2^n) 的運算時間會造成 TLE 後，\n我們只好採取更快的策略，最直覺的我們會想到 DP\n因為 DP 最擅長幫我們把解題時間從 O(2^n) 優化至 O(n^2) 的時間。\n而要讓題目優化至 O(n^2) 時間，\n我們就先建立一個 start, end 的二維查詢表。\n我們更新的方式也很簡單，\n如果當發現 dp[i][j] 是 palindrome，那也表示 dp[i+1][j-1] 也是 palindrome，直到 start \u0026gt; end，\n因此我們可以劃出上圖的紅色斜線，\n判斷時，我們初始化都是 0，如果之後發現已經被改成 1，\n即可 continue 直接 pass 計算。\ninput handling 一同在 DP 內處理\nBoundary conditions 兩層 DP 的 for 迴圈，\n分別代表著 start, end\nReference 回文子串 · Palindromic Substrings ","date":"2022-06-06T17:57:09+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0375.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-647/","tags":["DFS","DP"],"title":"【Leetcode】python - [647] Palindromic Substrings 個人解法筆記"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 226. Invert Binary Tree\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def invertTree(self, root: Optional[TreeNode]) -\u0026gt; Optional[TreeNode]: if not root: return root self.invertTree(root.left) self.invertTree(root.right) root.left, root.right = root.right, root.left return root 算法說明 單純的一路一直把左右交換就好，\n把題目切分成「更小的子樹，也需做一樣的事情」。\ninput handling if not root (None)，直接 return root (None)\nBoundary conditions 一路 DFS Tree 直到底部，中間過程 swap 「left, right」\nReference ","date":"2022-06-06T16:57:53+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-226/","tags":["DFS"],"title":"【Leetcode】python - [226] Invert Binary Tree 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 191. Number of 1 Bits\n難度 easy\n個人範例程式碼 class Solution: def hammingWeight(self, n: int) -\u0026gt; int: # 7 (10) = 111 (2) # 4 (10) = 100 (2) # 3 (10) = 011 (2) cnt = 0 while(n != 0): cnt += (n \u0026amp; 1) # and first bit n = n \u0026gt;\u0026gt; 1 # (// 2) return cnt 算法說明 簡單的位元運算題目，\n我們可以先觀察「 10進位 (10) 與 2進位 (2) 」的變化，\n7 (10) = 111 (2)\n4 (10) = 100(2)\n3 (10) = 11(2)\n我們提取第一個位元 (最右側位元) 的方式是 (原數字 \u0026amp; 1)，\n並一路往右 \u0026raquo; 1，直到原值只剩下 0。\ninput handling x\nBoundary conditions 用 while 至 0 來控制範圍。\nReference 判断一个整数中有多少个1 · Number of 1 Bits ","date":"2022-06-06T16:23:17+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-191/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [191] Number of 1 Bits 個人解法筆記"},{"categories":["DP (座標型 - 二維)"],"content":"題目出處 1143. Longest Common Subsequence\n難度 medium\n個人範例程式碼 class Solution: def longestCommonSubsequence(self, text1: str, text2: str) -\u0026gt; int: m, n = len(text1), len(text2) dp = [[0]*(n+1) for _ in range(m+1)] for i in range(1, m+1): for j in range(1, n+1): if text1[i-1] == text2[j-1]: dp[i][j] = dp[i-1][j-1] + 1 # same case, update value form text[i-1][j-1] else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) # default update return dp[-1][-1] 算法說明 經典的 LCS 問題，我們建表來進行搜索，\n表格更新的邏輯為：\n如果有字母相同，表示 text1, text2 一起移動： dp[i][j] = dp[i-1][j-1] + 1 其他情況(無字母相同)，只更新最大值： dp[i][j] = max(dp[i-1][j], dp[i][j-1]) input handling 一同在 dp 內處理。\nBoundary conditions 用兩層 for 迴圈來控制範圍\nReference 最长公共子序列 · Longest Common Subsequence ","date":"2022-06-06T16:03:01+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0374.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/dynamic-programming/761-classic-dp/leetcode-python-1143/","tags":["DP"],"title":"【Leetcode】python - [1143] Longest Common Subsequence 個人解法筆記"},{"categories":["717 - Hash Table"],"content":"題目出處 347. Top K Frequent Elements\n難度 medium\n個人範例程式碼 class Solution: def topKFrequent(self, nums: List[int], k: int) -\u0026gt; List[int]: if not nums: return nums # O(N) counter = Counter(nums) freq_counter = defaultdict(list) # O(N) for key, value in counter.items(): freq_counter[value].append(key) ans = [] # O(NlogN) for key in sorted(freq_counter.keys(), reverse=True): ans += freq_counter[key] k -= len(freq_counter[key]) if k \u0026lt;= 0: break return ans 直接使用 dict.items() + sorted tuple 排序 更俐落一點的寫法，直接把 dict.items() 轉作 tuple 來看後，直接排序\n如果用法不熟，用上面方法即可。\nclass Solution: def topKFrequent(self, nums: List[int], k: int) -\u0026gt; List[int]: if not nums: return nums # O(N) counter = Counter(nums) # key: freq # O(NlogN) sorted_tuple_by_freq = sorted(counter.items(), key=lambda x: x[1], reverse=True) # print(sorted_tuple_by_freq) ans = [] for i in range(k): ans.append(sorted_tuple_by_freq[i][0]) return ans 算法說明 我們先計算數字頻率，可以搭配內建函數 counter 快速實現，\n再來針對出現次數進行排序，\n然後選出前 K 個答案即可。\ninput handling 如果沒有 nums，return nums\nBoundary conditions 用 for 來控制範圍\nReference 前K个高频元素 · Top K Frequent Elements ","date":"2022-06-06T04:15:49+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/hash-table/leetcode-python-347/","tags":[],"title":"【Leetcode】python - [347] Top K Frequent Elements 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 659 · Encode and Decode Strings\n難度 medium\n個人範例程式碼 class Solution: \u0026#34;\u0026#34;\u0026#34; @param: strs: a list of strings @return: encodes a list of strings to a single string. \u0026#34;\u0026#34;\u0026#34; def encode(self, strs): # write your code here return \u0026#34; \u0026#34;.join(strs) \u0026#34;\u0026#34;\u0026#34; @param: str: A string @return: dcodes a single string to a list of strings \u0026#34;\u0026#34;\u0026#34; def decode(self, str): # write your code here return str.split(\u0026#34; \u0026#34;) 算法說明 這題是應用的 design 類問題，\n因為我們在傳輸數據時，大部分都沒有辦法直接傳遞「我們定義好的資料結構」，\n通常我們都會先進行 encoding 把資料加密，例如把資料轉成文字化。\n等待送達目的後，再對資料進行 decoding 解密，把文字化資料轉回「我們要的資料結構」，\n而這題概念上大概就是要我們實作這樣的概念，方法有無限多種，\n但以考題來說，這題應該是理解概念就好。\ninput handling x (design 類問題)\nBoundary conditions x (design 類問題)\nReference 编码和解码字符串 · Encode and Decode Strings ","date":"2022-06-06T03:34:37+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-659/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [659] Encode and Decode Strings 個人解法筆記"},{"categories":["717 - Hash Table","Interval"],"content":"題目出處 919 · Meeting Rooms II\n難度 medium\n個人範例程式碼 from typing import ( List, ) from lintcode import ( Interval, ) \u0026#34;\u0026#34;\u0026#34; Definition of Interval: class Interval(object): def __init__(self, start, end): self.start = start self.end = end \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param intervals: an array of meeting time intervals @return: the minimum number of conference rooms required \u0026#34;\u0026#34;\u0026#34; def min_meeting_rooms(self, intervals: List[Interval]) -\u0026gt; int: # Write your code here if not intervals: return 0 time_table = [] for interval in intervals: time_table.append((interval.start, +1)) time_table.append((interval.end, -1)) time_table.sort(key=lambda x: x[0]) ans = 0 booked_room = 0 for time, room_used in time_table: booked_room += room_used ans = max(ans, booked_room) return ans 或者，我們也可以用 hashtable 來記錄 (概念相同) from typing import ( List, ) from lintcode import ( Interval, ) \u0026#34;\u0026#34;\u0026#34; Definition of Interval: class Interval(object): def __init__(self, start, end): self.start = start self.end = end \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param intervals: an array of meeting time intervals @return: the minimum number of conference rooms required \u0026#34;\u0026#34;\u0026#34; def min_meeting_rooms(self, intervals: List[Interval]) -\u0026gt; int: # Write your code here if not intervals: return 0 time_table = collections.defaultdict(int) for interval in intervals: time_table[interval.start] += 1 time_table[interval.end] -= 1 # print(time_table) ans = 0 used_room = 0 for time in sorted(time_table.keys()): used_room += time_table[time] ans = max(ans, used_room) return ans 算法說明 這題目有前一題單純只判斷有沒有重複會議室，而這題是要直接計算所需要的會議室數量，上一題可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-920/\n現在要安排需要的會議室數量，\n我們可以直接把所有的時間點 (start, end) 都存入一個 table 裡面，排序後就可以得到我們要的答案。\n儲存的方式為 (start_time, +1), (end_time, +1)，\n最後我們只要將第一個欄位的 time 排序即可。\n之後我們可以再使用 +1, -1 去判斷個時段的會議室增減。\ninput handling 如果沒有 intervals, 回傳 0 (題目沒特別要求)\nBoundary conditions 用 for 迴圈控制範圍\nReference 会议室 II · Meeting Rooms II ","date":"2022-06-06T03:22:51+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/hash-table/lintcode-python-919/","tags":["Interval"],"title":"【Lintcode】python - [919] Meeting Rooms II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 920 · Meeting Rooms\n難度 easy\n個人範例程式碼 from typing import ( List, ) from lintcode import ( Interval, ) \u0026#34;\u0026#34;\u0026#34; Definition of Interval: class Interval(object): def __init__(self, start, end): self.start = start self.end = end \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param intervals: an array of meeting time intervals @return: if a person could attend all meetings \u0026#34;\u0026#34;\u0026#34; def can_attend_meetings(self, intervals: List[Interval]) -\u0026gt; bool: # Write your code here if not intervals: return True intervals.sort(key = lambda x:x.start) end = intervals[0].end for i, interval in enumerate(intervals): if i \u0026gt; 0: if end \u0026lt;= interval.start: # end \u0026lt;= next_start (0,8), (8,10) end = interval.end continue else: return False return True 算法說明 單純檢查會議室有沒有重疊時間的問題，我們可以先 sort start 時間後，\n再檢查是否「上一個 end \u0026lt;= 下一個 start 」的時間點\n(題目有說，(0, 8),(8, 10)不算是共用會議室，因此要有「等於」)\n當我們發現不符合我們上述的規則時，\n直接 return False。\ninput handling 如果沒有 intervals, 回傳 True (題目沒特別要求)\nBoundary conditions 用 for 迴圈控制範圍\nReference 会议室 · Meeting Rooms ","date":"2022-06-06T03:02:02+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-920/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [920] Meeting Rooms 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 503. Next Greater Element II\n難度 medium\n個人範例程式碼 class Solution: def nextGreaterElements(self, nums: List[int]) -\u0026gt; List[int]: if not nums: return [] ans = [-1 for i in range(len(nums))] # default not found stack = [] # first round for i, num in enumerate(nums): while stack and nums[stack[-1]] \u0026lt; num: idx = stack.pop() ans[idx] = num stack.append(i) # push index in stack # sencond round, clear stack for i, num in enumerate(nums): if not stack: break while stack and nums[stack[-1]] \u0026lt; num: idx = stack.pop() ans[idx] = num return ans 算法說明 本題的前一題為無迴圈版本，這題目的 array 為循環數組，前一題可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-496/\n像這類有「找後續/前綴」中比較大或比較小的第一個數字，\n我們通常會用 stack 來保存「等待被決定的內容」。\n當我們發現「stack[-1]」\u0026lt;「新的數字」，表示找到答案，\n我們 pop[-1] 出 stack，並記錄「 pop 的答案就是當前數字 」。\n最後剩下在 stack 的內容，答案都是 -1\n與前一題不同處 與前一題不同的地方是，因為這次題目給的 array 變成有循環 array，\n因此我們只需要循環兩次即可。\n在第二次循環的過程中，目標是處理掉 stack 剩下的內容，\n如果最後都還處理不掉的話，就是 -1 (找不到的情況，我們 default 就先給 -1)\ninput handling 如果沒有 nums, 回傳 [] (題目沒特別要求)\nBoundary conditions 用 for 迴圈控制範圍\nReference 下一个更大的数 II · Next Greater Element II ","date":"2022-06-06T01:56:25+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0373.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-503/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [503] Next Greater Element II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 496. Next Greater Element I\n難度 easy\n個人範例程式碼 class Solution: def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -\u0026gt; List[int]: ans_hashtable = {} stack = [] for num in nums2: while stack and stack[-1] \u0026lt; num: # new is greater ans_hashtable[stack[-1]] = num # last number\u0026#39;s answer = this num del stack[-1] stack.append(num) for rest_element in stack: ans_hashtable[rest_element] = -1 return [ans_hashtable[num] for num in nums1] 算法說明 像這類有「找後續/前綴」中比較大或比較小的第一個數字，\n我們通常會用 stack 來保存「等待被決定的內容」。\n當我們發現「stack[-1]」\u0026lt;「新的數字」，表示找到答案，\n我們 pop[-1] 出 stack，並記錄「 pop 的答案就是當前數字 」。\n最後剩下在 stack 的內容，答案都是 -1\ninput handling 如果沒有 nums, 回傳 [] (題目沒特別要求)\nBoundary conditions 用 for 迴圈控制範圍\nReference 下一个更大的数 I · Next Greater Element I ","date":"2022-06-06T01:51:25+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0373.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-496/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [496] Next Greater Element I 個人解法筆記"},{"categories":["850 - Google Sheet / Excel"],"content":"前言 我在 Google Sheet / Excel 會使用到的「統計」單一儲存格內的「個別文字數量」，\n在這篇文章整理一下公式。\n範例 就像下圖一樣，我們統計個別 A, B, C 在左側格子內出現的次數，\n統計公式 我們以統計 A 的公式為例，我們在右方輸入：\n=LEN($A1)-LEN(SUBSTITUTE($A1,\u0026#34;A\u0026#34;,\u0026#34;\u0026#34;)) 表示我們計算 \u0026ldquo;A1\u0026rdquo; 格子中，\u0026ldquo;A\u0026rdquo; 的數量\n公式的想法是來自於此網站：\nExcel-計算字串中的字元數量 而此公式我們也翻譯一下他的概念，\n我們把 \u0026ldquo;A\u0026rdquo; 都取代成 \u0026ldquo;\u0026quot;，因此剩下的長度被原來的長度剪掉後，\n就會是我們要的長度了。\nReference Excel-計算字串中的字元數量 ","date":"2022-06-05T03:23:51+08:00","image":"https://wongwongnotes.com/images/restored/2021/12/google_sheet_count_str.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/productivity/google-sheet-excel/google-sheet-excel-count-str/","tags":["Bash","Linux","Ubuntu"],"title":"【Google Sheet / Excel #4】「統計」單一儲存格內的「個別文字數量」範例 (欄位數文字)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 238. Product of Array Except Self\n難度 medium\n個人範例程式碼 class Solution: def productExceptSelf(self, nums: List[int]) -\u0026gt; List[int]: if not nums: return [] result = [1 for _ in range(len(nums))] prefix_product = 1 for i in range(len(nums)): result[i] *= prefix_product # first *= 1 prefix_product *= nums[i] postfix_product = 1 for i in range(len(nums)-1, -1, -1): result[i] *= postfix_product # first *= 1 postfix_product *= nums[i] return result 算法說明 這題要 O(n^2) 時間做出來非常容易，但題目先是希望要求 O(n) 時間算完，\n既然都要求這樣的時間，我們勢必要做一些特殊的處理。\n這裡我們用到類似 prefixSum 的概念 (我們用的是 prefixProduct)\n因為仔細觀察，我們會發現答案就等於「前面積*後面積」，而數字分布是「前面積、(該數字)、後面積」，\n因此我們可以簡單的用一個數字紀錄答案。\n同時我們也可以實現「 O(1) 空間」的題目追加要求\ninput handling 如果沒有 nums, 回傳 [] (題目沒特別要求)\nBoundary conditions 用兩次 for 迴圈控制範圍\nReference 数组除了自身的乘积 · Product of Array Except Self ","date":"2022-06-04T04:37:26+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0372.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-238/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [238] Product of Array Except Self 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 152. Maximum Product Subarray\n難度 medium\n個人範例程式碼 class Solution: def maxProduct(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 max_product = [0 for _ in range(len(nums))] min_product = [0 for _ in range(len(nums))] for i, num in enumerate(nums): if i \u0026gt; 0: if num \u0026gt; 0: max_product[i] = max(num, num * max_product[i-1]) # only me, or bigger (smaller) min_product[i] = min(num, num * min_product[i-1]) else: max_product[i] = max(num, num * min_product[i-1]) min_product[i] = min(num, num * max_product[i-1]) else: max_product[0] = nums[0] min_product[0] = nums[0] # print(max_product) # print(min_product) return max(max_product) 算法說明 看起來複雜的問題，想一下發現最主要在搞事的就是負號，\n因為正常來說，我們一直乘整數，數字只會一直不斷變大，\n因此這樣的話，「我們唯一要思考的就只有負號的處理」\n為了處理負號，我們同時記錄「至當前數字的 min 與 max 的 dp」，\n紀錄時，我們要比較的內容也是關鍵，\n我們要比較：「當前數字，與之前的內容乘上該數字是誰大(小)」，\n而保留當前數字的原因，因為保留當前數字也等於，\n「表示前面我們不要了，只保留當前數字下來繼續往後乘」\n因此：\n當 nums[i] \u003e= 0 : max_dp[i] = max(nums[i], nums[i]*max[i-1]) min_dp[i] = min(nums[i], nums[i]*min[i-1]) 當 nums[i] \u003c 0 : max_dp[i] = max(nums[i], nums[i]*min[i-1]) min_dp[i] = min(nums[i], nums[i]*max[i-1]) 而初始化：\nmax_dp[0] = min_dp[0] = nums[0]\n或者，我們也可以這樣簡化 根據我們上面的討論，其實我們都知道我們要比較的就只有\n「當前數字 (表示前面我們不要了，只保留當前數字下來繼續往後乘)」、\n「nums[i]min[i-1]」、\n「nums[i]max[i-1]」\n同時取 max, min 記錄下來即可。\n因此簡化後的結果就是： class Solution: def maxProduct(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 max_product = [0 for _ in range(len(nums))] min_product = [0 for _ in range(len(nums))] for i, num in enumerate(nums): if i \u0026gt; 0: max_product[i] = max(num, num * max_product[i-1], num * min_product[i-1]) min_product[i] = min(num, num * max_product[i-1], num * min_product[i-1]) else: max_product[0] = nums[0] min_product[0] = nums[0] return max(max_product) input handling 一同在 dp 內處理\nBoundary conditions 用 for 迴圈控制範圍\nReference 乘积最大子序列 · Maximum Product Subarray ","date":"2022-06-04T03:28:20+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-152/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [152] Maximum Product Subarray 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 338. Counting Bits\n難度 easy\n個人範例程式碼 class Solution: def countBits(self, n: int) -\u0026gt; List[int]: dp = [0 for _ in range(n+1)] for i in range(1, n+1): if i % 2 == 0: # ood case dp[i] = dp[i\u0026gt;\u0026gt;1] else: # even case dp[i] = dp[i\u0026gt;\u0026gt;1] + 1 return dp 算法說明 個人覺得算法比較偏 tricky 的題目，看看即可，\n要注意的點是在題目不希望我們用 O(nlogn) 去實現，也就是不希望我們「一個一個算」，\n題目希望我們用 O(n) 時間完成，也就是我們必須想辦法「找到數字之間的相關性」，\n就可以猜測這個做法應該跟 dp 有關\n推理 dp 相關性 推理過程我們拆成奇數跟偶數分析，我們發現\n偶 -\u0026gt; 奇：最好討論的情況，因為末位一定是 0，dp[i] = dp[i\u0026raquo;1] + 1，(dp[i\u0026raquo;1] 代表左側所有位數) 奇 -\u0026gt; 偶：我們觀察後，比較難發現他會找到最低位的 0 第一次出現的位置 (不懂可以看圖)，我們要做有點連環式的 dp 分析 例如: '101' -\u003e '110' ，我們可以不管他前一個數字 '101' 是啥， 我們找到 '110' 的 '0' 會知道它的結果等於 dp['11'] 例如: '1111' -\u003e '10000' ，我們可以不管他前一個數字 '11111' 是啥， 我們找到 '10000' 的 '0' 會知道它的結果等於 dp['1000']， 而 dp['1000'] = dp['100'] = dp['10'] = dp['1'] = dp['0'] + 1， 類似這樣的連鎖效應。 input handling 一同在 dp 內處理\nBoundary conditions 用 for 迴圈控制範圍\nReference 数 1 · Counting Bits ","date":"2022-06-02T17:37:20+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0371.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-338/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [338] Counting Bits 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 799 · Backpack VIII\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: the value from 1 - n @param value: the value of coins @param amount: the number of coins @return: how many different value \u0026#34;\u0026#34;\u0026#34; def back_pack_v_i_i_i(self, n: int, value: List[int], amount: List[int]) -\u0026gt; int: # write your code here m = len(value) dp = [False for _ in range(n+1)] dp[0]= True current_max_value = 0 ans = 0 for i in range(1, m+1): cnt = [0 for _ in range(n+1)] current_max_value += amount[i-1] * value[i-1] for current_value in range(min(n, current_max_value)+1): last_value = current_value - value[i-1] if last_value \u0026gt;= 0 and dp[last_value] and (not dp[current_value]) and cnt[last_value] \u0026lt; amount[i-1]: # only found dp[last_value] True, dp[current_value] False ans += 1 cnt[current_value] = cnt[last_value] + 1 dp[current_value] = dp[current_value] or dp[last_value] # print(cnt) # print(dp) return ans 算法說明 本題是背包系列問題的第 7 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n第八題，這題我們要處理的是有重複、但有限制使用上限數量的問題。\n不同於只能使用一個，也不同於可以使用無線數量，\n我們在搜尋時需要多加一個計數器，來幫助我們避免超過使用上限。\n一種思維是 [2,4]*[3,1] 直接看做 [2,2,2,4] 來處理，也許會更容易 (就能夠比照單一數量的題目辦理)。\n不過處理上這題會有時間要求，如果直接無腦三層 for 迴圈，會發生 TLE。\n因此我們會需要做一些修改。\n我們可以把同個元素視為一組，再一次 O(n) 運算中就全部算完。\n三層 for 我們會要花 O(n^2) 的時間才算得完，使花的時間增加不少。\n同時優化空間與時間的版本： 我們只有一個 dp，配上一個我們新宣告的 cnt 作為我們的計數器，\n而我們只在 dp[last] = T 且 dp[current] = F 進行操作，理由是：\ndp[last] = T，理由是我們本來就應該要確認，先前的數值是組的出來的。 dp[current] = F，理由是我們要排除 1+1, 2 這樣計數器會被增加的情況，實際上我們不需要用 2 就組得出來 三層 for 迴圈的解法 - 會 TLE 這題如果無腦三層 for 迴圈的寫法，會觸發 TLE，導致不能通過。\n運算邏輯比較簡單，我們可以參考下圖：\n或者以下列的方式理解：\n以下就是三層 for 迴圈的程式碼 - 會 TLE from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: the value from 1 - n @param value: the value of coins @param amount: the number of coins @return: how many different value \u0026#34;\u0026#34;\u0026#34; def back_pack_v_i_i_i(self, n: int, value: List[int], amount: List[int]) -\u0026gt; int: # write your code here m = len(value) dp = [[False] *(n+1) for _ in range(2)] num_of_selected_items = 0 dp[num_of_selected_items][0]= True current_max_value = 0 for i in range(m): for get_i_amount in range(1, amount[i]+1): # get j amount num_of_selected_items += 1 dp[num_of_selected_items%2][0]= True # init current_max_value += value[i] for current_value in range(min(n, current_max_value)+1): # need update: from 0 to min(n, current_max_value)+1, current_max_value = we take all thing\u0026#39;s value dp[num_of_selected_items%2][current_value] = dp[(num_of_selected_items-1)%2][current_value] last_value = current_value - value[i] if last_value \u0026gt;= 0: dp[num_of_selected_items%2][current_value] = dp[num_of_selected_items%2][current_value] or dp[(num_of_selected_items-1)%2][last_value] # print(num_of_selected_items, dp) # print(dp[num_of_selected_items%2]) cnt = 0 for value in range(1, n+1): if dp[num_of_selected_items%2][value]: cnt += 1 return cnt input handling 一同在 dp 內處理\nBoundary conditions 用三層 for 來控制搜尋範圍，\n外層 for 控制選擇種類，\n內層 for 控制上限數量，\n最內層 for 使用的金錢上限 (表示我們目前花了多少錢)\nReference 背包问题VIII · Backpack VIII ","date":"2022-06-02T16:58:22+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0369.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-799/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [799] Backpack VIII 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 798 · Backpack VII\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: the money of you @param prices: the price of rice[i] @param weight: the weight of rice[i] @param amounts: the amount of rice[i] @return: the maximum weight \u0026#34;\u0026#34;\u0026#34; def back_pack_v_i_i(self, n: int, prices: List[int], weight: List[int], amounts: List[int]) -\u0026gt; int: # write your code here m = len(prices) dp = [[0]*(n+1) for _ in range(2)] # rotated array num_of_selected_items = 0 for i in range(m): # take one kind of price for get_j_amount in range(1, amounts[i]+1): # get j amount num_of_selected_items += 1 for current_price in range(prices[i], n+1): dp[num_of_selected_items%2][current_price] = dp[(num_of_selected_items-1)%2][current_price] last_price = current_price - prices[i] if last_price \u0026gt;= 0: dp[num_of_selected_items%2][current_price] = max(dp[num_of_selected_items%2][current_price], dp[(num_of_selected_items-1)%2][last_price] + weight[i]) # print(i, get_j_amount, dp) return dp[num_of_selected_items%2][-1] 算法說明 本題是背包系列問題的第 7 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n第七題，這題我們要處理的是有重複、但有限制使用上限數量的問題。\n不同於只能使用一個，也不同於可以使用無線數量，\n我們在搜尋時需要多加一個計數器，來幫助我們避免超過使用上限。\n一種思維是 [2,4]*[3,1] 直接看做 [2,2,2,4] 來處理，也許會更容易 (就能夠比照單一數量的題目辦理)。\ninput handling 一同在 dp 內處理\nBoundary conditions 用三層 for 來控制搜尋範圍，\n外層 for 控制選擇種類，\n內層 for 控制上限數量，\n最內層 for 使用的金錢上限 (表示我們目前花了多少錢)\nReference 背包问题VII · Backpack VII ","date":"2022-06-02T16:34:20+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-798/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [798] Backpack VII 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 801 · Backpack X\n難度 medium\n個人範例程式碼 class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: the money you have @return: the minimum money you have to give \u0026#34;\u0026#34;\u0026#34; def back_pack_x(self, n: int) -\u0026gt; int: # write your code here # 150, 250, 350 = 3, 5, 7 (//50) prices = [3, 5, 7] target = n // 50 dp = [price for price in range(target+1)] # 0,1,2,3... for current_target in range(1, target+1): for price in prices: last_price = current_target - price if last_price \u0026gt;= 0: dp[current_target] = min(dp[current_target], dp[last_price]) rest = n % 50 return dp[-1]*50 + rest 算法說明 本題是背包系列問題的第 10 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n第 10 題，也是背包問題的變化題型，這題我們是變成單位比較大值，\n我們替換成鈔票，面額直接是 150, 250, 350 去組合。\n首先我們如果直接一個一個處理，時間會太浪費，\n因此我們先取最大公因數 [150, 250, 350] = [3, 5, 7]\ndp 儲存我們「剩餘的最小價格」。\n外圈 for 我們選擇新的幣種，\n內圈 for 我們想計算的總額，記得也要先取 //50，並保留 rest = %50。\n最後務必記得回傳 「rest + dp[-1]*50」，一定要記得還有 rest 的部分！！\ninput handling 一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for 我們選擇新的幣種 (表示我們擁有新幣種後，可能有的組合。)\n內層 for 使用的金錢 (表示我們目前用多少錢去換)\nReference 背包问题X · Backpack X ","date":"2022-06-01T19:04:51+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-801/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [801] Backpack X 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 800 · Backpack IX\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: Your money @param prices: Cost of each university application @param probability: Probability of getting the University\u0026#39;s offer @return: the highest probability \u0026#34;\u0026#34;\u0026#34; def backpack_i_x(self, n: int, prices: List[int], probability: List[float]) -\u0026gt; float: # write your code here # probability of receiving at least one offer = 1 - \u0026#34;(not get offer * not get offer * ...)\u0026#34; \u0026lt;- we record minimum of this dp = [[1] * (n+1) for _ in range(2)] # record no offer probability for i in range(1, len(prices)+1): for j in range(1, n+1): dp[i%2][j] = dp[(i-1)%2][j] last_cost = j - prices[i-1] if last_cost \u0026gt;= 0: no_offer_prob = dp[(i-1)%2][last_cost] * (1 - probability[i-1]) dp[i%2][j] = min(dp[i%2][j], no_offer_prob) # print(dp) ans = 0 for each_price_no_offer_prob in dp[len(prices)%2]: ans = max(ans, 1 - each_price_no_offer_prob) return ans 算法說明 本題是背包系列問題的第 9 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n第九題，我們一間公司最多可以選一次，屬於變化題型的一種，大概念與之前相同。\n這題我們要算的是「只少拿到一間公司 offer 的成功機率」，\n我們先嘗試推導公式 = 「1 - (沒拿到公司 offer 的機率) * (沒拿到公司 offer 的機率) * (沒拿到公司 offer 的機率) \u0026hellip;」\n其中，「(沒拿到公司 offer 的機率)」= 「1 - 「(拿到公司 offer 的機率)」」\n為了我們處理方便，我們在 dp 中記錄下 「min((沒拿到公司 offer 的機率) * (沒拿到公司 offer 的機率) * (沒拿到公司 offer 的機率))\u0026hellip;」 的這一段。\n外圈 for 我們新發現的公司\n內圈 for 控制我們目前使用的金錢\n優化空間 - 滾動數組 這裡我們先放上優化前的 code (會有「空間超過的問題」)\nfrom typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param n: Your money @param prices: Cost of each university application @param probability: Probability of getting the University\u0026#39;s offer @return: the highest probability \u0026#34;\u0026#34;\u0026#34; def backpack_i_x(self, n: int, prices: List[int], probability: List[float]) -\u0026gt; float: # write your code here # probability of receiving at least one offer = 1 - \u0026#34;(not get offer * not get offer * ...)\u0026#34; \u0026lt;- we record minimum of this dp = [[1] * (n+1) for _ in range(len(prices)+1)] # record no offer probability for i in range(1, len(prices)+1): for j in range(1, n+1): dp[i][j] = dp[i-1][j] last_cost = j - prices[i-1] if last_cost \u0026gt;= 0: no_offer_prob = dp[i-1][last_cost] * (1 - probability[i-1]) dp[i][j] = min(dp[i][j], no_offer_prob) # print(dp) ans = 0 for each_price_no_offer_prob in dp[-1]: ans = max(ans, 1 - each_price_no_offer_prob) return ans 我們仔細觀察我們 dp 的運算過程，基本上每一層新的 dp[i][]，\n我們都只會參考 dp[i-1][] 的資料而已，(也就說，我們只參考前一層)，\n因此我們可以利用這個特性節省空間，宣告空間 i = 2 即可，\n而取 i 的變化時，我們取 mod 2，\n最後的答案就位於 dp[n%2][volume] 中。\ninput handling 如果沒有 nums，回傳 0，\n其他一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for 發現一間新的公司 (表示這間新的公司，我們決定去或不去有可能有哪些組合。)\n內層 for 使用的金錢 (表示我們目前花了多少錢)\nReference 背包问题 IX · Backpack IX ","date":"2022-06-01T18:45:19+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-800/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [800] Backpack IX 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 563 · Backpack V\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: an integer array and all positive numbers @param target: An integer @return: An integer \u0026#34;\u0026#34;\u0026#34; def back_pack_v(self, nums: List[int], target: int) -\u0026gt; int: # write your code here if not nums: return 0 # [1,1,1,1] has 4 ans, remove 1 has 4 choice n = len(nums) dp = [[0] * (target +1) for _ in range(2)] dp[0][0] = 1 prefix_sum = 0 for i in range(1, n+1): # take next category of coin dp[i%2][0] = 1 prefix_sum += nums[i-1] for j in range(1, min(target, prefix_sum)+1): # find current max dp[i%2][j] = dp[(i-1)%2][j] last_volume = j - nums[i-1] if last_volume \u0026gt;= 0: # add take new one coin method dp[i%2][j] += dp[(i-1)%2][last_volume] return dp[n%2][target] 算法說明 本題是背包系列問題的第 5 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n第五題，我們變成只有有限數量的物品可以拿，求可能的方法總數。\n因為拿的順序會影響結果，例如：「(2,2,3) 與 (3,2,2) 是同一個結果」，\n我們一開始就先 nums.sort()\n但這邊要注意陷阱「同 size 的不同物品，是為不同組合」，例如： [1,1,1,1], target = 3，要算 4 個結果 (可以想像我們是在決定拿掉哪一個)。\n外圈 for 我們去控制目前新拿的物品種類\n內圈 for 控制目前背包最大的容量\ndp 的運作 dp[i][j]，j 代表目前的大小，i 反映我們目前選擇的物件。\n優化空間 - 滾動數組 這裡我們先放上優化前的 code (會有「空間超過的問題」)\nfrom typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: an integer array and all positive numbers @param target: An integer @return: An integer \u0026#34;\u0026#34;\u0026#34; def back_pack_v(self, nums: List[int], target: int) -\u0026gt; int: # write your code here if not nums: return 0 # [1,1,1,1] has 4 ans, remove 1 has 4 choice n = len(nums) dp = [[0] * (target +1) for _ in range(n+1)] dp[0][0] = 1 for i in range(1, n+1): # take next category of coin dp[i][0] = 1 for j in range(1, target+1): dp[i][j] = dp[i-1][j] last_volume = j - nums[i-1] if last_volume \u0026gt;= 0: # add take new one coin method dp[i][j] += dp[i-1][last_volume] return dp[-1][-1] 我們仔細觀察我們 dp 的運算過程，基本上每一層新的 dp[i][]，\n我們都只會參考 dp[i-1][] 的資料而已，(也就說，我們只參考前一層)，\n因此我們可以利用這個特性節省空間，宣告空間 i = 2 即可，\n而取 i 的變化時，我們取 mod 2，\n最後的答案就位於 dp[n%2][volume] 中。\n小優化時間 在內層的 for 迴圈，我們其實每次不一定有必要都算到 target 那麼大，\n我們可以知道，多一個新的物品，最大可能的 size 就是前面全選的合。\n因此，在內層的 for 迴圈中，\n我們可以多取一個 「min(target, prefix_sum) + 1」，\n可以節省大量的明明算不到 target，卻總是算到 target 位置的時間。\ninput handling 如果沒有 nums，回傳 0，\n其他一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for size (表示這個新的物品，我們決定拿與不拿有可能有哪些組合。)\n內層 for volume (表示我們目前背包的大小)\nReference 背包问题 V · Backpack V ","date":"2022-06-01T18:34:58+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-563/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [563] Backpack V 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 562 · Backpack IV\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: an integer array and all positive numbers, no duplicates @param target: An integer @return: An integer \u0026#34;\u0026#34;\u0026#34; def back_pack_i_v(self, nums: List[int], target: int) -\u0026gt; int: # write your code here if not nums: return 0 dp = [0 for _ in range(target+1)] dp[0] = 1 nums.sort() for num in nums: # take new category by ascending for current_target in range(target+1): last_target_before_take_this_coin = current_target - num if last_target_before_take_this_coin \u0026gt;= 0: dp[current_target] += dp[last_target_before_take_this_coin] return dp[-1] 算法說明 本題是背包系列問題的第 4 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n第四題，我們一樣有無限的物品可以拿，只是這題我們要改求可能的方法總數。\n因為拿的順序會影響結果，例如：「(2,2,3) 與 (3,2,2) 是同一個結果」，\n我們一開始就先 nums.sort()\n外圈 for 一樣的是我們控制目前最大的容量\n內圈 for 我們去控制目前新拿的物品種類\ndp 的運作 初始化一樣 dp[0] = 1\nlast_volume = current_volume - size\n如果 last_volume \u0026gt;= 0， dp[current_volume] += dp[last_volume] # 表示新增的可能性 (因為順序有先 sort 過，剛好會是直接排好的結果)\n例如：\ndp[3] += dp[2] (dp[2] 的所有可能性 + 1 size) dp[3] += dp[1] (dp[1] 的所有可能性 + 2 size) input handling 如果沒有 nums，回傳 0，\n其他一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for volume (表示我們目前背包的大小，最大可能的價值)\n內層 for value (表示新增「一個」新的種類後，保留目前容量可能的所有可能組合)\nReference ","date":"2022-06-01T17:28:05+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-562/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [562] Backpack IV 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 440 · Backpack III\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param a: an integer array @param v: an integer array @param m: An integer @return: an array \u0026#34;\u0026#34;\u0026#34; def back_pack_i_i_i(self, a: List[int], v: List[int], m: int) -\u0026gt; int: # write your code here n = len(a) dp = [0 for _ in range(m+1)] # save max value for current_volume in range(m+1): for i, size in enumerate(a): if current_volume - size \u0026gt;= 0: dp[current_volume] = max(dp[current_volume], dp[current_volume - size] + v[i]) return max(dp) 算法說明 本題是背包系列問題的第 3 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n這題是給我們無限數量的物品可以拿，我們只提供物品的種類。\n因為物品變成可以無限使用，首先我們可想像的第一個問題是，\n我們的 i 比照之前方法辦理的話，會有無限個，\n這時我們該怎麼辦法？\n無限物品數量的思路 最簡單的方法，我們可以把 for 的內外圈對調，就可以輕鬆解決了。\n我們的想法變為「背包目前最大的大小」在外圈，\n「我們看到不同種類的硬幣」在內圈，因為我們可以無限取用。\n拿新物品的前一個體積大小 last_volume = current_volume - size]\n我們比較每次 dp[current_volume] = max(dp[current_volume], dp[last_volume] + value[此物品])，\n只保留當前背包的最大價值。\ninput handling 一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for volume (表示我們目前背包的大小，最大可能的價值)\n內層 for value (表示新增價值後，保留目前容量可能的最大價值)\nReference ","date":"2022-06-01T17:08:25+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-440/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [440] Backpack III 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 125 · Backpack II\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param m: An integer m denotes the size of a backpack @param a: Given n items with size A[i] @param v: Given n items with value V[i] @return: The maximum value \u0026#34;\u0026#34;\u0026#34; def back_pack_i_i(self, m: int, a: List[int], v: List[int]) -\u0026gt; int: # write your code here # dp[i][j] = max price (i this round take, j volume) n = len(a) dp = [[0] * (m+1) for _ in range(n+1)] for i in range(1, n+1): for j in range(1, m+1): dp[i][j] = dp[i-1][j] last_volume = j - a[i-1] if last_volume \u0026gt;= 0: dp[i][j] = max(dp[i][j], dp[i-1][last_volume] + v[i-1]) print(dp[-1]) return max(dp[-1]) 算法說明 本題是背包系列問題的第 2 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n這題比起第一題，我們多考慮物品的價值，第一題只需要考慮 size 即可。\n我們用 dp 保存當下背包的 max value\ninput handling 一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for size (表示我們選擇新的項目)\n內層 for volume (表示紀錄目前已有的選擇下，可否達到裝滿的目標，並記錄最大價值)\nReference ","date":"2022-06-01T16:57:21+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-125/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [125] Backpack II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 92 · Backpack\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param m: An integer m denotes the size of a backpack @param a: Given n items with size A[i] @return: The maximum size \u0026#34;\u0026#34;\u0026#34; def back_pack(self, m: int, a: List[int]) -\u0026gt; int: # write your code here # define: dp[i][j], i = this round take, j = volume # dp[i][j] = dp[i-1][j] # last round take # = dp[i-1][j - A[i-1]] # inherit last take, we take new can succeed (only when j - A[i-1] \u0026gt;= 0) # ans: last True dp[i][volume?] # init: dp[all][0] = True, other False dp = [[False]*(m+1) for _ in range(len(a)+1)] # i = m, j = count nums dp[0][0] = True for i in range(1, len(a)+1): dp[i][0] = True for j in range(1, m + 1): dp[i][j] = dp[i-1][j] if j - a[i-1] \u0026gt;= 0: # can take this dp[i][j] = dp[i][j] or dp[i-1][j - a[i-1]] # print(dp) for i in range(m+1): if dp[-1][m-i]: # check max volume return m-i return -1 # not found 算法說明 本題是背包系列問題的第 1 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\n最經典的背包問題，我們用 dp 去解，\n要理解背包問題，建議自己畫過一次表，會比較容易理解 DP 邏輯運作的奧妙。\n用圖解析 DP 的分析過程 在這題目裡面，因為我們只有單純拿，因此我們不需要注意順序，不用花 O(nlogn) 的時間先 sort()\nstate 分析概念：我們建立一張表 dp[i][j]， i 位置表示我們這回合要處理的新增物品，j 表示背包目前已經有的大小。\ninit 初始化時：dp[i][0], dp[0][j] 都為 True，其他 init 為 False\ndefine dp[i][j] = dp[i-1][j] # default 情況，我們複製上個階段的所有結果。\n特殊情況： last_volume = j - nums[i-1] # 表示如果本次我們選擇拿新東西，是否有可能有這個組合的大小。\ndp[i][j] = dp[i][j] or dp[i-1][last_volume] # 任意 True 即可\nans dp[-1][j] # 從最大直到找到第一個 True，就是結果。\ninput handling 一同在 dp 內處理\nBoundary conditions 用兩層 for 來控制搜尋範圍，\n外層 for size (表示我們選擇新的項目)\n內層 for volume (表示紀錄目前已有的選擇下，可否達到裝滿的目標)\nReference 背包问题 · Backpack ","date":"2022-06-01T16:50:25+08:00","image":"https://wongwongnotes.com/images/restored/2022/06/img_0366.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-92/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [92] Backpack 個人解法筆記 | 內含背包問題模板 #重要題型"},{"categories":["DFS (全部方案)","DP (座標型 - 一維)"],"content":"題目出處 377. Combination Sum IV\n難度 medium\n個人範例程式碼 - 2022/5/30 一刷，DP 另解 本題是 Combinations 系列的第 5 題，前面的題目可以參考：\n第 1 題：不允許重複，給定數字範圍的全部組合，目標是指定組合內固定的數量。\n第 2 題：允許重複，順序不同視為相同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是一個結果」\n第 3 題：允許有限重複(題目指定上限數量)，求全部組合。\n第 4 題：不允許重複，給定數字範圍的全部組合，目標是求指定的和。\n第 5 題：允許重複，但順序不同視為不同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是兩個結果」。(這題已經可以當作排列的題目了。)\n本題也是背包系列問題的第 6 題，建議初學者可以從第一題開始去學：\n第 1 題：最基本的背包問題，不重複 size，物品只有一個，計算組合可能性\n第 2 題：最基本的背包問題，不重複 size，物品只有一個，計算最大價值\n第 3 題：完全背包問題，有重複 size，物品無限數量，計算最大價值\n第 4 題：有重複 size，物品無限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 5 題：有重複 size，物品有限數量，計算可能組合 ，「(1,2,3) 與 (2,3,1) 視為相同答案」\n第 6 題：有重複 size，物品無限數量，計算可能「排列」 ，也就是說「(1,2,3) 與 (2,3,1) 視為不同答案」\n第 7 題：有重複物品，有限制物品數數量上限，問可能組合\n第 8 題：有重複物品，有限制物品數數量上限，問最大價值\n第 9 題：變化問題，有重複 size，計算機率\n第 10 題：變化問題，只是把 size 面額提高\nclass Solution: def combinationSum4(self, nums: List[int], target: int) -\u0026gt; int: if not nums: return 0 nums.sort() dp = [0 for _ in range(target+1)] for each_target in range(1, target+1): for num in nums: if each_target == num: dp[each_target] += 1 if each_target - num \u0026gt;= 0: dp[each_target] += dp[each_target - num] # add new case (with more this num) # 0 1 2 3 4 # 0 0 0 0 0 # 1: 1 # 2: 2 (case 1) # 3: 3 (case 2) (case 1) # 4: (case 3+1) (case 2+2) (case 1+3) return dp[-1] 注意：DFS 時間複雜度為 O(num^target)，DP 為(target^2)\n類似 DP 強項：把 O(2^n) 優化時間為 O(n^2) 的概念。\n算法說明 其實這也是背包問題的一種，只是這類的背包問題「拿背包的順序也會影響到結果」，\n因此，我們把拿背包時，分成「之前case」+ 「現在新拿的 num」，\n於是，我們遇到一個新的數量時，\n只需要去計算所有的「可新拿的一個 num」搭配「扣除此 num 價值後，所有可能的組合」，即為答案。\n範例 以題目的數字為例：\ntarget 為 4，我們宣告一個 dp 從 0 1 2 3 4\n當 target = 0，0 當 target = 1，1 存在 num，0+1 當 target = 2，2 存在 num, 0+1, 再加上「多拿一顆 1 + (dp[1] 的所有組合)」 當 target = 3，3 存在 num, 0+1, 再加上「多拿一顆 1 + (dp[2] 的所有組合)」, 再加上「多拿一顆 2 + (dp[1] 的所有組合)」 當 target = 4，0 再加上「多拿一顆 1 + (dp[3] 的所有組合)」, 再加上「多拿一顆 2 + (dp[2] 的所有組合)」，再加上「多拿一顆 3 + (dp[1] 的所有組合)」 input handling 處理沒有 input nums 的情況，return 0\nBoundary conditions 用 for 來控制範圍\n個人範例程式碼 - 2022/5/30 一刷，DFS + memoization 解 class Solution: def combinationSum4(self, nums: List[int], target: int) -\u0026gt; int: if not nums: return 0 nums.sort() memo = {} return self.dfs(nums, target, [], 0, memo) def dfs(self, nums, target, combination, ans, memo): # end of recursion if target == 0: return 1 if target \u0026lt; 0: return 0 if target in memo: return memo[target] ans = 0 # define and split for num in nums: combination.append(num) cur_ans = self.dfs(nums, target - num, combination, ans, memo) combination.pop(-1) # backtracking memo[target - num] = cur_ans ans += cur_ans return ans 算法說明 純 DFS 會 TLE，會需要 memoization 輔助運算 (純計算數量，因此更容易使用。)\ninput handling 處理沒有 input nums 的情況，return 0\nBoundary conditions dfs 結束迴圈的條件：\nk \u0026lt; 0，回傳 0 個結果 k = 0，回傳 1，找到 1 個結果 memoization 記憶化搜索 透過紀錄 memo[target] 有多少數量，我們可以拿過去計算的經驗告訴我們會有多少結果。\n如果沒有 memoization，純 DFS 會 TLE。\nReference 组合总和 IV · Combination Sum IV ","date":"2022-05-30T18:22:37+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-377/","tags":["DFS","DP"],"title":"【Leetcode】python - [377] Combination Sum IV 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 128. Longest Consecutive Sequence\n難度 medium\n個人範例程式碼 - 2022/5/30 一刷，節省空間另解 class Solution: def longestConsecutive(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 hashset = set(nums) # O(N) current_max = 0 while(hashset): num = hashset.pop() # hashset.remove(num) high, low = num+1, num-1 while high in hashset: hashset.remove(high) high += 1 # end more + 1 while low in hashset: hashset.remove(low) low -= 1 # end more - 1 current_max = max(current_max, high - low - 1) return current_max 算法說明 這邊把下方的第一版空間使用再次簡化，\n我們不用 visited 了！我們打算只使用一個 hashset 搞定全部事情！\n這樣就是考驗對於 set 熟悉的程度了，我們會用到：\nset.remove()：時間 O(1) set.pop()：任意取一個 set 的內容，並刪除，時間 O(1) input handling 處理沒有 input 的情況，return 0\nBoundary conditions 用 while(set) 來檢查 set 裡面還有沒有內容，如果沒有就搜尋完畢。\n(邊搜尋的過程，一邊刪除 set 的內容)\n個人範例程式碼 - 2022/5/30 一刷 class Solution: def longestConsecutive(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 hashset = set(nums) # O(N) visited = set() current_max = 0 for num in nums: # O(N) if num in visited: # O(1) continue cnt = 1 visited.add(num) high, low = num+1, num-1 while high in hashset: visited.add(high) high += 1 cnt += 1 while low in hashset: visited.add(low) low -= 1 cnt += 1 current_max = max(current_max, cnt) return current_max 算法說明 滿特別的一個題目。\n直覺會想用 sort 來解，但題目要求 O(N) 時間內解完，\n因此光是 sort 如果就會花上 O(NlogN) 就會時間超過。\n因此我們採用「用空間來換取時間的方法」，\n我們建立兩個 hashset，一個存 list 內的所有數字，一個存 visited。\n每次當我們搜尋的過程中，就新增一個 visited。\n因此如果像是題目要的搜尋 [100,4,200,1,3,2]\n找到 4 時，會順便把 1, 2, 3 順便都找完，\n而輪到 1, 2, 3 時就會跳過。\ninput handling 處理沒有 input 的情況，return 0\nBoundary conditions 用 for 來控制範圍\nReference Python3 集合 最长连续序列 · Longest Consecutive Sequence ","date":"2022-05-30T04:09:26+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-128/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [128] Longest Consecutive Sequence 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 105. Construct Binary Tree from Preorder and Inorder Traversal\n難度 medium\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) -\u0026gt; Optional[TreeNode]: if not preorder or not inorder: return None # preorder (M) L R # inorder L (M) R # use preorder get first node val = preorder[0] # pre [(3),9,20,15,7], [(20),15,7] root = TreeNode(val) root_idx = inorder.index(val) # in [9,(3),15,20,7], [15,(20),7]] # preorder[1:root_idx+1] del first, inorder[:root_idx]) del idx root.left = self.buildTree(preorder[1:root_idx+1], inorder[0:root_idx]) # preorder[root_idx+1:], inorder[root_idx+1:]) del idx root.right = self.buildTree(preorder[root_idx+1:], inorder[root_idx+1:]) return root 算法說明 看似複雜的問題，其實我們可以從「拆成很多子問題下手」，\n因為就算是子樹，也會用一樣的概念去建構\n有上面的觀念之後，看來就是 recursion 的回合了！\n我們決定好怎麼定義我們的左右子樹，\nroot.left 的部分 我們直接拿題目來分析，\npreorder 我們先拿了第一個，作為我們的 root [(3),9,20,15,7]，\n之後我們要去找，有多少東西在左樹內，\n我們需要去 inorder 找所有 root 左側的內容，\nroot_idx = inorder.index(val) # inorder [9,(3),15,20,7]\n因此我們知道左側的 inorder 範圍：\ninorder[:root_idx])，我們刪除已經被我們當 root 的點，位於 idx\n而左側的 preorder 範圍：\n我們必須刪除第一個點，因為他是我們的 root，剩下的範圍會到 root，\n(取範圍時要注意取到 idx+1，才會是到「包含」 idx 的範圍)，\n因此，preorder 我們取 preorder[1:root_idx+1]\n合併後得到 root.left = self.buildTree(preorder[1:root_idx+1], inorder[0:root_idx])\nroot.right 的部分 右側的部分稍微簡單一點，在上方取得 idx 後，我們後面的範圍都是 right 的範圍了。\n一樣以題目來舉例：\npreorder [(3),9,20,15,7]\ninorder [9,(3),15,20,7]\ninorder idx 3 以後的內容，都會是我們右子樹的內容，包含 preorder 範圍也是如此。\n因此我們得到 root.right = self.buildTree(preorder[root_idx+1:], inorder[root_idx+1:])\ninput handling 處理沒有 input 的情況，return None\n或是處理只有一個值的情況 [-1]，return [-1]\nBoundary conditions 用 not list 來控制範圍，表示已經沒有內容物\nReference 前序遍历和中序遍历树构造二叉树 · Construct Binary Tree from Preorder and Inorder Traversal ","date":"2022-05-30T03:40:04+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-105/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [105] Construct Binary Tree from Preorder and Inorder Traversal 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 73. Set Matrix Zeroes\n難度 medium\n個人範例程式碼 class Solution: def setZeroes(self, matrix: List[List[int]]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify matrix in-place instead. \u0026#34;\u0026#34;\u0026#34; hash_column = set() hash_row = set() for i in range(len(matrix)): for j in range(len(matrix[0])): if matrix[i][j] == 0: hash_column.add(i) hash_row.add(j) for i in list(hash_column): for j in range(len(matrix[0])): matrix[i][j] = 0 for i in range(len(matrix)): for j in list(hash_row): matrix[i][j] = 0 算法說明 用兩組 row, column 的 hashset 去儲存有 0 的 column, row，\n最後再依照 hashset 裡面儲存的內容，更新矩陣內容為 0。\ninput handling 題目沒有特別要求\nBoundary conditions 用兩層 for loop 來控制範圍\nReference 矩阵归零 · Set Matrix Zeroes ","date":"2022-05-29T20:50:12+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-73/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [73] Set Matrix Zeroes 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 48. Rotate Image\n難度 Medium (但我覺得其實很難想XDD)\n題目分類 Array\n特別注意題目說的「inplace」 特別注意題目說的「inplace」，基本上就是希望不要用到額外儲存空間，\n可以當作不能「製造出新的矩陣」，只能單純的「矩陣內位置互換」\n換成程式碼的意思就是：「一個個index去換值」，不要對整個矩陣操作 先講點題外話(喂 這題基本上就是真正實作「圖片的旋轉」了！\n(不能靠 OpenCV 或 Numpy 幫助的那種XDD)\n學起來這題後，我有體會到 OpenCV 中旋轉圖片的函數實作有多偉大與困難XDD\n真心更尊敬 OpenCV 的開發者惹 (人家本來就超強XDD)\n個人範例程式碼 - 2022/5/29 二刷 class Solution: def rotate(self, matrix: List[List[int]]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify matrix in-place instead. \u0026#34;\u0026#34;\u0026#34; m, n = len(matrix), len(matrix[0]) if m != n: return # transpose for i in range(m): for j in range(i+1, n): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] # reverse for i in range(m): matrix[i].reverse() 算法說明 利用「旋轉 90 度 = 先轉置，再單行反轉的概念 」，我們可以推得\n分析範圍，我們只能做成三角，尋找三角的方式 我們可以透過 i+1 來控制三角形的範圍 (也就是 m 的範圍)。\ninput handling 題目預設是沒有，這裡我多處理了一個 m != n 的特殊情況 (題目也不預期會發生)\nBoundary conditions 用兩層 for loop 來控制範圍\n個人範例程式碼 - 2021/4/3 一刷 (方法一) class Solution: def rotate(self, matrix: List[List[int]]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify matrix in-place instead. \u0026#34;\u0026#34;\u0026#34; N = len(matrix) # one side for i in range(N//2 + N%2): for j in range(N//2): tmp = matrix[i][j] # save temporarily matrix[i][j] = matrix[-j+(N-1)][i] # 1 matrix[-j+(N-1)][i] = matrix[-i+(N-1)][-j+(N-1)] # 2 matrix[-i+(N-1)][-j+(N-1)] = matrix[j][-i+(N-1)] # 3 matrix[j][-i+(N-1)] = tmp # 4 真的別小看這只有八行的程式碼\u0026hellip;，實際上寫出來花的時間根本遠遠超過這八行\n解法說明 個人推薦可以先思考轉180度，再來考慮90度 我是認真的XD，\n過來人的經驗，180度要先解出來，90度才是下一個階段的問題。\n直接就硬爆 90度 大魔王連怎麼死的都不知道\n來先解 180度 吧，直接看圖! (觀察只有兩個東西在互換) 重要觀念：i, j 是「變化量 (delta)」，不要用「座標」的概念下去想。\n寫 for 迴圈的時候，就會知道這問題出在哪了! (可以見文章最下面錯誤的部分)\n示意圖：(我們可以發現，這題只有兩個東西在互換) 觀察 x軸, y軸的變化 180度 比較簡單，「x軸」上的變化只會影響在「x軸」，\n等等 90度 就沒有這麼簡單了!!!\n整理一下變換公式： 先有這樣的觀念，我們再來推 90度的答案!\n個人手繪解法筆記 - (方法一比較正常思考一點) 再強調一次重要觀念：i, j 是「變化量 (delta)」，不要用「座標」的概念下去想。\n寫 for 迴圈的時候，就會知道這問題出在哪了! (可以見文章最下面錯誤的部分)\n每次的互換，只同時牽扯到四個東西 示意圖：(看得出來每次互換只扯到四個東西吧?) 我們將每個座標對應的關係，都「仔細地」寫出來! 示意圖：(仔細觀察 i,j 變化量出現的位置，牽扯到 x軸, y軸 值上面的移動。) 列成循環算式，並更正方向錯誤的座標。 如果一個「i,j 變化量」的方向錯誤，\n我們可以視為「加上(N-1)後，減去變化量的值」，\n(可以想想，N-1是最大邊界，不能再更多了，只能再扣掉變化量。)\n示意圖：(整理一下就能夠得到我們最終的循環公式囉!) 另外一個大魔王? 什麼時候才是終止條件? (你以為能無腦全部掃過嗎XDD，那就是轉四次回原狀惹) 為什麼不能夠無腦全部掃過? 這就是另外一個大魔王惹，真的不能無腦全部掃過\n因為全部掃過 = 轉四次 = 變回原狀，根本沒解到題目XDDD\n那要怎麼找到只需要掃一次的東西呢?\n什麼時候才是終止條件? (其實也沒那麼難啦XDD) 因為是四角轉換，我們從最簡單的 2*2 來看，是不是就很簡單了呢?\n也就是說，放大來看後，我們一樣只需要去抓一個角即可。\n示意圖：(四角互換) 先來處理比較容易的 「N = 偶數」邊 偶數沒什麼問題，就是稍微算一下範圍確認 for 要到多少即可。\n記得 range 的範圍，與實際範圍的不同。\n注意: +1, -1 對答案都會有影響XDD 請小心。\n再來處理比較難搞的 「N = 奇數」邊 奇數就比較麻煩一點點，我們先上一下色，\n我們發現 y軸上 的分析，需要比 x軸 少一格，\n不然就會有重複運算的問題。\n稍微注意這點也就沒什麼好怕的了。\n我們奇偶數不分開處理，再更方便一點 我們仔細分析我們剛剛得到的範圍，(當然你要 N = 奇偶數 分開處理一定可以的XD)\n幾乎都出現了 in range (N/2)，只有在奇數的時候多出現一個 in range ((N/2)+1)\n那這個「1」可以怎麼來呢? 就是只有「奇數」才會來嘛!\n所以 「N%2 = 1」 (就會得到我們的奇數囉!!)\n我們將範圍公式都改寫成：「in range (N/2 + N%2)」，這樣就非常漂亮了!\n注意：只有「x軸」 需要這個範圍\n不要太開心連「y軸」一起用下去了XDD，偶數就會錯惹!!!\n(現在你知道這題 +1, -1 的問題真的有夠煩的吧!)\n個人範例程式碼 - 2021/4/3 一刷 (方法二，炫炮解法) class Solution: def rotate(self, matrix: List[List[int]]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify matrix in-place instead. \u0026#34;\u0026#34;\u0026#34; N = len(matrix) # one side # transpose for i in range(N): for j in range(i+1, N): tmp = matrix[i][j] matrix[i][j] = matrix[j][i] matrix[j][i] = tmp # print(f\u0026#34;{matrix}\u0026#34;) #dubug # right and left reverse for i in range(N): for j in range(N//2): tmp = matrix[i][j] matrix[i][j] = matrix[i][-j+(N-1)] matrix[i][-j+(N-1)] = tmp 解法說明 直接看圖吧! 這題我們利用 「轉90度 = 轉置 + 左右翻轉」的概念\n(嗯\u0026hellip; 有夠快的\u0026hellip;，我們剛剛在幹嘛)\n仔細分析題目的能力還是很重要的!!! (不一定是解題，工作上更是常需要!)\n示意圖: (轉90度 = 轉置 + 左右翻轉) 轉置細節：也是不能無聊全部掃過 即使是轉置矩陣，我們也沒辦法無腦掃過XDD，\n我們只能掃「一個對角線內的全部值」，\n我們可以運用「j = i + 1」來達到我們想要的效果!\n左右反轉細節：思考座標點，跟想像的不太一樣 這邊我踩了一個雷，就是我想像中的座標概念，\n與實際上的座標概念是不同的。\n主要原因是：list[list] 會先讀取到第一個是「該行」矩陣，也就是「y軸」\n請務必想清楚這一點，不然就會錯惹QQ (好複雜XDDD)\n觀念錯誤筆記 觀念錯誤的部分：i, j 是「變化量 (delta)」，不要用「座標」的概念下去想。\n寫 for 迴圈的時候，就會知道這問題出在哪了!\n來先解 180度 吧，直接看圖! 從圖中我們大概知道，「x軸上的移動，會影響的只會在x軸」\n(也許聰明的你已經想到了，90度的影響會跑到y軸去！！！會更複雜!)\n所以我們由上圖整理一下我們的x,y軸變化 我們將數值整理一下，\n可以發現x軸上 i + offset(變化量)的時候，\n對應的 180度 x軸上後的變化量 為 i+(N-1)-offset(變化量)\n也就是說\ni + offset -\u0026gt; i+(N-1)-offset\n同理可以推得\nj + offset -\u0026gt; j+(N-1)-offset\n先有這樣的觀念，我們再來推 90度的答案!\n每次的互換，只同時牽扯到四個東西 示意圖：(看得出來每次互換只扯到四個東西吧?) 將它換成 offset 示意圖：(換成 k = offset 之後的圖) 慢慢來，我們把他慢慢的都寫出來 示意圖：(一個個把算式寫出來就對了! 就只有這四行關鍵算式!!!) Reference 旋转图像 · Rotate Image https://ithelp.ithome.com.tw/articles/10237207 https://leetcode.com/problems/rotate-image/solution/ https://leetcode.com/problems/rotate-image/discuss/18888/1-line-in-Python ","date":"2022-05-29T15:59:50+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7790-1024x459.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-48/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [48] Rotate Image 個人解法筆記"},{"categories":["716 - Linked List","Two pointers (同向雙指針 →→) / slow, fast (快慢雙指針)"],"content":"題目出處 19. Remove Nth Node From End of List\n難度 Medium\n題目分類 LinkedList, TwoPointers\n個人範例程式碼 - 2022/5/29 二刷 # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def removeNthFromEnd(self, head: Optional[ListNode], n: int) -\u0026gt; Optional[ListNode]: if not head: return head dummy = ListNode(0) dummy.next = head slow = fast = dummy for i in range(n): fast = fast.next while(fast.next): fast = fast.next slow = slow.next else: slow.next = slow.next.next return dummy.next 算法說明 使用快慢指針，快慢的間距始終保持 n，\n最後當找不到 fast.next (為 None，即最後一個數字時)\n做一次「slow.next = slow.next.next」，即可刪除該數字，得到我們要的結果。\ninput handling 這題 input 有 cornet case 要處理，例如 [1], n = 1 的情況。\n因此我們使用 dummy = ListNode(0)，並以 dummy.next 作為我們最終的解答。\n使用 dummy 的目的，會使得 head 本身是可以刪除的。\nBoundary conditions 使用 fast 來控制我們搜尋的範圍。\n個人解法筆記 (解法重點) - 2021/6/19 一刷 大概念 - TwoPointers 我們可以想像成有兩個指標，一個跑比較快，一個跑比較慢\n跑比較快的幫我們去找 head 跑比較慢的幫我們定位要刪除的 node 所以跑比較快的與跑比較慢的會固定間距「n」\n正常 case 遍歷停止條件 我們留意最後一個 front 停下來的地方，因為要固定間距 n，\n所以當最後搜尋到 front.next 沒有東西時就應該要停下\n刪除方法 而刪除的方法，則是靠 back 所在位置的 next 替換成 next.next\n你可能會想問為什麼不能在 4 ? ，因為「只有 3 的 next 才能連接到 5」\n特殊 case 只有一個值時，front 會跑到空值，所以我們判斷 front 為空，\n則回傳 head.next\n示意圖 個人範例程式碼 class Solution: def removeNthFromEnd(self, head: ListNode, n: int) -\u0026gt; ListNode: back = front = head for _ in range(n): front = front.next if not front: return head.next while(front.next): front, back = front.next, back.next # print(back.val) back.next = back.next.next return head Reference 删除链表中倒数第n个节点 · Remove Nth Node From End of List https://leetcode.com/problems/remove-nth-node-from-end-of-list/discuss/8802/3-short-Python-solutions ","date":"2022-05-29T01:45:09+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/img_9393-621x1024.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-19/","tags":[],"title":"【Leetcode】python - [19] Remove Nth Node From End of List 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 222. Count Complete Tree Nodes\n難度 medium\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def countNodes(self, root: Optional[TreeNode]) -\u0026gt; int: if not root: return 0 left_depth = self.get_depth(root.left) right_depth = self.get_depth(root.right) if left_depth == right_depth: # (left completed) return 1 + (2**left_depth - 1) + self.countNodes(root.right) else: # left_depth \u0026gt; right_depth (right completed) return 1 + (2**right_depth - 1) + self.countNodes(root.left) def get_depth(self, root): if not root: return 0 return 1 + self.get_depth(root.left) 算法說明 拆成兩路進行，兩路我們都直接找「最左下」，比較兩者的深度，此時會有兩種情況\n當左 右，表示左邊一定是 completed binary tree 當左 \u0026gt; 右，表示右邊一定是 completed binary tree 你可能想問，那「左 \u0026lt; 右」?\n不可能，你再想想 completed Tree 的定義XD\n因此，我們簡略圖示一下，當「左 右」，左邊一定是 completed\n當「左 \u0026lt; 右」，右邊一定是 completed\n我們可以搭配 「2**depth -1」快速計算出 completed tree 的總 node 數。\ninput handling 處理沒有 root 的情況，return 0\nBoundary conditions 用「not root」去控制 traverse tree 的結束。\nReference 统计完全树节点数 · Count Complete Tree Nodes ","date":"2022-05-28T20:50:20+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0363.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-222/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [222] Count Complete Tree Nodes 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 205. Isomorphic Strings\n難度 easy\n個人範例程式碼 class Solution: def isIsomorphic(self, s: str, t: str) -\u0026gt; bool: if not s or not t: return False if len(s) != len(t): return False s_to_t = {} t_to_s = {} for i in range(len(s)): if s[i] in s_to_t and t[i] in t_to_s: if s_to_t[s[i]] \u0026lt;mark\u0026gt; t[i] and t_to_s[t[i]] \u0026lt;/mark\u0026gt; s[i]: continue else: return False else: if s[i] in s_to_t: # one side alraedy exist return False if t[i] in t_to_s: return False s_to_t[s[i]] = t[i] t_to_s[t[i]] = s[i] return True 算法說明 注意：來回雙向都要配對\n這題要注意的點就是「雙向配對」，因為如果只有單向配對時，有可能出現 \u0026ldquo;abcd\u0026rdquo; 也能 matching \u0026ldquo;ssss\u0026rdquo; 的情況。\n(因為 hashmap 中存的東西，不論 a,b,c,d 都對應到 s)\ninput handling 處理 len(s) != len(t) 的情況，處理 s 或 t 不存在值的情況。\nBoundary conditions 用 for 來控制範圍\nReference 字符同构 · Isomorphic Strings ","date":"2022-05-28T19:43:42+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-205/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [205] Isomorphic Strings 個人解法筆記"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 由於最近 G suite 無限空間的方案即將結束了，\n為了把放在學校雲端的資料備份下來，\n除了自己買了 google 雲端空間來用之外，\n現在也另外買了一個 14TB 大空間可以冷儲存的硬諜來用。\n我自己事前查到的評價 大概看了一下巴哈，沒什麼大災情，\n除了之前好像有病毒是針對這個的之外，\n因為我自己是把資料冷儲存，好像也不太需要擔心這個。\n(平常也沒有接在電腦上了。)\n購入價 我是在 pchome 買的，配合活動 83折 總價 8099 元，還另外送行動電源，\n14 TB 總價 8099 元，1 TB 約 578.5 元，\n比較現在市面上普遍 1 TB 超過 600 元的價格，非常的划算。\n至於空間真的買有點大了，但想說一個可以用很久，所以就不如一次買大一點吧。\n開箱 △ 期待已久的包裹，終於到了!\n△ 打開後的內容物\n△ 14TB的硬碟 與 贈送的行動電源\nWD - My Book 14TB 3.5吋外接硬碟 △ 商品外觀\n△ 打開包裝後的樣子\n△ 全部的內容物\n△ 側拍，鋼琴烤漆很美，表面可以反射\n△ 另外一面的側拍，鋼琴烤漆過的表面真的很漂亮!\n△ 商品裡面有提供萬國插座，在各國都能直接使用，很方便!\n△ 為了保持傳輸穩定，我把傳輸線從後面往前接\n△ 接上電腦顯示了容量，因為進位方式不同，我們大約有 12.7 TB 可以使用\n贈品 - 行動電源 △ 畢竟是贈品，簡單紀錄一下\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n基本上就是做為我保存資料使用，\n目前覺得比較大的缺點是，硬碟運轉時的震動很強，\n大概就是那種放地板，都會怕震動吵到樓下的那種感覺 (嗯嗯嗯\u0026hellip;的那種)，\n雖然不是聲音的影響，但震動影響滿因人而異的，我自己是會再多墊個地毯減震。\n","date":"2022-05-27T19:35:43+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/WDmybook_4-scaled.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/wd-my-book-14tb/","tags":[],"title":"【嗡嗡開箱 #7】WD - My Book 14TB 3.5吋外接硬碟 | 不專業開箱文"},{"categories":["710 - Python LeetCode"],"content":"題目出處 45. Jump Game II\n難度 Medium\n個人範例程式碼 class Solution: def jump(self, nums: List[int]) -\u0026gt; int: # record min jump, init dp = [float(\u0026#34;inf\u0026#34;)] * len(nums) dp[0] = 0 # start # function for i in range(len(nums)): for j in range(i+1, i+1 + nums[i]): # 1:3 = 12, 1:1 = 0 if j \u0026lt; len(nums): dp[j] = min(dp[j], dp[i]+1) # jump+1 from dp[i] else: # over range break # print(dp) return dp[-1] # 2 3 1 1 4 # 1 inf inf inf inf # 1 1 1 inf inf # 1 1 1 2 2 算法說明 此題的前一題為不需要算步數的版本，只需要知道可不可抵達，可參考 (啊怎麼第二題的題號還比較前面?) ： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-55/\n計算步數，我們一樣使用 DP，「紀錄最小抵達該樓層時使用步數」\nfunction: dp[i] = min(dp[i], dp[j] + 1) 我們要比較的是，第 j 樓可以一步到此(+1) 的步數，是否比原來紀錄的可能步數還要少。\ninit: dp[0] = 1, 其他初始化 float(\u0026ldquo;inf\u0026rdquo;) (會被比較小的步數取代) ans: dp[-1] DP 變化如下 # 2 3 1 1 4 # 0 inf inf inf inf (init) # 0 1 1 inf inf (1F) # 0 1 1 2 2 (2F) 優化 這裡可以做一些小優化，\n因為到接近終點的樓層時， i + jump 會超過終點很多，\n此時我們可以「提早先 break 迴圈」 (因為超過終點的範圍都不重要)\ninput handling 一同在 for 裡面處理掉\nBoundary conditions 用 for 來控制 DP 的範圍\nReference 跳跃游戏 II · Jump Game II ","date":"2022-05-23T14:38:16+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-45/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [45] Jump Game II 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 63. Unique Paths II\n難度 Medium\n個人範例程式碼 class Solution: def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -\u0026gt; int: m, n = len(obstacleGrid), len(obstacleGrid[0]) if not m or not n: return 0 dp = [[0 for _ in range(n)] for _ in range(m)] # init if obstacleGrid[0][0] == 1: return 0 else: dp[0][0] = 1 for i in range(1, m): if obstacleGrid[i][0] == 1: dp[i][0] = 0 else: dp[i][0] = dp[i-1][0] for j in range(1, n): if obstacleGrid[0][j] == 1: dp[0][j] = 0 else: dp[0][j] = dp[0][j-1] # function for i in range(1, m): for j in range(1, n): if obstacleGrid[i][j] == 1: dp[i][j] = 0 else: dp[i][j] = dp[i-1][j] + dp[i][j-1] # print(dp) # ans return dp[-1][-1] 算法說明 此題的前一題為無障礙物版本，可以參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-62/\n也是經典的二維 DP 問題，\n透過建立下方的圖片，就可以求得所有路徑的答案\n(DP 記錄到達那一格的所有方法數)\n而這一題我們只需要多做處理障礙物的部分，但這次我們需要特別小心，\n除了中間之外，連「邊邊初始化也要處理」\n處理中間 邊邊初始化 (要多注意) input handling 如果沒有 m 或 n，return 0 (無方法)\n且如果 A[0][0] 位置就是障礙物，也是 return 0 (無方法)\nBoundary conditions 用 for 來控制 DP 的範圍\nReference 不同的路径 II · Unique Paths II ","date":"2022-05-23T14:01:17+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0361.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-63/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [63] Unique Paths II 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 62. Unique Paths\n難度 Medium\n個人範例程式碼 class Solution: def uniquePaths(self, m: int, n: int) -\u0026gt; int: if not m or not n: return 0 dp = [[1 for _ in range(n)] for _ in range(m)] for i in range(1, m): for j in range(1, n): dp[i][j] = dp[i][j-1] + dp[i-1][j] return dp[-1][-1] 算法說明 也是經典的二維 DP 問題，\n透過建立下方的圖片，就可以求得所有路徑的答案\n(DP 記錄到達那一格的所有方法數)\ninput handling 如果沒有 m 或 n，return 0 (無方法)\nBoundary conditions 用 for 來控制 DP 的範圍\nReference ","date":"2022-05-23T13:56:05+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0359.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-62/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [62] Unique Paths 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 518. Coin Change 2\n難度 Medium\n個人範例程式碼 class Solution: def change(self, amount: int, coins: List[int]) -\u0026gt; int: if not coins: return 0 dp = [0] * (amount + 1) # init dp[0] = 1 for coin in coins: for i in range(coin, amount + 1): # start from new coin dp[i] += dp[i - coin] # function return dp[-1] # answer 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 此題的前一題為限制使用金幣數量的版本，這題的金幣數量是無限制的。可參考： https://wongwongnotes.com/posts/algorithm-practice/dynamic-programming/761-classic-dp/leetcode-python-322/\n這題要處理可以無限使用硬幣，會稍微複雜一些\n我們先假設有新的硬幣種類，是一個個出現，從小一路分析到大，\nDP 會變成以下的樣子：\n# 0 1 2 3 4 5 # 1 0 0 0 0 0 (init) # 1 1 1 1 1 1 (1) # 1 1 2 2 3 3 (2) # 1 1 2 2 3 4 (5) 我們可以推得公式：\ninit: 全部 = 0，dp[0] = 1 function: dp[i] += dp[i-coin] (if i-coin \u0026gt;= 0) answer: dp[-1] 常見錯誤 - 處理重複時出錯 這題比較麻煩的是，我們有可能會碰到處理重複時出錯的問題，\n例如: (1,2,1) 與 (1,1,2)，其實兩者是同一個意思\n因此我們才需要假設「新種類的硬幣，是一個個的出現」。\n錯誤程式碼範例： 下面的程式碼，因為想要「一次處理好」不同數值下，可以湊出的硬幣總數，\n因此碰到了上述所說 (1,2,1) 與 (1,1,2)，其實兩者是同一個意思的問題。\ndp = [0 for _ in range(amount+1)] for i in range(amount+1): for coin in coins: if i == coin: dp[i] += 1 if i - coin \u0026gt;= 0: dp[i] += dp[i - coin] return dp[-1] input handling 如果沒有 coins，return 0，(無組合)\nBoundary conditions 用 for 來控制 DP 的範圍\nReference 零钱兑换 2 · Coin Change 2 ","date":"2022-05-23T13:25:06+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-518/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [518] Coin Change 2 個人解法筆記 #重要題型"},{"categories":["DFS (組合 combination)"],"content":"題目出處 78. Subsets\n難度 Medium\n個人範例程式碼 class Solution: def subsets(self, nums: List[int]) -\u0026gt; List[List[int]]: if not nums: return [[]] ans = [] self.dfs(nums, 0, [], ans) # print(ans) return ans # [] # [1] [12] [123] # [13] # [2] [23] # [3] def dfs(self, nums, start, combination, ans): ans.append(combination[:]) # deepcopy # end of recursion # define and split (use or not use) for i in range(start, len(nums)): combination.append(nums[i]) self.dfs(nums, i+1, combination, ans) combination.pop() # backtracking 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 經典 DFS 的組合問題，思考方式如下：\n每一層我們都可以決定「取特定數字或不取」，但取或不取，順序都要一直往後。\ninput handling 如果沒有 nums，return [[]]\nBoundary conditions 用 dfs 內的 for loop 控制搜尋範圍\nReference 子集 · Subsets ","date":"2022-05-23T13:09:00+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0358.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-78/","tags":["DFS"],"title":"【Leetcode】python - [78] Subsets 個人解法筆記 #重要題型"},{"categories":["DFS (組合 combination)"],"content":"題目出處 90. Subsets II\n難度 Medium\n個人範例程式碼 class Solution: def subsetsWithDup(self, nums: List[int]) -\u0026gt; List[List[int]]: if not nums: return [[]] nums.sort() ans = [] self.dfs(nums, 0, [], ans) return ans def dfs(self, nums, start, combination, ans): ans.append(combination[:]) # deepcopy # end of recursion, define ans split for i in range(start, len(nums)): if i \u0026gt; start and nums[i] == nums[i-1]: # only take first duplicate continue combination.append(nums[i]) self.dfs(nums, i+1, combination, ans) combination.pop() 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 此題的前一題為無重複版本，這題要多處理重複，可參考： https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-78/\n經典 DFS 的組合問題， 這題要多處理重複的部分，\n我們先 sort() 確保有一致的順序，\n我們利用 i \u0026gt; start 來確保跟開始項目相同時，第一組重複有被計算到\n注意：不是 i \u0026gt; 0，而是 i \u0026gt; start\nstart 位置就是代表重複字母的第一個字，除了第一次判斷會拿之外，後面都不會再拿。\nif i \u0026gt; start and nums[i] == nums[i-1]: # only take first duplicate continue input handling 如果沒有 nums，return [[]]\nBoundary conditions 用 dfs 內的 for loop 控制搜尋範圍\nReference 子集 II · Subsets II ","date":"2022-05-23T13:01:52+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-90/","tags":["DFS"],"title":"【Leetcode】python - [90] Subsets II 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 300. Longest Increasing Subsequence\n難度 Medium\n個人範例程式碼 class Solution: def lengthOfLIS(self, nums: List[int]) -\u0026gt; int: # 1 3 5 4 7 # 1 1 1 1 1 # 1 2 3 3 4 dp = [1 for _ in range(len(nums))] for i in range(1, len(nums)): for j in range(i): if nums[j] \u0026lt; nums[i]: dp[i] = max(dp[i], dp[j]+1) return max(dp) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 經典題目 LIS，使用 DP 解題\n# 1 3 5 4 7 # 1 1 1 1 1 # 1 2 3 3 4 init: dp 都 1 function: dp[i] = max(dp[i], dp[j]+1) if 0 \u0026lt;= j \u0026lt; i ans: max(dp) input handling 同 DP 內控制範圍\nBoundary conditions 用 for loop 控制搜尋範圍\nReference ","date":"2022-05-23T03:07:54+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-300/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [300] Longest Increasing Subsequence 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 332. Reconstruct Itinerary\n難度 Hard\n個人範例程式碼 class Solution: def findItinerary(self, tickets: List[List[str]]) -\u0026gt; List[str]: graph = self.build_graph(tickets) self.ans = [] self.dfs(graph, \u0026#39;JFK\u0026#39;) return self.ans[::-1] def build_graph(self, tickets): graph = collections.defaultdict(list) for _from, _to in tickets: graph[_from].append(_to) for _from in graph: graph[_from].sort(reverse = True) # dfs get reverse lexical order (since we get ans from end to start) return graph def dfs(self, graph, _from): # end of recursion, when find no route while graph[_from]: # define and split self.dfs(graph, graph[_from].pop()) # append ans, from end to start self.ans.append(_from) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題要同時考到建構出一個 graph，\n並 dfs 找尋路徑。\n(這題還沒有靠自己完全寫出來，目前先參考下方 reference 進行仿寫)\n反向建立 _to 順序，正向 dfs 搜尋 因為題目希望最後我們能夠照字母大小排序，而我們建立 graph 時，\n先把「名稱排序比較後面的，放到前面順位」，\n這樣「dfs 搜尋時，就會先 pop()」，\n而最後「我們在把 from end to start 的路徑倒過來」。\ninput handling x\nBoundary conditions 在 graph 與 bfs 中控制搜尋範圍，搜尋完全部 node 就正常結束。\n(沒有全部搜尋完，就沒有結果)\nReference 重新安排行程 · Reconstruct Itinerary ","date":"2022-05-23T03:00:14+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-332/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [332] Reconstruct Itinerary 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 892 · Alien Dictionary\n難度 Hard\n個人範例程式碼 import heapq from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param words: a list of words @return: a string which is correct order \u0026#34;\u0026#34;\u0026#34; def alien_order(self, words: List[str]) -\u0026gt; str: # Write your code here graph = self.build_graph(words) if not graph: return \u0026#34;\u0026#34; return self.topological_sort(graph) def build_graph(self, words): graph = {} # node: neighbors # init graph for word in words: for c in word: if c not in graph: graph[c] = set() # add edges n = len(words) for i in range(n-1): # compare i, i+1 max_compare_length = min(len(words[i]), len(words[i+1])) for j in range(max_compare_length): if words[i][j] != words[i+1][j]: # compare two words, if different [i]-\u0026gt;[i+1] graph[words[i][j]].add(words[i+1][j]) break # finish add edge if j == max_compare_length - 1: # already last word, but edge not found # case: err, errt (next must be longer) if len(words[i]) \u0026gt; len(words[i+1]): return None # error case, early return return graph def topological_sort(self, graph): # init indegree, all node = 0 indegree = { node: 0 for node in graph } # calculate indegree for node in graph: for neighbor in graph[node]: indegree[neighbor] = indegree[neighbor] + 1 # find 0 indegree heap = [node for node in graph if indegree[node] == 0] heapq.heapify(heap) # smallest in lexicographical order` # bfs - topological sorting ans_order = \u0026#34;\u0026#34; while heap: node = heapq.heappop(heap) ans_order += node for neighbor in graph[node]: indegree[neighbor] -= 1 if indegree[neighbor] == 0: heapq.heappush(heap, neighbor) # if all nodes popped if len(ans_order) == len(graph): return ans_order return \u0026#34;\u0026#34; 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題要同時考到建構出一個 graph，\n並還需要做 topological sorting (BFS) 找出路徑。\n(這題還沒有靠自己完全寫出來，目前先參考下方 reference 進行仿寫)\ninput handling 建構 graph 時，注意有沒有例外狀況\n例如：沒有找到任何的不同，且 length word[i] \u0026gt; word[i+1]\n(errt, err) 此類順序是有問題的\nBoundary conditions 在 graph 與 bfs 中控制搜尋範圍，搜尋完全部 node 就正常結束。\n(沒有全部搜尋完，就沒有結果)\nReference 外星人字典 · Alien Dictionary ","date":"2022-05-23T02:54:58+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-892/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [892] Alien Dictionary 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 674. Longest Continuous Increasing Subsequence\n難度 easy\n個人範例程式碼 - 單純走過 class Solution: def findLengthOfLCIS(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 ans = 0 cnt = 1 for i in range(len(nums)): if i \u0026gt; 0 and nums[i] \u0026gt; nums[i-1]: cnt += 1 else: ans = max(ans, cnt) cnt = 1 ans = max(ans, cnt) return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 單純走過去，邊走邊看內容，並更新最大。\ninput handling 如果沒有 nums，回傳 0 (沒數字也不能數)\nBoundary conditions 用 for 控制範圍\n個人範例程式碼 - stack class Solution: def findLengthOfLCIS(self, nums: List[int]) -\u0026gt; int: ans = 0 stack = [] for i, num in enumerate(nums): if stack: if num \u0026gt; stack[-1]: stack.append(num) else: ans = max(len(stack), ans) stack = [num] else: stack = [num] ans = max(len(stack), ans) return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題如果用 stack ，其實還能像上面的方法一樣再更簡化，\n不過其實 stack 的運用空間已經很小了，其實以結果來講算還可以。\ninput handling 如果沒有 nums，回傳 0 (沒數字也不能數)\nBoundary conditions 用 for 控制範圍\nReference 最长上升连续子序列 · Longest Continuous Increasing Subsequence ","date":"2022-05-22T23:29:58+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-674/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [674] Longest Continuous Increasing Subsequence 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 309. Best Time to Buy and Sell Stock with Cooldown\n難度 medium\n個人範例程式碼 class Solution: def maxProfit(self, prices: List[int]) -\u0026gt; int: if len(prices) \u0026lt;= 1: return 0 buy = [0 for _ in range(len(prices))] # max profit, if buy today or not sell = [0 for _ in range(len(prices))] # max profit, if sell today or not cooldown = [0 for _ in range(len(prices))] # = sell[i-1] buy[0] = -prices[0] # init for i in range(1, len(prices)): cooldown[i] = sell[i-1] # if sell yesterday(or before) is max, or sell today will max sell[i] = max(sell[i-1], buy[i-1]+prices[i]) # if buy yesterday(or before), or buy today will max # sell \u0026amp; cooldown both keep current max profit buy[i] = max(buy[i-1], cooldown[i-1]-prices[i]) return max(cooldown[-1], sell[-1]) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 要考慮滿多東西的 DP 問題，算是買賣股票系列問題中的複雜版\n我們先定義主要的 DP buy: 代表至當日若有任何買入，可獲得的最大 profit (可能是在 今日 或 n天前 買入) sell: 代表至當日若有任何賣出，可獲得的最大 profit (可能是在 今日 或 n天前 賣出) cooldown: 固定等於 sell[i-1]，是紀錄 sell 的 cooldown，供 buy 時參考 化成公式：\n都有可能是今日不採取任何策略才是最好的情況，因此都會需要與昨日比較。\nbuy[i] = max(buy[i-1], cooldown[i-1]-prices[i]) cooldown[i-1]-prices[i] 代表我們拿 sell 最大 profit，並在今日買入會更大\nsell[i] = max(sell[i-1], buy[i-1] - prices[i]) buy[i-1] - prices[i] 代表，在前 n 日買入時，在今日如果賣出，會得到更好的收益\ncooldown[i] = sell[i-1] 這邊我們要注意的事情是，\n就算 sell 在當日選擇了賣出的策略較好，\n但 buy 仍會記錄最好的買入價。\n因此不會因為「今日先選擇了 sell 策略」，明日「只剩下 buy 的選擇」\n例子 例如: 1,2,3\n分析到 2 時，也許 sell 在當日是最好的 sell[2] 策略，\n但其實 sell 在 3 才是最好的，這個訊息被記錄在 buy[2] 裡面，\n我們可以在 sell[3] 知道從 buy[2] 獲得最大利益。\n初始化 buy[0] = -prices[0] sell[0] = 0 cooldown[0] = 0 input handling 如果 len(prices) \u0026lt;= 1，return 0\n只有小於一日，買入也是賠錢。\nBoundary conditions 用 for 來控制搜尋範圍\nReference 带有冷却时间的买卖股票最好时间 · Best Time to Buy and Sell Stock with Cooldown ","date":"2022-05-22T18:42:12+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0357.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-309/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [309] Best Time to Buy and Sell Stock with Cooldown 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 509. Fibonacci Number\n難度 Easy\n個人範例程式碼 class Solution: def fib(self, n: int) -\u0026gt; int: dp = [0 for _ in range(n+1)] # define for i in range(n+1): # init if i == 0: dp[i] = 0 elif i == 1: dp[i] = 1 else: # define dp[i] = dp[i-1] + dp[i-2] return dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 基本的經典費波納契數列問題。\n主定義: dp[i] = dp[i-1] + dp[i-2] 初始化: dp[0] = 0 dp[1] = 1 input handling 都在 for 裡面處理\nBoundary conditions for 來控制範圍，「注意當題目問 n 時，我們需要宣告 n + 1」\n例：要求 dp(2)， 共需要 0, 1, 2，三個位置。\nReference ","date":"2022-05-22T00:45:37+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-509/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [509] Fibonacci Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 746. Min Cost Climbing Stairs\n難度 Easy\n個人範例程式碼 class Solution: def minCostClimbingStairs(self, cost: List[int]) -\u0026gt; int: if not cost: return 0 dp = [] for i in range(len(cost)+1): if i \u0026lt;= 1: dp.append(0) else: dp.append(min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])) return dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 算是陽春的 DP 問題，\n主要目標：dp[i] = min(dp[i-2]+A[i-2], dp[i-1]+A[i-1]) 初始目標: dp[0] = 0 dp[1] = 0 最後記得要「多算一個欄位」，代表 「 top 」。\ninput handling 如果沒有任何 cost，return 0\nBoundary conditions for 來控制範圍\nReference 最少费用的爬台阶方法 · Min Cost Climbing Stairs ","date":"2022-05-21T20:13:23+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0355.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-746/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [746] Min Cost Climbing Stairs 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 198. House Robber\n難度 Medium\n個人範例程式碼 class Solution: def rob(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 dp = [] for i in range(len(nums)): if i == 0: dp.append(nums[0]) elif i == 1: dp.append(max(nums[0], nums[1])) else: dp.append(max(dp[i-1], dp[i-2] + nums[i])) # print(dp) return dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 算是陽春的 DP 問題，\n主要目標：dp[i] = max(dp[i-2]+A[i], dp[i-1]) 初始目標: dp[0] = A[0] dp[1] = max(A[0], A[1]) input handling 處理 input 沒有房子與 input 只有一棟房子的情況\n沒有房子，return 0 只有一棟房子，return 那一棟房子 Boundary conditions for 來控制範圍\nReference 打劫房屋 · House Robber ","date":"2022-05-21T20:07:46+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-198/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [198] House Robber 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 213. House Robber II\n難度 Medium\n個人範例程式碼 class Solution: def rob(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 if len(nums) == 1: return nums[0] dp_no_first = [0, nums[1]] dp_contains_first = [nums[0], max(nums[0], nums[1])] return max(self.get_max_rob(nums, dp_no_first), self.get_max_rob(nums[:-1], dp_contains_first)) def get_max_rob(self, nums, dp): for i in range(2, len(nums)): dp.append(max(dp[i-2] + nums[i], dp[i-1])) return dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題的前一題為沒有循環房子的版本，可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-198/\n相比上一題，這一題因為房子的排列會變成一個圈，因此我們把頭尾的關係分為兩段來考慮，\n不包含 0，一路算到 n 包含 0，一路算到 n-1 input handling 處理 input 沒有房子與 input 只有一棟房子的情況\n沒有房子，return 0 只有一棟房子，return 那一棟房子 Boundary conditions for 來控制範圍\nReference 打劫房屋 II · House Robber II ","date":"2022-05-21T20:02:21+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0356.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-213/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [213] House Robber II 個人解法筆記"},{"categories":["198 - Python 問題解決"],"content":"前言 這是我在寫 leetcode debug 時發生的問題\nTypeError: \u0026#39;int\u0026#39; object is not subscriptable 解決方法 這個問題的發生原因，是因為我們嘗試將 int 的 object 對其進行取 index 的操作，\n問題舉例 舉個我發生問題的例子：\nedges = [[0, 1], [1, 2]] for i, edge in enuemrtate(edges): print(edge[i][0]) 可以先思考一下我上面的程式碼有什麼問題? 再來看解答哦!!\n公布上方問題的解答 對於每一個元素，其實我們要用 edges[index] 去取他，\nedge 本身就已經等於 [0, 1] 了\n如果取 edge[0][0] = 0[0] ，當然電腦也不知道該回傳什麼給你了!\n基本上這類問題小心就好，這邊其實就是因為 edge 與 edges 兩個變數太像了，才容易發生這樣的問題。\nReference python报错：TypeError: \u0026lsquo;int\u0026rsquo; object is not subscriptable ","date":"2022-05-19T18:31:58+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/int-object-is-not-subscriptable/","tags":["Python"],"title":"【Python】問題解決：TypeError: 'int' object is not subscriptable"},{"categories":["710 - Python LeetCode"],"content":"題目出處 435. Non-overlapping Intervals\n難度 Medium\n個人範例程式碼 class Solution: def eraseOverlapIntervals(self, intervals: List[List[int]]) -\u0026gt; int: intervals.sort(key = lambda x: x[1]) cnt = 0 end = float(\u0026#34;-inf\u0026#34;) for i, interval in enumerate(intervals): if interval[0] \u0026gt;= end: end = interval[1] # update end else: cnt += 1 return cnt 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題我們使用的是 greedy 的作法，\n我們先依照 end 來排序，\n依據 greedy 的策略，我們把初始 end 設定為 -inf，\n正常情況：如果每一次 start \u0026gt;= 前一個 end (表示無重疊)，且因為我們已經先將 end 排序，因此可以視為得到所有以 end 開頭的區間。 異常情況：start 比前一個 end 小，表示重疊了，此時我們 ans += 1，然後 continue 不理這一組 (視為會被刪掉的組合) input handling 一同在 for loop 內處理。\nBoundary conditions 用 for loop 控制範圍\nReference 无重叠区间 · Non-overlapping Intervals ","date":"2022-05-19T16:01:48+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0354.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-435/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [435] Non-overlapping Intervals 個人解法筆記"},{"categories":["DP (座標型 - 二維)"],"content":"題目出處 322. Coin Change\n難度 Medium\n個人範例程式碼 class Solution: def coinChange(self, coins: List[int], amount: int) -\u0026gt; int: if not coins: return -1 dp = [float(\u0026#34;inf\u0026#34;) for _ in range(amount+1)] dp[0] = 0 for i in range(1, amount+1): for coin in coins: if i \u0026gt;= coin and dp[i-coin] != float(\u0026#34;inf\u0026#34;): dp[i] = min(dp[i], dp[i-coin]+1) # last dp add this coin return -1 if dp[-1] == float(\u0026#34;inf\u0026#34;) else dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 完全背包問題可以把公式解開\n初始化，全部 INF，多留一個欄位\ndp[0] = 0 dp[i] = INF (無解的情況，保持原樣) dp[i] = min(dp[i], dp[i-可行的目標]+1) (+1 就是代表替換為此目標，例如可以用5元「換一次 [i-5]+1 個五元」的結果) input handling 如果沒有 coins，return -1，\n最後 dp[-1] 即為答案 (如果是保持 INF 不變，那就回傳 -1)\nBoundary conditions 用 for 來控制 DP 的範圍\nReference 换硬币 · Coin Change ","date":"2022-05-18T02:28:31+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/dynamic-programming/761-classic-dp/leetcode-python-322/","tags":["DP"],"title":"【Leetcode】python - [322] Coin Change 個人解法筆記 | 內含 DP 背包問題模板 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2274. Maximum Consecutive Floors Without Special Floors\n難度 medium\n個人範例程式碼 class Solution: def maxConsecutive(self, bottom: int, top: int, special: List[int]) -\u0026gt; int: if bottom \u0026gt;= top: return 0 special.sort() max_floor = max(top - special[-1], special[0] - bottom) for i, s in enumerate(special): if i \u0026gt; 0: max_floor = max(special[i] - special[i-1] - 1 , max_floor) return max_floor 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題有點像是 interval 的變化版，\n幸好題目幫我們把限制設定好 (bottom \u0026lt; special floors \u0026lt; top)，\n不然處理上會有更多細節要注意。\ninput handling 如果 bottom \u0026gt;= top，return 0 (錯誤的 case)\n一開始先處理 top 跟 sorting 後的 special[-1]，差了多少 (可能為 0)，\n以及 bottom 與 sorting 後的 special[0]，差了多少 (可能為 0)。\n這邊真的要幸好題目設定好這些限制，不然處理上真的會麻煩很多\u0026hellip;\n再來做中間層的處理。\nBoundary conditions for loop 控制範圍\nReference [Java/C++/Python] Sort ","date":"2022-05-18T01:14:33+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2274/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2274] Maximum Consecutive Floors Without Special Floors 個人解法筆記 | 293rd LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2273. Find Resultant Array After Removing Anagrams\n難度 easy\n個人範例程式碼 class Solution: def removeAnagrams(self, words: List[str]) -\u0026gt; List[str]: if not words: return words i = 0 last_word = \u0026#34;\u0026#34; while(i \u0026lt; len(words)): sort_word = \u0026#34;\u0026#34;.join(sorted(words[i])) if sort_word == last_word: words.pop(i) # remove, and keep i else: last_word = sort_word i += 1 # go next return words 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 讓我們先來理解題目，題目的意思簡單說就是，\n我們將每個字都取 counter，如果前後 counter 相同，那我們就去把後面的刪除，\n這邊要「特別注意」，題目只有說「前後」，因此「相隔很多的不用刪」，\ninput handling 如果沒有 input words，return words\nBoundary conditions for loop 控制範圍\n特別注意，當 list remove 時，index 要繼續使用原來的 (因為原來位置被刪除，後面補前面)\nReference ","date":"2022-05-18T01:07:01+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2273/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2273] Find Resultant Array After Removing Anagrams 個人解法筆記 | 293rd LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 217. Contains Duplicate\n難度 Easy\n題目分類 Array, Hash Table, Sorting\n個人範例程式碼 - 2022/5/18 三刷 class Solution: def containsDuplicate(self, nums: List[int]) -\u0026gt; bool: if not nums: return False record = set() for num in nums: if num in record: return True else: record.add(num) return False 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 找重複，有重複 return True\ninput handling 如果沒有 input，return False\nBoundary conditions for 迴圈控制範圍\n個人範例程式碼 - 2022/3/3 二刷 class Solution: def containsDuplicate(self, nums: List[int]) -\u0026gt; bool: loopuptable = {} for ele in nums: if ele in loopuptable.keys(): return True else: loopuptable[ele] = True return False 說明 dictionary search 的時間只需要 O(1)，\n透過建立 dict 可以幫助我們快速查找已經出現的元素。\n註：「if ele in loopuptable.keys()」 也可以寫成 「if ele in loopuptable」\n只是寫成前者會更清楚。\n個人範例程式碼 - 2022/2/24 一刷 class Solution(object): def containsDuplicate(self, nums): \u0026#34;\u0026#34;\u0026#34; :type nums: List[int] :rtype: bool \u0026#34;\u0026#34;\u0026#34; return (len(set(nums)) != len(nums)) 說明 這題考的東西很簡單，就是找有沒有重複的東西，\n「找重複」非常適合使用 set 來做，我們只需要比較 set 之後的大小是否等於原長度即可。\nReference One line solution in python ","date":"2022-05-18T00:29:33+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-217/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [217] Contains Duplicate 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 1091. Shortest Path in Binary Matrix\n難度 Medium\n個人範例程式碼 DIRECTIONS = [(1,1), (1,0), (0,1), (-1,1), (1,-1), (-1,0), (0,-1), (-1,-1)] class Solution: def shortestPathBinaryMatrix(self, grid: List[List[int]]) -\u0026gt; int: self.m = len(grid) self.n = len(grid[0]) return self.bfs(grid) def bfs(self, grid): length = 0 visited = set() layer = [(0, 0)] # first layer while(layer): next_layer = [] for i, j in layer: if self.is_valid(grid, i, j, visited): visited.add((i, j)) if i \u0026lt;mark\u0026gt; self.m - 1 and j \u0026lt;/mark\u0026gt; self.n - 1: return length+1 for dx, dy in DIRECTIONS: # prepare for next layer next_layer.append((i+dx, j+dy)) else: continue length += 1 layer = next_layer else: return -1 # not found def is_valid(self, grid, i, j, visited): return 0 \u0026lt;= i \u0026lt; self.m and 0 \u0026lt;= j \u0026lt; self.n and (i,j) not in visited and grid[i][j] == 0 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 最短路徑，我們用 BFS 去找，\n因為要計算最短路徑的長度，我選擇用 layer 的方式分析，\n此外，因為題目有指定 8 個方向，我另外把這 8 個變數獨立寫出來。\n手動的最短路徑策略 這邊注意我在設計最短路徑上是有策略的。\n搜尋上，為了求最短路徑，我們當然會優先選擇「圖上往右下的部分」，\n再來往下或往右，\n再來往左下或右上，\n以此類推，這樣我們就能優先確保先進 visited 的都是最短路徑。\n換作程式碼如下： DIRECTIONS = [(1,1), (1,0), (0,1), (-1,1), (1,-1), (-1,0), (0,-1), (-1,-1)] input handling 在 bfs 一起處理，這邊注意我有踩到一個小雷。\n當起點就是 1 時，注意你要怎麼處理才能回傳 -1 Boundary conditions 當 bfs 搜尋到結果後，直接 return 就是最短路徑。\nReference Easy and Neat Python Solution ","date":"2022-05-17T18:10:21+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0353.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-1091/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [1091] Shortest Path in Binary Matrix 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 76. Minimum Window Substring\n難度 hard\n個人範例程式碼 - two pointer + sliding window class Solution: def minWindow(self, s: str, t: str) -\u0026gt; str: if not s: return s counter_t = Counter(t) ans = \u0026#34;\u0026#34; fast = 0 queue = [] valid_num = 0 window_counter = defaultdict(int) for slow, c in enumerate(s): while fast \u0026lt; len(s) and not self.is_sub_counter(window_counter, counter_t): # correct: collections \u0026lt;= set(queue) # print(slow, fast, window_counter) window_counter[s[fast]] += 1 queue.append(s[fast]) fast += 1 else: # window matches collection words if self.is_sub_counter(window_counter, counter_t): ans = s[slow:fast] if ans == \u0026#34;\u0026#34; or len(s[slow:fast]) \u0026lt;= len(ans) else ans # remove start until set not match c = queue.pop(0) window_counter[c] -= 1 return ans def is_sub_counter(self, counter_s, counter_t): # print(counter_s, counter_t) for key, value in counter_t.items(): if key not in counter_s: return False if counter_s[key] \u0026lt; value: # \u0026#34;a\u0026#34;, \u0026#34;aa\u0026#34; = 1 \u0026lt; 2, must \u0026gt;= 2 return False return True 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題最麻煩的部分是在 counter 跟 is sub counter 的關係，\n如果這題單純只有設計成要找 set() 的樣子會簡單很多，\n例如：ABC 找 ABC 用 set() 很容易，\n但要比較 AABC 與 AABC 沒辦法用 set() 的方法，因此我們需要另外寫一個方法 is_sub_counter()\ninput handling 如果沒有 s，回傳 s\nBoundary conditions 利用同向 two pointer: slow, fast 控制搜尋範圍。\nfast：替我們決定 sliding window 的結束位置 slow：替我們決定 sliding window 個人範例程式碼 - two pointer + sliding window 優化版 class Solution: def minWindow(self, s: str, t: str) -\u0026gt; str: if not s: return s counter_t = Counter(t) ans = \u0026#34;\u0026#34; fast = 0 queue = [] valid = 0 window_counter = defaultdict(int) for slow, c in enumerate(s): # print(slow, fast, valid, window_counter, ans) while fast \u0026lt; len(s) and valid \u0026lt; len(t): # correct: collections \u0026lt;= set(queue) # print(slow, fast, valid, window_counter, ans) key = s[fast] if key in counter_t: window_counter[key] += 1 if window_counter[key] \u0026lt;= counter_t[key]: valid += 1 fast += 1 else: # window matches collection words if valid == len(t): ans = s[slow:fast] if ans == \u0026#34;\u0026#34; or len(s[slow:fast]) \u0026lt;= len(ans) else ans # remove start until set not match if c in counter_t: window_counter[c] -= 1 if window_counter[c] \u0026lt; counter_t[c]: valid -= 1 return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 上面的作法其實沒什麼問題，速度慢的原因「最主要是在調用 is_sub_counter() 這個 function 太多次」，\n然而，我們稍微計算一下就能知道，每次我們使用 is_sub_counter()，我們都需要花 O(N) 的時間去計算 (for counter_t)，\n但這個動作其實是可以邊在我們 sliding window 的過程中順便記憶的，這樣就可以避免掉每一次重複的比對\ndef is_sub_counter(self, counter_s, counter_t): # print(counter_s, counter_t) for key, value in counter_t.items(): if key not in counter_s: return False if counter_s[key] \u0026lt; value: # \u0026#34;a\u0026#34;, \u0026#34;aa\u0026#34; = 1 \u0026lt; 2, must \u0026gt;= 2 return False return True 因此在新的概念中，我們設計了一個數字 valid，\n幫助我們記憶前面的 sliding window 中，現在有 valid 的數量，\n我們在設計時比較兩個 counter 紀錄，只有在範圍比較小的時候紀錄有 valid，\n而當「重複數字」我們不記在 valid，但 counter 仍要記錄，\n「 因為在 sliding 的過程中有可能會砍掉重複的，但依然保持 valid 」\ninput handling 如果沒有 s，回傳 s\nBoundary conditions 利用同向 two pointer: slow, fast 控制搜尋範圍。\nfast：替我們決定 sliding window 的結束位置 slow：替我們決定 sliding window Reference 最小子串覆盖 · Minimum Window Substring ","date":"2022-05-15T22:11:55+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-76/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [76] Minimum Window Substring 個人解法筆記"},{"categories":["716 - Linked List","Two pointers (同向雙指針 →→) / slow, fast (快慢雙指針)"],"content":"題目出處 142. Linked List Cycle II\n難度 medium\n個人範例程式碼 # Definition for singly-linked list. # class ListNode # def __init__(self, x) # self.val = x # self.next = None class Solution: def detectCycle(self, head: Optional[ListNode]) -\u0026gt; Optional[ListNode]: if not head or not head.next: return None slow = fast = head while(fast and fast.next): slow = slow.next fast = fast.next.next if fast == slow: # find loop break # out of while-else, no else else: return None while (head != slow): head = head.next slow = slow.next return head 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 首先我們先找出是否有 loop，找到後我們在開始找迴圈的起點。\n證明 假設迴圈的長度為 l 起點到迴圈入口的距離為 a slow, fast 相遇處為 a+b (相距迴圈入口 b) fast 走的距離，\n= 可以是 a+l(走了一圈迴圈)+b\n= 也等於 2*(a+b) = 兩倍的 slow 移動距離\n這樣我們可以推得 a+l+b = 2*(a+b), 得到 l = a+b\n我們現在想要求 a a = l-b，注意圖上 l-b，也就是把相遇處剩下的路再繼續走完。\n因此我們只要\n從 head 走 a 從相遇處走 l-b 走到下一個相遇處就是我們要的 a。\ninput handling 如果沒有 head 或沒有 head.next，return None\nBoundary conditions 透過 while(fast and fast.next) 控制搜尋範圍\npython while-else 用法說明 一般來說，我們通常都只會使用到 while，\n但其實在 python 中，while 是可以搭配 else 使用的。\n邏輯如下：\nwhile(條件成立): # 成立時循環 else: # 當不成立時，出現在這 # 當條件不成立時，開始作這邊的事情 這個寫法，可能很多人覺得好像有沒有 else 似乎不會有太大的影響?\n畢竟當 while 條件不成立後，後續的程式碼「本來就會執行」\n沒有錯!!!! 但是這個小細節，卻可以讓我們的程式碼邏輯更漂亮!\n例如說當我們有無關 while 的相關內容，我們才應該擺在外面，\n但有時候是剛結束迴圈時有一些收尾的相關內容要處理，這時候用 else 的寫法就相當漂亮了!\n範例 假設我們要把一個 mylist 「純手動」印成 [1,2,3] 的樣子，\n最後還要印出一個 finish!\nmylist = [1,2,3] idx = 0 print(\u0026#39;[\u0026#39;, end = \u0026#39;\u0026#39;) while(idx \u0026lt; len(mylist)): print(mylist[idx], \u0026#39;,\u0026#39;) idx += 1 else: print(\u0026#39;]\u0026#39;) print(\u0026#39;finish!\u0026#39;) 這個例子應該就很好懂了，我們集中「把要處理 list 列印的動作」放在 while-else 裡面，\n最後比較無關的 finish! 放在 while-else 外面，\n這樣看起來程式碼邏輯有沒有比都放在外面更清楚了呢? (當然，見仁見智啦XDDD，我覺得有XD)\n特殊用法 - while-else 搭配 break 使用的情況 這也是我近期解題才發現的，當 while - else 搭配 break 使用時，能發揮更好閱讀的效果!\n請讀者先想像一下，下面的程式碼會印出什麼呢?\nwhile(True): break else: print(1) print(2) 答案不是 1 2 !!! 而是只有 2 !!!\n這樣我們就能夠更進階的使用這個功能了，\n「透過 while 迴圈條件結束的，寫在 else 裡面」 「如果是透過 break 結束的，寫在最外面」 Reference 带环链表 II · Linked List Cycle II [C++/Java/Python] Slow and Fast || Image Explanation || Beginner Friendly ","date":"2022-05-12T14:57:22+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0352.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-142/","tags":[],"title":"【Leetcode】python - [142] Linked List Cycle II 個人解法筆記 | 內含 python while-else 用法介紹"},{"categories":["710 - Python LeetCode"],"content":"題目出處 42. Trapping Rain Water\n難度 hard\n個人範例程式碼 class Solution: def trap(self, height: List[int]) -\u0026gt; int: left, right = 0, len(height)-1 max_left, max_right = height[left], height[right] water_contains = 0 while(left \u0026lt; right): # print(left, right, water_contains) if max_left \u0026lt;= max_right: left += 1 water_contains += self.is_positive(min(max_left, max_right) - height[left]) # current max_left = max(max_left, height[left]) else: right -= 1 water_contains += self.is_positive(min(max_left, max_right) - height[right]) # current max_right = max(max_right, height[right]) return water_contains def is_positive(self, num): return num if num \u0026gt; 0 else 0 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 考慮比較複雜的同向 two pointer 問題。\n我們需要先推導出一個公式，\n「當前水位 = min(左最高, 右最高) - 當前高度」\n下圖的右邊，我們判斷左右高最矮的部分，可以計算當前「藍色最高為 1 」的水量 (必定，就算中間全空都會有) 你可能會考慮這樣的問題，問說左邊會怎麼算到 2，如果左邊真的能算到 2， 那一定是從右邊一路過來，「有比 2 更高的事件發生 (下圖畫的不是很好，右側任意位置只要有出現比 2 更高的都可以!)」 否則，左邊最高會永遠維持 1，而不用擔心算錯最主要的原因是我們不會這麼早算這格，必須確定「1 or 2」才會算 (右側有比 2 高就確定了)\n注意，處理時請注意負值 這裡要多注意一點，處理時，\n因為我們是計算 「min(left_max, right_max) - 當前高度」，因此很有可能會有計算到負值的可能。\n這時候我們需要另外寫一個判斷式，專門處理 \u0026lt; 0 的情況，\n可以用我上方的方法，或是使用以下方法，\n都可以「保證最小是 0，如果 \u0026gt; 0 就是該值」\nmax(0, min(left_max, right_max)) input handling x\nBoundary conditions 透過 while(left \u0026lt; right): 處理\nReference 接雨水 · Trapping Rain Water ","date":"2022-05-12T14:08:29+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0351.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-42/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [42] Trapping Rain Water 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 49. Group Anagrams\n難度 medium\n個人範例程式碼 class Solution: def groupAnagrams(self, strs: List[str]) -\u0026gt; List[List[str]]: str_dict = {} for each_str in strs: sorted_each_str = self.sort_alphabetical(each_str) if sorted_each_str not in str_dict: str_dict[sorted_each_str] = [] str_dict[sorted_each_str].append(each_str) ans = [] for key, value in str_dict.items(): ans.append(value) return ans def sort_alphabetical(self, s): return \u0026#34;\u0026#34;.join(sorted(s)) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n另外，最後那段後來發現可以優化為一行表示，不用自己重跑一次 dict，使程式碼更為簡潔： return list(str_dict.values()) 算法說明 其實就是一個整理 set 的題目，原本想要直接用 set() 作為 key，\n後來發現 key 不能使用 set 可惜了XD\n變通一下，把 key 換成「排序後的字串」，就能夠發揮類似的效果了，\n我們要的就是個統一的標準。\ninput handling x\nBoundary conditions for 控制範圍\nReference 错位词分组 · Group Anagrams ","date":"2022-05-12T13:32:16+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-49/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [49] Group Anagrams 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 793 · Intersection of Arrays\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param arrs: the arrays @return: the number of the intersection of the arrays \u0026#34;\u0026#34;\u0026#34; def intersection_of_arrays(self, arrs: List[List[int]]) -\u0026gt; int: # write your code here ans = set(arrs[0]) for arr in arrs: ans \u0026amp;= set(arr) return len(ans) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 題目說不需要重複，那就是瘋狂的 set and 取交集。\ninput handling set and 的過程會幫我們處理掉\nBoundary conditions x\nReference ","date":"2022-05-12T03:55:45+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-793/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [793] Intersection of Arrays 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 https://leetcode.com/problems/container-with-most-water/\n難度 Medium\n題目分類 Array, TwoPointers\n題外話 這題與 [42] Trapping Rain Water 類似，但 42 題比較難，\n或是我有想過如果水缸換成「中柱隔開不計算」，搞不好都是另外一種更難的題目。\n個人範例程式碼 - 2022/5/12 二刷 class Solution: def maxArea(self, height: List[int]) -\u0026gt; int: if not height or len(height) \u0026lt; 2: return 0 start, end = 0, len(height)-1 # [1,8,6,2,5,4,8,3,7] = 7(8-1)*7(min([8],[1])) max_water = 0 while(start \u0026lt; end): max_water = max(max_water, (end - start) * min(height[start], height[end])) if height[start] \u0026lt; height[end]: # try to find two highest side start += 1 else: end -= 1 else: max_water = max(max_water, (end - start) * min(height[start], height[end])) return max_water 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 策略是，想辦法找到左右都是最高的，只去移動(更新)邊比較小的那側。\n利用 two pointer 去找，過程中不斷更新。\ninput handling 如果 not height or len(height) \u0026lt; 2，return 0 (錯誤的狀況)\nBoundary conditions 用 while(start \u0026lt; end) 控制搜尋的範圍。\n個人手繪解法筆記 (解法重點) - 2021/3/31 一刷 也因為「即使中柱隔開，照樣計算」，所以我們只要找到「兩側最高」乘上「寬度」綜合起來最大就是答案。\nTwoPointers 我們用兩個指標 r, l 分別一個從左邊掃，一個從右邊掃，\n從 height[r], height[l] 選比較矮的，往前走尋找更高的\n(往前走也就是：r-1, l+1)\n注意：我們定義的 r, l 是 「index」\n而 height[r], height[l] 才是真正的「高」\n(第一個自己 debug 就是發現這個錯誤XDD)\n每算一次更新一次最大面積 (還好這題不用考慮中間有卡柱子的問題，簡單很多)，\n掃到中止目標 (寬 = 0 或 r 小於等於 l)\n說「小於等於」是考慮所有「不可能」的情況，所以不只說等於\n個人範例程式碼 class Solution: def maxArea(self, height: List[int]) -\u0026gt; int: max_area = 0 l = 0 # index r = len(height)-1 # index width = len(height)-1 while(width \u0026gt; 0 and l \u0026lt; r): # normal case area = width * min(height[r], height[l]) # print(f\u0026#34;area = {area}, r = {r}, l = {l}\u0026#34;) # debug max_area = max(max_area, area) # update if height[r] \u0026gt; height[l]: # l smaller l += 1 width -= 1 else: r -= 1 width -= 1 return max_area Reference 装最多水的容器 · Container With Most Water https://leetcode.com/problems/container-with-most-water/discuss/1069698/Python-O(n)-by-two-pointers-w-Comment ","date":"2022-05-12T03:54:28+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7754-1024x569.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-11/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [11] Container With Most Water 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 349. Intersection of Two Arrays\n難度 Easy\n個人範例程式碼 class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -\u0026gt; List[int]: return list(set(nums1) \u0026amp; set(nums2)) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 一個 set and 就結束了\u0026hellip;\n好舒服的 easy 題目\u0026hellip;\ninput handling set and 的過程會幫我們處理掉\nBoundary conditions x\nReference ","date":"2022-05-12T03:49:37+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-349/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [349] Intersection of Two Arrays 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 350. Intersection of Two Arrays II\n難度 Easy\n題目分類 hash-table, two-pointers, binary-search, sort\n個人範例程式碼 - 2022/5/12 三刷 class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -\u0026gt; List[int]: intersect_counter = (Counter(nums1) \u0026amp; Counter(nums2)) ans = [] for key, value in intersect_counter.items(): for _ in range(value): ans.append(key) return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 此題目的前一題為不需要進行重複值處理，這題需要處理重複，可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-349/\nCounter \u0026amp; Counter：可以找到所有重複的數值 \u0026amp; 數量\n這裡特別注意：很幸運的，他有幫我們處理掉 「\u0026ldquo;3個重複數\u0026rdquo; \u0026amp; \u0026ldquo;2個重複數\u0026rdquo; = \u0026ldquo;2個重複數\u0026rdquo;」 的情況，\n不然我們預期可能直接因為數量不同就 False，這樣我們就不能使用這個方法了。\ninput handling dict and 的過程中會處理掉\nBoundary conditions x\n個人範例程式碼 - 2022/3/4 二刷 (set \u0026amp; counter 流) class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -\u0026gt; List[int]: ans = [] for ele in (set(nums1) \u0026amp; set(nums2)): for i in range(min(Counter(nums1)[ele], Counter(nums2)[ele])): ans.append(ele) return ans 說明 這題看到求「重複」的，第一個直覺反應是不是會想到用 set 呢?\n可是一用 set 之後，就會發現這題目設計的難度之處，\n不過沒關係，遇到重複了，我們還有 Counter!\n先用 set 快速找出有重複的，\n再用確定有重複的，計算數量後，取較少的即可得到答案。\ncorner case 特殊情況處理 記得要找的是兩個集合的 min，而不是「只找單邊」\nBoundary conditions/ Edge conditions 邊際情況處理 x\n個人範例程式碼 - 2022/2/25 一刷 (two pointer 流) class Solution(object): def intersect(self, nums1, nums2): \u0026#34;\u0026#34;\u0026#34; :type nums1: List[int] :type nums2: List[int] :rtype: List[int] \u0026#34;\u0026#34;\u0026#34; # print(set(nums1) \u0026amp; set(nums2)) nums1, nums2 = sorted(nums1), sorted(nums2) res = [] pt1, pt2 = 0, 0 while(pt1 \u0026lt; len(nums1) and pt2 \u0026lt; len(nums2)): if nums1[pt1] \u0026gt; nums2[pt2]: pt2 += 1 elif nums1[pt1] \u0026lt; nums2[pt2]: pt1 += 1 else: # nums1[pt1] == nums2[pt2] res.append(nums1[pt1]) pt1 += 1 pt2 += 1 return res 說明 這題看到求「重複」的，第一個直覺反應是不是會想到用 set 呢?\n可是一用 set 之後，就會發現這題目設計的難度之處，\n因為它是允許有 [1,1,2,2] 去交集 [2,2] 的\n所以如果我門直接用 set，會把重複的 2 給用掉。\n因此這題不能用 set 的作法。\n我們採用的是 sort 後並使用兩個 pointer，一個個對照，\n只要發現比較小的那側，就往下一個數字前進，\n而如果兩者是一樣的，我們就同時一起前進。\n邊際情況處理 這題目需要處理的邊際情況為 pointer 什麼時候結束，\n我們需要知道，當其中一個 pointer 已經「指完了全部的內容」，\n就沒有繼續往下看的必要了。因為另外一個再往下看，也一定找不到一樣的值。\n所以我們使用 while 設定範圍在 pt1 \u0026lt; len(nums1) 的範圍內，\n就可以處理這個問題。\nReference 两数组的交集 II · Intersection of Two Arrays II Three Python Solutions ","date":"2022-05-12T02:55:05+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-350/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [350] Intersection of Two Arrays II 個人解法筆記"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 此為我使用 Raspberry Pi 3 發生以下問題的解決方法，\n最主要是想直接執行桌面的 script 會發生此問題。\nFailed to execute child process \u0026#34;xterm\u0026#34; 解決方法 透過以下文章提供的解法，順利解決，\n我猜是 mobaxterm 有修改到一些 terminal 的設定\nsudo ln -s /usr/bin/lxterminal /usr/bin/xterm Reference Failed to execute child process \u0026ldquo;xterm\u0026rdquo; ","date":"2022-05-11T02:56:11+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/failed-to-execute-child-process-xterm/","tags":["Pi","Raspberry"],"title":"【Rpi3】問題解決：Failed to execute child process \"xterm\" (Raspberry Pi 3 / 樹莓派 )"},{"categories":["710 - Python LeetCode"],"content":"題目出處 88. Merge Sorted Array\n難度 Easy\n題目分類 array, two-pointers\n個人範例程式碼 - 2022/5/11 class Solution: def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums1 in-place instead. \u0026#34;\u0026#34;\u0026#34; m, n = m-1, n-1 # get last idx for idx in range(len(nums1)-1, -1, -1): if(m \u0026gt;= 0 and n \u0026gt;= 0): if nums1[m] \u0026lt; nums2[n]: nums1[idx] = nums2[n] n -= 1 else: nums1[idx] = nums1[m] m -= 1 elif m \u0026gt;= 0: nums1[idx] = nums1[m] m -= 1 elif n \u0026gt;= 0: nums1[idx] = nums2[n] n -= 1 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 從尾巴開始往前填，注意一下順序即可\ninput handling 在 for 迴圈中一起處理\nBoundary conditions for 迴圈控制範圍\n個人範例程式碼 - 2022/2/25 class Solution(object): def merge(self, nums1, m, nums2, n): \u0026#34;\u0026#34;\u0026#34; :type nums1: List[int] :type m: int :type nums2: List[int] :type n: int :rtype: None Do not return anything, modify nums1 in-place instead. \u0026#34;\u0026#34;\u0026#34; while (m \u0026gt; 0 and n \u0026gt; 0): if(nums1[m-1] \u0026gt;= nums2[n-1]): nums1[m+n-1] = nums1[m-1] # put nums1[m] in to the last place of nums1 m -= 1 else: nums1[m+n-1] = nums2[n-1] n -= 1 else: while n \u0026gt; 0: # m = 0 nums1[m+n-1] = nums2[n-1] n -= 1 # else m \u0026gt; 0, already in nums1 說明 這題我們可以先注意題目的敘述：\n最需要思考的部分在於「題目希望最後的結果直接儲存在 nums1 裡面」\n雖然這樣的限制會使我們解題更加的「受到限制」，但反過來說，\n其實這也是題目給我們「最大的提示」。\n題目的 nums1 給的空間為 m+n，所以最後我們都會將所有的值擺入 nums1 中，\n而因為我們可以無憂慮去更改的值為最後的 0,0,0\u0026hellip; 部分\n(因為這個值就算被改消失了，也不影響我們想計算的結果)\n所以這邊可以推斷出一定要「從後面開始解」，「從最無痛的 0 開始取代」，\n再來我們就可以來思考，什麼時候擺入 nums1的值? 什麼時候擺入 nums2 的值?\n我們可以靠著題目所給的 m, n 來進行這樣的操作，透過值不斷擺入，\n我們去移動 m, n 作為 index 的座標，即可順利完成此題目。\n邊際情況處理 這題要注意的邊際情況為 m=0, n=0 的時候我們要怎麼處理，\n而照題目的敘述，\nn=0 基本上對我們來說沒差，因為 m 控制的 nums1 即代表我們要的答案 m=0 才是重點，如果還有剩餘的 n，都需要擺入 nums1 中，而位置索幸很好取得， 依然可以透過 m+n-1 算出 注意 index 細節，才是這題要考細心的部分 這題有很多 index 細節考驗著作答者的細心度，\n包含：\n迴圈的範圍僅限於 m, n \u0026gt; 0 時，其他需要進例外處理 m-1, n-1 才是當下座標 要擺入的位置為 m+n-1 處理到 n \u0026gt; 0 就應該停下，而非 n \u0026gt;= 0 python while-else 用法說明 一般來說，我們通常都只會使用到 while，\n但其實在 python 中，while 是可以搭配 else 使用的。\n邏輯如下：\nwhile(條件成立): # 成立時循環 else: # 當不成立時，出現在這 # 當條件不成立時，開始作這邊的事情 這個寫法，可能很多人覺得好像有沒有 else 似乎不會有太大的影響?\n畢竟當 while 條件不成立後，後續的程式碼「本來就會執行」\n沒有錯!!!! 但是這個小細節，卻可以讓我們的程式碼邏輯更漂亮!\n例如說當我們有無關 while 的相關內容，我們才應該擺在外面，\n但有時候是剛結束迴圈時有一些收尾的相關內容要處理，這時候用 else 的寫法就相當漂亮了!\n範例 假設我們要把一個 mylist 「純手動」印成 [1,2,3] 的樣子，\n最後還要印出一個 finish!\nmylist = [1,2,3] idx = 0 print(\u0026#39;[\u0026#39;, end = \u0026#39;\u0026#39;) while(idx \u0026lt; len(mylist)): print(mylist[idx], \u0026#39;,\u0026#39;) idx += 1 else: print(\u0026#39;]\u0026#39;) print(\u0026#39;finish!\u0026#39;) 這個例子應該就很好懂了，我們集中「把要處理 list 列印的動作」放在 while-else 裡面，\n最後比較無關的 finish! 放在 while-else 外面，\n這樣看起來程式碼邏輯有沒有比都放在外面更清楚了呢? (當然，見仁見智啦XDDD，我覺得有XD)\n特殊用法 - while-else 搭配 break 使用的情況 這也是我近期解題才發現的，當 while - else 搭配 break 使用時，能發揮更好閱讀的效果!\n請讀者先想像一下，下面的程式碼會印出什麼呢?\nwhile(True): break else: print(1) print(2) 答案不是 1 2 !!! 而是只有 2 !!!\n這樣我們就能夠更進階的使用這個功能了，\n「透過 while 迴圈條件結束的，寫在 else 裡面」 「如果是透過 break 結束的，寫在最外面」 使用方式可以看我下面這題的示範：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-142/\nReference Python easy to understand in-place solution ","date":"2022-05-11T02:52:15+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-88/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [88] Merge Sorted Array 個人解法筆記 | 內含 python while-else 用法說明"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 此為 Raspberry Pi 3 / 樹莓派 結合 anydesk TCP tunnel + MobaXterm ssh 快速連線設定筆記\nanydesk 設定 windows 主機直接設定 anydesk TCP tunnel，這裡就不贅述\n詳請可見： https://wongwongnotes.com/posts/linux-shell/system-network/linux/windows-anydesk-ssh/\nMobaXterm 設定 把 anydesk tcp tunnel 的 9000 直接搬到這邊\nReference 【Windows】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (Windows anydesk ssh command line) ","date":"2022-05-11T01:55:33+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/rpi3_anydesk.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/rpi3-anydesk-mobaxterm/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 結合 anydesk TCP tunnel + MobaXterm ssh 快速連線設定筆記 (rpi3 x anydesk x mobaxterm)"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 此為我測試 Raspberry Pi 3 / 樹莓派 LCD 模組的筆記\n有一個重點是，當使用 LCD 模組做為顯示螢幕時，HDMI 是不可以同時輸出畫面的。\n去 github 下載相關的套件 前兩步驟只需要第一次執行即可，後續啟動不需要做前兩步驟。\n(這邊雖然寫了，但是與下面啟動步驟重複)\nsudo rm -rf LCD-show git clone https://github.com/goodtft/LCD-show.git chmod -R 755 LCD-show cd LCD-show/ sudo ./LCD35-show 開啟 LCD 顯示模式 (不可與 HDMI 模式同時) #!/bin/bash chmod -R 755 LCD-show cd LCD-show/ sudo ./LCD35-show LCD 模式，顯示範例 開啟 HDMI 顯示模式 (不可與 LCD 模式同時) #!/bin/bash chmod -R 755 LCD-show cd LCD-show/ sudo ./LCD-hdmi Reference Raspberry Pi 3 Complete Tutorial – Let’s Get Started ","date":"2022-05-11T01:41:10+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_3433-scaled.jpg","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/raspberry-pi-3-lcd-module/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 LCD 模組設定 LCD module 3.5 英吋螢幕顯示 Install 3.5 Inch LCD on Raspberry Pi (updated: 2022/5/11)"},{"categories":["DFS (BST)"],"content":"題目出處 98. Validate Binary Search Tree\n難度 medium\n個人範例程式碼 - DFS # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def isValidBST(self, root: Optional[TreeNode]) -\u0026gt; bool: return self.dfs(root, float(\u0026#34;-inf\u0026#34;), float(\u0026#34;inf\u0026#34;)) def dfs(self, root, lowerbound, upperbound): # end of recursion # define and split ans = True if root.left: # left: update upperbound if lowerbound \u0026lt; root.left.val \u0026lt; root.val: ans \u0026amp;= self.dfs(root.left, lowerbound, root.val) else: return False if root.right: # right: update lowerbound if root.val \u0026lt; root.right.val \u0026lt; upperbound: ans \u0026amp;= self.dfs(root.right, root.val, upperbound) else: return False return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 DFS 中，我們需要特別注意 BST 的特性，也就是他上下的範圍，常見錯誤如下\n如果「只注意他與上層的關係」，會出現下圖中「三角形的錯誤」，因此我們需要同時考慮上下界\n然而，在 BST 中處理上下界並不難，上下界的特性也十分容易分析。\n我們只需要注意兩個重點：\n往左走時：更新 upperbound 往右走時：更新 lowerbound 見下圖：\ninput handling 同 DFS 一起處理\nBoundary conditions 每個點都必須同時界於 lowerbound ~ upperbound，不然 return False\n個人範例程式碼 - inorder traversal # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def isValidBST(self, root: Optional[TreeNode]) -\u0026gt; bool: if not root: return True stack = [] node = root # 1. to left bottom while node: stack.append(node) node = node.left ans = [] # 2. pop right, see right, to left bottom while stack: node = stack.pop(-1) ans.append(node.val) if node.right: node = node.right while node: # to left bottom stack.append(node) node = node.left # print(ans) # return ans == sorted(ans) for i, num in enumerate(ans): if i \u0026gt; 0 and ans[i-1] \u0026gt;= ans[i]: return False return True 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 直接用 inorder traversal 把整個順序列出來，然後再依序比較後者必須比前者大。\ninput handling 如果沒有 root，return 0\nBoundary conditions inorder traversal 的 stack 流派\n使用 stack\n左到底 pop，找右一個，然後一樣往左邊底 在 pop 時紀錄，就會得到最終答案。\nReference 验证二叉查找树 · Validate Binary Search Tree ","date":"2022-05-10T22:24:51+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0348.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-bst/leetcode-python-98/","tags":["DFS"],"title":"【Leetcode】python - [98] Validate Binary Search Tree 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 100. Same Tree\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -\u0026gt; bool: return self.dfs(p, q) def dfs(self, p, q): # end of recursion if not p and not q: return True if not p or not q: return False # define if p.val == q.val: # True # split return self.dfs(p.left, q.left) and self.dfs(p.right, q.right) else: return False 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 基本的 Tree traverse 變形，用 dfs 實作。\ninput handling 一同在 dfs 內部處理\nBoundary conditions dfs 結束條件\n正確結束:\nif not p and not q, return True 錯誤情況:\nnot p 但還有 q，或相反，return False p.val 不等於 q.val，return False Reference Shortest+simplest Python ","date":"2022-05-10T21:11:47+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-100-same-tree-python/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [100] Same Tree 個人解法筆記"},{"categories":["DFS (全部方案)"],"content":"題目出處 2267. Check if There Is a Valid Parentheses String Path\n難度 hard\n個人範例程式碼 - 純 DFS (會 TLE) DIRECTIONS = [ (1,0), (0,1) ] class Solution: def hasValidPath(self, grid: List[List[str]]) -\u0026gt; bool: if not grid: return False return self.dfs(grid, 0, 0, set(), []) def dfs(self, grid, i, j, visited, queue): # print(i, j, visited, queue) # end of recursion if i \u0026lt;mark\u0026gt; len(grid)-1 and j \u0026lt;/mark\u0026gt; len(grid[0])-1: if grid[i][j] \u0026lt;mark\u0026gt; \u0026#34;)\u0026#34; and queue and queue[-1] \u0026lt;/mark\u0026gt; \u0026#34;(\u0026#34;: return len(queue) == 1 # queue is empty else: return False # not \u0026#34;)\u0026#34; or queue[-1] not \u0026#34;(\u0026#34; # define and split ans = False visited.add((i, j)) for d_x, d_y in DIRECTIONS: if ans == True: # Find case, early return continue if self.is_valid(grid, i+d_x, j+d_y) and (i+d_x, j+d_y) not in visited: if grid[i][j] == \u0026#34;(\u0026#34;: queue.append(\u0026#34;(\u0026#34;) ans |= self.dfs(grid, i+d_x, j+d_y, visited, queue) queue.pop(-1) # backtracking else: # \u0026#34;)\u0026#34; if not queue: # return False, wrong: need remove continue else: queue.pop(-1) # pop \u0026#34;)\u0026#34; ans |= self.dfs(grid, i+d_x, j+d_y, visited, queue) queue.append(\u0026#34;)\u0026#34;) # backtracking visited.remove((i, j)) return ans def is_valid(self, grid, i, j): m, n = len(grid), len(grid[0]) return 0 \u0026lt;= i \u0026lt; m and 0 \u0026lt;= j \u0026lt; n 算法說明 矩陣搜索，很直覺想到的就是使用 dfs 去搜尋，\n不過速度上會不夠，我們需要優化一下\n個人範例程式碼 - DFS + Memoization class Solution: def hasValidPath(self, grid: List[List[str]]) -\u0026gt; bool: self.memo = {} return self.dfs(grid, 0, 0, 0) def dfs(self, grid, i, j, cnt): # end of recursion if i \u0026lt;mark\u0026gt; len(grid)-1 and j \u0026lt;/mark\u0026gt; len(grid[0])-1 and grid[i][j] == \u0026#34;)\u0026#34;: return cnt == 1 if not self.is_valid(grid, i, j): return False if cnt \u0026lt; 0: return False # define if grid[i][j] == \u0026#34;(\u0026#34;: cnt += 1 else: cnt -= 1 # split if (i, j, cnt) in self.memo: return self.memo[(i, j, cnt)] ans = self.dfs(grid, i+1, j, cnt) or self.dfs(grid, i, j+1, cnt) self.memo[(i, j, cnt)] = ans return ans def is_valid(self, grid, i, j): m, n = len(grid), len(grid[0]) return 0 \u0026lt;= i \u0026lt; m and 0 \u0026lt;= j \u0026lt; n 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這個是在閱讀別人的解答後，自己重寫的解法，用 cnt 優化了 \u0026ldquo;()\u0026rdquo; 的 queue 判斷式，這個想法非常的漂亮。\n另外這邊我也才注意到原題目要求的搜索方向固定只有「右」和「下」，\n因此多方向的寫法其實也可以簡化得更漂亮。\n判定條件 結束條件：\n「i = len(grid)-1」 and 「j = len(grid[0])-1」判定結束位置在右下角 grid[i][j] = \u0026ldquo;)\u0026quot;，結束必須為 \u0026ldquo;)\u0026rdquo; return cnt = 1 (我們也必須要知道的是，剩下個括弧數剛好等於 1，等於只剩「\u0026rdquo;(\u0026quot;」) 終止條件:\n當 cnt \u0026lt; 0 ，表示只有 \u0026ldquo;)\u0026quot;，retrun False 當不在範圍內時，return False Memoization 筆記 (記憶化搜索) Memoization 記憶化搜索是解決這個 TLE 的關鍵，\n簡單來說就是我們把「重複性」且「已經解過的問題」，做解答的筆記。\n如果說，DFS 是從起點往終點搜尋 Memoization，就是類似從終點往回看的過程紀錄 (類似 DP 概念) 以下面的圖片來說明，可能路線可能有非常多種，\n但我們可以知道只要到「特定座標」，且「特定 cnt」，\n就是一個重複性的題目，我們可以先把結果記錄下來。\n下面看圖應該會更明顯，我們用兩種不同的路線都抵達了 (2, 2)，\n而我們只需要計算第一次，之後就直接從 memo 找結果，不用再計算了。\n你可能會想問，這樣我們怎麼省時間的? 上面我們是用 True 為舉例比較好懂，\n但他更可以替我們省下大量的 False 的時間，\n因為我們也能夠以同樣概念的做 (2, 2, 0) = False (我隨便舉例的，理論上不可能)\n這樣我們第二次碰到 (2, 2, 0) 這題目時 (從不同路線抵達此處，也呈現同樣狀態)，\n就可以不用再往下重搜了，直接回傳 False。\ninput handling 同 dfs 結束條件，一起在 dfs 內部處理\nBoundary conditions 當上述終止條件或結束條件觸發時，return。\nReference [Java/C++/Python] DP Solution Python | DP | Memoization | Simple Solution ✅ Python Simple Memoisation/Caching ","date":"2022-05-10T15:05:11+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0347.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-2267/","tags":["DFS"],"title":"【Leetcode】python - [2267] Check if There Is a Valid Parentheses String Path 個人解法筆記 | 292nd LeetCode Weekly Contest (內含：Memoization 記憶化搜索筆記)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2266. Count Number of Texts\n難度 medium\n個人範例程式碼 MOD_NUM = pow(10, 9)+7 class Solution: def countTexts(self, pressedKeys: str) -\u0026gt; int: if not pressedKeys: return 0 # cur = i = -1, i-1=-2 dp = [1] # init for i in range(len(pressedKeys)): dp.append(dp[-1]) # default append last, not same # dp[-1] + a if i \u0026gt; 0 and pressedKeys[i] == pressedKeys[i-1]: # same 1+1 dp[-1] += dp[-3] # dp[i-2] + b if i \u0026gt; 1 and pressedKeys[i] == pressedKeys[i-2]: # same 1+2 dp[-1] += dp[-4] # dp[i-3] + c if i \u0026gt; 2 and pressedKeys[i] in [\u0026#34;7\u0026#34;,\u0026#34;9\u0026#34;] and pressedKeys[i] == pressedKeys[i-3]: # same 1+3, only 79 dp[-1] += dp[-5] # dp[i-4] + d dp[-1] %= MOD_NUM # print(dp) return dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題目的圖，是直接拿 leetcode 第 17 題的圖來用的嗎XDD\nhttps://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-17/\n不同的是，這一題比較接近真實使用狀況，\n而 17 題只是單純算組合而已。\n思考上偏硬的題目，先注意到只有 7, 9 有4個文字需要處理。\n我們可以知道:\n新的下一個字母，都是由「之前全部組合 + 一個新字」 當新字為重複時，等同於「之\u0026quot;前前\u0026quot;全部組合 + 兩新字視為一個新字」\n\u0026hellip; 以此類推 改寫成程式碼變成：\n新的下一個字母，都是由「之前全部組合 + 一個新字」 dp[i] = dp[i-1] # dp[i-1] + \u0026#34;a\u0026#34; 當新字為重複時，等同於「之前前全部組合 + 兩新字視為一個新字」 dp[i] += dp[i-2] # 加上新的可能性，前兩個組合 + 兩個字視為一個新字 (dp[i-2] + \u0026#34;b(aa)\u0026#34;) \u0026hellip; 以此類推\n(推導可從下圖觀察，過程比較亂一些)\ninput handling 如果沒有 input ，return 0\nBoundary conditions for loop 控制範圍\nReference Python | DP with diagrams for beginners ","date":"2022-05-10T14:27:05+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0345.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2266/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2266] Count Number of Texts 個人解法筆記 | 292nd LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2264. Largest 3-Same-Digit Number in String\n難度 easy\n個人範例程式碼 class Solution: def largestGoodInteger(self, num: str) -\u0026gt; str: if not num or len(num) \u0026lt;= 2: return \u0026#34;\u0026#34; ans = -1 for i, c in enumerate(num): if i \u0026lt;= len(num)-3 and num[i] \u0026lt;mark\u0026gt; num[i+1] and num[i] \u0026lt;/mark\u0026gt; num[i+2]: # 012, 3 - 3 = 0 ans = max(int(num[i:i+3]), ans) if ans == -1: return \u0026#34;\u0026#34; if ans == 0: return \u0026#34;000\u0026#34; return str(ans) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題就是要找連續三個數字，然後要最大，\n簡單的字串處理，稍微小心細節處理即可。\n我們用 int 紀錄文字，初始 -1\n如果最後沒找到 (-1)，就回傳 \u0026quot;\u0026quot; 最後得到 0，回傳 \u0026ldquo;000\u0026rdquo; (轉 int 會消失) 其他回傳 str(對應數字) input handling 如果 no input 或 input len \u0026lt;= 2，為不可能的 case，return \u0026quot;\u0026quot;\nBoundary conditions 用 for 控制範圍\nReference ","date":"2022-05-10T13:44:20+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2264/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2264] Largest 3-Same-Digit Number in String 個人解法筆記 | 292nd LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 7. Reverse Integer\n難度 medium\n個人範例程式碼 class Solution: def reverse(self, x: int) -\u0026gt; int: negative_flag = False if x \u0026lt; 0: x = -x negative_flag = True ans = int(str(x)[::-1]) if ans \u0026gt;= (1 \u0026lt;\u0026lt; 31) -1: return 0 return -ans if negative_flag else ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 先抽出負號，再直接以 string 的方式顛倒，\n需要注意題目要求數值範圍介於 -2^31 ~ 2^31-1 之間，\n2^31 = (1 \u0026laquo; 31)\ninput handling 處理負號，拿出來後再丟回去。\nBoundary conditions 處理好數字範圍\nReference 反转整数 · Reverse Integer ","date":"2022-05-06T04:33:38+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-7/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [7] Reverse Integer 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 56. Merge Intervals\n難度 medium\n個人範例程式碼 class Solution: def merge(self, intervals: List[List[int]]) -\u0026gt; List[List[int]]: if not intervals: return intervals intervals = sorted(intervals, key = lambda x: x[0]) ans = [] for i, interval in enumerate(intervals): if i == 0: prev = interval continue if prev[1] \u0026lt; interval[0]: # end \u0026lt; start ans.append(prev) prev = interval else: prev[0] = min(prev[0], interval[0]) prev[1] = max(prev[1], interval[1]) ans.append(prev) # last return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 interval 經典題目，記得必須先經過 sorted 處理。\nsorted 搭配 lambda 的用法 針對第一個項目的排序 - lambda 法 intervals = sorted(intervals, key = lambda x: x[0]) 針對第二個項目的排序 - function 法 這個是留給萬一臨時忘記 lambda 怎麼寫的人，比較不漂亮，但是可行的替代方案\ndef sort_key(self, key): return key[0] intervals = sorted(intervals, key = self.sort_key) 完整的程式碼 class Solution: def merge(self, intervals: List[List[int]]) -\u0026gt; List[List[int]]: if not intervals: return intervals intervals = sorted(intervals, key = self.sort_key) ans = [] for i, interval in enumerate(intervals): if i == 0: prev = interval continue if prev[1] \u0026lt; interval[0]: # end \u0026lt; start ans.append(prev) prev = interval else: prev[0] = min(prev[0], interval[0]) prev[1] = max(prev[1], interval[1]) ans.append(prev) # last return ans def sort_key(self, key): return key[0] input handling 如果沒有 intervals，回傳 intervals\nBoundary conditions for 處理範圍，補上最後一個區間\nReference 合并区间 · Merge Intervals ","date":"2022-05-05T21:13:51+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-56/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [56] Merge Intervals 個人解法筆記 | 內含：sorted key 搭配 lambda 的用法範例"},{"categories":["710 - Python LeetCode"],"content":"題目出處 57. Insert Interval\n難度 medium\n個人範例程式碼 class Solution: def insert(self, intervals: List[List[int]], newInterval: List[int]) -\u0026gt; List[List[int]]: if not intervals: return [newInterval] ans = [] for interval in intervals: if newInterval[0] \u0026gt; interval[1]: # head ans.append(interval) elif newInterval[1] \u0026lt; interval[0]: #tail ans.append(newInterval) newInterval = interval else: # merge newInterval = [min(interval[0], newInterval[0]), max(interval[1], newInterval[1])] ans.append(newInterval) # last return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 merge 類型變化，\n排序時我們處理 min(start), max(end)\ninput handling 如果沒有 input intervals，return [newInterval]\nBoundary conditions for 到底，處理最後一組 interval，\n有個滿漂亮的解法是 merge 之後的我們都使用 newInterval 幫助我們完成任務。\nReference 插入区间 · Insert Interval ","date":"2022-05-05T20:43:00+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-57/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [57] Insert Interval 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 61. Rotate List\n難度 medium\n個人範例程式碼 # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def rotateRight(self, head: Optional[ListNode], k: int) -\u0026gt; Optional[ListNode]: if not head or k == 0: return head cur = head length = 0 while(cur): length += 1 prev = cur cur = cur.next else: prev.next = head # circular k = length - (k % length) # counterclockwise -\u0026gt; clockwise while(k \u0026gt; 0): # move k times (clockwise) k -= 1 prev = head head = head.next else: prev.next = None # tail return head # head 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 網路上看到覺得最漂亮的算法，\n既然都要 rotate 了，不如直接先串成一個環吧!\n注意 rotate 逆時針 從題目敘述我們會發現 rotate 方向為「逆時針」，\n這樣我們就有必要先計算總共有幾個元素，\n再來透過 k = k - (k % length)，我們就可以得到「順時針要走多少 k 」\ninput handling 如果沒有 input head，return head\nBoundary conditions while loop 控制 k 的搜尋範圍，\n先弄成環之後，處理環的前後。\nReference 旋转链表 · Rotate List ","date":"2022-05-05T19:22:36+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-61/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [61] Rotate List 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 268. Missing Number\n難度 easy\n個人範例程式碼 class Solution: def missingNumber(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 num_set = set(nums) n = len(nums) + 1 for i in range(n): if i not in num_set: # O(1) return i 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 因為 list 搜尋是 O(N) 時間，\n為了加速，先轉成 hashset，搜尋時間變成 O(1)\ninput handling 如果沒有 input，return 0\nBoundary conditions for loop 控制範圍\n個人範例程式碼 - set class Solution: def missingNumber(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 num_set = set([x for x in range(len(nums)+1)]) return (num_set - set(nums)).pop() 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 set - set，找到缺少的內容\nset.pop() 可以取得 set 裡面的內容，例如從 {2} 中取得 2\ninput handling 如果沒有 input，return 0\nBoundary conditions for loop 控制範圍\nReference 寻找缺失的数 · Missing Number ","date":"2022-05-04T16:31:39+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-268/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [268] Missing Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 70. Climbing Stairs\n難度 easy\n個人範例程式碼 class Solution: def climbStairs(self, n: int) -\u0026gt; int: # dp[i] = dp[i-1] + dp[i-2] if n == 0: return 0 dp = [] for i in range(n): if i == 0: dp.append(1) elif i == 1: dp.append(2) else: dp.append(dp[-1] + dp[-2]) return dp[-1] 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 利用 dp[i] = dp[i-1] + dp[i-2]\ninput handling 處理開頭 i = 0 的情況，return 0\ni = 1, return 1\ni = 2, return 2\nBoundary conditions 控制 for-loop 的範圍\nReference ","date":"2022-05-04T16:22:12+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-70/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [70] Climbing Stairs 個人解法筆記"},{"categories":["716 - Linked List","Two pointers (同向雙指針 →→) / slow, fast (快慢雙指針)","Two pointers (背向雙指針 ←→)"],"content":"題目出處 234. Palindrome Linked List\n難度 easy\n個人範例程式碼 - Deque (比較進階)，空間 O(N) # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def isPalindrome(self, head: Optional[ListNode]) -\u0026gt; bool: if not head: return False dq = deque([]) while(head): dq.append(head.val) head = head.next return self.is_palindrome(dq) def is_palindrome(self, dq): while(len(dq)\u0026gt;=2): if dq.popleft() == dq.pop(): continue else: return False else: # if len(dq) \u0026lt;mark\u0026gt; 1, True or len(dq) \u0026lt;/mark\u0026gt; 0, True return True 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 Deque 解 palindrome 的題目真的是無敵，但因為是比較進階的資料結構，也許不一定是面試想要看到的解\ninput handling 處理沒有 head 的情況，return False\nBoundary conditions 控制 pointer 搜尋至 None\n個人範例程式碼 - 同向 two pointer, 使用空間 O(N/2) stack # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def isPalindrome(self, head: Optional[ListNode]) -\u0026gt; bool: if not head: return False slow = head fast = head.next while fast and fast.next: slow = slow.next fast = fast.next.next else: if not fast: return self.odd_case(head, slow, fast) else: # not fast return self.even_case(head, slow, fast) def odd_case(self, head, slow, fast): mid = slow stack = deque([]) slow = slow.next while(slow): stack.append(slow.val) slow = slow.next # print(stack) while(stack): if head.val == stack.pop(): head = head.next else: return False else: return True def even_case(self, head, slow, fast): mid = slow stack = deque([]) slow = slow.next while(slow): stack.append(slow.val) slow = slow.next # print(stack) while(stack): if head.val == stack.pop(): head = head.next else: return False else: return True 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 我猜此題目最多會要求到把使用空間壓縮至 O(1)，\n這樣上面 Deque 的做法會因為空間 O(n) 而不能用。\n如果要實現 O(1) 空間的話，\n這題就可以考到 LinkedList 的反轉，同向 two pointer。\n不過這邊求速度，就先不寫 LinkedList 的反轉，\n使用 O(N/2) 空間的 stack 完成同樣的任務\ninput handling 處理沒有 head 的情況，return False\nBoundary conditions 控制 slow, fast pointer 搜尋至 None，\n注意結束條件的 fast and fast.next\n個人範例程式碼 - 同向 two pointer，使用 reverse LinkedList 空間 O(1) # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def isPalindrome(self, head: Optional[ListNode]) -\u0026gt; bool: if not head: return False slow = head fast = head.next while fast and fast.next: slow = slow.next fast = fast.next.next else: if not fast: return self.odd_case(head, slow, fast) else: # not fast return self.even_case(head, slow, fast) def odd_case(self, head, slow, fast): dummy = self.reverse(slow) while dummy: # print(dummy.val, head.val) if dummy.val == head.val: head, dummy = head.next, dummy.next else: return False return True def even_case(self, head, slow, fast): dummy = self.reverse(slow.next) while dummy: # print(dummy.val, head.val) if dummy.val == head.val: head, dummy = head.next, dummy.next else: return False return True def reverse(self, node): prev = None while node: tmp = node.next node.next = prev prev = node node = tmp return prev # prev \u0026lt;- node(None) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 同上 slow, fast 作法，但實現 O(1) 空間，利用 LinkedList 的反轉，同向 two pointer。\ninput handling 處理沒有 head 的情況，return False\nBoundary conditions 控制 slow, fast pointer 搜尋至 None，\n注意結束條件的 fast and fast.next\nReference 回文链表 · Palindrome Linked List Python easy to understand solution with comments (operate nodes directly). 11 lines, 12 with restore, O(n) time, O(1) space ","date":"2022-05-04T03:42:30+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0343.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-234/","tags":[],"title":"【Leetcode】python - [234] Palindrome Linked List 個人解法筆記 (內含 reverse LinkedList 方法)"},{"categories":["BFS (連通圖搜索)","DP (座標型 - 二維)"],"content":"題目出處 221. Maximal Square\n難度 medium\n個人範例程式碼 - BFS (會 TLE) class Solution: def maximalSquare(self, matrix: List[List[str]]) -\u0026gt; int: if not matrix: return 0 ans = 0 for i in range(len(matrix)): for j in range(len(matrix[0])): if matrix[i][j] == \u0026#34;1\u0026#34;: ans = max(ans, self.bfs(matrix, i, j)) return ans def bfs(self, matrix, start_i, start_j): edge = 1 while(True): for i in range(start_i, start_i+edge): for j in range(start_j, start_j+edge): # print(i, j) if self.is_bounded(matrix, i, j) and matrix[i][j] == \u0026#34;1\u0026#34;: continue else: return (edge-1)**2 edge += 1 def is_bounded(self, matrix, i, j): m, n = len(matrix), len(matrix[0]) return 0 \u0026lt;= i \u0026lt; m and 0 \u0026lt;= j \u0026lt; n 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 BFS 應該是這題最直覺的解，不過會 TLE，需要再加速。\ninput handling 處理沒有 matrix 的情況，return 0\nBoundary conditions 控制 for-loop 的範圍\n個人範例程式碼 - DP class Solution: def maximalSquare(self, matrix: List[List[str]]) -\u0026gt; int: if not matrix: return 0 dp = [list(map(int, x)) for x in matrix] # deepcopy -\u0026gt; int for i in range(1, len(dp)): for j in range(1, len(dp[0])): dp[i][j] = self.get_max_square(dp, i, j) # print(dp) # print(max(max(x) for x in dp)) return max(max(x) for x in dp)**2 def get_max_square(self, dp, i, j): if dp[i][j] == 0: return 0 # must be 1 return min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])+1 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 概念參考自：\n[Python] Thinking Process Diagrams - DP Approach 我們新建一個 dp，專門來記錄右下點的最大正方形「邊長」，\n這個「邊長」，我們可以推出規律為：\n如果該格是 0，就是 0 (連 1*1 正方形都不可能) 如果該格是 1，判斷 min([i-1][j-1], [i-1][j], [i][j-1]) + 1 ，即為此格， 意義為，如果任一格為 0，表示缺一角\n如果情況為 1,2,2 也是，表示有缺一角，但仍滿足 2*2 的情況 = min(1,2,2)+1\ninput handling 處理沒有 matrix 的情況，return 0\nBoundary conditions 控制 for-loop 的範圍\nReference 最大正方形 · Maximal Square\n[Python] Thinking Process Diagrams - DP Approach\nHow to map array of string to int in Python?\nHow to convert 2D string list to 2D int list python? [duplicate]\n","date":"2022-05-04T02:46:19+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0341.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/bfs-topological-sort/leetcode-python-221/","tags":["BFS","DP"],"title":"【Leetcode】python - [221] Maximal Square 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 91. Decode Ways\n難度 medium\n個人範例程式碼 class Solution: def numDecodings(self, s: str) -\u0026gt; int: if not s: return 0 if s[0] == \u0026#34;0\u0026#34;: return 0 # dp[i] = (dp[i-1], contain this word) + (dp[i-1], contain two word) dp = [1, 1] for i, c in enumerate(s): ans_contain_this_word = 0 if self.is_valid(s[i]): # dp[i-1] + i, check if 0 ans_contain_this_word += (dp[-1]) if i \u0026gt; 0 and self.is_valid_2(s[i-1:i+1]): # dp[i-2] + i-1,i ans_contain_this_word += (dp[-2]) dp.append(ans_contain_this_word) return dp[-1] def is_valid_2(self, s): if s[0] == 0: return False return 10 \u0026lt;= int(s) \u0026lt;= 26 def is_valid(self, s): return 1 \u0026lt;= int(s) \u0026lt;= 9 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 類似 fibonacci 數列的題目，這樣就是要用 dp 去解啦!\ndp[i] = (dp[i-1], 現在數字) + (dp[i-2], 最後兩位數字)\ndp[i-1] 我們去確認現在數字是否 1~9，排除 0\ndp[i-2] 我們去確認是否兩位數字介於 10~26，排除 0 開頭與其他不符合規則的數字\ninput handling 處理沒有 s 或是「 s 以 0 為開頭的情況」， return 0 Boundary conditions 控制 for-loop 的範圍\nReference 解码方法 · Decode Ways ","date":"2022-05-03T03:20:40+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-91/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [91] Decode Ways 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2259. Remove Digit From Number to Maximize Result\n難度 easy\n個人範例程式碼 class Solution: def removeDigit(self, number: str, digit: str) -\u0026gt; str: if not number or not digit: return -1 if digit not in number: return -1 max_num = 0 for i in range(len(number)): if number[i] == digit: max_num = max(max_num, int(number[:i]+number[i+1:])) return str(max_num) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 先來說明一下題目的意思，題目敘述有點小複雜，\n簡單來說就是，digit 是我們要刪除的目標，\n從原本的 number 中刪除一個 digit 使結果最大。\n因為是 easy，應該窮舉直接比較大小即可。\ninput handling 處理 digit 不在 number 的情況， return -1 處理 not digit or not number 的情況， return -1 Boundary conditions 控制 for-loop 的範圍\nReference python - easy solution ","date":"2022-05-03T02:20:50+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2259/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2259] Remove Digit From Number to Maximize Result 個人解法筆記 | 291th LeetCode Weekly Contest"},{"categories":["Two pointers (同向雙指針 →→) / Sliding window"],"content":"題目出處 2260. Minimum Consecutive Cards to Pick Up\n難度 medium\n個人範例程式碼 class Solution: def minimumCardPickup(self, cards: List[int]) -\u0026gt; int: visited = set() min_window = float(\u0026#34;inf\u0026#34;) slow, fast = 0, 0 for fast, card in enumerate(cards): if card not in visited: visited.add(card) else: while card in visited: visited.remove(cards[slow]) slow += 1 visited.add(card) min_window = min(min_window, len(visited) + 1) # add duplicate return -1 if min_window == float(\u0026#34;inf\u0026#34;) else min_window 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 用同向的 two pointers slow, fast 控制頭尾，\n控制 sliding window 的移動\nfast 往前走，slow 走在後，\n發現有重複的項目時，slow 一直 pop 直到去掉重複\n計算 window 大小時，計算 fast-slow+2 (+1 是範圍的關係，+1 是計算去掉的重複項)\ninput handling 如果沒有找到結果，回傳 -1\n(沒有重複文字的 window)\nBoundary conditions 控制 fast for-loop 的範圍\nReference [Python] O(n) time \u0026amp; space, by using dict ","date":"2022-05-03T02:16:03+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-sliding-window/leetcode-python-2260/","tags":["Sliding","window"],"title":"【Leetcode】python - [2260] Minimum Consecutive Cards to Pick Up 個人解法筆記 | 291th LeetCode Weekly Contest"},{"categories":["DFS (排列 permutation)","Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 2261. K Divisible Elements Subarrays\n難度 medium\n個人範例程式碼 - 同向 two pointers, 正三角 class Solution: def countDistinct(self, nums: List[int], k: int, p: int) -\u0026gt; int: self.ans = set() # decide start (one side) for i in range(len(nums)): self.dfs(nums, i, i, k, p) # decide end # print(self.ans) return len(self.ans) def dfs(self, nums, start, end, k, p): # end of recursion if end \u0026gt;= len(nums): return # define if nums[end] % p == 0: k -= 1 if k \u0026lt; 0: return self.is_valid_ans(nums, start, end, k, p) # split self.dfs(nums, start, end+1, k, p) def is_valid_ans(self, nums, start, end, k, p): if k \u0026lt; 0: return subset = tuple(nums[start:end+1]) # start~end # print(subset, k) if subset in self.ans: return else: self.ans.add(subset) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 用 dfs 搭配同向的 two pointers 控制頭尾(dfs 控制尾巴移動)，組出全部的結果。\ninput handling x (題目沒有特別要求要處理)\nBoundary conditions 控制 recursion 結束條件 if end \u0026gt;= len(nums)\n個人範例程式碼 - 反向 two pointers, 反三角 class Solution: def countDistinct(self, nums: List[int], k: int, p: int) -\u0026gt; int: self.ans = set() # decide start (one side) for i in range(len(nums)-1, -1, -1): self.dfs(nums, i, i, k, p) # decide end # print(self.ans) return len(self.ans) def dfs(self, nums, start, end, k, p): # end of recursion if start \u0026lt; 0: return # define if nums[start] % p == 0: k -= 1 if k \u0026lt; 0: return self.is_valid_ans(nums, start, end, k, p) # split self.dfs(nums, start-1, end, k, p) def is_valid_ans(self, nums, start, end, k, p): if k \u0026lt; 0: return subset = tuple(nums[start:end+1]) # start~end # print(subset, k) if subset in self.ans: return else: self.ans.add(subset) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 用 dfs 搭配反向的 two pointers 控制頭尾(dfs 控制頭移動)，組出全部的結果。\ninput handling x (題目沒有特別要求要處理)\nBoundary conditions 控制 recursion 結束條件 if start \u0026lt; 0\nReference [Python3] DFS ","date":"2022-05-03T02:09:01+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0340.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-permutation/leetcode-python-2261/","tags":["DFS"],"title":"【Leetcode】python - [2261] K Divisible Elements Subarrays 個人解法筆記 | 291th LeetCode Weekly Contest (內含 substring 常見作法)"},{"categories":["DP (座標型 - 一維)","DFS (全部方案)"],"content":"題目出處 2262. Total Appeal of A String\n難度 hard\n個人範例程式碼 - dfs 解 (會 TLE) class Solution: def appealSum(self, s: str) -\u0026gt; int: if not s: return 0 self.ans = 0 # decide start for i in range(len(s)): self.dfs(s, i, i, set()) # decide end return self.ans def dfs(self, s, start, end, wordset): # s[start:end+1] start~end # end of recursion if end \u0026gt;= len(s): return # define if s[end] in wordset: pass else: wordset.add(s[end]) self.ans += len(wordset) # split self.dfs(s, start, end+1, wordset) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 dfs 解法本身概念是對的，如果這題目最終要的是全部結果的窮舉，那一定是用 dfs 去組。\n而這題目只需要數數量，因此 dfs 雖然方向對了，但可以透過一些手段加速。\ninput handling 如果 input 是 \u0026ldquo;\u0026quot;，return 0 (題目沒有要求要處理)\nBoundary conditions 控制 recursion 結束條件 if end \u0026gt;= len(s)\n個人範例程式碼 - DP 解 class Solution: def appealSum(self, s: str) -\u0026gt; int: # dp[i] = dp[i-1] + newword dp_contain_thisword = [0] worddict = {} for i, c in enumerate(s): if c not in worddict: dp_contain_thisword.append(dp_contain_thisword[-1] + i+1) else: dp_contain_thisword.append(dp_contain_thisword[-1] + i - worddict[c]) worddict[c] = i # print(dp_contain_thisword) return sum(dp_contain_thisword) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 原作者的說明：\nDP 我們將原來的題目視作，dp[i] = dp[i-1] + i 的格式\n也就是說\n- 「包含此字的所有組合」=「包含前一個字的所有組合」+ 此字 - 而「全部組合」= 「包含此字的所有組合」總和 「包含此字的所有組合」=「包含前一個字的所有組合」+ 此字 正常情況，不重覆字就是「加上 idx+1」 (+1 是因為 0 開始) 有重複情況，我們可以視為，「重複之前的字母，都會少算 1 次」，因此需要多減去 prev[idx] input handling 如果 input 是 \u0026ldquo;\u0026quot;，return 0 (題目沒有要求要處理)\nBoundary conditions 控制 for loop\n個人範例程式碼 - 歸納法 解 class Solution: def appealSum(self, s: str) -\u0026gt; int: letter_contain_thisword = [] # 1*5, 2*4, 3*3 = left_len(a,ab,abc) * right_len(c,cd,cde) # (a,ab,abc,abca) * (a, ab) = 4*2 -\u0026gt; (4-1)*2, since abca = bca (abca,bca,ca,a) = (bca,ca,a) prev_idx = {} for i, c in enumerate(s): if c in prev_idx: letter_contain_thisword.append((i - prev_idx[c])*(len(s)-i)) else: letter_contain_thisword.append((i + 1)*(len(s)-i)) prev_idx[c] = i return sum(letter_contain_thisword) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 原作者的說明：\nCombinatorics 這個思維的出發點是「計算包含此字母的 substring 共有幾個」\n正常情況 我們計算的是「包含此字的所有 substring」，因此\nabcde = 15 + 24 + 33 + 42 + 5*1\n(a) * (a,ab,abc,abcd,abcde)\n(ab,b) * (b,bc,bcd,bcde)\n(abc,bc,c) * (c,cd,cde)\n重複情況 我們計算的是「包含此字的所有 substring」，因此\nabcab = 15 + 24 + 3*3 + (3-0)*2 + (4-1)*1\n(abca,bca,ca,a)(a,ab)\n(abcab,bcab,cab,ab,b)(b)\n因為重複的關係，因此 (abca,bca,ca,a) = (bca,ca,a)，\n只要是第一個 a 以前的都算在重複的範圍 (都要少算 1)，因此可以推得 「i - prev[c]」\ninput handling 如果 input 是 \u0026ldquo;\u0026quot;，return 0 (題目沒有要求要處理)\nBoundary conditions 控制 for loop\nReference Combinatorics DP ","date":"2022-05-03T01:17:30+08:00","image":"https://wongwongnotes.com/images/restored/2022/05/img_0338.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/dynamic-programming/dp/leetcode-python-2262/","tags":["DFS","DP"],"title":"【Leetcode】python - [2262] Total Appeal of A String 個人解法筆記 | 291th LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 264. Ugly Number II\n難度 Medium\n個人範例程式碼 class Solution: def nthUglyNumber(self, n: int) -\u0026gt; int: if not n: return -1 visited = set([1]) heap = [1] for _ in range(n): cur = heapq.heappop(heap) for factor in [2, 3, 5]: new = cur * factor if new not in visited: visited.add(new) heapq.heappush(heap, new) return cur 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這裡我們用 minheap 來解，因為要不斷的輸出最小，\n透過 minheap 減少我們搜尋的時間。\ninput handling 如果 input 是 0，return -1 (題目沒有要求要處理)\nBoundary conditions for 迴圈控制數量\nReference 丑数 II · Ugly Number II ","date":"2022-04-30T01:49:02+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-264/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [264] Ugly Number II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 642 · Moving Average from Data Stream\n難度 easy\n個人範例程式碼 class MovingAverage(object): \u0026#34;\u0026#34;\u0026#34; @param: size: An integer \u0026#34;\u0026#34;\u0026#34; def __init__(self, size): # do intialization if necessary self.size = size self.queue = collections.deque([]) self.sum = 0.0 \u0026#34;\u0026#34;\u0026#34; @param: val: An integer @return: \u0026#34;\u0026#34;\u0026#34; def next(self, val): # write your code here self.queue.append(val) self.sum += val if len(self.queue) \u0026gt; self.size: self.sum -= self.queue.popleft() return self.sum/len(self.queue) # Your MovingAverage object will be instantiated and called as such # obj = MovingAverage(size) # param = obj.next(val) 算法說明 Data Stream 的題型，基本上看到 data stream 就要先有意識到，\n現在我們要設計的是一個「動態」的數據結構，\n也就是說輸入的資料不會是「一次性的」，而是「連續性的」\n有這個概念之後我們就可以開始來設計了，\n這題還算簡單，不需要講太多\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x (題目沒有特別要求)\nBoundary conditions 因為不是一次性操作，因此沒有邊界限制，\n而動態的操作要注意「允許儲存的上限 = size」\nReference 数据流滑动窗口平均值 · Moving Average from Data Stream ","date":"2022-04-29T03:51:44+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-642/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [642] Moving Average from Data Stream 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 242. Valid Anagram\n難度 easy\n題目分類 Hash table, string, sorting\n個人範例程式碼 - 2022/4/28 (與 2022/3/4 解法完全相同) class Solution: def isAnagram(self, s: str, t: str) -\u0026gt; bool: return Counter(s) == Counter(t) Time Complexity O(n)\nSpace Complexity x\n算法說明 這題目碰到 python Counter，一行再見\u0026hellip; python 太強大了。\n這題就是計算數量，看有沒有相等，相等就是 True，\n而 python collection 內建的 Counter function，\n快速幫我們把計算數量的 hash dictionary 完成了。\n如果還不知道 python Counter 用法的，「強烈建議」一定要會，寫程式效率會快超級多！！！\n可參考我的另外一篇文：\n【Python】python counter() 用法整理 – 快速計算資料內容的數量\n我們就不用再另外寫 hash table 做 count 的動作了。\ncorner case 特殊情況處理 x\nBoundary conditions/ Edge conditions 邊際情況處理 x\nReference ","date":"2022-04-28T13:17:18+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-242/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [242] Valid Anagram 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 55. Jump Game\n難度 Medium\n個人範例程式碼 一開始建了一個新的答案的 list class Solution: def canJump(self, nums: List[int]) -\u0026gt; bool: if len(nums) \u0026lt;= 1: return True max_can_reach_idx = [0 for _ in range(len(nums))] for i, num in enumerate(nums): if i == 0: max_can_reach_idx[0] = num+i if i \u0026gt; 0 and max_can_reach_idx[i-1] \u0026gt;= i: max_can_reach_idx[i] = max(max_can_reach_idx[i-1], num+i) return max_can_reach_idx[-1] \u0026gt; 0 後來發現可以簡化至單一個 max_idx 即可，並當發現不行時，提早 early return class Solution: def canJump(self, nums: List[int]) -\u0026gt; bool: max_idx = 0 for i, num in enumerate(nums): if i \u0026gt; max_idx: # can not reach, first 0 \u0026gt; 0 return False max_idx = max(num+i, max_idx) return max_idx \u0026gt;= len(nums)-1 # 4 \u0026gt;= 5-1 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 透過建立一個新的 list，我們紀錄\n- 前一個 idx 最大可抵達的位置 - 相比前一個 idx 最大可抵達位置，看下一個位置是否能抵達，同時取 max(此位置能抵達的最遠, 上一個位置能抵達的最遠) input handling 如果 input len \u0026lt;= 1，return True\nBoundary conditions 後來優化的版本中，其實當發現 max_idx 已經到不了的時候，就已經可以 early return 了\n第一個版本是一定會算完\nReference 1-6 lines, O(n) time, O(1) space ","date":"2022-04-28T04:32:36+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-55/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [55] Jump Game 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 53. Maximum Subarray\n難度 Easy\n題目分類 Array, Divide and Conquer, Dynamic Programming\n個人範例程式碼 - 2022/4/28 class Solution: def maxSubArray(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 current_max_with_this = [nums[0]] for i in range(1, len(nums)): current_max_with_this.append(max(current_max_with_this[i-1]+nums[i], nums[i])) return max(current_max_with_this) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 建一個新的 array，記錄\u0026quot;包含自己\u0026quot;的當前最大用\n當前最大 = max(當前數字, 當前數字+到前一個之前最大)\ninput handling 如果 input not nums (沒有數字)，return 0\nBoundary conditions for 控制範圍\n個人範例程式碼 - 2022/2/24 class Solution: def maxSubArray(self, nums: List[int]) -\u0026gt; int: dp = [] # dp = the biggest answer \u0026#39;include me\u0026#39; for i in range(len(nums)): if i == 0: dp.append(nums[i]) else: bigger = max(nums[i], nums[i]+dp[i-1]) dp.append(bigger) return max(dp) 說明 這題我使用的是 DP 的解法，\n我宣告一個 DP 來儲存「包含自己的」時最好的答案。\n換句話說，我將題目拆解成到「包含每一個 index 的當下」 都是一個小題目，將題目的答案儲存進 dp 中。\n注意是「包含自己」的最好答案，也就是說，我們只需要比較「自己」與「dp(n-1)+自己」誰比較大。\nDP 裡面都是「一定要包含自己的最佳解」，而不是「全域的最佳解」。\n所以假設原來的題目變成：\nnums = [1,2,-1,4]\ndp = [1,3,2,6]\n2 就是因為一定要包含「-1」，我們只有「-1」跟「-1+\u0026lsquo;包含前一個\u0026rsquo;的最大」誰比較大。\n","date":"2022-04-28T04:16:13+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-53/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [53] Maximum Subarray 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 739. Daily Temperatures\n難度 Medium\n個人範例程式碼 class Solution: def dailyTemperatures(self, temperatures: List[int]) -\u0026gt; List[int]: if not temperatures: return [] stack = [] ans = [0 for _ in range(len(temperatures))] for today, temperature in enumerate(temperatures): while stack and temperatures[stack[-1]] \u0026lt; temperature: # today\u0026#39;s temperature is higher idx = stack.pop(-1) ans[idx] = today - idx stack.append(today) return ans 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 stack 的經典題型，透過 stack 紀錄沿路上的過去史，\n並不斷地以「今日」去比對「過去尚未產生結果的結果」\npop 至過去結果溫度「比今日小的所有結果」，相距日期則為「today - 該日」 input handling 如果沒有 input，return []\nBoundary conditions for 搜尋所有的天數，\n如果過程中有發現更高的溫度，就去更新結果。\nReference 每日温度 · Daily Temperatures ","date":"2022-04-28T02:28:45+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0336.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-739/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [739] Daily Temperatures 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 https://leetcode.com/problems/longest-substring-without-repeating-characters/\n難度 Medium\n題目分類 HashTable, TwoPointers, String, SlidingWindow\n個人範例程式碼 - 2022/4/27 class Solution: def lengthOfLongestSubstring(self, s: str) -\u0026gt; int: if not s: return 0 fast = 0 window_element = set() max_length = 0 for slow in range(len(s)): while fast \u0026lt; len(s) and s[fast] not in window_element: window_element.add(s[fast]) max_length = max(max_length, len(window_element)) fast += 1 window_element.remove(s[slow]) return max_length 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 使用 sliding window 的概念，我們需要使用 同向的 pointer，\n思路很簡單，我們透過建立一個 window_element，裡面代表著現在我們鎖定的元素範圍，\n而兩個 pointer：\nfast，走在前，不斷收集不重複元素 slow，走在後，當無法蒐集不重複元素時，開始邊移動，並移除 window 的元素 input handling 如果沒有 s，return 0\nBoundary conditions 搜尋至 len(s) 的範圍，\n兩個 pointer 用 for-loop, while 來判斷範圍\n個人解法筆記 - 2021/3/31 個人範例程式碼 class Solution: def lengthOfLongestSubstring(self, s: str) -\u0026gt; int: if len(s) == 0: ans = 0 else: ans = 0 queue_list = [] l = 0 # left pointer for r in range(len(s)): # s[r], get one new word # found duplicate in sliding window, remove until duplicate not found while s[r] in queue_list: queue_list.pop(0) l += 1 # not found or all duplicate removed, push new word s[r] queue_list.append(s[r]) # update answer ans = max(ans, r-l+1) return ans sliding window 正如其名，移動的窗戶，也就是表示在 window 範圍內必須符合我們要的答案。\ntwo pointers 我們建立兩個指標 l,r，\nr 是正常的向前搜尋，\nl 是當目前 l,r 所框出的 window 範圍出現不符合題目要求時，「反覆」移動 l 直到 window 符合題目要求。\n示意圖：(r先跑，l依照條件變化而跑) 示意圖：(l依照條件變化而跑，為了符合條件) 我們可以發現當下長度 = (r-l) + 1\nHashTable 示意圖：(用 Queue 來儲存已存在的 window 中的字) 在搜尋目前 window 是否符合條件時，我們需要建立一個 HashTable，幫助我們速查特定值是否存在表裡面，\n我們建立的 HashTable 也需要達到 Queue 的功能 (因為隨著 l 的移動，會有先進先出的問題)，\n我們可以看到，當 b 進來時，我們必須一直丟到 Queue 裡面沒有 b 為止，「才是正確的 window 」\nReference https://leetcode.com/problems/longest-substring-without-repeating-characters/discuss/1134357/O(n)-Time-and-Space-Sliding-Window https://www.youtube.com/watch?v=wiGpQwVHdE0 ","date":"2022-04-27T10:24:26+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/IMG_7729-1024x768.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-3/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [3] Longest Substring Without Repeating Characters 個人解法筆記 (updated: 2022/4/27)"},{"categories":["DFS (全部方案)"],"content":"題目出處 79. Word Search\n難度 medium\n個人範例程式碼 DIRECTIONS = [ (1, 0), (-1, 0), (0, 1), (0, -1) ] class Solution: def exist(self, board: List[List[str]], word: str) -\u0026gt; bool: if not board: return False # first point for i in range(len(board)): for j in range(len(board[0])): if board[i][j] == word[0] and self.dfs(board, word[1:], i, j, {(i,j)}): return True return False # after all search def dfs(self, board, word, i, j, visited): # end of recursion if not word: return True # define and split for (dx, dy) in DIRECTIONS: next_x, next_y = i+dx, j+dy # print(next_point) if self.is_valid_point(next_x, next_y, board, visited, word[0]): visited.add((next_x, next_y)) if self.dfs(board, word[1:], next_x, next_y, visited): return True visited.remove((next_x, next_y)) # backtracking return False # def dfs(self, board, word, start_point, visited) # # end of recursion # if not word # return True # # define and split # for (dx, dy) in DIRECTIONS # next_point = (start_point[0]+dx, start_point[1]+dy) # # print(next_point) # if self.is_valid_point(next_point, board, visited, word[0]) # visited.add(next_point) # if self.dfs(board, word[1:], next_point, visited) # return True # visited.remove(next_point) # backtracking # return False def is_valid_point(self, x, y, board, visited, word): if (x, y) in visited: return False r = len(board) c = len(board[0]) return 0 \u0026lt;= x \u0026lt; r and 0 \u0026lt;= y \u0026lt; c and board[x][y] == word 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題是學習矩陣內的四方向搜尋，類似題目可參考：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-54/\n不過這題目有個難點是很容易 TLE，在處理上我們需要稍微優化一下：\n優化比較的項目長度 例如說可能不要比較整個 prefix，而是只比較單一個 word\n不要使用 tuple 實作 我註解掉的部分是使用 tuple 實作的，概念上應該會更清楚，\n但因為會影響到時間，後來取消掉註解後才沒有 TLE\ninput handling 當沒有 matrix 時，return False\nBoundary conditions 搜尋到目標後，就可以 return True 了。\n沿路上發現字母不對，就直接 return False\nReference 单词搜索 · Word Search ","date":"2022-04-27T04:05:03+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-79/","tags":["DFS"],"title":"【Leetcode】python - [79] Word Search 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 LeetCode 版本，標準非常高，基本上沒實作出 Trie 一定 TLE： [212. Word Search II](https://leetcode.com/problems/word-search-ii/) LintCode 版本，基本版先通過這個就好 (本文的內容)： [132 · Word Search II](https://www.lintcode.com/problem/132/) 難度 hard\n個人範例程式碼 from typing import ( List, ) DIRECTIONS = [ (1, 0), (0, 1), (-1, 0), (0, -1) ] class TrieNode(): def __init__(self): self.children = {} self.isWord = False class Trie(): def __init__(self): self.root = TrieNode() def insert(self, word): node = self.root for c in word: if c not in node.children: node.children[c] = TrieNode() node = node.children[c] node.isWord = True def search(self, word): node = self.root for c in word: if c in node.children: node = node.children[c] else: return False return node.isWord def startswith(self, prefix): # print(prefix) node = self.root for c in prefix: if c in node.children: node = node.children[c] else: return False return True class Solution: \u0026#34;\u0026#34;\u0026#34; @param board: A list of lists of character @param words: A list of string @return: A list of string \u0026#34;\u0026#34;\u0026#34; def word_search_i_i(self, board: List[List[str]], words: List[str]) -\u0026gt; List[str]: if not board: return [] # self.prefix_set = self.make_prefix_set(words) self.trie = Trie() for word in words: self.trie.insert(word) self.ans = [] for x in range(len(board)): for y in range(len(board[0])): self.dfs(board, words, x, y, set(), \u0026#34;\u0026#34;) return self.ans def dfs(self, board, words, x, y, visited, prefix): # print(prefix) # end of recursion if not words: # all finished return if prefix in words: self.ans.append(prefix[:]) words.remove(prefix[:]) # prevent duplicate # if prefix != \u0026#34;\u0026#34; and prefix not in self.prefix_set: if prefix != \u0026#34;\u0026#34; and not self.trie.startswith(prefix): return # define and split if not self.is_in_bound(x, y, board): return if (x,y) in visited: return visited.add((x, y)) for dx, dy in DIRECTIONS: self.dfs(board, words, x+dx, y+dy, visited, prefix+board[x][y]) visited.remove((x, y)) # backtracking def is_in_bound(self, x, y, board): m, n = len(board), len(board[0]) return 0 \u0026lt;= x \u0026lt; m and 0 \u0026lt;= y \u0026lt; n # def make_prefix_set(self, words) # prefix_set = set() # for word in words # for i in range(len(word)) # prefix_set.add(word[:i+1]) # return prefix_set 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 此題目的前一題為「單純找一個單字」就好，並只需要回傳 True/False，可參考： https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-79/\n非常難的題目，最主要難的點是在會 TLE 的部分，特別是 LeetCode 設定的標準，\n對使用 python 而言非常的嚴苛。\n最主要是實作 Trie 的內容與矩陣的四方向移動，\n在矩陣的四方向移動，可以先參考以下比較簡單的題目：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-54/\n注意：可繼續搜尋的項目 例如： [\u0026ldquo;app\u0026rdquo;, \u0026ldquo;apple\u0026rdquo;]，不能搜尋到 app 就 return，會找不到 apple\ninput handling 當沒有 matrix 時，return []\nBoundary conditions 用 Trie 判斷全部搜尋結果是很簡單的，只是需要各種的搶時間以避免 TLE。\nReference 這邊我試跑 Leetcode 其他人的解答也幾乎都 TLE\u0026hellip;，這 Leetcode 標準到底設定有多硬\u0026hellip;\nPython dfs solution (directly use Trie implemented). 27 lines, uses complex numbers [Python] Trie solution with dfs, explained 单词搜索 II · Word Search II ","date":"2022-04-27T04:00:00+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-212/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [212] Word Search II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 54. Spiral Matrix\n難度 medium\n個人範例程式碼 MOVES = [ (0, 1), (1, 0), (0, -1), (-1, 0) ] class Solution: def spiralOrder(self, matrix: List[List[int]]) -\u0026gt; List[int]: if not matrix: return [] x, y = 0, 0 direction = 0 visited = {(0, 0)} ans = [matrix[0][0]] total_num = len(matrix) * len(matrix[0]) while(len(ans) \u0026lt; total_num): next_x, next_y = x + MOVES[direction][0], y + MOVES[direction][1] if self.isvalid(next_x, next_y, matrix, visited): ans.append(matrix[next_x][next_y]) visited.add((next_x, next_y)) x, y = next_x, next_y else: direction = (direction + 1)%4 return ans def isvalid(self, x, y, matrix, visited): r = len(matrix) c = len(matrix[0]) return 0 \u0026lt;= x \u0026lt; r and 0 \u0026lt;= y \u0026lt; c and (x, y) not in visited 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 螺旋狀的 traverse 矩陣，\n記得設定好邊界條件，就可以處理\ninput handling 當沒有 matrix 時，return []\nBoundary conditions 要處理的條件有 - x, y 邊界 - 有沒有 visited 而結束的判斷條件為 while len(ans) \u0026lt; total_nums，\n當得到全部的結果後，就會結束迴圈。\nReference ","date":"2022-04-27T03:46:15+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-54/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [54] Spiral Matrix 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 829 · Word Pattern II\n難度 hard\n個人範例程式碼 class Solution: \u0026#34;\u0026#34;\u0026#34; @param pattern: a string,denote pattern string @param str: a string, denote matching string @return: a boolean \u0026#34;\u0026#34;\u0026#34; def word_pattern_match(self, pattern: str, str: str) -\u0026gt; bool: # write your code here self.pattern_to_str = {} self.str_to_pattern = {} return self.dfs(pattern, str, 0) def dfs(self, pattern, str, start): # print(pattern, start, self.pattern_to_str) # end of recursion if not pattern: if start == len(str): return True return False # define and split # match pattern if pattern[0] in self.pattern_to_str: match_pattern = self.pattern_to_str[pattern[0]] end_idx = start + len(match_pattern) if end_idx \u0026lt;= len(str) and match_pattern == str[start:end_idx]: return self.dfs(pattern[1:], str, end_idx) # to end_idx-1, from end_idx else: # pattern not match return False # find next word for i in range(start+1, len(str)+1): substr = str[start:i] # to i-1 if substr in self.str_to_pattern: if not self.str_to_pattern[substr] == pattern[0]: # match to another character continue # find next else: # add new word self.pattern_to_str[pattern[0]] = substr self.str_to_pattern[substr] = pattern[0] if self.dfs(pattern[1:], str, i): # to i-1, from i return True # backtracking del self.pattern_to_str[pattern[0]] del self.str_to_pattern[substr] return False 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 這題與前一題的差別為，前一題已經都幫忙把 string 切好了，而這題目連切都要自己切，還考了怎麼切： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-290/\n這題我們透過 dfs 的方式不斷的去進行往下的切割，直到完成\n注意要判斷是否結束字時 pattern 已經使用完 有一種特殊狀況是 \u0026ldquo;aaa\u0026rdquo; 與 \u0026ldquo;aaaa\u0026rdquo;\n如果沒有特別處理，容易會漏掉最後一個配對，卻回傳 True。\n這邊我們是判斷 start = len(str)，因為我們的 start 會不斷移動，\n最後當移動超過範圍時。就是找到了正確結果的情況下，結束了我們的配對任務。\ninput handling x\nBoundary conditions 上方說明了，當 DFS 的 start point 超過了可搜尋範圍，就結束 DFS。\nReference 字模式 II · Word Pattern II ","date":"2022-04-26T22:15:50+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-829/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [829] Word Pattern II 個人解法筆記"},{"categories":["BFS (最短路徑)","DFS (全部方案)"],"content":"題目出處 126. Word Ladder II\n難度 hard\n個人範例程式碼 class Solution: def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -\u0026gt; List[List[str]]: if endWord not in wordList: return [] wordList.append(beginWord) distance = {} self.bfs(endWord, distance, wordList) # from end to start self.ans = [] self.dfs(beginWord, endWord, distance, wordList, [beginWord]) # from start to end return self.ans def bfs(self, start, distance, worddict): distance[start] = 0 queue = deque([start]) while queue: word = queue.popleft() print(self.get_next_words(word, worddict)) for next_word in self.get_next_words(word, worddict): if next_word not in distance: distance[next_word] = distance[word] + 1 queue.append(next_word) def get_next_words(self, word, worddict): words = [] for i in range(len(word)): for c in \u0026#39;abcdefghijklmnopqrstuvwxyz\u0026#39;: next_word = word[:i] + c + word[i+1:] if next_word != word and next_word in worddict: words.append(next_word) # print(words) return words def dfs(self, cur, target, distance, worddict, path): # print(distance) if cur == target: self.ans.append(path[:]) return for word in self.get_next_words(cur, worddict): # different way will see other things if word not in distance: # single node continue if distance[word] != distance[cur] - 1: continue path.append(word) self.dfs(word, target, distance, worddict, path) path.pop() # backtracking 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n算法說明 超難的一題，考了非常多概念，對細節處理的要求也非常多\n我們先從建立圖片下手。\n從 BFS 先找到最短路徑 題目的做成的文字圖有可能會有循環，我們需要先找最短路徑\n透過最短路徑，反向 DFS 尋找所有結果 知道最短路徑後，我們才能用 DFS 將整個圖走過，\n看在最短路徑的範圍當中，能夠找到有幾種結果。\n先從 endWord 找回 start，再從 start 順便存路徑會最順。 注意處 從兩個起點搜尋的注意事項 這邊要處理在從兩端搜尋時，還是都要把每個點都看過一次，\n例如上圖中下方的 graph 結構，我們會發現會有一些只靠單向搜尋「會距離很遠」的位置\n獨立的點 有一種特殊的 case 是從起點開始，就根本就沒有任何的連線，\n雖然這題的測資沒有考到，但這邊一樣有處理。\n狀況大概如下圖： 只能說，這個就是細節處理的部分，萬一真的有這樣的情況，我們還是要回傳 []\ninput handling 如果 endWord 不在 worddict 中，return []\nBoundary conditions BFS 找最短路徑 搜尋至 queue 沒有東西的時候\n「while queue」幫忙控制結束\nDFS 找所有結果 反向搜尋至 distance 為 0 時結束，\n我們在上一步驟中的 BFS，有順便記錄了 distance 最大的長度，\n這邊就是反向操作到 0。\nReference 单词接龙 II · Word Ladder II How can I remove a key from a Python dictionary? ","date":"2022-04-26T22:04:21+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0335.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/bfs/leetcode-python-126/","tags":["BFS","DFS"],"title":"【Leetcode】python - [126] Word Ladder II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 290. Word Pattern\n難度 easy\n個人範例程式碼 class Solution: def wordPattern(self, pattern: str, s: str) -\u0026gt; bool: if not s: return True wordlist = s.split(\u0026#34; \u0026#34;) worddict = {} patterndict = {} if not len(wordlist) == len(pattern): return False for idx, each_pattern in enumerate(pattern): if each_pattern in patterndict: if not patterndict[each_pattern] == wordlist[idx]: return False if wordlist[idx] in worddict: if not worddict[wordlist[idx]] == each_pattern: return False patterndict[each_pattern] = wordlist[idx] worddict[wordlist[idx]] = each_pattern return True 算法說明 這題是考簡單的字串處理，但細節要注意很多，很難一次全對\n確認同一 pattern 無重複配對 (word = a, b) 這個是要注意 word = a 且 word = b 要 return 為 False 的情況\n確認同一 word 無重複配對 (word, code = a) 這個是要注意 word = a 且 code = a 要 return 為 False 的情況\n確認本身長度不同的問題 這個是要注意 \u0026ldquo;aaa\u0026rdquo; 不等於 \u0026ldquo;aa aa\u0026rdquo;，等類似的狀況，需要多判斷長度是否相同。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 s，return True\nBoundary conditions for loop 控制範圍\nReference Short in Python 字模式 · Word Pattern ","date":"2022-04-26T16:18:50+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-290/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [290] Word Pattern 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2249. Count Lattice Points Inside a Circle\n難度 medium\n個人範例程式碼 class Solution: def countLatticePoints(self, circles: List[List[int]]) -\u0026gt; int: self.ans_set = set() for circle in circles: self.get_lattice_for_each(circle) return len(self.ans_set) def get_lattice_for_each(self, circle): cx, cy, r = circle for x in range(cx, cx+r+1): for y in range(cy, cy+r+1): if (cx - x)**2 + (cy - y)**2 \u0026lt;= r**2: self.ans_set.add((x, y)) self.ans_set.add((x-2*(x-cx), y)) self.ans_set.add((x, y-2*(y-cy))) self.ans_set.add((x-2*(x-cx), y-2*(y-cy))) 算法說明 搜尋範圍內有哪些點，直接的去抓 top, down, left, right，並找尋出現的點\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n速度優化 這邊我們可以進行一次數學加速，我們只需要分析 1/4 圓，就能進行後續分析。\n優化前 def get_lattice_for_each(self, circle): x, y, r = circle down, top = y-r, y+r left, right = x-r, x+r for c_x in range(left, right+1): for c_y in range(down, top+1): if (x - c_x)**2 + (y - c_y)**2 \u0026lt;= r**2: self.ans_set.add((c_x, c_y)) 優化後 def get_lattice_for_each(self, circle): cx, cy, r = circle for x in range(cx, cx+r+1): for y in range(cy, cy+r+1): if (cx - x)**2 + (cy - y)**2 \u0026lt;= r**2: self.ans_set.add((x, y)) self.ans_set.add((x-2*(x-cx), y)) self.ans_set.add((x, y-2*(y-cy))) self.ans_set.add((x-2*(x-cx), y-2*(y-cy))) 錯誤的討論 這邊我第一次不小心想的太單純，順便記錄一下，\n我一開始想說直接把 (cx, cy) 當原點，然後直接加即可，\n問題在於「計算出的 (x, y)」，「並不是從 (cx, cy) 出發，而是從 (0, 0) 出發」，\n因此才會導致不能直接像我們下圖想的那麼單純，直接減 x\ninput handling 如果沒有 nums，return nums\nBoundary conditions for loop 控制範圍\nReference [Java/C++/Python] O(1) Space ","date":"2022-04-26T15:01:01+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0332.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2249/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2249] Count Lattice Points Inside a Circle 個人解法筆記 | 290th LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2248. Intersection of Multiple Arrays\n難度 easy\n個人範例程式碼 class Solution: def intersection(self, nums: List[List[int]]) -\u0026gt; List[int]: if not nums: return nums ans = set(nums[0]) for num in nums: ans \u0026amp;= set(num) return sorted(list(ans)) 算法說明 原本以為是要用 priotity queue 或 pointer 解的題目，\n後來發現題目沒有預先排序，那直接暴力解吧，\n全部轉 set() 後直接取交集。\n得到結果的 set() 之後，記得排序。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 nums，return nums\nBoundary conditions for loop 控制範圍\nReference [Python3] - 1 LINE Solution || Simple Explanation ","date":"2022-04-26T14:51:52+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2248/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2248] Intersection of Multiple Arrays 個人解法筆記 | 290th LeetCode Weekly Contest"},{"categories":["DFS (組合 combination)"],"content":"題目出處 17. Letter Combinations of a Phone Number\n難度 medium\n個人範例程式碼 letter_dict = { \u0026#34;2\u0026#34;:[\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;,\u0026#34;c\u0026#34;], \u0026#34;3\u0026#34;:[\u0026#34;d\u0026#34;,\u0026#34;e\u0026#34;,\u0026#34;f\u0026#34;], \u0026#34;4\u0026#34;:[\u0026#34;g\u0026#34;,\u0026#34;h\u0026#34;,\u0026#34;i\u0026#34;], \u0026#34;5\u0026#34;:[\u0026#34;j\u0026#34;,\u0026#34;k\u0026#34;,\u0026#34;l\u0026#34;], \u0026#34;6\u0026#34;:[\u0026#34;m\u0026#34;,\u0026#34;n\u0026#34;,\u0026#34;o\u0026#34;], \u0026#34;7\u0026#34;:[\u0026#34;p\u0026#34;,\u0026#34;q\u0026#34;,\u0026#34;r\u0026#34;,\u0026#34;s\u0026#34;], \u0026#34;8\u0026#34;:[\u0026#34;t\u0026#34;,\u0026#34;u\u0026#34;,\u0026#34;v\u0026#34;], \u0026#34;9\u0026#34;:[\u0026#34;w\u0026#34;,\u0026#34;x\u0026#34;,\u0026#34;y\u0026#34;,\u0026#34;z\u0026#34;] } class Solution: def letterCombinations(self, digits: str) -\u0026gt; List[str]: if not digits: return [] return self.dfs(digits, [\u0026#34;\u0026#34;]) def dfs(self, digits, combinations): # end of recursion if not digits: return combinations # define and split new_combinations = [] for letter in letter_dict[digits[0]]: for prefix in combinations: new_combinations.append(prefix + letter) return self.dfs(digits[1:], new_combinations) 算法說明 還算基礎的 dfs 題目，透過不斷的 dfs，每一次處理掉一個 digits\n(除了一開始建那個 table 可能還比較麻煩一些XD)\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 input，return []\nBoundary conditions 我們每搜尋一層就減去一個對應的 digit，最後當沒有 digit 時，回傳結果\nReference ","date":"2022-04-26T03:51:36+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-17/","tags":["DFS"],"title":"【Leetcode】python - [17] Letter Combinations of a Phone Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 208. Implement Trie (Prefix Tree)\n難度 medium\n個人範例程式碼 class TrieNode: def __init__(self): self.children = {} self.hasword = False # only True if exist word, # apple in dict, but app not in, False class Trie: def __init__(self): self.root = TrieNode() def insert(self, word: str) -\u0026gt; None: cur = self.root for c in word: if c not in cur.children: cur.children[c] = TrieNode() cur = cur.children[c] cur.hasword = True def search(self, word: str) -\u0026gt; bool: cur = self.root for c in word: if c not in cur.children: return False cur = cur.children[c] return cur.hasword def startsWith(self, prefix: str) -\u0026gt; bool: cur = self.root for c in prefix: if c not in cur.children: return False cur = cur.children[c] return True # Your Trie object will be instantiated and called as such # obj = Trie() # obj.insert(word) # param_2 = obj.search(word) # param_3 = obj.startsWith(prefix) 算法說明 資料結構設計類型的題目，考的就是設計資料結構的能力，比較難。\n這邊要實作的有三個功能，startsWith, search, insert，\n我們先來了解 Trie (字典樹) Trie (字典樹) 是用來特化搜尋「前綴文字 Prefix」用的資料結構，\n特色就是我們把每個單字建成一個樹狀，而當我們搜尋一個前綴字時，能夠快速順著字典往下，\n一個 TrieNode 會有，\nchildrens：紀錄往下的字母 hasword：到此本身是否有這個文字 特別注意 hasword 的功能，例如 apple 在字典中，\n但沿著路徑我們會搜尋到 app，為了反映 app 這個字不屬於字典的字，\n我們用 hasword 來記錄這個點。(apple 紀錄在 e 上，app 如果有，會記錄在最後一個 p 上。)\nTrie (字典樹) 範例圖 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 注意搜尋的過程中，有時候可以有搜尋到路徑，但實際上沒有那個文字，所以我們會需要 hasword 這個 bool，\n例如我們有 apple 在 Trie 中，\n沿路會看到 app，但不表示搜尋到 app 就有 app 這個字。\nReference 实现 Trie（前缀树） · Implement Trie (Prefix Tree) ","date":"2022-04-26T03:48:14+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0331.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-208/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [208] Implement Trie (Prefix Tree) 個人解法筆記"},{"categories":["DFS (排列 permutation)","Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 31. Next Permutation\n難度 medium\n個人範例程式碼 class Solution: def nextPermutation(self, nums: List[int]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums in-place instead. \u0026#34;\u0026#34;\u0026#34; if not nums: return nums last = len(nums)-1 while(last \u0026gt; 0): if nums[last-1] \u0026lt; nums[last]: # ...last-2, last-1, last # 34 -\u0026gt; 43 self.change_inplace(nums, last-1) return last -= 1 else: # all descend # nums.reverse() self.inplace_reverse(nums, 0, len(nums)-1) return def change_inplace(self, nums, idx): change_idx = self.find_next_same_or_bigger(nums, idx) nums[change_idx], nums[idx] = nums[idx], nums[change_idx] # inplace self.inplace_reverse(nums, idx+1, len(nums)-1) # inplace reverse after idx, nums[:idx] return def find_next_same_or_bigger(self, nums, idx): last = len(nums)-1 target = nums[idx] while nums[last] \u0026lt;= target: last -= 1 else: return last def inplace_reverse(self, nums, start, end): while(start \u0026lt; end): nums[start], nums[end] = nums[end], nums[start] start += 1 end -= 1 print(nums) return 算法說明 排列問題的進階版，求「下一個排列」，\n直接的做法可以簡單歸納為，\n- 從結尾處開始找「第一個」往下的地方 - 如果找到了，交換「往下的那個」與「之前往上的過程中 \u003c= 此數的數字 (注意等於)」，交換後把整個數列顛倒 - 如果找不到，下一個數字就是全部順序直接顛倒 (也就是第一個數字) 圖片說明 我們找第一個往下的地方，上面我們有標示箭頭，\n將「在下的位置」與「之前上升時，\u0026gt;= 下降數的第一個數字」交換\n注意這邊可以多留意 [1,1,5] -\u0026gt; [1.5.1] 這個 case，可以解釋為何要處理 「\u0026gt;=」\n交換後，我們會發現之前的上升順序還是會保持上升，我們直接顛倒方向，使後綴的部分呈現下降順序。\n以文字說明 - 1 (邊看圖想會比較懂在做什麼) - 1243 我們抓到下降的 4-\u003e2， - 2 與第一個 3 (\u003e=2)交換，此時為 1342， - 把交換處之後的倒序，變成 1324，就是下一個數字 以文字說明 - 2 (邊看圖想會比較懂在做什麼) - 1342 我們抓到下降的 4-\u003e3， - 3 與第一個 4 (\u003e= 3)交換，此時為 1432， - 把交換處之後的倒序，變成 1423，就是下一個數字 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n這邊也可以參考 leetcode 上很讚的圖片說明版： Easy python solution based on lexicographical permutation algorithm\ninput handling 如果沒有 nums，回傳 nums\nBoundary conditions 做完上述的交換後，return 結果\nReference Easy python solution based on lexicographical permutation algorithm 下一个排列 · Next Permutation Python solution with comments. ","date":"2022-04-26T03:36:34+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0330.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-permutation/leetcode-python-31/","tags":["DFS"],"title":"【Leetcode】python - [31] Next Permutation 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 862 · Next Closest Time\n難度 medium\n個人範例程式碼 class Solution: \u0026#34;\u0026#34;\u0026#34; @param time: the given time @return: the next closest time \u0026#34;\u0026#34;\u0026#34; def next_closest_time(self, time: str) -\u0026gt; str: # write your code here digits = set(time) hr, m = int(time[0:2]), int(time[3:5]) current = hr*60+m # 0 - 1440 for i in range(1, 1441): next_time = (current + i)%1440 next_time = self.change_format(next_time) print(next_time) if set(next_time) \u0026lt;= digits: return next_time def change_format(self, x): hr, m = x//60, x%60 return f\u0026#34;{hr:02}:{m:02}\u0026#34; 算法說明 這題基本上可以直接照題目的要求做，但因為時間格式的關係，不是那麼好直接處理，\n當然也是可以使用 datetime 之類的 library，但這樣也許有失題目本身想考的東西XD\n最後採取的策略是換算 24hr*60 = 1440 min，\n用 for 去跑 1440 次，並 %1440，並找到最近的符合結果即可。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\nsubset 用法 這邊可以使用 subset 處理，可以使用「\u0026lt;=」取子集合。\n例如：{a,b,c} \u0026lt;= {a,b,c,d} 這樣的概念。\ninput handling x\nBoundary conditions 當符合結果時，return 結果\nReference 下一个最近的时间 · Next Closest Time ","date":"2022-04-26T03:11:45+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-862/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [862] Next Closest Time 個人解法筆記 | 內有 set 判斷是否 subset 的用法"},{"categories":["710 - Python LeetCode"],"content":"題目出處 52. N-Queens II\n難度 hard\n個人範例程式碼 class Solution: def totalNQueens(self, n: int) -\u0026gt; int: if n == 0: return [[]] self.ans = 0 self.used_horizontal = set() self.used_diagonal_x_add_y = set() self.used_diagonal_x_minus_y = set() board_set = set() self.dfs(n, board_set, 0) return self.ans def dfs(self, n, board_set, floor): # end of recursion if floor == n: self.ans += 1 return # define and split for i in range(n): queen = (floor, i) if self.check_board_conditions(queen): board_set.add(queen) self.add_board_conditions(queen) self.dfs(n, board_set, floor+1) self.remove_board_conditions(queen) # backtracking board_set.remove(queen) # backtracking def check_board_conditions(self, queen): (floor, i) = queen if i in self.used_horizontal: return False if floor + i in self.used_diagonal_x_add_y: return False if floor - i in self.used_diagonal_x_minus_y: return False return True def remove_board_conditions(self, queen): (floor, i) = queen self.used_horizontal.remove(i) self.used_diagonal_x_add_y.remove(floor + i) self.used_diagonal_x_minus_y.remove(floor - i) def add_board_conditions(self, queen): (floor, i) = queen self.used_horizontal.add(i) self.used_diagonal_x_add_y.add(floor + i) self.used_diagonal_x_minus_y.add(floor - i) 算法說明 此題目的前一題要求列出全部結果，而這題只須回傳結果數量，但做法相同，前一題可參考： https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-51/\n經典的 N-皇后 難題，可以參考的 wiki 在這邊：\n八皇后問題 wiki 這邊採用的策略是先把座標存在 set() 當中，\n最後等結果都確定了，再把 set() 轉為 board。\nboard 正確的判斷條件 總共分成三種\n橫的不重複 直的不重複 斜的不重複 橫的不重複 橫的不重複：我們在 for 環圈的時候，一橫只允許一個，因此不用另外判斷 直的不重複 直的不重複：我們紀錄直的座標到 used，每次嘗試擺上一個新皇后時，看一下有沒有 used 斜的不重複 斜的不重複：這個比較複雜，基本還是使用 used 的概念，細節我們下面細講 斜的不重複，這邊我們會提到一些數學的技巧：\n我們稍微看一下，會發現斜線上的特性：\n正斜線：x+y 負斜線：x-y 這邊我們計算圖片上的座標，我們就會發現，同一條斜線上的結果，規律會是 x-y 或 x+y 為定值\n我們嘗試區分析範圍，這邊我們就會知道\n正斜線：x+y，範圍介於 0 ~ 2n 負斜線：x-y，範圍介於 -n ~ n 注意：但我們「不需要」去把這個範圍都掃過，因為也會有「不存在結果依然正確的可能性」\n可以思考明明只有 n 個數，怎麼可能放的進 「0 ~ 2n」 或 「-n ~ n」。\n我們透過紀錄 used，只要確保數值不重覆即可。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當 n = 0 時，return [[]]\nBoundary conditions 我們以 floor 來紀錄層數，我們每次 dfs floor 不斷增加，\n搜尋至 floor = n 時，添加結果回傳 (表示已經排完全部結果，且因為我們是邊放 queen 邊檢查，能到這層必為正確結果)\nReference N皇后问题（一） · N-Queens ","date":"2022-04-26T02:17:15+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0329.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-52/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [52] N-Queens II 個人解法筆記"},{"categories":["DFS (全部方案)"],"content":"題目出處 51. N-Queens\n難度 hard\n個人範例程式碼 class Solution: def solveNQueens(self, n: int) -\u0026gt; List[List[str]]: if n == 0: return [[]] self.ans = [] self.used_horizontal = set() self.used_diagonal_x_add_y = set() self.used_diagonal_x_minus_y = set() board_set = set() n = 5 self.dfs(n, board_set, 0) return self.ans def dfs(self, n, board_set, floor): # end of recursion if floor == n: self.make_board(n, board_set) return # define and split for i in range(n): queen = (floor, i) if self.check_board_conditions(queen): board_set.add(queen) self.add_board_conditions(queen) self.dfs(n, board_set, floor+1) self.remove_board_conditions(queen) # backtracking board_set.remove(queen) # backtracking def check_board_conditions(self, queen): (floor, i) = queen if i in self.used_horizontal: return False if floor + i in self.used_diagonal_x_add_y: return False if floor - i in self.used_diagonal_x_minus_y: return False return True def remove_board_conditions(self, queen): (floor, i) = queen self.used_horizontal.remove(i) self.used_diagonal_x_add_y.remove(floor + i) self.used_diagonal_x_minus_y.remove(floor - i) def add_board_conditions(self, queen): (floor, i) = queen self.used_horizontal.add(i) self.used_diagonal_x_add_y.add(floor + i) self.used_diagonal_x_minus_y.add(floor - i) def make_board(self, n, board_set): print(board_set) board = [] for i in range(n): each_floor = [\u0026#34;Q\u0026#34; if (i, j) in board_set else \u0026#34;.\u0026#34; for j in range(n)] board.append(\u0026#34;\u0026#34;.join(each_floor)) self.ans.append(board) 算法說明 經典的 N-皇后 難題，可以參考的 wiki 在這邊：\n八皇后問題 wiki 這邊採用的策略是先把座標存在 set() 當中，\n最後等結果都確定了，再把 set() 轉為 board。\nboard 正確的判斷條件 總共分成三種\n橫的不重複 直的不重複 斜的不重複 橫的不重複 橫的不重複：我們在 for 環圈的時候，一橫只允許一個，因此不用另外判斷 直的不重複 直的不重複：我們紀錄直的座標到 used，每次嘗試擺上一個新皇后時，看一下有沒有 used 斜的不重複 斜的不重複：這個比較複雜，基本還是使用 used 的概念，細節我們下面細講 斜的不重複，這邊我們會提到一些數學的技巧：\n我們稍微看一下，會發現斜線上的特性：\n正斜線：x+y 負斜線：x-y 這邊我們計算圖片上的座標，我們就會發現，同一條斜線上的結果，規律會是 x-y 或 x+y 為定值\n我們嘗試區分析範圍，這邊我們就會知道\n正斜線：x+y，範圍介於 0 ~ 2n 負斜線：x-y，範圍介於 -n ~ n 注意：但我們「不需要」去把這個範圍都掃過，因為也會有「不存在結果依然正確的可能性」\n可以思考明明只有 n 個數，怎麼可能放的進 「0 ~ 2n」 或 「-n ~ n」。\n我們透過紀錄 used，只要確保數值不重覆即可。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當 n = 0 時，return [[]]\nBoundary conditions 我們以 floor 來紀錄層數，我們每次 dfs floor 不斷增加，\n搜尋至 floor = n 時，添加結果回傳 (表示已經排完全部結果，且因為我們是邊放 queen 邊檢查，能到這層必為正確結果)\nReference N皇后问题（一） · N-Queens ","date":"2022-04-26T02:12:02+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0329.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs/leetcode-python-51/","tags":["DFS"],"title":"【Leetcode】python - [51] N-Queens 個人解法筆記"},{"categories":["DFS (排列 permutation)"],"content":"題目出處 47. Permutations II\n難度 medium\n個人範例程式碼 class Solution: def permuteUnique(self, nums: List[int]) -\u0026gt; List[List[int]]: if not nums: return nums nums.sort() self.ans = [] self.dfs(nums, []) return self.ans def dfs(self, nums, permutations): # end of recursion if not nums: self.ans.append(list(permutations)) return # define and split for i, num in enumerate(nums): if i \u0026gt; 0 and nums[i] == nums[i-1]: # if duplicate, we only record first time continue permutations.append(num) self.dfs(nums[:i] + nums[i+1:], permutations) permutations.pop() # backtracking 算法說明 此題目的前一題，差別在這題需要多處理重複的元素，可參考： https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-permutation/leetcode-python-46/\n排列類型的題目，使用 dfs 去搜尋全部的結果\n處理順序 另外在處理順序時，我們使用傳入「start[0:i] + start[i+1:]」的方式，暫時移除了 start[i] 的元素，\n這樣的操作方式會複製一個新的 list，而不會影響到原本的 list\n處理重複 為了處理重複，我們先對 nums 進行排序，我們使用\nnums.sort() 另外在處理重複的數字時，我們只採用第一組的結果，後面都 continue 掉，因此：\nif i \u0026gt; 0 and nums[i] == nums[i-1]: # if duplicate, we only record first time continue 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當沒有 nums 時，return nums\nBoundary conditions 搜尋至 no nums 時，添加結果回傳 (表示元素已使用完畢)\nReference 带重复元素的排列 · Permutations II ","date":"2022-04-24T03:06:44+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-permutation/leetcode-python-47/","tags":["DFS"],"title":"【Leetcode】python - [47] Permutations II 個人解法筆記 #重要題型"},{"categories":["DFS (排列 permutation)"],"content":"題目出處 46. Permutations\n難度 medium\n個人範例程式碼 class Solution: def permute(self, nums: List[int]) -\u0026gt; List[List[int]]: if not nums: return nums self.ans = [] self.dfs(nums, []) return self.ans def dfs(self, nums, permutations): # end of recursion if not nums: self.ans.append(list(permutations)) # deepcopy return # define and split for i, num in enumerate(nums): permutations.append(num) # temporarily remove element i = start[0:i] + start[i+1:] self.dfs(nums[:i] + nums[i+1:], permutations) permutations.pop() # backtracking 算法說明 排列類型的題目，使用 dfs 去搜尋全部的結果，\n另外在處理順序時，我們使用傳入「start[0:i] + start[i+1:]」的方式，暫時移除了 start[i] 的元素，\n這樣的操作方式會複製一個新的 list，而不會影響到原本的 list\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當沒有 nums 時，return nums\nBoundary conditions 搜尋至 no nums 時，添加結果回傳 (表示元素已使用完畢)\nReference 全排列 · Permutations ","date":"2022-04-24T03:02:08+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-permutation/leetcode-python-46/","tags":["DFS"],"title":"【Leetcode】python - [46] Permutations 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 140. Word Break II\n難度 hard\n個人範例程式碼 class Solution: def wordBreak(self, s: str, wordDict: List[str]) -\u0026gt; List[str]: if not s: return [] self.memo = {} return self.dfs(s, wordDict) def dfs(self, s, word_dict): if s in self.memo: return self.memo[s] # end of recursion if len(s) == 0: return [] # define and split partitions = [] # full match if s in word_dict: partitions.append(s) # part match for idx in range(0, len(s)): prefix = s[:idx+1] # 0 to idx # print(prefix) if prefix not in word_dict: continue sub_partitions = self.dfs(s[idx+1:], word_dict) # start from idx+1 for partition in sub_partitions: partitions.append(prefix + \u0026#34; \u0026#34; + partition) self.memo[s] = partitions return partitions 算法說明 此題的前一題，只需要判斷可不可行就好，可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-139/\n這題可以先嘗試寫一個基本的版本，用 dfs 搜索出全部的結果，\n這題很佛心不會 TLE，但真的很慢XDDD\n思考方式為用一個 start 作為 pointer，切出「到目前為止之前的全部內容」\n以下的方法基本沒有太大問題，只是速度比較慢而已，此時我們可以用記憶化搜索的方式加速\nclass Solution: def wordBreak(self, s: str, wordDict: List[str]) -\u0026gt; List[str]: if not s: return False self.ans = [] self.dfs(s, 0, wordDict, []) return self.ans def dfs(self, s, start, word_dict, prefix_words): # end of recursion if start \u0026gt; len(s): return if start == len(s): self.ans.append(\u0026#34; \u0026#34;.join(prefix_words)) return # define and split for idx in range(start, len(s)): prefix = s[start:idx+1] # start to idx if prefix in word_dict: prefix_words.append(prefix) self.dfs(s, idx+1, word_dict, prefix_words) prefix_words.pop() # backtracking else: continue return 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n記憶化搜索 memoization 加速 加速的思路，是來自我們思考每次在遞迴的過程中，\n最終都會有一段處理相同文字的過程。\n(可以想像，每次的開頭不同時，結尾我們都在反覆處理一樣的東西)\n因此我們用一個 memo 紀錄一下，紀錄「相同的文字」，我們上次處理的結果\n因此我們這邊判斷 s 是否有存在於 memo 中，有則直接 return partition 結果\ninput handling 當沒有 s 時，return []\nBoundary conditions 搜尋至 start = len(s) 時，剛好抵達結束搜尋的範圍 (其他狀況應該是無法抵達的)\nReference 单词拆分II · Word Break II ","date":"2022-04-23T19:55:23+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0327.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-140/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [140] Word Break II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 139. Word Break\n難度 medium\n個人範例程式碼 class Solution: def wordBreak(self, s: str, wordDict: List[str]) -\u0026gt; bool: if not s: return False self.memo = {} return self.dfs(s, 0, wordDict) def dfs(self, s, start, word_dict): if start in self.memo: return self.memo[start] # end of recursion if start \u0026gt; len(s): return False if start == len(s): return True # define and split ans = False for idx in range(start, len(s)): prefix = s[start:idx+1] # start to idx if prefix in word_dict: ans = ans or self.dfs(s, idx+1, word_dict) else: continue # Memoization search self.memo[start] = ans return ans 算法說明 這題可以先嘗試寫一個基本的版本，能夠通過大部分的 case，但是會 TLE\n思考方式為用一個 start 作為 pointer，指出「到目前為止之前的全部內容」\nclass Solution: def wordBreak(self, s: str, wordDict: List[str]) -\u0026gt; bool: if not s: return False return self.dfs(s, 0, wordDict) def dfs(self, s, start, word_dict): # end of recursion if start \u0026gt; len(s): return False if start == len(s): return True # define and split ans = False for idx in range(start, len(s)): prefix = s[start:idx+1] # start to idx if prefix in word_dict: ans = ans or self.dfs(s, idx+1, word_dict) else: continue return ans 上面的方法基本沒有太大問題，只是速度太慢而已，此時我們可以用記憶化搜索的方式加速\n記憶化搜索 memoization 加速 加速的思路，是來自我們思考每次在遞迴的過程中，\n最終都會有一段處理相同結尾的過程。\n(可以想像，每次的開頭不同時，結尾我們都在反覆處理一樣的東西)\n因此我們用一個 memo 紀錄一下，「從 start 開始往後」的結果，在之前的判斷中，答案為 True or False\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當沒有 s 時，return False\nBoundary conditions 搜尋至 start \u0026gt; len(s) 時，超過結果，為 False\n搜尋至 start len(s) 時，剛好結束，為 True\nReference 单词拆分（一） · Word Break ","date":"2022-04-23T19:05:13+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0326.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-139/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [139] Word Break 個人解法筆記"},{"categories":["DFS (排列 permutation)"],"content":"題目出處 680 · Split String\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param s: a string to be split @return: all possible split string array \u0026#34;\u0026#34;\u0026#34; def split_string(self, s: str) -\u0026gt; List[List[str]]: # write your code here if not s: return [[]] self.ans = [] self.dfs(s, 0, []) return self.ans def dfs(self, s, start, combinations): print(combinations) # end of recursion if start \u0026gt; len(s): return if start == len(s): self.ans.append(list(combinations)) return # define and split combinations.append(f\u0026#34;{s[start]}\u0026#34;) self.dfs(s, start+1, combinations) combinations.pop() # backtracking if start + 1 \u0026lt; len(s): combinations.append(f\u0026#34;{s[start:start+2]}\u0026#34;) self.dfs(s, start+2, combinations) combinations.pop() # backtracking 算法說明 簡單來說就是一個求，「拿一個字母」或「拿兩個字母」的 組合題目。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意：如果沒有 s，要回傳的是 [[]]，而不是 [] Boundary conditions 注意邊界條件的設定\n會有兩種情況\nstart 超過 len(s)，錯誤情況，預期不會發生 (也有被下面判斷式限制住) start 剛好等於 len(s)，剛好的情況，就是我們要的答案 另外，判斷此層的 dfs 時，也有分兩種狀況\ns[start]：判斷一個字母的情況 s[start:start+2]：判斷兩個字母的情況的，記得要先判斷「if start+1 \u0026lt; len(s)」，不然直接取座標會出錯 Reference ","date":"2022-04-23T18:44:55+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-permutation/lintcode-python-680/","tags":["DFS"],"title":"【Lintcode】python - [680] Split String 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 10. Regular Expression Matching\n難度 hard\n個人範例程式碼 class Solution: def isMatch(self, s: str, p: str) -\u0026gt; bool: self.memo = {} return self.dfs(s, p, 0, 0) def dfs(self, s, p, start_s, start_p): # print(start_s, start_p) if (start_s, start_p) in self.memo: return self.memo[(start_s, start_p)] # end of recursion if len(s) \u0026lt;= start_s and len(p) \u0026lt;= start_p: return True if len(s) \u0026gt; start_s and len(p) \u0026lt;= start_p: # s left return False if len(s) \u0026lt;= start_s and len(p) \u0026gt; start_p: # p left # must be a* case return self.is_empty(p[start_p:]) if start_p+1 \u0026lt; len(p) and p[start_p+1] == \u0026#34;*\u0026#34;: # if * means nothing, self.dfs(s, p, start_s, start_p+2) # skip a* # if * means duplicate, self.dfs(s, p, start_s+1, start_p) # keep a*, if s[i] == p[i] ans = (self.is_char_matched(s[start_s], p[start_p]) and self.dfs(s, p, start_s+1, start_p)) or self.dfs(s, p, start_s, start_p+2) elif self.is_char_matched(s[start_s], p[start_p]): ans = self.dfs(s, p, start_s+1, start_p+1) else: ans = False self.memo[(start_s, start_p)] = ans return ans def is_char_matched(self, s, p): return s \u0026lt;mark\u0026gt; p or p \u0026lt;/mark\u0026gt; \u0026#34;.\u0026#34; def is_empty(self, p): for idx in range(1, len(p), 2): if p[idx] == \u0026#34;*\u0026#34;: continue else: return False return True if p[-1] == \u0026#34;*\u0026#34; else False 算法說明 此題有類似題，但這題判斷更難寫，可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-44/\n超難的配對問題，比上面那題還更難，首先我們先不管加速，嘗試先寫一個能解的版本\n基本版的寫完後，就會發現能 pass，但超慢，\n原因是因為我們的搜索時間是「指數等級」的，看來是要稍微優化一下。\n比上面那題佛心了\u0026hellip; 上面那題還會 TLE，但可能這題更難寫條件式的關係\n判斷的先後順序 這裡的 * 沒有像上一題那麼萬用，他必須要搭配合理的「前綴字」才能發揮效力。\n在處理上，我們需要先判斷「i+1」是否為 ，\n免得我們一個衝動讓 「c != a」，return False 就糗大了\n* 的處理 一樣也是分兩路\n如果 * 代表的是沒有東西，則判斷 self.dfs(s, p, start_s, start_p+2) 如果 * 代表的是重複的東西，則判斷 self.dfs(s, p, start_s+1, start_p) 但注意!!!! 這裡還有一個陷阱!!!\n我們需要多保證 s[i] p[i]，不能直接寫，\n否則在碰到 cab 與 aab 這種 case ，就會出錯了。(被 c* 干擾了，c 沒有重複)\n因此整理之後得到\n如果 * 代表的是沒有東西，則判斷 self.dfs(s, p, start_s, start_p+2) 如果 * 代表的是重複的東西，則判斷 self.is_char_matched(s[start_s], p[start_p]) and self.dfs(s, p, start_s+1, start_p) 結束條件 分成三種：\n如果 start_s, start_p 同時 \u0026gt;= len(s), len(p)，屬正常情況 return True 只有 start_p \u0026gt;= len(p)，表示有剩餘 s，return False 只有 start_s \u0026gt;= len(s)，表示有剩餘 p，要特別處理 我們要判斷 p 是否可以代表 \u0026ldquo;\u0026quot;，\n我們在上述切割時，有遇到 * 都是保留「a」的情況進行切割，\n因此 p[0] = \u0026ldquo;\u0026rdquo; 的情況不會單獨出現 (除非 input 有問題)，任何「a*」一定會被完整的切割掉\n最後要多判斷一個 [-1] \u0026ldquo;*\u0026quot;，確保尾巴沒有殘留的東西 (因為 range 2 有可能會漏掉一個)\ndef is_empty(self, p): for idx in range(1, len(p), 2): if p[idx] \u0026#34;*\u0026#34;: continue else: return False return True if p[-1] \u0026#34;*\u0026#34; else False 記憶化搜索 (DP) 我們用一個策略紀錄我們搜尋的結果，主要因為我們進行的「重複搜尋」太多了，\n我們透過記憶 start_p, start_q 的兩個起始位置，與對應結果。\n當遞迴再一次回到此位置時，直接去 hashtable 拿 (start_p, start_q) 的結果來用。\n這邊我們強調一下增加的部分：\n拿取 if (start_s, start_p) in self.memo_matched_begin: return self.memo_matched_begin[(start_s, start_p)] 儲存 self.memo_matched_begin[(start_s, start_p)] = ans 這個就是一種「以空間換取時間」的策略\n於是我們就能完成這道 hard 的題目。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 見上述說明\nReference [正则表达式匹配 · Regular Expression Matching](https://www.jiuzhang.com/solution/regular-expression-matching/) ","date":"2022-04-23T03:21:45+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-10/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [10] Regular Expression Matching 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 44. Wildcard Matching\n難度 hard\n個人範例程式碼 class Solution: def isMatch(self, s: str, p: str) -\u0026gt; bool: self.memo_matched_begin = {} return self.dfs(s, p, 0, 0) def dfs(self, s, p, start_s, start_p): if (start_s, start_p) in self.memo_matched_begin: return self.memo_matched_begin[(start_s, start_p)] # end of recursion if start_s \u0026gt;= len(s) and start_p \u0026gt;= len(p): return True if start_s \u0026lt; len(s) and start_p \u0026gt;= len(p): # s left return False if start_s \u0026gt;= len(s) and start_p \u0026lt; len(p): # p left for idx in range(start_p, len(p)): if p[idx] == \u0026#34;*\u0026#34;: continue else: return False return True # define and split if self.is_char_matched(s[start_s], p[start_p]): # normal pattern ans = self.dfs(s, p, start_s+1, start_p+1) elif p[start_p] == \u0026#34;*\u0026#34;: # can be nothing to anything(keep *) # * = nothing, self.dfs(s, p, start_s, start_p+1) # * = anything, self.dfs(s, p, start_s+1, start_p) ans = self.dfs(s, p, start_s, start_p+1) or self.dfs(s, p, start_s+1, start_p) else: ans = False self.memo_matched_begin[(start_s, start_p)] = ans return ans def is_char_matched(self, s, p): return s \u0026lt;mark\u0026gt; p or p \u0026lt;/mark\u0026gt; \u0026#34;?\u0026#34; 算法說明 超難的配對問題，首先我們先不管加速，嘗試先寫一個能解的版本\n基本版的寫完後，就會發現簡單的都能 pass，比較難的 case 會 TLE，\n原因是因為我們的搜索時間是「指數等級」的，看來是要稍微優化一下。\n記憶化搜索 (DP) 我們用一個策略紀錄我們搜尋的結果，主要因為我們進行的「重複搜尋」太多了，\n我們透過記憶 start_p, start_q 的兩個起始位置，與對應結果。\n當遞迴再一次回到此位置時，直接去 hashtable 拿 (start_p, start_q) 的結果來用。\n這邊我們強調一下增加的部分：\n拿取 if (start_s, start_p) in self.memo_matched_begin: return self.memo_matched_begin[(start_s, start_p)] 儲存 self.memo_matched_begin[(start_s, start_p)] = ans 這個就是一種「以空間換取時間」的策略\n於是我們就能完成這道 hard 的題目。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 見上述說明\nReference 通配符匹配 · Wildcard Matching ","date":"2022-04-23T02:05:31+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-44/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [44] Wildcard Matching 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 131. Palindrome Partitioning\n難度 medium\n個人範例程式碼 class Solution: def partition(self, s: str) -\u0026gt; List[List[str]]: if not s: return [] self.ans = [] self.dfs(s, [], 0, len(s)) return self.ans def dfs(self, s, partitions, start, end): # end of recursion if start \u0026gt;= end: self.ans.append(list(partitions)) return # define and spliit for idx in range(start, end): split_s = s[start:idx+1] # least one word, max = [start:(end-1+1)] = [start:end] if self.is_palindrome(split_s): partitions.append(split_s) self.dfs(s, partitions, idx+1, end) # go next partitions.pop() return def is_palindrome(self, s): print(s) start = 0 end = len(s)-1 while(start \u0026lt;= end and s[start] == s[end]): start += 1 end -= 1 else: if start \u0026lt;= end: # check return False else: return True 算法說明 組合類的變化題，使用 dfs，嘗試窮舉所有的組合。\n由 start 開始，計算至 end 取 split 時記得要取 [start:idx+1] 才會是 start~idx 而往下走時，要指定的下一個座標也是 idx+1 (因為目前只判斷到包含 idx) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 s，直接 return []\nBoundary conditions 見上述說明\nReference 分割回文串 · Palindrome Partitioning ","date":"2022-04-23T00:55:21+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0324.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-131/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [131] Palindrome Partitioning 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 90 · k Sum II\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param a: an integer array @param k: a postive integer \u0026lt;= length(A) @param target: an integer @return: A list of lists of integer we will sort your return value in output \u0026#34;\u0026#34;\u0026#34; def k_sum_i_i(self, a: List[int], k: int, target: int) -\u0026gt; List[List[int]]: # write your code here if not a: return [] self.ans = [] self.dfs(a, k, target, [], 0) return self.ans def dfs(self, a, k, target, path, start_idx): # end of recursion if k \u0026lt;= 0: if sum(path) == target: self.ans.append(list(path)) # deepcopy return # define and split for idx in range(start_idx, len(a)): path.append(a[idx]) self.dfs(a, k-1, target, path, idx+1) path.pop() # backtracking 算法說明 此題有類似問題，差別是在這題是「限制可用數字的數量」，可參考： https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-39/\nhttps://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-40/\n組合類的問題，使用 dfs 搜尋出全部的組合，在判斷可行的方案。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 a，直接 return []\nBoundary conditions 見上述說明\nReference ","date":"2022-04-22T22:52:32+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-90/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [90] k Sum II 個人解法筆記"},{"categories":["DFS (組合 combination)"],"content":"題目出處 40. Combination Sum II\n難度 medium\n個人範例程式碼 class Solution: def combinationSum2(self, candidates: List[int], target: int) -\u0026gt; List[List[int]]: if not candidates: return [] ans = [] self.dfs(sorted(candidates), target, 0, [], ans) return ans def dfs(self, candidates, target, start_idx, combination, ans): # end of recursion if target \u0026lt; 0: return if target == 0: ans.append(list(combination)) # deepcopy return # define for idx in range(start_idx, len(candidates)): if idx != start_idx and candidates[idx] == candidates[idx-1]: # remove duplicate continue combination.append(candidates[idx]) self.dfs(candidates, target-candidates[idx], idx+1, combination, ans) # no duplicate use combination.pop() 算法說明 本題是 Combinations 系列的第 3 題，前面的題目可以參考：\n第 1 題：不允許重複，給定數字範圍的全部組合，目標是指定組合內固定的數量。\n第 2 題：允許重複，順序不同視為相同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是一個結果」\n第 3 題：允許有限重複(題目指定上限數量)，求全部組合。\n第 4 題：不允許重複，給定數字範圍的全部組合，目標是求指定的和。\n第 5 題：允許重複，但順序不同視為不同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是兩個結果」。(這題已經可以當作排列的題目了。)\n組合類的問題，使用 dfs 搜尋出全部的組合，在判斷可行的方案。\n與允許使用重複的前一題比較，這題要多處理「數組內建重複」的部分，\n因此我們多使用了\n「idx != start_idx」：當 idx 不等於「起始位置」 這個判斷條件非常重要，\n我們只判斷起始位置，因為有可能會有 [1,1,6] 這種 case 要處理\n我們讓頭兩組 [1, 1] 能夠被計算為一個結果，是第一組也被我們控制在唯一的一組。\n如果數組有 [1, 1, 1]， 我們在搜尋前兩個 [1, 1] 就會決定了這唯一的結果。\n「candidates[idx] candidates[idx-1]」 判斷重複的時候，用 continue 直接略過。\n(for 搭配 continue，比起直接用 while 略過，感覺程式可讀性會更好。)\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 candidates，直接 return []\nBoundary conditions 見上述說明\nReference 数字组合 II · Combination Sum II ","date":"2022-04-22T22:45:30+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-40/","tags":["DFS"],"title":"【Leetcode】python - [40] Combination Sum II 個人解法筆記"},{"categories":["DFS (組合 combination)"],"content":"題目出處 39. Combination Sum\n難度 medium\n個人範例程式碼 class Solution: def combinationSum(self, candidates: List[int], target: int) -\u0026gt; List[List[int]]: if not candidates: return [] ans = [] self.dfs(candidates, target, 0, [], ans) return ans def dfs(self, candidates, target, start_idx, combination, ans): # end of recursion if target \u0026lt; 0: return if target == 0: ans.append(list(combination)) # deepcopy return # define and split for idx in range(start_idx, len(candidates)): combination.append(candidates[idx]) self.dfs(candidates, target-candidates[idx], idx, combination, ans) # idx + 1 = next index combination.pop() 算法說明 本題是 Combinations 系列的第 2 題，前面的題目可以參考：\n第 1 題：不允許重複，給定數字範圍的全部組合，目標是指定組合內固定的數量。\n第 2 題：允許重複，順序不同視為相同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是一個結果」\n第 3 題：允許有限重複(題目指定上限數量)，求全部組合。\n第 4 題：不允許重複，給定數字範圍的全部組合，目標是求指定的和。\n第 5 題：允許重複，但順序不同視為不同結果，也就是說「(1,2,3) 與 (3, 2, 1) 是兩個結果」。(這題已經可以當作排列的題目了。)\n組合類的問題，使用 dfs 搜尋出全部的組合，在判斷可行的方案。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 candidates，直接 return []\nBoundary conditions 見上述說明\nReference 数字组合 · Combination Sum ","date":"2022-04-22T22:37:14+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-combination/leetcode-python-39/","tags":["DFS"],"title":"【Leetcode】python - [39] Combination Sum 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 901 · Closest Binary Search Tree Value II\n難度 hard\n個人範例程式碼 from typing import ( List, ) from lintcode import ( TreeNode, ) \u0026#34;\u0026#34;\u0026#34; Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param root: the given BST @param target: the given target @param k: the given k @return: k values in the BST that are closest to the target we will sort your return value in output \u0026#34;\u0026#34;\u0026#34; def closest_k_values(self, root: TreeNode, target: float, k: int) -\u0026gt; List[int]: if not root or k == 0: return [] # write your code here lower_stack = self.get_stack(root, target) # init to found target upper_stack = list(lower_stack) # delete duplicate closest element, change wrong lower[-1] or upper[-1] if lower_stack[-1].val \u0026lt; target: self.move_upper(upper_stack) else: self.move_lower(lower_stack) ans = [] for i in range(k): if self.is_lower_closer(lower_stack, upper_stack, target): ans.append(lower_stack[-1].val) self.move_lower(lower_stack) else: ans.append(upper_stack[-1].val) self.move_upper(upper_stack) return ans def is_lower_closer(self, lower_stack, upper_stack, target): if not lower_stack: # has nothing return False if not upper_stack: return True return target - lower_stack[-1].val \u0026lt; upper_stack[-1].val - target def move_lower(self, stack): if stack[-1].left: # has left, move to the right bottom node = stack[-1].left while node: stack.append(node) node = node.right else: # no left, move to the first turn right parent node = stack.pop() while stack and stack[-1].left == node: node = stack.pop() def move_upper(self, stack): if stack[-1].right: # has right, move to the left bottom node = stack[-1].right while node: stack.append(node) node = node.left else: # no right, move to the first turn left parent node = stack.pop() while stack and stack[-1].right == node: node = stack.pop() def get_stack(self, root, target): stack = [] while root: stack.append(root) if target \u0026lt;= root.val: root = root.left else: root = root.right return stack 算法說明 BST iteration 集大成的最難題目，能考的都考了。\n這題最難的部分在於實作 iteration (當然，要重新 traverse 絕對是更簡單的方法，不過速度上會慢)，\n分成兩種狀況討論：\n往更小「一個」移動 往更大「一個」移動 往更小「一個」移動 說到小，就先想到 left\n這裡又分成兩種情況，\n如果有 left，往 left 的 最右底點移動 (才會是「第一個」更小) 如果沒有 left，找第一個 edge 右彎的 parent 看不懂就看下圖可能會比較好懂\u0026hellip; 一點點XD\n往更大「一個」移動 說到大，就先想到 right\n這裡又分成兩種情況，\n如果有 right，往 right 的 最左底點移動 (才會是「第一個」更大) 如果沒有 right，找第一個 edge 左彎的 parent 看不懂就看下圖可能會比較好懂\u0026hellip; 一點點XD\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當 k = 0 或是沒有 root 時，return []\nBoundary conditions 見上述說明\nReference 二叉搜索树中最接近的值 II · Closest Binary Search Tree Value II ","date":"2022-04-22T17:06:02+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0323.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-901/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [901] Closest Binary Search Tree Value II 個人解法筆記 #綜合難題"},{"categories":["710 - Python LeetCode"],"content":"題目出處 763. Partition Labels\n難度 medium\n個人範例程式碼 class Solution: def partitionLabels(self, s: str) -\u0026gt; List[int]: # partition need no word duplicate hashtable = self.set_hashtable(s) # print(hashtable) intervals = self.set_interval(hashtable) # print(intervals) intervals = self.merge_interval(intervals) # print(intervals) return [interval[1]-interval[0]+1 for interval in intervals] # 0~2 = 2-0+1 def set_interval(self, hashtable): return [interval for interval in hashtable.values()] def merge_interval(self, intervals): ans = [] for idx, interval in enumerate(intervals): if idx == 0: start = interval[0] end = interval[1] else: if end \u0026gt;= interval[0]: end = max(interval[1], end) else: # no cross interval ans.append([start, end]) start = interval[0] end = interval[1] ans.append([start, end]) # last interval return ans def set_hashtable(self, s): hashtable = {} for idx, word in enumerate(s): if word not in hashtable: hashtable[word] = [idx, idx] else: hashtable[word][1] = idx return hashtable 算法說明 這題是 interval 系列的變化題，我們可以建立一個 hashtable，\n紀錄每一個單字的 [起始, 結束] 位置。\n最後因為題目要求最大化 interval 數量，但需要盡量保持不重複數字\n(這邊直接就當作同一個字母，「不能出現在不同區間啦！」題目敘述太繞了!)\n因此，我們先用文字組出每一個區間，然後合併區間。就是一個這樣的題目了!\n討論 interval 合併的策略 主要情況有兩種：\n先固定好 start 的順序，\n假設後進來的 [interval[0], interval[1]]\n有要合併的條件： end \u0026gt;= interval[0] 而合併後，有分為兩種情況：\n- interval[1] \u003e= end：類似交錯的情況，合併區間變為 [start, interval[1]] - end \u003e= interval[1]：類似包起來的情況，合併區間變為 [start, end] 以上的 1，2 又可以合併變為 [start, max(interval[1], end)]\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 見上述說明\nReference https://www.facebook.com/groups/1451299754892511/posts/5171314816224301/ ","date":"2022-04-22T15:08:54+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0322.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-763/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [763] Partition Labels 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 228. Summary Ranges\n難度 easy\n個人範例程式碼 class Solution: def summaryRanges(self, nums: List[int]) -\u0026gt; List[str]: if not nums: return nums ans = [] tmp_ans = [] idx = 0 while(idx \u0026lt; len(nums)): tmp_ans.append(nums[idx]) while idx+1 \u0026lt; len(nums) and nums[idx+1] == nums[idx] + 1: idx += 1 tmp_ans.append(nums[idx]) else: if len(tmp_ans) \u0026gt; 1: s = f\u0026#34;{tmp_ans[0]}-\u0026gt;{tmp_ans[-1]}\u0026#34; else: s = f\u0026#34;{tmp_ans[0]}\u0026#34; ans.append(s) idx += 1 tmp_ans = [] return ans 算法說明 這題難度不高，麻煩的點在於邊界處理，必須要細心，不然很容易犯錯。\n採取的策略是 while-loop 「idx+1 \u0026lt; len(nums) and nums[idx+1] nums[idx] + 1」\n要注意的點在於邊界控制 「idx+1 \u0026lt; len(nums)」。\n另外有嘗試過 「nums[idx] nums[idx-1] + 1」，也是可行，不過更要注意邊界控制，\n這例子要特別注意 「0, 1, 2」這種狀況，有可能 0 被單獨處理掉了。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 沒有 input 時，return nums\nBoundary conditions 見上述說明\nReference C++ | 12 lines | put dummy ","date":"2022-04-22T13:57:58+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-228/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [228] Summary Ranges 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 900 · Closest Binary Search Tree Value\n難度 easy\n個人範例程式碼 from lintcode import ( TreeNode, ) \u0026#34;\u0026#34;\u0026#34; Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param root: the given BST @param target: the given target @return: the value in the BST that is closest to the target \u0026#34;\u0026#34;\u0026#34; def closest_value(self, root: TreeNode, target: float) -\u0026gt; int: # write your code here if not root: return -1 upper = float(\u0026#34;inf\u0026#34;) lower = float(\u0026#34;-inf\u0026#34;) stack = collections.deque([root]) while stack: node = stack.pop() if node.val \u0026gt; target: upper = min(node.val, upper) if node.left: stack.append(node.left) elif node.val \u0026lt; target: lower = max(node.val, lower) if node.right: stack.append(node.right) else: # same value return node.val return upper if (upper - target \u0026lt; target - lower) else lower 算法說明 算是考 BST 的基本定義，這邊可以用圖來說明一下發生的現象。\n由圖上我們可以發現隨著搜尋過程中，target 區間正在不斷的縮小，此題就是利用這個特性。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling root 沒東西時，回傳 -1\nBoundary conditions 搜尋到沒有東西，或是搜尋到「等於 target (必定為最近)」，直接 return 了\nReference 二叉搜索树中最接近的值 · Closest Binary Search Tree Value ","date":"2022-04-22T13:33:30+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0320.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-900/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [900] Closest Binary Search Tree Value 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 173. Binary Search Tree Iterator\n難度 medium\n個人範例程式碼 - 2022/4/22 版本 (優化版) # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class BSTIterator: def __init__(self, root: Optional[TreeNode]): self.stack = [] self.to_left_bottom(root) def next(self) -\u0026gt; int: node = self.stack.pop() if node.right is not None: node_right = node.right self.to_left_bottom(node_right) return node.val def hasNext(self) -\u0026gt; bool: return len(self.stack) \u0026gt; 0 def to_left_bottom(self, root): while root: self.stack.append(root) root = root.left # Your BSTIterator object will be instantiated and called as such # obj = BSTIterator(root) # param_1 = obj.next() # param_2 = obj.hasNext() 算法說明 思路來源 與之前的相比，優化了 stack 的使用，不使用 top 的功能，只使用 pop 使程式碼更簡潔。\n一樣保持往左走到底的觀念，不過這次我們不扣住頂端，這樣的好處是 pop 會直接回到頂端不用再用另外一個 while loop 檢查，\n而只要發現能往右走，就往右走「一步」，然後依然「往左走到底」\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 當 stack 為空時，結束迴圈\n個人範例程式碼 - 2022/4/21 版本 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class BSTIterator: def __init__(self, root: Optional[TreeNode]): self.stack = [] self.to_left_bottom(root) def next(self) -\u0026gt; int: cur = node = self.stack[-1] if node.right is not None: node = node.right self.to_left_bottom(node) # do not pop parent else: node = self.stack.pop() while self.stack and self.stack[-1].right == node: # until parent\u0026#39;s right = n, next node = parent node = self.stack.pop() return cur.val def hasNext(self) -\u0026gt; bool: return len(self.stack) \u0026gt; 0 def to_left_bottom(self, root): while root: self.stack.append(root) root = root.left # Your BSTIterator object will be instantiated and called as such # obj = BSTIterator(root) # param_1 = obj.next() # param_2 = obj.hasNext() 算法說明 這題考的是程式設計，很多設計上的細節需要注意。\n而大方向如下：\n- 當到達一個新 node 時，往左邊存 stack 直到底 - 每 pop 一個 node 時，先檢查有沒有右側，有的話一樣往右做 1 (此 node 於 stack 尚未 pop)，沒有的話做 3 - 當已經找不到右邊時，下一個節點位於「一路 pop，直到 parent.right != node」(往右走的路都退回，找第一個左彎的點)，然後他的 parent 就是我們要的下一個 node 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 當 stack 為空時，結束迴圈\nReference 二叉查找树迭代器 · Binary Search Tree Iterator 86 · Binary Search Tree Iterator ","date":"2022-04-22T12:43:34+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0317.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-173/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [173] Binary Search Tree Iterator 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 448 · Inorder Successor in BST\n難度 medium\n個人範例程式碼 - recursive 版本 \u0026#34;\u0026#34;\u0026#34; Definition for a binary tree node. class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param: root: The root of the BST. @param: p: You need find the successor node of p. @return: Successor of p. \u0026#34;\u0026#34;\u0026#34; def inorderSuccessor(self, root, p): # write your code here if not root: return None self.found = False self.ans = None self.dfs(root, p) return self.ans def dfs(self, node, p): if not node: return self.dfs(node.left, p) if self.found == True: # next node self.ans = node self.found = False if node == p: self.found = True self.dfs(node.right, p) 算法說明 inorder 排序的延伸，這裡要先知道 Successor 的定義比較好解\nSuccessor 定義簡單就是：在 inorder 排序下，此 node 的下一個 node。\n而討論可能的相對位置 (不清楚可以想一下 inorder 定義)\n：\n如果有右子樹，就是右樹的第一個 node 如果沒有右樹，就是往上方的 node 尋找第一個左彎的 node 借一下 BST iterator 的 圖3，相對位置就是 next\n不過說實在，這樣還要討論太麻煩了，不如就直接無腦開始 traverse 還比較快XD\n這也是我最後的做法，只需要注意修改 inorder 的中間位置，\n這邊要增加讀取 val，且發現上一個為 p 時，需要做一下記號。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 找不到對應結果時，回傳 -1 (題目沒有要求這部份)\nBoundary conditions x\nReference Finding the In-Order Successor of a Node ","date":"2022-04-22T03:09:03+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0317.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-448/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [448] Inorder Successor in BST 個人解法筆記"},{"categories":["DFS (BST)"],"content":"題目出處 230. Kth Smallest Element in a BST\n難度 medium\n個人範例程式碼 - recursive 版本 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def kthSmallest(self, root: Optional[TreeNode], k: int) -\u0026gt; int: self.k = k self.final_ans = -1 self.dfs(root) return self.final_ans def dfs(self, root): if not root: return self.dfs(root.left) self.k -= 1 if self.k == 0: self.final_ans = root.val self.dfs(root.right) return 算法說明 建立一個 global 的計數器 (這設計可能不太好)，然後照 BST 的概念下去找第 K 小的值。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 找不到對應結果時，回傳 -1 (題目沒有要求這部份)\nBoundary conditions x\n個人範例程式碼 - iterative 版本 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def kthSmallest(self, root: Optional[TreeNode], k: int) -\u0026gt; int: self.stack = deque() self.to_left_bottom(root) while(k \u0026gt; 0): cur = node = self.stack[-1] if node.right is not None: node = node.right self.to_left_bottom(node) else: node = self.stack.pop() while self.stack and self.stack[-1].right == node: node = self.stack.pop() k -= 1 # get one node if k == 0: return cur.val else: return -1 def to_left_bottom(self, node): while(node): self.stack.append(node) node = node.left 算法說明 iterative 的解法，我們借用 Binary Search Tree Iterator 的概念來使用，可參考：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-173/\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 找不到對應結果時，回傳 -1 (題目沒有要求這部份)\nBoundary conditions x\nReference 902 · Kth Smallest Element in a BST 【Leetcode】python – [173] Binary Search Tree Iterator 個人解法筆記 #重要題型 ","date":"2022-04-22T02:09:26+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-bst/leetcode-python-230/","tags":["DFS"],"title":"【Leetcode】python - [230] Kth Smallest Element in a BST 個人解法筆記"},{"categories":["DFS (Tree Basic)","716 - Linked List"],"content":"題目出處 114. Flatten Binary Tree to Linked List\n難度 medium\n個人範例程式碼 - iterative 解 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def flatten(self, root: Optional[TreeNode]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify root in-place instead. \u0026#34;\u0026#34;\u0026#34; if not root: return stack = deque([root]) while stack: node = stack.pop() if node.right: stack.append(node.right) if node.left: stack.append(node.left) node.right = stack[-1] if stack else None # prev node -\u0026gt; current node node.left = None return 算法說明 個人覺得很酷的一題，直接把 Tree 改成 Linked List，考了非常多資料結構的操作。\n把前一個 node 連結至 stack[-1] 的位置。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當沒有 root 時，直接回傳\nBoundary conditions 搜尋至 None 時，return\n個人範例程式碼 - recursive 解 class Solution: \u0026#34;\u0026#34;\u0026#34; @param root: a TreeNode, the root of the binary tree @return: nothing \u0026#34;\u0026#34;\u0026#34; def flatten(self, root: TreeNode): # write your code here if not root: return self.prev = None self.dfs(root) return def dfs(self, root): if not root: return if self.prev is not None: self.prev.left = None self.prev.right = root self.prev = root tmp_right = root.right self.dfs(root.left) self.dfs(tmp_right) # root.right 回到此處時已經被修改 算法說明 這做法要注意的是，因為我們是 in-place 的改 node right，\n在 dfs 回來之後已經不是原來我們預期的那個 node.right，因此要先事前拷貝一份。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當沒有 root 時，直接回傳\nBoundary conditions 搜尋至 None 時，return\n個人範例程式碼 - recursive 解 (使用 global 變數，個人覺得比較不直覺一點) class Solution: \u0026#34;\u0026#34;\u0026#34; @param root: a TreeNode, the root of the binary tree @return: nothing \u0026#34;\u0026#34;\u0026#34; def flatten(self, root: TreeNode): # write your code here if not root: return self.prev = None self.dfs(root) return def dfs(self, root): if not root: return # divide self.dfs(root.right) self.dfs(root.left) # conquer right -\u0026gt; prev(deeper node) root.right = self.prev root.left = None self.prev = root 算法說明 這個方法使用了 class 的 global 變數，使用好不好見仁見智，\n但邏輯清楚一些，每次都記錄前一個 node 作為 prev，並把 current node 連結 prev node。\n這裡有個比較不直覺的地方，prev node 紀錄的是「相對比較底層的」node，\n因此是用 return 過程時的路徑 node.right 去連接 prev (這部分概念有點跟 prev 實質意義上顛倒)\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 當沒有 root 時，直接回傳\nBoundary conditions 搜尋至 None 時，return\nReference 453 · Flatten Binary Tree to Linked List ","date":"2022-04-21T19:57:16+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0318.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-114/","tags":["DFS"],"title":"【Leetcode】python - [114] Flatten Binary Tree to Linked List 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 578 · Lowest Common Ancestor III\n難度 medium\n個人範例程式碼 \u0026#34;\u0026#34;\u0026#34; Definition of TreeNode: class TreeNode: def __init__(self, val): this.val = val this.left, this.right = None, None \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param: root: The root of the binary tree. @param: A: A TreeNode @param: B: A TreeNode @return: Return the LCA of the two nodes. \u0026#34;\u0026#34;\u0026#34; def lowestCommonAncestor3(self, root, A, B): # write your code here self.found_A = False self.found_B = False ans = self.lca(root, A, B) return ans if (self.found_A and self.found_B) else None def lca(self, root, A, B): if not root: return None # divide found_left = self.lca(root.left, A, B) # keep traverse found_right = self.lca(root.right, A, B) # keep traverse if root == A: self.found_A = True if root == B: self.found_B = True # conquer if found_left is not None and found_right is not None: return root elif root in (A, B): # found root in A or B return root elif found_left: return found_left elif found_right: return found_right else: return None 算法說明 這已經是系列題目的第四題了，前面可參考\n此系列題目的第一題，由於設計上多了 parent node，相對來說比較沒有考到 LCA 的經常考點： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-474/\n此系列題目的第二題，BST 與 BT 只多了比較好找數字，基本上有正確實作 LCA 其實也不用管數字排序： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-235/\n此系列題目的第三題，與這題的差別在於，第四題會需要再多處理 A, B 可能不存在 Tree 中的情況： https://wongwongnotes.com/posts/algorithm-practice/patterns/leetcode/leetcode-python-236/\n修改部分，因為這題需要多處理「不存在樹中的情況」，\n我們必須徹底的 traverse 完整棵樹。\n這邊使用的策略是\n另外建一個 class 中 global 找到的 A, B 的 bool 回傳時，判斷此 node 是否為 A or B (找到的當下回傳) 之所以這樣能找到正確答案是因為，最早回傳的 A or B 會一路往上傳，\n而當 parent 如果發現也是 A or B，我們透過回傳「此 root」，就可以把結果洗掉。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\nReference 578 · Lowest Common Ancestor III ","date":"2022-04-21T14:46:50+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-578/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [578] Lowest Common Ancestor III 個人解法筆記"},{"categories":["730 - Leetcode 重要題型"],"content":"題目出處 236. Lowest Common Ancestor of a Binary Tree\n難度 medium\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, x) # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: \u0026#39;TreeNode\u0026#39;, p: \u0026#39;TreeNode\u0026#39;, q: \u0026#39;TreeNode\u0026#39;) -\u0026gt; \u0026#39;TreeNode\u0026#39;: return self.lca(root, p, q) def lca(self, root, p, q): if not root: return None if root \u0026lt;mark\u0026gt; p or root \u0026lt;/mark\u0026gt; q: return root # divide found_left = self.lca(root.left, p, q) found_right = self.lca(root.right, p, q) # conquer if found_left is not None and found_right is not None: return root elif found_left: return found_left elif found_right: return found_right else: # not found return None 算法說明 這已經是系列題目的第三題了，前面可參考\n此系列題目的第一題，由於設計上多了 parent node，相對來說比較沒有考到 LCA 的經常考點： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-474/\n此系列題目的第二題，BST 與 BT 只多了比較好找數字，基本上有正確實作 LCA 其實也不用管數字排序： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-235/\nLCA 的基本寫法，非常重要的題目。\n拆成四路分析，左右返回是否有找到 A or B\n討論 case 如下 如果兩側各返回 found，表示一邊各找到一個，此 node 即為答案 如果只有 left/right 返回 found，表示已經在下層找到答案，正在往上回傳。 如果都沒有 found，表示目前往下還找不到，返回 None 而特殊的 case 為 當 A 為 root，而 B 在此 root 之下，或相反的情況，\n此時我們做的處理為返回 root，因為兩者的交集就是在 root 是兩者同時的 LCA。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\nReference 474 · Lowest Common Ancestor II ","date":"2022-04-21T04:01:12+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/patterns/leetcode/leetcode-python-236/","tags":["LeetCode"],"title":"【Leetcode】python - [236] Lowest Common Ancestor of a Binary Tree 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 235. Lowest Common Ancestor of a Binary Search Tree\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, x) # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: \u0026#39;TreeNode\u0026#39;, p: \u0026#39;TreeNode\u0026#39;, q: \u0026#39;TreeNode\u0026#39;) -\u0026gt; \u0026#39;TreeNode\u0026#39;: return self.lca(root, p, q) def lca(self, root, p, q): if not root: return None if p \u0026lt;mark\u0026gt; root or q \u0026lt;/mark\u0026gt; root: return root # divide found_left = self.lca(root.left, p, q) found_right = self.lca(root.right, p, q) # conquer if found_left is not None and found_right is not None: # two side found return root elif found_left: # found left return found_left elif found_right: # found right return found_right else: # not found return None 算法說明 此題的前一題，由於設計上多了 parent node，相對來說比較沒有考到 LCA 的經常考點： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-474/\nLCA 的基本寫法，非常重要的題目。\n拆成四路分析，左右返回是否有找到 A or B\n討論 case 如下 如果兩側各返回 found，表示一邊各找到一個，此 node 即為答案 如果只有 left/right 返回 found，表示已經在下層找到答案，正在往上回傳。 如果都沒有 found，表示目前往下還找不到，返回 None 而特殊的 case 為 當 A 為 root，而 B 在此 root 之下，或相反的情況，\n此時我們做的處理為返回 root，因為兩者的交集就是在 root 是兩者同時的 LCA。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\nReference 474 · Lowest Common Ancestor II ","date":"2022-04-21T03:48:49+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-235/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [235] Lowest Common Ancestor of a Binary Search Tree 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 474 · Lowest Common Ancestor II\n難度 easy\n個人範例程式碼 \u0026#34;\u0026#34;\u0026#34; Definition of ParentTreeNode: class ParentTreeNode: def __init__(self, val): self.val = val self.parent, self.left, self.right = None, None, None \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param: root: The root of the tree @param: A: node in the tree @param: B: node in the tree @return: The lowest common ancestor of A and B \u0026#34;\u0026#34;\u0026#34; def lowestCommonAncestorII(self, root, A, B): # use hashset path_of_A = set() while(A): path_of_A.add(A) A = A.parent while(B): if B in path_of_A: return B else: B = B.parent return None 算法說明 LCA 的基本題型，或是說更簡單的，這題中的 TreeNode 設計中多了 parent，使題目更簡單了。\n我們可以快速使用 set() 幫我們記憶路徑，從底部往上找 parent，\n兩者對比後即可快速得到答案。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\nReference 474 · Lowest Common Ancestor II ","date":"2022-04-21T03:39:47+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-474/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [474] Lowest Common Ancestor II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 596 · Minimum Subtree\n難度 easy\n個人範例程式碼 from lintcode import ( TreeNode, ) \u0026#34;\u0026#34;\u0026#34; Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None \u0026#34;\u0026#34;\u0026#34; class Solution: \u0026#34;\u0026#34;\u0026#34; @param root: the root of binary tree @return: the root of the minimum subtree \u0026#34;\u0026#34;\u0026#34; def __init__(self): # class variable (global in class) self.subtree = TreeNode(0) self.subtree_sum = float(\u0026#34;inf\u0026#34;) def find_subtree(self, root: TreeNode) -\u0026gt; TreeNode: self.dfs(root) return self.subtree def dfs(self, root): if not root: return 0 thissum = self.dfs(root.left) + self.dfs(root.right) + root.val if thissum \u0026lt; self.subtree_sum: self.subtree_sum = thissum self.subtree = root return thissum 算法說明 基礎的 DFS 用來 traverse Tree 裡面的內容，這次是從底下算上來，慢慢地找最小總和。\n這邊我們為了方便處理，我們多寫了一個 「def init(self)」給他，\n定義了在此 class function 裡面的 global 變數。\n不過這樣的寫法不確定好不好，\n畢竟題目說不定本來就有內建了 init(self) 在裡面，\n而只希望我們調用題目所給的 template「def find_subtree(self, root: TreeNode) -\u0026gt; TreeNode:」並修改這之後的內容。\n更新：其實這部分做點小修改就行，我們把具有 global 功能的 self.subtree、self.subtree_sum，\n定義於 「def find_subtree()」即可。\ndef __init__(self): # class variable (global in class) self.subtree = TreeNode(0) self.subtree_sum = float(\u0026#34;inf\u0026#34;) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions end of recursion dfs 直到最 None 的節點，回傳 0\ndefine of recursion 每個 node 的和，等於「左子樹和 + 右子樹和 + 自己」\nthissum = self.dfs(root.left) + self.dfs(root.right) + root.val 每次都與 global 的 self.subtree_sum 比較，\n如果比較小，就更新 node 與 新的最小 sum。\nif thissum \u0026lt; self.subtree_sum: self.subtree_sum = thissum self.subtree = root split of recursion 我們在上述一併做掉了，請見\nthissum = self.dfs(root.left) + self.dfs(root.right) + root.val Reference ","date":"2022-04-20T00:44:09+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-596/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [596] Minimum Subtree 個人解法筆記"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 113. Path Sum II\n難度 medium\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def pathSum(self, root: Optional[TreeNode], targetSum: int) -\u0026gt; List[List[int]]: ans = [] self.dfs(root, targetSum, [], ans) return ans def dfs(self, root, target, path, ans): # None if not root: return path.append(root.val) # Leaf if not root.left and not root.right: if sum(path) == target: ans.append(path[:]) # copy a new one path.pop() return # not leaf self.dfs(root.left, target, path, ans) self.dfs(root.right, target, path, ans) path.pop() return 算法說明 此題的前一個題目，不同處在於需要多求出完整路徑，可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-112/\n基礎的 DFS 用來 traverse Tree 裡面的內容，並沿路計算總和。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有正確結果，回傳 []\nBoundary conditions end of recursion 判斷 Null 或是 Leaf\nNull False\nif not root: return Leaf 判斷是否總和正確，如果是則加入目前的 path\n注意：因為 list 在 python function 中，pass by assignment 可以變更 mutable object\n因此我們複製時要複製 「ans.append(path[:])」，等於創造出一個新的物件，\n不然最後的結果會隨著 path 改變而跟著變動。\n# Leaf if not root.left and not root.right: if sum(path) == target: ans.append(path[:]) # copy a new one path.pop() return define of recursion 增加路過的路徑。\npath.append(root.val) 重要：這邊務必注意，因為 list 在 python function 中，pass by assignment 可以變更 mutable object，所以勢必要注意對應的 pop() 位置在哪。 path.pop() split of recursion 拆成兩路，同樣的也如同我們上面說要注意的點。\n我們先把結果拿回來，再把 path 的內容先 pop 出來後，再把 ans 回傳。\n# not leaf self.dfs(root.left, target, path, ans) self.dfs(root.right, target, path, ans) path.pop() Reference Python solutions (Recursively, BFS+queue, DFS+stack) 376 · Binary Tree Path Sum 376 · Binary Tree Path Sum 376 · Binary Tree Path Sum Python 是 Pass By Value, Pass by Reference, 還是 Pass by Sharing？ ","date":"2022-04-20T00:21:55+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-113/","tags":["DFS"],"title":"【Leetcode】python - [113] Path Sum II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 112. Path Sum\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -\u0026gt; bool: return self.dfs(root, targetSum, []) def dfs(self, root, target, path): # Null if not root: return False path.append(root.val) # leaf if not root.left and not root.right: if sum(path) == target: path.pop() return True # not leaf ans = self.dfs(root.left, target, path) or self.dfs(root.right, target, path) # need pop path before return path.pop() return ans 算法說明 基礎的 DFS 用來 traverse Tree 裡面的內容，並沿路計算總和。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有正確結果，回傳 False\nBoundary conditions end of recursion 判斷 Null 或是 Leaf\nNull False\n# Null if not root: return False Leaf 判斷是否總和正確，如果是回傳 True\n# leaf if not root.left and not root.right: if sum(path) == target: path.pop() return True define of recursion 增加路過的路徑。\npath.append(root.val) 重要：這邊務必注意，因為 list 在 python function 中，pass by assignment 可以變更 mutable object，所以勢必要注意對應的 pop() 位置在哪。 path.pop() split of recursion 拆成兩路，同樣的也如同我們上面說要注意的點。\n我們先把結果拿回來，再把 path 的內容先 pop 出來後，再把 ans 回傳。\n# not leaf ans = self.dfs(root.left, target, path) or self.dfs(root.right, target, path) # need pop path before return Reference Short Python recursive solution - O(n) Python 是 Pass By Value, Pass by Reference, 還是 Pass by Sharing？ ","date":"2022-04-20T00:02:52+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-112/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [112] Path Sum 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 257. Binary Tree Paths\n難度 easy\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def binaryTreePaths(self, root: Optional[TreeNode]) -\u0026gt; List[str]: ans = [] self.dfs(root, \u0026#34;\u0026#34;, ans) return ans def dfs(self, root, path, ans): if not root: # Null point return if not root.left and not root.right: # last point path += f\u0026#34;{root.val}\u0026#34; ans.append(path) return path += f\u0026#34;{root.val}-\u0026gt;\u0026#34; self.dfs(root.left, path, ans) self.dfs(root.right, path, ans) return 算法說明 基礎的 DFS 用來 traverse Tree 裡面的內容，並回傳路徑。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有內容，回傳 []\nBoundary conditions end of recursion 判斷 Null 或是 Leaf\nNull if not root: # Null point Leaf if not root.left and not root.right: # last point define of recursion 單純增加路過的路徑。\npath += f\u0026#34;{root.val}-\u0026gt;\u0026#34; split of recursion 拆成兩路\nself.dfs(root.left, path, ans) self.dfs(root.right, path, ans) Reference 480 · Binary Tree Paths ","date":"2022-04-19T23:43:34+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-257/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [257] Binary Tree Paths 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 210. Course Schedule II\n難度 medium\n個人範例程式碼 class Solution: def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -\u0026gt; List[int]: return self.bfs(numCourses, prerequisites) def bfs(self, num_courses, prerequisites): next_courses, indegree = self.get_indegree(num_courses, prerequisites) start_courses = [course for course, degree in indegree.items() if degree == 0] learned_courses = [] queue = collections.deque(start_courses) visited = set() while queue: course = queue.popleft() learned_courses.append(course) visited.add(course) for next_course in next_courses[course]: indegree[next_course] -= 1 if indegree[next_course] == 0 and next_course not in visited: queue.append(next_course) else: return learned_courses if num_courses == len(learned_courses) else [] def get_indegree(self, num_courses, prerequisites): next_courses = {x:[] for x in range(num_courses)} indegree = {x:0 for x in range(num_courses)} for course, next_course in prerequisites: next_courses[next_course].append(course) indegree[course] += 1 # print(next_courses, indegree) return next_courses, indegree 算法說明 此問題的前一個問題，主要差別是在有沒有要求輸出一個明確的結果，可以參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-207/ 這題考的是 BFS 中的拓樸排序法，個人認為比較難的是在 graph 操作的部分，其他的概念相對來說還好。\n另外處理 start_courses ，去尋找 indegree 為 0 的 node\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果 Graph 有 loop，會導致無法上完全部課程，此時回傳 [] Boundary conditions 當 queue 為空時，結束迴圈，並判斷是否上完全部課程。\nReference 课程表 · Course Schedule ","date":"2022-04-19T23:27:58+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-210/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [210] Course Schedule II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 207. Course Schedule\n難度 medium\n個人範例程式碼 class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -\u0026gt; bool: if not prerequisites: # no course return True return self.bfs(numCourses, prerequisites) def bfs(self, num_courses, prerequisites): # init pre_courses, next_courses = self.get_table(num_courses, prerequisites) start_courses = [] learned_course = [] for course, pre in pre_courses.items(): if not pre: start_courses.append(course) queue = collections.deque(start_courses) visited = set() while queue: course = queue.popleft() learned_course.append(course) visited.add(course) for next_course in next_courses[course]: pre_courses[next_course].remove(course) if not pre_courses[next_course] and next_course not in visited: # no pre-courses queue.append(next_course) else: return num_courses == len(learned_course) def get_table(self, num_courses, prerequisites): pre_courses = {x:[] for x in range(num_courses)} # for record studied next_courses = {x:[] for x in range(num_courses)} # for search for course in prerequisites: pre_courses[course[1]].append(course[0]) next_courses[course[0]].append(course[1]) return pre_courses, next_courses 算法說明 這題考的是 BFS 中的拓樸排序法，個人認為比較難的是在 graph 操作的部分，其他的概念相對來說還好。\n另外處理 start_courses ，去尋找 indegree 為 0 的 node\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 如果沒有 prerequisites，則必 True (無課程或無先修課)\nBoundary conditions 當 queue 為空時，如果還沒有辦法上完全部的課程，則為 False\n(可能 Graph 有 loop)\nReference 课程表 · Course Schedule ","date":"2022-04-19T23:17:27+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-207/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [207] Course Schedule 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2244. Minimum Rounds to Complete All Tasks\n難度 medium\n個人範例程式碼 class Solution: def minimumRounds(self, tasks: List[int]) -\u0026gt; int: # 1: -1 round # 2: 1 round # 3: 2 rounds # 3k: k rounds # 3k+1: 4=3(k-1)+2+2, k+1 times # 3k+2: 5=3(1)+2, k+1 times counters = Counter(tasks) ans = 0 for num_of_task in counters.values(): if num_of_task \u0026lt;= 1: return -1 else: # 3 = 1 # 4 = 2 # 5 = 2 # 6 = 2 ans += (num_of_task+2)//3 return ans 算法說明 這題考的是數學歸納法的概念，比較是數學的範圍\n我們推論：\n1: -1 round 2: 1 round 3: 2 rounds 3k: k rounds 3k+1: 4=3(k-1)+2+2, k+1 times 3k+2: 5=3(1)+2, k+1 times 而為了計算方便，我們可以做一些使計算上方便的技巧。\n3 = 1 4 = 2 5 = 2 6 = 2 我們透過移動 k+2 使得 // 3 後能夠直接得到我們要的數字。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling x\nBoundary conditions 只要發現其中一個任務不可能執行完成，提早 return -1。\nReference [Java/C++/Python] Sum up (freq + 2) / 3 ","date":"2022-04-17T17:42:19+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2244/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2244] Minimum Rounds to Complete All Tasks 個人解法筆記 | 289th LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2243. Calculate Digit Sum of a String\n難度 easy\n個人範例程式碼 class Solution: def digitSum(self, s: str, k: int) -\u0026gt; str: # print(s) # end if len(s) \u0026lt;= k: return s # define end_string = \u0026#34;\u0026#34; start = 0 while(start \u0026lt; len(s)): string_sum = self.get_string_sum(s[start:min(start+k, len(s))]) end_string += string_sum start += k # split return self.digitSum(end_string, k) def get_string_sum(self, s): start = 0 string_sum = 0 while(start \u0026lt;= len(s)-1): string_sum += int(s[start]) start += 1 return str(string_sum) 算法說明 - str -\u003e int 三個一數，計算新的結果，最後處理結尾部分 - int -\u003e str 開始下一串字的拼接 循環至長度 len(s) \u0026lt;= k 後結束。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 len(s) \u0026lt;= k 的情況，這裡我是直接回傳 s。\nBoundary conditions 注意結束條件：\n循環至長度 len(s) \u0026lt;= k 後結束。\nReference ✅✅C++ || Easy Solution || 0ms || Faster ","date":"2022-04-17T16:34:03+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2243/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2243] Calculate Digit Sum of a String 個人解法筆記 | 289th LeetCode Weekly Contest"},{"categories":["710 - Python LeetCode"],"content":"題目出處 2. Add Two Numbers\n難度 medium\n個人範例程式碼 基本版本 # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -\u0026gt; Optional[ListNode]: dummy = head = ListNode(0) carry = 0 while(l1 and l2): this_val = l1.val + l2.val + carry carry = this_val // 10 head.next = ListNode(this_val % 10) l1 = l1.next l2 = l2.next head = head.next else: while(l1): this_val = l1.val + carry carry = this_val // 10 head.next = ListNode(this_val % 10) l1 = l1.next head = head.next while(l2): this_val = l2.val + carry carry = this_val // 10 head.next = ListNode(this_val % 10) l2 = l2.next head = head.next if carry: head.next = ListNode(carry) return dummy.next 算法說明 - 當 l1, l2 還有值時，為第一階段 - 其中一個沒有值時，用 while 去將剩下的 l1 或 l2 掃完 - 最後判定有沒有剩餘的 carry 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為空的情況，這裡我是直接回傳 None。\nBoundary conditions 注意 LinkList 的結束條件：\n結束的 None 也算是一個節點。 會被 if node 判為 False。 與之相對應的是使用 node.next 來判斷，\n可是因為 node.next 依然可以走到 None 的 Node，因此容易因此而判錯。\n優化版本 優化，將 l1, l2, carry 有無剩餘的內容一起看，一路處理到沒剩餘內容為止。\n# Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -\u0026gt; Optional[ListNode]: dummy = head = ListNode(0) carry = 0 while(l1 or l2 or carry): # something still exist if l1: carry += l1.val l1 = l1.next if l2: carry += l2.val l2 = l2.next head.next = ListNode(carry % 10) head = head.next carry = carry // 10 else: return dummy.next Reference Is this Algorithm optimal or what? ","date":"2022-04-16T22:37:42+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2] Add Two Numbers 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 133. Clone Graph\n難度 medium\n個人範例程式碼 \u0026#34;\u0026#34;\u0026#34; # Definition for a Node. class Node: def __init__(self, val = 0, neighbors = None): self.val = val self.neighbors = neighbors if neighbors is not None else [] \u0026#34;\u0026#34;\u0026#34; class Solution: def cloneGraph(self, node: \u0026#39;Node\u0026#39;) -\u0026gt; \u0026#39;Node\u0026#39;: root = node if node is None: return node # traverse, get all nodes by BFS old_nodes = self.get_all_nodes(node) # print(len(nodes)) # clone all nodes mapping = {} for old_node in old_nodes: mapping[old_node] = Node(old_node.val) # clone all edges for old_node in old_nodes: # new_node = mapping[old_node] for neighbor in old_node.neighbors: new_neighbor = mapping[neighbor] new_node.neighbors.append(new_neighbor) return mapping[root] def get_all_nodes(self, node): queue = [node] result = set([node]) while queue: head = queue.pop(0) for neighbor in head.neighbors: if neighbor not in result: result.add(neighbor) queue.append(neighbor) return result 算法說明 不好處理的題目，題目本身不難，但基本上可以視為 Graph 的基本操作題型。\n我們分成三大步驟\n- 找到所有的點，透過 BFS traverse - 複製所有的點 - 複製所有的邊 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為空的情況，這裡我是直接回傳 node。\nBoundary conditions 注意 BFS 的結束條件：\n當 queue 為空，所有的點都為 visited Reference 克隆图 · Clone Graph ","date":"2022-04-16T22:06:51+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-133/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [133] Clone Graph 個人解法筆記 | Graph 的基本操作 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 263. Ugly Number\n難度 Easy\n個人範例程式碼 class Solution: def isUgly(self, n: int) -\u0026gt; bool: if not n: return False if n \u0026lt;= 0: return False if n == 1: return True while(n%2 == 0): n = n // 2 while(n%3 == 0): n = n // 3 while(n%5 == 0): n = n // 5 return (n == 1) 算法說明 沒什麼好說的\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意定義，小於等於 0 都不是，return False\n等於 1 時，return True\nBoundary conditions 當不能整除時，結束迴圈。\nReference 2-4 lines, every language ","date":"2022-04-15T19:59:02+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-263/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [263] Ugly Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 127. Word Ladder\n難度 hard\n個人範例程式碼 class Solution: def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -\u0026gt; int: if endWord not in wordList: return 0 # not in wordlist wordset = set(wordList) this_layer = [beginWord] visited = set([beginWord]) distance = 0 # BFS, thinking of layer while this_layer: distance += 1 # new bfs layer next_layer = [] for each_word in this_layer: if each_word == endWord: # find ans return distance for next_word in self.get_next_word(each_word, wordset): if next_word in visited: pass else: next_layer.append(next_word) visited.add(next_word) this_layer = next_layer return 0 # not found def get_next_word(self, each_word, wordset): # find all possible case words = [] for i, each_char in enumerate(each_word): left, right = each_word[:i], each_word[i+1:] for char in \u0026#39;abcdefghijklmnopqrstuvwxyz\u0026#39;: if each_word[i] == char: continue else: new_word = left + char + right if new_word in wordset: words.append(new_word) return words 算法說明 看似 string 其實是一個 Graph 的題目，透過 BFS 來分層遍歷，找到最淺能達到目標的點。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\nget_next_word 這裡我們使用的方式比較單純，可以採取的策略有：\n從 wordList 去反推下一層可能的文字 直接拆字去推，再去 wordList 找 個人使用的是第二種方法。\n注意：加速技巧 原本題目使用的 wordList 搜尋時間為：O(n)，\n若改為 set, hashmap，時間可以壓縮至 O(1)\ninput handling 處理 input endWord 不在 wordList 的情況，輸出 0 。\nBoundary conditions 迴圈結束於\n找到目標 找不到目標，且沒有下一層 layer (全部都 visited) Reference 单词接龙 · Word Ladder TimeComplexity ","date":"2022-04-15T18:46:13+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-127/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [127] Word Ladder 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 121. Best Time to Buy and Sell Stock\n難度 Easy\n題目分類 array, dynamic-programming\n個人範例程式碼 - 2022/4/15 class Solution: def maxProfit(self, prices: List[int]) -\u0026gt; int: if not prices or len(prices) \u0026lt; 2: return 0 min_buy = prices[0] best_profit = 0 for i, today_price in enumerate(prices): if i == 0: # pass first day continue else: min_buy = min(min_buy, today_price) best_profit = max(best_profit, today_price - min_buy) return best_profit 算法說明 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有 input 或 input 只有一天 (沒得賣啊!) 的情況，return 0\nBoundary conditions for 迴圈，不怕出界\n個人範例程式碼 - 2022/3/2 class Solution: def maxProfit(self, prices: List[int]) -\u0026gt; int: min_buyin_record = 999999 # min buy in res_record = 0 # max result for today in range(len(prices)): if today == 0: # first min_buyin_record = prices[today] else: min_buyin_record = min(min_buyin_record, prices[today]) res_record = max(res_record, prices[today] - min_buyin_record) return res_record Time Complexity O(n)\n算法說明 唯一要記錄只有兩個\n到今日之前的「購入最低價」 到今日之前的「最佳結果」 corner case 特殊情況處理 當只有一天時，需要回傳 0，\n這邊我在初始化時解掉了。\nBoundary conditions/ Edge conditions 邊際情況處理 初始值處理 第一天時，因為沒有最大值，我特別去判斷現在進來的是不是第一天， 不過後來有使用另外一個做法，先定義最小值為「999999」， 預設第一天就會被取代。 這個可以成功的原因是因為：\n題目限制「0 \u0026lt;= prices[i] \u0026lt;= 10^4」\n兩種作法應該都可以，我之後應該會選擇第一種，去避免自訂義值的情況。\nReference ","date":"2022-04-15T14:39:20+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-121/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [121] Best Time to Buy and Sell Stock 個人解法筆記"},{"categories":["715 - Binary Serach"],"content":"題目出處 143 · Sort Colors II\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param colors: A list of integer @param k: An integer @return: nothing \u0026#34;\u0026#34;\u0026#34; def sort_colors2(self, colors: List[int], k: int): # write your code here # sort 1~k self.partition(colors, 1, k, 0, len(colors)-1) return def partition(self, colors, color_from, color_to, start, end): # end if color_from \u0026lt;mark\u0026gt; color_to or start \u0026lt;/mark\u0026gt; end: return left, right = start, end pivot = (color_from + color_to) // 2 # define while(left \u0026lt;= right): while(left \u0026lt;= right and colors[left] \u0026lt;= pivot): # if same, left part left += 1 while(left \u0026lt;= right and colors[right] \u0026gt; pivot): right -= 1 if(left \u0026lt;= right): colors[left], colors[right] = colors[right], colors[left] # careful infinite loop: left, right still can move else: # split # print(colors, start, right, left, end) self.partition(colors, color_from, pivot, start, right) self.partition(colors, pivot+1, color_to, left, end) 算法說明 此題是考 partition 的綜合題。\n這題會經常錯在遞迴判斷式與邊界條件，導致無法 AC。\n遞迴判斷式 遞迴的定義 # define while(left \u0026lt;= right): while(left \u0026lt;= right and colors[left] \u0026lt;= pivot): # if same, left part left += 1 while(left \u0026lt;= right and colors[right] \u0026gt; pivot): right -= 1 if(left \u0026lt;= right): colors[left], colors[right] = colors[right], colors[left] # careful infinite loop: left, right still can move 遞迴的拆解 這邊要處理的是邊界問題，往前我們還需要多注意 while(left \u0026lt;= right)，\n這會導致我們是「交錯」才結束，\n結束時的狀態為 「start \u0026lt; right \u0026lt; left \u0026lt; end」，由於與 pivot 相等的在兩側都有可能出現，\n拆解的搜尋條件為\nstart ~ right left ~ end self.partition(colors, color_from, pivot, start, right) self.partition(colors, pivot+1, color_to, left, end) 遞迴的中止 如果「color_from color_to」 or 「start end」，return\n「color_from color_to」：已經同顏色，不必再分 「start end」：同位置，不必再分 input handling 這題沒有特別說要怎麼處理特殊的 input，預設的 input 都給正確的。\nBoundary conditions 上方已經說明完囉\n另解 - 更換中止條件 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param colors: A list of integer @param k: An integer @return: nothing \u0026#34;\u0026#34;\u0026#34; def sort_colors2(self, colors: List[int], k: int): # write your code here # sort 1~k self.partition(colors, 1, k, 0, len(colors)-1) return def partition(self, colors, color_from, color_to, start, end): # end if color_from == color_to: return left, right = start, end pivot = (color_from + color_to) // 2 # define while(left \u0026lt;= right): while(left \u0026lt;= right and colors[left] \u0026lt;= pivot): # if same, left part left += 1 while(left \u0026lt;= right and colors[right] \u0026gt; pivot): right -= 1 if(left \u0026lt;= right): colors[left], colors[right] = colors[right], colors[left] # careful infinite loop: left, right still can move else: # split # print(colors, start, right, left, end) if start \u0026lt; right: # still need sorting self.partition(colors, color_from, pivot, start, right) if left \u0026lt; end: # still need sorting self.partition(colors, pivot+1, color_to, left, end) 更換部分 當顏色一樣的時候，中止 # end if color_from == color_to: return 限制範圍才持續往下做 partition (其實也等同於 start end) if start \u0026lt; right: # still need sorting self.partition(colors, color_from, pivot, start, right) if left \u0026lt; end: # still need sorting self.partition(colors, pivot+1, color_to, left, end) Reference 颜色分类 II · Sort Colors II ","date":"2022-04-14T20:15:54+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/binary-search/lintcode-python-143/","tags":[],"title":"【Lintcode】python - [143] Sort Colors II 個人解法筆記 #重要題型 #常錯題型"},{"categories":["715 - Binary Serach"],"content":"題目出處 75. Sort Colors\n難度 medium\n個人範例程式碼 class Solution: def sortColors(self, nums: List[int]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums in-place instead. \u0026#34;\u0026#34;\u0026#34; # only 0, 1, 2 self.partition(nums, 2, 0, len(nums)-1) return def partition(self, nums, pivot, start, end): if pivot == 0: return left, right = start, end while(left \u0026lt;= right): while(left \u0026lt;= right and nums[left] \u0026lt; pivot): # 0, 1 left += 1 while(left \u0026lt;= right and nums[right] \u0026gt;= pivot): # 2 right -= 1 if(left \u0026lt;= right): nums[left], nums[right] = nums[right], nums[left] # check twice # left += 1 # right -= 1 else: # after left and right cross, all 0, 1 will be left, 2 will be right # start, right(0, 1), left(2), end # start, right(0), left(1), end self.partition(nums, pivot-1, 0, right) 算法說明 此題是考 partition 的延伸題，也可以單純使用三指標來快速解掉，\n不過之後還有一題多顏色的題目，基本上 partition 還是要會解。\n這題會經常錯在遞迴判斷式與邊界條件，導致無法 AC。\n遞迴判斷式 遞迴的定義 while(left \u0026lt;= right): while(left \u0026lt;= right and nums[left] \u0026lt; pivot): # 0, 1 left += 1 while(left \u0026lt;= right and nums[right] \u0026gt;= pivot): # 2 right -= 1 if(left \u0026lt;= right): nums[left], nums[right] = nums[right], nums[left] 遞迴的拆解 這邊要處理的是邊界問題，往前我們還需要多注意 while(left \u0026lt;= right)，\n這會導致我們是「交錯」才結束，\n結束時的狀態為 「start \u0026lt; right \u0026lt; left \u0026lt; end」，由於與 pivot 相等的在兩側都有可能出現，\n拆解的搜尋條件為\nstart ~ right left ~ end 這題本身較簡單，但其實不需要拆到那麼複雜，\n但為了後續的 partition 題目方便，這裡就先用最泛用的方式解\nself.partition(nums, pivot-1, 0, right) 遞迴的中止 如果 pivot = 0，return\n一樣也是因為這題題目相對單純，因此我們可以這樣寫死。\ninput handling 這題沒有特別說要怎麼處理特殊的 input，預設的 input 都給正確的。\nBoundary conditions 上方已經說明完囉\nReference 颜色分类 · Sort Colors ","date":"2022-04-14T19:51:56+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/binary-search/leetcode-python-75/","tags":[],"title":"【Leetcode】python - [75] Sort Colors 個人解法筆記 #重要題型 #常錯題型"},{"categories":["715 - Binary Serach"],"content":"題目出處 5 · Kth Largest Element\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param k: An integer @param nums: An array @return: the Kth largest element \u0026#34;\u0026#34;\u0026#34; def kth_largest_element(self, k: int, nums: List[int]) -\u0026gt; int: # write your code here if not nums: return -1 # not found # 1, 2, 3, first: idx = 2 = len(s)-k (k=1) return self.quickselect(k-1, nums, 0, len(nums)-1) def quickselect(self, k, nums, start, end): if not nums: return -1 # recursion end if start == end: return nums[k] # recursion define left, right = start, end pivot = nums[(start + end) // 2] while(left \u0026lt;= right): while(left \u0026lt;= right and nums[left] \u0026gt; pivot): # bigger go to left, descending sorting left += 1 while(left \u0026lt;= right and nums[right] \u0026lt; pivot): right -= 1 if(left \u0026lt;= right): nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1 else: # recursion split # start \u0026lt; right \u0026lt; left \u0026lt; end # print(nums) if start \u0026lt;= right and right \u0026gt;= k: # search left and still need sorting return self.quickselect(k, nums, start, right) elif left \u0026lt;= end and left \u0026lt;= k: # search right and still need sorting return self.quickselect(k, nums, left, end) else: return nums[k] 算法說明 此題有類似題，可參考： https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-461/\n最大的差別只在於 smallest 變 largest，簡單處理把 ascending 換成 descending 即可。\n這題用到 QuickSelect 的概念，簡單來說就是藉由「每一次都切一半，減少一半的搜尋範圍」，\n分出剩餘的另外一半後，再去嘗試切一半，進行 O(logN) 的快速尋找。\n這題會經常錯在遞迴判斷式與邊界條件，導致無法 AC。\n遞迴判斷式 遞迴的定義 這裡我自己錯好多次，總之不熟悉處理起來很麻煩，\n導入 quickselect 的概念，我們把原本要搜尋的範圍切成兩半，\n將比 pivot 小的往左擺，大的往右擺，一樣大的先不管\n(管了會出事，會有無限迴圈：例 [3, 9, 4, 8]: 9 never moves.)\n遞迴的拆解 這邊要處理的是邊界問題，往前我們還需要多注意 while(left \u0026lt;= right)，\n這會導致我們是「交錯」才結束，\n結束時的狀態為 「start \u0026lt; right \u0026lt; left \u0026lt; end」，由於與 pivot 相等的在兩側都有可能出現，\n拆解的搜尋條件為\nstart ~ right left ~ end 可附加條件為 「right \u0026gt;= k」、「left \u0026lt;= k」，我們可以提早結束 k 的搜尋。\nstart ~ right, right \u0026gt;= k：左側還比 k 多，需要再排序 left ~ end, left \u0026lt;= k：右側還比 k 少 (= 左側比 k 多)，需要再排序 遞迴的中止 如果 start end，return nums[k] ，\n怎麼保證這一定會發生就是個大問題了，這也是最常出錯的地方，我錯好多次\u0026hellip;\n簡單來說我們可以想一些比較極端的 case，程式也都要能正常結束。\n例如： [1,1]，k = 1 (index 0), k = 2(index 1)\n這邊我暫時先這樣想，哪天有更好的想法再回來補吧。\ninput handling 這題沒有特別說要怎麼處理特殊的 input，預設的 input 都給正確的。\nBoundary conditions 上方已經說明完囉\nReference 无序数组K小元素 · Kth Smallest Numbers in Unsorted Array ","date":"2022-04-13T23:45:45+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/binary-search/lintcode-python-5/","tags":[],"title":"【Lintcode】python - [5] Kth Largest Element 個人解法筆記 #重要題型 #常錯題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 461 · Kth Smallest Numbers in Unsorted Array\n難度 medium\n個人範例程式碼 - 1 (相等時結束的討論) from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param k: An integer @param nums: An integer array @return: kth smallest element \u0026#34;\u0026#34;\u0026#34; def kth_smallest(self, k: int, nums: List[int]) -\u0026gt; int: # write your code here return self.quickselect(nums, k-1, 0, len(nums)-1) def quickselect(self, nums, k, start, end): # recusion end if start \u0026lt;mark\u0026gt; end: # (\u0026lt;/mark\u0026gt;k) return nums[k] # recursion define left, right = start, end pivot = nums[(start + end) // 2] # print(nums, pivot, start, end) while(left \u0026lt;= right): if nums[left] \u0026lt; pivot: # [3, 9, 4, 8]: 9 never moves. left += 1 elif nums[right] \u0026gt; pivot: right -= 1 else: nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1 else: # two part start \u0026lt; right \u0026lt; left \u0026lt; end (\u0026lt;= , \u0026gt;) # recursion split if right \u0026gt;= k and start \u0026lt;= right: # search left return self.quickselect(nums, k, start, right) elif left \u0026lt;= k and left \u0026lt;= end: # search right return self.quickselect(nums, k, left, end) else: return nums[k] 算法說明 這題用到 QuickSelect 的概念，簡單來說就是藉由「每一次都切一半，減少一半的搜尋範圍」，\n分出剩餘的另外一半後，再去嘗試切一半，進行 O(logN) 的快速尋找。\n這題會經常錯在遞迴判斷式與邊界條件，導致無法 AC。\n遞迴判斷式 遞迴的定義 這裡我自己錯好多次，總之不熟悉處理起來很麻煩，\n導入 quickselect 的概念，我們把原本要搜尋的範圍切成兩半，\n將比 pivot 小的往左擺，大的往右擺，一樣大的先不管\n(管了會出事，會有無限迴圈：例 [3, 9, 4, 8]: 9 never moves.)\n遞迴的拆解 這邊要處理的是邊界問題，往前我們還需要多注意 while(left \u0026lt;= right)，\n這會導致我們是「交錯」才結束，\n結束時的狀態為 「start \u0026lt; right \u0026lt; left \u0026lt; end」，由於與 pivot 相等的在兩側都有可能出現，\n拆解的搜尋條件為\nstart ~ right left ~ end 可附加條件為 「right \u0026gt;= k」、「left \u0026lt;= k」，我們可以提早結束 k 的搜尋。\nstart ~ right, right \u0026gt;= k：左側還比 k 多，需要再排序 left ~ end, left \u0026lt;= k：右側還比 k 少 (= 左側比 k 多)，需要再排序 遞迴的中止 如果 start end，return nums[k] ，\n怎麼保證這一定會發生就是個大問題了，這也是最常出錯的地方，我錯好多次\u0026hellip;\n簡單來說我們可以想一些比較極端的 case，程式也都要能正常結束。\n例如： [1,1]，k = 1 (index 0), k = 2(index 1)\n這邊我暫時先這樣想，哪天有更好的想法再回來補吧。\ninput handling 這題沒有特別說要怎麼處理特殊的 input，預設的 input 都給正確的。\nBoundary conditions 上方已經說明完囉\n稍微修改一下解，修改一下定義式 這邊可以將上面的解答稍微修改一下，可能概念上會更漂亮一些。(因人而異)\n主要修改的是迴圈的方式，上面的作法是「外圍跑完 N 輪，每一輪都會有決定要幹嘛」，\n這邊的作法是「外圍只是限制，而每一次的 pointer 都直接迴圈到下一個要更改的點」。\n概念大同小異，可以依個人決定哪個程式碼比較漂亮而決定採用哪一個。\nwhile(left \u0026lt;= right): while(left \u0026lt;= right and nums[left] \u0026lt; pivot): left += 1 while(left \u0026lt;= right and nums[right] \u0026gt; pivot): right -= 1 if(left \u0026lt;= right): nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1 Reference 无序数组K小元素 · Kth Smallest Numbers in Unsorted Array ","date":"2022-04-13T14:03:08+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-461/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [461] Kth Smallest Numbers in Unsorted Array 個人解法筆記 #重要題型 #常錯題型"},{"categories":["630 - 演算法 Algorithm"],"content":"前言 Priority Queue (優先權佇列) 是一種特殊的 Queue，\n在特殊題型上會非常實用。\n個人理解後，我自己是不太會把 Priority Queue 當 Queue 看，應用上的內容已經差滿多的了。\n只要牽扯到「優先權」的題目，都非常適合用 Priority Queue 來快速解。\n例如：排程類題目，重要性(優先序)高的必須要先做，再來做比較不急的事情。\nPriority Queue (優先權佇列) 的精神 Priority Queue 當初被設計的時候，就是為了解決重要性問題而設計的 Queue，\n他擁有類似 Queue 能「放入任務」、「取出任務」的概念，\n不過在「取出任務」的這個部分就不像我們一般認知的那種單純的 Queue。\nPriority Queue 依照「重要性」，決定取出的順序，\n因此在傳入 Priority Queue 時，我們常會需要「多帶一個能代表重要性的 Key」\n範例程式碼 我覺得有時候理解直接閱讀程式碼會比較快啦 XDD\n註：在預設的定義中，「數字越小表示越重要」，會先被 get(pop) 出來\n無 key 版本，單純以傳入數字排序 from queue import PriorityQueue q = PriorityQueue() q.put(4) q.put(2) q.put(5) q.put(1) q.put(3) while not q.empty(): next_item = q.get() print(next_item) 結果 1 2 3 4 5 有 key 版本，依照傳入 key 排序 from queue import PriorityQueue q = PriorityQueue() q.put((4, \u0026#39;Read\u0026#39;)) q.put((2, \u0026#39;Play\u0026#39;)) q.put((5, \u0026#39;Write\u0026#39;)) q.put((1, \u0026#39;Code\u0026#39;)) q.put((3, \u0026#39;Study\u0026#39;)) while not q.empty(): next_item = q.get() print(next_item) 結果 (1, \u0026#39;Code\u0026#39;) (2, \u0026#39;Play\u0026#39;) (3, \u0026#39;Study\u0026#39;) (4, \u0026#39;Read\u0026#39;) (5, \u0026#39;Write\u0026#39;) Priority Queue 的底層設計 Priority Queue 是由 Heap 實做的，因此「 與 Queue 在放入與取出資料時的時間複雜度並不相同 」，\n也是因為此原因，通常我不太把 Priority Queue 當 Queue 看，\n我倒覺得他更像 Heap。\n而因為是 Heap 的原因，\n插入與取出的時間複雜度皆為 O(logN)，與 Queue 的 O(1) 也非常不同。\nPriority Queue 與 Stack, Queue 比較 這邊我把我腦袋中所想的畫出來\n而從上圖我們也可以推論\ninsert delete Stack O(1) O(1) Queue O(1) O(1) Priority Queue O(logN) O(logN) 所以我才說，理解後真的不會把 Priority Queue 當 Queue 看吧XDD\nReference What is the Python priority queue? TimeComplexity Python 佇列Queue和PriorityQueue ","date":"2022-04-13T13:10:13+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0314.jpg","permalink":"https://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/priority-queue/","tags":["Algorithm","演算法"],"title":"【演算法筆記 #4】Priority Queue (優先權佇列) 重點整理筆記，包含與常見的 Stack, Queue 的比較"},{"categories":["630 - 演算法 Algorithm"],"content":"前言 QuickSort 算是面試中或是演算法中經典的問題，非常重要，\n而且最容易搞錯「他的範圍」，因此這邊來將自己的常見錯誤做個整理。\n先一個正式版本 基本上，要講出 QuickSort 的核心精神，人人幾乎都不太會犯錯，\n但程式非常容易寫錯，特別是在區間的控制過程。\nQuickSort 的核心精神 QuickSort 的核心精神就是選定一個 pivot (可以頭可以尾)，\n將剩下的數字透過 pivot 分成 「 \u0026lt; pivot 」 與 「 \u0026gt; pivot 」 兩組。\n最容易出錯的地方 - 決定邊界 在撰寫程式的時候，如果沒有仔細思考，或甚至已經仔細思考了都還經常會寫錯。\n注意：這邊示範的並「不是」主流的教科書寫法，而是用釐清觀念候「用自己的方式」寫的。\n我們的目的如上圖，就是希望在循環結束時，能夠\n讓 left 左邊的一切都是 smaller (than pivot) 讓 right 右邊的一切都是 bigger (than pivot) 因此 (以下為說明用的程式碼，並不完整)\nwhile(left \u0026lt;= right): while(left \u0026lt;= right and a[left] \u0026lt; pivot): left += 1 while(left \u0026lt;= right and a[right] \u0026gt; pivot): right -= 1 if left \u0026lt;= right: a[left], a[right] = a[right], a[left] 注意啦! 光上面這裡有很多細節了! left \u0026lt;= right 首先是 left \u0026lt;= right，如果寫 left \u0026lt; right 達不到交錯的效果，有可能出迴圈要多做處理\n不是不能這樣寫，就是要多做一些處理，沒有什麼不能的寫法\na[left] \u0026lt; pivot, a[right] \u0026gt; pivot 注意這裡不處理等於，原因是處理等於也不一定好，\n等於表示與 pivot 相等，用於處理重複的 array，\n但如果硬要處理等於的情況，在 [1,1,1,1,1] 類似這樣的 case，\n一定會用 worst case 的 O(n) 時間去解，而不是平均的 O(nlogN)。\n可以先理解 QuickSort 的 worst case 是什麼情況及為什麼會發生，\n這樣更能夠懂為什麼會舉到上面的例子。\n此種類的完整的 QuickSort 程式碼 def partition(self, nums, start, end): # recusion end if start \u0026gt;= end: return # recursion define left, right = start+1, end pivot = nums[start] while(left \u0026lt;= right): while(left \u0026lt;= right and nums[left] \u0026lt; pivot): left += 1 while(left \u0026lt;= right and nums[right] \u0026gt; pivot): right -= 1 if left \u0026lt;= right: nums[left], nums[right] = nums[right], nums[left] else: nums[start], nums[right] = nums[right], nums[start] print(nums, left, right) # recursion split self.partition(nums, start, right-1) self.partition(nums, left, end) 抓 partition 與 pivot 的重點 我們設計的思想是\n一開始： pivot(start) \u003c (left, start+1) \u003c (right, end) - 結束時，交換前 (注意交錯)： (start) \u0026lt; start+1 \u0026lt; right \u0026lt; left \u0026lt; end\n務必注意交錯的位置的「 right \u0026lt; left 」，這是最最重要的部分。\n結束時，交換後 (注意交錯)： start \u0026lt; right-1 \u0026lt; pivot(right) \u0026lt; left \u0026lt; end\n而 start ~ right-1 都比 pivot 小\nleft \u0026lt; end 都比 pivot 大。\n結束迴圈 recursion 「start \u0026gt;= end」 不論是\nself.partition(nums, start, right-1) self.partition(nums, left, end) right 會越來越逼近 start，直到重疊 start \u0026gt;= end\n而 left 也會越來越逼近 end (因為 left 與 right 是交錯，只會更逼近 end)\n另外一種寫法 建議先嘗試閱讀以下程式碼，思考一下\n(這我寫過的東西，我也想了很久為什麼)\nwhile(left \u0026lt;= right) if a[left] \u0026lt; pivot: left += 1 elif a[right] \u0026gt; pivot: right -= 1 else: a[left], a[right] = a[right], a[left] # left += 1 # right -= 1 建議想好後再看，這樣才會更印象深刻。\n注意 left \u0026lt;= right 一樣要注意的 left \u0026lt;= right，因為一定要交錯。\n「left += 1」、「right -= 1」可有可無 其中「left += 1」、「right -= 1」可有可無，\n沒有當下處理也會在之後的迴圈被處理掉。\n此種類的完整的 QuickSort 程式碼 def partition(self, nums, start, end): # recusion end if start \u0026gt;= end: return # recursion define left, right = start+1, end pivot = nums[start] while(left \u0026lt;= right): if nums[left] \u0026lt; pivot: left += 1 elif nums[right] \u0026gt; pivot: right -= 1 else: nums[left], nums[right] = nums[right], nums[left] else: nums[start], nums[right] = nums[right], nums[start] # recursion split self.partition(nums, start, right-1) self.partition(nums, left, end) 抓 partition 與 pivot 的重點 我們設計的思想是\n一開始： pivot(start) \u003c (left, start+1) \u003c (right, end) - 結束時，交換前 (注意交錯)： (start) \u0026lt; start+1 \u0026lt; right \u0026lt; left \u0026lt; end\n務必注意交錯的位置的「 right \u0026lt; left 」，這是最最重要的部分。\n結束時，交換後 (注意交錯)： start \u0026lt; right-1 \u0026lt; pivot(right) \u0026lt; left \u0026lt; end\n而 start ~ right-1 都比 pivot 小\nleft \u0026lt; end 都比 pivot 大。\n結束迴圈 recursion 「start \u0026gt;= end」 不論是\nself.partition(nums, start, right-1) self.partition(nums, left, end) right 會越來越逼近 start，直到重疊 start \u0026gt;= end\n而 left 也會越來越逼近 end (因為 left 與 right 是交錯，只會更逼近 end)\nReference Python 快速排序 无序数组K小元素 · Kth Smallest Numbers in Unsorted Array ","date":"2022-04-10T03:29:01+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0313.jpg","permalink":"https://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/quicksort/","tags":["Algorithm","演算法"],"title":"【演算法筆記 #3】QuickSort 重點整理筆記，包含 Partition, Pivot 常見錯誤討論 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 31 · Partition Array\n難度 medium\n個人範例程式碼 - 1 (相等時結束的討論) from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: The integer array you should partition @param k: An integer @return: The index after partition \u0026#34;\u0026#34;\u0026#34; def partition_array(self, nums: List[int], k: int) -\u0026gt; int: # write your code here if not nums: return 0 start, end = 0, len(nums)-1 while(start \u0026lt; end): if nums[start] \u0026lt; k: start += 1 elif nums[end] \u0026gt;= k: end -= 1 else: nums[start], nums[end] = nums[end], nums[start] start += 1 end -= 1 else: # the first index i nums[i] \u0026gt;= k if nums[start] \u0026gt;= k: return start elif nums[end] \u0026gt;= k: return end else: return end+1 # special case, all \u0026lt; k 算法說明 基本的 partition 作法，#重要題型\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 的情況，輸出 0\nBoundary conditions 注意結束條件，start \u0026lt; end ，表示當 「start end」 會結束。\n特殊情況：全部 \u0026lt; k 這時候依照題目敘述，我們應該要回傳 end + 1，\n但 end + 1，不會在我們的搜尋範圍，因此「我們需要特別做討論」。\n個人範例程式碼 - 2 (start 超過 end 時才結束的討論) from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: The integer array you should partition @param k: An integer @return: The index after partition \u0026#34;\u0026#34;\u0026#34; def partition_array(self, nums: List[int], k: int) -\u0026gt; int: # write your code here if not nums: return 0 start, end = 0, len(nums)-1 while(start \u0026lt;= end): if nums[start] \u0026lt; k: start += 1 elif nums[end] \u0026gt;= k: end -= 1 else: nums[start], nums[end] = nums[end], nums[start] start += 1 end -= 1 else: return start 算法說明 基本的 partition 作法，#重要題型\n這裡展示另外一種作法，是為了處理 end + 1 搜尋\n透過改變條件「start \u0026lt;= end」，因此只有當 start 與 end 交錯時才會終止。\n當全部 \u0026lt; k 時，start 會走到 end + 1 並結束。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 的情況，輸出 0\nBoundary conditions 注意結束條件，start \u0026lt;= end ，表示當 start 與 end 相交會結束。\n特殊情況：全部 \u0026lt; k 在這個 case 當中，我們改變了條件「start \u0026lt;= end」，\n因此如果全部都 \u0026lt; k 的情況 start 會走到 end + 1，得到最終結果。\nReference 数组划分 · Partition Array ","date":"2022-04-09T20:54:57+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-31/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [31] Partition Array 個人解法筆記 #重要題型"},{"categories":["710 - Python LeetCode"],"content":"題目出處 382 · Triangle Count\n難度 medium\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param s: A list of integers @return: An integer \u0026#34;\u0026#34;\u0026#34; def triangle_count(self, s: List[int]) -\u0026gt; int: # write your code here if not s: return 0 ans = 0 s.sort() for idx, max_edge in enumerate(s): # ans = self.count_triange_fix_max_edge(max_edge, ans, s, start=idx+1, end=len(s)-1) ans = self.count_triange_fix_max_edge(max_edge, ans, s, start=0, end=idx-1) # notice this !!!!! return ans def count_triange_fix_max_edge(self, max_edge, ans, s, start, end): while(start \u0026lt; end): if s[start] + s[end] \u0026gt; max_edge: ans += (end -start) end -= 1 # too big else: # if s[start] + s[end] \u0026lt;= max_edge: start += 1 # too small else: return ans 算法說明 3Sum 的延伸題，3Sum 可以視為 a + b = c 的問題，\n此題為 a + b \u0026gt; c 的題型 (建議先解完 3Sum 再過來解此題。)\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-16/\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 的情況，輸出 0 。(題目沒說，但這邊先預留特殊狀況處理)\nBoundary conditions 解這題要特別小心，很多細節要處理\n1. 注意「最大邊」設定為哪一邊 我的策略是一開始就要決定好哪邊是最大邊，\n2. 設定好「最大邊」後，討論結果的範圍 特別注意我們決定好「最大值」後，搜尋的範圍在 0 ~ 「idx-1」，\n因此\n# ans = self.count_triange_fix_max_edge(max_edge, ans, s, start=idx+1, end=len(s)-1) # wrong !!! ans = self.count_triange_fix_max_edge(max_edge, ans, s, start=0, end=idx-1) # notice this !!!!! 3. 移動範圍 因為題目只要求「計數」，因此這邊我們相對不用處理到太細節，\n而要使結果滿足 a + b \u0026gt; c 的情況有兩種可能，\n我們先固定 c 後： 「a 增加」或「b 減少」 這裡有個加速的做法，因為我們知道當 a + b \u0026gt; c 的 b 已經滿足時，剩下的「b -\u0026gt; c」範圍等式必定成立，\n因此我們可以直接「ans += (c-b)」，再來移動「結束位置」 (end -= 1)\n而一般情況是，start += 1，來去滿足「還不夠大」的部分。\nReference 三角形计数 · Triangle Count ","date":"2022-04-09T20:14:41+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0311.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-382/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [382] Triangle Count 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 16. 3Sum Closest\n難度 medium\n個人範例程式碼 class Solution: def threeSumClosest(self, nums: List[int], target: int) -\u0026gt; int: if not nums or len(nums) \u0026lt; 3: return -1 nums.sort() ans = float(\u0026#34;inf\u0026#34;) for idx, first_num in enumerate(nums): ans = self.two_sum(nums, target, ans, first_num, idx+1, len(nums)-1) return ans def two_sum(self, nums, target, ans, first_num, left, right): while(left \u0026lt; right): if first_num + nums[left] + nums[right] == target: return target elif first_num + nums[left] + nums[right] \u0026lt; target: ans = self.find_closest(ans, first_num + nums[left] + nums[right], target) left += 1 else: # if first_num + nums[left] + nums[right] \u0026gt; target: ans = self.find_closest(ans, first_num + nums[left] + nums[right], target) right -= 1 else: return ans def find_closest(self, a, b, target): if abs(a - target) \u0026lt;= abs(b - target): return a else: return b 算法說明 這題有幾乎相同的類似題，可參考：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-533/\n前者是 2Sum，這題更難改成 3Sum，但思路與 3Sum 大同小異\nhttps://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-15/\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 或 len \u0026lt; 3 的情況，輸出 -1 。(題目沒說，但這邊先預留特殊狀況處理)\nBoundary conditions 特別留意 3Sum 的搜尋條件，特別是 a \u0026lt;= b \u0026lt;= c 的處理部分 (可以看 3Sum 的文章)\nReference 最接近的三数之和 · 3Sum Closest 【Leetcode】python – [15] 3Sum 個人解法筆記 (last update: 2022/4/6) 【Leetcode】python – [Google | Onsite] Two Sum – Closest to Target 個人解法筆記 (Lintcode – 533) ","date":"2022-04-08T17:35:24+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-16/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [16] 3Sum Closest 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 107. Binary Tree Level Order Traversal II\n難度 medium\n題目分類 Tree, Breadth-First Search, Binary Tree\n個人範例程式碼 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def levelOrderBottom(self, root: Optional[TreeNode]) -\u0026gt; List[List[int]]: if root is None: return [] ans = [] queue = [root] while(queue): tmp_ans = [] tmp_queue = [] for node in queue: tmp_ans.append(node.val) if node.left is not None: tmp_queue.append(node.left) if node.right is not None: tmp_queue.append(node.right) ans.append(tmp_ans) queue = tmp_queue else: return ans[::-1] 算法說明 這題有幾乎相同的類似題，可參考：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-102/\nBFS traversal，搭配 Queue 即可完成。\n這題沒有發揮 Queue 的 FIFO 特性，但要使用也是可以的。\n無聊記：Queue = pop(0)，Q 長的很像 0\u0026hellip; 比較 Stack = pop(-1) 或 pop () 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 None 的情況，輸出 []。\nBoundary conditions 注意：BFS 結束條件為，「當 Queue 不再有東西的時候」。\nReference 二叉树的层次遍历 II · Binary Tree Level Order Traversal II ","date":"2022-04-07T21:03:10+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-107/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [107] Binary Tree Level Order Traversal II 個人解法筆記"},{"categories":["716 - Linked List","Two pointers (同向雙指針 →→) / slow, fast (快慢雙指針)"],"content":"題目出處 876. Middle of the Linked List 難度 easy\n個人範例程式碼 - two pointers # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def middleNode(self, head: Optional[ListNode]) -\u0026gt; Optional[ListNode]: if head is None: return None slow = head fast = slow.next while(fast is not None and fast.next is not None): slow = slow.next fast = fast.next.next else: if(fast is None): # odd case return slow else: # fast is not None, fast.next is None: # even case return slow.next 算法說明 同向的快慢 pointers，這題目雖然簡單，但很考慮細節的處理。\n這部分可以看下圖，說明請見 Boundary conditions ： 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 None 的情況，輸出 None。\nBoundary conditions 我們依照上面的圖片繼續說明：\n基本上拆成兩種狀況討論：\nodd case：奇數情況，特性為 fast 直接指到 None 上，return slow even case：偶數情況，特性為 fast.next 才指到 None 上 (或 fast 有值卻進入了 else part)，return slow.next 結束條件 fast or fast.next is None 的時候，我們再來分析情況\n(至少要自己本身能指到 None) Reference 链表的中点 · Middle of Linked List ","date":"2022-04-07T18:55:28+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0310.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-876/","tags":[],"title":"【Leetcode】python - [876] Middle of the Linked List 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 533 · Two Sum - Closest to target Google | Onsite | Two Sum - Closest to Target 難度 medium\n個人範例程式碼 - two pointers from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: an integer array @param target: An integer @return: the difference between the sum and the target \u0026#34;\u0026#34;\u0026#34; def two_sum_closest(self, nums: List[int], target: int) -\u0026gt; int: if not nums: return -1 ans = float(\u0026#34;inf\u0026#34;) # biggest value nums.sort() start, end = 0, len(nums) - 1 while(start \u0026lt; end): print(ans) if target - (nums[start] + nums[end]) == 0: return 0 # early return elif target \u0026gt; (nums[start] + nums[end]): # sum is smaller than target ans = min(ans, target - (nums[start] + nums[end])) start += 1 else: # target \u0026lt; (nums[start] + nums[end]) # sum is bigger than target ans = min(ans, (nums[start] + nums[end]) - target) end -= 1 else: return ans 算法說明 先 sort 後以 two pointers 處理，這邊容易有個思考上的盲點，\n就是如果「減去負數」的處理不就「數值會變更大」了嗎?\n這部分可以看下圖： 我們會發現其實 target 的移動與結果影響不大。\n就算是\u0026quot;減去\u0026quot;\u0026ldquo;負的\u0026rdquo;，也只是「往離 target」更遠的方向前進。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 的情況，輸出 -1。 (題目沒說，但做為表示找不到結果)\nBoundary conditions 結束條件 相向相交時，start \u0026lt; end\nReference 两数和的最接近值 · Two Sum - Closest to target ","date":"2022-04-07T18:16:33+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0309.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-533/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [Google | Onsite] Two Sum - Closest to Target 個人解法筆記 (Lintcode - 533)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 Amazon | OA 2019 | Two Sum - Unique Pairs 587 · Two Sum - Unique pairs 難度 medium\n個人範例程式碼 - two pointers from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: an array of integer @param target: An integer @return: An integer \u0026#34;\u0026#34;\u0026#34; def two_sum6(self, nums: List[int], target: int) -\u0026gt; int: if not nums: return 0 nums.sort() ans = 0 start, end = 0, len(nums)-1 while(start \u0026lt; end): if(start \u0026gt; 0 and nums[start] == nums[start - 1]): start += 1 continue if(end \u0026lt; len(nums)-1 and nums[end] == nums[end + 1]): end -= 1 continue if nums[start] + nums[end] == target: ans += 1 start += 1 end -= 1 elif nums[start] + nums[end] \u0026lt; target: start += 1 else: # nums[start] + nums[end] \u0026gt; target: end -= 1 else: return ans 算法說明 簡單的組合，先 sort 後以 two pointers 處理，\n而注意重複，這邊以第一次就去做為準下去寫。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 的情況，輸出 0。\nBoundary conditions 結束條件 相向相交時，start \u0026lt; end\n處理重複 這邊以第一次就去做為準下去寫。\n因此有\nif(start \u0026gt; 0 and nums[start] nums[start - 1]): start += 1 continue if(end \u0026lt; len(nums)-1 and nums[end] nums[end + 1]): end -= 1 continue 特別注意「start \u0026gt; 0」、「end \u0026lt; len(nums)-1」這兩個部分。\nReference 两数之和 - 不同组成 · Two Sum - Unique pairs ","date":"2022-04-07T17:31:08+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-587/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [Amazon | OA 2019] Two Sum - Unique Pairs 個人解法筆記 (Lintcode - 587)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 102. Binary Tree Level Order Traversal\n難度 medium\n題目分類 Tree, Breadth-First Search, Binary Tree\n個人範例程式碼 - 第二版：2022/4/7 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def levelOrder(self, root: Optional[TreeNode]) -\u0026gt; List[List[int]]: if root is None: return [] queue = [root] ans = [] while(queue): tmp_queue = [] tmp_ans = [] for ele in queue: tmp_ans.append(ele.val) if ele.left is not None: tmp_queue.append(ele.left) if ele.right is not None: tmp_queue.append(ele.right) ans.append(tmp_ans) queue = tmp_queue else: return ans 算法說明 BFS traversal，搭配 Queue 即可完成。\n這題沒有發揮 Queue 的 FIFO 特性，但要使用也是可以的。\n無聊記：Queue = pop(0)，Q 長的很像 0\u0026hellip; 比較 Stack = pop(-1) 或 pop () 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 None 的情況，輸出 []。\nBoundary conditions 注意：BFS 結束條件為，「當 Queue 不再有東西的時候」。\n個人範例程式碼 - 第一版：2022/3/19 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def levelOrder(self, root: Optional[TreeNode]) -\u0026gt; List[List[int]]: ans = [] this_level = [root] if root: while(this_level): this_level_val = [] next_level = [] for node in this_level: this_level_val.append(node.val) if node.left: next_level.append(node.left) if node.right: next_level.append(node.right) ans.append(this_level_val) this_level = next_level return ans 算法說明 既然是 level order，我們的思路是使用一層一層的方式下去把答案兜出來。\ncorner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 原本的做法我是到最後一層才做檢查「if root」，\n不過如果是採取這樣的作法，會多一層只有「None 的 layer」，\n我們需要特別留意儲存的最後一層。\nReference 二叉树的层次遍历 · Binary Tree Level Order Traversal ?? Python | BFS Queue - 公瑾™ (102) ","date":"2022-04-07T12:43:11+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-102/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [102] Binary Tree Level Order Traversal 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 680. Valid Palindrome II\n難度 easy\n個人範例程式碼 - two pointers class Solution: def validPalindrome(self, s: str) -\u0026gt; bool: start, end = 0, len(s) - 1 while(start \u0026lt; end): if(s[start] == s[end]): # same start += 1 end -= 1 else: # not same, try cut one return self.is_palindrome(s, start+1, end) or self.is_palindrome(s, start, end-1) return True def is_palindrome(self, s, start, end): while(start \u0026lt; end): if (s[start] == s[end]): start += 1 end -= 1 else: return False return True 算法說明 這題可以用比較聰明的作法，\n我們既然都知道我們會需要「多次判斷」是否是「回文」，\n我們可以先寫好判斷回文的 function 「is_palindrome」，\n讓我們的主程式可以多次的去 call。\n而刪除的概念可以理解為，如果我們找到兩端各一個「非相同的文字」，\n我們就嘗試「左邊」與「右邊」都去掉嘗試判斷看看，\n因為只要判斷一次刪除，因此如果回傳都沒有解，即為最終無解的情況。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有 input 的狀況，內建 start \u0026lt; end 條件可以處理\nBoundary conditions start \u0026lt; end，交錯或等於就結束\nReference 有效回文 II · Valid Palindrome II ","date":"2022-04-06T20:17:11+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-680/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [680] Valid Palindrome II 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 167. Two Sum II - Input Array Is Sorted\n難度 medium\n個人範例程式碼 - two pointers class Solution: def twoSum(self, numbers: List[int], target: int) -\u0026gt; List[int]: if not numbers: return [-1, -1] start, end = 0, len(numbers)-1 while(start \u0026lt; end): if(numbers[start] + numbers[end] == target): return [start+1, end+1] elif(numbers[start] + numbers[end] \u0026lt; target): # smaller start+= 1 else: # (numbers[start] + numbers[end] \u0026gt; target): end -= 1 else: return [-1, -1] 算法說明 這題我們在之前的題目已經有詳細說明了，可參考：\n【Leetcode】python - [1] Two Sum 個人解法筆記 (last update: 2022/04/06) | 內有 list comprehesion / dict comprehesion 整理 另外，這題因為有「已經排序」的特性，目的就是為了解決表格中的 sort，\n加速 two pointers 做法的時間複雜度。\nhashmap two pointers 空間複雜度 O(n) O(1) 時間複雜度 O(1) O(n), 但有前置步驟 sort = O(nlogN) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有 input 的狀況，return [-1, -1]\nBoundary conditions start \u0026lt; end，交錯或等於就結束\nReference 两数和 II-输入已排序的数组 · Two Sum II - Input array is sorted ","date":"2022-04-06T19:34:18+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-167/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [167] Two Sum II - Input Array Is Sorted 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 283. Move Zeroes\n難度 easy\n個人範例程式碼 - two pointers class Solution: def moveZeroes(self, nums: List[int]) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums in-place instead. \u0026#34;\u0026#34;\u0026#34; start, end = 0, 0 while(end \u0026lt; len(nums)): if(nums[end] != 0): # != 0 nums[start], nums[end] = nums[end], nums[start] # change start +=1 end += 1 return nums 算法說明 同向 pointers，因為題目有說要保留「the relative order」(相對順序)\nstart 保留最後指向 0 位置 (在指到第一個 0 之前，都是指數字與 end 一起前進，讓自己互換)\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有 input 的狀況，return []\nBoundary conditions 當 end 先行達到 len(nums)，就結束迴圈\nReference 移动零 · Move Zeroes ","date":"2022-04-06T18:36:09+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0308.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-283/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [283] Move Zeroes 個人解法筆記"},{"categories":["717 - Hash Table","Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 https://leetcode.com/problems/two-sum/\n難度 Easy\n個人範例程式碼 - 三刷 (2022/04/06) class Solution: def twoSum(self, nums: List[int], target: int) -\u0026gt; List[int]: list_number_and_index = [(ele, idx) for idx, ele in enumerate(nums)] list_number_and_index.sort() start, end = 0, len(nums)-1 while(start \u0026lt; end): if list_number_and_index[start][0] + list_number_and_index[end][0] == target: return sorted([list_number_and_index[start][1], list_number_and_index[end][1]]) elif list_number_and_index[start][0] + list_number_and_index[end][0] \u0026lt; target: # need bigger start += 1 else: # nums[start] + nums[end] \u0026gt; target: # need smaller end -= 1 else: return [-1, -1] 算法說明 - two pointers 這次使用 two pointers 的作法，這個做法的優點是可以比較下表，\n基本上就是能節省掉 hashmap 的空間，取而代之的是比較多的時間\n空間可以換時間，時間可以換空間。 這個觀念非常的重要!!!\nhashmap two pointers 空間複雜度 O(n) O(1) 時間複雜度 O(1) O(n), 但有前置步驟 sort = O(nlogN) 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有結果的狀況 [-1, -1]\nBoundary conditions two pointers 循環條件 while(start \u0026lt; end)\n補充：list comprehesion list_number_and_index = [(ele, idx) for idx, ele in enumerate(numbers)] 補充：dict comprehesion dict_number_to_index = {ele: idx for idx, ele in enumerate(numbers)} 個人範例程式碼 - 二刷 (2022/02/24) class Solution(object): def twoSum(self, nums, target): \u0026#34;\u0026#34;\u0026#34; :type nums: List[int] :type target: int :rtype: List[int] \u0026#34;\u0026#34;\u0026#34; mydict = {} for idx in range(len(nums)): # print(mydict.keys()) if nums[idx] in mydict.keys(): return [mydict[nums[idx]], idx] else: mydict[target - nums[idx]] = idx 個人範例程式碼 - 一刷 (2021/03/16) class Solution: def twoSum(self, nums, target): # 2 7 11 15 my_dict = {} for idx, ele in enumerate(nums): rest = target - ele if rest in my_dict: return [my_dict[rest], idx] else: my_dict[ele] = idx # record 解法 - HashTable 這題主要考的是 HashTable，\n需要先建立一張表 (此處建立 dict)，\n利用 「key -\u0026gt; value」 的特性保存 「剩餘值 對應 index」\n因此當後續的計算，發現剩餘值存在 dict 的 key 中，\n就能快速反查表得到 index。\n示意圖 (將找不到 remain value 的值，作為 key 存進 dict 中)\nReference 两数之和 · Two Sum dict comprehesion Create a dictionary with list comprehension ","date":"2022-04-06T12:47:52+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7753-1024x768.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/hash-table/leetcode-python-1/","tags":["Python","Python 基礎用法"],"title":"【Leetcode】python - [1] Two Sum 個人解法筆記 | 內有 list comprehesion / dict comprehesion 整理"},{"categories":["710 - Python LeetCode"],"content":"題目出處 140 · Fast Power\n難度 medium\n個人範例程式碼 class Solution: \u0026#34;\u0026#34;\u0026#34; @param a: A 32bit integer @param b: A 32bit integer @param n: A 32bit integer @return: An integer \u0026#34;\u0026#34;\u0026#34; def fast_power(self, a: int, b: int, n: int) -\u0026gt; int: ans, tmp = 1, a while(n \u0026gt; 0): if(n % 2 == 1): ans = (ans * tmp) % b tmp = (tmp * tmp) % b n = n // 2 else: return ans % b 算法說明 有非常類似的問題，詳細說明可參考：【Leetcode】python – [50] Pow(x, n) 個人解法筆記 核心概念為，將 n 次方拆成二進位的方式計算 (這樣就不用重複算)\n例如 9 = 8 + 0 + 0 + 1，\n我們只重複計算 1^2^2^2^2\u0026hellip;. 可以減少一半的計算次數，複雜度為 O(n) -\u0026gt; O(logN)\n另外處理負數要特別注意。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理輸入 n = 0 ，return 1,\n與輸入 n \u0026lt; 0 ，作負數處理。\nBoundary conditions 循環直到當 n 0 時 ( 1//2 = 0 )\nReference 快速幂 · Fast Power ","date":"2022-04-05T20:48:27+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/lintcode-python-140/","tags":["LeetCode","Python"],"title":"【Lintcode】python - [140] Fast Power 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 50. Pow(x, n)\n難度 medium\n個人範例程式碼 class Solution: def myPow(self, x: float, n: int) -\u0026gt; float: # write your code here # 2 = 1 + 0 # 7 = 1 + 1 + 1 # 5 = 1 + 1 + 0 # 13 = 1 + 1 + 0 + 1 ans = 1 minus_flag = False if n == 0: return 1 if n \u0026lt; 0: minus_flag = True n = -n while(n \u0026gt; 0): if n % 2 == 0: pass # print(0) else: # print(1) ans *= x x = x * x n = n \u0026gt;\u0026gt; 1 # // 2 else: if minus_flag: return 1/ans else: return ans 算法說明 核心概念為，將 n 次方拆成二進位的方式計算 (這樣就不用重複算)\n例如 9 = 8 + 0 + 0 + 1，\n我們只重複計算 1^2^2^2^2\u0026hellip;. 可以減少一半的計算次數，複雜度為 O(n) -\u0026gt; O(logN)\n另外處理負數要特別注意。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理輸入 n = 0 ，return 1,\n與輸入 n \u0026lt; 0 ，作負數處理。\nBoundary conditions 循環直到當 n 0 時 ( 1//2 = 0 )\nReference x的n次幂 · Pow(x, n) ","date":"2022-04-05T20:43:38+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-50/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [50] Pow(x, n) 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 162. Find Peak Element\n難度 medium\n個人範例程式碼 class Solution: def findPeakElement(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 start, end = 0, len(nums) - 1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(nums[mid] \u0026gt;= nums[mid+1]): end = mid else: start = mid else: if(nums[start] \u0026gt;= nums[end]): return start else: return end 算法說明 幾乎類似題： [852. Peak Index in a Mountain Array](https://leetcode.com/problems/peak-index-in-a-mountain-array/) 可以參考那邊的做法，這邊簡單寫\nbinary search，仔細討論會發現 mid 總共有 4 種狀況\n- [mid-1] \u003c [mid] \u003c [mid+1] ，正在往上走，把 start 移動往 mid (加速上坡) - [mid-1] \u003e [mid] \u003e [mid+1] ，正在往下走，把 end 移動往 mid (減少下坡) - [mid-1] \u003c [mid] \u003e [mid+1] ，paek 就是我們要的山峰 - [mid-1] \u003e [mid] \u003c [mid+1] ，valley 山谷，往兩側走都可 也可以再簡化，其實我們只要判斷一邊就好，\n最後會縮小到一個範圍當中，其中一邊是高的，就是我們的局部最佳解。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有輸入的時候，return 0 (題目有保證不會有輸入問題)\nBoundary conditions binary search 的結束條件\nReference 寻找峰值 · Find Peak Element ","date":"2022-04-05T18:31:43+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-162/","tags":[],"title":"【Leetcode】python - [162] Find Peak Element 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 852. Peak Index in a Mountain Array\n難度 easy\n個人範例程式碼 class Solution: def peakIndexInMountainArray(self, arr: List[int]) -\u0026gt; int: if not arr: return 0 start, end = 0, len(arr)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(arr[mid] \u0026lt; arr[mid+1]): # next go up, change start near to peak start = mid else: # next go down end = mid else: if arr[start] \u0026gt;= arr[end]: return start else: return end 算法說明 binary search，仔細討論會發現 mid 總共有 4 種狀況\n- [mid-1] \u003c [mid] \u003c [mid+1] ，正在往上走，把 start 移動往 mid (加速上坡) - [mid-1] \u003e [mid] \u003e [mid+1] ，正在往下走，把 end 移動往 mid (減少下坡) - [mid-1] \u003c [mid] \u003e [mid+1] ，paek 就是我們要的山峰 - [mid-1] \u003e [mid] \u003c [mid+1] ，valley 山谷，往兩側走都可 也可以再簡化，其實我們只要判斷一邊就好，\n最後會縮小到一個範圍當中，其中一邊是高的，就是我們的局部最佳解。\n移動方式示意圖 觀察 start, end 的移動方法\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有輸入的時候，return 0\nBoundary conditions binary search 的結束條件\nReference 山脉序列中的最大值 · Maximum Number in Mountain Sequence ","date":"2022-04-05T18:21:52+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0306.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-852/","tags":[],"title":"【Leetcode】python - [852] Peak Index in a Mountain Array 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 153. Find Minimum in Rotated Sorted Array\n難度 medium\n個人範例程式碼 class Solution: def findMin(self, nums: List[int]) -\u0026gt; int: if not nums: return 0 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if nums[mid] == nums[end]: # if special case exist start = mid elif nums[mid] \u0026gt; nums[end]: # still going up start = mid else: # [mid] \u0026lt; [end] # going down, move start yo get closer end = mid else: if(nums[start] \u0026lt;= nums[end]): return nums[start] else: return nums[end] 算法說明 binary search\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理沒有輸入的時候，return 0\nBoundary conditions 正常的 start, end 要注意，此外\n注意點 - 要比較的大小對象? First? Last? 請思考以下問題：\n我們的 nums[mid] 應該要比較誰?\n(A) \u0026gt; nums[first] (B) \u0026gt;= nums[first] (C) \u0026gt; nums[end] (D) \u0026gt;= nums[end] 思考一下，答案為 (D) \u0026gt;= nums[end]\n(B), (C) 基本上代表是同一個意思，而只比較 first 會出問題的地方在於 [2, 3, 1] 與 [1, 2, 3]\n正常的 [2, 3, 1]，我們可以透過 2 \u0026gt; 1 處理掉，start 往中間移動，\n但 [1, 2, 3]，我們會發現我們移動了 1 -\u0026gt; 2，start 被移動掉了，因此找不到正確答案。\n而 (A) \u0026gt; nums[first]，就更不用提了。\nReference 寻找旋转排序数组中的最小值 · Find Minimum in Rotated Sorted Array ","date":"2022-04-05T17:35:43+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-153/","tags":[],"title":"【Leetcode】python - [153] Find Minimum in Rotated Sorted Array 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 447 · Search in a Big Sorted Array\n難度 medium\n個人範例程式碼 - Binary Search class Solution: \u0026#34;\u0026#34;\u0026#34; @param reader: An instance of ArrayReader. @param target: An integer @return: An integer which is the first index of target. \u0026#34;\u0026#34;\u0026#34; def searchBigSortedArray(self, reader, target): start, end = 0, 1 while(reader.get(end) \u0026lt;= target): end *= 2 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(reader.get(mid) == target): end = mid elif(reader.get(mid) \u0026lt; target): start = mid else: # (reader.get(mid) \u0026gt; target): end = mid else: if(reader.get(start) == target): return start elif(reader.get(end) == target): return end else: return -1 算法說明 - Binary Search 先不斷的 *=2 放大 end，直到找到一個比較可靠的右邊界為止\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 應該要做但我沒做XD，不過題目有保證輸入正確就是\nBoundary conditions binary search 的邊界條件 這裡有兩個 Boundary conditions 要注意，一個是一開始的 binary search 的邊界條件\n個人範例程式碼 - Exponential backoff (倍增) class Solution: \u0026#34;\u0026#34;\u0026#34; @param reader: An instance of ArrayReader. @param target: An integer @return: An integer which is the first index of target. \u0026#34;\u0026#34;\u0026#34; def searchBigSortedArray(self, reader, target): # write your code here first_element = reader.get(0) if first_element == target: return 0 if first_element \u0026gt; target: return -1 idx, jump = 0, 1 while(jump): while(jump and reader.get(idx + jump) \u0026gt;= target): # bigger or same jump = jump // 2 # \u0026gt;\u0026gt; 1 # until 0 print(jump) else: # when smaller idx += jump jump = jump * 2 # \u0026lt;\u0026lt; 1 else: if reader.get(idx+1) == target: return idx+1 else: # reader.get(start) \u0026gt; target: return -1 return -1 算法說明 - Exponential backoff (倍增) 倍增法的概念，先不斷放大，當超過時再縮小範圍搜尋。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\n畫個示意圖吧! input handling 應該要做但我沒做XD，不過題目有保證輸入正確就是\nBoundary conditions 當 \u0026gt;= target 時，縮小 jump 當 \u0026lt; target 時，利用 idx + jump，並 jump 不斷的增加 Reference python 「\u003e\u003e， \u003c\u003c」 用法，BitwiseOperators BitwiseOperators - [在大数组中查找 · Search in a Big Sorted Array](https://www.jiuzhang.com/solution/search-in-a-big-sorted-array/) ","date":"2022-04-05T16:29:26+08:00","image":"https://wongwongnotes.com/images/restored/2022/04/img_0305.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/lintcode-python-447/","tags":[],"title":"【Lintcode】python - [447] Search in a Big Sorted Array 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 658. Find K Closest Elements\n難度 medium\n個人範例程式碼 class Solution: def findClosestElements(self, arr: List[int], k: int, x: int) -\u0026gt; List[int]: start, end = 0, len(arr) - 1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if arr[mid] == x: start = mid elif arr[mid] \u0026lt; x: start = mid else: # a[mid] \u0026gt; x end = mid else: return self.find_k_neighbors(arr, x, k, start, end) def find_k_neighbors(self, arr, x, k, start, end): # print(start, end) ans = [] while(k \u0026gt; 0): if((start \u0026gt;= 0) and (end \u0026lt; len(arr)) and (x - arr[start] \u0026lt;= arr[end] - x)): # smaller first ans.append(arr[start]) start -= 1 elif((start \u0026gt;= 0) and (end \u0026lt; len(arr)) and (x - arr[start] \u0026gt; arr[end] - x)): ans.append(arr[end]) end += 1 elif(start \u0026lt; 0): ans.append(arr[end]) end += 1 else: # end \u0026gt;= len(a) ans.append(arr[start]) start -= 1 k -= 1 # print(ans) return sorted(ans) 算法說明 方法是一樣 binary search 找到中間後 O(logN)，往兩邊搜尋 K 個 O(K)。 = O(logN+K)\n個人覺得這題 LintCode 版本出的比較好，Leetcode 出的版本最後多了一個排序，\n只是我覺得光是找最近的 K 的結果就已經找到的關鍵，就不用再排序了!\nLintCode 版本的題目：460 · Find K Closest Elements Leetcode 版本的差別只差在我最後多補了一個 sorted 給他。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 應該要做但我沒做XD，不過題目有保證輸入正確就是\nBoundary conditions binary search 的邊界條件 這裡有兩個 Boundary conditions 要注意，一個是一開始的 binary search 的邊界條件\nfind K 的邊界條件 另外一個要注意的是在找 K 個鄰近的數值時，我們需要注意「不能找到範圍之外」。\nReference 在排序数组中找最接近的K个数 · Find K Closest Elements ","date":"2022-04-05T02:32:16+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-658/","tags":[],"title":"【Leetcode】python - [658] Find K Closest Elements 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 278. First Bad Version\n難度 easy\n個人範例程式碼 # The isBadVersion API is already defined for you. # def isBadVersion(version: int) -\u0026gt; bool class Solution: def firstBadVersion(self, n: int) -\u0026gt; int: # isBadVersion = true # OOO(X)XXX # FFF(T)TTT \u0026lt;- notice this! if not n: return 0 start, end = 1, n while(start + 1 \u0026lt; end): mid = (start + end) // 2 if isBadVersion(mid): # [mid] = false (still good version.), move end to search after end = mid else: # if isBadVersion(mid) == True: # [mid] = true, move start to search behind start = mid else: if isBadVersion(start): return start elif isBadVersion(end): return end else: return 0 return 0 算法說明 變化版本的 binary search，採用的策略為先縮小範圍再得到答案。\n也就是說 left, right 縮小至最小範圍的 left, right，再去判斷答案。\n當然比較常見的方法是：left, right 找 mid，mid 直接找到答案會更為簡潔。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意 input nums 為 0 的時候的處理，return 個 0 給他 (題目好像沒特別提到這個，我自己加的)\nBoundary conditions start 與 end 的處理一直都是 binary search 常出錯的地方，這邊要細心一些。\n我採用網路上看到的 start + 1 \u0026lt; end 的方式，先縮小 left, right 範圍\n並且不把目標放在直接找到 mid 做為答案。\n再從最小範圍 left, right 中找到最終解答。\n其他重點 注意 isBadVersion 是「錯的」回傳「True」，\n這個雙重否定有弄到我XD，\n概念要剛好相反。\nReference 第一个错误的代码版本 · First Bad Version ","date":"2022-04-05T02:22:32+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-278/","tags":[],"title":"【Leetcode】python - [278] First Bad Version 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 34. Find First and Last Position of Element in Sorted Array\n難度 easy\n個人範例程式碼 class Solution: def searchRange(self, nums: List[int], target: int) -\u0026gt; List[int]: if not nums: return [-1, -1] ans = [] ans.append(self.find_first_position(nums, target)) ans.append(self.find_last_position(nums, target)) return ans def find_first_position(self, nums, target): if not nums: return -1 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(nums[mid] == target): # find first, move end end = mid elif(nums[mid] \u0026lt; target): start = mid else: #(nums[mid] \u0026gt; target): end = mid else: # when quit loop if(nums[start] == target): # find first return start elif(nums[end] == target): return end else: return -1 return -1 def find_last_position(self, nums, target): if not nums: return -1 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(nums[mid] == target): # find last, move start start = mid elif(nums[mid] \u0026lt; target): start = mid else: #(nums[mid] \u0026gt; target): end = mid else: # when quit loop if(nums[end] == target): # find last return end elif(nums[start] == target): return start else: return -1 return -1 算法說明 此題為以下兩題的綜合題：\n【Lintcode】python – [458] Last Position of Target 個人解法筆記 【Lintcode】python – [14] First Position of Target 個人解法筆記 因為重複的問題，算法又要求 log(N)，如果同時做左右搜尋會顯得程式碼比較難以閱讀與 debug，\n不如就拆成兩個來寫，時間 2*O(logN) = O(logN)\n拆開寫的好處我自己在撰寫時已經體驗過了，要 debug 直接找對應的哪個 function 就好!\n非常的方便!!!\n基本的 binary search，採用的策略為先縮小範圍再得到答案。\n也就是說 left, right 縮小至最小範圍的 left, right，再去判斷答案。\n當然比較常見的方法是：left, right 找 mid，mid 直接找到答案會更為簡潔。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意 input nums 為 [] 的時候的處理，return 個 -1 給他\nBoundary conditions start 與 end 的處理一直都是 binary search 常出錯的地方，這邊要細心一些。\n我採用網路上看到的 start + 1 \u0026lt; end 的方式，先縮小 left, right 範圍\n並且不把目標放在直接找到 mid 做為答案。\n再從最小範圍 left, right 中找到最終解答。\nReference 在排序数组中查找元素的第一个和最后一个位置 · Find First and Last Position of Element in Sorted Array ","date":"2022-04-01T16:33:35+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-34/","tags":[],"title":"【Leetcode】python - [34] Find First and Last Position of Element in Sorted Array 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 458 · Last Position of Target\n難度 easy\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: An integer array sorted in ascending order @param target: An integer @return: An integer \u0026#34;\u0026#34;\u0026#34; def last_position(self, nums: List[int], target: int) -\u0026gt; int: # write your code here if not nums: return -1 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if (nums[mid] == target): # find last, move start start = mid elif (nums[mid] \u0026lt; target): start = mid else: # (nums[mid] \u0026gt; target): end = mid else: # when quit loop if nums[end] == target: return end elif nums[start] == target: return start else: return -1 return -1 算法說明 基本的 binary search，採用的策略為先縮小範圍再得到答案。\n也就是說 left, right 縮小至最小範圍的 left, right，再去判斷答案。\n當然比較常見的方法是：left, right 找 mid，mid 直接找到答案會更為簡潔。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意 input nums 為 [] 的時候的處理，return 個 -1 給他\nBoundary conditions start 與 end 的處理一直都是 binary search 常出錯的地方，這邊要細心一些。\n我採用網路上看到的 start + 1 \u0026lt; end 的方式，先縮小 left, right 範圍\n並且不把目標放在直接找到 mid 做為答案。\n再從最小範圍 left, right 中找到最終解答。\nReference 目标最后位置 · Last Position of Target ","date":"2022-04-01T16:26:39+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/lintcode-python-458/","tags":[],"title":"【Lintcode】python - [458] Last Position of Target 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 14 · First Position of Target\n難度 easy\n個人範例程式碼 from typing import ( List, ) class Solution: \u0026#34;\u0026#34;\u0026#34; @param nums: The integer array. @param target: Target to find. @return: The first position of target. Position starts from 0. \u0026#34;\u0026#34;\u0026#34; def binary_search(self, nums: List[int], target: int) -\u0026gt; int: # write your code here if not nums: return -1 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if(nums[mid] == target): # find first, move end end = mid elif(nums[mid] \u0026lt; target): start = mid else: # (nums[mid] \u0026gt; target): end = mid else: # when quit loop if nums[start] == target: return start elif nums[end] == target: return end else: # not found return -1 return -1 算法說明 基本的 binary search，採用的策略為先縮小範圍再得到答案。\n也就是說 left, right 縮小至最小範圍的 left, right，再去判斷答案。\n當然比較常見的方法是：left, right 找 mid，mid 直接找到答案會更為簡潔。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意 input nums 為 [] 的時候的處理，return 個 -1 給他\nBoundary conditions start 與 end 的處理一直都是 binary search 常出錯的地方，這邊要細心一些。\n我採用網路上看到的 start + 1 \u0026lt; end 的方式，先縮小 left, right 範圍\n並且不把目標放在直接找到 mid 做為答案。\n再從最小範圍 left, right 中找到最終解答。\nReference 二分查找 · First Position of Target ","date":"2022-04-01T16:24:41+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/lintcode-python-14/","tags":[],"title":"【Lintcode】python - [14] First Position of Target 個人解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 704. Binary Search\n難度 easy\n個人範例程式碼 class Solution: def search(self, nums: List[int], target: int) -\u0026gt; int: if not nums: return -1 start, end = 0, len(nums)-1 while(start + 1 \u0026lt; end): mid = (start + end) // 2 if nums[mid] == target: # to find first, move end end = mid elif nums[mid] \u0026lt; target: start = mid else: # nums[mid] \u0026gt; target: end = mid else: # when quit loop if nums[start] == target: return start elif nums[end] == target: return end else: return -1 return -1 算法說明 基本的 binary search，採用的策略為先縮小範圍再得到答案。\n也就是說 left, right 縮小至最小範圍的 left, right，再去判斷答案。\n當然比較常見的方法是：left, right 找 mid，mid 直接找到答案會更為簡潔。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意 input nums 為 [] 的時候的處理，return 個 -1 給他\nBoundary conditions start 與 end 的處理一直都是 binary search 常出錯的地方，這邊要細心一些。\n我採用網路上看到的 start + 1 \u0026lt; end 的方式，先縮小 left, right 範圍\n並且不把目標放在直接找到 mid 做為答案。\n再從最小範圍 left, right 中找到最終解答。\nReference Binary Search · Binary Search ","date":"2022-04-01T16:20:32+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-704/","tags":[],"title":"【Leetcode】python - [704] Binary Search 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 409. Longest Palindrome\n難度 easy\n個人範例程式碼 class Solution: def longestPalindrome(self, s: str) -\u0026gt; int: if not s: # input handling return 0 ans = 0 contains_odd = 0 word_counter = self.counter(s) # 直接使用等同於 collections.Counter(s)，就不用另外自己寫 counter for key, value in word_counter.items(): if value % 2 == 0: ans += value else: ans = ans + value - 1 if contains_odd: pass else: contains_odd = 1 return ans + contains_odd def counter(self, s): counter = {} for char in s: if char not in counter: counter[char] = 1 else: counter[char] += 1 return counter 算法說明 原本想要直接用 counter 把這題爆出來，後來發現 Lintcode 似乎沒有內建這種東西，我就自己重寫了一個\n後來發現其實還是有 collections 可以用，但反正都寫了就這樣吧~\n另外要注意的事情：\n當偶數的時候，直接加上去 當奇數的時候，-1後加上去，並保留最多一個中心點。 最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 注意沒有輸入的時候的處理，input 為空白的時候 return 個 0 給他\nBoundary conditions 我們這題沒辦法，「一定要把每個元素都看過」，至少 O(n)，\nReference ","date":"2022-03-30T21:36:02+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-409/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [409] Longest Palindrome 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 125. Valid Palindrome\n難度 easy\n個人範例程式碼 class Solution: def isPalindrome(self, s: str) -\u0026gt; bool: if not s: # input handling, include \u0026#34; \u0026#34; return True left = 0 right = len(s)-1 s = s.lower() while(left \u0026lt; right): if not s[left].isalnum(): left += 1 elif not s[right].isalnum(): right -= 1 elif s[left] == s[right]: left += 1 right -= 1 else: return False return True 算法說明 使用 two pointer 前後對照，並使用 isalnum() 排除非字母的內容。\n(最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解)\n補充 isalpha(), isalnum() 因為本題敘述中提到 removing all non-alphanumeric characters，因此需要保留「alpha 與 numeric」，\n不能只判斷 \u0026ldquo;字母\u0026rdquo; 使用 isalpha()。\n參考資料： Python isalnum()方法 Python String isalpha() Method input handling 注意沒有輸入的時候的處理，另外同時處理 input 為 \u0026quot; \u0026quot; 的情況，\n答案皆為 True (沒有就是 True)\nBoundary conditions 搜尋範圍為 left \u0026lt; right 區間，此外我們來討論是否需要考慮 left right 的情況\n這邊假設都是一堆特殊符號跟 c，\n如果判斷等於，\n則奇數 cc (自己等於自己) 會多判一次，不影響結果\n偶數 cc 本來就應該要判斷的，也不影響結果。\n因此本題寫 left \u0026lt; right 或 left \u0026lt;= right，都能得到正確答案。\nReference Solution in Python 3 (beats ~100%) (two lines) ( O(1) solution as well ) Python in-place two-pointer solution. [Python] Two pointers, O(n) time \u0026amp; O(1) space, explained ","date":"2022-03-29T13:35:55+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/img_0303.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-125/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [125] Valid Palindrome 個人解法筆記 | 內含 python isalpha(), isalnum() 的整理"},{"categories":["710 - Python LeetCode"],"content":"題目出處 28. Implement strStr()\n難度 easy\n個人範例程式碼 class Solution: def strStr(self, haystack: str, needle: str) -\u0026gt; int: if haystack \u0026lt;mark\u0026gt; None or needle \u0026lt;/mark\u0026gt; None: # input handling return -1 for i in range(len(haystack)-len(needle)+1): # bounding, O(n) if (self.compare_strstr(haystack[i:i+len(needle)], needle)): return i return -1 def compare_strstr(self, part_of_haystack, needle): # O(m) return part_of_haystack == needle 算法說明 字串比對字串，很簡單的處理\n(最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解)\ninput handling 注意沒有輸入的時候的處理，\n這裡要特別小心，「if not X」，會因為在Python中，None、False、0、\u0026quot;\u0026quot;(空字符串)、、()(空元組)、{}(空字典) 都相當於 False 而出錯，\n我們需要特別抓出「None」來判斷，因此我們必須要寫\nif haystack \u0026lt;mark\u0026gt; None or needle \u0026lt;/mark\u0026gt; None: # input handling 而不能寫\nif not haystack or not needle: Boundary conditions 本題最小的搜尋範圍為 len(source) - len(target)，\n而因為 range 的語法關係，有 upper bounding 需要 +1\n因此範圍為 「for i in range(len(haystack)-len(needle)+1): # bounding, O(n)」\n補充 - 此題最佳算法為 KMP 演算法 O(n) 我上述舉的例子為 O(mn)，但這題的最佳作法為 KMP 演算法，\n但由於目前階段還沒有想要鑽研特定項目到太深，這個特殊作法日後再來補\n可參考：O(m+n) and O(mn) solutions Reference python中的None與NULL用法說明 Python 56 ms Time O(N*M) Space O(1) My answer by Python ","date":"2022-03-29T12:59:31+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/img_0302.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-28/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [28] Implement strStr() 個人解法筆記 | 內含 python None 的整理"},{"categories":["841 - Coding Style"],"content":"前言 Coding Style 其實是一個大題目，但因為學校普遍比較不注重這個，\n因此很多人都是到職場才學或甚至沒學過，\n它的重要性其實甚至比好的演算法還重要，\n就好比今天在職場上大家互相合作 debug，\ncoding style 好：看起來就像是一篇舒服的文章，人人都可以快速理解，甚至很願意幫你 debug coding style 爛：看起來就像是一篇雜亂無章的文章，人人都避之唯恐不及，不要拿那種恐怖的東西給我看好嗎? 而且，除了同事要理解，後續維護/交接的人，或甚至是「未來的你」，看到雜亂無章的 coding style，\n「只要未來的你有機會恨過去的自己是在寫三X」，你就應該好好的培養 coding style 的習慣。\n即使算法差，但未來的你學會了更多更好的算法，回頭來看過去曾經寫的東西，還是會覺得「舒服容易理解」，\n基本上那就是成功了! 這就是好 coding style，無關乎算法好壞，但可能比好的算法還更重要\n01 起手式 - 變數命名 命名本身要「具有意義」，這個是基本中的基本，\n你應該不會想要拿到別人的 code 之後看到他的變數命名是 「a,b,c,d,e\u0026hellip;」然後你還會想幫他 debug 吧?\n我學習的 Coding style 為 Google 開源專案風格指南，感謝大大整理出的中文版\n有興趣可以參考：https://tw-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules.html\n另外 Coding style 並非只有一套，學習自己喜歡的即可。\n讓人痛苦的 Coding style 我只是想示範怎麼樣會讓人頭痛而已\ndef this_is_funcion1(): pass def ThisIsFuncion2(): pass def THIS_IS_FUNCTION_3(): pass 我不能說他有錯，但拿這種程式請別人幫忙 debug，你可能會先讓那個人瘋掉\u0026hellip;\n先學一些基本的就好 完整的表可以去官網查：https://tw-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules.html\n但我這個實用導向，然後慢慢學，慢慢優化，\n所以我先學幾個基本的來用，\nfunction name 我標題應該不是下得很好，反正是我要看的(喂\n記憶點：只有 ClassName 比較特別。其他都是 lower_with_under 的格式。 module_name package_name ClassName method_name function_name variable name 記憶點：只有 GLOBAL_VAR_NAME 比較特別。其他都是 lower_with_under 的格式。 GLOBAL_VAR_NAME instance_var_name function_parameter_name local_var_name 小結 蛤? 你說這就沒了嗎?\n欸不是，這種類似習慣的東西慢慢學，慢慢優化就好，沒有一天在那邊大爆學的啦!\n相信我，你一天大爆學，大概明天就全忘光還不會實作，\n習慣就是慢慢地去改就好XD\nReference Google 開源專案風格指南 Google C++ Style Guide [Python] 值得參考的 Coding Style 整理筆記 ","date":"2022-03-29T12:07:28+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/software-engineering/coding-style/coding-style-1/","tags":["Coding","Style"],"title":"【程式優化】Google Coding Style - 01 | variable/ function python 等命名原則"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 由於我手機螢幕之前摔到，邊角的玻璃貼已經破損很久了，\n而破損久了就覺得可能多少會影響一些運氣的問題。\n大概這破損也持續了半年吧，也趁這次大採購的過程中順便替換一下，\n順便看能不能改改運。\n△ 我手機摔到而壞掉的玻璃貼，這個是某 D 牌的玻璃保護貼，可以看到邊角的破損\n(我原本使用的是某 D 牌的保護貼)\n我自己事前查到的評價 犀牛盾，基本上大牌我覺得本來就沒什麼好擔心的。\n而這次的 3D 壯撞貼 聽說是他們的新款，\n因為玻璃貼這種東西本來就不太會換，就一次買好一點了。 (我更怕的是手機摔壞)\n購入價 我是在 犀牛盾官方 買的，配合活動 9 折 購入價 $774，\n而且據說還都會附「貼膜輔助工具」，滿適合我這種手殘黨的，就買了。\n開箱 △ 3D 壯撞貼 的外包裝\n△ 個人特別喜歡這種小細節，讓人方便拉的小設計！\n△ 打開盒子，拿出裡面的內容物！！！\n△ 開箱後的內容物大概有這些！！！\n△ 這個就是傳說中讓手殘黨也能順利貼成功的「貼膜輔助工具」！\n△ 安裝說明書，拍一下XD △ 3D 壯撞貼 的本體\n△ 撕掉我原本破掉的玻璃貼\n△ 安裝「貼膜輔助工具」的樣子，因為他下方有個孔洞的設計，所以能精準對位\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n使用了一週之後，我的使用心得\u0026hellip;\n這次的產品比較薄，安裝後剛開始覺得滿特別的，\n原本的 9H 玻璃貼比較厚，有稍微習慣一下，\n透明的感覺很不錯，至少沒有特別明顯的有貼膜感。\n至於保護力嘛\u0026hellip; 我又沒摔倒要怎麼測試啦！！！！ (當然希望是不要！)\n","date":"2022-03-25T23:28:54+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/3D_impact_screen_protector_7-scaled.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/3d_impact_screen_protector/","tags":[],"title":"【嗡嗡開箱 #6】犀牛盾 - 3D 壯撞貼 | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 作為一個工程師同時兼作家，每天大量的用眼真的很需要好好照顧自己的眼睛，\n因此對於眼睛保養的東西自然花錢就比較不手軟一些XD，\n這次開箱這個做為嘗試，希望能改善長期使用眼睛的眼睛疲勞問題。\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 我有看到網路上大多人都說這個是眼睛按摩器中最多人推薦的 (看 ptt 網友們的評價)，\n而最多人推的就是他的冷熱敷功能，其他有看到有人也推 osim，\n不過就單純只有熱敷了，因此最後想體驗一下冷熱敷看看。\n題外話：這裡有個不太好的示範！！強烈建議這類按摩用品先去實體店體驗一下再進行購買！！！\n免得到時候萬一後悔是「不能退貨」的！！！\n購入價 我是在 momo 買的，配合 600 元折價券，購入價 $4080\n開箱 △ 收到包裹的樣子，跟想像中差不多大\n△ 按摩器盒子本體\n△ 拿出來的大小，盒子分兩層，很有精裝版的感覺\n△ 眼睛按摩器的本體！！！\n△ 其他配件與說明書\n△ 全部內容物來合照一張\n△ 加上防污親膚布套後，可以重複清洗，避免臉上的油殘留於本體\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n真的跟 ptt 網友們說的一樣，冷熱敷的功能真的很讚！！！\n但深層按摩我自己覺得是還好，有按到，但不是特別有感 (特別是眼罩因為氣壓還會翹起來XD)\n就沒特別有感，我後來都只會用模式2 (純冷熱)，就比較少用氣壓了，\n不過畢竟今天才第一天開箱，也許後續會漸漸習慣也說不定？\n","date":"2022-03-20T02:06:41+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/ts-188_unbox_7-scaled.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/ts-188/","tags":[],"title":"【嗡嗡開箱 #5】煥眼舒冷熱眼部按摩器 TS-188 (曜石黑/精裝版) - tokuyo | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 最近由於剛離職，開始在家學習一陣子順便準備面試的生活，\n作為一個工程師，因此開始想買一些電腦的周邊配備練習\n畢竟自己就是靠這個生存的XD，希望讓自己做事情的效率或舒適度能夠更上一層樓。\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 我有看到網路上大多人都說這個是最強多工的文書鍵盤，\n對於像我這種\n- 大部分時間都是在開發 - 不是開發的時間也幾乎都是在寫文章，重度鍵盤使用XD - 常常會在多電腦之間切換工作，特別還會跨 windows, mac, linux - 很少打遊戲，幾乎只看實況、YT 這鍵盤應該是非常符合我的需求的\n(如果是要買來玩遊戲那派的就可以先不用理會這篇文了，這鍵盤一定不太適合)\n購入價 我是在 pchome 買的，當時有優惠 -300 送手托 (難道不是預設就會附的嗎？！)\n購入價 $3690\n開箱 △ 收到包裹的樣子，比想像中還大好多！！！\n△ 箱子裡面的東西長這樣\n△ 全部拿出來的話就是這樣囉~\n手托 先來開箱比較小的手托，比較快XD\n△ 開箱出來就一個手托與說明書，手托比想象中的還扁XD 不過還滿舒服的！\n重頭戲 - MX Keys 鍵盤本體 △ 打開箱子長這樣\n△ 打開盒子\n△ 這就是網路傳說中的最強文書鍵盤 MX Keys ！！！\n△ 拿１０塊錢來比較一下，這高度\u0026hellip; 我開始有點擔心是不是有點太矮\n△ 最後再把箱子裡面的全部內容物再拍一遍，全部大概長這個樣子~\n羅技神奇的多功能跨平台「黑科技flow」 註： 本功能需要搭配 MX Master 系列滑鼠使用!\n這台電腦複製，那台電腦貼上，不管作業系統，「跨OS」還能「跨螢幕」無痛切換!\n其實我也不是什麼羅技的鐵粉，但我怎麼知道，\n我想要的功能，他剛好都做出來了XDDD，我也只好買下去了XD\n我們來看一次這個能解釋一切的 gif：\n因為這個無痛跨「windows」、「mac」電腦的滑鼠與鍵盤，\n完全解決我的痛點，甚至「還不用從滑鼠鍵盤底下切換」，\n我只能給滿分了，目前沒看過類似的產品，這完全就是我要的！！！\n題外話：使用的鍵盤與滑鼠組合為：MX Keys + MX master 3\n羅技「flow」技術，「鍵盤滑鼠一起」無痛地跨到另外一台電腦 羅技「flow」更誇張的是，滑鼠移動的同時，連鍵盤都可以「自動切換」過去。\n多台電腦的概念，直接變成「一組鍵盤滑鼠」，就能實現全部控制。\n目前這真的是我碰到最無痛的使用方式了，\n以往都還要「按鍵盤或滑鼠的切換按鍵」，這次「直接滑鼠移過去就好了」。\n好扯XDDD\n羅技「flow」技術，無痛「跨 OS」檔案傳輸 羅技「flow」還有另外一個主打功能，\n就是滑鼠拖曳檔案的同時，或是鍵盤複製貼上的時候，\n可以直接「無痛的在另外一台電腦上貼上」。\n讓我們來看看示範 gif，「不管作業系統，這台複製，那台貼上」： 當初我第一次試用這個功能的時候\u0026hellip;\n我心中全部都是問號．．．\n羅技你不是做滑鼠鍵盤的嗎！！！怎麼搞起檔案傳輸來了！！！\n太扯了太扯了！！！ (讚到不行的意味，好扯，真的好用)\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n先說使用了一天之後，我的使用心得\n我原本 windows 電腦使用的鍵盤是 K70，而 mac 使用的就是筆電的原生鍵盤，\n這邊讀者可以猜猜看我有沒有順利的同時習慣兩者呢？\n.\n.\n.\n.\n.\n.\n.\n.\n答案是，連我自己都很意外的，我反而抓不到 windows 電腦 K70 的那種手感了！\n取而代之的，我完全能把這個當作我的 Mac 筆電原生鍵盤使用！！！\n這真是太神奇了！！！\n因為平常使用的 K70 都是外接式的鍵盤，我原本還預期手感應該比較類似，而沒有適應的陣痛期，\n沒想到結果完全反過來！！！\n但這結果我非常滿意XDD\n因為我本來就是打算買一個 mac 為主的鍵盤，同時通用於 windows，\n因為桌子小的關係，不希望同時擺兩組鍵盤，\nMX keys 的 easy switch 功能很讚，一個按鍵就能快速切換 windows 與 mac 了！\n而因為我目前開發多在 mac 上的關係，因此終於能把 Mac 當作另外一台類似桌機的電腦來使用了！\n我覺得這產品對我來說真的讚！\n他成功滿足了我\n- 多電腦間切換，windows, mac - 多電腦切換後，且鍵盤能都適用 - 可以讓我以外接鍵盤的方式，適應 mac 上面鍵盤的配置 (可以說是把 mac 接螢幕當桌機用) - 無線，終於可以不用困擾於桌面的到處都有亂亂的線了 (還是有啦，只是有少了有感XD) (我必須說這個結果真的很可能是因為我自己使用習慣的關係XDD\n","date":"2022-03-19T13:37:50+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8875_4_MOV_AdobeExpress.gif","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/mx-keys/","tags":[],"title":"【嗡嗡開箱 #4】羅技 MX Keys 智能無線鍵盤 | 不專業開箱文"},{"categories":["710 - Python LeetCode"],"content":"題目出處 145. Binary Tree Postorder Traversal\n難度 easy\n題目分類 Stack, Tree, Depth-First Search, Binary Tree\n解 Tree 樹問題的重點整理 這一篇的前身是以下連結，我們繼續在這篇整理重點\n【Leetcode】python – [144] Binary Tree Preorder Traversal 個人解法筆記 (內含範例程式碼) 內含 處理 Tree 樹問題的重點\nTree 基本中的基本，我們一定至少需要學會兩種 parse tree 的方式，\n一種是 iterative (直接的取) 另一種是 recursive (遞迴的取) 基本上這兩種都務必熟悉，未來解題時萬一一種解不出來，就可以嘗試用另外一種方法。\n個人範例程式碼 - recursive 版 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def postorderTraversal(self, root: Optional[TreeNode]) -\u0026gt; List[int]: ans = [] self.dfs(root, ans) return ans def dfs(self, root, ans): if root: self.dfs(root.left, ans) self.dfs(root.right, ans) ans.append(root.val) 算法說明 recursive 的 parse tree 基本上可以結合 dfs 的概念，\n而我們需要另外寫一個 function dfs()，因為我們會需要把答案帶著走，原題目的函數沒辦法直接遞迴。\n老實說個人覺得，Tree 系列的題目用 recursive 的概念是最接近原本學習知識觀念的解法XD。\n寫起來非常直覺，而且稍微變化一下其他的解就都能直接跑出來。\n主要變化的範圍如下，然後依照 (M)RL、R(M)L、RL(M) 改一下順序，全部的解法就都出來了!\nself.dfs(root.left, ans) self.dfs(root.right, ans) ans.append(root.val) corner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 處理沒有節點的情況， if not root 直接回傳。\n另外一種漂亮的解法：直接只處理 「if root:」，我們才做事情。\n個人範例程式碼 - iterative 版 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def postorderTraversal(self, root: Optional[TreeNode]) -\u0026gt; List[int]: ans = [] stack = [root] # LR(M) -\u0026gt; (M)RL while(stack): if root: # left to leaf ans.append(root.val) # add M first stack.append(root.left) # record later root = root.right else: # can not left root = stack.pop(-1) return ans[::-1] 算法說明 我們要處理的問題變得比較麻煩一些，變成 LR(M),\n但因為我們其實都是在控制 (M) 出現的時機，\n因此比較好的方式是我們把它提造最前面去做，\n因此我們以 (M)RL 去做一棵樹，而最後「再反序」。\n我們分兩個 part\n可以往下走的情況， (此處是往右先) 我們可以往右就右到底。\n先加 (M)，存左，往右走\nif root: # left to leaf ans.append(root.val) # add M first stack.append(root.left) # record later root = root.right 不能往下走，就往左走的情況 表示上述 「if root」 找不到結果的情況，\npop 出「最後一個」「分歧點」，\n也就是「回到上一個岔路往左走」。\nelse: # can not left root = stack.pop(-1) corner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 結束條件為 Stack 為空，我們運用 Stack 可以幫我們保存的概念。\nReference 此系列解法個人的其他文章 【Leetcode】python – [144] Binary Tree Preorder Traversal 個人解法筆記 (內含範例程式碼) 內含 處理 Tree 樹問題的重點 【Leetcode】python – [94] Binary Tree Inorder Traversal 個人手寫解法筆記 (內含範例程式碼) 內含 處理 Tree 樹問題的重點 個人認為整理的最完整的解法 Summary of preorder, inorder, postorder, four traversal ways for each 很用心的 DFS 製圖 All DFS traversals (preorder, inorder, postorder) in Python in 1 line 結合 visited 的很酷的 solution Python 3 - All Iterative Traversals - InOrder, PreOrder, PostOrder - Similar Solutions - [Python solutions (recursively and iteratively).](https://leetcode.com/problems/binary-tree-preorder-traversal/discuss/45290/Python-solutions-(recursively-and-iteratively).) - [Python recursive and iterative solutions.](https://leetcode.com/problems/binary-tree-postorder-traversal/discuss/45786/Python-recursive-and-iterative-solutions.) ","date":"2022-03-19T10:10:49+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-145/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [145] Binary Tree Postorder Traversal 個人解法筆記"},{"categories":["923 - Windows"],"content":"前言 由於因為我最近買了新的螢幕，\n剛好有時候會同時需要「一個螢幕」分別操作 windows 與 mac 輪流切換，\n可是以螢幕的概念，即使切換，畫面只是「不顯示」而已，\n但是視窗依然是可以移動過去「被切掉的那個螢幕的」，\n因此這篇紀錄一下解決辦法。\n簡單來說，就是視窗「出現在一個我們看不到的地方」，但那個地方只是螢幕沒有顯示出來而已。\n解決辦法 這邊提供的是我自己後來最習慣的解法\nstep 1. 「按住shift」並點選功能表右邊對應的的視窗預覽 (也就是開始右邊那堆小 icon) 「按住shift」並點選功能表右邊對應的的視窗預覽 (也就是開始右邊那堆小 icon)，\n這時候按下右鍵，這時候會叫出一些可以操作的選項，例如「移動」\n這時候我們「點選移動」\n注意：如果「移動」選項不能點選的話，請先按「還原」，再按一次就會出現可以移動的選項了!\nstep 2. 選擇「移動」，並任意方向按一下方向鍵，移動滑鼠，就可以將視窗拉回當前畫面。 這時候我們選擇「移動」，並任意方向按一下鍵盤方向鍵，移動滑鼠，就可以將視窗拉回當前畫面。\n我們會發現視窗是被滑鼠黏住的狀態。\nReference How to move a lost off-screen window back to desktop in Windows 10 ","date":"2022-03-13T22:55:58+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/window-off-screen-2.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/windows/windows-10-window-out-of-screen/","tags":[],"title":"【Windows】問題解決：windows 畫面跑出視窗外 透過滑鼠快速移動視窗回到當前螢幕筆記 (windows 10 window out of screen)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 94. Binary Tree Inorder Traversal\n難度 easy\n題目分類 Stack, Tree, Depth-First Search, Binary Tree\n解 Tree 樹問題的重點整理 這一篇的前身是以下連結，我們繼續在這篇整理重點\n【Leetcode】python – [144] Binary Tree Preorder Traversal 個人解法筆記 (內含範例程式碼) 內含 處理 Tree 樹問題的重點\nTree 基本中的基本，我們一定至少需要學會兩種 parse tree 的方式，\n一種是 iterative (直接的取) 另一種是 recursive (遞迴的取) 基本上這兩種都務必熟悉，未來解題時萬一一種解不出來，就可以嘗試用另外一種方法。\n個人範例程式碼 - recursive 版 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def inorderTraversal(self, root: Optional[TreeNode]) -\u0026gt; List[int]: ans = [] self.dfs(root, ans) return ans def dfs(self, root, ans): if root: self.dfs(root.left, ans) ans.append(root.val) self.dfs(root.right, ans) else: return 算法說明 recursive 的 parse tree 基本上可以結合 dfs 的概念，\n而我們需要另外寫一個 function dfs()，因為我們會需要把答案帶著走，原題目的函數沒辦法直接遞迴。\n老實說個人覺得，Tree 系列的題目用 recursive 的概念是最接近原本學習知識觀念的解法XD。\n寫起來非常直覺，而且稍微變化一下其他的解就都能直接跑出來。\n主要變化的範圍如下，然後依照 (M)RL、R(M)L、RL(M) 改一下順序，全部的解法就都出來了!\nself.dfs(root.left, ans) ans.append(root.val) self.dfs(root.right, ans) corner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 處理沒有節點的情況， if not root 直接回傳。\n另外一種漂亮的解法：直接只處理 「if root:」，我們才做事情。\n個人範例程式碼 - iterative 版 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def inorderTraversal(self, root: Optional[TreeNode]) -\u0026gt; List[int]: # left mid right nodeStack = [] ans = [] while root or nodeStack: if root: nodeStack.append(root) root = root.left # repeat put left if exist else: # nothing left curNode = nodeStack.pop() ans.append(curNode.val) # append left root = curNode.right return ans 算法說明 直覺上的解法我們需要配合 Stack，遞迴的做法我們需要配合 DFS。\nStack 為 pop() 或 pop(-1) 就可以直接實現。\n以下筆記手寫，字醜傷眼注意XDD 由上圖，我們可以發現，我們會先\n向左走到底，一邊存 node 進 stack 當中， 發現沒辦法向左之後，pop一個值， 此值就是我們拿 inorder 中間值的時候 pop 後往右 注意：由於「後處理要先放」，因此順序會是「先右後左」 你可能會想問這麼注意順序為什麼不用 Queue?\n因為「先進先出」的結構我們沒辦法保存應該要後處理的東西。\n例如 第一層的 左右，加入第二層後，\n第一層的右一定會先出後，才會做第二層，因此沒辦法。\ncorner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 結束條件為 Stack 為空，我們運用 Stack 可以幫我們保存的概念。\n結論 - 處理樹的重點 直接的解法我們需要配合 Stack，遞迴的做法我們需要配合 DFS Stack 為 pop() 或 pop(-1) 就可以直接實現，注意「後處理要先放」。\ninterative 的重點 我們可以發現，不論是哪一種 interative 方法，「向左走到底都是必須的」，\n因此，我們就只要專心的看何時是我們該拿值的地方，\n就可以順著把這三題都解出來\n我們 traversal tree 主要分成兩種情況\n向左走到底，一邊存 node 值 (preorder 此時取值) \u0026lt;\u0026ndash; (M)RL 無法向左的情況，從 stack pop 出一個值 (inorder 此時取值)，並向右 做完第二步就繼續左到底，如果不行就 pop，如此反覆\n而我們也可以分析出，類似相關的題目中\ninorder - R(M)L：拿時存答案 preorder - (M)RL：先放值(mid)，再拿 postoreder - RL(M)：先放值(mid)，再拿，最後反序 postorder我們處理時會比較特別，因為他是 RL(M) 的結構，\n而我們 traversal tree 時，通常都一定會先從根開始看，\n因此比較好的處理方式，我們會一開始就先把順序倒過來看，\n變成我們處理 (M)LR，最後再反序。\nReference 個人認為整理的最完整的解法 Summary of preorder, inorder, postorder, four traversal ways for each 很用心的 DFS 製圖 All DFS traversals (preorder, inorder, postorder) in Python in 1 line 結合 visited 的很酷的 solution Python 3 - All Iterative Traversals - InOrder, PreOrder, PostOrder - Similar Solutions - [Python solutions (recursively and iteratively).](https://leetcode.com/problems/binary-tree-preorder-traversal/discuss/45290/Python-solutions-(recursively-and-iteratively).) - [Python recursive and iterative solutions.](https://leetcode.com/problems/binary-tree-postorder-traversal/discuss/45786/Python-recursive-and-iterative-solutions.) ","date":"2022-03-13T22:26:12+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/img_0273.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-94/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [94] Binary Tree Inorder Traversal 個人手寫解法筆記 內含 處理 Tree 樹問題的重點"},{"categories":["420 - Docker"],"content":"前言 這篇的前身是 【Windows】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (Windows anydesk ssh command line) 上一個方法我們是使用 anydesk 作為 ssh 的跳板，並且透過 VScode 遠端連進去，\n這篇的方法省掉一個 anydesk 的步驟，我們直接連進去。\n另外其實還有一篇是不透過 anydesk 的舊方法，可以參考這一篇 【Docker】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發 透過 VScode 連線至遠端 container - 直接於 VScode 設定連線位置 (2022/3/13 新增方法) 新方法簡單很多，直接在 VScode 設定時修改 docker.host 的位置指定給要連線的主機即可，\n須注意以下幾點：\nVPN 連上遠端網域 ssh 先連上對方主機 (可以使用 ssh target 的地方連入)，此時可以看到對方的 continer 找到對應的 container 進行連線 基本的 docker 擴充功能必須先安裝好，\n而且 local 也需要具備有 docker 的功能，\n如果不知道如何在 windows 中啟用 docker 功能可以參考這篇：\nhttps://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/\nstep 1. VPN 連上遠端網域 之前已經有很多教學了，不知道的可以參考\n【MobaXterm】在 Windows 中使用 MobaXterm VPN 連線至遠端系統 (linux, ubuntu) 遠端 ssh 顯示畫面\nstep 2. ssh 先連上對方主機 (可以使用 ssh target 的地方連入)，此時可以看到對方的 continer ssh 先連上對方主機 (可以使用 ssh target 的地方連入)，此時可以看到對方的 continer\n範例圖：我們可以從 VScode 側邊的遠端設定當中，直接切換「目前視窗」，變成「遠端的連線視窗」 範例圖：我們可以從 VScode 左下角的狀態判斷自己是否已經連線 step 3. 找到對應的 container 進行連線 到這邊就會跟之前的結果一樣了，因此我們就不用再像舊方法一樣，設定東設定西的。\n會顯示遠端的 container 透過 VScode 連線至遠端 container - ssh 反向跳板 (2021/11/13 舊方法) step 1. terminal 建立 ssh 反向通道，使本地可查看遠端 docker 內容 ssh -p 9000 -nNTL localhost:23750:/var/run/docker.sock ubuntu@localhost step 2. VScode 設定的部分 基本的 docker 擴充功能必須先安裝好，\n而且 local 也需要具備有 docker 的功能，\n如果不知道如何在 windows 中啟用 docker 功能可以參考這篇：\nhttps://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/\nstep 2.1 打開「設定」，我們準備進行修改 我們打開並修改設定「settings.json」，\n點選「檔案」-\u0026gt;「喜好設定」中的「設定」。\nstep 2.2 在上方搜尋「settings.json」(不用全輸入)，打開「settings.json」，我們準備進行修改 或者也可以點擊右上角的符號，也可以叫出 「settings.json」 step 2.3 在「settings.json」中加入遠端 container port 的設定 在「settings.json」中加入這行\n\u0026#34;docker.host\u0026#34;: \u0026#34;tcp://localhost:23750\u0026#34; 示意圖： 只需要確保有加入紅框那行即可，\n其他行代表的是其他設定，可以不用管。\n完成結果 我們可以在 local 自己的 VScode，\n可以透過「Docker extension」直接看到遠端 container 內的資料。\n(注意：不需要再另外經由 VScode 的 ssh 至遠端)\n會顯示遠端的 container Reference 【Docker】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發 SSH through TCP tunneling SSH 遠端連線回家中的 Windows 電腦 ","date":"2022-03-13T18:56:04+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/vscode-remote-container-3.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/windwos-vscode-ssh-container/","tags":["Docker"],"title":"【Windows】在 windows VScode 透過 ssh 連線至遠端 Linux Docker container (2022/3/13 新增方法)"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 最近由於剛離職，開始在家學習一陣子順便準備面試的生活，\n作為一個工程師，因此開始想買一些電腦的周邊配備練習\n畢竟自己就是靠這個生存的XD，希望讓自己做事情的效率或舒適度能夠更上一層樓。\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 我有看到網路上大多人都說這個是最強多工的文書滑鼠，\n對於像我這種\n- 大部分時間都是在開發 - 常常會在多電腦之間切換工作，特別還會跨 windows, mac, linux 這滑鼠應該是非常符合我的需求的！！！\n特別是我有被羅技的特別技術「flow」吸引到！！！\n什麼是「flow」呢？\n讓我們直接先用一段 gif 來解釋一切：\n有注意到嗎XD，「windows」與「mac」之間的滑鼠超無痛切換！！！\n購入價 我是在 原價屋 買的，購入價 $3390，\n當時還會送一個美容用的粉晶按摩美容儀\n開箱 △ 外盒，看起來只有一個滑鼠的大小XD\n△ 打開後，是一個設計還不錯的盒子\n△ 打開盒子，看到我們的滑鼠本體大概長這樣\n△ 先開比較小盒的XD 裡面有說明書跟線材\n△ 滑鼠本人，功能鍵設計還滿多的滑鼠\n△ 側拍，注意除了拇指輪與側邊兩個按鍵外，底下有個微突起的地方也是一個按鍵！！！\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n使用了一週之後，我的使用心得\u0026hellip;\n真的太好用了！！！ (原本我是想說太神了，不過這樣略顯浮誇)\nMX 系列的「flow」技術，絕對是羅技出過最鬼的功能，「跨OS」還能「跨螢幕」無痛切換 讓我們再看一次這個能解釋一切的 gif：\n因為這個無痛跨「windows」、「mac」電腦的滑鼠與鍵盤，\n完全解決我的痛點，甚至「還不用從滑鼠鍵盤底下切換」，\n我只能給滿分了，目前沒看過類似的產品，這完全就是我要的！！！\n題外話：使用的鍵盤與滑鼠組合為：MX keys + MX master 3\n羅技「flow」技術，「鍵盤滑鼠一起」無痛地跨到另外一台電腦 羅技「flow」更誇張的是，滑鼠移動的同時，連鍵盤都可以「自動切換」過去。\n多台電腦的概念，直接變成「一組鍵盤滑鼠」，就能實現全部控制。\n目前這真的是我碰到最無痛的使用方式了，\n以往都還要「按鍵盤或滑鼠的切換按鍵」，這次「直接滑鼠移過去就好了」。\n好扯XDDD\n羅技「flow」技術，無痛「跨 OS」檔案傳輸 羅技「flow」還有另外一個主打功能，\n就是滑鼠拖曳檔案的同時，或是鍵盤複製貼上的時候，\n可以直接「無痛的在另外一台電腦上貼上」。\n讓我們來看看示範 gif，「不管作業系統，這台複製，那台貼上」： 當初我第一次試用這個功能的時候\u0026hellip;\n我心中全部都是問號．．．\n羅技你不是做滑鼠鍵盤的嗎！！！怎麼搞起檔案傳輸來了！！！\n太扯了太扯了！！！ (讚到不行的意味，好扯，真的好用)\nMX master 3 擁有的側邊拇指滾輪，又可以簡化不少操作 MX master 3 具有的一大特色就是「拇指輪」的設計，\n讓「捲動」這件事情不再只受限於中間的滾輪，\n我自己通常會使用於「mac 之間的桌布切換」，對我來說切換非常的方便，\n而且可以很巧妙的替代「巧控觸控板」的「四指切換桌布功能」。\n鍵盤的部分可以看我的另外一篇開箱文 鍵盤的部分可以看我的另外一篇開箱文，我搭配的鍵盤為 MX keys\nhttps://wongwongnotes.com/posts/life-and-work/reviews/reviews/mx-keys/\n我覺得這產品對我來說真的讚！\n他成功滿足了我！！！\n- 多電腦間切換，windows, mac，而且非常無痛！！！ - 多電腦切換後，且鍵盤滑鼠都能適用 - 可以讓我以外接鍵盤滑鼠的方式，適應 mac 上面的操作 (可以說是把 mac 接螢幕當桌機用) - 無線，終於可以不用困擾於桌面的到處都有亂亂的線了 (還是有啦，只是有少了有感XD) ","date":"2022-03-12T15:10:58+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/IMG_8875_4_MOV_AdobeExpress.gif","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/mx-master-3/","tags":[],"title":"【嗡嗡開箱 #3】羅技 MX Master 3 無線滑鼠 | 不專業開箱文"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 作為一個工程師同時兼作家，每天就是有使用電腦非常大量的多工需求，\n原本已經有兩個螢幕了，但還是覺得工作空間不太夠 (真的是工作狂呢)\n因此這次想要多買一個 21:9 的螢幕在家，搭配上原本的螢幕，\n投資自己就是最好的投資，讓自己的工作效率更加的提升！！！\n另外就是希望能順便「保護眼睛」(雖然保護眼睛的最佳方法一定是不要用電腦，花大錢買螢幕根本幹話\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看在下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 這個網路上的推薦比較少，但我有稍微做了一點功課，我當時選螢幕的重點就是\n- 要有 21:9 - 解析度 2K - CP 值高，也就是價格考量 (其他我比較還好，例如像是 TN 或 IPS 面板我自己比較沒差)\n購入價 我是在 pchome 買的，當時沒有任何優惠，購入價 $14900\n來把東西裝起來囉! 開箱 △ 收到包裹的樣子，這也太大！！！ 第一次收到這種快比人還高的箱子\n△ 打開裡面，第一眼看到的樣子\n△ 先把螢幕拿出來放在沙發上，21:9 真的好大\u0026hellip;\n△ 稍微拿成直的一下，這螢幕主打有 1000R，還真的滿曲的 △ 換個角度看一下，真的好曲，真不愧是主打的 1000R △ 螢幕的背面，有附上耳機孔與幾個 USB 插孔\n安裝 △ 拿出螢幕後，剩下的內容物大概有這些\n△ 這個是底座\n△ 這個是柱子與連結螢幕後面的連接板\n△ 把底座與柱子組合起來~\n△ 最後把連接版卡進去本體就完成了！ 但只有用卡榫，然後螢幕有那麼重，真的讓我還有點擔心會不會撐不住螢幕的重量呢\u0026hellip;\n△ 這是其他箱子內就有送的接線\n△ 初步完成了！ 這是放在我桌面的新樣子！ 真的好大，好爽XDD 新的工作區模樣 △ 我工作區的樣子，windows 版，這個被拉好長XDDD 好讚又好不習慣XDD\n△ 21:9 可以切成左右兩個畫面，而且顯示的內容幾乎不太被壓縮，可以很順的對照文件與參考資料！(這邊順便幫我自己的網站工商一下XDD)\n△ 我目前工作區的樣子，這是 mac 連接時的情況\n這邊要另外提一個我超喜歡的點！！！ 這台螢幕直接 type-c 接 mac 可以直接連接螢幕 + 幫 Mac 充電！！！ 等於說不用再另外接插頭的線，也能夠雙螢幕了！！！ 這設計對我來說真的太讚了！！！\n△ 測試一下 mac 使用終端機，這顯示的內容量，對於想多工的我來說根本讚爆！！！\n初次使用 當然還是要先不務正業一下，玩起來！！！ △ 當然還是要先不務正業的玩一下瑪力歐賽車！！！\n這邊要提示一下，switch 本身並沒有支援 21:9 的輸出，因此這個是畫面自動拉伸的結果 (比例會有點怪)\n△ 不知道是不是畫面比例也被拉長的關係，感覺要切內線變得更難了XDDD\n△ 玩起來！！！ 這邊另外提一下，這個螢幕本身是沒有音響的，需要從耳機孔另外接喇叭哦，不過省下這個喇叭的錢(因為我也會外接)，對我來說 CP 真的讚！\n去 YT 找影片來播放看看效果 △ YT 在我以前的螢幕最多只能顯示四個預覽，現在換成 21:9 直接變成 6 個了！！！\n這邊隨便找一個 YT 的 小幸運 MV\n只能說，大螢幕看起來真的不一樣，大的好爽，整個感覺是真的都不一樣了XDDD\n去 YT 找 4K 21:9 的影片來播放看看效果 這邊就給放一點圖片大家欣賞一下吧~~~\n補充照片 使用 google sheet (excel) 全展開表格的樣子 用這樣的螢幕展開 google sheet (excel) 的表格可以橫向展開 A - AF (共 31 欄)，\n不過因為我 windows 工作列放右側，應該是可以展開更多沒問題。\n使用心得 使用一周後心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n目前用了一週，沒有任何不滿意，只有無限的滿意與更滿意！！！！ (真的回不去了！天啊！！！)\n另外這裡想特別提一下，\n- 我覺得最讚的就是 mac type-c 的螢幕連接兼充電！！！ 只要一線搞定真的讚到不行！！！ - 護眼功能我剛好有使用到，我覺得也滿不錯的 - 1000R 的曲度真的舒服，之前看到 3000R 還沒有這樣的體會... 現在回不去了 3000R 對我來說跟平面一樣 - (對了，小螢幕應該也回不去了，如果有可以選大螢幕的選項的話) 硬要說的缺點 就是桌子如果太小真的會沒空間放這麼大的螢幕\u0026hellip;\n買之前請「務必」、「一定要」先量好桌子的尺寸！！！！\n使用四個月後的心得 用到現在相當舒服，甚至已經不會特別感覺它是一個 21:9 的螢幕，就是一個可以讓工作區變很大的螢幕XD\n文書處理如果是 excel 可以拉到更遠的表格 (我有在上方補圖)，\n其他看有沒有想要做什麼特別的事情，有需要我也可以再貼圖上來XD\n(其實我當初也是要買的時候完全沒有店家願意展示這種特殊比例的螢幕，我真的很困擾QQ，當時只能直接買來賭看看了)\n硬要說唯一缺點就是桌面空間不足會用得很憋，\n還有桌深要注意，因為這個1000R曲率很高，沉浸感好 (體驗遊戲時更容易沉浸在遊戲給的體驗中) 換來的是離螢幕會更近，\n但純工作應該是不太需要這樣的沉靜感(沒有要跑進另一個世界的感覺XD)，如果這部分有想節省成本就可以往平面考慮。\n把螢幕推更遠也可以改善這問題，但就是桌深要夠你能推更遠XD\n","date":"2022-03-12T12:44:59+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/S34A650UXC_unbox-100-scaled-1.png","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/s34a650uxc/","tags":[],"title":"【嗡嗡開箱 #2】S34A650UXC 34型 21:9 WQHD高解析度曲面顯示器 (螢幕S6) - SAMSUNG 三星 | 不專業開箱文"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 144. Binary Tree Preorder Traversal\n難度 easy\n題目分類 Stack, Tree, Depth-First Search, Binary Tree\n個人範例程式碼 - recursive 版 Tree 基本中的基本，我們一定至少需要學會兩種 parse tree 的方式，\n一種是 iterative (直接的取) 另一種是 recursive (遞迴的取) 基本上這兩種都務必熟悉，未來解題時萬一一種解不出來，就可以嘗試用另外一種方法。\n# Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def preorderTraversal(self, root: Optional[TreeNode]) -\u0026gt; List[int]: # pre = m l r if not root: return [] else: ans = [] # in-place change self.dfs(root, ans) return ans def dfs(self, node, ans): if not node: return else: ans.append(node.val) # mid self.dfs(node.left, ans) # l self.dfs(node.right, ans) # r return ans 算法說明 recursive 的 parse tree 基本上可以結合 dfs 的概念，\n而我門需要另外寫一個 function dfs()，因為我們會需要把答案帶著走，原題目的函數沒辦法直接遞迴。\ncorner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 處理沒有節點的情況， if not root 直接回傳。\n另外一種漂亮的解法：直接只處理 「if root:」，我們才做事情。\n個人範例程式碼 - iterative 版 # Definition for a binary tree node. # class TreeNode # def __init__(self, val=0, left=None, right=None) # self.val = val # self.left = left # self.right = right class Solution: def preorderTraversal(self, root: Optional[TreeNode]) -\u0026gt; List[int]: # pre = m l r ans = [] node_stack = [root] while node_stack: cur_node = node_stack.pop() if cur_node: ans.append(cur_node.val) node_stack.append(cur_node.right) node_stack.append(cur_node.left) return ans 算法說明 直覺上的解法我們需要配合 Stack，遞迴的做法我們需要配合 DFS。\nStack 為 pop() 或 pop(-1) 就可以直接實現。\n注意：由於「後處理要先放」，因此順序會是「先右後左」 你可能會想問這麼注意順序為什麼不用 Queue?\n因為「先進先出」的結構我們沒辦法保存應該要後處理的東西。\n例如 第一層的 左右，加入第二層後，\n第一層的右一定會先出後，才會做第二層，因此沒辦法。\ncorner case 特殊情況處理 當樹為空 [] 或 為只有 root [1] 時，需要注意。\nBoundary conditions/ Edge conditions 邊際情況處理 結束條件為 Stack 為空，我們運用 Stack 可以幫我們保存的概念。\n結論 - 處理樹的重點 直接的解法我們需要配合 Stack，遞迴的做法我們需要配合 DFS Stack 為 pop() 或 pop(-1) 就可以直接實現，注意「後處理要先放」。\nReference Python solutions (recursively and iteratively). ","date":"2022-03-09T16:43:45+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-144/","tags":["DFS"],"title":"【Leetcode】python - [144] Binary Tree Preorder Traversal 個人解法筆記 內含 處理 Tree 樹問題的重點"},{"categories":["710 - Python LeetCode"],"content":"題目出處 232. Implement Queue using Stacks\n難度 easy\n題目分類 Stack, Design, Queue\n個人筆記 這篇比較偏程式架構設計，我專注在分析答案，就不特別解。\n算法說明 「兩個 Stack 可以拼出一個 Queue」，\n對於這個問題我們可以這樣想，\nStack 作為 FILO 的資料結構，先進入的資料會放在底層\n而當有兩個 Stack，FILO，再一次 FILO (此時位於底層的資料，就會被拿上來到頂層)，\n總合起來就會得到 FIFO 了！\n研究別人的解法 這裡我研究的解法是：Share my python solution (32ms)\nclass MyQueue: def __init__(self): self.inStack = [] self.outStack = [] def push(self, x): while self.outStack: self.inStack.append(self.outStack.pop()) self.outStack.append(x) while self.inStack: self.outStack.append(self.inStack.pop()) def pop(self): return self.outStack.pop() def peek(self): return self.outStack[-1] def empty(self): return (not self.outStack and not self.inStack) # Your MyQueue object will be instantiated and called as such # obj = MyQueue() # obj.push(x) # param_2 = obj.pop() # param_3 = obj.peek() # param_4 = obj.empty() 初始化就是兩個 Stack，分成 in, out 兩組，\n「記得我們的大概念就是要兩個 FILO + FILO = FIFO」\ninit：初始化兩組 inStack, outStack push：放一個資料進入 inStack pop：從 outStack 拿出一筆資料，在這之前應該會需要經過把 inStack 的資料全部以 FILO 放入 outStack peek：與 pop 類似，但 pop 會需要回傳並刪除 Queue 中的內容，而 peek 只是我們想看看即將被 pop 的項目是什麼，而不做 pop 的動作。 empty：我們需要同時檢查 inStack, outStack 是否為空 移動過程：作為移動 inStack 的資料內容至 outStack 的內容使用，這裡有個重點「outStack此時必須為空」，因為我們可以將每次的 move 視為一組，而當這組的 Queue 順序順利清空前，「不能」再放新資料進去，否則會造成順序錯誤。\n圖例 下圖我們做的步驟是\n先放入 123 準備拿出 123，但沒一次全拿 放入 456 準備繼續拿出數字，這時我們會發現不能先移動 456，順序會亂掉， 需要等待前一組「123」全部都移除完才能夠進行再次移動下一組的動作。 Reference Share my python solution (32ms) ","date":"2022-03-09T14:47:36+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/img_0271.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-232/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [232] Implement Queue using Stacks 個人筆記"},{"categories":["413 - Git 遠端互動"],"content":"前言 這個是 Git 系列的第三篇，\n卻也是我們第一次才把本地的東西推上雲端的第一篇，\n其實真的要做 Git，或說是版本控制 「不一定要推上雲端」，強烈建議要有這個概念，\n「雲端」對我們來說只是「異地備份」的概念，而不是我們拿來「版本控制」的地方\n推上雲端的事前設定 這系列的筆記我應該會再找一天來好好重新整理，\n先把目前有的內容都丟上來XD，畢竟聊勝於無XD\n第一次設定，需要告訴他你的雲端在哪 我們很興奮地去 github 建立了一個自己的 repo，\n那我們要怎麼把 local 的資料夾推上去呢?\n我們需要先讓他知道我們的雲端在哪。\n假設我們今天先去 Github 建立了一個自己的 repo，\n(這邊建議建立時不要產生 readme.md，讓他保持資料夾乾淨，\n不然要先把這檔案同步到 local 才能做後續動作)\n設定好之後，我們會得到一個雲端的位置，\n也就是上面那一串網址，後面有「.git」的部份\n其實 github 在下面都已經很貼心地告訴我們要做什麼了XD，\n不過我們還是說明一下，重要的是在「理解」自己在做什麼XD\ngit init # 初始化 git 資料夾 git add . # 這邊的 . 是表示將所有有更新過的檔案都加入 git commit # 建立一個 commit 記錄點 git remote add [origin] [https://github.com/....git] # 建立一個 origin 變數，代替後面的雲端網址，以後我們都是使用 origin 代稱後面的網址 git push origin master # 將 master branch push 到 [origin] 這個雲端位置 真正的推上雲端 - push 基本上如果 local 的版控有先學好的話，\n會知道我們推上雲端之前都應該會要有的 「commit 的定版」，\n我們在確定有「commit 定版」後，只是做把本地的檔案「複製一份」到雲端而已，\n所以推上雲端的指令通常很簡單，但背後的觀念「必須要很清楚自己在幹嘛」，\n不然後續可能禍害無限XD (甚至別人也不知道你的「不知道在幹嘛」在幹嘛 = =)\ngit push 例外處理 - fatal: The current branch master has no upstream branch. 有時候我們可能會碰到如以下的情況\nC:\\Users\\howar\\Desktop\\leetcode\u0026gt;git push fatal: The current branch master has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin master 說明一下這是什麼意思，這表示雲端那邊還「不知道你 local 有了這條 branch」，\n因此我們在做 push 的動作時，也需要「同時把這個新的 branch 推上去」，\n才能讓「雲端」跟「本地」的 git 有一樣的版本控制。\n解決方式 我們要做的事情其實也很簡單，他們已經很貼心地告訴我們該怎麼解決了，\n就是把他給你的那行複製下來，直接執行即可。\n以上面例子來說，我們就是複製：\ngit push --set-upstream origin master 把本地的 git branch 透過 \u0026ndash;set-upstream 也推上至雲端，\n即可解決雲端 branch 不同步的問題。\n註：「git push \u0026ndash;set-upstream」的指令，可以簡寫為「git push -u」，效果是一樣的。\n","date":"2022-03-08T23:59:57+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/git-remote.drawio-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-remote/git-push/","tags":["Git","初學者","遠端互動"],"title":"【Git 遠端互動 #1】git push | 第一次將 git 的內容推上 (push 上) 雲端 (此處雲端以 github 示範)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 20. Valid Parentheses\n難度 easy\n題目分類 String, Stack\n個人範例程式碼 我們先宣告一個字典，我們可以藉由字典查出對應的括弧。\nclass Solution: def isValid(self, s: str) -\u0026gt; bool: checkdict = { \u0026#34;(\u0026#34;:\u0026#34;)\u0026#34;, \u0026#34;[\u0026#34;:\u0026#34;]\u0026#34;, \u0026#34;{\u0026#34;:\u0026#34;}\u0026#34;, } stack = [] # append \u0026lt;-\u0026gt; pop(-1) #FILO Last out!!! for ele in s: if ele in checkdict.keys(): stack.append(checkdict[ele]) elif not len(stack)\u0026lt;mark\u0026gt;0 and stack.pop(-1) \u0026lt;/mark\u0026gt; ele: pass else: return False return len(stack)==0 Time Complexity O(n)\nSpace Complexity O(1)\n算法說明 這題是非常經典的 Stack 考題\n注意了：請務必自己想清楚為什麼是 Stack 而不是 Queue，以確保觀念清楚\n基本上我們就用一個 list 來當作 Stack 儲存，\npython 裡面，撇除直接使用 package，\n我們可以使用 append 與 pop 的組合，用 list 拼湊出 Queue 與 Stack\n可參考：【Python】python list 清除, 移除內容元素 remove, pop, del, clear相關用法整理 sample code (內含範例程式碼) 用 List 拼出 Queue Queue 可以由 list append + pop(0) 組合而成\n先進先出，所以我們先拿第 0 個。\n用 List 拼出 Stack Stack 可由 list append + pop(-1) 組合而成\n或是也可以用 pop()，但個人不會這樣使用的原因是因為覺得 pop(-1) 對於觀念感覺會更清楚\n因為 -1 也可以說明我們是拿最後一個，先進會後出\ncorner case 特殊情況處理 注意：就算符合中間「運算時」的規則，結束時也必須為空\nBoundary conditions/ Edge conditions 邊際情況處理 x\nReference Simple Python solution with stack ","date":"2022-03-06T21:30:19+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-20/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [20] Valid Parentheses 個人解法筆記 內含用 python List 組出 Stack, Queue 的方法整理"},{"categories":["716 - Linked List"],"content":"題目出處 83. Remove Duplicates from Sorted List\n難度 easy\n題目分類 Linked List\n個人範例程式碼 記得上述所說的，我們需要先宣告 None 作為結尾\n# Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def deleteDuplicates(self, head: Optional[ListNode]) -\u0026gt; Optional[ListNode]: cur = head while cur and cur.next: # [1,1,1] if cur.val == cur.next.val: cur.next = cur.next.next # pass one # need check many times else: cur = cur.next # duplicat 1 in index 0 moved !!! return head Time Complexity O(n)\nSpace Complexity O(1)\n算法說明 利用先前提到的 remove 方法，\n操作「cur.next cur.next.next」的時機。\npart2 remove 操作 請參考： [【Leetcode】python – [203] Remove Linked List Elements 個人解法筆記 (內含範例程式碼) 內含 Linked List remove 操作 part 2 (for 新手教學)](https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-203/) corner case 特殊情況處理 一同整理在下方「邊際情況處理」\nBoundary conditions/ Edge conditions 邊際情況處理 這題很可惜沒有一次就寫對，我只有思考到以下前三種情況：\n- [] - [1] - [1,1] - [1,1,1] 至於為什麼第四種情況會出問題? 當時我的 code 長這樣\nclass Solution: def deleteDuplicates(self, head: Optional[ListNode]) -\u0026gt; Optional[ListNode]: cur = head while cur and cur.next: # [1,1,1] if cur.val == cur.next.val: cur.next = cur.next.next # pass one # need check many times cur = cur.next # duplicat 1 in index 0 moved !!! return head 讀者可以先閱讀一下，看看能不能自己發現問題點在哪? (但我註解好像已經寫了 Orz)\n總之是以上操作會有一個問題是，每一次必做 cur = cur.next，\n當有類似 [1,1,1] 的 case 時，判斷完前兩個相同，第一個 1 就當作沒問題往下走了，\n下一步會是第三個位置，但第一三位置的重複沒有判斷到 !!!\nReference ","date":"2022-03-06T19:50:25+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-83/","tags":[],"title":"【Leetcode】python - [83] Remove Duplicates from Sorted List 個人解法筆記"},{"categories":["716 - Linked List"],"content":"題目出處 206. Reverse Linked List\n難度 easy\n題目分類 Linked List, Recursion\nLinked List reverse 反轉操作 part 3 part1 基本概念 請參考： [【Leetcode】python – [21] Merge Two Sorted Lists 個人解法筆記 (內含範例程式碼) 內含 Linked List 基本操作 (for 新手教學)](https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-21/) part2 remove 操作 請參考： [【Leetcode】python – [203] Remove Linked List Elements 個人解法筆記 (內含範例程式碼) 內含 Linked List remove 操作 part 2 (for 新手教學)](https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-203/) 這邊我們要來研究 Linked list 的 反轉操作，\n一樣的，這操作硬要說也不難，但只要思緒不夠清楚，\n一定是轉了半天轉不出來XDD 最後絕望放棄XD\n我們試著來把概念講的簡單一點吧!\n這裡作者有找到一個超棒的動畫解釋，有興趣可以看，「真的很讚的動畫」： Easy [C++/Java/Python/JavaScript] Explained+Animated 試試看講簡單一點 做反轉動作來說，最簡單我們都需要「三個紀錄點」，\n分別代表「過去」、「現在」、「未來」，\n每一個記錄點都非常重要，\n我們一定要注意「不能在過程中失去任何重要紀錄點，一旦被蓋掉就再也找不回來了!」\n因此我們基本的策略就有了下圖，1 ~ 3，\n我們移動的方式一定是：\n- 過去 = 現在 - 現在 = 未來 - 未來 = 更未來 到這邊，「務必務必謹守上述這個原則」，\n再來我們再來討論反轉，什麼時候適合做反轉呢?\n就是在做上述的「移動 1~3」之前，所以「更改」反轉的線，就是在第 0 步驟，\n初始化 這裡有個小重點，不論如何，我們的 Linklist 最終「必須結束於 None」，\n因此我們要先宣告個「新結束點」給他，\n那想當然，可以讓一開始 prev 擔任這個步驟。\n而我們真正作為起頭的 head ，可以先給予他 cur (代表現在)，\n由於此時我們還不確定有沒有 head 存在，因此還不能直接給 head.next (會跳錯)\n個人範例程式碼 記得上述所說的，我們需要先宣告 None 作為結尾\n# Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def reverseList(self, head: Optional[ListNode]) -\u0026gt; Optional[ListNode]: prev = None # must have a None end. if head: # init cur = head head = head.next cur.next = prev # must have a None end. else: # special case return head # loop while head: prev = cur cur = head head = head.next cur.next = prev # reverse return cur Time Complexity O(n)\nSpace Complexity O(1)\n算法說明 遍歷一次 head 是一定要的，問題是我們該怎麼移除，\n移除的方法我們上面也提到過了，\n只需要把「下個」替換成「下下個」就可以輕鬆解決，\n只是要多考慮一些「不存在的邊際情況」，這邊我們下面會提。\ncorner case 特殊情況處理 一同整理在下方「邊際情況處理」\nBoundary conditions/ Edge conditions 邊際情況處理 我們把題目稍微分析一下，我這裡大概把題目分類成於以下三種情況，\n- [None] - [1] - [1,2,3] \u003c- 正常情況 其中我們來看看 1.2. 我們該怎麼辦。\n1. [None] 如果一開始 if head 就沒東西，我們就直接 return head\n2. [1] 我們已經把 cur = head，而 while head 會直接判找不到東西，\n因此回傳 cur，沒有問題。\nReference Python Iterative and Recursive Solution Easy [C++/Java/Python/JavaScript] Explained+Animated ","date":"2022-03-06T19:33:52+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/img_0270.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-206/","tags":[],"title":"【Leetcode】python - [206] Reverse Linked List 個人解法筆記 內含 Linked List reverse 反轉操作 part 3 (for 新手教學)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 387. First Unique Character in a String\n難度 easy\n題目分類 Hash Table, String, Queue, Counting\n個人範例程式碼 class Solution: def firstUniqChar(self, s: str) -\u0026gt; int: worddict = Counter(s) for idx, ele in enumerate(s): if worddict[ele] \u0026gt;= 2: # has duplicate pass else: # no duplicate return idx return -1 Time Complexity O(n)\nSpace Complexity x\n算法說明 透過 Counter 快速完成計算數量，\n只要 Counter 計算出數量大於 2 的結果，\n直接回傳最早的答案。\ncorner case 特殊情況處理 x\nBoundary conditions/ Edge conditions 邊際情況處理 沒有答案時，要回傳 -1\nReference ","date":"2022-03-05T18:21:11+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-387/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [387] First Unique Character in a String 個人解法筆記"},{"categories":["716 - Linked List"],"content":"題目出處 203. Remove Linked List Elements\n難度 easy\n題目分類 Linked List, Recursion\nLinked List remove 操作 part 2 part1 請參考： [【Leetcode】python – [21] Merge Two Sorted Lists 個人解法筆記 (內含範例程式碼) 內含 Linked List 基本操作 (for 新手教學)](https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-21/) 這邊我們要來研究 Linked list 的 remove，\n說 remove 其實也不難，最麻煩的應該是要考慮的邊際情況很多，\n大觀念來說，我們只要「把下一個 替換成 下下一個」，就可以順利完成，\n這觀念聽起來就超簡單的XDDD\n但問題就是在要處理一些特殊狀況\n這個我們在後續的「邊際問題處理」再來提\n個人範例程式碼 我們會習慣宣告假開頭為 dummy，並給予任意值 (這裡給 -1)，\n反正我們要用的是 dummy.next 之後的東西，\n就當是一種解題習慣吧。\n# Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def removeElements(self, head: Optional[ListNode], val: int) -\u0026gt; Optional[ListNode]: dummy = cur = ListNode(-1) cur.next = head # start from head, if wrong will pass later while cur.next: # head exist, we need to parse all Linkedlist if cur.next.val == val: # check next cur.next = cur.next.next # pass next (remove) else: cur = cur.next # save one ans head = head.next # move return dummy.next Time Complexity O(n)\nSpace Complexity O(1)\n算法說明 遍歷一次 head 是一定要的，問題是我們該怎麼移除，\n移除的方法我們上面也提到過了，\n只需要把「下個」替換成「下下個」就可以輕鬆解決，\n只是要多考慮一些「不存在的邊際情況」，這邊我們下面會提。\ncorner case 特殊情況處理 一同整理在下方「邊際情況處理」\nBoundary conditions/ Edge conditions 邊際情況處理 這題問題本身不難，麻煩的是在考慮的邊際情況很多，\n這些問題是我自己想的，「一定有些對於讀者來說是可以不用思考的問題」，\n總之只要考慮周全就是好方法，不一定要照我的XD\n0. (一般情況) 在中間，要移除 沒什麼問題，當 cur.next = cur.next.next 我們就會看到下一個不是的了\n-\u0026gt; 我們觀察的值為 (cur.next).val\n1. 如果你要移除的，已經是最後一個? 當 cur.next = cur.next.next 我們就會看到 None 了\n-\u0026gt; 我們觀察的值為 (cur.next).val\n注意：這裡會有個問題可以思考，我們該檢查有沒有值得是 cur.next 還是 cur.next.next?\n答案會是 cur.next (也代表指向 this 的 pointer，至於如果是 cur.next.next，可能會因為 cur.next 已經是 None 而跳錯。)\n2. 如果最後連續 N 個，都是要移除的? 當 cur.next = cur.next.next ，不斷替換 cur.next 我們遲早就會看到 None 了\n-\u0026gt; 我們觀察的值為 (cur.next).val\n3. 如果全部都是要移除的? 當 cur.next = cur.next.next 直到我們看到 None，而我們要確保「cur 與 cur 之前的答案都是可行的」\n-\u0026gt; 我們觀察的值為 (cur.next).val\n4. 如果沒有值? cur.next 為 None 我們會發現。\n5. 如果開頭就是要移除的? 我們的開頭設為「cur.next = head」，也就是說我們只希望讓 cur 之後的答案為正確的。\nReference Simple Python solution with explanation (single pointer, dummy head). Simple \u0026amp; clear python solution ","date":"2022-03-05T15:36:05+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-203/","tags":[],"title":"【Leetcode】python - [203] Remove Linked List Elements 個人解法筆記 內含 Linked List remove 操作 part 2 (for 新手教學)"},{"categories":["716 - Linked List","Two pointers (N pointers merge 多項合併)"],"content":"題目出處 21. Merge Two Sorted Lists\n難度 easy\n題目分類 Linked List, Recursion\nLinked List 操作 (for 新手教學) 新手如我，最不熟悉 Linked List 的操作，剛好網路上有其他大神分享，\n我們來簡單的重新敘述一下他教學的內容：\n參考：Python3 Solution with a Detailed Explanation - dummy explained\npointer 跟著移動? 假設我們定義一個 head = temp = ListNode(0)，\n(這樣寫太難懂的話，請理解為 temp = ListNode(0)， head = temp)\n在 Linked List 操作中可能最讓人困惑的就是，當我們去移動 temp 時，head 會不會跟著移動?\n在 python 裡面，我們進行 temp = temp.next 的動作，\n實際上我們只有移動 「temp 指向的位置」，我們可以理解「temp.next」在此只是一個「位置的代稱」，\n就像我們說\nb = 1\na = b\nb = 2\n最後會知道 a = 1, b = 2 而不是 a, b = 2，\n不要像作者我在一開始因為開始有點複雜，就被搞亂了。\n好啊，那 pointer 不移動，為啥 temp 去改能影響到 head? head 一開始指到的記憶體位置與 temp 是相同的，\n而我們控制 temp 在此點的指向，因此 head 確實可以從這個記憶體位置往下找。\n比喻 我們可以想像我們現在正在挖金礦，我們順著 線索 1 -\u0026gt; 2 -\u0026gt; 3 不斷往前，\n但我們如果要告訴後來的人這條路該怎麼走，我們是必要紀錄起始點的位置。\n因此這裡就會分為 head, temp， head 幫助我們保留起始位置，而 temp 負責幫我們移動。\n中間的每一個線索都已經由 temp 在過程中建立好正確連結，\n因此 head 尋著 temp 打通的道路再走一次，就是走 temp 開過的路。\nListNode(0)? return head.next 不用想那麼多，這只是建立一個供我們參考的基準點。\n從我們最後 return head.next 就知道，我們只在乎這個基準點之後的事情。\n個人範例程式碼 # Definition for singly-linked list. # class ListNode # def __init__(self, val=0, next=None) # self.val = val # self.next = next class Solution: def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -\u0026gt; Optional[ListNode]: head = temp = ListNode(0) while list1 and list2: if list1.val \u0026lt; list2.val: temp.next = list1 # ans next Node list1 = list1.next else: temp.next = list2 # ans next Node list2 = list2.next # move next temp = temp.next # ans move next temp.next = list1 or list2 # remain things return head.next Time Complexity O(m+n)\nSpace Complexity O(1)\n算法說明 如上面介紹所述\ncorner case 特殊情況處理 注意沒有值的情況，例如開頭就沒有的時候\nBoundary conditions/ Edge conditions 邊際情況處理 終止條件 注意沒有值的情況，\n我們大原則是每次都會檢查「this Node 是否為 None」再開始接續動作。\n提取剩餘內容 另外最後的 list1 or list2 是個非常漂亮的做法，\n可以快速把剩餘的內容提取出來。\nReference Python solutions (iteratively, recursively, iteratively in-place). Simple 5 lines Python Python3 Solution with a Detailed Explanation - dummy explained ","date":"2022-03-05T14:31:19+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-21/","tags":["merge"],"title":"【Leetcode】python - [21] Merge Two Sorted Lists 個人解法筆記 內含 Linked List 基本操作 (for 新手教學)"},{"categories":["716 - Linked List","Two pointers (同向雙指針 →→) / slow, fast (快慢雙指針)"],"content":"題目出處 141. Linked List Cycle\n難度 easy\n題目分類 Hash Table, Linked List, Two Pointers\n個人範例程式碼 class Solution: def hasCycle(self, head: Optional[ListNode]) -\u0026gt; bool: slow = fast = head # while slow.next and fast.next.next: # check exist # while fast.next and fast.next.next: # check exist while fast and fast.next: # check exist slow = slow.next fast = fast.next.next if slow is fast: # has cycle return True return False # no cycle Time Complexity x\nSpace Complexity x\n算法說明 這題考 Linked List 的遍歷操作，\n最麻煩的應該是邊界控制，這我們下面再提。\nFloyd Cycle Detection Algorithm, Tortoise and Hare Algorithm (龜兔賽跑演算法) 演算法使用的是「Floyd Cycle Detection Algorithm, Tortoise and Hare Algorithm (龜兔賽跑演算法)」\n或簡稱 Floyd Cycle 之類的，簡單來說就是當一個東西有環，\n我們放一個跑比較快(step2)的兔子跟跑比較慢(step1)的烏龜，\n兔子跟烏龜在有環的情況下，它們遲早會相會。\ncorner case 特殊情況處理 要處理沒有東西 [] 的情況\u0026hellip; 被搞到\nBoundary conditions/ Edge conditions 邊際情況處理 這題我犯了兩次錯誤，\n第一次我寫 while slow.next and fast.next.next:\n會跳錯，因為 slow 並不重要，fast 跑在前都會先檢查過了，\n當會跳錯的地方在於「fast.next」不存在。\n第二次我寫 while fast.next and fast.next.next:\n還是錯，原因是這樣沒有處理到當 「fast」 不存在的情況。\n第三次 while fast and fast.next 就是正確的了，\n我們可以保證 fast 與 fast.next 必存在，\n而 fast.next 可以指向 None，\n當 fast = fast.next.next 時，我們會在下一個迴圈把 None 判出來。\n補充 - 系統思維加速 (面試題) 也可以用 hashset() 的方式來實作，hash pointer。\nReference AC Python 76ms Floyd loop detection in 7 lines Except-ionally fast Python ","date":"2022-03-05T13:04:23+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/linked-list/leetcode-python-141/","tags":[],"title":"【Leetcode】python - [141] Linked List Cycle 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 74. Search a 2D Matrix\n難度 medium\n題目分類 Array, Binary Search, Matrix\n個人範例程式碼 class Solution: def searchMatrix(self, matrix: List[List[int]], target: int) -\u0026gt; bool: m = len(matrix) n = len(matrix[0]) l = 0 # left r = m*n-1 # right # matrix[idx//n][idx%n] while(l \u0026lt;= r): mid = (l+r)//2 if matrix[mid//n][mid%n] == target: # found return True elif matrix[mid//n][mid%n] \u0026lt; target: # mid smaller than target l = mid+1 else: # mid bigger smaller than target r = mid-1 return False Time Complexity O(logN)\nSpace Complexity O(1)\n算法說明 「查詢」的重要概念 這題帶出了「查詢的重要觀念」，\n查詢這件事情基本上的時間複查度，\n通常一般會是：\nO(n): 每一個都看過 O(1): 有 hash map，用空間換取時間 這裡要來講一個特別的情況，當我們發現這個要查找的內容「有序或有規律」可尋，\n也就是說，只要他「不是完全雜亂的」，\n我們可以嘗試使用 binary search = O(logN)\n這題目很明顯已經排好順序了，我們可以每次很輕鬆的進行對半切的動作，\n這樣的概念就是「binary search」，之後在 tree 會更常使用。\n處理成一維陣列 我們處理的重點就是在 binary search，\n我們直接把這個矩陣當一個一維陣列來看，轉換方式為：\nm = len(matrix)\nn = len(matrix[0])\n而 matrix[idx//n][idx%n] 的 idx 就可以代表我們想要的位置。\n尋找過程 l = 0 # left\nr = m*n-1 # right\n我們找 mid = (l + r) //2\n當 matrix[mid] target， 找到了! 當 matrix[mid] \u0026gt; target，我們比目標大，mid - 1 當 matrix[mid] \u0026lt; target， 我們還是比目標小，mid + 1 然後重複濃縮 mid 的範圍\n直到 l \u0026gt;= r (因為一直有 +1 -1的操作)，遲早會重疊。\ncorner case 特殊情況處理 當只有一行時，需要注意處理 idx+1，idx-1 都會出錯\nBoundary conditions/ Edge conditions 邊際情況處理 計算到 l \u0026gt;= r (因為一直有 +1 -1的操作)，遲早會重疊。\nReference Don\u0026rsquo;t treat it as a 2D matrix, just treat it as a sorted list ","date":"2022-03-05T12:33:26+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-74/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [74] Search a 2D Matrix 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 383. Ransom Note\n難度 easy\n題目分類 Hash table, string, counting\n個人範例程式碼 class Solution: def canConstruct(self, ransomNote: str, magazine: str) -\u0026gt; bool: ran_dict = Counter(ransomNote) mag_dict = Counter(magazine) for idx, ele in enumerate(ran_dict.keys()): if ele not in mag_dict: return False # not found elif ran_dict[ele] \u0026gt; mag_dict[ele]: return False # not enough else: pass return True Time Complexity O(n)\nSpace Complexity x\n算法說明 這題本身不難，還可以體驗一下 python Counter 的強大之處。\n練習一下 string 怎麼 parse，for char in string，\n即可「一個一個 char」的把「string」內容一個一個字提取出來。\n此外，python collection 內建的 Counter function，\n快速幫我們把計算數量的 hash dictionary 完成了。\n如果還不知道 python Counter 用法的，「強烈建議」一定要會，寫程式效率會快超級多！！！\n可參考我的另外一篇文：\n【Python】python counter() 用法整理 – 快速計算資料內容的數量\n我們就不用再另外寫 hash table 做 count 的動作了。\ncorner case 特殊情況處理 處理「不存在」值的情況 處理「不足夠」值的情況 Boundary conditions/ Edge conditions 邊際情況處理 x\nReference ","date":"2022-03-04T23:10:50+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-383/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [383] Ransom Note 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 36. Valid Sudoku\n難度 Medium\n題目分類 array, hash-table, matrix\n個人範例程式碼 class Solution: def isValidSudoku(self, board: List[List[str]]) -\u0026gt; bool: # Each row must contain the digits 1-9 without repetition. for each_row in board: if self.checknums(each_row): pass else: return False # early return # Each column must contain the digits 1-9 without repetition. transpose_board = list(zip(*board)) # unpack list, then rezip by column for each_row in transpose_board: if self.checknums(each_row): pass else: return False # early return # Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. for y_idx in range(0, 9, 3): for x_idx in range(0, 9, 3): collection = self.getsubbox(board, x_idx, y_idx) if self.checknums(collection): pass else: return False # early return return True def getsubbox(self, board, start_x ,start_y): collection = [] for y_idx in range(3): for x_idx in range(3): collection.append(board[start_y+y_idx][start_x+x_idx]) return collection def checknums(self, nums: List[str]) -\u0026gt; bool: keep = set() for ele in nums: if ele == \u0026#34;.\u0026#34;: pass elif ele in keep: return False else: keep.add(ele) return True Time Complexity x\n算法說明 這題，基本上就是條件多了一點，\n要比較有耐心的處理，並且把指定的項目都拿出來，\n除此之外難度不高。\n要有耐心處理條件反而是我覺得這題難的地方。\n我的算法基本上就依照題目敘述，\n先建立一個檢查的函數，之後我們都以「9個位置為單位」傳入，\n檢查出結果並回傳。\n比較麻煩的大概是小的 sub-box，這裡要有點耐心處理。\n幾個小細節可以注意：\n「轉置矩陣」好用技巧：用「*」拆開 list後，再用「zip」重組。 簡單來說，想快速得到轉置矩陣(像這題我們為了把 column 拉出來)，\n我們可以用「*」拆開 list後，再用「zip」重組。\na = [[1,2],[3,4]] print(*a) # [1,2],[3,4] \u0026lt;- 拆開 list print(list(zip(*a))) # [[1,3],[2,4]] \u0026lt;- 拆開 list，zip 重組，從 zip 轉 list 格式 詳情可以看我的另外一篇：\n【Python】python zip 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code early return：只要發現不合規則，即可以 return。 相對於此方法的就是建立一個 flag，過程中全檢查，最後才回傳這個 flag，但因為我們早知道結果，不需要全部組合都檢查才 return。\n搜尋請用 set/ dict 而不要用 list：set/dict 的查找速度為 O(1)，list 為 O(n)。 簡單來說，只要單純是做搜尋功能的，建議都用 set/dict，查找速度為 O(1)，\n有些人 list 因為太方便用習慣了，但 list 查詢速度為 O(n)，可能會浪費很多時間在查找上。\n一提再提的重點：只要有「找重複」的事情，幾乎必使用 set() 這題換句話說就是「找有沒有重複數字」，這部分應該會使用到 set。\nset 在「找尋重複項目」可以發揮最快的時間，幾乎是「找重複問題」必用。\ncorner case 特殊情況處理 x\nBoundary conditions/ Edge conditions 邊際情況處理 x\n討論區看到的一些酷解法 - 1 這個作者很聰明的，把在 sudoku 出現的每一個元素都弄成一個 set，\n依據規則總共有三種：\nEach row must contain the digits 1-9 without repetition. 單一元素建立 set (出現過的數字, y) Each column must contain the digits 1-9 without repetition. 單一元素建立 set (x, 出現過的數字) Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. 單一元素建立 set (x//3, y//3, 出現過的數字) 這個解法漂亮的地方在於，[0,1,2,3,4,5,6,7,8] 經過 // 3 後，會變成 [0,0,0,1,1,1,2,2,2]，\n因此我們就可以比較重複元素了！\n可以參考：\n1-7 lines Python, 4 solutions 範例程式碼 看完解答後我自己也照他的概念重寫了自己的，真的快又準確，好猛\u0026hellip;\nclass Solution: def isValidSudoku(self, board: List[List[str]]) -\u0026gt; bool: # Each row must contain the digits 1-9 without repetition. rule1_set = set() # Each column must contain the digits 1-9 without repetition. rule2_set = set() # Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. rule3_set = set() for y, each_row in enumerate(board): for x, ele in enumerate(each_row): if ele != \u0026#39;.\u0026#39;: rule1_ele = (ele, y) rule2_ele = (x, ele) rule3_ele = (x//3, y//3, ele) if rule1_ele in rule1_set: return False else: rule1_set.add(rule1_ele) if rule2_ele in rule2_set: return False else: rule2_set.add(rule2_ele) if rule3_ele in rule3_set: return False else: rule3_set.add(rule3_ele) return True corner case 特殊情況處理 注意要處理 “.”，不然會以為已經出現過了。\nBoundary conditions/ Edge conditions 邊際情況處理 x\n討論區看到的一些酷解法 - 2 與上面酷解法差不多，這邊只是想強調比對時，可以用\nif len(list) == len(set(list)): # 兩者相等，表示 set 未刪去東西 (無重複項) else: # 兩者不相等，表示 set 有刪去東西 (有重複項) 注意：不可以比較「 list = set(list))」，因為「資料型態不同」，必定為 False\n可以參考：\nMy 12 lines C/C++ solution with 1 time traversal and 9x9x3 memory 1-7 lines Python, 4 solutions 範例程式碼 class Solution: def isValidSudoku(self, board: List[List[str]]) -\u0026gt; bool: # Each row must contain the digits 1-9 without repetition. rule1_list = [] # Each column must contain the digits 1-9 without repetition. rule2_list = [] # Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. rule3_list = [] for y, each_row in enumerate(board): for x, ele in enumerate(each_row): if ele != \u0026#39;.\u0026#39;: rule1_ele = (ele, y) rule2_ele = (x, ele) rule3_ele = (x//3, y//3, ele) rule1_list.append(rule1_ele) rule2_list.append(rule2_ele) rule3_list.append(rule3_ele) return len(set(rule1_list)) == len(rule1_list) and \\ len(set(rule2_list)) == len(rule2_list) and \\ len(set(rule3_list)) == len(rule3_list) # if no duplicate in list, will return True corner case 特殊情況處理 注意要處理 “.”，不然會以為已經出現過了。\nBoundary conditions/ Edge conditions 邊際情況處理 x\nReference My short solution by C++. O(n2) A readable Python solution My 12 lines C/C++ solution with 1 time traversal and 9x9x3 memory 1-7 lines Python, 4 solutions ","date":"2022-03-04T02:44:27+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-36/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [36] Valid Sudoku 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 136. Single Number\n難度 Easy\n題目分類 hash-table, bit-manipulation\n個人範例程式碼 class Solution: def singleNumber(self, nums: List[int]) -\u0026gt; int: ans = 0 for ele in nums: ans ^= ele return ans Time Complexity O(n)\n算法說明 這題算是經典題目，因為經典做法太神奇了，\n這題考的概念可以用 XOR 漂亮的解出來。\npython 裡面，XOR 的符號表示做 「^」，\n我們運用以下特性，同一個東西重複 XOR 兩次則變回原樣，\n就可以在最小時間與空間解決此問題。\n0 ^ a = a a ^ a = 0 b ^ a ^ a = b corner case 特殊情況處理 x\nBoundary conditions/ Edge conditions 邊際情況處理 注意初始值應該為「0」而不是「1」\nReference C++ EASY SOLUTIONS - (SORTING , XOR , MAPS (OR FREQUENCY ARRAY)) ","date":"2022-03-03T21:38:20+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-136/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [136] Single Number 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 118. Pascal\u0026rsquo;s Triangle\n難度 easy\n題目分類 array\n個人範例程式碼 class Solution: def generate(self, numRows: int) -\u0026gt; List[List[int]]: ans = [[1]] if numRows == 0: return [[]] else: layer = 1 # start from layer 2 while layer \u0026lt; numRows: this_layer = self.generate_layer(ans[len(ans)-1]) ans.append(this_layer) layer+=1 return ans def generate_layer(self, last_layer): this_layer = [] for idx, ele in enumerate(last_layer): if idx == 0: this_layer.append(1) else: this_layer.append(last_layer[idx-1]+last_layer[idx]) this_layer.append(1) return this_layer Time Complexity O(n^2)\nSpace Complexity O(n^2) = 剛好題目要求\n算法說明 這題很直覺，我是直接用類似「dp(n) = dp(n-1) + k」的方式做出來的，\n因為「上一層」與「下一層」有強烈的相關性。\n算法簡單來說是：\n先產生下層的第一位數字 1，\n第二位開始，都是由上一層的 n-1, n 組成\n最後補上一個 1\n圖示如下 - 先產生 1 1 3 3 1 1 - 上一層 1+3, 3+3, 3+1 填入 1 3 3 1 1 4 6 4 - 補上最後一個1 1 3 3 1 1 4 6 4 1 corner case 特殊情況處理 雖然題目有說到 「1 \u0026lt;= numRows \u0026lt;= 30」，\n但我把 0, 1 的情形都考慮了進去\nBoundary conditions/ Edge conditions 邊際情況處理 傳入上層 layer 時，我是以目前 ans 的 len 大小判斷，\n上一層 layer 的「index 位置」會是 「目前 len(ans) - 1」，\n而不是「目前 len(ans) - 2」，因為目前這層尚未新增。\n特殊酷解法 這是我在 leetcode 討論區看到的很酷的解法，\n這邊紀錄一下\n連結：\nPython 4 lines short solution using map. 大概念就是，下一層的 layer 可以視為「上層補 0 與 此反轉的總和」，\n文字敘述太難懂了，不如我們直接看圖示。\n0 1 3 3 1 + 1 3 3 1 0 = 1 4 6 4 1 一看圖示就懂了，真的太強了這個 Orz！！！\n可以寫出來的程式碼也很簡潔：\n(這邊保留註解的部分是留給我自己看的，下面會說明，實際上 2, 3 選一種即可)\nclass Solution: def generate(self, numRows: int) -\u0026gt; List[List[int]]: ans = [[1]] for _ in range(numRows - 1): # we already have layer 1, do \u0026#34;numRows-1\u0026#34; times. tmp = ans[-1] + [0] # this_layer = tmp + tmp[::-1] # wrong! will be 1,0,0,1 # print(this_layer) this_layer2 = [x + y for x, y in zip(tmp, tmp[::-1])] # add reverse # print(this_layer2) # this_layer3 = list(map(lambda x, y: x+y, tmp, tmp[::-1])) # add reverse # print(this_layer3) ans.append(this_layer2) return ans 這裡我們會碰到一個陷阱與問題，\n就是 python 的 list 該如何相加？\n注意：python 裡面 list 的「+」，是相當於「把內容往後堆」，\n而非「兩者相加」。\npython list add 加法 (sum of two list) 主要有兩種流派，map 流與 zip 流。\n(官方文件寫 map 的方法)\n這邊我先自己寫一次看看\nzip 做 list sum 只需要注意 zip 出來的資料型態是 zip，\n需要用 list 再轉一次格式。\n可參考：\n【Python】python zip 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code a = [1,2] b = [3,4] sum = [x + y for x,y in zip(a,b)] print(sum) # or you can use sum2 = list(x + y for x,y in zip(a,b)) print(sum2) map 做 list sum 一樣，需要注意 map 出來的資料型態是 map，\n需要用 list 再轉一次格式。\n可參考：\n【Python】python map 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code a = [1,2] b = [3,4] sum = list(map(lambda x, y: x + y, a, b)) print(sum) 這裡注意一下，我們不能直接用上面 zip 中的 [] 方式直接轉，\n會顯示 [map object]，不符合我們的預期。\nReference Add SUM of values of two LISTS into new LIST Python 4 lines short solution using map. 【Python】python zip 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code 【Python】python map 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code ","date":"2022-03-03T14:32:47+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/%E6%88%AA%E5%9C%96-2022-03-03-%E4%B8%8B%E5%8D%883.33.27.png","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-118/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [118] Pascal's Triangle 個人解法筆記 — sum of two list (list add 相加方法整理)"},{"categories":["710 - Python LeetCode"],"content":"題目出處 189. Rotate Array\n難度 Medium\n題目分類 array, math, two pointers\n個人範例程式碼 class Solution: def rotate(self, nums: List[int], k: int) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums in-place instead. \u0026#34;\u0026#34;\u0026#34; len_nums = must_visited = len(nums) start_idx = 0 while(must_visited != 0): i = start_idx prev = nums[start_idx] while(True): next_i = (i+k)%len_nums save = nums[next_i] # save next nums[next_i] = prev # use prev to change next prev = save # next give to keep i = next_i # move to next index must_visited -= 1 # prevent for len_nums can divide k if i == start_idx: # return to start break start_idx += 1 # prevent for len_nums can divide k Time Complexity O(n)\nSpace Complexity O(1) # 這題有特別要求希望是這個空間\n算法說明 題目要求空間 O(1)，通常就表示這個題目「高機率」可以用 swap 的方式解出來 (想辦法只用交換的) 題目提到「in-place」，表示這題目可以用 swap 的方法解的「可能性很高」，不過這個提示沒有上方機率那麼高，有時候我們也是可以另外 assign 空間再去進行「in-place」的動作。\n方法一 - circular array 直覺上，我們可以將此 array 當作一個環，不斷的交換一個循環，直到回到起點結束交換。\n為了方便起見，以下的例子我們故意將 index 與裡面的元素值取相同。\n我們可以觀察，k=2 時，我們下一個移動的目標 next_i = (i+k)%(array長度) \u0026lt;- 取餘數\n我們再次觀察，k=3 時，我們下一個移動的目標 next_i = (i+k)%(array長度) \u0026lt;- 取餘數\n注意!! 此方法有個陷阱!! 當「矩陣的長度可以被 k 整除時，會有快速循環的現象」，\n例如「len(nums) = 4, k = 2」\n[0,1,2,3]\n如果只照上述循環後，我們就只會變更 [2,1,0,3] \u0026lt;- 注意只有 0, 2 交換。\n為了避免此問題，我們需要另外設計一個 visited 的機制，確保每一個位置都有換過!!\n範例程式碼 class Solution: def rotate(self, nums: List[int], k: int) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums in-place instead. \u0026#34;\u0026#34;\u0026#34; len_nums = must_visited = len(nums) start_idx = 0 while(must_visited != 0): i = start_idx prev = nums[start_idx] while(True): next_i = (i+k)%len_nums save = nums[next_i] # save next nums[next_i] = prev # use prev to change next prev = save # next give to keep i = next_i # move to next index must_visited -= 1 # prevent for len_nums can divide k if i == start_idx: # return to start break start_idx += 1 # prevent for len_nums can divide k corner case 特殊情況處理 處理當 len(nums) = 0 或 k 的情況，基本上都不用做事情，可以節省時間。\n上述範例沒有做，但程式碼已經可以處理這個情況。\nBoundary conditions/ Edge conditions 邊際情況處理 結束位置在，當 index 返回出發點時。 但要讓整個交換完成，需要注意是否有整除問題，所以真正結束是在 visited 剛好等於 array 長度時， 期間我們需要去變換起始的 index 位置。 方法二 - recursive 這個方法我也是參考 discussion 看到的，非常酷的做法!\n他運用的概念是「矩陣反轉兩次後，會回到原位置」。\n因此如果在第二步驟，我們依照「目標 k 位置」進行「分別反轉」，\n就可以得到答案。\n舉例：k = 3，以下為了方便起見，直接讓 index 值等於元素值\n[0,1,2,3,4,5,6] \u0026lt;- 原始位置\n[6,5,4,3,2,1,0] \u0026lt;- 第一次 全反轉\n[4,5,6,0,1,2,3] \u0026lt;- 拆兩次反轉 0k-1, klen(nums)-1\n範例程式碼 class Solution: def rotate(self, nums: List[int], k: int) -\u0026gt; None: \u0026#34;\u0026#34;\u0026#34; Do not return anything, modify nums in-place instead. \u0026#34;\u0026#34;\u0026#34; if len(nums) \u0026lt;= 1: return else: k = k % len(nums) self.reverse(nums, 0, len(nums)-1) self.reverse(nums, 0, k-1) self.reverse(nums, k, len(nums)-1) return def reverse(self, nums, start, end): while start \u0026lt; end: nums[start], nums[end] = nums[end], nums[start] start += 1 end -= 1 corner case 特殊情況處理 處理當 len(nums) = 0 或 k 的情況，基本上都不用做事情，可以節省時間。 上述範例沒有做，但程式碼已經可以處理這個情況。\n當 0 or k 時，就是 list 進行全部反轉，再反轉一次回來的意思。\n處理當 len(nums) = 1 的情況，會超出範圍 新增以下片段\nif len(nums) \u0026lt;= 1: return 處理當 k \u0026gt; len(nums) 的情況，會超出範圍 這個就是考這題要細心的地方，因為題目沒說 k 必小於 len(nums)，\n通常會沒考慮到這點，也是要看到測資才會想到\u0026hellip;\n新增以下片段\nk = k % len(nums) Boundary conditions/ Edge conditions 邊際情況處理 k=3 時，\n前半段位置從 0 到 2 = (k-1) 後半段則是 k 開始至 len(nums)-1 Reference 4 solutions in python (From easy to hard) My solution by using Python ","date":"2022-03-02T21:57:30+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/img_0266.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-189/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [189] Rotate Array 個人解法筆記"},{"categories":["710 - Python LeetCode"],"content":"題目出處 566. Reshape the Matrix\n難度 Easy\n題目分類 array\n個人範例程式碼 class Solution: def matrixReshape(self, mat: List[List[int]], r: int, c: int) -\u0026gt; List[List[int]]: m, n = len(mat), len(mat[0]) total = m*n if total != r*c : return mat else: ans = [[0] * c for _ in range(r)] # count to c, then add one r for idx in range(total): # origin matrix = [idx//n][idx%n] # which y, and which x # notice: [idx//n][idx%n] not m,n # after matrix = [idx//n][idx%c] # which y, and which x # notice: [idx//c][idx%c] not r,c (r -\u0026gt; auto add axis) ans[idx//c][idx%c] = mat[idx//n][idx%n] return ans Time Complexity O(mn)\n算法說明 先判斷是否元素數量相等，\n再來去進行移動動作，用 index 去巧妙控制他。\ncorner case 特殊情況處理 元素數量不相等時，直接回傳原來的答案。\nBoundary conditions/ Edge conditions 邊際情況處理 這題可以練習 array 初始化與位置操作相關的技巧，\narray 初始化 二維陣列的初始化，\n我們可以記憶：\n從第一維的 [0] 開始複製，「複製 c 次」完成第一維， 這個動作要「重複做 r 次」，完成複製 r 維。 ans = [[0] * c for _ in range(r)] # count to c, then add one r 元素 index 操作與控制 這裡有個元素 index 操作的技巧，\n當我們已經知道所有元素的內容皆會被使用，\n而我們只是要單純控制輸出方式時，\n像這題中，我們可以發現邏輯上，\n我們都是「先完成第一維後，再新增一維」\n以這題來說，有另外一個 index 操作技巧，\n我們先有「完成一層後，才新增一層」的概念，\n因此我們可以很容易用「%」、「//」取餘數、整除，\n來獲得相對應的位置。\nfor idx in range(total): # origin matrix = [idx//n][idx%n] # which y, and which x # notice: [idx//n][idx%n] not m,n # after matrix = [idx//c][idx%c] # which y, and which x # notice: [idx//c][idx%c] not r,c (r -\u0026gt; auto add axis) ans[idx//c][idx%c] = mat[idx//n][idx%n] 特別注意：取的座標為 「//n」、「%n」，而非 m, n，\n因為我們真正「決定是否換行、與現在座標餘數」的，都只有 n 。\nReference [Python] One pass - Clean \u0026amp; Concise ","date":"2022-03-02T16:06:37+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-566/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [566] Reshape the Matrix 個人解法筆記 內含 array 初始化, index 操作與控制範例"},{"categories":["710 - Python LeetCode"],"content":"題目出處 weekly-contest-282 2186. Minimum Number of Steps to Make Two Strings Anagram II\n難度 Medium\n題目分類 個人範例程式碼 class Solution: def minSteps(self, s: str, t: str) -\u0026gt; int: ans = len(s) + len(t) for same_word in (set(s) \u0026amp; set(t)): ans -= min(Counter(s)[same_word], Counter(t)[same_word])*2 # two side skip step return ans Time Complexity O(n)\n算法說明 最大步數為 len(s) + len(t)，我們再去看要減少多少就好。\n統計每個字元後，用 set 取「交集」，找出重複的字元數量\ncorner case 特殊情況處理 x\nBoundary conditions/ Edge conditions 邊際情況處理 注意：因為兩組字母是雙向的減少步驟，因此在減少步數時「需要*2」 Reference 【Python】python counter() 用法整理 – 快速計算資料內容的數量 ","date":"2022-03-02T12:06:40+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/python-leetcode/leetcode-python-2186/","tags":["LeetCode","Python"],"title":"【Leetcode】python - [2186] Minimum Number of Steps to Make Two Strings Anagram II 個人解法筆記"},{"categories":["111 - Python 基礎語法"],"content":"前言 這篇我們要來研究 python set 使用方法，\n在解 leetcode 相關的題目的時候，\n很常會有需要「找重複內容」的時候，這時 set() 就能發揮很大的功能。\n初始化 能初始化建立 set 的方式有很多種，\n我們在解 leetcode 題目的時候，\n最常使用把「題目的一個資料型別」直接轉為「set」的格式。\n過程中，我們就會順便把「重複的內容濾掉」，\n這樣就可以解很多處理重複的問題。\nmyset = set() myset = {1,2,3} myset = set( [ 1, 2, 3 ] ) # list -\u0026gt; set myset = set( ( 1, 2, 3 ) ) # tuple -\u0026gt; set # set = {} # 注意：此為常見錯誤示範！ 特別注意：這裡有個常見錯誤是 set = 「{}」 並不會成功宣告空 set，而是要使用 「set()」\n應用 說到 set 最經常的應用，就是拿來取「交集」、「聯集」、「差集」了！\n這些幾乎都是我們在處理重複性問題時會需要的東西。\n聯集 聯集我們會使用來解兩個集合「綜合起來，不重複的所有項目」\nprint(setA | setB) 交集 交集我們會使用來解兩個集合「共同重複」的項目\nprint(setA \u0026amp; setB) 差集 差集我們常用來解，以自己為中心，扣掉另外一個集合的元素，\n剩下「自己獨有」的元素\n取得 A 獨有元素 print(setA - setB) 取得 B 獨有元素 print(setB - setA) 結果 Reference 更完整的 set 整理推薦以下連結，我這邊目前只寫到我自己需要用的部分XD：\n[Python] 學習使用集合 (Set) ","date":"2022-03-02T11:04:55+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/%E6%88%AA%E5%9C%96-2022-03-02-%E4%B8%8A%E5%8D%8810.51.03.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-set/","tags":["Python","基礎語法"],"title":"【Python 基礎語法 #6】python set() 用法整理 - 快速找出重複的資料內容"},{"categories":["112 - Python 進階語法"],"content":"前言 這篇我們要來研究 python 裡面取 ASCII 的方法\n在 C++ 裡面，字串 (string)「\u0026quot;\u0026quot;」與 char (character)「\u0026rsquo;\u0026rsquo;」，\n是有嚴格的不同的，\n而 python 裡面沒有特別定義 char，使用上我們常把 「\u0026quot;\u0026quot;」與「\u0026rsquo;\u0026rsquo;」混用，\n且有一樣的效果。\n然而，有分別的 「\u0026quot;\u0026quot;」與 「\u0026rsquo;\u0026rsquo;」帶給我們的方便是，\n我們可以直接透過「\u0026rsquo;\u0026rsquo;」取得字元原始的 ASCII 碼，\n取得 char 所代表的一個數值，使我們在運算紀錄上更方便 (從處理 char 變成處理 int)\npython 裡面如果我們也要取得 ASCII 碼，我們則需要使用 ord()\n小知識：ord 全名為 ordinal，指的是序數\n範例程式碼 \u0026gt;\u0026gt;\u0026gt; print(ord(\u0026#39;a\u0026#39;)) 97 \u0026gt;\u0026gt;\u0026gt; print(ord(\u0026#39;A\u0026#39;)) 65 \u0026gt;\u0026gt;\u0026gt; print(type(ord(\u0026#39;A\u0026#39;))) \u0026lt;class \u0026#39;int\u0026#39;\u0026gt; \u0026gt;\u0026gt;\u0026gt; print(type(ord(\u0026#39;a\u0026#39;))) \u0026lt;class \u0026#39;int\u0026#39;\u0026gt; 結果 我們可以透過 ord() 取得 ASCII 碼\nReference Python ord() 函数 ","date":"2022-03-01T02:22:23+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/ord-1.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-ord/","tags":["Python","進階語法"],"title":"【Python 進階語法 #4】python ord() 用法整理 - python 取得 ASCII 的方法"},{"categories":["719 - 其他問題"],"content":"題目出處 weekly-contest-282 2185. Counting Words With a Given Prefix\n難度 Easy\n個人範例程式碼 class Solution: def prefixCount(self, words: List[str], pref: str) -\u0026gt; int: pref_length = len(pref) ans_cnt = 0 for ele in words: if len(ele) \u0026lt; pref_length: continue else: if self.compare_str(ele[0: pref_length], pref): ans_cnt += 1 else: pass return ans_cnt def compare_str(self, word_pref: str, pref: str) -\u0026gt; bool: return (word_pref == pref) Time Complexity O(n)\n算法說明 沒什麼，就取子字串直接比較\ncorner case 特殊情況處理 x\nBoundary conditions/ Edge conditions 邊際情況處理 python 子字串長度 str[0:n] 取得範圍為 str 的 index 0 到 index n-1(包含)\nReference ","date":"2022-02-28T20:05:47+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/misc-problems/leetcode-python-2185/","tags":[],"title":"【Leetcode】python - [2185] Counting Words With a Given Prefix 個人解法筆記"},{"categories":["630 - 演算法 Algorithm"],"content":"前言 sorting 簡單說明 (記憶用) 簡單說明 Selection sort (選擇排序) 先完成的排序是「當前index」，從當前往後搜尋全部，找最小 Insertion sort (插入排序) 每一層 loop 會完成的是「當前 index」之前的所有排序，也就是說，for-loop 的外層代表處理完成的 index，而內層處理插入當前的哪個位置 Bubble sort (氣泡排序) 不斷交換的排序，已交換len=4為例，比較 「(1,2),(2,3,(3,4)) / (1,2),(2,3),(3,4) / (1,2),(2,3),(3,4)」 (共 3組 x 3輪) Quick sort (快速排序) 1.先挑選一個基準 (pivot)，經常選最後一個值，後續的數值與基準比大小，建立two pointer，比小往左、比大往右，「停止條件：two pointer 交錯」 / 2.每一輪完成一次「對基準排序」後，將基準擺在中間 / 3. 依據 「divide and conquer (O(logn))」 的概念，完成剩餘的左右兩側(partition)的基準排序 (partition = [0, pivot-1], [pivot], [pivot+1, n-1]) 「停止條件：代表 front,end 的 two pointer 交錯」 Merge sort (合併排序) 一樣也是「divide and conquer (O(logn))」 的概念，1. 將 array 拆成 n/2，n/2再拆兩個n/4... 2. 拆到剩餘 2 or 1(種子)，開始合併，合併時只需要看如何已排序的兩數列如何交錯即可(O(m+n))，使用 two pointer Heap sort (堆積排序) Complete Binary Tree /Max Heap (root max) / MaxHeapify() = 自上而下調整矩陣 BuildMaxHeap() 1. 最後一個位置換到第一個 / 2.最後一個消失(取得 Max) 3. 自上而下的 MaxHeapify() Radix sort (基數排序) sorting 整理 Best Worst Average Space Complexity Selection sort O(n^2) O(n^2) O(n^2) O(1)，無新增矩陣等級的空間使用 Insertion sort O(n) 當數列已經是從小到大的 O(n^2) O(n^2) | Bubble sort | | 特殊情況 bubble sort early break: 如果某一回合沒有 swap 發生，直接結束 所以當從小至大排列時，bubble sort 有 best case O(n) -\u0026gt; early break\ninsertion sort : 恰好小至大，免做「插入動作 O(n)」 所以當從小至大排列時，insertion sort 有 best case O(n) -\u0026gt; early break\nquick sort can not divide well: 如果一個矩陣從小到大或從大到小，因為沒辦法好好發揮 divide and conquer 的效果，O(m+n)*O(n) 的 m=0, n=n (類似概念) = O(n^2) 所以當從小至大 or 大至小排列時，quick sort 有 worst case O(n^2)\n說明 插入排序法(Insertion Sort) Best case O(n)\n直到遇到第一個比正處理的值小的值\n快速排序法(Quick Sort) Worst case(On^2)\n當資料的順序恰好為由大到小或由小到大時\n有分割跟沒分割一樣\n(資料太整齊, pivot 產生不了 Divide and conquer的效果)\nReference [演算法] 排序演算法(Sort Algorithm) [Day21]排序？index sort? lambda又有你的事了？ Comparison Sort: Quick Sort(快速排序法) Comparison Sort: Merge Sort(合併排序法) Comparison Sort: Heap Sort(堆積排序法) ","date":"2022-02-28T02:19:54+08:00","image":"https://wongwongnotes.com/images/restored/migrated/1Pe7ksv.png","permalink":"https://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/sorting/","tags":["Algorithm","演算法"],"title":"【演算法筆記 #2】sorting 時間複雜度（Big O）— Best/Average/Worst case 比較 #面試準備"},{"categories":["Popworld 蹦世界"],"content":"《圖靈計畫》 遊戲介紹 由「Popworld 蹦世界」所出品的在家透過手機就能玩的密室逃脫解謎遊戲《圖靈計畫》，\n這次的劇情是在講一個比較技術的東西，關於「人工智慧」的未來這件事情。\n這部分內容剛好是作者我比較熟悉的東西，所以特別有感，\n但確實作為一般大眾能理解的故事題材確實比較硬一些。\n這邊我可以簡介一下：\n「圖靈測試」是由圖靈這位電腦科學家所設計出的測試實驗，\n測試內容簡單來說是：如果有一台「A電腦」努力學習想假裝自己是「人」、\n另外一台「B電腦」努力學習可以分辨出眼前的「人」是「電腦還是人」，\n如果B電腦無法順利分辨A電腦是人還是電腦，我們就稱做A電腦通過「圖靈測試」。\n然而，被破解的B電腦還能再透過學習進行強化，所以可以成為更強的「分辨器」，\n而A電腦也能透過學習進行強化，成為更強的「模仿人的機器」。\n這個就是現代人工智慧部分理論的基礎來源了！\n這次要感謝「Popworld 蹦世界」在疫情期間製作推出這款在家也能解謎的遊戲《圖靈計畫》，\n對於假日因為擔心疫情困在家中的人們，\n很推薦拿起你的手機，透過手機的app與遠端的好友不受疫情影響的玩遊戲哦！\n△ 融合現在科技題目的解謎遊戲，究竟會是怎麼樣的遊戲呢?\n如果在找密室逃脫的遊戲評價，這裡還有一些當初我自己有參考的密室逃脫心得文，也歡迎一併參考：\n遊戲相關 以下是自己的玩完後的一些心得，老樣子，有優點、有缺點都會說，\n「沒有爆雷」的部分，留給還沒遊玩的朋友們，(除了大約的行走距離提示XD)\n一切都為「個人\u0026amp;團隊成員」們的「真實體驗」，絕沒有多加修飾XD\n題目類型：「雙人合作與競爭，透過 AR 與題目互動、類似密室逃脫的思考題型」 這款遊戲主要的題目類型是「雙人合作與競爭，透過 AR 與題目互動、類似密室逃脫的思考題型」，\n雙人合作與競爭是個非常酷的玩法！\n有時我們可以選擇合作，讓大家同時獲得最大利益，\n有時我我們也可以選擇競爭，跟對方一較高下，\n這很像是博弈論裡面的賽局理論，要合作創造最大的雙贏？還是競爭讓自己的得到最高的分數呢？\n相信不同的玩家組合一定會有不同的玩法！創造出不同的遊戲結局！\n△ Alpha 與 Beta，究竟你們會是合作關係還是競爭關係呢?\n注意：本遊戲的兩位玩家要去分別下載不同的「遊戲包」\n一個要下載 「圖靈計畫\u0026ndash;Alpha」、另一個要下載「圖靈計畫\u0026ndash;Beta」，\n兩者配對後才能進行遊戲哦!\n「圖靈計畫\u0026ndash;Alpha」：https://popworld.cc/guide/4968/preview 「圖靈計畫\u0026ndash;Beta」: https://popworld.cc/guide/5392/preview 題目數量 主要題目一共有 4 關 (或應該說是四大階段)，每一題又有分成「表世界」與「裏世界」，\n算上表、裏關，遊戲時間大約有 8 關左右的長度。\n這麼多關的解謎遊戲，應該不是打算給大家跑計時完成的吧 XDD\n可以花上個幾天，慢慢的把它解出來，而且這樣豐富的內容只這一款遊戲就都包含了!\nCP 值根本超級高啊!!!\n會讓人有種穿梭在人間與電腦世界中的感覺，\n不過也許因為作者剛好讀電腦相關科系，\n一般民眾應該是沒有像我一樣那麼有感吧\u0026hellip;? (其實我也沒什麼感覺，就覺得很特別XD)\n題材真的很酷！很新穎的想法！\n△ 這次我們可以體驗在家進入另外一個世界的感覺，尋找事情的真相!\n解題時間: 無限制 (總共有四大階段，，一般人大約 6 ~ 8 小時) 這款遊戲，別看他只有4關，而且還要乘上表世界跟裏世界，總共8關。\n謎題的長度與難度絕對夠玩上一天啊！！！\n如果很需要找尋打發時間的遊戲\n(疫情期間大家在家都無聊嘛！是不是該找個又有趣又能打發一下時間的遊戲了！)\n謎題難度(解題需要的背景知識): 中偏難(4/5) 這次的題目難度我覺得非常的有意思！\n很多題目的答案雖然我們花很久的時間才解開，\n但是真的難的很過癮，很適合喜歡思考的人類！！！\n特別是平常玩密室逃脫會嫌題目太簡單的！\n這款的難度與複雜度，絕對夠你玩得過癮！\n謎題難度(解題過程夠不夠直覺): 中偏難(4/5) 這部分我目前遊玩後覺得算是「已經非常的不錯，但還有可以優化的地方！」\n所謂謎題的直覺度，指的就是我們要有多大的程度「代入出題者的思維才能解開謎題」，\n正常來說，我們直覺性的思考，應該是可以應不需切換出題者的思維，\n這次的遊戲裡面很多題目都相當的直覺。\n但部分的題目是知道答案後才知道「啊！原來出題者是這樣想！」\n這樣就顯得可惜了，因為題目本來是簡單的，\n但因為不夠直覺自然就不知道該如何去解。\n這次遊玩下來是部分題目會有這樣的感覺，但不影響我對他整體的好評價！\n(而且有聽說製作小組還會再針對這部分做題目的優化，也許讀者之後拿到的版本可能就是有優化過的題目囉！)\n我特別重視題目的直覺度更勝過難度，因為有時候當知道答案後，\n會發現題目本身不難，但因為要「代入出題者的思維才能解開」，\n這種我就覺得顯得不夠直覺了，會是個小缺點，\n應該要是「直覺上就能推理出答案，而不用代入出題者思維」。\n謎題驚喜度：能夠在雙人遊戲中，巧妙搭配「競爭與合作」的玩法，創造了無限的遊戲可能性 (5/5) 這部分我真的必須強調，\n這遊戲主打雙人遊玩，\n兩個人可以選擇合作也可以選擇為了自己的高分而競爭，\n所以「不同的人玩一定會有不同的結果」，\n這就創造了無限的可能性！！！\n也就是說，今天我如果拿這款跟某一個人玩，\n隔天再跟另外一個人玩，一定會有不一樣的過程！！！\n這是一個非常創新的想法！\n(以往密室逃脫的概念就是，多少人玩多少次，過程幾乎都是一樣的。)\n你的朋友會不會為了分數陷害你呢？或是你們會一起合作到底呢？\n賽局理論、囚徒困境，等著來玩遊戲的兩位朋友，來看看對方到底是怎麼樣的人吧！\n謎題故事性: 透過故事引導出圖靈計畫的概念，故事稍長但絕不遜色 (4/5) 這部分算是我覺得這遊戲很有意思的地方，\n就如同我前面所說，「圖靈測試」本來就是一個很技術無聊的東西，\n作為一個大眾化的主題我覺得有點太硬XD，\n但也因為故事與娛樂性，能把這樣的概念帶出來，我覺得這樣的想法真的很棒！！！\n但必須要說有一些小缺點，就是後半部的故事就有點跳 tone 了，\n後面的劇情不知道為什麼要那樣接，\n好像有點邏輯但完全有點奇怪的呀！！！\n(不過遊戲不會因此而感覺遜色就是XDD)\n一些個人的小建議 (1) 我們是雙人現場解題，有些問題兩人都在線上一起解的話可能沒有那麼方便 我們是雙人現場面對面的溝通解題，\n雖然這遊戲當初設計時，是打算給大家在家線上遊玩的，\n但我們光面對面解有些關卡就有碰到一些問題了，\n實在很難想像如果真的兩個人在線上溝通 (符合製作團隊當初的想法) 會發生什麼事情XDD\n這邊因為沒有實際經驗，就留給大家自行體驗了XD\n(2) 建議中途可以新增「吃飯暫停」的功能，畢竟大家也要吃飯但又怕影響成績 XD 這邊其實也算是因為有遊玩過別家的經驗，\n當時有印象深刻有一個「吃午餐時間」的功能也不影響計分，\n這樣其實可以讓玩家們很「安心的吃飯」，也可以「好好的稍微中場休息」一下。\n會非常建議能加入這個功能哦!\n-\u0026gt; 玩完後想推薦給什麼樣的人：「因為疫情在家，又想體驗去異世界解謎」的朋友!!! 這款我會想推薦給想「因為疫情在家，又想體驗去異世界解謎」的朋友!!!\n大家因為疫情在家期間，想必都不太敢往外亂跑，\n這時這個雙人遊戲很適合讓你們打發一整天的時光，\n又能夠體會到異世界探險的感覺哦!!!\n△ Beta 這個詭異的微笑，代表的是什麼呢? XD 喜歡實境解謎嗎？作者這裡也推薦一些有趣的實境解謎心得文，歡迎參考哦： 其他團體遊戲推\n","date":"2022-02-25T19:45:36+08:00","image":"https://wongwongnotes.com/images/restored/2022/02/turing.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/popworld/turing_strategy/","tags":["Popworld 蹦世界","嗡嗡粗門玩","實境解謎","蹦世界"],"title":"【在家解謎】《圖靈計畫》遊玩後心得 (無雷) - 解謎遊戲推薦 | Popworld 蹦世界"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\nerror: non-void function does not return a value in all control paths [-Werror,-Wreturn-type] 問題來源 我解 leetcode 提交答案碰到的情況\n解決方法 C++ 的單一 function 有出現沒有 return 值的狀況，\n有時候「不一定是邏輯有誤，但會導致 compile 的錯誤」\n實際上由於 C++ 的編譯器是十分嚴謹的，即使自己已經認為考慮了所有的情況，C++ 也會認為你可能有沒考慮到的內容，請把 default 內容都補齊。\n範例 印象中我碰到問題類似以下的結構：\nint foo(){ if(條件) return 1; else return 0; } 問題就出在 compiler 似乎認為這種狀況會存在沒有 return 的情形，\n詳細原因未知 (我當下只有先紀錄解決方法，等未來再碰到再來研究原因)\n解決範例 解決方法：我當下是多設定一個 return default 值就可以解決。\nint foo(){ if(條件) return 1; else return 0; return 0; // for default } Reference 修改error: non-void function does not return a value in all control paths [-Werror,-Wreturn-type] } ","date":"2022-02-25T15:54:07+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/c-error-non-void-function/","tags":["C++"],"title":"【C++】問題解決：error: non-void function does not return a value in all control paths [-Werror,-Wreturn-type] }"},{"categories":["720 - C++ LeetCode"],"content":"題目出處 88. Merge Sorted Array\n難度 Easy\n題目分類 array, two-pointers\n個人範例程式碼 class Solution { public: void merge(vector\u0026lt;int\u0026gt;\u0026amp; nums1, int m, vector\u0026lt;int\u0026gt;\u0026amp; nums2, int n) { while(m \u0026gt; 0 \u0026amp;\u0026amp; n \u0026gt; 0){ if(nums1[m-1] \u0026gt;= nums2[n-1]){ nums1[m+n-1] = nums1[m-1]; m -= 1; } else{ nums1[m+n-1] = nums2[n-1]; n -= 1; } } while(n \u0026gt; 0){ nums1[m+n-1] = nums2[n-1]; n -= 1; } } }; 說明 這題我們可以先注意題目的敘述：\n最需要思考的部分在於「題目希望最後的結果直接儲存在 nums1 裡面」\n雖然這樣的限制會使我們解題更加的「受到限制」，但反過來說，\n其實這也是題目給我們「最大的提示」。\n題目的 nums1 給的空間為 m+n，所以最後我們都會將所有的值擺入 nums1 中，\n而因為我們可以無憂慮去更改的值為最後的 0,0,0\u0026hellip; 部分\n(因為這個值就算被改消失了，也不影響我們想計算的結果)\n所以這邊可以推斷出一定要「從後面開始解」，「從最無痛的 0 開始取代」，\n再來我們就可以來思考，什麼時候擺入 nums1的值? 什麼時候擺入 nums2 的值?\n我們可以靠著題目所給的 m, n 來進行這樣的操作，透過值不斷擺入，\n我們去移動 m, n 作為 index 的座標，即可順利完成此題目。\n邊際情況處理 這題要注意的邊際情況為 m=0, n=0 的時候我們要怎麼處理，\n而照題目的敘述，\nn=0 基本上對我們來說沒差，因為 m 控制的 nums1 即代表我們要的答案 m=0 才是重點，如果還有剩餘的 n，都需要擺入 nums1 中，而位置索幸很好取得， 依然可以透過 m+n-1 算出 注意 index 細節，才是這題要考細心的部分 這題有很多 index 細節考驗著作答者的細心度，\n包含：\n迴圈的範圍僅限於 m, n \u0026gt; 0 時，其他需要進例外處理 m-1, n-1 才是當下座標 要擺入的位置為 m+n-1 處理到 n \u0026gt; 0 就應該停下，而非 n \u0026gt;= 0 Reference Python easy to understand in-place solution ","date":"2022-02-25T04:20:01+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/cpp-solutions/c-leetcode/leetcode-cpp-88/","tags":["C++","LeetCode"],"title":"【Leetcode】C++ - [88] Merge Sorted Array 個人解法筆記"},{"categories":["720 - C++ LeetCode"],"content":"題目出處 https://leetcode.com/problems/two-sum/\n難度 easy\n題目分類 Array, HashTable\n個人範例解法 - 一刷 (2021/10/18) class Solution { public: vector\u0026lt;int\u0026gt; twoSum(vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { map\u0026lt;int,int\u0026gt; dict; map\u0026lt;int,int\u0026gt;::iterator iter; vector\u0026lt;int\u0026gt; ans; for (int i=0; i\u0026lt;nums.size();i++){ iter = dict.find(target-nums[i]); // cout\u0026lt;\u0026lt;iter; // printf(\u0026#34;(%d,%d)\u0026#34;,iter-\u0026gt;first,iter-\u0026gt;second); if (iter != dict.end()){ // num found ans.push_back(iter-\u0026gt;second); //nums find in dict (smaller) ans.push_back(i); // current index (bigger) return ans; } else{ //iter == dict.end(), not found dict[nums[i]]=i; } } return ans; } }; 個人範例解法 - 二刷 (2022/02/24) 第二次要順便複習一下如何找 map 的 keys 的方法\n(python 寫太多都忘了)\n補充附於文末\nclass Solution { public: vector\u0026lt;int\u0026gt; twoSum(vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { map\u0026lt;int, int\u0026gt; mymap; vector\u0026lt;int\u0026gt; ans; for(int i=0; i \u0026lt; nums.size(); i++){ if(mymap.find(nums[i]) != mymap.end()){ // found ans.push_back(mymap[nums[i]]); ans.push_back(i); return ans; } else{ mymap[target - nums[i]] = i; } } return ans; } }; 解法重點 這題主要考的是 DP，\n需要先建立一張表\n(此處利用 map\u0026lt;int,int\u0026gt;，建立 int -\u0026gt; int 對應的 dict)，\n利用 「key-\u0026gt;value」 的特性保存 「值-\u0026gt;index」\n因此就能快速反查表得到 index\n動態規劃 (Dynamic programming，DP) 可參考：\nhttps://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/dynamic-programming/\nmap find 的方法 被 python 寵壞的我，整個 dict find 了起來 Orz\n來我們回到一下 C++ map 的世界。\n根據 iterator 找到元素 既然是特殊的資料型態，我們自然要靠 iterator 來幫助我們遍歷一下，\n思考的方式很簡單，iterator 會幫助我們一直看資料，\n那什麼情況會一直看到最後呢?\n就是「沒找到」的時候啦!!!\nmap\u0026lt;int, int\u0026gt; mymap; if(mymap.find(nums[i]) != mymap.end()){ // found } else{ // not found } 或不喜歡逆邏輯 (使用\u0026rsquo;不等於\u0026rsquo;符號) 的人，我們也可以正向的來 map\u0026lt;int, int\u0026gt; mymap; if(mymap.find(nums[i]) == mymap.end()){ // not found } else{ // found } Reference How to find if a given key exists in a C++ std::map ","date":"2022-02-24T23:03:30+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/cpp-solutions/c-leetcode/leetcode-cpp-1/","tags":["C++","LeetCode"],"title":"【Leetcode】C++ - [1] Two Sum 個人解法筆記 (內含 C++ map find 方法補充 (dict find)) #easy #Array #HashTable"},{"categories":["720 - C++ LeetCode"],"content":"題目出處 53. Maximum Subarray\n難度 Easy\n題目分類 array, divide-and-conquer, dynamic-programming\n個人範例程式碼 class Solution { public: int maxSubArray(vector\u0026lt;int\u0026gt;\u0026amp; nums) { vector\u0026lt;int\u0026gt; dp; for(int i=0; i\u0026lt; nums.size(); i++){ if(i == 0){ dp.push_back(nums[i]); // record \u0026#39;include me\u0026#39; biggest } else{ dp.push_back((nums[i] \u0026gt; nums[i]+dp[i-1]) ? nums[i] : nums[i]+dp[i-1]); // record \u0026#39;include me\u0026#39; biggest } } auto it = std::max_element(dp.begin(), dp.end()); // std::vector\u0026lt;int\u0026gt;::iterator it; // cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; // int max_value = *std::max_element(dp.begin(), dp.end()); // cout \u0026lt;\u0026lt; max_value \u0026lt;\u0026lt; endl; return *it; } }; 說明 這題我使用的是 DP 的解法，\n我宣告一個 DP 來儲存「包含自己的」時最好的答案。\n換句話說，我將題目拆解成到「包含每一個 index 的當下」 都是一個小題目，將題目的答案儲存進 dp 中。\n注意是「包含自己」的最好答案，也就是說，我們只需要比較「自己」與「dp(n-1)+自己」誰比較大。\nDP 裡面都是「一定要包含自己的最佳解」，而不是「全域的最佳解」。\n所以假設原來的題目變成：\nnums = [1,2,-1,4]\ndp = [1,3,2,6]\n2 就是因為一定要包含「-1」，我們只有「-1」跟「-1+\u0026lsquo;包含前一個\u0026rsquo;的最大」誰比較大。\nC++ vector max 用法整理 vector 想必是 C++ 最常被使用的資料型態了。\n因此如何取 max 我們應該也要來好好記錄一下\n以下方法基本上兩者大同小異，但因為 vector 需要透過 iterator 遍歷，\n這邊就沒有像 python 那麼方便了。\nvector 取 max 方法一 - iterator 基本上就是很標準的寫法，使用的是 std 裡面定義的 std::max_element，\n會回傳位址，需要用 *it 取得該值。\nvector\u0026lt;int\u0026gt; dp; auto it = std::max_element(dp.begin(), dp.end()); // std::vector\u0026lt;int\u0026gt;::iterator it; cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; 註：這邊的 「auto it」 等於 「std::vector::iterator it」，\n宣告一個 iterator 變數實在太麻煩了，真的感謝 C++ 11 發明出了 auto 這個東西\u0026hellip;\nvector 取 max 方法二 - 繞過 iterator 直接取值 我們直接將 max_element 取得的 iterator 直接取值，\n(這也才是我們想要的)\n就可以直接得到值了，不用另外宣告 iterator。\nvector\u0026lt;int\u0026gt; dp; int max_value = *std::max_element(dp.begin(), dp.end()); cout \u0026lt;\u0026lt; max_value \u0026lt;\u0026lt; endl; Reference std::max_element 用法與範例 How can I get the maximum or minimum value in a vector? ","date":"2022-02-24T22:58:52+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/cpp-solutions/c-leetcode/leetcode-cpp-53/","tags":["C++","LeetCode"],"title":"【Leetcode】C++ - [53] Contains Duplicate 個人解法筆記 內含 C++ vector max 用法整理"},{"categories":["418 - Git 問題解決"],"content":"前言 有時候我們可能會碰到如以下的情況\nfatal: The current branch master has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin master 以下是我的處理方式。\n說明 說明一下這是什麼意思，這表示雲端那邊還「不知道你 local 有了這條 branch」，\n因此我們在做 push 的動作時，也需要「同時把這個新的 branch 推上去」，\n才能讓「雲端」跟「本地」的 git 有一樣的版本控制。\n解決方式 我們要做的事情其實也很簡單，git 已經很貼心地告訴我們該怎麼解決了，\n就是把他給你的那行複製下來，直接執行即可。\n以上面例子來說，我們就是複製：\ngit push --set-upstream origin master 把本地的 git branch 透過 \u0026ndash;set-upstream 也推上至雲端，\n即可解決雲端 branch 不同步的問題。\n註：「git push \u0026ndash;set-upstream」的指令，可以簡寫為「git push -u」，效果是一樣的。\n","date":"2022-02-24T19:04:51+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git/git-no-upstream-branch/","tags":["Git","Linux"],"title":"【Git】問題解決：fatal: The current branch master has no upstream branch. (git 無法 push)"},{"categories":["441 - VScode"],"content":"前言 這篇只是做漪一些刷題前的準備筆記，\n不定時補充一些有用的東西。\n安裝 VScode leetcode extension 我是安裝第一個啦\u0026hellip; 感覺最像是官方出的，\n其他我沒試過\n登入 leetcode 帳號 因為 Leetcode 升級了安全性設定，所以先在都沒有辦法靠帳號密碼登入，\n只能夠靠 cookie 流派的方式\nstep 1. 先去 leetcode 官網登入 沒什麼好說的，連結在這裡: https://leetcode.com/\nstep 2. 開啟 google chrome 的 檢查模式 (可以按 F12 或 右健選「檢查」) google chrome 的 檢查模式的長相大概像下圖這樣：\nstep 3. 選擇 network，重新整理網頁，找到 graphql 這個檔案 參考上圖圈選的位置操作。\nstep 4. 點擊 graphql 這個檔案後，查看 Headers，往下找到 cookie 這個字，右鍵選 copy value 參考上圖圈選的位置操作。\n資料夾路徑設定 去 settings.json 搜尋 leetcode，並修改以下路徑，\n即可以改變檔案存放的位置。\n範例 我把所有自動產生的程式碼都放在我的桌面 leetcode 資料夾，\n這邊的路徑是「windows 格式」的資料夾路徑，\n只擷取重點部分：\n\u0026#34;leetcode.workspaceFolder\u0026#34;: \u0026#34;c:\\\\Users\\\\howar\\\\Desktop\\\\leetcode\u0026#34;, 事前準備 - 建立 GitHub repo (for me) 我只是會習慣用 github 來記錄一下刷題的情況與進度，\n這個就真的是 for me 了。\n本次刷題建立的 repo： https://github.com/howarder3/leetcode-2022\nReference 【Leetcode】問題解決: Login failed. Please make sure the credential is correct. (內附圖文說明) 在vscode中配置LeetCode插件，從此愉快地刷題 ","date":"2022-02-24T18:14:58+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/leetcode-4.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-leetcode-extension/","tags":["VSCode"],"title":"【Leetcode】VScode leetcode extension 初始化設定筆記 (設定 VScode leetcode 插件筆記)"},{"categories":["720 - C++ LeetCode"],"content":"題目出處 217. Contains Duplicate\n難度 Easy\n題目分類 Array, Hash Table, Sorting\n個人範例程式碼 class Solution { public: bool containsDuplicate(vector\u0026lt;int\u0026gt;\u0026amp; nums) { // unordered_set set // Hash table | RB-tree // (unordered) | (ordered) // insert and search fast O(1) | search fast only when data less // memory use more | memory use less // // set: ordered, print as ascend, need know n-1... // unordered: no-sort needed, single lookup, only insert, delete, search set\u0026lt;int\u0026gt; s(nums.begin(), nums.end()); // for(auto it = s.begin(); it != s.end(); it++) // cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; return nums.size() != s.size(); } }; 說明 這題考的東西很簡單，就是找有沒有重複的東西，\n「找重複」非常適合使用 set 來做，我們只需要比較 set 之後的大小是否等於原長度即可。\nC++ set 用法整理 set, unordered_set 的差別 說到 C++ 的 set，我們可以先來比較 set, unordered_set 的差別，\nset unordered_set 實現方式 RB-tree Hash table 順序 ordered unordered 速度 search fast only when data less insert and search fast O(1) 空間使用 memory use less memory use more 使用場合 ordered/ print as ascend(descend)/ need know realtion of n-1, n-2... no-sort needed/ single lookup/ only insert, delete, search vector 轉 set, vector to set 剛好此題目有需要 vector 轉 set，\n我們這邊示範一下如何轉換這樣的資料結構。\n而轉換的過程中，重複的部分會因為 set 的特性自然地消失，\n因此在這題目中我們能夠自然地知道有沒有重複的內容。\nvector\u0026lt;int\u0026gt; v; set\u0026lt;int\u0026gt; s(v.begin(), v.end()); for(auto it = s.begin(); it != s.end(); it++) cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; 如何印 set, how to print set 如果我們使用一般的 for 迴圈，因為 set 不具有「順序性」\n(這裡指的不是有沒有 unordered 的問題，而是資料結構本身)\n我們沒有辦法用 index 的方式去呼叫，類似 set[0], set[1]\u0026hellip;\n因此我們會需要宣告一個 iterator 幫助我們遍歷這個資料結構!\n以下示範剛好結合 vector 轉換 set 的內容：\nvector\u0026lt;int\u0026gt; v; set\u0026lt;int\u0026gt; s(v.begin(), v.end()); for(auto it = s.begin(); it != s.end(); it++) cout \u0026lt;\u0026lt; *it \u0026lt;\u0026lt; endl; 補充一個類似的 map, unordered_map map 就類似 python 中的 dict (抱歉作者比較熟 python XDD)，\n總之就是有 「key-value」 pair 的資料結構\nmap unordered_map 實現方式 RB-tree Hash table 順序 ordered unordered 速度 慢 (但效率穩定) 快 空間使用 節省 memory 使用較多 memory 使用場合 元素自動排序 元素不需要排序/ only insert, delete, search/ 不需要擔心 memory 使用 Reference Single line C++ solution 60ms 【C++】【总结】unordered_map,unordered_set,map和set的用法和区别 set::begin() and set::end() in C++ STL How to convert vector to set? c++ set與unordered set的區別 C++ std::unordered_set 用法與範例 C++ std::set 用法與範例 unordered_map 與 map 的對比（轉） ","date":"2022-02-24T03:37:54+08:00","permalink":"https://wongwongnotes.com/posts/algorithm-practice/cpp-solutions/c-leetcode/leetcode-cpp-217/","tags":["C++","LeetCode"],"title":"【Leetcode】C++ - [217] Contains Duplicate 個人解法筆記 — set / unordered_set 用法整理"},{"categories":["923 - Windows"],"content":"前言 這篇是以下文章的前言，因為很多作業會有需要 windows ssh 的獨立設定，\n因此我們把這篇設定文章獨立拆開來寫。\n【Windows】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (Windows anydesk ssh command line)\n因為 Windows 想要 ssh 需要多設定一些東西，所以就有了這篇。\nWindows 設定步驟 - 開啟 ssh 連線方式 (外對內、內對內 (localhost)) Step 1. 安裝 OpenSSH 相關的設定 step 1-1. 左下角搜尋，找到「應用程式與功能」 (也可以直接從控制台中找) step 1-2. 左下角搜尋，找到「應用程式與功能」 (也可以直接從控制台中找) 點選「選用功能」，因為這功能一般人不會用到，\n所以 Windows 藏的很深也是很正常的。\nstep 1-3. 從選用功能中新增 OpenSSH 相關功能 (建議都裝，至少也要裝伺服器) 這邊就是要來安裝 Windows 做的原生 ssh 支援功能，\n可以先從下方的「已安裝功能」嘗試搜尋 OpenSSH 看看有沒有曾經裝過。\n注意：下面的搜尋是搜尋「已安裝功能」，不要像我第一次一樣以為這邊就是「搜尋安裝」的地方了XD\nstep 1-4. 從上方的「新增選用功能」，安裝 OpenSSH 相關功能 這邊因為我已經裝過了，沒辦法進行圖片示範，\n總之應該會搜尋到一些可以安裝的東西。\nstep 2. 從 powershell 中開啟 ssh 連線的相關設定 step 2-1. (重要) 以「系統管理員」執行 Windows PowerShell 這邊我會「右鍵」左下角的 Windows 符號，選擇以「系統管理員」執行 Windows PowerShell\nstep 2-2. (重要) 輸入指令已開啟 ssh 服務 下面指令輸入完後，就可以開啟 ssh 服務了\nStart-Service sshd step 2-3. (選用) 自行決定要不要開起自動啟用 ssh 服務 因為我會很常使用 ssh 服務，這邊我設定給他自動啟動。\nSet-Service -Name sshd -StartupType \u0026#39;Automatic\u0026#39; 更：但後來有點擔心會有資安問題，我就把它關閉改成要用時自動啟動了。 (手動啟用)\nSet-Service -Name sshd -StartupType \u0026#39;Manual\u0026#39; 這邊可以參考 Microsoft 官方文件，網址： Microsoft 官方文件\n到這邊基本上 ssh 的部分就都搞定了，真的很累XDDD\nReference 【Docker】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發 SSH through TCP tunneling SSH 遠端連線回家中的 Windows 電腦 ","date":"2022-02-23T21:13:24+08:00","image":"https://wongwongnotes.com/images/restored/2021/11/windows-ssh-6.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/windows/windows-ssh/","tags":["sshfs"],"title":"【Windows】windows 開啟 ssh 的方式"},{"categories":["914 - Wordpress"],"content":"前言 此為我的網站發生以下問題時，我個人的解決辦法。\n此頁面目前無法運作 www.wongwongnotes.com 重新導向太多次。 若要修正此問題，請嘗試清除 Cookie. ERR_TOO_MANY_REDIRECTS 截圖如下：\n解決辦法 如果直接照著上面建議的方法做，\n照理來說我們會先去清除 cookie，\n不過我試了這個方法後，發現沒有效果！！！\n最後解決的方法 最後我去修改文章分類，可能因為我個人在文章分類時會有習慣有「多重分類的情況」，\n我將一些比較不重要的分類給整理了一下後，此問題順利解決。\n整理分類的內容如下面範例：\nReference How to Fix the “Too Many Redirects” Error in WordPress (13 Methods) ","date":"2022-02-14T16:07:22+08:00","image":"https://wongwongnotes.com/images/restored/2022/02/%E6%88%AA%E5%9C%96-2022-02-14-%E4%B8%8B%E5%8D%884.05.46.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/err_too_many_redirects/","tags":["WordPress"],"title":"【Wordpress】問題解決：此頁面目前無法運作重新導向太多次。 若要修正此問題，請嘗試清除 Cookie. ERR_TOO_MANY_REDIRECTS"},{"categories":["211 - C++ 基礎語法"],"content":"前言 在 C++ 呼叫成員變數時，我們在網路上常見會看到兩種使用方法，\n一種是使用「-\u0026gt;」、一種是使用「.」，\n有時候混淆起來導致我們撰寫程式常常跳錯，真的很困擾，\n為此我們就來搞清楚這一點吧！！！\n先講結論 如果你想要無腦用的話，\n那就看到宣告為指標的 class instance 就使用 「-\u0026gt;」！\n不是宣告為指標的 class instance 就使用「.」！\n還是要講一下細節 「.」：是「物件」呼叫成員的方法 「-\u0026gt;」：是「指標物件」呼叫成員的方法 真的要理解上，以下兩個是同樣意思的\n「a-\u0026gt;b」=「(*a).b」\n使用上因為我們通常在宣告時就已經有宣告是指標了，\n所以使用「-\u0026gt;」能提供給我們更多的便利。\n簡單的範例 A obj_A; obj_A.a_public = 2; A* ptr_A; // 宣告為指標的 instance ptr_A-\u0026gt;a_public = 2; // 同義於 (*ptr_A).a_public = 2; Reference Difference between - and . in C [duplicate] What is the difference between . and - in C++? ","date":"2022-02-11T17:39:52+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-pointer-dot/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","基礎語法"],"title":"【C++ 基礎語法 #3】C++ class 使用「-\u003e (pointer)」、「. (dot)」呼叫成員變數比較整理"},{"categories":["441 - VScode"],"content":"前言 windows 環境安裝 g++ 以編譯 C++ 的程式，\n沒有像 MacOS 或 Linux 那麼方便，\n我們會需要多裝滿多東西的。\n這邊簡單的做一下安裝紀錄的筆記。\nStep1. 安裝 Mingw Mingw 算是 Microsoft 官方提供給 Windows 使用者安裝 g++ 的標準解了，\n就連官方的文件也是推薦我們這樣安裝。\n官方安裝說明參考網站：\nUsing GCC with MinGW Step 1-1. 取得最新版的 Mingw 我們可以透過 MSYS2 的網站取得最新版的 Mingw-w64，\n簡單來說它可以幫助我們以最自然的方式建立 gcc 的環境，與安裝 C++ 相關的工具與 libraries.\n去這裡下載最新版的 Mingw-w64：MSYS2 Mingw-w64 的安裝說明也在上面的這網站裡面了，還有一步步的圖示，真的讚。\nStep 1-2. 安裝並更新相關 Mingw 套件 這步驟雖然在上面官方網站的說明也有寫到，不過實際操作有些比較會讓人遲疑的地方我也在重新記錄一下。\n使用下面兩個指令更新相關套件：\n# Commands used # Update the package database and base packages using pacman -Syu # Update rest of the base packages pacman -Su 範例： 測試 安裝完成後，在 terminal 我們可以直接輸入以下的內容測試\n# gcc version gcc --version # g++ version g++ --version # gdb version gdb --version Step 2. 設定 Windows 環境變數 (必要性待確認) Step 2-1. 去電腦的內容裡面，開啟環境變數設定 位置位於「檔案總管」，右鍵點選我的電腦，選擇「內容」 在「關於」裡面點選「進階系統設定」，從「系統內容」的下面找到「環境變數」，開啟他 在環境變數中，照著以下的方式設定。(以下路徑為預設的 MinGW 安裝路徑) Step 3. 回到 VScode 設定編譯環境 Step 3-1. 對 cpp 檔案，輸入 「ctrl + shift + B」 即可以快速開啟編譯視窗 Step 3-2. 我們可以觀察底下是否有將 cpp 檔案編譯成功 Step 3-3. 我們可以透過對應檔名的 exe 測試程式 (編譯成功的前提下) 範例： 不過這個範例，雖然可以編譯並執行 c++ 程式，\n但這樣感覺好像跟 g++ 的執行方式有點不太一樣\u0026hellip;\n因此我們等等就來介紹 g++ 的使用方式。\n透過 terminal 使用 g++ 來執行 c++ 程式 這部分待補，目前我用上面方法用很順，\n不過也許哪天會想再來研究 Windows 該怎麼用\n(目前開發主力都在 MacOS 與 Linux 上了)\nReference Using GCC with MinGW How to Download and Install C Cpp Toolset ( gcc g++ gdb ) in Windows 10 using mingw-w64 and msys2 How to Download and Install C Cpp Toolset ( gcc g gdb ) in Windows 10 using mingw-w64 and msys2 MSYS2 MinGW - Minimalist GNU for Windows 安裝MinGW - Minimalist GNU for Windows ","date":"2022-01-31T22:25:09+08:00","image":"https://wongwongnotes.com/images/restored/2022/01/g15.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-gcc-gdb/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","VSCode"],"title":"【C++】在 Windows Visual Studio Code 上面安裝 g++, gcc, gdb 的環境，並可以在 Visual Studio Code 編譯並執行筆記"},{"categories":["720 - C++ LeetCode"],"content":"題目出處 https://leetcode.com/problems/decode-ways/description/\n難度 medium\n題目分類 Array, HashTable\n個人範例解法 class Solution { public: int numDecodings(string s) { // dp int n = s.size(); if(n \u0026lt;= 0){ return 0; } vector\u0026lt;int\u0026gt; dp(n+1); dp[n] = 1; // ..... 1 for(int i=n-1; i\u0026gt;=0 ; i--){ if(s[i] == \u0026#39;0\u0026#39;){ // invalid case dp[i] = 0; continue; } // default dp[i] = dp[i+1]; // if special case int num = stoi(s.substr(i, 2)); // count 2 // cout \u0026lt;\u0026lt; num \u0026lt;\u0026lt; endl; if( 10 \u0026lt;= num \u0026amp;\u0026amp; num \u0026lt;= 26 ){ dp[i] = dp[i+1] + dp[i+2]; } } // for(int i=0; i\u0026lt;n+1; i++) // cout \u0026lt;\u0026lt; dp[i] \u0026lt;\u0026lt; \u0026#34; \u0026#34;; return dp[0]; } } 解法重點 此為我的解法，應還有其他解法\n這題主要考的是 DP，需要先建立一張表。\n以一般的 case 而言 (我們一個一個數，應該 dp[i] = dp[i+1]) 特殊 case 的話，允許我們一次走兩格，所以答案會變成 dp[i] = dp[i+1] + dp[i+2] 如果碰到 0 開頭的子字串，表示為非法字串，dp[i] = 0 動態規劃 (Dynamic programming，DP) 可參考：\nhttps://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/dynamic-programming/\nReference Evolve from recursion to dp ","date":"2022-01-30T18:19:13+08:00","image":"https://wongwongnotes.com/images/restored/2022/01/img_0257-232x300.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/cpp-solutions/c-leetcode/leetcode-cpp-91/","tags":["C++","LeetCode"],"title":"【Leetcode】C++ - [91] Decode Ways 個人解法筆記 #medium #DP"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在使用 ubuntu 18.04 碰到登入後會自動登出 (跳回登入畫面)\n最後以下的問題的解決辦法。\n情況敘述 先說明此問題的可能性應該有非常多種，這裡只提供我自己碰到這問題有成功的解法，「不適用」於各種情況。\n- 某次更換電腦硬體之後，發現 ubuntu 的帳號登入就會有自動登出的現象 - 但是「登入其他帳號」，卻「不會」有自動登出的現象 - 由上述推測問題發生的範圍僅發生在 ubuntu 這個帳號底下，因此開始進行檔案的比對 以下事情發生當時我於「~/.config/autostart」所有的檔案\n「~/.config/autostart」等價於「/home/ubuntu/.config/autostart」\n# in ~/.config/autostart \u0026gt; ls indicator-multiload.desktop remmina-applet.desktop synology-drive-autostart.desktop 解決方法 最終，在 「~/.config」 內的 autostart 這個資料夾找到了出問題的檔案，\n出問題的檔案名稱為「indicator-multiload.desktop」，\n這個檔案是當初我想裝 linux 系統監控的狀態列所安裝的內容，\n那我的推測是應該這個程式去綁定了 CPU 的 Hardward ID，\n在初始化桌面的時候，因為硬體已經被更換過，所以導致抓不到原來的 HWID，因此才直接登出。\n不過怎麼會是直接登出，而不是「該程式」跳出錯誤而中止執行\u0026hellip;\n這個就\u0026hellip; 我也不知道這怎麼設計的\n使用 indicator-multiload 讓 ubuntu 狀態列上可以顯示目前 CPU, memory, swap 的使用情況 呈現效果 我們可以直接在狀態列監控目前 CPU, memory, swap 的使用狀態，\n可以在電腦當機事前，做一些預防性的關閉一些沒有使用的程式。\n安裝 我們需要安裝的套件名稱為：ubuntu tweak\n安裝方法由於之前裝的過程中沒有留下紀錄，\n這邊只從 history 看有可能的安裝指令。\nsudo apt-get install gnome-tweak-tool Reference ","date":"2022-01-24T10:38:03+08:00","image":"https://wongwongnotes.com/images/restored/2022/01/indicator-2-300x214.png","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/ubuntu-can-not-log-in/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】Ubuntu 問題解決: 登入後自動登出 (跳回登入畫面) ubuntu 18.04 can not log in"},{"categories":["241 - C++ rapidjson"],"content":"前言 python 中有很方便的 type() 功能使我們能直接知道資料的格式，\n但 C++ 當中沒有這種方便的功能。\n但我們可以透過 rapidjson 內建的方式，嘗試取得在 json 檔案內的文件類型。\n使用方法 透過 rapidjson 範例中已經定義好的文件類型，我們可以進行檔案格式的解析。\n定義名稱 我們先定義名稱：\nstatic const char* kTypeNames[] = { \u0026#34;Null\u0026#34;, \u0026#34;False\u0026#34;, \u0026#34;True\u0026#34;, \u0026#34;Object\u0026#34;, \u0026#34;Array\u0026#34;, \u0026#34;String\u0026#34;, \u0026#34;Number\u0026#34; }; 取用類別 然後當我們想要取用類別名稱的時候，可以使用「.GetType()」取得類別名稱，\n另外結合剛剛我們先定義好的名稱類型，「kTypeNames[data.GetType()]」 即可取得類別名稱。\ncout \u0026lt;\u0026lt; kTypeNames[document[\u0026#34;key\u0026#34;].GetType()] \u0026lt;\u0026lt; endl; 註：如果直接使用「.GetType()」，會取得對應的位置，還需要結合 「kTypeNames[]」，才能取得看得懂的資料類別名稱。\nReference Tutorial RapidJSON 文档 Tencent / rapidjson ","date":"2022-01-20T22:00:01+08:00","permalink":"https://wongwongnotes.com/posts/cpp/data-processing/c-rapidjson/c-rapidjson-datatype/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","rapidjson"],"title":"【C++】C++ rapidjson 取得檔案類型的方法"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\nerror: ‘Document’ was not declared in this scope 問題來源 我原先想進行 C++ rapidjson 的 Document 相關操作，碰到此問題\n解決方法 沒有正確地引入 rapidjson 的相關套件，include 進來後即可解決問題。\n#include \u0026#34;rapidjson/document.h\u0026#34; Reference ","date":"2022-01-20T20:37:55+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/document-was-not-declared/","tags":["C++"],"title":"【C++】問題解決：error: ‘Document’ was not declared in this scope"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\nerror: ‘rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::GenericValue(const rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;\u0026amp;) [with Encoding = rapidjson::UTF8\u0026lt;\u0026gt;; Allocator = rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt;]’ is private within this context 問題來源 我原先想進行以下的操作\nValue value6 = document[\u0026#34;key6\u0026#34;]; 出問題的段落在於我嘗試指派一個 private 成員給 Value 變數，\n但因為如同上面敘述中的 「is private within this context」，導致私有成員的變數被禁止取用。\n(利用 function 作為資料的參考也會碰到同樣的問題。)\n解決方法 我們改取用位址，即可解決這個問題。\nValue \u0026amp;value6 = document[\u0026#34;key6\u0026#34;]; Reference ","date":"2022-01-20T20:32:33+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/error-genericvalue-allocator/","tags":["C++"],"title":"【C++】問題解決：error: ‘rapidjson::GenericValue\u003cEncoding, Allocator\u003e::GenericValue(const rapidjson::GenericValue\u003cEncoding, Allocator\u003e\u0026) [with Encoding = rapidjson::UTF8\u003c\u003e; Allocator = rapidjson::MemoryPoolAllocator\u003c\u003e]’ is private within this context"},{"categories":["222 - C++ Makefile"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\n/usr/include/rapidjson/document.h:1681: const Ch* rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::GetString() const [with Encoding = rapidjson::UTF8\u0026lt;\u0026gt;; Allocator = rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt;; rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::Ch = char]: Assertion `IsString()\u0026#39; failed. 問題來源 我原先想進行以下的操作\nd.Parse(document[\u0026#34;key6\u0026#34;].GetString()); 出問題的段落在於 「GetString()」這個部分，\n因為我嘗試去存取一個不存在 String 位置 (或是該值本身就不被定義為「String」)，\n所以才出現此 error。\n解決方法 我們應該要去檢查 GetString() 所取得的結果是否真的為 String，\n並進行更正即可以解決問題。\nReference ","date":"2022-01-20T20:19:22+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/rapidjson-document-h1681/","tags":["C++","Makefile"],"title":"【C++】問題解決：/usr/include/rapidjson/document.h:1681: const Ch* rapidjson::GenericValue\u003cEncoding, Allocator\u003e::GetString() const [with Encoding = rapidjson::UTF8\u003c\u003e; Allocator = rapidjson::MemoryPoolAllocator\u003c\u003e; rapidjson::GenericValue\u003cEncoding, Allocator\u003e::Ch = char]: Assertion `IsString()' failed."},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\n/usr/include/rapidjson/document.h:1497: rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;\u0026amp; rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::operator[](rapidjson::SizeType) [with Encoding = rapidjson::UTF8\u0026lt;\u0026gt;; Allocator = rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt;; rapidjson::SizeType = unsigned int]: Assertion `IsArray()\u0026#39; failed. 問題來源 我原先想進行以下的操作\nint n = document[\u0026#34;key1\u0026#34;][0]; 出問題的段落在於我嘗試指派一個不存在的位置給變數，\n「Assertion `IsArray()\u0026rsquo; failed.」這段就有點出了，問題點在於我所指派的值並不是「Array」。\n解決方法 請再檢查一下自己想嘗試存取的 Array 位置，\n是否如同自己想像的「那個位置」是否真的存在。\nReference ","date":"2022-01-20T20:09:59+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/rapidjson-document-h1497/","tags":["C++"],"title":"【C++】問題解決：/usr/include/rapidjson/document.h:1497: rapidjson::GenericValue\u003cEncoding, Allocator\u003e\u0026 rapidjson::GenericValue\u003cEncoding, Allocator\u003e::operator[](rapidjson::SizeType) [with Encoding = rapidjson::UTF8\u003c\u003e; Allocator = rapidjson::MemoryPoolAllocator\u003c\u003e; rapidjson::SizeType = unsigned int]: Assertion `IsArray()' failed."},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\nerror: invalid initialization of reference of type ‘rapidjson::Document\u0026amp; {aka rapidjson::GenericDocument\u0026lt;rapidjson::UTF8\u0026lt;\u0026gt; \u0026gt;\u0026amp;}’ from expression of type ‘rapidjson::GenericValue\u0026lt;rapidjson::UTF8\u0026lt;\u0026gt; \u0026gt;’ 問題來源 我原先想進行以下的操作\n// Object (python dict) cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key6] = \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] Get type of document[key6] = \u0026#34; \u0026lt;\u0026lt; kTypeNames[document[\u0026#34;key6\u0026#34;].GetType()] \u0026lt;\u0026lt; endl; // kTypeNames[itr-\u0026gt;value.GetType()]); Value \u0026amp;value6 = document[\u0026#34;key6\u0026#34;]; Document d2; d2 = value6; // error: invalid initialization of reference of type ‘rapidjson::Document\u0026amp; {aka rapidjson::GenericDocument\u0026lt;rapidjson::UTF8\u0026lt;\u0026gt; \u0026gt;\u0026amp;}’ from expression of type ‘rapidjson::GenericValue\u0026lt;rapidjson::UTF8\u0026lt;\u0026gt; \u0026gt;’ 出問題的段落在於 「d2 = value6」這個部分，\n因為 value6 的格式為 Value，\n而 d2 的格式為 Document，\n所以當我們想進行等於的操作時，因為格式不同產生的問題。\n說明 我們先解讀一下這個問題的內容敘述是什麼意思，\n基本上我們可以抓到兩個關鍵字\nrapidjson::GenericDocument rapidjson::GenericValue 我們可以先看 rapidjson 官方的定義：\n// Each JSON value is stored in a type called Value. // A Document, representing the DOM, contains the root Value of the DOM tree. // All public types and functions of RapidJSON are defined in the rapidjson namespace. Document 全名為 DOM 格式 (Document Object Model, DOM)，\n包含著 DOM tree 裡面所有的 Value。\n換句話說我們可以說，Document 可以用來作為儲存 Value 的文件格式。\n我們理解上述內容後，我們就可以來看上面的 error 內容：\n就是我們嘗試想把 Value 格式的內容引入至 Document，\n因為格式沒有對應導致的問題。\n解決方法 因為當我們想把 GenericDocument -\u0026gt; GenericValue 時，\n可以進行以下的操作「tmpJsonObject.CopyFrom(json_content, json_content.GetAllocator()); 」。\n// prepare json Document d = make_json(); Value v(kObjectType); v.CopyFrom(d, d.GetAllocator()); Reference ","date":"2022-01-20T19:58:55+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/error-invalid-initialization-rapidjsondocument/","tags":["C++"],"title":"【C++】問題解決：error: invalid initialization of reference of type ‘rapidjson::Document\u0026 {aka rapidjson::GenericDocument\u003crapidjson::UTF8\u003c\u003e \u003e\u0026}’ from expression of type ‘rapidjson::GenericValue\u003crapidjson::UTF8\u003c\u003e \u003e’"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\nerror: variable ‘std::ifstream in’ has initializer but incomplete type 解法 此問題發生在我們沒有正確地引入 library，導致對應的功能無法使用\n需在開頭補上\n#include \u0026lt;fstream\u0026gt; 把對應的 library 引入，即可以解決。\nReference C++: variable \u0026lsquo;std::ifstream ifs\u0026rsquo; has initializer but incomplete type ","date":"2022-01-19T15:33:41+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/error-variable-stdifstream-in/","tags":["C++"],"title":"【C++】問題解決：error: variable ‘std::ifstream in’ has initializer but incomplete type"},{"categories":["241 - C++ rapidjson"],"content":"前言 json 算是檔案與檔案溝通之間，非常經常使用的格式，\n由於非常經常使用，我們將 json 製作檔案的格式紀錄下來，\n供之後反覆使用方便。\n範例程式碼 以下我們先分開說明每一種資料型態在 C++ rapidjson 處理的方式，\n最後再整合起來，用一支程式進行全部的示範。\n處理基本資料型態 (bool, int, double, string) 寫入 json (基本資料型態 bool, int, double, string) Document json_content; Document::AllocatorType\u0026amp; allocator = json_content.GetAllocator(); json_content.SetObject(); // set string, int, bool json_content.AddMember(\u0026#34;key1\u0026#34;, \u0026#34;value1\u0026#34;, allocator); // char* // json_content.AddMember(\u0026#34;key1\u0026#34;, Value().SetString(\u0026#34;value1\u0026#34;.c_str(), allocator), allocator); // string json_content.AddMember(\u0026#34;key2\u0026#34;, 10, allocator); // int json_content.AddMember(\u0026#34;key3\u0026#34;, true, allocator); // bool 從 json 讀取 (基本資料型態 bool, int, double, string) Document document; // Object (python dict) document.Parse(json_content.str().c_str()); cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key1] = \u0026#34; \u0026lt;\u0026lt; document[\u0026#34;key1\u0026#34;].GetString() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key2] = \u0026#34; \u0026lt;\u0026lt; document[\u0026#34;key2\u0026#34;].GetInt() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key3] = \u0026#34; \u0026lt;\u0026lt; document[\u0026#34;key3\u0026#34;].GetBool() \u0026lt;\u0026lt; endl; 處理進階資料結構 - 1D Array (Python list) 這裡註記 Python list 的原因是因為作者本身是從 Python 學過來的XD，\n由於資料結構命名不同，但指的是同樣的東西，因此在這邊做一些筆記。\n寫入 json (1D Array) // set array (python list) // 1D array Value oneDimArray(kArrayType); for(int i = 0 ; i \u0026lt; 5; i++) { oneDimArray.PushBack(i, allocator); } json_content.AddMember(\u0026#34;key4\u0026#34;, oneDimArray, allocator); 從 json 讀取 (1D Array) Value \u0026amp;value4 = document[\u0026#34;key4\u0026#34;]; // array assert(value4.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, value4 is not Array\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key4] = \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; ; for(SizeType i = 0; i \u0026lt; value4.Size(); i++){ if(i \u0026lt; value4.Size() - 1) cout \u0026lt;\u0026lt; value4[i].GetInt() \u0026lt;\u0026lt; \u0026#34;,\u0026#34;; else cout \u0026lt;\u0026lt; value4[i].GetInt() \u0026lt;\u0026lt; \u0026#34;] \u0026#34;; } 處理進階資料結構 - 2D Array (Python list) 這裡註記 Python list 的原因是因為作者本身是從 Python 學過來的XD，\n由於資料結構命名不同，但指的是同樣的東西，因此在這邊做一些筆記。\n寫入 json (2D Array) // set Value array (python list) // 2D array Value secondDimArray(kArrayType); for(int j = 0 ; j \u0026lt; 5; j++){ Value firstDimArray(kArrayType); for(int i = 0 ; i \u0026lt; 5; i++){ firstDimArray.PushBack(j*5+i, allocator); } secondDimArray.PushBack(firstDimArray, allocator); } json_content.AddMember(\u0026#34;key5\u0026#34;, secondDimArray, allocator); 從 json 讀取 (2D Array) void printOneDimArray(Value \u0026amp;arr){ assert(arr.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, arr is not Array\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; ; for(SizeType i = 0; i \u0026lt; arr.Size(); i++){ if(i \u0026lt; arr.Size() - 1) cout \u0026lt;\u0026lt; arr[i].GetInt() \u0026lt;\u0026lt; \u0026#34;,\u0026#34;; else cout \u0026lt;\u0026lt; arr[i].GetInt() \u0026lt;\u0026lt; \u0026#34;]\u0026#34;; } } // read 2D array cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key5] = \u0026#34; \u0026lt;\u0026lt; endl; Value \u0026amp;value5 = document[\u0026#34;key5\u0026#34;]; // array cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] Get type of document[key5] = \u0026#34; \u0026lt;\u0026lt; kTypeNames[document[\u0026#34;key5\u0026#34;].GetType()] \u0026lt;\u0026lt; endl; // kTypeNames[itr-\u0026gt;value.GetType()]); assert(value5.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, value5 is not Array\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; ; for(SizeType i = 0; i \u0026lt; value5.Size(); i++){ Value \u0026amp;eachArr = document[\u0026#34;key5\u0026#34;][i]; // array assert(eachArr.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, eachArr is not Array\u0026#34;); printOneDimArray(eachArr); if(i \u0026lt; eachArr.Size() - 1) cout \u0026lt;\u0026lt; \u0026#34;,\u0026#34;; else cout \u0026lt;\u0026lt; \u0026#34;]\u0026#34;; } cout \u0026lt;\u0026lt; endl; 處理進階資料結構 - Object (Python dict) 寫入 json (Object - python dict) // set Value Object (python dict) // add sub_json_content to key6 Value sub_json_content(kObjectType); // sub_json_content.AddMember(\u0026#34;sub_key1\u0026#34;, Value().SetString(\u0026#34;sub_value1\u0026#34;, allocator, allocator); // allocator = json_content.GetAllocator() // string sub_json_content.AddMember(\u0026#34;sub_key1\u0026#34;, \u0026#34;sub_value1\u0026#34;, allocator); // char* sub_json_content.AddMember(\u0026#34;sub_key2\u0026#34;, 20, allocator); // int sub_json_content.AddMember(\u0026#34;sub_key3\u0026#34;, false, allocator); // bool json_content.AddMember(\u0026#34;key6\u0026#34;, sub_json_content, allocator); 從 json 讀取 (Object - python dict) // Object (python dict) cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key6] = \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] Get type of document[key6] = \u0026#34; \u0026lt;\u0026lt; kTypeNames[document[\u0026#34;key6\u0026#34;].GetType()] \u0026lt;\u0026lt; endl; Value \u0026amp;value6 = document[\u0026#34;key6\u0026#34;]; assert(value6.IsObject() \u0026amp;\u0026amp; \u0026#34;assertion error, value6 is not Object\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] value6[sub_key1] = \u0026#34; \u0026lt;\u0026lt; value6[\u0026#34;sub_key1\u0026#34;].GetString() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] value6[sub_key2] = \u0026#34; \u0026lt;\u0026lt; value6[\u0026#34;sub_key2\u0026#34;].GetInt() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] value6[sub_key3] = \u0026#34; \u0026lt;\u0026lt; value6[\u0026#34;sub_key3\u0026#34;].GetBool() \u0026lt;\u0026lt; endl; 範例程式碼 下面的範例是我們新增一份以 timestamp 作為檔案名稱的 json 檔案。\n其他相關的變數：\n基本功能設定 root_folder：檔案根目錄 Document make_json()：json 的內容設定 void generate_jsonfile(char* file_name, string json_content)：產生對應的 json 檔案 void read_jsonfile(char* file_name)：讀取對應的 json 檔案 小工具 string json_to_string(Document\u0026amp; d)：(小工具) 以字串顯示 Document 格式的內容 string json_to_string(Value\u0026amp; d)：(小工具) 以字串顯示 Value 格式的內容 int64_t timeMs()：(小工具) 取得現在的 timestamp (13 位) void printOneDimArray(Value \u0026amp;arr)：(小工具) 顯示一維的 Array 內容 (裡面有寫排版) static const char* kTypeNames[]：顯示資料型態的名稱，GetType() 所使用 #include \u0026#34;rapidjson/document.h\u0026#34; // error: ‘Document’ was not declared in this scope #include \u0026#34;rapidjson/writer.h\u0026#34; #include \u0026#34;rapidjson/stringbuffer.h\u0026#34; // #include \u0026#34;rapidjson/prettywriter.h\u0026#34; // for stringify JSON #include \u0026lt;iostream\u0026gt; #include \u0026lt;string\u0026gt; #include \u0026lt;fstream\u0026gt; #include \u0026lt;sstream\u0026gt; #include \u0026lt;linux/limits.h\u0026gt; // doc \u0026amp; tutorial: // https://rapidjson.org/md_doc_tutorial.html // http://rapidjson.org/zh-cn/index.html // https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp // Each JSON value is stored in a type called Value. // A Document, representing the DOM, contains the root Value of the DOM tree. // All public types and functions of RapidJSON are defined in the rapidjson namespace. // ---------------------------- // Parse it into a Document: // #include \u0026#34;rapidjson/document.h\u0026#34; // using namespace rapidjson; // // ... // Document document; // document.Parse(json); // The JSON is now parsed into document as a DOM tree: // ---------------------------- // Document, Value // Value contains Object, Array // Value Object = python dict // Value Array = python list using namespace std; using namespace rapidjson; static const char* kTypeNames[] = { \u0026#34;Null\u0026#34;, \u0026#34;False\u0026#34;, \u0026#34;True\u0026#34;, \u0026#34;Object\u0026#34;, \u0026#34;Array\u0026#34;, \u0026#34;String\u0026#34;, \u0026#34;Number\u0026#34; }; void printOneDimArray(Value \u0026amp;arr){ assert(arr.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, arr is not Array\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; ; for(SizeType i = 0; i \u0026lt; arr.Size(); i++){ if(i \u0026lt; arr.Size() - 1) cout \u0026lt;\u0026lt; arr[i].GetInt() \u0026lt;\u0026lt; \u0026#34;,\u0026#34;; else cout \u0026lt;\u0026lt; arr[i].GetInt() \u0026lt;\u0026lt; \u0026#34;]\u0026#34;; } } string json_to_string(Document\u0026amp; d){ StringBuffer buffer; Writer\u0026lt;StringBuffer\u0026gt; writer(buffer); // PrettyWriter\u0026lt;StringBuffer\u0026gt; writer(buffer); d.Accept(writer); // cout \u0026lt;\u0026lt; \u0026#34;[json_to_string (Document version)] json string = \u0026#34; \u0026lt;\u0026lt; buffer.GetString(); return buffer.GetString(); } string json_to_string(Value\u0026amp; d){ StringBuffer buffer; Writer\u0026lt;StringBuffer\u0026gt; writer(buffer); // PrettyWriter\u0026lt;StringBuffer\u0026gt; writer(buffer); d.Accept(writer); // cout \u0026lt;\u0026lt; \u0026#34;[json_to_string (Value version)] json string = \u0026#34; \u0026lt;\u0026lt; buffer.GetString(); return buffer.GetString(); } int64_t timeMs() { struct timespec time; clock_gettime(CLOCK_REALTIME, \u0026amp;time); return ((int64_t)time.tv_sec)*1000 + time.tv_nsec/1000000; } void read_jsonfile(char* file_name){ printf(\u0026#34;[Reading json content] Reading json file: %s \u0026#34;, file_name); ifstream in(file_name); string inputStr; ostringstream json_content; while(getline(in, inputStr)){ json_content \u0026lt;\u0026lt; inputStr; } in.close(); cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] All json content = \u0026#34; \u0026lt;\u0026lt; json_content.str() \u0026lt;\u0026lt; endl; Document document; // Object (python dict) document.Parse(json_content.str().c_str()); cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key1] = \u0026#34; \u0026lt;\u0026lt; document[\u0026#34;key1\u0026#34;].GetString() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key2] = \u0026#34; \u0026lt;\u0026lt; document[\u0026#34;key2\u0026#34;].GetInt() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key3] = \u0026#34; \u0026lt;\u0026lt; document[\u0026#34;key3\u0026#34;].GetBool() \u0026lt;\u0026lt; endl; // 1D array Value \u0026amp;value4 = document[\u0026#34;key4\u0026#34;]; // array assert(value4.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, value4 is not Array\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key4] = \u0026#34; \u0026lt;\u0026lt; endl; printOneDimArray(value4); cout \u0026lt;\u0026lt; endl; // cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; ; // for(SizeType i = 0; i \u0026lt; value4.Size(); i++){ // if(i \u0026lt; value4.Size() - 1) // cout \u0026lt;\u0026lt; value4[i].GetInt() \u0026lt;\u0026lt; \u0026#34;,\u0026#34;; // else // cout \u0026lt;\u0026lt; value4[i].GetInt() \u0026lt;\u0026lt; \u0026#34;] \u0026#34;; // } // 2D array cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key5] = \u0026#34; \u0026lt;\u0026lt; endl; Value \u0026amp;value5 = document[\u0026#34;key5\u0026#34;]; // array cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] Get type of document[key5] = \u0026#34; \u0026lt;\u0026lt; kTypeNames[document[\u0026#34;key5\u0026#34;].GetType()] \u0026lt;\u0026lt; endl; // kTypeNames[itr-\u0026gt;value.GetType()]); assert(value5.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, value5 is not Array\u0026#34;); cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; ; for(SizeType i = 0; i \u0026lt; value5.Size(); i++){ Value \u0026amp;eachArr = document[\u0026#34;key5\u0026#34;][i]; // array assert(eachArr.IsArray() \u0026amp;\u0026amp; \u0026#34;assertion error, eachArr is not Array\u0026#34;); printOneDimArray(eachArr); if(i \u0026lt; eachArr.Size() - 1) cout \u0026lt;\u0026lt; \u0026#34;,\u0026#34;; else cout \u0026lt;\u0026lt; \u0026#34;]\u0026#34;; } cout \u0026lt;\u0026lt; endl; // Object (python dict) cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] document[key6] = \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] Get type of document[key6] = \u0026#34; \u0026lt;\u0026lt; kTypeNames[document[\u0026#34;key6\u0026#34;].GetType()] \u0026lt;\u0026lt; endl; // kTypeNames[itr-\u0026gt;value.GetType()]); Value \u0026amp;value6 = document[\u0026#34;key6\u0026#34;]; // object (python dict) assert(value6.IsObject() \u0026amp;\u0026amp; \u0026#34;assertion error, value6 is not Object\u0026#34;); cout \u0026lt;\u0026lt; json_to_string(value6) \u0026lt;\u0026lt; endl; // cout \u0026lt;\u0026lt; value6[\u0026#34;sub_key1\u0026#34;] \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] value6[sub_key1] = \u0026#34; \u0026lt;\u0026lt; value6[\u0026#34;sub_key1\u0026#34;].GetString() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] value6[sub_key2] = \u0026#34; \u0026lt;\u0026lt; value6[\u0026#34;sub_key2\u0026#34;].GetInt() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;[Reading json content] value6[sub_key3] = \u0026#34; \u0026lt;\u0026lt; value6[\u0026#34;sub_key3\u0026#34;].GetBool() \u0026lt;\u0026lt; endl; } void generate_jsonfile(char* file_name, string json_content){ // python // with open(file_name, \u0026#39;w\u0026#39;) as f_out: // ret = json.dumps(json_content) // f_out.write(ret) // printf(\u0026#34;Generating file: %s \u0026#34;, file_name); FILE *f = fopen(file_name, \u0026#34;w\u0026#34;); if(f) { const char *s = json_content.c_str(); int r = fwrite(s, 1, strlen(s), f); fclose(f); printf(\u0026#34;[Writing json content] Success to write file: %s \u0026#34;, file_name); } else { printf(\u0026#34;[Writing json content] Failed to write file: %s \u0026#34;, file_name); } } Document make_json(){ // tmp_ljob = {} // tmp_ljob[\u0026#34;key1\u0026#34;] = \u0026#34;value1\u0026#34; // tmp_ljob[\u0026#34;key2\u0026#34;] = \u0026#34;value2\u0026#34; // tmp_ljob[\u0026#34;key3\u0026#34;] = \u0026#34;value3\u0026#34; // return tmp_ljob Document json_content; Document::AllocatorType\u0026amp; allocator = json_content.GetAllocator(); json_content.SetObject(); // set string, int, bool json_content.AddMember(\u0026#34;key1\u0026#34;, \u0026#34;value1\u0026#34;, allocator); // string // json_content.AddMember(\u0026#34;key1\u0026#34;, Value().SetString(\u0026#34;value1\u0026#34;.c_str(), allocator), allocator); // string json_content.AddMember(\u0026#34;key2\u0026#34;, 10, allocator); // int json_content.AddMember(\u0026#34;key3\u0026#34;, true, allocator); // bool // set array (python list) // 1D array Value oneDimArray(kArrayType); for(int i = 0 ; i \u0026lt; 5; i++) { oneDimArray.PushBack(i, allocator); } json_content.AddMember(\u0026#34;key4\u0026#34;, oneDimArray, allocator); // 2D array Value secondDimArray(kArrayType); for(int j = 0 ; j \u0026lt; 5; j++){ Value firstDimArray(kArrayType); for(int i = 0 ; i \u0026lt; 5; i++){ firstDimArray.PushBack(j*5+i, allocator); } secondDimArray.PushBack(firstDimArray, allocator); } json_content.AddMember(\u0026#34;key5\u0026#34;, secondDimArray, allocator); // set Document Object (python dict) // add sub_json_content to key6 Value sub_json_content(kObjectType); // sub_json_content.AddMember(\u0026#34;sub_key1\u0026#34;, Value().SetString(\u0026#34;sub_value1\u0026#34;, allocator, allocator); // allocator = json_content.GetAllocator() sub_json_content.AddMember(\u0026#34;sub_key1\u0026#34;, \u0026#34;sub_value1\u0026#34;, allocator); // string sub_json_content.AddMember(\u0026#34;sub_key2\u0026#34;, 20, allocator); // int sub_json_content.AddMember(\u0026#34;sub_key3\u0026#34;, false, allocator); // bool json_content.AddMember(\u0026#34;key6\u0026#34;, sub_json_content, allocator); return json_content; } int main(){ // prepare json Document json_content = make_json(); Value tmpJsonObject(kObjectType); tmpJsonObject.CopyFrom(json_content, json_content.GetAllocator()); cout \u0026lt;\u0026lt; \u0026#34;[json_to_string] json_to_string = \u0026#34; \u0026lt;\u0026lt; json_to_string(json_content) \u0026lt;\u0026lt; endl; // document cout \u0026lt;\u0026lt; \u0026#34;[json_to_string] json_to_string = \u0026#34; \u0026lt;\u0026lt; json_to_string(tmpJsonObject) \u0026lt;\u0026lt; endl; // value // d.SetObject(); // prepare filename (python) // current_timestamp = int(time.time() * 1000) # 13 digits // file_name = root_folder + \u0026#34;{:13d}.json\u0026#34;.format(current_timestamp) char buf_path[PATH_MAX]; string root_folder = \u0026#34;./\u0026#34;; int64_t current_timestamp = timeMs(); // printf(\u0026#34;%ld \u0026#34;, timeMs()); sprintf(buf_path, \u0026#34;%s/%ld.json\u0026#34;, root_folder.c_str(), current_timestamp); // printf(\u0026#34;generating file: %s \u0026#34;, buf_path); // json_content = make_json() generate_jsonfile(buf_path, json_to_string(json_content)); read_jsonfile(buf_path); return 0; } 執行結果 編譯 \u0026amp; 執行 g++ test_write_json.cpp \u0026amp;\u0026amp; ./a.out 結果 [json_to_string] json_to_string = {\u0026#34;key1\u0026#34;:\u0026#34;value1\u0026#34;,\u0026#34;key2\u0026#34;:10,\u0026#34;key3\u0026#34;:true,\u0026#34;key4\u0026#34;:[0,1,2,3,4],\u0026#34;key5\u0026#34;:[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19],[20,21,22,23,24]],\u0026#34;key6\u0026#34;:{\u0026#34;sub_key1\u0026#34;:\u0026#34;sub_value1\u0026#34;,\u0026#34;sub_key2\u0026#34;:20,\u0026#34;sub_key3\u0026#34;:false}} [json_to_string] json_to_string = {\u0026#34;key1\u0026#34;:\u0026#34;value1\u0026#34;,\u0026#34;key2\u0026#34;:10,\u0026#34;key3\u0026#34;:true,\u0026#34;key4\u0026#34;:[0,1,2,3,4],\u0026#34;key5\u0026#34;:[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19],[20,21,22,23,24]],\u0026#34;key6\u0026#34;:{\u0026#34;sub_key1\u0026#34;:\u0026#34;sub_value1\u0026#34;,\u0026#34;sub_key2\u0026#34;:20,\u0026#34;sub_key3\u0026#34;:false}} [Writing json content] Success to write file: .//1642562237053.json [Reading json content] Reading json file: .//1642562237053.json [Reading json content] All json content = {\u0026#34;key1\u0026#34;:\u0026#34;value1\u0026#34;,\u0026#34;key2\u0026#34;:10,\u0026#34;key3\u0026#34;:true,\u0026#34;key4\u0026#34;:[0,1,2,3,4],\u0026#34;key5\u0026#34;:[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19],[20,21,22,23,24]],\u0026#34;key6\u0026#34;:{\u0026#34;sub_key1\u0026#34;:\u0026#34;sub_value1\u0026#34;,\u0026#34;sub_key2\u0026#34;:20,\u0026#34;sub_key3\u0026#34;:false}} [Reading json content] document[key1] = value1 [Reading json content] document[key2] = 10 [Reading json content] document[key3] = 1 [Reading json content] document[key4] = [0,1,2,3,4] [Reading json content] document[key5] = [Reading json content] Get type of document[key5] = Array [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19],[20,21,22,23,24]] [Reading json content] document[key6] = [Reading json content] Get type of document[key6] = Object {\u0026#34;sub_key1\u0026#34;:\u0026#34;sub_value1\u0026#34;,\u0026#34;sub_key2\u0026#34;:20,\u0026#34;sub_key3\u0026#34;:false} [Reading json content] value6[sub_key1] = sub_value1 [Reading json content] value6[sub_key2] = 20 [Reading json content] value6[sub_key3] = 0 Reference https://rapidjson.org/md_doc_tutorial.html http://rapidjson.org/zh-cn/index.html https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp ","date":"2022-01-19T11:19:10+08:00","permalink":"https://wongwongnotes.com/posts/cpp/data-processing/c-rapidjson/cpp-json-template/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","rapidjson"],"title":"【C++】C++ rapidjson 使用範例，產生 json 檔案的範本 cpp json template"},{"categories":["141 - Python JSON處理"],"content":"前言 json 算是檔案與檔案溝通之間，非常經常使用的格式，\n由於非常經常使用，我們將 json 製作檔案的格式紀錄下來，\n供之後反覆使用方便。\n範例程式碼 下面的範例是我們新增一份以 timestamp 作為檔案名稱的 json 檔案。\n其他相關的變數：\nroot_folder：檔案根目錄 generate_jsonfile(file_name, json_content)：產生對應的 json 檔案 make_json()：json 的內容設定 import json import time root_folder = \u0026#39;./\u0026#39; def generate_jsonfile(file_name, json_content): print(f\u0026#34;generating file: {file_name}\u0026#34;) with open(file_name, \u0026#39;w\u0026#39;) as f_out: ret = json.dumps(json_content) f_out.write(ret) def make_json(): tmp_ljob = {} tmp_ljob[\u0026#34;key1\u0026#34;] = \u0026#34;value1\u0026#34; tmp_ljob[\u0026#34;key2\u0026#34;] = \u0026#34;value2\u0026#34; tmp_ljob[\u0026#34;key3\u0026#34;] = \u0026#34;value3\u0026#34; return tmp_ljob if __name__ == \u0026#39;__main__\u0026#39;: current_timestamp = int(time.time() * 1000) # 13 digits file_name = root_folder + \u0026#34;{:13d}.json\u0026#34;.format(current_timestamp) json_content = make_json() generate_jsonfile(file_name, json_content) Reference ","date":"2022-01-17T16:10:25+08:00","permalink":"https://wongwongnotes.com/posts/python/data-formats/python-json/python-json-template/","tags":["JSON處理","Python"],"title":"【Python JSON 處理 #1】python json 使用範例 — 產生 json 檔案範本"},{"categories":["420 - Docker"],"content":"前言 此篇為解決以下問題的過程：\nError response from daemon: Unknown runtime specified nvidia 解法 \u0026amp; 說明 問題發生在我嘗試安裝某個與 container 有關的套件之後，\n因為 「/etc/docker/daemon.json」在安裝時被修改過了，\n才導致上述的錯誤發生。\n還原 /etc/docker/daemon.json 原本的 「etc/docker/daemon.json」 應該存在以下內容，\n❯ cat /etc/docker/daemon.json { \u0026#34;runtimes\u0026#34;: { \u0026#34;nvidia\u0026#34;: { \u0026#34;path\u0026#34;: \u0026#34;nvidia-container-runtime\u0026#34;, \u0026#34;runtimeArgs\u0026#34;: [] } } } 特別注意 \u0026ldquo;nvidia-container-runtime\u0026rdquo; 這一段，\n我的問題發生在當我安裝了套件之後， \u0026ldquo;runtimes\u0026rdquo; 這個 key 整個被洗掉了，\n所以才會出現以上的 error。\n最後將 json 的內容，補回以上 \u0026ldquo;runtimes\u0026rdquo; 的 key 那一段就可以解決了。\nReference 【Docker】daemon.json的作用（八） ","date":"2022-01-17T14:44:00+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/WSL-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/docker-error-response-from-daemon-unknown-runtime-specified-nvidia/","tags":["Bash","Docker","Linux","Ubuntu"],"title":"【Docker】問題解決：Error response from daemon: Unknown runtime specified nvidia"},{"categories":["941 - Minecraft"],"content":"情境 遊戲進度應該在可附魔之後並且能開啟地獄門時建造本塔。 主功能為練等，刷蜘蛛以外怪的物品。 運氣太差周遭沒有村莊可作為殭屍村民捕捉器。 後進度應銜接村莊建立，以盡早拿到有修補腹膜的圖書管理員為目標。 優勢 物件導向(啥 低工安意外率 後續取出特定怪種容易 可嘗試拼接其他功能(目前可作為唱片收集器，怪物頭收集嘗試中) 藍圖 註：特別框起來的部分只要放開關側即可\n建構順序 - 27~31底座區 - 確保中心底層可以通到水域後向上疊完7~26的玻璃 - 從中心跳離高點在開關任意邊堆土堆並往上設置紅石線路 - 到達頂端0先架設照明並利用土堆回到10架設至中心的閘門 - 由下而上建完生怪區後再到土堆區向下清除回到地面 - 設置完31以下部分完成基本功能 其他注意事項 需要有螢石粉來造紅石燈，至少五個 需要高空壓制來提升效能 ","date":"2022-01-03T17:42:11+08:00","image":"https://wongwongnotes.com/images/restored/2022/01/%E8%9E%A2%E5%B9%95%E6%93%B7%E5%8F%96%E7%95%AB%E9%9D%A2-60.png","permalink":"https://wongwongnotes.com/posts/os-misc/games/minecraft/minecraft_mob_tower/","tags":["Minecraft"],"title":"【Minecraft】MC生存藍圖 - 可開關式生怪塔"},{"categories":["442 - Sublime Text"],"content":"前言 這篇要透過 alias 指令自製 subl 的效果，\nsubl 在有些 Linux 系統預設安裝就會有，\n但當沒有的時候，我們可以透過 alias 的方式來自製。\n個人情況 我因為在使用 ubuntu 18.04 時碰到 sublime text 3 無法開啟的狀況，\n因此載了 sublime text 2 來用用，發現需要一個快速從 terminal 開啟的指令，\n就自己寫了這個小腳本。\n撰寫一份捷徑腳本 start_sublime.sh vim start_sublime.sh 腳本 start_sublime.sh 內容 #!/bin/bash /home/ubuntu/Desktop/SublimeText2/sublime_text 在 terminal 內設定 alias 捷徑 alias subl=\u0026#34;bash /home/ubuntu/Desktop/SublimeText2/start_sublime.sh\u0026#34; 在 ~/.bashrc (.zshrc) 設定此 alias ， 讓之後新啟動的 termianl 可以自動套用此 alias 啟動 terminal 設定檔 ~/.bashrc (.zshrc) ，看個人使用的 script vim ~/.bashrc # or vim ~/.zshrc 設定 alias alias subl=\u0026#34;bash /home/ubuntu/Desktop/SublimeText2/start_sublime.sh\u0026#34; 完成！ 之後只需要在 terminal 輸入 subl 即可快速開啟 sublime！ subl ","date":"2022-01-03T16:33:56+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/222.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-alias-subl/","tags":[],"title":"【Sublime】透過 alias 自製 subl 指令 (當 Linux 系統沒有內建的時候)"},{"categories":["214 - C++ 系統控制"],"content":"基礎概念 inotify 是取用 linux 底下的系統自動監聽檔案變化的方式，\n在 C++ 中，當我們獲得了系統給的 event，我們會得到該檔案的「檔名」，\n我們就能再進一步依照此絕對路徑做對應的資料操作。\n注意：inotify 的功能目前只在 linux 系統能使用，mac 上不能使用哦~ (自己親自測試過QQ)\n使用範例 inotify 做到的效果就是：如果當某個資料夾底下有資料變化，會告訴你「檔名」\n舉一些例子例如：\n新增：IN_CREATE 修改：IN_MODIFY 刪除：IN_DELETE 資料被開啟：IN_OPEN 資料屬性變化：IN_ATTRIB (可搭配touch使用，獲得目標檔案路徑) 下方程式碼說明 這是一個範例程式碼，\n用途是透過無限迴圈偵測指定資料夾路徑 \u0026ldquo;./target_path/\u0026rdquo; 底下的「不間斷的」檔案變化。\nSample Code #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;errno.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #include \u0026lt;sys/inotify.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;cxxabi.h\u0026gt; #include \u0026lt;typeinfo\u0026gt; #define EVENT_SIZE (sizeof(struct inotify_event)) #define BUF_LEN (1024 * (EVENT_SIZE + 16)) using namespace std; void get_file_path_loop(string watch_path){ char buffer[BUF_LEN]; int length, i = 0; int fd, wd; /* https://man7.org/linux/man-pages/man7/inotify.7.html */ fd = inotify_init(); if (fd \u0026lt; 0) { perror(\u0026#34;inotify_init\u0026#34;); } wd = inotify_add_watch(fd, watch_path.c_str(), IN_MODIFY | IN_CREATE | IN_DELETE | IN_ATTRIB); while(1){ length = read(fd, buffer, BUF_LEN); printf(\u0026#34;[Event] The file length = %d \u0026#34;, length); if (length \u0026lt; 0) { perror(\u0026#34;read\u0026#34;); } i = 0; while (i \u0026lt; length) { struct inotify_event *event = (struct inotify_event *) \u0026amp;buffer[i]; if (event-\u0026gt;len) { if (event-\u0026gt;mask \u0026amp; IN_CREATE) { printf(\u0026#34;[Event IN_CREATE] The file %s/%s was created. \u0026#34;, watch_path.c_str(), event-\u0026gt;name); } else if (event-\u0026gt;mask \u0026amp; IN_DELETE) { printf(\u0026#34;[Event IN_DELETE] The file %s/%s was deleted. \u0026#34;, watch_path.c_str(), event-\u0026gt;name); } else if (event-\u0026gt;mask \u0026amp; IN_MODIFY) { printf(\u0026#34;[Event IN_MODIFY] The file %s/%s was modified. \u0026#34;, watch_path.c_str(), event-\u0026gt;name); } else if (event-\u0026gt;mask \u0026amp; IN_ATTRIB) { printf(\u0026#34;[Event IN_ATTRIB] The file %s/%s was IN_ATTRIB. \u0026#34;, watch_path.c_str(), event-\u0026gt;name); } } i += EVENT_SIZE + event-\u0026gt;len; } } (void) inotify_rm_watch(fd, wd); (void) close(fd); } int main(int argc, char **argv) { string watch_path = \u0026#34;./target_path/\u0026#34;; get_file_path_loop(watch_path); return 0; } Reference inotify(7) — Linux manual page inotify(7) - Linux man page inotify_add_watch(2) — Linux manual page https://stackoverflow.com/questions/13351172/inotify-file-in-c ","date":"2022-01-03T01:19:51+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-system-detect/cpp-inotify/","tags":["C++","C++ 系統偵測","Linux","Ubuntu","系統控制"],"title":"【C++ 系統控制 #2】C++ inotify 偵測指定路徑底下的文件變化"},{"categories":["850 - Google Sheet / Excel"],"content":"前言 我在 Google Sheet / Excel 會經常使用到的 計數、數數 常用公式整理，\n在這篇文章整理一下公式。\n數特定範圍「有值」的總數 使用 COUNT 就可以得到該行「有值」的數量\n範例：數特定範圍「有值」的總數 =COUNT(F2:F124)] 數一整行「有特定值」的總數 使用 countif 就可以得到該行「有特定值」的數量\n範例：數「特定範圍值 為 100」 的數量 =countif(F121:F124, 100) 範例：數「特定範圍值 為 true」 的數量 =COUNTIF(AK2:AK183, true) Reference Excel 小教室 – 常用函數 IF、COUNTIF、COUNT、COUNTA 介紹 ","date":"2021-12-15T18:02:42+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/productivity/google-sheet-excel/google-sheet-excel-count/","tags":["Bash","Linux","Ubuntu"],"title":"【Google Sheet / Excel #3】計數、數數 用 常用公式整理，內附「數總數」快速範例 COUNT, COUNTIF"},{"categories":["850 - Google Sheet / Excel"],"content":"前言 我在 Google Sheet / Excel 會經常使用到的 整除、取餘數的功能，\n在這篇文章整理一下公式。\n整除與取餘數的公式 整除 整除在算分類的時候非常好用\n=QUOTIENT(5, 2) # 答案 = 2 取餘數 取餘數，一樣也是在分類的時候非常好用\n=MOD(5,2) # 答案 = 1 實作快速轉換 「秒數」快速轉換「時、分、秒」 我們先有個概念，\n- 秒/3600，「取整數」就會是小時， - (秒-「整小時*3600」)/60，「取整數」就會是分， - 而剩下的 (秒-「整小時*3600」- 「整分*60」) 就會是秒數。 於是我們就可以寫出公式了！\n需要經過變數處理的公式 origin_sec = 總秒數 hr = QUOTIENT(origin_sec, 3600) min = QUOTIENT((origin_sec - hr * 3600), 60) sec = origin_sec - hr * 3600 - min * 60 無變數，可以直接算的公式 代入變數一下，即可以得到以下「無變數」的內容：\norigin_sec = 總秒數 hr = QUOTIENT(origin_sec, 3600) min = QUOTIENT((origin_sec - QUOTIENT(origin_sec, 3600) * 3600), 60) sec = origin_sec - QUOTIENT(origin_sec, 3600) * 3600 - QUOTIENT((origin_sec - QUOTIENT(origin_sec, 3600) * 3600), 60) * 60 範例 A2 = 86400 hr = =QUOTIENT(A2, 3600) min = QUOTIENT((A2 - QUOTIENT(A2, 3600) * 3600), 60) sec = A2 - QUOTIENT(A2, 3600) * 3600 - QUOTIENT((A2 - QUOTIENT(A2, 3600) * 3600), 60) * 60 Reference QUOTIENT 函數 https://isvincent.pixnet.net/blog/post/49322673 ","date":"2021-12-15T16:29:00+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/productivity/google-sheet-excel/google-sheet-excel-sec-format/","tags":["Bash","Linux","Ubuntu"],"title":"【Google Sheet / Excel #2】整除、取餘數 常用公式整理，內附「總秒數」快速轉換「時、分、秒」快速範例"},{"categories":["398 - Linux 問題解決"],"content":"sudo: unable to execute /bin/rm: # 前言\n這是在使用 linux 進行正規表達式輸入指令時。會碰到 Argument list too long 的問題\n通常會出現在使用 cp, rm, mv 多個項目當中會出現\nsudo: unable to execute /bin/rm: Argument list too long 解決方法 通常使用「正規表達式」要處理檔案太多時會出現此問題，\n常見發生此問題的有 cp, rm, mv 指令，\n此時我們可以改用 find 搭配 xargs 來達到一樣的效果，\n-i：将 xargs 的數值賦予給 {} 原本的使用方式 rm ./*.jpg 修改為 find ./ -name \u0026#34;*.jpg\u0026#34; | xargs -i rm {} 雖然要輸入的指令變長了，但可以順利解決 Argument list too long 的問題！\nReference Linux中“Argument list too long”解决方法 【Linux小筆記】用xargs處理Argument list too long的錯誤以及其原理 ","date":"2021-12-15T14:46:41+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/argument-list-too-long/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】問題解決：sudo: unable to execute /bin/rm: Argument list too long"},{"categories":["450 - ffmpeg"],"content":"前言 利用 ffmpeg 來直接修改影片的對比度、亮度、飽和度\nSample code 透過修改後面的數值，可以直接改變影片的結果 (替影片上一層濾鏡的感覺)。\ncontrast：對比度 (default: 1.0) Set the contrast expression. The value must be a float value in range -2.0 to 2.0. The default value is \u0026ldquo;1\u0026rdquo;. brightness：亮度 (default: 0.0) Set the brightness expression. The value must be a float value in range -1.0 to 1.0. The default value is \u0026ldquo;0\u0026rdquo;. saturation：飽和度 (default: 1.0) Set the saturation expression. The value must be a float in range 0.0 to 3.0. The default value is \u0026ldquo;1\u0026rdquo;. ffmpeg -y -i input.mp4 -vf \u0026#34;eq=contrast=1:brightness=0:saturation=1\u0026#34; -pix_fmt yuv420p ./output.mp4 Reference Difference between FFMPEG eq (brightness / contrast / saturation) and CSS filters? ffmpeg Color Correction: Gamma, Brightness and Saturation Adjusting video contrast, brightness, saturation, and color balance with FFMPEG eq ","date":"2021-12-15T12:06:24+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/ffmpeg-contrast-brightness-saturation/","tags":["ffmpeg"],"title":"【ffmpeg】利用 ffmpeg 來直接修改影片的對比度、亮度、飽和度 (contrast, brightness, saturation)"},{"categories":["521 - Deep Learning - Optimizers"],"content":"前言 當我們想要計算一支 pytorch 程式的運算時間，\n單純只用 time.time() 好像測出來的時間感覺不太準，\n要怎麼比較客觀的得到數據呢？\n這時我們就會需要 torch.cuda.synchronize() ！\ntorch.cuda.synchronize() 說明 因為在 pytorch 程式當中，很多程式都是非同步 (unsynchronized) 進行的，\n也就是說，雖然我們已經執行了該行程式碼，\n但「不會」等待該行程式碼執行完，才繼續做下一行。\n這樣可以確保程式的效率，但也因此會出現怎麼跑時間都很短很快的錯覺！\n我們可以在想測試時間的前一行加上「torch.cuda.synchronize() 」！\n這樣就可以看到相對比較正確的執行囉！\n範例 time_start = time.time() # some pytorch code here torch.cuda.synchronize() time_end = time.time() Reference pytorch 正确的测试时间的代码 torch.cuda.synchronize() ","date":"2021-12-14T21:23:42+08:00","permalink":"https://wongwongnotes.com/posts/ai/deep-learning/deep-learning-optimizers/torch-cuda-synchronize/","tags":["Optimizers"],"title":"【Pytorch】pytorch 內計算程式運算時間筆記 torch.cuda.synchronize()"},{"categories":["014 - Youtube"],"content":"前言 最近有想上傳 youtube 4K 影片的需求，\n結果上傳很久都沒有成功 (最高畫質 1080p)，\n這邊紀錄一下錯誤處理流程。\n錯誤處理流程 可能性1：手機上傳預設畫質就是 1080p，想要更高畫質需要去更改設定 「手機版 youtube 預設上傳最高就是 1080p」，如果要更高畫質，\n我們需要依照以下步驟：\n- 點個人頭像，開啟設定 - 選擇上傳的影片 - 選擇「上傳品質」，「從 1080p 改成 完整畫質」 如下圖，點選第一個「上傳品質」。 可能性 2：上傳時如果套用 youtube 濾鏡，即使是 4K 影片上傳也會自動降至 1080p 這個是讓我之前困惑很久的一件事，\n總之如果上傳時套用 youtube 提供的濾鏡，\n會直接把最高 4K 的畫質，降到只剩下最高 1080p\n所以如果想要濾鏡的話，請直接在剪輯軟體上面用吧！！！\n","date":"2021-12-08T19:09:56+08:00","image":"https://wongwongnotes.com/images/restored/2021/12/img_7373-145x300.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/youtube/youtube-4k/","tags":["Youtube","嗡嗡粗門玩","實境解謎"],"title":"【Youtube】youtube 上傳 4K 高畫質影片失敗，處理過程紀錄"},{"categories":["521 - Deep Learning - Optimizers"],"content":"前言 這篇是我在使用 Pytorch 的時候碰到的問題，出現了以下的訊息：\nTraceback (most recent call last): RuntimeError: Expected condition, x and y to be on the same device, but condition is on cuda:0 and x and y are on cuda:0 and cpu respectively 解決方法 解決方法：\ncuda:0 指的是 GPU 位置，\n上述的內容指的是「指定的數值」並沒有被放置對應的「GPU記憶體位置」導致無法計算。\n一般來說數值會儲存於 CPU 上，\n我們需要進行額外的動作才可以再把數值從「CPU 轉到 GPU 上」。\n修改範例 只需要把值傳入對應的 GPU 即可\nDEVICE = torch.device(\u0026#34;cuda\u0026#34; if torch.cuda.is_available() else \u0026#34;cpu\u0026#34;) x = x.to(DEVICE) y = y.to(DEVICE) 只有位於同一個「GPU記憶體位置」的數值能夠互相計算。\n","date":"2021-12-08T16:23:54+08:00","permalink":"https://wongwongnotes.com/posts/ai/deep-learning/deep-learning-optimizers/condition-is-on-cuda0-and-x-and-y-are-on-cuda0-and-cpu-respectively/","tags":["Optimizers"],"title":"【Pytorch】問題解決：RuntimeError: Expected condition, x and y to be on the same device, but condition is on cuda:0 and x and y are on cuda:0 and cpu respectively"},{"categories":["310 - Linux 基礎指令"],"content":"前言 apt, apt-get, dpkg 都是我們在 Ubuntu 安裝軟體時經常使用的指令，\n這篇就來整理一下。\napt apt 為 Ubuntu 16.04 之後，新的流行安裝軟體包方式\napt install # install 安裝 apt remove # remove 移除特定安裝包 apt list # list 列出所有已安裝內容 apt-get apt-get 為舊版的 (比較少用，apt 可以包含大部份的功能)\n所以這裡就不多加討論，基本上幾乎已經被 apt 取代\n(其實我都還有在用就是XD，看安裝說明書建議怎麼安裝)\n記法：「-get」 因為要多打4個字太麻煩，漸漸被淘汰\napt-get install # install 安裝 apt-get remove # remove 移除特定安裝包 apt-get 似乎沒有 list 所有安裝包的功能，或是我沒查到？\ndpkg dpkg 為基於 debian 系統 (例如：Ubuntu) 的安裝包，我們常看到的 .deb 副檔名的檔案\n都可以用以下方式安裝：\ndpkg -i package.deb # install 安裝 .deb 檔案 dpkg -r package # remove 移除特定安裝包 dpkg -l # list 列出所有已安裝內容 Reference apt 和 apt-get的區別 【Ubuntu】apt與dpkg指令介紹 apt 和 apt-get 之間有什麼區別？ 手動安裝 Could I remove apt with apt? ","date":"2021-12-03T12:11:17+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-ubuntu-apt-apt-get-dpkg/","tags":["Bash","Docker","Linux","Ubuntu","基礎指令"],"title":"【Linux 基礎指令 #4】Ubuntu 上安裝軟體的方式筆記 apt, apt-get, dpkg 總整理"},{"categories":["521 - Deep Learning - Optimizers"],"content":"前言 這篇是我在使用 Pytorch 的時候碰到的問題，出現了以下的訊息：\nNo module named \u0026#39;ranger\u0026#39; 解決方法 Ranger 在此指的是 Deep Learning 的一種 Optimizer，\n由於目前還不能夠透過 PyPI 安裝，\n只能手動去官方的 github 安裝。\n這邊就不多介紹這種 Ranger Optimizer 的細節了，著重在解決問題\n去官方的 github：\nRanger-Deep-Learning-Optimizer 依照官方說的 github，進行以下的安裝指令：\ngit clone https://github.com/lessw2020/Ranger-Deep-Learning-Optimizer cd Ranger-Deep-Learning-Optimizer pip install -e . 使用方式 (Ranger 2021) from ranger21 import Ranger21 optimizer = Ranger21(model.parameters(), lr=learning_rate, num_epochs=num_epochs, num_batches_per_epoch = BATCH_SIZE) ","date":"2021-12-01T18:08:57+08:00","permalink":"https://wongwongnotes.com/posts/ai/deep-learning/deep-learning-optimizers/pytorch-no-module-named-ranger/","tags":["Optimizers"],"title":"【Pytorch】問題解決：No module named 'ranger'"},{"categories":["559 – Pytorch 問題解決"],"content":"前言 這篇是我在使用 Pytorch 的時候碰到的問題，出現了以下的訊息：\nAttributeError: \u0026#39;int\u0026#39; object has no attribute \u0026#39;float\u0026#39; 解決方法 我碰到這個問題的時候，最後解決問題的方法是發現根本沒有 load 資料進來！\n發生錯誤的程式碼部份：\nprint(type(mae)) mae = mae.float() / num_examples 主要是第二行，mae 無法進行 float() 的動作，\n實際原因是因為資料量為 0，導致 type 不為 Tensor\n導致 print(type(mae)) 得到 \u0026lt;class \u0026lsquo;int\u0026rsquo;\u0026gt;，\n而正確的應該是 \u0026lt;class \u0026rsquo;torch.Tensor\u0026rsquo;\u0026gt;\n本次結論 請檢查資料有沒有成功的 load 進來！！！\n","date":"2021-12-01T17:38:31+08:00","permalink":"https://wongwongnotes.com/posts/ai/frameworks/pytorch-troubleshooting/pytorch-int-object-has-no-attribute-float/","tags":[],"title":"【Pytorch】問題解決：AttributeError: 'int' object has no attribute 'float'"},{"categories":["310 - Linux 基礎指令"],"content":"前言 有時候太依賴 GUI 之後，當我們面對純 terminal 指令環境的電腦會不知道該怎麼操作重新開機與關機，這邊做一下筆記。\n關機 sudo shutdown now 重新開機 sudo reboot 或\nsudo shutdown -r now # 重新開機 記法：-r = reboot Reference 【Linux】linux/ubuntu/mac 基礎終端機 (terminal) 指令 \u0026amp; 基礎知識總整理，初學者/新手 必須知道的基礎指令 \u0026amp; 基礎知識大全（持續更新） ubuntu的常用關機、重開機指令 ","date":"2021-12-01T12:32:47+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-ubuntu-shutdown-reboot/","tags":["Linux","sshfs","基礎指令"],"title":"【Linux 基礎指令 #2】shutdown, reboot - 在 ubuntu terminal 中執行關機、重新開機指令"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是在使用 rapidjson 無法正常運作的解決辦法，出現以下的訊息\n/opt/work/lib/rapidjson/document.h:885: rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::ConstMemberIterator rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::MemberEnd() const [with Encoding = rapidjson::UTF8\u0026lt;\u0026gt;; Allocator = rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt;; rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::ConstMemberIterator = rapidjson::GenericMemberIterator\u0026lt;true, rapidjson::UTF8\u0026lt;\u0026gt;, rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt; \u0026gt;]: Assertion `IsObject()\u0026#39; failed. 解決方法 step1. 檢查 json 格式 碰到這個問題的時候，\n第一點我想到的是先去確認 json 格式有沒有錯誤，\n結果發現沒有！！！\nstep2. 無法取得正確的資料夾路徑 後來發現，因為我使用 docker container 作為開發環境，\n在修改資料夾的過程中，把 mount 資料夾進行了刪除的動作，\n因此，在 container 中的 docker 無法正確讀取資料夾。\n我將 docker stop 並重新 start 後，\n讓 mount 資料夾路徑可以被重新取得，問題解決！\n","date":"2021-11-30T18:26:07+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/assertion-isobject-failed/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】問題解決：rapidjson::GenericMemberIterator\u003ctrue, rapidjson::UTF8\u003c\u003e, rapidjson::MemoryPoolAllocator\u003c\u003e \u003e]: Assertion `IsObject()' failed."},{"categories":["Popworld 蹦世界"],"content":"實境解謎簡介 由「Popworld 蹦世界」所出品的實境遊戲《巡行者》，\n這次的主場位於陽明山的「陽明書屋」，平時大家去陽明山玩耍時，\n應該很少會特別跑到像陽明書屋這樣的地方，\n但雖然這裡不是廣為人知的陽明山熱門景點，卻有著許多美景與建築，\n絕對不遜色於那些陽明山的熱門景點!\n這款遊戲因為遊玩場域比較特別 - 「陽明書屋巡守步道」過去是「軍事重地」，是士兵巡邏的路線，人員進出都要嚴格控管，所以要「先線上跟陽明書屋預約場次」才能遊玩哦！\n預約傳送門！Let\u0026rsquo;s Go! ：https://popworld.cc/guide/5706/preview\n這次要感謝「Popworld 蹦世界」特別與「陽明書屋」合作推出這款實境遊戲《巡行者》，\n題目都充分的讓人感受到製作團隊的用心，\n並與現場場地結合，一關一關的引導我們逛完「陽明書屋」，\n體驗遊戲解謎的樂趣，還能順便逛完這附近的重要景點，真的讓人樂在其中！\n「實境解謎」主打可以去實際景點旅遊，又能夠同時體驗解謎的樂趣。\n也就是說不同於密室逃脫，我們主要的活動都在室外，如果喜歡解謎又不喜歡待在室內，\n或者很喜歡旅遊又想增添一些趣味性，都很適合來玩這樣的遊戲!\n△ 這次我們進入山林中，尋找謎題的解答!\n喜歡實境解謎嗎？作者這裡也推薦一些有趣的實境解謎心得文，歡迎參考哦：\n遊戲相關 以下是自己的玩完後的一些心得，老樣子，有優點、有缺點都會說，\n「沒有爆雷」的部分，留給還沒遊玩的朋友們，(除了大約的行走距離提示XD)\n一切都為「個人\u0026amp;團隊成員」們的「真實體驗」，絕沒有多加修飾XD\n題目類型：「融合實景的解謎、與透過 AR 與題目互動」 這款遊戲主要的題目類型是「融合實景的解謎、與透過 AR 與題目互動」，\n不得不說製作團隊將遊戲內容藏在陽明書屋附近的各個角落，\n藉由引導的方式，帶領玩家們探索附近的地點，\n不時有題目結合 AR 的互動，我非常喜歡這樣的設計!\n小提醒: 因為解謎會需要用到 GPS 及網路，記得「開啟手機 GPS 定位 與 行動網路」幫助定位哦! △ 運用手機的 AR 功能，解開各種的謎題吧!\n題目數量 主要題目一共有7題，分散在「陽明書屋」附近的七個地方，\n整體而言，遊玩的過程覺得非常的輕鬆，幾乎都是在附近可以走到的距離，\n很適合搭配這樣的行程邊走邊逛，把整個陽明書屋逛過一遍。\n(最喜歡這種邊玩邊旅遊的感覺XD)\n解題時間: 一般人大約 2 ~ 3 小時 既然是一款主打「實境解謎遊戲」，\n也就是說我們可以瘋狂解題，拚最佳解題時間，(最快好像看到有人兩個小時以內)\n也可以不用那麼拚解題時間，慢慢觀光，邊觀光邊享受解題的樂趣。\n但就如先前所說，這邊因為環境比較特殊，如果超過平均時間（2-3小時）還沒破關，可能會有書屋服務站的人員來關心，他們怕玩家真的遇到危險了XDD\n這款很輕鬆、給小朋友都能玩，最大的特色我會放在 「透過遊戲帶領我們參觀各個地景」的表現！\n以目前玩過的幾款遊戲來說，「Popworld 蹦世界」相對別家更著重運用「AR 結合實際地景的技術」，\n來表現出當地的特色景觀。\n相比其他家，會藉由「文字、圖片」作為媒介帶你們去逛地景，\n但果然還是「AR 結合實際地景」的場地帶入感會明顯的強更多！\n因為「人就在那裡」才能這樣做啊！！！\n謎題難度(解題需要的背景知識): 適中(3/5) 這次的題目難度我覺得非常的剛剛好!\n不會太簡單，也不會太難，很適合三五好友們一起來解謎!\n之前有遇到太簡單的題目，一眼看就知道下一關要去哪，這樣顯得沒什麼解謎的意思，\n也有遇到太難的題目，不過與其說太難不如說太不直覺，這次完全沒碰到這樣的情況呢!\n只有碰到我們自己把題目想的太難的情況XD，難度真的適中，\n如果想得太複雜了記得先放鬆一下思緒，重新想想適中的題目難度大概會是怎麼樣的吧!\n謎題難度(解題過程夠不夠直覺): 適中(3/5) 大部分題目都是「可以經過稍微思考一下就得到答案」的，\n不要像我們一樣，自以為太聰明就往太複雜的地方想XD\n我特別重視題目的直覺度更勝過難度，因為有時候當知道答案後，\n會發現題目本身不難，但因為要「代入出題者的思維才能解開」，\n這種我就覺得顯得不夠直覺了，會是個小缺點，\n應該要是「直覺上就能推理出答案，而不用代入出題者思維」。\n這次的遊戲「完全沒有」這樣的情況哦!!!\n△ 究竟是魔鬼真的藏在細節處? 還是只是我們自己想太多呢? (全部人擠在裡面在幹嘛啦XDD)\n謎題驚喜度：我很享受在「山林裡面穿梭尋找線索的感覺」 (5/5) 這次的場地選在深山當中，因此在深山中尋寶更顯得有「找到寶藏的味道」，\n我們每發現一樣藏在山林中的線索，都是一種驚喜。\n不過取而代之的代價就是，畢竟是山林嘛\u0026hellip; 總會有些蚊蟲的很煩人XD\n△ 穿梭於山林之中找尋線索的感覺，真的很有實境解謎的體驗感!\n謎題故事性: 比較像在跑大地遊戲，不過有用簡單的故事將劇情串起來 (3/5) 這次的遊戲設計本身比較像在玩跑大地遊戲的感覺，\n因此劇情本身有，但相對來講好像又不是那麼的重要XD\n我自己玩完後對遊戲的內容(玩法)印象非常深刻，\n但真的不記得跑過什麼劇情了，我只記得有能夠描述當下我們在幹嘛的劇情XD\n一些個人的小建議 (1) 遊戲設計本身沒什麼大問題，但部分關卡好像在跑連續的大地遊戲 (跟實境的互動差了一點) 這點就算是比較雞蛋裡面挑骨頭的建議了，遊戲本身我覺得已經做得非常的優質了!!!\n會說這點的原因主要是因為我們有發現，其實大部分的遊戲內容，\n「只是把關卡的素材放在那邊」，那其實只要稍微改一下題目，或許放哪邊都可以，\n我們認為更好的實境解謎體驗是「一定要看當下的實境有什麼，才能解出題目」，\n這個在其中有幾道題目表現得非常不錯，但由於不能暴雷的關係XD，\n再給玩家們自行體驗吧XD\n但我必須說這真的不是什麼大問題，只是有結合到實境要素的題目，\n都是我們團隊在回程討論時最熱烈的內容。\n畢竟，如果只是把關卡素材擺在哪，其實密室也能玩XD\n(2) 建議中途可以新增「吃飯暫停」的功能，畢竟大家也要吃飯但又怕影響成績 XD 這邊其實也算是因為有遊玩過別家的經驗，\n當時有印象深刻有一個「吃午餐時間」的功能也不影響計分，\n這樣其實可以讓玩家們很「安心的吃飯」，也可以「好好的稍微中場休息」一下。\n會非常建議能加入這個功能哦!\n(3) 「不是製作團隊的問題」山林有蚊蟲，請玩家自己注意!!! 先做好準備! (不要穿短褲, 噴香水\u0026hellip;) 這個是山林中玩遊戲天然就會有的問題，\n寫在這邊只是想提醒讀者XD，\n「記得先做足防蚊蟲準備再來玩啊! 不然可能玩的過程中會因為蚊蟲感到很煩燥呢!」\n△ 進入山林之中找尋謎題的線索，也要注意蚊蟲的問題，才能有更好的遊戲體驗哦!\n-\u0026gt; 補充: 一些我很喜歡的出題團隊「小心思」~ (1) 用心的細節 - 遊戲結束後的 AR 彩蛋 作為一款主打「AR 實境解謎」的遊戲，果然最後的獎勵也與「AR 有關」可以讓大家合影XD\n我覺得十分的有趣，也能讓玩家們很有「解完這款遊戲」成就感!!!\n-\u0026gt; 玩完後想推薦給什麼樣的人：「旅遊為主，解謎為輔」的朋友!!! 這款我會想推薦給想「旅遊為主，解謎為輔」的朋友!!!\n特別是陽明山都已經熱門景點跑到膩了，\n但我猜陽明書屋這附近可能去都沒去過吧XD\n這款謎題難度適中，很適合「慢慢邊旅遊，慢慢解XD」，順便帶領你逛逛陽明書屋的周遭!\n題目的設計，都有感受到製作團隊的用心。\n既然是主打「實境解謎遊戲」，觀光也是重點中的重點，\n陽明書屋附近有很多優美的山林景色，還有據由歷史意義的各種建築供大家參觀，\n比起其他熱門景點來說，是個相對不會那麼人擠人又悠閒的地方，\n很適合假日跑來這邊放鬆 (先別跟我吐槽地點偏僻的問題，這款遊戲就是打算帶領你們走走這附近的XD)，\n如果不是為了拼最佳排名，很建議跟著題目的腳步一起玩過陽明書屋哦！\n推薦這款 Popworld 蹦世界 -實境遊戲《巡行者》給想要來陽明山的「陽明書屋」附近逛逛的朋友們!\n△ 歡迎大家一起來陽明書屋探險哦!\n如果在找密室逃脫的遊戲評價，這裡還有一些當初我自己有參考的密室逃脫心得文，也歡迎一併參考： 其他團體遊戲推薦\n","date":"2021-11-23T01:01:23+08:00","image":"https://wongwongnotes.com/images/restored/2021/11/img_3581-1024x768.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/popworld/yangming-bookstore/","tags":["Popworld 蹦世界","嗡嗡粗門玩","實境解謎","蹦世界"],"title":"【實境解謎】陽明書屋實境遊戲《巡行者》遊玩後心得 (無雷) - 解謎遊戲推薦 | Popworld 蹦世界"},{"categories":["114 - Python 字串處理"],"content":"情境敘述 在設定每日看兩篇財經新聞後的現在，因財經政治領域的單字苦手而進度嚴重落後，在一一查詢生字的過程中想到了之前系統性處理文本資料的流程，或許能讓自己優先處理重點生字來加速對文章的理解，而開始下列的操作。\n狀態敘述 僅使用單篇文章(先忽略統計操作帶來的助益) 未使用任何字集 無文章理解 不參考他人作法下的直覺(?)操作 使用套件 import re from collections import Counter 流程 直接貼入文章後第一步要做的是切割文字成字集，由於英文字元稀少相當的友善，我們可以用\nre 的反選擇將非字母和數字的字元通通挑出做為切割依據(*p1)\nsyb_set = \u0026#34;\u0026#34;.join(list(set(re.findall(r\u0026#34;\\W\u0026#34;,article)))) ```text \u0026lt;mark\u0026gt;syb_set\u0026lt;/mark\u0026gt;得到如此的字串 ```python \u0026#34;:©. ( ”—‘)“,?]/$[- ’\u0026#34; ```text 但由於特定字的出現目前要手動跳脫成以下字串(*p2) ```python \u0026#34;[:©.\\u2009(\\xa0”—‘)“,?\\]/$\\[\\- ’ ]\u0026#34; ```text 此時即可將文章切割成文字清單(文章順便全部改小寫方便計數) ```python word_raw = re.split(\u0026#34;[:©.\\u2009(\\xa0”—‘)“,?\\]/$\\[\\- ’ ]\u0026#34;,article.lower()) ```text 引入Counter的特性直接查看計數後的結果 ```python word_count = Counter(word_raw) word_count ________output________ Counter({\u0026#39;\u0026#39;: 336, \u0026#39;5tn\u0026#39;: 1, \u0026#39;a\u0026#39;: 49, \u0026#39;abide\u0026#39;: 1, \u0026#39;able\u0026#39;: 1, \u0026#39;about\u0026#39;: 3, \u0026#39;accenture\u0026#39;: 1,... ```text 挑掉不必要的部分如空字串、包含數字的詞和stop word，停用詞可從\u0026lt;mark\u0026gt;word_count.keys()\u0026lt;/mark\u0026gt;中手動挑掉(自己造一個清單(*p3))，其他部分連同停用詞清單在重建字典時利用條件處理完成 ```python stop_word = [\u0026#39;and\u0026#39;, \u0026#39;are\u0026#39;, \u0026#39;the\u0026#39;, \u0026#39;have\u0026#39;, \u0026#39;in\u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;but\u0026#39;, ...] cleaner_word_count = Counter({k:v for k,v in word_count.items() if (not bool(re.search(\u0026#34;\\d\u0026#34;,k))) and \\ (k != \u0026#34;\u0026#34;) and \\ (k not in stop_word)}) ```text 如此就可得到陽春版的詞集以及詞頻了 可用 \u0026lt;mark\u0026gt;cleaner_word_count.most_common()\u0026lt;/mark\u0026gt; 查看依照詞頻排序的結果。 # 成果 - 簡易的文章詞彙、詞頻呈現。 - \u0026lt;del\u0026gt;一日英文的閱讀時間改成寫玩具所造成的壓力感\u0026lt;/del\u0026gt; # 問題點 - p1：縮寫和其他可能帶有符號的詞彙會被不當處理，字的變形會被視為不同類。 - p2、3：非完全自動，帶入字典、詞集的必要性高。 - \u0026lt;del\u0026gt;似乎對學英文沒什麼幫助\u0026lt;/del\u0026gt; - 若以生字學習為目標，在停用詞外可能還要附帶熟練度的抽象資料來影響呈現結果。 # 後續方向 - 列入足夠多的文章後開始帶入統計手法。 - 解決目前未自動化的部分 - 完善詞集建立。 - (遠程)嘗試連結具\u0026lt;mark\u0026gt;記憶曲線資料\u0026lt;/mark\u0026gt;的應用。 - (遠程)嘗試建構英文學習的輔助閱讀介面(\u0026lt;mark\u0026gt;視覺化\u0026lt;/mark\u0026gt;問題(但目前想到的都是英文教學者要求不要做的事，像是直接顯示中文之類的XD)) # 程式碼片段 ```python import re from collections import Counter as ct # 帶入自己的文章 article = \u0026#34;\u0026#34;\u0026#34;... \u0026#34;\u0026#34;\u0026#34; # 此動作尚未自動化 #syb_set = \u0026#34;\u0026#34;.join(list(set(re.findall(r\u0026#34;\\W\u0026#34;,article)))) syb_set = \u0026#34;[:©.\\u2009(\\xa0”—‘)“,?\\]/$\\[\\- ’ ]\u0026#34; words_raw = re.split(syb_set, article.lower()) words_count = ct(words_raw) # 需自行建構 stop_word = [] cleaner_words_count = ct({k:v for k,v in words_count.items() if (not bool(re.search(\u0026#34;\\d\u0026#34;,k))) and \\ (k != \u0026#34;\u0026#34;) and \\ (k not in stop_word)}) cleaner_words_count.most_common() ```text \u0026lt;!-- Post ID: 4694 Post Date: 2021-11-14 14:27:57 Categories: 114 - Python 字串處理 Tags: 114 - Python 字串處理, Python, 字串處理, 114 --\u0026gt; ","date":"2021-11-14T14:27:57+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/words-count-for-eng-article/","tags":["Python","字串處理"],"title":"【英文文本處理】單字學習 week 1 - 形成文章詞頻統計"},{"categories":["360 - Linux 網路遠控"],"content":"前言 其實這篇要講的東西與另外一篇大同小異：\n【Linux】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (anydesk ssh command line)\n不過因為 Windows 想要 ssh 需要多設定一些東西，所以就有了這篇。\ndescription: \u0026ldquo;紀錄在 Windows 上用 AnyDesk 與 SSH 進行遠控 — PuTTY、工具整合、遠端執行。\u0026rdquo; 我們要遠端開發的時候，最經常會需要的東西就是 VPN，\n透過 VPN 能實現兩個不同網域能在同一個區網下的概念。\nAnydesk 或是 teamviewer 強大的地方就在於，各種網路的跳板，\n他們都已經幫我們實現了，這樣我們就可以簡單地實現不同區網間的電腦連線。\n現在我們想基於 Anydesk 這個方便的機制，以 Anydesk 作為 VPN 跳板，\n直接與遠端主機進行 ssh 連線。\n補充：在某些遊戲中，我們會使用類似 hamachi 或 gcc LAN (已關閉服務)，\n達到虛擬區網的效果。\nWindows 專屬步驟 - 開啟 ssh 連線方式 (外對內、內對內 (localhost)) Step 1. 安裝 OpenSSH 相關的設定 step 1-1. 左下角搜尋，找到「應用程式與功能」 (也可以直接從控制台中找) step 1-2. 左下角搜尋，找到「應用程式與功能」 (也可以直接從控制台中找) 點選「選用功能」，因為這功能一般人不會用到，\n所以 Windows 藏的很深也是很正常的。\nstep 1-3. 從選用功能中新增 OpenSSH 相關功能 (建議都裝，至少也要裝伺服器) 這邊就是要來安裝 Windows 做的原生 ssh 支援功能，\n可以先從下方的「已安裝功能」嘗試搜尋 OpenSSH 看看有沒有曾經裝過。\n注意：下面的搜尋是搜尋「已安裝功能」，不要像我第一次一樣以為這邊就是「搜尋安裝」的地方了XD\nstep 1-4. 從上方的「新增選用功能」，安裝 OpenSSH 相關功能 這邊因為我已經裝過了，沒辦法進行圖片示範，\n總之應該會搜尋到一些可以安裝的東西。\nstep 2. 從 powershell 中開啟 ssh 連線的相關設定 step 2-1. (重要) 以「系統管理員」執行 Windows PowerShell 這邊我會「右鍵」左下角的 Windows 符號，選擇以「系統管理員」執行 Windows PowerShell\nstep 2-2. (重要) 輸入指令已開啟 ssh 服務 下面指令輸入完後，就可以開啟 ssh 服務了\nStart-Service sshd step 2-3. (選用) 自行決定要不要開起自動啟用 ssh 服務 因為我會很常使用 ssh 服務，這邊我設定給他自動啟動。\nSet-Service -Name sshd -StartupType \u0026#39;Automatic\u0026#39; 更：但後來有點擔心會有資安問題，我就把它關閉改成要用時自動啟動了。 (手動啟用)\nSet-Service -Name sshd -StartupType \u0026#39;Manual\u0026#39; 這邊可以參考 Microsoft 官方文件，網址： Microsoft 官方文件\n到這邊基本上 ssh 的部分就都搞定了，真的很累XDDD\n正式建立連線 - (共通步驟) step 0. 建立連線 將我們本機端的連線 port 設為 9000 (自訂，可更改) 遠端主機的 port 設定為 22 (ssh 固定 port，不可更改) 透過 ssh 連至 host step 1. ssh 連線至遠端 terminal 在「anydesk 已建立連線」的狀態下 (可以只單純使用檔案傳輸模式)，\n在終端機輸入以下指令。\n註：ubuntu 是「遠端主機的 hostname」\nlocalhost 是本地主機的 (我們用 9000 去連對方的 22)\nssh -p 9000 ubuntu@localhost 結果圖 總之就是用 ssh 成功連上遠端電腦了!\n透過 VScode 連線至遠端 host step 1. 使用 VScode ssh 連線至遠端 (有 GUI 介面) 打開 VScode 左下角的遠端連線 (需要先安裝 ssh 相關的套件)\n輸入以下內容，即完成連線： ubuntu@localhost:9000 示意圖： 透過 VScode 連線至遠端 container step 1. terminal 建立 ssh 反向通道，使本地可查看遠端 docker 內容 ssh -p 9000 -nNTL localhost:23750:/var/run/docker.sock ubuntu@localhost step 2. VScode 設定的部分 基本的 docker 擴充功能必須先安裝好，\n而且 local 也需要具備有 docker 的功能，\n如果不知道如何在 windows 中啟用 docker 功能可以參考這篇：\nhttps://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/\nstep 2.1 打開「設定」，我們準備進行修改 我們打開並修改設定「settings.json」，\n點選「檔案」-\u0026gt;「喜好設定」中的「設定」。\nstep 2.2 在上方搜尋「settings.json」(不用全輸入)，打開「settings.json」，我們準備進行修改 或者也可以點擊右上角的符號，也可以叫出 「settings.json」 step 2.3 在「settings.json」中加入遠端 container port 的設定 在「settings.json」中加入這行\n\u0026#34;docker.host\u0026#34;: \u0026#34;tcp://localhost:23750\u0026#34; 示意圖： 只需要確保有加入紅框那行即可，\n其他行代表的是其他設定，可以不用管。\n完成結果 我們可以在 local 自己的 VScode，\n可以透過「Docker extension」直接看到遠端 container 內的資料。\n(注意：不需要再另外經由 VScode 的 ssh 至遠端)\n會顯示遠端的 container Reference 【Docker】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發 SSH through TCP tunneling SSH 遠端連線回家中的 Windows 電腦 ","date":"2021-11-13T23:41:23+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/vscode-remote-container-3.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/windows-anydesk-ssh/","tags":["Linux","sshfs","網路遠控"],"title":"【Windows】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (Windows anydesk ssh command line)"},{"categories":["360 - Linux 網路遠控"],"content":"前言 我們要遠端開發的時候，ssh 是一個我們經常使用且方便的功能，\n而 mac 為了安全性考量，預設並沒有開啟 ssh 的功能，\n這篇文章就是 mac 開啟 ssh 功能的筆記。\nMac 專屬步驟 - 開啟 ssh 連線方式 (外對內、內對內 (localhost)) step 1. 打開系統偏好設定，找到共享 (也可以右上角直接搜尋 「internet 共享」) step 2. 打開系統偏好設定，找到共享 (也可以右上角直接搜尋 「internet 共享」) 打勾遠端登入，即可開啟 mac ssh 功能\n(開啟後請特別留意資安問題，開啟這個之前務必知道自己在做什麼。)\nReference Mac上無法ssh localhost ","date":"2021-11-13T02:35:27+08:00","image":"https://wongwongnotes.com/images/restored/2021/11/mac-ssh.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/ssh-mac-localhost/","tags":["Linux","sshfs","網路遠控"],"title":"【Mac】在 Mac 開啟 ssh 的方法，從外部主機 ssh 連線至 Mac，ssh Mac localhost"},{"categories":["360 - Linux 網路遠控"],"content":"前言 其實這篇要講的東西與另外一篇大同小異：\n【Linux】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (anydesk ssh command line)\n不過因為 Mac 想要 ssh 需要多設定一些東西，所以就有了這篇。\ndescription: \u0026ldquo;紀錄在 Mac 上用 AnyDesk 與 SSH 遠控方案 — 共享設定遠端登入、port 9000 轉 22、ssh -p 9000 連線的完整步驟。\u0026rdquo; 我們要遠端開發的時候，最經常會需要的東西就是 VPN，\n透過 VPN 能實現兩個不同網域能在同一個區網下的概念。\nAnydesk 或是 teamviewer 強大的地方就在於，各種網路的跳板，\n他們都已經幫我們實現了，這樣我們就可以簡單地實現不同區網間的電腦連線。\n現在我們想基於 Anydesk 這個方便的機制，以 Anydesk 作為 VPN 跳板，\n直接與遠端主機進行 ssh 連線。\n補充：在某些遊戲中，我們會使用類似 hamachi 或 gcc LAN (已關閉服務)，\n達到虛擬區網的效果。\nMac 專屬步驟 - 開啟 ssh 連線方式 (外對內、內對內 (localhost)) step 1. 打開系統偏好設定，找到共享 (也可以右上角直接搜尋 「internet 共享」) step 2. 從共享中開啟 ssh 連線的設定 (也可以右上角直接搜尋 「internet 共享」) 打勾遠端登入，即可開啟 mac ssh 功能\n(開啟後請特別留意資安問題，開啟這個之前務必知道自己在做什麼。)\n正式建立連線 - (共通步驟) step 0. 建立連線 將我們本機端的連線 port 設為 9000 (自訂，可更改) 遠端主機的 port 設定為 22 (ssh 固定 port，不可更改) 透過 ssh 連至 host step 1. ssh 連線至遠端 terminal 在「anydesk 已建立連線」的狀態下 (可以只單純使用檔案傳輸模式)，\n在終端機輸入以下指令。\nssh -p 9000 ubuntu@localhost 透過 VScode 連線至遠端 host step 1. 使用 VScode ssh 連線至遠端 (有 GUI 介面) 打開 VScode 左下角的遠端連線 (需要先安裝 ssh 相關的套件)\n輸入以下內容，即完成連線： ubuntu@localhost:9000 示意圖： 透過 VScode 連線至遠端 container step 1. terminal 建立 ssh 反向通道，使本地可查看遠端 docker 內容 ssh -p 9000 -nNTL localhost:23750:/var/run/docker.sock ubuntu@localhost step 2. VScode 設定的部分 基本的 docker 擴充功能必須先安裝好，\n而且 local 也需要具備有 docker 的功能，\n如果不知道如何在 windows 中啟用 docker 功能可以參考這篇：\nhttps://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/\nstep 2.1 打開「設定」，我們準備進行修改 我們打開並修改設定「settings.json」，\n點選「檔案」-\u0026gt;「喜好設定」中的「設定」。\nstep 2.2 在上方搜尋「settings.json」(不用全輸入)，打開「settings.json」，我們準備進行修改 或者也可以點擊右上角的符號，也可以叫出 「settings.json」 step 2.3 在「settings.json」中加入遠端 container port 的設定 在「settings.json」中加入這行\n\u0026#34;docker.host\u0026#34;: \u0026#34;tcp://localhost:23750\u0026#34; 示意圖： 只需要確保有加入紅框那行即可，\n其他行代表的是其他設定，可以不用管。\n完成結果 我們可以在 local 自己的 VScode，\n可以透過「Docker extension」直接看到遠端 container 內的資料。\n(注意：不需要再另外經由 VScode 的 ssh 至遠端)\n會顯示遠端的 container Reference 【Docker】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發 SSH through TCP tunneling ","date":"2021-11-13T01:58:41+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/anydesk-vpn.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/mac-anydesk-ssh/","tags":["Linux","sshfs","網路遠控"],"title":"【Mac】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (Mac anydesk ssh command line)"},{"categories":["011 -【嗡嗡開箱】"],"content":"前言 平常喜歡拍照，之前買了 insta360 one X2，作為我的第一台 360 全景相機，\n後來覺得雖然 insta360 可以「模仿」空拍機視角，但實際上那個「高度感」還是相差非常多的！\n因此這次做好一些研究後，決定直接購買了這台 DJI AIR 2S！\n因為這是我自己買的XD，也沒有收過任何的業配\n所以有空再來補體驗心得XD，先讓我用一陣子 (或有人想看再下面留言給我XD)\n這邊先暫時做一個純開箱的照片紀錄\n我自己事前查到的評價 \u0026amp; 相比 DJi Mini 2 最後決定要先買台 網路上的討論非常多，但我非常推薦在購買的時候，當下直接與店員也當面討論一下，\n也許店家會更了解你的需求！\n我當下就是 DJI Air 2S 與 DJI mini 2 兩台正在比較要買哪台\n(秉持著科技就是要買新不買舊的概念 Air 2 很早就不在我考慮的範圍)\nDJI mini 2 對我來說有以下優點 1. 便宜，CP 值高，摔壞相對來講比較不心疼 當下做很多功課，知道做為一個「空拍機新手」，炸機是多少一定會有的經歷，\n如果先買比較便宜的一台當作練習？等習慣了再換比較高級的？\n然後這樣就算炸機也筆記不會心痛\n2. 方便攜帶，體積較輕，免註冊的優點 (但這個真的還好) 畢竟是 mini，只有 249 克，非常輕又方便攜帶\n然後這邊特別提一下依照台灣現有法規， 249 克以下的空拍機使用時是不用註冊的。\n但是註冊這件事情很簡單，我當時購買在電腦不到 5 分鐘就完成了，\n註冊有點類似買車要登記那樣，就是一種對這台機器負責任的感覺，其實登記並沒有什麼麻煩的問題。\n我覺得 249 克比較大的影響應該是要「帶出國的部分」，台灣地區確實註冊簡單，\n但非 249 克帶出國的話，還要特別在當地註冊，這個就會因為外國的關係麻煩很多了，\n甚至還要等申請通過。\nDJI air 2S 對我來說有以下優點 1. 整體性能較好 (畢竟也是價格反映出來的XD) 當下做很多功課，知道做為一個「空拍機新手」，炸機是多少一定會有的經歷，\n如果先買比較便宜的一台當作練習？等習慣了再換比較高級的？\n然後這樣就算炸機也筆記不會心痛\n2. 大師鏡頭極度對空拍機新手友善，讓空拍機新手也能很輕鬆的有如大作般的航空運鏡 大師鏡頭是 air 2S 的最新功能，我真的有被這個功能燒到，\n第一個是「全自動航拍運鏡」，真的新手不知道該怎麼運鏡，這個一鍵就搞定 (可能甚至比特別操作的運的還好) 第二個是「過程中自動的避障，避免炸機的危險」，雖然還沒有到全方向避障，但自動避障又全程自動運鏡，真的是對新手極度友善！！！ 最後是「自動剪輯」，這個對於想要快速出產作品的人真的是福音。 (懶人覺得讚) 3. 四方向的避障，比起 mini 2 的下避障，對新手會是更能避免炸機的風險的 更多的避障，其實才是對新手來說更需要的，這邊剛好店家就有提到，\n(這邊感謝「懷爸瘋科技」的專業建議)\n確實越高價位避障做得越好，但更專業的操作者其實反而相對新手更不需要依賴這個XD，\n因此新手應該要買更好的避障，卻因為入門想先買個便宜的，\n買了避障功能較少的，因此容易炸機後又來買新的。\n到後來多換個幾次，其實整體花費差不多 (當然也要很經常使用啦)\n只是 air 2S 確實比較難壞是一定的！ 包含炸機的部分！\n最終決定購買 air 2S 的理由 「價位」與「長期保存」的取捨 我的預算確實當初想抓低一點，希望能控制在 30000 以內\nmini 2 套裝價大約 18000 air 2S 套裝價大約 44180 這邊補充一個對價位的感覺：\n一般來說 20000 以下的機型，比較偏入門，甚至有些高手會說比較像玩具的感覺XD 而 40000~60000 大約是一般版，就是一般消費者使用建議都會買到這個價位，除非預算抓很緊才會往上面的區間買。 100000 左右或以上就是專業機型了，給專業的攝影師所使用的，一般的消費者想要夠高檔的也可以用啦XD。 但真的好貴XD 整理來說我覺得，下一個等級的機型價位，幾乎都可以買上一個等級的兩台左右了XDDD\n但價格來說，確實不是我最硬的考量，因為個人使用東西的習慣是非常愛惜東西，\n很多東西都是用了好久都沒有壞也沒有要換，最後都是收著。\n因此我的消費策略會是直接買比較好的，畢竟我很有自己我可以把他照顧的「很久」，\n長期來看應該是比較划算的選擇。\n「四向避障」對空拍機新手友善，而且購買後我也好幾次驗證了這件事！ 此外，因為「四方向的避障」對於新手非常友善。\n如果我購買 mini 2 ，因為操作不當而導致炸機的機會比較高，\n因此參考店家的建議，要買、肯用心保存，推薦直接買好一點的。\n很多攝影師，都會更建議空拍新手要多花些錢買有避障功能才能避免炸機。\n而後來確實有幾次出門的時候，我都被這個「避障」拯救了我的飛機，\n真的好險當初有做出這個正確的決定！！！ 不然可能現在買 mini 2 可能早就炸機了！\n「大師鏡頭」對空拍機新手友善，不知道飛機該怎麼運鏡，他都幫你運好了！ 另外一個空拍機新手會面臨到的問題，就是不知道「該怎麼運鏡」，\n第一次使用就會有深切體會了，\n雖然 mini 2 一樣內建就有很多飛行的範本，\n不過都沒有這個「大師鏡頭」運出來的「整套」流暢運境的感覺還要好。\n因此綜合以上理由，我最後購買了 air 2S ！！！\n給一樣猶豫於 air 2S 與 mini 2 的人，一些其他作者的心得分享 https://www.youtube.com/watch?v=2xsfQ2uDWA8\n此外也推薦他的炸機經驗分享！ 很多很適合新手飛行前可以多注意的地方！\nhttps://www.youtube.com/watch?v=KeAAv39fBsU\n購入價 我是在 懷爸瘋科技 買的，購入價 $55180，分六期，\n我買的是「dji air 2s with smart controller」的組合，\n原因也是因為店家推薦，盡量不要把手機與操作飛機的機型混在一起，\n有時候飛機正在飛，突然手機一個有什麼事情要處理，\n飛機亂飛會非常危險！！！一定要注意！！！\n店內首次開箱 由於我是先在店內進行開箱的作業，有一些我比較不懂的東西就直接問，這樣我也比較放心\n△ 在現場先進行一些初步的檢查與設定\n此外，我也是同時在店內先進行一些空拍機註冊的設定，還有獲得一些經驗分享之類的，\n這邊真的是線上購買不會有的額外服務，真的很感謝 懷爸瘋科技 的專業服務！！！\n這個比起就算一些網路商店有一些折扣的優惠都還值得啊！！！\n到家後開箱 △ 帶回家後，外盒的樣子\n△ 初步的把盒子裡面的一些東西拿出來\n△ 全部的配件大概有這些\n△ 飛行小夥伴本人\n△ 鏡頭近拍\n使用心得 這邊先簡略的寫，因為我也才剛開始使用，\n如果未來哪一天有人想看更多的心得，\n相信到時我使用已經有使用一段時間，應該有更多可以分享！\n歡迎到時再留言給我說想看！ (這樣應該更清楚的知道我這不是業配文了吧XDDD)\n目前用了快半年，但其實也不算飛過太多次，只有無限的滿意與更滿意！！！！ (真的回不去了！天啊！！！)\n「避障功能」真的讚！ 另外這裡想特別提一下，\n有幾次我在花蓮飛的時候，真的差一點炸機，但我都被「避障功能」救到，\n我真的難以想像當初如果我買的是 mini 2 ，現在我的飛機會是怎麼樣\u0026hellip;\n台灣的空拍機法規\u0026hellip; 還是有點讓人困擾 (能飛的地方感覺也好少) 台灣的空拍機法規目前還是一樣非常的複雜，跟網路上大家所說的差不多，\n基本上我研究到現在簡單來說，有分成「200 呎(60公尺) 以上歸中央管」、「200 呎 (60公尺) 以下歸地方管」，\nreference： 蝦生活, 政策快訊 【空拍機/無人機法規】2022 台灣空拍機禁航區、考證照懶人包！ 而中央出的「Drone map」app 是以中央為主的，有時候地方政府如何管理「不見得」有寫進去\u0026hellip;\n所以該不會有那種地方(60公尺以下)禁飛，中央(60公尺以上)說可以飛？\n我不知道，我也不敢亂試XDD\n大部分被規定的地方都還算好理解，但只能說，\n如果是在大都市(例如：台北)，想飛個飛機真的要跑好遠，不然真的不用想\u0026hellip;.\n然後應該是一定要有車了！因為基本上「大眾交通運輸能到的地方」＝「不能飛的地方」XDD，\n因為可能會有很多人麻，沒辦法XD\n總之，買回來之後真的因為卡在「大眾交通運輸」的關係，搭火車也要離開車站一段才能飛XDD\n後來就比較少飛一點了，這裡必需還是要說，\n果然還是出門帶 Insta360 one X2 的 360 全景相機，還是能更自在的拍攝一點XD\n最後附上我自己這半年拍的幾張照片 這一區當然不能跟專業的攝影師比XD，想看這台的更多美照請左轉 YT XDD\n順便補充一句之前聽到一位攝影師說過的話，\n當你使用空拍機的時候，你就不是「攝影師」了，你是一位「駕駛員」，\n這半年飛著飛著也遇過一些狀況，真的很考驗駕駛的能力XD，大家加油啦！\n(還會有小朋友會想丟石頭把飛機打下來的 =口=，你知道這飛機要五萬多嗎?! (暈倒\n△ 台北 - 福德坑夕陽 (我目前最喜歡的一張照片！！！)\n△ 台北 - 福德坑，可以看到底下在公園玩的人，都有拍到的哦！！！\n△ 花蓮 - 新光兆豐休閒農場 (真的像城堡一樣呢！好想當我家XDD)\n不過這張因為是影片截圖，畫質有點不優，(當初忘記要拍這角度的照片了QQ，倒是拍了很多照片XD)\n△ 花蓮 - 瑞穗溫泉\n△ 花蓮 - 大農大富平地森林園區\n△ 台南 - 七股鹽山\n△ 台南 - 觀夕平臺\n就這樣啦！！！ 有空再補，希望我分享之後的使用心得的歡迎再留言！\n","date":"2021-11-09T15:42:26+08:00","image":"https://wongwongnotes.com/images/restored/2022/03/dji_export_1638197883575-scaled.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/reviews/dji-air-2s/","tags":[],"title":"【嗡嗡開箱 #1】DJI air 2S 空拍機開箱 - DJI | 不專業開箱文"},{"categories":["382 - Bash 檔案處理"],"content":"前言 我們在 python 裡面有類似 glob 或 os.walk 的方式，\n能夠快速取得「在一個特定資料夾底下的所有檔案路徑」，\n我們現在想要用單純的 bash script 來實現這一件事情，\n我們這裡來示範如何達成。\n利用 bash 達成，對指定目標路徑底下的檔案搜索 #!/bin/bash TARGET=\u0026#34;/home/ubuntu/Desktop\u0026#34; for each_file in \u0026#34;$search_dir\u0026#34;$TARGET/* do echo \u0026#34;$each_file\u0026#34; done 利用 bash 達成，類似 glob 效果的正規表達式 #!/bin/bash TARGET=\u0026#34;/home/ubuntu/Desktop\u0026#34; for each_file in \u0026#34;$search_dir\u0026#34;$TARGET/* do echo \u0026#34;$each_file\u0026#34; done Reference How to get the list of files in a directory in a shell script? ","date":"2021-11-08T21:57:13+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash/bash-get-all-file/","tags":["Bash","Linux","Ubuntu","檔案處理"],"title":"【Bash 檔案處理 #1】Linux terminal 取得資料夾內所有檔名(glob)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這個是我自己在操作 autossh 發生問題的解決辦法，\n主要想達成的目標就是連線到遠端的主機。\n顯示了以下的錯誤訊息：\n\u0026gt; ssh remotepc @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Permissions 0664 for \u0026#39;xxxxxx.pem\u0026#39; are too open. It is required that your private key files are NOT accessible by others. This private key will be ignored. Load key \u0026#34;xxxxxx.pem\u0026#34;: bad permissions ubuntu@ip: Permission denied (publickey). Permissions 0664 ：不一定是 664，這邊指的是檔案的權限 \u0026ldquo;xxxxxx.pem\u0026rdquo; ：為對應主機的金鑰，你自己應該會有一個想要連線主機的金鑰 解決方法 Permissions 0664 for \u0026lsquo;xxxxxx.pem\u0026rsquo; are too open.\n這段指的就是權限檔案的權限太高，0664 太開放了，系統認為是個危險的金鑰。\n這時我們只需要去修改金鑰的權限即可。\n如果讓金鑰只允許能讀取，設定為 400 chmod 400 ~/.ssh/id_rsa 如果讓金鑰可以讀可以寫，設定為 600 chmod 600 ~/.ssh/id_rsa 600 相對來說是比較方便的設定，畢竟可以同時讀可以寫，\n算是彈性比較大的版本。\n將金鑰 (.pem) 的權限改為 600 或 400 就可以了解決上述的問題的。\nReference ssh \u0026ldquo;permissions are too open\u0026rdquo; error ","date":"2021-11-02T15:24:39+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/warning-unprotected-private-key-file/","tags":["Linux"],"title":"【Linux】問題解決：ssh WARNING: UNPROTECTED PRIVATE KEY FILE! Permissions 0664 for 'xxxxxx.pem' are too open."},{"categories":["222 - C++ Makefile"],"content":"前言 今天我們要來繼續討論 Makefile，\n(這個是 day 3 的延伸討論)\nMakefile 裡面常常造成新手閱讀困擾的「$開頭的」這些符號，\n「$@」, 「$^」, 「$\u0026lt;」, 「$* 」,「$? 」，\n我們今天來一個個分析他們。\n讓 Makefile 超級難閱讀的關鍵字元XDDD，為了看懂就學一下吧！\n介紹「$@」, 「$^」, 「$\u0026lt;」, 「$* 」,「$? 」 我們一樣先對 makefile 的邏輯有個概念，依照以下的格式：\ntarget: dependency - target 是我們要製作的檔案， - dependency 是我們產生目標檔案的前置需求。 「$@」: 取代 target 名稱，因為我們時常會讓 target 等同於要製作出的檔名，我們就會使用「$@」代替 「$^」: 在多參數的時候，「^」取代的是全部的 dependency，也就是所有做為參數的都會被「$^」參考到。 「$\u0026lt;」: 代表的是第一個 dependency 「$* 」: 代表的是第一個 dependency，但不包含副檔名。 「$? 」: 代表比 targets 還新的 dependency。(在 dependencies 中，有更新需要被重新修改的項目) 範例1 all: library.cpp main.cpp 結果 「$@」: all 「$\u0026lt;」: library.cpp 「$^」: library.cpp main.cpp 範例2 all: test1 test2 test3 @echo \u0026#34;test1 test2 test3\u0026#34; @echo \u0026#34;@=$@\u0026#34; @echo \u0026#34;^=$^\u0026#34; @echo \u0026#34;?=$?\u0026#34; @echo \u0026#34;\u0026lt;=$\u0026lt;\u0026#34; @echo \u0026#34;*=$*\u0026#34; test1: @echo \u0026#34;test1\u0026#34; test2: @echo \u0026#34;test2\u0026#34; test3: @echo \u0026#34;test3\u0026#34; clean: @echo \u0026#34;clean\u0026#34; 結果 make test1 test2 test3 test1 test2 test3 @=all ^=test1.cpp test2.cpp test3.cpp ?=test1.cpp test2.cpp test3.cpp \u0026lt;=test1.cpp *= 範例3 - 印出變數 「$* 」: 目前看起來最常用到的用法，我自己普遍看到都是拿來印變數。 abc=123 # print variable print-% : ; @echo $* = $($*) 結果 \u0026gt; make print-abc abc = 123 Reference CU - AGSO - Introduction To Linux - Session 5 - Part 3 - GNU make and Makefiles Makefile 語法和示範 Makefile 語法簡介 簡單學 makefile：makefile 介紹與範例程式 Makefile教學: 一篇文章教會妳Phony What do the makefile symbols $@ and $\u0026lt; mean? How to print out a variable in makefile ","date":"2021-10-29T14:47:18+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/%E6%88%AA%E5%9C%96-2021-10-29-%E4%B8%8B%E5%8D%882.32.23.png","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/cpp-makefile-5/","tags":["C++","Makefile"],"title":"【C++ Makefile #5】Makefile 內建變數 -「$@」, 「$^」, 「$＜」, 「$* 」,「$? 」"},{"categories":["398 - Linux 問題解決"],"content":"前言 ssh 算是工程師遠端連線的常用工具了，\n因為實在太常用，所以我們有時也會想新增 alias 來讓我們的指令更方便使用，\n我曾經想寫\nalias sss=\u0026#34;ssh ubuntu@ip\u0026#34; 發現達不到效果，接下來我們來討論問題的原因。\n上面寫法達不到效果的原因 主要原因是因為 \u0026ldquo;@\u0026rdquo; 這個符號會吃到後面的空白，\n所以 alias 再解讀的時候，會變成\u0026quot;ssh ubuntu@ ip\u0026quot;，\n注意 ip 前面那格空白，就是導致失敗的主因。\n於是我們就會得到以下的 error message：\nssh: Could not resolve hostname : Name or service not known alias 修改寫法 既然 \u0026ldquo;ssh ubuntu@ip\u0026rdquo; 這個寫法行不通，\n我們就修改寫法，把 hostname 用參數 \u0026ldquo;-l\u0026rdquo; 的方式指定即可。\n於是得到我們的最終成果：\nalias sss=\u0026#39;ssh -l ubuntu $1\u0026#39; Reference ssh: Could not resolve hostname server: Name or service not known ","date":"2021-10-28T15:23:47+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/ssh-could-not-resolve-hostname/","tags":["Linux"],"title":"【Linux】問題解決：ssh: Could not resolve hostname : Name or service not known alias"},{"categories":["360 - Linux 網路遠控"],"content":"前言 ssh 算是工程師遠端連線的常用工具了，\n因為實在太常用，所以我們有時也會想新增 alias 來讓我們的指令更方便使用，\n我曾經想寫\nalias sss=\u0026#34;ssh ubuntu@ip\u0026#34; 發現達不到效果，接下來我們來討論問題的原因。\n上面寫法達不到效果的原因 主要原因是因為 \u0026ldquo;@\u0026rdquo; 這個符號會吃到後面的空白，\n所以 alias 再解讀的時候，會變成\u0026quot;ssh ubuntu@ ip\u0026quot;，\n注意 ip 前面那格空白，就是導致失敗的主因。\n於是我們就會得到以下的 error message：\nssh: Could not resolve hostname : Name or service not known alias 修改寫法 既然 \u0026ldquo;ssh ubuntu@ip\u0026rdquo; 這個寫法行不通，\n我們就修改寫法，把 hostname 用參數 \u0026ldquo;-l\u0026rdquo; 的方式指定即可。\n於是得到我們的最終成果：\nalias sss=\u0026#39;ssh -l ubuntu $1\u0026#39; Reference ssh: Could not resolve hostname server: Name or service not known ","date":"2021-10-28T15:21:57+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/terminal-ssh-alias/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #12】在 terminal 中 設定 ssh alias (bashrc, zshrc 中使用 alias)"},{"categories":["222 - C++ Makefile"],"content":"前言 今天我們要來繼續討論 Makefile，\nMakefile 裡面有個十分常用的 target -「.PHONY」，\n我們來看看這到底是什麼。\n什麼是「.PHONY」? 我們先來介紹 「.PHONY」存在的意義是什麼，\n如果我們去查電子辭典，會發現它對 phony 給出的解釋為 「假的、冒牌貨」，\n沒錯，「.PHONY」本身就是一個「假的 target」，而他的主要功能就是\n「變免 target 檔案名稱」與「實際上要使用的檔案名稱衝突」\n(作為「單純的目標指令」，非「檔案名」，或者也可以理解為「作為達到目的用的假檔案」)\n我們先來談談 .PHONY 如何「變免 target 檔案名稱」與「實際上要使用的檔案名稱衝突」 我們也依照官方的例子來說明這件事情，\n假設同一個目錄底下有一個叫做「clean」的檔案！！\n在前面的文章中，我們都知道\n「target: prerequisites」：當 target 或 prerequisites 沒有被更新，指令不會被執行 我們來看看通常 clean: 的指令，後面都沒有接東西，\nclean: rm -rf *.o 在這個條件底下，「make clean」這個指令會在執行一次後，永遠不會被執行。\n因為「clean 這個檔案不會再被更新了 (當然，一般我們也不會希望就這麼剛好有一個 clean 的檔案)」\n如果要讓 make clean 依然發揮他原來的「清除檔案」效果。\n我們需要改寫成：\n.PHONY: clean clean: rm -rf *.o 在這裡，「.PHONY」被定義為一個純粹的方法，\n也就是說，「clean」被作為一個純粹的方法使用，\n所以「make clean」可以視為是一個單純的清除功能，「不會去尋找 clean 檔案」。\n結論 如果我們在製作 Makefile 的過程中，有碰到過程中「有目標」，卻沒有「必要對應的檔案」，\n我們都可以定義在 「.PHONY」之後。\nReference CU - AGSO - Introduction To Linux - Session 5 - Part 3 - GNU make and Makefiles Makefile 語法和示範 Makefile 語法簡介 簡單學 makefile：makefile 介紹與範例程式 What is the purpose of .PHONY in a Makefile? Makefile教學: 一篇文章教會妳Phony ","date":"2021-10-27T16:35:53+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/cpp-makefile-4/","tags":["C++","Makefile"],"title":"【C++ Makefile #4】Makefile 常用 fake target -「.PHONY」"},{"categories":["222 - C++ Makefile"],"content":"前言 今天我們要來繼續討論 Makefile，\n我們來學習 Makefile 的常用變數 -「$@」, 「$^」\n(使用時請輸入小寫，這邊因為會吃到語法，所以改用全形大寫。)\n我們的今天的 Makefile 這份檔案與上次的 Makefile 「功能」是完全相同的，\n而我們要注意的是兩者不同的地方，「關於一些重複字詞的優化」。\nRM=rm T=touch otherfiles = myfile myotherfile buildfiles = myfile2 all: $(buildfiles) myfile: $(T) $@ myotherfile: # @ = echo to target $(T) $@ myfile2: $(otherfiles) # ^ = all dependency cat $^ \u0026gt; $@ clean: $(RM) $(buildfiles) $(RM) $(otherfiles) 說明 - 「$@」, 「$^」 與 day2 不同的是，這邊多使用了兩種特別的符號「$@」, 「$^」\n「$@」, 「$^」是在 Makefile 裡面常見的符號，\n我們可以透過這兩者分別取代以下說明的內容：\n「$@」：取代 target 名稱，因為我們時常會讓 target 等同於要製作出的檔名，我們就會使用「＄@」代替 「$^」：取代參數，這邊在 myfile2 為 target 時使用， 我們使用 「$^」代替了「$(otherfiles)」，這很像是我們一般寫程式碼時，引入變數的感覺， 我們就是使用這個符號，達到輸入變數的效果。 此外，在其他多參數的時候，「^」取代的是全部的 dependency，也就是所有做為參數的都會被參考到。\nReference CU - AGSO - Introduction To Linux - Session 5 - Part 3 - GNU make and Makefiles Makefile 語法和示範 Makefile 語法簡介 簡單學 makefile：makefile 介紹與範例程式 ","date":"2021-10-26T23:26:34+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/cpp-makefile-3/","tags":["C++","Makefile"],"title":"【C++ Makefile #3】Makefile 常用變數 -「$@」, 「$^」"},{"categories":["222 - C++ Makefile"],"content":"前言 今天我們要來繼續討論 Makefile，\n我們來學習如何定義自己的變數。\n我們的今天的 Makefile RM=rm T=touch otherfiles = myfile myotherfile buildfiles = myfile2 all: $(buildfiles) myfile: $(T) myfile myotherfile: $(T) myotherfile myfile2: $(otherfiles) cat myfile myotherfile \u0026gt; myfile2 clean: $(RM) $(buildfiles) $(RM) $(otherfiles) 說明 - 變數 在這份檔案中，我們定義了四個變數，\n其中有兩個是命令相關，另外兩個是檔案相關。\n命令相關的變數 RM=rm T=touch 檔案相關的變數 otherfiles = myfile myotherfile buildfiles = myfile2 說明 - 目標方法 (target) 我們在這份檔案一共定義了五種目標\nall: 1. 完成「所有 buildfiles」 myfile: 1. touch myfile myotherfile: 1. touch 「所有 myotherfile」 myfile2: 1. 前提要先完成 「所有 otherfiles」 2. 做 cat myfile myotherfile \u0026gt; myfile2 clean: 清除所有檔案 我們這邊特別留意 myfile2 的寫法 myfile2: $(otherfiles) cat myfile myotherfile \u0026gt; myfile2 「:(冒號)」後面只要加上 $(otherfiles) 或任何的 target，\n就表示我們要先完成目標的 target 做為「前置步驟」，\n所以像這邊的意思就是，先完成 $(otherfiles)，再去做 myfile2\nReference CU - AGSO - Introduction To Linux - Session 5 - Part 3 - GNU make and Makefiles Makefile 語法和示範 Makefile 語法簡介 ","date":"2021-10-26T23:08:10+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/cpp-makefile-2/","tags":["C++","Makefile"],"title":"【C++ Makefile #2】新增自己的變數"},{"categories":["222 - C++ Makefile"],"content":"前言 在 makefile 學習的過程中，基本上應該都會踩過一次下面敘述的雷，\n我們來好好說明這是什麼意思。\nmake: Nothing to be done for `all\u0026#39; 這個要講到 makefile 的邏輯，\nmakefile 只有在「兩種情況」下才會產生新檔案。\n目標檔案不存在 「目標檔案依賴的檔案」比「目標檔案」更新 第一點很好懂，不存在就是什麼都要從頭開始建。\n第二點講之前，我們先理解 Makefile 的語法：\n目標檔案：依賴的檔案(可為多個\u0026hellip;, 依賴的檔案1 依賴的檔案2) 換句話說，如果當「任何的檔案都沒有被更新時」，「make 這個指令就會覺得什麼事情都不需要做」。\n這就是「make: Nothing to be done for `all\u0026rsquo;」產生的原因。\n要怎麼解決上面的問題? 很簡單，但也最笨的方法就是，\n每一次都先「make clean，再 make」，就沒問題了。\n不過自己要先把 make clean 內容寫正確啊!! 不然是沒辦法避免這個錯誤的!\nmake clean make # or make all ","date":"2021-10-26T21:42:40+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/make-done-for-all/","tags":["C++","Makefile"],"title":"【C++ Makefile】問題解決：(常見問題) make: Nothing to be done for `all'"},{"categories":["222 - C++ Makefile"],"content":"前言 Makefile 可以是做為我們編譯一份 C++ 程式的索引，\n類似一份說明書，告訴我們先處理哪一份 C++ 檔案後，再去處理哪一份檔案。\n因此當撰寫到大型的 C++ 程式時，\n我們都會需要 Makefile 來協助我們決定編譯 C++ 檔案的順序。\nMakefile 簡介 其實 Makefile 跟 shell script 用途差不多，\n我們有時候也會透過寫 shell script 來自動化執行一些 linux 指令。\n而 Makefile 透過模組化的定義，更擅長處理一些大型程式的編譯。\n通常是更有架構、更有組織的程式碼，我們都會使用 Makefile。\n我們的第一份 Makefile all: myfile myfile: touch myfile clean: rm -f myfile 說明 可以看到在這份裡面我們還沒有處理任何與 C++ 有關的東西，\n沒錯，Makefile 並不是只能用來單純處理 C++ 的檔案 (只是常用，我剛開始學的時候也以為是 C++ 專用)\n這份 Makefile 主要分成三個 part\nall: myfile all 會需要 myfile 這個項目 myfile: touch myfile myfile 這個項目會去 touch myfile 這個檔案 (如果不存在就會產生) clean: rm -f myfile 我們去移除 myfile 這個檔案。 執行指令的方式有：「make all」、「make myfile」、「make clean」，\n而通常使用上我們只會下「make all」、「make clean」，做為建檔與刪檔使用。\n註：如果我們在 terminal 只單純下 「make」 = 「make all」\n後來發現這是我的大誤會，單純下 make 會執行「makefile 的第一個 target」，只是這裡剛好是 all 而已\n補充 「make」 = 「make all」的誤會 我之前一直以為 「make」 = 「make all」，\n總之結論是這是一個美麗的誤會，\n單純下 make 會執行「makefile 的第一個 target」，只是這裡剛好是 all 而已，\n我們進行以下測試\nMakefile 裡面寫下 all2: echo \u0026#34;touch2\u0026#34; all: echo \u0026#34;touch3\u0026#34; clean: echo \u0026#34;clean\u0026#34; 得到結果 echo \u0026#34;touch2\u0026#34; touch2 我們沒有得到全部的輸出 (包含clean)，\n只有得到 touch2 的結果。\n表示他是執行 Makefile 內的第一個 target。\n常見問題：make: Nothing to be done for `all' 在 makefile 學習的過程中，基本上應該都會踩過一次下面敘述的雷，\n我們來好好說明這是什麼意思。\nmake: Nothing to be done for `all\u0026#39; 這個要講到 makefile 的邏輯，\nmakefile 只有在「兩種情況」下才會產生新檔案。\n目標檔案不存在 「目標檔案依賴的檔案」比「目標檔案」更新 第一點很好懂，不存在就是什麼都要從頭開始建。\n第二點講之前，我們先理解 Makefile 的語法：\n目標檔案：依賴的檔案(可為多個\u0026hellip;, 依賴的檔案1 依賴的檔案2) 換句話說，如果當「任何的檔案都沒有被更新時」，「make 這個指令就會覺得什麼事情都不需要做」。\n這就是「make: Nothing to be done for `all\u0026rsquo;」產生的原因。\n那要怎麼解決上面的問題? 很簡單，但也最笨的方法就是，\n每一次都先「make clean，再 make」，就沒問題了。\n不過自己要先把 make clean 內容寫正確啊!! 不然是沒辦法避免這個錯誤的!\nmake clean make # or make all Reference CU - AGSO - Introduction To Linux - Session 5 - Part 3 - GNU make and Makefiles ","date":"2021-10-26T21:37:07+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/cpp-makefile-1/","tags":["C++","Makefile"],"title":"【C++ Makefile #1】嘗試撰寫自己的第一份 Makefile"},{"categories":["222 - C++ Makefile"],"content":"前言 make, 對應使用的文件是 Makefile,\n其實他不只用於 C++ 的編譯，真的要說他比較像是「自動化工具」，\n只是在 C++ 編譯時更廣為使用。\n既然都是自動化, makefile 與 bash script 的差別? make 更專注於軟體的建構過程 - 對於複雜的程式 dependency, 表示清楚程式之間的關係 - make 能檢查哪些文件有被修改、需要被重新編譯，提高效率 (對比 bash script 全部都會執行) bash 則是專注於腳本的自動化 - 任務比較多種類型都能包含，不會很少特別拿來建構軟體 - 本身就是 shell script (一種語言)，逐行執行對應的指令 ","date":"2021-10-25T22:59:09+08:00","permalink":"https://wongwongnotes.com/posts/cpp/compiler-build/c-makefile/c-makefile-concept/","tags":["C++","Makefile"],"title":"【C++ Makefile #0】Make, Makefile 的基本觀念, 其實 Makefile 不一定只用來用於 C++ 編譯?"},{"categories":["191 - Python 觀念理解"],"content":"前言 這篇我們要來研究 python 裡面，function 內記憶體位置的變化，\n研究這點重要的原因在於，\n如果我們能清楚的了解 python 內記憶體位置是如何變化的，\n那對於我們之後是否拿到正確的內容會有絕對的幫助。\n範例程式碼 def test(a): print(\u0026#34;in func\u0026#34;, id(a)) a=10000 print(\u0026#34;in func\u0026#34;, id(a)) return a print(\u0026#34;------ test 1 ------\u0026#34;) a=2000 print(a, id(a)) print(\u0026#34;------ test 2 ------\u0026#34;) test(a) print(a, id(a)) print(\u0026#34;------ test 3 ------\u0026#34;) a=test(a) print(a, id(a)) 結果 ------ test 1 ------ 2000 140187642266992 ------ test 2 ------ in func 140187642266992 in func 140187642266672 2000 140187642266992 ------ test 3 ------ in func 140187642266992 in func 140187642266672 10000 140187642266672 說明 測試一 這是我們的對照組，主要是觀察 a 現在的記憶體位置\n我們得到 a 位於記憶體 「140187642266992」 的地方\n測試二 我們要測試的是，進入 function 的 a，\n與離開 function 的 a，記憶體位置是否相同。\n在 function 中，剛進入時的 a 為 「140187642266992」\n而當我我們改變了 a 的值之後，a 的記憶體位置指向 「140187642266672」(不一樣)\n這邊我們得知了一件事情，修改過後的 a，\n記憶體位置會指向不同的地方。\n但出了 function 之後， a 又變回記憶體 「140187642266992」 的地方，且顯示原來的值「2000」\n測試三 在測試二中，我們並沒有指派「在 function 外的 a 更新值」，\n我們在實驗中進行指派。\n在 function 中，剛進入時的 a 為 「140187642266992」\n而當我我們改變了 a 的值之後，a 的記憶體位置指向 「140187642266672」(不一樣)\n這邊我們得知了一件事情，修改過後的 a，\n記憶體位置會指向不同的地方。\n但出了 function 之後， a 變到記憶體 「140187642266672」 的地方，且值也被改為「10000」\n注意！！！ 「140187642266672」居然與 function 內被改動過的 a 值相同！！！\n我們來給這件事做個解釋。\n結論與說明 我們觀察上面三個例子，我們可以得到\npython 中，其實每一個變數基本上都像是一個「指標 (pointer)」，\n只是不像 C++ 一樣，還特別需要多一個「*」代表指標。\n從測試二、測試三的比較中，\n我們發現其實 2000、10000，這兩個值早已固定在記憶體位置中的某個地方。\n也就是說，其實是 a 去改變了他指向的記憶體位置，造成了值的改變。\n所以 python 傳的其實是指標，而當這個指標被改動的時候，\n這個「指向的值(與值對應的記憶體位置)」，不會跟著被改動！\n2000、10000都是「固定的記憶體位置」。被改變的只有「a 指向的記憶體位置」。\nReference ","date":"2021-10-22T19:16:23+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/concepts/python-call-by-value-reference/","tags":["Python","觀念理解"],"title":"【Python 觀念理解 #1】python 小知識 - 記憶體位置與 function 關係之研究 (call by value? call by reference?)"},{"categories":["111 - Python 基礎語法"],"content":"前言 小技巧篇通常不會介紹太詳細的觀念，\n通常都只是看到網路上的一些不錯的用法，\n就做一點筆記當作小抄這樣。\n不會小技巧依然可以寫出 python !!!，\n但請不要「本末倒置」為了使用小技巧導致自己的程式失去了可讀性!!!\nlist comprehension list comprehension 是一個能夠以一行處理掉 list 邏輯的 python 程式碼，\n通常使用時，可以更節省空間，並且縮短程式碼的行數\n(相反的，也可能減少了程式碼易讀性)\n基礎 list comprehension 使用方法 最基本的使用方法，透過 list comprehension 快速產生一個連續數值的 list\nans = [x for x in range(10)] print(ans) 結果： [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] list comprehension 搭配 if 用法 稍微進階一點的使用方法，透過 list comprehension 搭配 if，\n會呈現有點像是「過濾」的效果。\nx_list = [-1, -2, 3] ans = [x for x in x_list if x \u0026lt; 0] print(ans) 結果： [-1, -2] list comprehension 搭配 if-else 三元運算子 再更進階一點的使用方法，透過 list comprehension 搭配 if-else 三元運算子，\n可以「對每個值進行處理」，所以可以達成向下面製作出「絕對值」的效果。\nx_list = [-1, -2, 3] ans = [x if x\u0026gt;0 else -x for x in x_list] print(ans) 結果： [1, 2, 3] Reference [Python教學]Python Comprehension語法應用教學 if-else 三元運算子 ","date":"2021-10-22T18:33:54+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-list-comprehension/","tags":["Python","基礎語法"],"title":"【Python 基礎語法 #5】python 小技巧 - list comprehension 使用範例"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是在使用 ssh 無法連線的解決辦法，出現類似以下的訊息\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the ECDSA key sent by the remote host is ... Please contact your system administrator. ... Host key verification failed. 錯誤圖片範例 解決方法 方法一：重建 ssh key ssh-keygen -R 192.168.0.1 192.168.0.1: 可任意替換成你要的ip 方法二：直接修改 /root/.ssh/known_hosts 文件 vim /root/.ssh/known_hosts 把出問題的刪掉！！！\n方法三：「最暴力的」把 /root/.ssh/known_hosts 文件直接刪掉！！！ 執行此指令前，請務必知道自己在幹嘛！！！\n如果搞不清楚狀況就直接執行此指令，後果請自負！\nsudo rm -rf /root/.ssh/known_hosts Reference 【Linux】ssh linux ubuntu / mac 中無法 ssh 無法連接 的解決辦法 can’t ssh cant ssh SSH連線出現錯誤 WARNING REMOTE HOST IDENTIFICATION HAS CHANGED ","date":"2021-10-22T16:31:44+08:00","image":"https://wongwongnotes.com/images/restored/2019/10/ssh-1.png","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/remote-host-identification-has-changed/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】問題解決：WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! (ssh 無法連線)"},{"categories":["922 - Mac / MacOS"],"content":"前言 【嗡嗡精選】這系列是留給我自己用的，方便快速查詢的筆記。\n文件編輯類 功能 快速鍵 檔案「絕對路徑」複製+貼上 \"option + command + c\", \"command + v\" 檔案「純 filename」複製+貼上 \"command + c\", \"command + v\" ","date":"2021-10-22T12:09:36+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/wongwong-mac-note/","tags":["Bash","Docker","Linux","Ubuntu","macOS"],"title":"【Mac】mac 快速鍵個人自用小抄 (get absolute path mac)"},{"categories":["111 - Python 基礎語法"],"content":"前言 小技巧篇通常不會介紹太詳細的觀念，\n通常都只是看到網路上的一些不錯的用法，\n就做一點筆記當作小抄這樣。\n快速將整個 list 內容轉成 int (整數) dst_list = list(map(int, src_list)) 這個寫法非常漂亮，適用於 python3，\n利用 map 把整個 src_list 轉換成 int 型態，\n由於這時還是 map class，再用 list()，即可得到最終成果。\n這寫法非常漂亮。\nReference ","date":"2021-10-22T01:17:08+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-list-int/","tags":["Python","基礎語法"],"title":"【Python 基礎語法 #4】python 小技巧 - 快速將整個 list 內容轉成 int (整數)"},{"categories":["520 - 深度學習 Deep Learning"],"content":"前言 在機器學習的領域中，有時候會因為場景不同，\n我們可能需要進行重新訓練，才能在不同的場景得到較高的精準度，\n但有時候也許只是距離結果只差一點，因為訓練 model 來說相對成本較高，\n如果只是需要微調的話，或許使用「回歸 (regression)」，\n是一個可以快速達到目標精準度、且較低成本的選項？\n線上計算回歸的網站 (Online regression calculator) Function approximation with regression analysis 我們需要先整理好我們的 x, y 資料，並以「一格空白」分開，\n按下 calculate 之後就可以得到答案了。\n結果 得到的結果：\n所有的回歸方程式 看圖 依據圖上顯示的內容，我們可以再次調整我們的回歸方程式。\n注意事項 注意1：資料筆數需要相同！\n注意2：空白須為「固定一格」，這點有點雞肋，但看在計算能力很強的情況下，\n就先算了，放過他 (推薦用 excel 整理資料)。\n沒辦法，其他網站都只有線性的迴歸分析，\n只有這網站相對來說有比較豐富的功能。\nexcel 整理資料小技巧 - 直轉橫的，可以利用複製貼上 (貼上為轉置資料)，快速把直行資料轉成橫的資料 - 再利用取代功能，調整空白。 ","date":"2021-10-21T18:14:00+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/regression-2.png","permalink":"https://wongwongnotes.com/posts/ai/deep-learning/deep-learning/online-regression-calculator/","tags":["Deep learning","機器學習","深度學習","Ｍachine learning"],"title":"【機器學習】可以線上計算回歸方程式的網站 - 計算工具推薦 (Online regression calculator)"},{"categories":["198 - Python 問題解決"],"content":"前言 這篇是我在實作 OpenCV 時，碰到以下錯誤訊息的解決辦法：\nAttributeError: \u0026#39;JpegImageFile\u0026#39; object has no attribute \u0026#39;shape\u0026#39; 解決辦法 個人產生此問題的原因是因為在使用「圖片處理 package 的時候」，\n將「pillow (import PIL)」與「OpenCV (import cv2)」混用了，\n個人導致此錯誤的原因是因為使用用了 PIL 的方式讀檔\npillow, opencv 混用 (錯誤) img = Image.open(path) print(img.shape) #error !!! 讀進來的會是 PIL 的格式，不支援 OpenCV 的 shape 參數，\n所以請修改成一致，\n改為只用 opencv 這邊使用 OpenCV 來示範：\nimg = cv2.imread(path) print(img.shape) #error !!! Reference How to fix \u0026ldquo;AttributeError: \u0026lsquo;JpegImageFile\u0026rsquo; object has no attribute \u0026lsquo;read\u0026rsquo;? Python 與 OpenCV 基本讀取、顯示與儲存圖片教學 Day24-Python 影像處理套件 PIL ","date":"2021-10-21T16:02:56+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/opencv-attribute-shape/","tags":["Bash","Linux","Python","Ubuntu"],"title":"【OpenCV】問題解決: AttributeError: 'JpegImageFile' object has no attribute 'shape'"},{"categories":["570 - TensorRT"],"content":"前言 這篇是我在使用 TensorRT 的時候碰到的問題，出現了以下的訊息：\nThe engine plan file is generated on an incompatible device, expecting compute 6.1 got compute 7.5, please rebuild. 解決方法 此為 model 讀取 TensorRT 時，讀到了「不同 GPU 轉換而成的 trt weight」，\n導致於 GPU cuda 無法解析。\n請將對應的錯誤 GPU 版本 trt weight 刪除，並「重新轉換出符合此 GPU 的 trt weight」！\nTensorRT recconvert code 範例 print(\u0026#39;Start loading weight.\u0026#39;) self.net = MLModel(cfg=self.cfg, phase = \u0026#39;test\u0026#39;) trained_model = \u0026#34;./core/weights/weight.pth\u0026#34; self.net = self.load_model(self.net, trained_model, cpu) self.net.eval().cuda() print(\u0026#39;Finished loading model!\u0026#39;) cudnn.benchmark = True self.device = torch.device(\u0026#34;cpu\u0026#34; if cpu else \u0026#34;cuda\u0026#34;) self.net = self.net.to(self.device) x = torch.ones((1, 3, 720, 1280)).cuda() tic = time.time() print(\u0026#34;Start Convert to TensorRT\u0026#34;) self.trt_net = torch2trt(self.net, [x]) # print(os.getcwd()) torch.save(self.trt_net.state_dict(), \u0026#39;./core/weights/weight_trt.pth\u0026#39;) print(f\u0026#34;Conver to TensorRT cost {time.time() -tic} sec\u0026#34;) print(\u0026#34;Convert to TensorRT Finish\u0026#34;) Reference [TensorRT] ERROR: INVALID_CONFIG: The engine plan file is generated on an incompatible device, expecting compute 6.1 got compute 7.5, please rebuild. ","date":"2021-10-21T15:18:08+08:00","permalink":"https://wongwongnotes.com/posts/ai/tensorrt/tensorrt/tensorrt-incompatible-device/","tags":["TensorRT"],"title":"【TensorRT】問題解決：The engine plan file is generated on an incompatible device, expecting compute 6.1 got compute 7.5, please rebuild. (Pytorch TensorRT)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在使用 ssh 連線時，碰到以下的問題的解決辦法。\nkex_exchange_identification: Connection closed by remote host 解決方法 個人最後解決這個問題的方式是，\n需要再去確認連線的 port，\n「遠端可被 ssh 連線的 port 只有 22」，當時我不小心設定成25(20)，\n導致了以上的問題，修改後就沒事了!\n至於 port 9000 或多少沒差，那只是我們 local 要連線遠端所使用的 port。\n將我們本機端的連線 port 設為 9000 (自訂，可更改) 遠端主機的 port 設定為 22 (ssh 固定 port，不可更改) Reference ssh: connect to host 127.0.0.1 port 2222: Connection refused 【Linux】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (anydesk ssh) What causes SSH error: kex_exchange_identification: Connection closed by remote host? ","date":"2021-10-21T02:35:30+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/kex_exchange_identification/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】Ubuntu 問題解決: kex_exchange_identification: Connection closed by remote host"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在使用 ssh 連線時，碰到以下的問題的解決辦法。\nssh: connect to host localhost port 9000: Connection refused 解決方法 個人最後解決這個問題的方式是，\n需要再去確認連線的 port，\n「遠端可被 ssh 連線的 port 只有 22」，當時我不小心設定成25，\n導致了以上的問題，修改後就沒事了!\n至於 port 9000 或多少沒差，那只是我們 local 要連線遠端所使用的 port。\n將我們本機端的連線 port 設為 9000 (自訂，可更改) 遠端主機的 port 設定為 22 (ssh 固定 port，不可更改) Reference ssh: connect to host 127.0.0.1 port 2222: Connection refused 【Linux】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (anydesk ssh) ","date":"2021-10-21T02:33:18+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/ssh-connection-refused/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】Ubuntu 問題解決: ssh: connect to host localhost port 9000: Connection refused"},{"categories":["899 - Debug / Error"],"content":"前言 這篇是我編譯 Arduino Nano 碰到以下問題的解決方法\navrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x00 解決方法：務必檢查 「開發版、處理器(最重要)、序列埠」 是否都有設定正確!! 這邊以 Nano 版的設定舉例，\n開發版：選擇 「Arduino Nano」 處理器：選擇 「ATmega328P (Old Bootloader)」，務必選這個，才會支援編譯成功 序列埠：選擇讓 Arduino 連接上電腦 USB 的哪一個序列埠。 範例 檢查序列埠的方式 打開檔案總管，對「本機」按下右鍵，選擇「管理」，\n會看到以下畫面，找到「裝置管理員」，選擇「連接埠」，\n找到對應的 Nano 所插上的 USB插槽，\n這邊我的電腦顯示的名稱為「USB-SERIAL CH340 (COM3)」，(注意，不一定是這個名字)\n我們就可以知道我們在 Arduino 的設定那邊，也要將序列埠也設定為「COM3」。\n於是，我們回到 Arduino，找到序列埠的設定，也設定為「COM3」。\n到此，基本的設定 Arduino Nano 部分應該就沒問題囉！\nArduino UNO 的部分請不要參考此文照著設定，要注意的地方是一樣的，\n但編譯 (不用 Old Bootloader)，與對應開發板 (Arduino UNO版) 皆需要更改。\nReference \u0026ldquo;avrdude: stk500_getsync(): not in sync: resp=0x00,\u0026rdquo; aka Some Dude Named Avr Won\u0026rsquo;t Let Me Upload My Program ","date":"2021-10-21T01:01:25+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/arduino-lcd-7.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/other/debug-error/arduino-avrdude-stk500-getsync/","tags":["Debug","Error"],"title":"【Arduino】問題解決：avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x00 (Arduino Nano 無法上傳程式碼)"},{"categories":["047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】","132 - PyQt5"],"content":"看完這篇文章你會得到的成果圖 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day30_final_project_package_method\n複習之前的內容 (前情提要) 完整版請參考：\n【PyQt5】Day 28 final project – 1 / 來搞一個自己的 photoshop 吧！UI 篇 + 純程式架構篇 (結合 PyQt + OpenCV) 【PyQt5】 Day 29 final project – 2 / 來搞一個自己的 photoshop 吧！後段程式細節篇 (結合 PyQt + OpenCV) 我們之前討論到了我們是如何設計程式的程式架構，\n以大概念來說，我們主軸還是圍繞在\nUI controller start 三大面向，而 UI 我們已經透過 Qt desinger 設定完成，\n而 start 沒什麼好說。\n我們開始著重討論 controller 的細節。\n獨立「圖像本身」與「圖像處理方法」，額外設計圖像處理介面。 我們選擇獨立「圖片本身」與「圖片處理方法」，\n我們想避免把所有圖片的功能全部都做在我們的圖像中心 (image center) 裡面，\n這樣會變成一個超級巨大的 class (又名為 god class)，\n功能太多之後要維護一個特定功能太難了，所以我們才獨立「圖像處理方法」進行操作。\n這部分是套用 design pattern 的設計原則 (使用 Interface Segregation Principle(ISP) 介面隔離原則)\n我們可以把介面分離出來，更方便之後功能的維護。\n介面設計與繼承方法 套用 design pattern 後 (使用 Interface Segregation Principle(ISP) 介面隔離原則) 套用 design pattern 的 Interface Segregation Principle(ISP) 介面隔離原則後，\n我們把「修改圖片的方法」這個介面獨立出來，更方便我們維護「圖片修改」的部分。\n而繼承的部分，從變更圖片的「所有共通方法 -\u0026gt; 滑條類方法/筆類方法 -\u0026gt; 各項細節方法」。\n我們在 day29 的時候，介紹了每個功能的實作細節 day28，我們講解了我們系統的大架構，與 UI 的設計。\n而 day 29，我們把每個細節的功能全部都介紹完了。\n那今天我們還有什麼事情可以做呢?\n今天我們要來談封裝方法，建構出「步驟的流程」 我們仔細觀察不論是 小畫家 或 photoshop 的程式，\n都會有提供「還原」或「重做」的功能。\n我們該如何在我們自己的 photoshop 實作出這種功能呢?\n分析「還原」或「重做」的功能，別人是怎麼做出來的? 保存圖片流 首先，如果用最簡單的方法，也許我們可以存圖片?\n也就是說，我們開一個 queue，「每更新一次畫面，就存一個 frame」，\n這樣聽起來簡單暴力，但是可行XDDD\n保存方法流 (保存變化量) 不過，如果我們再更仔細的觀察，因為如果「把每張圖都存起來」，\n勢必會消耗大量的儲存空間，因此應該會有更好的優化方法，\n我們思考有沒有可能對方存的只有「原圖 + 修改步驟」，\n換句話說，也就是「原圖 + (圖片的變化量)」。\n我們可以常常在還原功能那邊看到「上一個步驟」具體進行了什麼的操作，\n而不是「保存的上一張圖片」，因此，我們也乾脆來實作一個保存「變更的方法」。\n如果這樣子做，我們就可以省下大量的儲存圖片空間，\n而且我們也可以直接知道上一個「步驟內容」是什麼。\n介面微更新 我們新增了可以記錄步驟的框框，「還原」或「重做」的按鈕。\n實作保存方法的機制 我們宣告了一個新的 class method_steps_recoder\nclass method_steps_recoder(object): def __init__(self, text_recordsteps): self.method_steps = [] self.text_recordsteps = text_recordsteps def add_each_method_step(self, each_method_step): self.method_steps.append(each_method_step) def update_recordsteps(self): msg = f\u0026#34;All saved steps: \u0026#34; for idx, ele in enumerate(self.method_steps): msg +=(f\u0026#34;{idx+1}: {ele} \u0026#34;) self.text_recordsteps.setText(msg) 稍微想了一下，這個保存機制初始化的時間，\n應該與圖片剛初始化的時間同時，\n因此我們也在 class image_center 開始讀檔的時候，\n宣告 method_steps_recoder()，同時傳入要修改的參數。\nself.method_steps_recoder = method_steps_recoder(self.ui.text_recordsteps) # record steps 因為介面繼承的關係，我們可以輕鬆地增加記錄功能 我們上面已經把介面繼承寫得非常有架構了，因此這次要記錄步驟的功能，\n我們只需要去更新上層的介面即可。\n我們在 slider_method_interface 新增一個函數 append_each_method_step()，\n並修改 slider_release_event(滑條釋放的時間)，會呼叫這個函數，保存這次的更新內容。\n就完成了這部分的所有功能了!\nclass slider_method_interface(method_interface): # final update back to image center (not necessary, for double check) def slider_release_event(self): img = self.setimage(self.tmp_origin_img) self.append_each_method_step() # append all the methods include variables in to method_steps_recoder self.image_center.update_img(img) def append_each_method_step(self): self.image_center.method_steps_recoder.add_each_method_step(self) # append all the methods include variables in to method_steps_recoder 結果 因此，現在只要有滑條值的變化，都會啟動一次紀錄 (每拖曳並放開滑鼠時紀錄一次)，\n如下所示：\n【問題】然而，光是這樣的架構還不足以我們實現「還原」或「重做」 實作到此的我，發現目前想要實現出「還原」或「重做」，\n還存在一些問題：\n1. 還原上一步時，該如何復原目前圖片的變更？ 依照演算法，很多對圖片的變更可能都是「對圖片的破壞性變更」，\n也就是說，替圖片「減少一筆」的難度遠比「增加一筆」高出非常多。\n2. 還原上一步時，哪些畫面上的零件也需要還原？ 例如像是滑條顯示、步驟顯示，這些可能需要都被還原。\n目前實作上只有處理圖片的架構比較完整，\n但這些內容並沒有被好好的封裝起來，導致還原上有困難。\n3. 滑條對應的內容，是「一個 instance」 而不是 「new 一個新的 instance」 這大概是我目前系統架構做不出還原功能的致命傷。\n因為滑條只有一個，而照理來說「每滑條每變動一次」，\n就應該要 「new 一個新的滑條變動的 instance」，\n因為目前這部分我是綁死再一起的，所以這邊確實應該還要再拆分。\n預期未來解決問題的方法 上面三個問題，也可以濃縮成一個設計問題。\n基本上我會考慮將機制改為，存「原圖 + 所有變化的方法」，\n與上述不同的是不只是存「圖片的變化」，\n這次連 UI 當下的狀態可能也需要被儲存下來。\n因此，未來如果要繼續實作這部分的功能。\n我會考慮把「儲存的方法」改為存「UI 變化設定 \u0026amp; 當下圖的圖片變化」\n系統運作的邏輯會類似保存：\n原圖 -\u0026gt; 圖面＆UI變化 -\u0026gt; 圖面＆UI變化 -\u0026gt; 圖面＆UI變化... 所以我們紀錄的東西反而是「步驟」，\n至於還原的時候，可以以當時保存最舊的圖片，\n依照「步驟」全部重新運算，\n應該就能夠如我們預期的完成「還原」或「重做」的功能。\n優化效能 此外，我們要處理一下我們系統的效能優化，\n昨天的程式執行後，如果是不夠強大的 CPU，或解析度太大的圖片，\n會沒有辦法應付「移動滑條」造成「圖片的連續變化」運算。\n【修改】我們暫時先移除，隨著滑條圖片一起變動的功能 主要是因為，滑條跟圖片一起變，圖片解析度太大，\n我們電腦處理不來，會導致程式嚴重卡在運算上。\n於是我們就先移除這個「動態演飾」的效果，\n我們只保留「變動前的樣子」、「變動後的結果」。\n透過這樣的方式大幅減少中間過程的對電腦效能上的負擔。\n修改程式碼部分 各個 setsliderlabel 改為不更新原圖，只更新 label。 # trigger function, get your signal from here def setsliderlabel(self): self.label.setText(f\u0026#34;{self.prefix}{self.slider.value():+}\u0026#34;) # self.update_img() # for the efficiency reason, we don\u0026#39;t let the picture change with our slider 在 class slider_method_interface 中，更新 slider_release_event (釋放滑鼠時)， 變成只在釋放滑鼠時更新圖片，由於介面繼承設計的關係， 其他的功能也會被同步修改完成。 這樣就完成了我們的效能優化。\nclass slider_method_interface(method_interface): # final update back to image center (not necessary, for double check) def slider_release_event(self): img = self.setimage(self.tmp_origin_img) self.append_each_method_step() # append all the methods include variables in to method_steps_recoder self.image_center.update_img(img)r 最終結果 優化部分 這次我們先完成了圖片的滑條優化功能，我們把因為滑條的連續變動，\n導致圖片的連續變化，電腦計算跟不上的問題進行了修正。\n實現「還原」或「重做」功能的部分 我們考慮到現有機制如果真的想要實現「還原」或「重做」的功能，\n我們必須把「滑條」與「滑條影響圖片的內容」介面整個進行修改，\n也就是說XD，我們之前設計的介面還不夠細、想得不夠周全XD\n應該是要：\n滑條控制(只有一個) -\u0026gt; new 一個新的圖片變化方法(方法應該要多組，可改多次) -\u0026gt; 保存圖片修改方法、UI變化內容(最好可以附帶一段此方法的說明，含操作的變數) -\u0026gt; 保存此方法(存進 list)。 最後就是不斷循環。 有機會我們再把程式的這部分架購進行優化，目前這個做下去預計又是一個架構上的大改了XD\n最終成品！ 30天的結語 今天算是鐵人賽這三年中最辛苦的一年，\n老實說我有先囤了一些稿才來報名，因為今年公司比較忙碌，\n而且甚至到鐵人賽的最後一天才開賽XD，\n但沒想到最後居然還是在最後幾天被迫熬夜加班才跟得上進度XD。\n不過我抱持的心情就是，既然都參加了，就一定要好好的把它完成！\n所以才會想先屯稿、拖到最後一天開賽XD\n說真的我覺得寫鐵人賽最大的受惠者永遠是作者，\n30天前我根本連Qt都沒用過，現在我也能變成這樣XD\n真的信不信由你，我真的是30天內從零開始學的XD，\n所以才說如果真的有心想學，鐵人賽最終會是讓自己受惠最多的短時間高度成長體驗。\nReference ","date":"2021-10-15T03:07:30+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/pyqt5-30-2-1024x743.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/th-psqq-pyqt/pyqt5-30/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 30 - final project - 3 / 來搞一個自己的 photoshop 吧！把每個方法封裝起來製作出還原功能吧！ (結合 PyQt + OpenCV)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day28-30_final_project\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n複習昨日的內容 (前情提要) 完整版請參考：【PyQt5】Day 28 final project – 1 / 來搞一個自己的 photoshop 吧！UI 篇 + 純程式架構篇 (結合 PyQt + OpenCV)\n昨天我們討論到了我們是如何設計程式的程式架構，\n以大概念來說，我們主軸還是圍繞在\nUI controller start 三大面向，而 UI 我們已經透過 Qt desinger 設定完成，\n而 start 沒什麼好說。\n我們開始著重討論 controller 的細節。\n獨立「圖像本身」與「圖像處理方法」，額外設計圖像處理介面。 我們選擇獨立「圖片本身」與「圖片處理方法」，\n我們想避免把所有圖片的功能全部都做在我們的圖像中心 (image center) 裡面，\n這樣會變成一個超級巨大的 class (又名為 god class)，\n功能太多之後要維護一個特定功能太難了，所以我們才獨立「圖像處理方法」進行操作。\n這部分是套用 design pattern 的設計原則 (使用 Interface Segregation Principle(ISP) 介面隔離原則)\n我們可以把介面分離出來，更方便之後功能的維護。\n介面設計與繼承方法 套用 design pattern 後 (使用 Interface Segregation Principle(ISP) 介面隔離原則) 套用 design pattern 的 Interface Segregation Principle(ISP) 介面隔離原則後，\n我們把「修改圖片的方法」這個介面獨立出來，更方便我們維護「圖片修改」的部分。\n而繼承的部分，從變更圖片的「所有共通方法 -\u0026gt; 滑條類方法/筆類方法 -\u0026gt; 各項細節方法」。\n今天我們從各個功能的細節開始談 圖像中心 image_center 我們所有關於圖像的處理都在這邊，注意因為我們把「變化方法」丟出去做成介面了，\n所以這裡只有「顯示相關」不包含「修改」。\n因此這部分被簡化過，我們有：\n讀檔 read_file_and_init 更新圖片 update_img, __update_label_img 處理圖片顯示的縮放 set_zoom_value, __update_img_zoom 而 update_img, set_zoom_value 是給外部呼叫的，作為 trigger 我們的 image_center 進行更新。\nclass image_center(object): def __init__(self, img_path, ui): self.img_path = img_path self.ui = ui self.label_mouse_controller = label_mouse_controller(self) self.zoom_value = 1 self.read_file_and_init() def read_file_and_init(self): try: self.origin_img = opencv_engine.read_image(self.img_path) # if cancel, no error !!!! self.origin_img_height, self.origin_img_width, self.origin_img_channel = self.origin_img.shape # need this to make error !!! except: self.origin_img = opencv_engine.read_image(\u0026#39;./demo_materials/sad.png\u0026#39;) self.origin_img_height, self.origin_img_width, self.origin_img_channel = self.origin_img.shape self.display_img = np.copy(self.origin_img) # make a clone self.__update_label_img() def update_img(self, img): self.display_img = img # default = not change, like zoom self.__update_label_img() def set_zoom_value(self, value): self.zoom_value = value def __update_img_zoom(self): qpixmap_height = self.origin_img_height * self.zoom_value self.qpixmap = self.qpixmap.scaledToHeight(qpixmap_height) def __update_label_img(self): bytesPerline = 3 * self.origin_img_width qimg = QImage(self.display_img, self.origin_img_width, self.origin_img_height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(qimg) self.__update_img_zoom() self.ui.label_img.setPixmap(self.qpixmap) self.ui.label_img.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) 「滑鼠控制」相關 label_mouse_controller 我們需要一個幫助我們感應「滑鼠在圖片上動作」的功能，例如之後的畫筆可能會使用到，\n我們將這些功能封裝成一個 class label_mouse_controller，\n當要製作畫筆類的功能時，他會協助我們完成「圖像上偵測滑鼠」的相關動作。\n我們定義的功能有：\n偵測滑鼠按壓時：mouse_press_event 偵測滑鼠放開時：mouse_release_event 偵測滑鼠按下並拖曳時：mouse_moving_event class label_mouse_controller(object): def __init__(self, image_center): self.image_center = image_center self.ui = self.image_center.ui # new pointer point to self.image_center.ui self.ui.label_img.mousePressEvent = self.mouse_press_event self.ui.label_img.mouseReleaseEvent = self.mouse_release_event self.ui.label_img.mouseMoveEvent = self.mouse_moving_event def mouse_press_event(self, event): msg = f\u0026#34;{event.x()=}, {event.y()=}, {event.button()=}\u0026#34; x = event.x() y = event.y() norm_x = x/self.image_center.qpixmap.width() norm_y = y/self.image_center.qpixmap.height() real_x = int(norm_x*self.image_center.origin_img_width) real_y = int(norm_y*self.image_center.origin_img_height) self.ui.label_click_pos.setText(f\u0026#34;Clicked postion = ({x}, {y})\u0026#34;) self.ui.label_norm_pos.setText(f\u0026#34;Normalized postion = ({norm_x:.3f}, {norm_y:.3f})\u0026#34;) self.ui.label_real_pos.setText(f\u0026#34;Real postion = ({real_x}, {real_y})\u0026#34;) def mouse_release_event(self, event): msg = f\u0026#34;{event.x()=}, {event.y()=}, {event.button()=}\u0026#34; def mouse_moving_event(self, event): msg = f\u0026#34;{event.x()=}, {event.y()=}, {event.button()=}\u0026#34; 「圖形處理」介面相關 正如同我們上面所說，我們將所有的方法都包裝好，並照上圖的方式一層層的繼承下來。\n分類他是畫面方法或是畫筆類方法，再個別「繼承後，進行更細部的定義」。\n「圖形處理」介面大祖宗 (method_interface) 所有的「圖形處理」介面，基本上都會依照此介面定義，\n我們先在這個做好基本的功能，更客製化的細節功能就交給孫子們去處理。\n這裡只有定義：\n初始化參數：「init」 更新圖片：update_img import abc class method_interface(abc.ABC): @abc.abstractmethod def __init__(self): return NotImplemented @abc.abstractmethod def update_img(self): return NotImplemented 「圖形處理」介面父母輩 (slider_method_interface, pen_method_interface) 因為時間的關係，只來得及做一半 (slider_method_interface)，\n我們在裡面多定義了會使用到「滑條來修改圖片」的相關功能，會使用到的介面。\n而「會滑條來修改圖片」的眾多功能，就交給孩子們去做更細節的定義吧!\n這裡定義了：\n更多詳細的「與滑條有關的」初始化參數：「init」 滑條按下與釋放：slider_press_event, slider_release_event 取得滑條值： getslidervalue 設定滑條值 (當滑條的值被變更時，觸發此功能)： setsliderlabel 更新圖片相關：setimage, update_img class slider_method_interface(method_interface): def __init__(self, slider, label, image_center): self.label = label self.slider = slider self.image_center = image_center self.tmp_origin_img = self.image_center.display_img self.slider.setRange(-100, 100) self.slider.setProperty(\u0026#34;value\u0026#34;, 0) self.slider.valueChanged.connect(self.setsliderlabel) self.slider.sliderPressed.connect(self.slider_press_event) self.slider.sliderReleased.connect(self.slider_release_event) self.prefix = \u0026#34;\u0026#34; # get first picture snapshot, def slider_press_event(self): self.tmp_origin_img = self.image_center.display_img # final update back to image center (not necessary, for double check) def slider_release_event(self): img = self.setimage(self.tmp_origin_img) self.image_center.update_img(img) # image do the method def setimage(self, img): return img @property def getslidervalue(self): return self.slider.value() # trigger function, get your signal from here def setsliderlabel(self): self.label.setText(f\u0026#34;{self.prefix}{self.slider.value():+}\u0026#34;) self.update_img() def update_img(self): self.image_center.update_img(self.tmp_origin_img) # default = origin_image no change, like zoom in/out 「圖形處理」孩子輩 (method_lightness, method_saturation, method_contrast\u0026hellip;) 這裡我們就來開始撰寫「與滑條相關」的各項細部功能，像是「光線、飽和度、對比度\u0026hellip;」，\n都會是在這邊實作，而因為我們已經有在上面定義好了滑條相關的方法，\n這邊如果沒有必要多做修改，可以完全不用新增「滑條的處理方法」(傳入正確的變數就會自動搞定了)，\n只需要專注在實現「修改圖片的方法」即可。\n這邊隨便舉個範例，調整光線 method_lightness：\nsetimage：處理圖片光線變化的方法 update_img：將變化後的圖片傳回去圖像中心更新 (image_center) setsliderlabel: trigger 用，感應滑條變化的時間 你可能看完會很好奇，怎麼都沒有「滑條相關」的細節實作?\n這就是繼承的好處，因為我們已經在「父母輩」定義好了實作方法，\n而在 「init」 中直接傳入對應的參數，瞬間就實作完「滑條相關」的細節 (因為都是共通的概念)。\n這邊就是這樣處理，相當的方便，又不用重寫多次滑條處理方法。\nclass method_lightness(slider_method_interface): def __init__(self, slider, label, image_center): super().__init__(slider, label, image_center) self.prefix = \u0026#34;lightness: \u0026#34; self.update_img() def setimage(self, img): return opencv_engine.modify_lightness(img, lightness=self.slider.value()) def update_img(self): img = self.setimage(self.tmp_origin_img) self.image_center.update_img(img) # trigger function, get your signal from here def setsliderlabel(self): self.label.setText(f\u0026#34;{self.prefix}{self.slider.value():+}\u0026#34;) self.update_img() OpenCV 圖像處理引擎 (opencv_engine) 製作一個 OpenCV 的圖像處理引擎，並把它全部包成可以直接取用的方法「@staticmethod」，\n我們只在這支程式中使用「import cv2」，方便我們集中管理。\nimport cv2 import numpy as np import math class opencv_engine(object): @staticmethod def point_float_to_int(point): return (int(point[0]), int(point[1])) @staticmethod def read_image(file_path): return cv2.imread(file_path) @staticmethod def draw_point(img, point=(0, 0), color = (0, 0, 255)): # red point = opencv_engine.point_float_to_int(point) print(f\u0026#34;get {point=}\u0026#34;) point_size = 1 thickness = 4 return cv2.circle(img, point, point_size, color, thickness) @staticmethod def draw_line(img, start_point = (0, 0), end_point = (0, 0), color = (0, 255, 0)): # green start_point = opencv_engine.point_float_to_int(start_point) end_point = opencv_engine.point_float_to_int(end_point) thickness = 3 # width return cv2.line(img, start_point, end_point, color, thickness) @staticmethod def draw_rectangle_by_points(img, left_up=(0, 0), right_down=(0, 0), color = (0, 0, 255)): # red left_up = opencv_engine.point_float_to_int(left_up) right_down = opencv_engine.point_float_to_int(right_down) thickness = 2 # 寬度 (-1 表示填滿) return cv2.rectangle(img, left_up, right_down, color, thickness) @staticmethod def draw_rectangle_by_xywh(img, xywh=(0, 0, 0, 0), color = (0, 0, 255)): # red left_up = opencv_engine.point_float_to_int((xywh[0], xywh[1])) right_down = opencv_engine.point_float_to_int((xywh[0]+xywh[2], xywh[1]+xywh[3])) thickness = 2 # 寬度 (-1 表示填滿) return cv2.rectangle(img, left_up, right_down, color, thickness) @staticmethod def modify_lightness(img, lightness = 0): # range: -100 ~ 100 if lightness == 0: # no change return img # lightness 調整為 \u0026#34;1 +/- 幾 %\u0026#34; # 圖像歸一化，且轉換為浮點型 fImg = img.astype(np.float32) fImg = fImg / 255.0 # 顏色空間轉換 BGR -\u0026gt; HLS hlsImg = cv2.cvtColor(fImg, cv2.COLOR_BGR2HLS) hlsCopy = np.copy(hlsImg) # 亮度調整 hlsCopy[:, :, 1] = (1 + lightness / 100.0) * hlsCopy[:, :, 1] hlsCopy[:, :, 1][hlsCopy[:, :, 1] \u0026gt; 1] = 1 # 應該要介於 0~1，計算出來超過1 = 1 # 顏色空間反轉換 HLS -\u0026gt; BGR result_img = cv2.cvtColor(hlsCopy, cv2.COLOR_HLS2BGR) result_img = ((result_img * 255).astype(np.uint8)) return result_img @staticmethod def modify_saturation(img, saturation = 0): # range: -100 ~ 100 if saturation == 0: # no change return img # saturation 調整為 \u0026#34;1 +/- 幾 %\u0026#34; # 圖像歸一化，且轉換為浮點型 fImg = img.astype(np.float32) fImg = fImg / 255.0 # 顏色空間轉換 BGR -\u0026gt; HLS hlsImg = cv2.cvtColor(fImg, cv2.COLOR_BGR2HLS) hlsCopy = np.copy(hlsImg) # 飽和度調整 hlsCopy[:, :, 2] = (1 + saturation / 100.0) * hlsCopy[:, :, 2] hlsCopy[:, :, 2][hlsCopy[:, :, 2] \u0026gt; 1] = 1 # 應該要介於 0~1，計算出來超過1 = 1 # 顏色空間反轉換 HLS -\u0026gt; BGR result_img = cv2.cvtColor(hlsCopy, cv2.COLOR_HLS2BGR) result_img = ((result_img * 255).astype(np.uint8)) return result_img @staticmethod def modify_contrast_brightness(img, brightness=0 , contrast=0): # range: -100 ~ 100 if brightness \u0026lt;mark\u0026gt; 0 and contrast \u0026lt;/mark\u0026gt; 0: return img B = brightness / 255.0 c = contrast / 255.0 k = math.tan((45 + 44 * c) / 180 * math.pi) img = (img - 127.5 * (1 - B)) * k + 127.5 * (1 + B) # 所有值必須介於 0~255 之間，超過255 = 255，小於 0 = 0 img = np.clip(img, 0, 255).astype(np.uint8) return img 最終結果 把上面落落長的東西都實作完，並 debug 完，\n終於暫時有了現在的作品！\n但現在還有一些效能問題要處理，例如說載入太大解析度的圖片時，\n我們使用「滑條功能」，因為會產生「連續的變化計算」，\n太大解析度的電腦計算速度可能會跟不上。\n目前這部分可能還需要想想怎麼樣優化會更好XD\n(或者直接縮放後以低解析度作處理XD，紀錄「方法步驟」後，最後存檔才重新實現這些步驟。)\n這個是我下一篇想要談的XD，有沒有機會把「方法」當作一個個的「物件」，保存進一個 queue 呢?\nReference ","date":"2021-10-14T02:51:17+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/%E6%88%AA%E5%9C%96-2021-10-12-%E4%B8%8B%E5%8D%889.26.33-1024x743.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-29/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 29 final project - 2 / 來搞一個自己的 photoshop 吧！後段程式細節篇 (結合 PyQt + OpenCV)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day28-30_final_project\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n設計我們的 UI 雖然原本我有想直接拿 Day17 的內容繼續改，\n後來覺得架構上還不夠漂亮，最後決定乾脆直接砍掉重練比較快哈哈哈。\n既然要搞就一次搞到最大吧! 這次我設計的 final project UI 初版長這樣\n欸等等先別吐血啊!!! 這 UI 不是一天搞出來的啊!!\nUI 就請大家自己慢慢刻哈哈哈哈哈!!!\n(你說突然這也突然跳太高階了吧?!\n其實只是設定一些顏色而已XD，之前因為這個相對簡單就沒特別介紹)\n排版控制 Layout 系列 如果還有機會的話有空在介紹，不過這邊的內容多半是用於排版，\n所以也難能以單一例子舉出 demo 在幹嘛，\n總之可以自己玩玩看 Layouts 這一塊，\n先「拉一個 Layout」，再把「要排版的元件丟進去」，就對自動對整齊了!\nLayouts 系列： 設定字體顏色，背景顏色的地方位於 styleSheet 使用的應該是類似 css 語法? 我不確定XD\n總之有興趣可以直接參考下面的延伸閱讀進行修改，\n設定位置在 Qt desinger 的這邊\n延伸閱讀：給想研究更多怎麼設定顏色的人，請參考 Qt Style Sheets Examples\n設定 Logo 的地方 Qlabel 就是一個我們可以輸入圖片的地方，\n透過右邊的「\u0026hellip;」，選擇一張我們要的圖片，就可以直接把 LOGO 嵌入 UI 中了。\n執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 讚到不行XDDDD (自己講\n程式架構篇 這次的 final project 真的內容會做太多，如果沒有好好規劃一下架構，\n很有可能寫出來的東西會一團亂XDDD\n所以我們要先趁今天來規劃，明天再來慢慢把功能實現\n先從大方向切入，我們有那些大的「重要元素」? 依照 day5 的設計，我們至少也會有\nUI (作為前端顯示介面) controller (做為後端邏輯控制) start (啟動用，沒什麼好討論的) 而 UI 因為我們在 Qtdesigner 已經設計得夠複雜了，也不太需要另外寫程式，\n所以基本上在經由 pyuic 的轉換後，就已經完成了 UI.py\npyuic5 -x day28.ui -o UI.py controller 是我們這邊要討論的重點，在我們的 photoshop 後端邏輯裡面，\n我之前在 day17 實作的最終版是把「圖像+圖像變化方法」寫在同一個 class 當中，\n我們如果繼續讓我們的「變化方法增加」，不適不能這樣實作，\n但最終我們會有一個超級巨大的 class 共同實作 「圖像+圖像變化方法」，\n於是我決定把「變化方法」拆出去獨立一個 class，\n使得「圖像」與「變化方法」分開成兩套獨立機制，而能互向協助。\n這也符合 design pattern 的 Interface Segregation Principle(ISP) 介面隔離原則\n以遊戲來說，我們也可以舉例：\n我們大可以把一個角色的所有「屬性、裝備、技能」全部都包在這個角色當中，\n但如果另外一個玩家也玩了同一個職業，我們何必把技能「整個複製進去另外一個角色當中呢?」\n於是我們可以把「技能」這個介面分離出去，讓有需要的類別再去呼叫這個介面即可。\n因此，這時候就能透過這樣的拆分方式，把我們實作的彈性與擴充功能的彈性提升。\n舉例： 套用 design pattern 前 套用 design pattern 前，基本上這個設計沒什麼問題，\n硬要說缺點只是維護職業技能時，「需要一個個角色去調整內部的技能」\n套用 design pattern 後 (使用 Interface Segregation Principle(ISP) 介面隔離原則) 套用 design pattern 的 Interface Segregation Principle(ISP) 介面隔離原則後，\n我們把技能獨立出來維護，這樣的好處就是我們可以調整一次職業技能，\n所有的角色都能得到修正!\n而這邊我們也要做同樣的事情，我們要把「修改圖片的方法」這個介面獨立出來!\n於是整理一下，得到我們目前想設計的架構 獨立「圖片本身」與「圖片處理方法」，\n另外因為滑鼠事件的資訊比較特別，需要從圖片上獲取，所以我們也另外獨立出來處理\n【重要】抓出誰會是觸發事件的 trigger 第一次設計這個系統的時候，就碰到一個最關鍵的問題，\n「沒有提早先想好架構就開始寫，導致繞了很多彎路」，\n應該要先想好再下去寫的問題就是，在這個系統裡面，「什麼事情會是觸發事件的 trigger?」\n如果我們不知道「什麼事件會被使用者觸發」，\n「就不知道要從哪個點開始啟動後續的修改」，\n我一開始就是瘋狂地開始寫功能XDD，然後沒注意誰呼叫誰，\n結果功能都有了，但是 trigger 位置寫在錯誤的 class，\n所以要重搞順序XDD\n以結論來說，這個系統的 trigger 位置如下 所以在寫系統時，我們需要特別注意，\n我們箭頭指的地方就是「會觸發事件的 trigger」，\n凡經過此處的程式都是「一次修改的起點」，引導我們進行往後的修改。\n圖形修改介面 (interface) 設計 基本上我們設計的「修改圖片」都有符合一些同樣的原則，\n我們可以使用「介面繼承」的方式來實現，\n為了達到這樣的效果，我們需要 「import abc」 這個 python 酷酷的 library，\n細節的部分我會再另外寫一篇文章，這邊我們先直接實作。\n註：冷知識(?) abc = the infrastructure for defining 「abstract base classes (ABCs)」 in Python\n簡單來說，就是「抽象的類別 (class)」定義\n我們預計設計的架構如下，一共會有三層，底下會一層層介紹：\n介面的大祖宗 (method_interface)，一切介面方法從此開始實作 我們可以大致歸納我們全部的變化介面都會有兩個基本的要素：\n初始化參數:「init」 更新圖片: update_img 另外，我們強制這兩個介面在繼承後都必須被定義，\n所以我們透過「@abc.abstractmethod」，定義這個抽象的方法 (未實作功能)，\n以及「return NotImplemented」，如果使用者繼承介面後未定義這個函數，\n會跳 「NotImplemented error」，強制跳錯 (逼使用者一定要定義介面內容)。\nimport abc class method_interface(abc.ABC): @abc.abstractmethod def __init__(self): return NotImplemented @abc.abstractmethod def update_img(self): return NotImplemented 介面的父母 (滑條方法介面 slider_method_interface, 畫筆方法介面 pen_method_interface) 我們會實作的「圖像變化方法」，能夠產生變化的方法大概有兩種，\n一種是滑條類，另一種是畫筆類。\n這兩類在實作時，都會繼承我們的介面大祖宗 (method_interface)，\n然後再分別基於滑條的特性與畫筆的特性，實作各自的介面。\n這所有的介面方法都屬於「圖片變化方法」，所以我們會強烈推薦用繼承的方法撰寫。\n會比起一個個慢慢定義有更好的架構與維護性\n(維護一個父類別，也許底下的所有子類別都能全部受惠到這次的更動。不用一個個慢慢改。)\n最大的差別就是：\n滑條拉回去後，可以復原 (不會是破壞原圖的變動) 畫筆畫下去後，不可復原 (會破壞原圖的變動) 因此這兩個方法我們要分開實作，實作細節一樣我們明日再提，\n今天光是講大架構就已經夠累了。\n各個細部的方法實作 在看該方法是「滑條方法介面 slider_method_interface」或是「畫筆方法介面 pen_method_interface」後，\n我們就可以開始實作細部功能了，這部分的實作細節我們就明日在提吧。\n(今天光是講完大架構相信大家已經都累了XDDD)\n最終系統架構圖 這個是舊版，滑鼠那部分因為是新加的，目前還沒更新上去，\n不過相信上面已經說明得相當完整了，應該能大約類推出他在圖形上會呈現的細節\nReference ","date":"2021-10-13T14:39:54+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/%E6%88%AA%E5%9C%96-2021-10-12-%E4%B8%8B%E5%8D%889.21.16.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-28/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 28 final project - 1 / 來搞一個自己的 photoshop 吧！UI 篇 + 純程式架構篇 (結合 PyQt + OpenCV)"},{"categories":["047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】","132 - PyQt5"],"content":"看完這篇文章你會得到的成果圖 這篇文章，主要是設計給我自己要用的 Video Player 畫 ROI 工具。\n所以很多功能都是替我自己客製化。\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day27_video_roi_project\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n設計我們的 UI 這篇文章由於是我在撰寫 day25 的時候另外寫的，還沒有做出 day26 的優化更新，\n如果有興趣的可以自己再改，這邊只專注在講新增的功能\n主要就是新增滑條的部分，新元素的名稱：\nself.button_clear_points：清除目前有的點 self.button_generate_rois：產生 roi 用按鈕 self.text_save_points：顯示儲存的點 self.text_output_rois：顯示 roi 結果 轉換 day27.ui -\u0026gt; UI.py pyuic5 -x day27.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 設計我們的 controller video_controller 新增功能 連結按鍵 連結這兩個按鍵至兩個對應的 function\ndef set_video_player(self): self.ui.button_clear_points.clicked.connect(self.clear_points) self.ui.button_generate_rois.clicked.connect(self.generate_rois) 以按鍵更新文字框 按下按鍵後，就要進行對應的文字更新，\n如果是清除鍵，除了文字清空外，也要把儲存的點清空。 如果是產生 roi 鍵，就把現有的點輸出成我要的格式。 def clear_points(self): self.list_collect_points = [] self.__update_text_show_points() def __update_text_show_points(self): msg = \u0026#34;Current points (right click to return origin): \u0026#34; for ele in self.list_collect_points: msg += f\u0026#34;({ele[0]},{ele[1]}) \u0026#34; self.ui.text_save_points.setText(msg) def generate_rois(self): msg = \u0026#34;[ \u0026#34; for ele in self.list_collect_points: msg += f\u0026#34;[{ele[0]},{ele[1]}], \u0026#34; msg += \u0026#34;]\u0026#34; self.ui.text_output_rois.setText(msg) 設定滑鼠控制，建立「畫點畫線」的機制 替我們的 Qlabel 定義一個按鍵 function，\n偵測 Qlabel 上的滑鼠點擊事件，並儲存座標進 self.list_collect_points 裡面。\n新增點之後，更新點至顯示畫面。\ndef mouse_press_event(self, event): print(f\u0026#34;[show_mouse_press] {event.x()=}, {event.y()=}, {event.button()=}\u0026#34;) norm_x = event.x()/self.qpixmap.width() norm_y = event.y()/self.qpixmap.height() if event.button() == 2: # right clicked self.list_collect_points.append(self.list_collect_points[0]) self.__update_text_show_points() def __update_points_onscreen(self, frame): if len(self.list_collect_points) == 0: pass else: # len(list) \u0026gt;= 1 # first points frame = opencv_engine.draw_point(frame, point=self.list_collect_points[0], color = (0, 0, 255)) # red # if len = 1, no lines for idx in range(1, len(self.list_collect_points)): frame = opencv_engine.draw_point(frame, point=self.list_collect_points[idx], color = (0, 0, 255)) # red frame = opencv_engine.draw_line(frame, start_point =self.list_collect_points[idx-1], end_point=self.list_collect_points[idx], color = (0, 255, 0)) # green return frame 這邊「畫點畫線」的功能，我們去 opencv_engine 新增 新增畫點畫線功能，使用 OpenCV class opencv_engine(object): @staticmethod def norm_point_to_int(img, point): img_height, img_width, img_channel = img.shape return (int(img_width*point[0]), int(img_height*point[1])) @staticmethod def draw_point(img, point=(0, 0), color = (0, 0, 255)): # red point = opencv_engine.norm_point_to_int(img, point) # print(f\u0026#34;get {point=}\u0026#34;) point_size = 10 thickness = 4 return cv2.circle(img, point, point_size, color, thickness) @staticmethod def draw_line(img, start_point = (0, 0), end_point = (0, 0), color = (0, 255, 0)): # green start_point = opencv_engine.norm_point_to_int(img, start_point) end_point = opencv_engine.norm_point_to_int(img, end_point) thickness = 3 # width return cv2.line(img, start_point, end_point, color, thickness) 測試結果 好啦，我自己要用的話 Video Player 畫 ROI 工具就這樣完成了!!!\nReference ","date":"2021-10-09T02:27:45+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-5-1.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/th-psqq-pyqt/pyqt5-27/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 27 project / 製作影片 ROI 標註工具 (PyQt 結合 OpenCV 在圖上畫點畫線)"},{"categories":["444 - nano"],"content":"前言 nano 是傳統終端機 (terminal) 經常使用的編輯器，\n學習 nano 能幫助我們在不用安裝視覺化軟體的情況下修改檔案。\n不過我比較習慣用 vim，用 nano 除非是預設又不得已的情況\u0026hellip;\n那就先來改怎麼把預設編輯器修改成 vim 吧! sudo update-alternatives --config editor 會出現一堆選單，選擇有 vim 的選項，就完成囉!\n(沒有 vim 請先安裝 vim)\n保存檔案 Ctrl+O：可以保存修改\n退出 nano Ctrl+X：可以退出 nano\nReference nano编辑器使用教程 Change the Default Editor From Nano on Ubuntu Linux ","date":"2021-10-09T00:54:39+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/nano/linux-ubuntu-nano/","tags":["Bash","Linux","Ubuntu","nano"],"title":"【nano #1】nano 的 新手/初學者 的基礎使用指令 與 個人常用功能總整理 (持續更新)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 多了一條滑條，我們可以直接控制，另外我們也可以直接透過滑條來操控進度 另外這次有解決上一篇 lag 的問題，會說明原因以及解法。 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day26_video_player_add_slider_project\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n設計我們的 UI 主要就是新增滑條的部分，新元素的名稱：\nself.slider_videoframe：滑條 轉換 day26.ui -\u0026gt; UI.py pyuic5 -x day26.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 設計我們的 controller 使用 QSlider 我們已經在 【PyQt5】Day 14 – 使用 QSlider 製作可拖曳的滑條 有詳細的教學 QSlider 該如何使用了，\n這邊我們就直接使用吧！\ndef init_video_info(self): self.ui.slider_videoframe.setRange(0, self.video_total_frame_count-1) self.ui.slider_videoframe.valueChanged.connect(self.getslidervalue) def __get_frame_from_frame_no(self, frame_no): self.setslidervalue(frame_no) def getslidervalue(self): self.current_frame_no = self.ui.slider_videoframe.value() def setslidervalue(self, value): self.ui.slider_videoframe.setValue(self.current_frame_no) 1. 取得滑條值的部分 我們在 init_video_info() 新增了關於滑條初始化的功能，\n我們設定好這個滑條的 range 為 (0, 全部 frame 數 -1)，\n並且將這個滑條連結於 getslidervalue() 的功能上，\n只要我們移動滑條，就會啟動這個函數。\n2.變更滑條值的部分 我們製作了一個函數 setslidervalue()，當我們更改 frame 的時候，\n我們可以直接也更改滑條的值。\n而呼叫這個 setslidervalue() 的 function 位於取得 frame from frame number 的時候，\n也同步呼叫這個函數，就可以完成「隨著 frame 變化更改滑條的值」。\n優化我們的播放器效能 (解決昨天的 lag 問題) 昨天我們提到我們程式執行的時候會有 lag 的問題，\n那時我是直接給個優化的方向，是我們可以考慮提程式的 decode 加入「multiprocessing」的平行運算功能。\n不過今天處理的過程中，我稍微替我的程式加了幾個計時器，\n後來意外發現卡住的 function 其實只有一個，這樣就好處理了！\n處理我們先前程式的 bottleneck 以這支程式來說，很直覺的我會認為會慢都是牽扯到 decode 那一段的速度，\n因為處理圖片基本上就是最花時間的地方\u0026hellip;\n因此我加了一些 timer 在昨天的 code\ndef __get_frame_from_frame_no(self, frame_no): time_start = time.time() self.vc.set(1, frame_no) ret, frame = self.vc.read() time_end = time.time() print(time_end - time_start) 我們來計時一下，這段處理到底花了多少時間。\n結果發現了一個很有趣的現象：\nstop 時，平均一個 frame 只需要處理 0.01~0.02 秒左右 一但進入 start 或 pause 的狀態，平均一個 frame 需要處理 0.06~0.07 秒左右 這就很奇怪了！！！ 照理來說處理一個圖片，應該也不會到有那麼大的誤差。\n而且是平均時間，還不是幾張圖片或許資訊比較豐富所以處理比較久。\n這表示我們設計的機制一定有什麼可優化的問題。\n再繼續往下查，抓出產生問題的關鍵 function 最後我們發現一件有趣的事情：\n原本我以為是處理處片的時間很久，結果只花了 0.001 秒\nret, frame = self.vc.read() 然而卻是以下這行，設定人在哪個 frame 的函數，可能會造成約 0.05 秒左右的延遲。\nself.vc.set(1, frame_no) 但是，我之前做過的專案經驗告訴我，正常來說的解碼不會那麼久，\n所以一定是我不夠正確的使用這一行。\n所以我決定修改機制。\n重新設計使用 vc.set() 的機制，減少使用 照官方文件的定義，即使沒有 vc.set()，只需要一直 vc.read() 也能夠一直往下取 frame，\n我猜可能這就是原因了，因為 OpenCV (或說是他使用的 ffmpeg library) 在 decode 的時候，\n針對連續的 frame 有做優化的演化法，\n「所以如果我每次都重新設定第幾個 frame，會導致這個優化演算法失效」\n可以想像是，因為我們的影片都是連續的，\n所以搞不好可以透過計算向量差的方式，更快的算出下一張圖片。(而這機制被我的設計弄到失效)\n於是我們更改一下原本的邏輯，「只要必須要設定 frame 時，才使用 vc.set()」\n把顯示 frame 的函數拆成兩個 function 我們把 self.vc.set(1, frame_no) 這個會造成 bottleneck 的 funciton 獨立出來。\ndef set_current_frame_no(self, frame_no): self.vc.set(1, frame_no) # bottleneck def __get_next_frame(self): ret, frame = self.vc.read() self.ui.label_framecnt.setText(f\u0026#34;frame number: {self.current_frame_no}/{self.video_total_frame_count}\u0026#34;) self.setslidervalue(self.current_frame_no) return frame 配合上述機制的修改對應設計 def timer_timeout_job(self): if (self.videoplayer_state == \u0026#34;play\u0026#34;): if self.current_frame_no \u0026gt;= self.video_total_frame_count-1: #self.videoplayer_state = \u0026#34;pause\u0026#34; self.current_frame_no = 0 # auto replay self.set_current_frame_no(self.current_frame_no) else: self.current_frame_no += 1 if (self.videoplayer_state == \u0026#34;stop\u0026#34;): self.current_frame_no = 0 self.set_current_frame_no(self.current_frame_no) if (self.videoplayer_state == \u0026#34;pause\u0026#34;): self.current_frame_no = self.current_frame_no self.set_current_frame_no(self.current_frame_no) frame = self.__get_next_frame() self.__update_label_frame(frame) 原本會更新畫面的函數位置不變，而我們在 pause、stop、與影片播放完畢後，\n都啟用 set_current_frame_no() 這個函數，才會去啟動 vc.set() 修改 frame index。\ndef getslidervalue(self): self.current_frame_no = self.ui.slider_videoframe.value() self.set_current_frame_no(self.current_frame_no) 另外一個也會影響到 frame index 的就是滑條，\n我們也是在滑條「被移動」的時候，才會去呼叫 set_current_frame_no() 啟動 vc.set()\n測試結果 我的影片播放器終於順暢了！！！ 耶！！！\n此外昨天保留的計算 fps 機制就可以拿回來用了。\n如果不想要讓影片已超快的 1ms 更新，可以改回上面 timer 的做法。\nself.timer.start(1000//self.video_fps) # start Timer, here we set \u0026#39;1000ms//Nfps\u0026#39; while timeout one time self.timer.start(1) # but if CPU can not decode as fast as fps, we set 1 (need decode time) Reference ","date":"2021-10-08T16:15:38+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/pyqt5-27-4.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-26/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 26 project / 替我們影片播放器增加一個顯示進度的滑條 video player add slider (與昨日 bottleneck 處理細節)"},{"categories":["399 – Bash 自用腳本"],"content":"前言 有時候我們使用 linux 介面的時候，不見得都有 GUI 能夠使用，\n這邊記錄一下純用指令安裝 anaconda 的過程。\n安裝 anaconda 這是我需要的，所以留在這邊\n要確認最新版本 Anaconda 請至此：[https://repo.anaconda.com/archive/](https://repo.anaconda.com/archive/) 請搜尋 「Linux-x86_64.sh」 結尾的版本。 方法一：透過 wget 取得安裝腳本 bash Anaconda-latest-Linux-x86_64.sh wget -P /tmp https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh sha256sum /tmp/Anaconda3-2021.05-Linux-x86_64.sh bash /tmp/Anaconda3-2021.05-Linux-x86_64.sh 方法二：透過 curl 取得安裝腳本 # 安裝 anaconda cd /tmp sudo apt install curl -y curl -O https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh sha256sum Anaconda3-2021.05-Linux-x86_64.sh bash Anaconda3-2021.05-Linux-x86_64.sh 安裝 anaconda 過程中的注意事項 (注意 conda init !!!!!) 記得以上兩種安裝方式中，有個 conda init 的選項，\n如果想要 terminal 預設啟動就是 conda 環境，強烈建議要選 yes !!! (他預設給 no，但我覺得大家的習慣來說應該更希望 yes)\n啊如果不小心就一直按 Enter，應該也會不小心把 conda init 的選項按成 no，\n沒關係我們就自己去改，去修改 vim ~/.bashrc (或看你的 shell 也許是 ~/.zshrc)\n找個地方補一下以下這段：\n(記得使用者要改啊! 如果你使用者名稱不是 ubuntu，記得改成你的名稱)\n# \u0026gt;\u0026gt;\u0026gt; conda initialize \u0026gt;\u0026gt;\u0026gt; # !! Contents within this block are managed by \u0026#39;conda init\u0026#39; !! __conda_setup=\u0026#34;$(\u0026#39;/home/ubuntu/anaconda3/bin/conda\u0026#39; \u0026#39;shell.bash\u0026#39; \u0026#39;hook\u0026#39; 2\u0026gt; /dev/null)\u0026#34; if [ $? -eq 0 ]; then eval \u0026#34;$__conda_setup\u0026#34; else if [ -f \u0026#34;/home/ubuntu/anaconda3/etc/profile.d/conda.sh\u0026#34; ]; then . \u0026#34;/home/ubuntu/anaconda3/etc/profile.d/conda.sh\u0026#34; else export PATH=\u0026#34;/home/ubuntu/anaconda3/bin:$PATH\u0026#34; # export PATH=\u0026#34;/home/ubuntu/anaconda3/envs/mlenv/bin:$PATH\u0026#34; # set python3 as default # export PATH=\u0026#34;/home/ubuntu/anaconda/envs/python3/bin:$PATH\u0026#34; fi fi unset __conda_setup # conda config --set auto_activate_base false # conda activate myenv # \u0026lt;\u0026lt;\u0026lt; conda initialize \u0026lt;\u0026lt;\u0026lt; (optional) 隨便建立一個測試的環境 只是測試用，可不做。\n(記得要重新啟動 terminal 修改才會生效)\nconda list conda create --name myenv python=3 conda activate myenv Reference 要確認最新版本 Anaconda 請至此：https://repo.anaconda.com/archive/ ","date":"2021-10-08T02:07:46+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/bash/linux-ubuntu-anaconda/","tags":["Bash","Linux","Ubuntu","自用腳本"],"title":"【Linux】自動化腳本 / Linux (ubuntu) 上純使用 terminal 安裝 anaconda (install anaconda without GUI)"},{"categories":["923 - Windows"],"content":"前言 如果也是長期在 linux 上進行開發的工程師，\n相信都對 bash, zsh 等等不陌生，\n但每次回到 windows 想進行類似的操作，卻又因為語法不同而非常困擾，\n這次要推薦的也許是 windows 上最貼近 linux 的 terminal，\n在 windows 上安裝 fluent terminal (可使用 bash, zsh)。\n我處理完後的樣子如下 (在 windows 電腦上) 使用到目前為止簡評一下：除了 $HOME 跟想像中不太一樣之外，\n其他跟 windows 都是互通的，覺得很不錯。\n(windows 的指令與 linux 環境差很多，一直很不習慣)\n安裝 fluent terminal step 1. (前置作業) 在 windows 安裝 WSL (Windows Subsystem for Linux) 第一次安裝的話，打開 powershell (記得管理員權限)\nEnable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux 這邊細節就不贅述了，在我的另外一篇文章有講到更多細節。\n請參考：【Docker】在 windows 上使用安裝並使用 Docker (全圖文說明) windows 安裝 WSL (Windows Subsystem for Linux)\nstep 2. (前置作業) 準備一個 linux 環境 這邊我們可以去 Microsoft Store 找一個自己喜歡的 linux 環境，\n如果不知道怎麼選就選最經典的 Ubuntu 吧!\nstep 3. 安裝 Chocolatey，這是一種 Windows 套件的安裝包管理器 (類似 apt) 【Docker】在 windows 上使用安裝並使用 Docker (全圖文說明) windows 安裝 WSL (Windows Subsystem for Linux)\n這裡我使用 powershell 的方式安裝 (記得務必使用系統管理員)\nSet-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString(\u0026#39;https://chocolatey.org/install.ps1\u0026#39;)) 或者也可以使用 CMD 的方式安裝 (記得務必使用系統管理員)\n```bash Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString(\u0026#39;https://chocolatey.org/install.ps1\u0026#39;)) ```text ## step 4. 使用 Chocolatey 安裝 fluent terminal ```bash choco install fluent-terminal ```text # fluent terminal 的一些初始設定 從上面的步驟中，我們已經安裝好 fluent terminal， 打開來後，我們需要先進行一些初始設定。 ## 設定 terminal 為 WSL terminal 打開 fluent terminal，可以從左上角的選單中找到設定， 我們需要去修改「終端設定」裡面的選項，修改為 WSL， 並在「\u0026lt;mark\u0026gt;預設設定\u0026lt;/mark\u0026gt;」那邊打勾。 ![](/images/restored/2021/10/windows-fluent-terminal-1.png) \u0026gt; 基本上，到此就已經有了一個能在 windows 執行 linux bash 的系統了。 # (optional) 安裝 zsh, oh-my-zsh ## 安裝 zsh, oh-my-zsh 既然都已經是 linux 環境了，應該不用我們多教XD ```bash #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/5] install zsh\u0026#34; sh -c \u0026#34;(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; sudo apt-get install zsh -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] install oh-my-zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; echo \u0026#34;[5/5] change default shell to zsh shell\u0026#34; chsh -s /bin/zsh ```text 到此，zsh, oh-my-zsh 應該都已經裝好了。 \u0026gt; 註：zsh 為 shell script oh-my-zsh 為主題，如果你只想要 zsh shell 的可不用裝主題 XD # (optional) 安裝 powerlevel10k \u0026amp; 對應字體 ## 安裝 powerlevel10k ```bash #!/bin/bash # ----- powerlevel10k theme ------ # echo \u0026#34;install powerlevel10k\u0026#34; git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k echo \u0026#39;source ~/powerlevel10k/powerlevel10k.zsh-theme\u0026#39; \u0026gt;\u0026gt;! ~/.zshrc echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc ```text - 參考內容：我的另一篇文：[【Linux】自動化腳本 / 安裝 zsh, oh-my-zsh, powerlevel10k](https://wongwongnotes.com/posts/linux-shell/troubleshooting/bash/auto-script-zsh/) ## 安裝 powerlevel10k 對應字體 因為 windows 原生應該是沒有 powerlevel10k 的一些特殊字元， 需要我們特別去安裝一下。 其他網站提供的下載的方式很多不能用了，另外如果將整包字體打包下載檔案也非常的巨大，因此參考其中一篇文章提供的方法，直接下載我們要的字體包。 - 字體載點：[https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/SourceCodePro.zip](https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/SourceCodePro.zip) 我是安裝裡面的以下字體： - Sauce Code Pro ExtraLight Nerd Font Complete.ttf - Sauce Code Pro ExtraLight Nerd Font Complete Mono.ttf ## 設定 fluent terminal 字體 一樣去剛剛的設定裡面，找到終端，裡面第一個選擇我們剛剛安裝的字體， 我選擇的是「Sauce Code Pro ExtraLight Nerd Font Complete Mono.ttf」， 另外下面的字體大小、字體粗度(Font Widght)、字間距、半透明度， 就自己照自己喜歡改吧。 ![](/images/restored/2021/10/windows-fluent-terminal-2.png) ## (optional) 修改 powerlevel10k 等各種設定檔 (需修改 ~/.zshrc, ~/.p10k.zsh 文件) 這邊基本上就是修改主題與介面， 懂得就自己修改成自己喜歡的樣子吧。 不懂的話... 建議是不要亂改啦，(建議做一點功課再來慢慢改) 萬一不小心改到 terminal 不能用了，也是滿麻煩的XD ```bash echo \u0026#34;[optional] set ~/.zshrc (zsh settings)\u0026#34; vim ~/.zshrc echo \u0026#34;[optional] set ~/.p10k.zsh (powerlevel10k settings)\u0026#34; cp ~/.p10k.zsh ~/.p10k.zsh.bak # backup #p10k configure # GUI version vim ~/.p10k.zsh echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc ```text - 我修改完 ~/.p10k.zsh 後的長這樣 ![](/images/restored/2021/10/windows-fluent-terminal-3.png) ## (optional) install zsh plugins zsh 好用的插件，我推薦以下兩個，自動完成與自動標示指令。 \u0026gt; 因為插件的關係，下面的\u0026#34;$\u0026#34;符號會被吃掉， 如果使用指令無法順利安裝，請參考官網的安裝指南： - [\u0026lt;mark\u0026gt;zsh-syntax-highlighting 官方文件\u0026lt;/mark\u0026gt;](https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.md) - [\u0026lt;mark\u0026gt;zsh-autosuggestions 官方文件\u0026lt;/mark\u0026gt;](https://github.com/zsh-users/zsh-autosuggestions/blob/master/INSTALL.md) ```bash #!/bin/bash # ----- install zsh plugins ------ # echo \u0026#34;[optional] install Syntax Highlighting Plugin\u0026#34; # doc: https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.md git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting echo \u0026#34;[optional] install ZSH-AutoSuggestion Plugin\u0026#34; # doc: https://github.com/zsh-users/zsh-autosuggestions/blob/master/INSTALL.md git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions ```bash ### 設定 plugins (需修改 ~/.zshrc 文件) 在 ~/.zshrc 裡面新增 plugins=() 裡面的東東， 一樣建議不知道自己在幹嘛就不要亂改XD ```bash echo \u0026#34;[optional] set ~/.zshrc to use plugins\u0026#34; vim ~/.zshrc plugins=( git zsh-syntax-highlighting zsh-autosuggestions ) echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc ```text # (optional) 安裝 anaconda 這是我需要的，所以留在這邊 \u0026lt;li\u0026gt;要確認最新版本 Anaconda 請至此：[https://repo.anaconda.com/archive/](https://repo.anaconda.com/archive/) 請搜尋 「\u0026lt;mark\u0026gt;Linux-x86_64.sh\u0026lt;/mark\u0026gt;」 結尾的版本。\u0026lt;/li\u0026gt; ## 方法一：透過 wget 取得安裝腳本 ```bash bash Anaconda-latest-Linux-x86_64.sh wget -P /tmp https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh sha256sum /tmp/Anaconda3-2021.05-Linux-x86_64.sh bash /tmp/Anaconda3-2021.05-Linux-x86_64.sh ```text ## 方法二：透過 curl 取得安裝腳本 ```bash # 安裝 anaconda cd /tmp sudo apt install curl -y curl -O https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh sha256sum Anaconda3-2021.05-Linux-x86_64.sh bash Anaconda3-2021.05-Linux-x86_64.sh ```text ## 安裝 anaconda 過程中的注意事項 (\u0026lt;mark\u0026gt;注意 conda init !!!!!\u0026lt;/mark\u0026gt;) 記得以上兩種安裝方式中，有個 conda init 的選項， 如果想要 terminal 預設啟動就是 conda 環境，\u0026lt;mark\u0026gt;強烈建議要選 yes !!! \u0026lt;/mark\u0026gt; (他預設給 no，但我覺得大家的習慣來說應該更希望 yes) 啊如果不小心就一直按 Enter，應該也會不小心把 conda init 的選項按成 no， 沒關係我們就自己去改，去修改 vim ~/.bashrc (或看你的 shell 也許是 ~/.zshrc) 找個地方補一下以下這段： (記得使用者要改啊! 如果你使用者名稱不是 ubuntu，記得改成你的名稱) ```bash # \u0026gt;\u0026gt;\u0026gt; conda initialize \u0026gt;\u0026gt;\u0026gt; # !! Contents within this block are managed by \u0026#39;conda init\u0026#39; !! __conda_setup=\u0026#34;$(\u0026#39;/home/ubuntu/anaconda3/bin/conda\u0026#39; \u0026#39;shell.bash\u0026#39; \u0026#39;hook\u0026#39; 2\u0026gt; /dev/null)\u0026#34; if [ $? -eq 0 ]; then eval \u0026#34;$__conda_setup\u0026#34; else if [ -f \u0026#34;/home/ubuntu/anaconda3/etc/profile.d/conda.sh\u0026#34; ]; then . \u0026#34;/home/ubuntu/anaconda3/etc/profile.d/conda.sh\u0026#34; else export PATH=\u0026#34;/home/ubuntu/anaconda3/bin:$PATH\u0026#34; # export PATH=\u0026#34;/home/ubuntu/anaconda3/envs/mlenv/bin:$PATH\u0026#34; # set python3 as default # export PATH=\u0026#34;/home/ubuntu/anaconda/envs/python3/bin:$PATH\u0026#34; fi fi unset __conda_setup # conda config --set auto_activate_base false # conda activate myenv # \u0026lt;\u0026lt;\u0026lt; conda initialize \u0026lt;\u0026lt;\u0026lt; ```text ## (optional) 隨便建立一個測試的環境 只是測試用，可不做。 (記得要重新啟動 terminal 修改才會生效) ```bash conda list conda create --name myenv python=3 conda activate myenv ```text # 小結：使用到目前為止的簡評 使用到目前為止簡評一下：除了 $HOME 跟想像中不太一樣之外， 其他跟 windows 都是互通的，覺得很不錯。 (windows 的指令與 linux 環境差很多，一直很不習慣) # Reference - [Cool windows terminal with Oh My ZSH.](https://lemmusm.medium.com/cool-windows-terminal-with-oh-my-zsh-8d2c1c759805) - [How to Install Anaconda on Ubuntu 20.04](https://linuxize.com/post/how-to-install-anaconda-on-ubuntu-20-04/) - 字體載點：[https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/SourceCodePro.zip](https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/SourceCodePro.zip) - 要確認最新版本 Anaconda 請至此：[https://repo.anaconda.com/archive/](https://repo.anaconda.com/archive/) \u0026lt;!-- Post ID: 4393 Post Date: 2021-10-08 01:49:33 Categories: 923 - Windows Tags: 923, sshfs, windows, Windows, 923 - Windows --\u0026gt; ","date":"2021-10-08T01:49:33+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/windows-fluent-terminal-3-1024x594.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/windows/windows-fluent-terminal/","tags":["sshfs"],"title":"【Windows】也許是 windows 上最貼近 linux 的 terminal，安裝 fluent terminal，在 windows 上使用 zsh (oh-my-zsh)"},{"categories":["047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】","132 - PyQt5"],"content":"看完這篇文章你會得到的成果圖 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day25_video_player_project\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n設計我們的 UI 我們在裡面加入了一些我們需要的元素：\nself.button_stop：停止鍵\nself.button_play：播放鍵\nself.button_pause：暫停鍵\nself.button_openfile：開啟檔案鍵\nself.label_videoframe：顯示畫面\nself.label_framecnt：顯示目前 frame 數/ 全部 frame 數\nself.label_filepath：顯示檔案路徑\n一些 UI 設計小細節 與之前設計圖片不同的是，我們拿掉了可以捲動的滑條， 我希望能夠強制更改比例以符合視窗 (方便一個視窗就能瀏覽)。 我設計的顯示框為 800x450，等於 16:9， 符合目前最常見的影片比例 1920x1080、1280x720 轉換 day25.ui -\u0026gt; UI.py pyuic5 -x day25.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 設計我們的 controller 設計使用狀態 (state) 我們要設計一個播放器，我們必須要想好播放器的架構可能會有哪幾種 ”state (狀態)“，\n我們可以簡單地想一下：\n按下 play 後，進行 play 狀態，影片播放 按下 pause 後，進行 pause 狀態，影片暫停 按下 stop 後，進行 stop 狀態，影片回到第一格 而剛開始載入影片時，我們選擇的狀態是 pause，因為暫停狀態才可以任意變更 frame 值 (後續的應用)，\n而停止狀態永遠都會回到第一格。\n以上大概就是我們設計的 state。\n設計 video_controller.py 正如同我們前面的文章，這次我們把 img_controller 修改為 video_controller，\n並加入類似的功能。\nfrom PyQt5 import QtCore from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import QTimer from opencv_engine import opencv_engine # videoplayer_state_dict = { # \u0026#34;stop\u0026#34;:0, # \u0026#34;play\u0026#34;:1, # \u0026#34;pause\u0026#34;:2 # } class video_controller(object): def __init__(self, video_path, ui): self.video_path = video_path self.ui = ui self.qpixmap_fix_width = 800 # 16x9 = 1920x1080 = 1280x720 = 800x450 self.qpixmap_fix_height = 450 self.current_frame_no = 0 self.videoplayer_state = \u0026#34;stop\u0026#34; self.init_video_info() self.set_video_player() def init_video_info(self): videoinfo = opencv_engine.getvideoinfo(self.video_path) self.vc = videoinfo[\u0026#34;vc\u0026#34;] self.video_fps = videoinfo[\u0026#34;fps\u0026#34;] self.video_total_frame_count = videoinfo[\u0026#34;frame_count\u0026#34;] self.video_width = videoinfo[\u0026#34;width\u0026#34;] self.video_height = videoinfo[\u0026#34;height\u0026#34;] def set_video_player(self): self.timer=QTimer() # init QTimer self.timer.timeout.connect(self.timer_timeout_job) # when timeout, do run one # self.timer.start(1000//self.video_fps) # start Timer, here we set \u0026#39;1000ms//Nfps\u0026#39; while timeout one time self.timer.start(1) # but if CPU can not decode as fast as fps, we set 1 (need decode time) def __get_frame_from_frame_no(self, frame_no): self.vc.set(1, frame_no) ret, frame = self.vc.read() self.ui.label_framecnt.setText(f\u0026#34;frame number: {frame_no}/{self.video_total_frame_count}\u0026#34;) return frame def __update_label_frame(self, frame): bytesPerline = 3 * self.video_width qimg = QImage(frame, self.video_width, self.video_height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(qimg) if self.qpixmap.width()/16 \u0026gt;= self.qpixmap.height()/9: # like 1600/16 \u0026gt; 90/9, height is shorter, align width self.qpixmap = self.qpixmap.scaledToWidth(self.qpixmap_fix_width) else: # like 1600/16 \u0026lt; 9000/9, width is shorter, align height self.qpixmap = self.qpixmap.scaledToHeight(self.qpixmap_fix_height) self.ui.label_videoframe.setPixmap(self.qpixmap) # self.ui.label_videoframe.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) # up and left self.ui.label_videoframe.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # Center def play(self): self.videoplayer_state = \u0026#34;play\u0026#34; def stop(self): self.videoplayer_state = \u0026#34;stop\u0026#34; def pause(self): self.videoplayer_state = \u0026#34;pause\u0026#34; def timer_timeout_job(self): frame = self.__get_frame_from_frame_no(self.current_frame_no) self.__update_label_frame(frame) if (self.videoplayer_state == \u0026#34;play\u0026#34;): self.current_frame_no += 1 if (self.videoplayer_state == \u0026#34;stop\u0026#34;): self.current_frame_no = 0 if (self.videoplayer_state == \u0026#34;pause\u0026#34;): self.current_frame_no = self.current_frame_no 我們開始來慢慢解釋這些東西。\n播放邏輯 這邊我們使用 Qtimer，原因很簡單，每支影片都有他自己的 fps，\n我們透過計算可以得到「我們應該每多少毫秒，就該換下一個 frame 顯示」。\n我們用 frame number 來管理現在要顯示哪一個畫面，\n而控制 frame number 的就是我們目前 state 的狀態，以每一個 QTimer timeout 的頻率更新。\ndef set_video_player(self): self.timer=QTimer() # init QTimer self.timer.timeout.connect(self.timer_timeout_job) # when timeout, do run one # self.timer.start(1000//self.video_fps) # start Timer, here we set \u0026#39;1000ms//Nfps\u0026#39; while timeout one time self.timer.start(1) # but if CPU can not decode as fast as fps, we set 1 (need decode time) def timer_timeout_job(self): frame = self.__get_frame_from_frame_no(self.current_frame_no) self.__update_label_frame(frame) if (self.videoplayer_state == \u0026#34;play\u0026#34;): self.current_frame_no += 1 if (self.videoplayer_state == \u0026#34;stop\u0026#34;): self.current_frame_no = 0 if (self.videoplayer_state == \u0026#34;pause\u0026#34;): self.current_frame_no = self.current_frame_no 但這邊我們在執行後才發現我們雖然邏輯正確，但想得太美了\nOpenCV 在 decode 所需要花的時間大於我們想要控制的顯示時間，\n(簡單來說，decode 太久，導致沒辦法在依照我們想要的 fps 播放)\n所以我先暫時改成 self.timer.start(1)，讓我們只休息 1ms，\n但畢竟 QT 是以 multithread 在進行操作，\n這段優化的空間可能要改以 multiprocess 進行才能夠讓我們影片順暢的播放 (這個比較不是此系列重點，有空我們再來實作)\n延伸閱讀：【Python】multiprocessing – 用多核心來執行程式 (內含範例程式碼) sample code 延伸閱讀：【Python】multiprocessing pool, map, apply_async – 用多核心來執行程式並取得結果 (內含範例程式碼) sample code 播放鍵相關 (play, stop, pause) def play(self): self.videoplayer_state = \u0026#34;play\u0026#34; def stop(self): self.videoplayer_state = \u0026#34;stop\u0026#34; def pause(self): self.videoplayer_state = \u0026#34;pause\u0026#34; 這邊我使用的邏輯，就是讓按鍵會直接更改到 state 的狀態，\n而 state 會去控制現在視窗要顯示的 frame\n取得 frame 的圖片，並更新至 UI 介面上 從上面應該可以理解一些小細節，我們用 frame number 來管理我們要顯示的 frame，\n而我們透過 frame number 取得 frame 影像的機制，我們寫在 __get_frame_from_frame_no() 當中。\n而取得介面後，並更新於 UI 介面的機制，我們寫在 __update_label_frame() 當中。\n這邊也有個小細節，我們會自動以 16:9 為基準去看讀入影片的比例，\n如果很明顯是較寬 例如 160:9，我們就以寬 (160) 為基準去縮放影片大小 如果很明顯是較高 例如 16:90，我們就以高 (90) 為基準去縮放影片大小 def __get_frame_from_frame_no(self, frame_no): self.vc.set(1, frame_no) ret, frame = self.vc.read() self.ui.label_framecnt.setText(f\u0026#34;frame number: {frame_no}/{self.video_total_frame_count}\u0026#34;) return frame def __update_label_frame(self, frame): bytesPerline = 3 * self.video_width qimg = QImage(frame, self.video_width, self.video_height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(qimg) if self.qpixmap.width()/16 \u0026gt;= self.qpixmap.height()/9: # like 1600/16 \u0026gt; 90/9, height is shorter, align width self.qpixmap = self.qpixmap.scaledToWidth(self.qpixmap_fix_width) else: # like 1600/16 \u0026lt; 9000/9, width is shorter, align height self.qpixmap = self.qpixmap.scaledToHeight(self.qpixmap_fix_height) self.ui.label_videoframe.setPixmap(self.qpixmap) # self.ui.label_videoframe.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) # up and left self.ui.label_videoframe.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # Center 設計 opencv_engine.py 讀取影片的資訊 相信有閱讀之前文章的讀者應該都不陌生，\n而這邊我們要透過 opencv 協助我們完成影片的讀取，並分析一些資訊。\n程式被呼叫的地方在 video_controller 的 init_video_info，\n我們把所有必要的影片資訊封裝成一個 dict 回傳。\ndef init_video_info(self): videoinfo = opencv_engine.getvideoinfo(self.video_path) self.vc = videoinfo[\u0026#34;vc\u0026#34;] self.video_fps = videoinfo[\u0026#34;fps\u0026#34;] self.video_total_frame_count = videoinfo[\u0026#34;frame_count\u0026#34;] self.video_width = videoinfo[\u0026#34;width\u0026#34;] self.video_height = videoinfo[\u0026#34;height\u0026#34;] 所以我們在 opencv_engine.py 實作一個新的方法。\n@staticmethod def getvideoinfo(video_path): # https://docs.opencv.org/4.5.3/dc/d3d/videoio_8hpp.html videoinfo = {} vc = cv2.VideoCapture(video_path) videoinfo[\u0026#34;vc\u0026#34;] = vc videoinfo[\u0026#34;fps\u0026#34;] = vc.get(cv2.CAP_PROP_FPS) videoinfo[\u0026#34;frame_count\u0026#34;] = int(vc.get(cv2.CAP_PROP_FRAME_COUNT)) videoinfo[\u0026#34;width\u0026#34;] = int(vc.get(cv2.CAP_PROP_FRAME_WIDTH)) videoinfo[\u0026#34;height\u0026#34;] = int(vc.get(cv2.CAP_PROP_FRAME_HEIGHT)) return videoinfo 把一些我們感興趣的資訊都存進 videoinfo 裡面，並回傳。\n設計 controller.py 設計按鍵與功能的連結、開啟檔案 def setup_control(self): self.ui.button_openfile.clicked.connect(self.open_file) def open_file(self): filename, filetype = QFileDialog.getOpenFileName(self, \u0026#34;Open file Window\u0026#34;, \u0026#34;./\u0026#34;, \u0026#34;Video Files(*.mp4 *.avi)\u0026#34;) # start path self.video_path = filename self.video_controller = video_controller(video_path=self.video_path, ui=self.ui) self.ui.label_filepath.setText(f\u0026#34;video path: {self.video_path}\u0026#34;) self.ui.button_play.clicked.connect(self.video_controller.play) # connect to function() self.ui.button_stop.clicked.connect(self.video_controller.stop) self.ui.button_pause.clicked.connect(self.video_controller.pause) 我們先讓開啟檔案按鍵的功能連結起來，\n開檔成功之後，才綁定按鍵的功能，這些功能定義在 video_controller 中。\n我們預期所有的按鍵行為應該是在「開檔後」才會執行 (例如：沒讀取影片，沒必要讓「播放」有功能。)\n測試結果 我們目前有一個很 lag 的 video player，\n原因可能是因為 decode 速度不夠快，可能可以透過 multiprocess 優化。\n後續：後來有找到原因，為 vc.set 反覆執行會吃掉大量程式效率，之後文章會再分享該如何修正\nReference ","date":"2021-10-07T17:37:55+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/pyqt5-25-1.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/th-psqq-pyqt/pyqt5-25-video_player/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 25 project / 自己做一個影片播放器 DIY video player (結合 PyQt + OpenCV)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day24_color_detector_project\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n設計我們的 UI 接下來我們要設計一個偵測滑鼠指示顏色的小工具\n我們針對顏色設計一下欄位：\nself.text_pos：顯示位置用 self.text_rgb：顯示 RGB 用 self.text_hex：顯示 HEX 用 轉換 day24.ui -\u0026gt; UI.py pyuic5 -x day24.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 設計我們的 controller 這次我們的 controller 有三個主要的新功能\nQTimer：負責更新頻率 QCursor：取得現在滑鼠座標位置 QApplication：取得現在螢幕的截圖 QTimer：負責更新頻率 為了避免我們的程式吃光我們電腦的所有資源，\n我們用更新時間加以限制，不需要到幾乎無時無刻都在更新，\n以「20ms (0.02 秒)」的頻率更新畫面，基本上人體就已經感覺不出差別了，\n而且也比較省電腦資源。而上述的功能可以使用 QTimer 來實現。\ndef setup_control(self): self.ui.label.setText(\u0026#34;\u0026#34;) timer = QTimer(self) timer.timeout.connect(self.get_current_cursor_color) timer.start(20) 我們在 setup_control 進行這樣的設定，另外我們想把剛剛 Color 那段字消掉，\n只留下顏色就好，於是我們補上一行 self.ui.label.setText(\u0026quot;\u0026quot;)\nQCursor：取得現在滑鼠座標位置、 QApplication：取得現在螢幕的截圖 QCursor 可以幫助我們取得現在滑鼠的座標，\n而透過 QApplication 可以幫助我們取得現在螢幕的截圖。\ndef get_current_cursor_color(self): x = QCursor.pos().x() y = QCursor.pos().y() self.ui.text_pos.setText(f\u0026#34;X:{x:^4d} Y:{y:^4d}\u0026#34;) pixmap = QApplication.primaryScreen().grabWindow( QApplication.desktop().winId(), x, y, 1, 1) image = pixmap.toImage() color = QColor(image.pixel(0, 0)) self.set_label_color(color) set_label_color() 設定顏色與相關顏色資訊 這部分是拿昨天我們做的顏色選擇器來改的，\n主要功能都相同，我們把昨天 print 顯示的字串，\n現在改成直接顯示在 textEdit 裡面。\n傳進來的資料型態都是 Qcolor，\n這邊簡單分析一下，\n我們可以透過 color.red(), color.green(), color.blue() 取得 RGB 的 0~255 的值，\n使用 color.name() 取得顏色的 hex 值。\ndef set_label_color(self, color): r, g, b = color.red(), color.green(), color.blue() strRGB = (f\u0026#34;{r:^3d}, {g:^3d}, {b:^3d}\u0026#34;) self.ui.label.setStyleSheet(\u0026#39;background-color:rgb({});\u0026#39;.format(strRGB)) self.ui.text_rgb.setText(f\u0026#34;({strRGB})\u0026#34;) self.ui.text_hex.setText(color.name().upper()) 更符合使用者的設計：視窗永久置頂 這邊我們拿 【PyQt5】Day 22 – PyQt 視窗的個性化/屬性控制 setWindowFlags，禁止放大縮小、永遠顯示於最上層/最下層 的 setWindowFlags 內容來應用，\n我們只允許「縮小與關閉」跟「永遠讓視窗在最上層」。\n我們去新增 UI.py 如下：\n(我們這次選擇的是 Form 不是 MainWindow，需要稍微修改一下)\n# 只有縮小/關閉 (取消放大) # 視窗永遠在最上層，適合互動性高的程式 Form.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint| Qt.WindowStaysOnTopHint) 測試結果 總之就是可以自動偵測滑鼠目前所指位置的顏色，並顯示出來。\nReference Python3與PyQt5編程示例：實現顏色拾取器小工具 原文網址：https://kknews.cc/code/gpkapl9.html ","date":"2021-10-06T10:57:23+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/pyqt5-24-5.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-24/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 24 project / 偵測滑鼠目前指示顏色的小工具 (滴管工具), 利用 QCursor 偵測滑鼠, QApplication 取得截圖"},{"categories":["320 - Linux 搜尋內容"],"content":"前言 在 linux 的終端機中，我們可以使用 grep 來幫助我們搜尋一些資訊。\n包含文件檔案、或 log 的文件內容等，\n甚至可以搭配 tail -f，隨時監控程式是否有一些我們預料之外的狀況發生。\ngrep -A -B -C 使用方式 cat test.txt | grep \u0026#34;key\u0026#34; -A N # N = N 行 cat test.txt | grep \u0026#34;key\u0026#34; -B N # N = N 行 cat test.txt | grep \u0026#34;key\u0026#34; -C N # N = N 行 grep -A -B -C 說明 我們可以這樣記：\nA：After 顯示搜尋到 \u0026ldquo;key\u0026rdquo; 的之後 N 行 B：Before 顯示搜尋到 \u0026ldquo;key\u0026rdquo; 的之前 N 行 C：Center (Context) 顯示搜尋到 \u0026ldquo;key\u0026rdquo; 的前後 N 行 Reference ","date":"2021-10-05T21:48:14+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/grep-log-abc/","tags":["Bash","Linux","Ubuntu","搜尋內容"],"title":"【Linux 搜尋內容 #4】grep - 蒐集 log 或任何文件內的訊息 (grep -A -B -C 的範圍搜尋)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是我在使用 fish shell 時，後來因為想要更換回 zsh shell，\n結果 terminal 沒有反應更換成功，走冤枉路很久最後才找到的解決辦法。\n想從 fish shell 更換回 zsh shell 的原因是，\n從 fish shell 下指令時有時候不夠直覺，\n例如：「mv .//.mp4 .」，fish shell 是無法直接解析這個指令的，\n這讓我覺得有點不夠直覺。\n解決方法 sudo chsh -s /bin/zsh 或 sudo chsh -s /usr/bin/fish ubuntu 執行上述兩個指令的其中一個指令之後，\n記得要「重新開機」，才會套用變更。\n之前我就是卡在沒有重新開機，所以其實已經改成功了，\n但因為沒有「重新開機」，所以以為沒有修改成功。\n系統「不會主動提示」要你在執行 chsh 指令後要重新開機，所以必須自行重開。\nReference How to Change Your Default Shell on Linux with chsh chsh -s /usr/bin/fish does not work on Ubuntu 12.04 ","date":"2021-10-05T21:28:29+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/chsh-not-work/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】Ubuntu 問題解決: chsh -s /bin/zsh, /usr/bin/fish does not work, 無法更換 terminal shell fish to zsh"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day23_QColorDialog\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n系統內建的調色盤 QColorDialog 在 mac, windows, linux 上都會有系統預設的調色盤 QColorDialog,\n隨著使用系統不同也會得到不同的調色盤。\n而我們今天要藉由這個工具來幫助我們挑選顏色。\n建立 UI 替 Qlabel 建立一個 border 我們修改 Qframe 的屬性\n這樣我們就能建立一個明確的邊框\n關於 Qframe 的屬性，可以參考這邊查詢\n延伸閱讀：QFrame类\n文內提供的參考圖：\n轉換 day23.ui -\u0026gt; UI.py pyuic5 -x day23.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 建立控制 controller.py 設定按鈕、顯示顏色 我們先將 label 內的文字設為 \u0026ldquo;QColorDialog\u0026rdquo;，\n然後連結 pushButton 至 set_label_color 這個 function\nfrom PyQt5 import QtCore from PyQt5.QtWidgets import QMainWindow, QColorDialog from UI import Ui_MainWindow class MainWindow_controller(QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): self.ui.label.setText(\u0026#34;QColorDialog\u0026#34;) self.ui.pushButton.clicked.connect(self.set_label_color) 在 set_label_color()，我們使用 QColorDialog 我們只要呼叫 QColorDialog.getColor()，就會呼叫系統內建的調色盤，\n之後我們可以透過\nprint(color.name())：會得到 CSS 值 #ff5b87\nprint(color.red(), color.green(), color.blue())：會得到 RGB 值 (0~255) # 255 91 135\nself.ui.label.setStyleSheet(\u0026lsquo;background-color:rgb({});\u0026rsquo;.format(strRGB))：直接設定背景顏色，需先轉換格式為 strRGB\ndef set_label_color(self): color = QColorDialog.getColor() # OpenColorDialog if color.isValid(): print(color.name()) #ff5b87 print(color.red(), color.green(), color.blue()) # 255 91 135 r, g, b = color.red(), color.green(), color.blue() strRGB = (\u0026#39;{:^3d}, {:^3d}, {:^3d}\u0026#39;.format(r, g, b)) self.ui.label.setStyleSheet(\u0026#39;background-color:rgb({});\u0026#39;.format(strRGB)) mac 上的 QColorDialog 系統內建的顏色選擇器，已經做得非常精緻了 選擇好顏色後，填入結果 另外依照我們目前的寫法，我們也可以在 terminal 取得我們填色的 RGB 與 CSS，\n之後我們可以使用這個值來繼續做顏色的操作。\nReference [PyQt5] 基本教學(11) 使用 QColorDialog 調色盤來進行顏色的設定 PyQt5 – How to add border on QLabel ? ","date":"2021-10-05T18:59:17+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/pyqt5-23-5.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-23-qcolordialog/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 23 - 使用系統內建的調色盤 QColorDialog，來替我們選擇顏色 QColor (Color Picker)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github 視窗的個性化控制 setWindowFlags 這篇文章我們要來討論 PyQt 裡面一個很酷的東西，setWindowFlags，\n我們主要要討論的功能都是未來我會用到的功能。\n在 setWindowFlags 裡面有很多的定義，\n其中有幾個我會用到\n顯示於最上層/最下層 Qt.WindowStaysOnTopHint: 視窗永遠在最上層，適合互動性高的程式 Qt.WindowStaysOnBottomHint: 視窗永遠在最下層，適合背景程式 關於放大/縮小的設定 當我們沒有特別設定參數時，只繼承 「QDialog」 的視窗預設都只會有關閉按鈕。\n如果是繼承自 「QWidget」，則縮小、放大、關閉按鈕都會有。\nQt.WindowMinimizeButtonHint: 顯示最小化按鈕 Qt.WindowMaximizeButtonHint: 顯示最大化按鈕 Qt.WindowMinMaxButtonsHint: 顯示最小化按鈕和最大化按鈕 Qt.WindowCloseButtonHint: 顯示關閉按鈕 這些功能都定義在 PyQt5.QtCore 中，記得要 import !!!\nfrom PyQt5.QtCore import * 搭配上述的使用範例 我們就可以用以下的設定方式，來對視窗做一些特別的控制\n只有縮小/關閉 (取消放大) Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint 只有放大/關閉 (取消縮小) Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint 只有關閉 (取消放大縮小) Qt.WindowCloseButtonHint 範例 我們先透過 Qtdesinger 隨便建立一份 UI 介面\n並如同之前的方式，建立一個可直接執行的 UI.py\n轉換 day22.ui -\u0026gt; UI.py pyuic5 -x day22.ui -o UI.py 直接打開觀察目前的視窗 我們可以發現目前的視窗中，放大/縮小/關閉的功能都是有的。\n觀察目前的程式碼 打開 UI.py，我們發現我們的視窗果然是繼承於「QWidget」\n所以預設是縮小、放大、關閉按鈕都會有。\nself.centralwidget = QtWidgets.QWidget(MainWindow) 嘗試修改視窗屬性 # 只有縮小/關閉 (取消放大) MainWindow.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint) # 只有放大/關閉 (取消縮小) # MainWindow.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) # 只有關閉 (取消放大縮小) # MainWindow.setWindowFlags(Qt.WindowCloseButtonHint) # 視窗永遠在最上層，適合互動性高的程式 # MainWindow.setWindowFlags(Qt.WindowStaysOnTopHint) # 視窗永遠在最下層，適合背景程式 # MainWindow.setWindowFlags(Qt.WindowStaysOnBottomHint) # 如果要一起使用，記得都要 | 連接在一起，不然後來的會洗掉之前的 結果 例如我們取消放大的功能，結果如下圖\n注意「右上角」反白的「放大」功能。 注意 如果以上的要一起使用(混用)，記得都要 | 連接在一起，不然後來的會洗掉之前的\n例如，如果我同時想要\u0026quot;取消放大縮小、只留下關閉\u0026quot;，且還要視窗永遠在最上層。\n我要寫\n# 只有縮小/關閉 (取消放大) # 視窗永遠在最上層，適合互動性高的程式 MainWindow.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint | Qt.WindowStaysOnTopHint) 不能這樣寫 (後者會洗掉前者的功能)\n# 只有縮小/關閉 (取消放大) MainWindow.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint) # 視窗永遠在最上層，適合互動性高的程式 MainWindow.setWindowFlags(Qt.WindowStaysOnTopHint) Reference [PyQt5] 讓窗口位於螢幕最前方或最後方 Qt個性化定製最大化最小化關閉等設定 ","date":"2021-10-05T00:10:08+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-5-1.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-22-setwindowflags/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 22 - PyQt 視窗的個性化/屬性控制 setWindowFlags，禁止放大縮小、永遠顯示於最上層/最下層"},{"categories":["914 - Wordpress"],"content":"前言 不知道每一位撰寫 wordpress 的朋友有時候會不會碰到一種狀況。\n當一張圖片上傳後，結果後來可能發現\n圖片中「打錯字」 發現圖片「漏打一些東西」 發現圖片「有內容想更新」 這時候就會碰到一個麻煩，我們如果上傳新的圖片，是不是原本舊的、用好的連結\n「全部都要重用了！！！」 Q___Q 大崩潰！！！\n這邊要提供一個可以無痛換圖的插件推薦，\n非常的實用也非常的無痛，個人也非常經常使用。\n插件 - Enable Media Replace 可以直接去插件市場裡面找到這個插件。\n使用方式 當我們有想要更換圖片的時候，打開媒體庫的地方，\n選擇要更換的圖片，發現會多了新的選項。\n我們可以看到「上傳新檔案」的這個新選項 注意事項 這邊我們可以很直覺的看到，我們新舊圖片的差別。\n更換檔案 \u0026amp; 更新連結 (左下) 這邊要注意，如果單純只想做圖片的替換、更新 只需要選「僅更換檔案」即可。 如果選下面的「更換檔案，同時使用新的檔案名稱並更新全部連結」 要承擔的風險簡單來說就是，如果「有網址是直接指定這張圖片的，會全部失效。」 日期選項 這邊影響比較還好，就看看要不要順便把圖片最新日期也更新一下，\n這就比較看個人，「喜歡留下舊版圖片的時間」或是「喜歡最新版圖片的時間」。\nReference 【WordPress】Bug \u0026amp; 問題解決: wordpress 引用文章/文章連結 時 縮圖壞掉/破圖/沒有顯示縮圖 ，分享文章到 facebook 時 縮圖壞掉/破圖/沒有顯示縮圖 (附例圖) ","date":"2021-10-04T17:34:51+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/%E6%88%AA%E5%9C%96-2021-10-04-%E4%B8%8B%E5%8D%885.28.29.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/wordpress-change-picture/","tags":["WordPress"],"title":"【Wordpress #6】換圖插件推薦，wordpress 更換圖片、更新圖片的好用工具！(wordpress change picture)"},{"categories":["340 - Linux 系統控制"],"content":"前言 我們可以運用 flock 幫助我們管理任務的流程。\n在執行多腳本的時候，這個功能非常的方便。\nflock flock -s /tmp/lock1.lock sleep 3 flock -s /tmp/lock1.lock echo \u0026#34;task 2\u0026#34; flock -x /tmp/lock1.lock echo \u0026#34;task 3\u0026#34; 說明 指定說明 -s : 代表此檔案為 shared lock，可以同時被多程式執行。所以擁有此 lock 的程式都可以馬上執行 -x：代表指定的檔案為 exclusive lock，只能同時被一個程式執行。 程式碼說明 上面的程式碼中，表示我們透過 /tmp/lock1.lock 這個檔案來做為我們任務的管理器\n(其中不論是路徑、檔名、副檔名我們都可以任意更改，只需指定一個檔案即可。)\nflock -s /tmp/lock1.lock sleep 3 : 執行第一個任務 sleep 3 此指令擁有 lock1.lock flock -s /tmp/lock1.lock echo \"task 2\" : 執行第二個任務 echo \"task 2\" 此指令也擁有 lock1.lock，由於是 shared lock (-s)，所以可以直接執行 flock -x /tmp/lock1.lock echo \"task 3\": 執行第三個任務 echo \"task 3\" 此指令也擁有 lock1.lock，由於是 exclusive lock (-x)，所以一次只能被執行一支 (需等待前一支程式執行完才能執行) 結果 我們執行上述的腳本後，發現結果為：\nsleep 3、echo task2 同時執行 等待 sleep 的 3 秒後，echo task3 才被執行。 也就是說，我們可以完全確保什麼指令可以先被執行，\n什麼指令需要等到前一個任務的 (lock) 被釋放後，才能夠被執行。\nReference Linux 小撇步：利用flock來做同步和非同步應用 ","date":"2021-10-04T16:55:27+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-ubuntu-flock/","tags":["Bash","Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #7】linux ubuntu 使用 flock 建立程式執行時的順序控制, 固定系統執行的 process 數量 (確保程式執行順序)"},{"categories":["922 - Mac / MacOS"],"content":"前言 htop 在 linux 是一個很好用的效能檢測工具\n我們來看看 mac 上如何也安裝這個好東西。\n安裝 brew install htop 延伸閱讀：MacOS 使用 htop 查看CPU、記憶體使用量\n結果範例 (有些應用因為安全關係，下面的截圖就不截了，實際上不只這樣。)\n可以看出我的 mac 一共有 8核心，\n8G 的記憶體與 2G 的 swap 記憶體，隨時間變化監控中。\nReference MacOS 使用 htop 查看CPU、記憶體使用量 ","date":"2021-10-04T11:06:44+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/%E6%88%AA%E5%9C%96-2021-10-10-%E4%B8%8B%E5%8D%882.36.28-1024x112.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/mac-htop/","tags":["macOS"],"title":"【Mac】mac 上使用 htop，mac 上安裝 htop 流程"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 這一篇我們會拿現有的 day 17 成品來改，\n我們這篇主要要學習的是滑鼠的監聽事件完全掌握。\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day21_mouse_control\nUI 部分 這篇文章的 UI 我們直接沿用 Day 17 的內容，\n所以這部分沒什麼好多說的，也不是我們這篇文的重點。\nPyQt 滑鼠的完全控制總整理 我們先來看看官方文件 關於滑鼠可以用的函數們 官方文件連結：QLabel Class 我們是監聽 Qlabel (圖片)裡面的事件，所以我們查詢的文件位於 QLabel 底下。\n我們可以看到，一共可以監聽的滑鼠事件有：\nmousePressEvent：按下滑鼠時的反應 mouseReleaseEvent：鬆開滑鼠時的反應 mouseMoveEvent：移動滑鼠時的反應 而這些動作都會觸發一個 \u0026ldquo;QMouseEvent\u0026rdquo;，我們再來去看看 QMouseEvent 有什麼\nQMouseEvent 能使用的函數 官方文件連結：QMouseEvent Class 看起來 PyQt 有完整的能夠幫我們紀錄滑鼠各種事件細節的功能，這篇我們就來使用看看\n測試上述功能 我們修改 day17 的 img_controller.py，修改\u0026amp;新增以下內容：\n為了方便比較，我們先暫持移除 set_clicked_position 的功能。 self.ui.label_img.mousePressEvent = self.show_mouse_press # set_clicked_position self.ui.label_img.mouseReleaseEvent = self.show_mouse_release self.ui.label_img.mouseMoveEvent = self.show_mouse_move 並新增下面對應的功能 function\ndef show_mouse_press(self, event): print(\u0026#34;[show_mouse_press]\u0026#34;, event) def show_mouse_release(self, event): print(\u0026#34;[show_mouse_release]\u0026#34;,event) def show_mouse_move(self, event): print(\u0026#34;[show_mouse_move]\u0026#34;,event) 結果 我們可以發現，不論是按下去、(按下狀態時)移動、放開滑鼠，\n我們的事件都有以 QMouseEvent 的形式被記錄下來。\n使用 QMouseEvent 的提供的方法 再來我們觀察 QMouseEvent 內部提供的方法，\n這邊因為功能很多，我就只先試未來我很有可能會用到的。\n特別著重在：我要知道「按下去位置」、「移動位置」、「鬆開位置」，\n並知道「左鍵還是右鍵」。\n找出滑鼠移動的各種位置 我們依照文件修改一下剛剛的 function\ndef show_mouse_press(self, event): print(f\u0026#34;[show_mouse_press] {event.x()=}, {event.y()=}, {event.button()=}\u0026#34;) def show_mouse_release(self, event): print(f\u0026#34;[show_mouse_release] {event.x()=}, {event.y()=}, {event.button()=}\u0026#34;) def show_mouse_move(self, event): print(f\u0026#34;[show_mouse_move] {event.x()=}, {event.y()=}, {event.button()=}\u0026#34;) 結果 這是一段我測試時的操作\n我們可以整理出一個結果，\n滑鼠的 x, y 關於滑鼠移動的 x, y，我們可以透過上述的方式直接取得，\n中間 x, y，代表著滑鼠移動的軌跡。\n滑鼠的按鍵 button() event.button() = 0：沒有按鍵輸入\nevent.button() = 1：滑鼠左鍵\nevent.button() = 2：滑鼠右鍵\n到此，我們掌握了大部分我在滑鼠會使用的功能，\n之後我們會透過這些函數加以運用。\nReference 官方文件連結：QLabel Class 官方文件連結：QMouseEvent Class ","date":"2021-10-04T02:05:39+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/pyqt5-21-4.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-21/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 21 - 透過 PyQt 實現滑鼠監聽總整理，完全掌握滑鼠控制 (listen mouse)"},{"categories":["381 - Bash 基本語法"],"content":"for numeric (次數 for-loop) #!/bin/bash # numeric for i in {1..10} do echo $i done for arr (arr for-loop) #!/bin/bash # array elements arr=(\u0026#34;test1\u0026#34; \u0026#34;test2\u0026#34; \u0026#34;test3\u0026#34;) for elements in ${arr[@]}; do echo $elements done 或是\n#!/bin/bash # array elements arr=(\u0026#34;test1\u0026#34; \u0026#34;test2\u0026#34; \u0026#34;test3\u0026#34;) for elements in ${arr[*]}; do echo $elements done 備註：「@」, 「*」的差別 基本上這兩個大同小異，只差在有沒有被 Quote 「\u0026quot;\u0026quot;」才會出現差別。\n範例 #!/bin/bash # 定義一個包含空格的元素陣列 arr=(\u0026#34;apple banana\u0026#34; \u0026#34;cat dog\u0026#34; \u0026#34;egg fox\u0026#34;) # 使用未引用的 $arr[*] 和 $arr[@], 展開結果相同 echo \u0026#34;Unquoted *: $arr[*]\u0026#34; echo \u0026#34;Unquoted @: $arr[@]\u0026#34; # 使用引用的 \u0026#34;${arr[*]}\u0026#34; 和 \u0026#34;${arr[@]}\u0026#34;, 展開結果相同 echo \u0026#34;Quoted *: ${arr[*]}\u0026#34; echo \u0026#34;Quoted @: ${arr[@]}\u0026#34; # 使用引用的 \u0026#34;${arr[*]}\u0026#34; 和 \u0026#34;${arr[@]}\u0026#34; 在迴圈中, 結果不同 echo \u0026#34;Loop with quoted *:\u0026#34; for i in \u0026#34;${arr[*]}\u0026#34;; do echo \u0026#34;$i\u0026#34; done echo \u0026#34;Loop with quoted @:\u0026#34; for i in \u0026#34;${arr[@]}\u0026#34;; do echo \u0026#34;$i\u0026#34; done 結果 這邊我們可以注意換行的方式，如果使用了 「${arr[*]}」搭配 Quote ，\n那會變成一串字 (被組成了單一個結果)，因此如果使用 for 會跑不出我們要的結果。\n我自己的記法 為了怕混淆，我就記要使用 for loop 時，沒事不要 Quote，\n另外使用「@」，可以避免沒有展開的結果 (但這些其實有搞清楚就沒事了XD)\nReference Bash for https://www.cyberciti.biz/faq/bash-for-loop/ ","date":"2021-10-02T09:55:04+08:00","image":"https://wongwongnotes.com/images/restored/2023/11/%E6%88%AA%E5%9C%96-2023-12-08-%E4%B8%8B%E5%8D%884.49.32.png","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/bash-for-loop/","tags":["Bash","基本語法"],"title":"【Bash 基本語法 #4】bash for loop 實作範例程式碼 (備註：「@」, 「*」的差別)"},{"categories":["820 - Cloud"],"content":"前言 在雲端服務設計的過程中，\n我們會碰到使用 stateful / stateless 的差別\nstateful：狀態會被保存，也就代表這是持續的任務，不會短時間結束。 stateless：(無狀態) 狀態不會被保存，也就代表每一次都是一個獨立任務，結束就結束。 可以先思考一個經典的排隊問題： 旁邊櫃檯沒人排隊(比喻：此主機忙碌、別的主機空閒)，你可以去隔壁櫃臺要求提供服務嗎？\n想想在郵局的情況、再想想在大賣場的情形\nglobal queue 才有機會將這個櫃台的人分配給其他櫃員處理，\n否則，他只能夠排隊在該櫃台、一直等待服務。\n以上面的排隊問題，換到真實的情境： 開了兩台主機 一台CPU爆強、一台GPU爆強\n結果在拿task的時候，一個狂吃CPU的 task，使用了 GPU爆強的機器\n這樣是不是浪費了GPU爆強的機器使用成本?\n說明 \u0026amp; 比喻 有一個很妙的比喻拿來形容 global / seperate queue 的差別非常合適：\nseperate queue 使用情境：不想要堵塞，客戶為尊，客戶到馬上就處理，一般來說會使用許多的 service，讓服務馬上到馬上進行 就像「全聯排隊」，一個收銀台排隊一排，能不能先結帳要看運氣，\n有時運氣不好，前面排隊的結帳很久，後來才來結帳的人反而先完成。\n- seperate queue, multiqueue, multi service global queue 使用情境：可能會有堵塞的情況，而為了求公平，讓先到的客戶先處理，而服務成本一般較高(像主機、櫃員)，讓想使用的客戶自行排隊等待服務時間。 就像「郵局排隊」，先到的客戶先處理，一個接著一個\nglobal queue, single queue, multi service 一般情況使用 一般情況下 global queue 優於 seperate queue，\n但設計上 seperate queue 相對 global queue 容易設計，\n不用考慮太多整體的事情。\n程式設計 程式設計：seperate queue 設計可以較隨意 (每一個櫃位要擺哪，要怎麼排隊隨便啦)，\nglobal queue 的設計須以 global queue 為主軸，讓每一個系統能夠去提取 task 來做 (排隊動線為尊，櫃檯配合排隊動線設計)。\nReference [ASP.NET]Stateful與Stateless ","date":"2021-10-01T18:11:24+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/cloud-platforms/cloud/cloud-stateful-stateless/","tags":["Cloud"],"title":"【Cloud】stateful / stateless, global queue/ seperate queue 差別 \u0026 使用"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 與昨天一樣，不過我們今天要談一個 PyQt 中非常重要的 QThread 概念 !\n前言 今天要談一個 PyQt 中非常重要的 QThread 概念!\n我們要修改昨天的 QProgressBar 功能，將它潛在問題修改並解決他。\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day20_qthread\n重要的 QThread 概念 我們今天要來先討論一下在 PyQt 中非常重要的東西!\n這個就叫做 「QThread」 !\n為什麼「QThread」很重要，其實在昨天的 QTimer 中我們就已經有說過，\n我們的程式一定不會是一直線運行的，程式執行的過程中，\n勢必要有些「背景處理」的事情!\n例如：時間就應該要背景更新，而不是我們主程式隨時切換去給時間+1\n這樣光用想的就知道，萬一上一行程式執行慢了一點，時間就會開始有越來越大的誤差了吧!\n為什麼我們要挑在這裡先講? 因為 ProgressBar 正好就符合這樣的需求，\n我們可以試著想想，如果一個 ProgressBar 正在跑，我們就不能同時做其他事情，\n這樣子的程式，不能說不好 (畢竟還是能動，只是要等他結束才能做其他事XD)\n但不覺得很不符合使用者需求嗎XD\n觀察 - 居然程式會「沒有回應」，還不能關閉?! 我們把運行到一半的程式，強制用右上角的「X」關閉看看，\n我們發現居然程式「不但關不掉」，甚至還「沒有回應」了?!\n這就是沒有做 QThread 搞得鬼，因為在 Windows 判斷一個視窗「沒有回應」的判斷條件中，\n當我們對視窗「進行某個行為(例如：按按鈕、關閉視窗)」，卻沒有得到回應，\n沒有回應的原因就是因為他被「卡在單一任務的過程中，無法給予回應」\nwindows 就會判定這個程式是「沒有回應」的\n(其實不只 windows, ubuntu, mac 在這個邏輯底下都是)\n所以，我們必須把這個任務放入 QThread 執行，使得我們的主線任務可以被空出來，\n能應付並接收新的其他任務。\n將 ProgressBar 修改為 QThread 的版本 (修改昨天的 controller.py) 主要可以分為兩個任務：\n定義 Thread 任務內容：class ThreadTask(QThread)，名稱可改 從主程式去呼叫 thread 執行任務：我們使用 pyqtSignal，來傳送訊號，協助我們變更值 宣告部分 注意「QThread, pyqtSignal」被宣告在「PyQt5.QtCore」裡面\nfrom PyQt5 import QtCore, QtWidgets from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QFileDialog from PyQt5.QtCore import QThread, pyqtSignal import time from UI import Ui_MainWindow Thread 任務宣告 宣告 pyqtSignal 我們在 ThreadTask 裡面宣告一個 global 的 pyqtSignal，\n並指定給 qthread_signal，(需要宣告類別)\n結果就像 「qthread_signal = pyqtSignal(int)」\n送出訊號 emit 我們把訊號送回去給主程式，我們透過 emit 這個 function，\n可以協助我們把值送回主程式，並不影響主程式的任務。\nclass ThreadTask(QThread): qthread_signal = pyqtSignal(int) def start_progress(self): max_value = 100 for i in range(max_value): time.sleep(0.1) self.qthread_signal.emit(i+1) 主要控制的部分 我們把按鈕連結 ButtonClick() 這個 function，\n我們在 ButtonClick() 這個任務當中，宣告我們的一份新的 ThreadTask 任務，\n並把訊號連接至一個 function，\nclass MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): self.ui.progressBar.setMaximum(100) self.ui.pushButton.clicked.connect(self.ButtonClick) def ButtonClick(self): self.qthread = ThreadTask() self.qthread.qthread_signal.connect(self.progress_changed) self.qthread.start_progress() def progress_changed(self, value): self.ui.progressBar.setValue(value) 連結 pyqtSignal 與 某一個 function() 我們需要把值的變化傳至一個 function 當中，\n我們利用「self.qthread.qthread_signal.connect(self.progress_changed) 」\n連結「訊號 (qthread_signal)」與 「function - progress_changed()」的關係\n而這個 function 會需要保留 value 作為一個欄位，\n實際上在連接時，並不是以常見的形式傳入這個值\n(正確來說，應該是前面的 qthread_signal 被作為 value 傳入)\ndef progress_changed(self, value): self.ui.progressBar.setValue(value) 我們仔細看，value 就是代表 qthread_signal，\n所以我們可以直接從 setValue 去改他。\n啟動 thread 用的 start_progress() 這部分就沒什麼好說的，我們會需要一個 function 幫助我們啟動 Thread。\n並用 emit 把訊號送回來。\n","date":"2021-10-01T02:58:40+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/Qthread.drawio.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-20/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 20 - PyQt 最重要的 QThread 概念 / 為什麼 windows, mac, ubuntu (linux) 程式會「沒有回應」?"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 這篇我們要來學一個新的東西 QProgressBar!\nQProgressBar 就是一個能顯示出進度條效果的酷功能哦!\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day19_progress_bar\nUI 設計部份 (UI.py) 我們從 Display Widgets 中選取 Progress Bar，\n從預覽中，我們就已經可以感覺到進度條的效果囉!\n另外我們拉一個按鈕，作為「開始鍵」。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day19.ui -\u0026gt; UI.py pyuic5 -x day19.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py controller 設計部份 (controller.py) 單純控制 QProgressBar 很簡單，我們只需要設定\n上限值：setMaximum 增加值：setValue 注意都是 「整數 int」 的變化, 更改值請以更改上限為主，讓他自己去換算。\n使用 「小數」 會跳錯!!!\ndef setup_control(self): self.ui.pushButton.clicked.connect(self.start_progress) def start_progress(self): max_value = 100 self.ui.progressBar.setMaximum(max_value) for i in range(max_value): time.sleep(0.1) self.ui.progressBar.setValue(i+1) 設定主程式 setup_control() self.ui.pushButton.clicked.connect(self.start_progress)：設定按鍵功能，與 start_progress() 這個 function 串接 按鈕啟動的程式 start_progress() 設定上限值為 100 max_value = 100 self.ui.progressBar.setMaximum(max_value) 設定每次增加 1 for i in range(max_value): time.sleep(0.1)：休息 0.1 秒 self.ui.progressBar.setValue(i+1) 每次值增加 1 執行結果 照我們 day5 的程式架構，我們執行\npython start.py 按下 GO 之後，進度條就順利執行囉!\n","date":"2021-10-01T02:40:24+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-5-1.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-19/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 19 - 使用 QProgressBar，製作進度條的功能"},{"categories":["381 - Bash 基本語法"],"content":"前言 bash if condition\n範例 這邊以外部傳入參數 $1，作為我們 If 要判斷的內容\n#!/bin/bash if [[ $1 == \u0026#34;123\u0026#34; ]]; then echo \u0026#34;hello\u0026#34; elif [[ $1 == \u0026#34;456\u0026#34; ]]; then echo \u0026#34;world\u0026#34; else echo \u0026#34;not found\u0026#34; fi 結果 reference https://linuxhint.com/bash_if_else_examples/ 比較一個括弧跟兩個括弧的差別 (Single and Double Brackets) https://www.baeldung.com/linux/bash-single-vs-double-brackets ","date":"2021-10-01T01:22:00+08:00","image":"https://wongwongnotes.com/images/restored/2023/11/%E6%88%AA%E5%9C%96-2023-11-05-%E4%B8%8A%E5%8D%881.17.53.png","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/bash-if/","tags":["Bash","基本語法"],"title":"【Bash 基本語法 #3】bash if 實作範例程式碼"},{"categories":["850 - Google Sheet / Excel"],"content":"前言 自動顯示假日的公式、自動顯示今日日期的公式\n作法 自動更改日期 (單格) 格式 -\u0026gt; 條件式格式設定\n規則 -\u0026gt; 日期為 今天 自動更改日期 (一整行) 格式 -\u0026gt; 條件式格式設定\n自訂公式 -\u0026gt; =(A$1=TODAY()) 註：「＄」表示固定欄位，以上面 A＄1 的意思來說，\n就是去判斷每一直行 (A會自動改變，＄1代表的1永遠不變)\n我們去確認這個「每一行的 “第一個值” 是否等於 “今天”」\n自動顯示假日 格式 -\u0026gt; 條件式格式設定\n自訂公式 -\u0026gt; =OR((WEEKDAY(A$1)=6),(WEEKDAY(A$1)=7)) 註2：上面已經解釋了 A＄1的意思\n然後因為他剛好是日期格式，所以我們可以再用 WEEKDAY() 去判斷他今天是星期幾，\n「1-7」分別是「星期一-星期日」，這裡並不像 C++ 一樣\n用「==」作為判斷是否相同，而是用「=」\n如果有像我一樣已經很習慣 c++ 判斷式的可以特別注意這點公式上的不同\n範本 可以直接複製副本去研究看看\n可參考：weekday color\n","date":"2021-09-30T18:05:04+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/productivity/google-sheet-excel/google-sheet-auto-weekend/","tags":["Bash","Linux","Ubuntu"],"title":"【Google Sheet / Excel #1】自動化公式 / 自動更改日期、自動顯示假日"},{"categories":["399 – Bash 自用腳本"],"content":"前言 alias 就是 縮寫的意思，\n透過設定好的縮寫，可以大幅加快我們使用 terminal 開發的效率\n編輯 bashrc, zshrc, config.fish (依照個人使用的 shell 而定) vim ~/.bashrc vim ~/.zshrc vim ~/.config/fish/config.fish 個人自用 alias (通用) # -------------------- personal setting -------------------- # alias ls=\u0026#39;ls --color=auto\u0026#39; alias ll=\u0026#39;ls -al\u0026#39; alias grep=\u0026#39;grep --color=auto\u0026#39; alias f=\u0026#34;find ~+ . -name\u0026#34; alias ..=\u0026#39;cd ..\u0026#39; 個人自用 alias (ubuntu) # -------------------- ubuntu setting -------------------- # alias apt-get=\u0026#39;sudo apt-get -y\u0026#39; alias open=\u0026#39;xdg-open\u0026#39; # ubunut open folder 預設自動啟動 anaconda 適用於修改 zshrc, bashrc\n# \u0026gt;\u0026gt;\u0026gt; conda initialize \u0026gt;\u0026gt;\u0026gt; # !! Contents within this block are managed by \u0026#39;conda init\u0026#39; !! __conda_setup=\u0026#34;$(\u0026#39;/Users/howardweng/opt/anaconda3/bin/conda\u0026#39; \u0026#39;shell.bash\u0026#39; \u0026#39;hook\u0026#39; 2\u0026gt; /dev/null)\u0026#34; if [ $? -eq 0 ]; then eval \u0026#34;$__conda_setup\u0026#34; else if [ -f \u0026#34;/Users/howardweng/opt/anaconda3/etc/profile.d/conda.sh\u0026#34; ]; then . \u0026#34;/Users/howardweng/opt/anaconda3/etc/profile.d/conda.sh\u0026#34; else export PATH=\u0026#34;/Users/howardweng/opt/anaconda3/bin:$PATH\u0026#34; fi fi unset __conda_setup # \u0026lt;\u0026lt;\u0026lt; conda initialize \u0026lt;\u0026lt;\u0026lt; ","date":"2021-09-30T17:49:21+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/bash/linux-my-alias/","tags":["Bash","Docker","Linux","Ubuntu","自用腳本"],"title":"【Bash】個人自用 alias"},{"categories":["381 - Bash 基本語法"],"content":"前言 撰寫 bash 的 程式碼可以幫助我們自動化完成一連串的指令，\n(或者我們也會說這是一種「腳本」，會自動完成一些事情)\n以下的功能，建立在 「chmod +x」 的條件下才有用\n如果沒有下「chmod +x」，使變成 .sh 執行檔\n則會有以下兩種情況： ./test.sh：會以預設的 shell 執行，例如 sh (dash)、或有安裝 zsh, fish 的話\n(可能會有語言不通無法執行的問題，即使有加 #!/bin/bash)\nbash ./test.sh：指定 bash 的語言，來解釋執行腳本\nzsh ./test.sh：指定 zsh 的語言，來解釋執行腳本，其他以此類推\n作為宣告解釋這份腳本的語言使用 有時候我們看到任何一份 \u0026ldquo;.sh\u0026rdquo; 開頭的檔案，\n第一行都會寫\n#!/bin/bash 這其實就是在宣告，這份腳本的執行，\n請用 bash 的語言來解釋他，\nsh 系列的語言有很多種，雖然大同小異，\n但實際上功能依然有些微的差別，\n例如有些 bash 有的指令，換成 zsh 可能就有不同的寫法。\n影響 如果我們沒有加入這行指定使用的 shell script，\n大部分情況可能都沒有問題，\n(那是因為語言共通性高的緣故)，\n但碰到一些語言不共通的時候，例如 sh, bash\nsh 其實是 dash 的縮寫，讀者有興趣的話可以看下方的說明。\n我們可以透過以下指令來看現在電腦中有哪些可使用的 shell script cat /etc/shells 確認現在 terminal 使用的 shell script echo $SHELL 科普類 sh 其實是某一個語言的簡稱 我們可以透過以下指令來看，現在是用什麼來解釋 sh 這個語言\nls -l /bin/sh 我們可能得到結果是：\nlrwxrwxrwx 1 root root 4 八 13 2020 /bin/sh -\u0026gt; dash 表示 sh 其實是 dash 的 soft link (捷徑)，並不等同於 bash\n這也是為什麼有時候 「sh ./test.sh」、 「bash ./test.sh」會有一個可執行，\n另外一個不可執行的原因\n延伸閱讀：[Linux] sh 和 bash 之間的差異\nReference shell 腳本第一行#!/bin/bash你真的清楚嗎？ ","date":"2021-09-30T17:24:36+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/bin-bash-bin-sh/","tags":["Bash","Linux","Ubuntu","基本語法"],"title":"【Bash 基本語法 #2】什麼是 #!/bin/bash, #!/bin/sh，為什麼要加在 script 前面 (問題解決：sh, bash 的不同)"},{"categories":["111 - Python 基礎語法","380 - Bash 自動化程式撰寫","211 - C++ 基礎語法"],"content":"前言 timestamp 是我們在紀錄 log 時重要的依據，\n通常我們會使用從「1970年1月1日」開始以秒為單位計算，\n不斷1秒 +1 的這個值作為 timestamp\n目前總共累積至 10位數 (精準度到秒)\n無聊想更了解 timestamp 換算，可以玩玩這個網站 延伸閱讀：專題文章：Unix timestamp 時間戳線上轉換工具\n1000000000 = 2001年09月09日09點46分40秒 2000000000 = 2033年05月18日11點33分20秒 而紀錄時，通常會使用 13位 (也就是精準度到 ms)，\n因為程式運算很快，有時候一秒就能處理非常多的事情，\n(相對來說，很多程式處理一行都是 ms 等級的時間。)\n記錄到 ms，我們更能精確的抓準可能是在程式中的哪一的部分出錯。\npython 取得 timestamp import time print(time.time()) # 10位，s from datetime import datetime ts = datetime.now().timestamp() print(ts) 注意 如果使用 datetime.utcnow().timestamp() 會是「當前時區」的 timestamp，\n以台灣來說就是 UTC+8 了\n延伸閱讀： [Python] 你知道 datetime.utcnow().timestamp() 不是 Unix Timestamp 嗎？\n延伸閱讀： Python 使用 timestamp 的注意事項\nC++ 取得 timestamp C++ 17 或以前 #include \u0026lt;ctime\u0026gt; #include \u0026lt;iostream\u0026gt; int main() { std::time_t result = std::time(nullptr); // 10位，s std::cout \u0026lt;\u0026lt; std::asctime(std::localtime(\u0026amp;result)) \u0026lt;\u0026lt; result \u0026lt;\u0026lt; \u0026#34; seconds since the Epoch \u0026#34;; } C++ 20 在 chrono 這個 library 提供了 time_since_epoch 這個新的函式可以使用，\n不過好像大部分公司的系統都沒更到那麼新\u0026hellip;\n上面的方法應該還是比較好用的。\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;chrono\u0026gt; int main() { const auto p1 = std::chrono::system_clock::now(); // 10位，s std::cout \u0026lt;\u0026lt; \u0026#34;seconds since epoch: \u0026#34; \u0026lt;\u0026lt; std::chrono::duration_cast\u0026lt;std::chrono::seconds\u0026gt;( p1.time_since_epoch()).count() \u0026lt;\u0026lt; \u0026#39; \u0026#39;; // 13位，ms auto millisec_since_epoch = duration_cast\u0026lt;milliseconds\u0026gt;(system_clock::now().time_since_epoch()).count(); cout \u0026lt;\u0026lt; \u0026#34;milliseconds since epoch: \u0026#34; \u0026lt;\u0026lt; millisec_since_epoch \u0026lt;\u0026lt; endl; } C++ 其他方法 - 1 #include \u0026lt;sys/time.h\u0026gt; struct timeval tp; gettimeofday(\u0026amp;tp, NULL); long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000; // 13位, ms time_t s = tp.tv_sec * 1000; // 10位, s C++ 其他方法 - 2 int64_t current_timestamp; struct timespec time; clock_gettime(CLOCK_REALTIME, \u0026amp;time); current_timestamp = ((int64_t)time.tv_sec)*1000 + time.tv_nsec/1000000; cout \u0026lt;\u0026lt; \u0026#34;current_timestamp = \u0026#34; \u0026lt;\u0026lt; current_timestamp \u0026lt;\u0026lt; endl; 變形：自製 function timeMs (呼應 time(NULL) 的實作方法 ) int64_t timeMs(int64_t *t) { struct timespec time; clock_gettime(CLOCK_REALTIME, \u0026amp;time); return ((int64_t)time.tv_sec)*1000 + time.tv_nsec/1000000; } Linux/Bash 取得 timestamp string timestamp=$(date +%s) 取得 s 的方法 (10位) date +\u0026#34;%s\u0026#34; # 10位, s 取得 ms 的方法 (19 digits (nanoseconds)) date +\u0026#34;%s.%N\u0026#34; # 19 digits (nanoseconds) 取整 echo \u0026#39;(\u0026#39;`date +\u0026#34;%s.%N\u0026#34;` \u0026#39; * 1000000)/1\u0026#39; | bc # 取整 function，取得現在的時間 timestamp() { date +\u0026#34;%T\u0026#34; # current time } 結合 watch 使用，用 terminal 來顯示 timestamp (自動更新) 10 digits watch -n .1 date +\u0026#34;%s\u0026#34; 19 digits (nanoseconds) watch -n .1 date +\u0026#34;%s.%N\u0026#34; Reference How To Get Current Timestamp In Python Get Unix timestamp with C++ How to get current timestamp in milliseconds since 1970 just the way Java gets 如何在 C++ 中以毫秒為單位獲取時間 Timestamp To Date Converter ","date":"2021-09-30T03:29:18+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-c-bash-timestamp/","tags":["Bash","C++","Python","基礎語法","自動化程式撰寫"],"title":"【Python/C++/Linux/Bash】 timestamp 取得方式總整理"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 這篇我們要來學一個新的東西 QTimer!\nQTimer 是獨立於程式運作的計時器，\n你可能會想什麼時候會用到?\n其實很意外的，真正的程式反而非常經常會需要這個，功能來作為「自動更新」，\n我們仔細想想，一般的程式一行接一行執行下去，但萬一有個背景任務，\n是隨著時間要定期更新的，我們主程式的流程哪有「突然切換至時間功能，並更新」，這樣的設計\n(如果是 step by step 的設計程式，這樣也太難。)\n因此我們有 QTimer 的設計，來幫助我們定期更新一些東西，或做一些其他事情。\n之後也會介紹 QThread，也同樣的是讓主程式在執行的同時，能夠同時有其他的支線任務可以獨立運行!\n之前內容的重點複習 (前情提要) 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day18_qtimer\nUI 設計部份 (UI.py) 這次我們的重點不是放在 UI 設計，我們簡單拉一個作為結果顯示用的 Qlabel 即可。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day18.ui -\u0026gt; UI.py pyuic5 -x day18.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py controller 設計部份 (controller.py) QTimer 的使用方式很簡單，主要我們需要設定一個 timeout 時間，\n每經過一次 timeout，我們的程式就會做一次指定的事情\nfrom PyQt5 import QtCore from PyQt5.QtWidgets import QMainWindow, QFileDialog from PyQt5.QtCore import QTimer import time import os from UI import Ui_MainWindow class MainWindow_controller(QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): self.timer=QTimer() # init QTimer self.timer.timeout.connect(self.run) # when timeout, do run one self.timer.start(1) # start Timer, here we set \u0026#39;1ms\u0026#39; while timeout one time self.time_counter = 0 # init time counter # for testing: 3599000 def run(self): self.ui.label.setText(str(self.set_time_counter_format(self.time_counter))) # show time_counter (by format) self.time_counter += 1 # time_counter + 1 def set_time_counter_format(self, time_counter): ms = time_counter % 1000 total_sec = max(0, (time_counter - ms)//1000) hour = max(0, total_sec//3600) minute = max(0, total_sec//60 - hour * 60) sec = max(0, (total_sec - (hour * 3600) - (minute * 60))) return f\u0026#34;{hour}:{minute:0\u0026gt;2}:{sec:0\u0026gt;2}.{ms:0\u0026gt;3}\u0026#34; 我們先來看一下，最主要的設定 QTimer 部分\n設定主程式 setup_control() self.timer=QTimer()：初始化 QTimer self.timer.timeout.connect(self.run)：當 QTimer 每經過一次 timeout，執行 run 的動作 self.timer.start(1)：開始 QTimer，這裡的\u0026quot;1\u0026quot; 代表的是 1ms，所以我們每 1ms 就會 timeout 一次 self.time_counter = 0：我們初始化計數器，從 0 開始計數。 設定 run()，每 timeout 就執行一次 self.ui.label.setText(str(self.set_time_counter_format(self.time_counter))))：設定格式並顯示時間 self.time_counter += 1：計數器 +1 (因為 timeout 一次) 設定時間格式 set_time_counter_format() 這邊就是我自己計算的公式了，有幾個計算上的重點 max(0, x)：避免計算結果 \u0026lt; 0 導致顯示負數，我們直接給予 0 這個值 f\u0026quot;{hour}:{minute:0\u0026gt;2}:{sec:0\u0026gt;2}.{ms:0\u0026gt;3}\u0026quot;： f-string 的 格式設定，我們抓住「:」冒號後的就是格式設定內容，「0」表示補0，「\u0026gt;」表示靠右，「2(3)」表示總共2(3)位。 執行結果 照我們 day5 的程式架構，我們執行\npython start.py 於是我們就這樣完成了我們的碼表 (stopwatch)。\n此外，我們可以透過更改 counter 的初始值來修正程式錯誤\n例如依照我們上面的算式，我們可以設定初始值為 \u0026ldquo;3599000\u0026rdquo;，我們可以同時驗證「小時」、「分鐘」、「秒」、能不能正確增加 (因為從約 0:59:50開始)\nReference f-string 格式設定 ","date":"2021-09-30T01:53:01+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/pyqt5-18-2.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-18/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 18 / Project 使用 QTimer，自製碼表(計時器) PyQt5 stopwatch DIY"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 碰到以下開頭的內容，問題的解決方式\nundefined reference to ... 可能的原因種類很多，以下舉例幾種我碰過且有解決的方式。\n範例1 /opt/work/core/Module.h:18: undefined reference to `vtable for Module\u0026#39; collect2: error: ld returned 1 exit status 解法1 去除多餘的 .o file (有可能過去存在的 .cpp，因為改版而消失)\n殘留的 .o 檔未清除乾淨導致的編譯錯誤。\n/opt/work/core/Module.h:18: undefined reference to `vtable for Module\u0026#39; collect2: error: ld returned 1 exit status 範例2 undefined reference to `boost::system::generic_category()\u0026#39; 解法2 library 問題，\n可以往 library 是否有被正確 include 的方向去查 (可能是 include 路徑不正確，或未 include)\n請在 makefile 或編譯當中添加路徑 -lboost_system\n整理\n-l： library 名稱\n-L：library 路徑\n類似情況 undefined reference to `Beanstalk::Client::~Client()\u0026#39; 請在 makefile 或編譯當中添加路徑 -lbeanstalk\n可參考官方網站：beanstalkd client Reference [C++中Boost使用出现错误undefined reference to boost::system::generic_category()'解决方法](https://blog.csdn.net/qq_31261509/article/details/79412700 \u0026quot;C++中Boost使用出现错误undefined reference toboost::system::generic_category()\u0026lsquo;解决方法\u0026quot;) 可參考官方網站：beanstalkd client ","date":"2021-09-29T19:01:51+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/c-undefined-reference-to/","tags":["C++"],"title":"【C++】問題解決：undefined reference to ..."},{"categories":["299 - C++ 問題解決"],"content":"問題描述 C++ 使用 rapidjson 導致出現以下 error 的解決辦法。\n/opt/work/lib/rapidjson/document.h:1065:29: error: no matching function for call to \u0026#39;rapidjson::GenericValue\u0026lt;rapidjson::UTF8\u0026lt;\u0026gt; \u0026gt;::GenericValue(std::__cxx11::basic_string\u0026lt;char\u0026gt;\u0026amp;)\u0026#39; GenericValue v(value); 解法 此為資料型態錯誤問題，\n有可能是例如 int 使用 string 的方式寫入（讀取）。\n以我自己的經驗而言，\n我嘗試寫 string 進入 rapidjson 當中，\n但是我應該要先從 string 轉成 c_string，\n也就是說\nstring teststr = \u0026#34;test\u0026#34;; result.AddMember(\u0026#34;testkey\u0026#34;, Value().SetString(teststr.c_str(), allocator), allocator); 注意 SetString(teststr.c_str(), allocator) 的部份，\n必須要將 string 轉換成 c_string\n我之前沒有做這個轉換導致了這個 error。\n","date":"2021-09-29T16:25:13+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/c-error-no-matching-function-for-call-to-rapidjson/","tags":["C++"],"title":"【C++】問題解決：'error: no matching function for call to 'rapidjson::GenericValue\u003crapidjson::UTF8\u003c\u003e \u003e::GenericValue(std::__cxx11::basic_string\u003cchar\u003e\u0026)' GenericValue v(value);"},{"categories":["350 - Linux 終端機操作"],"content":"install fish #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/5] install fish\u0026#34; #sudo apt-add-repository ppa:fish-shell/release-3 # for ubuntu sudo apt-get install fish -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] install oh-my-fish\u0026#34; curl -L https://get.oh-my.fish | fish echo \u0026#34;[5/5] change default shell to zsh shell\u0026#34; chsh -s /usr/bin/fish pi # chsh -s /usr/bin/fish ubuntu #grep ubuntu /etc/passwd #check change shell in /etc/passwd 設定 fish 介面 因為 fish 本身就帶有自動完成、語法高亮、語法偵錯的功能。\n不需要像 zsh 還需要另外裝 plugins，\n有一點要小心，fish_config 之後會產生一份 「~/.config/fish/functions/fish_prompt.fish」的設定檔，\n會導致 omf theme 失效。\n可參考：\nChanged prompt in fish_config, now can\u0026rsquo;t use o-m-f themes Omf theme not changing echo \u0026#34;[optional] set fish shell in web\u0026#34; fish_config # bug will cause omf theme not work!!!! #rm ~/.config/fish/functions/fish_prompt.fish # if you want ot use omf theme again echo \u0026#34;[optional] change oh-my-fish theme\u0026#34; omf update omf install agnoster omf theme agnoster oh-my-fish 主題 可參考：Available themes\nReference 更多 fish 的優點：如何在 Linux 中安裝、配置和使用 Fish Shell？\nFish shell：讓指令更接近懶人使用 Oh My Fish! 讓你的 Shell 漂亮起來 | Linux 中國 Omf theme not changing Changed prompt in fish_config, now can\u0026rsquo;t use o-m-f themes Fish shell：讓指令更接近懶人使用 ","date":"2021-09-28T18:01:37+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/auto-script-fish/","tags":["Bash","Docker","Linux","Ubuntu","終端機操作"],"title":"【Linux 終端機操作 #8】自動化腳本 / 安裝 fish, oh-my-fish （問題解決：omf theme not working）"},{"categories":["399 – Bash 自用腳本"],"content":"install zsh, oh-my-zsh #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/5] install zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; sudo apt-get install zsh -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] install oh-my-zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; echo \u0026#34;[5/5] change default shell to zsh shell\u0026#34; chsh -s /bin/zsh #grep pi /etc/passwd #check change shell in /etc/passwd install powerlevel10k #!/bin/bash # ----- powerlevel10k theme ------ # echo \u0026#34;install powerlevel10k\u0026#34; git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k echo \u0026#39;source ~/powerlevel10k/powerlevel10k.zsh-theme\u0026#39; \u0026gt;\u0026gt;! ~/.zshrc echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc [optional] 修改設定檔 (需修改 ~/.zshrc, ~/.p10k.zsh 文件) echo \u0026#34;[optional] set ~/.zshrc (zsh settings)\u0026#34; vim ~/.zshrc echo \u0026#34;[optional] set ~/.p10k.zsh (powerlevel10k settings)\u0026#34; cp ~/.p10k.zsh ~/.p10k.zsh.bak # backup #p10k configure # GUI version vim ~/.p10k.zsh echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc (optional) 下載字體 (Nerd font, powerline font) 其他網站提供的下載的方式很多不能用了，另外如果將整包字體打包下載檔案也非常的巨大，因此參考其中一篇文章提供的方法，直接下載我們要的字體包。\n字體載點：https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/SourceCodePro.zip\n我是安裝裡面的以下字體：\nSauce Code Pro ExtraLight Nerd Font Complete.ttf Sauce Code Pro ExtraLight Nerd Font Complete Mono.ttf (optional) install zsh plugins zsh 好用的插件，我推薦以下兩個，自動完成與自動標示指令。\n因為插件的關係，下面的\u0026quot;$\u0026ldquo;符號會被吃掉，\n如果使用指令無法順利安裝，請參考官網的安裝指南：\nzsh-syntax-highlighting 官方文件 zsh-autosuggestions 官方文件 #!/bin/bash # ----- install zsh plugins ------ # echo \u0026#34;[optional] install Syntax Highlighting Plugin\u0026#34; # doc: https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.md git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting echo \u0026#34;[optional] install ZSH-AutoSuggestion Plugin\u0026#34; # doc: https://github.com/zsh-users/zsh-autosuggestions/blob/master/INSTALL.md git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions 設定 plugins (需修改 ~/.zshrc 文件) echo \u0026#34;[optional] set ~/.zshrc to use plugins\u0026#34; vim ~/.zshrc plugins=( git zsh-syntax-highlighting zsh-autosuggestions ) echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc Reference Day 19 - 好用工具篇 【Ubuntu 筆記】Install Oh-My-Zsh ","date":"2021-09-28T17:45:21+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/bash/auto-script-zsh/","tags":["Bash","Docker","Linux","Ubuntu","自用腳本"],"title":"【Bash】安裝 zsh, oh-my-zsh, powerlevel10k 個人自用 script"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 此為我測試 Raspberry Pi 3 / 樹莓派 照相機模組的筆記\n去設定開啟照相機功能 sudo raspi-config 範例程式碼 外接 hdmi 直接顯示於螢幕 如果有外接 hdmi，可以使用這組 (會直接將畫面呈現並覆蓋於螢幕上)\n開啟攝影機並顯示 10秒鐘 (使用 python 控制)\nfrom picamera import PiCamera from time import sleep camera = PiCamera() camera.start_preview() sleep(10) camera.stop_preview() 透過遠端桌面，搭配 OpenCV 顯示視窗來顯示畫面 上述方法是直接顯示畫面於螢幕上，但如果透過遠端桌面會看不到，\n因此我們先將圖片存下來 (tmp.jpg)，然後再使用 OpenCV 輸出畫面。\nfrom picamera import PiCamera from time import sleep import cv2 wait_time = 1 def close_cv2_window(): cv2.destroyAllWindows() camera.stop_preview() exit() def show_img_OpenCV(img): cv2.imshow(\u0026#39;test\u0026#39;, img) #cv2.waitKey(1) ch = cv2.waitKey(wait_time) if cv2.getWindowProperty(\u0026#39;test\u0026#39;,1) == -1 : close_cv2_window() # break if ch \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): # q close_cv2_window() if ch == 27: # ESC close_cv2_window() #break def resize_img(img): scale_percent = 50 width = int(img.shape[1] * scale_percent / 100) height = int(img.shape[0] * scale_percent / 100) dim = (width, height) resize_img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) return resize_img def read_img(path): cv2_img = cv2.imread(path) return resize_img(cv2_img) camera = PiCamera() camera.start_preview() while(True): # sleep(wait_time) path = \u0026#39;/home/pi/Desktop/tmp.jpg\u0026#39; camera.capture(path) cv2_img = read_img(path) show_img_OpenCV(cv2_img) Reference Closing video window using close \u0026ldquo;X\u0026rdquo; button in OpenCV, Python ImportError: libcblas.so.3: cannot open shared object file: No such file or directory Getting started with the Camera Module Picamera HOWTO Raspberry Pi 3 Camera Module - Raspberry Pi Tutorial DestroyWindow does not close window on Mac using Python and OpenCV HOW TO USE the Raspberry Pi High Quality Camera ","date":"2021-09-28T01:35:28+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/raspberry-pi-3-camera-module/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 照相機模組設定 camera module (內含測試範例程式碼) / (問題解決：picamera no view, picamera preview anydesk)"},{"categories":["830 - Raspberry Pi 3"],"content":"前言 強制指定為 3.5 mm 音源輸出 (後面數字為 1，0 為 default)\namixer cset numid=3 1 設定完後，記得「先插上音源，再重新開機」，應該就可以了\n(個人測試可行)\nReferencv Pi 3 B+ no audio device found ","date":"2021-09-28T01:19:37+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/raspberry-pi-3-audio-setting/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 3.5 mm jack 音源設定 audio setting (問題解決: rpi3 3.5 mm jack no sound)"},{"categories":["830 - Raspberry Pi 3"],"content":"安裝個人常用套件腳本 安裝文字編輯器，螢幕鍵盤 (預防萬一只有滑鼠)\n#!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; # update, upgrade sudo apt-get update -y sudo apt-get upgrade -y sudo apt update -y # code editor echo \u0026#34;[2/5] installing code editor\u0026#34; sudo apt install vim -y sudo apt install code -y # keyboards echo \u0026#34;[3/5] installing keyboards...\u0026#34; sudo apt install matchbox-keyboard -y sudo apt install florence -y sudo apt install onboard -y # apt-get echo \u0026#34;[4/5] installing apt-get opencv\u0026#34; sudo apt-get install libcblas-dev -y sudo apt-get install libhdf5-dev -y sudo apt-get install libhdf5-serial-dev -y sudo apt-get install libatlas-base-dev -y sudo apt-get install libjasper-dev -y sudo apt-get install libqtgui4 -y sudo apt-get install libqt4-test -y # pip echo \u0026#34;[5/5] installing pip python-opencv\u0026#34; pip3 install opencv-python pip3 install -U numpy Raspberry Pi 3 系統設定 開機設定檔 (可將 SD 插入其他電腦中進行設定) sudo vim /boot/config.txt 系統設定 (開啟特定功能需要，例如相機, ssh) 也可以從 GUI 中的 Raspberry Pi 設定中調整，下面附上的是使用 terminal 的方法\nsudo raspi-config 可參考：https://ithelp.ithome.com.tw/articles/10235452\n關機 sudo shutdown -h now 重新開機 sudo reboot 安裝 zsh or fish 這只是個人習慣，換個比較好用的 shell。\n安裝 zsh shell / 自動化腳本 #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/5] install zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; sudo apt-get install zsh -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] install oh-my-zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; echo \u0026#34;[5/5] change default shell to zsh shell\u0026#34; chsh -s /bin/zsh #grep pi /etc/passwd #check change shell in /etc/passwd # ----- powerlevel10k theme ------ # echo \u0026#34;install powerlevel10k\u0026#34; git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k echo \u0026#39;source ~/powerlevel10k/powerlevel10k.zsh-theme\u0026#39; \u0026gt;\u0026gt;! ~/.zshrc echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc echo \u0026#34;[optional] set ~/.zshrc (zsh seetings)\u0026#34; #vim ~/.zshrc echo \u0026#34;[optional] set ~/.p10k.zsh (powerlevel10k seetings)\u0026#34; #p10k configure # GUI version cp ~/.p10k.zsh ~/.p10k.zsh.bak # backup vim ~/.p10k.zsh echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc # ----- zsh plugins ------ # echo \u0026#34;[optional] install Syntax Highlighting Plugin\u0026#34; # doc: https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.md git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting echo \u0026#34;[optional] install ZSH-AutoSuggestion Plugin\u0026#34; # doc: https://github.com/zsh-users/zsh-autosuggestions/blob/master/INSTALL.md git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions echo \u0026#34;[optional] set ~/.zshrc to use plugins\u0026#34; vim ~/.zshrc #plugins=( #git #zsh-syntax-highlighting #zsh-autosuggestions #) echo \u0026#34;[restart] restart zsh\u0026#34; source ~/.zshrc 安裝 fish shell / 自動化腳本 #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/5] install fish\u0026#34; #sudo apt-add-repository ppa:fish-shell/release-3 # for ubuntu sudo apt-get install fish -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] install oh-my-fish\u0026#34; curl -L https://get.oh-my.fish | fish echo \u0026#34;[5/5] change default shell to zsh shell\u0026#34; chsh -s /usr/bin/fish pi # chsh -s /usr/bin/fish ubuntu #grep ubuntu /etc/passwd #check change shell in /etc/passwd echo \u0026#34;[optional] change oh-my-fish theme\u0026#34; omf install agnoster omf theme agnoster 設定 swap memory (用 SD 卡空間增加 ram 量，避免容易因為 memory 不足產生的當機) swap為虛擬記憶體，當memory不足時，我們可以暫時使用swap空間作為memory使用。\n我們可以自己決定要分配多少的 swap memory 給系統。\n查看目前系統的 swap 記憶體大小 swapon -s # 或者，可以使用 top 或安裝 htop 來看目前的 swap 記憶體大小 top htop 查看目前系統磁碟空間 (剩餘的磁碟大小） df -h 建立好 swapfile 檔案，並指定要分配多少記憶體大小給他，啟動 swap 記憶體 下面以建立 4G 的記憶體作為示範 (4G 可自行更改) sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile 可能會碰到的問題：fallocate: fallocate failed: Text file busy 表示目前的 swap 記憶體正在使用中，需要先關閉swap記憶體\nsudo swapoff -a 掛載 /etc/fstab ，使開機能夠自動啟動 swap memory 因為我們的設定並不是永久設定，重新開機時設定會消失，\n如果想要開機自動啟用 swap memory，我們可以掛載 /etc/fstab\nsudo vim /etc/fstab 設定值範例如下 /swapfile swap swap sw 0 0 重要：確認有無正確設定 swap 如果 swap 設定有問題，可能會導致「無法開機」或更嚴重的後果。\n我們可以用以下執令來確認： sudo mount -a 如果沒有問題，正常來講就不會回傳任何東西。\n如果有問題，請再次檢查有沒有哪一個步驟出錯。\n有問題的範例：\n(這只是隨便舉例，表示沒有設定好內容。)\n詳細可參考這篇：https://wongwongnotes.com/posts/linux-shell/system-admin/linux-swap/\nReference https://www.opencli.com/linux/linux-add-swap https://wongwongnotes.com/posts/linux-shell/system-admin/linux-swap/ Install and Use Vim on Raspberry Pi Day3 - 遠端控制樹莓派 Setting up an On-Screen Keyboard on the Raspberry Pi 3 Solutions to Enable On-screen Keyboard on Raspberry Pi OS Linux 關機指令（shutdown、halt 與 poweroff）教學與範例 2013/10/07 chsh -s /usr/bin/zsh not working How to Change Your Default Shell on Linux with chsh Visual Studio Code on Raspberry Pi ","date":"2021-09-28T01:15:40+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Screenshot-from-2021-03-22-15-52-46.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/embedded/raspberry-pi/raspberry-pi-3-basic-setting/","tags":["Pi","Raspberry"],"title":"【Rpi3】Raspberry Pi 3 / 樹莓派 個人基礎設定安裝筆記 personal basic setting (內含虛擬鍵盤設定)"},{"categories":["299 - C++ 問題解決"],"content":"問題描述 有時 C++ 執行到一半會莫名停止，\n此文為之前解 bug 最後找到的有效解法。\n解法 可能原因有無限多種，但這邊只列出個人過去踩雷的經驗。\ninotify 在 scandir 後 return -1 inotify 在 scandir 後 return -1，\n也就是找不到目標資料夾，\n如果沒有做適當處理的話，程式可能就會卡死在這邊。\ninotify 未找到檔案，導致程式卡在無限迴圈當中 inotify 撰寫的邏輯會當沒得到檔案時，\n程式永遠再等待，導致永遠卡在等待檔案的迴圈當中\n","date":"2021-09-27T16:30:18+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/cpp-stop/","tags":["C++"],"title":"【C++】問題解決：C++ 執行到一半莫名卡住的可能原因"},{"categories":["350 - Linux 終端機操作"],"content":"前言 fish 啟用 anaconda\n安裝 fish 如果要看怎麼安裝 fish\n可以參考這篇：【Linux】自動化腳本 / 安裝 fish, oh-my-fish （問題解決：omf theme not working）\n在 fish 中新 terminal 自動啟動 anaconda 謝天謝地，感謝 anaconda 幫助我們自動完成了一樣複雜的工作\n現在我們只要：\nconda init fish 自動啟動自己的環境 上面做完之後，再自己去 activate 就好了\n範例： conda activate myenv 小結 config.fish 修改後應該如下： 使用 vim 修改 vim ~/.config/fish/config.fish 修改結果 if status is-interactive # Commands to run in interactive sessions can go here end # \u0026gt;\u0026gt;\u0026gt; conda initialize \u0026gt;\u0026gt;\u0026gt; # !! Contents within this block are managed by \u0026#39;conda init\u0026#39; !! eval /home/ubuntu/anaconda3/bin/conda \u0026#34;shell.fish\u0026#34; \u0026#34;hook\u0026#34; $argv | source conda activate myenv # \u0026lt;\u0026lt;\u0026lt; conda initialize \u0026lt;\u0026lt;\u0026lt; Reference Add conda to path in fish How to add exports to fish like in .bashrc? ","date":"2021-09-27T14:23:41+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/fish-terminal-anaconda/","tags":["Bash","Docker","Linux","Ubuntu","終端機操作"],"title":"【Linux 終端機操作 #7】在 fish terminal 初始化 anaconda"},{"categories":["350 - Linux 終端機操作"],"content":"前言 以往我們移動資料夾的方式都是 cd + \u0026ldquo;打一點開頭字 + tab\u0026rdquo; + \u0026ldquo;打一點開頭字 + tab\u0026rdquo; \u0026hellip;，\n感覺已經很快了？ 不不不，直到我看到了這個更快的方法！！！\n才覺得以前的我根本太愚蠢了！！！\n上面那冗長的步驟，對於 z 來說只要 「z + 特定關鍵字 + tab」 就完成了\u0026hellip;\n那我之前是在白忙什麼\n就這樣！所以還不快學！\n閱讀這篇教學前，請先閱讀前一篇教學有關安裝「zsh+zim」的文章，\n沒有前置步驟是裝不起來的XD\n文章連結：\nhttps://wongwongnotes.com/posts/linux-shell/environment/linux-zim-zsh/\n介紹 z z，乾怎麼聽起來就超帥的啦！\nz 就是一個透過「儲存經常使用的資料夾路徑，\n使用時進行分析後，針對關鍵字搜尋你這次最有可能會想去的資料夾」\n也就是說，越接近最近使用且使用越頻繁的資料夾，就是最容易被搜尋到的！\n而且，沒有像 cd 一樣「綁死開頭關鍵字」！\n只要「絕對路徑內包含該字詞，都是有機會被搜尋到的！」\n安裝 z 安裝 z 需要透過 zim 這個框架進行安裝，\n如果還沒有安裝 zim 的，請去上一篇文章看教學。\n文章連結：\nhttps://wongwongnotes.com/posts/linux-shell/environment/linux-zim-zsh/\nstep 1. 編輯 zimrc 透過以下指令編輯 zimrc\nvim ~/.zimrc step 2. 輸入安裝 z 的指令 請隨便找一行輸入以下的內容，\n個人是放在有一堆 zmodule 的附近，這樣排起來也比較整齊。\nzmodule rupa/z step 3. 回到 terminal，開始安裝 請確認剛剛有先將文件修改完成，並輸入以下指令，即可開始安裝。\nzimfw install 額外補充 如果按一次 tab，找到的路徑不是我要的怎麼辦？ 就「多按幾次 tab」，總是會有中的吧！！！\n而且依然還是「關鍵字搜尋」，所以都比輸入「cd + 關鍵字開頭」有彈性的非常多！！！\nReference 我的 Shell 環境設定：zsh + zim + powerlevel10k + z ","date":"2021-09-26T02:24:33+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/linux-zim-zsh-z/","tags":["Linux","sshfs","終端機操作"],"title":"【Linux 終端機操作 #6】z，zim 套件中最快搜尋指定資料夾的套件，以後不用 cd 之後再慢慢 tab 尋找路徑了！"},{"categories":["350 - Linux 終端機操作"],"content":"前言 凡是使用終端機開發的工程師，\n遲早都會碰到經常使用 terminal 的階段，\n而這時，使用 terminal 的效率就會變成一個重要的指摽！\n如果有個又快又好用的 terminal，相信對工程師的開發效率來說一定是一大幫助！\n什麼是 zim (使用 zsh shell) zim 全名為 zsh improved framework，是 zsh shell 的一個強化框架XD，\n總之就是很多內建的東西只要安裝 zim 這一包就全部幫你完成，\n包含自動完成、git 相關指令自動完成、\n超強的「自動完成選單」、有時候還會被嚇到的「正規表示式自動轉譯」，\n而且「速度還超快」，嚇死人的非常的方便。\n根本就是一大包已經幫你整理好的超棒東西，直接整包弄好載下來！\n安裝 zim 的方法 安裝 zsh 安裝 zim 之前，請先安裝好 zsh，這部分我在其他的文章已經有提過了\nmac 的 zsh 是內建的，而 ubuntu 安裝 zsh 的方法為：\n#!/bin/bash echo \u0026#34;[1/4] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/4] install zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; sudo apt-get install zsh -y echo \u0026#34;[3/4] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/4] change default shell to zsh shell\u0026#34; chsh -s /bin/zsh #grep pi /etc/passwd #check change shell in /etc/passwd 這邊註一下：如果有裝過另外一個也是很有名的 oh-my-zsh 框架，\n建議是先移除以避免衝突啦\u0026hellip; 不過我自己先安裝了oh-my-zsh 沒移除直接安裝，\n目前看起來好像也沒事\u0026hellip;. (可能有變慢的問題XD 追求速度請慎！)\n安裝 zim 安裝好 zsh 後，zim 安裝方式也很簡單，只需要以下一行，就可以體驗各種強大的功能：\ncurl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh 補充小知識：zim 是 zsh improved 的縮寫哦！\n體驗一下 zim 的好用 基本的「語法判斷正確」與「自動完成」在 oh-my-zsh 都有了，\n這不夠看！ 接下來才是比較扯的！\n自動語法辭典 那個.. 指令都不用背了是吧?\n只要有點印象，按一下 tab，剩下的就通通跑出來囉！\n還可以繼續按 tab 配合上下左右，直接選到你要的指令，猛！\n連 git branch 的名稱都可以自動完成?! (神扯) 基本上一般我們使用 git 的時候，都會碰上需要輸入「完整 git branch 名稱的問題」，\nzim 終於用 tab 搞定了！！！ 還有各種 commit 紀錄也行\n也可以繼續按 tab 配合上下左右，直接選到你要的，神扯！\n支援正規表達式的內容自動展開?! (幹這真的爆扯) 正規表達式，是我們通常用來表示複數檔案的表達方法，\n例如：「*.mp4」，就等於該資料夾底下的所有 mp4 檔案，可以一次對大量檔案處理。\n但是\u0026hellip; zim 在輸入完正規表達式之後，按一下「tab」，\n「居然會自動展開正規表達式?! (也就是搜尋完全部符合正規表達式的結果，並列出來)」\n這我只能說 WTF 了?! 太猛了吧 zim (雖然不一定實用啦XDD 但這夠酷！)\n下面我只輸入 「day*」，按一下「tab」，\n所有符合「day 開頭的全部都被展開了！」，好扯！\n全步驟腳本整理 in host (ubuntu) #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; sudo apt-get update -y sudo apt-get upgrade -y echo \u0026#34;[2/5] install zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; sudo apt-get install zsh -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] change default shell to zsh shell\u0026#34; chsh -s /bin/zsh #grep pi /etc/passwd #check change shell in /etc/passwd echo \u0026#34;[5/5] install zim\u0026#34; curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh in container (no root) #!/bin/bash echo \u0026#34;[1/5] update system\u0026#34; apt-get update -y apt-get upgrade -y echo \u0026#34;[2/5] install zsh\u0026#34; sh -c \u0026#34;$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)\u0026#34; apt-get install zsh -y echo \u0026#34;[3/5] Show all current shells\u0026#34; cat /etc/shells echo $SHELL echo \u0026#34;[4/5] change default shell to zsh shell\u0026#34; chsh -s /bin/zsh #grep pi /etc/passwd #check change shell in /etc/passwd echo \u0026#34;[5/5] install zim\u0026#34; curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh Reference 打造屬於你自己的極速 Shell「iTerm + zsh + zim + powerlevel10k」 我的 Shell 環境設定：zsh + zim + powerlevel10k + z ","date":"2021-09-24T02:06:43+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/zim-3-1024x45.png","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/linux-zim-zsh/","tags":["Linux","終端機操作"],"title":"【Linux 終端機操作 #4】zim (+zsh) - 也許是目前最快也最實用的 linux terminal 與套件，終端機開發效率直接飆升"},{"categories":["350 - Linux 終端機操作"],"content":"前言 linux screen 是一個非常方便我們管理終端機視窗的工具，\n我們有時候可能在某一台電腦 terminal 操作到一半，\n突然會有需要換一台電腦，但又想要接續操作同一個 terminal 的內容，\n這時候 screen 可以幫助我們無縫切換這件事情。\n(也就是說，在 A 電腦上面顯示的所有 terminal 內容，可以直接顯示在 B 電腦 terminal 上)\n只有 terminal 當下分頁的全部內容，並沒有其他的視窗哦XD\n要其它視窗，你想要找的應該是 anydesk, teamviewer 之類的XD\n另外一個個人覺得很大的優點\n當如果是使用筆電操作遠端 terminal 的時候，\n如果當要移動，筆電合起來勢必「連線會中斷，導致任務中斷」\n使用 screen 就可以「讓任務在背景持續進行，就算要移動也不怕中斷任務」，\n而且可以「隨時再啟動同樣的畫面！！！」\n詳細介紹可以參考我之前的文：\n【Linux】透過 screen 讓 terminal 內的工作內容可以無縫接軌到任何電腦 (儲存當下 terminal 內容)，並可以繼續操作\n那麼今天這篇文要來討論什麼呢? screen 很方便我們現在知道了，\n但問題是「每次打開終端機的時候，我們都還要手動去輸入 screen 來啟動 screen」，\n「但絕大部分我們所要需求 screen 的情況，到了當下才啟動 screen 一切都要重來了！！！」\n但反過來說，如果反向操作的話，反而就沒有這個問題，\n因為「以 screen 啟動的 terminal 對使用者來說跟原生的根本沒差！」\n因此，我們要來研究「一開啟 terminal 就能夠啟動 screen 的方法」\n一開啟 terminal 就能夠啟動 screen 的方法 其實這說起來要實現也不難，我們只需要透過修改 zshrc 並新增 screen 這行指令，\n照理來說應該就沒問題了吧！！！\n「不！！！有個巨大的麻煩！！！」\n巨大的麻煩 我們試著去想，照上面的修改會發生什麼事，\n因為預設啟動 screen，而透過 screen 啟動的 terminal 會再啟動一次 screen，\n而這個又再次透過 screen 啟動的 terminal 會再啟動一次 screen，\n\u0026hellip;懂了嗎，這就是無窮迴圈的感覺呢！\n所幸，screen 預設有迴圈上限，超過 99 次就會被強制擋下來了。\n作者是不是連這點都想到了XDDD\n解決 screen 迴圈問題 mac 為了解決這個問題，我們需要先來研究一下 screen 造成環境參數的不同，\n在沒有 screen 的 terminal 下 echo $TERM 我們得到\n啟動 screen 後 ok，光是這樣的差異就夠我們設定了XD，還可以完全避免迴圈\n結論：在 mac 的 ~/.zshrc 中，請加上這一行 [[ $TERM != \u0026#34;screen\u0026#34; ]] \u0026amp;\u0026amp; exec screen -q ubuntu 為了解決這個問題，我們也需要先來研究一下 screen 造成環境參數的不同，\n在沒有 screen 的 terminal 下 echo $TERM 我們得到\n啟動 screen 後 ok，光是這樣的差異就夠我們設定了XD，還可以完全避免迴圈\n結論：在 ubuntu 的 ~/.zshrc 中，請加上這一行 [[ $TERM != \u0026#34;screen.xterm-256color\u0026#34; ]] \u0026amp;\u0026amp; exec screen -q Reference How can I start GNU Screen automatically when I open a new terminal window? ","date":"2021-09-22T12:00:19+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/screen-3.png","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/linux-default-screen-treminal/","tags":["Linux","sshfs","終端機操作"],"title":"【Linux 終端機操作 #2】終端機小技巧 - default start screen treminal，無縫接軌任何電腦，繼續使用同一個 terminal 殘留下來的訊息"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 這一篇我們會繼續拿現有的 day 16 成品來改，\n我們在 day 16 已經學會了如何取得點座標，\n接下來我們要將「點畫在畫面上」、並「取得該點座標」，\n而點座標又可分為「正規化 roi 比例座標」、「實際圖片座標」\n而在其中，「畫點」的功能，我們雖然能夠透過 PyQt 的 Qpainter 實現，\n不過因為我們後續也會大量使用 OpenCV 作為我們圖片處理的引擎，\n所以不如就趁先在開始導入吧！\n我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day17_roi_drawer\n導入 OpenCV 作為繪圖引擎 為了往後的開發順利，這次的開發我們必須謹慎先規劃一下了，\n接下來我們想導入的 OpenCV 作為我們圖像處理的引擎，\n我預期會是一個像 library 一樣的模組，\n我們把所有 OpenCV 圖像處理的功能的「細節」做在裡面，\n而 img_controller.py 只需要呼叫「這個 OpenCV engine 的 API 即可順利使用」。\n最好的情況甚至是 img_controller.py 都不用「import cv2」，\n而只有 OpenCV engine 這支程式統一「import cv2」，\n在此處理 OpenCV 相關的事情，所以我們等等也會稍微修改一下 day16 的部分。\n如下圖：(點圖可放大) UI 設計部份 (UI.py) 我們接續 day 16 的結果進行修改，\n我們新增兩個 QTextEdit，作為 roi 輸出的欄位。\n為什麼使用 QTextEdit? 因為這樣我們才能複製我們要用的結果XDD\nQlabel 只能顯示就不能複製了。\n我們就直接在 UI 上新增以下內容，並給予對應參數：\n*「顯示資訊用，不會改動 - Ratio ROI」：label_info_ratio_roi\n*「顯示資訊用，不會改動 - Real ROI」：label_info_real_roi\n*「依比例表示的 ROI 顯示欄位」：text_ratio_roi\n*「依實際圖片座標表示的 ROI 顯示欄位」：text_real_roi\n我設計的介面如同上圖\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day17.ui -\u0026gt; UI.py pyuic5 -x day17.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這樣我們的介面就大致出來囉！\ncontroller 設計部份 (controller.py) 從 UI.py 中找出物件名稱 這次我們新增了 3 個 label\n*「顯示資訊用，不會改動 - Ratio ROI」：label_info_ratio_roi\n*「顯示資訊用，不會改動 - Real ROI」：label_info_real_roi\n*「依比例表示的 ROI 顯示欄位」：text_ratio_roi\n*「依實際圖片座標表示的 ROI 顯示欄位」：text_real_roi\n老樣子，同 day13 的 scrollArea 說明，我們一樣需要刪除 scrollAreaWidgetContents 的部份 新增與調整的 scrollArea 片段 self.scrollArea = QtWidgets.QScrollArea(self.verticalLayoutWidget) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName(\u0026#34;scrollArea\u0026#34;) # self.scrollAreaWidgetContents = QtWidgets.QWidget() # self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 937, 527)) # self.scrollAreaWidgetContents.setObjectName(\u0026#34;scrollAreaWidgetContents\u0026#34;) # self.label_img = QtWidgets.QLabel(self.scrollAreaWidgetContents) self.label_img = QtWidgets.QLabel() # 調整為只單純宣告 self.label_img.setGeometry(QtCore.QRect(0, 0, 941, 521)) self.label_img.setObjectName(\u0026#34;label_img\u0026#34;) # self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.scrollArea.setWidget(self.label_img) 取得名稱後，去修改控制部分 我們今天會新增一個 opencv_engine.py，作為圖像處理的引擎使用，\n對我來說最理想的情況，就是只有 opencv_engine.py 這支程式 import cv2，\n然後我們將所有會用到 OpenCV 的功能封裝成 API (function)，\n再給 img_controller.py 去 call API(function)。\ncontroller.py：主要控制程式的部分 img_controller.py：另外封裝專門處理圖片的部分 opencv_engine.py：專門處理 OpenCV library 的引擎，基本上大部分圖像處理都靠他 專門處理圖像的引擎 opencv_engine.py 為了使用方便，我們把這個程式會呼叫的部分全部都用 class 封裝起來，\n而且方便直接取用，我們讓使用者不需要先宣告一個 instance(物件)，\n全部都寫成 static method，方便使用者直接呼叫這些已經包好的功能。\nimport cv2 class opencv_engine(object): @staticmethod def point_float_to_int(point): return (int(point[0]), int(point[1])) @staticmethod def read_image(file_path): return cv2.imread(file_path) @staticmethod def draw_point(img, point=(0, 0), color = (0, 0, 255)): # red point = opencv_engine.point_float_to_int(point) print(f\u0026#34;get {point=}\u0026#34;) point_size = 1 thickness = 4 return cv2.circle(img, point, point_size, color, thickness) 我們一共封裝了三個功能，point_float_to_int, read_image, draw_point，\nread_image(file_path) 對，所以我們等等也會把前幾天的透過 cv2.imread 的功能搬到這裡來，\n再改用 staticmethod 的呼叫方式 opencv_engine.read_image(file_path)，\n取得回傳的圖片。\npoint_float_to_int(point) 因為 OpenCV 的點座標處理會常常要求一定要整數輸入，\n而我們又常常以 (x, y) 這樣的座標為單位處理，\n而不是個別傳入 x 與 y，因此乾脆獨立依格 function 專門把傳進來的 (x, y)\n強轉成 (int x, int y) 並回傳。\ndraw_point(img, point=(0, 0), color = (0, 0, 255)) 這個就是我們的 OpenCV 老朋友了，\n為了使程式更加彈性，我特別把 point, color 拉出來，\n因此之後傳入時，我們除了座標之外、也可以指定傳入的顏色。\n不知道怎麼使用 OpenCV 畫點?\n可以參考我的另外一篇文，內有詳細說明：【OpenCV】11 – OpenCV 建立新空白圖、畫點、畫圓 create new pictures, draw points and draw circle\n專門處理圖片的 img_controller.py 依照上面的內容我們需要「修改圖片傳入的部分」，\n另外也需要新增「顯示 roi 文字的部分」、「呼叫顯示點的功能」\n導入 opencv_engine.py 我們要從 opencv_engine.py 導入 class opencv_engine，\n因此要 from opencv_engine import opencv_engine\n(前者為 .py 檔名，後者為 class 名稱)\nfrom opencv_engine import opencv_engine 讀檔部分修改 read_file_and_init(self) 這邊只截錄重點部分，\n因為我們這隻 img_controller.py 程式不想再 import cv2 了，\n全交由 opencv_engine 處理，\n所以我們將原來的 cv2.imread() 改為 opencv_engine.read_image()\ndef read_file_and_init(self): try: self.origin_img = opencv_engine.read_image(self.img_path) self.origin_height, self.origin_width, self.origin_channel = self.origin_img.shape except: self.origin_img = opencv_engine.read_image(\u0026#39;sad.png\u0026#39;) self.origin_height, self.origin_width, self.origin_channel = self.origin_img.shape 畫點 draw_point(self, point) 我們剛剛已經封裝好程式的功能了，所以這邊可以直接呼叫 opencv_engine.draw_point()，\n並把點座標傳入，記得先換算好座標 (昨天提到的，使用座標正規化統一處理)\ndef draw_point(self, point): # give me normalized point, i will help you to transform to origin cv image position cv_image_x = point[0]*self.origin_width cv_image_y = point[1]*self.origin_height self.display_img = opencv_engine.draw_point(self.display_img, (cv_image_x, cv_image_y)) self.__update_img() 更新 roi 內的文字 __update_text_point_roi(self, point) def __update_text_point_roi(self, point): # give me normalized point, i will help you to transform to origin cv image position cv_image_x = point[0]*self.origin_width cv_image_y = point[1]*self.origin_height self.ui.text_ratio_roi.append(f\u0026#34;[{point[0]:.6f}, {point[1]:.6f}]\u0026#34;) self.ui.text_real_roi.append(f\u0026#34;[{int(cv_image_x)}, {int(cv_image_y)}]\u0026#34;) 這邊就單純更新文字了，有正規化的 roi 點擊座標，\n相信更新起來也相當容易吧，只需要做一些簡單的運算。\n這邊為了我自己使用 roi 要使用的格式，我特別改為 list 顯示點座標，\n沒有什麼特別的不使用 tuple 而使用 list 的原因。\n修改傳回點座標的 get_clicked_position 新增最後兩行，畫點 draw_point、更新 roi 文字資訊 __update_text_point_roi\ndef get_clicked_position(self, event): x = event.pos().x() y = event.pos().y() self.__update_text_clicked_position(x, y) norm_x = x/self.qpixmap.width() norm_y = y/self.qpixmap.height() self.draw_point((norm_x, norm_y)) self.__update_text_point_roi((norm_x, norm_y)) 修改並重新整理系統流程 為什麼會突然提到這個\u0026hellip;，\n這個就是我前幾天不小心欠下的技術債，\n因為我沒有先規劃設定比例的邏輯，\n導致現在更新圖片的思路非常混亂。\n什麼混亂法呢? 就是顯示圖片的時候可能需要先想，要不要先去 call 設定比例，\n欸不對設定比例後接續也會更新圖片。\n可是我有時候只想更新圖片，比例也沒變，我還需要 call 設定比例嗎???\n就是沒有好好規劃啦! 所以我決定重新規劃架構，並畫出來這樣思路就很清楚了。\n目前的程式邏輯 這邊我已經先優化了 ratio 那邊的混亂邏輯才畫出來的，\n所以這已經是整理過的一版XD，\n啊不過做為示範，這裡其實還有可以優地的地方，\n觀察下圖我們可以發現：\n在初始化事件中 init() 與 set_path() 設定新圖片路徑中，\n我們有同樣的邏輯，都是先呼叫讀檔後去更新圖片。\n這些都是要整理出流程圖才知道能優化的，沒整理前我也沒想到這裡可以優化。\n所以我們就把流程圖優化成下圖 我們把重複的部分都塞進讀檔內，讓讀檔後可以直接更新，\n流程圖的邏輯就可以看起來更乾淨。\n統一命名格式 set, update 這個也是為了以後的自己好，因為程式會越寫越大，\n趁現在我們把命名格式統一一下，\nset 開頭的 function：可以外部呼叫，為修改事件的開頭 __update 開頭的 function：皆為 private function，不可外部呼叫，只作為更新畫面使用 好啦(累癱，至少現在趁程式還小，趕快把該整理的格式都整理後，之後相信會舒服很多的。\n於是，這是最終修改後的 img_controller.py 這會是我最後一次貼完整程式碼了，因為越寫越大XDD，\n再貼完整程式碼於文章中會太占版面XDD，\n之後會只貼更新的部分，想要完整程式碼的話可以去看我的 github。\nfrom PyQt5 import QtCore from PyQt5.QtGui import QImage, QPixmap from opencv_engine import opencv_engine class img_controller(object): def __init__(self, img_path, ui): self.img_path = img_path self.ui = ui self.ratio_value = 50 self.read_file_and_init() def read_file_and_init(self): try: self.origin_img = opencv_engine.read_image(self.img_path) self.origin_height, self.origin_width, self.origin_channel = self.origin_img.shape except: self.origin_img = opencv_engine.read_image(\u0026#39;sad.png\u0026#39;) self.origin_height, self.origin_width, self.origin_channel = self.origin_img.shape self.display_img = self.origin_img self.__update_text_file_path() self.ratio_value = 50 # re-init self.__update_img() def set_path(self, img_path): self.img_path = img_path self.read_file_and_init() def __update_img_ratio(self): self.ratio_rate = pow(10, (self.ratio_value - 50)/50) qpixmap_height = self.origin_height * self.ratio_rate self.qpixmap = self.qpixmap.scaledToHeight(qpixmap_height) self.__update_text_ratio() self.__update_text_img_shape() def __update_img(self): bytesPerline = 3 * self.origin_width qimg = QImage(self.display_img, self.origin_width, self.origin_height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(qimg) self.__update_img_ratio() self.ui.label_img.setPixmap(self.qpixmap) self.ui.label_img.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.ui.label_img.mousePressEvent = self.set_clicked_position def __update_text_file_path(self): self.ui.label_file_name.setText(f\u0026#34;File path = {self.img_path}\u0026#34;) def __update_text_ratio(self): self.ui.label_ratio.setText(f\u0026#34;{int(100*self.ratio_rate)} %\u0026#34;) def __update_text_img_shape(self): current_text = f\u0026#34;Current img shape = ({self.qpixmap.width()}, {self.qpixmap.height()})\u0026#34; origin_text = f\u0026#34;Origin img shape = ({self.origin_width}, {self.origin_height})\u0026#34; self.ui.label_img_shape.setText(current_text+\u0026#34;\\t\u0026#34;+origin_text) def __update_text_clicked_position(self, x, y): # give me qpixmap point self.ui.label_click_pos.setText(f\u0026#34;Clicked postion = ({x}, {y})\u0026#34;) norm_x = x/self.qpixmap.width() norm_y = y/self.qpixmap.height() print(f\u0026#34;(x, y) = ({x}, {y}), normalized (x, y) = ({norm_x}, {norm_y})\u0026#34;) self.ui.label_norm_pos.setText(f\u0026#34;Normalized postion = ({norm_x:.3f}, {norm_y:.3f})\u0026#34;) self.ui.label_real_pos.setText(f\u0026#34;Real postion = ({int(norm_x*self.origin_width)}, {int(norm_y*self.origin_height)})\u0026#34;) def set_zoom_in(self): self.ratio_value = max(0, self.ratio_value - 1) self.__update_img() def set_zoom_out(self): self.ratio_value = min(100, self.ratio_value + 1) self.__update_img() def set_slider_value(self, value): self.ratio_value = value self.__update_img() def set_clicked_position(self, event): x = event.pos().x() y = event.pos().y() self.__update_text_clicked_position(x, y) norm_x = x/self.qpixmap.width() norm_y = y/self.qpixmap.height() self.draw_point((norm_x, norm_y)) self.__update_text_point_roi((norm_x, norm_y)) def draw_point(self, point): # give me normalized point, i will help you to transform to origin cv image position cv_image_x = point[0]*self.origin_width cv_image_y = point[1]*self.origin_height self.display_img = opencv_engine.draw_point(self.display_img, (cv_image_x, cv_image_y)) self.__update_img() def __update_text_point_roi(self, point): # give me normalized point, i will help you to transform to origin cv image position cv_image_x = point[0]*self.origin_width cv_image_y = point[1]*self.origin_height self.ui.text_ratio_roi.append(f\u0026#34;[{point[0]:.6f}, {point[1]:.6f}]\u0026#34;) self.ui.text_real_roi.append(f\u0026#34;[{int(cv_image_x)}, {int(cv_image_y)}]\u0026#34;) 執行結果 照我們 day5 的程式架構，我們執行\npython start.py 我們所有在畫面點擊的點，都會在下方以兩種不同的方式表示，\n分別是比例的座標、實際的圖片座標，\n就這樣完成了我要使用的 roi 標註工具。\nReference 【OpenCV】11 – OpenCV 建立新空白圖、畫點、畫圓 create new pictures, draw points and draw circle 【OpenCV】12 – 運用 OpenCV 畫線、畫矩形、畫框、畫橢圓 draw lines, draw rectangle, draw ellipse ","date":"2021-09-21T04:12:27+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/pyqt5-17-6.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-17/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 17 / Project 製作標註 roi 工具, 開始導入 OpenCV 作為繪圖引擎, 在圖上畫點並顯示座標"},{"categories":["350 - Linux 終端機操作"],"content":"前言 這篇是記錄當我使用終端機時，\n因為一開始安裝了自己 zsh，\n後來有一些腳本臨時需要切換回 bash 才能夠執行，\n使用 zsh 會跳錯與不相容的問題，\n當時不知道怎麼切換，現在才知道了解決方法，因此記錄一下。\n解決方法 其實解決方法非常的簡單，就是直接在終端機裡面輸入「想要切換的對應 shell」即可\n範例 例如我想要切換為 bash shell bash 例如我想要切換為 zsh shell zsh 例如我想要切換為 fish shell fish 原來這麼簡單，當初要是早點知道就好了，省得一堆不相容的麻煩\u0026hellip; 總之還是紀錄一下\nReference ","date":"2021-09-21T02:02:49+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/change-bash-zsh-fish/","tags":["Linux","終端機操作"],"title":"【Linux 終端機操作 #1】快速切換 shell 的方法 (change bash, zsh, fish)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 這一篇我們會繼續拿現有的 day 15 成品來改，\n接下來我們要面對關於「處理圖片」與「顯示圖片」不一致的問題。\n這是一個會影響非常深遠的問題，因此我們需要早點針對這個問題進行規劃。\n我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day16_mouse_get_pos\n我們先來分析「處理圖片」與「顯示圖片」不一致的問題 為什麼會有「處理圖片」與「顯示圖片」不一致的問題?\n最主要的原因是因為我們拿進來的圖片可能會解析度較高，\n而我們處理的視窗就那麼大，我們沒辦法每次都讓他已「原解析度」來顯示。\n所以在「處理圖片」與「顯示圖片」之間溝通的橋樑我們必須早點做處理。\n而在我們程式中，「處理圖片」與「顯示圖片」分別對應到的是以下兩個變數。\n顯示的圖片 self.qpixmap 處理中的圖片 self.img 分析兩者之間的「程式」關係 依照 day15 的邏輯，我們處理圖片顯示的過程中如下，\n我們來看看這其中有沒有什麼可以簡化的地方。\n1. self.img 是由 OpenCV 的 imread 取得的圖片 (讀入的原圖) self.img = cv2.imread(self.img_path) 2. 我們會先經由以下處理，將他轉為 Qimg self.qimg = QImage(self.img, self.origin_width, self.origin_height, bytesPerline, QImage.Format_RGB888).rgbSwapped() 3. 再來會由 Qimg 轉為 QPixmap self.origin_qpixmap = QPixmap.fromImage(self.qimg) 4. Qpixmap 可能會經由一些縮放的處理，最後藉由 Qlabel 顯示在畫面上 self.label_img.setPixmap(self.qpixmap) 分析這個流程，發現實際上圖片經過了很多次的轉換，才到最後顯示的部分。 OpenCV image -\u0026gt; Qimg -\u0026gt; QPixmap -\u0026gt; Qlabel顯示 我們目前最多是在 QPixmap 這裡才處理縮放的問題。\n但接下來也許我們會需要針對原圖進行改動，這時候我們會需要處理原解析度的圖片。\n「也就是說，雖然我們是在 QPixmap 作業，但實際上處理的層級是在 OpenCV image」\n我們簡化這個流程後，我們可以知道我們可以記錄以下訊息會更方便我們處理：\nQPixmap 現在的長寬 (會因為顯示而改變) QPixmap 與 OpenCV image 的比例差距 (會因為顯示而改變) OpenCV image 原圖的長寬 (永遠不變) 並且可以得到換算公式：\n「QPixmap 現在的長寬」=「OpenCV image 的長寬」*「QPixmap 與 OpenCV image 的比例差距」 有沒有更不容易混淆的做法? - 不如我們都「正規化」一下 雖然上面我們已經把公式都寫出來也整理好了，但我覺得換算上還是很容易混淆\u0026hellip;\n例如：一不小心可能就會不小心把公式寫錯邊，到底誰乘誰?、到底誰除誰?\n所以我們就統一用「正規化」來溝通吧，這樣標準就一定一致了。\n如下圖：我們原來的作法 這個做法的優點就是直覺，但使用公式上需注意有沒有不小心乘除搞錯。\n等等我們要進行座標 (x ,y) 換算時更需要小心。\n如下圖：我們優化的作法 (正規化) 我們一律先把 (x,y) 座標正規化至一個長寬介於為 0~1 的比例上，\n再來進行後續的換算，這樣我們只要知道「顯示圖片」、「實際圖片」的長寬，\n在處理上都一慮用正規化的概念下去想 (x, y)，\n我們會相對比較難犯下不小心搞錯公式的問題。\n簡單來說，可以比較不容易出現公式錯誤的問題。(對我個人來說)\nUI 設計部份 (UI.py) 我們今天要來取得圖片上的座標，會由 day 15 的結果繼續進行更改，\n上述的討論中，我們已經有討論到我們怎麼樣處理「顯示圖片」與「原先圖片」的差異，\n我們就直接在 UI 上寫下以下內容，並給予對應參數：\n*「點擊座標(顯示圖片的座標)」：label_click_pos\n*「換算後，正規化的座標」：label_norm_pos\n*「實際座標(對應到原圖片的座標)」：label_real_pos\n我設計的介面如同上圖\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day16.ui -\u0026gt; UI.py pyuic5 -x day16.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這樣我們的介面就大致出來囉！\ncontroller 設計部份 (controller.py) 從 UI.py 中找出物件名稱 這次我們新增了 3 個 label\n*「點擊座標(顯示圖片的座標)」：label_click_pos\n*「換算後，正規化的座標」：label_norm_pos\n*「實際座標(對應到原圖片的座標)」：label_real_pos\n同 day13 的 scrollArea 說明，我們一樣需要刪除 scrollAreaWidgetContents 的部份 新增與調整的 scrollArea 片段 self.scrollArea = QtWidgets.QScrollArea(self.verticalLayoutWidget) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName(\u0026#34;scrollArea\u0026#34;) # self.scrollAreaWidgetContents = QtWidgets.QWidget() # self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 937, 527)) # self.scrollAreaWidgetContents.setObjectName(\u0026#34;scrollAreaWidgetContents\u0026#34;) # self.label_img = QtWidgets.QLabel(self.scrollAreaWidgetContents) self.label_img = QtWidgets.QLabel() # 調整為只單純宣告 self.label_img.setGeometry(QtCore.QRect(0, 0, 941, 521)) self.label_img.setObjectName(\u0026#34;label_img\u0026#34;) # self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.scrollArea.setWidget(self.label_img) 取得名稱後，去修改控制部分 截至到 day15，總共有 controller.py, img_controller.py 兩支程式來控制我們的系統，\ncontroller.py：主要控制程式的部分 img_controller.py：另外封裝專門處理圖片的部分 修改控制主程式的 controller.py 我們接續 day 15 的內容，\n新增我們剛剛在 UI 增加的 label，因為也是跟圖片有關的內容，\n我們只做參數的傳遞，其他交由 img_controller.py 處理。\nself.img_controller = img_controller(img_path=self.file_path, label_img=self.ui.label_img, label_file_path=self.ui.label_file_name, label_ratio=self.ui.label_ratio, label_img_shape=self.ui.label_img_shape, label_click_pos=self.ui.label_click_pos, label_norm_pos=self.ui.label_norm_pos, label_real_pos=self.ui.label_real_pos) 另外封裝專門處理圖片的 img_controller.py 我們替 day 15 的 function 「擴充」新的偵測座標功能\n宣告的地方，新增傳入的參數 class img_controller(object): def __init__(self, img_path, label_img, label_file_path, label_ratio, label_img_shape, label_click_pos, label_norm_pos, label_real_pos): self.label_click_pos = label_click_pos self.label_norm_pos = label_norm_pos self.label_real_pos = label_real_pos 更新圖片時，同步增加監聽偵測滑鼠位置的 mousePressEvent def __update_img(self): self.label_img.setPixmap(self.qpixmap) self.label_img.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.label_img.mousePressEvent = self.get_clicked_position self.label_img.mousePressEvent = self.get_clicked_position\n我們替 Qlabel 增加一個 mousePressEvent，而宣告的 function 就是我們等等會撰寫的 get_clicked_position()\n幫助我們取得回傳座標的 get_clicked_position def get_clicked_position(self, event): x = event.pos().x() y = event.pos().y() self.norm_x = x/self.qpixmap.width() self.norm_y = y/self.qpixmap.height() print(f\u0026#34;(x, y) = ({x}, {y}), normalized (x, y) = ({self.norm_x}, {self.norm_y})\u0026#34;) self.__update_text_clicked_position(x, y) 我們每觸發一次上述的點擊 mousePressEvent，就會執行一次 get_clicked_position 的內容，\n我們可以從 event 這個變數取得點擊的 (x, y)\nx = event.pos().x() y = event.pos().y() 在我們最上方的討論中，我們決定要把所有的座標進行正規化，\n以避免直接運算，容易產生的公式乘除錯誤的問題，\n因此我們直接透過以下公式將座標正規化。\nself.norm_x = x/self.qpixmap.width() self.norm_y = y/self.qpixmap.height() 最後我們可以顯示一下，我們所點擊的 (x, y)，與正規化後介於 0~1 之間呈現比例展示的 x, y 座標。\n並將這些資訊傳入我們修改文字的 function 中。\nprint(f\u0026quot;(x, y) = ({x}, {y}), normalized (x, y) = ({self.norm_x}, {self.norm_y})\u0026quot;) self.__update_text_clicked_position(x, y) 更新畫面座標資訊的 __update_text_clicked_position() 因為只是純更新資訊，我們將此 function 設為 private，不讓我們能夠輕易存取內容，\n我們更新三種座標的顯示：\n*「點擊座標(顯示圖片的座標)」：label_click_pos\n*「換算後，正規化的座標」：label_norm_pos\n*「實際座標(對應到原圖片的座標)」：label_real_pos\ndef __update_text_clicked_position(self, x, y): self.label_click_pos.setText(f\u0026#34;Clicked postion = ({x}, {y})\u0026#34;) self.label_norm_pos.setText(f\u0026#34;Normalized postion = ({self.norm_x:.3f}, {self.norm_y:.3f})\u0026#34;) self.label_real_pos.setText(f\u0026#34;Real postion = ({int(self.norm_x*self.origin_width)}, {int(self.norm_y*self.origin_height)})\u0026#34;) 這樣就更新完了。\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py 我們點擊任意的點，就會顯示「該座標」、「正規化座標」、「對應原圖實際座標」。\n而在我們的 terminal 當中也會顯示一些我們剛剛印出來的資訊，方便我們 debug。\n觀察並檢查座標 (x, y) - 我們在 UI 介面上點擊的原點在哪? 這邊有個衍伸的問題，我們在 UI 介面上點擊的原點在哪?\n也就是說 (0, 0) 是從哪裡開始算的呢?\n我們可以順著我們剛剛做出來的成品，一路找到 (0, 0) 的位置，\n我們發現 (0, 0) 座標剛好就位於「圖片」的左上角，\n而不是 「UI介面」的左上角，看起來完全這符合我們預期\n(這邊只是再確認座標與我們想像無誤，免得後續才回來處理很麻煩)\n至於圖片的最右下角，座標又是什麼呢?\n我們可以發現就是圖片目前「顯示」的解析度的上限值，\n因此我們可以完全確認，我們正在操作的座標就是 QPixmap 的座標，\n我們的換算都可以由 QPixmap 出發，依照比例進行換算。\nReference ","date":"2021-09-19T13:57:23+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/pyqt5-16-5-1024x825.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-16/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 16 - 在 PyQt5 中取得圖片座標 (滑鼠位置) mousePressEvent，觀察圖片在 Qt 中產生的方式，對原圖進行座標換算處理"},{"categories":["442 - Sublime Text"],"content":"前言 這是個讓我頭痛很久的問題\n身為一個多刀流(???)工程師，\n平常快速寫腳本、輕量程式都靠 sublime text 3，\n大型開發才使用 VScode。\n結果最近我的 sublime text 3 在 ubuntu 上徹底開不起來，\n處理很久都沒有辦法解決，\n(我猜有動到一些系統設定檔才導致怎麼重裝都沒用QQ)\n經過了好一段時間，終於找到了一個稍微可以的解決方案！\n下載舊版 sublime text 2 且免安裝版！！！\n解決方法 當然，這只是一時我實在想不到該怎麼解的替代方案，\n一定不是最好的解法，但畢竟能夠用就是個好方法？\nstep 1. 進到 sublime text 官網，選擇舊版 Sublime Text 2 快速連結：Sublime Text 2 直接點選 「Linux 64 bit」，會下載一個 tar.bz2 檔案 step 2. 解壓縮後，可以直接開起來了！ 歡迎回來我的 Sublime Text！ QAQ\n雖然是舊版但好非常多了！！！\n如果後續有找到什麼比較好的解法，會再更新，目前就暫時先這樣了。\n","date":"2021-09-17T11:51:37+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/sublime-fix.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-can-not-open/","tags":[],"title":"【Sublime】問題解決：ubuntu 無法開啟 sublime text 3 的替代方案 (can not open sublime text)"},{"categories":["899 - Debug / Error","299 - C++ 問題解決"],"content":"問題描述 當我們編譯 C++ 時，遇到以下問題\nfatal error: mysql/mysql.h: No such file or directory #include \u0026lt;mysql/mysql.h\u0026gt; 解法 我碰到此問題時，是 container 內的 linux 環境並沒有安裝 mysql 相關的 library，\n補上安裝即可解決。\n補上安裝指令：\nsudo apt-get install libmysqlclient-dev Reference linuxC無法訪問mysql.h：fatal error: mysql.h: No such file or directory compilation terminated. ","date":"2021-09-15T02:45:16+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/other/debug-error/cpp-fatal-error-mysql/","tags":["C++","Debug","Error"],"title":"【C++】問題解決：fatal error: mysql/mysql.h: No such file or directory #include \u003cmysql/mysql.h\u003e"},{"categories":["420 - Docker"],"content":"前言 有時候我們進行開發會在遠端的 container 進行程式的修改，\n但如果純靠 vim 在 terminal 中使用，\n有時候不習慣 vim 的使用者，也許開發效率就會大打折扣。\n這邊提供的是可以透過 VScode，透過建立 ssh tunnel 的方式，\n將遠端的 Docker container 拉到本地端進行編輯。\n主要兩大步驟概略說明 要完成利用 VScode 透過 ssh tunnel，\n直接連線到遠端 Docker 的 container 中進行開發。\n主要有兩大步驟，\n(step 1) Host terminal 建立遠端 ssh tunnel 連線：建立連線 (step 2) VScode 設定的部分：存取資料夾顯示用的介面 (step 1) Host terminal 建立遠端 ssh tunnel 連線 建立一個 ssh tunnel，使我們可以在 local 直接存取遠端 container 內的資料\nssh -nNTL localhost:23750:/var/run/docker.sock ubuntu@192.168.2.XXX username, ip 請自行修改\n示意圖： 輸入完密碼後，「應該會像是卡住一樣，這才是正常的」，\n表示現在這個 terminal 已經與遠端建立了連結，\n到工作結束前「請勿關閉這個 terminal」，(等於關閉連結)\n並繼續執行以下修改 VScode 設定檔的動作。\n指令說明 我們透過本地的 localhost:23750 建立 ssh 連線至遠端的 docker daemon (docker.sock)\ndocker daemon，是一個 service，用來管理 docker image、啟動與停止 container 的 service\n建立 ssh tunnel (Local Port Forwarding) ssh -L [LOCAL_IP:]LOCAL_PORT:DESTINATION:DESTINATION_PORT [USER@]SSH_SERVER 翻譯成上述的指令就是：\n\u0026ldquo;LOCAL_IP:\u0026rdquo; LOCAL_PORT : localhost:23750 \u0026ldquo;DESTINATION\u0026rdquo; : DESTINATION_PORT : /var/run/docker.sock \u0026ldquo;USER@\u0026ldquo;SSH_SERVER : 很好理解，就是遠端的服務 於是我們就可以把 local 的 23750 port 連接上遠端的 docker daemon service\n之後再去 VScode 修改監聽的 \u0026ldquo;docker.host\u0026rdquo; 位置，\n即可達到在 VScode 直接修改遠端 container 的內容。\n這邊分析有點不太確定，有錯請再麻煩通知我更正。\n上面說明圖示如下： 可以參考說明：How to Set up SSH Tunneling (Port Forwarding) 更詳細的說明 想更了解這個指令在幹嘛的，這邊推薦一個網站：https://explainshell.com/\n個人認為是學習 linux 指令的神器，相關的說明都非常清楚，\n只要把指令貼上去給他解析，他就會有詳細說明了。\n而且網頁也是可以互動的，是個人學習 linux 指令的神器。\n如下圖： (step 2) VScode 設定的部分 基本的 docker 擴充功能必須先安裝好，\n而且 local 也需要具備有 docker 的功能，\n如果不知道如何在 windows 中啟用 docker 功能可以參考這篇：\nhttps://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/\nstep 2.1 打開「設定」，我們準備進行修改 我們打開並修改設定「settings.json」，\n點選「檔案」-\u0026gt;「喜好設定」中的「設定」。\nstep 2.2 在上方搜尋「settings.json」(不用全輸入)，打開「settings.json」，我們準備進行修改 或者也可以點擊右上角的符號，也可以叫出 「settings.json」\nstep 2.3 在「settings.json」中加入遠端 container port 的設定 在「settings.json」中加入這行\n\u0026#34;docker.host\u0026#34;: \u0026#34;tcp://localhost:23750\u0026#34; 示意圖： 只需要確保有加入紅框那行即可，\n其他行代表的是其他設定，可以不用管。\n完成結果 我們可以在 local 自己的 VScode，\n可以透過「Docker extension」直接看到遠端 container 內的資料。\n(注意：不需要再另外經由 VScode 的 ssh 至遠端)\n會顯示遠端的 container Reference VS Code: connect a docker container in a remote server How to Set up SSH Tunneling (Port Forwarding) Day2：認識 Docker 基本概念 SSH Tunneling (Port Forwarding) 詳解 ","date":"2021-09-10T02:17:47+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/WSL-4.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/docker-vscode-ssh-container/","tags":["Bash","Docker","Linux","Ubuntu"],"title":"【Docker #3】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發"},{"categories":["420 - Docker"],"content":"前言 windows 上不論是使用或安裝 Docker 真的有夠不方便的\u0026hellip;\n畢竟 powershell 的概念實在與 mac 或 ubuntu 的 terminal 差很多\u0026hellip;\n這邊就是我在 windows 上安裝並使用 Docker 的安裝筆記\n安裝 Docker (Docker Desktop) step 1：先去 docker 官方網站下載 Docker Desktop 下載網址：https://docs.docker.com/desktop/windows/install/ 找到那顆大大的「Docker Desktop for Windows」，按下去下載就對了!\nstep 2：安裝時記得設定安裝「WSL 2」的相關套件，開始安裝 Docker Desktop ! 下載回來後就可以開始安裝，基本上都直接套用他說的設定就好，\n不需要特別去改，另外這邊特別提到有一個\n「Install required Windows components for WSL 2」\n(預設就已經是打勾了，記得務必要裝)\nWSL 是 Windows Subsystem for Linux 的縮寫，\n基本上就讓我們可以在 Windows 上模擬 Linux 般的環境使用，\n由於 Docker 的服務又與這個有關，請務必安裝。\nstep 3：安裝 Docker Desktop 完成囉! 這邊做個紀錄，個人安裝的是 Docker Desktop 3.3.3 的版本，\n寫這篇文章的時候好像已經有最新版 4.0.0，但我就沒有再多去測試了。\n按下按鈕後，就會「重新開機」並進行一些初始化必要設定，請務必注意先把工作儲存!!!\nWSL 相關問題 (常見) 如果沒有碰到 WSL 的相關問題，只能說恭喜，真的很幸運!\n(其實有在 windwos 開發過 linux 相關程式的人應該都曾經有裝過，只是可能你忘了XD)\nerror：WSL 2 installation is incomplete. 基本上我們要來額外處理 WSL 的安裝問題，我們可以直接點擊錯誤訊息上的連結，\n或點以下網址：https://docs.microsoft.com/zh-tw/windows/wsl/install-win10\n如圖： https://docs.microsoft.com/zh-tw/windows/wsl/install-win10\nstep 1. 到網頁底下，找到步驟 4，下載對應的 Linux 核心更新套件 找到圖中「WSL2 Linux 核心更新套件 (適用於 x64 電腦)」的地方，點擊下載\nstep 2. 安裝 WSL 相關套件 這邊基本上一直按 Next 就可以了，設定都不用動 安裝完成的畫面 成功完成安裝並啟動 Docker Desktop! 到這邊基本上我們就已經完成 Docker 在 windows 上的安裝囉!\n可能會碰到的其他問題 出現 WSL 2 is not installed WSL 2 is not installed Install WSL using this PowerShell script (in an administrative PowerShell) and restart your computer before using Docker Desktop: Enable-WindowsOptionalFeature -Online -FeatureName $(\u0026#34;VirtualMachinePlatform\u0026#34;, \u0026#34;Microsoft-Windows-Subsystem-Linux\u0026#34;) 如圖： 原因 windows 上要使用 docker 需要具備一些條件：\n- 安裝 WSL (Windows Subsystem for Linux)，以模擬 Linux 環境 (原生 windows 需要安裝才能具備此項目) - 去 BIOS 將 SVM 模式開啟 (虛擬化技術) - 在 Windows 10 上啟用 Hyper-V，以建立虛擬機器 注意：Hyper-V 角色無法安裝於 Windows 10 家用版。(by windows 官方)\n另外，個人由於有使用夜神模擬器，之前有去開關 VT 相關的功能，\n如果有沒有使用的讀者，請記得去 BIOS 開啟對應的 SVM 開關\n可參考文章：https://support.bignox.com/zh-TW/regular/amd-vt\n解法可參考我的另一篇文： https://wongwongnotes.com/posts/dev-tools/containers/docker/docker-wsl-2-is-not-installed/\nReference Install Docker Desktop on Windows 在 Windows 10 上安裝 Hyper-V 安裝 WSL ","date":"2021-09-09T21:30:24+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/WSL-4.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/","tags":["Bash","Docker","Linux","Ubuntu"],"title":"【Docker #2】在 windows 上使用安裝並使用 Docker (全圖文說明) windows 安裝 WSL (Windows Subsystem for Linux)"},{"categories":["420 - Docker"],"content":"問題 在 windows 環境使用 docker 時出現以下訊息的解決方式\nWSL 2 is not installed Install WSL using this PowerShell script (in an administrative PowerShell) and restart your computer before using Docker Desktop: Enable-WindowsOptionalFeature -Online -FeatureName $(\u0026#34;VirtualMachinePlatform\u0026#34;, \u0026#34;Microsoft-Windows-Subsystem-Linux\u0026#34;) 如圖： 原因 windows 上要使用 docker 需要具備一些條件：\n- 安裝 WSL (Windows Subsystem for Linux)，以模擬 Linux 環境 (原生 windows 需要安裝才能具備此項目) - 去 BIOS 將 SVM 模式開啟 (虛擬化技術) - 在 Windows 10 上啟用 Hyper-V，以建立虛擬機器 注意：Hyper-V 角色無法安裝於 Windows 10 家用版。(by windows 官方)\n另外，個人由於有使用夜神模擬器，之前有去開關 VT 相關的功能，\n如果有沒有使用的讀者，請記得去 BIOS 開啟對應的 SVM 開關\n可參考文章：https://support.bignox.com/zh-TW/regular/amd-vt\n個人解決方法 step 1: 依照圖上說明，按下「Use Hyper-V」 以啟動 Hyper-V 錯誤訊息：WSL 2 is not installed WSL 2 is not installed Install WSL using this PowerShell script (in an administrative PowerShell) and restart your computer before using Docker Desktop: Enable-WindowsOptionalFeature -Online -FeatureName $(\u0026#34;VirtualMachinePlatform\u0026#34;, \u0026#34;Microsoft-Windows-Subsystem-Linux\u0026#34;) 由於個人有使用夜神模擬器的緣故，會去關閉 Hyper-V 的相關功能，\n因此我們必須先按下「Use Hyper-V」 以恢復原先 Hyper-V 的功能。\n如圖： step 2: 切換至 WSL 2 based engine 以開啟 Hyper-V 錯誤訊息：An error occurred Required Windows feature(s) not enabled: Hyper-V. Switch to WSL 2 based engine? To enable Hyper-V: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v 個人使用的夜神模擬器，似乎有改動到 VM 相關的設定，\n因此 step 1 的步驟通常不會那麼順利，\n我們還需要去切換至 WSL 2 based engine 以開啟 Hyper-V，\n(再把 kernel 改回 WSL 的設定)，才能順利啟用 Docker 功能。\n如圖： step 3: 看到以下訊息，重新開機看能不能解決 錯誤訊息：An error occurred Hardware assisted virtualization and data execution protection must be enabled in the BIOS. See https://docs.docker.com/docker-for-windows/troubleshoot/#virtualization 通常依照上述步驟處理後，就會看到這個最後一個只能按 ok 的視窗，\n他是在說明目前的虛擬化技術必須去 BIOS 啟動\n(也就是我們上述提到的 SVM 技術必須要去 enabled)\n如果已經有開啟 SVM 的，試試看在此時重新開機，也許就能夠解決問題。\n如果還沒有開啟的，就去開啟 SVM。\n如圖：\nstep 4: 如果還是不行，試著重裝 Docker 看看 偶爾我還是會碰到上述步驟無效的問題，\n此時我會去控制台把 Docker Desktop 移除，\n並重新安裝，通常就能夠解決問題了\n(如果不能解決就再去確認上述的一些設定。)\n如圖： 重新安裝後可以順利啟動了。\n(個人安裝的是 Docker Desktop 3.3.3 的版本，\n個人有碰過安裝 Docker Desktop 4.0.0 的一些問題，不過目前靠著裝舊版本暫時解決了。)\nReference ","date":"2021-09-09T18:11:58+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/WSL-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/docker-wsl-2-is-not-installed/","tags":["Bash","Docker","Linux","Ubuntu"],"title":"【Docker】問題解決：WSL 2 is not installed (Docker windows)"},{"categories":["922 - Mac / MacOS"],"content":"前言 在家工作或者連學校網站的時候，\n我們有時候需要透過 VPN 來連線，\n之前介紹了 windows 可以用 OpenVPN 的程式來連線\n今天我們要介紹用 mac 的 OpenVPN 連線的方法。\nwindows版本可以參考：https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-ssh/\n準備 VPN 工具 - OpenVPN step 1. 下載 OpenVPN 軟體 OpenVPN mac 版 下載網址：https://openvpn.net/client-connect-vpn-for-mac-os/\n點進去後，按下下圖框框處的 Download OpenVPN Connect v3 即可開始下載\nstep 2. 安裝 OpenVPN 軟體 點兩下即可安裝，安裝完即可開始使用囉。\n取得 vpn 設定檔案 (副檔名為 .ovpn 的檔案，通常會由企業或學校機構提供。) 總之，這邊幫不了忙XD\n請向你的 企業 或 學校機構拿到一個「副檔名為 .ovpn 的檔案」。\n連線至 VPN step 1. 匯入 「.ovpn」 設定檔 打開來我們會看到一個這樣的畫面，點擊 file 的地方，\n把剛剛上一個步驟的 .ovpn 檔案拉進來。\nstep 2. 進行 VPN 相關設定 紅匡處：上一個步驟將 .ovpn 的檔案拖曳進來後， 會自動在紅匡處顯示一些相關的資訊， - 綠匡處：我們在綠匡處輸入機構授權的帳號密碼。 然後就先不用設定別的東西了，直接右上角藍匡「save」，即可完成設定。\n之前沒注意到右上角有個 save，設定很久都卡住\u0026hellip;\nstep 3. 啟動 VPN 很直覺的，點擊開關就可以了。 開啟後，可以看到一些流量的相關數據。 一些其他的問題 出現 Missing external certificate 無視他，直接按下 continue 即可。\n(記得除非必要，不然不要在上一步亂設定一些 proxy 之類的東西，直接 save 即可。)\nReference https://openvpn.net/client-connect-vpn-for-mac-os/ ","date":"2021-09-09T03:01:04+08:00","image":"https://wongwongnotes.com/images/restored/2021/09/openvpn-9.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/mac-openvpn/","tags":["VSCode","macOS"],"title":"【OpenVPN】mac 使用 OpenVPN 建立 VPN 遠端連線的方法 (全圖文說明)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day15_image_loader\nUI 設計部份 (UI.py) 我們今天要把 Day 10 的讀檔功能 、Day 14 的滑條功能 ，\n整合進 Day 13 的最終成果 當中。\n因此我們會從 Day 13 接下去改後續的功能。 我設計的介面如同上圖，\n部份想法如下：\n除了 zoom in, zoom out 可縮放圖片大小之外，也可用滑條改變，我預期 50 -\u0026gt; 100% 0 -\u0026gt; 10% 100 -\u0026gt; 1000% - 推算公式可以得到 y = 10^((x-50)/50) 推算公式的過程，我們把所有數值先正規化到 -1~1 間，就會很好推公式了\n我們把 10% -\u0026gt; 0.1，100% -\u0026gt; 1，1000% -\u0026gt; 10\n可參考一下的表，就是我們正規化後反推公式的過程。\n最後可得上方公式 y = 10^((x-50)/50)\nprogress bar 正規化 0 50 100 -50 0 50 -1 0 1 ratio 正規化 10% 100% 1000% 10^-1 10^0 10^1 -1 0 1 轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day15.ui -\u0026gt; UI.py pyuic5 -x day15.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這樣我們的介面就大致出來囉！\ncontroller 設計部份 (controller.py) 從 UI.py 中找出物件名稱 button 類 btn_open_file: 開啟檔案用按鈕 btn_zoom_in: 放大用按鈕 btn_zoom_out: 縮小用按鈕 label 類 label_ratio: 顯示現在圖片縮放比例 label_file_name: 顯示現在檔名 label_img_shape: 顯示現在/原來圖片大小 label_img: 顯示圖片 silder 類 silder_zoom: 控制圖片大小 scrollArea 類 scrollArea: 圖片縮放區域 同 day13 的 scrollArea 說明，我們一樣需要刪除 scrollAreaWidgetContents 的部份 self.scrollArea = QtWidgets.QScrollArea(self.verticalLayoutWidget) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName(\u0026#34;scrollArea\u0026#34;) # self.scrollAreaWidgetContents = QtWidgets.QWidget() # self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 937, 527)) # self.scrollAreaWidgetContents.setObjectName(\u0026#34;scrollAreaWidgetContents\u0026#34;) # self.label_img = QtWidgets.QLabel(self.scrollAreaWidgetContents) self.label_img = QtWidgets.QLabel() self.label_img.setGeometry(QtCore.QRect(0, 0, 941, 521)) self.label_img.setObjectName(\u0026#34;label_img\u0026#34;) # self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.scrollArea.setWidget(self.label_img) 可能會有新增與調整的 scrollArea 片段 取得名稱後，去修改 controller.py 我們繼續修改我們 day13 的程式碼，但我們這次發現我們的程式碼越來越大了，\n是時候該做點封裝了，我們決定將圖片有關的功能封裝至另外一個檔案 img_controller.py\n另外封裝專門處理圖片的 img_controller.py from PyQt5 import QtCore from PyQt5.QtGui import QImage, QPixmap import cv2 class img_controller(object): def __init__(self, img_path, label_img, label_file_path, label_ratio, label_img_shape): self.img_path = img_path self.label_img = label_img self.label_file_path = label_file_path self.label_ratio= label_ratio self.label_img_shape = label_img_shape self.ratio_value = 50 self.read_file_and_init() self.__update_img() def read_file_and_init(self): try: self.img = cv2.imread(self.img_path) self.origin_height, self.origin_width, self.origin_channel = self.img.shape except: self.img = cv2.imread(\u0026#39;sad.png\u0026#39;) self.origin_height, self.origin_width, self.origin_channel = self.img.shape bytesPerline = 3 * self.origin_width self.qimg = QImage(self.img, self.origin_width, self.origin_height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.origin_qpixmap = QPixmap.fromImage(self.qimg) self.ratio_value = 50 self.set_img_ratio() def set_img_ratio(self): self.ratio_rate = pow(10, (self.ratio_value - 50)/50) qpixmap_height = self.origin_height * self.ratio_rate self.qpixmap = self.origin_qpixmap.scaledToHeight(qpixmap_height) self.__update_img() self.__update_text_ratio() self.__update_text_img_shape() def set_path(self, img_path): self.img_path = img_path self.read_file_and_init() self.__update_img() def __update_img(self): self.label_img.setPixmap(self.qpixmap) self.label_img.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) def __update_text_file_path(self): self.label_file_path.setText(f\u0026#34;File path = {self.img_path}\u0026#34;) def __update_text_ratio(self): self.label_ratio.setText(f\u0026#34;{int(100*self.ratio_rate)} %\u0026#34;) def __update_text_img_shape(self): current_text = f\u0026#34;Current img shape = ({self.qpixmap.width()}, {self.qpixmap.height()})\u0026#34; origin_text = f\u0026#34;Origin img shape = ({self.origin_width}, {self.origin_height})\u0026#34; self.label_img_shape.setText(current_text+\u0026#34;\\t\u0026#34;+origin_text) def set_zoom_in(self): self.ratio_value = max(0, self.ratio_value - 1) self.set_img_ratio() def set_zoom_out(self): self.ratio_value = min(100, self.ratio_value + 1) self.set_img_ratio() def set_slider_value(self, value): self.ratio_value = value self.set_img_ratio() 讀者看到會不會看到突然覺得這個程式難度整個飛起來\u0026hellip;\n但其實這些都是我們前幾天學的東西哦，只是我們加入了一點物件導向的寫法，\n我們把每一個功能都分裝好，不要讓所有程式碼集中到 controller.py，\n不然 controller.py 太過強大，萬事都能做到，我們還真的不知道，\n如果今天要改一個功能，要去 controller.py 的哪找\u0026hellip;\n(就跟圖書館什麼都有，只是你能不能有效率的快速找到你要的東西一樣)\n主要我們分裝成這些函數，並遵守我自己定義的規則：\n- set_instance_name：使用者可以 call，去修改一些想要的變化，並在此實作複雜功能與算法 - __update_instance_name：private function，不希望使用者去 call，主要只負責單純的更新 info，而不實作任何複雜功能或算法 因此我們有：\ninit()：初始化 read_file_and_init()：實作讀取圖片檔案，並實作圖片初始化 set_img_ratio()：設定圖片比率，並實作變化 set_path()：更換檔案時，更新圖片路徑 set_zoom_in()：設定 zoom in 功能 set_zoom_out()：設定 zoom out 功能 set_slider_value()：設定縮放功能的那條 bar __update_img()：更新圖片 __update_text_file_path()：更新圖片路徑的文字 __update_text_ratio()：更新圖片縮放比率的文字 __update_text_img_shape()：更新圖片大小的文字 controller.py 的部份 from PyQt5 import QtCore from PyQt5.QtWidgets import QMainWindow, QFileDialog import time import os from UI import Ui_MainWindow from img_controller import img_controller class MainWindow_controller(QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): self.file_path = \u0026#39;\u0026#39; self.img_controller = img_controller(img_path=self.file_path, label_img=self.ui.label_img, label_file_path=self.ui.label_file_name, label_ratio=self.ui.label_ratio, label_img_shape=self.ui.label_img_shape) self.ui.btn_open_file.clicked.connect(self.open_file) self.ui.btn_zoom_in.clicked.connect(self.img_controller.set_zoom_in) self.ui.btn_zoom_out.clicked.connect(self.img_controller.set_zoom_out) self.ui.slider_zoom.valueChanged.connect(self.getslidervalue) def open_file(self): filename, filetype = QFileDialog.getOpenFileName(self, \u0026#34;Open file\u0026#34;, \u0026#34;./\u0026#34;) # start path self.init_new_picture(filename) def init_new_picture(self, filename): self.ui.slider_zoom.setProperty(\u0026#34;value\u0026#34;, 50) self.img_controller.set_path(filename) def getslidervalue(self): self.img_controller.set_slider_value(self.ui.slider_zoom.value()+1) 因為我們把大部分的功能都封裝在 img_controller.py 裡面了，\n因此現在我們只需要單純的「from img_controller import img_controller」，\n就能使用 img_controller 的 「set 開頭相關的函數」搞定所有圖片相關的功能。\n這邊我們在 setup_control() 實作的有 def setup_control(self): self.file_path = \u0026#39;\u0026#39; self.img_controller = img_controller(img_path=self.file_path, label_img=self.ui.label_img, label_file_path=self.ui.label_file_name, label_ratio=self.ui.label_ratio, label_img_shape=self.ui.label_img_shape) self.ui.btn_open_file.clicked.connect(self.open_file) self.ui.btn_zoom_in.clicked.connect(self.img_controller.set_zoom_in) self.ui.btn_zoom_out.clicked.connect(self.img_controller.set_zoom_out) self.ui.slider_zoom.valueChanged.connect(self.getslidervalue) 分別有設定圖片路徑，初始化 img_controller，設定按鍵功能，\n因為 button 因為吃的也是 function，可以直接 call img_controller 的函數來使用，\n而開檔案與滑條的部份，我們一樣另外使用 open_file()、getslidervalue() 來實作。\n特別注意圖片初始化問題，我們另外用 init_new_picture() 來處理 因為有時候開新的檔案，就會有一大堆東西要初始化，一個疏忽就會讓人非常頭痛，因此我們也另外使用 init_new_picture()，把所有應該要初始化的功能都集中在這邊。 (例如像 bar 需要回到中間 50 的地方)\n另外把程式封裝，還有一個最大的好處就是更容易 debug，\n我們因為功能切的很細，如果我們今天看畫面上「少了哪一個功能」，\n我們只需要去把對應的功能呼叫回來，問題就解決了。\n如果是沒有封裝好的情況，可能會要開始在「function 海當中尋找對應的那一行功能」，會大幅減少解 bug 的效率。\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py Reference ","date":"2021-08-30T19:11:43+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-15-3-1024x824.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-15/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 15 / Project 與檔案功能整合，製作出可讀取圖片並可縮放的 UI 介面 (使用 PyQt + OpenCV)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖\n前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day14_slider\nUI 設計部份 (UI.py) 今天我們要來實作一個簡單的功能，製作可以滑動的滑條\n我們在 input Widgets 中找到 Horizontal Slider，將他移進畫面中，\n另外加入一個 Qlabel 作為顯示用。\n讀者們可以開始自行設計自己的介面囉，以上為我的示範。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day14.ui -\u0026gt; UI.py pyuic5 -x day14.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這樣我們的介面就大致出來囉！\ncontroller 設計部份 (controller.py) 從 UI.py 中找出物件名稱 這次的功能很簡單，只有兩個物件\nself.label：顯示用的 self.horizontalSlider：可以拉的滑條 取得名稱後，去修改 controller.py 我們繼續修改我們 day12 的程式碼\nfrom PyQt5 import QtCore, QtWidgets from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QFileDialog from PyQt5.QtCore import QThread, pyqtSignal import time from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): self.ui.horizontalSlider.valueChanged.connect(self.getslidervalue) def getslidervalue(self): self.ui.label.setText(f\u0026#34;{self.ui.horizontalSlider.value()}\u0026#34;) print(self.ui.horizontalSlider.value()) setup_control() 修改的部份 self.ui.horizontalSlider.valueChanged.connect(self.getslidervalue) 我們一樣將 self.horizontalSlider 連結 getslidervalue() 的功能，\n等等我們將取得變化後的實作，製作在此處。\n記得要多呼叫 valueChanged 這個方法，\n我們才能夠透過偵測滑動條值的變化，直接同步反應數值的改變到畫面上。\ngetslidervalue() 的部份 我們可以使用 self.ui.horizontalSlider.value() 取得現在滑動條的數值，\n而 self.getslidervalue() 已經定義好是有變化才會觸發的功能，\n所以我們就藉由 self.ui.label.setText() 把對應的數值寫進 label 當中。\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py (因為我們有寫 print，所以在 terminal 中也會同步印出數據，讓我們能夠觀察變化。)\nReference ","date":"2021-08-27T19:09:37+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-14-4.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-14/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 14 - 使用 QSlider 製作可拖曳的滑條"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day13_scroll_area\n以 Qlabel 在 PyQt 中顯示圖片 這篇是延續 Day 12 顯示圖片 zoom in, zoom out 功能的後續開發，\n只有 zoom in, zoom out 有時還不足以應付我們處理細節，\n因此我們需要一個捲軸，幫助我們能更自由的移動圖片。\nUI 設計部份 (UI.py) 新增捲軸欄位 - 我們先新增一個 Vertical Layout (QVBoxLayout) 位於 Layout 當中，決定好圖片可顯示的範圍。 - 然後在此 Vertical Layout 裡面再新增一個 Scroll Area (QscrollArea) 位於 container 當中，作為可以移動的捲軸範圍。 在此 Scroll Area (QscrollArea) 當中，再新增一個 Qlabel。作為圖片顯示使用。 注意順序，先新增 Vertical Layout，疊加上 Scroll Area，再疊加上 Qlabel。 注意這些物件彼此之間的階層關係，一樣我們可以先修改一些物件名稱，方便我們等等使用。 UI 優化：顯示目前圖片的解析度 我們在介面的右下角新增能夠顯示目前圖片的解析度的 Qlabel，\n新增這個功能主要是能方便我們能夠確定現在圖片已經被我們縮放到什麼程度了。\n讀者們可以開始自行設計自己的介面囉，以上為我的示範。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day13.ui -\u0026gt; UI.py pyuic5 -x day13.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這樣我們的介面就大致出來囉！\ncontroller 設計部份 (controller.py) 修改 UI.py 的一些程式碼，達成在 QtDesigner 中做不到的事情 我們先觀察一下剛剛在 QtDesigner 中的物件階層關係，\n其中紅色框框的地方有多出一個我們不要的東西，scrollAreaWidgetContents，\n這個東西在 QtDesigner 中預設是會與 QscrollArea 一起被建立，\n但實際上因為我們已經很清楚我們需要的是 Qlabel 顯示的圖片，\n因此我們直接去改 UI.py 裡面的一些內容。\n程式碼中修改與 scrollAreaWidgetContents 相關的內容 我們可以透過搜尋功能幫助我們快速找到相關的段落，這些都是要刪掉的\nself.scrollAreaWidgetContents = QtWidgets.QWidget() self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 667, 427)) self.scrollAreaWidgetContents.setObjectName(\u0026#34;scrollAreaWidgetContents\u0026#34;) self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents) self.label.setGeometry(QtCore.QRect(0, 0, 1920, 1080)) self.label.setObjectName(\u0026#34;label\u0026#34;) self.scrollArea.setWidget(self.scrollAreaWidgetContents) 我們觀察一下，\n基本上前三行都是 scrollAreaWidgetContents 的定義，我們都用不到，直接刪。 self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)，\n是藉由 self.scrollAreaWidgetContents 定義出 self.label 的屬性，\n我們不想要這個屬性，但 self.label 是 QLabel 的屬性仍需要被宣告，\n因此我們將他改為 self.label = QtWidgets.QLabel()，單純只宣告他是 QLabel() 後兩行關於 self.label 的定義不需要修改，符合原先的定義即可 最後一行的 self.scrollArea.setWidget(self.scrollAreaWidgetContents)，因為我們已經去除了 self.scrollAreaWidgetContents 這個元素，改以 Qlabel 顯示的圖片直接置入 self.scrollArea 當中，因此我們修改成 self.scrollArea.setWidget(self.label)。 修改結果 上面的部份修改完後，結果如下： # self.scrollAreaWidgetContents = QtWidgets.QWidget() # self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 800, 400)) # self.scrollAreaWidgetContents.setObjectName(\u0026#34;scrollAreaWidgetContents\u0026#34;) self.label = QtWidgets.QLabel() self.label.setGeometry(QtCore.QRect(0, 0, 0, 0)) self.label.setObjectName(\u0026#34;label\u0026#34;) self.scrollArea.setWidget(self.label) 為何不使用 self.scrollAreaWidgetContents？\n目前測試的結果是不會成功的顯示出捲軸，可能的原因是因為 Qlabel 才有存在超過視窗範圍的大小，而 self.scrollAreaWidgetContents 作為容器，並沒有辦法以超過的大小觸發 self.scrollArea 的捲軸事件，因此功能失效。\n不過這部份原因目前只是我的猜測，總之捲軸的功能是無法正常運行的。\n從 UI.py 中找出物件名稱 這次除了 day12 既有的功能之外，我們新增了一些物件，\nself.btn_zoom_in、self.btn_zoom_out：同 day12 的 zoom in, zoom out 的按鈕 self.label：顯示圖片的 Qlabe self.scrollArea：圖片縮放的範圍 self.img_shape：作為 UI 優化新增的 label，我們可以從這裡觀察目前圖片的解析度。 取得名稱後，去修改 controller.py 我們繼續修改我們 day12 的程式碼\nfrom PyQt5 import QtCore, QtWidgets from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QFileDialog import cv2 from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.img_path = \u0026#39;cat.jpg\u0026#39; self.ui.btn_zoom_in.clicked.connect(self.func_zoom_in) self.ui.btn_zoom_out.clicked.connect(self.func_zoom_out) self.ui.scrollArea.setWidgetResizable(True) self.ui.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) # self.ui.label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # 將圖片置中 self.display_img() def display_img(self): self.img = cv2.imread(self.img_path) height, width, channel = self.img.shape bytesPerline = 3 * width self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(self.qimg) self.qpixmap_height = self.qpixmap.height() self.ui.label.setPixmap(QPixmap.fromImage(self.qimg)) def func_zoom_in(self): self.qpixmap_height -= 100 self.img_resize() def func_zoom_out(self): self.qpixmap_height += 100 self.img_resize() def img_resize(self): scaled_pixmap = self.qpixmap.scaledToHeight(self.qpixmap_height) print(f\u0026#34;current img shape = ({scaled_pixmap.width()}, {scaled_pixmap.height()})\u0026#34;) self.ui.img_shape.setText(f\u0026#34;current img shape = ({scaled_pixmap.width()}, {scaled_pixmap.height()})\u0026#34;) self.ui.label.setPixmap(scaled_pixmap) setup_control() 修改的部份 與 day12 的不同是，我們主要新增了這兩行\nself.ui.scrollArea.setWidgetResizable(True) self.ui.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) # self.ui.label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # 將圖片置中 self.ui.scrollArea.setWidgetResizable(True)：這行在 Qtdesigner 中也可以設定，預設是 False，我們將他改為 True，讓我們的 scrollArea 可以被捲動 self.ui.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)：將我們的圖片往左上角對齊，往左上角對齊有兩個好處，一個是我們之後如果要進行圖像處理，這樣算座標會非常方便。 但是如果為了好看，想讓圖片置中，可以改為以下敘述：\nself.ui.label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) img_resize() 的部份 (原 day12 resize_image()) 因為我們新增了 UI 優化的功能，稍微想一下就可以知道，\n這段程式碼基本上會跟著我們圖片變化一起改變，\n因此我們把「顯示圖片現在解析度」的功能新增在此處。\nprint(f\u0026quot;current img shape = ({scaled_pixmap.width()}, {scaled_pixmap.height()})\u0026quot;)：取得現在圖片高度、寬度並顯示在 terminal 當中 self.ui.img_shape.setText(f\u0026quot;current img shape = ({scaled_pixmap.width()}, {scaled_pixmap.height()})\u0026quot;)：取得現在圖片高度、寬度並顯示在 Qlabel 當中 執行結果 照我們 day5 的程式架構，我們執行\npython start.py Reference Scrollable QLabel image in PyQt5 ","date":"2021-08-27T17:38:59+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-13-2.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-13/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 13 - 使用 QVBoxLayout, QscrollArea 製作出捲軸，以高解析度檢視圖片 (基於 QImage 使用 OpenCV) PyQt5 scrollable image"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 zoom in zoom out 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day12_img_resize\n以 Qlabel 在 PyQt 中顯示圖片 這篇是延續 Day 11 顯示圖片的後續開發，\n在 day11 我們只是單純的顯示圖片，\n但有碰到了「圖片解析度太大，無法完全顯示」的問題，\n今天我們就要加入「zoom in」、「zoom out」的功能，來改善這個問題。\nUI 設計部份 (UI.py) 以 Qlabel 作為圖片顯示 我們與之前一樣新增一個 Qlabel，文字可以先不用管他，\n這個 Qlabel 是我們等等要作為顯示圖片使用，\n因此我們記得要去修改圖片大小，以免顯示的範圍太小。\n另外我們新增兩個按鈕，作為等等縮放圖片使用。 注意上圖中的圖片大小，另外變數名稱也建議修改。\n讀者們可以開始自行設計自己的介面囉，以上為我的示範。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day12.ui -\u0026gt; UI.py pyuic5 -x day12.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這樣我們的介面就大致出來囉！\ncontroller 設計部份 (controller.py) 從 UI.py 中找出物件名稱 這次我們有三個物件\n分別是 zoom in, zoom out 的按鈕：self.btn_zoom_in、self.btn_zoom_out 顯示圖片的 Qlabel：self.label 取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtCore, QtWidgets from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QFileDialog import cv2 from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.img_path = \u0026#39;cat.jpg\u0026#39; self.ui.btn_zoom_in.clicked.connect(self.func_zoom_in) self.ui.btn_zoom_out.clicked.connect(self.func_zoom_out) self.display_img() def display_img(self): self.img = cv2.imread(self.img_path) height, width, channel = self.img.shape bytesPerline = 3 * width self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(self.qimg) self.qpixmap_height = self.qpixmap.height() self.ui.label.setPixmap(QPixmap.fromImage(self.qimg)) def func_zoom_in(self): self.qpixmap_height -= 100 self.resize_image() def func_zoom_out(self): self.qpixmap_height += 100 self.resize_image() def resize_image(self): scaled_pixmap = self.qpixmap.scaledToHeight(self.qpixmap_height) self.ui.label.setPixmap(scaled_pixmap) setup_control() 修改的部份 「self.img_path = \u0026lsquo;cat.jpg\u0026rsquo;」：要顯示圖片的路徑 「self.ui.btn_zoom_in.clicked.connect(self.func_zoom_in)」：使按鍵連結 zoom in 的功能 「self.ui.btn_zoom_out.clicked.connect(self.func_zoom_out)」：使按鍵連結 zoom out 的功能 「self.display_img()」：等等會去 call 我們寫好的顯示圖片的 function display_img() 的部份 相關的功能我們在 day11，\n有介紹的很詳細了，這邊只說我們有修改的部份\n會修改的原因主要是我們新增了 zoom in 的功能，\n因此我們顯示圖片函式需要調整成可以隨時變動的 (不寫死、保有彈性，可重複使用)\n我們把原先的寫的內容拆解，\n特別其中的這四行：\nself.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.qpixmap = QPixmap.fromImage(self.qimg) self.qpixmap_height = self.qpixmap.height() self.ui.label.setPixmap(QPixmap.fromImage(self.qimg)) 第一行一樣我們透過 OpenCV 的方式，轉換成我們要的 Qimage\n第二行我們改成獨立從 Qimage 提取出 QPixmap，\n這邊我們可以理解為\nQImage 是實際上圖片的內容 QPixmap 是我們最終用來顯示再畫面上的內容 (可能為了顯示有縮放調整)，但不會更改到原圖 我們在第三行取得 QPixmap 的高度，作為我們等等縮放使用\n最後第一行一樣就是透過 label 顯示出來。\nfunc_zoom_in()、func_zoom_out() 的部份 這邊我們是去調整「我們想要呈現的」QPixmap 高度，\n在接下來的 resize_image() ，讓他依據這個高度自動比例縮放。\nresize_image() 的部份 scaled_pixmap = self.qpixmap.scaledToHeight(self.qpixmap_height) self.ui.label.setPixmap(scaled_pixmap) 這邊的第一行就是讓圖片 (Qpixmap) 自己去適應我們要的圖片高度，做出對應的圖片縮放。\n而第二行就是把縮放後的圖片 (Qpixmap) 塞進去我們的 label 當中。\n注意：我們從頭到尾都只有更改到 Qpixmap 而沒有改動到 Qimg，\n這也表示我們的原圖是一直有被保存下來的。\n我們所做的都只是「顯示上的更動」。\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py zoom in 的功能 zoom out 的功能 Reference QPixmap Class How to zoom a picture implemented with QPixmap on pyqt5 ","date":"2021-08-27T16:01:10+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-12-2-1024x600.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-12/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 12 - 建立一個可以縮放圖片大小的顯示器 (基於 QImage 使用 OpenCV)"},{"categories":["198 - Python 問題解決"],"content":"問題描述 當我們撰寫 pyqt5 程式時，出現以下 error 的解決辦法\nQObject::moveToThread: Current thread (0x55e28fa548d0) is not the object\u0026#39;s thread (0x55e28fceb860). Cannot move to target thread (0x55e28fa548d0) 記憶體位置可能不同，但解法大致是相同的。\n解法 個人試過有成功的方法 簡單來說，這是 OpenCV 版本過新導致的環境問題，\n網路上普遍的解法有看到\n- (個人有成功的方法) 使用 conda 安裝 pyqt，不要使用 pip install 安裝 建議先用 anaconda 建立一個乾淨的環境再來做這件事，\n建立好乾淨的環境後，安裝 conda 版本的 pyqt，\n再使用 pip 安裝 OpenCV\nconda install pyqt pip install opencv-python 其他網友的解法 (個人沒試過 or 試過失敗) - 使用 pip 降 OpenCV 的版本 (4.3.0.36) pip uninstall opencv-python pip install opencv-python==4.3.0.36 pip list | grep opencv-python - 移除 pip OpenCV 改用 apt-get 安裝 OpenCV sudo pip uninstall opencv-python sudo apt-get install libopencv-dev python-opencv Reference How to fix the error “QObject::moveToThread:” in opencv in python? ","date":"2021-08-26T10:03:43+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/yqt5-qobjec-tmovetothread/","tags":["Python"],"title":"【PyQt5】問題解決：QObject::moveToThread: Current thread (0x...) is not the object's thread (0x...). Cannot move to target thread (0x...)"},{"categories":["198 - Python 問題解決"],"content":"問題描述 當我們撰寫 pyqt5 程式時，出現以下 error 的解決辦法\nNameError: name \u0026#39;QImage\u0026#39; is not defined 解法 Qimage 位於 PyQt5.QtGui 底下，\n所以需要\nfrom PyQt5.QtGui import QImage 而我在實作時只有\nfrom PyQt5 import QtGui 會有 call 不到的情形\n總結 \u0026amp; 範例 改成以下寫法即可\nfrom PyQt5 import QtWidgets, QtCore from PyQt5.QtGui import QImage, QPixmap Reference NameError: name \u0026lsquo;QImage\u0026rsquo; is not defined [closed] ","date":"2021-08-26T09:49:00+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/pyqt5-nameerror-qimage/","tags":["Python"],"title":"【PyQt5】問題解決：NameError: name 'QImage' is not defined"},{"categories":["899 - Debug / Error"],"content":"問題描述 當我們想要使用 git 時，出現以下類似訊息 (日期可能不同) 的解決方法。\nremote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information. 問題成因 原因是 github 從 August 13, 2021 之後，\n對於 git 相關的存取已不再支援 password，須改用token方式進行。\n可參考 github 的 blog\nToken authentication requirements for Git operations 2021/8/27 更新：新的解法 舊的解法並不能一次就治好，必須還要每次都重新產生 token，\n這次為了安全性問題，反而造成了一些額外的麻煩啊\u0026hellip;\n因此現在找到了一個新的方法，可以直接避免後續需要重新產生 token 的問題。\n我們前面的步驟一樣要像舊的解法去產生 token，\n差別在最後一步，我們不把 token 當作密碼輸入，\n我們新增一個 remote，包含著我們的 token，\n雖然快速，但快的代價相對的就是容易有資安問題，請自行注意。\n新增 git remote\ngit remote set-url origin https://\u0026lt;githubtoken\u0026gt;@github.com/\u0026lt;username\u0026gt;/\u0026lt;repositoryname\u0026gt;.git 範例：\ngit remote set-url origin https://ghp_XXXXXXXXXXXXXXXXXXX@github.com/howarder3/test_repo.git 解法引用自：\nSupport for password authentication was removed. Please use a personal access token instead 舊的解法 這個算是 git 為了安全性所新增的功能，\n讓我們的 git 過一段時間之後憑證就會自動過期\n(也就是說，隔一段時間就會沒辦法直接與遠端同步)\n我們可以去 github 的頁面進行相關設定\nstep 1. 去個人頁面的 Settings step 2. 找到左側的 Developer settings step 3. 點選 Personal access tokens step 4. 選擇 Generate new token step 5. 設定 token 過期的時間、允許的範圍 step 6. 得到對應的 token 上面 step 5 後，我們會得到一串類似以下的 token\nghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 之後我們回到我們的 terminal，\n這次我們一樣輸入帳號，\n並將「密碼改為上面那串 token」，問題即可解決。\nReference Support for password authentication was removed. Please use a personal access token instead Token authentication requirements for Git operations ","date":"2021-08-26T00:53:51+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/git-error-5.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/other/debug-error/git-remote-support/","tags":["Debug","Error"],"title":"【Git】問題解決：remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. remote: Please see https://github.blog/20XX-XX-XX-token-authentication-requirements-for-git-operations/ for more information."},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day11_display_image\n以 Qlabel 在 PyQt 中顯示圖片 我們在先前的文章中已經有提過，我們可以使用 Qlabel 作為顯示文字，\n同樣的，我們也可以使用 Qlabel 來顯示圖片。\n今天我們就要來實作這個功能。\nUI 設計部份 (UI.py) 先修改主程式 window 大小 我們先點擊 QMainWindow 的部份，\n展開 geometry，這裡修改 Width, Height 可以直接改變整個視窗的大小。\n建議修改要大一點，因為要顯示的圖片可能也會很大。\n新增 Qlabel 作為圖片顯示 我們使用與之前一樣的方式新增一個 Qlabel，文字可以先不用管他，\n但我們需要先改變 Qlabel 所佔的範圍大小，\n展開 geometry，這裡修改 Width, Height 可以直接改變 Qlabel 顯示視窗的大小。\n而上面的 X, Y 可以個人需求修改，X, Y 表示的是「相對 MainWindow」此 Label 開始顯示的位置。\n(從左上角開始算，往右X、往下Y)\n這邊有兩件事情要注意：\n- 修改後的 Qlabel geometry 必須小於主程式的 QMainWindow (畫面比視窗大不合理吧XD) - 如果 Qlabel geometry 設定的解析度不夠大，有可能會只有只顯示部份圖片的情形 (從左上開始自動剪裁) 讀者們可以開始自行設計自己的介面囉，以上為我的示範。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day11.ui -\u0026gt; UI.py pyuic5 -x day11.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 這邊可以看到很單純的只有一段被初始化的文字，\n接下來我們要開始去改變裡面的內容。\ncontroller 設計部份 (controller.py) 從 UI.py 中找出物件名稱 這次我們只有一個物件 self.label\n取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtWidgets, QtCore from PyQt5.QtGui import QImage, QPixmap import cv2 from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.img_path = \u0026#39;cat_small.jpg\u0026#39; self.display_img() def display_img(self): self.img = cv2.imread(self.img_path) height, width, channel = self.img.shape bytesPerline = 3 * width self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped() self.ui.label.setPixmap(QPixmap.fromImage(self.qimg)) import 新增的部份 「import cv2」：我們為了要顯示圖片，會使用到 OpenCV 的 function 「from PyQt5.QtGui import QImage, QPixmap」：為了要顯示圖片，會使用到 PyQt5.QtGui 中的 QImage, QPixmap 這兩個物件的定義 setup_control() 修改的部份 「self.img_path = \u0026lsquo;cat.jpg\u0026rsquo;」：要顯示圖片的路徑 「self.display_img()」：等等會去 call 我們寫好的顯示圖片的 function display_img() 的部份 「self.img = cv2.imread(self.img_path)」：OpenCV 經典的讀圖 function，之前有介紹過，應該不用我們再多說 「height, width, channel = self.img.shape」：讀取圖片的 shape 與 channel，等等設定參數會用到 「bytesPerline = 3 * width」：設定「每一行」的影像佔用位置數量，目前因為有 3 個 channel，因此是 3 * width (如果有透明度可能就是 4 個 channel) 「self.qimg = QImage(self.img, width, height, bytesPerline, QImage.Format_RGB888).rgbSwapped()」：將轉成 OpenCV (numpy) 的格式圖片轉換成 QImage 的格式，並輸入圖片對應的長寬，每一行佔用的 bytes 數，而 QImage.Format_RGB888 代表的是 RGB 3 個 channel，「.rgbSwapped()」 表示將R, G, B 3 個 channel 轉換成 B, G, R (因為 OpenCV 是以 BGR 的方式儲存圖片，需要先進行轉換) 「self.ui.label.setPixmap(QPixmap.fromImage(self.qimg))」：將圖片在 label 中顯示 感謝網友 JSON 幫忙糾正錯誤！\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py 正常顯示圖片的情形 碰到解析度太大，導致我們設定的 Qlabel 無法完全顯示的情形 就會像下圖這樣，因為圖片太大了，800*600 只能顯示部份圖片\nReference 【QT】QImage 操作方式 【Qt】QImage使用总结 NameError: name \u0026lsquo;QImage\u0026rsquo; is not defined [closed] PyQt5 教學 \u0026ndash; 加入圖片 QPixmap [Python+OpenCV] 使用 PyQt5 顯示影像圖片 ","date":"2021-08-25T23:47:05+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-10-4.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-11/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 11 - 以 Qlabel 在 PyQt 中顯示圖片 (基於 QImage 使用 OpenCV)"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day10_open_file\n以 QFileDialog 讀取系統的檔案、資料夾 我們可以使用 QFileDialog 來開啟每個作業系統的檔案功能，\n這部分我們會在 controller.py 當中實作。\nUI 設計部份 (UI.py) 這次我們設計兩個按鈕，與對應的顯示檔案路徑的地方，\n這次我們使用 QTextEdit 來幫助我們顯示路徑\n(相對 Qlabel 的好處是，如果文字太長的話，會自動換行)\n一樣記得改一下變數，不然等等容易搞不清楚誰是誰。\n而今天的 UI 設計部份就比較簡單，\n因為等等的檔案處理我們會花比較多一點時間來實作。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day10.ui -\u0026gt; UI.py pyuic5 -x day10.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 接下來我們要開始去改變裡面的內容。\ncontroller 設計部份 (controller.py) 設定開啟 檔案/資料夾 的功能 接下來我們想藉由按鍵來觸發開啟 檔案/資料夾 的功能，\n我們呼叫 QFileDialog 來協助完成這個功能，\n在這個功能的底層，已經有針對不同作業系統實作對應開啟檔案的功能。\n請不要只看表面誤會以為 Windows, Mac, Linux 開啟檔案都是同一種實作方式\n實際上這部份依據不同作業系統的實作是在 Qt 底層已經做掉了，我們才能如此方便。\n先從 UI.py 取得變數名稱 (也是我們剛剛在 Qt designer 中命名的部份) 這次我們的變數有\n兩個按鈕\nself.file_button self.folder_button 兩個顯示路徑用方框 (QTextEdit)\nself.show_file_path self.show_folder_path 取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtCore, QtWidgets from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QFileDialog import cv2 from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): self.ui.file_button.clicked.connect(self.open_file) self.ui.folder_button.clicked.connect(self.open_folder) def open_file(self): filename, filetype = QFileDialog.getOpenFileName(self, \u0026#34;Open file\u0026#34;, \u0026#34;./\u0026#34;) # start path print(filename, filetype) self.ui.show_file_path.setText(filename) def open_folder(self): folder_path = QFileDialog.getExistingDirectory(self, \u0026#34;Open folder\u0026#34;, \u0026#34;./\u0026#34;) # start path print(folder_path) self.ui.show_folder_path.setText(folder_path) setup_control() 修改的部份 「self.ui.file_button.clicked.connect(self.open_file) 」、「self.ui.folder_button.clicked.connect(self.open_folder)」：我們分別設定對應要使用的函數名稱，等等會去 call 我們寫好開啟 檔案/資料夾 的 function open_file() 的部份 (開啟檔案) filename, filetype = QFileDialog.getOpenFileName(self, \u0026ldquo;Open file\u0026rdquo;, \u0026ldquo;./\u0026rdquo;) 我們可以直接使用 QFileDialog.getOpenFileName 這個已經設定好的函式，\n直接幫助我們完成開啟檔案的功能，\n而不同作業系統的問題，在這個功能的底層已經幫我們處理掉了，我們可以直接使用。\n\u0026ldquo;Open file\u0026rdquo; 是開始視窗後上方標題列的名稱，\n\u0026ldquo;./\u0026rdquo; 代表從哪裡開啟這個目錄，「\u0026quot;./\u0026quot;」就是當前目錄\n這裡會回傳兩個參數(tuple) filename, filetype，我們要使用的是第一個路徑，\n而第二個是檔案的副檔名 (與我們有沒有過濾檔案副檔名有關，因為不是我主要使用的功能，這邊先不細部討論)\nself.ui.show_file_path.setText(filename) 取得開啟後的「檔案路徑」，並顯示在畫面上\nopen_folder() 的部份 (開啟檔案) folder_path = QFileDialog.getExistingDirectory(self, \u0026ldquo;Open folder\u0026rdquo;, \u0026ldquo;./\u0026rdquo;) 我們可以直接使用 QFileDialog.getExistingDirectory 這個已經設定好的函式完成開啟資料夾的功能，\n一樣不同作業系統的問題，在這個功能的底層已經幫我們處理掉了，我們可以直接使用。\n\u0026ldquo;Open folder\u0026rdquo; 是開始視窗後上方標題列的名稱，\n\u0026ldquo;./\u0026rdquo; 代表從哪裡開啟這個目錄，「\u0026quot;./\u0026quot;」就是當前目錄\nself.ui.show_folder_path.setText(folder_path) 取得開啟後的「資料夾路徑」，並顯示在畫面上\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py 開啟檔案的視窗 將結果呈現在畫面上 Reference ","date":"2021-08-25T17:02:58+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-11-2.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-10/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 10 - 以 QFileDialog 讀取系統的檔案、資料夾"},{"categories":["198 - Python 問題解決"],"content":"問題描述 當我們撰寫 pyqt5 程式時，出現以下 error 的解決辦法\nAttributeError: \u0026#39;QTextEdit\u0026#39; object has no attribute \u0026#39;text\u0026#39; 或是\nAttributeError: \u0026#39;QTextEdit\u0026#39; object has no attribute \u0026#39;PlainText\u0026#39; 解法 這裡要注意 QLineEdit, QTextEdit, QPlainTextEdit 使用函數的不同\n如果想要讀取 QLineEdit 內的文字，\n我們使用的是 「.text()」 的方式呼叫。\n而如果是想要讀取 QTextEdit, QPlainTextEdit 內的文字，\n我們使用的是 「.toPlainText()」 的方式呼叫。\n所以如果出現 「AttributeError: \u0026lsquo;QTextEdit\u0026rsquo; object has no attribute \u0026rsquo;text\u0026rsquo;」\n就是上述的這個問題囉！\nReference python - pyqt4 : AttributeError: \u0026lsquo;QPlainTextEdit\u0026rsquo; object has no attribute \u0026rsquo;text\u0026rsquo; ","date":"2021-08-25T15:30:31+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/pyqt5-qtextedit-no-text/","tags":["Python"],"title":"【PyQt5】問題解決：AttributeError: 'QTextEdit' object has no attribute 'text', 'PlainText'"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day09_input_text_2\n介紹 QLineEdit, QTextEdit, QPlainTextEdit 我們在 day8 中已經有介紹過 QLineEdit 的使用方法，\n與 QLineEdit 類似的還有 QTextEdit, QPlainTextEdit，\n今日我們一併介紹。\nQLineEdit：特色是單行的文字輸入，通常會使用於帳號、密碼或 email 等等，大概只需要一行文字輸入的地方。 QTextEdit：特色是多行的文字輸入。 QPlainTextEdit：特色也是多行的文字輸入，基本上與 QTextEdit 功能相同。 我們一般使用上會使用 QTextEdit 作為文字顯示\n使用 QPlainTextEdit 來進行文字 input 處理\nUI 設計部份 (UI.py) Qt designer 部份 一樣先打開我們的 Qt designer，\n這次我們建立三行input、按鈕、與顯示文字，\n分別對應各自的結果。\n我們在 Input widgets 中找到 LineEdit, TextEdit, PlainTextEdit，\n拖曳至視窗自己喜歡的地方，\n我們今天想實作一個小功能，是昨天 day 8 的延伸練習，\n讓我們「輸入」的文字，可以藉由「點擊按鈕」，「顯示」在畫面的某處。\n因此我們今天總共需要3*3個物件\nQLineEdit, QTextEdit, QPlainTextEdit：作為讓我們輸入文字的地方 QPushButton*3：作為按鈕，可以點擊執行任務 Qlabel*3：作為顯示結果用 讀者們可以開始自行設計自己的介面囉，以下為我的示範：\n這邊我極度建議要修改物件的名稱 (上圖右上角紅框處) ，等等我們撰寫 controller.py 功能的時候，才不會要在那邊認說 label_1、label_2、label_3 誰是誰。\n另外我們這邊直接在介面上修改了預設的文字，(點兩下即可直接編輯)，\n其實這與前幾天我們在 controller.py 的作法也是相同的，只是這次我們把 setText 的功能搬至 UI.py 中，讓 UI.py 直接完成介面 initialize 的工作。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day9.ui -\u0026gt; UI.py pyuic5 -x day9.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 我們稍微閱讀一下轉換出來的 UI.py 的程式碼，\n我們就會發現與前幾天不同的地方，\n也就是我們在 UI.py 完成的初始化部份。\n物件名稱更改部份 稍微注意一下就會發現，\n我們剛剛在 Qtdesigner 修改的內容，直接反應在 class 的成員名稱上，\n另外 setObjectName 也同步被改動了，總共有這兩個變化。\n初始化內容部份 這部份我們在 UI.py 中發現了多了 retranslateUi() 這個 function，\n裡面有一連串的名稱變換，完成我們剛剛設定的文字初始化。\ncontroller 設計部份 (controller.py) 接下來，我們必須先研究一下我們剛剛的程式，\n把我們剛剛程式中的「物件名稱」找出來。\n從 UI.py 中找出物件名稱 畢竟我們剛剛已經先改過了變數名稱，這邊就方便了！\n我們這邊先把變數名稱列出來，方便我們之後設定。\nQLineEdit 部份 輸入：self.box_line 按鈕：self.button_line 顯示：self.label_line (上面的圖片我是我修改前的結果，修改後應該會是這個名稱) QTextEdit 部份 輸入：self.box_text 按鈕：self.button_text 顯示：self.label_text QPlainTextEdit 部份 輸入：self.box_plain 按鈕：self.button_plain 顯示：self.label_plain (務必記得名稱，不然等等不知道要改誰XD)\n取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.ui.button_line.clicked.connect(self.buttonClicked_line) self.ui.button_text.clicked.connect(self.buttonClicked_text) self.ui.button_plain.clicked.connect(self.buttonClicked_plain) def buttonClicked_line(self): msg = self.ui.box_line.text() self.ui.label_line.setText(msg) def buttonClicked_text(self): msg = self.ui.box_text.toPlainText() self.ui.label_text.setText(msg) def buttonClicked_plain(self): msg = self.ui.box_plain.toPlainText() self.ui.label_plain.setText(msg) 我們修改了 setup_control()，使按鈕能夠連接對應的 function，\n並新增了buttonClicked_line(), buttonClicked_text(), buttonClicked_plain() 的部份，完成我們按鈕的功能。\n【注意】 QLineEdit, QTextEdit, QPlainTextEdit 使用函數的不同 如果想要讀取 QLineEdit 內的文字，\n我們使用的是 「.text()」 的方式呼叫。\n而如果是想要讀取 QTextEdit, QPlainTextEdit 內的文字，\n我們使用的是 「.toPlainText()」 的方式呼叫。\n所以如果出現 「AttributeError: \u0026lsquo;QTextEdit\u0026rsquo; object has no attribute \u0026rsquo;text\u0026rsquo;」\n就是上述的這個問題囉！\n於是我們就完成我們今天的功能囉！\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py Reference QT中LineEdit TextEdit PlainTextEdit 这三个控件有什么区别，分别用在什么情况下呢 ","date":"2021-08-25T15:23:03+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-9-7.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-9/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 9 - 以 QLineEdit, QTextEdit, QPlainTextEdit 作為文字的輸入"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day08_input_text\nUI 設計部份 (UI.py) Qt designer 部份 一樣先打開我們的 Qt designer，\n這篇我們要介紹的是 QLineEdit，\n作為我們第二個「input」用的功能。\n我們在 Input widgets 中找到 Line Edit，\n拖曳至視窗自己喜歡的地方，\n另外我們今天想實作一個小功能，結合昨天與前天的 day 6、day 7，\n讓我們「輸入」的文字，可以藉由「點擊按鈕」，「顯示」在畫面的某處。\n因此我們今天總共需要3個物件\nQLineEdit：作為讓我們輸入文字的地方 QPushButtonl：作為按鈕，可以點擊執行任務 Qlabel：作為顯示結果用 讀者們可以開始自行設計自己的介面囉，以下為我的示範：\n一樣，我們可以修改物件的名稱 (上圖紅框處) ，使我們在未來更容易識別是哪一個功能。這邊作為示範就不改動了。\n文字大小那些也都可以自己改。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day8.ui -\u0026gt; UI.py pyuic5 -x day8.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 看起來是不是很可愛(?)呢，有了介面，\n接下來我們要開始實作功能面的事情。\ncontroller 設計部份 (controller.py) 接下來，我們必須先研究一下我們剛剛的程式，\n把我們剛剛程式中的「物件名稱」找出來。\n名稱也可以自己在設計 Qt desginer 時設定，可以看上一個 part 我有框起來的部份。\n從 UI.py 中找出物件名稱 如果讀者使用的是預設的名稱，應該會在剛轉換完成的 UI.py 中 找到以下這段，\n這次我們有了3個東西囉！等等我們要讓他們能互動起來\n(為了示範，名稱我都是用預設的沒有改。)\nself.pushButton：按鈕 self.lineEdit：輸入文字 self.label：輸出畫面 (務必記得名稱，不然等等不知道要改誰XD)\n取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO # qpushbutton doc: https://doc.qt.io/qt-5/qpushbutton.html self.ui.pushButton.setText(\u0026#39;Print message!\u0026#39;) self.ui.pushButton.clicked.connect(self.buttonClicked) def buttonClicked(self): msg = self.ui.lineEdit.text() self.ui.label.setText(msg) 我們在新增或修改了 setup_control(), buttonClicked() 這兩個部份\n在 setup_control() 中 self.ui.pushButton.setText(\u0026lsquo;Print message!\u0026rsquo;)：設定按鍵顯示訊息 [重要] self.ui.pushButton.clicked.connect(self.buttonClicked)：當按鍵被觸發時，要做 self.buttonClicked() 這個 function 裡面的事情。 在 buttonClicked() 中 這裡我們要定義的是按鈕被按下時，會執行的事件內容，\n我們首先要讀取使用者輸入的內容，\nmsg = self.ui.lineEdit.text()：將 lineEdit 的內容儲存至 msg 中 self.ui.label.setText(msg)：透過 label 顯示 msg 的內容 這樣就完成了我們今日想實現的功能！\n我們現在可以透過輸入一些內容，並點擊按鍵，\n就可以讓結果顯示在畫面上！\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py 每當我們按一次按鍵，框框內顯示的文字就會同步顯示在下方。\n","date":"2021-08-24T18:55:17+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-8-6.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-8/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 8 - 我們的第二個 input 手段 - QLineEdit"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day07_input_button\nUI 設計部份 (UI.py) Qt designer 部份 一樣先打開我們的 Qt designer，\n這篇我們要介紹的是 QPushButton，\n作為我們第一個「input」用的功能。\n這邊講的「input」，是廣義的「input」，也就是任何輸入進我們程式的訊號，\n反之，「output」，就是指任何從我們程式輸出的訊號\n我們在 Buttons 中找到 Push Button，\n拖曳至視窗自己喜歡的地方，就完成今天的設計囉！\n一樣，我們可以修改物件的名稱，使我們在未來更容易是哪一個功能。\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day7.ui -\u0026gt; UI.py pyuic5 -x day7.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 看起來符合我們的想像就沒問題了。\ncontroller 設計部份 (controller.py) 接下來，我們必須先研究一下我們剛剛的程式，\n把我們剛剛程式中的「物件名稱」找出來。\n名稱也可以自己在設計 Qt desginer 時設定，可以看上一個 part 我有框起來的部份。\n從 UI.py 中找出物件名稱 如果讀者使用的是預設的名稱，應該會在剛轉換完成的 UI.py 中 找到以下這段，\n我們發現我們剛剛的 Push Button 名稱為 pushButton，\n(注意大小寫，這可能導致程式錯誤。)\n當然，這絕對不是一個好名稱，不過這邊作為示範，我們就不多做改動。\n取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO # qpushbutton doc: https://doc.qt.io/qt-5/qpushbutton.html self.ui.pushButton.setText(\u0026#39;Print message!\u0026#39;) self.clicked_counter = 0 self.ui.pushButton.clicked.connect(self.buttonClicked) def buttonClicked(self): self.clicked_counter += 1 print(f\u0026#34;You clicked {self.clicked_counter} times.\u0026#34;) 我們在新增或修改了 setup_control(), buttonClicked() 這兩個部份\n在 setup_control() 中 self.ui.pushButton.setText(\u0026lsquo;Print message!\u0026rsquo;)：將按鈕修改為 \u0026ldquo;Print message!\u0026rdquo; self.clicked_counter = 0，設定一個儲存我們現在按了按鈕幾次的變數 [重要] self.ui.pushButton.clicked.connect(self.buttonClicked)，連結 self.buttonClicked() 這個 function，使當按鈕被按下時，能夠執行 function 裡面的功能。 在 buttonClicked() 中 這裡我們要定義的是按鈕被按下時，會執行的事件內容，\n這裡作為示範，我們寫的很簡單，\n用 self.clicked_counter += 1，更新點擊的次數，\n並用 print(f\u0026quot;You clicked {self.clicked_counter} times.\u0026quot;)，\n在 terminal 上印出現在點擊的次數。\n執行結果 照我們 day5 的程式架構，我們執行\npython start.py 結果會是，當我們每按一次按鈕，\nterminal 就會多顯示一行，並告訴我們現在點擊的次數。\n","date":"2021-08-24T17:11:37+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-7-4.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-7/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 7 - 我們的第一個 input 手段 - QPushButton"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 前言 我們接下來的討論，會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計\n如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)\n建議先閱讀 day5 文章後再來閱讀此文。\nhttps://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day06_output_text\nUI 設計部份 (UI.py) Qt designer 部份 一樣先打開我們的 Qt designer，\n這篇我們要介紹的是 Qlabel，\n作為我們第一個「output」用的功能。\n我們在 Display Widgets 中找到 Label，\n拖曳至視窗自己喜歡的地方，就完成今天的設計囉！\n轉換成 UI.py 一樣的編譯指令，我們加上 -x (也可不加)，\n我們就可以先檢視看看轉換後的視窗是不是跟我們想像的一樣。\n轉換 day6.ui -\u0026gt; UI.py pyuic5 -x day6.ui -o UI.py 執行看看 UI.py 畫面是否如同我們想像 一樣，這程式只有介面 (視覺上的呈現)，沒有任何互動功能\n看看我們製作出來的介面 python UI.py 看起來就是符合我們的想像了，這樣就沒問題了。\ncontroller 設計部份 (controller.py) 接下來，我們必須先研究一下我們剛剛的程式，\n把我們剛剛程式中的「物件名稱」找出來。\n名稱也可以自己在設計 Qt desginer 時設定，這邊作為快速示範就先不細講這一個部份。\n從 UI.py 中找出物件名稱 如果讀者使用的是預設的名稱，應該會在剛轉換完成的 UI.py 中 找到以下這段，\n我們發現我們剛剛的 label 名稱為 label，\n當然，程式寫久的都知道這絕對不是一個好名稱。\n我們晚點再來討論這個問題。\n取得名稱後，去修改 controller.py 還記得我們在 day5 中的模板嗎？這邊我們直接複製過來使用並修改。\nfrom PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.ui.label.setText(\u0026#39;Happy World!\u0026#39;) 我們在 setup_control() 中新增了這行，self.ui.label.setText(\u0026lsquo;Happy World!\u0026rsquo;)，\n意思是：\n「self.ui」(我們的 ui 介面)， 「.label」裡面就是我們剛剛說的 label 這個物件名稱， 「.setText」設定文字的方式，相關用法需要去查文件。 然後設定文字為我們要的 \u0026lsquo;Happy World!\u0026rsquo;。 就是這行的意思囉！\n有沒有發現，「.label」這個名稱，很容易讓人誤會以為是內建的某個功能？\n實際上他是我們自己新增的物件\u0026hellip;\n真的等等該換個名字給他，以免寫越多越混搞不清楚誰是誰。\n執行結果 如果照我們 day5 的程式架構，現在我們可以執行\npython start.py 就能看到結果了！\n哎呀！我們發現我們顯示的文字被切掉了一部份，\n看來我們還需要回去調整一下視窗大小。\n修改顯示視窗、文字大小、物件名稱\u0026hellip; 這邊我們再回來看我們的 Qt designer，\n我們開始來修改剛剛我們想改的「物件名稱、視窗大小\u0026hellip;」之類的東東。\n我們可以直接拖曳視窗，改變視窗的大小 (右邊可以看現在視窗的解析度) 右上角，我們可以看到現在所有視窗內的物件， 我們找到 class QLabel 的這個物件，並且修改 Object 中顯示的名稱 養成好的命名習慣，等到程式物件越來越多的時候，\n才會因為每個物件名稱都叫作 Label 搞不清楚誰是誰\n而下方我們同樣也可以修改文字的字體，與字體對應的大小 (字體的部份需要注意可能會有跨作業系統不支援字體的問題， 例如 windows 與 mac 不共通的字體) 稍微改了一下上述內容，以下是我的修改結果，讀者可以再自己嘗試。\n一樣，轉換成 UI.py，並修改 controller.py 對應的物件名稱，完成最終結果 controller.py from PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.ui.show_output.setText(\u0026#39;Happy World!\u0026#39;) 注意：我們同步修改了位於 setup_control() 中的物件名稱， 將 label 改為 show_output，使我們的物件名稱更清楚。 最終結果 ","date":"2021-08-24T10:48:37+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-6-7.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-6/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 6 - 我們的第一個 output 手段 - Qlabel"},{"categories":["810 - Arduino"],"content":"前言 這篇我們來介紹如何使用 Arduino 開發 I2C LCD 液晶顯示器，\n會使用「LCD 液晶顯示器」作為第一篇文章是因為顯示的功能，\n在往後的開發上我會經常用到。\n但不建議 Arduino 初學者一開始就學這個，\n建議還是先從最簡單的 LED 開始學習，\n這邊就是我留給自己的筆記。\n事前準備 這邊我使用的是：\nArduino Nano (比起 Arduino Uno 相對較小、輕便) Funduino I2C (IIC) 結合 LCD 液晶顯示器 這個 LCD 液晶顯示器並不是一般有很多 pin 腳的那種，\n而是另外接了一個 I2C (IIC) 晶片，\n購買時需要注意\n硬體部分 (接線部分) 不論是哪一牌的 I2C (IIC) 結合 LCD 液晶顯示器，\n我們都可以在後面看到四個腳位的敘述，如下圖。\n我們看到了\nGND，等等接上 Nano 的 GND VCC，等等接上 Nano 的 5V SDA，等等接上 Nano 的 SDA SCL，等等接上 Nano 的 SCL 圖片來源：google 我們可以看到 Nano 的腳位，SCL 位於 A5、SDA 位於 A4\n總和以上，我們接線的方式整理如下：\n接線方式 GND，等等接上 Nano 的 GND VCC，等等接上 Nano 的 5V SDA，等等接上 Nano 的 SDA (A4) SCL，等等接上 Nano 的 SCL (A5) 軟體部分 (程式部分) windows 作業系統 下載 I2C LCD 對應所需要的套件，並執行範例程式碼。 - 我們打開 Arduino，打開「草稿碼 -\u003e 匯入程式庫 -\u003e 管理程式庫」 - 搜尋「LCD I2C」，記得下載 I2C 的版本，不要下載錯了! (會是所有 pin腳位 設定的版本) 注意以下圖片套件名稱結尾的 \u0026ldquo;I2C\u0026rdquo;，我是下載作者為 \u0026ldquo;Macro Schwartz\u0026rdquo; 的版本 - 我們使用範例的程式碼，直接試看看結果 Arduino 有一個很貼心的地方，就是可以參考的範例很多，\n我們幾乎都可以在「檔案 -\u0026gt; 範例 -\u0026gt; 對應套件名稱」，找到範例的程式碼\n這邊我們選擇 \u0026ldquo;HelloWorld\u0026rdquo; 這支範例程式，讓我們快速能確認功能是否正常，\n(範例程式碼幾乎不會有錯誤，反而是網路上的一些程式碼不見得能直接順利執行。)\n照例來說應該就要能正常顯示結果了。\n結果圖示 以上面的範例來說，應該要能看到以下的結果。\n一些 Nano 的問題 務必檢查 「開發版、處理器(最重要)、序列埠」 是否都有設定正確!! 這邊以 Nano 版的設定舉例，\n開發版：選擇 Arduino Nano 處理器：選擇 ATmega328P (Old Bootloader)，務必選這個，才會支援編譯成功 序列埠：選擇讓 Arduino 連接上電腦 USB 的哪一個序列埠。 範例 檢查序列埠的方式 打開檔案總管，對「本機」按下右鍵，選擇「管理」，\n會看到以下畫面，找到「裝置管理員」，選擇「連接埠」，\n找到對應的 Nano 所插上的 USB插槽，\n這邊我的電腦顯示的名稱為「USB-SERIAL CH340 (COM3)」，(注意，不一定是這個名字)\n我們就可以知道我們在 Arduino 的設定那邊，也要將序列埠也設定為「COM3」。\n於是，我們回到 Arduino，找到序列埠的設定，也設定為「COM3」。\n到此，基本的設定 Arduino Nano 部分應該就沒問題囉！\nArduino UNO 的部分請不要參考此文照著設定，要注意的地方是一樣的，\n但編譯 (不用 Old Bootloader)，與對應開發板 (Arduino UNO版) 皆需要更改。\nReference Arduino 使用 1602 IIC（I2C） LCD 點陣液晶模組\n","date":"2021-08-16T02:55:19+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/arduino-lcd-7.png","permalink":"https://wongwongnotes.com/posts/cloud-iot/hardware/arduino/arduino-i2c-lcd/","tags":["Arduino"],"title":"【Arduino】1 - 使用 Arduino Nano 開發 I2C (IIC) LCD 液晶顯示器"},{"categories":["198 - Python 問題解決"],"content":"問題描述 我們使用 python 撰寫 OpenCV 程式的時候\n執行完 cv2.imshow 將圖片顯示出來後，\n使用內建的關閉視窗方法，卻導致「視窗沒有回應」、「視窗當掉」的問題。\n解法 OpenCV 在設計的時候，有 OpenCV 內建的關閉圖片的方式，\n雖然 windows、mac或linux都會有關閉視窗的「X」按鍵，\n但其實系統內建的關閉視窗方式並不一定會執行到 OpenCV一些清理一些系統參數的內容。\n所以這才導致有時候會一些 bug，例如都按下關閉視窗的「X」按鍵，\n圖片卻有無法順利關閉的現象、甚至造成「視窗沒有回應」、「視窗當掉」。\n這裡建議使用 OpenCV 內建的方式來處理圖片關閉的問題，免得會有其他的錯誤。\ncv2.waitKey(0) # 暫停等待按鍵，使 cv2.imshow 能顯示出畫面 cv2.destroyAllWindows() # 配合上一行，按下任意鍵則關閉所有視窗 (使用 OpenCV 內建的方法) ","date":"2021-08-15T02:05:28+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/cv2-imshow-error/","tags":["Python"],"title":"【OpenCV】問題解決：python 無法順利關閉 cv2.imshow 的視窗，出現視窗沒有回應、畫面當掉的現象"},{"categories":["198 - Python 問題解決"],"content":"問題描述 我們使用 python 撰寫 OpenCV 程式的時候\n執行完 cv2.imshow 圖片沒有顯示出來、圖片未顯示的解決方法。\n解法 可能原因有很多種，但這邊列出個人最常見的幾種。\n- 先看程式有沒有 error 如果有 error，建議就直接針對 error 去問 google，會有各自的解決方法。\n- 你可能沒有讓系統等待 這才是這篇文章主要想講的問題。\n因為 OpenCV 在設計的時候，執行完 cv2.imshow 是還需要等待才會將畫面停留的，\n所以你必須再加上以下兩行 (建議兩行都加，加第一行就會顯示、而第二行是關閉處理)。\ncv2.waitKey(0) # 暫停等待按鍵，使 cv2.imshow 能顯示出畫面 cv2.destroyAllWindows() # 配合上一行，按下任意鍵則關閉所有視窗 第二行的部分會建議使用 OpenCV 內建的方式來處理圖片關閉的問題，免得會有其他的錯誤。\n","date":"2021-08-15T01:15:31+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/opencv-imshow-noshow/","tags":["Python"],"title":"【OpenCV】問題解決：python 執行完 cv2.imshow 圖片沒有顯示、圖片未顯示"},{"categories":["170 - Python 平行加速"],"content":"前言 此為 multiprocessing 的程式模板v2，(相比 v2 簡化為一支程式)\n將任務封裝好後，直接丟入 list_tasks 即可自動發揮系統最大效能跑平行運算。\n封裝的概念，請務必先自行理解。\n我們通常在設計程式時，都會是預設以單一 process 去執行程式，\n但有時候會要重複執行多種任務。\n例：跑影片 x 100、分析結果 x 100、雜項任務 x 100\u0026hellip;\n會需要寫程式的任務，大部分都是需要被反覆執行的，我們正好利用這個特性\nmultiprocessing.Pool() 會依據系統現在的能力，自動分配任務，\n也就是說會讓你的電腦在“接近極限卻不當掉的”情況下，\n發揮最大多工的能力。\n範例程式碼 start_multiprocess.py 請在這邊將所有封裝好的任務存入 list_tasks，(用 list_tasks.append())\n例：list_tasks.append(CLASS_NAME(your_args, \u0026hellip;))\n封裝的任務必須至少包含以下兩個函數：\ninit(): 初始化變數 start_progress(): 讓自動化流程分配任務時，開始執行的入口 注意：請勿將 start_progress() 放在 init() 中執行，這樣在宣告階段程式就會先開始跑起來了！ 失去了自動分配資源的效果！\nimport os import cv2 import glob import multiprocessing as mp from multiprocessing import RLock from tqdm import tqdm from termcolor import colored list_tasks = [] # ------------- [YOU ONLY NEED TO SET HERE] ------------- # # * append your task in to list_tasks # -\u0026gt; list_tasks.append(CLASS_NAME(your_args, ...)) # # * your CLASS must have at least two functions # -\u0026gt; __init__() and start_progress() #---------------------------------------------------------# # put your task here #---------------------------------------------------------# #---------------------------------------------------------# #---------------------------------------------------------# #---------------------------------------------------------# #---------------------------------------------------------# class howard_print(object): @staticmethod def info(str): print(colored(f\u0026#34;[Info] \u0026#34;, \u0026#39;green\u0026#39;) + str) @staticmethod def warn(str): print(colored(f\u0026#34;[Warning] \u0026#34;, \u0026#39;yellow\u0026#39;) + str) @staticmethod def error(str): print(colored(f\u0026#34;[Error] \u0026#34;, \u0026#39;red\u0026#39;) + str) @staticmethod def finish(str): print(colored(f\u0026#34;[Finished] \u0026#34;, \u0026#39;cyan\u0026#39;) + str) @staticmethod def undefined(str): print(colored(f\u0026#34;[Undefined] \u0026#34;, \u0026#39;magenta\u0026#39;) + str) class multiprocess_task(object): def __init__(self, idx, total_job, each_task): os.system(\u0026#39;cls\u0026#39; if os.name == \u0026#39;nt\u0026#39; else \u0026#39;clear\u0026#39;) howard_print.info(f\u0026#39;Current working on job {idx}/{len(list_tasks)}.\u0026#39;) each_task.start_progress(total_job) howard_print.info(f\u0026#34;job {idx}/{len(list_tasks)} finished !!!\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: tqdm.set_lock(RLock()) # for managing output contention cpu_count = mp.cpu_count() print(f\u0026#34; --------------------- [start_multiprocess.py] --------------------- \u0026#34;) howard_print.info(f\u0026#34;Your CPU count (worker count): {cpu_count}\u0026#34;) pool = mp.Pool() res_list = [] howard_print.info(f\u0026#34;Total task count = {len(list_tasks)}\u0026#34;) howard_print.info(f\u0026#34;Task list = {list_tasks}\u0026#34;) for idx in range(len(list_tasks)): res_list.append(pool.apply_async(multiprocess_task, (idx, len(list_tasks), list_tasks[idx]))) for idx in range(len(res_list)): res_list[idx].get() ","date":"2021-08-11T14:41:18+08:00","permalink":"https://wongwongnotes.com/posts/python/networking/python/python-auto-job-v2/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #7】自動化平行任務程式模板 v2，讓系統發揮最大效能，自動分配資源平行運算"},{"categories":["198 - Python 問題解決"],"content":"問題描述 有時 python 執行到一半會莫名停止，\n此文為之前解 bug 最後找到的有效解法。\n解法 可能原因有很多種，但這邊只列出個人有成功的解法。\n簡單來說，我碰到的是在 call function 時回傳的 args 數量不對等的問題。\n例如：\ndef bug_func(): return 1,2,3,4,5 a,b,c,d = bug_func() 後來發現因為少一個變數而卡住，\n此方法不見得是各種卡住狀況的通用解，為檢查的方向。\n這種沒有直接跳 error 的 bug 最難搞了\u0026hellip;\u0026hellip;\n此外，依據 design pattern 的設計，\n當一個 function 如果有回傳 3 個以上的變數，\n會建議可以打包成一個 struct (dict)，\n再使用 dict[key]，會讓程式碼更容易被閱讀。\n建議修改方向： by design pattern def bug_func(): dict = {} dict[a] = 1 dict[b] = 2 dict[c] = 3 dict[d] = 4 dict[e] = 5 return dict result_dict = bug_func() # 之後再使用 dict[key] 去取用變數，記得 a,b,c,d,e 盡量是有意義的名字。 ","date":"2021-08-11T14:22:55+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/python-stop/","tags":["Python"],"title":"【Python】問題解決：python 執行到一半莫名卡住的可能原因"},{"categories":["922 - Mac / MacOS"],"content":"問題描述 此為當初購入 j5create - jcd831 安裝於 mac 上，驅動程式啟動失敗時，\n跳出以下的錯誤：\nt6 usb station the driver has not been matched to this device and usb interface 0 was not enumerated 解法 正確版本的 Mac OS 這是驅動程式安裝失敗導致的問題，我們首先要確認\n下載了最新版的驅動程式：特別是現在 Mac 的作業系統版本 (我這一開始有裝錯) Mac 的作業系統版本可於左上角 -\u0026gt; ”關於這台 Mac“ 確認\n像我這台的 Mac OS 是 11.6 我應該要下載正確的版本\n應該要去下載這個驅動程式\n權限開啟 權限有無開啟：系統偏好設定 -\u0026gt; 安全性與隱私權，請檢查有沒有把鎖頭打開，並允許確認螢幕控制 (這步驟我一開始也沒有把鎖頭打開並允許) ","date":"2021-08-09T16:50:50+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/%E6%88%AA%E5%9C%96-2021-09-30-%E4%B8%8B%E5%8D%882.14.44-1024x202.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/t6-usb-station/","tags":["macOS"],"title":"【Mac】問題解決：t6 usb station the driver has not been matched to this device and usb interface 0 was not enumerated"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 今天我們要延伸昨天的概念，開始來講解我們如何設計 controller.py\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day05_pyqt_template\n先複習一下 PyQt 的程式邏輯 細節我們在昨天已經有提過了，這個是我們設計「視覺化應用的精神」\n接下來我們會以「程式的角度」，來說明怎麼樣寫這樣的程式\n以「程式角度」，來說明如何建立 PyQt 的系統 我們設計完 UI.py 後，再來我們要設計 controller.py，\n我們可以先看下圖，先有個架構理解我們在做什麼：\n用「controller.py」去 import「UI.py」來使用， 用「start.py」去 import「controller.py」來使用， 之前在網路上看到的程式碼，很多都會將程式進入點也寫在這裡\n並將 controller.py 命名為 start.py 或 strat_UI.py 之類的\n這邊讀者只要有一隻程式會有包含「UI.py」、「controller.py」的概念，\n相信就能對網路上大部分的範例程式碼能夠進行初步的解讀，\n(之前我就是卡在 UI 與 control 的概念整個混在同一支程式碼中，所以學得非常混亂XD)\n先來看 controller.py 範例，將程式進入點 (start.py) 整在一起 這個範例是我看來不少文章後，做給自己的特製版\n第二行的「from UI import Ui_MainWindow」：UI 的部分，請改成你儲存的 UI.py 檔名 Ui_MainWindow，是 Qt dedigner 預設產生出 .py 檔就會取名的 class 封裝後的名稱，建議沒必要不需要特別去更改\nfrom PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow(QtWidgets.QMainWindow): def __init__(self): # in python3, super(Class, self).xxx = super().xxx super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.ui.textEdit.setText(\u0026#39;Happy World!\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: import sys app = QtWidgets.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) 說明 上面的範例程式，有調整過適合套用在往後要設計的各種 UI 程式裡面，\n我們接下來只需要改動的地方都在「setup_control()」之中，\n例如像上面範例中的\ndef setup_control(self): # TODO self.ui.textEdit.setText(\u0026#39;Happy World!\u0026#39;) 我們將我們設計的「UI程式」，封裝後儲存在「self.ui」中，\n而 self.ui 裡面的 textEdit，就是我們這次修改內容的目標。\n我們使用 .setText 修改成 \u0026lsquo;Happy World!\u0026rsquo;，就能得到以下的結果。\n我們使用 Day2 製作出來的 「UI.py」 程式碼是一樣的，為了不讓讀者還需要特別回去翻，\n我們這邊在附上範例一次，請將他存成「UI.py」，\n或自行去修改上面提到的「from UI import Ui_MainWindow」的「UI」檔名。\nfrom PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(\u0026#34;MainWindow\u0026#34;) MainWindow.resize(800, 600) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName(\u0026#34;centralwidget\u0026#34;) self.textEdit = QtWidgets.QTextEdit(self.centralwidget) self.textEdit.setGeometry(QtCore.QRect(270, 290, 104, 71)) self.textEdit.setObjectName(\u0026#34;textEdit\u0026#34;) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21)) self.menubar.setObjectName(\u0026#34;menubar\u0026#34;) MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName(\u0026#34;statusbar\u0026#34;) MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate(\u0026#34;MainWindow\u0026#34;, \u0026#34;MainWindow\u0026#34;)) self.textEdit.setHtml(_translate(\u0026#34;MainWindow\u0026#34;, \u0026#34;\u0026lt;!DOCTYPE HTML PUBLIC \u0026#34;-//W3C//DTD HTML 4.0//EN\u0026#34; \u0026#34;http://www.w3.org/TR/REC-html40/strict.dtd\u0026#34;\u0026gt; \u0026#34; \u0026#34;\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;meta name=\u0026#34;qrichtext\u0026#34; content=\u0026#34;1\u0026#34; /\u0026gt;\u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; \u0026#34; \u0026#34;p, li { white-space: pre-wrap; } \u0026#34; \u0026#34;\u0026lt;/style\u0026gt;\u0026lt;/head\u0026gt;\u0026lt;body style=\u0026#34; font-family:\\\u0026#39;PMingLiU\\\u0026#39;; font-size:9pt; font-weight:400; font-style:normal;\u0026#34;\u0026gt; \u0026#34; \u0026#34;\u0026lt;p style=\u0026#34; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\u0026#34;\u0026gt;Hello World! \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#34;)) 執行 將「controller.py」、「UI.py」準備好後，\n執行我們的程式，目前這個 「controller.py」也包含著我們程式的進入點。\npython controller.py 結果 我們可以注意到，中間的內文從「Hello World!」變成了「Happy World!」 最後，為了讓我們的邏輯在更漂亮一些，我們讓 start 與 controller 分離。 為了符合當初我們想的架構，如下圖，\n我們讓 controller.py 獨立出來，專心做控制的動作就好，\n這樣也更符合 Design Pattern 的 「單一職責原則 (SRP)」\n真正的實現我們上述所說的：\n用「controller.py」去 import「UI.py」來使用 用「start.py」去 import「controller.py」來使用 另外，在 python3 當中，super(MainWindow_controller, self).**init**() 的寫法， 可以簡寫為 super().**init**() 因此，我們將 controller.py 簡化為 from PyQt5 import QtWidgets, QtGui, QtCore from UI import Ui_MainWindow class MainWindow_controller(QtWidgets.QMainWindow): def __init__(self): super().__init__() # in python3, super(Class, self).xxx = super().xxx self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setup_control() def setup_control(self): # TODO self.ui.textEdit.setText(\u0026#39;Happy World!\u0026#39;) 並新增一個 start.py，作為之後程式的入口 from PyQt5 import QtWidgets from controller import MainWindow_controller if __name__ == \u0026#39;__main__\u0026#39;: import sys app = QtWidgets.QApplication(sys.argv) window = MainWindow_controller() window.show() sys.exit(app.exec_()) 小結 今天我們完成了一支 Qt 程式大致的模板，\n之後我們都可以照著這個模板進行後續的開發!\n","date":"2021-08-09T00:01:41+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-5-1.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-5/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 5 - 開始來設計我們的 controller.py，改以「程式角度」來說明如何建立 PyQt 的系統"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 (沒有，今天不寫程式，但要講重要觀念XD)\nPyQt 的程式邏輯 我特別作了一張圖，方便初學者理解\n之前我學的時候都沒有找到類似的圖，我看了一堆網路文章還是摸不著頭緒\u0026hellip;\n只能說真的是前人種樹後人乘涼XDD\n先了解 介面 (UI, frontend)，跟控制 (backend) 的不同 類似的觀念其實我們在 Day2 已經有提到過，\n不過這次我們要來真正的講我們到底在做什麼。\n如果有接觸一點網頁開發，相信對於「前端、後端」這兩個詞都不陌生，\n不過這裡我簡單說明一下，前端就是呈現的介面，他就是一個視覺上呈現的東西，\n還需要後端去告訴他運作的邏輯，才會是一個完整的網頁。\n例如：我知道畫面的某個地方有按鈕(前端)，但按下按鈕會發生什麼事情，就是交由(後端)去處理。\n所以我們可以畫出下面的這張圖，\n通常來說，一個功能設計分得夠乾淨的程式，\n再依照我的取名習慣，我會將他分為 UI.py 與 controller.py 兩個部分。\nUI.py 代表的是介面，也可以寫成單獨執行，我們就只會看到使用者介面，而沒辦法進行任何的邏輯判斷操作 (Day 2 提到過的，關於要不要下「-x」這個 flag 影響到的事情。) controller.py 代表的是邏輯，通常單獨執行也不知道你要控制的是啥，所以一定會搭配 UI 一起執行。 網路上有很多程式會把這兩個混在一起，組成一支很大的「.py」，\n我們都可以用這個概念去理解，\n而混在一起變成一隻並不代表不好，這只是一個「架構設計」的問題，\n也許人家的程式很小，特別寫成兩份 「.py」 反而還嫌囉嗦呢!\nQt desinger 的功能 Qt desinger 的功能就是幫助我們設計出好看的 UI.py，\n畢竟牽扯到「視覺設計」，如果這都還要靠寫 code 直接刻出來\u0026hellip; (太硬了啦!)\n而且 Qt desinger 也很適合設計師使用，由設計師設計出精美的畫面後，\n再經由指令自動轉換成 UI.py，最後我們就可能有相對美麗的介面囉!\n轉換的程式碼，我也一併整理過來 我們儲存的 Qt desinger 檔案，副檔名是 .ui\n經由下面的轉換，可以自動轉換成 .py 的程式碼\npyuic5 -x test.ui -o UI.py (通常已經有設計好 controller.py 的程式，我們不會特別下「-x」，不讓他可以單獨執行)\n-x: 輸出為可單獨執行的檔案 (有 main 的部分)，若無會只有單純封裝好的 UI class -o: 輸出 .py 檔案 概念圖示如下：\n撰寫更巨大的系統程式 基於 UI.py, controller.py 兩個概念，我們可以就開始無限的擴大我們的程式了!\n像我們可以在 UI.py 新增按鍵或文字，並同時在 controller.py 撰寫如何控制這些東西，\n甚至是「按鍵與文字」如何互動，都可以撰寫在 controller.py 裡面，\n讓我們可以經由後端的邏輯，完全控制介面的變化。\n因此，有了這個觀念，相信接下來各路程式好手們，\n我們就可以逐漸開發屬於自己的超大型視窗程式了。\n圖示：\n小結 今天我們知道非常重要的程式開發邏輯，\n往後我們會不斷地提到，現在是在設計「UI」，還是在設計「邏輯」，\n兩者雖然在開發時會併行，但絕不能混淆，\n混淆的話之後學習一定會變得一團亂，就跟我之前初學 Qt 一樣\u0026hellip;\n網路太多文章都會把「設計UI」、「設計邏輯」混在一起講，\n這並不是說他們有錯，因為本來「設計一個新的 UI」，很自然接下來就會「設計對應此 UI 的邏輯」，\n但以我們要「學習」的角度來看這個「結果」，\n「設計UI」、「設計邏輯」混在一起看就是一團亂，\n所以我之前才會學得超級痛苦 Q__Q，\n這個概念往後務必在心中養成拆乾淨的習慣，才會清楚現在的自己是在幹嘛。\n","date":"2021-08-08T19:16:21+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-4-3.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-4/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 4 - 重要的 Qt 程式邏輯觀念，務必先有此觀念後面才會懂自己在幹嘛"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 因為 PyQt5 要學的東西太多，\n我們先來學打包 python 好了，(逃避要學東西太多的現實)\n這樣就可以把每天的學習成果分享給親朋好友囉!\n打包的重點就在於，我們只要打包後，這個程式會變成單一執行檔，\n而且可以橫跨「windows, mac, ubuntu」的「同作業系統電腦」使用，\n還不用特別安裝「python」也能執行哦!\n(想想你之前下載遊戲的時候，有特別還要去安裝什麼程式語言嗎XD)\n更新：這邊我一開始有點誤會了，打包後的程式是在「同OS上能夠直接執行，不能夠跨作業系統」。 也就是說如果是同樣 windows 的電腦，我包好之後可以直接給另外一台 windows 的電腦使用， mac 電腦對 mac 電腦也是，以此類推。 至於「跨作業系統」自然是不行的，會需要至少一台該作業系統的電腦重包 (可以想像一般我們安裝軟體的時候，都會有很多作業系統的版本要你選，因為不同作業系統運作程式的邏輯不同，需要依照該作業系統的運作邏輯將程式打包起來。) 安裝 pyinstaller 老樣子的一行解決 (我想大家應該也都很習慣了(吧)\u0026hellip;?)\npip install pyinstaller 補充 - anaconda 安裝 pyinstaller 的方法 (感謝網友：steven yang 補充) 如果使用 pip 安裝 pyinstaller 會出現以下問題：\n無法辨識’pyinstaller’ 詞彙是否為Cmdlet、函數、指令檔或可執行程式的名稱) 的報錯。 這時如果有搭配 anaconda 的環境使用，可以採取在 anaconda terminal 輸入的以下方法：\nconda install -c conda-forge pyinstaller 有些東西在 anaconda 上用 pip 會不能運作\u0026hellip; ( \u0026lt;- 我自己也踩過很多次這個坑XDD )\n感謝網友：steven yang 幫忙補充 ^ ^\n來打包昨天的程式碼吧! 昨天我們完成的 UI.py，\n我們可以直接在終端機輸入\npyinstaller -F UI.py 結果 打包的過程，會稍微需要等一下，\n中途會跳出一堆東西，可以順便看看有沒有錯誤。\n最後等待程式跑完後，我們去同一個資料夾底下的 dist，裡面會看到一份「UI.exe」 。\n我們打開來試試看吧! 看起來就跟昨天的結果一模一樣呢!!!\n(請忽略上面的顏色XD，只是心情好剛好去改 windows 配色，那顏色不是用 Qt 做的XD)\n小結 這份「UI.exe」我們就可以拿來分享給全世界你的親朋好友囉!\n只要是有「同作業系統 (windows, mac, ubuntu)」的電腦，不用特別安裝環境應該都能夠打開!\nReference Using PyInstaller ","date":"2021-08-08T18:33:00+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-3-2.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-3/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 3 - 用 pyinstaller 將 python 程式打包，把每天的成果分享給你的親朋好友"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 這次是我初學 PyQt5 的一些筆記，\n每天都學一點點，至於整理的部分就慢慢一個個來\n畢竟知道 PyQt5 有很多東西XD\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day02_qt_designer\n安裝 先安裝 Qt designer 連結：https://build-system.fman.io/qt-designer-download\n找到自己對應的版本下載 安裝 (這邊以 windows 示範) 安裝完成，第一次執行 Qt designer 打開來會發現這個畫面 這個畫面代表有不同的模板可以選擇，第一次我們先不管就選「Main Window」。\n可以看到空白的畫面就這麼出來了!\n建立文字框，開始我們的第一次 Hello World 吧! 拖曳左側的 Text Edit 至畫面中任意地方，建立文字框 點擊文字框兩下，進入編輯模式，輸入 Hello World! 輸入 Hello World! 的畫面可以參考這邊 存檔囉! 這就是我們的第一份有 UI 介面的程式 得到了 .ui 檔，我們現在要將他轉成 .py 我們在安裝 PyQt5 時就已經有安裝好了，我們可以直接在終端機下以下的指令\n舉例：例如我想將剛剛儲存的 \u0026ldquo;test.ui\u0026rdquo; 轉換成 \u0026ldquo;UI.py\u0026rdquo;\n範例程式碼 pyuic5 -x test.ui -o UI.py 說明 -x: 輸出為可單獨執行的檔案 (有 main 的部分)，若無會只有單純封裝好的 UI class -o: 輸出 .py 檔案 有沒有「-x」的差別 紅色框 (我多框了 import 的部分，應該是沒有)，就是封裝好的 UI class，\n藍色框就是 main 的部分，正常來說我們應該是不會「單獨」執行介面，\n單獨執行介面，就是一個只有畫面，不能動的東西XD\n除了好看(?)之外，沒什麼好用的XD\n如果我們沒有下「-x」，就不會有 main 的部分 (純封裝好的 UI，要另外 import 寫控制)，\n正常來講，除非只是想看顯示出來是怎麼樣，不然不會下「-x」這個 flag。\n日後我們會更細部的來談這邊的架構，現在先知道差別即可。\n執行 到這邊相信大家都懂了XD，就一如往常的執行這份 python 吧!\npython UI.py 結果 看起來很簡單，不過這已經是我們設計一隻大型 Qt 程式重要的第一步囉!\nReference PyQt 入門，用 Python 寫第一支 GUI ","date":"2021-08-08T16:53:15+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-1-7.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-2/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 2 - 利用 Qt designer 建立第一支有自己介面的 PyQt5 程式"},{"categories":["820 - Cloud"],"content":"前言 在雲端服務設計的過程中，\n我們會碰到使用 global / seperate queue 的差別\n可以先思考一個經典的排隊問題： 旁邊櫃檯沒人排隊(比喻：此主機忙碌、別的主機空閒)，你可以去隔壁櫃臺要求提供服務嗎？\n想想在郵局的情況、再想想在大賣場的情形\nglobal queue 才有機會將這個櫃台的人分配給其他櫃員處理，\n否則，他只能夠排隊在該櫃台、一直等待服務。\n以上面的排隊問題，換到真實的情境： 開了兩台主機 一台CPU爆強、一台GPU爆強\n結果在拿task的時候，一個狂吃CPU的 task，使用了 GPU爆強的機器\n這樣是不是浪費了GPU爆強的機器使用成本?\n說明 \u0026amp; 比喻 有一個很妙的比喻拿來形容 global / seperate queue 的差別非常合適：\nseperate queue 使用情境：不想要堵塞，客戶為尊，客戶到馬上就處理，一般來說會使用許多的 service，讓服務馬上到馬上進行 就像「全聯排隊」，一個收銀台排隊一排，能不能先結帳要看運氣，\n有時運氣不好，前面排隊的結帳很久，後來才來結帳的人反而先完成。\nseperate queue, multiqueue, multi service global queue 使用情境：可能會有堵塞的情況，而為了求公平，讓先到的客戶先處理，而服務成本一般較高(像主機、櫃員)，讓想使用的客戶自行排隊等待服務時間。 就像「郵局排隊」，先到的客戶先處理，一個接著一個\nglobal queue, single queue, multi service 一般情況使用 一般情況下 global queue 優於 seperate queue，\n但設計上 seperate queue 相對 global queue 容易設計，\n不用考慮太多整體的事情。\n程式設計 程式設計：seperate queue 設計可以較隨意 (每一個櫃位要擺哪，要怎麼排隊隨便啦)，\nglobal queue 的設計須以 global queue 為主軸，讓每一個系統能夠去提取 task 來做 (排隊動線為尊，櫃檯配合排隊動線設計)。\nReference ","date":"2021-08-06T10:18:16+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/cloud-platforms/cloud/global-seperate-queue/","tags":["Cloud"],"title":"【Cloud】global / seperate queue 差別 \u0026 使用"},{"categories":["132 - PyQt5","047 - 13th 鐵人賽 – 【今年還是不夠錢買psQQ，不如我們用PyQt自己寫一個】"],"content":"看完這篇文章你會得到的成果圖 這次是我初學 PyQt5 的一些筆記，\n每天都學一點點，至於整理的部分就慢慢一個個來\n畢竟知道 PyQt5 有很多東西XD\n此篇文章的範例程式碼 github https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day01_install_and_hello\n安裝 先安裝 PyQt5 新的方法 (之後到 OpenCV 章節時，可避免一些問題) 建議使用 conda 的方式安裝，\n打開終端機的對應 conda 環境輸入\nconda install pyqt 舊的方法 (之後可能會有些問題) 打開終端機輸入\npip install PyQt5 至於前面步驟「可能」還有：\n安裝 python 安裝 anaconda (optional，看你想不想要管理 python 環境) 這部分就先不多說明，網路已經很多好文章了!\n範例程式碼 PyQt5 可以學習的東西太多了! 我一開始學也是超級混亂的!!!\n所以我們慢慢來!\n先什麼都不管，直接跑程式碼，至少我們要確認「有沒有安裝成功」!\n複製以下程式碼，直接跑就對了 應該要「能正常執行」，不然就是你沒安裝成功 PyQt5\nimport sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel from PyQt5.QtGui import QIcon from PyQt5.QtCore import pyqtSlot def window(): app = QApplication(sys.argv) widget = QWidget() textLabel = QLabel(widget) textLabel.setText(\u0026#34;Hello World!\u0026#34;) textLabel.move(110,85) widget.setGeometry(50,50,320,200) widget.setWindowTitle(\u0026#34;PyQt5 Example\u0026#34;) widget.show() sys.exit(app.exec_()) if __name__ == \u0026#39;__main__\u0026#39;: window() 執行 到這邊相信大家都懂了XD，就一如往常的執行這份 python 吧!\n(檔名請自行更換)\npython hello.py 結果 windows 版本 不論是任何版本，複製上面程式碼應該都要看到這個結果，\n不然請再去檢查你的 PyQt5 為何沒有安裝成功!\n(沒安裝成功的原因太多種了，這邊很難細述)\n說明 之後還會有更詳細的說明，但是既然這邊都提到了還是要交代一下！\n如果想看更詳細的說明，這邊可以先跳過，日後文章會再來細講\n建立一個視窗應用，並且宣告一個 QWidget 物件 這邊基本上寫法不太會改\napp 代表「系統」的視窗程式， widger 代表一個 QWidget() 的物件 (我們開發的視窗) app = QApplication(sys.argv) widget = QWidget() 在 widget 新增一個文字物件 這邊我們使用的是 QLabel，之後會在細講這東西，\n我們將這東西存入 textLabel 這個變數中，並且作了文字與位置的設定\ntextLabel = QLabel(widget) textLabel.setText(\u0026#34;Hello World!\u0026#34;) textLabel.move(110,85) 在 widget 設定視窗大小，設定標題名稱，並顯示於畫面上 這邊就是如何設定我們的 QWidget 視窗，\nsetGeometry 可以決定視窗大小 setWindowTitle 可以決定視窗名稱 show 將視窗顯示 widget.setGeometry(50,50,320,200) widget.setWindowTitle(\u0026#34;PyQt5 Example\u0026#34;) widget.show() 結束視窗應用 這邊是在偵測使用者呼叫的視窗是否有被關閉，\n就是我們一般視窗「右上角(windows) 的叉叉」，偵測到關閉程式後，\n讓程式順利結束運行，如果沒有這行就等於程式直接結束，\n也不會等使用者把視窗關閉XD\nsys.exit(app.exec_()) 小結 到這邊，看起來很簡單(嗎?)，不過這就是我們初探 PyQt5 的第一步囉!\n(到這邊，距離大致掌握 PyQt5 還好遙遠\u0026hellip; 太多東西要學了)\nReference PyQt Hello World ","date":"2021-08-05T17:55:05+08:00","image":"https://wongwongnotes.com/images/restored/2021/08/pyqt5-1-9.png","permalink":"https://wongwongnotes.com/posts/python/gui/pyqt/pyqt5-1/","tags":["PyQt5","Python","鐵人賽","13th鐵人賽"],"title":"【PyQt5】Day 1 - 安裝 PyQt，建立自己的第一支 PyQt5 程式"},{"categories":["170 - Python 平行加速"],"content":"前言 現在已有版本 v2，請參考 v2 版本為主。\nhttps://wongwongnotes.com/posts/python/networking/python/python-auto-job-v2/\nmultiprocessing 的程式模板，\n將任務封裝好後，直接丟入 list_tasks 即可自動發揮系統最大效能跑平行運算。\n封裝的概念，請務必先自行理解。\n範例程式碼 start_multiprocess.py 請在這邊將所有封裝好的任務存入 list_tasks，\n封裝的任務必須至少包含以下兩個函數：\ninit(): 初始化變數 start_progress(): 讓自動化流程分配任務時，開始執行的入口 注意：請勿將 start_progress() 放在 init() 中執行，這樣在宣告階段程式就會先開始跑起來了！ 失去了自動分配資源的效果！\nimport glob import multiprocessing as mp from multiprocessing import RLock from tqdm import tqdm from multiprocess_task import multiprocess_task list_tasks = [] # ------------- [YOU ONLY NEED TO SET HERE] ------------- # # TODO: 將封裝好的任務塞入 list_tasks 中 #---------------------------------------------------------# if __name__ == \u0026#39;__main__\u0026#39;: tqdm.set_lock(RLock()) # for managing output contention cpu_count = mp.cpu_count() print(f\u0026#34; --------------------- [start_multiprocess.py] --------------------- \u0026#34;) pool = mp.Pool() res_list = [] for idx in range(len(list_tasks)): res_list.append(pool.apply_async(multiprocess_task, (idx, len(list_tasks), list_tasks[idx]))) for idx in range(len(res_list)): res_list[idx].get() multiprocess_task.py 基本上不用改，讓他自行分配任務。\nimport os import cv2 import glob from tqdm import tqdm class multiprocess_task(object): def __init__(self, idx, total_job, each_task): each_task.start_progress(total_job) ","date":"2021-08-05T16:06:50+08:00","permalink":"https://wongwongnotes.com/posts/python/networking/python/multiprocessing-pool-auto/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #6】multiprocessing pool 程式模板，讓系統發揮最大效能，自動分配資源平行運算"},{"categories":["131 - Python OpenCV"],"content":"前言 此為以下問題的解決方法筆記：\nTypeError: cannot pickle \u0026#39;cv2.VideoCapture\u0026#39; object 解法 此問題會發生在 cv2 結合 multiprocessing 使用時，\ncv2.VideoCapture 不可作為成員變數使用。\n參考：https://github.com/MVIG-SJTU/AlphaPose/issues/164\n會跳錯的範例：\n將 cv2.VideoCapture 作為成員變數使用 self.vc = cv2.VideoCapture(self.video_path) 正確寫法：\n將 cv2.VideoCapture 作為 local variable vc = cv2.VideoCapture(self.video_path) Reference https://github.com/MVIG-SJTU/AlphaPose/issues/164 ","date":"2021-08-05T15:46:14+08:00","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/typeerror-cv2-videocapture/","tags":["Python"],"title":"【Python】問題解決：TypeError: cannot pickle 'cv2.VideoCapture' object"},{"categories":["170 - Python 平行加速"],"content":"前言 此部份可參考：【python】python thread multiprocess 比較總整理\n在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式，\n兩者分別的特性為：\nthread: 可直接使用全域變數 (global) 交換資訊 multiprocess: 需透過設定特定通道 (多核心執行的中央變數管理，另一篇文會提到) 才能拿到資訊 也因此會有不同的特性：\nthread: 適合小任務、資訊共用的任務 (直接拿 global 資訊來用) multiprocess: 適合大任務、資訊獨立的任務 (把相關資訊交由其他核心處理後，不太需要拿取新資訊) 而在時間上，python 的 thread 實作有使用 GIL (global interpreter lock) ，\n這是為了保護程式取用資料衝突的機制 (一般而言，我們會自己建立 mutex 或 lock 來實現這一點)，\n但也因為內建此機制的原因，實際執行上會慢於 multiprocess。\n(可以有一種想法是，當 python 的 thread 實現方式是建立在大量的 context switch上)\n而 multiprocess 可以幫助我們更好的運用硬體的多核心資源，\n也就是將電腦的能力發揮到更極限，達到更好的效果。\n(一般執行程式只需要一個核心)\n但要注意：送資料到別的核心需要時間 如果是簡單不需要太多時間的任務，\n使用多核心運算的方式反而會在資料搬運上浪費更多時間。\n小整理 功能 thread multiprocess 速度 快 (受限於 GIL) 更快 電腦資源使用 極限 更極限 資訊共用容易度 相對容易 複雜 (注意：送資料到別的核心需要時間) 適合任務 小任務、資訊共用的任務 大任務、資訊獨立的任務 thread 與 multiprocess 比較 threading 重點摘要 threading 是透過 context-switch 的方式實現\n也就是說，我們是透過 CPU 的不斷切換 (context-switch)，實現平行的功能。\n當大量使用 threading 執行平行的功能時，反而會因為大量的 context-switch，\n「實現了程式平行的功能，但也因為大量的 context-switch ，使得程式執行速度更慢」。\nmultiprocessing 重點摘要 multiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較圖 從下圖我們可以看到任務被完成的「概念」時間\nmain 1~4, main-end 任務 A1, A2 任務 B1, B2 任務 C1, C2 請留意圖中粗線的部分：\n在 multithread 中，\nCPU context-switch 會額外消耗我們程式執行的時間，程式實際完成時間可能比一般的還要慢。 在 multiprocess 中， 我們需要將資料轉移至其他 CPU 會額外消耗我們程式執行的時間，如果任務過於簡單，效益可能不大。 雖然示意圖中明顯感覺較快，但前提是任務夠複雜\n也就是說，「任務難度執行的時間 \u0026gt; 資料轉移至其他 CPU 的時間效益」，不然只會更慢。\nThread 基本使用，範例程式碼 import threading as td def task(a, b): print(\u0026#39;Task in the thread.\u0026#39;) print(a, b) if __name__==\u0026#39;__main__\u0026#39;: # must put thread in the main t1 = td.Thread(target=job,args=(1,2)) t1.start() t1.join() multiprocess 實現，與取得結果 以下為範例 code，我們可在 task 做自己想做的事情，\n最後透過 result.get() 拿回結果。\n這裡我們做個暫停 10 秒的實驗，\n如果最後程式執行結束時間超過 40秒 (4核心)，\n表示這個程式並沒有多核心的執行。\nsample code (範例程式碼) import multiprocessing import time def task(cpu_no): # do something print(\u0026#34;cpu_no: \u0026#34;, cpu_no) time.sleep(10) result = cpu_no return result def main(): # multiprocessing.freeze_support() pool = multiprocessing.Pool() cpus = multiprocessing.cpu_count() print(\u0026#34;cpu_count: \u0026#34;, cpus) results = [] for i in range(0, cpus): result = pool.apply_async(task, args=(i,) ) results.append(result) pool.close() pool.join() for result in results: print(\u0026#34;result: \u0026#34;, result.get()) if __name__ == \u0026#34;__main__\u0026#34;: main() 結果 我們可以看到，4核心的電腦，程式執行只花10秒左右，\n能證明我們的程式確實有多核心執行。\n","date":"2021-08-04T14:46:21+08:00","image":"https://wongwongnotes.com/images/restored/2020/11/%E6%88%AA%E5%9C%96-2020-11-04-%E4%B8%8B%E5%8D%8812.18.01.png","permalink":"https://wongwongnotes.com/posts/python/networking/python/python-pooling-multiprocess/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #5】python pooling multiprocess - 用多核心來執行程式"},{"categories":["170 - Python 平行加速"],"content":"前言 在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式，\n兩者分別的特性為：\nthread: 可直接使用全域變數 (global) 交換資訊 multiprocess: 需透過設定特定通道 (多核心執行的中央變數管理，另一篇文會提到) 才能拿到資訊 也因此會有不同的特性：\nthread: 適合小任務、資訊共用的任務 (直接拿 global 資訊來用) multiprocess: 適合大任務、資訊獨立的任務 (把相關資訊交由其他核心處理後，不太需要拿取新資訊) 而在時間上，python 的 thread 實作有使用 GIL (global interpreter lock) ，\n這是為了保護程式取用資料衝突的機制 (一般而言，我們會自己建立 mutex 或 lock 來實現這一點)，\n但也因為內建此機制的原因，實際執行上會慢於 multiprocess。\n(可以有一種想法是，當 python 的 thread 實現方式是建立在大量的 context switch上)\n而 multiprocess 可以幫助我們更好的運用硬體的多核心資源，\n也就是將電腦的能力發揮到更極限，達到更好的效果。\n(一般執行程式只需要一個核心)\n但要注意：送資料到別的核心需要時間 如果是簡單不需要太多時間的任務，\n使用多核心運算的方式反而會在資料搬運上浪費更多時間。\n小整理 功能 thread multiprocess 速度 快 (受限於 GIL) 更快 電腦資源使用 極限 更極限 資訊共用容易度 相對容易 複雜 (注意：送資料到別的核心需要時間) 適合任務 小任務、資訊共用的任務 大任務、資訊獨立的任務 thread 與 multiprocess 比較 threading 重點摘要 threading 是透過 context-switch 的方式實現\n也就是說，我們是透過 CPU 的不斷切換 (context-switch)，實現平行的功能。\n當大量使用 threading 執行平行的功能時，反而會因為大量的 context-switch，\n「實現了程式平行的功能，但也因為大量的 context-switch ，使得程式執行速度更慢」。\nmultiprocessing 重點摘要 multiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較圖 從下圖我們可以看到任務被完成的「概念」時間\nmain 1~4, main-end 任務 A1, A2 任務 B1, B2 任務 C1, C2 請留意圖中粗線的部分：\n在 multithread 中，\nCPU context-switch 會額外消耗我們程式執行的時間，程式實際完成時間可能比一般的還要慢。 在 multiprocess 中， 我們需要將資料轉移至其他 CPU 會額外消耗我們程式執行的時間，如果任務過於簡單，效益可能不大。 雖然示意圖中明顯感覺較快，但前提是任務夠複雜\n也就是說，「任務難度執行的時間 \u0026gt; 資料轉移至其他 CPU 的時間效益」，不然只會更慢。\n","date":"2021-08-03T12:24:25+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/multiprocess-python-1-1024x333.png","permalink":"https://wongwongnotes.com/posts/python/networking/python/python-thread-multiprocess/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #4】python thread multiprocess 比較總整理"},{"categories":["198 - Python 問題解決"],"content":"前言 此為以下問題的解決方法筆記：\n\u0026#39;cp950\u0026#39; codec can\u0026#39;t decode byte 0xe6 in position 111: illegal multibyte sequence 解法 簡單說就是 encoding 的問題，\n在開啟對應檔案時加入「encoding=\u0026ldquo;utf-8\u0026rdquo;」即可以解決。\n(注意檔案 open 的地方)\n範例 原本你的程式可能長這樣 (注意 open 的地方)\nwith open(each_file, \u0026#39;r\u0026#39;) as fin: jf = json.load(fin) 請改成這樣\nwith open(each_file, \u0026#39;r\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;) as fin: jf = json.load(fin) Reference https://wongwongnotes.com/posts/python/concepts/troubleshooting/cp950-codec/\nhttps://oxygentw.net/blog/computer/python-file-utf8-encoding/\n","date":"2021-06-24T16:20:09+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/cp950-codec-python/","tags":["Python"],"title":"【Python】問題解決：'cp950' codec can't decode byte 0xe6 in position 111: illegal multibyte sequence"},{"categories":["Popworld 蹦世界"],"content":"《狼人請睜眼》 遊戲介紹 由「Popworld 蹦世界」所出品的《狼人請睜眼》是一款由「桌遊」延伸而出的解謎遊戲\n雖然遊戲名稱叫作《狼人請睜眼》，但遊戲可不只狼人殺而已哦!\n由「狼人殺 與 台灣四大桌遊 (3C1B)」組成的遊戲內容 不知道讀者們有沒有聽過台灣的四大桌遊的「3C1B」這個稱號呢?\n「3C1B」表示 Catan(卡坦島)、Citadel(富饒之城)、Carcassonne(卡卡頌)、Bang!，\n也就是這款桌遊除了狼人殺之外，也會帶到這四款經典的桌遊，\n透過解謎的方式，來了解「狼人殺 與 台灣四大桌遊」的遊戲規則!\n所以不要被這標題騙到了XD 裡面還有很多其他遊戲的!\n因應疫情而推出的「《狼人請睜眼》 - 數位遊戲包」 因為最近疫情的關係，大家都待在家裡，\n因此「Popworld 蹦世界」也為此嘗試了將「將實體桌遊和遊戲搬至線上」的嘗試，\n帶給大家能夠在疫情期間，多了一個在家能玩的解謎遊戲!\n△ 結合 AR 的遊戲玩法，疫情期間在家也能夠進行遊玩! 《狼人請睜眼》 - 遊戲網站：https://reurl.cc/W3kZG9\n此遊戲中參考的桌遊列表 以下為本作品有參考的作品列表，供想玩的人參考:\n狼人殺 卡坦島 卡卡頌2.0 富饒之城加強版 Bang！卡牌版 △ 《狼人請睜眼》是一款「結合多款桌遊」的而進行的解謎遊戲 其中 Catan(卡坦島)、Citadel(富饒之城)、Carcassonne(卡卡頌)、Bang\n又被稱作 3C1B，有著「台灣四大桌遊之稱」\n△ 很可愛的《狼人請睜眼》 數位版遊戲網站：https://reurl.cc/W3kZG9 如果在找密室逃脫的遊戲評價，這裡還有一些當初我自己有參考的密室逃脫心得文，也歡迎一併參考：\n遊戲相關 以下是自己的玩完後的一些心得，老樣子，有優點、有缺點都會說，\n「沒有爆雷」的部分，留給還沒遊玩的朋友們，(除了大約的行走距離提示XD)\n一切都為「個人\u0026amp;團隊成員」們的「真實體驗」，絕沒有多加修飾XD\n題目類型：基於桌遊規則的「大量的邏輯思考、邏輯推理」 這款遊戲主要的題目類型幾乎都是「大量的邏輯思考、與邏輯推理」，\n適合喜歡「邏輯思考、邏輯推理」的朋友們！\n如果是沒有玩過「上述桌遊」的朋友，可能需要稍微斟酌一下XD\n遊戲進行中會「需要先進行桌遊規則的了解」，\n然後理解後該桌遊規則後才能進行解謎。\n有句俗話說，「理解桌遊的規則」通常是玩桌遊最痛苦的環節。\n那麼搭配這款解謎遊戲，或許能讓這理解規則的過程中多一些趣味哦!\n當然，也有可能是理解規則感受到痛苦\u0026hellip; 最後丟給組員去解讀的XDD (絕對不是在說我)\n此外，《狼人請睜眼》結合了「手機搭配平板/電腦」，使用兩種不同裝置進行互動的玩法，\n還有利用 AR 進行的解謎 (透過 AR 把原先「不存在」的桌遊道具，直接「變出來」在手機上了!)\n所以，就算沒有實體桌遊道具也能夠玩哦!\n示意圖 △ 結合 AR 的遊戲玩法，我們可以直接與數位板網頁進行互動哦! 題目數量 題目一共有5題，分別就是上述所說的桌遊各一題，\n可以遊玩前想想自己是否對這些規則有點印象，遊玩上會更加順利哦!\n(當然，遊玩過程中學也是沒問題的!) \u0026hellip;如果不是要拚解謎速度排名的話XDD\n狼人殺 卡坦島 卡卡頌2.0 富饒之城加強版 Bang！卡牌版 解題時間: 不懂上列桌遊規則大約 1.5 小時，懂的話會更快 既然是一款主打「基於桌遊的解謎遊戲」，\n預先懂這些桌遊規則的解題優勢就非常大，\n當然如果是想學習這些桌遊規則，也可以慢慢地學!\n謎題難度(解題需要的背景知識): 沒玩過這些桌遊的新手偏難(5/5)，桌遊老玩家適中 (3/5) 如果是沒玩過這些桌遊的新手：難度 (5/5) 老實說，一款桌遊真的沒有玩個幾次，還真的很難只從說明書就懂他的運作， 我覺得雖然說明書寫得很清楚，但光看說明書應該還是懵懂懵懂， 所以... 我覺得是「難在第一次看懂說明書的部分」 如果是桌遊老玩家：難度 (3/5) 如果是已有遊玩過這些桌遊為前提的老玩家， 相信對這些規則都應該很熟悉了， 那就可以直接進行「對這些桌遊的遊戲解謎」。 其中有幾個是讓你觀看現在的局勢，推測玩家的身分牌，\n我覺得是滿有趣的!\n△ 結合多款桌遊的《狼人請睜眼》，遊玩理解時理解新規則有時會成為玩家的難題 不過也有幾題覺得有些可惜，出的題目與桌遊規則連結性太有限了\u0026hellip;\n謎題難度(解題過程夠不夠直覺): 中間偏難 (4/5) 邏輯推理的部分其實滿難的\u0026hellip;\n舉例的話就是玩到一半的桌遊，要能夠回推出之前的狀態，\n這個真的會滿動腦的，推薦給喜歡看別人桌遊局的人!\n平常只能「觀棋不語真君子」，這次就能「給你大聲講」啦\n謎題故事性: 用簡單的故事將各個遊戲章節串起來 (3/5) 故事的部分，這次的遊戲用了狼人的故事帶過了全部的關卡，\n每一章節的故事也都有搭配精美的劇情圖，對於喜歡故事的朋友也能欣賞一下!\n不過有些小小美中不足的地方在於，每篇故事的連結性有點牽強，\n每篇故事都可以獨立看的感覺XD\n謎題驚喜度：有部分「推論桌遊局面」的設計讓人驚喜，此外結合 AR 的玩法也十分創新 (4/5) 我們團隊一致好評的就是某一關中，\n我們需要觀察桌遊的局面，並推測出玩家的身分，\n這題解出來真的滿有成就感，也滿有趣的!\n歡迎大家也在遊戲中體驗看看!\n另外，本次的數位體驗包部分，\n同時使用電腦與手機來解謎的想法，個人覺得超級酷!\n但這部分結合謎題的流暢處，也許是還在嘗試階段，\n雖然有「AR 的體驗」，但可惜覺得還沒有發揮到「AR 的精隨」\n(也就是有種，雖然有AR很酷! 但這東西沒有 AR 好像也沒有太大影響的感覺)\n但有創新的思維還是要給個讚! 是一種很酷的新解謎方式!\n相信在不斷的改版當中，一定會逐漸的讓玩家們有更深的沉靜在 AR 當中的體驗。\n△ 比起單純的手遊，在此遊戲中還能夠與其他裝置互動，感覺十分的特別! 一些個人的小建議 (1) 第一次閱讀並理解桌遊說明書很辛苦，有可能會玩不太下去XD 這遊戲本質上就是「透過其他桌遊規則設計的謎題」，\n所以如果是沒有該遊戲經驗的人，會需要「閱讀並理解桌遊說明書」。\n狼人殺 卡坦島 卡卡頌2.0 富饒之城加強版 Bang！卡牌版 但這些遊戲裡面，有些規則並不是靠閱讀就容易懂的，很多都是玩下去才知道\u0026hellip;\n所以這個謎題的部分\u0026hellip;\n要解開謎題還需要先花一點時間閱讀桌遊說明書，在沒有遊玩經驗的情況下理解規則。\n△ 第一次看到這樣的桌遊規則說明書會嚇到XD，實際上並不用全部看完啦XD (2) 「部分」的題目，與桌遊原先的規則關係較遠 這部分算是個人對題目的預期，\n因為畢竟是牽扯到其他款桌遊，會期待解謎的內容會與桌遊規則「十分相關」，\n不過有部分題目只有「角色與背景設定」有關聯，而沒有包含到實際遊玩「規則」的關聯性。\n玩完後想推薦給什麼樣的人 推薦給「玩過 狼人殺 與 台灣四大桌遊 (3C1B)」、「喜歡邏輯思考、邏輯推理」的朋友!!!\n這款有著不同於狼人殺的故事、也可在不同桌遊世界遊玩，\n也因為有了數位遊戲包 ，遊玩可以不受時間地點限制\n△ 《狼人請睜眼》打破了桌遊空間的限制，玩家們可以選擇「實體版」或「數位板」進行遊戲! 但是注意這個是用桌遊規則去做解謎，而非主打玩桌遊\n如果一個人在家也可以解哦! (甚至一個人解比多人討論還要容易XD)\n在家沒事的話，也可以來玩玩看這款由「Popworld 蹦世界」出品的《狼人請睜眼》!\n喜歡實境解謎嗎？作者這裡也推薦一些有趣的實境解謎心得文，歡迎參考哦： 其他團體遊戲推\n","date":"2021-06-08T21:21:28+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/%E7%8B%BC%E4%BA%BA%E8%AB%8B%E7%9D%9C%E7%9C%BC-7-1024x576.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/popworld/werewolf-gamepack/","tags":["Popworld 蹦世界","嗡嗡粗門玩","實境解謎","蹦世界"],"title":"【在家解謎】《狼人請睜眼》遊玩後心得 (無雷) - 解謎遊戲推薦 | Popworld 蹦世界"},{"categories":["921 - iphone / iOS"],"content":"前言 這個問題是我在使用 Apple watch 中的 app ，\n「autowake」 出現 no complication 個人解法，\n具體原因不確定，但我猜是軟體更新或系統更新造成的 Apple watch 無法與 iphone 同步，\n接下來我們要讓此 Apple watch app 與 iphone 重新同步。\n可惜示意圖我沒有截到XD 個人解決方法 斷掉 Apple watch app (autowake) 在 iphone 的同步 先打開在 iphone 上的 Watch app (相信有 Apple watch 應該都知道，這邊就不特別說明細節)\n直接往下來找到對應的 Apple watch app (autowake)\n示意圖: 來找到對應的 Apple watch app (autowake) 斷掉並重新連結 Apple watch app (autowake) 與 iphone 的同步 把下面這個同步開關「關掉後又重新打開」，就可以修好了哦!\n關掉: 可以解除 Apple watch app (autowake) 與 iphone 的同步\n開啟: 重新連結 Apple watch app (autowake) 與 iphone 的同步\n示意圖: 把下面這個同步開關「關掉後又重新打開」，就可以修好了哦!\n個人猜測 應該可能是軟體更新或系統更新導致的同步失效，總之重新再同步一次就可以囉!\n","date":"2021-06-08T12:39:23+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/img_9198-473x1024.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/autowake-no-complication/","tags":["iOS"],"title":"【iOS】問題解決: Apple watch - autowake 出現 no complication 個人解法 (Apple watch app 與 iphone 重新同步)"},{"categories":["441 - VScode"],"content":"前言 因應遠端辦公的需求提升，\n我也開始尋找屬於自己遠端辦公最佳效率的方式，\n除了之前說到的 MobaXterm (可參考下文)，\n能夠方便的在 windows 上面進行遠端的 terminal 操作，\n本文會提供一種新的方式，透過本地的 VScode，\n可以直接把遠端 (學校/公司) 的 VScode 視窗介面拉回家中辦公!\n效率比遠端桌面快多了! 且利用擴充套件可支援圖片與 pdf 瀏覽。\n最重要的是， VScode 不論是在 windows, mac, linux (ubuntu) 都可以使用! 非常的萬用!!!\nMobaXterm 介紹可參考： https://wongwongnotes.com/posts/dev-tools/terminal/mobaxterm/mobaxterm-windows-linux/\n比較示意圖 我們比較三種不同的方式，讀者可自行選擇自己習慣的方式 (只有習慣問題，沒有優劣之分)：\n純 terminal 派 優：速度最快，已足夠應付 coding 缺：無 GUI 視窗，僅能閱讀文字文件 VScode 遠端派 (本文會介紹) 優：速度次快，coding 有 GUI 介面、支援檔案總管 ，可透過擴充套件瀏覽圖片, pdf 缺：其他應用程式視窗不可使用 遠端桌面派 優：只要有網路，幾乎沒什麼做不到的事情 缺：速度慢，畢竟是即時傳送畫面 實作大致流程圖 簡單來說，我們需要先在本地電腦掛上 VPN，\n我們才能夠連線進去 (學校/公司)，再來就是透過 VScode 擴充套件的 ssh 進行連線。\n就可以完成實作囉!\n準備 VPN 的部分 準備 VPN 工具 - OpenVPN step 1. 下載 OpenVPN 軟體 OpenVPN 下載網址：https://openvpn.net/community-downloads/\n我們要下載的是 windows-64 位元 的檔案 (通常現在比較新的 windows 電腦都是這個，不是請自行調整)\nstep 2. 安裝 OpenVPN 軟體 安裝完即可。\n取得 vpn 設定檔案 (副檔名為 .ovpn 的檔案，通常會由企業或學校機構提供。) 總之，這邊幫不了忙XD\n請向你的 企業 或 學校機構拿到一個「副檔名為 .ovpn 的檔案」。\n連線至 VPN step 1. 匯入 「.ovpn」 設定檔 從右下角選單「右鍵」OpenVPN，選擇「匯入設定檔」，\n將剛剛上一個步驟中的「.ovpn 的檔案」匯入。\nstep 2. 透過 VPN 連線至遠端 從右下角選單「右鍵」OpenVPN，選擇「連線」即可。\n(連線時，也請向「企業」或「學校機構」取得連線使用的帳號密碼)\n準備 SSH 的部分 下載 VS code 的部分就不再贅述\nstep 1. 下載擴充套件「 Remote - SSH 」 step 2. 在 VScode 左下角啟動連線 ssh 的功能 (記得有學校/公司 VPN 需求的請先在上一步進行連線) 這邊可以選擇\n「Connect Current window to Host\u0026hellip;」 ：在「本視窗」進行 ssh 連線 「Connect to Host\u0026hellip;」 ：另外「開一個新視窗」進行 ssh 連線 step 3. 輸入密碼後，選擇遠端電腦的資料夾路徑 例如說，這邊以遠端的桌面 (/home/ubuntu/Desktop) 進行示範，並按下「OK」\nstep 4. 可以發現旁邊有各種的檔案出現了! 如果是「文字文件」直接開啟當然沒有問題，「圖片」也能直接開啟。\n比起終端機 terminal 直接操作，「圖片」能直接顯示，編輯程式碼也能使用「滑鼠」，更方便了!\n「VScode ssh」相比「純 termianl ssh」特色功能 檔案總管，方便使用滑鼠快速操作，比起「純 termianl ssh」更快速 例如上面的圖片所示，檔案總管的瀏覽，方便使用滑鼠快速操作，比起「純 termianl ssh」更快速\n可以用滑鼠協助編輯程式碼 這個應該是 VS code 的基本功能了，現在可以直接遠端編輯，「自己在家跟在學校公司一樣」!\n比起「純 termianl ssh」可以直接開啟圖片、pdf (pdf 需安裝套件) 想要在 VS code 遠端開啟 pdf，推薦可以下載「vscode-pdf」，可以直接在 VScode 顯示出 pdf 文件。\nVS code 使用技巧：使用「ctrl + `」，可以直接叫出終端機 (terminal)，且可以快速多開終端機 在 VS code 中使用快速鍵「ctrl + `」就可以直接呼叫出終端機 (terminal)，\n而在右方的「+」，可以快速新建終端機視窗，而且左側可以快速在不同的終端機之間切換。\n如果是純 termianl ssh，要多開 terminal 還需要重新建立連線，相對來說比較麻煩\n(或是也有其他方法我還不知道，不過個人目前覺得這方式很快!)\n自動儲存並記憶連線ip, 方便下次連線 第二次連線後，因為ip被儲存下來了，\n我們要連線非常的方便! 直接輸入密碼就可以使用了!\n「VScode ssh」相比「遠端桌面」特色功能 「遠端桌面」應有盡有，但因為是畫面傳輸，速度一定相對較慢 「遠端桌面」，如 chrome remote desktop, anydesk, teamviewer\n就是把遠端的畫面直接連接回自己的電腦，基本上功能「要有什麼就有什麼」。\n但也因為是畫面傳輸，一定會有延遲的問題，\n如果網路環境不佳，「寫 code 可能會非常的 lag」，\n這光用想的就很崩潰了吧!!! 寫個 code 還會 lag，整個效率大打折扣了!\n","date":"2021-06-05T02:46:58+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/vscode-ssh-2.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-ssh/","tags":["VSCode"],"title":"【VScode #2】直接把 coding 的「視窗」搬回家，不要再用慢慢的遠端桌面了! VScode ssh 遠端辦公"},{"categories":["170 - Python 平行加速"],"content":"前言 這一篇文是上一篇文的進階版，若還沒有看過上集可以看這篇：\nhttps://wongwongnotes.com/posts/python/networking/python/python-multiprocessing/\n這篇會更著重在說明「multiprocessing Pool」的用法，\n差別在會使用 multiprocessing Pool 可取得回傳結果，並讓系統自動分配資源，\n而使用 Process 不會回傳結果 (除非使用 mp.Queue 等等方式)\n一樣的前言介紹 在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式，\n若只是單純的平行需求，我們可以使用 threading 這個模組來快速完成平行處理的方式。\n但是 threading 只是透過頻繁的 CPU context-switch 的方式實現，\n要真正實現多核心 CPU 的平行運算，我們需要使用 multiprocessing，\n將任務指派給多個核心進行操作。\nmultiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較 threading 重點摘要 threading 是透過 context-switch 的方式實現\n也就是說，我們是透過 CPU 的不斷切換 (context-switch)，實現平行的功能。\n當大量使用 threading 執行平行的功能時，反而會因為大量的 context-switch，\n「實現了程式平行的功能，但也因為大量的 context-switch ，使得程式執行速度更慢」。\nmultiprocessing 重點摘要 multiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較圖 從下圖我們可以看到任務被完成的「概念」時間\nmain 1~4, main-end 任務 A1, A2 任務 B1, B2 任務 C1, C2 請留意圖中粗線的部分：\n在 multithread 中，\nCPU context-switch 會額外消耗我們程式執行的時間，程式實際完成時間可能比一般的還要慢。 在 multiprocess 中， 我們需要將資料轉移至其他 CPU 會額外消耗我們程式執行的時間，如果任務過於簡單，效益可能不大。 雖然示意圖中明顯感覺較快，但前提是任務夠複雜\n也就是說，「任務難度執行的時間 \u0026gt; 資料轉移至其他 CPU 的時間效益」，不然只會更慢。\nmultiprocess Pool 的使用 (with map) 這邊再提一次，這一篇文是上一篇文的進階版，若還沒有看過上集可以看這篇：\nhttps://wongwongnotes.com/posts/python/networking/python/python-multiprocessing/\n使用 multiprocessing Pool 可取得回傳結果，並讓系統自動分配資源，\n而使用 Process 不會回傳結果 (除非使用 mp.Queue 等等方式)\n而這邊我們執行 pool 內的任務的時候，使用「map」將任務一個個分配下去。\n範例程式碼 (multiprocess Pool) import multiprocessing as mp def task(num): return \u0026#39;The pool return result: \u0026#39; + str(num) if __name__==\u0026#39;__main__\u0026#39;: pool = mp.Pool() res = pool.map(task, range(10)) print(res) 運行結果 說明 pool = mp.Pool() 此外，Pool 內可以使用參數 processes = 「想要的 CPU 核數量」，\n例如：pool = mp.Pool(processes=4) ，就是我們指定了 4個 CPU 核。\n(預設就是 CPU 的對應核心數量)\nres = pool.map(task, range(10)) 以 map 的方式，將 list 中一個個的參數傳入，\n例如此處 range(10) 其實就等於 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]，\n傳入此 10 個任務，並得到對應的 10 個結果回傳。\nprint(res) 印出結果，注意結果都會被「存在一個 list 裡面」，\n如果想要單獨的結果，需要一個個拿出來。 (如上圖)\nmultiprocess Pool 的使用 (with apply_async) map, apply_async 都是能讓 pool 開始執行任務的方法，\n差別在於 map 可以傳入一個 list，\n而 apply_async 只能傳入一個 變數。\n範例程式碼 (multiprocess Pool) import multiprocessing as mp def task(num): return \u0026#39;The pool return result: \u0026#39; + str(num) if __name__==\u0026#39;__main__\u0026#39;: pool = mp.Pool() res = pool.apply_async(task, (0,)) print(res) print(res.get()) # use get() to get apply_async result 運行結果 說明 pool = mp.Pool() Pool 內可以使用參數 processes = 「想要的 CPU 核數量」，\n例如：pool = mp.Pool(processes=4) ，就是我們指定了 4個 CPU 核。\n(預設就是 CPU 的對應核心數量)\nres = pool.apply_async(task, (0,)) apply_async 的方式不同於 map，一次只能傳入一個變數\n例如：此處只能傳進 0，而得到的結果是封裝好的 applyResult，\n我們需要再使用 get() 來得到結果。\nprint(res.get()) 如同上述說明，res 是封裝好的 applyResult，\n我們需要再使用 get() 來得到 apply_async 的分析結果。\nmultiprocess Pool 的使用 (使用 apply_async 達到 map 的效果) 我們說 apply_async 只能一次執行一個，\n難道就不能讓他跑很多次，讓他有類似 map 的效果嗎?\n當然是可以的，把任務做成 list (迭代) 的樣子就可以了!\n範例程式碼 (使用 apply_async 達到 map 的效果) import multiprocessing as mp def task(num): return \u0026#39;The pool return result: \u0026#39; + str(num) if __name__==\u0026#39;__main__\u0026#39;: pool = mp.Pool() res_list = [] for i in range(10): res_list.append(pool.apply_async(task, (i,))) for res in res_list: print(res.get()) 運行結果 說明 可以看到結果都是一樣的 (輸出方式稍微改變一下我們先不管)。\n你可能會想問? 那 apply_async 與 map 到底差在哪????? 我們直接講重點：\n兩種情況各有各自使用的地方，請依照自己的需求選擇使用。\nmap：會等待 map 的任務執行完後，才執行接下來的主程式 apply_async：不等待 apply_async 的任務執行完，就會執行接下來的主程式 範例程式碼 - apply_async 與 map 的差別 以 map 執行數到 100 的任務 (會等待才做 main 下一行) import multiprocessing as mp def task(num): for i in range(num): print(i) return \u0026#39;The pool return result: \u0026#39; + str(num) if __name__==\u0026#39;__main__\u0026#39;: pool = mp.Pool() res = (pool.map(task, (100,))) print(\u0026#34;map wait the process finished.\u0026#34;) print(res) 執行結果 (只截重點，可以看到 map 等到任務完成才做) 以 apply_async 執行數到 100 的任務 (不會等待才做 main 下一行) import multiprocessing as mp def task(num): for i in range(num): print(i) return \u0026#39;The pool return result: \u0026#39; + str(num) if __name__==\u0026#39;__main__\u0026#39;: pool = mp.Pool() res = pool.apply_async(task, (100,)) print(\u0026#34;apply_async do NOT wait the process finished.\u0026#34;) print(res.get()) 執行結果 (只截重點，可以看到 apply_async 不會等到任務完成才做) 結論 我們知道 apply_async 與 map 的差別後，\n實際上，我們就看我們的任務，「main 有沒有需要等待才做下一行」，\n就可以決定我們要使用 apply_async 還是 map 囉!\nReference https://blog.gtwang.org/programming/python-threading-multithreaded-programming-tutorial/\nhttps://iter01.com/17324.html\nhttps://www.maxlist.xyz/2020/03/20/multi-processing-pool/\n","date":"2021-06-02T21:48:01+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/multiprocess-pool-python-5.png","permalink":"https://wongwongnotes.com/posts/python/networking/python/python-multiprocessing-pool/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #3】multiprocessing - 02 | pool, map, apply_async - 用多核心來執行程式並取得結果"},{"categories":["170 - Python 平行加速"],"content":"前言 在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式，\n若只是單純的平行需求，我們可以使用 threading 這個模組來快速完成平行處理的方式。\n但是 threading 只是透過頻繁的 CPU context-switch 的方式實現，\n要真正實現多核心 CPU 的平行運算，我們需要使用 multiprocessing，\n將任務指派給多個核心進行操作。\nmultiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較 threading 重點摘要 threading 是透過 context-switch 的方式實現\n也就是說，我們是透過 CPU 的不斷切換 (context-switch)，實現平行的功能。\n當大量使用 threading 執行平行的功能時，反而會因為大量的 context-switch，\n「實現了程式平行的功能，但也因為大量的 context-switch ，使得程式執行速度更慢」。\nmultiprocessing 重點摘要 multiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較圖 從下圖我們可以看到任務被完成的「概念」時間\nmain 1~4, main-end 任務 A1, A2 任務 B1, B2 任務 C1, C2 請留意圖中粗線的部分：\n在 multithread 中，\nCPU context-switch 會額外消耗我們程式執行的時間，程式實際完成時間可能比一般的還要慢。 在 multiprocess 中， 我們需要將資料轉移至其他 CPU 會額外消耗我們程式執行的時間，如果任務過於簡單，效益可能不大。 雖然示意圖中明顯感覺較快，但前提是任務夠複雜\n也就是說，「任務難度執行的時間 \u0026gt; 資料轉移至其他 CPU 的時間效益」，不然只會更慢。\nmultiprocess 基本使用 基本的 multiprocess 使用方式，跟 thread 幾乎一樣，\n如果學習過 multithread 的讀者，相信可以上手的很快。\n範例程式碼 (single-multiprocess) import multiprocessing as mp def task(a, b): print(\u0026#39;Task in the Process.\u0026#39;) print(a, b) if __name__==\u0026#39;__main__\u0026#39;: # must put thread in the main p1 = mp.Process(target=task, args=(1,2)) p1.start() p1.join() 運行結果 說明 p1 = mp.Process(target=task, args=(1,2)) 建立一個名字為 p1 的 Process，執行 task 任務，傳入參數 (1,2) - p1.start()：啟動 p1 任務 - p1.join()：等待 p1 任務結束 (一定會等到結束才執行下一行) 多個 Process 同時平行處理，「保證」任務「結果」的順序性 (multi-process) 我們「不保證」任務執行時，「過程中」輸出的順序，但完成「結果」的順序性可用 join() 來「保證」。\n範例程式碼 (multi-process) import multiprocessing as mp def task(num): print(\u0026#39;This is Process: \u0026#39;, num) if __name__==\u0026#39;__main__\u0026#39;: num_process = 5 process_list = [] for i in range(num_process): process_list.append(mp.Process(target = task, args = (i,))) process_list[i].start() for i in range(num_process): process_list[i].join() 運行結果 (注意：每次執行不一定相同) 注意：該輸入的都有輸出，但有些順序搶先輸出了，這也代表不同核心接到任務的順序。\n所以「每次執行不一定相同」。\n說明 process_list.append(mp.Process(target = task, args = (i,))) 建立 Process，存入，執行 task 任務，傳入參數 (i, ) - process_list[i].start()：啟動 process_list[i] 任務 - process_list[i].join()：等待 process_list[i] 任務結束 (一定會等到結束才執行下一行) 多個 Process 同時平行處理，「保證」任務「過程中」的順序性 (multi-process) 答：沒有必要\n如果是為了當任務「過程中」順序有高度要求時\u0026hellip;你可能要想想\n如果真要確保「過程中」照順序來，才做下一件事情，那你用 multi-process 到底要幹嘛XDD。\n直接寫就好了，不用想太多 multi-process 的事情!\n「又要多核心」、「又要平行任務」、「又要保證過程中的順序」，\n光是保證「保證過程中的順序」，你的 process 執行過程之間就會互相卡爆了\n還要效率不如直接不要平行了吧。\n所以這邊就不示範了，你可能要先想清楚：為什麼都用到 multi-process ，\n還需要保證任務「過程中」的順序性。(如果只是想確保執行「結果」的順序性，請見上面。)\n利用 multiprocessing 模組 查看自己的CPU「有多少核心」 我們可以利用 multiprocessing 模組內建的功能，\nmultiprocessing.cpu_count()，得到目前 cpu 的核心數量。\ncpu_count = multiprocessing.cpu_count() 結合上述的程式範例，製作出「依照 CPU 核心數執行任務」的範例程式碼模板 import multiprocessing as mp def task(num): print(\u0026#39;This is cpu core: \u0026#39;, num) if __name__==\u0026#39;__main__\u0026#39;: cpu_count = mp.cpu_count() print(\u0026#34;cpu_count: \u0026#34;, cpu_count) process_list = [] for i in range(cpu_count): process_list.append(mp.Process(target = task, args = (i,))) process_list[i].start() for i in range(cpu_count): process_list[i].join() 執行結果 (依照不同電腦的 CPU 能力而有異) 不過這樣的感覺很不踏實對吧! 感覺都要手動指定核心數量給 Process，\n能不能讓系統自動分配呢?\n當然是可以的，我們會再另外一篇文章 multiprocessing pool 教學進階的使用，\n使用 pool 就可以自動讓系統幫我們分配任務給多個核心，\n並且與 Process 最大的不同是「pool 能夠取得結果」。\nReference Python 多執行緒 threading 模組平行化程式設計教學\n【莫煩】Multiprocessing 多程式\n","date":"2021-06-02T12:47:16+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/multiprocess-python-4.png","permalink":"https://wongwongnotes.com/posts/python/networking/python/python-multiprocessing/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #2】multiprocessing - 01 | 用多核心來執行程式"},{"categories":["170 - Python 平行加速"],"content":"前言 在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式，\n若只是單純的平行需求，我們可以使用 threading 這個模組來快速完成平行處理的方式。\n使用注意：threading 是透過 context-switch 的方式實現\n也就是說，我們是透過 CPU 的不斷切換 (context-switch)，實現平行的功能。\n當大量使用 threading 執行平行的功能時，反而會因為大量的 context-switch，\n「實現了程式平行的功能，但也因為大量的 context-switch ，使得程式執行速度更慢」。\nthread 與 multiprocess 比較 threading 重點摘要 threading 是透過 context-switch 的方式實現\n也就是說，我們是透過 CPU 的不斷切換 (context-switch)，實現平行的功能。\n當大量使用 threading 執行平行的功能時，反而會因為大量的 context-switch，\n「實現了程式平行的功能，但也因為大量的 context-switch ，使得程式執行速度更慢」。\nmultiprocessing 重點摘要 multiprocessing 在資料傳遞上，會因為需要將資料轉移至其他 CPU 上進行運算，\n因此會需要考慮資料搬運的時間，\n而多核心真正的實現「平行運算的功能」，當任務較為複雜時，效率一定比較好。\nthread 與 multiprocess 比較圖 從下圖我們可以看到任務被完成的「概念」時間\nmain 1~4, main-end 任務 A1, A2 任務 B1, B2 任務 C1, C2 請留意圖中粗線的部分：\n在 multithread 中，\nCPU context-switch 會額外消耗我們程式執行的時間，程式實際完成時間可能比一般的還要慢。 在 multiprocess 中， 我們需要將資料轉移至其他 CPU 會額外消耗我們程式執行的時間，如果任務過於簡單，效益可能不大。 雖然示意圖中明顯感覺較快，但前提是任務夠複雜\n也就是說，「任務難度執行的時間 \u0026gt; 資料轉移至其他 CPU 的時間效益」，不然只會更慢。\nThread 基本使用 範例程式碼 (single-thread) import threading def task(a, b): print(\u0026#39;Task in the thread.\u0026#39;) print(a, b) if __name__==\u0026#39;__main__\u0026#39;: # must put thread in the main t1 = threading.Thread(target=task,args=(1,2)) t1.start() t1.join() 運行結果 說明 t1 = threading.Thread(target=task,args=(1,2)) 建立一個名字為 t1 的 Thread，執行 task 任務，傳入參數 (1,2) - t1.start()：啟動 t1 任務 - t1.join()：等待 t1 任務結束 (一定會等到結束才執行下一行) 多個 Thread 同時平行處理，「保證」任務「結果」的順序性 (multi-thread) 範例程式碼，「保證」任務「結果」的順序性 (multi-thread) import threading def task(num): print(\u0026#39;This is thread: \u0026#39;, num) if __name__==\u0026#39;__main__\u0026#39;: num_threads = 5 threads_list = [] for i in range(num_threads): threads_list.append(threading.Thread(target = task, args = (i,))) threads_list[i].start() for i in range(num_threads): threads_list[i].join() 運行結果 (注意：每次執行不一定相同) 注意：該輸入的都有輸出，但有些順序搶先輸出了，例如：「1 沒有跟在冒號後面」。\n所以「每次執行不一定相同」。\n說明 threads_list.append(threading.Thread(target = task, args = (i,))) 建立 Thread，存入，執行 task 任務，傳入參數 (i, ) - threads_list[i].start()：啟動 threads_list[i] 任務 threads_list[i].join()：等待 threads_list[i] 任務結束 (一定會等到結束才執行下一行，並且確保一定照 i 的順序結束) 此外，使用 Thread 本身就應該要確保，「任務內容的過程，沒有順序性問題」，\n多個 Thread 中共用一個變數計算，我們沒辦法保計算出來的結果一定合乎預期。\n.\n例如：有可能我們單一 thread 預期要對同一變數做「先加後乘」，我們執行兩個 thread，\n我們預期的順序是「加乘加乘」，但其實可能輸出是「加加乘乘」的結果。\n多個 Thread 同時平行處理，「保證」任務「過程中」的順序 (multi-thread) 這邊的示範，是為了當任務內容順序有高度要求時，\n我們必須進行以下處理，但勢必「因為 Thread 彼此等待，造成全部任務完成的時間會更慢」。\n如果有這樣的需求，甚至可以不用 Thread，\n因為有 context-switch 並不會比較快，甚至可能還會更慢。\n範例程式碼，「保證」任務「過程中」的順序 (multi-thread) import threading def task(num): print(\u0026#39;This is thread: \u0026#39;, num) if __name__==\u0026#39;__main__\u0026#39;: num_threads = 5 threads_list = [] for i in range(num_threads): threads_list.append(threading.Thread(target = task, args = (i,))) threads_list[i].start() threads_list[i].join() 運行結果 如此就能保證我們的順序性了。\n但這樣的寫法，不如就直接寫個 for-loop 跑 function 吧\n此方法用 thread 真的沒有比較快。\n只是能在做 thread 的過程中，能在主程式做一些其他事情。\nReference https://blog.gtwang.org/programming/python-threading-multithreaded-programming-tutorial/ ","date":"2021-06-02T03:01:34+08:00","image":"https://wongwongnotes.com/images/restored/2021/06/thread-python-2-1.png","permalink":"https://wongwongnotes.com/posts/python/networking/python/python-threading/","tags":["Python","python 平行運算","平行加速"],"title":"【Python 平行運算 #1】threading - 建立多執行緒來執行程式"},{"categories":["431 - MobaXterm"],"content":"前言 MobaXterm 是一個遠端的 terminal 連線軟體，\n透過 MobaXterm 我們可以連線至遠端的系統 (例如：linux, ubuntu)\n對於現在居家工作的我來說，透過 VPN 連線至公司電腦，\n使用 MobaXterm 這個軟體，就同時能滿足 「terminal」 與「檔案總管」的兩種需求!\n準備 VPN 的部分 準備 VPN 工具 - OpenVPN step 1. 下載 OpenVPN 軟體 OpenVPN 下載網址：https://openvpn.net/community-downloads/\n我們要下載的是 windows-64 位元 的檔案 (通常現在比較新的 windows 電腦都是這個，不是請自行調整)\nstep 2. 安裝 OpenVPN 軟體 安裝完即可。\n取得 vpn 設定檔案 (副檔名為 .ovpn 的檔案，通常會由企業或學校機構提供。) 總之，這邊幫不了忙XD\n請向你的 企業 或 學校機構拿到一個「副檔名為 .ovpn 的檔案」。\n連線至 VPN step 1. 匯入 「.ovpn」 設定檔 從右下角選單「右鍵」OpenVPN，選擇「匯入設定檔」，\n將剛剛上一個步驟中的「.ovpn 的檔案」匯入。\nstep 2. 透過 VPN 連線至遠端 從右下角選單「右鍵」OpenVPN，選擇「連線」即可。\n(連線時，也請向「企業」或「學校機構」取得連線使用的帳號密碼)\nMobaXterm 的部分 下載 MobaXterm step 1. 前往 MobaXterm 官方網站的下載頁 MobaXterm 官方網站的下載頁： https://mobaxterm.mobatek.net/download.html 點擊 Free 下方的「Download Now」，\n如果需要更多高級的功能，可以再考慮使用 Professional Edition (以初學者來說 Free 夠用)\nstep 2. 下載 MobaXterm 我們選擇左方的「Portable edition」，點擊下載並安裝即可。\n正式使用 MobaXterm 前置步驟 (連上 VPN) 準備完，終於要進我們的重頭戲了!\nstep 1. 設定遠端連線主機 按左上角的「Session」，新增「host」為「連線主機名稱與ip」，\n(因為有連 VPN 所以偵測的到。)\nstep 2. 點擊左側即可連線 記得輸入對應主機的帳號密碼~\nMobaXterm 主要特色 快速登入遠端主機 第一次登入之後，第二次有紀錄的話，\n點左側就可以直接快速連線，非常方便!\n左側可以像檔案總管一樣，直接檢視文件。 點擊也可以直接開啟 這邊僅做示範，總之就是可以直接用檔案總管的方式看到裡面的檔案。\n此外，下方還有一個 Follow terminal folder 可以勾選，\n勾選後，檔案總管的內容，會跟著「terminal 當前資料夾」進行資料夾內容的變化。\nX11 forward 功能，直接顯示視窗在本地端 這個我們使用 cv2 的 imshow 來進行示範，\n在 opencv 的 cv2.imshow 功能中，\n我們本應該要將畫面「輸出至遠端主機上」，\n但因為有了「 X11 forward 的功能」，我們能將畫面從遠端拉回來，\n顯示在自己的主機上。\n如下圖，我們可以看到顯示 cv2.imshow 的視窗，是「windows 介面的視窗」\nReference https://www.asus.com/tw/support/faq/1004469 https://it001.pixnet.net/blog/post/350223902-it%E5%A5%BD%E7%94%A8%E8%BB%9F%E9%AB%94%E2%80%93-mobaxterm%28%E9%81%A0%E7%AB%AFterminal%E9%80%A3%E7%B7%9A%E8%BB%9F%E9%AB%94%29 ","date":"2021-05-31T19:27:43+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/mobaxterm-5-2.png","permalink":"https://wongwongnotes.com/posts/dev-tools/terminal/mobaxterm/mobaxterm-windows-linux/","tags":["MobaXterm"],"title":"【MobaXterm #1】在 Windows 中使用 MobaXterm VPN 連線至遠端系統 (linux, ubuntu) 遠端 ssh 顯示畫面"},{"categories":["016 - Gather town"],"content":"前言 隨著台灣疫情的升溫，許多企業都漸漸開始轉換形式變為「在家上班 (work from home, WFH)」的形式，\n國外有個設計團隊，在去年這年大家都受到的影響過程中，\n開發出了一款「gather town」這個應用!\n我們可以建立自己的小人物，並且建立自己的「辦公室/遊樂場」，\n即使是遠端也能有如同真實生活中互動的感覺!\n個人覺得這個設計解法非常有趣，因此分享給大家 (並沒有收到任何業配，所以我只記錄真實感受XDD)\n立旗 with 疫情加油! 然後來立旗一下，這篇文章如果 like 每破百一次，我就多產一篇進階教學文!\n(也就是如果 like 真的破萬，我就產進階教學文 100 篇啦!)\n如果覺得我寫得不錯的話，歡迎在文章最底下「用 like (按讚) 轟炸我」 (一人最多五次XDD)\n希望疫情趕快過去才是真的，這篇文章也是因為疫情的關係才能撰寫的，但期望如果沒有這樣的需求才是好的!\ngather town 簡介 gather town 官網：https://gather.town/ 「gather town」的核心理念就是將每個人化作自己的分身，\n在虛擬的世界進行互動，比起 google meet 的單純會議用途，\n這個軟體的設計將辦公的環境，提升到了連「人與人連結」的感覺都做出來了XD\n初次使用 step 1. 建立一個自己的空間 在剛剛的官網中，可以從右上角的 Launch Gather 進入到以下畫面。\n這時候會分成兩種人，簡單說就是「活動空間的建立者」，與「參與活動空間的人」\n我們示範建立一個空間，我們按下圖中的 「Create a new space」，\n可以看到已經存在很多的範本可以給你選，但也可以選擇空白的，完全從零打造自己的辦公室!\n也可以直接做成跟上班環境一樣布置的辦公室，大家都會比較習慣XD\n這邊我們選第一個作為範本，會跳出右邊的視窗，進行空間的相關設定。\n這邊設定就看個人了，如果要參考圖文說明可以見下圖：\n第一次申請可能會要求登入 (可以不登)，但沒有綁定帳號的空間很容易消失!\n登入畫面大概長下面這樣：\n(可以直接綁 google! 也是挺方便的!)\nstep 2. 建立自己的虛擬角色 這邊想必大家創意一定最多了!\n發揮自己的美感建立自己的角色嗎!\n系統預設已經給得滿多了，不過我猜可能還是會有人覺得不夠多XDD\nstep 3. (準備視窗) 確認自己的麥克風、視訊相關的設定 這邊讓大家最後確認自己的相關設定，\n包含自己的視訊、麥克風等等相關的設定，也可以選擇關閉。\n選擇黑色畫面裡面的麥克風與攝影機就可以開關了。\n進入活動空間 - 新手教學篇 咦，怎麼跑到的地方不像是我剛剛建立的樣子?\n原來，第一次來還要先在無人島上跑新手教學啊!\n基本上 gather 預設的新手教學已經做得很好了XD，\n這邊就使用 gather 的新手教學，再次說明該怎麼操作。\n已經很習慣了請直接左側「Skip Tutorial」。\nstep 1. 移動 鍵盤的 「WASD」 或 「↑↓←→」都可以移動人物，\n我們的第一個任務是「將人物移動到藍色框」中。\nstep 2. 控制 關閉/開啟 麥克風 第二個教學是教你怎麼樣「關閉/開啟 自己的麥克風」，\n在同一個地方也可以同時「關閉/開啟 自己的攝影機」。\n我們可以移動到右下角，也就是代表自己「目前向他人顯示的視窗」。\n往後開始與人互動也都是同樣的操作哦! 這個「開關 麥克風/攝影機」很重要請務必記得!\n△ 圖示應該很明顯了XD 就不多做說明 (點一下 icon 改變設定)，這個畫面也代表自己的狀態，可以隨時確認! step 3. 與場景物件互動 在 gather town 裡面，要實現他的種種特別功能就是要靠與場景物件互動!\n我們可以使「X」來與場景物件互動。\n這個任務就是要你去與物件互動，按「X」拉下桿子，\n會跳出一段場景互動的說明，再按一次「X」結束互動，\n可以開啟回到原來活動空間的傳送門。\n△ 還特別準備一個傳送門，挺可愛的! 到此，也結束了新手教學，夠簡單吧XD。\n進入活動空間 - 正式使用篇 主介面介紹 直接來圖文說明吧! 不過相信大家在新手教學的階段應該都學得差不多了!\n左邊是主選單 右下角是自己的顯示視窗。 中間下面有三個設定：分別為 小地圖、分享螢幕畫面、顯示表情符號 小地圖：可以快速查看地圖上的自己現在在哪 (快速找現在自己的位置必備!) △ 這個小地圖做得很可愛! 很有遊戲的感覺XD 分享畫面：可以分享自己的螢幕畫面 (線上會議必備功能!!) △ 分享螢幕畫面後，可以點擊視窗直接看到對方的畫面哦! 顯示表情符號：就顯示你現在的心情吧XDD (也可以拿來投票，覺得滿實用的!) △ 在頭上顯示表情符號，也許可以當個另類的投票功能XD 邀請朋友 step 1. (建立空間的人) 傳送邀請連結 我們可以從左上角的查看現在線上人數那邊進行發邀請的動作，\n分為兩種形式：\nemail 邀請：透過 email 把連結丟給你朋友 連結邀請：直接把連結丟給你朋友 然後每一個連結都可以決定失效的時間，也可以選擇永久不會失效。\nstep 2. (想要參加空間的人) 點選邀請連結 如果有需要輸入密碼的話，會出現類似這樣的畫面，\n建立空間的人記得也要提供空間的密碼哦!\nstep 3. 進入空間 兩個人同時在同一個空間的時候，就會有這樣的畫面。\n△ 沒朋友的我只好自己另外一個視窗跟自己測試QQ (誤 空間互動細節介紹 - 如何判斷「進入對方的互動範圍」 自由空間篇 基本上有兩種方法都可以進行判斷\n- 我們可以看上面有沒有跳出「對方的視訊視窗」 - 我們可以看對方在地圖上「是不是透明的」 △ 如果看到對方在圖上是透明的，表示是不可互動的狀態 另外，個人測試在「自由空間」時，兩個人能互動的範圍大概在「4-5 個人物的距離」，\n也就是說，要講話還是靠近一點講啦XDDD (好像是廢話但還是要講\n私密空間篇 私密空間分很多種，可以是「會議室」、「教室」，\n也可以是「一個餐桌內」、「一個沙發區」、「一個籃球場」內，\n這部分在進階教學裡面我會再詳細說明，這邊我們先就「預設」的來講。\n(前提是這篇文章回饋夠好，我才有動力寫下去XDDD)\n進入私密空間後，會跳出如以下的訊息\n「You have entered a private space」\n「餐桌」範圍的私密空間 △ 像這張圖中，就是這個餐桌內的範圍，才聽得到彼此說話。 「辦公桌」範圍的私密空間 △ 像這張圖中，就是這個辦公桌內的白色範圍，才聽得到彼此說話，夠像辦公室了吧XD 「會議室」範圍的私密空間 △ 在會議室中，可以溝通的範圍會比「自由空間」大，全會議室的人都聽得到彼此說話。 個人認為相比 google meet 或其他視訊會議 的優缺點 這邊就是個人使用上「主觀」的優缺點了，\n由於一方的優點也代表另一方的缺點，所以我就只寫各自無法取代的長處!\n就看大家再自行斟酌囉!\ngather town 無法被取代的優點 使用習慣：重複使用、長期、經常性會議 現實人物角色化，互動極度貼近現實，甚至連辦公室/教室都可以完全擬真! 在不同會議室的切換極度快速，不用特別換網頁。 可以從旁就知道目前該人物在什麼會議中，與誰正在進行會議。 如果有需要，也可以自行建立「用餐中」、「廁所」、「外出」等空間，幾乎是不像 google meet 有時會有找不到人，也不知道對方可能在幹嘛的情形。 內建辦公室紓壓小遊戲(是優點也是缺點?) google meet 無法被取代的優點 使用習慣：單次、拋棄式會議 (要再開會議，就開新的會議室) 完全免費，不限制時間 (限制時間這點我不確定 gather town 有沒有，但 gather town 要更多人參與的功能需要付費，可見於 gather town pricing) 強大視訊去背功能 (對於背景很亂的房間，又不想要給別人看很重要!!) 進入會議室前可以檢視自身畫面 (gather town 只有一開始可以，或者可以躲去沒人看得角落檢查視訊XD) google 就大(公司)，相對來講可信任度更高(隱私方面、機密方面)，但這點也能說見仁見智XD gather town / google meet 共同優點 可以分享螢幕畫面 (ppt) 可以開關麥克風/攝影機鏡頭 都有手機板/網頁版/桌面應用 (gather town 的手機板聽說還有待優化的空間) 題外話 進入空間後，會跳一個小提醒，是關於我們個人隱私的保護，\n他說如果我們不在一段對話之中，會自動關閉我們的麥克風與攝影機，\n(太貼心了吧!)\n但如果是那種需要長時間在線上的公司，還是要去設定裡面，\n將麥克風/攝影機設定為「不自動關閉」哦!\n","date":"2021-05-28T19:53:40+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/gather-town-15.png","permalink":"https://wongwongnotes.com/posts/life-and-work/reviews/gather-town/wfh-gather-town/","tags":["Gather","town"],"title":"【全圖文說明】在神奇的 gather town 線上群聚囉! gather town 的基本使用，在家上班 WFH、居家辦公也能有如同在辦公室與同事見面的感覺!"},{"categories":["370 - Ubuntu"],"content":"前言 有時候 ubuntu 自動更新一直跳出來很煩XD，\n本文提供兩種方式關閉自動更新，\n分別為「GUI 圖形介面的方法」、「command line 的方法」，\n並附上詳細的圖文說明。\n「GUI 圖形介面的方法」關閉自動更新 Step 1. 搜尋 software 找到「Software Updater」 我們點擊左下角開啟所有的應用程式，我們可以搜尋 software 找到以下應用「Software Updater」 Step 2. 修改下方兩個值「Never」、「Display immediately」 我們開啟「Software Updater」後進行以下的調整， 第一個圈選處改成「Never」，表示我們不再 check update 第二個圈選處改成 「“Display immediately” 取代 “Download and install automatically”」，表示我們顯示重要更新。但不自動下載更新。 「command line 的方法」關閉自動更新 Step 1. 修改以下檔案 此檔案包含著更新相關的設定。\nsudo vim /etc/apt/apt.conf.d/20auto-upgrades Step 2. 將 ”1” 都改成 “0” 我們一共要改兩個值，0 就是取消的意思。\n第一個值是不自動 check update 第二個值是自動安裝 update APT::Periodic::Update-Package-Lists \u0026#34;0\u0026#34;; APT::Periodic::Unattended-Upgrade \u0026#34;0\u0026#34;; 如下：\nReference https://itsfoss.com/auto-updates-ubuntu/ ","date":"2021-05-06T14:31:41+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/ubuntu-update-1-1024x203.png","permalink":"https://wongwongnotes.com/posts/linux-shell/ubuntu/ubuntu-stop-auto-update/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】ubuntu 停止自動更新的方法 (內附圖文說明)"},{"categories":["112 - Python 進階語法"],"content":"前言 python 中有個內建函數為「map」，\n他能夠快速的幫助我們將「list 的全部內容」完成「對應的 function」\n老實說，其實不會「map」完全還能夠寫 python 的程式XD\n新手可以先不用急著學 map 的用法也沒關係，還是有其他方法能取代 map 的功能 (內文也會示範)\n比較 \u0026amp; 範例程式碼 因為 map 功能相對單純，我們先直接來看例子，\n讀者可以自己觀察判斷看看能不能理解！\n可先觀察結果，下面再來慢慢說明 def test(x): return x*2 mylist = [1, 2, 3, 4, 5] ans_list = [] for ele in mylist: ans_list.append(test(ele)) print(\u0026#34;test with for and basic list method = \u0026#34;, ans_list) print(\u0026#34;test with for in list = \u0026#34;, [test(ele) for ele in mylist]) print(\u0026#34;test with map = \u0026#34;, list(map(test, mylist))) 實驗結果 程式碼詳細說明 最新手的時候，「for 搭配 list 一個一個放」的寫法 這個方法非常陽春，最剛開始學的時候大家應該都這樣寫過XD\n直接宣告一個空的 list，慢慢的把執行完的結果也放進去XD\ndef test(x): return x*2 ans_list = [] for ele in mylist: ans.list.append(test(ele)) print(ans_list) 稍微學會一點的時候，「直接使用 for in list」的寫法 這個寫法就看起來高級一點了，直接一行的 for 放在 list 裡面，\ndef test(x): return x*2 print(\u0026#34;test with for in list = \u0026#34;, [test(ele) for ele in mylist]) 學會 map 的時候，「map and list」的寫法 這就是傳說中的邏輯加密技術，其實不會這個完全沒有問題XDD\n就只是會這個可以讓程式碼看起來會再更簡潔一點 (相對也更難懂XD)\n你可能會問，那 map 存在的必要性還有什麼?\n基本上會牽扯到「執行的效率問題」，但因為這不是本文的重點，有興趣的讀者可針對此關鍵字另外找有關「map 執行效率」的文章\ndef test(x): return x*2 print(\u0026#34;test with map = \u0026#34;, list(map(test, mylist))) map 詳細說明 看 map 函數時，請先抓出逗點，\n抓住一個概念「map(function, list)」，\n所以看上面的例子，map(test, mylist)\ntest: 就是我們的 function 名稱 mylist： 就是我們的 list (其實只要是 iterable object 都可以！) 注意一個重點！ python3 之後 map 回傳的是 「map object」，請用 list 再轉成你看得懂的型態 python3 之後 map 回傳的是 「map object」，\n如果直接 print，我們大概會看到如以下所示的內容：\n\u0026lt;map object at 記憶體位置\u0026gt; 所以這時候我們需要多使用一個「list」 進行轉換！才能變成我們看得懂的內容！\nlist(map(function, list)) map 結合 lambda 使用 這更是傳說中的邏輯加密技術，簡單來講就只是讓你的程式碼看起來更炫而已XDD（誤\n其實不用學這個還是能寫 python 的! 會了這個就能將程式碼寫得更簡略。\npython lambda 教學可以看我的另外一篇文章： https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-lambda/\n以上面例子繼續示範「map 結合 lambda 使用」範例程式碼 mylist = [1, 2, 3, 4, 5] test = lambda x: x*2 print(\u0026#34;test lambda and map = \u0026#34;, list(map(test, mylist))) 甚至是\nmylist = [1, 2, 3, 4, 5] print(\u0026#34;test lambda in map = \u0026#34;, list(map(lambda x: x*2, mylist))) 輸出都是一樣的結果哦！\n有沒有覺得一個簡單的功能看起來更難懂了呢（喂，\n使用這個時，請注意「程式可讀性」，如果與別人合作開發，\n請不要寫出可能只有你看得懂的程式碼哦XD！\n所以才說這個其實也不用學XD，上面還有很多更簡單的方法，還不用多記呢XD！\n結論 不過最後強調一下：使用 map 依然是有他的必要性存在的，這牽扯到「python map 執行程式的效率問題」\n但因為這不是本文的重點，本文只想對用法作個筆記而已\n有興趣的讀者可針對此關鍵字另外找有關「python map 執行效率」的文章 (或等我哪天有空想寫XD)\n但不太建議新手或對 python 還不熟的同學使用，很容易把自己搞得更亂。\n特別是因此而導致「程式可讀性的下降」，那是更不好的結果。\nReference https://www.runoob.com/python/python-func-map.html ","date":"2021-05-03T18:33:20+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/python-map-2.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-map/","tags":["Python","進階語法"],"title":"【Python 進階語法 #3】python map - 使用方法 與 其他寫法比較整理"},{"categories":["Popworld 蹦世界"],"content":"實境解謎簡介 由「Popworld 蹦世界」所出品的《奇憶北投》是一款主題在北投的「實境解謎遊戲」，\n「實境解謎」主打可以去實際景點旅遊，又能夠同時體驗解謎的樂趣。\n也就是說不同於密室逃脫，我們主要的活動都在室外，如果喜歡解謎又不喜歡待在室內，\n或者很喜歡旅遊又想增添一些趣味性，都很適合來玩這樣的遊戲!\n小吐槽一下XD，雖然說是「北投」，但活動地區基本上都在「新北投」附近XD\n雖然意思是一樣的XD，但對我們這種只知道捷運站的學生來說，一堆人都會跑錯站XD\n△ 購買遊戲的內容物，送的車票紀念悠遊卡十分可愛！這次的故事叫做「北投憶誌」! 喜歡實境解謎嗎？作者這裡也推薦一些有趣的實境解謎心得文，歡迎參考哦：\n遊戲相關 以下是自己的玩完後的一些心得，老樣子，有優點、有缺點都會說，\n「沒有爆雷」的部分，留給還沒遊玩的朋友們，(除了大約的行走距離提示XD)\n一切都為「個人\u0026amp;團隊成員」們的「真實體驗」，絕沒有多加修飾XD\n題目類型：「文字為主，結合 AR 與實景、文本互動」 這款遊戲主要的題目類型幾乎都是「文字為主，結合 AR 與實景、文本互動」，\n超適合喜歡「閱讀文字」的朋友們，以及想體驗「AR 結合實際地景」的表現！\n如果你是「不喜歡閱讀」大量文字的朋友，\n偷偷告訴你其實不用看那麼多字也都解得出來啦XDD\n題外話：如果你是「討厭數學」的朋友，這款只有一題，還不是很難的數學哦^ ^\n小提醒: 因為解謎會需要用到 GPS，記得「開啟手機 藍芽 與 wifi」幫助定位哦! (不用連接，開著就好)\n△ 因為「收不到 GPS 訊號」被隊員放生的我，請務必記得「開啟手機 藍芽 與 wifi」幫助定位哦!\n題目數量、關鍵地點分佈位置 - (無雷，有稍微提到行走距離替還沒玩的人打預防針XD) 主要題目一共有7題，分散在「新北投捷運站」附近的七個地方，\n整體而言，遊玩的過程覺得非常的輕鬆，幾乎都是在附近可以走到的距離，\n除了某一關會需要跑到距離稍遠的地方~ 其他都在很適合邊走邊逛的距離。\n只有其中一站要稍微走遠一點路，但畢竟是那附近都算是山路，\n對於純享受遊玩樂趣的人來說，爬山路可能會破壞一點遊玩的舒適度XD\n△ 這個是官方附的地圖，除了很精美之外，實際解謎也不會超出這個範圍哦! 解題限制時間: 無限制 (一般人大約 2.5 小時) 既然是一款主打「實境解謎遊戲」，\n也就是說我們可以瘋狂解題，拚最佳解題時間，(最快好像看到有人兩個小時以內)\n也可以不用那麼拚解題時間，慢慢觀光，邊觀光邊享受解題的樂趣。\n這款很輕鬆、給小朋友都能玩，最大的特色我會放在 「AR 結合實際地景」的表現！\n以目前玩過的幾款遊戲來說，「Popworld 蹦世界」相對別家更著重運用「AR 結合實際地景」，\n來表現出當地的特色景觀。\n相比其他家，會藉由「文字、圖片」作為媒介帶你們去逛地景，\n但果然還是「AR 結合實際地景」的場地帶入感會明顯的強更多！\n謎題難度(解題需要的背景知識): 新手適中(3/5)，老玩家中間偏易 (2/5) 如果整團都是新手：難度 (3/5)，新手可能需要習慣一下遊玩這遊戲的邏輯。 因為畢竟是第一次玩，可能還沒有習慣這種室外解謎的玩法，不過習慣一下也能夠快速上手囉! 如果團中有老手：難度 (2/5)，其實題目沒那麼難，答案也相當明顯XD。 如果你是高難度解謎控... 那遊玩這款重點就放在「AR 結合實際地景的互動」 吧！ (必須說這款真的不難，很多題目其實提示也相當明顯，瞎猜都猜得到那種XD) 有時候不太想玩太燒腦的，又想要實際逛逛該地點，最適合玩這類不用太燒腦的遊戲！\n還有「AR 結合實際地景的互動」這個酷東西! 難度什麼的不重要啦(誤\n謎題難度(解題過程夠不夠直覺): 中間偏易 (1~2/5) 大部分題目都是「簡單直覺」的，甚至不太要動腦XD，\n(真的想不出來，通常猜也猜得出來！不過亂猜小心結算被扣分XD！)\n題目的難度設計，家裡的小朋友稍微想一下也都能解的出來哦!\n非常適合整個家庭一起玩!\n題外話：有幾關的設計，個人覺得可以出題可以再稍微優化會更好XD\n例如：數學那關 跟 最後一關 (有被誤導的可能，雖然這可能也是當初題目故意這樣設計XD)\n謎題驚喜度：「AR 結合實際地景的互動」設計讓人驚喜 (5/5) 很多結合實景地點的互動，我非常喜歡這樣的設計！\n特別是其中有些關卡的「AR 結合實際地景的互動」，是在其他家遊戲中都沒有機會體驗到的!\n△ 用手機的 AR 結合實際地景的互動，app 也有設計拍照功能，可以先拍好去陰涼的地方解謎! (圖中為與我們競爭的另外一隊可愛小組，認真解謎中!)\n謎題故事性: 解謎當下我沒看故事就解完這款XD，但後來回顧故事內容卻非常豐富!!! (4/5) 老實說這款不用在「當下」就把全部故事看完也能解開 (應該不會有人想當下看完「50幾頁」的文字XDD)，\n但後來回顧故事內容的時候，能深刻感受到「製作團隊撰寫這些故事的用心」，\n這邊小扣一分XD，「如果能在解謎的當下，對整個故事有更強的帶入感就更棒了!」，\n因為我們這隊大部分人解完遊戲後，還不知道發生了什麼故事XDD，\n這部分我覺得真的很可惜，畢竟好不容易有了「50幾頁」左右的豐富內容，就這樣在結束遊戲後被成員忽略了XD\n△「50幾頁」左右的豐富內容，喜歡看故事的應該可以看得很過癮! 一些個人的小建議 (1) 對於「高難度」解謎熱衷的玩家，這款的難度應該沒辦法滿足XD 這點比較是對於「高難度」解謎熱衷的玩家打預防針XD，\n這款我覺得更著重在遊玩「AR 結合實際地景的互動」，\n畢竟都出來跑「實境解謎」了，我覺得「以旅遊為主，解謎為輔」的節奏才是更棒的!\n都出來玩了，不用花心思在苦惱解一些「非常困難」的問題，不是很棒嗎!\n(2) 建議中途可以新增「吃飯暫停」的功能，畢竟大家也要吃飯但又怕影響成績 XD 這邊其實也算是因為有遊玩過別家的經驗，\n當時有印象深刻有一個「吃午餐時間」的功能也不影響計分，\n這樣其實可以讓玩家們很「安心的吃飯」，也可以「好好的稍微中場休息」一下。\n會非常建議能加入這個功能哦!\n(3) 有一關稍微跑得有點遠，卻只有那一關有點可惜 那附近畢竟也算是山路，其中某一關需要跑到稍微有點遠的地方，\n但在那附近解完那一關卻又要回頭了XD，個人覺得有點小可惜，\n(可能也因為剛好跑到那附近的景點不多XD)\n(4) 故事很豐富，可惜劇情帶入感差了一點 (讀者不用理解故事也能解完所有謎題，這樣是好也是壞XD) 這個我想稍微幫製作組平反一下XD，製作組的故事明明寫的超用心的!!!\n稍微少了一些謎題可以引導我們去閱讀裡面的內容\n要全破這遊戲可以不用理解故事也能解完所有謎題，這樣是好也是壞XD\n好是對玩家方便，壞是可惜了這些內容。\n這樣豐富的內容稍微可惜了! 很多朋友也許解完就不會再看這些故事劇情了，\n但製作組用心寫的長篇故事都沒機會看XD，會很可惜錯過了這些用心的內容!\n可以用謎題引導我們去看故事解謎，這樣也不會白費這些內容豐富的完整故事。\n-\u0026gt; 補充: 一些我很喜歡的出題團隊「小心思」~ (1) 用心的細節 - 遊戲結束後的 AR 彩蛋 作為一款主打「AR 實境解謎」的遊戲，果然最後的獎勵也與「AR 有關」可以讓大家合影XD\n我覺得很棒!!! (除了有些上面的台詞有點瞎之外(誤\n△ 全部解完後，可以使用官方提供的特製 AR 特效框，進行通關拍照 (畫質的部分\u0026hellip;嘛XD)\n(2) 用心的細節 - 來自官方答錯的吐槽 老實說我特別喜歡這種官方吐槽，其實也能夠表示官方很用心都有想到XDD\n但是，你們的國文老師才「在」哭啦XD\n△ 來自官方的答錯吐槽，你們的國文老師才「在」哭啦XD\n(3) 用心的細節 - 外包裝的袋子，可以變身為「可攜帶式的 L夾」 其實我覺得這設計滿棒的! 既可以當袋子又可以當 L夾!\n不過\u0026hellip; 我比較喜歡保持物品完整性，所以就沒撕開了XD\n△ 外包裝的袋子是可以變身成「L夾」的哦！很喜歡這種小細節的設計！\n(不過本人因為不想破壞完整性，就沒有特別撕開了XD)\n-\u0026gt; 玩完後想推薦給什麼樣的人：「體驗 AR 與實景互動」、「旅遊為主，解謎為輔」的朋友!!! 推薦給想「體驗 AR 與實景互動」或「旅遊為主，解謎為輔」的朋友!!!\n這款謎題難度適中，不是要認真要難倒你那種，很適合「慢慢邊旅遊，慢慢解XD」\n說實話，這款遊戲對於「高難度解謎控」一定是沒辦法滿足的XD，\n但是「體驗 AR 與實景互動」就是一個很酷的體驗!\n與實景的「代入感」，比起單純的文字、圖案又更加強而讓人融入其境!\n題目的設計，都有感受到製作團隊的用心。(這次我們還買了兩組來「兩隊競爭」XD)\n△ 我們買了兩組來兩隊競爭！擺起來內容感覺真的滿豐富的!! 既然是主打「實境解謎遊戲」，觀光也是重點中的重點，\n新北投附近有很多適合逛與休息的地方，比起台北市來說是個相對悠閒的地方，\n很適合假日跑來這邊放鬆 (先別跟我吐槽新北投離台北市中心太遠XD)，\n如果不是為了拼最佳排名，很建議跟著題目的腳步一起玩過新北投哦！\n推薦這款 Popworld 蹦世界 -《奇憶北投》給想要來新北投附近逛逛的朋友們!\n如果在找密室逃脫的遊戲評價，這裡還有一些當初我自己有參考的密室逃脫心得文，也歡迎一併參考：\n-\u0026gt; 題外話：「Popworld 蹦世界」的 app 使用居然成為我們最難的一關XD，這邊附上教學 因為沒有收到教學XD (可能因為我們調皮，買了之後隔天就說要玩XDD)\n「Popworld 蹦世界」的 app 使用居然成為我們最難的一關XD，\n這邊附上我們自己試出來的教學文XDD\nStep1. 買遊戲的人，請分享購買序號給你的朋友們。 點選右下角，選擇「設定」 購買遊戲的人，選擇「購買管理」 不是買遊戲的人，要領取序號，選擇「兌換代碼」 Step2. 回到遊戲畫面，點選「Get」後下載遊戲，之後就可以按「開始」。 上一步完成後，回到遊戲畫面 點選「Get」後就會下載遊戲，之後就可以按「開始」進入組隊畫面。 Step3. 隊長選擇「建立隊伍」、隊員選擇「加入隊伍」。 這邊沒什麼好說的XD\n隊長選擇「建立隊伍」 隊員選擇「加入隊伍」，輸入隊長的「房間號碼」 Step4. 等待隊員都進來後，就可以按「開始遊戲」囉。 上一步中，隊長的「房間號碼」在我們圈起來的地方。 Step5. 出發囉!!!! 其他團體遊戲推薦：\n","date":"2021-05-03T01:38:09+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/IMG_8453-1-473x1024.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/popworld/beitouevent/","tags":["Popworld 蹦世界","嗡嗡粗門玩","實境解謎","蹦世界"],"title":"【實境解謎】《奇憶北投》遊玩後心得 (無雷) - 解謎遊戲推薦 | Popworld 蹦世界"},{"categories":["442 - Sublime Text"],"content":"前言 這篇要快速分享一下 Sublime 中的快速整理 Python 縮排小技巧，\n非常的爛，但卻非常的實用!\n你可能會想問為什麼要這樣做? 因為很多時候在 terminal 用 vim 修改程式碼，\n這時如果當初適用 tab 來控制縮排，在 vim 修改程式碼會非常痛苦，\n這時候我們先進行這篇文章的操作，會方便非常多!!!\n使用前請先完成我另外一篇文章的前置步驟 - 傳送門如下 請先完成以下文章的前置步驟哦!!! 【Sublime】Sublime 將縮排 “tab” 改成 4格空白 的方法 (圖文說明) sublime indent 4 spaces\nhttps://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-tab-space/\n快速從 tab 轉換成「4格空白」的方法 很簡單，將你那充滿 tab 的 python code 複製，\n直接開一個新的 sublime 貼上，因為「前置步驟」的設定，\n就全部都改完囉!!!\n範例圖 1 - 原來 tab 的 python code 範例圖 2 - 開新檔案，直接複製貼上，原來 tab 都轉成了「4格空白」!! ","date":"2021-05-02T01:19:07+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/222.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-tab-to-space/","tags":[],"title":"【Sublime】Python 縮排小技巧 (很爛但實用) 快速將 tab 改成「4格空白」的方法"},{"categories":["181 - Python Chatbot"],"content":"前言 這篇是專門寫利用 ngrok 架設本地伺服器的設定方法 (LINE bot local server)，\n如果想要設定的是雲端伺服器，可以參考另外一篇 heroku 雲端伺服器服務的設定教學。\n本地伺服器的好處就是方便 debug，缺點大概就是電腦要一直開著! (要開著才能提供服務啊!)\n但雲端伺服器 debug 就比較麻煩一些 (還需要經過上傳)，不過好處也就是電腦就不用一直開著囉XD!\n「line webhook URL 修改傳送門」：https://developers.line.biz/console/ 因為使用這個方法實在太常用到了，這邊也放一個，只要更新、重開 ngrok 都會用到。 ngrok 說明 ngrok 是一種將本地伺服器「映射到網際網路」上的方法(工具)，\n你可以想像成我們本地的電腦本身就可以當一個伺服器，\n例如：「http://127.0.0.1:5000」、「http://localhost:5000」，\n但因為只有在「自己的電腦上看得到」，所以我們才需要一個工具，\n將這個只有在只有在「自己電腦上看得到」的伺服器，映射到網際網路上。\n這樣就可以方便我們快速開發囉! (因為實際上我們都是在「本地端操作」)\n你可能會想問那 ngrok 的缺點是? 其實也很簡單，我們都說了我們的伺服器是只有在「自己的電腦上看得到」，\n所以，「電腦只要關機了」就沒有服務了。\n另外，免費版的 ngrok 只有「8 小時」，超過時間其實重連就可以繼續用\u0026hellip;\n但「映射網址」會被換掉，這點要注意，表示我們要去更換「line webhook URL」。\n「line webhook URL 修改傳送門」：https://developers.line.biz/console/ 因為使用這個方法實在太常用到了，這邊也放一個，只要更新、重開 ngrok 都會用到。 step 1. 先去 ngrok 官方網站下載 ngrok 主程式 ngrok 下載連結：https://ngrok.com/ 進去後點「Get started for free」 - 請選擇你的作業系統： step 2. 請「務必」記下下面內容與金鑰 接下來，我們會用到下面第一行啟用 ngrok 的金鑰 而第二行是在本地啟動 ngrok 的指令，後面的 port 可以自行修改 (必須對應到你的程式碼) step 3. 啟動 ngrok 方法 打開你的終端機，將 step 2 那兩行複製貼上\n注意：你必須先移動到你的 ngrok 所在路徑，\n另外 ngork 請解壓縮出來\u0026hellip; (我之前就傻傻以為下載不用解壓縮XD)\n./ngrok authtoken \u0026lt;你的金鑰\u0026gt; ./ngrok http 8080 如果 Windows 碰到上面指令不能用，請試著前面去掉「./」應該就可以了! (親身經驗，另外還有路徑要對)\nstep 4. ngrok 畫面 (此處以 Windows 示範，其他作業系統大同小異) 我們「反白的地方」就是我們的映射網址，請「務必複製下來」 (複製第二行 https 開頭那個) 題外話：terminal 複製小技巧 Linux terminal 複製：「command + shift + C」 Mac terminal 複製：「command + C」 Windows terminal 複製可以用下圖的方式，如下圖 先「選取複製範圍」-\u003e「點上方視窗右鍵」-\u003e 「編輯」-\u003e 「複製」 step 5. 使用 step 4 複製的 ngrok 映射網址，修改 line webhook url 路徑 拿我們剛剛在 step 4 複製的 ngrok 映射網址，去修改 line webhook URL 路徑，\n並在之後加入「/callback」，如下圖。\nline webhook URL 修改傳送門，之後更新都會用到：https://developers.line.biz/console/ ★重要：因為免費版的 ngrok 最大只能連線 8hr，\n重新連接的時候，http 映射位置會更新，記得去更新我們的 webhook\nstep 6. 驗證 ngrok 服務是否串接成功，按下 「Verify」 這邊我們按下「Verify」來驗證我們是否串接成功，記得下面的「Use Webhook」要打開。\n這邊如果有跳以下 Error 先沒關係，主要是因為我們只有「啟動 ngrok」而「未啟動程式服務」\nstep 7. 按下 「Verify」之後，回到 ngrok 畫面 回去看我們的 ngrok 視窗，如果有出現新的 Post /callback 就成功了! (出現 502 Bad Gateway 也沒關係)\n出現 502 Bad Gateway 是因為還沒有打開 localhost 的伺服器，也就是還「未啟動程式服務」。\n到這邊我們就這設定完 ngrok 的本地伺服器囉 (LINE bot local server) 接下來，有了「伺服器」跟「機器人服務前端」，\n我們就要來完成最重要的「機器人後端的服務」了! (可以想像成「機器人運作的邏輯」)\nReference https://www.learncodewithmike.com/2020/06/python-line-bot.html ","date":"2021-05-02T01:05:27+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/ngrok-6.png","permalink":"https://wongwongnotes.com/posts/python/web-automation/python-chatbot/linebot-local-server-ngork/","tags":["Python"],"title":"【Chatbot】ngrok 本地伺服器設定 — LINE bot 本地測試 (全圖文說明)"},{"categories":["181 - Python Chatbot"],"content":"前言 這篇是專門寫 LINE Developers bot 要如何註冊機器人與控制相關的設定，\n主要會提到以下內容 (全部都有圖文說明)：如何在「LINE Developers」建立機器人服務、\n如何在建立好 bot 之後調整一些「基本設定」，與一些「進階設定」的方法。\n步驟 Step 1: 先去 line developer 官網，登入後 Create a new Provider line developer 官網：https://developers.line.biz/console/ ，並登入\n先點選下面的「Create」，填好你的「Provider name」，按下「Create」\n(因為我已經建過幾個了，可能畫面有點不太一樣)\nStep 2: 建立好 Provider 後，點選 「Create a Messaging API channel」 點選下面的「Create a Messaging API channel」，填好你的「Provider name」，按下「Create」 Step 3: 建立好 Channel 後，接下來要填寫相關資訊 Step 3-1: 填寫 Channel 相關資訊 - 1 Channel type：Messaging API（必選這個） Provider：填作者的名字，看需求 Channel icon：(選填，之後也能改) 上傳你的 Bot 大頭貼 Channel name：你的 Bot 顯示的名字 Step 3-2: 填寫 Channel 相關資訊 - 2 Channel description：關於你的 Bot 功能描述 Category：你的 Bot 的分類 Subcategory：你的 Bot 的子分類 Email address：你的信箱 Step 3-3: 填寫 Channel 相關資訊 - 3 Privacy policy URL：(選填) 隱私權政策網址 Terms of use URL：(選填) 使用條款網址 剩下的兩個選項都記得打勾，就可以按「Create」了! Step 3-4: 最終確認畫面，沒問題就按「OK」 點選下面的「Create a Messaging API channel」，填好你的「Provider name」，按下「Create」 Step 4: 「重要」 這裡有一些必須要記的資訊 接下來我們要去下圖的兩個位置，取得圖上說明的「三項資訊」 這裡有必記重要參數！建議另外開一個記事本先記著！！！！ (不先記，等等後續步驟會超經常用，到時再慢慢找會很崩潰哦！) Step 4-1: 「重要」 在 Basic settings 取得 「Channel secret」、「user ID」 在 Basic settings 的下面，往下找 「Channel secret（必牢記！！！）」、 「Your user ID （建議記，偶而寫一些 Bot 專屬功能會用到）」 Step 4-2: 「重要」 在 Messaging API 取得 「Channel access token」 在 Messaging API 的下面，往下找 「Channel access token（必牢記！！！）」 - 第一次看到可能不會像下圖那樣，此時按一下「issue」能產生一大串金鑰，按一下旁邊可以複製 Step 5: 把你設定好的機器人加為好友吧! 在 Messaging API 的上面，應該會看到一個「QR code」，或是 「Bot basic ID」 到以上為止，我們就完成一個機器人「基本設定」的步驟了! 接下來，還有一些其他的設定，就看讀者要不要設定了 (建議要) 允許加入群組隊話、禁止透過(官方)自動回覆、允許歡迎訊息 這邊就是個人設定的部分，建議還是操作一下，但可以依照自己的想法設定。\n個人設定 - 1 ：(這邊有改版) 進入「LINE Offcial Account Manager」 (這邊有改版，原本不會另外跳視窗) 我們來到 Messaging API 的中間部分，\n點擊「Allow bot to join group chats」旁邊的「Edit」，\n進入「LINE Offcial Account Manager」 的設定畫面 。\n個人設定 - 2 ：「帳號設定」下面，允許加入群組或多人聊天室\u0026hellip;等等 這邊建議要開啟「允許群組聊天室」，其他看個人設定，也可以參考我的。 個人設定 - 3 ：「回應設定」下面，修改「基本設定」、「進階設定」\u0026hellip;等等 回應模式：聊天機器人 (因為我們需要使用 Webhook 功能回覆) 加入好友的歡迎訊息 (剛加入好友的歡迎訊息，可自行決定要不要) 自動回應訊息：(務必「停用」) (我們已經用 Webhook 回覆訊息了，這個自動回覆是官方內建的，停用來避免多餘的回覆) Webhook：(務必「啟用」) (這一步沒啟用我們上面那些都白做了...) 個人設定 - 4 ：回到第一個的畫面，重新整理一下，看看相關設定有沒有也改變了。 如果下面的選項沒有因為剛剛設定被改動的話，可以再點選右邊的「Edit」自行修改。\nAllow bot to join group chats ：Enabled (允許加入群組聊天) Auto-reply messages ：Disabled (允許自動官方回覆，這邊指的是透過官方提供的自行設定回覆， 但因為我們有串 Webhook 用程式來回覆，這邊就不需要了) Greeting messages ：Enabled (歡迎訊息，剛加入好友時會不會自動發感謝加好友的訊息，這邊看個人) 到這邊，我們就完成一個機器人設定的大致步驟了! 接下來就可以去寫程式並串接服務囉!\nReference ","date":"2021-05-01T20:00:36+08:00","image":"https://wongwongnotes.com/images/restored/2021/05/linebot-setting-2.png","permalink":"https://wongwongnotes.com/posts/python/web-automation/python-chatbot/line-developers-settings/","tags":["Python"],"title":"【Chatbot】(全圖文說明) LINE Developers bot 機器人註冊與設定"},{"categories":["231 - C++ OpenCV"],"content":"前言 在機器學習的領域中，我們很常會需要用到 iou 的計算，\niou 全名為 intersection over union，\n能替兩張圖形重疊的範圍提供一個參考分數，是一個相對具有參考意義的值。\n本篇文章中也提供範例程式碼，\n另外也發現網路上查到的作法有很多類似的常見錯誤，一並分享在此文當中。\n因為我自己也很常用XD，不時就會回來拿這段 function 去實作。\niou 的概念與公式 iou 基本上 = 兩矩形的交集 / 兩矩形的聯集\n所以：\n完全重合時：得到最大值 1 完全不重合時：得到最小值 0 部分重合：得到 0~1 範圍的值 以下為圖解 圖片引用自：https://blog.csdn.net/IAMoldpan/article/details/78799857 用 C++ 實作計算 iou 的 function - 網路常見「錯誤」示範 iou - 常見「錯誤」範例程式碼 #include \u0026lt;iostream\u0026gt; #include \u0026lt;opencv2/core/core.hpp\u0026gt; #include \u0026lt;opencv2/highgui/highgui.hpp\u0026gt; using namespace cv; using namespace std; int main() { Rect bbox_1(10, 20, 30, 40); Rect bbox_2(20, 30, 40, 50); Rect bbox_and; bbox_and = bbox_1 \u0026amp; bbox_2; Rect bbox_or; bbox_or = bbox_1 | bbox_2; cout\u0026lt;\u0026lt; \u0026#34;bbox_and.area() = \u0026#34; \u0026lt;\u0026lt; bbox_and.area() \u0026lt;\u0026lt; endl; cout\u0026lt;\u0026lt; \u0026#34;bbox_or.area() = \u0026#34; \u0026lt;\u0026lt; bbox_or.area() \u0026lt;\u0026lt; endl; cout\u0026lt;\u0026lt; \u0026#34;iou = \u0026#34; \u0026lt;\u0026lt; (bbox_and.area()*1.0 / bbox_or.area()) \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ iou.cpp -o iou.out -std=c++11 `pkg-config --cflags opencv`; ./iou.out bbox_and.area() = 600 bbox_or.area() = 3000 iou = 0.2 「正確」實驗結果分析 看下圖，我們可以大概知道圖形的分布\n首先是紅框 (bbox_1)，面積為 30*40=1200 再來是綠框 (bbox_2)，面積為 40*50=2000 紅綠框交疊部分 ，面積為 (40-20)*(60-30)=600 紅綠框聯集，面積為 1200+2000-600 = 2600 (兩個相加後，扣掉重複部分) 得到結果 600/2600 = 0.23076923076 (就是我們上面最後印出來的結果囉!)\n所以上面的程式問題出在哪? 網路上常見做 iou 算法的錯誤是出在計算「兩個矩形聯集」的計算部分，\n如上面的程式碼，使用了「bbox_1 | bbox_2」，\n這就是關鍵問題所在，「Rectangle OR」計算出的結果是「最大範圍的 Rectangle」，\n用下圖來說，我們其實算出的是「藍色的部份」\n「Rectangle OR」算出的「藍色的部份 Rectangle」，\n並非我們要的「聯集」，請見最上面的定義。\n這就是網路上常見的「錯誤計算 iou 程式碼」。\n用 C++ 實作計算 iou 的 function - 「正確」示範 iou - 「正確」範例程式碼 #include \u0026lt;iostream\u0026gt; #include \u0026lt;opencv2/core/core.hpp\u0026gt; #include \u0026lt;opencv2/highgui/highgui.hpp\u0026gt; using namespace cv; using namespace std; int main() { Rect bbox_1(10, 20, 30, 40); Rect bbox_2(20, 30, 40, 50); Rect bbox_and; bbox_and = bbox_1 \u0026amp; bbox_2; float bbox_or = bbox_1.area() + bbox_2.area() - bbox_and.area(); cout\u0026lt;\u0026lt; \u0026#34;bbox_and.area() = \u0026#34; \u0026lt;\u0026lt; bbox_and.area() \u0026lt;\u0026lt; endl; cout\u0026lt;\u0026lt; \u0026#34;bbox_or.area() = \u0026#34; \u0026lt;\u0026lt; bbox_or \u0026lt;\u0026lt; endl; cout\u0026lt;\u0026lt; \u0026#34;iou = \u0026#34; \u0026lt;\u0026lt; (bbox_and.area()*1.0 / bbox_or) \u0026lt;\u0026lt; endl; return 0; } 可以注意我們只有改寫「聯集」的地方，將他改寫成正確的算法。\n這樣計算出來才是正確的。\n編譯與結果 \u0026gt; g++ iou.cpp -o iou.out -std=c++11 `pkg-config --cflags opencv`; ./iou.out bbox_and.area() = 600 bbox_or.area() = 2600 iou = 0.230769 我們得到了正確的 iou 結果。\n想看 python 計算 iou 方法，可見我的另外一篇文 https://wongwongnotes.com/posts/python/gui/python-opencv/python-opencv-iou/\nReference https://www.cnblogs.com/lfri/p/10498876.html https://my.oschina.net/u/3800567/blog/1795889 https://blog.csdn.net/weixin_34195546/article/details/92412045 ","date":"2021-04-29T12:32:42+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/iou-3.png","permalink":"https://wongwongnotes.com/posts/cpp/visual-apps/c-opencv/opencv-cpp-iou/","tags":["C++"],"title":"【OpenCV】用 C++ 計算 iou 的方法 與網路算法常見錯誤"},{"categories":["131 - Python OpenCV"],"content":"前言 在機器學習的領域中，我們很常會需要用到 iou 的計算，\niou 全名為 intersection over union，\n能替兩張圖形重疊的範圍提供一個參考分數，是一個相對具有參考意義的值。\n本篇文章中也提供範例程式碼，\n因為我自己也很常用XD，不時就會回來拿這段 function 去實作。\niou 的概念與公式 iou 基本上 = 兩矩形的交集 / 兩矩形的聯集\n所以：\n完全重合時：得到最大值 1 完全不重合時：得到最小值 0 部分重合：得到 0~1 範圍的值 以下為圖解 圖片引用自：https://blog.csdn.net/IAMoldpan/article/details/78799857 用 python 實作計算 iou 的 function 這邊先講一下我們的 input，為兩個 bbox，\n兩個 bbox 都是一個具有四個值的 list，\n分別儲存兩個矩形的 [x, y, w, h]。\n要使用時請務必注意傳入格式！\n註：底下的 function 有使用到 python 3.8 以後才有的 f-string 功能，\n沒辦法正常運行的可以考慮用 format 的方式將 f-string 的部分重寫。\ndef get_iou(bbox_ai, bbox_gt): iou_x = max(bbox_ai[0], bbox_gt[0]) # x iou_y = max(bbox_ai[1], bbox_gt[1]) # y iou_w = min(bbox_ai[2]+bbox_ai[0], bbox_gt[2]+bbox_gt[0]) - iou_x # w iou_w = max(iou_w, 0) print(f\u0026#39;{iou_w=}\u0026#39;) iou_h = min(bbox_ai[3]+bbox_ai[1], bbox_gt[3]+bbox_gt[1]) - iou_y # h iou_h = max(iou_h, 0) print(f\u0026#39;{iou_h=}\u0026#39;) iou_area = iou_w * iou_h print(f\u0026#39;{iou_area=}\u0026#39;) all_area = bbox_ai[2]*bbox_ai[3] + bbox_gt[2]*bbox_gt[3] - iou_area print(f\u0026#39;{all_area=}\u0026#39;) return max(iou_area/all_area, 0) 實驗與結果 這邊以 colab 實驗，因此有加一些額外的功能\n(例如 colab 因為沒有視覺化視窗，不能用 cv2.imshow，改以用 matplotlib 套件代替)\ncolab 實驗連結 (可以自行遊玩) https://colab.research.google.com/drive/1wMb28P4RJDjmqyoV_znbbXZwEg_9C3sV?usp=sharing\ncolab 實驗用程式碼 from matplotlib import pyplot as plt import numpy as np import cv2 def show_img(img): image_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(image_rgb) plt.show() def draw_rectangle(img, bbox, color): left_up = (bbox[0], bbox[1]) right_down = (bbox[0]+bbox[2], bbox[1]+bbox[3]) thickness = 1 # 寬度 (-1 表示填滿) cv2.rectangle(img, left_up, right_down, color, thickness) return img def get_iou(bbox_ai, bbox_gt): iou_x = max(bbox_ai[0], bbox_gt[0]) # x iou_y = max(bbox_ai[1], bbox_gt[1]) # y iou_w = min(bbox_ai[2]+bbox_ai[0], bbox_gt[2]+bbox_gt[0]) - iou_x # w iou_w = max(iou_w, 0) # print(f\u0026#39;{iou_w=}\u0026#39;) iou_h = min(bbox_ai[3]+bbox_ai[1], bbox_gt[3]+bbox_gt[1]) - iou_y # h iou_h = max(iou_h, 0) # print(f\u0026#39;{iou_h=}\u0026#39;) iou_area = iou_w * iou_h # print(f\u0026#39;{iou_area=}\u0026#39;) all_area = bbox_ai[2]*bbox_ai[3] + bbox_gt[2]*bbox_gt[3] - iou_area # print(f\u0026#39;{all_area=}\u0026#39;) return max(iou_area/all_area, 0) shape = (100, 100, 3) # y, x, RGB # 第一種方法，直接建立全白圖片 100*100 img = np.full(shape, 255).astype(np.uint8) bbox_1 = [10, 20, 30, 40] bbox_2 = [20, 30, 40, 50] img = draw_rectangle(img, bbox_1, color=(0, 0, 255)) # show_img(img) img = draw_rectangle(img, bbox_2, color=(0, 255, 0)) show_img(img) print(get_iou(bbox_1, bbox_2)) 此為，為了方便解釋，我們也加了畫矩形的功能在裡面。\n實驗結果 看下圖，我們可以大概知道圖形的分布\n首先是紅框 (bbox_1)，面積為 30*40=1200 再來是綠框 (bbox_2)，面積為 40*50=2000 紅綠框交疊部分 ，面積為 (40-20)*(60-30)=600 紅綠框聯集，面積為 1200+2000-600 = 2600 (兩個相加後，扣掉重複部分) 得到結果 600/2600 = 0.23076923076 (就是我們上面最後印出來的結果囉!)\n程式碼詳細說明 基本上我們在做的事情就是\nmax(x1, x2) ，得到交集的左邊 x max(y1, y2) ，得到交集的上面 y min(x1+w1, x2+w2) ，得到交集的右邊 x min(y1+h1, y2+h2) ，得到交集的下面 y 就可以計算囉!\n但也是會有沒考慮到的地方(不是特例) 上面的圖，如果照我們的公式，\n我們取\nmax(x1, x2) ，得到圖中右邊的箭頭 x min(x1+w1, x2+w2) ，得到圖中左邊的箭頭 x 結果算出來就是負的了\u0026hellip;\n發現潛在的計算問題了嗎? 沒錯，也就是說，當我們發現 min(x1+w1, x2+w2) \u0026lt; max(x1, x2) 時 (相減會負)，\n或是 min(y1+h1, y2+h2) \u0026lt; max(y1, y2) 時 (相減會負)，\n我們就要讓他答案直接為 0 ! (也要避免負負得正!)\n這就是為什麼我們要做下面 max 的原因。\niou_w = max(iou_w, 0) iou_h = max(iou_h, 0) 關於最後的計算 你可能會想問，為什麼最後還要做以下的 max?\nreturn max(iou_area/all_area, 0) 其實只是為了再次避免我們算出負的，但基本上有上面那一點的預防，\n我們其實這個避免 0 的功能應該是用不到的XD，\n但寫了也是保險!\n想看 C++ 計算 iou 方法，可見我的另外一篇文 https://wongwongnotes.com/posts/cpp/visual-apps/c-opencv/opencv-cpp-iou/\nReference https://www.cnblogs.com/lfri/p/10498876.html ","date":"2021-04-29T00:56:56+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/iou-1.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/python-opencv-iou/","tags":["Python","Python OpenCV"],"title":"【OpenCV】用 python OpenCV 計算 iou 的方法"},{"categories":["181 - Python Chatbot"],"content":"前言 line chatbot 寫久了之後，如果只是單純「固定的」輸入輸出，\n也就是使用者只能輸入特定的「關鍵字」來觸發功能，\n或許就是少了那麼一點「自然對話的感覺」，\n本篇提供 Dialogflow 的基本 API 使用範本，\n至於「串接 chatbot」的部份請參考我的另外一篇文章，\n這邊只專注在提供怎麼用 python 連接 Dialogflow API 的服務。\n前置步驟 - 取得 Dialogflow API 金鑰 Step 1: 先去 Dialogflow 建立一個 agent (因為我已經建過幾個了，可能畫面有點不太一樣)\n*　按下「Create new agent」\nStep 2: 輸入 Dialogflow agent 的相關資訊 在下面的內容分別輸入，\n名稱 DEFAULT LANGUAGE 選繁體中文，「Chinese (Traditional) — zh-tw」\nDEFAULT TIME ZONE 選我們的時區，「(GMT+8:00) Asia/Hong_Kong」 GOOGLE PROJECT 預設是「Create a new Google project」，除非你已經有相關的專案。 - 最後按下「CREATE」就可以建立新的 agent 囉！ (這邊因為 demo 似乎是禁用字，後來我改成了 testwongwong)\nStep 3: 從建立好的 dialogflow 連結進入 Google Cloud Platform 點擊左上角的齒輪進入設定，並點擊 Project ID 的連結進入 Google Cloud Platform\n註：如果看到「您的權限不足，無法瀏覽這個頁面」\n請先「將其他 google 帳戶登出」，或直接使用「無痕模式」執行動作。\nStep 4: 進入設定「服務帳戶」 點選左側，「IAM 與管理」-\u0026gt; 「服務帳戶」 Step 5: 建立「服務帳戶」 建立「服務帳戶」 Step 6: 設定「服務帳戶」- 1 設定「服務帳戶名稱」：我這邊輸入與剛剛取的專案名稱一樣 （不一定要一樣） 設定「服務帳戶 ID」：(會依據上面自動產生) Step 7: 設定「服務帳戶」- 2 設定「Dialogflow」並選擇「Dialogflow API 用戶端」 Step 8: 設定「服務帳戶」- 3 這邊基本上不用設定，直接按完成即可。 Step 9: 進入「管理金鑰」頁面 從產生的帳戶結果中，按下右邊「管理金鑰」，進入金鑰管理頁面 Step 10: 新增金鑰 按下「新增金鑰」，選擇 JSON，並按下建立。 Step 11: 得到我們下載後的金鑰檔！ 我們就得到我們下載後的金鑰檔囉！ 開始測試並使用 Dialogflow API 這邊我們就直接來實驗 Dialogflow API 的使用吧！\n(關於 Dialogflow 的訓練方式會再用另外一篇文章寫)\n範例程式碼 import os import dialogflow from google.api_core.exceptions import InvalidArgument os.environ[\u0026#34;GOOGLE_APPLICATION_CREDENTIALS\u0026#34;] = \u0026#39;你剛剛得到的金鑰路徑\u0026#39; DIALOGFLOW_PROJECT_ID = \u0026#39;剛剛的 PROJECT ID\u0026#39; DIALOGFLOW_LANGUAGE_CODE = \u0026#39;zh-TW\u0026#39; SESSION_ID = \u0026#39;anything\u0026#39; text_to_be_analyzed = \u0026#34;你好\u0026#34; session_client = dialogflow.SessionsClient() session = session_client.session_path(DIALOGFLOW_PROJECT_ID, SESSION_ID) text_input = dialogflow.types.TextInput(text=text_to_be_analyzed, language_code=DIALOGFLOW_LANGUAGE_CODE) query_input = dialogflow.types.QueryInput(text=text_input) try: response = session_client.detect_intent(session=session, query_input=query_input) except InvalidArgument: raise print(\u0026#34;輸入文字:\u0026#34;, response.query_result.query_text) print(\u0026#34;得到的 intent:\u0026#34;, response.query_result.intent.display_name) print(\u0026#34;偵測到 intent 的 confidence:\u0026#34;, response.query_result.intent_detection_confidence) print(\u0026#34;回應的話:\u0026#34;, response.query_result.fulfillment_text) 產生結果 (我的 Dialogflow 有先進行訓練過，實際結果可能因訓練結果而有所不同。)\nDialogflow API 範例使用說明 主要改的內容有：\nos.environ[\u0026ldquo;GOOGLE_APPLICATION_CREDENTIALS\u0026rdquo;]：改成你剛剛得到的「金鑰路徑」 DIALOGFLOW_PROJECT_ID：改成你的「PROJECT ID」 DIALOGFLOW_LANGUAGE_CODE：改成使用語言，例如「zh-TW」 SESSION_ID：這邊基本上可以任意 text_to_be_analyzed：要分析的文字 Reference https://medium.com/swlh/working-with-dialogflow-using-python-client-cb2196d579a4 https://www.timelog.to/a181507263 ","date":"2021-04-28T17:16:16+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/dialogflow-12.png","permalink":"https://wongwongnotes.com/posts/python/web-automation/python-chatbot/python-dialogflow-api/","tags":["Python"],"title":"【Chatbot】Dialogflow API 串接 python 的方法"},{"categories":["123 - Google Colab (Colaboratory)"],"content":"前言 colaboratory 是一個由 google 提供非常方便的網頁開發平台，\n我們可以在上面進行 python 程式的快速開發，\n而且最方便的是「python 環境」我們使用完就可以直接拋棄，\n如果將「python 環境」裝在自己的電腦，\n有時候「python 環境」壞了可能就很難修，甚至最慘可能要重灌電腦QQ。\n為什麼連接 google 雲端硬碟? 這邊我們可以比較另外一篇「純上傳檔案」的文章，\n最主要的差別是：「純上傳檔案」基本上不會進行「檔案保存」。\n如果有需要「長期保存檔案」的需求，建議就要學此篇文章的方法。\n不進行保存的「純上傳檔案」，建議可以使用一些測試完就能丟掉的東西\n(例如：一次性的圖片處理。)\n或者是有「需要金鑰」的相關測試，讓本次使用後，金鑰直接消失，而不保存在雲端。\n等下次需要使用的時候，「再次上傳金鑰」，金鑰就能夠在本地端進行好好的管理。\n「純上傳檔案」的文章的文章連結： https://wongwongnotes.com/posts/python/environment/google-colab-colaboratory/python-colab-upload/\n連接 google 雲端硬碟 範例 from google.colab import drive drive.mount(\u0026#39;/content/gdrive\u0026#39;) # 此處需要登入 google 帳號 執行後，需要登入 google 帳號 ，我們就能完整的取用我們的雲端硬碟。\n使用方式說明 我們可以從左邊按一下資料夾，執行後我們就可以看到我們的雲端硬碟路徑了！\n再來只要對「對應檔案」點擊右鍵，按下 「Copy path」，\n就可以直接在雲端硬碟執行對應的檔案囉！\nReference https://ithelp.ithome.com.tw/articles/10217962 ","date":"2021-04-27T11:07:46+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/mount_google_drive.png","permalink":"https://wongwongnotes.com/posts/python/environment/google-colab-colaboratory/python-colab-mount-google-drive/","tags":["Colab","Python","字串處理"],"title":"【Colab】Python colab 連接 google 雲端硬碟取用資料 mount google drive"},{"categories":["123 - Google Colab (Colaboratory)"],"content":"前言 colaboratory 是一個由 google 提供非常方便的網頁開發平台，\n我們可以在上面進行 python 程式的快速開發，\n而且最方便的是「python 環境」我們使用完就可以直接拋棄，\n如果將「python 環境」裝在自己的電腦，\n有時候「python 環境」壞了可能就很難修，甚至最慘可能要重灌電腦QQ。\n為什麼要上傳檔案? 為什麼我們會需要上傳檔案? 畢竟 colaboratory 本身是雲端環境，\n我們如果在「本地端」開發，能夠相對感受到的好處就是「檔案易於取用」，\n我們透過上傳的方法。並直接將檔案串聯，就能彌補一些這方面的不方便。\n註：如果你想要找的是「直接取用 google drive 資料的方法」\n主要差別是在「檔案能否在使用完 colab 後被保存下來」，請參考我的另外一篇文章\nhttps://wongwongnotes.com/posts/python/environment/google-colab-colaboratory/python-colab-mount-google-drive/\n上傳檔案範例 from google.colab import files uploaded = files.upload() file_name = list(uploaded.keys())[0] print(file_name) 說明 uploaded = files.upload() 這行就是我們進行上傳的功能，上傳完後我們會得到一個 dict (包含檔案相關資訊) file_name = list(uploaded.keys())[0] 這行是我們從剛剛的 dict 提取出檔案的名稱，在下一行我們就可以直接使用這個 path 去完成其他功能 Reference https://ithelp.ithome.com.tw/articles/10217962 ","date":"2021-04-26T10:30:09+08:00","permalink":"https://wongwongnotes.com/posts/python/environment/google-colab-colaboratory/python-colab-upload/","tags":["Colab","Python","字串處理"],"title":"【Colab】Python colab 上傳檔案的方法 upload files"},{"categories":["112 - Python 進階語法"],"content":"前言 python 中有個指令為「lambda」，\n中文我們習慣稱呼為「匿名函數」，\n當我們的函數功能很小 (例如：一行就可以寫完的那種)，\n不想要另外進行 def 的宣告時，\n我們就可以使用 lambda 的方法完成一個快速的 function，\n「lambda」 與 「def function - return」的方法是互通的，\n而 lambda 的使用情境更像是為了快速實作而寫，\n我們就不需要再花時間去寫 「def function - return」\n比較 \u0026amp; 範例程式碼 一般的 「def function - return」 寫法 def add(a, b): return a+b a=10 b=20 c=add(a, b) print(c) 結果 「lambda」寫法 add = lambda a, b: a+b a=10 b=20 c=add(a, b) print(c) 結果 lambda 說明 我之前也是 lambda 學不太好的人XDD，\n透過這次整理把 lambda 整個整理清楚了！\n在閱讀一個 lambda function 時，先把「lambda」、「=」、「:」抓出來 例如下面的例子，\n我們把「lambda」、「=」、「:」抓出來後，\n剩下的大致可以分為三個部份，\n「=」前面的：「function name」 「lambda」之後緊接的：「input value」 「:」之後的：為「return value (output value)」 add = lambda a, b: a+b 依照上面的邏輯，我們分析後可以得到：\n「=」前面的：「function name」 此處為 add 「lambda」之後緊接的：「input value」 此處為 a, b 「:」之後的：為「return value (output value)」 此處為 a+b 這樣大家看懂了嗎XD\n對照一下正常的使用一般 「def function - return」 寫法 def add(a, b): return a+b 建議讀者自行練習一下，練久了自然就會懂這怎麼用了XD\n稍微變化一下用法，lambda 結合一行「if-else」 範例程式碼 test = lambda a, b: a-b if a\u0026gt;b else a+b a=10 b=20 print(test(a,b)) print(test(b,a)) 執行結果 分析 一樣我們先把這行程式碼拆成 3 個 part：\n「=」前面的：「function name」 此處為 test 「lambda」之後緊接的：「input value」 此處為 a, b 「:」之後的：為「return value (output value)」 此處為 a-b if a\u0026gt;b else a+b 而一行 「if-else」的用法：\n簡單來說就是： [if True 的結果] if [判斷式] else [else 的結果]\n所以換個寫法，我們可以知道我們這裡寫的是：\nif a\u0026gt;b: return a-b else return a+b 仔細推論一下，就知道我們的輸出是正確的囉！\n詳細可以參考我的另外一篇文章： https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-one-line-if-else/\n稍微變化一下用法，lambda 結合一行「for-loop」 範例程式碼 test = lambda x: [i for i in range(x)] a = 10 for i in test(a): print(i) 執行結果 你可能也會想問? 為什麼不直接 print? 範例程式碼 當然也是可以的！請見以下作法 (但並不推薦這樣使用)\ntest = lambda x: [print(i) for i in range(x)] a = 10 test(a) 執行結果 說明 \u0026amp; 不推薦這樣使用的原因 不推薦的原因1： 這邊是使用了 list 的宣告完成 for-loop 並印出值，\n但其實我們並不想要特別去宣告這個 list，\n例如下面，如果我們寫「print(test(a))」，會得到一串 None，\n實際上我覺得這會讓我們的程式邏輯看起來很怪 (雖然有達到了目的)。\n不推薦的原因2： for 本身是個 statement，「statement in list」的用法是正確的。\n但是問題就出在這個「list」，這並不是我們預期想要的東西，\n只能說他達到了我們要的目的，但多做了一些額外的事情。\nReference https://www.runoob.com/python/python-functions.html https://stackoverflow.com/questions/23896199/python-loop-for-inside-lambda https://www.tutorialspoint.com/How-to-create-a-lambda-inside-a-Python-loop https://realpython.com/python-lambda/ https://stackoverflow.com/questions/35238409/how-to-write-a-lambda-with-a-for-loop-in-it ","date":"2021-04-25T18:14:16+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/python-lambda-4.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-lambda/","tags":["Python","字串處理","進階語法"],"title":"【Python 進階語法 #2】lambda 與 def function 使用方法與比較整理"},{"categories":["044 - 個人演講 ppt","181 - Python Chatbot"],"content":"前言 這個是我於 2018/12/27 於助教課分享的 line chatbot 教學投影片。\n內容比較偏向新手，能夠幫助新手至少建立一個屬於自己的 chatbot\n(事後也能自行修改裡面的程式碼的那種，不是別人寫好可以直接套的模板)\n演講投影片 個人作品分享 可以參考我的這篇文，\n裡面有很多我自己用 python 實作出來的功能與 demo 影片。\nhttps://wongwongnotes.com/posts/life-and-work/portfolio/works/my-line-chatbot/\n","date":"2021-04-24T00:07:56+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/ppt/python-line-chatbot/","tags":["Python","ppt"],"title":"【Chatbot】[講義分享] 手把手實作line機器人 (linebot API 運用)"},{"categories":["111 - Python 基礎語法"],"content":"前言 python 更新至 3.8 後，f-string 新增了一個極好用的 debug 功能，\n可以同時輸出「變數的值 (variable value)」與「變數的名稱 (variable name)」，\n前者輸出「變數的值」完全不重要XD，重點是後者輸出「變數的名稱」，\n平常我們要輸出「變數的名稱」，我們都只能自己打，\n現在 f-string 提供了一個超方便的方式，將在此篇文章中介紹。\n以往我們可能會這樣寫來 debug 通常我們只能印出「變數的值」，而不能印出「變數的名稱」\n所以我們必須要這樣自己寫出「變數的名稱」。\n當 debug 的訊息量變多，做這樣的事情才能讓我們搞清楚要印出來的是什麼東西。\nword = \u0026#34;Hello\u0026#34; print(\u0026#39;word =\u0026#39;, word) print(f\u0026#39;word = {word}\u0026#39;) 輸出 word = Hello word = Hello python 3.8 後, f-string 增加新功能我們就可以這樣寫 word = \u0026#34;Hello\u0026#34; print(f\u0026#39;{word = }\u0026#39;) 輸出 word = \u0026#39;Hello\u0026#39; # 題外話：這個還特別顯示是 string 這功能真的超級方便的！ 而且格式支援非常豐富 (至少我目前還沒碰到不能印的格式)！\n推薦給大家！\npython 3.8 以前也不要擔心，還是有方法 只是比較麻煩一點，要多寫很多東西XDDD\n方法一 def show(variable): print(variable, \u0026#39;=\u0026#39;, repr(eval(variable))) 方法二 此方法其實就是將「方法一」寫得更簡短一些~\nshow2 = lambda *w: [print(x,\u0026#39;=\u0026#39;,repr(eval(x))) for x in w] 方法三 使用此方法，需要 「import sys」！！\nimport sys def show3(expression): frame = sys._getframe(1) print(expression, \u0026#39;=\u0026#39;, repr(eval(expression, frame.f_globals, frame.f_locals))) 範例 import sys def show(variable): print(variable, \u0026#39;=\u0026#39;, repr(eval(variable))) show2 = lambda *w: [print(x,\u0026#39;=\u0026#39;,repr(eval(x))) for x in w] def show3(expression): frame = sys._getframe(1) print(expression, \u0026#39;=\u0026#39;, repr(eval(expression, frame.f_globals, frame.f_locals))) test_str = \u0026#39;Hello world\u0026#39; show(\u0026#39;test_str\u0026#39;) show2(\u0026#39;test_str\u0026#39;) show3(\u0026#39;test_str\u0026#39;) 結果 test_str = \u0026#39;Hello world\u0026#39; test_str = \u0026#39;Hello world\u0026#39; test_str = \u0026#39;Hello world\u0026#39; 註： eval 表示「取值」，repr 表示顯示「變數型態」\n例如說，eval 得到了 string 的內容，\n而 repr 取得了 string 的型態 ，「\u0026lsquo;Hello world\u0026rsquo;」\n如果沒有 repr 則會印出 「Hello world」，讀者可以細心品味一下差別~\nReference https://stackoverflow.com/questions/18425225/getting-the-name-of-a-variable-as-a-string https://stackoverflow.com/questions/32000934/python-print-a-variables-name-and-value https://stackoverflow.com/questions/592746/how-can-you-print-a-variable-name-in-python https://stackoverflow.com/questions/46081715/python-print-variable-name-and-value-easily ","date":"2021-04-22T18:41:41+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/print-variable-value-name/","tags":["Python","基礎語法","字串處理"],"title":"【Python 基礎語法 #3】python print 變數的值 \u0026 變數名稱 方法總整理"},{"categories":["122 - Python conda (anaconda)"],"content":"前言 anaconda 下載後，有時我們會有要更新 python 版本的需求，\n例如像這次我是有個 f-string 好用 debug 功能，\n需要 python 3.8 以上才能使用，因此我才更新的XD。\n這邊提供我在 anaconda 更新至 python 3.8 的筆記，附贈 f-string 新增了什麼實用功能XD。\nanaconda 如何更新 python 版本 新方法，建立一個環境 for python 3.8 (以上) 我們應該要先更新 anaconda 的版本，\n才能夠支援對應的 python 環境。\n主要大概有以下流程：\n- 更新 anaconda 版本 - 啟動一個新的 python 環境 (如果沒有該版本的 python，就可安裝該版本) - 啟動該 python 環境 conda update -n base -c defaults conda conda create -n python39 python=3.9 conda activate python39 而如果你也想要「一啟動 terminal 就有這個 conda 環境」，請更新 zshrc (或 bashrc) 修改 ~/.zshrc，並在 conda 啟動後自動 activate 對應環境\nvim ~/.zshrc conda activate python39 就像下圖： 延伸閱讀：使用conda升级到python 3.8\n【更新】留給我自己的各種版本腳本 - python 3.9 conda update -n base -c defaults conda conda create -n python39 python=3.9 conda activate python39 【更新】留給我自己的各種版本腳本 - python 3.10 (暫時不推，太新有新東西還不支援) conda update -n base -c defaults conda conda create -n python310 python=3.10 conda activate python310 舊方法，更新 base 環境 for python 3.8 (比較不推薦，盡量以建立新環境為主，做好環境管理) 很簡單的一行指令即可更新：\nconda install -c anaconda python=3.8 題外話 - f-string 好用功能說明 f-string 在 python 3.8 提供了超簡易印出變數與值的方法，\n對於 debug 來說超級實用！！！\n以往我們可能會這樣寫來 debug 通常我們只能印出「變數的值」，而不能印出「變數的名稱」\n所以我們必須要這樣自己寫出「變數的名稱」。\nword = \u0026#34;Hello\u0026#34; print(\u0026#39;word =\u0026#39;, word) print(f\u0026#39;word = {word}\u0026#39;) 輸出 word = Hello word = Hello 在 python 3.8, f-string 增加新功能後我們就可以這樣寫 word = \u0026#34;Hello\u0026#34; print(f\u0026#39;{word = }\u0026#39;) 輸出 word = \u0026#39;Hello\u0026#39; # 題外話：這個還特別顯示是 string Reference https://stackoverflow.com/questions/58568175/upgrade-to-python-3-8-using-conda/59348995 ","date":"2021-04-21T18:00:40+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/conda39.png","permalink":"https://wongwongnotes.com/posts/python/environment/python-conda-anaconda/anaconda-upgrade-python/","tags":["Python","Python 基礎用法","conda"],"title":"【Python conda #1】anaconda 更新 (upgrade) python 3.8 python 3.9 版本筆記"},{"categories":["340 - Linux 系統控制"],"content":"前言 我們在終端機操作了很久，每次都是用 cd 的方式在進入不同的資料夾，\n但有時候確實 GUI 視窗的操作會比起在終端機 terminal 中方便許多，\n(例如：終端機很難做到的快速多選)\n這時候可能我們會想去打開 視覺化(GUI) 的資料夾，但步驟總是很多，\n這篇分享直接透過終端機 (terminal) 開啟資料夾的方法。\n解決方法： mac 直接使用 open 就可以了！ 懷疑是有特別設計過XDD\nopen [資料夾路徑] 範例：開啟當前資料夾 open . 解決方法： ubuntu 方法很簡單，大致分為兩種：\n方法一 (我是記這個) xdg-open [資料夾路徑] 範例：開啟當前資料夾 xdg-open . 方法二 (需要安裝) 先安裝 nautilus 套件，就可以囉！\nsudo apt-get install nautilus 使用方法 nautilus [資料夾路徑] 範例：開啟當前資料夾 nautilus . 以上的指令，搭配 alias 更好用! (2021/10/22 更新) 例如說，上面的指令我覺得太長了，\n或者太難記憶了，我們可以至 ~/.zshrc (我用 zsh shell)，\n新增以下的 alias\nalias open=\u0026#39;xdg-open\u0026#39; # ubunut open folder 這樣我們以後在 ubuntu 上就不用打完 \u0026ldquo;xdg-open\u0026rdquo;，\n跟 mac 一樣只需要打 open 就可以囉!\nReference https://www.maketecheasier.com/open-folder-in-finder-from-mac-terminal/ https://askubuntu.com/questions/464324/how-to-open-a-folder-in-linux-via-terminal https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/524405/ ","date":"2021-04-19T12:21:35+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-open-folder/","tags":["Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #6】mac/ubuntu 如何利用 terminal 打開 GUI 資料夾總整理 (2021/10/22 更新，使用 alias 更好用了!)"},{"categories":["212 - C++ 字串處理"],"content":"前言 此文章中會整理所有在 C/C++ 字串 的互相轉換用法，\n包含 char string stringstream 的互相轉換，\n其中 char 我們又會分成 char array, char pointer 介紹。\n這篇文章也是將我另一篇文章中的「互相轉換」獨立出來討論的內容，沒經驗者建議可以先參考前一篇文章。請見下文：\nhttps://wongwongnotes.com/posts/cpp/basics/cpp-string/c-cpp-string-all/\n先講結論，我們先觀察以下程式碼與結果 建議先自己實際操作過後，會比直接看結論學得更快哦！\nbefore→ / after↓ Char array Char pointer String stringstream Char array strcpy(dst, src) strcpy(Char array, Char pointer) strcpy(Char array, String.c_str() ) strcpy(Char array, stringstream.str().c_str()) Char pointer Char pointer = Char array 或 Char pointer = \u0026Char array[0] x Char pointer = String.c_str() Char pointer = stringstream.str().c_str() String string = Char array String = Char pointer x String = stringstream.str() stringstream stringstream \u003c\u003c Char array stringstream \u003c\u003c Char pointer stringstream \u003c\u003c String x 從 Char array 出發，轉成其他的字串型態 可以特別注意在 Char array 轉換成 Char pointer 中，我們有兩種轉換方式，\n其中一種是直接將值傳入 「*p」(值) = 傳入值， 另外一種是將位置傳入 = \u0026amp;s0[0] (string 的第 0 個位置) 範例程式碼 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { char s0[30] = \u0026#34;Hello World!\u0026#34;; char* s1 = s0; cout \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; char* s2 = \u0026amp;s0[0]; cout \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; string s3 = s0; cout \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; stringstream s4; s4 \u0026lt;\u0026lt; s0; cout \u0026lt;\u0026lt; s4.str() \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ test4.cpp -o a.out \u0026amp;\u0026amp; ./a.out Hello World! Hello World! Hello World! Hello World! 從 Char pointer 出發，轉成其他的字串型態 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { stringstream s0; s0 \u0026lt;\u0026lt; \u0026#34;Hello World!\u0026#34;; char s1[30]; strcpy(s1, s0.str().c_str()); cout \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; const char *s2 = s0.str().c_str(); cout \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; string s3 = s0.str(); cout \u0026lt;\u0026lt; s3 \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ test4.cpp -o a.out \u0026amp;\u0026amp; ./a.out Hello World! Hello World! Hello World! 從 String 出發，轉成其他的字串型態 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { string s0 = \u0026#34;Hello World!\u0026#34;; char s1[30]; strcpy(s1, s0.c_str()); cout \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; const char *s2 = s0.c_str(); cout \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; stringstream s3; s3 \u0026lt;\u0026lt; s0; cout \u0026lt;\u0026lt; s3.str() \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ test4.cpp -o a.out \u0026amp;\u0026amp; ./a.out Hello World! Hello World! Hello World! 從 stringstream 出發，轉成其他的字串型態 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { stringstream s0; s0 \u0026lt;\u0026lt; \u0026#34;Hello World!\u0026#34;; char s1[30]; strcpy(s1, s0.str().c_str()); cout \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; const char *s2 = s0.str().c_str(); cout \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; string s3 = s0.str(); cout \u0026lt;\u0026lt; s3 \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ test4.cpp -o a.out \u0026amp;\u0026amp; ./a.out Hello World! Hello World! Hello World! Reference char array, string, stringstream c++：像sprintf一樣的std :: string格式 How to convert a char array to a string? 字串長度、複製、串接 strlen() - C語言庫函數 Finding the length of a Character Array in C C++ - pointer to char、array of pointer to char、pointer to array of char (C++軟體開發 - 指標與字元與陣列 概念與實例) stringstream用法整理 ","date":"2021-04-16T14:20:53+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-string/convert-char-string-stringstream/","tags":["C++","C++ 字串處理","字串處理"],"title":"【C++ 字串處理 #4】字串 char string stringstream 「轉換」用法總整理"},{"categories":["914 - Wordpress"],"content":"前言 我們製作一個網站後，總是會希望自己的文章能被 google 搜尋到吧!\n有一種方式，就是等 google 慢慢爬蟲爬到你的文章。\n另外一種方式，我們也可以主動提交 sitemap 告訴 google 我們更新了!\n提交 sitemap 就是我們這篇文章要教學的事情!\n圖文教學 step1. 打開我們 wordpress 裡面的 Rank Math SEO 找到下圖中右下角的 sitemap -\u0026gt; 點 Settings，如下圖 (記得先啟用!) step2. 第一行網址的地方就是我們的 sitemap 囉! 如下圖，看第一行網址： step3. 打開 Google Search Console，點選 sitemap 並提交! 打開 Google Search Console ( https://search.google.com/search-console )，點選左邊的 sitemap 對照一下網址，將剛剛提交的網址送出就可以囉! 如下圖 Reference https://ranking.works/%E6%8A%80%E8%A1%93SEO/sitemap ","date":"2021-04-15T00:36:08+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/sitemap-3-1024x439.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/rank-math-seo-sitemap/","tags":["WordPress"],"title":"【Wordpress #5】如何利用 Rank Math SEO 提交 sitemap (圖文說明)"},{"categories":["442 - Sublime Text"],"content":"前言 這篇文章會教學在 Sublime 中將縮排 \u0026ldquo;tab\u0026rdquo; 改成 \u0026ldquo;4格空白\u0026rdquo; 的方法，\n身為一個經常寫 python 的人類，久了應該就會發現這個困擾!!!\n縮排 \u0026ldquo;tab\u0026rdquo; 會隨著編輯器的不同，而有不同空白的差異。\n例如用 vim 開起來的時候，會發現 \u0026ldquo;tab\u0026rdquo; 的概念與 vim 內按 \u0026ldquo;tab\u0026rdquo; 的縮排量不同\n最後就一直出現 indent error (縮排錯誤)，寫 python 遇到這個真的很崩潰。\n方法教學 step1. 打開你的 sublime 設定 位於「Preferences -\u0026gt; Settings」 可以見下圖：\nstep2. 打開你的 sublime 設定 在右邊的 User (個人化設定，當然不要只設定個人也可) 加入以下程式碼 { \u0026#34;draw_white_space\u0026#34;: \u0026#34;all\u0026#34;, \u0026#34;translate_tabs_to_spaces\u0026#34;: true } 這邊說明一下 「\"draw_white_space\": \"all\"」：表示顯示所有空白 (tab 應該是一條線，space 是 4個點，上面的圖中應該看得出來XD) 「\"translate_tabs_to_spaces\": true」：表示將 tab 轉換成 4格空白 (這個就是我們要的了!!! 我們之後按下 tab 就會自動變成 4格空白 ) 結語 python 寫久了自然就會碰到這個問題了\u0026hellip;，tab 雖然很方便，\n但不同編輯器的問題真的很麻煩 (例如碰系統的可能會使用到 vim，真的會崩潰XD)，\n不如就統一用4格空白吧!\n","date":"2021-04-14T23:48:11+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/222.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-tab-space/","tags":[],"title":"【Sublime】Sublime 將縮排 \"tab\" 改成 4格空白 的方法 (圖文說明) sublime indent 4 spaces"},{"categories":["212 - C++ 字串處理"],"content":"前言 此文章中會整理所有在 C/C++ printf 的相關用法，包含 printf, fprintf, sprintf, snprintf，\n以及 vprintf, vfprintf, vsprintf, vsnprintf，會比較他們之間的差別，以及提供程式碼範例。\n我們一共會介紹這些：\nprintf, fprintf, sprintf, snprintf vprintf, vfprintf, vsprintf, vsnprintf 先講結論 給新手、還沒有很注重「程式執行使用系統空間」的開發者結論 如果你是 C/C++ 新手，或是還沒有很注重「程式執行使用系統空間」的開發者，\n這篇基本上不用看了XD，你只要記得「printf」、「fprintf」、「sprintf」的差別就好！\n他們可以幫助你打天下！\n如果你是好奇 v 開頭的那系列 vprintf, vfprintf, vsprintf, vsnprintf ，我們可以仔細觀察，\n與 printf, fprintf, sprintf, snprintf 只差一個開頭的 「 v 」，\n這個 v 代表的是使用 va_list 的意思，也就是說，我們使用了「不限定傳入參數數量的方法」。\n也就是說，我們寫好了一個 function，他可以同時接受傳入 1, 2, 3\u0026hellip;各種數量的參數。\n知道這個概念即可，其他的就與原來「沒有 v」的相同。\n最後， printf, fprintf, sprintf, snprintf 的快速結論 function 主要特色 prinf 基本用法，直接顯示在 terminal 上 fprinf 「檔案相關」才會用到 sprinf 將值「傳入第一個變數」中 snprinf 將值「傳入第一個變數」中，n代表可以控制傳入大小，作為系統空間管理較為安全 printf 定義 簡單來說，就是直接印出來 (在你的 terminal 上)，\n可以說是基本中的基本用法XD。\n\u0026gt; printf int printf ( const char * format, ... ); Print formatted data to stdout Writes the C string pointed by format to the standard output (stdout). If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers. \u0026gt; http://www.cplusplus.com/reference/cstdio/printf/ 範例程式碼 - printf 這邊直接使用官方範例，裡面其實有講到很多格式調整的部份，\n但這裡就先不特別講 (太多內容XD。而且會偏題)，之後會有另外一篇文特別去寫。\n#include \u0026lt;stdio.h\u0026gt; using namespace std; void test_printf() { char s[] = \u0026#34;Hello\u0026#34;; printf(\u0026#34;Strings - padding: \u0026#34;); printf(\u0026#34;\\t.%10s. \\t.%-10s. \\t.%*s. \u0026#34;, s, s, 10, s); printf(\u0026#34;Strings - truncating: \u0026#34;); printf(\u0026#34;\\t%.4s \\t%.*s \u0026#34;, s, 3, s); printf(\u0026#34;Characters:\\t%c %% \u0026#34;, 65); printf(\u0026#34;Integers \u0026#34;); printf(\u0026#34;Decimal:\\t%i %d %.6i %i %.0i %+i %i \u0026#34;, 1, 2, 3, 0, 0, 4, -4); printf(\u0026#34;Hexadecimal:\\t%x %x %X %#x \u0026#34;, 5, 10, 10, 6); printf(\u0026#34;Octal:\\t\\t%o %#o %#o \u0026#34;, 10, 10, 4); printf(\u0026#34;Floating point \u0026#34;); printf(\u0026#34;Rounding:\\t%f %.0f %.32f \u0026#34;, 1.5, 1.5, 1.3); printf(\u0026#34;Padding:\\t%05.2f %.2f %5.2f \u0026#34;, 1.5, 1.5, 1.5); printf(\u0026#34;Scientific:\\t%E %e \u0026#34;, 1.5, 1.5); printf(\u0026#34;Hexadecimal:\\t%a %A \u0026#34;, 1.5, 1.5); } int main() { printf(\u0026#34; ------ test_printf ------ \u0026#34;); test_printf(); return 0; } 編譯與結果 \u0026gt; g++ printf_test.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out ------ test_printf ------ Strings - padding: . Hello. .Hello . . Hello. Strings - truncating: Hell Hel Characters: A % Integers Decimal: 1 2 000003 0 +4 -4 Hexadecimal: 5 a A 0x6 Octal: 12 012 04 Floating point Rounding: 1.500000 2 1.30000000000000004440892098500626 Padding: 01.50 1.50 1.50 Scientific: 1.500000E+00 1.500000e+00 Hexadecimal: 0x1.8p+0 0X1.8P+0 fprintf 定義 f 代表的是 file，透過 fprintf 能專門幫助我們處理「寫進檔案的字串」。\n\u0026gt; fprintf int fprintf ( FILE * stream, const char * format, ... ); Write formatted data to stream Writes the C string pointed by format to the stream. If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers. After the format parameter, the function expects at least as many additional arguments as specified by format. \u0026gt; http://www.cplusplus.com/reference/cstdio/fprintf/ 範例程式碼 - fprintf 我們將寫出的檔案寫在 「\u0026ldquo;file.txt\u0026rdquo;」 的檔案裡面。\n#include \u0026lt;stdio.h\u0026gt; using namespace std; void test_fprintf() { FILE * fp; fp = fopen(\u0026#34;file.txt\u0026#34;, \u0026#34;w+\u0026#34;); fprintf(fp, \u0026#34;%s %s %s %d!!!\u0026#34;, \u0026#34;Hello\u0026#34;, \u0026#34;world\u0026#34;, \u0026#34;in\u0026#34;, 2021); fclose(fp); } int main() { printf(\u0026#34; ------ test_fprintf ------ \u0026#34;); test_fprintf(); return 0; } 編譯與結果 \u0026gt; g++ printf_test.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out 在同目錄底下的 「\u0026ldquo;file.txt\u0026rdquo;」 檔案裡面： Hello world in 2021!!! sprintf 定義 s 代表的是 string，透過 sprintf 能幫助我們把字串保存在變數裡面。\n\u0026gt; sprintf int sprintf ( char * str, const char * format, ... ); Write formatted data to string Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by str. The size of the buffer should be large enough to contain the entire resulting string (see snprintf for a safer version). A terminating null character is automatically appended after the content. After the format parameter, the function expects at least as many additional arguments as needed for format. \u0026gt; https://www.cplusplus.com/reference/cstdio/sprintf/ 範例程式碼 - sprintf 以下的程式碼提供的兩種印出 string 訊息的方式：\nprintf(\"%s \", str); - puts(str); 可以自行比較一下差別，與找尋自己喜歡的用法。\n#include \u0026lt;stdio.h\u0026gt; using namespace std; void test_sprintf() { const char* s = \u0026#34;Hello\u0026#34;; sprintf(str, \u0026#34;%s\u0026#34;, s); printf(\u0026#34;%s \u0026#34;, str); puts(str); } int main() { printf(\u0026#34; ------ test_sprintf ------ \u0026#34;); test_sprintf(); return 0; } 編譯與結果 \u0026gt; g++ printf_test.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out ------ test_sprintf ------ Hello Hello snprintf 定義 snprintf 與 sprintf 用絕大部分都相同，\n只差在多一個 n ，代表的是 numbers of bytes 的控制，\n相對於 sprintf 會更安全的控制好電腦內儲存空間的限制，\n但如果還是新手，或還沒有很注重 注重「電腦內儲存空間的限制」的開發者，\n可以先使用「sprintf」。\n\u0026gt; snprintf int snprintf ( char * s, size_t n, const char * format, ... ); Write formatted output to sized buffer Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by s (taking n as the maximum buffer capacity to fill). If the resulting string would be longer than n-1 characters, the remaining characters are discarded and not stored, but counted for the value returned by the function. A terminating null character is automatically appended after the content written. After the format parameter, the function expects at least as many additional arguments as needed for format. \u0026gt; http://www.cplusplus.com/reference/cstdio/snprintf/ 範例程式碼 - snprintf (正常使用，感受不到他的重要性) 來自官方程式碼 #include \u0026lt;stdio.h\u0026gt; using namespace std; void test_snprintf() { char buffer [100]; int cx; cx = snprintf ( buffer, 100, \u0026#34;The half of %d is %d\u0026#34;, 60, 60/2 ); if (cx\u0026gt;=0 \u0026amp;\u0026amp; cx\u0026lt;100) // check returned value snprintf ( buffer+cx, 100-cx, \u0026#34;, and the half of that is %d.\u0026#34;, 60/2/2 ); puts (buffer); } int main() { printf(\u0026#34; ------ test_snprintf ------ \u0026#34;); test_snprintf(); return 0; } 編譯與結果 \u0026gt; g++ printf_test.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out ------ test_snprintf ------ The half of 60 is 30, and the half of that is 15. 範例程式碼 - snprintf (不正常使用，我們才能知道他的重要性) 我們先宣告一個「 buffer[10] 」的空間給系統知道，\n但我們故意存入一個15+1(含結束字元\u0026rsquo;\\0\u0026rsquo;)「\u0026ldquo;abcdeabcdeabcde\u0026rdquo;」，給這個 buffer，\n想當然，16個位置怎麼存得進去10個位置，\n應該要有未知的問題發生。\n#include \u0026lt;stdio.h\u0026gt; using namespace std; void test_snprintf() { char buffer[10]; int cx; // not safe // sprintf ( buffer, \u0026#34;abcdeabcdeabcde\u0026#34; ); // puts(buffer); cx = snprintf ( buffer, 10, \u0026#34;abcdeabcdeabcde\u0026#34; ); puts(buffer); cout \u0026lt;\u0026lt; \u0026#34;cx = \u0026#34; \u0026lt;\u0026lt; cx \u0026lt;\u0026lt; \u0026#34;, strlen(buffer) = \u0026#34; \u0026lt;\u0026lt; strlen(buffer) \u0026lt;\u0026lt; endl; // cx = 15, strlen(buffer) = 9 // abcdeabcd\\0 // 0123456789, that is, *(buffer+9) = \\0 } int main() { printf(\u0026#34; ------ test_snprintf ------ \u0026#34;); test_snprintf(); return 0; } 編譯與結果 正常使用 - snprintf \u0026gt; g++ test3.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out test3.cpp: In function ‘void test_snprintf()’: test3.cpp:7:6: warning: ‘abcdeabcdeabcde’ directive output truncated writing 15 bytes into a region of size 10 [-Wformat-truncation=] void test_snprintf() ^~~~~~~~~~~~~ test3.cpp:15:6: note: ‘snprintf’ output 16 bytes into a destination of size 10 cx = snprintf ( buffer, 10, \u0026#34;abcdeabcdeabcde\u0026#34; ); ~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------ test_snprintf ------ abcdeabcd cx = 15, strlen(buffer) = 9 可以看出： - 程式跳出了 warning，警告空間不足的問題 - 但因為 snprintf 限制了最大大小，所以只有存入 「abcdeabcd + '\\0'」 ，共10個字元 - 「程式有正常的執行結束！」 異常對照組 - 使用 sprintf \u0026gt; g++ test3.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out test3.cpp: In function ‘void test_snprintf()’: test3.cpp:12:11: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ writing 16 bytes into a region of size 10 overflows the destination [-Wstringop-overflow=] sprintf ( buffer, \u0026#34;abcdeabcdeabcde\u0026#34; ); ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------ test_snprintf ------ abcdeabcdeabcde strlen(buffer) = 15 *** stack smashing detected ***: \u0026lt;unknown\u0026gt; terminated [1] 10889 abort (core dumped) ./a.out 可以看出： - 程式也有跳出了 warning，警告空間不足的問題 - 因為沒有特別限制儲存空間大小，所以 「abcdeabcdeabcde + '\\0'」全部都存入了 ，共16個字元 - 「程式 \"沒有\" 正常的執行結束！，最後跳出了 core dumped。」 Reference printf、fprintf、sprintf和snprintf 区别 printf、fprintf、sprintf和snprintf 區別 vprintf printf、fprintf、sprintf和snprintf 區別 vprintf printf va_start vprintf printf区别 不定長度引數 vsnprintf用法解析 printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 輸出格式轉換 C中snprintf与vsnprintf函数，自定义可变参数格式化字符串 prinf 家族：printf, fprintf, sprintf, snprintf 區別 官方文件： printf, fprintf, sprintf, snprintf, printf_s, fprintf_s, sprintf_s, snprintf_s std：string formatting like sprintf sprintf() - C語言庫函數 ","date":"2021-04-13T15:49:35+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-string/cpp-printf/","tags":["C++","C++ 字串處理","字串處理"],"title":"【C++ 字串處理 #3】C | printf, fprintf, sprintf, snprintf 相關用法總整理"},{"categories":["212 - C++ 字串處理"],"content":"前言 此文章中會整理所有在 C/C++ 字串 的相關用法，包含 char string stringstream\n(與利用 sprinf, snprinf，assign值的方法)，\n其中 char 我們又會分成 char array, char pointer 介紹，以及會介紹他們彼此之間怎麼互相轉換。\n我們一共會介紹這些：\nchar array, char pointer (與利用 sprinf, snprinf，assign值的方法) String stringstream 先講結論，我們先觀察以下程式碼與結果 建議先自己觀察以下程式碼與結果，會比我直接講結論學得更快哦！\n範例程式碼 #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { char s0[10] = \u0026#34;Hello\u0026#34;; char s1[] = \u0026#34;Hello\u0026#34;; const char* s2 = \u0026#34;Hello\u0026#34;; string s3 = \u0026#34;Hello\u0026#34;; stringstream s4; cout \u0026lt;\u0026lt; \u0026#34; ------ s0 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s0).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s0 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s1 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s1).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s2 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s2).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s3 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s3).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s3 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s4 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4).name() \u0026lt;\u0026lt; endl; s4 \u0026lt;\u0026lt; s3; cout \u0026lt;\u0026lt; typeid(s4).name() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4.str()).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s4.str() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4.str().c_str()).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s4.str().c_str() \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ test.cpp -std=c++11 -o a.out \u0026amp;\u0026amp; ./a.out | c++filt --types; ------ s0 testing ------ char [10], Hello ------ s1 testing ------ char [6], Hello ------ s2 testing ------ char const*, Hello ------ s3 testing ------ std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, Hello char const*, Hello ------ s4 testing ------ std::__cxx11::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; std::__cxx11::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, Hello char const*, Hello 觀察與結論 Data type 主要特色 Char array [C] 基本的資料型態，一開始就決定資料儲存使用的空間大小 Char pointer [C] 基本的指標型態，資料所使用的儲存空間大小不一定 String [C++] 特別定義做字串處理用的資料型態 stringstream [C++] 主要功能並不是「作為字串使用」，只是作為資料型態轉換過度的橋樑 整理 char array, char pointer 這是 C/C++ 最基本的資料型態，\n相信會點進來的讀者對這都已經有基本概念了。\n我們主要知道，char 是代表「一個字元」的儲存。\n所以要儲存字串，要使用「 char array 」或是「 char pointer 」，\n我們才會得到「一個字元以上」的儲存空間！\n範例程式碼 - 遍歷字串 - char array, char pointer 我們使用「#include \u0026lt;string.h\u0026gt;」這個標頭檔，\n這個標頭檔定義了 「strlen()」的方法，使我們能使用來判斷 char array 長度。\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;string.h\u0026gt; using namespace std; int main() { int a = 2; float b = 3.0; char s0[10] = \u0026#34;Hello\u0026#34;; char s1[] = \u0026#34;Hello\u0026#34;; const char* s2 = \u0026#34;Hello\u0026#34;; for(int i=0; i \u0026lt; strlen(s0); i++) { cout \u0026lt;\u0026lt; s0[i] \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; *(s0+i) \u0026lt;\u0026lt; endl; } for(int i=0; i \u0026lt; strlen(s1); i++) { cout \u0026lt;\u0026lt; s1[i] \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; *(s1+i) \u0026lt;\u0026lt; endl; } for(int i=0; i \u0026lt; strlen(s2); i++) { cout \u0026lt;\u0026lt; s2[i] \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; *(s2+i) \u0026lt;\u0026lt; endl; } return 0; } 編譯與結果 - char array, char pointer \u0026gt; g++ test.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out H H e e l l l l o o H H e e l l l l o o H H e e l l l l o o 使用注意事項： 如何複製 char array? 只要是不同大小，都沒有辦法直接進行轉換。 以下皆會跳錯：\ns1 = s0; s0 = s1; datatype_test.cpp:59:8: error: incompatible types in assignment of ‘char [10]’ to ‘char [6]’ s1 = s0; ^~ datatype_test.cpp:60:8: error: incompatible types in assignment of ‘char [6]’ to ‘char [10]’ s0 = s1; ^~ 正確使用方式 - strcpy strcpy(dst, src); // 從 src (source) 複製到 dst (destination) 可使用範圍： char array \u0026lt;\u0026mdash;\u0026gt; char array char array \u0026mdash;\u0026gt; char pointer (只可以單向) 特別注意：從大複製到小，會有問題，「且不會跳 error 」。 Debug 時需特別注意。\nstrcpy(s0, s1); strcpy(s0, s2); strcpy(s2, s0); // error char 無法轉成 char* 將 char array 轉成 char pointer (char array to char pointer) pointer 本身就是資料的指標，我們只需要將資料的指標指向他就完成囉！\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;iostream\u0026gt; using namespace std; int main() { char s0[30] = \u0026#34;Hello World!\u0026#34;; char* s1 = s0; cout \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; char* s2 = \u0026amp;s0[0]; cout \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; } 編譯與結果 \u0026gt; g++ test4.cpp -o a.out \u0026amp;\u0026amp; ./a.out Hello World! Hello World! 如何 assign 值 進入 char array ? 主要有兩種方法： strcpy sprintf / snprintf - 其中「sprintf / snprintf」的差別只在於空間的使用控制，詳細可以參考以下文章： https://wongwongnotes.com/posts/cpp/basics/cpp-string/cpp-printf/\n範例程式碼 - assign 值 進入 char array #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; using namespace std; int main() { char buffer1[10]; char buffer2[10]; strcpy( buffer1, \u0026#34;abcdea\u0026#34; ); puts(buffer1); sprintf( buffer2, \u0026#34;abcdea\u0026#34; ); puts(buffer2); return 0; } 編譯與結果 \u0026gt; g++ test4.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out abcdea abcdea 更多「轉換方法」的總整理，我有整理在另外一篇文章： https://wongwongnotes.com/posts/cpp/basics/cpp-string/convert-char-string-stringstream/\n小提醒：在 C++ 裡面，單引號「\u0026rsquo;」與雙引號「\u0026quot;」是有嚴格的差別的 小提醒：在 C++ 裡面，單引號「\u0026rsquo;」與雙引號「\u0026quot;」是有嚴格的差別的\n(不像是隔壁棚的 python)\n單引號「\u0026lsquo;a\u0026rsquo;」代表的 char，是一個字元\n雙引號「\u0026ldquo;a\u0026rdquo;」代表的是 string，是字串\n範例 - C++ 中 單引號「\u0026lsquo;a\u0026rsquo;」，與 雙引號「\u0026ldquo;a\u0026rdquo;」的差別 char *test1=\u0026#39;a\u0026#39;; char *test2=\u0026#34;a\u0026#34;; 編譯與結果 - 這邊為故意讓他跳錯，我們要看的是類別 注意以下的錯誤訊息： 「*test1=’a’;」的錯誤說明，我們嘗試將 char 轉成 char * 「*test2=”‘a”;」的錯誤說明，我們嘗試將 string 轉成 char * 我們得知： 單引號「’a’」的類別為 char 雙引號「”a”」的類別為 string 「*test1」的類別為 char * 「*test2」的類別為 char * \u0026gt; g++ test3.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out test3.cpp: In function ‘void test_snprintf()’: test3.cpp:11:15: error: invalid conversion from ‘char’ to ‘char*’ [-fpermissive] char *test1=\u0026#39;a\u0026#39;; ^~~ test3.cpp:14:15: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings] char *test2=\u0026#34;a\u0026#34;; ^~~ 如何 assign 值 進入 char pointer ? 範例程式碼 - assign 值 進入 char pointer 依照 pointer 的概念，我們宣告一個新位置，\n並將舊位置指定到新位置，即可完成任務。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; using namespace std; int main() { char *buffer1; char *buffer2 = \u0026#34;abcdea\u0026#34;; buffer1 = buffer2; // assign pointer to pointer puts(buffer1); puts(buffer2); return 0; } 編譯與結果 可以注意到有跳出 warning，主要是因為宣告 char * ，\n我們會建議宣告時使用 const char *，表示我們宣告的是常數。\n\u0026gt; g++ test4.cpp -o a.out \u0026amp;\u0026amp; ./a.out test4.cpp: In function ‘int main()’: test4.cpp:9:19: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings] char *buffer2 = \u0026#34;abcdea\u0026#34;; ^~~~~~~~ abcdea abcdea String string 是 C++ 特別定義做字串處理用的資料型態。\n範例程式碼 - 遍歷字串 - string string 已經是 c++ 內建的資料型態，我們不需要另外 include 其他標頭檔。\n想要得到 string 長度有兩種方法，size() 與 length()，\n據官方文件所述，兩者功能沒有差別。只是就表達程式碼語意來說，可以自行選擇哪個能讓人比較好懂。\n#include \u0026lt;iostream\u0026gt; using namespace std; int main() { string s3 = \u0026#34;Hello\u0026#34;; for(int i = 0; i \u0026lt; s3.length(); i++) { cout \u0026lt;\u0026lt; s3[i] \u0026lt;\u0026lt; endl; } for(int i = 0; i \u0026lt; s3.size(); i++) { cout \u0026lt;\u0026lt; s3[i] \u0026lt;\u0026lt; endl; } return 0; } 編譯與結果 - string \u0026gt; g++ test2.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out H e l l o H e l l o char pointer 與 string 如何轉換? 相信這也是讀者接下來會想問的問題，(不然這至少也是我會想問的問題XD)\n我們就把問題拆開來看吧！\n將 char pointer 轉成 string (char pointer to string) 很簡單，直接等於「=」就好了！\n記法的話，也許你可以想像 Char 比較基本，string 是後來的，作法當然很簡單！\n我們可以用另外一篇文章提供的 type_name 方法來檢驗。\n可參考： https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-data-type/\n範例程式碼 - 將 string 轉成 char pointer (string to char pointer) #include \u0026lt;iostream\u0026gt; using namespace std; void show_type(auto s) { cout \u0026lt;\u0026lt; typeid(s).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s \u0026lt;\u0026lt; endl; } int main() { char src[] = \u0026#34;HelloWorld\u0026#34;; string dst; dst = src; show_type(src); show_type(dst); cout \u0026lt;\u0026lt; dst \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 注意結果的「char*」！！！\n\u0026gt; g++ test2.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out | c++filt --types; char*, HelloWorld std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, HelloWorld HelloWorld 將 string 轉成 char pointer (string to char pointer) 其實也很簡單，都已經幫我們做好了，只需要下「c_str()」即可。\n範例程式碼 - 將 string 轉成 char pointer (string to char pointer) #include \u0026lt;iostream\u0026gt; #include \u0026lt;string.h\u0026gt; using namespace std; void show_type_and_content(auto s) { cout \u0026lt;\u0026lt; typeid(s).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s \u0026lt;\u0026lt; endl; } int main() { string src = \u0026#34;HelloWorld\u0026#34;; char dst1[1024]; char *dst2 = new char [src.length()+1]; strcpy (dst1, src.c_str()); strcpy (dst2, src.c_str()); show_type_and_content(src); show_type_and_content(src.c_str()); show_type_and_content(dst1); show_type_and_content(dst2); return 0; } 編譯與結果 注意結果的「char const*」、「char*」！！！\n\u0026gt; g++ test2.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out | c++filt --types; std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, HelloWorld char const*, HelloWorld char*, HelloWorld char*, HelloWorld stringstream stringstream 主要功能並不是「作為字串使用」，只是作為資料型態轉換過度的橋樑。\n例如：int 轉換成 stringstream 再轉換成 string。 (反過來也行~)\n使用時，需 include 標頭檔「#include 」\n範例程式碼 - stringstream， 與將 stringstream 轉成 string, char pointer (stringstream to string, char pointer) #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { string s3 = \u0026#34;Hello\u0026#34;; stringstream s4; cout \u0026lt;\u0026lt; typeid(s4).name() \u0026lt;\u0026lt; endl; s4 \u0026lt;\u0026lt; s3; cout \u0026lt;\u0026lt; typeid(s4).name() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4.str()).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s4.str() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4.str().c_str()).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s4.str().c_str() \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 - string \u0026gt; g++ datatype_test.cpp -std=c++14 -o a.out \u0026amp;\u0026amp; ./a.out | c++filt --types; std::__cxx11::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; std::__cxx11::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, Hello char const*, Hello Reference char array, string, stringstream c++：像sprintf一樣的std :: string格式 How to convert a char array to a string? 字串長度、複製、串接 strlen() - C語言庫函數 Finding the length of a Character Array in C C++ - pointer to char、array of pointer to char、pointer to array of char (C++軟體開發 - 指標與字元與陣列 概念與實例) stringstream用法整理 ","date":"2021-04-08T12:24:06+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-string/c-cpp-string-all/","tags":["C++","C++ 字串處理","字串處理"],"title":"【C++ 字串處理 #2】字串 char string stringstream 相關用法總整理 與利用 sprinf, snprinf assign 值的方法"},{"categories":["211 - C++ 基礎語法"],"content":"前言 我們很常會想要知道一個 C++ 變數的 資料型態 (type)，\n但 C++ 難道沒有像 python 一樣直接 type() 就能直接看出資料型態 (type) 的 function 嗎?\n雖然有點不同，但其實還是有的！！！\n我覺得這篇應該會有很多人有跟我一樣的問題！！\n使用方法 - 主要使用 typeid 相關的功能 typeid(T).name() 範例程式碼 #include \u0026lt;iostream\u0026gt; using namespace std; int main() { const char* s = \u0026#34;Hello\u0026#34;; cout \u0026lt;\u0026lt; typeid(s).name() \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ test.cpp -std=c++11 -o a.out \u0026amp;\u0026amp; ./a.out PKc 「PKc」就是我們得到的結果，但我們看不懂啊！！！似乎我們還要想辦法去翻譯一下?\n註：\n我們印出來的是 g++ decorated name，也就是說我們需要去解構 (demangle) 他，詳細可參考下方連結。\nWhat you get from g++ is a decorated name, that you can \u0026ldquo;demangle\u0026rdquo; using the c++filt command or __cxa_demangle.\n參考此文：https://stackoverflow.com/questions/4465872/why-does-typeid-name-return-weird-characters-using-gcc-and-how-to-make-it-prin\n整理格式 依照上方連結的作法，我們在編譯時稍微修改一下 flag，\n編譯與結果 ❯ g++ datatype_test.cpp -std=c++11 -o a.out \u0026amp;\u0026amp; ./a.out | c++filt --types; char const* 「char const*」就是我們得到的結果，我們終於看得懂啦！！！\nPlayground - 觀察 char array, char*, string, stringstream 之間的關係 (可觀察變化學習)，後續會有文章專門討論 範例程式碼 #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; using namespace std; int main() { int a = 2; float b = 3.0; char s0[10] = \u0026#34;Hello\u0026#34;; char s1[] = \u0026#34;Hello\u0026#34;; const char* s2 = \u0026#34;Hello\u0026#34;; string s3 = \u0026#34;Hello\u0026#34;; stringstream s4; cout \u0026lt;\u0026lt; typeid(a).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; a \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(b).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; b \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s0 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s0).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s0 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s1 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s1).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s1 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s2 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s2).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s2 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s3 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s3).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s3 \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34; ------ s4 testing ------ \u0026#34; \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4).name() \u0026lt;\u0026lt; endl; s4 \u0026lt;\u0026lt; s3; cout \u0026lt;\u0026lt; typeid(s4).name() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4.str()).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s4.str() \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; typeid(s4.str().c_str()).name() \u0026lt;\u0026lt; \u0026#34;, \u0026#34; \u0026lt;\u0026lt; s4.str().c_str() \u0026lt;\u0026lt; endl; return 0; } 編譯與結果 \u0026gt; g++ datatype_test.cpp -std=c++11 -o a.out \u0026amp;\u0026amp; ./a.out | c++filt --types; int, 2 float, 3 ------ s0 testing ------ char [10], Hello ------ s1 testing ------ char [6], Hello ------ s2 testing ------ char const*, Hello ------ s3 testing ------ std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, Hello char const*, Hello ------ s4 testing ------ std::__cxx11::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; std::__cxx11::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; std::__cxx11::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, Hello char const*, Hello Refernce https://stackoverflow.com/questions/4465872/why-does-typeid-name-return-weird-characters-using-gcc-and-how-to-make-it-prin\nhttps://docs.microsoft.com/zh-tw/cpp/cpp/typeid-operator?view=msvc-160\nhttps://en.cppreference.com/w/cpp/language/typeid\nhttps://iter01.com/553394.html\nhttps://github.com/willwray/type_name#HH\nhttps://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c\n","date":"2021-04-07T18:28:28+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-data-type/","tags":["C++","C++ 字串處理","基礎語法"],"title":"【C++ 基礎語法 #2】C/C++ 顯示資料的類別（type）— printf / cout 取得 variable type"},{"categories":["299 - C++ 問題解決"],"content":"前言 此為 C++11 decltype 上遇到以下問題，個人的解決問題筆記\nerror: expected primary-expression before ‘decltype’ 解決方法 這是個邏輯問題，\n我們可以直接思考一個問題：\ncout \u0026lt;\u0026lt; int; 這樣的程式碼正確嗎?\n如果你想通了，那你應該就知道不能這樣做。\n(資料型態是不能這樣印出來的！！！)\n正確使用方式範例 int x = 0; decltype(x) y = 1; // y -\u0026gt; int 這裡y的類別就是 int ，是經由 decltype(x) 得到的。\n你應該是想要這樣做 雖然還不夠完美，但至少是能得到類別名稱的方法。\ncout \u0026lt;\u0026lt; typeid(x).name(); reference https://stackoverflow.com/questions/56002554/error-expected-primary-expression-before-decltype https://stackoverflow.com/questions/20804871/receiving-expected-primary-expression-when-calling-templated-function-from-wit https://tw511.com/a/01/3084.html http://blog.sina.com.cn/s/blog_49366773010179vx.html http://www.cplusplus.com/forum/general/227824/ https://www.javaer101.com/en/article/12992744.html https://www.javaer101.com/en/article/36362825.html https://www.reddit.com/r/cpp_questions/comments/dkpcl2/why_do_i_get_the_error_expected_primaryexpression/ ","date":"2021-04-06T14:30:48+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/cpp-error-decltype/","tags":["C++"],"title":"【C++】問題解決: error: expected primary-expression before ‘decltype’ (內含正確使用方式範例)"},{"categories":["914 - Wordpress"],"content":"前言 前一段時間剛好有想要放自己的一些演講的簡報 (google slides) 到我的 wordpress 上，\n但發現好像除了直接提供連結以外沒什麼好方法?\n後來仔細研究了之後找到了能直接顯示 google slides 的方法!\n這邊來分享給大家~~~\n效果示意圖：(在 wordpress 的文章中直接顯示 google slides) 引用自我的另一篇文章：【機器學習】pyladies 一日 AI 工程師 - Deep learning with PyTorch 演講講義分享 機器學習 深度學習 Deep learning Machine learning 詳細步驟 step1. 在 google slides 上面發布 如果是 powerpoint, ppt 或其他簡報，可以先上傳至 google slides\n再進行相同處理哦!\n我們需要先講我們的投影片發佈到網路上，如下圖。\n示意圖: (點選發佈到網路) step2. 發布 google slides 至網路上! 這邊基本上可以直接按「發布」，除非有什麼特別想要更改的設定。\n示意圖： step3. 點選「內嵌」，取得連結網址，貼至文章 (請依個人需求調整大小) 再來很簡單，就點選「內嵌」，把下面那一坨複製起來貼進文章就可以囉!\n如果你需要調整大小：\n請修改 width=\u0026ldquo;640\u0026rdquo; height=\u0026ldquo;380\u0026rdquo; 裡面的數字，分別為寬、高\n(每個網站不同，請自行調整至最適合大小)\n示意圖： Reference https://www.wpbeginner.com/plugins/how-to-add-google-slides-presentations-to-wordpress/ ","date":"2021-04-05T02:35:58+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/3.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/wordpress-google-slides/","tags":["WordPress","初學者"],"title":"【Wordpress #4】如何放置 google slides (powerpoint, ppt, 簡報) 在 wordpress 的文章中直接顯示"},{"categories":["912 - Google AdSense"],"content":"前言 不知道大家有沒有在使用 Google AdSense 時也碰過這樣的問題呢? 「很抱歉造成您的不便，但是我們目前無法處理您的要求。我們已將此問題告知工程師，並會儘快加以解決。」我也因為這個問題困擾很久，這篇文章中提供我的解決方法：\n快速解決方法 使用 google chrome 的「無痕模式」即可解決。\n細究原因 我覺得應該是 google 的擴充套件搞的鬼，\n畢竟在 google chrome 的「無痕模式」中是不會載入擴充套件的。\n但因為我使用的 chrome 擴充套件滿多的XD，\n就懶得一個個慢慢找到底是哪個出了問題。\nReference https://support.google.com/adsense/thread/98795459?hl=zh-Hant ","date":"2021-04-04T01:04:30+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/adsense.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/google-adsense/google-adsense-sorry/","tags":[],"title":"【Google AdSense】問題解決: 很抱歉造成您的不便，但是我們目前無法處理您的要求。我們已將此問題告知工程師，並會儘快加以解決。"},{"categories":["921 - iphone / iOS"],"content":"前言 最近看到網路推薦的記帳工具「秒速記帳(1SecSpeed)」，\n結果剛下載下來卻發現沒有顯示任何類別，後來研究了一陣子好不容易找到了解法，\n在此做個筆記XD。\n示意圖: (實際上出現問題時不會顯示上面的) 個人解決方法 刪除 Apple watch 上的 秒速記帳(1SecSpeed) App 先在 Apple watch 畫面長按 app，並刪除。\n如果會擔心資料不見，可以先檢查手機上的資料，\n手機上有資料就不用擔心，我們只刪「Apple watch」的。\n示意圖: (刪除 秒速記帳(1SecSpeed) App) 在 Apple watch 上的 AppStore 重新安裝 秒速記帳(1SecSpeed) App 從 Apple watch 的 AppStore 重新安裝 「秒速記帳(1SecSpeed)」，\n就可以解決「沒有出現類別」的問題。\n示意圖: (重新安裝 秒速記帳(1SecSpeed) App) 個人猜測 我猜應該只是「iphone 與 Apple watch」還沒有那麼快同步，\n所以重新下載讓他重新同步一下。\nReference https://www.1secspeed.com/1SecMoney/?l=zh-Hant\nme ","date":"2021-04-02T23:59:58+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7783.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/1secspeed-no-category/","tags":["iOS"],"title":"【iOS】問題解決: Apple watch 秒速記帳(1SecSpeed) 沒有出現類別 (個人解法)"},{"categories":["DFS (Tree Basic)"],"content":"題目出處 https://leetcode.com/problems/maximum-depth-of-binary-tree/\n難度 Easy\n題目分類 Tree, DFS, Recursion\n個人手繪解法筆記 (解法重點) 這題就是簡單考樹的遍歷，\n用 while 來一層一層刷出深度。\n從 list 翻譯樹的長相 示意圖: (我們就是照著紅色箭頭方向一個個節點看左右的。) 基本的確認，我們先確認有沒有這棵樹 總是會有一些要多考慮的事情\u0026hellip; 沒有樹的話我們要先處理\n(很討厭這種整個算法幾乎都寫對，結果錯「最簡單現象」的感覺XDD)\n一層層的遍歷，每個節點左右都看一下 每一層遍歷各節點的左右，這邊可以抓一個感覺，\n樹的成長 (level)，大概是照著 1 -\u0026gt; 2 -\u0026gt; 4 -\u0026gt; 8 -\u0026gt; 16\n這樣的數值在快速變化的。\n個人範例程式碼 class Solution: def maxDepth(self, root: TreeNode) -\u0026gt; int: depth = 0 if root: level = [root] else: level = [] while(level): depth += 1 next_level = [] for ele in level: if ele.left: next_level.append(ele.left) if ele.right: next_level.append(ele.right) level = next_level return depth Reference https://leetcode.com/problems/maximum-depth-of-binary-tree/discuss/34345/Python-BFS-solution ","date":"2021-04-02T00:34:35+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7770-1-1024x828.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/search-algorithms/dfs-tree-basic/leetcode-python-104/","tags":["DFS"],"title":"【Leetcode】python - [104] Maximum Depth of Binary Tree 個人手繪解法筆記"},{"categories":["Two pointers (相向雙指針 →←) / start, end (頭尾雙指針)"],"content":"題目出處 15. 3Sum\n難度 Medium\n題目分類 Array, TwoPointers\n個人範例程式碼 - two pointers (第二次解：2022/4/6) class Solution: def threeSum(self, nums: List[int]) -\u0026gt; List[List[int]]: nums.sort() ans = [] for idx, target in enumerate(nums): if idx \u0026gt; 0 and nums[idx] == nums[idx-1]: # prevent duplicate continue ans = self.two_sum(nums, ans, idx+1, len(nums)-1, -target) return ans def two_sum(self, nums, ans, start, end, target): last_ans = [] while(start \u0026lt; end): if(nums[start] + nums[end] == target): if [-target, nums[start], nums[end]] != last_ans: # prevent duplicate last_ans = [-target, nums[start], nums[end]] ans.append(last_ans) start += 1 end -= 1 elif(nums[start] + nums[end] \u0026lt; target): start += 1 else: # if(nums[start] + nums[end] \u0026gt; target): end -= 1 else: return ans 算法說明 3Sum 可以當作是 2Sum 的進階版，\n而變化的部分在於 target 的變化變成可以動態的，\n可以視為：3Sum = 2Sum + 「動態 target」\n因此我們可以沿用 2Sum 的作法，\n並把 2Sum 中 target 改為動態的尋找。\n最近在練習程式碼本身就可以自解釋的 Coding style，可以嘗試直接閱讀程式碼理解\ninput handling 處理 input 為 [] 的情況，應該要輸出 []。\n處理 input 為 [0, 0, 0] 的情況，應該也要輸出 [[0, 0, 0]]。\nBoundary conditions 有幾個重點：\n防止重複 1 : 「a \u0026lt;= b」 \u0026lt;= c 防止重複的第一個部分，就是避免重複數值時，\na, b 都被重算，我們都統一取第一個去運算。\n此外需要特別注意：因為判斷式為 「nums[idx] nums[idx-1]」，需要再多判斷一個「idx \u0026gt; 0」。 if idx \u0026gt; 0 and nums[idx] == nums[idx-1]: # prevent duplicate continue 防止重複 2 : a \u0026lt;=「b \u0026lt;= c」 這邊基本上我們統一拿來結果來判斷，只要 b, c 組合不相同，就視為不同的解。\n3. two pointers: start \u0026lt; end，交錯或等於就結束 two pointers 的基本結束條件。\n個人解法筆記 - (第一次解：2021/4/2) 這題是第 1 題的延伸題目，這次我們要處理 3數之和 的問題。\n大概念 我們將這個題目看成，「任兩數和」 等於 「負的第三數」\n「負的第三數」我們稱之為我們要的 target\n為了更乾淨我們處理的順序，我們會先進行「排序」\n示意圖：\nTwoPointers 在排序後，我們選定 target，我們使用 TwoPointers，\n從左 ( l ) 開始小往大掃、從右 ( r ) 開始大往小掃，\n當左( l ) 大於等於右( r ) ，也就是交錯甚至超過。\n就是我們的終止條件。\n示意圖：\n重複處理 case1 : target 的重複 重複時的選擇：我們選擇第一個 target。\n除了 target 重複會造成重複解答之外，\n選擇第一個也能夠保證當解答中有與target重複的數值也能被我們考慮到。\n示意圖：(處理 target 的重複) 可以觀察 i 與 target 的變化，仔細思考，可以得到一些心得： 重複處理 case2 : r, l 選到值的重複 重複時的選擇：我們選擇「l」的第一個值，r不同時處理。 因為，當我們得到答案並更新「l」之後，對應的 nums[r] 直接不成立。\n思考時，只需要思考單邊重複如何處理即可。 (多思考會錯，可以見下面錯誤區)\n選擇第一個值的原因是，我們預期「剩下的值」有可能存在答案。\n而當找到答案時，我們再將「l」移至下一個值 (也就是說，反覆 l+1 直到值不同)\n這樣才能處理極端狀況：[0, 0, 0]\n示意圖：(處理 l, r 的重複) 個人範例程式碼 class Solution: def threeSum(self, nums: List[int]) -\u0026gt; List[List[int]]: nums.sort() ans = [] # decide target for i in range(len(nums)): if(i \u0026gt; 0 and nums[i-1] == nums[i]): # when same target, only calculate first continue # go to next loop target = -nums[i] # print(f\u0026#34;i = {i}, target = {target}\u0026#34;) # debug l = i+1 r = len(nums)-1 while(l \u0026lt; r and l \u0026lt; len(nums) and r \u0026gt;= 0): # normal condition # print(f\u0026#34;l = {l}, r = {r}\u0026#34;) # debug if nums[l] + nums[r] == target: ans.append([nums[i], nums[l], nums[r]]) l += 1 while l \u0026lt; r and nums[l] == nums[l-1]: # when same value, only calculate first l += 1 elif nums[l] + nums[r] \u0026gt; target: # need smaller, r - 1 r -= 1 else: # nums[l] + nums[r] \u0026lt; target: need bigger, l + 1 l += 1 return ans 「錯誤紀錄」 手繪筆記 重點：錯誤錯在兩邊同時尋找，無法解[0,0,0] 這個 case!\n這題是第 1 題的延伸題目，這次我們要處理 3數之和 的問題。\n大概念 我們將這個題目看成，「任兩數和」 等於 「負的第三數」\n「負的第三數」我們稱之為我們要的 target\n為了更乾淨我們處理的順序，我們會先進行「排序」\n示意圖：\nTwoPointers 在排序後，我們選定 target，我們使用 TwoPointers，\n從左 ( l ) 開始小往大掃、從右 ( r ) 開始大往小掃，\n當左( l ) 大於等於右( r ) ，也就是交錯甚至超過。\n就是我們的終止條件。\n示意圖：\n重複處理 case1 : target 的重複 重複時的選擇：我們選擇第一個 target。\n除了 target 重複會造成重複解答之外，\n選擇第一個也能夠保證當解答中有與target重複的數值也能被我們考慮到。\n示意圖：\n重複處理 case2 : r, l 選到值的重複 重複時的選擇：我們選擇最後一個值。\n選擇最後一個值的原因是，我們預期「剩下兩個值」的組合應該會不同。\n所以我們只看最後一個。\n而且，就算真的重複了。(例如極端狀況：[0, 0, 0]) (此方法並沒有辦法處理)\n我們也會先進行 -target = nums[l] + nums[r] 的檢查後，\n抓出這樣的極端情況。\n示意圖：\nReference 三数之和 · 3Sum https://leetcode.com/problems/3sum/discuss/7498/Python-solution-with-detailed-explanation ","date":"2021-04-02T00:27:36+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_7761-2-1024x768.jpg","permalink":"https://wongwongnotes.com/posts/algorithm-practice/pointers/two-pointers-start-end/leetcode-python-15/","tags":[],"title":"【Leetcode】python - [15] 3Sum 個人解法筆記"},{"categories":["350 - Linux 終端機操作"],"content":"前言 這篇是給 linux/ubuntu/mac 終端機 (terminal) 新手使用者的教學總整理，\n這篇只有提供小技巧，「並沒有」包含指令介紹~\n如果需要各個「指令」詳細的內容，請參考這篇文章~\nhttps://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-ubuntu-terminal-basic/\n終端機 (terminal) 常用快速鍵 指令 功能 備註 Ctrl + PageUp/PageDown 切換上/下一個 terminal 分頁 (常用) Ctrl + C 強制結束程式/指令 (常用) 例如：指令/程式執行一半，想要強制中斷 Ctrl + C 中斷輸入指令 (常用) 例如：指令打到一半發現打錯想要重打 description: \u0026ldquo;整理 Ubuntu 系統實用快捷鍵的集合與說明 — Ctrl+PageUp/PageDown 分頁切換、Ctrl+C 中斷指令、以及常用終端機快速鍵。\u0026rdquo; ","date":"2021-03-31T14:50:11+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/environment/linux-ubuntu-hotkey/","tags":["Bash","Linux","Ubuntu","終端機操作"],"title":"【Linux 終端機操作 #0】linux/ubuntu/mac 基礎終端機 (terminal) 快速鍵/小技巧，初學者/新手 必須知道的一些實用功能！（持續更新）"},{"categories":["719 - 其他問題"],"content":"前言 問題解決: Login failed. Please make sure the credential is correct.\n解決方法 不知道是什麼bug\u0026hellip; 總之就先照網路上的解法解決吧!\n1. 先去 leetcode 官網登入 沒什麼好說的，連結在這裡: https://leetcode.com/\n2. 開啟 google chrome 的 檢查模式 (可以按 F12 或 右健選「檢查」) google chrome 的 檢查模式的長相大概像下圖這樣：\n3. 選擇 network，重新整理網頁，找到 graphql 這個檔案 參考上圖圈選的位置操作。\n4. 點擊 graphql 這個檔案後，查看 Headers，往下找到 cookie 這個字，右鍵選 copy value 參考上圖圈選的位置操作。\nReference https://www.programmersought.com/article/83515809689/ https://blog.csdn.net/DlMmU/article/details/104129662 https://juejin.cn/post/6844904052141228046 ","date":"2021-03-30T23:34:52+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/leetcode-4.png","permalink":"https://wongwongnotes.com/posts/algorithm-practice/data-structures/misc-problems/leetcode-login-failed/","tags":[],"title":"【Leetcode】問題解決: Login failed. Please make sure the credential is correct. (內附圖文說明)"},{"categories":["912 - Google AdSense"],"content":"前言 這篇是我好幾個月的困擾(應該有快半年?)，\n簡單來說就是我的網站申請 Google AdSense 後，\n卡在「我們正在為你完成設定」 後一直沒有變化。\n我們正在為您完成設定\n這項程序通常會在幾天內完成，但有時最久可能需要 2 週。我們會在一切準備就緒時通知您，接下來您就可以開始刊登廣告來賺取收益。\n就是這張圖，我這幾個月的惡夢XDD：\n基本上網路上也有一堆人有一樣的問題，\n例如：\nhttps://support.google.com/adsense/thread/29583528?hl=zh-Hant https://support.google.com/adsense/thread/42573213?hl=zh-Hant https://support.google.com/adsense/thread/61698083?hl=zh-Hant 但好像 Google 沒有特別為了這類情況提供回報的方式\u0026hellip;\n最多就是可以發文在論壇上，供其他網友回覆 (應該是非官方人員)\n但其實這類問題如果有官方的回報方式，應該是能得到比較好的處理啦\u0026hellip;\n(期望之後可以有! 哈哈哈)\n我的解決方法 老實說我一開始一直因為論壇上面有人「不建議」刪除帳號，\n而導致件事遲遲沒有變化，\n事情都隔了半年，像說就把帳號刪了吧！反正也不能用沒差了！\n結果意外開啟了解決之路\u0026hellip;\n刪除原先進度卡住的帳號 從 「設定 -\u0026gt; 帳戶資訊」 找到 「個人帳戶」\n按下關閉帳戶！\n如果沒辦法正常關閉，請參考下面做法：\nbug \u0026amp; 問題解決: 「出現錯誤，已為您聯繫工程師」之類的敘述 請使用無痕模式登入 google adsense，或使用別的瀏覽器。\n目前不確定具體原因，我推測可能是 chrome插件 造成的 bug\u0026hellip;\n關閉帳戶後，重新申請一個新的帳號 申請完後會變成這樣的畫面！ (終於不再是原本那一個卡住的畫面了！！！)\n結果\u0026hellip;\n原先等待接近半年未審核通過的 google adsense，這次重新審核不到一天就過了！\n(有種莫名感動又莫名心酸\u0026hellip; 早知道就早點這樣做了！)\nReference me\nhttps://support.google.com/adsense/thread/29583528?hl=zh-Hant https://support.google.com/adsense/thread/42573213?hl=zh-Hant https://support.google.com/adsense/thread/61698083?hl=zh-Hant https://support.google.com/adsense/thread/76809896?hl=zh-Hant ","date":"2021-03-29T11:09:05+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/adsense.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/google-adsense/google-adsense-setting/","tags":[],"title":"【Google AdSense】問題解決: 申請 Google AdSense 卡在「我們正在為您完成設定」 後一直沒有變化 (內含圖文說明)"},{"categories":["198 - Python 問題解決"],"content":"前言 這個是當我在研究 dialogflow api 所發生的問題，\n簡單來說就是當 python 串接 dialogflow api 執行時會跳出以下的 error。\n403 IAM permission \u0026#39;dialogflow.sessions.detectIntent\u0026#39; on \u0026#39;projects/xxxxxxx/agent\u0026#39; denied. 以下提供我的解決方法。\n解決方法 簡單來說是金鑰的檔案出了問題，建議將金鑰產生的步驟重新操作。\n以我的個人經驗來說：\n問題是出在 google cloud platfrom 那邊使用了不正確的使用者產生了金鑰，\n導致產生出的 json 結果是有問題的。\n金鑰重新產生的步驟 關於金鑰重新產生的步驟，可以參考我的另外一篇文：\nhttps://wongwongnotes.com/posts/python/web-automation/python-chatbot/python-dialogflow-api/\nReference https://www.timelog.to/a181507263 ","date":"2021-03-28T14:22:09+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/403-iam-permission/","tags":["Python"],"title":"【Python】問題解決：403 IAM permission 'dialogflow.sessions.detectIntent' on 'projects/xxxxxxx/agent' denied."},{"categories":["914 - Wordpress","912 - Google AdSense"],"content":"前言 這篇是當我申請 google Adsense 時，\n碰到 「收益警示：您必須修正某些 ads.txt 檔案問題，以免造成嚴重的收益損失。」的解決方式。\n有時候可能還會有下面這段內容：\n為 1 個網站建立 ads.txt 檔案\n為避免嚴重影響收益，請下載 ads.txt 檔案並上傳至每個網站的根目錄層級網域：\n可能需要等候幾天時間，系統才會反映變更內容。如果您有使用其他廣告聯播網，請記得將該聯播網加入 ads.txt 檔案。如想進一步瞭解詳情，請參閱 ads.txt 指南。\n解法 基本上就是沒有把 Google AdSense 提供的 ads.txt 設定好\n建議可以參考我的另外一篇文章，檢查一下設定過程中是否哪一步驟有出錯：\nhttps://wongwongnotes.com/posts/os-misc/web-platforms/google-adsense/google-adsense-set-ads-txt/\nReference https://web.archive.org/web/20230505083624/https://support.google.com/adsense/answer/7532444?hl=zh-Hant https://breaktime.zendesk.com/hc/zh-tw/articles/360008714754-%E4%BB%80%E9%BA%BC%E6%98%AFads-txt-%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A2%BA%E6%94%BE%E7%BD%AEads-txt%E4%BE%86%E8%AE%93%E5%BB%A3%E5%91%8A%E6%94%B6%E7%9B%8A%E6%8F%90%E5%8D%87- ","date":"2021-03-27T01:28:24+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/google-adsense-fix-ads-txt/","tags":["WordPress"],"title":"【Google AdSense】問題解決: 「收益警示：您必須修正某些 ads.txt 檔案問題，以免造成嚴重的收益損失。」"},{"categories":["912 - Google AdSense"],"content":"前言 我們在 申請 Google AdSense 的過程中，\n有一個步驟 Google AdSense 會要求我們上傳 ads.txt 這個檔案，\n如果是初次建立自己網站的朋友們可能會不知道該如何設定(像我就找了一下)，\n這篇文分享我設定過程中的筆記。\n設定過程 下載 ads.txt 之前沒有截到圖片，不過當「收益警示：您必須修正某些 ads.txt 檔案問題，以免造成嚴重的收益損失。」\n沿著這句話應該能找到下載 ads.txt 的地方。\n有時候可能還會遇到下面這段內容：\n為 1 個網站建立 ads.txt 檔案\n為避免嚴重影響收益，請下載 ads.txt 檔案並上傳至每個網站的根目錄層級網域：\n可能需要等候幾天時間，系統才會反映變更內容。如果您有使用其他廣告聯播網，請記得將該聯播網加入 ads.txt 檔案。如想進一步瞭解詳情，請參閱 ads.txt 指南。\n找到網站主機商提供的「檔案管理員」 這部份一開始會很因人而異，每個人網站的主機商都不同，\n但基本上主機商都會提供類似檔案管理員的功能，\n我們必須要找到這個東西。\n接下來我以「sugarhost」的主機商做為示範。\n在後台找到「檔案管理員」：\n上傳 ads.txt，將 ads.txt 放到正確的位置 找到「home」底下的「public_html」，按上面的上傳，把 ads.txt 傳到這個資料夾裡面。\n測試是否成功 上傳後，你可以在你的網址後加上 ads.txt 看看找不到得到東西 (沒有會顯示網頁錯誤)，\n例如：https://example.com/ads.txt 如果有，會顯示一行字，其中的 pub-0000000000000000 是指你的發布商 ID。\n範例：google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0 如果檢查都沒問題，這問題基本上就解決了，\n有時候如果出現警告還沒消失，在正確操作後等個幾天就會自動消失了。\nReference https://web.archive.org/web/20230505083624/https://support.google.com/adsense/answer/7532444?hl=zh-Hant https://breaktime.zendesk.com/hc/zh-tw/articles/360008714754-%E4%BB%80%E9%BA%BC%E6%98%AFads-txt-%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A2%BA%E6%94%BE%E7%BD%AEads-txt%E4%BE%86%E8%AE%93%E5%BB%A3%E5%91%8A%E6%94%B6%E7%9B%8A%E6%8F%90%E5%8D%87- ","date":"2021-03-25T01:19:39+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/ads.png","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/google-adsense/google-adsense-set-ads-txt/","tags":[],"title":"【Google AdSense #1】申請 Google AdSense 如何設置 ads.txt (內附圖文說明)"},{"categories":["131 - Python OpenCV"],"content":"前言 rtsp 為網路攝影機位置，\n我們可以透過讀取，並即時顯示畫面，\n做出一個類似播放器的效果。\n在下面的範例程式碼中，\n我們也提供了 平均 fps 計算 (全部 frame)、瞬時 fps 計算 (單一 frame)\nsample code import time import cv2 video_path = \u0026#34;rtsp://user:password@192.168.2.0/h264\u0026#34; SHOW_LIVEVIEW = True SHOW_INFO_IN_TERMINAL = True SHOW_INFO_IN_LIVEVIEW = True def write_text(img, text): # text = \u0026#34;FONT_HERSHEY_DUPLEX\u0026#34; position = (20, 100) font = cv2.FONT_HERSHEY_SIMPLEX size = 1 color = (0, 255, 255) thickness = 2 lineType = cv2.LINE_AA cv2.putText(img, text, position, font, size, color, thickness, lineType) return img if __name__ == \u0026#39;__main__\u0026#39;: vid = cv2.VideoCapture(video_path) Ts = time.time() cnt = 0 before_time = 0 while(True): img = vid.read()[1] shape = img.shape shape = (int(shape[1]), int(shape[0])) cnt = cnt + 1 time_pass = time.time()-Ts info_text = \u0026#34;Time: {:.2f}, Frame count: {:08d}, fps: {:.2f}, fps_one: {:.2f}\u0026#34;.format(time_pass, cnt, cnt/time_pass, 1/(time_pass-before_time)) if SHOW_INFO_IN_LIVEVIEW: img = write_text(img, info_text) if SHOW_INFO_IN_TERMINAL: print(f\u0026#34; {info_text}\u0026#34;, end = \u0026#34;\u0026#34;) before_time = time.time()-Ts img = cv2.resize(img, shape, interpolation=cv2.INTER_CUBIC) if SHOW_LIVEVIEW: img = cv2.resize(img, shape, interpolation=cv2.INTER_CUBIC) cv2.namedWindow(\u0026#39;rtsp\u0026#39;, cv2.WINDOW_NORMAL) cv2.resizeWindow(\u0026#39;rtsp\u0026#39;, shape[0], shape[1]) cv2.imshow(\u0026#39;rtsp\u0026#39;, img) cv2.waitKey(1) 說明 我自己設計的可控制部份 在程式碼一開始部份：\nvideo_path：設定 rtsp 位置，user, password 請自行設置，另外 rtsp 路徑請自行確認 SHOW_LIVEVIEW：是否在螢幕上顯示 rtsp 的即時畫面 (透過 imshow) SHOW_INFO_IN_TERMINAL：是否顯示資訊在終端機 (terminal) 上 SHOW_INFO_IN_LIVEVIEW：是否顯示資訊在螢幕畫面 (需搭配 SHOW_LIVEVIEW 使用) 上 OpenCV 的可控制部份 rtsp 讀取邏輯 vid = cv2.VideoCapture(video_path) while(True): img = vid.read()[1] 其實沒有想像中那麼難，就單純從路徑直接 vid.read()[1]，就可以直接拿一張圖片。\n註：你可能會好奇，那 vid.read()[0] 是什麼？ vid.read() 會回傳兩個值，第一個值 (也就是 vid.read()[0] ) 是 True/False，代表有沒有取得畫面。\n第二個值就是我們要的圖片囉！ 所以我們才用 vid.read()[1] 直接拿取圖片，\n不過我們也可以利用第一個值的概念，幫助我們判斷無法讀取 rtsp 的情況。\n關於在螢幕上的 rtsp 即時畫面顯示，可以在這裡設定 cv2.namedWindow(\u0026#39;rtsp\u0026#39;, cv2.WINDOW_NORMAL) cv2.resizeWindow(\u0026#39;rtsp\u0026#39;, shape[0], shape[1]) cv2.imshow(\u0026#39;rtsp\u0026#39;, img) cv2.waitKey(1) 這邊是我自製的顯示 rtsp 畫面，詳細代表的設定意義如下：\ncv2.namedWindow：設定畫面名稱 cv2.resizeWindow：修改畫面大小 cv2.imshow：顯示畫面 cv2.waitKey：保持畫面顯示 ","date":"2021-03-23T11:19:40+08:00","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/python-opencv-rtsp/","tags":["Python","Python OpenCV"],"title":"【OpenCV】用 OpenCV 開啟 rtsp 即時影像串流 - python OpenCV rtsp"},{"categories":["921 - iphone / iOS"],"content":"前言 這個問題是我在設定「iphone 充電時自動朗讀電量」的時候碰到的問題，\n簡單來說就是「捷徑的自動化功能」 siri 朗讀的聲音非常小聲，怎麼調整都沒有用。\n本文會提供解決方法，至於「iphone 充電時自動朗讀電量」的相關文章，\n可以幾位知名 IT 部落客 的文，內文都相當豐富，這邊就不多說明。\n關於「iphone 充電時自動朗讀電量」的相關文章 可以參考以下幾位知名 IT 部落客 的文，內文都相當豐富，這邊就不多說明了XD：\niPhone充電時自動唸出電量提示聲 ，iOS用戶必學招式 iOS 14 充電 捷徑 小技巧，充電讀電量、100%提醒與畫面變鬧鐘 充到幾 % 了？ iOS 捷徑充電提示音 快速設定好方便 iPhone 充電聲音教學：連接充電線時自動唸出目前電量 (iOS14) iOS自動化應用：iPhone 充電時自動唸出「現在剩餘電量」 iOS 14 充電 捷徑 小技巧，充電讀電量、100%提醒與畫面變鬧鐘 最終解決方法 其實這個解決方法我也試了很久，\n最後發現可以在「 iphone 朗讀的時候」，直接「按下音量鍵調整音量」\n就可以解決囉！\n雖然聽起來很簡單，但朗讀的文字那麼短，誰知道可以在那麼短的時間內調整音量XDD\n另外，不確定是不是 iphone 在設定各個程式時把每一個音量進行「獨立設計」導致，\n所以下面那些「失敗的解決辦法」才都沒有辦法使用。\n那些失敗的解決辦法 - 1 直接「將音量調整到最大」後，即可解決\n結果沒效，只有執行其他 app 的時候有用\n那些失敗的解決辦法 - 2 在自動化的過程中，設定「調整音量」\n如圖所示：\n結果沒效，有變更到音量，但一樣「沒有影響」到朗讀的聲音\n那些失敗的解決辦法 - 3 在自動化的過程中，設定「先將播放位置指定為 iphone，再調整音量」\n來自：https://discussionschinese.apple.com/thread/251955484 提供的方法 如圖所示：\n結果沒效，一樣有變更到音量，但一樣「沒有影響」到朗讀的聲音\nReference https://discussionschinese.apple.com/thread/251955484 https://mrmad.com.tw/battery-automatically-sounds-iphone-charging-shortcuts https://walker-a.com/archives/6561 https://agirls.aotter.net/post/58302 https://www.tech-girlz.com/2020/12/iphone-charging-auto.html https://applealmond.com/posts/76842 https://www.kocpc.com.tw/archives/368557 ","date":"2021-03-20T17:59:16+08:00","image":"https://wongwongnotes.com/images/restored/2021/04/img_8397-473x1024.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/iphone-shortcuts-sound/","tags":["iOS"],"title":"【iOS】問題解決：iphone, ipad 捷徑、自動化，siri 朗讀聲音很小的解決辦法"},{"categories":["115 - Python 檔案處理"],"content":"前言 我們在進行 python 檔案處理時，有時會需要移除指定路徑的資料夾，\n本文提供 移除資料夾的模版可供直接套用。\n範例與模板 import shutil path = \u0026#39;/folder_name\u0026#39; shutil.rmtree(path) 其中：\npath： 想要移除的資料夾路徑 注意事項 -「當目的路徑不存在，會有error！」 （見下圖）\n第一行指令，跳出了找不到該路徑的 error， 而實際上對應路徑真的不存在 第二行指令有順利執行，因為對應的路徑確實存在， 而將整個路徑全部移除掉了。 注意事項 - 應對處理方式 我們參考這篇文章提供的方法，預先檢查資料夾是否存在。\n並且搭配 if 的方式預先進行檢查，如果存在就不做刪除資料夾的動作。\nhttps://wongwongnotes.com/posts/python/core-syntax/file-processing/python-isfile-isdir/\nimport shutil path = \u0026#39;/folder_name\u0026#39; if os.path.isdir(path): shutil.rmtree(path) 如此一來，在刪除資料夾之前，\n我們都能先透過「if os.path.isdir(path)」這行先確認資料夾是否存在。\n避免程式出現 error 而中止。\n不走 python 流派也是有其他作法 可以參考這篇：\nhttps://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-ubuntu-terminal-basic/\n運用 linux 指令搭配 python 的 os.system() 也能達到一樣的效果哦！\nRefernce How do I remove/delete a folder that is not empty? ","date":"2021-03-18T20:16:14+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Screenshot-from-2021-03-18-20-09-57.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/python-shutil-rmtree/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #7】python 移除資料夾範例 rmdir shutil.rmtree()"},{"categories":["115 - Python 檔案處理"],"content":"前言 我們在進行 python 檔案處理時，會經常需要建立指定路徑的資料夾，\n本文提供 建立資料夾的模版可供直接套用。\n範例與模板 import os os.makedirs(path, mode=0o777) 其中：\npath： 想要建立的資料夾路徑 mode=0o777：建立資料夾的權限（像這裡就是權限777） 補充說明 「os.makedirs」建立的資料夾路徑，是必定建立的！\n(也就是說，如果沿著路徑上的資料夾不存在，都會建立)\nimport os path = \u0026#34;./a/b/c\u0026#34; os.makedirs(path, mode=0o777) 我們可以看到路徑會全部建立出來！\n注意事項 -「當目的路徑已經存在，會有error！」 我們將上面的程式碼重複執行兩次，\n我們會發現第二次的時候出現了 Error，\n表示我們不能在已經存在的目的路徑，再執行一次。（見下圖）\n注意事項 - 應對處理方式 我們參考這篇文章提供的方法，預先檢查資料夾是否存在。\n並且搭配 if 的方式預先進行檢查，如果存在就不做創建資料夾的動作。\nhttps://wongwongnotes.com/posts/python/core-syntax/file-processing/python-isfile-isdir/\nimport os path = \u0026#34;./a/b/c\u0026#34; if not os.path.isdir(path): os.makedirs(path, mode=0o777) 如此一來，在創建資料夾之前，\n我們都能先透過「if not os.path.isdir(path)」這行先確認資料夾是否存在。\n避免程式出現 error 而中止。\n注意事項 - 小實驗 我們可以先移除最終的目的資料夾「 c 」，在執行一次看看，\n我們發現程式就能夠正常執行，也就是說我們只需要保證「最終資料夾不存在」即可。\n不走 python 流派也是有其他作法 可以參考這篇：\nhttps://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-ubuntu-mkdir/\n運用 linux 指令搭配 python 的 os.system() 也能達到一樣的效果哦！\nRefernce https://www.runoob.com/python/os-makedirs.html\nhttps://wongwongnotes.com/posts/python/core-syntax/file-processing/python-isfile-isdir/\n","date":"2021-03-18T17:08:18+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Screenshot-from-2021-03-18-16-57-47.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/python-os-makedirs/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #6】python 建立資料夾範例 mkdir os.makedirs()"},{"categories":["933 - 股票學習筆記"],"content":"前言 這是我今天購買股票發生的問題XD，\n本人也算是股票的新手，所以這篇其實是在講解股市新手應該需要知道的基本常識。\n事情大概是這樣的 我原本今天想要趁低點買一張A股票，但是我戶頭裡面並沒有足夠的錢支付。 於是我就想等B股票的高點再賣出，就能湊到足夠的金額了。 後來又因為今天幾乎B股票都在跌，想期待看看B股票明天有沒有機會漲。 (這裡其實就有錯誤示範1：我們根本無法預測明天會漲會跌，這個假設其實本身跟賭博沒什麼差別。)\n原本想是想等明天，畢竟一買一賣應該錢就足夠了， 突然想起有一個規則是交割日是 T+2 日的什麼規定， 於是想了一下，發現可能有點不太妙， 畢竟我是先買了A股票，所以我應該就要先有足夠能力支付A股票的完整金額嗎? 這就是我們這篇文章主要討論的問題，什麼時候我們需要付完整金額? 什麼時候只需要補差額?\n因為很怕「違約交割」造成的信用問題 (聽起來就很麻煩) 緊張了一下就打電話去問，現在觀念變得超清楚了。 簡單的幾個問題，你都能正確回答嗎XD? 問題一：T日 買了A股票，T日 賣了A股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - A賣 的金額 問題二：T日 買了A股票，T+1日 賣了A股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - A賣 的金額 問題三：T日 買了A股票，T日 賣了B股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - B賣 的金額 問題四：T日 買了A股票，T+1日 賣了B股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - B賣 的金額 答案與說明 今天因為不懂，後來特別打電話與問朋友才學習了不少東西，\n簡單來說把握一個大原則就是：\n當日買賣一起結算，不同股票也是一起結算 (這裡不講不同證券行的問題)\n所以上面問題的答案就是：\n問題一：T日 買了A股票，T日 賣了A股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - A賣 的金額 問題二：T日 買了A股票，T+1日 賣了A股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - A賣 的金額 問題三：T日 買了A股票，T日 賣了B股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - B賣 的金額 問題四：T日 買了A股票，T+1日 賣了B股票，T+2日 至少要準備多少金額呢? (A) 購買A股票的金額(+手續費等等) (B) 補買賣差額即可，即 A買 - B賣 的金額 就是這麼簡單！\n結語 基本上只要是「同一天」進行的交易，金額都可以互相抵銷，\n(這樣講比較簡單，當然實際上還有一些手續費與稅金之類的東西，不是只有表面上的金額在互消)\n如果「隔了一天」，就要準備好足夠的金額去支付「今天」買的股票的實際金額囉！\n也就是說，千萬不要手殘買了一些你付不起的股票啊！\n只要買的當天沒賣其他股票，等到 T+2 日你就真的要準備足夠你買的金額了!!!\n補充說明：「當沖」是什麼? 在這個例子中我能使用嗎? 答案是：不行\n先理解一個觀念，「當沖」是必須要經過申請的，沒有申請的話基本上不用想這條路。\n為什麼會提到「當沖」? 如果股市新手碰到這個問題上網查資料，很有可能會查到一個答案，\n「買了戶頭付不起金額的股票，那就當日沖掉就好了」\n好像聽起來是可行的方法? 那我們可能要先理解一下「當沖」的概念\n什麼是「當沖」? 都說了這麼多現在才要解釋「當沖」XD\n基本上我們可以簡單理解為「同一張股票當日的買進賣出」\n也就是說，上面那句話的意思，就是希望我們在同一天把不小心買進來的那張股票直接賣掉\n那實際上我們真的能用「當沖」來解決問題嗎? - 首先你要有申請過，可以進行「當沖」交易，光是這一點就不行了。 - 在購買的時候，就應該以「當沖」的方式購買，如果不是買「當沖」的股票，基本上是不能當日賣掉的。 你可以理解為：你買的股票，這張股票實際上在 T+2 日才會真的歸你所有，而且必須同時向你收錢\n那此時的你，「並沒有這張股票的所有權」，所以當然自己沒有的東西就不能拿來交易啦！\nReference \u0026amp; 感謝 詳細解說的客服人員、眾多朋友的幫忙！！！！！\n","date":"2021-03-17T20:36:19+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/stock-market/stock-analysis/","tags":["股票學習筆記"],"title":"【股票學習筆記】交割日金額問題 - 到底該準備原本購買股票的價格還是只需要補差額? T, T+1, T+2 日與同股票不同股票的差別 (避免違約交割) T日買進，T+1日賣出，股票交割時間，賣股票入帳日，當沖觀念補充"},{"categories":["192 - Python 測試程式"],"content":"前言 這篇文章基本上內容會與我的另外一篇差不多，可以參考：\nhttps://wongwongnotes.com/posts/python/concepts/testing/python-doctest/\n而這篇文章會更專注在 leetcode 的使用部分。(也會提到前一篇文章沒提到的在 class 中使用的部分)\n一樣的前言： 在 python 裡面，我們常需要在程式碼寫完時，測試程式碼功能是否正常，\n「doctest」 這個套件雖然冷門，但使用時非常方便，這裡推薦給大家。\n你可能會問，這好處在哪? 好處一 - 可以反覆測試不用靠手重新輸入 這應該是最重要的好處了吧，每次要測試只需要多下一個「-v」就行。\n看看 leetcode 的那些測資，還有比只需要多下一個「-v」更方便的事情嗎?\n好處二 - testcase 直接是註解，不用改code直接貼(欸? 我自己是覺得，他剛好把我們「不要的測試部分」都「用註解包起來了」，\n整段 code 可以直接貼上去就能測試了，對懶人來說太方便了啦！\n(當然，一個乾淨的code，最好不要有這種莫名其妙的註解啦\u0026hellip;\u0026hellip;)\n安裝 使用時我們需要先安裝「doctest」這個套件。\n不過通常應該是都已經內建了。\n我們可以用 import doctest 來測試看看有沒有 error。\nimport doctest 使用 在前面的文章中，我們提到了 doctest 在 function 中的基本用法。\n這篇我們專門來講 doctest 在 class 中的用法。\n範例一 - class 基本用法 class Test: \u0026#34;\u0026#34;\u0026#34; \u0026gt;\u0026gt;\u0026gt; a=Test(5) \u0026gt;\u0026gt;\u0026gt; a.multiply_by_2() 10 \u0026#34;\u0026#34;\u0026#34; def __init__(self, number): self._number=number def multiply_by_2(self): return self._number*2 if __name__ == \u0026#34;__main__\u0026#34;: import doctest doctest.testmod() 在這個例子裡面，當我們執行 python test.py -v 會自動開始檢查： 當 a=Test(5) 時, a.multiply_by_2() 的值是否等於 10\n範例二 - leetcode 中的 class 實戰 我們以 leetcode 的第一題為例：\nhttps://leetcode.com/problems/two-sum/\n如果想知道答案與詳解，可直接參考我的這篇文章，這邊只講用法：\nhttps://wongwongnotes.com/posts/algorithm-practice/data-structures/hash-table/leetcode-python-1/\n# # @lc app=leetcode id=1 lang=python3 # # [1] Two Sum # nums : List[int] # target: int # List[int] # @lc code=start class Solution: \u0026#34;\u0026#34;\u0026#34; \u0026gt;\u0026gt;\u0026gt; a=Solution() \u0026gt;\u0026gt;\u0026gt; a.twoSum([2, 7, 11, 15], 9) [0, 1] \u0026#34;\u0026#34;\u0026#34; def twoSum(self, nums, target): # 2 7 11 15 my_dict = {} for idx, ele in enumerate(nums): rest = target - ele if rest in my_dict: return [my_dict[rest], idx] else: my_dict[ele] = idx # record if __name__ == \u0026#34;__main__\u0026#34;: import doctest doctest.testmod() # @lc code=end 在這個例子裡面，當我們執行 python test.py -v 會自動開始檢查： 當 a=Solution() 時，輸入 a.twoSum([2, 7, 11, 15], 9)，能不能正確回傳 [0, 1] 。\n範例三 - 其實只是範例二的再更簡化 我們把範例二的 testcase 再改寫的簡單一點：\n# # @lc app=leetcode id=1 lang=python3 # # [1] Two Sum # nums : List[int] # target: int # List[int] # @lc code=start class Solution: \u0026#34;\u0026#34;\u0026#34; \u0026gt;\u0026gt;\u0026gt; Solution().twoSum([2, 7, 11, 15], 9) [0, 1] \u0026#34;\u0026#34;\u0026#34; def twoSum(self, nums, target): # 2 7 11 15 my_dict = {} for idx, ele in enumerate(nums): rest = target - ele if rest in my_dict: return [my_dict[rest], idx] else: my_dict[ele] = idx # record if __name__ == \u0026#34;__main__\u0026#34;: import doctest doctest.testmod() # @lc code=end 在這個例子裡面，當我們執行 python test.py -v 會自動開始檢查： 當輸入 Solution().twoSum([2, 7, 11, 15], 9) 時，能不能正確回傳 [0, 1] 。\n跟範例二相比，連暫時的 a 都不用宣告了！ 越來越乾淨俐落了呢！\n此外，在範例三中，我更喜歡的是： 一行 input 與 一行 output，\n正完美符合 leetcode testcase 的撰寫方式！\nReference https://openhome.cc/Gossip/CodeData/PythonTutorial/AssertDocTest.html https://ithelp.ithome.com.tw/articles/10244417 http://technology-sea.blogspot.com/2012/02/python-doctest-test-case.html https://stackoverflow.com/questions/2708178/python-using-doctests-for-classes ","date":"2021-03-13T01:42:25+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/testing/leetcode-python-testcase/","tags":["Python"],"title":"【Leetcode】python - 利用 doctest 測試 leetcode python testcase 的優雅寫法 (doctest in class，搭配 class 的用法)"},{"categories":["192 - Python 測試程式"],"content":"前言 在 python 裡面，我們常需要在程式碼寫完時，測試程式碼功能是否正常，\n「doctest」 這個套件雖然冷門，但使用時非常方便，這裡推薦給大家。\n例如刷 leetcode 時，測試使用也非常的方便！\n安裝 使用時我們需要先安裝「doctest」這個套件。\n不過通常應該是都已經內建了。\n我們可以用 import doctest 來測試看看有沒有 error。\nimport doctest 基本使用 我們先來看一個簡單的例子，\n假設我們想要測試「我們製作的加法」功能是否正常。\n程式碼範例如下：\nimport doctest def my_add(a, b): \u0026#39;\u0026#39;\u0026#39; \u0026gt;\u0026gt;\u0026gt; my_add(1, 2) 3 \u0026#39;\u0026#39;\u0026#39; return a+b if __name__ == \u0026#34;__main__\u0026#34;: doctest.testmod() 我們可以注意到有註解的部份，就是我們要測試的內容。 # 我們測試當 my_add 這個函數放入 (1, 2) 會發生什麼事，「\u0026gt;\u0026gt;\u0026gt;」可以視為 input 的概念 \u0026gt;\u0026gt;\u0026gt; my_add(1, 2) # 理論上，這個 function 應該要回傳 3 給我 3 再提醒一次，我們需要用「\u0026rsquo;\u0026rsquo;\u0026rsquo; (3個單引號) 或 \u0026quot;\u0026quot;\u0026quot; (3個雙引號)」都可以，\n在測試範圍前後各一個，來把這一段測試用的程式碼註解掉 (正常執行不會跑)。\n執行時，我們可以分成「一般正常執行」與「測試執行」 2023/8/10，感謝網友 Yuki 的提醒！\n原本我學習到的內容有誤，以下內容已經校正完成\n一般測試，只顯示錯誤 python test.py 只有出現錯誤時才會跳出訊息。\n顯示所有測試，不論對錯 python test.py -v v 這裡指的是 verbose，可以簡單理解為不論對錯都顯示。\n結果 一般執行，只顯示報錯 我們可以看到，正常執行 (沒有下 -v ) 的時候\n程式沒有反應，「因為全部內容都是正確的」\n正確情況！不論結果對錯，一律顯示 (加 -v) 在測試執行 (有下 -v ) 的時候，跑出了一些內容，\ndoctest 幫我們測試了 「my_add(1, 2)」 的這個 function，\n我們「設定」他應該要回傳給我們 「3」。\n而實際上也真的回傳給我們 「3」。\n所以回傳 ok!\n錯誤情況！不論結果對錯，一律顯示 (加 -v) 這邊示範一個錯誤情況，可以看以下範例：\n我設定我們的函數應該要回傳給我們 「1」。\nimport doctest def my_add(a, b): \u0026#39;\u0026#39;\u0026#39; \u0026gt;\u0026gt;\u0026gt; my_add(1, 2) 1 \u0026#39;\u0026#39;\u0026#39; return a+b if __name__ == \u0026#34;__main__\u0026#34;: doctest.testmod() 結果 看到真的出現錯誤了！\nExpected: (我們期待的結果) 1 Got: (實際上得到的結果) 3 當然就 failure 啦！\n補充：適用於 leetcode 的 doctest in class，doctest 搭配 class 的用法 可以參考我的另外一篇文章，裡面有詳細介紹 doctest 在 class 中的用法，\n學起來在 leetcode 撰寫 testcase 非常的實用！\nhttps://wongwongnotes.com/posts/python/concepts/testing/leetcode-python-testcase/\nReference https://openhome.cc/Gossip/CodeData/PythonTutorial/AssertDocTest.html https://ithelp.ithome.com.tw/articles/10244417 http://technology-sea.blogspot.com/2012/02/python-doctest-test-case.html https://stackoverflow.com/questions/2708178/python-using-doctests-for-classes ","date":"2021-03-11T16:31:26+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Screenshot-from-2021-03-11-16-19-34.png","permalink":"https://wongwongnotes.com/posts/python/concepts/testing/python-doctest/","tags":["Python"],"title":"【Python 測試程式 #1】利用 doctest 測試 python testcase 的優雅寫法，適用於 leetcode (doctest in function，搭配 function 的用法)"},{"categories":["414 - Git 檔案相關"],"content":"前言 git 作為版本控制使用非常的方便，\n不過有時候我們在上傳的時候會有一些不想上傳的檔案 (機密檔案、密碼、單純不想上傳的檔案)\n這時設定 git ignore 就會是非常好用的功能！\n設定 git ignore 我們就再也不用煩惱我們指定的檔案會被加入 git 裡面囉！\ngit ignore 設定 產生 git ignore 設定檔 在對應的 git init 資料夾新增一個 .gitignore 的檔案\n註：通常「.」開頭的檔案，在系統中預設都是「隱藏的」\ntouch .gitignore 結果：就會產生 .gitignore 的一份檔案囉！ 不要特定的檔案 我們可以透過寫下指定的檔名，表示不要這個檔案\n(在 .gitignore 的檔案裡面) dontwantthisfile.txt 結果：「dontwantthisfile.txt」這個檔案就會被我們排除囉！ 我們也可以在 .gitignore 的檔案裡面這樣寫，表示我們不要所有的「.txt」檔案：\n*.txt 結果：所有的「.txt」檔案就會被我們排除囉！ 不要特定的資料夾 我們可以透過寫下指定的資料夾路徑，表示不要這個資料夾：\ndontwantthisfolder/ 結果：所有「dontwantthisfolder」資料夾裡面的東西，就會被我們排除囉！ Reference https://gitbook.tw/chapters/using-git/ignore.html ","date":"2021-03-10T16:52:56+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-ignore/git-ignore/","tags":["Git","初學者","檔案相關"],"title":"【Git 檔案相關 #1】git ignore 設定方法筆記 — 在 git add 時排除特定檔案"},{"categories":["310 - Linux 基礎指令"],"content":"前言 alias 就是 縮寫的意思，\n透過設定好的縮寫，可以大幅加快我們使用 terminal 開發的效率\n一些可能常見的設定 基本上知道用法後，這邊可以自由發揮的空間就非常大，\n可以先從日常使用中體驗不方便的部分，\n再自行增加自己覺得好用的 alias 哦！\n以下是我自己常用的： alias ls=\u0026#39;ls --color=auto\u0026#39; alias ll=\u0026#39;ls -al\u0026#39; alias grep=\u0026#39;grep --color=auto\u0026#39; alias f=\u0026#34;find ~+ . -name\u0026#34; alias ..=\u0026#39;cd ..\u0026#39; 列出所有現有的 alias alias 查詢 alias 對應的內容 alias \u0026lt;cmd\u0026gt; 取消 alias unalias 個人關鍵字 how to check alias in linux Reference 30 Handy Bash Shell Aliases For Linux / Unix / MacOS ","date":"2021-03-10T11:09:32+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-alias/","tags":["Linux","基礎指令"],"title":"【Linux 基礎指令 #1】alias - 替指令取個別名吧！使用範例與如何查詢 alias 原來的內容"},{"categories":["412 - Git 本地指令"],"content":"前言 git checkout branch 的切換是身為一個 git 常用者非常常用的功能，\n但是美中不足的就是，每次輸入 git checkout branch 時，\n都需要把「完整的 branch 名稱」打出來，簡短的 branch 名稱還好，\n像公司那種等級的版控，branch 名稱通常都有被規劃，名稱也長，\n通常我都會用複製的就不用慢慢打，但是這如果在沒有滑鼠可以複製時很更痛苦啊！！！\n(最新方法 2021/10/20) - 安裝 zim (使用 zsh shell) zim 真的太猛\u0026hellip;，使用過真的回不去\nzim 全名為 zsh improved framework，是 zsh shell 的一個強化框架XD，\n總之就是很多內建的東西只要安裝 zim 這一包就全部幫你完成，\n包含自動完成、git 相關指令自動完成、\n超強的「自動完成選單」、有時候還會被嚇到的「正規表示式自動轉譯」，\n而且「速度還超快」，嚇死人的非常的方便。\n安裝 zim 的方法 安裝方式也很簡單，只需要以下一行，就可以體驗各種強大的功能：\ncurl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh 小知識：zim = zsh improved\n技巧一：快速切換為上一次的 branch 「git checkout -」可以快速切換為上一次 git 所在的 branch，\n我通常在開發的時候，一次最多也是在兩個 branch 之間進行切換，\n「-」可以替代為上一次使用的 branch 對我來說已經非常夠用，而且輸入也夠快。\ngit checkout - 技巧二：git branch 的自動完成 以下方法參考自：Is there a shortcut for git branch name?\nstep 1. 自 github 下載安裝檔 wget https://raw.github.com/git/git/master/contrib/completion/git-completion.bash -O ~/.git-completion step 2. 去 shell 測定檔中 (bashrc, zshrc\u0026hellip;)，建立這個套件的捷徑 step 2-1. 修改設定檔 vim ~/.zshrc step 2-2. 設定自動完成至設定檔，使他能自動啟動 source ~/.git-completion step 3. 重新啟動 Shell 可以直接啟動一個新的，\n或者使用「source ~/.zshrc」，啟動剛剛的修改。\nsource ~/.zshrc Reference How do I quickly checkout a branch with a large name without using the mouse? Is there a shortcut for git branch name? 打造屬於你自己的極速 Shell「iTerm + zsh + zim + powerlevel10k」 我的 Shell 環境設定：zsh + zim + powerlevel10k + z ","date":"2021-03-09T16:51:24+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-local/git-branch-fast/","tags":["Git","sshfs","本地指令"],"title":"【Git 本地指令 #2】git checkout - 快速切換為上一次使用的 branch / git branch 自動完成 (git checkout branch without typing full name)"},{"categories":["310 - Linux 基礎指令"],"content":"前言 這篇是給 linux ubuntu mac 終端機 (terminal) 新手使用者的教學總整理，\n(最最最基本那種，一定要知道的)\n基本上只會寫到「重點中的重點」，如果需要各個指令詳細的內容，\n請再去尋找各篇文章閱讀XD\n持續更新：個人認為常用，且新手必須知道的\n先備 - 基礎路徑知識，簡易理解「相對路徑」與「絕對路徑」(重要) 「相對路徑」與「絕對路徑」的觀念個人認為非常重要！！！\n如果這個概念沒搞清楚，輕則找不到檔案而已，似乎還好。\n但重則可能會導致刪除整台電腦所有東西！！！這樣還不重要嗎！\n以下內容使用此範例：假設有一張圖片的路徑在：「/home/ubuntu/Desktop/test_result/out.jpg」\n相對路徑 「.」表示目前我們所在的路徑，以上面的例子，\n假設我們目前所在的路徑為桌面(/home/ubuntu/Desktop，可以用「pwd」查看)\n以上面為例，圖片路徑就是 「./test_result/out.jpg」\n我們從(桌面)這個路徑底下找 test_result 這個資料夾中的 out.jpg。\n就會是我們儲存圖片的地方。\n絕對路徑 絕對路徑就是檔案完整的路徑，也就是「/home/ubuntu/Desktop/test_result/out.jpg」\n這種寫法非常的明確，但相對的缺點就是彈性非常的差，\n如果今天將我們的 code 換了一台電腦執行，\n甚至只把資料夾移動到另一個地方，絕對路徑馬上就找不到我們要的檔案了。\n所以建議大家還是多使用「相對路徑」哦~\n雖然我剛學的時候也覺得「絕對路徑」真的很明確很棒XDDD\n題外話，但很重要！ 在網路笑話(?)當中常常有人提到 rm -rf / 這個指令，\n但如果當真實發生在你身上時可能就笑不出來了XDDD，\n(可能整個公司的資料都完蛋)\n這個指令就是「強制移除(rm -rf)」從「根目錄(\u0026quot;/\u0026quot;)」底下的所有檔案，\n等於把整台電腦都刪了！而且通常「難以救回」\n個人認為與其擔心發生這樣的事情，不如搞清楚這指令是怎麼回事，\n才是真正有效預防的方法，而不是因噎廢食而不敢使用 rm -rf\n檔案路徑相關總整理 指令 功能簡述 補充說明 cd 切換資料夾 記法：change directory ls 查看資料夾內檔案(只有檔名) ll 或 ls -l 查看資料夾內檔案(完整資訊) pwd 查看目前路徑 檔案處理相關總整理 指令 功能簡述 補充說明 mv 移動檔案 mv a b 將 a 移動到 b / 記法：move mv 重新命名 mv a b 將 a 重新命名為 b / 記法：move cp 複製檔案 cp a b 將 a 複製到 b / 記法：copy rm 移除檔案(請謹慎使用) rm a 移除 a 檔案 / 記法：remove rm -f 「強制」移除檔案(請謹慎使用) rm -f a 「強制」移除 a 檔案 / 記法：remove, -f = force rm -r 移除資料夾(請謹慎使用) rm -r a 移除 a 資料夾 / 記法：remove recursive rm -rf 「強制」移除資料夾(請謹慎使用) rm -rf a 「強制」移除 a 資料夾 / 記法：remove recursive, -f = force 原本想說只要把 含有 「-f」的指令特別說要謹慎使用就好\u0026hellip;，\n但後來想想可能大部份剛踏入這個領域的人，都還對電腦有「資源回收筒」的概念！\n注意！！！被「rm」處理掉的檔案是「沒有資原回收桶」的概念的！！！\n使用「rm」的指令時，請清楚了解自己在下什麼指令。\n使用rm移除的檔案「很難救回」，請確定自己真的有理解在下什麼指令\n(不是救不回，只是超麻煩。)\n系統運行相關總整理 指令 功能簡述 補充說明 top 查看目前系統執行中的所有程式 ps 查看目前使用者執行中的程式 ps -a 查看目前系統執行中的所有程式 記法：-a = all kill id 強制結束「編號為id」的程式 可由上方的指令查詢該程式的 id pkill name 強制結束「名字包含name」的程式 其他整理 指令 功能簡述 補充說明 clear 清空終端機 treminal 畫面 history 查看所有執行過的指令 history -c 清除所有執行過指令的歷史紀錄 記法：-a = clear reboot 或 shutdown -r now 重新開機 記法：-r = reboot shutdown now 關機 出現 permission denied 了? 解決方法 permission denied，照字面上的意思就是「執行這個指令的權限不足」，\n這時只需要在指令前加上「sudo」，並輸入密碼即可順利完成。\n通常會出現 permission denied ，表示該指令可能有風險，\n所以系統才自動先擋掉權限不足的使用者，\n加了「sudo」後，執行前可以「再確認自己下的這行指令的意思」，才更安全哦\n範例 例如前面我們所說的網路笑話(?)，如果正常執行「rm -rf /」，\n照理來說，系統應該會擋掉這個指令，並說 permission denied。\n畢竟你都要強制砍掉東西，要你確認再確認!!!\n如果想要順利執行，就需要輸入「sudo rm -rf /」，\n然後悲劇就會發生了\u0026hellip; 所以小孩子千萬不要學！！！\n(除了破壞別人電腦外，我還真想不到這指令有什麼用途\u0026hellip;)\n但是 rm -rf 在面對砍難以砍掉的檔案時，是非常有用的!!! (所以一定要注意路徑!!!) 對「路徑」觀念還不清楚的，請再去看上面的《基礎路徑知識，簡易理解「相對路徑」與「絕對路徑」》 ","date":"2021-03-09T15:37:25+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-ubuntu-terminal-basic/","tags":["Bash","Linux","Ubuntu","基礎指令"],"title":"【Linux 基礎指令 #0】linux/ubuntu/mac 基礎終端機 (terminal) 指令 \u0026 基礎知識總整理，初學者/新手 必須知道的基礎指令 \u0026 基礎知識大全（持續更新）"},{"categories":["914 - Wordpress"],"content":"前言 這篇是寫給我自己的筆記，\n平常我使用 markdown 撰寫文章，\n結果有時候會碰到再次編輯時，文章全部變成 html 的格式。\n非常困擾啊！全部都要重新再排版！\n(雖然比起文章直接消失\u0026hellip;這樣好很多了(?))\n解法 此為多個依賴 markdown 的外掛衝突而導致的，\n個人的衝突是同時使用 「Editor.md」、「Jetpack」的 markdown\n導致再次編輯時會變回 html 格式。\n關閉「Jetpack 的 markdown 功能」即可恢復正常\nReference me\n","date":"2021-03-08T23:59:59+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/wordpress-bug-markdown/","tags":["WordPress"],"title":"【Wordpress】問題解決: 使用 markdown 撰寫文章後，編輯時全部變回 html 格式 (markdown 格式跑掉)"},{"categories":["412 - Git 本地指令"],"content":"前言 我們的程式碼只要再上傳雲端前 (這邊通常指的都會是 github)，一切都還好處理，\n我們只需要在本地，把 git 的各種事情處理好，\n不碰到雲端那塊才來處理錯誤，一切都還好解決\n延續 day 1 的精神，新手的我們，盡量把所有的 git 問題在上雲端之前解掉，\n這樣可以省下非常多的麻煩與處理。\n在各種版本之間移動 git 會透過單向 Hash 的加密演算法，算出每一個 commit 的專屬 SHA-1 值。\n這個 Hash 值基本上算是我們在使用 git 中，最萬能的一個值。\n萬能的程度，我們可以說，只要有什麼版本控制問題，\n先找出你要版本的 Hash 值就對了。\ngit 裡面的時空概念，時空倒流前的準備 我們先有個概念，git 紀錄的內容就像是時間的變化，\n時間連續的變化，就代表不可以「只修改歷史上的某個時間點」，因為會造成「時空錯亂」。\n例如說，我今天喝了早餐店奶茶，才導致我肚子痛，\n我不可能只倒回「修改喝早餐店奶茶的事實」，「還保留肚子痛的這個結果」\n換成圖就是： 過去 -\u0026gt; 早餐店奶茶 -\u0026gt; 肚子痛 -\u0026gt; 現在\n我「不能」只修改過去的「某個時間點」： 過去 -\u0026gt; 「不喝」早餐店奶茶 -\u0026gt; 肚子痛 -\u0026gt; 現在\n稍微想一下，應該能懂這樣會有什麼「邏輯上的問題」吧，git 也是這個樣子的。\n也就是說，我們要時空倒流，勢必也需要放棄一些「後來努力的結果」\n直接放棄一切未來的內容，回到過去，並抹去未來的修改 (直接修改檔案的移動) 為了開始我們的時空倒流，我們有幾種方式，\n第一種當然最直接的是，放棄一切未來的「好與壞」，回到當下那個時間點\n(別想只留下好的XDD，會造成時空錯亂)\n用早餐點奶茶的例子，就是 過去 -\u0026gt; 早餐店奶茶 -\u0026gt; 肚子痛 -\u0026gt; 現在\n我們回到喝早餐店奶茶的時間，這時我們可以重新決定接下來該怎麼做。\n換成指令就是：\ngit reset --hard \u0026lt;SHA-1\u0026gt; 暫時回到回去，並且開啟新的路線，不抹去這段歷史 (像產生另外一個世界線的感覺，原支線還在) 通常做錯事情很糟糕，但也不至於到想完全「抹去這段歷史」，\n那我們可以選擇將人先回到這個過去的時間點，\n開一個新的世界線，然後開始新世界線的人生。\n這樣的好處在於，我們隨時可以回到另外一個世界線，看看另外一個世界線有沒有什麼「好東西」\n但壞處也就是，當碰到「不想被保留的修改」，例如不小心把「密碼上傳」、上傳一些「不應該上傳的檔案」，用這種方式就消不掉，他會永遠在一個「大家都看得到」的世界線上\ngit checkout \u0026lt;SHA-1\u0026gt; # 只有人回到過去某一個時間點，但這個世界線的歷史是存在的。 git branch -t \u0026lt;branch name\u0026gt; # 開一個新的 branch，表示建立一個新的世界線。 這邊提供的兩種方式我都會用，最常用的還是第一種，\n比較暴力，但通常都是處理一些預料之外的 commit，\n另外一種方式，通常是為了先保留一些開發好的東西，\n結果哪天突然發現過去某個時間 git 有問題，我就會採用第二種方法。\n如果最後不想要這個世界線(branch)了，\n我會再使用下面的指令，把不需要的 branch 刪除。\ngit branch -d \u0026lt;branch name\u0026gt; # 刪除 branch，表示刪除一個世界線。 ","date":"2021-03-08T19:12:36+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/git-remote.drawio-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-local/git-2/","tags":["Git","初學者","本地指令"],"title":"【Git 本地指令 #1】git commit - (local) 透過 git 修改過去的錯誤 commit (時空倒流)，git 錯誤處理 (建議先學的 git 基本實用功能)"},{"categories":["411 - Git 觀念整理"],"content":"什麼是 git git 是一種版本控制系統，講簡單一點，例如我們以前在交作業的時候，\n我們可能會有以下的檔案命名方式：\n我的作業-1 我的作業-2 我的作業-2-修改版 我的作業-2-修改版-最終版 我的作業-2-修改版-最終版-final 之類的\u0026hellip;\u0026hellip;有沒有感到很親切呢?\n那如果\u0026hellip;今天有一個專門幫我們命名這些檔案名稱的工具，\n而且他能幫我們命名好好的，不會像上面那樣可能自己最後都搞不清楚哪個才是最後一版，\n是不是就很棒呢! 「git」 就是專門幫助我們管理這東西的工具。\n基本使用概念 git 的使用是以一個資料夾底下的全部內容為單位的，\n在開始進行 git 前，第一步就是要在該目錄底下進行 git 的初始化 「git init」。\n初學者學習 git 碰壁的原因 與 建議學習 git 的方式 我之前學習 git 的時候，也學得非常不好，大量的指令搞不清楚，\n然後又常常出錯，那時候的挫折感導致我好一陣子都不想用 git，\n(每次 push 都時常有 error！誰要用啊！)\n直到後來遇見了某位開導我的老師，他直接點醒了我最大的盲點，\n後來我學習 git 的路上就順了非常多！\n初學者學習 git 有時候很多都太經常急著「上雲端」，但 git 絕大部分的功能都是在「本地端」完成的。\n在還不熟的時候，建議把一切東西都在「本地端」搞定後，再「一次打包上雲端」！\n在心中建立了這樣的概念後，後來我對 git 的認知整個改變，\n變成不再急著將 code 推上 github，反而好好在 local 把 git 相關的東西處理好再一次打包。\n這段話對我 git 的學習影響很深，也分享給大家！\n每天可以先養成 commit 的習慣，不用急著 push，等到有「穩定版」後再來 push 也不遲！\n(git 真的絕大部分的重點在 local 就可以處理的！ 不用急著跟雲端扯上關係)\n(如果你也是 git 學不好的人，不妨也可以試試看！)\n","date":"2021-03-07T17:30:20+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/git-remote.drawio-1.png","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-concepts/git-1/","tags":["Git","初學者"],"title":"【Git 觀念整理 #1】簡單開始學 git，認識 git 與基本觀念、心態建立，初學者學習 git 碰壁的原因 與 建議學習 git 的方式"},{"categories":["914 - Wordpress"],"content":"前言 這篇是我自己碰到問題時的情況，與整個解決問題過程的筆記。\n真的非常困擾啊!\n更何況我解這個 bug 解了快一個禮拜！不寫篇文實在是過不去啊！\n主要分成下面兩種問題：\nwordpress 引用文章/文章連結 時 縮圖壞掉/破圖/沒有顯示縮圖： 圖片說明如下：\n這篇是我自己引用自己文章時，意外發現產生的破圖情形!!\n可以很明顯看到就是圖片壞了。(但實際上是有圖的)\nwordpress 引用文章/文章連結 時 縮圖壞掉/破圖/沒有顯示縮圖： 圖片說明如下：\n可以看到 facebook 並沒有顯示這篇文章的縮圖!\n(但實際上是有圖的)\n解決過程 第一種嘗試 - 重新更新縮圖 既然是圖片的問題，想當然就直接就縮圖重新產生下手。\n因此我找了「regenerate Thumbnails」之類的外掛，\n結果：失敗了。 第二種嘗試 - 把 wordpress 外掛全關掉的嘗試是常識吧! (誤) 把 wordpress 外掛全關掉的嘗試是一定要做的!\n畢竟今天搞不好是哪個外掛不小心衝突了，誰知道呢?\n結果：失敗了QQ。 插曲01 - 好像只有 windows 有問題? 這是我偶然嘗試後的發現，\n其實這個問題只有出現在 windows上的 chrome\n因為我試了「mac」、「手機」 以下都能夠正常的顯示出圖片：\n△(mac 上顯示的示意圖，看！是我可愛的大臉(誤))\n第三種嘗試 - 直接換掉原圖 因為有插曲01的關係，\n我推測可能是因為「mac」、「手機」螢幕比我的 windows 小，\n所以我就開始嘗試替換掉原圖 (700x700)，換一張更大的原圖給他!\n主要原因是因為在第一種嘗試中，意外發現缺少了某些大小的縮圖：\n「直接換掉原圖」也是超冷門的知識，意外在這次解法中學到了!\n我找到「ENABLE MEDIA REPLACE」這個外掛!\n至於之所以會這樣做是因為我發現其實在手機上可以正常顯示的!\n我打算直接把原本的圖片換掉！給他一張更大的!\n結果：還是失敗了。 插曲02 - 好像只有 chrome 有問題? 這是我偶然嘗試後的發現，\n其實這個問題只有出現在 windows上的 chrome\n以下都能夠正常的顯示出圖片：\nmac - safari mac - chrome iphone - safari iphone - chrome windows - edge windows - firefox 看到後來我就以為是 chrome 的問題了\n第四種嘗試 - 用 chrome 無痕嘗試、關閉所有 chrome 的擴充功能 我以為是擴充功能在搞鬼，但結果不是QQ\n不論是「用 chrome 無痕嘗試、關閉所有 chrome 的擴充功能」結果都沒有效QQ\n結果：失敗了 第五種嘗試 - 能google的都找完了，能試的都試了，只好問客服了 我得到的答案包含一個關鍵字可能是「防盜鏈」設定的問題!\n(客服很貼心的還提供我可以參考的程式碼)\n只好來研究一下自己沒碰過的 php 惹 QQ\n因此我找到了這篇網站\nhttps://sofree.cc/htaccess-img/\n照著他所說的改! 感覺很有機會哦!\n這邊提示一下：要更改的路徑位置在 「./public_html/.htacess」\n結果：還是失敗了，但我認為某些人的情況應該可以成功 第六種嘗試 - 就是「防盜鏈」設定的問題! 上面直接暴力改 code 沒用，原本已經心灰意冷覺得沒救了!\n後來打開我自己的 GUI後台，\n意外發現這個!\n點進去設定一下，意外的!!!\n結果：成功! (個人猜測可能這邊的設定會把第五種嘗試的 php 洗掉，才會導致這樣。) △(看！是我可愛的大臉! 終於順利跑出來了! (誤))\n△(facebook 分享也是一併正常的囉！)\nReference https://wordpress.blog.tw/wordpress-plugin-enable-media-replace/ https://sofree.cc/htaccess-img/ me\n","date":"2021-03-07T04:47:36+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/wp_editor_md_13367f743d4a5b5293b63d15b86e68e7.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/wordpress-bug-broken-images/","tags":["WordPress"],"title":"【Wordpress】問題解決: wordpress 引用文章/文章連結 時 縮圖壞掉/破圖/沒有顯示縮圖 ，分享文章到 facebook 時 縮圖壞掉/破圖/沒有顯示縮圖 (附例圖)"},{"categories":["914 - Wordpress"],"content":"前言 這篇是因為我在調整我自己網站時，\n發現表格怎麼樣用都很醜，\n後來認真的研究該怎麼辦的研究成果。\n原來的樣子 有沒有看到表格都不見了!!!\n範例程式碼 引用自： https://web.archive.org/web/20240804021535/https://www.techiestuffs.com/styling-tables-using-css-in-wordpress-theme/\ntable { background: #fff; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; border: 1px solid #ddd; margin:auto; } table thead, table tfoot { background: #f5f5f5; } table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { font-size: 12px; line-height: 18px; text-align: left; } table thead tr th, table tfoot tr td { padding: 8px 10px 9px; font-size: 14px; font-weight: bold; color: #222; } table thead tr th:first-child, table tfoot tr td:first-child { border-left: none; } table thead tr th:last-child, table tfoot tr td:last-child { border-right: none; } table tbody tr.even, table tbody tr.alt { background: #f9f9f9; } table tbody tr:nth-child(even) { background: #f9f9f9; } table tbody tr td { color: #333; padding: 9px 10px; vertical-align: top; border: none; } 將上面這段程式碼加入在自己的外觀設定的 css 裡面 在外觀的這邊 直接貼上後發布 結果 直接給大家看吧XD (我有自己再稍微調整一下) 指令 功能簡述 補充說明 cd 切換資料夾 記法：change directory ls 查看資料夾內檔案(只有檔名) ll 或 ls -l 查看資料夾內檔案(完整資訊) pwd 查看目前路徑 想學習怎麼改，可以參考: https://www.w3schools.com/css/css_table.asp\n至於這段內容在哪XD，在我的這篇文當中：\nhttps://wongwongnotes.com/posts/linux-shell/basic-commands/linux/linux-ubuntu-terminal-basic/\nReference https://web.archive.org/web/20240804021535/https://www.techiestuffs.com/styling-tables-using-css-in-wordpress-theme/ https://www.w3schools.com/css/css_table.asp https://www.w3schools.com/css/tryit.asp?filename=trycss_table_fancy ","date":"2021-03-06T23:31:01+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/wp_editor_md_920307277b19ecbe8bedadfa979b2cb6.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/web-platforms/wordpress/wordpress-table-color-format/","tags":["WordPress"],"title":"【Wordpress #3】wordpress 調整文章內表格顏色 / 格式（markdown 也適用，內附 CSS 範例）"},{"categories":["412 - Git 本地指令"],"content":"什麼是 git git 是一種版本控制系統，講簡單一點，例如我們以前在交作業的時候，\n我們可能會有以下的檔案命名方式：\n我的作業-1 我的作業-2 我的作業-2-修改版 我的作業-2-修改版-最終版 我的作業-2-修改版-最終版-final 之類的\u0026hellip;\u0026hellip;有沒有感到很親切呢?\n那如果\u0026hellip;今天有一個專門幫我們命名這些檔案名稱的工具，\n而且他能幫我們命名好好的，不會像上面那樣可能自己最後都搞不清楚哪個才是最後一版，\n是不是就很棒呢! git 就是專門幫助我們管理這東西的工具。\n安裝 git sudo apt-get install git-all 基本使用概念 git 的使用是以一個資料夾底下的全部內容為單位的，\n在開始進行 git 前，第一步就是要在該目錄底下進行 git 的初始化 「git init」。\ngit 基本指令總整理 指令 功能 備註 git init 初始化要進行 git 的資料夾 git add [指定檔案] 增加更新 (新增) 內容 可以使用「.」代表目錄底下全部的更新檔案 git reset [指定檔案] 取消增加的內容 與 git add 相反的功能 git commit 確認增加的內容，並發佈一個新版本 git status 確認現在 git 的情況 (會告訴你有沒有新增內容) git push --set-upstream [branch名字] 第一次上傳時設定串流 想要備份到遠端，才需要參考此步驟 git push 上傳至遠端上備份 git pull 從遠端上備份下載 不同裝置、不同位置都適用 git branch 相關總整理 指令 功能 備註 git fetch 從遠端拉指定版本下來 與 git pull 不同在於不會自動 merge，而 git pull 只能拉到最新版 git checkout [a] branch切換至a git checkout -b [a] 開一個新的branch，命名為a，並切換至a (如果沒有存在，就是全新的，容易造成) git checkout -b [a] [b] 從遠端拉b的branch，命名為a，並切換至a （如果找不到b，會有error） git log git log git log -1 最後一個 git log git branch show出目前所在的branch git branch -a show出目前所有的branch git branch -D [a] kill branch a cat .git/config 直接show出來 more .git/config 看更多目前branch的資訊 git remote show origin show出目前遠端所有的branch git push -u origin [branch name] 第一次需要幫遠端(remote)建立branch git push --set-upstream origin [branch name] 等價於上面 git push 第二次以後就能直接push git --version git version git status 查看目前tracked/untracked files git commit -m \"msg\" commit 'msg' git merge --no-ff [branch] merge branch git config --list config infomation git config --global user.name [username] git config --global user.email [email] git reset HEAD 退回最後一個commit git reset [] 退回指定的commit git diff ../../.gitignore 比較 diff git checkout -- ../../.gitignore 取消tracked git checkout -- ../../core/ftModule/IMPFTTracker.cpp 取消tracked Reference https://www.itread01.com/content/1545176737.html https://stackoverflow.com/questions/348170/how-do-i-undo-git-add-before-commit ","date":"2021-03-06T12:28:05+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git-local/git_init/","tags":["Git","初學者","本地指令"],"title":"【Git #0】git 安裝，git 個人經常使用指令 總整理"},{"categories":["934 - 心理學筆記"],"content":"前言 這裡是我閱讀心理學一些文章的筆記。\n月暈效應 說明 - 「為什麼我們容易以偏概全?」 有沒有一種經驗，我們很常觀察一個人覺得他很聰明，就覺得他其他一切都會很美好。\n也就是說，我們以局部的印象，涵蓋到對整個人的印象 (以為帥、正，一切都是美好的)\n之前我們也有介紹到關於第一印象的事情，可參考：\n第一印象再加上月暈效應，幾乎構成了我們往後一段時間對同一人的評價。\n【心理學筆記】首因效應 -「一個人的第一印象，能大幅的左右我們往後的命運」\nhttps://wongwongnotes.com/posts/os-misc/growth/psychology/primary-effect/\n應用 月暈效應也告訴了我們，我們給人的第一印象非常的重要，\n會影響很長一段時間且不容易改變，\n因此不論是在任何的場合：貼心、熱情、好相處，往後與他人的相處總是無往不利\n(先不管這是不是做自己XD)\n破解 一樣，我只是想提醒自己看人千萬別只看表面XDD\n在理解一個人之前就擅自評斷他人的全部，是一種自視甚高的思維\n(你憑什麼覺得你只要一眼就可以了解對方？ 這難道不也是一種自大嗎？)\n第一印象的月暈效應就是人類的一種盲點，\n常常我們可能也會因此錯失了不少被自己誤解的美好人事物。\nReference https://zh.wikipedia.org/wiki/%E6%99%95%E8%BD%AE%E6%95%88%E5%BA%94 ","date":"2021-03-05T14:45:28+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/psychology/halo-effect/","tags":["心理學筆記"],"title":"【心理學筆記】月暈效應 -「為什麼我們容易以偏概全?」"},{"categories":["022 - 實況相關"],"content":"前言 streamelements 比起 nightbot，\n個人認為最主要的特色就是能夠有自己頻道專屬的「錢幣」!\n雖然近期 twitch 已經有出「忠誠點數」這個新功能，\n但這個機器人依然有更多可以「個人化」的空間與玩法!\n加入頻道 先去以下網址 https://streamelements.com/\n點選右上角的 login，選擇 twitch 按 Authorize 授權 給 twitch 到這邊應該就已經自動跟 twitch 綁定好了! (如果沒有的話再截圖留言給我，因為我已經設定過了不太確定第一次會不會不一樣)\n回到自己的頻道，在對話框輸入 「/mod streamelements」 指令設定相關 增加指令 → 在剛剛的 streamelements 畫面，按下「ADD NEW COMMAND」\n基本設定圖示 進階設定圖示 頻道「錢幣」設定 LOYALTY 這邊就是頻道「錢幣」設定的相關集合\n頻道「錢幣」設定 Reference https://www.twitch.tv/april_ponpon ","date":"2021-03-03T21:08:46+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/wp_editor_md_16f082c2510ca3fa69007cd2895ec097.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/022---%E5%AF%A6%E6%B3%81%E7%9B%B8%E9%97%9C/twitch-streamelements/","tags":["嗡嗡粗門玩"],"title":"【實況相關】twitch streamelements 指令設定教學 (內有完整圖片說明)"},{"categories":["370 - Ubuntu","299 - C++ 問題解決"],"content":"前言 此為 C++ 上遇到以下問題，個人的解決問題筆記\nRapidjson/document.h No such file or directory 解決方法 2021/9/14 第二次碰到時的解法 rapidjson library 未正確的被讀取，請檢查 makefile 路徑設定\n個人碰到的是 merge code 時，不小心按到換行導致找不到此 library\n2021/3/2 第一次碰到時的解法 rapidjson 沒有被正確的安裝\n請去官網：https://rapidjson.org/\n安裝 rapidjson\nlinux 安裝指令： sudo apt update sudo apt install rapidjson-dev Reference Rapidjson/document.h No such file or directory [https://github.com/discord/discord-rpc/issues/291](https://github.com/discord/discord-rpc/issues/291) - [How to Install rapidjson-dev in Ubuntu 18.04](https://www.howtoinstall.me/ubuntu/18-04/rapidjson-dev/) ","date":"2021-03-02T21:23:14+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/ubuntu/rapidjson-no-such-file/","tags":["C++","Ubuntu"],"title":"【C++】問題解決: Rapidjson/document.h No such file or directory"},{"categories":["370 - Ubuntu","398 - Linux 問題解決"],"content":"前言 此為 Linux / Ubuntu 上遇到以下問題，個人的解決問題筆記\nmkdir: cannot create directory ‘xxx’: Input/output error 解決方法 目前還不確定的正確成因： 問題的可能解決方向為：\n- 硬碟容量滿了 - 檢查資料夾權限是否足夠 Reference Error creating directory: Input/output error” in Lubuntu, and in Ubuntu\nhttps://askubuntu.com/questions/723212/error-creating-directory-input-output-error-in-lubuntu-and-in-ubuntu ","date":"2021-03-01T02:17:12+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/ubuntu/cannot-create-directory/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】Ubuntu 問題解決: mkdir: cannot create directory ‘xxx’: Input/output error"},{"categories":["551 – Pytorch 語法"],"content":"前言 在機器學習的領域中，我們經常用的資料格式就是 tensor，\n有時為了格式對齊，我們需要增加維度。\n常見使用地方：讀取時因為一層一層拆開後，要拼回去原來的資料型態 (shape) 範例程式碼 我們能使用 「.unsqueeze()」 來增加 tensor 維度，下面範例程式碼。\nimport torch a = torch.tensor([1, 2]) print(a) print(a.shape) print(a.unsqueeze(0)) print(a.unsqueeze(0).shape) print(a.unsqueeze(0)[0][0]) print(a.unsqueeze(0)[0][1]) print(a.unsqueeze(1)) print(a.unsqueeze(1).shape) print(a.unsqueeze(1)[0][0]) print(a.unsqueeze(1)[1][0]) 結果與說明 原先 a 的維度是一維兩個值 [2]\n後來經過 a.unsqueeze(0)，多了「第 0 維」變成二維 a[0][0] = 1 a[0][1] = 2 a[1][0] = error 經過 a.unsqueeze(1)，多了「第 1 維」變成二維 a[0][0] = 1 a[1][0] = 2 a[0][1] = error ","date":"2021-02-28T22:28:22+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/wp_editor_md_51b656ed8f8808fb1df2877cb7f35c9b.jpg","permalink":"https://wongwongnotes.com/posts/ai/frameworks/pytorch-tensor/pytorch-unsqueeze/","tags":["語法"],"title":"【Pytorch】tensor.unsqueeze() - tensor 增加維度的方法"},{"categories":["398 - Linux 問題解決"],"content":"前言 此為 ffmpeg 上遇到以下問題，個人的解決問題筆記\n[NULL @ 0x557eb6366300] Unable to find a suitable output format for \u0026#39;text=%{n}:\u0026#39; text=%{n}:: Invalid argument 解決方法 通常指的是 ffmpeg 下的指令中有誤，\n以下附上幾個可能的常見錯誤，可以檢查看看：\n「\u0026rsquo;\u0026rsquo;」、「\u0026quot;\u0026quot;」: 單雙引號使用無對稱 「{}」、「[]」、「()」: 括弧使用無對稱 output影片格式不支援：例如「mp4」打成「mp5」之類的\u0026hellip; 其他使用參數上的問題\u0026hellip; reference unable to find a suitable output format for \u0026lsquo;ffmpeg\u0026rsquo;\nhttps://stackoverflow.com/questions/32718364/unable-to-find-a-suitable-output-format-for-ffmpeg\n","date":"2021-02-27T22:06:56+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/ffmpeg-invalid-argument/","tags":["Bash","Linux","Ubuntu"],"title":"【ffmpeg】問題解決: Unable to find a suitable output format for 'text=%{n}:' text=%{n}:: Invalid argument"},{"categories":["551 – Pytorch 語法"],"content":"前言 在機器學習的領域中，我們經常用的資料格式就是 tensor，\n但有時為了方便我們查看數值，我們需要將他轉回純量。\n範例程式碼 我們能使用 「.item()」 來進行轉換，下面範例程式碼。\nimport torch a = torch.tensor([2]) print(a) print(a.item()) 結果 ","date":"2021-02-26T21:48:40+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/wp_editor_md_fd2fd708e45fdb1b998985e2c10a9428.jpg","permalink":"https://wongwongnotes.com/posts/ai/frameworks/pytorch-tensor/pytorch-item/","tags":["語法"],"title":"【Pytorch】tensor.item() - 從 tensor 轉純量的方法 tensor.item()"},{"categories":["320 - Linux 搜尋內容"],"content":"前言 我們使用 linux 的 terminal 環境時，有時候為了成功殺掉一個程式，\n我們必須得到程式的相關資訊。\nps：查詢正在執行中的程式資訊 ps [-aef] 後面的 [-aef] 可下可不下的參數。\n-a：顯示所有程序 -e：此參數的效果和指定 \u0026ldquo;a\u0026rdquo; 參數相同 -f：顯示 UID (哪位user執行), PPID, C 與 STIME 欄位 但有時候會列出太多資訊，我們找不到我們所需要的。\n這們我們會需要 grep 幫助我們進行搜尋。\n另外也有一些人常用： ps aux 可以顯示程式相關的全部資訊。\ngrep：進行結果搜尋 ps -a | grep your_process 就可以只顯示你所需要的資訊。\npgrep：專門用來找 pid 的 ps -a | pgrep your_process 就可以只顯示你指定程式的pid。\n你可能會想問：找到程式的 pid 有什麼用呢？\n我們就可以用指令刪除指定的程式囉！\n刪除指定程式 by pid ps | pgrep your_process | xargs pkill -9 Reference https://kknews.cc/zh-tw/code/rxm2yyo.html https://kknews.cc/zh-tw/code/qnnpkmg.html https://kknews.cc/zh-tw/code/gpz8brl.html ","date":"2021-02-25T18:07:04+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-grep-pgrep-pid/","tags":["Linux","搜尋內容"],"title":"【Linux 搜尋內容 #3】ps, grep, pgrep - 找到執行中程式的相關資訊 與 pid (ps aux, ps -ef)"},{"categories":["320 - Linux 搜尋內容"],"content":"前言 我們在使用終端機指令時，很常會顯示一些欄位相關的資訊。\n例如：「ls -l」/「ll」、「ps -a」、「top」、「history」等等\n這些的結果都是有「欄位」的概念的 (也就是說，他的資訊是由行列所構成)，\n我們可以利用 grep, awk, xargs ，三種功能的結合，快速找到自己需要的資訊。\ngrep 算是 linux terminal 使用者在查詢資料的必備指令之一，\n使用方式為： 原指令 | grep \u0026#39;搜尋目標\u0026#39; 注意中間的「|」，代表「且」的意思\n使用範例 top | grep \u0026#39;ubuntu\u0026#39; 結果：只顯示包含 \u0026ldquo;搜尋目標\u0026rdquo; 的結果 原本 top 會顯示所有系統執行中的程式，\n使用「grep」之後，我們只關心「ubuntu」這個使用者正在執行的程式。\nawk 這個是針對當輸出欄位有行列的概念時，能夠只輸出單一的直行。\n使用方式為：\n原指令 | grep \u0026#39;搜尋目標\u0026#39; | awk \u0026#39;{print $0}\u0026#39; $0：顯示全部內容 $1：顯示第1直行 $2：顯示第2直行\u0026hellip; 使用範例 1.我們可以先觀察使用「history」看歷史紀錄。 history 結果： (只節錄一部份)\n2.再來我們使用「grep \u0026lsquo;history\u0026rsquo;」只顯示出有history的欄位 history | grep \u0026#39;history\u0026#39; 結果： 3.我們使用「awk \u0026lsquo;{print $1}\u0026rsquo;」只抓出第1直行的「第幾條目」 history | grep \u0026#39;history\u0026#39; | awk \u0026#39;{print $1}\u0026#39; 結果： 就可以快速濾出我們想要的資料囉！\nxargs xargs 指的是我們對於篩選後的結果要進行什麼樣的動作，\n例如我們可以使用「grep」的敘述篩選出所有「包含指定內容」的檔案，\n然後透過「awk」只擷取檔名的部份，\n然後使用例如「xargs rm」，對所有篩選出的檔名進行「rm」的刪除操作。\n使用範例 我們現在有一個1000張圖片照編號排的資料夾，\n1. 看到所有檔案的完整資訊 ll 結果： 2. 利用 grep，取出檔名包含 \u0026ldquo;000001\u0026rdquo; 的檔案 ll | grep \u0026#39;000001\u0026#39; 結果： 3. 利用 awk，只取出檔名 (第9個直行，所以是 $9) ll | grep \u0026#34;000001\u0026#34; | awk \u0026#39;{print $9}\u0026#39; 結果： 4. 利用 xargs，對這些檔案進行操作 (這裡示範移除，rm) ll | grep \u0026#34;000001\u0026#34; | awk \u0026#39;{print $9}\u0026#39; | xargs rm 結果：因為都刪光光了所以沒圖XD 綜合運用 - 使用範例2 我們嘗試移除所有名字為 \u0026lsquo;none\u0026rsquo; 的 docker images，\n指令如下： docker images | grep \u0026#39;\u0026lt;none\u0026gt;\u0026#39; # docker images | grep \u0026#39;\u0026lt;none\u0026gt;\u0026#39; | awk \u0026#39;{print $3}\u0026#39; docker images | grep \u0026#39;\u0026lt;none\u0026gt;\u0026#39; | awk \u0026#39;{print $3}\u0026#39; | xargs docker rmi 效果如下： Reference https://unix.stackexchange.com/questions/37052/how-to-delete-files-filtered-out-by-awk https://alvinalexander.com/blog/post/linux-unix/any-easy-way-print-column-or-columns-from-text-file-using-awk/ ","date":"2021-02-24T18:48:31+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/wp_editor_md_b1044dccfbc51ddeb154c2f82823307d.jpg","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-ubuntu-grep-awk-xargs/","tags":["Bash","Linux","Ubuntu","搜尋內容"],"title":"【Linux 搜尋內容 #2】grep, awk, xargs - 尋找需要的資料，並快速濾出/篩選 結果欄位 所需要的資訊 (內附圖文說明) filter column"},{"categories":["320 - Linux 搜尋內容"],"content":"前言 在使用 terminal 時，經常會作各種搜尋，\n整篇進行一些「個人常用的整理」。\n這邊只進行一些「個人常用的整理」，因此不包含完整的功能介紹。\nack : 搜尋程式碼內文 (專門) find : 搜尋檔案、資料夾 grep : 搜尋程式碼內文 find + grep: 搜尋指定範圍檔案，並檢查內文 安裝 grep, find 通常都是 linux 內建的，通常要安裝的只有比較進階搜尋的 ack。\n安裝 ack macOS: brew install ack Linux: sudo apt-get install ack-grep ack 使用 內文搜尋 (程式碼內搜尋) 找在檔案裡面，出現 test 的文字片段 ack test 搜尋 (不分大小寫) ack 預設的搜尋「是有分大小寫的」\nack -i \u0026lt;test\u0026gt; 記法： i = ignore (忽略大小寫)\ngrep 使用 grep 建議都下「\u0026ndash;colour」這個 flag，會上顏色比較好判讀 有些系統內建的 alias 已經都有寫好這個方便的功能了，同樣的我們也能自己改\nalias grep= grep \u0026ndash;colour\n直接取代原來的 grep\n搜尋單一檔案內 grep test ./file.txt 搜尋資料夾內所有檔案 (習慣 ack 後可以取代) 搭配 -r 使用，結尾配上一個資料夾 grep -r test ./ --colour find 使用 找檔案 顯示相對路徑 find ./ -name \u0026#34;*.png\u0026#34; 顯示絕對路徑 find ~+ -name \u0026#34;*.png\u0026#34; 找檔案，指定檔案(f, file) / 資料夾(d, directory) 找檔案(f, file) find ./ -name \u0026#34;*.png\u0026#34; -type f 找資料夾(d, directory) find ./ -name \u0026#34;*.png\u0026#34; -type d 進階用法 find + grep 先鎖定檔案 (例如 .py) 後，再找尋特定檔名 假如我們直接搜尋特定檔名 test，如果沒有先鎖定範圍，\n有可能會找到\ntest.py test2.py test3.py test.cpp test2.cpp test3.cpp 但我們可能只要\ntest.py test2.py test3.py 這時我們就可以用到 find + grep\nfind ./ -name \u0026#34;*.py\u0026#34; | grep \u0026#34;test\u0026#34; 可以比較一下與下面指令的差別，更有學習效果。\n先鎖定檔案 (例如 .py) 後，再檔案內文字內搜尋 (找多個檔案的，特定程式碼內容) find ./ -name \u0026#34;*.py\u0026#34; -exec grep -H -n \u0026#34;test\u0026#34; {} \\; 可以比較一下與上面指令的差別，更有學習效果。\n說明：\n「-exec」：執行後面指令 「grep -H」：(顯示完整檔案路徑) 「grep -n」：(顯示行號) 「 {} 」: 帶入前面參數 「 ; 」：作為指令的結束 Reference How To Install and Use Ack, a Grep Replacement for Developers, on Ubuntu 14.04 Unix/Linux 的 find 指令使用教學、技巧與範例整理 在 Linux 下使用 find 指令查詢目錄與檔案的速查筆記 Linux搜尋資料夾下的檔案內文：find+grep指令產生器 / Search the Content of Files on Linux: find + grep Command Builder [轉]grep命令介紹 ","date":"2021-02-23T13:39:26+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-ack-grep-find/","tags":["Linux","搜尋內容"],"title":"【Linux 搜尋內容 #1】ack, grep, find - 在 terminal 的各種搜尋大全, 個人經常使用情境整理"},{"categories":["115 - Python 檔案處理"],"content":"前言 我們在進行 python 檔案處理時，會經常需要開關檔案，\n本文提供 with 開檔模版可以直接套用。\n說明 利用 python「 with 搭配 open 」的方式是個人非常喜歡使用的開關檔案方式！\n因為在 with 底下所執行完的程式碼之後，\n我們不需要再利用 file.close() 去關閉檔案\n「with 會幫助我們直接將檔案關閉」，個人也非常喜歡這樣的寫法！\n範例與模板 讀檔 讀取內容我們可以透過「readlind」，可以幫助我們一次讀一行，\n記得要 open 使用 \u0026ldquo;r\u0026rdquo; 模式 (read)\n一次讀一行，結果存在 line with open(\u0026#34;read_file.txt\u0026#34;,\u0026#34;r\u0026#34;) as f_read: while(True): line = f_read.readline() if not line: break 存檔 寫內容我們可以透過「write()」，記得要 open 使用 \u0026ldquo;w\u0026rdquo; 模式 (write)\nwith open(\u0026#34;write_file.txt\u0026#34;,\u0026#34;w\u0026#34;) as f_write: f_write.write(\u0026#34;write some text here\u0026#34;) ","date":"2021-02-23T13:13:06+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/python-with-open/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #5】python 開關檔範例 與 程式模板 with open / file open"},{"categories":["381 - Bash 基本語法"],"content":"前言 撰寫 bash 的 程式碼可以幫助我們自動化完成一連串的指令，\n(或者我們也會說這是一種「腳本」，會自動完成一些事情)\n如何使用 畢竟是最簡單的指令，其實就只要輸入\necho + 空白 + 你要的文字，\n就可以回傳你要的文字囉！\n可以應用的地方 基本上是隨時可用、想用就用，\n但隨著我們撰寫的 bash 指令愈來愈多內容，\n在必要的段落加上 echo，有時候非常方便我們去 debug，\n我們可以明確知道哪一行已經執行完，哪一行尚未執行，\n進而推測 bug 出現再哪一行，雖然簡單，但非常實用。\nSample Code echo \u0026#34;Hello world\u0026#34; 結果： Hello world\n進階使用：顯示變數 簡單來說就是 echo 我們也很經常用來顯示變數的值，\n我們在變數前面加一個「$」，代表的是我們要取變數的值。\n(沒有加的話，其實也是文字，在下面會有說明)\n範例如下：\n(注意：宣告變數時，等於不能分開！！！)\nSample Code # 注意等於不能分開 a=10 echo $a 結果： 10\n同時顯示文字與對應的變數 簡單來說就是結合前面兩個的綜合使用。\n這邊想特別提到的是，在 bash 裡面 不使用 「\u0026quot; \u0026ldquo;」框出文字區塊是可以的，\n但個人更喜歡使用「\u0026rdquo; \u0026ldquo;」使文字表達更為清楚，\n我們可以從下方例子看出差別。\n注意：\n在往後有些搜尋指令中，例如「grep」，有沒有「\u0026rdquo; \u0026ldquo;」會非常重要！\n因為沒有「\u0026rdquo; \u0026ldquo;」的字串若有兩個字以上，例如「Hello world」\n系統會誤會「Hello」視為一個單一的指令。\n懶人包：總之不管是不是字串，都用「\u0026rdquo; \u0026ldquo;」就一定沒問題了！\nSample Code # 注意等於不能分開 a=10 echo a=$a echo my number a=$a # 上下兩種表達方式都可以，為求文字表達更清楚，個人更喜歡下面的用法 echo \u0026#34;a\u0026#34;=$a echo \u0026#34;my number a\u0026#34;=$a 結果： a=10\nmy number a=10\na=10\nmy number a=10\n","date":"2021-02-22T15:53:07+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/Screenshot-from-2021-02-22-16-04-18.png","permalink":"https://wongwongnotes.com/posts/linux-shell/bash-script/bash-basics/bash-echo/","tags":["Bash","Linux","Ubuntu","基本語法"],"title":"【Bash 基本語法 #1】echo，bash 的 hello world 基本教學，開始自動化程式的第一堂課 / 撰寫腳本，利用 echo 顯示變數"},{"categories":["299 - C++ 問題解決"],"content":"前言 此為 C++ rapidjson 上遇到以下問題，個人的解決問題筆記\nrapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;\u0026amp; rapidjson::GenericValue\u0026lt;Encoding, Allocator\u0026gt;::operator[](const rapidjson::GenericValue\u0026lt;Encoding, SourceAllocator\u0026gt;\u0026amp;) [with SourceAllocator = rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt;; Encoding = rapidjson::UTF8\u0026lt;\u0026gt;; Allocator = rapidjson::MemoryPoolAllocator\u0026lt;\u0026gt;]: Assertion `false\u0026#39; failed. 解決方法 「Assertion false failed.」，這行也代表著格式出現錯誤，\n請再檢查 json 的 輸入/輸出格式是否有問題。\n個人常用檢查方式 (網站) ：\nJson Parser Online http://json.parser.online.fr/ 能夠快速檢查是否 json 的格式是否有問題。\nreference [Solved] Rapidjson IsString assertion failure\nhttps://discuss.cocos2d-x.org/t/solved-rapidjson-isstring-assertion-failure/38711 ","date":"2021-02-21T22:32:23+08:00","permalink":"https://wongwongnotes.com/posts/cpp/advanced-concepts/cpp-troubleshooting-2/cpp-rapidjson-assertion-false-failed/","tags":["Bash","C++","Linux","Ubuntu"],"title":"【C++】問題解決: rapidjson::GenericValue\u003cEncoding, Allocator\u003e\u0026 rapidjson::GenericValue\u003cEncoding, Allocator\u003e::operator[](const rapidjson::GenericValue\u003cEncoding, SourceAllocator\u003e\u0026) [with SourceAllocator = rapidjson::MemoryPoolAllocator\u003c\u003e; Encoding = rapidjson::UTF8\u003c\u003e; Allocator = rapidjson::MemoryPoolAllocator\u003c\u003e]: Assertion `false' failed."},{"categories":["921 - iphone / iOS"],"content":"前言 隨著手機的使用越來越普及，我們也越常利用手機來幫助我們完成日常生活中的各種問題，\n但有時在填表單時我們會需要慢慢一個個文字輸入自己的信箱(email), 地址(address)\u0026hellip;，\n每次都輸入這樣長的文字，久了也會漸漸開始覺得懶了，\n本文提供一個小技巧能夠快速完成這件事。\n使用示範 只需要輸入「ㄒㄒ」，就能快速完成自己的 email (ㄒㄒ = 信箱) △ 需要輸入「ㄒㄒ」，直接看到自己的信箱出現在選項中！\n只需要輸入「ㄉㄓ」，就能快速完成自己的地址 (ㄉㄓ = 地址) △ 需要輸入「ㄉㄓ」，直接看到自己的地址出現在選項中！\n使用教學 在手機中找到「鍵盤」的功能 路徑： 設定 -\u003e 一般 -\u003e 鍵盤 -\u003e 點選「替代文字」 ![](/images/restored/2021/02/IMG_6081-scaled.jpg) 按右上角的「＋」，可以新增新的替代文字 輸入「字詞」與「輸入碼」 「字詞」就是你要快速輸入的文字內容 「輸入碼」就是快速輸入的替代方案 注意：「輸入碼」目前只能設定「注音或英文」，這邊以「中文注音」輸入作為示範 ![](/images/restored/2021/02/IMG_6087.png) 輸入好之後，註冊就完成了！ 之後輸入 email 或 地址 不用再慢慢一個個打字囉！ Reference ","date":"2021-02-19T14:58:14+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/IMG_6081-scaled.jpg","permalink":"https://wongwongnotes.com/posts/os-misc/os/ios/phone-ipad-fasttext/","tags":["iOS"],"title":"【iOS】(圖文教學) iphone, ipad 鍵盤 快速輸入 信箱(email), 地址(address), 手機(phone)... 「替代文字」使用小技巧"},{"categories":["842 - 物件導向設計 OOP"],"content":"前言 在程式碼優化中，我們可以想像如果我們能將每一個功能拆的非常的精細\n(例如一個函數 (function) 只會有屬於他的單一功能）\n這部份也是 OOP (物件導向程式設計、Object Oriented Programming) 所強調的精神之一\n\u0026ldquo;S\u0026quot;OLID 物件導向設計原則中的 \u0026ldquo;S\u0026rdquo; 指的就是 Single Responsibility。\n以個人經驗來說，可以得到以下的好處：\n- 除錯(debug) 單一功能的問題：只需針對單一函數 (function) 去做 debug - 維護(maintain) 程式碼的單一功能：只需維護單一函數 (function) - 修改(modify) 程式碼的單一功能：只需修改單一函數 (function) - 程式碼可讀性 (readability) ：可以明確知道此函數就是針對單一功能去做撰寫，在大型系統系統中如果一個函數做太多功能，「別的工程師」在閱讀時也必須去了解此函數中更多程式碼的細節。 - 程式碼可重用性 (reusability)：如果每一個功能都拆的很乾淨，有時類似功能的單一函數相對容易做重複使用。 還有很多的好處，雖然以上看起來像在講廢話(欸)，\n實際上要去「實踐」這件事與「了解」這件事情完全是兩回事XD，\n我自己也正努力在「優化程式碼」的學習路上。\nSRP (單一職責原則、Single Responsibility Principle) 原文 (Robert C. Martin)\nA class should have only one reason to change\n自己的解釋\n降低單一功能當需要「改變」時，影響整體系統的機會。\n講完了。你說「蛤?」\n沒辦法這概念就這麼抽象XDDD，試著舉一個例子也許比較好懂。\n我想要定義一支狗的類別：\nclass Dogs: # 建構式 def __init__(self, name, sex): self.name = name # 姓名屬性 self.sex = sex # 性別屬性 假設我們知道了，「肚子餓」或「受傷」都會讓狗生氣。\n我們可以這樣增加功能：\nclass Dogs: # 建構式 def __init__(self, name, sex): self.name = name # 姓名屬性 self.sex = sex # 性別屬性 # 方法(Method) def hungry(self): print(\u0026#34;The dog is angry.\u0026#34;) def hurt(self): print(\u0026#34;The dog is angry.\u0026#34;) 我們可以看到不論是 「hungry(self)」, 「hurt(self)」，\n都會觸發狗生氣的反應。\n但是上面的寫法並沒有運用到 SRP 的觀念，\n我們假設我們想要增加狗「生氣」時會有的其他反應，\n有沒有發現同樣的功能我們可能需要改兩次一樣的東西?\n那我們在仔細想一下，現實中會有更多種可能會觸發狗「生氣」的方法，\n上面的方法雖然寫的快速又直接明瞭，但相對來說碰到修改「生氣」的功能時，\n變成「全部」都要慢慢一個個改。\n於是參照SRP 的觀念，\n我們定義了一個「生氣」的方法，象徵了當狗生氣時可能會有的反應。\nclass Dogs: # 建構式 def __init__(self, name, sex): self.name = name # 姓名屬性 self.sex = sex # 性別屬性 # 方法(Method) def angry(self): print(\u0026#34;The dog is angry.\u0026#34;) def hungry(self): self.angry() def hurt(self): self.angry() 這樣就能夠解決我們上述所說的，當我們想增加「生氣」會有的反應時，\n我們不需要全部一個個慢慢改動功能。\n也就是 SRP (Single Responsibility Principle) 所提倡的單一職責原則。\n使用情境、優缺點 上面的例子中，我們也看到大型程式中，SRP 的效果才會明顯的被展現，\n如果只是小程式講究快速開發，也許 SRP 有時候開發效率不見得會高，\n但維護功能上，我們也能看出 SRP 在大型系統的重要之處。\n我們只需要針對單一函數 「angry(self)」 去進行維護即可。\n若沒有使用 SRP ，維護上我們必須一個個函數去查看。\n就如同上述例子，我們必須同時修改「hungry(self)」, 「hurt(self)」的功能。\n更何況是更大型的系統，未使用 SRP 維護會更缺乏效率的。\nReference https://wadehuanglearning.blogspot.com/2019/12/blog-post.html http://teddy-chen-tw.blogspot.com/2014/04/solid.html https://ithelp.ithome.com.tw/articles/10191955 https://www.learncodewithmike.com/2020/01/python-class.html https://www.learncodewithmike.com/2020/01/python-method.html https://weilihmen.medium.com/%E9%97%9C%E6%96%BCpython%E7%9A%84%E9%A1%9E%E5%88%A5-class-%E5%9F%BA%E6%9C%AC%E7%AF%87-5468812c58f2 ","date":"2021-02-18T01:33:11+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/software-engineering/oop/oop-srp/","tags":["OOP","物件導向設計"],"title":"【程式優化】OOP (物件導向程式設計、Object Oriented Programming) - SRP (單一職責原則、Single Responsibility Principle)"},{"categories":["842 - 物件導向設計 OOP"],"content":"前言 程式碼的重複利用在長期開發程式是相當重要的一環。\n(大家應該都不希望每次有同樣的要求時，都要重寫一次程式碼吧XD)\n因此，我們撰寫程式碼時，特別是需要長期使用的，\n我們會特別把重複性高的功能拉出來，讓我們程式碼的可重用性 (code reusability) 提高。\ncode reuse (code reusability、程式碼可重用性) 要說 code reusability 之前，我們可以先理解 SRP (單一職責原則、Single Responsibility Principle)\n可參考：【程式優化】OOP (物件導向程式設計、Object Oriented Programming) – SRP (單一職責原則、Single Responsibility Principle)\nSRP 所提倡的概念是將程式碼的功能單一化，單一化後的功能我們很容易進行再次的使用，\n我們一樣舉 SRP 中所提到的例子。\nclass Dogs: # 建構式 def __init__(self, name, sex): self.name = name # 姓名屬性 self.sex = sex # 性別屬性 # 方法(Method) def angry(self): print(\u0026#34;The dog is angry.\u0026#34;) def hungry(self): self.angry() def hurt(self): self.angry() 我們可以指藉由修改 「angry(self)」 這個函數，\n同時讓「hungry(self)」 、 「hurt(self)」獲得更新版生氣函數的功能。\n此外如果要增加新的現象 (例如：主人打呼)，狗狗也會生氣，\n透過良好的 code reusability，我們可以很容易又有效率地實現它。\nReference https://wadehuanglearning.blogspot.com/2019/12/blog-post.html http://teddy-chen-tw.blogspot.com/2014/04/solid.html https://ithelp.ithome.com.tw/articles/10191955 https://www.learncodewithmike.com/2020/01/python-class.html https://www.learncodewithmike.com/2020/01/python-method.html https://weilihmen.medium.com/%E9%97%9C%E6%96%BCpython%E7%9A%84%E9%A1%9E%E5%88%A5-class-%E5%9F%BA%E6%9C%AC%E7%AF%87-5468812c58f2 ","date":"2021-02-17T18:35:02+08:00","permalink":"https://wongwongnotes.com/posts/cloud-iot/software-engineering/oop/oop_code_reuse/","tags":["OOP","Python","物件導向設計"],"title":"【程式優化】OOP (物件導向程式設計、Object Oriented Programming) - code reuse (code reusability、程式碼可重用性)"},{"categories":["630 - 演算法 Algorithm"],"content":"前言 這是我自己理解後所做的演算法筆記。\n動態規劃 (Dynamic programming，DP) 先備知識 討論動態規劃前，我們要先有 divide and conquer (將大問題化為多個小問題) 的觀念，\n我們先將大問題化為多個小問題後，開始分析這些小問題裡面，\n如何最佳化我們演算的效率。\n觀念 動態規劃 (Dynamic programming，DP) 中，我們更著重在討論這些小問題 (或說子問題)，\n怎麼樣去解決才能夠有最佳效率，我們會發現，在很多大問題化為的子問題中，\n我們會碰到大量的「解決相同(重複)子問題」情況，造成演算效率變差。\n例子 我們舉最經典的「費波納奇數列」為例：\nf(0) = 0 f(1) = 1 f(x) = f(x-1) + f(x-2) (n \u0026gt;= 2) 未使用動態規劃 解 f(5) 每一個箭頭，都表示我們要做一次加法，\n我們的總計算量 = 14 次\n使用動態規劃 解 f(5) 我們先建立一個表格，當算出一次結果時，就保存在表中。\nx f(x) 0 0 1 1 2 1 3 2 4 3 5 5 於是透過查表產剩下面的結果： 每一個箭頭，都表示我們要做一次加法，\n藍色箭頭表示我們因為已經算過一次 (並儲存答案在表中)，\n所以我們直接查表找答案。\n我們的總計算量 = 8 次 結論 雖然上面的例子看起來不會差太多，\n那是因為我們舉的例子數字還小，\n實際上的「時間複雜度」 未使用 DP: O(2^n) -\u0026gt; 約 2 的 n 次方 使用 DP: O(n) 這樣有沒有使用 DP 的差別就夠明顯了吧!\nReference https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92 https://zh.codeprj.com/blog/66e3901.html https://www.geeksforgeeks.org/program-for-nth-fibonacci-number/\nDynamic Programming Explanation with Fibonacci 用費波那契數列來入手動態規劃 https://www.youtube.com/watch?v=pqivnzmSbq4\u0026ab_channel=mycodeschool ","date":"2021-02-14T02:07:07+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Copy-of-Fibonacci.png","permalink":"https://wongwongnotes.com/posts/cs-theory/algorithm-theory/algorithm/dynamic-programming/","tags":["Algorithm","演算法"],"title":"【演算法筆記 #1】動態規劃 (Dynamic programming，DP)"},{"categories":["934 - 心理學筆記"],"content":"前言 這裡是我閱讀心理學一些文章的筆記。\n巴納姆效應 說明 - 「為什麼我們看心理測驗、星座或算命都會覺得準?」 「以模糊適用於每個人的敘述，造成預測很準確的錯覺」 「心理測驗、星座描述」經常使用，仔細思考會發現其實這幾乎是通用於所有人的敘述 描述內容模糊大眾化、適用每個人 應用 覺得自己不太會說話的人，如果利用這樣的表達方式，\n也許能讓聽者有種「他好像很理解我的錯覺」。\n也有些很厲害的直銷手法也會運用這樣的手段，讓聽者有「他好像很理解我的錯覺」。\n進而達成說服對方購買的成果。\n運用「巴納姆效應」得到一些個人訊息，再造新的句子，\n再得到更多的訊息，如此反覆去了解一個人，且對方會覺得「這個人好像真的很理解我」。\n破解 套用對方的句子到別人身上，如果發現其實也可以適用，\n其實這就表示同樣的這句話並不只有你適用，大家都適用。\n你會發現其實對方並沒有真的這麼了解你。\n(同理可以拿來破解心理測驗、星座敘述、算命，以及一些直銷的語言)\n但我並不否認上述這些仍有一定的準確程度，\n畢竟這些背後也有的是利用「大量的統計」才產生而出的結果，\n因此可以拿上面所說的「套用對方的句子到別人身上」看看，\n觀察這句子是否「真正的是量身訂製」\nReference https://zh.wikipedia.org/wiki/%E5%B7%B4%E7%B4%8D%E5%A7%86%E6%95%88%E6%87%89\nhttps://www.youtube.com/watch?v=I-Up1t3zY3Q\u0026ab_channel=%E6%96%87%E6%A3%AE%E8%AA%AA%E6%9B%B8\nhttps://www.youtube.com/watch?v=DiOF2p_XTYA\u0026list=LL\u0026index=2\u0026ab_channel=%E7%B6%AD%E6%80%9D%E7%B6%ADWeisWay\nhttps://www.youtube.com/watch?v=4Zgpe1NhA-0\u0026ab_channel=%E9%98%BF%E7%95%AB\n","date":"2021-02-12T20:47:00+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/psychology/barnum-effect/","tags":["心理學筆記"],"title":"【心理學筆記】巴納姆效應 -「為什麼我們看心理測驗、星座或算命都會覺得準?」"},{"categories":["398 - Linux 問題解決"],"content":"問題 使用 7z 進行壓縮、解壓縮時，會碰到 System ERROR: E_FAIL\nSystem ERROR: E_FAIL 解決方法 該路徑的權限不足，請更換資料夾路徑後再重新執行此指令。\nReference https://unix.stackexchange.com/questions/266870/e-fail-error-in-7zip ","date":"2021-02-11T16:36:36+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/Screenshot-from-2021-02-19-16-34-31.png","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/linux-7z-e_fail/","tags":["Linux"],"title":"【Linux】問題解決：7z 壓縮、解壓縮 System ERROR: E_FAIL"},{"categories":["111 - Python 基礎語法"],"content":"前言 有時候我們會需要清除 list 裡面的特定內容，這篇有 list 清除相關內容的總整理，\n包含 remove, pop, del, clear 的相關用法。\n移除特定元素 list.remove(element) list = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] list.remove(\u0026#39;dog\u0026#39;) 輸出結果為： ['cat', 'dog', 'pig', 'dog'] \u003e 特別注意我們只會清除最先找到的「第一個」相符元素 如果有要完全清除該元素的需求，可以先使用搜尋的方式：\ntarget = \u0026#39;dog\u0026#39; list = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] while(target in list): list.remove(target) 輸出結果為： [\u0026lsquo;cat\u0026rsquo;, \u0026lsquo;pig\u0026rsquo;] 移除特定位置 list.pop(index) list = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] list.pop(3) 輸出結果為： ['cat', 'dog', 'dog', 'dog'] 依照 0, 1, 2, 3, 4 的順序，我們移除了第3個欄位的元素。 其他 pop 常見用法 - pop(0) 移除第一個欄位，queue 常用 queue 的定義是先進先出，也就是說「先進來」的會優先拿掉，\n我們可以結合 append() 與 pop(0) 的這兩個功能，\n就能夠簡單實作出一個 queue。\n其他 pop 常見用法 - pop() 移除最後一個欄位 (等於 pop(-1))，stack 常用 stack 的定義是先進後出，也就是說「後進來」的會優先拿掉，\n我們可以結合 append() 與 pop() 或 pop(-1) 的這兩個功能，\n就能夠簡單實作出一個 stack。\n移除特定位置(進階) del list[index] del 的用法與 pop 類似，不同的地方在於 del 不回傳值而是直接修改原 list，\nlist = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] del list[3] 輸出結果為： ['cat', 'dog', 'dog', 'dog'] 依照 0, 1, 2, 3, 4 的順序，我們移除了第3個欄位的元素。 不同於pop的地方在於使用 del，我們不需要用一個參數去等於這個函數的結果。 del 進階用法，比 pop 更靈活的刪除特定範圍位置 del 有 pop 做不到的功能，就是能夠直接指定欄位進行刪除，\n例如：del list[0:2] ，刪除位置 0, 1 的元素 list = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] del list[0:2] 輸出結果為： [\u0026lsquo;dog\u0026rsquo;, \u0026lsquo;pig\u0026rsquo;, \u0026lsquo;dog\u0026rsquo;]\n例如：del list[:] ，等於直接刪除全部元素，用法等同於 clear()\nlist = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] del list[:] 輸出結果為： [] 將 list 完全清空 list.clear() list = [\u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;pig\u0026#39;, \u0026#39;dog\u0026#39;] list.clear() 輸出結果為：[] 我們將 list 完全清空 Reference How to Clear a Python List [4 Ways \u0026amp; Examples] Python List clear() Python List remove() Python List pop() Remove all occurrences of a value from a list? 在 Python 列表中的 del、remove 和 pop 方法區別的介紹 ","date":"2021-02-10T12:33:00+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-list-remove-pop-del/","tags":["Python","Python 基礎用法","基礎語法"],"title":"【Python 基礎語法 #2】python list 清除, 移除內容元素 remove, pop, del, clear 相關用法整理"},{"categories":["198 - Python 問題解決"],"content":"前言 當python執行時發生：AttributeError: module \u0026rsquo;numbers\u0026rsquo; has no attribute \u0026lsquo;Integral\u0026rsquo; 的問題。\n解決方法 此為重複名稱的 package 被 python import 導致混淆的錯誤\n請檢查同一個路徑底下是否有 numbers.py 的檔案\n其他類似的 AttributeError: module 問題也可以先檢查是否檔案的命名不小心使用到了一樣的檔名。\nReference https://stackoverflow.com/questions/53668779/attributeerror-module-numbers-has-no-attribute-integral ","date":"2021-02-09T10:55:43+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/python-attributeerror-module/","tags":["Python"],"title":"【Python】問題解決：AttributeError: module 'numbers' has no attribute 'Integral'"},{"categories":["115 - Python 檔案處理"],"content":"前言 在 linux 系統中，我們會經常使用 chmod 來做檔案權限的更改，\n我們同樣也能在 python 中使用 os.chmod 達到一樣的效果。\n範例程式碼 import os with open(\u0026#34;test.txt\u0026#34;, \u0026#39;w\u0026#39;) as f_out: f_out.write(\u0026#34;test\u0026#34;) # 可利用註解查看效果 os.chmod(\u0026#34;test.txt\u0026#34;, 0o777) 結果 修改前與修改後的檔案變化如下，我們可以看見檔案權限的變化。\nReference https://www.runoob.com/python/os-chmod.html ","date":"2021-02-08T17:58:17+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/Screenshot-from-2020-12-29-16-01-17.png","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/python-chmod/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #4】在 python 中利用 os.chmod 更改檔案的權限 (chmod 777)"},{"categories":["111 - Python 基礎語法"],"content":"前言 這個算是比較fancy的功能，有時為了排版漂亮、或邏輯已經很簡單，\n不需要撰寫多行程式碼時，才會使用。\nSample Code 使用方法 \u0026gt;\u0026gt;\u0026gt; \u0026#39;true\u0026#39; if True else \u0026#39;false\u0026#39; \u0026#39;true\u0026#39; \u0026gt;\u0026gt;\u0026gt; \u0026#39;true\u0026#39; if False else \u0026#39;false\u0026#39; \u0026#39;false\u0026#39; 也就是說，中間放判斷式，if前面放成立，else後面放不成立 範例 a = 10 ans = 1 if a \u0026gt;= 0 else -1 如果 a \u0026gt;= 0，ans = 1 如果 a \u0026lt; 0，ans = -1 Reference https://cloud.tencent.com/developer/ask/59821 ","date":"2021-02-07T10:40:20+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/basic-syntax/python-one-line-if-else/","tags":["Python","Python 檔案處理","基礎語法"],"title":"【Python 基礎語法 #1】python 一行 if else 語法範例 (one line if else)"},{"categories":["934 - 心理學筆記"],"content":"前言 這裡是我閱讀心理學一些文章的筆記。\n馬太效應 說明 - 「強者越強，弱者越弱；多的越多，少的越少」 指的是表現好的人通常會越好、表現壞的人通常會越壞 (一種強者越強的感覺) 「好的愈好，壞的愈壞，多的愈多，少的愈少」的現象。 思考看看: 社會上是不是也常見「富者越富，貧者越貧」的感覺?\n有名的人會越有名，有人緣的人會越來越有人緣，有錢會越有錢\u0026hellip;\n改變弱者的心態 如果自己對於賺錢的心態，永遠都用一種「窮者思維」去投資，恐怕難以擺脫貧窮\n嘗試改變思維，例如去培養「富者思維」，有機會透過改變心態來破解這樣的效應。\nReference https://wiki.mbalib.com/zh-tw/%E9%A9%AC%E5%A4%AA%E6%95%88%E5%BA%94 ","date":"2021-02-05T20:42:31+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/psychology/matthew-effect/","tags":["心理學筆記"],"title":"【心理學筆記】馬太效應 -「強者越強，弱者越弱；多的越多，少的越少」"},{"categories":["923 - Windows","360 - Linux 網路遠控"],"content":"前言 sshfs 在 linux 系統中是一個非常方便的功能，\n透過 sshfs 連接遠方的系統磁碟，結果就像是直接在電腦中多了可使用的硬碟一樣。\n準備工作 windows 會比 linux 系統稍微麻煩一些 (畢竟一行指令就搞定)，\n先下載以下三樣東西，找不到下載位置以下會有說明。\nsshfs-win：https://github.com/billziss-gh/sshfs-win/releases winfsp：https://github.com/billziss-gh/winfsp/releases SSHFS-Win Manager (GUI視覺化介面)：https://github.com/evsar3/sshfs-win-manager/releases sshfs-win https://github.com/billziss-gh/sshfs-win/releases\n進入後，往下找到下圖的部份，\n點選下載 sshfs-win-3.5.20357-x64.msi (現在電腦大部份都是64位元，32位元版本的電腦請下載x86版本)\nwinfsp https://github.com/billziss-gh/winfsp/releases\n進入後，按下 DOWNLOAD WINFSP 即可下載\nSSHFS-Win Manager (GUI視覺化介面) https://github.com/evsar3/sshfs-win-manager/releases\n進入後，往下找到下圖的部份，\n點選下載 sshfs-win-manager-setup-v1.2.1.exe 建立連結 點選右側，建立新的連線 連線介面 連線完成 按下，讓連線連接上就完成囉！\nReference https://blog.csdn.net/xieqiaokang/article/details/109557482 https://blog.gtwang.org/linux/sshfs-ssh-linux-windows-mac-os-x/ ","date":"2021-02-04T14:36:53+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/Screenshot-from-2021-02-08-14-32-39.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/windows/windows-sshfs/","tags":["Linux","sshfs","網路遠控"],"title":"【windows】在 windows 中使用 sshfs 連線至遠方磁碟"},{"categories":["360 - Linux 網路遠控"],"content":"前言 剛開始學習終端機時，我們會很經常用到 ip 來與不同電腦溝通，\n由於終端機沒有任何的 GUI (視覺化介面)，\n我們需要借助 scp 來幫助我們傳輸檔案，也就是這篇文章主要在教學的內容，\n此外，我們也會教學如何利用 ifconfig 來查詢 ip，掌握 ip 才是對外溝通的第一步驟。\n查詢 ip 最簡單的方式就是：\nifconfig 我們的ip位置就在框起來的地方。\n運用 scp 傳送檔案 scp 是一個我們在電腦與電腦傳送資料的功能\n使用方法： scp [起始位置] [目標位置] 光是這樣的指令用法就能無限多種了，\n最基本的，我們可以從我們自己電腦傳資料到對方電腦。\n注意：這邊是在同一個網域下進行傳輸示範，如果不是同網域需要再修改目標位置\n範例一：從我的電腦傳資料到對方電腦 scp data.txt ubuntu@192.168.2.100:/home/ubuntu/Desktop 在這個指令中，我們將 data.txt 傳出去給對方的電腦\ndata.txt：我們傳的檔案 ubuntu@192.168.2.100：對方電腦的使用者名稱＠對方電腦ip 記得在路徑前面要多一個冒號「:」 /home/ubuntu/Desktop : 要傳到的對方電腦路徑 範例二：從對方電腦傳資料到我的電腦 scp ubuntu@192.168.2.100:/home/ubuntu/Desktop/data.txt . 在這個指令中，我們將 data.txt 從對方的電腦的桌面拉了過來\n(注意：對方電腦桌面必須要真的有這個檔案，不然會有error)\nubuntu@192.168.2.100：對方電腦的使用者名稱＠對方電腦ip 記得在路徑前面要多一個冒號「:」 /home/ubuntu/Desktop/data.txt：對方電腦的檔案 「.」：注意後面空白後還有一個點，代表的是「當前路徑的位置」 小補充：如果要查詢「當前路徑的位置」，可以使用 「pwd」這個指令哦！\n","date":"2021-02-03T11:12:55+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/ifconfig-1024x726.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/ifconfig-ip/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #11】利用 ifconfig 查詢 ip 並用 scp 傳送資料"},{"categories":["370 - Ubuntu","398 - Linux 問題解決"],"content":"問題描述 這個是嘗試在 container 內顯示出 OpenCV 圖片所出現的錯誤，\n因為 container 內本身通常沒有 GUI 的功能，\n或者也許讀者在使用的環境沒有 GUI，可能該就會跳這樣的錯誤。\n解決方法 在 container 內安裝下述內容即可。\n(不過還要處理 host 能不能顯示的問題，這又是另外一件事情了。)\nsudo apt install libcanberra-gtk-module libcanberra-gtk3-module Reference https://www.itread01.com/content/1546962187.html https://askubuntu.com/questions/342202/failed-to-load-module-canberra-gtk-module-but-already-installed ","date":"2021-02-02T11:46:18+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/ubuntu/canberra-gtk-module/","tags":["Bash","Linux","Ubuntu"],"title":"【ubuntu】問題解決: Gtk-Message: Failed to load module \"canberra-gtk-module\""},{"categories":["360 - Linux 網路遠控"],"content":"ssh# 前言\nVS code 有時候透過 GUI 嘗試 ssh 登入遠端時，\n會碰到密碼輸入很多次都沒辦法成功的現象。\n看到網路上的分享，據說是 VScode GUI 在 ssh 連線時，\n會有密碼沒辦法輸入 / 輸入了「正確密碼」卻顯示「密碼錯誤」的狀況。\nssh authorized_keys 介紹 這裡只簡單介紹概念，\n在密碼學裡面，我們經常使用一組公鑰搭配私鑰的方式來進行配對驗證密碼。\n這裡我們就要進行這樣的操作，比喻來說\n公鑰就是可以給別人的 (類似鎖頭)，\n私鑰我們要自己留著 (類似鑰匙)。\n當公鑰與私鑰配對時，就可以解開密碼鎖。 (這觀念不完全正確，但可以簡易理解我們大概在幹嘛)\n本地端，處理 local (client) 的私鑰 主要我們要產生公鑰與私鑰。記得，公鑰我們等等要給遠端拿著。\nssh-keygen 預設應該會將檔案產生在 ~/.ssh 底下，\n檔名預設為 id_rsa，而 id_rsa.pub 為公鑰\n我們打開 id_rsa.pub ，應該會看到一串 ssh-rsa 開頭的東西，\n我們複製整段內容。\n遠端，remote (server) 放上我們的公鑰 複製剛剛上面我們所說的 ssh-rsa 開頭的文字，\n貼在遠端的 ~/.ssh/authorized_keys 的最後面，\n(如果沒有這個檔案的話，請自己新建一個)\n可能會用到的提示指令 (將內容加至檔案後面)\necho \u0026#34;public_key_string\u0026#34; \u0026gt;\u0026gt; ~/.ssh/authorized_keys vscode 設定 同樣的設定我們也可以直接更改 ~/.ssh/config 的設定檔，\n我們也可以透過 VScode 設定的路徑，直接修改這個設定檔。\n( IdentityFile 指的是 local 私鑰的位置，當我們要管理多組公鑰私鑰時，\n可以修改這個來管理。)\n我們可以從 VScode ssh remote 那邊打開 ~/.ssh/config 的設定檔 Host 192.168.0.1 HostName 192.168.0.1 User ubuntu IdentityFile ~/.ssh/id_rsa 完成！ 到這邊基本上就完成了，測試一下 VScode 連線還需不需要打密碼吧！\nwindows 連線至 mac, linux (處理麻煩的 windows 路徑) step 1. (windows 上) ssh-keygen 先建立我們的公私鑰 ssh-keygen 複製出公鑰 cat .\\.ssh\\id_rsa.pub step 2. (mac 上) 貼上公鑰(.pub) 的內容至 authorized_keys 打開你的 mac, linux，貼上剛剛公鑰(.pub) 的內容 至 「~/.ssh/authorized_keys」中\nvim ~/.ssh/authorized_keys step 3. 完成! 照上面的幾個步驟後，我就已經可以直接免密碼登入囉!\nmac, linux 連線至 windows (處理麻煩的 windows 路徑) windows 的路徑與 linux, mac 都不太一樣\u0026hellip; 我們需要另外設定，\n但有點太麻煩，日後再慢慢更新\nstep 1. 開啟 windows ssh 服務 之前有完整教學了，因為篇幅的關係，這邊直接提供連結：\nhttps://wongwongnotes.com/posts/os-misc/os/windows/windows-ssh/\n可以檢查看看有沒有開啟 windows ssh 服務 (記得要開啟管理員權限的 powershell)\nStart-Service sshd (mac, linux 連線至 windows) step 2. windows 找到 ip (hostname 目前研究到找得到，但還沒有找到能讓 client 端能解析的方法) 找 ip 找 「192.168」 開頭的那個\nipconfig 如果要找 username powershell 輸入\necho $env:USERNAME 或者是使用以下指令 (記得只需要看尾巴，帳號名有點被截斷感覺的部分)\nwhoami 如果要找 hostname (但目前研究到此，client 端仍無法解析這個 hostname) powershell 輸入\nhostname 先嘗試 ssh 連線看看 ssh username@ip # 目前碰到無法解析 hostname 的問題， # 無法使用 ssh username@hostname 發現已經可以連線了! 這是一件好事!\n注意，如果這個步驟還不能連線，應該要先檢查為什麼不能連線，再繼續往下做\n(windows 作為 server) windows 設定 authorized_keys 請將 authorized_keys 放置於以下路徑\nC:\\Users\\\u0026lt;username\u0026gt;\\.ssh\\authorized_keys (windows 作為 server) windows 額外的安全性設定 先放可以參考的網站如下：\nHow do I find my username and servername for windows SSH server Configuring SSH Public Key Authentication on Windows 可以先觀察 cat \u0026#34;C:\\ProgramData\\ssh\\sshd_config\u0026#34;| Select-String \u0026#34;Authentication\u0026#34; 觀察 PubkeyAuthentication yes 是否有被註解\n修改 sshd_config 修改以下檔案：\nC:\\ProgramData\\ssh\\sshd_config 修改：\n#PubkeyAuthentication yes 取消這行的註解 #StrictModes yes 取消註解，並改為 StrictModes no 目前到這邊還沒試成功，改天有空再繼續試，會再更新\n(windows 安全性比較高，但也真的比較麻煩啊\u0026hellip;)\nReference [教學] 產生SSH Key並且透過KEY進行免密碼登入 使用VSCode Remote透過 SSH 進行遠端開發 Remote SSH: Tips and Tricks How do I find my username and servername for windows SSH server Configuring SSH Public Key Authentication on Windows ","date":"2021-02-02T01:42:21+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/vscode-ssh-authorized_keys/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #10】透過建立 ssh authorized_keys 讓 VScode 繞過輸入 ssh 密碼 or 達成 ssh 免密碼登入"},{"categories":["934 - 心理學筆記"],"content":"前言 這裡是我閱讀心理學一些文章的筆記。\n吊橋效應 說明 - 「一次緊張的心跳加速，搞不好變成了我們誤以為的喜歡」 吊橋效應正如其名，簡單來說就是分別在平地與在吊橋上對男女的實驗，\n實驗結果顯示，在吊橋上的男女更覺得對方很有魅力。\n原理是來自人的錯覺，\n因為在「緊張的環境」，導致「緊張的心跳加速」，\n產生了錯覺「心跳加速的感覺誤以為是對方造成」。\n應用 吊橋效應藉由「適當的緊張感」產生心跳加快，\n容易造成心動的錯覺。\n延伸的應用例如：「看恐怖電影、去遊樂場、鬼屋」 (都是會讓人緊張的場合) 運動後心跳加速的狀態 (一樣是心跳加速可能造成的錯覺) - 「緊張/愛/激動/運動後的心跳加速」，在緊張的場合容易產生混淆 破解 這個就\u0026hellip;只能說多了解就能「見招拆招」吧。\n我對心理學有興趣也是為了去更了解自己的思考模式，\n當知道自己會這樣子思考之後，\n也許就能避免一些誤會產生。\n不過我不否認的是，有時候放棄一下理性主導，\n享受在感性主導的過程也沒什麼不好的啊XDD\n不然整天都理性的過活，死板板的不會覺得太無趣了嗎XDD\nReference https://wiki.mbalib.com/zh-tw/%E5%90%8A%E6%A1%A5%E6%95%88%E5%BA%94\nhttps://www.youtube.com/watch?v=DiOF2p_XTYA\u0026list=LL\u0026index=2\u0026ab_channel=%E7%B6%AD%E6%80%9D%E7%B6%ADWeisWay\nhttps://www.youtube.com/watch?v=EOFqzbt4VQk\u0026t=1s\u0026ab_channel=%E9%98%BF%E7%95%AB\n","date":"2021-02-01T15:24:07+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/psychology/suspension-bridge-effect/","tags":["心理學筆記"],"title":"【心理學筆記】吊橋效應 -「一次緊張的心跳加速，搞不好變成了我們誤以為的喜歡」"},{"categories":["360 - Linux 網路遠控"],"content":"前言 這篇的需求來自於，因為我在遠端的樹莓派使用了幾個 port 建立的幾個網頁，\n但畢竟我不常在樹莓派上面開發，因此會想要把遠端的 port 拉至 local 的 port 來顯示，\n簡單舉例，就是把遠端的 Port 8888，拉至 local 電腦的 Port 8888 一樣能夠顯示出對應的網頁。\n常見的 8888 應用於 jupyter notebook，我們可以透過這方式建立一個 remote 可使用的 jupyter notebook。\n這篇紀錄我達成這個需求的方式。\n這篇文的前一篇文章，有講到重複的許多觀念，可參考：\nhttps://wongwongnotes.com/posts/linux-shell/system-network/linux/ssh-config-ssh-authorized_keys/\nssh config 進階設定 在前一篇文章中，我們已經有提到如何修改一些 ssh config 的內容，\n這邊我們也可以剛好來整理一下，\nHost rpi HostName localhost User pi Port 9002 IdentityFile ~/.ssh/id_rsa LocalForward 5000 127.0.0.1:5000 LocalForward 5001 127.0.0.1:5001 LocalForward 5002 127.0.0.1:5002 我們可以觀察一下這份 config 的內容，\nHost rpi：我取的暱稱，方便我 ssh rpi 即可連線 HostName localhost：因為我是使用 anydesk 建立連線，我要連線的 port 為 9002 (對應遠端的 ssh port 22) User pi：使用者名稱 pi Port 9002：在 anydesk 中設定我要連線的 port 為 9002 (對應遠端的 ssh port 22) IdentityFile ~/.ssh/id_rsa：透過 authorized_keys 的公私鑰，達到免密碼登入 LocalForward 5000 127.0.0.1:5000：port forwarding (5000 -\u0026gt; localhost:5000) LocalForward 5001 127.0.0.1:5001：port forwarding (5001 -\u0026gt; localhost:5001) LocalForward 5002 127.0.0.1:5002：port forwarding (5002 -\u0026gt; localhost:5002) 快速建立遠端金鑰 注意：下面指令是建立在遠端 ~/.ssh/authorized_keys 不存在的時候才可以使用！！！ scp id_rsa.pub rpi:~/.ssh/authorized_keys # 個人筆記使用，不懂在幹嘛請別亂用，會出事 請注意如果該檔案已經存在，檔案會被覆寫掉！！！\n稍微說明一下上面在做什麼事情，\n我們透過 scp 把 id_rsa.pub (這是公鑰)，傳到遠端 rpi 主機的 ~/.ssh/authorized_keys 這個路徑與檔案。\nscp error scp: xxxxxx : No such file or directory 個人常見的可能原因，遠端的資料夾路徑沒有被完整的建立。\n例如：想要傳到遠端的路徑 ~/folder/file.txt\n但在遠端的資料夾，「~/」 底下並不存在「folder」這個資料夾就會跳錯。\nReference How to add local forward setting to my ssh config file? 20. 增進 SSH 使用效率 - ssh_config 【VScode】透過建立 ssh authorized_keys 讓 VScode 繞過輸入 ssh 密碼 or 達成 ssh 免密碼登入 【Linux】在 terminal 中尋找 username, hostname 作為 ssh 連線的方式 (以連線到 raspberrypi 為例) [教學] 產生SSH Key並且透過KEY進行免密碼登入 使用VSCode Remote透過 SSH 進行遠端開發 Remote SSH: Tips and Tricks How do I find my username and servername for windows SSH server Configuring SSH Public Key Authentication on Windows ","date":"2021-02-01T01:22:51+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/ssh-config-port-forwarding/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #9】透過 ssh config 設定 port forwarding，透過 local 網頁指定 Port 看到遠方主機對應的 Port 資訊 (內含 ssh config 設定整理)"},{"categories":["398 - Linux 問題解決","370 - Ubuntu"],"content":"前言 碰到以下訊息的解決方法。\n/dev/sda1 contains a file system with errors, check forced. ... Inodes that were part of a corrupted orphan linked list found. /dev/sda1: UNEXPECTED INCONSISTENCY: RUN fsck MANUALLY. (i.e., without -a or -p options) fsck exited with status code 4 The root filesystem on /dev/sda1 requires a manual fsck BusyBox v1.27.2 (Ubuntu 1:1.27.2-2ubuntu3.3) built-in shell (ash) Enter \u0026#39;help\u0026#39; for a list of built-in commands. (initramfs)_ 如圖: 解決方法 會出現此問題的原因是因為磁碟分區導致的錯誤，\n執行以下指令後，會嘗試進行自動修復，再重新開機即可。\nfsck -y /dev/sda1 # 嘗試進行自動修復 sudo mount -a # 測試用，沒有跳出訊息才是正常的!! (有跳出訊息就是有問題) reboot # 重新開機 Reference 修復 Linux 開機出現檔案系統有不一致性 (UNEXPECTED INCONSISTENCY) 的錯誤問題 /dev/sda1 contains a file system with errors, check forced [duplicate] ","date":"2021-01-30T00:38:48+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/%E6%88%AA%E5%9C%96-2021-03-12-%E4%B8%8A%E5%8D%8812.14.54.png","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/linux-fsck-mode/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】linux / ubuntu 問題解決: fsck 修復模式 /dev/sda1 contains a file system with errors, check forced. ... Inodes that were part of a corrupted orphan linked list found. /dev/sda1: UNEXPECTED INCONSISTENCY: RUN fsck MANUALLY. (i.e., without -a or -p options) fsck exited with status code 4 The root filesystem on /dev/sda1 requires a manual fsck BusyBox v1.27.2 (Ubuntu 1:1.27.2-2ubuntu3.3) built-in shell (ash) Enter 'help' for a list of built-in commands. (initramfs)_"},{"categories":["418 - Git 問題解決"],"content":"前言 這篇是git上傳時，\n碰到以下訊息的解決方法。\nYou\u0026#39;ve added another git repository inside your current repository. 解法 簡單來說此問題是因為在git資料夾底下中，\n已經存在一個已經建立git的資料夾，\n導致git的指令出錯。\n我們必須去資料夾底下找尋已經起始 git 的「.git」資料夾 (通常是隱藏的資料夾)，\n刪除此資料夾後即可正常執行 git 上傳。\n示意圖 git 資料夾 (資料夾內) 曾經 git init 過的資料夾 (必須刪除此 .git 資料夾) Reference https://blog.csdn.net/bbtang5568/article/details/82791750 ","date":"2021-01-29T12:13:32+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/version-control/git/git-current-repository/","tags":["Git"],"title":"【Git】問題解決: You've added another git repository inside your current repository."},{"categories":["450 - ffmpeg"],"content":"前言 有時候我們會需要印影片的 frame number 到影片上，\n除了 OpenCV 的解法之外，我們也可以用 ffmpeg 的 drawtext 來快速完成。\nSample code ffmpeg -i test_video.mp4 -vf \u0026#34;drawtext=fontfile=/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf: text=%{n}: x=50: y=50: fontsize=100: fontcolor=white\u0026#34; -y ./output_video.mp4 字體： (請替換成自己電腦存在的字體) fontfile=/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf - 位置：x=50, y=50 - 字體大小：fontsize=100 - 字體顏色：fontcolor=white - 輸出影片位置與檔名：./output_video.mp4 Reference https://superuser.com/questions/542989/getting-the-video-frame-number-in-vlc https://hhsprings.bitbucket.io/docs/programming/examples/ffmpeg/drawing_texts/drawtext.html ","date":"2021-01-28T17:26:38+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/ffmpeg-drawtext/","tags":["ffmpeg"],"title":"【ffmpeg】利用 ffmpeg 壓上影片的 frame number(drawtext)"},{"categories":["922 - Mac / MacOS","370 - Ubuntu","620 - 作業系統 OS"],"content":"前言 這篇這別把 「相對路徑」與「絕對路徑」的概念拿出來講，\n因為不論是對於系統操作或是程式設計上，\n有「相對路徑」與「絕對路徑」是非常重要的！！！\n基礎路徑知識，簡易理解「相對路徑」與「絕對路徑」 「相對路徑」與「絕對路徑」的觀念個人認為非常重要！！！\n如果這個概念沒搞清楚，輕則找不到檔案而已，似乎還好。\n但重則可能會導致刪除整台電腦所有東西！！！這樣還不重要嗎！\n以下內容使用此範例：假設有一張圖片的路徑在：「/home/ubuntu/Desktop/test_result/out.jpg」\n相對路徑 「.」表示目前我們所在的路徑，以上面的例子，\n假設我們目前所在的路徑為桌面(/home/ubuntu/Desktop，可以用「pwd」查看)\n以上面為例，圖片路徑就是 「./test_result/out.jpg」\n我們從(桌面)這個路徑底下找 test_result 這個資料夾中的 out.jpg。\n就會是我們儲存圖片的地方。\n絕對路徑 絕對路徑就是檔案完整的路徑，也就是「/home/ubuntu/Desktop/test_result/out.jpg」\n這種寫法非常的明確，但相對的缺點就是彈性非常的差，\n如果今天將我們的 code 換了一台電腦執行，\n甚至只把資料夾移動到另一個地方，絕對路徑馬上就找不到我們要的檔案了。\n所以建議大家還是多使用「相對路徑」哦~\n雖然我剛學的時候也覺得「絕對路徑」真的很明確很棒XDDD\n","date":"2021-01-27T11:56:40+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/os-file-path/","tags":["Bash","Linux","OS","Ubuntu","macOS","作業系統"],"title":"【Linux】(作業系統基礎知識) 簡單理解「相對路徑」與「絕對路徑」，程式設計 初學者/新手 必須知道的路徑知識總整理"},{"categories":["934 - 心理學筆記"],"content":"前言 這裡是我閱讀心理學一些文章的筆記。\n首因效應 說明 - 「一個人的第一印象，能大幅的左右我們往後的命運」 我們第一次遇到一個人、一個品牌，\n往往就會對這件事物有很大的刻板印象。\n以下只是舉例，這些都是在社會上可見的例子：\n當我們經過一家店，覺得這家店很髒，很自然會覺得這家店食物不好吃 當我們遇到一個人穿著很邋遢，就會覺得這個人一定很窮 當我們看到一個人學歷不好，就會覺得這個人一定不太會做事情 當我們看到一個人在特定領域成績亮眼，就會覺得這個人一定所有事情都很完美 我們能知道第一印象，往往能左右很長一段時間別人對你的觀感。\n破解 這邊我用破解這個詞並不是說很好 (不過反正只是我的筆記就算了XD)，\n我會想要盡量避免用「第一印象」去評斷一個人。\n例如：「往往公司HR看人學歷，去評斷一個人的實力」這類的事情，\n在我自己的身旁就有太多實力比學歷耀眼太多的人了!\n(當然也不否認學歷跟實力兼具的也有很多)\n「不要在自己根本沒有了解對方前，就以自己的判斷去評斷一個人」\n對我來說，這才是與每個人接觸時更重要的。\nReference https://medium.com/harlyblog/%E7%AC%AC%E4%B8%80%E5%8D%B0%E8%B1%A1%E6%B1%BA%E5%AE%9A%E4%B8%80%E7%94%9F-%E7%94%9A%E9%BA%BC%E6%98%AF%E9%A6%96%E5%9B%A0%E6%95%88%E6%87%89-primary-effect-a2a67dca44b1\nhttps://www.youtube.com/watch?v=Mc8Ne2_srCc\nhttps://www.youtube.com/watch?v=TQMiGR0-gk4\u0026t=38s\n","date":"2021-01-25T01:22:10+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/psychology/primary-effect/","tags":["心理學筆記"],"title":"【心理學筆記】首因效應 -「一個人的第一印象，能大幅的左右我們往後的命運」"},{"categories":["650 - 計算機概論"],"content":"前言 此為個人 ISO standards 個人常考重點總整理的筆記\n(僅整理常考的與對應的重點，不代表只會考這些)\nISO standards 筆記 ISO 筆記 ISO 9000 Quality management ISO 14000 environment management ISO 22000 food safety ISO 26000 social management ISO 27001 information management ISO 31000 risk management ISO 37001 Anti-bribery management ISO 50001 energy management Reference https://www.iso.org/standards.html ","date":"2021-01-24T14:58:00+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/intro-to-cs/intro-to-cs/iso-standards/","tags":["Python","計算機概論"],"title":"【計算機概論】ISO standards 筆記 個人常考重點總整理 大全 9000 14000 22000 26000 27001 31000 37001 50001"},{"categories":["360 - Linux 網路遠控"],"content":"前言 當要登入的電腦越來越多，\n有時候就會有想要給主機取代稱的需求，\n這篇我們主要講\n替主機取匿名 透過 ssh authorized_keys 公私鑰，達成免密碼登入 這篇文的前一篇文章，有講到重複的 ssh authorized_keys 概念，可參考：\nhttps://wongwongnotes.com/posts/linux-shell/system-network/linux/vscode-ssh-authorized_keys/\nssh authorized_keys 介紹 這裡只簡單介紹概念，\n在密碼學裡面，我們經常使用一組公鑰搭配私鑰的方式來進行配對驗證密碼。\n這裡我們就要進行這樣的操作，比喻來說\n公鑰就是可以給別人的 (類似鎖頭)，\n私鑰我們要自己留著 (類似鑰匙)。\n當公鑰與私鑰配對時，就可以解開密碼鎖。 (這觀念不完全正確，但可以簡易理解我們大概在幹嘛)\n本地端，處理 local (client) 的私鑰 主要我們要產生公鑰與私鑰。記得，公鑰我們等等要給遠端拿著。\nssh-keygen 預設應該會將檔案產生在 ~/.ssh 底下，\n檔名預設為 id_rsa，而 id_rsa.pub 為公鑰\n我們打開 id_rsa.pub ，應該會看到一串 ssh-rsa 開頭的東西，\n我們複製整段內容。\n遠端，remote (server) 放上我們的公鑰 複製剛剛上面我們所說的 ssh-rsa 開頭的文字，\n貼在遠端的 ~/.ssh/authorized_keys 的最後面，\n(如果沒有這個檔案的話，請自己新建一個)\n可能會用到的提示指令 (將內容加至檔案後面)\necho \u0026#34;public_key_string\u0026#34; \u0026gt;\u0026gt; ~/.ssh/authorized_keys ssh config 設定 同樣的設定我們也可以直接更改 ~/.ssh/config 的設定檔，\n我們也可以透過 VScode 設定的路徑，直接修改這個設定檔。\n( IdentityFile 指的是 local 私鑰的位置，當我們要管理多組公鑰私鑰時，\n可以修改這個來管理。)\n我們可以從 VScode ssh remote 那邊打開 ~/.ssh/config 的設定檔 或是直接編輯\nvim ~/.ssh/config 修改以下內容：\nHost 192.168.0.1 HostName 192.168.0.1 User ubuntu IdentityFile ~/.ssh/id_rsa 進階：取主機暱稱 剛剛上面我們已經完成的內容，我們可以快速地與遠端主機建立連線，\n但這樣還不夠方便，當我們如果有很多主機要管理的時候，\n就會碰到不夠方便的情況 (要一直瘋狂的記憶 ip)。\n但當然有方便管理的方式，\n我們可以替主機取我們自己想要的暱稱，\n這樣我們就可以快速連線了！\n修改也十分簡單，照上面的內容，我們可以修改成：\nHost myremote HostName 192.168.0.1 User ubuntu IdentityFile ~/.ssh/id_rsa 這樣，我們以後只要輸入 ssh myremote，就可以快速地進行連線囉！\n進階2：搭配 hostname 運用，看起來更俐落！ 在我的另外一篇文章當中，有提到如何運用 hostname 進行遠端主機連線，\nhttps://wongwongnotes.com/posts/linux-shell/system-network/linux/linux-terminal-username-hostname/\n搭配上述的概念，我們就可以把 ssh config 修改成：\nHost myremote HostName \u0026lt;hostname\u0026gt; User \u0026lt;username\u0026gt; IdentityFile ~/.ssh/id_rsa 這樣就可以輕鬆地進行 ssh 連線囉！\nReference 【VScode】透過建立 ssh authorized_keys 讓 VScode 繞過輸入 ssh 密碼 or 達成 ssh 免密碼登入 【Linux】在 terminal 中尋找 username, hostname 作為 ssh 連線的方式 (以連線到 raspberrypi 為例) [教學] 產生SSH Key並且透過KEY進行免密碼登入 使用VSCode Remote透過 SSH 進行遠端開發 Remote SSH: Tips and Tricks How do I find my username and servername for windows SSH server Configuring SSH Public Key Authentication on Windows ","date":"2021-01-24T00:41:29+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/ssh-config-ssh-authorized_keys/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #8】修改 ssh config 取主機暱稱 | 透過 ssh authorized_keys 公私鑰，達成 ssh 免密碼登入"},{"categories":["360 - Linux 網路遠控"],"content":"前言 「ssh 連線」是個極常被使用於伺服器連線的功能，\n在此有幾個方便的方式，可以幫助我們更容易的使用它，\n就是找到主機的 username 與 hostname !\n遠端的部分 (這裡用 raspberrypi 示範) 從遠端找到我們的 username 與 hostname，\n對應的指令為\n找到 username whoami 找到 hostname hostname # 你沒有看錯，就是 hostname local 的部分 在我們取得 username 與 hostname 後，\n我們就可以直接使用 ssh hostname 進行快速的 ssh 連線。\ndefault hostname 連線 ssh \u0026lt;hostname\u0026gt; 使用特定 username + hostname 連線 但有時候可能會碰到 default username 跟我們想像的情況不一樣的時候，\n這時候我們就會需要用到「我們剛剛找到的 username」。\n指令就會變成\nssh \u0026lt;username\u0026gt;@\u0026lt;hostname\u0026gt; 小提醒 在沒有 hostname 的情況下，我們預設都是直接打對應的 ip 去找，\n不過其實 ssh 很聰明，我們一般不用特別打 ip，而且 hostname 通常也比較好記。\n但是因為「不同的電腦，可能會有一樣的 hostname」，\n這時候 ip 就會派上用場了！\nssh \u0026lt;username\u0026gt;@\u0026lt;ip\u0026gt; Reference Find a Computer\u0026rsquo;s Hostname How do I get the current user\u0026rsquo;s username in Bash? ","date":"2021-01-23T01:53:42+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/%E6%88%AA%E5%9C%96-2022-08-21-%E4%B8%8B%E5%8D%882.01.51-1-1024x418.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/linux-terminal-username-hostname/","tags":["Bash","Linux","Ubuntu","網路遠控"],"title":"【Linux 網路遠控 #7】在 terminal 中尋找 username, hostname 作為 ssh 連線的方式 (以連線到 raspberrypi 為例)"},{"categories":["360 - Linux 網路遠控"],"content":"前言 X11 forward 是一種將畫面透過 ssh 從「遠端」顯示至「本地端」的技術\n設定遠端 修改 ssh_config - 1 sudo vim /etc/ssh/ssh_config 修改以下內容： ForwardX11 yes 修改 sshd_config - 2 sudo vim /etc/ssh/sshd_config 修改以下內容： AllowTcpForwarding yes X11Forwarding yes X11UseLocalhost yes 設定本地 安裝 ssh，設定 xhost\nxhost 表示允許 X 繪圖指令能傳入\n通常使用方式：\nxhost +HostName HostName: 遠端伺服器 如果不知道，可以直接允許全部 (懶人專用、但不安全)\nxhost + 連線 ssh -Y pi@192.168.2.133 ssh -Y ：信任的 X11 forwarding 連線 (trusted X11 forwarding) ssh -X ：不受信任的 X11 forwarding 連線 (untrusted X11 forwarding) 檢查畫面顯示位置 echo $DISPLAY 成功顯示範例： localhost:11.0 Reference xhost 和 DISPLAY X11 Forwarding SSH Connecting remotely to Raspberry Pi over the network using SSH (and X11 forwarding) ssh -Y (trusted X11 forwarding) and 「ssh -X」 (untrusted X11 forwarding)?\u0026quot;\u0026gt;What is the difference between 「ssh -Y」 (trusted X11 forwarding) and 「ssh -X」 (untrusted X11 forwarding)? ssh -X), get 「Can\u0026rsquo;t open display」 trying to run X applications\u0026quot;\u0026gt;With SSH X11 forwarding (「ssh -X」), get 「Can\u0026rsquo;t open display」 trying to run X applications Use X forwarding on a personal computer to securely run graphical applications installed on IU\u0026rsquo;s research supercomputers Raspberry Pi OpenCV 2.4.5 編譯安裝 ","date":"2021-01-22T23:31:38+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/linux-x11-forward/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #6】在 Linux 中 設定 X11 forward ssh 使遠端畫面能顯示在本地端"},{"categories":["115 - Python 檔案處理"],"content":"前言 我們有時在進行檔案操作時，\n會需要知道檔案/資料夾是否存在，\n此時就可以用這樣的方式來偵測。\n先備知識 - 「相對路徑」與「絕對路徑」的概念 「相對路徑」與「絕對路徑」的概念可以參考 - 這篇文章：\nhttps://wongwongnotes.com/posts/os-misc/os/macos/os-file-path/\n這裡就不再多加贅述了。\n範例 - 檢查檔案 os.path.isfile import os file_path = \u0026#34;./test.txt\u0026#34; result = os.path.isfile(file_path) print(result) 結果 如果檔案存在，則回傳 True\n如果檔案不存在，則回傳 False\n範例 - 檢查資料夾 os.path.isdir import os folder_path = \u0026#34;./test/\u0026#34; result = os.path.isdir(folder_path) print(result) 結果 如果資料夾存在，則回傳 True\n如果資料夾不存在，則回傳 False\nReference https://blog.gtwang.org/programming/python-howto-check-whether-file-folder-exists/ ","date":"2021-01-22T11:39:56+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/python-isfile-isdir/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #3】確認檔案是否存在 os.path.isfile / 確認資料夾是否存在 os.path.isdir is folder / file exist"},{"categories":["360 - Linux 網路遠控"],"content":"前言 我們要遠端開發的時候，最經常會需要的東西就是 VPN，\n透過 VPN 能實現兩個不同網域能在同一個區網下的概念。\nAnydesk 或是 teamviewer 強大的地方就在於，各種網路的跳板，\n他們都已經幫我們實現了，這樣我們就可以簡單地實現不同區網間的電腦連線。\n現在我們想基於 Anydesk 這個方便的機制，以 Anydesk 作為 VPN 跳板，\n直接與遠端主機進行 ssh 連線。\n補充：在某些遊戲中，我們會使用類似 hamachi 或 gcc LAN (已關閉服務)，\n達到虛擬區網的效果。\n(共通步驟) step 0. 建立連線 將我們本機端的連線 port 設為 9000 (自訂，可更改) 遠端主機的 port 設定為 22 (ssh 固定 port，不可更改) 透過 ssh 連至 host step 1. ssh 連線至遠端 terminal 在「anydesk 已建立連線」的狀態下 (可以只單純使用檔案傳輸模式)，\n在終端機輸入以下指令。\nssh -p 9000 ubuntu@localhost 透過 VScode 連線至遠端 host step 1. 使用 VScode ssh 連線至遠端 (有 GUI 介面) 打開 VScode 左下角的遠端連線 (需要先安裝 ssh 相關的套件)\n輸入以下內容，即完成連線： ubuntu@localhost:9000 示意圖： 透過 VScode 連線至遠端 container step 1. terminal 建立 ssh 反向通道，使本地可查看遠端 docker 內容 ssh -p 9000 -nNTL localhost:23750:/var/run/docker.sock ubuntu@localhost step 2. VScode 設定的部分 基本的 docker 擴充功能必須先安裝好，\n而且 local 也需要具備有 docker 的功能，\n如果不知道如何在 windows 中啟用 docker 功能可以參考這篇：\nhttps://wongwongnotes.com/posts/dev-tools/containers/docker/docker-windows/\nstep 2.1 打開「設定」，我們準備進行修改 我們打開並修改設定「settings.json」，\n點選「檔案」-\u0026gt;「喜好設定」中的「設定」。\nstep 2.2 在上方搜尋「settings.json」(不用全輸入)，打開「settings.json」，我們準備進行修改 或者也可以點擊右上角的符號，也可以叫出 「settings.json」 step 2.3 在「settings.json」中加入遠端 container port 的設定 在「settings.json」中加入這行\n\u0026#34;docker.host\u0026#34;: \u0026#34;tcp://localhost:23750\u0026#34; 示意圖： 只需要確保有加入紅框那行即可，\n其他行代表的是其他設定，可以不用管。\n完成結果 我們可以在 local 自己的 VScode，\n可以透過「Docker extension」直接看到遠端 container 內的資料。\n(注意：不需要再另外經由 VScode 的 ssh 至遠端)\n會顯示遠端的 container Reference 【Docker】利用 VScode 透過 ssh tunnel 直接連線到遠端 Docker 的 container 中進行開發 SSH through TCP tunneling ","date":"2021-01-20T12:01:45+08:00","image":"https://wongwongnotes.com/images/restored/2021/10/anydesk-vpn.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/vpn-anydesk-ssh/","tags":["Linux","sshfs","網路遠控"],"title":"【Linux 網路遠控 #5】將 anydesk 作為 VPN 跳板 (建立 tcp tunnel)，使用 ssh 進行遠端連線開發 (anydesk ssh command line)"},{"categories":["360 - Linux 網路遠控"],"content":"前言 sshpass 是一個讓 ssh 可以免密碼登入的套件\n(嚴格來講是，將密碼直接打在 terminal 上，直接讓 sshpass 自動輸入)\n可想而知，這方法超級不安全的 (會有資安問題)，請慎用！！！\n而且密碼是「明碼輸入」，請務必慎用！！！\n(要更有效率，就要犧牲一點安全的感覺?)\n當然還有更安全的方式，之後會分享，但就麻煩很多就是\u0026hellip;.\nubuntu 安裝 sshpass sudo apt-get install sshpass mac 安裝 sshpass mac 安裝 sshpass 比較麻煩，步驟比較多\n使用 Homebrew 安裝 個人使用這個方式是失敗的\nbrew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb 使用 Source Code 安裝 個人是用這個方法才成功\n下載 Source Code，並解壓縮進入資料夾 Source Code：[Non-interactive ssh password auth](https://sourceforge.net/projects/sshpass/) - 執行以下指令 ./configure sudo make install 使用方式 sshpass -p \u0026lt;my_password\u0026gt; ssh \u0026lt;username\u0026gt;@\u0026lt;hostname\u0026gt; 如果搭配 alias，並寫在 bashrc 或 zshrc 內，效果更佳！\nReference 利用 sshpass 幫你輸入 ssh 密碼 How to install sshpass on Mac? Installing SSHPASS Non-interactive ssh password auth ","date":"2021-01-19T19:59:05+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/linux-mac-sshpass/","tags":["Bash","Docker","Linux","Ubuntu","網路遠控"],"title":"【Linux 網路遠控 #4】ubuntu/mac 安裝 sshpass，讓 ssh 可以免密碼直接登入，使用的效率更高"},{"categories":["198 - Python 問題解決"],"content":"前言 這是我之前在研究利用 cython 來做 python加密，\n發現當 python 執行時發生以下問題的解決辦法。\nerror: no commands supplied 解決方法 此為 cython 指令執行時，flags 沒有正確被下的問題，\n(下面 Reference 的解法剛好不適用於本人碰到的情況)\n實際上的指令執行程式碼：\npython setup.py build_ext --inplace 而我之前執行時只有下： (這是錯的！！！)\npython setup.py Reference https://www.itread01.com/content/1545101537.html https://superuser.com/questions/222957/no-commands-supplied-error-when-executing-a-python-script-on-ubuntu/222958 ","date":"2021-01-19T17:54:53+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/python-no-commands-supplied/","tags":["Python"],"title":"【Python】問題解決：error: no commands supplied"},{"categories":["360 - Linux 網路遠控"],"content":"前言 sshfs 在 linux 系統中是一個非常方便的功能，\n透過 sshfs 連接遠方的系統磁碟，結果就像是直接在電腦中多了可使用的硬碟一樣。\n如何使用 先建立一個連接用資料夾，「注意資料夾權限」 因為我們等等要連接ip「192.168.2.255」的電腦，\n我就先在我的電腦上建立一個路徑「/mnt/Desktop_255」作為等等連接用，\n並給予足夠的權限。\nmkdir /mnt/Desktop_255 # 建立資料夾路徑 chmod -R 777 /mnt/Desktop_255 # 給予足夠權限，不一定要777 連接遠方位置 sshfs ubuntu@192.168.2.255:/home/ubuntu/Desktop/ /mnt/Desktop_255 其中：\nubuntu：對方電腦的使用者帳戶名稱 192.168.2.255：對方電腦的 ip /home/ubuntu/Desktop/：對方電腦的硬碟路徑 (記得前面要加冒號「:」) /mnt/Desktop_255：我方電腦的連接位置 我們就會在我們電腦上的「/mnt/Desktop_255」這個位置，看到「/home/ubuntu/Desktop/」裡面的全部檔案囉！\n此外，我們也可以直接進行檔案的修改，在對方的電腦上也會更新，就像是在自己的電腦上操作一樣！\n注意！！！！！ 有時候當連接失敗時 (資料夾會長的怪怪的)，有可能是權限步驟未完成導致，\n此時請務必先做「取消連接」的動作再刪除資料夾，\n否則其實是有連接上，只是顯示錯誤的內容，\n會因為移除資料夾的關係「把遠端的資料全部移除」！！！\n取消連接 我們可以使用 umount 指令取消連接\nsudo umount /mnt/Desktop_255 ","date":"2021-01-18T17:40:18+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/linux-sshfs/","tags":["Linux","sshfs","網路遠控"],"title":"【Linux 網路遠控 #3】使用 sshfs 連線 將遠方磁碟當作像是在自己電腦的硬碟 (mount disk on other computer)"},{"categories":["360 - Linux 網路遠控"],"content":"前言 我們使用習慣了像 windows, mac 或是任何有 GUI 介面的作業系統，\n但當我們沒有 GUI 時，該如何透過終端機 (terminal) 來下載檔案呢？\n下載檔案的方式 知道要下載的網址 首先對著你想下載的檔案按右鍵，複製就可以得到我們要的網址。\n對於可以下載的連結，右鍵選擇「複製連結網址」\n下載檔案 在終端機(terminal) 輸入以下指令，就可以下載囉！\ncurl -O [貼上要下載的網址] Reference https://blog.techbridge.cc/2019/02/01/linux-curl-command-tutorial/ ","date":"2021-01-17T12:12:16+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/%E6%88%AA%E5%9C%96-2021-03-17-%E4%B8%8B%E5%8D%8812.08.14.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/curl-terminal-download/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #2】使用 curl 將網路上的檔案 透過終端機 terminal 的方式下載 download"},{"categories":["360 - Linux 網路遠控"],"content":"前言 這篇的需求要解決的是，大部分時候我們都會使用 local port 進行開發\n例如：jupyter notebook，通常都會指定到 localhost:8888 這個 port\n但其實一直 local 並不是那麼的方便 (例如：工作環境會被限制在本地電腦上)\n透過 ssh forwarding 可以稍微讓這件事情變得靈巧一些\n具體來說，我們可以在另外一台電腦輸入 localhost:1234，也能夠直接連到目標開發主機的 localhost:8888 這個 port 上進行遠端開發\n實作方法 其實要做程式碼非常簡單，就是一行把 ssh 做 port forwarding 的程式碼而已\nssh -L 5000:localhost:8888 \u0026lt;username\u0026gt;@\u0026lt;remote_ip\u0026gt; 這邊說明一下，我們想用本地主機的 localhost:5000，\n連接上遠端主機的 localhost:8888，\n而遠端主機的 ip 跟 username 為 @\u0026lt;remote_ip\u0026gt;，\n單純在背景連線 通常只輸入上面那行，我們也會連進遠端主機內，如果我們沒有想進行連線的動作，只「單純背景連線」，我們加個 「-N」的 flag 即可。\n多重 port 連接 有時候我們會連接的 port 不只一個，\n例如：5000, 5001, 5002 可能都是在開發的狀態\n這時我們多下幾個 「-L」的 flag 就可以實現多重 port 連接\nssh -L 9000:localhost:5000 -L 9001:localhost:5001 -L 9002:localhost:5002 \u0026lt;username\u0026gt;@\u0026lt;remote_ip\u0026gt; 上面的結果就會變成：\n本地 9000 -\u0026gt; 遠端 5000 本地 9001 -\u0026gt; 遠端 5001 本地 9002 -\u0026gt; 遠端 5002 Reference ssh -L forward multiple ports SSH Tunneling (Port Forwarding) 詳解 ","date":"2021-01-16T14:00:21+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-network/linux/ssh-use-local-port-to-connect-remote-port/","tags":["Linux","網路遠控"],"title":"【Linux 網路遠控 #1】透過 ssh 使本地 port 連接遠端 port 的內容 (ssh use local port to connect remote port)"},{"categories":["398 - Linux 問題解決"],"content":"前言 碰到以下訊息的解決方法。\nerror: unknown filesystem. Entering rescue mode... grub rescue\u0026gt; 如圖: ![](/images/restored/2021/03/IMG_7020-scaled.jpg) 解決方法 此問題發生的原因是在硬碟拆裝的時候，\n導致 BIOS 的設定跑掉了，\n請進入 BIOS 設定 「legacy mode + UEFI 啟動模式」或 「legacy mode only」 就能正常啟動了！\n","date":"2021-01-16T00:28:54+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/IMG_7020-scaled.jpg","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/linux-grub-rescue/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】linux / ubuntu 問題解決: error: unknown filesystem. Entering rescue mode... grub rescue\u003e grub 救援模式"},{"categories":["450 - ffmpeg"],"content":"【ffmpeg】問題解決: Could not load font \u0026ldquo;Arial.ttf\u0026rdquo;: cannot open resource 此為找不到字體的問題，需要修改路徑，\n如果是 linux系統，路徑位置在\n/usr/share/fonts /usr/local/share/fonts ~/. fonts 範例： Sample Code ffmpeg -i test_video.mp4 -vf “drawtext=fontfile=/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf: text=%{n}: x=50: y=50: fontsize=100: fontcolor=white” -y ./output_video.mp4 其中 /usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf 就是字體路徑的位置 Reference https://blog.csdn.net/rong11417/article/details/104610077 ","date":"2021-01-15T18:23:21+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/could-not-load-font/","tags":["Bash","Linux","Ubuntu","ffmpeg"],"title":"【ffmpeg】問題解決: Could not load font \"Arial.ttf\": cannot open resource linux font path"},{"categories":["450 - ffmpeg"],"content":"前言 有時候我們會需要查詢一部影片的相關資訊，\n例如 fps 或 影片長度等等。\n在 Linux 系統中，\n我們可以使用 ffmpeg -i 的方式顯示影片相關的訊息。\nSample code ffmpeg -i test.mp4 範例結果 我們可以看到這部測試影片是：\n解析度：2688x1512 影片長度： 03:23 ","date":"2021-01-12T12:34:48+08:00","image":"https://wongwongnotes.com/images/restored/2021/01/Screenshot-from-2021-01-08-12-32-24.png","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/ffmpeg-information/","tags":["ffmpeg"],"title":"【ffmpeg】利用 ffmpeg 查詢影片的相關資訊"},{"categories":["340 - Linux 系統控制"],"content":"前言 我們有時買了一台新電腦，當我們懶得把一個又一個的應用程式重裝，\n這是後我們會想是不是能夠直接「複製整個硬碟」就好了呢？\n這一次就是來分享在 linux / ubuntu 上複製硬碟的技術\n事前準備 準備兩顆硬碟，查詢硬碟位置 去 ubuntu 的 menu 打開 disks\n確認新舊硬碟的名稱(不含數字)\n例如下圖中：舊 /dev/sda, 新/dev/sdb\n我們可以看出，這顆硬碟只剩下 88GB for free，也就是這是舊的硬碟\n記得記下來舊的硬碟是 /dev/sda (1代表的是第一個分割，暫時我們先不用管)\n一看就知道是新插的硬碟，\n記得記下來新的硬碟是 /dev/sdb (1代表的是第一個分割，暫時我們先不用管)\n安裝 我們要安裝 gddrescue 這個套件，來幫助我們完成硬碟複製的任務。\nsudo apt-get install gddrescue 複製 從舊的硬碟 /dev/sda 複製到新的硬碟 /dev/sdb 注意：順序一定要搞清楚！！！！ 不然用空的洗掉正常的\u0026hellip; 我也救不了你QQ\nsudo ddrescue -v /dev/sda /dev/sdb --force 等待跑完就完成囉！\n後續處理 \u0026amp; debug 運氣好的話，其實基本上沒有什麼需要後續處理的\n狀況一：用複製硬碟開機後沒有回應 請準備一顆可以開機的硬碟，或者可以開機的 USB，\n因為我有特別準備一個 ubuntu 的安裝碟，\n我使用裡面內建的「Try Ubuntu」的「Live demo」功能。\n我們需要打開一個終端機來進行操作：\n1. 尋找並複製新硬碟的 UUID 這時候可能你已經先把舊的硬碟移除了，\n應該新的硬碟就會成為 /dev/sda\n可以透過以下指令找到該硬碟的 UUID\nls /dev/disk/by-uuid/ -al 2. 尋找並複製新硬碟的 UUID 因為沒有 vim，我們只能使用 gedit\n(如果是其他可開機硬碟，使用 vim 當然也是可以的!)\nsudo gedit /media/ubuntu/XXXXX(你的硬碟的UUID)/etc/fstab 3. 在 /etc/fstab 中加入新 mount 的硬碟，格式如下 UUID=你的 UUID / ext4 errors=remount-ro 0 1 重新開機應該就可以正常開機了! 如果有其他狀況可以繼續往下看!\n狀況二：(ubuntu18) 無法正常進入系統，進入了 fsck 修復模式 如圖： 執行以下指令後，重新開機即可。\nfsck -y /dev/sda1 sudo mount -a # 測試用，沒有跳出訊息才是正常的!! (有跳出訊息就是有問題) reboot # 重新開機 狀況三：(ubuntu18) 無法正常進入系統，進入了 grub 修復模式 如圖： 此問題發生的原因是在硬碟拆裝的時候，\n導致 BIOS 的設定跑掉了，\n請進入 BIOS 設定 「legacy mode + UEFI 啟動模式」或 「legacy mode only」 就能正常啟動了！\nReference https://blog.gtwang.org/linux/linux-mount/ https://kknews.cc/zh-tw/code/o86gazq.html ","date":"2021-01-09T00:24:22+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/IMG_7020-scaled.jpg","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-copy-all-disk/","tags":["Bash","Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #5】linux / ubuntu 複製硬碟的方法"},{"categories":["340 - Linux 系統控制"],"content":"前言 有時候我們使用 terminal 操作電腦或伺服器時，\n沒有滑鼠、沒有GUI 介面供我們手動去點選關機或重新開機指令，\n這時我們就會需要用到這些指令了！\n關機 現在馬上安全的進行關機 # 現在馬上安全的進行關機 sudo shutdown now 重新開機 現在馬上安全的進行重新開機 記法： r = reboot\n# 現在馬上安全的進行重新開機 sudo shutdown -r now # 或者更簡單的 sudo reboot Reference https://www.itread01.com/content/1546955129.html https://opensource.com/article/19/7/reboot-linux https://ithelp.ithome.com.tw/articles/10232157 ","date":"2021-01-08T16:42:54+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-shutdown-reboot/","tags":["Bash","Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #4】linux ubuntu 使用 terminal (終端機) 關機 shutdown、重新開機 reboot 的方法"},{"categories":["520 - 深度學習 Deep Learning","044 - 個人演講 ppt"],"content":"前言 此為我在 Pyladies 演講的講義分享。\n一日 AI 工程師 - Deep learning with PyTorch\n2020-12-19 09:00 ~ 17:00 @ OnrampLab Studio\n活動網址：https://tw.pyladies.com/events/event.html?id=189 Pyladies 簡介 ? Pyladies 是致力於推廣女生學習 python 程式語言的女性技術社群。我初學製作 line chatbot 也是照著 pyladies 的技術文章一步一步累積而成，從四年前我就一直默默關注與欣賞她們。這次真的很榮幸受到邀請能與其他三位大神一起擔任講師！\n講義連結 課程投影片： 課程共筆： FB 相關文章 個人 FB 推薦文： https://www.facebook.com/wongwong0916/posts/10216029207797861 Pyladies 官方 FB 活動文： https://www.facebook.com/pyladies.tw/posts/2827801724155607 ","date":"2021-01-08T01:40:56+08:00","permalink":"https://wongwongnotes.com/posts/ai/deep-learning/deep-learning/pyladies-slides/","tags":["Deep learning","ppt","機器學習","深度學習","Ｍachine learning"],"title":"【講義分享】[機器學習] pyladies 一日 AI 工程師 - Deep learning with PyTorch"},{"categories":["231 - C++ OpenCV"],"content":"前言 此文為 C ++ 在 OpenCV 執行第一隻程式的方法範例\nsample code #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;opencv2/core/core.hpp\u0026gt; #include \u0026lt;opencv2/highgui/highgui.hpp\u0026gt; using namespace cv; using namespace std; int main(){ // generate black image cv::Mat image = cv::Mat::zeros(cv::Size(640, 480), CV_8UC3); // show result cv::imshow(\u0026#34;image\u0026#34;, image); cv::waitKey(0); return 0; } C++ compile g++ test.cpp -o test.out -std=c++11 `pkg-config --cflags --libs opencv` ./test.out test.cpp：為要編譯的程式碼 test.out：為編譯結果的 binary (執行也是用此執行檔執行) 執行結果 Reference https://docs.opencv.org/2.4/doc/tutorials/introduction/linux_install/linux_install.html https://blog.gtwang.org/programming/ubuntu-linux-install-opencv-cpp-python-hello-world-tutorial/ ","date":"2021-01-06T17:37:51+08:00","image":"https://wongwongnotes.com/images/restored/2021/01/Screenshot-from-2021-01-06-17-34-55.png","permalink":"https://wongwongnotes.com/posts/cpp/visual-apps/c-opencv/c-opencv-helloworld/","tags":["C++","C++ OpenCV"],"title":"【OpenCV】c++ OpenCV - 在 ubuntu 上第一次執行 OpenCV 程式"},{"categories":["115 - Python 檔案處理"],"content":"前言 shutil 是 python 的高階檔案操作 package\n範例程式碼 - 複製「檔案」 import shutil src = \u0026#34;./input_path/origin_file.txt\u0026#34; dst = \u0026#34;./output_path/copy_file.txt\u0026#34; shutil.copyfile(src, dst) 然後就會把 src 的檔案複製到 dst 囉！\n範例程式碼 - function 複製「檔案」進「資料夾」 def put_file_in_folder(folder, file): if not os.path.isdir(folder): os.makedirs(folder, mode=0o777) # absolute makedirs print(f\u0026#34;put {file} in to the folder {folder}\u0026#34;) shutil.copy(file, folder) Reference shutil \u0026mdash; 高阶文件操作 https://docs.python.org/zh-tw/3/library/shutil.html ","date":"2021-01-05T12:03:57+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/shutil-copy-file/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #2】利用 shutil 來複製檔案 shutil copy file"},{"categories":["115 - Python 檔案處理"],"content":"前言 我們經常會有需要取出檔名的時候，有時候會只需要不含副檔名的名稱，\n這時我們可以使用以下的方法，取出我們的檔名。\n以下為我們在此文章中命名的方式： filepath = \u0026#39;/home/ubuntu/python/example.py\u0026#39; basename = os.path.basename(filepath) # basename - example.py filename = os.path.splitext(basename)[0] # filename - example 最基本的 split，完全切開 (最直接，不 import 任何套件的想法) split 能依照指定的字元，將所有的內容依照指定內容切開後，\n存成一個list回傳。\nSample code filepath = \u0026#39;/home/ubuntu/python/example.py\u0026#39; result = filepath.split(\u0026#39;/\u0026#39;) 結果：result = [\u0026rsquo;\u0026rsquo;, \u0026lsquo;home\u0026rsquo;, \u0026lsquo;ubuntu\u0026rsquo;, \u0026lsquo;python\u0026rsquo;, \u0026rsquo;example.py\u0026rsquo;] 切的最細，可以搭配 join 使用，組合出自己想要的結果字串。\n【example.py】我只要檔名 basename (含副檔名) os.path.basename Sample code -\u0026gt; example.py filepath = \u0026#39;/home/ubuntu/python/example.py\u0026#39; basename = os.path.basename(filepath) # OR result = os.path.split(filepath)[1] # [\u0026#39;/home/ubuntu/python\u0026#39;, \u0026#39;example.py\u0026#39;] 的第二個欄位 結果：basename = \u0026rsquo;example.py\u0026rsquo; 我要檔名與他的路徑 os.path.split Sample code -\u0026gt; [路徑 + example.py] filepath = \u0026#39;/home/ubuntu/python/example.py\u0026#39; result = os.path.split(filepath) 結果：result = [\u0026rsquo;/home/ubuntu/python\u0026rsquo;, \u0026rsquo;example.py\u0026rsquo;] 【example】我只要檔名，不要他的副檔名 os.path.splitext (需要先取得 basename) Sample code -\u0026gt; example basename = \u0026#39;example.py\u0026#39; filename = os.path.splitext(basename)[0] # OR filename = basename.split(\u0026#34;.\u0026#34;)[0] # 此作法風險：檔案名稱內有其他\u0026#34;.\u0026#34;\u0026#34; 結果：filename = \u0026rsquo;example\u0026rsquo; 與上面合體 filepath = \u0026#39;/home/ubuntu/python/example.py\u0026#39; basename = os.path.basename(filepath) # example.py filename = os.path.splitext(basename)[0] # example Reference https://blog.csdn.net/ziyuzhao123/article/details/8811496 https://shengyu7697.github.io/blog/2020/03/28/Python-os-path-basename/ https://blog.csdn.net/zzc15806/article/details/81352742 ","date":"2021-01-03T12:05:35+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/file-processing/os-path-basename-split/","tags":["Python","Python 檔案處理","檔案處理"],"title":"【Python 檔案處理 #1】取出檔案名稱 (含副檔名、不含副檔名) 取出檔名路徑, 不要副檔名 (os path split basename)"},{"categories":["131 - Python OpenCV"],"content":"前言 這支程式可以幫助我們分析一張影像的模糊程度。\nsample code import cv2 import glob import os import shutil folder_path = glob.glob(\u0026#34;./input_folder/*.png\u0026#34;) output_folder = \u0026#34;./output_folder\u0026#34; BIG_BLUR_THESHOLD = 300 MID_BLUR_THESHOLD = 200 SMALL_BLUR_THESHOLD = 100 score_dist = {} def copyimgfile(text, img_path, fm, output_f): print(f\u0026#34;[{text}] img = {img_path}, blur_score = {fm}\u0026#34;) src = img_path dst = output_f + img_path.split(\u0026#34;/\u0026#34;)[-1] print(f\u0026#34;[copyfile] to {dst}\u0026#34;) shutil.copyfile(src, dst) def variance_of_laplacian(image): \u0026#39;\u0026#39;\u0026#39; 1.calculate laplacian of the images 2.calculate variance \u0026#39;\u0026#39;\u0026#39; return cv2.Laplacian(image, cv2.CV_64F).var() if __name__ == \u0026#39;__main__\u0026#39;: total_num = len(folder_path) for img_path in folder_path: # read image image = cv2.imread(img_path) # convert to grayscale images gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 1.calculate laplacian of the images # 2.calculate variance fm = int(variance_of_laplacian(gray)) score_rank = int(fm/100) if(score_dist.get(score_rank)): score_dist[score_rank] += 1 else: score_dist[score_rank] = 1 # rename files with score (if needed) # src = img_path # basename = os.path.basename(img_path) # new_basename = f\u0026#34;s{fm}-{basename}\u0026#34; # new_basename = basename.replace(\u0026#34;.png\u0026#34;, f\u0026#34;-s{fm}.png\u0026#34;) # dst = src.replace(basename, new_basename) # print(src) # print(dst) # os.rename(src, dst) # process result if fm \u0026gt; BIG_BLUR_THESHOLD: text = \u0026#34;Very Very Very Not Blurry\u0026#34; copyimgfile(text, img_path, fm, output_folder+f\u0026#34;/{BIG_BLUR_THESHOLD}/\u0026#34;) elif fm \u0026gt; MID_BLUR_THESHOLD: text = \u0026#34;Very Very Not Blurry\u0026#34; copyimgfile(text, img_path, fm, output_folder+f\u0026#34;/{MID_BLUR_THESHOLD}/\u0026#34;) elif fm \u0026gt; SMALL_BLUR_THESHOLD: text = \u0026#34;Very Not Blurry\u0026#34; copyimgfile(text, img_path, fm, output_folder+f\u0026#34;/{SMALL_BLUR_THESHOLD}/\u0026#34;) else: text = \u0026#34;Not Blurry\u0026#34; k = sorted(score_dist.keys()) for key in k: print(f\u0026#34;{key}: {score_dist[key]}\\t{score_dist[key]/total}%\u0026#34;) Reference https://blog.csdn.net/WZZ18191171661/article/details/96602043 ","date":"2021-01-01T14:13:47+08:00","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-blur-score/","tags":["Python","Python OpenCV"],"title":"【OpenCV】python OpenCV 分析影像模糊程度 檢測圖片模糊 blur"},{"categories":["231 - C++ OpenCV"],"content":"前言 此文為 C ++ 在 OpenCV 的矩型用法\nsample code 1. 宣告矩形 int x = 0; int y = 0; int width = 100; int height = 50; cv::Rect rect(x, y, width, height); 宣告一個左上角位於 (x,y)，\n長寬等於 (width, height) 的矩形。\n注意不是左上角與右下角的座標 2. (自己常用) 取得 x, y, w, h stringstream bbox; bbox \u0026lt;\u0026lt; \u0026#34;bbox: x = \u0026#34; \u0026lt;\u0026lt; rect.x; bbox \u0026lt;\u0026lt; \u0026#34;, y = \u0026#34; \u0026lt;\u0026lt; rect.y; bbox \u0026lt;\u0026lt; \u0026#34;, w = \u0026#34; \u0026lt;\u0026lt; rect.width; bbox \u0026lt;\u0026lt; \u0026#34;, h = \u0026#34; \u0026lt;\u0026lt; rect.height; 3. 一些參數與功能 rect.area(); // rect的面積 rect.size(); // rect的尺寸 100 × 50 rect.tl(); // 左上頂點座標 rect.br(); // 右下頂點座標 rect.width(); // rect的寬度 rect.height(); // rect的高度 rect.contains(Point(x, y)); // 查看rect有沒有包含Point(x, y) Reference https://www.itread01.com/content/1547177430.html https://docs.opencv.org/3.4/d2/d44/classcv_1_1Rect__.html ","date":"2020-12-25T18:06:41+08:00","permalink":"https://wongwongnotes.com/posts/cpp/visual-apps/c-opencv/cpp-opencv-rect/","tags":["C++","C++ OpenCV"],"title":"【OpenCV】c++ OpenCV - cv::Rect 矩形用法與相關功能函數"},{"categories":["221 - C++ 程式編譯"],"content":"前言 C++ 並不像 Python 一樣屬於直譯式語言，\n也就是說 C++ 的程式碼需要經過完整的 compile 才能完整的執行，\n而 python 不用，想要執行一行就能執行一行。\n這邊只單純整理一些比較基本的方式。\nSample Code 基本編譯 g++ test.cpp 預設編譯出來的 binary 執行檔名稱為 a.out\n我們可以再使用以下指令執行他。\n./a.out 修改編譯出來的檔名 如果不希望編譯出來的名稱為 「a.out」，\n我們可以使用 「-o」這個指令，\ng++ test.cpp -o test.out 這樣編譯出來的 binary 執行檔名稱就會變為 test.out 囉！\n執行：\n./test.out 使用 c++ 11 編譯 我們只需要增加 「-std=c++11」即可，\n例如：\ng++ -std=c++11 test.cpp -o test.out 這樣編譯出來的就會是 c++ 11 的版本囉！\n使用套件，此處以 pkg-config 作為示範 例如在我們文章中經常使用的 opencv 套件，\n我們會使用以下指令來進行編譯\ng++ test.cpp -o test.out -std=c++11 `pkg-config --cflags --libs opencv` Reference https://dywang.csie.cyut.edu.tw/moodle23/dywang/linuxProgram/node34.html https://stackoverflow.com/questions/10363646/compiling-c11-with-g https://blog.gtwang.org/programming/gcc-comipler-basic-tutorial-examples/ ","date":"2020-12-19T11:08:42+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/c-compile/","tags":["C++","C++ 系統偵測","程式編譯"],"title":"【C++ 程式編譯 #1】C++ compile 程式碼 使用 c++ 11 與使用相關的 package"},{"categories":["520 - 深度學習 Deep Learning"],"content":"前言 有時候因應我們有訓練時間的需求，\n我們會在一台主機上插上多張 GPU，\n我們可以使用以下方式，\n在同一台電腦上指定 GPU 進行運算。\n(如果沒有指定，預設都是 第0張 GPU) Sample Code 預設：只使用第一張 GPU (GPU0)\n指定用第二張 GPU (GPU1) 運行程式 CUDA_VISIBLE_DEVICES=1 python train.py 同時使用多張 GPU 運行程式 CUDA_VISIBLE_DEVICES=0,1,2,3 python train.py Reference https://stackoverflow.com/questions/40726039/tensorflow-cuda-visible-devices-doesnt-seem-to-work ","date":"2020-12-17T11:58:05+08:00","permalink":"https://wongwongnotes.com/posts/ai/deep-learning/deep-learning/gpu-execute-python/","tags":["機器學習","深度學習"],"title":"【機器學習】利用第 2 張、第 n 張或多張 GPU 同時執行 python 程式運算（CUDA_VISIBLE_DEVICES）"},{"categories":["450 - ffmpeg"],"content":"前言 我們可以利用 ffmpeg 剪接一段影片的片段，\n我們有兩種方法：\n取固定時間的影片時間 或者自己設定剪接影片的結束時間 Sample code 方法1 - 取影片的「固定時間區間」 -ss 開始時間：00:01:12 -i 輸入影片：input_video.mp4 取一段時間「-t」，代表的是取影片的「時間區間」，\n例如：-t 00:10:00，代表取10秒的clip。\nffmpeg -ss 00:01:12 -t 00:10:00 -i input_video.mp4 -c copy clip_video.mp4 方法2 - 取影片到「結束時間」 -ss 開始時間：00:01:12 -i 輸入影片：input_video.mp4 取影片時間至「-to」，代表的是要取影片的「結束時間」，\n例如：-to 00:11:12，代表取影片到 00:11:12。\nffmpeg -ss 00:01:12 -to 00:11:12 -i input_video.mp4 -c copy clip_video.mp4 Reference https://trac.ffmpeg.org/wiki/Seeking ","date":"2020-12-16T15:15:29+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/ffmpeg-cut-clip-video/","tags":["ffmpeg"],"title":"【ffmpeg】利用 ffmpeg 剪接影片"},{"categories":["450 - ffmpeg"],"content":"前言 我們可以使用 ffmpeg 將多張圖片合併成一部影片，\n我們首先需要將所有要合併的圖片放在同一個資料夾內。\n方法1 - 有流水號且有固定格式的圖片 此方法適用於圖片名稱有流水號且有固定格式的圖片，\n收集所有符合格式「 img%08d.jpg」的圖片，\n並合成出結果「output.mp4」。\nffmpeg -framerate 30 -i img%08d.jpg output.mp4 方法2 - 使用 glob 的方式收集圖片 此方法適用於能使用「正規表達式」表達出的圖片路徑，\n使用 glob 收集所有符合格式「\u0026rsquo; ./folder_path/*.jpg\u0026rsquo;」的圖片，\n並合成出結果「output.mp4」。\nffmpeg -framerate 30 -pattern_type glob -i \u0026#39;./folder_path/*.jpg\u0026#39; output.mp4 -framerate （新） -r (舊) Reference https://stackoverflow.com/questions/38895736/ffmpeg-merge-images-to-a-video https://ffmpeg.org/ffmpeg.html ","date":"2020-12-15T17:27:45+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/ffmpeg-merge-images-to-a-video/","tags":["ffmpeg"],"title":"【ffmpeg】利用 ffmpeg 將多張圖片合併成一部影片(glob)"},{"categories":["212 - C++ 字串處理"],"content":"前言 此文介紹 C++ 將兩個 String 結合的方式\nSample Code 方法1 - + string firstName = \u0026#34;John\u0026#34;; string lastName = \u0026#34;Doe\u0026#34;; string fullName = firstName + \u0026#34; \u0026#34; + lastName; cout \u0026lt;\u0026lt; fullName; 只要使用單純的「+」，就能將兩個字串結合，\n中間我們還可以加入像是「 \u0026quot; \u0026quot; 」，可以直接連接這兩段 String。\n方法2 - append string firstName = \u0026#34;John \u0026#34;; string lastName = \u0026#34;Doe\u0026#34;; string fullName = firstName.append(lastName); cout \u0026lt;\u0026lt; fullName; 我們也可以使用「append」的方式，將兩個字串結合。\n在網站的說明中，使用 append 的速度會比使用 + 更快一些。\n但使用 + 相對簡單很多。\nReference https://www.w3schools.com/cpp/cpp_strings_concat.asp ","date":"2020-12-07T14:11:14+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-string/c-concat-string/","tags":["C++","C++ 系統偵測","字串處理"],"title":"【C++ 字串處理 #1】C++ String 用法 — 連接兩個 String"},{"categories":["211 - C++ 基礎語法"],"content":"前言 有時候我們會需要使用 C++ 複製一個 2D array，\n但 C++ 並沒有像 等於「=」 這麼簡單的實作方式，\n我們可以使用 memcpy 複製 2D array。\nSample Code int src[5][2]; int dst[5][2]; memcpy(src, dst, 5*2*sizeof(int)); Reference https://stackoverflow.com/questions/2681061/memcpy-what-should-the-value-of-the-size-parameter-be http://www.cplusplus.com/forum/general/263317/ ","date":"2020-12-05T16:29:37+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-basics/cpp-copy-2d-array/","tags":["C++","C++ 系統偵測","基礎語法"],"title":"【C++ 基礎語法 #1】C++ 複製 2D array的方法 copy 2d array memcpy"},{"categories":["231 - C++ OpenCV"],"content":"前言 此文為 C ++ 在 OpenCV 的寫上文字的用法\nsample code #include \u0026lt;opencv2/opencv.hpp\u0026gt; // create 640*480 Mat cv::Mat image = cv::Mat::zeros(cv::Size(640, 480), CV_8UC3); // set to blue color image.setTo(cv::Scalar(100, 0, 0)); // set string and variables std::string text = \u0026#34;Hello World!\u0026#34;; int font_face = cv::FONT_HERSHEY_COMPLEX; double font_scale = 2; int thickness = 2; int baseline; // get text size cv::Size text_size = cv::getTextSize(text, font_face, font_scale, thickness, \u0026amp;baseline); // decide text position cv::Point origin; origin.x = image.cols / 2 - text_size.width / 2; origin.y = image.rows / 2 + text_size.height / 2; cv::putText(image, text, origin, font_face, font_scale, cv::Scalar(0, 255, 255), thickness, 8, 0); // show result cv::imshow(\u0026#34;image\u0026#34;, image); cv::waitKey(0); return 0; C++ compile g++ test.cpp -o test.out -std=c++11 `pkg-config --cflags --libs opencv` ./test.out test.cpp：為要編譯的程式碼 test.out：為編譯結果的 binary (執行也是用此執行檔執行) 執行結果 Reference https://blog.csdn.net/guduruyu/article/details/68491211 ","date":"2020-12-02T16:57:04+08:00","image":"https://wongwongnotes.com/images/restored/2021/01/Screenshot-from-2021-01-06-16-57-19.png","permalink":"https://wongwongnotes.com/posts/cpp/visual-apps/c-opencv/c-opencv-text/","tags":["C++","C++ OpenCV"],"title":"【OpenCV】c++ OpenCV - 在圖片上寫上文字 cv::putText"},{"categories":["450 - ffmpeg"],"content":"前言 我們可以使用 ffmpeg 將多部影片合併，\n我們首先需要建立一個檔案，\n將所有要合併的影片檔案路徑寫在此文件內。\n文件內容如下：假設此檔名我們儲存為 merge_video.txt\nfile \u0026#39;/path/to/file1.mp4\u0026#39; file \u0026#39;/path/to/file2.mp4\u0026#39; file \u0026#39;/path/to/file3.mp4\u0026#39; 註：可以使用 sublime 的多行 indent 快速完成這份文件，使用 「ctrl + shift + 上下」 (for mac)\n可參考文章：【Sublime】Sublime 實用快速鍵總整理\n完成後，我們只需要在同樣的資料夾底下使用以下指令，即可完成影片合併。\nffmpeg -f concat -safe 0 -i merge_video.txt -c copy output.mp4 我們就會得到合併後的 output.mp4 檔案囉！\nReference https://stackoverflow.com/questions/7333232/how-to-concatenate-two-mp4-files-using-ffmpeg ","date":"2020-11-26T12:41:20+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/multimedia/ffmpeg/ffmpeg_merge_video/","tags":["ffmpeg"],"title":"【ffmpeg】利用 ffmpeg 將多部影片合併 ffmpeg merge two video (合併影片)"},{"categories":["198 - Python 問題解決"],"content":"問題： QtCore (0x121e59700). One of the two will be used. Which one is undefined. 此為安裝 labelme 時， OpenCV 版本太高導致的問題\n依照下方指令即可解決問題\npip install opencv-python-headless Reference https://stackoverflow.com/questions/60271645/qt-complaining-2-sets-of-binaries ","date":"2020-11-15T14:39:18+08:00","permalink":"https://wongwongnotes.com/posts/python/concepts/troubleshooting/qtcore-one-of-the-two/","tags":["Bash","Linux","Python","Ubuntu"],"title":"【OpenCV】問題解決: QtCore (0x121e59700). One of the two will be used. Which one is undefined."},{"categories":["330 - Linux 檔案處理"],"content":"前言 我們有時會有單一檔案太大導致難以搬運的問題，\nLinux 提供了 split 的方式讓我們可以將單一檔案分成很多小檔案。\n分割檔案 可依照自己需求的容量大小調整：\nsplit -b 100M data.tar \u0026#34;data.part\u0026#34; split -b 1G data.tar \u0026#34;data.part\u0026#34; 切割結果如下：\ndata.partaa data.partab data.partac \u0026hellip; 如果想要利用數字來排序 split -d -b 200M data.tar \u0026#34;data.part\u0026#34; 切割結果如下：\ndata.part00 data.part01 data.part02 \u0026hellip; 合併檔案 cat data.part* \u0026gt; data.tar Reference https://blog.gtwang.org/linux/split-large-tar-into-multiple-files-of-certain-size/ https://linuxconfig.org/how-to-create-compressed-encrypted-archives-with-tar-and-gpg ","date":"2020-11-13T12:01:57+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-split/","tags":["Linux","檔案處理"],"title":"【Linux 檔案處理 #6】split - 在 Linux (ubuntu) 中進行檔案切割、檔案合併"},{"categories":["330 - Linux 檔案處理"],"content":"前言 我們有時候會需要替某些檔案中的文字進行大量替換的動作，\n例如：將一個檔案中所有的「大學一年級」，替換成「大學二年級」，\n我們可以用 linux 的指令快速實現這件事。\nSample Code 範例1 sed -i \u0026#39;s/a/b/g\u0026#39; change_file.txt 也就是將「change_file.txt」中所有的「a」替換成「b」，\n注意：會覆蓋原本的檔案內容！請先備份！\n範例2 有時候可能會有些特殊符號，例如「(空白)」、「:」、「\u0026quot;」、「\u0026rsquo;」\n我們需要在前面加「\\」處理，\nsed -i \u0026#39;s/\\\u0026#39;a\\\u0026#39;/\\\u0026#39;b\\\u0026#39;/g\u0026#39; change_file.txt 在所有的「\u0026rsquo;」，前面加上「\\」\n將「change_file.txt」中所有的「\u0026lsquo;a\u0026rsquo;」替換成「\u0026lsquo;b\u0026rsquo;」。\n注意：會覆蓋原本的檔案內容！請先備份！\nReference https://terryl.in/zh/linux-sed-command/ ","date":"2020-11-12T11:18:24+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-sed/","tags":["Bash","Linux","Ubuntu","檔案處理"],"title":"【Linux 檔案處理 #5】sed - 在 Linux (ubuntu) 中直接對檔案內容文字，進行取代的用法 (instead content)"},{"categories":["330 - Linux 檔案處理"],"content":"前言 在 windows, mac, ubuntu 上都有很方便的捷徑功能，\n也就是說，當檔案在A處，我們可以從B處去存取，\n但存在系統中的容量只算在「A處」的一份！\n我們現在要在終端機(terminal)使用一樣的功能，達到從B處也能拿到A處檔案的效果！\n註：在 linux 作業系統中，我們稱之為 軟連結 (Symbolic Link)，也就是我們常說的「捷徑」！\n實作 其實實作的方式很簡單：\nln -s [原始檔案] [捷徑位置] 範例圖 目前的資料夾裡面有很多圖片，我們用以下指令在桌面建立捷徑。\nln -s tmp_000000000.jpg ~/Desktop 結果 可以看到桌面多了一個檔案！\n與其他檔案不同的地方在於他是有「箭頭」的！\n也表示他是連結到某個地方的捷徑囉！\n(修改此檔案 = 直接修改原先的檔案)\n","date":"2020-11-11T14:54:41+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/%E6%88%AA%E5%9C%96-2021-03-17-%E4%B8%8B%E5%8D%8812.31.41.png","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-ln-s/","tags":["Linux","檔案處理"],"title":"【Linux 檔案處理 #4】在終端機 terminal 使用 ln -s 建立如 windows, mac, ubuntu 檔案系統上的捷徑 - 軟連結 (Symbolic Link)"},{"categories":["330 - Linux 檔案處理"],"content":"前言 在搬運檔案的過程中，我們會時常有壓縮檔案的需求，\n以方便我們進行搬運。\ntar (tarball，實際上只將檔案打包、無壓縮) 註：tar 無法直接用一般我們常見的方式進行檔案加密 tar 單檔打包會有約 2GB 的限制 簡記法： c 壓縮、x 解壓縮 c = compress\n壓縮： tar -cvf data.tar FolderName 解壓縮： tar -xvf data.tar gzip (tar.gz) 簡記法： c 壓縮、x 解壓縮 壓縮： tar -zcvf data.tar.gz FolderName 解壓縮： tar -zxvf data.tar.gz zip 想要加密可以使用此方法。 zip 單檔會有 2.2GB 的限制 # 壓縮 zip -r FileName.zip DirName # 解壓縮 unzip data.zip 7z x data.zip (似乎也可以，不確定?) # 加密壓縮 zip -P password data.zip FolderName # 加密解壓縮 unzip -p password data.zip 7z * 想要加密、切割檔案壓縮可以使用此方法。\n安裝方法1 (使用 apt-get 直接安裝) # 1. Update the package index sudo apt-get update # 2. Install p7zip-full deb package sudo apt-get install p7zip-full 安裝方法2 (deb, debian 檔案) p7zip ubuntu 18.04 安裝檔 (ubuntu 18.04 p7zip-full deb) http://archive.ubuntu.com/ubuntu/pool/universe/p/p7zip/p7zip-full_16.02+dfsg-6_amd64.deb - 簡記法： a 壓縮、x 解壓縮 加密壓縮 # 壓縮 7z a data.7z FolderName # 加密壓縮 # 等待 7z 要求輸入密碼 (較安全，可以兩次確認密碼，也不會直接顯示在 history 中) 7z a data.7z FolderName -p # 直接輸入密碼 (注意： -p 與 PASSWORD 是直接連在一起的) 7z a data.7z FolderName -pPASSWORD # 解壓縮 7z x data.7z 切割檔案壓縮 7z \u0026ldquo;-v\u0026rdquo; 的參數目前支持 b, k, m, g，分別代表：bytes, kilobytes, megabytes, gigabytes # 分成每個檔案 100m 去壓縮 (注意：-v 與 100m 是連在一起的) 7z -v100m a my_zip.7z FolderName # 分成每個檔案 1g 去壓縮 (注意：-v 與 1g 是連在一起的) 7z -v1g a my_zip.7z FolderName # 解壓縮 (輸入001檔即可，剩下會自動抓) 7z x my_zip.7z.001 結果：經壓縮後切割出的檔案\n註：當出現以下訊息的解決方法 System ERROR:\nE_FAIL\n解決方法： 該路徑的權限不足，請更換資料夾後再重新執行此指令。\n總結與合併使用：同時壓縮檔案並加密，依容量切割檔案 # 壓縮檔案並加密 7z -v1g a test.7z FolderName -p # 解壓縮檔案 7z x data.7z.001 結果：\n加密壓縮後切割出的檔案 ![](/images/restored/2021/02/Screenshot-from-2021-02-19-16-09-15.png) - 解壓縮的過程 (可以看到有找到4個檔案，Volumes = 4)，最下方會要求輸入密碼 其他切割方法 可以參考這篇文章：【Linux】linux, ubuntu 使用 split 指令檔案切割、檔案合併\nReference https://itsfoss.com/tar-vs-zip-vs-gz/ http://note.drx.tw/2008/04/command.html https://itsfoss.com/password-protect-zip-file/ https://ubuntu.pkgs.org/18.04/ubuntu-universe-arm64/p7zip-full_16.02+dfsg-6_arm64.deb.html https://superuser.com/questions/184557/how-to-create-multipart-7zip-file-in-linux https://askubuntu.com/questions/134227/how-to-extract-files-from-a-split-7zip-archive ","date":"2020-11-10T11:38:53+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/Screenshot-from-2021-02-19-16-11-02.png","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-tar-zip/","tags":["Linux","檔案處理"],"title":"【Linux 檔案處理 #3】tar, gzip (tar.gz), zip, 7z - 壓縮檔案常用指令，壓縮檔案切割、壓縮檔案加密 與 解壓縮套件安裝方法"},{"categories":["330 - Linux 檔案處理"],"content":"前言 在 linux 的終端機中，我們可以使用 mkdir 來實現建立資料夾的相關功能。\n以下會有所有參數的整理。\nmkdir 相關功能參數 單純建立一個資料夾 就很單純建立一個名為「FolderName」的資料夾，\n沒什麼特別的XD\nmkdir FolderName 必定建立資料夾 (如果路徑中沒有前n層資料夾，會自動建立) mkdir -p ./path/FolderName 我們直接用例子來說明： 假設「當前路徑」中沒有「path」 我們想要一個路徑為「./path/FolderName」的資料夾， 這指令會產生 error 的錯誤。 - 如果要正常執行： 一種方式是先建立「path」的資料夾後，\n輸入「mkdir ./path/FolderName」就能夠正常執行指令。\n更快的方式就是使用「-p」的方式： 直接一行指令就必定能建出指定的資料夾囉！\n但凡事都有好有壞，必定建立出資料夾的壞處是，\n假設我們不小心輸入錯誤的路徑，那整個路徑的所有資料夾都會被建立，\n有可能會造成其他的問題。\nmkdir -p ./path/FolderName 修改建立資料夾權限 chmod 在 Linux 系統中，我們最常使用的修改資料夾權限的方式是使用 chmod，\n如果同時要新建資料夾並且修改權限，\n我們可以使用「-m」來一次完成！\nmkdir -m 777 FolderName 此指令等價於：\nmkdir FolderName chmod 777 FolderName 綜合運用 我們想必定建立 ./path/FolderName 的資料夾路徑，同時有 777 權限，\n只要以下指令就能完成囉！\nmkdir -p -m 777 ./path/FolderName 此指令等價於：\nmkdir ./path mkdir ./path/FolderName chmod 777 ./path/FolderName chmod 這裡多介紹一個指令，\nchmod a+x 是我在寫腳本時偶爾會使用的指令。\na 代表所有的用戶 (user, group, other)\n+x 代表將所有的內容，改成可以執行的指令\nReference [【linux命令】chmod a+x](https://blog.csdn.net/u010276189/article/details/46683777) - - ","date":"2020-11-09T16:12:59+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/linux-ubuntu-mkdir/","tags":["Bash","Linux","Ubuntu","檔案處理"],"title":"【Linux 檔案處理 #2】mkdir - 在 Linux (ubuntu) 中建立資料夾相關指令整理 (必定建立資料夾、修改資料夾權限 chmod 777)"},{"categories":["330 - Linux 檔案處理"],"content":"前言 Rclone 是個訪問各種雲端硬碟非常好用的工具！！！\n只要我們想要實現 terminal 訪問，\n或是連接上後以本地的資料夾形式訪問，\nRclone 這個套件都可以幫我們輕鬆地做到！\n雖然以 google drive 官方已經有出了相關的應用，\n能夠直接實現類似的功能，\n不過這裡的 Rclone 更泛用！甚至連 Dropbox 那些都可以連哦！\nRclone 這個套件非常的厲害，透過他我們可以輕易實現連接雲端硬碟的功能。\n特別是「使用終端機操作 google drive」這一件事情。\n對於工程師來說，透過 rclone 能「使用終端機操作 google drive」，\n真的是太讚了。\nstep1. 安裝 Rclone 透過終端機下載 使用 curl 很方便，一行即可完成\nsudo -v ; curl https://rclone.org/install.sh | sudo bash 透過網頁下載 載點：Downloads 在底下選擇自己的作業系統版本，並下載。\nstep2. 安裝完成後，初始化設定 輸入以下指令，進入雲端硬碟設定頁面。\n這邊我們要設定好連線的「雲端硬碟名稱」與「對應的代號」\nrclone config 開始一些基本的設定\n這邊基本上只有一開始幾個要設定，其他直接 Enter 過去即可！\n這裡有一些筆記：\n1. n 2. gdrive (雲端硬碟代稱，請記得，下面會用) 3. 18 (google drive 的對應選項) 4. 之後都 Enter... 最後從瀏覽器登入 google drive，一切設定就都完成囉！\n△登入瀏覽器成功！\nstep 3. 掛載 google drive 作為本地硬碟使用 (mount google drive)， 本地資料夾 mount 雲端資料夾 這邊建議搭配 screen/tmux 使用，將 mount 運行在後台。\nscreen 的使用可以參考我的另外一篇文章\nhttps://wongwongnotes.com/posts/dev-tools/terminal/screen/linux-screen-terminal/\nmount 就是掛載的意思，基本上可以當作是進行連動。\n筆記一下，雖然有很多參數可以再去設定 (例如：限制最大快取的大小，免得佔用本地太多的硬碟空間)\n這邊就先示範最基本的，有需要再更新。\n基本的連線： rclone mount \u0026lt;gdrive剛剛輸入的代稱\u0026gt;:\u0026lt;根目錄以下的雲端路徑\u0026gt; \u0026lt;mount 本地的路徑\u0026gt; 範例： rclone mount howard:rclone/ ~/Desktop/rclone_remote Mac 可能會有的 error: Fatal error: failed to mount FUSE fs: mount stopped before calling Init: mount failed: cgofuse: cannot find FUSE 如果 Mac 出現以下的 error：\nFatal error: failed to mount FUSE fs: mount stopped before calling Init: mount failed: cgofuse: cannot find FUSE 那表示 Mac 需要去安裝 FUSE，\n安裝 macFUSE 可以去以下連結：\nmacFUSE 資料夾邏輯研究 總之我只是很好奇雲端的資料夾邏輯是怎麼樣，\n因此有了這樣的研究。\n從上面的圖片我們大概可以得到結論：\nhoward:rclone howard:/rclone 這兩個是同義的，基本上我們同樣代表都是從根目錄出發的邏輯。\n測試 雲端先建立檔案 我們先在網頁版新增對應的資料夾、與檔案\n△在根目錄新增 rclone 資料夾，並在裡面新增了一個資料夾與檔案！\n在本地查看剛剛新增的檔案 這邊我們做幾個步驟，\n透過 ls 我們有看到剛剛新增的資料夾與檔案。\n然後我們進去 test 這個資料夾，\n新增 test.txt 這個檔案，\n我們再來回去看看有沒有在雲端上看到\n在雲端上查看 果然，我們在 test 資料夾成功看到了剛剛的檔案！\n成功！\n△透過 rclone 新增檔案成功！\nReference rclone mount rclone mount rclone 掛載硬碟小記 ssh: Could not resolve hostname server: Name or service not known ","date":"2020-11-08T00:48:07+08:00","image":"https://wongwongnotes.com/images/restored/2022/08/%E6%88%AA%E5%9C%96-2022-08-26-%E4%B8%8B%E5%8D%885.28.27.png","permalink":"https://wongwongnotes.com/posts/linux-shell/file-text-utils/linux/rclone-mount-google-drive/","tags":["Linux","檔案處理"],"title":"【Linux 檔案處理 #1】透過 Rclone 掛載 google drive，達成本地檔案總管 / terminal 訪問 的效果 (rclone mount google drive)，將 google drive 作為本地硬碟使用 | (updated: 2022/10/23)"},{"categories":["131 - Python OpenCV"],"content":"前言 inotify 能幫助我們偵測某個資料夾出現的檔案，\n搭配上 OpenCV，我們就能製作出即時影片播放器。\n關於 inotify 基礎概念 inotify 是取用 linux 底下的系統自動監聽檔案變化的方式，\npyinotify 就是他的 python 版本實作，\n當我們獲得了系統給的 event，我們會得到該檔案的「絕對路徑」，\n我們就能再進一步依照此絕對路徑做對應的資料操作。\n注意：inotify的功能目前只在 linux 系統能使用，mac 上不能使用哦~ (自己親自測試過QQ)\n關於 inotify，可以參考這幾篇文章：\n【python】python pyinotify sample code 偵測指定路徑底下的文件變化 (內有範例程式碼)\n【C++】C++ inotify sample code 偵測指定路徑底下的文件變化 (內有範例程式碼)\n相關參數 WATCH_FOLDER_0 = 圖片會出現的位置 `time.sleep(0.005)`: 為保險起見，我們 delay 0.005 秒， 防止圖片未完整複製的問題。 sample code import pyinotify import os import cv2 import time WATCH_FOLDER_0 = \u0026#39;./oriimage/\u0026#39; def show_screen(path): time.sleep(0.005) img = cv2.imread(path) print(img.shape) shape = img.shape # shape = (int(shape[1]/2), int(shape[0]/2)) shape = (int(shape[1]), int(shape[0])) img = cv2.resize(img, shape, interpolation=cv2.INTER_CUBIC) # -------------------- show screen -------------------- # cv2.namedWindow(\u0026#39;rtsp\u0026#39;, cv2.WINDOW_NORMAL) cv2.resizeWindow(\u0026#39;rtsp\u0026#39;, shape[0], shape[1]) cv2.imshow(\u0026#39;rtsp\u0026#39;, img) cv2.waitKey(1) # os.remove(path) class MyEventHandler(pyinotify.ProcessEvent): def process_IN_CREATE(self, event): print(\u0026#34;CREATE event:\u0026#34;, event.pathname) path = event.pathname if path.endswith(\u0026#34;.jpg\u0026#34;): show_screen(path) def main(): pyinotify_flags = pyinotify.IN_CREATE # watch manager wm = pyinotify.WatchManager() wm.add_watch(WATCH_FOLDER_0, pyinotify_flags, rec=False) # event handler eh = MyEventHandler() # notifier notifier = pyinotify.Notifier(wm, eh) notifier.loop() if __name__ == \u0026#39;__main__\u0026#39;: main() ","date":"2020-11-07T19:01:53+08:00","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/pyinotify_video_player/","tags":["Python","Python OpenCV"],"title":"【OpenCV】python pyinotify video player 利用 OpenCV + inotify 製作即時影片播放器"},{"categories":["112 - Python 進階語法"],"content":"基本用法 assert (斷言)，可以幫助我們判斷當程式執行到某一個階段時，\n如果程式獲得的結果不如我們的預期，\n我們可以利用 assert 的功能讓程式強制停止運行，並印出錯誤訊息。\n例如：我們要求使用者輸入必須輸入「數字」，\n結果使用者輸入「文字」，我們可以提前判斷錯誤，結束後續的程式，\n以免發生更多超乎我們預期的結果。\nsample code (範例程式碼) # 當 True 的位置符合預期，才繼續執行程式 assert True, \u0026#34;Keep Going!!!\u0026#34; # 當 False 的位置不符合預期，強制停止程式，並印出錯誤訊息。 assert False, \u0026#34;Error: Something happened.\u0026#34; ","date":"2020-11-02T01:48:08+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/advanced-syntax/python-assert/","tags":["Python","Python 基礎用法","進階語法"],"title":"【Python 進階語法 #1】python assert (斷言) 用法"},{"categories":["114 - Python 字串處理"],"content":"前言 2022/12/19，本人已改只用 f-string 了，這篇是舊文。\n留著給需要的人參考吧，不過還是大推在 python 3.6 版本更新的 f-string !\n有時候碰到要在 3.6 版本以前的 python 開發還是用的到啦XD (只是機會超少XD)\n基本用法 s1 = \u0026#34;Hello\u0026#34; s2 = \u0026#34;world!\u0026#34; s = \u0026#34;{} {}\u0026#34;.format(s1, s2) # Hello world! 常使用參數 變數 \u0026#34;{:d}\u0026#34;.format(1) # 整數 \u0026#34;{:f}\u0026#34;.format(2.0) # 浮點數 格式、對齊 可以使用 \u0026gt; ^ \u0026lt; 這三種符號對齊，常用為「 \u0026gt; 向右對齊」\n\u0026#34;{:\u0026gt;8d}\u0026#34;.format(1) # 向右對齊，總長度8 \u0026#34;{:0\u0026gt;8d}\u0026#34;.format(1) # 向右對齊，總長度8，並補0 常用功能：因為檔名的編號如果是 1.jpg, 2.jpg, \u0026hellip; 10.jpg, 11.jpg\n會因為先抓到第一個數字1，而讓「2.jpg」排序在「10.jpg, 11.jpg」後面，\n因此我們會很常用到 \u0026quot;{:0\u0026gt;8d}\u0026quot; 補上空缺，\n變成 01.jpg, 02.jpg 解決檔案自動排序問題。\n另外，此功能預設就是向右對齊，所以有時候我們也會省略，寫成 \u0026ldquo;{:08d}\u0026rdquo; 數字格式化 \u0026#34;{:.2f}\u0026#34;.format(2.0) # 小數點後留下2位 常用功能：我們想讓小數點後面留下幾位數，就會寫成 \u0026ldquo;{:.3f}\u0026rdquo; (小數點後留三位數的意思)\nReference https://blog.jaycetyle.com/2018/01/python-format-string/ https://www.runoob.com/python/att-string-format.html ","date":"2020-10-31T23:42:20+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/string-processing/python-string-format/","tags":["Python","字串處理"],"title":"【Python 字串處理 #1】python - string format, str.format() 個人常用參數整理"},{"categories":["442 - Sublime Text"],"content":"快速編輯類 (皆使用 ESC 取消) 功能 mac 指令 ubuntu 指令 windows 指令 1. 多行同時輸入，適用同時修改多行 ctrl + shift + 方向鍵 alt + shift + 方向鍵 ctrl + alt + 方向鍵 2. 多行同時選取 shift + 方向鍵 多行同時選取，適用同時修改多行 (示意圖) [![](/images/restored/2020/10/wp_editor_md_dcff34d1b30d96877e144b36f68f9825.jpg)](/images/restored/2020/10/wp_editor_md_dcff34d1b30d96877e144b36f68f9825.jpg) 多行同時選取 (示意圖) [![](/images/restored/2020/10/wp_editor_md_71ce72dd9aea63d872a81da41ab3d5a5.jpg)](/images/restored/2020/10/wp_editor_md_71ce72dd9aea63d872a81da41ab3d5a5.jpg) ","date":"2020-10-30T00:43:23+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/wp_editor_md_71ce72dd9aea63d872a81da41ab3d5a5.jpg","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/fast-sublime/","tags":[],"title":"【Sublime】Sublime 實用快速鍵總整理"},{"categories":["340 - Linux 系統控制"],"content":"前言 swap為虛擬記憶體，當memory不足時，我們可以暫時使用swap空間作為memory使用。\n我們可以自己決定要分配多少的 swap memory 給系統。\n查看目前系統的 swap 記憶體大小 swapon -s # 或者，可以使用 top 或安裝 htop 來看目前的 swap 記憶體大小 top htop 查看目前系統磁碟空間 (剩餘的磁碟大小） df -h 建立好 swapfile 檔案，並指定要分配多少記憶體大小給他，啟動 swap 記憶體 方法一：fallocate (速度較快) fallocate 比較快，相比 dd 少了寫入的動作\n下面以建立 8G 的記憶體作為示範 ( 8G 可自行更改 ) sudo swapoff -a # turn off virtual memory sudo fallocate -l 16G /swapfile # 16G swap sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # turn on virtual memory free -h 方法二：dd (速度較慢，但處理較乾淨) 這個命令使用 dd 來創建一個 64 GB 的文件，\n通過從 /dev/zero 讀取數據並寫入 /swapfile 來完成\n「bs=1G count=64 」，指的是每個塊的大小被設置為 1 GB，總共複製 64 次。\ndd 因為有實際內容填充，因此速度較慢，但處理的會比 fallocate 相對乾淨，\n但效果都是一樣的，因此怎麼選擇就看個人。\nsudo swapoff -a # turn off virtual memory sudo dd if=/dev/zero of=/swapfile bs=1G count=16 # 16 = 16G swap, swap size = bs*count sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # turn on virtual memory free -h 可能會碰到的問題：fallocate: fallocate failed: Text file busy 表示目前的 swap 記憶體正在使用中，需要先關閉swap記憶體\nswapoff -a # turn off virtual memory 掛載 /etc/fstab ，使開機能夠自動啟動 swap memory 因為我們的設定並不是永久設定，重新開機時設定會消失，\n如果想要開機自動啟用 swap memory，我們可以掛載 /etc/fstab\nvim /etc/fstab 設定值範例如下 /swapfile swap swap sw 0 0 重要：確認有無正確設定 swap 如果 swap 設定有問題，可能會導致「無法開機」或更嚴重的後果。\n我們可以用以下執令來確認： sudo mount -a 如果沒有問題，正常來講就不會回傳任何東西。\n如果有問題，請再次檢查有沒有哪一個步驟出錯。\n有問題的範例：\n(這只是隨便舉例，表示沒有設定好內容。)\n關閉 swap memory (swapoff) 有時候當 swap memory 使用太多時，可以使用這個指令暫時關閉 swap memory。\n系統會自動將 swap memory 使用的內容轉移至 memory，\n請確保 memory 剩餘的空間足夠。\nsudo swapoff -a 開啟 swap memory (swapon) sudo swapon /swapfile Reference https://www.opencli.com/linux/linux-add-swap ","date":"2020-10-29T09:44:13+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Screenshot-from-2021-03-22-15-52-46.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-swap/","tags":["Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #3】Linux 增加 swap 的虛擬記憶體空間"},{"categories":["340 - Linux 系統控制"],"content":"前言 我們購買新的硬碟時，會需要在 Ubuntu (Linux 系統) 上新增硬碟 (SSD, HDD)，\n我們需要透過設定 /etc/fstab 來完成這個功能，本文內會提供如何設定 /etc/fstab，\n先教學如何去尋找硬碟的 UUID，再來完成我們想要的任務。\n尋找硬碟的 UUID ubuntu 環境下 - 視覺化方法 在 ubuntu 的環境下，我們可以搜尋 disks 這個方便的 application (是內建的) 在 application 中，搜尋 disks 並打開 在以下位置可以得到硬碟的 UUID 在以下位置可取得硬碟的 UUID terminal 內尋找 UUID 的方法 有時候我們也沒有每次都有視覺化的 GUI 可以使用，\n這時候當然只剩使用 terminal 來幫助我們完成任務啦！\n在 terminal 輸入以下指令 ll /dev/disk/by-uuid 得到以下畫面，中間白色部份就可以得到我們要的 UUID了！ 掛載 /etc/fstab ，使開機能夠自動啟動 如果想要開機自動掛載硬碟 (SSD, HDD)，我們可以去修改 /etc/fstab\nvim /etc/fstab 設定值範例如下 利用我們剛剛得到的 UUID，在 vim 裡面加入以下內容\nUUID= xxxxxxxxxxxxxx /PATH ext4 defaults 0 0 你可能會想問上面的一些細節? 這裡我們快速簡單的介紹一下\n\u0026lt;file system\u0026gt; \u0026lt;mount point\u0026gt; \u0026lt;type\u0026gt; \u0026lt;options\u0026gt; \u0026lt;dump\u0026gt; \u0026lt;pass\u0026gt; UUID=xxxxxxxxxxxxxx / ext4 defaults 0 1 UUID= xxxxxxxxxxxxxx /HDD ext4 defaults 0 0 首先我們在填的資訊，總共就是6個欄位，\n第一格 file system 我們強制指定硬碟的 UUID (這個值應該要唯一)，確保不會搞錯硬碟 第二格 mount point 我們指定 mount 的位置，這邊建議自行先新增空資料夾，避免出錯 第三格 type 我們應該要知道硬碟的檔案系統格式，通常在 ubuntu 中為 「ext4」 第四格 options 檔案系統參數，別的文章會有詳細介紹，這邊沒有特殊需求設定 「defaults」 即可。 不過既然你都看到上面寫了「\u0026rsquo;errors=remount ro\u0026rsquo;」就順便介紹一下，\n這個代表的意義是「如果嘗試掛載硬碟出現錯誤，他會被重新掛載成 read-only」\n可參考：https://hant.kaifa99.com/ubuntu/article_166159\n第五格 dump 能否被 dump 備份，基本上有 dump 備份的需求再去研究此功能，不需要設定 「0」 即可。 第六格 pass 是否以 fsck 檢驗磁區，基本上有此需求再去研究此功能，不需要設定「0」即可。 「重要」：確認有無正確設定 /etc/fstab 如果 /etc/fstab 設定有問題，可能會導致「無法開機」或更嚴重的後果。\n我們可以用以下執令來確認： sudo mount -a 如果沒有問題，正常來講就不會回傳任何東西。\n如果有問題，請再次檢查有沒有哪一個步驟出錯、或打錯字。\n有問題的範例：\n(這只是隨便舉例，表示沒有設定好內容。)\nReference https://askubuntu.com/questions/386536/how-to-find-the-attached-devices-uuid-through-terminal https://blog.xuite.net/cloud2013/wretch/161360859 https://code.yidas.com/linux-fstab/ https://hant.kaifa99.com/ubuntu/article_166159 ","date":"2020-10-28T19:33:27+08:00","image":"https://wongwongnotes.com/images/restored/2021/03/Screenshot-from-2021-03-22-15-52-46.png","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-etc-fstab/","tags":["Linux","Ubuntu","系統控制"],"title":"【Linux 系統控制 #2】在 Ubuntu (Linux 系統) 上透過 / etc/fstab 安裝新的硬碟、新增硬碟容量 (SSD, HDD) (內附圖文說明)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這篇文章主要說明當我碰到「fallocate: fallocate failed: text file busy」時，我是如何解決的。\n問題成因 出現此訊息時，表示目前的 swap 記憶體正在使用中，我們需要先關閉swap記憶體即可。\n解決方式 執行以下指令：\nswapoff -a Reference https://askubuntu.com/questions/920595/fallocate-fallocate-failed-text-file-busy-in-ubuntu-17-04 ","date":"2020-10-28T16:50:07+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/error-fallocate/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】問題解決：fallocate: fallocate failed: Text file busy"},{"categories":["340 - Linux 系統控制"],"content":"前言 在 linux 中查詢 cpu, gpu 型號的方式\n作法 查詢 cpu 方法一 cat /proc/cpuinfo 方法二 lscpu 查詢 gpu nvidia-smi -a Reference https://magiclen.org/linux-view-cpu/ https://blog.gtwang.org/linux/linux-query-gpu-memory-size-command-tutorial/ ","date":"2020-10-27T17:03:08+08:00","permalink":"https://wongwongnotes.com/posts/linux-shell/system-admin/linux-cpu-gpu/","tags":["Linux","系統控制"],"title":"【Linux 系統控制 #1】在 Linux 中查看 cpu, gpu 資訊"},{"categories":["116 - Python 系統控制"],"content":"基礎概念 inotify 是取用 linux 底下的系統自動監聽檔案變化的方式，\npyinotify 就是他的 python 版本實作，\n當我們獲得了系統給的 event，我們會得到該檔案的「絕對路徑」，\n我們就能再進一步依照此絕對路徑做對應的資料操作。\n注意：inotify的功能目前只在 linux 系統能使用，mac 上不能使用哦~ (自己親自測試過QQ)\n使用範例 pyinotify 做到的效果就是：如果當某個資料夾底下有資料變化，\n舉一些例子例如：\n新增：process_IN_CREATE 修改：process_IN_MODIFY 刪除：process_IN_DELETE 資料被開啟：process_IN_OPEN 資料屬性變化：process_IN_ATTRIB (可搭配touch使用，獲得目標檔案路徑) Sample Code import pyinotify class MyEventHandler(pyinotify.ProcessEvent): def process_IN_ACCESS(self, event): print(\u0026#34;ACCESS event:\u0026#34;, event.pathname) def process_IN_ATTRIB(self, event): print(\u0026#34;ATTRIB event:\u0026#34;, event.pathname) def process_IN_CLOSE_NOWRITE(self, event): print(\u0026#34;CLOSE_NOWRITE event:\u0026#34;, event.pathname) def process_IN_CLOSE_WRITE(self, event): print(\u0026#34;CLOSE_WRITE event:\u0026#34;, event.pathname) def process_IN_CREATE(self, event): print(\u0026#34;CREATE event:\u0026#34;, event.pathname) def process_IN_DELETE(self, event): print(\u0026#34;DELETE event:\u0026#34;, event.pathname) def process_IN_MODIFY(self, event): print(\u0026#34;MODIFY event:\u0026#34;, event.pathname) def process_IN_OPEN(self, event): print(\u0026#34;OPEN event:\u0026#34;, event.pathname) if __name__ == \u0026#39;__main__\u0026#39;: # To specify two or more events codes just orize them # pyinotify_flags = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY # Or if you want to be notified for all events just use this shortcut pyinotify_flags = pyinotify.ALL_EVENTS # watch manager wm = pyinotify.WatchManager() watch_path = \u0026#39;/home/ubuntu/Desktop/\u0026#39; wm.add_watch(watch_path, pyinotify_flags, rec=True) # rec = recursive # event handler eh = MyEventHandler() # notifier notifier = pyinotify.Notifier(wm, eh) notifier.loop() ","date":"2020-10-26T00:56:37+08:00","permalink":"https://wongwongnotes.com/posts/python/core-syntax/python/python-pyinotify/","tags":["Linux","Python","Python 系統偵測","Ubuntu","系統控制"],"title":"【Python 系統相關 #1】python pyinotify 偵測指定路徑底下的文件變化"},{"categories":["442 - Sublime Text"],"content":"安裝 terminus, sublime 的終端機 打開 Command Palette ![](/images/restored/2020/10/截圖-2020-10-31-下午2.15.52.png) 輸入 install, 找到 Package Control ![](/images/restored/2020/10/截圖-2020-10-31-下午2.16.01.png) 輸入 terminus, 下載 ![](/images/restored/2020/10/截圖-2020-10-31-下午2.16.19.png) 安裝完成!!!\n開啟 terminus 一樣從 Command Palette 進去 ![](/images/restored/2020/10/截圖-2020-10-31-下午2.16.38.png) 輸入 terminus, 找到 Terminus: Open Default Shell in Panel ![](/images/restored/2020/10/截圖-2020-10-31-下午2.18.48.png) 這樣我們就能看到我們的終端機囉~ ![](/images/restored/2020/10/截圖-2020-10-31-下午2.18.38.png) 關閉 terminus 回到剛剛的第二步，輸入 terminus, 找到 Terminus: Close ![](/images/restored/2020/10/截圖-2020-10-31-下午2.17.58.png) 解除安裝 terminus 打開 Command Palette，輸入package，找到 Package Control: Remove Package ![](/images/restored/2020/10/截圖-2020-10-31-下午2.19.02.png) 輸入 terminus，解除安裝 ![](/images/restored/2020/10/截圖-2020-10-31-下午2.19.08.png) Reference - https://www.youtube.com/watch?v=i7MxgpFokdU\u0026ab_channel=AndresAyala ","date":"2020-10-24T22:05:00+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/%E6%88%AA%E5%9C%96-2020-10-31-%E4%B8%8B%E5%8D%882.19.08.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/sublime-text/sublime-terminal/","tags":["terminus"],"title":"【Sublime】在 sublime 啟動終端機, Sublime terminal - 圖文教學 安裝/解除安裝 terminus"},{"categories":["城市尋寶"],"content":"\n簡介 城市尋寶 -《淡水 ‧ 1884》是一款主題在淡水的「實境解謎遊戲」\n一款主打「景點旅遊 × 實境解謎遊戲」，也就是說不同於密室逃脫，\n我們主要的活動都在室外，如果喜歡解謎又不喜歡待在室內，\n或者很喜歡旅遊又想增添一些趣味性，都很適合來玩這樣的遊戲!\n△ 買遊戲會送的小資料夾，非常的可愛!\n主要題目一共有7題 (紅橙黃綠藍靛紫)，外加一題彩蛋 (金)，\n每一個題目看似獨立，但能夠串起一整個故事，\n整體而言，玩完後覺得這款的故事結構性非常的強，如果很喜歡故事的非常推薦這款。\n△ 隨著劇情的推進，一篇又一篇的故事能不斷的被串起來!\n以下是自己的玩完後的一些心得，老樣子，有優點、有缺點都會說，\n個人真實體驗，絕沒有多加修飾XD\n喜歡實境解謎嗎？作者這裡也推薦一些有趣的實境解謎心得文，歡迎參考哦：\n遊戲相關 解題限制時間: 無限制 (一般可能大約 6~8 小時吧，可以玩整天) 既然是一款主打「景點旅遊 × 實境解謎遊戲」，\n也就是說我們可以瘋狂解題，拚最佳解題時間，(最快好像看到有人兩個小時)\n也可以不用那麼拚解題時間，慢慢觀光，邊觀光邊享受解題的樂趣。\n(雖然那個app\u0026hellip; 會「關心」你已經解了很久了，需要幫忙嗎?)\n我們這次算是一半一半，不過也沒想到只有七道題目會花到我們快7小時XD，\n有一部分原因是因為第一次玩，不知道怎麼去意會到出題者邏輯，\n一部分原因是自己想太難XD，大家都聰明過頭了(誤)，\n但玩完後心得\u0026hellip;「題目」要仔細看就是，\n我們一開始題目都大概看看，然後就卯起來先解了，然後解不出來(笑)\n謎題難度(解題需要的背景知識): 適中遍易 (2/5) 途中題目有些需要一點地理\u0026hellip;不過其實也沒有很難，幾乎可以說是常識，\n謎題難度(解題過程夠不夠直覺): 從簡單到難都有 (2~5/5) 大部分題目都是簡單直覺的，硬要說難度的話\u0026hellip;\n我覺得題目的難度設計，小學生稍微思考一下應該也都能解的出來哦!\n所以也非常適合整個家庭一起玩!\n但是部分題目就\u0026hellip; 我會覺得需要再多一點關鍵提示 (不開提示的前提下)，\n畢竟很多玩家都很希望不開提示的解完哈哈哈哈XD\n例如，有些題目也許可以強調答案的特性也許不那麼直覺 (讀者自己體驗吧，這邊不爆雷XD)\n謎題驚喜度：部分題目設計讓人驚艷 (5/5) 從遊戲中非常能感受到製作團隊的用心!!!\n有些題目解出來的時候，實在太佩服製作團隊能夠想到這樣的創意題目，\n有些題目的解法還讓我們在回程的過程中一直討論，非常讓人驚艷!!!\n謎題故事性: 前後故事連結性強 (5/5) 整個遊戲過程結合真實歷史故事與真實地點，非常有帶入感，\n而且每一關的故事最後能串出一個完整的劇情，\n而且結尾時也有說明此故事代表的意義，非常讓人印象深刻。\n補充: 我非常喜歡製作團隊的用心，完全感受的到!!! 有些題目解完後，當下的感覺就是「天啊，這到底要花多久場勘才想到這題目啊\u0026hellip;」，\n題目的用心程度真的完全沒話說，非常能感受到製作團隊的用心。\n例如像是其中一題，滿滿的感受到製作團隊真的很認真的場勘!!!!\n雖然不一定會照做，但有這樣的提醒真的太貼心!!!\n△ 雖然不一定需要，但是是超貼心的提醒啊!!!\n另外如果破關進度太慢的話，也會時不時收到小天使的關心，\n(某方面來說好像也是被嗆解太慢的感覺XDDD)\n△ 解題如果解太慢，還會收到小天使的貼心協助信哦~\n△ 最後完成時，還會收到來自小天使的恭喜信~ 十分的用心\n玩完後會不會推薦: 一定會推薦!!! (5/5) 說實話，最後的難題我們是有看一點提示才解開的XD，\n雖然有些題目我們解完後認為題目不夠直覺(也可能是我們想法沒那麼跳toneXD)，\n但知道解法後的驚喜還是讓我們回味無窮，甚至回程都一路在討論怎麼樣會更好。\n而且說實在，不論是題目設計、客服，真的都有感受到製作團隊的用心。\n(客服一直有種默默在遠方盯著我們遊戲進度的感覺XDD)\n另外，主打景點旅遊 × 實境解謎遊戲，觀光也是重點中的重點，\n在這次的解題過程中，透過遊戲更了解淡水的建築與歷史背景，\n以往就算是常常來這個地方走走，也沒有像這次玩遊戲一樣能深入了解這裡，\n如果想對淡水這地方了解得更透徹，同時也能享受遊戲的樂趣的話，\n非常推薦這款 城市尋寶 -《淡水 ‧ 1884》的遊戲!!!!\n如果在找密室逃脫的遊戲評價，這裡還有一些當初我自己有參考的密室逃脫心得文，也歡迎一併參考：\n其他團體遊戲推薦：\n","date":"2020-10-23T20:01:08+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/FD35E5E9-26DD-4227-9DA1-EABB9A213F21.jpeg","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/025---%E5%9F%8E%E5%B8%82%E5%B0%8B%E5%AF%B6/tamsui-1884/","tags":["嗡嗡粗門玩","城市尋寶","實境解謎","解謎遊戲"],"title":"【實境解謎】城市尋寶 推薦 -《淡水 ‧ 1884》遊玩後心得 (無劇情暴雷) 景點旅遊 × 實境解謎遊戲"},{"categories":["441 - VScode"],"content":"前言 又要介紹 vscode 常用的好功能，\n有時候是不是有碰到一行寫超長的文字，\n但手動換行又會造成邏輯可能出問題呢？\n這時候只要「option + Z」 (windows: alt + Z)，\n就可以「沒有實際換行，只有單純顯示上的換行囉！」\n效果 before 超長的一行，都看不到後面有什麼\nafter 按下 option + Z 後 (windows: alt + Z)，\n實際上沒有換行，但「顯示上」的換行了！\n如果要還原的話，再輸入一次「option + Z」就可以還原了！\n","date":"2020-10-22T01:39:25+08:00","image":"https://wongwongnotes.com/images/restored/2023/12/%E6%88%AA%E5%9C%96-2023-12-08-%E5%87%8C%E6%99%A81.45.55.png","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/vscode-ctrl-z/","tags":["VSCode"],"title":"【VScode #1】VScode 自動換行 (顯示上的換行，非內容的換行) option + Z / alt + Z"},{"categories":["441 - VScode"],"content":"終端機類 功能 ubuntu 指令 windows 指令 1. 啟動終端機 ctrl + ` ctrl + ` - 啟動終端機 (示意圖) 快速瀏覽類 功能 ubuntu 指令 windows 指令 1. 快速到單行開頭 home home 2. 快速到單行結尾 end end 3. 瀏覽頁面上下移動 ctrl + 上下 4. 跳躍一個詞 ctrl + 左右 5. 跳到某一行 ctrl + G (記法: go) 打數字 ctrl + G (記法: go) 打數字 6. 跳到 workspace 中某一個文件 ctrl + P 打文件名 ctrl + P 打文件名 快速編輯 (選取文字) 類 (皆可使用 ESC 取消) 記法：幾乎都與 shift 有關 功能 ubuntu 指令 windows 指令 1. 多行同時輸入，適用同時修改多行 ctrl + shift + 上下 / alt + shift + 上下 ctrl + alt + 上下 2. 選取一個詞 ctrl + shift + 左右 3. 多行同時選取 shift + 方向鍵 shift + 方向鍵 4. 單行內容交換 alt + 方向鍵 5. 單行選取到底 shift + end 6. 選取同樣詞的下一個 ctrl + D 7. 自動換行 (顯示上的，實際上行數相同) ctrl + Z - 多行同時選取，適用同時修改多行 (示意圖) - 多行同時選取 (示意圖) ","date":"2020-10-21T00:54:06+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/wp_editor_md_71ce72dd9aea63d872a81da41ab3d5a5.jpg","permalink":"https://wongwongnotes.com/posts/dev-tools/editors/vscode/fast-vscode/","tags":["VSCode"],"title":"【VScode #0】VScode 個人實用快速鍵總整理"},{"categories":["922 - Mac / MacOS"],"content":"前言 身為一個萌新工程師，整天盯著終端機看是必須的，因此有個漂亮的介面能讓整天工作看的舒爽真的非常重要呢！！！\n網路上目前安裝的方法有部分步驟已經有些過時，因此分享一些自己在安裝時做的一些筆記。(適用於最新的 macbook)\n安裝的好處 可以自己配置好看的終端機介面 (整天盯著終端機的工程師就是要一個好看的介面才舒服啊！) 可以使用 zsh 的各種好用 plugin，例如：自動完成、指令拼字檢查。 個人配置分享 可以直接查看電池電量(與是否充電中)、資料夾路徑。程式是否正確執行、歷史紀錄筆數、程式運行時間、網速、記憶體用量、現在使用的python版本與環境、與現在時間，方便記錄各種事情！(還有更多可以個人化配置的功能)\n安裝 homebrew, iterm2 # 安裝 homebrew /usr/bin/ruby -e \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\u0026#34; # 測試安裝完成 brew --version # 安裝iterm2，之後我們都使用 iterm2 做為我們的終端機 brew cask install iterm2 安裝 zsh 因為 macOS 2018 Mojave 已內建 zsh，我們不需要再裝 zsh。\n# change to zsh shell chsh -s /bin/zsh 此時退出shell重開，我們會發現原本的$字號變成%符號。\n安裝 oh-my-zsh # 安裝 oh-my-zsh sh -c \u0026#34;$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)\u0026#34; 安裝完成後，我們會發現原本%符號變成了~符號。\n並在家目錄底下多了一個 ~/.oh-my-zsh 的資料夾。\n安裝 powerlevel10k 應該算是這篇文章的重頭戲了，網路上的教學大多停留在安裝 powerlevel9k，powerlevel10k 有提供了一些方便的簡易設定。\n# 下載並安裝 powerlevel10k git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k echo \u0026#39;source ~/powerlevel10k/powerlevel10k.zsh-theme\u0026#39; \u0026gt;\u0026gt;! ~/.zshrc # 方便使用者的簡易設定 p10k configure # (進階編輯配置) ~/.zshrc (zsh 的預設配置) vim ~/.zshrc # (進階編輯配置) ~/.p10k.zsh (powerlevel10k 的配置) vim ~/.p10k.zsh 輸入 p10k configure 後，會視覺化的讓你選擇一些個人配置的喜歡的樣式，是 powerlevel10k 最大的特色。\n另外，如果想要更多個人化的設定，可以去設定 「~/.p10k.zsh」 這個文件中，配置自己想要的樣式。\n★ 這裡附贈個人設定後的檔案 for pc: [p10k_for_pc.zsh](https://drive.google.com/file/d/1BlrbCeaah8wY1xS-Yrv1z6TPoaenoUhI/view?usp=sharing) for notebook (有電量, wifi...) : [p10k_for_notebook.zsh](https://drive.google.com/file/d/1yH7UyDL-x2J_Izvy6ZiggvWIVCeSOvYA/view?usp=sharing) 備份: [https://drive.google.com/file/d/1v0SxjMkJ0xwxJ6BRFaOq-vKl-AhMdtUq/view?usp=sharing](https://drive.google.com/file/d/1v0SxjMkJ0xwxJ6BRFaOq-vKl-AhMdtUq/view?usp=sharing) 使用方式：修改檔名成 .p10k.zsh 並放置於 ~/ 底下\n此外有關於環境的配置 (例如 Anaconda)，可以去 「~/.zshrc」 中進行設定。\n下載字體 (Nerd font, powerline font) 其他網站提供的下載的方式很多不能用了，另外如果將整包字體打包下載檔案也非常的巨大，因此參考其中一篇文章提供的方法，直接下載我們要的字體包。\nhttps://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/SourceCodePro.zip\n新增 iterms 主題配色 https://github.com/mbadolato/iTerm2-Color-Schemes\nclone 並 import 至 iterm 2 主題\n安裝 plugins zsh 有很多方便的 plugins，對工程師來說非常方便，畢竟什麼事情能自動完成真的是懶人工程師的福音呢~~！\n個人常用的 plugins 分享：\nSyntax Highlighting Plugin 終端機下的指令自動檢查語法，不正確的語法也會以紅字顯示。\nZSH-AutoSuggestion Plugin 終端機中的指令自動完成，會自動記憶常使用的語法，之後只要按→就可以自動完成指令。\n# Syntax Highlighting Plugin git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting # ZSH-AutoSuggestion Plugin git clone https://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions # 修改 ~/.zshrc 以使用 plugins vim ~/.zshrc plugins=( git zsh-syntax-highlighting zsh-autosuggestions ) 註：原本系統的終端機出現亂碼，怎麼辦？ 原本系統的終端機介面與iterms 2的介面是不同的\n這邊的介面指的是文字的大小、字體、顏色\u0026hellip;等。 -\u0026gt; 不是顯示的文字「內容」 主要原因是找不到特殊符號的對應字體\n(可以想像，只有英文的字體包，不能顯示中文字，也會會壞掉)\n點選左上角終端機 -\u0026gt; 偏好設定，依照下圖設定去更改字體： Reference 超簡單！十分鐘打造漂亮又好用的 zsh command line 環境 【macOS】現代終端機都這麼潮嗎？Iterm2、zshell、oh-my-zsh、powerlevel9k 如何讓 Terminal 看起來好用又好看｜iTerms 2 + Oh-my-zsh 全攻略 zsh + oh-my-zsh + powerlevel9k (zim + powerlevel10k)in 2020 Macbook Pro 13 ","date":"2020-10-14T17:01:55+08:00","image":"https://wongwongnotes.com/images/restored/migrated/1_Z5f3XHtmUFhYEBjcpLjLbA.png","permalink":"https://wongwongnotes.com/posts/os-misc/os/macos/mac_powerlevel10k/","tags":["homebrew","iterm2","macOS","oh-my-zsh","powerlevel10k"],"title":"【Mac】mac 安裝 homebrew, iterm2, oh-my-zsh, powerlevel10k 筆記 (內附個人設定檔)"},{"categories":["420 - Docker"],"content":"完整步驟一次執行完 （下方可看詳細說明） docker commit -a=\u0026#39;author\u0026#39; -m=\u0026#39;commit message\u0026#39; [id] myimage docker save -o myimage.tar myimage docker load \u0026lt; myimage.tar docker run --name=myimage -i -t myimage bash 步驟1 - 儲存 container 為 image docker commit -a=\u0026#39;author\u0026#39; -m=\u0026#39;commit message\u0026#39; [id] myimage -a 表示 author: 作者名稱 -m 表示 commit message: 紀錄 commit 訊息 [id] : 要儲存的 container id (沒有中括弧) myimage: 儲存的 image 名稱 步驟2 - 將 image 轉為 tar 檔，即可隨身帶著走 docker save -o myimage.tar myimage myimage.tar : 要儲存的 tar 檔名 myimage: 要儲存的 image 名稱 (可從上一步驟得到) 步驟3 - 在新電腦中 將 tar 讀取為 image docker load \u0026lt; myimage.tar myimage.tar : 要讀取的 tar 檔名 (可從上一步驟得到) 步驟4 - 在新電腦中 將 image 啟動為 container docker run --name=myimage -i -t myimage bash \u0026ndash;name : container 名稱 myimage: 要讀取的 image 名稱 ","date":"2020-10-13T12:10:11+08:00","permalink":"https://wongwongnotes.com/posts/dev-tools/containers/docker/save-load-container/","tags":["Bash","Docker","Linux","Ubuntu"],"title":"【Docker #1】docker commit / save / load container — 把容器當作可移植的作業環境"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"My photoshop ver3.0 功能列表 # ------ My photoshop Release notes ------ # # ver3.0 (2020.10.12) 1. 優化整體程式穩定度 2. 新增油漆工具 (位於 \u0026#34;My color panel\u0026#34; 視窗，可自選色) 3. 新增新視窗 \u0026#34;My control panel\u0026#34;，為所有調整的主控版 (之前的調整功能也移至這裡) 4. 同上，同時移除主畫面 \u0026#34;My photoshop\u0026#34; 的所有調整功能 5. 新增可調整RGB分量功能 6. 新增可調整色溫功能 (冷色系、暖色系) 7. 新增銳化功能 8. 新增4種不同模糊功能 9. 新增22種不同 colormap 10. 新增視窗自動調整大小的功能 11. 新增自動將視窗移動至固定位置的功能，以後不用自己移動視窗了! # ver2.0 (2020.10.11) 1. 新增視窗 \u0026#34;My histogram\u0026#34;，可以隨時查看現在圖片的 RGB直方圖 2. 新增視窗 \u0026#34;My color panel\u0026#34;，可以自己選顏色、自己畫點 # ver1.0 (2020.10.10) 1. 新增儲存檔案功能 2. 離開應用程式的穩定優化 3. 新增關閉程式的文字提示 4. 新增調整光線、對比度功能 5. 新增調整明度、飽和度功能 6. 新增旋轉圖片功能 7. 新增可增加圖片噪點的功能，相當於增加圖片顆粒感 -\u0026gt; 此篇文章的程式碼 github Day30_My_photoshop_v3.ipynb\n前言 所以沒錢買 photoshop 的我，最後自己做了一個 photoshop 嗎\u0026hellip;\n這系列會把之前所學到的東西全部整理到一支程式中，\n當然我知道如果「用類似 QT 可以整出更好的視覺化界面」，\n但我們的重點還是放回「只靠 OpenCV 能做的極限」能到哪邊哈哈哈\n新增功能一覽 1. 優化整體程式穩定度 這次的改版主要做的事情是將 「無限while迴圈」 的部分，\n加了個 time.sleep(0.001)，\n這樣做可以大幅降低 memory 的使用率，\n而且 time.sleep(0.001) 對於人是幾乎不會有感覺的，\n加了這行程式碼能讓我們的程式更好的使用電腦資源。\n2. 新增油漆工具 (位於 \u0026ldquo;My color panel\u0026rdquo; 視窗，可自選色) 這個就是 「漫水填充法 cv2.floodFill」 的應用，\n可參考： 【沒錢ps,我用OpenCV!】Day 21 - 花式修圖3，OpenCV 也有 photoshop 的魔術棒工具?! 漫水填充法 cv2.floodFill (Magic Wand Tool)\n3. 新增新視窗 \u0026ldquo;My control panel\u0026rdquo;，為所有調整的主控版 (之前的調整功能也移至這裡) 4. 同上，同時移除主畫面 \u0026ldquo;My photoshop\u0026rdquo; 的所有調整功能 因為我們接下來會新增一大堆功能，\n如果像之前的畫面直接加在圖片下方空間會不夠用。\n因此我們新增一個主控台視窗 \u0026ldquo;My control panel\u0026rdquo;，\n我們將之後所有要加的功能加進這裡面。\n5. 新增可調整RGB分量功能 reference: 【沒錢ps,我用OpenCV!】Day 6 - 日系濾鏡3，運用 OpenCV 調整色調(冷色系/暖色系)、色溫(白平衡)modify color temperature, white balance\n6. 新增可調整色溫功能 (冷色系、暖色系) reference: 【沒錢ps,我用OpenCV!】Day 6 - 日系濾鏡3，運用 OpenCV 調整色調(冷色系/暖色系)、色溫(白平衡)modify color temperature, white balance\n7. 新增銳化功能 reference: 【沒錢ps,我用OpenCV!】Day 26 - 進階修圖6，銳化圖片，將模糊的圖片變得更清晰吧！ sharpen images\n8. 新增4種不同模糊功能 9. 新增22種不同 colormap reference: 【沒錢ps,我用OpenCV!】Day 27 - 花式修圖4，顏色映射 - OpenCV 內建的自動配色?! auto recolor images, cv2.applyColorMap\n這邊皆可以從之前我的文章找到相關內容哦!\n10. 新增視窗自動調整大小的功能 因為有時視窗大小會太大，而實際上我們並不需要太大的視窗，\n(例如：我們只有一條滑動條，但滑動條被拉的太長，實際上沒必要)\n因此我固定了視窗的大小。\n11. 新增自動將視窗移動至固定位置的功能，以後不用自己移動視窗了! 這算是對自己執行程式後的視窗分布比較友善一些，\n自動把所有視窗移動到對應位置，\n就不需要手動再移動了!\n而且，我們還模擬了 photoshop 視窗控制的分佈位置，\n現在看起來有更像 photoshop 的感覺了! (吧)\n-\u0026gt; 30天的感謝 與 未來更新 到了今天已經第30天了，很感謝所有一直默默在追隨的讀者們，\n另外也很感謝我的朋友們，願意讓我洗我的fb版面還沒刪我好友XDDD，\n另外也很感謝我的同事、主管、部分朋友，一直很支持我寫完這系列的主題，\n在30天途中也給了我不少能寫東西的點子，讓我有源源不絕的題目。\n其實我也沒想到自己居然最後乾脆自己做一個 photoshop 雛形出來，\n所以說沒錢買 photoshop，就乾脆自己把 photoshop 做出來嗎\u0026hellip;\n未來的話，其實這30天並不是終點，\n因為我也還有很多題目還沒寫完\u0026hellip;，\n這30天的 OpenCV 學習筆記讓我學習到了不少新東西，\n也等同於預寫了很多實用的函數，之後可以直接在需要時使用，\n(當然有需要的朋友也能夠直接拿去用哈哈哈哈哈)，\n但連續寫30天的我，也需要先休息一下XD，\n該出去玩不要再待在電腦前當個技術宅了XDDD，\n之後還是會繼續寫，不定期更新，\n我會繼續把實用的函數整理起來等著以後自用與順便幫助別人，\n以上，再次感謝所有支持我的人們。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 30 - Final Project v3，於是沒錢買ps的我，開發了自己的photoshop，我的天啊 My photoshop made by OpenCV\n","date":"2020-10-12T02:07:08+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424JkPgV13XD4.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-my-photoshop-v3/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】30 - Final Project v3 — 於是沒錢買 ps 的我，開發了自己的 photoshop"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"My photoshop ver2.0 功能列表 # ------ My photoshop Release notes ------ # # ver2.0 (2020.10.11) 1. 新增視窗 `My histogram`，可以隨時查看現在圖片的 RGB直方圖 2. 新增視窗 `My color panel`，可以自己選顏色、自己畫點 # ver1.0 (2020.10.10) 1. 新增儲存檔案功能 2. 離開應用程式的穩定優化 3. 新增關閉程式的文字提示 4. 新增調整光線、對比度功能 5. 新增調整明度、飽和度功能 6. 新增旋轉圖片功能 7. 新增可增加圖片噪點的功能，相當於增加圖片顆粒感 -\u0026gt; 此篇文章的程式碼 github Day29_My_photoshop_v2.ipynb\n前言 所以沒錢買 photoshop 的我，最後自己做了一個 photoshop 嗎\u0026hellip;\n這系列會把之前所學到的東西全部整理到一支程式中，\n當然我知道如果「用類似 QT 可以整出更好的視覺化界面」，\n但我們的重點還是放回「只靠 OpenCV 能做的極限」能到哪邊哈哈哈\n新增功能一覽 1. 新增視窗 「My histogram」，可以隨時查看現在圖片的 RGB直方圖 相關文章：【沒錢ps,我用OpenCV!】Day 14 - 進階修圖1，運用 OpenCV 顯示圖片直方圖、分離與合併RGB通道 show histogram, split, merge RGB channel\n我自己的印象中，photoshop 好像都會有個直方圖都在旁邊可以隨時查看?\n那我們來自己做吧!\n1-1. 建立新視窗 \u0026ldquo;My histogram\u0026rdquo; # 直方圖視窗 cv2.namedWindow(\u0026#34;My histogram\u0026#34;, window_flags) # cv2.WINDOW_NORMAL) 1-2. 使用之前教學的函數，畫出圖片 RGB直方圖 plot_histogram = show_histogram(img_copy) # 顯示調整後的效果 cv2.imshow(\u0026#34;My histogram\u0026#34;, plot_histogram) 1-3. 最大難題：如何把 plot 變成 圖片? 這問題才是這次更新功能最大的難關啊，\n我們之前絕大部分的plot都是直接顯示在 「jupyter notebook」 中，\n現在要把它變成圖片移出來，該怎麼辦呢?\n1-3-1. (failed) 嘗試使用 plt.show() plt.show() 我記得可以直接另外開啟視窗產生出圖片繪製結果，\n但在 「jupyter notebook」 中，似乎沒辦法這樣做。\n1-3-2. (passed?) 儲存 plt 為 jpg 檔後，再用 OpenCV 讀取 看標題就知道一定會成功了，\n總之這也是很直覺的方法，直接儲存圖片後再讀取圖片，\n但這會有一個很大的問題，「我們的硬碟無時無刻都會一直在讀寫」\u0026hellip;\n長久下來絕對不是一個好的方案 (除非想更快換硬碟)\n1-3-3. (最佳化) 儲存 plt 進 memory buffer 後，再讀取出來 這個方法的精隨在於，他「並沒有實際上做儲存檔案的動作」，\n而只是把這些資訊暫時放在記憶體的某個位置上，\n而我們能用如同 1-3-2. 的方法把圖片讀取出來。\ndef show_histogram(img): fig, ax = plt.subplots() # 畫出 RGB 三種顏色的分佈圖 color = (\u0026#39;b\u0026#39;,\u0026#39;g\u0026#39;,\u0026#39;r\u0026#39;) plt.style.use(\u0026#39;dark_background\u0026#39;) for idx, color in enumerate(color): histogram = cv2.calcHist([img],[idx],None,[256],[0, 256]) ax.plot(histogram, color = color) s, (width, height) = fig.canvas.print_to_buffer() plot_histogram = np.frombuffer(s, np.uint8).reshape((height, width, 4)) return plot_histogram 2. 新增視窗 「My color panel」，可以自己選色並作畫 這個不論是 photoshop 或 小畫家 應該都有的功能吧!\n我們先新增一個能夠顯示顏色的視窗:\n2-1. 建立新視窗 \u0026ldquo;My color panel\u0026rdquo; # 控制顏色 cv2.namedWindow(\u0026#34;My color panel\u0026#34;, window_flags) # cv2.WINDOW_NORMAL) 2-2. 建立 RGB滑動條，並顯示對應顏色 # create trackbars for color change cv2.createTrackbar(\u0026#39;R\u0026#39;,\u0026#39;My color panel\u0026#39;, 0, 255, nothing) cv2.createTrackbar(\u0026#39;G\u0026#39;,\u0026#39;My color panel\u0026#39;, 0, 255, nothing) cv2.createTrackbar(\u0026#39;B\u0026#39;,\u0026#39;My color panel\u0026#39;, 0, 255, nothing) def color_panel(img_copy): data = {} data[\u0026#39;r\u0026#39;] = cv2.getTrackbarPos(\u0026#39;R\u0026#39;,\u0026#39;My color panel\u0026#39;) data[\u0026#39;g\u0026#39;] = cv2.getTrackbarPos(\u0026#39;G\u0026#39;,\u0026#39;My color panel\u0026#39;) data[\u0026#39;b\u0026#39;] = cv2.getTrackbarPos(\u0026#39;B\u0026#39;,\u0026#39;My color panel\u0026#39;) color_rgb = np.zeros((300,400,3), np.uint8) color_rgb[:] = [data[\u0026#39;b\u0026#39;], data[\u0026#39;g\u0026#39;], data[\u0026#39;r\u0026#39;]] # 改變顯示 window 的內容 cv2.imshow(\u0026#39;My color panel\u0026#39;, color_rgb) cv2.resizeWindow(\u0026#34;My color panel\u0026#34;, 400, 300) return data[\u0026#39;img\u0026#39;] 使用昨天提到的 「cv2.getTrackbarPos」 得到顏色的回傳值，\n並使用 「color_rgb[:] = [data[\u0026lsquo;b\u0026rsquo;], data[\u0026lsquo;g\u0026rsquo;], data[\u0026lsquo;r\u0026rsquo;]]」，\n組合顏色並顯示顏色。\n2-3. 建立打點用的筆，並對圖片作畫 這部分的細節可以參考：【沒錢ps,我用OpenCV!】Day 23 - 綜合運用2，(資料標註) 用 OpenCV 來製作一個標記點小工具吧! 滑鼠與視窗控制 label points\n這邊我特別說明我們需要製作一個開關，來偵測現在是否開啟打點模式。\n製作開關： cv2.createTrackbar(\u0026#39;Points\u0026#39;,\u0026#39;My color panel\u0026#39;, 0, 1, nothing) 偵測開關： data[\u0026#39;points\u0026#39;] = cv2.getTrackbarPos(\u0026#39;Points\u0026#39;,\u0026#39;My color panel\u0026#39;) 如果開關打開，才是開啟打點模式： if data[\u0026#39;points\u0026#39;] == 1: # 標記點位置 cv2.circle(data[\u0026#39;img\u0026#39;], (x,y), 3, (data[\u0026#39;b\u0026#39;], data[\u0026#39;g\u0026#39;], data[\u0026#39;r\u0026#39;]), 5, 16) # 顯示修改的 (x,y) 位置 print(\u0026#34;change points: (x, y) = ({}, {})\u0026#34;.format(x, y)) 2-4. 最後，注意 imshow 圖片更新邏輯 我們需要注意一件事情，\n昨天我們的內容大多數都是「「從原圖」出發，依據對應值開始調整畫面」，\n但這次的打點內容，是「需要被保存在圖片上的」。\n因此我們必須要修改這部分的程式邏輯，\nimg = color_panel(img) img_copy = np.copy(img) 我們每次進行打點時，「更新原來的圖片」， 而做其他變化時，「不更新原來的圖片，從原圖片出發開始計算」。 這樣就可以開始我們的作畫囉~~~!\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 29 - Final Project v2，於是沒錢買ps的我，開發了自己的photoshop，我的天啊 My photoshop made by OpenCV\nReference https://www.itdaan.com/tw/6488189fb5d7d60b163039cd0a004f70\nhttps://blog.csdn.net/jacke121/article/details/54718563\nhttps://blog.csdn.net/aa846555831/article/details/52372884?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\u0026depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\nhttps://web-backend.icare.univ-lille.fr/tutorials/convert_a_matplotlib_figure\nhttps://www.itread01.com/content/1547426526.html\nhttp://www.shengwn.com/page/2018-07-22/how_to_make_a_filter_with_python.htm\nhttps://blog.csdn.net/aa846555831/article/details/52372884\nhttps://www.thetopsites.net/article/58641662.shtml\nhttps://blog.csdn.net/fanjiule/article/details/81606596\nhttps://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/\nhttps://blog.csdn.net/wuyoy520/article/details/47111295\n","date":"2020-10-11T02:04:37+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424hCiedxcO8V.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-my-photoshop-v2/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】29 - Final Project v2 — 於是沒錢買 ps 的我，開發了自己的 photoshop"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"My photoshop ver1.0 功能列表 # ------ My photoshop Release notes ------ # # ver1.0 (2020.10.10) 1. 新增儲存檔案功能 2. 離開應用程式的穩定優化 3. 新增關閉程式的文字提示 4. 新增調整光線、對比度功能 5. 新增調整明度、飽和度功能 6. 新增旋轉圖片功能 7. 新增可增加圖片噪點的功能，相當於增加圖片顆粒感 -\u0026gt; 此篇文章的程式碼 github Day28_My_photoshop_v1.ipynb\n前言 所以沒錢買 photoshop 的我，最後自己做了一個 photoshop 嗎\u0026hellip;\n這系列會把之前所學到的東西全部整理到一支程式中，\n當然我知道如果「用類似 QT 可以整出更好的視覺化界面」，\n但我們的重點還是放回「只靠 OpenCV 能做的極限」能到哪邊哈哈哈\n新增功能一覽 1. 新增儲存檔案功能 讀檔存檔根本是修圖的基本功能啊! 當然第一個要最優先做!\ncv2.imwrite(\u0026#34;result.jpg\u0026#34;, img_copy) 很簡單，就一個存檔而已。\n2. 離開應用程式的穩定優化 OpenCV 的 imshow 視窗非常容易卡死\u0026hellip;\n這可能算是萬年問題了\n基本上這個也應該要優先處理，\n免得我們做出一支關不掉的程式。\nwhile True: # 顯示調整後的效果 cv2.imshow(\u0026#34;My photoshop\u0026#34;, img_copy) ch = cv2.waitKey(5) if ch == 27: # 按 ESC 鍵退出 break elif ch == ord(\u0026#39;s\u0026#39;): # 按 s 鍵保存結果並退出 cv2.imwrite(\u0026#34;result.jpg\u0026#34;, img_copy) break # 關閉所有的窗口 cv2.destroyAllWindows() 上面就是我們設計的架構，\n我們不使用單純的 「cv2.waitKey(0)」，\n(他會無限偵測是否有按鍵輸入，才關閉，這並不是很穩定)，\n我們改使用 「while True」 製造無限迴圈，\n搭配上 「cv2.waitKey(5)」 等待 5ms 自動關閉，\n但又因為無限迴圈的關係，圖片並不會消失，\n結束程式的方式由我們自己來控制。\n結束程式的方式：\nif ch == 27: 如果按下 「ESC」，則 「break」 結束迴圈 elif ch == ord(\u0026rsquo;s\u0026rsquo;): 如果按下 S，則保存圖片後，「break」 結束迴圈 「注意：用了這樣的寫法後，不使用這兩個方式是關不掉程式的哦!」\n最後用 OpenCV內建的 cv2.destroyAllWindows() 釋出資源，\n達到穩定關閉程式的結果。\n3. 新增關閉程式的文字提示 msg = \u0026#34;Press ESC to exit, or press S to save and exit.\u0026#34; img_copy = cv2.putText(img_copy, msg, (10, h-10), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA) 我們在圖片左下角放上這層文字:\n「\u0026ldquo;Press ESC to exit, or press S to save and exit.\u0026quot;」\n我們先用下面這行找到圖片的高與寬：\nh, w, dim = img_copy.shape 然後得到圖片的高以後就能夠精準的放上文字了!\n「注意: 保存檔案的時候記得不要把文字也保存起來了! (除非你想要這行文字啦)」\n稍微注意一下這部分的程式邏輯~~~\n上面把基本的程式架構都寫出來了，再來就只剩最單純的新增功能了：\n4. 新增調整光線、對比度功能 reference: 【沒錢ps,我用OpenCV!】Day 8 - 日系濾鏡5，運用 OpenCV 改變圖片的對比度 modify contrast (內含：網路上常見錯誤調整對比度方式的分析)\n5. 新增調整明度、飽和度功能 reference: 【沒錢ps,我用OpenCV!】Day 5 - 日系濾鏡2，運用 OpenCV 調整亮度、飽和度(透過轉移至 HLS 顏色空間) modify lightness, saturation\n6. 新增旋轉圖片功能 reference: 【沒錢ps,我用OpenCV!】Day 3 - 基本修圖2，OpenCV 圖片的剪裁、旋轉、縮放 (crop, rotate, resize)，在 jupyter 中直接找尋圖片的座標\n7. 新增可增加圖片噪點的功能，相當於增加圖片顆粒感 reference: 【沒錢ps,我用OpenCV!】Day 7 - 日系濾鏡4，運用 OpenCV 為圖片增加一些顆粒感 (增加高斯噪點) add gaussian noise\n這部分歡迎參考我之前的文章，而這裡我著重講怎麼樣在視窗介面中實現這些功能。\n用 OpenCV 內建的功能產生新視窗、滑動條 # 修圖主視窗 cv2.namedWindow(\u0026#34;My photoshop\u0026#34;, cv2.WINDOW_AUTOSIZE) # cv2.WINDOW_NORMAL) 這樣我們就能建立一個名字叫做 \u0026ldquo;My photoshop\u0026rdquo; 的視窗了。\n用 OpenCV 內建的功能產生滑動條 def nothing(*arg): pass cv2.createTrackbar(\u0026#34;rotation\u0026#34;, \u0026#34;My photoshop\u0026#34;, 180, 2*180, nothing) rotation = cv2.getTrackbarPos(\u0026#39;rotation\u0026#39;, \u0026#39;My photoshop\u0026#39;) 主要有上面兩行，但在這之前我們需要先建立一個新視窗，\n在 「第二個參數 \u0026ldquo;My photoshop\u0026rdquo;」 中，他對應的就是「視窗的名稱」，\n第一行 「cv2.createTrackbar(\u0026ldquo;滑動條名稱\u0026rdquo;, \u0026ldquo;視窗名稱\u0026rdquo;, 最小值, 最大值, nothing)」\n第二行 「回傳值 = cv2.getTrackbarPos(\u0026ldquo;滑動條名稱\u0026rdquo;, \u0026ldquo;視窗名稱\u0026rdquo;)」\n我們在這之後就能運用回傳值實現我們各種想要的功能囉!\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 28 - Final Project v1，於是沒錢買ps的我，開發了自己的photoshop，我的天啊 My photoshop made by OpenCV\nReference https://www.itdaan.com/tw/6488189fb5d7d60b163039cd0a004f70\nhttps://blog.csdn.net/jacke121/article/details/54718563\nhttps://blog.csdn.net/aa846555831/article/details/52372884?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\u0026depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\nhttps://web-backend.icare.univ-lille.fr/tutorials/convert_a_matplotlib_figure\nhttps://www.itread01.com/content/1547426526.html\nhttp://www.shengwn.com/page/2018-07-22/how_to_make_a_filter_with_python.htm\nhttps://blog.csdn.net/aa846555831/article/details/52372884\nhttps://www.thetopsites.net/article/58641662.shtml\nhttps://blog.csdn.net/fanjiule/article/details/81606596\nhttps://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/\nhttps://blog.csdn.net/wuyoy520/article/details/47111295\n","date":"2020-10-10T02:02:04+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424tR7UlqKPIz.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-my-photoshop-v1/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】28 - Final Project v1 — 於是沒錢買 ps 的我，開發了自己的 photoshop"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 覺得好看嗎? 「但其實這通常不是拿來這樣用的」\n-\u0026gt; 此篇文章的程式碼 github Day27_顏色映射_colormap.ipynb\n前言 花式修圖的這個系列\u0026hellip;\n主要會講的是一些比進階再更進階的內容，\n會有比較多冷門的功能，或是更難的演算法。\n顏色映射 - OpenCV 內建的自動配色?! 今天要來介紹的是 OpenCV 中通常比較少(?)被使用的顏色映射函數 「cv2.applyColorMap」，\n其實他主要的用途是被拿來做「黑白圖片(一維度的值) 重新配色」，\n例如像是我們常見的「溫度圖」，「等高線圖」等等，會利用到這東西。\n例如 (這很常見吧) ：\n我們可以參考 OpenCV 官方的顏色映射對照 網址：https://docs.opencv.org/3.4/d3/d50/group__imgproc__colormap.html#ga9a805d8262bcbe273f16be9ea2055a65\n(我只擷取部分)\n我們大概可以看得出來，不同情況下的顏色對照值，\n「從左至右也代表著 從 0(黑) 到 255(白) 的對應」。\n來看看怎麼實現顏色映射吧! result_img = cv2.applyColorMap(img, cv2.COLORMAP_XXX)) 嘿對，你沒看錯就一行。\n而 「cv2.COLORMAP_XXX」 我們需要去文件中查表後才知道自己想使用哪一個，\n(表就是上面那個網址的連結，或者也可以直接參考我結果圖上面的名稱。)\n再來就看自己想要怎麼使用囉~~~\n我們再來看看另外一張風景圖的顏色映射~\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 27 - 花式修圖4，顏色映射 - OpenCV 內建的自動配色?! auto recolor images, cv2.applyColorMap\nReference https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_filtering/py_filtering.html\nhttps://chtseng.wordpress.com/2016/11/17/python-%E8%88%87-opencv-%E6%A8%A1%E7%B3%8A%E8%99%95%E7%90%86/\nhttps://blog.csdn.net/loveliuzz/article/details/73648505\nhttps://docs.opencv.org/3.4/d3/d50/group__imgproc__colormap.html#ga9a805d8262bcbe273f16be9ea2055a65\nhttps://www.itread01.com/content/1545834246.html\n","date":"2020-10-09T01:59:54+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/201204246f3yxNCZxz.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-applycolormap/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】27 - 顏色映射 — OpenCV 內建的自動配色 (cv2.applyColorMap)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 上圖為了強化效果，我們將 「sigma值 設定為 100」，我們比較一下原圖：\n-\u0026gt; 此篇文章的程式碼 github Day26_銳化_sharpen.ipynb\n前言 基本修圖技能學習完之後，\n再來我們要來學一些比較進階會使用的修圖技巧囉！\n銳化圖片，將模糊的圖片變得更清晰吧！ def sharpen(img, sigma=100): # sigma = 5、15、25 blur_img = cv2.GaussianBlur(img, (0, 0), sigma) usm = cv2.addWeighted(img, 1.5, blur_img, -0.5, 0) return usm 今天來寫一個小章節，但非常的實用。\n我們往往在拍照時會受限於解析度的關係，\n導致拍出來的照片有些較為模糊的邊緣，\n模糊的邊緣我們能再使用銳化使它變得更銳利，\n其實銳化就是模糊的反操作哦！\n銳化的功能實現 首先我們先使用 「cv2.GaussianBlur」 產生高斯模糊圖片，\n我們說銳化就是模糊的反操作，\n因此我們再使用 「cv2.addWeighted(img, 1.5, blur_img, -0.5, 0)」\n以「原圖 : 模糊圖片= 1.5 : -0.5」 的比例進行混合。\n就能得到銳化後的圖片囉！\n如果想要效果更強，可以在 「cv2.GaussianBlur」 調高 「sigma值」，\n不過在本文中為了凸顯效果，已經將 「sigma值」 設定的太高了，\n一般來說使用上我們不會設定 「sigma = 100」 這麼大的值。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 26 - 進階修圖6，銳化圖片，將模糊的圖片變得更清晰吧！ sharpen images\nReference http://www.shengwn.com/page/2018-07-22/how_to_make_a_filter_with_python.htm\nhttps://blog.csdn.net/elegentbeauty/article/details/79849887\nhttp://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html\nhttps://zhuanlan.zhihu.com/p/63502539\n","date":"2020-10-08T01:57:59+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424P2VHSCgPuZ.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-sharpen-images/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】26 - 銳化圖片，將模糊的圖片變得更清晰吧！ sharpen images"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day25_符合p圖輪廓的變形圖片_merge_two_images_transform.ipynb\n前言 我們該來運用之前學過的所有東西了!\n綜合運用篇就是來一次運用前面的所學!\n用 OpenCV 來把圖片p到各種奇怪的地方吧！ 今天的功能與昨天的內容非常像，而我們今天來把圖片p到想要的地方：\n主程式 (讀取圖片) 運用前天的內容，製作一個能讀取座標點的滑鼠控制畫面 (滑鼠處理) 利用滑鼠控制回傳的座標進行透視投影運算 取得要p圖片的四個座標 找到最佳映射矩陣，並建立透視投影後的圖片 將原圖片需要更改的部分，利用mask挖去 最後合併兩張圖片，得到結果 主程式 (讀取圖片) #Read the destination image ori_img = cv2.imread(\u0026#34;./testdata/tv.jpg\u0026#34;) print(\u0026#34;origin image: \u0026#34;) show_img(ori_img) print(\u0026#34;Click on four corners of bllboard and the press ENTER\u0026#34;) points = get_points(ori_img) 這部分就單純的讀取圖片，\n我們使用我們定義的 「get_points」 來幫助我們找到 「輪廓的四個座標點」。\n運用前天的內容，製作一個能讀取座標點的滑鼠控制畫面 (滑鼠處理) def mouse_handler(event, x, y, flags, data): if event == cv2.EVENT_LBUTTONDOWN: # 標記點位置 cv2.circle(data[\u0026#39;img\u0026#39;], (x,y), 3, (0,0,255), -1) # 改變顯示 window 的內容 cv2.imshow(\u0026#34;Image\u0026#34;, data[\u0026#39;img\u0026#39;]) # 顯示 (x,y) 並儲存到 list中 print(\u0026#34;get points: (x, y) = ({}, {})\u0026#34;.format(x, y)) data[\u0026#39;points\u0026#39;].append((x,y)) def get_points(img): # 建立 data dict, img:存放圖片, points:存放點 data = {} data[\u0026#39;img\u0026#39;] = img.copy() data[\u0026#39;points\u0026#39;] = [] # 建立一個 window cv2.namedWindow(\u0026#34;Image\u0026#34;, 0) # 改變 window 成為適當圖片大小 h, w, dim = img.shape print(\u0026#34;img height, width: ({}, {})\u0026#34;.format(h, w)) cv2.resizeWindow(\u0026#34;Image\u0026#34;, w, h) # 顯示圖片在 window 中 cv2.imshow(\u0026#39;Image\u0026#39;,img) # 利用滑鼠回傳值，資料皆保存於 data dict中 cv2.setMouseCallback(\u0026#34;Image\u0026#34;, mouse_handler, data) # 等待關閉視窗，藉由 OpenCV 內建函數釋放資源 cv2.waitKey(0) cv2.destroyAllWindows() # 回傳點 list return data[\u0026#39;points\u0026#39;] 這部分都與前天的成品相同，可參考昨天的描述。\n\u001c\n這是我們取的四個點：\n(注意：請「從左上角」，依照「順時針」順序，在圖片的四個角落「點四個點」)\n利用滑鼠控制回傳的座標進行透視投影運算 # 垂直堆疊點 points_2D = np.vstack(points).astype(float) print(\u0026#34; points list:\u0026#34;) print(points_2D) 我們將回傳的點座標堆疊成一個 「2D list」。\n取得要p圖片的四個座標 p_img = cv2.imread(\u0026#34;./testdata/cat.jpg\u0026#34;) h, w, dim = p_img.shape pst_src = np.array( [ [0,0], [w-1,0], [w-1,h-1], [0,h-1] ],dtype=float ) 找到最佳映射矩陣，並建立透視投影後的圖片 h, status = cv2.findHomography(pst_src, points_2D) # print(h) # 透視投影 (建立變形後的圖) print(\u0026#34;image after reshape: \u0026#34;) im_temp = cv2.warpPerspective(p_img, h, (ori_img.shape[1], ori_img.shape[0])) show_img(im_temp) 透視後的圖片大概是長這樣~~~\n將原圖片需要更改的部分，利用mask挖去 # 填充多邊形 - 黑色 (該區域全為0)，等同於覆蓋上 mask print(\u0026#34;add mask: \u0026#34;) cv2.fillConvexPoly(ori_img, points_2D.astype(int), 0, 16) show_img(ori_img) 最後合併兩張圖片，得到結果 # add wraped source image to destination image print(\u0026#34;result image: \u0026#34;) ori_img = cv2.add(ori_img, im_temp) show_img(ori_img) Reference https://zhuanlan.zhihu.com/p/143035374\nhttps://kknews.cc/code/3oqxejy.html\nhttps://blog.csdn.net/fanjiule/article/details/81606596\nhttps://blog.csdn.net/yefcion/article/details/79435591\nhttps://blog.csdn.net/fengyeer20120/article/details/87798638\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 25 - 綜合運用4，用 OpenCV 來把圖片p到各種奇怪的地方吧！ 透視投影 cv2.warpPerspective, merge two images\n","date":"2020-10-07T01:55:37+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424t8nWLJUUKB.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-warpperspective/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】25 - 綜合運用4，用 OpenCV 來把圖片p到各種奇怪的地方吧！ 透視投影 cv2.warpPerspective, merge two images"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果gif -\u0026gt; 此篇文章的程式碼 github Day24_自製文件掃描機_photo_scanner.ipynb\n前言 我們該來運用之前學過的所有東西了!\n綜合運用篇就是來一次運用前面的所學!\n(應用app) 用 OpenCV 來製作一個照片文件掃描機吧! 我們要製作一個照片文件掃描機，邏輯上大致要完成以下步驟：\n主程式 (讀取圖片) 運用昨天的內容，製作一個能讀取座標點的滑鼠控制畫面 (滑鼠處理) 利用滑鼠控制回傳的座標進行透視投影運算 主程式 (讀取圖片) #Read the destination image ori_img = cv2.imread(\u0026#34;./testdata/paper.jpg\u0026#34;) print(\u0026#34;origin image: \u0026#34;) show_img(ori_img) print(\u0026#34;Click on four corners of bllboard and the press ENTER\u0026#34;) points = get_points(ori_img) pts1 = np.float32(points) 這部分就單純的讀取圖片，\n我們使用我們定義的 「get_points」 來幫助我們找到「輪廓的四個座標點」。\n(注意：請「從左上角」，依照「順時針」順序，在圖片的四個角落「點四個點」)\n運用昨天的內容，製作一個能讀取座標點的滑鼠控制畫面 (滑鼠處理) def mouse_handler(event, x, y, flags, data): if event == cv2.EVENT_LBUTTONDOWN: # 標記點位置 cv2.circle(data[\u0026#39;img\u0026#39;], (x,y), 30, (0,0,255), -1) # 改變顯示 window 的內容 cv2.imshow(\u0026#34;Image\u0026#34;, data[\u0026#39;img\u0026#39;]) # 顯示 (x,y) 並儲存到 list中 print(\u0026#34;get points: (x, y) = ({}, {})\u0026#34;.format(x, y)) data[\u0026#39;points\u0026#39;].append((x,y)) def get_points(img): # 建立 data dict, img:存放圖片, points:存放點 data = {} data[\u0026#39;img\u0026#39;] = img.copy() data[\u0026#39;points\u0026#39;] = [] # 建立一個 window cv2.namedWindow(\u0026#34;Image\u0026#34;, 0) # 改變 window 成為適當圖片大小 h, w, dim = img.shape print(\u0026#34;img height, width: ({}, {})\u0026#34;.format(h, w)) cv2.namedWindow(\u0026#34;Image\u0026#34;, cv2.WINDOW_AUTOSIZE) # cv2.WINDOW_NORMAL) # 顯示圖片在 window 中 cv2.imshow(\u0026#39;Image\u0026#39;,img) # 利用滑鼠回傳值，資料皆保存於 data dict中 cv2.setMouseCallback(\u0026#34;Image\u0026#34;, mouse_handler, data) # 等待關閉視窗，藉由 OpenCV 內建函數釋放資源 cv2.waitKey(0) cv2.destroyAllWindows() # 回傳點 list return data[\u0026#39;points\u0026#39;] 這部分都與昨日的成品相同，可參考昨天的描述。\n\u001c\n利用滑鼠控制回傳的座標進行透視投影運算 target_height = 842 # A4 target_width = 595 # A4 pts2 = np.float32([[0,0],[target_width,0],[target_width,target_height],[0,target_height]]) # 計算最佳變形矩陣 M = cv2.getPerspectiveTransform(pts1, pts2) # 將原圖使用變形矩陣做透視變換 res = cv2.warpPerspective(ori_img, M, (target_width, target_height)) print(\u0026#34;photo scanner result: \u0026#34;) show_img(res, bigger=True) print(\u0026#34;Doing threshold: \u0026#34;) resgray = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY) # 先將圖片轉為灰階 # Otsu\u0026#39;s thresholding ret, thresh = cv2.threshold(resgray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) show_img(thresh, bigger=True) # 顯示圖片在 window 中 cv2.imshow(\u0026#39;Scanner\u0026#39;,res) cv2.imshow(\u0026#39;Threshold\u0026#39;,thresh) cv2.waitKey(0) cv2.destroyAllWindows() 我們先定義好文件的大小 (這裡是 「A4紙的大小(842x595)」)，\n我們使用 「cv2.getPerspectiveTransform」，計算最佳變形矩陣，\n我們可透過這行的結果將原圖片透過矩陣運算做最適當的變形。\n我們運用上面算出的矩陣搭配 「cv2.warpPerspective」，\n將原圖進行透視變換 (上一行只有計算最佳變形矩陣，這一行才有變形)。\n算出來的就是我們要的結果囉~~~\n最後我們還能再自己使用 「Otsu\u0026rsquo;s thresholding」 做二值化，\n將文件轉為黑白文件，就像印表機印出來的結果一樣呢！\n(自己做就不用載需要付費的app囉)\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 24 - 綜合運用3，(應用app) 用 OpenCV 來製作一個照片文件掃描機吧! photo scanner 透視投影\nReference https://blog.csdn.net/on2way/article/details/46801063 ","date":"2020-10-06T01:53:39+08:00","image":"https://wongwongnotes.com/images/restored/migrated/Qbw87FR.gif","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-photo-scanner/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】24 - 綜合運用3，(應用app) 用 OpenCV 來製作一個照片文件掃描機吧! photo scanner 透視投影"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果gif -\u0026gt; 此篇文章的程式碼 github Day23_標記點小工具_label_points.ipynb\n前言 我們該來運用之前學過的所有東西了!\n綜合運用篇就是來一次運用前面的所學!\n(資料標註) 用 OpenCV 來製作一個標記點小工具吧! 我們要製作一個標記點小工具，大致要完成三個功能：\n主程式 (讀取圖片) 控制滑鼠相關的函數 (滑鼠處理) 依據滑鼠回傳的數值做出對應的反應函數 (主要功能處理) 主程式 (讀取圖片) img_dst = cv2.imread(\u0026#34;./testdata/cat.jpg\u0026#34;) print(\u0026#34;Click on the screen and press any key for end process\u0026#34;) points = get_points(img_dst) print(\u0026#34; points list:\u0026#34;) print(points) 這部分就單純的讀取圖片，\n我們使用我們定義的 「get_points」 來呼叫我們主要的功能。\n控制滑鼠相關的函數 (滑鼠處理) def mouse_handler(event, x, y, flags, data): if event == cv2.EVENT_LBUTTONDOWN: # 標記點位置 cv2.circle(data[\u0026#39;img\u0026#39;], (x,y), 3, (0,0,255), 5, 16) # 改變顯示 window 的內容 cv2.imshow(\u0026#34;Image\u0026#34;, data[\u0026#39;img\u0026#39;]) # 顯示 (x,y) 並儲存到 list中 print(\u0026#34;get points: (x, y) = ({}, {})\u0026#34;.format(x, y)) data[\u0026#39;points\u0026#39;].append((x,y)) 我們先定義一個控制滑鼠的函數，\n「OpenCV」 有內建滑鼠偵測的函數 「cv2.EVENT_LBUTTONDOWN」，\n為了讓我們的功能更有回饋感，我們在點擊之處新增一個紅點，\n作為我們真的有點下去的視覺上回饋，\n我們在畫點之後更新畫面，即時顯示在畫面上，\n並將這些點儲存起來，供等等分析使用。\n\u001c\n依據滑鼠回傳的數值做出對應的反應函數 (主要功能處理) def get_points(im): # 建立 data dict, img:存放圖片, points:存放點 data = {} data[\u0026#39;img\u0026#39;] = im.copy() data[\u0026#39;points\u0026#39;] = [] # 建立一個 window cv2.namedWindow(\u0026#34;Image\u0026#34;, 0) # 改變 window 成為適當圖片大小 h, w, dim = im.shape print(\u0026#34;Img height, width: ({}, {})\u0026#34;.format(h, w)) cv2.resizeWindow(\u0026#34;Image\u0026#34;, w, h) # 顯示圖片在 window 中 cv2.imshow(\u0026#39;Image\u0026#39;,im) # 利用滑鼠回傳值，資料皆保存於 data dict中 cv2.setMouseCallback(\u0026#34;Image\u0026#34;, mouse_handler, data) # 等待按下任意鍵，藉由 OpenCV 內建函數釋放資源 cv2.waitKey() cv2.destroyAllWindows() # 回傳點 list return data[\u0026#39;points\u0026#39;] 這裡就是我們的主要功能區，\n我們先建立一個 data dictionary，\n將我們的圖片儲存在 「data[\u0026lsquo;img\u0026rsquo;]」 這個欄位，\n並宣告一個 「data[\u0026lsquo;points\u0026rsquo;]」 讓我們等等來儲存點。\n我們建立一個新的作業用視窗 「cv2.namedWindow」，\n並修改為符合圖片大小 「cv2.resizeWindow」，\n最後顯示圖片在視窗中 「cv2.imshow」。\n「cv2.setMouseCallback(\u0026ldquo;Image\u0026rdquo;, mouse_handler, data)」，\n第二個參數就是我們剛剛定義滑鼠功能的函數名字， 第三個參數我們傳入 data dictionary，讀取圖片並儲存點座標。 為求程式的穩定性，我們使用 OpenCV 的內建函數來釋放資源\n「cv2.waitKey(0)」，cv2.destroyAllWindows()\n最後我們回傳點座標「return data[\u0026lsquo;points\u0026rsquo;]」，\n完成我們這次的功能。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 23 - 綜合運用2，(資料標註) 用 OpenCV 來製作一個標記點小工具吧! 滑鼠與視窗控制 label points\nReference https://zhuanlan.zhihu.com/p/143035374\nhttps://kknews.cc/code/3oqxejy.html\nhttps://blog.csdn.net/fanjiule/article/details/81606596\nhttps://blog.csdn.net/yefcion/article/details/79435591\n","date":"2020-10-05T01:51:05+08:00","image":"https://wongwongnotes.com/images/restored/migrated/nYlBsmC.gif","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-label-points/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】23 - 綜合運用2 — 用 OpenCV 製作標記點小工具(滑鼠與視窗控制)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day22_p圖_merge_two_images.ipynb\n前言 我們該來運用之前學過的所有東西了!\n綜合運用篇就是來一次運用前面的所學!\n用 OpenCV 來P圖囉! 我們準備了之前的貓貓(img1):\n以及迷你版的我本人(img2):\n來練習把我P到貓貓圖上面吧!\n這裡我們先提供大家等等步驟的思路 - 將要P的圖，改成適當大小 (「cv2.resize」) - 將要P的圖，使用漫水填充法，濾出背景 (「cv2.floodFill」) - 將濾出背景的圖，挖出人物，取得遮罩(黑色) (「cv2.cvtColor, cv2.threshold」) - 將遮罩印在原來的圖片上，有點像挖空的感覺 (「cv2.bitwise_and」) - 挖空後，反向取得原先要P圖的人物 (「cv2.bitwise_not, cv2.bitwise_and」) - 合併人物至原來的圖片 (「cv2.add」) 將要P的圖，改成適當大小 (「cv2.resize」) def resize_img(img, scale_percent=25): width = int(img.shape[1] * scale_percent / 100) # 縮放後圖片寬度 height = int(img.shape[0] * scale_percent / 100) # 縮放後圖片高度 dim = (width, height) # 圖片形狀 resize_img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) return resize_img 因為原來的我圖片太大了，我們先將這張圖片縮小25%。\n將要P的圖，使用漫水填充法，濾出背景 (「cv2.floodFill」) copyIma = img2.copy() h, w = img2.shape[:2] mask = np.zeros([h+2, w+2], np.uint8) cv2.floodFill(copyIma, mask, (30, 30), (255, 255, 255), (100, 100, 100), (50, 50, 50), cv2.FLOODFILL_FIXED_RANGE) 這部分的內容可以參考昨天我所寫的：\n【沒錢ps,我用OpenCV!】Day 21 - 花式修圖3，OpenCV 也有 photoshop 的魔術棒工具?! 漫水填充法 cv2.floodFill (Magic Wand Tool)\n將濾出背景的圖，挖出人物，取得遮罩(黑色) (「cv2.cvtColor, cv2.threshold」) img2gray = cv2.cvtColor(copyIma,cv2.COLOR_BGR2GRAY) ret, mask = cv2.threshold(img2gray, 254, 255, cv2.THRESH_BINARY) 取得上面的漫水填充法結果圖後，我們一樣先轉成灰階，\n由於剛剛我們使用 (255, 255, 255) 來填充背景部分，\n我們設定閥值(threshold)為 254，濾出背景。\n最後我們要取得遮罩，等等要拿來挖空原圖，\n遮罩有點類似就是要挖空的形狀，所以我們把挖空的地方填成黑色。\n將遮罩印在原來的圖片上，有點像挖空的感覺 (「cv2.bitwise_and」) rows, cols, channels = img2.shape x = 450 y = 170 roi = img1[y:y+rows, x:x+cols] print(\u0026#34;Use mask to crop origin image1:\u0026#34;) img1_bg = cv2.bitwise_and(roi, roi, mask = mask) 我們先透過 roi 設定好我們想p圖的位置，\n之後直接把這區域先取出來，然後使用 「cv2.bitwise_and」 直接進行挖空，\n因為mask黑色部分是0，只要and之後都會是黑， 而白色部分是255，只要and之後都會保持原樣。 挖空後，反向操作取得原先要P圖的人物 (「cv2.bitwise_not, cv2.bitwise_and」) mask_inv = cv2.bitwise_not(mask) img2_fg = cv2.bitwise_and(img2, img2, mask = mask_inv) 現在換我們來 「cv2.bitwise_and」 要p的圖片，\n我們先將原來的mask運用 「cv2.bitwise_not」，取得反向的mask，\n變成這個mask專門來幫助我們挖掉背景。\n因為mask黑色部分(現在是背景)是0，只要and之後都會是黑， 而白色部分是255(現在是人)，只要and之後都會保持原樣。 合併人物至原來的圖片 (「cv2.add」) dst = cv2.add(img1_bg,img2_fg) img1[y:y+rows, x:x+cols] = dst 剛剛在步驟4，我們已經把原圖要p的地方挖空(=0)\n剛剛在步驟5，我們已經把要p的地方取出(有乾淨的值)，其他背景(=0)\n我們只需要將這兩個單純相加 (=0的地方已經不構成影響)，\n即可以得到乾淨的合成圖片。\n我們使用 「cv2.add」。\n最後只要合併結果至原圖，p圖就完成囉!\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 22 - 綜合運用1，用 OpenCV 來P圖囉! 來運用各種之前學習的各種東西吧! merge two images\nReference 【沒錢ps,我用OpenCV!】Day 3 - 基本修圖2，OpenCV 圖片的剪裁、旋轉、縮放 (crop, rotate, resize)，在 jupyter 中直接找尋圖片的座標\n【沒錢ps,我用OpenCV!】Day 15 - 進階修圖2，OpenCV 當然也有像 ps 圖層的功能! 什麼？圖片也能加減法?! Add, Subtract, AddWeighted\n【沒錢ps,我用OpenCV!】Day 17 - 進階修圖4，運用 OpenCV 的終極圖層處理大全, 想P圖該怎麼P (bitwise_or, and, xor, not, addWeighted)\n【沒錢ps,我用OpenCV!】Day 18 - 進階修圖5，運用 OpenCV 做圖片二值化，產生黑白的圖片吧！cv2.threshold 各種選擇參數大全\n【沒錢ps,我用OpenCV!】Day 21 - 花式修圖3，OpenCV 也有 photoshop 的魔術棒工具?! 漫水填充法 cv2.floodFill (Magic Wand Tool)\nhttps://docs.opencv.org/master/d0/d86/tutorial_py_image_arithmetics.html ","date":"2020-10-04T01:49:08+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424YpE8BgoZDY.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-merge-two-images/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】22 - 綜合運用1，用 OpenCV 來P圖囉! 來運用各種之前學習的各種東西吧! merge two images"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day21_魔術棒工具_magic_wand_tool.ipynb\n前言 花式修圖的這個系列\u0026hellip;\n主要會講的是一些比進階再更進階的內容，\n會有比較多冷門的功能，或是更難的演算法。\nOpenCV 也有 photoshop 的魔術棒工具?! 今天要來介紹的是 OpenCV 中類似 photoshop 魔術棒工具的函數，\n我們稱之為「漫水填充法」 「cv2.floodFill」\ndef floodfill(img, seed=(0, 0), loDiff=40, upDiff=30): copyimg = img.copy() h, w = img.shape[:2] mask = np.zeros([h+2, w+2], np.uint8) cv2.floodFill(copyimg, mask, seed, (0, 0, 0), (loDiff, loDiff, loDiff), (upDiff, upDiff, upDiff), cv2.FLOODFILL_FIXED_RANGE) 他的原理是從一個點的資訊出發，我們開始尋找鄰近點是否有與他相似的值，\n如果有，則填充一樣的顏色，最後我們可以再利用此顏色進行其他修圖。\n「cv2.floodFill」 的函數定義 cv2.floodFill(image, mask, seedPoint, newVal, loDiff, upDiff, flags) image: 你的圖 mask: 遮罩，預設為「原圖的大小長寬各+2」 seedPoint: 種子點 (顏色對照點) newVal: 要填充的顏色 loDiff: 與種子點顏色之「負向顏色差距最大值」 upDiff: 與種子點顏色之「正向顏色差距最大值」 flags: 可以分為 「FLOODFILL_MASK_ONLY」: 設定則表示不改變原始圖片(等於忽略newVal的值)，結果會改變在mask裡面 「FLOODFILL_FIXED_RANGE」: 設定這個表示考慮當前像素與種子點的差，沒有則代表都與鄰近的比 以上兩個參數可以用 or 相連表示同時使用: 即 「FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE」 「漫水填充法」 「cv2.floodFill」實作 又要來借用我自己的照片當示範一下啦\u0026hellip;\n原圖 填充後(此處用黃色來凸顯效果) 當然這參數我有調過，實際上使用也需要自行調整參數以達到最佳效果。\n用二值化濾出人物 img2gray = cv2.cvtColor(copyimg2,cv2.COLOR_BGR2GRAY) ret, mask = cv2.threshold(img2gray, 1, 255, cv2.THRESH_BINARY) 其實操作很簡單，一開始填充值都設 「黑色(0,0,0)」，\n在二值化時，只需要將「閥值設1 (1\u0026gt;0)」 即可得到過濾出人物。\n手動去掉不要的部分 roi = mask2[400:550, 950:w] # roi_y, roi_x roi[np.where(roi \u0026gt; 254)] = 0 mask2[400:550, 950:w] = roi 這部分沒辦法，就算用 photoshop 的魔術棒工具也是會有不完美的部分，\n我們在針對區域去掉我們不要的地方。\n得到最後乾淨的人物遮罩圖片 mask_inv = cv2.bitwise_not(mask2) 運用之前 「cv2.bitwise_not」的技巧，\n快速得到我們人物的遮罩。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 21 - 花式修圖3，OpenCV 也有 photoshop 的魔術棒工具?! 漫水填充法 cv2.floodFill (Magic Wand Tool)\nReference https://www.cnblogs.com/youmuchen/p/7450049.html\nhttps://docs.opencv.org/master/d0/d86/tutorial_py_image_arithmetics.html\nhttps://blog.csdn.net/weixin_42508025/article/details/84029054?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\u0026depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\nhttps://blog.csdn.net/E01114255/article/details/76186656?utm_source=blogxgwz4\u0026utm_medium=distribute.pc_relevant.none-task-blog-title-9\u0026spm=1001.2101.3001.4242\nhttps://www.itread01.com/content/1537189082.html\n","date":"2020-10-03T01:46:25+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424OhfpvRXI1Z.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-floodfill/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】21 - OpenCV 也有 photoshop 的魔術棒工具?! 漫水填充法 cv2.floodFill (Magic Wand Tool)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 比較 OpenCV 的不同 「Threshold算法」 「Otsu\u0026rsquo;s Threshold 大津二值化」 -\u0026gt; 此篇文章的程式碼 github Day20-1_大津二值化_Otsu_threshold.ipynb\nDay20-2_二值化大全_Threshold_collection.ipynb\n前言 花式修圖的這個系列\u0026hellip;\n主要會講的是一些比進階再更進階的內容，\n會有比較多冷門的功能，或是更難的演算法。\n「Otsu\u0026rsquo;s Threshold 大津二值化」 是什麼 「Otsu\u0026rsquo;s Threshold 大津二值化」，他用來幫助我們解決「手動設定閥值的選值困擾」，\n我們透過 「Otsu\u0026rsquo;s Threshold」 演算法，能夠「自動找出最佳的閥值」。\n圖片示例：\n(引用自：https://scikit-image.org/docs/0.13.x/auto_examples/xx_applications/plot_thresholding.html)\n參考右下角的圖片，我們自動找到了最佳閥值後，再進行二值化。\n「Otsu\u0026rsquo;s Threshold 大津二值化」，自動計算最佳閥值 # 先將圖片轉為灰階 img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # global thresholding ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) # Otsu\u0026#39;s thresholding ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) # Otsu\u0026#39;s thresholding after Gaussian filtering blur = cv2.GaussianBlur(img,(5,5),0) ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 我們看一下效果：\n我們可以發現在未做 「Otsu\u0026rsquo;s Threshold」 的 histogram 中，\n「「0的比例」」遠遠比有做 「Otsu\u0026rsquo;s Threshold」 還高，\n從結果圖中我們也能發現這點，\n如果我們只是單純要「提取人像」，\n很明顯地透過 「Otsu\u0026rsquo;s Threshold」 給了我們一個更好的答案。\n「Otsu\u0026rsquo;s Threshold」 的算法 重點程式碼也是一行，而有時我們也會先搭配模糊的方法先進行降噪。\nret, th = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 比較 OpenCV 的各種 「Threshold算法」 這裡就是這幾天文章的總整理啦~~~\n【沒錢ps,我用OpenCV!】Day 18 - 進階修圖5，運用 OpenCV 做圖片二值化，產生黑白的圖片吧！cv2.threshold 各種選擇參數大全\n【沒錢ps,我用OpenCV!】Day 19 - 花式修圖1，OpenCV 的圖片自適應二值化，產生更好效果的黑白圖片！cv2.adaptiveThreshold\n【沒錢ps,我用OpenCV!】Day 20 - 花式修圖2，OpenCV 的 Threshold 方法整理，Otsu\u0026rsquo;s Threshold 大津二值化，自動計算最佳閥值，做出最好的黑白效果圖片！\n結果總圖： 大家可以自己比較不同 「Threshold算法」，選出自己最喜歡的效果哦！\n【沒錢ps,我用OpenCV!】Day 20 - 花式修圖2，OpenCV 的各種 Threshold 方法整理，Otsu\u0026rsquo;s Threshold 大津二值化，自動計算最佳閥值，做出最好的黑白效果圖！\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\nReference https://zh.wikipedia.org/wiki/%E5%A4%A7%E6%B4%A5%E7%AE%97%E6%B3%95\nhttp://scipy-lectures.org/packages/scikit-image/auto_examples/plot_threshold.html\nhttps://www.bogotobogo.com/python/OpenCV_Python/python_opencv3_Image_Global_Thresholding_Adaptive_Thresholding_Otsus_Binarization_Segmentations.php\nhttps://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html\nhttp://gwang-cv.github.io/2017/08/25/python+opencv%E5%9B%BE%E5%83%8F%E4%BA%8C%E5%80%BC%E5%8C%96/\nhttps://scikit-image.org/docs/0.13.x/auto_examples/xx_applications/plot_thresholding.html\n","date":"2020-10-02T01:36:17+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424XkCXmAKvOM.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-threshold-all-otsu/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】20 - OpenCV 的各種 Threshold 方法整理，Otsu's Threshold 大津二值化，自動計算最佳閥值，做出最好的黑白效果圖！"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day19_自適應圖片二值化_adaptiveThreshold.ipynb\n前言 花式修圖的這個系列\u0026hellip;\n主要會講的是一些比進階再更進階的內容，\n會有比較多冷門的功能，或是更難的演算法。\nOpenCV 的圖片自適應二值化，產生更好效果的黑白圖片！ # 先將圖片轉為灰階 img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 將圖片做模糊化，可以降噪 blur_img = cv2.medianBlur(img,5) # 一般圖二值化(未模糊降噪) ret, th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) # 一般圖自適應平均二值化(未模糊降噪) th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,11,2) # 一般圖自適應高斯二值化(未模糊降噪) th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,11,2) # 一般圖二值化(有模糊降噪) ret, th4 = cv2.threshold(blur_img,127,255,cv2.THRESH_BINARY) # 一般圖算術平均法的自適應二值化(有模糊降噪) th5 = cv2.adaptiveThreshold(blur_img,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,11,2) # 一般圖高斯加權均值法自適應二值化(有模糊降噪) th6 = cv2.adaptiveThreshold(blur_img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,11,2) 什麼是 「自適應二值化」 我們先來討論什麼是 「自適應的二值化」，\n一般的 「二值化」 我們只會考慮單一點的值，直接去做閥值分析，\n但一張圖片的「每個鄰近的像素都是彼此有關連的」。\n如果單純只針對單一個點去看，似乎失去了對整張圖相鄰點的考慮，\n因此 「自適應二值化」 就是在幫助我們找到單一點與鄰近區域的關係。\n「自適應二值化」的算法 「cv2.adaptiveThreshold」 能夠幫助我們將一張圖片做自適應的二值化，\n自適應的二值化又分為\n算術平均法的自適應二值化: 「cv2.ADAPTIVE_THRESH_MEAN_C」，取的是「區域平均值」 高斯加權均值法自適應二值化: 「cv2.ADAPTIVE_THRESH_GAUSSIAN_C」，\n取的是「高斯平均值」(高斯分佈) 「cv2.adaptiveThreshold」 函數形式 cv2.adaptiveThreshold(image, 255, 自適應二值化算法, 閥值類型, 參考局部大小, 偏移量) image: 輸入圖片 自適應二值化算法: 可使用 「cv2.ADAPTIVE_THRESH_MEAN_C」 和 「cv2.ADAPTIVE_THRESH_GAUSSIAN_C」 閥值類型: 可使用 「THRESH_BINARY」 和 「THRESH_BINARY_INV」， 看是超過閥值要「黑白」還是「白黑」。 - 參考局部大小: 就是參考區域的大小 - 偏移量: 微調用的參數 「自適應二值化」 搭配模糊降噪，能有更好的效果 通常在做 「自適應二值化」 之前，我們都會先將圖片做模糊化，\n能夠達到降噪的效果，看下圖結果的圖片應該能很明顯地分得出差別。\n(文章內容中，我們使用 「cv2.medianBlur」 來做模糊降噪的示範。)\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 19 - 花式修圖1，OpenCV 的圖片自適應二值化，產生更好效果的黑白圖片！cv2.adaptiveThreshold\nReference https://blog.csdn.net/on2way/article/details/46812121\nhttps://blog.csdn.net/weixin_40647819/article/details/90213858?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\u0026depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\nhttps://blog.csdn.net/qq_37385726/article/details/82017177\nhttps://www.twblogs.net/a/5b8c1ba02b717718833082e1\n","date":"2020-10-01T01:33:34+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/20120424dceOKY04nL.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-adaptivethreshold/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】19 - OpenCV 的圖片自適應二值化，產生更好效果的黑白圖片！cv2.adaptiveThreshold"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day18_圖片二值化_threshold.ipynb\n前言 基本修圖技能學習完之後，\n再來我們要來學一些比較進階會使用的修圖技巧囉！\n運用 OpenCV 做圖片二值化，產生黑白的圖片 img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 先將圖片轉為灰階 # 將小於閾值的灰度值設為0，其他值設為最大灰度值。\u0026gt;127 =255, \u0026lt;127 =0 ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) # 將大於閾值的灰度值設為0，其他值設為最大灰度值。\u0026gt;127 =0, \u0026lt;127 =255 ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV) # 將大於閾值的灰度值設為閾值，小於閾值的值保持不變。 \u0026gt;127 =127 ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC) # 將小於閾值的灰度值設為0，大於閾值的值保持不變。 \u0026lt;127 =0 ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO) # 將大於閾值的灰度值設為0，小於閾值的值保持不變。 \u0026gt;127 =0 ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV) 「cv2.threshold」 最主要的功能是能夠幫助我們將一張圖片做二值化，\n二值化的意思是「圖片只會剩下兩個值」，通常是「黑(255)與白(0)」。\ncv2.threshold(img, 閥值, 最大灰度值, 使用的二值化方法) 下圖我們可以參考使用不同的「使用的二值化方法」跑出來的結果。\n(假設「閥值=127、最大灰度值=255」)\ncv2.THRESH_BINARY 將小於閾值的灰度值設為0，其他值設為最大灰度值。 白話文：「\u0026gt;127 =255, \u0026lt;127 =0」 cv2.THRESH_BINARY_INV 將大於閾值的灰度值設為0，其他值設為最大灰度值。 白話文：「\u0026gt;127 =0, \u0026lt;127 =255」 cv2.THRESH_TRUNC 將大於閾值的灰度值設為閾值，小於閾值的值保持不變。 白話文：「\u0026gt;127 =127」 cv2.THRESH_TOZERO 將小於閾值的灰度值設為0，大於閾值的值保持不變。 白話文：「\u0026lt;127 =0」 cv2.THRESH_TOZERO_INV 將大於閾值的灰度值設為0，小於閾值的值保持不變。 白話文：「\u0026gt;127 =0」 為什麼我們需要做二值化呢? 圖片二值化後，對於整張圖片來說就是乾淨的兩個值，\n只有乾淨的兩個值，很適合我們做一些輪廓偵測的運算。\n而算出輪廓後我們就可以做很多圖形的處理，\n例如畫出圖片輪廓、圖片邊緣的銳利度強化、\n加粗圖片的輪廓線、依照輪廓線替圖片添加陰影\u0026hellip;\n因此，這是非常重要的一門技術，搭配其他功能的操作能實現非常多的效果！\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 18 - 進階修圖5，運用 OpenCV 做圖片二值化，產生黑白的圖片吧！cv2.threshold 各種選擇參數大全\nReference https://blog.csdn.net/on2way/article/details/46812121 ","date":"2020-09-30T01:31:11+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424qIN5Byvvu5.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-threshold/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】18 - 運用 OpenCV 做圖片二值化，產生黑白的圖片吧！cv2.threshold 各種選擇參數大全"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day17_圖片聯集交集差集_or_and_xor_not_addWeighted.ipynb\n前言 基本修圖技能學習完之後，\n再來我們要來學一些比較進階會使用的修圖技巧囉！\nOpenCV 的 終極圖層處理大全 今天這集是之前\nDay 15 - 進階修圖2，OpenCV 當然也有像 ps 圖層的功能! 什麼？圖片也能加減法?! Add, Subtract, AddWeighted\n的續集，我們在這篇會把所有 OpenCV 圖層的處理方式都整理起來！\n先來看看我們今天的兩張圖 我們的圖一:\n我們的圖二:\ncv2.bitwise_or 圖片聯集 img1_img2_or =cv2.bitwise_or(img1, img2, dst=None, mask=None) 將兩張圖片做聯集，\n在這裡因為「白色是 (255, 255, 255)」\n「藍色是(255, 0, 0)」，聯集會留下 「白色(255, 255, 255)」\n(「只有要存在都保留」。)\n小提醒: 在 OpenCV 的世界中，所有顏色都是照 BGR 的順序哦 ~\ncv2.bitwise_and 圖片交集 img1_img2_and =cv2.bitwise_and(img1, img2, dst=None, mask=None) 將兩張圖片做交集，\n在這裡因為「白色是 (255, 255, 255)」\n「藍色是(255, 0, 0)」，交集會留下 「藍色(255, 0, 0)」\n(「一個不存在就不留」。)\ncv2.bitwise_xor 圖片互斥或 img1_img2_xor =cv2.bitwise_xor(img1, img2, dst=None, mask=None) 將兩張圖片做互斥或，\n在這裡因為「白色是 (255, 255, 255)」\n「藍色是(255, 0, 0)」，互斥或會留下 「黃色(0, 255, 255)」\n(「兩個都存在就不留」，「一個存在就留」。)\ncv2.bitwise_not 圖片非運算 img1_not =cv2.bitwise_not(img1) 將一張圖片做非運算，\n在這裡因為「白色是 (255, 255, 255)」\n非運算會留下 「黑色(0, 0, 0)」。\n反過來說，原本黑色的地方，\n經過圖片非運算，會變成白色。\n實際的運用 運用其實有很多東西能寫\u0026hellip; 但今天的版面已經很多內容了，\n決定合併到之後的內容再寫。\nP圖的問題? 但這裡先給些提示，我們如果真的要實現把一個人P到另外一張圖，\n我們直接用 「addWeighted」 是行不通的，\n因為 「addWeighted」 只有調整透明度後貼上去，\n因此貼上去的圖片「原本的背景一定還在」。\n那想P圖應該怎麼做呢? - 先將圖片做二值化(之後的內容會提到，簡單來說將圖片變為黑白色)。 - 把要挖掉的地方先用成黑色，可能會用到 「cv2.bitwise_not」 - 使用 「cv2.bitwise_and」 挖掉該區域 (因為「黑色=0」全挖空，「白色=255」全保留) - 用 「cv2.add」 將圖片貼上該空白(被挖掉的)區域 (因為「黑色=0」，相加沒影響) 可以去想想看上面P圖過程其中的奧秘哦！\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 17 - 進階修圖4，運用 OpenCV 的終極圖層處理大全, 想P圖該怎麼P (bitwise_or, and, xor, not, addWeighted)\nReference https://blog.csdn.net/JNingWei/article/details/78241973\nhttps://blog.csdn.net/qq_41895190/article/details/82905657\nhttps://medium.com/%E4%B8%80%E5%80%8B%E4%BA%BA%E7%9A%84%E6%96%87%E8%97%9D%E5%BE%A9%E8%88%88/boolean%E5%9C%96%E5%BD%A2%E6%8E%A7%E5%88%B6%E5%B0%8F%E9%81%8A%E6%88%B2-f5bb22a1e1c2\nhttps://blog.csdn.net/jnulzl/article/details/47129887\nhttps://blog.csdn.net/u011028345/article/details/77278467\n","date":"2020-09-29T01:28:25+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424C91dP5jVHY.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-bitwise_or-and-xor-not/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】17 - 運用 OpenCV 的終極圖層處理大全, 想P圖該怎麼P (bitwise_or, and, xor, not, addWeighted)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 還是請我們的貓貓助教來幫忙XDDDD\n-\u0026gt; 此篇文章的程式碼 github Day16_高斯模糊找輪廓_GaussianBlur_Canny.ipynb\n前言 基本修圖技能學習完之後，\n再來我們要來學一些比較進階會使用的修圖技巧囉！\n運用 OpenCV 幫助我們找圖片的輪廓(高斯模糊、Canny) 今天我們會來測試兩張圖片！\n首先我們先建立比較好理解的較簡單圖片。\nshape = (200, 200, 3) origin_img = np.zeros(shape, np.uint8) origin_img = draw_cirlces(origin_img, circle_mid=(50,50), color=(0,255,0) ,radius=30) origin_img = draw_cirlces(origin_img, circle_mid=(50,150), color=(255,255,0) ,radius=27) origin_img = draw_cirlces(origin_img, circle_mid=(100,100), color=(255,0,255) ,radius=23) origin_img = draw_cirlces(origin_img, circle_mid=(150,60), color=(255,0,0) ,radius=25) origin_img = draw_cirlces(origin_img, circle_mid=(150,150), color=(0,0,255) ,radius=21) origin_img = draw_cirlces(origin_img, circle_mid=(120,35), color=(0,255,255) ,radius=18) origin_img = draw_cirlces(origin_img, circle_mid=(110,150), color=(255,255,255) ,radius=40) print(\u0026#34;origin picture:\u0026#34;) show_img(origin_img) 其實上面的程式幾乎都只是在為這張範例圖片畫圓而已\u0026hellip;\n不用想得太複雜哈哈哈哈\n第一步 - 轉成灰階 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # start from BGR -\u0026gt; gray 因為彩色的圖片有三個顏色通道需要處理，\n但我們今天只需要找到輪廓，用一個通道即可，\n這樣能使得我們在運算上不會到太複雜，\n因此我們會先將圖片轉成灰階。\n第二步 - OpenCV 的高斯模糊 # 高斯模糊(高斯平滑)，與計算核心大小 def do_GaussianBlur(gray): kernel_size = 5 blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size), 0) return blur_gray 高斯模糊(又稱高斯平滑)，正如他的名字，就是我們將圖片模糊化，\n「kernel_size」 是我們進行運算時，對於多大範圍的圖片進行運算。\n簡單可以理解為：我們如果對 5x5 的 「kernel_size」 運算，\n那圖片每一個 5x5 的中心受到高斯模糊的影響會最大\n算出來就會是一張比較模糊的圖片。\n(看得出來有比較模糊吧哈哈哈哈哈)\nQ: 為什麼要模糊?\nA: 因為先做高斯模糊(或說經過高斯平滑)的圖片能夠去除很多圖片的噪聲(雜訊)，\n更容易讓我們找到更精準的輪廓。\n第三步 - OpenCV 的 Canny 邊緣檢測 # Canny邊緣運算 def do_Canny(blur_gray): low_threshold = 1 high_threshold = 90 edges = cv2.Canny(blur_gray, low_threshold, high_threshold) return edges 其中有兩個參數需要自己依情況調整，\n我們知道灰階圖片的值介於 「0~255」 之間，\n情況有三種：\n高於 「high_threshold」: 為 「strong edge」 ，我們直接保留 介於 「low_threshold」 與 「high_threshold」: 為 「weak edge」 ，\nCanny會檢測 「weak edge」 是否能與 「strong edge」 相連，\n如果會相連的才會被保留。 低於 「low_threshold」: 我們都不當作 edge 真實場景運用 我們來看看貓貓的圖跑上面的流程會發生什麼事情~\n我們設定 「low_threshold = 10、high_threshold = 90」 的時候\n我們設定 「low_threshold = 1、high_threshold = 10」 的時候\n(有一種素描畫的感覺?)\n「low_threshold, high_threshold」，我們會依據使用情境不同調整參數，\n達到自己需要的效果。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 16 - 進階修圖3，運用 OpenCV 幫助我們找圖片的輪廓(高斯模糊、Canny) cv2.GaussianBlur, cv2.Canny\nReference https://ask.csdn.net/questions/706468\nhttps://www.kancloud.cn/aollo/aolloopencv/269599\nhttps://chtseng.wordpress.com/2016/12/05/opencv-edge-detection%E9%82%8A%E7%B7%A3%E5%81%B5%E6%B8%AC/\nhttps://medium.com/@pomelyu5199/canny-edge-detector-%E5%AF%A6%E4%BD%9C-opencv-f7d1a0a57d19\n","date":"2020-09-28T01:16:58+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424LRj9POfSyG.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-gaussianblur-canny/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】16 - 運用 OpenCV 幫助我們找圖片的輪廓(高斯模糊、Canny) cv2.GaussianBlur, cv2.Canny"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 還是請我們的貓貓助教來幫忙XDDDD\n-\u0026gt; 此篇文章的程式碼 github Day15_圖層圖片加減法_add_subtract_addweighted.ipynb\n前言 基本修圖技能學習完之後，\n再來我們要來學一些比較進階會使用的修圖技巧囉！\nOpenCV 當然也有像 ps 圖層的概念! 我們先建立一個我們今天用來輔助說明的遮罩：\n我們將全部的值都設為128，也就是灰色的一張圖片！\nprint(\u0026#34;Create mask:\u0026#34;) # make all 128 channel mask = np.full(img.shape, 128).astype(np.uint8) show_img(mask) OpenCV 的圖片加法 add_result = cv2.add(img, mask) 很好理解，就是一個簡單的加法，\n這件事情所實現的算法是「將圖片上對應的每一個點都相加」，\n也就是某一個點是 「128(灰色) + 128(灰色) = 256(超過255 = 白)」\n所以可想而知，加上我們的灰色遮罩，整個都要白起來了！\nOpenCV 的圖片減法 add_result = cv2.subtract(img, mask) 這也是一樣的意思，就是一個簡單的減法，\n這件事情所實現的算法是「將圖片上對應的每一個點都相減」，\n例如某一個點是 「200(偏白色) - 128(灰色) = 72(偏黑)」\n所以可想而知，減掉我們的灰色遮罩，整個都要黑起來了！\nOpenCV 的圖層疊加 (設定透明度並疊加) overlapping82 = cv2.addWeighted(img, 0.8, mask, 0.2, 0) OS: 其實我們幾乎只會使用這個來修圖\u0026hellip; 前面那兩個\u0026hellip; 暫時想不到用法\n這個就是真正遮罩的概念了！\n我們使用 「cv2.addWeighted」 這個函數就能實現接近圖層的效果了！\ncv2.addWeighted(img1, alpha, img2, beta, gamma)\nimg1: 圖片1 alpha: 圖片1的透明度 img2: 圖片2 beta: 圖片2的透明度 gamma: 常數，最後每個點再加上這個值。(正就是調亮、負就是調暗) 這個是「圖片:灰色遮罩 = 5:5」 的效果 這個是「圖片:灰色遮罩 = 8:2」 的效果 大家可以再自己慢慢調成自己喜歡的樣子囉！\n當我們想要進行圖片疊加時，例如把某個人P圖到另外一張圖上，就是需要用到這個函數！\nOS: 所以前面兩個功能\u0026hellip;XD 不過畢竟是基本運算，應該要存在的啦\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 15 - 進階修圖2，OpenCV 當然也有像 ps 圖層的功能! 什麼？圖片也能加減法?! Add, Subtract, AddWeighted\nReference https://blog.csdn.net/JNingWei/article/details/78241973\nhttps://blog.csdn.net/qq_41895190/article/details/82905657\n","date":"2020-09-27T01:13:49+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424rhuLP8aiae.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-add-subtract-addweighted/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】15 - OpenCV 當然也有像 ps 圖層的功能! 什麼?圖片也能加減法?! Add, Subtract, AddWeighted"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 還是繼續請我們的貓貓助教來幫忙XDDDD\n-\u0026gt; 此篇文章的程式碼 github Day14_分離合併RGB直方圖_histogram_split_merge.ipynb\n前言 基本修圖技能學習完之後，\n再來我們要來學一些比較進階會使用的修圖技巧囉！\n運用 OpenCV 顯示圖片RGB直方圖、分離與合併RGB三通道 運用 OpenCV 分離RGB三通道 def split_RGBThreeChannel(img): (B, G, R) = cv2.split(img) # 3 channel # make all zeros channel zeros = np.zeros(img.shape[:2], dtype = np.uint8) print(\u0026#34;R channel:\u0026#34;) show_img(merge_RGBThreeChannel(R=R, G=zeros, B=zeros)) print(\u0026#34;G channel:\u0026#34;) show_img(merge_RGBThreeChannel(R=zeros, G=G, B=zeros)) print(\u0026#34;B channel:\u0026#34;) show_img(merge_RGBThreeChannel(R=zeros, G=zeros, B=B)) return R, G, B 分離RGB三通道使用 「cv2.split」 的方式，\n有些時候我們在進階修圖會針對單一通道的顏色進行調整，\n此時分離GRB三個通道就是非常重要的技術了！\n運用 OpenCV 合併RGB三通道 def merge_RGBThreeChannel(R, G, B): img = cv2.merge([B, G, R]) return img 使用 「cv2.merge」 這個函數，\n通常我們在進階修圖中，會先分離RGB三通道，\n進行各自顏色通道的調整後，\n最後合併RGB三通道，完成我們的修圖。\n運用 OpenCV 顯示圖片RGB直方圖 def show_histogram(img): # 畫出 RGB 三種顏色的分佈圖 color = (\u0026#39;b\u0026#39;,\u0026#39;g\u0026#39;,\u0026#39;r\u0026#39;) plt.style.use(\u0026#39;dark_background\u0026#39;) plt.figure(figsize=(10,5)) for idx, color in enumerate(color): histogram = cv2.calcHist([img],[idx],None,[256],[0, 256]) plt.plot(histogram, color = color) plt.xlim([0, 256]) plt.show() 運用前面分離RGB三通道的技術，\n我們可以針對RGB各通道各自畫出他們的直方圖。\n在之後的進階修圖中，我們會觀察各顏色通道分佈的直方圖，\n更細部的調整各顏色通道的數值。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 14 - 進階修圖1，運用 OpenCV 顯示圖片直方圖、分離與合併RGB通道 show histogram, split, merge RGB channel\nReference https://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/\nhttps://blog.csdn.net/sunny2038/article/details/9080047\nhttps://www.jianshu.com/p/9fd339f806a7\n","date":"2020-09-26T01:13:29+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424FAfqSKoixc.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-histogram-split-merge-rgb-channel/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】14 - 運用 OpenCV 顯示圖片直方圖、分離與合併 RGB 通道"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 繼續請我們的貓貓助教來幫忙XDDDD\n-\u0026gt; 此篇文章的程式碼 github Day13_文字查色碼顯示色碼_text_get_show_RGBColorCode.ipynb\n前言 完成下一份我們的大作前，我們先來補充一些基本技能吧！\n畢竟基本技能才是最經常被我們使用的呢！\n運用 OpenCV 在圖片上寫文字、查色碼、顯示色碼顏色 運用 OpenCV 在圖片上寫文字 def write_text(img): text = \u0026#34;FONT_HERSHEY_SIMPLEX\u0026#34; position = (10, 40) font = cv2.FONT_HERSHEY_SIMPLEX size = 1 color = (0, 255, 255) thickness = 1 lineType = cv2.LINE_AA cv2.putText(img, text, position, font, size, color, thickness, lineType) return img 重點在 「cv2.putText」 那一行，\n其他都是在設定相關的參數~ 可以依照需求變動。\ncv2.putText(img, text, position, font, size, color, thickness, lineType)\nimg 原圖 text 要寫的文字 position 寫文字的位置 font 字體 (可以參考下圖) size 字體大小 color 字的顏色 thickness 字的線條寬度 lineType 線條種類 字體範例參考 關於字體的種類，可以見以下的範例字型：\ncv2.putText(img, \u0026#34;FONT_HERSHEY_PLAIN\u0026#34;, (10, 80), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, \u0026#34;FONT_HERSHEY_DUPLEX\u0026#34;, (10, 120), cv2.FONT_HERSHEY_DUPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, \u0026#34;FONT_HERSHEY_COMPLEX\u0026#34;, (10, 160), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, \u0026#34;FONT_HERSHEY_TRIPLEX\u0026#34;, (10, 200), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, \u0026#34;FONT_HERSHEY_COMPLEX_SMALL\u0026#34;, (10, 240), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, \u0026#34;FONT_HERSHEY_SCRIPT_SIMPLEX\u0026#34;, (10, 280), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, \u0026#34;FONT_HERSHEY_SCRIPT_COMPLEX\u0026#34;, (10, 320), cv2.FONT_HERSHEY_SCRIPT_COMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) (個人常用區) 我經常使用的程式碼片段 - 傳入 (x, y) 這部份就是我的個人偏好了XD，\n讀者參考上述的內容應該也能改出具有個人特色的文字內容！\ndef write_text(img, text, position = (0, 0)): font = cv2.FONT_HERSHEY_SIMPLEX size = 1 color = (0, 255, 255) thickness = 2 lineType = cv2.LINE_AA cv2.putText(img, text, position, font, size, color, thickness, lineType) return img (個人常用區) 我經常使用的程式碼片段 - 傳入 bbox, 顯示文字在 bbox 上方 def write_text(img, bbox, text=\u0026#34;\u0026#34;, color=(0, 0, 0)): position = (int(bbox[0]), int(bbox[1])-30) cv2.putText(img, text, position, cv2.FONT_HERSHEY_DUPLEX, 1, color, 1, cv2.LINE_AA) return img 運用 OpenCV 查色碼 查詢色碼我們這裡提供兩種方法，這裡我們想查詢貓身上的顏色，大約是 「x=500, y=400」 的位置\n大約在這裡!!!\n運用 OpenCV 查色碼 - 方法一 def get_RGBColorCode(img, x=0 ,y=0): # method 1 (B, G, R) = cv2.split(img) # 3 channel b, g, r = B[y ,x], G[y ,x], R[y ,x] print(\u0026#34;RGB = ({}, {}, {})\u0026#34;.format(r, g, b)) 我們的第一種方法，是將「整張圖」的RGB拆成三個通道，\n再由各自的通道去找對應的座標點，\n得到我們指定位置的RGB。\nRGB = (193, 163, 125)\n運用 OpenCV 查色碼 - 方法二 def get_RGBColorCode(img, x=0 ,y=0): # method 2 b, g, r = img[y, x] print(\u0026#34;RGB = ({}, {}, {})\u0026#34;.format(r, g, b)) 我們的第二種方法，是直接找指定座標點的rgb，\n寫法看起來就乾淨很多，要注意順序是「先y後x」，\n跟我們的直覺有些不同。\nRGB = (193, 163, 125)\n運用 OpenCV 查色碼 - HEX def get_RGBColorCode(img, x=0 ,y=0): # method 2 b, g, r = img[y, x] print(\u0026#34;RGB = ({}, {}, {})\u0026#34;.format(r, g, b)) # 轉成我們常見的 Hex 色碼 rgb_hex = hex(r)[-2:] + hex(g)[-2:] + hex(b)[-2:] print(\u0026#34;RGB Hex = #{}\u0026#34;.format(rgb_hex)) 最後提供一個我們常見的「HEX色碼」的產生方法，\n方便我們查詢顏色。\nRGB Hex = #c1a37d\n運用 OpenCV 顯示色碼顏色 運用 OpenCV 顯示色碼顏色 - RGB def show_RGBColorCode(r=0 ,g=0, b=0): img = np.array([[[b, g, r]]], dtype=np.uint8) show_img(img) 我們傳入「rgb的三個值」，然後直接建立一個有三個顏色的通道，產生結果顏色。\n運用 OpenCV 顯示色碼顏色 - HEX def show_RGBColorCode_fromHEX(hex=\u0026#39;000000\u0026#39;): try: r, g, b = int(hex[0:2], 16), int(hex[2:4], 16), int(hex[4:6], 16) img = np.array([[[b, g, r]]], dtype=np.uint8) show_img(img) except: print(\u0026#34;HEX Color Code Error !\u0026#34;) 這個是特別為了 「HEX色碼」 產生顏色所寫的，\n使用 「try-except」 是避免輸入色碼錯誤，\n不過這裡寫的是簡單版的，只能除掉簡單的輸入錯誤而已哦~\n不過沒有人會想沒事故意輸入錯誤吧XDDD\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 13 - 基本修圖5，運用 OpenCV 在圖片上寫文字、查色碼、顯示色碼顏色 write text, get and show RGB Color Code\nReference https://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/\nhttps://blog.csdn.net/sunny2038/article/details/9080047\nhttps://www.itread01.com/content/1540965316.html\n","date":"2020-09-25T01:08:36+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/cv2_color_code.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-write-text-rgb-color-code/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】13 - 運用 OpenCV 在圖片上寫文字、查色碼、顯示色碼顏色 (cv2.putText)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day12_畫線畫矩形畫橢圓_lines_rectangle_ellipse.ipynb\n前言 完成下一份我們的大作前，我們先來補充一些基本技能吧！\n畢竟基本技能才是最經常被我們使用的呢！\n運用 OpenCV 畫線、畫矩形、畫橢圓 運用 OpenCV 畫線 def draw_lines(img): start_point = (20, 20) end_point = (120, 120) color = (0, 255, 0) # green thickness = 3 # 寬度 cv2.line(img, start_point, end_point, color, thickness) return img 重點在 「cv2.line」 那一行，\n其他都是在設定相關的參數~ 可以依照需求變動。\ncv2.line(img, start_point, end_point, color, thickness)\nimg 原圖片 start_point 線條起點 end_point 線條終點 color 線條顏色 thickness 線條粗細 運用 OpenCV 畫矩形 def draw_rectangle(img): left_up = (20, 60) right_down = (180, 120) color = (0, 0, 255) # red thickness = 2 # 寬度 (-1 表示填滿) cv2.rectangle(img, left_up, right_down, color, thickness) cv2.rectangle(img, (125, 20), (175, 40), (255, 0, 255), -1) return img 重點在 「cv2.rectangle」 那一行，\n其他都是在設定相關的參數~ 可以依照需求變動。\ncv2.rectangle(img, left_up, right_down, color, thickness)\nimg 原圖片 left_up 矩形左上角座標 right_down 矩形右下角座標 color 矩形顏色 thickness 矩形線條粗細 (-1 表示填滿) 比較特別的為 「thickness = -1」，具有填滿矩形的功能。\n另外補充說明，「left_up 矩形左上角座標」 與 「right_down 矩形右下角座標」，\n並非一定要是「左上\u0026amp;右下」，「只要能描述矩形四個端點即可」，\n例如：也可以給「左下\u0026amp;右上」\n運用 OpenCV 畫橢圓 def draw_ellipse(img): center = (100, 100) #圓心 axes = (25, 55) #橢圓的長半軸與短半軸的大小 rotateAngle = 45 #橢圓的旋轉角度 (傾斜 +45度) startAngle = 0 #橢圓弧的起始角度 endAngle = 360 #橢圓弧的終止角度 (180 表示只畫一半) color = (255, 0, 0) # blue thickness = 2 # 寬度 (-1 表示填滿) cv2.ellipse(img, center, axes, rotateAngle, startAngle, endAngle, color, thickness) cv2.ellipse(img, (50, 50), (20, 30), -45, 0, 180, (0, 255, 255), -1) return img 重點在 「cv2.ellipse」 那一行，\n其他都是在設定相關的參數~ 可以依照需求變動。\ncv2.ellipse(img, center, axes, rotateAngle, startAngle, endAngle, color, thickness)\nimg 原圖片 center 圓心 axes 橢圓的長半軸與短半軸的大小 rotateAngle 橢圓的旋轉角度 (傾斜角度) startAngle 橢圓弧的起始角度 endAngle 橢圓弧的終止角度 (180 表示只畫一半) color 橢圓的顏色 thickness 橢圓的線條粗細 (-1 表示填滿) 比較特別的為 「thickness = -1」，具有填滿橢圓的功能。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 12 - 基本修圖4，運用 OpenCV 畫線、畫矩形、畫橢圓 draw lines, draw rectangle, draw ellipse\nReference https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.full.html\nhttps://blog.gtwang.org/programming/opencv-basic-image-read-and-write-tutorial/\nhttps://blog.csdn.net/u011520181/article/details/83933325\nhttps://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/\nhttps://blog.gtwang.org/tag/opencv/\nhttps://www.geeksforgeeks.org/python-opencv-cv2-circle-method/\n","date":"2020-09-24T01:05:44+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424S0kU6NYIVq.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-lines-rectangle-ellipse/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】12 - 運用 OpenCV 畫線、畫矩形、畫框、畫橢圓"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day11_建立新空白圖畫點畫圓_new_pictures_point_circle.ipynb\n前言 完成下一份我們的大作前，我們先來補充一些基本技能吧！\n畢竟基本技能才是最經常被我們使用的呢！\n運用 OpenCV 建立新空白圖、畫點、畫圓 建立新空白圖 先定義好我們要的圖片大小，記得如果要彩色的圖片，維度需要設3 (才有RGB通道)，\n第二行照我們設定的圖片大小形成圖片，\n「因為全部的值都是0，所以圖片是全黑的」～\n# 建立全黑的新圖片 100*100 shape = (100, 100, 3) # y, x, RGB origin_img = np.zeros(shape, np.uint8) 那要怎麼建立全白的圖片呢？ 可以分為兩種方式，第一種就是透過 「np.full」 直接建立一個填滿255的圖片，\n第二種一樣先建立一個全黑的圖片的，再透過 img.fill(255) 填滿全部白色。\nshape = (100, 100, 3) # y, x, RGB # 第一種方法，直接建立全白圖片 100*100 origin_img = np.full(shape, 255).astype(np.uint8) # 第二種方法，一樣先建立全黑的圖片，再將全部用白色填滿。 origin_img = np.zeros(shape, np.uint8) origin_img.fill(255) 運用 OpenCV 畫點 def draw_points(img): point_size = 1 point_color = (0, 0, 255) # red thickness = 4 # 要畫的點座標 points_list = [(80, 20), (20, 80)] for point in points_list: cv2.circle(img, point, point_size, point_color, thickness) return img 其實重點只有 「cv2.circle」 一行，\n其他都是在設定相關的參數~ 可以依照需求變動。\ncv2.circle(img, point, point_size, point_color, thickness)\nimg 原圖片 point 要畫的點座標 point_size 點的大小 point_color 點的顏色 thickness 點的粗細(這裡值類似點的大小的效果) 運用 OpenCV 畫圓 def draw_cirlces(img): circle_mid = (80, 80) color = (0, 255, 0) # green cv2.circle(img, circle_mid, 10, color, 0) # 黃色圓圈，線條寬度為 3 px cv2.circle(img,(30, 30), 30, (0, 255, 255), 3) # yellow # 藍色實心圓圈 cv2.circle(img,(50, 50), 15, (255, 0, 0), -1) # blue return img 一樣重點也是一行 「cv2.circle」，\ncv2.circle(img, point, point_size, point_color, thickness)\nimg 原圖片 point 要畫的圓圓心 point_size 圓的大小 (也就是半徑) point_color 圓的顏色 thickness 圓的線條粗細， 「-1 表示將圓填滿顏色」 眼尖的讀者應該有發現，其實我們「畫點畫圓」是使用一樣的函數哦!\n只要將圓縮得很小就是點了！\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 11 - 基本修圖3，OpenCV 建立新空白圖、畫點、畫圓 create new pictures, draw points and draw circle\nReference https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.full.html\nhttps://blog.gtwang.org/programming/opencv-basic-image-read-and-write-tutorial/\nhttps://blog.csdn.net/u011520181/article/details/83933325\nhttps://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/\nhttps://blog.gtwang.org/tag/opencv/\nhttps://www.geeksforgeeks.org/python-opencv-cv2-circle-method/\n","date":"2020-09-23T01:04:34+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424i9FU8hZWcx.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-new-pictures-points-circle/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】11 - OpenCV 建立新空白圖、畫點、畫圓"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 總覺得總集篇應該要找個有人的照片，只好讓本人出現獻醜一下了~\n比較一下原圖 (先撇除原圖就已經拍很好看的問題，我們來專心討論修圖哈哈哈)\n-\u0026gt; 此篇文章的程式碼 github Day10_日系濾鏡總集篇_Japanese_style_filter.ipynb\n前言 我們已經完成了製作日系濾鏡的整個修圖過程囉！\n如果要參考某一個特定的部分，請參考以下連結：\n下方的程式碼會「著重在整理並加入參數」使「可用性更高」！\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) [【Day5】](https://ithelp.ithome.com.tw/articles/10236901) 2. 加強飽和度 (調整飽和度) [【Day6】](https://ithelp.ithome.com.tw/articles/10237471) 3. 將照片調成冷色調 [【Day7】](https://ithelp.ithome.com.tw/articles/10239714) 4. 增添顆粒感 [【Day8】](https://ithelp.ithome.com.tw/articles/10240032) 5. 降低對比 [【Day9】](https://ithelp.ithome.com.tw/articles/10240334) 6. 降低高光 運用 OpenCV 製作屬於自己的濾鏡吧 最後我們將前六天的成果，在函數中加入一些「可變動的參數」，\n這樣可調整性就會變得非常高，\n「以後我們想要生成什麼樣的濾鏡，只要調一調參數就可以囉！」\n至於說明的部分我直接打在下方程式碼裡面，方便直接對照。\ndef japanese_style_filter(img): print(\u0026#34;1. 調亮光線 (調整光線)\u0026#34;) print(\u0026#34;2. 加強飽和度 (調整飽和度)\u0026#34;) img = modify_lightness_saturation(img, lightness=0, saturation=50) # 單位: +- % show_img(img) print(\u0026#34;3. 將照片調成冷色調\u0026#34;) img = modify_color_temperature(img, cold_rate=20) # 看你要+多冷 show_img(img) print(\u0026#34;4. 增添顆粒感\u0026#34;) img = gaussian_noise(img, mean=0, sigma=0.05) # mean 平均, sigma 標準差 show_img(img) print(\u0026#34;5. 降低對比\u0026#34;) img = modify_contrast_and_brightness(img, brightness=20 , contrast=-35) # -255 ~ 255 show_img(img) print(\u0026#34;6. 降低高光\u0026#34;) img = reduce_highlights(img, light_threshold=255) # 光源的 threshold 以上會被做降光處理 show_img(img) return img 在網頁中運用 colab 直接產生自己的濾鏡結果 在網頁中運用 colab 直接產生自己的濾鏡連結：\nhttps://colab.research.google.com/drive/1TWX7JZpYitVCoDsP9E4mFo6D9cX1CW8R?usp=sharing\n嘿對，我用了一個線上就能直接執行的版本。\n這樣又更方便了~~~\n使用方式很簡單~~~\n1. 打開網頁，執行階段 -\u0026gt; 全部執行 2. 往下找，找到要你上傳圖片的地方，上傳自己要修的照片 3. 上傳完成後，結果就出來囉 (還附上變化過程呢！) 後記 (附上變化過程的用途) 其實附上變化過程就是更方便「讓我們知道哪裡需要調整參數」，\n如果「看哪個階段結果不是很滿意的話」，「直接去改那個階段的數字」就好囉！\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 10 - 日系濾鏡總集篇，運用 OpenCV 製作屬於自己的濾鏡吧 (內含可於網頁上直接完成濾鏡的 colab )](https://ithelp.ithome.com.tw/articles/10240974) ","date":"2020-09-22T02:16:04+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424N551L43i7R.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-japanese-filter/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】10 - 日系濾鏡總集篇，運用 OpenCV 製作屬於自己的濾鏡吧 (內含可於網頁上直接完成濾鏡的 colab )"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 為了凸顯本文效果，我有「特別將效果加強」，但實際使用還是要「依個人喜好去調整」 (可以看到高光源的地方都已經被模糊處理掉了！)\n-\u0026gt; 此篇文章的程式碼 github Day09_降低高光_reduce_highlights.ipynb\n前言 我們繼續製作屬於自己的日系濾鏡啦！\n日系濾鏡的修圖過程，大致上有以下步驟：\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) [【Day5】](https://ithelp.ithome.com.tw/articles/10236901) 2. 加強飽和度 (調整飽和度) [【Day6】](https://ithelp.ithome.com.tw/articles/10237471) 3. 將照片調成冷色調 [【Day7】](https://ithelp.ithome.com.tw/articles/10239714) 4. 增添顆粒感 [【Day8】](https://ithelp.ithome.com.tw/articles/10240032) 5. 降低對比 [【Day9】](https://ithelp.ithome.com.tw/articles/10240334) 6. 降低高光 接下來的日子裡我們就來一個個用程式碼實現！\n運用 OpenCV 降低圖片的高光 def\" data-lang=\"python\"\u003edef\"\u003efor contour in contours: x, y, w, h = cv2.boundingRect(contour) img_zero[y:y+h, x:x+w] = 255 mask = img_zero 目的就是為了產生遮罩用的圖片，方便我們做 「cv2.illuminationChange」 這個函數的運算。\n最後來看一下比較正常一點的結果 第一張圖片我所設的參數為 「alpha=0.2, beta=0.4」，\n但個人覺得他模糊高光的效果有點過頭了，\n因此調了一下參數，產生了以下 「alpha=0.2, beta=0.2」 的版本。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 9 - 日系濾鏡6，運用 OpenCV 降低圖片的高光 reduce highlights](https://ithelp.ithome.com.tw/articles/10240334) Reference https://www.pythonf.cn/read/57067\nhttps://kknews.cc/zh-tw/code/oka9mbm.html\nhttps://blog.csdn.net/zh_jessica/article/details/77967650\nhttps://www.cnblogs.com/lfri/p/10627595.html\n","date":"2020-09-21T02:14:22+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424JpvfC4TYJF.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-reduce-highlights/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】9 - 運用 OpenCV 降低圖片的高光"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 (增加對比度的結果：白的更白、黑的更黑) -\u0026gt; 此篇文章的程式碼 github Day08_調整對比度_modify_contrast.ipynb\n前言 我們繼續製作屬於自己的日系濾鏡啦！\n日系濾鏡的修圖過程，大致上有以下步驟：\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) [【Day5】](https://ithelp.ithome.com.tw/articles/10236901) 2. 加強飽和度 (調整飽和度) [【Day6】](https://ithelp.ithome.com.tw/articles/10237471) 3. 將照片調成冷色調 [【Day7】](https://ithelp.ithome.com.tw/articles/10239714) 4. 增添顆粒感 [【Day8】](https://ithelp.ithome.com.tw/articles/10240032) 5. 降低對比 6. 降低高光 接下來的日子裡我們就來一個個用程式碼實現！\n運用 OpenCV 改變圖片的對比度 網路上常見的方法 def modify_contrast_and_brightness(img): # 公式： Out_img = alpha*(In_img) + beta # alpha: alpha參數 (\u0026gt;0)，表示放大的倍数 (通常介於 0.0 ~ 3.0之間)，能夠反應對比度 # a\u0026gt;1時，影象對比度被放大， 0\u0026lt;a\u0026lt;1時 影象對比度被縮小。 # beta: beta参数，用來調節亮度 # 常數項 beta 用於調節亮度，b\u0026gt;0 時亮度增強，b\u0026lt;0 時亮度降低。 array_alpha = np.array([2.0]) # contrast array_beta = np.array([0.0]) # brightness # add a beta value to every pixel img = cv2.add(img, array_beta) # multiply every pixel value by alpha img = cv2.multiply(img, array_alpha) # 所有值必須介於 0~255 之間，超過255 = 255，小於 0 = 0 img = np.clip(img, 0, 255) print(\u0026#34;增加對比度 - 網路上常見的方法 (但沒有實現黑的更黑這件事): \u0026#34;) show_img(img) 「網路上常見的方法 - 增加對比度」結果如下：\n我們可以看見，圖片顏色的對比度確實有增加了(「白的更白」)，\n但是卻沒有實現「「黑的更黑」」這件事，\n為什麼網路上常見的方法會這樣呢?\n我們參考一張 OpenCV 官方論壇的圖來解釋這件事情：\n從右上角的圖片中，我們可以看到我們實現的方式是透過「乘上某一個 alpha 值」，\n導致「分佈被放大，再進行縮小」，\n這樣確實能讓「越大的數字(接近255)，被放的更大(超過255後，被計算為255)」，\n也就是說「白的更白」這件事就被這樣實現了，\n但「黑的更黑」呢? 上面公式就是出現了這樣的問題，\n我們發現越接近0的值，透過上面的公式沒有辦法更接近0，\n甚至反而因為放大效果，離0更遠了?!\n這樣就不太符合我們的增加對比度的意義了，\n我們想要的是「白的更白、黑的更黑」效果。\n改良版的方法 - 實現增加對比度「白的更白、黑的更黑」 先看一下結果圖 - 「增加對比度 (白的更白，黑的更黑)」：\n結果圖 - 「減少對比度 (白黑都接近灰，分不清楚)」：\n應該可以很明顯能理解我們所說的意思：\n增加對比度: 「白的更白，黑的更黑」 減少對比度: 「白黑都接近灰，分不清楚」 所以是怎麼實現的呢?\ndef modify_contrast_and_brightness2(img, brightness=0 , contrast=100): # 上面做法的問題：有做到對比增強，白的的確更白了。 # 但沒有實現「黑的更黑」的效果 import math brightness = 0 contrast = -100 # - 減少對比度/+ 增加對比度 B = brightness / 255.0 c = contrast / 255.0 k = math.tan((45 + 44 * c) / 180 * math.pi) img = (img - 127.5 * (1 - B)) * k + 127.5 * (1 + B) # 所有值必須介於 0~255 之間，超過255 = 255，小於 0 = 0 img = np.clip(img, 0, 255).astype(np.uint8) print(\u0026#34;減少對比度 (白黑都接近灰，分不清楚): \u0026#34;) show_img(img) 改良版的公式，我們巧妙運用三角函數的特性，\nc = contrast / 255.0\ncontrast = (-255, +255)，代表 c = (-1, +1)\nk = math.tan((45 + 44 * c) / 180 * math.pi)\n將 c 代入，得到約 「tan((1~89)/180*pi)」，\n我們來思考一下tan的特性：\n「tan \u0026gt; 45度」: 「y\u0026gt;x」，為「分數 (0~1)」，表示「更接近0」 「tan \u0026lt; 45度」: 「x\u0026gt;y」，為「假分數 (\u0026gt;1)」，表示「更遠離0」 那我們再看下一行：\nimg = (img - 127.5 * (1 - B)) * k + 127.5 * (1 + B)\n我們從255的一半 127.5開始看，\n後面127.5是正的\n前面127.5是負的，因為乘上k，\nk可以決定要「更大的負(整個式子結果更負)」或「更小的負(整個式子結果更正)」\n因為我們是從中間 127.5 開始切，\n所以就會有「小於127.5 往更負的方向跑，大於127.5 往更正的方向跑」的現象，\n但隨著值越大，一定會超出 0 ~ 255 的顏色範圍，\n所以最後我們用：\nimg = np.clip(img, 0, 255).astype(np.uint8)\n所有值必須介於 0~255 之間，超過255 = 255，小於 0 = 0\n完成了改變對比度的演算法。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽\n【沒錢ps,我用OpenCV!】Day 8 - 日系濾鏡5，運用 OpenCV 改變圖片的對比度 modify contrast (內含：網路上常見錯誤調整對比度方式的分析)\nReference https://www.pythonf.cn/read/57067\nhttps://kknews.cc/zh-tw/code/oka9mbm.html\nhttps://blog.csdn.net/zh_jessica/article/details/77967650\nhttps://www.cnblogs.com/lfri/p/10627595.html\n","date":"2020-09-20T02:13:17+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424QZwRnbCI72.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-modify-contrast/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】8 - 運用 OpenCV 改變圖片的對比度 modify contrast (內含：網路上常見錯誤調整對比度方式的分析)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 (增加顆粒感) 比較一下原圖：\n-\u0026gt; 此篇文章的程式碼 github Day07_增添顆粒感(增加高斯噪聲)_add_gaussian_noise.ipynb\n前言 我們繼續製作屬於自己的日系濾鏡啦！\n日系濾鏡的修圖過程，大致上有以下步驟：\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) [【Day5】](https://ithelp.ithome.com.tw/articles/10236901) 2. 加強飽和度 (調整飽和度) [【Day6】](https://ithelp.ithome.com.tw/articles/10237471) 3. 將照片調成冷色調 [【Day7】](https://ithelp.ithome.com.tw/articles/10239714) 4. 增添顆粒感 5. 降低對比 6. 降低高光 接下來的日子裡我們就來一個個用程式碼實現！\n運用 OpenCV 為圖片增加一些顆粒感 (增加高斯噪點) def gaussian_noise(img, mean=0, sigma=0.1): # int -\u0026gt; float (標準化) img = img / 255 # 隨機生成高斯 noise (float + float) noise = np.random.normal(mean, sigma, img.shape) # noise + 原圖 gaussian_out = img + noise # 所有值必須介於 0~1 之間，超過1 = 1，小於0 = 0 gaussian_out = np.clip(gaussian_out, 0, 1) # 原圖: float -\u0026gt; int (0~1 -\u0026gt; 0~255) gaussian_out = np.uint8(gaussian_out*255) # noise: float -\u0026gt; int (0~1 -\u0026gt; 0~255) noise = np.uint8(noise*255) print(\u0026#34;gaussian noise: \u0026#34;) show_img(noise) print(\u0026#34;Picture add gaussian noise: \u0026#34;) show_img(gaussian_out) # return gaussian_out , noise 我們實際上做了什麼事呢? 我們先認識什麼是「高斯噪聲」 要講解這個我們可以先從理解「高斯分佈」開始說明，\n-\u0026gt; 那什麼是「高斯分佈」呢?\n其實只是講得比較厲害一點而已，\n就是我們所熟悉的「常態分佈」啦~\n任何事情分佈的常態，都會接近「高斯分佈」，\n所以我們今天將噪聲取一個「高斯分佈」的值，\n就成為我們的「高斯噪聲」。\n我們的「高斯噪聲」大概長這樣(「平均=0，標準差 = 0.1」)：\n(請注意：雖然是「常態分佈」，但值依然是「隨機的」，所以「每次產生的結果都會不同」！)\n將高斯噪聲疊加到我們的圖上，產生我們要的「顆粒感」 講簡單點，其實就是把噪聲到圖上加起來而已哈哈哈哈。\n上面已經有「標準差 = 0.1」的結果，\n我們下面換一個比較大一點的「標準差 = 0.5」的結果，我們看看有什麼樣的效果~~~\n「說不定有人喜歡這種圖片的感覺?」\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 7 - 日系濾鏡4，運用 OpenCV 為圖片增加一些顆粒感 (增加高斯噪點) add gaussian noise](https://ithelp.ithome.com.tw/articles/10239714) Reference https://www.pythonf.cn/read/57067\nhttps://kknews.cc/zh-tw/code/oka9mbm.html\nhttps://blog.csdn.net/zh_jessica/article/details/77967650\nhttps://www.cnblogs.com/lfri/p/10627595.html\n","date":"2020-09-19T02:11:01+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424p9lMe3TYvp.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-add-gaussian-noise/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】7 - 運用 OpenCV 為圖片增加一些顆粒感 (增加高斯噪點) add gaussian noise"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day06_調整色調色溫白平衡_modify_color_temperature.ipynb\n前言 我們繼續製作屬於自己的日系濾鏡啦！\n日系濾鏡的修圖過程，大致上有以下步驟：\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) [【Day5】](https://ithelp.ithome.com.tw/articles/10236901) 2. 加強飽和度 (調整飽和度) [【Day6】](https://ithelp.ithome.com.tw/articles/10237471) 3. 將照片調成冷色調 4. 增添顆粒感 5. 降低對比 6. 降低高光 接下來的日子裡我們就來一個個用程式碼實現！\n運用 OpenCV 調整色調(冷色系/暖色系)、色溫(白平衡) def modify_color_temperature(img): # ---------------- 冷色調 ---------------- # # height = img.shape[0] # width = img.shape[1] # dst = np.zeros(img.shape, img.dtype) # 1.計算三個通道的平均值，並依照平均值調整色調 imgB = img[:, :, 0] imgG = img[:, :, 1] imgR = img[:, :, 2] # 調整色調請調整這邊~~ # 白平衡 -\u0026gt; 三個值變化相同 # 冷色調(增加b分量) -\u0026gt; 除了b之外都增加 # 暖色調(增加r分量) -\u0026gt; 除了r之外都增加 bAve = cv2.mean(imgB)[0] gAve = cv2.mean(imgG)[0] + 20 rAve = cv2.mean(imgR)[0] + 20 aveGray = (int)(bAve + gAve + rAve) / 3 # 2. 計算各通道增益係數，並使用此係數計算結果 bCoef = aveGray / bAve gCoef = aveGray / gAve rCoef = aveGray / rAve imgB = np.floor((imgB * bCoef)) # 向下取整 imgG = np.floor((imgG * gCoef)) imgR = np.floor((imgR * rCoef)) # 3. 變換後處理 # for i in range(0, height) # for j in range(0, width) # imgb = imgB[i, j] # imgg = imgG[i, j] # imgr = imgR[i, j] # if imgb \u0026gt; 255 # imgb = 255 # if imgg \u0026gt; 255 # imgg = 255 # if imgr \u0026gt; 255 # imgr = 255 # dst[i, j] = (imgb, imgg, imgr) # 將原文第3部分的演算法做修改版，加快速度 imgb = imgB imgb[imgb \u0026gt; 255] = 255 imgg = imgG imgg[imgg \u0026gt; 255] = 255 imgr = imgR imgr[imgr \u0026gt; 255] = 255 cold_rgb = np.dstack((imgb, imgg, imgr)).astype(np.uint8) print(\u0026#34;Cold color:\u0026#34;) print(cold_rgb.shape) show_img(cold_rgb) 我們實際上做了什麼事呢? 其實我每天都在程式碼中把註解寫得很清楚了，\n這邊越來越不知道要寫什麼了XD\n我們所說的「冷、暖色調」，其實就是「色溫變化的結果」，\n而「色溫的變化」，通常也就伴隨著「「白平衡的破壞」」。\n一個白平衡的圖片，色調並不會明顯偏冷或偏暖，\n但因為人類視覺上的觀感，有時我們能透過色調使圖片更有特色。\n例如：\n想呈現「「冷酷、冰冷」」的照片，適合使用「冷色調」。 想呈現「「溫暖、熱情」」的照片，適合使用「暖色調」。 -\u0026gt; 關於修改演算法的部分：\n原文的演算法使用「兩層for迴圈」進行計算，\n但既然都「使用了numpy矩陣」，使用「矩陣算法才是更有效率」的方式，\n因此才有修改版的寫法，如果讀者有興趣可以自行試試執行時間的差距。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 6 - 日系濾鏡3，運用 OpenCV 調整色調(冷色系/暖色系)、色溫(白平衡)modify color temperature, white balance](https://ithelp.ithome.com.tw/articles/10237471) Reference https://blog.csdn.net/weixin_39853245/article/details/101199768\nhttps://blog.csdn.net/qq_36187544/article/details/97657927?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\u0026amp;depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param\nhttps://blog.csdn.net/wzwxiaozheng/article/details/38434391?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param\u0026amp;depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param\nhttps://medium.com/%E9%9D%92%E8%98%8B%E6%9E%9C3c/%E6%94%9D%E5%BD%B1%E5%9F%BA%E7%A4%8E%E8%A8%93%E7%B7%B4-%E7%99%BD%E5%B9%B3%E8%A1%A1%E6%98%AF%E4%BB%80%E9%BA%BC-%E8%89%B2%E6%BA%AB%E8%A9%B2%E6%80%8E%E9%BA%BC%E8%AA%BF%E6%95%B4-efd8eed4df2b\nhttps://kknews.cc/zh-tw/photography/9p425vl.html\n","date":"2020-09-18T02:19:22+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424QGm94UZZ5T.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-temperature-white-balance/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】6 - 運用 OpenCV 調整色調(冷色系/暖色系)、色溫(白平衡)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day05_調整亮度飽和度(透過HSL顏色空間)_modify_lightness_saturation.ipynb\n前言 我們繼續製作屬於自己的日系濾鏡啦！\n日系濾鏡的修圖過程，大致上有以下步驟：\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) [【Day5】](https://ithelp.ithome.com.tw/articles/10236901) 2. 加強飽和度 (調整飽和度) 3. 將照片調成冷色調 4. 增添顆粒感 5. 降低對比 6. 降低高光 接下來的日子裡我們就來一個個用程式碼實現！\n運用 OpenCV 調整亮度、飽和度 def modify_lightness_saturation(img): origin_img = img # 圖像歸一化，且轉換為浮點型 fImg = img.astype(np.float32) fImg = fImg / 255.0 # 顏色空間轉換 BGR -\u0026gt; HLS hlsImg = cv2.cvtColor(fImg, cv2.COLOR_BGR2HLS) hlsCopy = np.copy(hlsImg) lightness = 0 # lightness 調整為 \u0026#34;1 +/- 幾 %\u0026#34; saturation = 300 # saturation 調整為 \u0026#34;1 +/- 幾 %\u0026#34; # 亮度調整 hlsCopy[:, :, 1] = (1 + lightness / 100.0) * hlsCopy[:, :, 1] hlsCopy[:, :, 1][hlsCopy[:, :, 1] \u0026gt; 1] = 1 # 應該要介於 0~1，計算出來超過1 = 1 # 飽和度調整 hlsCopy[:, :, 2] = (1 + saturation / 100.0) * hlsCopy[:, :, 2] hlsCopy[:, :, 2][hlsCopy[:, :, 2] \u0026gt; 1] = 1 # 應該要介於 0~1，計算出來超過1 = 1 # 顏色空間反轉換 HLS -\u0026gt; BGR result_img = cv2.cvtColor(hlsCopy, cv2.COLOR_HLS2BGR) result_img = ((result_img * 255).astype(np.uint8)) print(\u0026#34;High Saturation:\u0026#34;) show_img(result_img) 我們實際上做了什麼事呢? 我們先來認識 「HLS 顏色空間」，\n(以下圖片引用自：https://zhuanlan.zhihu.com/p/67930839)\n從上圖我們可以看見在 「HLS 顏色空間」 中色彩分佈的三維座標，\n分別是 「Hue(顏色)」、「Lightness(亮度)」、「Saturation(飽和度)」，\n我們能透過這三維找到我們所需要的顏色，\n至於看到這三維座標所代表的意義，\n相信你們已經能了解我們怎麼改變 「Lightness(亮度)」與「Saturation(飽和度)」了吧！\n我們首先將「int範圍(0255)」的顏色表示轉換為「浮點數(01)」，\n這樣方便我們做比例上的運算。\n在上面提供的程式碼裡面，最後的結果會改變為 「1 + x/100.0」，\n我們改變的「lightness」, 「saturation」值都是對應 「x改變的% (正或負)」 大小。\n避免運算結果超過範圍，我們在運算後，將「所有超過1的值皆設為1」。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 5 - 日系濾鏡2，運用 OpenCV 調整亮度、飽和度(透過轉移至 HLS 顏色空間) modify lightness, saturation](https://ithelp.ithome.com.tw/articles/10236901) Reference http://help.corel.com/paintshop-pro/v20/main/ct/documentation/index.html#page/Corel_PaintShop_Pro/Adjusting_hue_and_saturation.html\nhttps://www.itdaan.com/tw/6488189fb5d7d60b163039cd0a004f70\nhttps://zhuanlan.zhihu.com/p/67930839\n","date":"2020-09-17T02:09:19+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424PScjF3eWKg.jpg","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-lightness-saturation/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】5 - 運用 OpenCV 調整亮度、飽和度(透過轉移至 HLS 顏色空間)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 -\u0026gt; 此篇文章的程式碼 github Day04_調整亮度_modify_brightness.ipynb\n前言 再來我們要來製作屬於自己的日系濾鏡啦！\n研究了一下日系濾鏡的修圖過程，大致上有以下步驟：\n文章 階段目標 [【Day4】](https://ithelp.ithome.com.tw/articles/10236441) 1. 調亮光線 (調整光線) 2. 加強飽和度 3. 將照片調成冷色調 4. 增添顆粒感 5. 降低對比 6. 降低高光 接下來的日子裡我們就來一個個用程式碼實現！\n運用 OpenCV 調整光線 def modify_intensity(img): origin_img = img print(\u0026#34;origin picture:\u0026#34;) show_img(origin_img) maxIntensity = 255.0 # depends on dtype of image data # Parameters for manipulating image data phi = 1 theta = 1 # Increase intensity increase_img = (maxIntensity/phi)*(origin_img/(maxIntensity/theta))**0.5 increase_img = np.array(increase_img, dtype=np.uint8) print(\u0026#34;Increase intensity :\u0026#34;) show_img(increase_img) # Decrease intensity decrease_img = (maxIntensity/phi)*(origin_img/(maxIntensity/theta))**2 decrease_img = np.array(decrease_img, dtype=np.uint8) print(\u0026#34;Decrease intensity :\u0026#34;) show_img(decrease_img) 我們實際上做了什麼事呢? 我們看圖是最好懂的哈哈哈，\n中間的虛線是我們原來顏色的分布，分布範圍在 0 ~ 255，\n如果是增加光線，也就是紅線，可以看到全部的值往y = 255靠近 (更亮了) 如果是減少光線，也就是藍線，可以看到全部的值往y = 0靠近 (更暗了) 我們來看一下我們做了什麼運算 (來現字醜了(X：\n調亮：(maxIntensity/phi)*(origin_img/(maxIntensity/theta))**0.5 公式化簡後，除了 phi,theta 能夠改變最終值外，\n基本能發現他是讓原本的 「origin 值開根號」乘上 「根號255」，\n所以會呈現「凹口向下」的曲線。\n調暗：(maxIntensity/phi)*(origin_img/(maxIntensity/theta))**2 公式化簡後，除了 phi,theta 能夠改變最終值外，\n基本能發現他是讓原本的 「origin 平方」 除以 255，\n所以會呈現「凹口向上」的曲線。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 4 - 日系濾鏡1，運用 OpenCV 調整光線 (modify brightness)](https://ithelp.ithome.com.tw/articles/10236441) Reference https://engoo.com.tw/blog/%E3%80%90%E4%B8%BB%E9%A1%8C%E5%96%AE%E5%AD%97%E3%80%91%E4%BF%AE%E5%9C%96app%E9%83%BD%E6%98%AF%E8%8B%B1%E6%96%87%E7%9C%8B%E4%B8%8D%E6%87%82%EF%BC%9F%E8%B7%9F%E4%BF%AE%E5%9C%96%E3%80%81%E7%85%A7/\nhttps://ithelp.ithome.com.tw/articles/10219730\nhttps://www.itdaan.com/tw/50859156abb7ff6eb099b301cb74f130\nhttps://kknews.cc/zh-tw/n/6rknx8v.html\nhttps://zhuanlan.zhihu.com/p/26889255\n","date":"2020-09-16T02:07:24+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424SodKWaHpS4.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/python-opencv-brightness/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】4 - 運用 OpenCV 調整光線 (modify brightness, intensity)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 今天我們又請來了貓貓當我們助教囉~~~\n-\u0026gt; 此篇文章的程式碼 github Day03_圖片剪裁旋轉縮放_crop_rotate_resize.ipynb\n1. 裁減圖片 裁減圖片的方式，不需要使用 OpenCV 特別的函數，\n我們只需要「知道座標」後，決定要裁減的範圍即可。\ndef\" data-lang=\"python\"\u003edef\"\u003edef rotate_img(img): (h, w, d) = img.shape # 讀取圖片大小 center = (w // 2, h // 2) # 找到圖片中心 # 第一個參數旋轉中心，第二個參數旋轉角度(-順時針/+逆時針)，第三個參數縮放比例 M = cv2.getRotationMatrix2D(center, 15, 1.0) # 第三個參數變化後的圖片大小 rotate_img = cv2.warpAffine(img, M, (w, h)) return rotate_img 3. 縮放圖片 cv2.resize 縮放圖片我們使用 「cv2.resize」。\ndef resize_img(img): scale_percent = 50 # 要放大縮小幾% width = int(img.shape[1] * scale_percent / 100) # 縮放後圖片寬度 height = int(img.shape[0] * scale_percent / 100) # 縮放後圖片高度 dim = (width, height) # 圖片形狀 resize_img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) return resize_img 在 「jupyter notebook」 中，直接顯示的圖片結果看不出大小差異，\n我們可以使用前篇文章中的 「cv2.imshow」 ，\n這樣就可以明顯看出圖片大小的變化。\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 3 - 基本修圖2，OpenCV 圖片的剪裁、旋轉、縮放 (crop, rotate, resize)，在 jupyter 中直接找尋圖片的座標](https://ithelp.ithome.com.tw/articles/10236235) Reference https://www.chainnews.com/zh-hant/articles/420945635518.htm\nhttps://blog.gtwang.org/programming/how-to-crop-an-image-in-opencv-using-python/\nhttps://www.cnblogs.com/lfri/p/10596530.html\nhttps://blog.csdn.net/JNingWei/article/details/78218837\nhttps://blog.csdn.net/on2way/article/details/46801063\n","date":"2020-09-15T09:48:56+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424fRFuHKwYyx.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-crop-rotate-resize/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】3 - python OpenCV 的剪裁、旋轉、縮放 (改變大小) (crop, rotate, resize)"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"先來看看今天的結果圖 讓我們歡迎我們的貓貓助教~~~\n-\u0026gt; 此篇文章的程式碼 github Day02_圖片讀取顯示存檔_image_load_show_save.ipynb\n讀取圖片 cv2.imread 讀取圖片的方式很簡單，只需要使用 cv2.imread(file_name)，\n並指定好 「file_name」的路徑即可。\nfile_name = \u0026#34;./testdata/cat.jpg\u0026#34; # 讀取圖片 laod image origin_img = cv2.imread(file_name) -\u0026gt; 補充：快速查看檔案路徑的方式\n可以直接將檔案用滑鼠拖入 「terminal」 中 (「windows 可以使用 cmd」)，就可以直接得到絕對路徑囉~\n顯示圖片 cv2.imshow / 在 jupyter 中直接顯示圖片(透過 「matplotlib」) 一般傳統使用 OpenCV 的顯示方式就是使用 cv2.imshow('My Image', img)\n第一個參數表示視窗名稱，第二個參數就是你的圖片。\n這個方式會開一個新的視窗顯示出圖片，相比在 jupyter 內建顯示會清晰很多，\n但「有時開發還要一直切換視窗，會有點減低效率」，\n所以通常開發期間我會更常使用在 jupyter 中直接顯示圖片的方式，\n方便我快速察看結果。\n# 另外開一個視窗顯示圖片 show_img_OpenCV def show_img_OpenCV(img): # 顯示圖片，第一個參數表示視窗名稱，第二個參數就是你的圖片。 cv2.imshow(\u0026#39;My Image\u0026#39;, img) # 按下任意鍵則關閉所有視窗 cv2.waitKey(0) cv2.destroyAllWindows() 而我更常使用的是，透過 「matplotlib」 直接在 jupyter 中直接顯示圖片，\n除了開發快速(能直接顯示結果)之外，也能快速查找座標，\n(明天的內容就會理解快速找座標的便利了！)\n# 顯示圖片在 jupyter notebook 中 show_img_jupyter def show_img_jupyter(img): image_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(image_rgb) plt.show() 這裡特別要提到一件事情，我們一般認知的顏色通道通常為 「RGB」，\n但 OpenCV 中處理圖片的顏色通道為 「BGR」，\n如果直接顯示出來，就會發現圖片樣子很像，但顏色與想像中不一樣。\n(感覺怪怪的\u0026hellip;)\n此時我們需要使用 「cv2.cvtColor(img, cv2.COLOR_BGR2RGB)」，\n將顏色通道為從 「BGR」 先轉回 「RGB」 後再顯示，就會是我們預期的顏色了！\n儲存圖片 cv2.imwrite 儲存圖片的方式也很簡單，只需要使用 cv2.imwrite('./test_result/out.jpg', result_img)，\n前面第一個參數為圖片路徑(我們可以直接修改成我們要的副檔名)，第二個就是我們的圖片。\n# 儲存圖片 save image # 第一個參數為圖片路徑(可直接修改副檔名)，第二個為圖片 cv2.imwrite(\u0026#39;./test_result/out.jpg\u0026#39;, result_img) -\u0026gt; 補充 相對路徑與絕對路徑 看完上面文章的讀者們可能會感到疑惑，\n我們在寫檔案路徑的時候有使用 ./ 開頭的敘述，\n這是什麼意思呢?\n相對路徑 .表示目前我們所在的路徑，以 「./test_result/out.jpg」 文件為例，\n我們從這個路徑底下找 「test_result」 這個資料夾中的 「out.jpg」。\n就會是我們儲存圖片的地方。\n絕對路徑 「絕對路徑就是檔案完整的路徑」，這種寫法非常的明確，\n但缺點就是「彈性非常的差」，如果今天將我們的 code 換了一台電腦執行，\n甚至只把資料夾移動到另一個地方，絕對路徑馬上就找不到我們要的檔案了。\n所以建議大家還是多使用「相對路徑」哦~\n雖然我剛學的時候也覺得「絕對路徑」真的很明確很棒XDDD\n-\u0026gt; 咦? 怎麼程式碼裡面好像有偷塞些東西 如果讀者仔細看程式碼，可能會發現最後結果怎麼多了一個剪裁圖片的動作？！\n這只是為了讓圖片的結果顯得比較不同，(有感覺存到不一樣的檔XD)。\n這是明天我們才要來仔細討論的內容哈哈哈哈哈，\n但先放出來給大家搶先看囉~\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 2 - 基本修圖1，OpenCV 圖片的讀取、顯示、存檔 (load, show, save)，附贈簡易理解「相對路徑」與「絕對路徑」](https://ithelp.ithome.com.tw/articles/10235826) Reference https://www.chainnews.com/zh-hant/articles/420945635518.htm\nhttps://blog.gtwang.org/programming/opencv-basic-image-read-and-write-tutorial/\n","date":"2020-09-14T09:46:18+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/20120424zAI6jmo27a.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-load-show-save/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】2 - OpenCV 圖片的讀取、顯示、存檔 (load, imshow, save)，附贈簡易理解「相對路徑」與「絕對路徑」"},{"categories":["131 - Python OpenCV","046 - 12th 鐵人賽 - 【錢不夠買ps的我，只好用OpenCV來修圖了!】"],"content":"前言 今年因為開始了第一份工作的關係，開始忙碌了起來，\n但也因為工作的關係，使用了非常大量的 OpenCV 涵式庫。\n但是函式庫總是那麼包山包海，而大家經常使用卻只有那一些，\n希望能透過這次寫文章的機會找到一些很冷門、但卻有意外效果的 OpenCV 函式，\n這系列是我學習過程中的筆記，另外過程中會不斷慢慢調整，\n最終產出很多以 OpenCV 做的「濾鏡」。\n-\u0026gt; 此系列文章的程式碼 github 這30天的程式碼都會放在 github，有需要歡迎大家自行取用~\n(謎之聲：這裡還能偷看一些未發的文哦XD)\nhttps://github.com/howarder3/ironman2020_OpenCV_photoshop\n0. 此系列使用的環境與語言 這系列的文章我會使用 「opencv-python」 (版本 4.4.0) 來撰寫，\n搭配 「jupyter notebook」 進行簡單實作。\n1. 安裝 OpenCV 但其實也就一行\u0026hellip;\u0026hellip;打開你的terminal輸入\n「pip install opencv-python -y」\n(會下 -y 是不管什麼問題一概都yes，如果不要都yes請把他刪掉)\n安裝會需要一點時間，請耐心等待\u0026hellip;\n另外提供一個自己的習慣，我通常會開一個新的 「conda 環境」 去安裝，\n如果之後要刪除此套件，只需要直接刪除此環境，非常的乾淨俐落。\n但使用 「anaconda」 建立新的環境超出此文討論的範圍，\n再請讀者自行搜尋「下載 anaconda」 以及使用 「conda 建立新的環境」。\n或者也可直接服用github中的 Day01_環境設定_environment_setting.ipynb\n在 「jupyter notebook」 中執行以下程式碼：\n!pip install opencv-python !pip install matplotlib 並確認執行以下內容 「不會跳出任何 error」 就沒問題了!\nimport cv2 import numpy as np import matplotlib.pyplot as plt import glob from IPython.display import clear_output 執行成功大概會長下面這樣：\n附錄：OpenCV 快速測試用程式碼 (看會不會顯示出黑畫面) import numpy as np import cv2 img = np.zeros((100,100,3)) # shape cv2.imshow(\u0026#39;test\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() # AW bigcase 2. 電腦中圖片的基本概念 我們先了解一下一張圖片在電腦中怎麼儲存的，\n圖片基本上就是「一格一格」的 pixel 存在電腦裡面，\n不知道大家有沒有玩過類似下圖的遊戲\nreference：google - CrossMe\n電腦中的圖片就是像這張圖一樣，「一格一格」的 pixel 被儲存的，\n3. 電腦中圖片的大小計算 黑白、灰階 像我們常說的「720p」，就是相對於上面的格子有 「1280×720」 格，\n「1080p」，就是相對於上面的格子有 1920x1080 格，\n而 4K 是指 4096x2160 格(有沒有發現變成說比較大的數字了XD)，\n如果像上圖一樣，每一格要嘛是黑、要嘛是白，\n我們稱此圖為「黑白」，每一格需要 1 bit 儲存。\n而我們在圖像處理常使用的是「灰階」：\n圖片還是黑白的，但由最黑到最白之間可有256種明亮度。\n「每一格 pixel 需要 8 bits (1 byte) 儲存」。\n所以 「720p」 的「灰階」圖片，一共有 1280×720x1 bytes\n= 921600 bytes = 921KB (接近1MB)\n4. 電腦中圖片的大小計算 彩色(全彩、常說的RGB) 對於彩色圖片，而我們在圖像處理常使用的是「全彩」：\n全彩：每個顏色有三個通道 (R、G、B)，每個通道以 8 bits (1 byte) 表示，\n也就是說，「每一個 pixel 使用了 24 bits (3 bytes) 來表示」，\n所以，我們最多可以有1677萬種顏色。\n所以 「720p」 的「全彩」圖片，一共有 1280×720x3 bytes\n= 2764800 bytes = 2.7 MB\n5. 電腦中圖片的座標軸 (x, y) 在電腦中我們儲存的圖片，預設的座標軸都是在「第四象限」!\n並不是我們自然會認為的「第一象限」哦!\n上圖舉了個簡單的例子，像是我畫的點(我有故意把點弄大啦XDD)，\n座標是在 (224, 648)，小畫家的左下角也提供我們快速查詢座標的方式。\n知道「座標」的概念，對於我們往後的圖形處理是非常重要的！\n本文同步發佈在: 第 12 屆 iT 邦幫忙鐵人賽 [【沒錢ps,我用OpenCV!】Day 1 - 總是要從安裝 OpenCV 開始 + 電腦中圖片的基本概念(灰階、全彩RGB、座標軸、計算圖片大小)](https://ithelp.ithome.com.tw/articles/10235551) Reference 單元一、影像處理基本概念\nhttp://yuan.yocjh.kh.edu.tw/photoimpact/01.htm\n","date":"2020-09-13T01:30:39+08:00","image":"https://wongwongnotes.com/images/restored/2020/09/201204241omNVv2fI2.png","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/install-pyhton-opencv/","tags":["12th鐵人賽","Python","鐵人賽"],"title":"【OpenCV】1 - 安裝 python OpenCV install 電腦中圖片的基本概念總整理 (附錄：OpenCV 快速測試用程式碼)"},{"categories":["398 - Linux 問題解決"],"content":"前言 這是在使用 ssh 無法連線的解決辦法，出現類似以下的訊息\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the ECDSA key sent by the remote host is ... Please contact your system administrator. ... Host key verification failed. 錯誤圖片範例 解決方法 方法一：重建 ssh key ssh-keygen -R 192.168.0.1 192.168.0.1: 可任意替換成你要的ip 方法二：直接修改 /root/.ssh/known_hosts 文件 vim /root/.ssh/known_hosts 把出問題的刪掉！！！\n方法三：「最暴力的」把 /root/.ssh/known_hosts 文件直接刪掉！！！ 執行此指令前，請務必知道自己在幹嘛！！！\n如果搞不清楚狀況就直接執行此指令，後果請自負！\nsudo rm -rf /root/.ssh/known_hosts Reference 【Linux】ssh linux ubuntu / mac 中無法 ssh 無法連接 的解決辦法 can’t ssh cant ssh SSH連線出現錯誤 WARNING REMOTE HOST IDENTIFICATION HAS CHANGED ","date":"2019-10-20T18:03:14+08:00","image":"https://wongwongnotes.com/images/restored/2019/10/ssh-1.png","permalink":"https://wongwongnotes.com/posts/linux-shell/troubleshooting/linux/cant-ssh-ubuntu/","tags":["Bash","Linux","Ubuntu"],"title":"【Linux】問題解決：linux/ubuntu/mac 中無法 ssh 無法連接 的解決辦法 (can't ssh cant ssh)"},{"categories":["131 - Python OpenCV"],"content":"前言 很多時候，我們會需要將一張張的圖片合成一部影片，\n我們可以用 OpenCV 實現這件事情，\n以下為範例程式碼。\n如果要自己運用，需要修改的部分：\npath = “*.jpg” 需要改為路徑，結果充滿 .jpg 的檔案資料夾，*的符號意思是取代各種檔案名稱 result_name = ‘output.mp4’ 改為影片輸出結果的名稱。 執行後等待結果，我們就會在同一個目錄底下看到 output.mp4 的檔案了。\nimport cv2 import glob path = \u0026#34;*.jpg\u0026#34; result_name = \u0026#39;output.mp4\u0026#39; frame_list = sorted(glob.glob(path)) print(\u0026#34;frame count: \u0026#34;,len(frame_list)) fps = 30 shape = cv2.imread(frame_list[0]).shape # delete dimension 3 size = (shape[1], shape[0]) print(\u0026#34;frame size: \u0026#34;,size) fourcc = cv2.VideoWriter_fourcc(*\u0026#39;XVID\u0026#39;) out = cv2.VideoWriter(result_name, fourcc, fps, size) for idx, path in enumerate(frame_list): frame = cv2.imread(path) # print(\u0026#34; Making videos: {}/{}\u0026#34;.format(idx+1, len(frame_list)), end = \u0026#34;\u0026#34;) current_frame = idx+1 total_frame_count = len(frame_list) percentage = int(current_frame*30 / (total_frame_count+1)) print(\u0026#34; Process: [{}{}] {:06d} / {:06d}\u0026#34;.format(\u0026#34;#\u0026#34;*percentage, \u0026#34;.\u0026#34;*(30-1-percentage), current_frame, total_frame_count), end =\u0026#39;\u0026#39;) out.write(frame) out.release() print(\u0026#34;Finish making video !!!\u0026#34;) Reference [Python]影像處理匯入到匯出影片 ","date":"2019-10-15T17:44:47+08:00","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-make-video/","tags":["Python","video"],"title":"【OpenCV】OpenCV 利用 python OpenCV 將圖片製作成一部影片"},{"categories":["131 - Python OpenCV"],"content":"【OpenCV】利用 python OpenCV 將一部影片拆成一張張圖片 很多時候，我們會需要將一部影片拆成一張張的圖片，\n我們可以用 OpenCV 實現這件事情，\n以下為範例程式碼。\n如果要自己運用，需要修改的部分：\nvideo_path = '.mp4' 影片的路徑，需改為影片名稱。 output_folder = './output/' 影片轉換圖片後，圖片的輸出位置。 （注意！資料夾會自己建立，但如果資料夾已存在，會刪掉資料夾內所有東西！） 執行後等待結果，我們就會在同一個目錄底下 output 的資料夾拿到我們要的所有圖片了。\nimport os import cv2 video_path = \u0026#39;\u0026lt;your_file\u0026gt;.mp4\u0026#39; output_folder = \u0026#39;./output/\u0026#39; if os.path.isdir(output_folder): print(\u0026#34;Delete old result folder: {}\u0026#34;.format(output_folder)) os.system(\u0026#34;rm -rf {}\u0026#34;.format(output_folder)) os.system(\u0026#34;mkdir {}\u0026#34;.format(output_folder)) print(\u0026#34;create folder: {}\u0026#34;.format(output_folder)) vc = cv2.VideoCapture(video_path) fps = vc.get(cv2.CAP_PROP_FPS) frame_count = int(vc.get(cv2.CAP_PROP_FRAME_COUNT)) print(frame_count) video = [] for idx in range(frame_count): vc.set(1, idx) ret, frame = vc.read() height, width, layers = frame.shape size = (width, height) if frame is not None: file_name = \u0026#39;{}{:08d}.jpg\u0026#39;.format(output_folder,idx) cv2.imwrite(file_name, frame) print(\u0026#34; process: {}/{}\u0026#34;.format(idx+1 , frame_count), end = \u0026#39;\u0026#39;) vc.release() 一些我搜尋用的關鍵字 get images from video Reference [Python]影像處理匯入到匯出影片 ","date":"2019-10-09T16:58:29+08:00","permalink":"https://wongwongnotes.com/posts/python/gui/python-opencv/opencv-get-images-from-video/","tags":["Python"],"title":"【OpenCV】OpenCV 利用 python OpenCV 將一部影片拆成一張張圖片"},{"categories":["214 - C++ 系統控制"],"content":"我們可以使用 dirent.h 這個 library 幫助我們偵測特定資料夾底下的檔案數量，\n下方的程式碼只需要修改 「path」 ，更換為自己想要偵測的路徑即可。\nSample Code (範例程式碼) #include \u0026lt;dirent.h\u0026gt; int file_count = 0; DIR * dirp; struct dirent * entry; path = \u0026#34;/home/ubuntu/Desktop/\u0026#34; dirp = opendir(path); /* There should be error handling after this */ while ((entry = readdir(dirp)) != NULL) { if (entry-\u0026gt;d_type == DT_REG) { /* If the entry is a regular file */ file_count++; } } closedir(dirp); Reference Counting the number of files in a directory using C\n","date":"2019-10-08T11:06:19+08:00","permalink":"https://wongwongnotes.com/posts/cpp/basics/cpp-system-detect/cpp-count-files/","tags":["C++","C++ 系統偵測","系統控制"],"title":"【C++ 系統控制 #1】C++ 利用 dirent.h 計算資料夾的檔案數量 count files"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"今天是最後一天了，我們就來稍微回顧一下這30天走過的路，\n以及這30天過程中的心得，最後我想說文章的未來計劃與表達感謝。\n30天內容回顧與課程索引 在這30天的課程中，我們完成了兩個google所提供的課程，以下有學習內容的整理。\ncoursera - How Google does Machine Learning coursera - Launching into Machine Learning 訂閱課程 【Day 1】 Google ML - 參賽原因 與 就先從認識 coursera 與訂閱課程開始第一天吧\n在第一個課程中，主要了解一些基礎知識，\n以及看google在GCP上分享一些已經有的專案。\n這部分的內容多與將ML的結果實際運用至商業化有關。\n基礎知識 學習目標 介紹內容 基礎ML知識 什麼是ML? 為什麼ML最近才紅起來? 可參考：[【Day 3】](https://ithelp.ithome.com.tw/articles/10213576) 設計ML問題 如何設計一個ML問題？可參考：[【Day 4】](https://ithelp.ithome.com.tw/articles/10213578) ML與一般算法比較 比較一般算法與ML算法，看出ML的優勢。可參考：[【Day 11】](https://ithelp.ithome.com.tw/articles/10214863) ML的應用策略 學習目標 介紹內容 ML的成功策略 使用ML要成功，常需要的關鍵策略。可參考：[【Day 5】](https://ithelp.ithome.com.tw/articles/10213900) ML各階段與比重分配 企業運行ML時，在ML的各階段應該放的比重與心力。可參考：[【Day 6】](https://ithelp.ithome.com.tw/articles/10213904) ML失敗的常見原因 大部分企業使用ML卻失敗的前十大主因。可參考：[【Day 6】](https://ithelp.ithome.com.tw/articles/10213904) 企業如何引入ML ML在企業運行的五大階段與注意事項。 可參考：[【Day 7】](https://ithelp.ithome.com.tw/articles/10214219) GCP認識篇 學習目標 介紹內容 在GCP上運行ML的階段 在GCP上運行ML大概有哪五大階段? 可參考：[【Day 2】](https://ithelp.ithome.com.tw/articles/10213204) GCP上ML的介紹 GCP上ML的介紹。可參考：[【Day 8】](https://ithelp.ithome.com.tw/articles/10214362)、[【Day 11】](https://ithelp.ithome.com.tw/articles/10214863) 已訓練好的ML模型 已訓練好的ML模型。建議可直接使用，不需要再自己訓練。如：「Vision API(圖片辨識)」,「Video intelligence API(影片辨識)」,「Speech API(語音辨識)」,「Translation API(語言翻譯)」,「Natural Language API(自然語言處理)」。介紹：[【Day 4】](https://ithelp.ithome.com.tw/articles/10213578)，詳細整理與比較：[【Day 12】](https://ithelp.ithome.com.tw/articles/10214867)、lab實作：[【Day 14】](https://ithelp.ithome.com.tw/articles/10215285) GCP上的 lab 實作 Lab 介紹內容 Lab事前準備 Lab 0 - 在GCP上開始lab前的事前準備與注意事項。可參考：[【Day 9】](https://ithelp.ithome.com.tw/articles/10214523) GCP上使用VM Lab 1 - 在GCP上分析地震資料與製圖，並儲存在雲端。可參考： [【Day 10】 ](https://ithelp.ithome.com.tw/articles/10214718) BigQuery與Datalab Lab 2 - 使用 BigQuery 與 Datalab 視覺化分析資料。可參考：[【Day 13】 ](https://ithelp.ithome.com.tw/articles/10215097) google ML APIs Lab 3 - 使用google已訓練好的ML模型實作。如：「Vision API(圖片辨識)」,「Video intelligence API(影片辨識)」,「Speech API(語音辨識)」,「Translation API(語言翻譯)」,「Natural Language API(自然語言處理)」。可參考：[【Day 14】](https://ithelp.ithome.com.tw/articles/10215285) 在第二個課程中，我們深入去介紹如何訓練出一個好的ML模型，\n包含ML模型的演算法、架構與一些模型好壞的評估方法。\nML中的不同學習種類 【Day 15】 監督式學習(Supervised Learning) 與 非監督式學習(Unsupervised Learning) 的介紹和比較\n訓練「一個」ML模型 這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 從所有ML模型的訓練結果中，找到「最好的」ML模型：【Day 27】 原因：「訓練好一個模型」不等於「找到最好的模型」\n階段 要做的事情 (「訓練模型」) 使用「訓練資料集(Training)」訓練模型(調整參數)，也就是「「上方表格」」在做的內容 (「結束訓練」) 訓練到通過「驗證資料集(Validation)」結束訓練(未達到「overfitting」的狀態前) (「模型再調整」) 「超參數(hyperparameters)」調整或神經網路的「layer數」或「使用的node數」(一些「訓練前」就會先決定的東西) (loop) (「模型再調整」)後，重複上述(「訓練模型」)、(「結束訓練」)，完成訓練新的模型 (「找到最佳模型」) 從「所有訓練的模型」中，找到能使「驗證用資料集(Validation)」最小的loss，完成(「找到最佳模型」) (「決定是否生產」) 可以開始決定要不要將此ML模型投入生產。此時我們可以使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 訓練ML模型的小實驗 文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 使用「神經網路(neural network)」分類資料 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 觀察「batch size」如何影響「gradient descent」 參賽心得 這次是我第一次參加鐵人賽，\n原先預定的目標是希望能完成將google的五門課完成並做些Side Project，\n但後來開始上課後，發現五門課的份量真的滿多的，\n再加上一天能用的時間有限，而30天的時間就像是馬拉松一樣，\n應該照自己最合適的步調來跑，否則很怕跑到一半心有餘而力不足，\n於是就照著自己一天能看的量，並加上重新整理內容的時間分配進度。\n每日更新確實是一個非常大的挑戰，生活中也還有其他事情需要忙碌，\n每篇文章大約都花我五~九小時不等才完成，\n說實在真的有占用到一些自己處理其他事情的時間，\n參與鐵人賽的期間，就像將自己每日的時間濃縮，但時間長了也會開始疲憊，\n但這時一看到有其他鐵人也跟著自己一起賽跑著，似乎又有某種新的動力燃了起來。\n最後終於度過了鐵人賽這個艱難的考驗，真的有種說不出的成就感。\n自己本來就有在做AI相關的研究，然後自己的個性也很喜歡整理東西，\n也趁著這次機會的好好地把自己的觀念整理一次，\n並且一有機會就加上「自己的註」，與自己先前就會的知識作連結。\n在這過程中，也讓自己的觀念更加有脈絡與條理。\n另一方面，我文章寫到途中後就有個打算，\n未來我希望能把我的文章當成字典，當自己有遺忘的觀念的時候，\n隨手打開文章稍微搜尋一下，就能找到自己想要複習的觀念。\n最後，第一次寫鐵人賽的文筆不敢說很好，\n而且又是這種專業度很高的文，\n自己也很怕自己的專業度不夠能與iT邦上眾多大神相比。\n真的很感謝大家願意讀完我的文章。\n未來計畫 「學習是沒有止境的。」\n不論寫到哪，終究會有新的知識需要學習，\n未來這個系列我也還會繼續寫文章，文章會不定期的再更新。\n更新內容會與原先我希望能夠完成的其他課程有關。\n也希望能藉此讓自己走過的路，能夠留下一些紀錄與整理。\n感謝 感謝所有訂閱的讀者、讀過我文章的讀者們，希望我的文章能讓你們有所收穫。\n感謝所有一起奮鬥的鐵人們，你們的堅持也是我持續寫文章的動力。\n感謝朋友們的支持，沒有抱怨我每天在fb發文很吵。\n(但如果沒這樣的話，似乎少了點那種莫名的壓力去堅持完成鐵人賽)\n感謝google提供這樣免費的學習課程。\n感謝iT邦幫忙舉辦鐵人賽這樣的活動。\n最後，也感謝正在讀這篇文章的你！\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 30】 Google ML - 30天內容回顧與課程索引, 參賽心得, 未來計畫與感謝\n","date":"2019-10-01T01:42:15+08:00","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/all/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】30 - 30天內容回顧與課程索引, 參賽心得, 未來計畫與感謝"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ 「訓練一個ML模型」的整個過程：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★ 從上面的訓練中，找到「最好的」ML模型：【Day 27】\n原因：「訓練好一個模型」不等於「找到最好的模型」\n階段 要做的事情 (「訓練模型」) 使用「訓練資料集(Training)」訓練模型(調整參數)，也就是「「上方表格」」在做的內容 (「結束訓練」) 訓練到通過「驗證資料集(Validation)」結束訓練(未達到「overfitting」的狀態前) (「模型再調整」) 「超參數(hyperparameters)」調整或神經網路的「layer數」或「使用的node數」(一些「訓練前」就會先決定的東西) (loop) (「模型再調整」)後，重複上述(「訓練模型」)、(「結束訓練」)，完成訓練新的模型 (「找到最佳模型」) 從「所有訓練的模型」中，找到能使「驗證用資料集(Validation)」最小的loss，完成(「找到最佳模型」) (「決定是否生產」) 可以開始決定要不要將此ML模型投入生產。此時我們可以使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? ★小實驗系列：\n文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 使用「神經網路(neural network)」分類資料 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 觀察「batch size」如何影響「gradient descent」 Course - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting 「Performance Metrics」 Performance Metrics 「Confusion Matrix」 Module Quiz 0. 先整理今天重點 ★ 「混淆矩陣(confusion matrix)」：\n「模型預測」為真 「(positive)」 「模型預測」為非 「(negative)」 「真實情況」為真 true positive (TP) false negative (FN) 「真實情況」為非 false positive (FP) true negative (TN) 簡單來說，我們「模型預測」只要是正面肯定的，就是「positive」，否定則是「negative」，\n再來判定結果正不正確，只要「實際答案(label)」與「模型預測」不同就是「false」，相同就是「true」。\n★ 比較三種指標「Accuracy」、「Precision」、「Recall」：\n指標種類 「Accuracy」 「Precision」 「Recall」 計算方法 [(TP)+(TN)]/全部資料總數 (TP)/「模型預測」為 positive 的總數 (TP)/「真實資料」為 positive 的總數 公式變形 - (TP)/[(TP)+「false positive(FP)」] (TP)/[(TP)+「false negative(FN)」] 中文解釋 「預測」與「判斷」皆完全正確的 (包含「真預測為真(TP)」與「非預測為非(TN)」) 「模型預測」為真「(TP+FP)」，而「真實資料」為真的比率 「真實資料」為真「(TP+FN)」，而「模型預測」為真的比率 重視的點 最經典的顯示模型的正確度 我們比較重視「模型預測為真」的結果，是否能符合現實。 我們比較重視「真實為真」的結果，模型能不能預測到。 影響 - 「真實為非「(FN)」」的影響相對比較還好。 「模型預測為非「(FP)」」的影響相對比較還好。 例子 都是例子 下雨：預測為非(預測雨天，但實際晴天)，我們帶傘出門相對還好。 成績預警：預測為非(預測成績不好，但實際還ok)，我們告知要注意成績相對還好。 例子造成影響 - 「真實為非「(FN)」」還好 = 實際晴天的影響還好。 「模型預測為非「(FP)」」還好 = 誤發的成績預警影響還好。 對應的反例 - (這裡如果重視「Recall」：則變成「預測晴天，但實際雨天，卻沒帶傘。」) (這裡如果重視「Precision」：則變成「預測成績還好，但實際不ok，卻沒有通知學生注意成績。」) 1. Confusion Matrix 課程地圖\nOptimization Performance Metrics Confusion Matrix 1.1. 「混淆矩陣(confusion matrix)」 這是一個使用ML模型作臉部辨識的例子，\n可以看到我們的模型不正確的將「雕像的臉」辨識為「真實人臉」，\n這種情況我們稱為「false positive」。 另外可以看到我們的模型錯誤的將被冬天衣服遮住的「真實人臉」辨識為「非人臉」， 這種情況我們稱為「false negative」。 自己的註：\n簡單來說，我們「模型預測」只要是正面肯定的，就是「positive」，否定則是「negative」，\n再來判定結果正不正確，只要「實際答案(label)」與「模型預測」不同就是「false」，相同就是「true」。\n「混淆矩陣(confusion matrix)」能夠使用我們量化的評估模型的性能，\n我們現在有四個數字，各個象限各一個。\n然而我們通常只會用一個數字告訴別人我們的模型有多好，我們應該介紹哪一個?\n我們再來用下一個分類的例子來作更細的講解。\n1.2. Precision 如果我們知道一個停車位是可以用的，則他的 「label」 為「positive」， 如果我們的模型也預測這個停車位可以用，則我們稱為「true positive」 如果我們知道「沒有可以用的」停車位， 但我們的模型仍然「預測有」可以停的車位， 我們稱這種情況為「false positive」或「type I error」， 為了比較我們模型做的「positive」預測有多好，我們稱這個指標為「precision」 如果「precision」很高，當我們說有可用的停車位，我們可以非常確定真的有車位。\n「precision」如果是 1.0，表示「我們預測可用」的停車位中，這些車位實際上都真的是可用的。\n但缺點是，我們可能會遺失一些「我們預測不可用」的停車位，但實際上是可用的。\n這種情況我們稱為「false negatives」。\n再來我們用數學方式來講「Precision」的正式定義，\n「Precision」 = 「true positives」/ 「所有被模型預測positive的結果」 然後我們再看回這個「混淆矩陣(confusion matrix)」，\n增加什麼因素會造成我們的精度下降呢?\n答：增加「false positives」。\n在停車場的例子中，模型預測「有空位的」，但實際上「並沒有空位」，\n就會造成「false positives」增加，使「Precision」下降。\n★ 自己的註：\n「Precision」 = 「true positives」/ 「模型預測的所有positive」，\n也可以說 = 「true positives」/ 「false positives + true positives」\n如果看「Precision」=「true positives」/ 「false positives + true positives」\n我們應該能更容易理解為何「false positives」的增加，會使「Precision」下降。\n1.3. Recall Recall 與 precision 通常有著相反的關係，代表著不同的指標。\n我們「預測不可用」的停車位，但實際上是「可用」的，這種情況稱為「false negatives」。 如果一個模型有很高的「Recall」，\n我們會因為發現實際上很多可以停的位置感到開心。\n「Recall」如果是 1.0，表示我們預測可用的車位有10個，就真的有10個。\n但實際上有些可能實際上是「可用」的車位，但我們卻說是「不可用」的。\n「Recall」 = 「true positives」/ 「所有被真實中是positive的結果」 ★ 自己的註：\n「Recall」 = 「true positives」/ 「所有被真實中是positive的結果」\n也可以說 = 「true positives」/ 「false negatives + true positives」\n而「false negatives」指的是「真實中是，我們卻預測不是的」。\n當「false negatives」的增加，會使「Recall」下降。\n★ Precision 與 Recall 自己的小總結 這其實有點像心理學，當我告訴你東西很少，但實際發現很多，你會很開心。\n這就是「Recall」指標，東西可能會比實際預估的還要多。\n另外「Precision」則是比較像寧願誤判也不可以有漏網之魚，\n我們告訴你的東西很多，但實際有可能會比較少一點，這就是「Precision」指標。\n實際運用的話，像是判斷疾病，\n應該要用「Precision」指標(有疾病為positives)，寧願誤判也不可以漏掉。\n或者舉另外一個例子，如果用來判斷下雨的話(有下雨為positives)，\n寧願預測說有下雨，實際卻沒下(比起預測沒下雨，實際卻下雨了好很多。)\n(不過以結果來說，當然還是預測越準越好)\n適合用「Recall」指標的情況，例如以期中預警為例(成績還行為positives)，\n正確預測是好事，但如果錯誤預測，\n寧願「成績還行」卻「告知學生要注意成績(成績不行)」，\n總會比「成績不行」卻「沒告知學生注意成績(成績還行)」的好。\n(一樣的以結果來說，當然還是預測越準越好)\n1.4. 來看另外一個例子：比較「accuracy」、「precision」、「Recall」 這裡我們將會看到很多圖片，這些圖片裡面有些是貓，有些不是，\n我們可以先自己試著判斷看看，然後我們再來看模型預測的結果。\n希望你有正確判斷出含有貓圖片的圖，特別注意紅框處有一隻隱藏的貓。\n而出於我們預測的目的，老虎我們在這例子並不被歸類為貓。\n而我們也看我們的模型預測的結果，我們將這結果與我們的已知「(label)」比較，\n我們來衡量一下模型判斷的結果。\n整體看起來，我們的模型的準確率是3/8 = 0.375，\n也就是說我們模型的「accuracy」= 0.375，\n但「accuracy」指標不一定就是描述模型的最好的指標。\n我們現在來看我們的這些貓與非貓的資料，我們模型的「precision」是多少?\n我們的模型總共預測了五張屬於「positive」的分類，\n但實際上真實是貓的只有兩張，所以「precision」= 2/5 = 0.4\n「Recall」像是一個不喜歡作出「positive」決定的人，\n這裡我們看到所有「代表貓」的「真實例子(label)」，\n我們拿我們模型的預測只與這些「代表貓」的結果比較。\n我們得到的「Recall」為 2/4 = 0.5，\n換句話說這個是從所有真實應該為貓的圖片，\n看我們的模型有預測有多準確(true positives)，\n這個就是「Recall」指標。\n1.5. 段落小整理 這邊我們把最佳化這個章節的全部內容在做個小整理。 首先，我們先定義ML模型為一組參數與超參數的組合， 我們所嘗試的優化，就是指在參數空間(parameter space)中搜索最佳參數。 接下來，我們介紹了「損失函數(loss function)」， 這也是我們在訓練階段中，評估與量化模型現在性能的方式。 我們針對「損失函數(loss function)」討論了兩種方法，\n用於「線性回歸(linear regression)」的RMSE、\n用於分類任務的「交叉熵(cross-entropy)」，\n透過分析「梯度下降(gradient descent)」，\n我們能夠決定前進的方向與步長，\n這使我們知道如何在loss表面進行搜索，以尋找最佳參數。\n我們在「TensorFlow playground」對於不同的ML模型實驗， 並觀察「線性模型」是如何在給定「非線性特徵時」學習非線性關係， 以及神經網路(neural networks)是如何學習層次來分析特徵。 我們也看見「超參數(hyperparameters)」， 例如「學習速率(learning rate)」與「batch size」， 是如何影響「梯度下降(gradient descent)」的結果。 我們最後在決定accuracy, precision, recall之間進行選擇， 依據想解決的問題種類進行選擇，以提高模型的性能， 正如同我們在這個單元中看到的結果一樣， 我們所標記的訓練資料集會成為模型學習的動力。 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 29】 Google ML - Lesson 13 – 以混淆矩陣(confusion matrix)分析ML模型好壞，可評估的三種指標Accuracy, Precision, Recall\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-30T01:38:59+08:00","image":"https://wongwongnotes.com/images/restored/migrated/Fw0UbNz.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/confusion-matrix/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】29 - Lesson 13 – 以混淆矩陣(confusion matrix)分析ML模型好壞,可評估的三種指標Accuracy, Precision, Recal"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ 「訓練一個ML模型」的整個過程：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★ 從上面的訓練中，找到「最好的」ML模型：【Day 27】\n原因：「訓練好一個模型」不等於「找到最好的模型」\n階段 要做的事情 (「訓練模型」) 使用「訓練資料集(Training)」訓練模型(調整參數)，也就是「「上方表格」」在做的內容 (「結束訓練」) 訓練到通過「驗證資料集(Validation)」結束訓練(未達到「overfitting」的狀態前) (「模型再調整」) 「超參數(hyperparameters)」調整或神經網路的「layer數」或「使用的node數」(一些「訓練前」就會先決定的東西) (loop) (「模型再調整」)後，重複上述(「訓練模型」)、(「結束訓練」)，完成訓練新的模型 (「找到最佳模型」) 從「所有訓練的模型」中，找到能使「驗證用資料集(Validation)」最小的loss，完成(「找到最佳模型」) (「決定是否生產」) 可以開始決定要不要將此ML模型投入生產。此時我們可以使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? ★小實驗系列：\n文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 使用「神經網路(neural network)」分類資料 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 觀察「batch size」如何影響「gradient descent」 Course - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting 「Performance Metrics」 「Performance Metrics」 Confusion Matrix Module Quiz 0. 先整理今天重點 ★ 比較「損失函數(loss function)」與「性能指標(performance metrics)」：\n比較 「損失函數(loss function)」 「性能指標(performance metrics)」 使用時機 訓練中 訓練後 理解容易度 很難理解(較抽象) 很容易理解(較不抽象、很直接) 與商業目標 不是直接相關 此指標能直接反應於商業目標 共通點 衡量ML模型的指標 衡量ML模型的指標 1. Performance Metrics 課程地圖\nOptimization Performance Metrics Performance Metrics 在前面的章節中，我們曾在視覺化的網頁中訓練我們的模型，\n並使用「梯度下降法(gradient descent)」優化了我們模型的參數，\n我們最後所創造出的模型，透過許多層次的特徵結構，學習到了複雜的非線性關係。\n然而，我們在這章節的最後發現這樣的方法可能有問題，\n他的後果包含著「訓練時間長」、「最小值為次佳值」、以及「不適當的最小值」。\n在這個章節，我們會仔細來討論什麼是「不適當的最小值」，為什麼他會存在，\n以及透過「「性能指標(performance metrics)」」，我們如何能夠得到更好的結果。\n1.1. 不適當的最小值 (最小loss) 所以，什麼是不適當的最小值呢? 我們可以想像所有在「參數空間(parameter space)」的點都代表一種策略，\n有些點可能不能很好的「泛化(generalize)」，\n或者是不能透過模型反應出資料集的正確關係。\n舉個例子，當我們正在訓練一個模型，我們想預測停車場的圖片是否有空位，\n有一種策略是這樣的：我們不管怎麼樣就預測「所有的車位都是被佔滿的」。\n採取這樣的策略，當我們的資料集正值與負值的例子差不多相等時， 這個策略似乎不可能會成功。 但是，如果我們的資料集是傾斜(skewed)的， 也就是某一類的資料集明顯比另外一類多的時候， 例如，我們拿到的圖幾乎車位都是滿的， 預測「所有的車位都是佔滿的」似乎就是個不錯的策略， 而且我們的模型也不需要多花費心力去理解「特徵(features)」與「label」的正確關係。 我們期待的結果應該是，我們的模型能夠真正理解「空著的車位」這個概念的意思，\n預測「車位皆被佔滿」的模型，自然很難「泛化(generalize)」到其他的停車場也適用。\n自己的註：\n為什麼會有這樣的事情發生?\n我們可以想像每次選擇題的考卷，如果每次我「全選C」就能得到不錯的分數，\n那我還花心力去讀書並理解知識幹嘛? 寫C就能夠得到高分了啊！\n模型這邊在做的事情，就與上面的例子一樣。\n1.2. 完美的「損失函數(loss function)」存在嗎? (如果我們的loss值是整數?)\n我們很容易將不適當的最小值的存在視為是「損失函數(loss function)」的問題，\n如果我們能有一個完美的「損失函數(loss function)」，\n就能夠獎勵真正的最佳策略，只處罰不好的策略。\n但這是不可能的，我們「想關注的指標」與「梯度下降相符合的指標」始終會存在差距。\n自己的註：\n上面已經舉例過學生寫考卷的例子了，\n這邊也能以類似的例子來比喻，\n老師認為的好是「90分」，你認為的好是「60分」，\n我們每次都作答到「60分」使自己滿意了，但並不滿足「老師的滿意」。\n所以我們一樣回到剛剛討論的停車位的問題。\n假設我們依然在對停車位進行分類，\n一個完美的「損失函數(loss function)」應該會幫我們「最小化錯誤預測」的數量。\n然而，以這題而言，這樣的「損失函數(loss function)」應該會是分段的，\n他可以取的值的範圍將是整數，而不是實數。\n自己的註：\n因為車子的數量只能是整數。\n但這會是有問題的，問題出在「微分」。\n「梯度下降(Gradient descent)」使得我們的「權重(weights)」有所變化，\n反過來說，這也表示我們要能夠對「權重(weights)」微分，以取得我們的loss，\n分段的函數在每個範圍內有差距，雖然TensorFlow依然可以對他們微分，\n但loss的表面將不具有連續性，這使得我們在尋找下個參數時更具有挑戰性。\n所以我們必須重新定義問題的框架，\n與其尋找一個完美的「損失函數(loss function)」，\n不如我們應該使用一個新的指標來解決這個問題。\n1.3. 新的指標：「性能指標(performance metrics)」 而這種新的度量標準使我們能夠拒絕那些被認定為「不合適最小值」的模型，\n我們稱這個度量標準為「「性能指標(performance metrics)」」，\n「性能指標(performance metrics)」與損失函數(loss function)相比有兩個好處。\n他能夠更容易地被理解。因為它們通常是可以統計的簡單組合。\n「性能指標(performance metrics)」通常直接與「商業目標」相關。\n第二點比較微妙，但我們可得出的結論是：\n通常loss會與「商業目標」有共同目標，但不一定每次都對目標有一樣的影響，\n有時候，雖然我們的loss很小，但我們在「商業目標」的進展也同樣的很小。\n自己的註：\n也就是說，「最小化loss」通常是我們的訓練目標沒錯，\n但「最小化loss」的情況不代表這樣的模型拿來生產，結果也一定是最好的。\n再換句話說，兩個模型訓練的能力一個「+1」、一個「+10」，\n這個都叫做「好」，只是「+1」實際能造成的正面影響較小。\n而下一章，我們會複習三個「性能指標(performance metrics)」：\n混淆矩陣(confusion matrices) precision recall 並且知道何時該使用這些指標。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 28】 Google ML - Lesson 12 - 不適當的最小loss, 完美損失函數(loss function)存在嗎?ML模型的性能指標(performance metrics)\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-29T01:35:57+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424bKh5dNvFxQ.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/performance-metrics/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】28 - Lesson 12 - 不適當的最小loss, 完美損失函數(loss function)存在嗎?ML模型的性能指標(performance metrics)"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ 「訓練一個ML模型」的整個過程：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★ 從上面的訓練中，找到「最好的」ML模型：【Day 27】\n原因：「訓練好一個模型」不等於「找到最好的模型」\n階段 要做的事情 (「訓練模型」) 使用「訓練資料集(Training)」訓練模型(調整參數)，也就是「「上方表格」」在做的內容 (「結束訓練」) 訓練到通過「驗證資料集(Validation)」結束訓練(未達到「overfitting」的狀態前) (「模型再調整」) 「超參數(hyperparameters)」調整或神經網路的「layer數」或「使用的node數」(一些「訓練前」就會先決定的東西) (loop) (「模型再調整」)後，重複上述(「訓練模型」)、(「結束訓練」)，完成訓練新的模型 (「找到最佳模型」) 從「所有訓練的模型」中，找到能使「驗證用資料集(Validation)」最小的loss，完成(「找到最佳模型」) (「決定是否生產」) 可以開始決定要不要將此ML模型投入生產。此時我們可以使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? ★小實驗系列：\n文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 使用「神經網路(neural network)」分類資料 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 觀察「batch size」如何影響「gradient descent」 Course - Launching into Machine Learning 第四章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nGeneralization and Sampling Introduction to Generalization and Sampling Introduction 「Generalization」 Generalization and ML Models 「When to Stop Model Training」 Sampling Creating Repeatable Samples in BigQuery Demo of Splitting Datasets in BigQuery Lab: Creating Repeatable Dataset Splits Lab Introduction Lab: Creating Repeatable Dataset Splits in BigQuery Lab Solution Walkthrough Lab: Exploring and Creating ML Datasets Lab Introduction Lab: Exploring and Creating ML Datasets Lab Solution Walkthrough Module Quiz 0. 先整理今天重點 ★ 何時停止訓練，加上「驗證用資料集」的整個ML模型訓練過程：\n階段 要做的事情 (「訓練模型」) 使用「訓練用資料集」訓練模型(調整參數)，也就是我們訓練在做的內容 (「結束訓練」) 訓練到通過「驗證用資料集」結束訓練(未達到「overfitting」的狀態前) (「模型再調整」) 調整「超參數(hyperparameters)」或神經網路的「layer數」或「使用的node數」(一些「訓練前」就會先決定的東西) (loop) (「模型再調整」)後，重複上述(「訓練模型」)、(「結束訓練」)，完成訓練新的模型 (「找到最佳模型」) 從「所有新訓練的模型」中，找到能使「驗證用資料集」最小的loss，完成(「找到最佳模型」) (「決定是否生產」) 可以開始決定要不要將此ML模型投入生產。此時我們可以使用獨立「測試資料集」測試? 或使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★ 評估模型好壞的方法：\n比較 獨立的「測試資料集」測試 「交叉驗證(cross-validation)」 (或稱為「bootstrapping」) 總資料多寡 適合「總資料量較多」的 適合「總資料量較少」的 資料分組方式 分成三組：training(訓練用), validation(驗證用), test(測試用) 分成兩大組：training(訓練用), validation(驗證用)，然後各大組再細分成很多小組。 1. When to Stop Model Training 課程地圖\nGeneralization and Sampling Generalization When to Stop Model Training 為了幫助我們能建立適合的ML模型，\n(例如線性回歸(linear regression)或神經網路(neural network))\n我們可以使用驗證資料集(validation dataset)來幫助調整適合模型的「超參數(hyperparameters)」\n複習：\n「超參數(hyperparameters)」指的是在訓練前就設定好的參數。\n1.1. 「驗證用」資料集(validation dataset) 調整「超參數(hyperparameters)」的過程是透過連續進行訓練完成的，\n然後我們再將這些訓練與對應獨立的「驗證用資料集(validation dataset)」進行驗證，\n確認沒有「過度擬合(overfitting)」的現象。\n這裡顯示出我們的「驗證用資料集(validation dataset)」在訓練期間是如何被使用的。\n在最佳化的過程中，訓練模型的過程：\n(一開始參數皆為隨機生成)\n- 從目前「模型參數的權重(weight)」計算「導數(derivative)」 - 觀察「梯度下降(gradient descent)」的loss曲線方向 - 嘗試透過修改「模型參數(parameters)」最小化loss指標 - 然後重複 自己的註：\n這邊稍微補點數學說明，為什麼第一點的參數只看「權重(weight)」?\n因為bias是常數項，微分會消失。\n例如：y=ax+b，微分我們只需在意a，就不用在意b了。\n現在，我們還需要定期的使用「訓練未看過」的數據，\n我們稱為「驗證用資料集(validation dataset)」，來評估模型目前的性能，\n在完成一次完整的訓練後，我們依照「驗證用資料集」驗證模型的結果，\n看目前的「超參數(hyperparameters)」是否有用，是否可以進行更多的調整。\n如果「訓練用資料集」與「驗證用資料集」計算的loss差不多，\n那我們可以考慮先停下來，並重新優化「超參數(hyperparameters)」。\n而只要對損失指標進行了充分優化，並通過了「驗證用資料集」的驗證，\n(記得時間點是：在兩個資料集loss開始出現一些差異，但未達到「overfitting」的狀態時)\n這時我們就應該先停止訓練，然後決定這個模型是否可以投入生產。\n現在我們也可以用類似的循環去調整各個ML模型的參數，\n就像我們在訓練前對「超參數(hyperparameters)」所進行的操作一樣。\n例如：如果神經網路的層數或應該使用的節點數。\n類似「超參數(hyperparameters)」一樣，會在「訓練前」就先設定好的。\n你將會使用其中一種配置(例如一個只有六個節點的神經網路)，\n然後再訓練另外一種，最後都用「驗證用資料集」來看哪一種配置表現得更好。\n最後，我們會選擇一種模型配置，這個配置能夠使「驗證用資料集」產生出最少的loss。\n(記得：上章我們提過，如果只是讓「訓練用資料集」loss最小是不夠的。)\n★ 自己做個小結論，加上「驗證用資料集」的整個ML模型訓練過程：\n- 使用「訓練用資料集」訓練模型(調整參數) - 重複1.，直到通過「驗證用資料集」結束訓練(未達到「overfitting」的狀態前) - 調整「超參數(hyperparameters)」或神經網路的「layer數」或「使用的node數」 - 重新1.2.3.，直到找到能使「驗證用資料集」最小的loss，完成全部訓練 - 並可以開始決定要不要將此ML模型投入生產。 (google提供的輔助工具：)\n在之後的課程中，我們也會用Cloud ML Engine示範在「超參數空間(hyperparameter space)」做bayesian short search，\n也就是說我們不需要一次對一個「超參數(hyperparameter)」進行此實驗。\nCloud ML的引擎能幫忙我們使用不同的最佳化策略，\n並且以平行處理的方式進行實驗。\n1.2. 獨立的「測試用」資料集(test dataset) 評估法 當我們完成訓練之後，我們必須還要能告訴別人這模型有多好。\n那麼，你打算將哪個資料集作為最終通過與不通過的評估? 我們可以簡單的說，對於「驗證資料集」我們的模型最後有多少的loss嗎? (在我們的「訓練資料集」結果差不多的情況下。) 這樣想是不行的，因為我們已經用了「驗證資料集」作為停止我們訓練的依據，\n這樣表示「驗證資料集」已經不是獨立的了(模型已經看過了)。\n我們可以先想想該怎麼辦?\n自己的註：\n如果又拿這組「驗證資料集」，我們的模型是在確定「驗證資料集沒問題」的情況下才停止訓練，\n也就是說模型一定已經對於「訓練資料集」、「驗證資料集」都很清楚且能夠得到高分。\n所以我們還必須使用「其他的資料集」來證明這模型的預測是好的。\n所以，我們必須在一開始就將資料集分成三等份：\ntraining：訓練用資料集 validation：驗證用資料集 test：測試用資料集 只要我們的模型開始被訓練或驗證，我們就可以做「一次」分類，\n而且要注意的是，這分類從頭到尾只能夠使用「一次」。\n自己的註：\n這邊指的意思應該是，「訓練用資料集」、「驗證用資料集」、「測試用資料集」，\n一旦被決定了，就不能任意再重分，\n因為一組資料集只要被模型看過一次，如果再被看過第二次，這樣的測試就是不獨立的。\n而這組「測試用資料集」的結果正是我們可以告訴別人我們的模型有多好的依據，\n我們可以拿這組「測試用資料集」的loss作為指標，決定要不要拿來生產用。\n那至於如果我們在「測試用資料集」中預測的很差呢?\n甚至是我們好不容易都通過了「驗證用資料集」的檢查。\n那就表示我們不可以重新測試同樣的ML模型，我們應該要重新訓練全新的ML模型。\n或者是收集更多的資料，去提供更多的新資料給你的ML模型。\n自己的註：\n所以「測試用資料集」失敗就可以開始思考直接砍掉重練的意思嗎QQ\n1.3. 「交叉驗證(cross-validation)」與「bootstrapping」 評估法 我們再來思考一個很小很小問題，雖然目前這方法已經很好了。\n沒有人喜歡浪費資料，但「測試用資料集」只使用了一次，\n似乎很浪費，但他有被保留下來。\n我們難道不能在訓練時使用所有的資料，然後依然能合理的告訴別人模型的表現嗎?\n答案是可以的。\n這方法就是我們將「訓練用資料集」、「驗證用資料集」拆分成好幾等分，並且在不同的時間使用。\n訓練後並使用「驗證用資料集」計算Loss，這個「驗證用資料集」可能包含「第一次訓練」中未使用的資料，\n然後我們再重新拆分資料，現在我們的「訓練用資料集」可能包含著「第一次驗證」時所使用的資料，\n然後我們可能需要進行多次的「迭代(iteration)」。\n最後在多輪的「迭代(iteration)」之後，我們可以對於整個「驗證用資料集」進行loss的平均，\n我們就會得到整個「驗證用資料集(validation)」的loss標準差，\n這個值能夠幫助我們分析最終loss與它們的散佈情形。\n這樣的方法我們稱為「交叉驗證(cross-validation)」或「bootstrapping」，\n他的好處是，我們可以使用到所有的數據，但由於要將模型拆分成很多等份，\n因此我們也必須要訓練很多次。\n1.4. 評估方法小結論 如果你有大量的資料集，你應該使用獨立的「測試資料集」測試，並判斷這個模型訓練有沒有通過。 如果你沒有那麼多資料，你應該使用交叉驗證(cross-validation)的方法。 至於，如何實際將大數據集拆分成這一份一分的資料? 這個就是在下一章「取樣(sampling)」我們要來詳細討論的事情。 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 27】 Google ML - Lesson 11 - 我們應該何時停止ML模型的訓練? 利用驗證資料集, 測試資料集, 交叉驗證(cross-validation) 評估模型訓練結果的好壞\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-28T01:31:23+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/2012042455VUa5dqzA.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/cross-validation/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】27 - Lesson 11 - 我們應該何時停止ML模型的訓練? 利用驗證資料集, 測試資料集, 交叉驗證(cross-validation) 評估模型訓練結果的好壞"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 (我們今天要先進第四章節囉~)\n這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★小實驗系列：\n文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 使用「神經網路(neural network)」分類資料 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 觀察「batch size」如何影響「gradient descent」 Course - Launching into Machine Learning 第四章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nGeneralization and Sampling 「Introduction to Generalization and Sampling」 「Introduction」 「Generalization」 「Generalization and ML Models」 When to Stop Model Training Sampling Creating Repeatable Samples in BigQuery Demo of Splitting Datasets in BigQuery Lab: Creating Repeatable Dataset Splits Lab Introduction Lab: Creating Repeatable Dataset Splits in BigQuery Lab Solution Walkthrough Lab: Exploring and Creating ML Datasets Lab Introduction Lab: Exploring and Creating ML Datasets Lab Solution Walkthrough Module Quiz 0. 一樣先來整理今天重點 ★ 名詞解釋：\n「泛化(generalize)」：指ML模型「對未知資料集」的預測能力。 「泛化(generalize)」能力差：等於預測對未知資料集的預測能力差。但如果對「對自己的資料」的預測能力很好，有可能是發生了「過度擬合(overfitting)」的現象。\n★ 「欠擬合(underfitting)」 與 「過度擬合(overfitting)」\n比較 「欠擬合(underfitting)」 「過度擬合(overfitting)」 訓練前 (可能)決定了太簡單的模型 (可能)決定了太複雜的模型 訓練中 (可能)訓練太早結束 (可能)訓練過頭，也就是太晚結束 訓練後 - 對自已的資料 訓練後發現模型「對自已的資料」預測能力太差 訓練後發現模型「對自已的資料」預測能力非常好(可能好到沒有誤差) 訓練後 - 對新的資料 (對自己的資料都不行了還要試新資料嗎XD) 訓練後發現模型「對新的資料」預測能力非常差 代表的意義 我們的模型「對自已的資料」沒辦法達到理想的預測能力。 我們的模型「對新的資料」沒辦法達到理想的預測能力，然而對「對自已的資料」預測能力非常好。 自己的記法(不完全正確) 連自己的資料預測都不行，根本還不能預測東西。 訓練到根本把「自己的資料」都「背下來」了，難怪新資料完全不能預測。 自己的註：\n「最佳的ML模型訓練結果」應該介於「欠擬合(underfitting)」 與 「過度擬合(overfitting)」之間。\n★ 用圖簡單解釋 「欠擬合(underfitting)」 與 「過度擬合(overfitting)」：\n1. Introduction to Generalization and Sampling 課程地圖\nGeneralization and Sampling Introduction to Generalization and Sampling Introduction 我們在這章節要討論的是「泛化(generalization)」 與 「取樣(sampling)」\n在討論之前，我們一樣來複習前面所學過的內容，\n我們已經討論過ML model是如何訓練的，\n並且在TensorFlow playground中做了實驗，看到了視覺化的結果。\n現在我們就可以思考一個有趣的問題，\n什麼時候一個被訓練很精確的ML模型，實際上卻不是正確的結果，\n事實上這個問題的關鍵就是在：\n「對我們的模型而言的loss為0，實際上在現實生活中並不一定是真的好的。」\n自己的註：\n對一個資料集的分析，loss為0僅代表「這個誤差對資料集的預測沒有影響」，不等於預測是對的。\n★ 「好的ML模型」不是指對「我們的資料集」預測結果好，而是對於「未看過的資料」表現的也好。\n我們的ML模型如果將來想要產品化，這表示會有更多我們的ML模型沒看過的資料，\n這就是我們必須仔細評估的問題。\n所以我們要來討論怎麼去「評估我們沒看過的資料」?\n首先，我們必須要得到一些我們沒有在訓練中使用的資料，\n在我們的訓練完成後，我們就可以用這些資料來評估，\n我們也可以藉由這樣的評估來判斷我們的模型是否有「過度擬合(overfitting)」的現象，\n以及確定什麼時候應該模型要停止訓練。\n第二部分就是我們可以如何在一開始就創造一些未知的資料集，\n正常情況下，我們可能很難取得未看過的資料。\n但因為我們有訓練資料集，\n所以我們可以將訓練資料集拆成「訓練用」與「評估用」，\n我們可以使用其中一組資料集訓練我們的模型，\n而當模型訓練完成時，我們就可以使用另外的資料來測試與評估模型的表現。\n所以我們也會學到如何去建 training, evaluation, test 的資料集，\n並透過這樣的方式實際建立測試模型的指標。\n2. Generalization and ML Models 課程地圖\nGeneralization and Sampling Generalization Generalization and ML Models 我們先來解決「泛化(generalization)」的問題，這可以幫助我們理解\n「為什麼我們訓練一個預測準確的模型，並不一定是最好的模型。」\n2.1. 觀察「過度擬合(overfitting)」的現象 我們再來看一下我們所熟悉的出生率資料集，\n不過這次我們使用「母親的體重增加」在X軸上，\n來預測在Y軸上的「預測懷孕的時間」，\n首先我們可以直覺的感覺到，這兩組資料看起來有很強的相關性，\n也就是說，體重增加的越多，懷孕的時間就越長，\n這件事情似乎在嬰兒的成長過程中是很直觀的。\n我們也來複習前面一點的內容，當我們說到要對這樣的關係建立模型，\n證明這兩個數據具有相關性，我們需要使用「線性回歸模型(linear regression model)」\n而當我們討論「回歸問題(regression problems)」時，\n我們通常用來計算的loss的方式都是「MSE(mean squared error)」或「RMSE(root mean squared error)」\nMSE 「MSE(mean squared error)」可以告訴我們我們預測的回歸線與實際結果有多接近，\n我們實際上在計算就是「每個資料點」與「回歸線(我們的預測)」的距離，\n這段距離我們又可以稱為error(誤差)，\n在「MSE(mean squared error)」的計算之下，我們會將誤差平方，\n所以我們會移除負號而只觀測其值。\n此外，因為MSE的關係，我們可以加重「預測」與「實際值」差異的處罰。\nRMSE 用MSE取平方根即可得到RMSE，RMSE有點像所有數據與預測的平均距離。\n而因為RMSE能夠修正單位的問題，我們能直接解釋為Y軸上的測量單位。\n所以以相關係數來說，RMSE會是個更好的測量單位。\n不論是這兩種誤差的測量，越小的值都表示模型的性能更好，\n我們期望這樣的模型訓練最後能越接近零(表示預測沒有誤差)\n這裡我們使用線性回歸模型(linear regression model)，\n我們透過這模型簡單的繪製了一條最佳回歸線，最大程度的減少了誤差，\n我們最終的RMSE值為2.224，似乎還不夠小，那我們來看看另外一個例子。\n我們現在來看上面這個有趣的圖形，\n我們現在使用更複雜的模型來預測，更複雜的模型會有更多的「參數(parameters)」\n在這個例子中，這些「參數(parameters)」替我們捕獲到了資料集的每個資料的細節，\n然後我們的RMSE值就降到0了，現在這結果「對於我們的資料集」非常的精確。\n然而這樣的結果真的是最好的嗎? 我們能使用這模型來生產嗎?\n其實這結果非常的有問題，我們舉個例子，\n如果今天來了一個我們沒看過的資料，我們訓練出的模型就沒有這樣的直覺，\n所以如果問我們，具有8個node的比12個node的好嗎?\n我們有個16個node的神經網路，他有最低的RMSE，我們應該使用這個嗎?\n我們在這裡所看到的例子，可能是有數百次方的多項式，然後具有數百個node的神經網路。\n雖然更多的node(也代表更複雜的模型)能夠幫助我們優化更多的參數，\n也能幫助我們找到最適合「複雜數據的分類」結果，\n(例如像上一張分類螺旋資料集的例子)\n但同時，這樣子做也會讓模型去記住更簡單的、更小的數據集。\n自己的註：\n就是把整個數據集都「背下來」了，但「不是這個數據集」的反而不會了。\n這就是「過度擬合(overfitting)」。\n2.2. 泛化(Generalization) 所以，我們應該要在什麼時候停止訓練模型?\n並且我們要避免模型「背下資料集」導致「過度擬合(overfitting)」?\n現在我們有個驗證模型的方法，就是拿我們的模型給沒看過的新資料集看性能如何，\n然後我們就可以決定現在的模型對新資料集的「泛化(generalize)」程度如何，\n這也會是未來我們在生產ML模型的重要指標。\n自己的註：\n「泛化(generalize)」：基本上就是指ML模型對未知資料集的預測能力。\n如果沒有良好的「泛化(generalize)」，表示我們的模型只對我們的資料集預測好而已。\n我們現在再回到「線性回歸模型(linear regression model)」與「神經網路模型(neural network model)」，然後看它們表現的怎麼樣。\n我們的「線性回歸模型(linear regression model)」對於新資料集的「「泛化(generalize)」能力非常好」\n從我們「新資料集計算出」的RMSE與我們「訓練所計算出」的RMSE差不多可以判斷。\n在這樣的情況下，對於我們的ML模型是一件好事。\n我們就是希望再預測(training)與驗證(validation)過程中，\n我們的模型能有差不多的表現。\n我們現在看到第二個模型，我們可以發現他對於新資料集的「「泛化(generalize)」能力非常差」\n就算對於我們「訓練所計算出」的RMSE為0，\n對於「新資料集計算出」的RMSE從0跑到3.2，\n這代表一個非常嚴重的問題，也指示出我們的模型有著「過度擬合(overfitting)」的現象，\n也證明了這模型對於新資料的預測能力非常的弱。\n2.3. 泛化(Generalization)問題的解法 現在我們可能會想問，怎麼確定我們的模型訓練有沒有「過度擬合(overfitting)」?\n所以我們該如何決定我們訓練停止的時間? 這個問題的答案十分的簡單。\n我們先將我們的資料集分成兩部分。\n現在我們將我們的原始資料分成完全兩個獨立的組別，\n一個為「訓練用資料集(training)」、另一個為「驗證用資料集(validation)」\n我們可以拿我們的「訓練用資料集」來不斷的反覆訓練我們的模型，\n當我們完成訓練之後，拿訓練好的模型使用「驗證用資料集」來看模型的表現。\n如果模型泛化(generalize)的很好，它應該也會在「驗證用資料集」有差不多的loss值\n(也就是「訓練用資料集(training)」、「驗證用資料集(validation)」的loss差不多。)\n當我們看見我們的模型對於「驗證資料集(validation)」預測結果開始不好的時候，\n也就是像上圖，當我們計算出的loss開始增加，我們的模型訓練就該停止了。\n自己的註：\n正常來說「驗證資料集(validation)」的RMSE應該也要差不多2.2左右\n誤差太大也表示我們的ML模型有可能在「背資料」了。\n同時訓練和驗證ML模型，能夠幫我們找到正確的泛化結果與最合適訓練資料集的模型參數，而不是將模型整個背下來。\n2.4. 欠擬合(underfitting) 與 過度擬合(overfitting) 欠擬合(underfitting) 我們先看上圖中最上面的圖形，\n我們有一個過於簡單的線性模型，它並不太適合預測我們這組資料，\n上圖中，我們可以視覺上的發現很多點都是在這條預測之外，\n這樣的情形我們就稱為「欠擬合(underfitting)」\n過度擬合(overfitting) 而與上述相反的情況，如果結果太過符合所有的資料點，\n我們就稱之為「過度擬合(overfitting)」\n我們適當增加線性模型(linear model)的複雜度，使它成為n階多項式，\n讓結果能符合我們資料的預測，\n因此，這就是為什麼我們需要驗證的資料集，\n我們就是要決定是否模型的參數是否已經過度訓練導致「過度擬合(overfitting)」的發生。\n「過度擬合(overfitting)」或模型「只知道」我們訓練資料集的如何預測，\n很多時候都會到生產階段才知道，這也就是為什麼我們還需要驗證的資料集。\n一個正確的結果，也就是在「欠擬合(underfitting)」與「過度擬合(overfitting)」之間找到適當的模型複雜度，\n接下來我們就要來看如何使用驗證資料集(validation dataset)，幫助我們決定模型停止訓練的時間，與他是如何幫助我們避免「過度擬合(overfitting)」。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 26】 Google ML - Lesson 10 - 泛化(Generalization)-檢查ML模型對於未知資料集的預測能力, underfitting與overfitting的問題\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-27T01:29:04+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424QUkLu0z2es.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/generalization/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】26 - Lesson 10 - 泛化(Generalization)-檢查ML模型對於未知資料集的預測能力, underfitting與overfitting的問題"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★小實驗系列：\n文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 使用「神經網路(neural network)」分類資料 [【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) 觀察「batch size」如何影響「gradient descent」 Course - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls 「TensorFlow Playground」 Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced 「Lab: Practicing with Neural Networks」 Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 1. Lab: Practicing with Neural Networks 課程地圖\nOptimization TensorFlow Playground Lab: Practicing with Neural Networks 我們已經知道了「linear model」是如何幫我們分類資料集的，\n再來我們來看看「neural network」是怎麼做的。\n不過在那之前，我們需要理解一些新的功能，\n我們已經先實作好在 TenserFlow playground 裡面了。\n1.1. 實驗介面介紹 首先我們要知道「activation」功能 「Activation」是由「激活函數(activation function)」產生的，\n在第五課(The Art and Science of ML)時我們會有更詳細的介紹\n(不過可能來不及寫到那? 有時間再補一下了~)\n現在我們能夠改變的關鍵是「激活函數(activation function)」的選擇，\n這也是我們該如何從「neural networks」區分出「linear models」，\n而我們以前就是設置線性的「激活函數(activation function)」。\n自己的註：\n前面有提過，「linear models」是一種簡化版的「neural networks」\n第二個我們要知道的是「隱藏層(hidden layers)」功能 「hidden layers」的功能允許我們去更改「hidden layers」的數量，\n同時我們也能更改每一層hidden layers的「神經元(neurons)」數量\n我們可以先簡單把這段想成「更改資料進入神經網路後的轉換次數」\n在每層hidden layer的「神經元(neurons)」，會接收所有來自「上一層的結果」，\n而「上一層的結果」就會變成「這一層的輸入」，\n並且傳遞給所有在這「隱藏層(hidden layers)」的神經元，\n而我們的整個神經網路就是進行這樣的動作，讓每個神經元都能接收到訊息，\n我們可以用簡單的網路(network)架構來比喻用神經元的數量以及他們如何傳遞資訊\n第三個我們要知道的是「batch size」功能 前面【Day 23】有介紹過他的意義了，我們會在這個實驗使用他。\n再來我們可以使用以下的連結，嘗試去訓練一個能分辨資料集的模型：\n參考範例連結：https://goo.gl/VyoRWX 然而，這邊與其使用非線性函數的功能來實現，\n但我們會建議嘗試只靠修改「神經網路(neural network)」架構去改善表現的結果，\n(雖然我們目前「neural networks」還沒有細講理論，但我們可以先觀察變化)\n現在開始，我們可以在這個介面簡單操作，直到你可以訓練出一個良好的網路。\n1.2. 小實驗 - 使用「神經網路(neural network)」分類資料 (訓練後)\n我們再稍微操作一下之後，應該就能訓練\n出一個良好的模型，並且輸出結果可能中間會有包含藍色的多邊形區域，\n我們現在要一一的觀察「隱藏層(hidden layers)」裡面的內容，\n使我們對模型有更直覺的概念，\n我們先仔細看第一個hidden layer的「神經元(neurons)」(紅色框框的部分第一個)\n當我們將滑鼠移到每一個「神經元(neurons)」的上方，右側的output也會改為顯示我們的結果。\n我們可以觀察每一個「神經元(neurons)」，用我們理解最終結果的方式，\n特徵(features)X1與X2的值皆已經被編碼在正方形裡面，\n顏色表示「在這個神經元」中「X1與X2組合輸出的結果」，\n我們依序看過每一格「神經元(neurons)」，我們就可以試想一個問題，\n如果這些紅色格子內的「神經元(neurons)」疊加會是怎麼樣子的呢? 藍+藍 = 更藍 藍+白 = 淺藍 藍+橘 = 白 這時我們就可以開始去想，每一個「神經元(neurons)」是怎麼樣去參與共同決定最後的決策邊界? (也就是說，最後輸出的形狀與「隱藏層(hidden layers)」有什麼關聯?) 我們可以明顯看出，每個「神經元(neurons)」似乎都有對最後的決策邊界貢獻出了幾個邊。 現在我們可以用幾何的概念去想，如果「靠我們自己」製造出這個網路並獲得合理的結果的可能性有多小? 就好比，如果從藍點的周圍，我們能夠簡單劃出一個合理計算結果的形狀嗎? 1.3. 小實驗 - 觀察「batch size」如何影響「gradient descent」 在TenserFlow playground實驗，我們能直接看出我們的直覺是否準確。\n我們已經看過神經網路「hidden layer的結果」能用來決定最後的決策邊界(decision boundary)\n那只有一層hidden layer的神經網路與很多層hidden layer的差別呢?\n我們現在來分類一個螺旋狀的資料集試試看。\n參考範例連結：http://goo.gl/hrXd9T 我們也可以趁這個機會來了解「batch size」是怎麼影響「gradient descent」的，\n我們設定「batch size」為1，並以「neural network」的架構實驗。\n訓練大約經過300 epochs的時候我們先暫停一下，「注意loss曲線的變化」 再來，我們把「batch size」分別設定為10，我們再做一次， 一樣經過300 epochs的時候我們先暫停一下。 最後，我們把「batch size」分別設定為30，我們再做一次， 一樣我們也是訓練300個epochs 我們看得出loss曲線的平滑度有明顯的差異，\n而且是隨著「batch size」增加，平滑度也會增加。\n我們能用什麼我們已經知道的知識解釋觀察到的變化呢?\n我們就從「batch size」會影響「gradient descent」的角度下去想，\n當「batch size」小的時候， 我們的模型「更新參數」的基礎只基於一種「example」所計算出的loss。 但是每個「example」皆不相同，這就是問題所在。 當「batch size」增加的時候， 各個資料集所產生出來的「noise(雜訊)」會比較穩定， 漸漸的我們能夠看出比較清楚的特徵。 但是我們不能輕易的依據觀察結果下結論：「「batch size」能夠直接對收斂速度產生影響。」\n「batch size」與「learning rate」皆為「超參數(hyperparameter)」，\n所以「batch size」會因要訓練的題目不同而有不同，\n我們應該使用 hyperparameter tuning 來調整它。\n我們的模型完成訓練後，應該會長得像右邊這樣的圖，\n第一件事情我們先注意「第一層「隱藏層(hidden layer)」」與之後的層之間的關係。\n我們應該可以很明顯發現，在第一層「隱藏層(hidden layer)」的輸出，基本上都是線。\n自己的註：\n我稍微想了一下他的意思，應該是指我們可以看到在第一層 hidden layer 中內部的分區圖，基本上都是以「一條線」來區分「白色」與「藍色」區塊的。\n在第一層之後的隱藏層輸出複雜很多，這些後續的層是基於前面層的結果堆疊而成，\n(與我們上面所說的堆疊方式相同。)\n因此，我們可以將「neural network」視為特徵的層次架構，\n它們從前一層拿取自己的inputs，並以複雜的方式轉換成最終能分類資料的方法。\n這個就是最經典的「神經網路(neural network)」代表。\n這種方法與傳統的「機器學習(machine learning)」方法是很不相同的。\n在「神經網路(neural network)」之前，\n資料科學家花了很多時間做「特徵工程(feature engineering)」，\n現在我們有ML模型可以幫忙負擔一些工程，\n而我們可以將思考layers的意義作為一種「特徵工程(feature engineering)」的形式。\n1.4. 「過度擬合(over-fitting)」 接下來要講到的是模型學到的一些奇怪的東西，\n我們的模型似乎將兩個區域中沒有橙色的點，解釋為它是支持藍色的證據。\n我們稱模型將資料集中的雜訊(noise)也解釋了，這稱為為「過度擬合(over-fitting)」\n自己的註：\n也就是白色也等於橘色了，但原本應該只有橘色等於橘色的。\n當「模型的決定權」超出了「問題的嚴格必要限制」時，就會發生這樣的情況。\n當一個模型是 over-fit 的，它們通常「泛化(generalization)」的效果很差，\n(也就是對未看過的資料處理能力很差)\n比較好的結果應該是保留應該保留的內容，\n「雜訊(noise)」與我們想分析的pattern不應該會有一樣的分類結果\n這件事，我們會在往後「泛化(generalization)」與「取樣(sampling)」的內容仔細討論。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 25】 Google ML - Lab 5 - 在視覺化的網頁中觀察神經網路(neural network)如何分類資料, 並比較batch size如何影響gradient descent\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-26T01:26:37+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/201204249HkMOiKZOf.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/gradient-descent-2/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】25 - Lab 5 - 在視覺化的網頁中觀察神經網路(neural network)如何分類資料, 並比較batch size如何影響gradient descent"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) ★小實驗系列：\n文章 實驗內容 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) TensorFlow Playground 的簡介與介面介紹 [【Day 24】](https://ithelp.ithome.com.tw/articles/10220441) 「learning rate」 的改變對訓練過程的影響 而今天的文章我們先不討論新東西，我們來做點小實驗。\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls 「TensorFlow Playground」 「Lab: Introducing the TensorFlow Playground」 Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 1. Lab: Introducing the TensorFlow Playground 課程地圖\nOptimization TensorFlow Playground Lab: Introducing the TensorFlow Playground 所以，我們現在知道「gradient descent」是如何運作的，\n我們現在先使用一些工具來看看這方法實際上運作會是怎麼樣。\n你應該會看到很多我們之前有提到的現象。\n「TensorFlow Playground」是一個非常強大的工具，\n他能幫助我們視覺化「neural networks」是如何運作的。\n(「neural networks」稍後章節我們就會介紹了)\n事實上，我們現在所介紹的「linear models」就是一種簡化的「neural networks」\n所以這個工具也很適合做「linear models」的視覺化呈現。\n我們會用這個工具來證明我們前面所說的理論知識，可以使我們對ML更有直覺，\n我們也會直接了解「設定「learning rate」」與ML模型是如何降低梯度的。\n我也會指出這些內容與之後主題的關係，這些都會在之後的課程有更深入的探討。\n首先，我們先看一下介面，\n我們先刪除了某些工具的功能，因為他與我稍後將介紹的內容才有關係，\n但依然有許多有趣的功能是我們可以使用的。\n1.1. 介面介紹 首先，我們畫面框起來的指的是「features」，這些要給model看的inputs \u003e 在每一個box裡面的顏色代表的是feature的值，其中橘色代表負值、藍色代表正值， 然後這裡有個hidden layers的欄位，我們可以先當作這部分是代表「權重(weights)」， 我們可以透過「滑鼠靠近」來看這個些值是多少 隨著模型的訓練，這條線的寬度與不透明度會逐漸改變，\n這樣的視覺化表示也方便我們能夠快速的理解他們的值。\n在output column我們可以同時看見training data與我們的model現在預測的值， 以及所有的點在features space裡面的位置 - 我們也看到目前的training loss 這邊都會以「顏色」來表示值(「features」)\n最上方的控制列包含著「重新設定訓練, 開始訓練, 執行單個步驟」的按鈕\n還有一個可以下拉的列表，可以調整我們的「learning rate」\ndata欄位允許我們選擇不同的資料集，並且也能控制「batch size」的大小 1.2. 開始訓練 我們就先以訓練一個「linear model」來替我們分類資料開始吧!\n這邊我們可以參考範例連結：https://goo.gl/EEuEGp (這圖我另外截的XD，上面寫\u0026quot;「Don’t Worry, You Can’t Break It. We Promise.」\u0026ldquo;滿有喜感的XD)\n「可惡，越是這樣講越讓我想嘗試玩壞看看」\n當我們點進連結後，我們就會看到TensorFlow Playground的介面，，\n我們先介紹一下目前畫面上的配置(不用擔心沒有hidden layers的問題)，\n在目前我們所看到的介面設定中，這個模型接受一個「feature vector」，\n並與「weight factor」計算內積，再加上「bias」，\n然後使用計算結果去建立「決策邊界(decision boundary)」。\n因此我們可以將目前的配置視為「linear model」，\n我們現在會開始訓練這個model，讓他去嘗試分類一些屬於兩大不同族群的資料。\n我們點擊畫面上的step鍵(紅框處)，我們會注意到畫面開始有ㄧ些變化。 - 「epochs」的數值增加1 - 我們說代表「weights」的線換了顏色、也改變了大小 - 現在「loss function」的值也改變了 loss的圖形顯示了往下的斜率\noutput的「決策邊界(decision boundary)」也產生了改變\n我們移動一下滑鼠靠近目前代表1的「weights」，觀察一下「weights」目前的大小，\n我們現在點擊play的按鈕讓他繼續訓練，但當loss小於0.002時我們就先暫停一下。\n(應該會在200 epochs以內)\n這時，我們已經完成訓練我們的第一個模型了!\n1.3. 小實驗 - 「learning rate 的改變對訓練過程的影響」 現在讓我們稍微改變一點東西，\n首先我們先看不同的「learning rates」是怎麼影響模型訓練的。\n簡單複習一下：\n「learning rate」是「hyperparameter」，也就是在訓練模型前設定的。\n這個值會乘上偏微分的結果，並決定我們「weights」要改變(之前我們說的「移動」)的量，\n這邊我們先試第一種，我們看看當「learning rate」非常小(0.00001)的訓練結果 這邊我們只要先等到約100 epochs即可(大約需要兩秒)，然後先暫停訓練。 - 參考範例連結：https://goo.gl/3pmeKj 現在的loss是多少? loss的變化是怎麼樣的? 然後我們的模型變成了怎麼樣的「weights」? (調整「learning rate」的位置)\n然後我們將「learning rate」調整為0.001，並再重新訓練一次 一樣也是約100 epochs即可先暫停訓練。 現在的loss是多少? (應該會少了很多) loss的變化是怎麼樣的? 然後我們的模型變成了怎麼樣的「weights」? 再來我們將「learning rate」調整為0.1，並再重新訓練一次 一樣也是約100 epochs即可先暫停訓練。 現在的loss是多少? (應該會少更多) loss的變化是怎麼樣的? (減少的有多快?) 然後我們的模型變成了怎麼樣的「weights」? 最後我們將「learning rate」調整為10，並再重新訓練一次 我們先試著只做一步看看，注意「weight」的變化。 再來我們繼續訓練直到100 epochs暫停訓練。 \u003e * 現在的loss是多少? (應該會少更多) \u003e * loss的變化是怎麼樣的? (減少的有多快?) \u003e * 然後我們的模型變成了怎麼樣的「weights」? 我們觀察loss的曲線下降的速度，應該是非常急遽的下降的。\n當我們把這些訓練結果放在一起，我們比較這四種的變化，\n並試著用我們學到的最佳化知識解釋看看原因，\n(上面這張表是影片實驗結果的整理，正常來說我們的結果應該會有點不一樣，而重跑實驗也會有點不一樣也是同理。)\n「TensorFlow Playground」幫我們隨機初始化了「權重(weights)」，\n這也表示我們每次的搜尋都從一個隨機位置開始。\n我們先討論「Weight1」那一直行： 注意「weights」的變化與我們「learning rate」的關係， 我們發現隨著「learning rate」增加，「weight」也會增加。 為什麼呢?\n因為我們的模型在訓練的時候走比較大步。\n而事實上，當「learning rate」為10時，「第一步」改變的「weight」最為顯著。\n而我們再看「loss over time」那一直行： 我們發現當「learning rate」增加，「loss曲線」也逐漸變陡。\n這正與我們之前所分析的結果相同。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 24】 Google ML - Lab 4 - TensorFlow Playground - 讓我們在視覺化的網頁中體驗一下訓練ML模型吧！learning rate 改變對訓練過程的影響\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-25T01:22:15+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424EoNUdkWTBV.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/tensorflow-playground/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】24 - Lab 4 - TensorFlow Playground - 讓我們在視覺化的網頁中體驗一下訓練ML模型吧！learning rate 改變對訓練過程的影響"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 而今天的文章我們要來討論一下「加快ML模型訓練的方法」。\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions 「Gradient Descent」 Gradient Descent Troubleshooting a Loss Curve 「ML Model Pitfalls」 TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 0. 一樣先來個重點整理，下面會有詳細介紹 加速訓練的方法： 調整「計算偏微分時的資料總數」 利用「batch」的方法 調整我們「檢查loss的頻率」 基於「固定時間(Time-based)」的檢查 基於「固定步驟(Step-based)」的檢查 - 一大堆有關「batch」的名詞整理： 名詞 種類 意義 「mini-batching」 方法(method) 抽樣的方法 「batches」 被抽樣到的資料(data) 使用抽樣的方法得到的資料(subset of dataset) 「batch size」 樣本數大小(size, number) 代表抽樣的數目、樣本數大小(size, number) 「mini-batch size」 同上 「mini-batch size」比較常被稱作「batch size」。 「mini-batch gradient descent」 方法(method) 使用抽樣的結果去做「gradient descent」 「term batch gradient descent」 方法(method) 使用分堆的方式，也就是將「全部資料都下去分堆」。與「mini-batch gradient descent」最大的不同在於抽樣「使不使用全部資料」(另外一個只抽樣)，另外這個比較常用 「batch size」與「迭代(iteration)」與「epoch」的概念比較： \u003e 假設我現在有400筆資料，我做分堆： \u003e 我決定「一堆的大小(batch size)」要有40筆資料， \u003e 這樣一共會有10堆(通常稱為「number of batches」,「batch number」)， \u003e 也就是說每一輪我要學10堆資料，也就是學10個「迭代(iteration)」。 \u003e 學完「10個「迭代(iteration)」」後，等於我把資料集全部都看過一次了。 \u003e 這樣就是一輪(「epoch」)訓練的結束。 \u003e . \u003e 會有「batch size」與「迭代(iteration)」與「epoch」這些詞的原因， \u003e 是因為通常ML學習的資料量都很大，我們會需要分比較小堆。 1. ML Model Pitfalls 課程地圖\nOptimization Gradient Descent ML Model Pitfalls 有個常見的情況是，每次當我們重新訓練ML模型的時候，\n明明是一樣的code，我們也希望能產生一樣的實驗結果，但往往結果不同。\n但通常我們會希望每次訓練的結果皆相同，這樣才是一個穩定的模型。\n在ML裡面，很多時候我們再進行第二次訓練時，即使使用完全相同的「hyperparameter」\n最後訓練出來的參數(「parameter」)結果也會完全不同。\n這似乎是不太好的現象，我們希望尋找的是一個最佳參數(「parameter」)解，\n結果不同，難道是「gradient descent」無效或是我們實作錯誤嗎?\n並不一定，我們從上圖可以看出，當我們在搜尋Loss的表面時，\nLoss的表面可能不單純像左圖只有一個山谷，\n像右邊多於一個山谷的才是比較常見的Loss表面，\n這個特性有個名稱叫做「凸性(convexity)」\n左邊的是「convex surface」(凸面) 右邊的是「non-convex surface」(非凸面) 為什麼loss surface可能會有不只一個最小值?\n這意思是在參數空間(parameter space)中有很多等值(或接近等值)的點，\n(這裡的等值 = 參數算出來的「loss」等值)\n也就是說這些參數的設置，都能是ML模型具有相同的預測能力。\n自己的註：\n也就是說，不同的參數組合，算出「一樣的loss」，等於具有「相同的預測能力」。\n我們會在稍後介紹神經網路(「neural networks」)再更仔細的討論這問題。\n(因為這樣的情況主要發生在神經網路(「neural networks」))\n我們只要先記得loss surface會因為擁有的最小值數量，而有不同數量的山谷。\n自己的註：\n最大值(最小值)有分成兩種：local maxima(minima), global maxima(minima)\nlocal指的是區域性的，也就是一個範圍內的最大(最小)\nglobal指的是全域性的，也就是全部範圍內的最大(最小)\n以山來比喻，山都會有主峰，主峰就是全部(global)最高的山，\n但除了主峰之外還有有次高峰、第三高峰\u0026hellip;，這些都是區域性(local)的最高。\n我們指的很多山谷就是因為有很多local minima，然而最深的谷只會有一個。\n我們在訓練時，時常會覺得完成訓練時間太長，我們要來思考怎麼樣可以加快我們的訓練速度?\n不過為了要討論這問題，我們先從比較高階的角度看所有的步驟，\n並從演算法的角度去分析他們的time complexity，\n我們將訓練的主要演算法分成三大步驟：\n★Step 1 : 計算導數(偏微分)，上圖\n當我們計算偏微分時，「計算的時間成本」會與我們需要放入loss function的「比較資料數量」成正比，也會與我們ML「模型參數的總量」成正比。 而「模型參數的總量」可以從數千到數億個不等。 同樣的「比較資料的數量」也可以從數千到到數千億不等。 自己的小註記：\nStep 1 : 計算導數(偏微分) 這階段影響訓練速度的關鍵在於：\n模型參數(「parameters」)的總量 比較資料(「label」)的數量 ★Step 2 : 參數更新\n在這張圖中，「每個循環」也代表會「參數會更新一次」，\n而這「時間成本」僅由模型內「參數的數量」影響，\n然後，相對其他步驟，「更新參數」時間的時間成本較小。\n自己的小註記：\nStep 2 : 參數更新 這階段影響訓練速度的關鍵在於：\n模型參數(「parameters」)的總量\n-\u0026gt; 但「更新參數」本身影響的時間相對其他步驟不多。 ★Step 3 : 檢查loss\n我們來想一下計算Loss的時間，這個步驟的時間複雜度，\n會與我們的「想要計算loss的總資料數(「label」數)」與「我們模型的複雜度」成正比，\n但特別的是，即使我們說這一個循環有這三個步驟，\n但「檢查loss」的步驟不需要每次都做，\n因為通常loss function的變化都是在逐漸好的方向變化。\n自己的小註記：\nStep 3 : 檢查loss 這階段影響訓練速度的關鍵在於：\n想要計算loss的總資料數(「label」數) 模型參數(「parameters」)的總量 檢查 loss 的頻率 所以經由上述簡單的討論，我們應該心理先有個底了，\n我們該怎麼做才能加快訓練的速度呢?\n首先，模型裡面的參數如果是固定的數量， 未來我們會提到我們可以將裡面的參數做「「正規化(regularization)」」 - 另外，雖然減少檢查loss的總資料數(「label」數)聽起來會有用，但我們通常不建議這樣做。 自己的註：\n「正規化(regularization)」：可以讓模型的數字被限制在一定的範圍，\n例如原本所有的ML模型內的參數可能介於「11000」，\n透過「正規化(regularization)」的縮放，我們可以縮小1000倍(只是舉例)，\n參數就會變成「0.0011」，這樣的好處在於我們可以避免做「大數運算」。\n取而代之的，我們有兩大手段作為我們加快訓練速度的關鍵：\n調整我們「計算偏微分時的資料總數」 調整我們「檢查loss的頻率」 正如同我們前面所說，我們能加快模型訓練速度的關鍵之一是「調整我們計算偏微分的資料數」\n記得：\n偏微分來自於我們的loss function，\n而我們的loss function是將很多預測的error組合在一起的值。\n所以這個方法需要減少「我們給loss function」的資料總數在每一輪「迭代(iteration)」中，\n到這裡，我們可以先停下來想一下為什麼這樣是可以的。\n為何這個方法沒有問題?\n主要是因為我們可以從我們的訓練資料中抽取樣本，\n而這個樣本與其他相比是平均且平衡的。\n我們在之後會講到關於抽樣的陷阱以及如何避免，\n而現在我們先知道抽樣的策略是從我們的資料集用統一的機率抽樣的。\n所以所有在訓練資料集的例子都有一樣均等的機會被模型看過。\n在ML中，我們將這種在從訓練集中抽樣的做法稱為「mini-batching」，\n原本我們所計算的「gradient descent」預設會使用所有的資料集，\n然後使用「mini-batching」的資料，我們可以算出「mini-batch gradient descent」，\n而這些資料被抽樣出的資料被稱為「batches」。\n「mini-batch gradient descent」具有許多的好處：\n花費更少的時間運算 使用更少的記憶體 很容易做平行運算 另外一個很常聽到的名稱「term batch gradient descent」，\nbatch指的是以分批(batch)的方式來處理，\n所以「batch gradient descent」會計算整個資料集，\n與「mini-batch gradient descent」不相同。\n這裡我們討論的是「mini-batch gradient descent」，\n但有個常讓人搞錯的地方是，「mini-batch size」時常被稱作「batch size」，\nTensorFlow也是這樣稱呼的，所以我們也這樣稱呼他。\n在之後的討論中，如果我們討論到有關於「batch size」的事情時，\n我們就是在討論「mini-batch gradient descent」的「樣本數大小」。\n自己的註：\n這邊名詞太多覺得很混亂嗎XD?\n簡單整理一下：\n名詞 種類 意義 「mini-batching」 方法(method) 抽樣的方法 「batches」 被抽樣到的資料(data) 使用抽樣的方法得到的資料(subset of dataset) 「batch size」 樣本數大小(size, number) 代表抽樣的數目、樣本數大小(size, number) 「mini-batch size」 同上 「mini-batch size」比較常被稱作「batch size」。 「mini-batch gradient descent」 方法(method) 使用抽樣的結果去做「gradient descent」 「term batch gradient descent」 方法(method) 使用分堆的方式，也就是將「全部資料都下去分堆」。與「mini-batch gradient descent」最大的不同在於抽樣「使不使用全部資料」(另外一個只抽樣) ★另外再送大家「batch size」與「迭代(iteration)」與「epoch」的概念比較：\n假設我現在有400筆資料，我做分堆：\n我決定「一堆的大小(batch size)」要有40筆資料，\n這樣一共會有10堆(通常稱為「batch number」,「number of batches」)，\n也就是說每一輪我要學10堆資料，也就是學10個「迭代(iteration)」。\n學完「10個「迭代(iteration)」」後，等於我把資料集全部都看過一次了。\n這樣就是一輪(「epoch」)訓練的結束。\n.\n會有「batch size」與「迭代(iteration)」與「epoch」這些詞的原因，\n是因為通常ML學習的資料量都很大，我們會需要分比較小堆。\n.\n參考資料：神經網路中Epoch、Iteration、Batchsize相關理解和說明\n我們來討論一下那「mini-batches」應該有多大?\n「batch size」也像「learning rate」一樣是hyperparameter，\n而且同樣的，這兩個的最佳值都是「因題目而異」的，\n我們一樣可以使用「hyperparameter tuning」的方法找到他，\n我們也會在後面一並詳細介紹。\n通常「batch sizes」會介於10~1000個樣本之間。\n另外一個我們提到加速訓練的關鍵是改變我們檢查loss的頻率，\n稍微想一下，儘管一個個檢查每一個資料集的子集合是好事，但未必對訓練時間是好的。\n上面是我們將我們的講法轉換後寫出的code，想法非常的簡單，\n我們加入一些邏輯，使得計算loss function的高時間代價能夠減少計算的頻率，\n現代檢查loss function的頻率很多有名的策略：\n基於固定時間的檢查(Time-based) -\u0026gt; 例如：每三十分鐘檢查一次loss 基於固定步驟的檢查(Step-based) -\u0026gt; 例如：每一千個步驟檢查一次loss 我們減少檢查loss的頻率，並且引入「mini-batching」這套方法，\n我們開始將模型訓練的兩個基本部分分開並各自調整，\n「更改我們模型的參數」與「檢查我們是否有做正確的修正」。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 23】 Google ML - Lesson 9 - 加速ML模型訓練的兩大方法(如何設定batch/檢查loss頻率)、batch size, iteration, epoch的概念和比較\n參考資料 coursera - Launching into Machine Learning 課程 神經網路中Epoch、Iteration、Batchsize相關理解和說明 若圖片有版權問題請告知我，我會將圖撤掉 ","date":"2019-09-24T01:19:23+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/201204242vKdw5D43n.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/batch-size-iteration-epoch/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】23 - Lesson 9 - 加速ML模型訓練的兩大方法(如何設定batch/檢查loss頻率)、batch size, iteration, epoch的概念和比較"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 而今天的文章我們要來討論一下所謂的「學習速率(learning rate)」，\n可以幫我們調整學習的「步長(「step size」)」。\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions 「Gradient Descent」 Gradient Descent 「Troubleshooting a Loss Curve」 ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 昨日我們詳細了介紹「Gradient Descent」的概念，\n以及Loss在參數空間所能畫出的等高圖，\n今天我們要稍微更細的討論一下Loss隨著時間所變化的圖，可能會有哪些情況。\n1. Troubleshooting a Loss Curve 課程地圖\nOptimization Gradient Descent Troubleshooting a Loss Curve 想像我們現在正在進行「Gradient Descent」的過程，\n我們「修改我們ML模型的參數」依照「loss function結果的偏微分」，\n我們通常會紀錄並觀察「隨著訓練時間，loss的變化」。\n(這就是通常我們machine learning在做的事情)\n整個ML的訓練個過程可能數小時甚至到數天，\n你可以想像這樣的過程是多麼的費時，因此考慮到這點，\n我們需要稍微研究一下loss curve，來看看有沒有什麼可能的困難。\n(上圖是一個常見且我們期望的loss curve與時間的對應圖。)\n如同我們所想像的一樣，我們在一開始時，我們的loss下降快速，\n因為我們的「Gradient Descent」算出較大的「step size」，\n後來隨著時間增加，loss curve漸漸變緩和了，因為逐漸靠近loss最小值，\n我們的「Gradient Descent」也算出較小的「step size」。\n但loss curve還有可能有其他的樣子，不一定我們每次訓練都這麼順利。\n我們也可以藉此判斷我們是否有合適的「step size」。\n假如我們的loss curve長得像上圖這樣，\n這表示我們走個幾步就跨越了好多山谷，\n這也表示「step size」還是太大。\n自己的註：\n「step size」只依靠「loss function的偏微分結果」，\n以二維來說，是我們該點的切線斜率。\n套用到三維來說，也就是我們所說的「梯度」，\n表示有點類似該點的有\u0026quot;多\u0026quot;陡、往\u0026quot;哪裡\u0026quot;陡 \u0026lt;- 不確定能不能這樣說?\n我們知道該點的「梯度」後，不見得這山就跟我們預測的一樣「直接」傾斜到山谷。\n所以只靠「loss function的偏微分結果」決定的方向與步伐大小，「步伐大小不見得每次適用」。\n假如我們的loss curve長得像上圖這樣，\n這表示我們可能在同一個山谷裡面，但我們可能要花很久很久時間才能到谷底。\n自己的註：可以看到有緩緩下降的感覺。\n這也表示「step size」還是太小了。\n上述的這兩個例子都是「step size」不合適的情況，\n因此，除了單純的只靠計算之外，我們還需要一個「可以縮放的參數」，\n這個「可以縮放的參數」我們稱之為「learning rate」，\n加上這個參數後，我們就有了最經典的「梯度下降法(gradient descent)」。\n我們可以看到上圖就是最經典的「梯度下降法(gradient descent)」實現code，\n我們可以注意到在移動變數位置時，\n我們的偏微分結果乘上了「「learning rate」」的值，\n透過「「learning rate」」我們可以更直接的調整我們的「參數移動大小」。\n「「learning rate」」的調整，可以依照不同學習問題而改變，使達到最佳學習效果。\n「「learning rate」」也是「hyperparameter」的一種，設定於ML模型訓練開始前。\n而為了找出「「learning rate」」最適合的值，\n有一種方法稱為「hyperparameter tuning」，這個會在之後的章節細講。\n自己的註：\n「「learning rate」」就是依據訓練題目有所不同而調整的值\n不同的題目需要的「learning rate」不同，\n「梯度下降法(gradient descent)」能幫我們決定方向與步伐大概的大小，\n但為了防止計算出的步伐太慢，可能要調整「learning rate」使其加快 也為了防止計算出的步伐太快，導致一步就走過頭，透過調整「learning rate」使其變慢 通常「「learning rate」」會是一個小於1的分數，\n而我們現在只需先簡單記得怎麼樣計算「梯度下降法(gradient descent)」，\n並將「learning rate」視為一個調整學習腳步的「固定參數」即可。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 22】 Google ML - Lesson 8 - 學習速率 (learning rate) 介紹, 透過「learning rate」調整ML模型訓練的學習速度\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-23T01:16:09+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424RQRcro5Yon.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/learning-rate/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】22 - Lesson 8 - 學習速率 (learning rate) 介紹, 透過「learning rate」調整ML模型訓練的學習速度"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 而今天的文章我們就要來介紹所謂的「梯度下降法 (Gradient Descent)」\n與ML模型中參數的修正「方向」與「步長(「step size」)」概念。\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\n「Optimization」 Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset Introducing Loss Functions 「Gradient Descent」 「Gradient Descent」 Troubleshooting a Loss Curve ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 1. Gradient Descent 課程地圖\nOptimization Gradient Descent Gradient Descent 在昨天的章節中，我們介紹了「損失函數(loss function)」的計算方式，\n然而「損失函數loss function」只能「告訴我們參數的好壞」，\n我們仍需要一個「修改參數的方法」，\n今天我們要介紹的「梯度下降法 (Gradient Descent)」，就是一種「修改參數的方法」。\n自己的註1：\n「損失函數(loss function)」是判斷誤差大小的計算方法，然而還需要一個「修改參數的方法」。像這邊介紹的「梯度下降法 (Gradient Descent)」就是一個基於「損失函數(loss function)」的值去「修改參數的方法」。\n自己的註2：\n現在機器學習可使用於「修改參數的方法」有非常多種，然而這邊只介紹最經典的「梯度下降法 (Gradient Descent)」，仍有其他好的「修改參數的方法」可以使用。\n「梯度下降法 (Gradient Descent)」，是一種「搜尋參數的策略」，\n他是在一個參數空間中的每個點所代表的loss上，沿著表面往下走的過程，如上圖。\n自己的註：\n我們從上一節可以知道一個點表示一組參數，而一組參數能算出一個loss值(代表誤差多少)，\n我們可以將這個「loss值的計算結果」想像成「山的高度」，而對應位置就是參數的點，\n就能夠畫出像上圖的等高線圖。\n然而我們通常不可能把所有的loss都計算出來，我們頂多知道要評估哪個點時，\n才會去計算那一個點的loss，例如說我們可能只知道像上圖的兩個點。\n但即使如此，我們仍然要知道接下來我們要往哪裡移動，才能找到最小值。\n自己的註：\n還記得「最小化loss」是我們的訓練目標嗎?\n另外我們所謂的「修正模型參數」，也就等同於「修正點的位置」，\n那「最小的loss」會在這個像山的圖的哪邊呢?\n當然是山谷的地方，所以上面才說「梯度下降法」像是「沿著表面往下走的過程」。\n我們把這個問題稍微拆解成兩個不同卻同樣重要的問題。\n我應該往哪個方向移動? 我應該要走多遠? 現在我們先做個簡單的假設，我們先「固定我們走一步的移動距離」，\n「走一步的移動距離」又稱為，我們只討論我們「該往哪個方向移動」。\n而這使得我們能得到上述的簡單演算法。\n當 loss \u0026gt; 某一個很小的常數(epsilon)時，我們先計算方向，\n然後對於模型中的參數(parameter)，\n設定新的值為我們現在的點加上【往我們要的方向「走幾步」乘上「步長(「step size」)」】，\n然後針對新的點計算新的loss。\n我們可以用地形圖或等高線圖的概念去想，\n等高線上的每一條線代表一定的深度。\n線與線的距離越近，表示那段越陡峭。\n就像上面這張圖的每一個點，我們可以從點與點之間看出點移動的方式。\n這就是一個從頂部邊緣開始漸漸往下走，直到走到最終的最小值。\n另外一個可以注意的點是：因為我們現在固定步長(「step size」)，所以每個點之間的距離是一樣的。\n我們再來試著想一個問題，如果步長(「step size」)太小，我們的訓練會花很多時間。\n但我們還是能夠保證能找到「可能的最小值」，\n這裡會說是「可能的最小值」是因為「最小值可能不只一個」，後面我們會再討論。\n自己的註1：\n如果步長(「step size」)太小，我們的訓練會花很多時間。\n這句話也可以想像為走一步的距離小，走道目標的時間就會長。\n自己的註2：\n另外「最小值可能不只一個」，是因為這張圖只有一個山谷。\n但想想現實生活中的山谷也應該不是只有一個吧? 這裡也是一樣的。\n(如上面這張圖，從開始點到走到山谷，步長(「step size」)越小，到山谷花的時間自然就要越長)\n既然我們說步長(「step size」)越小，花的時間越長，\n那我們走大步一點總會比較快了吧? 然而事情也沒有這麼順利。\n如果步長(「step size」)太大，你有可能從loss表面的其中一面甚至直接跳到另外一面，\n甚至有可能整個直接跳出這個山谷中，然後到了全新未知的地方，如上圖。\n因為這個原因，步長(「step size」)太大，很有可能導致ML模型模辦法收斂。\n自己的註1：\n可以想像成，人走一個超級大步，連山谷都跨出去都有可能的那麼大步。(現實中可能有點扯啦XD)\n可以想像成巨人之類的XDDD，總之太大步也不行，\n有可能直接跨出山谷，或跨到山谷的另外一面。\n自己的註2：\n這裡突然提到「收斂」一詞，其實我們確實在找谷底的過程就是在做「收斂」的動作，\n「當抵達谷底時」=「收斂完成」=「找到最小的loss」=「完成學習目標」\n不能收斂的原因就是跟上面所說一樣，走太大步了! 谷底都被跨過了! 找不到谷底了!\n從上面的例子我們就可以知道，我們應該要指定一個剛剛好的步長(「step size」)，\n不可以太大、也不可以太小。\n但想要找到這個剛剛好的值，似乎是沒那麼容易?\n我們觀察左邊的圖與右邊的圖，\n我們都給這兩張圖設定一樣的步長(「step size」)，\n左邊的圖沒問題。 但在右邊的圖中，一開始的移動也許還可以，但我們看到在接近山谷時，\n這個我們設定的步長卻讓他一腳跨過了山谷，造成了訓練失敗。 所以從上面例子我們知道一個固定的步長(「step size」)，\n似乎沒辦法適用於所有的ML模型，那我們該怎麼改變步長(「step size」)呢?\n我們這裡用一些斜率與曲線變化的速率，\n使我們對步長(「step size」)與方向(「direction」)更有概念。\n我們看上圖，圖下方表示圖上方圖曲線(此曲線就是loss的變化曲線)的各點斜率值，\n我們發現值較大的地方通常比值較小的地方離底部更遠。\n自己的註：\n(這裡的值指的是絕對值之後的值，也就是說下圖負越多或正越多離底部越遠。)\n如果「斜率值越小」，表示我們「快要到底部了」(「要走一小步」) 如果「斜率值越大」，表示我們「離底部還很遠」(「要走一大步」) 另外再注意：\n如果「斜率是負」，表示我們的「「谷底在右手邊」(「向右找最小值」) 如果「斜率是正」，表示我們的「谷底在左手邊」(「向左找最小值」) 我們換一個點看，例如點B，\n他有「「正的斜率」」，告訴我們要「「向左找最小值」」，\n另外他的「「斜率值很大」」，告訴我們「「要走一大步」」。\n我們再換另一個點看，例如點C，\n他有「「正的斜率」」，告訴我們要「「向左找最小值」」，\n另外他的「「斜率值很小」」，告訴我們「「要走一小步」」，以避免走過頭。\n我們現在就將我們的一開始所說的「固定的步長(「step size」)」，\n用一個新函數「computeDerivative」來取代掉，\n同時這個函數也能夠同時替我們決定「要前進的方向(derivative)」\n我們將原本的點減掉「loss值的偏微分」，以獲得新的點。\n自己的註：\n也就是說，我們對loss值偏微分，依照剛剛上面的概念，\n我們能同時獲得「應該前進的方向」與「要走多遠」。\n啊對了，這方法就叫做「梯度下降法 (Gradient Descent)」，\n這邊就已經介紹完了XDD，梯度就是指「loss的偏微分」，下降就是「找谷底」。\n「loss的偏微分」：y是「loss值」，偏微的對象x是所有模型內的參數(「parameter」)，\n可以參考更上方的二維圖：「y反應loss值的大小，而x反應的是參數所在位置。」\n而上圖中的點也可以注意他的變化：「該走快時走很快，該走慢時走很慢，且走的方向很正確。」\n我們似乎找到了一個非常好的方法，\n他能幫我們找到合適的步長(「step size」)與要前進的方向(direction)，\n但這樣就沒有問題了嗎?\n以經驗來說，ML的種種問題集之中，我們所能建的loss表面，\n這個算法通常會花費較多的時間，可能會找到次小值而非最小值，甚至是沒有完成。\n自己的註：\n「「花費較多的時間」」：「梯度下降法 (Gradient Descent)」相對比較新的算法來說，確實較慢，但並非不能用(下面也有提到XD) 「「找到次小值而非最小值」」：這確實是常見問題，我們可以想像等高線上有很多山谷，我們從找到第一個點時，就會開始往一個山谷的谷底直直前進。然而，如果這個山谷不是全部山谷最深的，那我們就找不到最深的山谷。不過目前也已經有新方法能解決這個搜索的問題。 「「沒有完成」」：有時候花費時間太長，而且特別是在接近底部的時候，可以想像一個問題，我們在做「y = 1/x」的畫圖時，那種趨近x軸卻永遠碰不到x軸的感覺(y無限接近0, x無限增加)，收斂的感覺也很像這樣，一直無限接近，但遲遲沒有到。 但「梯度下降法 (Gradient Descent)」仍然是一個常被使用的方法，\n這也表示像上述可能會出現問題的資料集，我們往往很少會碰到。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 21】 Google ML - Lesson 7 - 梯度下降法 (Gradient Descent)介紹,使用「梯度下降法」決定ML模型中參數修正的「方向」與「步長(step size)」\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-22T01:11:56+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/201204241oV3BHJ399.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/gradient-descent/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】21 - Lesson 7 - 梯度下降法 (Gradient Descent)介紹,使用「梯度下降法」決定ML模型中參數修正的「方向」與「步長(step size)」"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 而今天的文章我們就要來介紹所謂的「損失函數(Loss Functions)」的概念。\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\n「Optimization」 Introduction to Optimization Introduction Defining ML Models Defining ML Models Introducing the Natality Dataset 「Introducing Loss Functions」 Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 1. Introducing Loss Functions 課程地圖\nOptimization Introducing Loss Functions 在前面的章節中，我們定義的ML模型內的參數 「parameters」 與 「hyperparameters」，\n並介紹了 linear models 裡面的 「parameters」 大概會做什麼運算。\n然後我們就要討論如何去最佳化這些在ML模型中的「parameters」，\n在【Day 19】 中，我們曾經提過在數據集不大時，可以使用的統計方法。\n後來我們提到可以在參數空間(parameter space)試著搜尋最佳參數，\n但要「比較每個參數的好壞」，我們會需要一個「判定的準則」。\n今天我們就是要來細講這個「判定的準則」，\n我們稱這個準則為「損失函數(loss functions)」，\n這個函數就是用來幫助依照現在ML模型(裡面參數「parameters」)的預測結果，\n做好壞的評估，並且他會以「數值化」的方式告訴我們有多好/壞。\n1.1 「regression problems(回歸問題)」 的損失函數 我們將目前所預測的值(prediction)與真實數據(「label」)直接比較差多少，\n我們稱之為「誤差(「error」)」，我們可參考上圖。\n但在每次訓練中，我們有非常多組參考資料(「example」)，\n我們會得到一堆誤差(「error」)，\n我們需要去思考該怎麼組合這些數據。\n最簡單的方法，就是直接加總，例如使用sum。 \u003e 然而，我們想一個問題，如果直接加總的話，正值與負值會被抵銷， \u003e 例如誤差組合：(+100,-100)與(0,0)，sum值相同，但代表意義相同?! \u003e 顯然，這方法存在問題。 因此，為了解決上述問題，我們應該要找一個更具代表性的\n能象徵我們預測的值(prediction)與真實數據(「label」)的算法，\n而這算法不會使得「誤差(「error」)」之間相互抵消。\n那「誤差(「error」)」的絕對值之和呢? google只有提到會有問題，沒關係我自己來補充註： 這方法稱作「MAE(Mean absolute error)」 - 平均絕對值誤差，這方法是合理的，\n但會有「在等於0時」不可微分的問題(這個可以自己畫圖或看以下參考資料)\n不可微分會有什麼問題? 簡單來說，我們會沒辦法透過微分決定ML模型的修正方向。\n但完全不能使用嗎? 倒也不完全是不能用，他有它的長處，但這邊再談下去就太多了，\n★就留個可參考的資料給有興趣的人：機器/深度學習: 基礎介紹-損失函數(loss function)\n- 我們常用的方法：「MSE(Mean Squared Error)」 「MSE(Mean Squared Error)」的算法是從我們的所有數據中，\n拿預測的值(predictd value)與真實數據(labeled value)相減 所有的相減值皆平方(避免誤差正負相消)，並取總和 再除以總數量平均 MSE方法計算結果是很值得參考的，確實很適合作為我們的 loss function。\n但MSE方法仍然有個小問題，在「單位的解釋」上我們有點難以解釋數據。\n例1：計算「體重」誤差，請問「公斤的平方」意義是?\n例2：計算「金錢」誤差，請問「美元的平方」意義是?\n因此，我們會採用「MSE(Mean Squared Error)的平方根」，\n用 「RMSE(Root Mean Squared Error)」 以獲得我們能解釋的單位。 自己的註：\n注意：「MSE(Mean Squared Error)」在「數值」上仍然具有誤差代表性，並非不能使用。\n而使用「RMSE(Root Mean Squared Error)」只是更能解釋「數值」量的意義(因為有單位)。\n(RMSE的算法，少做開根號的動作即為MSE。)\n(圖中ŷ表示我們預測的值(predictd value)、y表示真實數據(「labeled value」))\n當「RMSE(Root Mean Squared Error)」的數值越大，同時也能表示我們預測的表現越差。\n所以訓練我們要做的事情就是「最小化「RMSE(Root Mean Squared Error)」」。\n現在我們找到了一個方法，能幫助我們在參數空間(parameter space)中衡量參數(parameter)的好壞。\n記得：參數(parameter)使用於我們的ML模型中，也就是我們線性模型(linear model)中的參數。\n我們稍微比較上面的的兩張圖，這是兩張散佈圖(資料集為【Day 19】的嬰兒資料)，\n我們只看39歲以上的母親並畫上回歸線，\n視覺上我們非常難看出哪條線畫的比較好。\n這就是為何我們要決定我們的「損失函數(loss functions)」，\n他可以數值化且具體的指出哪一條線比較好，\n於是我們使用我們剛剛決定的「損失函數(loss functions)」：「RMSE(Root Mean Squared Error)」\n我們發現左邊的模型目前的RMSE值為145、右邊的模型目前的RMSE值為149，\n因此，透過「損失函數(loss functions)」我們知道左側的「目前模型訓練的結果比較好」。\n「目前模型訓練的結果比較好」：表示有較好的「weight」與「bias」。\n1.2 「classification problems(分類問題)」 的損失函數 (用「RMSE」預測分類問題的結果。下方內容有圖片解釋。)\n但我們發現有個問題：「RMSE」作為「損失函數(loss functions)」，\n在「線性回歸問題(linear regression problems」)的表現很好，\n在「分類問題(classification problems」)似乎不行。\n我們先回到分類問題的本身，還記得當初我們定義的分類問題，\n他的結果是將目標分類。也就是我們的「label」會是一個「類別」而「非連續數」。\n我們拿之前提到的「編碼(encode)」為例，\n透過「編碼(encode)」，我們能將我們預測的類別「以「0或1」的方式」表示。\n我們回來解釋上方的圖，\nX軸表示我們的預測結果(prediction) Y軸表示(loss)，表示預測值(prediction)與實際值(「label」)的RMSE誤差 藍色為預測結果是0所畫出來的線 綠色為預測結果是1所畫出來的線 這曲線出了什麼問題呢?\n我們可以看見當目標target(「label」)為0時，預測結果1的比預測結果0.5的糟糕三倍\n這邊比較難懂，自己稍微補充解釋一下：\n例如明明是正確結果是0，我們預測1，loss自然就是全錯 = 1\n明明是正確結果是0，我們預測0，loss自然就是全對 = 0\n明明是正確結果是0，我們預測0.5，loss是「算誤差總共的RMSE」約等於 0.3\n(很多的「0.5-0」然後平方、除總數、開根)\n這也是為什麼明明是正確結果是1，我們預測0.5，loss是也等於 0.3\n因為也是(很多的「0.5-1」然後平方、除總數、開根)，一樣吧！\n這結果說明了什麼? 有些糟糕的預測應該有更強的懲罰，而且這預測完全不夠直覺。\n所以證明我們會需要一個新的「損失函數(loss functions)」，\n針對我們的「classification problems(分類問題)」能夠有更直覺的懲罰。\n(自己的註：「直覺的懲罰」的概念要比較下圖比較好懂，下面會解釋。)\n「交叉熵(Cross Entropy)」是最常使用於分類問題的損失函數(loss functions)。\n「交叉熵(Cross Entropy)」又有個別名「log loss」\n上圖我們做一個與用「RMSE預測分類問題」的結果類似的圖，\n我們使用「交叉熵(Cross Entropy)」做為新的「損失函數(loss functions)」。\n特別注意：圖中顯示「交叉熵(Cross Entropy)」會強烈處罰錯誤的預測。\n這邊比較難懂，自己稍微補充解釋一下：\n與上圖比較中，如果以藍線來說(真實label為1)，預測結果如果是0，\n看藍色取線的左側，套一句【Day 17】的內容，有沒有一種預測錯就非常完蛋的感覺?\n與上面「RMSE」相比，處罰嚴重太多了，分類問題正是需要在分類錯誤時有最嚴重的懲罰，\n我之前所說的「分類正確沒事，分類錯誤非常完蛋!!! 只要有分錯邊，誤差瘋狂上升」，正是類似這樣的概念~。\n我們再拿我之前【Day 17】做的表格看一下、順便複習一下吧： ★ 均方差(mean squared error) 與 交叉熵(cross-entropy) 的比較★\n常用的計算誤差方法 **均方差(mean squared error)** **交叉熵(cross-entropy)** 使用問題種類 「regression problem」(回歸問題) 「classification problem」(分類問題) 訓練目標 最小化「均方差」 最小化「交叉熵」 一維畫線(解)依據 只要能使最小距離就好(平方最小) 線畫下去就是要分好兩類資料，不可以有人跑錯邊(誤差會指數成長) 我自己的理解方式(不完全正確) 計算距離，所以不管在線的哪邊沒差，離線平均都近一點就能最小惹 分類正確沒事，分類錯誤非常完蛋!!! 只要有分錯邊，誤差瘋狂上升 完整內容請參考：【Day 17】 Google ML - Lesson 3 - 多維度線性回歸解(N-D Regression), 交叉熵(cross-entropy)與均方差(MSE) 作為誤差函數計算所帶來的不同\n我們下面來舉一個實際的例子。\n上面這個就是「交叉熵(Cross Entropy)」的公式，\n簡單拆解一下我們可以說分成「兩大terms」，\n有趣的是這個公式每次「只會有一個term」有反應。\n自己的註：「反應」等於「會產生loss的值」\n「Positive term」：當結果是1時有反應。 「Negative term」：當結果是0時有反應。 你問為什麼嗎? 自己的註：\n因為「0乘任何東西都是0，在Positive term出現0相乘就沒反應了。」\n然後也是因為「0乘任何東西都是0，在Negative term出現1相乘就沒反應了(因為「1-1=0」)。」\n這裡我們有一張表，正是一個圖片分類的問題，它秀出兩種已經被encode的labels，\n這裡的encode方法為「有人臉的為1，沒有人臉的為0」\n並且有我們的預測結果(predictions)與實際結果(label)，看目前預測的結果似乎不錯。\n我們先看上方的example，因為它真的是人臉，所以label=1，\n而我們預測0.7，我們發現後面的「Negative term」消失了(因為「1-1=0」)，\n而只剩下 「Positive term」 提供 loss。\n我們在看下方的example，因為它不是人臉，所以label=0，\n而我們預測0.7，我們發現前面的「Positive term」消失了(因為「0乘任何東西都0」)，\n而只剩下 「Negative term」 提供 loss。\n稍微計算一下結果，我們得到的Cross Entropy Loss = 0.13\n似乎是不錯的數值，顯示我們的模型結果不錯，然而我們來做個比較會更明顯。\n如果我們的模型沒有訓練好(做出好的預測)，結果會是多少?\n我們將下方的預測結果改為 0.8，也表示著下方結果目前是被錯誤預測(分類)的，\n我們計算Cross Entropy Loss = 0.42，Loss有增加，\n別忘了我們的訓練目標是要將「最小化Loss」，\n所以確實增加的Loss不是接近我們要的訓練結果。\n因此，上面介紹的方法就是我們如何在參數空間(parameter space)中比較參數的好壞，\n自己再註一下：參數(parameter)指的就是 「weight」 與 「bias」\n不論是使用 「RMSE(Root Mean Squared Error)」 作為回歸問題的Loss Functions，\n或是使用 「交叉熵(Cross Entropy)」 作為分類問題的Loss Functions，\n要記住我們的目標是找出最佳的參數，Loss Functions是我們參考好壞的依據，\n但知道如何衡量好壞後，接下來我們要講的是如何去尋找這些點?\n這個我們會在下一章「梯度下降法 (Gradient Descent)」提到。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 20】 Google ML - Lesson 6 - 使用損失函數(Loss Functions)來評估ML模型的好壞吧! MSE, RMSE, Cross Entropy的計算方法與特性\n參考資料 coursera - Launching into Machine Learning 課程\nmedium - 機器/深度學習: 基礎介紹-損失函數(loss function)\nmedium - Understanding binary cross-entropy / log loss: a visual explanation\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-21T01:06:29+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424y6sF5gs5H8.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/loss-functions/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】20 - Lesson 6 - 使用損失函數(Loss Functions)來評估ML模型的好壞吧! MSE, RMSE, Cross Entropy 的計算方法與特性"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 而今天的文章我們也會先介紹並且稍微分析一下，接下來我們要用來作為訓練範例的資料集。\n今天我們會繼續 Launching into Machine Learning 的第三章節~\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization Introduction to Optimization Introduction 「Defining ML Models」 Defining ML Models 「Introducing the Natality Dataset」 Introducing Loss Functions Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 1. Introducing the Natality Dataset 課程地圖\nOptimization Defining ML Models Introducing the Natality Dataset 決定訓練資料集 簡單來說這邊就是要介紹接下來我們要作為示範訓練的資料集了，\n資料集很重要的原因在於它可以直接影響我們要使用什麼「ML模型」。\n我們這次要訓練的是一些關於新生兒的資料集。\n問題情境：\n有些嬰兒剛出生時會需要緊急護理，然而能提供這樣護理的醫生卻不多，\n我們希望能夠妥善的分配醫生，使得真的有需求的嬰兒能得到需要的護理。\n我們將這問題轉換為ML問題：\n我們想預測的目標是，預測哪些嬰兒會需要醫生的照顧\n再來我們來思考一下什麼東西能作為我們的參考的依據(「feature」)，\n什麼東西能作為我們的訓練目標(「label」)，\n假設這裡有三種資料：母親的年齡, 嬰兒的生日, 嬰兒的體重，哪些是能用的呢?\n我們在這邊稍微題外話一下：\n有些專業領域的知識，會告訴我們某些資料是不可能相關的，\n例如(我任意舉例的)：能不能用一個人的身高、體重預測那個人的年齡?\n我們應該都知道這個相關性是薄弱的，但在ML模型裡面我們就是想試著找看看關聯性。\n但另外一個預測的重點，那就是這些資料要在我們想預測時，資料是「可取得的」。\n回到剛剛上面問題的答案：\n嬰兒的生日：我們必須要等嬰兒出生才能知道的資料，所以就是預測時還不可取得的資料。 嬰兒的體重：嬰兒的體重恰好就是嬰兒是否需要照護的重要指標之一。 母親的年齡：雖然不確定有沒有相關性，但母親的年齡確實是我們可以觀測的資料。 選擇好我們要使用的資料後，我們來想想我們剛剛所定義的問題：\n我們想預測的目標是，預測哪些嬰兒會需要醫生的照顧，\n也就是預測「寶寶的健康」，嬰兒的體重是「寶寶的健康」的參考指標之一\n我們就先將我們想預測的目標(「label」)訂為實際數據的預測「嬰兒的體重」，\n而「母親的年齡」可以做為我們參考的依據(「feature」)，\n這個問題我們要預測的是「嬰兒的體重」，為連續值，\n所以這是「「回歸問題(regression problem)」」。\n再把資料丟進ML模型之前，我們通常還要先看一下這個資料是否可能存在某些特性，而非全部雜訊(noise)。\n我們先觀察一下數據分布，我們將「母親的年齡」與「嬰兒的體重」做成了散佈圖。\n而這個資料並不需要把全部的資料都拿來丟，我們只要「取樣(sample)」即可：\n一方面是，全部的資料都丟，在計算上是不太合適的(太多資料) 另外一方面是，如果丟入大量資料，除了製圖困難，我們也很難解釋他 (這個資料來自BigQuery中的公開資料，為美國政府收集的natality data)\n稍微注意一下，這些資料似乎有小的正相關性。\n這邊我們還可以比較另外一張圖，我們一樣使用這兩種資料作為座標，\n但這次我們不做散佈圖，我們將資料分成各個資料組的累積量。\n先稍微比較一下兩者的特色：\n圖的種類 「散佈圖(scatter plot)」 「資料組的累積圖」 介紹 使用一個個資料點呈現的方式，標記在圖上 使用資料累積的量所畫出的圖 資料取樣 需要「取樣(sample)」，所以不會用到全部的資料 使用全部的資料累積值，所以不需要取樣 代表性樣本 要擔心「非代表性樣本」的風險(指比較特殊的資料，不夠有代表性) 製圖結果 每次製圖結果可能不同(因為取樣) 每次製圖結果皆相同 這張圖是由一共22GB的資料所繪成，我們試著觀察一下裡面的關係，\n這時我們就會發現，我們看見了一些散佈圖中看不出來的資訊。\n我們發現，寶寶的體重似乎在母親年齡30歲左右達到最大值，\n而隨著母親年齡的增長或減小，嬰兒體重逐漸減少。\n這表示這組資料存在著「非線性」的關係，且在散佈圖中是觀察不出的關係。\n但我們想基於昨天提到的線性模型(linear model)而分析他，似乎看起來不是那麼容易。\n事實上，我們用線性模型(linear model)想去建立一個非線性函數(non-linear function)的模型，\n這樣的例子有個名詞叫做「欠擬合(underfitting)」。\n也許你可能會問為什麼不使用更複雜的模型來解這個問題? 這是因為這邊作為教學示範的原因。\n而其他複雜的模型我們之後也會討論到，此外我們還會討論「過度擬合(overfitting)」的概念。\n但簡單來說，隨著「模型的複雜度」增加，「訓練過程的失敗風險」也會增加。\n我們回到原先題目的散佈圖，我們認為母親的年齡與嬰兒的體重成些微的正相關，\n於是我們先將我們的直覺判斷，以「藍線」來模擬這個概念，\n而決定藍線之後，我們再來想思考這條線是否應該要高還是更低? 斜率是否正確?\n要怎麼樣去決定其他的線「紅、黃線」沒有比這條「藍線」好?\n這邊先來個題外話一下：\n有學過統計的人，可能會知道有個「least squares regression」的方法，他能夠幫助我們在線型模型(linear models)中決定最佳權重(weights)，這方法在這題目中也確實可行。\n但問題在於這些解只能在「一定的數量規模」內使用，當數據量只要一大，為了解決問題使用統計方法的計算量就顯得不切實際。\n那如果統計方法不行的話，這裡該怎麼辦?\n我們可以使用「「gradient descent(梯度下降法)」」：\n我們先考慮在空間中搜尋最佳化變數，\n記得我們的線型模型(linear model)裡面有兩個變數，weight 和 bias。\n因為這兩個數字都是實數，我們可以考慮將這兩個變數的每個組合想像成是2D空間中的一個點。\n有這樣的想像後，我們要怎麼找到最佳解呢?\n首先，我們要認知到，因為input是實數，所以基本上他有無限的輸入可能，\n所以以上面的圖來說，沒辦法呈現出所有的結果，\n也就是說，我們沒辦法考慮所有的組合。\n(自己的註：他這邊指的是所有的weight 和 bias組合，在這張圖上)\n所以，我們採取的是找到點後，先進行計算，然後與我們training data比較\n自己的註：比較training data的「label」與「我們預測結果」與實際的差異。\n而為了做到這件事，我們需要建立能對每個點評估「我們預測的結果」品質的方法，\n換句話說就是計算「我們預測結果」的誤差值(error)的方式，\n用一個數值來表示我們「我們預測結果」的品質，\n而這個就是我們所說的「「損失函數(loss functions)」」，下章節我們將仔細介紹。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 19】 Google ML - Lesson 5 - 接下來幾天作為範例的「訓練資料集介紹」、範例「資料集訓練前分析」(順便補上整個ML訓練流程，作為系列文章中的訓練階段參考)\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-20T00:51:23+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424T0b8Gz1a65.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/training-data/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】19 - Lesson 5 - 接下來幾天作為範例的「訓練資料集介紹」、範例「資料集訓練前分析」(順便補上整個ML訓練流程，作為系列文章中的訓練階段參考)"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以「監督式學習(Supervised Learning)」為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 今天我們會直接進 Launching into Machine Learning 的第三章節~\n第二章節剩下的歷史部分，因為我稍微想了一下內容，真的要寫起來應該可以寫成七篇文章，\n然而因為很多都是比較舊的算法，我自己因為有研究正在做，會比較想先看新的作法，\n所以我先細寫新章節的內容，一樣的，有機會我會回去補完那部分XD\n(一天都可以寫一整個算法了XD)\nCourse - Launching into Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nOptimization 「Introduction to Optimization」 「Introduction」 「Defining ML Models」 「Defining ML Models」 Introducing the Natality Dataset Introducing Loss Functions Gradient Descent Gradient Descent Troubleshooting a Loss Curve ML Model Pitfalls TensorFlow Playground Lab: Introducing the TensorFlow Playground Lab: TensorFlow Playground - Advanced Lab: Practicing with Neural Networks Loss Curve Troubleshooting Performance Metrics Performance Metrics Confusion Matrix Module Quiz 1. Introduction to Optimization 課程地圖\nOptimization Introduction to Optimization Introduction 這個章節的名稱為Optimization，也就是說我們如何在訓練過程中優化我們的結果。\n對於如何優化，我們在這的學習目標是：\n評價model的表現，利用「loss functions」 以loss functions作為基準的使用「Gradient Descent」演算法 最佳化「Gradient Descent」演算法，使它的效率達到最高 使用「Performance Metrics」作商業決策 (ML的整個過程，在Optimization這一章節，我們會先跑其中前半的流程。)\n2. Defining ML Models 課程地圖\nOptimization Defining ML Models Defining ML Models 在介紹怎麼決定ML模型之前，我們先對整個ML的整個「訓練過程」有點概念：\n註：我們在【Day 3】- 什麼是ML? 為什麼ML最近才紅起來? 有提過，整個ML過程會有兩個stage\nStage1 - training\nStage2 - inference(推理)(也有些人稱為prediction(預測))\n所以這邊的「ML的整個訓練過程」，只有指Stage1的部分，\n並不是訓練完整個ML的過程就結束了。\n★ML的整個「訓練過程」：\n定義 ML models -\u0026gt; 決定模型 介紹 Loss Functions -\u0026gt; 「訓練過程中」，我們評估「目前訓練當下」好壞的依據 梯度下降法 (Gradient Descent) -\u0026gt; 依據評估決定訓練的修正方向 使用 TensorFlow Playground -\u0026gt; 視覺化的看上述的變化過程 訓練結果的表現 Performance Metrics -\u0026gt; 「訓練結束後」，\u0026ldquo;外部\u0026quot;評估我們的訓練結果如何 那開始來介紹 ML模型 吧！\n2.0. ML模型是什麼? 訓練目標是什麼? ML模型簡單說就是個數學函數，我們透過「修改參數」使等式盡量成立(接近目標)，\n自己的註：y=Ax+B, 我們去努力試出A, B是什麼 (「修改參數」指的就是修改A, B)\n而在ML模型裡面，參數也有分成 「parameters」 與 「hyper-parameters」，\n「parameters」 : 隨著模型訓練的過程中不斷變化的(實數)變數 「hyper-parameters」 : 開始訓練前就設定好的變數，之後開始訓練也不會變。 而依照歷史的變化，線性模型(linear models)是最早被使用的ML模型，\n甚至到現在仍然是一種重要且被廣泛使用的模型。\n我們在ML模型中所做的事情，就是不斷調整變數(parameters)，\n使所有作為我們input的「feature」，都能透過這個模型，output吻合訓練目標「label」。\n再來我們要講ML模型會有哪些種類，\n我們前兩天的辛苦介紹 Regression and Classification 這兩種問題的定義就是為了這邊使用：\n如果不清楚 Regression and Classification 問題是什麼與有什麼差別，\n詳細介紹可以先參考前面兩天的文，我們這裡直接拿結論的表格，\n【Day 16】 監督式學習(Supervised Learning)中兩大問題 - Regression \u0026amp; Classification(回歸與分類) 【Day 17】 多維度線性回歸解(N-D Regression), 交叉熵(cross-entropy)與均方差(MSE) 作為誤差函數計算所帶來的不同 問題種類 「regression problem」(回歸問題) 「classification problem」(分類問題) 答案(label)特性 預測的答案(label)為「連續」值 預測的答案(label)為「非連續」值 利用資料的方式 我們使用數學函數組合不同的「features」，預測出一個「連續函數」作為我們結果的「label」 我們用「features」創造一個決策邊界，這個邊界幫助我們區分(分類)出結果「label」 訓練目標 最小化「預測的結果」與「實際的結果(「label」)」的誤差 最小化「誤分類(misclassification)」的數量，也就是「預測的分類」與「實際上的分類(「label」)」的誤差要為最小。 常用的計算誤差方法 均方差(mean squared error) 交叉熵(cross-entropy) 學習類型 監督式學習(Supervised Learning) 監督式學習(Supervised Learning) 使用的ML模型 「Regression model」(回歸模型) 「Classification model」(分類模型) 我們這邊只看最後一行，回歸問題用回歸模型解，分類問題用分類模型解。(滿自然而然的吧XD)\n這邊就是要來實際講我們做了什麼「數學運算」，來解決這兩類問題。\n2.1. 「Regression model」 回歸模型 我們將上述在ML模型中所做的事情數學化，可以簡單表示成 y = mx + b，\nx是input，也就是我們參考的「feature」 y是output，也就是我們訓練的目標「label」 我們透過調整b(bias)、m(weight、權重)，使我們方程式能接近我們想要的結果 而這個概念示範雖然只有二維空間，但只要知道「labels」與「features」之前的關係，\n我們可以將這概念任意拓展到更高的維度，\n所有的inputs(「features」)經過我們的model，就會產生對應的outputs(「labels」)。\n也就是說，我們可能可以建立一個模型，只要給他所有的inputs(「features」)，\n他就會吐出所有你想知道的outputs(「labels」)。\n當我們增加input的維度(也就是同時輸入更多「features」)，\n我們上述所說的斜率m，也必須因此變成n維度，我們把這個m稱作「權重(weight)」\n視覺上的表示，在原本的一條線變成n維度的同時，\n這時原本我們所說的「斜率(m)」也變成了「超平面(ω, Omega)」，如上面的右圖。\n同時我們也思考一下，原本的式子 y = mx + b，由於此處變多維空間，\n我們改表示成 y = Xω + b，X 因為輸入變多了，所以變成多維值\n所以 ω與b 應該也要變成更高維的值，才能繼續使等式成立。\n自己的註：\nX 代表所有inputs(「features」)\nω 為「權重(weight)」\ny 依然是我們訓練的目標「labels」\nb 「偏差值(bias)」，調整用的參數\n我們稍微想一下，X如果變成二維向量(x1,x2)，\noutput也變成多維(y1,y2)，也就是同時預測多個項目，\n先看b，b需要變成高維(b1,b2)才能維持計算等式，\n而ω也是，至少也要變成二維矩陣才能算啊!\n★ 自己的一些小結論：\n線性回歸模型的例子看起來比較直觀，重點就是在解 y = Xω + b 中的 ω與b，\nX(「features」)與 y(「labels」) 我們一開始都已經有了，只差解出ω與b，\n而我們所說的訓練就是在調整模型裡面的所有參數。\n2.2. 「Classification model」 分類模型 分類模型相對比較沒有回歸模型那麼直覺，\n回歸模型就是找出 y = Xω + b 中的 ω與b後，我們就能用X來預測y了，\n但我們要怎麼樣用線性模型(linear model)做分類呢? 連續數字要怎麼做分類?\n為了討論怎麼樣將我們模型output的連續數字轉換成分類，\n我們需要將這些結果做「encode」，也就是將這些資料做成員編碼，\n來看他是不是屬於這個class的成員。\n最簡單的編碼方式就是二進位編碼，\n自己的註：如果你有什麼特性一類、沒有那個特性一類，\n(在【Day 17】 多維度線性回歸解(N-D Regression), 交叉熵(cross-entropy)與均方差(MSE) 作為誤差函數計算所帶來的不同也有用過這樣的方法。)\n當然有很多數據可能會需要分類成兩個以上，這方法依然有效，只要將每個類視為獨立的類即可。\n自己的註：例如 (A,非A)，(B,非B)、 (C,非C)，\n這邊有點小細節，獨立的類不是像之前分成 (~15%, 15%25%, 25%)，\n應該後面會介紹，沒有我再自己補XD\n使用二進位編碼還有個好處，我們能更容易的管理我們的任務，\n但以下的討論我們先以分兩類為主，這樣比較容易解釋。\n我們要找到一種方法，「依據二進位分類規則」找到我們的目標線，\n一個簡單的方法就是單純依靠輸出的結果。\n以圖來看，這條線就像是將我們的圖片分成兩大塊，在線上方的與線下方的各一區，\n我們就稱這條線為決策邊界(decision boundary)，區隔出這兩類。\n而且，決策邊界(decision boundary)不只是描述目前的資料，\n他的目標是預測我們還沒看見的資料，\n而這種可以擴展預測到還沒看見的資料的特徵，我們稱之為「泛化(generalization)」，\n「泛化(generalization)」的過程對ML相當重要，我們後續也會有很多的討論。\n★ 自己的一些小結論：\n這段我真的看超級久\u0026hellip;\u0026hellip;，我看完「Classification model」一直在想說，\n所以你不是沒講到怎麼找到這條分類線嗎?\n後來想了大概快三小時，我才想通\n這個階段主要只在介紹怎麼生出一條線， 就有點像我只是在「任意」決定 y=ax+b 的a,b而已， 至於怎麼「找到」(或說是「修正出」)這條線，那是後面「修正階段」的事情 原來我自己的文章版本中(現在改掉了)，我自己寫的註是 「自己的註：如果你有什麼特性就是0、沒有那個特性就是1」， 這是根據前面章節使用的方法延伸過來的，但我就是被這例子誤導， 我後來又看了無數遍影片內容才懂，原來他指的分類是， 「如果你有什麼特性一類、沒有那個特性一類」， 這個差別在哪? 也就是說當任意線被決定後(例如：y=3x+2好了)， 我們可以直接將「y \u003e 3x+2」與「y \u003c 3x+2」分成兩類， 而我一直在想是0、是1的問題才卡那麼久......真的是被誤導很久 不過想出來的感覺也滿爽的啦XD (希望我想的真的是對的XD) 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 18】 Google ML - Lesson 4 - 什麼是ML模型？訓練的目標? 回歸模型(Regression model), 分類模型(Classification model)的運算\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-19T00:47:47+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424Eh6Nsvgl5V.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/regression-classification-model/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】18 - Lesson 4 - 什麼是ML模型?訓練的目標? 回歸模型(Regression model), 分類模型(Classification model)的運算"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"今天的內容會很數學\u0026hellip;\u0026hellip;不過我盡量試著用自己的方式講的比較簡單一點了，\n裡面附帶一點我自己比較ㄎㄧㄤ的理解方式，但不一定完全正確就是XD，\n希望大家能比較舒服的看完比較數學的這部分(?)\n前言 我們繼續新課程 Launching into Machine Learning 的第二章~\n這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以監督式學習(Supervised Learning)為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) Course - Launching into Machine Learning 第二章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPractical ML Introduction to Practical ML Introduction 「Supervised Learning」 Supervised Learning 「Regression and Classification」 ML History Short History of ML: Linear Regression Short History of ML: Perceptron Short History of ML: Neural Networks Short History of ML: Decision Trees Short History of ML: Kernel Methods Short History of ML: Random Forests Short History of ML: Modern Neural Networks 討論提示: Modern Neural Networks Module Quiz Regression and Classification 課程地圖\nPractical ML Supervised Learning Regression and Classification 0. 一樣也先來個小整理，不過今天還有其他內容哦 ★ 均方差(mean squared error) 與 交叉熵(cross-entropy) 的比較\n常用的計算誤差方法 **均方差(mean squared error)** **交叉熵(cross-entropy)** 使用問題種類 「regression problem」(回歸問題) 「classification problem」(分類問題) 訓練目標 最小化「均方差」 最小化「交叉熵」 一維畫線(解)依據 只要能使最小距離就好(平方最小) 線畫下去就是要分好兩類資料，不可以有人跑錯邊(誤差會指數成長) 我自己的理解方式(不完全正確) 計算距離，所以不管在線的哪邊沒差，離線平均都近一點就能最小惹 分類正確沒事，分類錯誤非常完蛋!!! 只要有分錯邊，誤差瘋狂上升 1. 「結構化」資料 與 「非結構化」資料 今天我們先討論一下昨天來不及討論的「資料」部分，\n我們之前所看到的表格資料我們都可以稱之為「結構化」的資料，\n-\u0026gt; 自己的註：也就是「有行有列有數據的表格」都可以算。\n「結構化」資料是最常見的ML資料類型。\n對比「結構化」資料，自然就有「非結構化」資料，\n-\u0026gt; 例如：圖片、音樂、影片\u0026hellip;\u0026hellip; 都屬於非結構化資料。\n稍微想一下應該就會知道\u0026hellip; 這些資料應該都沒有直接的「數據」好分析吧(?)\n我們來看一下上圖，上圖是一個有關出生率的公開資料(BigQuery中找得到)\n我們來思考一個問題：我們能不能預測嬰兒什麼時間會出生?\n老樣子，我們還是把該確定的東西確定一下，\n預測的目標「label」：嬰兒出生的時間(週) 參考的依據「features」：我們這邊可以選擇媽媽的年齡、體重的變化\u0026hellip; 因為嬰兒出生的時間是連續值，所以這是個「regression problem」 接下來，我們可以很容易的對 BigQuery 下 SQL select 找出你要的資料，\n這樣你的ML準備的資料就齊全了。\n由「結構化」資料預測事情是非常平常且頻繁的，而且也比較容易完成。\n我們再看一個例子，我們可以更清楚「結構化」資料的方便性。\n我們當然也可以用這表格來預測其他事情。\n例如，如果我們想預測嬰兒的體重呢?\n預測的目標「label」：嬰兒的體重 參考的依據「features」：除了嬰兒體重之外的資料皆可以作為參考 因為嬰兒的體重是連續值，所以這是個「regression problem」 這樣的預測可以幫助醫院提早診斷新生兒會不會有體重過輕的可能，\n可以提早替嬰兒準備恆溫箱等等相關的準備，因此能夠預測嬰兒的體重十分重要。\n2. 思考問題 Q：對於這個資料集，你覺得他會適合用「linear regression(線性回歸)」還是「linear classification(線性分類)」分析?\n答案是，兩個都很適合！\n我們來思考一下為什麼，我們仔細觀察這個資料的分布，\n如果我們先拿掉資料的顏色與類別(class)，沒有任何幫助我們理解數據的線，如下圖\n這個資料感覺可以用兩條斜線可以貫穿其中，\n而線旁上下的偏移點可以視為雜訊(noise)，\n只要能這樣判斷，基本上他就是個很適合用linear regression分析的例子。\n我們想預測的值是Y，我們替這張圖加了一些不同的顏色與形狀，\n使這個數據的結果明顯的呈現兩條線的分佈，\n兩條線有不同的斜率與截距、而這兩條線的noise又各自有不同的標準差。\n我們將我們所預測的線真的加進圖中，我們可以更明顯感覺到他們的線性關係，即使有些小雜訊(noise)，這些線就是能使用「linear regression」作為分析的證明。\n2.1 一維的線性回歸 「linear regression」 我們先看一維「linear regression」的分析結果，我們添加一條綠線，\n這條綠線表示我們剛剛所認定的藍線與紅線的「linear equation」(線性組合)\n也就是 「藍線」與「紅線」 可以組合成 「綠線」。 (經過一些數學運算)\n例如： A(ax+b) + B(cx+d) = ex+f 的感覺 \u0026lt;- 我盡力表達線性組合的概念了XD\n注意：這條綠線不管是離 class A 或 class B 皆有段距離。\n這條綠線，換個角度說，我們也可以說他將整個空間分佈成兩等分。\n原因是因為我們經由回歸計算，我們就是在做「最小化均方差」的損失，\n假使我們計算正確，兩個類別(class)對於這條線應該會有相等的拉力，\n也就是在這時我們可得到均方差(mean squared error)最小值，\n大約會使他們的平均值等距。\n註：前面的課程有提到「linear regression」的學習目標就是在做「最小化均方差」，如果忘記可以回前面章節看\n因為每個類別(class)有不同的斜率與截距，比起對整體做「linear regression」，\n我們對於個別兩個類別(class)做「linear regression」效果應該會更好，\n也許就是我們這裡所繪製的藍線與紅線。\n★ google講到這邊沒有做一個小結，我自己稍微做點小結論：\n這邊自己稍微解釋一下，「linear regression」基本上是預測一條線，\n使我們能依照「一個」「feature」(X)去做結果的預測「label」(Y)，\n(這裡要特別強調「一個「feature」」，因為等等二維就不是一個了)\n也就是遵照這條線的規則，因此我們能得到預測答案，\n目前我們會依照我們所有可參考的東西，依照綠色線的預測說是我們預測的結果。\n但很明顯的，如果可以用兩條線(藍線與紅線)來做預測，預測誤差一定會小更多。\n2.2 二維的線性回歸 「2D linear regression」 剛剛我們做了一維的線性回歸，現在我們來做看看二維的線性回歸：\n二維的線性回歸，也就是說我們不再只依靠一個「feature」去預測「label」，\n也就是不再只靠X去計算出Y，\n這裡我們使用兩個「feature」，「X」 與 「資料所對應的class」，去預測「Y」\n「資料所對應的class」，我們先下個他在圖上的定義：\n如果該資料屬於A類別，他的特徵值就是1 如果該資料屬於B類別，他的特徵值就是0 所以我們就可以畫出下面這張圖：\n二維的線性回歸(「2D linear regression」)結果，\n不會再是一條「線」，而是由線變成了「平面」。\n老樣子我們還是確認一下我們現在的資料：\n我們想預測的目標「label」：Y 我們作為參考的依據「features」有兩個，X 和 class 兩組不同的數據之間形成了一個平面，\n這兩組數據現在因為有多了一個類別(class)的維度而分開，\n我們把最適合 A class 與 B class 的線加入圖中(紅藍線)，\n我們也一併把一維的線性回歸結果線(綠線)也畫進圖中，\n注意：這個平面並沒有包含上面所說的任何一條線，\n因為資料的雜訊(noise)使這些線傾斜於這個平面，\n反過來說，如果沒有這些雜訊(noise)，這三條線應該會完美的皆在這平面上。\n★ google講到這邊沒有做一個小結，我自己稍微做點小結論：\n二維的線性回歸(「2D linear regression」)解從「線」，變成了「平面」，\n換句話說，我們提供兩種「features」(X 和 class)，能得到一個預測結果「label」(Y)，\n數學上空間中的平面只要給其中兩點，就可以找到剩下一點，所以我們說解是「平面」。\n.\n這個解答比一維的線性回歸好非常多，還記得我們在一維線性回歸得到的答案「綠線」，\n不管怎麼看都與所有的資料點有段距離嗎?\n我們甚至還說，如果可以用「藍紅線的兩種線性解」分別表示答案，還能得到比「綠線」更好的預測\n.\n那為什麼這個二維的線性回歸解答比較好?\n因為這個平面找得到的Y，離實際上真實資料點超近啊！(點到平面的直線距離)\n你可能會問為什麼?\n因為這題目需要參考兩個參數(X 和 class)能有更好的預測結果(Y)，\n舉例子就是：a+b=c，但我今天只給a，要預測c多少\u0026hellip;\n不是不能預測(至少我們還掌握一半的關鍵a)，只是誤差很多而已嘛\u0026hellip;..XD\n.\n當然很多情況當然可能參考更多的參數，也就是更多的「features」能做到更好的預測。\n那就是更高維的線性回歸問題了 。\n2.3 線性分類 「linear classification」 我們的思考問題還沒有討論完呢XD，\n既然我們都說不管是「linear regression(線性回歸)」還是「linear classification(線性分類)」都很適合分析，那當然我們也要解釋為什麼「linear classification」線性分類也適合。\n但說到這，相信讀者應該也知道答案了，在剛剛的分析中，我們可以看到「線性回歸」所畫出來的線，\n也替我們做了一個非常好的資料分離，所以自然這問題使用「linear classification」(線性分類)也很合適。\n但我們想討論的是，這條一維的線性回歸最佳解，\n會恰好等於我們的「linear classification」(線性分類)問題的最佳決策邊界嗎?\n自己的註：決策邊界昨天有提過，簡單來說就是可以區分兩類別(class)的決定線。\n我們以一維線性分類器所計算出來的結果，劃出圖上的黃線。\n-\u0026gt; 注意：我們可以發現黃線非常接近綠線，但不完全等於。\n想想看為什麼會這樣呢?\n答：因為計算誤差方式的不同，也可以說是最小化誤差的目標不同。\n我們直接搬之前我做的表好了XD\n問題種類 「regression problem」(回歸問題) 「classification problem」(分類問題) 答案(label)特性 預測的答案(label)為「連續」值 預測的答案(label)為「非連續」值 利用資料的方式 我們使用數學函數組合不同的「features」，預測出一個「連續函數」作為我們結果的「label」 我們用「features」創造一個抉擇邊界，這個邊界幫助我們區分(分類)出結果「label」 訓練目標 最小化「預測的結果」與「實際的結果(「label」)」的誤差 最小化「誤分類(misclassification)」的數量，也就是「預測的分類」與「實際上的分類(「label」)」的誤差要為最小。 常用的計算誤差方法 **均方差(mean squared error)** **交叉熵(cross-entropy)** 學習類型 監督式學習(Supervised Learning) 監督式學習(Supervised Learning) 注意到「回歸問題」與「分類問題」學習目標是不同的嗎?\n分類問題的訓練目標是「最小化交叉熵(cross-entropy)」 而回歸問題的訓練目標是「最小化均方差(mean squared error)」 而這兩個計算誤差的差別\u0026hellip;先不講太深的數學，\n但簡單的來說，均方差(mean squared error)存在兩次處罰(quadratic penalty)的計算，\n他在算實際點(「label」)與我們預測點(「prediction」)的歐氏距離(euclidean distance)，\n並試著將這距離最小化\n就先簡單理解，他有平方嘛，平方不就誤差被放大(更小)了嗎? \u0026lt;- 至少我先這樣簡單理解XD\n分類問題所使用的交叉熵(cross-entropy)，\n實際分類(「label」)與我們預測分類(「prediction」)結果相同，處罰會線性的成長(等於幾乎沒差) 如果實際分類(「label」)與我們預測分類(「prediction」)是相反的，處罰會指數性的成長 我是理解成，才這麼簡單分成兩類而已?! 你對了就沒事，你錯了你就超級完蛋!!! \u0026lt;- 至少我是先這樣理解XD\n所以我們可以仔細看看剛剛的圖，我們分類問題的決策邊界線感覺更在綠色的上面一點，\n為什麼? 因為你看綠色線上有些紅點資料正在上面，\n綠線上也有些紅點的雜訊(noise)分佈，甚至有些紅點還越線跑到綠色那邊?!?!\n照我們剛剛上面的結論 (或者用我的理解也好XD)，\n我大分類問題使用交叉熵(cross-entropy)，你對沒事，你錯你就超級完蛋!!!!\n我怎麼可能允許你在我分界上? 還甚至是越界!!! 不可能!!!\n數學一點的講法就是，這個越界造成了非常大的誤差!!!\n會違背我們的學習目標「最小化交叉熵(cross-entropy)」\n而對於線性回歸(「linear regression」)來說，雖然誤差是二次方的，\n(二次方也表示，你在線的這側或對側，只要距離一樣對我來說意義都一樣(平方嘛)。)\n只要「距離盡可能的小」，就可以滿足線性回歸的學習目標「最小化均方差(mean squared error)」\n不過最後我們還是要提到，這個資料集，\n不論使用linear regression或linear classification來分析皆非常適合，\n不同於我們之前所預測的 tips 和 bills 的那個資料集(前面章節提過那個)，\n這問題反而只適用linear regression，不然就是非線性的分類 non-linear classification。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 17】 Google ML - Lesson 3 - 多維度線性回歸解(N-D Regression), 交叉熵(cross-entropy)與均方差(MSE) 作為誤差函數計算所帶來的不同\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-18T00:44:34+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424rIbVu2NnRT.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/cross-entropy-mse/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】17 - Lesson 3 - 多維度線性回歸解(N-D Regression), 交叉熵(cross-entropy)與均方差(MSE) 作為誤差函數計算所帶來的不同"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天我們繼續新課程 Launching into Machine Learning 的第二章~\n這幾天的文章會是一系列的，會需要一起看才比較能看懂整個ML模型的輪廓，\n然而因為一天能寫的內容量有限，所以我會在前言部分稍微說明我寫到哪。\n複習一下ML的整個訓練過程 因為ML模型的訓練階段章節內容會分很多部分，我們要先確認好自己在哪個階段，\n以免吸收新內容卻不知道用在內容的什麼地方。\n★ML的整個「訓練過程」：這裡以監督式學習(Supervised Learning)為例\n階段 要做的事情 簡介 訓練前 決定資料集與分析資料 你想要預測的是什麼資料? 這邊需要先知道 「example」、「label」、「features」的概念。介紹可參考：[【Day 15】](https://ithelp.ithome.com.tw/articles/10215499)，而我們這次作為範例的訓練資料集介紹在[【Day 19】](https://ithelp.ithome.com.tw/articles/10217666)。 訓練前 決定問題種類 依據資料，會知道是什麼類型的問題。「regression problem(回歸問題)」? 「classification problem(分類問題)」? 此處可參考：[【Day 16】](https://ithelp.ithome.com.tw/articles/10216585)、與進階內容：[【Day 17】](https://ithelp.ithome.com.tw/articles/10215946) 訓練前 決定ML模型(ML models) 依據問題的種類，會知道需要使用什麼對應的ML模型。「回歸模型(Regression model)」? 「分類模型(Classification model)」? 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431)，「神經網路(neural network)」? 簡介於：[【Day 25】](https://ithelp.ithome.com.tw/articles/10221227) (模型裡面的參數) ML模型裡面的「參數(parameters)」與「超參數(hyper-parameters)」 此處可參考：[【Day 18】](https://ithelp.ithome.com.tw/articles/10217431) 訓練中 - 調整模型 評估當前模型好壞 「損失函數(Loss Functions)」：使用損失函數評估目前模型的好與壞。以「MSE(Mean Squared Error)」, 「RMSE(Root Mean Squared Error)」, 「交叉熵(Cross Entropy)」為例。此處可參考：[【Day 20】](https://ithelp.ithome.com.tw/articles/10218158) 訓練中 - 調整模型 修正模型參數 以「梯度下降法 (Gradient Descent)」為例：決定模型中參數的修正「方向」與「「步長(step size)」」此處可參考：[【Day 21】](https://ithelp.ithome.com.tw/articles/10218980) 訓練中 - 調整腳步 調整學習腳步 透過「學習速率(learning rate)」來調整ML模型訓練的「步長(step size)」，調整學習腳步。(此參數在「訓練前」設定，為「hyper-parameter」)。此處可參考：[【Day 22】](https://ithelp.ithome.com.tw/articles/10219458) 訓練中 - 加快訓練 取樣與分堆 設定「batch size」，透過「batch」從訓練目標中取樣，來加快ML模型訓練的速度。(此參數在「訓練前」設定，為「hyper-parameter」)。與「迭代(iteration)」,「epoch」介紹。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 加快訓練 檢查loss的頻率 調整「檢查loss的頻率」，依據「時間(Time-based)」與「步驟(Step-based)」。此處可參考：[【Day 23】](https://ithelp.ithome.com.tw/articles/10219945/draft) 訓練中 - 完成訓練 (loop) -\u003e 完成 重覆過程(評估當前模型好壞 -\u003e 修正模型參數)，直到能「通過「驗證資料集(Validation)」的驗證」即可結束訓練。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) 訓練後 訓練結果可能問題 「「不適當的最小loss?」」 此處可參考：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317) 訓練後 訓練結果可能問題 「欠擬合(underfitting)」?「過度擬合(overfitting)」? 此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 性能指標 「性能指標(performance metrics)」：以「混淆矩陣(confusion matrix)」分析，包含「「Accuracy」」、「「Precision」」、「「Recall」」三種評估指標。簡介於：[【Day 28】](https://ithelp.ithome.com.tw/articles/10222317)、詳細介紹於：[【Day 29】](https://ithelp.ithome.com.tw/articles/10222697) 訓練後 評估 - 新資料適用性 「泛化(Generalization)」：對於新資料、沒看過的資料的模型適用性。此處可參考：[【Day 26】](https://ithelp.ithome.com.tw/articles/10221245) 訓練後 評估 - 模型測試 使用「「獨立測試資料集(Test)」」測試? 使用「交叉驗證(cross-validation)」(又稱「bootstrapping」)測試? 此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) (資料分堆的方式) 訓練前 - 依據上方「模型測試」的方法，決定資料分堆的方式：訓練用(Training)、驗證用(Validation)、測試用(Test)。此處可參考：[【Day 27】](https://ithelp.ithome.com.tw/articles/10222043) Course - Launching into Machine Learning 第二章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPractical ML Introduction to Practical ML Introduction 「Supervised Learning」 Supervised Learning 「Regression and Classification」 ML History Short History of ML: Linear Regression Short History of ML: Perceptron Short History of ML: Neural Networks Short History of ML: Decision Trees Short History of ML: Kernel Methods Short History of ML: Random Forests Short History of ML: Modern Neural Networks 討論提示: Modern Neural Networks Module Quiz Regression and Classification 課程地圖\nPractical ML Supervised Learning Regression and Classification 今天我們要來更細部的解說Regression與Classification的差別，昨天已經有先做簡單的比較了。\n一樣先講結論，想看詳細說明或例子再往下拉吧~ 不論是regression problem或classification problem，\n皆是屬於監督式學習(Supervised Learning)的問題。\n問題種類 「regression problem」(回歸問題) 「classification problem」(分類問題) 答案(label)特性 預測的答案(label)為「連續」值 預測的答案(label)為「非連續」值 利用資料的方式 我們使用數學函數組合不同的「features」，預測出一個「連續函數」作為我們結果的「label」 我們用「features」創造一個決策邊界，這個邊界幫助我們區分(分類)出結果「label」 訓練目標 最小化「預測的結果」與「實際的結果(「label」)」的誤差 最小化「誤分類(misclassification)」的數量，也就是「預測的分類」與「實際上的分類(「label」)」的誤差要為最小。 常用的計算誤差方法 均方差(mean squared error) 交叉熵(cross-entropy) 學習類型 監督式學習(Supervised Learning) 監督式學習(Supervised Learning) 一樣的我們再整理一下：\n「Supervised Learning」(監督式學習) 「Unsupervised Learning」(非監督式學習) 有預設可能的答案(label)，用「資料」做label的預測(學習目標) 無預設可能的答案(label)，通常是將「資料」做分組(分群)，再來依據分佈的結果說明「發現(學習目標)」。 預測問題可能的答案(predict) 描述問題(資料)的分佈現象(description) Regression problem 一樣我們先從昨天的例子來看，\n我們想從其他資訊「features」預測tip的價格\ntip是我們的預測的目標，也就是我們的「label」\n也因為tip是「連續值」，所以這是個「regression problem」。\n在regression problem中，我們的目標是使用數學函數去組合不同的「features」，\n這樣我們就能預測出一個連續值作為我們結果的「label」。\n我們先稍微把問題簡化一下，假設現在只考慮一個「feature」: 帳單(bill)\n而這個問題的數學函數結果正是上圖中顯示的綠線，我們可以從綠線看出bill與tip的關係。\n更近一步的我們就可以從bill去預測tip會有多少。\n而現在這題目因為只考慮一種「feature」，只有二維的線性關係，線性也表示著連續的結果。\n我們可以考慮更多的「features」，這個結果也會更多維度。\n在regression problems中，我們的目標是最小化我們「預測的結果」與「實際的結果(「label」)」的誤差，\n我們常用的計算誤差的方法叫做均方差(mean squared error)\nClassification problem 一樣也是昨天的問題，另外一個我們提到的例子，\n我們想從其他資訊「features」預測「顧客的性別」\n「顧客的性別」是我們的預測的目標，也就是我們的「label」\n也因為「顧客的性別」是「不連續值」，所以這是個「classification problem」。\n我們看與上面一樣的圖，我們想用tip和bill去試著預測顧客的性別，\n但我們會發現這是個不好的idea，我們的仔細看這張圖裡面，\n男女的分佈非常的不明顯，所以結果一定也不太好。\n這個例子就很好說明了當我們想預測的東西「不是連續」的差別，\n性別是一個不連續的資料，當作為我們的預測的目標(「label」)時，\n這就是一個「classification problem」\n在classification problems中，我們使用「features」預測的結果不會是一個連續的值。\n我們用「features」創造一個抉擇邊界，這個邊界幫助我們區分(分類)出結果「label」\n在這個例子中，一樣我們也會畫出一條線，而這條線的兩側會代表著兩種不同的類別(class)，\n例如隨便假設tip比0.18倍的bill值還大(紅線)，我們就預測這位顧客可能是為男性，\n(當然在這個例子中我們可以看出這樣的預測結果明顯是錯誤的，我們可以看到有很多反例)\n我們知道在這個例子中抉擇邊界(紅線)應該不會是線性的(non-linear)，\n在這張圖中我們取黃線部分的結果會是更好的，問題是我們要怎麼要確定黃線比紅線好?\n在classification problems，我們的目標是最小化「誤分類(misclassification)」的數量，\n也就是我們「預測的分類」與「實際上的分類「label」」的誤差要為最小。\n我們常用的計算誤差的方法叫做交叉熵(cross-entropy)。\n另外，就算是預測tip，如果我們不用明確知道tip的值，\n我們也可以用「classification problem」方式解，\n假設我們可以將tip分成低中高三個等級：\n高：高於total bill的25% 中：介於total bill的15%~25%之間 低：低於total bill的15% 換句話說，我們把價格用區間的方式「離散化」了，\n現在「tip等級分類」的問題就是一個classification problem，\n一般來說，原來的連續feature都可以被「離散化」成為分類特徵(categorical feature)\n當然，反過來將分類特徵(categorical feature)嵌入至連續空間(continuous space)也是做得到的，\n這我們會在後面的章節討論，\n但不論如何，regression與classification問題皆是「預測問題」，\n對比於unsupervised problems的「描述問題」，方向是截然不同的。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 16】 Google ML - Lesson 2 - 監督式學習(Supervised Learning)中兩大問題 - Regression \u0026amp; Classification(回歸與分類)\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-17T00:42:42+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424eO4Mk6dvo3.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/regression-classification/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】16 - Lesson 2 - 監督式學習(Supervised Learning)中兩大問題 - Regression \u0026 Classification(回歸與分類)"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天要來進新課程 Launching into Machine Learning ~\n我們先來介紹一下這課程裡面有哪些章節，這次的課程一共有四章~\nLaunching into Machine Learning 第一章節：\nIntroduction Introduction to Launching into ML Introduction Intro to Qwiklabs 第二章節：\nPractical ML Introduction to Practical ML Supervised Learning ML History Module Quiz 第三章節：\nOptimization Introduction to Optimization Defining ML Models Introducing Loss Functions Gradient Descent TensorFlow Playground Performance Metrics Module Quiz 第四章節：\nGeneralization and Sampling Introduction to Generalization and Sampling Generalization Sampling Demo of Splitting Datasets in BigQuery Lab: Creating Repeatable Dataset Splits Lab: Exploring and Creating ML Datasets Module Quiz 比較歷史的部分我會先跳過~\n或者先只帶到與最近的ML模型重疊的知識部分\n由於第一章介紹比較實驗，我們先從第二章開始吧~\nCourse - Launching into Machine Learning 第二章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPractical ML 「Introduction to Practical ML」 「Introduction」 「Supervised Learning」 「Supervised Learning」 Regression and Classification ML History Short History of ML: Linear Regression Short History of ML: Perceptron Short History of ML: Neural Networks Short History of ML: Decision Trees Short History of ML: Kernel Methods Short History of ML: Random Forests Short History of ML: Modern Neural Networks 討論提示: Modern Neural Networks Module Quiz 1. Introduction to Practical ML 課程地圖\nPractical ML Introduction to Practical ML Introduction 這一章節要來介紹實際的ML內容與ML的歷史，\n我們主要會講到以下重點：\n區分ML問題的主要類別，以及學習如何實作 學習近幾年機器學習是如何進化的歷史，學習這段過程中運用到的技巧與技術 知道為何最近深度學習這領域會如此受歡迎，並參考google使用ML於自家產品中的例子 2. Supervised Learning 課程地圖\nPractical ML Supervised Learning Supervised Learning Supervised Learning 與 Unsupervised Learning 是兩種最常見的ML模型，也是兩種不同的ML演算法，\n這裡我們先比較一下這兩種的差別。\n先講結論： Supervised Learning 與 Unsupervised Learning 比較 這表格我自己聽完這堂課做的，有興趣看課程內容筆記再往下拉，有很多例子：\n「Supervised Learning」(監督式學習) 「Unsupervised Learning」(非監督式學習) 有預設可能的答案(label)，用「資料」做label的預測(學習目標) 無預設可能的答案(label)，通常是將「資料」做分組(分群)，再來依據分佈的結果說明「發現(學習目標)」。 然後 Supervised Learning 可再細分兩種不同的model：\n「regression model」(回歸模型) 「classification model」(分類模型) 預測的答案(label)為「連續」值 預測的答案(label)為「非連續」值 這裡有些中文比較細膩的地方，是我自己找資料的心得：\nSupervised Learning：做分類、回歸 Unsupervised Learning：做分群(分組) 特別注意「分類」與「分群(分組)」的不同：\n分類 分群(分組) 你「會」知道那個「類」的名字，我們依照這個「類的準則」分類 你「不會」知道那個「群(組)」的名字，我們讓他們自己找相似的一組 例如：我有身高體重，我想預測是「男生女生」 例如：我有一堆人的資料，我想分看看這群人中有沒有哪一小群有相似的特性，可能都喜歡吃日式料理的一群、吃韓式料理的一群...... (注意這個「結果」是分完後才去解釋的，我們並不像男女分類一樣一開始就知道要「依照某個準則分類」) 「Supervised Learning」(監督式學習) 「Unsupervised Learning」(非監督式學習) 以下課程就是有很多很多的例子\u0026hellip;\u0026hellip;\n2.1 Unsupervised Learning 非監督式學習 例圖：Unsupervised Learning 的例子\n我們想透過這圖片了解的是 income(薪水) 和 tenure(年資) 之間的關係，\n並對員工進行分組(分群)，以了解是否有些人成長比較快速。\n「unsupervised問題有個很重要的特性在於「沒有一個基本的答案(結果)」。」\n以這個問題來說，對於所有的人而言，\n我們不是一開始就知道他的薪水與年資是在比較快還是比較慢成長的，\n我們是在分析結果後才發現所有人的分布呈現如上圖，「看到圖片時」才知道有兩大分佈。\n特別留意順序：先看到圖片分佈，才知道有人屬於比較快的、有人屬於比較慢的(也是這時才定義結果)。\n因此，unsupervised問題最主要是在解「發現」的問題，\n我們想知道「所有的數據」能不能「被分出組別」來。\n2.2 Supervised Learning 監督式學習 同樣的我們也來看一下 supervised問題，\n「supervised問題重要的特性在於「我們有預設的答案(label)」。」\n而 supervised Learning 能預測的問題答案有兩種模型：\n「regression model」 : 預測的答案為「連續」值。\n「classification model」 : 預測的答案為「不連續」值。\n例圖：supervised Learning 的例子\n我們想透過這圖片了解的是 Bill(帳單) 和 Tips(小費) 之間的關係，\n來看看我們能不能從一些跡象預測 Tips(小費) 是多少?\n我們先將上方的資料表格化一下：\n※名詞解釋：\n「example」 : 每一橫行各代表一個example。\n「label」 : 任何一個直行，表示我們想預測的目標。\n「features」 : 除了「label」外的其他直行，我們參考這些數據來預測label。\n回到剛剛的問題，我們想要預測 Tips(小費) 是多少?\n我們想「預測的目標(label)」就是「Tips(小費)」的那個直行 而「除了Tips(小費)之外的直行」，皆為我們的「features」 回到上面的兩種問題模型，從表格我們可以看出Tips(小費)為「連續值」，所以這會是一個「regression model」。 我們可以使用一行或多行的「features」試著去推測出「label」。\n在 supervised Learning 中，要使用多少 「features」 去預測 「label」 並沒有數量上的限制。\n我們以同樣的問題來看另外一個例子，如果今天我們想預測是「顧客的性別」呢?\n我們想「預測的目標(label)」就是「顧客的性別(sex)」所在的直行 而「除了顧客的性別(sex)之外的直行」，皆為我們的「features」 回到上面的兩種問題模型，從表格我們可以看出顧客的性別(sex)為「不連續值」，所以這會是一個「classification model」。 同樣的，我們可以使用一行或多行的「features」試著去推測出「label」。\n一樣要使用多少 「features」 去預測 「label」 並沒有數量上的限制。\n這邊我們再練習個幾題，確認大家到目前的觀念是清楚的：\n我們想要預測狗的品種，我們應該使用什麼 model? label是? features是? 狗的種類為非連續值，使用「classification model」\n狗的品種是我們的預測目標，為「label」\n其他狗的相關特徵都可以作為我們預測結果前的參考資料，為「features」\n我們想要預測狗的體重，我們應該使用什麼 model? label是? features是? 狗的體重為連續值，使用「regression model」\n狗的體重是我們的預測目標，為「label」\n其他狗的相關特徵都可以作為我們預測結果前的參考資料，為「features」\n另外我們舉個特別的例子，例如現在有間銀行想要預測交易是否是詐騙，我們可以怎麼做?\n很直覺的我們應該會想，不就只是分成兩類? 「詐騙交易」與「非詐騙交易」嗎?\n確實如此，但在現實中會有些問題，例如我們如何「絕對的」從我們的分析肯定一定是詐騙交易?\n他可能符合某些「feature」與「example」相似，但也有些不相似，這樣就會有誤判的可能。\n所以我們還是會發現有些特別的case會因為我們訓練的限制而陷入模稜兩可的狀況。\n我們的模型會需要蒐集更多的資料來判斷這種目前可能會模稜兩可的問題，\n因此在前面課程中的所提到的 human in the loop 的概念這時顯得十分重要，\n更多資料就能夠幫助我們分辨更多這些介於模稜兩可之間的分類問題。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 15】 Google ML - Lesson 1 - Supervised Learning 與 Unsupervised Learning 監督式學習與非監督式學習的介紹和比較\n參考資料 coursera - Launching into Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-16T00:36:06+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424T4oPEbIu19.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/supervised-unsupervised/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】15 - Lesson 1 - Supervised Learning 與 Unsupervised Learning 監督式學習與非監督式學習的介紹和比較"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 我們來做 How Google does Machine Learning 的第二個lab~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning Chapter 1 - Introduction to specialization 【Day 2】- 讓你的ML在Google雲端平台運行的五大階段 Chapter 2 - What it means to be AI first 【Day 3】- 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】- 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】- ML要成功的秘訣與策略 Chapter 3 - How Google does ML 【Day 6】- ML會失敗的最常見十大陷阱 【Day 7】- ML在企業運行的五大階段與注意事項 Chapter 5 - Python notebooks in the cloud 【Day 8】- 先來初步認識一下google雲端上執行ML的環境 【Day 9】 Lab 0 - 每次在google雲端上開始lab前都要的事前準備與注意事項 【Day 10】 Lab 1 - 在google雲端上分析地震資料與製圖，並儲存在雲端 【Day 11】- Cloud Shell 的介紹與 google雲的三代變化, 使用ML與一般演算法的比較與優勢 【Day 12】- google圖片辨識(Vision), 影片辨識(Video), 語音辨識, 語言翻譯, 自然語言辨識(NL) API功能總整理 【Day 13】 Lab 2 - 使用 BigQuery 與 Datalab 視覺化分析資料 Course - How Google does Machine Learning 第五章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud Module Introduction Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process Demo of rehosting Cloud Datalab Working with managed services Computation and storage Lab: Rent-a-VM Intro to Qwiklabs Intro to Renting-VM Lab Lab: Rent-a-VM to process earthquake data Lab debrief Cloud Shell Third wave of cloud Third Wave of Cloud: Fully-Managed Services Third Wave of Cloud: Serverless Data Analysis Third Wave of Cloud: BigQuery and Cloud Datalab Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery Machine Learning with Sara Robinson ML, not rules Pre-trained ML APIs Vision API in action Video intelligence API Cloud Speech API Translation and NL 「Lab: Machine Learning APIs」 「Lab: Pretrained ML APIs Intro」 「Lab: Invoking Machine Learning APIs」 「Lab Solution」 Lab: Machine Learning APIs 課程地圖\nPython notebooks in the cloud Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution 在這個lab中，我們將實作之前 【Day 12】- google圖片辨識(Vision), 影片辨識(Video), 語音辨識, 語言翻譯, 自然語言辨識(NL) API功能總整理 的API應用\n我們會示範如何進行API串接，讓我們能直接使用google已訓練好的ML API來直接實現功能。\npart 0 : (事前準備) 開啟 GCP console 請先參考 【Day 9】- 每次在google雲端上開始lab前都要的事前準備與注意事項 的內容，完成到運行中階段。 part 1 : (建立機器) 建立並執行 Cloud Datalab VM Step 0 : 打開 Cloud Shell\n如果不清楚 Cloud Shell 如何開啟，請參考 【Day 11】- Cloud Shell 的介紹與 google雲的三代變化, 使用ML與一般演算法的比較與優勢 Step 1 : 首先我們要先知道我們的 compute zones 在哪，我們可以透過以下指令知道我們所有的 compute zones 位置，我們會在其中一個 compute zones 運行我們的 Datalab。\ngcloud compute zones list 註：影片中的範例使用的是 U.S. Central。\nStep 2 : 我們透過以下指令創建我們的 datalab VM。\ndatalab create mydatalabvm --zone \u0026lt;ZONE\u0026gt; 「」: 這個必須更改為Step 2中，我們所找到compute zones的名字。 依照影片中的範例，我們填入：「datalab create mydatalabvm \u0026ndash;zone us-central1-b」 創建過程約需稍等五分鐘。\nStep 3 : 創建過程中，可能會碰到如紅框處的問題(詢問ssh passphrase)。\n第一個問題回答 \u0026ldquo;Y\u0026rdquo;\n第二三個問題直接按 \u0026ldquo;Enter\u0026rdquo; return即可。\nStep 4 : 直到看到下方處出現請你開啟 \u0026ldquo;Web Preview\u0026rdquo; 的訊息才算建立完成，\n這時按 Cloud Shell 的右上角，點選 Change Port，\n並更換我們的 port 至 8081，完成後點選 CHANGE AND PREVIEW。\npart 2 : (啟用Cloud API) 開啟 Cloud Source Repositories API Step 1 : 確定 Cloud Source Repositories API 是被開啟的:\n點選一下網址，記得要切換至\u0026quot;Qwiklabs所提供的帳號\u0026ldquo;開啟API應用。 https://console.cloud.google.com/apis/library/sourcerepo.googleapis.com/?q=Repositories\npart 3 : (複製專案) 在 Cloud Datalab 將專案複製下來 Step 0 : 接下來會直接使用 Cloud Datalab 跑 pyhton 程式\n如果不清楚 Cloud Datalab 的基本指令與運作，請先複習 【Day 8】- 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境 Step 1 : 我們回到 part 1 所開啟的 Datalab，按左上角開啟一個新的 notebook。\nStep 2 : 我們可以點選紅框處更改notebook的名字，例如我們更改成：\u0026ldquo;checkout\u0026rdquo;。\nStep 3 : 我們在第一格中貼上這段code，這段code主要是幫我們從github複製檔案下來。\n%bash git clone https://github.com/GoogleCloudPlatform/training-data-analyst rm -rf training-data-analyst/.git ※解釋：\n「%bash」:此指令打在開頭，表示這整格cell之後皆執行Bash指令。\nStep 4 : 我們確認是否檔案有被成功的複製下來，我們輸入「!ls」，只要有看到\u0026quot;training-data-analyst\u0026quot;資料夾就是成功了。\n!ls 結果如下圖：\n※解釋：\n「!ls」:!表示執行單行Bash指令，後面接指令內容，例如這段我們執行ls。\nStep 5 : 現在我們可以回到上一頁，點進\u0026quot;training-data-analyst\u0026quot;資料夾，裡面有我們這次會使用到的code。\n(點擊左上角的Cloud Datalab即可回到索引頁。)\nStep 6 : 開啟 mlapis.ipynb。\n檔案路徑為：「training-data-analyst \u0026gt; courses \u0026gt; machine_learning \u0026gt; deepdive \u0026gt; 01_googleml \u0026gt; mlapis.ipynb」\npart 4 : (取得API key) 取得圖片辨識, 語音辨識\u0026hellip;相關的ML API key 再來我們要先去啟用我們的API服務，這樣才能執行Vision API, Translate API, Speech API\u0026hellip;\nStep 1 : 在「APIs \u0026amp; services」找到「Library」\nStep 2 : 我們搜尋「Vision API」\nStep 3 : 確認「Vision API」已經被開啟\nStep 4 : 我們重複Step 2, 3，依序確認「Translate API」, 「Speech API」\u0026hellip;等要用的API，確認這些都是已經被開啟的。\nStep 5 : 我們需要去取得 credentials(類似金鑰的證明文件)，我們在「APIs \u0026amp; services」找到「credentials」\nStep 6 : 我們點選「Create Credentials」找到「API key」\nStep 7 : 我們就會得到我們的API key了，我們將他複製下來。\npart 5 : (實作API) 實作各種 API 我們要回到 part 3 的 mlapis.ipynb檔案。 Step 1 : 將我們剛剛所複製的API key取代原本key的地方。\nStep 2 : 執行第二格cell的pip程式碼，我們要安裝Python client的服務。\n翻譯語言 「Translate API」 Step 3 : 執行第三格cell的程式碼，這行是「Translate API」的測試。\n# running Translate API from googleapiclient.discovery import build service = build(\u0026#39;translate\u0026#39;, \u0026#39;v2\u0026#39;, developerKey=APIKEY) # use the service inputs = [\u0026#39;is it really this easy?\u0026#39;, \u0026#39;amazing technology\u0026#39;, \u0026#39;wow\u0026#39;] outputs = service.translations().list(source=\u0026#39;en\u0026#39;, target=\u0026#39;fr\u0026#39;, q=inputs).execute() # print outputs for input, output in zip(inputs, outputs[\u0026#39;translations\u0026#39;]): print(\u0026#34;{0} -\u0026gt; {1}\u0026#34;.format(input, output[\u0026#39;translatedText\u0026#39;])) 我們可以在紅框處看到執行結果，我們也可以在綠框處更改想要翻譯的語言。\n註：fr:French\n※解釋：\n我們先輸入了input字串，再拿這些inputs向伺服器請求此內容的翻譯，\n而伺服器會回傳給我們outputs，這些outputs就是翻譯的結果。\n圖像辨識 「Vision API」 Step 4 : 我們執行第四格cell的程式碼，這行是「Vision API」的測試。\n我們會需要一張圖做為input，而此處已經有一個路標作為我們的範例使用。\n# Running Vision API import base64 IMAGE=\u0026#34;gs://cloud-training-demos/vision/sign2.jpg\u0026#34; vservice = build(\u0026#39;vision\u0026#39;, \u0026#39;v1\u0026#39;, developerKey=APIKEY) request = vservice.images().annotate(body={ \u0026#39;requests\u0026#39;: [{ \u0026#39;image\u0026#39;: { \u0026#39;source\u0026#39;: { \u0026#39;gcs_image_uri\u0026#39;: IMAGE } }, \u0026#39;features\u0026#39;: [{ \u0026#39;type\u0026#39;: \u0026#39;TEXT_DETECTION\u0026#39;, \u0026#39;maxResults\u0026#39;: 3, }] }], }) responses = request.execute(num_retries=3) print(responses) ※解釋：\n我們可以注意到紅框處，他是一個雲端的圖片位置，就是我們的input圖片\n綠框處與籃框處，代表的是我們運行的「Vision API」是v1版本\n橘框處代表的是我們透過「gcs_image_uri」這個參數擺放我們的圖片內容(此處為變數「IMAGE」)，傳送至雲端\n(這裡直接使用雲端圖片位置會更快，因為我們不需要再次上傳圖片。)\n「GCS」就是Google Cloud Storage的簡稱\n紫框處代表我們要求作「TEXT_DETECTION」，代表文字偵測。\n我們請求 Vision API 幫我們分析這張圖的內容，他會回傳 JSON 格式給我們。\n我來看回傳的所有內容，\n※解釋：\n開頭的zh指的是中文\n「boundingPoly」 : 指的是bounding polygon，表示偵測這個文字邊界範圍所圍成的多邊形。\nStep 5 : 因為回傳的結果很多，我們往下一格cell看，這格cell能幫我們只先取得第一個文字片段。\nforeigntext = responses[\u0026#39;responses\u0026#39;][0][\u0026#39;textAnnotations\u0026#39;][0][\u0026#39;description\u0026#39;] foreignlang = responses[\u0026#39;responses\u0026#39;][0][\u0026#39;textAnnotations\u0026#39;][0][\u0026#39;locale\u0026#39;] print(foreignlang, foreigntext) ※解釋：\n第一行「description」: 幫我們取得文字內容 (可以看到剩下的中文內容)\n第二行「locale」: 幫我們取得語言種類 (得到zh，表示中文)\nStep 6 : 再往下一格cell看，這裡我們再使用一下「Translate API」幫我們翻譯內容變成英文。\ninputs=[foreigntext] outputs = service.translations().list(source=foreignlang, target=\u0026#39;en\u0026#39;, q=inputs).execute() # print(outputs) for input, output in zip(inputs, outputs[\u0026#39;translations\u0026#39;]): print(\u0026#34;{0} -\u0026gt; {1}\u0026#34;.format(input, output[\u0026#39;translatedText\u0026#39;])) ※解釋：\n可以看到紅框處分別表示翻譯的目標語言，以及翻譯的結果。\n自然語言辨識 「Language API」 Step 7 : 往下一格cell看，我們接下來要實作自然語言辨識的功能，我們已經預先準備好一些引文。\n我們直接來看這些內容的情緒如何。\nlservice = build(\u0026#39;language\u0026#39;, \u0026#39;v1beta1\u0026#39;, developerKey=APIKEY) quotes = [ \u0026#39;To succeed, you must have tremendous perseverance, tremendous will.\u0026#39;, \u0026#39;It’s not that I’m so smart, it’s just that I stay with problems longer.\u0026#39;, \u0026#39;Love is quivering happiness.\u0026#39;, \u0026#39;Love is of all passions the strongest, for it attacks simultaneously the head, the heart, and the senses.\u0026#39;, \u0026#39;What difference does it make to the dead, the orphans and the homeless, whether the mad destruction is wrought under the name of totalitarianism or in the holy name of liberty or democracy?\u0026#39;, \u0026#39;When someone you love dies, and you’re not expecting it, you don’t lose her all at once; you lose her in pieces over a long time — the way the mail stops coming, and her scent fades from the pillows and even from the clothes in her closet and drawers. \u0026#39; ] for quote in quotes: response = lservice.documents().analyzeSentiment( body={ \u0026#39;document\u0026#39;: { \u0026#39;type\u0026#39;: \u0026#39;PLAIN_TEXT\u0026#39;, \u0026#39;content\u0026#39;: quote } }).execute() polarity = response[\u0026#39;documentSentiment\u0026#39;][\u0026#39;polarity\u0026#39;] magnitude = response[\u0026#39;documentSentiment\u0026#39;][\u0026#39;magnitude\u0026#39;] print(\u0026#39;POLARITY=%s MAGNITUDE=%s for %s\u0026#39; % (polarity, magnitude, quote)) ※解釋：\n可以看到紅框處的「polarity」: 如果為正，表示情緒正面；如果為負，表示情緒負面\n紅框處的另一個「magnitude」: 表示這段文字中的情緒詞有多強烈 (非常強烈的正面詞彙或負面詞彙)\n可以以綠框處的結果作為參考。\n語音辨識 「Speech API」 Step 8 : 往下一格cell看，我們接下來要實作語音辨識功能，我們一樣已經先準備好了一段語音。\n這段語音被存放在雲端中，我們只要下指令請求他幫我們將這段語音轉成文字稿即可得到回傳的JSON檔案。\nsservice = build(\u0026#39;speech\u0026#39;, \u0026#39;v1\u0026#39;, developerKey=APIKEY) response = sservice.speech().recognize( body={ \u0026#39;config\u0026#39;: { \u0026#39;languageCode\u0026#39; : \u0026#39;en-US\u0026#39;, \u0026#39;encoding\u0026#39;: \u0026#39;LINEAR16\u0026#39;, \u0026#39;sampleRateHertz\u0026#39;: 16000 }, \u0026#39;audio\u0026#39;: { \u0026#39;uri\u0026#39;: \u0026#39;gs://cloud-training-demos/vision/audio.raw\u0026#39; } }).execute() print(response) ※解釋：\n圖中紅框處 : 語音檔的雲端連結。\n綠框處「confidence」 : 指的是他對這個語音轉換文字稿的結果有多少的信心水準。\n從圖片中可以看見，信心水準非常高。\n這個lab中我們利用了一些已經訓練好的ML模型，\n透過API的串接我們能直接運算出結果。\n我們想讓讀者知道的是，「並不是所有的ML訓練皆要從0開始建立模型」，\n很多已經訓練好的結果，例如圖片辨識或語音辨識，\n是可以直接使用而無需從頭開始建的。\n而之後的課程，我們就要開始學習「從0開始建立屬於自己的ML模型」。\n(Course - How Google does Machine Learning 完) 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 14】 Google ML - Lab 3 - Machine Learning APIs - (API實作篇) google圖片辨識, 語音辨識, 語言翻譯, 自然語言辨識(NL)\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-15T00:32:18+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424pkwfhOK579.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/machine-learning-apis/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】14 - Lab 3 - Machine Learning APIs - (API實作篇) google圖片辨識, 語音辨識, 語言翻譯, 自然語言辨識(NL)"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 我們來做 How Google does Machine Learning 的第二個lab~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning Chapter 1 - Introduction to specialization 【Day 2】- 讓你的ML在Google雲端平台運行的五大階段 Chapter 2 - What it means to be AI first 【Day 3】- 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】- 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】- ML要成功的秘訣與策略 Chapter 3 - How Google does ML 【Day 6】- ML會失敗的最常見十大陷阱 【Day 7】- ML在企業運行的五大階段與注意事項 Chapter 5 - Python notebooks in the cloud 【Day 8】- 先來初步認識一下google雲端上執行ML的環境 【Day 9】 Lab 0 - 每次在google雲端上開始lab前都要的事前準備與注意事項 【Day 10】 Lab 1 - 在google雲端上分析地震資料與製圖，並儲存在雲端 【Day 11】- Cloud Shell 的介紹與 google雲的三代變化, 使用ML與一般演算法的比較與優勢 【Day 12】- google圖片辨識(Vision), 影片辨識(Video), 語音辨識, 語言翻譯, 自然語言辨識(NL) API功能總整理 Course - How Google does Machine Learning 第五章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud Module Introduction Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process Demo of rehosting Cloud Datalab Working with managed services Computation and storage Lab: Rent-a-VM Intro to Qwiklabs Intro to Renting-VM Lab Lab: Rent-a-VM to process earthquake data Lab debrief Cloud Shell Third wave of cloud Third Wave of Cloud: Fully-Managed Services Third Wave of Cloud: Serverless Data Analysis Third Wave of Cloud: BigQuery and Cloud Datalab 「Datalab and BigQuery」 「Lab Intro: Analyzing data using Datalab and BigQuery」 「Lab: Analyzing data using Datalab and BigQuery」 「Lab Debrief: Analyzing Data using Datalab and BigQuery」 Machine Learning with Sara Robinson ML, not rules Pre-trained ML APIs Vision API in action Video intelligence API Cloud Speech API Translation and NL Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution Lab: Analyzing Data using Datalab and BigQuery 課程地圖\nPython notebooks in the cloud Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery 在這個lab中，我們將使用BigQuery去分析7000萬行左右的資料，\n並將結果以幾十行的Pandas DataFrame輸出。\n再來我們可以直接使用Pandas DataFrame的結果作資料視覺化。\n註：「BigQuery」 = 大量資料分析工具\n在這裡我們只需要數秒鐘就可以創建圖形，這是使用其他方法可能做不到的。\n然而，在互動式的開發流程中我們會很需要即時的分析，\n對於大量資料的處理來說，這樣的速度是重要的，\n你可能會想說，那就不要處理這麼多資料就好了啊?\n問題是，如果我們處理的資料量小，這就不會是個好的 machine learning practice的範例了。\n統計方法 與 machine learning 的關鍵差別? 另外一件事情是，我們想順便討論 統計方法 與 machine learning 的關鍵差別，\n關於我們如何處理離群值。\n在統計方法中，我們會傾向移除離群值。\n但在 machine learning 中，離群值也會是我們學習的內容。\n而且如果要學習離群值，我們也必須要有足夠的離群值資料，\n我們還需要確保這些離群值在資料集中被分配，做好管理完整dataset的工作就顯得重要。\n在這個實驗中我們提供了BigQuery能幫助你管理大量的dataset，\n然後能替我們帶來更習慣的資料結構(例如：Pandas)，\n我們也可以使用python的繪圖工具製圖，就是我們這個lab的主要內容。\npart 0 : (事前準備) 開啟 GCP console 請先參考 【Day 9】- 每次在google雲端上開始lab前都要的事前準備與注意事項 的內容，完成到運行中階段。\npart 1 : (建立機器) 建立 Datalab VM Step 0 : 打開 Cloud Shell\n如果不清楚 Cloud Shell 如何開啟，請參考 【Day 11】- Cloud Shell 的介紹與 google雲的三代變化, 使用ML與一般演算法的比較與優勢\nStep 1 : 首先我們要先知道我們的 compute zones 在哪，我們可以透過以下指令知道我們所有的 compute zones 位置，我們會在其中一個 compute zones 運行我們的 Datalab。\ngcloud compute zones list 註：影片中的範例使用的是 U.S. Central。\nStep 2 : 我們透過以下指令創建我們的 datalab VM。\ndatalab create mydatalabvm --zone \u0026lt;ZONE\u0026gt; 「」: 這個必須更改為Step 2中，我們所找到compute zones的名字。 依照影片中的範例，我們必須填入：「datalab create mydatalabvm \u0026ndash;zone us-central1-b」 創建過程約需稍等五分鐘。\nStep 3 : 創建過程中，可能會碰到如紅框處的問題(詢問ssh passphrase)。\n第一個問題回答 \u0026ldquo;Y\u0026rdquo;\n第二三個問題直接按 \u0026ldquo;Enter\u0026rdquo; return即可。\nStep 4 : 直到看到下方處出現請你開啟 \u0026ldquo;Web Preview\u0026rdquo; 的訊息才算建立完成，這時按 Cloud Shell 的右上角，\n點選 Change Port，並更換我們的 port 至 8081，完成後點選 CHANGE AND PREVIEW。\npart 2 : (啟用API) 開啟 Cloud Source Repositories API Step 1 : 確定 Cloud Source Repositories API 是被開啟的:\n點選一下網址，記得要切換至\u0026quot;Qwiklabs所提供的帳號\u0026ldquo;開啟API應用。\nhttps://console.cloud.google.com/apis/library/sourcerepo.googleapis.com/?q=Repositories\npart 3 : (分析資料) 使用 BigQuery 分析資料 Step 1 : 我們先從 Google Console開啟 BigQuery (紅框處)，一樣透過\u0026rdquo;Qwiklabs所提供的帳號密碼\u0026ldquo;來進行登入\n稍微確認一下 Project名稱是否與 Qwiklabs的名稱相同。\nStep 2 : 我們可以按 COMPOSE QUERY 開始執行QUERY的視窗。(紅框處，實際介面可能稍有不同。)\nStep 3 : 在執行QUERY的視窗輸入以下指令，並執行。\n#standardSQL SELECT departure_delay, COUNT(1) AS num_flights, APPROX_QUANTILES(arrival_delay, 5) AS arrival_delay_quantiles FROM `bigquery-samples.airline_ontime_data.flights` GROUP BY departure_delay HAVING num_flights \u0026gt; 100 ORDER BY departure_delay ASC 這是 standard SQL的語法。\n但同樣的我們也可以不使用standard SQL的語法，我們點選 Show Options。\n我們可以看到更多選項，並取消勾選 Legacy SQL。\n我們在稍微注意一下語法中From的部分，我們會發現這個dataset來自 bigquery-samples 中的 airline_ontime_data，而 table 的名字是 flights。\nStep 4 : (練習題) 試從以下的結果判斷：35分前離開的班機延遲抵達時間的中位數是多少?\n說明：我們先看Step 3中例子的圖來解釋，「departure_dalay」:表示班機離開的時間，-37也表示37分鐘前離開。\n我們可以看到在「num_flights」中找到107個班機35分前離開，這些格子細分成五格表示五個級距，\n-66 表示80%飛機抵達時提前66分鐘或更早，-41~-66 表示 60%~80% 飛機在這個區間抵達。\n解答: 所以如果我們問你35分前離開的班機延遲抵達時間的中位數是多少? 答案就是提早28分鐘到達。\nStep 5 : (練習題2) 試著寫一個Query找出：擁有最大的航班數的airport pair (departure and arrival airport)。(指的是兩機場間的航班數最多)\n參考解答：\n#standardSQL SELECT departure_airport, arrival_airport, COUNT(1) AS num_flights FROM `bigquery-samples.airline_ontime_data.flights` GROUP BY departure_airport, arrival_airport ORDER BY num_flights DESC LIMIT 10 解釋：\n我們先用「GROUP BY」，將 departure_airport, arrival_airport選定 並在 departure_airport, arrival_airport 用 「COUNT(1) AS num_flights」，計算兩機場的總航班數 「ORDER BY」可以幫助我們排序，「num_flights DESC」，表示 num_flights的降序排列，因為透過降序我們就可以找到最多數量的位於最上方。 「LIMIT」限制10，因為我們只要找最多，所以也不需要顯示太多結果。 結果：LAX(起飛) 與 SAN(降落) 一共有 133394 航班數。\n我們約花2.3秒搜尋了7000萬筆的資料。看似不可思議，但其實這就是數以千計的電腦一起搜尋的結果。\n這就是我們所說的 run at scale，也是雲端運行服務，我們不必自己負擔運算過程，達到serverless的效果。\npart 4 : (視覺化結果) 使用 Datalab 建立視覺化結果 BigQuery分析東西很方便，但他沒辦法幫我們視覺化的分析結果，\n我們現在用Datalab來幫助我們建立視覺化結果。\nStep 0 : 接下來會直接使用 Cloud Datalab 跑 pyhton 程式\n如果不清楚 Cloud Datalab 的基本指令與運作，請先複習 【Day 8】- 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境\nStep 1 : 我們回到 part 1 所開啟的 Datalab，按左上角開啟一個新的 notebook。\nStep 2 : 我們在第一格cell中貼上這段code，這段code是呼叫BigQuery幫我們執行的，我們只是透過API串接過來使Datalab能夠使用。\nquery=\u0026#34;\u0026#34;\u0026#34; SELECT departure_delay, COUNT(1) AS num_flights, APPROX_QUANTILES(arrival_delay, 10) AS arrival_delay_deciles FROM `bigquery-samples.airline_ontime_data.flights` GROUP BY departure_delay HAVING num_flights \u0026gt; 100 ORDER BY departure_delay ASC \u0026#34;\u0026#34;\u0026#34; import google.datalab.bigquery as bq df = bq.Query(query).execute().result().to_dataframe() df.head() 而在code的最後幾行.to_dataframe()，將我們回傳的資料轉成 Pandas Dataframes。\n我們也可以透過.head()，從結果中先偷看前幾行的內容。\n也正如同我們之前在BigQuery所執行的一樣，有departure_delay, number of flights\u0026hellip;，\n但這例子中我們有10分位數，因為我們在「APPROX_QUANTILES(arrival_delay, 10)」中下了數字10，所以我們可以得到10個數字。\nStep 3 : 我們在第二格cell中貼上這段code。\nimport pandas as pd percentiles = df[\u0026#39;arrival_delay_deciles\u0026#39;].apply(pd.Series) percentiles = percentiles.rename(columns = lambda x : str(x*10) + \u0026#34;%\u0026#34;) df = pd.concat([df[\u0026#39;departure_delay\u0026#39;], percentiles], axis=1) df.head() 這段code能幫助我們把十分位數的部分整個分開整理清楚，如下圖。\n分開的原因與下步驟的製圖有關係。\nStep 4 : 我們在第三格cell中貼上這段code。\nwithout_extremes = df.drop([\u0026#39;0%\u0026#39;, \u0026#39;100%\u0026#39;], 1) without_extremes.plot(x=\u0026#39;departure_delay\u0026#39;, xlim=(-30,50), ylim=(-50,50)); 在第一行.drop(['0%', '100%'], 1)，我們先將0%, 100%的結果移除，不畫入圖中，\n我們在最後的結果只使用10%~90%的資料。\n我們試著閱讀這張圖，departure_delay如果是10，表示10分鐘delay，\n我們可以看見10%的班機仍然可以提前到達，但90%的班機會晚21分鐘以上到達。\n另外一方面中位數(50%)的 departure_delay 所對應到的 arrival_delay 約提早3~4分鐘。\n我們看結果會發現，除了一些 -20 以下的結果，這些結果的呈現滿線性的，\n我們的結果只要在中間不在邊緣，基本上可以做一個線性模型。\n現在我們可以回來想，我們分析了多大的資料才能得到這樣的結果，\n這樣的insight，正是一種很難透過其他方式能獲得的結果。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 13】 Google ML - Lab 2 - Analyzing Data using Datalab and BigQuery - 使用 BigQuery與Datalab視覺化分析資料\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-14T00:29:51+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424TDJbI4eAMB.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/datalab-and-bigquery/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】13 - Lab 2 - Analyzing Data using Datalab and BigQuery - 使用 BigQuery與Datalab視覺化分析資料"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 開頭先整理一下今天的內容 我們今天一共會整理google的5種Cloud API，以下針對類型先做個簡單整理：\nGoogle雲端上的API名稱 類型 用途 「Cloud Vision API」 (image -\u003e text) **圖片辨識API** 「Cloud Video intelligence API」 (video -\u003e text) **影片辨識API** 「Cloud Speech API」 (audio -\u003e text) **語音辨識API** 「Cloud Translation API」 (text -\u003e text) **語言翻譯API** 「Cloud Natural Language API」 (text -\u003e text) **自然語言處理API** Course - How Google does Machine Learning 第五章節的課程地圖： 「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud Module Introduction Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process Demo of rehosting Cloud Datalab Working with managed services Computation and storage Lab: Rent-a-VM Intro to Qwiklabs Intro to Renting-VM Lab Lab: Rent-a-VM to process earthquake data Lab debrief \u0026lt;\u0026ndash; (昨天到這邊) Cloud Shell Third wave of cloud Third Wave of Cloud: Fully-Managed Services Third Wave of Cloud: Serverless Data Analysis Third Wave of Cloud: BigQuery and Cloud Datalab Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery Machine Learning with Sara Robinson ML, not rules 「Pre-trained ML APIs」 「Vision API in action」 「Video intelligence API」 「Cloud Speech API」 「Translation and NL」 Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution 1. Vision API in action 課程地圖\nPython notebooks in the cloud Pre-trained ML APIs Vision API in action Cloud Vision API是google的圖片辨識的API，\n可以讓我們使用單個REST API request就能得到圖片的各種檢測結果。\npart 1 : case: 先看目前商業上使用Vision API的範例 Giphy：Giphy是一個app，我們能在上面搜尋GIF，並在也可以分享這些動圖。\ngif的裡面有可能包含文字，而它們可透過Vision API解釋gif裡面的內容，\n並藉此改進搜尋結果。\n他們使用的是Vision API裡面的「OCR(Optical Character Recognition)功能」，\n這個功能讓它們可以從gif中提取出文字內容，用於幫助增進他們的搜尋體驗。\n想要更了解Giphy怎麼做的，可以參考：https://engineering.giphy.com\npart 2 : 我們來看Vision API裡面有哪些功能 Vision API 功能名稱 用途 「label detection」 可以告訴你這圖片是什麼 「web detection」 在網路上搜尋相似圖片，並從這些搜尋結果中提取內容，回傳圖片更多相關資訊。 「OCR (Optical Character Recognition)」 解析你圖片中的文字內容，告訴你圖片中哪裡有文字，甚至可以告訴你這是什麼語言。 「Logo detection」 專門辨識公司logo用的 「Landmark detection」 如果這圖片包含著常見的地標，他可以告訴你是什麼地標。 同時提供對應地標的經度、緯度 「Crop hints」 可以幫助你裁剪照片，以符合你想要做的主題。 「Explicit Content Detection」 檢測圖片中是否有不適當的內容。 ※補充：\n「label detection」 例如：你給他看大象的圖片，他會告訴你這是大象\n以整體來說，Cloud Vision API可以提供上述所有功能的圖片內容檢測。\n這些功能在任何地方皆很實用，而且特別像是社群網站檢測不適當圖片內容，\n我們很難人工一張一張的檢測，這時候Vision API能夠幫助我們做初步的分類，\n我們只需要簡單所有圖片的子集合即可。\n自己的註：這邊講到的做法個人認為非常聰明，\n他們並不是直接將Vision API認定不適宜的結果，\n直接進行刪除的動作，而是先取這些圖片的子集合在\u0026quot;人工檢查\u0026quot;。\n這樣兩階段的作法，\n對於社群上的人：大幅的減少誤判機率\n對於自己訓練的ML：如果我們認為這些圖片是適合的，\n我們可以直接更改label，再次強化我們的ML判定。\n又能減少使用者不滿又能強化自己的ML模型，採用兩階段判定確實非常聰明。\npart 3 : 我們直接來demo Vision API的功能 Vision API有提供網頁版，在開始寫code串Vision API的功能前，\n我們可以先到這網站測試自己想要偵測的圖片：https://cloud.google.com/vision/\n可以先偷看串完Vision API會得到什麼結果。\n(這邊也是先使用影片中的範例，有興趣的朋友可以自己嘗試。)\nStep 1 : 首先我們先到網址中的紅框處，可以上傳自己的圖片。\nStep 2 : 得到我們圖片分析的結果。\n我們來看看我們可以得到什麼結果：\n「Landmarks」: 顯示了這圖中的地標在哪(71%表示信心指數，有多肯定這答案的意思)，我們還可以看到下方標出了實際上地圖的位置與衛星雲圖。 「Faces」: 顯示了這張圖中哪裡有臉；還有辨識表情，像這張圖他認為圖片中的人很開心。\n「Labels」: 告訴你這張圖可能的組成元素，與這張圖的標籤。\n「Web」: 如果拿這張圖到網路上搜尋類似圖片，會得到什麼結果的分析整理。\n「Properties」: 告訴你這張圖片的主色調組成。\n「Safe Search」: 告訴我們這張圖片是否有不適當內容，主要分成四種判定標準 Adult: 圖片內容是否色情 Spoof: 圖片內容是否像是meme類型的(可能有些網站要防止meme類型的內容不斷散播) Medical: 圖片內容是否像是做外科手術 Violence: 圖片內容是否暴力血腥的 很明顯的，這張圖片都沒有包含以上的分類要素。\n「JSON」: 將上面所有結果的內容包成JSON給你，也就是實際上串好API會拿到的東西。 2. Video intelligence API 課程地圖\nPython notebooks in the cloud Pre-trained ML APIs Video intelligence API Video intelligence API是google的影片辨識的API，\n可以讓我們分析一個影片內容，從一瞬間、一幀到一整個影片的分析都可以做到。\npart 1 : Video intelligence API裡面有哪些功能 Video intelligence API 功能名稱 用途 「label detection」 他會告訴你這影片中可能有什麼(代表著他的label) 「Video \u0026 scene-level annotations」 從各個場景或整個影片\"解釋\"影片的內容，算上面功能的進階版 「Shot Change Detection」 告訴你這個影片哪裡有(突然的)場景變化。 「Explicit Content Detection」 檢測影片中是否有不適當的內容。 「Regionalization」 使我們可以指定這個影片可以執行Video intelligence API的區域 ※補充：\n「Video \u0026amp; scene-level annotations」 自己的註：要\u0026quot;解釋\u0026quot;影片可以從很多個角度切入，\n例如說一個場景可以解釋出一個意義，\nscene等級的功能就是在做這樣的分析，而video等級的分析就是做整部影片的解釋。\n與label detection不同的是，label只會告訴你有什麼，但不會告訴你意義(沒有\u0026quot;解釋\u0026quot;)\n「Shot Change Detection」 \u003e 例如：如果你的影片從一個風景突然特寫到一個人，他會告訴你變化的時間點。 part 2 : demo Video intelligence API的功能 Video intelligence API一樣有提供網頁版，在開始串Video intelligence API前，\n我們可以先到這網站測試自己影片的結果：https://cloud.google.com/video-intelligence/\n(這邊也是先使用影片中的範例，有興趣的朋友可以自己嘗試。)\nexample 1 : 一部影片的分析\n我們先示範上傳一個影片，上傳後我們可以看到google已經為這個影片做了許多標籤：\n我們觀察結果，我們可以發現紅框處顯示了這影片中的label，\n也就是Video intelligence API在這影片中找到的東西。\n右側綠框處為這個label對應影片中的位置(秒數)。\n以圖片中的上方影片正好有一隻狗出現，往下方找第一行DOG的label，\n對應的紅色分布正是Video intelligence API偵測到有出現狗的片段。\n以人工看影片的方式找出狗不是做不到，\n但是會非常耗時且沒辦法大量快速的處理內容。\n而且我們要完全確定答案的話，我們必須將整個影片都看過才行。\n更何況如果我們不是要找狗而已，我們要找整個影片有哪些內容並記錄下來的話。\n那人工處理這件事情的速度會非常慢。\n我們可以用單一個REST API取得一個JSON格式的回覆結果，\n這個畫面也是將JSON格式視覺化呈現，讓我們知道會拿到什麼東西。\nexample 2 : 多部影片的分析\n這邊要提到的是對多部影片的分析，適用的情境例如：\n我們是一家體育新聞公司，我們想要找尋棒球賽事的精采片段。\n手動看手邊的每一部影片是一件耗時的工作，\n我們如果使用Video API就可以很輕鬆地得到JSON結果。\n我們從上方紅框處搜尋baseball，我們就可以找到baseball的影片。\n而下方會自動過濾出含有baseball的影片內容，以及baseball有出現的秒數。\n而且我們只要對紅點處點擊一下，我們立刻可以看到那一段影片內容。\n我們可以看見使用了Video Intelligence API，\n只需要幾分鐘就能幫我們處理了人工觀看影片可能需要的數小時的工作時間。\n3. Cloud Speech API 課程地圖\nPython notebooks in the cloud Pre-trained ML APIs Cloud Speech API Cloud Speech API是google的語音辨識的API，\n他可以幫助我們從語音轉換成\u0026quot;超過100種語言\u0026quot;的文字稿。\npart 1 : Cloud Speech API裡面有哪些功能 Speech API 功能名稱 用途 「Speech to text transcription」 只要丟語音給Cloud Speech，就可以回傳語音的文字稿。 「Speech timestamps」 可以讓你的文字稿具有能回查演講時間的功能(點文字就能跳到該語音段落)。 「Profanity filtering」 過濾不適當的語音內容。 「Batch \u0026 streaming transcription」 允許傳送一個完整的語音文件，或者也可以發送連續的語音串流。最後我們將會回傳一個完整的對應文字稿。 ※補充：\n「Speech timestamps」 也就是說，我們可以知道每一個字在speech中的開始與結束時間。\npart 2 : demo Cloud Speech API的功能 Cloud Speech API一樣有提供網頁版，\n我們可以先到這網站測試自己語音的分析結果：https://cloud.google.com/speech\n(這邊也是先使用影片中的範例，有興趣的朋友可以自己嘗試。)\n而這裡我們著重在展示「Speech timestamps」的功能，我們先稍微解釋一下其運作的原理：\nstep1 : 從一個影片中提取出語音。\nstep2 : 將語音送進 Cloud Speech API 並得到文字稿與時間標記。\nstep3 : 我們將結果視覺化，在我們視覺化的UI中，我們可以直接搜尋文字稿對應影片的段落。\nexample 1 : 一部影片的分析\n這個就是網頁顯示的結果，下方文字稿就是從上面影片內容擷取出的。\n我們可以在下方任意點擊一段文字，就會直接跳到影片的該段落(如圖所示)。\nexample 2 : 多部影片的分析\n再來我們想要實現的功能是，在大量的演說影片中找到我們要的關鍵字。\n我們回到主目錄，我們以搜尋Firebase作為示範，\n我們可以先在上方藍框處搜尋，他會幫我們從我們所有的影片中找到符合我們的結果，\n然後只要點擊對應的藍點處(如圖中的紅框處)，就可以直接跳到該段影片有提到Firebase的內容。\npart 3 : Cloud Translation裡面有哪些功能與demo 當我們從語音得到文字之後，我們可能會想開始嘗試分析。\n有一件可能必須做的事情就是我們得先試著翻譯他，Cloud Translation可以快速地幫助您完成。\nTranslation API 功能名稱 用途 「Translate text」 可以翻譯100多種不同的語言 「Detect language」 可以偵測100多種不同的語言 ※補充：\n「Detect language」 也就是當你看不懂是哪個國家的語言的時候，他可以幫助你找到是哪個國家的。\nCloud Translation 最有名的應用就是 Google Translate，相信大家都不陌生，\nCloud Translation API能翻譯100多種不同的語言，\n也就是說，如果我們現在想要提供多國語言服務，\n自動偵測語言與翻譯語言能幫助你輕鬆地找到這是什麼國家的語言，並翻譯內容。\nCloud Translation API一樣有提供網頁版，\n我們可以到這網站測試：https://cloud.google.com/translation\n4. Translation and NL 課程地圖\nPython notebooks in the cloud Pre-trained ML APIs Translation and NL (Translation的課程內容應該在上一節，但這裡是依照google課程的名稱命名的，可能命名有誤。)\nCloud Natural Language是google提供的自然語言辨識API，\n我們一樣可以利用單一的REST API request得到一段文字內容的理解。\npart 1 : Cloud Natural Language API裡面有哪些功能 Natural Language API 功能名稱 用途 「Extract entities」 可以從文章內容中提取entities(實體) 「Detect sentiment」 偵測這篇文章內容中或是句子的情緒，會告訴你情緒是正面還是負面。 「Analyze syntax」 可以分析語法，了解更多文章的語言細節，或是提取文章中的部分內容。 「Classify content」 可以將文章內容分類到不同類別。 part 2 : 「Analyze syntax」的demo與更詳細介紹 以下，我們將先針對「Analyze syntax」做更多的介紹，\n我們使用一個例句 \u0026ldquo;The natural language API helps us understand text.\u0026rdquo;\n我們做了一個視覺化的圖片來使我們能更了解透過API回覆的JSON內容，\n「Dependency parse tree(紅色部分)」，這個的功能是將所有有關係的詞彙做相互關聯， 像是那些單詞可能與其他的單詞有關係。 「Parse label(綠色部分)」，他會告訴我們每個詞在句子中的作用 例如，在這句話裡面 helps 是 root verb(主要的動詞)，API是nominal subject(主要的主詞)\n- 「Part of speech(黃色部分)」，他會告訴我們每個詞在句子中的詞性(形容詞、名詞、動詞...) 「Lemma(黑色部分)」，代表這個詞的canonical form(標準形式) 例如，在這句話裡面 helps 的 canonical form(lemma) 是 help\n這個對於計算特定單詞用於描述某些內容，在文章中所出現的次數非常有用\n你可能不希望在分析的過程中，helps 與 help 被當成兩個詞來分別計算，\n這時Lemma就可以幫助你完成這件事情。\n「Morphology (灰色部分)」，可以分析這個詞的Morphology(詞法) 例如：名詞有可數、不可數的特性\u0026hellip;，動詞有是不是第三人稱單數之類的特性\u0026hellip;\n這個結果也會根據語言而有所不同。\npart 3 : 「Content classification」的demo與更詳細介紹 以下，我們將針對「Content classification」做更多的介紹，\n我們拿一篇新聞文章來做分析，我們將這篇新聞的標題與第一句話丟進Natural Language API進行分類。\n我們可以看到他返回的類別是baseball(上方紅框處)，\n我們再注意另外一件事，文章裡面並沒有提到過baseball這個詞，\n但google告訴我們他有99%的肯定(下方紅框處)這篇文與baseball有關。\n這個API的方法總共提供了700多種可用的類別可以使用。\npart 4 : case: 使用Cloud Natural Language API的企業例子 這邊我們會提到一間公司使用各種不同的Natural Language API方法在他產品中的例子。\nWootric：一個客戶反饋的平台，他透過圖中紅框處幫助客戶收集使用者的回饋，\n他們在自己應用中的各處放置這個方框，\n並且要求用戶提供一個分數代表著它們的使用體驗與覺得這個應用最特別的地方。\n0-10的分數很容易理解，但是如果是開放式的回饋(自己填文字的)就比較難快速理解(需要時間消化內容)。\n而這個就正是他們使用natural language API的地方，\n它們使用「sentiment analysis」來分析這個人的文字回覆情緒是否與他們提供的數字分數吻合，\n然後他們使用「entities」與「syntax analysis」取解析出最關鍵的內容(entities)，\n並直接反饋給適當的對象，就可以做即時的處理，而不用派一個人手動的逐條閱讀回饋內容才反饋。\n例如：他們可能遇到對可用性感到憤怒的使用者，他們可以及時反應給適當的人，請他做即時的處理。\n而如果沒有這樣做，我們會需要一個人手動的閱讀每一條回饋內容，並一個個反饋給適當的對象。\npart 5 : demo Cloud Natural Language API的功能 Cloud Natural Language API一樣有提供網頁版，\n我們可以先到這網站測試自己文字分析結果：https://cloud.google.com/natural-language/\n紅框處可以打入自己想要的句子，\n我們以影片中句子作為例子：\u0026ldquo;I liked the sushi but the service was bad.\u0026rdquo;\n這是從某個餐廳的評論找下來的，假設我們是經營餐廳的人，每天我們可能會收集很多評論，\n我們不會想一句句的分析每個評論，我們可能只想找最正面與最負面的評論。\n我們來看看Cloud Natural Language API分析的結果：\n「Entities」：從這個句子的結果中，我們看到他從我們的句子中分析出了兩個entities(實體)，service與sushi，並且還能夠從這兩個實體中分別分析情緒。 我們除了可以理解整個句子的情感之外，還能分別針對兩個實體(service與sushi)查看情感。 natural language API 返回的情緒值介於-1到1，表示情緒的負面與正面。\n我們可以看到service的得分為-0.9，幾乎完全是負的，表示顧客認為服務很差。\n我們再來看sushi的得分為0.9，幾乎完全是正的，表示顧客非常滿意壽司。\n這樣的個別實體分析是非常有價值的，整句話的情緒分析做不到這樣的細節分析。\n「Sentiment」：我們再來看到整句話的情感分析，也能同樣看到上方的單個實體分析結果。 不過這個例子特別的地方在於，我們針對單個實體分析情緒，\n比起分析整句話的情緒，能得到更多有用的資訊。\n(sushi好而service不好，整句話只能知道是不好的。)\n「Syntax」：可以幫我們分析這句子的結構，因為上面已經有詳細demo過了，這邊只展示結果。\n「Categories」：可以看到這句話是屬於哪一種分類。影片中沒有展示。\n但我有自己試了，原來是找不到結果XD (是因為這樣才沒放在影片中嗎XD)\n回傳的結果會是 \u0026ldquo;No categories found. Try a longer text input.\u0026rdquo;\n照這句話的意思看來，可能是資訊量不足，還沒辦法做分類，\n上方的例子畢竟是用一篇新聞的句子才能找到結果，也許是因為這句話能提供的資訊還太少吧。\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 12】 Google ML - Lesson 9 - google圖片辨識(Vision), 影片辨識(Video), 語音辨識, 語言翻譯, 自然語言辨識(NL) API功能總整理\n","date":"2019-09-13T00:26:36+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424kTL0ZzyBiJ.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/google-apis/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】12 - Lesson 9 - google圖片辨識(Vision), 影片辨識(Video), 語音辨識, 語言翻譯, 自然語言辨識(NL) API功能總整理"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 我們繼續 How Google does Machine Learning 的第五章節~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】 Chapter 2(下) - ML要成功的秘訣與策略 【Day 6】 Chapter 3(上) - ML會失敗的最常見十大陷阱 【Day 7】 Chapter 3(下) - ML在企業運行的五大階段與注意事項 【Day 8】 Chapter 5(上) - 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境 【Day 9】 Lab 0 - 每次在google雲端上要開始lab前都要的事前準備與注意事項 【Day 10】 Lab 1 - 在google雲端上分析地震資料與製圖，並儲存在雲端 Course - How Google does Machine Learning 第五章節的課程地圖： 「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud Module Introduction Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process Demo of rehosting Cloud Datalab Working with managed services Computation and storage Lab: Rent-a-VM Intro to Qwiklabs Intro to Renting-VM Lab Lab: Rent-a-VM to process earthquake data Lab debrief \u0026lt;\u0026ndash; (昨天到這邊) 「Cloud Shell」 「Third wave of cloud」 「Third Wave of Cloud: Fully-Managed Services」 「Third Wave of Cloud: Serverless Data Analysis」 「Third Wave of Cloud: BigQuery and Cloud Datalab」 Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery 「Machine Learning with Sara Robinson」 「ML, not rules」 Pre-trained ML APIs Vision API in action Video intelligence API Cloud Speech API Translation and NL Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution 1. Cloud Shell 課程地圖\nPython notebooks in the cloud Cloud Shell 我們之前所做的事情是建立一個 Compute Engine VM，他幫我們實現了一些功能。\n但其實以這樣的需求來說，這樣做是有點浪費的。\n現在我們將來介紹 Google Cloud Shell，\n想要執行 Cloud Shell 只需要點選 GCP console 右上角的「Activate Google Cloud Shell」，即可開始執行。\n我們所開啟的視窗，稱為 Cloud Shell，他是一種小型的VM，\n雖然小，但當我們的目的只是嘗試些小事情(跑幾行code、複製東西\u0026hellip;)，Cloud Shell絕對夠用。\n我們看到Cloud Shell的視窗，基本上我們可以做與Compute Engine VM幾乎相同的事情，\n此外，Cloud Shell更適合給developers使用，\n在Compute Engine VM中，我們可能還需要安裝一些軟體(因為是全新的環境)，\n但這些在Cloud Shell幾乎都不用，已經都裝好了。\n這邊以「git」,「git clone」作為示範，可以看到我們能直接使用。\n但要注意的事情就是 Cloud Shell 是一個很短暫的 VM，\n也就是說大概一陣子(可能大約一小時)沒使用，這VM的內容會被自動回收。\n但我們也能隨時重新在啟動新的 Cloud Shell，\n或者我們也可以按新的tab啟動另外一個 Cloud Shell (下圖\u0026quot;+\u0026ldquo;號)，這仍是相同的VM。\n當有兩個tab的時候，你可以執行其他指令在另外一個tab，\n讓每個 Cloud Shell 所做的事情更清楚。\n注意：仍是在同個VM下執行指令。\n2. Third Wave of Cloud: Fully-Managed Services 課程地圖\nPython notebooks in the cloud Third wave of cloud Third Wave of Cloud: Fully-Managed Services 這邊講一點歷史，三波google雲的變化：\n原先的 Data lab 是由本機電腦拓展到雲端處理服務，\ngoogle從MapReduce，後來意識到困難(也可能是因應時代變化)。 改為開發了Dremel和Colossus，Dremel是做SQL查詢用的，\nColossus是一個允許大量資料讀取的文件系統，\nDremel和Colossus作為BigQuery和Google Cloud Storage，\n在Google Cloud Platform上提供，這就是指google的第二波雲。 當然，這樣的事情不只google在做，所以就會有第三波雲。\n最大的差別就是在，第二波雲主要在做的是租用基礎設施(之前lab在做的事情)。\n第三波雲重點放在完全彈性的服務。\n如果要處理比較新的項目，請使用第三波雲的Managed Services，\n這個服務能提供open source軟體的標準使用方法，而且也不須擔心基礎結構。 3. Third Wave of Cloud: Serverless Data Analysis 課程地圖\nPython notebooks in the cloud Third wave of cloud Third Wave of Cloud: Serverless Data Analysis 而上面這些內容實際上對應到的是什麼，這邊我們提供範例，\n我們開啟BigQuery console，BigQuery是一個存放data的倉庫。\n我們現在想查詢醫療保險索賠的public dataset，我想要查2014年的Medicare索賠。\n而我們想做一個ad hoc(臨時的)query查詢，\n所以我們也沒有創建indices，也沒有準備database使這查詢更有效率。\n我們只花了3秒左右查詢了276MB的資料，BigQuery是一個columnar database，\n我們可以注意一下結果，它們在Medicare的公共數據中。\n這個數據大約有2400萬行，因此我們能在幾秒鐘對一個大約2400萬行的數據集進行查詢，\n而且在不建立任何VM、不安裝任何軟體下完成這樣的查詢操作。\n當我們說第三波雲時，就是這件事，我們想做到完全的managed services，\n到時我們只需要寫一些code，並交由他執行即可。\n4. Third Wave of Cloud: BigQuery and Cloud Datalab 課程地圖\nPython notebooks in the cloud Third wave of cloud Third Wave of Cloud: BigQuery and Cloud Datalab 在上面的小實驗中，我們看到了BigQuery的一些優點\n能快速地進行大量資料的查詢。 - 我們能使用SQL2011做一個ad-hoc(臨時的)查詢。 而想將資料傳入BigQuery也非常簡單，直接上傳檔案， 或透過web GUI存到雲端中，再將它們的資料流向BigQuery。 另外匯出BigQuery的資料也非常簡單，google有提供非常多的APIs， 運行SQL query並保存成任何你想要的格式結果。 - BigQuery資料儲存不貴。 最重要的目的是，因為有了BigQuery， Datalab可以與BigQuery有良好的整合， 所以我們可以探索數據、運行query， 並將結果以Pandas DataFrame的方式匯出，還能用Python來製圖。 5. ML, not rules 課程地圖\nPython notebooks in the cloud Machine Learning with Sara Robinson ML, not rules 這邊我們要稍微討論一下機器學習比起一般方法更強大的地方，\n但要比較，我們就必須要先思考，「如果沒有機器學習，我們要怎麼解一樣的問題?」\n問題1：請說明一個方法(rule)，能分辨出下面的蘋果與橘子。 你可能會想說，我們只要分辨顏色就好了。 這是正確的方向。\n如果我們的程式分辨出大多像素是紅色，就輸出apple；大多像素是橘色，就輸出orange。\n但一樣的問題，如果我們今天拿到的是灰階的圖片呢?\n問題2：請說明一個方法(rule)，能分辨出下面的蘋果與橘子。 這時候我們可能就需要開始尋找表面的紋理或梗的形狀之類的。\n但你會發現，我們會因此而要開始改寫我們的演算法，才能夠達到這題目正確的分類結果。\n但正當你寫完的時候，如果我們突然想要分辨出三種水果呢?\n你到那時才會知道，你整個的算法又會需要再重寫了，\n但這些圖片是如此的相似、都是接近一個橢圓形的立體、都是水果，\n如果我們的任務改成相差很多的，或許就會變簡單了吧?\n這裡就有一個例子，我們來分辨狗與拖把的圖片：\n狗有生命且會呼吸，掃把沒眼睛、鼻子、嘴巴，\n正當你想這個問題看來能夠輕易地被解決的時候，\n突然有人請你分辨sheep dogs(羊狗)與拖把的差別時，你又崩潰了，\n況且這是一個連人由自己的眼睛分辨都很難的題目，\n我們就不要再寫code(寫rules)解決這樣的問題了吧，\n我們沒有必要特別針對特定的圖片多寫幾行code，只為了能多識別出那個圖像，\n而且就算寫出來了，還有可能在別的情況下，這份code又沒有用了。\n所以我們應該要改寫能自動幫助我們找到這些規則的code，\n這時候我們就需要依靠ML。\nGoogle Cloud Platform提供了兩種簡單的方式讓ML能加在我們自己的project中，\n而且我們可以自己定義ML的功能，如圖的左邊，\n「Tensorflow」: Google Brain團隊提供的open source library， 能讓我們使用自己的數據建構並訓練自己的ML模型。 「Cloud Machine Learning Engine」: Tensorflow模型除了在自己的電腦跑之外， 也能直接在google提供的架構下直接執行。 圖的右側指的是一些google已經訓練好的ML所提供的API，\ngoogle又稱之為\u0026quot;友善的機器學習\u0026rdquo;，可以透過REST API取得資料，\n下個章節中，我們會把所有的API的應用做介紹並且demo每一個功能。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 11】 Google ML - Lesson 8 - Cloud Shell 的介紹與 google雲的三代變化, 使用ML與一般演算法的比較與優勢\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-12T00:23:19+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424fTcV9apSY0.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/cloud-shell/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】11 - Lesson 8 - Cloud Shell 的介紹與 google雲的三代變化, 使用ML與一般演算法的比較與優勢"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天的內容一開始的幾個part會跟昨天重複(步驟上的相同)，\n原因是今天整理完之後，覺得把昨天的步驟整理到今天放在一起會比較像一個完整的lab。\n而昨天的lab內容我也會把這次lab 1的內容拿掉，\n剩下的內容改成lab 0，代表運行lab前的準備，\n這樣也有個好處，之後每個的lab開始前，\n都會有跟lab 0差不多的步驟，這樣之後也就不用再贅述這部分了。\n所以在今天的lab開始前，還是一樣需要先做lab 0的部分才能開始哦~\n(雖然下面步驟也會貼心的再提醒就是了XD)\n我們就繼續 How Google does Machine Learning 的第五章節~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】 Chapter 2(下) - ML要成功的秘訣與策略 【Day 6】 Chapter 3(上) - ML會失敗的最常見十大陷阱 【Day 7】 Chapter 3(下) - ML在企業運行的五大階段與注意事項 【Day 8】 Chapter 5(上) - 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境 【Day 9】 Lab 0 - 每次在google雲端上開始lab前都要的事前準備與注意事項 Course - How Google does Machine Learning 第五章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud Module Introduction Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process Demo of rehosting Cloud Datalab Working with managed services Computation and storage 「Lab: Rent-a-VM」 Intro to Qwiklabs \u0026lt;\u0026ndash; (昨天到這邊) 「Intro to Renting-VM Lab」 「Lab: Rent-a-VM to process earthquake data」 「Lab debrief」 Cloud Shell Third wave of cloud Third Wave of Cloud: Fully-Managed Services Third Wave of Cloud: Serverless Data Analysis Third Wave of Cloud: BigQuery and Cloud Datalab Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery Machine Learning with Sara Robinson ML, not rules Pre-trained ML APIs Vision API in action Video intelligence API Cloud Speech API Translation and NL Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution Lab: Rent-a-VM to process earthquake data 課程地圖\nPython notebooks in the cloud Lab: Rent-a-VM Lab: Rent-a-VM to process earthquake data Lab debrief 在這裡，我們將建立一個 Compute Engine，並使用一些位於Cloud Storage的data，\n並完成一個簡單的任務：使我們的網頁顯示現在的地震資料圖，並把這些資料儲存下來。\npart 0 : (事前準備) 開啟 GCP console 請先參考 【Day 9】- 每次在google雲端上開始lab前都要的事前準備與注意事項 的內容，完成到運行中階段。\npart 1 : (準備機器) 在 GCP console 中建立一台VM，並執行 SSH Step 1 : 在GCP console中，我們找到 MENU -\u0026gt; Compute Engine -\u0026gt; VM instances\nStep 2 : 按下Create新的VM後，\n設定名字 設定zone(可選擇靠近自己的地區的，或任意也沒關係，影片中示範：US Central 1-b) 設定CPU(影片中示範：1 vCPU，已經很足夠使用) 下方選擇\u0026quot;Allow full access to all the Cloud APIs\u0026quot; 按下最下方的Create Step 3 : 稍微等一下VM被建立，按下SSH，(這邊可能需要允許pop-ups)\nStep 4 : 我們可以看到SSH的畫面顯示如下。\npart 2 : (準備資料) 在 SSH 中 下載必要資料 Step 1 : 在SSH中，我們會需要使用git，如果還沒有安裝，\n我們輸入以下指令即可安裝git，\nsudo apt-get install git 如果是安裝完成的，我們直接輸入「git」，會看到他的使用說明(如何下參數)。\nStep 2 : 再來我們要使用git來下載我們想要的資料，\n我們輸入\ngit clone https://github.com/GoogleCloudPlatform/training-data-analyst 下載完我們輸入ls顯示當前資料夾，\n如果有看到顯示「training-data-analyst」，那表示下載成功了。\n我們接下來要來取用來自USGS的地震資料，\n而這段script，google在剛剛clone的資料夾內已經幫我們寫好了。\nStep 3 : 首先我們需要先切換資料夾：\ncd training-data-analyst/courses/machine_learning/deepdive/01_googleml/earthquakes 小技巧：使用「tab」，可以自動完成，不用完整的輸入資料夾名稱(如果資料夾存在)。\nStep 4 : 接下來我們輸入這段程式碼，less可以幫助我們預覽這份script檔案。\nless ingest.sh 使用方式為：\n「space」:往下捲動\nb:備份這個page\nq:離開(quit)\n讓我們稍微解析一下這份scripts：\nrm行：強制移除現有的\u0026quot;earthquakes.csv\u0026quot;檔案(如果存在的話)\n「wget」行：從USGS下載最近的資料，並命名為\u0026quot;earthquakes.csv\u0026quot;\nStep 5 : 執行這份script檔案，將我們要的檔案下載下來。\nbash ingest.sh Step 6 : 我們可以用「head」偷看一下這個下載檔案的前幾行。\nhead earthquakes.csv 我們可以看到有顯示時間、經緯度，與所有這幾週發生的地震資料。\npart 3 : (跑程式) 轉換資料部分 Step 0 : 我們可以先在以下網址看結果與code的解釋：\n基本上是取得地震資料，然後使用matplotlib製圖而成。\nhttps://github.com/GoogleCloudPlatform/datalab-samples/blob/master/basemap/earthquakes.ipynb\nStep 1 : 換我們自己動動手，首先先把我們所缺的python package都安裝起來：\n「basemap」：可以畫製地理位置地圖\n「numpy」：數字處理library\n「matplotlib」：基礎圖形繪製library\nbash install_missing.sh Step 2 : 執行transform.py，這份code會執行與剛剛Step 0相同的內容：\npython transform.py Step 3 : 執行「ls -lrt」，這會照時間順序顯示我們最近所建立的新東西：\nls -lrt Step 4 : 我們會發現有個「earthquakes.png」被建立，到這邊這階段就完成了。\npart 4 : (儲存結果) 建立bucket(雲端儲存資料的地方)，並儲存資料。 現在我們想將這個生成的結果存在GCP的雲端上。\nStep 1 : 我們先按一下Menu，選擇storage，並按下Create bucket。\nStep 2 : 這是關於bucket的設定介面，我們一共需要設定的東西如下，\n準備好就可以按Create了!\n`名字`：注意這名字一定要**全球唯一**!!! 我們可以直接使用之前的part 0階段產生的 Projcet ID，我們能確保這是全球唯一的，\n除非真的是極度的不幸，不然這名字基本上不太可能被其他人使用過XD\n「storage class」：可選multi-regional或regional 看想不想要跨地區儲存(全球化的應用會需要)，這邊我們先選regional\n「location」：選地區 基本上建議跟當初part 1所建立的 Compute Engine(VM) 在同一個位置，會比較快(減少資料取得時間)。\n所以這邊我們一樣選 us-central1\nStep 3 : 現在我們該來把我們結果的圖片存進去剛剛建的bucket了，\n我們回到我們的 SSH 中，我們直接使用GCP的「gsutil」工具來儲存資料。\ngsutil cp earthquakes.* gs://\u0026lt;這邊填上你bucket的名字\u0026gt;/earthquakes/ 稍微解析一下這段：\n「gsutil」：GCP提供的指令工具\ncp：copy\n「earthquakes.」：任何符合「earthquakes.」格式的資料，*代表任意值\n「gs://\u0026lt;這邊填上你bucket的名字\u0026gt;/earthquakes/」：目標路徑\npart 5 : (顯示結果) 找到剛剛儲存的資料，並顯示結果。 Step 1 : 我們回到我們剛剛的bucket，重新整理後，應該會看到檔案已經被儲存了。\n會看到三個檔案「earthquakes.htm」, 「earthquakes.png」, 「earthquakes.cs」。\n這表示這三個檔案已經透過剛剛的指令被複製進我們的bucket了。\nStep 2 : 再來我們要將我們在雲端所儲存的東西，製作成一個連結分享出去。\n先在左方紅框處的地方打勾，然後再打勾右方紅框處(才會看到顯示Public link)。\n※新版的介面UI改了，我們改在 SSH 中執行以下指令，以達到一樣的效果：\ngsutil acl ch -u AllUsers:R gs://\u0026lt;這邊填上你bucket的名字\u0026gt;/earthquakes/* Step 3 : 點一下Public link，就會顯示剛剛結果圖了，我們也可將這結果分享給別人(公開的情況下)。\n可以稍微注意網址：「storage.googleapis.com/\u0026lt;我們bucket的名稱\u0026gt;/earthquake/earthquake.htm」\n這就像是一般我們常見的資料夾結構\n到這邊我們就算完成我們這次的lab了，\n這次的lab我們主要學習的目標有：\nCompute Engine (VM) 的租用，並在上面運作程式碼 使用 Cloud Storage 儲存資料 不過在之後的lab中，就不會再使用這個VM了。\n這個lab主要的目的是在建立所有GCP上內容的基礎以及知識。\n之後自己想做 Side Project 也必須用到這些觀念。\n在之後大部分的課程中，我們將改使用 Managed Services。\nManaged Services 允許我們直接在上面跑程式，而不需要再自己配置VM、安裝軟體了。\nManaged Services 是一種更高階的方式運用 Cloud Services。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 10】 Google ML - Lab 1 - Rent-a-VM to process earthquake data - 在google雲端上分析地震資料與製圖，並儲存在雲端\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-11T02:50:07+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424ifjuteUV4H.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/rent-a-vm/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】10 - Lab 1 - Rent-a-VM to process earthquake data - 在google雲端上分析地震資料與製圖，並儲存在雲端"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天我們繼續 How Google does Machine Learning 的第五章節~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】 Chapter 2(下) - ML要成功的秘訣與策略 【Day 6】 Chapter 3(上) - ML會失敗的最常見十大陷阱 【Day 7】 Chapter 3(下) - ML在企業運行的五大階段與注意事項 【Day 8】 Chapter 5(上) - 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境 Course - How Google does Machine Learning 第五章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud Module Introduction Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process Demo of rehosting Cloud Datalab Working with managed services Computation and storage \u0026lt;\u0026ndash; (昨天到這邊) 「Lab: Rent-a-VM」 「Intro to Qwiklabs」 Intro to Renting-VM Lab Lab: Rent-a-VM to process earthquake data Lab debrief Cloud Shell Third wave of cloud Third Wave of Cloud: Fully-Managed Services Third Wave of Cloud: Serverless Data Analysis Third Wave of Cloud: BigQuery and Cloud Datalab Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery Machine Learning with Sara Robinson ML, not rules Pre-trained ML APIs Vision API in action Video intelligence API Cloud Speech API Translation and NL Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution Lab: Intro to Qwiklabs 課程地圖\nPython notebooks in the cloud Lab: Rent-a-VM Intro to Qwiklabs Intro to Renting-VM Lab 這邊會進入我們第一個lab，我們可以從Coursera直接執行我們的應用，\n我們可以參考下圖的使用說明：\nStep1: 在Coursera點選一個Lab Step2: 按下藍色的Open Tool(藍色按鈕)，進入Qwiklabs Step3: 在Qwiklabs按下START LAB(綠色按鈕)，開始一個lab Step4: 在開始lab的同時，會有一些credentials被同時生成(對應各個帳號) Step5: 在Qwiklabs可以點選Open Google Console(黃色按鈕) Step6: 他會要求你登入Google Home platform，這個帳號密碼在Step4找得到(並非個人google帳號) 再來我們來實際執行一次吧！(這裡的截圖取用影片中的範例，帳號密碼也只是影片中的範例)\npart 1 : 開始執行階段 Step 1: 按下藍色的Open Tool按鈕\nStep 2: 可以在右邊閱讀這個lab的詳細內容與注意事項，\n按下綠色的START LAB(綠色按鈕)就會進入Qwiklabs，\n這個視窗在開始Qwiklabs後可以不用關閉，繼續查詢lab的內容。\nStep 3: 開始lab之後，右上角時間會開始倒數，\n不過這個不用擔心，時間基本上一定夠做得完lab。\n(如果真的做不完，還可以再重啟lab，使他重新倒數計時。)\n我們也可以看到左側產生的各種帳號、密碼與 Project ID，作為之後的步驟使用\n(這裡的帳號、密碼與 Project ID都是臨時且唯一的，在lab結束後就會消失。)\n下一步我們就按上方的 Open Google Console。\nStep 4: 這裡會要求登入google，請務必輸入「Step3中的「帳號密碼」」，而不是自己的帳號。\n並同意使用條款，手機認證不用管沒關係。\nStep 5: 這邊也要同意GCP的使用條款。\nStep 6: 看到這個畫面，基本上準備程序已經完成了。\n注意事項 1：注意上方的project名稱是否有對應Step3中的lab名稱\n(不過這邊親測基本上除非名字差太多，不然基本上應該是都沒問題的。)\n如果真的不同的話，按上方的project名稱，點選正確的名稱打勾即可。\npart 2 : 運行中階段 左上角有Menu可以找到全部的功能 右上角(紅框處)有可以開始執行 Google Cloud Shell 的功能 (terminal for Google Cloud Platform) part 3 : 結束運行階段 (重要) 結束運行的階段非常重要，不可以直接關閉(可能會造成其他lab有使用上的問題)\nStep 1 : 我們回到這個畫面按下紅色的END LAB按鈕，並確認關閉。\n沒有正常關閉可能會造成下次執行同lab出問題，或是執行其他lab出問題。 「另外一個重要的注意事項」：只要關閉lab，裡面的 datasets 或 working 皆會全部清除。\n記得要先做好儲存的動作，不論是儲存在 GCP的帳號中(不等於lab的這個帳號) 或 自己的電腦裡皆可。 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 9】 Google ML - Lab 0 - Intro to Qwiklabs - 每次在google雲端上開始lab前都要的事前準備與注意事項\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-10T02:48:08+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424GNGO6OY2R3.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/intro-to-qwiklabs/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】9 - Lab 0 - Intro to Qwiklabs - 每次在google雲端上開始lab前都要的事前準備與注意事項"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天我們先進入 How Google does Machine Learning 的第五章節，之後再回到第四章~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】 Chapter 2(下) - ML要成功的秘訣與策略 【Day 6】 Chapter 3(上) - ML會失敗的最常見十大陷阱 【Day 7】 Chapter 3(下) - ML在企業運行的五大階段與注意事項 Course - How Google does Machine Learning 第五章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nPython notebooks in the cloud 「Module Introduction」 「Cloud Datalab」 「Cloud Datalab」 「Demo: Cloud Datalab」 「Development process」 「Demo of rehosting Cloud Datalab」 「Working with managed services」 「Computation and storage」 Lab: Rent-a-VM Intro to Qwiklabs Intro to Renting-VM Lab Lab: Rent-a-VM to process earthquake data Lab debrief Cloud Shell Third wave of cloud Third Wave of Cloud: Fully-Managed Services Third Wave of Cloud: Serverless Data Analysis Third Wave of Cloud: BigQuery and Cloud Datalab Datalab and BigQuery Lab Intro: Analyzing data using Datalab and BigQuery Lab: Analyzing data using Datalab and BigQuery Lab Debrief: Analyzing Data using Datalab and BigQuery Machine Learning with Sara Robinson ML, not rules Pre-trained ML APIs Vision API in action Video intelligence API Cloud Speech API Translation and NL Lab: Machine Learning APIs Lab: Pretrained ML APIs Intro Lab: Invoking Machine Learning APIs Lab Solution 1. Module Introduction 課程地圖\nPython notebooks in the cloud Module Introduction 這章基本的用文字介紹了一下雲端的環境，\n我們會用python notebook做機器學習的開發，\n而notebook的服務在google雲端上。\nCloud Datalab基本上是本章節使用的開發環境，\n而此服務在VM上運行，包含python notebook，\n我們同時也會教學Compute Engine的原理與Cloud Storage的知識。\n既然是VM就必須提調VM的兩個特性：\n可以實際控制與更改notebook的機器內容，且無須重寫 \u003e 例如：添加GPU或增加更多memory \u003e VM是短暫的，如果想保存內容須保存在VM外 \u003e 而這點最方便的方法就是使用Cloud Storage。 \u003e 我們將notebook的內容保存在Cloud Storage中， \u003e 同時還能達到版本控制的效果。 \u003e 而這個章節的學習目標，我們可以參考如下：\nModule Learning Objectives(這個module的學習目標)\nCarry out data science tasks in notebooks 練習使用notebooks完成資料科學的任務\nRehost notebooks on the cloud\n在雲上使用notebooks，並更改硬體規格 Execute ad-hoc queries at scale\n我們將將Cloud Datalab與BigQuery一起使用，BigQuery是雲上的數據分析服務，可以讓超越傳統數據庫的執行速度與規模進行queries的查詢 Invoke pre-trained ML models from Datalab\n能使用一些pre-trained ML models(前面的章節有介紹到)，並在Cloud Datalab中運行。 2. Cloud Datalab 課程地圖\nPython notebooks in the cloud Cloud Datalab Cloud Datalab Demo: Cloud Datalab Development process 這章稍微介紹了一下Datalab的環境，以jupyter作為基礎的open source平台。\n下面為Datalab的介面展示：\n可以看到主要有三個部分：code, output, markup sections彼此交錯\n也因為這樣的交錯才能使notebook如此實用，\ncode:\n在此打你的python code\n執行code的方式: 按 「shift+enter」 或 按上方的 「Run botton」\noutput:\n注意output可以為圖形，不像command line一樣。\nmarkup:\n可以寫一些markup(markdown語法)，可以解釋你這個部分在做什麼。\n最後是綠色部分:\n有可以將notebook匯出的按鈕，可以將此notebook下載下來。\n可以commit code至Google Cloud Platform至code repository的按鈕。\n「Clear all」: 清除所有output\n「Run all Cells」: 執行所有Cells\n所以整個Datalab的使用流程大概可以表示成下圖：\n另外要特別強調的是，Datalab的一大特色是可以線上協同合作\n傳統的notebook要線上合作有個基本的問題，誰當host?\n一但host只要關機，任何人都無法繼續工作。\n而Datalab解決了這個問題，而我們只要給一個URL就可以了。\n另外一種Share的方式可以利用版本控制系統，例如Git，\n而且只要我們想改動硬體的規格，任何人都可以直接改好後，\n重新啟動VM，就直接以新的硬體規格來執行程式。\n簡單來說，Datalab架於雲端的VM上，任何人只要連線即可開始工作。 而只要工作結束了，隨時也能將此VM刪除。 3. Demo of rehosting Cloud Datalab 課程地圖\nPython notebooks in the cloud Cloud Datalab Demo of rehosting Cloud Datalab 這個章節主要來介紹如何在Datalab中更換VM的硬體規格。\nStep1. 先將想更改規格的VM打勾，上方按下暫停VM\nStep2. 點選Edit，在方框處更改想要的硬體規格，記得在下方按\u0026quot;Save\u0026quot;\nStep3. 點選要重啟的VM，按上方Start，確認重新Start\n小提示：在未來的lab中，我們只需要用到\u0026quot;n1-standard-1 VM\u0026quot;(basic plain vanilla virtual machine)的硬體規格，就已經非常足夠執行我們所要的程式。\n4. Working with managed services 課程地圖\nPython notebooks in the cloud Working with managed services 這個章節會介紹透過雲端執行ML時的大致流程。\n以讀取CSV file作舉例，如果是一般的處理方式，\n首先我們將CSV file可以透過Pandas and Apache Beam進行資料前置處理，\n再丟入至Tensorflow進行訓練，透過訓練使其不斷的進步。\n如果同樣的例子透過GCP的方式處理，\n首先用Google Cloud Storage儲存資料，用Cloud Dataflow進行資料前置處理，\n再用Cloud Machine Learning進行訓練，透過Cloud ML Engine進行參數最佳化。\n使用GCP就能產生與其他GCP產品一樣的效果，\n而且數千台機器同時探索並分析數據，\n而且就算已經習慣使用如Pandas, Seaborn 或 Plotly之類的工具，\n也能夠直接在GCP使用，透過API直接串連起來。\nCompute Engine\n先去Cloud Shell並輸入\u0026quot;datalab create\u0026quot;，並設定一些參數，\n例如compute zone, machine type，這些都屬於Compute Engine。\nCompute Engine是一種向雲端租借的機器(VM)，所以也代表不用永久擁有。\n當VM消失時，在此機器上的工作也會消失，\n因此我們還會在下一個章節中介紹Cloud Storage的知識，\nCloud Storage就是透過雲端來幫助我們儲存這些資料的地方，避免資料遺失。\n5. Computation and storage 課程地圖\nPython notebooks in the cloud Computation and storage 這個章節會介紹Compute Engine的原理與Cloud Storage的知識，\n了解Compute Engine可以清楚我們是怎麼在雲端運算的，\n了解Cloud Storage則可以清楚資料是怎麼儲存的。\nCompute Engine基本上可以想像成是分散在世界各地的CPU，\n而Cloud Storage則是分散在世界各地的儲存裝置(硬碟)。\nCompute Engine\nDatalab雖然是一個Compute Engine所執行的，\n但我們可以客製化我們的Compute Engine，\n例如：幾核心電腦、有多少memory、硬碟有多少容量、作業系統\n而這些設定皆可以修改，不用太擔心一開始的設定值。\nVM上的disk速度非常快，但當VM消失時，disk也會消失。\nCloud Storage\n而Cloud Storage是持久的，也就是說放在Cloud Storage的資料會被複製存儲在多個位置。\n我們可以透過任何電腦取得這些資料，並直接讀取內容。\n而Google Center的網路以petabit bisectional bandwidth速度在通信，\n這也代表著十萬台機器可以以每秒10 gigabits的速度在相互通信。\n我們來談一下資料在雲上的形式，\n一個典型的Cloud Storage URL可能看起來像gs:acme-sales/data/sales003.csv，\n我們稱這樣的資料存儲為\u0026quot;bucket\u0026quot;，bucket的名稱是全球唯一的，\n就像是domain name或internet URL一樣，\n基本上除非真的非常不幸，否則bucket的名稱很難已經被使用。\nCompute Engine與Cloud Storage的連結方式：\ngs URL 就像是一個資料結結構一樣，一個gs URL對應一個在Cloud Storage的物件\n我們可以使用gsutil來取用他，這是一個給command line用的工具，\n我們可以透過Google Cloud SDK下載到他，\n雲端上的Compute Engine已經內建好gsutil了。\n如果是想在自己的電腦使用，可以去下載Google Cloud SDK，裡面包含gsutil的功能。\nGsutil 也使用大家所熟悉的 Unix command line的表示方式：\n像是 MB 與 RB 代表著\u0026quot;建立bucket\u0026quot;與\u0026quot;移除bucket\u0026quot; (這個真的滿有熟悉感的)\n或 CP 代表著\u0026quot;copy\u0026quot;\n如果不使用command line，也可以使用GCP console 或 programming API, REST API\n我們示範一個複製大量檔案作為例子，我們將sales*.csv複製到一個特別的Cloud Storage位置\n(註：我們前面提到Cloud Storage buckets是耐久的，也就表示這資料會被重複儲存至多個位置。)\n不過這裡要提醒的是，雖然資料被儲存在多個位置，但不代表不用在意\u0026quot;網路延遲問題\u0026quot;，\n可以的話還是盡量把資料儲存在計算的位置附近。\n另外一個情況是可能有時會有區域的伺服器故障，\n我們應該要分散apps與data至多個zones以保護這樣的情況，\n一個zone故障了，可以即時調用附近的zone來接續服務。\nzone是分散在一個regions的不同位置，被命名為「region name-a zone, letter」，\n而當我們建立一個全球可用的app，\n我們就可以考慮分散apps與data在不同的regions，來為全球的客戶提供服務。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 8】 Google ML - Lesson 7 - 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-09T02:45:51+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424sN1Zkl8d55.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/google-python-notebook/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】8 - Lesson 7 - 先來初步認識一下google雲端上執行 python notebook (Cloud Datalab) 的環境"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天來完成 How Google does Machine Learning 第三章節的最後部分~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】 Chapter 2(下) - ML要成功的秘訣與策略 【Day 6】 Chapter 3(上) - ML會失敗的最常見十大陷阱 Course - How Google does Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nHow Google does ML Introduction ML Surprise The secret sauce \u0026lt;- (昨天到這邊) 「ML and Business Processes」 「The Path to ML」 「End of phases deep dive」 Module 3 Quiz 1. ML and Business Processes 課程地圖\nHow Google does ML ML and Business Processes (from \u0026ldquo;No ML\u0026rdquo; to \u0026ldquo;ML\u0026rdquo;)在目前的組織中加入ML的方式，我們可以先原先的業務流程來看，\n我們先觀察客戶與公司之間的活動，這裡以call center作為例子：\n現在有個call center\ncall center員工接聽客戶問題，回答客戶問題 詢問顧客滿意度調查 依據調查結果，整理調整回答問題的方式與內容(訓練機構) 將調整後的內容給員工訓練 而這樣的例子我們用一個較一般化的表示方法如下圖：\n這樣就是一般的 General Feedback Loop，我們試著在其中加入ML，\n這裡我們必須先提到有五點 Path to ML 的方法\nIndividual contributor 一個人負責這件事情\nDelegation 開始多人負責這件事情，有組織化，或有組織平行的在運作相同任務\n(通常到這階段會開始制定一些rules)\nDigitalization 開始將一些\u0026quot;重複性高\u0026quot;的動作(rules)，透過電腦來自動化幫人處理\n一個容易理解的例子：ATM\nBig Data and Analytics 使用大量的data去更符合使用者的需求(insights)\nMachine Learning 再將前面的所有資料，去進化我們的電腦處理流程(使完全自動化處理)\n(自己的註：這邊的自動化，指的是連\u0026quot;符合使用者的需求\u0026ldquo;這件事也自動化了)\n例子：youtube推薦，要先讓使用者有回饋給youtube喜好，youtube才能主動推薦影片\n而回到我們該如何加入進去我們的 General Feedback Loop，\n- Individual contributor - Delegation - Digitalization 前三個步驟裡面可以影響到process本身，\n這三個步驟主要是再處理使用者面臨到我們產品的階段。\n- Big Data and Analytics - Machine Learning Big Data and Analytics主要影響到的是我們如何找尋使用者的insights，\n簡單換句話說就是知道(或更加清楚)使用者想到的是什麼，\nMachine Learning，我們原本藉由調查來知道使用者需求(上一個階段)，\n並依這件事情訓練員工，使顧客能享受到更好的服務，\n但現在我們可以用ML處理掉這階段，使得訓練員工(員工現在已經是我們的電腦)，\n自動化的讓顧客能享受到更好的服務。\n2. The Path to ML 課程地圖\nHow Google does ML The Path to ML 這個章節我們要接續講上章的Path to ML，\n但我們要來更詳細分析這幾個階段，\n如果需要定義的話可以往上一章節看。\n而在這個章節中，我們也可以拿這五個步驟來檢視一間企業目前可能碰到的問題。\nPath to ML的五大步驟如下圖：\nStep1. Individual contributor\n由個別的人去應對一件需求。\n如果skip這步驟會怎麼樣?\n- 可能很難擴展規模。 沒有一個人先嘗試過，或先提早失敗過，直接建立一個很大的團體， 如果問題發現後，很難能夠整個組織大幅度的調整。 如果這步驟太久會怎麼樣?\n一個人掌握了所有的技術，當他離開時也將所有技術帶走， 公司直接沒有了這樣的技術，嚴重可能公司因為沒有會此技術的人而停止運作。 - 很難去擴大規模去應對使用者的需求。 Step2. Delegation\n開始由個別的人擴展變多人去應對一件需求，能同時處理需求的量也提升。\n如果skip這步驟會怎麼樣?\n不知道怎麼建立process，很多rules都是再規模化的這階段被建立起來的。 如果沒有rules，也很難在下一個步驟交由電腦自動完成。 可以再這階段累積大量不同的使用者需求，藉由不同人不同個性能得到不同的回饋經驗， 例子：call center，每個人回覆的個性不同，也能幫助我們收集需求考慮得更全面。 (反過來說skip就沒有) 也是藉由大量客戶的反應回饋，造成**human in the loop**的良性循環。 (使客戶能反覆地提供我們改進的地方。) 如果這步驟太久會怎麼樣?\n付出很高的成本在服務每一個客戶 (試想一個人一直待在銀行，只為了等待客戶來處理事情的成本，與ATM比較) - 有太多的人，就會有太多種聲音，可能導致服務標準不一致。 - 甚至最後可能會讓組織發展停留在這階段。 Step3. Digitalization\n開始讓電腦來代替人力去自動化處理一些反覆的事情。\n如果skip這步驟會怎麼樣?\n- 就算是ML，也需要一些基礎的設備 沒有先做一些基本的IT project(software)， 沒辦法提早在這階段先測試一些簡單的case能否成功， 直接跳到ML階段，導致ML失敗，整個project也失敗了。 如果這步驟太久會怎麼樣?\n你的競爭者更快速的收集資料，並搶先找到新的insights， 別人的**General Feedback Loop**先被良好的建立後，你可能就會被打敗。 Step4. Big Data and Analytics\n藉由自動化的服務，收集大量使用者的數據，並找到新的使用者需求(痛點)。\n如果skip這步驟會怎麼樣?\n- 不夠乾淨與組織化的資料，你也不知道做ML訓練的最終目的是為了什麼。 沒有大量數據的證明，很難預估自己會成功。 ML是拿來**improving**成功的，也就是要先有一定的基礎才能透過ML強化， 我們先自己反覆的做這樣的分析，再交由ML替我們分析，這樣ML訓練能順水推舟。 如果這步驟太久會怎麼樣?\n基本上，在這裡待很久並不會產生太大的問題， google也很常在這個階段停留非常久、甚至數年。 但是透過ML能分析得更快，甚至讓結果更好， 停留在此有可能會限制能解決問題的複雜性。 Step5. Machine Learning\n這個步驟就是將前面的步驟(Step4. Big Data and Analytics)自動化，\n透過ML能處理的訊息量遠遠超過一個人能應付的極限，\n而且我們能用更快的速度處理前面所有的需求，\n甚至是更細緻的細節處理，帶來整體更好的效率與體驗。\n3. End of phases deep dive 課程地圖\nHow Google does ML End of phases deep dive 這章節是Path to ML的總結論，另外我們也會在此試著用另外的角度來看這五個流程。\n我們一樣先回到五個步驟，我們換個圖來表示：\n觀點1：誰是執行者?\n從第一列我們可以看出，\u0026gt; 階段從\n一個人(階段1.)-\u0026gt;\n很多人(階段2.)-\u0026gt;\n電腦處理(階段3.4.5.)」\n觀點2：我們怎麼選擇調整的參數? (我們從哪裡知道如何調整以符合使用者需求?)\n從第二列我們可以看出，階段從\n一個個的調查結果(階段1.2.3.)-\u0026gt;\n調查結果整體分析(階段4.)-\u0026gt;\n調查結果分析並找出公式(階段5.)\n觀點3：誰來幫助我們進步? (我們自己如何調整以符合使用者需求?)\n從第三列我們可以看出，階段從\n員工手冊或有人帶領新員工學習新的應對方式(階段1.2.) -\u0026gt;\n工程師找到規則，並對機器coding符合使用者需求(階段3.4.) -\u0026gt;\nmachine learning(階段5.)\n最後的提醒：\n從沒有到直接\"完全的使用ML\"來解決問題是很危險的(對企業來說) 依照上面所說的Path to ML做可以減少很多不確定性，是比較安全的做法 - Path to ML的終極目標就是將整個**General Feedback Loop**的過程自動化。 - 使用GCP平台的協助，能使這段Path to ML過程更加順利。 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 7】 Google ML - Lesson 6 - How Google does ML (下) - 讓ML在企業運行的五大階段與注意事項\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-08T02:43:55+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424E62DTsPVXV.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/ml-5steps/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】7 - Lesson 6 - How Google does ML (下) - 讓ML在企業運行的五大階段與注意事項"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天來閱讀 How Google does Machine Learning 的第三章節~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 【Day 5】 Chapter 2(下) - ML要成功的秘訣與策略 Course - How Google does Machine Learning 第三章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nHow Google does ML 「Introduction」 「ML Surprise」 「The secret sauce」 ML and Business Processes The Path to ML End of phases deep dive Module 3 Quiz 1. ML Surprise 課程地圖\nHow Google does ML ML Surprise ML是什麼?\nML是一種透過讓電腦寫程式去完成一項任務。\n而這個電腦只需要看一大堆\u0026quot;例子\u0026quot;，就可以自動想到最好的\u0026quot;程式\u0026quot;。\n傳統的軟體工程\n軟體工程師寫一大堆code(描述rules)，\n使我們能達成目標(inputs能產生對應Outputs)\nML會自己想出這些rules該怎麼完成\n一樣能使我們能達成目標(inputs能產生對應Outputs)\nML Surprise\ndefining the KPIs -\u0026gt; 想要達成的目標 collecting the data building the infrastructure optimizing the ML algorithm integration -\u0026gt; 整合組織內部之前的所有系統 上圖我們會發現一件事情是，很多公司會期待如上方的時間分布，\n但我們真實的時間分布應該如下圖，\n有許多公司花太多時間在優化演算法上，\n甚至會想要不斷的使用最新最強的演算法，\n但就結果而言這樣並不會比較好，我們甚至更應該花時間在找資料上。\n另外還有一點是，我們也應該花少一點時間去訂KPIs，\nML的影響力有可能短期對於KPI並不會有很大的影響，\n但就長期來看，ML的預測準確性會不斷提升，影響力是非常長遠的。\n2. The secret sauce 課程地圖\nHow Google does ML The secret sauce 這個章節講到ML失敗的最常見10大陷阱，也算是google自己對客戶的經驗談，\n(配合前一章節提到的五個階段)\n(最常見的第一名)認為訓練自己的ML演算法會比寫軟體還快 只依靠演算法建ML系統，實現演算法的過程仍需要一堆透過軟體才能實現的事，\n而這件事情往往只會使整個過程更加複雜。\n所以我們更推薦先從簡單的軟體開始先寫。\n你想做ML，但你\"沒準備好data\" 就請先停止別做ML，去蒐集資料吧，沒有資料你是做不了ML的。\n你以為data已經準備好了 這裡還有一種的經驗是，很多企業說自己擁有資料，\n但那些資料是已過時，甚至沒有人整理過的，\n沒有整理過的資料，甚至都不清楚資料裡面有什麼，\n公司內也沒有人最近有去查看這些資料並提出新的想法，\n這些都算是\u0026quot;沒準備好data\u0026quot;(還沒有\u0026quot;價值\u0026quot;的data)。\n相信我，等到要開始訓練ML時，你會花更多的心力在處理資料上，\n甚至是為了乾淨的資料還要重新收集，這才會是更大的挫折。\n將人員保留在循環中 當有人開始在系統中訓練ML的系統時，那些人會變得相當重要。\n這段個人的理解是，以google來說，\n使用者通常能夠提供ML的結果反饋，\n我們需要想辦法讓人能持續地給系統的反饋，\n(因為沒有這些人，我們就沒有資料，他們非常的重要)\n太過專注於研究最好的ML演算法 以使用者的角度來說，不論他們得到的是不是ML的結果，\n他們會更在乎的是有沒有得到好的建議或很酷的觀點。\n一開始，從我們ML系統得到的結果也許不是最好的，\n但我們要使使用者能喜歡我們的產品，使他們不斷持續地投入。\n這樣我們的結果就能不斷的更加優化。\nML往不好的方向最佳化 提到這裡也還有另外一個可能的問題，\n我們也要避免不正當的得到回饋，\n例如像是讓使用者一定要定期回饋使用心得，\n或者我們採取了不好的方式取得資料，像是一定要點擊某個結果，\n這部分是需要思考的，因為這有可能使我們的ML學習到不良的內容。\n你的ML對真實世界是有幫助的嗎? 要記得去思考你的ML結果對世界有沒有幫助，\n你訓練了他，把他放在那裡為用戶服務，\n但無法說他有多好，或沒辦法分辨他有辦法讓客戶能終身參與，\n或是使他具有終身價值，這樣沒辦法向人說明這個ML是有用的。\n到底要自己訓練一個model還是使用pre-train model? 使用pre-train model在初期能夠省下不少時間，\n初期使用也能幫助功能快速完成，而且也較容易使用。\nML演算法需要訓練多過一次 事實證明，一個好的ML演算法需要訓練非常非常多次，\n很多人會只訓練一次，並拿結果來說很好，\n但事實上可能只完成了10%左右，\n一個好的ML演算法，他需要被訓練很多很多次。\n想要設計自己的知覺偵測模型或使用自己的NLP演算法 這是一種特殊的陷阱，事實上以NLP來舉例，\ngoogle解決這些問題都是經過數十年的研究而得到很好的調整，\n而以商用來說，你幾乎只需要一個現成的、已經製作好的或已經定義好的，\n先不要嘗試在這部分試圖做自己的研究，這會非常的貴。\n聽到這你可能會覺得，天啊，ML怎麼會有這麼多的陷阱，\n但好消息是，ML的價值是長遠的，\n而且ML能達成幾乎比任何已經有的產品還要更好的結果。\n同時，當你覺得ML難時，你的競爭者也會覺得ML很難。\n但當你有ML產品釋出時，你能給顧客帶來更好的使用者體驗，\n而且不斷的使用者回饋能使你的ML開始一直一直不斷的精進。\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 6】 Google ML - Lesson 5 - ML會失敗的最常見十大陷阱 與 企業運行ML時，在ML各階段應該放的比重與心力\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-07T02:43:12+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424PGpM6AW6xD.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/ml-trap/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】6 - Lesson 5 - ML會失敗的最常見十大陷阱 與 企業運行ML時，在ML各階段應該放的比重與心力"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天繼續完成 How Google does Machine Learning 的第二章節最後一部分~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? 【Day 4】 Chapter 2(中) - 我們要怎麼樣設計一個ML問題? 與google提供的好用工具 Course - How Google does Machine Learning 第二章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nWhat it means to be AI first What it means to be AI first\nTwo stages of ML\nML in Google products\nDemo: ML in Google products\nDemo: ML in Google Photos Google Translate and Gmail Replacing heuristics\nIt\u0026rsquo;s all about data\nFraming an ML problem\nLab Intro - Framing an ML Problem Framing an ML problem Lab debrief ML in applications\nDemo: ML in applications Pre-trained models\nThe ML marketplace is evolving \u0026lt;\u0026mdash; (昨天到這邊)\n「A data strategy」\n「Training and serving skew」\n「An ML strategy」\n「Transform your business」\n「Transform your business」 「Lab Intro - ML use case」 「Non-traditional ML use case」 Module 2 Quiz\n11. A data strategy (接續昨天的編號) 課程地圖\nWhat it means to be AI first A data strategy 這邊講到google map的一些應用\n左圖：依照rule\u0026quot;搜尋最快路徑\u0026quot;\n需要參考的東西：交通堵塞? 橋未開放?\n怎麼蒐集上述的資料?\n最後使用 A* algorithm 解決問題 (最短路徑演算法)\n以結果來說，終究只是在設定rule\n中間：另外一個例子，如何知道使用者在二樓?\nwi-fi points, barometric pressure, typical walking speed 之類的資料\n你有了這些數據，嘗試使用ML來避免寫rule\n右邊：google地圖還能夠推薦使用者?\n連接使用者過去的歷史，清楚使用者偏好，並進行推薦\n我們希望google map能成為使用者的虛擬助手，\n這問題只有ML能夠實現地圖服務的\u0026quot;個人化\u0026quot;\n因此，ML就是能超越手寫規則的方法， 手寫規則有太多有可能無法實現的事情了。 從上例中左圖(通用)到右圖(個人化)，\n越個人化的事情越只能靠ML來實現，\n(前面章節也有提到，這就是local data的問題)\n但不管需不需要ML，要完成這些事情我們都需要大量大量的data，\n對我們而言，寫rules或models相對來說只是小case。\n比喻：ML是火箭引擎，data就是燃料\nIf machine learning is a rocket engine, data is the fuel.\n不論任何的階段，Data永遠是勝利的關鍵，\n收集data除了要求量之外，也需要多樣化，\n以認識下圖為例子，如果資料太少，你可能用再複雜的規則也不知其形，\n隨著資料夠多，整理的樣子就會更加得清楚。\n因此，ML最重要最首先的事情，就是收集數據的策略。\nML strategy is first and foremost a data strategy.\n12. Training and serving skew 課程地圖\nWhat it means to be AI first Training and serving skew 在ML之前，我們還是必須要先分析一下數據\n分析數據也代表著要收集資料，掌握數據是最耗時又最難的部分 收集好數據還需要去rating data(finding labels)，有先分析過再進入ML階段可以避免做白工 要建立一個好的ML模型，就要夠清楚你的資料，先分析數據也是為了這點 ML會將產品走向自動與規畫化，也就是ML是數據分析的延伸與擴展。 換句話說，如果不會數據分析，也不能做ML。\n有一個常見ML產品的fail原因: training-serving skew\n簡單說是兩個單位收集資料的方式不同，造成資料訓練與服務時沒有對應\nThe problem is that the result of stream processing and the result of batch processing have to be the same.\n解決(減少問題發生的機會)方法：\n用一樣的方法蒐集資料，一個同時處理batch與stream的階段\n所以較好的架構應該如下圖：\n13. An ML strategy 課程地圖\nWhat it means to be AI first An ML strategy ML要注意的事情：\u0026quot;ML的重點在量而不是在複雜度\u0026quot;\n即使是小的ML model有有它的價值。\n另外一個心態是，多盡快失敗並反覆重試\nThe idea is that if you\u0026rsquo;re failing fast, you get the ability to iterate. This ability to experiment is critical in the realm of machine learning.\n從上圖我們可以看到，多嘗試，多快速的失敗，更能夠達到成功。\n(慢慢嘗試後得到的慢速失敗(採取比較穩紮穩打的作法)，反而成功的機會會小一些。)\n另外提到資料，90%的企業資料皆是沒有被結構化的\n試想 emails, video footage, texts, reports, catalogs, fashion shoots, events, news, you name it. All unstructured data.\u0026gt;\n不過幸運的是，我們現在處理這些未結構化的資料，\n因為有了google的各種pre-trained model已經變很簡單了，\n我們就把這些unstructured data丟進這些ML APIs，\n我們就能得到一些像entities, places, labels, people\u0026hellip; 之類的資料，\n因此我們不必在花時間去處理unstructured data，\n丟進這些ML APIs，拿結果再丟進我們的 custom ML model 訓練即可。\n14. Transform your business 課程地圖\nWhat it means to be AI first Transform your business Transform your business Lab Intro - ML use case Non-traditional ML use case 這章節在講ML在商業上怎麼應用\n使用ML訓練的方向是什麼? 記住一個字：\u0026quot;delight\u0026quot;\n如何使你的使用者開心就是訓練的目標。\n影片中的舉例：\nDid the card get canceled? Did the flight get delayed, etc.? Or did they just walk into your store and see an empty shelf? Why are they contacting you? Did your system automatically find the right action to take for this customer? Did it offer to rebook them? 只要是需要想辦法讓使用者開心的問題，都可以是我們訓練的目標。\n另一個例子：既然你喜歡的音樂可能有版權問題，\n何不自己用ML生一個音樂? 一定會是你最喜歡的。\n商業上能受惠於ML的三種方式：\ninfuse your applications with machine learning 簡化使用者輸入、更適應用戶\nuse machine learning to fine-tune your business\n簡化業務的流程、創造新的商業機會 use machine learning to delight your users\n用ML讓你的使用者開心。預測需求，並為他們量身打造 而這裡又有份學習單：\n試著去思考公司內一個已經存在的應用，去想哪個部分能夠用ML取代呢? What are some of the benefits to doing so? \u003e 這樣做的優點是什麼? \u003e What kinds of data would you collect if you wanted to do this? \u003e 你想要這樣做需要收集什麼資料? \u003e Are you collecting that data today? If no, why not? \u003e 你現在能取得這樣的資料嗎? 本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 5】 Google ML - Lesson 4 - What it means to be AI first (下) - ML要成功的秘訣與策略\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-06T02:37:32+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424Df47LPg2mY.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/ml-success/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】5 - Lesson 4 - What it means to be AI first (下) - ML要成功的秘訣與策略"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天繼續 How Google does Machine Learning 的第二章節吧~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 【Day 3】 Chapter 2(上) - 什麼是ML? 為什麼ML最近才紅起來? Course - How Google does Machine Learning 第二章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nWhat it means to be AI first What it means to be AI first Two stages of ML ML in Google products Demo: ML in Google products Demo: ML in Google Photos Google Translate and Gmail Replacing heuristics \u0026lt;\u0026mdash; (昨天到這邊) 「It\u0026rsquo;s all about data」 「Framing an ML problem」 「Lab Intro - Framing an ML Problem」 「Framing an ML problem」 「Lab debrief」 「ML in applications」 「Demo: ML in applications」 「Pre-trained models」 「The ML marketplace is evolving」 A data strategy Training and serving skew An ML strategy Transform your business Transform your business Lab Intro - ML use case Non-traditional ML use case Module 2 Quiz 6. It\u0026rsquo;s all about data (接續昨天的編號) 課程地圖\nWhat it means to be AI first It\u0026rsquo;s all about data 這章節就開始再講有關於資料的事情了，\n首先我們要知道，很多資料是很難搞的，\n例如下圖的例子：\n你可能會想\u0026hellip;這些資料有什麼特別的?\n這些資料很多都是\u0026quot;在地的店名\u0026quot;，\n我們稱之為\u0026quot;hard queries, local queries\u0026quot;\n而且人們搜尋這個並不是在找網站，而是在找地圖上的商家，\n這時你會想一個個的替每家店寫一行新的code rule，\n好讓你的搜尋找的到這家店嗎? 聽起來就很\u0026quot;笨拙\u0026quot;吧!\n那我們來看看ML怎麼解決問題吧!\n現在有個人對著google搜尋\u0026quot;coffee near me\u0026quot;\n你看到了兩家店，你該怎麼推薦給他呢?\n我們先來想想怎麼收集數據，使它成為ML問題，\nML背後的想法就是\u0026quot;蒐集一大堆例子，使這些變為知識並做未來的預測\u0026quot;\n這問題中未來的預測是什麼?\n很簡單，就是這兩家咖啡店的其中一家。\n可是這問題的例子呢?\n比較喜歡去比較近的店? 比較喜歡評價比較高的店? 比較喜歡服務時間較快速的店? 咖啡的品質呢? 咖啡的價格符合CP值嗎? 會不會剛好沒有供應咖啡? 搞不好你更喜歡其中一家店的附餐三明治? 能不能坐下好好喝一杯咖啡? \u0026hellip;\u0026hellip; 太多種可能要考慮的點了\n那你說我們如果要coding，你要將上述的問題全部變成一個個if的判斷條件式嗎?\n所以google寧願讓用戶告訴我們，而不是google自己猜測並寫了一大堆rule，\n我們用大量數據做權衡評估結果，而現在我們先將問題簡化成只考慮距離，\n但資料怎麼來?\n一開始還是以heuristics的方式為主，但google的心態是，\n等到資料足夠時(有夠多的例子)，就會拋棄heuristics的方法。\n這裡的例子，就是我們前面所說的labeled data，\ninput：到商店的距離 label：用戶喜歡/不喜歡 然後就開始蒐集資料，能整理出如下圖的結果，\n很明顯的我們就能看出，當咖啡店離我們越遠，越沒有人想去。\n所以，ML就是關於收集適當data，\n然後在良好學習與取得能信任的例子之間取得平衡。\n7. Framing an ML problem 課程地圖\nWhat it means to be AI first Framing an ML problem Lab Intro - Framing an ML Problem Framing an ML problem Lab debrief 這邊有個ML學習單(可以算是要做ML問題之前要想的心法了吧)：\nThe first framing (maching learning problem)\n-\u0026gt; If the use case was an ML problem\u0026hellip;.\nWhat is being predicted?\n你將預測的東西是什麼，也就是你的答案\u0026quot;X\u0026quot;(label) What data is needed?\n你需要什麼數據呢? \u0026lt;- 非常多種data都有可能會有影響(大量各種的inputs) The second framing (software probelm)\n-\u0026gt; Now imagine the ML problem is a question of software\nWhat is the API for the problem during prediction?\n我們需要透過API取得什麼樣的資料? Who will use this service? How are they doing it today?\n誰會使用這樣的服務? 這使用者今天在做什麼? The third framing (data problem)\n-\u0026gt; Lastly, cast it in the framework of a data problem. What are some key actions to collect, analyze, predict, and react to the data/predictions (different input features might require different actions)\nWhat data are we analyzing?\n我們要分析什麼資料? What data are we predicting?\n我們要預測什麼? What data are we reacting to?\n當預測結果出來時，我們要做什麼反應? 8. Demo: ML in applications 課程地圖\nWhat it means to be AI first ML in applications Demo: ML in applications 舉例：Aucnet - 日本最大的實時汽車拍賣服務\n以前是拍下多張照片上傳，\n經銷商還要一一比對並指定汽車的型號與部分，非常耗時。\n現在只要照著網站上的所說的上傳車子不同角度的照片，\n資料只要足夠，透過ML就可以做到即時分析車子的各種資訊，\n甚至還能估計出車子目前的售價範圍。\n9. Pre-trained models 課程地圖\nWhat it means to be AI first Pre-trained models 上章所提到的Aucnet，就是使用在GCP使用Tensorflow服務做成的。\n最簡單的在我們的作品中使用ML的方法，就是使用Pre-trained models，\n我們可以看見一般的訓練ML方法在左邊，\n但右邊的Pre-trained ML models，\n就是google都已經幫你訓練好了，可以直接拿這些ML模型使用。\n舉例：Ocado - 網路最大線上雜貨店\n以往的方式，寫email，慢慢看慢慢分析 後來使用了NLP(自然語言處理)，提取email中的各種資料，\n幫助它們能分析email的內容，還可以確認優先級。 但是越來越多客戶不想寫信，它們更希望的是直接交談。\n以前的方法，可能要一個個回答他們的電話 現在的方法，使用Dialogflow (google的對話代理工具)，\n直接分析對話，拆解使用者需求，能達到一樣的效果。 (影片後面為Dialogflow的演示，以前打黑客松有稍微玩過一下，\n也許之後有做這30天的Side Project可以再來玩一下這個?)\n10. The ML marketplace is evolving 課程地圖\nWhat it means to be AI first The ML marketplace\n這張主要在介紹一些有名的品牌或企業，\n使用google的Pre-trained ML models與他們的用法 以下只說明技術部分：\nAUCNET custom models - 傳統的土法煉鋼，自己從頭建啦! (膜拜) Ocado NLP API - 自然語言，辨識情緒與文章entities(實體) Giphy vision API - 尋找文字、(另外的例子：拒絕不適當的上傳) OCR - 光學文字識別 Uniqlo Dialogflow - google更高階的應用 (裡面一堆功能都先幫你內建好呢!) 他們提出的觀點是，現在ML這個領域也有分high level(高階)與low level(低階)，\n我們想構建應用，以現在科技的成熟，也不一定要從low level(低階)出發。\n雖然像上述一樣許多已經成熟的API可以直接使用非常方便，\n不過不見得所有的應用都有像上述的API，\n所以我們將從custom models開始教學，\n畢竟市場上也必須有人為一些尚未有的功能建構出API，\n而或許建構出那個API的人就會是你。 (感受到期待與壓力XD)\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 4】 Google ML - Lesson 3 - 我們要怎麼樣設計一個ML問題? 與google提供的一些已訓練好能直接使用的ML模型\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-05T02:35:58+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424COoh4mLocI.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/design-ml/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】4 - Lesson 3 - 我們要怎麼樣設計一個ML問題? 與google提供的一些已訓練好能直接使用的ML模型"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天就來進入 How Google does Machine Learning 的第二章節吧~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 【Day 2】 Chapter 1 - 讓你的ML在Google雲端平台運行的五大階段 Course - How Google does Machine Learning 第二章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\nWhat it means to be AI first 「What it means to be AI first」 「Two stages of ML」 「ML in Google products」 「Demo: ML in Google products」 「Demo: ML in Google Photos」 「Google Translate and Gmail」 「Replacing heuristics」 It\u0026rsquo;s all about data Framing an ML problem Lab Intro - Framing an ML Problem Framing an ML problem Lab debrief ML in applications Demo: ML in applications Pre-trained models The ML marketplace is evolving A data strategy Training and serving skew An ML strategy Transform your business Transform your business Lab Intro - ML use case Non-traditional ML use case Module 2 Quiz 1. What it means to be AI first 課程地圖\nWhat it means to be AI first What it means to be AI first 什麼是AI(Artificial Intelligence)? 與ML(machine learning)差在哪?\nAI是一種discipline(學科)，有許多的theory and methods\n什麼是ML(machine learning)?\nML是一種toolset(工具集)，ML就是拿來解AI問題的\n(ML是讓machine訓練與學習用的，他們沒有intelligent，但會become intelligent)\n2. Two stages of ML 課程地圖\nWhat it means to be AI first Two stages of ML ML的兩個stages Stage1 - training Stage2 - inference(推理)(也有些人稱為prediction(預測)) Stage1 : training 這邊以 Supervised Learning (監督式學習)作為舉例：\ninput： input資料本身 label： input資料的所對應的答案 (見下圖左邊) ML model的本身就是一種數學函數(math functions)\n我們的目標 = 讓input經過我們的數學函數(math functions)後產生label\nML 需要的就是 labeled examples = input + label(ture anwser)\nStage2 : inference 經過這樣的學習之後，即使是沒看過的貓，我們的模型也能夠正確的判斷他是貓。\n但要達成這樣的事情，關鍵是我們會需要非常非常多的資料。\n所以一整個ML模型的產生(two stages)的流程如下圖：\n一個好的ML模型，不能單單只有在Stage 1上面下非常多的功夫，\n很多研究數據的人員，甚至是書本、教科書都過度強調Stage 1的training重要性，\n但這裡就會掉入一個陷阱，過度強調Stage 1模型訓練，\n而不進入Stage 2的生產階段，往往實際上所訓練出來的ML模型都是沒有實際用途的。\n3. ML in Google products 課程地圖\nWhat it means to be AI first ML in Google products 每層layer都是一個function，\n一整個model = functions，也稱之為Neural Network\n然而Neural Network(NN)也只是一種數學模型而已\n其他還有常用在ML的數學模型像是： linear methods, decision trees, radial basis functions, ensembles of trees, radial basis functions\n最早的NN沒有這麼多層layer，只有一層，\n然而這也代表著為何最近ML才紅起來的幾個關鍵原因：\n原因1在於電腦運算能力 越多層(deep NNs)自然需要更強大的電腦運算能力\n原因2在於資料的數量 越多層(deep NNs)需要更多的資料來調整參數\n原因3在於計算技巧 越多層(deep NNs)需要更佳的計算技巧，否則會花非常大量的時間運算，甚至有大量沒幫助的參數\n總和以上原因，這些都是在近幾年才被解決的問題，\n所以google才能在近幾年有近4000個deep learning的應用。\nGoogle的產品中到處充滿了ML models，\n而且通常一個產品可能充滿了無數個ML models，而非只有\u0026quot;一個\u0026quot;\n一個產品中會碰到許多複雜的問題，而我們必須還要去拆解問題，\n拆解一個小問題之後，才能針對一個小問題去想出一個ML model去解決它。\nOne solution needs many ML models.\n舉例：how to forecast rather an item will go out of stock?\n這不會是只靠一個ML model能解決的問題，\n光這個問題我們就能拆成三個小問題，\n但三個小問題之中又不見得只靠一個ML model就能解決，\n也因此我們才會說一個產品可能充滿了無數個ML models，\n而且可能才解決了一個複雜的問題。\n4. Google Translate and Gmail 課程地圖\nWhat it means to be AI first Demo: ML in Google products Google Translate and Gmail 這個章節大概介紹了 Google Translate 與 Gmail 裡面是怎麼使用 ML的\n光是 Google Translate 裡面大概就有這六種不同的ML在使用\nGmail 裡面提到的是跟 Smart Reply (自動回覆)有關的功能，\n用到 sequence to sequence model，\n不過這樣也表示我們收到的信內容其實有被拿去丟進模型分析了，\n雖然不是直接被人拿去閱讀就是(微妙)\u0026hellip;\u0026hellip; 科技進步與個人資料保護的兩個難題啊。\n5. Replacing heuristics 課程地圖\nWhat it means to be AI first Replacing heuristics 引用自 Eric Schmidt, the Executive Chairman of the Board at Google 的一段話\n\u0026ldquo;Machine learning,\u0026rdquo; says Eric, \u0026ldquo;This is the next transformation. The programming paradigm is changing. It\u0026rsquo;s not programming a computer. You teach a computer to learn something and then it does what you want.\u0026rdquo;\n這句話非常有趣，裡面沒有提到任何有關於data的事情，\n許多人都認為ML只是在做資料結果的預測，\n他認為ML是一種可以取代programming的方式，\nML是一種logic，而並非只是在處理data。\n舉一個例子，例如我們今天想搜尋\u0026quot;Giants\u0026quot;，\ngoogle要怎麼知道我們想搜尋的是 San Francisco Giants 還是 New York Giants 呢?\n之前google的作法，會在搜尋時先記錄使用者的位置，\n依據不同的位置給予不同的搜尋結果(如下圖)，\n我們可以看到有一堆rules，來決定顯示什麼結果給使用者\n很明顯的，這樣的做法是非常\u0026quot;笨重\u0026quot;的，\n這種做法，每當我們想加入一個新的結果，\n我們必須再為了多那結果去coding一行判斷式，只為了判斷新的東西，\n而且這種做法通常會導致非常難維護。\n然而，我們怎麼不嘗試用使用者的搜尋熱度去自動決定結果呢?\n因此，依據此概念產生的deep ML model, RankBrain就出現了，\n除了依照使用者的搜尋熱度可以自動的動態調整顯示結果，\n而且這樣的模型還能夠靠自己就能不斷的進步。\n透過 ML 取代 heuristic rules 就是在講像這樣的事情，\nML還能夠替我們解決什麼問題? 如果提到 ML 未來的可能性，\ngoogle認為，只要任何是能夠被寫下rules的東西，ML都可以完成，\n而且不只侷限於統計分析的這類事情。\nNotice that saying that machine learning is a way to replace rules, notice that this is a far more expansive answer to what kinds of problems can machine learning solve.\n所以 Google 說自己是 \u0026ldquo;AI-first\u0026rdquo; company，\n他們認為 ML 在未來可以取代任何 coding 中所寫的 rules，\n只要能收集正確的資料，都可以透過 ML 去完成同樣的問題。\n我們可以開始用新的角度來思考問題，\n我們不用再去想該如何替問題的改變去 coding rules，\n我們只需要想怎麼根據資料去訓練模型。\n我們不用再去想要為了修bug，去替它增加一個新的 rules，\n我們只需要想怎麼根據新資料去繼續訓練模型。\n至於處理那些特別的inputs與特別的rules，\n我們只需要想怎麼建立更大規模的模型，來應對這些需要的預測。\n(這段真的太猛了\u0026hellip;，如果真的實現了等於不用再慢慢地針對情況一一對應coding了，\n未來的工程都是在訓練模型，只是將我們coding所實現的rule，皆轉換成訓練的目標)\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 3】 Google ML - Lesson 2 - What it means to be AI first (上) - 什麼是ML? 為什麼ML最近才紅起來?\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-04T02:34:16+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/20120424jsvxsChklN.png","permalink":"https://wongwongnotes.com/posts/ai/machine-learning/google-machine-learning/whats-ml/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】3 - Lesson 2 - What it means to be AI first (上) - 什麼是ML? 為什麼ML最近才紅起來?"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 今天就廢話不多說直接來進入 How Google does Machine Learning 的課程吧~\n這次鐵人賽的30天中，我目前所寫文章的所有課程目錄如下：\n【Day 1】準備日 - 註冊coursera與訂閱課程 Course - How Google does Machine Learning 裡面又有五大章節，今天就先從第一章節開始看吧\n第一章節的課程地圖：「(紅字標記為本篇文章中會介紹到的章節)」\n「Introduction to specialization」 「Introduction」 「Intro to ML on GCP Specialization」 「Specialization Agenda」 「Why Google?」 「Why Google?」 「Why Google Cloud?」 「Latest from Google」 「Module 1 Quiz」 1. Intro to ML on GCP Specialization 課程地圖\nIntroduction to specialization Introduction Intro to ML on GCP Specialization 此章節有一個影片，介紹了 Google Cloud Platform (GCP)的服務，\n另外有提到未來會使用於機器學習的程式語言是python(在GCP上)，\n以及提到 Google BigQuery 的服務與 Open Source 的 Tensorflow 的整合，\n替整個數據研究與機器學習對於公司組織應用上更加的便利了\n(以自己的經驗來說，將整個機器學習的服務移至雲端，\n這樣確實能在買硬體的成本應該能省下不少錢，\n而且需要時再訂閱也不擔心買了硬體擱置不用的問題。)\n2. Specialization Agenda 課程地圖\nIntroduction to specialization Introduction Specialization Agenda 這章提到了 GCP 上的 ML 整體的運作流程，\n主要分別五大步驟，影片中提到的圖片如下\n(不確定有沒有版權問題，有的話我再將圖拿掉)\n2.1. Why ML? 影片中提到這章節也許單純的介紹會無聊會讓你想直接跳下章學習(笑，真懂XDD)\n但有一句話提到這張的重點，我們還是必須清楚整個 GCP上的ML的架構，\n這樣未來在使用時你才能夠在大方向的掌握使用上的進程。\nThe big picture is very important for you to be able to get buy-in from the rest of the organization.\n2.2. ML with Tensorflow 想藉由完成TensorFlow完成Machine Learning，至少需要兩件事情，\n準備\u0026quot;好的\u0026quot;資料集 使用TensorFlow建立你的機器學習模型 影片中有特別提到準備\u0026quot;好的\u0026quot;資料集是一個不能忽略的重點，\n有許多訓練好的模型結果最後的產品失敗(甚至是某些企業都會失敗)，\n這時候通常原因都是需要回來檢查我們的資料集。\n2.3. Improving ML Accuracy 這個章節會灌輸你一大堆能精進ML模型結果準確度的想法與觀念，\n然而不同的情況自然會使用不同的想法與對應方式，\n所以這章節更多是教觀念，幫助我們在之後面對更多 Machine Learning 上面的問題，\n而且還提到需要花時間去消化觀念並能運用到code中，不可只知其觀念名。\n2.4. ML at scale 這個章節主要會提到如何讓訓練完成的ML模型進入產品化的階段，\n這個部分可能又比前面還要更加的困難，許多企業級的ML projects也經常失敗在此處。\nOperationalizing a Machine Learning model 大概又可以分成以下幾步驟\ntraining it at scale in a distributed way serving out the predictions building a Machine Learning model end to end 而在此章節將依照以下順序講解：\nhow to train, deploy predict with ML models in a way that they\u0026rsquo;re production ready delve back into Machine Learning theory 最後會回到 Machine Learning的理論講解，\n不過ML理論本身是多屬於heuristic(啟發式)的，\n也就是我們幾乎只能仰賴一些工具與技巧。\n因此我們將介紹一些工具跟技巧，提供的範例主要是在訓練圖片時有效，\n但這些也有助於在處理時間序列數據或文本數據更佳有效。\n2.5. Specialized ML models 最後這章節會介紹一些能建立 powerful recommendation systems 的方法，\nRecommendation systems是一種ML model，\n指的是你怎麼建立你個人化的演算法的方式，\n大多數ML工程師也會在職涯的某些階段中會逐漸建立起來這樣的個人化演算法。\n事實上這部影片認為，也許最後只會有一種ML system被大家所建，\n但在建立powerful recommendation engines之前，\n我們都會反覆碰到前四章所學的東西，不論是對工具與技巧的理解等等，\n我們可能在各種不同的地方學習ML，但儘管如此，細節也有可能完全不同，\n所以當你在碰到的新的ML教材時，也不要完全跳過他，將他視為有用的複習課程，\n並在一次確定自己還記得這些部分中提出的想法。\n3. Why Google? 課程地圖\nIntroduction to specialization Why Google? Why Google? 影片中提到了一些machine learning與我們日常的關係，\n特別是以下的圖片中的應用大家應該都不陌生。\n此外一個驚人的事實是，近幾年使用machine learning逐漸上升，\n就連google已推出了突破4000個有關於machine learning應用的產品。\n(近幾年的飆升真的滿驚人的)\n4. Why Google Cloud? 課程地圖\nIntroduction to specialization Why Google? Why Google Cloud? 影片中提到一般人認為的ML服務(ML serving, not just ML training)可能像下圖，\n很多人可能花了非常大量的時間再處理圖片的左半部，\n然而，ML service最主要的核心應用的卻是在圖片的右半部\nGoogle有提到，training data(batch data)與streaming data的相容性，\n會是一個影響訓練模型成功機率的關鍵因素，\n他們嘗試能在batch data與streaming data的進行相同的處理方式以減少失敗的機會。\nML的簡圖，基本上也代表著訓練的每個階段，\n而這部分透過GCP的服務，很多東西都已經先準備好了，\n我們可以用GCP的系統，具有彈性、可靠，且非常好的工程能力\n5. Module 1 Quiz 不知道是不是因為英文的關係\u0026hellip;我覺得有點難，\n有幾題考得滿細的，不過忘記答案的話可以回去課程找答案，\n這章應該幾乎都考很介紹的東西，所以感覺超級像背科(背他介紹的東西XDDD\n不過都幾乎快把整個影片翻譯完的我，當然就直接交了\n耶依~ Module 1 Quiz結束囉~\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 2】 Google ML - Lesson 1 - Introduction to specialization - 讓你的ML在Google雲端平台運行的五大階段\n參考資料 coursera - How Google does Machine Learning 課程\n若圖片有版權問題請告知我，我會將圖撤掉\n","date":"2019-09-03T02:31:55+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/201204246dFc6F8lUg.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/th-google-machine-learning/ml-in-gcp/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】2 - Lesson 1 - Introduction to specialization - 讓你的ML在Google雲端平台運行的五大階段"},{"categories":["510 - Google Machine Learning","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》","045 - 11th 鐵人賽 - 《Google Machine Learning 學習筆記》"],"content":"前言 會有這次作筆記的原因主要是因為 Google Developers Groups Taiwan (以下簡稱 GDG Taiwan)\n有推廣他們的課程，其中像是這次的「Google 機器學習培訓計劃」也是他們舉辦的活動，\n而在 GDG Taiwan 的計畫之下有舉辦一個「ML Study Jam」的讀書會，\n我是透過這個讀書會知道這活動的消息的。\n此外，由於今年 ML Study Jam 與 iT 邦幫忙的鐵人賽合作，\n並且推出「如果同時完成了這次的學習課程且完成鐵人賽，就加碼送 TensorFlow 背包！」的活動\n背包與那篇貼文長這樣：\n(題外話：這貼文的照片與文字真的超陽春可愛的XD)\n總之為了包包還是努力寫一下30天的文章XD，\n雖然不知道拿到包包後會不會真的把這個包包背出去就是了\n對他們的計畫有興趣也可以追蹤 GDG Taiwan 的粉專： https://www.facebook.com/gdgtaiwan/ 0. 先來說說這次的30天學習筆記預計會有的內容 由google提供的免費課程有預先指定其中兩個，\n我將在會在30天中優先完成這兩門課程：\nHow Google does Machine Learning Launching to Machine Learning 至於剩下的天數，規劃大概有兩種：\n繼續完成其他非指定的課程 畢竟難得有免費又高級的課程，當然要多學一點啊！\n試著用從這課程中學習到的東西試著做點小應用 只是照著教學做，感覺就沒有實作感，所以可以的話當然會希望能作些實際的應用，\n不過目前要做什麼還沒決定XD (看學到什麼東西或許就會有靈感了XD)\n今天是開始做 Google machine learning 學習筆記的第一天，\n通常第一天就來先認識一下環境與做一些基本設定吧~\n1. 開始註冊帳號 有報名此活動的應該都會在自己的信件夾收到一封啟用信，\n我們 需要透過信中的連結(重要) 開始我們的課程\n點信中連結會看到以下畫面：\n點選左下角的 Machine Learning with TensorFlow on Google Cloud Platform，並註冊課程\n註冊帳號成功後會顯示訂閱美金49元的提示，\n這裡我們就先按訂閱(第一個月是免費的，稍後會取消續訂)\n註冊時coursera會需要你的信用卡資料\n(如果學生沒有信用卡，可能就需要先跟家人借，\n不過放心這次的學習課程不需要付費，但需要綁定卡片)\n看到這樣的畫面就表示完成囉！\n按開始回到課程頁面會看到以下的畫面：\n2. 取消第二個月繼續訂閱(第二個月之後就要付費囉~) 再來我們可以透過個人購買設定或這個網址，去取消續訂第二個月的課程\n按下方的取消訂閱即可\n看到這個畫面就表示取消續訂成功囉！\n3. 開始上課 最後我們回到課程頁面，或點選以下兩個課程連結，就可以開始上課囉~\nHow Google does Machine Learning Launching to Machine Learning 由於剛剛已經註冊了第一個，這邊示範註冊第二個課程( Launching to Machine Learning)\n(注意：註冊課程後會有完成課程的期限(目前看上面寫的)，\n如果沒有把握在時間內完成兩門課程，建議等有空再註冊第二門課)\n註冊課程成功囉！\n4. 繼續完成課程 完成今天的課程後，如果之後想要繼續課程，\n可以回到coursera的網頁，或點以下連結直接看課程網頁\n本文同步發佈在: 第 11 屆 iT 邦幫忙鐵人賽\n【Day 1】 Google ML - 參賽原因 與 就先從認識 coursera 與訂閱課程開始第一天吧\n參考資料 官方所提供的操作手冊 (裡面部分內容需要有報名活動才能使用) ","date":"2019-09-02T02:24:26+08:00","image":"https://wongwongnotes.com/images/restored/2019/09/201204245fZfxATlKr.png","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/th-google-machine-learning/know-coursera/","tags":["11th鐵人賽","Google ML","學習筆記","機器學習","鐵人賽"],"title":"【Google ML】1 - Google ML - 參賽原因 與 就先從認識 coursera 與訂閱課程開始第一天吧"},{"categories":["041 - 得獎作品"],"content":"【競賽作品】LIFE．LEAF - 生命葉 2019 OpenHCI 最佳故事獎 作品 第二組｜Life Leaf\nLife Leaf擔任一個陪伴者及傾聽者的角色，讓患有罕見疾病的病友能夠擁有一個匿名的傾訴對象，同時也能接收到來自對方的溫暖及富有同理心的陪伴。\nLIFE．LEAF 團隊提供使用者透過匿名的方式紓解壓力，並以錄音去記錄自己的生活與心理狀態，同時也能接收到與自己有相同病例的故事，並能給予觸覺、視覺、嗅覺的回饋，讓使用者明白自己並不孤單。\n“以科技為媒介，創造人與人間的情感共生。”\n團隊成員 想解決的問題： LIFE．LEAF以一個陪伴者及傾聽者的角色，\n讓患有罕見疾病的病友擁有一個匿名的傾訴對象，\n同時也能接收到來自對方的溫暖及附有同理心的陪伴。\nLIFE．LEAF提供使用者透過匿名的方式紓解壓力，\n並以錄音去記錄自己的生活與心理狀態，\n同時也能接收到與自己有相同病例的故事，\n並能給予有溫度的回饋，讓使用者明白自己並不孤單。\n外型設計為一片仿自然界的葉片形狀，\n綠色擁有平靜、健康、希望等意象，\n葉片上的紋路象徵生長與生活的痕跡。\n","date":"2019-08-24T02:22:00+08:00","image":"https://wongwongnotes.com/images/restored/2019/08/%E4%BA%8C%E5%B0%8F%E3%80%81%E5%A6%82%E5%BB%B7%E3%80%81%E5%81%A5%E8%B1%AA-1-1-1.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/life-leaf/","tags":["HCI","UI/UX","作品集","得獎作品","競賽作品"],"title":"【得獎作品】LIFE．LEAF - 生命葉"},{"categories":["042 - 競賽作品"],"content":"第五屆 跨界超越競賽 作品名稱：IO拍拍手 團隊名稱：跨界超越 直接跨年！ 團隊成員 競賽簡報 想解決的問題 解決癌友病友飲食管理困難及抗癌信心不足的問題\n主要可細分為以下三類：\n36年 - 在台灣，癌症連續蟬聯10大死因 5分鐘 - 台灣平均每5分鐘就有1人罹癌 40% - 有40%的癌症患者死於營養不良，而非死於癌症本身 目標使用者研究 癌友深度訪談：訪談了4位癌友＋1位照顧者 二手資料研究 專家訪談 使用者洞察 小結：遭遇飲食管理的困難、治療過程的挫折感 依照上述，我們想提供的解決方向：\n清楚的記錄及資訊呈現，幫助癌友掌握自己的健康並與醫師溝通 社群的支持 價值主張 增加對抗癌症的自信及動機 我們的解決方法 - 「IO拍拍手」app ! 設計概念 清楚的記錄及資訊呈現 飲食及日常生理記錄 飲食：拍照、六大類營養素、份量。 生活：喝水量、排便次數、體重變化、睡眠時數、心情。 資訊概覽 日、週、月為單位，呈現飲食與生活紀錄概覽，提供與醫師溝通時的資訊媒介。 社群的支持 小精靈養成 養好APP中的小精靈為包裝，以溫暖的語句回應。 癌友飲食分享社群 分享飲食照，並加入拍手給予鼓勵的機制。 Our slogan! 【IO 拍拍手，陪伴你左右】\n","date":"2019-01-21T01:54:59+08:00","image":"https://wongwongnotes.com/images/restored/2019/01/IO-clap.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/042---%E7%AB%B6%E8%B3%BD%E4%BD%9C%E5%93%81/io-clap/","tags":["HCI","UI/UX","作品集","競賽作品"],"title":"【競賽作品】IO拍拍手 - 陪伴你走過癌症的挫折與困難"},{"categories":["041 - 得獎作品"],"content":"SportLite (給長者們的運動交流平台) 2018 第六屆梅竹黑客松 Mei-Chu Hackathon 中華電信 企業組 全國第三名 作品 這次的作品以上次的評審給我們的經驗作為起點，\n我們想發展一個「零學習成本」的應用程式，\n考量到有些老年人與3C產品的學習困難。\n我們從line出發，其之一考量的是老年人也經常使用，\n其之二考量的是免下載，直接加好友即可開始使用，\n並且在設計使用流程時我們一再的注意，\n「能簡單就簡單，簡單的就小心別讓他複雜了」\n因此在我們的成果中，基本上除了輸入身高體重之外，\n所有的功能都只需要”按按鈕”即可完成，\n免去所有複雜的操作，所有複雜的計算也都透過系統直接算好，\n加line好友即可立即使用的應用就這樣誕生。\n另一部分回到我們的主要功能，\n我們主打的是老人運動輔助計算app，\n簡單可以理解為「老人版的Nike+」，\n透過與我們的linebot按幾個按紐互動一下，\n所有的運動計算例如里程、卡路里、時間等等，\n都直接幫您算到好，\n並且，所有功能我們不用app；line直接幫您實現，\n透過中華電信大IoT平台的數據，\n由生活中到處皆有的路燈作為數據採集，\n每日出門前直接分析進入溫度、濕度、空汙情況，\n並給予適當穿著建議，或者天氣狀況不佳不建議出門。\n＊ＦＢ心得連結＊\n","date":"2018-10-28T00:07:26+08:00","image":"https://wongwongnotes.com/images/restored/2018/10/sportlite.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/sportlite/","tags":["Python","chatbot","作品集","全國第三名","得獎作品","競賽作品"],"title":"【得獎作品】SportLite (給長者們的運動交流平台)"},{"categories":["023 - 攝影筆記"],"content":"前言 此為 2018/10/25 手機攝影 (二) 的筆記\n視角、構圖、景別 五種視角 六種構圖 七種景別 構圖 井字構圖：拍人 基本構圖法\n左線、中間、右線\n三角構圖：拍景(大山大海) 視覺延伸\n遠近感\n由進走到遠的地方(會有消失點\u0026amp;視覺延伸)\n對稱構圖(色彩/大小/動靜/亮暗) 視覺焦點\n主配角\n三等分法 風景最常用\n平穩\n層次感\n天空、海、草原\n對角構圖 視覺焦點\n視覺延伸(拉長)\n延伸左上/拉長右下\n留白構圖 想像空間\n簡潔單純\n(model看的方向做留白)\n打卡：\n- 三角+對稱 - 井字+留白 抽色相片\n","date":"2018-10-25T20:56:48+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/023---%E6%94%9D%E5%BD%B1%E7%AD%86%E8%A8%98/photography-phone2/","tags":["攝影筆記"],"title":"【攝影筆記】手機攝影 (二) #待整理筆記"},{"categories":["023 - 攝影筆記"],"content":"前言 此為 2018/10/18 手機攝影 (一) 的筆記\n講師使用手機：note8\n畫面設計的美感 三角對稱、構圖\n天天P圖\n動圖、gif\n手機攝影基本功 對焦、曝光、白平衡\n對焦：決定誰清楚 (拍人：對焦人的眼睛)\n近拍特寫：手機高於物體15cm、放大、對焦\n曝光：看起來明亮(室內光不足)、調整照片亮暗 通常手機拍攝”明亮值+1“，畫質較高\n(後製調亮：畫質變差)\n白平衡：色溫、ＡＷＢ、ＷＢ(情境模式) 從環境燈(黃燈)，還原物體原本的色彩\n好用攝影APP大推薦 美顏相機 修圖模式、進階美顏、最左邊、一鍵美顏\n無他相機 拍好不用P\n完美自拍術 第一招\n公式1 找到自拍鏡頭、找眉毛、鏡頭高於眉毛(眉毛到額頭之間)\n(男生)穩重：鏡頭與眉毛平行\n公式2 頭頂頂在畫面最上方(畫面乾淨簡潔)\n心法公式 拍瘦：減少被拍到的面積\n側身站(50%)：一個肩膀給鏡頭、另外一個肩膀鏡頭沒拍到\n畫面往左邊轉：把右手臂切掉\n讓臉比較瘦：伸出脖子擺頭\n第二招\n拿起桌上的杯子\n舉起來放在臉上(遮住臉部、減少被拍到的面積)\n第三招\n女生\n牙齒痛：手指不要出力、手肘不要翹起來繞臉頰8式\n男生\n拍出神秘感：神秘感8式\n(女生用同樣方式可以拍出“風格感”)\n美顏相機\n進入進階美顏\n瘦臉瘦身 第一招：瘦臉\n第二招：豐胸\n增高 第三招：增高\n150變170，馬上變林志玲\n人像完美角度 超完美打卡照\n五大悲劇\n- 我和風景誰才是你女友 - 意境沙龍照，不是路人照 - 奇蹟長腿照也需要顧臉 - 等你到快門到天荒地老 - 我們拍照時你是我的敵人 兩個公式:\n攝影師 SOP\n- 手機畫面拍到全身 - 蹲下來、手機成90度、(手機最佳位置為：肚臍到大腿) - 腳踩螢幕的最下面、頭上面預留空間(人與背景比例-人(頭畫面一半)：40%~50%) Model SOP\n- 眼：Model看到鏡頭： - 腳：先伸腳、腳尖微顛、腳尖指向鏡頭 伸腳：男：開腳45度、女：剪刀腳(前後交叉腳) :::success 腳不要併起來！手要有事情做！ (全身酸痛法：哪裡痛摸哪(單手)) ::: - POSE：看型錄 - 身體：身體微側、抬頭挺胸縮小腹 - 手：身體叉腰(手肘往外不要對準鏡頭)、左手摸頭髮 - 肩膀：一個肩膀對鏡頭、另外一個肩膀藏起來 - 伸右腳插右手 - 臉：臉朝向右邊肩膀方向 看左邊肩膀、看手錶、看前方\n先腳、手、頭\n地標比例：人大\n大絕招：從頭上面拍、狗狗大頭視角\nvideo：FUN假趣\n日系文青網美\n","date":"2018-10-18T20:54:05+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/023---%E6%94%9D%E5%BD%B1%E7%AD%86%E8%A8%98/protography-phone1/","tags":["攝影筆記"],"title":"【攝影筆記】手機攝影 (一) #待整理筆記"},{"categories":["023 - 攝影筆記"],"content":"前言 此為 2018/10/17 基礎攝影 (三) 的筆記\n廣角 // 望遠\n50mm\n鏡頭分類(以焦距分)\n廣角:16-35mm標準:24-70mm望遠鏡:70-200mm\niPhone:29mm\n大光圈 景深淺 f2.8\n小光圈 景深深 f16\n焦距長 景深淺\n焦距短 景深深(注意背景清晰度)\n與等效焦距無關\n焦距(焦長、焦段)\n成像距離\n取決於器材\n(鏡頭後方)\n物距\n取決於被攝物遠近\n(鏡頭前方)\n光圈 光圈大、景深淺\n光圈小、景深深\n焦距 焦距長、景深淺\n焦距短、景深深\n物距(對焦距離) 距離短、景深淺\n距離遠、景深深\n先光圈、後快門、最後調ISO\n先快門\n中心思想\n拍攝主題(內容)\n主題：有主題才有主體\n尋找符合主題的主體\n需要經驗與觀察力\nex: 故事性的照片\n以「形容詞」命名的主題( 語言-\u0026gt;圖像 )觀乎態度與切點對自己或他人具有意義主題：關於態度\n又可以稱作立場或切點，即「你如何看待事情」\n好/不好、喜歡/不喜歡、贊成/不贊成\n美感是否重要？(新聞攝影時？)\n拍攝手法(形式)\n以形式支撐內容包含視覺元素中可操作的一切\n手法 (支撐內容)\n形式本身也有訊息\n令人更能體會主旨\n創作者的一貫風格\n(黑白與彩色)\u0026lt;-脫離現實？\n視覺元素?\n","date":"2018-10-17T11:32:34+08:00","image":"https://wongwongnotes.com/images/restored/2021/02/sPI8HEm-scaled.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/023---%E6%94%9D%E5%BD%B1%E7%AD%86%E8%A8%98/photography-basic3/","tags":["攝影筆記"],"title":"【攝影筆記】基礎攝影 (三) #待整理筆記"},{"categories":["023 - 攝影筆記"],"content":"前言 此為 2018/10/4 基礎攝影 (二) 的筆記\n光圈 景深 （前景深-\u0026gt;前面清楚）、動態、畫質\n數值大、開孔小（眼睛瞳孔）\n光圈與光圈值成反比 f1.8 大光圈 f16 小光圈 f2.8 大光圈、淺景深 快門 控制曝光時間的裝置(喀到嚓的時間)\n高速快門 跑步 流動主體 快門長 (例如：光軌、星軌、水流、瀑布) 安全快門 通常安全快門 (秒) = 1/焦距\n1/60秒以上：一般拍攝手持相機、跑步 1/250秒：”凝結“在空中的瞬間 感光度iso iso高容易有訊噪 iso：底片感光能力的測量單位(對光敏感度) -\u0026gt; 影響：曝光 相機三模式 S 或 Tv 模式：快門先決模式 (shutter priority mode / time value) 速度、動態重要的時候 安全？創意？ 1/60、1/250 快門速太快光圈無法配合，曝光不足 A 或 Av 模式：光圈先決模式 (Aperture Priority mode) 景深 淺景深？深景深？ 光圈越大景深越淺(HEBE) 大合照 淺景深 M 模式：手動模式 (Manual Mode) 在測光錶上顯示相機偵測到的曝光 曝光補償 EV值 相機測光不準確或不是想要的 可以透過EV值+-來調整亮度 很直觀 三者間的權衡 每一個畫面都有許多種正確曝光組合\n取決於你想要如何表現這個影像\n何者最能展項創意？美感？故事？\n攝影技巧 曝光三要素：快門、感光度（ISO）、光圈（景深） 畫面 對齊對齊對齊 善用倒影、玻璃、鏡子的反射 風格 外澳：末日感 捕捉最自然的神情：聊天~ 數值設定好，等待機會 善用曝光三角 用鏡頭編織故事 ","date":"2018-10-04T19:43:50+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/023---%E6%94%9D%E5%BD%B1%E7%AD%86%E8%A8%98/photography-basic2/","tags":["攝影筆記"],"title":"【攝影筆記】基礎攝影 (二) 曝光三要素：快門, 感光度(ISO), 光圈(景深) / 相機三模式：快門先決 (S模式/Tv), 光圈先決(A模式/Av), 手動模式(M模式)"},{"categories":["023 - 攝影筆記"],"content":"前言 此為 2018/10/3 基礎攝影 (一) 的筆記\n構圖 構圖 = 硬體操作 + 軟體佈局\n硬體操作：在相機上做的事 光圈、快門、感光度（ＩＳＯ）像素焦距\n軟體佈局：對現實的選擇與取捨主體背景/前景距離角度 構圖的定義 廣義：影響畫面構成的任何要件 狹義：著重軟體的佈局 選擇主體、背景、控制距離和角度\n如何構圖？ 構圖的精髓在於“選擇”，選擇留下重要的東西，\n不重要的就要”捨棄“它。\n對於畫面， “沒有對錯，只有選擇”。\n主體 “誰”當主體主體放“哪”。\n容易成為主體的事物 人物動物物品風景建築光線\n常見構圖法 - 太陽構圖（中心點構圖） 主體放在畫面正中央\n特性：\n優點：一眼就能辨認主體 缺點：單調、缺乏變化 訣竅：謹慎控制主體大小、比例 - 井字構圖（三分點構圖 將主體放在畫面橫直三等分的等分點上\n特性：\n適合人像拍攝 畫面穩定又不失流動感 安全、不易拍壞 訣竅：\n需注意主體的方向性 善用背景與主體互動 缺點：\n人物攝影放中間，一眼就看出要拍什麼， 少了背景、環境、故事 反例：視線困在角落，負面情緒、封閉、失落\n背景/前景 背景(或近景)有襯托主體的功用，可強可弱，但盡量避免反客為主\n背景與主體為互補關係，也是需要相互調配的比例關係\n三分構圖\n將畫面劃分成前景、中景、背景，三個部份各有內容，相輔相成 特性：適合應用在風景攝影 優點：\n有層次感、距離感、立體感 訣竅：\n加強前景與背景的動能 簡化：二分法\n透視點構圖 利用背景的空間關係\n畫面的延伸、路的延伸特性：\n具有深度視覺效果（空間感）\n以假想之透視線引導視覺方向訣竅：\n尋找具延展性、重複性、規律性的物體\n把主體的動能放到空間裡\n距離 景深 主體所佔比例 角度 水平方向 拍物體的：正面、側面、背面 垂直方向 拍物體的 仰角 上寬下窄：正梯形 俯角 上窄下寬：倒梯形（自拍用） 平視角 平的：長方形 - 小活動： - 拍攝具高度、垂直於地面的物體（人亦可） - 用仰角、俯角、平視角在同一地點各拍一張 - 觀察在這三個畫面中，主體有什麼不同？ 手機怎麼攝影？ - 構圖精準 - 曝光正常：調整EV值、曝光三角 - 畫面穩定：拿好手機、安全快門 拍一張照片需要完成哪些事？ - 選好器材 - 打開電源/App(還有鏡頭蓋) - 調整構圖 修圖 最主要的修圖：\n裁切畫面 調整明暗 參考：\n曝光度+ 陰影+ 亮部- 顏色 飽和度+ 顏色跑出\n清晰 效果：清晰、去朦朧\n","date":"2018-10-03T19:24:14+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/023---%E6%94%9D%E5%BD%B1%E7%AD%86%E8%A8%98/photography-basic1/","tags":["攝影筆記"],"title":"【攝影筆記】基礎攝影 (一) 構圖、主體、修圖，常見構圖法：太陽構圖、井字構圖"},{"categories":["042 - 競賽作品"],"content":"心動時分 賴著你 (提供心臟病患者協助) 2018 HACK X HEART - 生技醫療創新黑客松 Demo 影片 提倡概念簡報 結果成品圖 程式系統架構 相關心得連結 ＊ＦＢ心得連結＊\n照片 ","date":"2018-09-30T00:40:55+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/heart_1.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/042---%E7%AB%B6%E8%B3%BD%E4%BD%9C%E5%93%81/hackheart/","tags":["作品集","競賽作品"],"title":"【競賽作品】心動時分 賴著你 (提供心臟病患者協助) - 2018 HACK X HEART"},{"categories":["041 - 得獎作品"],"content":"TransforM (再設計變電箱) 2018 OpenHCI 整體最大獎 作品 [embed]https://www.youtube.com/watch?v=j93rvNKrjfs[/embed]\n獲得最佳 OpenHCI 整體大獎的 TransforM，觀察到大街滿是塗鴉的變電箱，其背後隱藏著說者欲匿公開發表，但不接受評論，不違法地「表達自我」 之需求，與聽者不願接受過度衝擊訊息之問題，試圖利用互動設計再設計變電箱，同時滿足雙方的需求，趣味地重新定義變電箱所扮演的角色。\nTransform 原意有傳遞及改變形式的意義，加上我們藉由引用變電箱調節電壓的概念，重新定義變電箱所扮演的角色，賦予它傳遞及轉化訊息傳送者與訊息接收者之間的衝突矛盾的意義。「以訊息為主體」，滿足訊息傳送方想匿名、公開發表、不接受評論、不違法地「表達自我」之需求，且滿足接收方不願接受過度衝擊訊息之需求，緩解兩者之間的衝突點。\n＊ＦＢ心得連結＊\n＊新聞連結＊\n","date":"2018-08-24T00:16:16+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/OpenHCI_news.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/transform/","tags":["HCI","UI/UX","作品集","全國第一名","得獎作品","競賽作品"],"title":"【得獎作品】TransforM (再設計變電箱)"},{"categories":["610 - 作業研究 OR","640 - 管理科學 MS"],"content":"第八章 Transportation and Assignment problems 運輸問題 (Transportation problem) 雖然有六條算式，但剩下那條可由其他五條組合而得出，所以只有五條限制式\n解法 方法一：Northwest Corner Method 直接填滿\n方法二：Vogel\u0026rsquo;s Approximation method 1. 計算penalty \u0026#34;次小-最小\u0026#34; 2. 找到penalty最大，填滿他，刪除其他限制式 3. 重複1.2 直到找到所有值(資源耗盡) 方法三：優化解 the MODI method(uv method) 1. 找到不合的最大值 Cj-Zj \u0026#34;為負\u0026#34;且最小 2. theta取代該值，循環補償誤差 3. 修正值 4. 重複1.2.3. 直到Cj-Zj 皆為正 運輸問題 (Transportation problem) 的特殊題型 Dummy 問題 有時候supply與demand不一定相等，需要設一個dummy變數代表剩餘的資源\nDegeneracy (退化解)問題 解出來的變數值有0\nMaximum 的運輸問題 1. penalty改算 \u0026#34;最大-次大\u0026#34; 2. Cj-Zj 找正 ","date":"2018-06-24T20:00:57+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/operations-research/or/transportation-problem/","tags":["MS","OR","作業研究","管理科學"],"title":"【管理科學】運輸問題 (Transportation problem) 期末考筆記 - MS (管理科學、Management Science) / OR (作業研究、Operations Research)"},{"categories":["043 - 個人作品"],"content":"前言 這個是我在研究所課餘時，所做的 line chatbot 小秘書，\n主要功能為透過 line chatbot 提供小遊戲的功能，\n後續的更新還有新增賭場的功能，透過連動 google sheet 的方式作為我們的 database，\n我的 line chatbot 能有一個簡易的後端資料庫，能儲存每一個人的財產。\n後續還有提供教育的功能，讓使用者可以自行教導 line chatbot 說話。\n當時具體成就：一週最高可以有 100萬次的 reply (如下圖) 相關教學投影片 可參考我的另外一篇文章：\nhttps://wongwongnotes.com/posts/life-and-work/portfolio/ppt/python-line-chatbot/\n所有功能總覽 【使用說明書】 「!使用說明書」、「!說明書」、「!help」 「!helptxt」 【健康教育類】 「!教育」、「!調教」 「!學圖」、「!智乃看圖片」、「!給智乃看圖」、「!智乃看圖圖」 【遊戲抽抽類】 -　「小遊戲」 【 關鍵10秒鐘 】 「!tick」 「!stop」 【 多人競技場 】 多人競技場 「!pkall」 「!pkalloff」 「!pkstart」 「!pkrule」 多人競技場(進階) 「!pkkall」 「!pkkalloff」 「!pkkstart」 「!pkkrule」 【 計時賽模式 】 計時賽模式 「!tpk」 「!tpkoff」 「!tpklb」 計時賽模式(進階) 「!tpkk」 「!tpkkoff」 「!tpkklb」 【 對決模式 】 對決模式 「!pk」 「!pkoff」 對決模式(進階) 「!pkk」 「!pkkoff」 【 21 點 】 「!21」 【幾Ａ幾Ｂ】 「!ab」、「!幾A幾B」 「abreset」、「!abreset」 「abrecord」、「!abrecord」 【終極密碼】 「!終極密碼」、「!password」、「!psword」 【定時炸彈】 「!boom」、「!定時炸彈」 【撲克比大小】 「!比大小」 【機會、命運請選擇？】 「機會命運」 【梭哈】 「!sh」 【居家控制類】 「!開燈」、「開燈」 「!關燈」、「關燈」 「!關機」、「關機」 【如果很餓的話】 -　「好餓」 「!抽食物」 「!抽飲料」 【做點筆記吧！】 「!addnote」 「!delnote」 「!shownote」 「!restorenote」 「!backupnote」 【 帳號相關 】 「!newac」 「!money」、「!tibi」 「!reborn」 「!leaderboard」、「!排行榜」、「!lb」 【 遊戲排名相關 】 -　「!散步打排名」(lzbot、apriltestbot 限定功能) 「即時排名」、「即時戰況」、「排名」、「排行」、「分數」、「戰況」、「score」 「%數」、「%」 「一位差」 「分數差」 「場數差」 「追擊時間」、「脫褲子」 「時速」 「場速」 「活動進度」、「進度」 「剩餘時間」 「房號」、「room」、「rm」、「r」 「r1」、「room1」 「r2」、「room2」 「!fire」 「!stone」 「!pt」 「!lz」 「!ptstone」 「!ptcal」 【 模擬抽獎機 】 -　「!抽抽」(lzbot、apriltestbot 限定功能) 「!cgss單抽」 「!cgss十連」、「!cgss十抽」、「!cgss10連」、「!cgss10抽」 「!bgd單抽」 「!bgd十連」、「!bgd十抽」、「!bgd10連」、「!bgd10抽」、「!gbp十連」、「!gbp十抽」、「!gbp10連」、「!gbp10抽」 「!sc單抽」 「!sc十連」、「!sc十抽」、「!sc10連」、「!sc10抽」 【算命抽籤類】 「!機率」 「!抽數字」 【不知幹嘛類】 「貼圖辣」、「貼圖啦」、「貼圖」、「貼圖喇」 「母湯」 「母湯電影版」 【如果覺得太吵的話】 -　「!開關」 「!說話」 : 轉換為active mode 「!閉嘴」 : 轉換為slient mode 【如果壞掉了要維修】 「!壞掉啦」、「呼叫工程師」 【測試區】 「test」 : Hello World !!! 「state」 : 取得現在狀態(啟動模式/靜音模式) 「!getinfo」 : 取得資訊 「!網速」、「網速」、「speed」、「!speed」 : 取得回應速度 開發過程 FB 心得 開發過程FB - 1\n開發過程FB - 2\n","date":"2018-06-11T00:47:53+08:00","image":"https://wongwongnotes.com/images/restored/2018/06/line-chatbot-education-576x1024.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/works/my-line-chatbot/","tags":["Python","作品集","個人作品"],"title":"【作品集】自製 line chatbot (含賭場功能、教育功能、遊樂場功能)"},{"categories":["610 - 作業研究 OR","640 - 管理科學 MS"],"content":"Duality \u0026amp; Postoptimality Analysis (敏感度分析) forms primal form 原題\nCononical form 統一大小於，做符號轉換(大於、等於、小於)\nDual form 以係數來重新檢視問題(有點類似做轉置矩陣)\nTheorems Theorem 4-1 objective function primal form 與 Dual form 會相等\nTheorem 4-2 slack variables(除了目標式變數之外，我們自己加的變數(非人工變數))\nslack variables = 0 時，dual variable 會對應必有值\n反之，slack variables 有值 時，dual variable 必為 0\nTheorem 4-3 slack variables所對應的Zj那個row(final table)，會正好是dual variables的解\nPostoptimality Analysis 對於\u0026quot;目標變數\u0026quot;來說，可變動範圍表示\u0026quot;不影響最佳解的值\u0026quot;，但object function得到的值會變 換句話說就是 X1=5 X2=10 這種不會變，但Min Max會隨著你調整係數改變\n對於\u0026quot;constraints的值\u0026quot;來說，可變動範圍\u0026quot;會影響最佳解的值\u0026quot;，且object function得到的值也會變 邊濟效益(經濟學) shadow price: 正是指我們在上方所求得的 dual variables\n而 dual prices 也正對應 constraints的值\n想想一個問題：如何使 Max f = 12x1+8x2 能夠再增加? 1. 改變係數:我們去動12跟8 -\u0026gt; 改變係數，X1,X2不變，dual price改變，值增加 2. 改變限制:我們讓你有更多的資源 (造成X1,X2增加) -\u0026gt; 改變右邊資源，X1,X2改變，dual price不變，值增加 第四章末 Postoptimality Analysis 找B^(-1)的方法 BI -\u0026gt; IB^(-1)\nAnalysis of the coefficient 改設係數為C final table Cj-Zj\u0026lt;=0 看範圍\nAnalysis of the right-hand sides 設原限制右側值為b 透過B^(-1)運算得到新b \u0026gt;=0看範圍\n100 percent rule 單一變化的總和相加小於100%時，結果不受影響\nadd new decision variable 一樣設係數為C，透過B^(-1)運算得到final table 判斷 Cj-Zj需要\u0026gt;=0，表示生產新東西更有價值。\n","date":"2018-05-21T19:52:44+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/operations-research/or/duality-postoptimality-analysis/","tags":["MS","OR","作業研究","管理科學"],"title":"【管理科學】Duality \u0026 Postoptimality Analysis (敏感度分析) 期末考筆記 - MS (管理科學、Management Science) / OR (作業研究、Operations Research)"},{"categories":["610 - 作業研究 OR","640 - 管理科學 MS"],"content":"simplex method 變形 大於、等於的處理 使用 M\nMinimization 最小目標 1. +M 2. Cj-Zj改為判斷最小值 (同時停止目標變成 \u0026gt;=0 時) Alternate Optima 多重解 說明: 找到兩個 basic feasible solution 注意: 解到最後一張表之後\n當底下Cj-Zj有發現\u0026quot;非\u0026quot;當時basic variable 也為0時\n表示可以在”增益為0“的情況下進行代換\n既然\u0026quot;增益為0\u0026quot;，也表示最佳解被\u0026quot;保持一樣的值\u0026quot;\n但我們可透過代換找到不同的數字也得到\u0026quot;一樣的最大值\u0026quot;\n所以為多重解\nUnbounded solution 最大值無限大，無法找到 說明: 解在開放區域 解到theta皆為負 注意: theta為負表示資源可以不受限制的取用，\n這樣的話我們可以無限的使最大值增加，\n這樣的題目本身是不合理的(最大資源量應該有限制)，\n此為Unbounded solution(並非無解，而是解可以到無限大)\ninfeasible problems 無解問題 說明: 解完，M未消失 注意: M未消失，表示為infeasible，點在解區域外，為無解。\nunrestricted variables 無限制答案範圍的解 說明: 當變數可有負數解時 X1 = X11 - X12, X11,X12 \u0026gt;= 0 two-phase method 二階解(另解) 說明: 可以視為 simplex method 的另解 Q:為什麼要有這個另解? A:在很多時候我們很難去定義M到底是什麼，或說M到底有多大\n這在某些狀況會產生很大的問題，例如你要怎麼告訴電腦M是什麼?\n★Phase1 - 處理人工變數(單純來說就是在沒用M的情況下照解)\n利用Min必須為最小的特性(逆向操作：Max不可為負)， 當只有人工變數係數為正，為了使答案最小，人工變數的值必須為0 ★銜接Phase2 - 判斷情況\n1. Min不等於0 - 此題無解 2. Min等於0，人工變數答案為0 - 此題有解(進入Phase2階段) 3. Min等於0，人工變數答案不為0 - 此題有解(進入Phase2階段)，且有冗限制式(不重要的限制式) ★Phase2 - 因為M在Phase1被處理掉了，我們可以回歸正常的解法\n","date":"2018-05-10T19:57:15+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/operations-research/or/simplex-method-transform/","tags":["MS","OR","作業研究","管理科學"],"title":"【管理科學】simplex method 變形, two-phase method 二階解(另解) 第二次期中考筆記 - MS (管理科學、Management Science) / OR (作業研究、Operations Research)"},{"categories":["640 - 管理科學 MS","610 - 作業研究 OR"],"content":"solve LP problems exterme points 概念：畫圖求解 basic solutions 概念：變數嘗試代點0 simplex method 由George B. Dantzig.所提出\n-\u0026gt; Hill-Climbing Method\n例題 Max f. = 12X1+8X2 5X1+2X2\u0026lt;=150 2X1+3X2\u0026lt;=100 4X1+2X2\u0026lt;=80 ROUND1 步驟1：以上面敘述建表 步驟2：完成zi，Ci-zi欄，判斷是否找到解 運算規則： 1.Ca那個column乘上右側Xi，加總後得zi\n2.Xi上方數字減zi得到Ci-zi\n判斷解： Ci-zi那一個row如果還有數字為正，表示還能夠再增加(未找到最佳解)\n概念： 增加效益為正，為負表示硬增加只會讓Max更少\n範例(藍色部分)： 1. zi = 0*5+0*2+0*4 = 0 2. Ci-zi = 12-0 = 12 步驟3：(如果還未找到最佳解)挑選pivot column(藍色)，計算Θ，再找pivot row(綠色) 運算規則： - 找pivot column(藍色)：找Ci-zi的row最大數字 觀念： 我們希望能找到能讓目標增加最大的變數(如同跑得快的車子會比跑得慢的車子更快到達終點)，我們這裡挑選X1，因為他一次(增加1)就能幫我們增加12，而X2一次(增加1)只能幫我們增加8，我們優先挑增加最多的，當然能比較快達到目標。\n- 計算Θ，再找pivot row(綠色)：pivot column乘上Θ等於b 觀念： 雖然是選擇最快的車，但也不代表我們可以無限的衝刺，原先的限制式幫我們限制了X1能增加的極限，如同上面的例子中：\nX3 row：X1*30=150 X4 row：X1*50=100 X5 row：X1*20=80 算出來就是Θ，表示如果那行全部投資在X1可以達到的上限，我們可以發現X1最多就是增加20(再多就會超過X5 row的限制了)，而X5 row在此就成為我們的pivot row(綠色)\n補充: theta\u0026lt;0 的意義為 \u0026ldquo;這個限制式讓你有足夠的餘裕，你可以無限的增加\u0026rdquo;\ntheta=0 的意義為 \u0026ldquo;這個限制式我不影響你\u0026rdquo;\n步驟4：我們利用最後所找到的pivot column與pivot row再次重新出發 以上面例子來說，我們找到的是 X1(pivot column) 與 X5(pivot row)，\n我們將這兩個做交換如下(注意Ca有變)，\n交換後因為Θ、zi、Ci-zi的資訊是屬於舊的，我們可以直接刪去以免造成計算上的混亂 ：\n步驟5：玩弄你的數學！透過矩陣運算，將屬於Xa那個column的對應係數都調成1且其他係數為0 詳細過程我們做了什麼？\n1. X1 row /4 2. X3 row - (新X1 row*5) 3. X4 row - (新X1 row*2) 好的，這回合我們找到了解 (X1,X2,X3,X4,X5) = (20,0,50,60,0)，\n我們沒有找到最佳解，但沒關係，我們已經離解答接近了一步，\n我們利用最後所找到的pivot column與pivot row再次重新出發。\nROUND2 步驟1：我們現在的表長這樣 步驟2：完成zi，Ci-zi欄，判斷是否找到解 運算規則： 1.Ca那個column乘上右側Xi，加總後得zi 2.Xi上方數字減zi得到Ci-zi 判斷解： Ci-zi那一個row如果還有數字為正，表示還能夠再增加(未找到最佳解)\nzi(黃色部份)：只有12有值，只需特別計算那個row(藍色部分)\nCi-zi(綠色部分)：等於圖上紅色部分減黃色部分\n補充：\nCi-zi為增加的效益\n上方我們找到 12 , 8\n但此處我們找到 卻不是8而是2\n因為限制式開始讓我們想增加購買X2的量時\n必須付出相對的代價\n我們拿一個X2確實增加了8\n但也必須付出代價6\n所以實際拿一個X2變成只能增加2的效益\n步驟3：(如果還未找到最佳解)挑選pivot column(藍色)，計算Θ，再找pivot row(綠色) 運算規則： - 找pivot column(藍色)：找Ci-zi的row最大數字 我們找到了2\n- 計算Θ，再找pivot row(綠色)：pivot column乘上Θ等於b X3 row：X2*X3Θ=50 -1/2*X3Θ=50 X3Θ = -100 X4 row：X2*X4Θ=60 2*X4Θ=60 X4Θ = 30 X1 row：X2*X1Θ=20 1/2*X1Θ=20 X1Θ = 40 算出來的Θ，我們可以發現X2最多就是增加30(再多就會超過X4 row算式的限制了)，而X4 row在此就成為我們的pivot row(綠色)\n步驟4：我們利用最後所找到的 pivot column 與 pivot row 再次重新出發 以上面例子來說，我們找到的是X2(pivot column)與X4(pivot row)，\n我們將這兩個做交換如下(注意Ca有變)，\n交換後因為Θ、zi、Ci-zi的資訊是屬於舊的，我們可以直接刪去以免造成計算上的混亂 ：\n步驟5：玩弄你的數學！透過矩陣運算，將屬於Xa那個column的對應係數都調成1且其他係數為0 詳細過程我們做了什麼？\n1. X2 row /2 2. X3 row + (新X2 row*1/2) 3. X1 row - (新X2 row*1/2) 好的，這回合我們找到了解 (X1,X2,X3,X4,X5) = (5,30,65,0,0)，\n這回合我們沒有找到最佳解，但沒關係，我們已經離解答又更接近了一步，\n我們利用最後所找到的pivot column與pivot row再次重新出發。\nROUND3 步驟1：我們現在的表長這樣 步驟2：完成zi，Ci-zi欄，判斷是否找到解 運算規則： 1. Ca那個column乘上右側Xi，加總後得zi 2. Xi上方數字減zi得到Ci-zi 判斷解： Ci-zi那一個row如果還有數字為正，表示還能夠再增加(未找到最佳解)\n好的，我們發現Ci-zi那一個row的數值已經都小於0，這也表示無法再增加值(無法找到更好的解)\n所以我們找到了解 (X1,X2,X3,X4,X5) = (5,30,65,0,0)，\n也表示我們找到了答案，答案為X1=5，X2＝30(紫色部分)\n","date":"2018-04-30T19:39:52+08:00","image":"https://wongwongnotes.com/images/restored/migrated/YFhXurI.png","permalink":"https://wongwongnotes.com/posts/cs-theory/management-science/ms/simplex-method/","tags":["MS","OR","作業研究","管理科學"],"title":"【管理科學】solve LP problems — simplex method 詳細步驟與概念（第二次期中考筆記）"},{"categories":["640 - 管理科學 MS","610 - 作業研究 OR"],"content":"IP Problems 1.knapsack problem/背包問題 特色：基礎題，選擇要不要把東西放進背包(只有0or1可選) 2.knapsack problem/背包問題-續 特色：簡易條件式，可透過些微判斷得到條件式 3.fixed-charge problem/存在才需支付問題 特色：如何讓你的存在影響到我會存在 ex:要做某商品才需要付機器租用費，反之則不租則不用付\n4.set-covering problem/覆蓋範圍問題 特色：依照距離，能在時限內的只需要有1即可達成條件 條件式 IP problems 1.either-or problem ★小提示：\n難理解的話可以先思考這題，\n想讀書或睡覺\n★可能之一： 讀書 + \u0026ldquo;不睡\u0026rdquo;\n★可能之二： 睡覺 + \u0026ldquo;不讀\u0026rdquo;\n★特別注意:\neither-or 並不是 or問題，一方成立另一方不可以成立\n基本條件式：\nf\u0026lt;=0 or g\u0026lt;=0 (這邊其實這樣表示不是很好，應該只有一個能成立) 轉換成\nf \u0026lt;= My g \u0026lt;= M(1-y) 其中：\nM為極大數 y等於0或1 y等於0時:\nf\u0026lt;=0 f\u0026lt;=0的條件\u0026#34;單獨\u0026#34;發生 g\u0026lt;=M M極大 此式恆成立 (不重要的式子) y等於1時:\nf\u0026lt;=M M極大 此式恆成立 (不重要的式子) g\u0026lt;=0 g\u0026lt;=0的條件\u0026#34;單獨\u0026#34;發生 2.if-then problem 基本條件式：\nif f\u0026gt;0 then g\u0026gt;=0 轉換得\n-g \u0026lt;= My f \u0026lt;= M(1-y) M為極大數 y等於0或1 等於0時:\n-g\u0026lt;=0 -g\u0026lt;=0(等價於g\u0026gt;=0)的條件\u0026#34;單獨\u0026#34;發生 而前式f\u0026gt;0(自動成立) f\u0026lt;=M M極大 此式恆成立 (不重要的式子) 等於1時:\n-g\u0026lt;=M M極大 此式恆成立 (不重要的式子) f\u0026lt;=0 f\u0026lt;=0的條件\u0026#34;單獨\u0026#34;發生 g因此不受限 ","date":"2018-04-27T19:25:30+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/management-science/ms/ip-problem/","tags":["MS","OR","作業研究","管理科學"],"title":"【管理科學】IP problem, 條件式 IP problems (either-or, if-then problem) 第一次期中考筆記 - MS (管理科學、Management Science) / OR (作業研究、Operations Research)"},{"categories":["610 - 作業研究 OR","640 - 管理科學 MS"],"content":"LP solution procedure - define decision variables(決定變數) - Define objective function（決定目標函數：Max/Min） - construct constraints on decision variables(決定限制式) LP Problems 1. Allocation models/分配問題 特色：基本型，大於小於限制 2. blending models/混合問題 特色：注意上下界範圍需要考量 混合：和等於1 3. operations planning/工作規劃 特色：一方面小於工時，另一方面大於需求 4. input/output model/階段輸入輸出型產量問題 特色：一個階段會產生下一階段 建議先把流程畫出來再處理等式 5. shifting scheduling/工作排程的時間平移 特色：時間平移的多重等式，重複使用變數多 6. time-phased moedls/總工作量接續問題 特色：上一階段未完成的工作量，可以平移到下一時段 5.6.可以合考 5.當obj func. 6.當constraints\n7. MaxMin Objectives/極大極小問題 ★小提示：\n難理解的話可以先思考這題，\n現在一個學校有三個班級\n1.求三個班的第一名中最低分的(MinMax)\n2.求三個班的最後一名中最高分的(MaxMin)\n★進階思考：\n三個班的第一名中最低分的(MinMax)\n會不會一定大於等於\n三個班的最後一名中最高分的(MaxMin)呢?\n不過上述例子中，是由已知數值去做運算，\n實際上我們是要調整\u0026quot;變動\u0026quot;數值，所以這例子頂多作為幫助思考\n概念：balance，取平均\nMaximize the minimum\n用上面例子來說的話，全校總分固定，\n讓每個班級的最低分能有最大分數\n這樣應該就很好想像了，\n=\u0026gt; 只要大家都考差不多就好啦\n8. absolute function/絕對值問題 ★概念：\n我們都知道|x-y|只會有兩種解:\nx\u0026gt;y時，x-y\ny\u0026gt;x時，-(x-y)=y-x\n如果我們現在可以找到兩個數字\n$$\ns1^+，s1^-\n$$\n其中\n$$\ns1^+ \u0026gt;= 0，s1^- \u0026gt;= 0\n$$\n我們可將原本的式子改寫成\npart1:絕對值內限制 $$\nx-y = s1^+-s1^-\n$$\npart2:絕對值等式 $$\n|x-y| = s1^++s1^-\n$$\n★驗證：\n如果 x\u0026gt;y，|x-y|=x-y 此時\n$$\nx-y = s1^+-s1^- 中，s1^+=x-y，s1^-=0\n$$\n$$\n原絕對值|x-y| = s1^++s1^- = x-y 成立\n$$\n如果 y\u0026gt;x，|x-y|=y-x 此時\n$$\nx-y = s1^+-s1^- 中，s1^+=0，s1^-=y-x\n$$\n$$\n原絕對值|x-y| = s1^++s1^- = y-x 成立\n$$\n★注意：\nQ:為什麼我們可以知道s1會剛好是我們要的值?\nA:因為我們要找的是find Min of \u0026ldquo;s1+\u0026rdquo; + \u0026ldquo;s1-\u0026rdquo;\n當一方為0另一方有值即為最小，一但雙方皆非0，總和必不是min\n","date":"2018-04-22T21:53:38+08:00","permalink":"https://wongwongnotes.com/posts/cs-theory/operations-research/or/lp-problem/","tags":["MS","OR","作業研究","管理科學"],"title":"【管理科學】LP problem 與 solution procedure（第一次期中考筆記）"},{"categories":["043 - 個人作品"],"content":"自製打地鼠機 第4代 [embed]https://www.youtube.com/watch?v=-fOLVDIJHy8[/embed]\n開發過程FB\n自製打地鼠機 第3代 開發過程FB\n自製打地鼠機 第2代 自製打地鼠機 第1代 開發過程FB\nhttps://www.facebook.com/weng.hao.5/videos/10206603529361791/\n[embed]https://www.youtube.com/watch?v=x-SETBGANWA[/embed]\n","date":"2018-04-08T02:30:12+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/works/whack-a-mole/","tags":["作品集","個人作品"],"title":"【作品集】自製打地鼠機 第4代"},{"categories":["041 - 得獎作品"],"content":"iLoan 愛貸兒 2017上海銀行 - Fintech未來競技場 「滬港台金融科技創新Hackathon校園競賽」 兩岸三地FinTech創新大賽 （台灣區）全國第三名、台灣代表隊 （兩岸三地）優勝獎、最佳現場表現獎 作品連結 粉絲專頁 https://www.facebook.com/ILoan-愛貸兒-211685246039569/\n作品(messenger chatbot) demo影片 https://drive.google.com/file/d/1dy684UW6BwXHNSsjT5auKm06Vvri70q-/view\n","date":"2017-12-13T23:46:34+08:00","image":"https://wongwongnotes.com/images/restored/migrated/dsSbNdv.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/iloan/","tags":["作品集","優勝獎","全國第三名","台灣代表隊","得獎作品","最佳現場表現獎","競賽作品"],"title":"【得獎作品】 iLoan 愛貸兒"},{"categories":["932 - 閱讀筆記"],"content":"什麼是套牢? 所謂套牢，就是使用者必須付出相當的成本，才能完成品牌或技術的轉換。\nex:使用windows太久，突然要改用mac不習慣。\n為了改變這習慣，我們必須做出相當大的改變\n在資訊體系這個問題幾乎無可避免，成本的控制對買方與賣方同樣是一大挑戰。\n套牢的案例 貝爾公司 為了讓電話系統晉級到數位時代，投資一大筆金錢添購AT\u0026amp;T公司的數位轉換器(switch) 在當時，當然沒問題，這甚至讓他們脫癮而出。\n但是，誰想得到，這居然在未來成了一個非常嚴重的套牢問題\n因為AT\u0026amp;T作為轉換器，每當想連接新的周邊硬體時，都必須回去找AT\u0026amp;T升級，\n當他們如果想獅子大開口，你只有兩種選擇。\n- 直接被它們坑 - 把這家公司的東西全換了，但代價是你可能必須付出比1更大的成本 聯合電腦公司 某些仰賴IBM電腦的龐大資料庫公司，自然也成為重度套牢對象之一 在這個市場裡，消費者要承受系統與供應商的雙重套牢。\n消費者當然不願意輕易更換電腦操作系統→被IBM套牢\n其次他們也不會隨意更換系統管理軟體的廠商, 又一次被套牢。\n問題的關鍵 「轉換成本」是我們所重視的議題 大量生產市場的套牢問題 很多時候轉換成本雖小，對策略的成敗也有很大的影響。\n一個轉換成本是一億的大客戶 和 一億個轉換成本是一元的客戶相較，價值是一樣的。\n可靠的計算方式應該是：計算每一客戶的轉換成本占收益的比例，然後加總起來。\n評估客戶價值 所謂的轉換成本就是客戶被供應商套牢的程度,\n一個公司要爭取新顧客或制定現有顧客能接受的價格，都要參考轉換成本。\n反過來說，基本顧客群可以說是企業最珍貴的資產。\n我們顯而易見的是顧客的轉換成本\n但賣方爭取到新客戶時也必須要負擔一些成本\n顧客的成本與供應商的成本同等重要，兩者相加，才是單一顧客的總轉換成本\n常見套牢種類 1. 合約套牢 只能向單一供應商購買的合約 2. 耐久設備套牢 購買昂貴的耐久設備，再來添購搭配產品 3. 訓練套牢 人員訓練，對於某特定產品的依賴 4. 資訊與資料庫套牢 一種是資訊本身 (喪失資訊的成本?) 一種是資訊用以儲存的軟硬體 (有更好的儲存裝置出現，你要轉換的成本) 5. 專門供應商套牢 持續向單一廠商購買專門設備 6. 搜尋成本套牢 買賣雙方建立彼此關係的成本 (時間、心力) 7. 忠誠計畫套牢 吸引顧客持續消費提供的誘因 8. 供應商與合夥人的套牢 買賣雙方彼此套牢是一件常見的事情 套牢循環 品牌選擇 -\u0026gt; 試用期 -\u0026gt; 習慣期 -\u0026gt; 套牢 -\u0026gt; (回到品牌選擇)\n","date":"2017-09-21T21:37:18+08:00","permalink":"https://wongwongnotes.com/posts/os-misc/growth/reading/hold-up-problem/","tags":["閱讀筆記"],"title":"【閱讀筆記】《資訊經營法則》 ch5 - 套牢問題 閱讀心得"},{"categories":["041 - 得獎作品"],"content":":dog: SigmaGO 智慧導盲犬 :dog 2017台大黑客松 HackNTU 威盛電子-超級黑客獎(全國第一名) 作品 作品連結 https://hackmd.io/s/Skcy2I1L- 照片來源FB\n[embed]https://www.youtube.com/watch?v=ZKRa4QlMXMs[/embed]\n照片來源FB\n","date":"2017-07-23T23:58:24+08:00","image":"https://wongwongnotes.com/images/restored/migrated/lkLCNU9.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/sigmago/","tags":["作品集","全國第一名","得獎作品","競賽作品"],"title":"【得獎作品】SigmaGO 智慧導盲犬"},{"categories":["043 - 個人作品"],"content":"自製音樂遊戲機 作品FB連結\n[embed]https://www.youtube.com/watch?v=xjV8kjSKDI8[/embed]\n","date":"2017-01-18T00:38:46+08:00","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/works/music-machine/","tags":["作品集","個人作品"],"title":"【作品集】自製音樂遊戲機"},{"categories":["022 - 實況相關"],"content":"前言 這篇是 twitch nightbot 設定指令的筆記~\n以下為自己整理的詳細設定步驟。\n( nightbot 指令介面上的英文，我在圖上中文翻譯過了)\n裡面有一些是我與朋友之間的在那邊鬧的回憶XD\n將 nightbot 加入頻道 step1. 先去以下網址，按右上角log in 點這個網址：https://beta.nightbot.tv/ 應該會看到下面這張圖片，點「右上角log in」\nstep2. 選 twitch 那個 看到這個畫面選 twitch 那個。\n聰明的你，應該也能以此類推，youtube 也是這樣用的XD\nstep3. 按授權 (讓 nightbot 能管理頻道) 按下授權，就能讓 nightbot 能協助管理頻道。\n(剛好被大家發現我以前經營的 twitch 名稱惹 \u0026gt;_\u0026lt; )\nstep4. 按 join channel 這是在 nightbot 網頁介面中的最後一個步驟，\n按 join channel 讓他加入我們的頻道。\nstep5. 回到自己的 twitch 頻道 在對話框輸入 /mod nightbot nightbot 設定指令相關 方法一：如果是在頻道的話 (下面的這些指令只有台主或mod才能使用) 增加指令 !addcom (指令名) (指令內容) 注意空白↑ ↑ 已經存在的指令只能用!editcom，再用!addcom會不能用 修改指令 !editcom (指令名) (指令內容) 注意空白↑ ↑ 刪除指令 !delcom (指令名) (指令內容) 注意空白↑ ↑ 方法二：如果是在nightbot畫面的話 step1. 回到剛剛 nightbot 設定網頁的畫面，點左邊commands step2. 點custom 按右邊add command開始設定 step3. 痾\u0026hellip;.(圖好像太小? 下面有補大圖) 這邊照下面設定，設定完按submit就好了 補張大圖 至於修改跟刪除在這邊 (修改是左邊 、刪除是右邊) nightbot初期設定相關 nightbot一開始會亂ban人，\n因為他在保護你的台避免有垃圾留言，\n但其實有時候真的像是在亂ban\u0026hellip;\u0026hellip;需要把一些設定關掉\u0026hellip;\u0026hellip;\u0026hellip;\n進階 nightbot 設定教學 進階 nightbot 設定教學 - 使用參數、變數篇 指令參數 -cd=5 (冷卻時間5 ← 5秒最小) 範例： !addcom (指令) (指令反應) -cd=5 解釋：輸入「指令」 會顯示「指令反應」 cd時間為「五秒」 指令變數 $(count) 顯示的是數這個指令被執行多少次\n範例： !addcom 嗨 嗨嗨 $(count) 次\n解釋：輸入「嗨」 會顯示「 嗨嗨 N 次」\n$(user) 顯示是使用者名字\n範例： !addcom 87 $(user) 才是87\n解釋：JJ輸入「87」 會顯示「JJ 才是87」\n$(query) 顯示緊接的字串\n範例： !addcom 攻擊 打 $(query)\n解釋：輸入「攻擊 JJ」 會顯示「打 JJ」\n- 範本: !addcom !訂閱人數 $(twitch twitchid \"本台有{{subscriberCount}}人訂閱\") 進階 nightbot 設定教學 - 點歌篇 先去這邊檢查 !songs 有沒有 enabled，沒有的話，從按右邊把它enable起來~\n再來，前面你應該要先完成把nightbot加進你的台裡面了，\n這時候點這邊，進入自動撥放器的畫面，\n基本上這邊放著就會開始自己播歌了，再來只要在頻道輸入指令就可以了~\n常用指令：\n點歌：!songrequest +(youtube ID或網址) 切歌：!skipsong 查詢現在歌曲：!currentsong 查詢現在歌單：!songlists 進階 nightbot 設定教學 - 自己台的小電視篇 基本上就是用OBS的螢幕擷取就可以做到了ˊˇˋ\n有空再補圖QQ\n進階 nightbot 設定教學 - 內建指令篇 輸入 !winner，可以就目前有在看台的人抽出一位幸運兒\n","date":"2016-12-31T01:54:55+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/32.png","permalink":"https://wongwongnotes.com/posts/life-and-work/daily-life/022---%E5%AF%A6%E6%B3%81%E7%9B%B8%E9%97%9C/twitch-nightbot/","tags":["嗡嗡粗門玩"],"title":"【實況相關】twitch nightbot 設定指令教學 (內有完整圖片說明)"},{"categories":["041 - 得獎作品"],"content":"翻轉面試 2016 第四屆梅竹黑客松 104企業組 第一名 作品 《翻轉面試》，我們希望能夠改善傳統面試時，HR單方向的檢視應徵者，而使得應徵者的能力被縮小檢視的情況，我們提出一個解決方式，希望企業在面試之前先開出他們需要的人與條件(任務)，此任務即為將來應徵者若應徵成功時他們將負責的類似工作內容，對企業有興趣的應徵者可以在面試前先完成任務，同時企業能更精確的掌握他們需要的人才。\n我們的得獎作品為《翻轉面試》，我們希望能夠改善傳統面試時，HR單方向的檢視應徵者，而使得應徵者的能力被縮小檢視的情況，因此我們提出一個解決方式，希望企業在面試之前先開出他們需要的人與條件(任務)，此任務即為將來應徵者若應徵成功時他們將負責的類似工作內容，對企業有興趣的應徵者可以在面試前先完成任務，同時企業能更精確的掌握他們需要的人才。\n我們同時也期望能讓學生更理解企業需要的能力是什麼，試圖解決「學校所教的東西企業往往用不到」的問題，而且同時也能讓學生更有目標且有企劃性的去學習，企業也更能徵才徵到符合自己要求的學生。\n至104總公司的作品分享會，面對104的CEO、CTO、各部門高階主管 ＊ＦＢ心得連結＊\n","date":"2016-10-30T23:47:12+08:00","image":"https://wongwongnotes.com/images/restored/migrated/UWUVfU3.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/outerview/","tags":["作品集","全國第一名","得獎作品","競賽作品"],"title":"【得獎作品】翻轉面試 Outerview"},{"categories":["041 - 得獎作品"],"content":"反應小遊戲 v3.0 https://www.facebook.com/wongwong0916/posts/10205593852160492\nv3.0版本發布囉 =)\n修改震動煩人問題，\n還有顏色對比變明顯\n載點:https://goo.gl/Yax6T4\nv2.0 新增功能：將題目文字上色~XD\n載點：https://goo.gl/vQHgfl\n","date":"2016-08-07T00:57:33+08:00","image":"https://wongwongnotes.com/images/restored/2016/08/response-game.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/response-game/","tags":["Xamarin","作品集","得獎作品"],"title":"【作品集】反應小遊戲 v3.0"},{"categories":["041 - 得獎作品"],"content":"玩旅人 2016 第11屆戰國策全國創業競賽 學生餐旅組 全國第三名 Demo 影片 [embed]https://youtu.be/S3r_ptMPR7s[/embed]\n我們是一群常常有機會為家人朋友規劃旅程的人，但也同感旅程規劃時的耗時與不便，於是我們有了以地圖整合資訊的想法，讓旅客在地圖上就可以整合閱覽遊記與食記並同步規劃，讓旅程規劃變地直覺、簡單來改善旅程規劃的效率 部落客也可以直接以地圖分享資訊，有別於現存相關平台，希望以旅遊社群的概念，創造方便的第三方推薦旅遊 資訊平台，賦予旅遊景點嶄新價值。\n團隊簡介 本團隊雖然為學生身分，但團隊夥伴性質多元，專業涵蓋商管、資工、生命科學類學門；另外團隊合作默契佳，感情融洽，對於創新創意總能一起激盪出創新火花，團隊成員也都對團隊目標超級負責，是團隊合作典範的典範之一。\n今年團隊參加「第 11 屆戰國策創業大賽」，在 400 支隊伍中獲得餐旅組第三名，僅次於兩組餐旅專門學校的學生，初出茅驢讓我們振奮，也讓我們更堅定謹慎往前走。\n照片來源1\n照片來源2\n","date":"2016-05-20T00:15:03+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/now-you-go.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/041---%E5%BE%97%E7%8D%8E%E4%BD%9C%E5%93%81/now-you-go/","tags":["作品集","全國第三名","得獎作品","競賽作品"],"title":"【得獎作品】玩旅人 - 《Now，Go了沒》"},{"categories":["043 - 個人作品"],"content":"自製乒乓球小遊戲 「v3.2」\nhttp://www.codeskulptor.org/#user40_L582UIKpnL_10.py\n「v3.2」\n修正冰凍太準連續發球得分bug 新增文字提示 「v3.1」\nhttp://www.codeskulptor.org/#user40_L582UIKpnL_8.py\n「v3.1」\n場地變大 「v3.0」\nhttp://www.codeskulptor.org/#user40_L582UIKpnL_9.py\n「v3.0」\n龐大而緩慢的敵人，卻有能使出技能的潛力，\n這樣的敵人你是否能打倒它呢???\n1.新增會瞬間移動的點(紫色兩點會互相傳送)\n2.敵人每energy達到5會發動技能：mirror\n(效果：使玩家上下相反5秒鐘)\n「v2.0」\nhttp://www.codeskulptor.org/#user40_L582UIKpnL_2.py\n「v2.0」\n更強的敵人來襲！善用你的技能打倒他吧！\n1.新增energy系統，每切球一次可取得1點energy\n2.新增玩家技能：freeze\n消耗energy：5\n效果：冰凍對方5秒鐘(期間即使碰到仍會回彈)\n3.AI強化\n「v1.1」\nhttp://www.codeskulptor.org/#user40_QUCQLHCKKO_0.py\n「v1.0」\nhttp://www.codeskulptor.org/#user40_j7jnEsSTm3_2.py\n1.單人模式(與電腦pk)\n2.切球方向判定 依切球方向而改變球的方向(同時有殺球的速度效果)\n3.切球=殺球 速度*1.5倍\n3.1 擋球可恢復原速度\n3.2 再被切回來會再疊加倍率 最快50倍速(放心你玩不到這倍速的XD)\n4.新增：11分定勝負\n「v1.1」\nhttp://www.codeskulptor.org/#user40_QUCQLHCKKO_0.py\n去掉背景跑數據的版本\n","date":"2015-10-29T00:50:32+08:00","image":"https://wongwongnotes.com/images/restored/migrated/fcIdzCo.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/works/pingpong/","tags":["Python","作品集","個人作品"],"title":"【作品集】自製乒乓球小遊戲"},{"categories":["042 - 競賽作品"],"content":"智能紅綠燈 透過訊號控制，使得紅綠燈的等待時間更能有效地透過偵測動態變化。我使用arduino語言寫入Intel® Galileo 第 2 代開發板，使其能透過計算人數控制紅綠燈的時間長短。\n這份作品同時也受到教授的推薦，於2015年申請中華民國發明專利，此作品對於我資工領域的學習有著極大的肯定與意義。\n作品 Demo [embed]https://www.youtube.com/watch?v=to282YwfD6A\u0026amp;ab_channel=Howard[/embed]\n＊FB心得文＊\n","date":"2015-09-08T00:41:28+08:00","image":"https://wongwongnotes.com/images/restored/2020/10/traffic-light.jpg","permalink":"https://wongwongnotes.com/posts/life-and-work/portfolio/042---%E7%AB%B6%E8%B3%BD%E4%BD%9C%E5%93%81/traffic-light/","tags":["作品集","競賽作品"],"title":"【競賽作品】智能紅綠燈"}]