Je voudrais obtenir toute la combinaison d'un nombre sans aucune répétition . Comme 0.1.2, 0.2.1, 1.2.0, 1.0.2, 2.0.1, 2.1.0 . J'ai essayé de trouver un schéma facile, mais ne pouvait pas. J'ai dessiné un graphique/arbre pour cela et cela crie d'utiliser la récursivité . Mais j'aimerais le faire sans récursion, si cela est possible.
Quelqu'un peut-il m'aider s'il vous plaît à le faire?
Voici un énumérateur de permutation générique que j'ai écrit il y a un an. Il peut aussi produire des "sous-permutations":
public class PermUtil <T> {
private T[] arr;
private int[] permSwappings;
public PermUtil(T[] arr) {
this(arr,arr.length);
}
public PermUtil(T[] arr, int permSize) {
this.arr = arr.clone();
this.permSwappings = new int[permSize];
for(int i = 0;i < permSwappings.length;i++)
permSwappings[i] = i;
}
public T[] next() {
if (arr == null)
return null;
T[] res = Arrays.copyOf(arr, permSwappings.length);
//Prepare next
int i = permSwappings.length-1;
while (i >= 0 && permSwappings[i] == arr.length - 1) {
swap(i, permSwappings[i]); //Undo the swap represented by permSwappings[i]
permSwappings[i] = i;
i--;
}
if (i < 0)
arr = null;
else {
int prev = permSwappings[i];
swap(i, prev);
int next = prev + 1;
permSwappings[i] = next;
swap(i, next);
}
return res;
}
private void swap(int i, int j) {
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
L’idée de base de mon algorithme est que toute permutation peut être exprimée sous la forme d’une séquence unique de commandes swap. Par exemple, pour <A, B, C>, la séquence de permutation 012 laisse tous les éléments en place, tandis que 122 commence par permuter l'index 0 avec l'index 1, puis permute 1 avec 2, puis permute 2 avec 2 (c'est-à-dire endroit). Cela se traduit par la permutation BCA.
Cette représentation est isomorphe à la représentation de permutation (c'est-à-dire une relation un à un) et il est très facile de "l'incrémenter" lorsqu'elle traverse l'espace de permutations. Pour 4 éléments, il commence à 0123 (ABCD) et se termine à 3333 (DABC).
Vous devriez utiliser le fait que quand vous voulez toutes les permutations de N nombres, il y a N! possibilités. Par conséquent, chaque nombre x de 1..N! code pour une telle permutation. Voici un exemple qui imprime de manière itérative toutes les permutations d’une piqûre.
private static void printPermutationsIterative(String string){
int [] factorials = new int[string.length()+1];
factorials[0] = 1;
for (int i = 1; i<=string.length();i++) {
factorials[i] = factorials[i-1] * i;
}
for (int i = 0; i < factorials[string.length()]; i++) {
String onePermutation="";
String temp = string;
int positionCode = i;
for (int position = string.length(); position > 0 ;position--){
int selected = positionCode / factorials[position-1];
onePermutation += temp.charAt(selected);
positionCode = positionCode % factorials[position-1];
temp = temp.substring(0,selected) + temp.substring(selected+1);
}
System.out.println(onePermutation);
}
}
Il est facile d'écrire la permutation récursive, mais cela nécessite l'exportation des permutations à partir de boucles profondément imbriquées. (C'est un exercice intéressant.) J'avais besoin d'une version qui permutait les chaînes pour les anagrammes. J'ai écrit une version qui implémente Iterable<String>
afin qu'il puisse être utilisé dans des boucles foreach. Il peut facilement être adapté à d'autres types tels que int[]
ou même à un type générique <T[]>
en modifiant le constructeur et le type d'attribut 'array'.
import Java.util.Iterator;
import Java.util.NoSuchElementException;
/**
* An implicit immutable collection of all permutations of a string with an
* iterator over the permutations.<p> implements Iterable<String>
* @see #StringPermutation(String)
*/
public class StringPermutation implements Iterable<String> {
// could implement Collection<String> but it's immutable, so most methods are essentially vacuous
protected final String string;
/**
* Creates an implicit Iterable collection of all permutations of a string
* @param string String to be permuted
* @see Iterable
* @see #iterator
*/
public StringPermutation(String string) {
this.string = string;
}
/**
* Constructs and sequentially returns the permutation values
*/
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
char[] array = string.toCharArray();
int length = string.length();
int[] index = (length == 0) ? null : new int[length];
@Override
public boolean hasNext() {
return index != null;
}
@Override
public String next() {
if (index == null) throw new NoSuchElementException();
for (int i = 1; i < length; ++i) {
char swap = array[i];
System.arraycopy(array, 0, array, 1, i);
array[0] = swap;
for (int j = 1 ; j < i; ++j) {
index[j] = 0;
}
if (++index[i] <= i) {
return new String(array);
}
index[i] = 0;
}
index = null;
return new String(array);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
En général, tout algorithme récursif peut toujours être réduit à un algorithme itératif en utilisant des structures de données en pile ou en file d'attente.
Pour ce problème particulier, il pourrait être plus instructif d'examiner l'algorithme C++ STL std::next_permutation
. Selon Thomas Guest sur wordaligned.org , l'implémentation de base ressemble à ceci:
template<typename Iter>
bool next_permutation(Iter first, Iter last)
{
if (first == last)
return false;
Iter i = first;
++i;
if (i == last)
return false;
i = last;
--i;
for(;;)
{
Iter ii = i;
--i;
if (*i < *ii)
{
Iter j = last;
while (!(*i < *--j))
{}
std::iter_swap(i, j);
std::reverse(ii, last);
return true;
}
if (i == first)
{
std::reverse(first, last);
return false;
}
}
}
Notez qu'il n'utilise pas de récursivité et qu'il est relativement simple de traduire en un autre langage similaire à C, tel que Java. Vous voudrez peut-être lire sur std :: iter_swap , std :: reverse et des itérateurs bidirectionnels (que Iter
représente dans ce code).
Voici les classes génériques et itératives de permutation, kpermutation et générateur de combinaisons que j'ai écrites en fonction des implémentations ici et ici . Mes classes les utilisent comme classes intérieures. Ils implémentent également l'interface Iterable pour être accessible.
List<String> objects = new ArrayList<String>();
objects.add("A");
objects.add("B");
objects.add("C");
Permutations<String> permutations = new Permutations<String>(objects);
for (List<String> permutation : permutations) {
System.out.println(permutation);
}
Combinations<String> combinations = new Combinations<String>(objects, 2);
for (List<String> combination : combinations) {
System.out.println(combination);
}
KPermutations<String> kPermutations = new KPermutations<String>(objects, 2);
for (List<String> kPermutation : kPermutations) {
System.out.println(kPermutation);
}
La classe des combinaisons:
public class Combinations<T> implements Iterable<List<T>> {
CombinationGenerator cGenerator;
T[] elements;
int[] indices;
public Combinations(List<T> list, int n) {
cGenerator = new CombinationGenerator(list.size(), n);
elements = (T[]) list.toArray();
}
public Iterator<List<T>> iterator() {
return new Iterator<List<T>>() {
int pos = 0;
public boolean hasNext() {
return cGenerator.hasMore();
}
public List<T> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
indices = cGenerator.getNext();
List<T> combination = new ArrayList<T>();
for (int i = 0; i < indices.length; i++) {
combination.add(elements[indices[i]]);
}
return combination;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
private final class CombinationGenerator {
private int[] a;
private int n;
private int r;
private BigInteger numLeft;
private BigInteger total;
//------------
// Constructor
//------------
public CombinationGenerator(int n, int r) {
if (n < 1) {
throw new IllegalArgumentException("Set must have at least one element");
}
if (r > n) {
throw new IllegalArgumentException("Subset length can not be greater than set length");
}
this.n = n;
this.r = r;
a = new int[r];
BigInteger nFact = getFactorial(n);
BigInteger rFact = getFactorial(r);
BigInteger nminusrFact = getFactorial(n - r);
total = nFact.divide(rFact.multiply(nminusrFact));
reset();
}
//------
// Reset
//------
public void reset() {
for (int i = 0; i < a.length; i++) {
a[i] = i;
}
numLeft = new BigInteger(total.toString());
}
//------------------------------------------------
// Return number of combinations not yet generated
//------------------------------------------------
public BigInteger getNumLeft() {
return numLeft;
}
//-----------------------------
// Are there more combinations?
//-----------------------------
public boolean hasMore() {
return numLeft.compareTo(BigInteger.ZERO) == 1;
}
//------------------------------------
// Return total number of combinations
//------------------------------------
public BigInteger getTotal() {
return total;
}
//------------------
// Compute factorial
//------------------
private BigInteger getFactorial(int n) {
BigInteger fact = BigInteger.ONE;
for (int i = n; i > 1; i--) {
fact = fact.multiply(new BigInteger(Integer.toString(i)));
}
return fact;
}
//--------------------------------------------------------
// Generate next combination (algorithm from Rosen p. 286)
//--------------------------------------------------------
public int[] getNext() {
if (numLeft.equals(total)) {
numLeft = numLeft.subtract(BigInteger.ONE);
return a;
}
int i = r - 1;
while (a[i] == n - r + i) {
i--;
}
a[i] = a[i] + 1;
for (int j = i + 1; j < r; j++) {
a[j] = a[i] + j - i;
}
numLeft = numLeft.subtract(BigInteger.ONE);
return a;
}
}
}
La classe de permutations:
public class Permutations<T> implements Iterable<List<T>> {
PermutationGenerator pGenerator;
T[] elements;
int[] indices;
public Permutations(List<T> list) {
pGenerator = new PermutationGenerator(list.size());
elements = (T[]) list.toArray();
}
public Iterator<List<T>> iterator() {
return new Iterator<List<T>>() {
int pos = 0;
public boolean hasNext() {
return pGenerator.hasMore();
}
public List<T> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
indices = pGenerator.getNext();
List<T> permutation = new ArrayList<T>();
for (int i = 0; i < indices.length; i++) {
permutation.add(elements[indices[i]]);
}
return permutation;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
private final class PermutationGenerator {
private int[] a;
private BigInteger numLeft;
private BigInteger total;
//-----------------------------------------------------------
// Constructor. WARNING: Don't make n too large.
// Recall that the number of permutations is n!
// which can be very large, even when n is as small as 20 --
// 20! = 2,432,902,008,176,640,000 and
// 21! is too big to fit into a Java long, which is
// why we use BigInteger instead.
//----------------------------------------------------------
public PermutationGenerator(int n) {
if (n < 1) {
throw new IllegalArgumentException("Set must have at least one element");
}
a = new int[n];
total = getFactorial(n);
reset();
}
//------
// Reset
//------
public void reset() {
for (int i = 0; i < a.length; i++) {
a[i] = i;
}
numLeft = new BigInteger(total.toString());
}
//------------------------------------------------
// Return number of permutations not yet generated
//------------------------------------------------
public BigInteger getNumLeft() {
return numLeft;
}
//------------------------------------
// Return total number of permutations
//------------------------------------
public BigInteger getTotal() {
return total;
}
//-----------------------------
// Are there more permutations?
//-----------------------------
public boolean hasMore() {
return numLeft.compareTo(BigInteger.ZERO) == 1;
}
//------------------
// Compute factorial
//------------------
private BigInteger getFactorial(int n) {
BigInteger fact = BigInteger.ONE;
for (int i = n; i > 1; i--) {
fact = fact.multiply(new BigInteger(Integer.toString(i)));
}
return fact;
}
//--------------------------------------------------------
// Generate next permutation (algorithm from Rosen p. 284)
//--------------------------------------------------------
public int[] getNext() {
if (numLeft.equals(total)) {
numLeft = numLeft.subtract(BigInteger.ONE);
return a;
}
int temp;
// Find largest index j with a[j] < a[j+1]
int j = a.length - 2;
while (a[j] > a[j + 1]) {
j--;
}
// Find index k such that a[k] is smallest integer
// greater than a[j] to the right of a[j]
int k = a.length - 1;
while (a[j] > a[k]) {
k--;
}
// Interchange a[j] and a[k]
temp = a[k];
a[k] = a[j];
a[j] = temp;
// Put tail end of permutation after jth position in increasing order
int r = a.length - 1;
int s = j + 1;
while (r > s) {
temp = a[s];
a[s] = a[r];
a[r] = temp;
r--;
s++;
}
numLeft = numLeft.subtract(BigInteger.ONE);
return a;
}
}
}
Et la classe KPermutations qui utilise réellement les classes de permutations et de combinaisons:
public class KPermutations<T> implements Iterable<List<T>> {
Combinations<T> combinations;
public KPermutations(List<T> list, int k) {
if (k<1){
throw new IllegalArgumentException("Subset length k must me at least 1");
}
combinations = new Combinations<T>(list, k);
}
public Iterator<List<T>> iterator() {
return new Iterator<List<T>>() {
Iterator<List<T>> it = combinations.iterator();
Permutations<T> permutations = new Permutations<T>(combinations.iterator().next());
// Has more combinations but no more permutation for current combination
public boolean hasNext() {
if (combinations.iterator().hasNext() && !permutations.iterator().hasNext()){
permutations = new Permutations<T>(combinations.iterator().next());
return true;
}
//Has more permutation for current combination
else if (permutations.iterator().hasNext()){
return true;
}
// No more combination and permutation
return false;
}
public List<T> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return permutations.iterator().next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
Ici, j'ai une solution en scala , qui pourrait être utilisée à partir de Java, mais peut être - avec beaucoup plus de code - également implémentée en Java, pour permettre l’utilisation d’un itérateur pour la boucle for simplifiée:
for (List<Integer> list: permutations)
doSomething (list);
Pour permettre la boucle for simplifiée, nous devons implémenter Iterable, ce qui signifie que nous devons fournir une méthode qui retourne un Iterator, qui se trouve être une autre interface, ce qui signifie que nous devons implémenter 3 méthodes: hasNext (); suivant (); et enlever ();
import Java.util.*;
class PermutationIterator <T> implements Iterator <List <T>> {
private int current = 0;
private final List <T> lilio;
public final long last;
public PermutationIterator (final List <T> llo) {
lilio = llo;
long product = 1;
for (long p = 1; p <= llo.size (); ++p)
product *= p;
last = product;
}
public boolean hasNext () {
return current != last;
}
public List <T> next () {
++current;
return get (current - 1, lilio);
}
public void remove () {
++current;
}
private long fac (long l)
{
for (long i = l - 1L; i > 1L; --i)
l *= i;
return l;
}
/**
new version, which produces permutations in increasing order:
*/
private List <T> get (final long code, final List <T> list) {
if (list.isEmpty ())
return list;
else
{
int len = list.size (); // len = 4
long max = fac (len); // max = 24
long divisor = max / len; // divisor = 6
int i = (int) (code / divisor); // i = 2
List <T> second = new ArrayList <T> (list.size ());
second.addAll (list);
T el = second.remove (i);
List <T> tt = new ArrayList <T> ();
tt.add (el);
tt.addAll (get (code - divisor * i, second));
return tt;
}
}
public List <T> get (final int code)
{
return get (code, lilio);
}
}
class PermutationIterable <T> implements Iterable <List <T>> {
private List <T> lilio;
public PermutationIterable (List <T> llo) {
lilio = llo;
}
public Iterator <List <T>> iterator () {
return new PermutationIterator <T> (lilio);
}
private long invers (final List <T> pattern, final List <T> matcher)
{
if (pattern.isEmpty ())
return 0L;
T first = pattern.get (0);
int idx = matcher.indexOf (first);
long l = (pattern.size () - 1L) * idx;
pattern.remove (0);
matcher.remove (idx);
return l + invers (pattern, matcher);
}
/**
make a deep copy, since the called method will destroy the parameters
*/
public long invers (final List <T> lt)
{
List <T> copy = new ArrayList <T> (lilio.size ());
copy.addAll (lilio);
return invers (lt, copy);
}
}
class PermutationIteratorTest {
public static List <Integer> genList (int... a) {
List <Integer> li = new ArrayList <Integer> ();
for (int i: a)
li.add (i);
return li;
}
public static void main (String[] args) {
List <Integer> il = new ArrayList <Integer> ();
// autoboxing, add '0' to 'z' as Character:
for (int c = 0; c < 3; ++c)
{
il.add (c);
}
PermutationIterable <Integer> pi = new PermutationIterable <Integer> (il);
for (List<Integer> li: pi)
show (li);
System.out.println ("-again-");
// do it a second time:
for (List <Integer> li: pi)
show (li);
// test the inverse:
System.out.println ("for (2,1,0) expecting 5 ?= " + pi.invers (genList (2, 1, 0)));
System.out.println ("for (2,0,1) expecting 4 ?= " + pi.invers (genList (2, 0, 1)));
System.out.println ("for (1,0,2) expecting 3 ?= " + pi.invers (genList (1, 2, 0)));
System.out.println ("for (1,2,0) expecting 2 ?= " + pi.invers (genList (1, 0, 2)));
System.out.println ("for (0,2,1) expecting 1 ?= " + pi.invers (genList (0, 2, 1)));
System.out.println ("for (0,1,2) expecting 0 ?= " + pi.invers (genList (0, 1, 2)));
Random r = new Random ();
PermutationIterator <Integer> pitor = (PermutationIterator <Integer>) pi.iterator ();
for (int i = 0; i < 10; ++i)
{
int rnd = r.nextInt ((int) pitor.last);
List <Integer> rli = pitor.get (rnd);
show (rli);
}
}
public static void show (List <?> lo) {
System.out.print ("(");
for (Object o: lo)
System.out.print (o);
System.out.println (")");
}
}
PermutationIterator contient la méthode publique supplémentaire public List <T> get (final int code)
qui est pratique si vous souhaitez choisir une certaine permutation par index, par exemple, par hasard. Vous connaissez la taille (en dernier) et pouvez donc prendre une permutation de la plage valide par index.
PermutationIterable contient une méthode 'invers' qui générera le contraire: l'indice d'une certaine permutation.
En interne, invers et get fonctionnent de manière récursive, mais toutes les permutations ne sont pas produites de manière récursive. Cela ne devrait donc pas poser de problème, même pour les grandes permutations. Notez que pour 21 éléments, vous dépassez la taille de longs et que 20 étapes de récursivité ne devraient pas poser de problème.
La plupart des exemples que j'ai vus jusqu'à présent ont été soit trop compliqués, n'utilisant que des chaînes ou des swaps. J'ai donc pensé en créer un qui soit itératif, intuitif, générique et swap gratuit.
public static <T> List<List<T>> permutations(List<T> es){
List<List<T>> permutations = new ArrayList<List<T>>();
if(es.isEmpty()){
return permutations;
}
// We add the first element
permutations.add(new ArrayList<T>(Arrays.asList(es.get(0))));
// Then, for all elements e in es (except from the first)
for (int i = 1, len = es.size(); i < len; i++) {
T e = es.get(i);
// We take remove each list l from 'permutations'
for (int j = permutations.size() - 1; j >= 0; j--) {
List<T> l = permutations.remove(j);
// And adds a copy of l, with e inserted at index k for each position k in l
for (int k = l.size(); k >= 0; k--) {
ArrayList<T> ts2 = new ArrayList<>(l);
ts2.add(k, e);
permutations.add(ts2);
}
}
}
return permutations;
}
Exemple: nous voulons toutes les permutations de [a, b, c]
Nous ajoutons a et obtenons [a] // [b, c] restant
Nous prenons un dans la liste et ajoutons [a, b] et [b, a] // [c] restant
On enlève [b, a] et insère [b, a, c], [b, c, a], [c, b, a] puis on enlève [a, b], et insère [a , b, c], [a, c, b], [c, a, b]
Vous pouvez utiliser Factoradics (vous pouvez voir une implémentation ici ) ou le L-algorithme de Knuth qui génère toutes les permutations. Ce qui suit est une implémentation de ce dernier:
public class Perm {
public static void main(String... args) {
final int N = 5;
int[] sequence = new int[N];
for (int i = 0; i < N; i++) {
sequence[i] = i + 1;
}
printSequence(sequence);
permutations(sequence);
}
private static int factorial(int n) {
int fact = 1;
for (int i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
private static void swap(int[] elements, int i, int j) {
int temp = elements[i];
elements[i] = elements[j];
elements[j] = temp;
}
/**
* Reverses the elements of an array (in place) from the start index to the end index
*/
private static void reverse(int[] array, int startIndex, int endIndex) {
int size = endIndex + 1 - startIndex;
int limit = startIndex + size / 2;
for (int i = startIndex; i < limit; i++) {
// swap(array, i, startIndex + (size - 1 - (i - startIndex)));
swap(array, i, 2 * startIndex + size - 1 - i);
}
}
private static void printSequence(int[] sequence) {
for (int i = 0; i < sequence.length; i++) {
System.out.printf("%d, ", sequence[i]);
}
System.out.println();
}
/**
* Implements the Knuth's L-Algorithm permutation algorithm
* modifying the collection in place
*/
private static void permutations(int[] sequence) {
final int N = sequence.length;
// There are n! permutations, but the first permutation is the array without
// modifications, so the number of permutations is n! - 1
int numPermutations = factorial(N) - 1;
// For every possible permutation
for (int n = 0; n < numPermutations; n++) {
// Iterate the array from right to left in search
// of the first couple of elements that are in ascending order
for (int i = N - 1; i >= 1; i--) {
// If the elements i and i - 1 are in ascending order
if (sequence[i - 1] < sequence[i]) {
// Then the index "i - 1" becomes our pivot index
int pivotIndex = i - 1;
// Scan the elements at the right of the pivot (again, from right to left)
// in search of the first element that is bigger
// than the pivot and, if found, swap it
for (int j = N - 1; j > pivotIndex; j--) {
if (sequence[j] > sequence[pivotIndex]) {
swap(sequence, j, pivotIndex);
break;
}
}
// Now reverse the elements from the right of the pivot index
// (this Nice touch to the algorithm avoids the recursion)
reverse(sequence, pivotIndex + 1, N - 1);
break;
}
}
printSequence(sequence);
}
}
}
IEnumerable<IEnumerable<int>> generatePermutations(int length)
{
if (length <= 0) throw new ArgumentException();
var resultCollection = new List<IEnumerable<int>> { new [] { 0 } };
for (var index = 1; index < length; index++)
{
var newResultCollection = new List<IEnumerable<int>>();
foreach (var result in resultCollection)
{
for (var insertIndex = index; insertIndex >= 0; insertIndex--)
{
var list = new List<int>(result);
list.Insert(insertIndex, index);
newResultCollection.Add(list);
}
}
resultCollection = newResultCollection;
}
return resultCollection;
}
Ceci a bien entendu été fait auparavant et une solution est Algorithme de permutation de Bells . Vous trouvez une sollution ici , où vous pouvez trouver une requête récursive dans Prolog et l’algorithme non récursif de permutation de Bell écrit en Pascal.
Les convertir en Java reste un exercice pour le lecteur.