8

Python3を使用する場合、キーワード引数random.shuffleの関数を明示的に送信すると、実行時間が約半分になるリストのシャッフルが必要になることに気付きました。Python2にも同じ問題があるかどうかを確認しましたが、Python3でのみ発生することがわかりました。random.randomrandom

次のコードを使用して、2つのバージョンの実行時間を測定します。

from timeit import Timer
t1 = Timer("random.shuffle(l)", "import random; l = list(range(100000))")
t2 = Timer("random.shuffle(l, random = random.random)", "import random; l = list(range(100000))")
print("With default rand: %s" % t1.repeat(10,1))
print("With custom rand: %s" % t2.repeat(10,1))

ideoneでテストケースを作成し、Python3と同じコードをPython2で確認できるようにしました。

シャッフルのドキュメントによるとrandom.random、オプションのキーワード引数を省略した場合、デフォルトの場合と同じ関数が使用されるため、デフォルトの場合randomと同じ関数を使用して乱数を生成しても違いはありません。

shuffleフォルダ内の関数のそれぞれのソース(Python2とPython3)を確認したところ、キーワードの関数を使用してPython3バージョンを明示的に呼び出すとLib/random.py、同じように動作することがわかりましrandom。この引数を省略すると、Python3はヘルパー関数を使用する_randbelowため、問題の原因が存在するはずです。_randbelow速度が低下するため、Python3がなぜ使用するのかわかりませんshuffle。私が理解している限り、その利点は任意の大きな乱数を生成することにありますが、2 ^ 32要素(私の場合は100000)よりもはるかに少ないリストのシャッフルを遅くすることはありません。

Python3を使用すると、ランタイムがより近くなるはずなのに、なぜランタイムにこのような違いが見られるのか、誰かに説明してもらえますか?

PS:Python2でのランタイムがPython3よりも優れている理由には興味がないことに注意してください。ただし、Python3で引数rand=rand.rand引数を使用する場合とPython3のみで使用しない場合のランタイムの違いです。

4

1 に答える 1

4

関数のdocstringがrandom.shuffleコードと矛盾しています。Python 2.7.2+では、docstringは正しいです:

    def shuffle(self, x, random=None, int=int):
    """x, random=random.random -> shuffle list x in place; return None.

    Optional arg random is a 0-argument function returning a random
    float in [0.0, 1.0); by default, the standard random.random.
    """

    if random is None:
        random = self.random
    for i in reversed(xrange(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = int(random() * (i+1))
        x[i], x[j] = x[j], x[i]

しかし、Python 3.2では、次のことがわかります。

def shuffle(self, x, random=None, int=int):
    """x, random=random.random -> shuffle list x in place; return None.

    Optional arg random is a 0-argument function returning a random
    float in [0.0, 1.0); by default, the standard random.random.
    """

    randbelow = self._randbelow
    for i in reversed(range(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = randbelow(i+1) if random is None else int(random() * (i+1))
        x[i], x[j] = x[j], x[i]

したがって、docstringはまだ古い話を伝えていますが、現在使用されているデフォルトの関数はrandom.randbelowです。

于 2013-02-27T19:13:56.133 に答える