【问题标题】:count collection of subset containing all elements of set计算包含集合所有元素的子集集合
【发布时间】:2017-09-22 15:40:43
【问题描述】:

给定一个包含 M 个 1 的集合 S(M),计算所有非空子集的集合,使得每个集合的并集生成 S(M)。子集的基数也可以是 K,其中 1

S(M) = {1,1,1,1}, K = 2.

possible collections can be 
{{1,1},{1,1}},
{1},{1},{1,1}},
{{1,1},{1},{1}}

我已经看过几个答案,但我相信它们与我的不同。

我还阅读了Stirling number of second kind,但我仍然希望将子集的基数条件合并到该公式中。

我编写了一个可能的递归解决方案,但它不适用于较大的 M 值。

如果有人需要更多信息,请告诉我。

任何帮助或指导将不胜感激。

问候

阿杰

【问题讨论】:

  • 你的意思是_multi_sets?为什么 {{1},{1},{1,1}} 和 {{1,1},{1},{1}} 被认为是不同的?
  • 在这种情况下,位置很重要。
  • 那么听起来所有元素都是1的事实是无关紧要的。使它们不同可以消除多重集合的复杂性,如果我理解正确的话,可以消除“位置”的复杂性。
  • 我没明白你的意思。如果我们写出两个子集的基数,那么它将是 {1,1,2} 和 {2,1,1}。对于这种情况,我相信我可以使用排列和组合来获得 {1,1,2} 的所有组合数,即 3。另外,如果您知道任何可以通过考虑 {{1} 来解决问题的解决方案,{ 1},{1,1}} 和 {{1,1},{1},{1}} 相同,请告诉我。
  • 我可能仍然不明白您要解决的确切问题。你能编辑使用最严格的术语(set/multiset/sequence/...?),特别是进一步解释 K 的作用吗?

标签: set subset combinations


【解决方案1】:

如果我正确理解了这个问题中的术语,您是在问有多少种方法可以将长度为 M 的序列 ABCDEFGHIJ... 划分为单词(例如, ABC DE FGH IJ ...) 每个长度不大于K。 (对于给出的示例,答案是五个:ABCD、AB CD、A BC D、AB CD 和 AB CD。)这是stars and bars 解决的问题的一种对偶,因为限制是大小子集的数量而不是它们的数量。

我没有尝试为此提供封闭形式的解决方案,但直接dynamic programming 攻击在 O(M^2 K) 时间内起作用:令 C_n 为前 n 个字母的可能性数。 C_0=1,对于所有 nC_n 为 0。对于 n>0,我们有 C_n=sum(i=[1..K]) C_(n-i)。只需按顺序计算值,直到达到 C_M。 (当然,将最后一个 K 值保留在用作 SIPO shift register 的队列中就足够了。)

请注意,值变大的速度非常快:M=100 和 K=2,我得到 5.7e20 种可能性;将 K 更改为 10 得到 6.1e29。提到的 10^5 的 M(又是 K=2)给出 4.2e20898。因此,我将 M 的额外因素包含在对应于任意精度算术的复杂度中。

【讨论】:

    【解决方案2】:

    方法应该是

    Given M and K
    
    Find the max number of divisions(subsets)
    Find the min number of divisions(subsets)
    
    Iterate from max to min using i
    -Apply DP to find different combinations of sizes of subsets. 
    -A permutation of each combination should yield a count for that number of combinations(i). 
    -Sum up all the counts.
    

    下面是带有 DP 的此逻辑的 Java 实现。变量steptoGo 分别对应MK。我尝试用一​​些随机数运行它,但很低。

    注意:您还需要更新静态数组维度 当使用 M 的巨大值时。

    import java.util.Arrays;
    public class HelloWorld{
    
    
        public static int step = 4;
        public static long[][] dMem = new long[100][100];
    
         public static void main(String []args){
    
            int toGo = 30;
            int max = toGo;
            int min = toGo/step;
            long temp = 0;
    
            long count = 0;
            for(int i = max; i >= min; i--){
                temp = nSteps(i, toGo);
                temp = temp == -1 ? 0 : temp;
                count += temp;
               // System.out.println(count+"\n");
            }
            System.out.println("Num of ways = "+count);
         }
    
         public static long nSteps( int n, int toGo ){
    
             long steps = 0, temp = 0;
             //System.out.println(" n = "+n+" toGo = "+toGo);
             if( n == toGo ) return 1;
             if( n > toGo || n <= 0 || toGo <= 0) return -1;
             if( dMem[n][toGo] != 0 ) return dMem[n][toGo];
             for( int i = 1; i <= step; i++){
                 temp = nSteps( n-1, toGo-i );
                 temp = temp == -1 ? 0 : temp;
                 steps += temp;
             }
             steps = steps == 0 ? -1 : steps;
             dMem[n][toGo] = steps;
             return steps;
         }
    }
    

    【讨论】:

    • 这个算法的运行时间是多少?由于我只需要计算组合,我们可以有一个公式来做到这一点吗?
    • 让我看看。我应该能想出一个。我只需要检查我们是否可以从 DP 中获得一些东西,因为我从 max 开始,所有子集都只是单个元素。我们应该从那里逐渐将子集总数减少 1。
    • 已经更新了我建议的 Java 实现。我真的不确定@Davis 指出的范围将如何处理,范围可能很大(没有什么长的~ 10 ^ 18)。
    猜你喜欢
    • 1970-01-01
    • 2020-07-09
    • 2013-08-11
    • 2012-12-01
    • 2019-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-31
    相关资源
    最近更新 更多