Skip to main content

Posts

Showing posts from March, 2017

[探索 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); 至於為何要用這種寫法呢, 網路上的  討論  頗多, 但參考  MSDN  的描述, 一言以蔽之, 就是「它會呼叫

[探索 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() 操作最快, 也就是上述 ReverseByArray() 版本, 比另外兩個版本快上