yokila
yokila
Published on 2022-10-22 / 7 Visits
0
0

JAVA不降低取值密度和取值概率的指定范围内double随机数和int随机数生成

前言

因为突发奇想要生成一批学生考试成绩的测试数据,所以就考虑到随机数的生成,但是发现java各种库(Math、Random、ThreadLocalRandom)自带的随机数生成,取值都是 [x,y) ,于是为了搞出double形式的 [x,y] 取值范围内随机数,诞生了这一篇文章。

(关于为什么这些库的随机数生成,取值范围都是[x, y),大家自行去研究,这里不作展开)

纵览CSDN、博客园、stackflow,我阅读了应该不下几十篇文章/问答,对于整型,也就是int类型、long类型的指定范围内随机数生成,大家都是信手拈来。

但是一旦到达double,这种还有小数部分的数字,就出现了各种欠缺。

像是使用取余数之类的手段,我始终觉得会如同一位外国友人所言——将会降低取值的密度。至于其他的一些手段,我也有试着验证过,有的是会使得取值的概率不一,有的甚至并不能取到闭区间的上限值。

总而言之,似乎在double类型的情况下,指定范围内取随机数,我并未找到一个很好的思路。

于是,我只能先提供出当下觉得相对而言比较认可的写法,希望可以抛砖引玉,得到更好的思路。

一、获取指定范围内的int随机数

/**
     * 获取指定范围内的整型随机数
     * @param min 最小值
     * @param minReach 是否取到最小值
     * @param max 最大值
     * @param maxReach 是否取到最大值
     * @return 符合要求的随机整型数字
     */
    public static int getIntegerNumber(Integer min, boolean minReach, Integer max, boolean maxReach) {
        int num;
        Random rand = new Random();
        if (minReach) {
            // [min, max]
            if (maxReach) {
                // num = min + rand.nextInt(max - min + 1); // 方式一
                num = ThreadLocalRandom.current().nextInt(min, max + 1); // 方式二
            } else { // [min, max)
                // num = min + rand.nextInt(max - min);  // 方式一
                num = ThreadLocalRandom.current().nextInt(min, max); // 方式二
            }
        } else {
            // (min, max]
            if (maxReach) {
                num = max - rand.nextInt(max - min);
            } else { // (min, max) = [min + 1, max)
                num = min + 1 + rand.nextInt(max - min - 1);
            }
        }
        return num;
    }

注意:ThreadLocalRandom得JDK1.7后才支持

二、获取指定范围内的double随机数

/**
     * 生成指定范围的随机double数字
     * @param min 最小值
     * @param minReach 最小值是否可以取到(true-可以)
     * @param max 最大值
     * @param maxReach 最大值是否可以取到(true-可以)
     * @return 符合要求的随机数
     */
    public static double genDoubleNumber(double min, boolean minReach, double max, boolean maxReach) {
        double num;
        if(minReach) {
            // [min, max]
            if(maxReach) {
                // todo 虽然涉及循环,但是目前没有找到不降低取值密度的写法
                // 使用取余数之类带整型的写法,基本都会降低取值密度
                do {
                    // 该公式的取值范围是:[min, max + 0.001)
                    num = ThreadLocalRandom.current().nextDouble(min, max + 0.001);
                } while(num > max); // 排除掉取值大于max的情况,剩下的就是符合要求的值了
            } else { // [min, max)
                // num = min + Math.random() * (max - min); // 方式一
                num = ThreadLocalRandom.current().nextDouble(min, max); // 方式二
            }
        } else {
            // (min, max]
            if(maxReach) {
                num = max - Math.random() * (max - min);
            } else { // (min, max)
                /**
                 * 解释:
                 * (max - 1 - Math.random() * (max - min - 1)) ∈ (min, max - 1]
                 * Math.random() ∈ [0, 1)
                 * 两者相加,可知最终结果取值范围就是 (min, max)
                 * 注意,不要尝试使用乘法结合律
                 */
                num = Math.random() + (max - 1 - Math.random() * (max - min - 1));
            }
        }
        return num;
    }

N、补充

  • 本文使用代码基于我写的存放于github的公开代码(点击前往仓库),欢迎前去查看是否有遗漏或者bug或者复制下来检验

  • 如果你正无聊,不妨浏览我的更多奇奇怪怪的笔记文章。

  • 如果你觉得本文对你有所收获,请点赞转发,让更多人看到这篇文章,谢谢!!!

  • 如果你觉得有哪里不对,也欢迎在评论区留言指教!!!


Comment