您的位置:首页 > 编程语言 > Java开发

归纳法-生成全排列算法Java实现

2016-02-19 14:11 483 查看

   全排列问题

    从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。《算法设计与技巧分析》这本书中,使用归纳法生成一组数【1,2,3……n】的所有排列问题,并用数组P[1……n]来存放每一个排列。该归纳法基于这样一个假设:可以生成n-1个数的所有排列,那么就可以扩展方法,生成1,2,……n这n个数的全排列。

   全排列生成方法

    生成2,3,……n,这n-1个数的所有排列,再在这些所有排列前面加上第N个数1;接下来生成1,3,4……n这n-1个数的排列,然后再在每个排列前面加上第N个数2,依次类推,直到生成1,2,3……n-1这n-1个数的所有排列,并且在每个排列前面加上第N个数n,那么这所有的排列就构成了1,2,……n这个N个数所有的排列结果集了。

     这一过程可以描述为,设一集合变量p = {r1, r2, r3, ... ,rn}, 全排列为permute(p),定义一个集合变量pn,pn=p-{rn},即pn是集合p移除某个元素rn后的集合。那么r1permute(p1)就是集合[r2,r3,……rn]这n-1个数的排列的,然后每个排列之前在加上第N个元素r1得到的排列。

   归纳公式

    求p=【1,2,3……n】的全排列的算法,可以归纳得到计算公式为:permute(p)=1.permute(p1)+2.permute(p2)+3.permute(p3)……n.permute(pn),也就是直接对于序列1,2,3……n,依次将第i个元素和第一个元素进行交换,然后再求剩余n-1个元素的集合的全排列,最后加上第一个元素,就成了一轮排列;还原交换的数据,再去求下一轮排列。所以实现算法又称为置换算法,递归实现全排列算法如下:

/**
* 归纳法的排列Arrangement
* 1.2.3....n:生成所有排列的问题,以数组p[1-n]来存放每一个排列,假设可以生成n-1个数的所有排列。
* 那么在这些所有排列的前面加上第n个数就好了。
* 置换法:初始时时1-n的顺序排列的一种排列方式
* 因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。
* r1perm(p1)就是以r1开头的所有pn-r1的其他元素的全排列
为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。
* @author 金涛
*
*/
public class Permutation {
private static int count=0 ;
private static int n ;
private static int [] arrangementResult;

public static void arrange(){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字n:");
int n = scanner.nextInt();
Permutation.n = n;
scanner.close();
arrangementResult= new int[n+1];
for(int i =1;i<n+1;i++){
arrangementResult[i] = i;
}

permutation(1);
}

private static void permutation(int m) {
//由于一个数的全排列就是其本身,所以m=n时是一种全排列
if(m==n){
count++;
System.out.print("此次排列结果:");
printResult();
}else{
for(int j = m;j<=n;j++){
swap(j,m);
permutation(m+1);
swap(m,j);
}
}
}

private static void printResult(){
for(int i =1;i<=n;i++){
System.out.print(arrangementResult[i]+" ");
}
System.out.println();
}

private static void swap(int j,int m){
int temp_j = arrangementResult[j];
int temp_m = arrangementResult[m];
arrangementResult[j]=temp_m;
arrangementResult[m]=temp_j;
}

public static void main(String[] args) {
arrange();
System.out.println("总共有"+count+"种排列方式。");
}
}


      此处有两个交换操作,第一个交换是将第j个元素,与待求全排列的集合的第一个元素交换,然后再求取出首元素后剩余N-1个元素的全排列,这是递归算法的实现。完成一轮全排列的输出后,还需要将元素交换回去,保证初始原始的顺序,再求下一轮的全排序。

   算法分析

     由于存在n!种排列,所以算法permute(1)的第一步输出某个排列的操作,总共需要执行n*n!次,即输出所有的排列;第二步的for循环操作,permute(1)时,m=1,因此for循环执行n次,然后再加上递归调用permute(2)的执行次数,由此可归纳出该算法中for循环的反复次数f(n)的递推公式为:



     算法分析和设计中,这个递推公式在算法设计与分析中得出的总和f(n)=n!h(n)<2n!(求解挺复杂,待细细推论理解)。这说明整个算法的执行时间由两部分组成:输出部分n*n!+循环总次数<n*n!+2*n!。也就是(n+1)*n!,算法的时间复杂度由输出语句决定,即n*n!。算法使用的递归,当n过大时,还是很耗时间的。问答频道看到一个求全排列算法的问题,顺便重新学习下这个算法的实现。算法设计这门学问还是很深奥的,各种算法思想,时间和空间复杂度分析,考验我早已遗忘的数学知识啊!


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 排列算法