2008年4月24日木曜日

同一直線上の3点

久野君たちの努力により, Beautiful Codeの翻訳がでた. 以前 三省堂の洋書の棚にあるのを見たことはあったが, その時はパスした.

翻訳をみると, なにしろ多くの人がそれぞれのプログラム言語で書いた自分のプログラムを(それもかなり大きい部分を)自讚しているから, 読むのが大変そうである.

短くて面白かったのは, 33章「『本』のためにプログラムを書く」であった. 要するに平面上の3点A, B, Cの座標が与えられた時, その3点が同一直線上にあるかを判定するプログラムを書くのだ.

私がやっても多分こういうアプローチになるであろうという風に話は展開していく.

まずA,Bの2点を通る直線の式を決め, 点Cがそれに乗っているかを問うもの. これは最初の2点がy軸と平行な線上にあるときの始末が面倒.

次はABを通る直線の勾配と, ACを通る直線の勾配を計算し, それらが一致するかを見るもの. 勾配が無限大になるときはnilを返すようにすると, nil同士もeqで比べればtになるので便利なようだが, 気持は悪い.

3番目はBCの距離a, CAの距離b, ABの距離cを計算し, a,b,cを大きさの順にソートし, 最大の距離が残りの2つの距離の和になるかどうかを見る. この難点はPythagorasの定理で距離を求めるのに関数sqrtを使うことだ. 誤差は避けられない.

このようにして, 最後に辿り着いたのは, 3点で構成する三角形の面積を求め, それが0なら三角形はつぶれて3点は同一直線上にあることが分かるというもの.

たしかに高校生のとき, 三角形 ABC の面積は行列式

|Ax Ay 1|
|Bx By 1|
|Cx Cy 1|

の1/2と習った. 面積が0かとうかを見るのだから, 1/2はいらない, 正負も問題にならないから, A,B,Cを反時計まわりにおくことにこだわることもない.

(Ax*By+Bx*Cy+Cx*Ay)-(Ax*Cy+Bx*Ay+Cx*By)

の計算だから誤差も何もない. これで決りだ.

今年の夏のプロシンのテーマは「プログラムの品格」のようなものだ. 本書もひとしきり話題になりそうな予感.

2008年4月14日月曜日

dancing links

前回のdancing linksの続きである.

TAOCP 4巻分冊0が4月18日に発売になるとアマゾンからメイルが来た. その分冊0は7章の最初の部分で, そこにGraeco-Latin squareの話題が登場する.

Graeco-Latin squareの作り方は, 互いにorthogonalな2つのlatin squareを用意し, それを合わせればよいのだが, あるlatin square Lからそれとorthogonalなlatin square Mの作り方として, Lからtransversalという組を探す. これは直接orthogonalなlatin squareを作るより容易であると本文に書いてある.

その続きは次のようだ.

Once the transversals are known, we're left with an exact cover problem, of 10 stages, which is much simpler than the original 90 stage problem (6). All we need to do is cover the square with ten transversals that don't intersect --- because every such set of ten is equivalent to a latin square M that is orthogonal to L.

10とか90とかの数値は, ここでは10x10のsquareを求めているからである.

しかしこれだけの説明では, よく分からなかったので, 考えた結果がこのblogの趣旨である.

大きいsquareは面倒なので, 4x4を対象とする.
例えば Lとして

L=((0 3 1 2)
(2 1 3 0)
(3 0 2 1)
(1 2 0 3))

は各行 各列に0,1,2,3が1回ずつ現れるから, latin squareになっている. これとorthogonalなlatin squareを求めるべく, Lのtransversalを探す. transversalはLの各行から1個 各列から1個 各文字から1個をとる組で,

A=((0 ) B=(( 3 ) C=(( 1 ) D=(( 2)
( 1 ) (2 ) ( 0) ( 3 )
( 2 ) ( 1) (3 ) ( 0 )
( 3)) ( 0 )) ( 2 )) (1 ))

E=((0 ) F=(( 1 ) G=(( 2) H=(( 3 )
( 3 ) (2 ) ( 1 ) ( 0)
( 1) ( 0 ) (3 ) ( 2 )
( 2 )) ( 3)) ( 0 )) (1 ))

の8個が存在する.

このうちA,B,C,Dを重ねるとちょうどLになり, またE,F,G,Hを重ねてもちょうどLになる.

定義が後回しになったが, LとMがorthogonalというのは, LとMから対応する位置の要素をとって組にすると, 同じ値の組が複数回は出来ないということである.

したがって各transversalの0,1,2,3の位置には0,0,0,0のように同じ値をおけば, orthogonalなlatin squareが得られる. つまり

