简体   繁体   English

如何在Java中以递归方式从N元素集生成所有k元素子集

[英]How to Generate all k-elements subsets from an N-elements set recursively in Java

So I am stuck with this problem of trying to find all k-elements subsets from a given N-elements set. 所以我坚持试图从给定的N元素集中找到所有k元素子集的问题。 I know what the total number of k-subsets is using the formula C(n,k)=C(n-1, k-1)+C(n-1, k) and I also have an idea how to do it in a iterative manner, but I get stuck when I try to think of a recursive solution. 我知道k子集的总数是多少使用公式C(n,k)= C(n-1,k-1)+ C(n-1,k),我也知道如何做到这一点以迭代的方式,但当我试图想到一个递归的解决方案时,我陷入困境。 Can anyone give me a hint? 任何人都可以给我一个提示吗? Thanks! 谢谢!

For each element of the set, take that element, then add in turn to that all (k-1) subsets of the remaining N-1 element set. 对于集合的每个元素,取该元素,然后依次添加剩余N-1个元素集的所有(k-1)个子集。

"It was a dark and stormy night, and the Captain said ..." “这是一个黑暗而暴风雨的夜晚,船长说......”

Better 更好

This is broken for the k=0 case, because I think it'll return a set containing the empty set, which isn't quite right. 对于k=0情况,这是打破的,因为我认为它将返回一个包含空集的集合,这不太正确。 Anyway. 无论如何。 There's also an iteration in here, you could probably replace that with recursion if the goal is being PURELY recursive. 这里还有一个迭代,如果目标是PURELY递归,你可以用递归替换它。 This is a fairly straightforward modification of the algorithm given at wikipedia: powerset . 这是对wikipedia:powerset中给出的算法的相当直接的修改。 I'll leave fixing the corner cases (k=0) to the reader. 我将把角落案例(k = 0)修复给读者。

This is not properly tail-recursive, not that it matters in most JVMs. 这不是正确的尾递归,而是在大多数JVM中都很重要。 (I guess the IBM JVM does this...) (我猜IBM JVM会这样做......)

class RecursivePowerKSet
{  
  static public <E> Set<Set<E>> computeKPowerSet(final Set<E> source, final int k)
  {
    if (k==0 || source.size() < k) {
      Set<Set<E>> set = new HashSet<Set<E>>();
      set.add(Collections.EMPTY_SET);
      return set;
    }

    if (source.size() == k) {
      Set<Set<E>> set = new HashSet<Set<E>>();
      set.add(source);
      return set;
    }

    Set<Set<E>> toReturn = new HashSet<Set<E>>();

    // distinguish an element
    for(E element : source) {
      // compute source - element
      Set<E> relativeComplement = new HashSet<E>(source);
      relativeComplement.remove(element);

      // add the powerset of the complement
      Set<Set<E>> completementPowerSet = computeKPowerSet(relativeComplement,k-1);
      toReturn.addAll(withElement(completementPowerSet,element));
    }

    return toReturn;
  }

  /** Given a set of sets S_i and element k, return the set of sets {S_i U {k}} */ 
  static private <E> Set<Set<E>> withElement(final Set<Set<E>> source, E element)
  {

    Set<Set<E>> toReturn = new HashSet<Set<E>>();
    for (Set<E> setElement : source) {
      Set<E> withElementSet = new HashSet<E>(setElement);
      withElementSet.add(element);
      toReturn.add(withElementSet);
    }

    return toReturn;
  }

  public static void main(String[] args)
  {
    Set<String> source = new HashSet<String>();
    source.add("one");
    source.add("two");
    source.add("three");
    source.add("four");
    source.add("five");

    Set<Set<String>> powerset = computeKPowerSet(source,3);

    for (Set<String> set : powerset) {
      for (String item : set) {
        System.out.print(item+" ");
      }
      System.out.println();
    }   
  }
}

Power Set Only This doesn't probably quite get there, and isn't really elegant, but it computes the full powerset recursively, then trims it (iteratively) for size. 仅限功率设置这可能不太适合,并且不是很优雅,但它会递归地计算完整的powerset,然后对其进行修整(迭代)以获得大小。

class RecursivePowerSet
{


  static public <E> Set<Set<E>> computeConstrainedSets(final Set<Set<E>> source, final SizeConstraint<Set<E>> constraint)
  {
    Set<Set<E>> constrained = new HashSet<Set<E>>();
    for (Set<E> candidate : source) {
      if (constraint.meetsConstraint(candidate)) {
        constrained.add(candidate);
      }
    }
    return constrained;
  }

  static public <E> Set<Set<E>> computePowerSet(final Set<E> source)
  {

    if (source.isEmpty()) {
      Set<Set<E>> setOfEmptySet = new HashSet<Set<E>>();
      setOfEmptySet.add(Collections.EMPTY_SET);
      return setOfEmptySet;
    }


    Set<Set<E>> toReturn = new HashSet<Set<E>>();

    // distinguish an element
    E element = source.iterator().next();

    // compute source - element
    Set<E> relativeComplement = new HashSet<E>(source);
    relativeComplement.remove(element);

    // add the powerset of the complement
    Set<Set<E>> completementPowerSet = computePowerSet(relativeComplement);
    toReturn.addAll(completementPowerSet);
    toReturn.addAll(withElement(completementPowerSet,element));

    return toReturn;
  }

  static private <E> Set<Set<E>> withElement(final Set<Set<E>> source, E element)
  {

    Set<Set<E>> toReturn = new HashSet<Set<E>>();
    for (Set<E> setElement : source) {
      Set<E> withElementSet = new HashSet<E>(setElement);
      withElementSet.add(element);
      toReturn.add(withElementSet);
    }

    return toReturn;
  }

  public static void main(String[] args)
  {
    Set<String> source = new HashSet<String>();
    source.add("one");
    source.add("two");
    source.add("three");
    source.add("four");
    source.add("five");

    SizeConstraint<Set<String>> constraint = new SizeConstraint<Set<String>>(3);

    Set<Set<String>> powerset = computePowerSet(source);
    Set<Set<String>> constrained = computeConstrainedSets(powerset, constraint);
    for (Set<String> set : constrained) {
      for (String item : set) {
        System.out.print(item+" ");
      }
      System.out.println();
    }

  }

  static class SizeConstraint<V extends Set> {

    final int size;
    public SizeConstraint(final int size)
    {
     this.size = size; 
    }

    public boolean meetsConstraint(V set)
    {
      return set.size() == size;
    }
  }

}

Here's some pseudocode. 这是一些伪代码。 You can cut same recursive calls by storing the values for each call as you go and before recursive call checking if the call value is already present. 如果调用值已经存在,您可以通过在递归调用检查之前存储每个调用的值来剪切相同的递归调用。

The following algorithm will have all the subsets excluding the empty set. 以下算法将包含除空集之外的所有子集。

list * subsets(string s, list * v){
    if(s.length() == 1){
        list.add(s);    
        return v;
    }
    else
    {
        list * temp = subsets(s[1 to length-1], v);     
        int length = temp->size();

        for(int i=0;i<length;i++){
            temp.add(s[0]+temp[i]);
        }

        list.add(s[0]);
        return temp;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM