Cは実際にチューリング完全ですか?


40

私は誰かにCがチューリング完全であることを説明しようとしていましたが、実際には技術的にチューリング完全であるかどうかはわかりません。(Cは、実際の実装ではなく、抽象的なセマンティクスのように。)

「明白な」答え(おおよそ:任意の量のメモリをアドレス指定できるため、RAMマシンをエミュレートできるので、チューリング完全です)は、C標準で許可されている限り、実際には正しくありません。 size_tを任意に大きくするには、一定の長さに固定する必要があり、固定する長さに関係なく、有限です。(つまり、任意の停止しているチューリングマシンが与えられた場合、「適切に」実行されるようにsize_tの長さを選択できますが、すべての停止しているチューリングマシンが適切に実行されるようにsize_tの長さを選択する方法はありません)

だから:C99チューリング完全ですか?


3
Cの「抽象セマンティクス」とは何ですか?それらはどこでも定義されていますか?
ユヴァルフィルマス16

4
@YuvalFilmus -を参照してください例えば、ここで、すなわちCなどとは対照的に、標準で定義された「これはgccがそれをしない方法です」。
TLW

1
現代のコンピューターにはTMのような無限のメモリはないが、まだ「普遍的なコンピューター」と見なされる「技術」があります。また、「セマンティクス」のプログラミング言語は、実装がすべてメモリに制限されていることを除いて、実際には有限のメモリを想定していないことに注意してください。たとえば、PCはチューリングマシンとして動作します。とにかく、本質的にすべての「主流」プログラミング言語はチューリング完全です。
vzn

2
これは本当にC.のチューリング完全に対する有効な引数ではありませんので、Cは(チューリングマシンのように)、その計算のために内部のコンピュータメモリを使用することに限定されない
reinierpost

@reinierpost-それは、テレタイプが賢明だと言っているようなものです。つまり、「C +外部のTM相当」はチューリング完全であり、Cがチューリング完全ではないということです。
TLW

回答:


34

よくわかりませんが、かなり微妙な理由で答えはノーだと思います。私は数年前に理論計算機科学について尋ねましたが、ここで紹介する以上の答えは得られませんでした。

ほとんどのプログラミング言語では、次の方法でチューリングマシンをシミュレートできます。

  • 有限量のメモリを使用するプログラムで有限オートマトンをシミュレートします。
  • 整数のリンクリストのペアでテープをシミュレートし、現在の位置の前後のテープの内容を表します。ポインターを移動するとは、リストの1つの頭を他のリストに移動することを意味します。

テープが長すぎると、コンピューターで実行される具体的な実装ではメモリが不足しますが、理想的な実装ではチューリングマシンプログラムを忠実に実行できます。これは、ペンと紙で、またはより多くのメモリを搭載したコンピューターを購入し、プログラムがメモリを使い果たした場合に、ワードあたりのビット数がより多いアーキテクチャを対象とするコンパイラを購入することで実現できます。

これはCでは機能しません。なぜなら、リンクリストを永久に成長させることは不可能だからです。ノードの数には常にある程度の制限があります。

その理由を説明するために、最初にC実装とは何かを説明する必要があります。Cは実際にはプログラミング言語のファミリーです。ISO C標準(より正確には、この規格の特定のバージョン)(英語ができること格式のレベルを持つ)を定義構文と意味プログラミング言語の家族。Cには多くの未定義の動作実装定義の動作があります。Cの「実装」は、実装定義のすべての動作を体系化します(体系化するもののリストは、C99の付録Jにあります)。Cの各実装は、個別のプログラミング言語です。「実装」という言葉の意味は少し独特であることに注意してください。それが実際に意味するのは言語バリアントであり、同じ言語バリアントを実装する複数の異なるコンパイラプログラムがある場合があります。

Cの特定の実装では、1バイトに可能な値があります。すべてのデータは、バイトの配列として表すことができます。型には、最大 2つのCHAR_BIT × sizeof(t)の可能な値があります。この数は、Cの実装によって異なりますが、Cの特定の実装では定数です。2CHAR_BITt2CHAR_BIT×sizeof(t)

特に、ポインターは最大で値しかとることができません。これは、アドレス可能なオブジェクトの最大数が有限であることを意味します。2CHAR_BIT×sizeof(void *)

CHAR_BITおよびの値sizeof(void*)は観察可能であるため、メモリが不足すると、これらのパラメータの値を大きくしてプログラムの実行を再開することはできません。異なるプログラミング言語、つまり異なるC実装でプログラムを実行することになります。

言語のプログラムが持つことができる状態の数が限られている場合、プログラミング言語は有限オートマトンよりも表現力がありません。アドレス可能なストレージに制限されているCのフラグメントは、最大プログラム状態のみを許可します。ここで、nはプログラムの抽象構文ツリーのサイズ(制御フローの状態を表す)です。プログラムは、その数の状態を持つ有限オートマトンによってシミュレートできます。Cがより表現力豊かであれば、他の機能を使用する必要があります。n×2CHAR_BIT×sizeof(void *)n

Cは最大再帰深度を直接課しません。実装には最大値を設定できますが、最大値を設定しないこともできます。しかし、関数呼び出しとその親の間でどのように通信するのでしょうか?引数がアドレス可能な場合は、再帰の深さを間接的に制限するため、引数は適切ではありません。関数がある場合、アクティブなフレームのint f(int x) { … f(…) …}すべての出現は独自のアドレスxf持ち、ネストされた呼び出しの数は番号で制限されます以下のための可能なアドレスのx

Cプログラムは、register変数の形式でアドレス指定不可能なストレージを使用できます。「通常の」実装では、アドレスを持たない少数の変数のみを使用できますが、理論的には、実装では無制限の量のregisterストレージを使用できます。そのような実装では、引数がである限り、関数を無制限に再帰呼び出しできますregister。しかし、引数はregisterであるため、それらへのポインターを作成することはできないため、それらのデータを明示的にコピーする必要があります:ポインターで作られた任意のサイズのデータ​​構造ではなく、有限量のデータのみを渡すことができます。

無制限の再帰の深さ、および関数が直接呼び出し元(register引数)からのみデータを取得し、直接呼び出し元にデータを返す(関数の戻り値)という制限があるため、決定性プッシュダウンオートマトンの力が得られます。

さらに進む方法が見つかりません。

