やんちょふのブログ

ご連絡はTwitterアカウント@syoyuu616にて受け付けております

強化学習メモ 指数減衰加重平均について

前書き

今、強化学習の勉強をしているのでメモ書き程度に書き残します。

前準備

行動と、行動に対する報酬が設定されたタスクについて考えます。また、報酬はあらかじめ決められた平均、分散による確率分布から与えられるものとします。(例えば正規分布)
ある行動 aを行うことによる真の価値が分かれば、報酬をより多く得られる行動を選択することができるようになります。ここで行動 aを行う真の価値を Q^*(a)とします。

次に、タスクを行う側は行動に対する真の価値がわからないと仮定します。その場合、いくつかの行動について複数回試行した結果から価値を推定すると考えます。 t回目の試行での Q^*(a)
定量 Q_t(a)とします。行動価値の推定量は、例えばある時点までにある行動を行ったことによる報酬の平均などが考えられます。

そこで、ある時点までの報酬の総和を算出することが重要となります。(今回の本筋ではないので中略します)

ある行動 aをそれまでに k回行っていたとして、行動 a k回行った時までに得られた報酬の平均を Q_kと表記します。また、各回で得られた報酬を r_1, r_2, \cdot, r_kなどと表すことにします。
この時、 k+1回目の試行によって報酬 r_{k+1}が得られたとすると、 k+1回目までの報酬の平均は次の式で表すことができます。

 Q_{k+1} = \frac{1}{k+1}\sum_{i=1}^{k+1}r_i = \frac{1}{k+1} \left( r_{k+1} + kQ_k + Q_k - Q_k \right) = Q_k + \frac{1}{k+1} \left( r_{k+1}-Q_k \right)

この式からより一般的に次の形で報酬の平均を算出します。

 Q_{k+1} = Q_k + \alpha \left( r_{k+1} - Q_k \right)

ちなみに 0 < \alpha \leq 1とかで設定します。この式を Q_kについて展開していくと次のようになります。

 
\begin{eqnarray}
Q_k &=& Q_{k-1} + \alpha \left( r_{k} - Q_{k-1} \right) \\
       &=& \alpha r_k + \left( 1-\alpha \right) Q_{k-1} \\
       &=& \alpha r_k + \left(1-\alpha \right) \left( Q_{k-2} + \alpha \left( r_{k-1} - Q_{k-2} \right) \right) \\
       &=& \alpha r_k + \left(1-\alpha \right) \left( \alpha r_{k-1} + \left( 1-\alpha \right) Q_{k-2} \right) \\
       &=& \alpha r_k + \alpha \left(1-\alpha \right) r_{k-1} +\left( 1-\alpha \right)^2 Q_{k-2} \\
       &=& \alpha r_k + \alpha \left(1-\alpha \right) r_{k-1} + \cdots + \alpha \left( 1-\alpha \right)^{k-1} r_1 + \left(1-\alpha \right)^k Q_0 \\
       &=& \left(1-\alpha \right)^k Q_0 + \sum_{i=1}^{k} \alpha \left( 1-\alpha \right)^{k-i} r_i
\end{eqnarray}

以上よりまとめると次式が得られます。

 Q_k = \left(1-\alpha \right)^k Q_0 + \sum_{i=1}^{k} \alpha \left( 1-\alpha \right)^{k-i} r_i

報酬の初期値と各報酬に対して重みがかかっているような形になっているのが確認できます。この重みですが重みの総和について次のような関係があります。

 \left(1-\alpha \right)^k + \sum_{i=1}^{k} \alpha \left( 1-\alpha \right)^{k-i} = 1

総和が1になるため、加重平均と呼ばれたりします。今回の記事ではこの等式が成り立つことを示します。

等式の証明


\begin{eqnarray}
S &=& (1-\alpha)^k + \sum_{i=1}^k \alpha(1-\alpha)^{k-i} \\
   &=& (1-\alpha)^k + \alpha (1-\alpha)^{k-1} + \alpha (1-\alpha)^{k-2} + \cdots + \alpha (1-\alpha) + \alpha
\end{eqnarray}

