当前位置: 首页 > news >正文

康拓展开及其逆运算和全排列函数

有所摘抄,但重要的是自己的想法。奋斗


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

     

       X   =    a[n]*(n-1)! + a[n-1]*(n-2)! + ... + a[i]*(i-1)! + ... + a[1]*0!

index:     1     2     ...          n-i+1   ...        n

            其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n

a[i] 是:假设 一串数字中第index个元素为e,   a[i]表示第index个数e之后的数比e小的数的个数

X表示序列是全排列中第几个排列(从0开始)。

举个例子:

原序列: 1 2 3 4 5 6 7 8

求: 3 5 7 4 1 2 9 6 8

X = 2*8! + 3*7! + 4*6! + 2*5! + 0*4! + 0*3! + 2*2! + 0*1! + 0*0! = 98884.

解释:

     排列的第一位是3,比3小的数有两个,以这样的数开始的排列有8!个,因此第一项为2*8!

        排列的第二位是5,比5小的数有1、2、3、4,由于3已经出现,因此共有3个比5小的数,这样的排列有7!个,因此第二项为3*7!

        以此类推,直至0*0!

 用途: 

显然,n位(0~n-1)全排列后,其康托展开唯一且最大约为n!,因此可以由更小的空间来储存这些排列。由公式可将X逆推出对应的全排列。

    总之就是用来压缩空间的。。本人也不大懂。


例题:

现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?

每行输入一行字符串,保证是a~l这12个字符的某种排列
EOF结束

输出一个整数,代表这个排列排在第几位

 
	abcdefghijkl
	abcdefghiklj
	gfkedhjblcia
	1
	4
	260726926

#include <iostream>
#include <cstdio>
#define LL long long
using namespace std;
int factorial(int l){
	if(l == 0 || l == 1) return 1;
	else return l * factorial(l-1);
}
int main(){
	char a[13];
	LL ans;
	int i, j, k;
	while(~scanf("%s", a+1)){
		ans = 1;
		for(i = 1; i <= 12; ++i){
			k = 0;
			for(j = i+1; j <= 12; ++j)
				if(a[j] < a[i]) ++k;
			ans += factorial(12-i) * k;
		}
		printf("%lld\n", ans);
	}
	return 0;
}


另外,康托展开的逆运算,即通过知道它是第几个全排列求全排列

 

例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
(1)找出第96个数

首先用96-1得到95
用95去除4! 得到3余23
有3个数比它小的数是4
所以第一位是4
用23去除3! 得到3余5
有3个数比它小的数是4但4已经在之前出现过了所以第二位是5(4在之前出现过,所以实际比5小的数是3个)
用5去除2!得到2余1
有2个数比它小的数是3,第三位是3
用1去除1!得到1余0
有1个数比它小的数是2,第二位是2
最后一个数只能是1
所以这个数是45321

(2)找出第16个数

首先用16-1得到15
用15去除4!得到0余15
用15去除3!得到2余3
用3去除2!得到1余1
用1去除1!得到1余0
有0个数比它小的数是1
有2个数比它小的数是3 但由于1已经在之前出现过了所以是4(因为1在之前出现过了所以实际比4小的数是2)
有1个数比它小的数是2 但由于1已经在之前出现过了所以是3(因为1在之前出现过了所以实际比3小的数是1)
有1个数比它小得数是2 但由于1,3,4已经在之前出现过了所以是5(因为1,3,4在之前出现过了所以实际比5小的数是1)
最后一个数只能是2
所以这个数是14352


最后,还有一个全排列函数 :next_permutation函数大笑

这是一个求一个排序的下一个排列的函数,可以遍历全排列,要包含头文件<algorithm>
与之完全相反的函数还有prev_permutation

但做本题是会超时(不用想也是,一个一个求肯定TLE)

该函数可以对很多类型的数组序列进行求下一个字典序排列,即按全排的顺序求下一个排序序列

这里也贴一下本题的TLE代码(嘿嘿)吐舌头