(もちろん、ファイル入出力関数を介して、プログラムにテープの内容を外部に保存させることもできます。しかし、Cがチューリング完全かどうかを尋ねるのではなく、Cと無限ストレージシステムがチューリング完全かどうかを尋ねます。答えは退屈な「はい」です。ストレージをチューリングオラクルとして定義することもできます。最初のテープコンテンツを呼び出し  fopen("oracle", "r+")、最終的なテープコンテンツfwritefread戻します。


アドレスのセットが有限でなければならない理由はすぐにはわかりません。私はあなたがリンクする質問への答えにいくつかの考えを書きました:cstheory.stackexchange.com/a/37036/43393
Alexey B.

4
申し訳ありませんが、同じロジックで、チューリング完全なプログラミング言語はまったくありません。各言語には、アドレス空間に関して明示的または暗黙的な制限があります。無限のメモリを持つマシンを作成する場合、ランダムアクセスポインターも明らかに無限の長さになります。したがって、そのようなマシンが登場した場合、高レベル言語用のAPIとともに、シーケンシャルメモリアクセス用の命令セットを提供する必要があります。
IMil

14
@IMilこれは真実ではありません。プログラミング言語の中には、暗黙的にではなく、アドレス空間に制限がないものがあります。明らかな例を挙げると、テープの初期状態がプログラムを形成するユニバーサルチューリングマシンは、チューリング完全なプログラミング言語です。実際に実際に使用される多くのプログラミング言語には、同じ特性があります(LispやSMLなど)。言語には「ランダムアクセスポインター」の概念は必要ありません。(続き)
ジル「SO-悪であるのをやめる」

11
@IMil (続き)通常、実装はパフォーマンスを向上させますが、特定のコンピューターで実行される実装は、コンピューターのメモリのサイズによって制限されるため、チューリング完全ではないことがわかります。しかし、それは、実装が言語全体を実装せず、(Nバイトのメモリで実行されるプログラムの)サブセットのみを実装することを意味します。プログラムをコンピューターで実行できます。メモリーが足りなくなった場合は、より大きなコンピューターに移動するなど、永久に、または停止するまで実行できます。これは、言語全体を実装する有効な方法です。
ジル「SO-悪をやめろ」

6

C99のva_copy可変引数APIへの追加は、チューリング完全性への裏口を私たちに与えるかもしれません。最初に引数を受け取った関数以外の関数で可変引数リストを複数回反復処理することが可能になるva_argsため、ポインターなしのポインターを実装するために使用できます。

もちろん、可変引数APIの実際の実装にはおそらくどこかにポインターがありますが、抽象マシンでは、代わりにマジックを使用して実装できます。

以下に、任意の遷移ルール​​を使用した2スタックプッシュダウンオートマトンを実装するデモを示します。

#include <stdarg.h>
typedef struct { va_list va; } wrapped_stack; // Struct wrapper needed if va_list is an array type.
#define NUM_SYMBOLS /* ... */
#define NUM_STATES /* ... */
typedef enum { NOP, POP1, POP2, PUSH1, PUSH2 } operation_type;
typedef struct { int next_state; operation_type optype; int opsymbol; } transition;
transition transition_table[NUM_STATES][NUM_SYMBOLS][NUM_SYMBOLS] = { /* ... */ };

void step(int state, va_list stack1, va_list stack2);
void push1(va_list stack2, int next_state, ...) {
    va_list stack1;
    va_start(stack1, next_state);
    step(next_state, stack1, stack2);
}
void push2(va_list stack1, int next_state, ...) {
    va_list stack2;
    va_start(stack2, next_state);
    step(next_state, stack1, stack2);
}
void step(int state, va_list stack1, va_list stack2) {
    va_list stack1_copy, stack2_copy;
    va_copy(stack1_copy, stack1); va_copy(stack2_copy, stack2);
    int symbol1 = va_arg(stack1_copy, int), symbol2 = va_arg(stack2_copy, int);
    transition tr = transition_table[state][symbol1][symbol2];
    wrapped_stack ws;
    switch(tr.optype) {
        case NOP: step(tr.next_state, stack1, stack2);
        // Note: attempting to pop the stack's bottom value results in undefined behavior.
        case POP1: ws = va_arg(stack1_copy, wrapped_stack); step(tr.next_state, ws.va, stack2);
        case POP2: ws = va_arg(stack2_copy, wrapped_stack); step(tr.next_state, stack1, ws.va);
        case PUSH1: va_copy(ws.va, stack1); push1(stack2, tr.next_state, tr.opsymbol, ws);
        case PUSH2: va_copy(ws.va, stack2); push2(stack1, tr.next_state, tr.opsymbol, ws);
    }
}
void start_helper1(va_list stack1, int dummy, ...) {
    va_list stack2;
    va_start(stack2, dummy);
    step(0, stack1, stack2);
}
void start_helper0(int dummy, ...) {
    va_list stack1;
    va_start(stack1, dummy);
    start_helper1(stack1, 0, 0);
}
// Begin execution in state 0 with each stack initialized to {0}
void start() {
    start_helper0(0, 0);
}

注:場合 va_listが配列型の、実際には関数への非表示ポインターパラメーターがあります。したがって、おそらくすべてのva_list引数のタイプをに変更した方がよいでしょうwrapped_stack


これはうまくいくかもしれません。考えられる懸念は、無制限の数の自動va_list変数の割り当てに依存していることですstack。これらの変数にはaddressが必要であり&stack、制限された数のみが可能です。この要件はregister、おそらくすべてのローカル変数を宣言することで回避できますか?
カイ

@chi AIUI変数は、誰かがアドレスを取得しようとしない限り、アドレスを持つ必要はありません。また、省略記号の直前に引数を宣言することは違法registerです。
feersum

同じロジックで、int誰かがバウンドまたはsizeof(int)?を使用しない限り、バウンドを持つ必要はありません。
カイ

@chiまったくありません。標準は、抽象セマンティクスの一部として、intがいくつかの有限の境界INT_MINとの間の値を持つことを定義しINT_MAXます。そして、もし値intがそれらの限界を超えると、未定義の動作が起こります。一方、標準では、すべてのオブジェクトが特定のアドレスのメモリに物理的に存在することを意図的に必要としません。レイアウト、または不要な場合は完全に省略します。
-feersum

4

非標準の算術演算でしょうか?

そのため、問題はの有限サイズのようsizeof(t)です。しかし、私は回避策を知っていると思います。

私の知る限り、Cはその整数型に標準整数を使用する実装を必要としません。したがって、非標準の算術モデルを使用できます。次に、設定しますsizeof(t)非標準の数にしますが、有限数のステップで到達することはありません。したがって、チューリングマシンのテープの長さは常に「最大」よりも短くなります。最大値に到達するのは文字通り不可能だからです。sizeof(t)単に単語の通常の意味では数字ではありません。

これはもちろん技術の1つです。 です。テネンバウムの定理です。Peano算術の唯一のモデルは標準モデルであり、明らかにそうではないと述べています。しかし、私が知る限り、CはPeanoの公理を満たすデータ型を使用する実装を必要とせず、計算可能な実装も必要としないため、これは問題になりません。

非標準の整数を出力しようとするとどうなりますか?さて、非標準の文字列を使用して非標準の整数を表現できるため、その文字列の先頭から数字をストリームするだけです。


実装はどのようになりますか?
reinierpost

@reinierpost PAの数え切れないほどの非標準モデルを使用したデータを表していると思います。PA度を使用して算術演算を計算ます。そのようなモデルは、有効なC実装を提供するはずです。
PyRulez

申し訳ありませんが、これは機能しません。sizeof(t)それ自体は型の値size_tなので、0からの間の自然な整数SIZE_MAXです。
ジル「SO-悪であるのをやめ

@Gilles SIZE_MAXも同様に非標準の自然です。
PyRulez

これは興味深いアプローチです。また、たとえばintptr_t / uintptr_t / ptrdiff_t / intmax_t / uintmax_tを非標準にする必要があることに注意してください。C ++では、これはC.わから...前進保証の抵触はない実行します
TLW

0

IMO、強力な制限は、アドレス可能なスペース(ポインターサイズを介して)が有限であり、これが回復できないことです。

メモリを「ディスクにスワップ」できると主張することもできますが、ある時点でアドレス情報自体がアドレス可能なサイズを超えることになります。


これが受け入れられた答えの要点ではありませんか?この2016年の質問の答えに新しいものが追加されるとは思わない。
カイ

@chi:いいえ、受け入れられた答えは外部メモリへのスワッピングについて言及していませんが、これは回避策と思われるかもしれません。
イブダウスト

-1

実際には、これらの制限はチューリングの完全性とは無関係です。実際の要件は、テープを無限ではなく、任意の長さにすることです。それは別の種類の停止問題を作成します(宇宙はどのようにテープを「計算」しますか?)

「リストを無限に大きくすることはできないので、Pythonはチューリング完全ではありません」と言っているのと同じくらい偽です。

[編集:編集方法を明確にしてくれたホイットレッジ氏に感謝します。]


7
これは質問に答えるとは思わない。質問はすでにこの答えを予想しており、なぜそれが有効でないのかを説明しました:「C標準ではsize_tを任意に大きくすることができますが、それはある程度の長さで修正する必要があります。 「。その議論に対して何か回答はありますか?その議論が間違っている(または正しい)理由が答えで説明されていない限り、質問を答えられたものとして数えることはできないと思います。
DW

5
常に、typeの値size_tは有限です。問題はsize_t、計算全体を通して有効な範囲を確立できないことです。どの範囲についても、プログラムがオーバーフローする可能性があります。しかし、C言語では、size_t:の境界が存在すると述べていますが、特定の実装では、sizeof(size_t)バイトまでしか成長できません。また、素敵です。あなたを批判する人々が「自分で考えることはできない」と言うのは失礼です。
ジル 'SO-悪である停止

1
これは正解です。旋盤は無限のテープを必要とせず、「任意の長さ」のテープを必要とします。つまり、テープは、計算を完了するために必要な長さであると想定できます。また、コンピューターに必要なだけのメモリがあると想定することもできます。無限のテープは絶対に必要ではありません。なぜなら、有限の時間で停止する計算は無限のテープを使用できない可能性があるからです。
ジェフリーLホイットレッジ

この答えが示すのは、TMごとに、シミュレートするのに十分なポインタ長でC実装を記述できるということです。ただし TM をシミュレートできる1つの C実装を記述することはできません。そのため、特定の実装がTコンプリートであることは仕様で禁止されています。ポインターの長さが固定されているため、T-completeでもありません。

1
これは、このコミュニティのほとんどの個人の能力不足のためにほとんど目に見えない別の正しい答えです。一方、受け入れられた回答は偽であり、そのコメントセクションは、モデレーターが重要なコメントを削除することで保護されています。さようなら、cs.stackexchange。
19:31のxamid

-1

リムーバブルメディアを使用すると、無制限のメモリの問題を回避できます。おそらく人々はこれが虐待だと思うだろうが、私はそれは大丈夫で、とにかく本質的に避けられないと思う。

ユニバーサルチューリングマシンの実装を修正します。テープには、リムーバブルメディアを使用します。ヘッドが現在のディスクの末尾または先頭から外れると、マシンはユーザーに次または前のディスクを挿入するように促します。シミュレートされたテープの左端を示すために特別なマーカーを使用するか、両方向に境界のないテープを使用できます。

ここで重要なのは、Cプログラムが実行しなければならないことはすべて有限であることです。コンピューターは、オートマトンをシミュレートするのに十分なメモリのみを必要とし、size_tのみを必要とし、(実際にはかなり少ない)量のメモリとディスク上のアドレス指定を可能にするのに十分な大きさである必要があります。ユーザーは次または前のディスクを挿入するように求められるだけなので、「ディスク番号123456を挿入してください...」と言うのに無制限に大きな整数は必要ありません。

主要な異論はユーザーの関与に対するものであると思われますが、無制限のメモリを実装する他の方法がないように思われるため、どの実装でも避けられないようです。


3
私は、Cの定義がそのような無制限の外部ストレージを必要としない限り、チューリングの完全性の証拠として受け入れられないと主張します。(もちろん、ISO 9899は、実世界のエンジニアリングのために書かれている必要はありません。)私が懸念するのは、これを受け入れた場合、同様の理由で、DFAはチューリング完全であると主張するかもしれないということです。テープ(外部ストレージ)でヘッドを駆動します。
カイ

@chi DFA引数がどのように続くかわかりません。DFAの全体的なポイントは、ストレージへの読み取りアクセスのみを持つことです。「テープ上でヘッドを駆動する」ことを許可した場合、それはまさにチューリングマシンではありませんか?
デビッドリチャービー

2
確かに、私はここで少し選び抜いています。要点は、「テープ」をCに追加し、DFでCをシミュレートし、この事実を使用して、DFAで同じことができない場合に、Cがチューリング完了であると主張することはなぜですか?Cがそれ自体で無制限のメモリを実装する方法を持たない場合、チューリングが完了したと見なすべきではありません。(少なくとも境界は非常に大きいため、実際にはほとんどの場合問題にならないため、少なくとも「道徳的に」チューリング完全と呼びます)私は、問題を明確に解決するには、厳密な正式な仕様が必要だと思いますC(ISO規格では十分ではない)
カイ

1
@chi CにはファイルI / Oルーチンが含まれているので問題ありません。DFAはサポートしていません。
デビッドリチャービー

1
Cは、これらのルーチンが何を行うかを完全には指定していません。セマンティクスのほとんどは実装定義です。AC実装は、ファイルの内容を保存するために必要ではありません。たとえば、いわば、すべてのファイルが「/ dev / null」であるかのように動作できると思います。また、無制限の量のデータを保存する必要もありません。大部分のC実装が行うことを考慮し、その動作を理想的なマシンに一般化するとき、あなたの議論は正しいと思います。Cの定義のみに厳密に依存している場合にのみ、プラクティスを忘れて、それが成り立つとは思わない。
カイ

-2

size_t無限に大きくなることを選択する

size_t無限に大きくすることもできます。当然、そのような実装を実現することは不可能です。しかし、私たちが住んでいる世界の有限性を考えると、それは驚くことではありません。

実用的な意味

しかし、そのような実装を実現できたとしても、実際的な問題があります。次のCステートメントを検討してください。

printf("%zu\n",SIZE_MAX);

SIZE_MAXSIZE_MAXO2sze_tsize_tSIZE_MAXprintf

幸いなことに、理論的な目的のために、printfすべての入力に対して保証が終了するという要件を仕様に見つけることができませんでした。したがって、私が知る限り、ここでCの仕様に違反することはありません。

計算の完全性について

理論的な実装がチューリング完了であることを証明することはまだ残っています。これは、「任意のシングルテープチューリングマシン」を実装することで確認できます。

私たちのほとんどはおそらく学校プロジェクトとしてチューリングマシンを実装しているでしょう。特定の実装の詳細は説明しませんが、一般的に使用される戦略は次のとおりです。

  • 状態の数、シンボルの数、および状態遷移表は、特定のマシンに対して固定されています。したがって、状態とシンボルを数値として、状態遷移表を2次元配列として表すことができます。
  • テープはリンクリストとして表すことができます。単一の二重リンクリスト、または2つの単一リンクリスト(現在の位置からの各方向に1つ)を使用できます。

次に、そのような実装を実現するために必要なものを見てみましょう。

  • 固定されているが、任意に大きな数のセットを表す機能。任意の数を表すために、MAX_INT無限であることも選択します。(または、他のオブジェクトを使用して状態とシンボルを表すこともできます。)
  • テープ用に任意の大きなリンクリストを作成する機能。繰り返しますが、サイズに制限はありません。つまり、テープを作成するためだけに永遠に時間を費やすため、このリストを前もって作成することはできません。しかし、動的メモリ割り当てを使用すれば、このリストをインクリメンタルに作成できます。を使用できますmallocが、もう少し考慮する必要があります。
    • C仕様ではmalloc、利用可能なメモリが使い果たされた場合などに失敗することが許可されています。したがって、私たちの実装は、malloc失敗しない限り真に普遍的です。
    • ただし、メモリが無限にあるマシンで実装を実行する場合、malloc失敗する必要はありません。C標準に違反することなく、私たちの実装はそれmallocが決して失敗しないことを保証します。
  • ポインターの逆参照、配列要素の参照、およびリンクリストノードのメンバーへのアクセス。

したがって、上記のリストは、仮想C実装でチューリングマシンを実装するために必要なものです。これらの機能は終了する必要があります。ただし、それ以外のものは終了しないようにすることができます(標準で要求されている場合を除く)。これには、算術演算、IOなどが含まれます。


6
printf("%zu\n",SIZE_MAX);そのような実装では何が印刷されますか?
ルスラン

1
@Ruslan、このような実装は不可能です。チューリングマシンを実装することは不可能です。しかし、そのような実装が可能であれば、無限に大きい数の10進表現、おそらくは10進数字の無限ストリームを出力すると思います。
ネイサンデイビス

2
@NathanDavisチューリングマシンを実装することは可能です。秘Theは、無限のテープを作成する必要がないことです。必要に応じて、テープの使用済み部分を増分的に作成するだけです。
ジル「SO-悪であるのをやめる」

2
@Gilles:私たちが住んでいるこの有限の宇宙では、チューリングマシンを実装することは不可能です。
gnasher729

1
@NathanDavisしかし、あなたがそうすれば、あなたは変わったsizeof(size_t)(またはCHAR_BITS)。あなたが再び開始する必要があり、新しい状態から再開することはできませんが、プログラムの実行は今、これらの定数が異なっていることが異なる場合があります
ジル「SO-停止されて悪」

-2

ここでの主な論点は、size_tのサイズは有限ですが、無限に大きくなる可能性があるということです。

回避策はありますが、これがISO Cと一致するかどうかはわかりません。

無限のメモリを持つマシンがあると仮定します。したがって、ポインタのサイズに制限はありません。あなたはまだあなたのsize_tタイプを持っています。sizeof(size_t)とは何かを尋ねると、答えはただsizeof(size_t)になります。たとえば、100を超えるかどうかを尋ねると、答えはイエスです。sizeof(size_t)/ 2を尋ねると、答えはまだsizeof(size_t)であると推測できます。あなたがそれを印刷したいなら、私たちはいくつかの出力に同意することができます。これら2つの違いは、NaNなどです。

要約すると、size_tの条件を制限してサイズを制限しても、既存のプログラムが破損することはありません。

PSメモリsizeof(size_t)の割り当ては引き続き可能です。必要なサイズだけが必要なので、すべての偶数(または同様のトリック)を取るとしましょう。


1
「これら2つの違いはNaNである可能性があります」。いいえ、できません。C.における整数型のNaNのようなものはありません
TLW

標準に従って、sizeofを返す必要がありsize_tます。そのため、特定の値を選択する必要があります。
ドラコニス

-4

はい、そうです。

1.引用された回答

私の(および他の)正解に対する大量の下票に対する反応として-誤解の驚くほど高い承認と比較して-私はあまり理論的に深遠な代替説明を探しました。これを見つけました。ここでよくある間違いのいくつかをカバーして、もう少し洞察が得られるように願っています。引数の重要な部分:

[...]彼の議論は次のとおりです。実行中に任意の量のストレージを必要とする可能性のある終了プログラムを作成したと仮定します。そのプログラムを変更することなく、事後的に、この計算に対応するのに十分なストレージを提供するコンピューターハードウェアとそのCコンパイラーを実装することができます。これには、charの幅(CHAR_BITS経由)および/またはポインター(size_t経由)を広げる必要がありますが、プログラムを変更する必要はありません。これが可能であるため、Cは実際にプログラムを終了するためのチューリング完全です。

この引数の扱いにくい部分は、プログラムの終了を検討する場合にのみ機能するということです。終了プログラムには、ストレージ要件に静的な上限があるという素晴らしい特性があります。これは、「適合する」まで、ストレージサイズを増やしながら目的の入力でプログラムを実行することで実験的に決定できます。

私が一連の思考で誤解を招いた理由は、「有用な」終了しないプログラムの幅広いクラスを検討していたからです[...]

要するに、すべての計算可能な関数にはC言語の解決法がある(上限がないため)ため、すべての計算可能な問題にはCプログラムがあり、したがってCはチューリング完全です。

2.私の元の答え

理論的なコンピューターサイエンスの数学的概念(チューリング完全性など)と実際のアプリケーション、つまり実用的なコンピューターサイエンスの技術との間には、広範囲にわたる混乱があります。チューリング完全性は、物理的に存在するマシンまたは時空制限モデルのプロパティではありません。これは、数学理論の特性を記述する抽象的なオブジェクトです。

C99は、実質的に他の一般的なプログラミング言語と同様に、実装ベースの制限に関係なくチューリング完全です。これは、機能的に完全な論理接続セットを表現でき、原則として無制限の量のメモリにアクセスできるためです。人々は、Cが制限されることを明示的にランダムメモリアクセスを制限することを指摘しているが、これはチューリング完全性がされている間、これらはさらに、C標準で制限を明記しているので、1は回避できなかったものではありませんそれらなし伴うすでに:

これは、非構造的証明に十分な論理システムに関する非常に基本的なことです。論理的結果のセットがXであるような、いくつかの公理図式とルールをもつ計算を考えてみましょう。ここで、いくつかのルールまたは公理を追加すると、論理的結果のセットが大きくなります。つまり、Xのスーパーセットでなければなりません。 、モーダルロジックS4はS5に適切に含まれています。同様に、チューリング完全な下位仕様があるが、いくつかの制限を上に追加する場合、これらはXの結果を妨げません。つまり、すべての制限を回避する方法が必要です。チューリング完全でない言語が必要な場合は、計算を拡張するのではなく、縮小する必要があります。何かが不可能であると主張する拡張機能ですが、実際には矛盾を追加するだけです。ただし、チューリング完全性が実際のアプリケーションとは無関係であるように、C標準のこれらの矛盾は実際的な結果をもたらさないかもしれません。

再帰の深さに基づいて任意の数値をシミュレートする(つまりスケジューリング/擬似スレッドを介して複数の数値をサポートする可能性がある、Cの再帰の深さに理論的な制限はない)、またはファイルストレージを使用して無制限のプログラムメモリをシミュレートする(idea)おそらく、C99のチューリング完全性を建設的に証明する無限の可能性のうち2つだけです。計算可能性のために、時間と空間の複雑さは無関係であることを思い出すべきです。特に、チューリング完全性を偽造するために限られた環境を仮定することは、その制限が前提の複雑さの限界を超えるすべての問題を除外するため、単なる循環推論です。

:私はこの答えを書いたのは、ある種のアプリケーション指向の制限された思考のために人々が数学的な直観を得るために止められないようにするためです。ほとんどの学習者はより多くの人々がそのような誤った信念を広めるための推論の基本的な欠陥。この答えに反対票を投じれば、あなたは問題の一部に過ぎない。)


