数学问题(2) — 排列问题的程序实现

上一篇博文   数学问题(1) — 组合问题的程序实现 中讲解的组合问题的算法实现。问题的背景差不多,废话不多说,接着来讲排列问题的实现。排列的算法叫:“全排列——邻位对换法”

1、根据上一篇博文   数学问题(1) — 组合问题的程序实现 先得到组合情况,再遍历每一种组合的排列。一个[1,2…n]的一个排列,其上每一个整数都给了一个方向, 如果它的箭头所指的方向的邻点小于它本身,我们称整数k是可移的。
 2、显然1永远不可移,n除了以下两种情形外,它都是可移的: (1) n是第一个数,且其方向指向左侧 (2) n是最后一个数,且其方向指向右侧
3、我们可按如下算法产生所有排列: 1,开始时:存在排列123…n,除1外,所有元素均可移,即方向都指向左侧。
4、当最大元素n可移动时,将其从一端依次移动到另一端,即可得到n-1个全排列;
 5、当n移动到某一端后,不能再移动,此时寻找最大的可移数m,将m与其箭头所指的邻数互换位置,这样就又得到一个新的全排列;
 6、将所得新排列中所有比m大的数p的方向调整,即改为相反方向,这样使得n又成了可移数。 3,重复第2步直到所有的元素都不能移动为止。

举例:数组 ["红", "绿", "蓝","黄"]

------4中选3的排列的种类总数有:24---------
 红 绿 蓝
 红 蓝 绿
 蓝 红 绿
 蓝 绿 红
 绿 蓝 红
 绿 红 蓝
 红 绿 黄
 红 黄 绿
 黄 红 绿
 黄 绿 红
 绿 黄 红
 绿 红 黄
 红 蓝 黄
 红 黄 蓝
 黄 红 蓝
 黄 蓝 红
 蓝 黄 红
 蓝 红 黄
 绿 蓝 黄
 绿 黄 蓝
 黄 绿 蓝
 黄 蓝 绿
 蓝 黄 绿
 蓝 绿 黄
 

代码:

import java.util.ArrayList;
import java.util.List;

/**
 * 排列算法
 *
 * @author zkd
 * @Date 2014/5/11
 */
public class Permutation {

    //打印n取m的排列所有情况
    public void diaplayPermutation(String[] object, int n, int m) {
        System.out.println("------" + n + "中选" + m + "的排列的种类总数有:"+countOfPermutation(n,m)+"---------");
        List<String[]> list = PermutationObject(object, n, m);
        for (int i = 0; i < list.size(); i++) {
            String[] sa = list.get(i);
            String s = "";
            for (int j = 0; j < sa.length; j++) {
                s += (" " + sa[j]);
            }
            System.out.println(s);
        }

    }

    //对一个组合情况进行全排列 返回总排列种数
    private List<String[]> PermutationObject(String[] object, int n, int m) {
        List<String[]> result = new ArrayList<>();//存储总的排列结果
        List<String[]> cob = generateCombinationObject(object, n, m);//取得组合情况
        List<int[]> pai = fullPermutationSequence(m);//取得全排列序列
        //对每个组合用全排列序列进行排列
        for (int i = 0; i < cob.size(); i++) {
            String[] str = cob.get(i);
            for (int j = 0; j < pai.size(); j++) {
                int[] lie = pai.get(j);
                String[] s = new String[m];
                int t = 0;
                for (int k = 0; k < lie.length; k++,t++) {
                    s[t] = str[lie[k]-1];
                }
                result.add(s);
            }
        }
        return result;
    }

   //返回全排列序列
    public List<int[]> fullPermutationSequence(int n) {
        List<int[]> list = new ArrayList<>();
        int[] a = new int[n];
        boolean[] p = new boolean[n];
        for (int i = 0; i < n; i++) {
            a[i] = i + 1;
            p[i] = false;
        }

        do {
            list.add(a.clone());
            if (n == a[n - 1]) {
                for (int i = n - 1; i > 0; i--) {
                    int temp = a[i];
                    a[i] = a[i - 1];
                    a[i - 1] = temp;
                    boolean f = p[i];
                    p[i] = p[i - 1];
                    p[i - 1] = f;
                    list.add(a.clone());
                }
            } else {
                for (int i = 1; i < n; i++) {
                    int temp = a[i];
                    a[i] = a[i - 1];
                    a[i - 1] = temp;
                    boolean f = p[i];
                    p[i] = p[i - 1];
                    p[i - 1] = f;
                    list.add(a.clone());
                }
            }
        } while (canMove(a, p, n));
        return list;
    }

