【个人项目】解题思路 - 生成数独终局 (1120161918)

生成数独终局

数学原理分析

       数独是一种逻辑游戏。它要求满足每一行、每一列、每一个九宫格(3×3)内的数字均含1-9,不重复。


       生成数独终局的方案有很多,比如随机数先分配第一行位置然后利用回溯法解数独,在这个项目要求不重复,利用随机数的方案是危险的。我们通过观察数独的特性,用基本组有规律的构造更多的数独终局是一个有效方案。


       上图是一种数独终局,我们根据数独要求,1、2、3行任意互换,仍然满足各行、各列、各九宫格1~9出现1次仅1次。对4、5、6行同理,对7、8、9行也同理。对列也如此。那么实际上由一个数独终局可以生成46656种数独终局,但由于题目中要求第一个元素是(学号后两位相加)%9+1(我的学号是1120161918,计算结果为1),因此由一个数独终局,只能生成5184种数独终局,理论上提供193个不同构的数独终局便能够达到题目要求。

       利用高中数学竞赛的数论基础,进一步发现对于任意一个第一行,第二行的元素是第一行元素下标+6,第三行+3,第四行+8,第五行+5,第六行+2,第七行+7,第八行+4,第九行+1,得到的9×9局面满足数独要求。实际上此处原理是数论同余的原理,每三行的位移量属于mod 3 的同余类 (mod 3 的最小非负完全剩余系就是0,1,2)。

       用这个原理可以解释为什么1、2、3互换得到的结果任然是数独终局。实际上只需要三行位移量在同一mod 3 同余类的不同值就可以了。这样我们还能得到更多的数独终局。

       只需要构造第一行:第一个数字是学号要求(我的为:1),后面8个数字做全排列,然后利用剩余类构造剩下8行。由于第一行不动(以满足学号要求),可以由一个得到 2×6×6=72种,而8!=40320,一共得到2903040种数独终局,已经超过1e6了。


程序实现

      如果在生成的时候利用循环实现第一行全排列赋值是相当费时间的,当然直接打表40320种会使程序变得相当长,可以主函数一开始就计算全排列存起来,方便后续多次调用,这样的效率是比较高的。

      递归求取全排列实现如下:

int init[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
/// <summary> 递归求取全排列 </summary>
void permutation(int offset)
{
    if(offset == 8) {
		for(int i=0;i<9;i++) {
			origin[count][i]=init[i];
		}
		count++;
		return;
    }
	for(int i=offset; i<9; i++) {
		swap(init[i], init[offset]);
		permutation(offset + 1);
		swap(init[i], init[offset]);
    }
}


       保存好全排列后可以先将输出顺序用数组保存,这样实现便很简单,大量减少循环结构,对软件测试是很有用的。

int shift[9] = { 0, 6, 3, 8, 5, 2, 7, 4, 1 };

int generate_1[2][3] = { {0,1,2}, {0,2,1} };
int generate_2[6][3] = { {3,4,5}, {3,5,4}, {4,3,5}, {4,5,3}, {5,3,4}, {5,4,3} };
int generate_3[6][3] = { {6,7,8}, {6,8,7}, {7,6,8}, {7,8,6}, {8,6,7}, {8,7,6} };
       完整的实现代码后续将会展示。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值