文章を書くことについて

最近,なんだか精神衛生上,あまり良くないことが多くて悩みが尽きない.悩んだところで何かが解決するわけではないことはよくよくわかっているし,考えてもしょうがないのだろうが,感情的な問題は頭でわかっていてもどうしようもないところがある.おそらく感情的な問題は,無意識的な問題があるので,それこそ頭に刷り込むことでしか改善しない気がするのだが,それは長期戦になるため,なかなか厳しいものがあるなと感じている.

さて,今回は文章を書くことについて書いてみたい.というのも,周りから文章を書くのが早すぎじゃないのかと指摘されたため,そのことについて反省してみようと思ったからだ.

文章を書くときは考えない

前に見た記事で,ものすごく効率の良いプログラマは一日のうちに3000から10000行ものプログラムを書き上げるというのを見た.これは別にアセンブラのような一行一行があまり意味を持たない言語ではなく,c言語を使った実装でそれだけ高速に書き上げることができるらしいのだ.一日の作業時間がたとえ18時間程度だったとしても,3000行もの量を書くためには,一分間あたりで3000/(18*60)=2.77…行を生み出さなければならないことになる.プログラムを書くときは,書いている間に発覚する問題に対処する時間や,テストを書きデバッグを行う時間,単に変数名を決めるために使われる時間まで,様々なところに実際に”書く”こと以外にかかる時間が存在する.したがって,プログラムを書いている間に,そのような思考をする時間を含めたら,分辺りに2から3行程度の速度というのは,途端に達成困難になってしまうと思われる.

ではそのような超人的な速度で書き上げる人々というのは,一体どういう理屈でそのような作業速度を会得したのだろうか.考えられる一つの答えは,プログラム作成にかかるほとんどすべての作業を,無意識下で処理できるレベルまで鍛錬を積んだのではないかということだ.私達は日常生活でたくさんの複雑なことをやり遂げている.例えば,多くの人にとって,自転車に乗ることはたやすいことだろうが,実際にはそれほど簡単なことではない.自転車に乗るためには,バランスを取り,如何にペダルを踏んで前に進むのかなど,いくつかのタスクを同時並行的に達成しなければならない.ロボットを使って同じような制御を行うことを考えれば,それがどれほど難しいタスクであるかが理解できるのではないだろうか.だが実際には,自転車に乗ることができない人は殆どいない.それどころか,自転車に乗りながらものを食べたり,携帯をいじることすら簡単に行うことができる.このようなことができるのは,おそらく私達の脳内で,無意識が意識の肩代わりをして,処理の自動的な遂行を手助けしてくれていることが要因ではないかと考えられる.

私達が,初めて自転車に乗ったときは,おそらく意識的にそれを遂行しようとしたはずである.それはバランスを如何にとれば倒れずに済むのかや,ハンドルをどの程度傾けたら横に曲がれるのかなどである.しかし,そのようなことも慣れることで,すなわち,意識から無意識的な処理に移行することで,私達は自由自在に自転車を乗りこなせるようになるわけである.

このような理屈は,あくまで自転車のような運動には当てはまるかもしれないが,プログラムを作成することや文章を書くことなどの知的な作業では違うのではないかと思う人もいるかも知れない.しかし,私の意見はそうではない.例えば,私達が普段行う日常会話は,それほど頭を働かせずに行っている.ただ,これを外国語で同様に行おうと思ったら途端に前途多難になるはずである.これは日常会話が本質的に非常に難しい問題であり,私達はそれを経験により無意識的に行えるようになったに過ぎないということだ.現に,日常会話で使用している語彙はそれほど多くなく,基本的には同じような表現の使い回しで会話を行っているのではないだろうか.

文章を書く際には,文法的にただしく,文脈上の自然さを保ちながらわかりやすく書く必要がある.それらをすべて意識的に行おうと思えば,非常に時間がかかる上に,辛く厳しいものになると考えられる.しかし,日々の鍛錬により,文章を書くことであっても,無意識的に読み手に優しい文章をかけるようになるのではないかと思う.

内容についてどう思いますか?
  • いいね (0)
  • だめ (0)

tmux(byobu)でDISPLAY環境変数をリセット(re-set)する方法

私の調べ方が甘いのか日本語でこの手のことに関する記事が見当たらないのでメモ.
ssh先のマシンでtmuxのセッションを立ち上げたあとに,デタッチしてsshを切ったとする.このあともう一度つなぐとDISPLAY環境変数が変化してしまい,アタッチしたあとにX forwardingが動かないことがある.

これに対する解決策がこのページに書いてあった.
https://goosebearingbashshell.github.io/2017/12/07/reset-display-variable-in-tmux.html

このページに従うと,そもそも新しいtmuxでは,今の環境変数の状態を確認することができるらしい.

この中のDISPLAYが今の最も新しい(今のsshのセッション)の設定内容であるらしいのだ.ということはこの環境変数を再設定すれば使えるようになる.具体的には

