pb 抽奖概率算法_加减取余抽奖法 --- 一个好玩的抽奖算法

d41b80a9e83743b8c89194a0914ab7c5.png

写这篇文章的主要原因,是自己做了一个小抽奖。

3366e9ad2684238476cfa9da37ed1356.png
不是转发这篇文章,是转发想法。

备注: 参与抽奖需要转发的是下面这个想法。

不是文章,不是文章,不是文章

转发本想法 + 关注,送三箱可乐给3位幸运…​www.zhihu.com

之前也参与过其他人的抽奖,但是开奖过程和抽奖真实性,总是不那么权威和公开透明。

其实现场的抽奖算法很多,也很简便的方法。

这篇文章,并不是发表什么抽奖算法的高论,只是自己想拿来玩一玩,做个小游戏小抽奖。

所以设计一个大家都能自己参与运算,自己动手算出来结果的抽奖,来娱乐玩玩而已。


奖到底应该怎么抽?什么才是公正的抽奖算法?

抽奖绝大多数的情况下,一定会用到至少一个随机数。

随机数的生成,是会绝对影响到抽奖的公平公正的。

假设说100人转发,如果只是单纯抽一个1-100的随机数,这个随机数完全可以通过作弊的手段生成,而且生成也没有对证。

所以第一点,随机数很重要,必须要选取一个事先无法预测,但是事后大家都认可,不存在操控的随机数。

第二点,抽奖算法很重要,必须要在数学概率上,满足对中奖范围的离散定义域均匀分布。


随机数的生成 --- 数字种子和简单的加减运算

这个随机数,通过3个种子进行运算生成。

  • 种子数 S1 为 上证指数 收盘数值,扔掉小数,直接取整数
  • 种子数 S2 为 深证成指 收盘数值,扔掉小数,直接取整数
  • 种子数 S3 为 创业板指 收盘数值,扔掉小数,直接取整数

这些种子数,都是时刻可能变化,并且所有人都能参与控制的数字。

当然,极端情况如果某人能控制股市,也可以作弊的。
都能控制股市了,还至于来这作弊么?

但种子数只是一部分而已,接下来是随机数的生成运算。

运算就简单一点了,只用加减法。

定义运算符 O ∈ {+,-}, 定义 O(0) 为 +,O(1) 为 -

有3个种子数,中间可以插两个运算符。

比如 S1 + S2 + S3 , 这就是插了两个运算符 {+,+},换成O就是 { O(0), O(0) }。

S1 + S2 - S3 , 这就是插了两个运算符 {+,-}, 换成O就是{ O(0), O(1) }。

取运算结果为 R ,

那么 R = S1 O(x) S2 O(y) S3, 其中 x ∈{0,1}, y ∈{0,1}

如果看到这,看懂了为什么我要取0,1函数的话,应该能猜得到下面就是01真值表了。

为什么要取3个种子,是为了获取2个运算符位.

2个符位就是 2^2 个结果,而我的抽奖,需要抽4个值出来,所以需要4个随机数。

97e46368ad0cb497c5e9d8d24eae8db8.png

从上到下,得到四个运算结果。

为了消除负数对后续运算的影响,分别取绝对值,得到结果为 R1, R2, R3, R4


抽奖算法的选取 --- 简单的取余运算

四个随机数已经有了,假设有 N 个人参与了转发。

那么现在就要通过一个运算,得到一个结果,来指定到底是哪一个人中奖。

也就是要找到一个中奖的编号。

先对这 N 个人编号,每个参与人的编号为 I , 按照转发的时间顺序,最早转发为编号 I = 0,逐个递增,最晚转发则为编号 I=(N-1).

随机数 R,通过取余运算得到编号,满足对中奖范围 {0,1,2,3...,N-1} 离散定义域的均匀分布。

举例说明,假设R为 123,N为 12 , 那么 R%N = 3.

假设R为 100,N为 10 , 那么 R%N = 0.

假设R为 109,N为 10 , 那么 R%N = 9.

现在有四个随机数 R1, R2, R3, R4,会分别各自做四次取余,得到四个编号 I1, I2, I3, I4

因为取余运算是各自独立事件,有可能会取余得到相同结果。

如果出现相同结果编号,则编号向后顺延加1,直至结果集中没有重复元素。

伪代码如下:

input: 
    Random number set R = {R1,R2,R3,R4}
    Total number of people N
output: 
    The index set I

for each r in R {
  i = r % N
  while( I contain i ){
    i = i + 1
  }
  set I add the element i
}
return I

举例:R = {100, 123, 53, 23}, N = 10

I1 = 100%10 = 0, 第0编号中奖

I2 =123%10 = 3,第3编号中奖

I3 = 53%10 = 3,由于3已经存在,所以顺延+1,第4编号中奖

I4 = 23%10 = 3,由于3和4都存在,顺延+1至第5,第5编号中奖

我的抽奖结果是3位幸运用户抽中可乐,1位幸运用户抽中充电宝。

所以要简单点,前三位中奖为可乐,第四位中奖为充电宝。


抽奖算法拓展

其实这是一个拓展性很强的算法,下面讲点抽象的数学内容,有兴趣了解的可以往下看。

这个抽奖流程需要的东西有:

  • 种子数集合 S ,种子数目为m
  • 数的二元运算集合 O
  • 抽奖人数 N
  • 能将随机数映射到离散域 {0,1,2,3...,N-1} 并且 均匀分布的算子 f

根据期望的中奖人数 E 可以调整 S 集合的大小,也就是调节m。

满足 2^(m-1) ≥ E 即可。

全世界的证券指数多得是,S应该很够用。

二元运算符用加减,只是为了方便对应01真值表,当然也可以换成其他满足随机分布的二元运算。如果换运算符,则要看运算集合 O 的 大小,应当使其满足

|O| ^(m-1) ≥ E

算子 f 也可以不用取余,但是取余是一个比较简单方便的方法。

根据自己的需要,f 完全可以换成其他的哈希函数,调整使其满足均匀分布即可。


抽奖代码如下:

import java.util.*;

public class Lottery {

    public static final int SSEC = 3385; // 上证指数 
    public static final int SZI = 13666; // 深证成指 
    public static final int CHPI = 2684; // 创业板指
    public static final int N = 76; // 参与抽奖的总人数 编号为0-75

    public static void main(String[] args) {
        List<Integer> rands = getRandList();
        System.out.println("rands:" + rands.toString());

        List<Integer> luckyIndexs = new ArrayList<Integer>();
        rands.forEach(r -> {
            int index = r % N;
            while(luckyIndexs.contains(index)){
                index++;
            }
            luckyIndexs.add(index);
        });
        System.out.println("luckyIndexs:" + luckyIndexs.toString());
    }

    public static List<Integer> getRandList() {
        List<Integer> seeds = Arrays.asList(SSEC, SZI, CHPI);
        return Arrays.asList(
                seeds.get(0) + seeds.get(1) + seeds.get(2),
                Math.abs(seeds.get(0) + seeds.get(1) - seeds.get(2)),
                Math.abs(seeds.get(0) - seeds.get(1) + seeds.get(2)),
                Math.abs(seeds.get(0) - seeds.get(1) - seeds.get(2))
        );
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值