と置きます。ここで (1-\alpha) Sは次式のようになります。

 (1-\alpha)S = (1-\alpha)^{k+1} + \alpha (1-\alpha)^{k} + \alpha (1-\alpha)^{k-1} + \alpha (1-\alpha)^{k-2} + \cdots + \alpha (1-\alpha)^2 + \alpha (1-\alpha)

次に S - (1-\alpha)Sを考えます。


\begin{eqnarray}
S - (1-\alpha)S &=& (1-\alpha)^k - (1-\alpha)^{k+1} - \alpha (1-\alpha)^k + \alpha \\
&=&  (1-\alpha)^k - (1-\alpha)(1-\alpha)^{k} - \alpha (1-\alpha)^k + \alpha \\
&=& (1-\alpha)^k - (1 - \alpha)^k + \alpha (1-\alpha)^k - \alpha (1-\alpha)^k + \alpha \\
&=& \alpha
\end{eqnarray}

また左辺について、
 S-(1-\alpha)S = \alpha S
なので、次の式が得られます。
 \alpha S = \alpha
 \therefore S = 1
元の式に直すと次のようにまとめられます!
 (1-\alpha)^k + \sum_{i=1}^k \alpha(1-\alpha)^{k-i} = 1

まとめ

特にこれといって言うことはありませんね。計算メモとして残しておきます。ありがとうございました。

高専数学続論

前書き

この記事はKosen13s' Advent Calendar 2018の記事として書かれています
This article is written as the article of Kosen13s' Advent Calendar 2018.
そのため、他の記事も読まれることをお勧めします。内容には順序関係がないので
この記事だけよんでいただいても問題はないと思います。
筆者は熊本高専熊本キャンパス人間情報システム工学科の出身です。
熊本キャンパスは基本的にすべて情報系の学科から構成されるので
生物化学や土木などの別分野での高専には当てはまらない話の可能性がありますし、
私の出身学科にすら共感者が出ない可能性も少なからずあると思います。

本記事で用いられる意見は筆者個人の意見です。もしかしたら事実と異なる
可能性がありますので、深く真に受けないようにお願いします。

概要

高専生を悩ませる(窮地に追い込む)要素というと留年が主だと思うわけですが
留年という現象の原因としてはレポート未提出と数学が難しいということが挙げられる
と思います.もちろん留学等々で留年されるケースもあると思いますが、今回はそういった事例は除き、オーソドックスな留年を前提に話を進めます。
個人的にはレポート未提出が留年原因のほとんどだと思いますが、ここではそちらには触れないものとします。

高専生は数学が苦手であるから数学の単位を落としそうになるからという仮説が考えられますが、一応理系といわれる人が受験して入学していますので基礎的な数学知識は有していると思われます。

ではなぜ数学が鬼門になるのかですが、僕は数学に対するモチベが上がらないからであると思っています。僕の周りでは高専に技術的なモチベを携えて入学し、実際にガンガン開発したりコード書いたりする人はそれなりに居ました。

しかし、そういう人ほど数学に苦しみます。苦しむ理由には積分微分のテクニックを覚えられないとか、基礎練習に時間を掛けたくないということも考えられます。しかし、一つの大きな原因としては、そもそも何をしているのか、もしくは何をしたいのかということがわからず、全く楽しくないということが挙げられるのではないでしょうか(以下これを前提として話を進めます)。特に4,5年生で教わる数学なんていうのは段々専門科目とのかかわりも見えなくなってきたような気もします。

在学している方については以上ですが、これから編入する方で数学知識が何につながるのかに興味がある方、社会人になったけど結局あの数学は何だったのかという疑問を少しでも解消したいと思います。

そのため本記事では、「高専数学ってこんなことにつながってるんですよ」ということについて書いていきます。

高専数学の復習

とりあえず高専数学でどんなことをしたのかを軽く並べてみることにしましょう。うろ覚えですが大体次のようなことをやった気がします。

テイラー展開オイラーの公式

テイラー展開は次の式ですね。

\displaystyle
\begin{align}
f(x+a) = f(a) + f^{(1)}(a)x + \frac{1}{2!} f^{(2)}(a) x^2 + \cdots + \frac{1}{n!} f^{(n)}(a) x^n + R_{n+1}
\end{align}
剰余項の説明は省きます。マクローリン展開テイラー展開をx=0の周りで適用したものですから省略します。

ここから応用して次のオイラーの公式が導出されますね。
 
