验证计算结果可以用这个排列组合计算器
计算注数前先要了解清楚什么是排列,什么是组合。推荐文章:5分钟彻底了解排列组合
排列组合计算公式简介
排列组合公式:排列组合公式大全
排列组合公式例子:排列组合算法,排列组合例题,排列组合计算
Factorial 阶乘
在公式中我们需要先计算阶乘,然后再计算排列组合算法,这里再阐述阶乘的公式:
n! // 表达式
5! = 5 * 4 * 3 * 2 * 1 = 120 //例子
很简单不是吗,n每次减1,再相乘。
根据公式我们可以写出阶乘的算法:
// 阶乘算法
func factorial(_ n: Int) -> Int {
var n = n
var result = 1
while n > 1 {
result *= n
n -= 1
}
return result
}
尝试打印下结果
factorial(5) // return 120
factorial(6) // return 720
factorial(7) // return 5040
factorial(20) // return 2432902008176640000 溢出
请注意:使用此函数不能计算超过20的阶乘数,否则会得到整数溢出的结果。
Permutations 排列
接下来我们来编写排列算法,回顾一下公式:
排列数计算公式
排列算法如下:
//排列算法
func permutations(_ n: Int, _ r: Int) -> Int{
return factorial(n) / factorial(n-r)
}
尝试计算结果:
permutations(5, 3) // return 60
permutations(6, 3) // return 120
permutations(7, 3) // return 210
在这个算法中有一个问题,由于计算时使用了阶乘算法,阶乘数过大时会导致计算结果整型数溢出,最终导致计算不准确,例如p(21,3)是计算不出结果的。可以通过swift在线运行测试
优化排列算法
实际上,计算排列数的可能性不需要按照计算全部的阶乘。
例如计算p(5,3)=60,仅需要计算5 * 4 * 3 = 60。这样可以大大加快计算速度。看算法:
//排列数计算
func permutations(_ n: Int, _ r: Int) -> Int {
var n = n
var answer = n
for _ in 1..<r { //注意r
n -= 1
answer *= n
}
return answer
}
试运行结果
permutations(5, 3) // returns 60
permutations(50, 6) // returns 11441304000
permutations(9, 4) // returns 3024
其中计算permutations(9, 4)
是这么计算的。
9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1
P(9, 4) = --------------------------------- = 9 * 8 * 7 * 6 = 3024
5 * 4 * 3 * 2
解释:计算结果时只需要计算遍历4次即可计算出结果,也就是9 * 8 * 7 * 6,次数正好来源于选择数的r值。每次让n递减,并相乘。
但事实上,算法仍然不能计算特别大的值,例如P(30,15),这个数值已经超出了swift 语言的最大整数,同样会导致整型数溢出。
打印排列数
前面已经介绍了如何计算排列数,那么我们如何打印出每个排列数呢?
来自Niklaus Wirth的递归算法:
func permuteWirth<T>(_ a: [T], _ n: Int) {
if n == 0 {
print(a) // display the current permutation
} else {
var a = a
permuteWirth(a, n - 1)
for i in 0..<n {
a.swapAt(i, n)
permuteWirth(a, n - 1)
a.swapAt(i, n)
}
}
}
使用方法如下:
let letters = ["a", "b", "c", "d", "e"]
permuteWirth(letters, letters.count - 1)
结果输出:
["a", "b", "c", "d", "e"]
["b", "a", "c", "d", "e"]
["c", "b", "a", "d", "e"]
["b", "c", "a", "d", "e"]
["a", "c", "b", "d", "e"]
...
这和我们之前看到的结果是一样的,会有120中不同的排列方式。
Combinationes 组合数
根据阶乘的算法写出组合的算法,回顾一下组合数计算公式:
组合数计算公式
// 组合数算法
func combinations(_ n: Int, choose r: Int) -> Int {
return permutations(n, r) / factorial(r)
}
let combinations = combinations(28, choose: 5) // prints 98280
这个方法同样受限于阶乘的限制,一旦阶乘超过20就无法结算准确数值了。
有一种更快的计算方式可以避免这个情况,这种方法仍然来源于C(n,r)公式:
n! n * (n - 1) * ... * 1
C(n, r) = ------------- = ------------------------------------------
(n - r)! * r! (n - r) * (n - r - 1) * ... * 1 * r!
通过变形可以得到以下计算方式:
n * (n - 1) * ... * (n - r + 1) (n - 0) * (n - 1) * ... * (n - r + 1)
C(n, r) = --------------------------------------- = -----------------------------------------
r! (0 + 1) * (1 + 1) * ... * (r - 1 + 1)
写出算法如下:
func quickBinomialCoefficient(_ n: Int, choose r: Int) -> Int {
var result = 1
for i in 0..<r {
result *= (n - i)
result /= (i + 1)
}
return result
}
使用方法:
quickBinomialCoefficient(8, choose: 2) // prints 28
quickBinomialCoefficient(30, choose: 15) // prints 155117520
除了以上计算组合数的方案之外,还有另外一种速度更快的方案,二项式系数算法:
这种新方法速度非常快,但仍有限制数量的大小。毫无问题C(30,15)计算是没问题的,但像C(66,33)这个大小仍然会导致分子中的整数溢出。这是一种使用动态编程来克服计算阶乘和分割的算法。它基于Pascal的三角形:
0: 1
1: 1 1
2: 1 2 1
3: 1 3 3 1
4: 1 4 6 4 1
5: 1 5 10 10 5 1
6: 1 6 15 20 15 6 1
下一行中的每个数字都是通过添加前一行中的两个数字来组成的。例如,在行6中,通过从行5添加5和10来产生数字15.这些数字被称为二项式系数,并且当它们发生时它们与C(n,k)相同。
// 组合数算法4 二项式系数
func binomialCoefficient(_ n: Int, choose r: Int) -> Int {
var bc = Array(repeating: Array(repeating: 0, count: n + 1), count: n + 1)
for i in 0...n {
bc[i][0] = 1
bc[i][i] = 1
}
if n > 0 {
for i in 1...n {
for j in 1..<i {
bc[i][j] = bc[i - 1][j - 1] + bc[i - 1][j]
}
}
}
return bc[n][r]
}
使用方法
binomialCoefficient(66, choose: 33) // 打印一个大数
注数计算
完成上面的排列组合算法,接下来处理注数计算的问题。
从上面的算法中,我们可以获得两个算法:
排列算法:
//排列数算法
func permutations(_ n: Int, _ r: Int) -> Int {
var n = n
var answer = n
for _ in 1..<r { //注意r
n -= 1
answer *= n
}
return answer
}
和组合数算法
// 组合数算法4 二项式系数
func binomialCoefficient(_ n: Int, choose r: Int) -> Int {
var bc = Array(repeating: Array(repeating: 0, count: n + 1), count: n + 1)
for i in 0...n {
bc[i][0] = 1
bc[i][i] = 1
}
if n > 0 {
for i in 1...n {
for j in 1..<i {
bc[i][j] = bc[i - 1][j - 1] + bc[i - 1][j]
}
}
}
return bc[n][r]
}
在彩票游戏中,最经典的彩种是时时彩,通过计算时时彩的玩法简单介绍如何计算注数:
时时彩组选120:号码一共10个数,选中5个,在5个中取1个进行组合,一共有多少组合?
通过组合算法得到结果:
let c = binomialCoefficient(5, choose: 5) // prints 1
let c = binomialCoefficient(10, choose: 5) // prints 252
五星直选复式
//五星直选复式
let an = permutations(5, 1)
let an1 = permutations(2, 1)
let an2 = permutations(1, 1)
let an3 = permutations(1, 1)
let an4 = permutations(1, 1)
let ans = an * an1 * an2 * an3 * an4
简单介绍两种玩法,查看更多玩法
喜欢的话给个Star。
网友评论