C는 실제로 Turing-complete입니까?


39

나는 C가 Turing-complete라고 누군가에게 설명하려고 노력했고 실제로 그것이 실제로 Turing-complete인지 알지 못한다는 것을 깨달았습니다. (C는 실제 구현이 아닌 추상 의미론과 동일합니다.)

"명백한"답변 (대략 : 임의의 양의 메모리를 처리 할 수 ​​있으므로 RAM 시스템을 에뮬레이트 할 수 있으므로 Turing-complete입니다)은 C 표준이 허용하는 것처럼 알 수있는 한 실제로 정확하지 않습니다. size_t가 임의로 커지려면 어느 정도 길이로 고정되어야하며, 고정 된 길이에 관계없이 여전히 유한합니다. (즉, 임의의 정지 튜링 기계를 고려할 때 size_t의 길이를 선택하여 "적절하게"실행되도록 할 수 있지만, 모든 정지 튜링 기계가 올바르게 실행되도록 size_t의 길이를 선택할 수있는 방법은 없습니다)

C99 Turing-complete입니까?


3
C의 "추상 의미론"은 무엇입니까? 그들은 어디서나 정의되어 있습니까?
Yuval Filmus

4
@YuvalFilmus - 참조는 예 여기 예를 들어, 반대로 표준에 정의 된 "이 GCC는 그것을 수행하는 방법이다", 즉 C를.
TLW

1
최신 컴퓨터에는 TM과 같은 무한 메모리가 없지만 여전히 "유니버설 컴퓨터"로 간주되는 "기술"이 있습니다. "의미론 (semantics)"에서 프로그래밍 언어는 모든 구현 이 물론 메모리에 제한되어 있다는 점을 제외하고는 유한 메모리를 실제로 가정하지 않습니다 . 예를 들어 PC가 튜링 머신으로 작동합니까?를 참조하십시오 . 어쨌든 본질적으로 모든 "주류"프로그래밍 언어는 튜링 완료입니다.
vzn

2
C (Turing 머신과 같은)는 계산에 내부 컴퓨터 메모리를 사용하는 것으로 제한되지 않으므로 C의 Turing 완전성에 대해 실제로 유효한 인수는 아닙니다.
reinierpost

@reinierpost-텔레타이프가 현명하다고 말하는 것과 같습니다. 즉, "C + 외부 TM과 동등한"은 Turing-complete이며 C는 Turing-complete가 아닙니다.
TLW

답변:


34

나는 확실하지 않지만 미묘한 이유로 대답이 아니오라고 생각합니다. 나는 몇 년 전에 이론적 컴퓨터 과학을 물었고 여기에 제시 할 것 이상의 답을 얻지 못했습니다.

대부분의 프로그래밍 언어에서 다음을 통해 튜링 머신을 시뮬레이션 할 수 있습니다.

  • 유한 한 양의 메모리를 사용하는 프로그램으로 유한 오토 마톤을 시뮬레이션하는 단계;
  • 현재 위치 전후에 테이프의 내용을 나타내는 연결된 정수 목록으로 테이프를 시뮬레이션합니다. 포인터를 이동한다는 것은 목록 중 하나의 머리를 다른 목록으로 옮기는 것을 의미합니다.

테이프가 너무 길면 컴퓨터에서 실행되는 구체적인 구현에 메모리가 부족하지만 이상적인 구현은 Turing 머신 프로그램을 충실하게 실행할 수 있습니다. 이것은 펜과 종이로, 또는 더 많은 메모리를 가진 컴퓨터를 구입하고 프로그램에 메모리가 부족한 경우 워드 당 더 많은 비트 수를 가진 아키텍처를 대상으로하는 컴파일러를 사용하여 수행 할 수 있습니다.

영원히 커질 수있는 링크 된 목록을 가질 수 없기 때문에 C에서는 작동하지 않습니다. 노드 수에는 항상 제한이 있습니다.

이유를 설명하기 위해 먼저 C 구현이 무엇인지 설명해야합니다. C는 실제로 프로그래밍 언어의 패밀리입니다. ISO C 표준 구문과 의미 프로그래밍 언어의 가족 (영어 허용하는 형식의 수준) (이 표준보다 정확하게, 특정 버전)를 정의합니다. C에는 정의되지 않은 동작구현 정의 된 동작 이 많이 있습니다.. C의 "구현"은 모든 구현 정의 동작을 체계화합니다 (체계화 할 항목 목록은 C99의 부록 J에 있습니다). C의 각 구현은 별도의 프로그래밍 언어입니다. "구현"이라는 단어의 의미는 약간 특이합니다. 실제로 의미하는 것은 언어 변형이며, 동일한 언어 변형을 구현하는 여러 다른 컴파일러 프로그램이있을 수 있습니다.

