[#44066] Ruby 1.8.6-p111 / 1.8.5-p114 released (Security Fix) — Urabe Shyouhei <shyouhei@...>

Hi all.

18 messages 2007/10/04
[#44067] Re: Ruby 1.8.6-p111 / 1.8.5-p114 released (Security Fix) — Takahiro Kambe <taca@...> 2007/10/04

こんにちは。

[#44068] Re: Ruby 1.8.6-p111 / 1.8.5-p114 released (Security Fix) — Shugo Maeda <shugo@...> 2007/10/04

前田です。

[#44090] Windowsアプリにrubyを組み込んだときのエラーメッセージ — "湊大典" <minato.daisuke@...>

こんにちは。

19 messages 2007/10/07
[#44091] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — Nobuyoshi Nakada <nobu@...> 2007/10/09

なかだです。

[#44097] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — "湊大典" <minato.daisuke@...> 2007/10/10

こんばんは、中田さん。

[#44098] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — "U.Nakamura" <usa@...> 2007/10/10

こんにちは、なかむら(う)です。

[#44103] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — "湊大典" <minato.daisuke@...> 2007/10/11

こんにちは、中村さん。

[#44104] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — Nobuyoshi Nakada <nobu@...> 2007/10/11

なかだです。

[#44105] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — "湊大典" <minato.daisuke@...> 2007/10/12

そうなんですか。

[#44106] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — "きむらこういち" <hogemuta@...> 2007/10/12

木村です。

[#44108] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — pegacorn <subscriber.jp@...> 2007/10/12

From: "きむらこういち" <hogemuta@gmail.com>

[#44109] Re: Windowsアプリにrubyを組み込んだときのエラーメッセージ — "Nobuyoshi Nakada" <nobu@...> 2007/10/13

なかだです。

[#44125] ruby-tk with tcltk8.5b1 — Ryutaro Amano <wn9r-amn@...>

天野竜太郎と申します。

14 messages 2007/10/19

[#44147] 2個づつの組を作る方法のすべて — "142QN4969@..." <ohrs@...>

小原と申します。御世話になっています。

15 messages 2007/10/27

[ruby-list:44160] Re: 2個づつの組を作る方法のすべて

From: m-hatake@...
Date: 2007-10-30 06:58:19 UTC
List: ruby-list #44160
畠山と申します。

いわゆる「組み合わせ最適化問題」だと思います。
たくさんある組み合わせの中から最適なものを選ぶ、と。

遺伝的アルゴリズム(GA)やヒューリスティック探索など、まあ有名な
アルゴリズムが多々あると思います。

そんなに厳密に最適でなくとも近似的な解でよければ、
結城浩さんが言われたような
1.とりえあず、ランダムに対戦の組み合わせをつくって
2.評価関数を使って良い対戦の組を選択する、
という手法が手っ取りばやく比較的楽にコーディングできる手法だと
思います。

そのランダムな組の中から比較的良いものを選んで、それらの
少し組み合わせを変えてまた良いものを選んで・・・と
繰り返すと、遺伝的アルゴリズムの出来上がりだと(大雑把ですが)
思います。(専門家の人からしたら怒られるような説明かもしれませんが・・・)

ちょうど似たような問題に取り組んでいて、枠組みだけもっていたので、
即席でつくってみました。専門の人から見たら改良の余地は多々あるで
しょうがまぁ参考までにどうぞ。

> ・好きな個数だけ対戦表を作り、評価関数がもっともよいものを選ぶ。

以下のコードで
Geneというクラスが一つの対戦組の情報をもっています。
全部で100個Geneインスタンスを作ってその中から「良い」組み合わせ
を選んでいます。
Geneクラスの中のcalc_fitnessメソッドの部分が評価関数の部分です。
その組み合わせが「良い」か「悪い」かを計算する部分です。
ここでは、
・対戦する人との間隔が短いほどマイナス評価
・対戦する人とのレベルが違うとその差にしたがってマイナス評価
してfitnessという変数に値を入れています。
fitnessの値が低いほど、良い組み合わせと考えて選択しています。

ただ、下の評価関数ですと「平均的に良い」組み合わせが
良いと評価されるので、だいたいの人は満足するのだけど、
一人だけめっちゃレベルの違う人と当たってしまう、といった
組み合わせも「良い」と評価されてしまう場合があります。
分散などのばらつき具合なども評価できるともっと良い
評価関数になると思います。


以下の例では、
・一日に一回だけ誰かと対戦
・10日分の対戦履歴
・20人の強さ情報
を仮定してやっています。

問題設定を間違って解釈していたらごめんなさい。

====
# PARAMETERS
NUM_AGENTS = 20
NUM_LEVEL  = 20
NUM_GENE_POOL = 100
MAX_GENERATION = 100
MUTATION_RATE = 0.1

# 10日分の対戦履歴(テスト用、ランダムに生成)
$match_history = []
10.times do 
	list = Array.new(NUM_AGENTS){|i| i}.sort_by{rand}
	pair = []
	pair << list.slice!(0,2) while list.length > 0
	$match_history << pair
end

# 表示
print "対戦履歴\n"
$match_history.each_with_index do |day,i|
	print "day ",i,": "
	day.each do |pair|
		print "[",pair[0],",",pair[1],"]"
	end
	print "\n"
end

# プレーヤーaとbの対戦がどのくらい前に行われたかを検索するメソッド
def search_match(match_history, a,b)
	count = match_history.length
	match_history.each_with_index do |daylist,i|
		daylist.each do |game|
			if (game[0] == a && game[1] == b) || (game[1] == a && game[0] == b)
				count = i
				break
			end
		end
	end
	return count
end

# プレーヤー情報(レベルをランダムに設定)
class Agent
	def initialize
		@level = (rand * NUM_LEVEL).truncate
	end
	attr_reader:level
end

# 対戦組み合わせの情報
class Gene
	def initialize(agents, match_history)
		@match_history = match_history
		@agents = agents
		# 適当にランダムな2つずつの組をつくる
		list = Array.new(agents.length){|i| i}.sort_by{rand}
		@agent_pair = []
		@agent_pair << list.slice!(0,2) while list.length > 0

		# 評価値を計算しておく
		@fitness = 0
		calc_fitness
	end
	attr_reader:agent_pair
	attr_reader:fitness

	def calc_fitness
		# 注意:対戦日数に関するコストとレベル差に関したコストにそれぞれ
		#    どのくらい重みをつけるかは自由

		@agent_pair.each do |ap|
			# 前回対戦した時からのゲーム数に応じてコストを加算
			period = search_match(@match_history, ap[0], ap[1])
			if period != nil
				@fitness += 1.1 ** (@match_history.length - period)
			end

			# レベル差に応じてコストを加算
			@fitness += 1.1**(@agents[ap[0]].level - @agents[ap[1]].level).abs

		end


	end

	def mutation
		a = (rand * @agent_pair.length).truncate
		b = (rand * @agent_pair.length).truncate
		pos = (rand * 2).truncate
		@agent_pair[a][pos],@agent_pair[b][pos] = @agent_pair[b][pos],@agent_pair[a][pos] 
	end

	def debug
		calc_fitness
		p @agent_pair
		p @fitness
		mutation
		p @agent_pair
	end
end



# GA
class Genetic_Argorithm

	def initialize
		@agents = Array.new(NUM_AGENTS).map!{Agent.new}
		@gene_pool = Array.new(NUM_GENE_POOL).map!{Gene.new(@agents,$match_history)}
	end
	attr_reader:agents

	def selection
		# fitnessの高い上位半分を選抜

		temp_gene_pool = @gene_pool.clone
		new_gene_pool = []
		(NUM_GENE_POOL/2).times do 
			top_fitness = temp_gene_pool[0].fitness
			top_no = 0
			temp_gene_pool.each_with_index do |g,i|
				if top_fitness > g.fitness
					top_fitness = g.fitness
					top_no = i
				end
			end
			new_gene_pool << temp_gene_pool[top_no]
			temp_gene_pool.delete_at(top_no)
		end
		@gene_pool = new_gene_pool
	end

	def crossover
		# パッとアルゴリズムが思いつかないので簡単なアルゴリズムで。
		# 上位10位からランダムに突然変異させたものを2.5割
		# あと2.5割はランダムに追加
		(NUM_GENE_POOL/4).times do
			pos = (rand * 10).truncate
			new_gene = @gene_pool[pos].clone
			new_gene.mutation
			@gene_pool << new_gene
		end
		(NUM_GENE_POOL/4).times do
			@gene_pool << Gene.new(@agents,$match_history)
		end
	end

	def mutationn
		# MUTATION_RATEでGeneの配列をランダムに入れ替える
		@gene_pool.each do |g|
			if rand < MUTATION_RATE
				g.mutation
			end
		end
	end

	def main
		# プレーヤー情報
		print "プレーヤー情報\n"
		@agents.each_with_index do |a,i|
			print "No.",i," level:",a.level,"\n"
		end
		print "\n"

		MAX_GENERATION.times do |i|
			print "generation ",i,"\n"
			print "top fitness ",@gene_pool[0].fitness,"\n"
			selection
			crossover
			mutationn
		end



		# 結果表示
		print "上位3位表示\n"
		3.times do |i|
			print "\n"
			print i," total fitness:",@gene_pool[i].fitness,"\n"
			print "No(level) vs. No(level)\t\tperiod\n"
			@gene_pool[0].agent_pair.each do |ap|
				print ap[0],"(",@agents[ap[0]].level,")\tvs.\t",ap[1],"(",@agents[ap[1]].level,")\t\t",search_match($match_history, ap[0], ap[1]),"\n"
			end
		end

	end
end


# Main

Genetic_Argorithm.new.main

====

実行結果例

対戦履歴
day 0: [13,0][7,17][16,6][3,14][11,8][2,12][10,1][4,9][19,15][5,18]
day 1: [18,2][19,17][6,15][9,14][10,11][3,4][12,13][8,1][16,5][7,0]
day 2: [2,7][4,18][11,5][0,3][14,15][9,12][16,19][13,6][10,17][8,1]
day 3: [0,8][15,6][18,10][1,13][14,11][17,16][3,2][5,9][4,12][19,7]
day 4: [17,9][19,2][1,5][4,15][12,6][16,18][11,7][14,3][8,10][0,13]
day 5: [1,11][0,8][10,3][7,12][13,5][14,2][9,4][15,17][19,16][18,6]
day 6: [18,2][4,0][16,9][10,1][8,15][19,13][5,11][3,6][17,14][7,12]
day 7: [1,7][3,0][11,12][8,6][18,9][10,5][4,2][16,19][13,14][15,17]
day 8: [5,14][11,3][16,6][4,19][9,13][17,2][0,7][10,12][15,8][18,1]
day 9: [9,8][2,14][16,13][15,7][17,11][10,3][5,1][0,4][18,6][19,12]
プレーヤー情報
No.0 level:14
No.1 level:0
No.2 level:6
No.3 level:17
No.4 level:3
No.5 level:7
No.6 level:2
No.7 level:19
No.8 level:13
No.9 level:18
No.10 level:5
No.11 level:11
No.12 level:9
No.13 level:10
No.14 level:12
No.15 level:19
No.16 level:14
No.17 level:5
No.18 level:5
No.19 level:16

generation 0
top fitness 36.9894771014285
generation 1
top fitness 28.0065476443931
generation 2
top fitness 28.0065476443931
(中略)
generation 98
top fitness 23.39581
generation 99
top fitness 23.39581

上位3位の組み合わせを表示

0 total fitness:23.39581
No(level) vs. No(level)		period
8(13)	vs.	4(3)		10
16(14)	vs.	14(12)		10
19(16)	vs.	12(9)		9
10(5)	vs.	15(19)		10
1(0)	vs.	5(7)		9
6(2)	vs.	17(5)		10
2(6)	vs.	9(18)		10
7(19)	vs.	3(17)		10
18(5)	vs.	13(10)		10
0(14)	vs.	11(11)		10

1 total fitness:23.39581
No(level) vs. No(level)		period
8(13)	vs.	4(3)		10
16(14)	vs.	14(12)		10
19(16)	vs.	12(9)		9
10(5)	vs.	15(19)		10
1(0)	vs.	5(7)		9
6(2)	vs.	17(5)		10
2(6)	vs.	9(18)		10
7(19)	vs.	3(17)		10
18(5)	vs.	13(10)		10
0(14)	vs.	11(11)		10

2 total fitness:23.39581
No(level) vs. No(level)		period
8(13)	vs.	4(3)		10
16(14)	vs.	14(12)		10
19(16)	vs.	12(9)		9
10(5)	vs.	15(19)		10
1(0)	vs.	5(7)		9
6(2)	vs.	17(5)		10
2(6)	vs.	9(18)		10
7(19)	vs.	3(17)		10
18(5)	vs.	13(10)		10
0(14)	vs.	11(11)		10


From: "142QN4969@plala.or.jp" <ohrs@lapis.plala.or.jp>
Subject: [ruby-list:44159] Re: 2個づつの組を作る方法のすべて
Date: Tue, 30 Oct 2007 12:48:02 +0900

> 小原です。返信ありがとうございます。
> 
> 申し訳ありません。具体的なイメージが浮かびません。
> 
> > ・この二つの要件を評価関数にする。
> >     ・この関数は会員同士の対戦履歴を持っていて
> >      「日にちが開いている」ことを評価できる。
> >      開いているほうがよい評価値を出す。
> >     ・この関数は会員の強さを持っていて、
> >      「差がない」ほうがよい評価値を出す。
> 
> どんな関数になるのでしょうか?
> 
> > ・会員をランダムにカップリングして、対戦表を作る。
> > ・好きな個数だけ対戦表を作り、評価関数がもっともよいものを選ぶ。
> 
> 「会員をランダムにカップリングする」とは、どういうことなのでしょうか?
> 定例会に出席している会員同志の、対戦表が欲しいのですが、、、。
> 対戦相手が決まれば、早速囲碁を打って楽しみます。
> 私は、なにかとんでもない勘違いしているのでしょうか?
> 
> 
> [44157]の私の説明も具体的ではないので、もう一度トライします。
> 
> システムでは、以下の情報をfileに保存しています。
> 
>  *members :会員情報file  
>    レコード形式 →  会員コード:氏名:段級位:rank-seq(+改行コード)
>            例         102:山川 草木          :1段:51
>  *taisen  :対戦履歴file  
>    レコード形式 →  日付+ラウンド:会員コード+会員コード:0(+改行コード)
>            例       0708041:102123:0
>  *presents:出席者file (これは定例会当日に作る。= 出席をとる)
>    レコード形式 →  日付:会員コード:会員コード:.....:会員コード(+改行コード)
>            例       070915:102:103:105:107.....:123
> 
>   (members、taisen のレコードは固定長です。)
> 
> 上記の file の 例を使って作業を進めます。
> 
> 作業の目的は、対戦配列 match=[] を完成することです。
> 
>  1。presents を読み 070915 の出席者の配列を作る。
>     mem0=["012","103",105",....,"123"]
>  2。members の情報と会わせて、各要素の頭にrank-seqを付けた配列を作る。
>     更に、(降順に)ソートする
>     mem1=["54112","54115","52108".....,"47121"]
>  3。mem1 の各要素の頭2文字を取ったものを要素とする配列を作る
>     mem2=["112","115","108",....,"121"]
>  4。x=mem2[0];a=mem2[1,10] とし、対戦履歴ファイルを参照して
>     x+a[0] 、x+a[1]、、、、x+a[9]の対戦日を調べ(注)、最も対戦日の古い者 y 
>    を x の 対戦相手とする。該当者が複数人いたときは、最初の者を y とする。
>    つまり   macth.push(x+y)
>       (注)x+a[0]の対戦日は、a[0]+xとして記録されているかもしれない。
>       #一日一回の対戦をする場合は、配列 a のなかには少なくとも一人、
>       #九日以上前に対戦した人がいるはずです。
>       #一日二回の対戦をする場合は四日以上、、、、。
>       #ただし、棋力の差は、最大で 10 になりますが、仕方ないと思います。
>       #会員構成、出席者により、色々な結果になると思われますが、
>       #私達の場合、約1月の試行では、納得してもらえる範囲と考えています。
>       #棋力差を小さくするには、配列 a の size を小さくする。
>  5。men2 より x,y を削除して 4。の操作を mem2.size=10 
>     となるまで繰り返す。
>  6。10人をカップリングする方法全てにわたり、最新の対戦日を調べ、
>     これが最も古いものを、best のカップリングとし、macth[]に加える。
> 
> 10人ではたいへんなので、4人でやります。
> 
>  6ー1。mem2=["116","117","118","119"] から
>        対戦の組を要素とする配列 mmmを作る。
>  6ー2。mmm=[["116117","118119"],
>             ["116118","117119"],
>             ["116119","117118"]]
>           =[ mm1,mm2,mm3] と略記します
> 
>  6ー3。mmm の各要素のペアの対戦日を対戦履歴fileで調べその
>        最新のものを、各要素の対戦日とし、それを各要素に push する
> 
>        117、116 の対戦日= 0705051、118、119 の対戦日= 0706051、なら
>        mm1.push(0706051) => mm1=["116117","118119","0706051"]    です。
> 
>        同様に mm2,mm3 に付いても調べて
> 
>       mmm=[["116117","118119","070651"],
>            ["116118","117119","0801232],
>            ["116119","117118","0707071]]
>  6ー4。mmm のうち、最も古い対戦日のくみを best として選ぶ。
>        best=["116117","118119","070651"]
>  6ー5。best の対戦日を削除したものを、match に加える。
>        macth=match+best
> 
> 
> 
>  以上 よろしく
> 
> 
> 

In This Thread