あるふぁのらくがき帳。

日々の数学やゲームの備忘録とか。

Minecraftで学ぶJavaプログラミング入門 #2: アイテムの追加

MinecraftのMod開発講座、第2回です。

 

mikan-alpha.hatenablog.com

 

前回は、開発を始まるまでの下準備を行いました。
今回から実際にゲームに触れてみましょう。

 

1. プロジェクトのインポート

Eclipseを起動します。

ロードの途中で、ワークスペースを選択するウィンドウが出ますので、適当なディレクトリをワークスペースにしておきます。
私はMod開発用に新規でディレクトリを作成してそれを選択しました。

ロードが完了したら、エディターが開きます。

f:id:mikan_alpha:20201113224738p:plain

初期状態

ここで、前回用意したプロジェクトを開きます。

メニューのファイルから、インポートを選択します。
ここで、「既存のGradleプロジェクト」を選びます。

f:id:mikan_alpha:20201113225130p:plain

検索すると、はやい!

次の画面で、前回用意したForgeのディレクトリを指定します。

f:id:mikan_alpha:20201113225440p:plain

あとは、完了を押して待ちます。

インポートが完了したあと、こんな感じで色々ずらっと増えていたらOKです。

f:id:mikan_alpha:20201113230254p:plain

読み込み後

試しに、src/main/javaの中の、com.example.examplemodパッケージに入っているEampleMod.javaを開いてみます。

f:id:mikan_alpha:20201113230918p:plain

Javaの世界へようこそ

こんな風に中身が問題なく表示できれば良いです。

ここから、開発を始めていきましょう。

2. Javaの基礎の話

一応Javaに触れたことない人にも読んでもらうことを目指しているので、今回出てくる要素の解説をざっくりと。

プログラムの書き方

Javaにおいては、セミコロン(;)で処理のまとまりを区切ってプログラムを書いていきます。基本的には、行の先頭からその行のセミコロンまでを処理のひとまとまり(最小単位)だと思っておけば問題ないでしょう。
このセミコロンの前に記述してある内容が、コンピューターへの命令となるわけです。

この処理がいくつか集まって、{}で囲まれたブロックのことを、Javaではメソッド(関数)と呼びます。細胞の集合が組織みたいなものです。
関数と書いたとおり、数学の経験がある人は数学の関数と同じと思ってもらって構いません。
何か入力を受け取って、中でなんらかの処理をして、そして値を返します*1

上で見たソースコードには、たくさん関数が記述されています。

f:id:mikan_alpha:20201114102100p:plain

先頭に修飾子、返り値の型(void)、メソッド名、引数の情報があり、{}で囲まれている範囲がこのメソッドの処理内容です。修飾子については詳しくは触れませんが、どこからこのメソッドにアクセスができるか、などのような情報を表すタグのようなものです。

さらにこのメソッドがいくつか集まってできるのが、クラスです。
Javaでプログラミングをしていく際の基本単位です。

先程のソースコードでは一番外側にこのように書かれています。

f:id:mikan_alpha:20201114102836p:plain

メソッドと同様に、修飾子が付いていてその後にclass、そしてクラス名となっていて、{}で囲んだブロックがこのクラスになります。

変数(メンバ変数、ローカル変数)

プログラミングに欠かせない要素の1つが、変数です。

これも、数学の変数と同じものと思ってもらっていいでしょう。

ただ一つ違う点があるとすれば、変数の中身が「数字」とは限らない、という点です。

例えば、「hello」という文字列を変数に代入することもできるし、何かオリジナルのオブジェクトを代入することもできます。このあたりは、この後すぐ出会うことになります。

 

こんな風に変数に色んなものを入れておけるので、中に何が入ってるのかコンピューターに教えるために、変数には「型」という概念があります。

変数を宣言したいときには、「型名 変数名;」というように記述します。

例えば、「int x;」と書くと(32bit)整数型の変数が一つ確保されます。
上の例で出した文字列の変数を宣言したければ、「String str;」というように。
変数名は、1文字でなくても良いです。というより、むしろプログラミングにおいては分かりやすい名前にしておくべきでしょう*2

 

上で見たソースコードにおいては、例えばこれが変数です。

f:id:mikan_alpha:20201114000731p:plain

変数の例

先頭の紫文字は今は無視して、型名と変数名を見ましょう。
この変数の型は「Logger」で、変数名は「LOGGER」ということになります。

またこの例のように、宣言と同時に変数に何か代入しておくこともできます。
Javaにおいては、「=」は(右辺を左辺に)代入の意味になりますので、プログラミングを始めたばかりの人は注意すべきポイントです。

 

また、変数には2種類あって、メンバ変数ローカル変数というものがあります。

この先で出てくる内容を含みますが、一応説明をしておくと、メンバ変数はクラス内かつメソッド外で宣言される変数、ローカル変数はメソッド内で宣言される変数という定義です。メンバ変数は、クラス内のメソッドだけでなくクラス外からも参照できたりしますが、ローカル変数は宣言されたそのメソッド内からのみ参照できる変数となります。(変数のスコープ)

3. Modをゲームに認識させる

先程開いたExampleMod.javaをそのまま書き換えていっても良いのですが、一応パッケージだけ作り直しておきます。