주어진 C 구현에서 바이트에는 가능한 값이 있습니다. 모든 데이터는 바이트 배열로 표현할 수 있습니다. 유형 에는 최대 2 개의 CHAR_BIT × sizeof (t) 개의 값이 있습니다. 이 숫자는 C의 구현마다 다르지만 주어진 C의 구현에서는 상수입니다.2CHAR_BITt2CHAR_BIT×sizeof (t)

특히, 포인터는 최대 값만 사용할 수 있습니다. 즉, 주소 지정 가능한 개체의 최대 개수는 유한합니다.2CHAR_BIT×sizeof (void *)

CHAR_BIT및 의 값을 sizeof(void*)확인할 수 있으므로 메모리가 부족한 경우 해당 매개 변수에 대해 더 큰 값으로 프로그램 실행을 재개 할 수 없습니다. 다른 프로그래밍 언어 (다른 C 구현)로 프로그램을 실행하고있을 것입니다.

언어의 프로그램이 제한된 수의 상태 만 가질 수있는 경우 프로그래밍 언어는 유한 오토마타보다 더 표현력이 없습니다. 주소 지정 가능한 스토리지로 제한된 C의 단편은 최대 프로그램 상태 만 허용합니다. 여기서 n 은 프로그램 의 추상 구문 트리의 크기 (제어 흐름 상태를 나타냄)입니다. 프로그램은 많은 상태의 유한 오토 마톤으로 시뮬레이션 할 수 있습니다. C가 더 표현력이 좋으면 다른 기능을 사용해야합니다.×2CHAR_BIT×sizeof (void *)

C는 최대 재귀 깊이를 직접적으로 부과하지 않습니다. 구현은 최대 값을 가질 수 있지만이를 갖지 않는 것도 허용됩니다. 그러나 함수 호출과 부모 간의 통신은 어떻게 이루어 집니까? 인수가 주소 지정이 가능한 인수는 재귀의 깊이를 간접적으로 제한하기 때문에 좋지 않습니다. 함수가 있으면 활성 프레임 int f(int x) { … f(…) …}에서 발생하는 모든 고유 주소가 있으므로 중첩 된 호출 수는 숫자로 제한됩니다 가능한 주소 .xfx

AC 프로그램은 register변수 형식으로 주소를 지정할 수없는 저장소를 사용할 수 있습니다 . "정상"구현은 주소가없는 작고 유한 한 수의 변수 만 가질 수 있지만 이론적으로 구현하면 무한한 양의 register스토리지를 허용 할 수 있습니다. 이러한 구현에서는 인수가 인 한 무한한 양의 재귀 호출로 함수를 호출 할 수 있습니다 register. 그러나 인수가 register이므로 포인터를 만들 수 없으므로 명시 적으로 데이터를 복사해야합니다. 포인터로 구성된 임의의 크기의 데이터 구조가 아닌 유한 한 양의 데이터 만 전달할 수 있습니다.

무한 재귀 깊이와 함수가 직접 호출자 ( register인수) 로부터 데이터를 가져 오고 직접 호출자 (함수 반환 값)로 데이터를 반환 할 수 있다는 제한을 통해 결정적 푸시 다운 오토마타 의 힘을 얻을 수 있습니다.

더 나아갈 방법을 찾을 수 없습니다.

(물론 파일 입력 / 출력 기능을 통해 프로그램이 테이프 내용을 외부에 저장하도록 할 수 있습니다. 그러나 C가 Turing-complete인지 아닌지, C + 무한 저장 시스템인지 Turing-complete인지 묻지 않습니다. 당신은뿐만 아니라 튜링 오라클 될 수있는 저장 정의 할 수있는 대답은 지루한 "예"입니다 -. 전화  fopen("oracle", "r+"), fwrite그것에 초기 테이프 내용과 fread. 다시 최종 테이프 내용)


주소 집합이 유한해야하는 이유는 명확하지 않습니다. cstheory.stackexchange.com/a/37036/43393
Alexey B.

