数独中的随机打乱函数

首先,对于数独的随机生成终盘,是肯定会用到随机函数的,我的基础随机数的取法,采用了基本的线性同余法,取一次种。

我在本文中想分析的是一个随机打乱函数。在写随机生成数独终盘的时候,有一个不可避免的问题:如何随机的生成一个1~9的排列,或者如何将n个数随机打乱形成新的序列(随机性越强越好,速度越快越好)。

1.基本方法

首先,最容易想到的方法莫过于直接取一个1~n的随机数。这时候有两种做法:①将第一个数放入该位置,②将该位置的数放入第一个格子。这两种方法其实没有本质上的区别(至于随机取一个数放入随机的位置也没有区别),所以在之后我都默认用第一种方法。但对于本问题来说这与平时的取随机数不同,在第一个数取完就会减少一个格子,所以在填入数的时候需要判断该格子是否已经有数,如果有则需要重新取随机数再重复上述过程。

2.大量的交换位置

对于随机打乱一个已知的序列,还有这样一种做法:每次取两个随机数,然后交换该位置的两个数。这样的方法乍一看还挺巧妙,但是仔细分析,这种方法需要大量的交换位置。而且需要足够的交换次数才能保证随机性。

3.每次减少能填数的位置

在第一种做法中,由于取到的随机数的位置可能有数,导致失败。所以为了保证每次都成功,由于每次减少了一个可取位置,所以取随机数的范围也缩小一个。在每次取完随机数后n后,寻找第n个不为空的格子将数顺序的填进去。

4.取一次随机数

第一种方法中,需要大量的取随机数,所以我在考虑通过减少取随机数的次数加快效率。最终我得到了一种只需要取一个随机数的方法,但是实际上又回到了上一种方法。不过我还是记录了这种方法,因为它还是挺有趣的。

将0~n-1的数按不同的顺序依次排列,一共有n!种不同的取法,所以,在0~n!-1内任取一个数就能确定一种唯一的0~n-1的序列。在经过研究后我得到了一种方法:设最初取得的数为X,则
第一位0的位置为:[X/(n-1)!](X=0~n-1)
然后让X=X-第一位*(n-1)!
第二位1的位置为:[X/(n-2)!](X=0~n-2)
上面的意思就是消除第一位的位权影响;将X看成一个n位数,第一位位权为(n-1)!,第二位为(n-2)!,……,倒数第二位为1!,最后一位为0!(因为最后一位没有选择了)。所以第一位有n!/(n-1)!=n种选择,第二位有(n-1)!/(n-2)!=n-1种选择,……,倒数第二位有2!/1!=2种,最后一位有1!/0!=1种。这样通过每一位有不同的位权,就能在n!中确定一个唯一的序列。
以4为例:4!=24
对于第一位:0~23
0~5——0
6~11——1
12~17——2
18~23——3
对于第二位:在减去第一位*位权之后,取值范围为0~5,下同理
0~1——0
2~3——1
4~5——2
对于第三位:0~1
0——0
1——1
对于最后一位:0
0——0(唯一的选择)
所以假如X=19,则可得到3010的序列,转化一下,则对于0123这四个数的排列:
第一位为3:得到XXX0
第二位为0:得到1XX0
第三位为1:得到1X20

第四位为0:得到1320

最终结果为1320。

所以这种方法还是如同第三种方法一样,虽然只取了一次随机数,但是内部过程与其完全相同,甚至转化的过程更费时间。但是,这确实是一个有效的由n!确定一种n个数唯一排列的方法。

5.取哈希

在经过一些思考之后,我得到了这种方法。这种方法还是类似第三种方法,每次取一个随机数。但是在取完随机数之后,便有了不同的做法。我先判断这个随机数的这一位是否有值,如果没有则填入,如果有则给该值加1模9,继续判断下一位是否有值。通过这样,就避免了多次取随机数,每个随机数都有价值,保证随机性,接下来的哈希过程则保证了一定能找到一个空位置。不过后来经过测试,发现这样的随机性有些差,经常出现一排数从小到大有序排列。这肯定是哈希的值为1的问题了。就在这时,我发现,9这个数有些特殊,哈希值非常多,124578都可以。所以我一一测试,发现7的效果比较好。但这时候又碰到问题了,如果所需打乱的数组的大小不是9呢?我当初这个函数设计出来是打算复用的。这时候我突然想到,我能不能把每个数适合的哈希值存进数组里面,然后一一调用呢?因为本题的规模最大只可能有9或者9以下的数组。然后又顺理成章的想到,我能不能每次都用不同的哈希值呢?我又不需要再次查找,我只需要保证每次都找到空格子。所以这时我的最终的版本就出炉了。
int HashList[10][7] = 

0,0,0,0,0,0,0,
0,0,0,0,0,0,0,
1,1,0,0,0,0,0,
2,1,2,0,0,0,0,
2,1,3,0,0,0,0,
4,1,2,3,4,0,0,
2,1,5,0,0,0,0,
6,1,2,3,4,5,6,
4,1,3,5,7,0,0,
6,1,2,4,5,7,8,
};

上面的数组每行的第一位存的都是该数字的有效哈希值个数,然后在每次找有效地址时随机取一个哈希值(当然,这个值在一次查找成功前不能变化,不能在里面继续取随机,我犯过一次这样的错误导致死循环)。

这种方法目测查找长度是低于第三种方法的,但是没有经过测试。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值