primary 02: 前缀和,对数器与随机函数

  1. 数据结构分为物理结构与逻辑结构:
    逻辑结构: 有序表,链表,二叉树等
    物理结构: 紧密结构(连续结构),跳转结构
    ps: 所有的逻辑结构都是基于物理结构所形成的(单一或组合。)

  2. 基本数据结构:
    数组:便于寻址,不便于增加和删除
    链表:便于增加和删除,不便于寻址

稍微特殊一点的是,当数组很大时,可能会出现少量的跳转结构。 比如一亿长度的数组,1000万紧密存放在一起,然后尾指针指向下一个块的地址。

  1. 前缀和解法 --> 针对范围和问题
    给定一个数组,任意给出左右边界的值,,求以O(1)的时间复杂度返回累加和。
    (1)二维数组

(2)前缀和 --> 一维数组

public class Code01_PreSum {

	public static class RangeSum1 {

		private int[] arr;

		public RangeSum1(int[] array) {
			arr = array;
		}

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

	}

	public static class RangeSum2 {

		private int[] preSum;

		public RangeSum2(int[] array) {
			int N = array.length;
			preSum = new int[N];
			preSum[0] = array[0];
			for (int i = 1; i < N; i++) {
				preSum[i] = preSum[i - 1] + array[i];
			}
		}

		public int rangeSum(int L, int R) {
			return L == 0 ? preSum[R] : preSum[R] - preSum[L - 1];
		}

	}

}

随机函数

  1. JAVA中的随机函数 -> Math.random() --> 返回double类型 [0, 1)区间 等概率返回 (因为小数有精度,所以小数不是无穷的)

    –> [0, 1) --> [0, 8) 注意是double类型
    Math.random() * 8

    –>double [0, 1) --> (int)(Math.random() * K) --> int [0, K) --> int [0, K-1]

  2. 通过随机函数,可以得到任意范围的值 e.g. [16, 54] ([0, 1) * 39 + 16 )。
    Math.random * K + t

  3. 通过随机函数,实现不等概率返回,对于生成的任意数值,使得出现在[0 ,x]出现的概率的
    3.1 使得 [0, x) 的出现的概率为 x^2
    在这里插入图片描述
    3.2 使得[0, x)出现的概率为 1-(1 - x)^2
    答: Math.min(Math.random(), Math.random());

  4. 随机函数面试题
    4.1 从 1 ~ 5 随机 f(x) 到 1 ~ 7 g(x) 随机 等概率
    (1)1 2 --> 0; 3 重做; 4 5 --> 1 >>>>>>>>> 实现等概率01生成器
    (2)1 ~ 7共7个数字, 三位二进制可以满足(8种组合),对二进制进行移位 (x<<3 + x<<2 + x<<1) 属于范围 [0, 7]
    (3)封装函数, do { } while (x == 0) ----> 0重做,其余输出,从而完成 1 - 7等概率随机

    // 1~5随机 [1, 5] --> int [1, 6) --> int [0, 5) + 1
    public static int f() {
        return (int) (Math.random() * 5 + 1);
    }
    
    // 等概率01生成器
    public static int _01Generator() {
        int ans;
        do {
            ans = f();
        } while (ans == 3);
        return ans < 3 ? 0 : 1;
    }
    
    public static int g() {
        int ans;
        do {
            // 小心这里,记不清顺序 就乖乖把 括号加上,保证运算顺序
            ans = (_01Generator() << 2) + (_01Generator() << 1) + (_01Generator() << 0);
        } while (ans == 0);
        return ans;
    }
    

    OR
    (3)在第二步之后,将其变成 [0, 6]的等概率生成器(7重做), 然后 封装函数,使其返回值 +1 ,就变成了 [1, 7]。

    // return [0, 6]
    public static int g1() {
        int ans = 0;
        do {
            ans = (_01Generator() << 2) + (_01Generator() << 1) + (_01Generator() << 0);
        } while (ans == 7); // return [0,7] ----> [0, 6]
        return ans;
    }
    
    public static int g2(){
        return g1() + 1;
    }
    

    4.2 从 a ~ b 随机到 c ~ d 随机 - 等概率
    推荐使用后一种(3),基本方法相同。但latter更有普适性。(比如 3-19生成器 变为 128-150生成器,只需将[128,150] —> [0, 22] + 128,从而不需要生成过多二进制位)
    构造01等概率发生器(奇数重新生成,偶数直接对半分),寻找包含范围的二进制位数,调整对应的生成随机数范围
    4.3 从01不等概率到01等概率随机
    0 0 --> 重做
    1 1 --> 重做
    1 0 --> 50% --> 返回 1
    0 1 --> 50% --> 返回 0

    // 01不等概率生成器 0的概率为 P = 0.8, 1的概率为 0.2
    public static int x(){
        return Math.random() > 0.8 ? 0: 1;
    }
    
    // 转换为01等概率
    public static int y(){
        int ans;
        do{
            ans = x();
        }while( ans == x());
        return ans;
    }
    