4
죄송하지만 같은 논리에 따라 Turing-complete 프로그래밍 언어는 전혀 없습니다. 각 언어에는 주소 공간에 대한 명시 적 또는 암시 적 제한이 있습니다. 무한 메모리를 가진 머신을 만들면 임의 액세스 포인터의 길이도 분명히 길어집니다. 따라서 이러한 머신이 나타나면 고급 언어 용 API와 함께 순차적 메모리 액세스를위한 명령어 세트를 제공해야합니다.
IMil

14
@IMil 이것은 사실이 아닙니다. 일부 프로그래밍 언어는 주소 공간에 대한 제한이 없으며 암시 적으로도 아닙니다. 명백한 예를 들자면, 테이프의 초기 상태가 프로그램을 구성하는 범용 Turing 머신은 Turing-complete 프로그래밍 언어입니다. 실제로 실제로 사용되는 많은 프로그래밍 언어 (예 : Lisp 및 SML)는 동일한 속성을 갖습니다. 언어에는 "무작위 액세스 포인터"라는 개념이 없어도됩니다. (계속)
Gilles 'SO- 악마 그만

11
@IMil (계속) 구현은 일반적으로 성능을 위해 수행되지만 특정 컴퓨터에서 실행되는 구현은 컴퓨터 메모리 크기에 의해 제한되므로 Turing-complete가 아님을 알고 있습니다. 그러나 그것은 구현이 전체 언어를 구현하지 않고 (N 바이트의 메모리에서 실행되는 프로그램의 하위 세트 만) 구현한다는 것을 의미합니다. 컴퓨터에서 프로그램을 실행할 수 있으며 메모리가 부족한 경우 더 큰 컴퓨터로 이동하거나 영원히 또는 중지 될 때까지 이동하십시오. 그것은 전체 언어를 구현하는 유효한 방법입니다.
Gilles 'SO- 악마 그만

6

C99의 va_copyvariadic argument API에 추가 하면 Turing-completeness의 뒷문이 될 수 있습니다. 원래 인수를받은 함수가 아닌 함수에서 가변 인수 목록을 두 번 이상 반복 va_args할 수 있으므로 포인터없는 포인터를 구현하는 데 사용할 수 있습니다.

물론, variadic 인수 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

@chi AIUI 누군가가 주소를 가져 오려고하지 않는 한 변수는 주소를 가질 필요가 없습니다. 또한 줄임표 바로 앞에 인수를 선언하는 것은 불법 register입니다.
feersum

같은 논리에 의해 int누군가가 경계를 사용하지 않는 한 경계를 가질 필요 는 없습니다 sizeof(int).
chi

@chi 전혀 아닙니다. 표준 int은 a의 유한 경계 INT_MIN와 사이의 값 을 갖는 추상 의미론의 일부로 정의합니다 INT_MAX. 그리고 int오버플로 값이 이러한 바운드를 초과하면 정의되지 않은 동작이 발생합니다. 반면, 표준은 의도적으로 모든 객체가 특정 주소의 메모리에 물리적으로 존재하도록 요구하지 않습니다. 이는 객체를 레지스터에 저장, 객체의 일부만 저장하고 표준과 다르게 표현하는 것과 같은 최적화를 허용하기 때문입니다. 필요하지 않은 경우 레이아웃을 생략하거나 완전히 생략합니다.
feersum

4

비표준 산술, 아마도?

따라서 문제는 유한 크기 인 것 같습니다 sizeof(t). 그러나 해결 방법을 알고 있다고 생각합니다.

내가 아는 한 C는 정수 유형에 표준 정수를 사용하는 구현이 필요하지 않습니다. 따라서 우리는 비표준 산술 모델을 사용할 수 있습니다 . 그런 다음 sizeof(t)비표준 숫자로 설정 하면 유한 한 단계 만 거치지 않습니다. 따라서 튜링 머신 테이프의 길이는 문자 그대로 최대 값에 도달 할 수 없기 때문에 항상 "최대 값"보다 작습니다. sizeof(t)단순히 단어의 규칙적인 의미에서 숫자가 아닙니다.

이것은 Tennenbaum의 정리 : 물론 기술 중 하나 입니다. Peano 산술의 유일한 모델은 표준 모델이지만 분명히 그렇지 않습니다. 그러나 내가 아는 한 C는 Peano 공리를 만족시키는 데이터 유형을 사용하기 위해 구현이 필요하지 않으며 구현이 계산 가능해야하지 않으므로 문제가되지 않습니다.

