acfun-helper icon indicating copy to clipboard operation
acfun-helper copied to clipboard

抽奖功能修改建议

Open wpscott opened this issue 4 years ago • 8 comments

  1. 改用new Date() % Array.lengthwindow.crypto.getRandomValues获取随机数
  2. 增加继续抽奖功能来排除已中奖用户

wpscott avatar Aug 09 '20 03:08 wpscott

  1. 改用new Date() % Array.lengthwindow.crypto.getRandomValues获取随机数
  2. 增加继续抽奖功能来排除已中奖用户

收到

Sokwva avatar Aug 09 '20 03:08 Sokwva

随机数用Math.floor会有问题,我觉得用string来截取比较好 parseInt((Math.random() * (max - min) + min).toString().split('.')[0])

当然最好是random.org的api更好了

NocKit avatar Aug 09 '20 04:08 NocKit

随机数用Math.floor会有问题,我觉得用string来截取比较好 parseInt((Math.random() * (max - min) + min).toString().split('.')[0])

当然最好是random.org的api更好了

问题2我已经提交了pr(>д<)

Sokwva avatar Aug 09 '20 05:08 Sokwva

昨天的抽奖中:

55:41 抽中 damands#353

58:21 抽中 来自非洲的猴子#245、Graven`L#43

01:21:23 抽中 lics#509 、嘚忒#59 、来自非洲的猴子#245 、深巷少女和狗#296 、C137兵库北#646 、AC娘的表弟#345

01:24:22 抽中 这个扳手#291、司马相柳#327、小爷就爱笑#578、糟糕姬#188

01:26:04 抽中 lics#509、本娘天生36D#46

01:27:03 抽中 司马相柳#327

01:27:18 抽中 一根会思考的芦苇#539

01:28:11 抽中 damands#353

01:28:25 抽中 C137兵库北#646

353,245,509,327,646楼,都出现了连续抽中的情况,也引发了弹幕对抽奖机随机性的质疑,随机数生成方式应当需要修改。

SingSky avatar Aug 09 '20 05:08 SingSky

先写入计划了,群主和我最近比较忙,有时间了慢慢迁移吧

Sokwva avatar Aug 12 '20 15:08 Sokwva

最近有空摸鱼研究了一下建议一

在线随机数生成器

参考AVUP用过的在线随机数生成器使用的random.js,采用了MersenneTwister伪随机数算法,随机数生成算法大致为

var mTwister = new MersenneTwister();
var number = Math.round( mTwister.random() * max) - min ) ) + min;

Math.random()

根据V8的博客There’s Math.random(), and then there’s Math.random()Math.random()使用的是xorshift128+,一个比MersenneTwister效率高的伪随机数算法。

比较

从代码上看助手的Math.floor(Math.random() * (max - min)) + minMath.round( mTwister.random() * max) - min ) ) + min的 主要差别在于使用Math.floor()还是Math.round()。 因为在抽奖时min通常为0,代码可以简化为Math.floor(Math.random() * max)Math.round(mTwister.random() * max)。 从概率论来看,Math.floor()Math.round()要好,因为Math.round(mTwister.random() * max)的取值范围为[0, max],可能导致数组越界。

测试代码

const array = Array(114);
array.fill(0);
for(let i = 0; i < 114514; i++) {
    array[Math.round(Math.random() * array.length)]++;
}

可以看到array的长度改变了,而且array[0]的次数只有其他的一半,因为另外一半四舍五入成1了。

验证

rnd generator

可以看到1出现的次数明显多于0和2。

结论

综上,助手抽奖使用的随机数算法可以认为是没有问题的,只需要增加一个排除已中奖用户的功能即可。

关于Math.floor()

如果担心Math.floor(Math.random() * max)的取值问题,那么可以转变思路,通过打乱待抽奖评论的顺序,然后从头或尾开始一个个移除来保证随机性和公平性。

wpscott avatar Sep 03 '20 00:09 wpscott

最近有空摸鱼研究了一下建议一

在线随机数生成器

参考AVUP用过的在线随机数生成器使用的random.js,采用了MersenneTwister伪随机数算法,随机数生成算法大致为

var mTwister = new MersenneTwister();
var number = Math.round( mTwister.random() * max) - min ) ) + min;

Math.random()

根据V8的博客There’s Math.random(), and then there’s Math.random()Math.random()使用的是xorshift128+,一个比MersenneTwister效率高的伪随机数算法。

比较

从代码上看助手的Math.floor(Math.random() * (max - min)) + minMath.round( mTwister.random() * max) - min ) ) + min的 主要差别在于使用Math.floor()还是Math.round()。 因为在抽奖时min通常为0,代码可以简化为Math.floor(Math.random() * max)Math.round(mTwister.random() * max)。 从概率论来看,Math.floor()Math.round()要好,因为Math.round(mTwister.random() * max)的取值范围为[0, max],可能导致数组越界。

测试代码

const array = Array(114);
array.fill(0);
for(let i = 0; i < 114514; i++) {
    array[Math.round(Math.random() * array.length)]++;
}

可以看到array的长度改变了,而且array[0]的次数只有其他的一半,因为另外一半四舍五入成1了。

验证

rnd generator

可以看到1出现的次数明显多于0和2。

结论

综上,助手抽奖使用的随机数算法可以认为是没有问题的,只需要增加一个排除已中奖用户的功能即可。

关于Math.floor()

如果担心Math.floor(Math.random() * max)的取值问题,那么可以转变思路,通过打乱待抽奖评论的顺序,然后从头或尾开始一个个移除来保证随机性和公平性。

@niuchaobo

Sokwva avatar Sep 03 '20 01:09 Sokwva

最近有空摸鱼研究了一下建议一

在线随机数生成器

参考AVUP用过的在线随机数生成器使用的random.js,采用了MersenneTwister伪随机数算法,随机数生成算法大致为

var mTwister = new MersenneTwister();
var number = Math.round( mTwister.random() * max) - min ) ) + min;

Math.random()

根据V8的博客There’s Math.random(), and then there’s Math.random()Math.random()使用的是xorshift128+,一个比MersenneTwister效率高的伪随机数算法。

比较

从代码上看助手的Math.floor(Math.random() * (max - min)) + minMath.round( mTwister.random() * max) - min ) ) + min的 主要差别在于使用Math.floor()还是Math.round()。 因为在抽奖时min通常为0,代码可以简化为Math.floor(Math.random() * max)Math.round(mTwister.random() * max)。 从概率论来看,Math.floor()Math.round()要好,因为Math.round(mTwister.random() * max)的取值范围为[0, max],可能导致数组越界。

测试代码

const array = Array(114);
array.fill(0);
for(let i = 0; i < 114514; i++) {
    array[Math.round(Math.random() * array.length)]++;
}

可以看到array的长度改变了,而且array[0]的次数只有其他的一半,因为另外一半四舍五入成1了。

验证

rnd generator

可以看到1出现的次数明显多于0和2。

结论

综上,助手抽奖使用的随机数算法可以认为是没有问题的,只需要增加一个排除已中奖用户的功能即可。

关于Math.floor()

如果担心Math.floor(Math.random() * max)的取值问题,那么可以转变思路,通过打乱待抽奖评论的顺序,然后从头或尾开始一个个移除来保证随机性和公平性。

谢谢铁汁的验证;我们当时加入抽奖功能的理想使用场景是文章区的抽奖,文章区的抽奖当时主要是Up主定一个时间来抽奖,抽奖的次数一般只有一次,所以当时没有考虑到vup们的多次且需要去重的抽奖,代码层面的实现并没有考虑到需要去除上次抽过的Acer;再次感谢铁汁对我们的建议和验证。

Sokwva avatar Sep 03 '20 01:09 Sokwva