我正在尝试在powerset中找到n-th
集。 n-th
我的意思是,powerset按以下顺序生成 - 首先按大小生成,然后按字典顺序生成 - 因此,[a, b, c]
的powerset中集合的索引是:
0 - []
1 - [a]
2 - [b]
3 - [c]
4 - [a, b]
5 - [a, c]
6 - [b, c]
7 - [a, b, c]
在寻找解决方案时,我所能找到的只是一种返回元素列表第n个排列的算法 - 例如here。
上下文:
我正在尝试检索向量V
元素的整个powerset,但我需要一次使用一组这样做。
要求:
n-th
的powerset的V
- 这就是为什么我愿意在这里有一个n-th set
功能; n-th
一个; 答案 0 :(得分:5)
我没有这个函数的封闭表单,但是我确实有一个黑客攻击非循环next_combination
函数,如果有帮助的话,欢迎你使用。它假设您可以将位掩码拟合为某种整数类型,这可能不是一个不合理的假设,因为64个元素集有2个 64 可能性。
正如评论所说,我发现“词典排序”的定义有点奇怪,因为我会说词典排序是:[], [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}一直到右边。
1
第6行执行此操作(仅当前一个失败时)再添加一个s/01(1*)(0*)$/10\2\1/
并将1
一直移到右侧:
1
我不知道这种解释是否有助于或阻碍:)
这是一个快速而又脏的驱动程序(命令行参数是集合的大小,默认值为5,最大值是unsigned long中的位数):
s/(1*)0(0*)/\21\1/
考虑到枚举的大小,很难相信这个函数可能对超过64个元素的集合有用,但枚举一些有限的部分可能很有用,例如三个元素的所有子集。在这种情况下,如果修改适合单个单词,则bit-hackery才真正有用。幸运的是,这很容易测试;你只需要在bitset的最后一个字上进行上述计算,直到#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;
}
为零的测试。 (在这种情况下,您不需要bit和last_zero
,实际上您可能想要选择另一种指定集大小的方法。)如果mask
变为零(实际上是非常罕见),那么你需要以其他方式进行转换,但原则是相同的:找到last_zero
之前的第一个0
(注意1
的情况1}}位于单词的末尾,0
位于下一个单词的开头);将1
更改为01
,找出需要移动的10
个,并将它们移到最后。
答案 1 :(得分:4)
考虑到元素列表L = [a, b, c]
,L
的powerset由:
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
答案 2 :(得分:2)
假设你的集合的大小为N。
因此,有(N选择k)组大小为k。只需从n中减去(N选择k)直到n即将为负,你可以非常快速地找到正确的k(即第n组的大小)。这可以减少您找到N组的第n个k子集的问题。
N-set的第一个(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");
}