비표준 정수를 출력하려고하면 어떻게됩니까? 비표준 문자열을 사용하여 비표준 정수를 나타낼 수 있으므로 해당 문자열의 앞면에서 숫자를 스트리밍하십시오.


구현은 어떤 모습입니까?
reinierpost

@ reinierpost 필자는 셀 수없는 비표준 PA 모델을 사용하여 데이터를 나타내는 것으로 추측합니다. PA 학위를 사용하여 산술 연산을 계산 합니다. 그러한 모델은 유효한 C 구현을 제공해야한다고 생각합니다.
PyRulez

죄송합니다. 작동하지 않습니다. sizeof(t)자체는 유형의 값 size_t이므로 0과 사이의 자연 정수 SIZE_MAX입니다.
Gilles 'SO- 악마 그만'

@Gilles SIZE_MAX는 비표준적인 것입니다.
PyRulez

이것은 흥미로운 접근법입니다. 예를 들어 intptr_t / uintptr_t / ptrdiff_t / intmax_t / uintmax_t가 비표준이되어야합니다. C ++에서 이것은 앞으로의 진행 보증을 무시하고 C에 대해 확실하지 않습니다.
TLW

0

IMO의 강력한 한계는 포인터 크기를 통해 주소 지정 가능한 공간이 유한하며 복구 할 수 없다는 것입니다.

메모리가 "디스크로 스왑"될 수 있다고 주장 할 수 있지만, 어느 시점에서 주소 정보 자체가 주소 지정 가능한 크기를 초과합니다.


이것이 대답의 요점이 아닌가? 나는 그것이 2016 년 질문의 답에 새로운 것을 추가한다고 생각하지 않습니다.

@chi : 아니요, 허용되는 답변은 외부 메모리로의 스와핑을 언급하지 않으며, 이는 해결 방법으로 여겨 질 수 있습니다.
이브 다우 스트

-1

실제로, 이러한 제한은 Turing 완전성과 관련이 없습니다. 실제 요구 사항은 테이프가 무한정이 아닌 임의의 길이가되도록하는 것입니다. 그것은 다른 종류의 정지 문제를 일으킬 것입니다 (우주는 어떻게 테이프를 "계산"합니까?)

"Python은 튜링이 완전하지 않습니다. 목록을 무한정 크게 만들 수 없기 때문입니다."

[편집 : 편집 방법을 명확히 해준 Mr. Whitledge에게 감사드립니다.]


7
나는 이것이 질문에 대답하지 않는다고 생각한다. 이 질문은 이미이 답변을 예상하고 왜 이것이 유효하지 않은지를 설명했다. "C 표준은 size_t가 임의로 커질 수 있지만 어느 정도의 길이로 고정되어야하며, 어떤 길이로 고정 되더라도 여전히 유한하다 ". 그 주장에 대한 답변이 있습니까? 나는 그 논증이 왜 틀린지 (또는 옳은지) 설명하지 않는 한, 우리는 질문을 답으로 간주 할 수 없다고 생각합니다.
DW

5
주어진 시간에 type 값 size_t은 유한합니다. 문제는 size_t계산 전체에서 유효한 범위를 설정할 수 없다는 것입니다. 모든 범위의 경우 프로그램이 오버플로 할 수 있습니다. 그러나 C 언어 size_t는 주어진 구현에서 다음과 같은 한계가 있다고 말합니다 sizeof(size_t). 또한 좋을 것 입니다. 당신을 비판하는 사람들이“자신을 생각할 수 없다”고 말하는 것은 무례합니다.
Gilles 'SO- 악마 그만해

1
이것이 정답입니다. 터닝 머신에는 무한 테이프가 필요하지 않으며 "임의로 긴"테이프가 필요합니다. 즉, 테이프가 계산을 완료하는 데 필요한 길이 인 것으로 가정 할 수 있습니다. 컴퓨터에 필요한만큼의 메모리가 있다고 가정 할 수도 있습니다. 무한 시간에 정지되는 계산은 무한량의 테이프를 사용할 수 없기 때문에 무한 테이프는 반드시 필요한 것은 아닙니다.
Jeffrey L Whitledge

