如何验è¯æ··æ´—算法是å¦ç»Ÿä¸€ï¼Ÿ

时间:2018-05-30 16:08:11

标签: python algorithm shuffle

我有Knuth's shuffling algorithm的简å•Python实现:

def knuth_shuffle(ar):
    num = len(ar)
    for i in range(num):
        index = random.randint(0, i)
        ar[i], ar[index] = ar[index], ar[i]
    return ar

如何测试(使用scipy或其他任何包)洗牌确实是统一的?我找到了几个相关的帖å­ï¼ˆ1,2),但他们没有回答我的问题。一般æ¥è¯´ï¼Œäº†è§£å¦‚何执行此类检查会很棒。

3 个答案:

答案 0 :(得分:2)

编辑:

作为评论中的Paul Hankin,我的原始测试åªæ£€æŸ¥äº†æ¯ä¸ªå…ƒç´ è½å…¥æ¯ä¸ªä½ç½®çš„概率,而ä¸æ˜¯æ‰€æœ‰æŽ’列都是åŒç­‰å¯èƒ½çš„,这是一个更强烈的è¦æ±‚。下é¢çš„代ç æ®µè®¡ç®—æ¯ä¸ªæŽ’列的频率,这是我们应该看到的:

import math
import random

def knuth_shuffle(ar):
    num = len(ar)
    for i in range(num):
        index = random.randint(0, i)
        ar[i], ar[index] = ar[index], ar[i]
    return ar

# This function computes a unique index for a given permutation
# Adapted from https://www.jaapsch.net/puzzles/compindx.htm#perm
def permutation_index(permutation):
    n = len(permutation)
    t = 0
    for i in range(n):
      t = t * (n - i)
      for j in range(i + 1, n):
        if permutation[i] > permutation[j]:
            t += 1
    return t

N = 6  # Test list size
T = 1000  # Trials / number of permutations

random.seed(100)
n_perm = math.factorial(N)
trials = T * n_perm
ar = list(range(N))
freq = [0] * n_perm
for _ in range(trials):
    ar_shuffle = ar.copy()
    knuth_shuffle(ar_shuffle)
    freq[permutation_index(ar_shuffle)] += 1

