재귀의 실제 사례 [닫힘]


97

재귀 적 접근 방식이 DFS (Depth-First Search) 외에 자연스러운 솔루션 인 실제 문제 는 무엇입니까 ?

( 하노이의 탑 , 피보나치 수 또는 계승 현실 세계 문제 고려하지 않습니다 . 그것들은 제 마음에 약간 인위적입니다.)


2
모든 제안에 감사하지만 모두가 트리 / 네트워크 순회를 제안합니다. 이것들은 본질적으로 Depth-First-Search (또는 BFS)의 모든 예입니다. 다른 동기 부여 알고리즘 / 문제를 찾고있었습니다.
redfood

10
이 질문이 좋아요! "기술 X의 주요 실제 사용을 제외하고 기술 X의 모든 사용을 말해주세요."
Justin Standard

1
나는 항상 재귀를 사용하지만 일반적으로 수학과 그래픽에 대해 사용합니다. 프로그래머가 아닌 사람들에게 의미있는 재귀의 예를 찾으려고합니다.
redfood

6
나만의 모험 소설을 선택하세요! 나는 모든 것을 읽고 싶다. 그리고 재귀는 그렇게하는 가장 좋은 방법이다.
Andres

현실 세계에는 재귀가 없습니다. 재귀는 수학적 추상화입니다. 재귀를 사용하여 많은 것을 모델링 할 수 있습니다. 그런 의미에서 피보나치는 이런 방식으로 모델링 할 수있는 실제 문제가 꽤 있기 때문에 절대적으로 현실 세계입니다. 피보나치가 실제 세계가 아니라고 생각한다면 다른 모든 예는 실제 세계의 예가 아니라 추상화라고 주장 할 것입니다.
Zane

답변:


41

여기에는 많은 수학적 예제가 있지만 실제 예제 를 원했기 때문에 약간의 생각으로 이것이 아마도 제가 제공 할 수있는 최선의 방법 일 것입니다.

치명적이지 않고 신속하게 스스로 고치는 특정 연속 감염에 걸린 사람을 찾습니다 (유형 A) 증상을 나타내며 단순히 스프레더 역할을합니다.

이것은 B 형이 A 형의 다수를 감염시킬 때 매우 성가신 혼란을 야기합니다.

당신의 임무는 모든 유형 B를 추적하고 질병의 중추를 막기 위해 면역화하는 것입니다. 불행히도, 당신은 모든 사람에게 전국적인 치료법을 투여 할 수 없습니다.

이 작업을 수행하는 방법은 감염된 사람 (유형 A)이 주어진 경우, 지난주에 모든 연락처를 선택하여 각 연락처를 힙에 표시하는 사회적 발견입니다. 사람이 감염되었는지 테스트 할 때 "후속 조치"대기열에 추가하십시오. 사람이 B 형인 경우 머리의 "후속 조치"에 추가합니다 (이 빠르게 중지하고 싶기 때문에).

지정된 사람을 처리 한 후 대기열에서 사람을 선택하고 필요한 경우 예방 접종을 적용합니다. 이전에 방문하지 않은 모든 연락처를 가져온 다음 감염되었는지 테스트하십시오.

감염된 사람의 대기열이 0이 될 때까지 반복 한 다음 또 다른 발병을 기다립니다.

(좋아, 이것은 약간 반복적이지만 반복적 인 문제를 해결하는 반복적 인 방법입니다.이 경우 문제에 대한 가능성있는 경로를 찾으려고하는 인구 기반의 폭 우선 순회는 물론 반복적 인 솔루션이 종종 더 빠르고 더 효과적입니다. , 그리고 나는 모든 곳에서 재귀를 강제로 제거하여 본능적으로 변합니다. .... 젠장!)


2
감사합니다. 이것은 여전히 ​​그래프 순회이지만 동기가 부여되어 프로그래머가 아닌 사람들에게 의미가 있습니다.
redfood

환자 0을 찾는 것이 더 좋은 예라고 생각합니다. 감염을 일으킬 수있는 모든 상호 작용을 확인합니다. 더 전염성이 발견되지 않을 때까지 상호 작용의 시간에 전염성이 있었다 모든 관련에 반복
윌리엄 피츠 패트릭

