利用挖洞法生成数独

前言知识

基于挖洞法生成有效的数独游戏格局需要确保生成的谜题有唯一解,并且在挖洞过程中保持数独规则的完整性。

什么是挖洞法

挖洞法(Hole-Digging Method)是一种生成数独谜题的方法,通过从一个已经解决的数独板中去掉一些数字,从而创建一个有趣的、具有一定难度的数独游戏格局。这个方法的目标是创造一个数独谜题,使得玩家需要填充空格,而填充过程中需要遵守数独的规则,并且最终谜题仍然有唯一解。

以下是挖洞法的详细解释:

生成完整数独解: 首先,挖洞法需要生成一个完整的数独解。这个完整解是一个符合数独规则的9x9数独板,其中每一行、每一列和每个3x3子宫格都包含1到9的数字,且每个数字在每行、每列和子宫格内都不重复。

挖洞过程: 一旦有了完整的数独解,挖洞过程就开始了。这是通过从数独板中去掉一些数字(将它们设为0或其他占位符号)来实现的。去掉的数字越多,难度就越大。

保持唯一解: 挖洞的关键在于保持数独谜题有唯一解。这意味着,无论玩家填充哪个空格,只有一种方法可以将谜题解决。为了确保唯一解,挖掉一个数字后,需要通过解数独的方法来检查是否还有唯一解。如果去掉某个数字导致谜题有多个解,那么这个挖洞的位置就不合适。

控制难度: 挖洞的数量和位置可以影响数独游戏的难度。挖掉更多的数字通常会增加难度,因为玩家必须更多地依赖逻辑推理和试错来填充格子。

验证生成: 生成的数独谜题应该可以通过求解数独的方法来验证其唯一解和有效性。

总之,挖洞法是一种用于生成数独谜题的方法,它需要在保持数独规则和唯一解的前提下,逐步去掉数字,创造出不同难度级别的数独游戏格局。这是一种常见的数独生成方法,但需要仔细调整和验证,以确保生成的谜题是有趣和有效的。

如何生成一个完整的数独盘

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>

using namespace std;
const int N = 9;
const int EMPTY = 0;

bool is_valid(std::vector<std::vector<int>>& board, int row, int col, int num) {
    // 检查行和列中有没有和num一样的数字
    for (int i = 0; i < N; i++) {
        if (board[row][i] == num || board[i][col] == num) {
            return false;
        }
    }

    // 检查小九宫格里面有没有和 num 一样的数字
    int start_row = 3 * (row / 3);
    int start_col = 3 * (col / 3);
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[start_row + i][start_col + j] == num) {
                return false;
            }
        }
    }

    return true;
}

bool solve_sudoku(std::vector<std::vector<int>>& board) {
    for (int row = 0; row < N; row++) {
        for (int col = 0; col < N; col++) {
            if (board[row][col] == EMPTY) {                        // 如果这个位置还没填好
                for (int num = 1; num <= N; num++) {               // 依次填充
                    if (is_valid(board, row, col, num)) {          // 判断填入的数字合不合法
                        board[row][col] = num;                     // 填入
                        if (solve_sudoku(board)) {                 // 继续填入下一个
                            return true;
                        }
                        board[row][col] = EMPTY;                   // 回溯
                    }
                }
                return false;
            }
        }
    }
    return true;
}
// 输出数独
void print_sudoku(std::vector<std::vector<int>>& board) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            std::cout << board[i][j] << " ";
        }
        std::cout << std::endl;   // 换行
    }
}

int main() {
    std::vector<std::vector<int>> sudoku_solution(N, std::vector<int>(N, 0));
    if (solve_sudoku(sudoku_solution)) {
        std::cout << "Generated Sudoku Solution:" << std::endl;
        print_sudoku(sudoku_solution);
    }
    else {
        std::cout << "No solution found." << std::endl;
    }

    return 0;
}

利用递归和回溯进行生成一个完整的数独

开始挖洞

#include "fun.h"

#include <iostream>
#include <vector>
#include <algorithm>
#include <random>

using namespace std;
const int N = 9;
const int EMPTY = 0;

bool is_valid(std::vector<std::vector<int>>& board, int row, int col, int num) {
    // 检查行和列中有没有和num一样的数字
    for (int i = 0; i < N; i++) {
        if (board[row][i] == num || board[i][col] == num) {
            return false;
        }
    }

    // 检查小九宫格里面有没有和 num 一样的数字
    int start_row = 3 * (row / 3);
    int start_col = 3 * (col / 3);
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[start_row + i][start_col + j] == num) {
                return false;
            }
        }
    }

    return true;
}

bool solve_sudoku(std::vector<std::vector<int>>& board) {
    for (int row = 0; row < N; row++) {
        for (int col = 0; col < N; col++) {
            if (board[row][col] == EMPTY) {                        // 如果这个位置还没填好
                for (int num = 1; num <= N; num++) {               // 依次填充
                    if (is_valid(board, row, col, num)) {          // 判断填入的数字合不合法
                        board[row][col] = num;                     // 填入
                        if (solve_sudoku(board)) {                 // 继续填入下一个
                            return true;
                        }
                        board[row][col] = EMPTY;                   // 回溯
                    }
                }
                return false;
            }
        }
    }
    return true;
}

// 输出数独
void print_sudoku(std::vector<std::vector<int>>& board) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            std::cout << board[i][j] << " ";
        }
        std::cout << std::endl;   // 换行
    }
}
void remove_numbers(std::vector<std::vector<int>>& board, int holes) {
    std::random_device rd;            // 创建一个随机设备以获取种子
    std::mt19937 gen(rd());           // 创建一个 Mersenne Twister 随机数生成器
    std::vector<int> positions(N * N);

    // 初始化 positions 数组,表示数独盘中每个格子的索引
    for (int i = 0; i < N * N; i++) {
        positions[i] = i;
    }

    // 使用随机数生成器对格子索引进行随机排序
    std::shuffle(positions.begin(), positions.end(), gen);

    // 逐个尝试挖洞
    for (int i = 0; i < N * N; i++) {
        int row = positions[i] / N;
        int col = positions[i] % N;
        int temp = board[row][col];    // 临时保存当前格子的值
        board[row][col] = EMPTY;       // 将当前格子置为空

        std::vector<std::vector<int>> board_copy = board;

        // 检查挖洞后的数独是否仍然有唯一解
        if (solve_sudoku(board_copy)) {
            if (--holes == 0) {
                break;                  // 达到所需挖洞数量,退出循环
            }
        }
        else {
            board[row][col] = temp;     // 恢复格子的原始值,因为挖洞后无唯一解
        }
    }
}


int main() {
    std::vector<std::vector<int>> sudoku_solution(N, std::vector<int>(N, 0));
    if (solve_sudoku(sudoku_solution)) {
        std::cout << "Generated Sudoku Solution:" << std::endl;
        print_sudoku(sudoku_solution);

        std::vector<std::vector<int>> sudoku_puzzle = sudoku_solution;
        int holes = 40;  // 要挖掉的数字数量
        remove_numbers(sudoku_puzzle, holes);

        // 打印生成的数独谜题
        std::cout << "Generated Sudoku Puzzle:" << std::endl;
        print_sudoku(sudoku_puzzle);
    }
    else {
        // 操作失败
        std::cout << "No solution found." << std::endl;
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/wniuniu_/article/details/132533926
今日推荐