\displaystyle
\begin{align}
e^{i\theta} = \cos{\theta} + i\sin{\theta}
\end{align}

既にここまでで微分を使ってしまっているわけですが、先ほどの分類は適当なので気にしないでください。

微分方程式 (簡易説明)

次にここまでで得られたものを使って(使わなかったりして)微分方程式を記述してみましょう。
単純な微分方程式としては次のマルサスの方程式が有名です。
 
\displaystyle
\begin{align}
\frac{dN}{dt} = \lambda N
\end{align}
時間経過による人口の推移を示した方程式です。単純な変数分離形ですから解は簡単に求めることができ、次のような解が得られます。
 \displaystyle
\begin{align}
N(t) = Ce^{\lambda t} \end{align}
人口は時間経過に対して指数関数的に増加するということがわかります。(少なくともこのモデルではですが)
この記事では微分方程式が話のメインだったりするのでここら辺の説明を多めに話します。
先ほどのマルサスの方程式はより一般的な表現で言うと、一階線形微分方程式というものになります。一階線形微分方程式は次の式で表されます。
 \displaystyle
\begin{align} \frac{dy}{dx} + Q(x)y = R(x) \end{align}

(少し余談ですが、方程式の一般形とかの書き方も流派ありますよね。)

とりあえず微分方程式までは思い出していただけたでしょうか?

線形空間

さて先ほどの微分方程式は、人口推移の記述モデルとして使われていたりして比較的何をしているのかわかりやすかったと(個人的には)思うわけですが
筆者は線形空間の話がいまいちよくわかりませんでした。

詳しい説明は専門書等を読んでください。ここではざっくりジョルダン標準形の話をします。
まずジョルダン標準形について話す前に行列の対角化について復習しましょう。

行列の対角化

まず体Kによるn次正方行列をの集合を M_n(K)と書くことにしましょう.今、 A \in M_n(K)を考えます。Aの異なる固有値 \lambda_iに対する幾何的重複度 m_iは次の式で表されます。
 m_i = n - Rank(A-\lambda_i E)
同時に、Aの固有多項式からAの固有値に対する代数的重複度が得られます。先ほど固有値に対する幾何的重複度と書きましたが、正確に書くとAの固有空間に対する幾何的重複度で、Aのすべての固有空間について幾何的重複度と代数的重複度が一致する(すなわち一般固有空間が固有空間の直和で表すことができる場合)、Aが対角化可能です。また、Aが対角化可能であればこの条件が満たされます。

例を挙げてみましょう。次の行列 A \in M_3({\mathrm R})について考えてみます。
 A = \left( \begin{array} {ccc} 1 & 2 & 1 \\ 0 & 2 & 0 \\ 0 & 1 & 3 \end{array} \right)

固有多項式から異なる固有値が3つ存在しそれぞれ \lambda_1 = 1,\lambda_2 = 2, \lambda_3 = 3であることがわかりますね(出来レースのような行列ですみません)。固有空間は次のようになります。
 V(1) = \left< \left( \begin {array} {c} 1 \\ 0 \\ 0 \end {array} \right) \right>, V(2) = \left< \left( \begin {array} {c} 1 \\ 1 \\ -1 \end {array} \right) \right>,V(3) = \left< \left( \begin {array} {c} 1 \\ 0 \\ 2 \end {array} \right) \right>
ここで対角化行列 P \in M_3(R)を次のように取ります。
 P = \left( \begin{array} {ccc} 1&1&1 \\ 0&1&0 \\ 0&-1&2 \end{array} \right)
するとAは次のように対角化できます。
 P^{-1}AP = \left( \begin{array} {ccc} 1&0&0 \\ 0&2&0 \\ 0&0&3 \end{array} \right) = diag(\lambda_1, \lambda_2, \lambda_3)
すっきりしましたね(果てしない満足感 > 牛丼)

ジョルダン標準形

先ほど行列の対角化を簡単に説明しましたが、もちろん対角化できない行列も存在します。先ほど示した対角化条件を満たさない行列たちです。例としては次に示す行列 B \in M_n({\mathrm R})です。
 B = \left( \begin {array} {ccc} 3&0&-2 \\ 0&0&2 \\ 0&-2&4 \end {array} \right)
