递增全排列算法

问题

给定一个递增序列<a_1,a_2,\cdots,a_n>
其中,\forall i,j \in \{1,2,\cdots,n\},若i<j,则a_i<a_j。求它的递增全排列集合\mathbb P

所谓递增全排列\mathbb P是这样一个有序集合:
{\mathbb P}=<p_1,p_2,\cdots,p_{n!}>其中p_i(i=1,2,\cdots,n!)是序列的一个全排列,并且\forall i,j \in \{1,2,\cdots,n!\},都有p_i<p_j,表示p_j的自然顺序大于p_i。例如,对于递增序列<1,2,3>123,231都是它的一个全排列,且231的自然顺序大于123
显然,对于\mathbb P,一定有p_1=a_1a_2\cdots a_np_{n!}=a_n a_{n-1} \cdots a_1

分析

{\mathbb P}=P(<a_1,a_2,\cdots,a_n>)
我们定义加法运算a+b表示两个序列a,b的拼接。例如,若a=a_1a_2\cdots a_mb=b_1b_2\cdots b_n,则
a+b=a_1a_2\cdots a_m b_1b_2\cdots b_n
那么,自然可以定义一个序列a与一个有序集合的加法运算
a+{\mathbb P}=<a+p_1,a+p_2,\cdots,a+p_{n!}>
这个式子进一步可以用求和符号简化为
a+{\mathbb P}=<\sum_{i=1}^{n!}a+p_i>

现在考虑函数P,由于其产生一个递增全排列集合,显然前(n-1)!项全排列的第一个元素是a_1,第(n-1)!+1到第2\cdot (n-1)!项全排列的第一个元素都是a_2,……,第(k-1)\cdot(n-1)!+1到第k\cdot (n-1)!项的第一个元素都是a_k。这就意味着
P(<a_1,a_2,\cdots,a_n>)=<\sum_{i=1}^n \left( a_i+P(<a_1,a_2,\cdots,a_{i-1},a_{i+1},\cdots,a_n>)\right)>
由于剩余的序列<a_1,a_2,\cdots,a_{i-1},a_{i+1},\cdots,a_n>仍然是有序的,因此对剩余的(n-1)个序列产生递增的全排列集合就成为该问题的子问题,我们使用递归定义了它们。

现在考虑最基本的情况:

  1. 序列没有任何元素,显然P(<>)=\varnothing
  2. 序列只有一个元素,显然P(<a>)=<a>,即一个元素的全排列只有一种,就是它本身。

由此我们可以得到产生递增的全排列集合的算法。

算法

  • 输入:一个有限的递增有序序列A=<a_1,a_2,\cdots,a_n>
  • 输出:序列A递增全排列集合R
  1. 初始化结果集R=\varnothing
  2. 若序列A中仅有一个元素a,或为空(此时a=\varnothing),则R=R\cup \{<a>\},算法结束,返回R
  3. 否则,分别令i=1,2,\cdots,n,对于a_i,递归地使用该算法计算剩余的有序序列<a_1,a_2,\cdots,a_{i-1},a_{i+1},\cdots,a_n>的递增全排列集合R'_i,令R_i=a_i+R'_i,由此我们得到了以a_i为固定首位的全排列集合。从而归并到结果集合中R=R\cup R_i
  4. 由于A中元素是有限的,所以算法的递归过程一定会到达2中的状态并终止,从而算法在有限步骤后一定会终止。

代码

使用JavaScript实现的代码如下

function generatePermutation(n){
    let seq=(length=>Array.from({length},($,i)=>i+1))(n),result=[];
    //Require substring of seq starting from start is a rigidly-ascending ordered sequence,
    //namely, for any i,j with start<=i<j<length, seq[i]<seq[j] holds.
    (function permutation(seq,result,start=0){
        //recursive function for all permutations of seq with a specified start index(counting from 0).
        //The permutation of single element is this element itself, so stop here.
        start===seq.length-1 && result.push(Array.from(seq));

        //Now,we can get all permutations by the following: 
        //  Permutation(<a1,a2,...,an>)
        //  =SUM_[from i=1 to n] ( ai + Permutation(<a1,a2,...,a_{i-1},a_{i+1},...,an>) )
        //  where a1<a2<...<an, and notation <...> stands for an ordered sequence.
        for(let i=start;i<n;i++){
            // to achieve this, we can move ai to the head, leaving the remnants,say,
            // <a1,a2,...,a_{i-1},a_{i+1},...,an> ,still ordered. Thus, we apply permutation
            // on these remnants, which turns to be a subproblem. 
            // NOTICE: if ascending ordered permutations are required, we use function move.
            // Otherwise, a simple swap can be used here.
            // 
            move(seq,i,start);      //swap(seq,i,start);
            permutation(seq,result,start+1);
            move(seq,start,i+1);    //swap(seq,i,start);
        }
    })(seq,result);
    return result;
}

//move seq[from] to before seq[to]
function move(seq,from,to){
    [[to,0,seq[from]],[from+(from>to),1]].forEach(arr=>seq.splice(...arr))
}

// swap seq[i1] with seq[i2]
function swap(seq,i1,i2){
    [seq[i1],seq[i2]]=[seq[i2],seq[i1]];
}

generatePermutation(4)

为了保证剩余序列的有序,使用move函数将a_i元素移动到数列的首位,此时对剩余序列(start=start+1开始的下标)进行递归排序。

注意,代码中还提供了一个交换函数swap,如果将generatePermutation中的move替换为swap,那么产生的结果集合仍然是全排列集合,但是它不是有序排列的集合。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268