假设给出了一个排序的元素列表,并且我想生成满足某些条件的所有子集,因此如果给定的集合不满足条件,那么更大的子集也将不满足它,并且所有集合都满足元素确实满足它。
例如,给定一个小于100的所有正整数的列表,确定总和小于130的子集:(100,29)(0,1,40),(0)等......
我该怎么做(最好是用Python)?
谢谢! :)
答案 0 :(得分:4)
您可以使用Branch-and-bound技术生成所有子集:您可以以增量方式生成所有子集(生成已确定的子集的超集),使用作为修剪条件“不会探索此分支树如果根不满足约束“。
如果你想在约束方面保持通用,我认为这是最好的策略。
确保以正确的方式编写生成子集的代码,否则您会生成很多时间相同的子集:为了避免因为地图查找而导致内存开销而耗费时间的记忆,您可以以这种方式生成子集:
GetAllSubsets(List objects) {
List generated = {};
GetAllSubsets(generated, [], objects);
return generated;
}
GetAllSubsets(List subsetGenerated, List objectFixed, List objectsToFix) {
GetAllSubsets(subsetGenerated, objectFixed, objectsToFix.sublist(1, objectsToFix.length());
if (satisfy(toCheck = objectsFixed.add(objectsToFix.get(0)))) {
subsetGenerated.add(toCheck);
GetAllSubsets(subsetGenerated, toCheck, objectsToFix.sublist(1, objectsToFix.length());
}
}
实际上,第一次调用GetAllSubsets时添加的子集没有objectsToFix的第一个元素,其中第二个调用添加的子集(如果没有违反修剪条件)具有该元素,因此交集生成的两组子集是空的。
答案 1 :(得分:2)
你可以递归地构造你的集合,从空集开始并尝试添加更多元素,如果其中一个子集(以及它的所有超集)都无法满足条件,则放弃递归执行行。这是一些伪代码,假设一个集合S,其条件满足您希望列出的子集。为方便起见,假设S的元素可以索引为x(0),x(1),x(2),...
EnumerateQualifyingSets(Set T)
{
foreach (x in S with an index larger than the index of any element in T)
{
U = T union {x}
if (U satisfies condition)
{
print U
EnumerateQualifyingSets(U)
}
}
}
第一个调用将是T作为空集。然后,将打印与条件匹配的所有S的子集。这种策略至关重要地依赖于这样一个事实,即不符合条件的S的子集不能包含在其中。
答案 2 :(得分:1)
有一些方法可以做到这一点,但除非你能以某种方式限制条件,否则需要采取O(2 ^ n)步骤。如果您考虑,例如1-100上的条件,其中将选择所有子集(例如,&lt;Σ i i in 1- n < / em>),那么你最终会枚举所有的子集。
你正在看
for i in the powerset of {1-n}
if cond(i)
note that set
您可以通过简单地生成从0到s n -1的所有二进制数来获取集合的powerset,并在位i为1时为子集选择元素i。
答案 3 :(得分:1)
我为类计划生成算法做了类似的事情。我们的日程安排课程有2个元素 - 添加到日程表中的课程列表,以及可添加的课程列表。
伪代码:
queue.add(new schedule(null, available_courses))
while( queue is not empty )
sched = queue.next()
foreach class in sched.available_courses
temp_sched = sched.copy()
temp_sched.add(class)
if(temp_sched.is_valid())
results.add(temp_sched)
queue.add(temp_sched)
这个想法是从一个空的时间表和可用的类列表开始,并在树下搜索有效的时间表(有效的含义符合用户给出的要求,没有时间冲突等)。如果计划无效,它将被丢弃 - 我们不能通过添加类(即修剪树)来使无效的计划有效。
修改它可以很容易地处理你的问题。
答案 4 :(得分:1)
以下是akappa答案的具体示例,使用递归函数生成子集:
def restofsubsets(goodsubset, remainingels, condition):
answers = []
for j in range(len(remainingels)):
nextsubset = goodsubset + remainingels[j:j+1]
if condition(nextsubset):
answers.append(nextsubset)
answers += restofsubsets(nextsubset, remainingels[j+1:], condition)
return answers
#runs slowly
easieranswer = restofsubsets([], range(101), lambda l:sum(l)<40)
#runs much faster due to eliminating big numbers first
fasteranswer = restofsubsets([], range(100,-1,-1), lambda l:sum(l)<40)
#runs extremely slow even with big-numbers-first strategy
finalanswer = restofsubsets([], range(100,-1,-1), lambda l:sum(l)<130)
答案 5 :(得分:0)
我认为在最坏的情况下,您仍然必须生成所有子集并计算每个集合的总和以确定它是否合格。渐近地,它是子集生成过程的成本。
以下是我在javascript中为同一个想法实现的方法。
//this is to generate an array to test
var numbers = (function(start, end){
var result = [],
i = start;
for(; i <= end; i++){
result.push(i);
}
return result;
})(1, 12);
//this is the qualifying function to determine if the generated array is qualified
var fn = (function(maxSum){
return function(set){
var sum = 0;
for(var i = 0 ; i< set.length; i++){
sum += set[i];
if( sum > maxSum ){
return false;
}
}
return true;
}
})(30);
//main function
(function(input, qualifyingFn){
var result, mask, total = Math.pow(2, input.length);
for(mask = 0; mask < total; mask++){
result = [];
sum = 0;
i = input.length - 1;
do{
if( (mask & (1 << i)) !== 0){
result.push(input[i]);
sum += input[i];
if( sum > 30 ){
break;
}
}
}while(i--);
if( qualifyingFn(result) ){
console.log(JSON.stringify(result));
}
}
})(numbers, fn);