不确定此问题是否应该在Math-Overflow上或此处,因此请先在此处尝试:
假设我们得到一个N 1和M 0的数字。
有(M + N)!/(M!* N!)不同的这样的数字,可以在可数集中排序。
例如,具有2个1和3个零的所有数字的有序集合是:
0 00011
1 00101
2 00110
3 01001
4 01010
5 01100
6 10001
7 10010
8 10100
9 11000
我们如何有效地计算相应集合中给定数字的索引?
注意:此问题的输入仅数字,不整个(相应)设置。
答案 0 :(得分:6)
让choose (n, k) = n! / k! / (n-k)!
。
观察排序集的以下结构:
0 0|0011
1 0|0101
2 0|0110
3 0|1001
4 0|1010
5 0|1100
------
6 1|0001
7 1|0010
8 1|0100
9 1|1000
在排序集中,总共有choose (N + M, M)
个数字(长度为N + M
的二进制字符串)。
首先将数字从零开始,其中有choose (N + M-1, M-1)
个。然后输入一个开头的数字,其中有choose (N-1 + M, M)
个。这两个部分中的每一部分也都有分类。
所以,如果您的数字b 1 b 2 ... b k 以零开头(b 1 = 0),其有序集合中的索引与所有二进制字符串{{的有序集合中的b 2 ... b k 的索引相同1}}个和N
个零。如果它以1开头(b 1 = 1),则其在有序集合中的索引与b 2 的索引相同... b k < / sub>在M-1
个和N-1
个零的所有二进制字符串的有序集合中,加上以零开头的二进制字符串的总数,即{{1 }}
通过这种方式,您递归地下降到涉及原始二进制字符串后缀的子问题,每当遇到1时,将所需数量增加一些。最后,您得到一个空二进制字符串,显然是一个,只包含0个零和0个的字符串。
答案 1 :(得分:5)
这在组合算法中称为排名。这是一个C函数,可以帮助您:
unsigned long rank_choose(unsigned long n, unsigned long k, unsigned long c) {
unsigned long res = 0;
for (; n > 0; n--) {
if (c & 1) { res += binomial(n-1, k); k--;}
c >>= 1;
}
return res;
}
假设您有一个函数binomial(n, k)
来计算系数n!/k!/(n-k)!
。请注意,我在此提出的解决方案使用 endianess reverse 表示:
const int m = 5, n = 2;
int k = 12;
std::cout << std::bitset<m>(k) << " " << rank_choose(m, n, k) << std::endl;
k = 9;
std::cout << std::bitset<m>(k) << " " << rank_choose(m, n, k) << std::endl;
返回:
01100 2
01001 7
以下是另一个字节的解决方案:
unsigned long rank_choose_rev(unsigned long n, unsigned long k, unsigned long c) {
unsigned long res = 0, mask = 1<<(n-1);
for (; n > 0; n--) {
if (c & mask) { res += binomial(n-1, k); k--;}
mask >>= 1;
}
return res;
}
然后
01100 5
01001 3
注意:下面的@Gassa很好地描述了这些算法(给他+1)。
答案 2 :(得分:0)
我将在一个例子的帮助下解释一个解决方案。假设有3 ones
和3 zeroes
,我们必须找到010110
的索引
左起第一个位于第二个位置。因此,所有在其右边都有双零的数字应小于这个数字:
00---- (C(4,3)) = 4
现在将此位置放在其位置并移至下一个位置。下一个是在第4位。因此,以下所有数字应小于候选人:
0100-- (C(2,2)) = 1
现在将此位置放在其位置并移至下一个位置。下一个是在第5位。因此,以下所有数字应小于候选人:
01010- (C(1,1)) = 1
Hence the number of numbers less than the candidate = 4 + 1 + 1 = 6
000111
001011
001101
001110
010011
010101
010110