一种生成满足certian条件的集合的子集的算法

时间:2009-06-01 18:13:13

标签: python algorithm subset

假设给出了一个排序的元素列表,并且我想生成满足某些条件的所有子集,因此如果给定的集合不满足条件,那么更大的子集也将不满足它,并且所有集合都满足元素确实满足它。

例如,给定一个小于100的所有正整数的列表,确定总和小于130的子集:(100,29)(0,1,40),(0)等......

我该怎么做(最好是用Python)?

谢谢! :)

6 个答案:

答案 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);