f:id:mikan_alpha:20201114104253p:plain

Javaの一般的な命名規則に従いました

ここで、これから作るModのIDを決めます。ユニークな文字列である必要があります。他のModと被らないものにしましょう。

決めたら、IDと同じ名前のパッケージを作ってそこにExampleMod.javaをコピーしておきます。

f:id:mikan_alpha:20201114105109p:plain

IDは「tutmod」にしました

リネームもしておきました。元々あったパッケージは削除して構いません。

さて、このプログラムをゲームに認識させるために、少しだけやることがあります。

f:id:mikan_alpha:20201114105455p:plain

まずこのクラスに付いているアノテーションの値を、先程自分で決めたIDに書き換えます。

 

次に、mods.tomlを開きます。

f:id:mikan_alpha:20201114105621p:plain

開いたら、modId=のところの値も自分のIDに書き換えます。

f:id:mikan_alpha:20201114105757p:plain

必要に応じて、他のところも書き換えておきましょう。
ここではdisplayName(表示名)だけ変えておきました。

他にもModのロゴ画像を表示できたり、公式サイトを載せたりできるので、確認してみてください。

 

最後に、メニューの「プロジェクト」からプロパティを開きます。

実行/デバッグ設定を開くと、以下のように予め3種類の起動構成が用意されているはずです。

f:id:mikan_alpha:20201114110038p:plain

ここで、「runClient」と「runServer」とそれぞれ編集します。

「環境」タブを開いて、MOD_CLASSESの「examplemod」と書いてある部分を自分のIDに置換してください。

f:id:mikan_alpha:20201114110330p:plain

終わったら、適用を押して閉じます。

 

準備が終わったので、この状態でデバッグをしてみましょう!

今編集した「runClient」を実行してみます。

f:id:mikan_alpha:20201114110652p:plain

 

こんな感じにMinecraftが起動できれば問題なしです。

f:id:mikan_alpha:20201114111044p:plain

 

Modsを開いて、今開発しているModが読み込まれていることを確認します。

lorem ipsumが読めます。

f:id:mikan_alpha:20201114111240p:plain

 

4. 無機能アイテムを追加する

Modを開発する準備も完璧に整いました。
ここから実際にアイテムを作ってみましょう。

 

アイテム本体

まずはアイテム本体のクラスを準備します。
管理のためにitemというパッケージを新しく作成して、そこにアイテムのクラスを入れていくことにします。

f:id:mikan_alpha:20201114133945p:plain

クラス名はMinecraft本体の命名規則に従っています

クラスを新規作成すると、最低限の記述のみのソースが自動で生成されます。

ここで、Minecraft本体のItem(net.minecraft.item.Item)クラスを継承します。

f:id:mikan_alpha:20201114134418p:plain

Eclipseの機能でコンストラクタも簡単に生成してくれます

こうするだけで、Minecraftのアイテムの最小限の機能だけを持ったアイテムが作れます!

右クリックしたときの処理を追加するとか、そういった独自の機能を持たせたい場合にはこのクラスでItemクラスのメソッドをオーバーライドしていきます。

 

アイテムの基本の設定だけしましょう。

コンストラクタにあるProperties(Item.Properties)の設定をします。これは名前の通りで、アイテムが最大で何個スタックできるか、耐久値はいくつか、どのクリエイティブタブに属するか、のような情報を持つクラスです。

今は飛ばしてしまうので、インスタンスだけスーパークラスに投げておきます。

f:id:mikan_alpha:20201114140436p:plain

次の行で、このアイテムのシステム内名称を設定しています。これは必ず設定しなければなりません。setRegistryNameの第1引数はMODのId、第2引数にアイテムの名前を渡します。

f:id:mikan_alpha:20201114140714p:plain

メインクラスでMODのIdを管理するようにしました

これで、アイテム自体は完成です。

 

アイテム登録用クラス

次にこのアイテムをゲームに登録します。
ModItemsというクラスを作って、そこでアイテムのインスタンス管理をすることにします。

f:id:mikan_alpha:20201114140937p:plain

こんな感じにアイテムを入れる変数とメソッドを定義してあげて、ゲームの初期化時にこのinit()を呼んであげればよいです。

f:id:mikan_alpha:20201114142202p:plain

publicにするのを忘れずに

外からアクセスするものなので、publicにします。

ゲーム初期化時にアイテムを登録する

Modのメインクラスに戻ります。下の方にいくと、RegistryEventsというクラスがあると思います。

ここにブロック登録用のメソッドが予め書かれているので、これを真似してアイテム用のメソッドを作ります。

f:id:mikan_alpha:20201114142601p:plain

ここでアイテムやブロックを登録します

onItemsRegistryの中で、ModItems.init()を呼び出して、そしてアイテムを登録します。

f:id:mikan_alpha:20201114143455p:plain

初期化を忘れるとぬるぽになる

これでゲームにアイテムが追加できたので、Minecraftを起動してみます。

giveコマンドでアイテムを召喚するとこんな風になると思います。

f:id:mikan_alpha:20201114143744p:plain

視界の1/4が謎の物体で埋まります。