とすれば良い.問題は,これを起動時に自動でやりたいということである.ただし,tmuxでアタッチ時に自動でコマンドを実行する方法がわからないので,とりあえずは.zshrcに上記の設定を書いて,必要であればシェル上で

を行うことで再設定するようにすることにした.もっといい方法もある気がするが,そんな頻繁に変化するものでもないので,とりあえずはこれでオッケ.

内容についてどう思いますか?
  • いいね (2)
  • だめ (0)

似ているgraphを探す方法

ある与えられたグラフに対して,全く同じ形をしているグラフを探す問題をグラフ同型判定問題という.数学の分野で同型という言葉が出てくるときは,一見同じようには見えない場合でも,見方(例えば記号の付け方)などを変えることで同じにできるような場合にそう呼ぶことが多い.この場合の同型も同じような意味合いである.グラフには大きく分けて有向グラフなのか無効グラフなのかという違いがあるが,ここでは話を簡単にするため無向グラフに限定する.実は,多くの実問題で計算量や問題設定の緩和を図るために有向グラフを無向グラフにすることは比較的ありふれた話である.したがって,以下の話は必ずしも有向グラフでは無駄というわけではない.

グラフの同型判定問題や,もしくはより難しい問題である,あるグラフの中に探したいグラフを一部として含むかどうかを探す部分グラフ同型判定問題はNPであることはわかっているものの,NP困難であるかどうかはわかっていない(Web上の記事によってNP完全だったりNP困難であったり,もしくはどちらかわかっていないと書かれていたり,実際のところどうなのかわからない).どちらにせよ,この問題を解くことが今の所容易ではないことは変わらない.多くの文脈(例えば化学式の検索など)で,このグラフ同型性判定問題を解決したいと考えられており,多くのアルゴリズムが今までに提案されてきた.

さて,私が今回,扱いたいのはこのグラフ同型性判定問題ではなく,より曖昧とした問題,すなわち2つのグラフの類似度を知りたいという問題である.

不完全マッチング(inexact matching)

今回扱いたいのは不完全マッチング(inexact matching)である.実はこの単語,論文中に出てきた表現であり,私の知る限り日本語訳を見たことがない.inexact matchingは直訳すれば不正確マッチングだが,不正確は日本語の意味合いを考えると,誤解を招きかねない.完全マッチングという別の用語はあるものの,不完全という用語がおそらく今回の意味ではピッタリのように感じられるので,ここでは便宜上,不完全マッチングと呼ぶことにする.

さて,不完全マッチングとは要するに,与えられた2つのグラフは同型ではないのだが,微妙に似ており,その上で2つのグラフ間の最も妥当なノードもしくはエッジの対応付けはどのようなものかという問題である.この対応付けがわかると,2つのグラフの間の距離のような概念まで導入することができ,一昔に流行ったSVNではカーネル法に帰着できると分類問題に帰着可能になるため,このグラフ間の距離を求めることにはビッグデータなどの応用も含めて,一定の需要があるらしいのだ.さて,不完全マッチングでは,この妥当な対応付けというところが味噌で,不完全マッチングを提供するアルゴリズムによって,妥当の意味が違ってくる.一番多く使われているのは,グラフ編集距離(graph edit distance)と呼ばれるものを用いるやり方である.これは文字列に対する編集距離に概念としては近い.グラフのノードもしくはエッジの追加や削除などを行って,片方のグラフをもう片方のグラフに近づけたときに,最も少ない手数で対応付けられたときのコストをもって,2つのグラフの距離とする.また,このときに対応付けられたノードもしくはエッジが今回の場合の答えになるというものだ.しかし,この最も最短のグラフ編集の手順を探す問題はNP困難であることが知られている.最も高速に厳密解を得る方法としては,A*アルゴリズムを用いる方法があるが,この方法ではたかだか15ノード程度が求められる関の山で,それ以上となると手も足も出ない.そこで,これまでに提案されたアルゴリズムでは,近似的にこの問題を解く方法が模索されてきた.

もう一つのこの問題の考え方は,2つのグラフのノード,エッジ同士の対応付に関する適応関数を定義し,その適応関数が最大となるような割当を探す問題に帰着させる方法である.この方法では,予め2つのグラフ間のノードもしくはエッジを対応付けした際の利益のようなものを設定しておく.例えば,グラフA, Bについて,Aの1番めのノードとBの2番めのノードを対応付けたら,10点だとか,Aの2番めとBの2番め同士なら5点みたいな感じである.その上で,すべてのノードとエッジを対応付けたあとのすべての適応値の合計が最大となるように対応付けを行ったときの対応をマッチングの結果とするのである.

上記の2つのやり方,すなわち
1. グラフの編集距離を最小とするやり方
2. グラフの各要素の対応付で最も適応関数の値が最大となるものを探すやり方
の2つは,本質的に違う問題のように感じられるだろうが,実はどちらについても二次割当問題(QAP)というNP困難な問題に帰着できることが知られている.したがって,結局の所,この問題を解くことはQAPを如何に高速に解くかという話になる.

手っ取り早く最新の結果を使いたい人へ

本当は,このあとにこの問題がどうして二次割当問題に帰着可能で,過去の研究ではいかにしてこれを解こうとしているのかを説明しようと思ったのだが,今回も例に漏れず面倒になってきたので,すでにあるライブラリだけ紹介して終わりにする.余裕があれば,そのあたりの話も追記したい.調べた感じでは,次の3つのgithubの実装が役立ちそうだ.
– https://github.com/bgauzere/graph-lib
– http://www.f-zhou.com/gm_code.html
– https://github.com/egbertbouman/fgm-cpp
上記の内,2つはC++で実装されており,残りの一つはMatlabだ.特にこれらは割と最近の結果(2017年)のものまで含まれており,その論文によれば500ノードくらいまではぼちぼち動くらしいので,それなりに使い物になるのではないかと思う.

内容についてどう思いますか?
  • いいね (2)
  • だめ (0)

枯れる分野と研究生活

私がまだ小学生や中学生だった頃,未来は非常に明るいもののように感じられた.その頃は自分がおとなになったときに,どんなことをしてるかは漠然としていたけれど,それでも将来の自分に対する夢と希望であふれていたように思う.ところが大学に入り周りの状況を知るに連れて,世の中がそれほど甘いものではないことを徐々に理解していった.

枯れる分野と研究室選択

一応忠告だが,これからする話はあくまで工学部の情報系に限ったことである.他の分野でどうであるかは,その分野によりすぎるところがあるので,参考程度に留めるように.

研究分野には,当然人気がある分野とない分野がある.今の流れで言えば,人気がある分野は,例えば画像や音声などのマルチメディア系であり,人気がない分野は例えばプログラミングの型システムや,デバイス系などだろうか.実はこういった画像系や音声系などの人気のある分野は,重箱の隅をつつくような地味な研究テーマを扱っていることが多い.今でこそディープラーニングで賑わっているものの,それより少し前はやることに意味があまり見いだせないようなことをやっていることが多々あった.これは,人気があればあるほど研究が進んでしまうために,やり尽くされてしまう傾向があるためだ.アカデミックの世界では既存研究がすでに存在することはやっても意味がないとみなされるため,分野が発展にするに連れてどうしても苦しくなる.そして苦しくなれば苦しくなるほど,重箱の隅をつつくような研究が増えていく.こういった研究は,大抵の場合,やってる側としてもつまらない事が多く,またつまらない研究はインパクトも大したものではないことがほとんどである.しかし,こういった状況はよほどの革新がない限り改善されることはないため,ジリ貧状態は悪化する一方だ.もしジリ貧状態から本当に脱却したいのであれば,異なる分野にシフトするかない.

大学の学部生の段階では,上記のような,分野によってはやることがないくらい堀尽くされていることがあることをよく知らないため,研究室選びのときに,単に面白そうという理由だけで選択することになる.面白くなさそうな分野をやるのは,苦痛にしかならないことがあるため,当然,面白そうな分野を選択するべきなのだが,だからといって本当に面白いことができるかどうかは別問題である.実のところ,入るべき研究室とは,
– 分野が枯れておらず
– 先生が人としてまともで
– それなりに興味が持てる分野
という三拍子を揃えているところをいう.そして,当たり前だが,この3つの要素を満たしている研究室というのは一握りしかない.それ以外の大多数の研究室は,致命的な問題を多く抱えているのが実情なのだ.特に,その先,研究者として生きていくことを考えているのであれば,トップカンファレンスや高インパクトファクターのジャーナルに論文を通していることも重要となってくる.しかし,今の時代,流行に乗った分野かつ,まともな指導教官などそうそういるはずもなく,理想とは一転して暗い研究室生活を経験するのは理系大学ではありふれたことである.

完璧とは絶望だヨ。

サブタイのセリフを知っている人は多いだろう.これはBLEACHの涅マユリの有名なセリフの一つだ.BLEACHの作者である久保帯人が科学についての知識があるとは思えないが,この「完璧とは絶望だヨ。」は科学のある側面を絶妙に捉えている.分野にもよるだろうが,特に工学は人にとっての利便性が最も重要視されるため,研究が進むに連れ,ほとんど完璧とでも言える状態になってしまう事がある.そうなると,研究する意味がほぼ皆無になってしまい,その分野で食っていくのが絶望的な状態になってしまう.例えば,通信路における符号理論,ネットワークセキュリティ,回路設計における論理回路や回路アーキテクチャの研究は大部分がやり尽くされた感があるように思う.加えて,世の中がこれほど便利になったのは,様々な分野が彫り尽くされたことの裏返しなのだから,上に上げた以外にも多くの分野がやり尽くされてしまっているのではなかろうか.