4
이 실제 사례는 이제 매우 친숙한 느낌입니다. (
haroldolivieri

109

재귀의 실제 사례

해바라기


12
매트릭스 설계자에 의해 재귀로 코딩 됨 :)
Marcel

3
재귀는 어때? 물론입니다. 하지만 재귀? 프랙탈 양배추는 잘 작동했지만이 꽃에서 자기 유사성은 보이지 않습니다.
Clément

1
뺨에 약간 혀가 있지만 일반적으로 재귀를 통해 구현되는 피보나치 수열로 설명 할 수있는 phyllotaxis의 예입니다.
Hans Sjunnesson

1
"일반적으로 재귀를 통해 구현된다"는 것이 반드시 꽃이 그렇게한다는 것을 의미하지는 않습니다. 아마도 그렇습니다. 분자 생물학 자로는 충분 하지 않지만 그것이 어떻게 작용하는지에 대한 설명이 없이는 이것이 특별히 도움이되지 않는다고 생각합니다. 반대 투표. 설명 (생물학적으로 정확한지 여부에 관계없이 통찰력을 제공 할 수 있음)을 추가하고 싶다면 기꺼이 투표를 재검토하겠습니다.
lindes jul.

65

파일 시스템의 디렉토리 구조와 관련된 모든 것은 어떻습니까? 재귀 적으로 파일 찾기, 파일 삭제, 디렉토리 생성 등

다음은 디렉토리 및 하위 디렉토리의 내용을 재귀 적으로 인쇄하는 Java 구현입니다.

import java.io.File;

public class DirectoryContentAnalyserOne implements DirectoryContentAnalyser {

    private static StringBuilder indentation = new StringBuilder();

    public static void main (String args [] ){
        // Here you pass the path to the directory to be scanned
        getDirectoryContent("C:\\DirOne\\DirTwo\\AndSoOn");
    }

    private static void getDirectoryContent(String filePath) {

        File currentDirOrFile = new File(filePath);

        if ( !currentDirOrFile.exists() ){
            return;
        }
        else if ( currentDirOrFile.isFile() ){
            System.out.println(indentation + currentDirOrFile.getName());
            return;
        }
        else{
            System.out.println("\n" + indentation + "|_" +currentDirOrFile.getName());
            indentation.append("   ");

            for ( String currentFileOrDirName : currentDirOrFile.list()){
                getPrivateDirectoryContent(currentDirOrFile + "\\" + currentFileOrDirName);
            }

            if (indentation.length() - 3 > 3 ){
                indentation.delete(indentation.length() - 3, indentation.length());
            }
        }       
    }

}

2
파일 시스템은 동기 부여를 제공하지만 (좋습니다. 감사합니다) 이것은 DFS의 구체적인 예입니다.
redfood

4
"DFS"라는 두문자어를받지 못했습니다. 교실에 앉은 지 오래되었습니다.
Matt Dillard

5
깊이 우선 검색 : dfs (node) {foreach child in node {visit (child); }}
Haoest

간단한 코드 예제는 다음을 참조하십시오. stackoverflow.com/questions/126756/…
Jonik

이 코드에 오류가 있습니까? getPrivateDirectoryContent ()를 getDirectoryContent ()로 바꿔야합니까?
Shn_Android_Dev


16

Matt Dillard의 예가 좋습니다. 더 일반적으로, 나무를 걷는 것은 일반적으로 재귀에 의해 매우 쉽게 처리 될 수 있습니다. 예를 들어, 구문 분석 트리 컴파일, XML 또는 HTML 위의 걷기 등이 있습니다.


나는이 "컴파일 파싱 트리"가 합리적인 대답이라고 생각한다. 이것은 트리를 포함하지만 여전히 검색 문제는 아니다. 언어의 편집이나 해석에 대한 일반적인 개념으로 일반화 될 수 있습니다. 예를 들어 영어와 같은 자연어를 "해석"(이해, 듣기) 할 수도 있습니다.
imz-Ivan Zakharyaschev


13

