CakePHPとWPFの相互運用 cake bake を使って MVC を自動生成する

最初に CakePHP の cake bake all を使って Model/View/Controller を一気に作成したいところですが、既存のデータベースから移行する場合、CakePHP の複数形ってのにひっかかります。

データベース上に store というテーブルがあって、これに対応する MVC を作ろうとすると、こんな風に Model.php 内でエラーがでます。CakePHP のテーブル名のルールとして複数形にするわけで、本来ならば「stores」を探しているのですが、このテーブルがありません、ってな具合ですね。

Error: Table stores for model Store was not found in datasource default.
#0 C:xampphtdocscakephp-2.4.5libCakeModelModel.php(3498): Model->setSource('stores')
#1 C:xampphtdocscakephp-2.4.5libCakeModelModel.php(1355): Model->getDataSource()
#2 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandTaskModelTask.php(503): Model->schema(true)
#3 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandTaskModelTask.php(801): ModelTask->doAssociations(Object(Model))
#4 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandBakeShell.php(174): ModelTask->bake(Object(Model), false)
#5 C:xampphtdocscakephp-2.4.5libCakeConsoleShell.php(431): BakeShell->all()
#6 C:xampphtdocscakephp-2.4.5libCakeConsoleShellDispatcher.php(207): Shell->runCommand('all', Array)
#7 C:xampphtdocscakephp-2.4.5libCakeConsoleShellDispatcher.php(66): ShellDispatcher->dispatch()
#8 C:xampphtdocscakephp-2.4.5appConsolecake.php(36): ShellDispatcher::run(Array)
#9 {main}

Model 名は「Store」なんだから、なんとか store テーブルも見つけてくれれば便利じゃないかと思うんですが、これで結構悩みます。
で、テーブル名を変えられる、あるいは新しいシステムの場合はいいんですが、既存のシステムがあったり、それなりの命名規約があたりすると変更は難しいのです。なので、テーブル名はそのまま「store」のままで使いたいですね。

■テーブル名に単数名を使う

そういう場合は、あらかじめ、$useTable に store を指定した Model を作っておきます。そうやって、

<?php
App::uses('AppModel', 'Model');
/**
 * Store Model
 *
 */
class Store extends AppModel {

/**
 * Use table
 *
 * @var mixed False or table name
 */
	public $useTable = 'store';
}
&#91;/code&#93;
<p>
そのあとに、
</p>
<ol>
<li>cake bake Model で Store モデルクラスを作り直す(バリデーションとか)</li>
<li>cake bake all で Store テーブルに対する MVC を作る</li>
</ol>
<p>
という手順を踏みます。こうすると、Store モデルクラスが、「store」テーブルを対象にするという複数形なしの方法で作れます...が、これってコマンドラインでぽちぽち y/n を決めないといけないので、もっといい方法がないものかと思っ、
</p>
[code]
> cake bake Model Store
> cake bake all Store

の2コマンドで一気にできますね。何故か cake bake Model Store のほうは複数系を探さずに単数系のままなので、一気にモデルクラスができます。そのあと、bake all Store でモデル名(テーブル名?)を指定すると、これまた PHPUnit で y を押すだけで、一気に作れます。
なので、「y」だけ書かれた応答ファイルだけ作っておいて次のようなバッチファイルで動かせば大丈夫ですね。

cmd /c cake bake Model Store
cmd /c bake all Store < yes.txt
&#91;/code&#93;
<p>
テーブル全体は適当なperlかPHPでスクリプトを書いてやればOKです。
そうして作成してやると、ひとまず1つのテーブルに対して CRUD ができる MVC が作成されます。
</p>
<a href='http://www.moonmile.net/blog/wp-content/uploads/2014/01/wpid-dworkblogimage20140128_02org1.jpg'><img border='0' src='http://www.moonmile.net/blog/wp-content/uploads/2014/01/wpid-dworkblogimage20140128_02thum1.jpg'/></a>
<p>
この例では、starttime というテーブルに http://localhost:81/cakephp-2.4.5/Starttimes でアクセスをします。コントローラー自体は複数形で生成されるので、元テーブル名と若干ずれてしまうのですが、まあ良しとしましょう。
Person テーブルが People でアクセスするとか、迷う点もあるのですが。
</p>
<p>
■テーブルの外部結合を指定する
</p>
<p>
あらかじめ、テーブルの外部結合をしておけば CakePHP が拾ってくれる?と思うのですが、既存のテーブルには外部結合が設定されていませんでした。まあ、そのまま使うのもよいのですが、せっかく自動生成された View で ID の羅列があるのはデバッグ上面倒くさいです。$belongsTo を使うと Model に外部結合を設定できます。
</p>
<p>
Model クラスに下記な風にアソシエーションの設定を追加します。
アソシエーションの設定は、$belongsTo(多対1)だけを使いました。データ上、親からその子を検索するというのがなくて、子テーブルから親の名前が頻繁に発生するからです。このあたりは、システムの特性に合わせてですね。
</p>
[code lang="php"]
class Store extends AppModel {
	public $belongsTo = array(
	    'Areagroup' => array(
	        'className'    => 'Areagroup',
	        'foreignKey'   => 'AreaGroupID'
	    ),
	    'Sellingcompany' => array(
	        'className'    => 'Sellingcompany',
	        'foreignKey'   => 'SellingCompanyID'
	    )
	);

この例では、Storeテーブルが、AreagroupテーブルとSellingcompanyテーブルに外部結合しているという設定です。子であるStore(お店)が、Areagroup(地域)やSellingcompany(会社)に含まれているというスタイルが直観的に書けます。あとはいろいろ調べたのですが、あまり必要はないでしょう。必要であれば適宜クエリを書いて取ってきてしまいます。
というのも、親から子を探す時にたいていは検索条件を入れるので、単純な $hasMany ではうまくいかないのです。適切な設定をしたり適切なモデルを作ってもよいですが、そうなると特定の検索条件ごとにモデルが必要になってしまいモデルクラス自体が氾濫してしまいます。なので、普通に ER 図にできる範囲でよいかと。

アソシエーションの設定が終わったら View だけを書き換えます。

cake bake View Store

とすると Store モデルに関する View だけを書き換えます。cake bake all をするとせっかく設定した $belongsTo が消えてしまうので注意してください。

アソシエーションをつけたときには、指定先のページが開くリンクが作られます。

■Viewでリンク先の名称を変える

先ほどの SellingCompanyID(会社ID)はわかるのですが、これが数値のままだと編集しづらいですね。これは View のほうを直接修正します。 $belongsTo で設定したときは、$store[‘Sellingcompany’][‘ID’] のように外部連携先のテーブル名とIDが設定されています。ページを開くときは、IDで指定するのですが画面表示の場合は、
$store[‘Sellingcompany’][‘Name’] のように名前を設定しておきます。

<td>
  <?php echo $this->Html->link($store['Sellingcompany']['Name'], array('controller' => 'sellingcompanies', 'action' => 'view', $store['Sellingcompany']['ID'])); ?>
</td>
<td>
  <?php echo $this->Html->link($store['Areagroup']['Name'], array('controller' => 'areagroups', 'action' => 'view', $store['Areagroup']['ID'])); ?>
</td>

こうすることで、会社名と地域名をが表示できます。

実際は、CakePHP は Web API のバックエンドとしか使っていないので、この View は使われません。ですが、最初のデバッグ時には有効なのと、簡単なデータ編集ならばこれで十分なのでそのままにしておきます。

カテゴリー: CakePHP, WPF | CakePHPとWPFの相互運用 cake bake を使って MVC を自動生成する はコメントを受け付けていません

余裕があるうちにCakePHPとWPFの相互運用をまとめていく(準備編)

去年末にリリースした、某予約システムですが、CakePHPとWPFの相互運用をしています。この発想は、手始めに Silverlight+WCFの組み合わせで実験 | Moonmile Solutions Blog からありました。サーバー側にLAMP環境を据えて、クライアントでWindowsを使うという仕組みですね。当時から、Web APIはあったのですが、結合の部分はWCFのほうがよいのではないか?と思ってWindows寄りに考えていました。最近は、ASP.NET MVC、Web APIの流れで、WCFを直接扱うよりもRESTfulで送ればよいという流れがあって、艦これ諜報員の場合も単純なJSONで戻してくるので「諜報」が楽ってのもあります。

仕事なので、サーバーに何を使うのかの選定から始めるわけですが、コストの問題やらサーバーのプログラマ調達の問題などがあって、

