現有一數字,例如12345,問這個數字有多少種排列可能,最簡單的就是位數的階乘,5位數字等於5*4*3*2*1=120,這是理論上沒有重復數字的情況下,如果現在是11234,11123,11112,11223有重復數字組成的數字怎么計算呢?
若一個數字由所有不相同的數字組成,則該數字的排列可能是該數組位數的階乘,若該數字中存在重復的數字,例如,有m個1相同,結果就是n! / m!,n值是數字的位數,如果還存在p個相同的數字,那結果就是n! / m! /p!
根據上面的算法,上面的數字計算如下:
11234=5! / 2!=60
11123=5! / 3!=20
11112=5! / 4! = 4
11223=5! / 2! / 2! = 30
附上C#實現
public class MathHelper { /// <summary> /// 輸入一個數字,算出該數值有多少種排列可能. /// </summary> /// <param name="num">The num.</param> /// <returns></returns> public static long CountPermutation(long num) { string number = num.ToString(); IDictionary<char, int> rptNum = new Dictionary<char, int>(); foreach (char ch in number) { if (rptNum.Keys.Where(it => it == ch).Count() > 0) rptNum[ch] += 1; else rptNum.Add(ch, 1); } long totalFac = Factorial(number.Length); foreach (var item in rptNum.Where(it => it.Value > 1)) { totalFac /= Factorial(item.Value); } return totalFac; } /// <summary> /// 計算階乘. /// </summary> /// <param name="num">The num.</param> /// <returns></returns> public static long Factorial(int num) { if (num == 1) return num; return num * Factorial(--num); } }
輸出數字的全排列
上面計算出了一個數字有多少種排列可能,下面就要分別列出這些排列結果!網上已經有了很多的算法,最基礎的還是迭代循環,但數字一大效率是一個問題,另外一點就是無法去除重復數字,例如計算1123,1122這樣數字的全排列結果的時候,本人數學不太好,索性,在博客園找到一個同學的實現:
一組數字的全排列按序輸出
再議“生成全排列算法”
通過第一個同學的思路和實現,完成了一個C#版本,對於低數位的數字來說效率還是挺不錯的,性能方面的測試我就先偷個懶,目前先應用上就行,畢竟目前這個實現已經滿足我的需求了,呵呵!
C#實現
/// <summary> /// 全排列算法類 /// </summary> public class FullArrangementHelper { private int _length; public IList<long> Result { get; private set; } public FullArrangementHelper() { Result = new List<long>(); } public IList<long> GetFullArrangement(long num) { string numStr = num.ToString(); int[] nums = new int[numStr.Length]; for (int i = 0; i < numStr.Length; i++) nums[i] = int.Parse(numStr[i].ToString()); _length = nums.Length; nums = nums.OrderBy(it => it).ToArray<int>();//排序 FullArrangement(nums, 0); return Result; } /// <summary> /// 計算全排列算法 /// </summary> /// <param name="nums">要計算全排列的數字.</param> /// <param name="pos">開始計算排列的位置,例如,現在的數字是1234,如果pos為0,就代表計算這四位的全排列,1的下標為0, /// 如果為1,則計算后3位的全排列,依次下推.</param> private void FullArrangement(int[] nums, int pos) { //將現在的數字添加到結果中 Result.Add(ConvertToNum(nums)); //最大是數字的長度-2是因為按照下標計算,此處pos-1是因為后續的步驟中需要+1來對相鄰的兩個數字做比較 for (int i = _length - 2; i > pos - 1; i--) NextArrangement(nums, i); } /// <summary> /// 計算下一輪全排列 /// </summary> /// <param name="nums">數字的分解數組.</param> /// <param name="pos">開始計算排列的位置.</param> private void NextArrangement(int[] nums, int pos) { int[] cop = new int[_length]; //根據下標依次計算數字的全排列,實際就是大排列都由小排列一步步擴大 for (int i = pos + 1; i < _length; i++) if (nums[i] > nums[pos] && nums[i] != nums[i - 1]) { for (int t = 0; t < _length; t++) cop[t] = nums[t]; //交換數字位 for (int j = i; j > pos; j--) { int temp = cop[j]; cop[j] = cop[j - 1]; cop[j - 1] = temp; } FullArrangement(cop, pos + 1); } } private long ConvertToNum(int[] nums) { StringBuilder sb = new StringBuilder(); foreach (var n in nums) sb.Append(n); return long.Parse(sb.ToString()); } }