たとえば、私はこの配列を持っています:
int a[] = new int[]{3,4,6,2,1};
このようなものであれば、{3,2,1,4,6}
、その他は同じであってはなりません。配列の長さがnの場合、n!の可能な組み合わせがあることを知っています。このアルゴリズムはどのように書けますか?
更新:ありがとう、しかし次のような擬似コードアルゴリズムが必要です。
for(int i=0;i<a.length;i++){
// code here
}
ただのアルゴリズム。はい、API関数は優れていますが、あまり役に立ちません。
C++を使用している場合は、 std::next_permutation
from <algorithm>
ヘッダーファイル:
int a[] = {3,4,6,2,1};
int size = sizeof(a)/sizeof(a[0]);
std::sort(a, a+size);
do {
// print a's elements
} while(std::next_permutation(a, a+size));
10行のコードですべての順列を印刷する方法は次のとおりです。
_public class Permute{
static void permute(Java.util.List<Integer> arr, int k){
for(int i = k; i < arr.size(); i++){
Java.util.Collections.swap(arr, i, k);
permute(arr, k+1);
Java.util.Collections.swap(arr, k, i);
}
if (k == arr.size() -1){
System.out.println(Java.util.Arrays.toString(arr.toArray()));
}
}
public static void main(String[] args){
Permute.permute(Java.util.Arrays.asList(3,4,6,2,1), 0);
}
}
_
配列の最初の要素(k = 0)を取得し、配列の任意の要素(i)と交換します。次に、2番目の要素から始まる配列に順列を再帰的に適用します。このようにして、i番目の要素から始まるすべての順列を取得します。トリッキーな部分は、再帰呼び出しの後、i番目の要素を最初の要素に戻す必要があります。そうしないと、最初の場所で繰り返し値を取得できます。それを元に戻すことにより、要素の順序を復元します(基本的にはバックトラッキングを行います)。
反復値の場合のイテレータと拡張
以前のアルゴリズムの欠点は、再帰的であり、イテレーターとうまく連携しないことです。別の問題は、入力で繰り返し要素を許可すると、そのままでは機能しないことです。
たとえば、入力[3,3,4,4]が与えられると、可能なすべての順列(繰り返しなし)は
_[3, 3, 4, 4]
[3, 4, 3, 4]
[3, 4, 4, 3]
[4, 3, 3, 4]
[4, 3, 4, 3]
[4, 4, 3, 3]
_
(上記のpermute
関数を単純に適用すると、[3,3,4,4]が4回取得されます。これは、この場合に自然に見たいものではありません。そのような置換の数は4!/(2!* 2!)= 6)
このケースを処理するために上記のアルゴリズムを変更することは可能ですが、見た目は良くありません。幸いなことに、繰り返し値を処理し、再帰的ではない、より良いアルゴリズムがあります(私は here を見つけました)。
まず、オブジェクトの配列の順列は、任意の順序で列挙することにより、整数の順列に減らすことができます。
整数配列の順列を取得するには、昇順で並べ替えられた配列から始めます。あなたの「目標」は、それを下降させることです。次の順列を生成するには、シーケンスの下降に失敗した下から最初のインデックスを見つけようとします。この場合、残りの尾の順序を降順から昇順に切り替えながら、そのインデックスの値を改善します。
アルゴリズムの中核は次のとおりです。
_//ind is an array of integers
for(int tail = ind.length - 1;tail > 0;tail--){
if (ind[tail - 1] < ind[tail]){//still increasing
//find last element which does not exceed ind[tail-1]
int s = ind.length - 1;
while(ind[tail-1] >= ind[s])
s--;
swap(ind, tail-1, s);
//reverse order of elements in the tail
for(int i = tail, j = ind.length - 1; i < j; i++, j--){
swap(ind, i, j);
}
break;
}
}
_
イテレータの完全なコードは次のとおりです。コンストラクターはオブジェクトの配列を受け入れ、HashMap
を使用して整数の配列にマッピングします。
_import Java.lang.reflect.Array;
import Java.util.*;
class Permutations<E> implements Iterator<E[]>{
private E[] arr;
private int[] ind;
private boolean has_next;
public E[] output;//next() returns this array, make it public
Permutations(E[] arr){
this.arr = arr.clone();
ind = new int[arr.length];
//convert an array of any elements into array of integers - first occurrence is used to enumerate
Map<E, Integer> hm = new HashMap<E, Integer>();
for(int i = 0; i < arr.length; i++){
Integer n = hm.get(arr[i]);
if (n == null){
hm.put(arr[i], i);
n = i;
}
ind[i] = n.intValue();
}
Arrays.sort(ind);//start with ascending sequence of integers
//output = new E[arr.length]; <-- cannot do in Java with generics, so use reflection
output = (E[]) Array.newInstance(arr.getClass().getComponentType(), arr.length);
has_next = true;
}
public boolean hasNext() {
return has_next;
}
/**
* Computes next permutations. Same array instance is returned every time!
* @return
*/
public E[] next() {
if (!has_next)
throw new NoSuchElementException();
for(int i = 0; i < ind.length; i++){
output[i] = arr[ind[i]];
}
//get next permutation
has_next = false;
for(int tail = ind.length - 1;tail > 0;tail--){
if (ind[tail - 1] < ind[tail]){//still increasing
//find last element which does not exceed ind[tail-1]
int s = ind.length - 1;
while(ind[tail-1] >= ind[s])
s--;
swap(ind, tail-1, s);
//reverse order of elements in the tail
for(int i = tail, j = ind.length - 1; i < j; i++, j--){
swap(ind, i, j);
}
has_next = true;
break;
}
}
return output;
}
private void swap(int[] arr, int i, int j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public void remove() {
}
}
_
使用法/テスト:
_ TCMath.Permutations<Integer> perm = new TCMath.Permutations<Integer>(new Integer[]{3,3,4,4,4,5,5});
int count = 0;
while(perm.hasNext()){
System.out.println(Arrays.toString(perm.next()));
count++;
}
System.out.println("total: " + count);
_
すべての7!/(2!*3!*2!)=210
順列を出力します。
Javaでの順列の実装は次のとおりです。
あなたはそれをチェックする必要があります!
編集:リンクデスから保護するために以下に貼り付けたコード:
// Permute.Java -- A class generating all permutations
import Java.util.Iterator;
import Java.util.NoSuchElementException;
import Java.lang.reflect.Array;
public class Permute implements Iterator {
private final int size;
private final Object [] elements; // copy of original 0 .. size-1
private final Object ar; // array for output, 0 .. size-1
private final int [] permutation; // perm of nums 1..size, perm[0]=0
private boolean next = true;
// int[], double[] array won't work :-(
public Permute (Object [] e) {
size = e.length;
elements = new Object [size]; // not suitable for primitives
System.arraycopy (e, 0, elements, 0, size);
ar = Array.newInstance (e.getClass().getComponentType(), size);
System.arraycopy (e, 0, ar, 0, size);
permutation = new int [size+1];
for (int i=0; i<size+1; i++) {
permutation [i]=i;
}
}
private void formNextPermutation () {
for (int i=0; i<size; i++) {
// i+1 because perm[0] always = 0
// perm[]-1 because the numbers 1..size are being permuted
Array.set (ar, i, elements[permutation[i+1]-1]);
}
}
public boolean hasNext() {
return next;
}
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
private void swap (final int i, final int j) {
final int x = permutation[i];
permutation[i] = permutation [j];
permutation[j] = x;
}
// does not throw NoSuchElement; it wraps around!
public Object next() throws NoSuchElementException {
formNextPermutation (); // copy original elements
int i = size-1;
while (permutation[i]>permutation[i+1]) i--;
if (i==0) {
next = false;
for (int j=0; j<size+1; j++) {
permutation [j]=j;
}
return ar;
}
int j = size;
while (permutation[i]>permutation[j]) j--;
swap (i,j);
int r = size;
int s = i+1;
while (r>s) { swap(r,s); r--; s++; }
return ar;
}
public String toString () {
final int n = Array.getLength(ar);
final StringBuffer sb = new StringBuffer ("[");
for (int j=0; j<n; j++) {
sb.append (Array.get(ar,j).toString());
if (j<n-1) sb.append (",");
}
sb.append("]");
return new String (sb);
}
public static void main (String [] args) {
for (Iterator i = new Permute(args); i.hasNext(); ) {
final String [] a = (String []) i.next();
System.out.println (i);
}
}
}
これは、反復子でラップされたリストの2つの順列
import Java.util.Iterator;
import Java.util.LinkedList;
import Java.util.List;
/* all permutations of two objects
*
* for ABC: AB AC BA BC CA CB
*
* */
public class ListPermutation<T> implements Iterator {
int index = 0;
int current = 0;
List<T> list;
public ListPermutation(List<T> e) {
list = e;
}
public boolean hasNext() {
return !(index == list.size() - 1 && current == list.size() - 1);
}
public List<T> next() {
if(current == index) {
current++;
}
if (current == list.size()) {
current = 0;
index++;
}
List<T> output = new LinkedList<T>();
output.add(list.get(index));
output.add(list.get(current));
current++;
return output;
}
public void remove() {
}
}
がある n!
指定された配列サイズの合計順列n
。以下は、DFSを使用してJavaで記述されたコードです。
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> results = new ArrayList<List<Integer>>();
if (nums == null || nums.length == 0) {
return results;
}
List<Integer> result = new ArrayList<>();
dfs(nums, results, result);
return results;
}
public void dfs(int[] nums, List<List<Integer>> results, List<Integer> result) {
if (nums.length == result.size()) {
List<Integer> temp = new ArrayList<>(result);
results.add(temp);
}
for (int i=0; i<nums.length; i++) {
if (!result.contains(nums[i])) {
result.add(nums[i]);
dfs(nums, results, result);
result.remove(result.size() - 1);
}
}
}
入力配列[3,2,1,4,6]の場合、合計5つあります! = 120の可能な順列:
[[3,4,6,2,1],[3,4,6,1,2],[3,4,2,6,1],[3,4,2,1,6],[3,4,1,6,2],[3,4,1,2,6],[3,6,4,2,1],[3,6,4,1,2],[3,6,2,4,1],[3,6,2,1,4],[3,6,1,4,2],[3,6,1,2,4],[3,2,4,6,1],[3,2,4,1,6],[3,2,6,4,1],[3,2,6,1,4],[3,2,1,4,6],[3,2,1,6,4],[3,1,4,6,2],[3,1,4,2,6],[3,1,6,4,2],[3,1,6,2,4],[3,1,2,4,6],[3,1,2,6,4],[4,3,6,2,1],[4,3,6,1,2],[4,3,2,6,1],[4,3,2,1,6],[4,3,1,6,2],[4,3,1,2,6],[4,6,3,2,1],[4,6,3,1,2],[4,6,2,3,1],[4,6,2,1,3],[4,6,1,3,2],[4,6,1,2,3],[4,2,3,6,1],[4,2,3,1,6],[4,2,6,3,1],[4,2,6,1,3],[4,2,1,3,6],[4,2,1,6,3],[4,1,3,6,2],[4,1,3,2,6],[4,1,6,3,2],[4,1,6,2,3],[4,1,2,3,6],[4,1,2,6,3],[6,3,4,2,1],[6,3,4,1,2],[6,3,2,4,1],[6,3,2,1,4],[6,3,1,4,2],[6,3,1,2,4],[6,4,3,2,1],[6,4,3,1,2],[6,4,2,3,1],[6,4,2,1,3],[6,4,1,3,2],[6,4,1,2,3],[6,2,3,4,1],[6,2,3,1,4],[6,2,4,3,1],[6,2,4,1,3],[6,2,1,3,4],[6,2,1,4,3],[6,1,3,4,2],[6,1,3,2,4],[6,1,4,3,2],[6,1,4,2,3],[6,1,2,3,4],[6,1,2,4,3],[2,3,4,6,1],[2,3,4,1,6],[2,3,6,4,1],[2,3,6,1,4],[2,3,1,4,6],[2,3,1,6,4],[2,4,3,6,1],[2,4,3,1,6],[2,4,6,3,1],[2,4,6,1,3],[2,4,1,3,6],[2,4,1,6,3],[2,6,3,4,1],[2,6,3,1,4],[2,6,4,3,1],[2,6,4,1,3],[2,6,1,3,4],[2,6,1,4,3],[2,1,3,4,6],[2,1,3,6,4],[2,1,4,3,6],[2,1,4,6,3],[2,1,6,3,4],[2,1,6,4,3],[1,3,4,6,2],[1,3,4,2,6],[1,3,6,4,2],[1,3,6,2,4],[1,3,2,4,6],[1,3,2,6,4],[1,4,3,6,2],[1,4,3,2,6],[1,4,6,3,2],[1,4,6,2,3],[1,4,2,3,6],[1,4,2,6,3],[1,6,3,4,2],[1,6,3,2,4],[1,6,4,3,2],[1,6,4,2,3],[1,6,2,3,4],[1,6,2,4,3],[1,2,3,4,6],[1,2,3,6,4],[1,2,4,3,6],[1,2,4,6,3],[1,2,6,3,4],[1,2,6,4,3]]
お役に立てれば。
プリミティブ配列の例:
public static void permute(int[] intArray, int start) {
for(int i = start; i < intArray.length; i++){
int temp = intArray[start];
intArray[start] = intArray[i];
intArray[i] = temp;
permute(intArray, start + 1);
intArray[i] = intArray[start];
intArray[start] = temp;
}
if (start == intArray.length - 1) {
System.out.println(Java.util.Arrays.toString(intArray));
}
}
public static void main(String[] args){
int intArr[] = {1, 2, 3};
permute(intArr, 0);
}
簡単なJava実装、c ++ std::next_permutation
:
public static void main(String[] args){
int[] list = {1,2,3,4,5};
List<List<Integer>> output = new Main().permute(list);
for(List result: output){
System.out.println(result);
}
}
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
int size = factorial(nums.length);
// add the original one to the list
List<Integer> seq = new ArrayList<Integer>();
for(int a:nums){
seq.add(a);
}
list.add(seq);
// generate the next and next permutation and add them to list
for(int i = 0;i < size - 1;i++){
seq = new ArrayList<Integer>();
nextPermutation(nums);
for(int a:nums){
seq.add(a);
}
list.add(seq);
}
return list;
}
int factorial(int n){
return (n==1)?1:n*factorial(n-1);
}
void nextPermutation(int[] nums){
int i = nums.length -1; // start from the end
while(i > 0 && nums[i-1] >= nums[i]){
i--;
}
if(i==0){
reverse(nums,0,nums.length -1 );
}else{
// found the first one not in order
int j = i;
// found just bigger one
while(j < nums.length && nums[j] > nums[i-1]){
j++;
}
//swap(nums[i-1],nums[j-1]);
int tmp = nums[i-1];
nums[i-1] = nums[j-1];
nums[j-1] = tmp;
reverse(nums,i,nums.length-1);
}
}
// reverse the sequence
void reverse(int[] arr,int start, int end){
int tmp;
for(int i = 0; i <= (end - start)/2; i++ ){
tmp = arr[start + i];
arr[start + i] = arr[end - i];
arr[end - i ] = tmp;
}
}
このように...
import Java.util.ArrayList;
import Java.util.Arrays;
public class rohit {
public static void main(String[] args) {
ArrayList<Integer> a=new ArrayList<Integer>();
ArrayList<Integer> b=new ArrayList<Integer>();
b.add(1);
b.add(2);
b.add(3);
permu(a,b);
}
public static void permu(ArrayList<Integer> prefix,ArrayList<Integer> value) {
if(value.size()==0) {
System.out.println(prefix);
} else {
for(int i=0;i<value.size();i++) {
ArrayList<Integer> a=new ArrayList<Integer>();
a.addAll(prefix);
a.add(value.get(i));
ArrayList<Integer> b=new ArrayList<Integer>();
b.addAll(value.subList(0, i));
b.addAll(value.subList(i+1, value.size()));
permu(a,b);
}
}
}
}
Java、テストケース付きの再帰(動的プログラミング)による実装(TestNG).
PrintPermutation.Java
import Java.util.Arrays;
/**
* Print permutation of n elements.
*
* @author eric
* @date Oct 13, 2018 12:28:10 PM
*/
public class PrintPermutation {
/**
* Print permutation of array elements.
*
* @param arr
* @return count of permutation,
*/
public static int permutation(int arr[]) {
return permutation(arr, 0);
}
/**
* Print permutation of part of array elements.
*
* @param arr
* @param n
* start index in array,
* @return count of permutation,
*/
private static int permutation(int arr[], int n) {
int counter = 0;
for (int i = n; i < arr.length; i++) {
swapArrEle(arr, i, n);
counter += permutation(arr, n + 1);
swapArrEle(arr, n, i);
}
if (n == arr.length - 1) {
counter++;
System.out.println(Arrays.toString(arr));
}
return counter;
}
/**
* swap 2 elements in array,
*
* @param arr
* @param i
* @param k
*/
private static void swapArrEle(int arr[], int i, int k) {
int tmp = arr[i];
arr[i] = arr[k];
arr[k] = tmp;
}
}
PrintPermutationTest.Java(TestNG)
を介したテストケース
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* PrintPermutation test.
*
* @author eric
* @date Oct 14, 2018 3:02:23 AM
*/
public class PrintPermutationTest {
@Test
public void test() {
int arr[] = new int[] { 0, 1, 2, 3 };
Assert.assertEquals(PrintPermutation.permutation(arr), 24);
int arrSingle[] = new int[] { 0 };
Assert.assertEquals(PrintPermutation.permutation(arrSingle), 1);
int arrEmpty[] = new int[] {};
Assert.assertEquals(PrintPermutation.permutation(arrEmpty), 0);
}
}
ウィキによると https://en.wikipedia.org/wiki/Heap%27s_algorithm
ヒープのアルゴリズムは、n個のオブジェクトのすべての可能な順列を生成します。これは、1963年にB. R.ヒープによって最初に提案されました。このアルゴリズムは動きを最小限に抑えます。他のn-2個の要素は妨害されません。 1977年の順列生成アルゴリズムのレビューで、Robert Sedgewickは、それがコンピューターによる順列生成のための最も効果的なアルゴリズムであると結論付けました。
したがって、再帰的に実行したい場合、Sudoコードは次のようになります。
procedure generate(n : integer, A : array of any):
if n = 1 then
output(A)
else
for i := 0; i < n - 1; i += 1 do
generate(n - 1, A)
if n is even then
swap(A[i], A[n-1])
else
swap(A[0], A[n-1])
end if
end for
generate(n - 1, A)
end if
Javaコード:
public static void printAllPermutations(
int n, int[] elements, char delimiter) {
if (n == 1) {
printArray(elements, delimiter);
} else {
for (int i = 0; i < n - 1; i++) {
printAllPermutations(n - 1, elements, delimiter);
if (n % 2 == 0) {
swap(elements, i, n - 1);
} else {
swap(elements, 0, n - 1);
}
}
printAllPermutations(n - 1, elements, delimiter);
}
}
private static void printArray(int[] input, char delimiter) {
int i = 0;
for (; i < input.length; i++) {
System.out.print(input[i]);
}
System.out.print(delimiter);
}
private static void swap(int[] input, int a, int b) {
int tmp = input[a];
input[a] = input[b];
input[b] = tmp;
}
public static void main(String[] args) {
int[] input = new int[]{0,1,2,3};
printAllPermutations(input.length, input, ',');
}
3項目再帰ソリューションの視覚的表現: http://www.docdroid.net/ea0s/generatepermutations.pdf.html
壊す: