软件工程个人项目之数独工程(一)

软件工程个人项目之数独工程(一)

简介:

实现一个能够生成数独终局并能求解的控制台程序
github项目地址:
https://github.com/guangyezhao/MyFirst

审题:

当我拿到这个要求之后,我首先关注点放在了算法和语言上,第一个问题就是要我生成一个数独终局。
下面先用psp表格来给大家看一下我的时间分配:
在这里插入图片描述
那么我首次选择的算法是随机数加回溯
接下来说一下我的设计实现过程
对于这个函数,因为用到了命令行参数的相关知识,主函数就是我的主控模块和输入模块的结合体,然后实现的函数,就是一个生成函数,里面会调用一个检查函数,检查当前点可用值的一个小集合,然后是一个输出函数。对于解题函数,是一个检查函数(防止两个检查函数不兼容的情况,我打算写两个),一个输出函数。具体的我给大家看一下我在github上的第一个进展:
在这里插入图片描述
这就是基本的框架。
具体的关系见下图:
在这里插入图片描述
对上面的数据流图进一步的分解:
在这里插入图片描述
在这里插入图片描述
这个代码我觉得没有必要在这里添加,因为我生成一个数独终局都需要四十秒的时间,真的是一种非常糟糕的算法!如果你真的想利用随机数的话,对于每一个空的随机,尽量不要让他的范围是1到9,举个例子,比如我数独终局的第一行在进行生成过程中,到了最末尾的数字,其实他是已经固定了,你根本就不需要再通过随机数来生成,这样的成本太大了!因为九分之一的概率映射到实际生产过程中将是一个不可估计的次数,因此在后来的算法中,我避开了随机数这条路,因为真的非常耗时。
接下来,我的首个方法,丢弃随机数,怎么才能丢弃随机数呢,那我就从小到大依次遍历好了,那我绝对不允许一到九这样庞大的遍历,回溯这种暴力行为已经耗时很长了,因此我采用空间换时间的做法,我定义了一个数组叫temp[9]里面每个值对应的要么是0要么是1,目的就是用来保存当前位哪个数值可以用,哪个不能用。因为子函数都是保存在栈里了,所以相当于每个数独的空,都有一个临时的这样的数组,取名叫临时也是因为他的值在返回的时候就消失了。
对于这个数组的赋值我放到了一个函数里面:`int check(int row, int line, int *templ)//检查函数,查看这个点填上这个数字之后满足不满足要求
{
int blocklinenum, blockrownum;
blocklinenum = line / 3;//块行数
blockrownum = row / 3;//块列数

int startaddline = blocklinenum * 3;//块的起始行地址
int startaddrow = blockrownum * 3;//块的起始列地址
int i = 0; int j = 0;
while (1)
{
	if (j == 3)
	{
		j = 0;
		i++;
	}
	//if(zhongju[startaddline+i][startaddrow+j]==zhongju[line][row]&&line!= startaddline + i&& row!=startaddrow + j&&(startaddline+i < line || (startaddline +i== line && startaddrow +j< row)))
	if (((startaddline + i) * 9 + (startaddrow + j)) < (line * 9 + row))
	{
		templ[zhongju[startaddline + i][startaddrow + j] - 1] = 1;
		j++;
	}
	else
		break;
}
for (i = 0; i < line; i++)
{
	templ[zhongju[i][row] - 1] = 1;
}
for (j = 0; j < row; j++)
{
	templ[zhongju[line][j] - 1] = 1;
}
//int usespot = 1;//标识是否有可以用的值
j = 0;
for (i = 0; i < 9; i++)
{
	if (templ[i] == 0)
	{
		//zhongju[line][row] = 0;
		j++;
	}
}
return j;

}`
我觉得上面的代码大家都看的懂,因为所用的名字都是通俗易懂的,这个代码段是我写的最成功的,因为我的代码经过我的修改,经历了很多的版本,只有这个代码段在每次版本里面都没有经过修改!(在这里呢我要说一下啊:这个子函数进系统的栈后,要为这个函数保存很多东西,如果你在这个子函数定义了很多的很占内存的地方,如果你把他生成动态链接库时,调用会出错,这是我在做gui界面的时候总结出来的,所以我在界面的动态链接库里,抹掉了这个函数,因为他将引起系统的崩溃,使得对地址空间引用错误

function tianshu:

对于生成数独终局这个功能我最最重要的函数就是这个了,他是函数的核心代码,我先给大家看看我得2th版本的这个函数:

void tianshu(int row,int line,int sum)//,row line==next
{
	//ofstream file1("终局.txt");
	int templ[9] = {0};//模板,每次清0,查看是否十个数都已经遍历过
	int sym =check(row, line, templ);//可以用的数字的个数
		int i;
		while (sym--)
		{
			for (i = 0; i < 9; i++)
			{
				if (templ[i] == 0)
				{
        templ[i] = 1;
		zhongju[line][row] = i + 1;
		break;
				}
			}
			if (row==8)
		{
			if (line==8)
			{
				Print(zhongju);
					num++;
					if (num == sum)
						flag = 0;
					return;//如果最后一个填完了
			}
			else
			{
				//row = 0;
				//line++;
				tianshu(0, line+1,sum);
				i = 0;
			}
		}
		else
		{
			//row++;
			tianshu(row+1, line,sum);
			i = 0;
		}
			if (flag == 0)
				break;
		}
}

这个函数问题就是我没有输出,因为还没讲到输出,而且他不是很重要,这里我就没有把输出给大家看,后面我会着重讲一下输出的问题
对于上面的代码,我觉得大家也看到了,就是个回溯,没有别的技巧,所以我们这个项目的最难的问题生成1000000个数独终局来说,对我这个函数一点也不友好!在后面我发现其实我这个函数,最大的限度是100000,而且十万的数量对于我来讲,是可以接受的,是二十秒。
接下来代码进入了一个瓶颈,首次实现了,但是跑得慢,跑不出来1000000,怎么办?
请大家看下一章!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值