Enigmatic Code

Programming Enigma Puzzles

Enigma 313: The 21 card trick

From New Scientist #1461, 20th June 1985 [link]

I showed my son the famous 21 card trick. I used 21 cards numbered from 1 to 21, which remained face upwards throughout the trick. I started with them in numerical order in a pile, 1 being on the top. I dealt them into three piles (1 on to the first, 2 on to the second, 3 on to the third, 4 on to the first again, and so on) and asked my son to choose one of the numbers without telling me which. Then he had to tell me which pile his number was in.

I then picked up the piles, placing his chosen pile in the middle of the three. With this new pile I started all over again and repeated the performance twice more, my son pointing out the pile which contained his chosen number each time. After the third and final collecting up of the cards his chosen card was (of course) in 11th place.

He then tried it on me, but he didn’t know the trick. He started with the cards in order as I had done, I chose a number, he went through the three deals (but collecting up the piles in his own fashion) and, not surprisingly, the trick went wrong.

The number I had chosen ended up in the 10th place, not the 11th. The number in the 11th place was in fact half my number. And the number which was twice mine ended up in the same place in the pile as it had started in.

What was my chosen number?



3 responses to “Enigma 313: The 21 card trick

  1. Jim Randell 2 October 2015 at 9:31 am

    This Python program examines all the possible orders that the piles can be picked up in. It runs in 37ms.

    from itertools import permutations, product
    from enigma import irange, reduce, printf
    # initial cards
    cards0 = list(irange(1, 21))
    # deal out the cards, and then pick up the piles
    # cards - the list of 21 cards
    # pick - a permutation of (0, 1, 2) to recombine the piles
    def deal(cards, pick):
      # make the piles
      piles = (cards[-3::-3], cards[-2::-3], cards[-1::-3])
      # recombine them in the specified way
      return piles[pick[0]] + piles[pick[1]] + piles[pick[2]]
    # choose three ways of collecting the piles
    for ps in product(permutations((0, 1, 2)), repeat=3):
      # produce the final order of the cards
      cards = reduce(deal, ps, cards0)
      # the number half mine is at position 11
      # it must be between 1 and 5
      h = cards[10]
      if not(0 < h < 6): continue
      # and my number (= 2h) is at position 10
      n = 2 * h
      if cards[9] != n: continue
      # and 2n is in position 2n
      t = 2 * n
      if cards[t - 1] != t: continue
      printf("n={n} cards={cards}")

    Solution: Your chosen number was 10.

    On the first deal the “10” card would appear in pile 1, and the piles are collected as pile 1 + pile 2 + pile 3.

    On the second deal the “10” card would appear in pile 1, and the piles are collected as pile 2 + pile 1 + pile 3.

    On the third deal the “10” card would appear in pile 1, and the piles are collected as pile 3 + pile 1 + pile 2.

    The final order of the cards is: (13, 8, 3, 1, 18, 7, 2, 17, 12, 10, 5, 16, 11, 6, 4, 21, 19, 14, 9, 20, 15), with the chosen “10” card at position 10, the “5” card at position 11 and the “20” card at position 20.

    • Brian Gladman 3 October 2015 at 12:04 pm
      from itertools import permutations
      # perform the card shuffling process
      def process(t, r=0):
        # have we completed three rounds?
        if r == 3:
          yield t
          # split the pack into the three piles and permute their order
          for p in permutations([t[i-3::-3] for i in range(3)]):
            # recombine the reordered piles and continue to the next round
            yield from process(sum((list(x) for x in p), []), r + 1)
      # consider each possible sequence generated by the process
      for seq in process(list(range(1, 22))):
        # try each possible even number such that 2 * nbr <= 21
        for nbr in range(2, 12, 2):
          # look for sequences with this number in position ten, half this number
          # in position eleven and twice this number in its original position
          if seq[9] == 2 * seq[10] == nbr and seq.index(2 * nbr) + 1 == 2 * nbr:
            print('The number was {} {}'.format(nbr, seq))
      • Jim Randell 3 October 2015 at 5:28 pm

        I don’t think you need to bother with the loop at line 17. Something like this would do:

          n = seq[9]
          if 0 < n < 12 and n % 2 == 0 and n == 2 * seq[10] and seq[2 * n - 1] == 2 * n:
            print("The number was {n} {seq}".format(n=n, seq=seq))

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: