转载了一篇关于全排列的哈希函数,Poj1077就是应用了全排列的哈希;

我们经常使用的数的进制为“常数进制”,即始终逢p进1。例如,p进制数K可表示为
    K = a0*p^0 + a1*p^1 + a2*p^2 + ... + an*p^n (其中0 <= ai <= p-1),
它可以表示任何一个自然数。

对于这种常数进制表示法,以及各种进制之间的转换大家应该是很熟悉的了,但大家可能很少听说变进制数。这里我要介绍一种特殊的变进制数,它能够被用来实现全排列的Hash函数,并且该Hash函数能够实现完美的防碰撞和空间利用(不会发生碰撞,且所有空间被完全使用)。这种全排列Hash函数也被称为全排列数化技术。

变进制数:

我们考查这样一种变进制数:第1位逢2进1,第2位逢3进1,……,第n位逢n+1进1。它的表示形式为
    K = a1*1! + a2*2! + a3*3! + ... + an*n! (其中0 <= ai <= i),
也可以扩展为如下形式(因为按定义a0始终为0),以与p进制表示相对应:
    K = a0*0! + a1*1! + a2*2! + a3*3! + ... + an*n! (其中0 <= ai <= i)。
(后面的变进制数均指这种变进制数,且采用前一种表示法)

先让我们来考查一下该变进制数的进位是否正确。假设变进制数K的第i位ai为i+1,需要进位,而ai*i!=(i+1)*i!=1*(i+1)!,即正确的向高位进1。这说明该变进制数能够正确进位,从而是一种合法的计数方式。

n位变进制数K的性质:
(1)当所有位ai均为i时,此时K有最大值
    MAX[K] = 1*1! + 2*2! + 3*3! + ... + n*n!
           = 1! + 1*1! + 2*2! + 3*3! + ... + n*n! - 1
           = (1+1)*1! + 2*2! + 3*3! + ... + n*n! - 1
           = 2! + 2*2! + 3*3! + ... + n*n! - 1
           = ...
           = (n+1)!-1
    因此,n位K进制数的(9php.com)最大值为(n+1)!-1。
(2)当所有位ai均为0时,此时K有最小值0。
因此,n位变进制数能够表示0到(n+1)!-1的范围内的所有自然数,共(n+1)!个。

ps:全排列的Hash函数完美利用空间防碰撞的前提。

全排列的Hash函数:

在一些状态空间搜索算法中,我们需要快速判断某个状态是否已经出现,此时常常使用Hash函数来实现。其中,有一类特殊的状态空间,它们是由全排列产生的,比如N数码问题。对于n个元素的全排列,共产生n!个不同的排列或状态。下面将讨论如何使用这里的变进制数来实现一个针对全排列的Hash函数。

从数的角度来看,全排列和变进制数都用到了阶乘。如果我们能够用0到n!-1这n!个连续的变进制数来表示n个元素的所有排列,那么就能够把全排列完全地数化,建立起全排列和自然数之间一一对应的关系,也就实现了一个完美的Hash函数。那么,我们的想法能否实现呢?答案是肯定的,下面将进行讨论。

假设我们有b0,b1,b2...bn 共 n+1 个不同的元素,假设各元素之间有一种次序关系b0 < b1 < b2 ...< bn。对它们进行全排列,共产生(n+1)!种不同的排列。对于产生的任一排列 c0,c1,c2,..,cn,其中第i个元素ci(1 <= i <= n)与它前面的i个元素构成的逆序对的个数为di(0 <= di <= i),那么我们得到一个逆序数序列d1,d2,...,dn(0 <= di <= i)。这不就是前面的n位变进制数的各个位么?于是,我们用n位变进制数M来表示该排列:
   M = d1*1! + d2*2! + ... + dn*n!
因此,每个排列都可以按这种方式表示成一个n位变进制数。下面,我们来考查n位变进制数能否与n+1个元素的全排列建立起一一对应的关系。

由于n位变进制数能表示(n+1)!个不同的数,而n+1个元素的全排列刚好有(n+1)!个不同的排列,且每一个排列都已经能表示成一个n位变进制数。如果我们能够证明任意两个不同的排列产生两个不同的变进制数,那么我们就可以得出结论:
★ 定理1 n+1个元素的全排列的每一个排列对应着一个不同的n位变进制数。

对于全排列的任意两个不同的排列p0,p1,p2,...,pn(排列P)和q0,q1,q2,...,qn(排列Q),从后往前查找第一个不相同的元素,分别记为pi和qi(0 < i <= n)。
(1)如果qi > pi,那么,
如果在排列Q中qi之前的元素x与qi构成逆序对,即有x > qi,则在排列P中pi之前也有相同元素x > pi(因为x > qi且qi > pi),即在排列P中pi之前的元素x也与pi构成逆序对,所以pi的逆序数大于等于qi的逆序数。又qi与pi在排列P中构成pi的逆序对,所以pi的逆序数大于qi的逆序数。
(2)同理,如果pi > qi,那么qi的逆序数大于pi的逆序数。
因此,由(1)和(2)知,排列P和排列Q对应的变进制数至少有第i位不相同,即全排列的(9php.com)任意两个不同的(9php.com)排列具有不同的变进制数。至此,定理1得证。

