Perfect shuffle

From Rosetta Code
Revision as of 00:09, 20 November 2016 by Hout (talk | contribs) (→‎{{header|JavaScript}}: ES6)
Task
Perfect shuffle
You are encouraged to solve this task according to the task description, using any language you may know.

A perfect shuffle (or faro/weave shuffle) means splitting a deck of cards into equal halves, and perfectly interleaving them - so that you end up with the first card from the left half, followed by the first card from the right half, and so on:

7♠ 8♠ 9♠ J♠ Q♠ K♠
7♠  8♠  9♠
  J♠  Q♠  K♠
7♠ J♠ 8♠ Q♠ 9♠ K♠

When you repeatedly perform perfect shuffles on an even-sized deck of unique cards, it will at some point arrive back at its original order. How many shuffles this takes, depends solely on the number of cards in the deck - for example for a deck of eight cards it takes three shuffles:

original:

1 2 3 4 5 6 7 8

after 1st shuffle:

1 5 2 6 3 7 4 8

after 2nd shuffle:

1 3 5 7 2 4 6 8

after 3rd shuffle:

1 2 3 4 5 6 7 8

The Task

  1. Write a function that can perform a perfect shuffle on an even-sized list of values.
  2. Call this function repeatedly to count how many shuffles are needed to get a deck back to its original order, for each of the deck sizes listed under "Test Cases" below.
    • You can use a list of numbers (or anything else that's convenient) to represent a deck; just make sure that all "cards" are unique within each deck.
    • Print out the resulting shuffle counts, to demonstrate that your program passes the test-cases.

Test Cases

input (deck size) output (number of shuffles required)
8 3
24 11
52 8
100 30
1020 1018
1024 10
10000 300

ALGOL 68

<lang algol68># returns an array of the specified length, initialised to an ascending sequence of integers # OP DECK = ( INT length )[]INT:

    BEGIN
        [ 1 : length ]INT result;
        FOR i TO UPB result DO result[ i ] := i OD;
       result
    END # DECK # ;
  1. in-place shuffles the deck as per the task requirements #
  2. LWB deck is assumed to be 1 #

PROC shuffle = ( REF[]INT deck )VOID:

    BEGIN
        [ 1 : UPB deck ]INT result;
        INT left pos  := 1;
        INT right pos := ( UPB deck OVER 2 ) + 1;
        FOR i FROM 2 BY 2 TO UPB result DO
            result[ left pos  ] := deck[ i - 1 ];
            result[ right pos ] := deck[ i     ];
            left pos  +:= 1;
            right pos +:= 1
        OD;
        FOR i TO UPB deck DO deck[ i ] := result[ i ] OD
    END # SHUFFLE # ;
  1. compares two integer arrays for equality #

OP = = ( []INT a, b )BOOL:

    IF LWB a /= LWB b OR UPB a /= UPB b
    THEN # the arrays have different bounds #
        FALSE
    ELSE
        BOOL result := TRUE;
        FOR i FROM LWB a TO UPB a WHILE result := a[ i ] = b[ i ] DO SKIP OD;
        result
    FI # = # ;
  1. compares two integer arrays for inequality #

OP /= = ( []INT a, b )BOOL: NOT ( a = b );

  1. returns the number of shuffles required to return a deck of the specified length #
  2. back to its original state #

PROC count shuffles = ( INT length )INT:

    BEGIN
        []            INT original deck  = DECK length;
        [ 1 : length ]INT shuffled deck := original deck;
        INT   count         := 1;
        WHILE shuffle( shuffled deck );
              shuffled deck /= original deck
        DO
            count +:= 1
        OD;
        count
    END # count shuffles # ;
  1. test the shuffling #

[]INT lengths = ( 8, 24, 52, 100, 1020, 1024, 10 000 ); FOR l FROM LWB lengths TO UPB lengths DO

   print( ( whole( lengths[ l ], -8 ) + ": " + whole( count shuffles( lengths[ l ] ), -6 ), newline ) )

OD</lang>

Output:
       8:      3
      24:     11
      52:      8
     100:     30
    1020:   1018
    1024:     10
   10000:    300

AutoHotkey

<lang AutoHotkey>Shuffle(cards){ n := cards.MaxIndex()/2, res := [] loop % n res.push(cards[A_Index]), res.push(cards[round(A_Index + n)]) return res }</lang> Examples:<lang AutoHotkey>test := [8, 24, 52, 100, 1020, 1024, 10000] for each, val in test { cards := [], original:=rep:="" loop, % val cards.push(A_Index), original .= (original?", ":"") A_Index while (res <> original) { res := "" for k, v in (cards := Shuffle(cards)) res .= (res?", ":"") v rep := A_Index } result .= val "`t" rep "`n" } MsgBox % result return</lang>

Outputs:

8	3
24	11
52	8
100	30
1020	1018
1024	10
10000	300

C

<lang c>/* ===> INCLUDES <============================================================*/

  1. include <stdlib.h>
  2. include <stdio.h>
  3. include <string.h>

/* ===> CONSTANTS <===========================================================*/

  1. define N_DECKS 7

const int kDecks[N_DECKS] = { 8, 24, 52, 100, 1020, 1024, 10000 };

/* ===> FUNCTION PROTOTYPES <=================================================*/ int CreateDeck( int **deck, int nCards ); void InitDeck( int *deck, int nCards ); int DuplicateDeck( int **dest, const int *orig, int nCards ); int InitedDeck( int *deck, int nCards ); int ShuffleDeck( int *deck, int nCards ); void FreeDeck( int **deck );

/* ===> FUNCTION DEFINITIONS <================================================*/

int main() {

   int i, nCards, nShuffles;
   int *deck = NULL;
   for( i=0; i<N_DECKS; ++i ) {
       nCards = kDecks[i];
       if( !CreateDeck(&deck,nCards) ) {
           fprintf( stderr, "Error: malloc() failed!\n" );
           return 1;
       }
       InitDeck( deck, nCards );
       nShuffles = 0;
       do {
           ShuffleDeck( deck, nCards );
           ++nShuffles;
       } while( !InitedDeck(deck,nCards) );
       printf( "Cards count: %d, shuffles required: %d.\n", nCards, nShuffles );
       FreeDeck( &deck );
   }
   return 0;

}

int CreateDeck( int **deck, int nCards ) {

   int *tmp = NULL;
   if( deck != NULL )
       tmp = malloc( nCards*sizeof(*tmp) );
   return tmp!=NULL ? (*deck=tmp)!=NULL : 0; /* (?success) (:failure) */

}

void InitDeck( int *deck, int nCards ) {

   if( deck != NULL ) {
       int i;
       for( i=0; i<nCards; ++i )
           deck[i] = i;
   }

}

int DuplicateDeck( int **dest, const int *orig, int nCards ) {

   if( orig != NULL && CreateDeck(dest,nCards) ) {
       memcpy( *dest, orig, nCards*sizeof(*orig) );
       return 1; /* success */
   }
   else {
       return 0; /* failure */
   }

}

int InitedDeck( int *deck, int nCards ) {

   int i;
   for( i=0; i<nCards; ++i )
       if( deck[i] != i )
           return 0; /* not inited */
   return 1; /* inited */

}

int ShuffleDeck( int *deck, int nCards ) {

   int *copy = NULL;
   if( DuplicateDeck(&copy,deck,nCards) ) {
       int i, j;
       for( i=j=0; i<nCards/2; ++i, j+=2 ) {
           deck[j] = copy[i];
           deck[j+1] = copy[i+nCards/2];
       }
       FreeDeck( &copy );
       return 1; /* success */
   }
   else {
       return 0; /* failure */
   }

}

void FreeDeck( int **deck ) {

   if( *deck != NULL ) {
       free( *deck );
       *deck = NULL;
   }

} </lang>

Output:
Cards count: 8, shuffles required: 3.
Cards count: 24, shuffles required: 11.
Cards count: 52, shuffles required: 8.
Cards count: 100, shuffles required: 30.
Cards count: 1020, shuffles required: 1018.
Cards count: 1024, shuffles required: 10.
Cards count: 10000, shuffles required: 300.


Press "Enter" to quit...

C++

<lang cpp>

  1. include <iostream>
  2. include <algorithm>
  3. include <vector>

int pShuffle( int t ) {

   std::vector<int> v, o, r;
   for( int x = 0; x < t; x++ ) {
       o.push_back( x + 1 );
   }
   r = o;
   int t2 = t / 2 - 1, c = 1;
   while( true ) {
       v = r;
       r.clear();
       for( int x = t2; x > -1; x-- ) {
           r.push_back( v[x + t2 + 1] );
           r.push_back( v[x] );
       }
       std::reverse( r.begin(), r.end() );
       if( std::equal( o.begin(), o.end(), r.begin() ) ) return c;
       c++;
   }

}

int main() {

   int s[] = { 8, 24, 52, 100, 1020, 1024, 10000 };
   for( int x = 0; x < 7; x++ ) {
       std::cout << "Cards count: " << s[x] << ", shuffles required: ";
       std::cout << pShuffle( s[x] ) << ".\n";
   }
   return 0;

} </lang>

Output:
Cards count: 8, shuffles required: 3.
Cards count: 24, shuffles required: 11.
Cards count: 52, shuffles required: 8.
Cards count: 100, shuffles required: 30.
Cards count: 1020, shuffles required: 1018.
Cards count: 1024, shuffles required: 10.
Cards count: 10000, shuffles required: 300.

EchoLisp

<lang lisp>

shuffler
a permutation vector which interleaves both halves of deck

(define (make-shuffler n) (let ((s (make-vector n))) (for ((i (in-range 0 n 2))) (vector-set! s i (/ i 2))) (for ((i (in-range 0 n 2))) (vector-set! s (1+ i) (+ (/ n 2) (vector-ref s i)))) s))

output
(n . # of shuffles needed to go back)

(define (magic-shuffle n) (when (odd? n) (error "magic-shuffle:odd input" n)) (let [(deck (list->vector (iota n))) ;; (0 1 ... n-1) (dock (list->vector (iota n))) ;; keep trace or init deck (shuffler (make-shuffler n))]

(cons n (1+ (for/sum ((i Infinity)) ; (in-naturals missing in EchoLisp v2.9) (vector-permute! deck shuffler) ;; permutes in place #:break (eqv? deck dock) ;; compare to first 1))))) </lang>

Output:

<lang lisp> map magic-shuffle '(8 24 52 100 1020 1024 10000))

   → ((8 . 3) (24 . 11) (52 . 8) (100 . 30) (1020 . 1018) (1024 . 10) (10000 . 300))
Let's look in the On-line Encyclopedia of Integer Sequences
Given a list of numbers, the (oeis ...) function looks for a sequence

(lib 'web) Lib: web.lib loaded. map magic-shuffle (range 2 18 2))

   → ((2 . 1) (4 . 2) (6 . 4) (8 . 3) (10 . 6) (12 . 10) (14 . 12) (16 . 4))

(oeis '(1 2 4 3 6 10 12 4)) → Sequence A002326 found </lang>

Elixir

Translation of: Ruby

<lang elixir>defmodule Perfect do

 def shuffle(n) do
   start = Enum.to_list(1..n)
   m = div(n, 2)
   shuffle(start, magic_shuffle(start, m), m, 1)
 end
 
 defp shuffle(start, start, _, step), do: step
 defp shuffle(start, deck, m, step) do
   shuffle(start, magic_shuffle(deck, m), m, step+1)
 end
 
 defp magic_shuffle(deck, len) do
   {left, right} = Enum.split(deck, len)
   Enum.zip(left, right)
   |> Enum.map(&Tuple.to_list/1)
   |> List.flatten
 end

end

Enum.each([8, 24, 52, 100, 1020, 1024, 10000], fn n ->

 step = Perfect.shuffle(n)
 IO.puts "#{n} : #{step}"

end)</lang>

Output:
8 : 3
24 : 11
52 : 8
100 : 30
1020 : 1018
1024 : 10
10000 : 300

Haskell

<lang Haskell>module Shuffles

  where

shuffle :: [a] -> [a] shuffle lst = let (a,b) = splitAt (length lst `div` 2) lst

             in foldMap (\(x,y) -> [x,y]) $ zip a b

findCycle :: Eq a => (a -> a) -> a -> [a] findCycle f x = takeWhile (/= x) $ iterate f (f x)

main = mapM_ report [ 8, 24, 52, 100, 1020, 1024, 10000 ]

 where
   report n = putStrLn ("deck of " ++ show n ++ " cards: "
                        ++ show (countSuffles n) ++ " shuffles!")
   countSuffles n = 1 + length (findCycle shuffle [1..n])</lang>
Output:
deck of 8 cards: 3 shuffles!
deck of 24 cards: 11 shuffles!
deck of 52 cards: 8 shuffles!
deck of 100 cards: 30 shuffles!
deck of 1020 cards: 1018 shuffles!
deck of 1024 cards: 10 shuffles!
deck of 10000 cards: 300 shuffles!

J

The shuffle routine:

<lang J> shuf=: /: $ /:@$ 0 1"_</lang>

Here, the phrase ($ $ 0 1"_) would generate a sequence of 0s and 1s the same length as the argument sequence:

<lang J> ($ $ 0 1"_) 'abcdef' 0 1 0 1 0 1</lang>

And we can use grade up (/:) to find the indices which would sort the argument sequence so that the values in the positions corresponding to our generated zeros would come before the values in the positions corresponding to our ones.

<lang J> /: ($ $ 0 1"_) 'abcdef' 0 2 4 1 3 5</lang>

But we can use grade up again to find what would have been the original permutation (grade up is a self inverting function for this domain).

<lang J> /:/: ($ $ 0 1"_) 'abcdef' 0 3 1 4 2 5</lang>

And, that means it can also sort the original sequence into that order:

<lang J> shuf 'abcdef' adbecf

  shuf 'abcdefgh'

aebfcgdh</lang>

And this will work for sequences of arbitrary length.

(The rest of the implementation of shuf is pure syntactic sugar - you can use J's dissect and trace facilities to see the details if you are trying to learn the language.)

Meanwhile, the cycle length routine could look like this:

<lang J> shuflen=: [: *./ #@>@C.@shuf@i.</lang>

Here, we first generate a list of integers of the required length in their natural order. We then reorder them using our shuf function, find the cycles which result, find the lengths of each of these cycles then find the least common multiple of those lengths.

So here is the task example (with most of the middle trimmed out to avoid crashing the rosettacode wiki implementation):

<lang J> shuflen"0 }.2*i.5000 1 2 4 3 6 10 12 4 8 18 6 11 20 18 28 5 10 12 36 12 20 14 12 23 21 8 52 20 18 ... 4278 816 222 1332 384</lang>

Task example:

<lang J> ('deck size';'required shuffles'),(; shuflen)&> 8 24 52 100 1020 1024 10000 ┌─────────┬─────────────────┐ │deck size│required shuffles│ ├─────────┼─────────────────┤ │8 │3 │ ├─────────┼─────────────────┤ │24 │11 │ ├─────────┼─────────────────┤ │52 │8 │ ├─────────┼─────────────────┤ │100 │30 │ ├─────────┼─────────────────┤ │1020 │1018 │ ├─────────┼─────────────────┤ │1024 │10 │ ├─────────┼─────────────────┤ │10000 │300 │ └─────────┴─────────────────┘</lang>

Note that the implementation of shuf defines a behavior for odd length "decks". Experimentation shows that cycle length for an odd length deck is often the same as the cycle length for an even length deck which is one "card" longer.

Java

Works with: Java version 8

<lang java>import java.util.Arrays; import java.util.stream.IntStream;

public class PerfectShuffle {

   public static void main(String[] args) {
       int[] sizes = {8, 24, 52, 100, 1020, 1024, 10_000};
       for (int size : sizes)
           System.out.printf("%5d : %5d%n", size, perfectShuffle(size));
   }
   static int perfectShuffle(int size) {
       if (size % 2 != 0)
           throw new IllegalArgumentException("size must be even");
       int half = size / 2;
       int[] a = IntStream.range(0, size).toArray();
       int[] original = a.clone();
       int[] aa = new int[size];
       for (int count = 1; true; count++) {
           System.arraycopy(a, 0, aa, 0, size);
           for (int i = 0; i < half; i++) {
               a[2 * i] = aa[i];
               a[2 * i + 1] = aa[i + half];
           }
           if (Arrays.equals(a, original))
               return count;
       }
   }

}</lang>

    8 :     3
   24 :    11
   52 :     8
  100 :    30
 1020 :  1018
 1024 :    10
10000 :   300

JavaScript

ES6

<lang JavaScript>(() => {

   'use strict';
   // shuffle :: [a] -> [a]
   const shuffle = xs =>
       concat(zip.apply(null, splitAt(div(length(xs), 2), xs)));
   // firstycle :: Eq a => (a -> a) -> a -> [a]
   const firstCycle = (f, x) =>
       until(
           m => EqArray(x, m.current),
           m => {
               const fx = f(m.current);
               return {
                   current: fx,
                   all: m.all.concat([fx])
               };
           }, {
               current: f(x),
               all: [x]
           }
       );
   // Two arrays equal ?
   // EqArray :: [a] -> [b] -> Bool
   const EqArray = (xs, ys) => {
       const [nx, ny] = [xs.length, ys.length];
       return nx === ny ? (
           nx > 0 ? (
               xs[0] === ys[0] && EqArray(xs.slice(1), ys.slice(1))
           ) : true
       ) : false;
   };
   // GENERIC FUNCTIONS
   // zip :: [a] -> [b] -> [(a,b)]
   const zip = (xs, ys) =>
       xs.slice(0, Math.min(xs.length, ys.length))
       .map((x, i) => [x, ys[i]]);
   // concat :: a -> [a]
   const concat = xs => [].concat.apply([], xs);
   // splitAt :: Int -> [a] -> ([a],[a])
   const splitAt = (n, xs) => [xs.slice(0, n), xs.slice(n)];
   // div :: Num -> Num -> Int
   const div = (x, y) => Math.floor(x / y);
   // until :: (a -> Bool) -> (a -> a) -> a -> a
   const until = (p, f, x) => {
       const go = x => p(x) ? x : go(f(x));
       return go(x);
   }
   // range :: Int -> Int -> [Int]
   const range = (m, n) =>
       Array.from({
           length: Math.floor(n - m) + 1
       }, (_, i) => m + i);
   // length :: [a] -> Int
   // length :: Text -> Int
   const length = xs => xs.length;
   // maximumBy :: (a -> a -> Ordering) -> [a] -> a
   const maximumBy = (f, xs) =>
       xs.reduce((a, x) => a === undefined ? x : (
           f(x, a) > 0 ? x : a
       ), undefined);
   // transpose :: a -> a
   const transpose = xs =>
       xs[0].map((_, iCol) => xs.map((row) => row[iCol]));
   // show :: a -> String
   const show = x => JSON.stringify(x, null, 2);
   // replicateS :: Int -> String -> String
   const replicateS = (n, s) => {
       let v = s,
           o = ;
       if (n < 1) return o;
       while (n > 1) {
           if (n & 1) o = o.concat(v);
           n >>= 1;
           v = v.concat(v);
       }
       return o.concat(v);
   };
   // justifyRight :: Int -> Char -> Text -> Text
   let justifyRight = (n, cFiller, strText) =>
       n > strText.length ? (
           (replicateS(n, cFiller) + strText)
           .slice(-n)
       ) : strText;
   // TEST
   const cols = transpose([
           ['Deck', 'Shuffles']
       ].concat(
           [8, 24, 52, 100, 1020, 1024, 10000]
           .map(x => [x.toString(), firstCycle(shuffle, range(1, x))
               .all.length.toString()
           ])));
   return transpose(cols.map(col => {
       const width = length(maximumBy(length, col)) + 2;
       
       return col.map(x => justifyRight(width, ' ', x));
   })).map(row => row.join('  ')).join('\n');

})();</lang>

Output:
   Deck  Shuffles
      8      3
     24     11
     52      8
    100     30
   1020   1018
   1024     10
  10000    300

Lua

<lang Lua>-- Perform weave shuffle function shuffle (cards)

   local pile1, pile2 = {}, {}
   for card = 1, #cards / 2 do table.insert(pile1, cards[card]) end
   for card = (#cards / 2) + 1, #cards do table.insert(pile2, cards[card]) end
   cards = {}
   for card = 1, #pile1 do
       table.insert(cards, pile1[card])
       table.insert(cards, pile2[card])
   end
   return cards

end

-- Return boolean indicating whether or not the cards are in order function inOrder (cards)

   for k, v in pairs(cards) do
       if k ~= v then return false end
   end
   return true

end

-- Count the number of shuffles needed before the cards are in order again function countShuffles (deckSize)

   local deck, count = {}, 0
   for i = 1, deckSize do deck[i] = i end
   repeat
       deck = shuffle(deck)
       count = count + 1
   until inOrder(deck)
   return count

end

-- Main procedure local testCases = {8, 24, 52, 100, 1020, 1024, 10000} print("Input", "Output") for _, case in pairs(testCases) do print(case, countShuffles(case)) end</lang>

Output:
Input   Output
8       3
24      11
52      8
100     30
1020    1018
1024    10
10000   300


MATLAB

Function:

   function [New]=PerfectShuffle(Nitems, Nturns)
   if mod(Nitems,2)==0 %only if even number
       X=1:Nitems; %define deck
       for c=1:Nturns %defines one shuffle
           X=reshape(X,Nitems/2,2)'; %split the deck in two and stack halves
           X=X(:)'; %mix the halves
       end
       New=X; %result of multiple shufflings
   end
   end


Main:

   Result=[]; %vector to store results 
   Q=[8, 24, 52, 100, 1020, 1024, 10000]; %queries
   for n=Q %for each query
       Same=0; %initialize comparison
       T=0; %initialize number of shuffles
       while ~Same %while the result is not the original query
           T=T+1; %one more shuffle
           R=PerfectShuffle(n,T); %result of shuffling the query
           Same=~(any(R-(1:n))); %same vector as the query
       end %when getting the same vector
       Result=[Result;T]; %collect results
   end
   disp([Q', Result])

Output:

          8           3
         24          11
         52           8
        100          30
       1020        1018
       1024          10
      10000         300

Oforth

<lang oforth>: shuffle(l) l size 2 / dup l left swap l right zip expand ;

nbShuffles(l) 1 l while( shuffle dup l <> ) [ 1 under+ ] drop ;</lang>
Output:
>[ 8, 24, 52, 100, 1020, 1024, 10000 ] map(#[ seq nbShuffles ]) .
[3, 11, 8, 30, 1018, 10, 300] ok

PARI/GP

This example is in need of improvement:

The task description was updated; please update this solution accordingly and then remove this template.

<lang parigp>magic(v)=vector(#v,i,v[if(i%2,1,#v/2)+i\2]); shuffles_slow(n)=my(v=[1..n],o=v,s=1);while((v=magic(v))!=o,s++);s; shuffles(n)=znorder(Mod(2,n-1)); vector(5000,n,shuffles_slow(2*n))</lang>

Output:
%1 = [1, 2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 28, 5, 10, 12, 36, 12,
 20, 14, 12, 23, 21, 8, 52, 20, 18, 58, 60, 6, 12, 66, 22, 35, 9, 20, 30, 39, 54
, 82, 8, 28, 11, 12, 10, 36, 48, 30, 100, 51, 12, 106, 36, 36, 28, 44, 12, 24, 1
10, 20, 100, 7, 14, 130, 18, 36, 68, 138, 46, 60, 28, 42, 148, 15, 24, 20, 52, 5
2, 33, 162, 20, 83, 156, 18, 172, 60, 58, 178, 180, 60, 36, 40, 18, 95, 96, 12,
196, 99, 66, 84, 20, 66, 90, 210, 70, 28, 15, 18, 24, 37, 60, 226, 76, 30, 29, 9
2, 78, 119, 24, 162, 84, 36, 82, 50, 110, 8, 16, 36, 84, 131, 52, 22, 268, 135,
12, 20, 92, 30, 70, 94, 36, 60, 136, 48, 292, 116, 90, 132, 42, 100, 60, 102, 10
2, 155, 156, 12, 316, 140, 106, 72, 60, 36, 69, 30, 36, 132, 21, 28, 10, 147, 44
, 346, 348, 36, 88, 140, 24, 179, 342, 110, 36, 183, 60, 156, 372, 100, 84, 378,
 14, 191, 60, 42, 388, 88, 130, 156, 44, 18, 200, 60, 108, 180, 204, 68, 174, 16
4, 138, 418, 420, 138, 40, 60, 60, 43, 72, 28, 198, 73, 42, 442, 44, 148, 224, 2
0, 30, 12, 76, 72, 460, 231, 20, 466, 66, 52, 70, 180, 156, 239, 36, 66, 48, 243
, 162, 490, 56, 60, 105, 166, 166, 251, 100, 156, 508, 9, 18, 204, 230, 172, 260
, 522, 60, 40, 253, 174, 60, 212, 178, 210, 540, 180, 36, 546, 60, 252, 39, 36,
556, 84, 40, 562, 28, 54, 284, 114, 190, 220, 144, 96, 246, 260, 12, 586, 90, 19
6, 148, 24, 198, 299, 25, 66, 220, 303, 84, 276, 612, 20, 154, 618, 198, 33, 500
, 90, 72, 45, 210, 28, 84, 210, 64, 214, 28, 323, 290, 30, 652, 260, 18, 658, 66
0, 24, 36, 308, 74, 60, 48, 180, 676, 48, 226, 22, 68, 76, 156, 230, 30, 276, 40
, 58, 700, 36, 92, 300, 708, 78, 55, 60, 238, 359, 51, 24, 140, 121, 486, 56, 24
4, 84, 330, 246, 36, 371, 148, 246, 318, 375, 50, 60, 756, 110, 380, 36, 24, 348
, 384, 16, 772, 20, 36, 180, 70, 252, 52, 786, 262, 84, 60, 52, 796, 184, 66, 90
, 132, 268, 404, 270, 270, 324, 126, 12, 820, 411, 20, 826, 828, 92, 168, 332, 9
0, 419, 812, 70, 156, 330, 94, 396, 852, 36, 428, 858, 60, 431, 172, 136, 390, 1
32, 48, 300, 876, 292, 55, 882, 116, 443, 21, 270, 414, 356, 132, 140, 104,[+++]

(By default gp won't show more than 25 lines of output, though an arbitrary amount can be printed or written to a file; use print, write, or default(lines, 100) to show more.)

Perl

<lang perl>use List::Util qw(all);

sub perfect_shuffle {

  my $mid = @_ / 2;
  map { @_[$_, $_ + $mid] } 0..($mid - 1);

}

for my $size (8, 24, 52, 100, 1020, 1024, 10000) {

   my @shuffled = my @deck = 1 .. $size;
   my $n = 0;
   do { $n++; @shuffled = perfect_shuffle(@shuffled) }
       until all { $shuffled[$_] == $deck[$_] } 0..$#shuffled;
   
   printf "%5d cards: %4d\n", $size, $n;

}</lang>

Output:
    8 cards:    3
   24 cards:   11
   52 cards:    8
  100 cards:   30
 1020 cards: 1018
 1024 cards:   10
10000 cards:  300

Perl 6

Translation of: Perl

<lang perl6>sub perfect-shuffle (@deck) {

   my $mid = @deck / 2;
   flat @deck[0 ..^ $mid] Z @deck[$mid .. *];

}

for 8, 24, 52, 100, 1020, 1024, 10000 -> $size {

   my @deck = ^$size;
   my $n;
   repeat until [<] @deck {
       $n++;
       @deck = perfect-shuffle @deck;
   }

   printf "%5d cards: %4d\n", $size, $n;

}</lang>

Output:
    8 cards:    3
   24 cards:   11
   52 cards:    8
  100 cards:   30
 1020 cards: 1018
 1024 cards:   10
10000 cards:  300

Phix

<lang Phix>function perfect_shuffle(sequence deck) integer mp = length(deck)/2 sequence res = deck

   integer k = 1
   for i=1 to mp do
       res[k] = deck[i]        k += 1
       res[k] = deck[i+mp]     k += 1
   end for
   return res

end function

constant testsizes = {8, 24, 52, 100, 1020, 1024, 10000} for i=1 to length(testsizes) do

   sequence deck = tagset(testsizes[i])
   sequence work = perfect_shuffle(deck)
   integer count = 1
   while work!=deck do
       work = perfect_shuffle(work)
       count += 1
   end while
   printf(1,"%5d cards: %4d\n", {testsizes[i],count})

end for</lang>

Output:
    8 cards:    3
   24 cards:   11
   52 cards:    8
  100 cards:   30
 1020 cards: 1018
 1024 cards:   10
10000 cards:  300

PicoLisp

<lang PicoLisp>(de perfectShuffle (Lst)

  (mapcan '((B A) (list A B))
     (cdr (nth Lst (/ (length Lst) 2)))
     Lst ) )

(for N (8 24 52 100 1020 1024 10000)

  (let (Lst (range 1 N)  L Lst  Cnt 1)
     (until (= Lst (setq L (perfectShuffle L)))
        (inc 'Cnt) )
     (tab (5 6) N Cnt) ) )</lang>

Output:

    8     3
   24    11
   52     8
  100    30
 1020  1018
 1024    10
10000   300

Python

<lang python> import doctest import random


def flatten(lst):

   """
   >>> flatten([[3,2],[1,2]])
   [3, 2, 1, 2]
   """
   return [i for sublst in lst for i in sublst]

def magic_shuffle(deck):

   """
   >>> magic_shuffle([1,2,3,4])
   [1, 3, 2, 4]
   """
   half = len(deck) // 2 
   return flatten(zip(deck[:half], deck[half:]))

def after_how_many_is_equal(shuffle_type,start,end):

   """
   >>> after_how_many_is_equal(magic_shuffle,[1,2,3,4],[1,2,3,4])
   2
   """
   start = shuffle_type(start)
   counter = 1
   while start != end:
       start = shuffle_type(start)
       counter += 1
   return counter

def main():

   doctest.testmod()
   print("Length of the deck of cards | Perfect shuffles needed to obtain the same deck back")
   for length in (8, 24, 52, 100, 1020, 1024, 10000):
       deck = list(range(length))
       shuffles_needed = after_how_many_is_equal(magic_shuffle,deck,deck)
       print("{} | {}".format(length,shuffles_needed))


if __name__ == "__main__":

   main()

</lang> Reversed shuffle or just calculate how many shuffles are needed: <lang python>def mul_ord2(n): # directly calculate how many shuffles are needed to restore # initial order: 2^o mod(n-1) == 1 if n == 2: return 1

n,t,o = n-1,2,1 while t != 1: t,o = (t*2)%n,o+1 return o

def shuffles(n): a,c = list(range(n)), 0 b = a

while True: # Reverse shuffle; a[i] can be taken as the current # position of the card with value i. This is faster. a = a[0:n:2] + a[1:n:2] c += 1 if b == a: break return c

for n in range(2, 10000, 2): #print(n, mul_ord2(n)) print(n, shuffles(n))</lang>

Racket

<lang racket>#lang racket/base (require racket/list)

(define (perfect-shuffle l)

 (define-values (as bs) (split-at l (/ (length l) 2)))
 (foldr (λ (a b d) (list* a b d)) null as bs))

(define (perfect-shuffles-needed n)

 (define-values (_ rv)
   (for/fold ((d (perfect-shuffle (range n))) (i 1))
             ((_ (in-naturals))
              #:break (apply < d))
     (values (perfect-shuffle d) (add1 i))))
 rv)

(module+ test

 (require rackunit)
 (check-equal? (perfect-shuffle '(1 2 3 4)) '(1 3 2 4))
 
 (define (test-perfect-shuffles-needed n e)
   (define psn (perfect-shuffles-needed n))
   (printf "Deck size:\t~a\tShuffles needed:\t~a\t(~a)~%" n psn e)
   (check-equal? psn e))
 (for-each test-perfect-shuffles-needed
           '(8 24 52 100 1020 1024 10000)
           '(3 11  8  30 1018   10   300)))</lang>
Output:
Deck size:	8	Shuffles needed:	3	(3)
Deck size:	24	Shuffles needed:	11	(11)
Deck size:	52	Shuffles needed:	8	(8)
Deck size:	100	Shuffles needed:	30	(30)
Deck size:	1020	Shuffles needed:	1018	(1018)
Deck size:	1024	Shuffles needed:	10	(10)
Deck size:	10000	Shuffles needed:	300	(300)

REXX

unoptimized

<lang rexx>/*REXX program performs a "perfect shuffle" for a number of even numbered decks. */ parse arg X /*optional list of test cases from C.L.*/ if X= then X=8 24 52 100 1020 1024 10000 /*Not specified? Then use the default.*/ w=length(word(X, words(X))) /*used for right─aligning the numbers. */

   do j=1  for words(X);  y=word(X,j)           /*use numbers in the test suite (list).*/
     do k=1  for y;       @.k=k;      end /*k*/ /*generate a deck to be used (shuffled)*/
     do t=1  until eq();  call magic; end /*t*/ /*shuffle until  before  equals  after.*/
   say 'deck size:'    right(y,w)","       right(t,w)      'perfect shuffles.'
   end   /*j*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ eq: do ?=1 for y; if @.?\==? then return 0; end; return 1 /*──────────────────────────────────────────────────────────────────────────────────────*/ magic: z=0 /*set the Z pointer (used as index).*/

      h=y%2                                     /*get the half─way (midpoint) pointer. */
               do s=1  for h;  z=z+1;  h=h+1    /*traipse through the card deck pips.  */
               !.z=@.s;        z=z+1            /*assign left half; then bump pointer. */
               !.z=@.h                          /*   "   right  "                      */
               end   /*s*/                      /*perform a perfect shuffle of the deck*/
               do r=1  for y;  @.r=!.r;  end    /*re─assign to the original card deck. */
      return</lang>

output   (abbreviated)   when using the default input:

deck size:     8,     3 perfect shuffles.
deck size:    24,    11 perfect shuffles.
deck size:    52,     8 perfect shuffles.
deck size:   100,    30 perfect shuffles.
deck size:  1020,  1018 perfect shuffles.
deck size:  1024,    10 perfect shuffles.
deck size: 10000,   300 perfect shuffles.

optimized

This REXX version takes advantage that the 1st and last cards of the deck don't change. <lang rexx>/*REXX program does a "perfect shuffle" for a number of even numbered decks. */ parse arg X /*optional list of test cases from C.L.*/ if X= then X=8 24 52 100 1020 1024 10000 /*Not specified? Use default.*/ w=length(word(X, words(X))) /*used for right─aligning the numbers. */

   do j=1  for words(X);  y=word(X,j)           /*use numbers in the test suite (list).*/
     do k=1  for y;       @.k=k;       end      /*generate a deck to be shuffled (used)*/
     do t=1  until eq();  call magic;  end      /*shuffle until  before  equals  after.*/
   say 'deck size:'    right(y,w)","       right(t,w)      'perfect shuffles.'
   end     /*j*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ eq: do ?=1 for y; if @.?\==? then return 0; end; return 1 /*──────────────────────────────────────────────────────────────────────────────────────*/ magic: z=1; h=y%2 /*H is (half─way) pointer.*/

             do L=3  by 2  for h-1; z=z+1; !.L=@.z; end     /*assign left half of deck.*/
             do R=2  by 2  for h-1; h=h+1; !.R=@.h; end     /*   "   right  "   "   "  */
             do a=2        for y-2;        @.a=!.a; end     /*re─assign──►original deck*/
      return</lang>

output   is the same as the 1st version.

Ruby

<lang ruby>def perfect_shuffle(deck_size = 52) deck = (0...deck_size).to_a shuffled_deck = [deck.first(deck_size / 2), deck.last(deck_size / 2)] 1.step do |i| return i if deck == (shuffled_deck = shuffled_deck.transpose.flatten) shuffled_deck = [shuffled_deck.shift(deck_size / 2), shuffled_deck] end end

[8, 24, 52, 100, 1020, 1024, 10000].each do |i| puts "Perfect Shuffles Required for Deck Size #{i}: #{perfect_shuffle(i)}" end</lang>

Output:
Perfect Shuffles Required for Deck Size 8: 3
Perfect Shuffles Required for Deck Size 24: 11
Perfect Shuffles Required for Deck Size 52: 8
Perfect Shuffles Required for Deck Size 100: 30
Perfect Shuffles Required for Deck Size 1020: 1018
Perfect Shuffles Required for Deck Size 1024: 10
Perfect Shuffles Required for Deck Size 10000: 300

Sidef

Translation of: Perl

<lang ruby>func perfect_shuffle(deck) {

    var a = deck/2
    a[0] ~Z a[1] -> flatten

}

[8, 24, 52, 100, 1020, 1024, 10000].each { |size|

   var deck = @(1..size);
   var shuffled = deck;
   var n = 0;
   loop {
       ++n;
       shuffled = perfect_shuffle(shuffled);
       shuffled.each_index { |i|
           shuffled[i] == deck[i] || goto :NEXT;
       }
       break;
       @:NEXT;
   }
   printf("%5d cards: %4d\n", size, n);

}</lang>

Output:
    8 cards:    3
   24 cards:   11
   52 cards:    8
  100 cards:   30
 1020 cards: 1018
 1024 cards:   10
10000 cards:  300

Tcl

Using tcltest to include an executable test case ..

<lang Tcl>namespace eval shuffle {

   proc perfect {deck} {
       if {[llength $deck]%2} {
           return -code error "Deck must be of even length!"
       }
       set split [expr {[llength $deck]/2}]
       set top [lrange $deck 0 $split-1]
       set btm [lrange $deck $split end]
       foreach a $top b $btm {
           lappend res $a $b
       }
       return $res
   }
   proc cycle_length {transform deck} {
       set d $deck
       while 1 {
           set d [$transform $d]
           incr i
           if {$d eq $deck} {return $i}
       }
       return $i
   }
   proc range {a {b ""}} {
       if {$b eq ""} {
           set b $a; set a 0
       }
       set res {}
       while {$a < $b} {
           lappend res $a
           incr a
       }
       return $res
   }

}

set ::argv {} package require tcltest tcltest::test "Test perfect shuffle cycles" {} -body {

   lmap size {8 24 52 100 1020 1024 10000} {
       shuffle::cycle_length perfect [range $size]
   }

} -result {3 11 8 30 1018 10 300}</lang>

zkl

<lang zkl>fcn perfectShuffle(numCards){

  deck,shuffle,n,N:=numCards.pump(List),deck,0,numCards/2;
  do{ shuffle=shuffle[0,N].zip(shuffle[N,*]).flatten(); n+=1 }
  while(deck!=shuffle);
  n

} foreach n in (T(8,24,52,100,1020,1024,10000)){

  println("%5d : %d".fmt(n,perfectShuffle(n)));

}</lang>

Output:
    8 : 3
   24 : 11
   52 : 8
  100 : 30
 1020 : 1018
 1024 : 10
10000 : 300