顾名思义: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 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