4、6、9宫数独

本文介绍了作者对数独算法的优化,使用大数组加速计数,位运算减少内存消耗。文章详细描述了一个父类与4宫格、6宫格、9宫格数独子类的实现,并给出了性能测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看了一篇数独算法的文章:数独(Sudoku)算法实现+初步优化_按照难度生成数独题目的算法-CSDN博客

感觉算法优化得很好,特别是用一个大数组来快速查数,比map快得多。但付出的代价是占用更多的内存。另外就是位运算,作者用了位运算可以节省内存,也节省了查询哪个数字已用所需的时间。

有没有快速的位运算计算一个数是2的几次方呢,如给出128马上算出是7,如果能比查数组更快就可以淘汰那个稀疏数组了。

于是拿来修改一下,实现一个父类与4、6、9宫数独子类。

测试示例来源于数独题目 -- 三思数独

代码如下。

//参考 https://blog.csdn.net/BLCJY/article/details/106110737

#include <vector>
#include <cmath>
#include <iostream>
#include <sstream>
#include <chrono>
using namespace std;
constexpr int size9 = 9; // 状态数组按最长的分配

class sudoku
{
private:
    const int count;
    const int countR;
    const int countAll;
    const int suSize;
    int row[size9];
    int column[size9];
    int blocks[size9];
    static int num[(1 << (size9 - 1)) + 1];
    const vector<int>& maskQ; //掩码
    vector<int> suTab; //题目

    static void initNum();

    bool dfs(int z);
public:
    sudoku(int co, vector<int>& mas);

    bool work(const vector<int>& tab);

    string ToString() const;
};

int sudoku::num[(1 << (size9 - 1)) + 1];

void sudoku::initNum()
{
    for (int i = 0; i < size9; i++)
    {
        sudoku::num[1 << i] = i + 1;
    }
}

sudoku::sudoku(int co, vector<int>& mas) :count(co), countR((1 << count) - 1), countAll(count* count), maskQ(mas), suSize(sizeof(int)* countAll), suTab(countAll)
{
    if (num[1] == 0) {
        sudoku::initNum();
    }
}

bool sudoku::work(const vector<int>& tab)
{
    memset(row, 0, sizeof(row));
    memset(column, 0, sizeof(column));
    memset(blocks, 0, sizeof(blocks));
    memset(&suTab[0], 0, suSize);

    for (int k = 0; k < countAll; ++k)
    {
        if (tab[k] > 0 && tab[k] <= count) // 不在合法范围的统一当0
        {
            suTab[k] = 1 << (tab[k] - 1);
            int r = k / count;
            int l = k % count;
            if (suTab[k] & row[r] || suTab[k] & column[l] || suTab[k] & blocks[maskQ[k]])
            {
                return false; //题目有错,不计算
            }
            row[r] |= suTab[k];
            column[l] |= suTab[k];
            blocks[maskQ[k]] |= suTab[k];
        }
    }
    return dfs(0);
}

bool sudoku::dfs(int z)
{
    if (z == countAll)
    {
        return 1;
    }
    if (suTab[z])
    {
        return dfs(z + 1);
    }
    int x = z / count;
    int y = z % count;
    int b = ((row[x] | column[y]) | blocks[maskQ[z]]) ^ countR;//取反一下,表示所有的未用过的数字
    while (b > 0)
    {
        int m = b & (-b);// b&(-b)可以求算出b中第一个"1"与它后面的所有的"0"构成的数字
        row[x] |= m;
        column[y] |= m;
        blocks[maskQ[z]] |= m;

        suTab[z] = m;
        if (dfs(z + 1) == 1)
        {
            return 1;
        }
        int mr = m ^ countR;
        row[x] &= mr;   //回溯
        column[y] &= mr;
        blocks[maskQ[z]] &= mr;
        b &= mr;
    }
    suTab[z] = 0;  //找不到的话就还原
    return 0;
}

string sudoku::ToString() const
{
    stringstream ss;
    for (int k = 0; k < countAll; ++k)
    {
        ss << num[suTab[k]] << " ";
        if (k % count == (count - 1))
        {
            ss << endl;
        }
    }
    return ss.str();
}

