カッコ言語の時間計算量は次のように見積もれます。
入力を読むテープヘッドは逆戻りせず、一回だけ入力を読むので、 時間。
1 テープ Turing 機械では閉じカッコを見つけるとそれに対応するカッコを探 すためヘッドが戻り、対応を確認したらもとの閉じカッコの隣へと復帰します。 閉じカッコを発見した時、ヘッドが戻り、復帰する動作にかかるヘッドの移動 距離は高々 時間。 閉じカッコは高々 個あるので、全体の時間計算量は
今回は Turing 機械のインタプリタを構成します。 そのためには、Turing 機械自身を Turing 機械で扱えるデータにしなければ なりません。 そのために、Gödel Numbering という符号化を行います。
まず、二つの自然数を一つの自然数で表すことを考えます。 これは、 座標面上の格子点を図のように数えて いくことにより実現します。
つまり、 , , , , , , … が成り立つような関数 を求めます。
この数え上げを実現させるには次のように を計算します。
を求めよ。
のとき、 と定義する。
を求めなさい。
を求めるアルゴリズムを示しなさい。
任意の自然数の列 を一つの自然数に埋め込む方法を考えます。 これは、効率さえ問われなければ次のようにすることにより実現できます。
素数の列を とします。 その時、 は一つの数を表しますが、素因数分解の一意性から、この数が与えられれば正しく 元の数 を復元することができます。
168750 はどのような数列を表しているか?
Turing 機械は、次の 5 つの組で定義できます。
集合を一つの数にするには、要素を表す文字列を二進数などで表し、それをカ ンマなどの区切り記号で列挙すれば可能です。 但し、状態、アルファベットなど、どんな記号を使うかが本質ではなく単純に 数が分かればいい状態だと、集合の要素数のみで事足ります。 終了状態の集合だけはすべての要素を列挙しなければならないでしょう。 δ関数は ( 現在の状態, 入力文字1, 入力文字2, ..., 入力文字k, 次の状態, 出力文字1, 出力文字2, ..., 出力文字k, ヘッドの移動1, ヘッドの移動2, ..., ヘッドの移動k ) を必要な分だけ繰り返すことで表現できます。
結局、Turing 機械の構成は多くの数の組で表現できることが分かります。 そのため、それを前節までで説明した手法により、一つの自然数を Turing 機 械の定義と解釈することができます。
また、素因数分解を使用したコード化において、あらゆる数列が一つの数にな ります。 そのため、自然数によって Turing 機械のコードを全て数え上げることができ ます。
Church の提唱を使えば、Turing 機械のコードを解釈する Turing 機械が存在 することは言えます。 しかし、計算量や Turing 機械のテープ数による能力の違いを議論するため、 ここでは Turing 機械の構成の概略を説明します。
万能 Turing 機械とは Turing 機械のコードと、本来与えるべき入力の対から、 そのコードの指す Turing 機械に入力を与えたときの出力を出力する Turing 機械を指します。
ここでは、与えられた Turing 機械のコードが仮定しているテープ数に比べて、 十分にテープ数を持つ Turing 機械により、与えられたコードのδ関数を 1 step 実行する方法を説明します。
与えられたコードが k 本テープを使用する Turing 機械を表してい るとします。 その時、まず、 k 本のテープをそのシミュレートする Turing 機械が使用す るテープとしてそのまま割り当てます。 次に、シミュレートの作業用に、与えられたコードを記憶するために 1 本、 シミュレートしているTuring 機械の状態を記憶するために 1 本、シミュレー トしている Turing 機械のテープヘッドの位置を記憶するテープを k 本用意 します。
さて、 Turing 機械の 1 step のシミュレーションの手順を示します。 まず、現在の状態と、テープの位置の記号から、それに適合するδ関数の値を 読みだします。 これにはコードを解析する必要があります。 コードを解析するには、素数を生成して素因数分解を行う必要があります。 まず、一本のテープに素数列を生成します。 これは素朴にエラトステネスのフルイを使用します。 Turing 機械のコードの長さを とすると、必要な素数の長さも ですので、必要な素数の最大値は高々 までになります。一方、割算 を実現するには、 を始めに記憶しておき、 を桁をずらしながら引き算が可能かどうかを調べていきます。 これは、結果を入れるテープを含め 3 本のテープを使い、 時間で計算できます。 よって、コードから一つのδ関数の割り当てを取り出すためには、素数を 順に生成していき、各素数で割れるだけ割ることで取り出すことができます。 素数の生成に 時間かかるので、 適合するδ関数の値を読み出すのに、 を定数とすると、 程度の時間がかかります。 また、テープは数本(5〜6本)余分に使用します。 そして、読み出したδ関数の値に従い、各テープに記号を書き、移動させます。 結局、シミュレーションに使用するテープは数本で、かかる時間は 程度になります。 但し、 があらかじめ与えられた定数だとすると、もともとの Turing 機械の定数倍の時間だけかかると言えます。
前節のシミュレーションでは、与えられた Turing 機械のコードに対して、さ らにテープを追加してシミュレーションしました。 ここでは一つ Turing 機械を固定してあらゆる Turing 機械をシミュレートす ることを考えます。 そのため、与えられた時間計算量 の k テープ Turing 機械を 時間でシミュレートする 1 テープ Turing 機械を構成します。
1 テープ Turing 機械で k テープ Turing 機械を 1 step シミュ レーションするにはテープを次のように使います。
各テープの内容は ます毎に書かれます。 各テープは 2 マスを対にし、一マスはテープの内容を、もう一マスはテープ ヘッドがなければ 0 、あれば 1 を記入します。 そして、シミュレーションでは毎回各テープのヘッドの位置を読み込みます。 各テープのヘッドの記号を覚えておくため、 マスのテープ領域は あらかじめ用意しておきます。 そしてδ関数を計算します。 δ関数により得られたテープへの書き込み文字やテープヘッドの移動に関して は、各テープのテープヘッドの位置に目的の記号を書いてヘッドを動かすこと をシミュレートします。 一回に一つのテープだけを対象にすることで、書き込む記号の種類は有限個に なります。 したがって、テープヘッドを検索してヘッド位置に記号を書き、ヘッドを移動 する処理を行うために、書き込む記号ヘッドの移動方向ごとに異なるラベ ルを使用すれば、あらたなテープ上の記憶は必要ありません。 結局、 1 step のシミュレーションで必要なのは、各テープヘッドが読み書きす る記号を保存する k マスの領域と、ヘッドの検索時間と書き込む 時間になります。 もともとの Turing 機械の時間計算量が であれば、その時間内に使用できるテープのマスの最大値はやはり ですので、一回の検索にかかる時間は高々 時間でできます。 これを 1 step のシミュレーションで定数回行います。 最終的にこのシミュレーションは 回行うので、時間計算量は になります。
なお、最初の状態では入力の値が一マス毎に書かれている状態なので、シミュ レーションの準備をしなければなりません。 しかし、これは、テープの最初に マスの領域をとり、その後、 マス毎に入力の値をコピーします。 これにかかる時間は入力の長さ に比例します。
2 テープ Turing 機械だと 時間でシミュレートできることが知られています。
実際のシミュレートの仕方は笠井琢美「コンピュータサイエンス大学講座 17 -- 計算量の理論」近代科学社(1987) に詳しく載っています。
基本的なアイディアは以下の通りです。
このようにすると、 ステップのシミュレーションに 時間かかるのであれば、 ステップのシミュレーションには 時間かかることになる。
さて、ここで、十分大きな定数 c を用いて を表すと次のようになる。
これは次のように計算できる。 まず、右の項に注目して、 i を減らして 2 倍すると次が得られる。
これを片々引き算すると次が得られる。
ここで以下の和を考えると、 が求まります。
したがって以下を得ることができます。
ここで上記の議論を 1 テープと同様にテープを k マスに拡張して考えます。 但し、帰納的にシミュレートするため、スタックが必要となるので、上記を実現 するためにもう一本テープが必要です。 そのため、 2 本テープが必要です。 また、 ステップで計算が終わるような Turing 機械をシミュレートするとき、 として高々 までシミュレートすれば計算が終ります。 したがって、 その時の実行時間は になります。
時間 Turing 機械を定数本のテープを持つ Turing 機械でシミュレーションす るとき、 シミュレーションの時間が であるかどうかは重要な未解決問題です。
1 テープ Turing 機械のシミュレーションの仕方において、テープのマス目の 使用量を見積もりなさい。 つまり、領域計算量 S(n) の Turing 機械において、 1 テープ Turing 機械 ではどの程度のテープのマス目が必要か?
一般のプログラムにおいて、配列変数を除外して考えると、通常の変数に対し てテープ一本を割り当てるような k テープ Turing 機械に対応づけることが できます。 これは自明ではありませんが、特定の CPU の 1 step の動作を、 レジスタの本数だけテープを用意した k テープ Turing 機械で定数ステップ で動作させられるように作ることができます。 C 言語のプログラムもそれぞれの命令が定数ステップの機械語に変換され、そ れぞれの命令において、データの長さに応じたステップ数がかかると仮定する とします。 その時、前回の C 言語のプログラムのステップ数の計算のうち、データの長 さに比例したような見積りを行うことを考えます。 T(n) ステップで動作するような C 言語のプログラムは結局 2 テープ Turing 機械で O(T(n)log T(n)) 時間でシミュレートできることになります。
万能 Turing 機械のコードの長さ に対して、シミュレーションの時間計算量が の多項式に比例する(多項式時間)ようにするにはどうすれば良いか?
計算不能な問題を取り上げます。