3838 * manipulate permutations in a variety of ways.
3939 *
4040 * @author <a href=https://www.cicirello.org/ target=_top>Vincent A. Cicirello</a>, <a href=https://www.cicirello.org/ target=_top>https://www.cicirello.org/</a>
41- * @version 1.28 .2021
41+ * @version 3.29 .2021
4242 */
4343public final class Permutation implements Serializable , Iterable <Permutation >, Copyable <Permutation > {
4444
@@ -117,7 +117,6 @@ public Permutation(int n, int value) {
117117 *
118118 * @param n The length of the permutation.
119119 * @param value The integer value of the permutation in the interval: 0..(n!-1).
120- * @since 1.2.5
121120 */
122121 public Permutation (int n , BigInteger value ) {
123122 permutation = new int [n ];
@@ -250,7 +249,6 @@ public int toInteger() {
250249 * the runtime is dominated by the cost of the primitive operations: O(N^2).</p>
251250 *
252251 * @return a mixed radix representation of the permutation
253- * @since 1.2.5
254252 */
255253 public BigInteger toBigInteger () {
256254 int N = permutation .length ;
@@ -291,7 +289,6 @@ public int[] getInverse() {
291289 *
292290 * @return The inverse of the permutation, such that for all i,
293291 * this.get(i) == j iff inverse.get(j) == i.
294- * @since 1.3
295292 */
296293 public Permutation getInversePermutation () {
297294 return new Permutation (getInverse (), false );
@@ -301,7 +298,6 @@ public Permutation getInversePermutation() {
301298 * Inverts the Permutation, such that if p1 is the Permutation immediately
302299 * prior to the call to invert, and if p2 is the Permutation immediately after
303300 * the call to invert, then p1.get(i) == j iff p2.get(j) == i, for all i, j.
304- * @since 1.3
305301 */
306302 public void invert () {
307303 int [] inverse = getInverse ();
@@ -314,22 +310,7 @@ public void invert() {
314310 * the source of efficient random number generation.
315311 */
316312 public void scramble () {
317- if (permutation .length > 0 ) {
318- // Since we're scrambling entire permutation, just generate a new
319- // permutation of integers in [0, n).
320- // Avoid swapping using trick described in Knuth, Vol 2, page 145,
321- // last complete paragraph.
322- permutation [0 ] = 0 ;
323- for (int i = 1 ; i < permutation .length ; i ++) {
324- int j = RandomIndexer .nextInt (i +1 );
325- if (j == i ) {
326- permutation [i ] = i ;
327- } else {
328- permutation [i ] = permutation [j ];
329- permutation [j ] = i ;
330- }
331- }
332- }
313+ scramble (ThreadLocalRandom .current ());
333314 }
334315
335316 /**
@@ -385,24 +366,9 @@ public void scramble(SplittableRandom r) {
385366 *
386367 * @param guaranteeDifferent if true and if permutation length is at least 2, then method
387368 * guarantees that the result is a different permutation than it was originally.
388- * @since 1.4
389369 */
390370 public void scramble (boolean guaranteeDifferent ) {
391- if (guaranteeDifferent ) {
392- boolean changed = false ;
393- for (int i = permutation .length - 1 ; i > 1 ; i --) {
394- int j = RandomIndexer .nextInt (i +1 );
395- if (i != j ) {
396- swap (i ,j );
397- changed = true ;
398- }
399- }
400- if (permutation .length > 1 && (!changed || ThreadLocalRandom .current ().nextBoolean ())) {
401- swap (0 ,1 );
402- }
403- } else {
404- scramble ();
405- }
371+ scramble (ThreadLocalRandom .current (), guaranteeDifferent );
406372 }
407373
408374 /**
@@ -411,7 +377,6 @@ public void scramble(boolean guaranteeDifferent) {
411377 * @param r a source of randomness.
412378 * @param guaranteeDifferent if true and if permutation length is at least 2, then method
413379 * guarantees that the result is a different permutation than it was originally.
414- * @since 1.4
415380 */
416381 public void scramble (Random r , boolean guaranteeDifferent ) {
417382 if (guaranteeDifferent ) {
@@ -437,7 +402,6 @@ public void scramble(Random r, boolean guaranteeDifferent) {
437402 * @param r a source of randomness.
438403 * @param guaranteeDifferent if true and if permutation length is at least 2, then method
439404 * guarantees that the result is a different permutation than it was originally.
440- * @since 1.4
441405 */
442406 public void scramble (SplittableRandom r , boolean guaranteeDifferent ) {
443407 if (guaranteeDifferent ) {
@@ -469,23 +433,7 @@ public void scramble(SplittableRandom r, boolean guaranteeDifferent) {
469433 * or if either i or j are greater than or equal to length()
470434 */
471435 public void scramble (int i , int j ) {
472- if (i ==j ) { return ; }
473- if (i > j ) {
474- int temp = i ;
475- i = j ;
476- j = temp ;
477- }
478- boolean changed = false ;
479- for (int k = j ; k > i + 1 ; k --) {
480- int l = i + RandomIndexer .nextInt (k -i +1 );
481- if (l != k ) {
482- swap (l ,k );
483- changed = true ;
484- }
485- }
486- if (!changed || ThreadLocalRandom .current ().nextBoolean ()) {
487- swap (i ,i +1 );
488- }
436+ scramble (i , j , ThreadLocalRandom .current ());
489437 }
490438
491439 /**
@@ -548,6 +496,74 @@ public void scramble(int i, int j, SplittableRandom r) {
548496 }
549497 }
550498
499+ /**
500+ * Randomly shuffles a non-contiguous set of permutation elements. As long as there
501+ * are at least 2 different indexes passed to this method, it is guaranteed to
502+ * change the Permutation.
503+ * @param indexes An array of indexes into the permutation. This method assumes
504+ * that the indexes are valid indexes into the permutation. That is, it assumes
505+ * that 0 ≤ indexes[i] < this.length().
506+ * @param r source of randomness
507+ * @throws ArrayIndexOutOfBoundsException if any of the indexes[i] are negative or
508+ * greater than or equal to this.length().
509+ */
510+ public void scramble (int [] indexes , SplittableRandom r ) {
511+ if (indexes .length > 1 ) {
512+ boolean changed = false ;
513+ for (int j = indexes .length -1 ; j > 1 ; j --) {
514+ int i = RandomIndexer .nextInt (j +1 , r );
515+ if (i != j ) {
516+ swap (indexes [i ],indexes [j ]);
517+ changed = true ;
518+ }
519+ }
520+ if (!changed || r .nextBoolean ()) {
521+ swap (indexes [0 ],indexes [1 ]);
522+ }
523+ }
524+ }
525+
526+ /**
527+ * Randomly shuffles a non-contiguous set of permutation elements. As long as there
528+ * are at least 2 different indexes passed to this method, it is guaranteed to
529+ * change the Permutation.
530+ * @param indexes An array of indexes into the permutation. This method assumes
531+ * that the indexes are valid indexes into the permutation. That is, it assumes
532+ * that 0 ≤ indexes[i] < this.length().
533+ * @param r source of randomness
534+ * @throws ArrayIndexOutOfBoundsException if any of the indexes[i] are negative or
535+ * greater than or equal to this.length().
536+ */
537+ public void scramble (int [] indexes , Random r ) {
538+ if (indexes .length > 1 ) {
539+ boolean changed = false ;
540+ for (int j = indexes .length -1 ; j > 1 ; j --) {
541+ int i = RandomIndexer .nextInt (j +1 , r );
542+ if (i != j ) {
543+ swap (indexes [i ],indexes [j ]);
544+ changed = true ;
545+ }
546+ }
547+ if (!changed || r .nextBoolean ()) {
548+ swap (indexes [0 ],indexes [1 ]);
549+ }
550+ }
551+ }
552+
553+ /**
554+ * Randomly shuffles a non-contiguous set of permutation elements. As long as there
555+ * are at least 2 different indexes passed to this method, it is guaranteed to
556+ * change the Permutation.
557+ * @param indexes An array of indexes into the permutation. This method assumes
558+ * that the indexes are valid indexes into the permutation. That is, it assumes
559+ * that 0 ≤ indexes[i] < this.length().
560+ * @throws ArrayIndexOutOfBoundsException if any of the indexes[i] are negative or
561+ * greater than or equal to this.length().
562+ */
563+ public void scramble (int [] indexes ) {
564+ scramble (indexes , ThreadLocalRandom .current ());
565+ }
566+
551567 /**
552568 * Retrieves the i'th integer of the permutation.
553569 * @param i the index of the integer to retrieve.
@@ -567,7 +583,6 @@ public int get(int i) {
567583 * @return An array containing the permutation elements from positions i through j, inclusive.
568584 * @throws IllegalArgumentException if j < i
569585 * @throws IndexOutOfBoundsException if i is negative, or j ≥ Permutation.length()
570- * @since 2.0
571586 */
572587 public int [] get (int i , int j ) {
573588 if (j < i ) throw new IllegalArgumentException ("j must not be less than i" );
@@ -585,7 +600,6 @@ public int[] get(int i, int j) {
585600 * @return The array containing the permutation elements from positions i through j, inclusive.
586601 * @throws IllegalArgumentException if j < i
587602 * @throws IndexOutOfBoundsException if i is negative, or j ≥ Permutation.length()
588- * @since 2.0
589603 */
590604 public int [] get (int i , int j , int [] array ) {
591605 if (j < i ) throw new IllegalArgumentException ("j must not be less than i" );
@@ -606,7 +620,6 @@ public int[] get(int i, int j, int[] array) {
606620 * @return an int array containing the Permutation elements in the same order that they appear in
607621 * the Permutation.
608622 *
609- * @since 1.3
610623 */
611624 public int [] toArray () {
612625 return permutation .clone ();
@@ -624,7 +637,6 @@ public int[] toArray() {
624637 * @return an int array containing the Permutation elements in the same order that they appear in
625638 * the Permutation.
626639 *
627- * @since 1.5
628640 */
629641 public int [] toArray (int [] array ) {
630642 if (array == null || array .length != permutation .length ) {
@@ -653,12 +665,35 @@ public int length() {
653665 * or if either i or j are greater than or equal to length()
654666 */
655667 public void swap (int i , int j ) {
656- if (i ==j ) return ;
657668 int temp = permutation [i ];
658669 permutation [i ] = permutation [j ];
659670 permutation [j ] = temp ;
660671 }
661672
673+ /**
674+ * Creates a permutation cycle from a sequence of permutation
675+ * indexes. Let p1 be the permutation before the call to cycle,
676+ * and let p2 be the permutation after the call to cycle. For i
677+ * from 1 to indexes.length - 1,
678+ * p2.get(indexes[i-1])==p1.get(indexes[i]);
679+ * and p2.get(indexes[indexes.length - 1])==p1.get(indexes[0]).
680+ * Note that passing an array containing two indexes to this method
681+ * is equivalent to a {@link #swap}, and passing fewer than 2 indexes
682+ * does nothing.
683+ * @param indexes an array of indexes into the permutation.
684+ * @throws ArrayIndexOutOfBoundsException if there exists any indexes[i]
685+ * ≥ this.length() or indexes[i] < 0.
686+ */
687+ public void cycle (int [] indexes ) {
688+ if (indexes .length > 1 ) {
689+ int temp = permutation [indexes [0 ]];
690+ for (int i = 1 ; i < indexes .length ; i ++) {
691+ permutation [indexes [i -1 ]] = permutation [indexes [i ]];
692+ }
693+ permutation [indexes [indexes .length -1 ]] = temp ;
694+ }
695+ }
696+
662697 /**
663698 * Swaps 2 non-overlapping blocks, where a block is a subsequence.
664699 * @param a Starting index of first block.
@@ -667,7 +702,6 @@ public void swap(int i, int j) {
667702 * @param j Ending index, inclusive, of second block.
668703 * @throws IllegalArgumentException if the following constraint is violated:
669704 * 0 ≤ a ≤ b < i ≤ j < length().
670- * @since 2.0
671705 */
672706 public void swapBlocks (int a , int b , int i , int j ) {
673707 if (a < 0 || b < a || i <= b || j < i || j >= permutation .length ) {
@@ -789,9 +823,9 @@ public void removeAndInsert(int i, int size, int j) {
789823 *
790824 * @param p An array of integers. Each of the integers in the interval [0, p.length)
791825 * must occur exactly one time each.
792- * @throws IllegalArgumentException if p either contains duplicates, or contains any negative elements,
793- * or contains any elements equal or greater than p.length.
794- * @since 1.2.5
826+ * @throws IllegalArgumentException if p either contains duplicates,
827+ * or contains any negative elements,
828+ * or contains any elements equal or greater than p.length.
795829 */
796830 public void set (int [] p ) {
797831 if (p .length != permutation .length ) {
@@ -807,12 +841,14 @@ public void set(int[] p) {
807841 }
808842
809843 /**
810- * Returns an Iterator over all Permutations the length of this Permutation. Iteration begins at this Permutation.
844+ * Returns an Iterator over all Permutations the
845+ * length of this Permutation. Iteration begins at this Permutation.
811846 * This Iterator does not iterate over the integers within the Permutation.
812- * If you do need to iterate over all permutations of a given length, then this method is much more efficient than
813- * using the {@link #Permutation(int,int)} constructor repeatedly incrementing the value passed for the second parameter.
847+ * If you do need to iterate over all permutations
848+ * of a given length, then this method is much more efficient than
849+ * using the {@link #Permutation(int,int)} constructor
850+ * repeatedly incrementing the value passed for the second parameter.
814851 *
815- * @since 1.2
816852 * @return an Iterator
817853 */
818854 @ Override
@@ -898,8 +934,7 @@ public int hashCode() {
898934 * that public method is expected to ensure that the Permutation is fully valid before returning.</p>
899935 *
900936 * @author <a href=https://www.cicirello.org/ target=_top>Vincent A. Cicirello</a>, <a href=https://www.cicirello.org/ target=_top>https://www.cicirello.org/</a>
901- * @version 1.19.5.13
902- * @since 1.3
937+ * @version 3.29.2021
903938 */
904939 public static class Mechanic {
905940
@@ -951,7 +986,6 @@ protected final void set(Permutation p, int index, int value) {
951986 * @param index The index for the start of the new elements.
952987 * @param subpermutation The new elements, which are copied into the permutation beginning
953988 * at position index of the permutation.
954- * @since 2.0
955989 */
956990 protected final void set (Permutation p , int index , int [] subpermutation ) {
957991 System .arraycopy (subpermutation , 0 , p .permutation , index , subpermutation .length );
0 commit comments