徐松亮算法教学-基于C语言的数独(九宫格)多种终盘生成方法(包含矩阵镜像旋转转置等相关算法)

版权声明:本文为博主徐松亮的原创作品,未经允许不得转载,多谢支持!QQ:5387603

推荐点击此链接:欢迎进入徐松亮博客一站式导航搜索(随时更新)


 

目录

一,前言

二,开发环境

电脑系统

编译器

编程语言

编辑与工程环境

三,方法

方法概述

生成(或随机选定)种子矩阵终盘

两数交换法

非跨界行交换

非跨界列交换

块行交换

块列交换

矩阵旋转

矩阵行镜像

矩阵列镜像

矩阵对角线镜像

四,源码

五,运行结果演示

运行结果---两数交换法

运行结果---非跨界行交换

运行结果---非跨界列交换

运行结果---块行交换

运行结果---块列交换

运行结果---矩阵旋转

运行结果---矩阵行镜像

运行结果---矩阵列镜像

运行结果---矩阵对角线镜像

六,引用资料致谢


一,前言

  • 昨天,发布了一篇数独解题的文章,但是只有解题没有出题,总是感觉差点意思。
  • 网上搜了一些相关资料,大多都是单一方式,流程基本上是填入部分随机数---产生一个解盘---扣掉一些数。这个方案如果只是用于出题也是可行的,但是这么简单的话,我真是懒得总结。
  • 本文的矩阵转换算法都为博主亲自编写调试,都是自身转换,而不需要建立同样大小的缓存,尤其是矩阵旋转相关代码,还真花费了我一些时间,很是推荐,比目前网上的要好很多,后续我会把矩阵转换相关的代码标准化后另建立学习文档。
  • 偶然在网上发现一篇文章,它讲了多种数独终盘生成办法,主要是用的矩阵变换,还是那句话:虽然我对数独本身这个游戏没什么兴趣,但是对它涉及到的计算机数学问题,还是颇有兴趣。
  • 做到这,我想到一个问题,接数独是不是也可以用这篇文章讲的矩阵变换方法,经过各种行列兑换,生成一个解,原理有点像拧魔方似的,方法理论上应该是可行的,但不过这个方法不能统计出解的数量。

二,开发环境

三,方法

  • 方法概述

    • 基于一个成型的终盘,经过下列任意变换后,还是一个符合规则的终盘。
  • 生成(或随机选定)种子矩阵终盘

    • 如果能真正的随机生成一个终盘,那就不用下面的变换了,所以我们在此用选定的现有矩阵做种子。
    • 真正做可以多做几个,然后用随机数来选。
  • 两数交换法

    • 就是随机选取两个数,可以是1和9,也可以是3和5,全局交换后,仍是有效终盘。
    • 下图举例,我们已2和8来做交换,如图所示:
  • 非跨界行交换

    • 以宫为界,即1-3行为一组,4-6行为一组,7-9行为一组,组内可执行行交换,组间不能。符合此规则执行行交换后,终盘仍为符合规则的终盘。
    • 下图举例,同色为界,我们执行1与3行的数据交换。

  • 非跨界列交换

    • 以宫为界,即1-3列为一组,4-6列为一组,7-9列为一组,组内可执行行交换,组间不能。符合此规则执列列交换后,终盘仍为符合规则的终盘。
    • 下图举例,我们执列1与3列的数据交换。

  • 块行交换

    • 以宫为界,即1-3行为一组,4-6行为一组,7-9行为一组,组与组交换在此我起名称作块行交换。符合此规则交换后,终盘仍为符合规则的终盘。
    • 下图举例,我们组1与组2的数据交换。

  • 块列交换

    • 以宫为界,即1-3列为一组,4-6列为一组,7-9列为一组,组与组交换在此我起名称作块列交换。符合此规则交换后,终盘仍为符合规则的终盘。
    • 下图举例,我们组1与组2的数据交换。

  • 矩阵旋转

    • 矩阵旋转,就是相当于你转着圈的去看数独题,你从四个方向看,这个题都是成立的。
    • 下图举例,如图。

  • 矩阵行镜像

    • 矩阵行镜像,就好比你看完报纸正面,反过来看报纸背面一样。符合此规则交换后,终盘仍为符合规则的终盘。
    • 下图举例,如图

  • 矩阵列镜像

    • 矩阵列镜像,原理同行镜像其实是一样的。符合此规则交换后,终盘仍为符合规则的终盘。
    • 下图举例,如图

  • 矩阵对角线镜像

    • 矩阵对角线镜像,原理相当于数学当中的矩阵转置。
    • 下图举例,如图

四,源码

/**
  ******************************************************************************
  * @file    xsl_game_sudo.c
  * @author  徐松亮 许红宁(5387603@qq.com)
  * @version V1.0.0
  * @date    2018/11/01
  ******************************************************************************
  * @attention
  * 待解决
  * 1,数独出题
  * GNU General Public License (GPL)
  *
  * <h2><center>&copy; COPYRIGHT 2017 XSLXHN</center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stdint.h>
// 用于计算耗时统计
#include <time.h>
/* Private define ------------------------------------------------------------*/
#define XSLGAMESUDO_SUDOKU_LEVEL 9
#define OK 0
#define ERR 1
/* Private typedef -----------------------------------------------------------*/
typedef struct
{
	// 输入---计算前用户设定
	uint8_t cells[XSLGAMESUDO_SUDOKU_LEVEL][XSLGAMESUDO_SUDOKU_LEVEL];
	//-----最大输出答案数量(1,为了防止pOutBuf数据溢出;2,为了计算适可而止;)
	uint32_t MaxOutAnswersCount;
	// 输入输出
	//-----输出缓存(NULL---无解输出)
	uint8_t *pOutBuf;
	//-----解最大数量(NULL---不求此值则非全局搜索)
	uint32_t *pAnswersCount;
	// 输出---计算后的统计结果
	uint32_t OutAnswersCount;
} XSLGAMESUDO_S_SUDO;
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static const uint8_t XslGameSudo_SudoGenerateBuf[XSLGAMESUDO_SUDOKU_LEVEL][XSLGAMESUDO_SUDOKU_LEVEL] = {
	8, 1, 2, 7, 5, 3, 6, 4, 9,
	9, 4, 3, 6, 8, 2, 1, 7, 5,
	6, 7, 5, 4, 9, 1, 2, 8, 3,
	1, 5, 4, 2, 3, 7, 8, 9, 6,
	3, 6, 9, 8, 4, 5, 7, 2, 1,
	2, 8, 7, 1, 6, 9, 5, 3, 4,
	5, 2, 1, 9, 7, 4, 3, 6, 8,
	4, 3, 8, 5, 2, 6, 9, 1, 7,
	7, 9, 6, 3, 1, 8, 4, 5, 2};
//
XSLGAMESUDO_S_SUDO XslGameSudo_s_Sudo;
/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
 * @brief   格式化打印
 * @note    打印指定行列的数组
 * @param   *pXslGameSudoS	---	数据指针
 * 			mode			---	0-打印问题	1-打印答案 2-只打印数独不打印字符串
 * @return  null
 */
static void XslGameSudo_FormatPrint(XSLGAMESUDO_S_SUDO *pXslGameSudoS, uint8_t mode)
{
	uint8_t i, j, k;
	uint8_t *pbuf;
	// 打印问题
	if ((mode == 0) || (mode == 2))
	{
		if (mode == 0)
		{
			printf("Sudo Questions:\n");
		}
		pbuf = (uint8_t *)pXslGameSudoS->cells;
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++)
		{
			for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL; j++)
			{
				printf("%2d", pbuf[i * XSLGAMESUDO_SUDOKU_LEVEL + j]);
				if (j == (XSLGAMESUDO_SUDOKU_LEVEL - 1))
					printf("\n");
			}
		}
	}
	// 打印答案
	else if (mode == 1)
	{
		if (pXslGameSudoS->pAnswersCount == 0)
		{
			printf("Sudo Processor : No Solution!\n");
			return;
		}
		//
		pbuf = pXslGameSudoS->pOutBuf;
		for (k = 0; k < pXslGameSudoS->OutAnswersCount; k++)
		{
			printf("Sudo Answers(%d):\n", k + 1);
			for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++)
			{
				for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL; j++)
				{
					printf("%2d", pbuf[(k * XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL) + (i * XSLGAMESUDO_SUDOKU_LEVEL + j)]);
					if (j == (XSLGAMESUDO_SUDOKU_LEVEL - 1))
						printf("\n");
				}
			}
		}
		if (pXslGameSudoS->pAnswersCount != NULL)
		{
			printf("Sudo Answers Count: %d\n", *(pXslGameSudoS->pAnswersCount));
		}
	}
}

