From ce094c4af2b7cfca2405b4b939c79e473a3e7c96 Mon Sep 17 00:00:00 2001 From: chvmvd Date: Sun, 15 Jan 2023 21:21:25 +0900 Subject: [PATCH 1/4] Update error article --- docs/02algorithms/09error/index.mdx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/02algorithms/09error/index.mdx b/docs/02algorithms/09error/index.mdx index da064e113..240316998 100644 --- a/docs/02algorithms/09error/index.mdx +++ b/docs/02algorithms/09error/index.mdx @@ -21,7 +21,11 @@ $12345 = \underbrace{1.2345}_{\text{仮数部}}\times 10^{\overbrace{-4}^{\text{ まずは、10 進数で考えましょう。 例えば、$\frac{1}{3}$ は有限桁の小数では表せません。$0.3$ も $0.33$ も $0.333$ も $\frac{1}{3}$ の近似でしかありません。 -たとえ、 $0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333$ のようにしても同じことです。 +たとえ、次のようにどんなに桁を大きくしても有限である限り近似には変わりません。 + +$$ +0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 +$$ 2 進数においても同じことが起こります。 10 進数の $0.1$ を 2 進数で表すことを考えます。 @@ -37,11 +41,11 @@ $$ このように $0.1_{(10)}$ は 2 進数では有限桁で表せません。 -コンピューターは、有限桁しか表現できないことから、有効桁以降を切り捨てます。これによって、誤差が出るのが丸め誤差です。 +無限桁を扱うことはできないので、有効桁以降を切り捨てることになります。これによって、誤差が出るのが丸め誤差です。 ## 桁落ち -有効数字を 7 桁で $\sqrt{1001}-\sqrt{999}$ を計算することを考えます。 +有効数字 7 桁で $\sqrt{1001}-\sqrt{999}$ を計算することを考えます。 $$ \sqrt{1001} = 31.638584\dots \simeq 31.63858 @@ -57,11 +61,11 @@ $$ \sqrt{1001}-\sqrt{999} \simeq 0.03162 $$ -このようにすると、有効数字が 4 桁になってしまいます。 +このようにすると、有効数字が 4 桁に減ってしまいます。 このように値がほぼ同じ数値同士で減算をしたときに有効桁数が減少することによって生まれる誤差が桁落ちです。 -今回の場合は、回避策があります。 +実は今回の場合は、回避策があります。 $$ \begin{align*} @@ -74,7 +78,7 @@ $$ \end{align*} $$ -これで、桁落ちを回避できます。 +今回の場合ならば、これで桁落ちを回避できます。 ## 情報落ち @@ -98,7 +102,7 @@ $$ となって正しく計算できません。 -次のように、何度も足す場合には計算結果が変わってきてしまいます。 +次のように、何度も足す場合には深刻になってきます。 $$ \begin{align*} From 0a92ae94f0ab209f2734570477ae024ba11c36b2 Mon Sep 17 00:00:00 2001 From: chvmvd Date: Sun, 15 Jan 2023 21:37:59 +0900 Subject: [PATCH 2/4] Update gaussian-elimination article --- .../10gaussian-elimination/index.mdx | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/02algorithms/10gaussian-elimination/index.mdx b/docs/02algorithms/10gaussian-elimination/index.mdx index 2452a35af..6401d4de1 100644 --- a/docs/02algorithms/10gaussian-elimination/index.mdx +++ b/docs/02algorithms/10gaussian-elimination/index.mdx @@ -83,11 +83,11 @@ $$ ## Gauss の消去法 -Gauss の消去法を使えば、システマティックに連立一次方程式を解くことができます。掃き出し法とも呼ばれます。 +線形代数で習う Gauss の消去法を使えば、システマティックに連立一次方程式を解くことができます。そのため、このアルゴリズムを用いれば連立方程式を解くプログラムを簡単に作ることができます。Gauss の消去法は、掃き出し法とも呼ばれます。 Gauss の消去法は、前進消去と後退代入の二段階から成ります。 -簡単に Gauss の消去法を説明しておきます。 +先に線形代数の復習として、簡単に Gauss の消去法を説明しておきます。 次のように $n$ 個の未知数 $x_1, x_2, x_3, \dots , x_n$ に対して、$m$ 個の方程式を考えます。 @@ -206,10 +206,10 @@ $$ \right. $$ -これで $x_n$ から順番に求めていくことで連立方程式を解くことができます。 +これで $x_n$ はすぐに求めることができます。さらに、$x_n$ の解を代入すれば、$x_{n-1}$ もすぐに求まります。これを繰り返していくことで、連立方程式を解くことができます。 -実際に連立方程式を解いてみましょう。 -Gauss の消去法を次の方程式系について行うと、以下のようになります。 +実際に具体的な連立方程式を解いてみましょう。 +Gauss の消去法を次の方程式系について行ってみます。 $$ \left\{ @@ -221,7 +221,7 @@ $$ \right. $$ -まずは、前進消去を行います。 +まずは、前進消去を行います。基本変形を繰り返して、行階段行列を作っていきます。 $$ \begin{alignat*}{2} @@ -305,15 +305,14 @@ $$ \right. $$ -Gauss の消去法を使えば、このようにシステマティックに連立方程式を解けます。 -これなら、プログラムを作るのは簡単そうです。 - :::info 次のように、前進消去の段階で行簡約行列を作れば、後退代入を行う必要がなくなります。これは、Gauss-Jordan の消去法と呼ばれます。 -しかし、実は先程のように行簡約行列まで計算しないで途中で止める Gauss の消去法の方が計算量が少なくなるので、Gauss の消去法を使って説明をしました。 +Gauss-Jordan の消去法の方が良さそうですが、実は先程のように行簡約行列まで計算しないで途中で止める Gauss の消去法の方が計算量が少し少なくなります。そのため、Gauss の消去法の方がよく使われます。 + +Gauss-Jordan の消去法で先程の連立方程式を解いてみます。 -行簡約行列は次のようになります。 +拡大係数行列に基本変形を繰り返すと、次のような行簡約行列が得られます。 $$ \tilde{B} = @@ -350,8 +349,8 @@ $$ これで連立方程式を解くことができました。 -実際に連立方程式を解いてみます。 -これを次の方程式系について行うと、以下のようになります。 +実際に具体的な連立方程式を解いてみます。 +Gauss-Jordan の消去法を次の方程式系について行っていきます。 $$ \left\{ @@ -464,7 +463,7 @@ $i$ 行、$i$ 列が pivot となるので、$i$ 行目を pivot の値で割っ 次は、後退代入を行います。 -$x_n$ は $d_n$ になります。求まった $x_n$ をそれよりも上の式に代入して、$b_{j, n}x_n(1\leq j\leq n-1)$ を右辺に移動させ、$d_j(1\leq j\leq n-1)$ の値を更新します。これを繰り返すと、後退代入ができます。 +$x_n = d_n$ になります。求まった $x_n$ をそれよりも上の式すべてに代入して、$b_{j, n}x_n(1\leq j\leq n-1)$ を右辺に移動させ、$d_j(1\leq j\leq n-1)$ の値を更新します。こうすると、$x_{n-1}$ が求まります。これを繰り返すと連立一次方程式が解けます。 プログラムは次のようになります。計算量は、$O(n^3)$ です。 @@ -474,7 +473,8 @@ $x_n$ は $d_n$ になります。求まった $x_n$ をそれよりも上の式 ## 部分ピボット選択 -次のような連立方程式を解こうとすると、次のようなエラーが出てしまいます。 +さきほどのプログラムを使えば、多くの様々な連立一次方程式が解けます。 +しかし、次の連立方程式を解くと、次のようにエラーが出てしまいます。 $$ \left\{ @@ -488,12 +488,12 @@ $$ -これは、前進消去の際に 0 で割る操作ができてしまったためです。 -これを解決するために、部分ピボット選択を行います。誤差を小さくする役割もあります。 +これは、前進消去の際に 0 で割る操作が起こってしまったからです。 +これを解決するために、部分ピボット選択を行います。部分ピボット選択には、誤差を小さくする役割もあります。 部分ピボット選択は、pivot 列の pivot 行以降の行で絶対値が最大になる行を pivot に使うように変形することです。 -先程の連立方程式なら、次のようになります。 +先程の連立方程式なら、次のように計算していきます。1 つ目から、2 つ目への変形が部分ピボット選択によるものです。 $$ \begin{alignat*}{2} @@ -556,7 +556,7 @@ $$ \end{alignat*} $$ -部分ピボット選択を使うと、次のようになります。 +部分ピボット選択を入れると、次のようなプログラムになります。 From d04a5491d44aff11b07132b66fc1900d1e12e81e Mon Sep 17 00:00:00 2001 From: chvmvd Date: Sun, 15 Jan 2023 21:58:50 +0900 Subject: [PATCH 3/4] Update dp article --- docs/02algorithms/11dp/index.mdx | 73 ++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/docs/02algorithms/11dp/index.mdx b/docs/02algorithms/11dp/index.mdx index 065e1437a..0051e0d8c 100644 --- a/docs/02algorithms/11dp/index.mdx +++ b/docs/02algorithms/11dp/index.mdx @@ -23,15 +23,15 @@ $$ \end{align*} $$ -### 再帰を使った場合 +### 再帰を使ったフィボナッチ数列 再帰を使ったプログラムは次のように作れました。 -しかし、これでは計算量が $O(2^n)$ なので `n` が小さければこれで問題ありませんが、`n` が大きくなると時間がかかりすぎます。このプログラムで `n = 40` とすると、実行するのに 1 分もかかりました。 +しかし、これでは計算量が $O(2^n)$ なので `n` が小さければこれで問題ありませんが、`n` が大きくなると時間がかかりすぎます。このプログラムで `n = 40` としたら、実行するのに 1 分もかかりました。 -原因は、次の図をみるとよくわかります。同じ数字を何度も計算してしまっています。たとえば、`fib(2)` は 3 回も計算しています。`n` が大きくなると大変です。 +原因は、次の図をみるとよくわかります。同じ数字を何度も計算してしまっています。たとえば、`fib(2)` は 3 回も計算しています。`n` がさらに大きくなると大変です。 ```mermaid flowchart @@ -51,7 +51,7 @@ flowchart H --> O["fib(0)"] ``` -### DP +### DP を使ったフィボナッチ数列 これを解決するのが、動的計画法(Dynamic Programming)です。DP とよく言われます。 @@ -61,19 +61,19 @@ DP には大きく分けて、二種類あります。トップダウン方式 #### トップダウン方式 -計算結果をメモ化して、行うのがトップダウン方式です。メモ化再帰とも呼ばれます。 +計算結果をメモ化して無駄な計算を省くのがトップダウン方式です。メモ化再帰とも呼ばれます。 -配列に計算結果をメモしておいて、すでに計算してあったらその値を利用します。 +配列に計算結果をメモしておいて、すでに計算してあったらその値を再利用します。 -メモ化したプログラムは次のようになります。これなら、計算量は $O(n)$ なので、`n = 40` どころか `n = 100` でも余裕です。 +メモ化してフィボナッチ数列を解くプログラムは次のようになります。これなら、計算量は $O(n)$ なので、`n = 40` どころか `n = 100` でも余裕で計算できます。 -冒頭の `memo = [-1 for _ in range(10000)]` は、リストの内包表記と呼ばれ、配列 `memo` を `-1` で埋める操作です。`[-1, -1, ..., -1]` となっています。 +冒頭の `memo = [-1 for _ in range(10000)]` は、リストの内包表記と呼ばれ、配列 `memo` を `-1` で埋める操作です。この操作で `memo` は `[-1, -1, ..., -1]` となっています。 #### ボトムアップ方式 -ボトムアップ方式は、`f(2)` を求めてから `f(3)` を求めるというように下から順番に求めていこうというものです。 +ボトムアップ方式は、`f(2)` を求めてから `f(3)` を求めて `f(3)` を求める…というように下から順番に求めていこうというものです。 プログラムは次のようになります。こちらも、計算量は $O(n)$ です。 @@ -86,22 +86,22 @@ DP には大きく分けて、二種類あります。トップダウン方式 > **部分和問題**(ぶぶんわもんだい)は、計算複雑性理論・暗号理論における問題で、与えられた $n$ 個の整数 $a_1,\dots,a_n$ から部分集合をうまく選んで、その集合内の数の和が与えられた数 $N$ に等しくなるようにできるかどうかを判定する問題である。NP 完全であることが知られている。 > -- [フリー百科事典『ウィキペディア(Wikipedia)』](https://ja.wikipedia.org/wiki/部分和問題) -例としては、3、4、6 を使って 10 を作れるかという問題であれば、4 と 6 を足せば、10 であるのでできるという答えになります。 +例としては、3、4、6 を使って 10 を作れるかという問題であれば、4 と 6 を足せば、10 であるので**できる**という答えになります。 -### 全探索 +### 全探索を使う -この問題は、全探索すれば解くことは可能です。$a_i(1\leq n)$ を含めるか含めないかの 2 通りずつがあるので、計算量は、$O(2^n)$ です。 +この問題は、全探索すれば解くことは可能です。$a_i(1\leq n)$ を含めるか含めないかの 2 通りずつがあるので、計算量は、$O(2^n)$ です。説明が長くなるので、全探索のプログラムは飛ばします。 -### 動的計画法 +### 動的計画法を使う -#### 表を考える +動的計画法を使って、部分和問題を解いていきましょう。 -表を用意します。 - -$i$ 行 $j$ 列は、$\{a_k\}(1\leq k\leq i)$ の中からいくつかを使って $j$ をつくることができるかの真偽値とします。(真は 1、偽は 0 とします) +#### DP テーブルを作る 3、4、6 を使って 10 を作れるかという問題を考えます。 +動的計画法は、表を作って考えるので次のように表を用意します。 + | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | | --------------------------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | $\varnothing$ | | | | | | | | | | | | @@ -109,9 +109,12 @@ $i$ 行 $j$ 列は、$\{a_k\}(1\leq k\leq i)$ の中からいくつかを使っ | $\{a_1,a_2\}=\{3,4\}$ | | | | | | | | | | | | | $\{a_1,a_2,a_3\}=\{3,4,6\}$ | | | | | | | | | | | | -0 行目を考えます。 +表の $i$ 行 $j$ 列は、$\{a_k\}(1\leq k\leq i)$ の中からいくつかを使って $j$ をつくることができるかの真偽値とします。(真は 1、偽は 0 とします) + +まず 0 行目を考えます。 $0$ 行 $j$ 列は、$\varnothing$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。 +0 しか作れません。 よって、次のようになります。 @@ -125,6 +128,7 @@ $0$ 行 $j$ 列は、$\varnothing$ の中からいくつかを使って $j$ を 1 行目を考えます。 $1$ 行 $j$ 列は、$\{a_1\}=\{3\}$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。 +0 と 3 なら作れます。 よって、次のようになります。 @@ -138,6 +142,7 @@ $1$ 行 $j$ 列は、$\{a_1\}=\{3\}$ の中からいくつかを使って $j$ 次に、2 行目を考えます。 $2$ 行 $j$ 列は、$\{a_1,a_2\}=\{3,4\}$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。 +0 と 3、4、7 が作れます。 よって、次のようになります。 @@ -151,6 +156,7 @@ $2$ 行 $j$ 列は、$\{a_1,a_2\}=\{3,4\}$ の中からいくつかを使って 次に、3 行目を考えます。 $3$ 行 $j$ 列は、$\{a_1,a_2,a_3\}=\{3,4,6\}$ の中からいくつかを使って $j$ をつくることができるかの真偽値となります。 +0 と 3、4、6、7、9、10 が作れます。 よって、次のようになります。 @@ -161,23 +167,24 @@ $3$ 行 $j$ 列は、$\{a_1,a_2,a_3\}=\{3,4,6\}$ の中からいくつかを使 | $\{a_1,a_2\}=\{3,4\}$ | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | | $\{a_1,a_2,a_3\}=\{3,4,6\}$ | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | -#### 漸化式を考える +#### 漸化式を作る -これを元に漸化式を作っていきます。 +表を元に漸化式を作っていきます。 $i$ 行目を考える時、$a_i$ を入れるか入れないかの二択になります。 -- $a_i$ を入れないときは、一個上のセルの値の真偽値そのままです。つまり、$i-1$ 行 $j$ 列の真偽値となります。 -- $a_i$ を入れるときは、$a_k(1\leq k\leq i-1)$ を使って、**$j-a_i$** を作ることができていれば、$j$ を作れそうです。例えば、$3$ と $4$ を使って、$10-6=4$ が作れていれば、$10$ を作ることができます。 - そうすると、$i-1$ 行 $j-a_i$ 列の真偽値となります。 +- $a_i$ を入れないときは、何も変わらないので一個上のセルの値の真偽値そのままです。つまり、$i$ 行 $j$ 列の真偽値は、$i-1$ 行 $j$ 列の真偽値となります。 +- $a_i$ を入れるときは、$a_k(1\leq k\leq i-1)$ を使って、**$j-a_i$** を作ることができていれば、それに $a_i$ を加えることで $j$ を作れそうです。 + 例えば、$3$ と $4$ を使って、$10-6=4$ が作れていれば、それに $6$ を加えることで $10$ を作ることができます。 + そうすると、$i$ 行 $j$ 列の真偽値は、$i-1$ 行 $j-a_i$ 列の真偽値となります。 -そうすると、次の漸化式が作れます。 +これらをまとめると、次の漸化式が作れます。 $$ \mathit{dp}[i][j]=dp[i-1][j]\lor dp[i-1][j-a_i] $$ -ここで、$j-a_i<0$ のことを考えると、漸化式は次のようになります。 +ここで、$j-a_i<0$ のことまで考えると、漸化式は次のようになります。 $$ \mathit{dp}[i][j]= @@ -187,7 +194,7 @@ $$ \end{dcases} $$ -#### プログラムを書く +#### プログラムを作る まず、表を次のように初期化します。 @@ -222,15 +229,17 @@ $$ > **ナップサック問題**(ナップサックもんだい、Knapsack problem)は、計算複雑性理論における計算の難しさの議論の対象となる問題の一つで、$n$ 種類の品物(各々、価値 $v_i$、重量 $w_i$)が与えられたとき、重量の合計が $W$ を超えない範囲で品物のいくつかをナップサックに入れて、その入れた品物の価値の合計を最大化するには入れる品物の組み合わせをどのように選べばよいか」という整数計画問題である。同じ種類の品物を 1 つまでしか入れられない場合($x_i\in \{0, 1\}$)や、同じ品物をいくつでも入れてよい場合($x_i$ は 0 以上の整数)など、いくつかのバリエーションが存在する。 > -- [フリー百科事典『ウィキペディア(Wikipedia)』](https://ja.wikipedia.org/wiki/ナップサック問題) -$x_i\in \{0,1\}$ とした、0-1 ナップサック問題を解いてみてください。 +ここでは、$x_i\in \{0,1\}$ とした、0-1 ナップサック問題を解いてみてください。 -表を用意します。 +まずは先ほどと同様に表を考えます。 + +$v_1=2,v_2=3,v_3=6,w_1=2,w_2=3,w_3=5,W=10$ として考えてみます。 $i$ 行 $j$ 列は、重さが $j$ 以下になるように $i$ 番目までの品物の中からいくつかをナップサックに入れたときの価値の最大値とします。 -$v_1=2,v_2=3,v_3=6,w_1=2,w_2=3,w_3=5,W=10$ として考えてみます。 +表は次のようになります。 | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | @@ -241,8 +250,8 @@ $v_1=2,v_2=3,v_3=6,w_1=2,w_2=3,w_3=5,W=10$ として考えてみます。 これから、漸化式を考えます。 -- $i$ 番目の品物を使わなければ、一個上のセルの値そのままです。つまり、$i-1$ 行 $j$ 列の値です。 -- $i$ 番目の品物を使う場合は、$i-1$ 行 $j-w_i$ 列の値に $v_i$ を足したものになりそうです。 +- $i$ 番目の品物を使わなければ、一個上のセルの値そのままです。つまり、$i$ 行 $j$ 列の値は、$i-1$ 行 $j$ 列の値です。 +- $i$ 番目の品物を使う場合は、$i$ 行 $j$ 列の値は、$i-1$ 行 $j-w_i$ 列の値に $v_i$ を足したものになりそうです。 これから、漸化式を作ると次のようになります。 @@ -254,7 +263,7 @@ dp[i][j]= \end{dcases} $$ -プログラムは次のようになります。計算量は、$O(nW)$ です。 +この漸化式を使うとプログラムは次のようになります。計算量は、$O(nW)$ です。 From 6bfa8d621a6be6283df1b23e6f67f681a5e33e2a Mon Sep 17 00:00:00 2001 From: chvmvd Date: Sun, 15 Jan 2023 22:03:21 +0900 Subject: [PATCH 4/4] Update postscript --- docs/03postscript/index.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/03postscript/index.mdx b/docs/03postscript/index.mdx index 2ec6e44cd..9fedae90a 100644 --- a/docs/03postscript/index.mdx +++ b/docs/03postscript/index.mdx @@ -4,12 +4,12 @@ sidebar_position: 3 # あとがき -当初の予定よりもだいぶ詳しめに作ったので、だいぶ時間がかかりました… +当初の予定よりもだいぶ詳しめに作ったので、だいぶ時間がかかってしまいました… 今後誰かの役に立つとうれしいですね。 -一応、誤植などが見つかれば来年以降も修正するつもりなので、はじめのページの誤植報告フォームからお知らせください。 -このシケプリを消す予定はないので、GitHub がサービス終了しない限りこのシケプリが消えることはないはずです。少なくともこのシケプリのソースコードは公開しているので、どこかしらには残ると思います。 +一応、誤植などが見つかれば来年以降も修正するつもりなので、はじめのページの誤植報告フォームからいつでもお知らせください。 +また、このシケプリを消す予定はないので、GitHub がサービス終了しない限りこのシケプリが消えることはないはずです。 -今までの内容がわかっていれば、プログラミングの基礎はマスターできていると思いますし、アルゴリズムについても基本的なことは網羅できているはずです。今後、他のプログラミング言語を勉強しなければならなくなったりしても、すぐに習得できると思います。 +今までの内容がわかっていれば、プログラミングの基礎はマスターできていると思いますし、アルゴリズムについても基本的なことは網羅できているはずです。今後、他のプログラミング言語を勉強しなければならなくなったときなどに、アルゴリズム入門の内容を思い出してください。 それでは、試験頑張ってください。さようなら。