Консультация № 176388
30.01.2010, 17:31
0.00 руб.
0 10 1
Здравствуйте, эксперты.

Я написал простенькую программу, которая генерирует судоку. Однако в некоторых (прибл. 1 из 10) случаях программа выполняется "зависает" - вовлечена в бесконечный цикл.

Прошу проверить код, и исправить программу.

Код:
#include <iostream>
#include <cstdlib>
using namespace std;

const int SIZE = 9;
int sudoku[SIZE][SIZE];
int test_array[SIZE*SIZE][SIZE] = {0};

bool checkrow(int, int);
bool checkcol(int, int);
bool checksqr(int, int);
bool test(int, int);
void moveback(int& i, int& j);
void write(int i, int j);
void print();

int main()
{
srand(rand() ^ time(0));

int i, j;
for(i=0; i<9; i++)
{
for(j=0; j<9; j++)
{
for(;;)
{
if(!test(i, j))
moveback(i, j);

sudoku[i][j] = rand()%9+1;
write(i, j);

if(checksqr(i, j) && checkrow(i, j) && checkcol(i, j) )
break;
}
}
}

print();

return 0;

}

bool checkrow(int x, int y)
{
for(int i=0; i<y; i++)
if(sudoku[x][i] == sudoku[x][y])
return false;

return true;
}

bool checkcol(int x, int y)
{
for(int i=0; i<x; i++)
if(sudoku[i][y] == sudoku[x][y])
return false;

return true;
}

bool checksqr(int x, int y)
{
int i_start = x/3; i_start *= 3;
int j_start = y/3; j_start *= 3;

for(int i=i_start; i < i_start+3; i++)
for(int j=j_start; j < j_start+3; j++)
{
if(i==x && j==y)
return true;

if(sudoku[i][j] == sudoku[x][y])
return false;
}
return true;
}

bool test(int i, int j)
{
int current = i*9 +j+1;

for(int x=1; x<9; x++)
if( test_array[current][x] == 0)
return true;

return false;
}

void moveback(int& i, int& j)
{
int current = i*9 +j+1;

for(int x=0; x<9; x++)
test_array[current][x] = 0;

if(j < 1)
{
i--; j=8;
}
else
j--;
}

void write(int i, int j)
{
int current = i*9 + j+1;
int value = sudoku[i][j];
test_array[current][value] = 1;
}

void print()
{
int i, j;

for(i=0; i<9; i++)
{
if( i%3 == 0 )
cout << "=========================\n";

cout << "| ";

for(j=0; j<9; j++)
{
cout << sudoku[i][j] << " ";

if((j+1)%3 == 0)
cout << "| ";
}

cout << endl;
}
cout << "=========================\n";
}

Обсуждение

Неизвестный
30.01.2010, 19:56
общий
Иванов Андрей Владимирович:
Как то раз, решил я доработать один свой старый алгоритм. А доработать решил, т.к. на момент написания не имел особого опыта в программировании. Так вот, на то чтобы разобраться как же это все работает, у меня ушло более 4х часов чистого времени. А все по тому, что на момент написания программы, я не знал о необходимости написания комментариев к ней. Уже после этого, наш преподаватель по физике сказал очень умную вешь: Сможешь ли ты разобраться как ты решал эту задачу через 10 лет?
А вы хотите, чтобы другой человек(который не писал этот код) разбирался вслепую в вашем коде. При этом, вы задаете бесплатный вопрос на не распространенную тему. Я вот, например, не знаю что такое судоку и как его генерировать. Поэтому к времени, которое я(или кто-то другой) затратит на расшифровку вашего кода, надо добавлять еще и время на изучние темы...
Почему не указали в каком компиляторе компилируется программа? У меня например она не компилируется. Значит добавляем время на приведение программы в рабочее состояние для данного компилятора...
Код:

for(;;)
{
...
...
...
if(checksqr(i, j) && checkrow(i, j) && checkcol(i, j) )
break;
}

Во-первых, чем вам не угодил стандартный "do while"? Зачем эти изощрения? Вы сами загоняете программу в бесконечный цикл, так чего же вы хотите?
Разберайтесь теперь, почему у вас возникает такая ситуация, в которой ваши функции не возвращяют утвердительный результат, во всех трех случаях.
Неизвестный
30.01.2010, 20:54
общий
По-моему, все ГОРАЗДО проще! Совсем недавно был точно такой вопрос. в этой же рассылке Уж не Вы ли его задавали? Ошибка была элементарная
Во всяком случае посмотрите в архиве
Неизвестный
30.01.2010, 20:56
общий
Иванов Андрей Владимирович:
Программа работает корректно. Она не зависает, а ищет решение. Сами же написали
for(;;)
{
if(!test(i, j))
moveback(i, j);
}
Надо просто подождать. По крайней мере, я прогнала раз 50, программа зависла пару раз, но через несколько секунд выдавала ответ..
Неизвестный
31.01.2010, 00:55
общий
>> Patriotix-N