実は物理学の分野でも,このように言われた負の時代があったようだ.大昔,ギブスという有名な物理学者がちょうど学生だった頃,物理学では,まだ量子力学と相対性理論が知られておらず,すべての事象はニュートン力学で書き表されると考えられていた.ニュートン力学では,初期状態として位置と運動量(速度)が与えられれば,あらゆる物体の挙動が一意に定まるという性質がある.そのため,当時はラプラスの悪魔という,世界は実は生まれた直後から将来どうなるのかが全て決まっているという考えに支配されていた.そのため,物理学をそれ以上,研究することに意味があるとは考えられておらず,当時成績優秀であったギブズが物理を専攻した際には,周りに物理学なんてやめたほうがいいと促されたという話が残っている.しかし,その後すぐに相対性理論と量子力学が主にアインシュタインやボーアによって切り開かれることで,そんな負の時代から一転,黄金時代に移り変わったのだから皮肉なものである.

企業で働くという考え

すでに述べたとおり,面白そうなことほど研究が進むため,現状残っている研究課題が面白い内容である保証はまったくない.特にお金になる分野は,企業が一気に投資して研究を進めるため,大学の研究者はジリ貧になりやすい傾向がある.

数学の分野では世の中の問題がすべて解かれることはないことを次のように説明する.すなわち,今の世界に残された問題の数は明らかに加算無限個存在するが,人間がいくら頑張っても永遠にとき続けることは不可能であり,したがって,世の中のすべての問題がとき終わることはない.これは確かに数学では真だろうが,他の分野ではおそらくそうではない.どうしても先行研究があると論文にならないという性質上,ジリ貧になってしまうものである.

面白いことがしたいのであれば,あえて研究者になるという道を選ぶ必要はない.もっと手っ取り早く面白いことをする方法として,企業で働くという選択肢もある.大企業に行ってしまったら,面白いことをするという目的はほとんど叶えられそうにないが,ベンチャーや起業などを行えば,比較的望み通りの結末が得られるのではないかと思う.

内容についてどう思いますか?
  • いいね (5)
  • だめ (0)

頭の中のざわざわ

ざわざわという言葉が正しいのかどうかわからないが,昔から頭の中で何かが暴れだすような感覚に襲われて苦しい状態になることが時々あった.特に子供の頃,風邪などで寝込んでいるときにこの状態になることがあって,その場合は辛くて寝ることができなくなる.

大人になるに連れて症状が軽減されてきていて,今の今まで完全に忘れていたのだが,つい先程その症状に襲われて,そういえばと思い出したのであった.頭の中がざわついて辛くなるという症状がよくある話なのかわからないが,実際にそれに悩まされている人の話を聞いたことは殆ど無い.この症状についてすぐに思い当たったのは脳内のニューロンの発火が暴走するような現象が起きているのではないかという仮説だ.ビッグファイブでいうところの開放性は,脳内のニューロン同士の結びつき(シナプス)が強さを示す指標で,開放性が高い人はそれが災いして幻聴や幻覚などを感じ取る傾向にある.それと似たようなことが頭の中で起きて,勝手に頭の中がざわつき始めるのではないだろうか.もしそうであるならば,この症状は開放性が高い統合失調症やADHDの患者によく当てはまるように思われる.

実際に調べてみると,やはり統合失調症やADHDなどの疾患と関係があるようだった.私の場合は症状が軽い上に,現在は比較的落ち着いてきているため,そこまで問題になってはいないが,この状態に頻繁になる人は生きていくのが結構大変なのだろう.

内容についてどう思いますか?
  • いいね (2)
  • だめ (0)

summer pockets

Key作品といえば,Kanon,Air,Clannad,Little bustersあたりが有名だろうか.その後に出た,RewriteやAngel Beats,charlotte などはいまいち話題にならなかったように思う(実は知らない間にHarmoniaというが出てたみたいだが).ただ,Litlle bustersは今からすでに10年ほど前の作品であり,その後に出た作品がどれも不発に終わっていることを考えれば,Keyに昔ほどの勢いはないだろう.

ところが最近Keyから発売されたSummer pocketsがものすごい高評価を得ていることをレビューで知り,興味本位でやってみることにした.以下はその感想である.

Air,Clannadのいいとこ取りをした作品

やり終えて最初に思ったのは,これは鍵好きが好きそうなお話だなという印象だった.Key作品といえば,微妙にファンタジー要素を含みながら,でも基本は日常を描いたようなものが多い.この作品も例に漏れず,島にまつわる秘密を軸に各シナリオが描かれている.だーまえが原案とはいえ,シナリオライターが別であることを考えれば,今までのKey作品を踏襲して書いたように感じられる.特に,Clannadの影響はもろに受けていて,メインヒロインからグランドルートに至るまでの一連の流れは,Clannadで出てきた演出を組み合わせて作られたように思われる.そのため,Clannadの内容を知っていると,Summer pocketsは全体的に二番煎じ感があり,よくできたKey作品という印象が拭えない.逆に言えば,Key作品としてSummer pocketsから入った人であれば,そういう前知識がないぶん,話をすんなり受け止められるのではないだろうか.それに,二番煎じとはいえ,シナリオ全体の出来は非常によく,名作と呼ばれる所以がよく分かる.シナリオのできが良かっただけあって,やはりもう少しオリジナルティがあっても良かったなと思わざる得ないのが残念だ.

Key作品でおすすめは?と聞かれたら進められる作品

