このごろ他のことが忙しかったので久々の投稿です.
ギルブレスシャッフル
52枚のトランプカードを,例えばダイヤ・クローバー・ハート・スペード・ダイヤ・クローバー… のように4種のスートが繰り返されるように並べて重ねる.
束の上から一枚とって裏向きのまま机の上に重ねることを何回か(10~30枚程度)繰り返す.
できた二つの束をリフルシャッフル(マジシャンがよくやる,二つの束を交互に重ねるシャッフル)すると,
一見スートの並びはランダムに見えるが,その実4枚ごとに4種類のスートを必ず含むような並びになる.
……というもの.
もちろん,手元にトランプがあればそれで試すことが出来ますが,スートが繰り返されるようにトランプを並べ替えるのが面倒なので,退屈なことはPythonにやらせます.
Pythonで実装
まずはトランプを用意します.0~51の52個の数字をそれぞれ4で割った余りをリストに入れて,0,1,2,3をそれぞれスートに対応させることにしましょう.
余りというのは周期性があるので,この時点で既にスートが繰り返されるような束ができているわけです.素晴らしい.
#52枚
cards=list(range(52))
#4つのスート
cards=[i % 4 for i in cards]
続いて,適当な枚数分束の上から一枚ずつ取って重ねていき別の束を作ります.
あまり極端な枚数だと面白くないので,10~30あたりの数字をランダムに出し,その数字の位置でトランプを二分割します.
新しくできた束は順番が逆になるのでreversedをかけます.
import random
rand=random.randrange(10,30)
#print("rand:"+str(rand))
cards_taken = cards[:rand] #新しく作られる束
cards_remain = cards[rand:] #残る束
#逆向きに重ねられていくのでreversed
cards_taken = list(reversed(cards_taken))
いよいよ二つの束をリフルシャッフルします.
二つの束は基本的に枚数が異なるため,交互に入れられるのは少ない方の束の枚数分だけということになります.
実際にリフルシャッフルする様子を思い浮かべてみましょう.
まずどちらかの束がなくなるまで,下から交互に束が混ざっていきます.
そして,余った方の束が上に重なり,シャッフルが終了します.
これをコードに落とし込むことを考えます.
二つの束のうち,少ない方の枚数分だけ二つの束(リスト)からpopし「交互に混ざる部分」を作ります.
そして,残ったカードは「混ざらない余り」となります.
現実世界でシャッフルを行うと
【上】
混ざらない余り
交互に混ざる部分
【下】
といった具合になります.
リストの一番左をトランプの一番上に対応付けることを考えると
「混ざらない余り」に「交互に混ざる部分」をextendで加えることになります.
最後に「交互に混ざる部分」をreversedしていますが,これはpopで束を後ろから取ってきているためです.
ちなみに,extendの際どちらの束が残っているか確認する必要はありません.すでに片方は空だからです.
riffle=[]
#リフルシャッフル
for i in range(min(len(cards_remain), len(cards_taken))):
riffle.append(cards_remain.pop())
riffle.append(cards_taken.pop())
#混ざる部分と混ざらない部分を合体
output = []
output.extend(cards_remain)
output.extend(cards_taken)
output.extend(list(reversed(riffle)))
最後に,outputを見てみましょう.
print(output)
#[1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 3, 3, 0, 2, 1, 1, 2, 0, 3, 3, 0, 2, 1, 1, 2, 0, 3, 3, 0, 2, 1, 1, 2, 0, 3, 3, 0, 2, 1, 1, 2, 0, 3, 3, 0, 2, 1, 1, 2, 0, 3]
確かに,4つごとに0~3が一回ずつ現れていますね.
また,4つごとの塊に着目すると,何種類かのパターンが何回か現れていますね.
面白いですね.
#さいごに
ギルブレスシャッフルについては数か月前,アニメで知りました.
当時コード書いてこうなることを確認してそのままにしていたので今更ですが上げてみました.