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

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


目录

一,前言

二,开发环境

电脑系统

编译器

编程语言

编辑与工程环境

三,编程思路与流程

流程

设计数独数据结构

编写测试代码框架

编写核心代码---接口函数

编写核心代码---寻找下一个空单元

编写核心代码---利用递归回溯寻找空单元并填入合规数据

四,完整源码(免费精简版)

五,免费精简版源码结果演示

六,正常版源码下载链接与演示

源码下载链接

演示


一,前言

  • 数独,说实话我玩过,且并不是很喜欢玩,觉得无聊也太浪费时间,当然玩的水平也不咋样。
  • 但是我为什么又写这篇文章又编写代码的去做呢?
    • 因为它是一个很好的适合计算机处理的数学算法的问题。
    • 数独规则很简单
    • 解题需要计算机编程的递归
    • 出题需要矩阵变换
  • 数独的最大终盘数量
    • 目前普遍认为是6670903752021072936960,也就是所如果完全随机,一个人一辈子玩数独很难遇到万全一样的题,这个数量级大大出乎了我的意料。
    • 刨去对称,换行等等等规则后,也有5472730538个终盘。
    • 本人一开始想用穷举给算出来,看到这个数后就服了,即使用最快的计算机就算把计算机算坏了也算不完,而且统计这么大的数,还要做大数计算程序,目前的64位计算机肯定是不行。
  • 本文着重说明通过C语言来解析数独问题,至于数独的随机出题问题由下一篇文章说明
  • 本文只针对标准的9宫格数独,不讨论其他变种数独,也不讨论数独的难度等级等其他问题。
  • 本文为了简化代码,题目直接在源码中,结果直接终端打印,没有做文件数据的输入输出等操作。
  • 本文有免费代码下载(复制即可),可以计算并打印出第一个解。
  • 本文另有收费代码,可以计算并打印出指定数量的解,并且统计题目有效解的总数量。
    • 代码只建议用来学习,并没有做太多的容错处理。
    • 这是我个人第一个收费的代码,且只是象征性收取5元哦,对我来说,只是跑跑流程,玩一玩,看看这玩意能不能赚到钱,哈哈,毕竟打字编程改良验证每个环节也消耗了我的一些精力。所以太认真较真讲究值与不值的朋友就别买了。

二,开发环境

  • 电脑系统

    • windows10
  • 编译器

    • cygwin下的gcc
  • 编程语言

    • C语言
  • 编辑与工程环境

    • VSCode
    • 不了解VSVode安装配置的请看本人的原创文档:
      • 徐松亮软件应用教学-基于Visual Studio Code的C语言开发环境搭建

三,编程思路与流程

  • 流程

    • 本着自顶向下的程序设计理念,从使用者的角度来设计代码。
  • 设计数独数据结构

    • 满足条件:

      • 灵活载入题目
      • 用户可以自由设定求解的数量(打印出来解的数量)
      • 可以统计指定题目的所有解的数量(不打印具体解的内容,只统计解的个数)
    • 程序实现如下,按输入,输入输出,输出三类,给出如下程序结构并标注。
  • 编写测试代码框架

    • 满足条件:

      • 1,自由载入数独题目
      • 2,配置解析参数
        • 解一个解就行?

          • 耗时最小
        • 解指定数量的解,并打印?
          • 耗时依照题目和设定数量而定
        • 解指定数量的解,并且要统计出所有解的数量?
          • 耗时最长,因为得出指定数量的解之后程序依然不能停止,需要持续计算,直到穷举所有数据为止,只有这样才会统计出所有的解的数量。
    • 程序实现如下:
  • 编写核心代码---接口函数

    • 满足条件:

      • 只有一个结构体指针参数,计算的结果也打印到这个结构体里,这样用户只要弄明白这个结构体即可。
      • 里面包含两层函数
        • 1,初始化
        • 2,执行计算
    • 程序实现如下:
  • 编写核心代码---寻找下一个空单元

    • 满足条件

      • 每个单元有效的数字为1-9,而初始的0就可以用来表达是空单元。
      • 为了加快计算速度,我们每次不是从头找,而是从参数指定行开始搜索。
    • 程序实现如下:
  • 编写核心代码---利用递归回溯寻找空单元并填入合规数据

    • 程序架构

      • 函数(结构体指针,行,列)
        {参数循环{1,编写数独规则1-9都尝试完了,仍不合规,则回溯填充违规,则继续尝试下个数2,寻找下一个空单元找不到下一个空单元,说明每个单元都已经填入合规数,解成立。3,利用递归尝试向下一单元填入合规数}
        }
    • 程序实现:

      • 数独规则核心代码

四,完整源码(免费精简版)

