查找一组给定条件的最小数量子集的算法

时间:2016-04-17 05:48:49

标签: algorithm subset

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

  1. 每组中的元素数是常数,即上述 例如5
  2. 在子集中,任何两个元素都满足条件: (num1-num2)% d != 0num1>num2和其中d是常数差异 两个数字之间。
  3. 对于上面的示例:如果d=4和子集中的元素数量为5,那么其中一个子集将为:{12,13,15,22,17}

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

3 个答案:

答案 0 :(得分:0)

好的,所以它不是100%关于编程。这是一项标准的离散数学练习。

让我们分步进行:

  1. 所有可能子集的组合数量: n!其中 n 是第一组中元素的数量。我在这里假设我们不允许重复子集中的选定元素。并且子集中元素的顺序并不重要(否则我们需要使用组合而不是排列)
  2. 现在我们只选择 r 元素,而不是所有 n ,所以现在它 n!/(nr) !其中r是子集中元素的数量。
  3. 最难完成的部分。现在我们需要添加一个条件,你只能采用之前的 n!/(n-r)!并减去" bad"对元素。基本上,排除以禁止值开头的任何排列。让我们找到所有被禁止的值:
  4. 两个简单的嵌套循环,检查每对的(num1-num2)%d!= 0,这将花费n *(n-1)/ 2次迭代。或者只是n ^ 2次迭代而没有动态更改嵌套循环中的迭代器。

答案 1 :(得分:0)

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

将你的集合划分为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)

此代码的输出,显示大小最多为5的子集,使得没有子集包含两个等于模6的数字:

[4, 14, 25, 30, 39]
[6, 13, 17, 27, 40]
[12, 21, 26, 28, 29]
[15, 22]

可以使用优先级队列和正在运行的计数器优化parts和while循环条件,但这会模糊正在发生的事情。优化后的表格将在O(n log n)时间内运行;写成它的O(n ^ 2 log n)

[我应该说,我很确定这个解决方案是正确的,但我不太清楚如何证明这一点。我有兴趣看一个证明(或一个反例来证明它是错的)。]

答案 2 :(得分:0)

我会做这样的事情:

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