4
あなたの最後の段落には従いません。制限を追加すると表現力が高まると主張しますが、明らかにそうではありません。制限は表現力を低下させるだけです。たとえば、Cを使用して、プログラムが(あらゆる種類の)640kbを超えるストレージにアクセスできないという制限を追加すると、明らかにチューリング完全ではない派手な有限オートマトンになりました。
デビッドリチャービー

3
一定量のストレージがある場合、所有するリソースより多くのリソースを必要とするものをシミュレートすることはできません。メモリが配置できる構成は限られています。つまり、実行できることは限られています。
デビッドリチャービー

2
「物理的に既存のマシン」を参照する理由がわかりません。チューリング完全性は、物理システムではなく、数学計算モデルのプロパティであることに注意してください。有限のオブジェクトである物理システムがチューリングマシンのパワーに近づくことはできないことに同意しますが、それは無関係です。プログラミング言語を使用して、そのセマンティクスの数学的定義を検討し、その数学的オブジェクトがチューリング完全かどうかを確認できます。Conwayのライフゲームは、物理的な実装が不可能な場合でも強力なチューリングです。
カイ

2
@xamidこのサイトのモデレートポリシーに関して懸念がある場合は、Computer Science Metaにお問い合わせください。それまでは、素敵にしてください。他人の言葉による虐待は許されません。(手元の主題に関係しないすべてのコメントを削除しました。)
ラファエル

2
ポインターの幅を変更してもプログラムは変更されないと言いますが、プログラムはポインターの幅を読み取って、その値で必要な処理を実行できます。同じですCHAR_BITS
ドラコニス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.