创作背景
数独(sudoku)是源自十八世纪末的瑞士,后在美国发展,并在日本发扬光大的数学智力拼图游戏,拼图是九宫格(即3格宽 x 3格高)的正方形,每一格又细分为一个九宫格,在每一个九宫格里,分别填上1至9的数字,让整个大的九宫格每一列、每一行的数字都不重复,下面是一个数独游戏。
2 | 3 | 5 | ||||||
1 | 9 | 8 | 6 | 7 | ||||
4 | 6 | 5 | 2 | |||||
2 | 4 | 7 | ||||||
1 | 9 | 7 | 3 | 2 | 8 | |||
7 | 6 | 5 | ||||||
6 | 4 | 8 | 5 | |||||
5 | 1 | 3 | 2 | 4 | ||||
4 | 6 | 2 |
让我们开始吧
1.使用js解题
1.1 将数独表翻译成数组
这里我们首先要将表里的数据初始化到数组中哦!
const suzi = Array(81).fill(null)
suzi[0] = 2
suzi[4] = 3
suzi[6] = 5
suzi[10] = 1
suzi[12] = 9
suzi[14] = 8
suzi[15] = 6
suzi[16] = 7
suzi[18] = 4
suzi[20] = 6
suzi[23] = 5
suzi[25] = 2
suzi[28] = 2
suzi[31] = 4
suzi[35] = 7
suzi[36] = 1
suzi[38] = 9
suzi[39] = 7
suzi[41] = 3
suzi[42] = 2
suzi[44] = 8
suzi[45] = 7
suzi[50] = 6
suzi[52] = 5
suzi[55] = 6
suzi[57] = 4
suzi[60] = 8
suzi[62] = 5
suzi[64] = 5
suzi[65] = 1
suzi[66] = 3
suzi[68] = 2
suzi[70] = 4
suzi[74] = 4
suzi[76] = 6
suzi[80] = 2
2. 建立参考值
2.1得出每个小的九宫格可能的数字(所谓的参考值)
2.1.1 九宫格已经存在的有用值——单独的数字
function defaltValues(groupIndex: number, data: TSuzi[], distances: number[] ) {
return distances.map(item => {
const _index = groupIndex + item
return data[_index];
}).filter(value => value);
2.1.2 九宫格中未填的可能值——得到的一个字符串
function referValue(value: number[], index: number, result: string[], config: { groupIndexes: number[], distances: number[], aReferValues: number[]}) {
const { groupIndexes, distances, aReferValues } = config;
const groupIndex = groupIndexes[index]
const str = aReferValues.filter(item => !value.includes(item)).join('')
return distances.reduce((res: string[], item) => {
const index = groupIndex + item;
res[index] = str
return res
}, result)
}
2.2 最终的参考值
function initValues(data: TSuzi[], config: { distances: number[], aReferValues: number[], groupIndexes: number[] }) {
const { distances, aReferValues, groupIndexes } = config
const allValues = groupIndexes.map(groupIndex => defaltValues(groupIndex, data, distances));
const result: string[] = Array(81).fill(null)
allValues.forEach((item, index) => {
referValue(item as number[], index, result, {
groupIndexes,
distances,
aReferValues
});
})
return result
}
3. 列出比较方法
3.1 横向或纵向
function compareRowOrColumn(value: string, rowNums: number[]) {
if (rowNums.length === 9) return value;
return compare(value, rowNums)
}
3.2 每一个小九宫格
function compareCircle(centerIndex: number, data: TSuzi[], distances: number[]) {
const nums = distances.map((dist) => {
return data[centerIndex + dist]
});
if (nums.every((item) => typeof item === 'number')) return;
// 归类 统计字符串出现的次数 得到字符串长度与次数一致的字符串
const obj: any = {}
nums.forEach((item) => {
if (obj[`${item}`]) {
obj[`${item}`]++;
} else {
obj[`${item}`] = 1
}
})
let repeatNums: number[] = [], repeatString: string[] = [];
for (let key in obj) {
if (obj[key] > 1) {
if (key.length === obj[key]) {
repeatNums = repeatNums.concat(key.split('').map((item) => parseInt(item)));
repeatString.push(key)
}
}
}
nums.forEach((item, index, arr) => {
if (item && +item > 10) {
const result = compare(item as string, arr as number[], {
repeatNums,
repeatString
});
if (typeof result === "number") {
const _index = centerIndex + distances[index]
data[_index] = result
arr[index] = result;
}
}
})
}
3.3 具体的比较方法
function compare(value: string, nums: number[], others?: {
repeatNums: number[];
repeatString: string[];
}) {
if (others) {
nums = Array.from(new Set(nums.concat(others.repeatNums))) ;
}
if (others && others.repeatString.includes(value)) return value;
const res = nums.reduce((_res, row) => {
if (+row > 10) return _res;
return _res.replace('' + row, '');
}, value);
return res.length > 1? `${res}` : +res
}
4. 得到横/纵向存在的数字
function rowsAndColumnsData(data: TSuzi[]) {
const rows: number[][] = [], columns: number[][] = []
data.forEach((item, index) => {
const columnIndex = index % 9, rowIndex = Math.floor(index / 9);
if (typeof item === "number") {
if (columns[columnIndex]) {
columns[columnIndex].push(item)
} else {
columns[columnIndex] = [item]
}
if (rows[rowIndex]) {
rows[rowIndex].push(item)
} else {
rows[rowIndex] = [item]
}
}
})
return {
rows,
columns
}
}
5. 填空空
function compareAndFlat(data: TSuzi[], rows: number[][], columns: number[][], initValues: string[], config: {
groupIndexes: number[],
distances: number[],
}) {
const { groupIndexes, distances } = config;
const _rows = [...rows], _columns = [...columns];
const _data = data.map((su, index) => {
const _columnIndex = index % 9, _rowIndex = Math.floor(index / 9);
const value = su;
let _value: string | number = initValues[index];
if (!value || +value > 10) {
_value = compareRowOrColumn(value as string || _value , _rows[_rowIndex])
if (typeof _value === 'string') {
_value = compareRowOrColumn(_value, _columns[_columnIndex])
}
if (typeof _value === 'number') {
_rows[_rowIndex].push(_value)
_columns[_columnIndex].push(_value)
}
return _value
}
return su
})
groupIndexes.forEach(group => {
compareCircle(group, _data, distances)
})
return _data
}
最终执行
function sudoku(data: TSuzi[]) {
const aReferValues = [1, 2, 3, 4, 5, 6, 7, 8, 9], groupIndexes = [10, 13, 16, 37, 40, 43, 64, 67, 70], distances = [-10, -9, -8, -1, 0, 1, 8, 9, 10];
const initValue = initValues(data, { distances, groupIndexes, aReferValues });
const { rows, columns } = rowsAndColumnsData(data);
const _data = compareAndFlat(data, rows, columns, initValue, {
groupIndexes,
distances
})
if (_data.some((item) => typeof item !== 'number')) {
return sudoku(_data)
}
return _data
}
结果成功得到 🎉🎉🎉🎉🎉
2 | 9 | 7 | 6 | 3 | 4 | 5 | 8 | 1 |
5 | 1 | 3 | 9 | 2 | 8 | 6 | 7 | 4 |
4 | 8 | 6 | 1 | 7 | 5 | 9 | 2 | 3 |
6 | 2 | 5 | 8 | 4 | 9 | 1 | 3 | 7 |
1 | 4 | 9 | 7 | 5 | 3 | 2 | 6 | 8 |
7 | 3 | 8 | 2 | 1 | 6 | 4 | 5 | 9 |
3 | 6 | 2 | 4 | 9 | 7 | 8 | 1 | 5 |
9 | 5 | 1 | 3 | 8 | 2 | 7 | 4 | 6 |
8 | 7 | 4 | 5 | 6 | 1 | 3 | 9 | 2 |
如果没有得到结果😔😔😔😔😔,请你检查一下你的数字是不是在正确的位置!!!