雑に描いた餅

餅は餅屋、わたしは技術屋、と言いたかった。

Project Eulerと数学とわたし

この記事は

最近Project Eulerというものにハマっています。 projecteuler.net 簡単にいうと、プログラミングを使って数学の計算問題をたくさん解いていこうぜ、っていうサイトです。

この記事は、Project Eulerを始めたいちソフトウェアエンジニアの話です。

自己紹介

わたしは

わみ というソフトウェアエンジニアです。JavaやKotlin、後はTypeScriptとかClojureとか細々と書いてます。

最近子どもが生まれて、絶賛育休中です。寝かしつけって奥が深いですよね。

わたしと数学

高校数学を数学ⅡBまで修了しています。いわゆる文系学部卒だと思ってもらえるとよいかと思います。

どちらかといえば得意な方でしたが、高校を卒業して10年以上が経過しているのでもはやあまり関係ないんじゃないかしら、という気持ちです。

Project Eulerとは

2001年から長く続く、プログラミングによって計算問題を解くサイトです。例として、以下のような問題が英語で出題されているので、答えとなる数字を入力して解答するという非常にシンプルなつくりになっています。

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3,5,6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.

(意訳:10未満の自然数のうち、3または5の倍数は3,5,6,9である。これらの合計は23である。1000以下の自然数について、3または5の倍数の和を求めよ。)

世界中に何十万人以上の利用者がおり、各問題に正答者のみが閲覧できるフォーラムが用意されています。そこではそれぞれの正答者がたどり着いた解法が紹介されていたりします。2023年9月末現在で846問が掲載されており、最近でも不定期で問題が追加されているようです。

Wikipediaにも記事があるので、詳しく知りたい方はそちらもご覧ください。 ja.wikipedia.org

わたしとProject Euler

きっかけ

とある同僚に「お前の問題の解き方はなっていない(意訳)」と言われて紹介を受けたのがきっかけです。

一応ソフトウェアエンジニアとして10年弱のキャリアがあるのですが、アルゴリズムと真剣に向き合うことなくここまで過ごしてきました。

そんな訳なので、アルゴリズムを実践の中で学びつつ、いわゆる「プログラマ的な問題の解き方」が身についたらいいなーくらいの気持ちで始めました。

どのくらい解いたか

始めて10日前後、ざっくり30題解きました。解くときに書いたコードは以下のリポジトリになんとなく上げてます。 github.com

特にノルマや目標は決めていないのですが、それでも1日最低1題は解くようにしています。単体テストは書いたり書かなかったりしています。複雑な問題だなと感じたら、テスト駆動で少しずつ解きほぐしながら解いてる感じです。

どうやって解いてるか

プログラミング言語はKotlin、IDEIntelliJ IDEAを使っています。問題ごとにファイルを分けて、それぞれにmain関数を生やして、答えやそれに近い状態のものをprintln()で出力させてます。

Kotlinで書いてるのは、自分の中で一番経験がある言語なので問題を解くことにある程度集中できるかなと思ったのが理由です。

とはいえ、中には数字を文字列的に捉えて解くような問題もあるので(各桁の数字の積を求めるなど)、その時は若干めんどくささを感じています。

Project Eulerを始めてよかったこと

色々な解き方を発見できた

ここまで解いた問題は、「一定の範囲内の数値について、とある条件に当てはまるものを発見する」性質のものが多かったです。

プログラムでいうと、制御構造(if文など)やループ(forなど)を駆使して解くことになるわけですね。Java育ちの私は最初手続き的な解き方を多くしてきました。例えば以下みたいな感じですね。

// 1000以下の自然数のうち、3もしくは5で割り切れる数字の和を求める
fun hoge() {
    var answer = 0
    for (i in 1..999) {
        if (i % 3 == 0 || i % 5 == 0) answer += i
    }
    println(answer)
}

Project Eulerでは、特に実行時間や実行環境など、解法に対する厳しい縛りは用意されていません。(普通のPCで、常識的な範囲内で解けるくらいのアルゴリズムにしてね、くらい)なので、どのような解き方をするかは基本的に自由なわけです。

ところが、この方法で問題を解いていくうちに、「せっかくKotlinを使ってるし、もっと違った解き方をしてみたいな」という気持ちになってきました。KotlinはJava由来の手続き型な記法だけでなく、関数型のエッセンスも取り入れた言語となっています。なので最近では、より関数型に近い形で解くことを目指しています。上と同じ問題であれば、以下のように書けますね。

// 1000以下の自然数のうち、3もしくは5で割り切れる数字の和を求める
fun functionalHoge() {
    val answer = (1..999).filter { it % 3 == 0 || it % 5 == 0 }.reduce { acc, i -> acc + i }
    println(answer)
}

関数型や手続き型を行ったり来たりしつつ、より良い解き方を模索していくのは楽しいですね。

錆びついた数学の記憶を掘り起こせた

高校を卒業して10年以上が経過しているので、高校数学の記憶はさすがに薄れてきました。とはいえ、日常業務の中で数学的知識が要求される場面は少なくありません。そんな中で、Project Eulerの問題を正しく解くためには、数学の知識が必要不可欠になるわけです。

問題を解いていく中で、「一回習ったけど忘れていた知識たち」と再会を果たすのはなかなか面白いなと思っています。具体的には、順列と組み合わせ、素数無理数あたりが挙げられるでしょうか。

さいごに

Project Eulerは楽しいのでみんな始めてみましょう。

個人的にはProject Euler仲間がいるとモチベーションにつながるので、もしやってるよ〜っていう人がいたらX(旧Twitter)で教えてください。