オタクof数理の共同ブログ

京大情報学科数理工学コースの学生4人による共同ブログです

数学のメモその3

今回は、一旦ゼータ関数を置いといてフーリエ解析に関することを書きます。
授業の中でPlancherel(プランシュレル)の定理というものを習いました、

Plancherelの定理
f,g,\hat{g}\in L^1(\mathbb{R})\Rightarrow \langle\hat{f},\hat{g}\rangle=\langle f,g\rangle

f\in L^1(\mathbb{R}):有界連続\Rightarrow\|f\|=\|\hat{f}\|\ (f,\hat{f}\in L^2(\mathbb{R}))

というものなんですが授業ではさらっと流されて証明もプリントで、、、みたいな感じやったんですけどこの定理は結構面白いことを言ってるんじゃないかなあと思いました。

適当な正規直交関数系\displaystyle \{f_i (x)\}_{i=1}^{\infty}を一つ考えてみましょう。正規直交関数系とは

\begin{equation}
\langle f_i,f_j\rangle=\delta_{i,j}
\end{equation}

を満たすもののことです。勘の良い方はこれで僕が何を言いたいのか分かったかも知れませんね笑。この関数が{}^{\forall}i\in\mathbb{N}に関してf_i(x)\in L^2(\mathbb{R})などなど適当な条件を満たしているとしましょう。f_iのFourier変換を\mathcal{F}f_i=\hat{f}_iと書くことにすると、
\langle \hat{f}_i,\hat{f}_j\rangle=\langle f_i,f_j\rangle=\delta_{i,j}

がプランシュレルの定理から分かります。これが何を意味しているかというと適当な条件を満たす正規直交関数系をひとつ持ってこればそれをFourier変換して得られる系もまた正規直交関数系となるのです!!

教授氏にこのことについて質問してみると、チェビシェフの多項式においてこのことが成り立つそうです。また時間があるときに確かめてみたいです。
それでは。

数学のメモその2

前回に続き\displaystyle\zeta(2)=\frac{\pi^2}{6}の証明を書きたいと思います。
〜2重積分を用いる〜
この証明は2重積分

\begin{eqnarray}
I=\int_0^1\int_0^1\frac{1}{1-xy}dxdy
\end{eqnarray}

の値を2通リに求めることからわかります。1つ目は\displaystyle\frac{1}{1-xy}を等比級数\sum_{n\ge 0}(xy)^nに展開することです。
\begin{eqnarray}
I&=&\int_0^1\int_0^1(xy)^ndxdy=\sum_{n\ge 0}\int_0^1\int_0^1x^ny^ndxdy\\
&=&\sum_{n\ge 0}\left(\int_0^1x^ndx\right)\left(\int_0^1y^ndy\right)=\sum_{n\ge 0}\frac{1}{n+1}\frac{1}{n+1}\\
&=&\sum_{n\ge 0}\frac{1}{(n+1)^2}=\sum_{n\ge 1}\frac{1}{n^2}=\zeta(2)
\end{eqnarray}

Iの2つ目の計算方法は変数変換です。
\displaystyle u=\frac{y+x}{2},\ v=\frac{y-x}{2}

と変数変換します。するとx=u-v,y=u+vより
\displaystyle\frac{1}{1-xy}=\frac{1}{1-u^2+v^2}

である。またヤコビアン2である。
f:id:otaku_of_suri:20160703141457p:plain
変数変換後の積分領域は上の図の色がけ部であるから\displaystyle u\le \frac{1}{2}\displaystyle u\ge \frac{1}{2}に分けて積分する。またvに関して対称であることも考慮すると
\begin{eqnarray}
I&=&4\int_0^{\frac{1}{2}}\left(\int_0^u\frac{dv}{1-u^2+v^2}\right)du+4\int_{\frac{1}{2}}^1\left(\int_0^{1-u}\frac{dv}{1-u^2+v^2}\right)du\\
&=&4\int_0^{\frac{1}{2}}\frac{1}{\sqrt{1-u^2}}\arctan\left(\frac{u}{\sqrt{1-u^2}}\right)du\\
&&+4\int_{\frac{1}{2}}^1\frac{1}{\sqrt{1-u^2}}\arctan\left(\frac{1-u}{\sqrt{1-u^2}}\right)du
\end{eqnarray}

