疑念は探究の動機であり、探究の唯一の目的は信念の確定である。

数学・論理学・哲学・語学のことを書きたいと思います。どんなことでも何かコメントいただけるとうれしいです。特に、勉学のことで間違いなどあったらご指摘いただけると幸いです。 よろしくお願いします。くりぃむのラジオを聴くこととパワポケ2と日向坂46が人生の唯一の楽しみです。

第1回: Pythonで最大公約数と最小公倍数のプログラムを書いた

どうも僕です。

Python(パイソン)を勉強し始めたので、いろいろなプログラムを書いています。今回は最大公倍数と最小公倍数を求めるプログラムを書きました。プログラムを書きながら、Pythonの使い方を勉強しています。私は数年前にC言語を少し授業で勉強したっきりの、まるっきりの素人です。一番最初には中央値を求めるプログラムを書きました。しかし、それは別の日にブログに書きます。最大公倍数はGreatest Common Divisorというので、gcdという関数で定義します。同様に、最小公倍数はLeast Common Multipleというのでlcmという関数で定義しました。例えば、{\text{gcd}(24, 18)} と入力すれば、{\text{gcd}(24, 18) = 6}と表示される関数です。また、{\text{lcm}(24, 18) = 72} と表示されます。結論から言えば、そのようなプログラムは次のようにしてできました。今回はそれを解説したいと思います。

f:id:yoheiwatanabe0606:20180718003246p:plain

f:id:yoheiwatanabe0606:20180718081526p:plain

最大公倍数と最小公倍数についての初等整数論 

最大公約数と最小公倍数

まず最大公倍数と最小公倍数の知識についてまとめます。簡単のために2つの整数 {m, n} についてのみ考えます。{m, n}最大公倍数(greatest common divisor)とは {m, n} の共通の約数のうち最も大きい数のことです。例えば、{(m, n) = (24, 9)} つまり {m = 24, n = 9} のとき、{m = 24} の約数は {1, 2, 3, 4, 6, 8, 12, 24} であり、{n = 9} の約数は {1, 3, 9} であるので、このときの最大公倍数は {3} です。{m, n} の最大公倍数を {\text{gcd}(m, n)} と書きましょう。すると、先ほどの例は {\text{gcd}(24, 9) = 3} となります。

同様に、{m, n}最小公倍数(least common multiple)とは {m, n} を約数とするような数のうち最小のものをいいます。例えば、{m = 24, n = 9} の最小公倍数は {72} です。というのも、{72 = 3\cdot 24 = 12\cdot 9} となり、{72}{24}{9} の約数をもつ最小の数です。ここでドット {\cdot} は掛け算を表しています。{m, n} の最小公倍数を {\text{lcm}(m, n)} と書きましょう。すると、{\text{lcm}(24, 9) = 72} となります。

 

最大公倍数と最小公倍数の求め方: ユークリッド互除法

整数 {m, n} の最大公約数 {\text{gcd}(m, n)} と最小公倍数 {\text{lcm}(m, n)} は次のような関係があります。

{\text{gcd}(m, n)\cdot \text{lcm}(m, n) = m\cdot n}

実際、{(m, n) = (24, 9)} のとき、{\text{gcd}(24, 9) = 3,\, \text{lcm}(24, 9) = 72} より、

{3\cdot 72 = 216 = 24\cdot 9} となります。

このような関係があるので、最大公倍数がわかれば最小公倍数を求めることができます。同様に、最小公倍数がわかれば最大公倍数を求めることができます。ここでは最大公倍数をユークリッド互除法と呼ばれるアルゴリズムによって求めて、それから最小公倍数を求めます。ユークリッド互除法(ごじょほう)は次のようなものです。2つの整数の大きい数を小さい数で割り、その余りがあったときは、その小さな数をその余りで割り、それが余りが0になるまでおこない、余りが0のときの割られたものがその2数の最大公約数であるというものです。

 例えば、{(m, n) = (24, 9)} とすると次のようになります。

