답변:
우선 : 물론 재귀 냄새가납니다 !
당신은 또한 그 원리를 알고 싶었 기 때문에 나는 그것을 인간의 언어로 설명하기 위해 최선을 다했습니다. 나는 재귀가 대부분 매우 쉽다고 생각합니다. 두 단계 만 파악하면됩니다.
에서 인간의 언어 :
간단히 말해 :
1. 1 요소의 순열은 하나의 요소입니다.
2. 요소 집합의 순열은 각 요소의 목록이며 다른 요소의 모든 순열과 연결됩니다.예:
세트에 하나의 요소가 있으면
반환하십시오.
파마 (a)-> a집합에 두 개의 문자가있는 경우 : 각 요소에 대해 다음과 같이 나머지 요소의 순열을 추가하여 요소를 반환하십시오.
파마 (ab)->
a + 파마 (b)-> ab
b + 파마 (a)-> ba
또한 : 세트의 각 문자에 대해 :> 세트의 나머지로 연결된 문자를 반환합니다.
파마 (abc)->
a + perm (bc)-> abc , acb
b + 파마 (ac)-> bac , bca
c + perm (ab)-> 택시 , cba
perm (abc ... z)->
a + perm (...), b + perm (....)
....
http://www.programmersheaven.com/mb/Algorithms/369713/369713/permutation-algorithm-help/ 에서 의사 코드 를 찾았습니다 .
makePermutations(permutation) {
if (length permutation < required length) {
for (i = min digit to max digit) {
if (i not in permutation) {
makePermutations(permutation+i)
}
}
}
else {
add permutation to list
}
}
씨#
OK, http://radio.weblogs.com/0111551/stories/2002/10/14/permutations.html 에서보다 정교한 (그리고 c # 태그가 붙어 있기 때문에) 다소 길지만 복사하기로 결정했습니다. 어쨌든 게시물은 원본에 의존하지 않습니다.
이 함수는 문자열을 가져와 정확한 문자열의 가능한 모든 순열을 기록합니다. 예를 들어 "ABC"가 제공된 경우 유출해야합니다.
ABC, ACB, BAC, BCA, CAB, CBA.
암호:
class Program
{
private static void Swap(ref char a, ref char b)
{
if (a == b) return;
var temp = a;
a = b;
b = temp;
}
public static void GetPer(char[] list)
{
int x = list.Length - 1;
GetPer(list, 0, x);
}
private static void GetPer(char[] list, int k, int m)
{
if (k == m)
{
Console.Write(list);
}
else
for (int i = k; i <= m; i++)
{
Swap(ref list[k], ref list[i]);
GetPer(list, k + 1, m);
Swap(ref list[k], ref list[i]);
}
}
static void Main()
{
string str = "sagiv";
char[] arr = str.ToCharArray();
GetPer(arr);
}
}
Swap(ref list[k], ref list[i]);
)은 불필요합니다.
(a[x], a[y]) = (a[y], a[x]);
LINQ를 사용할 수 있으면 두 줄의 코드입니다. 여기에 내 답변을 참조 하십시오 .
편집하다
다음은 T 목록에서 모든 순열 (조합이 아닌)을 반환 할 수있는 일반 함수입니다.
static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
예:
IEnumerable<IEnumerable<int>> result =
GetPermutations(Enumerable.Range(1, 3), 3);
출력-정수 목록 :
{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,1,2} {3,2,1}
이 함수는 LINQ를 사용하므로 .net 3.5 이상이 필요합니다.
const string s = "HALLOWEEN";
var result = GetPermutations(Enumerable.Range(0, s.Length), s.Length).Select(t => t.Select(i => s[i]));
여기에서 해결책을 찾았습니다. Java로 작성되었지만 C #으로 변환했습니다. 도움이 되길 바랍니다.
C #의 코드는 다음과 같습니다.
static void Main(string[] args)
{
string str = "ABC";
char[] charArry = str.ToCharArray();
Permute(charArry, 0, 2);
Console.ReadKey();
}
static void Permute(char[] arry, int i, int n)
{
int j;
if (i==n)
Console.WriteLine(arry);
else
{
for(j = i; j <=n; j++)
{
Swap(ref arry[i],ref arry[j]);
Permute(arry,i+1,n);
Swap(ref arry[i], ref arry[j]); //backtrack
}
}
}
static void Swap(ref char a, ref char b)
{
char tmp;
tmp = a;
a=b;
b = tmp;
}
재귀 는 필요하지 않습니다. 여기 에이 솔루션에 대한 좋은 정보가 있습니다.
var values1 = new[] { 1, 2, 3, 4, 5 };
foreach (var permutation in values1.GetPermutations())
{
Console.WriteLine(string.Join(", ", permutation));
}
var values2 = new[] { 'a', 'b', 'c', 'd', 'e' };
foreach (var permutation in values2.GetPermutations())
{
Console.WriteLine(string.Join(", ", permutation));
}
Console.ReadLine();
나는 수년 동안이 알고리즘을 사용해 왔으며 각 순열 을 계산하기 위해 O (N) 시간 과 공간이 복잡합니다 .
public static class SomeExtensions
{
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> enumerable)
{
var array = enumerable as T[] ?? enumerable.ToArray();
var factorials = Enumerable.Range(0, array.Length + 1)
.Select(Factorial)
.ToArray();
for (var i = 0L; i < factorials[array.Length]; i++)
{
var sequence = GenerateSequence(i, array.Length - 1, factorials);
yield return GeneratePermutation(array, sequence);
}
}
private static IEnumerable<T> GeneratePermutation<T>(T[] array, IReadOnlyList<int> sequence)
{
var clone = (T[]) array.Clone();
for (int i = 0; i < clone.Length - 1; i++)
{
Swap(ref clone[i], ref clone[i + sequence[i]]);
}
return clone;
}
private static int[] GenerateSequence(long number, int size, IReadOnlyList<long> factorials)
{
var sequence = new int[size];
for (var j = 0; j < sequence.Length; j++)
{
var facto = factorials[sequence.Length - j];
sequence[j] = (int)(number / facto);
number = (int)(number % facto);
}
return sequence;
}
static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
private static long Factorial(int n)
{
long result = n;
for (int i = 1; i < n; i++)
{
result = result * i;
}
return result;
}
}
O(N-1)
시퀀스 및 O(N)
스왑에 대해서만 사용 됩니다 O(N)
. : 그리고 난 여전히 같은 하나의 순열 생성하기 위해 생산하지만 리팩토링과 함께이 사용하고 있습니다 . 나는 이것보다 더 나은 성능을 가진 것을 사용하게되어 기쁠 것입니다. 그래서 더 좋은 것에 대한 참조를 무료로 호출하십시오. 대체 대안의 대부분은 메모리에서 사용 하므로 그것을 확인할 수도 있습니다. GetPermutation(i)
0 <= i <= N!-1
O(N!)
void permute (char *str, int ptr) {
int i, len;
len = strlen(str);
if (ptr == len) {
printf ("%s\n", str);
return;
}
for (i = ptr ; i < len ; i++) {
swap (&str[ptr], &str[i]);
permute (str, ptr + 1);
swap (&str[ptr], &str[i]);
}
}
교체 기능을 작성하여 문자를 교체 할 수 있습니다.
이것은 permute (string, 0);
우선, 집합에는 문자열이나 정수가 아닌 순열이 있으므로 "문자열의 문자 집합"을 의미한다고 가정하겠습니다.
크기 n의 집합에는 n이 있습니다! n 순열.
k = 1 ... n으로 호출 된 다음 의사 코드 (Wikipedia에서)! 모든 순열을 제공합니다.
function permutation(k, s) {
for j = 2 to length(s) {
swap s[(k mod j) + 1] with s[j]; // note that our array is indexed starting at 1
k := k / j; // integer division cuts off the remainder
}
return s;
}
다음은 동등한 파이썬 코드입니다 (0 기반 배열 인덱스 용).
def permutation(k, s):
r = s[:]
for j in range(2, len(s)+1):
r[j-1], r[k%j] = r[k%j], r[j-1]
k = k/j+1
return r
k := k / j;
.
C #에서 약간 수정 된 버전으로, 모든 유형의 배열에서 필요한 순열이 생성됩니다.
// USAGE: create an array of any type, and call Permutations()
var vals = new[] {"a", "bb", "ccc"};
foreach (var v in Permutations(vals))
Console.WriteLine(string.Join(",", v)); // Print values separated by comma
public static IEnumerable<T[]> Permutations<T>(T[] values, int fromInd = 0)
{
if (fromInd + 1 == values.Length)
yield return values;
else
{
foreach (var v in Permutations(values, fromInd + 1))
yield return v;
for (var i = fromInd + 1; i < values.Length; i++)
{
SwapValues(values, fromInd, i);
foreach (var v in Permutations(values, fromInd + 1))
yield return v;
SwapValues(values, fromInd, i);
}
}
}
private static void SwapValues<T>(T[] values, int pos1, int pos2)
{
if (pos1 != pos2)
{
T tmp = values[pos1];
values[pos1] = values[pos2];
values[pos2] = tmp;
}
}
Permutations(vals).ToArray()
하면 동일한 배열에 대한 N 참조로 끝납니다. 결과를 저장하려면 수동으로 사본을 작성해야합니다. 예Permutations(values).Select(v => (T[])v.Clone())
class Program
{
public static void Main(string[] args)
{
Permutation("abc");
}
static void Permutation(string rest, string prefix = "")
{
if (string.IsNullOrEmpty(rest)) Console.WriteLine(prefix);
// Each letter has a chance to be permutated
for (int i = 0; i < rest.Length; i++)
{
Permutation(rest.Remove(i, 1), prefix + rest[i]);
}
}
}
간단하기 때문에 FBryant87 접근 방식이 마음에 들었 습니다 . 불행히도, 그것은 많은 다른 "솔루션"처럼 모든 순열을 제공하지는 않습니다. 예를 들어 같은 숫자를 두 번 이상 포함하면 정수입니다. 656123을 예로 들어 보겠습니다. 라인 :
var tail = chars.Except(new List<char>(){c});
모든 발생의 원인이 제외됩니다 사용하여 제거 할, 즉 때 C = 6, 두 자리 숫자가 제거되고 우리가, 내가로 자신을 시도하고 그것을 해결하기로 결정 나는이 문제를 해결 시도 솔루션의 아무도 이후 5123.을 예로 남아 있습니다 FBryant87 의 기본 코드입니다. 이것이 내가 생각해 낸 것입니다.
private static List<string> FindPermutations(string set)
{
var output = new List<string>();
if (set.Length == 1)
{
output.Add(set);
}
else
{
foreach (var c in set)
{
// Remove one occurrence of the char (not all)
var tail = set.Remove(set.IndexOf(c), 1);
foreach (var tailPerms in FindPermutations(tail))
{
output.Add(c + tailPerms);
}
}
}
return output;
}
.Remove 및 .IndexOf를 사용하여 처음 발견 된 항목을 제거하기 만하면됩니다. 적어도 내 용도에 맞는 것으로 작동하는 것 같습니다. 나는 그것이 더 영리해질 수 있다고 확신합니다.
한 가지주의해야 할 점 : 결과 목록에 중복이 포함되어있을 수 있으므로 메소드를 예를 들어 HashSet 대신 리턴하도록하거나 리턴 후 원하는 메소드를 사용하여 중복을 제거해야합니다.
다음 순열을 찾기위한 알고리즘을 포함하여 모든 순열을 찾기위한 세 가지 알고리즘을 다루는 좋은 기사가 있습니다.
http://www.cut-the-knot.org/do_you_know/AllPerm.shtml
C ++ 및 Python에는 각각 next_permutation 및 itertools.permutations 함수 가 내장되어 있습니다.
순전히 기능적인 F # 구현은 다음과 같습니다.
let factorial i =
let rec fact n x =
match n with
| 0 -> 1
| 1 -> x
| _ -> fact (n-1) (x*n)
fact i 1
let swap (arr:'a array) i j = [| for k in 0..(arr.Length-1) -> if k = i then arr.[j] elif k = j then arr.[i] else arr.[k] |]
let rec permutation (k:int,j:int) (r:'a array) =
if j = (r.Length + 1) then r
else permutation (k/j+1, j+1) (swap r (j-1) (k%j))
let permutations (source:'a array) = seq { for k = 0 to (source |> Array.length |> factorial) - 1 do yield permutation (k,2) source }
스왑을 변경하여 CLR 어레이의 가변 특성을 활용하여 성능을 크게 향상시킬 수 있지만이 구현은 소스 어레이와 관련하여 스레드로부터 안전하며 일부 상황에서는 바람직 할 수 있습니다. 또한 16 개 이상의 요소를 가진 배열의 경우, int32 오버플로가 발생하기 때문에 요소 17이 더 큰 / 임의 정밀도를 가진 유형으로 int를 대체해야합니다.
다음은 재귀를 사용하는 C #의 간단한 솔루션입니다.
void Main()
{
string word = "abc";
WordPermuatation("",word);
}
void WordPermuatation(string prefix, string word)
{
int n = word.Length;
if (n == 0) { Console.WriteLine(prefix); }
else
{
for (int i = 0; i < n; i++)
{
WordPermuatation(prefix + word[i],word.Substring(0, i) + word.Substring(i + 1, n - (i+1)));
}
}
}
다음은 입력으로 문자열과 정수 모두에 대한 이해하기 쉬운 순열 함수입니다. 이를 통해 출력 길이를 설정할 수도 있습니다 (일반적인 경우 입력 길이와 동일).
끈
static ICollection<string> result;
public static ICollection<string> GetAllPermutations(string str, int outputLength)
{
result = new List<string>();
MakePermutations(str.ToCharArray(), string.Empty, outputLength);
return result;
}
private static void MakePermutations(
char[] possibleArray,//all chars extracted from input
string permutation,
int outputLength//the length of output)
{
if (permutation.Length < outputLength)
{
for (int i = 0; i < possibleArray.Length; i++)
{
var tempList = possibleArray.ToList<char>();
tempList.RemoveAt(i);
MakePermutations(tempList.ToArray(),
string.Concat(permutation, possibleArray[i]), outputLength);
}
}
else if (!result.Contains(permutation))
result.Add(permutation);
}
과에 대한 정수가 바로 호출 방법 변경 MakePermutations은 () 그대로 남아 :
public static ICollection<int> GetAllPermutations(int input, int outputLength)
{
result = new List<string>();
MakePermutations(input.ToString().ToCharArray(), string.Empty, outputLength);
return result.Select(m => int.Parse(m)).ToList<int>();
}
예 1 : GetAllPermutations ( "abc", 3); "abc" "acb" "bac" "bca" "cab" "cba"
예 2 : GetAllPermutations ( "abcd", 2); "ab" "ac" "ad" "ba" "bc" "bd" "ca" "cb" "cd" "da" "db" "dc"
예 3 : GetAllPermutations (486,2); 48 46 84 86 64 68
다음은 모든 순열을 인쇄하는 기능입니다. 이 함수는 peter이 설명하는 논리를 구현합니다.
public class Permutation
{
//http://www.java2s.com/Tutorial/Java/0100__Class-Definition/RecursivemethodtofindallpermutationsofaString.htm
public static void permuteString(String beginningString, String endingString)
{
if (endingString.Length <= 1)
Console.WriteLine(beginningString + endingString);
else
for (int i = 0; i < endingString.Length; i++)
{
String newString = endingString.Substring(0, i) + endingString.Substring(i + 1);
permuteString(beginningString + endingString.ElementAt(i), newString);
}
}
}
static void Main(string[] args)
{
Permutation.permuteString(String.Empty, "abc");
Console.ReadLine();
}
아래는 순열의 구현입니다. 내가 재미있게하기 위해 변수 이름을 신경 쓰지 마십시오. :)
class combinations
{
static void Main()
{
string choice = "y";
do
{
try
{
Console.WriteLine("Enter word :");
string abc = Console.ReadLine().ToString();
Console.WriteLine("Combinatins for word :");
List<string> final = comb(abc);
int count = 1;
foreach (string s in final)
{
Console.WriteLine("{0} --> {1}", count++, s);
}
Console.WriteLine("Do you wish to continue(y/n)?");
choice = Console.ReadLine().ToString();
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
} while (choice == "y" || choice == "Y");
}
static string swap(string test)
{
return swap(0, 1, test);
}
static List<string> comb(string test)
{
List<string> sec = new List<string>();
List<string> first = new List<string>();
if (test.Length == 1) first.Add(test);
else if (test.Length == 2) { first.Add(test); first.Add(swap(test)); }
else if (test.Length > 2)
{
sec = generateWords(test);
foreach (string s in sec)
{
string init = s.Substring(0, 1);
string restOfbody = s.Substring(1, s.Length - 1);
List<string> third = comb(restOfbody);
foreach (string s1 in third)
{
if (!first.Contains(init + s1)) first.Add(init + s1);
}
}
}
return first;
}
static string ShiftBack(string abc)
{
char[] arr = abc.ToCharArray();
char temp = arr[0];
string wrd = string.Empty;
for (int i = 1; i < arr.Length; i++)
{
wrd += arr[i];
}
wrd += temp;
return wrd;
}
static List<string> generateWords(string test)
{
List<string> final = new List<string>();
if (test.Length == 1)
final.Add(test);
else
{
final.Add(test);
string holdString = test;
while (final.Count < test.Length)
{
holdString = ShiftBack(holdString);
final.Add(holdString);
}
}
return final;
}
static string swap(int currentPosition, int targetPosition, string temp)
{
char[] arr = temp.ToCharArray();
char t = arr[currentPosition];
arr[currentPosition] = arr[targetPosition];
arr[targetPosition] = t;
string word = string.Empty;
for (int i = 0; i < arr.Length; i++)
{
word += arr[i];
}
return word;
}
}
Peter가 말한 인간의 언어 설명 을 보여주는 내가 쓴 높은 수준의 예가 있습니다 .
public List<string> FindPermutations(string input)
{
if (input.Length == 1)
return new List<string> { input };
var perms = new List<string>();
foreach (var c in input)
{
var others = input.Remove(input.IndexOf(c), 1);
perms.AddRange(FindPermutations(others).Select(perm => c + perm));
}
return perms;
}
성능과 메모리가 문제가된다면이 효율적인 구현을 제안합니다. 위키 백과의 힙 알고리즘에 따르면 가장 빠릅니다. 그것이 당신의 필요에 맞길 바랍니다 :-)!
이것을 10의 Linq 구현과 비교하는 것처럼! (코드 포함) :
Linq : 50051 millisecs의 36288000 품목
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace WpfPermutations
{
/// <summary>
/// EO: 2016-04-14
/// Generator of all permutations of an array of anything.
/// Base on Heap's Algorithm. See: https://en.wikipedia.org/wiki/Heap%27s_algorithm#cite_note-3
/// </summary>
public static class Permutations
{
/// <summary>
/// Heap's algorithm to find all pmermutations. Non recursive, more efficient.
/// </summary>
/// <param name="items">Items to permute in each possible ways</param>
/// <param name="funcExecuteAndTellIfShouldStop"></param>
/// <returns>Return true if cancelled</returns>
public static bool ForAllPermutation<T>(T[] items, Func<T[], bool> funcExecuteAndTellIfShouldStop)
{
int countOfItem = items.Length;
if (countOfItem <= 1)
{
return funcExecuteAndTellIfShouldStop(items);
}
var indexes = new int[countOfItem];
for (int i = 0; i < countOfItem; i++)
{
indexes[i] = 0;
}
if (funcExecuteAndTellIfShouldStop(items))
{
return true;
}
for (int i = 1; i < countOfItem;)
{
if (indexes[i] < i)
{ // On the web there is an implementation with a multiplication which should be less efficient.
if ((i & 1) == 1) // if (i % 2 == 1) ... more efficient ??? At least the same.
{
Swap(ref items[i], ref items[indexes[i]]);
}
else
{
Swap(ref items[i], ref items[0]);
}
if (funcExecuteAndTellIfShouldStop(items))
{
return true;
}
indexes[i]++;
i = 1;
}
else
{
indexes[i++] = 0;
}
}
return false;
}
/// <summary>
/// This function is to show a linq way but is far less efficient
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="length"></param>
/// <returns></returns>
static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
/// <summary>
/// Swap 2 elements of same type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="a"></param>
/// <param name="b"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
/// <summary>
/// Func to show how to call. It does a little test for an array of 4 items.
/// </summary>
public static void Test()
{
ForAllPermutation("123".ToCharArray(), (vals) =>
{
Debug.Print(String.Join("", vals));
return false;
});
int[] values = new int[] { 0, 1, 2, 4 };
Debug.Print("Non Linq");
ForAllPermutation(values, (vals) =>
{
Debug.Print(String.Join("", vals));
return false;
});
Debug.Print("Linq");
foreach(var v in GetPermutations(values, values.Length))
{
Debug.Print(String.Join("", v));
}
// Performance
int count = 0;
values = new int[10];
for(int n = 0; n < values.Length; n++)
{
values[n] = n;
}
Stopwatch stopWatch = new Stopwatch();
stopWatch.Reset();
stopWatch.Start();
ForAllPermutation(values, (vals) =>
{
foreach(var v in vals)
{
count++;
}
return false;
});
stopWatch.Stop();
Debug.Print($"Non Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
count = 0;
stopWatch.Reset();
stopWatch.Start();
foreach (var vals in GetPermutations(values, values.Length))
{
foreach (var v in vals)
{
count++;
}
}
stopWatch.Stop();
Debug.Print($"Linq {count} items in {stopWatch.ElapsedMilliseconds} millisecs");
}
}
}
다음은 JavaScript (NodeJS) 솔루션입니다. 주요 아이디어는 한 번에 하나의 요소를 가져 와서 문자열에서 "제거"하고 나머지 문자를 변경하고 요소를 앞에 삽입하는 것입니다.
function perms (string) {
if (string.length == 0) {
return [];
}
if (string.length == 1) {
return [string];
}
var list = [];
for(var i = 0; i < string.length; i++) {
var invariant = string[i];
var rest = string.substr(0, i) + string.substr(i + 1);
var newPerms = perms(rest);
for (var j = 0; j < newPerms.length; j++) {
list.push(invariant + newPerms[j]);
}
}
return list;
}
module.exports = perms;
그리고 여기 테스트가 있습니다 :
require('should');
var permutations = require('../src/perms');
describe('permutations', function () {
it('should permute ""', function () {
permutations('').should.eql([]);
})
it('should permute "1"', function () {
permutations('1').should.eql(['1']);
})
it('should permute "12"', function () {
permutations('12').should.eql(['12', '21']);
})
it('should permute "123"', function () {
var expected = ['123', '132', '321', '213', '231', '312'];
var actual = permutations('123');
expected.forEach(function (e) {
actual.should.containEql(e);
})
})
it('should permute "1234"', function () {
// Wolfram Alpha FTW!
var expected = ['1234', '1243', '1324', '1342', '1423', '1432', '2134', '2143', '2314', '2341', '2413', '2431', '3124', '3142', '3214', '3241', '3412', '3421', '4123', '4132'];
var actual = permutations('1234');
expected.forEach(function (e) {
actual.should.containEql(e);
})
})
})
내가 생각할 수있는 가장 간단한 해결책은 다음과 같습니다.
let rec distribute e = function
| [] -> [[e]]
| x::xs' as xs -> (e::xs)::[for xs in distribute e xs' -> x::xs]
let permute xs = Seq.fold (fun ps x -> List.collect (distribute x) ps) [[]] xs
이 distribute
함수는 새 요소 e
와 n
-element 목록을 가져와 n+1
각각 e
다른 위치에 삽입 된 목록 목록을 반환합니다 . 예를 들어, 10
목록에서 가능한 네 곳 각각에 삽입 [1;2;3]
:
> distribute 10 [1..3];;
val it : int list list =
[[10; 1; 2; 3]; [1; 10; 2; 3]; [1; 2; 10; 3]; [1; 2; 3; 10]]
이 permute
함수는 각 요소를 접고 지금까지 누적 된 순열을 분산시켜 모든 순열을 마무리합니다. 예를 들어, 목록의 6 개의 순열은 다음과 [1;2;3]
같습니다.
> permute [1;2;3];;
val it : int list list =
[[3; 2; 1]; [2; 3; 1]; [2; 1; 3]; [3; 1; 2]; [1; 3; 2]; [1; 2; 3]]
중간 누산기를 유지하기 위해를 fold
로 변경하면 scan
순열이 한 번에 요소를 생성하는 방식에 약간의 빛이 비춰집니다.
> Seq.scan (fun ps x -> List.collect (distribute x) ps) [[]] [1..3];;
val it : seq<int list list> =
seq
[[[]]; [[1]]; [[2; 1]; [1; 2]];
[[3; 2; 1]; [2; 3; 1]; [2; 1; 3]; [3; 1; 2]; [1; 3; 2]; [1; 2; 3]]]
문자열의 순열을 나열합니다. 문자가 반복 될 때 복제를 피합니다.
using System;
using System.Collections;
class Permutation{
static IEnumerable Permutations(string word){
if (word == null || word.Length <= 1) {
yield return word;
yield break;
}
char firstChar = word[0];
foreach( string subPermute in Permutations (word.Substring (1)) ) {
int indexOfFirstChar = subPermute.IndexOf (firstChar);
if (indexOfFirstChar == -1) indexOfFirstChar = subPermute.Length;
for( int index = 0; index <= indexOfFirstChar; index++ )
yield return subPermute.Insert (index, new string (firstChar, 1));
}
}
static void Main(){
foreach( var permutation in Permutations ("aab") )
Console.WriteLine (permutation);
}
}
@Peter의 솔루션을 기반으로 다음 Permutations()
은 모든 LINQ 스타일 확장 방법을 선언하는 버전입니다.IEnumerable<T>
있습니다.
사용법 (문자열 문자 예) :
foreach (var permutation in "abc".Permutations())
{
Console.WriteLine(string.Join(", ", permutation));
}
출력 :
a, b, c
a, c, b
b, a, c
b, c, a
c, b, a
c, a, b
또는 다른 컬렉션 유형에서 :
foreach (var permutation in (new[] { "Apples", "Oranges", "Pears"}).Permutations())
{
Console.WriteLine(string.Join(", ", permutation));
}
출력 :
Apples, Oranges, Pears
Apples, Pears, Oranges
Oranges, Apples, Pears
Oranges, Pears, Apples
Pears, Oranges, Apples
Pears, Apples, Oranges
using System;
using System.Collections.Generic;
using System.Linq;
public static class PermutationExtension
{
public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> source)
{
var sourceArray = source.ToArray();
var results = new List<T[]>();
Permute(sourceArray, 0, sourceArray.Length - 1, results);
return results;
}
private static void Swap<T>(ref T a, ref T b)
{
T tmp = a;
a = b;
b = tmp;
}
private static void Permute<T>(T[] elements, int recursionDepth, int maxDepth, ICollection<T[]> results)
{
if (recursionDepth == maxDepth)
{
results.Add(elements.ToArray());
return;
}
for (var i = recursionDepth; i <= maxDepth; i++)
{
Swap(ref elements[recursionDepth], ref elements[i]);
Permute(elements, recursionDepth + 1, maxDepth, results);
Swap(ref elements[recursionDepth], ref elements[i]);
}
}
}
다음은 모든 순열을 재귀 적으로 인쇄하는 함수입니다.
public void Permutations(string input, StringBuilder sb)
{
if (sb.Length == input.Length)
{
Console.WriteLine(sb.ToString());
return;
}
char[] inChar = input.ToCharArray();
for (int i = 0; i < input.Length; i++)
{
if (!sb.ToString().Contains(inChar[i]))
{
sb.Append(inChar[i]);
Permutations(input, sb);
RemoveChar(sb, inChar[i]);
}
}
}
private bool RemoveChar(StringBuilder input, char toRemove)
{
int index = input.ToString().IndexOf(toRemove);
if (index >= 0)
{
input.Remove(index, 1);
return true;
}
return false;
}
class Permutation
{
public static List<string> Permutate(string seed, List<string> lstsList)
{
loopCounter = 0;
// string s="\w{0,2}";
var lstStrs = PermuateRecursive(seed);
Trace.WriteLine("Loop counter :" + loopCounter);
return lstStrs;
}
// Recursive function to find permutation
private static List<string> PermuateRecursive(string seed)
{
List<string> lstStrs = new List<string>();
if (seed.Length > 2)
{
for (int i = 0; i < seed.Length; i++)
{
str = Swap(seed, 0, i);
PermuateRecursive(str.Substring(1, str.Length - 1)).ForEach(
s =>
{
lstStrs.Add(str[0] + s);
loopCounter++;
});
;
}
}
else
{
lstStrs.Add(seed);
lstStrs.Add(Swap(seed, 0, 1));
}
return lstStrs;
}
//Loop counter variable to count total number of loop execution in various functions
private static int loopCounter = 0;
//Non recursive version of permuation function
public static List<string> Permutate(string seed)
{
loopCounter = 0;
List<string> strList = new List<string>();
strList.Add(seed);
for (int i = 0; i < seed.Length; i++)
{
int count = strList.Count;
for (int j = i + 1; j < seed.Length; j++)
{
for (int k = 0; k < count; k++)
{
strList.Add(Swap(strList[k], i, j));
loopCounter++;
}
}
}
Trace.WriteLine("Loop counter :" + loopCounter);
return strList;
}
private static string Swap(string seed, int p, int p2)
{
Char[] chars = seed.ToCharArray();
char temp = chars[p2];
chars[p2] = chars[p];
chars[p] = temp;
return new string(chars);
}
}
다음은 약간 단순화 된 C # 답변입니다.
public static void StringPermutationsDemo()
{
strBldr = new StringBuilder();
string result = Permute("ABCD".ToCharArray(), 0);
MessageBox.Show(result);
}
static string Permute(char[] elementsList, int startIndex)
{
if (startIndex == elementsList.Length)
{
foreach (char element in elementsList)
{
strBldr.Append(" " + element);
}
strBldr.AppendLine("");
}
else
{
for (int tempIndex = startIndex; tempIndex <= elementsList.Length - 1; tempIndex++)
{
Swap(ref elementsList[startIndex], ref elementsList[tempIndex]);
Permute(elementsList, (startIndex + 1));
Swap(ref elementsList[startIndex], ref elementsList[tempIndex]);
}
}
return strBldr.ToString();
}
static void Swap(ref char Char1, ref char Char2)
{
char tempElement = Char1;
Char1 = Char2;
Char2 = tempElement;
}
산출:
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
3 1 2
이것은 내가 이해하기 쉬운 솔루션입니다.
class ClassicPermutationProblem
{
ClassicPermutationProblem() { }
private static void PopulatePosition<T>(List<List<T>> finalList, List<T> list, List<T> temp, int position)
{
foreach (T element in list)
{
List<T> currentTemp = temp.ToList();
if (!currentTemp.Contains(element))
currentTemp.Add(element);
else
continue;
if (position == list.Count)
finalList.Add(currentTemp);
else
PopulatePosition(finalList, list, currentTemp, position + 1);
}
}
public static List<List<int>> GetPermutations(List<int> list)
{
List<List<int>> results = new List<List<int>>();
PopulatePosition(results, list, new List<int>(), 1);
return results;
}
}
static void Main(string[] args)
{
List<List<int>> results = ClassicPermutationProblem.GetPermutations(new List<int>() { 1, 2, 3 });
}
다음은 언급 된 알고리즘의 구현입니다.
public class Program
{
public static void Main(string[] args)
{
string str = "abcefgh";
var astr = new Permutation().GenerateFor(str);
Console.WriteLine(astr.Length);
foreach(var a in astr)
{
Console.WriteLine(a);
}
//a.ForEach(Console.WriteLine);
}
}
class Permutation
{
public string[] GenerateFor(string s)
{
if(s.Length == 1)
{
return new []{s};
}
else if(s.Length == 2)
{
return new []{s[1].ToString()+s[0].ToString(),s[0].ToString()+s[1].ToString()};
}
var comb = new List<string>();
foreach(var c in s)
{
string cStr = c.ToString();
var sToProcess = s.Replace(cStr,"");
if (!string.IsNullOrEmpty(sToProcess) && sToProcess.Length>0)
{
var conCatStr = GenerateFor(sToProcess);
foreach(var a in conCatStr)
{
comb.Add(c.ToString()+a);
}
}
}
return comb.ToArray();
}
}
new Permutation().GenerateFor("aba")
출력string[4] { "ab", "baa", "baa", "ab" }
//Generic C# Method
private static List<T[]> GetPerms<T>(T[] input, int startIndex = 0)
{
var perms = new List<T[]>();
var l = input.Length - 1;
if (l == startIndex)
perms.Add(input);
else
{
for (int i = startIndex; i <= l; i++)
{
var copy = input.ToArray(); //make copy
var temp = copy[startIndex];
copy[startIndex] = copy[i];
copy[i] = temp;
perms.AddRange(GetPerms(copy, startIndex + 1));
}
}
return perms;
}
//usages
char[] charArray = new char[] { 'A', 'B', 'C' };
var charPerms = GetPerms(charArray);
string[] stringArray = new string[] { "Orange", "Mango", "Apple" };
var stringPerms = GetPerms(stringArray);
int[] intArray = new int[] { 1, 2, 3 };
var intPerms = GetPerms(intArray);
/// <summary>
/// Print All the Permutations.
/// </summary>
/// <param name="inputStr">input string</param>
/// <param name="strLength">length of the string</param>
/// <param name="outputStr">output string</param>
private void PrintAllPermutations(string inputStr, int strLength,string outputStr, int NumberOfChars)
{
//Means you have completed a permutation.
if (outputStr.Length == NumberOfChars)
{
Console.WriteLine(outputStr);
return;
}
//For loop is used to print permutations starting with every character. first print all the permutations starting with a,then b, etc.
for(int i=0 ; i< strLength; i++)
{
// Recursive call : for a string abc = a + perm(bc). b+ perm(ac) etc.
PrintAllPermutations(inputStr.Remove(i, 1), strLength - 1, outputStr + inputStr.Substring(i, 1), 4);
}
}