이 답변이 보여주는 것은 각 TM에 대해 시뮬레이션하기에 충분한 포인터 길이로 C 구현을 작성할 수 있다는 것입니다. 그러나 TM을 시뮬레이션 수있는 하나의 C 구현 을 작성할 수는 없습니다 . 따라서이 사양에서는 특정 구현이 T- 완료되는 것을 금지합니다. 포인터 길이가 고정되어 있기 때문에 T- 완료 자체도 아닙니다.

1
이것은이 지역 사회에서 대부분의 개인이 무능력하기 때문에 거의 눈에 띄지 않는 또 다른 정답입니다. 한편, 승인 된 답변은 거짓이며 그 의견 섹션은 중재자가 중요한 의견을 삭제함으로써 보호됩니다. 안녕, cs.stackexchange.
xamid

-1

이동식 매체를 사용하면 무한한 메모리 문제를 피할 수 있습니다. 아마도 사람들은 이것이 학대라고 생각할 것입니다. 그러나 나는 그것이 괜찮고 어쨌든 피할 수 없다고 생각합니다.

범용 Turing 머신의 구현을 수정하십시오. 테이프에는 이동식 미디어를 사용합니다. 헤드가 현재 디스크의 끝 또는 시작 부분에서 떨어지면 머신은 다음 또는 이전 디스크를 삽입하라는 메시지를 표시합니다. 특수 마커를 사용하여 시뮬레이션 된 테이프의 왼쪽 끝을 표시하거나 양방향으로 제한되지 않은 테이프를 가질 수 있습니다.

여기서 핵심은 C 프로그램이 수행해야 할 모든 것이 유한하다는 것입니다. 컴퓨터는 오토 마톤을 시뮬레이션하기에 충분한 메모리 size_t만 필요하며 고정 된 크기의 고정 된 크기의 메모리와 디스크에서 메모리를 처리 할 수있을만큼 충분히 커야합니다. 사용자에게 다음 또는 이전 디스크 만 삽입하라는 메시지가 표시되므로 "디스크 번호 123456을 삽입하십시오 ..."라고 말할 때 무한정 큰 정수가 필요하지 않습니다.

나는 주된 이의가 사용자의 참여에 대한 것이라고 생각하지만, 무한한 메모리를 구현하는 다른 방법이 없기 때문에 어떤 구현에서도 피할 수없는 것처럼 보입니다.


3
C 정의에 이러한 무제한 외부 저장소가 필요하지 않으면 튜링 완전성의 증거로 받아 들여질 수 없다고 주장합니다. (ISO 9899는 물론 실제 엔지니어링을 위해 작성 될 필요는 없습니다.) 저를 우려하는 것은 비슷한 이유에 의해 DFA가 튜링이 완료되었다고 주장 할 수도 있습니다. 테이프 (외부 스토리지)를 통해 헤드를 구동합니다.
chi

@chi DFA 주장이 어떻게 따르는 지 모르겠습니다. DFA의 요점은 스토리지에 대한 읽기 권한 만 있다는 것입니다. "테이프 위를 향한 주행"을 허용한다면 정확히 튜링 머신이 아닙니까?
David Richerby 2016 년

2
사실, 나는 여기서 조금 따끔 거립니다. 요점은 : C에 "테이프"를 추가하고, C가 DFA를 시뮬레이션하고,이 사실을 사용하여 DFA와 동일한 작업을 수행 할 수 없을 때 C가 튜링이 완료되었다고 주장하는 것이 좋은 이유는 무엇입니까? C가 자체적으로 무한 메모리를 구현할 방법이 없다면 튜링 완료로 간주해서는 안됩니다. (나는 여전히 그것을 "도덕적으로"라고 부른다. 튜링은 적어도 한계가 너무 커서 실제로는 대부분의 경우 중요하지 않기 때문에)이 문제를 결정적으로 해결하려면 엄격한 공식 사양이 필요하다고 생각한다. C (ISO 표준으로는 충분하지 않음)
chi

1
@chi C는 파일 I / O 루틴을 포함하기 때문에 괜찮습니다. DFA는 그렇지 않습니다.
David Richerby 2016 년