根本思路: 重点:** 01等概率发生器 ** + do-while循环 使得部分重做(重新生成), 针对01进行二进制移位 (看看需要几个二进制位)+ do-while, over

对数器 - 生成随机样本自己做比对的机器

对数器是干什么用的?
对数器是用来调bug的,对数器让你不需要依赖线上的测试,自己就可以调试代码。 可以随意控制样本大小。 大样本随机。

(factor 1)比对器 (两个方向 : 一个是生成正确结果,然后与测试函数进行比较; 另一个是将测试函数的结果作为参数,进行正误校验)
(factor 2) 生成随机数组生成器,创建数组,其中random length & random value
(factor 3 optional)数组拷贝,从而将实例备份,当出现错误实例时,输出错误实例

当出现错误实例后,将错误实例代入代码中,就可以调BUG了。
举例:

package class02;

import class01.BasicSort;

public class Comp {
    // 返回int[] arr, arr长度在[0, maxLen - 1], arr中的值在[0, maxValue - 1]
    public static int[] lenRandomValueRandom(int maxLen, int maxValue) {
        //长度随机
        int len = (int) (Math.random() * maxLen);
        int[] arr = new int[len];
        // 给数组中每一个元素赋随机值
        for (int i = 0; i < len; i++) {
            arr[i] = (int) (Math.random() * maxValue);
        }
        // 返回数组,这个数组的长度和值都是随机的
        return arr;
    }

    // 传入一个数组, 返回一个一模一样的新数组
    public static int[] copyArray(int[] oldArr) {
        int[] newArr = new int[oldArr.length];
        for (int i = 0; i < oldArr.length; i++) {
            newArr[i] = oldArr[i];
        }
        return newArr;
    }

    // 暴力方法,验证一个数组是有序的, 这里验证升序
    public static boolean isSorted(int[] arr) {
        // 一定要注意 边界检测  ---> 防止数组越界
        if (arr.length < 2) {
            return true;
        }
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max > arr[i]) {
                return false;
            }
            max = Math.max(max, arr[i]);
        }
        return true;
    }

    public static void main (String[] args) {
        int maxLen = 50;
        int maxValue = 1000;
        int testTimes = 10000;

        // 开始 **实测**
        for (int i = 0; i < testTimes; i++) {
            // 首先创建一个 随机数组 ----》 随即长度 + 随机值
            int[] arr1 = lenRandomValueRandom(maxLen, maxValue);
            // 克隆出一个tmp备份, 与arr1相同,但是两个内存区域。 arr1出错时, 打印备份
            int[] tmp = copyArray(arr1);
            BasicSort.selectSort(arr1);
            if(!isSorted(arr1)) {
                // 如果没有生成 下列语句 那么说明 结果一定正确 ----》 REASON: 大样本随机都对
                System.out.println("排序出错了");
                // 如果出错了,输出 错误的 实例  -----》 使用错误实例 来 调bug
                // 不要使用 这个 System.out.println(tmp);, 这个打印的是toString方法,[I@77a2aa4a
                for (int j = 0; j < tmp.length; j++) {
                    System.out.print(tmp[j] + " ");
                }
                System.out.println();
                // 加上这句话,出错一个之后就跳出,从而对单个错误实例进行检验
                break;
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值