Вы не должны быть экспертом данной рассылки!
Т. к. вы поленились даже прочитать условия данного раздела (С++) а именно: по умолчанию администратор рассылки будет считать, что Вы работаете с компилятором g++ из коллекции GCC. Но да ладно...

Далее:
Вопрос простой, и код программы - тоже.
Если человек знающий, и использует правильно отладчик, то быстро найдет решение.
С вами я полностью не согласен!

>> Лейла
Вы не правы, есть ошибка. Если интересно, посмотрите внимательно на функцию void moveback(int& i, int& j)

Неизвестный
31.01.2010, 10:16
общий
Иванов Андрей Владимирович:
Извиняюсь, я действительно не читал условий рассылки. Не подскажете где они написаны?

Почему же вы не согласны с лейлой?
У меня программа тоже ни разу не зависла... Правда пару раз ее работа длилась около минуты.

P.S. А вот комментарии следовало бы делать...
Неизвестный
01.02.2010, 16:00
общий
Иванов Андрей Владимирович:
Вы же уже задавали вопрос по судоку (даже два, последний - 175854). Программа, которую я Вам написал, работает стабильно и без длительных пауз. Впрочем, желание разобраться и написать собственный вариант весьма похвально.
Неизвестный
01.02.2010, 19:58
общий
>> Patriotix-N

Знаю только, где найти условия для тех, кто задает вопрос:

Нажмите на "задать вопрос", выберите раздел
Компьютеры -> Программирование -> C++ -> "продолжить".
А потом - назад (браузером), вопрос не будет задан...
давно
Старший Модератор
31795
6196
02.02.2010, 16:24
общий
Patriotix-N:
Цитата: Отправка вопроса экспертам - шаг 5
Информация от Администратора рассылки:
Уважаемый пользователь
Задавая вопрос в рассылку по C / C++, для быстрого получения ответа соблюдайте ряд несложных для исполнения требований:
1. Несмотря на то, что C и C++ являются кроссплатформенными языками программирования, в ряде случаев (например, при необходимости создать приложение с графическим интерфейсом пользователя либо с использованием системных API) эксперту может понадобиться знание целевой операционной системы. Поэтому в тех случаях, когда для Вас это имеет значение, указывайте её в тексте вопроса. В случае если Вы этого не сделали и не ответили на уточняющие вопросы в мини-форуме, по умолчанию администратор рассылки будет считать, что Вы работаете с одним из дистрибутивов Linux в графической среде KDE.
2. Если для Вас требуется создать приложение с использованием какого-то определённого набора инструментов, то указывайте его в вопросе (компилятор, интегрированная среда разработки, библиотека классов для построения графического интерфейса пользователя и т.п.). При этом не используйте русскую транслитерацию названия среды разработки / компилятора / библиотеки. Указывайте название программного продукта, с которым работаете, полностью с приведением номера его версии. Microsoft Visual C++ 6 и Microsoft Visual C++ 2008 Express Edition, Borland C++ 5.02 и Borland C++ Builder 6.0, Turbo C++ 3.1 и Turbo C++ 2006 Explorer - совсем не одно и то же. В случае если Вы этого не сделали и не ответили на уточняющие вопросы в мини-форуме, по умолчанию администратор рассылки будет считать, что Вы работаете с компилятором g++ из коллекции GCC 4.x в командной строке без использования IDE.
3. Повторю, что в случае, если имеется необходимость создать графическое (оконное) приложение, указывайте, с помощью каких инструментов оно должно быть создано: GTK, Qt, API Вашей операционной системы, MFC, VCL, Windows Forms и т.д. и т.п. В случае если Вы этого не сделали и не ответили на уточняющие вопросы в мини-форуме, по умолчанию администратор рассылки будет считать, что для создания графического интерфейса пользователя Вы используете библиотеку Qt версии 4.x.
4. Не пытайтесь связываться с экспертами и администратором по внутреннему пейджеру портала: этим Вы лишь замедляете решение Вашей задачи, т.к. все уточнения, связанные с Вашей задачей, тем самым доступны лишь Вашему корреспонденту, но не другим экспертам. Для переписки по любым проблемам, связанным с Вашим вопросом, используйте его мини-форум. Администратор рассылки все личные сообщения, адресованные ему по поводу заданных вопросов, переносит в мини-форум и отвечает только там (если сочтёт нужным).
5. Не пытайтесь задавать всё новые и новые вопросы (имеются в виду просьбы решить новые задачи, не имеющие отношения к прежней) в мини-форуме вопроса, на который уже было дано решение. Также не задавайте несколько не связанных между собой вопросов в одной форме: администратор рассылки оставляет за собой право удалять лишние вопросы на своё усмотрение.
6. Администратор рассылки оставляет за собой право удалять вопросы, связанные с программированием на C / C++ лишь косвенно, если на них не поступило ответов в течение 5 дней.
Удачи!
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
02.02.2010, 18:44
общий
Зенченко Константин Николаевич:
Большое спасибо!
давно
Старший Модератор
17042
808
04.02.2010, 07:01
общий
это ответ
Здравствуйте, Иванов Андрей Владимирович.
Ответ на основе комментария в мини-форуме эксперта amnick.