/********************************************************************************* @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_SudoBuf[XSLGAMESUDO_SUDOKU_LEVEL][XSLGAMESUDO_SUDOKU_LEVEL] = {8, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 3, 6, 0, 0, 0, 0, 0,0, 7, 0, 0, 9, 0, 2, 0, 0,0, 5, 0, 0, 0, 7, 0, 0, 0,0, 0, 0, 0, 4, 5, 7, 0, 0,0, 0, 0, 1, 0, 0, 0, 3, 0,0, 0, 1, 0, 0, 0, 0, 6, 8,0, 0, 8, 5, 0, 0, 0, 1, 0,0, 9, 0, 0, 0, 0, 4, 0, 0};
//
XSLGAMESUDO_S_SUDO XslGameSudo_s_Sudo;
/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*** @brief   格式化打印* @note    打印指定行列的数组* @param   *pXslGameSudoS --- 数据指针*           mode            --- 0-打印问题  1-打印答案* @return  null*/
static void XslGameSudo_FormatPrint(XSLGAMESUDO_S_SUDO *pXslGameSudoS, uint8_t mode)
{uint8_t i, j, k;uint8_t *pbuf;// 打印问题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->OutAnswersCount == 0){printf("Sudo Processor : No Solution!\n");return;}//pbuf = pXslGameSudoS->cells;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");}}}}
}
/*** @brief   寻找下一个未填充的单元* @note    寻找下一个未填充的单元* @param   buf            --- 输入输出    --- 数据*             startrow    --- 输入      --- 查找起始行,此函数用于优化计算速度*          *row        ---*            *col        ---* @return  0-没有空单元  1-有空单元*/
static uint8_t XslGameSudo_findNextEmpty(XSLGAMESUDO_S_SUDO *pXslGameSudoS, int startrow, uint8_t *row, uint8_t *col)
{uint8_t i, j;for (i = startrow; i < XSLGAMESUDO_SUDOKU_LEVEL; i++){for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL; j++){if (pXslGameSudoS->cells[i][j] == 0){*row = i;*col = j;return OK;}}}return ERR;
}
/*** @brief   寻找下一个未填充的单元* @note    寻找下一个未填充的单元* @param   arr*           row*            col* @return  0-没有符合规则的算法  1-已经填入符合规则的数据*/
static int XslGameSudo_Cal(XSLGAMESUDO_S_SUDO *pXslGameSudoS, uint8_t row, uint8_t col)
{uint8_t i = 0, j = 0, n = 0;uint8_t next_row = 0, next_col = 0;while (1){//-----------------------1,向空单元填数next_num:// 填充失败判断-->尝试填充1-9都失败++n;if (n >= (XSLGAMESUDO_SUDOKU_LEVEL + 1)){break;}// 填充违规判断--1-->判断行重复for (j = 0; j < XSLGAMESUDO_SUDOKU_LEVEL; j++){if (pXslGameSudoS->cells[row][j] == n){goto next_num;}}// 填充违规判断--2-->判断列重复for (i = 0; i < XSLGAMESUDO_SUDOKU_LEVEL; i++){if (pXslGameSudoS->cells[i][col] == n){goto next_num;}}// 填充违规判断--3-->判断所在小九宫格重复uint8_t x = (row / 3) * 3;uint8_t y = (col / 3) * 3;for (i = x; i < x + 3; i++){for (j = y; j < y + 3; j++){if (pXslGameSudoS->cells[i][j] == n){goto next_num;}}}//填充确认-->可以填充pXslGameSudoS->cells[row][col] = n;//-----------------------2,寻找下一个空单元//如果9宫格已填满if (ERR == XslGameSudo_findNextEmpty(pXslGameSudoS, row, &next_row, &next_col)){pXslGameSudoS->OutAnswersCount=1;return OK;}//-----------------------3,向下一个空单元填数if (ERR == XslGameSudo_Cal(pXslGameSudoS, next_row, next_col)){pXslGameSudoS->cells[row][col] = 0;continue;}else{return OK;}}// 失败return ERR;
}
/*** @brief   数独解析* @note    数独解析,计算的结果都包含在参数结构体里,所以并没有返回值。* @param   *pXslGameSudoS   --- 数独结构体指针* @return  null*/
void XslGameSudo_Processor(XSLGAMESUDO_S_SUDO *pXslGameSudoS)
{uint8_t row, col;//初始化XslGameSudo_findNextEmpty(pXslGameSudoS, 0, &row, &col);pXslGameSudoS->OutAnswersCount = 0;if (pXslGameSudoS->pAnswersCount != NULL){*(pXslGameSudoS->pAnswersCount) = 0;}//计算XslGameSudo_Cal(pXslGameSudoS, row, col);
}
/*** @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 Processor(simply)         \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 = 0;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();}
}

五,免费精简版源码结果演示

六,正常版源码下载链接与演示

  • 收费源码下载

    • 本来想用Demo大师,不过没弄明白,还是用最笨的办法吧。
    • QQ添加联络本博主:5387603
    • 支付宝/微信付费5元
    • 获取获取下载链接和下载码
    • 操作半天,实属扯犊子,太麻烦,说不上什么时候就全开源了,但是让我现在就开源,白折腾一通还心有不甘,这篇就这么地吧,下回还是全开源的好!
  • 演示

徐松亮算法教学-基于C语言的数独(九宫格)求解(含多解和解数统计)相关推荐

  1. 徐松亮STM32教学-嵌入式工程师培训资料(重磅资料)

    版权声明:本文为博主徐松亮的原创作品,未经允许不得转载,多谢支持!QQ:5387603 推荐点击此链接:欢迎进入徐松亮博客一站式导航搜索(随时更新) 之所以所这篇资料是重磅资料,是因为基于STM32开 ...

  2. 徐松亮Python教学-黑客篇-zip压缩文件的破解

    版权声明:本文为博主徐松亮的原创作品,未经允许不得转载,多谢支持!QQ:5387603 推荐点击此链接:欢迎进入徐松亮博客一站式导航搜索(随时更新) 首先,此文档已学习为主,不要用来随意破解别人的zi ...

  3. 徐松亮常用开发软件与网站

    版权声明:本文为博主徐松亮的原创作品,未经允许不得转载,多谢支持! 推荐点击此链接:欢迎进入徐松亮博客一站式导航搜索(随时更新) 俗话说,万事开头难,无论做什么,了解工具是方向,方向对了事半功倍,方向 ...

  4. 徐松亮的杂谈随笔(随时更新)

    版权声明:本文为博主徐松亮的原创作品,未经允许不得转载,多谢支持!QQ:5387603 推荐点击此链接:欢迎进入徐松亮博客一站式导航搜索(随时更新) 本文是个人的点滴记录,没有主题,是突然想到了什么, ...

  5. 广义相加模型(GAM)与向前逐步选择算法(基于R语言)

    广义相加模型(GAM)与向前逐步选择算法(基于R语言) 一.题目 (a)使用College数据集,以Outstate作为响应变量,其余作为预测变量,使用逐步回归得到一组合适的预测变量的子集. (b)将 ...

  6. c语言通用数据结构和常用算法库,基于C语言的通用数据结构和算法库

    本人最近在学习数据结构的课程,在过程中发现用C语言来实现各种数据结构类型的时候很难做到真正意义上的通用的数据结构,于是在网上搜罗了一些所谓的C语言通用数据结构库,在此也将这些数据结构库一一罗列,方便大 ...

  7. 算法图解(基于C语言)

    第一章 算法简介 一些常见的大O运行时间(以排序算法举例) O(log n), 也叫对数时间,这样的算法包括二分查找. O(n) , 也叫线性时间,这样的算法包括简单查找. O(n*log n),快速 ...

  8. gui窗口遮挡算法_基于 C 语言开发的 GUI 框架

    一.介绍 AWTK全称Toolkit AnyWhere,是ZLG开发的开源GUI引擎,旨在为嵌入式系统.WEB.各种小程序.手机和PC打造的通用GUI引擎,为用户提供一个功能强大.高效可靠.简单易用. ...

  9. 基于python 的电影推荐算法_基于python语言编程的矩阵分解电影推荐算法

    [实例简介]一种基于矩阵分解方法的电影推荐算法 [实例截图] [核心代码] import numpy as np from numba import cuda, float64, jit from s ...

最新文章

  1. linux回调函数的使用
  2. UVA11997求前k个和,多路归并问题
  3. django 使用Ajax方式POST JSON数据包
  4. sql server 配置管理器里为什么是32位_死磕 Nginx 系列:Nginx 限流配置
  5. 【渝粤教育】国家开放大学2019年春季 1260软件工程 参考试题
  6. java null转换jason_Java笔记Java常量、变量
  7. LintCode解题目录
  8. 如何保持安全在线的五大技巧
  9. HDUOJ-----X问题
  10. 车辆检测及型号识别,准确率接近90%
  11. 在CentOS中安装和部署nacos配置中心
  12. Unity中制作小地图
  13. MySQL 事务的实现原理,写得太好了!
  14. 二年级四则运算扩展,可以指定题目数量,并可支持真分数运算
  15. 《人人都是产品经理》读书笔记
  16. 【区块链论文整理】SIGMOD篇(一)
  17. 于Cd(Ⅲ)金属有机骨框架的新型造影剂Cd-MOF/Gd-DTPA/DMPE-DTPA-Gd-DMPE/
  18. 【百问网】七天智能家居实战
  19. 【C++】不同模板对象之间赋值
  20. 服务器系统防火墙设置在哪里设置方法,服务器怎么设置防火墙设置在哪里

热门文章

  1. 内固定取出术后护理_骨折内固定取出术后注意事项
  2. 论文阅读笔记 (CVPR 2019) Gait Recognition via Disentangled Representation Learning
  3. oracle参数说明
  4. 经典二十四点程序游戏
  5. 如何恢复vscode的默认配置_VS恢复默认设置的2种方法
  6. 一个牛人给Java初学者的建议(必看篇)
  7. 骁龙芯片性能排行2020_手机芯片性能排行榜
  8. java 圆周率_Java 计算圆周率
  9. vivado下使用ILA抓取波形
  10. 搜索算法-深搜与广搜