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

发布于 2024-09-30 11:50:17 字数 143 浏览 3 评论 0原文

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

So I am stuck with this problem of trying to find all k-elements subsets from a given N-elements set. 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. Can anyone give me a hint?
Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

软糯酥胸 2024-10-07 11:50:17

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

“那是一个漆黑的暴风雨之夜,船长说……”

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.

"It was a dark and stormy night, and the Captain said ..."

依 靠 2024-10-07 11:50:17

更好

对于 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();
    }   
  }
}

仅限 Power Set
这可能还没有完全实现,也不是很优雅,但它递归地计算完整的幂集,然后(迭代地)修剪它的大小。

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

}

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. Anyway. There's also an iteration in here, you could probably replace that with recursion if the goal is being PURELY recursive. This is a fairly straightforward modification of the algorithm given at wikipedia: powerset. I'll leave fixing the corner cases (k=0) to the reader.

This is not properly tail-recursive, not that it matters in most JVMs. (I guess the IBM JVM does this...)

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.

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

}
oО清风挽发oО 2024-10-07 11:50:17

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

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

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

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文