どうやら、一筋縄ではいかない砂の迷宮に迷い込んだらしい。この顛末を書き残しておくか。
ある時、私の元に、一人の若き冒険者(新人君)がやってきた。C言語(1972年開発の基礎的プログラミング言語)という基礎的な武術は心得ているという彼に、私はC#(シーシャープ、Microsoft開発の現代的プログラミング言語)という新たな魔法体系の習得を命じた。彼は翌日には「1週間でC#の基礎が学べる本」という魔導書を手にし、その瞳は希望に満ち溢れていた。
一週間の自己学習を終えた彼に、私は尋ねた。「1から100までの素数(1と自分自身以外で割り切れない自然数)を表示するプログラムを、どうやって作る?」と。 魔導書の内容は8割も理解しているはずの彼が、この単純な問いに、全く答えられない。インターネットという名の、無限の図書館で答えを調べることを許しても、なお、自らの言葉でその理(ことわり)を説明することさえ、できなかったのだ。 なぜ、彼は素数を数えられなかったのか?これは、その謎を解き明かす、一人のベテラン旅人の、思索の記録である。
この羊皮紙のあらまし
この羊皮紙が導く者
- システムエンジニア(SE)という、未来の冒険者を目指す者
- 新米SEという、若き旅人に、いかにして道を教えるべきか悩む指導者
- 初心者向けの魔導書(技術書)が、実戦でどれほど役に立つのか、その真実を知りたい者
- 「インプット(知識の吸収)」と「アウトプット(実践による創造)」のバランスに悩む学習者
- 素数を数えるアルゴリズム(問題解決の手順)の実装例から、プログラミングの本質を学びたい探求者
砂漠の道標
- 素数(Prime number) - 1と自分自身以外では割り切れない、2以上の自然数。
- C#(シーシャープ) - Microsoft社が開発した、現代的で汎用性の高いプログラミング言語。
- アルゴリズム(Algorithm) - 問題を解決するための、明確な手順や計算方法。
- エラトステネスのふるい(Sieve of Eratosthenes) - 古代ギリシャの数学者が考案した、効率的に素数を見つける方法。
- アウトプット(Output) - 学んだ知識を実際に使い、何かを創り出す行為。
- インプット(Input) - 書籍やインターネットから知識を吸収する行為。
- 合成数(Composite number) - 素数ではない自然数。1と自分自身以外の約数を持つ。
- 平方根(Square root) - ある数を2乗するともとの数になる値。√記号で表される。
なぜ、彼はプログラムを作れなかったのか
新人君は、自己学習の時から、分からないことがあると、すぐにインターネットという名の、巨大な図書館へ駆け込んでいた。それ自体は、悪いことではない。 しかし、彼は「調べる前に、まず自らの頭で仮説を立てる」「調べた後に、その知恵を吟味し、考察する」という、最も重要な冒険者の作法を、ほとんど行っていなかった。
情報の海に頼りすぎるあまり、「自ら考える」という、最も基本的な筋力が、完全に欠落してしまっていたのだ。
素数を数える、三つの流派
素数の演習問題について、彼が途方に暮れている間、私は退屈しのぎに、いくつかの方法で素数を数えるプログラムを組んでみた。
直感の流派:プッチ神父に導かれて
「素数は孤独な数字…」。そんな言葉を思い出しながら、15分ほどで組み上げた、最も直感的な方法だ。 「1と自分自身でしか割れない」ならば、「それより小さい素数で割り切れなければ、それは素数だ」。この単純な理屈を、素数テーブル(既に見つかった素数のリスト)を使い、力技で実現する。
const int N = 100; var primes = new List<int>(); for (int i = 2; i <= N; i++) { var isComposite = false; foreach (var p in primes) { if (i % p == 0) { isComposite = true; break; } } if (!isComposite) primes.Add(i); } Console.WriteLine(String.Join(", ", primes));
改良の流派:先人の知恵を借りて
インターネットで少し調べると、より効率的な方法が見つかる。2以外の偶数は素数ではないのだから、最初から除外してしまえばいい。
const int N = 100; var primes = new List<int>(); for (int i = 3; i <= N; i += 2) // 3から、2ずつ増やす { // (中略) } primes.Insert(0, 2); // 最後に2を加える Console.WriteLine(String.Join(", ", primes));
古の流派:エラトステネスのふるい
さらに深く探求すれば、「エラトステネスのふるい(Sieve of Eratosthenes)」という、古代ギリシャの賢者が編み出した、最も効率的なアルゴリズム(問題解決の手順)に辿り着く。
const int N = 100; var primes = new List<int>() { 2 }; for (int i = 3; i <= N; i += 2) { var isComposite = false; // 対象の平方根までを調べれば十分、という賢者の知恵 for (var j = 2; j < Math.Sqrt(i) + 1; j++) { if (i % j == 0) { isComposite = true; break; } } if (!isComposite) primes.Add(i); } Console.WriteLine(String.Join(", ", primes));
羊皮紙を巻く前に
「1週間で学べる本」を読めば、それでプロになれるのか?答えは否だ。それは、旅に出るための、最低限の地図を手に入れたに過ぎない。
真に重要なのは、その地図を手に、実際に荒野を歩き、道に迷い、自らの力で新たな道を見つけ出すという、実践の経験だ。 まず、自らの頭で考え、仮説を立てて、不格好でもいいから何かを創り出す(アウトプット)。そして、その後に、先人たちの知恵(インプット)を借りて、自分の創り出したものを、より良いものへと磨き上げていく。 この、アウトプットとインプットのサイクルを、何度も、何度も回すこと。それこそが、冒険者として成長するための、唯一にして、最短の道なのだ。
学習における重要な教訓
- 「考える筋力」の鍛錬 - 調べる前に自分の頭で仮説を立て、調べた後に内容を吟味する習慣が、真の理解を生む。
- アウトプット駆動の学習 - 不完全でも自分で何かを作る経験が、知識を血肉に変える最短経路である。
- 段階的な改良の価値 - 直感的な方法から始め、先人の知恵で洗練させていくプロセスが、深い理解を育む。
- 失敗からの学び - 新人君の「素数を数えられない」という失敗は、指導者にも学習者にも、等しく貴重な教訓を与える。
指導者への提言
「答えを教える」のではなく、「考える機会を与える」こと。新人君が自力で仮説を立て、試行錯誤し、失敗から学ぶ過程を、辛抱強く見守ることが、真の教育である。
まとめ
今回の新人君への教育は、私自身に「自ら考えること」の重要性を、改めて教えてくれた。 あんたも、情報の海でただ知識を浴びるだけで、「わかった気」になるという、同じ轍を踏まぬよう、心してほしい。
この羊皮紙が、技術を学ぶすべての旅人と、それを導くすべての指導者にとって、学びの本質を見つめ直すきっかけとなることを願う。
おっと、どうやら相棒が腹を空かせたようだ。今日はこのへんで筆を置くとしよう。
砂漠で見つけた魔法のランプ
- 1週間でC#の基礎が学べる本(Amazon) | C#の基礎文法を学ぶための入門書。本羊皮紙で言及した新人君の学習教材であり、初学者に推奨される一冊。
- エラトステネスのふるい(Wikipedia) | 紀元前3世紀の数学者エラトステネスが考案した、効率的に素数を列挙するアルゴリズム。本羊皮紙で紹介した「古の流派」の理論的背景。
ラクダの独り言
ご主人が、若い衆に「素数を数えろ」だなんて、無茶なことを言っている。俺に言わせりゃ、素数なんて数えられたところで、腹の足しにもなりゃしねえ。それより、目の前にある干し草の数を正確に数える方が、よっぽど大事だと思うんだがな。まったく、人間ってのは、役に立たねえことを教えたがるもんだ。やれやれだぜ。
