首页 > 其他分享 >康托(Cantor)展开与逆展开理解与运用

康托(Cantor)展开与逆展开理解与运用

时间:2024-08-04 20:53:40浏览次数:14  
标签:排列 21 29% Cantor plus 1% net 展开 康托

前言

        文章仅作参考、学习

        作者本人的文章是分享自己对于一些算法、数据结构、技巧的理解,写的内容可能比较简单或偏于大众化,也更好理解。文章后面通常会配套题目与题解:)。

        本文章内容依据“CC BY-NC-SA 4.0”许可证进行授权。转载请署名、附上出处链接,不得用于商业用途,详细见本篇文章后记。

        通篇代码使用C++不妨碍非代码部分的理解:))。

        如果本文有任何错误,也请各位帮忙指正,感谢!

目录

前言

Now

是什么

康托展开

原理、举例

公式

完整定义

C++代码

代码实现

代码分析

代码优化

逆康托展开

原理

问题整理、转换

证明

        第一步:确定基础情况

        第二步:归纳、做出假设

        第三步:确定并证明联系

回归原问题

说明

其他证明

举例

C++代码

代码实现

代码分析

代码优化

练习

后记

授权许可

Reference-参考

源自


Now

        可以不用,不能不知道——今天我们来看一个关于计算全排列排名的好东西:康托展开与逆展开


是什么

        康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

………………引自——百度百科

        简单来说,一个全排列中的任意排列都可以通过康托展开得到一个自然数,两者是一一对应的。康托展开可以求一个全排列的全部排列按字典序排序的排名同样可以通过这个排名逆推原来的排列

康托展开

        这里先讨论展开。

原理、举例

        这里先不说什么公式(需要的直接跳到后面),直接举例

        比如,有一个1~5的排列,现在需要计算2, 5, 4, 3, 1这一排列P按字典序排序的排名。那么可以将其转化为计算按字典序在该排列前面一共有多少个排列。(比较排名?e.g.:对于排列1, 2, 3, 4, 5与2, 5, 4, 3, 1,显而易见,因为两者第一个元素前者比较小,所以前者字典序比较小,所以排名应在后者前面。我们可以通过比较相同位置的元素的大小来确定排名的先后)

        所以,我们先由排列P(2, 5, 4, 3, 1)的第一个元素开始依次往后看。

  •         对于第一个元素2,我们可以知道 别的 第一个元素<2 的排列 字典序一定是小于排列P的;所以1~5中,1 < 2,既当前位置有1这一种可能使该排列的字典序小于P,后面还有4个元素,这4种元素的排列总数为4!,因为前面有1种可能,所以这里可以得到   1*4!   个排列字典序小于P
  •         继续向下一位看。在1~5中排除掉刚刚已经在第一个位置的2(前面出现过了后面不可能再出现),就是在(1, 3, 4, 5)中,有(1, 3, 4) < 5,这里有3种可能,后面3个元素共有3!个排列,所以这里又可以得到   3*3!   个排列字典序小于P
  •         同理,下一位有   (1, 3) < 4(上面使用过2,故不算), 后面剩余2位,可以得到   2 * 2!   个排列
  •         下一位:有1 < 3 ,后面剩1位,得到   1*1!   
  •         最后一位:(无元素) < 1 ,后面剩0位,得到   0*0!   

总结一下:刚刚得到字典序小于P(2, 5, 4, 3, 1)的排列个数

X=1*4!+3*3!+2*2!+1*1!+0*0!=47

但这里需要注意:现在我们求的是 按字典序排序下 在排列P之前 有几个排列,我们原先要计算的是排名,所以要加上1,得X=48

公式

        上面举例过一遍了,下面的公式应该很好理解吧,就不细说了

        X=a_{1}(n-1)!+a_{2}(n-2)!+...+a_{i}(n-i)!+...+a_{n-1}\cdot1!+a_{n}\cdot0! + 1

        其中:

                {a_i}表示比第i位上的元素字典序小且没使用过的元素个数

                n既排列元素的个数

                X是排列的排名

完整定义

………………引自Zhihu user---我不玩了给我退学

(这里说排名从0开始,建议计算后手动加1.比较符合“排名”,更形象,加不加也无所谓啦)(看不看无所谓,知道基本意思就行了)

C++代码

代码实现
//计算阶乘
int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

//康托展开函数
int cantorExpansion(const vector<int>& p){
    int n = p.size();
    int x = 0;
    //计算康托展开的每一位
    for(int i = 0; i < n - 1;i++){
    	//smc:smaller_count
        int smc = 0;
        for(int j = i + 1; j < n;j++){
            if (p[j] < p[i]){
                smc++;
            }
        }
        x += smc * factorial(n - i - 1);
    }

    return x;
}

上面这样写比较直观;当然可以省去factorial函数,像下面这样(也就只改了个x的计算)

int cantorExpansion(const vector<int>& p){
	int n = p.size();
    int x = 0;
    for(int i = 0; i < n - 1;i++){
        int smc = 0;
        for(int j = i + 1; j < n;j++){
            if(p[j] < p[i]){
                smc++;
            }
        }
        x = (x + smc) * (n - i - 1);
    }
    return x;
}

