数独 ( 二 ) ——生成数独终局

我项目的github链接 :https://github.com/gmj0301/gmj
请查看前面的博客,有修改!

要求

  1. 在命令行中使用 -c 参数加数字 N (1<=N<=1000000) 控制生成数独终局的数量
  2. 将生成的数独终局用一个文本文件 (假设名字叫 sudoku.txt) 的形式保存起来,每次生成的 txt 文件需要覆盖上次生成的 txt 文件,文件内的格式如下,数与数之间由空格分开,终局与终局之间空一行,行末无空格
  3. 程序在处理命令行参数时,不仅能处理格式正确的参数,还能够处理各种异常的情况
  4. 在生成数独终局时,左上角的第一个数为:(学号后两位相加)% 9 + 1

解题思路

  1. 根据要求(4),我的学号后两位是90,则左上角的第一个数为(9+0)% 9 + 1 = 1

  2. 根据要求(1)可知,最多要生成 1000000 个不同的数独终局。
    经过学习了解后,我发现数独终局的一个特点。

    对于这个给定的数独终局,从第二行开始,每行分别是第一行左移 3,6,1,7,4,2,5,8 列的结果。而题目中要求我生成的数独终局左上角第一个数为 1,这样第一行有8个空可以随意填入 2 ~ 9 这 8 个数字。对于任意一个 2 ~ 9 的全排列,都可以通过上述方法生成一个数独终局。这样就有 8!= 40320 种终局。但是这远远不够,再想办法直接生成数独终局是很困难的且容易重复,最好的方法是在已生成的数独终局上进行变换产生新的终局

    在网上了解学习后,总结了如下几种在已有一个数独终局的情况下,生成数独终局的方法。

    (1)两个数字相互交换法
    这个方法是选择两个数字,例如 1 和 9,在填 1 的地方填 9;在填 9 的地方填 1。但是因为已有的数独终局第一行是按照 2 ~ 9 的全排列,交换后可能恰好生成之前已有的终局。不能使用这种方法。

    (2)调整块
    将前三行、中间三行、后三行作为三个块,任意交换其中两个块。但是左上角第一个数字为 1 导致第一行不能动,这样只能交换后两个块,一个数独终局只能产生一个新的数独终局,这样最终只有 80640 个数独终局,不够。同理可以将列作为块,但是因为已有的数独终局第一行是按照 2 ~ 9 的全排列,交换后可能恰好生成之前已有的终局。不能使用这种方法。

    (3)旋转矩阵
    将矩阵旋转后,左上角第一个数字不再是 1,不能使用这种方法。

    (4)调整行或列
    交换只发生在前三行,中间三行,最后三行,前三列,中间三列以及最后三列之间。而不能越界交换,比如第一行和第四行交换就是不允许的。
    因为已有的数独终局第一行是按照 2 ~ 9 的全排列,交换列后可能恰好生成之前已有的终局。不能交换列。由于左上角不能动,我们只交换 4 ~ 6 中的任意两行或者 7 ~ 9 行中的任意两行,这样在已有的每种终局就可以变成 3! × 3! = 36种终局,一共1451520种终局,已经超过了 1000000 种,可以满足要求了。

设计函数

使用 C 语言进行编程。

经过分析,先通过全排列的方式生成第一行,经过转换生成数独终局;再通过交换行生成新的数独终局。因为交换行时,每一行内的数字的相对位置不变,只是行的顺序发生变化,所以在输出时按照新的输出顺序输出即可,不需要真的改变数独。

整体的设计实现过程如下:

函数设计如下:

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>int shift[8] = { 3, 6, 1, 4, 7, 2, 5, 8 };
int c1[3] = { 3, 4, 5 };
int c2[3] = { 6, 7, 8 };
int snum1 = 1, snum2 = 1, flag_first = 0;void produce(int sudo[9][9]);  //第一行已知,通过变换得到完整的数独终局void swap(int& a, int& b); //交换函数,交换a和bbool nextPermutation(int* p, int n); //全排列函数void jiaohan_1(int s1, int* c1);     //中间4,5,6行的变换void jiaohan_2(int s2, int* c2);     //最后7,8,9行的变换void init(int sudo[9][9], int p[8]); //初始化,规定左上角第一个为1,第一行为123456789void output(int sudo[9][9], FILE* fp);  //输出函数void change(); //行变换的参数设置void create(int N); //生成N个数独终局并写入文件

其中比较重要的函数的具体实现如下:

(1)全排列
由于第一位数字不动,后面 2 ~ 9 这 8 个数字进行全排列。

