I’m all for having it in lists FWIW I found it and dumped it down there. The algorithm was a bit hard to understand need time to get into it again. Also this code is from 2013 … and I didn’t get to optimise it for speed
Here is the original algorithm together with the description.
AFAIK: The content of the linked is still the state of the art of functional perfect shuffle implementation.
%% adapted from haskell
%% http://okmij.org/ftp/Haskell/perfect-shuffle.txt
build_tree(List) ->
grow_level([{leaf, X} || X <- List]).
grow_level([Node]) ->
Node;
grow_level(L) ->
grow_level(inner(L)).
inner([]) ->
[];
inner([_]=X) ->
X;
inner([E1,E2|Rest]) ->
[join(E1, E2) | inner(Rest)].
join({leaf, _}=L, {leaf, _}=R) ->
{node, 2, L, R};
join({node, Ct, _, _}=L, {leaf, _}=R) ->
{node, Ct+1, L, R};
join({leaf, _}=L, {node, Ct, _, _}=R) ->
{node, Ct+1, L, R};
join({node, Ctl, _, _}=L, {node, Ctr, _, _}=R) ->
{node, Ctl+Ctr, L, R}.
shuffle(Elements) ->
N = length(Elements),
shuffle(Elements, [ random:uniform(N-I+1)-1 || I <- lists:seq(1,N-1) ]).
shuffle(Elements, Randseq) ->
shuffle1(build_tree(Elements), Randseq).
shuffle0({leaf, E}, []) ->
[E];
shuffle0(Tree, [Ri|Rest]) ->
extract_tree(Ri, Tree, fun(T) -> shuffle0(T, Rest) end).
extract_tree(0, {node, _, {leaf, E}, R}, K) ->
[E|K(R)];
extract_tree(1, {node, 2, {leaf, _}=L, {leaf, R}}, K) ->
[R|K(L)];
extract_tree(N, {node, C, {leaf, _}=L, R}, K) ->
extract_tree(N-1, R, fun(New_r) -> K({node, C-1, L, New_r}) end);
extract_tree(N, {node, N1, L, {leaf, E}}, K) when N+1 == N1 ->
[E|K(L)];
extract_tree(N, {node, C, {node, C1, _, _}=L, R}, K) ->
if N < C1 ->
extract_tree(N, L, fun(New_l) -> K({node, C-1, New_l, R}) end);
true ->
extract_tree(N-C1, R, fun(New_r) -> K({node, C-1, L, New_r}) end)
end.
shuffle1({leaf, E}, []) ->
[E];
shuffle1(Tree, [Ri|Rest]) ->
{E, New} = extract_tree1(Ri, Tree),
[E | shuffle1(New, Rest)].
extract_tree1(0, {node, _, {leaf, E}, R}) ->
{E, R};
extract_tree1(1, {node, 2, {leaf, _}=L, {leaf, E}}) ->
{E, L};
extract_tree1(N, {node, C, {leaf, _}=L, R}) ->
{E, New_r} = extract_tree1(N-1, R),
{E, {node, C-1, L, New_r}};
extract_tree1(N, {node, N1, L, {leaf, E}}) when N+1 == N1 ->
{E, L};
extract_tree1(N, {node, C, {node, C1, _, _}=L, R}) ->
if N < C1 ->
{E, New_l} = extract_tree1(N, L),
{E, {node, C-1, New_l, R}};
true ->
{E, New_r} = extract_tree1(N-C1, R),
{E, {node, C-1, L, New_r}}
end.