全排列的生成算法(八)——序数法

n个元素的全排列有n!个,如果将排列按顺序编号,并能够按照某种方法建立起每一个序号与一个排列之间的对应关系,那么就可以根据序号确定排列,反过来也可以根据排列确定它的序号。根据排列的序号生成对应排列的方法就称为序数法。

通常,我们使用的计数法是十进制数。十进制数的位权是10,也就是逢十进一。另外还有二进制、八进制和十六进制等,它们的位权分别是2、8和16。

排列数与n!的阶乘密切相关,因此可以用一种阶乘进制数来建立排列与它的序号的对应关系。阶乘进制数用0!、1!、2!、……分别作为(从右向左的)第一位、第二位、……的位权,显然这是一种可变的位权。举例来说,一个多位数123,如果是十进制,它的大小是3×100+2×101+1×102=(123)10,就是一百二十三。如果是八进制,就是(123)8=3×80+2×81+1×82=(83)10,就是十进制的83。如果是阶乘进制,它就是3×0!+2×1!+1×2!=(8)10,即十进制的8。

某一个n阶排列的序号是m,那么将m转换为阶乘进制数后,阶乘进制数的第i位就是在i右面比i小的元素个数。例如4阶排列中(从0开始计数的)第19个排列的序号是19,将19转换成阶乘进制数是3010,那么,第一位是0,表明1的右面没有比1小的元素,而第二位是1,则2的右面有一个元素小于2,第三位是0,即3的右面没有比它小的元素,第四位是3,4的右面有3个元素小于它。显然,这个排列是4 2 1 3。

序数法生成全排列的算法如下:

第一步:将排列的序号m转换成阶乘进制数

第二步:根据阶乘进制数的各位数值将元素1、2、……、n赋给数组p的相应元素。

在第一步中,从十进制数转换成阶乘进制数,步骤与一般十进制装换成其他进制数相同,也就是用欲转换的数制的位权除十进制数取余数。

第二步中则与递增进位法相同,将数字n、n-1、……、1填入n个空格的相应位置。

具体算法如下:

//全排列的序数法

//输入:排列元素个数n

//输出:全体排列

#include <iostream>

#include <iomanip>

using namespace std;

Void perm(int);

Void output(int *,int);

int total;

int main(void)

{

freopen("in.dat","r",stdin);

int n;

while(cin>>n)

{

total=0;

perm(n);

}

return 0;

}

void output(int *p,int n)

{

cout<<setw(4)<<++total<<": ";

for(int i=0;i<n;i++)

cout<<setw(3)<<p[i];

cout<<endl;

}

void perm(int n)

{

int i,j,k,m,s;

int *p=new int[n],*q=new int[n];

fill_n(q,n,0);                     //初始化

m=0;                           //原始排列序号

while(1)

{

fill_n(p,n,0);                 //数组初始化

for(i=0;i<n;i++)             //按阶乘进制数各位的值安排各元素位置

for(j=n-1,k=0;j>=0;j--)   //从n开始

{

if(p[j])

continue;

if(k==q[i])

{

p[j]=n-i;

break;

}

k++;

}

output(p,n);                 //输出一个排列

s=++m;               //下一个排列的序号

for(i=1;i<=n;i++)           //将序号转换成阶乘进制数

{

q[n-i]=s%i;

s/=i;

}

for(j=0,k=0;j<n;j++)        //如果阶乘进制数各位回到0

k+=q[j];

if(k==0)                  //结束

break;

}

}

阶乘的序数转换成阶乘进制数后,也可以按照的增进位方式产生后继(阶乘进制)序数。

现在设置数组q,并用q[i]表示阶乘进制数的第n-i-1位,那么就有0≤q[i]≤i-1。这样,从q[0]=q[1]=……出发,对q[n-2]累加1,并按照q[i-1]=i时进位,就可以数次得到序数0、1、……、n!。同时也可以生成全体n阶排列。

//递增进位序数法

//输入:排列元素个数

//输出:全体n阶全排列

#include <iostream>

#include <iomanip>

using namespace std;

void perm(int);

void output(int *,int);

int total;

int main(void)

{

freopen("in.dat","r",stdin);

int n;

while(cin>>n)

{

total=0;

perm(n);

}

return 0;

}

void output(int *p,int n)

{

cout<<setw(5)<<++total<<": ";

for(int i=0;i<n;i++)

cout<<setw(3)<<p[i];

cout<<endl;

}

void perm(int n)

