我有一个列表,我想对从2的组合开始的列表元素进行一些操作。
让我们说下面是我的清单:
List<string> strArr = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H" };
如果我们一次选择2个元素,它将生成以下组合: - (A,B)(A,C)(A,D)(A,E)(A,F)(A,G)(A,H)(B,C)(B,D) 等等
如果我们一次选择3个元素,它将生成以下组合: - (A,B,C)(A,B,D)(A,B,E)(A,B,F)(A,B,G)(A,B,H)(A,C,D)( A,C,E)(A,C,F)(A,C,G)(A,C,H)(A,D,E)(A,D,F)(A,D,G)(A ,D,H)(A,E,F)(A,E,G)(A,E,H)(A,F,G)(A,F,H)(A,G,H)(B, C,D)(B,C,E)(B,C,F) 等等
获得这些组合非常简单。我跟着Algorithm to return all combinations of k elements from n 它给了我确切的输出。
但是我不能使用这个代码,因为我有另一个要求,我将继续删除列表中的元素,以防它们满足某些条件,因此组合的数量将继续减少。所以我不想使用LINQ获得所有组合,因为它会妨碍我的性能。
我想到了以下方式:
List<string> strArr = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H" };
// Loop for selecting combination of two elements at time
for (int i = 0; i < strArr.Count; i++)
{
for (int j = i + 1; j < strArr.Count; j++)
{
// Writing on Console
// Actually do some operation to check whether these two elements in list needs to be removed or not
Console.Write(strArr[i] + strArr[j]);
Console.WriteLine();
// Check whether current combination of 2 elements need to be removed or not
if (<< condition >>)
{
// Remove both the current elements
// Remove current element of outer loop
strArr.RemoveAt(i);
// Remove current element of inner loop
// Subtracting one as list size is reduced by 1
strArr.RemoveAt(j - 1);
//
i--;
break;
}
}
}
bool isRemoved = false;
// Loop for selecting combination of three elements at time
for (int i = 0; i < strArr.Count; i++)
{
for (int j = i + 1; j < strArr.Count; j++)
{
for (int k = j + 1; k < s.Count; k++)
{
// Writing on Console
// Actually do some operation to check whether these three elements in list needs to be removed or not
Console.Write(strArr[i] + strArr[j] + strArr[k]);
Console.WriteLine();
// Check whether current combination of 3 elements need to be removed or not
if (<< condition >>)
{
// Remove all the three elements
// Remove current element of outer loop
strArr.RemoveAt(i);
// Remove current element of inner loop
// Subtracting 1 as list size is reduced by 1
strArr.RemoveAt(j - 1);
// Subtracting 2 as list size is reduced by 2
strArr.RemoveAt(k - 2);
isRemoved = true;
i--;
break;
}
// If elements are removed then exit from loop with variable j
if (isRemoved)
{
break;
}
}
}
}
// Now make loop for selecting combination of four elements at time
// and keep removing the elements depending upon condition
删除元素将确保我获得更快的性能,并且我希望执行此操作直到最后到达。我无法思考如何在递归中保持这些深层循环。任何人都可以帮我在递归中添加这些无限循环吗?
感谢您花时间为我编写解决方案,但这不是我想要的......我会在没有代码的情况下简要说明。
我希望您现在了解用例。
任何人都可以帮我实现上述用例吗?
答案 0 :(得分:0)
不确定这是否正是您所需要的,但可能会被视为一种方法。
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
List<Tuple<Expression, Expression>> conditions = new List<Tuple<Expression, Expression>>();
// a complex condition, that the current item contains both "B" and "H"
Expression<Func<IEnumerable<string>, bool>> expression1 = item => item.Contains("B") && item.Contains("H");
// an expression which is used to exclude the elements from the list
Expression<Func<string, bool>> expression2 = j => j != "B" && j != "H";
// associate the condition with the exclusion filter
var condition = new Tuple<Expression, Expression>(expression1, expression2);
conditions.Add(condition);
List<string> strArr = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H" };
IEnumerable<IEnumerable<string>> result = Process(strArr, conditions);
}
private static IEnumerable<IEnumerable<string>> Process(IEnumerable<string> strArr, List<Tuple<Expression, Expression>> conditions)
{
List<IEnumerable<string>> response = new List<IEnumerable<string>>();
int k = 0;
for (int i = 1; i <= strArr.Count(); i++)
{
k++;
var r = strArr.Combinations(Math.Min(strArr.Count(), k));
bool stop=false;
foreach (IEnumerable<string> item in r)
{
if (stop)
{
break;
}
foreach (Tuple<Expression, Expression> condition in conditions)
{
if (Enumerable.Repeat<IEnumerable<string>>(item, 1).Any(Evaluate(condition.Item1) as Func<IEnumerable<string>, bool>))
{
var initialCount = strArr.Count();
strArr = strArr.Where(Evaluate(condition.Item2) as Func<string, bool>);
i -= initialCount - strArr.Count();
stop = true;
break;
}
else
{
foreach (var item1 in r)
{
response.Add(item1);
}
}
}
}
}
return response;
}
public static object Evaluate(Expression e)
{
if (e.NodeType == ExpressionType.Constant)
return ((ConstantExpression)e).Value;
return Expression.Lambda(e).Compile().DynamicInvoke();
}
}
public static class Helper
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c))
);
}
}
}
我已将此answer用作助手。您还可以看到Process
方法与一组条件松散耦合(在此示例中只有一个)。
答案 1 :(得分:0)
以下解决方案将迭代输入列表中所有可能的元素组合,从2个元素的组合开始并从那里向上移动。如果提供的过滤器函数返回true,则从中删除所选元素;因此,随着更多元素被移除,迭代总数减少。结果不会自动跟踪;由呼叫者根据需要跟踪结果。我要遵循的示例用法将演示如何跟踪结果。
public static void PermutateElements<T>(
IEnumerable<T> elements,
Predicate<IEnumerable<T>> filterGroup)
{
var chooseFrom = new LinkedList<T>(elements);
var chosen = new List<T>(chooseFrom.Count);
for (int chooseCount = 2; chooseCount < chooseFrom.Count - 1; chooseCount++)
{
Permutate(chooseFrom, chooseCount, filterGroup, chosen, 0);
}
}
static bool Permutate<T>(LinkedList<T> chooseFrom, int chooseCount,
Predicate<IEnumerable<T>> filterPermutation, IList<T> chosen, int skipLast)
{
int loopCount = chooseFrom.Count;
for (int i = 0; i < loopCount; i++)
{
var choosingNode = chooseFrom.First;
chooseFrom.RemoveFirst();
bool removeChosen = false;
if (i < loopCount - skipLast)
{
chosen.Add(choosingNode.Value);
if (chooseCount == 1)
removeChosen = filterPermutation(chosen);
else
removeChosen = Permutate(chooseFrom, chooseCount - 1, filterPermutation, chosen, skipLast + i);
chosen.RemoveAt(chosen.Count - 1);
}
if (!removeChosen)
chooseFrom.AddLast(choosingNode);
else if (chosen.Count > 0)
return true;
}
return false;
}
下面的例子使用这些函数来组合字母;我们想把字母A到Z并将它们放入任意组,这样每个组包含的元音多于元音,并且至少包含一个元音:
HashSet<char> vowels = new HashSet<char>(new char[] { 'A', 'E', 'I', 'O', 'U', 'Y' });
var results = new List<IEnumerable<char>>();
Predicate<IEnumerable<char>> processGroup = delegate(IEnumerable<char> groupElements)
{
int vowelCount = groupElements.Count(x => vowels.Contains(x));
int consonantCount = groupElements.Count(x => !vowels.Contains(x));
if (vowelCount < consonantCount && vowelCount > 0)
{
results.Add(new List<char>(groupElements));
return true;
}
else
return false;
};
var elements = new char[26];
for (int i = 0; i < elements.Length; i++)
elements[i] = (char)('A' + i);
PermutateElements(elements, processGroup);
结果需要3131次迭代才能完成(比没有删除的所有可能组合的迭代次数少得多)如下:
ABC DEF GHI JKO PQU Vwy的
此时所有的元音都用完了,所以不再需要合法的组合。
答案 2 :(得分:0)
这是我用c ++编写的用于解决类似问题的算法。如果你稍微修改它以便在c#中工作,你应该能够将它用于你的目的。
void r_nCr(const unsigned int &startNum, const unsigned int &bitVal, const unsigned int &testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
{
unsigned int n = (startNum - bitVal) << 1;
n += bitVal ? 1 : 0;
for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
cout << (n >> (i - 1) & 1);
cout << endl;
if (!(n & testNum) && n != startNum)
r_nCr(n, bitVal, testNum);
if (bitVal && bitVal < testNum)
r_nCr(startNum, bitVal >> 1, testNum);
}
您可以看到有关其工作原理的解释here。