Skip to main content

Posts

[探索 3 分鐘] 判斷字串內容是否是全數字或全英文, 效率型做法 (c#)

Question : [密碼欄位檢查] 內容不可全數字或全英文, 範例輸出入如下 (input → output)。 123213123123 → false (因為全數字) adfdsbrr → false (因為全英文) abc123 → true 看似簡單, 但條件後來加了一個, 不可用 regex。以及有個小陷阱, 就是 : 不是去檢查英文或數字, 而是不能清一色英文或數字; 這個有了解嗎 ? 如果單純跑迴圈去檢查英文或數字, 那就誤會了哦。一開始沒想太多也不知道好用的函式, 但覺得 ASCII 滿適合上陣的, 先丟兩個版本。 public static bool ASCIIIsStrongPassword(string s) { s = s.ToLower(); long i; if (long.TryParse(s, out i)) return false; //全數字NG if (s.Any(c => c < 'a' || c > 'z')) return true; //擁有一個非英文就pass return false; } public static bool XORIsStrongPassword(string s) { s = s.ToLower(); bool isDigit = false; bool isLetter = false; for (var i = 0; i < s.Length; ++i) { if (s[i] >= '0' && s[i] <= '9') isDigit = true; else if (s[i] >= 'a' && s[i] <= 'z') isLetter = true; if (isDigit && isLetter) return true; } return !isDigit && !isLetter; } 這兩個算創意版...

[探索 3 分鐘] 檢查字串中最多相同數字的最大數量, 效率型做法 (c#)

Question : 有個固定長度 4 的字串, 內容為數字, 需判斷字串中最多相同數字的最大數量, 有點饒舌, 範例輸出入如下 (input → output)。 1314 → 2 (因為 2 個 1) 8888 → 4 (因為 4 個 8) 5566 → 2 (因為 2 個 5 或 2 個 6) 就是這麼簡單。當下是想到暴力法, 畢竟只有 4 個數字...(其實也沒這麼暴力, 隱含著 divide-and-conquer 的概念)。 public static int MaxSameNumbers(string s) { char[] foo = s.ToArray(); Array.Sort(foo); s = new string(foo); var l = s.Substring(0, 2); var r = s.Substring(2); int countL = 1, countR = 1; if (l[0] == l[1]) countL = 2; if (r[0] == r[1]) countR = 2; if (l[1] == r[0]) return countL + countR; else return Math.Max(countL, countR); } 有點囉嗦, 需要先針對字串排序, 如此一來, 左半邊最多 2 個連號, 右半邊也是, 也就是各邊的最大值都在 1 ~ 2 ; 而最後只需要額外再確認是不是中間相連的數字竟也相同。要注意題目不是都 5566, 1314 這種, 還會有 9555, 8888 這種的, 所以解法呢 ? 針對這種中間有相同數字的 case, 把兩邊的最大值加起來即可。但由於真的太囉嗦了, 換一版字典的寫法。 public static int LinqMaxSameNumbers(string s) { var d = new Dictionary<char, int>(); for(var i = 0; i < s.Length; ++i) { if (d.ContainsKey(s[i])) ++d[s[i]]...

[探索 3 分鐘] 檢查浮點數是否恰為整數, 效率型做法 (c#)

Question : 判斷一個浮點數輸入值, 是不是剛好是整數, 範例輸出入如下 (input → output)。 11.112 → false 11.000 → true  就是這麼簡單。想到兩招, 一個用 Math 來計算, 如果無條件捨去跟無條件進位答案相同, 那肯定本身就是整數; 另一招是用安全的 TryParse 來判斷是否為 float。 public static bool IsIntegerByMath(float f) { return Math.Ceiling(f) == Math.Floor(f); } public static bool IsIntegerByParse(float f) { int i; return int.TryParse(f.ToString(), out i); } 如此一來, 程式會動了, 考試應該也有過。但若要追求簡潔或效能的極致, 還有以下的做法。 public static bool IsIntegerByMode(float f) { return f % 1 == 0; } public static bool IsIntegerFloor(float f) { return f == Math.Floor(f); } 網路上的這兩個版本, 直接理解 C# 語言對於浮點數與整數的相互操作, 寫出更高效的寫法。而上面的執行效率各是如何 ? 未測先估 TryParse 是最慢的, 但最快的版本究竟快在哪裡 ? 跑個 1,000 萬次來試試。 答案揭曉, Math 類操作最快, 也就是上述 IsIntegerFloor() 版本; 扣除掉本來就會 BG 的 IsIntegerByParse() 版本, 雖然比剩餘兩個版本快, 但差異幾乎可以忽略。翻 Math.Floor 原始碼 , 原來又是個的 native 函式。 [MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern double Floor(double d); 至於為何要用這種寫法呢, 網路上的  討論  頗多, 但參考  M...

[探索 3 分鐘] 將字串內容反轉輸出, 效率型做法 (c#)

Question : 將字串內容反轉, 範例輸出入如下 (input → output)。 abc → cba  123 → 321 就是這麼簡單。一開始也沒想太多, 就是跑一個迴圈, 從尾巴開始讀每個字元, 用字串累加到頭就結束了。而且沒有遺忘連續字串相加要使用 StringBuilder, 因為效能考量。 public string ReverseByStringBuilder(string s) { var sb = new StringBuilder(); for (var i = s.Length - 1; i >= 0; --i) sb.Append(s[i]); return sb.ToString(); } 另一個版本行數差不多, 但使用 CharArray, 然後又用到  Array.Reverse  方法。 public string ReverseByArray(string s) { char[] charArray = s.ToCharArray(); Array.Reverse(charArray); return new string(charArray); } 也查到  這一版 , 直接用另外一個陣列, 把原來的值拷貝過來, 頭放尾, 尾放頭, 就少了 StringBuilder 類別的開銷了。 public string ReverseByCharBuffer(string s) { char[] c = s.ToCharArray(); for (int i = 0; i < s.Length / 2; ++i) { char t = s[i]; c[i] = s[s.Length - i - 1]; c[s.Length - i - 1] = t; } return new string(c); } 現在好奇兩件事, 上面的執行效率各是如何 ? 最快的版本究竟快在哪裡 ? 跑個 100 萬次來試試。 結果如您想像嗎 ? Array.Reverse() 操作最快, 也就是上述 Re...

[探索 5 分鐘] i++ 與 ++i 的功能與性能是否相同 ? 用程式範例介紹 (c#)

值相同, 但我們知道這兩個 Foo 操作是不同的 Foo( x++ ); // post-increment Foo( ++x ); // pre-increment 大家比較常用的 i++, x++... 我們稱為 post-increment, 是在這一行程式完成之後, 再進行 +1 的動作, 反之稱之為 pre-increment。所以其實上面例子可以理解為如下 (功能上, 非編譯器行為), 更囉嗦的版本。 // Foo ( x++ ) Foo( x ); x = x + 1; // Foo ( ++x ) x = x + 1; Foo( x ); wiki  的例子更清楚, 請特別注意 y 的值。 int x, y; // Increment operators x = 1; y = ++x; // x is now 2, y is also 2 y = x++; // x is now 3, y is 2 這樣看完應該更無懸念了, post-increment 不是等號左邊跑完進行 ++, 也不是等號右邊跑完進行 ++, 就是這一行整個跑完才進行 ++。 // Copy one array to another void copy_array(float *src, float *dst, int n) { while (n-- > 0) // Loop that counts down from n to zero *dst++ = *src++; // Copies element *(src) to *(dst), // then increments both pointers } 以上是說明功能上的不同; 在場景上, 陣列或指標的操作很適合 post-increment, 有「同一行, 同時完成操作並與結束往下一個位置移動, 的簡潔代碼」設計 。性能呢 ? Foo( ++x ) 跟 Foo ( x++ ) 各跑 1 千萬次, 結果是 Foo( ++x ) 這個 pre-increment 呼叫版本性能穩定領先, 非常微小的領先。 附帶一提, 就單一行性能來比較...

[探索 3 分鐘] 用 JavaScript 與 css 客製化部落格 blogger

用 Google 的 blogger 撰寫技術日誌並夾帶程式碼, 需要動一些手腳, 這邊記錄我的設定: 程式碼的保留字顏色 css 設定 引用文字的 css 設定 基本上在 Blogger > Layout > Sidebar > + > HTML / JavaScript Gadget > 輸入自定義的 <style> 與<script> 即可。 客製化「程式碼」區塊樣式 接著會希望編輯文章時遇到特別要 Highlight 程式碼的地方, 把程式碼區段給夾起來, 做到簡單易用又便於閱讀、美觀。 run_prettify.js 版本 把程式夾在 <pre class="prettyprint">...</pre> 或 <code class="prettyprint">...</code> 就會自動被美化。 <pre><code class="prettyprint"> // Your Code </code></pre> 其他使用方法請參考  usage 。 highlight.js 版本 (推薦) <pre><code class="cs"> public ActionResult Index() { return View(); } </code></pre> 其他使用方法請參考 usage 。 客製化「引言」區塊樣式  預設的「引用」長相實在沒什麼特色, 所以也調整一下, 純粹個人偏好喜歡「框」起來的感覺。 程式筆記 只要在文頭所指示的位置, 放入以下的腳本內容, 儲存版型後重新檢視 post 文, 沒問題的話 style 就改為新版了。注意一下程式碼 style 須依賴.js 與 .css 檔案, 也請各自夾帶在 <script /> 與 <style /> 區塊中。 引言 style <style> blockquote { @import url(http://fo...

[探索 5 分鐘] Data Recovery Utility 工具 TestDisk 使用心得

今天遇到一個令人崩潰的問題, Cannon 相機中的相片不小心被砍了, 大約有 1,000 多張 !! 對於非常重視「回憶」的我來說是無法接受的, 雖然「人不該活在過去」... Anyway, 還好找到一個   TestDisk  軟體救回來了, 是免費開源的軟體, 因為這類工具通常都要付費, 遇到的機率不高, 但一遇到通常急到都會想花錢趕快救回來。所以趕快來分享這個拯救過程, 因為沒有 GUI, 會截命令列的圖給同學參考。 TestDisk 這個 Data Recovery Utility 名稱為 TestDisk, 我當初用的版本是 6.14 (目前最新是 7.1 )。 請直接下載最新版本  7.1 , unzip 後可得到 testdisk_win.exe 主程式。然後一路往下操作, 我選擇選了 SD 卡所在磁碟 G: G: > [Proceed]  Intel/PC Partition > [Proceed]  [Advanced | Filesystem Utils]  > [Proceed]  FAT 32 LBA -> [Undelete] Select a media 選擇磁碟機 Select the partition table type 選擇檔案格式 Advanced Filesystem Utils 選擇進階, 因為有指定的檔案來源與目標資料夾要輸入 Undelete 確定磁區 Choose source folder  確定被刪除的檔案來源, 通常 Cannon 相機照片會存在這目錄 ~/DCIM/100CANON Choose destination folder  把準備要救回來的相片存到指定的資料夾, 如 C:/backup, 確認後輸入大寫 C (小寫 c 無效哦 ) Recovering 拷貝中, 敬請期待 Magic !! 最後 1,000 多張照片就通通救回來, 已抱緊大哭了 (是因為感動)。希望可以幫助到需要的朋友。 參考資料 http://www.cgsecurity.org/wi...