數(shù)獨(dú)自動生成算法

前言

半個月前成功嘗試完成了數(shù)獨(dú)的求解算法。但是總感覺有什么事情還沒有完成。思來想去,原來是只是求解了數(shù)獨(dú),但是數(shù)獨(dú)不知從何而來。是否可以通過算法自動生成一個數(shù)獨(dú)?恰逢清明三天假,在節(jié)日來臨之前,用一晚上進(jìn)行了實(shí)現(xiàn)。

數(shù)獨(dú)規(guī)則

首先看一下數(shù)獨(dú)


屏幕快照 2018-03-25 下午6.30.52.png

數(shù)獨(dú)的規(guī)則比較簡單:

  • 每一行包括了1到9的數(shù)字,并且不能重復(fù)。
  • 每一列包括了1到9的數(shù)字,并且不能重復(fù)。
  • 每一組包括了1到9的數(shù)字,并且不能重復(fù)。

數(shù)獨(dú)題的基本要求:
數(shù)獨(dú)題的要求為數(shù)獨(dú)要有唯一解,表現(xiàn)在:

  • 已填滿的空格不能與它所在的行、列、組重合。
  • 采用遍歷的方式能夠找到數(shù)獨(dú)的解,并且解是唯一的。

生成數(shù)獨(dú)題的步驟和流程圖

步驟

  • 步驟一、生成一個所有單元都是空的空數(shù)獨(dú)
  • 步驟二、隨機(jī)選擇一個空單元,找到它的所有可能解
  • 步驟三、遍歷空單元的每個可能解。將每個解填入,求的填入后數(shù)獨(dú)的解的個數(shù)。
  • 步驟四、如果有可能解的填入之后數(shù)獨(dú)解個數(shù)為1,則此填入此可能解之后的數(shù)獨(dú)即為生成的數(shù)獨(dú),否則,隨機(jī)選擇一個可能解,進(jìn)行步驟二。
    代碼

獲取數(shù)獨(dú)的核心代碼如下:

         // 獲取空數(shù)獨(dú)  對應(yīng)步驟一    
        char[] charArray = new char[81];
        for (int i = 0; i < 81; i++) {
            charArray[i] = '0';
        }
        TreeSet<Integer> indexs = new TreeSet<>();
        TreeSet<Object> currentTreeSet = new TreeSet<>();
        int time=0;
        a: do {
           //隨機(jī)選擇一個空單元,求出它的可能解 對應(yīng)步驟二
            int index = (int) (Math.random() * 81);
            if (indexs.contains(index)) {
                continue;
            }
            indexs.add(index);
            initData(charArray);
            int list = index / 9;
            int row = index % 9;
            System.out.println("list:"+list+"row"+row+"index"+index);
            SoduNode currentNode = soduNodes[list][row];
            Integer[] suitValue = currentNode.getSuitValue();


         //   遍歷空單元的每個可能解。將每個解填入,求的填入后數(shù)獨(dú)的解的個數(shù)。對應(yīng)步驟三
            boolean hasOnlySolution = false;
            currentTreeSet.clear();

            b: for (int i = 0; i < suitValue.length; i++) {
                currentNode.value = suitValue[I];
                charArray[index] = (char) ('0' + suitValue[I]);
      
                int solutionNum = getSolutionNum(charArray);
                System.out.println("solutionNum"+solutionNum+"");
 // 如果有可能解的填入之后數(shù)獨(dú)解個數(shù)為1,則此填入此可能解之后的數(shù)獨(dú)即為生成的數(shù)獨(dú) 對應(yīng)步驟四
                if (solutionNum == 1) {
                    break a;

                } else if (solutionNum > 1) {
                    currentTreeSet.add(suitValue[I]);
                }
            }
            Integer[] solutions = new Integer[currentTreeSet.size()];
            currentTreeSet.toArray(solutions);
 // 如果未有可能解的填入之后數(shù)獨(dú)解個數(shù)為1,隨機(jī)選擇一個可能解,進(jìn)行步驟二。
            int randomSolution = (int) (Math.random() * solutions.length);
            currentNode.value = solutions[randomSolution];
            charArray[index] = (char) ('0' + solutions[randomSolution]);
            time++;
            System.out.println("********************"+time+"*********************");
            for (int i = 0; i < 9; i++) {
                if(i%3==0){
                    System.out.println("********************************************");
                }
                System.out.println(SoduNode.getNodesValue(soduNodes[i][0].listNode));
            }
            System.out.println("*****************************************");
            System.out.println("\n");

        } while (true);
        System.out.println("********************result*********************");
        for (int i = 0; i < 9; i++) {
            System.out.println(SoduNode.getNodesValue(soduNodes[i][0].listNode));
        }