class sudoku4 : public sudoku
{
    vector<int> maskQ4 =
    {
        0, 0, 1, 1,
        0, 0, 1, 1,
        2, 2, 3, 3,
        2, 2, 3, 3,
    };

public:
    sudoku4() :sudoku(4, maskQ4)
    {
    }
};

class sudoku6 : public sudoku
{
    vector<int> maskQ6 =
    {
        0, 0, 0, 1, 1, 1,
        0, 0, 0, 1, 1, 1,
        2, 2, 2, 3, 3, 3,
        2, 2, 2, 3, 3, 3,
        4, 4, 4, 5, 5, 5,
        4, 4, 4, 5, 5, 5,
    };
public:
    sudoku6() :sudoku(6, maskQ6)
    {
    }
};

class sudoku9 : public sudoku
{
    vector<int> maskQ9 =
    {
        0, 0, 0, 1, 1, 1, 2, 2, 2,
        0, 0, 0, 1, 1, 1, 2, 2, 2,
        0, 0, 0, 1, 1, 1, 2, 2, 2,
        3, 3, 3, 4, 4, 4, 5, 5, 5,
        3, 3, 3, 4, 4, 4, 5, 5, 5,
        3, 3, 3, 4, 4, 4, 5, 5, 5,
        6, 6, 6, 7, 7, 7, 8, 8, 8,
        6, 6, 6, 7, 7, 7, 8, 8, 8,
        6, 6, 6, 7, 7, 7, 8, 8, 8
    };
public:
    sudoku9() :sudoku(9, maskQ9)
    {
    }
};

void test(sudoku& su, vector<int>& tab)
{
    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
    auto sol = su.work(tab);
    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::chrono::steady_clock::duration elapsed = end - begin;
    long long elapsed_nano = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
    std::cout << std::endl;
    std::cout << "共用时" << elapsed_nano / 1000.0 << "微秒" << std::endl;
    if (sol)
    {
        cout << su.ToString();
    }
    else
    {
        cout << "No Solution";
    }
}

int main()
{
    vector<int> table4
    {
        2, 0, 0, 0,
        0, 3, 4, 0,
        4, 2, 0, 3,
        0, 0, 0, 0,
    };//测试用例
    sudoku4 sd4;

    vector<int> table6
    {
        5, 1, 3, 0, 2, 6,
        2, 4, 0, 1, 5, 0,
        0, 0, 0, 5, 0, 1,
        1, 6, 0, 0, 0, 0,
        0, 2, 0, 3, 0, 0,
        3, 0, 0, 6, 4, 0,
    };//测试用例
    sudoku6 sd6;

    vector<int> table9a
    {
        1, 8, 0, 0, 5, 0, 0, 0, 4,
        9, 3, 7, 6, 4, 8, 1, 5, 0,
        0, 5, 0, 0, 1, 0, 0, 3, 0,
        0, 1, 0, 4, 0, 5, 0, 8, 0,
        7, 6, 3, 0, 8, 0, 4, 1, 5,
        0, 4, 0, 1, 0, 3, 0, 2, 0,
        0, 9, 0, 0, 2, 0, 0, 4, 0,
        0, 2, 6, 5, 3, 4, 7, 9, 1,
        4, 0, 0, 0, 9, 0, 0, 6, 3,
    };//测试用例
    vector<int> table9b
    {
        8, 5, 0, 2, 0, 3, 1, 0, 6,
        3, 2, 0, 0, 6, 5, 8, 9, 0,
        0, 0, 6, 0, 0, 4, 5, 2, 3,
        7, 0, 0, 0, 0, 9, 2, 5, 1,
        0, 1, 0, 0, 5, 0, 0, 8, 0,
        9, 6, 5, 8, 0, 0, 0, 0, 4,
        2, 3, 8, 4, 0, 0, 9, 0, 0,
        0, 7, 1, 9, 2, 0, 0, 3, 8,
        6, 0, 9, 5, 0, 8, 0, 1, 2,
    };//测试用例
    sudoku9 sd9;

    test(sd4, table4);
    test(sd6, table6);
    test(sd9, table9a);
    test(sd9, table9b);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值