ここから、テクスチャやアイテム名のローカライズの設定をしましょう。

5. アイテム名などを設定する

アイテム名などの設定は、あまりプログラムを使わずにできます。

src/main/javaではなくて、src/main/resourcesの方を開いて、以下のようにパッケージを作ります。
リソースパックなどに触れたことがある人は馴染みがあると思いますが、assetsの後ろがminecraftではなくてModのIdになるので気をつけましょう。

f:id:mikan_alpha:20201114144238p:plain

リソースパックと同じ

アイテム名を設定する

アイテム名のローカライズは、JSONを利用します。

以下のように、例として英語用の言語ファイルを作ってみます。

f:id:mikan_alpha:20201114144819p:plain

日本語ならja_jp.json

記法はJSONそのままです。左側にローカライズ前の文字列、右側にローカライズ後の文字列を書けばあとは勝手にゲーム側で書き換えてくれます。

f:id:mikan_alpha:20201114145028p:plain

アイテム名以外のローカライズにも使います。書き方は全部同じです。

このようにちゃんとローカライズされていたらOKです。

f:id:mikan_alpha:20201114145428p:plain

テクスチャを設定する

テクスチャの設定もJSONです。models.itemの中にアイテムのRegistryNameと同じ名前のファイルを置いておくだけで勝手に読み込んでくれます。

f:id:mikan_alpha:20201114150239p:plain

コピペ

ファイルの中身はバニラのリンゴからコピペしました。

自作のテクスチャにしたい場合には、layer0の値を書き換えます。

f:id:mikan_alpha:20201114150617p:plain

できた

6. 次回予告

今回はアイテムをやったので、次回はブロックの追加をします。

実は、結構似ているので次回は楽かもしれません。その後はレシピかな?

それではまた次回。

*1:ただし入力を受け取らない関数や値を返さない関数もあります

*2:変数名の長さは重要度に比例し、使用頻度に反比例すると良いです。

Minecraftで学ぶJavaプログラミング入門 #1: 開発環境の準備

※この講座は、以下のバージョンを想定しています。
Minecraft JavaEdition (1.13.x-)1.16.x
Minecraft Forge 34.1.0 (for 1.16.3)

 

1. はじめに

お久しぶりです。元々はYouTube上で動画にでもしようかと思っていたのですが、個人的に撮影する時間があんまり取れなさそうだったので、とりあえず文字ベースでやってみようかと思います。よろしくお願いします。

 

想定している読者は、(PCの)Minecraftをプレイしたことがあって(これが大事)、プログラミングはやったことないけど興味がある*1とか、ゲーム開発楽しそうだけど敷居が高くてあんまりできてないとか、そういう人たちです。

1からゲームを作るのとはまた違いますが、なんとなくゲームプログラミングの楽しさが伝わるといいなと思います。書いてる人がそもそもプログラミング素人なので、そんな高度なことはしません。というより、できません。気軽に読んでもらってOKです!あとはパソコンの基本的な操作とかができれば可。

 

この講座の目的は、MinecraftのModを自分の手で作りながら、Javaの基礎に触れることです。プログラミングをしなくてもModが作れるツールみたいなのもあった気がしますが、やっぱり自由度は格段に違いますし、何よりコードを書いてると頭がよくなった気分になれて楽しい!!!!ので、ここでは自分でコーディングします。

以下、目次です。

2. Java(JDK)をインストールする

さて、早速始めていきましょう。

Minecraftというゲームは、上でも少し話した通り、Javaという言語で書かれています。なので、基本そのModもJavaを用いて作ることになります*2

Minecraftというゲームは便利にできていて、ただプレイするだけならランチャーが勝手にJavaの実行環境(JRE)を用意してくれます。つまり、パソコンにわざわざJavaをインストールする、ということをせずに遊べてしまうわけです。

ただこの実行環境というのはランチャー内で使うだけの専用のものになっていて、「Javaでプログラミングしたい!」となったときは別途準備する必要があります。
(既にJDKを使ったことがある人はこの項は飛ばして構いません)

 

ここで登場するのが、JDK改めJava Development Kitです。

これは、Javaでプログラミングをするために必要な道具が一式そろった開発キットです。Mod開発をしていくにはこれがないと始まりせんので、準備しましょう。

本講座ではAmazonさんのAmazon Corretto 8を使うことにします。

aws.amazon.com

上のページからAmazon Corretto 8をダウンロードして、インストールしてください。

ボタンを押すとダウンロードURLがずらっと出てくるので、お使いのプラットフォームに合わせたものをダウンロードしましょう。

f:id:mikan_alpha:20201108163625p:plain

Windowsの場合は、インストーラー形式(.msi)のものが一番簡単だと思います。

2. 統合開発環境(IDE)を用意する

さて、プログラミングに欠かせないものと言えば、便利なエディタです。ここでは、Minecraft Forgeも標準でサポートしている、Eclipseというエディタを使います。

Google等で「Eclipse」と検索すると以下のサイトがトップに出てくると思います。

mergedoc.osdn.jp

サイトトップのページから「Eclipse 2020」を選択します。

f:id:mikan_alpha:20201108163937p:plain

