プログラマーだけが理解できる、数学の捉え方が変わるかもしれない話


プログラムはできるけど、数学は難しいと感じている人向けの記事です。

長年数学はとっつきにくい存在だったんですが、最近、数学ってプログラムじゃない?
と思えるようになってきたら突然親しみやすくなってまいりました。

感じ方の問題

どうにも数式やいろんな記号、数学的な用語がでてくるとそれだけで苦手というか難しいと感じてしまいます。

例えばこの数式

f(x) = x^2

これは2次関数です、グラフで表すと以下のようになります。

と言われてみてどうでしょうか?

私が久しぶりに見た時の感想は
あー、なんか見た事はあるな、何に使うかはしらんけどって感じでした。

では、引数に正方形の1辺の長さを受け取って、正方形の面積を返す関数を作ってと言われるとどうでしょうか?

f(x) { return x * x;}

プログラマーだったらこんな関数を定義するのは難しくないと思います。
これは最初に記載した以下の2次関数と全く同じ計算をしています。

f(x) = x^2

つまりこの2次関数は正方形の面積を表していて、グラフも正方形の1辺と面積の関係を表しています。

  • 横軸を正方形の1辺の長さ
  • 縦軸を正方形の面積

としてみればその通りになっていると思います。

数学として関数という言葉を聞くとなんだか難しそうな気がしてきます。
2次関数とか、三角関数とか、関数って沢山あるしよくわからないという気持ちになります。

しかし、プログラムとして関数という言葉を聞くと別に抵抗ないと思います。
プログラムにも色んな関数が沢山あると思いますが、そりゃ関数は沢山あるよと思うだけです。

数学でもプログラムでも関数というのは同じものです。

例え同じモノであったとしても
数学として捉えてしまうと途端によくわからない感覚に襲われます。

でもプログラマーならプログラムとして捉えてみると、すんなり入ってくる感じになるんじゃないかと思います。
なりませんかね?

この記事では、ざっくばらんに
数学をプログラム脳で捉えてみた試みについて記載したいと思います。

数学記号は関数とか演算子の一種

四則演算

例えば足し算、引き算、掛け算、割り算といった四則演算はプログラムでは+, - , *, /という演算子です。

これは数学でもプログラムでも見た目は大差ないので違和感はないと思います。

絶対値

絶対値は数値の横に線を書いてこのように表します。

|-5|

絶対値というのは実数の世界で言えば、符号を取っ払ったものです。
(正確には原点からの距離と定義されていますがそれは置いておきます)

5の絶対値は5、-5の絶対値も5です。

これはプログラムの世界で考えれば関数です。

基本的にはabsという名前の関数で初めから用意されていると思います。

abs(5); // 5
abs(-5); // -5

このように数学の絶対値記号はプログラムだと関数として捉える事ができます。

三角関数

sin(θ)、cos(θ)、tan(θ)

高校で習う三角関数というものですが、なんとなく難しそうな記号の一種にも見えてきたりします。
ですがこれは名前にもある通り、ただの関数です。

プログラムの世界にもsincostanという関数として用意されていると思います。

sin(0); // 0
cos(0); // 1
tan(0); // 0

引数に角度を受け取り、その角度に対応した値を返すというだけの関数。

それを何に使うの?となるとそれらを活用する分野に行かなければ用途はないと思いますが
例えばWebの世界ならアニメーションの動きなどには良く使われているんじゃないでしょうか
むしろ使われてない分野を探す方が難しいというくらい良く使われる関数な気がします。

総和記号Σ

\sum_{x=3}^{100}(x + 1)

こんな記号を見たことがあるでしょうか?シグマと呼ばれる記号です。
このシグマについて、プログラム脳で捉えてみたいと思います。

まずこんな関数があったとします。

f(x) { return x + 1; }

こんな処理を書いたとします。

a = f(3) + f(4) + f(5);

これはf(3)からf(5)の戻り値を全部足した結果を求めています。
(実際にはa = 4 + 5 + 6という処理)