재귀는 문제를 해결하는 데 동일한 알고리즘을 사용할 수있는 하위 문제로 나누어 문제를 해결할 수있을 때마다 적절합니다. 트리와 정렬 된 목록에 대한 알고리즘은 자연스럽게 적합합니다. 계산 기하학 (및 3D 게임)의 많은 문제는 바이너리 공간 분할 (BSP) 트리, 지방 분할 또는 세계를 하위 부분으로 나누는 다른 방법을 사용하여 재귀 적으로 해결할 수 있습니다 .

알고리즘의 정확성을 보장하려는 경우에도 재귀가 적합합니다. 불변 입력을 받고 입력에 대한 재귀 호출과 비 재귀 호출의 조합 인 결과를 반환하는 함수가 주어지면 일반적으로 수학적 유도를 사용하여 함수가 올바른지 (또는 그렇지 않은지) 쉽게 증명할 수 있습니다. 반복 함수 나 변이 할 수있는 입력으로이 작업을 수행하는 것은 종종 다루기 어렵습니다. 이는 재무 계산 및 정확성이 매우 중요한 기타 응용 프로그램을 처리 할 때 유용 할 수 있습니다.


11

확실히 많은 컴파일러가 재귀를 많이 사용합니다. 컴퓨터 언어는 본질적으로 재귀 적입니다 (즉, 다른 'if'문 등에 'if'문을 포함 할 수 있습니다).


재귀가 아닌 if 문이 포함됩니다.
John Meagher

그러나 그것들을 파싱하려면 재귀가 필요합니다, John.
Apocalisp

2
John, if 문을 중첩 할 수 있다는 사실은 언어 정의 (및 언어 파서)가 재귀적임을 의미합니다.
Derek Park

재귀 하강은 컴파일러를 직접 코딩하는 가장 쉬운 방법 중 하나입니다. yacc와 같은 도구를 사용하는 것만 큼 쉽지는 않지만 작동 방식을 이해하기 쉽습니다. 전체 테이블 기반 상태 머신을 설명 할 수 있지만 일반적으로 블랙 박스가됩니다.
Eclipse

"파싱 트리 컴파일"을 언급 한 Cody Brocious의 답변은 언어 분석 / 해석 / 컴파일이라는 영역도 지적했습니다.
imz-Ivan Zakharyaschev

9

컨테이너 컨트롤의 모든 자식 컨트롤에 대해 읽기 전용을 비활성화 / 설정합니다. 일부 자식 컨트롤이 컨테이너 자체이기 때문에이 작업을 수행해야했습니다.

public static void SetReadOnly(Control ctrl, bool readOnly)
{
    //set the control read only
    SetControlReadOnly(ctrl, readOnly);

    if (ctrl.Controls != null && ctrl.Controls.Count > 0)
    {
        //recursively loop through all child controls
        foreach (Control c in ctrl.Controls)
            SetReadOnly(c, readOnly);
    }
}

8

SICP의 유명한 평가 / 적용주기

대체 텍스트
(출처 : mit.edu )

eval의 정의는 다음과 같습니다.

(define (eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((quoted? exp) (text-of-quotation exp))
        ((assignment? exp) (eval-assignment exp env))
        ((definition? exp) (eval-definition exp env))
        ((if? exp) (eval-if exp env))
        ((lambda? exp)
         (make-procedure (lambda-parameters exp)
                         (lambda-body exp)
                         env))
        ((begin? exp) 
         (eval-sequence (begin-actions exp) env))
        ((cond? exp) (eval (cond->if exp) env))
        ((application? exp)
         (apply (eval (operator exp) env)
                (list-of-values (operands exp) env)))
        (else
         (error "Unknown expression type - EVAL" exp))))

적용의 정의는 다음과 같습니다.

(define (apply procedure arguments)
  (cond ((primitive-procedure? procedure)
         (apply-primitive-procedure procedure arguments))
        ((compound-procedure? procedure)
         (eval-sequence
           (procedure-body procedure)
           (extend-environment
             (procedure-parameters procedure)
             arguments
             (procedure-environment procedure))))
        (else
         (error
          "Unknown procedure type - APPLY" procedure))))

다음은 eval-sequence의 정의입니다.