如果éšæœºæ’­æ”¾æ˜¯ç»Ÿä¸€çš„,则生æˆçš„freqå‘é‡çš„值应根æ®å…·æœ‰T * N!次试验的二项分布和æˆåŠŸæ¦‚率1 / (N!)进行分é…。以下是上一个示例(使用Seaborn完æˆï¼‰çš„分布估计图,其中频率值应为1000å·¦å³ï¼š

Permutation frequency distribution

我认为看起æ¥å¥½ä½†æ˜¯ï¼Œå¯¹äºŽå®šé‡ç»“果,您需è¦è¿›è¡Œæ›´æ·±å…¥çš„统计分æžï¼Œä¾‹å¦‚Pearson's chi-squared test建议的David Eisenstat。

原始答案:

我将在这里æ出一些基本的想法,但我没有最强的统计背景,所以有人å¯èƒ½æƒ³è¦è¡¥å……或纠正任何错误。

您å¯ä»¥å°†æ¯ä¸ªå€¼çš„频率矩阵放入æ¯ä¸ªä½ç½®è¿›è¡Œå¤šæ¬¡è¯•éªŒï¼š

def knuth_shuffle(ar):
    num = len(ar)
    for i in range(num):
        index = random.randint(0, i)
        ar[i], ar[index] = ar[index], ar[i]
    return ar

N = 100  # Test list size
T = 10000  # Number of trials
ar = list(range(N))
freq = [[0] * N for _ in range(N)]

for _ in range(T):
    ar_shuffle = ar.copy()
    kunth_shuffle(ar_shuffle)
    for i, j in enumerate(ar_shuffle):
        freq[i][j] += 1

一旦你能åšåˆ°è¿™ä¸€ç‚¹ï¼Œä½ å¯ä»¥é‡‡å–几ç§æ–¹æ³•ã€‚一个简å•çš„想法是,如果洗牌是统一的,freq / T应倾å‘于1 / N,因为T倾å‘于无穷大。所以你å¯ä»¥ä½¿ç”¨ä¸€ä¸ªéžå¸¸å¤§çš„"值T并看到这些值足够接近"。或者检查freq / T - 1 / N的标准å差是å¦è¶³å¤Ÿå°ï¼†ï¼ƒ34;。

这些"足够接近"并且"足够å°ï¼†ï¼ƒ34;虽然ä¸æ˜¯å¾ˆæ‰Žå®žçš„概念。基础分æžéœ€è¦æ›´å¤šç»Ÿè®¡å·¥å…·ã€‚我认为您需è¦test the hipothesisæ¯ä¸ªé¢‘率值都æ¥è‡ªbinomial distribution T次试验1 / NæˆåŠŸæ¦‚率。正如我所说,没有完整解释的背景,这å¯èƒ½ä¸é€‚åˆå®ƒï¼Œä½†å¦‚果你真的需è¦å½»åº•çš„分æžï¼Œä½ å¯ä»¥é˜…读这个主题。

答案 1 :(得分:2)

您å¯ä»¥é€šè¿‡å°†æ‰€æœ‰å¯èƒ½çš„éšæœºæ•°åºåˆ—注入knuth_shuffleæ¥æ£€æŸ¥è¿™ä¸€ç‚¹ï¼Œç„¶åŽéªŒè¯æ‚¨æ˜¯å¦åªéœ€å‡†å¤‡ä¸€æ¬¡ã€‚

此代ç æ‰§è¡Œæ­¤æ“作:

import collections
import itertools
import random

def knuth_shuffle(ar, R=random.randint):
    num = len(ar)
    for i in range(num):
        index = R(0, i)
        ar[i], ar[index] = ar[index], ar[i]
    return ar

def fact(i):
    r = 1
    while i > 1:
        r *= i
        i -= 1
    return r

def all_random_seqs(N):
    for r in range(fact(N)):
        seq = []
        for i in range(N):
            seq.append(r % (i+1))
            r //= (i+1)
        it = iter(seq)
        yield lambda x, y: next(it)

for N in range(1, 6):
    print N
    results = collections.Counter()
    for R in all_random_seqs(N):
        a = list('ABCDEFG'[:N])
        knuth_shuffle(a, R)
        results[''.join(a)] += 1
    print 'checking...'
    if len(results) != fact(N):
        print 'N=%d. Not enough results. %s' % (N, results)
    if any(c > 1 for c in results.itervalues()):
        print 'N=%d. Not all permutations unique. %s' % (N, results)
    if any(sorted(c) != list('ABCDEFG'[:N]) for c in results.iterkeys()):
        print 'N=%d. Some permutations are illegal. %s' % (N, results)

此代ç æ£€æŸ¥å¤§å°ä¸º1,2,3,4,5的输入列表的确切正确性。您å¯ä»¥åœ¨N之å‰å†è¿œä¸€ç‚¹ï¼å¤ªå¤§äº†ã€‚

您还希望使用random.randint对代ç ç‰ˆæœ¬æ‰§è¡Œå®Œæ•´æ€§æ£€æŸ¥ï¼ˆä¾‹å¦‚,生æˆ500次'ABCD',并确ä¿æ¯æ¬¡æŽ’列至少一次。)

答案 2 :(得分:0)

如果您从给定的固定订å•ä¸­éšæœºæ´—牌相åŒçš„商å“,则éšæœºå•†å“中一个固定ä½ç½®ä¸­æ¯ä¸ªå•†å“çš„æ•°é‡åº”该趋å‘于相åŒçš„值。

下é¢æˆ‘将列表0..9洗牌几次并打å°è¾“出:

from random import shuffle  # Uses Fischer-Yates

tries = 1_000_000
intcount = 10
first_position_counts = {n:0 for n in ints}
ints = range(intcount)
for _ in range(tries):
    lst = list(ints)   # [0, 1, ...9] In that order
    shuffle(lst)
    first_position_counts[lst[0]] += 1

print(f'{tries} shuffles of the ints 0..{intcount-1} should have each int \n',
      'appear in the first position {tries/intcount} times.')
for item in first_position_counts.items():
    print(' %i: %5i' % item)

è¿è¡Œä¸€æ¬¡ï¼Œä½ å¯èƒ½å¾—到类似的东西:

 0: 99947
 1: 100522
 2: 99828
 3: 100123
 4: 99582
 5: 99635
 6: 99991
 7: 100108
 8: 100172
 9: 100092

å†æ¬¡ï¼š

 0: 100049
 1: 99918
 2: 100053
 3: 100285
 4: 100293
 5: 100034
 6: 99861
 7: 99584
 8: 100055
 9: 99868

现在,如果你有数以åƒè®¡çš„项目è¦æ´—牌,那么它们应该以{{1​​}}排列之一结æŸï¼Œä½†æ˜¯n!å˜å¤§ï¼Œå¼ºå¤§;如果它是“å¯æ¯”较的â€ï¼Œè‚¯å®šæ¯”ä½ çš„éšæœºæ•°å‘生器的å¯èƒ½èŒƒå›´æ›´å¤§ï¼Œé‚£ä¹ˆå®ƒå°±ä¼šå´©æºƒã€‚