//TLE
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main(){
	char b[20];
	int res;
	while(~scanf("%s", b)){
		char a[] = "abcdefghijkl";
		char aa[] = "gfkedhjblcia"
		if(strcmp(a, b) == 0) {printf("1\n"); continue;}
		res = 1;
		while(next_permutation(a, a+12)){
			++res;
			if(strcmp(a, b) == 0) {
				printf("%d\n", res);
				break;
			}
		}
	}
	return 0;
}

此函数各种类型的用法详解:http://blog.sina.com.cn/s/blog_9f7ea4390101101u.html



2017/4/1日增:
1.
//康拓代码实现
#include <bits/stdc++.h>
using namespace std;
int factor(int k){
	if(k == 0 || k == 1) return 1;
	return k * factor(k-1);
}
int main(){
	int n, i, ans, k;
	char jk[25];
	while(cin >> jk+1){
		ans = 0, n = strlen(jk+1);
		for(i = 1; i <= n; ++i){
			k = 0;
			for(int j = i+1; j <= n; ++j)
				if(jk[i] > jk[j]) ++k;
			ans += k * factor(n-i);
		}
		cout << ans+1 << endl;	//最小序列ans为0,同理ans+1;
	}
	return 0;
}
2.
//逆康拓代码实现
#include <bits/stdc++.h>
using namespace std;
int factor(int k){
	if(k == 0 || k == 1) return 1;
	return k * factor(k-1);
}
int ans[105], book[105];
int main(){
	int n, k, m, key, x, j;
	while(cin >> n >> m){
		memset(book, 0, sizeof book);
		--n;				//最小序列ans为0,所以需要--;
		for(int i = 1; i <= m; ++i){
			k = factor(m-i);
			key = n/k+1;	//在(m-i)!的系数+1,即当前位置的数值大于后面的个数+1,+1为了方便后面求该数
			n %= k;			//更新n
			j = 1;			//初始化j
			while(key){
				if(!book[j]) --key;
				++j;
			}
			ans[i] = j-1;	//当找到该数时j多加了一次1
			book[j-1] = 1;
		}
		for(int i = 1; i <= m; ++i)
			printf("%d", ans[i]);
		printf("\n");
	}
	return 0;
}


相关文章:

  • 用R分析时间序列(time series)数据
  • QDUoj GZS的三角形 棋盘里的数学 (数学规律题)
  • N-tier architecture N层架构 (转)
  • 树状数组区间更新+区间查询+单点查询
  • PHPCMS如何实现后台访问限制?
  • 树的直径 —— 即一棵树的最长路 附题(大臣的旅费 by蓝桥杯)
  • 一个关于按位或的故事~~(QDU-码农必修)
  • ConcurrentHashMap 解读(一)
  • Today一只菜鸡的PAT甲级测试(PAT1124, PAT1125, PAT1126, PAT1127)
  • 快速排序--自行实现+qsort+sort
  • 归并排序--二路归并
  • quartz定时任务时间设置描
  • ctype.h头文件中的tolower和toupper以及cctype其他函数的应用
  • mbr损坏以及grub.conf的配置文件丢失或出错的方法
  • 单链表的基本操作
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • Intervention/image 图片处理扩展包的安装和使用
  • Java多线程(4):使用线程池执行定时任务
  • MQ框架的比较
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • scala基础语法(二)
  • spark本地环境的搭建到运行第一个spark程序
  • Wamp集成环境 添加PHP的新版本
  • 从setTimeout-setInterval看JS线程
  • 工作手记之html2canvas使用概述
  • 开源SQL-on-Hadoop系统一览
  • 利用jquery编写加法运算验证码
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 实习面试笔记
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • #{}和${}的区别?
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • $().each和$.each的区别
  • (2)Java 简介
  • (k8s中)docker netty OOM问题记录
  • (NSDate) 时间 (time )比较
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (转) Android中ViewStub组件使用
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)scrum常见工具列表
  • (转载)CentOS查看系统信息|CentOS查看命令
  • *** 2003
  • .bashrc在哪里,alias妙用
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • ::before和::after 常见的用法
  • @converter 只能用mysql吗_python-MySQLConverter对象没有mysql-connector属性’...