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

我项目的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. sklearn使用投票器VotingClassifier算法构建多模型融合的软投票器分类器(soft voting)并自定义子分类器的权重(weights)、计算融合模型的混淆矩阵、可视化混淆矩阵
  2. R语言——str函数
  3. PoE交换机的选择和使用注意事项介绍
  4. u盘显示项目不在计算机zhng,能装在u盘上的操作系统有哪些?
  5. LATEX调整公式、图片与正文间距离,文字间距离,调整空白大小
  6. JavaScript学习笔记:常量,枚举,宏定义
  7. java整形数组的最大最小值
  8. 如何将工作流程“简单化”,从而提高工作效率?
  9. 失业日记 9月30日
  10. android 开源fc模拟器_安卓FC模拟器NESemu
  11. Mac SOME/IP编译以及Ubuntu的SOME/IP集成与编译
  12. 那些年踩过的坑-之《Android Q-高通平台UAC(USB Audio Class)调试记录》
  13. 1053 住房空置率 (20分)
  14. 如果让你选最佳的远程桌面,以下9款远程桌面你心中的排名 NO1 是哪个?
  15. 读锁应该插队吗?什么是读写锁的升降级?
  16. 弹性计算双周刊 第20期
  17. 【Win11尝鲜】Win 11设置任务栏图标靠左或居中显示
  18. Python学习笔记——map和reduce实现str2float函数
  19. MATLAB 单双引号
  20. 如何在Java中创建临时文件?

热门文章

  1. FAF世链区块链大会|赤兔云算CTO 杨钧淏:共建区块链生态,创造新财富时代
  2. DTS北洋曳舞社网站开发手记
  3. Mockplus Cloud自动生成规格,Mockplus Cloud交互式动画原型
  4. 数据可视化之大数据可视化
  5. echarts 大屏适配
  6. 区块链-拜占庭将军问题介绍
  7. 定解问题(一)| 通解与特解 + 适定性 | 偏微分方程(五)
  8. 【诊断数据库文件-CDD】创建速成班-北汇信息小课堂(一)
  9. 1447D Catching Cheaters
  10. 抖音算法2023届秋招快上车