となります。ここで\displaystyle\int\frac{dx}{a^2+x^2}=\frac{1}{a}\arctan{\frac{x}{a}}+Cを用いました。
ここでu=\sin\thetaとおくと、\displaystyle\frac{u}{\sqrt{1-u^2}}=\frac{\sin\theta}{\cos\theta}=\tan\thetaであるから結局、\displaystyle\arctan{\frac{u}{\sqrt{1-u^2}}}=\arcsin uとなります。
またu=\cos\thetaとおくと、\displaystyle\frac{1-u}{\sqrt{1-u^2}}=\frac{1-\cos\theta}{\sin\theta}=\frac{2\sin^2\frac{\theta}{2}}{2\cos\frac{\theta}{2}\sin\frac{\theta}{2}}=\tan\frac{\theta}{2}であるから結局、\displaystyle\arctan{\frac{1-u}{\sqrt{1-u^2}}}=\frac{1}{2}\arccos uとなります。よって、
\displaystyle I=4\int_0^{\frac{1}{2}}\frac{\arcsin u}{\sqrt{1-u^2}}du+2\int_{\frac{1}{2}}^1\frac{\arccos u}{\sqrt{1-u^2}}du

がわかります。ここで第1項の積分についてはu=\sin\theta、第2項の積分についてはu=\cos\thetaと変数変換すると
\begin{eqnarray}
I&=&4\int_0^{\frac{\pi}{6}}\frac{\theta}{\cos\theta}\cos\theta d\theta+2\int_{\frac{\pi}{3}}^{0}\frac{\theta}{\sin\theta}(-\sin\theta)d\theta\\
&=&4\int_{0}^{\frac{\pi}{6}}\theta d\theta+2\int_{0}^{\frac{\pi}{3}}\theta d\theta\\
&=&4\left[\frac{\theta^2}{2}\right]^{\frac{\pi}{6}}_{0}+2\left[\frac{\theta^2}{2}\right]^{\frac{\pi}{3}}_{0}\\
&=&4\frac{1}{2}\frac{\pi^2}{36}+2\frac{1}{2}\frac{\pi^2}{9}=\frac{\pi^2}{6}
\end{eqnarray}
となります!!!以上より
\displaystyle\zeta(2)=\frac{\pi^2}{6}

が示されました。

あと1つ証明があるのでそれはまた今度ということで。

数学のメモ

授業の演習問題で\displaystyle\zeta(2)=\frac{\pi^2}{6}の証明する問題が出たのでいろいろな証明を載せたいと思います。

フーリエ級数展開の利用
\displaystyle\zeta(2)=\sum_{n=0}^{\infty}\frac{1}{n^2}についてこれは無限級数なのでフーリエ級数展開を用いることを考えます。(授業の演習問題はこの方法だった)
周期関数f(t)=t^2(-\pi\le t\le\pi)の複素フーリエ級数展開を計算すると

\displaystyle
\begin{equation}
t^2=\frac{\pi^2}{3}+\sum_{n\ne 0,n=-\infty}^{\infty}\frac{2(-1)^n}{n^2}e^{-int}
\end{equation}

である。t=\piを代入すると
\displaystyle\begin{eqnarray}
\pi^2&=&\frac{\pi^2}{3}+\sum_{n\ne0}\frac{2(-1)^n}{n^2}e^{-int}\\
&=&\frac{\pi^2}{3}+\sum_{n\ne0}\frac{2(-1)^n}{n^2}(-1)^n\\
&=&\frac{\pi^2}{3}+\sum_{n\ne0}\frac{2}{n^2}\\
&=&\frac{\pi^2}{3}+2\sum_{n=1}^{\infty}\frac{2}{n^2}\\
&=&\frac{\pi^2}{3}+4\sum_{n=1}^{\infty}\frac{1}{n^2}
\end{eqnarray}