今までであれば,Key作品でおすすめは?と聞かれれば,やはりClannadやLittle bustersなどを勧めていただろう.しかし,これらの作品はやはり古くなってきているし,かと言ってRewriteやAngel BeatsなどはKeyらしくなかったり,内容的にイマイチだったりと問題があった.それに対して,今回のSummer pocketsは過去のKey作品のいいところを引き継ぎながら,現代版として甦った作品だ.長さ自体も,過去のKey作品と比べれば比較的短いが,かといって別に不十分というほど短いわけでもない.したがって,おすすめのKey作品として名を挙げやすい作品になっているのではないかと思う.

総評

このところ不調続きだったKeyだが,ここに来てSummer pocketsという名作を生み出した.全体として二番煎じ感があったとはいえ,過去のKeyのいいところが全面的に出たいい作品になっていると思う.この追い風に乗って,良い作品を更に生み出してもらえればと思う次第である.

内容についてどう思いますか?
  • いいね (1)
  • だめ (0)

関数型言語に関して思うこと

ここ最近というわけではないが,関数型言語に関する話題をよく目にするようになったような気がする.何を持ってして関数型言語であるかというのは非常に悩ましい問題ではあるが,おそらく最も古くに生み出された関数型言語がLispであろうことを考えれば,関数型言語の歴史は非常に古い.それに対して,世の中のコンピュータの基盤であるOSなどの低レイヤを支えるc言語がLispより20年近くあとに生み出されたことを考えれば,c言語やその影響を受けた後続の言語(JavaやLLなど)がいかに新しいかがわかるはずだ.

私はもともとc言語からプログラミングを入門し,次にC++を学び,Java,Pythonと進めてきた.特にC++に触れてきた時間は非常に長く,プログラミングの思考回路がC++をもとに組み上がっていると言っても過言ではない.したがって,初めて関数型言語に触れたときは強い隔たりを感じた.特に違和感を感じたのは,関数型言語の記述がアセンブリから完全に乖離している点だった.CやC++などの手続き型言語が,何をしたいのかを書くのに対して,関数型言語が何が成り立つのかを書くというのはよく言われる話だが,コンピュータの根幹をなすアセンブリ言語が前者の立場にあることを考えれば,関数型言語がそのままの形では全く相容れないことは明らかである.

ではなぜそのような関数型言語が今になって注目されているのだろうか.その一つの答えは,関数型言語を支える非常に強力な仕組みである型システムが,プログラムの記述を簡単にし,更にはバグさえも防ぎ得るということにあるではないだろうか.そもそもc言語やそこから派生した言語は,人間がミスをすると簡単にバグが挿入される可能性がある.これは例えれば鋭いナイフのようなもので,その切れ味をうまく活かせれば大抵のことは効率よく行うことができるが,使い方を誤れば自身を傷つけてしまう諸刃の剣となる.これはc言語の前身であるB言語の更に前身であるBCPLの哲学である

BCPLの哲学は、最良を知ると皆が考える暴君がいるのではなく、何が許されて何が許されないのかという決まりが定められているのでもない。どちらかといえばBCPLは、たとえ明らかに馬鹿げた事態に直面したときでさえ、不平を言わずに自らの能力を最大限に活かしてサービスを提供せんとする召使いとして振舞う。彼が何をしていて、細かい制限に制約されないということを、プログラマーは常に理解しているものと考える。
「BCPL, the language and its compiler」より

にあらわれている.これはプログラマが過ちを侵さないという絶対の信頼のもとで成り立っている.しかし,実際にはそのようなことは絶対にありえない.むしろ世の中にこれほど人為的ミスとしてのバグがはびこっているのは,人間がそこまで賢くないことを示唆しているのではなかろうか.

さて,人の書くプログラムには絶対にバグが入り込むと考えたとき,これを防ぐためにはどのようにしたら良いだろうか.関数型言語における,この問題に対する答えはバグが入り込む可能性があるような書き方は,そもそも許さないというものである.関数型言語では,関数の副作用を極力許さないことと,型に基づいた統一的な記述により制約をかけている.このような制約は,動的型付けであり副作用がてんこ盛りのLLから見れば,非常に強いものだが,バグを防ぐ上では非常によく機能することがわかっている.

ある言語が,手続き型言語と関数型言語のどちらであるかは程度の問題であって,完全な線引があるわけではない.pythonやperlなどであっても記述の仕方によっては関数型言語のように書くことができるだろうし,LispやOCamlでも手続き型言語のような記述ができるはずである.しかし,言語のサポートによって関数型言語として代表されるような記述がどこまで可能であるかが変わってくる.特に大きな線引は高階関数が扱えるかどうかではないかと思う.関数型言語が特に流行りだした頃から,多くの言語でラムダ式(無名関数)をサポートするようになったが,部分適用が言語レベルでサポートされているのは殆ど見たことがない.しかし,関数型言語にとって高階関数が無くてはならない存在であることを考えれば,これをサポートしていない多くの言語は一般には関数型言語とは呼べないのではないか.