(0) {24 = 2\cdot 9 + 6}

(大きい数 {24} を小さな数 {9} で割ると、{6} が余りになります。)

(1) {9 = 1\cdot 6 + 3}

(小さな数 {9} を(0)の余り {6} で割ると、余りが {3} となります。)

(2) {6 = 2\cdot \underline{3} + 0}

(小さな数 {6} を(1)の余り {3} で割ると、余りは {0} となります。)

(3) アンダーラインのある数 {\underline{3}} が求める最大公約数です。  

 

一般的に表せば次のようになります。整数 {m \geq n} に対して、

(0) {m = q_0\cdot n + r_0\,\, (0\leq r_0\prec n)}

(1) {n = q_1\cdot r_0 + r_1\,\, (0\leq r_1\prec r_0)}

(2) {r_0 = q_2\cdot r_1 + r_2\,\, (0\leq r_2\prec r_1)}

{\ldots} 

(k-1) {r_{k-3} = q_{k-1}\cdot r_{r_2} + r_{k-1}\,\, (0\leq r_{k-1}\prec r_{k-2})}

(k) {r_{k-2} = q_{k}\cdot \underline{r_{k-1}} + 0}

 

{\text{gcd}(m, n) = r_{k-1}}

{\text{lcm}(m, n) = \frac{m\cdot n}{r_{k-1}}}

 

以上が最大公約数と最小公倍数を求めるプログラムに必要な数学的知識です。

 

 

Pythonのプログラムの基本知識

関数

2つの整数 {m, n} に対して、最大公約数 {\text{gcd}(m, n)} を求める関数を定義します。そのためにまずはPythonの関数について説明します。

それは例えば次のようなものです。

f:id:yoheiwatanabe0606:20180718031109p:plain

これは関数 {\text{function}(x) = x + 2} を表しています。{f(x) = x + 2}

例えば、この関数の変数 {x}{3} を代入すると、つまり {x = 3} とすると、

f:id:yoheiwatanabe0606:20180718031341p:plain

となり、{\text{function}(3) = 3 + 2 = 5} を返します。

ここで注意しなければならないことは、(i) コロン( : )を必ず書くことと(ii) 定義の次の行は必ず、空白を少なくとも1つは入れなければならないことです。

実際に、コロンを入れなかったり、定義の後にスペースを入れなかったら次のような、エラーが起こります。

f:id:yoheiwatanabe0606:20180718031832p:plain

 

1つめはコロン : を入れずに改行したためのエラーであり、2つめは改行した後、スペースを入れていなかったためのエラーです。

ちなみに、スペースは少なくとも1つあればいいのですが、慣習としてスペースは半角で4つのスペースが入れられます。

f:id:yoheiwatanabe0606:20180718032130p:plain

1つめはスペースが1つしかないもの。それでもよい。だが、慣習としてスペースは2つめのように半角で4つ開ける(開けられる)。

 

最後にreturnについてですが、returnがなくても関数は定義できますが、実際に {x} の値を代入したときに、returnがないと表示されません。returnの代わりにprintというものを使っても構いません。次の写真を参照にしてください。

f:id:yoheiwatanabe0606:20180718032837p:plain

function1はreturnがないので、{x = 3} を代入しても、計算はしているけれども、答え {5} が表示されません。

function2はreturnのあとに、カンマ( , )を入れることによって、答えが複数出ます。つまり {x = 3} であるため {(3 + 2, 3 + 3, 3 + 4)} が計算されたので、{(5, 6, 7)} という答えが表示されました。もしも、答えを縦に表示したいのならば、funtion3のようにprintを使えばいいです。returnは関数のときに使える(と思います。まだreturnはよくわかりません)。

 

さて、ここまでのテストでは変数は1つしか考えていませんでしたが、もちろん変数は何個でも構いません。今回使うのは {m, n} の2変数です。

