xを無限に積み上げた次の式を、JavaScriptとSVGで可視化してみます。
$$
y=x^{x^{x^{\cdot^{\cdot}}}}
$$
今回作るグラフでは、底 $x$ とべき塔の高さ $n$ をスライダーで変更できます。有限段の近似値、無限べき塔の極限、さらに $x=0$ 付近で奇数段と偶数段に分岐する様子を確認します。
この記事は、MathAbyssの動画「無限回計算する数学の世界」の指数タワーに関する解説をきっかけに、反復過程をブラウザ上で操作できるグラフとして実装したものです。
反復として定義する
無限べき塔は、まず有限段の数列として考えると実装しやすくなります。
$$
y_0=1,\qquad y_{n+1}=x^{y_n}
$$
JavaScriptでは、そのまま次の関数になります。
function tower(x, depth) {
let y = 1;
for (let i = 0; i < depth; i++) {
y = Math.pow(x, y);
// 発散時のInfinityや描画負荷を避ける
if (!Number.isFinite(y) || y > 30) {
return 30;
}
}
return y;
}
たとえば $x=\sqrt{2}$ の場合、反復値は次のように2へ近づきます。
$$
\sqrt{2},\quad
\sqrt{2}^{\sqrt{2}},\quad
\sqrt{2}^{\sqrt{2}^{\sqrt{2}}},\quad\ldots\to 2
$$
無限べき塔が収束する範囲
極限が存在するとして $y=x^y$ と置きます。$x$ について解けば、
$$
x=y^{1/y}
$$
です。
$x=y^{1/y}$ は $y=e$ で最大となり、上側の境界は
$$
x=e^{1/e}\simeq1.444667861
$$
になります。
反復 $y_{n+1}=x^{y_n}$ が安定する条件も考えると、実数の無限べき塔が収束する範囲は
$$
e^{-e}\le x\le e^{1/e}
$$
すなわち、
$$
0.065988\ldots\le x\le1.444667\ldots
$$
です。
グラフの極限曲線は $y$ を媒介変数として描けます。
const lowerY = 1 / Math.E;
const upperY = Math.E;
const points = [];
for (let i = 0; i <= 280; i++) {
const y = lowerY + (i / 280) * (upperY - lowerY);
const x = Math.pow(y, 1 / y);
points.push({ x, y });
}
この方法なら、Lambert W関数を直接実装しなくても極限曲線を描画できます。
0付近で2本に分岐する理由
$x\to0^+$ のとき、有限べき塔の最初の数段は次のようになります。
$$
y_1=x\to0
$$
$$
y_2=x^x\to1
$$
$$
y_3=x^{x^x}\to0
$$
したがって、
- 奇数段 $y_1,y_3,y_5,\ldots$ は0側
- 偶数段 $y_2,y_4,y_6,\ldots$ は1側
に分かれます。
$0<x<e^{-e}$ では固定点 $y=x^y$ 自体は存在しますが、その固定点は反復に対して不安定です。そのため、数列は固定点には近づかず、奇数段と偶数段の2枝に振動します。
分岐部分は、複数の高さを同時に計算すれば描画できます。
for (let depth = 1; depth <= 8; depth++) {
const points = [];
// x=0そのものでは0^0が現れるため、正の微小値から始める
for (let i = 1; i <= 420; i++) {
const x = (i / 420) * 0.14;
points.push({ x, y: tower(x, depth) });
}
drawCurve(points, {
className: depth % 2 === 1 ? "odd" : "even"
});
}
SVGのパスへ変換する
計算した点列をSVGの座標へ変換します。
const plot = {
left: 66,
top: 20,
width: 666,
height: 350,
xMin: 0,
xMax: 1.55,
yMin: 0,
yMax: 3.2
};
function scaleX(x) {
return plot.left
+ ((x - plot.xMin) / (plot.xMax - plot.xMin)) * plot.width;
}
function scaleY(y) {
return plot.top + plot.height
- ((y - plot.yMin) / (plot.yMax - plot.yMin)) * plot.height;
}
function toPath(points) {
return points.map((point, index) => {
const command = index === 0 ? "M" : "L";
return `${command}${scaleX(point.x)},${scaleY(point.y)}`;
}).join(" ");
}
document.querySelector("#finite-path")
.setAttribute("d", toPath(points));
スライダー変更時に再描画する
input イベントで値を取得し、有限べき塔の曲線と選択点を更新します。
<label>
底 x
<input id="base" type="range"
min="0.0001" max="1.55"
step="0.0001" value="1.4142">
</label>
<label>
高さ n
<input id="depth" type="range"
min="1" max="50"
step="1" value="10">
</label>
const baseInput = document.querySelector("#base");
const depthInput = document.querySelector("#depth");
function update() {
const selectedX = Number(baseInput.value);
const depth = Number(depthInput.value);
const points = [];
for (let i = 1; i <= 320; i++) {
const x = (i / 320) * 1.55;
points.push({ x, y: tower(x, depth) });
}
document.querySelector("#finite-path")
.setAttribute("d", toPath(points));
const selectedY = tower(selectedX, depth);
updateSelectedPoint(selectedX, selectedY);
updateConvergenceLabel(selectedX);
}
baseInput.addEventListener("input", update);
depthInput.addEventListener("input", update);
update();
実装時の注意点
x=0を直接計算しない
有限べき塔では段数によって $0^0$ が現れます。グラフのサンプリングは 0.000001 など、正の微小値から始めるのが安全です。
発散値をクリップする
$x>e^{1/e}$ では値が急激に大きくなります。Infinity をそのままSVG座標へ渡さず、上限値を決めてクリップします。
境界では収束が非常に遅い
$x=e^{-e}$ と $x=e^{1/e}$ では、有限段の値が極限へ近づく速度がかなり遅くなります。「50段計算したから十分」とは限らない点に注意が必要です。
まとめ
無限べき塔は単に $y=x^y$ の曲線を描くだけでなく、反復
$$
y_{n+1}=x^{y_n}
$$
の安定性を見ることで全体像が分かります。
- 収束域は $e^{-e}\le x\le e^{1/e}$
- $x=\sqrt2$ では極限は2
- $0<x<e^{-e}$ では奇数段と偶数段が2枝に分岐
- JavaScriptとSVGを使えば、パラメータ変更を即座にグラフへ反映できる
静的な曲線だけでなく、有限段の反復を重ねて表示すると、0付近で起きている現象も視覚的に理解しやすくなります。
参考資料・引用元
本記事の説明とJavaScriptによるグラフ実装は、動画を参考に独自に再構成しています。
