31
\$\begingroup\$

A Faro shuffle is a technique frequently used by magicians to "shuffle" a deck. To perform a Faro shuffle you first cut the deck into 2 equal halves then you interleave the two halves. For example

[1 2 3 4 5 6 7 8]

Faro shuffled is

[1 5 2 6 3 7 4 8]

This can be repeated any number of times. Interestingly enough, if you repeat this enough times, you will always end up back at the original array. For example:

[1 2 3 4 5 6 7 8]
[1 5 2 6 3 7 4 8]
[1 3 5 7 2 4 6 8]
[1 2 3 4 5 6 7 8]

Notice that 1 stays on the bottom and 8 stays on the top. That makes this an outer-shuffle. This is an important distinction.

The Challenge

Given an array of integers A, and a number N, output the array after N Faro shuffles. A may contain repeated or negative elements, but it will always have an even number of elements. You can assume the array will not be empty. You can also assume that N will be a non-negative integer, although it may be 0. You can take these inputs in any reasonable manner. The shortest answer in bytes wins!

Test IO:

#N, A,                                              Output
1,  [1, 2, 3, 4, 5, 6, 7, 8]                        [1, 5, 2, 6, 3, 7, 4, 8]
2,  [1, 2, 3, 4, 5, 6, 7, 8]                        [1, 3, 5, 7, 2, 4, 6, 8]
7,  [-23, -37, 52, 0, -6, -7, -8, 89]               [-23, -6, -37, -7, 52, -8, 0, 89]
0,  [4, 8, 15, 16, 23, 42]                          [4, 8, 15, 16, 23, 42]
11, [10, 11, 8, 15, 13, 13, 19, 3, 7, 3, 15, 19]    [10, 19, 11, 3, 8, 7, 15, 3, 13, 15, 13, 19]

And, a massive test case:

23, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

Should output:

[1, 30, 59, 88, 18, 47, 76, 6, 35, 64, 93, 23, 52, 81, 11, 40, 69, 98, 28, 57, 86, 16, 45, 74, 4, 33, 62, 91, 21, 50, 79, 9, 38, 67, 96, 26, 55, 84, 14, 43, 72, 2, 31, 60, 89, 19, 48, 77, 7, 36, 65, 94, 24, 53, 82, 12, 41, 70, 99, 29, 58, 87, 17, 46, 75, 5, 34, 63, 92, 22, 51, 80, 10, 39, 68, 97, 27, 56, 85, 15, 44, 73, 3, 32, 61, 90, 20, 49, 78, 8, 37, 66, 95, 25, 54, 83, 13, 42, 71, 100]  
\$\endgroup\$
7
  • \$\begingroup\$ Can the array contain zero elements? \$\endgroup\$
    – Leaky Nun
    Jun 5, 2016 at 4:54
  • \$\begingroup\$ @LeakyNun We'll say no, you don't have to handle zero elements. \$\endgroup\$
    – DJMcMayhem
    Jun 5, 2016 at 5:00
  • \$\begingroup\$ Related. Related. Related. \$\endgroup\$ Jun 5, 2016 at 15:12
  • \$\begingroup\$ Related \$\endgroup\$ Jun 6, 2016 at 7:11
  • 1
    \$\begingroup\$ Any permutation of a finite set, if repeated enough times, will end up back where it started; this isn't special to Faro shuffles. \$\endgroup\$ Aug 14, 2016 at 8:45

20 Answers 20

19
\$\begingroup\$

vim, 62 59 54

qrma50%mb:norm@q<cr>ggqOjdd'apjma'b@q<esc>0"qDJ<C-a>D@"i@r<esc>xxdd@"

Wow. This is possibly the hackiest thing I've written for PPCG, and that's saying something.

Input is taken as N on the first line followed by the elements of the array, each on its own line.

qr         first, we're going to record the contents of the @r macro. this is
             the macro which does the faro-shuffle operation.
  ma       set the mark 'a at the beginning of the file
  50%      move to the 50% point of the file (i.e. halfway down)
  mb       set another mark here
  :norm@q  evaluate the recursive macro @q. we'll get to what that does later,
             but the interesting part here is that it's :norm@q instead of @q.
             this is because a recursive macro terminates at the end of the
             file, which means when @q terminates, @r would also abort, which
             would make calling it with a count impossible. running @q under
             :norm prevents this.
  gg       move back to the top of the file for the next iteration
