数独 ( 二 ) ——生成数独终局
数独 ( 二 ) ——生成数独终局
我项目的github链接 :https://github.com/gmj0301/gmj
请查看前面的博客,有修改!
要求
- 在命令行中使用 -c 参数加数字 N (1<=N<=1000000) 控制生成数独终局的数量
- 将生成的数独终局用一个文本文件 (假设名字叫 sudoku.txt) 的形式保存起来,每次生成的 txt 文件需要覆盖上次生成的 txt 文件,文件内的格式如下,数与数之间由空格分开,终局与终局之间空一行,行末无空格
- 程序在处理命令行参数时,不仅能处理格式正确的参数,还能够处理各种异常的情况
- 在生成数独终局时,左上角的第一个数为:(学号后两位相加)% 9 + 1
解题思路
根据要求(4),我的学号后两位是90,则左上角的第一个数为(9+0)% 9 + 1 = 1
根据要求(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");}
}
相关测试会在后面的博客更新。
因为一开始有些函数不完善,有进行了更改。
数独 ( 二 ) ——生成数独终局相关推荐
- 【个人项目】解题思路 - 生成数独终局 (1120161918)
生成数独终局 数学原理分析 数独是一种逻辑游戏.它要求满足每一行.每一列.每一个九宫格(3×3)内的数字均含1-9,不重复. 生成数独终局的方案有很多,比如随机数先分配第一行位置然后利用回溯法解数独, ...
- (附代码)数独大作业【读取数独,解数独,生成唯一解数独(随机,特定形状,不同难度生成),玩数独】
注:未经同意不要转载. 上学期简单的做了一个数独程序,实现了一些功能,想简单的为大家提供的思路. 为了避免某些情况出现,具体代码暂时先不发了,有不太懂的地方可以评论提问啊. 下面是我的具体报告: 一, ...
- 数独-- 一个高效率生成数独的算法
关于数独,来自维基百科的玩法解释:在9×9格的大九宫格中有9个3×3格的小九宫格,并提供一定数量的数字.根据这些数字,利用逻辑和推理,在其它的空格上填入1到9的数字.每个数字在每个小九宫格内只能出现一 ...
- 数独的生成以及解答--回溯算法c++附详细代码
一,数独的规则 横向上9个数字满足1-9不重复: 竖向上9个数字满足1-9不重复: 将大网格拆分为9个3*3的小网格,每个小网格内同样满足1-9不重复 二,生成数独的思路 首先准备一个空的数独,从第一 ...
- 数独的生成和破解算法分析
最近在捉摸数独的破解方法,自己本不是搞软件的,而是电子的.所以虽然写出来了一个,但是方法很笨拙. 在网上查好时,发现了有一种算法的思维与众不同,既简单又高效,不像其他算法一样递归的太深. 我对作者的代 ...
- C语言生成一个随机的九行九列数独,一个随机生成数独的C++程序
下面是编程之家 jb51.cc 通过网络收集整理的代码片段. 编程之家小编现在分享给大家,也给大家做个参考. //mySIZE是数独棋盘的边长,棋盘是mySIZE*mySIZE的大小 int mySI ...
- 数独的生成算法和解题算法
github项目地址:https://github.com/Xcodingman/sudo.git 配置环境:windows10 vs2013 打开工程文件,运行相对应的cpp文件即可 1.数独解题与 ...
- c++ 随机生成数独(不保证唯一解)
给出一个最简单的生成数独初盘的程序,不保证有唯一解,终盘的正确性通过填充的过程来确定,可以用舞蹈链算法求解该数独得到其中一个解 随机生成的方法就算交换行和列然后随机挖洞 这里有一点要注意的就是交换的行 ...
- 随机数独的生成的实验思路概述
实验背景 数独起源于拉丁方阵,由单元格.行.列.宫等元素组成,规则是在每行.每列.每宫的九个单元格中填入数字1-9,不重复.给定一定数量提示数的盘面作为初始条件,称为初盘.根据规则将所有单元格填满得到 ...
最新文章
- 实验四:汇编代码调用系统调用的工作过程
- python词频统计_python统计词频的三种方法
- python全栈开发基础【第二十三篇】线程
- redis(一)--简介
- 消防验收找问题,一般就是这些了!
- 利用xposed绕过安卓SSL证书的强校验
- 在 ubuntu 20.04 LTS 上安装 ROS2 执行 rosdep update 命令时出现的问题的解决办法
- Activiti的使用技巧
- apache为什么更适合处理动态请求_[适合初中级Java程序员修炼手册从0搭建整个Web项目](一)...
- 2使用教学_建水三中智能交互式液晶一体机设备投入使用
- MySQL日期、字符串、数值型转换
- 如何抓取Camera systrace
- Java的Redis连接池代码
- 单片机编程用什么软件?单片机开发软件有哪些?华维告诉你.
- 叩丁狼开发工程师:SSR服务架构特点分析
- 重力对手表的走时精度有何影响?12:06:44
- FreeType字体程序库介绍
- Linux开机密码重置
- sar adc的常用指标(二)
- 生活中的经济学(总结于半小时漫画书经济学 生活常识篇)
热门文章
- 数字0是奇数还是偶数_C程序检查数字是偶数还是奇数
- matlab cui,阻力汽车论文,关于基于Matlab-CUI的汽车动力性相关参考文献资料-免费论文范文...
- serial port not selected
- 服务器配置https,以及报错解决
- 暴多的教学视频,想要的就快下!
- 支付宝客户端架构分析:自动化日志收集及分析 1
- JAVA JSP 餐厅点餐系统源码(点餐系统)jsp点餐系统网上订餐系统在线订餐系统
- 值得学习Google的编程样式指南
- 怎么压缩图片 ? 掌握这几种免费压缩图片的方法就够了
- springboot yml文件不是绿叶子问题