计算n个元素的一个排列的变进制数的算法大致如下(时间复杂度为O(n^2)):
template <typename T>
size_t PermutationToNumber(const T permutation[], int n)
{
    // n不能太大,否则会溢出(如果size_t为32位,则n <= 12)
    size_t result = 0;
    for (int j = 1; j < n; ++j) {
        int count = 0;
        for (int k = 0; k < j; ++k) {
            if (permutation[k] > permutation[j])
                ++count;
        }
        // factorials[j]保存着j!
        result += count * factorials[j];
    }

return result;
}

说明:
(1)由于n!是一个很大的数,因此一般只能用于较小的n。
(2)有了计算排列的变进制数的算法,我们就可以使用一个大小为n!的数组来保存每一个排列的状态,使用排列的变进制数作为数组下标,从而实现状态的快速检索。如果只是标记状态是否出现,则可以用一位来标记状态。

n数码问题, 全排列哈希的更多相关文章

  1. 三羊献瑞——第六届蓝桥杯C语言B组(省赛)第三题

    原创 三羊献瑞 观察下面的加法算式: 祥 瑞 生 辉 + 三 羊 献 瑞 ------------------- 三 羊 生 瑞 气 (如果有对齐问题,可以参看[图1.jpg]) 其中,相同的汉字代表 ...

  2. Eight(bfs+全排列的哈希函数)

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22207   Accepted: 9846   Special Judge ...

  3. 【算法】BFS+哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...

  4. BFS-八数码问题与状态图搜索

    在一个3*3的棋盘上放置编号为1~8的八个方块,每个占一格,另外还有一个空格.与空格相邻的数字方块可以移动到空格里.任务1:指定的初始棋局和目标棋局,计算出最少的移动步数:任务2:数出数码的移动序列. ...

  5. [LeetCode] Palindrome Permutation II 回文全排列之二

    Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empt ...

  6. BFS(八数码) POJ 1077 || HDOJ 1043 Eight

    题目传送门1 2 题意:从无序到有序移动的方案,即最后成1 2 3 4 5 6 7 8 0 分析:八数码经典问题.POJ是一次,HDOJ是多次.因为康托展开还不会,也写不了什么,HDOJ需要从最后的状 ...

  7. 双向广搜+hash+康托展开 codevs 1225 八数码难题

    codevs 1225 八数码难题  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond   题目描述 Description Yours和zero在研究A*启 ...

  8. hiho_100_八数码

    题目大意 8数码问题,在3x3的矩阵中填入0-8九个数字,0可以和它相邻的数字进行交换.从初始状态到达状态F(3x3的方格从上到下,从左到右,形成的数字串为123456780)所需要最少移动的次数. ...

  9. 面试:如何找出字符串的字典序全排列的第N种

    1.题目 如何找出字符串的字典序全排列的第N种?(字符串全排列的变种) 2.思路 主要想通过这题,介绍一下康托展开式.基于康托展开式可以解决这个问题. 一般的解法:①求出所有全排列 ②按照字典序排个序 ...

随机推荐

  1. Hive数据导入

    可以通过多种方式将数据导入hive表 1.通过外部表导入 用户在hive上建external表,建表的同时指定hdfs路径,在数据拷贝到指定hdfs路径的同时,也同时完成数据插入external表. ...

  2. 再回首,Java温故知新(九):Java基础之流程控制语句

    流程控制语句分为条件语句.循环语句和中断语句. 中断语句包括break和continue,两者的区别在于break会跳出整个循环,而continue则是跳出此次循环,之后还会继续下一次循环. 条件语句 ...

  3. IOS-AssetsLibrary(相册)框架介绍

    AssetsLibrary框架介绍: AssetsLibrary框架包含了ALAssetsLibrary,ALAssetsGroup,ALAsset,ALAssetsFilter,ALAssetRep ...

  4. iOS 数据持久化(2):SQLite3

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...

  5. 解决github提交commit,contributions不统计显示绿色的问题

    最近使用GitHub Desktop时,发现自己好多次的commits都没有被记录在Contributions中,但是点开项目详情里面可以看到自己的commit确实上传成功了,所以就忧伤了,为什么没有 ...

  6. PL/SQL 触发器简介

    与公司同事交流了一下,得知触发器很少用.性能是一方面,主要是如果用太多触发器,可能到时你都不知道会有什么操作自动发生. 有些操作可以在程序中控制.例如在插入某个表时,写个log表的记录.这可以用触发器 ...

  7. VS2010在WIN7 64位系统下架设网站及路由器配置

    步骤一:安装IIS 打开[控制面板]-[程序和功能],在左侧进入[打开或关闭windows功能],按照下图选择Internet信息项目下的子选项并安装: 步骤二:配置应用程序池 打开[控制面板]-[管 ...

  8. linux常用命令之ln

    ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s,具体用法是:ln –s 源文件 目标文件. 当我们需要在不同的目录,用到相同的 ...

  9. app图标和启动页设置

    弄了一下午,终于把iOS中图标的设置和启动页的设置弄明白了.我想以后再也不会浑了. 进入正题: 一:apple 1).iPhone4s 3.5寸屏,也就是640*960,但在模拟器上正常用的是320* ...

  10. Xcode添加静态库以及编译选项配置常见问题

    一,Xcode编译出现Link错误,出现"duplicate symbols for architecture i386 clang"提示.问题:链接时,项目有重名文件.解决:根据 ...