数独暴力猜解

思路

  1. 验证已填格子是否合法,非法则回 false
  2. 选一个空格子,如果没有空格子则返回 true
  3. 对空格子进行填数,依次尝试 1-9
  4. 猜解剩余格子,重复步骤 1
  5. 步骤 4 猜解成功则返回 true,否则回到步骤 3 尝试下一个填数
  6. 1-9 均返回 false,则返回 false

实现

  1. 约定

    • 使用 9 x 9 的二维数组存储所有格子
    • 每个格子维护一个可填数的 mask,采用 bitmap 方式存储,所以 0b111_111_111 可填 1-9,0b000_000_000 无可填数
    • 为其中一个格子填数时,更新其所对应的行、列、区块上的每个格子的 mask
    • 当有格子的 mask 为 0b000_000_000 时,数独不可解
    • 当行、列、区块上 mask 有冲突时,数独不可解
    • 因涉及递归,所以每个格子实际存储为一个 mask 栈
  2. 代码
    // 测试
    public class SudoKu {
        public static String sudokuString(BiFunction<Integer, Integer, Integer> supplier) {
            StringBuilder str = new StringBuilder("   _0_1_2_3_4_5_6_7_8\n");
            for (int r = 0; r < 9; r++) {
                str.append(r).append(" | ");
                for (int c = 0; c < 9; c++) {
                    int v = supplier.apply(r, c);
                    str.append(v).append(" ");
                }
                str.append(‘\n‘);
            }
            return str.toString();
        }
    
        public static void main(String[] args) {
    
            List<Integer> inputs = Arrays.asList(821007900, 7000000, 400003000, 908040000, 000000001, 374201000, 160040, 60000000,
                    709008600);
    
            int[][] cells = new int[9][9];
    
            for (int rowIndex = 0; rowIndex < inputs.size(); rowIndex++) {
                int input = inputs.get(rowIndex);
                for (int ci = 8; input > 0; input /= 10, ci--) {
                    cells[rowIndex][ci] = input % 10;
                }
            }
    
            SudoKuResolver rc = new SudoKuResolver(cells);
    
            System.out.printf("Input >>\n%s", sudokuString(rc::getCellValue));
            System.out.printf("resolving...\n");
            long s = System.currentTimeMillis();
            boolean resolved = rc.resolve();
            long e = System.currentTimeMillis();
            long cost = e - s;
            System.out.printf("finished, cost: %s\n" , cost);
    
            System.out.printf("Output >>\n%s", resolved ? sudokuString(rc::getCellValue) : "不可解");
        }
    }
    
    // 解析
    class SudoKuResolver {
        public final static int[] MIN_INDEX = new int[] { 0, 0, 0, 3, 3, 3, 6, 6, 6 };
        public final static int[] MAX_INDEX = new int[] { 2, 2, 2, 5, 5, 5, 8, 8, 8 };
    
        private static final Logger LOGGER = LoggerFactory.getLogger(SudoKuResolver.class);
    
        private final CellMaskBucket[][] cellBuckets;
    
        private int depth;
    
        public SudoKuResolver(int[][] cells) {
            this.cellBuckets = new CellMaskBucket[9][9];
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    cellBuckets[i][j] = new CellMaskBucket(i, j);
                    cellBuckets[i][j].setMask(0b111_111_111);
                }
            }
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    int v = cells[i][j];
                    if (v != 0) {
                        CellMaskBucket bucket = cellBuckets[i][j];
                        bucket.setMask(toMask(cells[i][j]));
                    }
                }
            }
        }
        public boolean resolve() {
    
            // 是否有效
            if (!isValid()) {
                return false;
            }
    
            // 选出格子
            CellMaskBucket cell = pickCell();
            if (cell == null) {
                return true;
            }
    
            // 使用调用栈保存拆解步骤
            int m = cell.getMask();
            if (LOGGER.isDebugEnabled()) {
                int trys[] = computeTrys(m);
                LOGGER.debug("{}: try [ {} ]", cell, Strings.join(",", trys));
            }
    
            while (Integer.bitCount(m) > 0) {
                int zc = Integer.numberOfTrailingZeros(m);
                int tv = 1 << zc;
                push();
    
                setCellMask(cell, tv);
    
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("{}: try {}\n{}", cell, zc + 1, SudoKu.sudokuString(this::getCellValue));
                }
    
                if (resolve()) {
                    return true;
                } else {
                    m &= ~tv;
                    pop();
    
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("{}: not {}", cell, zc + 1);
                    }
                }
            }
            // 都不可解,返回 false
            return false;
        }
    
        void push() {
            if (++depth > 80) {
                throw new ArrayIndexOutOfBoundsException(depth--);
            }
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    cellBuckets[i][j].bucket[depth] = cellBuckets[i][j].bucket[depth - 1];
                }
            }
        }
    
        void pop() {
            if (--depth < 0) {
                throw new ArrayIndexOutOfBoundsException(depth++);
            }
        }
    
        void setCellMask(CellMaskBucket bucket, int mask) {
            bucket.setMask(mask);
            rcbTravel(bucket.row, bucket.col, (i, j) -> {
                int om = cellBuckets[i][j].getMask();
                if (Integer.bitCount(om) > 1) {
                    this.cellBuckets[i][j].setMask(om & (~mask));
                }
            });
        }
    
        void rcbTravel(int r, int c, BiConsumer<Integer, Integer> act) {
            // 行
            for (int i = 0; i < 9; i++) {
                if (i == c) {
                    continue;
                }
                act.accept(r, i);
            }
            // 列
            for (int i = 0; i < 9; i++) {
                if (i == r) {
                    continue;
                }
                act.accept(i, c);
            }
            // 区块
            for (int i = MIN_INDEX[r]; i <= MAX_INDEX[r]; i++) {
                for (int j = MIN_INDEX[c]; j <= MAX_INDEX[c]; j++) {
                    if (i == r || j == c) {
                        continue;
                    }
                    act.accept(i, j);
                }
            }
    
        }
    
        CellMaskBucket pickCell() {
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    CellMaskBucket cs = cellBuckets[i][j];
                    if (Integer.bitCount(cs.getMask()) > 1) {
                        return cs;
                    }
                }
            }
            return null;
        }
    
        boolean isValid() {
            int rms, cms, re, ce, bms, be;
            for (int i = 0; i < 9; i++) {
                rms = cms = re = ce = bms = be = 0;
                for (int j = 0; j < 9; j++) {
                    // 行
                    int rm = cellBuckets[i][j].getMask();
                    if (Integer.bitCount(rm) == 1) {
                        re += 1;
                        rms += rm;
                        if (Integer.bitCount(rms) != re) {
                            return false;
                        }
                    }
    
                    // 列
                    int cm = cellBuckets[j][i].getMask();
                    if (Integer.bitCount(cm) == 1) {
                        ce += 1;
                        cms += cm;
                        if (Integer.bitCount(cms) != ce) {
                            return false;
                        }
                    }
    
                    // block
                    int r = (i / 3) * 3 + (j / 3);
                    int c = (i % 3) * 3 + (j % 3);
                    int bm = cellBuckets[r][c].getMask();
                    if (Integer.bitCount(bm) == 1) {
                        be += 1;
                        bms += bm;
                        if (Integer.bitCount(bms) != be) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    
        public int getCellValue(int r, int c) {
            int m = this.cellBuckets[r][c].getMask();
            return toDecimal(m);
        }
    
        class CellMaskBucket {
    
            private final int row, col;
            private final int[] bucket = new int[81];
    
            CellMaskBucket(int row, int col) {
                this.row = row;
                this.col = col;
            }
    
            void setMask(int v) {
                bucket[depth] = v;
            }
    
            int getMask() {
                return bucket[depth];
            }
    
            @Override
            public String toString() {
                return "(" + row + ", " + col + ")";
            }
        }
        static int toDecimal(int mask) {
            if (Integer.bitCount(mask) == 1) {
                return Integer.numberOfTrailingZeros(mask << 1);
            }
            return 0;
        }
    
        static int toMask(int dec) {
            return dec == 0 ? 0b111_111_111 : 1 << (dec - 1);
        }
    
        static int[] computeTrys(int m) {
            int[] r = new int[Integer.bitCount(m)];
            for (int i = 0; i < r.length; i++) {
                int z = Integer.numberOfTrailingZeros(m);
                r[i] = z + 1;
                m >>= r[i];
                m <<= r[i];
            }
            return r;
        }
    }

原文地址:https://www.cnblogs.com/realhyx/p/10354368.html

时间: 2024-10-11 07:21:42

数独暴力猜解的相关文章

FTP弱口令猜解【python脚本】

ftp弱口令猜解 python脚本: #! /usr/bin/env python # _*_ coding:utf-8 _*_ import ftplib,time username_list=['root','ftp','admin'] password_list=['root','123','ftp','oracle'] def ftp(ip,port=21): for username in username_list: user =username.rstrip() for passw

SQL注入思路与手工猜解大进阶

什么叫SQL注入? 许多网站程序在编写时,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患.用户可以提交一段数据库查询代码,(一般是在浏览器地址栏进行,通过正常的www端口访问)根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入. SQL注入的思路 思路最重要.其实好多人都不知道SQL到底能做什么呢?这里总结一下SQL注入入侵的总体的思路: 1. SQL注入漏洞的判断,即寻找注入点 2. 判断后台数据库类型 3. 确定XP_CMDSHELL

猜解数据库(MYSQL)信息

http://43.247.91.228:84/Less-1/?id=1' and if (length(database())=8,sleep(5),0) --+ 注:http://43.247.91.228:84/Less-1/为靶场地址,发送该请求,如果数据库名的长度为8则响应会延迟5秒,否则会立 即得到响应(网状状况没那么糟糕的情况下).以下代码为使用SLEEP()延迟注入猜解数据库名长度的PYTHON脚本 import requests url1 = "http://43.247.91

暴力法解凸包

给定平面上一系列的点,用暴力法求解它们的凸包,此算法比普通的暴力法要优化,用新找到的极点去寻找下一个极点.此算法不能用于任何两个点在一直线上的情况. 输入 ConvexHull.txt 7,810,1714,1415,2316,1217,322,1724,426,18 C代码 1 /*brute force solution to convex hull problem */ 2 /*only limited to there is no point on the same line with

数独暴力破解代码(code by unity)

using System.Collections.Generic;using UnityEditor;using UnityEngine; public class testSudo{ class SudokuItem { public bool isNormal = false; //固定数字 不需要检测期望值 public int value; //该item存的值 public List<int> expectValueList; //该item是否可用1-9 1:可用 0:不可用 }

数独暴力遍历代码

还是递归大法好. #include <stdio.h> #include <stdlib.h> #include <assert.h> #define CELL_DEEP 3 #define MATRIX_DEEP 9 #define MATRIX_NUM (MATRIX_DEEP*MATRIX_DEEP) #define FULL_BITMAP 0x1FF #define TEST_BITMAP(bitmap, bit) (bitmap) & (1 <&

数独--暴力、剪枝搜索

import java.util.Scanner; public class Main { static int arr[][]; static Scanner in = new Scanner(System.in); public static void main(String[] args) { arr = new int[9][9]; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { arr[i][j] = in.ne

详解SQL盲注测试高级技巧

写在前面: 这篇文章主要写了一些加快盲注速度的技巧和盲注中比较精巧的语句,虽然注入并不是什么新技术了.但是数据库注入漏洞依然困扰着每一个安全厂商,也鞭策着每一个安全从业者不断前进. 正文: 首先来简单介绍一下盲注,盲注是不能通过直接显示的途径来获取数据库数据的方法.在盲注中,攻击者根据其返回页面的不同来判断信息(可能是页面内容的不同,也可以是响应时间不同).一般情况下,盲注可分为三类. Booleanbase Timebase Errorbase 其中第一类Boolean就是我们最常接触到的普通

详解强大的SQL注入工具——SQLMAP

1. 前言  Windows下的注入工具好的又贵,免费的啊D.明小子等又不好用,我们根本没必要花 时间去找什么破解的havij.pangolin什么的,特别是破解的工具很可能被绑了木马.其实 Linux下的注入工具也是非常强大的,不过分的说,可以完全取代Windows下面的所有注入 工具.  就如backtrack系统里面就有非常丰富的注入工具,对MSSQL.MYSQL.oracle等各种 数据库的应有尽有了,而且这些工具都是免费的,并且是开放源代码的,我们还可以用来修 改为合适自己使用的注入工