查找幂集的第n个集
Find n-th set of a powerset
我正试图在幂集中找到n-th
集。通过n-th
,我的意思是幂集是按照以下顺序生成的——首先是大小,然后是词典——因此,[a, b, c]
的幂集中集合的索引是:
0 - []
1 - [a]
2 - [b]
3 - [c]
4 - [a, b]
5 - [a, c]
6 - [b, c]
7 - [a, b, c]
在寻找解决方案时,我所能找到的只是一个返回元素列表的第n个排列的算法——例如,在这里。
上下文:
我试图检索元素的向量V
的整个幂集,但我需要一次只检索一个幂集。
要求:
- 我只能同时维护两个向量,第一个向量包含列表中的原始项,第二个向量包含
V
的幂集中的n-th
集——这就是为什么我愿意在这里使用n-th set
函数 - 我需要在解空间上的线性时间中而不是这样做——这意味着它不能列出所有集合,而它们选择
n-th
集合 - 我最初的想法是使用位来表示位置,并获得我所需要的有效映射——作为我发布的"不完整"解决方案
我没有这个函数的闭合形式,但我确实有一个非循环next_combination
函数,如果有帮助的话,欢迎使用它。它假设您可以将位掩码拟合为某种整数类型,这可能不是一个不合理的假设,因为64元素集有264的可能性。
正如评论所说,我觉得"词典排序"的定义有点奇怪,因为我认为词典排序应该是:[], [a], [ab], [abc], [ac], [b], [bc], [c]
。但我以前不得不做"先按大小,然后按字典"的枚举。
// Generate bitmaps representing all subsets of a set of k elements,
// in order first by (ascending) subset size, and then lexicographically.
// The elements correspond to the bits in increasing magnitude (so the
// first element in lexicographic order corresponds to the 2^0 bit.)
//
// This function generates and returns the next bit-pattern, in circular order
// (so that if the iteration is finished, it returns 0).
//
template<typename UnsignedInteger>
UnsignedInteger next_combination(UnsignedInteger comb, UnsignedInteger mask) {
UnsignedInteger last_one = comb & -comb;
UnsignedInteger last_zero = (comb + last_one) &~ comb & mask;
if (last_zero) return comb + last_one + (last_zero / (last_one * 2)) - 1;
else if (last_one > 1) return mask / (last_one / 2);
else return ~comb & 1;
}
第5行执行相当于(扩展)正则表达式替换的位破解操作,它找到字符串中的最后一个01
,将其翻转到10
,并将所有后续的1
一直向右移动。
s/01(1*)(0*)$/1021/
第6行执行此操作(仅在前一行失败的情况下),再添加一个1
,并将1
一直向右移动:
s/(1*)0(0*)/211/
我不知道这个解释是有帮助还是阻碍:)
这里有一个快速而肮脏的驱动程序(命令行参数是集合的大小,默认为5,最大为无符号长中的位数):
#include <iostream>
template<typename UnsignedInteger>
std::ostream& show(std::ostream& out, UnsignedInteger comb) {
out << '[';
char a = 'a';
for (UnsignedInteger i = 1; comb; i *= 2, ++a) {
if (i & comb) {
out << a;
comb -= i;
}
}
return out << ']';
}
int main(int argc, char** argv) {
unsigned int n = 5;
if (argc > 1) n = atoi(argv[1]);
unsigned long mask = (1UL << n) - 1;
unsigned long comb = 0;
do {
show(std::cout, comb) << std::endl;
comb = next_combination(comb, mask);
} while (comb);
return 0;
}
考虑到枚举的大小,很难相信这个函数对一组超过64个元素有用,但它可能对枚举某些有限的部分有用,例如三个元素的所有子集。在这种情况下,只有当修饰适合一个单词时,bit hackery才真正有用。幸运的是,这很容易测试;您只需要对位集中的最后一个字进行如上所述的计算,直到测试last_zero
为零。(在这种情况下,你不需要比特和mask
,实际上你可能想选择一种不同的方式来指定集合大小。)如果last_zero
为零(这实际上非常罕见),那么你需要用其他方式进行转换,但原理是相同的:找到在1
之前的第一个0
(注意0
在单词的末尾而1
在下一个单词的开头的情况);将01
更改为10
,计算出需要移动多少个1
,然后将它们移动到最后。
考虑元素列表L = [a, b, c]
,L
的幂集由以下公式给出:
P(L) = {
[],
[a], [b], [c],
[a, b], [a, c], [b, c],
[a, b, c]
}
将每个位置视为一个点,您将有映射:
id | positions - integer | desired set
0 | [0 0 0] - 0 | []
1 | [1 0 0] - 4 | [a]
2 | [0 1 0] - 2 | [b]
3 | [0 0 1] - 1 | [c]
4 | [1 1 0] - 6 | [a, b]
5 | [1 0 1] - 5 | [a, c]
6 | [0 1 1] - 3 | [b, c]
7 | [1 1 1] - 7 | [a, b, c]
正如您所看到的,id
并没有直接映射到整数。需要应用适当的映射,这样您就可以:
id | positions - integer | mapped - integer
0 | [0 0 0] - 0 | [0 0 0] - 0
1 | [1 0 0] - 4 | [0 0 1] - 1
2 | [0 1 0] - 2 | [0 1 0] - 2
3 | [0 0 1] - 1 | [0 1 1] - 3
4 | [1 1 0] - 6 | [1 0 0] - 4
5 | [1 0 1] - 5 | [1 0 1] - 5
6 | [0 1 1] - 3 | [1 1 0] - 6
7 | [1 1 1] - 7 | [1 1 1] - 7
为了解决这个问题,我使用了一个二进制树来进行映射——我发布它是为了让人们可以从中看到一个解决方案:
#
______________|_____________
a /
_____|_____ _______|______
b / /
__|__ __|__ __|__ __|__
c / / / /
[ ] [c] [b] [b, c] [a] [a, c] [a, b] [a, b, c]
index: 0 3 2 6 1 5 4 7
假设您的集合大小为N。
因此,有(N选择k)个大小为k的集合。你可以很快找到正确的k(即第N个集合的大小),只需从N中减去(N选择k),直到N即将变为负数。这将问题简化为找到N集的第N个k子集。
N集的第一个(N-1选择k-1)k-子集将包含其最小元素。因此,如果n小于(n-1选择k-1),则拾取第一个元素并在集合的其余部分上递归。否则,您有一个(N-1选择k)其他集合;丢弃第一个元素,从N中减去(N-1选择k-1),然后递归。
代码:
#include <stdio.h>
int ch[88][88];
int choose(int n, int k) {
if (n<0||k<0||k>n) return 0;
if (!k||n==k) return 1;
if (ch[n][k]) return ch[n][k];
return ch[n][k] = choose(n-1,k-1) + choose(n-1,k);
}
int nthkset(int N, int n, int k) {
if (!n) return (1<<k)-1;
if (choose(N-1,k-1) > n) return 1 | (nthkset(N-1,n,k-1) << 1);
return nthkset(N-1,n-choose(N-1,k-1),k)<<1;
}
int nthset(int N, int n) {
for (int k = 0; k <= N; k++)
if (choose(N,k) > n) return nthkset(N,n,k);
else n -= choose(N,k);
return -1; // not enough subsets of [N].
}
int main() {
int N,n;
scanf("%i %i", &N, &n);
int a = nthset(N,n);
for (int i=0;i<N;i++) printf("%i", !!(a&1<<i));
printf("n");
}
- 使用堆查找第K个最大元素的时间复杂性
- 如何实现高效的算法来计算大型数据集的多个不同值?
- 查找字符在两个索引之间出现的次数
- 提升几何体:C++并集多个多边形
- 查找两个排序向量中共有的元素
- 查找数组中第一个最小值和最后一个最大值元素之间的算术平均值
- C++ 函数,用于查找传入的 N 个数字的平均值、总和、最小值和最大值
- 在C++中查找两个向量之间最相似的值
- 使用XOR查找O(n)-解决方案中的两个字符串是否为变位符
- 使用STD集查找时,该程序将不会编译
- 查找第一个文件/查找下一个文件不返回文件夹中的所有文件
- CDao记录集::查找错误
- C++集查找值较小但更接近x
- 将两个双数组集组合为一个查找其并集和交集的最简单方法
- 使用多个求和查找有序集的组合数
- C++ 初学者,第一个程序.查找速度和距离
- 多个查找集
- 使用C++根据行中的第一个字符串查找行(仅一个)
- 如何根据多个参数查找容器中的对象范围
- 查找幂集的第n个集