    //换位法判断能否移位 此过程也有可能产生一种排列,判断后要存储
    private boolean canMove(int[] a, boolean[] p, int n) {
        int max = 1;
        int pos = -1;
        for (int i = 0; i < n; i++) {
            if (a[i] < max) {
                continue;
            }
            if ((p[i] && (i < n - 1) && (a[i] > a[i + 1])) || // 指向右侧
                    (!p[i] && (i > 0) && (a[i] > a[i - 1]))) // 指向左侧
            {
                max = a[i];
                pos = i;
            }
        }
        if (pos == -1) // 都不能移动
            return false;

        // 与其箭头所指的邻数互换位置
        if (p[pos]) // 指向右侧
        {
            int temp = a[pos];
            a[pos] = a[pos + 1];
            a[pos + 1] = temp;
            boolean flag = p[pos];
            p[pos] = p[pos + 1];
            p[pos + 1] = flag;
        } else // 指向左侧
        {
            int temp = a[pos];
            a[pos] = a[pos - 1];
            a[pos - 1] = temp;
            boolean flag = p[pos];
            p[pos] = p[pos - 1];
            p[pos - 1] = flag;
        }
        // 将所得排列中所有比max大的数p的方向调整
        for (int i = 0; i < n; i++) {
            if (a[i] > max)
                p[i] = !p[i];
        }
        return true;
    }

    //根据组合序列返回组合结果集
    private List<String[]> generateCombinationObject(String[] object, int n,
            int m) {
        List<int[]> list = generateCombination(n, m);
        List<String[]> result = new ArrayList<>();
        int j, k;
        int[] a;
        for (int i = 0; i < list.size(); i++) {
            a = list.get(i);
            String[] s = new String[m];
            k = 0;
            for (j = 0; j < a.length; j++) {
                if (1 == a[j]) {
                    s[k] = object[j];
                    if (k < m - 1) {
                        k++;
                    }
                }
            }
            result.add(s);
        }
        return result;
    }

   //返回n选m的组合情况序列
    private List<int[]> generateCombination(int n, int m) {
        List<int[]> list = new ArrayList<>();
        int[] tempNum = new int[n];
        boolean flag; // 判断算法的结束
        // 初始化数组,同时得到第一组组合系列
        for (int i = 0; i < n; i++) {
            if (i < m) {
                tempNum[i] = 1;
            } else {
                tempNum[i] = 0;
            }
        }
        int[] b = tempNum.clone();
        list.add(b);
        if (n == m) {
            return list;
        }

        do {
            int pose = 0; // 记录改变的位置
            int sum = 0; // 记录改变位置 左侧 1 的个数
            // 然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为“01”
            for (int i = 0; i < (n - 1); i++) {
                if (tempNum[i] == 1 && tempNum[i + 1] == 0) {
                    tempNum[i] = 0;
                    tempNum[i + 1] = 1;
                    pose = i;
                    break;
                }
            }
            // 同时将其左边的所有“1”全部移动到数组的最左端。
            for (int i = 0; i < pose; i++) {
                if (tempNum[i] == 1)
                    sum++;
            }
            for (int i = 0; i < pose; i++) {
                if (i < sum)
                    tempNum[i] = 1;
                else
                    tempNum[i] = 0;
            }

            // 判断是否为最后一个组合:当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。
            flag = false;
            for (int i = n - m; i < n; i++) {
                if (tempNum[i] == 0)
                    flag = true;
            }
            int[] a = tempNum.clone();
            list.add(a);
        } while (flag);

        return list;
    }


    public int countOfPermutation(int n,int m){
        int sum=1;
        for(int i=n;i>=(n-m+1);i--){
            sum*=i;
        }
        return sum;
    }

    public static void main(String[] args) {
        Permutation p = new Permutation();
        String[] object2 = { "红", "绿", "蓝","黄" };
        p.diaplayPermutation(object2, 4, 3);

    }


}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值