となります。あとは式変形すると
\displaystyle
\sum_{n=1}^{\infty}\frac{1}{n^2}=\frac{\pi^2}{6}

となることがわかりました。


別の級数から求める
先ほどの関数の複素でない場合のフーリエ級数展開から得られる

\displaystyle
\sum_{n=1}^{\infty}\frac{(-1)^{n+1}}{n^2}=\frac{\pi^2}{12}

があります。これを書き下してみると
\displaystyle
\frac{1}{1^2}-\frac{1}{2^2}+\frac{1}{3^2}-\frac{1}{4^2}+\frac{1}{5^2}-\frac{1}{6^2}+\cdots=\frac{\pi^2}{12}

一方、適切な不等式評価を行うと\displaystyle\sum_{n=1}^{\infty}\frac{1}{n^2}は収束することがわかるので、この値を\displaystyle\alphaと置いてみます。
ここで以下のような級数を考えます。
\displaystyle
2\frac{1}{2^2}+2\frac{1}{4^2}+2\frac{1}{6^2}+2\frac{1}{8^2}+\cdots

この級数はきちんと収束して先ほどの\displaystyle\alphaを用いると\displaystyle 2\cdot\frac{1}{2^2}\alpha=\frac{1}{2}\alphaとなります。
そこでこの2つの無限級数を足し合わせてみます。
\displaystyle\begin{eqnarray}
\frac{1}{1^2}-\frac{1}{2^2}+\frac{1}{3^2}-\frac{1}{4^2}+\frac{1}{5^2}-\frac{1}{6^2}+\cdots&=&\frac{\pi^2}{12}\\
2\frac{1}{2^2}+2\frac{1}{4^2}+2\frac{1}{6^2}+2\frac{1}{8^2}+\cdots&=&\frac{1}{2}\alpha
\end{eqnarray}

すると左辺はちょうど\displaystyle\frac{1}{1^2}+\frac{1}{2^2}+\frac{1}{3^2}+\cdots=\sum_{n=1}^{\infty}\frac{1}{n^2}となり、これは\displaystyle\alphaに一致します。右辺と比較すると\displaystyle\frac{\pi^2}{12}+\frac{1}{2}\alpha=\alphaより、
\displaystyle
\alpha=\frac{\pi^2}{6}

が得られました。

続きは長くなるので次回に書きます。

C++で8パズルをA*探索、IDA*探索を用いて解いた

授業で8パズルの探索を図示する課題があったんだけど、どうせならってことでプログラムを書いた。

A*アルゴリズムと言うのは、推定関数h(x)を使って、ゴールまでの距離を推定して、今までの探索の深さとの合計によってそのノードの評価値f(x)を決めて、それを元に最良優先探索をするアルゴリズムのこと。
IDA*アルゴリズムは、A*と同じようにf(x)を決めて、cutoff以下のf(x)の点だけを展開して深さ優先探索して、詰んだらcutoffを増やしてやり直し、っていうアルゴリズム(反復深化+A*みたいなかんじ)

コマンドライン引数で、
1:A*,推定関数は位置が違うパズルの数
2:A*,推定関数はマンハッタン距離
3:IDA*,推定関数は位置が違うパズルの数
4:IDA*,推定関数はマンハッタン距離

出力の方法が雑魚いのは勘弁して下さい\(^o^)/

#include <iostream>
#include <cstdlib>
#include <functional>
#include <queue>
#include <stack>
#include <map>
#include <vector>

struct state {
    int puzzle[9];
    int depth;
    int evaluation;
    bool operator>(const state &s) const {
        return evaluation > s.evaluation;
    }
};

state start = { {1, 2, 3,
                4, 5, 6,
                7, 8, 0},
                0,
                0};
state goal = { {4, 1, 5,
                 0, 3, 2,
                 7, 8, 6}, // puzzle
                0,           // depth
                0};        // evaluation
std::priority_queue <state, std::vector<state>, std::greater<state> > open;
std::stack<state> open2;
std::vector<state> closed;
int count = 0;
int max = 0;

