Advertisement
Guest User

Untitled

a guest
Aug 17th, 2019
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.28 KB | None | 0 0
  1. [TestMethod]
  2. public void Solve()
  3. {
  4. var board = new char[,] {
  5. {'5','3','.','.','7','.','.','.','.'},
  6. {'6','.','.','1','9','5','.','.','.'},
  7. {'.','9','8','.','.','.','.','6','.'},
  8. {'8','.','.','.','6','.','.','.','3'},
  9. {'4','.','.','8','.','3','.','.','1'},
  10. {'7','.','.','.','2','.','.','.','6'},
  11. {'.','6','.','.','.','.','2','8','.'},
  12. {'.','.','.','4','1','9','.','.','5'},
  13. {'.','.','.','.','8','.','.','7','9'}
  14. };
  15.  
  16. var expected = new char[,] {
  17. {'5','3','4','6','7','8','9','1','2'},
  18. {'6','7','2','1','9','5','3','4','8'},
  19. {'1','9','8','3','4','2','5','6','7'},
  20. {'8','5','9','7','6','1','4','2','3'},
  21. {'4','2','6','8','5','3','7','9','1'},
  22. {'7','1','3','9','2','4','8','5','6'},
  23. {'9','6','1','5','3','7','2','8','4'},
  24. {'2','8','7','4','1','9','6','3','5'},
  25. {'3','4','5','2','8','6','1','7','9'}
  26. };
  27.  
  28. var sudoku = new Sudoku();
  29. sudoku.Solve(board);
  30.  
  31. CollectionAssert.AreEqual(expected, board);
  32. }
  33.  
  34. public class SolverOptions
  35. {
  36. public int MaxRecursion { get; set; } = -1;
  37. public int MaxSolutions { get; set; } = -1;
  38. public bool IncludeCluesInSolution = false;
  39.  
  40. public bool HasRecursionLevelExceeded(int recursionLevel)
  41. {
  42. return MaxRecursion > -1 && recursionLevel > MaxRecursion;
  43. }
  44.  
  45. public bool HasSolutionsExceeded(IEnumerable<ISet<int>> solutions)
  46. {
  47. return MaxSolutions > -1 && solutions.Count() >= MaxSolutions;
  48. }
  49. }
  50.  
  51. public interface ICSPSolver
  52. {
  53. IReadOnlyCollection<ISet<int>> Solve(ExactCover problem, SolverOptions options);
  54. }
  55.  
  56. public class ExactCover
  57. {
  58. public ISet<int> Constraints { get; }
  59. public IDictionary<int, ISet<int>> Sets { get; }
  60. public ISet<int> Clues { get; }
  61.  
  62. public ExactCover(ISet<int> constraints, IDictionary<int, ISet<int>> sets, ISet<int> clues)
  63. {
  64. Constraints = constraints;
  65. Sets = sets;
  66. Clues = clues;
  67. }
  68.  
  69. public IReadOnlyCollection<ISet<int>> Solve(ICSPSolver solver, SolverOptions options)
  70. {
  71. return solver.Solve(this, options);
  72. }
  73. }
  74.  
  75. class DLXNode
  76. {
  77. internal DLXNode header, row;
  78. internal DLXNode up, down, left, right;
  79. internal int constraint, set, rowCount;
  80.  
  81. internal DLXNode() => up = down = left = right = header = row = this;
  82.  
  83. internal bool IsLast => right == this;
  84.  
  85. internal void AddLast(DLXNode node) => row.left.Append(node);
  86.  
  87. internal void AddLastDown(DLXNode node) => header.up.AppendDown(node);
  88.  
  89. internal void Append(DLXNode node)
  90. {
  91. right.left = node;
  92. node.right = right;
  93. node.left = this;
  94. right = node;
  95. }
  96.  
  97. internal void AppendDown(DLXNode node)
  98. {
  99. down.up = node;
  100. node.down = down;
  101. node.up = this;
  102. down = node;
  103. header.rowCount++;
  104. }
  105.  
  106. internal IEnumerable<DLXNode> Iterate(Func<DLXNode, DLXNode> direction)
  107. {
  108. var node = this;
  109. do
  110. {
  111. yield return node;
  112. node = direction(node);
  113.  
  114. } while (node != this);
  115. }
  116.  
  117. public override string ToString()
  118. {
  119. var isHeader = header == this;
  120. var isRow = row == this;
  121. var isRoot = isHeader && isRow;
  122.  
  123. return isRoot ? "R"
  124. : isHeader ? $"H{header.constraint}"
  125. : isRow ? $"R{row.set}"
  126. : $"C({header.constraint},{row.set})";
  127. }
  128. }
  129.  
  130. public class DLX : ICSPSolver
  131. {
  132. public IReadOnlyCollection<ISet<int>> Solve(ExactCover problem, SolverOptions options)
  133. {
  134. var root = Parse(problem);
  135. var solutions = new List<ISet<int>>();
  136. var currentSolution = new Stack<int>();
  137. var recursionLevel = 0;
  138.  
  139. Explore(root, solutions, currentSolution, problem.Clues, recursionLevel, options);
  140.  
  141. return solutions.AsReadOnly();
  142. }
  143.  
  144. internal bool CheckForSolution(
  145. DLXNode root,
  146. IList<ISet<int>> solutions,
  147. Stack<int> currentSolution,
  148. ISet<int> clues,
  149. int recursionLevel,
  150. SolverOptions options)
  151. {
  152. if (root.IsLast
  153. || options.HasRecursionLevelExceeded(recursionLevel)
  154. || options.HasSolutionsExceeded(solutions))
  155. {
  156. if (root.IsLast)
  157. {
  158. var solution = new HashSet<int>(currentSolution);
  159. if (options.IncludeCluesInSolution)
  160. {
  161. foreach (var clue in clues)
  162. {
  163. solution.Add(clue);
  164. }
  165. }
  166. solutions.Add(solution);
  167. }
  168.  
  169. return true;
  170. }
  171.  
  172. return false;
  173. }
  174.  
  175. internal DLXNode GetHeaderWithMinimumRowCount(DLXNode root)
  176. {
  177. DLXNode next = null;
  178.  
  179. foreach (var header in root.Iterate(n => n.right).Skip(1))
  180. {
  181. if (next == null || header.rowCount < next.rowCount)
  182. {
  183. next = header;
  184. }
  185. }
  186.  
  187. return next;
  188. }
  189.  
  190. internal void Explore(
  191. DLXNode root,
  192. IList<ISet<int>> solutions,
  193. Stack<int> currentSolution,
  194. ISet<int> clues,
  195. int recursionLevel,
  196. SolverOptions options)
  197. {
  198. if (CheckForSolution(
  199. root, solutions, currentSolution, clues, recursionLevel, options))
  200. {
  201. return;
  202. }
  203.  
  204. var header = GetHeaderWithMinimumRowCount(root);
  205.  
  206. if (header.rowCount <= 0)
  207. {
  208. return;
  209. }
  210.  
  211. Cover(header);
  212.  
  213. foreach (var row in header.Iterate(n => n.down).Skip(1))
  214. {
  215. currentSolution.Push(row.row.set);
  216. foreach (var rightNode in row.Iterate(n => n.right).Skip(1))
  217. {
  218. Cover(rightNode);
  219. }
  220. Explore(root, solutions, currentSolution, clues, recursionLevel + 1, options);
  221. foreach (var leftNode in row.Iterate(n => n.left).Skip(1))
  222. {
  223. Uncover(leftNode);
  224. }
  225. currentSolution.Pop();
  226. }
  227.  
  228. Uncover(header);
  229. }
  230.  
  231. internal void Cover(DLXNode node)
  232. {
  233. if (node.row == node) return;
  234.  
  235. var header = node.header;
  236. header.right.left = header.left;
  237. header.left.right = header.right;
  238.  
  239. foreach (var row in header.Iterate(n => n.down).Skip(1))
  240. {
  241. foreach (var rightNode in row.Iterate(n => n.right).Skip(1))
  242. {
  243. rightNode.up.down = rightNode.down;
  244. rightNode.down.up = rightNode.up;
  245. rightNode.header.rowCount--;
  246. }
  247. }
  248. }
  249.  
  250. internal void Uncover(DLXNode node)
  251. {
  252. if (node.row == node) return;
  253.  
  254. var header = node.header;
  255.  
  256. foreach (var row in header.Iterate(n => n.up).Skip(1))
  257. {
  258. foreach (var leftNode in row.Iterate(n => n.left).Skip(1))
  259. {
  260. leftNode.up.down = leftNode;
  261. leftNode.down.up = leftNode;
  262. leftNode.header.rowCount++;
  263. }
  264. }
  265.  
  266. header.right.left = header;
  267. header.left.right = header;
  268. }
  269.  
  270. internal DLXNode Parse(ExactCover problem)
  271. {
  272. var root = new DLXNode();
  273. var headerLookup = new Dictionary<int, DLXNode>();
  274. var rowLookup = new Dictionary<int, DLXNode>();
  275. var givens = new HashSet<int>(problem.Clues
  276. .SelectMany(x => problem.Sets[x]).Distinct());
  277.  
  278. foreach (var constraint in problem.Constraints.Where(x => !givens.Contains(x)))
  279. {
  280. var header = new DLXNode { constraint = constraint, row = root };
  281. headerLookup.Add(constraint, header);
  282. root.AddLast(header);
  283. }
  284.  
  285. foreach (var set in problem.Sets.Where(x => !x.Value.Any(y => givens.Contains(y))))
  286. {
  287. var row = new DLXNode { set = set.Key, header = root };
  288. rowLookup.Add(set.Key, row);
  289. root.AddLastDown(row);
  290.  
  291. foreach (var element in set.Value)
  292. {
  293. if (headerLookup.TryGetValue(element, out var header))
  294. {
  295. var cell = new DLXNode { row = row, header = header };
  296. row.AddLast(cell);
  297. header.AddLastDown(cell);
  298. }
  299. }
  300. }
  301.  
  302. return root;
  303. }
  304. }
  305.  
  306. [TestMethod]
  307. public void ManySolutions()
  308. {
  309. var problem = new ExactCover(
  310. new HashSet<int> { 1, 2, 3 },
  311. new Dictionary<int, ISet<int>> {
  312. { 0, new HashSet<int> { 1 } }
  313. , { 1, new HashSet<int> { 2 } }
  314. , { 2, new HashSet<int> { 3 } }
  315. , { 3, new HashSet<int> { 2, 3 } }
  316. , { 4, new HashSet<int> { 1, 2 } }
  317. },
  318. new HashSet<int>());
  319.  
  320. var solutions = problem.Solve(
  321. new DLX(),
  322. new SolverOptions());
  323.  
  324. var printed = Print(problem, solutions);
  325.  
  326. AssertAreEqual(@"
  327. Constraints: {1, 2, 3}
  328. Set 0: {1}
  329. Set 1: {2}
  330. Set 2: {3}
  331. Set 3: {2, 3}
  332. Set 4: {1, 2}
  333. Solutions: 3
  334. Solution #1: {1}, {2}, {3}
  335. Solution #2: {1}, {2, 3}
  336. Solution #3: {3}, {1, 2}", printed);
  337. }
  338.  
  339. [TestMethod]
  340. public void ManySolutionsWithClues()
  341. {
  342. var problem = new ExactCover(
  343. new HashSet<int> { 1, 2, 3 },
  344. new Dictionary<int, ISet<int>> {
  345. { 0, new HashSet<int> { 1 } }
  346. , { 1, new HashSet<int> { 2 } }
  347. , { 2, new HashSet<int> { 3 } }
  348. , { 3, new HashSet<int> { 2, 3 } }
  349. , { 4, new HashSet<int> { 1, 2 } }
  350. },
  351. new HashSet<int> { 2 });
  352.  
  353. var solutions = problem.Solve(
  354. new DLX(),
  355. new SolverOptions() { IncludeCluesInSolution = true });
  356.  
  357. var printed = Print(problem, solutions);
  358.  
  359. AssertAreEqual(@"
  360. Constraints: {1, 2, 3}
  361. Set 0: {1}
  362. Set 1: {2}
  363. Set 2: {3} [Clue]
  364. Set 3: {2, 3}
  365. Set 4: {1, 2}
  366. Solutions: 2
  367. Solution #1: {1}, {2}, {3}
  368. Solution #2: {3}, {1, 2}", printed);
  369. }
  370.  
  371. string Print(ExactCover problem, IReadOnlyCollection<ISet<int>> solutions)
  372. {
  373. var b = new StringBuilder();
  374. var i = 0;
  375. b.AppendLine($"Constraints: {Print(problem.Constraints)}");
  376. foreach (var set in problem.Sets)
  377. {
  378. var isClue = problem.Clues.Contains(set.Key);
  379. if (isClue)
  380. {
  381. b.AppendLine($"Set {set.Key}: {Print(set.Value)} [Clue]");
  382. }
  383. else
  384. {
  385. b.AppendLine($"Set {set.Key}: {Print(set.Value)}");
  386. }
  387. }
  388. b.AppendLine($"Solutions: {solutions.Count}");
  389. foreach (var solution in solutions)
  390. {
  391. b.AppendLine($"Solution #{++i}: {string.Join(", ", solution.OrderBy(_ => _).Select(s => Print(problem.Sets[s])))}");
  392. }
  393. return b.ToString();
  394. }
  395.  
  396. string Print<T>(IEnumerable<T> set) => !set.Any() ? "Empty" : $"{{{string.Join(", ", set.OrderBy(_ => _))}}}";
  397.  
  398. static string Normalize(string input) => Regex.Replace(input, @"s+", string.Empty);
  399.  
  400. static void AssertAreEqual(string excepted, string actual) => Assert.AreEqual(Normalize(excepted), Normalize(actual));
  401.  
  402. public class Sudoku
  403. {
  404. public void Solve(char[,] board)
  405. {
  406. var problem = Reduce(board);
  407.  
  408. // The challenge allows us to assert a single solution is available
  409. var solution = problem.Solve(
  410. new DLX(), new SolverOptions { MaxSolutions = 1 }).Single();
  411.  
  412. Augment(board, solution);
  413. }
  414.  
  415. internal void Augment(char[,] board, ISet<int> solution)
  416. {
  417. var n2 = board.Length;
  418. var n = (int)Math.Sqrt(n2);
  419.  
  420. foreach (var match in solution)
  421. {
  422. var row = match / (n * n);
  423. var column = match / n % n;
  424. var number = match % n;
  425. var symbol = Encode(number);
  426.  
  427. board[row, column] = symbol;
  428. }
  429. }
  430.  
  431. internal ExactCover Reduce(char[,] board)
  432. {
  433. var n2 = board.Length;
  434. var n = (int)Math.Sqrt(n2);
  435. var m = (int)Math.Sqrt(n);
  436.  
  437. // The constraints for any regular Sudoku puzzle are:
  438. // - For each row, a number can appear only once.
  439. // - For each column, a number can appear only once.
  440. // - For each region(small square), a number can appear only once.
  441. // - Each cell can only have one number.
  442.  
  443. // For 9x9 Sudoku, the binary matrix will have 4 x 9² columns.
  444.  
  445. var constraints = new HashSet<int>(Enumerable.Range(0, 4 * n * n));
  446.  
  447. // The sets for any regular Sudoku puzzle are all permutations of:
  448. // - Each row, each column, each number
  449.  
  450. // For 9x9 Sudoku, the binary matrix will have 9³ rows.
  451.  
  452. var sets = new Dictionary<int, ISet<int>>();
  453. var clues = new HashSet<int>();
  454.  
  455. foreach (var row in Enumerable.Range(0, n))
  456. {
  457. foreach (var column in Enumerable.Range(0, n))
  458. {
  459. var region = m * (row / m) + column / m;
  460.  
  461. foreach (var number in Enumerable.Range(0, n))
  462. {
  463. sets.Add((row * n + column) * n + number, new HashSet<int>
  464. {
  465. // number in row
  466. row * n + number,
  467. // number in column
  468. (n + column) * n + number,
  469. // number in region
  470. (2 * n + region) * n + number,
  471. // cell has number
  472. (3 * n + row) * n + column
  473. });
  474. }
  475.  
  476. if (TryDecode(board[row, column], out var given))
  477. {
  478. clues.Add((row * n + column) * n + given);
  479. }
  480. }
  481. }
  482.  
  483. var problem = new ExactCover(constraints, sets, clues);
  484.  
  485. return problem;
  486. }
  487.  
  488. internal char Encode(int number) => (char)('1' + number);
  489.  
  490. internal bool TryDecode(char symbol, out int number)
  491. {
  492. if (symbol == '.')
  493. {
  494. number = -1;
  495. return false;
  496. }
  497.  
  498. number = symbol - '1';
  499. return true;
  500. }
  501. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement