无重复数独题目生成

我算半个数独爱好者,但是市面上的数独软件很多都有广告,web版则需要联网,就想着自己用JAVA写一个。简简单单用swing,搭建好GUI后,发现最大的问题居然是数独题目的生成。从网上找题库实在是不甘心,索性动动脑子查查资料自己编一个。

数独的规则就不多介绍了,唯一让我没想到的是,小小的一个数独题目,居然还有这么多的坑和讲究。最难的部分是,如何保证这个数独题目是有唯一解的。数独题目中的数字越少,它有唯一解的可能性就越低。所以生成一个数字很少且有唯一解的数独要求还是很多的。

知乎查到的算法是,生成一个满数的数独矩阵,写一个解数独的方法,然后随机去掉一个数字,查看这个数独是否有多种解,然后再去掉一个,再次查看是否有多个解,使用回溯法,持续下去,直到找到可以去掉最多数字的时候。然而这种算法虽然可以生成很难的数独,但速度太慢,被我放弃了。(还有一个先随便生成题目然后验证是否有解,再验证是否有唯一解的方法,很适合生成困难数独,但我目前没有尝试。此方法可能表现更好,但还没写hhh,没有调查就没有发言权)

所以我用了以下思路:

第一步,生成一个满数的数独矩阵,用int[ ][ ] matrix弄个二位数组

第二步,约定一个随意的值作为数独题目剩余的数字n,比如n=40

第三步,将数慢数独矩阵随意挖空81-40=41个数字

第四步,使用回溯法解数独,记录最后一次回溯产生在哪里

第五步,把第四步的最后一次回溯返回false,也就是强行失败,看看是否还有解,若有则为第二个解。则此时此数独解不唯一,则返回第一步,重新生成数独。若无解,则此时此数独解唯一。

虽然看起来好像挺复杂,但是当n大于30时,计算机的运算量是完全可以接受的。虽然n为30时数独剩下的数字实在是太多了,也就是说太简单了。。更难的方法我还在探索中,准备尝试先随便生成题目的算法。

下面粘贴一些代码吧:

第一步:1,写个方法判断一下,当前位置如果填了某数,那会不会冲突,取个名叫isDup

2,把数组的第一行随便填满,这个很容易

3,用递归生成拥有全部数字的矩阵,还不一定成功,毕竟填着填着数独可能就无解了

    //回溯算法生成全数字矩阵,t从9开始,因为第一行已经满了
    public boolean addANumb(int t){
        int x = t/9;
        int y = t%9;
        Random random = new Random();
        int index = Math.abs(random.nextInt());
        int atry = 0;
        for (int i=0;i<9;i++){
            atry = seed[(i+index)%9];
            if (isDup(x,y,atry,matrix)){
                matrix[x][y]=atry;
                if (t==80)
                    return true;
                return addANumb(t+1);

            }
        }
        return false;
    }

    //返回一个全矩阵,用循环,失败了就重新构造重新生成一个
    public void generateMatrix(){
        boolean flag = false;
        while (true){
            GenerateNumb g = new GenerateNumb();
            flag=g.addANumb(9);
            if (flag){
                this.matrix = g.matrix;
                break;
            }
        }
    }

第四步:写个求解数独的程序,还是递归回溯。别忘了记录最后一次递归是在哪里产生的

//求解数独,memory用来记录最后一次回溯在哪里
    public boolean solve(int count){
        int x=count/9;
        int y=count%9;
        while (gqMatrix[x][y]!=0){
            count++;
            if (count>=81){
                return true;
            }
            x=count/9;y=count%9;
        }
        if (memory<count){
            memory=count;
        }
        for (int i=1;i<=9;i++){
            if (isDup(x,y,i,gqMatrix)){
                gqMatrix[x][y]=i;
                if (count==80||solve(count+1)){
                    return true;
                }else {
                    gqMatrix[x][y]=0;
                }
            }
        }
        return false;
    }

第五步:看看最后一步递归失败的时候,有没有其他解,有解返回的是true。如果这时候返回false,那这个数独是有唯一解且一定有解的(毕竟是完整的数独矩阵去掉一些数字变成的嘛)

//,基本和上面的一样,只是用memroy判断是不是到了最后一次递归,是的话直接返回false
public boolean solveIfOnly(int count){
        int x=count/9;
        int y=count%9;

        while (gqMatrix[x][y]!=0){
            count++;
            if (count>=81){
                return true;
            }
            x=count/9;y=count%9;
        }
        if (count==memory){
            memory=0;
            return false;
        }
        for (int i=1;i<=9;i++){
            if (isDup(x,y,i,gqMatrix)){
                gqMatrix[x][y]=i;
                if (count==80||solveIfOnly(count+1)){
                    return true;
                }else {
                    gqMatrix[x][y]=0;
                }
            }
        }
        return false;
    }

就这样结束了,写到头秃。

其实现在已经知道之前提到的方法估计表现更好,但还是耐着性子把这个方法写完了。毕竟这是正道(笨道)啊,人间正道是沧桑啊。。。。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值