【伯樂線上導讀:】新加坡總理李顯龍兩週前在一個創業者論壇上透露,他最後編寫的一個程式是解決數獨問題的程式(Sudoku solver),還是多年前用 C++ 實現的。李顯龍今天在個人 Facebook 主頁公佈了程式原始碼(的 Google Drive 連結)。
(資料圖:李顯龍)
不方便翻梯子的朋友,直接看下面的原始碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
#include "stdio.h" int InBlock[81], InRow[81], InCol[81]; const int BLANK = 0; const int ONES = 0x3fe; // Binary 1111111110 int Entry[81]; // Records entries 1-9 in the grid, as the corresponding bit set to 1 int Block[9], Row[9], Col[9]; // Each int is a 9-bit array int SeqPtr = 0; int Sequence[81]; int Count = 0; int LevelCount[81]; void SwapSeqEntries(int S1, int S2) { int temp = Sequence[S2]; Sequence[S2] = Sequence[S1]; Sequence[S1] = temp; } void InitEntry(int i, int j, int val) { int Square = 9 * i + j; int valbit = 1 << val; int SeqPtr2; // add suitable checks for data consistency Entry[Square] = valbit; Block[InBlock[Square]] &= ~valbit; Col[InCol[Square]] &= ~valbit; // Simpler Col[j] &= ~valbit; Row[InRow[Square]] &= ~valbit; // Simpler Row[i] &= ~valbit; SeqPtr2 = SeqPtr; while (SeqPtr2 < 81 && Sequence[SeqPtr2] != Square) SeqPtr2++ ; SwapSeqEntries(SeqPtr, SeqPtr2); SeqPtr++; } void PrintArray() { int i, j, valbit, val, Square; char ch; Square = 0; for (i = 0; i < 9; i++) { if (i % 3 == 0) putc('\n', stdout); for (j = 0; j < 9; j++) { if (j % 3 == 0) putc(' ', stdout); valbit = Entry[Square++]; if (valbit == 0) ch = '-'; else { for (val = 1; val <= 9; val++) if (valbit == (1 << val)) { ch = '0' + val; break; } } putc(ch,stdout); } putc ('\n', stdout); } } void ConsoleInput() { int i, j; char InputString[80]; for (i = 0; i < 9; i++) { printf("Row[%d] : ", i + 1); scanf("%s", InputString); for (j = 0; j < 9; j++) { char ch = InputString[j]; if (ch >= '1' && ch <= '9') InitEntry(i, j, ch - '0'); } } PrintArray(); } void PrintStats() { int i, j, S; printf("\nLevel Counts:\n\n"); S = 0; while (LevelCount[S] == 0) S++; i = 0; while (S < 81) { int Seq = Sequence[S]; printf("(%d, %d):%4d ", Seq / 9 + 1, Seq % 9 + 1, LevelCount[S]); if (i++ > 4){ printf("\n"); i = 0; } S++; } printf("\n\nCount = %d\n", Count); } void Succeed() { PrintArray(); PrintStats(); } int NextSeq(int S) { int S2, Square, Possibles, BitCount; int T, MinBitCount = 100; for (T = S; T < 81; T++) { Square = Sequence[T]; Possibles = Block[InBlock[Square]] & Row[InRow[Square]] & Col[InCol[Square]]; BitCount = 0; while (Possibles) { Possibles &= ~(Possibles & -Possibles); BitCount++; } if (BitCount < MinBitCount) { MinBitCount = BitCount; S2 = T; } } return S2; } void Place(int S) { LevelCount[S]++; Count++; if (S >= 81) { Succeed(); return; } int S2 = NextSeq(S); SwapSeqEntries(S, S2); int Square = Sequence[S]; int BlockIndex = InBlock[Square], RowIndex = InRow[Square], ColIndex = InCol[Square]; int Possibles = Block[BlockIndex] & Row[RowIndex] & Col[ColIndex]; while (Possibles) { int valbit = Possibles & (-Possibles); // Lowest 1 bit in Possibles Possibles &= ~valbit; Entry[Square] = valbit; Block[BlockIndex] &= ~valbit; Row[RowIndex] &= ~valbit; Col[ColIndex] &= ~valbit; Place(S + 1); Entry[Square] = BLANK; // Could be moved out of the loop Block[BlockIndex] |= valbit; Row[RowIndex] |= valbit; Col[ColIndex] |= valbit; } SwapSeqEntries(S, S2); } int main(int argc, char* argv[]) { int i, j, Square; for (i = 0; i < 9; i++) for (j = 0; j < 9; j++) { Square = 9 * i + j; InRow[Square] = i; InCol[Square] = j; InBlock[Square] = (i / 3) * 3 + ( j / 3); } for (Square = 0; Square < 81; Square++) { Sequence[Square] = Square; Entry[Square] = BLANK; LevelCount[Square] = 0; } for (i = 0; i < 9; i++) Block[i] = Row[i] = Col[i] = ONES; ConsoleInput(); Place(SeqPtr); printf("\n\nTotal Count = %d\n", Count); return 0; } |
李顯龍說:
這個程式非常基礎,在 DOS 視窗下執行。按行輸入資料(比如:1-3-8—6),程式會列印出解決方案(如果有多種方案,則輸出所有的)、程式所用的步數,還有一些搜尋資料。
該程式做了回溯搜尋,選擇下一個扇出(fanout)最小的單元格。
李顯龍給讀原始碼的朋友留了一個問題:如果 X 是一個(二進位制)整數,那 (x & -x) 怎麼計算的呢?
如果各位發現了他 Bug,可給他留言!
程式執行的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
C:\CPP>sudoku2 Row[1] : --------- Row[2] : -----3-85 Row[3] : --1-2---- Row[4] : ---5-7--- Row[5] : --4---1-- Row[6] : -9------- Row[7] : 5------73 Row[8] : --2-1---- Row[9] : ----4---9 --- --- --- --- --3 -85 --1 -2- --- --- 5-7 --- --4 --- 1-- -9- --- --- 5-- --- -73 --2 -1- --- --- -4- --9 987 654 321 246 173 985 351 928 746 128 537 694 634 892 157 795 461 832 519 286 473 472 319 568 863 745 219 Level Counts: (3, 9): 1 (8, 9): 2 (4, 9): 4 (5, 9): 8 (6, 9): 15 (1, 9): 23 (3, 8): 33 (8, 8): 46 (4, 8): 79 (1, 8): 119 (5, 8): 182 (6, 8): 250 (9, 8): 287 (4, 7): 347 (6, 7): 478 (6, 5): 588 (6, 1): 732 (6, 3): 828 (6, 4): 862 (6, 6): 895 (4, 5): 795 (4, 3): 761 (5, 5): 843 (7, 5): 829 (2, 5): 616 (1, 5): 594 (2, 7): 543 (2, 3): 565 (7, 3): 551 (2, 4): 577 (3, 6): 565 (3, 4): 590 (1, 4): 572 (1, 6): 595 (7, 4): 612 (5, 4): 576 (7, 6): 476 (7, 7): 389 (7, 2): 262 (4, 2): 184 (2, 2): 140 (2, 1): 95 (5, 6): 56 (8, 7): 34 (8, 6): 18 (3, 1): 13 (8, 1): 10 (3, 7): 7 (3, 2): 8 (1, 7): 10 (5, 1): 10 (5, 2): 6 (8, 2): 4 (8, 4): 2 (9, 1): 1 (1, 1): 1 (9, 2): 1 (9, 3): 1 (9, 4): 1 (4, 1): 1 (9, 6): 1 (9, 7): 1 (1, 3): 1 (1, 2): 1 Count = 17698 Total Count = 24395 C:\CPP> |
李顯龍後來更新的小提示:
As several of you noted, (x & –x) returns the least significant ‘1’ bit of x, i.e. the highest power of two that divides x. This assumes two’s complement notation for negative numbers, as some of you also pointed out. e.g. if x=12 (binary 1100), then (x & -x) = 4 (binary 100). I didn’t invent this; it is an old programming trick.
下面摘錄一些網友評論
EgliWei:程式設計師當中最懂政治的,懂政治中最會寫程式的,程式設計師中地位最高的。
2gua:滿滿的C++技術情懷..
zl1030:大體掃了幾眼總理的演算法,9行9列9塊都是位運算,用到了遞迴,很精巧,值得研究!
sinox2010p1:劍橋大學電腦科學,科班出身。程式碼寫得好,英語單詞,可讀性強。只是用s,s2做變數似乎跟其他單詞變數有點矛盾。這個人要是不做總理開軟體公司可能也很成功。
一位來自劍橋大學的教授是這麼評價李顯龍的:
他是真正優秀的,遠遠超出其他學生。我認為他涉足電腦科學(數學之後)主要是因為他的父親(李光耀)不想讓他呆在純數學的世界裡。他不但刻苦、認真、專業,而且懂得去創新。所有跡象都表明他會是一個世界級的數學家。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式