bool nextPermutation(int* p, int n)
{int last = n - 1;int i, j, k;//从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。   i = last;while (i > 0 && p[i] < p[i - 1])i--;//若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回false。   if (i == 0)return false;//从后查到i,查找大于p[i - 1]的最小的数,记入k   k = i;for (j = last; j >= i; j--)if (p[j] > p[i - 1] && p[j] < p[k])k = j;//交换p[k]和p[i - 1]   swap(p[k], p[i - 1]);//倒置p[last]到p[i]   for (j = last, k = i; j > k; j--, k++)swap(p[j], p[k]);return true;
}

(2)生成数独终局
从第二行开始,每行分别是第一行左移 3,6,1,7,4,2,5,8 列的结果。
设置一个一维数组 shift 记录需要左移的列数,剩下每一行根据 shift 来判断当前位置填入的数字。

void produce(int sudo[9][9])  //第一行已知,通过变换得到完整的数独终局
{for (int i = 1; i < 9; i++)for (int j = 0; j < 9; j++)sudo[i][j] = sudo[0][(j + shift[i - 1]) % 9];//第i行是第一行左移shift[i]得到的  即sudo[i][j] = sudo[0][j右移shift[i]]//j右移后可能超出9,所以要模9取余
}

(3)改变输出顺序
只交换 4 ~ 6 中的任意两行或者 7 ~ 9 行中的任意两行,这样在已有的每种终局就可以变成 36 种终局。
中间三行的顺序有:4,5,6;4,6,5;5,4,6;5,6,4;6,4,5;6,5,4 这六种。
最后三行的顺序有:7,8,9;7,9,8;8,7,9;8,9,7;9,7,8;9,8,7 这六种。
两者组合可以产生 36 种。只需要设置两个参数,一个表示中间三行的顺序,另一个表示后三行,通过组合即可表示。不需要真的改变数组。

void jiaohan_1(int s1, int* c1)     //中间4,5,6行的变换
{if (s1 == 1) { c1[0] = 3; c1[1] = 4; c1[2] = 5; }if (s1 == 2) { c1[0] = 3; c1[1] = 5; c1[2] = 4; }if (s1 == 3) { c1[0] = 4; c1[1] = 3; c1[2] = 5; }if (s1 == 4) { c1[0] = 4; c1[1] = 5; c1[2] = 3; }if (s1 == 5) { c1[0] = 5; c1[1] = 3; c1[2] = 4; }if (s1 == 6) { c1[0] = 5; c1[1] = 4; c1[2] = 3; }
}void jiaohan_2(int s2, int* c2)     //最后7,8,9行的变换
{if (s2 == 1) { c2[0] = 6; c2[1] = 7; c2[2] = 8; }if (s2 == 2) { c2[0] = 6; c2[1] = 8; c2[2] = 7; }if (s2 == 3) { c2[0] = 7; c2[1] = 6; c2[2] = 8; }if (s2 == 4) { c2[0] = 7; c2[1] = 8; c2[2] = 6; }if (s2 == 5) { c2[0] = 8; c2[1] = 6; c2[2] = 7; }if (s2 == 6) { c2[0] = 8; c2[1] = 7; c2[2] = 6; }
}

输出时一个个往文件里面写入,代码如下:

void output(int sudo[9][9], FILE* fp)  //输出函数
{jiaohan_1(snum1, c1);jiaohan_2(snum2, c2); //判断当前两个参数表示哪两种顺序char s[18];//输出数独for (int i = 0; i < 9; i++){int k = i;if (k >= 3 && k <= 5) k = c1[k - 3]; //如果当前是中间三行,按照顺序输出else if (k > 5) k = c2[k - 6];       //如果当前是中间三行,按照顺序输出for (int j = 0; j <= 8; j++) {int kt = j * 2;s[kt] = sudo[k][j] + '0';s[kt + 1] = ' ';}s[17] = '\0';fputs(s, fp);         //写入文件fprintf(fp, "\n");}
}

相关测试会在后面的博客更新。
因为一开始有些函数不完善,有进行了更改。

