洗牌问题

Posted sinlearn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洗牌问题相关的知识,希望对你有一定的参考价值。

题目内容

题目: 手里面n张不同牌面的牌,编写一个洗牌程序,让随机取出一张牌的概率相同。

要求: 说明算法思路、分析时间复杂度、用Array编写洗牌程序、编写测试用例。

算法思路

技术图片

时间复杂度


??时间复杂度应该为:O(n)

实现程序

下面给出4种实现方法、比较各种方法的好坏,其中shuffle_best(cards)是上面算法分析的实现。

import random
def shuffle_system(cards):
    random.shuffle(cards)  # 官方给你的shuffle函数
import random
def shuffle_1st(cards):
    for k in range(len(cards)):
        i = random.randint(0,len(cards)-1) # 随机取出一张
        j = random.randint(0,len(cards)-1) # 随机取出一张
        cards[i], cards[j] = cards[j], cards[i] # 两张交换
import random
def shuffle_2nd(cards):
    for k in range(len(cards)):
        i = random.randint(0,len(cards)-1) # 随机取出一张
        cards[i], cards[k] = cards[k], cards[i] # 两张交换
    

最好的解法:
?? ??randomi = ?i + random.randint(0,(len(cards) - i -1)) ,此处 i +保证了前面放好的了牌不会被取到。

import random
def shuffle_best(cards):
    for i in range(len(cards)):  
        randomi =  i + random.randint(0,(len(cards) - i -1)) # 从剩下的随机取出一张
        cards[i], cards[randomi] = cards[randomi], cards[i] # 依次取出的和从剩余中随机取出的交换
   

测试用例

def test_shuffle(f):
    result = [[0 for i in range(0,10)] for j in range(0,10)]
    
    for i in range(10000):
        A = [i for i in range(0,10)]
        f(A)
        for j in range(len(A)):
            result[A[j]][j] += 1 # 第j张牌在第j个位置
    print("
".join([‘‘.join([‘{:6}‘.format(item) for item in row])for row in result]))

技术图片
用官方给出的shuffle接口,每张牌出现的概率大都为1/10,符合要求。
技术图片
第一种洗牌程序,主对角线上的概率为2/10,不是很理想。
技术图片
第二种洗牌程序,主对角线下面的元素也不是很理想,我们想要的是都在1000次左右。
技术图片
第三种洗牌程序,每张牌在每个位置出现的位置的次数都接近1000,相比1st 和 2nd 的情况要好一点,是我们想要。


以上是关于洗牌问题的主要内容,如果未能解决你的问题,请参考以下文章

洗牌算法

我怎样才能有效地洗牌?

代码实现:模拟斗地主洗牌和发牌并对牌进行排序的代码实现

玩物丧志(天凤麻雀洗牌代码)

程序员的算法趣题Q50: 完美洗牌

游戏常用算法-洗牌算法