(define (eval-sequence exps env)
  (cond ((last-exp? exps) (eval (first-exp exps) env))
        (else (eval (first-exp exps) env)
              (eval-sequence (rest-exps exps) env))))

eval-> apply-> eval-sequence->eval


7

재귀는 게임 개발 (및 기타 유사한 영역)에서 충돌 감지를 위해 BSP 트리와 같은 것에서 사용됩니다.


7

사람들은 종종 재귀 적 방법을 사용하여 문서 스택을 정렬합니다. 예를 들어 이름이있는 문서 100 개를 정렬한다고 가정 해보십시오. 먼저 첫 글자를 기준으로 문서를 더미에 배치 한 다음 각 더미를 정렬합니다.

사전에서 단어를 찾는 것은 종종 반복적 인 이진 검색과 유사한 기술로 수행됩니다.

조직에서 상사는 종종 부서장에게 명령을 내리고 차례로 관리자에게 명령을 내리는 식입니다.


5

최근에 얻은 실제 요구 사항 :

요구 사항 A : 요구 사항 A를 완전히 이해 한 후이 기능을 구현하십시오.


4

파서와 컴파일러는 재귀 하강 방식으로 작성 될 수 있습니다. lex / yacc와 같은 도구는 더 빠르고 효율적인 파서를 생성하므로이를 수행하는 가장 좋은 방법은 아니지만 개념적으로 간단하고 구현하기 쉬우므로 일반적으로 유지됩니다.


4

재귀는 문제 (상황)에 적용되어 더 작은 부분으로 분해 (축소) 할 수 있으며 각 부분은 원래 문제와 비슷해 보입니다.

자신과 유사한 작은 부분을 포함하는 항목의 좋은 예는 다음과 같습니다.

  • 트리 구조 (가지는 나무와 유사 함)
  • 목록 (목록의 일부는 여전히 목록 임)
  • 용기 (러시아 인형)
  • 시퀀스 (시퀀스의 일부는 다음과 유사 함)
  • 개체 그룹 (하위 그룹은 여전히 ​​개체 그룹 임)

재귀는 그 조각 중 하나가 케이크 조각이 될만큼 충분히 작아 질 때까지 문제를 더 작고 작은 조각으로 계속 나누는 기술입니다. 물론, 그것들을 분리 한 후에는 원래 문제의 전체적인 해결책을 형성하기 위해 올바른 순서로 결과를 다시 결합해야합니다.

일부 재귀 정렬 알고리즘, 트리 워킹 알고리즘, 맵 / 리 듀스 알고리즘, 분할 및 정복이 모두이 기술의 예입니다.

컴퓨터 프로그래밍에서 대부분의 스택 기반 호출 반환 유형 언어에는 이미 재귀 기능이 내장되어 있습니다.

  • 문제를 더 작은 조각으로 나누기 ==> 원래 데이터의 더 작은 하위 집합에 대해 자신을 호출),
  • 조각이 어떻게 분할되는지 추적 ==> 호출 스택,
  • 결과를 다시 연결 ==> 스택 기반 수익


4

