Problem (W. Fitch Cheney, 1950). A spectator picks 5 cards from a standard 52-card deck and hands them to the assistant, who hides one and shows the other 4 in an order of their choosing. The magician names the hidden card. How?

Answer. Among 5 cards two share a suit (pigeonhole: 4 suits). On a 13-hour clock of ranks, one of those two is at most 6 steps clockwise from the other. Hide that one; show the other first (it gives the suit and the base rank); order the remaining 3 cards to encode the distance ( orders).

FactValue
distance encoding, cards compared by (rank, suit) as low / mid / high1 = LMH, 2 = LHM, 3 = MLH, 4 = MHL, 5 = HLM, 6 = HML
largest deck for which a 5-card trick exists (Kleitman and Kwiatkowski, 1976)
general: cards chosen, shown in orderdeck of up to cards, and no more
explicit strategy for the largest deckhide the card with (); the order of the shown cards encodes the hidden card’s position among the unseen cards
// Classic trick. Card c in 0..51: suit c / 13, rank c % 13. Returns the 4 cards to show, in order.
vector<int> cheneyEncode(vector<int> hand) {
    sort(all(hand));
    for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) if (i != j && hand[i] / 13 == hand[j] / 13) {
        int d = ((hand[j] - hand[i]) % 13 + 13) % 13;   // hide hand[j], d steps after hand[i]
        if (d < 1 || d > 6) continue;
        vector<int> rest; for (int k = 0; k < 5; k++) if (k != i && k != j) rest.push_back(hand[k]);
        static const int ord[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
        vector<int> shown = {hand[i]};
        for (int t = 0; t < 3; t++) shown.push_back(rest[ord[d - 1][t]]);   // rest is sorted: L, M, H
        return shown;
    }
    return {};                                           // impossible: two cards always share a suit
}
int cheneyDecode(const vector<int>& shown) {
    vector<int> rest(shown.begin() + 1, shown.end()), s = rest; sort(all(s));
    int code = 0;                                        // rank of the permutation, 0..5
    static const int ord[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
    for (int p = 0; p < 6; p++) if (rest[0] == s[ord[p][0]] && rest[1] == s[ord[p][1]]) code = p;
    int suit = shown[0] / 13, rank = (shown[0] % 13 + code + 1) % 13;
    return suit * 13 + rank;
}
 
// General trick: deck 0..N-1 with N <= n! + n - 1; hand of n cards; show n - 1 of them in an order.
vector<int> generalEncode(vector<int> hand) {
    int n = sz(hand); sort(all(hand));
    ll sum = 0; for (int c : hand) sum += c;
    int s = int(sum % n), h = hand[s];
    vector<int> shown; for (int i = 0; i < n; i++) if (i != s) shown.push_back(hand[i]);
    ll T = sum - h, r = ((-T) % n + n) % n, q = (h - s - r) / n;   // h - s = q n + r: position among unseen cards
    vector<int> pool = shown, res;                       // write q in the factorial number system
    for (int k = n - 1; k >= 1; k--) {
        ll f = 1; for (int t = 1; t < k; t++) f *= t;
        int idx = int(q / f); q %= f;
        res.push_back(pool[idx]); pool.erase(pool.begin() + idx);
    }
    return res;
}
int generalDecode(const vector<int>& shown) {
    int n = sz(shown) + 1; vector<int> pool = shown; sort(all(pool));
    ll q = 0, T = 0;
    for (int k = 0; k < n - 1; k++) {
        int idx = int(lower_bound(all(pool), shown[k]) - pool.begin());
        ll f = 1; for (int t = 1; t < n - 1 - k; t++) f *= t;
        q += idx * f; pool.erase(pool.begin() + idx); T += shown[k];
    }
    ll pos = q * n + ((-T) % n + n) % n;                 // index among cards not shown
    vector<int> s = shown; sort(all(s));
    int c = int(pos);
    for (int x : s) if (x <= c) c++;                     // skip the shown cards
    return c;
}