1
C는 이러한 루틴이 수행하는 작업을 완전히 지정하지는 않습니다. 대부분의 시맨틱은 구현 정의입니다. AC 구현은 파일 내용을 저장하는 데 필요하지 않습니다. 예를 들어 모든 파일이 "/ dev / null"인 것처럼 동작 할 수 있다고 생각합니다. 무제한의 데이터를 저장할 필요도 없습니다. C 구현의 대부분을 고려하고 그 동작을 이상적인 머신으로 일반화 할 때 당신의 주장이 맞다고 말하고 싶습니다. 우리가 엄격하게 C 정의에만 의존하고 연습을 잊어 버린다면, 나는 그것이 정의를 가지고 있다고 생각하지 않습니다.
chi

-2

size_t무한대로 선택

당신은 size_t무한대로 크게 선택할 수 있습니다 . 당연히 그러한 구현을 실현하는 것은 불가능합니다. 그러나 우리가 살고있는 세상의 유한 한 특성을 고려할 때 놀라운 것은 아닙니다.

실질적인 의미

그러나 그러한 구현을 실현하는 것이 가능하더라도 실질적인 문제가있을 것입니다. 다음 C 문을 고려하십시오.

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

SIZE_MAXSIZE_MAX영형(2에스나는이자형_)size_tSIZE_MAXprintf

다행스럽게도 이론 상으로는 사양 printf에서 모든 입력에 대해 보장되는 요구 사항을 찾을 수 없었습니다 . 따라서 내가 아는 한 여기에서는 C 사양을 위반하지 않습니다.

전산 완전성

우리의 이론적 구현이 Turing Complete 라는 것을 여전히 증명해야 한다 . "단일 탭 튜링 머신"을 구현하여이를 보여줄 수 있습니다.

우리 대부분은 아마도 튜링 머신을 학교 프로젝트로 구현했을 것입니다. 특정 구현에 대한 세부 정보는 제공하지 않지만 일반적으로 사용되는 전략은 다음과 같습니다.

  • 상태 수, 기호 수 및 상태 전이 테이블은 특정 시스템에 대해 고정되어 있습니다. 따라서 상태와 기호를 숫자로 나타내고 상태 전이 테이블을 2 차원 배열로 나타낼 수 있습니다.
  • 테이프는 연결된 목록으로 표시 될 수 있습니다. 단일 이중 연결 목록 또는 두 개의 단일 연결 목록 (현재 위치에서 각 방향에 대해 하나씩)을 사용할 수 있습니다.

이제 그러한 구현을 실현하는 데 필요한 것을 보자.

  • 고정되어 있지만 임의로 큰 숫자 집합을 나타내는 기능. 임의의 숫자를 나타 내기 위해 MAX_INT무한대로 선택 합니다. 또는 다른 객체를 사용하여 상태와 기호를 나타낼 수도 있습니다.
  • 테이프에 대해 임의로 큰 링크 목록을 구성하는 기능. 다시 한 번, 크기에는 제한이 없습니다. 이것은 우리가 테이프를 구성하기 위해 영원히 지출 할 것이기 때문에이 목록을 미리 구성 할 수 없다는 것을 의미합니다. 그러나 동적 메모리 할당을 사용하면이 목록을 점차적으로 구성 할 수 있습니다. 우리는 사용할 수 malloc있지만 고려해야 할 것이 조금 더 있습니다.
    • malloc예를 들어 사용 가능한 메모리가 소진 된 경우 C 사양은 실패 할 수 있습니다. 따라서 우리의 구현은 malloc결코 실패하지 않는 한 진정한 보편적 입니다.
    • 그러나 우리의 구현이 무한 메모리를 가진 머신에서 실행된다면, malloc실패 할 필요가 없습니다 . C 표준을 위반하지 않으면 구현이 malloc실패하지 않을 것입니다.
  • 포인터를 역 참조하고 배열 요소를 조회하며 연결된 목록 노드의 멤버에 액세스하는 기능

위의 목록은 가상 C 구현에서 Turing Machine을 구현하는 데 필요한 것입니다. 이러한 기능은 종료해야합니다. 그러나, 표준에 의해 요구되지 않는 한, 다른 어떤 것도 종료되지 않을 수 있습니다. 여기에는 산술, IO 등이 포함됩니다.


6
printf("%zu\n",SIZE_MAX);그러한 구현에서 무엇을 인쇄 할 것입니까 ?
Ruslan

1
@Ruslan, Turing 머신을 구현하는 것이 불가능한 것처럼 그러한 구현은 불가능합니다. 그러나 그러한 구현이 가능하다면 무한한 큰 숫자, 아마도 무한한 10 진수 스트림의 소수 표현을 인쇄한다고 생각합니다.
Nathan Davis

