1,代码:
1.1主函数
#include "stdafx.h"
#include
#include "iostream"
#include "ChessBoard.h"
#include "fstream"
#include "sstream"
using namespace std;
int main(int argc, char* argv[])
{
ChessBoard CB;
int bn = 0;//board number
string s = argv[2];
stringstream ss;
ss << s;
ss >> bn;
ofstream file("sudotiku.txt");
for (int i = 0; i < bn; i++){
CB.printChessBoard(file);
CB.exchange();
file << endl;
cout << endl;
}
file.close();
cout << "执行完毕,请检查";
system("pause");
return 0;
}
1.2类头文件
#pragma once
#include "fstream"
using namespace std;
class ChessBoard
{
public:
ChessBoard();
~ChessBoard();
int* getExchangedNum();
ofstream& printChessBoard(ofstream &);
void exchange();
private:
int exchangedNum[9];//已经交换过的数
int enumNum[9];
int chessBoardRelationship[9][9];
};
1.3类
#include "stdafx.h"
#include "ChessBoard.h"
#include "iostream"
#include
#include
#include "fstream"
using namespace std;
ChessBoard::ChessBoard()
{
int init[9][9] ={
{ 1, 2, 3, 5, 6, 7, 9, 4, 8 },
{ 4, 5, 6, 8, 9, 1, 3, 7, 2 },
{ 7, 8, 9, 2, 3, 4, 6, 1, 5 },
{ 9, 4, 8, 1, 2, 3, 5, 6, 7 },
{ 3, 7, 2, 4, 5, 6, 8, 9, 1 },
{ 6, 1, 5, 7, 8, 9, 2, 3, 4 },
{ 5, 6, 7, 9, 4, 8, 1, 2, 3 },
{ 8, 9, 1, 3, 7, 2, 4, 5, 6 },
{ 2, 3, 4, 6, 1, 5, 7, 8, 9 }
};
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
chessBoardRelationship[i][j]=init[i][j];
}
}
exchange();
}
void ChessBoard::exchange(){
for (int i = 0; i < 9; i++){
enumNum[i] = i + 1;
}
int p = 0;
for (int i = 0; i < 9; i++){
LARGE_INTEGER nFrequency;
if (::QueryPerformanceFrequency(&nFrequency))
{
LARGE_INTEGER nStartCounter;
::QueryPerformanceCounter(&nStartCounter);
::srand((unsigned)nStartCounter.LowPart);
}
p = rand() % (9 - i);
exchangedNum[i] = enumNum[p];
enumNum[p] = enumNum[8 - i];
}
}
ChessBoard::~ChessBoard()
{
}
int* ChessBoard::getExchangedNum(){
return &(exchangedNum[0]);
}
ofstream & ChessBoard::printChessBoard(ofstream &file){
int count=0;
int count1=0;
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
cout << exchangedNum[chessBoardRelationship[i][j]-1]<<" ";
file << exchangedNum[chessBoardRelationship[i][j] - 1] << " ";
count++;
if (count % 3 == 0){
cout << " ";
file << " ";
}
}
count1++;
if (count1 % 3 == 0){
cout << endl;
file << endl;
}
cout << endl;
file << endl;
}
return file;
}
2,分析:
以上代码几乎没有注释,这没有关系,本人也从来没有玩过数独游戏,这也没关系,让我们从最原始的问题开始。
首先,一个数独棋盘应是有解的,在提示数足够多的情况下,它应是有唯一解的。我曾注意到一位同僚,在随机生成n个数独棋盘这个问题上,采取如下策略:先求解出一个棋盘,然后,随机重新排列每个三行、每个三列、每三个三行和每三个三列。这样做显然不会违背数独规则,但是,其中似乎有些不变的东西。
从线性代数的角度看,这显然是一个无法再化简的矩阵,也就是说,无论怎样变化,它还是同一个矩阵,那么,我们可以很容易地手写一个矩阵,假定它最终可以变换为这样的关系形式:
1, 2, 3, 5, 6, 7, 9, 4, 8
4, 5, 6, 8, 9, 1, 3, 7, 2
7, 8, 9, 2, 3, 4, 6, 1, 5
9, 4, 8, 1, 2, 3, 5, 6, 7
3, 7, 2, 4, 5, 6, 8, 9, 1
6, 1, 5, 7, 8, 9, 2, 3, 4
5, 6, 7, 9, 4, 8, 1, 2, 3
8, 9, 1, 3, 7, 2, 4, 5, 6
2, 3, 4, 6, 1, 5, 7, 8, 9
请注意,这只是一种关系形式,而不是最终输出的数值,其中每两个数之间没有任何关系,这个矩阵嘛就是代码中给出的chessBoardRelationship矩阵。
这个矩阵明显是死的,那么我们应当对它进行变换,按照上面的描述,策略如下:1对应的数值是在1到9之中随机的,而且一旦确定,则这个对应的映射关系不能被其它映射所破坏,即数值不能被重复选定。我们使用两个数组来描述这样的映射关系。
最终问题,输出,按照关系数组的格式,根据关系数组的数值查找映射,输出数值,由于此时关系数组每个数值对应的映射时随机的,可能出现的映射总量为9位数,问题得到解决。
这其中涉及到一些细节,比如,如何让映射关系不重复,如何在运行函数时传入参数,内联函数等,这些在网络上都能得到详尽的解答,再次不再赘述。
关于程序运行的效率,需要在此代码的基础上屏蔽到控制台的输出,然后测试,经测试,实际在形成10000个数独棋盘时,命令行下光标闪烁5次,由于在本机上光标每秒闪烁一次,估算的运行效率为,5秒以内,形成10000个已经解答的数独棋盘并输出到文件。经使用visual studio自带测试工具,总cpu时间稳定在4700毫秒。(测试平台:ThinkPad E485 AMD_Ryzen5 2500U 8G)
3,心得
3.1,写程序要善于“投机取巧”,
3.2,要善于思考已有的方案。
最开始拿到这个题目时,我曾花费大量精力,连续四天,力图使其在每一个位置上随机输出正确的数字,并力图一次形成未解答的棋盘和答案,甚至离成功只有一步之遥,然而,在了解他人的做法以后,迅速跳出了这个圈子,即便是想达到题目之外的要求,也可以先得出已解答的棋盘,再从中选取提示数,从构思直到最终确认实现题目要求,在几乎没有c++基础的情况下,6小时就达成要求。