q          end recording
O          now we're inserting contents of the @q macro, the recursive part
             we can't record it directly because it's destructive
  j        move to line directly below mark 'b (which was just set before @q)
  dd       delete this line and bring it...
  'ap      up after mark 'a (which starts on line 1, bringing the N/2th line
             directly below line 1, aka line 2)
  jma      replace mark 'a one line below this so that the next time we call
             'ap, the line from the second half is interleaved with the lines
             from the first half
  'b       jump back to mark 'b (remember, 'b is the last line of the first
             half of the file, originally reached via 50%)
  @q       call ourselves, causing the macro to run until hitting EOF
0"qD       delete this into register "q
J          delete the empty line that remains
<C-a>      here's another interesting bit: we want to run @r N times. but 0@r
             means "go to column 0, and then run @r once." so we have to
             increment the input number...
D@"        and then *that* many times...
  i@r        insert @r...
xx         ... and finally, delete two characters, which is the extra @r from
             the increment
dd         delete the sequence of @rs into the "" register...
@"         and run it!

I actually possibly found several vim bugs while writing this answer:

  • recording macros is not possible within other macros (when setting their text manually, not with q) or within :*maps.

  • :let @a='<C-v><cr>'<cr>i<C-r>a outputs two newlines, not one, for whatever arcane reason.

I might investigate those further later.

Thanks to Dr Green Eggs and Ham DJ for 3 bytes!

\$\endgroup\$
3
  • 4
    \$\begingroup\$ This is beautiful and horrifying. I probably don't have enough patience to do this in vim. :P Also, you can take off 2 bytes by doing "rck instead of vgg"rc, and you can take off another 5 by doing dw@"i@r<esc> instead of AA@R<C-v><esc><esc>0D@" \$\endgroup\$
    – DJMcMayhem
    Jun 5, 2016 at 6:48
  • \$\begingroup\$ @DrGreenEggsandHamDJ Can't do that first one because that grabs a trailing newline as well, but that second optimization works. Thanks! \$\endgroup\$
    – Doorknob
    Jun 5, 2016 at 16:28
  • \$\begingroup\$ I wrote a different approach that almost works really well, but it gets tripped up if not every line is the same length. If someone can salvage it, be my guest: Try it online! \$\endgroup\$
    – DJMcMayhem
    Aug 21, 2023 at 23:07
11
\$\begingroup\$

05AB1E, 5 bytes

Code:

F2äø˜

Explanation, input: N, array:

F      # Do the following N times
 2ä    # Split the array into 2 pieces
   ø   # Zip
    ˜  # Deep flatten

Uses the CP-1252 encoding. Try it online!.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Damn, I was too slow! \$\endgroup\$
    – user53406
    Jun 5, 2016 at 8:06
6
\$\begingroup\$

Python, 68 57 bytes

f=lambda n,x:n and f(n-1,sum(zip(x,x[len(x)/2:]),()))or x

Thanks to @Sp3000 for golfing off 11 bytes!

Test it on Ideone.

\$\endgroup\$
0
6
\$\begingroup\$

Python 2, 59 bytes

def f(n,L):exec"l=len(L)/2;L=(L+L[1:]*~-l)[::l];"*n;print L

A different approach, slightly longer than the other Python answers. Only works for positive even numbers of elements.

e.g. for 1, [1,2,3,4,5,6,7,8], take the array and append len(L)/2-1 copies of itself minus the first element, e.g.

[1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8]

Then take every len(L)/2th element.

[1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8,2,3,4,5,6,7,8]
 ^       ^       ^       ^       ^       ^       ^       ^
\$\endgroup\$
6
\$\begingroup\$

Haskell, 62 bytes

0!a=a
n!a|s<-length a=(n-1)![a!!mod(div(s*i+i)2)s|i<-[0..s-1]]

Let s = 2·t be the size of the list. The i-th element of the new list is obtained by taking the enter image description here-th element of the old list, zero-indexed, modulo s.

Proof: if i = 2·k is even, then

                                         enter image description here

and if i = 2·k + 1 is odd, then

                        enter image description here

Thus the values used for indexing are 0, t, 1, t + 1, 2, t + 2, …

\$\endgroup\$
5
\$\begingroup\$

J - 12 bytes

Adverb (!) taking number of shuffles on the left and the array to shuffle on the right.