  • サーバーにLinux+MySQLを使う
  • クライアントにWindowsを使う
  • クライアントにブラウザを使う

というパターンになっています。クライアントがWindowsだけならば、直接MySQLにアクセスしてもよいのですが、ブラウザも含めるとなるとやり取りは、HTMLかjQuery+JSONかってパターンですよね。そこそこ古いブラウザも含めるとなると、HTMLをべたべたに返してやる必要があります。また、もともとのデータベースがMySQLだったこともあって、そのまま移行。レンタルサーバーの運用コストを考えてLinuxを使う、といったところです。まあ、結果論でいえば、Windows Server+ASP.NETの組み合わせでもよかったのでは?と思うときもありますが、それは結果論で。

さて、サーバーの選定と大まかな仕組みを決めたところなのですが、パフォーマンス的にどのくらいかというと、某予約システムでは、1000件/日程度のアクセスしかないので、それほどのパフォーマンスはいりません。これが1000件/秒とか1000件/分とかだったら考え直すのでしょうが、ここはあっさりCakePHPを選択しました。CakePHPがいかに遅いとはいえ(実際遅いのですが)このぐらいのスピードだと耐えられるでしょう。しかし、実装してきてわかったのですが、Web APIをふつうのApplication APIのようにバシバシ呼び出しとレスポンスが悪くなります。当たり前ですね。間にHTTP通信を絡めてしまうし、エンドのCakePHP自体のレスポンスもあるし。なので、ある程度は、APIの呼び出し回数は考慮しないといけません。

image

本来は、ブラウザからCakePHPへアクセスしたかったのですが、諸事情があってブラウザからOriginal に作成した MVC をアクセスしています。ブラウザでの予約は、不特定多数の一般的なお客さんなので、Viewの部分はそれなりにリッチに書かなければいけません。なので、CakePHPの吐き出すソースは何に使うというとおもにWindowsクライアントからのWeb API呼び出しに使います。また、本来ならば Original MVC から直接MySQLへのアクセスを禁止するほうがよいのですが、SELECTに限って許しています。これも諸事情の関係ですね。

なので、CakePHPのView部分に関しては、いろいろ豊富な方法があるのですが一切使っていません。あくまで Web APIに徹する(Windowsクライアントからの呼び出しに応答する)役目を負わせています。

CakePHPからの応答がXML型式になっていますが、これはJSONでも構いません。当時、WindowsクライアントでJSONをパースする方法が(私的に)見当たらなかったのでXMLにしたのですが、実は DataContractJsonSerializer クラス (System.Runtime.Serialization.Json) を使えばOKです。Xml系のオブジェクトになるので、自前の ExDoc と組み合わせて使っています。XMLの構造=クラス構造とはしたくなかった≒面倒だった、というのも理由のひとつですが、システム的にクライアントとサーバーのアップデートは「同時には行えない」ことが分かっていたので、APIからの構造が変わっても古いクライアントでも緩く動く、というシステム構成を作っておくためです。このあたりのテクニックは、cookpad に学んでいます。

Windows クライアントは WPF で作っています。業務用なので Windows フォームでもよかったのですが、先行きタブレット版のストアアプリとかスマートフォン対応を考えると XAML で書いておいたほうがいいですよね。Windows クライアントのほうは社内業務用なので、インストーラ付きでガシガシ作れる状態です。ただし、Windows 7対応ということで、まだWindows 8にはなっていません。

WPF アプリのほうは、MVVM パターンを使っています。なので、なにかの機能を入れて WPF クライアントを拡張しようとすると MVVM+MVC の苦行パターンにはまります。この辛さは半端ではありません。去年の夏の1か月は暑いさなかこの「苦行」をしていました。マスター系のクライアントも MVVM で作っていたので、10テーブルぐらいのマスター画面を WPF で、あと諸々の業務用画面を WPF でという具合ですね。それに対応する CakePHP の Controller をちまちま作るという感じす。もちろん、CakePHP は bake を使って自動生成させるのですが、定型的なブラウザのマスター画面ならいいのですが、ちょっと気の利いたテーブル構成(複数テーブルっていう意味で)とちょっと気の利いたマスター画面を作ろうと思うと、手作業で追加が発生するんですね。当然ですが。まあ、それでも、そこそこのコード量を自動的に吐き出してくれるので、そこそこのスピードでマスター画面が作成できたのはよかったかと。

そのあたりも含めて、CakePHPとWPFの相互運用をテーマにぼちぼちと書き下していく予定です。ちなみに、機能拡張の部分はそれぞれが分離されているので非常に楽です。CakePHPのWeb APIになっているので、ブラウザでAPIの結果をXML型式で確認できるとかデバッグも多少楽になっています。複雑なSQLをどう解くのか(どうテストするのか?)という点では、PHPUnitを使うのか、Web API経由でMSTestを使うのかという問題もありますが、いまのところ MSTest 一本でやっています。

カテゴリー: CakePHP, WPF | 余裕があるうちにCakePHPとWPFの相互運用をまとめていく(準備編) はコメントを受け付けていません

リモートデスクトップ接続すると画面が真っ黒になる、ときの対策(おそらく自分限定)

えらく嵌ったのでメモ書きとして。Windows 7ではでなくて、確かWindows 8でもなでなくて、Winodws 8.1で出ているのでドライバーの問題のような気がするのですが、原因は定かではない。けど、対処の仕方は明らかに「Intel HD Graphics 3000」ドライバー が悪さをしていると、と。追記 2014/02/21
コメントにある通り、「AMD Radeon 6600M and 6700M Seriese」のほうがダメなようです。「「AMD Radeon 6600M and 6700M Seriese を無効」することで、リモートデスクトップ&外部端子がうまく動きます。

■現象

リモートデスクトップ接続をしたときに画面が真っ黒になります。真っ黒になるのはいくつか原因があるよう、ビットマップキャッシュの問題もあるのですが、今回は違って接続先のグラフィックドライバーが変なことをやっているようです。画面は真っ黒ですが、マウスやキーは効いています。強制的に切断すると、なんとなく開いたウィンドウがでてきます。

うちの vaio のノート VPCSB は Windows 8.1 をクリーンインストールすると、ディスプレィアダプタが2つ見つかります。内部ディスプレイ用と外部端子用なんですかね?謎なのですが、2つあります。

image

でもって、Windows 8.1 はクリーンインストール時に、ネットワークから「自動的に」最新のグラフィックドライバーをダウンロードして「勝手に」インストールします。ええ、auto にインストールしてくれるのは便利なので、最適化されるのはいいのですが、ここでインストールされてしまう「AMD Radeon 6600M and 6700M Seriese」が問題なのです。

image

このノート自体、型が古いので(出た当時はWindows 7だったし)8.1 に対応しきれないから、なのかもしれまんせんが、メモリは 8GB 積んだし、CPU は Corei5 なのでそこそこ開発にも使えます。なので、最近 SSD 256 MB に乗せ換えて(何故か)英語版 Windows 7 で動かしいたのですが…まあ、これが別件の不具合で使い物にならなくなったんですよね。いや、それはまあ、VMWare 上に戻せばいいわけで、復旧させているわけですが。

■対処

で、Windows 8.1 をクリーンインストールしたはずなのに、コントロールパネルの「プログラムと機能」を開くと、こっそり?「インテル HD グラフィックス・ドライバー」がインストールされているわけで、これをアンインストールすると、無事リモートデスクトップで接続できるようになります。そもそも、グラフィックドライバーが2つ表示されるのが変な感じがするので、なんらかの競合っぽいのですが、まあアンインストールしてもOK。

image

ただし、なんらかの電源の制御も入っているらしくノートの蓋を閉じたときの制御が「電源コントロール」から消えてしまいます

試しに サポート: インテル® HD グラフィックス 3000/2000 内蔵第 2 世代インテル® Core™ プロセッサー から最新のバージョンをダウンロードして適用してもダメでした。そもそも、Windows 8.1 が自動でダウンロードしてくるのが、9.17.* ぐらいまで一緒なので、そこそこ最新なはずなんですよね。このあたりはよくわかりません。 → 「AMD Radeon 6600M and 6700M Seriese」を無効かして、「Intel HD Graphics 3000」だけの残すようにするとうまく動きます。Intel HD Graphics 3000 のほうは、外部出力端子のドライバーのようです。

私の場合、ノートブックに対してリモートデスクトップ接続することが多いので、これができないと不便なんですよね。ノートのキーボードを使うよりもデスクトップから使ったほうが効率が良いというのもあるのですが、ファイル移動とか資料のコピーとか設定なんかもリモートデスクトップ経由なので。

 

カテゴリー: 雑談 | 4件のコメント

余裕があるから F# で数独を解くを発表してみる

.NETラボ 勉強会 2014年1月 : .NET Lab の発表スライドです。

1週間ほど前にプログラムを作ったので、それを整理して発表すれば…と思ったのですが、直前に仕事先の不具合と前日の風邪であえなくダウン。スライドを作ったのは、当日の午前中です。ええ、いつものことなんですが。

当日発表に使ったデモコードは、http://sdrv.ms/1aWXJZH からダウンロードできます。

F#については初心者なので、突っ込みどころ満載なのですが(というほど内容も書いてありませんが)、その分、デモなところでC#プログラマから見てF#を使うと便利そうなところを紹介できと思います。ここ1か月間集中的に使ってみてわかったのは、