f:id:yoheiwatanabe0606:20180718033530p:plain

 

計算の演算は足し算だけでなくさまざまあります。次の図を参照してください。

f:id:yoheiwatanabe0606:20180718033849p:plain

上から順に 和({+})、差({-})、積({\times})、商 or 割り算({/})、最後が剰余です。ここでは {3 \div 2 = 1 ... 1} の余り {1} を表示しています。

今回、関数 {\text{gcd}(m, n)}{\text{lcm}(m, n)} を定義するにあたって、積と商と剰余が使われています。

 

If文(条件文)

「もし~ならば、こうであり、もし~でないならば、しかじかである」というふうにしたいときは条件文(if)を使います。例えば、「もし、{x} が偶数ならば、EVEN(偶数)を表示して、{x} がそうでなければ(つまり奇数であるならば)、ODD(奇数)を表示する」という関数は次のようにできます。

f:id:yoheiwatanabe0606:20180718042315p:plain

ここで「{x} が偶数である」というのは「 {x}{2} で割った余りが {0} である」ということと同じです。したがって、剰余の記号%を使っています。

ただしPythonのとき等号(イコール)は通常とは異なり、==とイコールを2つつけます。 ですので、x%2 == 0と書かれています。ノットイコール {\neq} のときは、!=を使います。

 

While文(繰り返し文)

whileはある条件を満たすまで繰り返し計算を行うときに使われます。例えば、次のようなプログラムを考えます。

f:id:yoheiwatanabe0606:20180718050822p:plain

これは次のようなことが書かれています。

(I) {\tt{i} =  0} ---- {\tt{i}}{0} としろ({\tt{i}}{0} で定義しろ)。

(II) {\tt{while}\,\, \tt{i} \prec 10:} ---- {\tt{i}} が 10より小さいならば、以下のことをせよ。

(III) {\tt{i}**2} ---- {\tt{i}} の二乗をせよ(pythonでは {i^2}{i**2} で書く)。

(IV) {\tt{i} = \tt{i} + 1} ---- {\tt{i}} にプラス1をせよ。

(V) 繰り返し

(VI) {\tt{i}} が 10になれば止まる。

 

whileをさらに詳しく書くと次のようになります。

1) 初期値 {\tt{i}}{0} より、{\tt{while\,\, i} \prec 10} を満たす。

2) したがって、{\tt{i}**2} を計算する。つまり、{i^2 = 0^2 = 0} である。{0} が表示される。

3) {\tt{i} = \tt{i} + 1} より初期値 {\tt{i} = 0} に1を足す。 {\tt{i} = 0 + 1 = 1} つまり次の初期値を {\tt{i} = 1} にする。

最初の {\tt{i} = 0}{i_0 = 0} とすると、次の初期値は {i_1 = i_0 + 1 = 0 + 1 = 1} である。 

4) 初期値 {\tt{i} = 1} つまり {i_1 = 1}{10} より小さい。つまり、{\tt{while}} の条件を満たす。

5) したがって、{\tt{i}**2} を計算する。つまり、{i^2_1 = 1^2 = 1} である。よって、{1} が表示される。

6) 初期値 {\tt{i} = 1} に1を足す。つまり、{\tt{i} = 1 + 1 = 2} である。

この初期値を {i_2} とすれば、{i_2 = i_1 + 1 = 1 + 1 = 2} である。

7) {\tt{i} = 2 \prec 10} より、{\tt{while}} の条件を満たす。

8) したがって、{\tt{i} ** 2} を計算する。つまり、{i^2_2 = 2^2 = 4} である。よって、{4} が表示される。

9) 同様に繰り返される。

10)  初期値が {\tt{i} = 9} のとき、{\tt{i} \prec 10} を満たすので、{\tt{i**2} = 9^2 = 81} が計算されて表示される。

