最近看了一篇数独算法的文章:数独(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;
}