% randperm.pl - permute a list randomly, or select randomly from a list
% Version 0.2
% Copyright (C) 2006 Matthew Skala

% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.

% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.

% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% I've used this code in many different projects, most notably Caves of
% Golorp.  It's not a module because it's so small, though it would be easy to
% put it in one.

% The API is:
%
%   random_element(+L,-E)
%   return a randomly selected element from the list L.
%
%   randomly_permute(+I,-O)
%   randomly permute the elements, like shuffling a deck of cards.
%
%   Both predicates are "deterministic" in Prolog's sense of only succeeding
%   once, though they succeed in a random fashion.  If you want to build a
%   failure-driven loop that iterates through the elements of a list in a
%   random order (useful, for instance, if you want to use random variable
%   or value selection in a Davis-Putnam constraint solving algorithm), then
%   do something like:
%      randomly_permute(L,X),member(E,X),...

insert_after_nth([],_,X,[X]):-!.
insert_after_nth(L,0,X,[X|L]):-!.
insert_after_nth([H|T],P,X,[H|NT]):-
   succ(NP,P),!,insert_after_nth(T,NP,X,NT).

random_element([],_,E,E):-!.
random_element([H|T],N,P,E):-
   succ(N,NN),
   (random(NN)=:=0->PP=H;PP=P),
   !,random_element(T,NN,PP,E).
random_element(L,E):-
   random_element(L,0,nothing,E).

% improved, O(n log n) algorithm
randomly_permute([],[]):-!.
randomly_permute([X],[X]):-!.
randomly_permute([X,Y],[X,Y]):-random(2)=:=0,!.
randomly_permute([X,Y],[Y,X]):-!.
randomly_permute(A,B):-
   random_split(A,C,D),randomly_permute(C,E),randomly_permute(D,F),
   (random(2)=:=0->append(E,F,B);append(F,E,B)).

random_split([],[],[]).
random_split([H|A],[H|B],C):-random(2)=:=0,!,random_split(A,B,C).
random_split([H|A],B,[H|C]):-random_split(A,B,C).

