最小长度子集的有效幂集算法
本文关键字:有效 算法 子集 | 更新日期: 2023-09-27 18:27:18
我正在使用下面的C#函数来获得一个限制为最小长度子集的幂集
string[] PowerSet(int min_len, string set)
{
IEnumerable<IEnumerable<string>> seed =
new List<IEnumerable<string>>() { Enumerable.Empty<string>() };
return set.Replace(" ", "")
.Split(',')
.Aggregate(seed, (a, b) => a.Concat(a.Select(x => x.Concat(new[] { b }))))
.Where(subset => subset.Count() >= min_len)
.Select(subset => string.Join(",", subset))
.ToArray();
}
问题是,当原始集很大时,即使最小长度也很大,算法也必须非常努力地工作。
例如:
PowerSet(27, "1,11,12,17,22,127,128,135,240,254,277,284,292,296,399,309,322,326,333,439,440,442,447,567,580,590,692,697");
应该很容易,但对于上面的功能来说太长了。我正在寻找一个简洁的修改我的功能,可以有效地处理这些情况。
快速查看您的方法,低效之处之一是创建了每个可能的子集,无论它是否有足够的成员来保证包含在有限的超集中。
请考虑实现以下扩展方法。这种方法可以根据不必要的子集的数量来修剪掉它们,以避免计算量过大。
public static List<List<T>> PowerSet<T>(List<T> startingSet, int minSubsetSize)
{
List<List<T>> subsetList = new List<List<T>>();
//The set bits of each intermediate value represent unique
//combinations from the startingSet.
//We can start checking for combinations at (1<<minSubsetSize)-1 since
//values less than that will not yield large enough subsets.
int iLimit = 1 << startingSet.Count;
for (int i = (1 << minSubsetSize)-1; i < iLimit; i++)
{
//Get the number of 1's in this 'i'
int setBitCount = NumberOfSetBits(i);
//Only include this subset if it will have at least minSubsetSize members.
if (setBitCount >= minSubsetSize)
{
List<T> subset = new List<T>(setBitCount);
for (int j = 0; j < startingSet.Count; j++)
{
//If the j'th bit in i is set,
//then add the j'th element of the startingSet to this subset.
if ((i & (1 << j)) != 0)
{
subset.Add(startingSet[j]);
}
}
subsetList.Add(subset);
}
}
return subsetList;
}
每个增量i
中的集合比特数告诉子集中有多少成员。如果没有足够的设置位,那么创建由位组合表示的子集的工作就没有意义了。CCD_ 2可以通过多种方式来实现。请参阅如何计算32位整数中的集位数?以获得各种方法、解释和参考文献。下面是SO问题的一个例子。
public static int NumberOfSetBits(int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
现在,虽然这个解决方案适用于您的示例,但我认为如果将最小子集大小降低得太远或继续增大startingSet
的大小,您将遇到长运行时间和内存问题。如果你的问题中没有具体的要求,我无法判断这个解决方案是否适合你和/或对你的预期输入案例范围是否安全。
如果发现此解决方案仍然太慢,则可以将操作拆分为并行计算,也许可以使用PLINQ功能。
最后,如果您想用LINQ来修饰扩展方法,它将如下所示。然而,正如所写的,我认为你会看到较慢的性能没有一些改变
public static IEnumerable<List<T>> PowerSet<T>(List<T> startingSet, int minSubsetSize)
{
var startingSetIndexes = Enumerable.Range(0, startingSet.Count).ToList();
var candidates = Enumerable.Range((1 << minSubsetSize)-1, 1 << startingSet.Count)
.Where(p => NumberOfSetBits(p) >= minSubsetSize)
.ToList();
foreach (int p in candidates)
{
yield return startingSetIndexes.Where(setInd => (p & (1 << setInd)) != 0)
.Select(setInd => startingSet[setInd])
.ToList();
}
}