実数とは何か?無限とは?

【まえがき】

 私は長い間、実数というものに納得が行かずに使ってきました。 実数なんて簡単じゃん。\(\sqrt{2}\) とかのことでしょ?と思うかもしれませんが、 この \(\sqrt{2}\) が大問題で、 無限小数展開された \(\sqrt{2}\) なんてものが本当に存在するのかと、 ずっと悩んでいました。

 最近、ようやく納得できる解釈にたどり着いたので、 未来の自分が忘れてしまわないように書き留めておくことにしました(笑)。 あくまで、私の個人的見解なので、数学的に厳密というわけではありません。 もし、参考にする人がいたとしたら、そのあたり自己責任でお願いします。

【無理数の無限小数展開】

 まず、何を悩んでいたのか書いておきましょう。 はじめに、自然数は存在すると認めます。 実はこれも考え出すと怪しくなるのですが、 さすがにこんな基本的なものを疑うのは哲学者の仕事なのでおいておきます。 そして有理数、これは2つの自然数の比で表すことができます。 これは小数展開した時、有限な桁数になるか循環小数になることを意味します。 つまり、有限な手段でその具体的値を表すことができるわけです。

 しかし、無理数の値を具体的に指定するには \(1.41421356...\) のように無限小数展開する必要があります。循環小数にもならないので、 有限な手段では無理数を厳密に表すことはできません。 しかし、無限小数展開などというものは所詮幻想でしかありません。 この世のどこにも実際に \(\sqrt{2}\) を完全に無限小数展開した人などいません。 それに、無限に長い数の足し算や掛け算など実行できません。 そんなものが本当に実在するのでしょうか?

 ここでちょっと見方を変えてみましょう。 \(\sqrt{2}\) は実は厳密に表すことができます。 1辺の長さが1の正方形の対角線を考えれば、これは厳密に \(\sqrt{2}\) です。 無限小数展開では表せませんが、\(\sqrt{2}\) は確実に実在しています。 ならば、図形を使わず \(\sqrt{2}\) を厳密に表すにはどうしたら良いでしょうか? これが私の悩みでした。

 ちなみに、何で無限小数展開にこだわったのかというと、 カントールの対角線論法に無限小数展開が使われるからです。 実数全体の方が自然数全体より多い、 つまり1対1対応できないことの根拠に無限小数展開が使われています。

【極限の概念】

 無限小数展開をもっと数学的に考えてみると何になるでしょうか? そう、極限の概念ですね。\((1, 1.4, 1.41, 1.414, 1.4142, \ldots)\) という数列 \(\{a_n\}\) を考えると \(\displaystyle\lim_{n\to\infty}a_n=\sqrt{2}\) になります。つまり、無理数の実在を保証するには極限値の存在、 すなわち、収束の概念を理解すれば良いことになります。 \(n\rightarrow\infty\) なんて書かれると、 いかにも \(n\) を無限にした時の値を求める計算問題のように感じてしまいますが、 実は \(\lim\) は証明問題です。イメージとしては定積分と似ています。 定積分の問題では四則演算やべき根を計算すれば良いわけではなく、 積分の公式で式を変形して値を求めます。まさか本当に定積分の定義に従って、 短冊の面積を総和して答えを求める人はいないでしょう。 \(\lim\) も同様に証明問題なのです。 つまり、文章と数式という有限な手段によって表されます。

 では、\(\{a_n\}\) が収束するとはどういう意味でしょうか。 これはコーシーの収束条件で表されます。 \[ 任意の~\epsilon\gt 0~に対し適当な自然数~m~を決めると、~p\gt m,~q\gt m~であるすべての~p,~q~に対して~|a_p - a_q|\lt\epsilon \]  大雑把にいうと、どんなに小さな \(\epsilon\gt 0\) を指定しても、 自然数 \(m\) を十分大きく取れば、必ず数列 \(\{a_n\}\) の振れ幅を \(\epsilon\) より小さくすることができるということです。 この条件が満たされれば数列 \(\{a_n\}\) が収束する、 すなわち極限値が存在すると考えるようです。

 これはよくできています。どこにも無限が顔を出しません。 有限な手段で極限が定義できるわけです。 では、コーシーの収束条件を満たす数列とはどんなものでしょうか?

 ここからがミソです。つまり私がたどり着いた解釈です。 以下のように考えました。 \[ 任意の自然数~n~に対して、\sqrt{2}~の~ n~桁目を計算するアルゴリズムが存在する。 \]  0桁目を整数部、1桁目から小数点以下の数字とします。 \(\sqrt{2}\) の場合、例えばニュートン・ラフソン法を使って 近似値を計算すれば、有限な手段で \(n\) 桁目を計算することができます。 このアルゴリズム自身を、つまり数式自身を \(\sqrt{2}\) と同一視しようというわけです。 \(n\) 桁目が計算できるということは、数列 \(\{a_n\}\) が計算できるのと同義です。 \(\{a_n\}\) がコーシーの収束条件を満たすことは明らかでしょう。 つまり、極限値の存在とは、 その実数の \(n\) 桁目を計算するアルゴリズムの存在と考えるわけす。

 アルゴリズムが存在することさえ示せば、 実際に \(n\) 桁目を計算する必要はありません。ここ重要ですよ。 無限のメモリがあるコンピュータと無限の時間があれば、 そのアルゴリズムで何桁でも計算できるという可能性が重要で、 実際に無限のメモリや時間は必要ありません。 \(\sqrt{2}\) が1辺の長さが1の正方形の対角線で表されるという事実は、 実は \(\sqrt{2}\) を計算するアルゴリズムが存在することと対応しています。

 ただし、他の数と大小を比較したり、具体的大きさを知ろうとすれば、 当然、そのアルゴリズムでせっせと必要な精度まで計算しなければなりません。