こちらも出来レースのような行列になってますが筆者の力不足です。さて固有多項式 \phi(x)から固有方程式を求めると次のような式が得られます。
 (x-2)^2(x-3) = 0
この方程式から2つの固有値 \lambda_1 = 2, \lambda_2 = 3が得られます。また\lambda_1の代数的重複度は2となっています。ここで固有値についてそれぞれ幾何的重複度を求めてみましょう。まず \lambda_1についてですが次のようになります。
 B - 2E =\left( \begin{array} {ccc} 1&0&-2 \\ 0&-2&-2 \\ 0&2&2\end{array} \right) \to \left( \begin{array} {ccc} 1&0&-2 \\ 0&1&1 \\ 0&0&0\end{array} \right)
 \therefore m_1 = n - Rank(A-2E) = 3 - 2 = 1
はい、代数的重複度と幾何的重複度が一致してませんね。(暗黒微笑)
 \lambda_2についても同様に検証してみましょう。
 B - 3E =\left( \begin{array} {ccc} 0&0&-2 \\ 0&-3&2 \\ 0&-2&1\end{array} \right) \to \left( \begin{array} {ccc} 0&1&0 \\ 0&0&1 \\ 0&0&0\end{array} \right)
 \therefore m_2 = n - Rank(A-3E) = 3 - 2 = 1
 \lambda_2に関しては代数的重複度と幾何的重複度が一致していますね。

対角化出来ない場合はジョルダン標準形に変形できます。
まずは固有値に対する固有空間を求めます。
 V(\lambda_1) = \left< \left( \begin {array} {c} 2\\-1\\1 \end{array} \right) \right>,V(\lambda_2) = \left< \left( \begin {array} {c} 1\\0\\0 \end{array} \right) \right>
ここで、 dim V(\lambda_1) = 1なのでジョルダン標準形を構成する要素(ジョルダン細胞といいます)の数は1つだということがわかります。また、代数的重複度が2だったので、 \lambda_1に対するジョルダン細胞のサイズは 2\times2となります。ここら辺がこうなる理由はここでは省略します。

 \lambda_1に対するジョルダン細胞を J(\lambda_1, 2)と書くことにします。 \lambda_2に対するジョルダン細胞はサイズが 1\times1ですので J(\lambda_2,1)と表記します。すると次のような関係があります。
 J(\lambda_1, 2) = \left( \begin{array} {cc} 2&1 \\ 0&2 \end{array}\right),J(\lambda_2, 1) = \left( 3\right)

ここで正則行列 Pを用いて次のようにジョルダン標準形に変換できます。
 P^{-1}BP = J(\lambda_1, 2) \bigoplus J(\lambda_2, 1) = \left( \begin {array} {ccc} 2&1&0 \\ 0&2&0 \\ 0&0&3 \end{array} \right)
果てしない達成感を感じますね。

応用編(微分方程式系)

懐かしい話をここまでしてきましたが実はこれまでの話は全てつながっています。特に微分方程式ジョルダン標準形など(線形空間の話)は異なる科目だったりしますが合わせ技として使えます。ここでは微分方程式系を合わせ技で解析する話をします。(簡単のために2階の微分方程式に限定した話をします)

一般解の求め方(簡易版)

次の線形微分方程式系について考えます。
 \begin{eqnarray} \begin{cases} \frac{dx}{dt} = ax+by&\\ \frac{dy}{dt} = cx+dy \end{cases} \end{eqnarray}
