1 简述游戏中常用的两种随机算法(下)-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

简述游戏中常用的两种随机算法(下)

jf_78858299 来源:labuladong 作者:labuladong 2023-04-12 11:43 次阅读

先说结论,当你遇到第i个元素时,应该有1/i的概率选择该元素,1 - 1/i的概率保持原有的选择 。看代码容易理解这个思路:

/* 返回链表中一个随机节点的值 */
int getRandom(ListNode head) {
    Random r = new Random();
    int i = 0, res = 0;
    ListNode p = head;
    // while 循环遍历链表
    while (p != null) {
        i++;
        // 生成一个 [0, i) 之间的整数
        // 这个整数等于 0 的概率就是 1/i
        if (0 == r.nextInt(i)) {
            res = p.val;
        }
        p = p.next;
    }
    return res;
}

对于概率算法,代码往往都是很浅显的,但是这种问题的关键在于证明,你的算法为什么是对的?为什么每次以1/i的概率更新结果就可以保证结果是平均随机的?

我们来证明一下,假设总共有n个元素,我们要的随机性无非就是每个元素被选择的概率都是1/n对吧,那么对于第i个元素,它被选择的概率就是:

图片

i个元素被选择的概率是1/i,在第i+1次不被替换的概率是1 - 1/(i+1),在第i+2次不被替换的概率是1 - 1/(i+2),以此类推,相乘的结果是第i个元素最终被选中的概率,也就是1/n。因此,该算法的逻辑是正确的。

同理,如果要在单链表中随机选择k个数,只要在第i个元素处以k/i的概率选择该元素,以1 - k/i的概率保持原有选择即可 。代码如下:

/* 返回链表中 k 个随机节点的值 */
int[] getRandom(ListNode head, int k) {
    Random r = new Random();
    int[] res = new int[k];
    ListNode p = head;

    // 前 k 个元素先默认选上
    for (int i = 0; i < k && p != null; i++) {
        res[i] = p.val;
        p = p.next;
    }

    int i = k;
    // while 循环遍历链表
    while (p != null) {
        i++;
        // 生成一个 [0, i) 之间的整数
        int j = r.nextInt(i);
        // 这个整数小于 k 的概率就是 k/i
        if (j < k) {
            res[j] = p.val;
        }
        p = p.next;
    }
    return res;
}

对于数学证明,和上面区别不大:

图片

虽然每次更新选择的概率增大了k倍,但是选到具体第i个元素的概率还是要乘1/k,也就回到了上一个推导。

类似的,回到扫雷游戏的随机初始化问题,我们可以写一个这样的sample抽样函数:

// 在区间 [lo, hi) 中随机抽取 k 个数字
int[] sample(int lo, int hi, int k) {
    Random r = new Random();
    int[] res = new int[k];

    // 前 k 个元素先默认选上
    for (int i = 0; i < k; i++) {
        res[i] = lo + i;
    }

    int i = k;
    // while 循环遍历数字区间
    while (i < hi - lo) {
        i++;
        // 生成一个 [0, i) 之间的整数
        int j = r.nextInt(i);
        // 这个整数小于 k 的概率就是 k/i
        if (j < k) {
            res[j] = lo + i - 1;
        }
    }
    return res;
}

这个函数能够在一定的区间内随机选择k个数字,确保抽样结果是均匀随机的且只需要 O(N) 的时间复杂度。

蒙特卡洛验证法

上面讲到的洗牌算法和水塘抽样算法都属于随机概率算法,虽然从数学上推导上可以证明算法的思路是正确的,但如果你笔误写出 bug,就会导致概率上的不均等。更神奇的是,力扣的判题机制能够检测出这种概率错误。

那么最后我就来介绍一种方法检测随机算法的正确性:蒙特卡洛方法。我猜测力扣的判题系统也是利用这个方法来判断随机算法的正确性的。

记得高中有道数学题:往一个正方形里面随机打点,这个正方形里紧贴着一个圆,告诉你打点的总数和落在圆里的点的数量,让你计算圆周率。

