• java实现解数独的算法


    BeginIndexRangeVO.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import java.io.Serializable;
    4. @Getter
    5. @Setter
    6. public class BeginIndexRangeVO implements Serializable {
    7. private int beginRowNum;
    8. private int beginColNum;
    9. public BeginIndexRangeVO(int beginRowNum, int beginColNum) {
    10. this.beginRowNum = beginRowNum;
    11. this.beginColNum = beginColNum;
    12. }
    13. }

    PointVO.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import java.io.Serializable;
    4. import java.util.List;
    5. @Getter
    6. @Setter
    7. public class PointVO implements Serializable {
    8. /*
    9. 2 - 9 6 - - 8 5 4
    10. 4 8 6 9 5 2 7 3 1
    11. - - - - 4 - 6 2 9
    12. - - 2 4 - 9 5 8 6
    13. 5 - 4 - - - 3 9 2
    14. - 9 - 2 - 5 1 4 7
    15. - - 1 - 2 4 9 - 3
    16. 9 4 - 3 1 - 2 - 5
    17. - 2 - - 9 - 4 1 8
    18. */
    19. private String number;
    20. private String tryNumber;
    21. private int rowNum;
    22. private int colNum;
    23. private int gongNum=0;
    24. private String code;
    25. private List possibleNumbers;
    26. private List possibleNumbers_bak;
    27. public PointVO(String number, int rowNum, int colNum) {
    28. this.number = number;
    29. this.rowNum = rowNum;
    30. this.colNum = colNum;
    31. }
    32. public String getCode(){
    33. if( this.code != null ){
    34. return this.code;
    35. }
    36. this.code = this.rowNum + "-" + this.colNum;
    37. return this.code;
    38. }
    39. public int getGongNum(){
    40. if( this.gongNum > 0 ){
    41. return this.gongNum;
    42. }
    43. if( this.rowNum <= 3 ){
    44. if( this.colNum <= 3 ){
    45. this.gongNum = 1;
    46. }else if( this.colNum <= 6 ){
    47. this.gongNum = 2;
    48. }else {
    49. this.gongNum = 3;
    50. }
    51. }else if( this.rowNum <= 6 ){
    52. if( this.colNum <= 3 ){
    53. this.gongNum = 4;
    54. }else if( this.colNum <= 6 ){
    55. this.gongNum = 5;
    56. }else {
    57. this.gongNum = 6;
    58. }
    59. }else {
    60. if( this.colNum <= 3 ){
    61. this.gongNum = 7;
    62. }else if( this.colNum <= 6 ){
    63. this.gongNum = 8;
    64. }else {
    65. this.gongNum = 9;
    66. }
    67. }
    68. return this.gongNum;
    69. }
    70. }

    RowColVO.java:
    1. import lombok.Getter;
    2. import lombok.Setter;
    3. import java.io.Serializable;
    4. @Getter
    5. @Setter
    6. public class RowColVO implements Serializable {
    7. private int rowNum;
    8. private int colNum;
    9. public RowColVO(int rowNum, int colNum) {
    10. this.rowNum = rowNum;
    11. this.colNum = colNum;
    12. }
    13. }

    ShuduSolver.java:
    1. import com.alibaba.fastjson.JSONObject;
    2. import java.util.*;
    3. public class ShuduSolver {
    4. private static final Map map_gongNum_beginIndexRange = new HashMap<>();
    5. private static final Map> map_gongNum_numbers = new HashMap<>();
    6. private static final Map> map_rowNum_numbers = new HashMap<>();
    7. private static final Map> map_colNum_numbers = new HashMap<>();
    8. private static Map map_code_point;
    9. private static int count_fill = 0;
    10. static {
    11. map_gongNum_beginIndexRange.put(1, new BeginIndexRangeVO(1, 1));
    12. map_gongNum_beginIndexRange.put(2, new BeginIndexRangeVO(1, 4));
    13. map_gongNum_beginIndexRange.put(3, new BeginIndexRangeVO(1, 7));
    14. map_gongNum_beginIndexRange.put(4, new BeginIndexRangeVO(4, 1));
    15. map_gongNum_beginIndexRange.put(5, new BeginIndexRangeVO(4, 4));
    16. map_gongNum_beginIndexRange.put(6, new BeginIndexRangeVO(4, 7));
    17. map_gongNum_beginIndexRange.put(7, new BeginIndexRangeVO(7, 1));
    18. map_gongNum_beginIndexRange.put(8, new BeginIndexRangeVO(7, 4));
    19. map_gongNum_beginIndexRange.put(9, new BeginIndexRangeVO(7, 7));
    20. }
    21. /**
    22. * 优化:储存一个存储已经正确填写多少个格子的全局变量,没进行操作前先判断下是否等于81,如果已经结束了,就直接返回
    23. * @param args
    24. */
    25. public static void main(String[] args) {
    26. /* int[][] chupan ={
    27. { 0,0,0, 0,0,0, 0,0,0 },
    28. { 0,0,0, 0,0,0, 0,0,0 },
    29. { 0,0,0, 0,0,0, 0,0,0 },
    30. { 0,0,0, 0,0,0, 0,0,0 },
    31. { 0,0,0, 0,0,0, 0,0,0 },
    32. { 0,0,0, 0,0,0, 0,0,0 },
    33. { 0,0,0, 0,0,0, 0,0,0 },
    34. { 0,0,0, 0,0,0, 0,0,0 },
    35. { 0,0,0, 0,0,0, 0,0,0 }
    36. };*/
    37. /*int[][] chupan = {
    38. { 2,0,0, 0,8,0, 0,1,0 },
    39. { 0,4,0, 1,0,0, 3,0,0 },
    40. { 0,0,0, 0,9,0, 0,0,0 },
    41. { 0,0,0, 7,0,0, 8,4,0 },
    42. { 0,9,0, 0,2,1, 0,0,0 },
    43. { 0,0,6, 0,0,0, 0,0,0 },
    44. { 0,3,0, 0,0,0, 5,8,0 },
    45. { 1,0,0, 8,0,0, 0,0,7 },
    46. { 0,2,7, 0,5,3, 0,0,0 }
    47. };*/
    48. /*int[][] chupan ={
    49. { 0,9,0, 3,0,0, 0,0,0 },
    50. { 0,0,0, 0,2,9, 0,0,1 },
    51. { 6,0,0, 0,0,0, 0,0,7 },
    52. { 0,7,0, 0,0,0, 0,1,0 },
    53. { 5,0,0, 0,0,0, 0,6,4 },
    54. { 0,0,0, 8,0,4, 0,0,9 },
    55. { 0,1,0, 2,0,0, 6,0,0 },
    56. { 7,8,0, 0,9,0, 0,0,0 },
    57. { 0,0,4, 0,5,0, 0,0,3 }
    58. };*/
    59. int[][] chupan ={
    60. { 1,0,4, 0,0,0, 0,0,0 },
    61. { 0,0,0, 0,0,5, 0,0,0 },
    62. { 0,6,0, 0,8,0, 0,7,3 },
    63. { 0,0,0, 8,0,1, 9,0,0 },
    64. { 6,5,0, 0,0,0, 0,0,0 },
    65. { 0,0,0, 3,0,0, 0,0,8 },
    66. { 0,2,0, 0,3,0, 0,0,7 },
    67. { 0,0,0, 0,0,7, 1,3,0 },
    68. { 4,7,0, 0,0,0, 8,9,0 }
    69. };
    70. List points = initChuPan( chupan );
    71. map_code_point = points2CodeAndPointMap(points);
    72. init_map_xxxNum_numbers( );
    73. print( );
    74. makeACommonTest( );
    75. print( );
    76. // 做笔记,记录每个空格子可能得候选数字
    77. calculatePossibleNumbers( );
    78. printPossibles( );
    79. tryFill();
    80. printPossibles();
    81. }
    82. /**
    83. *
    84. * @param rowNum
    85. * @param colNum
    86. * @return true:通过尝试的方式已经为该行该列填了正确的数字了,false:通过尝试的方式暂无法为该行该列填正确的数字
    87. */
    88. private static boolean tryFill( int rowNum,int colNum){
    89. String code = getCode(rowNum, colNum);
    90. PointVO point = map_code_point.get(code);
    91. if( point.getNumber() != null ){
    92. // 排除非空格子
    93. return false;
    94. }
    95. List possibleNumbers = deepCopyList( point.getPossibleNumbers() );
    96. List numbers_canNotFill = new ArrayList<>();
    97. for( String possibleNumber:possibleNumbers ){
    98. calculatePossibleNumbers();
    99. String errorMsg = tryFill(rowNum, colNum, possibleNumber);
    100. if( errorMsg != null ){
    101. // 发生了错误,所以 possibleNumber 不能填到该格子上
    102. numbers_canNotFill.add( possibleNumber );
    103. }
    104. }
    105. calculatePossibleNumbers();
    106. possibleNumbers.removeAll( numbers_canNotFill );
    107. String pointName = getPointName(rowNum, colNum);
    108. if( possibleNumbers.size() == 1 ){
    109. String number = possibleNumbers.get(0);
    110. fillNumber( rowNum,colNum,number );
    111. System.out.println( "通过尝试的方式确定 " + pointName + " 填写数字 " + JSONObject.toJSONString( numbers_canNotFill ) + " 都会产生矛盾,所以 " + pointName + " 的正确数字是 " + number );
    112. return true;
    113. }else {
    114. // System.out.println( "通过尝试的方式只能确定 " + pointName + " 填写数字 " + JSONObject.toJSONString( numbers_canNotFill ) + " 会产生矛盾,但是还无法确定填写数字 " + JSONObject.toJSONString( possibleNumbers ) + " 是否会产生矛盾,所以暂时无法为 " + pointName + " 填写正确的数字" );
    115. }
    116. return false;
    117. }
    118. private static boolean gameSuccess(){
    119. return count_fill == 81;
    120. }
    121. private static void tryFill(){
    122. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    123. for (int colNum = 1; colNum <=9 ; colNum++) {
    124. boolean setSuccess = tryFill(rowNum, colNum);
    125. if( setSuccess ){
    126. makeACommonTest();
    127. }
    128. if( gameSuccess() ){
    129. return;
    130. }
    131. }
    132. }
    133. }
    134. /**
    135. *
    136. * @param rowNum
    137. * @param colNum
    138. * @param number
    139. * @return 返回 errorMsg,为 null 表示让该行该列填该数字暂未导致错误( 即不确定该行该列能否填该数字 ),
    140. * 否则表示让该行该列填该数字导致出现了错误( 即该行该列不能填该数字 )
    141. */
    142. private static String tryFill(int rowNum, int colNum, String number) {
    143. String code = getCode(rowNum, colNum);
    144. PointVO point = map_code_point.get(code);
    145. List possibleNumbers = new ArrayList<>();
    146. possibleNumbers.add( number );
    147. point.setPossibleNumbers( possibleNumbers );
    148. boolean findConflict = scanAndTryToFindConflict();
    149. String pointName = getPointName(rowNum, colNum);
    150. if( findConflict ){
    151. return "为 " + pointName + " 格子设置数字" + number + " 会导致冲突";
    152. }else {
    153. // System.out.println( "为 " + pointName + " 格子设置数字" + number + " 暂时不会导致冲突,所以暂不确定 " + pointName + " 能否设置数字" + number );
    154. return null;
    155. }
    156. }
    157. private static String getPointName(int rowNum, int colNum) {
    158. return rowNum + "行" + colNum + "列";
    159. }
    160. /**
    161. * 初始化三个缓存map( map_rowNum_numbers、map_colNum_numbers、map_gongNum_numbers )
    162. */
    163. private static void init_map_xxxNum_numbers( ) {
    164. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    165. for (int colNum = 1; colNum <=9 ; colNum++) {
    166. String code = getCode(rowNum, colNum);
    167. PointVO point = map_code_point.get(code);
    168. String number = point.getNumber();
    169. if( number == null ){
    170. continue;
    171. }
    172. // 行
    173. Set numbers = map_rowNum_numbers.get(rowNum);
    174. if( numbers == null ){
    175. numbers = new HashSet<>();
    176. map_rowNum_numbers.put( rowNum,numbers );
    177. }
    178. numbers.add( number );
    179. // 列
    180. numbers = map_colNum_numbers.get(colNum);
    181. if( numbers == null ){
    182. numbers = new HashSet<>();
    183. map_colNum_numbers.put( colNum,numbers );
    184. }
    185. numbers.add( number );
    186. // 宫
    187. int gongNum = getGongNum(rowNum, colNum);
    188. numbers = map_gongNum_numbers.get(gongNum);
    189. if( numbers == null ){
    190. numbers = new HashSet<>();
    191. map_gongNum_numbers.put( gongNum,numbers );
    192. }
    193. numbers.add( number );
    194. }
    195. }
    196. }
    197. public static boolean scanAndTryToFindConflict( ){
    198. // 寻找 possibleNumber.size = 1的空格子
    199. Set codes = map_code_point.keySet();
    200. boolean needReScan = false;
    201. for( String code:codes ){
    202. PointVO point = map_code_point.get(code);
    203. if( point.getNumber() != null ){
    204. continue;
    205. }
    206. if( point.getPossibleNumbers().size() != 1 ){
    207. continue;
    208. }
    209. // 找到一个 possibleNumber.size = 1 的空格子
    210. String possibleNumber = point.getPossibleNumbers().get(0);
    211. // 将 possibleNumber 从同行、同列、同宫的空格子的 possibleNumbers 集合中删除,删除如果会导致集合的长度变味0,则发生了矛盾,return true
    212. // 删除后如果出现了新的 size=1的空格子,则后面需要重新扫描,已经检查过的 size=1的空格子就不需要重建检查了,防止死循环
    213. // 如果过程中有产生新的 size=1的,则需要递归重新扫描
    214. // 同行
    215. for (int colNum = 1; colNum <=9 ; colNum++) {
    216. String code_sameRow = getCode(point.getRowNum(), colNum);
    217. PointVO point_sameRow = map_code_point.get(code_sameRow);
    218. if( point_sameRow.getCode().equals( code ) ){
    219. // 排除自己
    220. continue;
    221. }
    222. if( point_sameRow.getNumber() != null ){
    223. // 排除非空格子
    224. continue;
    225. }
    226. boolean remove = point_sameRow.getPossibleNumbers().remove(possibleNumber);
    227. if( point_sameRow.getPossibleNumbers().size() == 0 ){
    228. // 发生了矛盾的地方
    229. return true;
    230. }
    231. if( remove && point_sameRow.getPossibleNumbers().size() == 1 ){
    232. // 产生了新的 size =1的格子,需要递归重新扫描
    233. needReScan = true;
    234. }
    235. }
    236. // 同列
    237. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    238. String code_sameCol = getCode(rowNum, point.getColNum());
    239. PointVO point_sameCol = map_code_point.get(code_sameCol);
    240. if( point_sameCol.getCode().equals( code ) ){
    241. // 排除自己
    242. continue;
    243. }
    244. if( point_sameCol.getNumber() != null ){
    245. // 排除非空格子
    246. continue;
    247. }
    248. boolean remove = point_sameCol.getPossibleNumbers().remove(possibleNumber);
    249. if( point_sameCol.getPossibleNumbers().size() == 0 ){
    250. // 发生了矛盾的地方
    251. return true;
    252. }
    253. if( remove && point_sameCol.getPossibleNumbers().size() == 1 ){
    254. // 产生了新的 size = 1的格子,需要递归重新扫描
    255. needReScan = true;
    256. }
    257. }
    258. // 同宫
    259. int gongNum = getGongNum(point.getRowNum(), point.getColNum());
    260. BeginIndexRangeVO beginIndexRange = map_gongNum_beginIndexRange.get(gongNum);
    261. int beginRowNum = beginIndexRange.getBeginRowNum();
    262. int endRowNum = beginRowNum + 2;
    263. int beginColNum = beginIndexRange.getBeginColNum();
    264. int endColNum = beginColNum + 2;
    265. for (int rowNum = beginRowNum; rowNum <=endRowNum ; rowNum++) {
    266. for (int colNum = beginColNum; colNum <=endColNum ; colNum++) {
    267. String code_sameGong = getCode(rowNum, colNum);
    268. PointVO point_sameGong = map_code_point.get(code_sameGong);
    269. if( point_sameGong.getCode().equals( code ) ){
    270. // 排除自己
    271. continue;
    272. }
    273. if( point_sameGong.getNumber() != null ){
    274. // 排除非空格子
    275. continue;
    276. }
    277. boolean remove = point_sameGong.getPossibleNumbers().remove(possibleNumber);
    278. if( point_sameGong.getPossibleNumbers().size() == 0 ){
    279. // 发生了矛盾的地方
    280. return true;
    281. }
    282. if( remove && point_sameGong.getPossibleNumbers().size() == 1 ){
    283. // 产生了新的 size =1的格子,需要递归重新扫描
    284. needReScan = true;
    285. }
    286. }
    287. }
    288. }
    289. if( needReScan ){
    290. return scanAndTryToFindConflict( );
    291. }else {
    292. return false;
    293. }
    294. }
    295. private static List deepCopyList(List list) {
    296. List list_copy = new ArrayList<>();
    297. for( String item:list ){
    298. list_copy.add( item );
    299. }
    300. return list_copy;
    301. }
    302. private static void calculatePossibleNumbers( ) {
    303. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    304. for (int colNum = 1; colNum <=9 ; colNum++) {
    305. calculatePossibleNumberForTargetRowAndCol( rowNum,colNum );
    306. }
    307. }
    308. }
    309. private static void calculatePossibleNumberForTargetRowAndCol(int targetRowNum, int targetColNum) {
    310. String code = getCode(targetRowNum, targetColNum);
    311. PointVO point = map_code_point.get(code);
    312. if( point.getNumber() != null ){
    313. return;
    314. }
    315. Set numbers_canNotFill = new HashSet<>();
    316. numbers_canNotFill.addAll( map_rowNum_numbers.get(targetRowNum) );
    317. numbers_canNotFill.addAll( map_colNum_numbers.get(targetColNum) );
    318. int gongNum = getGongNum(targetRowNum, targetColNum);
    319. numbers_canNotFill.addAll( map_gongNum_numbers.get(gongNum) );
    320. List numbers_possible = new ArrayList<>();
    321. for (int i = 1; i <=9 ; i++) {
    322. numbers_possible.add( String.valueOf( i ) );
    323. }
    324. numbers_possible.removeAll( numbers_canNotFill );
    325. point.setPossibleNumbers( numbers_possible );
    326. point.setPossibleNumbers_bak( deepCopyList( numbers_possible ) );
    327. point.setTryNumber( null );
    328. }
    329. private static void makeACommonTest( ) {
    330. if( gameSuccess() ){
    331. // 已经结束了
    332. return;
    333. }
    334. int testTime_max = 10;
    335. int testTime = 0;
    336. while ( true ){
    337. testTime++;
    338. if( testTime > testTime_max ){
    339. break;
    340. }
    341. // method1( map_code_point );
    342. // method2( map_code_point );
    343. method_hangBingChuxxx( );
    344. method_lieBingChuxxx( );
    345. method_gongBingChu( );
    346. }
    347. }
    348. private static void method_lieBingChu( ) {
    349. // 遍历每一列
    350. for (int colNum = 1; colNum <=9 ; colNum++) {
    351. // 对于当前列,遍历该列每一行的空格子:
    352. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    353. String code = getCode(rowNum, colNum);
    354. PointVO point = map_code_point.get(code);
    355. if( point.getNumber() != null ){
    356. continue;
    357. }
    358. // 对于该列当前行的空格子:
    359. // 计算该格子所在的行、列、宫已经填写的数字的并集去重,如果该集合的长度为8,表示已经确定了该格子填什么数字了,即为缺失的数字
    360. Set numbers_canNotFill = new HashSet<>();
    361. numbers_canNotFill.addAll( map_rowNum_numbers.get( rowNum ) );
    362. numbers_canNotFill.addAll( map_colNum_numbers.get( colNum ) );
    363. int gongNum = getGongNum(rowNum, colNum);
    364. numbers_canNotFill.addAll( map_gongNum_numbers.get( gongNum ) );
    365. String number_shouldFill = getSingleMissingNumber( numbers_canNotFill );
    366. if( number_shouldFill != null ){
    367. fillNumber( point,number_shouldFill );
    368. // System.out.println( "列摒除:" + colNum + "列第" + rowNum + "行的数字不能填写" + JSONObject.toJSONString( numbers_canNotFill ) + ",只能填写" + number_shouldFill );
    369. }
    370. }
    371. }
    372. }
    373. private static String getShouldFillNumber(int rowNum, int colNum) {
    374. Set numbers_canNotFill = new HashSet<>();
    375. numbers_canNotFill.addAll( map_rowNum_numbers.get( rowNum ) );
    376. numbers_canNotFill.addAll( map_colNum_numbers.get( colNum ) );
    377. numbers_canNotFill.addAll( map_gongNum_numbers.get( getGongNum(rowNum, colNum) ) );
    378. return getSingleMissingNumber( numbers_canNotFill );
    379. }
    380. /*
    381. - - - - 2 - - 8 5
    382. 6 5 2 - 7 - - - -
    383. 3 - - - 5 - - 1 2
    384. 7 2 3 9 - 4 - 5 -
    385. 5 4 - 2 - - - 3 -
    386. - 6 9 - - 5 - 2 -
    387. - - 5 - - - 2 6 -
    388. 2 - - 5 8 6 1 - 3
    389. - 3 6 - - 2 5 7 -
    390. */
    391. private static void method_gongBingChu( ) {
    392. if( gameSuccess() ){
    393. // 已经结束了
    394. return;
    395. }
    396. // 遍历每一个宫:
    397. for (int gongNum = 1; gongNum <=9 ; gongNum++) {
    398. // 对于当前宫,遍历每一个空格子:
    399. BeginIndexRangeVO beginIndexRange = map_gongNum_beginIndexRange.get(gongNum);
    400. // 计算该宫全部的数字
    401. int beginRowNum = beginIndexRange.getBeginRowNum();
    402. int endRowNum = beginRowNum + 2;
    403. int beginColNum = beginIndexRange.getBeginColNum();
    404. int endColNum = beginColNum + 2;
    405. for (int rowNum = beginRowNum; rowNum <=endRowNum ; rowNum++) {
    406. for (int colNum = beginColNum; colNum <=endColNum ; colNum++) {
    407. // 对于当前的空格子,计算该格子所处的行、列、宫的已填数字的并集并去重,从集合[1,2,3,4,5,6,7,8,9]
    408. // 中删除这个集合,产生的新集合就是可填数字的集合,如果可填数字的集合的长度为1,表示已经确定了该格子需要填写的数字了
    409. String code = getCode(rowNum, colNum);
    410. PointVO point = map_code_point.get(code);
    411. if( point.getNumber() != null ){
    412. continue;
    413. }
    414. String number_shouldFill = getShouldFillNumber( rowNum,colNum );
    415. if( number_shouldFill != null ){
    416. // 已经确定该格子该填什么数字了
    417. fillNumber( point,number_shouldFill );
    418. // System.out.println( "宫摒除:" + gongNum + "宫的格子( " + rowNum + "行" + colNum + "列 )不能填写 xxx,只能填写" + number_shouldFill );
    419. }
    420. }
    421. }
    422. }
    423. }
    424. private static String getSingleMissingNumber(Set numbers) {
    425. if( numbers == null || numbers.size() != 8 ){
    426. return null;
    427. }
    428. int sum = 0;
    429. for( String number:numbers ){
    430. sum += Integer.valueOf( number );
    431. }
    432. // ps:45 = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
    433. return String.valueOf( 45 - sum );
    434. }
    435. private static boolean fillNumber( PointVO point,String number ) {
    436. if( point == null || number == null ){
    437. return false;
    438. }
    439. point.setNumber( number );
    440. count_fill++;
    441. // System.out.println( "count_fille = " + count_fill + ":" + point.getRowNum() + "行" + point.getColNum() + "列 填写数字 " + number );
    442. // 更新三个 map_xxxNum_numbers
    443. addNumber2_map_xxxNum_numbers( point.getRowNum(),point.getColNum(),number );
    444. if( gameSuccess() ){
    445. printPossibles();
    446. System.exit( 0 );
    447. }
    448. return true;
    449. }
    450. /**
    451. * 更新三个 map_xxxNum_numbers
    452. * @param rowNum
    453. * @param colNum
    454. * @param number
    455. */
    456. private static void addNumber2_map_xxxNum_numbers(int rowNum, int colNum, String number) {
    457. int gongNum = getGongNum(rowNum, colNum);
    458. map_gongNum_numbers.get( gongNum ).add( number );
    459. map_rowNum_numbers.get( rowNum ).add( number );
    460. map_colNum_numbers.get( colNum ).add( number );
    461. }
    462. private static boolean fillNumber(int rowNum, int colNum, String number) {
    463. String code = getCode(rowNum, colNum);
    464. PointVO point = map_code_point.get(code);
    465. if( point == null ){
    466. return false;
    467. }
    468. return fillNumber( point,number );
    469. }
    470. /*
    471. - - - - 2 - - 8 5
    472. 6 5 2 - 7 - - - -
    473. 3 - - - 5 - - 1 2
    474. 7 2 3 9 - 4 - 5 -
    475. 5 4 - 2 - - - 3 -
    476. - 6 9 - - 5 - 2 -
    477. - - 5 - - - 2 6 -
    478. 2 - - 5 8 6 1 - 3
    479. - 3 6 - - 2 5 7 -
    480. */
    481. private static void method2( ) {
    482. if( gameSuccess() ){
    483. // 已经结束了
    484. return;
    485. }
    486. // 遍历每一个空格子
    487. // 找到该格子所在的宫、行、列中全部数字的并集再去重后得到一个新的集合,如果新集合的大小为8,则缺少的那个数字即是需要填写到该格子中的数字
    488. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    489. for (int colNum = 1; colNum <=9 ; colNum++) {
    490. String code = getCode(rowNum, colNum);
    491. PointVO point = map_code_point.get(code);
    492. if( point.getNumber() != null ){
    493. continue;
    494. }
    495. String number_confirm = getShouldFillNumber( rowNum,colNum );
    496. if( number_confirm != null ){
    497. System.out.println( rowNum + "行" + colNum + "列的格子不能填写 xxx,所以只能填写" + number_confirm );
    498. fillNumber( point,number_confirm );
    499. }
    500. }
    501. }
    502. }
    503. /*
    504. - 2 - 4 - 9 1 - -
    505. 4 - 6 - 5 - - 8 9
    506. - 7 - - 8 3 - 2 4
    507. 7 1 - 5 3 - - - -
    508. - - - - 9 - 2 - -
    509. - - - - 4 - - - 7
    510. - 6 - - - - - - -
    511. - - 7 3 - - 8 - 1
    512. 3 4 - - - 5 - 6 -
    513. */
    514. private static int getGongNum(int rowNum, int colNum) {
    515. if( rowNum <= 3 ){
    516. if( colNum <= 3 ){
    517. return 1;
    518. }else if( colNum <= 6 ){
    519. return 2;
    520. }else {
    521. return 3;
    522. }
    523. }else if( rowNum <= 6 ){
    524. if( colNum <= 3 ){
    525. return 4;
    526. }else if( colNum <= 6 ){
    527. return 5;
    528. }else {
    529. return 6;
    530. }
    531. }else {
    532. if( colNum <= 3 ){
    533. return 7;
    534. }else if( colNum <= 6 ){
    535. return 8;
    536. }else {
    537. return 9;
    538. }
    539. }
    540. }
    541. private static void method1( ) {
    542. if( gameSuccess() ){
    543. // 已经结束了
    544. return;
    545. }
    546. for (int i = 1; i <=9 ; i++) {
    547. String targetNumber = String.valueOf( i );
    548. for (int gongNum = 1; gongNum <=9 ; gongNum++) {
    549. setTargetNumberForTargetGong( gongNum,targetNumber );
    550. }
    551. }
    552. }
    553. /*
    554. - - - - 2 - - 8 5
    555. 6 5 2 - 7 - - - -
    556. 3 - - - 5 - - 1 2
    557. 7 2 3 9 - 4 - 5 -
    558. 5 4 - 2 - - - 3 -
    559. - 6 9 - - 5 - 2 -
    560. - - 5 - - - 2 6 -
    561. 2 - - 5 8 6 1 - -
    562. - 3 6 - - 2 5 7 -
    563. */
    564. private static void method_hangBingChuxxx( ) {
    565. if( gameSuccess() ){
    566. // 已经结束了
    567. return;
    568. }
    569. // 遍历每一行,对于某一行,看该行缺少什么数字,比如缺1,则看该行空缺的格子,哪些不能填1,如果排除后只剩下一个格子了,则该格子必须填1,
    570. // 不能填1的依据是该格子所在的宫或者所在的列或者所在的行已经存在1了
    571. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    572. for( int number = 1;number<=9;number++ ){
    573. // 检查该行是否存在当前数字
    574. String number_str = String.valueOf( number );
    575. if( map_rowNum_numbers.get( rowNum ).contains( number_str ) ){
    576. continue;
    577. }
    578. // 该行不存在该数字
    579. // 检测该行哪些格子不能填写该数字,也即确定该行第几列可以填写该数字
    580. int colNum = getColNumThatCanFillTargetNumberInTargetRow( rowNum,number_str );
    581. if( colNum > 0 & colNum < 10 ){
    582. fillNumber( rowNum,colNum,number_str );
    583. }
    584. }
    585. }
    586. }
    587. /*
    588. - - - - 2 - - 8 5
    589. 6 5 2 - 7 - - - -
    590. 3 - - - 5 - - 1 2
    591. 7 2 3 9 - 4 - 5 -
    592. 5 4 - 2 - - - 3 -
    593. - 6 9 - - 5 - 2 -
    594. - - 5 - - - 2 6 -
    595. 2 - - 5 8 6 1 - -
    596. - 3 6 - - 2 5 7 -
    597. */
    598. private static int getColNumThatCanFillTargetNumberInTargetRow(int targetRowNum, String targetNumber) {
    599. List colNumList_blank = new ArrayList();
    600. List colNumList_canNotFill = new ArrayList();
    601. for (int colNum = 1; colNum <=9 ; colNum++) {
    602. String code = getCode(targetRowNum, colNum);
    603. PointVO point = map_code_point.get(code);
    604. if( point.getNumber() != null ){
    605. continue;
    606. }
    607. colNumList_blank.add( colNum );
    608. // 检测当前格子所在的宫是否存在该数字
    609. int gongNum = getGongNum(targetRowNum, colNum);
    610. if( map_gongNum_numbers.get( gongNum ).contains( targetNumber ) ){
    611. colNumList_canNotFill.add( colNum );
    612. continue;
    613. }
    614. // 检测当前格子所在的列是否存在该数字
    615. if( map_colNum_numbers.get( colNum ).contains( targetNumber ) ){
    616. colNumList_canNotFill.add( colNum );
    617. }
    618. }
    619. colNumList_blank.removeAll(colNumList_canNotFill);
    620. if( colNumList_blank.size() == 1 ){
    621. Integer colNum_canFill = colNumList_blank.get(0);
    622. // System.out.println( "行摒除:第" + targetRowNum + "行的第" + JSONObject.toJSONString( colNumList_canNotFill ) + "列不能填写数字" + targetNumber + "了,只能是第" + colNum_canFill + "列可以填写数字" + targetNumber + "了" );
    623. return colNum_canFill;
    624. }
    625. return -1;
    626. }
    627. /*
    628. - - - - 2 - - 8 5
    629. 6 5 2 - 7 - - - -
    630. 3 - - - 5 - - 1 2
    631. 7 2 3 9 - 4 - 5 -
    632. 5 4 - 2 - - - 3 -
    633. - 6 9 - - 5 - 2 -
    634. - - 5 - - - 2 6 -
    635. 2 - - 5 8 6 1 - -
    636. - 3 6 - - 2 5 7 -
    637. */
    638. private static void method_lieBingChuxxx( ) {
    639. if( gameSuccess() ){
    640. // 已经结束了
    641. return;
    642. }
    643. for (int colNum = 1; colNum <=9 ; colNum++) {
    644. // 遍历该列的每一个空的格子
    645. for( int number=1;number<=9;number++ ){
    646. // 检测该列是否存在该数字
    647. String number_str = String.valueOf( number );
    648. if( map_colNum_numbers.get( colNum ).contains( number_str ) ){
    649. continue;
    650. }
    651. // 该列不存在该数字
    652. int rowNum_canFill = getRowNumThatCanFillTargetNumberInTargetCol( colNum,number_str );
    653. if( rowNum_canFill > 0 && rowNum_canFill < 10 ){
    654. fillNumber( rowNum_canFill,colNum,number_str );
    655. }
    656. }
    657. }
    658. }
    659. /*
    660. - - - - 2 - - 8 5
    661. 6 5 2 - 7 - - - -
    662. 3 - - - 5 - - 1 2
    663. 7 2 3 9 - 4 - 5 -
    664. 5 4 - 2 - - - 3 -
    665. - 6 9 - - 5 - 2 -
    666. - - 5 - - - 2 6 -
    667. 2 - - 5 8 6 1 - -
    668. - 3 6 - - 2 5 7 -
    669. */
    670. /**
    671. * 在指定列中找到可以填写指定数字的行号
    672. * @param targetColNum
    673. * @param targetNumber
    674. * @return 1~9表示找到了可以填写该数字的行号,其他表示未找到可以填写该数字的行号
    675. */
    676. private static int getRowNumThatCanFillTargetNumberInTargetCol(int targetColNum, String targetNumber) {
    677. List rowNumList_blank = new ArrayList<>();
    678. List rowNumList_canNotFill = new ArrayList<>();
    679. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    680. String code = getCode(rowNum, targetColNum);
    681. PointVO point = map_code_point.get(code);
    682. if( point.getNumber() != null ){
    683. continue;
    684. }
    685. rowNumList_blank.add( rowNum );
    686. // 检测当前格子所在的行和宫是否已经存在该数字
    687. if( map_rowNum_numbers.get( rowNum ).contains( targetNumber ) ){
    688. rowNumList_canNotFill.add( rowNum );
    689. continue;
    690. }
    691. int gongNum = getGongNum(rowNum, targetColNum);
    692. if( map_gongNum_numbers.get( gongNum ).contains( targetNumber ) ){
    693. rowNumList_canNotFill.add( rowNum );
    694. }
    695. }
    696. rowNumList_blank.removeAll(rowNumList_canNotFill);
    697. if( rowNumList_blank.size() == 1 ){
    698. Integer rowNum_canFill = rowNumList_blank.get(0);
    699. // System.out.println( "列摒除:" + targetColNum + "列的第" + JSONObject.toJSONString( rowNumList_canNotFill ) + "行都不能填写数字" + targetNumber + ",所以数字" + targetNumber + "只能填写在第" + rowNum_canFill + "行" );
    700. // 列摒除:9列的第[]行都不能填写数字5,所以数字5只能填写在第3行
    701. return rowNum_canFill;
    702. }
    703. return -1;
    704. }
    705. /**
    706. * 为指定的宫设置指定的数字
    707. * @param targetGongNum
    708. * @param targetNumber
    709. * @return true 表示找到了合适的位置设置数字了,fasle无法确定哪里需要填写该数字
    710. */
    711. private static boolean setTargetNumberForTargetGong(int targetGongNum, String targetNumber) {
    712. if( map_gongNum_numbers.get( targetGongNum ).contains( targetNumber ) ){
    713. // 该宫内已经存在该数字
    714. return false;
    715. }
    716. // 未填写数字的格子的个数
    717. int pointCount_blank = 0;
    718. // 不能填写目标数字的格子的个数
    719. int pointCount_canNotBeTargetNumber = 0;
    720. // 逐个检查该宫的9个位置中还没有填写数字的位置,
    721. // 如果该位置所在的行或列已经存在该数字了,则该位置不能填该数字,假设空位置有n个,检测到不能填该数字的空位置有n-1个,则剩下的一个位置必须填该数字,否则无法判断哪个空位置需要填该数字
    722. BeginIndexRangeVO beginIndexRange = map_gongNum_beginIndexRange.get(targetGongNum);
    723. int beginRowNum = beginIndexRange.getBeginRowNum();
    724. int endRowNum = beginRowNum + 2;
    725. int beginColNum = beginIndexRange.getBeginColNum();
    726. int endColNum = beginColNum + 2;
    727. int rowNum_maybeFind = 0;
    728. int colNum_maybeFind = 0;
    729. for (int rowNum = beginRowNum; rowNum <=endRowNum ; rowNum++) {
    730. for (int colNum = beginColNum; colNum <=endColNum ; colNum++) {
    731. String code = getCode(rowNum, colNum);
    732. PointVO point = map_code_point.get(code);
    733. if( point.getNumber() != null ){
    734. // 该位置已经有数字了
    735. continue;
    736. }
    737. pointCount_blank++;
    738. if( map_rowNum_numbers.get( rowNum ).contains( targetNumber ) ){
    739. // 该位置所在的行已经存在 targetNumber 了
    740. pointCount_canNotBeTargetNumber++;
    741. continue;
    742. }
    743. if( map_colNum_numbers.get( colNum ).contains( targetNumber ) ){
    744. // 该位置所在的列已经存在targetNumber 了
    745. pointCount_canNotBeTargetNumber++;
    746. continue;
    747. }
    748. // 该位置为空,并且所在的行和列都没有出现过 targetNumber,所以该位置可能需要填写 targetNumber
    749. rowNum_maybeFind = rowNum;
    750. colNum_maybeFind = colNum;
    751. }
    752. }
    753. if( ( pointCount_blank - pointCount_canNotBeTargetNumber ) == 1 ){
    754. // 此时找到了填写 targetNumber 的地方了
    755. return fillNumber( rowNum_maybeFind,colNum_maybeFind,targetNumber );
    756. }else {
    757. // 该宫无法确定当前数字需要填写到哪个位置
    758. return false;
    759. }
    760. }
    761. private static String getCode(int rowNum, int colNum) {
    762. return rowNum + "-" + colNum;
    763. }
    764. public static Map points2CodeAndPointMap( List points ){
    765. Map map_code_point = new HashMap<>();
    766. for( PointVO point:points ){
    767. map_code_point.put( point.getCode(),point );
    768. }
    769. return map_code_point;
    770. }
    771. public static void print( ){
    772. System.out.println();
    773. System.out.println( "--------------------------------------------------------------------------------------" );
    774. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    775. for (int colNum = 1; colNum <=9 ; colNum++) {
    776. String code = getCode( rowNum,colNum );
    777. PointVO point = map_code_point.get(code);
    778. if( point.getNumber() == null ){
    779. System.out.print( "-" + " ");
    780. }else {
    781. System.out.print( point.getNumber() + " ");
    782. }
    783. if( colNum % 3 == 0 ){
    784. System.out.print(" ");
    785. }
    786. }
    787. System.out.println();
    788. if( rowNum % 3 ==0 ){
    789. System.out.println();
    790. }
    791. }
    792. }
    793. public static void printPossibles( ){
    794. System.out.println();
    795. System.out.println( "--------------------------------------------------------------------------------------" );
    796. Map map_colNum_maxLen = new HashMap<>();
    797. for (int colNum = 1; colNum <=9; colNum++) {
    798. int maxLen = 1;
    799. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    800. String code = getCode(rowNum, colNum);
    801. PointVO point = map_code_point.get(code);
    802. if( point.getNumber() != null ){
    803. continue;
    804. }
    805. List possibleNumbers = point.getPossibleNumbers();
    806. if( possibleNumbers.size() > maxLen ){
    807. maxLen = possibleNumbers.size();
    808. }
    809. }
    810. maxLen+=2;
    811. map_colNum_maxLen.put( colNum,maxLen );
    812. }
    813. for (int rowNum = 1; rowNum <=9 ; rowNum++) {
    814. for (int colNum = 1; colNum <=9 ; colNum++) {
    815. String code = getCode( rowNum,colNum );
    816. PointVO point = map_code_point.get(code);
    817. String value = null;
    818. if( point.getNumber() == null ){
    819. value = "[" + getStickTogetherFormat( point.getPossibleNumbers() ) + "]";
    820. }else {
    821. value = point.getNumber();
    822. }
    823. int maxLen = map_colNum_maxLen.get( colNum );
    824. if( value.length() < maxLen ){
    825. value += getBlank( maxLen - value.length() );
    826. }
    827. System.out.print( value + " ");
    828. if( colNum % 3 == 0 ){
    829. System.out.print(" ");
    830. }
    831. }
    832. System.out.println();
    833. if( rowNum % 3 ==0 ){
    834. System.out.println();
    835. System.out.println();
    836. }
    837. }
    838. }
    839. private static String getBlank(int count) {
    840. String blank = "";
    841. for (int i = 0; i < count; i++) {
    842. blank += " ";
    843. }
    844. return blank;
    845. }
    846. private static String getStickTogetherFormat(List list) {
    847. if( list == null || list.size() == 0 ){
    848. return "";
    849. }
    850. String str = "";
    851. for( String item:list ){
    852. str += item;
    853. }
    854. return str;
    855. }
    856. public static List initChuPan(int[][] chupan) {
    857. List points = new ArrayList<>();
    858. for (int rowNum = 0; rowNum < 9; rowNum++) {
    859. int[] row = chupan[rowNum];
    860. for (int colNum = 0; colNum <9 ; colNum++) {
    861. int number = row[colNum];
    862. String number_str = null;
    863. if( number > 0 && number < 10 ){
    864. count_fill++;
    865. number_str = String.valueOf( number );
    866. }
    867. points.add( new PointVO(number_str,rowNum+1,colNum+1 ) );
    868. }
    869. }
    870. return points;
    871. }
    872. }

    测试输出样例:

    1. --------------------------------------------------------------------------------------
    2. 1 - 4 - - - - - -
    3. - - - - - 5 - - -
    4. - 6 - - 8 - - 7 3
    5. - - - 8 - 1 9 - -
    6. 6 5 - - - - - - -
    7. - - - 3 - - - - 8
    8. - 2 - - 3 - - - 7
    9. - - - - - 7 1 3 -
    10. 4 7 - - - - 8 9 -
    11. --------------------------------------------------------------------------------------
    12. 1 8 4 - - 3 5 - 9
    13. 7 3 - - - 5 - 8 1
    14. - 6 5 1 8 - - 7 3
    15. 3 4 7 8 - 1 9 - -
    16. 6 5 8 - - - 3 1 -
    17. - 1 - 3 - - 7 - 8
    18. 5 2 1 9 3 8 - - 7
    19. 8 9 6 - - 7 1 3 -
    20. 4 7 3 - 1 - 8 9 -
    21. --------------------------------------------------------------------------------------
    22. 1 8 4 [267] [267] 3 5 [26] 9
    23. 7 3 [29] [246] [2469] 5 [246] 8 1
    24. [29] 6 5 1 8 [249] [24] 7 3
    25. 3 4 7 8 [256] 1 9 [256] [256]
    26. 6 5 8 [247] [2479] [249] 3 1 [24]
    27. [29] 1 [29] 3 [24569] [2469] 7 [2456] 8
    28. 5 2 1 9 3 8 [46] [46] 7
    29. 8 9 6 [245] [245] 7 1 3 [245]
    30. 4 7 3 [256] 1 [26] 8 9 [256]
    31. 通过尝试的方式确定 1行8列 填写数字 ["2"] 都会产生矛盾,所以 1行8列 的正确数字是 6
    32. 通过尝试的方式确定 2行3列 填写数字 ["2"] 都会产生矛盾,所以 2行3列 的正确数字是 9
    33. --------------------------------------------------------------------------------------
    34. 1 8 4 2 7 3 5 6 9
    35. 7 3 9 6 4 5 2 8 1
    36. 2 6 5 1 8 9 4 7 3
    37. 3 4 7 8 5 1 9 2 6
    38. 6 5 8 7 9 2 3 1 4
    39. 9 1 2 3 6 4 7 5 8
    40. 5 2 1 9 3 8 6 4 7
    41. 8 9 6 4 2 7 1 3 5
    42. 4 7 3 5 1 6 8 9 2

  • 相关阅读:
    Java Spring Boot 写 API 接口
    《web课程设计》使用HTML+CSS制作大学生校园二手交易网站
    【2022 小目标检测综述】Towards Large-Scale Small Object Detection: Survey and Benchmarks
    java面试题之 int和Integer的区别
    聊聊并发编程——多线程之AQS
    Typora~阿里云OSS+Typora+PicGo解决方案
    「网络安全」SQL注入攻击的真相
    【C语言步行梯】分支语句if...else、switch详谈
    数据结构<5>二叉树和堆——原理+实现
    缺口的大利润!伦敦银如何使用缺口交易
  • 原文地址:https://blog.csdn.net/heshiyuan1406146854/article/details/133812049