如何根据设置的长度对字符串集进行排列,我说一个数组中有50个值,并且只想组合以逗号分隔的24个值示例:(string1,string2,string3),但从未重复组合和顺序。我下面有这段代码。
class Program
{
static void Main(string[] args)
{
//var values1 = new[] { 1, 2, 3, 4, 5 };
//foreach (var permutation in values1.GetPermutations())
//{
// Console.WriteLine(string.Join(", ", permutation));
//}
var values2 = new[] { "asd", "das", "sad", "q1we", "asd" };
foreach (var permutation in values2.GetPermutations())
{
Console.WriteLine(string.Join(",", permutation));
}
Console.ReadLine();
}
}
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;
}
}
如何将字符串数组(24个值)组合成100行唯一的组合?您能解释一下如何做以及什么是最好的方式吗?
答案 0 :(得分:1)
我想我会这样做
public static class StringPermutator
{
/// <summary>
/// Class to permutate input values
/// </summary>
/// <param name="inputValues">An array of inputs to be permutated</param>
/// <param name="numberOfResults">The number of outputs we want to have</param>
/// <param name="maxValuesPerRow">The number of values to be combined in each output row</param>
/// <returns>An IEnumerable of unique permutated string ouptuts</returns>
public static IEnumerable<string> Permutate<T>(T[] inputValues, int numberOfResults, int maxValuesPerRow)
{
HashSet<string> output = new HashSet<string>();
Random rnd = new Random();
//Loop until we have the number of results we want
while (output.Count < numberOfResults)
{
StringBuilder sb = new StringBuilder();
HashSet<int> usedIndexes = new HashSet<int>();
//Loop until we have the right number of values in a single row
while (usedIndexes.Count < maxValuesPerRow)
{
int index = rnd.Next(inputValues.Length);
//Ensure that each index we use is unique and only used once per row
if (usedIndexes.Add(index))
sb.Append(inputValues[index].ToString()).Append(",");
}
sb.Length--; //remove the last comma
output.Add(sb.ToString()); //value is only added if unique
}
return output.ToList();
}
}
您可以这样称呼它
var result = StringPermutator.Permutate(stringValues, 100, 24);
foreach (var permutation in result)
{
Console.WriteLine(string.Join(",", permutation));
}
基本上,该类使用HashSet来确保只能输入唯一的值,因此可以确保我们的输出没有重复,并且我们一直循环直到获得正确数量的生成的输出值为止。
在此循环中,我们随机选择要使用的索引,并确保该索引对于每个输出值也是唯一的,然后再次使用HashSet存储使用的索引并循环,直到将正确数量的值组合到单个输出行。
返回是一个可枚举的列表。 这应该适用于任何类型的输入值,而不仅仅是字符串。
编辑:
仅根据评论进行澄清。 如果没有足够的输入来生成所需数量的排列和行组合,则可能会陷入循环。因此,您应该编写一种方法来突破此限制,但是为了使示例简单起见,我没有这样做。
答案 1 :(得分:0)
Select()
,Distinct()
和Take()
结合使用,您将获得所需的东西:
var results = values2.Permutations(24)
.Select(p => String.Join(",", p))
.Distinct()
.Take(100);
foreach (var permutation in results)
Console.WriteLine(permutation);
其中Permutations()
被实现为扩展方法。这里的数字24表示每个排列应包含多少个项目。它是 n P k 中的k
。
Select()
调用创建一个字符串字符串,其中包含针对特定排列的所有项目。
Distinct()
调用可确保我们仅以唯一的字符串结尾。
Take()
调用限制了我们收集的字符串数量。
这是一个使用递归生成排列的天真的实现:
public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> source, int k)
{
if (k < 0)
throw new ArgumentOutOfRangeException(nameof(k), "May not be negative");
var items = source.ToArray();
if (k > items.Length)
throw new ArgumentOutOfRangeException(nameof(k), "May not be bigger than the number of items in source");
var buffer = new ArraySegment<T>(items, 0, k);
return Permute(0);
IEnumerable<T[]> Permute(int depth)
{
if (depth == k)
{
yield return buffer.ToArray();
yield break;
}
for (int i = depth; i < items.Length; i++)
{
Swap(depth, i);
foreach (var permutation in Permute(depth + 1))
yield return permutation;
Swap(depth, i);
}
}
void Swap(int a, int b)
{
if (a != b)
{
T t = items[a];
items[a] = items[b];
items[b] = t;
}
}
}
我将它留给您,以用您选择的算法替换实现。