面试话题:随机数飘落分布概率

刚刚参加一次面试,跟技术面试官探讨了一个话题。面试官给我出了一个题目,涉及算法。大意是:如果我们要设计一个产品抽奖,每种奖品都有一定概率被抽中,当然也要一定概率什么也抽不中。面试官要考试,对问题的描述半遮半掩,多次对话后我弄清了他的意图,于是稍作思考,给出了两种方案。

1. 把未抽中的机会也虚拟为一种产品,将所有产品的概率计算总和,按照100分配实际概率。再给出一个抽奖总次数,生成一个奖品池,比如一个集合,随机抽取后删除该奖品。不过,如果抽奖机会总数很大,这种方案很耗资源。

2. 同上,给每一种奖品分配一个计数器,被随机抽中就会减掉一个,被抽完就删除或者不再被抽取。这种方案不会消耗太多资源。

作为一个面试者,我所理解的就是对概率的精确控制。就如同发行奖券,总数是一定要有的。可是面试官告诉我,没有总数。

我不知道他想要什么样的答案。

我请教他,他告诉我,为每一种产品被抽中的概率设置一个数据空间,看看随机数飘落在那个空间,就抽中哪一种奖品,落到未抽中的空间,就不被抽中。大意就是,把概率参考数据加起来,最大就是这个总和,最小为0,按照概率参考数据依次排列,每一种奖品被抽中概率拥有一个范围。随机生成一个0到总数的随机数,看看会在哪个范围之内。

奖品被抽中概率参考:

小米:20

华为:40

OPPO:15

未中奖:25

我明白了他的意思,感觉的确是很简洁。不过,随机数会均匀分布吗?在我印象中好象不是。他告诉我目前Java的Random是最均匀的。那好吧。

很明显,我追求的是精确,他的需求是大概率。我还是没有领会到面试官的原意。

各自保留意见之后。我回到家,写了一个demo。来对Random随机数的均匀度做一个测试。

package com.chris.algorithm;

import org.junit.Test;

import java.util.Random;

/**
 * @author Chris Chan
 * Create on 2021/5/21 0:55
 * Use for:
 * Explain: 测试随机数发生器的均匀度
 */
public class RandomTest {
    //private Random random = new Random();
    private Random random = new Random();

    @Test
    public void test01() {
        int count = 10000;
        int[] counts = new int[10];
        for (int i = 0; i < count; i++) {
            double v = random.nextDouble() * 100;
            for (int j = 0; j < 10; j++) {
                if (v >= j * 10.0D && v < j * 10.0D + 10) {
                    counts[j]++;
                    break;
                }
            }
        }
        showIntArray(counts);
    }

    @Test
    public void test02() {
        int count = 10000;
        int[] counts = new int[10];
        for (int i = 0; i < count; i++) {
            double v = Math.random() * 100;
            for (int j = 0; j < 10; j++) {
                if (v >= j * 10.0D && v < j * 10.0D + 10.0D) {
                    counts[j]++;
                    break;
                }
            }
        }
        showIntArray(counts);
    }

    private void showIntArray(int[] ints) {
        for (int i = 0; i < ints.length; i++) {
            System.out.printf("%-8d\t", ints[i]);
        }
    }
}

我分别测试了util的Random和Math.random(),结果都差不多.

多次测试,发现区间分布最高值和最低值有时候相差百分之十以上,这算不算均匀呢?估计得看业务需求的重视程度了。处于严格控制的角度,我觉得可能会有问题,可预测的后果就是很有可能我们准备的奖品严重不足,活动预算超支。但是如果这活动本身设计就有很大的包容性,允许有出入,按照面试官的想法,那当然是可以的,算法也简单。

那,面试官的方案是不是会让随机数飘落均匀分布呢?我也写了一个例子来测试。

package com.chris.algorithm;

import java.util.Random;

/**
 * @author Chris Chan
 * Create on 2021/5/21 0:02
 * Use for:
 * Explain:
 */
public class MainTest {
    private Random random = new Random();

    public static void main(String[] args) {
        new MainTest().test01();
    }

    private void test01() {
        //在0~20之间生成6个随机数double中将概率参考数,最后一个为不中奖概率参考数
        double[] nums1 = new double[6];
        for (int i = 0, len = nums1.length; i < len; i++) {
            nums1[i] = random.nextDouble() * 20;
        }
        System.out.println("原始中奖概率参考数:");
        showDoubleArray(nums1);

        //计算各自所占的概率
        double sum = 0.0D;
        for (int i = 0, len = nums1.length; i < len; i++) {
            sum += nums1[i];
        }
        double[] nums2 = new double[6];
        for (int i = 0, len = nums2.length; i < len; i++) {
            nums2[i] = nums1[i] / sum * 100;
        }
        System.out.println("\n\n概率参考书总和:");
        System.out.printf("%-8.15f\n", sum);
        System.out.println("各自所占概率百分数:");
        showDoubleArray(nums2);

        //拼装为连续的左闭区间
        double[] nums3 = new double[7];
        double start = 0.0D;
        for (int i = 0, len = nums3.length; i < len; i++) {
            nums3[i] = start;
            if (i < len - 1) {
                start += nums1[i];
            }
        }
        System.out.println("\n\n拼装的左闭区间序列:");
        showDoubleArray(nums3);

        //飘落随机数
        int count = 100000;
        int[] counts = new int[6];
        for (int i = 0; i < count; i++) {
            double v = random.nextDouble() * sum;
            for (int j = 0, len3 = nums3.length; j < len3 - 1; j++) {
                if (v >= nums3[j] && v < nums3[j + 1]) {
                    counts[j] += 1;
                    break;
                }
            }
        }
        System.out.println("\n\n随机飘落结果统计");
        System.out.println(count);
        showIntArray(counts);


    }

    private void showDoubleArray(double[] doubles) {
        for (int i = 0; i < doubles.length; i++) {
            System.out.printf("%-8.15f\t", doubles[i]);
        }
    }

    private void showIntArray(int[] ints) {
        for (int i = 0; i < ints.length; i++) {
            System.out.printf("%-17d\t", ints[i]);
        }
    }
}

测试结果。

多次测试,结果显示,抽奖随机数飘落分布概率和预设的奖品应有的概率十分接近。面试官的话还是没错的,跟预想的结果很相似。具体需要精确的概率,还是大概率,就根据业务需求来选择算法吧。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值