반복이 재귀를 대체 할 수 있습니까?


41

나는 스택 오버플로를 보았습니다. 예를 들어 여기 , 여기 , 여기 , 여기 , 여기 그리고 여기 에 언급하고 싶지 않은 다른 것들, "재귀를 사용하는 모든 프로그램은 반복만을 사용하는 프로그램으로 변환 될 수 있습니다."

가능하다고 답한 고도로 공감 된 을 가진 고도로 공감 된 스레드 가있었습니다 .

지금은 그들이 틀렸다는 말이 아닙니다. 그 대답은 컴퓨팅에 대한 나의 빈약 한 지식과 이해에 반하는 것입니다.

나는 모든 반복 함수가 재귀로 표현 될 수 있다고 생각하며 wikipedia에는 ​​그 효과에 대한 진술이 있습니다. 그러나 나는 그 대화가 사실인지 의심합니다. 우선, 기본이 아닌 재귀 함수를 반복적으로 표현할 수 있을지 의심됩니다.

또한 과잉 연산이 반복적으로 표현 될 수 있을지는 의문입니다.

@YuvalFIlmus 내 질문에 대한 그의 대답 (내가 이해하지 못함)에서 수학 연산 시퀀스를 추가 시퀀스로 변환 할 수는 없다고 말했습니다.

YF의 대답이 실제로 정확하다면 (그것이 맞지만 그의 추론이 내 머리 위에 있음) 이것이 모든 재귀가 반복으로 변환 될 수있는 것은 아닙니다. 모든 재귀를 반복으로 변환 할 수 있다면 모든 작업을 일련의 추가로 표현할 수 있기 때문입니다.

내 질문은 이것입니다 :

모든 재귀를 반복으로 변환 할 수 있습니까?

밝은 고등학생 또는 1 학년생이 이해할 수있는 답변을 제공하십시오. 감사합니다.

추신 : 나는 원시 재귀가 무엇인지 모른다.

PPS : 대답이 '예'인 경우, 예를 들어 비원시 재귀 함수의 반복 버전을 작성할 수 있습니까? 예를 들어 Ackermann이 대답합니다. 이해하는 데 도움이 될 것입니다.


21
CPU에서 실행되는 모든 것은 반복적입니다. 고차 언어로 재귀 적으로 쓸 수도 있지만 어쨌든 여러 반복 어셈블러 명령어로 컴파일됩니다. 말 그대로, 컴파일러는 정확히 당신이 요구하는 것을 수행합니다. 모든 멋진 재귀를 CPU에 대한 반복 명령으로 변환합니다.
Davor

6
낮은 수준에서 대부분의 사람들은 재귀가 반복 + 스택과 같다는 것을 알고 있습니다. 상황에 맞지 않는 문법 모델 재귀, 푸시 다운 오토마타는 스택 메모리가있는 반복적 인 "프로그램"입니다.
Hendrik Jan

2
다른 답변이 표시되는지 확인하려면 일반적으로 24 시간 이상 기다리는 것이 좋습니다. 받아 들여진 질문은 솔직히 길고 복잡해 보였고, 필요 이상으로 문제를 고의적으로 복잡하게 만드는 것 같습니다. 귀하의 질문에 대한 기본적인 대답은 "재귀에 사용되는 스택은 반복적으로 명시 적으로 구현 될 수 있습니다"이며, 그보다 훨씬 더 복잡 할 필요는 없습니다. 어쨌든 재귀 스택에도 해당 기능이 필요하므로 메모리가 제한되지 않거나 메모리에 대한 고려 사항이 적용되지 않습니다.
AnoE

만 반복과 튜링 기계 에뮬레이터를 구현하는 것이 가능
사지 (Sarge) 러시아 식 수프의 일종에게

오래 전에 사용했던 비교 언어 클래스에서 우리는 어셈블리, FORTRAN (현대 포트란이 아님) 및 재귀 언어로 Ackermann의 기능을 작성해야했습니다. 예, 실제로 가능합니다. 물론 이론 상으로는 가능합니다. 예를 들어 튜링 기계와 람다 미적분이 동일하다는 것을 증명하는 질문을 참조하십시오 .
David Hammen

답변:


51

반복 과 무제한 메모리로 재귀를 대체 할 수 있습니다 .

반복 (예 : while 루프)과 유한 한 양의 메모리 만있는 경우 유한 한 오토 마톤 만 있으면됩니다. 유한 한 양의 메모리를 사용하면 계산에는 유한 한 수의 단계가 있으므로 유한 한 오토 마톤으로 모든 단계를 시뮬레이션 할 수 있습니다.

