たびとの旅路 ~電脳砂漠の冒険譚~

フロッピー頼りに歩き、クラウドの地平を見つめる今日まで。見つけたオアシス、迷い込んだ砂の迷宮、全てこの羊皮紙に。

素数は、なぜ数えられなかったのか? ~ある新米SEと、アウトプットの重要性~

どうやら、一筋縄ではいかない砂の迷宮に迷い込んだらしい。この顛末を書き残しておくか。

ある時、私の元に、一人の若き冒険者(新人君)がやってきた。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. 「考える筋力」の鍛錬 - 調べる前に自分の頭で仮説を立て、調べた後に内容を吟味する習慣が、真の理解を生む。
  2. アウトプット駆動の学習 - 不完全でも自分で何かを作る経験が、知識を血肉に変える最短経路である。
  3. 段階的な改良の価値 - 直感的な方法から始め、先人の知恵で洗練させていくプロセスが、深い理解を育む。
  4. 失敗からの学び - 新人君の「素数を数えられない」という失敗は、指導者にも学習者にも、等しく貴重な教訓を与える。

指導者への提言

「答えを教える」のではなく、「考える機会を与える」こと。新人君が自力で仮説を立て、試行錯誤し、失敗から学ぶ過程を、辛抱強く見守ることが、真の教育である。

まとめ

今回の新人君への教育は、私自身に「自ら考えること」の重要性を、改めて教えてくれた。 あんたも、情報の海でただ知識を浴びるだけで、「わかった気」になるという、同じ轍を踏まぬよう、心してほしい。

この羊皮紙が、技術を学ぶすべての旅人と、それを導くすべての指導者にとって、学びの本質を見つめ直すきっかけとなることを願う。

おっと、どうやら相棒が腹を空かせたようだ。今日はこのへんで筆を置くとしよう。

砂漠で見つけた魔法のランプ

ラクダの独り言

ご主人が、若い衆に「素数を数えろ」だなんて、無茶なことを言っている。俺に言わせりゃ、素数なんて数えられたところで、腹の足しにもなりゃしねえ。それより、目の前にある干し草の数を正確に数える方が、よっぽど大事だと思うんだがな。まったく、人間ってのは、役に立たねえことを教えたがるもんだ。やれやれだぜ。