前缀和&等概率器

前缀和

问题描述

有一个整数数组,给出数组范围L、R,求L到R之间的数据和

思路

  1. 循环L到R之间的数据累加
  2. 构造二维数组,里面保存所有可能的L到R的数据累加和
  3. 重新构造一个整数数组,后面一个元素保存前面所有元素和自己本身元素之和,计算arr[L,R] = arr[R] - arr[L-1]

二维地图

public static int sumLR(int[] arr, int L, int R){
        if(L > R || L >= arr.length || R >= arr.length) {
            return -1;
        }
        if(L==R) {
            return arr[L];
        }
        int[][] sumMap = new int[arr.length][arr.length];
        for (int i = 0; i < arr.length; i++) {
            for (int j = i; j < arr.length; j++) {
                if(i == j) {
                    sumMap[i][j] = arr[i];
                } else {
                    sumMap[i][j] = sumMap[i][j-1] + arr[j];
                }
            }
        }
        return sumMap[L][R];
    }

前缀和

前缀和节省空间 但是多了一步计算,数据量非常大时没有二维数组计算快

  1. 循环一遍 计算0~i的和放到help数组中
  2. 计算arr[3,7] = h[7] - h[2]
public static int sumLR2(Integer[] arr, int L, int R){
        if(L > R || L >= arr.length || R >= arr.length) {
            return -1;
        }
        if(L==R) {
            return arr[L];
        }
        int[] helper = new int[arr.length];
        helper[0] = arr[0];
        for (int i = 1; i < arr.length; i++) {
            helper[i] = helper[i-1] + arr[i];
        }
        return L == 0 ? helper[R] : helper[R] - helper[L - 1];
    }

测试类

定义测试样本

public class PreSumTest {

    /**
     * @param maxSize  数组大小
     * @param maxValue 数组值范围
     * @return 随机生成数组集合
     */
    public static Integer[] generateRandomArray(int maxSize, int maxValue) {

        Integer[] arr = new Integer[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            // [-? , +?]
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        return arr;
    }

    /**
     * 拷贝新的数组
     */
    public static Integer[] copyArray(Integer[] arr) {
        if (arr == null) {
            return null;
        }
        Integer[] res = new Integer[arr.length];
        System.arraycopy(arr, 0, res, 0, arr.length);
        return res;
    }

    public static void printArray(Integer[] arr) {
        if (arr == null) {
            return;
        }
        StringBuffer sb = new StringBuffer("{");
        for (int i= 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                sb.append(arr[i]);
            } else {
                sb.append(arr[i] + ",");
            }
        }
        sb.append("}");
        System.out.println(sb.toString());
    }

    public static Integer sum(Integer[] arr, int L, int R) {
        int sum = 0;
        for (int i = L; i <= R ; i++) {
            sum += arr[i];
        }
        return sum;
    }

    public static void main(String[] args) {
        //测试的次数
        int testTime = 50000;
        //测试的数组大小
        int maxSize = 100;
        //测试的数组值范围
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++) {
            Integer[] arr1 = generateRandomArray(maxSize, maxValue);
            printArray(arr1);
            if(arr1.length == 0) {
                continue;
            }
            Random random = new Random();
            //随机返回一个值在[0,num)的int类型的整数,包括0不包括num
            int L = random.nextInt(arr1.length );
            int R = random.nextInt(arr1.length );
            int left = Math.min(L, R);
            int right = Math.max(L, R);
            //发现不相等R
            int i1 = PreSum.sumLR(arr1, left, right);
            int i2 = sum(arr1, left, right);
            if (!Objects.equals(i1, i2)) {
                succeed = false;
                printArray(arr1);
                System.out.println("left = " + left + "; right = " + right);
                break;
            }
        }
        System.out.println(succeed ? "Nice!" : "ERROR!");
    }

}

等概率发生器

Math.random()是等概率发生器,在[0, 1)范围内等概率生成值

public static void main(String[] args) {
        int testTimes = 10000000;
        int count = 0;
        for (int i = 0; i < testTimes; i++ ) {
            if (Math.random() < 0.75) {
                count++;
            }
        }
        //打印的数据约等于0.75
        System. out.println( (double) count / (double) testTimes);
    }