bool IsFinished(state s)
{
    for (int i = 0; i < 8; i++) {
       if (s.puzzle[i] != goal.puzzle[i]) {
           return false;
       } 
    }
    return true;
}

int ManhattanOne(int n, int place)
{
    if (goal.puzzle[n] == 0) {
        return 0;
    }
    int q, r;
    q = abs(place / 3 - n / 3);
    r = abs(place % 3 - n % 3);
    return q + r;
}

int manhattan(state s)
{
    int eval = 0;
    std::map<int, int> goalplace;
    for (int i = 0; i < 9; i++) {
        goalplace[goal.puzzle[i]] = i;
    }
    for (int i = 0; i < 9; i++) {
        eval += ManhattanOne(goalplace[s.puzzle[i]], i);
    }
    return eval;
}

int wrong(state s)
{
    int eval = 0;
    for (int i = 0; i < 9; i++) {
        if (s.puzzle[i] != 0 and s.puzzle[i] != goal.puzzle[i]) {
            eval++;
        }
    }
    return eval;
}

void PrintState(state s)
{
    std::cout << '|' << s.puzzle[0] << '|' << s.puzzle[1] << '|' << s.puzzle[2] << '|' << ' ' << count << std::endl;
    std::cout << '|' << s.puzzle[3] << '|' << s.puzzle[4] << '|' << s.puzzle[5] << '|' << " f(n) = " << s.evaluation << std::endl;
    std::cout << '|' << s.puzzle[6] << '|' << s.puzzle[7] << '|' << s.puzzle[8] << '|' << " g(n) = " << s.depth << std::endl << std::endl;
    return;
}

bool IsSamePuzzle(state s1, state s2)
{
    for (int i = 0; i < 9; i++) {
        if (s1.puzzle[i] != s2.puzzle[i]) {
            return false;
        }
    }
    return true;
}

bool InClosed(state s)
{
    for (int i = 0; i < closed.size(); i++) {
        if (IsSamePuzzle(s, closed[i])) {
            return true;
        }
    }
    return false;
}

void AStar(std::function<int(state)> fun)
{
    if (open.empty()) {
        std::cerr << "Search have mistakenly stopped!" << std::endl;
    }
    if (max < open.size() + closed.size()) {
        max = open.size() + closed.size();
    }
    count++;
    state now = open.top();
    open.pop();
    closed.push_back(now);
    PrintState(now);
    if (IsFinished(now)) {
        std::cout << "Finished!" << std::endl;
        return;
    }
    state s = now;    
    int zero;
    for (int i = 0; i < 9; i++) {
        if (now.puzzle[i] == 0) {
            zero = i;
            break;
        }
    }
    if (zero - 3 >= 0) {
        std::swap(s.puzzle[zero - 3], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            open.push(s);
        }
        s = now;
    }
    if (zero % 3 != 0) {
        std::swap(s.puzzle[zero - 1], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            open.push(s);
        }
        s = now;
    }
    if (zero % 3 != 2) {
        std::swap(s.puzzle[zero + 1], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            open.push(s);
        }
        s = now;
    }
    if (zero + 3 < 9) {
        std::swap(s.puzzle[zero + 3], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            open.push(s);
        }
    }
    AStar(fun);
    return;
}

