说明
单宫行列法:当前宫内,一个候选数字可能出现的所有空白格位置,都在同一行中,那么与其所在行的另外两宫同行中,不能出现该数字,出现则剔除。
图片
如上图所有,左下角第七宫中,第8行中,候选数字1只能在同一行中,就可以排除第9宫中同一行的有候选数字1的情况。故能确定第8行第7列数字为5,第8行,第8列的数字为2。
图片取自App Sudo Cool
算法代码
GridXYCalc
package com.suduku.calc;
import com.suduku.entity.Box;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 单宫行列法 <br/>
* 测试数据:DataConstant.XING_02_10
*/
public class GridXYCalc extends AbstractCalc {
@Override
Box solve() {
Set<Map.Entry<Integer, List<Box>>> entries = getGMap().entrySet();
// 遍历所有宫
for(Map.Entry<Integer, List<Box>> entry : entries) {
// 遍历所有候选数字
for(Integer n : Box.INIT_LIST) {
// 获取当前宫中候选数字为n的单元格列表
List<Box> nList = entry.getValue().stream().filter(b -> b.isBlank() && b.getCList().contains(n)).collect(Collectors.toList());
// 如果存在候选数字n的空白格
if(!nList.isEmpty()) {
// 获取这些单元格的横坐标,判断是否在同一行中
Set<Integer> xSet = nList.stream().map(Box::getX).collect(Collectors.toSet());
if(xSet.size() == 1) {
// 获取该行中,非当前宫的单元格,如果候选数字中存在n,则剔除
Integer x = xSet.iterator().next();
List<Box> oxList = getXList(x).stream().filter(b -> b.isBlank() && b.getG() != entry.getKey() && b.getCList().contains(n)).collect(Collectors.toList());
for(Box box : oxList) {
getListener().sendMsg("通过所在【宫:%d,行:%d】\t确定移除数字【%d】\n", entry.getKey() + 1, x + 1, n);
box.getCList().remove(n);
return box;
}
}
// 获取这些单元格的纵坐标,判断是否在同一列中
Set<Integer> ySet = nList.stream().map(Box::getY).collect(Collectors.toSet());
if(ySet.size() == 1) {
// 获取该列中,非当前宫的单元格,如果候选数字中存在n,则剔除
Integer y = ySet.iterator().next();
List<Box> oyList = getYList(y).stream().filter(b -> b.isBlank() && b.getG() != entry.getKey() && b.getCList().contains(n)).collect(Collectors.toList());
for(Box box : oyList) {
getListener().sendMsg("通过所在【宫:%d,列:%d】\t确定移除数字【%d】\n", entry.getKey() + 1, y + 1, n);
box.getCList().remove(n);
return box;
}
}
}
}
}
return null;
}
}
输出结果
尝试【 单宫行列法 】
通过所在【宫:4,列:1】 确定移除数字【2】
确认位置【行:2,列:1】 值为:【】 候选值为:【1,3,6,7,8】
9 ( ) 0 (235 ) 0 (356 ) | 1 ( ) 0 (23 ) 0 (236 ) | 7 ( ) 8 ( ) 4 ( ) |
{0}(13678 ) 0 (238 ) 0 (13678 ) | 4 ( ) 0 (238 ) 0 (236789 ) | 0 (19 ) 0 (139 ) 5 ( ) |
0 (1378 ) 0 (38 ) 4 ( ) | 0 (3789 ) 5 ( ) 0 (3789 ) | 6 ( ) 0 (139 ) 2 ( ) |
0 (235 ) 6 ( ) 0 (359 ) | 0 (2359 ) 7 ( ) 0 (2359 ) | 8 ( ) 4 ( ) 1 ( ) |
0 (2578 ) 1 ( ) 0 (578 ) | 0 (258 ) 4 ( ) 0 (258 ) | 3 ( ) 6 ( ) 9 ( ) |
0 (38 ) 4 ( ) 0 (389 ) | 6 ( ) 1 ( ) 0 (389 ) | 2 ( ) 5 ( ) 7 ( ) |
4 ( ) 0 (3589 ) 2 ( ) | 0 (3578 ) 0 (38 ) 0 (3578 ) | 0 (159 ) 0 (179 ) 6 ( ) |
0 (1568 ) 7 ( ) 0 (1568 ) | 0 (258 ) 9 ( ) 4 ( ) | 0 (15 ) 0 (12 ) 3 ( ) |
0 (35 ) 0 (359 ) 0 (359 ) | 0 (2357 ) 6 ( ) 1 ( ) | 4 ( ) 0 (279 ) 8 ( ) |
绑定算法
CalcEnum
GRID_XY(GridXYCalc.class, "单宫行列法", "单宫行列法()"),
测试数据
DataConstant
public static final String XING_02_10 = "900100080000400000004050600060070800010000009000600250402000000070090003000001408";
优化内容
AbstractCalc
添加便捷方法
/**
* 功能描述: 算法枚举 <br/>
*
* @return "com.suduku.calc.enums.CalcEnum"
*/
public CalcEnum getCalcEnum() {
return CalcEnum.indexOf(this.getClass());
}
/**
* 功能描述: 方便使用,减少算法实现中的获取变量太长的写法 <br/>
*
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getBoxList() {
return this.sudo.getBoxList();
}
/**
* 功能描述: 方便使用 <br/>
*
* @return "com.suduku.listener.SudoListener"
*/
protected SudoListener getListener() {
return this.sudo.getListener();
}
/**
* 功能描述: 方便使用 <br/>
*
* @return "java.util.Map<java.lang.Integer,java.util.List<com.suduku.entity.Box>>"
*/
protected Map<Integer, List<Box>> getXMap() {
return this.sudo.getXMap();
}
/**
* 功能描述: 方便使用 <br/>
*
* @return "java.util.Map<java.lang.Integer,java.util.List<com.suduku.entity.Box>>"
*/
protected Map<Integer, List<Box>> getYMap() {
return this.sudo.getYMap();
}
/**
* 功能描述: 方便使用 <br/>
*
* @return "java.util.Map<java.lang.Integer,java.util.List<com.suduku.entity.Box>>"
*/
protected Map<Integer, List<Box>> getGMap() {
return this.sudo.getGMap();
}
/**
* 功能描述: 方便使用,获取当前单元格所在行的单元格集合 <br/>
*
* @param box 单元格
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getXList(Box box) {
return getXMap().get(box.getX());
}
/**
* 功能描述: 方便使用,获取当前单元格所在行的单元格集合 <br/>
*
* @param x 行坐标
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getXList(Integer x) {
return getXMap().get(x);
}
/**
* 功能描述: 方便使用,获取当前单元格所在列的单元格集合 <br/>
*
* @param box 单元格
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getYList(Box box) {
return getYMap().get(box.getY());
}
/**
* 功能描述: 方便使用,获取当前单元格所在列的单元格集合 <br/>
*
* @param y 列坐标
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getYList(Integer y) {
return getYMap().get(y);
}
/**
* 功能描述: 方便使用,获取当前单元格所在宫的单元格集合 <br/>
*
* @param box 单元格
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getGList(Box box) {
return getGMap().get(box.getG());
}
/**
* 功能描述: 方便使用,获取当前单元格所在宫的单元格集合 <br/>
*
* @param g 宫坐标
* @return "java.util.List<com.suduku.entity.Box>"
*/
protected List<Box> getGList(Integer g) {
return getGMap().get(g);
}
CalcEnum
优化indexOf获取方法
package com.suduku.calc.enums;
import com.suduku.calc.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
/**
* 功能描述: 算法枚举 <br/>
*
*/
@Getter
@AllArgsConstructor
public enum CalcEnum {
/***/
ONLY_NUM(OnlyNumCalc.class, "唯余法", "唯一余数法(当前单元格中,候选数字只有一个)"),
ONLY_BOX(OnlyBoxCalc.class, "摒除法", "摒除法(数字在所在行或列或宫中,只有一个空格能够填写,则确定是唯一数字)"),
GRID_XY(GridXYCalc.class, "单宫行列法", "单宫行列法()"),
;
private static final Map<Class<? extends AbstractCalc>, CalcEnum> CE_MAP = new HashMap<>(CalcEnum.values().length);
static {
for(CalcEnum ce : CalcEnum.values()) {
CE_MAP.put(ce.getClazz(), ce);
}
}
/**
* 功能描述: 通过类,获取枚举 <br/>
*
* @param clazz 类
* @return "com.suduku.calc.enums.CalcEnum"
*/
public static CalcEnum indexOf(Class<? extends AbstractCalc> clazz) {
return CE_MAP.get(clazz);
}
private Class<? extends AbstractCalc> clazz;
private String name;
private String msg;
}
SudoPrintImpl
输出优化,在未确定数字的情况下,输出提示空,而不是0
@Override
public void change(List<Box> list, Box b) {
sendMsg("确认位置【行:%d,列:%d】\t值为:【%s】\t候选值为:【%s】\n",
b.getX() + 1, b.getY() + 1, b.getV() == 0 ? "" : b.getV(),
b.getCList().stream().map(String::valueOf).collect(Collectors.joining(",")));
SudoUtil.print(list, b);
}
总结
通过现有的算法,可以解出该数独。 最终结果:
尝试【 唯余法 】
确认位置【行:9,列:8】 值为:【7】 候选值为:【】
9 ( ) 5 ( ) 6 ( ) | 1 ( ) 2 ( ) 3 ( ) | 7 ( ) 8 ( ) 4 ( ) |
1 ( ) 2 ( ) 7 ( ) | 4 ( ) 8 ( ) 6 ( ) | 9 ( ) 3 ( ) 5 ( ) |
8 ( ) 3 ( ) 4 ( ) | 9 ( ) 5 ( ) 7 ( ) | 6 ( ) 1 ( ) 2 ( ) |
2 ( ) 6 ( ) 5 ( ) | 3 ( ) 7 ( ) 9 ( ) | 8 ( ) 4 ( ) 1 ( ) |
7 ( ) 1 ( ) 8 ( ) | 5 ( ) 4 ( ) 2 ( ) | 3 ( ) 6 ( ) 9 ( ) |
3 ( ) 4 ( ) 9 ( ) | 6 ( ) 1 ( ) 8 ( ) | 2 ( ) 5 ( ) 7 ( ) |
4 ( ) 8 ( ) 2 ( ) | 7 ( ) 3 ( ) 5 ( ) | 1 ( ) 9 ( ) 6 ( ) |
6 ( ) 7 ( ) 1 ( ) | 8 ( ) 9 ( ) 4 ( ) | 5 ( ) 2 ( ) 3 ( ) |
5 ( ) 9 ( ) 3 ( ) | 2 ( ) 6 ( ) 1 ( ) | 4 ( ){7}( ) 8 ( ) |
============数独解题完成,尝试次数为:69============