{

int *p=new int[n];

int *q=new int[n];              //阶乘进制数

fill_n(q,n,0);                  //初始化

int i,j,k;

while(1)

{

fill_n(p,n,0);              //初始化

for(i=0;i<n;i++)          //按照阶乘进制数各位安排元素位置

for(j=n-1,k=0;j>=0;j--)

{

if(p[j])

continue;

if(k==q[i])

{

p[j]=n-i;

break;

}

k++;

}

output(p,n);                   //输出一个排列

i=n-2;                       //计算下一个序数

q[i]++;

while(q[i]>n-i-1 && i>0)       //递增进位

q[i--]=0,q[i]++;

if(q[0]==n)                  //超出n位

break;                   //结束

}

}

int main(void)

{

int n=5;

total=0;

perm(n);

return 0;

}

全排列的算法(八)——序数法相关推荐

  1. c支限界算法语言n皇后问题分,算法(八)-回溯法-N皇后问题

    问题描述: 算法思想: 代码实现: #include using namespace std; class Queen { friend int nQueen(int);//求解八皇后 private ...

  2. c语言全排列库函数,几种全排列的算法(C语言实现)

    /* * 几种排列组合的算法 */ #include int a[20]; int n; //打印数组 void showArray(int *a) { int i; for(i=1;i<=n; ...

  3. 排列的生成(二) —— 序数法

    1. 定义 n n n个元素的全排列有 n ! n! n!个,如果将排列按顺序编号,并能够按照某种方法建立起每一个序号与一个排列之间的对应关系,那么就可以根据序号确定排列,反过来也可以根据排列确定它的 ...

  4. 五大经典算法之回溯法

    一.基本概念   回溯法,又称为试探法,按选优条件向前不断搜索,以达到目标.但是当探索到某一步时,如果发现原先选择并不优或达不到目标,就会退回一步重新选择,这种达不到目的就退回再走的算法称为回溯法. ...

  5. 分油问题回朔法c语言算法,用回溯法求“韩信分油”问题所有解

    裴南平 摘要:回溯法是一种常用的计算机程序设计方法.使用回溯法解决"韩信分油问题"也称"泊松分酒问题",在算法中保存每一步执行的中间结果,程序扩展前,判斷程序是 ...

  6. 第39级台阶回溯算法c语言,五大经典算法之回溯法 - osc_9ipdey7e的个人空间 - OSCHINA - 中文开源技术交流社区...

    一.基本概念 回溯法,又称为试探法,按选优条件向前不断搜索,以达到目标.但是当探索到某一步时,如果发现原先选择并不优或达不到目标,就会退回一步重新选择,这种达不到目的就退回再走的算法称为回溯法. 与穷 ...

  7. 求全排序的经典算法“后补法”

    //用于求全排序的经典算法"后补法",代码如下: #include <stdio.h> #include <conio.h> #include <st ...

  8. 算法最少分组法_数据结构

    20210328 https://blog.csdn.net/sinat_41144773/article/details/89530403 树.二叉树(完全二叉树.满二叉树)概念图解 二叉树:每个结 ...

  9. java 二分查找_计算机入门必备算法——二分查找法

    1.引言 笔者对于计算机的研究一直停滞不前,近期想对一些算法进行复习和进一步的研究,每天都会更新一个新的算法,算法有难有易,层层递进.不希望能学的有多么高深,只希望在一些最基本的算法上有编码的思路,或 ...

最新文章

  1. Python入门100题 | 第050题
  2. tensorflow在训练的时候权重是nan,如何解决
  3. 1.3.3 错题整理(组成原理)
  4. eclipse export jar file 和 runnable jar file 的区别
  5. Java执行存储过程
  6. 【qduoj - 1012】反转数字(模拟,水题)
  7. bat导出远程oracle数据,windows 任务计划 实现oracle远程 数据库备份
  8. cli php 增强包_Linux 上安装 PHP 扩展
  9. 数据结构与算法(三)-线性表之静态链表
  10. linux 解压加密zip,linux 系统下 zip 的加密压缩与解压缩命令
  11. 《刘润·5分钟商学院》学习总结01
  12. 小米商城网页制作大全-完结篇
  13. PHP学习之SAPI
  14. numpy到pytorch,鸟枪换炮
  15. 8个免费的高质量UI图标大全网站
  16. Arduino使用雨滴模块
  17. 更新again:微机原理与汇编语言-练习题
  18. 画论56 恽格《南田画跋》
  19. Windows-tree命令生成目录树
  20. 图像增强算法汇总(直方图均衡化、拉普拉斯、Log变换、gamma伽马变换)附MATLAB代码

热门文章

  1. Kafka 是如何建模数据的?
  2. java 小孩报数_N个小孩围成一圈1-3报数,报3出局
  3. ASP.NET的六种验证控件,及正则表达式
  4. docker 如何批量删除镜像
  5. 取消检验批过账(决策)
  6. 来看看怎么用OpenCV解构jagarikin的视觉错觉图
  7. Prometheus组件详解
  8. mapv地图文字标注
  9. 硬盘分区表丢失怎么恢复
  10. The missing semester of your CS education--调试及性能分析