Cantor expansion

Cantor expansion的本质是将一个排列hash成为一个数,这个数就是这个排列rank值,将原本需要用nn的空间来记录的排列状态在n!的空间内记录下来,有效利用了空白的空间。而将排列变为排名的桥梁便是展开后的那个an数组。其实原理非常简单,但是对于解决类似N数码问题等牵涉的排列状态记录的问题时特别有用。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 9;//保存排列的长度
int fac[maxn + 1];//保存阶乘
/*
*康托展开的值与实际rank值相差1
*/
void initfac(){//初始化阶乘
    fac[0] = 1;
    for (int i = 1; i <= maxn; i++){
        fac[i] = fac[i - 1] * i;
    }
}

int cantorEncode(const int a[]){//将排列逆康托展开,排列下标从1开始,所得值为rank
    bool flag[maxn + 1] = { false };
    int x = 0;
    for (int i = 1; i <= maxn; i++){
        int cnt = 0;
        for (int j = i + 1; j <= maxn; j++){
            if (!flag[j]){
                flag[j] = true;
                cnt++;
            }
        }
        x += fac[maxn - i] * cnt;
    }
    return x+1;
}
void cantorDecode(int rank, int a[]){//将x=rank-1康托展开,保存到a[]中,下标从1开始
    rank--;
    int tmp[maxn + 1] = { 0 };
    for (int i = 1; i <= maxn; i++){
        tmp[i] = rank / fac[maxn - i];
        rank -= fac[maxn - i] * tmp[i];
    }
    bool flag[maxn + 1] = { false };
    for (int i = 1; i <= maxn; i++){
        int cnt = 0;
        for (int j = 1; j <= maxn;j++){
            if (!flag[j]){
                cnt++;
                if (cnt == tmp[i]+1){
                    a[i] = j;
                    flag[j] = true;
                    break;
                }
            }
        }
    }
}
void printArray(int a[]){
    for (int i = 1; i <= maxn; i++){
        printf("%d%c", a[i], " \n"[i == maxn]);
    }
}


康托展开就是一种特殊的哈希函数

  把一个整数X展开成如下形式:

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

  其中,a为整数,并且0<=a<i,i=1,2,..,n

  {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。

  代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。

  他们间的对应关系可由康托展开来找到。

  如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :

  第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个 
。所以321是第6个大的数。 2*2!+1*1!是康托展开。

  再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以 
有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。

时间: 2024-11-13 04:40:12

Cantor expansion的相关文章

Cantor expansion and deCantor expansion

Cantor expansion is a way to use the Full Permutation and the id in the Permutation.In this way to map the id and the permutation,we can create a easy hash.It can be said a the most easy hash.\(ans=a_{0}\cdot (n-1)!+a_{1}\cdot(n-2)!+\cdot\cdot\cdot+a

[算法总结]康托展开Cantor Expansion

[算法总结]康托展开Cantor Expansion 一.关于康托展开 1.什么是康托展开 求出给定一个由1~n个整数组成的任意排列在1~n的全排列中的位置. 解决这样问题的算法叫康托展开. 例如: \(n=4\),序列a={\(1,3,4,2\)},那么a在1~4中的全排列位置为第4个. 2.康托展开实现原理 要知道序列a排在第几位,我们就需要知道序列a之前有多少位. 我们按照上面的栗子计算: 1.比1小的数有0个,有\(0\times(4-1)!=0\)种排列. 2.比3小的数有2个,但是1

codeforce 285 div2 D 题解

codeforce 285 div2 D 题解 说明 这道题目是看了思路分析才知道的,关键问题在于数据量过大,需要快速检索的同时不能辅助空间过大. 因此利用了下面3种方法结合解决该问题 康拓展开与逆康拓展开 树状数组 二分查找 代码 /** * @brief Codeforces Round #285 (Div. 2) d * @file d.cpp * @author mianma * @created 2015/01/27 18:18 * @edited 2015/01/29 22:45 *

Leetcode分类解析:组合算法

Leetcode分类解析:组合算法 所谓组合算法就是指:在解决一些算法问题时,需要产生输入数据的各种组合.排列.子集.分区等等,然后逐一确认每种是不是我们要的解.从广义上来说,组合算法可以包罗万象,甚至排序.各种搜索算法都可以算进去.最近读<The Algorithm Design Manual>时了解到这种归类,上网一查,甚至有专门的书籍讲解,而且Knuth的巨著TAOCP的第四卷就叫组合算法,看来还真是孤陋寡闻了!于是最近着重专攻了一下Leetcode中所有相关题目,在此整理一下学习心得.

leetcode笔记:Permutation Sequence

一.题目描述 题目的意思是,假设有{1,2,3,4,-,n},对其中的元素进行排列,总共有n!种组合,将它们从小到大排序,问其中第k个组合的形式是怎样的? 二.题目分析 方法一:可以一个一个的按次序暴力求解.具体实现可参照题目:Next Permutation.这里并没有实现,主要研究的是方法二的Cantor expansion算法. 方法二:数学解法:Cantor expansion Cantor expansion算法的思想是,在n!个排列中,第一位的元素总是(n-1)!一组出现的,也就说如

cantor三分集

值得一提的是,第一次听说cantor三分集是在数字电路课上,然而数电是我最不喜欢的课程之一...... 分形大都具有自相似.自仿射性质,所以cantor三分集用递归再合适不过了,本来不想用matlab的,毕竟以后不会靠这东西.但是考虑到其方便的绘图功能还是用了.matlab写递归还是头一遭,心慌慌,不过试了一下发现和其他语言基本没差别! 源码 function cantor(Ax, Ay, Bx, By) precision = 0.001; if Bx-Ax < precision plot(

bzoj1730 [Usaco2005 dec]Barn Expansion 牛棚扩张

1730: [Usaco2005 dec]Barn Expansion 牛棚扩张 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 134  Solved: 72[Submit][Status][Discuss] Description Farmer John has N (1 <= N <= 25,000) rectangular barns on his farm, all with sides parallel to the X and Y ax

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result异常的解决方法

今天在写一个JAVA程序的时候出现了异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.发现报错的语句是: 1 foo.divide(bar)); 原来JAVA中如果用BigDecimal做除法的时候一定要在divide方法中传递第二个参数,定义精确到小数点后几位,否则在不整除的情况下,结果是无限循环小数时,就会抛出以上异常.解决方法:

Non-terminating decimal expansion; no exact representable decimal result(转)

Non-terminating decimal expansion; no exact representable decimal result - lopper的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/lopper/article/details/5314686 由于需要处理精度比较高的浮点数,所以弃用double类型,改用BigDecimal类来进行数值处理. 在加减乘时都没有出现问题,但是到除法运算时,提示了如下错误: 大概的意思是“无法结束的除