数独”(日语是すうどく,英文为Sudoku)
“数独”(sudoku)一词来自日语,意思是“单独的数字”或“只出现一次的数字”。概括来说,它就是一种填数字游戏。但这一概念最初并非来自日本,而是源自拉丁方块,它是十八世纪的瑞士数学家欧拉发明的。出生于1707年的欧拉被誉为有史以来最伟大的数学家之一。
欧拉从小就是一个数学天才,大学时他在神学院里攻读古希伯来文,但却连续13次获得巴黎科学院的科学竞赛的大奖。
1783年,欧拉发明了一个“拉丁方块”,他将其称为“一种新式魔方”,这就是数独游戏的雏形。不过,当时欧拉的发明并没有受到人们的重视。直到20世纪70年代,美国杂志才以“数字拼图”的名称将它重新推出。
1984年日本益智杂志Nikoli的员工金元信彦偶然看到了美国杂志上的这一游戏,认为可以用来吸引日本读者,于是将其加以改良,并增加了难度,还为它取了新名字称做“数独”,结果推出后一炮而红,让出版商狂赚了一把。至今为止,该出版社已经推出了21本关于数独的书籍,有一些上市后很快就出现了脱销。
数独后来的迅速走红,主要归功于一位名叫韦恩·古尔德的退休法官。古尔德现在居住在爱尔兰,1997年,无意中发现这个游戏,并编写了一个计算机程序来自动生成完整的数独方阵。2004年年底,伦敦《时报》在古尔德的建议下开辟了数独专栏,《每日电讯报》紧随其后,在2005年1月登出了数独。后来,世界各国数十家日报相继开辟专栏来介绍数独,有的甚至把它摆在头版大事炒作,招揽读者。专门介绍这种娱乐的杂志和一本又一本的书籍如雨后春笋般涌现,相关的比赛,网站和博客等等,也接二连三地冒出来。
此外,出版商还授权软件商开发了上百个数独游戏软件。供人们在网上购买。目前,日本共有5家数独月刊,总发行量为66万份。由于数独在日本已经被注册商标,其他竞争者只好使用其最初在美国的名字“数字拼图”。
数独游戏和传统的填字游戏类似,但因为只使用1到9的数字,能够跨越文字与文化疆域,所以被誉为是全球化时代的魔术方块。
数独游戏进入英国后,很多人立刻迷上了它。由于该游戏简单易学,而且初级游戏并不难,所以很多人在工作休息时间以及乘车上班途中都是埋头在报纸上狂玩数独。更有人宣称多玩数独游戏可以延缓大脑衰老。
目前,英国涌现出了大量的关于数独游戏的书籍,专门推广此类游戏的网站也纷纷出现,人们可以从网上下载数独软件到电脑,也可以把软件下载到手机上玩。
规则简单易掌握
数独的游戏规则很简单,9x9个格子里,已有若干数字,其它宫位留白,玩家需要自己按照逻辑推敲出剩下的空格里是什么数字,使得每一行与每一列都有1到9的数字,每个小九宫格里也有1到9的数字,并且一个数字在每个行列及每个小九宫格里都只能出现一次。
做这种游戏不需要填字谜那样的语言技巧和文化知识,甚至也不需要复杂的数学能力。因为它根本不需要加减乘除运算。当然,你也千万别小看它,并不是那么容易被“制服”的。当你握笔沉思的时候,这9个数字很可能让你头痛不已,脉搏加快,恼火不已。不过,当你成功填完所有数字的时候,你肯定会感到欣喜若狂。有数独迷宣称,做此类游戏,一名大学教授很可能不敌一名工厂工人。
看起来很像中国古代的九宫格。
数独通法〔可解决任何数独问题〕(仅供参考)
第一步:看横行(原则:这行已确定数大于等于四)
每一个空格写入可能的数字(根据横纵行已有的,但不看九宫)
第二步:看九宫
划去无机会的数字
第三步;重复1
第四步:重复2
此时,已基本每个空格都有数字了(一般数独已解),并且横纵行,九宫原则(明显原则)均已用尽
隐含原则1:{若一个单元(横行\纵行\九宫)某组内未确定格数,与其内部元素数相同,则这几个元素必在这几格内}例:
某一横行内所填确定数字如下:
(12)(6)(234)(7)(53)(9)(24)(8)(14)
在第1379格(4个)内含1234四个元素
所以,这四个数只能在其中,所以第五格内3去掉
第五步:重复12,利用隐含原则1
第六步:检验全局,利用1_5
此时仅仅余下几个格了(难的数独已解),还有第二隐含原则:
(12)(6)(234)(7)(53,8)(9,1)(24)(8,9)(14)
这一行很复杂,隐含原则一也很难奏效
但可见,数5在这一行仅有一次机会,所以,第五格只能是它!
第七步:重复12,利用隐含原则2
第八步:检验全局,利用1_7
所有数独已解,若解不出来,三种原因
1你解错了 2有一个条件没看见 3这个数独有问题
单向扫看法:在第一个例子中,我们注意看一下第2宫。
我们知道,每个宫内必须包含数字9,第1宫以及第3宫中都包含数字9,并且第1宫的9位于第3行。
第3宫的9位于第2行,这也就意味着第2宫的9不能在第2行和第3行,所有第2宫的9只能放置在第2宫第1行的空格内。
2双向扫看法:同样的技巧也可以扩展到相互垂直的行与列中。让我们想一下第3宫中1应该放在哪里。在这个例子中,第1行以及第2行已经有1了,那么第3宫中只有底部的俩个空格可以填1。不过,方格g4已经有1了,所有第g列不能再有1。
所以i3是该宫唯一符合条件填上数字1的地方。
3寻找候选法:通常地,一个方格只能有一个数字的可能性,因为剩下的其他8个数字都已经被相关的行列宫所排除了。我们看一下下面例子中b4这个方格。b4所在的宫中已经存在了数字3,4,7,8,1和6位于同一行,5和9位于同一列,排除上述所有数字,b4只能填上2。
4数字排除法:排除法是一个相对繁杂的寻找数字的方法。我们可以从c8中的1间接推出e7和e9必须包含数字1,不管这个1在哪个方格,我们可以确认的是,第e列的数字1肯定在第8宫内,所以第2宫内中间这一列就不可能存在数字1。因此,第2宫的数字一必须填在d2处。
function B=shudu(A)
%计算数独的程序。
%0表示待填的空格
%例子
%A=[2 0 0 0 9 0 0 0 7;
% 0 0 0 0 6 3 0 0 9;
% 0 0 9 1 0 5 0 0 0;
% 3 0 8 0 0 6 2 0 1;
% 0 0 1 0 0 0 3 0 0;
% 7 0 5 3 0 0 4 0 8;
% 0 0 0 6 0 9 5 0 0;
% 1 0 0 2 4 0 0 0 0;
% 6 0 0 0 3 0 0 0 4];
%shudu(A)
%ans =
% 2 3 6 8 9 4 1 5 7
% 5 1 4 7 6 3 8 2 9
% 8 7 9 1 2 5 6 4 3
% 3 4 8 9 5 6 2 7 1
% 9 2 1 4 8 7 3 6 5
% 7 6 5 3 1 2 4 9 8
% 4 8 3 6 7 9 5 1 2
% 1 5 7 2 4 8 9 3 6
% 6 9 2 5 3 1 7 8 4
[a,b]=find(A==0);%找0
if isempty(a)%如果没有0,就说明填满了,这就是答案。
B=A;
else%如果有0,就列出每个0的所有可能取值。
for i=1:length(a)
I{i}=[];
t=1:9;
for j=1:9
if A(a(i),j)~=0
t(A(a(i),j))=0;
end
if A(j,b(i))~=0
t(A(j,b(i)))=0;
end
end
for j=(ceil(a(i)/3)3-2):(ceil(a(i)/3)3)
for k=(ceil(b(i)/3)3-2):(ceil(b(i)/3)3)
if A(j,k)~=0
t(A(j,k))=0;
end
end
end
I{i}=find(t~=0);
if isempty(I{i})%如果没有可能项,说明矛盾。
B=[];return;
end
z(i)=length(I{i});
end
[p,q]=min(z);%从可能取值最少的地方开刀,这样快点
for j=1:p%将可能的值一个个代入,递归
C=A;C(a(q),b(q))=I{q}(j);
B=shudu(C);
if ~isempty(B);
return;
end
end
end
public class ShuDu {
/存储数字的数组/
static int[][] n = new int[9][9];
/生成随机数字的源数组,随机数字从该数组中产生/
static int[] num = {1,2,3,4,5,6,7,8,9};
public static void main(String[] args) {
//生成数字
for(int i = 0;i < 9;i++){
//尝试填充的数字次数
int time = 0;
//填充数字
for(int j = 0;j < 9;j++){
//产生数字
n[i][j] = generateNum(time);
//如果返回值为0,则代表卡住,退回处理
//退回处理的原则是:如果不是第一列,则先倒退到前一列,否则倒退到前一行的最后一列
if(n[i][j] == 0){
//不是第一列,则倒退一列
if(j > 0){
j-=2;
continue;
}else{//是第一列,则倒退到上一行的最后一列
i--;
j = 8;
continue;
}
}
//填充成功
if(isCorret(i,j)){
//初始化time,为下一次填充做准备
time = 0;
}else{ //继续填充
//次数增加1
time++;
//继续填充当前格
j--;
}
}
}
//输出结果
for(int i = 0;i < 9;i++){
for(int j = 0;j < 9;j++){
Systemoutprint(n[i][j] + " ");
}
Systemoutprintln();
}
}
/
是否满足行、列和3X3区域不重复的要求
@param row 行号
@param col 列号
@return true代表符合要求
/
public static boolean isCorret(int row,int col){
return (checkRow(row) & checkLine(col) & checkNine(row,col));
}
/
检查行是否符合要求
@param row 检查的行号
@return true代表符合要求
/
public static boolean checkRow(int row){
for(int j = 0;j < 8;j++){
if(n[row][j] == 0){
continue;
}
for(int k =j + 1;k< 9;k++){
if(n[row][j] == n[row][k]){
return false;
}
}
}
return true;
}
/
检查列是否符合要求
@param col 检查的列号
@return true代表符合要求
/
public static boolean checkLine(int col){
for(int j = 0;j < 8;j++){
if(n[j][col] == 0){
continue;
}
for(int k =j + 1;k< 9;k++){
if(n[j][col] == n[k][col]){
return false;
}
}
}
return true;
}
/
检查3X3区域是否符合要求
@param row 检查的行号
@param col 检查的列号
@return true代表符合要求
/
public static boolean checkNine(int row,int col){
//获得左上角的坐标
int j = row / 3 3;
int k = col /3 3;
//循环比较
for(int i = 0;i < 8;i++){
if(n[j + i/3][k + i % 3] == 0){
continue;
}
for(int m = i+ 1;m < 9;m++){
if(n[j + i/3][k + i % 3] == n[j + m/3][k + m % 3]){
return false;
}
}
}
return true;
}
/
产生1-9之间的随机数字
规则:生成的随机数字放置在数组8-time下标的位置,随着time的增加,已经尝试过的数字将不会在取到
说明:即第一次次是从所有数字中随机,第二次时从前八个数字中随机,依次类推,
这样既保证随机,也不会再重复取已经不符合要求的数字,提高程序的效率
这个规则是本算法的核心
@param time 填充的次数,0代表第一次填充
@return
/
public static int generateNum(int time){
//第一次尝试时,初始化随机数字源数组
if(time == 0){
for(int i = 0;i < 9;i++){
num[i] = i + 1;
}
}
//第10次填充,表明该位置已经卡住,则返回0,由主程序处理退回
if(time == 9){
return 0;
}
//不是第一次填充
//生成随机数字,该数字是数组的下标,取数组num中该下标对应的数字为随机数字
int ranNum = (int)(Mathrandom() (9 - time));
//把数字放置在数组倒数第time个位置,
int temp = num[8 - time];
num[8 - time] = num[ranNum];
num[ranNum] = temp;
//返回数字
return num[8 - time];
}
}
一、玩数独的方法有两个,就是直观法与直观法候选数法,具体介绍有:
1、直观法:不做任何记号,直接从数独的盘势观察线索,推论答案的方法。
2、候选数法:删减等位群格位已出现的数字,将剩余可填数字填入空格做为解题线索的参考,可填数字称为候选数(Candidates,或称备选数)。
3、直观法和候选数法只是填制时候是否有注记的区别,依照个人习惯而定,并非鉴定题目难度或技巧难度的标准,无论是难题或是简单题都可上述方法填制,一般程序解题以候选数法较多。
二、数独基本由三个连续宫组成大行列,分大行及大列组成。
第一大行:由第一宫、第二宫、第三宫组成。
第二大行:由第四宫、第五宫、第六宫组成。
第三大行:由第七宫、第八宫、第九宫组成。
第一大列:由第一宫、第四宫、第七宫组成。
第二大列:由第二宫、第五宫、第八宫组成。
第三大列:由第三宫、第六宫、第九宫组成。
三、数独基本解法:
1、摒除法:用数字去找单元内唯一可填空格,称为摒除法,数字可填唯一空格称为排除 (Hidden Single),根据不同的作用范围,摒余解可分为下述三种:
(1)数字可填唯一空格在「宫」单元称为宫排除(Hidden Single in Box),也称宫摒除法。
(2)数字可填唯一空格在「行」单元称为行排除法(Hidden Single in Row),也称行摒除法。
(3)数字可填唯一空格在「列」单元称为列排除法(Hidden Single in Column),也称列摒除法。
2、唯一余数法:用格位去找唯一可填数字,称为余数法,格位唯一可填数字称为唯余解。
二、其规律就是通过基础解法出数只需一种解法,摒除法或唯余法,超出此范围而需要施加进阶解法时,解题点需要进阶解法协助基础解法来满足隐性唯一或显性唯一才能出数,该解题点的解法需要多个步骤协力完成,因此称做组合解法。
三、另外在2006年Gary McGuire撰写了程式,试图通过暴力法来证明16提示数的数独是否存在,方法很简单,既然Bertram Felgenhauer和Frazer Jarvis已经计算出不等价的终盘总数为5,472,730,538个,那么将每个终盘是16提示的情况都跑一遍,如果没有找到16提示的数独,那么就可以证明最少提示数为17个。
扩展资料:
1、影响数独难度的因素很多,就题目本身而言,包括最高难度的技巧、各种技巧所用次数、是否有隐藏及隐藏的深度及广度的技巧组合、当前盘面可逻辑推导出的出数个数等等。
2、对于玩家而言,了解的技巧数量、熟练程度、观察力自然也影响对一道题的难度判断。市面上数独刊物良莠不齐,在书籍、报纸、杂志中所列的难度或者大众解题时间纯属参考,常有难度错置的情况出现。
3、一般意义上,按照最为基础的数独规则,一般称为标准数独(Standard Sudoku)。而产生的解题思路和技巧,也称为标准数独技巧。
参考资料:
“数独sudoku”来自日文,但概念源自“拉丁方块”,是十八世纪瑞士数学家欧拉发明的。游戏规则很简单:
在九个九宫格里,填入1到9的数字,让每个数字在每个行、列及九宫格里都只出现一次。谜题中会预先填入若干数字,其它宫位则留白,玩家得依谜题中的数字分布状况,逻辑推敲出剩下的空格里是什么数字。
这种风靡日本及欧美的“数独sudoku”,据说原创者是18世纪的瑞士人,但没有得到应有的注目,直到20多年前,美国人重新挖掘它的魅力,接着日本杂志出版商在八○年代末期在一本美国杂志上看到这个游戏,带回日本后,增加它的游戏难度,并命名为“数独sudoku”,“数独”谜戏于是诞生,并逐渐受到日本人的注意、沉迷,日本坊间书局还出版了许多“数独”的书。纽西兰裔英籍退休法官韦恩.古德(Wayne
Gould)一九九七年旅游日本时,买了一本数独游戏书,从此就迷上了,进而研究出计算机程序,从2004年开始供稿给全球十几家报社,立即受到读者的热烈回响,引发了一场“数独”热。
开始的话:这个程序现在还不稳定,有时出现运行时错误,跟踪是由于vector的size()方法引起的。调试发现中间的min_seq并没有完全按照作者的意图变化。
运行时,如果出现错误,就反复运行,运行成功即可出现一个正确的99数独矩阵。
如果要玩预先填充一些数的游戏,只需修改初始矩阵即可。
算法:为每个位置定义一个可选元素集合,每个更新是把它所在的行,列,所在的3×3方阵中已出现的元素从集合中去掉。填充时,从最小候选集合中选一个(可随即)填进去,更新候选集合,再填充,直到所有位置填充完毕,游戏结束。
/9×9数独游戏的计算机程序/
/作者:xiaocui/
/时间:2006623/
/版本:v10/
/算法思想/
/对每个位置的元素,考虑其可选取的数字
的集合,每次把候选元素个数最小的那个位置填充
从该最小候选集合中随机选取一个元素填充,重复
这个过程,直到所有元素填充完毕/
/适用填充全空的数独方格 和 填充已有一些数的数独方格/
/对初始化的候选集的第一次更新正是为了解决第2类数独游戏/
/对于已填充一部分元素的,直接修改MATRIX矩阵即可/
/数独游戏的结果不止一种/
#include <iostream>
#include <ctime>
#include <vector>
using namespace std;
/初始9×9的矩阵/
/元素为0,说明该位置还未填充/
int MATRIX[9][9]={ {0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0} };
/初始给出的元素个数/
int INITIAL_COUNT;
/已填充元素个数,作为填充结束标志/
int FINISH_COUNT=0;
/各个元素的初始候选集合/
vector<vector<int> > IVEC(81);
/函数原型/
/得到初始给出的元素个数/
int get_initialcount();
/初始化候选集合/
void initial_candidate();
/从vector中删除指定元素/
void delete_value(vector<int> &ivec,int value);
/更新候选集合/
void refresh_candidate();
/返回9×9候选集合元素最少的候选集合序号/
int min_seq();
/随机生成一个位置序号并取得该序号所对应的元素值/
int choose_seq(int min_seq);
/填充该元素并判断是否填充完毕/
int is_finish(int min_seq, int choose_value);
int main()
{
/得到初始给出的元素个数/
INITIAL_COUNT=get_initialcount();
/初始化候选集合/
initial_candidate();
/先更新候选集合(为了应付已经填充一部分数的情况)/
refresh_candidate();
int i;
int MinSeq;
int ChooseValue;
MinSeq=min_seq();
ChooseValue=choose_seq(MinSeq);
while(is_finish(MinSeq,ChooseValue)!=1)
{
refresh_candidate();
MinSeq=min_seq();
ChooseValue=choose_seq(MinSeq);
}
/输出填好的数独游戏结果/
for( i=0;i<9;++i)
{
for(int j=0;j<9;++j)
{
cout<<MATRIX[i][j]<<'\t';
}
cout<<endl;
}
return 0;
}
/函数定义/
/得到初始给出的元素个数/
int get_initialcount()
{
int count=0;
for(int i=0;i<9;++i)
{
for(int j=0;j<9;++j)
{
if(MATRIX[i][j]!=0)
{
count++;
}
}
}
return count;
}
/初始化候选集合/
void initial_candidate()
{
for(int i=0;i<81;++i)
{
for(int j=1;j<10;++j)
{
IVEC[i]push_back(j);
}
}
}
/从vector中删除指定元素/
void delete_value(vector<int> &ivec,int value)
{
/如果ivec已经为空,直接退出/
if (ivecsize()==0)
{
return;
}
vector<int>::iterator iter=ivecbegin();
while( iter<ivecend() && (iter)!=value )
{
iter++;
}
if(iter<ivecend())//在vector中找到已填充的元素,把它删除
{
ivecerase(iter);
}
}
/更新候选集合/
void refresh_candidate()
{
int i;
int rownum,colnum;
int row,col;
/更新81个vector/
for(i=0;i<81;++i)
{
row=i/9;
col=i%9;
if(MATRIX[row][col]!=0)//该位置已经填充
{
if(IVEC[i]size()!=0)//该vector不空
{
/删除整个候选集/
IVEC[i]erase(IVEC[i]begin(),IVEC[i]end());
}
}
else
{
/删除同一行中的元素/
for(colnum=0;colnum<9;++colnum)
{
delete_value(IVEC[i],MATRIX[row][colnum]);
}
/删除同一列中的元素/
for(rownum=0;rownum<9;++rownum)
{
delete_value(IVEC[i],MATRIX[rownum][col]);
}
/删除在一个3×3方阵中的元素/
/在第1块中,删除3×3方阵元素/
if(row/3==0 && col/3==0)
{
for(int r=0;r<3;++r)
{
for(int c=0;c<3;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第2块中,删除3×3方阵元素/
if(row/3==0 && col/3==1)
{
for(int r=0;r<3;++r)
{
for(int c=3;c<6;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第3块中,删除3×3方阵元素/
if(row/3==0 && col/3==2)
{
for(int r=0;r<3;++r)
{
for(int c=6;c<9;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第4块中,删除3×3方阵元素/
if(row/3==1 && col/3==0)
{
for(int r=3;r<6;++r)
{
for(int c=0;c<3;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第5块中,删除3×3方阵元素/
if(row/3==1 && col/3==1)
{
for(int r=3;r<6;++r)
{
for(int c=3;c<6;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第6块中,删除3×3方阵元素/
if(row/3==1 && col/3==2)
{
for(int r=3;r<6;++r)
{
for(int c=6;c<9;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第7块中,删除3×3方阵元素/
if(row/3==2 && col/3==0)
{
for(int r=6;r<9;++r)
{
for(int c=0;c<3;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第8块中,删除3×3方阵元素/
if(row/3==2 && col/3==1)
{
for(int r=6;r<9;++r)
{
for(int c=3;c<6;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
/在第9块中,删除3×3方阵元素/
if(row/3==2 && col/3==2)
{
for(int r=6;r<9;++r)
{
for(int c=6;c<9;++c)
{
delete_value(IVEC[i],MATRIX[r][c]);
}
}
}
}
}
}
/返回9×9候选集合元素最少的候选集合序号/
int min_seq()
{
int count[81];
int i;
for(i=0;i<81;++i)
{
count[i]=IVEC[i]size();
}
int value=10;
int min_seq;
for(i=0;i<81;++i)
{
if(count[i]==0)
{
continue;
}
if(count[i]<value)
{
value=count[i];
min_seq=i;
}
}
return min_seq;
}
/随机生成一个位置序号并取得该序号所对应的元素值/
int choose_seq(int min_seq)
{
/根据当前时间设置种子/
srand((unsigned)time( NULL ));
int random_seq=rand()%(IVEC[min_seq]size());
return IVEC[min_seq][random_seq];
}
/填充该元素并判断是否填充完毕/
int is_finish(int min_seq, int choose_value)
{
int row, column;
row=min_seq/9;
column=min_seq%9;
MATRIX[row][column]=choose_value;
FINISH_COUNT++; /已填充元素个数加1/
/填充完毕判断/
if(FINISH_COUNT==81-INITIAL_COUNT)
{
return 1;
}
else
{
return 0;
}
}
>
以上就是关于关于数独更详细的介绍全部的内容,包括:关于数独更详细的介绍、数独方法、求用matlab编写的数独游戏的界面,9*9,简单的等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)