재귀의 몇 가지 훌륭한 예는 함수형 프로그래밍 언어 에서 찾을 수 있습니다. 함수형 프로그래밍 언어 ( Erlang , Haskell , ML / OCaml / F # 등)에서는 모든 목록 처리에서 재귀를 사용하는 것이 매우 일반적입니다.

일반적인 명령형 OOP 스타일 언어의 목록을 다룰 때 연결 목록으로 구현 된 목록을 보는 것은 매우 일반적입니다 ([item1-> item2-> item3-> item4]). 그러나 일부 함수형 프로그래밍 언어에서는 목록 자체가 재귀 적으로 구현됩니다. 여기서 목록의 "머리"는 목록의 첫 번째 항목을 가리키고 "꼬리"는 나머지 항목을 포함하는 목록을 가리 킵니다 ( [항목 1-> [항목 2-> [항목 3-> [항목 4-> []]]]]). 제 생각에는 꽤 창의적입니다.

이러한 목록 처리는 패턴 일치와 결합 될 때 매우 강력합니다. 숫자 목록을 합산하고 싶다고 가정 해 보겠습니다.

let rec Sum numbers =
    match numbers with
    | [] -> 0
    | head::tail -> head + Sum tail

이것은 본질적으로 "빈 목록으로 호출 된 경우 0을 반환"(재귀를 중단 할 수 있음)하고, 그렇지 않으면 head 값 + 나머지 항목과 함께 호출 된 Sum 값 (따라서 재귀)을 반환합니다.

예를 들어 URL 목록이있을 수 있고 각 URL이 연결되는 모든 URL을 분리 한 다음 모든 URL과의 총 링크 수를 줄여 페이지에 대한 '값'을 생성합니다 (Google PageRank 와 함께 가져 오고 원본 MapReduce 문서에 정의 된 것을 찾을 수 있습니다 .) 이렇게하면 문서에서 단어 수를 생성 할 수도 있습니다. 그리고 아주 많은 것들이 있습니다.

이 기능 패턴을 모든 유형의 MapReduce 코드로 확장 할 수 있습니다. 여기서 목록을 가져 와서 변환하고 다른 것을 반환 할 수 있습니다 (다른 목록 또는 목록의 일부 zip 명령).


3

XML 또는 트리 인 모든 것을 순회합니다. 솔직히 말해서 나는 일에서 재귀를 거의 사용하지 않습니다.


꼬리 재귀조차도?
Apocalisp

나는 내 경력에서 한 번 재귀를 사용했으며 프레임 워크가 변경되면 재귀를 제거했습니다. 우리가하는 일의 80 %는 CRUD입니다.
Charles Graham

1
처음에 "XML"을 언급하는 것은 매우 이상합니다. 그것은 자연스럽지 않습니다. 당신이 가르치려는 평범한 사람이 일상 생활에서 다루어야하는 것이 아닙니다. 그러나 그 아이디어는 물론 상당히 합리적입니다.
imz-Ivan Zakharyaschev 2011 년

3

계층 적 조직의 피드백 루프.

최고 상사는 최고 경영진에게 회사의 모든 사람으로부터 피드백을 수집하라고 말합니다.

각 임원은 부하 직원을 수집하고 부하 직원으로부터 피드백을 수집하도록 지시합니다.

그리고 아래로.

직접보고가없는 사람 (트리의 리프 노드)은 피드백을 제공합니다.

피드백은 각 관리자가 자신의 피드백을 추가하는 트리 위로 이동합니다.

결국 모든 피드백은 최고 보스에게 다시 돌아갑니다.

재귀 적 방법은 각 수준에서 필터링을 허용하기 때문에 자연스러운 해결책입니다. 최고 보스 수있는 글로벌 이메일을 보내 직접 다시 그 / 그녀에게 각 직원의 보고서 의견이 있지만, 거기에 재귀 가장 여기서 일하는 때문에, 그리고 "당신은 해고 야"문제 "당신이 진실을 처리 할 수 없습니다."


2

웹 사이트에 대한 CMS를 구축하고 있다고 가정합니다. 여기서 페이지는 트리 구조로되어 있으며 루트는 홈페이지입니다.

또한 당신의 {user | client | customer | boss}가 당신이 트리에서 당신이 어디에 있는지 보여주기 위해 모든 페이지에 이동 경로를 배치하도록 요청했다고 가정 해보자.

주어진 페이지 n에 대해, 페이지 트리의 루트로 백업되는 노드 목록을 재귀 적으로 작성하기 위해 n의 부모와 그 부모 등으로 걸어 갈 수 있습니다.

물론이 예제에서는 페이지 당 여러 번 db를 치고 있으므로 페이지 테이블을 a로, 페이지 테이블을 다시 b로 조회하고 a.id를 조인하는 SQL 별칭을 사용할 수 있습니다. b. parent 그래서 데이터베이스가 재귀 조인을 수행하도록합니다. 오랜만에 구문이 도움이되지 않을 것입니다.

그런 다음 다시 한 번만 계산하여 페이지 레코드와 함께 저장하고 페이지를 이동하는 경우에만 업데이트 할 수 있습니다. 아마도 더 효율적일 것입니다.

어쨌든 내 $ .02


2

N 레벨 깊이의 조직 트리가 있습니다. 여러 노드가 확인되었으며 확인 된 노드로만 확장하려고합니다.

이것은 제가 실제로 코딩 한 것입니다. 재귀와 함께 멋지고 쉽습니다.


2

제 직업에는 트리로 설명 할 수있는 일반적인 데이터 구조를 가진 시스템이 있습니다. 이는 재귀가 데이터 작업에 매우 효과적인 기술임을 의미합니다.

재귀없이 해결하려면 불필요한 코드가 많이 필요합니다. 재귀의 문제는 무슨 일이 일어나는지 따라 가기가 쉽지 않다는 것입니다. 실행의 흐름을 따를 때 정말 집중해야합니다. 그러나 작동 할 때 코드는 우아하고 효과적입니다.



2
  • XML 파일을 구문 분석 합니다.
  • 다차원 공간에서 효율적인 검색. 예. 2D의 쿼드 트리, 3D의 옥트 트리, kd- 트리 등
  • 계층 적 클러스터링.
  • 생각해 보면 모든 계층 구조를 순회하는 것은 자연스럽게 재귀에 도움이됩니다.
  • 루프가없고 재귀가 유일한 방법 인 C ++의 템플릿 메타 프로그래밍입니다.

"XML"은이 답변의 아이디어에 필수적인 것은 아닙니다 (특히 XML을 언급하는 것은 당신이 가르치는 사람들을 역겹거나 지루할 수 있습니다). 일반적인 언어 (컴퓨터 언어 또는 자연 언어) 만 재귀 구문 분석 문제의 예가됩니다.
imz-Ivan Zakharyaschev

포스터는 "재귀 적 접근이 자연스러운 해결책 인 실제 문제"를 요구했습니다. xml 파일을 구문 분석하는 것은 확실히 실제 문제이며 자연스럽게 재귀에 적합합니다. XML에 대해 이상한 혐오감을 느끼는 것 같다는 사실이 XML이 널리 사용된다는 사실을 바꾸지는 않습니다.
Dima


2

내가 아는 가장 좋은 예는 quicksort 이며 재귀를 사용하면 훨씬 간단합니다. 보세요:

shop.oreilly.com/product/9780596510046.do

www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047

(3 장 : "내가 작성한 가장 아름다운 코드"아래의 첫 번째 부제목을 클릭하십시오.)


1
MergeSort도 재귀를 사용하면 더 간단합니다.
Matthew Schinckel

1
링크가 끊어졌습니다. 책 제목을 추가 할 수 있습니까?
Peter Mortensen

@PeterMortensen, 그 책은 Greg Wilson과 Andy Oram의 Beautiful Code입니다. O'Reilly가 더 이상 내부를 들여다 볼 수없는 것 같지만 링크를 업데이트했습니다. 그러나 Amazon을 살펴볼 수 있습니다.
Fabio Ceconello 2014 년

1

전화 및 케이블 회사는 사실상 대규모 네트워크 또는 그래프 인 배선 토폴로지 모델을 유지합니다. 재귀는 모든 부모 또는 모든 자식 요소를 찾으려고 할 때이 모델을 순회하는 한 가지 방법입니다.

재귀는 처리 및 메모리 관점에서 비용이 많이 들기 때문에이 단계는 일반적으로 토폴로지가 변경되고 결과가 수정 된 사전 주문 목록 형식으로 저장 될 때만 수행됩니다.


1

개념 형성 과정 인 귀납적 추론은 본질적으로 재귀 적입니다. 당신의 두뇌는 현실 세계에서 항상 그것을합니다.


1

컴파일러에 대한 의견도 마찬가지입니다. 추상 구문 트리 노드는 자연스럽게 재귀에 적합합니다. 모든 재귀 데이터 구조 (연결된 목록, 트리, 그래프 등)도 재귀를 사용하여 더 쉽게 처리됩니다. 우리 대부분은 실제 문제의 유형으로 인해 학교를 졸업하면 재귀를 많이 사용하지 않는다고 생각하지만 옵션으로 인식하는 것이 좋습니다.


1

자연수의 곱셈은 재귀의 실제 예입니다.

To multiply x by y
  if x is 0
    the answer is 0
  if x is 1
    the answer is y
  otherwise
    multiply x - 1 by y, and add x
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.