cern.colt.GenericPermuting Maven / Gradle / Ivy
Show all versions of parallelcolt Show documentation
/*
Copyright (C) 1999 CERN - European Organization for Nuclear Research.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose
is hereby granted without fee, provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear in supporting documentation.
CERN makes no representations about the suitability of this software for any purpose.
It is provided "as is" without expressed or implied warranty.
*/
package cern.colt;
/**
* Generically reorders (permutes) arbitrary shaped data (for example, an array,
* three arrays, a 2-d matrix, two linked lists) using an in-place
* swapping algorithm. Imagine having a couple of apples. For some reason you
* decide to reorder them. The green one before the red one. The pale one after
* the shiny one, etc. This class helps to do the job.
*
* This class swaps elements around, in a way that avoids stumbling over its own
* feet: Let before be the generic data before calling the reordering
* method. Let after be the generic data after calling the reordering
* method. Then there holds after[i] == before[indexes[i]].
*
* Similar to {@link GenericSorting}, this class has no idea what kind of data
* it is reordering. It can decide to swap the data at index a with the
* data at index b. It calls a user provided {@link cern.colt.Swapper}
* object that knows how to swap the data of these indexes.
*
* For convenience, some non-generic variants are also provided. Further a
* method to generate the p-th lexicographical permutation indexes.
*
* Example:
*
*
*
*
* Reordering
* [A,B,C,D,E] with indexes [0,4,2,3,1] yields
* [A,E,C,D,B]
* In other words, in the reordered list, we first have the element at old index 0, then the one at old index 4, then the ones at old indexes 2,3,1.
* g[0]<--g[0], g[1]<--g[4], g[2]<--g[2], g[3]<--g[3], g[4]<--g[1].
*
* Reordering
* [A,B,C,D,E] with indexes [0,4,1,2,3] yields
* [A,E,B,C,D]
* In other words g[0]<--g[0], g[1]<--g[4], g[2]<--g[1], g[3]<--g[2], g[4]<--g[3].
*
*
*
*
*
* Here are some example swappers:
*
*
*
*
* // a swapper knows how to swap two indexes (a,b)
*
* // reordering an array
* Swapper swapper = new Swapper() {
* public void swap(int a, int b) {
* String tmp; // reordering String[]
* // int tmp; // reordering int[]
* tmp = array[a];
* array[a] = array[b];
* array[b] = tmp;
* }
* };
*
* // reordering a list
* Swapper swapper = new Swapper() {
* public void swap(int a, int b) {
* Object tmp;
* tmp = list.get(a);
* list.set(a, list.get(b));
* list.set(b, tmp);
* }
* };
*
* // reordering the rows of a 2-d matrix (see {@link cern.colt.matrix})
* Swapper swapper = new Swapper() {
* public void swap(int a, int b) {
* matrix.viewRow(a).swap(matrix.viewRow(b));
* }
* };
*
* // reordering the columns of a 2-d matrix
* Swapper swapper = new Swapper() {
* public void swap(int a, int b) {
* matrix.viewColumn(a).swap(matrix.viewColumn(b));
* }
* };
*
*
*
*
*
* @see cern.colt.Swapper
* @see cern.colt.GenericSorting
*
* @author [email protected]
* @version 1.0, 10-Oct-99
*/
public class GenericPermuting extends Object {
/**
* Makes this class non instantiable, but still let's others inherit from
* it.
*/
protected GenericPermuting() {
}
/**
* Returns the p-th permutation of the sequence
* [0,1,...,N-1]. A small but smart and efficient routine, ported
* from Cernlib. The
* Fortran source. A sequence of N distinct elements has
* N! permutations, which are enumerated in lexicographical order
* 1 .. N!.
*
* This is, for example, useful for Monte-Carlo-tests where one might want
* to compute k distinct and random permutations of a sequence,
* obtaining p from {@link cern.jet.random.tdouble.sampling}
* without replacement or a random engine like
* {@link cern.jet.random.tdouble.engine.DoubleMersenneTwister}.
* Note: When N! exceeds the 64-bit range (i.e. for N > 20
* ), this method has different behaviour: it makes a sequence
* [0,1,...,N-1] and randomizes it, seeded with parameter
* p.
*
* Examples:
*
*
* http://www.hep.net/wwwmirrors/cernlib/CNASDOC/shortwrups_html3/node255.html
* // exactly lexicographically enumerated (ascending)
* permutation(1,3) --> [ 0,1,2 ]
* permutation(2,3) --> [ 0,2,1 ]
* permutation(3,3) --> [ 1,0,2 ]
* permutation(4,3) --> [ 1,2,0 ]
* permutation(5,3) --> [ 2,0,1 ]
* permutation(6,3) --> [ 2,1,0 ]
* permutation(1 ,20) --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
* permutation(2 ,20) --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 18]
* permutation(1000000,20) --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 17, 18, 13, 19, 11, 15, 14, 16, 10]
* permutation(20! -2 ,20) --> [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 1, 2, 0]
* permutation(20! -1 ,20) --> [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 0, 1]
* permutation(20! ,20) --> [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
*
* <br>
* // not exactly enumerated, rather randomly shuffled
* permutation(1,21) --> [18, 20, 11, 0, 15, 1, 19, 13, 3, 6, 16, 17, 9, 5, 12, 4, 7, 14, 8, 10, 2]
* permutation(2,21) --> [1, 9, 4, 16, 14, 13, 11, 20, 10, 8, 18, 0, 15, 3, 17, 5, 12, 2, 6, 7, 19]
* permutation(3,21) --> [12, 0, 19, 1, 20, 5, 8, 16, 6, 14, 2, 4, 3, 17, 11, 13, 9, 10, 15, 18, 7]
*
*
*
* @param p
* the lexicographical ordinal number of the permutation to be
* computed.
* @param N
* the length of the sequence to be generated.
* @return the p-th permutation.
* @throws IllegalArgumentException
* if p < 1 || N < 0 || p > N!.
*/
public static int[] permutation(long p, int N) {
if (p < 1)
throw new IllegalArgumentException("Permutations are enumerated 1 .. N!");
if (N < 0)
throw new IllegalArgumentException("Must satisfy N >= 0");
int[] permutation = new int[N];
if (N > 20) { // factorial(21) would overflow 64-bit long)
// Simply make a list (0,1,..N-1) and randomize it, seeded with "p".
// Note that this is perhaps not what you want...
for (int i = N; --i >= 0;)
permutation[i] = i;
cern.jet.random.tdouble.DoubleUniform gen = new cern.jet.random.tdouble.DoubleUniform(
new cern.jet.random.tdouble.engine.DoubleMersenneTwister((int) p));
for (int i = 0; i < N - 1; i++) {
int random = gen.nextIntFromTo(i, N - 1);
// swap(i, random)
int tmp = permutation[random];
permutation[random] = permutation[i];
permutation[i] = tmp;
}
return permutation;
}
// the normal case - exact enumeration
if (p > cern.jet.math.tdouble.DoubleArithmetic.longFactorial(N))
throw new IllegalArgumentException("N too large (a sequence of N elements only has N! permutations).");
int[] tmp = new int[N];
for (int i = 1; i <= N; i++)
tmp[i - 1] = i;
long io = p - 1;
for (int M = N - 1; M >= 1; M--) {
long fac = cern.jet.math.tdouble.DoubleArithmetic.longFactorial(M);
int in = ((int) (io / fac)) + 1;
io = io % fac;
permutation[N - M - 1] = tmp[in - 1];
for (int j = in; j <= M; j++)
tmp[j - 1] = tmp[j];
}
if (N > 0)
permutation[N - 1] = tmp[0];
for (int i = N; --i >= 0;)
permutation[i] = permutation[i] - 1;
return permutation;
}
/**
* A non-generic variant of reordering, specialized for int[], same
* semantics. Quicker than generic reordering. Also for convenience (forget
* about the Swapper object).
*/
public static void permute(int[] list, int[] indexes) {
int[] copy = list.clone();
for (int i = list.length; --i >= 0;)
list[i] = copy[indexes[i]];
}
/**
* Deprecated. Generically reorders arbitrary shaped generic data g
* such that g[i] == g[indexes[i]]. (The generic data may be one
* array, a 2-d matrix, two linked lists or whatever). This class swaps
* elements around, in a way that avoids stumbling over its own feet.
*
* Example:
*
*
* Reordering
* [A,B,C,D,E] with indexes [0,4,2,3,1] yields
* [A,E,C,D,B]
* In other words g[0]<--g[0], g[1]<--g[4], g[2]<--g[2], g[3]<--g[3], g[4]<--g[1].
*
* Reordering
* [A,B,C,D,E] with indexes [0,4,1,2,3] yields
* [A,E,B,C,D]
* In other words g[0]<--g[0], g[1]<--g[4], g[2]<--g[1], g[3]<--g[2], g[4]<--g[3].
*
*
*
*
*
* @deprecated
* @param indexes
* the permutation indexes.
* @param swapper
* an object that knows how to swap two indexes a,b.
* @param work
* the working storage, must satisfy
* work.length >= indexes.length; set
* work==null if you don't care about performance.
*/
@Deprecated
public static void permute(int[] indexes, cern.colt.Swapper swapper, int[] work) {
permute(indexes, swapper, work, null);
}
/**
* Generically reorders arbitrary shaped generic data g such that
* g[i] == g[indexes[i]]. (The generic data may be one array, a 2-d
* matrix, two linked lists or whatever). This class swaps elements around,
* in a way that avoids stumbling over its own feet.
*
* Example:
*
*
* Reordering
* [A,B,C,D,E] with indexes [0,4,2,3,1] yields
* [A,E,C,D,B]
* In other words g[0]<--g[0], g[1]<--g[4], g[2]<--g[2], g[3]<--g[3], g[4]<--g[1].
*
* Reordering
* [A,B,C,D,E] with indexes [0,4,1,2,3] yields
* [A,E,B,C,D]
* In other words g[0]<--g[0], g[1]<--g[4], g[2]<--g[1], g[3]<--g[2], g[4]<--g[3].
*
*
*
*
*
* @param indexes
* the permutation indexes.
* @param swapper
* an object that knows how to swap two indexes a,b.
* @param work1
* some working storage, must satisfy
* work1.length >= indexes.length; set
* work1==null if you don't care about performance.
* @param work2
* some working storage, must satisfy
* work2.length >= indexes.length; set
* work2==null if you don't care about performance.
*/
public static void permute(int[] indexes, cern.colt.Swapper swapper, int[] work1, int[] work2) {
// "tracks" and "pos" keeps track of the current indexes of the elements
// Example: We have a list==[A,B,C,D,E], indexes==[0,4,1,2,3] and swap B
// and E we need to know that the element formlerly at index 1 is now at
// index 4, and the one formerly at index 4 is now at index 1.
// Otherwise we stumble over our own feet and produce nonsense.
// Initially index i really is at index i, but this will change due to
// swapping.
// work1, work2 to avoid high frequency memalloc's
int s = indexes.length;
int[] tracks = work1;
int[] pos = work2;
if (tracks == null || tracks.length < s)
tracks = new int[s];
if (pos == null || pos.length < s)
pos = new int[s];
for (int i = s; --i >= 0;) {
tracks[i] = i;
pos[i] = i;
}
for (int i = 0; i < s; i++) {
int index = indexes[i];
int track = tracks[index];
if (i != track) {
swapper.swap(i, track);
tracks[index] = i;
tracks[pos[i]] = track;
int tmp = pos[i];
pos[i] = pos[track];
pos[track] = tmp;
}
}
}
/**
* A non-generic variant of reordering, specialized for Object[],
* same semantics. Quicker than generic reordering. Also for convenience
* (forget about the Swapper object).
*/
public static void permute(Object[] list, int[] indexes) {
Object[] copy = list.clone();
for (int i = list.length; --i >= 0;)
list[i] = copy[indexes[i]];
}
}