uint8_t XslGameSudo_Generate(XSLGAMESUDO_S_SUDO *pXslGameSudoS)
{
	uint8_t num = 1;
	// 生成种子数独
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	// 两数交换法
	{
		uint8_t commutation_data_1, commutation_data_2;
		uint8_t i;
		uint8_t *pbuf = pXslGameSudoS->cells;
		// 设定交换数
		commutation_data_1 = 2;
		commutation_data_2 = 8;
		// 执行数据交换
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL; i++)
		{
			if (pbuf[i] == commutation_data_1)
			{
				pbuf[i] = commutation_data_2;
			}
			else if (pbuf[i] == commutation_data_2)
			{
				pbuf[i] = commutation_data_1;
			}
			else
			{
				;
			}
		}
	}
	printf("Point Exchange method sudoku(point:2,8):\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 非跨界行交换
	{
		uint8_t commutation_row_1, commutation_row_2;
		uint8_t i, temp;
		// 设定交换行(0-8有效)
		commutation_row_1 = 0;
		commutation_row_2 = 2;
		// 执行交换行
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++)
		{
			temp = pXslGameSudoS->cells[commutation_row_1][i];
			pXslGameSudoS->cells[commutation_row_1][i] = pXslGameSudoS->cells[commutation_row_2][i];
			pXslGameSudoS->cells[commutation_row_2][i] = temp;
		}
	}
	printf("Row Exchange method sudoku(row:1,3):\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 非跨界列交换
	{
		uint8_t commutation_col_1, commutation_col_2;
		uint8_t i, temp;
		// 设定交换行(0-8有效)
		commutation_col_1 = 0;
		commutation_col_2 = 2;
		// 执行交换行
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++)
		{
			temp = pXslGameSudoS->cells[i][commutation_col_1];
			pXslGameSudoS->cells[i][commutation_col_1] = pXslGameSudoS->cells[i][commutation_col_2];
			pXslGameSudoS->cells[i][commutation_col_2] = temp;
		}
	}
	printf("Col Exchange method sudoku(col:1,3):\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 块行交换
	{
		uint8_t commutation_blockrow_1, commutation_blockrow_2;
		uint8_t i, j, temp;
		// 设定交换行(0-2有效)
		commutation_blockrow_1 = 0;
		commutation_blockrow_2 = 1;
		// 执行交换行
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++)
		{
			for (j = 0; j < 3; j++)
			{
				temp = pXslGameSudoS->cells[commutation_blockrow_1 * 3 + j][i];
				pXslGameSudoS->cells[commutation_blockrow_1 * 3 + j][i] = pXslGameSudoS->cells[commutation_blockrow_2 * 3 + j][i];
				pXslGameSudoS->cells[commutation_blockrow_2 * 3 + j][i] = temp;
			}
		}
	}
	printf("Block Row Exchange method sudoku(Block Row:1,2):\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 块列交换
	{
		uint8_t commutation_blockrow_1, commutation_blockrow_2;
		uint8_t i, j, temp;
		// 设定交换行(0-2有效)
		commutation_blockrow_1 = 0;
		commutation_blockrow_2 = 1;
		// 执行交换行
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++)
		{
			for (j = 0; j < 3; j++)
			{
				temp = pXslGameSudoS->cells[i][commutation_blockrow_1 * 3 + j];
				pXslGameSudoS->cells[i][commutation_blockrow_1 * 3 + j] = pXslGameSudoS->cells[i][commutation_blockrow_2 * 3 + j];
				pXslGameSudoS->cells[i][commutation_blockrow_2 * 3 + j] = temp;
			}
		}
	}
	printf("Block Col Exchange method sudoku(Block Col:1,2):\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 矩阵旋转(下面是方形矩阵的自旋转程序(不用额外缓存))
	{
		uint8_t i, j, k, n, x;
		uint8_t temp1, temp2;
		//设置参数
		n = XSLGAMESUDO_SUDOKU_LEVEL;
		x = n / 2;
		// 一圈一圈处理
		k = n - 1;
		for (i = 0; i < x; i++)
		{
			for (j = 0; j < k; j++)
			{
				//右列
				temp1 = pXslGameSudoS->cells[i + j][n - 1 - i];
				pXslGameSudoS->cells[i + j][n - 1 - i] = pXslGameSudoS->cells[i][i + j];
				//下行
				temp2 = pXslGameSudoS->cells[n - 1 - i][n - 1 - i - j];
				pXslGameSudoS->cells[n - 1 - i][n - 1 - i - j] = temp1;
				//左列
				temp1 = pXslGameSudoS->cells[n - 1 - i - j][i];
				pXslGameSudoS->cells[n - 1 - i - j][i] = temp2;
				//左上
				pXslGameSudoS->cells[i][i + j] = temp1;
			}
			k -= 2;
		}
	}
	printf("Rotate method sudoku(90 degrees):\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 矩阵行镜像
	{
		uint8_t i, j;
		uint8_t temp;
		//
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL / 2; i++)
		{
			for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL; j++)
			{
				temp = pXslGameSudoS->cells[j][XSLGAMESUDO_SUDOKU_LEVEL - 1 - i];
				pXslGameSudoS->cells[j][XSLGAMESUDO_SUDOKU_LEVEL - 1 - i] = pXslGameSudoS->cells[j][i];
				pXslGameSudoS->cells[j][i] = temp;
			}
		}
	}
	printf("X Mirror sudoku:\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 矩阵列镜像
	{
		uint8_t i, j;
		uint8_t temp;
		//
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL / 2; i++)
		{
			for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL; j++)
			{
				temp = pXslGameSudoS->cells[XSLGAMESUDO_SUDOKU_LEVEL - 1 - i][j];
				pXslGameSudoS->cells[XSLGAMESUDO_SUDOKU_LEVEL - 1 - i][j] = pXslGameSudoS->cells[i][j];
				pXslGameSudoS->cells[i][j] = temp;
			}
		}
	}
	printf("Y Mirror sudoku:\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
	memcpy((uint8_t *)(pXslGameSudoS->cells), (uint8_t *)&XslGameSudo_SudoGenerateBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	printf("%d,----------Seeds sudoku:\n", num++);
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	// 矩阵对角线镜像
	{
		uint8_t i, j;
		uint8_t temp;
		for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL - 1; i++)
		{
			for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL - 1 - i; j++)
			{
				temp = pXslGameSudoS->cells[i + j + 1][i];
				pXslGameSudoS->cells[i + j + 1][i] = pXslGameSudoS->cells[i][i + j + 1];
				pXslGameSudoS->cells[i][i + j + 1] = temp;
			}
		}
	}
	printf("Transpose sudoku:\n");
	XslGameSudo_FormatPrint(pXslGameSudoS, 2);
	//----------
}
/**
 * @brief   main函数
 * @note    主函数入口
 * @param   null
 * @return  null
 */