무한한 메모리가 있으면 거래가 변경됩니다. 이 무한한 기억은 많은 표현력을 가질 수있는 많은 형태를 취할 수 있습니다. 예를 들어, 튜링 머신은 간단하게 유지합니다. 단일 테이프가 있고 컴퓨터는 한 번에 한 단계 씩 테이프에서 앞뒤로 만 이동할 수 있지만 재귀 기능으로 수행 할 수있는 모든 작업을 수행하기에 충분합니다.

튜링 머신은 필요에 따라 확장되는 추가 스토리지를 갖춘 이상적인 컴퓨터 모델 (유한 상태 머신)로 볼 수 있습니다. 테이프에 한정된 경계가있을뿐만 아니라 입력이 주어진 경우에도 필요한 테이프의 양을 확실하게 예측할 수없는 것이 중요합니다. 입력에서 필요한 테이프의 양을 예측 (예 : 계산) 할 수있는 경우 최대 테이프 크기를 계산 한 다음 현재 유한 테이프를 포함한 전체 시스템을 유한 상태 머신으로 처리하여 계산을 중지할지 여부를 결정할 수 있습니다. .

컴퓨터로 튜링 머신을 시뮬레이션하는 다른 방법은 다음과 같습니다. 테이프의 시작 부분을 메모리에 저장하는 컴퓨터 프로그램으로 Turing 기계를 시뮬레이션하십시오. 계산이 메모리에 맞는 테이프 부분의 끝에 도달하면 컴퓨터를 더 큰 컴퓨터로 교체하고 계산을 다시 실행하십시오.

이제 컴퓨터를 사용하여 재귀 계산을 시뮬레이션한다고 가정하십시오. 재귀 함수를 실행하는 기술은 잘 알려져 있습니다. 각 함수 호출 에는 스택 프레임 이라는 메모리가 있습니다. 결정적으로 재귀 함수는 변수를 전달하여 여러 호출을 통해 정보를 전파 할 수 있습니다. 컴퓨터에서의 구현 측면에서 볼 때 이는 함수 호출이 ( * ) * 부모 호출 의 스택 프레임에 액세스 할 수 있음을 의미합니다 .

컴퓨터는 유한 상태 머신 (엄청나게 많은 상태를 가지고 있지만 여기서 계산 이론을 수행하고 있기 때문에 중요한 것은 유한하다는 것) 인 프로세서는 유한 메모리와 결합 된 프로세서입니다. 마이크로 프로세서는 하나의 거대한 while 루프를 실행합니다. "전원이 켜져있는 동안 메모리에서 명령을 읽고 실행합니다." (실제 프로세서는 그보다 훨씬 복잡하지만 계산할 수있는 것, 속도 및 편리성에 영향을 미치지 않습니다.) 컴퓨터는 반복을 제공하기 위해이 while 루프만으로 재귀 함수를 실행할 수 있습니다. 마음대로 메모리 크기를 늘리는 기능을 포함하여 액세스 메모리에 액세스합니다.

재귀를 기본 재귀로 제한하면 반복을 경계 반복으로 제한 할 수 있습니다 . 즉, 예기치 않은 실행 시간으로 while 루프를 사용하는 대신 루프 시작 부분에 반복 횟수가 알려진 루프에 for 루프를 사용할 수 있습니다. 반복 횟수는 프로그램 시작 부분에 알려지지 않았을 수 있습니다. 자체 루프는 이전 루프에 의해 계산 될 수 있습니다.

여기서는 증거를 스케치하지는 않지만 기본 재귀에서 전체 재귀로, for 루프에서 while 루프로 이동하는 것 사이에는 직관적 인 관계가 있습니다. 두 경우 모두 사전에 알지 못합니다. 중지. 완전 재귀를 사용하면 최소화 연산자를 사용하여 조건을 만족하는 매개 변수를 찾을 때까지 계속 진행합니다. while 루프의 경우 루프 조건이 충족 될 때까지 계속 진행하면됩니다.

forwhilen


요청 된 수준으로 유지되는 자세한 설명을 수락합니다.
Tobi Alafin

12
실제 컴퓨터보다 주목할 가치가 있다고 생각합니다. 재귀는 또한 메모리 (전화 스택 증가)를 사용합니다. 따라서 실제 애플리케이션에서는 무제한 메모리가 필요하지 않습니다 (모든 것이 동일하게 제한되므로). 그리고 재귀는 종종 반복보다 더 많은 메모리가 필요합니다.
Agent_L

@Agent_L 예, 재귀에는 모든 스택 프레임을 저장하기 위해 무제한 메모리가 필요합니다. 재귀 방식을 사용하면 무제한 메모리가 필요하지만 반복과 같은 작업 순서를 직관적으로 분리 할 수는 없습니다.
Gilles 'SO- 악마 그만