A,B,C,Dから
M0=((0 1 2 3)
(1 0 3 2)
(2 3 0 1)
(3 2 1 0))

E,F,G,Hから
M1=((0 3 1 2)
(1 2 0 3)
(2 1 3 0)
(3 0 2 1))

ができる (LM)の組を作ると M0とM1のそれぞれから

((00 31 12 23) ((00 33 11 22)
(21 10 33 02) (21 12 30 03)
(32 03 20 11) (32 01 23 10)
(13 22 01 30)) (13 20 02 31))

が得られorthogonalなことが分かる.

Lからdancing linksでtransversalを得る方法, transversalを使わず, 直接orthogonalなlatin square(Mateという)を得る方法はTAOCP ex7-17参照のこと.

2008年4月9日水曜日

dancing links

KnuthのTAOCP, 7巻の分冊が出回り始めた. その最初の方にexact cover problemを解くのにdancing linksが適してると書いてある.

dancing linksについてはKnuthが楽しんで書いた論文があり, 講義のビデオも存在する. exact cover proble (日本語では「敷き詰め問題」か) は条件をならべた行列から解の候補を選び, それにより使えなくなった行や列を外し, またback trackするときは, 外した行や列をもとにもどす.

この行列はスパースなので, 上下左右に双方向リンクで実装すると, うまくいくというのがdancing linksの話の味噌である.

私としては面白そうなアルゴリズムを知ったら, さっそくコーディングして例題を走らせ, 体験によってアルゴリズムを理解, 記憶することが多い.

dancing linksについてもさっそくプログラムを書いた. googleで探すとjavaなんかで書いたプログラムも見つかるが, 自分で書くのが楽しいのでschemeで書くことにした.

Knuthの論文の最初の簡単な例題は, 直ぐにうまくいったが, 当然dancing linksを使えばうまく解けると書いてある他の例は, 結構てこずった.

8×8-2×2の60区画にペントミノのピースを置く問題である. ピースXを左上の(3,3)に置くというのに挑戦た. ぜんぜん解が出てこないのである. 当然プログラムがおかしいかといろいろ見てみたが, 合っているとしか思えない.

実は候補の列を選ぶのに, ブランチ数の少ないものを選ぶと書いてあったが, そうしなくても解が出るようにも読めたので, 残っている列の最左端から選んだのが失敗であった. あるとき思いついて, ブランチ数最小の列を選んでみたら, たちまち最初の解が出た. 超簡単な例では, 最左端でも問題なく解が得られたが, ペントミノ程度になると, ブランチ数を考慮する必要があった.

dancing linksの双方向リンクによる削除挿入は, 野下浩平君とその学生の一松宏君が8クイーン問題を解くのに考えたといわれている. しかし縦と横はすべてにクイーンを置くので, exact cover problemであるが, 斜め方向は半分くらいしか埋めないので, この辺の扱いは別になる. Knuthの例の論文をよく読むと, こういうのはgeneralized exact cover problemというので, 列にはprimaryとsecondaryを用意する. primaryの列名は双方向リンクで接続するが, secondaryの列名は自分自身で循環するリンクにすると書いてあった. そういう風にプログラムをなおすと, なるほどうまく行った.

数独にも適しているといわれるdancing linksである. 数独ではいくつかの解が与えられているので, 解を探し始める前に, 与えられた解について列や行を削除する(coverする)必要がある. 解に相当する行を探す手間が必要で, この辺はプログラムでかなり工夫が必要であった.

最後に, 上下左右に双方向リンクにしないでも, 実装できるのではないかとプログラムをしてみた. ノードはlispの対で, carに下へのリンク, cdrに右へのリンクを置き, 循環するようにリストを実装すると, たしかにこれでもうまく出来ることがわかった.

2008年3月23日日曜日

中山道

以前から少しずつ歩いていた中山道533Kmも遂に京都三条大橋へ辿り着いた. (詳しくいえば草津宿から大津宿を経て三条大橋までは旧東海道) 一緒に歩いてくれた多田君, 寺田君, 岩崎君に感謝したい. 実はまだ日本橋から本郷東大前を通り埼京線板橋駅まで8Kmが残っているが, これは車やバスで幾度も通り, 勝手知ったる道なので, もう済んだようなものでもある.

中山道は人気があり, 解説書やウェブページも多く, 歩こうと思えば問題は少ない. われわれは「ボクたちが歩く中山道」を手引きにした. また歩き出してから気づいたが, 道中処々に赤いシールが貼ってあり, 分岐点などで重要な道案内になっていた. 誰が貼ったのか知らないが有り難かった.

トラックがびゅんびゅん走る国道を歩くこともあったが, だいたいは静かな田舎道で, スローライフそのものである. それと引換えに山道では, 宿泊場所が左程ない; 食堂は国道を横切るときに探すしかない; という制約があることも判明した.

