【问题标题】:How to Generate all k-elements subsets from an N-elements set recursively in Java如何在Java中递归地从N元素集中生成所有k元素子集
【发布时间】:2011-05-05 03:17:53
【问题描述】:

所以我遇到了这个问题,试图从给定的 N 元素集中找到所有 k 元素子集。我知道使用公式 C(n,k)=C(n-1, k-1)+C(n-1, k) 的 k 子集的总数是多少,我也知道怎么做以迭代的方式,但是当我尝试考虑递归解决方案时,我陷入了困境。谁能给我一个提示? 谢谢!

【问题讨论】:

  • 你应该阅读格雷码。
  • 你会如何迭代,迭代有什么问题?
  • @MB,因为这是作业要求的,我怀疑。
  • @MB,在迭代中没有奇迹发生。简单的话是蹩脚的。
  • @Woot4Moo:我提供了一些...

标签: java recursion combinatorics combinations tail-recursion


【解决方案1】:

对于集合的每个元素,取那个元素,然后依次添加到剩余 N-1 个元素集合的所有 (k-1) 个子集。

“那是一个黑暗而暴风雨的夜晚,船长说……”

【讨论】:

  • 我真的需要知道如何将其转换为递归方法,因为如前所述,我有一个涉及嵌套循环的迭代解决方案,但不知道如何将其转换为递归方法。
  • 那是递归的方法!它说产生 N 集的 k 子集的问题与获取 N 的每个元素,然后计算剩余的 N-1 大小集的 k-1 集相同。当 K 达到 1 时,计算出 hte 子集是微不足道的。
  • 对不起,你是对的!我想评论作为迭代解决方案的第二个答案,但它消失了,所以我最终在错误的地方发表评论。
  • 这有重复。没有这些就好了。
【解决方案2】:

更好

这对于k=0 的情况是错误的,因为我认为它会返回一个包含空集的集合,这不太正确。反正。这里还有一个迭代,如果目标是纯递归的,你可以用递归替换它。这是对wikipedia: powerset 给出的算法的相当简单的修改。我将把修复角落案例 (k=0) 的工作留给读者。

这不是正确的尾递归,在大多数 JVM 中并不重要。 (我猜 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();
    }   
  }
}

仅限电源组 这可能不会完全到达那里,也不是很优雅,但它会递归计算完整的 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;
    }
  }

}

【讨论】:

  • @user497302:为什么不呢?它递归地计算所有 k 子集……编译它,它就可以工作了。
【解决方案3】:

这是一些伪代码。您可以通过在执行过程中存储每个调用的值以及在递归调用之前检查调用值是否已经存在来减少相同的递归调用。

以下算法将包含除空集之外的所有子集。

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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-12
    • 1970-01-01
    • 2011-11-14
    • 2013-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多