[logo] Web連載「数学ガールの秘密ノート」
Share

第321回 シーズン33 エピソード1
テトラちゃんのアルゴリズム講義(前編) ただいま無料

登場人物紹介

:数学が好きな高校生。

テトラちゃんの後輩。 好奇心旺盛で根気強い《元気少女》。言葉が大好き。

$ \newcommand{\ABS}[1]{\left|\mathstrut #1\right|} \newcommand{\KAKUDO}[1]{#1^\circ} \newcommand{\TT}[1]{\textrm{#1}} \newcommand{\ANGLE}[1]{\angle\textrm{#1}} \newcommand{\TRI}[1]{\triangle\textrm{#1}} \newcommand{\HIRANO}{\unicode[sans-serif,STIXGeneral]{x306E}} \newcommand{\SQRT}[1]{\sqrt{\mathstrut #1}} \newcommand{\TRUE}{\textbf{true}} \newcommand{\FALSE}{\textbf{false}} \newcommand{\FOCUS}[1]{\fbox{ $#1$ }} \newcommand{\GEQ}{\geqq} \newcommand{\LEQ}{\leqq} \newcommand{\NEQ}{\neq} \newcommand{\TRIANGLE}{\triangle} \newcommand{\REMTEXT}[1]{\textbf{#1}} \newcommand{\PS}[1]{\left(#1\right)} \newcommand{\SET}[1]{\{\,#1\,\}} \newcommand{\SETM}{\,|\,} \newcommand{\EMPTYSET}{\{\,\}} \newcommand{\REAL}{\mathbb R} \newcommand{\ZEE}{\mathbb Z} \newcommand{\LL}{\left\langle\,} \newcommand{\RR}{\,\right\rangle} \newcommand{\LLRR}[1]{\LL#1\RR} $

放課後の図書室

放課後。

いつものようには図書室に行くと、テトラちゃんがノートに何かを書きながらうなっていた。

「テトラちゃん、今日はどんな数学をやってるの?」

テトラ「あ、先輩……」

いつも元気なテトラちゃん、今日は反応がやや鈍いな。

「どうしたの、難しい数学に挑戦中?」

テトラ「い、いえ、そういうわけじゃないんですが……」

「テトラちゃんの歯切れがわるいのはめずらしいね。考えているところ邪魔しちゃったかなあ」

テトラ「難しい問題でもありませんし、そもそも数学でもないんです。いえ、数学になるんでしょうか……」

「もしよければ話を聞くよ。役に立てるかどうかわからないけど」

テトラ「ありがとうございます! 実はですね、先日双倉図書館(ならびくらとしょかん)で開かれた一般講座に参加したんですよ」

「そういえば、そんな話していたね。テーマは何だったの?」

テトラアルゴリズムです! 《リンクとストラクチャー》という副題が付いていて、 いろんなアルゴリズムのお話を聞いてきました。盛りだくさん過ぎてちょっと消化不良ですが……」

テトラちゃんはそういってお腹を撫でた。

学んだこと、お腹に入るんだろうか。

「おもしろかった?」

テトラ「はい。おもしろかったんですが、あとから配付資料を読み返してみると、 あちこちに『自分は理解していないな』と思うことが見つかったんです。 それから、何というんでしょうか……全体的にもやもやっとしていることもあります。 いえ、何をやってるかははっきりわかるんですよ。そんなに難しい話ではありませんでしたから。 でも、改めて考えると、何をやってるかがはっきりしなくなるんです。 だから、難しくて……」

「はっきりわかるけれど、はっきりしなくなる。 難しくはないけど、難しい。 それじゃ確かに……はっきりしなくて難しいね!」

テトラ「はい……わけがわからない悩みですみません」

そのときは、ちょっとしたアイディアを思いついた。

「うん、だったらね、テトラちゃんが講義をするっていうのはどう?」

テトラ「あ、あたしが……講義? どういうことでしょう」

「テトラちゃんは一般講座に出席していろいろ学んできたわけだよね。 だから、その成果を僕に講義するんだ」

テトラ「あたしが……先輩に講義する」

「そうそう。いまの段階だとテトラちゃんの方がずっとわかっているはずだけど、 テトラちゃんが講義をして教えてくれるなら、僕もその理解に近付けてうれしい。 そして、もしかしたらテトラちゃんの「もやもや」がどこにあるのかを一緒に考えられるかもしれないよね!」

テトラ先輩に教わるのではなく、先輩に教える……あたしが?」

「よろしくお願いします。テトラ先生!」

テトラ「わ、わかりました。けれど、何日か準備させてください……」

テトラちゃんのアルゴリズム講義は、一週間後に始まることになった。

【CM】

ユーリ「アルゴリズムに関するユーリたちの活躍は『数学ガール/乱択アルゴリズム』にも出てきますぞ!」

テトラちゃんの講義開始

一週間後。

テトラ「そ、それではお話します」

「よろしくお願いします」

テトラ「まずですね。あたし、先輩にすごく感謝しています!」

「え? まだ何にも話してないのに?」

テトラ「あたし《先輩に講義する》というつもりで準備をしていたんですが、 そうしたら、不思議なことがたくさん起きたからです」

「へえ……」

テトラ「配付資料を読むときに《書かれている内容を理解しよう》という気持ちだけじゃなくて、 《書かれている内容を理解した上で、先輩にちゃんと説明できるようにしよう》という気持ちになりました。 そうすると、不思議なことに、自分が何をわかっていないかがはっきりしてきたんですよ」

「それはおもしろいね! ああ、でも、それは納得いく話だなあ」

テトラ「えっ?」

「ほら、僕たちはよく数学の話をするよね。 そのときに相手に伝わるように説明しようとする。 でもなかなかうまくいかないし、伝わらないこともある」

テトラ「はい……」

「できるだけはっきり説明しようと思うと、 自分自身の理解があやふやなところが浮き彫りになること、あるねえ」

テトラ「そうなんですか、先輩も?」

「うん。テトラちゃんが放つ《根源的な質問》もすごく助かる。 それに答えることで、自分の理解が試されている感じがするんだ」

テトラ「あ、あたしは理解が遅いので……でも、そういっていただけるのはうれしいです」

「じゃあ、テトラちゃんの……テトラ先生の講義を聴くよ」

最大値を求めるアルゴリズム

テトラ「双倉図書館の一般講座で学んだことを、あたしなりにまとめたので、 その順番でお話しします」

「うん。すごく楽しみだよ!」

テトラ「まずこちら、List 1のアルゴリズムを見てください」

List 1

テトラちゃんが提示したMAX-VALUEというアルゴリズムをざっと眺めた。

ああ、これはきっと、 $$ \LLRR{a_1,a_2,a_3,\ldots,a_n} $$ で与えられた数列の中から最も大きな数を見つけ出すんだろうな。

whileからend-whileまでの繰り返しで、 $m < a_k$ という比較を行っている。

きっと最大値が $m$ に代入されるんだろう。

「なるほど。List 1は僕にも理解できるよ。最大値を求めるアルゴリズムだね」

テトラ「はい。あたしもそう考えたんですが、あさはかでした」

「あさはか?」

テトラ「あたしは、書かれたものをちゃんと読まなかったんです。 確かにこのアルゴリズムは、 $a_1,a_2,a_3,\ldots,a_n$ という数列が与えられたときに、 要素の最大値を求めるものなんですが、重大な誤りがあるんです!」

「重大な誤り……」

テトラ「はい。重大な誤りです」

はちょっと焦った。

確かにも、List 1の内容をろくに読まずに口を開いてしまった。 あさはかだった。《重大な誤り》とは何だろう……

「……ああ、わかったよ。これじゃkの値はずっと1のままだ。増えていかないね」

テトラ「はい、そうなんです。kの値はずっと1のままですから、4行目の、 $$ k \LEQ n $$ という条件はいつまでも成り立ちます。つまりList 1のアルゴリズムは無限ループに入ってしまうことになります!  その《重大な誤り》を修正したものがこちらのList 2になります」

List 2

「そうだね。List 2では7aの行でkを増やしている」

テトラ「講師の先生から、やんわりと注意がありました。 アルゴリズムはパパッと読んでパパッと理解しようと考えないこと、だそうです」

「確かに……」

テトラ「では、仕切り直して、お話を始めます。 List 2は、 $$ \LLRR{a_1,a_2,a_3,\ldots,a_n} $$ という数列と、要素数 $n$ が与えられたとして、 最大値を求めるアルゴリズムです。 やっていることの概略はこうです」

  • 1: $\LLRR{a_1,a_2,a_3,\ldots,a_n}$ という数列とnが入力です。
  • 2: 変数mに $a_1$ を代入します。このmが最大値になる予定です。
  • 3: 変数kに1を代入します。このkを使って要素を一つずつ調べるんです。
  • 4〜8: kがn以下の間、kを1ずつ増やして繰り返します。
  • 5〜7: もしもmよりも $a_k$ が大きかったら、それが新たな最大値候補となります。
  • 7a: kを1増やします。
  • 9: ここでmは最大値になっていますので、これが出力となります。

「うんうん、そうだね。特に気になるところはないよ」

テトラ「え?」

「え?」

テトラ「あたしは一つ気になりました。これは《無駄な比較》をしていますよね?」

「無駄な比較って? ……ああ、ほんとだね! 最初の比較のこと?」

テトラ「はい、そうです。 $k = 1$の時点、List 2の5行目で $$ m < a_k $$ の比較をするとき、 $$ m = a_1 $$ であることは最初からわかっています。 だって、2行目で代入したばかりですから!」

「確かにね……テトラちゃんはそれに気付いてたんだ」

テトラ「はい。手を挙げて講師の先生に質問しました。 kの値は2から始まってもいいですか……と。 そうしたら『よく気がつきましたね』と褒めていただきました!」

「……」

テトラ「ですから、List 3のようにすれば、比較の回数を減らすことができます。1回分だけですけれど」

List 3

「なるほど、確かにそうだね。 うん、テトラちゃん。僕はちょっと《反省》してるよ」

テトラ「えっ?」

「僕は、どこか先輩気取りでいて、どこか気が緩んでいたみたいだよ。 もっと真剣に取り組むべきだったね。ごめん」

テトラ「そ、そんなことおっしゃらないでください! こうやって、 聞いていただけるだけでありがたいんですから……そ、 それでですね。最大値を求める話は、ここから始まるんです。 このアルゴリズムをどのように実装するか、です」

「実装?」

配列を使った実装

テトラ「はい。今回の講座では、 アルゴリズムがどのようなものかというお話から一歩進んで、 データ構造というものにも触れていました」

「データ構造……」

テトラ「data structureです。その最も簡単なものが配列です。 配列というものを使って数列を表現し、 先ほどの最大値を求めるアルゴリズムを実装した擬似コードがList 4です」

List 4

「A[k] という表記は $a_k$ を表しているんだよね」

テトラ「はい、そうです。配列というのは、 同じ種類のデータが、 コンピュータのメモリ上に一列に並んでいるようなデータ構造です。 プログラミング言語によっては最初の要素が0番になっていることもよくありますが、 先日の講座では最初の要素が1番になっていました。 こちらの配列のイメージ図は、 $$ \LLRR{31,41,59} $$ という三つの数からなる数列を配列で表現した様子です」

配列のイメージ図

List 4を読んで、どんな処理をやっているかを理解した。理解したと思う。

けれど、さっきと同じように早合点をやらかしてはまずいので、もう一度じっくりと読み返した。

「……」

テトラ「……」

「うん、これについては特に問題はない……と思うけど。 テトラちゃんは何か気になることはあったの?」

テトラ「はい。一つ気付いたことがありました。 List 4では $n \GEQ 1$ が前提になっている、と思ったのです」

「nは要素の数だよね」

テトラ「はい。もしも、 $n = 0$ だとしたら、 List 4の2行目で、A[1]というありえない要素の値を得ることになってしまいますから」

「なるほど! 確かにそうなるなあ。 ああ、それは一つ前のList 3でも同じ前提があったわけだね」

テトラ「そうですね。 ですから、アルゴリズムについて語るときには、 こんなふうに入力出力を明記することが大事なんだと再確認したんです」

List 5

MAX-VALUE: 最大値をもとめるアルゴリズム(配列版)

入力

  • 配列で表現した数列$A = \LLRR{a_1, a_2, a_3, \ldots, a_n}$
  • 数列のサイズ$n\quad (n \GEQ 1)$

出力

  • 数列の要素のうち最大の値

手続き

「うーん……」

再度、は反省した。

簡単だとあなどる気持ちがあると、 アルゴリズムを読むのは難しいんだな。

テトラちゃんは一つ一つをていねいにしっかりと考えて読んでいる。 《条件忘れのテトラちゃん》なんて、もう言えないな。

線形リストを使った実装

テトラ「次からが難しくなってきます。 次は線形リストを使って最大値を求めるアルゴリズムを表現したものです」

List 6

MAX-VALUE: 最大値をもとめるアルゴリズム(線形リスト版)

入力

  • 線形リストで表現した数列$L = \LLRR{a_1, a_2, a_3, \ldots, a_n}$

出力

  • 数列の要素のうち最大の値

手続き

「あちこちに出てくる c.next というのは何を表しているの?」

テトラ「はい、それは次のノードを指すリンクを表しています」

「リンク?」

テトラ「たとえば、 $\LLRR{31,41,59}$ という数列を線形リストで表したものは、こんな図で表現します」

「なるほど?」

テトラ「間に仕切りが入った長い四角はコンピュータのメモリ上にある《ノード》という領域を表しています。ここには三個のノードがあります。 ノードがメモリ上のあちこちにあって、データを保持している……と考えます。三個のノードがそれぞれに $31,41,59$ という三個の数を保持しているんです」

「うん」

テトラ「一つのノードはいくつかの《フィールド》に区切られています。 今回の場合はvalueとnextという二つのフィールドがあります」

  • valueフィールドは、要素の値を保持しています。
  • nextフィールドは、次のノードへのリンクを保持しています。

「valueフィールドの値はわかるけど、nextフィールドのリンクというのは抽象的だね」

テトラ「はい。そうですね。実際にはコンピュータ上では、メモリのアドレスが保持されていると考えればいいと思います。 《ノードはここにある》と指しているのでポインタと呼ばれることもあります。 リンク、アドレス、ポインタはだいたい同じ概念です。 コンピュータの中でつじつまが合っているなら、リンクの具体的な値は何でも構いません。たとえば、こうかもしれません」

「nextフィールドの値が、次のノードのアドレスになっているイメージなんだね。 nextの値が次のノードを《指している》ということだ」

テトラ「はい、そうです! なので、図では矢印を使って表現しています。リンクを使って、鎖のように次々とノードをつなぐことで、複数の要素からなる数列が表現できるんですっ!」

「この最後のノードのnextフィールドにある斜線は、終わりの印だよね。nilって書いてある」

テトラ「はいはい。斜線は、nextフィールドの値が《どこも指していない》という場合に使う表現です。 線形リストの場合にはリストの終わりを表しています。 プログラミング言語ではNULLやnullやNoneなどと表現されますが、講座ではnil(ニル)という表現を使っていました。 あたしはnowhereという名前がいいなと思ったんですが」

「……」

List 6のアルゴリズムを改めて読み返していた。

テトラちゃんの説明でおおよそは読めるようになったけれど、 これは、やはり……

テトラ「このアルゴリズムを理解するために、 あたしは $\LLRR{31,41,59}$ という小さな具体例で実際にウォークスルーしてみることにしました。 《例示は理解の試金石》ですから!」

「そうだよね!」

テトラちゃんはいっしょにList 6の動きを、一歩一歩順番に確かめていった。

(1)→(2)→(3)で、mの値が決まったところ

(4)→(5)→(6)で、mとc.valueを比較するところ

(7)→(8)→(9)→(10)→(11)→(12)で、再度mとc.valueを比較するところ

(13)→(14)→(15)→(16)→(17)→(18)で、(19)の終了直前

テトラ「確かに、最大値の $59$ が得られています!」

この記事は期間限定で「ただいま無料」となっています。

ひと月500円でWeb連載をご購読いただくと、 370本すべての記事が読み放題になりますので、 ぜひ、ご購読ください。


購読済みの方/すぐに購読したい方はこちら

結城浩のメンバーシップで購読 結城浩のpixivFANBOXで購読

(第321回終わり)

(2021年4月30日)

[icon]

結城浩(ゆうき・ひろし) @hyuki


『数学ガール』作者。 結城メルマガWeb連載を毎週書いてます。 文章書きとプログラミングが好きなクリスチャン。2014年日本数学会出版賞受賞。

Twitter note 結城メルマガ Home