LaTeX+TikZでモンテカルロ積分の可視化
この記事は
TeX & LaTeX Advent Calendar 2016 - Adventar
の13日目の記事です.
はじめに
LaTeXでモンテカルロ積分を行うプログラムを作りました.Tikzで可視化もしました.
何を作ったのか
モンテカルロ積分によって円周率の近似値を求めるプログラムです. 左にモンテカルロ積分を可視化した図,右にプロットした点の数や求まった円周率の近似解を表示するプログラムです.
モンテカルロ積分について
モンテカルロ積分
モンテカルロ積分とはモンテカルロ法によって定積分の近似値を求めるアルゴリズムです. モンテカルロ法は疑似乱数を用いてシミュレーションや数値計算を行う手法の総称です. つまりモンテカルロ積分は疑似乱数を用いて定積分の近似値を求めるアルゴリズムです.
モンテカルロ積分で円周率を求めるには
積分値にが含まれるような関数の積分値の近似値を求めます. 今回は以下の定積分を解いて円周率を解きます.
この定積分の解は
ですので定積分の近似解を4倍すれば円周率の近似値が得られそうです.
モンテカルロ積分の手順
今回の関数は の積分を0から1まで求めるので次のようなイメージになります.
この内部の面積をとします.
この関数に囲まれた部分の面積を求めます.次にこの正方形内部にランダムに点を打ちます. 次の図はプロット点数1000で描画した時のイメージです.青の点が内部の点,赤の点が外部の点です.
全体の点数と青の点数の比と正方形の面積と求めたい面積の比が近似することを利用して比の方程式での近似値を求めます.
の本当の値はなので円周率の近似値は
となります.
TeX+TikZでモンテカルロ積分の実装
変数やマクロを定義する
積分範囲はTeXのマクロに定義し,赤青の点数などカウントが必要なものはLaTeXの変数に代入してしまいます.
\def\startpoint{0} \def\endpoint{1} \newcounter{redplot} \newcounter{blueplot} \setcounter{redplot}{0} \setcounter{blueplot}{0}
プロット点数は標準入力で
プロット点数は入力できるようにしました.
\newcount\input \message{プロット点数を入力してください} \read-1 to \input
正方形や関数を描く
Tikzで正方形や関数を描画します.
% 枠を描画 \draw (\startpoint,\startpoint) rectangle (\endpoint,\endpoint); % 関数を描画 \draw plot[domain=\startpoint:\endpoint,samples=100] (\x,{func(\x)});
これで準備は整いました.後は点をプロットして円周率の近似値を求めます.
fpパッケージを使いTeXで固定小数点演算を行う
0から1の積分なのでランダムにプロットする点は必ず小数になります. しかし,TeXは標準で四則演算くらいはできますが小数を扱うことができません.なので fpパッケージと呼ばれるTeXで固定小数演算を行うパッケージを使います. 私の実行環境であるTeXLive2016では標準で入っていました. fpは小数演算の四則演算だけでなく指数計算や対数計算など様々な計算をすることが可能です. 詳しくは以下の方の記事が分かりやすいです.
ランダムに点をプロットする
用意できたら点をランダムにプロットしていきます. fpパッケージには乱数を発生するマクロもあります. まずはプロットする点をランダムに決めていきます.
% 今日,0時から何分経過したかでシードを決定 \FPseed = \time \begin{document} . . . % ランダムにx座標を決める \FPrandom{\randomX} % f(randomX)を求める \FPpow{\randomFuncX}{\randomX}{2} % randomX^2 \FPsub{\randomFuncX}{1}{\randomFuncX} % 1 - randomX^2 \FProot{\randomFuncX}{\randomFuncX}{2} % √1 - randomX^2 % randomXをfp以外で評価できる形にする \FPeval\plotX{\randomX} % f(randomX)をfp以外で評価できる形にする \FPeval\evalX{\randomFuncX} % ランダムにy座標を決める \FPrandom{\randomY} % randomYをfp以外で評価できる形にする \FPeval\plotY{\randomY}
本当は以下のようにも書けるのですがオーバーフローを引き起こすので上のような遠回りをしています.
\FPeval\plotX{random()} \FPeval\evalX{root(1 - \plotX^2,2)} \FPeval\plotY{random()}
点が関数の内側かどうかを判定する
ある点(X, Y)が関数の内側にあるかどうかは以下の不等式で判定可能です.
これをTikZの条件式で判定します.
% 関数の求める範囲の外側の場合は赤,内側の場合は青で点をプロット \pgfmathparse{\evalX < \plotY ? "red" : "blue"} % blueかredを\colorに定義 \edef\color{\pgfmathresult}
次に定義した\colorを元に青の点と赤の点をカウントします.
% 赤の点と青の点を数える \ifthenelse{\equal{\color}{red}}{\stepcounter{redplot}}{\stepcounter{blueplot}}
点をプロット
Tikzのコマンドで点をプロットします.
% 点をプロットする \filldraw[fill=\color, ultra thin] (\plotX, \plotY) circle [radius=0.01];
入力分ループを回す
それではここまでの処理をTikZのforechで回していきます.
\foreach \i in {1,...,\input} { % ランダムにx座標を決める \FPrandom{\randomX} \FPpow{\randomFuncX}{\randomX}{2} \FPsub{\randomFuncX}{1}{\randomFuncX} \FProot{\randomFuncX}{\randomFuncX}{2} \FPeval\plotX{\randomX} \FPeval\evalX{\randomFuncX} % ランダムにy座標を決める \FPrandom{\randomY} \FPeval\plotY{\randomY} % 関数の求める範囲の外側の場合は赤,内側の場合は青で点をプロット \pgfmathparse{\plotY > \evalX ? "blue" : "red"} \edef\color{\pgfmathresult} % 赤の点と青の点を数える \ifthenelse{\equal{\color}{red}}{\stepcounter{redplot}}{\stepcounter{blueplot}} % 点をプロットする \filldraw[fill=\color, ultra thin] (\plotX, \plotY) circle [radius=0.01]; }
コンパイル
それではコンパイルしていきます.私の環境ではplatex+dvipdfmxでコンパイルしています. コンパイルしたら「プロット点数を入力してください」と表示されるので 1000とか入力します. そうするとコンパイルされてdviが出てくるのでそれもpdfに変換して表示してみると 以下のような図が出ると思います.
近似値なども表示する
プロット点数や近似値も表示させます. 全体の面積を求めます.
% 枠内全体の面積を変数に代入 % 四捨五入して整数にしている \FPeval\overallS{round((\endpoint - \startpoint)^2,0)}
比の方程式を解いて,4倍して円周率の近似値を求めます. 小数点以下が多いので第9位まで丸めます.
%\arabic{blueplot} ... 青の点数 %\overallS ... 正方形の面積 %\input ... 全体のプロット点数 \FPeval\result{round(4 * \arabic{blueplot} * \overallS / \input,9)}
さて,それでは全て表示させます.
\begin{tikzpicture} \draw (7,5) node {全体の点数 = \input}; \draw (7,4) node {青い点数 = \arabic{blueplot}}; \draw (7,3) node {赤い点数 = \arabic{redplot}}; \draw (7,2) node {全体的な面積 = \overallS}; \draw (7,1) node {$\displaystyle\int^1_0 4\sqrt{1-x^2}$ = \result}; \end{tikzpicture}
図の方は一辺が1で原寸大ですので近似値などの表示に対して図が小さすぎます. なので図と文字のtikzpicture環境を分け図の方をscalebox環境で拡大しています.
プロット点数1000でやってみると次のような図が表示されます.
様々なプロット点数でやってみました.
それにしても精度が悪いですね.この辺は要改善ですね.
ソースコード
TeXで強制改行コマンドの後に角括弧をつけるとエラーを吐く理由と対策について
はじめに
はまった上にこれについて書いてある記事なども見つからなかったので備忘録的に残します.
強制改行(\\)の後に角括弧([])を使うと謎のエラーが
LaTeXを書いている時に
[1] http://hoge.hoge.com \\ [2] http://fuga.fuga.com
の部分でエラーが発生しました. しかも,エラーの内容は???でした.
! Illegal unit of measure (pt inserted).
このエラーはTeXで定義されていない寸法単位が使われている時に出るエラーです.
寸法単位なんかどこにも出てきていないしエラーとは関係なさそうに見えます.
この症状は環境が異なっても起こりました.自分は以下の環境で試しました.
- TeXLive 2015
- e-pTeX + dvipdfmx
- LuaTeX
問題は強制改行(\\)のオプション引数
強制改行もTeXのマクロの一つです.よく調べてみると強制改行はオプション引数を取るみたいです.
\\[10pt]
などとすると行間を10pt空けて改行してくれるみたいです.
つまり強制改行のオプション引数は行間の幅を指定する為のもので
強制改行後の角括弧はオプション引数としての括弧として認識されてしまったということです.
角括弧の中の数字には寸法単位がついていない為先述したような謎のエラーが出たという訳です.
なぜ,強制改行(\\)と角括弧([])は違う行にあるのにこのような症状が出るのか
これにはTeXの組版としてのルールが絡んできます. 今回影響したTeXの性質として次のような性質があります.
- (空行以外の)改行文字は空白文字になる
- 行頭と行末にある空白文字・タブは全て無視される
これにより強制改行と角括弧の間の改行文字が無視されたという訳です.
解決策
これを解決するには強制改行の後に\relaxというコマンドを入れます. つまり以下のようにすれば解決します.
[1] http://hoge.hoge.com \\ \relax [2] http://fuga.fuga.com
\relaxは「なにもしない」コマンドです.しかし,これが大いに役に立ちます.
\relaxはオプション引数を取らないので角括弧がオプション引数としての括弧として認識されないからです.
最後に
今回はこうなる理由が知りたかったので合わせて解決も書きましたが
本当は強制改行は極力使わない方が良いと思います.
今回の解決策を施したところで1行目が段落の始まりで字下げを行っているので2行目と行頭が揃っていません.
字下げを行わないコマンドもありますがそれならverbatim環境などを使った方がまだいいと思います.
一番良いのは参考文献の記述を人力でやるのではなくthebibliography環境などを使うことだと思います.
LaTeXの表を生成できるサイトTables Generator
はじめに
LaTeXで表を作成するのはとても面倒くさいですよね.
表の部分だけExcelを使いたいと思う人も多いのではないでしょうか.
そこでこんなサイトを見つけました.
このサイトはマウスで作成した表からLaTeXのコードを生成するWebアプリです.
使ってみる
下図にある赤枠の表を編集して青枠の「Generate」ボタンを押すと緑枠内にLaTeXのコードを生成してくれます.
試しに生成されたコードをコピペしてPDFを生成してみます.
赤枠の表と同じような表ができあがりました.
表を作ってみる
次にサンプル以外の表を生成してみます. 今回は次の表を作っていきます.
最初にサンプルの表を消します.
次に作る表のサイズを決めます.今回は3x3の表を作ります.
そうすると,プレビューに空の表ができたと思います.これを編集していきます. 1行1列目から文字を入れていきます.表の編集したいマスをダブルクリックすると文字が打てます.
他のマスも同様に編集していきます.
次にマウスを表の上でドラッグしてマスを全選択します.
そして,図のマウスが指しているボタンを押すとすべてのマスに罫線が引かれます.
次に各マス内の文字列をセンタリングします.
全選択はされたままなので図のマウスで指しているボタンを押すと
全てのマス内の文字列がセンタリングされます.
これで完成です.後は「Generate」ボタンを押すとLaTeXのコードを吐き出してくれます.
以下のようなコードができれば完成です.
\begin{table}[] \centering \caption{My caption} \label{my-label} \begin{tabular}{|c|c|c|} \hline い & ろ & は \\ \hline に & ほ & へ \\ \hline と & ち & り \\ \hline \end{tabular} \end{table}
キャプションは自動で付きますので扱いは自由にしてください.
色をつけたり,更に複雑な表も作れる
この他にも更に複雑な表も作れます.
直感で操作できますのでいろいろ試してみてください.
LaTeXでは色をつけたりする際には追加のスタイルファイルが必要ですが,
Tables Generatorではコードを生成する時に必要なパッケージをコメントで案内してくれます.
Excelで作って読み込むこともできる
Excel,LibreOffice,CSVなど,別のデータから表を読み出すこともできます.
- Import CSV file... CSVファイルを読み表を生成
- Paste table data... Office系のソフトで作った表をコピペすることで表を生成
- From LaTeX code... LaTeXのソースコードから表を生成
- Save table... 編集途中の表を保存する
- Load table... 保存した編集途中の表を読み込む
また,ここでは紹介しませんでしたがLaTeXだけでなく,
HTMLやMarkdownのコードも生成することができます.
まとめ
マスが多い表ほど,このサイトは重宝すると思います.
時間的コストはOfficeで作るのとではそれほど変わらないと思います.
LaTeXで載せたプログラムソースがページを跨ぐ場合の対処法について
はじめに
私はとある大学に通う情報系の学生なのですが,レポートをLaTeXで書くことが多々あります.
情報系の演習ではレポートにプログラムのソースコードを載せることがあります.
しかし,ソースコードが長くて1ページに収まりきらないこともよくあります.
その時,LaTeXでははみ出した部分のソースを正常に表示してくれないことがあります.
はみ出した部分は自動的に改ページしてくれないのです.
そんな時の対処法をここでは環境,コマンド別に紹介していきます.
今回対象となる環境,コマンド
- itembox + verbatim
- listing
- minted
itembox + verbatim
私が1年生の時に受講していた演習においてレポートにソースを載せる際はこの方法でした.
最初からある環境のみを使用しているので簡単です.
しかし,ソースがページを跨ぐ場合ははみ出てしまい,次のページに表示してくれません.
他の学生は一旦itemboxを切って,次のページで新しいitemboxで囲んだりしたりしてました.
実はあるパッケージを導入すればページを跨いだ場合でも自動的にitemboxを改ページしてくれるのです.breakitembox
itemboxに代わる,breakitemboxという環境を使えばページを跨いだ場合でもきちんと対処してくれます.導入方法
- 以下のパッケージを全てダウンロードする.
- itembkbx.sty
- itembbox.sty
- emathC.sty
- jquote.sty
- eclbkbox.sty
- \usepackage{itembkbx}で読み込む.
- 使う
パッケージはCTANにはないのでgithubからダウンロードしましょう.
github.com
パッケージを全てダウンロードしたらそれらを全て読み込める状態にします.
texファイルと同じディレクトリに置く
breakitemboxを使いたいtexソースと同じディレクトリに先程ダウンロードしたパッケージを置きます.
$TEXMFHOMEに置く
$TEXMFHOMEの場所を把握します.
kpsewhich -var-value $TEXMFHOME
パスが返ってくるのでその中にtexディレクトリを作ります. デフォルトでは以下のようになっていると思います.
~/texmf/tex
その直下にパッケージを置きます. 置いたら次のコマンドで更新します.
mktexlsr [$TEMFHOMEが設定されているパス] # ex) mktexlsr ~/texmf
使い方
使い方はitemboxとさほど変わりません.
\begin{breakitembox}[l]{見出し} \begin{verbatim} #include<stdio.h> int main(void){ printf(Hello World!\n); return 0; } \end{verbatim} \end{breakitembox}
このようにすると以下のような結果が得られます.
行数が多いプログラムはファイルから読み込みましょう.
verbatimパッケージを読み込んでverbatiminputコマンドを使います.
\begin{breakitembox}[l]{見出し} \verbatiminput{test.c} \end{breakitembox}
また,ページを跨ぐ場合にはこんな感じになります.
listing
listingはbreakitemboxとは違いソースコードを載せる専用のパッケージです.
こちらはデフォルトでページを跨いだ時の処理をしてくれる便利なパッケージです.
使用するパッケージはlistings,jlistingの2つです.
listingsはtexlive2015の時点では最初から入っているようなのでダウンロードはする必要ありません.
ない人はCTANからダウンロードしましょう.
CTAN: Package listings
jlistingはlistingsに日本語対応させる為のパッケージなので入れておきましょう.こちらからダウンロードします.
Listings - MyTeXpert
ダウンロードしたら$TEXMFHOMEに入れて使ってみます.
使い方
まずはプリアンブル部にlistingの設定を書きます.
今回はlisting中心の記事ではないので以下をコピペして使ってください.
\usepackage{listings,jlisting} \usepackage{color} \lstset{% language={C}, basicstyle={\small},% identifierstyle={\small},% commentstyle={\small\itshape\color[rgb]{0,0.5,0}},% keywordstyle={\small\bfseries\color[rgb]{0,0,1}},% ndkeywordstyle={\small},% stringstyle={\small\ttfamily\color[rgb]{1,0,1}}, frame={tb}, breaklines=true, columns=[l]{fullflexible},% numbers=left,% xrightmargin=0zw,% xleftmargin=3zw,% numberstyle={\scriptsize},% stepnumber=1, numbersep=1zw,% lineskip=-0.5ex% }
それでは実際に使っていきます.
\begin{lstlisting}[caption=test,label=test] #include<stdio.h> int main(void){ printf("Hello World!\n"); return 0; } \end{lstlisting}
このように出力されます.
ページをまたいでも大丈夫です.
ソースコードをファイルから読む場合は以下のようにしましょう.
\lstinputlisting[caption=aaa,label=aaa]{test.c}
minted
このパッケージもまたソースコードを載せるためのパッケージです.
導入方法は過去の記事にあるのでそちらを参考にしてください.
muscle-keisuke.hatenablog.com
mintedもlisting同様ページを跨いだ時に適切な処理をしてくれます.
\begin{minted}{c} #include<stdio.h> int main(void){ printf("Hello World!\n"); return 0; } \end{minted}
出力は以下の通りです.
ページを跨ぐ時でもこのように適切に処理してくれます.
注意点
mintedは確かにデフォルトでページを跨ぐ処理をやってくれるのですが,ある条件下ではそれが働かなくなります.それは
- キャプションをつけるためにlisting環境を使った時,
- ソースコードを載せる部分の背景色を指定した時
です.
このいずれかの条件が入っている時出力は次のようになります.
ページ末尾にあるページ番号を超えてコードが出力されているのが分かります.
これを防ぐために次の環境,コマンドを使います.
- mdframed ...背景色が使えない代わりの枠組み
- captionof ...listing環境が使えない -> キャプションを付加できない よってその代わり
mdframed環境はmdframedパッケージで,captionofコマンドはcaptionパッケージで読み込みます.どちらもtexlive2015には標準で入っているはずですが,ない場合はCTANから
CTAN: Package mdframed
CTAN: Package caption
使い方は以下の通りです.
\definecolor{bg}{rgb}{0.95,0.95,0.95} \begin{mdframed}[ backgroundcolor=bg, topline=false, bottomline=false, leftline=false, rightline=false] \begin{minted}{c} #include<stdio.h> int main(void){ printf("Hello World!\n"); return 0; } \end{minted} \end{mdframed} \captionof{listing}{テストキャプション}
最初の行でdefinecolorコマンドを使っていますが,これは背景色を定義しています.definecolorコマンドはcolorパッケージの中ですので読み込むのを忘れずにしてください.
minted環境をmdframed環境で囲んでください.
mdframed環境のオプション引数ですが,
backgroundcolor=定義したもしくはデフォルトで定義されている色
で背景色を設定できます.
その後の引数は単純にmdframedの枠線を出力しない為の設定です.
(mintedでも枠線を定義できるので重複する)
下から二番目のところでcaptionofコマンドを使ってキャプションを設定しています.第二引数にキャプションの文を書きます.
第一引数のlistingという部分はキャプションの種類です.主に使うものを以下にまとめます.
- listing ...ソースコードなどを載せる時のキャプション
- figure ...グラフや図を載せる時のキャプション
- table ...表を載せる時のキャプション
という感じです.
captionofコマンドではなく通常のcaptionコマンドでつけたキャプションとの番号のずれはなくそこらへんはうまくやってくれます.
実行すると以下のように出力されます.
ページを跨いだ場合でも大丈夫です.
まとめ
itembox+verbatimを使って長いソースコードをぶつ切りにしてた人は
breakitembox+verbatiminputを使いましょう.
また,listingやmintedを使えばデフォルトでページを跨ぐ処理を適切に行います.
ただし,mintedでページを跨がない場合はmdframedとcaptionofを駆使して解決しましょう.
連立方程式を一番美しく書けるempheq
empheqとは?
- empheqは連立方程式を表現するためのスタイルファイル
- 特殊な記号を使ってもその通りに表示することが可能
- 他にもオプションがたくさんある
- 少なくとも自分の実行環境であるTexLive2014では標準で入っている
- ない人はCTANなどからダウンロードしてください
他のスタイルファイルとの違い
- 連立方程式を表現するスタイルファイルはarray,cases,empheqなどがある
array
array環境は式を2段組にして1つの式として扱います.元々は連立方程式を扱うための環境ではありません. array環境で連立方程式は次のように表現します.
\begin{align} \left\{ \begin{array}{ll} f(x) &= x^2+3x+1 \\ f'(x) &= 2x+3 \end{array} \right. \end{align}
align環境を使っているのでamsmathパッケージを入れる必要があります. ゴリ押し感が半端ないですね.\left{で左の大きい括弧を表現していますが右に括弧はいらないので\right.とやって無理やり消しています. 数式同士の間隔も狭いです.
cases
cases環境は同名で2つあります.
casesパッケージのnumcasesとsubnumcases
式番号の付け方でnumcasesとsubnumcasesは違います.
numcasesは連立してる式の一つ一つに(1),(2),(3)...
subnumcasesは(1a),(1b),(1c)...
と付けます.他は特に変わりません.なのでnumcases環境を基に説明していきます.
numcases環境は引数を1つ取ります.その引数に指定した数式が連立方程式の左辺に来ます.しかし,これには問題があります.
この引数は省略できないことです.なので左辺に式が必要無い時は次のように記述する必要があります.
\begin{numcases} {} f(x)=x^2+3x+1 \\ f'(x)=2x+3 \end{numcases}
caseパッケージを入れる必要があります. その他の問題点もあります.それは数式を任意の位置に揃える時に発生します.連立方程式に限らずalign環境やeqnarray環境(非推奨)を使って複数の数式を並べるとき&をつけることによって数式の位置を揃えることが出来ました.numcases環境を使って数式を揃える時も同じなのですが, & を使った後に数式モードが解除されるという問題点があります.なので&を記述した後はまた$$で挟んで数式モードに戻る必要があります.
\begin{numcases} {} f(x)&=$x^2+3x+1$ \\ f'(x)&=$2x+3x+1$ \end{numcases}
amsmathパッケージのcases環境
arrayやnumcasesよりもいい環境なのがcases環境です.式同士の間隔も適切で左辺に式を持ってくるのも簡単です.また,式の位置を揃える時も変なことになりません.次のように記述します.
\begin{align} \begin{cases} f(x)&=x^2+3x+1 \\ f'(x)&=2x+3 \end{cases} \end{align}
amsmathパッケージをいれる必要があります.
共通の問題点
cases環境はarray環境やnumcases環境のデメリットを完全になくしています.しかし,これらの環境全てに共通して起こっている問題点があります.それは大型の数式がインラインモードでしか入らないことです(numcasesはディスプレイモードになるが&を使った後はインラインモードにしかならない).それらの問題点を全て克服したのがempheq環境です.今のところ問題点は見つかっていません.
empheq
書き方は今までの環境と少し違います.次のように記述します.
\begin{empheq}[left=\empheqlbrace]{align} \f(x) &= x^2+3x+1 \\ \f'(x) &= 2x+3 \end{empheq}
empheqパッケージを入れる必要があります.
括弧の種類を変える
empheqには多種のオプションがあります.まず,括弧の種類を変えることができます.
コマンド | 括弧の種類 |
---|---|
\empheqlbrace | { |
\empheqrbrace | } |
\empheqlbrack | [ |
\empheqrbrack | ] |
\empheqlangle | < |
\empheqrangle | > |
\empheqlparen | ( |
\empheqrparen | ) |
\empheqlvert | | |
\empheqrvert | | |
\empheqlVert | || |
\empheqrVert | || |
オプション([]内)で上記のコマンドのいずれかを指定します.左に括弧を付けたい場合はleft=hogehogeといった具合です. 連立方程式の場合は{が普通ですので[left=\empheqlbrace]と指定します.右に括弧を付けたい場合はleft=の部分をright=にするだけです.
数式を2列以上にする
連立方程式ですので数式を2行以上にできるのは当たり前ですがempheqでは数式を2列以上にできます.これは例えば条件によって式が別れる時の条件式などに使えます.以下のような例です. このように数式を2列以上にする場合はempheqの引数であるalignをalignat=列数とします.そして列の分かれ目で\quadコマンドを使うことで次の列を記述できます.上の画像の式は次のように書きます.
\begin{empheq}[left={|x|=\empheqlbrace}]{alignat=2} x & \quad (x\geq0) \\ -x & \quad (otherwise) \end{empheq}
式番号を番号以外にする
矛盾しているように聞こえますがこれは式番号に使われる(1),(2),(3)などを(*),(※)とかに変更できるということです. 次のように記述します.
\begin{empheq}[left={|x|=\empheqlbrace}]{alignat=2} x & \quad (x\geq0) \tag{*} \\ -x & \quad (otherwise) \tag{※} \end{empheq}
ちなみに式番号を省略したい場合は引数のalignやalignat=2の部分をalignやalignat=2とすれば省略できます. align単体で使う時と一緒ですね.
式をボックスで囲む
これは使うことあるのかわからないですが一応紹介しておきます. left=やright=を指定するオプション内でbox=hogehogeとすることで任意のボックスで式を囲むことができます.
\begin{empheq}[box=\fbox,left={|x|=\empheqlbrace}]{alignat=2} x & \quad (x\geq0) \tag{*} \\ -x & \quad (otherwise) \tag{※} \end{empheq}
\fboxで角ばったボックスで囲めます. 左辺や左括弧を囲みたくない場合はbox=の部分をinnerbox=とします. 自分で定義したボックスで囲むことも可能です.
\definecolor{myblue}{rgb}{.8, .8, 1} \newcommand*\mybluebox[1]{% \colorbox{myblue}{\hspace{1em}#1\hspace{1em}}} \begin{empheq}[box=\mybluebox,left={|x|=\empheqlbrace}]{alignat=2} x & \quad (x\geq0) \tag{*} \\ -x & \quad (otherwise) \tag{※} \end{empheq}
比較
今まで紹介した環境を比較してみます.
ソースコードの貼り付けができるmintedをpLaTeXで使ってみた
mintedとは
- LaTeXでソースコードの貼り付けができるスタイルファイル
- 150以上のプログラミング言語をサポート
- PygmentsというPythonで書かれたシンタックスハイライターを元にしている
- pdfLaTeXやXeTeXなどで使うことを想定されておりpLaTeXを使う日本ではマイナー
- listingとの甲乙は検証中
導入方法
- Pygmentsのインストール
- minted.styの導入
- 使う
Pygmentsのインストール
Pygmentsが入っていない場合は以下のコマンドでインストールします。
sudo apt-get install python-pygments
minted.styの導入
Pygmentsが入りましたら次にminted.styをダウンロードします。以下のサイトからダウンロードしてください。
CTAN: Package minted
下の"Download the contents of this package in one zip archive"からダウンロードできます。
ダウンロードしたらダウンロード先のディレクトリまで進み、ファイルの解凍を行います。
解凍したディレクトリに入りmakeコマンドを実行してください。
実行が終わりますとディレクトリ内にminted.styというファイルがありますので。texmfの中に入れるなりして導入してください。
使う
ここまで来たら後は必要なことを記述して使うだけです。 記述の例はTeXLive2014と2015で違いますので気をつけてください。 mintedを使うTeXファイルの中で次の命令をプリアンブル部に記述します。
TeXLive2014の場合
\makeatletter\chardef\pdf@shellescape=\@ne\makeatother \usepackage{minted}
TeXLive2015の場合
\usepackage[cache=false]{minted}
これでmintedを使うことができます。以下はmintedを使った例です。
\documentclass{jsarticle} \chardef\pdf@shellescape=\@ne \usepackage{minted} \begin{document} \begin{minted}{c} #include<stdio.h> int main(void){ printf("Hello World!\n"); return 0; } \end{minted} \end{document}
コンパイルしたいころですがここで問題が発生します。なんとmintedは不具合のためかタブスペースが^^Iと変換されて表示されます。 試行錯誤を重ねたのですが原因はわからず...結局テキストエディタでタブをスペースに変換することにしました。タブをスペースに自動変換する方法は自分の使っているテキストエディタ毎に調べてください。設定ができましたらこれをコンパイルしていきます。 コンパイルの時はオプションをつけなければなりません。次のようにオプションをつけてコンパイルを行ってください。
platex -shell-escape [filename]
latexmkでコンパイルをする場合もshellescapeの設定を行えばコンパイルをすることができます。
latexmkの導入についての記事は以下をご覧ください。
muscle-keisuke.hatenablog.com
一度ターゲットを決めたら変更のたびにコンパイルを自動でやるので便利ですよ!
実行結果は以下のようになります。
また、texファイル内に直接ソースコードを記述せずソースファイルを外部から読み込んで表示することも可能です。先ほどの例にあるCのソースコードをhoge.cという名前で保存したとしましょう。それをLaTeXでmintedを使って読み込んで表示したい場合は以下のように記述します。
\inputminted{c}{hoge.c}
言語を指定する引数がソース直書きの時と違い最初に来ていることに注意してください。長いソースコードの場合はこの方がすっきりしていいと思います。
mintedの例
ソースコードを行番号つきで載せる
ソースコードを行番号つきで載せる時は以下のmintedのオプションにlinenosと付けます。
\begin{minted}[linenos]{c}
ソースコードのコメントに数式を付ける
LaTeXに載せたソースコードのコメントを付ける時に数式を使った方がわかりやすい場合があります。次のようにLaTeX用の数式コマンドを使っても オプションにmathescapeを付ければLaTeXと同じように数式を表示することができます。
\begin{minted}[linenos,mathescape]{c} #include<stdio.h> int main(void){ int k=0; int x=0; // This code mean \sum_{k=0}^{100} k^2+2k+1 for(k=0;k<100;++k){ x=k*k + 2*k +1; } printf("x=%d\n"); return 0; } \end{minted}
結果は次のように綺麗な数式でコメントを残すことができます。
その他のオプション
そのほかにも枠を付けたり図や表のようなキャプションやラベルをつけることも可能です。
\definecolor{bg}{rgb}{0.95,0.95,0.95} \begin{listing} \begin{minted}[linenos, mathescape, numbersep=5pt, frame=lines, framesep=2mm, bgcolor=bg]{c} #include<stdio.h> int main(void){ int k=0; int x=0; // This code mean \sum_{k=0}^{100} k^2+2k+1 for(k=0;k<100;++k){ x=k*k + 2*k +1; } printf("x=%d\n"); return 0; } \end{minted} \caption{mintedの例} \label{lst:example} \end{listing} ソース\ref{lst:example}はmintedの例である。
引用したい時も助かります。
新しいmintedマクロ
ここまでmintedに関する様々なオプションを紹介してきました。しかし、たくさんのオプションを使うときにいちいち記述していたらごちゃごちゃしたり面倒くさかったりします。そこでmintedには新しいminted用のマクロを作れるマクロがあります。それが\newmintedと\newmintedfileです。2つのマクロの違いは外部からソースを読み込むかどうかの違いしかありません。次のソースを元に\newmintedと\newmintedfileで書き換える次のようになります。
\begin{minted}[linenos, mathescape, numbersep=5pt, frame=lines, framesep=2mm ]{c} #include<stdio.h> int main(void){ printf("Hello World!\n"); return 0; } \end{minted} \inputminted[linenos, mathescape, numbersep=5pt, frame=lines, framesep=2mm]{c}{hoge.c}
書き換えると
\newminted[myMinted]{c}{ linenos, mathescape, numbersep=5pt, frame=lines, framesep=2mm } \newmintedfile[myMintedfile]{c}{ linenos, mathescape, numbersep=5pt, frame=lines, framesep=2mm } \begin{myMinted} #include<stdio.h> int main(void){ printf("Hello World!\n"); return 0; } \end{myMinted} \myMintedfile{hoge.c}
ソースが長くなった気がしますがそれは最初に定義を書いているからです。newminted、newmintedfileマクロによって新しいminted用のマクロmyMintedとmyMintedfileを定義してそれを使っています。普段から同じオプションを使う人はこのように定義をしておくとコードがすっきりするかもしれません。
Exact Cover Problemとそれを応用した数独解読アルゴリズム
Exact Cover Problemを知ったきっかけ
先日、演習の一環で数独を解読を行うアルゴリズムをJavaで書くという演習がありました。どうせなら早く解けるようにしたいということで何かいいアルゴリズムはないかと調べました。そしたらこのような記事を見つけました。 d.hatena.ne.jpこの記事で紹介されている問題とアルゴリズムを使うと数独を解くプログラムができるらしいです。 Exact Cover Problemとそれを解くKnuth's Algorithm Xについては引用元の記事をご覧ください。
Exact Cover Problemを数独に応用する
数独問題=Exact Cover Problem
人間が数独を解くときは「ここには何が入りそうだ」とかを考えて解候補をマスの端に書いたりして考えます。今回紹介するExact Cover Problemもそれに似た感じです。
数独をExact Cover Problemとしてみる
数独として成り立つには次のような条件があります。
- 各行と各列が交差する部分つまり各セルには必ず正確な数字が入っている必要がある。...行と列の関係
- 各行には正確な数字が重複しないようにはいっている必要がある。...行と数字の関係
- 各列には正確な数字が重複しないように入っている必要がある。...列と数字の関係
- 各ボックスには正確な数字が重複しないように入っている必要がある。...ボックスと数字の関係
上記の4つの条件をすべての行、列、ボックスにおいてすべて満たすと数独的に正しいことになります。Exact Cover Problemとして考えるには人間がマスの端に可能性のある数字を書き込むようにこの各条件と考え得るすべての場合と照らし合わせて考えます。考え得るすべての場合というのは「1行1列に1が入る場合」、「1行1列に2が入る場合」、...「9行9列に9が入る場合」までを考えます。そしてそれらは各条件を持った集合です。例えば「1行1列に1が入る場合」というのは{1行1列に数字がある、1行目に1がある、1列目に1がある、一つ目のボックスに1がある}という4つの元を持った集合です。そしてこれらの元は他の場合と重複して持っていてはいけません。かつすべての条件を網羅していてなくてはいけません。つまり数独はExact Cover Problemなのです。
Knuth's Algorithm Xに数独を適用する
数独がExact Cover Problemであることは前節でわかっていただけたでしょうか。ここからはExact Cove Problemとして数独を見たときにそれをコンピュータにどう解かせるかを考えていきます。引用させていただいたJAPL JさんのブログによるとKnuth's Algorithm Xのアルゴリズムでは各元とそれを持った集合の関係行列を用意していました。これを前節で説明したものに適用する前に表現を定義しておきます。まずすべての考え得る場合からです。「m行n列に数字pがはいる場合」というのはRmCn#pと記述することにします。例えば「3行6列に数字8が入る場合」というのはR3C6#8となります。次に条件について考えます。行と列の関係についてm行n列に数字が入っているという条件はRmCnと記述することにします。行と数字の関係についてm行目に数字pが入るという条件はRm#pと記述します。列と数字の関係についてn列目に数字pが入るという条件はCn#pと記述します。ボックスと数字の関係についてx番目のボックスに数字pがはいるという条件はBx#pと記述します。定義をまとめると以下のようになります。
これを基に数独の関係行列を作成すると以下のようになります。(の行列なので間を省略)
R1C1 | R1C2 | R9C9 | R1#1 | R1#2 | R9#9 | C1#1 | C1#2 | C9#9 | B1#1 | B1#2 | B9#9 | |||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
R1C1#1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | ||||
R1C1#2 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | ||||
R1C1#9 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||||
R1C2#1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | ||||
R1C2#2 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||||
R1C9#9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | ||||
R2C1#1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | ||||
R2C1#2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||||
R9C9#9 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
しかし、この関係行列は数独問題が全くの空白の場合です。数独の問題を解く時は大抵、所々に初期値が入っているのものです。例えば3行2列目に数字9が入っていることがわかっていたとしましょう。そうすると関係行列のR3C2、R3#9、C2#9、B1#9の列はすべて1になります。なぜならどの場合を考えたとしてもそれらの列の条件を満たしているのは明らかだからです。そしてR3C2#1~R3C2#8の8行はすべて消えます。なぜならこれらの行の場合はあり得ないからです。この行列を解いて残った解候補が答えとなります。
プログラムを書く
数独をアルゴリズムで解くビジョンが見えてきたと思います。次にこれをプログラムに解かせるにはどうすれば効率がよいかを考えます。
今回の演習はJavaの演習なのでJavaで実装しければなりません。 考えた末全然実装方法が思いつかなかったのでネットの力を頼ることにしました。そしたらGitHubにExact Cover Problemを実装したソースコードがありました。それが以下になります。
rafalioさんという方が数独をKnuth's Algorithm Xで解くプログラムをJavaで実装されていました。最初プログラムを見た時はちんぷんかんぷんでしたが時間をかけながら一つずつ理解していきました。rafalioさんが書かれたプログラムにできるだけ細かくコメントを記述したものをGitHubに上げました。以下になります。
※ただし、演習で使う用に改造したので元のソースコードとは異なる部分があります。
メソッドなどの詳しい動作説明
自分が解釈や理解に苦しんだクラスやメソッドを選んで説明させていただきます。
Dancing Links
引用元のブログでも紹介している通りDancing Linksという概念を用いてKnuth's Algorithm Xを解きます。DancingLinksについて概要だけ述べます。
- 上下左右に双方向に繋がる自己参照型リスト
- 今回の場合行列の1になる部分だけリンクする疎行列のスタイル
- 各列の一番上にはカラムノードという列を表すノードがある
hookRigntメソッドとhookDownメソッド
ノード同士の連結にはノードを引数に取るhookRightメソッドとhookDownメソッドで実装されています。それぞれ引数にとったノードを右または下にリンクさせる関数です。この関数だけで双方向のリンクを実現するので左または上にリンクさせる関数は不要です。hookRight関数を例にリンクの様子を説明すると以下のようになります。 図はn1が端のノードではないことを前提に作っています。もしn1が端のノードの場合はn1.Rは循環して最初のノードとなります。hookDownメソッドも同様です。 このメソッドを繰り返すことでDancingLinksを形成します。DancingNodeクラスとColumnNodeクラス
Knuth's Algorithm Xには「列の中で一番1が少ない列を選択する」という動作があります。この時使うのがColumnNodeです。ColumnNodeはフィールドにString型を持ち自分が何列目かをそこに格納します。各DancingNodeはフィールドにColumnNodeを持ち、自分自身が何列目かがわかることができます。unLinkUDメソッド,unLinkLRメソッド
次に削除の手順を示します。DancingNodeのメソッドにunLinkLR,unLinkUDの2つのメソッドがあります。これらを使って削除を行います。これらのメソッドによって違うノードからの参照ができなくなります。coverメソッド
前節で説明したunLinkUD,unLinkLRメソッドを使い列ごとの削除を行います。正確にいうと削除ではありません。正確に言うとノードを覆い(cover)、見えなくします。そうすることによって列を削除したと見なして処理を続けます。なぜいっそのこと削除しないのかという話になります。それは後で列の復元をする必要があるからです。Knuth's Algorithm Xでは列を選択した後、「その列に1が含まれる行を1つだけ選択するという動作」があります。その行の選択の仕方によって解が変わる可能性があります。最悪の場合解が出ないことがあります。しかし、それを事前に判断することができません。rafalioさんのソースコードではすべての行をfor文で網羅しています。そして再帰関数によって最後まで処理を試みます。ここで解なしが発生した場合再帰処理から復帰しなければなりません。その際に列を完全に削除してしまうと復元ができないのでcoverするにとどまるというわけです。reLinkUDメソッド,reLinkLRメソッド
unLinkUD,unLinkLRメソッドによって参照不能になったメソッドを再び双方向に戻します。UDのほうが上下に双方向,LRが左右に双方向を復元させます。uncoverメソッド
coverメソッドによって覆った列を復元するメソッドです。他のノードからは参照できませんが自分自身のノードからは参照できるのでそこからreLinkUD,reLinkLRによって双方向に戻します。coverメソッドを発動した時点でガベージコレクションに削除されない理由を次の節で説明します。knuthsAlgoritmXメソッド
プログラムの心臓部です。内部でselectColumnNodeHeuristicメソッドという1が最も少ないColumnNodeを返すメソッドを呼んでいます。そのメソッドによって返されたColumnNode及びそこから参照できるすべてのDancingNodeが属するColumnNodeをcoverメソッドで参照できなくします。その際にガベージコレクションによって回収されないようにanswerというコレクションに対象のDancingNodeを追加します。内部でknuthsAlgoritmXメソッドを再帰的に呼び出し、行列が0になるかもしくはリンクするDancingLinkがなくなるまで行います。行列が0になった場合は成功なのでanswerをhandlerクラスのhandleSolutionメソッドという解を決定づけるメソッドの引数に渡します。リンクするDancingLinkがなくなるつまり1を全く含まない列が出てきた場合は解なしなのでそのままメソッドを終了していきます。関数を終了するとその後に復元処理をする必要があります。次の行について調べる必要があるからです。ガベージコレクションに回収されないようにanswerに格納していたDancingNodeを取り出します。answerからはDancingNodeを削除します。取り出したDancingNode及びそこからたどれるすべてのDancingNodeについてuncoverを行い列を復元させます。そのレベルにおいて全ての行の探索が終われば列を復元させて同じ処理を行います。このような具合に解を見つけていきます。以下にknuthsAlgorithmXの簡単なフローチャートを載せておきます。