(可能有些看官不理解第二段代码计算x的部分,这里解释一下:x+smc会累加的smc并乘上下一位对应的位数(也就是这一位阶乘的最高位),下一次一并计算,上次只乘了阶乘的最后一位的smc会再乘阶乘的下一位,直到都乘过了。光说可能有点抽象,下面模拟以下全排列1~5在此函数下X的计算过程( {a_i}表示比第i位上的元素字典序小且没使用过的元素个数):   

X=((((a_1*4)+a_2)*3+a_3)*2+a_4)*1  

展开可以得到

X=1*2*3*4\cdot a_1+1*2*3\cdot a_2+1*2\cdot a_3+1 \cdot a_4

自己推一下就能理解啦qwq)

代码分析

        两个不同的cantor函数写法都用了两层循环所以时间复杂度都是\theta (n^2)

        另外factorial函数分析得其实际复杂度是\theta (n)

        所以总体时间复杂度都是\theta (n^2)

代码优化

        这里其实可以用线段树将时间复杂度降到\theta (n \log n),至于为什么不放代码,别问,问就是不会:)。这里先插个锚点

标签:排列,21,29%,Cantor,plus,1%,net,展开,康托
From: https://blog.csdn.net/Ymy251325/article/details/140898645

相关文章

  • C++对象析构顺序问题——由QObject::desroyed展开的思考
    C++对象析构顺序问题——由QObject::desroyed展开的思考C++析构函数执行的顺序是最先执行继承链最末端的子类的,最后执行顶层的基类的。而QObject::destroyed(QObject*obj=nullptr)信号在Qt文档中说是“在obj被完全析构时之前立即触发,并且不会被阻塞”。这里的“完全析......
  • C语言 #具有展开功能的排雷游戏
    文章目录前言一、整个排雷游戏的思维梳理二、整体代码分布布局三、游戏主体逻辑实现--test.c四、整个游戏头文件的引用以及函数的声明--game.h五、游戏功能的具体实现--game.c六、老六版本 总结前言路漫漫其修远兮,吾将上下而求索。一、整个排雷游戏的思维......
  • 洛谷P1098 [NOIP2007 提高组] 字符串的展开
    题目链接:-P1098[NOIP2007提高组]字符串的展开题目叙述:[NOIP2007提高组]字符串的展开题目描述在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于d-h或者4-8的字串,我们就把它当作一种简写,输出时,用连续递增的字......
  • 实现el-table行展开可以定位到指定行功能
    实现方法1.拿到每一行的高度,2.再拿到每一行展开行的高度3.累加起来,让滚动条滚动到对应的高度tableScrollToRow(tableElement,rowIndex){constexpandedRows=tableElement.bodyWrapper.querySelectorAll(".el-table__expanded-cell");consttheTableRows=......
  • 安装量终于破千了!聊聊浏览器扩展开发的相关问题与解决方案
    浏览器扩展开发的相关问题与解决方案我开发的浏览器扩展安装量终于过千了!在FirefoxAddOns已经有2.1k+安装,在ChromeWebStore已经有2k+安装。实际上在Firefox的扩展市场里是周平均安装量,当天的实际安装量要高出平均值不少,而Chrome的扩展市场在超过1k安装量之后就不精确显示安......
  • c++参数包展开和折叠表达式
    template<typenameT>voidfun2(Tt){cout<<t<<endl;}//利用逗号表达式和初始化列表展开template<typename...Arg6>voidfun1(Arg6...args){intarr[]={(fun2(args),0)...};}template<typenameT>intfunc3Imp(T&&t......
  • 将格内多行文字展开成多格
    表格的A列是分类,B列由多行文字组成,即分隔符是换行符。AB1AccountNumberInteraction21Jan1,2023-Hello.32Jan2,2023-Goodmorning.Jan3,2023-Goodnight.Jan4,20Jan5,2023-Goodnight.Jan6,2023-Goodafternoon.43Jan1,2023-Goodnight.Jan2,2......
  • 洛谷P1014Cantor 表 C语言
    #include<stdio.h>intmain(){intinput;inth,k;inti,sum=0;scanf("%d",&input);for(i=1;;i++){sum+=i;//求出input数在那个范围内,i就是行数,sum就是所有行加起来数的个数if(sum>=input){h=......
  • 实现多个菜单或者多个操作按钮一键收起展开
    项目中有多个菜单按钮,显示为一排时很占位置不好看,所以就考虑做成可以收缩起来,使用时在展开,并且加上收起展开的动画效果,使其看起来更加自然好看,效果展示图片如下(结尾有视频效果展示):展开时:收起时:下面将我实现的代码展示:html代码片段:<divclass="bottom-operate-container"......
  • vue elementUI el-tree 下拉树功能(包括搜索/默认高亮/展开下拉框默认定位于选中项的位
    <template><div><el-form:model="formData"ref="refFormData"label-width="180px"><el-form-itemlabel="景点"prop="location_id"><el-selectv-model="formData.location_name&qu......