为了账号安全,请及时绑定邮箱和手机立即绑定

Shuffle-洗牌算法

顾名思义:Shuffle算法,叫做洗牌算法,它的目标正好与各种的sort算法相反,即把一个有序(或者无序)的一系列元素打乱。

举个两例子

1、大家都知道扑克牌,我们每次都需要在摸牌之前把牌洗掉,用来让每个人摸到每张牌的概率尽量相等,增加游戏的随机性和乐趣;

2、还有音频播放器,有一些人不喜欢顺序播放,而喜欢使用随机播放(其实随机播放分为两种,random和shuffle,后文会介绍到),比如iPod Shuffle的卖点之一就是“你永远不知道你将要听到的下一首歌曲是什么”。

至少,如果要模拟扑克牌游戏,或者做音频播放器,都要使用shuffle算法,而二者的shuffle算法却有一些区别,一个是一次性的洗牌,另一个则是每次取一首歌。那么怎么实现他们呢?

<?
//SortHelper排序帮助类,这里只截取一个在下文用到的函数
class SortHelper{
    //交换两个数组元素
    public static function swap(&$arr, $l, $j)
    {
        $temp = $arr[$l];
        $arr[$l] = $arr[$j];
        $arr[$j] = $temp;
    }
}

ShuffleExp洗牌算法

/**
 * ShuffleExp洗牌算法
 * 洗牌算法实际上就是常见的随机问题。我们可以抽象理解为:得到一个M以内的所有自然数的随机顺序数组。
 * 好的洗牌算法保证每一次的概率都基本一样
 * 
 */
class ShuffleExpRun{

    public $N;
    public $n;
    public $m;

    public function __construct($N, $n, $m)
    {
        $this->N = $N;
        $this->n = $n;
        $this->m = $m;
    }

    /**
     * 洗牌算法 - 自证函数主入口
     * 
     * 构建数据
     * 计算概率
     * 输出概率值
     */
    function ShuffleExpRun()
    {

        $freq = $arr = [];
        for ($i = 0; $i < $this->N; $i++) { 
            //构建数据
            $this->reset($arr);

            //洗牌算法
            $this->shuffle($arr);

            //统计每一位的总数量
            for ($j = 0; $j < $this->n; $j++) { 
               $freq[$j] += $arr[$j];
            }
        }
        //计算概率并输出
        for ($i=0; $i < $this->n; $i++) { 
            echo $i. ":". floatval($freq[$i] / $this->N) . "\n";
        }
    }

    /**
     * 构建数据
     */
    public function reset(&$arr)
    {
        //默认
        for ($i = 0; $i < $this->m; $i++) { 
            $arr[$i] = 1;
        }
        for ($i = $this->m; $i < $this->n; $i++) { 
            $arr[$i] = 0;
        }
    }

    /**
     * 洗牌算法 - 可单独使用此函数 - 放到这里是为了自证此函数真的是等概率算法
     * 从后往前排
     * 总数组长度 n, 初始化用 n - 1(因为数组索引从0开始)
     * 每次从 [0, i+1) 区间里随机选择一个元素和 i 位置的进行交换
     */
    public function shuffle(&$arr)
    {
        for ($i = $this->n - 1; $i >= 0; $i--) {
            //从 [0, i+1) 区间里随机选择一个元素
            $x = intval($this->randomFloat() * ($i+1));
            SortHelper::swap($arr, $i, $x);
        }
    }

    /**
     * 辅助函数
     * 生成 min - max 之间的随机数(float)带小数
     * 如果都不传,默认生成 0 - 1 之间的随机数
     * 
     */
    function randomFloat($min = 0, $max = 1) {
        return $min + mt_rand() / mt_getrandmax() * ($max - $min);
    }
    
}

$shuff = new ShuffleExpRun(100000,10,5);
$shuff->ShuffleExpRun();

概率分布

执行后可以看到概率分布

图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
软件工程师
手记
粉丝
19
获赞与收藏
134

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消