彩票的注数算法(Swift)

验证计算结果可以用这个排列组合计算器
计算注数前先要了解清楚什么是排列,什么是组合。推荐文章: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..

试运行结果

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(_ 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..

使用方法如下:

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..

使用方法:

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..

使用方法

binomialCoefficient(66, choose: 33)   // 打印一个大数

注数计算

完成上面的排列组合算法,接下来处理注数计算的问题。
从上面的算法中,我们可以获得两个算法:
排列算法

//排列数算法
func permutations(_ n: Int, _ r: Int) -> Int {
  var n = n
  var answer = n
  for _ in 1..

和组合数算法

// 组合数算法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..

在彩票游戏中,最经典的彩种是时时彩,通过计算时时彩的玩法简单介绍如何计算注数:
时时彩组选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。

你可能感兴趣的:(彩票的注数算法(Swift))