数独 ( 二 ) ——生成数独终局相关推荐

  1. 【个人项目】解题思路 - 生成数独终局 (1120161918)

    生成数独终局 数学原理分析 数独是一种逻辑游戏.它要求满足每一行.每一列.每一个九宫格(3×3)内的数字均含1-9,不重复. 生成数独终局的方案有很多,比如随机数先分配第一行位置然后利用回溯法解数独, ...

  2. (附代码)数独大作业【读取数独,解数独,生成唯一解数独(随机,特定形状,不同难度生成),玩数独】

    注:未经同意不要转载. 上学期简单的做了一个数独程序,实现了一些功能,想简单的为大家提供的思路. 为了避免某些情况出现,具体代码暂时先不发了,有不太懂的地方可以评论提问啊. 下面是我的具体报告: 一, ...

  3. 数独-- 一个高效率生成数独的算法

    关于数独,来自维基百科的玩法解释:在9×9格的大九宫格中有9个3×3格的小九宫格,并提供一定数量的数字.根据这些数字,利用逻辑和推理,在其它的空格上填入1到9的数字.每个数字在每个小九宫格内只能出现一 ...

  4. 数独的生成以及解答--回溯算法c++附详细代码

    一,数独的规则 横向上9个数字满足1-9不重复: 竖向上9个数字满足1-9不重复: 将大网格拆分为9个3*3的小网格,每个小网格内同样满足1-9不重复 二,生成数独的思路 首先准备一个空的数独,从第一 ...

  5. 数独的生成和破解算法分析

    最近在捉摸数独的破解方法,自己本不是搞软件的,而是电子的.所以虽然写出来了一个,但是方法很笨拙. 在网上查好时,发现了有一种算法的思维与众不同,既简单又高效,不像其他算法一样递归的太深. 我对作者的代 ...

  6. C语言生成一个随机的九行九列数独,一个随机生成数独的C++程序

    下面是编程之家 jb51.cc 通过网络收集整理的代码片段. 编程之家小编现在分享给大家,也给大家做个参考. //mySIZE是数独棋盘的边长,棋盘是mySIZE*mySIZE的大小 int mySI ...

  7. 数独的生成算法和解题算法

    github项目地址:https://github.com/Xcodingman/sudo.git 配置环境:windows10 vs2013 打开工程文件,运行相对应的cpp文件即可 1.数独解题与 ...

  8. c++ 随机生成数独(不保证唯一解)

    给出一个最简单的生成数独初盘的程序,不保证有唯一解,终盘的正确性通过填充的过程来确定,可以用舞蹈链算法求解该数独得到其中一个解 随机生成的方法就算交换行和列然后随机挖洞 这里有一点要注意的就是交换的行 ...

  9. 随机数独的生成的实验思路概述

    实验背景 数独起源于拉丁方阵,由单元格.行.列.宫等元素组成,规则是在每行.每列.每宫的九个单元格中填入数字1-9,不重复.给定一定数量提示数的盘面作为初始条件,称为初盘.根据规则将所有单元格填满得到 ...

最新文章

  1. 实验四:汇编代码调用系统调用的工作过程
  2. python词频统计_python统计词频的三种方法
  3. python全栈开发基础【第二十三篇】线程
  4. redis(一)--简介
  5. 消防验收找问题,一般就是这些了!
  6. 利用xposed绕过安卓SSL证书的强校验
  7. 在 ubuntu 20.04 LTS 上安装 ROS2 执行 rosdep update 命令时出现的问题的解决办法
  8. Activiti的使用技巧
  9. apache为什么更适合处理动态请求_[适合初中级Java程序员修炼手册从0搭建整个Web项目](一)...
  10. 2使用教学_建水三中智能交互式液晶一体机设备投入使用
  11. MySQL日期、字符串、数值型转换
  12. 如何抓取Camera systrace
  13. Java的Redis连接池代码
  14. 单片机编程用什么软件?单片机开发软件有哪些?华维告诉你.
  15. 叩丁狼开发工程师:SSR服务架构特点分析
  16. 重力对手表的走时精度有何影响?12:06:44
  17. FreeType字体程序库介绍
  18. Linux开机密码重置
  19. sar adc的常用指标(二)
  20. 生活中的经济学(总结于半小时漫画书经济学 生活常识篇)

热门文章

  1. 数字0是奇数还是偶数_C程序检查数字是偶数还是奇数
  2. matlab cui,阻力汽车论文,关于基于Matlab-CUI的汽车动力性相关参考文献资料-免费论文范文...
  3. serial port not selected
  4. 服务器配置https,以及报错解决
  5. 暴多的教学视频,想要的就快下!
  6. 支付宝客户端架构分析:自动化日志收集及分析 1
  7. JAVA JSP 餐厅点餐系统源码(点餐系统)jsp点餐系统网上订餐系统在线订餐系统
  8. 值得学习Google的编程样式指南
  9. 怎么压缩图片 ? 掌握这几种免费压缩图片的方法就够了
  10. springboot yml文件不是绿叶子问题