设为首页 加入收藏

TOP

洗牌算法及 random 中 shuffle 方法和 sample 方法浅析(二)
2019-06-18 12:07:57 】 浏览:165
Tags:洗牌 算法 random shuffle 方法 sample 浅析
# invariant:  non-selected at [0,n-i)
        j = randbelow(n-i)
        result[i] = pool[j]
        pool[j] = pool[n-i-1]   # move non-selected item into vacancy
else:
    selected = set()
    selected_add = selected.add
    for i in range(k):
        j = randbelow(n)
        while j in selected:
            j = randbelow(n)
        selected_add(j)
        result[i] = population[j]
return result

setsize 变量虽然看得一头雾水,但是下面的 ifelse 部分还是能看懂的。if 里是洗牌算法,而 else 里是那个却是我看着很 “low” 记录已选项算法。

这是怎么回事?为了弄明白其中的道理,我去搜了很多文章查看,最有价值的是下面这篇:https://blog.csdn.net/harry_128/article/details/81011739

随机取样有两种实现方式,一是随机抽取且不放回,就是洗牌算法;二是随机抽取且放回,就是我想到的记录已选项算法。random.sample 根据条件选择其中之一执行。那么就是说,洗牌算法和记录已选项算法之间是各有优劣的。这让我有点惊讶,不明摆着洗牌算法更优吗?

首先,这个抽样算法肯定不能改变原序列的顺序,而洗牌算法是会改变序列顺序的,所以只能使用序列的副本,代码中也是这么做的 pool = list(population) 创建副本,而记录已选项算法是不会改变原序列顺序的,所以无需创建副本。创建副本也需要消耗时间和空间,算法自然也是要把这考虑进去的。当需要取的样本数量 K 相较于样本总体数量 N 较小时,随机取到重复值的概率也就相对较小。

sample 是依据什么来判断应该用哪个算法的呢?源码中的判断基于 setsize 变量,其中还有一段让人看不懂的公式。其实这是在计算 set 所需的内存开销,算法的实现主要考虑的是额外使用的内存,如果 list 拷贝原序列内存占用少,那么用洗牌算法;如果 set 占用内存少,那么使用记录已选项算法。

What?居然是根据额外占用内存多少来判断?这有点太不可思议了。Why?

我们来看一下算法的时间复杂度。对于算法很渣渣的小伙伴(例如我)来说,计算算法的时间复杂度也是件挺困难的事,为了简单起见,我用一种简单的方式来说明。

先说洗牌算法,时间复杂度是 O(K),这个比较好理解。那么,对于记录已选项算法,时间复杂度是 O(NlogN)。这个别问我是怎么算出来的,我没算,抄的。有兴趣的小伙伴可以自行去计算一下。

我们来想一个简单的,对于记录已选项算法,如果每次选取的值恰好都没有重复,那么时间复杂度是多少呢?很显然是 O(K)。那么当 K 远小于 N 的时候,我们可以认为时间复杂度就是 O(K)。

sample 算法的思想就是,当 K 较 N 相对较小时,两种算法的时间复杂度都是 O(K),则选用占用内存较小的;当 K 较 N 相对较接近时,记录已选项算法的时间复杂度就会高于 O(K),这时就选用洗牌算法。

只得感叹算法真的博大精深。

首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇【经验分享】给初学者的建议!零.. 下一篇Python 安装第三方库,pip instal..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目