一个非常简单粗暴又理论上不会重复的随机数算法

        这几天写代码的时候,发现自己需要一个随机数生成器。平方取中,线性同余以及java自带的Random(这个好像用的也是线性同余?)看起来效果不够理想,而梅林旋转虽然质量高但是真的好复杂。

        我对于这个生成器期望大约是:

1.≥一亿的循环周期。

2.理论上任意种子都有落在取值范围内任意一个非无限小区间的可能。

3.知道初始种子以后可以快速推算使用这个种子进行第n次生成获得的数字。

4.低时间复杂度,中等以下代码量和理解难度。

        也许看起来好他妈苛刻?但是我脑筋一转还真就想到一个。

        那就是使用三角函数(这里以sin为例)。

        以一个long或者double作为种子-seed,以一个整数或者有限位浮点数为a。生成的第n个随机数为sin((n-1)*a+seed)。

        代码量低到一两行代码就能写完,原理简单到义务教育毕业大约就能看懂。简单运算就能得知使用某个种子进行第N次生成获得的数字为何。而且sin的周期是2π,一个整数或者有限位的小数加多少次也加不出π的整数倍,所以在忽略精度溢出的情况下,可以说应该永不重复,同时可能落在取值范围内任意一个非无限小区间。

        当然,由于sin函数的形状,这样子处理落在不同区间内的概率会不同。但思路想到利用π这里了,改进一下也不难,比如(((n-1)*a+seed)%π)/π就可以保证落在各个区间的概率基本相等(除的时候可以用BigDecimal,更精准)。

        最后再补充一下,如果觉得(((n-1)*a+seed)%π)/π这个方法输出数字变化频率较为明显(易于看出是每次加一个固定值后取模),将这个公式输出的随机值变换后作为自己的增量效果并不好,例如下图。

​
for(int i=0;i<10000;i++){
    valD=BigDecimal.valueOf(val).divideAndRemainder(pi)[1].divide(pi,6).doubleValue();
    val=(long)(val+1+4.2*valD);
    System.out.println(valD);
}

​

        我试过自己能想到的几种变换,都会导致随机数落点变得很不均匀。

        但是,可以进行两次运算,将第一次运算输出的随机数变换后作为第二个式子的增量。例如下图。

for(int i=0;i<10000;i++){
    valD=BigDecimal.valueOf(time+i).divideAndRemainder(pi)[1].divide(pi,6).doubleValue();
    val=(long)(val+1+10*valD);
    System.out.println(BigDecimal.valueOf(val).divideAndRemainder(pi)[1].divide(pi,6).doubleValue());
}

        这样子输出的随机数落点既然均匀,同时输出数字规律就不是那么明显了,没那么易于一眼看出是加某个固定值再取模。但是呢,不再可以知道种子和固定增量瞬间推算第N次输出的随机值。。

        不知道如此简单的方案之前有没有人想到过,但不论有没有,我当时想到的时候觉得自己像个小天才(厚着脸皮夸一下自己)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值