Algorithm 查找一组值的所有唯一子集

Algorithm 查找一组值的所有唯一子集,algorithm,set,unique,subset,Algorithm,Set,Unique,Subset,我有一个算法问题。我试图从一组更大的值中找到所有唯一的值子集 例如,假设我有一个集合{1,3,7,9}。我可以使用什么算法来找到这些3的子集 {1,3,7} {1,3,9} {1,7,9} {3,7,9} 子集不应该重复,顺序也不重要,出于这些目的,集合{1,2,3}与集合{3,2,1}是相同的。鼓励使用Psudocode或常规类型 蛮力方法显然是可能的,但并不可取 例如,这种蛮力方法如下所示 for i = 0 to size for j = i + 1 to size for k

我有一个算法问题。我试图从一组更大的值中找到所有唯一的值子集

例如,假设我有一个集合{1,3,7,9}。我可以使用什么算法来找到这些3的子集

{1,3,7} {1,3,9} {1,7,9} {3,7,9} 子集不应该重复,顺序也不重要,出于这些目的,集合{1,2,3}与集合{3,2,1}是相同的。鼓励使用Psudocode或常规类型

蛮力方法显然是可能的,但并不可取

例如,这种蛮力方法如下所示

for i = 0 to size for j = i + 1 to size for k = j + 1 to size subset[] = {set[i],set[j],set[k]}
不幸的是,对于子集中所需的每个元素,这需要一个额外的循环。例如,如果您想要8个元素的子集,这是不可取的。

如果您只对大小为3的子集感兴趣,则可以使用三个简单的嵌套for循环来完成

for ( int i = 0; i < arr.size(); i++ )
  for ( int j = i+1; j < arr.size(); j++ )
    for ( int k = j+1; k < arr.size(); k++ )
      std::cout << "{ " << arr[i] <<"," << arr[j] <<"," << arr[k] <<" }";
对于更一般的情况,必须使用递归

void recur( set<int> soFar, set<int> remaining, int subSetSize ) {
  if (soFar.size() == subSetSize) {
    print soFar;
    return;
  }

  for ( int I = 0; I < remaining.size(); I++ ) {
    //take out Ith element from remaining and push it in soFar.
    // recur( newSofar, newRemaining, subSetSize);
  }
}

如果您只对大小为3的子集感兴趣,那么可以使用三个简单的嵌套for循环来完成

for ( int i = 0; i < arr.size(); i++ )
  for ( int j = i+1; j < arr.size(); j++ )
    for ( int k = j+1; k < arr.size(); k++ )
      std::cout << "{ " << arr[i] <<"," << arr[j] <<"," << arr[k] <<" }";
对于更一般的情况,必须使用递归

void recur( set<int> soFar, set<int> remaining, int subSetSize ) {
  if (soFar.size() == subSetSize) {
    print soFar;
    return;
  }

  for ( int I = 0; I < remaining.size(); I++ ) {
    //take out Ith element from remaining and push it in soFar.
    // recur( newSofar, newRemaining, subSetSize);
  }
}

一些使用递归的Java代码

void recur( set<int> soFar, set<int> remaining, int subSetSize ) {
  if (soFar.size() == subSetSize) {
    print soFar;
    return;
  }

  for ( int I = 0; I < remaining.size(); I++ ) {
    //take out Ith element from remaining and push it in soFar.
    // recur( newSofar, newRemaining, subSetSize);
  }
}
基本思想是尝试用当前位置交换每个元素,然后在下一个位置上递归,但我们也需要startPos在这里指示我们交换的最后一个位置是什么,否则我们将得到一个简单的置换生成器。一旦我们有足够的元素,我们打印所有这些元素并返回