图片

这其实就是利用了蒙特卡罗方法:当打的点足够多的时候,点的数量就可以近似代表图形的面积。结合面积公式,可以很容易通过正方形和圆中点的数量比值推出圆周率的。

当然,打的点越多,算出的圆周率越准确,充分体现了大力出奇迹的道理。

比如,我们可以这样检验水塘抽样算法sample函数的正确性:

public static void main(String[] args) {
    // 在 [12, 22) 中随机选 3 个数
    int lo = 12, hi = 22, k = 3;
    // 记录每个元素被选中的次数
    int[] count = new int[hi - lo];
    // 重复 10 万次
    int N = 1000000;
    for (int i = 0; i < N; i++) {
        int[] res = sample(lo, hi, k);
        for (int elem : res) {
            // 对随机选取的元素进行记录
            count[elem - lo]++;
        }
    }
    System.out.println(Arrays.toString(count));
}

这段代码的输出如下:

[300821, 299598, 299792, 299198, 299510, 300789, 300022, 300326, 299362, 300582]

当然你可以做更细致的检查,不过粗略看看,各个元素被选中的次数大致是相同的,这个算法实现的应该没啥问题。

对于洗牌算法中的shuffle函数也可以采取类似的验证方法,我们可以跟踪某一个元素x被打乱后的索引位置,如果x落在各个索引的次数基本相同,则说明算法正确,你可以自己尝试实现,我就不贴代码验证了。

拓展延伸

到这里,常见的随机算法就讲完了,简单总结下吧。

洗牌算法主要用于打乱数组,比如我们在 快速排序详解及运用中就用到了洗牌算法保证快速排序的效率。

水塘抽样算法的运用更加广泛,可以在序列中随机选择若干元素,且能保证每个元素被选中的概率均等。

对于这些随机概率算法,我们可以用蒙特卡洛方法检验其正确性。

最后留几个拓展题目:

1、本文开头讲到了将二维数组坐标(x, y)转化成一维数组索引的技巧,那么你是否有办法把三维坐标(x, y, z)转化成一维数组的索引呢?

2、如何对带有权重的样本进行加权随机抽取?比如给你一个数组w,每个元素w[i]代表权重,请你写一个算法,按照权重随机抽取索引。比如w = [1,99],算法抽到索引 0 的概率是 1%,抽到索引 1 的概率是 99%。

3、实现一个生成器类,构造函数传入一个很长的数组,请你实现randomGet方法,每次调用随机返回数组中的一个元素,多次调用不能重复返回相同索引的元素。要求不能对该数组进行任何形式的修改,且操作的时间复杂度是 O(1)

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4607

    浏览量

    92819
  • 游戏
    +关注

    关注

    2

    文章

    742

    浏览量

    26312
  • 随机
    +关注

    关注

    0

    文章

    12

    浏览量

    9733