void IDAStar_rep(std::function<int(state)> fun, int c)
{
    if (open2.empty()) {
        std::cout << c << " roop end" << std::endl;
        c++;
        open2.push(start);
        closed.clear();
        IDAStar_rep(fun, c);
        return;
    }
    if (max < open2.size() + closed.size()) {
        max = open2.size() + closed.size();
    }
    state now = open2.top();
    closed.push_back(now);
    open2.pop();
    count++;
    if (IsFinished(now)) {
        PrintState(now);
        std::cout << "Finished!" << std::endl;
        return; 
    }
    state s = now;
    PrintState(now);
    int zero;
    for (int i = 0; i < 9; i++) {
        if (now.puzzle[i] == 0) {
            zero = i;
            break;
        }
    }
    if (zero - 3 >= 0) {
        std::swap(s.puzzle[zero - 3], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            if (s.evaluation <= c) {
                open2.push(s);
            }
        }
        s = now;
    }
    if (zero % 3 != 0) {
        std::swap(s.puzzle[zero - 1], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            if (s.evaluation <= c) {
                open2.push(s);
            }
        }
        s = now;
    }
    if (zero % 3 != 2) {
        std::swap(s.puzzle[zero + 1], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            if (s.evaluation <= c) {
                open2.push(s);
            }
        }
        s = now;
    }
    if (zero + 3 < 9) {
        std::swap(s.puzzle[zero + 3], s.puzzle[zero]);
        if (not InClosed(s)) {
            s.depth++;
            s.evaluation = fun(s) + s.depth;
            if (s.evaluation <= c) {
                open2.push(s);
            }
        }
        s = now;
    }
    IDAStar_rep(fun, c);
    return;
}

void IDAStar(std::function<int(state)> fun)
{
    IDAStar_rep(fun, open2.top().evaluation);
    return;
}

int main(int argc, char *argv[])
{
    if(argc != 2) {
        std::cerr << "Usage: <Executable filename> <type number>" << std::endl; 
        exit(0);
    }

    std::function< int(state) > fun;
    std::function< void(std::function< int(state) >) > alg;

    switch (*argv[1]) {
        case '1':
                 fun = wrong;
                 alg = AStar;
                 break;
        case '2':
                 fun = manhattan;
                 alg = AStar;
                 break;
        case '3':
                 fun = wrong;
                 alg = IDAStar;
                 break;
        case '4':
                 fun = manhattan;
                 alg = IDAStar;
                 break;
    }
    start.evaluation = fun(start);
    open.push(start);
    open2.push(start);
    alg(fun);

    std::cout << "Most saved nodes: " << max << std::endl;

    return 0;
}

結果は、以下のとおり
1のとき、28手、メモリに保存したノードの数52
2のとき、21手、メモリに保存したノードの数35
3のとき、33手、メモリに保存したノードの数17
4のとき、10手、メモリに保存したノードの数12

IDA*つえー

行列の固有値計算のJacobi法をpythonで実装した

数値計算ライブラリのnumpyはほんとに便利。

import numpy as np

def jacobi(A,N,check):
    B = np.fabs(A - np.diag(list(np.diag(A))))
    nondiagmax = np.max(B)
    while nondiagmax > check:
        k = int(np.argmax(B) / 3)
        m = np.argmax(B) % 3
        cos2phi = np.fabs(A[k,k] - A[m,m]) / \
                np.sqrt(4.0 * A[k,m]**2 + (A[k,k] - A[m,m])**2)
        cosphi = np.sqrt((1.0 + cos2phi) / 2.0)
        sinphi = np.sign(-A[k,m] * (A[k,k] - A[m,m])) * \
                np.sqrt((1.0 - cos2phi) / 2.0)
        P = np.matrix(np.identity(N))
        P[k,k] = cosphi
        P[k,m] = sinphi
        P[m,k] = -sinphi
        P[m,m] = cosphi
        A = ((P.T).dot(A)).dot(P)
        B = np.fabs(A - np.diag(list(np.diag(A))))
        nondiagmax = np.max(B)
    print(A)
    print(np.diag(A))

if __name__ == "__main__":
    EPS = 1e-8
    N = 3
    A = np.matrix([[3.0,0.01,0.1],[0.01,2.0,0.1],[0.1,0.1,1.0]])
    print(A)
    jacobi(A,N,EPS)

結果より、固有値は、[ 3.00521156 2.00950971 0.98527872]

MacOS X El CapitanでC言語のOpenMPによる並列計算を行う方法

並列計算とは、簡単にいえば、同時進行できる計算を複数のCPUのコアに同時に計算させてしまおうと言う試みである。

gccでは、新しいバージョンでしかOpenMPがサポートされていないため、homebrewで最新版のgccをインストールする。

$ brew install homebrew/versions/gcc6

