递归的真实例子

时间:2008-09-19 21:36:12

标签: recursion

除了深度优先搜索(DFS)之外,递归方法是自然解决方案的真实世界问题是什么?

(我不考虑Tower of HanoiFibonacci number或因子现实问题。我脑子里有点做作。)

55 个答案:

答案 0 :(得分:105)

递归的真实例子

A sunflower

答案 1 :(得分:64)

如何处理文件系统中的目录结构。递归查找文件,删除文件,创建目录等

这是一个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 :(得分:44)

Quicksortmerge sort以及大多数其他N-log N排序。

答案 3 :(得分:31)

这里有很多蹩脚的例子,但是你想要一个真实世界的例子,所以有一点想法,这可能是我能提供的最好的:

你发现一个人感染了特定的感染,这是非致命的,并迅速自我修复(A型),除了五分之一的人(我们称之为B型),他们会永久感染它并且没有任何症状,仅仅是一个吊具。

当B型感染多种A型时,这会产生非常恼人的破坏波。

你的任务是追踪所有类型的Bs并免疫它们以阻止疾病的骨干。不幸的是,你不能在全国范围内治疗所有人,因为那些类型的人也对B型治疗方法致命过敏。

你这样做的方式是社交发现,给予感染者(A型),在上周选择所有联系人,在堆上标记每个联系人。当您测试某人受感染时,请将其添加到“跟进”队列中。当一个人是B型时,将它们添加到头部的“跟进”(因为你想要快速停止)。

处理完一个人后,从队列前面选择一个人,并在必要时进行免疫接种。获取以前未访问的所有联系人,然后测试他们是否被感染。

重复直到被感染者的队列变为0,然后等待另一次爆发..

(好吧,这有点迭代,但它是一种解决递归问题的迭代方法,在这种情况下,广泛首次遍历人口基础试图发现可能的问题路径,此外,迭代解决方案往往更快而且更加有效,我强迫性地去除各处的递归,这本身就变得本能了...... ....该死的!)

答案 4 :(得分:16)

Matt Dillard的榜样很好。更一般地,树的任何行走通常可以非常容易地通过递归来处理。例如,编译解析树,遍历XML或HTML等

答案 5 :(得分:16)

递归通常用于Backtracking algorithm的实现。对于“真实世界”的应用,如何Sudoku solver

答案 6 :(得分:13)

只要通过将问题分成子问题就可以解决问题,递归是合适的,可以使用相同的算法来解决问题。树和排序列表上的算法是很自然的。计算几何(和3D游戏)中的许多问题可以使用binary space partitioning(BSP)树,fat subdivisions或其他将世界划分为子部分的方式递归求解。

当您尝试保证算法的正确性时,递归也是合适的。给定一个接受不可变输入的函数并返回一个结果,该结果是对输入的递归和非递归调用的组合,通常很容易使用数学归纳来证明函数是正确的(或不正确)。使用迭代函数或可能变异的输入来执行此操作通常是难以处理的。在处理财务计算和其他正确性非常重要的应用程序时,这非常有用。

答案 7 :(得分:11)

当然很多编译器都会大量使用递归。计算机语言本身具有递归性(即,您可以在其他'if'语句中嵌入'if'语句等。)

答案 8 :(得分: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);
    }
}

答案 9 :(得分:8)

来自SICP

的着名评估/应用周期

alt text
(来源: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))))

以下是apply的定义:

