一切罪恶的来源是昨晚睡前玩了一把数独,找虐的选了个最难的模式,做了一个多小时才做完,然后就睡不着了..........程序员不能受这委屈,今天咋样也得把这玩意儿破解了
package main
import (
"fmt"
"os"
)
func shuzu_print(shuzu [9][9]int) {
fmt.Println("------------------------")
for i := 0; i < 9; i++ {
fmt.Println(shuzu[i])
}
}
func main() {
shuzu := [9][9]int{ // 初始化数独
{2, 0, 0, 0, 4, 0, 0, 1, 0},
{6, 1, 7, 0, 0, 3, 0, 0, 5},
{0, 5, 0, 0, 0, 0, 0, 0, 3},
{0, 0, 9, 0, 3, 6, 5, 7, 8},
{0, 6, 5, 0, 8, 0, 0, 4, 2},
{0, 0, 8, 4, 1, 5, 0, 6, 9},
{0, 0, 0, 3, 0, 0, 0, 9, 0},
{0, 9, 2, 0, 7, 0, 8, 3, 0},
{8, 3, 0, 9, 2, 0, 0, 5, 7},
}
shuzu_print(shuzu)// 打印
handle(shuzu)
}
func handle(shuzu [9][9]int) {
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if i == 8 && j == 8 { // 已经填完了,这个exit 是为了打断所有尝试
shuzu_print(shuzu)
os.Exit(1)
return
}
if shuzu[i][j] != 0 {
continue
}
set := make(map[int]struct{}) // map 实现set,存储同行,同列,所在小九宫格已经存在的数字
for k := 0; k < 9; k++ {
if shuzu[i][k] != 0 {
set[shuzu[i][k]] = struct{}{}
}
}
for k := 0; k < 9; k++ {
if shuzu[k][j] != 0 {
set[shuzu[k][j]] = struct{}{}
}
}
i_3 := i / 3
j_3 := j / 3
for ii := i_3 * 3; ii < (i_3+1)*3; ii++ {
for jj := j_3 * 3; jj < (j_3+1)*3; jj++ {
if shuzu[ii][jj] != 0 {
set[shuzu[ii][jj]] = struct{}{}
}
}
}
if len(set) == 9 { // 如果同行,同列,所在小九宫格1-9均存在了,那么发生矛盾,此支线走不下去
return
}
for kk := 1; kk < 10; kk++ {
if _, ok := set[kk]; !ok {
shuzu[i][j] = kk
handle(shuzu)// 同行,同列,所在小九宫格1-9不存在的数字均要进行尝试
}
}
}
}
}
跑了一下没问题,而且是秒出结果,但是很悲剧,下面这个例子(就是我昨晚那个关卡的例子)就不行了,二十分钟都跑不完,调试了一下,0行1列的位置可以填3,8,9(正解是8),但是拿3去尝试的时候,一直到第二行填完才出现矛盾,并且这个过程大概花了五六秒的时间,也太暴力了吧
shuzu := [9][9]int{
{1, 0, 4, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 5, 0, 0, 0},
{0, 6, 0, 0, 8, 0, 0, 7, 3},
{0, 0, 0, 8, 0, 1, 9, 0, 0},
{6, 5, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 3, 0, 0, 0, 0, 8},
{0, 2, 0, 0, 3, 0, 0, 0, 7},
{0, 0, 0, 0, 0, 7, 1, 3, 0},
{4, 7, 0, 0, 0, 0, 8, 9, 0},
}
人在做这个的时候,会先把容易填的填完,不会按照顺序说一定要先填哪一个,尝试在这儿进行如下优化
package main
import (
"fmt"
"time"
)
func shuzu_print(shuzu [9][9]int) {
fmt.Println("------------------------")
for i := 0; i < 9; i++ {
fmt.Println(shuzu[i])
}
}
var num int = 0 // 记录一下函数执行的次数
func main() {
shuzu := [9][9]int{
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{5, 9, 0, 8, 0, 0, 7, 0, 0},
{0, 0, 0, 0, 2, 1, 8, 0, 0},
{0, 3, 7, 0, 0, 0, 0, 0, 0},
{0, 5, 0, 7, 9, 0, 0, 0, 0},
{0, 0, 0, 0, 3, 0, 1, 8, 0},
{0, 0, 5, 0, 0, 2, 0, 0, 0},
{8, 1, 0, 0, 0, 0, 0, 4, 0},
{0, 0, 6, 0, 8, 0, 9, 0, 3},
}
shuzu_print(shuzu)
t1 := time.Now()
fmt.Println(t1)
result := handle2(shuzu)
shuzu_print(result)
fmt.Println(time.Now().Sub(t1)) // 记录一下花费的时间
}
type Left struct { // 定义一个结构体,表示i行j列剩余可以填的数字
i int
j int
len int
left []int
}
func check(shuzu [9][9]int) bool {// 检查数组有没有填完
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if shuzu[i][j] == 0 {
return false
}
}
}
return true
}
// 这个里面还用了goto,loop ,让函数有个返回
func handle2(shuzu [9][9]int) [9][9]int {
num += 1
left_map := map[int]Left{}
if check(shuzu) {
println("hhhhhhhhhhhhhhhh")// 填完了,真开心hhhhh
shuzu_print(shuzu)
fmt.Println(num) // 看一下该函数执行了多少次
return shuzu
}
new_shuzu := shuzu
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if shuzu[i][j] != 0 {
continue
}
set := make(map[int]struct{}) // 同行同列同小组已经存在的数字
for k := 0; k < 9; k++ {
if shuzu[i][k] != 0 {
set[shuzu[i][k]] = struct{}{}
}
}
for k := 0; k < 9; k++ {
if shuzu[k][j] != 0 {
set[shuzu[k][j]] = struct{}{}
}
}
i_3 := i / 3
j_3 := j / 3
for ii := i_3 * 3; ii < (i_3+1)*3; ii++ {
for jj := j_3 * 3; jj < (j_3+1)*3; jj++ {
if shuzu[ii][jj] != 0 {
set[shuzu[ii][jj]] = struct{}{}
}
}
}
left_len := 9 - len(set)
if left_len == 0 {
goto Loop // 出现矛盾的时候,跳出执行
}
if left_len == 1 { // 只剩一个可填,直接处理
for kk := 1; kk < 10; kk++ {
if _, ok := set[kk]; !ok {
shuzu[i][j] = kk
new_shuzu = handle2(shuzu)
goto Loop// 直接跳出循环了
}
}
} else {
_, ok := left_map[left_len]
if !ok {
left_num := []int{}
for kk := 1; kk < 10; kk++ {
if _, ok := set[kk]; !ok {
left_num = append(left_num, kk)
}
}
left_map[left_len] = Left{i, j, left_len, left_num} //对于每种剩余可填长度,记录一个即可
}
}
}
}
for k := 2; k < 10; k++ { // 依次找剩余可填长度为2,3,4....,找到一个即可
left, ok := left_map[k]
if ok {
for _, value := range left.left {
shuzu[left.i][left.j] = value
new_shuzu = handle2(shuzu)
if check(new_shuzu) {
goto Loop
}
}
break
}
}
Loop:
// fmt.Println("跳出循环")
return new_shuzu
}
运行结果:函数执行3760次,10ms运行完成,基本满意,终于不用我想破脑壳做一个小时了
问题及优化