とにかく,高階関数はもちろんとして,完全に純粋すなわち副作用を持たない言語としてHaskellが有名である.Haskellのすごいところは,その徹底ぶりにある.多くの関数型言語は,副作用を全く持たないことを保証できていない.これはおそらく,利便性の観点から完全に副作用を除外すると,非常に書きづらくなる恐れがあるからではないだろうか.画面に文字を表示するということですら,簡約のやり方によっては表示の順番が変わってしまう恐れがあるため,そのままでは副作用は避けられない.しかし,Haskellではモナドを用いることで,理論的にも妥当なやり方で副作用を追いやることに成功している.さらに,Haskellは遅延評価を用いることで便宜的とはいえ,加算無限の概念を記述することも可能である.Haskellのすごいところは,そういった,一見達成困難なことを理論的に美しい方法で,コンピュータ上の実装のレベルまで落とし込んでいる点にある.これにより,Haskellは数学と非常に相性が良い.特に関数型言語の利点である,性質を記述することでプログラムが可能であるという点がうまく引き出されており,これは数学の式の記述に非常に近いものになっている.

ここまで思ったことをつらつら書いてみたが,あまりに何も考えてなさすぎてとりとめのないものになってしまった.とにかく,最後に私が言えることは,関数型言語を全く知らない人はどのようなものかを一度勉強してみるべきだということだ.そこには,手続き型言語だけでは感じることができない,理論によって整理されたきれいな世界が広がっているのだから.

内容についてどう思いますか?
  • いいね (2)
  • だめ (1)

proxy dhcpを使ってネットワークブートする方法.

概要

ネットワークブートは一般にdhcpの機能として提供されます.dhcpのサーバの動作は以下の手順を踏みます.
1. 対象のマシンを起動させるときに,ネットワークブートを選択します.
2. ネットワークブートを選択すると,dhcpサーバをブロードキャストアドレスに問い合わせて探します.
3. dhcpサーバは対象のマシンにipアドレスを割り当てると同時に,ネットワークブートする際に必要なイメージの場所を通知します(このとき使用されるのはtftpサーバのアドレスです.).
4. 対象のマシンは通知されたipアドレスを用いてネットワークに接続し,ブートイメージを指定されたtftpサーバから落として起動します.
5. これ以降は,使用するブートイメージによって動作が変わります.

さて,上述したとおり,ネットワークブートではdhcpサーバの設定を変更することが必須となるわけですが,ネットワーク管理者でない限り,dhcpサーバの設定をいじれることは稀です.ではもうどうしようもないのかというとそんなことはなくて,proxy dhcpという方法があります.これは,proxy dhcpサーバという,ネットワークブートの機能だけをもたせたサーバをたてることで,ネットワークブートを行います.このサーバはipアドレスなどの割当は本物のdhcpサーバにお願いし,ブートに関する処理だけをproxy dhcp側で分離して行うものです.

今回の例では,以下のようなディレクトリ構成を想定しています.
– /srv/tftpディレクトリをtftpでの公開ディレクトリとする
– /srv/tftp/pxelinux.0という名前でpxelinuxのブートローダのイメージを配置しておく.
– /srv/tftp/debianディレクトリにカーネルといろいろを格納しておく.
– /srv/tftp/pxelinux.cfgディレクトリにpxelinuxの設定を置く.

やり方

proxy dhcpとして使える有名なソフトはdnsmasqというものです.これは標準レポジトリでインストールすることができます.dnsmasq自体は,汎用のdhcpサーバとして使える上に,tftpサーバの役割も持っています.ここでは,dnsmasqのproxy dhcpとtftpサーバの機能のみを利用することにします.そのために以下の設定を/etc/dnsmasq.confに加えます.

port=0はproxy dhcpとして使用することを意味しています.次のenable-tftpはtftpサーバを有効にする設定です.tftp-rootはtftpサーバのルートディレクトリを示します.ここでは/srv/tftp以下をtftpサーバで公開することになります.dhcp-rangeはproxy dhcpの適用範囲となるホストをネットワークアドレスで指定します.最後の,pxe-serviceですが,最初のx86PCは絶対に必要なおまじないだと考えましょう.次の”pxelinux”は単にラベルなので好きに指定して良いです.最後のpxelinuxはブートイメージを表します.この名前の最後には自動的に’.0’が末尾に追加されることになるので,実際に指定されるファイルの絶対アドレスは/srv/tftp/pxelinux.0となります.ここではネットワークブートにpxelinuxを使用するので,ブートするイメージはpxelinux.0です(このファイルはネット上から適当にダウンロードした後に,事前に/srv/tftpディレクトリを作ってコピーしておきます).