11) 初期値 {\tt{i} = 9} に1を足す。つまり、{\tt{i} = 9 + 1 = 10} である。この初期値を {i_{10}} とおけば、{\tt{while}} の条件 {i_{10} \prec 10} を満たさない。なぜなら、{10}{10} より小さくないから。

12) したがって、計算は止まる。

 

 

ここで計算は上から順に行われることに注意しよう。例えば、{\tt{while}} 以降を

(1)

{\tt{i} ** 2}

{\tt{i = i + 1}}

と書くのか

(2)

{\tt{i = i + 1}}

{\tt{i}**2}

によって答えが異なってきます。実際に試してみると、次のようになります。

f:id:yoheiwatanabe0606:20180718055059p:plain

(2)のほうは、初期値 {\tt{i} = 0} から次の初期値 {\tt{i} = \tt{i} + 1 = 0 + 1 = 1}  を計算して、それから {\tt{i**2} = 1^2 = 1} を計算して表示されています。

 

もしも、{\tt{i} = \tt{i} + 1} を設定しないと、初期値はずっと {\tt{i} = 0} であるため、{\tt{while}} 条件をいつまでも満たします。したがって、ずっと計算して無限ループをします。ですので、そのへんはしっかり確認してください。

今回の最大公約数を求めるプログラムを作る場合、大きい整数と小さい整数の剰余が {0} でない限り、ずっと計算し続けます。そのため有限回で必ず剰余が {0} になるということを保障しなければなりません。前節の {(m, n) = (24, 9)} の場合は2回目で----(2)----{6\div 3 = 2 ... 0} となります。それが任意の2つの整数でも成り立つことを示さなければなりません。実際、ユークリッド互除法が有限回でストップすることは初等整数論で示されます。ですので、無限ループに陥ることはありません。

  

Int・Max・Min 

最後に定義せずに使える特別な関数を説明します。まずはintです。これは小数に対して、その整数部分を出力する関数です。

f:id:yoheiwatanabe0606:20180718043849p:plain

もちろん、整数を入力したら整数が出力します。

maxおよびminは最大値と最小値を選ぶような関数です。 

f:id:yoheiwatanabe0606:20180718044811p:plain

 

以上が今回プログラムで使われるPythonの基本的な知識のまとめです。 

 

 

プログラムを作る

以上によって、数学的な知識とプログラムの知識をまとめました。最後にユークリッド互除法で最大公約数を求めるプログラムを定義します。最大公約数が求められたら最小公倍数も求められます。それは最初の見出しのプログラムです。このプログラムを解説します。

整数 {m, n} として、最大公約数 {\text{gcd}(m, n)} ができているとします。このとき、最小公倍数 {\text{lcm}(m, n)}{m\cdot n = \text{gcd}(m, n)\cdot \text{lcm}(m, n)} より、次のように定義することができます。

f:id:yoheiwatanabe0606:20180718064857p:plain

{\tt{int}} がついているのは、割り算を行なった結果、{8.0} のように小数第一位まで入ってしまうので、見やすくするために導入されています。

 

そこであとは最大公約数を求める関数 {\text{gcd}(m, n)} を定義します。

STEP 1  数の大小を決める

ユークリッド互除法をおこなうためには大きい数から小さい数を割らなければなりません。しかし、変数 {m, n} はどちらが大きいか小さいかの語順は関係なく代入されます。そのため、まずはじめに {\tt{x} = \text{max}(m, n) }{\tt{y} = \text{min}(m, n)} と定めます。以後はこの {\tt{x, y}} を変数として考えます。

 

STEP 2  もし、{\tt{x}}{\tt{y}} が割り切れるならば、{\tt{y}} を表示させる。

今後の議論のために、もし {\tt{x}}{\tt{y}} が割り切れるならば、{\tt{y}} と表示させるようにします。例えば、{\tt{x} = 30} であり、{\tt{y} = 15} のときは、割り切れるので、最大公約数は {15} です。ですので、 {\text{gcd}(m, n) = 15} と表示させます。

 

STEP 3  もし、{\tt{x}}{\tt{y}} が割り切れないならば、ユークリッド互除法によって、割り切れるまで繰り返す。

この箇所が一番難しいです。

まず「もし、{\tt{x}}{\tt{y}} が割り切れないならば」というのは、{\tt{if}} 文の {\tt{else:}} 以降のことです。つまり「もしそうでないならば」ということです。

次に「{\tt{x}}{\tt{y}} が割り切れるまで繰り返す」とは「割り切れないまで以下のことを繰り返せ」ということです。つまり、 {\tt{while\,\, x}}%{\tt{y! = 0:}} とします。

そして、{j-1} 回目 {r_{j-3} = q_{j-1}\cdot r_{j-2} + r_{j-1}}

から {j} 回目 {r_{j_2} = q_{j}\cdot r_{j-1} + r_{j}} を求めるときは、次のように書けばいい。

{\tt{z = x}}%{\tt{y}}

{\tt{x = y}}

{\tt{y = z}}

 

例えば、{(\tt{x}, \tt{y}) = (24, 9)} を考えてみましょう。

まず当然ながら{\tt{x}}%{\tt{y} = 24} % {9 = 6\neq 0} です。ですので、{\tt{while}} の条件を満たします。{\tt{x_0 = x = 24}}{\tt{y_0 = y = 9}} とおいて、{\tt{while}} 以下は次の通りです。

1) {\tt{z_0 = 24}}%{\tt{9 = 6}} 

2) {\tt{x_1 = y_0 = 9}}

3) {\tt{y_1 = z_0 = 6}}

4) {\tt{x_1}}%{\tt{y_1} != 0} かどうか調べる。{\tt{x_1}}%{\tt{y_1 = 9}}%{\tt{6 = 3}\neq 0} である。したがって、{\tt{while}} 条件を満たす。

5) {\tt{z_1 = x_1}}%{\tt{y_1 = 3}}

6) {\tt{x_2 = y_1 = 6}}

7) {\tt{y_2 = z_1 = 3}}

{\tt{x_2}}%{\tt{y_2}!= 0} かどうか調べる。すると、{6\div 3 = 2 ... 0} であり余りは {0} であるから、{\tt{while}} の条件を満たさない。

 

STEP 4  割り切れたならば、{\tt{z = x}}%{\tt{y}} を表示する。 

「割り切れたならば 」とは、ここでは「{\tt{x}}%{\tt{y != 0}}でないこと」つまり、{\tt{else}} です。

このとき、{\tt{z = x}}%{\tt{y}} を表示すればいいので、{\tt{return\,\, z}} とすればいいです。

 

以上で最大公約数のプログラムができました。

例えば、実装したら次のようになりました。

f:id:yoheiwatanabe0606:20180718080619p:plain

 

 

おわり

はじめてプログラムを書いてみました。少し面白いなと思いました。おそらくコンピュータ・サインエンスは「どのようなプログラムが有限回で終わるか」などを議論しているのだなと思いました。

今後もPythonでプログラムを書きます。見やすさも試行錯誤します。

 

 

追記 2018/07/19

最大公約数を求めるプログラム {\text{gcd}(m, n)} は次のようなものでもいい。

f:id:yoheiwatanabe0606:20180719192050p:plain

わざわざ {\tt{if}} 文を使わなくてもよい。その代わりに {\tt{return}}{\tt{z}} ではなく、{\tt{y}} にしなければならない(もし、{\tt{z}} とすると、例えば {x = 30, y = 15} のとき、{\text{gcd}(30, 15) = 15} とはならず、{\text{gcd}(30, 15) = z = x}%{y = 30}%{15 = 2} が表示されてしまうからである)。

こちらのプログラムの方が少しだけだがシンプルである。 最小公倍数 {\text{lcm}(m, n)} は同じプログラムである。

 

 

僕から以上