/:#/:@$0,#^:

The J parser has rules for writing tacit adverbs, but they have very low precedence: if you want to use a train of verbs as a left argument, you can omit an otherwise necessary set of parentheses. So the above is actually short for (/:#/:@$0,#)^:, which takes the number of shuffles on the left as an adverb, and then becomes a monadic function taking the array to shuffle on the right.

That said, we shuffle as follows. # is the length of the array, so 0,# is a two element list: 0 followed by something nonzero. Then #/:@$ replicates that into a list as long as the input array, and takes its sort vector.

The sort vector of a list is the information for how to sort the list: the (0-based) invdex of the smallest element, followed by the index of the next-smallest, and so on. For example, the sort vector of 0 1 0 1 ... will thus be 0 2 4 ... 1 3 5 ....

If J were now to sort this sort vector, it would Faro-shuffle it; but that would be trivial, since we'd get 0 1 2 3 ... back. So we use dyadic /: to sort the input array as if it were 0 2 4 ... 1 3 5 ..., which Faro-shuffles it.

Example usage below. Try it yourself at tryj.tk!

   1 (/:#/:@$0,#^:) 1 2 3 4 5 6 7 8
1 5 2 6 3 7 4 8

   f =: /:#/:@$0,#^:

   2  f  1 2 3 4 5 6 7 8
1 3 5 7 2 4 6 8

   7  f  _23 _37 52 0 _6 _7 _8 89   NB. "negative 1" is spelled _1
_23 _6 _37 _7 52 _8 0 89

   1  f  0 0 0 0 1 1 1              NB. odd-length lists
0 1 0 1 0 1 0
\$\endgroup\$
5
\$\begingroup\$

Pyth - 8 7 bytes

saved 1 byte thanks to @issacg

usCc2GE

Try it online here.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Hmm... there must be something wrong in the Jelly answer if Pyth beats Jelly. \$\endgroup\$
    – Leaky Nun
    Jun 5, 2016 at 4:26
  • 2
    \$\begingroup\$ Swap the input order and remove the Q to save a byte. There must be something wrong with the Pyth answer if Jelly beats Pyth. :) \$\endgroup\$
    – isaacg
    Jun 5, 2016 at 23:05
  • \$\begingroup\$ @isaacg darn, could've sworn i'd tried that before. Why does that work? shouldn't that hook onto the default for u with None and do fixed point? \$\endgroup\$
    – Maltysen
    Jun 6, 2016 at 0:40
  • \$\begingroup\$ @Maltysen You're right, I think it only happened to work on the one test case I tried. Sorry about that. \$\endgroup\$
    – isaacg
    Jun 6, 2016 at 0:52
  • \$\begingroup\$ @LeakyNun Thanks to @Dennis and @issacg, Pyth and Jelly are now equal (7 bytes). ;D \$\endgroup\$ Jun 8, 2016 at 13:57
3
\$\begingroup\$

Jelly, 9 7 bytes

2 bytes thanks to Dennis!

œs2ZFð¡

Try it online!

Explanation

œs2ZFð¡  Main dyadic chain. Arguments: x,y
      ¡  Repeat the following y time:
œs2          Split into two.
   Z         Transpose.
    F        Flatten.

Previous 9-byte version:

œs2ZF
Ç⁴¡

Try it online!

\$\endgroup\$
0
2
\$\begingroup\$

JavaScript (ES6), 61 51 bytes

(n,a)=>[...a].map((e,i)=>a[(i<<n)%~-a.length||i]=e)

Modifies the input array in place and returns a copy of the original array. If this is unacceptable, &&a can be suffixed to return the modified array. Only works for small values of n due to the limitations of JavaScript's integer arithmetic. 61 60 byte recursive version that works with larger n, based on @Lynn's formula:

f=(n,a,l=a.length)=>n?f(n-1,a.map((_,i)=>a[(i*-~l>>1)%l])):a
\$\endgroup\$
2
\$\begingroup\$

MATL, 11 bytes

w:"tn2/e!1e

Thanks to @Dennis for a correction

Try it online!

Explanation

w         % Take the two inputs N and A. Swap them
:         % Generate [1 2 ... N]
"         % Repeat N times
  tn2/    %   Duplicate A. Number of elements divided by 2
  e       %   Reshape to that number of rows
  !       %   Transpose
  1e      %   Reshape to one row
          % End (implicit)
          % Display (implicit)
\$\endgroup\$
3
  • \$\begingroup\$ Why is the w necessary? \$\endgroup\$
    – David
    Jun 6, 2016 at 0:03
  • \$\begingroup\$ @David That was the correction. Without it, for N = 0 the loop is not entered and the second input is not taken \$\endgroup\$
    – Luis Mendo
    Jun 6, 2016 at 10:26
  • \$\begingroup\$ Ahh that's annoying! \$\endgroup\$
    – David
    Jun 7, 2016 at 1:11
2
\$\begingroup\$

J, 22 19 17 bytes

3 bytes thanks to @Gareth.

2 bytes thanks to @algorithmshark.

-:@#({.,@,.}.)]^:

Usage

>> f =: -:@#({.,@,.}.)]^:
>> 2 f 1 2 3 4 5 6 7 8
<< 1 3 5 7 2 4 6 8

Where >> is STDIN and << is STDOUT.

Previous 22-byte version:

({~[:,/@|:@i.2,-:@#)^:

Usage

>> f =: ({~[:,/@|:@i.2,-:@#)^:
>> 2 f 1 2 3 4 5 6 7 8
<< 1 3 5 7 2 4 6 8

Where >> is STDIN and << is STDOUT.

\$\endgroup\$
4
  • \$\begingroup\$ Because of J's parsing rules, you can drop the outer parens for 2 chars. \$\endgroup\$ Jun 5, 2016 at 16:38
  • \$\begingroup\$ Alternative using a transposed index {~2,@|:@i.@,-:@#^: for 18 bytes. \$\endgroup\$
    – miles
    Jun 5, 2016 at 19:05
  • \$\begingroup\$ Another alternative that uses 17 bytes also [:,@|:]]\~_2%~#^: \$\endgroup\$
    – miles
    Jul 23, 2016 at 18:50
  • \$\begingroup\$ @milesI believe ,@|:@$~2,-:@#^: works for 15 bytes \$\endgroup\$
    – Jonah
    Feb 14, 2019 at 22:26
1
\$\begingroup\$

Mathematica 44 bytes

With 4 bytes saved thanks to @miles.

Riffle@@TakeDrop[#,Length@#/2]&~Nest~##&

Riffle @@ TakeDrop[#, Length@#/2] &~Nest~## &[list, nShuffles] splits the list into two equal sublists and shuffles (Riffles) them.


 Riffle @@ TakeDrop[#, Length@#/2] &~Nest~## &[Range@8, 1]

{1, 5, 2, 6, 3, 7, 4, 8}


Riffle @@ TakeDrop[#, Length@#/2] &~Nest~## &[Range@100, 23]

{1, 30, 59, 88, 18, 47, 76, 6, 35, 64, 93, 23, 52, 81, 11, 40, 69, 98, 28, 57, 86, 16, 45, 74, 4, 33, 62, 91, 21, 50, 79, 9, 38, 67, 96, 26, 55, 84, 14, 43, 72, 2, 31, 60, 89, 19, 48, 77, 7, 36, 65, 94, 24, 53, 82, 12, 41, 70, 99, 29, 58, 87, 17, 46, 75, 5, 34, 63, 92, 22, 51, 80, 10, 39, 68, 97, 27, 56, 85, 15, 44, 73, 3, 32, 61, 90, 20, 49, 78, 8, 37, 66, 95, 25, 54, 83, 13, 42, 71, 100}

\$\endgroup\$
2
  • \$\begingroup\$ Using TakeDrop we can find a solution using 40 bytes as Riffle@@TakeDrop[#,Length@#/2]&~Nest~##& while also taking the sequence ## to be parsed as additional arguments to Nest. \$\endgroup\$
    – miles
    Jun 5, 2016 at 23:34
  • \$\begingroup\$ @miles. Very nice use of TakeDrop. And it is better to use ## to insert the sequence. \$\endgroup\$
    – DavidC
    Jun 6, 2016 at 0:12
1
\$\begingroup\$

APL, 23 21 chars

({⊃,/⍵(↑,¨↓)⍨2÷⍨⍴⍵}⍣N)A

Without the assumption (Thanks to Dennis) and 1 char shorter:

({{∊,⌿2(2÷⍨≢⍵)⍴⍵}⍣⎕)⎕

Try it on online.

\$\endgroup\$
0
1
\$\begingroup\$

java, 109 bytes

int[]f(int[]a,int n){for(int x,q=a.length,d[];0<n--;a=d){d=new int[q];for(x=0;x<q;x++)d[(2*x+2*x/q)%q]=a[x];}return a;}

Explanation: There is a pattern to how the elements move when they are faro shuffled:

let x be the original index

let y be the new index

let L be the length of the array

  • y is double x
  • if x is greater than or equal to half of L then increment y
  • keep y within the array's bounds

or as code: y=(2*x+x/(L/2))%L

This assumes that indicies start at 0. Here's the code further explained:

int[] faroShuffle( int[] array, int numberOfShuffles ) {
    //repeat the faro shuffle n times
    for( int index, length=array.length, destination[]; 0<numberOfShuffles--; array=destination ) {
        //new array to copy over the elements
        destination=new int[length];
        //copy the elements into the new array
        for( index=0; index<length; index++ )
            destination[(2*index+2*index/length)%length]=array[index];
        //at the end of each loop, copy the reference to the new array and use it going forward
    }
    return array;
}  

see ideone for test cases

\$\endgroup\$
1
  • \$\begingroup\$ I know it's been more than a year, but you can golf a few parts: void f(int[]a,int n){for(int x,q=a.length,d[];0<n--;a=d)for(d=new int[q],x=0;x<q;)d[(2*x+2*x/q)%q]=a[x++];} (107 bytes - your current answer is 119 btw, not 109, so -12 bytes). Since you modify the input array, there is no need to return it, so you can change it to a void to reduce bytes. Oh, and if you convert to a Java 8 lambda with currying you could make it even shorter: a->n->{for(int x,q=a.length,d[];0<n--;a=d){d=new int[q];for(x=0;x<q;x++)d[(2*x+2*x/q)%q]=a[x];}} (96 bytes) \$\endgroup\$ Aug 31, 2017 at 14:52
1
\$\begingroup\$

Julia, 45 42 bytes

a\n=n>0?reshape(a,endof(a)÷2,2)'[:]\~-n:a

Try it online!

How it works

We (re)define the binary operator \ for this task. Let a be an array and n a non-negative integer.

If n is positive, we shuffle the array. This is achieved by reshaping it into a matrix of length(a) ÷ 2 rows and two columns. ' transposes the resulting matrix, creating of two rows, then flattening the result with [:]. Since Julia stores matrices in column-major order, this interleaves the two rows.

Afterwards, we call \ recursively with the shuffled a and n - 1 (~-n) as arguments, thus performing additional shuffles. Once n reaches 0, we return the current value of a.

\$\endgroup\$
0
\$\begingroup\$

Pyke, 7 bytes

VDlec,s

Try it here!

V       - Repeat N times:
 D      -  a,b = a (2nd arg first time round)
  le    -  b = len(b)//2
    c   -  a = chunk(a,b)
     ,  -  a = zip(*a)
      s -  a = sum(a, [])
\$\endgroup\$
0
\$\begingroup\$

Actually, 15 bytes

`;l½≈@│t)HZ♂i`n

Try it online!

Explanation:

`;l½≈@│t)HZ♂i`n
`            `n  do the following n times:
 ;l½≈              push half the length of the array
     @             swap
      │            duplicate entire stack
       t)H         last L//2 elements, first L//2 elements
          Z♂i      zip, flatten each element
\$\endgroup\$
0
\$\begingroup\$

Prolog, 116 bytes

a([],[[],[]]).
a([H,I|T],[[H|U],[I|V]]):-a(T,[U,V]).
f(X,0,X).
f(X,N,Y):-N>0,M is N-1,f(X,M,Z),a(Z,[A,B]),append(A,B,Y).

Usage

?- f([1,2,3,4,5,6,7,8],2,X).
X = [1, 5, 2, 6, 3, 7, 4, 8] ;
false.
\$\endgroup\$
0
\$\begingroup\$

Perl 5 -lan, 52 bytes

$a=<>;@F=map@F[$_,@F/2+$_],0..$#F/2while$a--;say"@F"

Try it online!

\$\endgroup\$
0
\$\begingroup\$

PHP, 98 bytes

function($a,$n){while($n--)for($z=count($a)/2;$z;)array_splice($a,$z--,0,array_pop($a));return$a;}

Try it online.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.