さて,この設定を行ったらdnsmasqを再起動します.次に必要なのはpxelinuxの設定です.この設定ファイルは/srv/tftp/pxelinux.cfgディレクトリにまとめます(存在しなければ作ってください).このディレクトリの位置は(おそらく)pxelinux.0と同じ階層である必要があるのかなと思います.このディレクトリ内のファイル名にはMACアドレスかipアドレス,そしてdefaultが使えます.例えばネットワークブートするマシンのMACアドレスがac-1f-6b-0a-XX-XXの場合には,pxelinux.cfgの中に,”01-ac-1f-6b-0a-XX-XX”というファイルを作ると,pxelinux.0はこのファイルを読み出すように働きます.もし,pxelinux.cfgの中に該当するMACアドレスのファイルがなければ,次にipアドレスで合致するファイルを探し,それもなければ”default”という名前のファイルを読み出します.

設定ファイルには例えば以下のようなことを書きます.

まずdefault debianですが,これは”debian”という名前のファイル,もしくはlabelを最初に読み出すことを指示します.例えばブート時にメニューを表示したいときには,menu.c32をpxelinux.0と同じディレクトリに持ってきておいて,default menu.c32とすれば,メニューを表示させることができます(現在は,menu.c32を使用する際に,libutil.c32もコピーしておく必要があります).ここでは,defaultの次の行にある,LABEL debian以下の設定を標準で呼び出すことを表しています.次の,kernel debian/vmlinuzはカーネルとしてdebian/vmlinuzを読み出せという指示です.このアドレスは,tftpのルートから見たディレクトリを指します.ですから,今回の場合は,/srv/tftp/debian/vmlinuzのことです.次のappendの行は様々な設定を行うためのものです.ここの部分の各オプションは次の意味を持ちます.

  • root : ブートしたあとのルートとなるデバイスを指定します.ディスクレスサーバの場合は/dev/nfsです.
  • initrd : initramfsのイメージを指定します.
  • net.ifnames : ネットワークカードの名前を認識した順でeth0, eth1, .., ethnという形で/dev/以下に割り当てます.ネットワークカードのデバイス名がわかっている場合はこのオプションは必要ありません.
  • nfsroot : nfsでルートシステムをマウントする際に指定します.ここではipアドレス172.20.24.21の/srv/nfsrootというディレクトリがマウントされます.
  • ip : ipアドレスの割り当て方を指定します.dhcpで割り当てる場合はip=dhcpとします.静的な場合は,ip=<ipアドレス>:<ネットワークブートの接続先サーバのipアドレス>:<デフォルトゲートウェイ>:<サブネットマスク>:<ホスト名>:<使用するネットワークデバイス>という形で指定します.後ろの方を省略する場合は,指定したいところまで記述してそれ以降を単に書かなければ良いです.例えばip=172.20.24.10:172.20.24.21だけでもOKです.
  • init : おそらく一番最初に起動するプロセスのことではないでしょうか.昔はinitというプロセスが一番最初に呼び出されて動いてましたが,今はsystemdを使うのが一般的です.
  • rw : ディスクへの読み書きを許可します.異常が出る可能性も充分あるので,roにしたほうが良いかもしれません.
  • quiet : 忘れました.

こんな感じの設定を各設定ファイルに書きます.もしすべてのマシンで同じ設定でブートするのであれば,pxelinux.cfgにはdefaultファイルのみを作ればいいです.さて,上記の設定を見れば分かる通り,カーネルとinitramfsのイメージだけは必ず用意する必要があります.iniramfsのイメージの作り方は,まずinitramfs.confを作ります.例を示します.

特に特筆すべき事はありませんが,一つだけ.DEVICEオプションには使用するネットワークデバイスを指定できます.さて,この設定ファイルを作ったら,

を実行します.ここで,-dオプションはinitramfs.confの場所を指定します.また-oオプションは出力ファイル名のことです.これを実行すると大量にエラーが出ますが,あまり気にする必要はありません.最後に,vmlinuzを実行中のリナックスからとってきます.ホストのカーネルイメージは/boot以下にあります.例えば,/boot/vmlinuz-4.9.0-amd64みたいな感じです.これをvmlinuzにして/srv/tftp/debian/以下にコピーします.

さてここまでをまとめるとディレクトリ構造は以下の通りとなります.

最後に,必要なものがあります.それはldlinux.c32というファイルです.これは/usr/lib/syslinux/modules/bios/ldlinux.c32というところにあります.これをpxelinux.0があるディレクトリにコピーします(sudo apt install syslinuxを事前にしておくこと).

以上により必要な処理は終わりです.ネットワークブートをして動作を確認します.

内容についてどう思いますか?
  • いいね (3)
  • だめ (0)

べき乗則の話について

最近,「歴史は「べき乗則」で動く」という本を読んだ.amazonのURI

この本では,世の中の多くの減少における確率分布がべき乗則に従っており,その背景にはフラクタル構造のような再帰的な枠組みが隠されているのだということを終始語っている.これはべき乗則がスケールに対する普遍性を持っており,その背後にフラクタルのようないくら拡大縮小しても構造が変わらないものが存在していると仮定するとうまく説明可能であることに由来している.

本書では,地震の規模と発生頻度,株価の変動,その他生物の絶滅などに至る多くの事柄がべき乗則に従っていることを述べている.本書で取り扱われる議論をそのまま適用すれば,これらの現象を支配する要因にはフラクタル構造が隠れており,それゆえ大地震が発生した理由や,株価が大暴落することには特別な意味があるわけではないことになる.したがって,なぜ人がこれらの現象が起きるのか説明(予測)できないのかといえば,そもそも予測すること自体が不可能な事柄であるからという結論になるのである.