ではf(3) から f(100)までの和と言われたらどうでしょうか?
めちゃくちゃ長い計算式を書くのはめんどくさいですよね?

こんな風に書きたくなりませんか?

f(x) { return x; }

a = 0;
for(x = 3; x <= 100; ++x) {
    a += f(x);
}

for文を使って書けば短く済ませる事ができます。

ここでsigmaという関数を定義してみます。

sigma(start, end, func) {
    a = 0;
    for (x = start; x <= end; ++x) {
        a += func(x);
    }
    return a;
}

この関数を使って、以下の計算を書き直してみます。

sigmaを使ってないバージョン

f(x) { return x + 1; }

a = 0;
for(x = 3; x <= 100; ++x) {
    a += f(i);
}

sigmaを使ったバージョン

a = sigma(3, 100, (x) => { return x + 1; })

このプログラムを数学のΣ記号を使って書くとこうなります。

\sum_{x=3}^{100}(x + 1)

シグマは総和記号といい、要するに沢山の数の和を求めるよーという記号です。

第一引数: start はループの初期値、シグマ記号の下に記される
第二引数:end はループ終了の値、シグマ記号の上に記される
第三引数:func は 和を取りたい式、シグマ記号の右に記される

シグマをプログラムとして捉えるとなると、若干複雑で戸惑うかもしれませんが
要するに伝えたい事は

数学の記号というのは、愚直に書くと長くて大変な事だし、これパターン化できるよね?
という事を記号にしてコンパクトにしているという事です。

それはプログラムもまったく同じ事だと思いませんか?
同じこと何度も書きたくないな→関数にまとめようってなりますよね

プログラムの場合は一連の処理を関数としてまとめますが
数学の場合は一連の処理を記号で表現する事も多いと思います。

数学の記号だと思うと難しそうという抵抗感が否めませんが
これらは関数なんだと思うと、なんだかすっと受け入れられる気がしてきませんか?
という事を伝えてみたかったのですがどうだったでしょうか?

陽関数と陰関数

陽関数と陰関数というものに触れてみたいと思います。
陽関数と陰関数って聞きなれない言葉ですよね、少なくとも僕は何それ?って思いました。
正直言えば私もちゃんと理解できてるのかよくわかりません。

とりあえず書いてみます。

陽関数:y = f(x) といういつもの形で表した関数 \\
陰関数:f(x, y) = 0 という形で表現した関係 

数学的には陽関数は関数、陰関数は関係とされていますがいまいちよくわかりませんね。

とりあえずプログラム的に捉えたら
引数xを与えると、yが戻ってくる関数が陽関数です。
引数x, yを与えると、0が戻ってくる関数が陰関数です。

例えば、これは陽関数です。

y = x^2 

この陽関数を陰関数にするとこうなります。

y - x^2 = 0

陽関数の形であれば、両辺から右辺を引いてあげれば陰関数の形になります。
陽関数と陰関数は同じ関数の異なる表現という感じになります。

おそらくここまできてもだから何?という感想しかないと思うので

これをプログラム脳で捉えてみたいと思います。

陽関数

引数に正方形の1辺の長さを渡すと、正方形の面積を返す関数を定義して使ってみると

f(x) { return x * x; }
y = f(2); // 4

このような感じになり、これは陽関数の形になっていると思います。

この関数は正方形の面積を求める事にも使えるでしょうし
引数に時間を与えて、戻り値を座標として使えば、時間と共に加速する動きにも使えるでしょうし
戻り値を色として使えば、色合いを変化させるアニメーションにも使えるでしょう。

様々な用途に使えますが、何かを与えると、何かが得られるというごく普通の関数です。

陰関数

では、引数に正方形の1辺の長さ(x)と正方形の面積(y)を渡すと
0を返す関数を定義してみるとどうなるでしょうか?
やってみます。

f(x, y) { return y - x * x; }
f(2, 4); // 0
f(2, 6); // 2

この関数は陰関数の形になっているのがわかるでしょうか?