【実数の四則演算】

 さて、無理数の存在、すなわち実数の存在は上記の考えで保証できました。 その実数の \(n\) 桁目を計算するアルゴリズムと実数を同一視すればつじつまは合います。 しかし、アルゴリズム自身が実数だというなら、 その四則演算が実行できなければいけません。 これは、以下の公式をイプシロン-デルタ論法で証明することで確認できます。 \[ \lim_{n\to\infty}a_n + \lim_{n\to\infty}b_n = \lim_{n\to\infty}(a_n + b_n) \] \[ \lim_{n\to\infty}a_n\lim_{n\to\infty}b_n = \lim_{n\to\infty}(a_nb_n) \]

 \(\displaystyle\lim_{n\to\infty}a_n\) と \(\displaystyle\lim_{n\to\infty}b_n\) はそれぞれ1つの実数を表します。 するとその和は、\(a_n + b_n\) を求めるアルゴリズムの極限で計算できます。 \(\lim\) が証明問題であったことに注意してください。 \(a_n + b_n\) の極限値を証明で求めることで \(\displaystyle\lim_{n\to\infty}a_n + \displaystyle\lim_{n\to\infty}b_n\) が求まります。 あるいは \(\sqrt{2}\) や \(\pi\) のような有名な数値で表せないなら、 そのまま \(a_n + b_n\) の極限値という名前にすれば良いわけです。 ただし、\(a_n\) や \(b_n\) は具体的数式に展開します。 積に関しても同様です。つまり、有限な数式の世界で和や積が定義できるわけです。 無限に長い数の足し算や掛け算など実行できないじゃないか!と言いましたが、 この公式があれば有限の世界で計算できますね。 まあ、できると言っても、とてつもなく面倒で複雑な数式になりますが。

【本当は簡単じゃない 0.999... = 1 の証明】

 皆さんも、一度はこれを聞いて不思議に思ったことがあると思います。そう、 0.999... が 1 になるという話です。直感的には、0.999...(9が無限に続く) はほんのちょっとだけ 1 より小さくなるような気がしますが、 以下のような証明により 0.999... = 1 だと説明されます。

\[ \begin{eqnarray} x & = & 0.999\ldots\\ 10x & = & 9.999\ldots\\ 10x - x & = & 9.999\ldots - 0.999\ldots\\ 9x & = & 9\\ x & = & 1 \end{eqnarray} \]

 しかし、この証明には問題があります。 上記の証明には無限に続く数の掛け算や引き算が、 何のためらいもなく使われています。無限に続く数の四則演算が、 有限な数の四則演算と同じような結果になる保障など何処にもありません。 そもそも、無限に続く数の四則演算が可能なのかすら怪しいです。 では、上記の証明は全くのデタラメなのでしょうか? 実は、0.999... を以下の等比級数の極限と解釈すれば正しい証明になります。