この話は一見,非常に的を得た説明のように感じられる.しかし,本書の最後にある訳者のコメントには,このべき乗則による説明は不十分であるという見解が述べられている.理由としては簡単で,たとえ現象の発生確率がべき乗則ではなく,正規分布に従っている場合でも,平均から大きくハズレた現象が発生することを予想することは困難であるからである.これも確かに正しいように感じられる.

さて,ではどちらの意見のほうが正しいのだろうか.この話を考える際には,おそらく次の2つのことに注意しなければならない.それはある分布からサンプリングすると必ずその分布にそったランダムな値が得られてるということ.またそのことと,予想可能であるかどうかは特に関連がないことである.すなわち,ある現象の発生確率がある分布に従っているとするとき,その情報だけでは,ある瞬間にその現象が起きる可能性は確率的にしか評価できない.したがって,その事実だけではどれほどの大きさで対象となる現象が発生するのか予測することは困難となる.しかし,この予測可能性は,分布のサンプリングだけに着目するのではなく,分布の形に着目すれば,その可否がわかるというのが本書の主題だろう.例えば,正規分布に従っている現象を対象とすれば,正規分布はスケールに対する普遍性を持たないのだから,平均に近い部分に対応する現象と,平均から外れた部分に対応する現象とでは,その発生原因は大きく異なるだろうという予想がたつ.しかし,べき乗則のような分布に従っていた場合には,スケール普遍性により,正規分布に当たるような特定の発生原因(すなわち予兆)を見つけることが困難なので,極端な事象(大地震など)を予想することができないというのが,本書の著者が言いたいことだったのではないだろうか.

しかし,このような説明もあまりに感覚に訴えかけすぎていて,その妥当性がそこまで納得できるものになっていない.結局の所どういうふうに考えればよいのだろうか(誰か教えてください).

内容についてどう思いますか?
  • いいね (1)
  • だめ (0)

配列の中で2番めに大きい要素

最近ブログを書いてないって言われたので,何かを書こうとおもったけれど,何を書くのが全く思いつかないので,今日,聞かれた質問について書こうと思う.

聞かれたのは,与えられた配列の中から2番めに大きい要素を知る方法である.1番大きい要素の取得の仕方はよくある話だが2番めはあまり聞いたことがない気がする.どうやって解くのが最も効率が良いだろうか.ちなみにプログラム演習の課題のようなので,あまり高度なライブラリを使用するのは避けなければいけないという制約がついている.

最も簡単な解法は,一番最大の要素をまず見つけ出し,次にその要素を取り除いた配列から最大の要素を取り出す方法だ.これは愚直だがわかりやすく書きやすい.ただし,最大値を求めるループを2回使う必要があり,いくらか冗長な気がする.次に,思いつくのはソートして2番めの要素を取り出す方法である.スクリプト言語を使っているのであればこれが一番ありがちなのではないだろうか.ただし,一から全て実装する場合は,これは少し面倒だ(ライブラリがまったくないそんな状態は普通ないのだが).

さてここではよりフォーマルな形で定式化することで,この問題を効率よく解く方法を考えたい.まず与えられた配列を\(a_n, \dots, a_1\)という数列であるとしよう.\(a_k, \dots, a_1\)の中で一番大きい要素を返す関数を\(f(k)\)とする.このとき明らかに
$$f(k) = \begin{cases}
-\infty & (k=0) \\
f(k-1) & (f(k-1) \geq a_{k}) \\
a_{k} & (otherwise)
\end{cases}$$
が成り立つ.次に,\(a_k, \dots, a_1\)の中で2番めに大きい要素を返す関数を\(g(k)\)としよう.定義から明らかに\(g(k) \leq f(k)\)である.\(g(k)\)の計算は\(f(k)\)と非常に近いはずである.しかし,2番めであるという制約が,問題を難しくしている.まず,\(g(k)\)と\(g(k-1)\),そして\(a_k\)の関係を考えよう.まず,\(a_k\leq g(k-1)\)のときは明らかに\(g(k)=g(k-1)\)である.次に,\(a_k> g(k-1)\)の場合は,\(g(k)=a_k\)としたいところだが,\(f(k)\)との兼ね合いがあるため,それだけでは条件として不十分だ.実際には,\(f(k-1) < a_k\)のときは,\(g(k)\ne a_k\)としなければ,2番めに大きいという条件を満たせない.逆に言えば,これだけで必要条件を満たすことができる.したがって,まとめれば
$$g(k) = \begin{cases}
-\infty & (k=0) \\
g(k-1) & (g(k-1) \geq a_{k}) \\
a_{k} & (f(k-1) \geq a_{k}) \\
g(k-1) & (otherwise)
\end{cases}$$
である.思ったより簡単だった.

内容についてどう思いますか?
  • いいね (2)
  • だめ (1)