Swift 4.1 MIT LiCENSE build status

swift-combinatorics

Swift 中的组合数学

概要

import Combinatorics

for chars in Permutation(of:"swift") {
    print(String(chars))
}

描述

以下是随机访问迭代器,每个元素通过 [_:Int] 获得。会检查边界,如果超出范围,则会 fatalError()

let p = Permutation(0, 1, 2, 3)
p.count         // 24
p[0]            // [0, 1, 2, 3]
p[p.count - 1]  // [3, 2, 1, 0]

init 初始化器

它们都支持以下形式的初始化器。

Permutation(seed:[Element], size:Int=default)
Permutation(of:Sequence, size:Int=default)
Permutation(_ source:Element)

第一个是规范初始化器。size 指定迭代器返回的数组的大小,默认为 seed.count。第二个形式是为了方便,在这种情况下,任何 Sequence 都会被转换为 [Element]

排列 (Permutation)

返回一个迭代器,该迭代器返回排列后的数组。

var p = Permutation(of:"abcd")
p.count      // 24
p[p.count-1] // ["d","c","b","a"]
p.map { $0 } // [["a","b","c","d"]...["d","c","b","a"]]
p = Permutation(of:"abcd", size:2)
p.count      // 12
p[p.count-1] // ["d", "c"]
p.map { $0 } // [["a","b"] ... ["d","c"]]

组合 (Combination)

返回一个迭代器,该迭代器返回排列后的数组,但具有相同元素的数组被视为相同,而不管顺序如何。因此,您不应省略 size,否则您只会得到一个结果。

var c = Combination(of:"abcd")
c.count      // 1
c[c.count-1] // ["a","b","c","d"]
c.map { $0 } // [["a","b","c","d"]]
c = Combination(of:"abcd", size:2)
c.count      // 6
c[c.count-1] // ["c","d"]
c.map { $0 } // [["a","b"],["a","c"],["a","d"],["b","c"], ["b","d"], ["c","d"]]

BaseN

返回一个迭代器,该迭代器返回相应的“数字”。

var d = BaseN(of:0...3)
d.count      // 4 ** 4 == 256
d[d.count-1] // [3,3,3,3]
d.map { $0 } // [[0,0,0,0]...[3,3,3,3]]
d = BaseN(of:0...3, size:2)
d.count      // 16
d[d.count-1] // [3,3]
d.map { $0 } // [[0,0]...[3,3]]

幂集 (PowerSet)

返回一个迭代器,该迭代器返回每次迭代的幂集的元素。 size 固定为 seed.count,其中 seed 是源序列。

let s = PowerSet(of:0...3)
s.count // 2 ** 4 == 16
s.map { $0 } // [[],[0],[1],[0,1]...[0,1,2,3]]

CartesianProductProductSet

返回一个迭代器,该迭代器返回每次迭代的笛卡尔积的元素。

let suits = "♠️♦️❤️♣️"
let ranks =  1..13
let cp = CartesianProduct(suits, ranks)
cp.count // 52
cp.map { $0 } //[("♠️",1)...("♣️",13)]

与其他迭代器不同,CartesianProduct 接受两个 Collection,并以元组形式返回它们的笛卡尔积。它们的 .Element 的类型不必匹配。

迭代器本身也是一个集合,因此您可以通过连续应用乘数来构建多维笛卡尔积。

let cp = CartesianProduct("01", "abc")
let cpcp = CartesianProduct(cp, "ATCG")
cp.count // 24
cpcp.map{ $0 } // [(("0","a"),"A")...(("1","c"),"G")]

如您所见,CartesianProduct 返回一个元组。这在数学上是正确的,但更难使用。但是在 Swift 中,(T,T)(T,T,T) 是不同的类型,因此您无法编写一个返回不同长度的元组的函数。

为了缓解这种情况,Combinatorics 提供了 ProductSet。所有元素的类型必须相同,但您会得到一个数组而不是元组。

let ps = ProductSet([0,1],[2,4,6],[3,6,9,12],[4,8,12,16,20])
ps.count // 2 * 3 * 4 * 5 == 120
ps.map{ $0 } // [[0, 2, 3, 4] ... [1, 6, 12, 20]]

算术函数

此模块还附带以下算术函数,这些函数作为静态函数捆绑在 Combinatorics 中。

// T:SignedInteger
Combinatorics.factorial<T>(_ n:T)->T
Combinatorics.permutation<T>(_ n:T, _ k:T)->T
Combinatorics.combination<T>(_ n:T, _ k:T)->T

如您所见,它们被通用地定义,因此您不仅可以使用 Int,还可以使用 BigInt(如果可用)。

使用 Int 以外的索引

在底层,上面的迭代器定义如下

public typealias Permutation        = CombinatoricsIndex<Int>.Permutation
public typealias Combination        = CombinatoricsIndex<Int>.Combination
// …
public typealias ProductSet         = CombinatoricsIndex<Int>.ProductSet

为什么?因为 Int 通常对于组合数学来说足够大。幸运的是,Swift 允许您通用地定义 subscript,其索引不必是 Int。 请参阅 BigCombinatorics 以了解如何使用 BigInt 索引。

用法

构建

$ git clone https://github.com/dankogai/swift-combinatorics.git
$ cd swift-combinatorics # the following assumes your $PWD is here
$ swift build

REPL

简单地

$ scripts/run-repl.sh

或者

$ swift build && swift -I.build/debug -L.build/debug -lCombinatorics

在您的 repl 中,

  1> import Combinatorics
  2> Permutation(of:"swift").map{ String($0) }
$R0: [String] = 120 values {
  [0] = "swift"
  [1] = "switf"
  [2] = "swfit"
   // ...
  [119] = "tfiws"
}

Xcode

Xcode 项目被故意排除在存储库之外,因为它应该通过 swift package generate-xcodeproj 生成。 为方便起见,您可以

$ scripts/prep-xcode

然后 Workspace 会打开, Playground 在顶部。 该 Playground 是作为手册编写的。

iOS 和 Swift Playground

不幸的是,Swift Package Manager 不支持 iOS。 更糟糕的是,Swift Playgrounds 不支持模块。 但是不用担心。 此模块非常紧凑,您只需要复制 Combinatorics.swift

如果是 Swift Playgrounds,只需将其复制到 Sources 文件夹下即可。 如果您太懒,只需运行

$ scripts/ios-prep.sh

并且 iOS/Combinatorics.playground 就设置好了。 您不必在其中 import Combinatorics

从您使用 SwiftPM 管理的项目

将以下内容添加到 dependencies 部分

.package(
  url: "https://github.com/dankogai/swift-combinatorics.git", from: "0.0.1"
)

并将以下内容添加到 .target 参数

.target(
  name: "YourSwiftyPackage",
  dependencies: ["Combinatorics"])

现在您所要做的就是

import Combinatorics

在您的代码中。 尽情享受吧!

先决条件

Swift 4.1 或更高版本,OS X 或 Linux 用于构建。