收藏 人收藏

    评论

    相关推荐

    简述两种示波器测量眼图的差别

    简述两种示波器测量眼图的差别 中心议题: 力科示波器进行眼图测量 新旧款软件包使用方法不同 力科示波器捕获了50MS的数据,并一次性地
    发表于 04-21 16:52 3896次阅读
    <b class='flag-5'>简述</b><b class='flag-5'>两种</b>示波器测量眼图的差别

    两种典型的ADRC算法介绍

    前言  上篇中详细阐述了经典的自抗扰控制算法的原理,本篇将围绕两种ADRC算法展开,针对扩张状态观测器的参数整定问题进行详解,同时,对跟踪微分器的几个重要应用进行介绍。两种典型的ADR
    发表于 09-07 08:02

    智能插座常用两种通信协议是什么?

    智能插座常用两种通信协议是什么?
    发表于 09-26 09:18

    c语言常用算法

    非常实用的《c语言常用算法程序集》针对工程中常用的行之有效的算法而编写,其主要内容包括多项式的计算、复数运算、随机数的产生、矩阵运算、矩阵特
    发表于 04-11 16:41

    网络中常用的队列管理方法比较

    本文主要介绍了网络中常用两种队列管理方法:先进先出(FIFO)和随机提前检测(RED),并且通过实验比较了这两种队列管理方法在解决网络拥塞控制方面的表现,体现了研究
    发表于 05-25 11:24 9次下载

    基于游戏中NPC路径规划的混合算法

    路径规划是游戏人工智能领域的核心问题,如何建立一高效的路径规划方法仍是研究的热点之一。针对游戏中NPC的路径规划问题,将A*算法与改进的人工势场法相结合,提出了一
    发表于 11-14 14:55 7次下载

    帕塞瓦定理的两种常见形式

    帕塞瓦定理的两种常见形式, 在我的《随机信号分析》里面作为附录4, 即帕塞瓦定理的两种常见形式, 第三形式即不常用的形式, 明天再给读者介
    的头像 发表于 04-02 11:13 9819次阅读

    Wincc如何与PLC进行通讯两种常用的方式介绍

    西门子WINCC与SiemensPLC通讯连接有多种方式,下面介绍两种常用的通讯方式。
    的头像 发表于 02-17 09:27 3w次阅读
    Wincc如何与PLC进行通讯<b class='flag-5'>两种</b><b class='flag-5'>常用</b>的方式介绍

    单片机常用两种延时控制方式

    单片机中常用的延时控制方式有两种。一是采用编程的方式达到延时的目的,另一方法则是通过单片机中的个定时器T0和T1进行计时达到延时的目的
    发表于 07-17 10:22 5998次阅读
    单片机<b class='flag-5'>常用</b>的<b class='flag-5'>两种</b>延时控制方式

    常用的hdl语言有哪两种

    Verilog HDL和VHDL是目前两种常用的硬件描述语言,同时也都是IEEE标准化的HDL语言。
    发表于 08-25 09:14 9243次阅读

    说透游戏中常用两种随机算法

    这些 2D 游戏相较现在的大型 3D 游戏虽然看起来有些简陋,但依然用到很多有趣算法技巧,本文就来深入研究一地图的随机生成
    的头像 发表于 11-09 11:17 1107次阅读

    详解PMSM中常用两种坐标变换

    期介绍了Clarke的Park变化的基本原理,但是经过这两种变换后会存在两种系数,相信大家都很迷惑,这是什么原因? 主要原因是存在两种遵循的方式:1、变换前后电流所产生的旋转磁场等
    的头像 发表于 01-19 15:52 2453次阅读
    详解PMSM<b class='flag-5'>中常用</b>的<b class='flag-5'>两种</b>坐标变换

    简述游戏中常用两种随机算法(上)

    没事儿的时候我喜欢玩玩那些经典的 2D 网页小游戏,我发现很多游戏都要涉及地图的随机生成,比如扫雷游戏中地雷的位置应该是随机分布的:
    的头像 发表于 04-12 11:43 693次阅读
    <b class='flag-5'>简述</b><b class='flag-5'>游戏中常用</b>的<b class='flag-5'>两种</b><b class='flag-5'>随机</b><b class='flag-5'>算法</b>(上)

    基于Python实现随机森林算法

    机器学习算法是数据挖掘、数据能力分析和数学建模必不可少的一部分,而随机森林算法和决策树算法是其中较为常用
    的头像 发表于 09-21 11:17 1196次阅读
    基于Python实现<b class='flag-5'>随机</b>森林<b class='flag-5'>算法</b>

    PCBA加工中常见的两种焊接方式详解

    ,在PCBA行业中经常被使用。接下来深圳PCBA加工厂家为大家详细介绍PCBA加工手工焊接的两种方式,为您揭秘行业内的技术细节。 PCBA加工过程中常用焊接方式 第一方式是传统手工焊接。这种方式主要依靠技术工人的手动操作进行焊
    的头像 发表于 06-14 09:18 530次阅读