C 查找整数分区的字典顺序

C 查找整数分区的字典顺序,c,algorithm,lexicographic,integer-partition,C,Algorithm,Lexicographic,Integer Partition,对于排列,给定N和k,我有一个函数,可以按字典顺序查找N的kth排列。另外,给定一个排列perm,我有一个函数可以在N的所有排列中找到排列的词典索引。为此,我使用了中建议的“阶乘分解” 现在我想对N的整数分区做同样的事情。例如,对于N=7,我希望能够在索引(左)和分区(右)之间来回: 我试过一些方法。我想到的最好的是 sum = 0; for (int i=0; i<length; ++i) sum += part[i]*i; return sum; 这不太管用,但似乎在正确的轨道上

对于排列,给定
N
k
,我有一个函数,可以按字典顺序查找
N
k
th排列。另外,给定一个排列
perm
,我有一个函数可以在
N
的所有排列中找到排列的词典索引。为此,我使用了中建议的“阶乘分解”

现在我想对
N
的整数分区做同样的事情。例如,对于
N=7
,我希望能够在索引(左)和分区(右)之间来回:

我试过一些方法。我想到的最好的是

sum = 0;
for (int i=0; i<length; ++i)
  sum += part[i]*i;
return sum;
这不太管用,但似乎在正确的轨道上。我之所以想到这一点,是因为它计算了我必须向下移动一个数字的次数(比如
6,3,2
6,3,1,1
)。不过,我不知道如何修复它,因为我不知道如何解释什么时候事情必须重新组合(比如
6,3,1,1
转到
6,2,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
。这里你计算的是最大部分小于3的7个分区的数量加上最大部分小于2的4个分区的数量加上。。。同样的逻辑可以扭转这种局面。在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;
}
int
排名(整数部分[],整数大小,整数长度){
int r=0;
int n=大小;
int k;
对于(int i=0;i0){
对于(k=0;kn)断裂;
}
零件[长度+++]=k;
N-=k;
n-=numPar(n,k-1);
}
返回长度;
}
此处
numPar(int n)
应返回
n
的分区数,
numPar(int n,int k)
应返回
n
的分区数,最大部分为
k
。您可以使用递归关系自己编写这些函数。

\include
#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;
}
//要除以n以下k的组合数 整数分区(整数n,整数k){ int p,i;
如果(你能显示调用显示段的代码吗?@Ryker哪部分?我刚才用
printf
打印坏索引、好索引和分区,但如果有帮助的话,我可以包括这些。如果我理解正确,这可以归结为生成一个按字典排序的整数分区列表,对吗?@kuroineko No.我想能够在不计算前面的分区的情况下计算第n个分区。好的,那么你想要一个函数以最小的时间和/或内存消耗动态计算第n个分区,另一个函数可以给出给定分区的排序索引,对吗?
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;
}
#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;
}