移ったページで、Javaの欄のFull Editionから同様にお使いのプラットフォームのEclipseをダウンロードします。

f:id:mikan_alpha:20201108164217p:plain

サイズが大きいので、気長に待ちましょう。

(あれ、JDK自分で用意する必要なかったな……?)

 

ダウンロードが終わったら、インストールする必要はないソフトなので解凍して分かりやすい場所に置いておいてください。

3. Minecraft Forgeのセットアップ

ここからは、MinecraftのMod開発特有の準備です。

今回はMinecraft Forgeを使ってModを作るので、この下準備をします。

Google等で「Minecraft Forge」で検索すると公式サイトがヒットします。

files.minecraftforge.net

現在(2020/11/08)はトップにMinecraft 1.16.3用のものが表示されていますが、そうでなければ左のメニューから1.16.3を選択してください。また、異なるバージョンのModが作りたい場合には同様に左メニューからModを作りたいバージョンを選びます。
(この講座で扱うバージョンでない場合、Modの書き方が大きく異る場合があるのでそれぞれのバージョンの講座を参照してください

バージョンを選択したら、Recommended(推奨版、安定版)の方のMdkをダウンロードします。

f:id:mikan_alpha:20201108165421p:plain

Sourcesではないので注意(昔はMdkがsrcでした)

別にLatest(最新版)でもいいのですが、Recommendedがある場合はこちらを選んでおいた方が無難です。Forgeに滅多にバグは見ないのですが*3、念の為。

これもダウンロードが済んだら解凍して分かりやすい場所に置いてください。
解凍する場所ですが、パスに2バイト文字が含まれない場所が望ましいです。

 

次に、これをこれから使うためのセットアップをします。

「gradlew」等があるトップの階層に、バッチファイルを作成します(Windowsの場合)。

f:id:mikan_alpha:20201108170214p:plain

setup.batという名前で作成しました

そしてこのバッチファイルの中身に、「gradlew genEclipseRuns」と書いて起動します。

f:id:mikan_alpha:20201108170456p:plain

※表示される内容は若干異なっていて大丈夫です

これも時間がかかりますので、お茶でも飲みながら気長に待ちます。
特にエラー等吐いてなければ問題ないです。

最後に緑文字が出てプロンプトが閉じたら準備完了です!お疲れさまでした。

ディレクトリ内に色々ファイルが増えていると思います。

4. 次回予告

今回でMod開発を始めるための準備が整いました!

次回は実際に、無機能なアイテムをゲームに追加してみたいと思います。

今回はまだコーディングする場面がありませんでしたが、次回からは逆にほぼずっとコーディングです、お楽しみに。

*1:でも、入門とは書いたけど本当にやったことないとちょっと難しいかも……。C++みたいな似た言語を触ったことがあれば、とても役に立つと思います。

*2:Kotlinでもできたりしますが、ここでは触れないことにします。

*3:自分も過去一度しかForge自体が原因のバグに遭遇したことがないです。

今から15回の反復計算で円周率を20億桁求めます

みなさん、お手元に無限の精度*1の電卓のご用意はできていますでしょうか。

2つ変数を用意します。

f:id:mikan_alpha:20200920220608p:plain

初期条件

以下の漸化式に従って、1 / z_{15} を求めます。15回繰り返しましょう。

f:id:mikan_alpha:20200920221241p:plain

漸化式

そうしたらば、きっとあなたの手の中には20億個の見覚えのある数たちが列を成していることでしょう。

*1:もしなければ、10^10桁程度の精度があるものが望ましいです

項別微分っていつできる?

自分用の備忘録も兼ねて、項別微分について書こうと思います。

ある関数列 \{ f_n(x) \}_{n \geq 1} について、\sum_{n=1}^{\infty} f_n(x) という関数を考えます。

この関数を微分したいと思ったとき、普通は(微分の線形性的に)項別に微分しようとしてしまうと思うのですが、実はこれができないケースが存在します。
(これは微分の定義にも極限が絡んでいるために、極限の交換が発生するためです)

具体的には、以下のような定理が知られています。

定理 1.
区間 I 上で微分可能でかつその導関数が連続である(C^1 級)関数列 \{ f_n(x) \}_{n \geq 1} に対して、

(i) \sum_{n=1}^{\infty} f_n(x)IS(x)各点収束

(ii) \sum_{n=1}^{\infty} f_n'(x) が IT(x)一様収束

の2つを満たすとき、S(x) もまた I 上の各点で連続で微分可能になり S'(x) = T(x) が成立する。

すなわち、\frac{d}{dx} \sum_{n=1}^{\infty} f_n(x) = \sum_{n=1}^{\infty} \frac{d}{dx} f_n(x) となる。(微分と総和が交換可能)

今日はこの証明のために、一様収束などの話をしたいと思います。

一様収束とは?

まず、関数の一様normというものを定義します。

定義 1.
ある区間 I 上で定義された関数 f について、

\begin{align}
\| f \| := \sup_{x \in I} | f(x) |
\end{align}

f一様normという。

このノルムは、一般的な絶対値と同じ性質を持ちます。(三角不等式など)

この一様normを用いて、一様収束という概念を定義します。

定義 2.
区間 I 上で定義された関数列 \{ f_n(x) \}_{n \geq 1} を考える。
このとき、\{ f_n(x) \}_{n \geq 1}I 上の関数 f(x) に一様収束することを、\lim_{n \to \infty} \| f_n - f \| = 0 により定義する。

ある区間 I 上の関数列 \{ f_n(x) \}_{n \geq 1} が 関数 f(x) に一様収束するとき、I 上の各点 x_0 において f_n(x_0)f(x_0) に収束します。
(これは直感的にも明らかだと思うので証明は省きます)

この I 上の各点 x_0 において f_n(x_0)f(x_0) に収束、が成り立つとき、\{ f_n(x) \}_{n \geq 1}f(x)各点収束するといいます。一様収束するならば各点収束しますが、逆は一般に成り立ちません。

また、一様収束する連続な関数列の収束先は、また連続になります。

一様収束の性質

色々な性質がありますが、ここでは冒頭の定理の証明に必要なものだけピックアップしました。

定理 2.
区間 I:[ a,b ] 上で一様収束する連続関数列 \{ f_n(x) \}_{n \geq 1} に対して、以下が成り立つ。

\begin{align}
\lim_{n \to \infty} \int_a^b f_n(x) \, dx = \int_a^b \lim_{n \to \infty} f_n(x) \, dx
\end{align}

定理 3.
区間 I 上で一様収束する連続な導関数列 \{ f_n'(x) \}_{n \geq 1} に対して、f_n(x) もまた各点収束するとき以下が成り立つ。

\begin{align}
\lim_{n \to \infty} \frac{d}{dx} f_n(x) = \frac{d}{dx} \lim_{n \to \infty} f_n(x)
\end{align}

定理2の証明

f(x) := \lim_{n \to \infty} f_n(x) とする。仮定より、f(x) もまた連続関数である。

\begin{align}
0 &\leq | \int_a^b f_n(x) \, dx - \int_a^b f(x) \, dx | \\
&= | \int_a^b \left( f_n(x) - f(x) \right) \, dx | \\
&\leq \int_a^b | f_n(x) - f(x) | \, dx \\
&\leq \int_a^b \| f_n - f \| \, dx \\
&= \| f_n - f \| (b - a)
\end{align}

従って、以下を得る。

\begin{align}
0 \leq | \int_a^b f_n(x) \, dx - \int_a^b f(x) \, dx | \leq \| f_n - f \| (b - a)
\end{align}

ここで各辺において n \to \infty とするとはさみうちの原理から以下が従う。

\begin{align}
\lim_{n \to \infty} | \int_a^b f_n(x) \, dx - \int_a^b f(x) \, dx | = 0
\end{align}

よって、定理が示された。

\begin{align}
\lim_{n \to \infty} \int_a^b f_n(x) \, dx = \int_a^b f(x) \, dx
\end{align}

定理3の証明

g(x) := \lim_{n \to \infty} f_n'(x) とする。仮定より、g(x) もまた連続関数である。

x, x_0 \in I に対して、

\begin{align}
\int_{x_0}^x g(t) \, dt &= \int_{x_0}^x \lim_{n \to \infty} f_n'(t) \, dt \\
&= \lim_{n \to \infty} \int_{x_0}^x f_n'(t) \, dt \\
&= \lim_{n \to \infty} \left[ f_n(t) \right]_{x_0}^x \\
&= \lim_{n \to \infty} \left( f_n(x) - f_n(x_0) \right) \\
&= f(x) - f(x_0)
\end{align}

従って、以下を得る。

\begin{align}
f(x) = \int_{x_0}^x g(t) \, dt + f(x_0)
\end{align}

ここで、両辺を x を変数として微分する。

\begin{align}
\frac{d}{dx} f(x) &= \frac{d}{dx} \left( \int_{x_0}^x g(t) \, dt + f(x_0) \right) \\
&= g(x) \\
&= \lim_{n \to \infty} f_n'(x)
\end{align}

f(x) = \lim_{n \to \infty} f_n(x) から、定理が示された。

\begin{align}
\frac{d}{dx} \lim_{n \to \infty} f_n(x) = \lim_{n \to \infty} \frac{d}{dx} f_n(x)
\end{align}

冒頭の定理の証明

上で証明した定理3を使うと、すぐに示すことができます。

定理1の証明

\begin{align}
\frac{d}{dx} S(x) &= \frac{d}{dx} \sum_{n=1}^{\infty} f_n(x) \\
&= \frac{d}{dx} \left( \lim_{n \to \infty} \sum_{k=1}^{n} f_k(x) \right) \\
&= \lim_{n \to \infty} \frac{d}{dx} \sum_{k=1}^{n} f_k(x) \\
&= \lim_{n \to \infty} \sum_{k=1}^{n} f_k'(x) \\
&= \sum_{n=1}^{\infty} f_n'(x)
\end{align}

よって、S'(x) = T(x) が成立。
ただし、2行目から3行目の微分と極限の交換に定理3を用いた。

具体例とか

最後に、項別微分不可能な例を紹介したいと思います。

以下のような関数を考えてみます。

\begin{align}
f(x) = \sum_{n=1}^{\infty} a^n \cos (b^n x)
\end{align}

a,b はそれぞれ正の定数で、a < 1b > 1ab > 1 とします。

級数判定法というのを用いると、この(関数項)級数は一様絶対収束することが分かります。これで各点収束の条件は満たされています。
また、各項は明らかに連続な関数なので、その収束先である f(x) は連続であることも分かります。

あとは、f_n(x) := a^n \cos (b^n x) としたとき、\sum_{n=1}^{\infty} f_n'(x) が一様収束するかが重要になります。

実際に計算してみると f_n'(x) = - a^n b^n \sin (b^n x) となります。ここで、ab > 1 の仮定から、これの総和は明らかに発散してしまいます。

従って、導関数列の和は一様収束しないので項別微分不可能ということになります。

次回予告とか

夏も真っ盛りで、暑苦しい日々が続いていますね。私も毎日溶けそうになっています。

まあ実際はほとんど家の中でゲームしてるんですけど……。

次回は、ちょっと計算機科学よりの内容か、自己同形数の話を書いてみるか、どっちかかなあという気分です。全然予定は立ててないので確定ではないです。

一様収束の話とか、自分も最近初めて学んだのですが、他の数学を学ぶ人の役に立ったらいいなと思います。というわけで、お疲れさまでした。

N以下の素数の逆数和の発散速度がlog log Nくらいなことのお気持ち

なんか適当に検索かけても出なかったのでお気持ちだけ書きます。(厳密な証明は英語Wikipediaあたりにあります)

以下、特に断りが無ければ p素数とします。

さて、以下のような事実が知られています。

\begin{align}
\sum_{p} \frac{1}{p} = \infty
\end{align}

素数の逆数和は、調和級数(全ての自然数の逆数和)と同様に発散します。感覚的には、平方数の逆数和は \frac{\pi^2}{6} に収束するので、素数は全ての自然数よりは少なくて、でも平方数よりはずっと多いといった感じです。

一方で、上の無限和は発散することには発散するのですが、その速度がめちゃくちゃ遅いことでも有名です。具体的には下のようになります。

\begin{align}
\sum_{p \leq n} \frac{1}{p} = O(\log \log n)
\end{align}

対数のさらに対数ということでかなり遅いことが見て取れます。気になる方は、お手元の電卓か、Wolfram|Alphaで大きい数で計算してみましょう。
ざっくり計算したければ、N という整数の対数 \log N はだいたいその桁数くらいなので、それを2回すると良いです。かなり小さい数字になることが分かると思います。

お役立ち情報

この発散速度が役に立つ場面として、計算機科学における素数判定アルゴリズムの一つである、エラトステネスの篩の(時間)計算量があります。

エラトステネスの篩とは、ある与えられた整数 N 以下の素数を見つけるためのアルゴリズムです。詳しい話は端折りますが、計算量解析をしてあげると、このアルゴリズムの計算量は O(N \log \log N) 程度になります。愚直に整数を1つずつ N まで素数判定をすると O(N \sqrt{N}) になってしまうので、かなりの計算量の改善になっています。

お気持ち

それでは本題です。使う道具は、ゼータ関数オイラー積表示と関数 \log (1+x)テイラー展開、そして調和級数の発散速度が対数オーダーであることです。

\begin{align}
\zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s} = \prod_{p} \left( 1 - p^{-s} \right)^{-1}
\end{align}

\begin{align}
\log(1+x) = x - \frac{1}{2} x^2 + \frac{1}{3} x^3 - \frac{1}{4} x^4 + \cdots
\end{align}

\zeta(1) の対数をとります。

\begin{align}
\log \zeta(1) &= \log \left( \sum_{n=1}^{\infty} \frac{1}{n} \right) \\
&= \log \left( \prod_{p} \left( 1 - \frac{1}{p} \right)^{-1} \right) \\
&= - \sum_{p} \log \left( 1 - \frac{1}{p} \right)
\end{align}

総和の中身の \log ( 1 - 1/p ) をテイラー級数を使って展開します。

\begin{align}
- \sum_{p} \log \left( 1 - \frac{1}{p} \right) &= - \sum_{p}  \left( - \frac{1}{p} - \frac{1}{2} \cdot \left( \frac{1}{p} \right)^2 - \frac{1}{3} \cdot \left( \frac{1}{p} \right)^3 - \cdots \right) \\
&= \sum_{p}  \left( \frac{1}{p} + \frac{1}{2} \cdot \left( \frac{1}{p} \right)^2 + \frac{1}{3} \cdot \left( \frac{1}{p} \right)^3 + \cdots \right) \\
&= \sum_{p} \frac{1}{p} + \frac{1}{2} \sum_{p} \frac{1}{p^2} + \frac{1}{3} \sum_{p} \frac{1}{p^3} + \cdots
\end{align}

したがって、

\begin{align}
\log \left( \sum_{n=1}^{\infty} \frac{1}{n} \right) = \sum_{p} \frac{1}{p} + \frac{1}{2} \sum_{p} \frac{1}{p^2} + \frac{1}{3} \sum_{p} \frac{1}{p^3} + \cdots
\end{align}

が得られました。

左辺はもう既に完成されてますので、右辺に注目します。

1つ目の項以外は、それぞれ以下のように上から評価できるので、有限値に収束することが分かります。(\zeta(s)s > 1 で絶対収束するため)

\begin{align}
\sum_{p} \frac{1}{p} + \frac{1}{2} \sum_{p} \frac{1}{p^2} + \frac{1}{3} \sum_{p} \frac{1}{p^3} + \cdots \quad \leq \quad \sum_{p} \frac{1}{p} + \frac{1}{2} \zeta(2) + \frac{1}{3} \zeta(3) + \cdots
\end{align}

有限の値は収束発散には影響を及ぼさないので、有限値の部分を全てまとめて S とおいてあげると、

\begin{align}
\log \left( \sum_{n=1}^{\infty} \frac{1}{n} \right) = \sum_{p} \frac{1}{p} + S
\end{align}

となります。ざっくり、有限項の和でもこの関係が成り立っていると考えてあげると、以下が得られます。

\begin{align}
\sum_{p \leq N} \frac{1}{p} \quad \approx \quad \log \left( \sum_{n=1}^{N} \frac{1}{n} \right) \quad \approx \quad \log \left( \log N \right)
\end{align}

Cauchyの平均値の定理とTaylor展開

Cauchyの平均値の定理を用いてTaylorの定理を証明します。

Cauchyの平均値の定理

関数 g,h は閉区間 [a,b] 上で連続かつ開区間 (a, b) 上で微分可能であって、h(b) \not= h(a) かつ区間内の各点 x において (g'(x), h'(x)) \not= (0, 0) とする。このとき、

\begin{align}
\frac{g(b) - g(a)}{h(b) - h(a)} = \frac{g'(c)}{h'(c)}
\end{align}

を満たす c \in (a,b) が存在する。

証明

f(x) := (g(x) - g(a))(h(b) - h(a)) - (g(b) - g(a))(h(x) - h(a)) とおく。

このとき、f(a) = f(b) = 0 である。これは定義式に代入することで直ちに確かめられる。

ここで(Lagrangeの)平均値の定理から、

\begin{align}
\frac{f(b) - f(a)}{b-a} = f'(c)
\end{align}

を満たす c \in (a,b) が存在する。ここで、左辺は 0 となることに注意する。

f導関数

\begin{align}
f'(x) = g'(x) \cdot (h(b) - h(a)) - h'(x) \cdot (g(b) - g(a))
\end{align}

であるから、ここで x=c とすることで定理の式を得る。

Taylorの定理

区間 a, b 上で連続かつ開区間 (a,b) 上で n微分可能な関数 f に対して、

\begin{align}
f(b) = f(a) + f'(a)(b-a) + \frac{f''(a)}{2!} (b-a)^2 + \cdots + \frac{f^{(n-1)}(a)}{(n-1)!} (b-a)^{n-1} + \frac{f^{(n)}(c)}{n!} (b-a)^{n}
\end{align}

を満たす c \in (a,b) が存在する。

証明(n=2の場合)

平均値の定理より、n=1 のケースは既に示されている。

\begin{align}
f(b) = f(a) + f'(c) (b - a)
\end{align}

g(x) := f(x) + f'(x)(b-x), h(x) := (b-x)^2 とおく。

ここで、Cauchyの平均値の定理を適応すれば、

\begin{align}
\frac{f(b) - (f(a) + f'(a)(b-a))}{0 - (b-a)^2} = \frac{f''(c)(b-c)}{-2 (b-c)}
\end{align}

となる c \in (a,b) が存在する。

この式を整理することで n=2 のケースが示される。

また一般の n についても、同様に証明ができる。

Taylor展開

Taylorの定理を書いたのでわざわざ別に書くまででもないですが、Taylorの定理において関数が無限回微分可能で、剰余項が n \to \infty としたとき0に収束するならばTaylor展開可能です。

微分方程式の美味しい炊き方、そして黄金比を食べることによるその効果。

お久しぶりです。今日は数学の話をします。

今回は、この微分方程式を解いていきたいと思います。

\begin{align}
f'(x) = f^{-1}(x)
\end{align}

ある関数の導関数逆関数が等しい、と言っています。

どうでしょう、ぱっとすぐに解が思いつきますか?
一体どんな関数がこの方程式の解なのでしょうか。

タイトルでネタバレしてないか?

観察 1. f'(x) = f(x)

まずは簡単なものから考えてみましょう。

最初にこれを解いてみます。

\begin{align}
f'(x) = f(x)
\end{align}

これはすぐに解ける人も多いことでしょう。

何回微分しても変わらない、これは、指数関数の特徴ですね。
上の微分方程式の解は、f(x) = e^x です。

また、x に依存しない定数をかけておいてあげても微分には影響しないので、もっと一般的に、f(x) = Ce^xC は定数)が解となります。

ちょっと天の邪鬼な人は、f(x) = 0 も解であることに気がついているでしょう。ただ、自明な解はあまり面白くないので、今日はこれは無しにしておきます。以下も同様です。

ここで、f(x) = Ce^x としたときの逆関数 f^{-1}(x) についても見ておきます。

式変形してあげると、f^{-1}(x) = \log \frac{x}{C} です。
これはどうも、最初の微分方程式を満たしそうにありません。

観察 2. f''(x) = -f(x)

さて次はこれを見てみます。

\begin{align}
f''(x) = - f(x)
\end{align}

2回微分すると、自身の符号を反転したものに等しい関数を探せ、ということです。
これも、分かる人にはすぐ解かれてしまうでしょう。

例えば、f(x) = \sin x としてあげれば解の1つになります。また、別の人は f(x) = \cos x としたかもしれません。

これもまた一般化してあげると、f(x) = A \sin x + B \cos xA,B は定数)という形(三角関数の線形結合)の関数は全て解になります。

さて、また逆関数についても考えておきたいですが……線形結合した形を変形していくのは少し面倒です。

r = \sqrt{A^2 + B^2} とおいてあげれば、f(x) = r \left( \frac{A}{r} \sin x + \frac{B}{r} \cos x \right) ですから、三角関数を合成してあげて、f(x) = r \sin (x + \alpha) のような形になるでしょう。

これを x について解いてあげれば、f^{-1}(x) = \arcsin(\frac{x}{r}) - \alpha でしょうか。

あまり綺麗じゃない気がしますが、とりあえず逆三角関数であることだけ分かれば十分です。三角関数と逆三角関数は、名前こそ似ていても違う性質の関数であることに注意しておきます。

少なくとも、これも最初の微分方程式の解にはならなそうです。

観察から分かること

2つほど例を上げてみましたが、何か気づくことはありましたでしょうか。私は気が付きませんでした。

ある関数 f(x)f'(x) = f^{-1}(x) を満たす時、どんな条件が必要でしょうか。

それは、f'(x)f^{-1}(x)共に同じ性質種類であることです。
数学らしからぬふわっとした言葉ですが……。

つまりは、例えば f'(x)指数関数ならば同時に f^{-1}(x)指数関数でなければならなくて、f'(x)三角関数ならば同時に f^{-1}(x)三角関数でなければならないわけです。
しかしながら、この2つの関数についてはこれは有り得ないということを上で述べました。指数関数の逆関数は対数関数になってしまいますし、三角関数逆関数は逆三角関数です。

では、これが成り立つような関数にはどんなものがあるでしょうか?

 

私たちは、どうやら最も簡単な例を忘れているようです。

多項式関数

ここで、多項式関数について考えてみます。多項式と言いつつも、ここでは1項のみからなる関数を考えましょう。

f(x) = A x^rA は定数、r \not= 0)とおきます。指数は整数に限りません。