\[ x = \lim_{n\to\infty}\frac{9}{10} + \frac{9}{10^2} + \cdots + \frac{9}{10^n} \]

 念のため説明しますが、 極限は \(n\) を無限にした時の値を求める計算ではありません。 コーシーの収束条件を満たす時の収束する値を割り当てる計算にすぎません。 ですから、これは 9 が無限に続く数ではありません。あくまでその代替です。

 \(x\) を極限と解釈すると極限の線型性が使えるようになります。 つまり、\(a_n, b_n\) が収束する時、以下の公式が成り立ちます。

\[ \lim_{n\to\infty}ka_n + lb_n = k\lim_{n\to\infty}a_n + l\lim_{n\to\infty}b_n \]

 極限の線型性を使うと、 上記の 0.999... = 1 の証明が正当化できて以下のようになります。

\[ \begin{eqnarray} x & = & \lim_{n\to\infty}\frac{9}{10} + \frac{9}{10^2} + \cdots + \frac{9}{10^n}\\ 10x & = & \lim_{n\to\infty}9 + \frac{9}{10} + \cdots + \frac{9}{10^{n-1}}\\ 10x - x & = & \lim_{n\to\infty}9 - \frac{9}{10^n}\\ 9x & = & 9\lim_{n\to\infty}1 - \frac{1}{10^n}\\ x & = & \lim_{n\to\infty}1 - \frac{1}{10^n}\\ x & = & 1 \end{eqnarray} \]

 0.999... = 1 の証明は、0.999... を極限と解釈した上で、 イプシロン-デルタ論法により、 極限の線型性を証明しないと本当は十分ではないわけです。 ですから、 最初に示した 0.999... = 1 の証明を得意げに語る人がいたら突っ込んであげましょう。 私も昔、得意げに語ってたんですけどね(^^;

 余談ながら、\(\displaystyle x = \lim_{n\to\infty}1 - \frac{1}{10^n}\) の部分が興味深いですね。もし無限小のような量を許すなら、 確かに 0.999... は 1 よりほんのちょっとだけ小さいという直感は間違っていないわけです。 超準解析という分野では無限小も厳密に扱えるそうです。 そうした話題に興味のある人は、 こちらのページへどうぞ。

【確率論的実数】

 実は、上記のようなアルゴリズムを持つ実数の濃度は可算無限でしかありません。 アルゴリズムは有限な記号の有限な組み合わせなので当然ですね。しかし、 カントールによると実数の濃度は可算無限(自然数の濃度)より多いとされています。 つまり、アルゴリズムがない実数がたくさんあるということです。 アルゴリズムがない実数とはどんなものでしょうか? ここから、私がたどり着いたもう1つの解釈を紹介しましょう。 私はこのアイディアを確率論的実数と呼んでいます。

 今、0から9の数字をランダムに出力する真の乱数生成機が存在するとします。 真の乱数など存在しないという人もいますが、ここでは真の乱数の存在を認めましょう。 真の乱数生成機とは、簡単にいうとサイコロですね。真の乱数生成機ですから、 \(n\) 回目にどの数字が出るか決して予測することはできません。 つまり、\(n\) 回目にどの数字が出るかを決定するアルゴリズムは存在しません。

 このサイコロは、ありえねー! と叫びをあげたくなるほどのものすごいハイパーテクノロジーです。 このサイコロを振り続ければ、 アルゴリズムでは決して生成できない数列を生成できます。 また、この真の乱数生成機で小数点以下 \(n\) 位の数字を決めて、 上記 \(\sqrt{2}\) の時の \(\{a_n\}\) のような数列を作れば、 コーシーの収束条件を満たすことになるので、 何らかの実数に収束することになります(整数部については適当に決めるとします)。 乱数が一様分布だとすると、あらゆる実数に収束する可能性があります。 すごいでしょ?

 確率なんて可能性でしかないのだから、 これであらゆる実数が実在するというのは乱暴では?という考え方もあります。 しかし、収束の概念自身が、 いくらでも極限値に近づく可能性を1つの数値として認めたものですから、 確率による可能性を数値として認めないのは不公平と私は思います。

 では、この確率論的実数は、どんなものに収束すると考えれば良いのでしょうか? 確率がからむので、1つの実数値で表すことはできません。つまり、 集合的な存在になります。 このサイコロが、0から1の間の実数の小数点以下の数字を生成するとし、 収束先の極限値が \(0\le x\le 1\) と考えます。 ただし、\(1\) は \((0.9, 0.99, 0.999, \ldots)\) の極限で考えます。 すると確率論的実数は、 \(x\) に収束する確率密度関数が \(p(x) = 1\) となる実数 \(x\) で表されると考えれば良いというのが私のアイディアです。

 ただ、 私はこの確率論的実数の四則演算をどう定義すれば良いのか分かりませんでした。 おぼろげに、確率密度関数を使った何らかの計算になると想像しています。 例えば、平均値を求める演算が、 アルゴリズムのある実数へのかけ橋になっているのではないかと考えています。

【カントールの対角線論法】

 私が無限小数展開にこだわったのは、 カントールの対角線論法に無限小数展開が使われていたからです。 私がたどり着いた実数の解釈で、対角線論法を解釈するとどうなるでしょうか? ちょっと考えてみましょう。 カントールの対角線論法をご存じない人は適当にググってみてくださいね。 カントールの対角線論法とは、自然数全体と実数全体の間に、 1対1対応が存在しないことの証明法です。

 今、\(0\) から \(1\) の間の任意の実数を1つ表すことを考えましょう。 整数部を \(0\) として、 小数点以下 \(n\) 位の数を上記の真の乱数生成機の出力 \(f(n)\) で表せば、 \(0\) から \(1\) の間の実数が1つ決まります。 このような \(f(n)\) がたくさんあるとして、\(f_k(n)~(k = 1,2,3,\ldots)\) を考えます。\(f_k(n)\) は確率論的なので様々なパターンが考えられますが、 もし、\(f_k(n)\) のあらゆるパターンの中の1つで、 \(0\) から \(1\) の間の実数を全て表すことができるなら、変数 \(k\) を使って、 \(0\) から \(1\) の間の実数全体に自然数の番号が振れるはずです。 番号が振れれば1対1対応があることになります。

 ここで、次のような関数 \(g(n)\) を考えます。 \[ g(n) = \left\{ \begin{array}{cl} 1+f_n(n) & (~0\le f_n(n)\le 8~) \\ 0 & (~f_n(n)=9~) \end{array} \right. \]  すると、\(g(n)\) も \(0\) から \(1\) の間の1つの実数を表しますから、 何らかの番号 \(m\) により \(g(n) = f_m(n)\) と表せるはずです。 しかし、\(g(m)\) の値を考えると矛盾します。実際、 \(0\le g(m)\le 8\) だとすると \(g(m) = 1+f_m(m) = 1+g(m)\) となり、 \(g(m)=9\) だとすると \(g(m) = 0\) となるからです。 すなわち、\(0\) から \(1\) の間の実数全体に自然数の番号は振れません。 \(0\) から \(1\) の間の実数と実数全体は1対1対応するので、 自然数全体と実数全体の間には、1対1対応はありません。

 何とか証明できているみたいですね。補足説明しておきましょう。 上記の真の乱数生成機で \(0\) から \(9\) の乱数を生成して \(L\) 行 \(L\) 列の数表を作り、 この数表の \(k\) 行 \(n\) 列目の数を \(s_L(k, n)\) で表すと、 厳密な表現ではありませんが、 \(\displaystyle f_k(n) = \lim_{L\to\infty}s_L(k, n)\) と表せます。 本当は、数表の各行の数を小数点以下の数字とみなした数の極限を考えます。 つまり、無限小数展開の無限は、\(s_L(k, n)\) の極限を考えれば回避できます。 真の乱数生成機を使って確率論的数を任意の桁数で任意個並べるという 確率論的アルゴリズム があれば、実際に乱数を生成する必要はありません。 \(\sqrt{2}\) の時と同じですね。 もちろん、通常の決定論的アルゴリズムはありませんよ。

 これで、自然数の濃度と実数の濃度を比較するのに、 論理の中に無限が出てくることを回避できます。 つまり、自然数の濃度や実数の濃度は単なるラベルであって、 本当の無限ではなく、だから人間にも取り扱い可能なのだということです。

【リシャールのパラドクス】

 実数全体には自然数で番号を振れないことが、 上記のカントールの対角線論法で分かりました。 しかし、実数を表すアルゴリズム全体の濃度は可算無限のはずなので、 これには自然数で番号を振れるはずですよね。 ところが、実はこれもできないというのが、次に紹介するリシャールのパラドクスです。

 今、\(0\) から \(1\) の間の実数を表すアルゴリズムに番号 \(k\) を振って、 その実数の小数点以下 \(n\) 位の数を \(f_k(n)\) で表すとします。 実数を表すアルゴリズムは可算無限個のはずなので、\(k\) で番号を振れるはずです。 ところが、\(f_k(n)\) に \(k\) でパラメータ化されたアルゴリズムがあるとすると、 次の関数 \(g(n)\) を考えることで矛盾します。 \[ g(n) = \left\{ \begin{array}{cl} 1+f_n(n) & (~0\le f_n(n)\le 8~) \\ 0 & (~f_n(n)=9~) \end{array} \right. \]  なぜなら、\(f_k(n)\) にアルゴリズムがあれば、 \(g(n)\) にもアルゴリズムがあることになり、 \(g(n)\) も1つの実数を表すアルゴリズムであることになります。 しかし、これは カントールの対角線論法 と同様の議論で矛盾するからです。

つまり、\(k\) を指定しただけで、 実数を表すアルゴリズムを全て列挙できるアルゴリズムは書けないということです。 そのような完全かつ構成的な方法は存在しません。 アルゴリズム論で言えば、部分関数の全域性は決定不能なことが関係しています。 実数を表す関数は全域的である必要があるからです。

 果たして、 実数を表すアルゴリズム全体は、自然数全体と1対1対応するのでしょうか? 実は私もよく分かっていません。選択公理があれば大丈夫そうな気もしますが。

【不完全性定理】

 リシャールのパラドクスを使うと、 不完全性定理のエッセンスを理解することができます。 ちょっとやってみましょう。不完全性定理とは、 ある条件を満たす正しいシステムには、 証明も反証もできない命題が存在するというものです。 もう少し詳しい説明がお望みの方は こちらのページ へどうぞ。

 リシャールのパラドクスから、番号 \(k\) を指定するだけで、 実数を表すアルゴリズムを全て列挙するアルゴリズムはないことが分かりました。 このことは、任意に与えられたソースコードが、 実数を表すアルゴリズムであるか否かを判定する判定機は作れないことを意味します。 これは、以下のように考えれば分かります。

 全てのソースコードは適当なコーディングルールを定めると、 1つの自然数として表されます。例えば、プログラミング言語のソースコードは、 アスキーコードなどを使えば1つの巨大な自然数とみなすことができます。 なので、0 から始めて、0,1,2,3,... を次々ソースコードに変換すれば、 あらゆるソースコードが得られます。まあ、大半は無意味な記号列ですけどね。

 判定機を使えば、 これらのソースコードから実数を表すアルゴリズムを順々に取り出すことができます。 つまり、 番号 \(k\) を指定して実数を表すアルゴリズムを全て列挙することができます。 しかし、リシャールのパラドクスにより、そのようなアルゴリズムは存在しないので、 任意に与えられたソースコードが、 実数を表すアルゴリズムか否かを判定する判定機は作れません。

 判定機を作れないということは、任意に与えられたソースコードが、 実数を表すアルゴリズムかを証明することもできないということです。 そのような証明が存在するなら、判定もできてしまいます。 また、この判定機が判定できるときは必ず正しい判定をするなら、 実数を表すアルゴリズムであるのに、 そうであると判定できなかったソースコードを、 実数を表すアルゴリズムではないと判定することもありません。 つまり、実数を表さないと証明することもできません。

 すなわち、「このソースコードは実数を表すアルゴリズムである」という意味の、 証明も反証もできない命題が存在しているということです。

【無限集合】

 実数の話からちょっと脱線して、無限集合の話をしましょう。 今、\(X = \{0,2,5\}\) のような自然数の集合があるとし、 \(X\) の大きさ \(\|X\|\) が次のように計算できるとします。

\[ \|X\|=\sqrt{\frac{1}{(1+0)^2}+\frac{1}{(1+2)^2}+\frac{1}{(1+5)^2}} \]

 一般には、\(X = \{x_1,x_2,\ldots,x_n\}\) の時、 次の式で大きさを計算することにします。 ただし、空集合の大きさは \(0\) とします。

\[ \|X\|=\sqrt{\sum_{k=1}^n\frac{1}{(1+x_k)^2}} \] \[ \|\{\}\| = 0 \]

 すると、任意の自然数の集合 \(X,~Y\) の間に、 以下の式で距離 \(d\) を定義できます。 ただし、\(\oplus\) は排他的論理和を表します。

\[ d(X,Y) = \|X\oplus Y\| \]

 距離を定義したので、自然数の集合を要素とする空間に位相が入ったことになり、 極限が扱えるようになります。

 ここで、次のような集合列(集合族)

\[ a_0=\{\},~a_1=\{0\},~a_2=\{0,1\},~a_3=\{0,1,2\},\ldots,~a_n=\{0,1,2,\ldots,n-1\} \]

 を考えると、

\[  任意の~n~に対して、 \|a_n\|^2=1+\frac{1}{2^2}+\frac{1}{3^2}+\cdots+\frac{1}{n^2}\lt\frac{\pi^2}{6} \]

 が成り立つので、

\[ 任意の~\epsilon\gt 0~に対し適当な自然数~m~を決めると、 p\gt m, ~q\gt m~であるすべての~p,~q~に対して~d(a_p,a_q)\lt \epsilon \]

 とすることができて、\(a_n\) が収束することになります。つまり、その作り方から、 \(a_n\) は自然数全体の集合 \(\mathbb{N}\) に収束することになります。

\[ \lim_{n\to\infty}a_n = \mathbb{N} \]

 何を当たり前のことをと思うかもしれませんが。極限が \(n\) を無限にした時の値を求める演算ではなかったことを思い出してください。 極限はコーシーの収束条件を満たす時の収束先を割り当てる計算です。 収束先の存在は、 その数式(アルゴリズム)における任意の \(n\) での計算可能性と同一視できます。 つまり、無限集合を無限を使わず定義することができるということです。

 自然数全体の集合 \(\mathbb{N}\) なんて作るのは無理と思う人もいるでしょう。 もちろん、本当に全ての自然数を書き下している訳ではありません。 いくらでも大きくなる収束先、その可能性を1つの集合と見ているわけです。 極限による収束の概念を認めるならば、\(\mathbb{N}\) の存在は \(\sqrt{2}\) と同程度のファンタジーということになります。 \(\sqrt{2}\) が存在しないと思う人はそれほど多くないでしょう。 ならば、\(\mathbb{N}\) も同様に認めるしかないわけです。

 もっとも、ここで定義した距離で極限を求めるためには、 自然数の概念どころか実数の概念が必要なので、 極限で \(\mathbb{N}\) を定義するのは明らかな循環論法です。 \(\mathbb{N}\) の存在は、集合論の無限の公理で保証されます。ですから、 極限での \(\mathbb{N}\) の定義は、無限集合の別の見方を与えていると考えられます。

 実数の話に例えるなら、有限集合は有理数に対応し、無限集合は無理数に対応します。 つまり、 どのような有限集合を使っても表すことができない集合が無限集合ということです。 また、実数の四則演算から類推すると、 無限集合の集合演算は、 有限集合の集合演算を表す数式(アルゴリズム)の極限になることが予想されます。 無限集合の無限個の要素に対して演算を行うことは、 無限に長い数(無限小数)の四則演算と同じで、本当は実行できないはずだからです。

 ただ、これは自然数の集合に限っての話です。 実数の集合となると、このようなアナロジーが通用するのかは分かりません。

 また、極限集合という概念があって、ここで定義した距離 \(d\) による位相などは使わないで集合の極限を定義する方法もあるようです。

【やっぱり 0.999... < 1 かも?】

 無限集合 のところの最後で極限集合のことに触れました。 極限集合を用いると、集合間に距離を定義せずとも集合の極限が考えられます。 そして、ちょっと強引ですが、ある解釈のもと、 \(0.999...\lt 1\) という結果を導くことができます。

 まず、極限集合の定義から見てみましょう。\(A_n\) を集合列とすると、 以下のような上極限と下極限が一致するとき、極限値が存在すると言い、 それを \(\displaystyle\lim_{n\to\infty}A_n\) と書きます。 以下、和集合・積集合の \(\infty\) は、 本当に無限という意味ではなく「全て」の意味です。

\[ A_n の上極限 = \bigcap_{k=1}^{\infty}\bigcup_{n=k}^{\infty}A_n \] \[ A_n の下極限 = \bigcup_{k=1}^{\infty}\bigcap_{n=k}^{\infty}A_n \]

 なにやら面倒で意味不明な式ですが、 \(A_n\) が単調増加な集合列の時、すなわち \(A_1\subset A_2\subset A_3\subset\cdots\) の時は、以下の公式で極限値が計算できます。

\[ \lim_{n\to\infty}A_n = \bigcup_{n=1}^{\infty}A_n \]

 同様に、\(A_n\) が単調減少な集合列の時、すなわち \(A_1\supset A_2\supset A_3\supset\cdots\) の時は、以下の公式で極限値が計算できます。

\[ \lim_{n\to\infty}A_n = \bigcap_{n=1}^{\infty}A_n \]

 次に、細実数の概念を定義します。細実数とは私が勝手に定義した概念です。 実数 \(x\) に対して閉区間 \([0,x]\) を対応させて1つの数値とみなします。 誤解の恐れがなければ、細実数 \([0,x]\) のことを単に \(x\) と書きます。 つまり、\([0,1]\) は \(1\) と書きます。 そして、細実数間の大小関係を集合の包含関係で定義します。 すなわち、\(A\subset B\) なら \(A\lt B\) です。 ただし、\([0,x]\) の \(x\) が \(0\) 以上の範囲で考えます。

 四則演算とかは定義しません。うまい具合に定義できるのかもしれませんが、 今は必要ではありません。とりあえず必要なのは極限操作です。 閉区間は集合なので、極限集合で極限値が計算できます。

 今、 \(A_1=[0,0.9],~A_2=[0,0.99],~A_3=[0,0.999],\ldots\) のような単調増加な細実数列を考えます。 すると、\(A_n\) の極限が 0.999... と見なせます。\(A_n\) は単調増加ですから、 極限集合の単調増加の公式を用いると以下のように計算できます。

\[ 0.999...= \lim_{n\to\infty}A_n = \bigcup_{n=1}^{\infty}A_n = [0,1) \]

 細実数は閉区間で定義しましたが、半開区間も細実数ということにしましょう。 閉区間の細実数を閉細実数、半開区間の細実数を開細実数と呼ぶことにします。 細実数の大小は集合の包含関係で定義しましたから、 \([0,1)\subset[0,1]\) であり、\([0,1)\lt[0,1]\) ということになります。 つまり、0.999... は 1 より小さいということです。

 これは細実数が、通常の実数より高精度に大小関係を判断できるからです。 事実、\([0,1)\) の区間の長さも、\([0,1]\) の区間の長さも、 通常の実数の精度で見れば、どちらも 1 で区別できません。 つまり、実数で考えると 0.999... は 1 に収束しているということです。

 このように、実数より精度の高い順序を用いて考えれば、 0.999... が 1 よりわずかに小さくなるという初学者の直感は、 間違いなどではなく、数学的な根拠があるということです。 細実数が、0.999... < 1 となる詳しい理由が知りたい人は、 こちらのページへどうぞ。

 さて、最後にいくつか細実数の性質を見ておきましょう。 細実数は初め閉区間で定義しました。 しかし、極限を計算すると半開区間に収束して閉区間に収まりませんでした。 つまり、閉細実数は極限操作に対して閉じておらず、 開細実数を加えてようやく極限操作に対して閉じることになります。 これ、見覚えがありますよね。有理数は極限操作に対して閉じておらず、 無理数を加えてようやく極限操作に対して閉じることになるのとそっくりです。 有理数を極限操作で完備化すると実数が得られます。 なので実数は、もうこれ以上ないほどギチギチに詰まっていそうな印象ですが、 予想に反して、集合の包含関係を順序と考えると、 さらにもう1段詰め込めるということです。

 また、細実数では実数には見られない不思議な現象が起きます。 例えば次のような細実数列 \(A_n\) を考えます。 \(A_1=[0,0.9],~A_2=[0,1.1],~A_3=[0,0.99],~A_4=[0,1.01],~A_5=[0,0.999],~A_6=[0,1.001],\ldots\) 実数の感覚で考えると、これは \([0,1]\) に収束しそうに思えます。 ところが実際には、上極限と下極限が一致せず収束しません。 厳密さに欠ける表現になりますが、無限の向こうでも、 \([0,1)\) と \([0,1]\) の間で振動していると考えることができます。

【極限のプログラマ的解釈】

 今まで極限が可能性を実在とみなすと説明してきました。 なんだか抽象的でピンとこないかもしれません。 そこで、極限をプログラミングで考えてみたらどうなるか、 私の考えを説明しましょう。

 自然数や有理数をプログラムで書いたら以下のように書けます。

    int two() { return 2; }

 では、\(\sqrt{2}\) のような無理数をプログラムで書いたらどうなるでしょうか? 私は以下のようになると考えています。

    double sqrt2() {
        int n = 0;
        double an = 0.0, rn = 1.0;
        while (true) {
            int xn = 2 の平方根の小数点以下 n 桁目。ただし n == 0 なら整数部;
            an += xn / rn;
            n++;
            rn *= 10.0;
        }
        return an;
    }

 つまり、while が無限ループになっていて計算が終了しません。 これが可能性の意味です。 どこまでも \(\sqrt{2}\) に近づいていく可能性、それがこのプログラムです。

 しかし、計算が終了しないのでは、それ以上他の計算を続行できません。 これでは無意味ですよね。そこで考え方の階層を変えます。 今までプログラムの返す値を数値として考えていましたが、 プログラムのソースコード自身を数値と考えてみたらどうでしょう? これが極限の発想です。すると、sqrt2() も無意味ではなくなります。 つまり、可能性が実在になるのです。

 例えば \(2\sqrt{2}\) を計算する場合、 two() * sqrt2() を計算するのではなく、 以下のようなソースコードを数値とみなします。

    double twoSqrt2() {
        int n = 0;
        double an = 0.0, rn = 1.0;
        while (true) {
            int xn = 2 の平方根の小数点以下 n 桁目。ただし n == 0 なら整数部;
            an += 2 * xn / rn; // ここが、2 掛けるになっている
            n++;
            rn *= 10.0;
        }
        return an;
    }

 しかし、この方法、計算の難易度がかなり上がっていることが分かると思います。 単なる数値を処理していれば良かったものが、 数式処理をしなければならなくなったのですから。 つまり、本来実数の計算とは数式処理なのであって、極めて複雑だということです。

 ここで、少しばかり補足説明しておきましょう。 説明には Java 言語を用いましたが、次のことが気になった人もいるでしょう。 つまり、int は 32bit だし、double は 64bit だから、無限に計算を続けることはできないのではないかという問題です。

 確かに具体的値を計算しないといけないケース、 例えば \(\sqrt{2}\) と \(\pi\) どちらが大きいか? とかを図形的な証明を使わず数値的に比較しようとすると、これは問題になります。 しかし、そのような具体的値が必要ない場合は、数式処理に切り替えた段階で、 sqrt2() などのプログラムは実行する必要がなくなります。 そのため、変数のビット数が足りるかという問題は心配する必要がなくなります。 この辺りが議論の中から無限が消える仕掛けです。

 最後に、自然数全体の集合 \(\mathbb{N}\) のプログラムを示しておきましょう。 つまり、\(\lim\) が返す極限値は次のようなプログラムです。

    TreeSet<Integer> N() {
        int n = 0;
        TreeSet<Integer> set = new TreeSet<Integer>();
        while (true) {
            set.add(n);
            n++;
        }
        return set;
    }

 Java8 以上なら Stream を用いると、遅延評価という仕組みを使って、 具体的値が必要なければそれ以上計算しないようにして、 自然数全体の集合 \(\mathbb{N}\) のようなものを実際に扱うことができます。 そして、必要になった時点で必要なだけ集合の要素を取り出すことができます。

    import java.util.stream.Stream;

    class Natural {
        public static void main(String[] args) {
            Stream<Integer> N = Stream.iterate(0, n -> n + 1); // 自然数全体の集合
            N.limit(10).forEach(System.out::println); // 先頭の10個を表示
        }
    }

 ただし、具体的値を求めてしまうと、 変数のビット数が有限であることが問題になります。 つまり、大きすぎる数を求めてしまうと、値が変数に収まりきらなくなり、 極限の幻想が壊れてしまいます。