  • F# intaractive を使って、トライ&エラーを繰り返してプログラムが作りやすい
  • 中途のパラメータを手作業で入れながら、プログラムを実行しやすい
  • .NETのライブラリを簡単に呼び出せる

ところが(私にとっての)利点です。この手のロジックを作るとき、トライ&エラーはスクリプト言語(私の場合Perl)を使ってみたり、C++でCppUnitを作って何度も繰り返したりするのですが、F#で作ると…というか、Visual Studio の F# Interactive との組み合わせが非常に便利です。スクリプトで書いて、Alt+Enterで実行して、結果を見るという感じで繰り返します。おそらく他の関数型言語(OCamlとかHaskellとか)にも似た環境はあると思うのですが、Maxima と同じ感覚です。

そんな訳で、引き続きパズル系を解く/作るに使うのとFEM作りへ。

カテゴリー: F#, 勉強会 | 余裕があるから F# で数独を解くを発表してみる はコメントを受け付けていません

有限要素法をF#で実現しようという続きメモ(渡航編)

年末から続けている構造解析をF#で作成しようという話のメモを少し。主に参考書の紹介です。

仕事がら有限要素法を使って計算をしている…部分の修正などを行っているわけですが、有限要素法のコアな部分は触っていないんですよね。なので、専門家が話すテクニカルタームについていけないのが不甲斐ないので、再勉強がてらF#も同時に学習しようというねらいです。

なんで、F#かというと、手元のプログラムがFortranとC++の組み合わせでたまにC#/WPFが混じります。これに組み合わせるとなると、.NET系がよいわけで、ならば頭文字FつながりでF#がよいか、というノリで。いや、実のところは、有限要素法なコアなところを.NETのPCLで作っておけば、タブレットとかストアアプリとか使いやすいだろうという腹づもりです。更にXamarinと組み合わせればそのままiPad上で動くかもしれません。

ちなみに、有限要素法のソフトウェアとしては NASTRAN と ANSYS が2強らしく、これからデータコンバートをすると適当なメッシュデータを作れます。実は Femap の無償版があることを知ったので少ない節点ならばこれで試すのもありですよね。あと ADVENTUREプロジェクト というのがあって、ver.1 ならばメールアドレスのみでソースを見れます。

0から学ぶ…ってほどでもないのです。以下の本からスタートしています。

 

式を Fortran に直したり C++ に直すというよりも、根本的なところを学びたかったので、仕事上実践の部分と基本的なところと2冊用意しました。2年前に買ったのですが、ざっと読み下したままだたんですよね。なので、「実践 有限要素法~」の演習問題のところを正月に鉛筆で解いていました。

そんな中で行列が出てきたので、大学以来の線形代数の本もいくつか。

大学の教科書はもう手元にないので図書館で借りてきています。ざっと読み返して、行列と行列式と逆行列がわかればOKです。固有値はいるのかな?原子力学科のときは使ったのですが、構造解析で必要かどうかはわかりません。ただし、行列式とか逆行列とかは、数式で解くとは限りません。というか、計算機用のアルゴリズムが必要です。

線形代数自体をプログラムで解くときに、一番おもしろかったのがこれです。

行列を解いていくと、そのまま立体の回転とか、画像の変換とかにも使えることが分かります。多変量分析もそうですよね。このあたり、それぞれ別々にツールがあるわけですが、根っこは行列で解くところにあるので相互に別の分野のロジックを持ってくることも可能です。なので、線形代数の特徴を知っておくと応用範囲が広いかな…ってのを今頃知った次第です。

わき道にそれますが、行列の次元の話ついで、超弦理論の本を読みました。非常にわかりやすくて数式がほとんど(3箇所だけ?)出てきません。なぜ超弦理論が10次元(あるいは11次元)なのかが明快に説明されています。超弦(超ひも)論を知ったのは20年前なのですが、それ以降すごい発展を遂げていたんですね。

さて、ここにきてやって F# の本を買いました。ぽちぽちと Kindel で Programing F# 3.0 を読んでいたのですが、手元におくために日本語の本も一冊。

F# で必要な要素を絞り込んでいるので(配列の扱いとループぐらいか使わない)、WEBなどで大丈夫かと思ったのですが、やっぱり拾い読み用に手元に置きたいかなと。行列を扱う場合、2次元配列/Array2Dで扱うか、F# MathProvider にある matrix を使うかと迷うところです。実は、数独の問題を解いたときに知ったのですが、Array2D の場合は、Aij は、A.[j,i]となって x y が逆なんですよね。これは C 言語の二重配列と同じので当たり前といえば当たり前なのですが、数式からそのままプログラムコードに落とすときに間違いやすいところです。matrix のほうは、A.[i,j] のように x y の順になっているので、こっちのほうがいいです。

あと、数式の場合は 1 始まりなのですが、プログラムの場合は 0 始まりになります。ループを使うとき(maxのとき)に気を付ければいいのですが、その脳内変換が面倒ですね。なので、できるだけループを使わず、再帰を使うのが F# らしいかと思っています…が、matrix には Seq 等が使えない(Arrray2D にも使えない)ので、このあたり適当な拡張が必要かなと。

で、現在のところはこれです。

具体的に Visual Basic 2008 を使って有限要素法のソフトを作っていきます。元ネタは Fortran だそうで、それらしいコードなのですが、グラフィック部分が .NET なんですよね。全部実装すると、pre, solver, post の3種類が揃うのでワンセットでかなりのことができそうです。今後は、この VBのコードを F# にコンバートしていこうと思ってます。

カテゴリー: F#, FEM | 有限要素法をF#で実現しようという続きメモ(渡航編) はコメントを受け付けていません

[WPF] 最初に開くXAMLを切り替える

備忘録的に、メモ。
WPFアプリで最初に開く画面は、App.xaml の StartupUri 属性にしてあるのですが、これをコンパイル時に切り替えます。たとえば、DEBUG 時に「MenuWindow.xaml」を開いて RELEASE時に「LoginWindow.xaml」を開くというパターンです。デバッグ時にはログイン画面が面倒だから、飛ばしてしまってメニュー画面を開きたいときに便利です。という自分。

/// <summary>
/// App.xaml の相互作用ロジック
/// </summary>
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
#if DEBUG
        this.StartupUri = new Uri("MenuWindow.xaml", UriKind.Relative);
#endif
        base.OnStartup(e);
    }
}

