C# 计算排列组合数,及列出所有组合形式的算法

 /// <summary>
        /// 计算Int32类型的整数的阶乘,目前最大只能对20以内的正整数求阶乘
        /// </summary>
        /// <param name="n">Int32类型的正整数</param>
        /// <returns></returns>
        public static long Factor(this int n)
        {
            long result = -1;

            checked
            {
                try
                {
                    if (n < 0)
                    {
                        result = -1;
                    }
                    else
                    {
                        if (n == 0 || n == 1)
                        {
                            result = 1;
                        }
                        else
                        {
                            result = n * (n - 1).Factor();
                        }
                    }
                }
                catch (OverflowException)
                {
                    result = -1;
                }
            }
            return result;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

计算阶乘的递归算法,后续会用到,虽然用了long类型,但也只能计算到20的阶乘

        /// <summary>
        /// 计算从n个不同元素中任选m个元素的排列个数.n应该大于等于m!
        /// </summary>
        /// <param name="n">供排列选择的元素个数,正整数</param>
        /// <param name="m">排列选取的元素个数,正整数</param>
        /// <returns>排列个数</returns>
        public static long Permutation(int n,int m)
        {
            int[] N = new int[n];
            int[] SubM = new int[n-m];
            long result = 0;
            if (n < m)
            {
                result = 0;
            }
            else
            { //初始化数组N和M
                for (int i = 0; i < n; i++)
                {
                    N[i] = i + 1;
                    if (i < n - m)
                    {
                        SubM[i] = i + 1;
                    }
                }
                //消除两个数组中的重复元素
                for (int i = 0; i < n - m; i++)
                {
                    if (SubM[i] == N[i])
                    {
                        N[i] = 1;
                    }
                }
                //计算N中剩余元素的累乘
                result = 1;
                checked
                {
                    try
                    {
                        for (int i = 0; i < n; i++)
                        {
                            result *= N[i];
                        }
                    }
                    catch (OverflowException)
                    {
                        result = -1;
                    }

                }
            }
            return result;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

计算排列数,主要思想是类似于小学数学中求分式相乘中的消除法,主要是为了避免求阶乘的限制

        /// <summary>
        /// 计算从n个不同元素中选取m个元素的组合个数。n应该大于等于m
        /// </summary>
        /// <param name="n">供组合选择的元素个数,正整数</param>
        /// <param name="m">组合选取的元素个数,正整数</param>
        /// <returns>组合个数</returns>
        public static long Combination(int n, int m)
        {
            long factM = m.Factor();
            long result = 0;
            int[] N, M, subM;

            if (n < m)
            {
                result = 0;
            }
            else
            {
                if (factM > 0)
                {
                    result = Permutation(n, m) / factM;
                }
                else
                {
                    N = new int[n]; M = new int[m]; subM = new int[n - m];
                    //初始化三个数组
                    for (int i = 0; i < n; i++)
                    {
                        N[i] = i + 1;
                        if (i < m)
                        {
                            M[i] = i + 1;
                        }
                        if (i < n - m)
                        {
                            subM[i] = i + 1;
                        }
                    }
                    //消除重复元素,因为当m的阶乘溢出时才会进入此分支,所以只考虑和数组M进行消除
                    for(int i = 0; i < m; i++)
                    {
                        if (N[i] == M[i])
                        {
                            N[i] = 1;
                        }
                    }
                    //计算数组N和subM的累乘
                    long rN = 1, rSubM = 1;
                    for (int i = 0; i < n; i++)
                    {
                        rN *= N[i];
                        if (i < n - m)
                        {
                            rSubM *= subM[i];
                        }
                    }
                    //计算组合个数
                    result = rN / rSubM;
                }
            }

            return result;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

计算组合数,思路和求排列数一致,不再赘述

/// <summary>
        /// 获得从n个不同元素中任意选取m个元素的组合的所有组合形式的列表
        /// </summary>
        /// <param name="elements">供组合选择的元素</param>
        /// <param name="m">组合中选取的元素个数</param>
        /// <returns>返回一个包含列表的列表,包含的每一个列表就是每一种组合可能</returns>
        public static List<List<T>> GetCombinationList<T>(List<T> elements,int m)
        {
            List<List<T>> result = new List<List<T>>();//存放返回的列表
            List<List<T>> temp = null; //临时存放从下一级递归调用中返回的结果
            List<T> oneList = null; //存放每次选取的第一个元素构成的列表,当只需选取一个元素时,用来存放剩下的元素分别取其中一个构成的列表;
            T oneElment; //每次选取的元素
            List<T> source = new List<T>(elements); //将传递进来的元素列表拷贝出来进行处理,防止后续步骤修改原始列表,造成递归返回后原始列表被修改;
            int n = 0; //待处理的元素个数

            if (elements != null)
            {
                n = elements.Count;
            }
            if(n==m && m != 1)//n=m时只需将剩下的元素作为一个列表全部输出
            {
                result.Add(source);
                return result;
            }
            if (m == 1)  //只选取一个时,将列表中的元素依次列出
            {
                foreach(T el in source)
                {
                    oneList = new List<T>();
                    oneList.Add(el);
                    result.Add(oneList);
                    oneList = null;
                }
                return result;
            }

            for (int i = 0; i <= n - m; i++)
            {
                oneElment = source[0];
                source.RemoveAt(0);
                temp = GetCombinationList(source, m - 1);
                for (int j = 0; j < temp.Count; j++)
                {
                    oneList = new List<T>();
                    oneList.Add(oneElment);
                    oneList.AddRange(temp[j]);
                    result.Add(oneList);
                    oneList = null;
                }
            }


            return result;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

求所有可能的组合形式,一个递归实现,依次选择待组合元素中的前n-m个元素,每次选择后在剩下的元素中选取m-1个元素,直到m==1或者列表元素个数等于m则停止递归 

猜你喜欢

转载自blog.csdn.net/weixin_39706943/article/details/80665347