この関数の導関数は、明らかに f'(x) = rA x^{r-1} です。

逆関数はどうでしょうか?

少し計算すれば、f^{-1}(x) = (\frac{x}{A})^{1/r} となることが分かります。

そしてこの2つは、実は上であげた解となる条件を満たしています
どちらも多項式関数の形を保っているのがお分かりでしょうか。

計算のために、少し式を整理します。

\begin{align}
f'(x) &= (rA) x^{r-1} \\
f^{-1}(x) &= A^{-1/r} x^{1/r}
\end{align}

これでもう明らかです!

あとは係数部分、指数部分について r,A連立方程式を解いてあげましょう。

指数に着目してあげれば、

\begin{align}
r-1 = \frac{1}{r}
\end{align}

という式が出てきます。
これを整理してあげれば、

\begin{align}
r^2 - r - 1 = 0
\end{align}

です。おや、いやに見覚えがありませんか?
解の公式などでこれを解けば、

\begin{align}
r = \frac{1 \pm \sqrt{5}}{2}
\end{align}

となりました。

これは、黄金比 \phi とその共役無理数 \bar{\phi} です!

\begin{align}
\phi = \frac{1 + \sqrt{5}}{2} \quad , \quad \bar{\phi} = \frac{1 - \sqrt{5}}{2}
\end{align}

