我试图在生成数独网格时检查平方有效性。 (如果删除isSquarevalid())代码将正常工作。 所以我添加了一个isSquareValid(),但这样做填充了我的9x9网格0。 我不确定怎么做,所以我以一种丑陋的方式做到了(检查方格是否为45),如果它没有,那么它返回false。我不明白为什么它用0填充我的网格。
#include <vector>
#include <iostream>
#include <cstdlib>
#include <numeric>
#include <time.h>
#include <string>
#include <sstream>
using std::vector;
using std::pair;
// helper for std::accumulate
bool logical_and(bool x, bool y) {
return x & y;
}
class Grid {
public:
typedef int ElementType;
typedef vector< vector<ElementType> > GridElements;
Grid(const int linesize) :
linesize_(linesize)
{
srand(time(NULL));
// resizes to linesize_ rows & columns, with initial values == 0
gridElements_.resize(linesize_, vector<ElementType>(linesize_, 0));
}
// use like this: cout << grid.to_s();
std::string to_s() const {
std::stringstream ss;
for (int row = 0; row < gridElements_.size(); row++) {
for (int col = 0; col < gridElements_[row].size(); col++) {
ss << gridElements_[row][col] << " ";
}
ss << std::endl;
}
ss << std::endl;
return ss.str();
}
// return true if there are no repeated numbers within filled elements in
// rows/columns, false otherwise
bool isValid() const {
// you would also need to write and call a checkSquare method if you're doing a sudoku puzzle
for (int i = 0; i < linesize_; i++) {
if (!isRowValid(i) || !isColValid(i) || !isSquareValid()) {
return false;
}
}
return true;
}
// the recursive function that actually puts values in the grid elements
// max recursion depth (I think) is linesize_^2
bool fill(int row, int col) {
// stopping conditions
if (!isValid()) {
return false;
}
if ((row == linesize_) || (col == linesize_)) {
return true;
}
int nextCol = (col + 1) % linesize_;
int nextRow = row;
if (nextCol < col) {
nextRow++;
}
// keep a record of what numbers have been tried in this element
vector<bool> attemptedNumbers(linesize_ + 1, false);
attemptedNumbers[0] = true;
// We will continue choosing values for gridElements_[row][col]
// as long as we haven't tried all the valid numbers, and as long as
// the rest of the grid is not valid with this choice
int value = 0;
bool triedAllNumbers = false;
bool restOfGridValid = false;
while (!triedAllNumbers && !restOfGridValid) {
while (attemptedNumbers[value]) {
value = rand() % linesize_ + 1;
}
attemptedNumbers[value] = true;
gridElements_[row][col] = value;
// uncomment this for debugging/intermediate grids
//std::cout << to_s();
// triedAllNumbers == true if all the numbers in [1, linesize_] have been tried
triedAllNumbers = std::accumulate(attemptedNumbers.begin(), attemptedNumbers.end(), true, logical_and);
restOfGridValid = fill(nextRow, nextCol);
}
if (triedAllNumbers && !restOfGridValid) {
// couldn't find a valid number for this location
gridElements_[row][col] = 0;
}
return restOfGridValid;
}
private:
// checks that a number is used only once in the row
// assumes that values in gridElements_ are in [1, linesize_]
// return false when the row contains repeated values, true otherwise
bool isRowValid(int row) const
{
vector<bool> numPresent(linesize_ + 1, false);
for (int i = 0; i < linesize_; i++) {
int element = gridElements_[row][i];
if (element != 0) {
if (numPresent[element]) {
return false;
}
else {
numPresent[element] = true;
}
}
// don't do anything if element == 0
}
return true;
}
// checks that a number is used only once in the column
// assumes that values in gridElements_ are in [1, linesize_]
// return false when the column contains repeated values, true otherwise
bool isColValid(int col) const
{
vector<bool> numPresent(linesize_ + 1, false);
for (int i = 0; i < linesize_; i++) {
int element = gridElements_[i][col];
if (element != 0) {
if (numPresent[element]) {
return false;
}
else {
numPresent[element] = true;
}
}
else {
break;
}
}
return true;
}
bool isSquareValid() const
{
int square1 = gridElements_[0][0] + gridElements_[0][1] + gridElements_[0][2] + gridElements_[1][0] + gridElements_[2][0] + gridElements_[2][1] + gridElements_[1][1] + gridElements_[1][2] + gridElements_[2][2];
int square2 = gridElements_[3][0] + gridElements_[4][0] + gridElements_[5][0] + gridElements_[3][1] + gridElements_[3][2] + gridElements_[4][1] + gridElements_[4][2] + gridElements_[5][1] + gridElements_[5][2];
int square3 = gridElements_[6][0] + gridElements_[7][0] + gridElements_[8][0] + gridElements_[6][1] + gridElements_[6][2] + gridElements_[7][1] + gridElements_[7][2] + gridElements_[8][1] + gridElements_[8][2];
int square4 = gridElements_[0][3] + gridElements_[0][4] + gridElements_[0][5] + gridElements_[1][3] + gridElements_[1][4] + gridElements_[1][5] + gridElements_[2][3] + gridElements_[2][4] + gridElements_[2][5];
int square5 = gridElements_[0][6] + gridElements_[0][7] + gridElements_[0][8] + gridElements_[1][6] + gridElements_[1][7] + gridElements_[1][8] + gridElements_[2][6] + gridElements_[2][7] + gridElements_[2][8];
int square6 = gridElements_[3][3] + gridElements_[3][4] + gridElements_[3][5] + gridElements_[4][3] + gridElements_[4][4] + gridElements_[4][5] + gridElements_[5][3] + gridElements_[5][4] + gridElements_[5][5];
int square7 = gridElements_[6][3] + gridElements_[7][3] + gridElements_[8][3] + gridElements_[6][4] + gridElements_[7][4] + gridElements_[8][4] + gridElements_[6][5] + gridElements_[7][5] + gridElements_[8][5];
int square8 = gridElements_[3][6] + gridElements_[3][7] + gridElements_[3][8] + gridElements_[4][6] + gridElements_[4][7] + gridElements_[4][8] + gridElements_[5][6] + gridElements_[5][7] + gridElements_[5][8];
int square9 = gridElements_[6][6] + gridElements_[6][7] + gridElements_[6][8] + gridElements_[7][6] + gridElements_[7][7] + gridElements_[7][8] + gridElements_[8][6] + gridElements_[8][7] + gridElements_[8][8]; /*obsolète */
if (square1 != 45)
return false;
if (square2 != 45)
return false;
if (square3 != 45)
return false;
if (square4 != 45)
return false;
if (square5 != 45)
return false;
if (square6 != 45)
return false;
if (square7 != 45)
return false;
if (square8 != 45)
return false;
if (square9 != 45)
return false;
return true;
}
// the size of each row/column
int linesize_;
// the 2d array
GridElements gridElements_;
};
int main(int argc, char** argv) {
// 9x9 grid
Grid grid(9);
// pretty sure this is mathematically guaranteed to always return true, assuming the algorithm is implemented correctly ;)
grid.fill(0, 0);
std::cout << grid.to_s();
system("pause");
}
答案 0 :(得分:1)
重写:所以我注意到我误解了你的填充功能,并且不得不做一些测试以使检查工作,因为我最初的想法是不可行的。我没有查看评论中引用的论文,这可能会给你一个更优化的解决方案,但试图扩展你的ansatz以包括正确的方块。最后,最好的方法是简单地调整attemptNumbers数组:它预先填充了所讨论的正方形中已存在的所有数字。这需要在课堂上使用几个辅助函数:activeSquareStart
,activeSquareMembers
,numsInSquare
以及作为检查器,截至目前仍然失败,isSquareValid
。
请注意,我使用的确切代码具有结构化绑定,并且必须使用-std=c++17
之类的标记或其他类似的标记进行编译:
g++ -g -std=c++17 sudoku.cpp
或
g++ -O3 -std=c++17 -DNDEBUG sudoku.cpp
表示没有调试输出。 Visual Studio必须存在类似的标志,我只是不知道它们是什么。除了在项目属性中的某处设置NDEBUG,这可能是VS的默认行为。
#include <vector>
#include <iostream>
#include <cstdlib>
#include <numeric>
#include <time.h>
#include <string>
#include <sstream>
#include <cmath>
#include <iterator>
#include <iomanip>
using std::vector;
// helper for std::accumulate
bool logical_and(bool x, bool y) {
return x & y;
}
//because I was curious how many cycles of filling attempts are required
static ulong cycles = 0;
class Grid {
public:
typedef int ElementType;
typedef vector< vector<ElementType> > GridElements;
typedef std::pair <size_t, size_t> Coordinate;
Grid(const size_t linesize) :
linesize_(linesize)
{
srand(time(NULL));
// some check to make sure I can create subsquares
// I think it needs square numbers so that subsquares work correctly
// using any int != 0 is true
if( sqrt(linesize_)*sqrt(linesize_) != linesize_) {
throw "ERROR: can't create required squares";
}
// resizes to linesize_ rows & columns, with initial values == 0
gridElements_.resize(linesize_, vector<ElementType>(linesize_, 0));
}
// use like this: cout << grid.to_s();
std::string to_s() const {
std::stringstream ss;
for (size_t row = 0; row < gridElements_.size(); row++) {
if(row%(size_t)sqrt(linesize_) == 0){
ss << "\n";
}
for (size_t col = 0; col < gridElements_[row].size(); col++) {
if(col%(size_t)sqrt(linesize_) == 0){
ss << "| ";
}
ss << std::setw(2) << gridElements_[row][col] << std::setw(0) << " ";
}
ss << std::endl;
}
ss << std::endl;
return ss.str();
}
// return true if there are no repeated numbers within filled elements in
// rows/columns, false otherwise
bool isValid() const {
// you would also need to write and call a checkSquare method if you're doing a sudoku puzzle
for (size_t i = 0; i < linesize_; i++) {
if (!isRowValid(i) || !isColValid(i)) {
return false;
}
}
// I had a isSquareValid function being called for all squares here, but that destroyed the puzzle (only 0s)
// since valid squares are now invariants for this problem, I'll leave it to you to actually implement the check :)
// it does test the correct squares, so far
/* for(size_t i = 0; i < linesize_; i+=(size_t)sqrt(linesize_)){ */
/* for(size_t j = 0; j < linesize_; j+=(size_t)sqrt(linesize_)){ */
/* if(!isSquareValid(i,j)){ */
/* return false; */
/* } */
/* } */
/* } */
return true;
}
// the recursive function that actually puts values in the grid elements
// max recursion depth (I think) is linesize_^2
bool fill(size_t row, size_t col) {
// stopping conditions
if (!isValid()) {
return false;
}
if ((row == linesize_) || (col == linesize_)) {
return true;
}
size_t nextCol = (col + 1) % linesize_;
size_t nextRow = row;
if (nextCol < col) {
nextRow++;
}
// keep a record of what numbers have been tried in this element
// exclude all numbers already present in the active squre before beginning
vector<bool> attemptedNumbers = numsInSquare(row, col);
attemptedNumbers[0] = true;
// We will continue choosing values for gridElements_[row][col]
// as long as we haven't tried all the valid numbers, and as long as
// the rest of the grid is not valid with this choice
int value = 0;
bool triedAllNumbers = false;
bool restOfGridValid = false;
while (!triedAllNumbers && !restOfGridValid) {
while (attemptedNumbers[value]) {
value = rand() % linesize_ + 1;
}
attemptedNumbers[value] = true;
gridElements_[row][col] = value;
// uncomment this for debugging/intermediate grids
#ifndef NDEBUG
std::cout << ++cycles << '\n';
std::cout << to_s();
#endif
// triedAllNumbers == true if all the numbers in [1, linesize_] have been tried
triedAllNumbers = std::accumulate(attemptedNumbers.begin(), attemptedNumbers.end(), true, logical_and);
restOfGridValid = fill(nextRow, nextCol);
}
if (triedAllNumbers && !restOfGridValid) {
// couldn't find a valid number for this location
gridElements_[row][col] = 0;
}
return restOfGridValid;
}
private:
// checks that a number is used only once in the row
// assumes that values in gridElements_ are in [1, linesize_]
// return false when the row contains repeated values, true otherwise
bool isRowValid(size_t row) const {
vector<bool> numPresent (linesize_ + 1, false);
for (size_t i = 0; i < linesize_; i++) {
int element = gridElements_[row][i];
if (element != 0) {
if (numPresent[element]) {
return false;
}
else {
numPresent[element] = true;
}
}
// don't do anything if element == 0
}
return true;
}
// checks that a number is used only once in the column
// assumes that values in gridElements_ are in [1, linesize_]
// return false when the column contains repeated values, true otherwise
bool isColValid(int col) const {
vector<bool> numPresent (linesize_ + 1, false);
for (size_t i = 0; i < linesize_; i++) {
int element = gridElements_[i][col];
if (element != 0) {
if (numPresent[element]) {
return false;
}
else {
numPresent[element] = true;
}
}
else {
break;
}
}
return true;
}
//NEW HELPER FUNCTIONS START HERE
//upper left corner of square the coordinate (row,col) lies in
Coordinate activeSquareStart(size_t row, size_t col) const {
return Coordinate{ row - (row % int(std::sqrt(linesize_))), col - (col % int(std::sqrt(linesize_)))};
}
vector<Coordinate> activeSquareMembers(size_t const row, size_t const col) const{
//requires -std=c++17 or similar flag for structured binding auto [..] = .. to work!
// if that is a problem, create a temporary std::pair instead
auto [x, y] = activeSquareStart(row, col);
vector<Coordinate> members;
members.reserve(linesize_);
for(size_t i=0; i<sqrt(linesize_); ++i){
for(size_t j=0; j<sqrt(linesize_); ++j){
members.push_back({x+i, y+j});
}
}
return members;
}
vector<bool> numsInSquare(size_t const row, size_t const col) const{
vector<bool> numPresent (linesize_ + 1, false);
numPresent[0] = true;
vector<Coordinate> squareMembers = activeSquareMembers(row, col);
for(auto e : squareMembers){
numPresent[ gridElements_.at(e.first).at(e.second) ] = true;
}
return numPresent;
}
bool isSquareValid(size_t const row, size_t const col) const{
vector<bool> numPresent = numsInSquare(row, col);
// logical error probably here
return std::accumulate(numPresent.begin(), numPresent.end(), true, logical_and);
}
// the size of each row/column
size_t linesize_;
// the 2d array
GridElements gridElements_;
};
int main(int argc, char** argv) {
// 9x9 grid
Grid grid(16);
// pretty sure this is mathematically guaranteed to always return true, assuming the algorithm is implemented correctly ;)
grid.fill(0, 0);
std::cout << grid.to_s();
}