【算法数据结构初阶篇】:随机函数

news2024/11/19 5:57:37

随即函数的用处非常大,比如可能用来用做对数器,生成大量随机的测试数据,用来验证我们写的程序是否有误,可以帮助我们快速定位存在错误的测试用例,进行debug。这里注意Java中的随机函数Math.random()是等概率的返回[0,1)区间的任意浮点数

目录

一、将1-5(a-b)的随机函数 转换成 1-7(c-d)的随机函数

一、逻辑分析:

二、代码演示:

 二、等概率返回0和1的动态函数

 三、等概率返回一个区间内任何数

四、[0,x)范围上的数出现概率由原来的x调整成x平方

五、已知一个函数以不等概率返回0 1,调用其函数得基础上改造成以等概率返回 

 六、随机函数实现一个对数器,即随机生成大量的测试用例来验证程序是否有错误


 结合实际情况,我们对随机函数做一些相应的转换运用

一、将1-5(a-b)的随机函数 转换成 1-7(c-d)的随机函数

一、逻辑分析:

这里需要巧妙用到,二进制位运算得方式来转换,核心就是

  1. 先将等概率返回1-5的随机函数转换成等概率返回0和1的随即函数。
  2. 再将0和1随机函数转换为目标函数f(c-c,d-c)的随机函数,如题目标为[1,7]函数,则先转换为[1-1,7-1]即 [0,6]的随机函数,这里就需要用到二进制位运算
  3. 再将[0,6]等概率随即函数加上目标函数左边界c,题为1,所以得到[1,7]

二、代码演示:

package class02;

public class Code02_RandToRand {
    //1.等概率返回[1-5]
    public static int f1_5() {
        //Math.random()返回[0,1)   *5 则为 [0,5) 将其转换int整形则为 [0,4]  再 +1 则为[1-5]
        return (int) (Math.random() * 5) + 1;
    }

    //2.[1-5]转换成等概率返回[0,1]
    public static int a() {
        int ans = 0;
        //核心点就是将1-5 奇数个,需要取偶数个,进行均分到0和1 比如 1 2 输出0, 4 5 输出1,循环3,不为3时则跳出
        do {
            ans = f1_5();
        } while (ans == 3);
        return ans > 3 ? 1 : 0;
    }

    //3.[0,1]转换成等概率返回[0-6]
    public static int f0_6() {
        int ans = 0;
        do {
            /*
            用二进制位运算来处理, 6 -> 110, 需要取三位,每一位都需要等概率返回0或1,然后会存在111 ->7 ,
            不在范围内所以需要进行循环7 不为7时则跳出
             */

            ans = (a() << 2) + (a() << 1) + a();
        } while (ans == 7);
        return ans;
    }

    //4.[0-6]转换成等概率返回[1-7]
    public static int f1_7() {
        return f0_6() + 1;
    }

    public static void main(String[] args) {
        System.out.println("测试开始");
        // Math.random() -> double -> [0,1)
        // 随机函数等概率返回[0,1)证明
        int testTimes = 10000000;
        int count = 0;
        for (int i = 0; i < testTimes; i++) {
            if (Math.random() < 0.75) {
                count++;
            }
        }
        //验证:随即函数是等概率的返回一个数,小于0.75的数,概率出现的占比就约等同与0.75
        System.out.println((double) count / (double) testTimes);
        System.out.println("=========");
        //验证:等概率[1-5]转换成等概率[1-7] 定义长度8数组 下标是0-7 才能给1-7存数值
        int N = 8;
        int counts[] = new int[N];
        for(int i = 0; i < testTimes; i++){
            int num = f1_7();
            counts[num]++;
        }
        for(int i = 1; i < N; i++){
            System.out.println(i + "这个数,出现了" + counts[i] + "次");
        }


    }
}

输出如下:

总结:

随机函数返回小于0.75,测验千万次,小于0.75的概率真的就是接近0.75证明等概率

函数F(1-7)等概率返回1-7,也能发现次数都十分接近。

 二、等概率返回0和1的动态函数