uint8_t MemBuf[1024]; //81个字节存储一组解,1024可以存储大于10个解
uint32_t SudoCount;
void main(int argc, char **argv)
{
	uint8_t res;
	uint32_t time1, time2;
	printf("--------------------------------------------------\n");
	printf("             XSL Sudo Generate(Normal)            \n");
	printf("--------------------------------------------------\n");
	XslGameSudo_Generate(&XslGameSudo_s_Sudo);
while(1);
	printf("--------------------------------------------------\n");
	printf("             XSL Sudo Processor(Normal)           \n");
	printf("--------------------------------------------------\n");
	// 数据载入
	memcpy((uint8_t *)(XslGameSudo_s_Sudo.cells), (uint8_t *)&XslGameSudo_SudoBuf[0][0], XSLGAMESUDO_SUDOKU_LEVEL * XSLGAMESUDO_SUDOKU_LEVEL);
	// 设置配置
	//---最多解析10个解
	XslGameSudo_s_Sudo.MaxOutAnswersCount = 5;
	XslGameSudo_s_Sudo.pOutBuf = MemBuf;
	XslGameSudo_s_Sudo.pAnswersCount = &SudoCount;
	// 打印原始数独
	XslGameSudo_FormatPrint(&XslGameSudo_s_Sudo, 0);
	// 启动数独测试
	time1 = GetTickCount();
	XslGameSudo_Processor(&XslGameSudo_s_Sudo);
	time2 = GetTickCount();
	// 打印结果
	XslGameSudo_FormatPrint(&XslGameSudo_s_Sudo, 1);
	// 打印耗时
	printf("Time(ms):%ld\n", time2 - time1);
	// 定住页面,否则程序结束直接闪退,延时10秒自动退出
	time1 = time2 = 0;
	time1 = GetTickCount();
	while (((time2 - time1) < 100000) || (time2 == 0))
	{
		time2 = GetTickCount();
	}
}

五,运行结果演示

  • 运行结果---两数交换法

  • 运行结果---非跨界行交换

  • 运行结果---非跨界列交换

  • 运行结果---块行交换

  • 运行结果---块列交换

  • 运行结果---矩阵旋转

  • 运行结果---矩阵行镜像

  • 运行结果---矩阵列镜像

  • 运行结果---矩阵对角线镜像

六,引用资料致谢

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐松亮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值