2
@NathanDavis 튜링 머신을 구현할 수 있습니다. 비결은 무한 테이프를 만들 필요가 없다는 것입니다. 필요한만큼 테이프의 사용 된 부분을 증 분식으로 빌드하면됩니다.
Gilles '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)입니다. 당신이 그것을 인쇄하려면 우리는 일부 출력에 동의 할 수 있습니다. 이 둘의 차이는 NaN 등일 수 있습니다.

요약하면 size_t의 조건을 완화해도 크기가 유한하면 이미 존재하는 프로그램이 중단되지 않습니다.

추신 : 메모리 sizeof (size_t) 할당은 여전히 ​​가능합니다. 셀 수있는 크기 만 필요하므로 모든 짝수 (또는 유사한 트릭)를 가져 봅시다.


1
"이 둘의 차이점은 NaN 일 수 있습니다." 아닙니다. C에는 정수형 NaN과 같은 것은 없습니다.
TLW

표준에 따르면을 sizeof반환해야합니다 size_t. 따라서 특정 가치를 선택해야합니다.
Draconis

-4

그렇습니다.

1. 인용 답변

놀랍게도 허위 답변에 대한 승인과 비교할 때 내 (및 다른) 정답에 대한 많은 다운 보트에 대한 반응으로 이론적으로 덜 심오한 대안 설명을 검색했습니다. 내가 찾은 하나. 여기에 일반적인 오류 중 일부가 포함되어 있기 때문에 약간의 통찰력이 표시되기를 바랍니다. 논쟁의 핵심 부분 :

[...] 그의 주장은 다음과 같습니다. 실행 중에 임의의 저장 용량을 요구할 수있는 지정된 종료 프로그램을 작성했다고 가정하십시오. 그 프로그램을 바꾸지 않고서는,이 계산을 수용하기에 충분한 저장 공간을 제공하는 컴퓨터 하드웨어와 C 컴파일러를 구현하는 것이 가능합니다. 이것은 char의 너비 (CHAR_BITS를 통해) 및 / 또는 포인터 (size_t를 통해)를 넓힐 필요가 있지만 프로그램을 수정할 필요는 없습니다. 이것이 가능하기 때문에 C는 실제로 프로그램 종료를위한 Turing-Complete입니다.

이 주장의 까다로운 부분은 프로그램 종료를 고려할 때만 작동한다는 것입니다. 종료 프로그램은 저장 요구 사항에 정적 상한이 있다는이 멋진 특성을 가지고 있으며, "적합"할 때까지 증가하는 저장 용량으로 원하는 입력에 대해 프로그램을 실행하여 실험적으로 결정할 수 있습니다.

내가 내 생각의 기차에서 오도 한 이유는 "유용한"비 종료 프로그램의 넓은 클래스를 고려하고 있다고 생각했다 ...]

요컨대, 모든 계산 가능한 함수에 대해 C 언어로 된 솔루션이 있기 때문에 (무제한 상한으로 인해) 모든 계산 가능한 문제에는 C 프로그램이 있으므로 C는 Turing-complete입니다.

2. 내 원래 답변

이론적 컴퓨터 과학 (예 : Turing-completeness)의 수학적 개념과 실제 응용, 즉 실제 컴퓨터 과학의 기술 사이에는 광범위한 혼란이 있습니다. 튜링-완전성은 물리적으로 존재하는 기계 또는 시공간 제한 모델의 속성이 아닙니다. 수학적 이론의 속성을 설명하는 추상 객체 일뿐입니다.

C99는 기능적으로 완전한 논리적 연결 집합을 표현할 수 있으며 원칙적으로 무제한의 메모리에 액세스 할 수 있기 때문에 거의 모든 다른 일반적인 프로그래밍 언어와 마찬가지로 구현 기반 제한 사항에 관계없이 Turing-complete입니다. 사람들은 C 명시 적으로 한정 할 수있는 임의 메모리 액세스를 제한하는 지적 밖으로있다, 그러나 튜링 완전성을하는 동안 이들은 C 표준에서 추가로 언급 한 제한이 있기 때문에 이것은 아무것도 한 수를 우회하지 않습니다 그들없이 수반 이미 :