其实就是跟前面 1-5等概率转换成等概率返回0和1一样,只不过现在是这个区间的未知,动态的。

核心在于将区间转成 [0,max-min] ,其次是要判断是奇数还是偶数,如果是偶数的话,取出区间的个数后除以2得到一个中间值,这个中间值就用来判断[0,max-min] 小于则返回0 大于则返回1,因为个数是偶数个 可以平分,达到等概率,若为奇数,则需要剔除中间值,达到偶数个 ,然后各取两边的数平分给0和1

代码如下: 

    /*
    等概率返回0 1 解决思路:先判断区间个数,如果是奇数,则中间值不能进行输出,1-5,则不输出3 输出12 45 分别表示0 1
    然后把区间min-max 转换成 0 - (max-min)  这样再去与求得的区间个数/2的中间值做判断大小返回 0 1
     */
    public static int return0_1(int min, int max) {
        int size = max - min + 1;
        //与1运算可以判断最低位是1还是0 如果是1,表示是奇数, 与1运算后结果为1 ,偶数则最低位位0 ,与1运算后结果为0
        boolean flag = (size & 1) != 0;
        int ans = 0;
        int mid = size / 2;
        do {
            /*如果是奇数 flag=true,并且ans值等于中间值,就需要进行循环,直到不是中间值跳出 即排除中间值,这样就是偶数个平均0 1概率
            例如 13-17  size=17-13+1=5个数 [0,1)*5=[0,5) int -> [0,4] 实际上就是把13-17区间-13 转换成0-4 ,
            然后mid=2,也就是0-4的中间值,
             */
            ans = (int) (Math.random() * (size));
        } while (flag && ans == mid);
        return ans > mid ? 1 : 0;
    }

 测试结果如下: 在千万次测试用例 出现0和1的次数很接近

 三、等概率返回一个区间内任何数

思路:还是先转换区间,[from,to]区间转换为[0,to-from]最后就是等概率返回[0,to-from]再加上from这个最小值即为题目所求值了。新区间的等概率事件,可以利用上面提到的 用二进制判断需要几位,如1-7 转换为0-6,而6的二进制是110,需要三位二进制,这个上面其实是提到了,因为这里我们具体不清楚需要几位二进制,所以遍历次数num,通过每次左移,然后利用或运算进行累加和,得到ans ,注意就是三位二进制是有111的概率 为7 超过了边界6,所以要排除大于边界值的概率值

 代码如下: 

   //等概率返回from~to范围上任何一个数 13-17
    public static int random(int from, int to) {
        if (from == to) {
            return from;
        }
        // 1 ~ 7
        // 0 ~ 6
        // 0 ~ range
        int range = to - from;
        int num = 1;
        // 求0~range需要几个2进制位 num=3
        while ((1 << num) - 1 < range) {
            num++;
        }

        // 我们一共需要num位
        // 最终的累加和,首先+0位上是1还是0,1位上是1还是0,2位上是1还是0...
        int ans = 0;
        do {
            ans = 0;
            for (int i = 0; i < num; i++) {
                //return0_1(from, to) 等概率返回0 1,然后一步步开始左移,或运算表示将每次移位后的值加到ans上,一个为1 则得 1,
                //超过了range边界则需要进行循环
                ans |= (return0_1(from, to) << i);
            }
        } while (ans > range);
        //最后就是把起始值即最小值加上[0,range]区间等概率返回得值
        return ans + from;
    }

 测试结果如下: 在千万次测试用例中 等概率出现1-7,次数很接近

四、[0,x)范围上的数出现概率由原来的x调整成x平方

