简单的随机立减——JAVA实现概率计算(数字不同范围按照不同几率产生随机数)-记录

JAVA实现概率计算(数字不同范围按照不同几率产生随机数)

转载于JAVA实现概率计算(数字不同范围按照不同几率产生随机数)

原文是int 转成了double-记录

maven

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

import org.apache.commons.lang3.RandomUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 按几率产生随机数
 * 例如,产生0.1-100的随机数,0.1-1的几率是90%,1-10的几率是9%,10-100的几率是1%
 */
public class RateRandomNumber {

    /**
     * 产生随机数
     * @param min 最小值
     * @param max 最大值
     * @return 随机结果
     */
    public static double produceRandomNumber(double min,double max){
        return RandomUtils.nextDouble(min,max); //[min,max]
    }

    /**
     * 按比率产生随机数
     * @param min 最小值
     * @param max 最大值
     * @param separates 分割值(中间插入数)
     * @param percents 每段数值的占比(几率)
     * @return 按比率随机结果
     */
    public static double produceRateRandomNumber(double min,double max,List<Double> separates,List<Double> percents){
        if(min > max){
            throw new IllegalArgumentException("min值必须小于max值");
        }
        if(separates == null || percents==null || separates.size()==0){
            return produceRandomNumber(min,max);
        }
        if(separates.size() +1 != percents.size()){
            throw new IllegalArgumentException("分割数字的个数加1必须等于百分比个数");
        }
        BigDecimal bigDecimal = new BigDecimal(0);
        for(Double p:percents){
            if(p<0 || p>100){
                throw  new IllegalArgumentException("百分比必须在[0,100]之间");
            }
            bigDecimal = bigDecimal.add(new BigDecimal(Double.toString(p)));
        }
        if(bigDecimal.doubleValue() != 100){
            throw new IllegalArgumentException("百分比之和必须为100");
        }
        for(double s:separates){
            if(s <= min || s >= max){
                throw new IllegalArgumentException("分割数值必须在(min,max)之间");
            }
        }
        int rangeCount = separates.size()+1; //例如:3个插值,可以将一个数值范围分割成4段
        //构造分割的n段范围
        List<Range> ranges = new ArrayList<Range>();
        double scopeMax = 0.0;
        for(int i=0;i<rangeCount;i++){
            Range range = new Range();
            range.min = (i==0 ? min:separates.get(i-1));
            range.max = (i== rangeCount-1 ?max:separates.get(i));
            range.percent = percents.get(i);

            //片段占比,转换为[1,100]区间的数字
            range.percentScopeMin = scopeMax +1;
            range.percentScopeMax = range.percentScopeMin + (range.percent-1);
            scopeMax = range.percentScopeMax;

            ranges.add(range);
        }
        //结果赋初值
        double r = min;
        double randomInt = RandomUtils.nextDouble(1,101); //[1,100]
        for(int i=0;i<ranges.size();i++){
            Range range = ranges.get(i);
            //判断使用哪个range产生最终的随机数
            if(range.percentScopeMin <= randomInt && randomInt <= range.percentScopeMax){
                r = produceRandomNumber(range.min,range.max);
                break;
            }
        }
        return r;
    }

    public static class Range{
        public double min;
        public double max;
        public double percent; //百分比

        public double percentScopeMin; //百分比转换为[1,100]的数字的最小值
        public double percentScopeMax; //百分比转换为[1,100]的数字的最大值
    }

    public static void main(String[] args) {
        List<Double> separates = new ArrayList<Double>();
        separates.add(1.0);
        separates.add(10.0);
        separates.add(20.0);
        List<Double> percents = new ArrayList<Double>();
        percents.add(90.0);
        percents.add(5.0);
        percents.add(4.0);
        percents.add(1.0);
        int a = 0;
        int b = 0;
        int c = 0;
        int d = 0;
        for(int i=0;i<10000;i++) {
            double number = produceRateRandomNumber(0, 10000, separates, percents);
            if (number < 1) {
                a++;
            }
            if (1.0 < number && number < 10.0) {
                b++;
            }
            if (10.0 < number && number < 20.0) {
                c++;
            }
            if (20.0 < number) {
                d++;
            }
            System.out.println(String.format("%.2f",number));
        }
        System.out.println(" a = " + a);
        System.out.println(" b = " + b);
        System.out.println(" c = " + c);
        System.out.println(" d = " + d);
//        System.out.println(" number = " + number);
    }
}

### SecureRandom 的随机性特性分析 #### 随机性的定义 随机性是指事件发生的结果无法被精确预测的性质。对于计算机中的随机数生成器而言,分为伪随机数生成器(PRNG)和真随机数生成器(TRNG)。前者基于算法计算得出结果,后者依赖于物理过程[^1]。 #### SecureRandom 的工作原理 `SecureRandom` 是 Java 提供的一个类,用于生成安全性更高的随机数。它是一种加密强度较高的 PRNG 实现,通常利用操作系统提供的熵源作为种子数据来初始化内部状态。这种设计使得 `SecureRandom` 能够提供更难以预测的随机数序列[^3]。 #### 关于重复值的可能性 尽管 `SecureRandom` 使用复杂的算法和高熵种子来增强随机性,理论上仍然存在生成相同值的概率。这是因为任何有限范围内的随机数生成器都会受到鸽巢原理的影响——即如果尝试生成的数量超过可能值的总数,则必然会出现重复[^4]。 然而,在实际应用中,由于其巨大的数值空间以及良好的分布均匀性和周期长度,重复发生的概率极低到可以忽略不计的程度。例如,默认情况下,`SecureRandom` 基于 SHA-1 或其他强哈希函数实现,这些函数具有非常大的输出域,从而显著降低了碰撞几率。 #### 种子的重要性 如同普通的 `Random` 类一样,`SecureRandom` 的行为也取决于初始种子的选择。如果不显式指定种子,则会自动从系统收集足够的熵信息以构建一个不可预见性强的起始点;这一步骤极大地提高了后续产生数字串的安全属性[^2]。 以下是使用 `SecureRandom` 的一段示范代码: ```java import java.security.SecureRandom; public class SecureRandomExample { public static void main(String[] args) { SecureRandom secureRandom = new SecureRandom(); int randomNumber; for(int i=0;i<5;i++){ randomNumber = secureRandom.nextInt(100); // Generates a number between 0 and 99. System.out.println(randomNumber); } } } ``` 此程序展示了如何调用 `secureRandom.nextInt()` 方法得到一系列介于零至九十九之间的整型变量实例。 ### 结论 综上所述,虽然 `SecureRandom` 不可能是绝对意义上的“无重复”,但在绝大多数应用场景下,它的表现已经足够接近理想模型的要求,并且远优于标准库里的简单版本如 `Math.random()` 或者基础 `Random` 对象所提供的功能集。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yizhi-w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值