ここで、 {\it{ \bf x}} = \left( \begin{array} {c} x(t) \\ y(t) \end{array}\right), A=\left( \begin{array} {cc} a&b\\c&d \end{array} \right)とおくと、 {\bf x}^{\\'} = A{\bf x}と書けます。
実はこの行列Aの固有値固有ベクトルを求めることで、元の微分方程式の相平面x-yにおける軌道の性質について解析することができます!(要するに解がわかります)

行列Aの固有値 \lambda_1,\lambda_2固有値に対する固有ベクトル v(\lambda_1),v(\lambda_2)が求まった想定しましょう。この時微分方程式系は次の2つの解(直線解)を持ちます。
 Y_1(t) = e^{\lambda_1x}v(\lambda_1), Y_2(t) = e^{\lambda_2x}v(\lambda_2)
すると線形微分方程式系の一般解は次の式で表すことができます。 C_1,C_2は任意定数です。
 Y(t) = C_1Y_1(t) + C_2Y_2(t)
原理はともかくとして、実際にこの方法を使ってモデルを解析してみましょう。

調和振動子

例として調和振動子を考えてみましょう。調和振動子の動きは次の式で表すことができます。
 \displaystyle \frac{d^2x}{dt^2} = -kx
要するにばね運動ですね。
ここで次のように変数を定義しましょう。
 y = \frac{dx}{dt}
すると次の連立方程式で元の式を表すことができます。
 \begin{eqnarray} \begin{cases} \frac{dx}{dt} = y&\\ \frac{dy}{dt} = -kx \end{cases} \end{eqnarray}
また {\it{ \bf x}} = \left( \begin{array} {c} x(t) \\ y(t) \end{array}\right), A=\left( \begin{array} {cc} 0&1\\-k&0 \end{array} \right)とします。
では、Aの固有空間を求めてみましょう。
 \phi(\lambda) = \left| \begin{array} {cc} \lambda&-1\\k&z \end{array} \right|=\lambda^2+k=0
 \lambda = \pm{i\sqrt{k}}
 \lambda_1 = i\sqrt{k}, \lambda_2 = -i\sqrt{k}
 A - i\sqrt{k}E = \left( \begin{array} {cc} -i\sqrt{k}&1\\-k&-i\sqrt{k} \end{array} \right) \to \left( \begin{array} {cc} -i\sqrt{k}&1\\0&0 \end{array} \right)
 V(\lambda_1) = \left< \left( \begin{array} {c} 1 \\ i\sqrt{k} \end{array} \right) \right>
ここでオイラーの公式を使います!解X(t)は次の式で表されます。
 X(t) = e^{i\sqrt{k}t}\left(\begin{array} {c} 1\\i\sqrt{k}\end{array}\right) = \left(\begin{array} {c} \cos{\sqrt{k}t}+i\sin{\sqrt{k}t}\\-\sqrt{k}\sin{\sqrt{k}t}+i\sqrt{k}\cos{\sqrt{k}t}\end{array}\right)
解を実部と虚部とでそれぞれ次のように置き直します。
 X_{Re}(t) = \left( \begin{array}{c} \cos{\sqrt{k}t}\\-\sqrt{k}\sin{\sqrt{k}t}\end{array}\right), X_{Im}(t) = \left( \begin{array}{c} \sin{\sqrt{k}t}\\\sqrt{k}\cos{\sqrt{k}t}\end{array}\right)
すると
 X(t) = C_1 X_{Re}(t) + C_2 X_{Im}(t)
となります。綺麗な形になりましたね!(強引な幕引き)任意定数ですから仮に C_2 = 0として解の軌道を考えてみましょう。プログラムを使って描画したものが次図になります。

f:id:yanchof:20181211013222p:plain
調和振動子のx-y相平面上での動き

時間経過とともに解はちょうど解曲線上を時計回りに動くことが式からわかります。この解曲線が何を意味してるかというと、時間経過によって同じ位置、速度に状態が戻るということです。つまり系の中で運動が保存されています。摩擦等を考えない場合を思い出していただきたいのですが、その場合、ばね運動は永遠に終わりませんね?そういう性質が図の解曲線からわかるのです。

ジョルダン標準形と微分方程式

行列Aが次のような形だったとしましょう。
 \left( \begin{array} {cc} \lambda&0\\0&\lambda \end{array} \right)
解は次のようになりますね。
 X(t) = C_1 e^{\lambda t} \left( \begin{array} {c} 1\\0 \end{array} \right) + C_2 e^{\lambda t} \left( \begin{array} {c} 0\\1 \end{array} \right)

 X^{\\'}(t) = BX(t)という式があったとして、Bを対角化する行列をUとします。ここで U^{-1}BU=\left( \begin{array} {cc} \lambda & 0\\0&\lambda \end{array} \right) と対角化したとします。この式が次のような微分方程式を構成すると考えます。
 Y^{\\'}(t) = U^{-1}BUY(t)
この微分方程式の解は冒頭で説明したようになりますね。実はこの時 X = UYという関係にあります。式に代入して確認してみましょう。
 Y = U^{-1}X, Y^{\\'} = U^{-1}X^{\\'}
 U^{-1}X = U^{-1}BX
 X^{\\'} = BX
ちゃんと元の式に戻りましたね。XとYの関係からすごく単純なYについての解を求めた後で変換行列UをYに書ければXが求まることがわかりました。

では次のような行列Cがあったとしましょう。
 \left( \begin{array} {cc} \lambda&1\\0&\lambda \end{array} \right)
先ほどと同様にすると(詳細は後日)解は次のようになります。
 X(t) = C_1 e^{\lambda t} \left( \begin{array} {c} 1\\0 \end{array} \right) + C_2 e^{\lambda t} \left( \begin{array} {c} t\\1 \end{array} \right)
行列Cはジョルダン標準形になっています。つまり、行列を変換してジョルダン標準形にした後、単純な行列計算で解が求まります。

例を見てみましょう。今、行列Dが次のように与えられたとします。
 D = \left( \begin{array} {cc} 3&1\\-1&1 \end{array} \right)
固有値 \lambda = 2です。適当な行列Pを使って次のように変換できます。
 P^{-1}DP = \left( \begin{array} {cc} 2&1\\0&2 \end{array} \right)
この時 P = (p_1, p_2)とすると、
 \begin{eqnarray} \begin{cases} Dp_1 = 2p_1&\\ Dp_2 = p_1+2p_2 \end{cases} \end{eqnarray}
この関係から次の式が成り立ちます。
 \begin{eqnarray} \begin{cases} (D - 2E)p_1=0&\\ (D - 2E)p_2=p_1\end{cases} \end{eqnarray}
 D-2E = \left( \begin{array} {cc} 1&1\\-1&-1 \end{array} \right) \to  \left( \begin{array} {cc} 1&1\\0&0 \end{array} \right)
以上より、 p_1 = \left( \begin{array}{c} 1\\-1\end{array} \right), p_2=\left( \begin{array}{c} 1\\0\end{array} \right)と取ることができます。
また、 Y^{\\'}=P^{-1}DPYとして
 Y =  C_1 e^{2t} \left( \begin{array} {c} 1\\0 \end{array} \right) + C_2 e^{2t} \left( \begin{array} {c} t\\1 \end{array} \right)
となることは先ほど説明した通りです。ここからXは次のようにして求まります。
 X = PY =  C_1 e^{2t} \left( \begin{array} {cc} -1&1\\1&0 \end{array} \right)\left( \begin{array} {c} 1\\0 \end{array} \right)+ C_2 e^{2t} \left( \begin{array} {cc} -1&1\\1&0 \end{array} \right)\left( \begin{array} {c} t\\1 \end{array} \right)
 \therefore X = C_1 e^{2t} \left( \begin{array} {c} -1\\1 \end{array} \right) + C_2 e^{2t} \left( \begin{array} {c} 1-t\\t \end{array} \right)

f:id:yanchof:20181212002550p:plain
解曲線の例

ほら!ジョルダン標準形に変換する作業が微分方程式の解を導出するのに役立ちましたね!

まとめ

筆者もジョルダン標準形への変換がどういった形で使われるのかわかっていませんでしたが、応用編でしめしたように微分方程式系を解くのに使えたりします。もちろんそれだけではありませんが、微分方程式の一般解を求めるのに使えるだけでも面白いですよね。数えきれないほどの使い道があります(たぶん)。

マルサス方程式や調和振動子のように、現象を微分方程式系で表すことができます。そして微分方程式系で表したモデルは線形空間の知識を応用して解析できたりします(出来なかったりすることもあると思いますが)。異なる数学要素が上手くつながっていて、講義で知ったときは少し感動しました。

長々と説明してきましたが筆者は数学は未だによくわかってません。しかし、楽しい!(ここ重要)。筆者の学科・コースではこのような講義が中心となって行われています。しかし人気がありません。なので興味を持たれた方はぜひ編入先の候補として考えてみてはいかがでしょうか。

最後までお読みいただきありがとうございました!
今回説明を省略した箇所については後日、別の記事として書こうと思いますのでよければそちらもどうぞ。

iPad+Apple Pencil 使用感メモ その1

iPadを購入した件

iPadを購入しました!2日程使用したので今のところの使用感を書いてみます。

購入したのは次の3つです

iPad(第6世代) 32GB Wifi モデル ・スマートカバー ・Apple Pencil

iPadでもApple Pencilが対応しているので今回購入するに至りました。

モデルについて

 WifiモデルにするかWifi Cellularモデルにするか悩みました。 格安SIMを買うのかポケットWifiを契約するのかで、そこまで価格が 変わらないなと思い、必要になったらポケットWifiサービスをどこかで 契約しようと考え、お安いWifiモデルにしました。

ApplePencil と iPad で書いてみた感想

 さて、書き味についてですが、フィルム等つけていないデフォルトの状態ですが 若干の遅延を感じなくはありませんが、書くうえでほとんど影響ない程度です。

入れたアプリについて

 書き味についてすごく短くまとめましたが、実際の使用感に影響を与える割合としては アプリの比重が大きいように感じます。今のところ次の2つを併用してます

・NoteShelf 2 ・GoodNote4

 どちらもノートを作成、書くという順序は変わりませんが細々とした部分に違いを感じます

NoteShelf 2

Noteshelf 2

Noteshelf 2

  • Fluid Touch Pte. Ltd.
  • 仕事効率化
  • ¥1,200

 大学の講義のノートを取ったり、本の内容をまとめる作業はこのアプリを使っています。 アプリを開いた時の画面は次のようになっています。

f:id:yanchof:20180517224010j:plain

 ノートのカバーデザインは自由に選べます。右上のプラスマークから新規ノートの作成や ドキュメントのインポートができます。

f:id:yanchof:20180517224806p:plain

 ノートの画面ですが画像のようになっており、ペンの種類の切り替えも少ない動作でできるので不便さはあまり感じません。罫線付きのテンプレートも用意されていて、講義の時は罫線付き、メモ用途では白地というように使い分けています。また、右上の項目をタッチすると、複数ページのプレビューを出すことができ、目当てのページをすぐに出すことができます。

 また、図形の描画ができるのですが、図形モードをオンにした状態で雑に円を描くと綺麗な円に直してくれたり、適当に線を引くと直線に直してくれたりと、作図が楽になり、紙に定規とペンで書く場合よりも快適です。

 個人的な難点としては、書籍のPDFをインポートしたときにノートの表紙を書籍の表紙にしてほしいくらいです。(方法があるかもしれませんが、少なくともインポートした時点では表紙はアプリ側で用意されました)

 細かいカスタマイズなしにパッと表紙をそろえてほしいというのがユーザとしてアプリに望むことなので、難点としてあげました。しかし、紙のノートの代用としては十分な機能を備えていると思います。ルーズリーフバインダーを2つ、講義用に持ち歩いていましたが、iPad一つでまかなえるため、移動もとても楽です。

GoodNotes 4

GoodNotes 4

GoodNotes 4

  • Time Base Technology Limited
  • 仕事効率化
  • ¥960

先ほど挙げたPDFのインポート周りのことがあり、別のアプリも試してみようと思って入れました。

f:id:yanchof:20180517232213j:plain

(一部黒塗りですが)アプリを開いた画面です。カテゴリの設定が行えます。この機能についてはまだ使っていないので後日また書こうと思います。

NoteShelfとの違いですが、ちょうど黒塗りになっていて見えませんが、書籍のPDFをインポートすると、ちゃんと書籍の表紙が反映されます。なにも設定をいじらずとも期待した通りの動作を提供してくれるというのが、アプリのユーザとしてのスタンスですのでとても嬉しいポイントです。

GoodNotesの難点としては、ペンの切り替えがNoteShelfに比べて不自由かなというところです。NoteShelfと同様に、ペン・マーカー・消しゴムが用意されていますが、ペンの種類の変更にかかる操作数がちょっとだけ多かったりすることが使っていると気になります。

まとめ

 アプリの難点を挙げましたが、本当に些細な違いしかありません。しかし、ペンで書くという慣れた操作をデジタルに移行すると、思った通りの動作に求める即時性のハードルがどうしても高くなってしまいます。

 とは言っても、今のところ紙のノートの代用として十分なパフォーマンスだと思うので、またしばらく使ってみた感想を書こうと思います。

C++学習記録 スレッド 編1

大学に入学しました。やんちょふです。

しばらくPythonで数値処理ばかりしており、C++の勉強をさぼっていましたので、学習を開始しました。学習メモを不定期で更新したいと思います。

今回はスレッドです。C++11から追加された、マルチスレッドを扱うライブラリの使い方を試してみました。以下コードです。

#include <iostream>
#include <future>
#include <thread>

void Wait(int c) {
  std::this_thread::sleep_for(std::chrono::seconds(c));
}

int main() {
  std::vector<std::thread> threads;
  std::chrono::system_clock::time_point start, end;

  //multi thread
  start = std::chrono::system_clock::now();
  for (int i = 0; i < 3; i++) {
    threads.push_back(std::thread([] { Wait(3); }));
  }
  for (auto &th : threads) {
    th.join();
  }
  end = std::chrono::system_clock::now();
  double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
  std::cout << "multi: " << elapsed << std::endl;

  //single thread
  start = std::chrono::system_clock::now();
  for (int i = 0; i < 3; i++) {
    Wait(3);
  }
  end = std::chrono::system_clock::now();
  elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
  std::cout << "single: " << elapsed << std::endl;

  return 0;
}

3つのスレッドでそれぞれ3秒待たせた場合と、一つのスレッドで3秒を3回分待たせるプログラムです。これを実行すると結果は次のように出力されました。

multi: 3003
single: 9040

並列に処理が行われていることが確認できました。

以上のようにしてマルチスレッドを書くことができましたが、ソースコードの説明をざっくりとします。

まず次のコードです。

std::this_thread::sleep_for(std::chrono::seconds(c));

関数名そのままですが、指定した秒数待機させるコードです。chronoは時間に関するヘッダです。上記のソースコードでは単位として秒を指定しており、またmain関数内ではミリ秒を指定しています。このほかにマイクロ秒(microseconds)、分(minutes)、時間(hours)が指定できます。

this_thread名前空間は現在のスレッドに関する情報にアクセスするグループとなっています。今回使用したsleep_for関数以外にsleep_until関数などがあります。

main関数内では処理開始前と開始後とで以下のコードを記述しています。

 start = std::chrono::system_clock::now();
//何らかの処理
 end = std::chrono::system_clock::now();
double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

duration_cast関数は入力した値を、指定した時間単位に変換します。もとの値よりも分解能が小さい単位にしか変換できない点が注意です。(Wait関数ではミリ秒単位にすればよかったと思いましたが動きがわかったのでこれで良しとします)

今回は以上です。

実行環境
OS : Windows10
コンパイラ : mingw gcc 7.3.0

【Python3】モンティホール問題

ふとモンティホール問題について気になったので、スクリプトを書いて実験してみました。

#coding: utf-8
import random

def set_door():
    rand_num = random.randint(0, 2)
    if rand_num == 0:
        door = (1, 0, 0)
    elif rand_num == 1:
        door = (0, 1, 0)
    else:
        door = (0, 0, 1)
    return door

def pre_open_door(door, selected):
    for i in range(0, 3):
        if i != selected and door[i] == 0:
            return i

def re_select_door(door, opened, selected):
    for i in range(0, 3):
        if i != selected and i != opened:
            return i

if __name__ == "__main__":
    count = 0
    select_right_count = 0
    reselect_right_count = 0

    for i in range(1000000):
        door = set_door()
        select = random.randint(0, 2)
        opened = pre_open_door(door, select)
        re_select = re_select_door(door, opened, select)
        if door[re_select] == 1:
            reselect_right_count += 1
        if door[select] == 1:
            select_right_count += 1
        count += 1

    print("reselect:", reselect_right_count/count)
    print("non reselect:", select_right_count/count)

実験結果

  • 1回目
    reselect: 0.666524
    non reselect: 0.333476

  • 2回目
    reselect: 0.666154
    non reselect: 0.333846

  • 3回目
    reselect: 0.665123
    non reselect: 0.334877

再選択する場合の確率は0.6666...で再選択しない場合の確率は0.3333...なので 実験結果から正しい確率(に近い値)が得られています。

プログラミング初心者なのであまりきれいなコードではありませんが、とりあえず モンティホール問題の正解確率が出せたので今回はこれで良しとします。すっきりした。

続きを読む