我们知道Math.random()得到的是一个[0-1)区间等概率任意一个浮点数,即[0,..x,1)区间中,[0-x)的概率接近与x,这个前面已经有代码验证,现在想改造,使得返回[0-x)随机数的概率从x调整到x^2,这里核心就是通过调用两次的随机数,然后为了使两次值都是在[0-x)区间的,我们需要用Math.max(Math.random(), Math.random()),只有两次都使在这两个区间内的,最大值得到的才能是[0,x)区间内的,假设一个不在那就是大于等于x,相当于最大值取不到[0,x),而两次取数都需要确保在[0,x)区间,概率就变成平方了。

 代码如下:

    // 返回[0,1)的一个小数
    // 任意的x,x属于[0,1),[0,x)范围上的数出现概率由原来的x调整成x平方
    public static double xToXPower2() {
        return Math.max(Math.random(), Math.random());
    }

    public static void main(String[] args) {
        System.out.println("测试开始");
        // Math.random() -> double -> [0,1)
        // 随机函数等概率返回[0,1)证明
        int testTimes = 10000000;
        int count = 0;
        double x = 0.17;
        for (int i = 0; i < testTimes; i++) {
            if (xToXPower2() < x) {
                count++;
            }
        }
        //验证:[0,x)区间概率从x调整到了x平方
        System.out.println((double) count / (double) testTimes);
        System.out.println((Math.pow(x, 2)));

测试结果如下: 与调用Math.pow()平方函数的值接近

 

如果要得到[0,x)返回概率x^3呢? 取三次随机数就可以啦。

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

五、已知一个函数以不等概率返回0 1,调用其函数得基础上改造成以等概率返回0 1 

假如有个固定函数,不等概率返回0 1,我们也不知道该函数得实现方式,以及以什么不等概率返回,然后通过其函数要转换成等概率的返回 0 1,那我们可以将其调用两次, 如果两次结果相等,那我们就跳过,如果两次结果不一样,那我们就可以返回,表示这次循环两次结果不一样 可以是 0 1  也可能是 1 0 我们就将第一次得到的返回,这样就得到等概率的返回0 1

代码如下:

    // 你只能知道,x会以固定概率返回0和1,但是x的内容,你看不到!
    public static int x() {
        return Math.random() < 0.84 ? 0 : 1;
    }

    // 等概率返回0和1
    public static int y() {
        int ans = 0;
        do {
            //第一次调用得到值,如果条件中第二次调用值与之相等,则继续循环,直到两次是不相等,则返回ans,不等即表示0  1跳出  结果就返回0 或者 1 0跳出,结果就返回1  这两种都是等概率的返回0 1的 
            ans = x();
        } while (ans == x());
        return ans;
    }
    
    public static void main(String[] args) {
        int count0 = 0;
        int count1 = 0;
        for (int i = 0; i < testTimes; i++) {
            if (y() == 0) {
                count0++;
            }else count1++;
        }
        System.out.println("0出现次数:" + count0);
        System.out.println("1出现次数:" + count1);
    }

 测试结果如下:测试接近

 六、随机函数实现一个对数器,即随机生成大量的测试用例来验证程序是否有错误

验证选择排序算法是否没有错误,可以定义大量随机用例数组进行传参,另外需要深拷贝一份用例数组,保留原来的顺序。用来判断如果出错了遍历错误的测试用例,两者的长度也肯定是相等的。

代码如下:

package class02;

/**
 * 随机函数写一个对数器,即随机生成大量的测试用例来测试程序是否正常
 */
public class Code03_Comp {
    public static void selectionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            swap(arr, i, minIndex);
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    // 返回一个数组arr,arr长度[0,maxLen-1],arr中的每个值[0,maxValue-1]
    public static int[] lenRandomValueRandom(int maxLen, int maxValue) {
        int len = (int) (Math.random() * maxLen);
        int[] ans = new int[len];
        for (int i = 0; i < len; i++) {
            ans[i] = (int) (Math.random() * maxValue);
        }
        return ans;
    }

    public static int[] copyArray(int[] arr) {
        int[] ans = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            ans[i] = arr[i];
        }
        return ans;
    }

    // arr1和arr2一定等长
    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 = 5;
        int maxValue = 1000;
        int testTime = 10000;
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = lenRandomValueRandom(maxLen, maxValue);
            int[] tmp = copyArray(arr1);
            selectionSort(arr1);
            if (!isSorted(arr1)) {
                for (int j = 0; j < tmp.length; j++) {
                    System.out.print(tmp[j] + " ");
                }
                System.out.println();
                System.out.println("选择排序错了!");
                break;
            }

        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/163524.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP/IP网络编程(4)——基于 TCP 的服务端/客户端(1)

文章目录第 4 章 基于 TCP 的服务端/客户端&#xff08;1&#xff09;4.1 理解 TCP 和 UDP4.1.1 TCP/IP 协议栈4.1.2 链路层4.1.3 IP 层4.1.4 TCP/UDP 层4.1.5 应用层4.1.6 生活小例子4.2 实现基于 TCP 的服务器/客户端4.2.1 TCP 服务端的默认函数的调用程序4.2.2 进入等待连接…

微信小程序原生开发功能合集一:微信小程序开发介绍

一、专栏介绍 本专栏主要内容为微信小程序常用功能开发过程的介绍说明&#xff0c;包括开发微信小程序常用组件的封装、常用功能的开发等&#xff0c;提供源代码、开发过程讲解视频、完整的课程等。 组件封装&#xff1a; 下拉选择组件、图片上传组件、视频上传组件、富文本组件…

C# Dictionary(字典)各种属性的用法

C# Dictionary(字典&#xff09;各种属性的用法 要使用Dictionary集合&#xff0c;需要导入C#泛型命名空间&#xff01; ​System.Collections.Generic​&#xff08;程序集&#xff1a;mscorlib&#xff09; Dictionary的描述 1、从一组键&#xff08;Key&#xff09;到一组值…

seo关键词排名下降怎么办(SEO网站关键词排名不稳定)

关键词优化网站排名突然下降是什么原因 一般来讲&#xff0c;网站的排名不会大幅度的下降&#xff0c;除非搜索引擎的流量特别小&#xff0c;而这也不是偶然发生的。网站一旦获得对应的权重或者排名&#xff0c;就不要轻易的更改板块和文章&#xff0c;甚至是文章标题之类的&a…

Allegro如何导出和导入层叠操作指导

Allegro如何导出和导入层叠操作指导 在做PCB设计的时候,需要导出或者导入层叠,Allegro升级到了172以后,支持单独导出和导入层叠,如下图 具体操作如下 导出层叠,选择setup选择Cross-section

《图机器学习》-Message Passing and Node Classification

Message Passing and Node Classification一、前言二、How do we leverage node correlation in network&#xff1f;三、Relational Classification四、Iterative classification五、Loopy Belief Propagation一、前言 现在的主要问题是&#xff1a; 给定一个网络&#xff0c…

利用idea把项目上传到不同的仓库

前言 以项目已经创建好&#xff0c;并已经和一个远程仓库地址联通的情况下。 由于GitHub经常会出现问题&#xff0c;毕竟国外的东西&#xff0c;时常会不稳定&#xff0c;所以我们有时候也会使用国产Git代码管理工具——码云。 说真的&#xff0c;码云可真的是一个好东西。要…

SpringBoot原理-自动配置-Condition

目录 分析 通过获取 run() 的返回值再获取容器中的bean Conditional() 注解 解说案例 1.导入Jedis坐标后&#xff0c;加载该Bean&#xff0c;没导入&#xff0c;则不加载 思考 spring-boot-autoconfigure condition包 ConditionalOnClass ConditionalOnBean Conditi…

Python【bs4模块】讲解

导入&#xff1a;from bs4 import BeautifulSoup#爬取某豆的电影榜单 import pprint import requests from bs4 import BeautifulSoup class pachong:#pass #这段代码的意思如果暂时没有想好在类中定义任何属性和方法&#xff0c;你可以先写pass Python就会认为这段不会运行直接…

基于GeoDa软件的省级人口经济空间分析

前言本为从地统计学角度&#xff0c;借助空间自相关的方法&#xff0c;从县域尺度&#xff0c;研究江西省各县经济发展水平之间的相关关系&#xff0c;及经济发展与人口之间的相关关系&#xff0c;直观的说明江西省2000年的经济发展状况。通过对各市第一、第二、第三产业的GDP等…

Servlet总结(未完待续)

Servlet总结一. 简介二. Servlet执行流程三. 生命周期四. 体系结构五. urlPattern配置匹配规则六. Request和Response1. Request1.1 Request继承体系1.2 Request获取请求数据API1.3 通用方式获取请求参数1.4 通用方式中文乱码解决1.5 Request请求转发2.1 Response一. 简介 Ser…

轻量级 Java 权限认证框架——Sa-Token

Sa-Token 介绍 Sa-Token 是一个轻量级 Java 权限认证框架&#xff0c;主要解决&#xff1a;登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。 Sa-Token最新开发文档地址&#xff1a;https://sa-token.cc Sa-Token功能结构图…

MySQL最左匹配原则

说到最左匹配原则&#xff0c;我们还得先从组合索引说起。 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS user; CREATE TABLE user (id int(5) NOT NU…

【Java AWT 图形界面编程】LayoutManager 布局管理器 ⑥ ( BoxLayout 布局 )

文章目录一、BoxLayout 布局二、BoxLayout 布局 API三、BoxLayout 布局代码示例1、BoxLayout 布局垂直排列代码示例2、BoxLayout 布局水平排列代码示例一、BoxLayout 布局 BoxLayout 布局 不是 AWT 中的布局 , 而是 Swing 中引入的 ; 在 BoxLayout 布局 中 , 可以 在 垂直 和 …

[Effective Objective] 对象、消息、运行期

对象&#xff1a;“对象”&#xff08;object&#xff09;就是“基本构造单元”(building block)&#xff0c;开发者可以通过对象来储存并传递数据。 消息&#xff1a;在对象之间传递数据并执行任务的过程就叫做“消息传递”&#xff08;Messaging&#xff09;。 运行期&…

最粗暴的方法实现一个栈

对于栈和队列是一个很简单的知识&#xff0c;用的感觉也不是很多&#xff0c;但是&#xff0c;我们仍然的学习&#xff01;&#xff01;加油&#xff01;&#xff01;在实现最简单的栈之前&#xff0c;我们需要简单了解一下栈是什么&#xff1f;&#xff1f;栈&#xff08;stac…

iplatform平台简介

前置条件&#xff1a;原则规范一&#xff09;统一技术栈1&#xff09;关于JDK统一使用Open JDK&#xff0c;版本最低1.8&#xff0c;几年后可能会升级到17&#xff1b;避免使用Sun JDK&#xff0c;这是商业软件&#xff0c;而且包含部分私有&#xff08;com.sun&#xff09;类库…

pandas 实战:分析三国志人物

简介 背景 Pandas 是 Python 的一个工具库&#xff0c;用于数据分析。由 AQR Capital Management 于 2008 年 4 月开发&#xff0c;2009 年开源&#xff0c;最初被作为金融数据分析工具而开发出来。Pandas 名称来源于 panel data&#xff08;面板数据&#xff09;和 Python d…

基于SEIR模型的传染病预测软件开发(完整代码+数据集+报告)

1 操作页面及用户使用说明(1) 界面说明App页面主要分为4个区域&#xff0c;分别是&#xff1a;曲线显示区、模型初始化和预防参数设定区、传染病特征参数设定区、绘图控制区。① 曲线显示区&#xff1a;显示模型预测的不同人数量随时间的变化曲线。② 模型初始化和预防参数设定…

泛函分析中的向量空间

一、向量空间背景 (1) 具有如下点内积或标量内积的实数域RRR上的欧式空间RNR^NRN&#xff1a; ⟨u,v⟩uTvu0v0u1v1⋯uN−1vN−1∑i0N−1uivi\langle\boldsymbol{u}, \boldsymbol{v}\rangle\boldsymbol{u}^{\mathrm{T}} \boldsymbol{v}u_{0} v_{0}u_{1} v_{1}\cdotsu_{N-1} v_{…