(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

答案 10 :(得分:7)

在BSP树之类的东西中使用递归来进行游戏开发(以及其他类似区域)中的碰撞检测。

答案 11 :(得分:7)

人们经常使用递归方法对文档堆栈进行排序。例如,假设您正在对包含名称的100个文档进行排序。首先将文件放入第一个字母的堆中,然后对每个堆进行排序。

在字典中查找单词通常是通过类似二进制搜索的技术执行的,这是递归的。

在组织中,老板经常向部门主管发出命令,而部门负责人又向管理人员发出命令,等等。

答案 12 :(得分:4)

解析器和编译器可以用递归下降方法编写。这不是最好的方法,因为像lex / yacc这样的工具可以生成更快,更高效的解析器,但在概念上简单易行,所以它们仍然很常见。

答案 13 :(得分:4)

我有一个系统在一些地方使用纯tail recursion来模拟状态机。

答案 14 :(得分:4)

我最近得到的真实世界要求:

要求A:在彻底理解要求A后实施此功能。

答案 15 :(得分:4)

一些很好的递归示例可以在functional programming种语言中找到。在函数式编程语言(ErlangHaskellML / OCaml / F#等)中,任何列表处理都使用递归是很常见的。

在处理典型命令式OOP风格语言中的列表时,将列表实现为链接列表([item1 - > item2 - > item3 - > item4])非常常见。但是,在某些函数式编程语言中,您会发现列表本身是递归实现的,其中列表的“头部”指向列表中的第一个项目,“尾部”指向包含其余项目的列表( [item1 - > [item2 - > [item3 - > [item4 - > []]]]])。我认为这很有创意。

这种列表处理与模式匹配相结合,非常强大。假设我要总结一个数字列表:

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

这基本上说“如果我们用空列表调用,返回0”(允许我们打破递归),否则返回head的值+用其余项调用的Sum值(因此,我们的递归)

例如,我可能有一个URLs列表,我想拆分每个URL链接到的所有网址,然后我减少所有网址的链接总数,以生成“值”一个页面(Google使用PageRank采用的方法,您可以在原始MapReduce文件中找到该方法。您也可以这样做以在文档中生成单词计数。还有很多很多其他的东西。

您可以将此功能模式扩展到任何类型的MapReduce代码,您可以在其中获取某些内容,转换它并返回其他内容(无论是其他列表,还是列表中的某些zip命令)。< / p>

答案 16 :(得分:4)

递归适用于问题(情境),您可以将其分解(缩小)成较小的部分,每个部分看起来与原始问题类似。

包含类似于自身的较小部分的事物的好例子是:

  • 树结构(树枝就像一棵树)
  • 列表(列表的一部分仍然是列表)
  • 容器(俄罗斯娃娃)
  • 序列(序列的一部分看起来像下一个)
  • 对象组(子组仍然是一组对象)

递归是一种技术,可以将问题分解成越来越小的碎片,直到其中一个碎片变小到足以成为一块蛋糕。当然,在你打破它们之后,你必须以正确的顺序将结果“拼接”回来,以形成原始问题的完整解决方案。

一些递归排序算法,树行走算法,map / reduce算法,分而治之都是这种技术的例子。

在计算机编程中,大多数基于堆栈的回调类型语言已经具有内置的递归功能:即

  • 将问题分解为更小的部分==&gt;称自己为原始数据的较小子集,
  • 跟踪片断的划分方式==&gt;调用堆栈,
  • 将结果回缝==&gt;基于堆栈的回报

答案 17 :(得分:3)

分层组织中的反馈循环。

高层老板告诉高层管理人员收集公司每个人的反馈意见。

每位高管都会收集他/她的直接下属,并告诉他们收集直接下属的反馈意见。

然后就行了。

没有直接报告的人 - 树中的叶节点 - 提供反馈。

反馈回到树上,每位经理都加入他/她自己的反馈。

最终,所有反馈都会让它回到顶级老板。

这是一种自然的解决方案,因为递归方法允许在每个级别进行过滤 - 重复项的整理和删除令人反感的反馈。顶级老板可以发送全球电子邮件,并让每位员工直接向他/她报告反馈,但有“你无法处理真相”和“你被解雇”的问题,所以这里的递归效果最好。

答案 18 :(得分:3)

XML,或遍历任何树。虽然,说实话,我几乎从不在工作中使用递归。

答案 19 :(得分:2)

Windows Forms或WebForms(.NET Windows Forms / ASP.NET)中解析控件树。

答案 20 :(得分:2)

在我的工作中,我们有一个具有通用数据结构的系统,可以将其描述为树。这意味着递归是处理数据的一种非常有效的技术。

在没有递归的情况下解决它需要大量不必要的代码。递归的问题在于跟踪发生的事情并不容易。在执行流程时,您必须集中注意力。但是当它工作时,代码优雅而有效。

答案 21 :(得分:2)

您有一个N级深度的组织树。检查了几个节点,并且您只想扩展到已经检查过的那些节点。

这是我实际编码的内容。 它很好,很容易递归。

答案 22 :(得分:2)

假设您正在为网站构建CMS,其中您的网页采用树形结构,并且根目录是主页。

假设您的{user | client | customer | boss}请求您在每个页面上放置一个痕迹痕迹,以显示您在树中的位置。

对于任何给定页面n,您可能希望以递归方式向上走到n的父级及其父级,依此类推,以构建返回到页面树根的节点列表。

当然,在该示例中,您每次都会多次访问数据库,因此您可能希望使用一些SQL别名来查找页表作为a,并将页表再次作为b,并加入a .id与b.parent,所以你让数据库做递归连接。已经有一段时间了,所以我的语法可能没用。

然后,您可能只想计算一次并将其与页面记录一起存储,只有在移动页面时才更新它。这可能会更有效率。

无论如何,这是我的$ .02

答案 23 :(得分:2)

财务/物理的计算,例如复合平均值。

答案 24 :(得分:2)

  • 解析XML文件。
  • 在多维空间中进行高效搜索。 E. g。 2D中的四叉树,3D中的八叉树,kd树等
  • 分层聚类。
  • 想一想,遍历任何层次结构自然有助于递归。
  • C ++中的模板元编程,没有循环和递归是唯一的方法。

答案 25 :(得分:2)

我知道的最好的例子是quicksort,递归时简单得多。看看:

shop.oreilly.com/product/9780596510046.do

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

(点击第3章下的第一个副标题:“我写过的最漂亮的代码”)。

答案 26 :(得分:1)

电话和有线电视公司维护其布线拓扑模型,实际上是一个大型网络或图形。当您想要查找所有父元素或所有子元素时,递归是遍历此模型的一种方法。

由于从处理和内存的角度来看递归是昂贵的,所以此步骤通常仅在更改拓扑并且结果以修改的预先排序的列表格式存储时执行。

答案 27 :(得分:1)

如果没有导致堆栈溢出的实际限制,那么使用迭代的所有内容都会通过递归更自然地完成; - )

但严重的Recursion和Iteration是可以互换的,你可以使用递归重写所有算法来使用迭代,反之亦然。 数学家喜欢递归和程序员喜欢迭代。这也许就是为什么你看到你提到的所有这些人为的例子。 我认为称为数学归纳的数学证明方法有一些问题,为什么数学家喜欢递归。 http://en.wikipedia.org/wiki/Mathematical_induction

答案 28 :(得分:1)

求平均值O(n)的中位数。相当于在n个事物列表中找到第k个最大项目,k = n / 2:

int kthLargest(list,k,first,last){   j =分区(列表,第一个,最后一个)   if(k == j)      返回清单[j]   否则如果(k

在这里,partition选择一个枢轴元素,并在一次传递数据时,重新排列列表,使得小于枢轴的项目首先出现,然后是枢轴,然后是大于枢轴的项目。 “kthLargest”算法与quicksort非常相似,但仅在列表的一侧进行递归。

对我来说,这是最简单的递归算法,其运行速度比迭代算法快。无论k如何,它平均使用2 * n比较。这比运行k遍历数据的天真方法要好得多,每次都找到最小值并丢弃它。

阿莱霍

答案 29 :(得分:1)

归纳推理是概念形成的过程,本质上是递归的。在现实世界中,你的大脑会一直这样做。

答案 30 :(得分:1)

我编写了一个XML解析器,如果没有递归就会更难写。

我想你总是可以使用堆栈+迭代,但有时递归就是如此优雅。

答案 31 :(得分:1)

编写一个功能,将12345.67之类的数字翻译成“一万二千三百四十五美元六十七美分。”

答案 32 :(得分:1)

任何带有树或图形数据结构的程序都可能会有一些递归。

答案 33 :(得分:1)

查找素数的方法是递归的。对于使用大数因子的各种加密方案,可用于生成散列密钥。

答案 34 :(得分: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

答案 35 :(得分:1)

大多数递归对于处理递归数据结构非常自然。这基本上意味着列表结构和树结构。但是,递归也是以某种方式动态创建/树结构的一种很好的自然方式,例如通过分而治之的quicksort或二进制搜索。

我认为你的问题在某种意义上有点误导。深度优先搜索的真实世界是什么?深度优先搜索可以做很多事情。

例如,我想要给出的另一个例子是递归下降编译。在许多现实世界的编译器中使用它已经足够成为现实世界的问题了。但你可以说它是DFS,它基本上是一个深度优先搜索有效的解析树。

答案 36 :(得分:1)

同样关于编译器的评论。抽象语法树节点自然适合递归。所有递归数据结构(链表,树,图等)也可以通过递归更轻松地处理。我确实认为,由于现实问题的类型,我们大多数人一旦离开学校就不会使用递归,但最好将其作为一种选择。

答案 37 :(得分:0)

你有一幢建筑物。 该建筑有20个房间。 从法律上讲,每个房间只能容纳一定数量的人。 你的工作是自动将人分配到一个房间。如果我的房间满了,你需要找一个可用的房间。 鉴于只有某些房间可以容纳某些人,你还需要小心哪个房间。

例如:

房间1,2,3可以互相滚动。这个房间适合那些不能独自行走的孩子,所以你希望他们远离其他一切,以避免分心和其他疾病(这对老年人来说不是一件好事,但对于6mo,它可能变得非常糟糕。三个人都满了,必须拒绝这个人进入。

第4,5,6房间可以互相滚动。这个房间适合对花生过敏的人,因此他们不能进入其他房间(可能有花生的东西)。如果所有三个人都满了,请提出警告,询问他们的过敏程度和perahsp,他们可以获准进入。

在任何时候,房间都可以改变。所以你可以允许7-14号房间成为无花生的房间。你不知道要检查多少房间。

或者,也许你想根据年龄分开。等级,性别等 这些只是我参加的几个例子。

答案 38 :(得分:0)

由于你似乎不喜欢计算机科学或肮脏的例子,这里有一个不同的:电线拼图。

许多电线谜题涉及通过在线环内外工作来移除长的闭合线圈。这些谜题是递归的。 其中之一被称为“箭头动力学”。我起诉你可以找到它,如果你google为“箭头动力学线拼图”

这些谜题很像河内的塔楼。

答案 39 :(得分:0)

  1. 大学储蓄计划:让A(n)= n个月后为大学储蓄的金额 A(0)= $ 500 每个月,50美元存入一个年利率为5%的账户。
  2. 然后A(n) = A(n-1) + 50 + 0.05*(1/12)* A(N-1)

答案 40 :(得分:0)

答案 41 :(得分:0)

如果你有两个不同但相似的序列,并且想要匹配每个序列的组件,以便首先接受相同的序列顺序,那么你可以递归地分析这些序列以形成树,然后递归地处理树把它弄平。

参考: Recursion & Memoization Example Code

答案 42 :(得分:0)

我们使用它们来进行SQL路径查找。

我还要说调试很费劲,对于一个可怜的程序员来说,搞砸它是非常容易的。

答案 43 :(得分:0)

递归是一种非常基本的编程技术,它适用于许多问题,列出它们就像列出所有可以通过添加某种类型来解决的问题。通过我的项目Euler的Lisp解决方案,我发现:一个交叉总函数,一个数字匹配函数,几个搜索空格的函数,一个最小的文本解析器,一个将数字拆分成十进制数字列表的函数,一个函数构造图形和遍历输入文件的函数。

问题在于,如今不是大多数主流编程语言都没有尾调用优化,因此深度递归对它们来说是不可行的。这种不足意味着大多数程序员不得不忘记这种自然的思维方式,而是依赖于其他可以说不那么优雅的循环结构。

答案 44 :(得分:0)

通过递归解决的“真实世界”问题将是嵌套玩偶。你的功能是OpenDoll()。

考虑到它们的叠加,你会递归打开玩偶,如果愿意,可以打电话给OpenDoll(),直到你到达最里面的玩偶。

答案 45 :(得分:0)

我拥有的最后一个真实世界的例子非常轻浮,但它有时会证明递归“恰好适合”。

我使用的是责任链模式,因此Handler对象要么自己处理请求,要么将其委托给链。记录链的构造很有用:

public String getChainString() {
    cs = this.getClass().toString();
    if(this.delegate != null) {
        cs += "->" + delegate.getChainString();
    }
    return cs;
}

你可能会认为这不是最纯粹的递归,因为虽然该方法调用“本身”,但每次调用它时它都处于不同的实例中。

答案 46 :(得分:0)

我认为这真的取决于语言。在某些语言中,Lisp例如,递归通常是对问题的自然响应(通常使用语言,在这种情况下,编译器已经过优化以处理递归)。

Lisp中对列表的第一个元素执行操作然后在列表的其余部分调用函数以便累积值或新列表的常见模式非常优雅且最自然的方式那种语言很多东西。在Java中,没有那么多。

答案 47 :(得分:0)

我刚写了一个递归函数来确定是否需要使用DataContractSerializer序列化类。最大的问题来自模板/泛型,其中一个类可以包含需要进行数据处理序列化的其他类型...所以它会遍历每个类型,如果它不是datacontractserializable检查它的类型。

答案 48 :(得分:0)

使用亚音速从数据库表生成树结构化菜单的方法。

public MenuElement(BHSSiteMap node, string role)
    {
        if (CheckRole(node, role))
        {
            ParentNode = node;

            // get site map collection order by sequence
            BHSSiteMapCollection children = new BHSSiteMapCollection();

            Query q = BHSSiteMap.CreateQuery()
                    .WHERE(BHSSiteMap.Columns.Parent, Comparison.Equals, ParentNode.Id)
                    .ORDER_BY(BHSSiteMap.Columns.Sequence, "ASC");

            children.LoadAndCloseReader(q.ExecuteReader());

            if (children.Count > 0)
            {
                ChildNodes = new List<MenuElement>();

                foreach (BHSSiteMap child in children)
                {
                    MenuElement childME = new MenuElement(child, role);
                    ChildNodes.Add(childME);
                }
            }
        }
    }

答案 49 :(得分:0)

我在C#中编写了一个树来处理一个表上的查询,该表具有默认情况下的6段密钥(如果key [0]不存在,则使用默认情况并继续)。查找是递归完成的。我尝试了一本字典词典(等)字典,它很快就变得太复杂了。

我还在C#中编写了一个公式求值程序,用于评估存储在树中的公式以使评估顺序正确。虽然这可能是为问题选择不正确的语言,但这是一个有趣的练习。

我没有看到很多人做过的例子,而是他们使用过的图书馆。希望这能让你有所思考。

答案 50 :(得分:0)

检查创建的图像是否适用于大小限制框。

function check_size($font_size, $font, $text, $width, $height) {
        if (!is_string($text)) {
            throw new Exception('Invalid type for $text');
        }   
        $box = imagettfbbox($font_size, 0, $font, $text);
        $box['width'] = abs($box[2] - $box[0]);
        if ($box[0] < -1) {
            $box['width'] = abs($box[2]) + abs($box[0]) - 1;
        }   
        $box['height'] = abs($box[7]) - abs($box[1]);
        if ($box[3] > 0) {
            $box['height'] = abs($box[7] - abs($box[1])) - 1;
        }   
        return ($box['height'] < $height && $box['width'] < $width) ? array($font_size, $box['width'], $height) : $this->check_size($font_size - 1, $font, $text, $width, $height);
    }

答案 51 :(得分:0)

GIS或制图的几何计算,例如找到圆的边缘。

答案 52 :(得分:0)

查找平方根的方法是递归的。用于计算现实世界中的距离。

答案 53 :(得分:-2)

河内的塔楼

以下是您可以与之互动的内容:http://www.mazeworks.com/hanoi/

  

使用递归关系,此解决方案所需的确切移动次数可以通过以下公式计算:2h - 1.通过注意步骤1和3采取Th - 1移动获得此结果,并且步骤2采取一次移动, Th = 2Th - 1 + 1。   请参阅:http://en.wikipedia.org/wiki/Towers_of_hanoi#Recursive_solution

答案 54 :(得分:-3)

间接递归的一个真实世界的例子是询问你的父母你是否可以拥有圣诞节的视频游戏。爸爸:“问妈妈。”妈妈:“问爸爸。” [简而言之,“不,但我们不想告诉你,以免发脾气。”]