|
# de Bruijn card trick computatorator |
|
# by Christian Perfect |
|
# based on a trick by Persi Diaconis and Ron Graham |
|
|
|
#names of cards |
|
faces = ['King','Ace','2','3','4','5','6','7','8','9','10','Jack','Queen'] |
|
suits = ['Diamonds','Clubs','Hearts','Spades'] |
|
|
|
#generate de bruijn sequence |
|
sequence = [0,0,0,0,0,1] |
|
for i in range(6,64): |
|
sequence.append( (sequence[i-6]+sequence[i-5]) % 2 ) |
|
|
|
#get a string of binary digits representing an integer |
|
def binarise(n,length): |
|
digits=[] |
|
while n>0: |
|
digits.insert(0,int(n%2)) |
|
n=(n-n%2)/2 |
|
digits = [0]*(length-len(digits))+digits #pad to the desired length |
|
return digits |
|
|
|
#decode a string of binary digits to an integer |
|
def debinarise(digits): |
|
n=0 |
|
for i in digits: |
|
n*=2 |
|
n+=i |
|
return n |
|
|
|
#decode a six-digit binary string to a card |
|
def decode_sequence(seq): |
|
number = debinarise(seq[:4]) |
|
suit = debinarise(seq[4:]) |
|
return number,suit |
|
|
|
#turn a card into a six-digit binary string |
|
def encode_card(card): |
|
number,suit = card |
|
return ''.join([str(x) for x in binarise(number,4)+binarise(suit,2)]) |
|
|
|
#display a card's binary encoding and its name |
|
def show_card(card): |
|
number,suit = card |
|
return '%s: %s of %s' % (encode_card(card), faces[number], suits[suit]) |
|
|
|
|
|
# The actual computation! |
|
sequence*=2 #take two copies of the sequence to cope with the cycle at the end |
|
|
|
# Get the ordering of the (64, not all real) cards from the sequence |
|
cards = [decode_sequence(sequence[i:i+6]) for i in range(0,64)] |
|
|
|
# The deck of cards consists of the first 52 |
|
deck = cards[:52] |
|
|
|
# Get the unused cards from the end of the 64-deck which have usable values |
|
castoffs = [(x,y) for (x,y) in cards[52:] if x<13] |
|
# Separate them into red and black |
|
castoffs_red = [(x,y) for (x,y) in castoffs if y%2==0] |
|
castoffs_black = [(x,y) for (x,y) in castoffs if y%2==1] |
|
|
|
# Get the cards from the 52-deck that don't have usable values |
|
toswap = [(x,y) for (x,y) in deck if x>=13] |
|
swaps = {} |
|
|
|
# Match up bad cards in the 52-deck with good cards in the castoffs |
|
for card in toswap: |
|
number,suit = card |
|
b=castoffs_red.pop() if suit%2==0 else castoffs_black.pop() |
|
swaps[card]=b |
|
|
|
# Show the resulting ordering of the real 52-card deck |
|
for i in range(0,52): |
|
digits = sequence[i:i+6] |
|
card = decode_sequence(digits) |
|
if card in swaps.keys(): |
|
card = swaps[card] |
|
print(show_card(card)) |
|
|
|
# Show which bad codes are swapped with which good ones |
|
print('Swaps') |
|
for a,b in swaps.items(): |
|
print('%s -> %s' % (encode_card(a),show_card(b))) |
From what I can see they all (David, Christian, Rob) DeBruijn sequence do NOT wrap (even with the proper substitutions) which means you cannot do multiple cuts with any assurance that it will work and the spectator cannot cut very thin nor very thick. These decks will have 5 cards at the end (or 5 at the beginning depending on your point of view) that will be duplicates or don't work. I tried to fix it but couldn't.
So I wrote my own debruin 52 (wraps) from scratch last month (took a lot of effort and a lot of luck) but the bit breakdown is 24, 28 and has 12 numbers outside the range 1-52 but wraps perfectly. I searched the internet to find another DeBruijn 52 and the only place I could find a true 52 wrapping DeBruijn is in Leo Boudreau's book Spirited Pasteboards lybrary.com under "Heady Stuff" published in 1987.
Leo's bit breakdown is 24, 28 and has 8 numbers outside the range 1-52. Of course neither mine nor Leo's deBruijn sequence has David/Christian's built in scheme but they are solid and wrap perfectly and I highly recommend Leo's DeBruijn 52 over mine because his has fewer values outside 1-52.
Glowball Yelnif.