zoukankan      html  css  js  c++  java
  • 算法之洗牌算法(随机置乱算法)

    排序算法用于将一个序列变成有序的,而洗牌算法则用于将一个序列打“乱”,可以认为是排序算法相反操作。洗牌算法需要借助随机数实现来打“乱”序列。

    什么才是“真的乱”

    洗牌算法正确性的判断准则(“乱”的判断依据):对于包含n个元素的序列,其全排列有n!种可能。故若序列打乱的结果有n!种且每种出现的概率一样,则是正确的洗牌算法。因打乱结果的种数肯定不大于n!,故反例有两种情况:

    打乱结果的种数小于n!:显然此时全排列中的某些结果无法由洗牌算法产生,故此时的洗牌算法不对;

    打乱结果的种数等于n!但每种的出现的概率不一样

    怎样做到“真的乱”

    洗牌算法代码通过随机获取元素并交换来产生随机结果。

    原理:将数组分为已打乱和未打乱的前后两部分(初始时两者分别由0、n个元素),每次随机从未打乱部分中选择一个元素加入到已打乱部分中。易得种数为 n! 。

    代码:四种写法本质上一样。

    // 得到一个在闭区间 [min, max] 内的随机整数
    int randInt(int min, int max);
    
    // 第一种写法
    void shuffle(int[] arr) {
        int n = arr.length();
        /******** 区别只有这两行 ********/
        for (int i = 0 ; i < n; i++) {
            // 从 i 到最后随机选一个元素
            int rand = randInt(i, n - 1);
            /*************************/
            swap(arr[i], arr[rand]);
        }
    }
    
    // 第二种写法
        for (int i = 0 ; i < n - 1; i++)
            int rand = randInt(i, n - 1);
    
    // 第三种写法
        for (int i = n - 1 ; i >= 0; i--)
            int rand = randInt(0, i);
    
    // 第四种写法
        for (int i = n - 1 ; i > 0; i--)
            int rand = randInt(0, i);
    View Code

    反例:每次都从整个数组中随机选一个元素交换到首位,进行n轮。虽然结果种数也为n!,但由于每轮都有n种选择故总结果数为nn(包含重复的结果),由于nn>n!且前者不能被后者整除故有些排列出现的概率会比较大,从而不是正确的洗牌算法。

    如何验证“真的乱”

    可以用蒙特卡洛方法来验证各种结果出现的频率是否大致一样,若大致一样则说明洗牌算法是正确的。

    法1:给定一个数组如{1,2,3,4,5},进行N次(N最好大些)实验:以该数组为参数调用洗牌算法,记录每次洗牌后的结果并统计。若结果数为n!且各结果出现的频数大致一样则说明正确。

    法2:与上面类似,只不过数组中除了一个元素为1外其他元素为0。每次实验只需记录洗牌后1的位置,最后看1出现的位置是否大致一样。

    参考资料

    https://github.com/labuladong/fucking-algorithm/blob/master/算法思维系列/洗牌算法.md

  • 相关阅读:
    hdp3.1 hive 3.0的使用记录
    大数据相关的面试题(摘自网络)hbase,kafka,spark
    记一次ZOOKEEPER集群超时问题分析
    git 常用的撤销操作
    角色权限管理与数据权限管理
    spark examples 导入idea并测试
    spark restful 作业提交
    Redhat 7.4更新为Centos7的yum并安装docker-ce
    提醒,处理字符串和文件的时候一定要注意编码
    spark 作业提交
  • 原文地址:https://www.cnblogs.com/z-sm/p/12393211.html
Copyright © 2011-2022 走看看