C# 数独求解算法的实现

C# 数独求解算法的实现

本文将详细讲解如何使用C#语言实现数独求解算法。

数独简介

数独是一种逻辑类的游戏,玩家需要在9*9宫的大九宫格中,填入数字1~9,使每行、每列、每个小九宫格内都恰好包含数字1~9,且不重复。

算法思路

数独求解算法的基本思路是采用回溯算法。从数独的左上角开始,依次尝试填入1~9的数字,若当前填入的数字满足数独条件,则进入下一格继续填入数字;否则回溯到上一格重新填入数字。当填完数独的最后一格时,判断数独是否已成功求解。

算法实现

数独数据结构设计

我们首先需要定义数独的数据结构,以便于进行算法实现。数独的数据结构应该包含以下信息:

  • 二维数组grid:表示数独的状态,0表示该格尚未填入数字,1~9表示当前格填入的数字。
  • HashSet数组rows、cols、blocks:分别表示每一行、每一列、每一个小九宫格内已出现的数字。
public class Sudoku
{
    private int[][] grid;
    private HashSet<int>[] rows;
    private HashSet<int>[] cols;
    private HashSet<int>[] blocks;
}

初始化数独数据结构

对于数独,我们需要先将其数据结构进行初始化。具体来说,对于每一个元素(grid[i][j]):

  • 将该元素所在的行(rows[i])、列(cols[j])、小九宫格(blocks[i/3][j/3])都加入到HashSet中,表示这些数字已在数独中出现过。
  • 如果该元素不为0,则将其加入到以上的三个HashSet中。
public void Initialize(int[][] board)
{
    grid = board;

    rows = new HashSet<int>[9];
    cols = new HashSet<int>[9];
    blocks = new HashSet<int>[9];
    for (int i = 0; i < 9; i++)
    {
        rows[i] = new HashSet<int>();
        cols[i] = new HashSet<int>();
        blocks[i] = new HashSet<int>();
    }

    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            if (board[i][j] != 0)
            {
                rows[i].Add(board[i][j]);
                cols[j].Add(board[i][j]);
                blocks[i / 3 * 3 + j / 3].Add(board[i][j]);
            }
        }
    }
}

回溯算法实现

数独求解的具体实现方法,是通过回溯算法实现的。为方便实现,我们将回溯算法定义为递归函数:

public bool Solve(int i, int j)
{
    if (j == 9)
    {
        i++;
        j = 0;
        if (i == 9)
        {
            return true; // 数独已成功求解
        }
    }

    if (grid[i][j] != 0)
    {
        return Solve(i, j + 1);
    }

    for (int num = 1; num <= 9; num++)
    {
        if (!rows[i].Contains(num) && !cols[j].Contains(num) && !blocks[i / 3 * 3 + j / 3].Contains(num))
        {
            grid[i][j] = num;
            rows[i].Add(num);
            cols[j].Add(num);
            blocks[i / 3 * 3 + j / 3].Add(num);

            if (Solve(i, j + 1))
            {
                return true;
            }

            grid[i][j] = 0;
            rows[i].Remove(num);
            cols[j].Remove(num);
            blocks[i / 3 * 3 + j / 3].Remove(num);
        }
    }
    return false;
}

算法的每一次递归都尝试填入1~9中的一个数字,若填入的数字合法,则进入下一格继续回溯;否则回溯到上一格重新填入数字,直至所有格子都填满。

示例说明

假设下面是一个未解决的数独:

0 0 0 0 0 0 0 6 7
0 9 0 0 0 0 8 0 3
8 5 0 7 0 0 0 0 0
4 0 0 0 9 0 0 0 0
0 3 0 0 7 0 0 8 5
0 0 0 0 0 0 4 1 0
0 7 1 0 5 0 0 0 0
0 0 0 0 1 0 3 0 9
5 0 2 0 0 0 0 7 0

我们可以使用以下代码进行求解:

static void Main(string[] args)
{
    int[][] board = new int[9][];
    board[0] = new int[] { 0, 0, 0, 0, 0, 0, 0, 6, 7 };
    board[1] = new int[] { 0, 9, 0, 0, 0, 0, 8, 0, 3 };
    board[2] = new int[] { 8, 5, 0, 7, 0, 0, 0, 0, 0 };
    board[3] = new int[] { 4, 0, 0, 0, 9, 0, 0, 0, 0 };
    board[4] = new int[] { 0, 3, 0, 0, 7, 0, 0, 8, 5 };
    board[5] = new int[] { 0, 0, 0, 0, 0, 0, 4, 1, 0 };
    board[6] = new int[] { 0, 7, 1, 0, 5, 0, 0, 0, 0 };
    board[7] = new int[] { 0, 0, 0, 0, 1, 0, 3, 0, 9 };
    board[8] = new int[] { 5, 0, 2, 0, 0, 0, 0, 7, 0 };

    Sudoku s = new Sudoku();
    s.Initialize(board);
    bool solved = s.Solve(0, 0);

    if (solved)
    {
        Console.WriteLine("数独已成功求解:");
        for(int i=0;i<9;i++)
        {
            for (int j = 0; j < 9; j++)
            {
                Console.Write("{0} ", s.Get(i, j));
            }
            Console.WriteLine();
        }
    }
    else
    {
        Console.WriteLine("数独无解!");
    }
    Console.ReadKey();
}