OnStartup イベントをオーバーライドして、StartUri プロパティを書き換えます。この場合は、DEBUG 時には「MenuWindow.xaml」が使われて、RELEASE 時には App.xaml に指定されているページが開かれるというパターンです。
設定ファイルを読み込んで、トップ画面を開くときにも使えるかも。

カテゴリー: WPF | [WPF] 最初に開くXAMLを切り替える はコメントを受け付けていません

[F#] 数独をF#で作る

数独をF#で解く – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5304

の続き。二次元トラスは別途「Visual Basicでわかる やさしい有限要素法の基礎」が届いたので、これを見てから。この本、VB6 なのかと思ったら、VB2008 で書かれた新しい本でした。これだったら、結構流用がきくかも。元ネタは Fortran らしいのですが、グラフィックな部分とか .NET で書かれているので助かる。XAMLのPATHあたりに直すと結構いいかもという感じですね。

さて、

数独の問題を作るほうは、

  1. 矛盾なくNxNを敷き詰める。
  2. ランダムに1個空白にする。
  3. 解が1つしかないことを確認する。再び2へ

を地道に実装したのが次です。やっぱり、再帰のところがうまく作れなくて、excepiton で抜けていますが、これはいずれ修正する…かも。

open System

let bsize = 3
let size = bsize * bsize
let size2 = size * size
let pazzle = Array2D.zeroCreate<int> size size

// ルールにマッチする数を取り出す
let rec rule (m:int[,]) x y v (lst:int list) =
    // 横一列をチェック
    let rule_x (m:int[,]) x y v =
        [|for i in 0..size-1 -> m.[y,i]|] |> Array.exists (fun t -> t = v)
    // 縦一列をチェック
    let rule_y (m:int[,]) x y v =
        [|for i in 0..size-1 -> m.[i,x]|] |> Array.exists (fun t -> t = v)
    // boxをチェック
    let rule_box (m:int[,]) x y v = 
        let x' = x/bsize*bsize
        let y' = y/bsize*bsize
        [| for i in 0..bsize-1 do
            for j in 0..bsize-1 -> m.[y'+j,x'+i] |] |> Array.exists (fun t -> t = v)

    if v = 0 then 
        []
    elif rule_x m x y v = false &&
        rule_y m x y v = false &&
        rule_box m x y v = false then
        v::rule m x y (v-1) lst
    else
        rule m x y (v-1) lst

// ランダムに1つ選ぶ
let rnd = new Random()

let rec remove i (l:'a list) =
     match i,l with
     | 0, x::xs -> xs
     | i, x::xs -> x::remove (i-1) xs
     | i, [] -> failwith "index out of range"

let rec shaffle (l:'a list) =
    match l with
    | x::[] -> [x]
    | x::xs -> 
        let i = rnd.Next(l.Length)
        let l' = remove i l
        l.[i]::shaffle l'
    | [] -> []

let rec quest (m:int[,]) x y =
    // printfn "%d %d" x y
    // printfn "%A" m
    if x = 9 && y = 8 then
        // 完成
        printfn "success."
        printfn "%A" m
        raise (new Exception("ok"))
    elif x = 9 then
        quest m 0 (y+1) 
    else
        let lst = rule m x y size []
        if lst.Length > 0 then
            let lst' = shaffle lst
            for i in 0..lst'.Length-1 do
                let v = lst'.[i]
                let m' = Array2D.copy m
                m'.[y,x] <- v
                quest m' (x+1) y 
        
let Quest (m:int&#91;,&#93;) =
    try 
        quest m 0 0
    with
        | _ -> printfn "ok."

// 数字の敷き詰めを作成
Quest pazzle

ごちゃごちゃしていますが、1番の数の敷き詰めを作っているところです。左上から順番に番号をいれて、ルールに沿って(縦/横/ボックス)候補の数を配置しています。配置するときにランダムに数値を選ばせるために、shaffle を使っています。単純に数え上げのところは、この部分はいらないのですが、それだと同じ問題になってしまうし。
3×3の場合には、結構なスピードで敷き詰めができあがります…が、4×4にすると途端に遅くなりますね。1分程度で終わる場合もあれば、30分やってやっと終わる場合もあります。このあたり、最適化の山を登っている感じ。達成しない準最適化の山に登ってしまうと一度降りるのに時間がかかります。将棋の枝切りとかこのあたりのロジックなのかも。そうそう、やねうらおさんの「探索の深さが強さのイコールとはならない」というのは、このあたりの話で、完全に探索ができない場合には「探索自体の深さ」は「強さ」=正解そのものとは違っていて、さらに将棋の指し手が極めて少ない(2,3手とか)のであれば、その探索の深さ(指し手の多さ)は、強さに比例しない…だろう、ってことだと思います。まあ、実装できるのがすごいんですが。なんとなく想像で。

一旦敷き詰めができた配列をコピーして、今度は問題を作っていきます。

let ans' = 
    [[7; 3; 1; 2; 6; 4; 9; 8; 5]
     [9; 4; 2; 5; 3; 8; 7; 6; 1]
     [8; 6; 5; 7; 1; 9; 3; 2; 4]
     [2; 7; 9; 8; 5; 3; 4; 1; 6]
     [6; 5; 4; 9; 7; 1; 8; 3; 2]
     [1; 8; 3; 6; 4; 2; 5; 7; 9]
     [5; 2; 8; 3; 9; 6; 1; 4; 7]
     [4; 9; 6; 1; 8; 7; 2; 5; 3]
     [3; 1; 7; 4; 2; 5; 6; 9; 8]]
let A' = Array2D.init 9 9 (fun i j -> ans'.[i].[j])

let mutable scnt = 0

let rec solve (m:int[,]) x y = 
    // printfn "%d %d" x y
    if x = 9 && y = 8 then
        // 完成
        scnt <- scnt + 1
    elif x = 9 then
        solve m 0 (y+1)
    elif m.&#91;x,y&#93; <> 0 then
        solve m (x+1) y 
    else 
        // 縦/横/boxをチェック
        let mutable v = [|0..9|]
        for i in 0..8 do v.[m.[i,y]] <- 0
        for i in 0..8 do v.&#91;m.&#91;x,i&#93;&#93; <- 0
        let x0 = x/3*3
        let y0 = y/3*3
        for i in 0..2 do
            for j in 0..2 do
                v.&#91;m.&#91;x0+i,y0+j&#93;&#93; <- 0

        let m' = Array2D.copy m
        for i in 0..9 do
            if v.&#91;i&#93; <> 0 then
                m'.[x,y] <- v.&#91;i&#93;
                solve m' (x+1) y

let make_pazzle (A0:int&#91;,&#93;) =
    let A = Array2D.copy A0 
    
    // (x,y)をランダムに決める
    let slst = 
        shaffle &#91;for y in 0..size-1 do
                    for x in 0..size-1 -> (x,y) ]
    // 一つずつ取り出して、解法が2以上になった時にやめる
    for i in 0..size2-1 do
        printfn "%d" i
        printfn "%A" A'
        let A' = Array2D.copy A
        let (x,y) = slst.[i]

        A.[x,y] <- 0
        scnt <- 0
        solve A 0 0 
        if scnt > 1 then
            printfn "%A" A'
            raise (new Exception("ok"))

make_pazzle A'

問題の手順も簡単で、ランダムに1個空白(0にする)して、それをいちいち解いていきます。このとき、解法が2つ以上あれば、そこでストップしているだけです。これも非常に(コンピュータにとって)手間がかかる方法なのですが、なんか他に思いつかなかったので、これで。
空白にするコマをランダムに決めてしまうので、いわゆる数独の難しさを考慮していません。なので、これも低い最適化の山に登ってしまうと、途端につまらない≒空白の数の少ない問題を作成してしまいます。このあたりは、一定量の空白になるまで問題作成を繰り返せばいいんでしょうが、それだといつまでやっても終わらないって感じになりそうです。これも適当な足切りが必要でしょうね。

同じものをC#で書いたらどうなるんだろう?という疑問もありますが、このぐらいだと多分似た感じで書けそうです。
F#で書いてみてわかったんですが、配列を

F(x+1) = F(x) + a

の問題で書き直すとうまくいきます…つーか、逆な話で、この形の数式が出てくる場合は F# の再帰関数を使うと便利なわけです。実はこの形って、フィードバックそのもなので、F(1)の結果がF(2)に影響して、さらにF(2)の結果がF(3)に影響して、最終的にF(n)に影響する≒結果が出る、というパターンですね。F(F(F(F(…)))) な感じ。これを再帰の終端が、N から始まって 0 にして終わるのが F# の再帰関数のコツ(F#に限らないけど)、通常の for ループの場合は、0 から始まって max で終わるけど、再帰関数の場合は max を渡してやって減算していって 0 の時に終了、ってやるとうまくいきます…ってのは何処かにあるだろうか。

たとえば、フィボナッチ数列は

let rec fibo n =
    match n with
    | 0 -> 1
    | 1 -> 1
    | x -> fibo(x-1) + fibo(x-2) 

let fibonacci n =
    for i in 0..n do
        let ans = fibo i
        printf "%d," ans

fibonacci 20

とできるわけで、計算量はさておき、数式のシンプルさをそのまま表せるのが便利ですよね。

カテゴリー: F# | [F#] 数独をF#で作る はコメントを受け付けていません

[F#] 数独をF#で解く

ふと、数独(sudoku)をF#で解く…で検索すると、

Sudoku solver in F#
http://www.ffconsultancy.com/dotnet/fsharp/sudoku/

があって、コードがええとよくわからないので、自作しました。
最初は素朴な方法を使っていて、左上から順番に数を入れて計算して、数を入れて計算してという方法をやっていたのですがいっこうに終わらないで、ちょっと考え直し。元ソースをよく見ると、左上からチェックして入れた後は、右に進むという方法をとっているので、ああ、これで十分かということで。

アルゴリズムとして、

  1. 左上(0,0)から始める。
  2. 0以外だったら、右へ進む。
  3. 1 右端に達したら次の行のはじめへ
  4. 0だったら、
  5. 横一列を調べる
  6. 縦一列を調べる
  7. 3×3のボックスを調べる
  8. 数が埋まれば、埋めて右に進んで再帰

な感じで進めればOK。そういえば、行列計算をちまちま自作していたときに思ったのですが、このあたり「アルゴリズム」≒計算機が得意とする手順は、がぜん生きていますね。普段、リスト構造とかツリー構造とかオブジェクトの構造とか、既存の.NETライブラリで賄えるものが多かったのですが、行列計算、線形代数の分野になると、高速化とか誤差も含めて数学的な解法とは違った古式ゆかしき「アルゴリズム」が生きている分野なのだな、と再確認しています。

さて、このアルゴリズムを直に実装したのが以下です。

let rec solve (m:int[,]) x y = 
    printfn "%d %d" x y
    if x = 9 && y = 8 then
        // 完成
        printfn "%A" m
        exit 0
    elif x = 9 then
        solve m 0 (y+1)
    elif m.[x,y] <> 0 then
        solve m (x+1) y 
    else 
        // 縦/横/boxをチェック
        let mutable v = [|0..9|]
        for i in 0..8 do v.[m.[i,y]] <- 0
        for i in 0..8 do v.[m.[x,i]] <- 0
        let x0 = x/3*3
        let y0 = y/3*3
        for i in 0..2 do
            for j in 0..2 do
                v.[m.[x0+i,y0+j]] <- 0

        let m' = Array2D.copy m
        for i in 0..9 do
            if v.[i] <> 0 then
                m'.[x,y] <- v.[i]
                solve m' (x+1) y

for ループの部分が「えー」とか、match を使ったほうがいいよとか、fold の使い方を知らないとか色々あるわけですが、素直に書いたのでこんな感じ。
ただし、この再帰関数 solve は元に帰ってこなくて、完成したとき(9,8 のとき)には、無理矢理 exit してプログラムを止めています。これが再帰関数を戻すと再び別の解を探し出すので無駄なんですよね。元ネタをみると、例外を発生させているので、それでもいいのですが。末尾再帰をしているわけではないので、スタックを食うのは仕方がなく(バックトラック法だし)、ただし、ツリー構造を探索していく中で各ノードはパラレルに動かせるので探索自体は深くなりそうなときは適当にスレッド化することも可能なのかなと。このあたりは、巨大な行列計算と似た感じになると思っています。

まあ、9×9の数独ならば1秒も経たないで解けてしまうのでスレッド化する意味はありません。が、10倍ぐらいの、100×100(数字は1から100まで)の数独ならばどうなんだろうというのがあります。ウィキペディアを見ると、

Mathematics of Sudoku – Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Mathematics_of_Sudoku

ボックス(bands)が5×5の場合の計算はあるのですが、それ以上はどうなるかわからんという状態みたいです。まあ、パズルとして人間が解ける限界を超えているし、パズルとしての面白味もないので解く意味はないのですが、コンピュータに解かせるときの最適化具合を見るのにはいいかも、と。

数独を解かせるためには、配列の配列を渡してやって、Array2D に直します。

// Input puzzle
let example = [|[|0;0;0;0;6;0;4;0;0|];
                [|0;5;0;0;0;3;6;0;0|];
                [|1;0;0;0;0;5;0;0;0|];
                [|0;4;1;0;0;0;0;0;0|];
                [|0;9;0;0;0;0;0;2;0|];
                [|5;0;2;0;0;0;3;4;0|];
                [|3;0;0;7;0;0;0;0;0|];
                [|0;0;6;5;0;0;0;9;0|];
                [|0;0;4;0;1;0;0;0;0|]|]

let pazzle = Array2D.init 9 9 (fun i j -> example.[i].[j])

solve pazzle 0 0 

答えはこんな感じ

[[2; 3; 7; 1; 6; 8; 4; 5; 9]
 [4; 5; 8; 9; 7; 3; 6; 1; 2]
 [1; 6; 9; 2; 4; 5; 7; 8; 3]
 [6; 4; 1; 3; 5; 2; 9; 7; 8]
 [7; 9; 3; 4; 8; 1; 5; 2; 6]
 [5; 8; 2; 6; 9; 7; 3; 4; 1]
 [3; 1; 5; 7; 2; 9; 8; 6; 4]
 [8; 2; 6; 5; 3; 4; 1; 9; 7]
 [9; 7; 4; 8; 1; 6; 2; 3; 5]]

実は、元ネタの数独は複数回答あって、問題としてよろしくないんですよね。
それはさておき、検算をするコードが以下

let ans = 
    [[2; 3; 7; 1; 6; 8; 4; 5; 9]
     [4; 5; 8; 9; 7; 3; 6; 1; 2]
     [1; 6; 9; 2; 4; 5; 7; 8; 3]
     [6; 4; 1; 3; 5; 2; 9; 7; 8]
     [7; 9; 3; 4; 8; 1; 5; 2; 6]
     [5; 8; 2; 6; 9; 7; 3; 4; 1]
     [3; 1; 5; 7; 2; 9; 8; 6; 4]
     [8; 2; 6; 5; 3; 4; 1; 9; 7]
     [9; 7; 4; 8; 1; 6; 2; 3; 5]]

let A = Array2D.init 9 9 (fun i j -> ans.[i].[j])

let check (m:int[,]) =
    
    for y in 0..8 do
        let mutable v = [|0..9|]
        for i in 0..8 do
            v.[m.[i,y]] <- 0
        if Array.sum v <> 0 then
            printfn "error y=%d" y

    for x in 0..8 do
        let mutable v = [|0..9|]
        for i in 0..8 do
            v.[m.[x,i]] <- 0
        if Array.sum v <> 0 then
            printfn "error x=%d" x

    for x in 0..3..8 do
        for y in 0..3..8 do
            let mutable v = [|0..9|]
            for i in 0..2 do
                for j in 0..2 do
                    v.[m.[x+i,y+j]] <- 0
            if Array.sum v <> 0 then
                printfn "error x=%d y=%d" x y
            
    printfn "OK"

# ああ、そうか。MSTest を使ってもいいんだっけ。どっかにサンプルページがあったはずなので後ほど。

で、bounds が 10×10の数独(全体が100×100)は、どうなるのかなと考えてみたものの…そんな数独の問題はどこにもないわけで、ということは自分で問題を作らないとダメなのですよ。どうせならば、解がひとつしかないパターンの自動作成をしたいわけで、ここは思案のしどころ。ざっと、問題作成の手順を書き下すと、

  1. 矛盾なくNxNを敷き詰める。
  2. ランダムに1個空白にする。
  3. 解が1つしかないことを確認する。再び2へ

な感じかな。3の部分を最適化したいところですが、基本は先の解法コードを拡張するだけなので、後は計算時間の問題かな。

カテゴリー: F# | [F#] 数独をF#で解く はコメントを受け付けていません

[F#] F#らしくLU分解を書き直してみる

LU分解を F# らしく書き直してみます。
せっかくの F# なのに for ループを使っているのがもったいないし(?)、数式のΣから外れています。ここを sum 関数を使って書き直したいのですが… matrix に対しての Seq.sum とかは要素全体に掛け算してしまうので無理(だと思う)なので、

C#er のためのやさしい再帰入門: いげ太のブログ
http://igeta.cocolog-nifty.com/blog/2011/02/tailcall.html#more

を参考にして、再帰関数化しています。

/// LU分解
let LU2 ( A : matrix ) =
let n = A.NumCols
let L = Matrix.identity n
let U = Matrix.zero n n

// sum(k=0,n) L(i,k)*U(k,j) の計算を関数化
// 余計わかりづらいか?
let sum i j k =
let rec f i j k acc =
if k < 0 then 0.0 elif k > 0 then f i j (k-1) (acc + L.[i,k]*U.[k,j])
else L.[i,k]*U.[k,j]
f i j k 0.0

for j in 0..n-1 do
// Uを計算
for i in 0..j do
U.[i,j] <- A.[i,j] - sum i j (i-1) // Lを計算 for i in j+1..n-1 do L.[i,j] <- (A.[i,j] - sum i j (j-1))/U.[j,j] // 結果を返す (L,U) [/code]

LとUを計算するところで、ΣL.[i,k]*U.[k.j] が同じなので、括りだしてみたのですが、余計わかりづらいかも。最後の for ループに適用するとよいのかもしれません。まあ、この程度の簡単なものならば再帰を使うとかえってややこしいという感じですかね。

ならば、for の二重ループの中で、sum を定義して、ちょっと簡単にしてみたのが次のコードです。

/// LU分解
let LU3 ( A : matrix ) =
let n = A.NumCols
let L = Matrix.identity n
let U = Matrix.zero n n

// sum(k=0,n) L(i,k)*U(k,j) の計算を関数化
// 普通にforループで合計を計算する

for j in 0..n-1 do
// Uを計算
for i in 0..n-1 do
let sum max =
let mutable s = 0.0
for k in 0..max do
s <- s + L.[i,k]*U.[k,j] s if i <= j then // Uを計算 U.[i,j] <- A.[i,j] - sum (i-1) else // Lを計算 L.[i,j] <- (A.[i,j] - sum (j-1))/U.[j,j] // 結果を返す (L,U) [/code]

ループの中で関数が定義できるから、便利ってのがありますね。ループ変数のi,jもそのまま使えるので、sum 関数の引数が少なくて済みます。

お次は、このLU分解を使って、二次元トラスを具体的に解いてみます。

カテゴリー: F# | [F#] F#らしくLU分解を書き直してみる はコメントを受け付けていません

[F#] LU分解を作る

旧タイプなので、連立一次方程式を解くときに逆行列を使って解けばOK…と頭から思い込んでいたのですが、Gaussの消去法を使うと逆行列は出てこなくて「?」となるわけです。上三角化を使うのだから、LU分解でいいんですよね。

LU分解で行列式と逆行列の計算 その3: メモブログ
http://sssiii.seesaa.net/article/379560261.html

のところから、

連立1次方程式 I
http://nalab.mind.meiji.ac.jp/~mk/labo/text/linear-eq-1.pdf

のPDFを読んで、なるほど、LU分解で十分ですね。ってことで自前で実装してみます。

4 LU分解
http://akita-nct.jp/yamamoto/lecture/2004/5E/linear_equations/text/html/node4.html#eq:LU____________

を参考にして式を流用します。最初、手順がうまくわからなくて、自分で 4×4 の行列を作って手計算をした後に、数式に直して、元の式と一致することを確認。なるほど、行単位じゃなくて「列単位」で計算するので、計算する順番は、β11,α21,α31,α41 で計算した後に、β12,β22,α32,α42 と計算していくんですね。もう一度読み直したらそう書いてあるし…

出来上がったコードがこんな感じ。

/// LU分解
let LU ( A : matrix ) =
    let n = A.NumCols
    let L = Matrix.identity n
    let U = Matrix.zero n n
    
    for j in 0..n-1 do
        // Uを計算
        for i in 0..j do
            U.[i,j] <- A.&#91;i,j&#93;
            for k in 0..i-1 do
                U.&#91;i,j&#93; <- U.&#91;i,j&#93; - L.&#91;i,k&#93;*U.&#91;k,j&#93;
        // Lを計算
        for i in j+1..n-1 do
            L.&#91;i,j&#93; <- A.&#91;i,j&#93;
            for k in 0..j-1 do
                L.&#91;i,j&#93; <- L.&#91;i,j&#93; - L.&#91;i,k&#93;*U.&#91;k,j&#93;
            L.&#91;i,j&#93; <- L.&#91;i,j&#93; / U.&#91;j,j&#93;
    // 結果を返す
    (L,U)
&#91;/code&#93;
<p>
次のように行列を設定しておくと
</p>
[code lang="csharp"]
let A = matrix [[1.0;2.0;1.0];
                [2.0;1.0;0.0];
                [1.0;1.0;2.0]]

LU A

こんな感じで、L と U にして返してくれます。

val it : matrix * matrix =
  (matrix [[1.0; 0.0; 0.0]
           [2.0; 1.0; 0.0]
           [1.0; 0.3333333333; 1.0]], matrix [[1.0; 2.0; 1.0]
                                              [0.0; -3.0; -2.0]
                                              [0.0; 0.0; 1.666666667]])

先のページにも書いてあるのですが、Ujj で割ってしまうので、ピボット選択は必須…なのか?(ちょっとよくわからず)。浮動小数点だったら大丈夫な気もするのですが、これはあとで確認してみます。まあ、割り算のところにチェックを入れるのは数値計算の基本なので、なるべく割り算で誤差が出ないようにする方向でよいかと。

ちなみに F# MathProvider を使うと

let (p,L,U) = MathProvider.LinearAlgebra.lu A

として、次なる結果を得られます。

val p : (int -> int)
val U : matrix = matrix [[2.0; 1.0; 0.0]
                         [0.0; 1.5; 1.0]
                         [0.0; 0.0; 1.666666667]]
val L : matrix = matrix [[1.0; 0.0; 0.0]
                         [0.5; 1.0; 0.0]
                         [0.5; 0.3333333333; 1.0]]

MathProvider の LU 分解のコードはもっとシンプルで、こんな感じになっています。うまく交互に計算すると L のほうはほとんど計算せずに済みみたいです。行列数が多くなると swapR のパフォーマンスが問題になりますが、有限要素法以外だったら大丈夫かと。

for i=0 to nA-2 do
let mutable maxi = i // Find the biggest pivot element.
for k=i+1 to nA-1 do
if abs(U.[maxi,i]) < abs(U.[k,i]) then maxi <- k done //let maxi = maxIndex (i+1) (nA-1) (fun k -> abs(U.[k,i]))

if maxi <> i then
swapR U i maxi // Swap rows if needed.
swapR L i maxi // REVIEW can be made more performant.
let t = P.[i]
P.[i] <- P.[maxi] P.[maxi] <- t for j=i+1 to nA-1 do L.[j,i] <- U.[j,i] / U.[i,i] for k=i+1 to nA-1 do U.[j,k] <- U.[j,k] - L.[j,i] * U.[i,k] done U.[j,i] <- 0.0 done done [/code]

sum のところは、もっと F# らしく書き換えられないかな、と思案中です。コードが短くなるしΣを使ったほうが、数式を同じになるので。

カテゴリー: F# | [F#] LU分解を作る はコメントを受け付けていません