通过这个可以做一些有意思的运算

如何等概率返回0到k范围内的值

/**
     * 随机返回[0, k-1]范围的值
     */
    public static int range(int k){
        // [0, K) --> [0, k - 1]
        return (int) (Math.random() * k);
    }

    public static void rangeTest(int k, int testTimes){
        //测试
        int[] arr = new int[k];
        for (int i = 0; i < testTimes; i++) {
            int ans = (int) (Math.random() * k);
            arr[ans]++;
        }
        System.out.println(Arrays.toString(arr));
    }
    

如何利用Math.random()函数,把得到[0,x)范围上的数的概率从x调整成x^2 ?

思路:Math.random()在[0,x)上的概率是 x ,如果两次的最大值 < x,说明产生一个数的概率变为x^2

public static double xToXPower2(){
        return Math.max(Math.random(), Math.random());
    }

    public static void testXToXPower2(){
        double x = 0.21;
        int num = 100000;
        int count = 0;
        for(int i = 0;i < num;i++){
            //返回一个最大数,如果最大数小于x ,说明 两个数都小于x,所以概率为x的平方
            if(xToXPower2() < x){
                count++;
            }
        }
        System.out.println((double)count/(double)num);
        System.out.println(Math.pow(x,2));//Math.pow(x,2) 平方函数
    }

思考:

为什么不使用Math.min()函数

Math.random()在[0,x)上的概率是 x,如果使用min函数,只要有一个发生在[0,x)上就成功,只有都不在[0,x)上min函数的结果才会 >= x。
不在[0,x)上的概率是 1- x ; 两次都不在就是 (1-x)^2 ;最终的概率就是 1 - (1-x)^2

如何调整到x^3的概率

public static double xToXPower3(){
        return Math.max(Math.max(Math.random(), Math.random()), Math.random());
    }

1~5随机调整为1~7随机

有一个函数f1()等概率返回 1-5 范围的数,如何借助当前f1()函数实现1 - 7范围的等概率返回?

 /**
     * 等概率返回1~5
     */
    private static int f1(){
        return (int)(Math.random() * 5) + 1;
    }


    private static int f2(){
        int ans = 0;
        do {
            ans = f1();
        } while (ans == 3);
        //改造f1函数为0到1的等概率发生器
        return ans < 3 ? 0 : 1;
    }
    
/**
     * 得到000 ~ 111的等概率器 也就是0~7的概率
     *
     * 为什么不直接 f2() * 6 + 1 得到 1~7的概率? 因为f2只会返回0和1,这样只能得到1和7两个值而不是1到7
     */
    private static int f3(){
        return (f2() << 2)  + (f2() << 1) + f2() ;
    }

/**
     *  0到6等概率发生器
     */
    private static int f4(){
        int ans = 0;
        do {
            ans = f3();
        } while (ans == 7);
        return ans;
    }

    /**
     * 得到1到7的等概率器
     */
    private static int g(){
        return f4() + 1;
    }

a~b随机调整为c~d随机

思路:参考上面的例子 比如3~19 调整为 17~56

  1. a~b取中间位数据 对半分割 转为0到1的等概率 (3~10 和 12到19)
  2. 计算c~d的范围需要左移的次数 (左移6次 最大值63)
  3. c~d 调整为 0 ~ d - c的范围 (17~56 调整为 0 到 39)
  4. 0到63的范围内数重做为只返回0到39之间的数据
  5. 返回0到39的随机 + 17 得到 17~56 的随机

01不等概率随机调整为01等概率随机

有一个函数x()返回 0 和 1 的概率为固定概率,但不是等概率,如何用该函数实现一个等概率发生器函数y()?

/**
     * 一个固定概率 返回 0 和 1 的函数
     * 但此函数不是等概率返回 0和1
     */
    public static int x (){
        return Math.random() < 0.85 ? 0 : 1;
    }

x() 返回 0 的概率 为 p,返回1的概率为 1-p
则两次x()方法 返回 01 和 10的概率是相同的都是 p * (1-p)。只要代码能保证两次结果不一致 则是等概率返回0和1

public static int y(){
        int ans = 0;
        do{
            ans = x();
        }while (ans == x());
        return ans;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值