homebrewによってインストールしたgccは、gcc-6という名前になっているので、gccと書いてコンパイルできるようにシンボリックリンクを作成する。

$ sudo ln -sf /usr/local/bin/gcc-6 /usr/local/bin/gcc

ついでにg++も

$ sudo ln -sf /usr/local/bin/g++-6 /usr/local/bin/g++

サンプルコードをコンパイル・実行して、Hello, World!が複数表示されればよい。

#include <stdio.h>
#include <omp.h>
int main()
{
#pragma omp parallel
 {
   printf("hello world from %d of %d\n",
	  omp_get_thread_num(), omp_get_num_threads());
 }
 return 0;
}
$ gcc -fopenmp -o hello hello.c
$ ./hello

円周率は有理数です。

んな訳あるかい!!!
ということで、どうもよねすけです。今日はエイプリルフールなのでこんな調子乗ったタイトルにしてみました。

円周率が無理数超越数であることは広く知られていることです。
が、その証明ってみんなあんまり知らないんじゃないかなあ、と思ったので今回は円周率の無理数性の証明を書きたいと思います。(時間の都合で途中式をずいぶんと省略してあるので良かったら自分で実際に手を動かして考えてみてください。)
なお、今回の証明には以下の本を参考にしました。この本、個人的にはめっちゃ好きな本なので良かったら手に取って見てください。

天書の証明

天書の証明


円周率を\piとおく。\pi^2無理数であることを示す。そうすれば\pi無理数であることもわかる。
証明するにあたって以下の補題を用いる。

補題
あるn\geq 1を固定し、
\begin{eqnarray}
f(x)=\frac{x^n(1-x)^n}{n!}
\end{eqnarray}
とおく。

  • 関数f(x)\displaystyle f(x)=\frac{1}{n!}\sum_{i=n}^{2n}c_{i}x^{i}という形の多項式で、係数c_{i}は整数である。
  • 0 < x < 1に対して、\displaystyle 0 < f(x) < \frac{1}{n!}である。
  • すべてのk\geq 0に対して、微係数\displaystyle f^{(k)}(0)\displaystyle f^{(k)}(1)は整数である。

このとき、ある整数a,b > 0に対して、\displaystyle\pi^2=\frac{a}{b}と仮定する。多項式
\begin{eqnarray}
F(x)=b^n\left(\pi^{2n}f(x)-\pi^{2n-2}f^{(2)}(x)+\pi^{2n-4}f^{(4)}(x)-\cdots\right)
\end{eqnarray}
は、\displaystyle F''(x)=-\pi^2F(x)+b^n \pi^{2n+2}f(x)を満たす。
補題の3つ目から、F(0),F(1)は整数である。
\begin{eqnarray}
\frac{d}{dx}\left(F'(x)\sin{\pi x}-\pi F(x)\cos{\pi x}\right)&=&(F''(x)+\pi^2 F(x))\sin{\pi x}\\
&=&b^n \pi^{2n+2}f(x)\sin{\pi x}\\
&=&\pi^2 a^n f(x)\sin{\pi x}
\end{eqnarray}
となり、
\displaystyle\begin{eqnarray}
N=\pi\int_0^1 a^n f(x)\sin{\pi x}dx=F(0)+F(1)
\end{eqnarray}
は整数となる。さらに、境界を除けば正の関数の積分として定義されているので、Nは正。しかし、十分に大きなnを持ってきて\displaystyle\frac{\pi a^n}{n!} < 1となるようにすると、補題の2つ目から
\displaystyle 0 < N=\pi\int_0^1 a^n f(x)\sin{\pi x}dx < \frac{\pi a^n}{n!} < 1
となる。これはNが正の整数であることに矛盾。よって\pi^2無理数
これより円周率\pi無理数であることが示された。




なんだか、きつねにつままれたような証明ですね~。



最後に。
今日から新年度ですね。進級した方は進級おめでとうございます。進学が決まった方、もっとおめでとうございます!!!これからの新生活、頑張っていきましょう!!!

それでは。