AB
- CD
-----
EF
+ GH
-----
PPP
有人寫 Code 去計答案出嚟。趁假期得閒,貪得意玩埋一份。屋企唔想開 Visual Studio, 就走咗去 https://dotnetfiddle.net/.
因為多過一個答案,Brute force 難免,問題只係點樣解決得靚啲。觀察所得,係 AB-CD=EF 同 EF+GH=PPP 嘅解法一樣,Divide and Conquer, 即係解到 EF+GH=PPP, 就基本上解咗題。所以得出第一個版本係咁:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
public class Program { | |
public static void Main() { | |
var level1 = Calc(111); | |
foreach (var i in level1) { | |
Console.WriteLine(string.Format("{0} + {1} = 111", i, 111 - i)); | |
} | |
} | |
public static List<int> Calc(int n) { | |
var result = new List<int>(); | |
for (int i = 10; i < 100; i++) { | |
int j = n - i; | |
var x = new HashSet<int>(); | |
x.Add(i / 10); | |
x.Add(i % 10); | |
x.Add(j / 10); | |
x.Add(j % 10); | |
if (x.Count == 4) { | |
result.Add(i); | |
} | |
} | |
return result; | |
} | |
} |
好快就可以意識到,只係 j 嘅計法唔同,加上
x.Add(n / 10);
x.Add(n % 10);
呢兩句太過累贅,於是就變成咁:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
public class Program { | |
public static void Main() { | |
var level1 = Calc(111, (n, i, x) => { | |
AddDigits(x, i); | |
AddDigits(x, n - i); | |
}, 4); | |
foreach (var i in level1) { | |
var level2 = Calc(i, (n, j, x) => { | |
AddDigits(x, j); | |
AddDigits(x, n + j); | |
AddDigits(x, i); | |
AddDigits(x, 111 - i); | |
}, 8); | |
foreach (var j in level2) { | |
Console.WriteLine(string.Format("{2} - {3} = {0} + {1} = 111", i, 111 - i, j, i + j)); | |
} | |
} | |
} | |
public static List<int> Calc(int n, Action<int, int, HashSet<int>> fn, int d) { | |
var result = new List<int>(); | |
for (int i = 10; i < 100; i++) { | |
var x = new HashSet<int>(); | |
fn(n, i, x); | |
if (x.Count == d) { | |
result.Add(i); | |
} | |
} | |
return result; | |
} | |
public static void AddDigits(HashSet<int> x, int n) { | |
x.Add(n / 10); | |
x.Add(n % 10); | |
} | |
} |
之後就見到擺入 HashSet 嗰嘅數字,只係作為 Validation 嘅準備,可以 Refactor 成一個 IsValid 嘅 Delegate; 然後再將 AddDigits() 簡化為 CountDigits():
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
public class Program { | |
public static void Main() { | |
var level1 = Calc(111, (n, i) => CountDigits(i, n - i) == 4); | |
foreach (var i in level1) { | |
var level2 = Calc(i, (n, j) => CountDigits(j, n + j, i, 111 - i) == 8 && n + j > 9 && n + j < 100); | |
foreach (var j in level2) { | |
Console.WriteLine(string.Format("{2} - {3} = {0} + {1} = 111", i, 111 - i, j, i + j)); | |
} | |
} | |
} | |
public static List<int> Calc(int n, Func<int, int, bool> isValid) { | |
var result = new List<int>(); | |
for (int i = 10; i < 100; i++) { | |
if (isValid(n, i)) { | |
result.Add(i); | |
} | |
} | |
return result; | |
} | |
public static int CountDigits(params int[] numbers) { | |
var x = new HashSet<int>(); | |
foreach (var n in numbers) { | |
x.Add(n / 10); | |
x.Add(n % 10); | |
} | |
return x.Count; | |
} | |
} |
睇住個 Calc(), 覺得 value 不大,因為只係一個 Loop 入面有個 if, 於是就直接將佢 Inline 入個 Main() 度; CountDigits() 用 LINQ 一句 KO 埋:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
public class Program { | |
public static void Main() { | |
for (int i = 10; i < 100; i++) { | |
if (CountDigits(i, 111 - i) == 4) { | |
for (int j = 10; j < 100; j++) { | |
if (CountDigits(j, i + j, i, 111 - i) == 8 && i + j > 9 && i + j < 100) { | |
Console.WriteLine(string.Format("{2} - {3} = {0} + {1} = 111", i, 111 - i, j, i + j)); | |
} | |
} | |
} | |
} | |
} | |
public static int CountDigits(params int[] numbers) { | |
return numbers.SelectMany(o => new int[]{o / 10, o % 10}).Distinct().Count(); | |
/* | |
var x = new HashSet<int>(); | |
foreach (var n in numbers) { | |
x.Add(n / 10); | |
x.Add(n % 10); | |
} | |
return x.Count; | |
*/ | |
} | |
} |
最後,想用最少嘅 Code 做嘢; 掉走埋 CountDigits(); 改返 ab, cd, ef, gh, ppp 做 variable 名; 執走第一個 if; fix 埋 p = 1 呢個已知數:
再貪玩少少,用 LINQ 代替兩個 Loop:
或者好多人會覺得寫一大段 Code 去解決一條小學生嘅題目係無聊,唔夠 Cost Effective. 但係每個 Software Engineer, 都要有解決問題嘅熱誠。呢個 Exercise, 由一開始 Divide and Conquer 嘅想法,Refactor 成一條 LINQ 攪掂,係完全意想不到。可能做唔到傳說中嘅 Python 咁5行攪掂 (到呢一刻都未搵到個傳說响邊),但其實 C# 一啲都唔失禮。Ceremony vs Essence 嚟講,C# 都算係咁 (斜視 Java 中)。
大慨,呢個世界上有多過一個方法去解決問題,而大家都想搵最好嘅方法,最好可以係最快、最少 Code, 最 readable, 最 reusable, 最 maintainable 等等,無絕對標準的 (最快都重要分寫得快同行得快),响各方面達致平衡,就係 Software Engineering 引人入勝嘅地方。
既然平時返工要趕 Project 無時間要左抄右抄,放假有時間偶爾寫吓完全屬於自己認為最好嘅 Code, 何樂而不為?
P.S. 有無人識將呢句 Fluent style 嘅 LINQ 變成 Query style: new int[]{ef, gh, ab, cd}.SelectMany(o => new int[]{ o / 10, o % 10}).Distinct().Where(o => o != 1)