静态无效子集[]arr、int pos、int深度、int startPos { 如果pos==深度 { 对于int i=0;iarr.length 回来 //交换pos和i 内部温度=arr[pos]; arr[pos]=arr[i]; arr[i]=温度; 海底沙坑,位置+1,深度,i+1; //交换pos和我回来-否则事情就会变得一团糟 温度=arr[pos]; arr[pos]=arr[i]; arr[i]=温度; } } 公共静态无效字符串[]args { 子集newint[]{1,3,7,9},0,3,0; } 印刷品:

1  3  7  
1  3  9  
1  7  9  
3  7  9  
通过示例进行更详细的解释:

首先,在上面的代码中,元素通过与自身进行交换而保持在相同的位置,它不做任何事情,只是使代码更简单

还要注意的是,在每一步中,我们都会恢复所做的所有掉期交易

假设我们有输入123445,我们想找到大小为3的子集

首先,我们只考虑前3个元素-1 2 3

然后我们分别用4和5交换3, 前3个元素给出了1 2 4和1 2 5

注意,我们刚刚完成了包含1和2的所有集合

现在我们需要的是13x形式的集合,所以我们交换2和3,得到12345。但是我们已经有了包含1和2的集合,所以这里我们想跳过2。所以我们分别用4和5交换2,前3个元素得到1 3 4和1 3 5

现在我们交换2和4得到1 4 3 2 5。但是我们想跳过3和2,所以我们从5开始。我们交换3和5,前3个元素得到1 4 5

等等

在这里跳过元素可能是最复杂的部分。请注意,每当我们跳过元素时,它只涉及从我们交换2和4时所用的位置之后继续,我们从交换4后继续。这是正确的,因为在没有经过处理的情况下,元素不可能到达我们正在交换的位置的左侧,也不可能到达该位置的右侧,因为我们从左到右处理所有元素

从for循环的角度考虑

从for循环的角度来考虑这个算法也许是最简单的

for i = 0 to size
  for j = i + 1 to size
    for k = j + 1 to size
      subset[] = {set[i],set[j],set[k]}
每个递归步骤都代表一个for循环

startPos分别为0、i+1和j+1

深度是有多少个for循环

pos是我们目前使用的for循环


因为我们从不在更深的循环中倒退,所以使用数组的开头作为元素的存储是安全的,只要我们在完成迭代时还原更改。

一些使用递归的Java代码

void recur( set<int> soFar, set<int> remaining, int subSetSize ) {
  if (soFar.size() == subSetSize) {
    print soFar;
    return;
  }

  for ( int I = 0; I < remaining.size(); I++ ) {
    //take out Ith element from remaining and push it in soFar.
    // recur( newSofar, newRemaining, subSetSize);
  }
}
基本思想是尝试用当前位置交换每个元素,然后在下一个位置上递归,但我们也需要startPos在这里指示我们交换的最后一个位置是什么,否则我们将得到一个简单的置换生成器。一旦我们有足够的元素,我们打印所有这些元素并返回

静态无效子集[]arr、int pos、int深度、int startPos { 如果pos==深度 { 对于int i=0;iarr.length 回来 //交换pos和i 内部温度=arr[pos]; arr[pos]=arr[i ]; arr[i]=温度; 海底沙坑,位置+1,深度,i+1; //交换pos和我回来-否则事情就会变得一团糟 温度=arr[pos]; arr[pos]=arr[i]; arr[i]=温度; } } 公共静态无效字符串[]args { 子集newint[]{1,3,7,9},0,3,0; } 印刷品:

1  3  7  
1  3  9  
1  7  9  
3  7  9  
通过示例进行更详细的解释:

首先,在上面的代码中,元素通过与自身进行交换而保持在相同的位置,它不做任何事情,只是使代码更简单

还要注意的是,在每一步中,我们都会恢复所做的所有掉期交易

假设我们有输入123445,我们想找到大小为3的子集

首先,我们只考虑前3个元素-1 2 3

然后我们分别用4和5交换3, 前3个元素给出了1 2 4和1 2 5

注意,我们刚刚完成了包含1和2的所有集合

现在我们需要的是13x形式的集合,所以我们交换2和3,得到12345。但是我们已经有了包含1和2的集合,所以这里我们想跳过2。所以我们分别用4和5交换2,前3个元素得到1 3 4和1 3 5

现在我们交换2和4得到1 4 3 2 5。但是我们想跳过3和2,所以我们从5开始。我们交换3和5,前3个元素得到1 4 5

等等

在这里跳过元素可能是最复杂的部分。请注意,每当我们跳过元素时,它只涉及从我们交换2和4时所用的位置之后继续,我们从交换4后继续。这是正确的,因为在没有经过处理的情况下,元素不可能到达我们正在交换的位置的左侧,也不可能到达该位置的右侧,因为我们从左到右处理所有元素

从for循环的角度考虑

从for循环的角度来考虑这个算法也许是最简单的

for i = 0 to size
  for j = i + 1 to size
    for k = j + 1 to size
      subset[] = {set[i],set[j],set[k]}
每个递归步骤都代表一个for循环

startPos分别为0、i+1和j+1

深度是有多少个for循环

pos是我们目前使用的for循环


由于我们从不在更深的循环中倒退,因此使用数组的开头作为元素的存储是安全的,只要我们在完成迭代时还原更改即可。

很抱歉,我同时使用了这两个标记,我的错。可以从数学上证明唯一子集的数量等于父集合中给定的元素数量吗?我觉得这是真的,但我没有测试它。对于3的子集,它似乎遵循这个方程,唯一子集=1/6*x^3+x^2+11/6^x+1。其中x是集合大小与子集大小的差异。暴力到底是什么?我可能会调用任何方法来生成这些集合,而不是简单地计算它们。本质上,这是一个有大量答案的问题。很抱歉,我遵循了这两个标记,我的错误。可以从数学上证明唯一子集的数量等于父集合中给定的元素数量吗?我觉得这是真的,但我没有测试它。对于3的子集,它似乎遵循这个方程,唯一子集=1/6*x^3+x^2+11/6^x+1。其中x是集合大小与子集大小的差异。暴力到底是什么?我可能会调用任何方法来生成这些集合,而不是简单地用蛮力来计算它们。本质上,与有大量答案的问题相同,我只是发布嵌套循环是不可取的。在Dukeling在评论中询问了蛮力方法之后。哈哈,我只是发布了嵌套循环是不受欢迎的。在Dukeling在评论中询问了蛮力方法之后。在数组中交换数字是聪明的。你能更详细地解释一下这是如何工作的吗?@Chase试图解释得更详细一点-希望能有所帮助。哦,基本上是我做的,但递归地做得很好。递归有时还是会把我搞糊涂。在数组中交换数字是聪明的。你能更详细地解释一下这是如何工作的吗?@Chase试图解释得更详细一点-希望能有所帮助。哦,基本上是我做的,但递归地做得很好。递归有时还是会把我搞得一团糟。