up:: 代数学

Swiftで代数学入門 〜 2. 群・環・体の定義 - Qiita

群の定義

集合 に演算 があり、以下を満たすとき (Group)という:

  • [結合性] 任意の に対し が成り立つ
  • [単位元] ある があり、任意の に対して
  • [逆元] 任意の に対して、その逆元 があり、

3

このドットは積と呼ばれるが、いわゆる掛け算である必要はない。また逆元も-1乗でなくその数の集合にいれば何でもいい。だから演算に対して群をなす、という言い方になる。演算が無いと群として成り立てない。

なのでとすれば通る。この二つが通るので整数は演算+について閉じており演算+ついて群をなすと言える。

有理数は0以外の数全体で×、÷について閉じている。
このことはが演算×に関して群をなすと表現する。にはとする。

先ほどの群の定義に、可換性を追加すると、それを満たす群は可換群と呼ばれるようになる。可換群は数として扱いやすい。

可換性は以下。

の演算 が以下を満たすとき 可換群(アーベル群) という:

  • [可換性] 任意の に対し、

整数は可換性があるが、整数から作る行列は可換ではない。

逆に、群の定義から逆元の要請を取り除くと、関数型言語で聞くモノイドというものになる。扱いにくいが広く扱える。

protocol Groupの定義

本題。swiftで上記の条件を記述。
swiftにはクラスや構造体に決まり事としてprotocolというものを準拠させられる。インターフェースを実装するみたいなの。staticにした要素はインスタンスではなく準拠させた型自体から呼ばれる。
swiftでは演算と単位元が既にどんな型にもあるのでstatic。

上から演算、単位元、逆元。まだ概念なのでこのままプロトコルを作るわけじゃないことに注意。

protocol Group {
    static func *(a: Self, b: Self) -> Self
    static var identity: Self {get}
    var inverse: Self {get}
}

まずこうなる。結合性の要請、及び他の要請も定義通りのものが返ってくる保証はない。そのためExtensionでテスト用メソッドを追加しておく。

プロトコルに準拠した型に関数のデフォルト実装や型などを追加できる、エクステンションという機能がある。これはプロトコルを拡張するように書く。

extension Group: Equatable {
    static func testAssociativity(a: Self, _ b: Self, _ c: Self) -> Bool {
        return (a * b) * c == a * (b * c)
    }
 
    static func testIdentity(a: Self) -> Bool {
        return (a * Self.identity == a) && (Self.identity * a == a)
    }
 
    static func testInverse(a: Self) -> Bool {
        return (a * a.inverse == Self.identity) && (a.inverse * a == Self.identity)
    }
}

ついでに==演算を使うためEquatableに準拠しておく。集合はそもそも集合の元を=比較できるという定義を含んでるものなので。

protocol AdditiveGroup の定義

さっきは+でも×でも同じく群をなすと言える話をした。
しかし実際は微妙に違う(特に逆元の定義が)ので、コードではそこを分けて実装する必要がある。

演算+を持つ群を加法群と呼ぶ。加法群と呼ぶときは演算の可換性を仮定することが多い。

protocol AdditiveGroup: Equatable {
    static func +(a: Self, b: Self) -> Self
    prefix static func -(a: Self) -> Self
    static var zero: Self { get }
}

演算と単位元。逆元は+-反転なのでーを定義すればいい。ついでにーの方の演算も定義しておくと便利。

func -<G: AdditiveGroup>(a: G, b: G) -> G {
    return (a + (-b))
}

なお、演算は+-両方Int型に入っているので、Intに追加するという形でプロトコルを作ることもできる。
真面目に作ると-演算に-使わない方法、同値関係で割る云々やらなきゃいけないので今回はこれ。

typealias Z = Int
 
extension Z: AdditiveGroup {
    static var zero: Z { return 0 }
}

とりあえずここまでで群は完成する。
加法群Zの完成。

環の定義

集合 に二つの演算 があり、以下を満たすとき (Ring) という:

  • に関して可換群である.
    • に関する単位元を ,
    • に関する の逆元を と書く.
  • に関してモノイドである.
    • に関する単位元を と書く.
  • [分配性] 任意の に対して以下が成り立つ:

300 + 12 1 = 42 1

加法群に可換性、積にモノイド、そして分配性を追加。
なおポイントはモノイドの部分。つまり積に関しては逆元の存在を仮定しないこと。

ちなみに、環の定義から「任意の に対して、 」であることが言える。
証明は+の単位元から0+0=0、そしてa・0=0から置き換え使ってa・0を作っていくだけ。

protocol Ring の定義

ではswift。
まずモノイドやって、それをさっき作ったAdditiveGroupと一緒に準拠させればいい。分配則はテストで。

protocol Monoid: Equatable {
    static func *(a: Self, b: Self) -> Self
    static var identity: Self {get}
}

AdditiveGroupは+、-(加法群での逆元)、zero(0)(加法群での単位元)。Monoidは*、Identity(1)(積での単位元)を要請しているのでこれで全部行ける。

protocol Ring: AdditiveGroup, Monoid { }

後でinverseも使うので、protocol Group: MonoidとしてGroupに残しておくといい。

さて、これもまたIntで実装する。Intは+、ー、×を持っているので実装書かなくていい。zeroとIdentityをデフォルト実装としてExtensionに書いて、あとはIntに準拠させてやる。

extension Ring {
    static var zero: Self {
        return Self.init(0)
    }
 
    static var identity: Self {
        return Self.init(1)
    }
}
typealias Z = Int
extension Z: Ring { }

整数環Zの完成。

体の定義

集合 が二つの演算 に関して を成し、 に関して を成すとき、 (Field) という。

43 1 + 0(1) 0 = 4(4) 1

環に×の逆元の定義を追加。ただし0は除く。
新たな定義はないのでprotocol FieldはGroupとRingに準拠するだけでいい。0以外云々はテストで。

protocol Field: Group, Ring { } 

ついでに二項演算/を定義しておく。-と同じ理由で便利。これは積での逆元に使う演算になる。

func /<F: Field>(a: F, b: F) -> F {
    return a * b.inverse
}

ではいよいよ最初の悲願、有理数体と実数体の間を見つけるため有理数体を……作る前に、実数体Rを作成。

typealias R = Double
 
extension R: Field {
    var inverse: R {
        return 1 / self
    }
}

Doubleは+、-、×、÷が入っているので、そこに×の逆元を実装しておく。
GroupはMonoid(演算*、/(積での逆元に使う演算)と1(積での単位元))に、RingはAdditive Group(演算+、-(加法群での逆元)と0(加法群での単位元かつ積での単位元))とMonoidにそれぞれ準拠しており、Groupに実装してあるのは演算、単位元、逆元のテスト、シグネチャのみ=宣言だけの関数はinverseだけなので、inverseに×の逆元を実装しても大丈夫。

次からいよいよ有理数体Qを作る。