Программа для генерации судоку в приложении.

Приложение:
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;

const int size = 9;
const int initial_mask = ~((-1) << size);

// В прямоугольнике шириной RW и высотой RH также не должно быть одинаковых точек
// Должно выполняться условие (RH*RW == size)
const int RH = 3, RW = size/RH;

int sudoku[size][size]; // это матрица судоку

// этот массив будет использоваться для пометки проверенных чисел
// лишняя колонка позволяет избежать дополнительной проверки индекса
int check[size][size+1];


// генерируем ячейку
bool cell( int i, int j )
{
int chk = check[i][j]; // маска чисел, уже проверенных в этой ячейке
if( !chk )
return false; // на предыдущих проходах уже были проверены все числа

for(;;)
{
// генерируем число, новое для этой ячейки
int number, bit;
do {
number = rand() % size;
bit = 1 << number;
} while( !(chk & bit) );
chk &= ~bit;
++number;

// проверяем, было ли это число ранее ...
bool repeated = false;
// ... в столбце
for(int l=0; l<i; l++)
if( sudoku[l][j] == number ) {
repeated = true;
break;
}
if( repeated ) {
// number уже есть в этом стобце
if( chk == 0 ) return false; // проверены все числа
continue; // пытаемся снова
}

// ... в строке
for(int k=0; k<j; k++)
if( sudoku[i][k] == number ) {
repeated = true;
break;
}

if( repeated ) {
// number уже есть в этой строке
__repeated:
if( chk == 0 ) return false; // проверены все числа
continue; // пытаемся снова
}

// ... в прямоугольнике RH x RW
int i_mod = i % RH; // номер текущей строки в под-области
if( i_mod ) { // для всех строк, кроме первых в под-областях
int j_from = j - j % RW;

// на несовпадение в текущей строке уже проверили,
// проверяем только вышележащие строки в под-области
for( l = i-i_mod; l < i; ++l )
for( k = 0; k < RW; ++k )
if( sudoku[l][j_from+k] == number )
goto __repeated; // некрасиво, но это простейший способ выйти из вложенных циклов
}

sudoku[i][j] = number; // нашли подходящее (пока) число
check[i][j] = chk; // сохраняем маску проверенных чисел

cout << number << " ";
return true;
}

}

// генерируем строку
bool row( int i )
{
check[i][0] = initial_mask; // сброс маски для первой ячейки
int j = 0;
while( j < size ) {
if( cell( i, j )) {
// ячейка заполнена, переходим к следующей
++j;
check[i][j] = initial_mask; // сброс маски для очередной ячейки
// индекс j здесь не проверяли - помните объявление int check[size][size+1] ?
}
else {
// увы, не получилось
// возвращаемся к предыдущей ячейке и ищем для нее какое-то другое число
// будет использована ранее сохраненная маска check[][]
if( --j < 0 )
return false; // придется вернуться к предыдущей строке

// вывод отладочной информации - отслеживание возвратов
cout << endl << i << ": ";
for( int k = 0; k < j; ++k )
cout << sudoku[i][k] << " ";
}
}
cout << endl;
return true;
}

void main()
{
time_t kl;
time( &kl );
srand(kl);

int i = 0;
while( i < size ) {
cout << i << ": ";
if( row( i ))
++i;
else
if( --i < 0 ) { cout << endl << "error! << endl"; return; }
}
cout << "\nMatrix:\n";

// формируем разделитель
char szDivider[32];
{
short* p = (short*)szDivider;
for( int j = 0; j < size; ++j ) {
if( j && j % RW == 0 )
*p++ = *(short*)"+-";
*p++ = *(short*)"--";
}
*(char*)p = 0;
}

for( i = 0; i < size; ++i ) {
if( i && i % RH == 0 )
cout << szDivider << endl;
for( int j = 0; j < size; ++j ) {
if( j && j % RW == 0 )
cout << "| ";
cout << sudoku[i][j] << " ";
}
cout << endl;
}
}
Об авторе:
We have but faith: we cannot know;
For knowledge is of things we see;
And yet we trust it comes from thee,
A beam in darkness: let it grow.
-----
https://www.linkedin.com/in/andreynkuznetsov
https://www.researchgate.net/profile/Andrey_Kuznetsov11
http://www.researcherid.com/rid/K-8824-2014
Форма ответа