この関数を使って、結果が0になるということは
引数に与えた正方形の1辺の長さと面積の関係が正しい事がわかります。

また、このx,yを座標だとして捉えてみましょう。
x,yが y=x^2が描く放物線上にあるかどうか、座標と放物線の関係を判定できます。
シェーダーの世界とかなんか使えそうですね。

陰関数の形にしただけなのに、確かにこれは関係を表現しているなと思います。

まとめ

初めの頃は陽関数とか陰関数とか、なんでそんな書き方するのとか、別に陽関数だけでも良くない?
陽関数は関数で、陰関数は関係ってどういうこと?
という気持ちになったりしたのですが

プログラムにして用途を考えてみると不思議なものです。

確かに陽関数はただの関数なのですが
陰関数は、何かと何かの関係を表現している事に気づきます。

しかしなんでこんな表現をするのでしょうか?

プログラムの場合を考えてみます。

プログラムで何かを実現しようとした場合
その実装方法はいくつもあると思いますが

より分かりやすく、可読性というものを意識すると思います。

そう考えると、関数として扱いたい場合は陽関数で表現するけど
関係を表現したいときは陰関数で表現した方が分かりやすいと思いました。

なんでこんな表現するの?
え、この場合はこっちのがわかりやすくない?それだけだけど

そう言われると
「なるほど、数学もプログラム同じだな」と思えてきたりしないでしょうか?

数式を見るときの心構え

長い数式が出てきたり、なんでこんな計算が思いつくの?ってものに出会うと
やっぱ自分センスないわー、無理だわーって思いませんか?
僕は思います。

では、長ったらしいプログラムや、綺麗なプログラムと出逢った時はどうでしょう?
時には天才的なコードに自分のセンスの無さを嘆きたくなることもあると思いますが

多くの場合は、そのプログラムは一瞬で作り上げたわけではなく
沢山考えて、試行錯誤して、さらにリファクタリングした結果、綺麗になったもの
無意識のうちにそういうものだと思って受け止めている気がします

プログラマーだったらプログラムを見て一瞬で理解できるでしょうか?
短いプログラムなら理解できるでしょうが、普通はそんな事ないはずです。
だから地道に読み解きます、そういうもんだと思っているので数式よりは抵抗が少ないと思います。

数式も同じだと思います。
我々が目にする多くの数式は試行錯誤の上、最終的に最適化されて綺麗になったモノが多いと思います。
数学はセンスという言葉も聞かれますが、どちらかというと地道な努力だと思います。

もちろん数学的センスも必要だと思います。
ただ、プログラマーであれば、そのセンスって既に持っているんじゃないでしょうか?
と思います。

プリミティブ型は数学でいうと数

これは個人的にふんわり感じている事です、ポエムに近い内容になります。

プログラムの世界ではプリミティブ型といわれるものがあります。
bool型、int型、char型、float型などなど

プログラムで登場するデータは、プログラムがどんなに複雑になったとしても素をたどればプリミティブ型しかありません。
それらを組み合わせたり、計算したり、複雑な計算は関数にまとめたり、クラスを使ってまとめる事で新しい概念を作ったりします。

では数学はどうでしょうか?
プログラムとはちょっと違う気もしますが、数学でいうプリミティブ型とは数のような気がしています。
数には大まかに以下の種類があります。

自然数、整数、実数、複素数。

これが数学でいうところのプリミティブ型だとすると
数学はどんなに複雑になってもこのプリミティブ型しか登場しません。
それらを組み合わせたり、計算したり、特定の計算式を関数として名前つけたり、まとめる事で新しい概念を作ったりします。

まとめ

数学をプログラムとして捉えると、プログラマーだったらとっつきやすくなるのでは?
と思って記事を書いてみました。

この考え方をしていると、ベクトルや行列はクラスで表現できるしなー
オペレーターのオーバーロードも四則演算の定義だしなーと

数学の様々な事がプログラムとして理解できる気がしてきます。

そして数学できる人だったらプログラムなんて速攻習得できそうだなと思いました。

~fin~