クラス Group#initialize の set は定義された集合(配列)、op は定義された演算(Proc オブジェクト)。@ie は単位元、@order は位数、インスタンスメソッド inverse(a) は a の逆元を与えます。(結合法則の確認はひどく時間がかかる場合があるので、引数 cck = false でスキップを選択できます。)
group.rb
class Group def initialize(set, op, cck = true) @set = set @op = op @order = @set.size #位数 raise "Array クラスを使ってください" if @set.class != Array raise "演算の引数の数は2でなければなりません" unless @op.arity == 2 @set.repeated_permutation(2) do |ar| raise "群が閉じていません" unless @set.include?(@op[ar[0], ar[1]]) end if cck #結合法則のチェックをするか @set.repeated_permutation(3) do |ar| unless @op[@op[ar[0], ar[1]], ar[2]] == @op[ar[0], @op[ar[1], ar[2]]] raise "結合法則が成立していません" end end end ar = @set.select {|a| ie?(a)} raise "単位元が存在しません" unless ar.size == 1 @ie = ar[0] @set.each do |a| raise "逆元が存在しません" unless @set.include?(inverse(a)) end end attr_reader :ie, :order def inverse(a) @set.each do |x| return x if @op[a, x] == @ie and @op[x, a] == @ie end raise "逆元が存在しません" end def ie?(a) @set.each do |x| return false unless x == @op[a, x] and x == @op[x, a] end true end end
3次の対称群(参照)を実装してみました。
perm = lambda do |num1, num2| n1 = num1.chars.map(&:to_i) n2 = num2.chars.map(&:to_i) n1.map {|i| n2[i]}.join end ar = ["012", "021", "102", "120", "201", "210"] g = Group.new(ar, perm) p g.ie #=>"012" 単位元 p g.inverse("120") #=>"201" 逆元 p ar.each_with_object([]) {|x, a| a << "#{x}→#{g.inverse(x)}"} #["012→012", "021→021", "102→102", "120→201", "201→120", "210→210"] すべての逆元
4次の対称群もまったく同様です。
ar = [0, 1, 2, 3].permutation(4).map(&:join) g = Group.new(ar, perm) p g.ie #=>"012" 単位元 p g.order #=>24 位数 p g.inverse("1203") #=>"201" 逆元 p ar.each_with_object([]) {|x, a| a << "#{x}→#{g.inverse(x)}"} #["0123→0123", "0132→0132", "0213→0213", "0231→0312", "0312→0231", # "0321→0321", "1023→1023", "1032→1032", "1203→2013", "1230→3012", # "1302→2031", "1320→3021", "2013→1203", "2031→1302", "2103→2103", # "2130→3102", "2301→2301", "2310→3201", "3012→1230", "3021→1320", # "3102→2130", "3120→3120", "3201→2310", "3210→3210"] #すべての逆元
クラインの4元群。
ar = %w(e a b c) op = lambda do |x, y| if x == "e" then y elsif y == "e" then x elsif (x == "a" and y == "b") or (x == "b" and y == "a") then "c" elsif (x == "b" and y == "c") or (x == "c" and y == "b") then "a" elsif (x == "c" and y == "a") or (x == "a" and y == "c") then "b" else "e" end end g = Group.new(ar, op) p g.ie #=>"e" 単位元 p ar.each_with_object([]) {|x, a| a << "#{x}→#{g.inverse(x)}"} #=>["e→e", "a→a", "b→b", "c→c"] すべての逆元 st = "" ar.repeated_permutation(2) do |a| st += "(#{a[0]}, #{a[1]})→#{op[a[0], a[1]]} " end p st #"(e, e)→e (e, a)→a (e, b)→b (e, c)→c (a, e)→a (a, a)→e (a, b)→c (a, c)→b # (b, e)→b (b, a)→c (b, b)→e (b, c)→a (c, e)→c (c, a)→b (c, b)→a (c, c)→e "
いい加減に群を定義するとエラーが出る。これは演算については閉じているが、単位元がない。
その他、色々と集合と演算を定義して、群になるか遊んでみて下さい。
ar = %w(a b) op = lambda {|x, y| x} g = Group.new(ar, op) #=>**.rb:**:in `initialize': 単位元が存在しません (RuntimeError)
※注記
2017/6/30 に全面的に書き直しました。