康托展开及其逆运算

来源:互联网 发布:电脑去水印软件 编辑:程序博客网 时间:2024/05/16 01:36

代码:

long long fact[12] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800};//0到11的阶乘bool book[101];//逆康托展开需要用到标记数组long long cantor(int per[],int n)//康托展开,输入由1到n的全排列组成的int型数组{    long long ranks = 0;//排名    int sum;//计数变量    for(int i = 1; i <= n; ++i)    {        sum = 0;        for(int j = i + 1; j <= n; ++j)//共有sum个比per[i]小的数            if(per[j] < per[i])                sum++;        ranks += sum * fact[n - i];//根据公式,相乘后相加    }    return ranks + 1;//由于公式是由0开始排名的,所以要加1}void inverse_cantor(int n,int ranks)//逆康托展开,n代表是1到n的全排列,ranks代表排名{    ranks--;//因为在康托展开中加了1,所以这里要减去    int num,sum;//num为比当前位小的数的个数    for(int i = n; i >= 1; --i)//方便计算阶乘    {        sum = 0;//重置        num = ranks / fact[i - 1];//取商        ranks = ranks % fact[i - 1];//取余数        num++;//num + 1为当前位应该是多少,如num = 2,代表有两个比当前位小的数,当前位本应该是2 + 1 = 3        int j;//后面需要用到,不能定义在for循环里面        for(j = 1; ; ++j)            if(book[j] == false)//如果该数没被标记            {                sum++;                if(sum == num)//sum与num相等,意味着[1,j]内没被标记的数有num个,也就是说j就是我们要找的答案                    break;            }        book[j] = true;//标记        cout << j;//输出        if(i == 2)//如果只剩下一个数了,不必再进行如此的计算            for(j = 1; j <= n; ++j)                if(book[j] == false)//找出哪个数还没被标记过                {                    cout << j << endl;//输出                    return;                }    }}
原创粉丝点击