あとはこれを使って係数部分についても方程式を解いてあげましょう。

\begin{align}
\phi A = A^{-1 / \phi}
\end{align}

式を整理します。

\begin{align}
A^{1 + 1 / \phi} = \frac{1}{\phi}
\end{align}

ここで黄金比の性質 \phi^{-1} = \phi - 1 を使うと、

\begin{align}
A^{\phi} = \frac{1}{\phi}
\end{align}

こうです!後はもう簡単です。

\begin{align}
A = \sqrt[\phi]{\phi^{-1}}
\end{align}

これで微分方程式を解くことができました!

まとめ

今日はこんな微分方程式を扱いました。

\begin{align}
f'(x) = f^{-1}(x)
\end{align}

ここで解を多項式関数と仮定してあげると、解(の1つ?)である以下の関数が出てきました。

\begin{align}
f(x) = \sqrt[\phi]{\phi^{-1}} x^{\phi}
\end{align}

自分もこの微分方程式を解いていて「まさか黄金比が出てくるとは……」という感じでした。

微分方程式の解き方的には、他の解の存在とかが分からないので良くないのかもしれませんが、ともかく解の一例を与えることができました。

この微分方程式の解って一意なのでしょうか?微分方程式の解の一意性等について詳しい方がいたら、情報をお待ちしています。

以上、お疲れさまでした。

参考


A very interesting differential equation.