程序输出结果为:

数独已成功求解:
2 4 3 5 8 1 9 6 7 
6 9 7 2 4 3 8 5 3 
8 5 1 7 6 9 2 3 4 
4 1 5 8 9 2 7 3 6 
9 3 6 4 7 5 1 8 5 
7 2 8 3 6 1 4 1 9 
3 7 1 9 5 4 6 2 8 
0 0 0 0 1 0 3 0 9 
5 6 2 1 3 8 1 7 0 

上面就是数独求解算法的实现过程。对于更加复杂的数独问题,我们可以使用类似的算法进行求解。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C# 数独求解算法的实现 - Python技术站

(0)
上一篇 2023年6月6日
下一篇 2023年6月6日

相关文章

  • TypeScript Type Innference(类型判断)

    TypeScript Type Inference(类型判断)是 TypeScript 编译器所提供的一种类型推断机制,即在编译时自动推断变量、函数返回值等类型信息,从而使代码更加简洁、易读、易于维护。 TypeScript的类型推断包括以下两种情况: 变量定义时初始化赋值; 函数返回类型推断。 变量定义时初始化赋值 当定义变量并进行初始化赋值操作的时候,T…

    C# 2023年6月8日
    00
  • 浅谈C#中Action和Func回调的常用方式

    下面是详细讲解“浅谈C#中 Action 和 Func 回调的常用方式” 的完整攻略。 什么是回调函数 回调函数是一种常见的程序设计模式,用于将一个函数作为参数传递给另一个函数。在调用这个另一个函数时,它将执行传递的函数作为一部分操作。这种方法被称为“回调”功能。 C# 中的回调 C# 中的回调是通过委托实现的。一个委托是一个类型,它持有对一个或多个方法的引…

    C# 2023年5月15日
    00
  • ASP.NET编程简单实现生成静态页面的方法【附demo源码下载】

    为了更好地讲解“ASP.NET编程简单实现生成静态页面的方法”,我们需要分为以下几个部分进行详细讲解: 为什么需要生成静态页面? 静态页面生成的基本思路和流程 实现过程和示例说明 1. 为什么需要生成静态页面? 当我们访问一个网站时,实际上每一次访问都需要服务器去动态生成页面并将结果返回给浏览器。但是,当网站的访问量很大时,频繁地动态生成页面会极大地消耗服务…

    C# 2023年5月31日
    00
  • 记一次 .NET 某设备监控系统 死锁分析

    一:背景 1. 讲故事 上周看了一位训练营朋友的dump,据朋友说他的程序卡死了,看完之后发现是一例经典的死锁问题,蛮有意思,这个案例算是学习 .NET高级调试 入门级的案例,这里和大家分享一下。 二:WinDbg 分析 1. 程序为什么会卡死 因为是窗体程序,所以看主线程的线程栈就好了,如果卡在 用户态 那这个问题相对容易解决,如果卡在 内核态 这个问题就…

    C# 2023年4月18日
    00
  • .net设计模式之装饰模式(Decorator)

    当我们需要在不改变原有类的情况下对其进行新功能添加或修改时,装饰模式是一种适用的设计模式。它允许向一个现有对象添加新的功能,同时又不改变其结构。该模式是一种结构性模式。 装饰模式(Decorator)的基本结构 装饰模式有四个角色: 抽象构建(Component):定义一个对象接口,可以给这些对象动态地添加职责。 具体构建(ConcreteComponent…

    C# 2023年6月3日
    00
  • websocket与C# socket相互通信

    web端代码就是js代码,C#有两种方式:使用第三方库,如Fleck,使用C#原生socket编程实现   web端: <!doctype html> <html lang=”zh-CN”> <head> <meta charset=”UTF-8″> <title>下发网站上文件到学生机</t…

    C# 2023年4月24日
    00
  • C#使用二分查找法判断指定字符的方法

    下面为您详细讲解“C#使用二分查找法判断指定字符的方法”的完整攻略。 什么是二分查找法 二分查找,也称折半查找,是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或小于中间元素,则搜索下一次查找的数组区间为当前数组区间的左半部分或右半部分。依此类推,直到找到要查找的…

    C# 2023年6月7日
    00
  • C#自定义函数NetxtString生成随机字符串

    下面就为大家讲解一下如何在C#中自定义函数NetxtString生成随机字符串。 1、概述 NetxtString是一个C#字符串扩展类,提供了生成随机字符串的方法,可以指定生成字符串的长度和字符集。下面是该类的源码: public static class NetxtString { private static Random random = new R…

    C# 2023年5月31日
    00
合作推广
合作推广
分享本页
返回顶部