비 구조적 증거로 충분해야하는 논리 시스템에 대한 매우 기본적인 사항은 다음과 같습니다 . 논리적 결과 집합이 X가되도록 공리 스키마 및 규칙이있는 미적분을 고려하십시오. 이제 규칙 또는 공리를 추가하면 논리적 결과 집합이 커집니다. 즉, X의 수퍼 셋이어야합니다. , 모달 로직 S4가 S5에 올바르게 포함되어 있습니다.. 마찬가지로 Turing-complete 인 하위 사양이 있지만 일부 제한 사항을 추가하면 X의 결과를 막을 수 없습니다. 즉, 모든 제한 사항을 피할 수있는 방법이 있어야합니다. 튜링 완료 언어가 아닌 언어를 원할 경우 확장이 아닌 미적분을 줄여야합니다. 무언가는 불가능하지만 실제로는 불일치를 추가한다고 주장하는 확장. C 표준의 이러한 불일치는 실제 적용과 관련이없는 것처럼 튜링-완전성이 실제적인 결과를 나타내지 않을 수도 있습니다.

재귀 깊이에 따라 임의의 숫자를 시뮬레이션 (즉, ; 스케줄링 / 의사 스레드를 통해 여러 개의 숫자를 지원할 수있는 가능성 재귀 C의 깊이에 이론상의 제한이 없다 ), 또는 무제한 프로그램 메모리 (시뮬레이션 파일 스토리지를 사용하여 아이디어를 있다가) C99의 Turing-completeness 를 건설적으로 증명할 수있는 무한한 가능성 중 단 두 가지 일 것입니다 . 계산 가능성, 시간 및 공간 복잡성은 관련이 없다는 것을 기억해야합니다. 특히, Turing-completeness를 위조하기 위해 제한된 환경을 가정하는 것은 원형 추론 일 뿐이며, 그 한계는 가정 된 복잡성 한계를 초과하는 모든 문제를 배제하기 때문입니다.

( 참고 : 나는 단지 어떤 응용 지향적 인 제한적 사고로 인해 사람들이 수학 직관을 얻지 못하도록 막기 위해이 답변을 썼다. 대부분의 학습자들이 그것을 바탕으로 한 오답으로 인해 허위로 답을 읽는 것은 매우 유감입니다. 추론의 근본적인 결함으로 인해 더 많은 사람들이 그러한 잘못된 믿음을 퍼뜨릴 수 있습니다.


4
나는 당신의 마지막 단락을 따르지 않습니다. 제한을 추가하면 표현력이 향상된다고 주장하지만 이는 사실이 아닙니다. 제한은 표현력 만 감소시킬 수 있습니다. 예를 들어 C를 사용하여 어떤 프로그램도 640kb 이상의 저장소에 액세스 할 수 없다는 제한을 추가하면 분명히 튜링이 완료되지 않은 멋진 유한 자동 장치로 바꿨습니다.
David Richerby

3
고정 된 양의 스토리지가있는 경우 보유한 것보다 더 많은 리소스가 필요한 것을 시뮬레이션 할 수 없습니다. 메모리에있을 수있는 구성은 유한하게 많기 때문에 할 수있는 일이 엄청나게 많음을 의미합니다.
David Richerby

2
왜 "실제로 존재하는 기계"를 말하는지 이해가되지 않습니다. Turing-completeness는 물리적 시스템이 아니라 수학적 계산 모델의 속성입니다. 유한 한 물체 인 물리적 시스템이 Turing 머신의 힘에 가까워 질 수는 없지만 동의하지 않는다는 것에 동의합니다. 우리는 여전히 어떤 프로그래밍 언어를 사용하고, 그 의미론의 수학적 정의를 고려하고, 수학적 객체가 Turing 완료인지 검사 할 수 있습니다. Conway의 인생 게임은 가능한 물리적 구현이 없더라도 강력한 Turing입니다.

2
@xamid이 사이트의 운영 정책에 대해 우려가 있으시면 Computer Science Meta로 가져 가십시오 . 그때까지는 좋으십시오 . 다른 사람의 언어 학대는 용납되지 않습니다. (현재 다루는 주제와 관련이없는 의견은 모두 삭제했습니다.)
Raphael

2
포인터 너비를 수정해도 프로그램이 변경되지는 않지만 프로그램은 포인터 너비를 읽고 해당 값으로 원하는 것을 수행 할 수 있습니다. 동일합니다 CHAR_BITS.
Draconis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.