아마도 관심있는 주제는 교회 튜링 논문 일 것입니다. 튜링 기계는 (고유 한) 재귀 개념이없는 반복성이 높은 기계입니다. Lambda 미적분학은 재귀 방식으로 계산을 표현하기 위해 고안된 언어입니다. Church-Turing 논문은 모든 람다 표현이 재귀와 반복을 연결하는 튜링 머신에서 평가 될 수 있음을 보여주었습니다.
Cort Ammon

1
@BlackVegetable 재귀 메서드에 내부 변수가없고 사용하는 유일한 메모리가 콜 스택 (TCO에 의해 최적화 될 수있는 것) 인 경우, 반복 버전은 내부 변수가없고 일정한 양의 메모리 만 사용합니다 ( 변수) 카운터와 같은 모든 반복에서 공유됩니다.
Agent_L

33

페치 실행 무한 반복을 사용하여 임의의 프로그램을 실행하는 CPU에서 목격 한대로 모든 재귀를 반복으로 변환 할 수 있습니다. 이것은 Böhm-Jacopini 정리 의 한 형태입니다 . 또한 많은 Turing-complete 계산 모델에는 재귀가 없습니다 (예 : Turing 기계카운터 기계) .

기본 재귀 함수 는 바운드 반복을 사용하는 프로그램에 해당합니다. 즉, 루프가 미리 실행되는 반복 횟수를 지정해야합니다. Ackermann 함수는 원시 재귀가 아니기 때문에 경계 반복은 일반적으로 재귀를 시뮬레이션 할 수 없습니다. 그러나 무한 반복은 부분적으로 계산 가능한 기능을 시뮬레이션 할 수 있습니다.


25

a(n,m)

s

push(s,x)xxpop(s)empty(s)

Ackermann(n0,m0):

  • s= (빈 스택 초기화)
  • push(s,n0)
  • push(s,m0)
  • while(true):
    • mpop(s)
    • if(empty(s)):
      • return m (루프를 종료합니다)
    • npop(s)
    • if(n=0):
      • push(s,m+1)
    • else if(m=0):
      • push(s,n1)
      • push(s,1)
    • else:
      • push(s,n1)
      • push(s,n)
      • push(s,m1)

나는 이것을 실론에서 구현했다 . 원한다면 WebIDE에서 실행할 수있다. 루프의 각 반복이 시작될 때 스택을 출력합니다.

물론 이것은 암시 적 호출 스택을 재귀에서 매개 변수 및 반환 값이있는 명시 적 스택으로 옮겼습니다.


15
이것이 중요한 포인트라고 생각합니다. 재귀는 특별한 것이 아니며 컴파일러가 수행하도록하는 대신 호출 스택을 직접 추적하여 제거 할 수 있음을 분명히 보여주었습니다. 재귀는 이러한 종류의 프로그램을 쉽게 작성할 수있는 컴파일러 기능 일뿐입니다.
David Richerby 2016

4
요청한 프로그램을 작성해 주셔서 감사합니다.
Tobi Alafin

15

이미 좋은 답변이 있지만 (이것도 경쟁하기를 희망하지는 않지만)이 간단한 설명을하고 싶습니다.

재귀는 단지 런타임 스택의 조작입니다. 재귀는 (재귀 함수의 새로운 호출을 위해) 새로운 스택 프레임을 추가하고, 재귀는 (재귀 함수의 방금 완성 된 혁신을 위해) 스택 프레임을 제거합니다. 재귀는 결국 모든 프레임 프레임이 종료되고 (바람직하게!) 결과가 호출자에게 반환 될 때까지 일부 스택 프레임이 추가 / 제거됩니다.

이제 자신의 스택을 만들고 스택으로 푸시하여 재귀 함수 호출을 대체하고 스택을 팝핑하여 재귀 함수에서 반환을 대체하고 스택이 비어있을 때까지 실행되는 while 루프가 있으면 어떻게됩니까?


2

내가 알 수있는 한, 내 경험으로는 반복으로 모든 재귀를 구현할 수 있습니다. 위에서 언급했듯이 재귀는 개념적으로 무제한이지만 실제로 제한적인 스택을 사용합니다 (스택 오버플로 메시지를 받았습니까?). 저의 초창기 (마지막 밀레니엄 마지막 세기의 3/4 분기)에 재귀 알고리즘을 구현하는 비 재귀 언어를 사용하고 있었으며 아무런 문제가 없었습니다. 그래도 어떻게 증명할 지 잘 모르겠습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.