Algorithm 求给定条件集的最小子集数的算法

Algorithm 求给定条件集的最小子集数的算法,algorithm,subset,Algorithm,Subset,对于给定的一组数{12,13,15,21,22,26,6,14,27,28,29,30,39,40,4,17,25},我想找到满足这两个条件的最小子集数 每个集合中的元素数是一个常数,即上述值 示例是5 在子集中,两个元素中的任何一个满足以下条件: (num1-num2)%d!=0num1>num2其中d是常数差 在两个数字之间 对于上面的示例:如果d=4且子集中的元素数为5,则其中一个子集将为:{12,13,15,22,17} 我正在寻找一种算法,以找到满足条件的最小子集数。好的,所以这不是真

对于给定的一组数
{12,13,15,21,22,26,6,14,27,28,29,30,39,40,4,17,25}
,我想找到满足这两个条件的最小子集数

  • 每个集合中的元素数是一个常数,即上述值 示例是
    5
  • 在子集中,两个元素中的任何一个满足以下条件:
    (num1-num2)%d!=0
    num1>num2
    其中d是常数差 在两个数字之间
  • 对于上面的示例:如果
    d=4
    且子集中的元素数为
    5
    ,则其中一个子集将为:
    {12,13,15,22,17}


    我正在寻找一种算法,以找到满足条件的最小子集数。

    好的,所以这不是真正的100%编程。这是一个标准的离散数学练习

    让我们分步骤进行:

  • 所有可能子集的组合数量:n其中n是第一组元素的数量。我在这里假设我们不允许在子集中重复选定的元素。子集中元素的顺序无关紧要(否则我们需要使用组合而不是排列)
  • 现在我们只选择r元素,而不是所有n元素,所以现在是n/(右)其中r是子集中的元素数量
  • 最难的部分完成了。现在我们需要添加一个条件,对于该条件,您只能使用前面的n/(右)并减去“坏”元素对的数量。基本上,排除任何以禁止值开始的排列。让我们找到所有禁止的值:
  • 两个简单的嵌套循环,检查(num1-num2)%d!=每对0,大约需要n*(n-1)/2次迭代。或者只进行n^2次迭代,而不动态更改嵌套循环中的迭代器

  • 你可以贪婪地解决这个问题

    将集合划分为S[i],其中初始S[i]={S在S中,这样S%d==i}

    然后,重复选取k(在您的示例中为5)最大的子集S[i],并从每个子集中移除一个元素

    下面是一些实现此功能的效率稍低但简单的代码:

    def part(S, k, d):
        "Split S into subsets of size<=d, each with elements unique mod k"
        parts = [[] for _ in xrange(k)]
        for s in S:
            parts[s % k].append(s)
        while sum(len(p) for p in parts):
            parts.sort(key=len, reverse=True)
            yield [x.pop() for x in parts[:d] if x]
    
    S = [12,13,15,21,22,26,6,14,27,28,29,30,39,40,4,17,25]
    for s in part(S, 6, 5):
        print sorted(s)
    
    通过优先级队列和运行计数器,可以优化
    部分
    和while循环条件,但这会掩盖正在发生的事情。优化后的表单将在O(n logn)时间内运行;如前所述,它是O(n^2 log n)


    [我应该说,我非常确定这个解决方案是正确的,但我不太明白如何证明它。我想看看证明(或反例来证明它是错误的)。]

    我会这样做:

    def run(self, n,m):
            if len(n)<=1:
            return [n]             
            x = n[0:1]  //get 1st part
            y = n[1:]   //get remaining
            t = self.run(y,m)
            r=[]
            if t:
                r+=[x]  //add current element
                r+=[i for i in t if count(i)<=m]    // add fixed combinations
                r+=[ i+x for i in t if valid(i+x)]  // all combined combinations
            return r
    def valid(combination):
        //code to check your condition
        (num1-num2)% d != 0
    
    def运行(自身,n,m):
    
    如果len(n)那么,你可以对它进行暴力:)很可能,你将别无选择,只能对它进行暴力,因为这比“背包”式的问题稍微复杂一些。这些条件不一定只适用于一个元素,因此在不知道特定子集的其他元素的情况下,无法筛选出子集的潜在成员,因此在测试条件之前必须选择子集(尽管%d!=0与常量差相矛盾)。由于需要找到最小数量的子集,而不是找到任何匹配的子集,这将是一个计算量非常大的问题。你说
    d
    是两个数字之间的常数差,那么set
    {12,13,15,22,17}
    如何满足这一点呢?这个例子看起来是错误的:13=17(mod 4)
    def run(self, n,m):
            if len(n)<=1:
            return [n]             
            x = n[0:1]  //get 1st part
            y = n[1:]   //get remaining
            t = self.run(y,m)
            r=[]
            if t:
                r+=[x]  //add current element
                r+=[i for i in t if count(i)<=m]    // add fixed combinations
                r+=[ i+x for i in t if valid(i+x)]  // all combined combinations
            return r
    def valid(combination):
        //code to check your condition
        (num1-num2)% d != 0