流程圖

數(shù)獨(dú)生成器.jpg

判斷數(shù)獨(dú)是否只有一個解的步驟和流程圖

步驟

  • 步驟一、利用數(shù)獨(dú)求解算法獲取數(shù)獨(dú)的一個解。并且改進(jìn)數(shù)獨(dú)求解的算法,每次選擇一個空單元時,記錄未被驗(yàn)證的解集。
  • 步驟二、如果沒有成功,則此數(shù)獨(dú)沒有解,返回。
  • 步驟三、按照解數(shù)獨(dú)時選擇空單元的順序,從后往前遍歷。每遍歷一個單元時,將之后的單元置空。
  • 步驟四、從后往前遍歷單元時,遍歷每個單元的未被驗(yàn)證的解集。將解決中的解填入單元中,求此時數(shù)獨(dú)是否有解。
  • 步驟五、如果按照五的步驟能夠發(fā)現(xiàn)解,說明此數(shù)獨(dú)有多解,否則,數(shù)獨(dú)只有一個解。
    ** 代碼**
@Override
    public int findSolution(char[] charArray) {
        initData(charArray);
        System.out.println("");
//對應(yīng)步驟一 利用數(shù)獨(dú)求解算法獲取數(shù)獨(dú)的一個解。并且改進(jìn)數(shù)獨(dú)求解的算法,每次選擇一個空單元時,記錄未被驗(yàn)證的解集。
        if (!findSoduResult()) {
// 步驟二、如果沒有成功,則此數(shù)獨(dú)沒有解,返回。
            return NONE_RESULT;
        }
        System.out.println("findSoduResult");
//步驟三、按照解數(shù)獨(dú)時選擇空單元的順序,從后往前遍歷。每遍歷一個單元時,將之后的單元置空。
        for (int m = 80; m >= 0; m--) {
            int i = 80 / 9;
            int k = 80 % 9;
步驟四、從后往前遍歷單元時,遍歷每個單元的未被驗(yàn)證的解集。將解決中的解填入單元中,求此時數(shù)獨(dú)是否有解。
            TreeSet<Integer> allValueSet = soduNodes[i][k].getAllValueSet();
            if (!soduNodes[i][k].needTobeSolve || allValueSet.size() == 1) {
                if (soduNodes[i][k].needTobeSolve) {
                    soduNodes[i][k].value = 0;
                }
                continue;
            }
            TreeSet<Integer> leftAllValue = soduNodes[i][k].getAllValueSet();
            Iterator<Integer> iterator = leftAllValue.iterator();
            a: while (iterator.hasNext()) {
                Integer leftValue = (Integer) iterator.next();
                if (leftValue == soduNodes[i][k].value) {
                    continue a;
                }
                soduNodes[i][k].value = leftValue;
//步驟五、如果按照五的步驟能夠發(fā)現(xiàn)解,說明此數(shù)獨(dú)有多解。
                if (findSoduResult()) {
                    return MULTI_RESULT;
                }
            }
            soduNodes[i][k].value = 0;

        }
//步驟五、如果按照五的步驟不能夠發(fā)現(xiàn)解,說明此數(shù)獨(dú)有唯一解。
        return SINGLE_RESULT;
    }

流程圖:

判斷數(shù)獨(dú)是否唯一解.jpg

運(yùn)行

以下是整個算法運(yùn)行過程:

********************result*********************
********************************************
0 9 0 * 0 4 6 * 0 0 3 * 
4 0 0 * 0 1 0 * 0 0 6 * 
0 0 0 * 0 9 0 * 2 7 4 * 
********************************************
1 0 0 * 0 8 0 * 0 0 0 * 
0 0 0 * 0 7 0 * 0 5 0 * 
9 0 5 * 0 6 0 * 0 3 1 * 
********************************************
0 0 0 * 0 3 0 * 0 0 0 * 
8 2 6 * 4 0 1 * 3 0 7 * 
3 0 0 * 0 0 8 * 0 6 0 * 

聯(lián)系我

郵箱:
sysuzhuminh@gmail.com
apk下載:
https://www.pgyer.com/ezPc
GitHub:
https://github.com/huolizhuminh/sodu
qq群:

數(shù)獨(dú)愛好者群二維碼.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容