对于排列,给定N
和k
,我有一个函数,用于按字典顺序查找k
的{{1}}排列。此外,给定一个排列N
,我有一个函数,可以在perm
的所有排列中找到排列的词典索引。为此,我使用this answer中建议的“因子分解”。
现在我想对N
的整数分区做同样的事情。例如,对于N
,我希望能够在索引(左)和分区(右)之间来回:
N=7
我尝试了一些事情。我想出的最好的是
0 ( 7 )
1 ( 6 1 )
2 ( 5 2 )
3 ( 5 1 1 )
4 ( 4 3 )
5 ( 4 2 1 )
6 ( 4 1 1 1 )
7 ( 3 3 1 )
8 ( 3 2 2 )
9 ( 3 2 1 1 )
10 ( 3 1 1 1 1 )
11 ( 2 2 2 1 )
12 ( 2 2 1 1 1 )
13 ( 2 1 1 1 1 1 )
14 ( 1 1 1 1 1 1 1 )
给出以下内容:
sum = 0;
for (int i=0; i<length; ++i)
sum += part[i]*i;
return sum;
这不太奏效,但似乎走在正确的轨道上。我想出了这个,因为它计算了我有多少次向下移动一个数字(如 0 0( 7 )
1 1( 6 1 )
2 2( 5 2 )
3 3( 5 1 1 )
3 4( 4 3 )
4 5( 4 2 1 )
6 6( 4 1 1 1 )
5 7( 3 3 1 )
6 8( 3 2 2 )
7 9( 3 2 1 1 )
10 10( 3 1 1 1 1 )
9 11( 2 2 2 1 )
11 12( 2 2 1 1 1 )
15 13( 2 1 1 1 1 1 )
21 14( 1 1 1 1 1 1 1 )
转到6,3,2
)。但是,我无法看到如何修复它,因为我不知道如何解决事情必须重新组合的问题(例如6,3,1,1
转到6,3,1,1
)。
答案 0 :(得分:2)
考虑为什么“因子分解”适用于排列,同样的逻辑在这里起作用。但是,不必使用k!
作为k
个对象的排列数,而是必须使用分区函数p(n,k)
作为n
的分区数,最大部分最多k
。对于n=7
,这些数字是:
k | p(7,k)
0 | 0
1 | 1
2 | 4
3 | 8
4 | 11
5 | 13
6 | 14
7 | 15
例如,要获得(3,2,1,1)
的词典索引,请计算
p(3+2+1+1) - [ p(3+2+1+1,3-1) + p(2+1+1,2-1) + p(1+1,1-1) + p(1,1-1) ] - 1
是15 - [4 + 1 + 0 + 0] - 1 = 9
。在这里,您计算7的分区数,最大部分小于3加上分区数为4,最大部分小于2加...相同的逻辑可以反转这一点。在C中,(未经测试的!)函数是:
int
rank(int part[], int size, int length) {
int r = 0;
int n = size;
int k;
for (int i=0; i<length; ++i) {
k = part[i];
r += numPar(n,k-1);
n -= k;
}
return numPar(size)-r;
}
int
unrank (int n, int size, int part[]) {
int N = size;
n = numPar(N)-n-1;
int length = 0;
int k,p;
while (N>0) {
for (k=0; k<N; ++k) {
p = numPar(N,k);
if (p>n) break;
}
parts[length++] = k;
N -= k;
n -= numPar(N,k-1);
}
return length;
}
此处numPar(int n)
应返回n
的分区数,而numPar(int n, int k)
应返回n
的分区数,其中最大部分为k
{} 。你可以使用递归关系自己编写这些。
答案 1 :(得分:0)
#include <stdio.h>
// number of combinations to divide by the number of k below n
int partition(int n, int k){
int p,i;
if(n<0) return 0;
if(n<2 || k==1) return 1;
for(p=0,i=1;i<=k;i++){
p+=partition(n-i,i);
}
return p;
}
void part_nth_a(int n, int k, int nth){
if(n==0)return;
if(n== 1 || n==k && nth == 0){
printf("%d ", n);
return ;
}
int i, diff;
for(i=0;i<k;++i){
diff = partition(n, k-i) - partition(n, k-i-1);
if(nth < diff){
printf("%d ", k-i);
n -= (k-i);
if(diff == 1)
part_nth_a(n, k-i, 0);
else
part_nth_a(n, k-i, nth);
return;
}
nth -= diff;
}
}
void part_nth(int n, int nth){
if(nth == 0){
printf("%d ", n);
return ;
}
int i, j, numOfPart;
for(i=1;i<n;++i){
numOfPart = n-i < i ? partition(i, n-i) : partition(i, i);
if(nth <= numOfPart)
break;
nth -= numOfPart;
}
printf("%d ", n-i);
if(n-i < i)
part_nth_a(i, n-i, nth-1);
else
part_nth_a(i, i, nth-1);
}
int main(){
int n = 7;
int i, numOfPart = partition(n, n);
for(i=0;i<numOfPart;++i){
printf("%2d ( ", i);
part_nth(n, i);
printf(")\n");
}
return 0;
}