それにしても春秋のよい季節にのんびり歩けるのは最高であった. 昔の人は16日で歩き通したらしいから, 1日あたり33Km程度の速さである. これではのんびり歩く気分ではなかったを思われる. (中山道最大のイベント和宮東下でも25日) われわれは半日行程では10~15Km. 1日行程では20~25Kmであった.

昔と違うのは電車線と併走している場合, 草臥れると最寄駅から電車で宿泊地へ向かい, 翌日また電車で戻って歩き出せたことであった. 追分~下諏訪, 大井~太田のような山道区間を除けば行程はかなりフレキシブルであった.

自治体により中山道の周知は様々であった. 「歴史の道 中山道」の道標が立っているところもあれば, 全く無関心な町もあった. 中山道の保存にもっと尽力して欲しいものである. 中山道を歩く人がさらに増えるように.

2008年3月12日水曜日

3シリンダ機関車

前回書いた3シリンダ機関車の中央シリンダの制御シャフトの続きで, リンク機構はベクトルで考えると明瞭なことが分かった.

120度づつ離れたベクトル a, b, cがある. aに2 to 1 leverを接続すると, 相手の端は逆向きで大きさが半分のベクトル a'になる. これはちょうどbとcの中間なので, a'を中心にしたbの動きはちょうとcになるわけだ.

2008年3月8日土曜日

3シリンダ機関車

時間があったので鉄道博物館へいった. いつも何か発見があり楽しい.

国産の3シリンダ機関車C53の走り装置が置いてあった. 3シリンダ機関車は, 両側のシリンダは普通の蒸気機関車のとそっくりだが, 外から見えない台車の中央に3つめのシリンダーがある.

その中央のシリンダからはクランク軸になっている動輪に力を伝達する. 問題は中央の制御シャフトがどうなっているかであるが, 実に意外な構造であった. 両側の滑り弁を動かす制御シャフトが機関車の先頭の方へ突き抜け, それをリンクでつないで中央の制御シャフトを動かすのであった.

googleで探したら,
http://www.watercressline.co.uk/tw/pics/bitn2to1.jpg
にその図解があった.

こういう計算をしてみた. 一方の制御シャフトの動きをa, 他方のそれをb, 中央のそれをcとする.

a=sin x
b=sin (x+2π/3) = sin x cos 2π/3 + cos x sin 2π/3=-1/2 sinx + √3/2 cos x
c=sin (x+4π/3) = sin x cos 4π/3 + cos x sin 4π/3=-1/2 sinx - √3/2 cos x

これを眺めると a + b = -c とわかる.

たしかにベンツのマークのような, 120度ずつはなれたベクトルをa, b, cとすると, a+bはcと反対向きのベクトルになるから, 納得できる.

さて上の図解によると, 下のvalve spindle linkの動きをfulcrum pinを中心にして2 to 1 leverで上へ伝達する. その点を中心として, 上のvalve spindle linkの動きをequal leverで中央のvalve spindle linkへ伝えている.

従って, 下の動きをaとすると, 2 to 1 leverの上の動きは -1/2 aになる. 上の動きをb, 中央の動きを x とすると, bとxの平均が-1/2 aなのだから (x + b)/2 = -1/2 a. したがって x = - (a + b)となり, ちょうどcの動きとなっている.

すばらしい.

2008年2月22日金曜日

銀河

京都へ出張した帰路, 3月14日で運転をやめる寝台急行銀河を利用した. 以前銀河で下ったときの乗客は半分以下で, 車掌といつまで運転するのかなぁと話したりした. 運転打ち切りの直前ともあって今回は満員であった.

寝ながら聞く鉄橋を渡る響き, すれ違う列車の音, まれに停車する駅の物憂いアナウンス, 「おはようございます. 何月何日何曜何時何分です」という車掌の声で朝を知る. 独特の寝台列車の朝の雰囲気である.

子供のころから寝付かれない夜は今寝台列車で旅していると考えるとすぐ眠れた. 「銀河鉄道の夜」も何度も読んだ.

最近利用する寝台列車は函館からの帰りの北斗星ばかりだが, 利用回数の多いのは銀河であった.

新幹線を併走する路線で寝台列車を利用するのは, 物好きといわれても仕方ない. 能登は残っているし, これからは金沢へ向かう能登と函館から戻る北斗星をときどき利用するだけになりそうだ.

ついでに
英字新聞を読んだ人からアメリカでは洪水で何万という人が寝ていて流されたと聞いたがこのsleeperは寝ている人ではないらしい. sleeperは寝台車であると同時に線路の枕木のことでもある.