发布于 2026-01-06 2 阅读
0

递归的基本原理

递归的基本原理

Se pra você:

  • Recursão é um tema obscuro ou quer entender mais um pouco sobre;
  • 尾部呼叫 TCO以便与外星人进行通信;
  • 蹦床和补救措施

Então este artigo é pra você。

您可以通过Ruby示例来解释说明形式和解决问题的术语。我们不关心示例,因为它是简单的,因为它是一种不可知论的语言

Portanto,venha comigo nesta viagem Interminável


Para Continuar, volte ao topo do post


议程


O que é recursão

在计算机程序中,人们习惯于解决一些重大问题,因为这些问题都是我们使用的功能或方法。

递归是一种极其简单的计算技术,它解决了确定函数和递归执行问题的问题。

这是一个“chama a si mesma”功能,用于解析器计算和连续执行。


Fibo para os íntimos

典型的递归和描述的例子,斐波那契数列,或斐波那契数列,都与确定的位置有关。

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55.........
Enter fullscreen mode Exit fullscreen mode

这是一个有趣结果:

fib(0) = 0
fib(1) = 1
fib(2) = 1
...
fib(7) = 13
fib(10) = 55
Enter fullscreen mode Exit fullscreen mode

在 Ruby 中可以实现递归的方法:

def fib(position)
  return position if position < 2

  fib(position - 1) + fib(position - 2)
end
Enter fullscreen mode Exit fullscreen mode

Este código, entretanto, não é Performático。总线上的编号为 10000 (dez mil) na sequencia, o 程序中的多个缓慢点会导致递归冗余

                 fib(10)
             /                \
     fib(9)                 fib(8)
        /          \          /   \
fib(8)     fib(7)     fib(7)    fib(6)
  /      \       /       \       /   \
fib(7) fib(6) fib(6) fib(5) fib(6) fib(5)
   /    \     /     \     /     \     /    \
fib(6) fib(5) fib(5) fib(4) fib(5) fib(4) fib(5) fib(4)
  /   \   /   \   /   \   /   \   /   \   /   \   /   \
...
Enter fullscreen mode Exit fullscreen mode

结果,主要的输入功能、执行速度都趋向于指数形式的渐进,而不是Big-O系列O(2^n)

大O指数

可能会减少复杂性吗?

您是否可以在函数中应用技术,或者在递归中使用 apenas uma chamada 递归,或者实现辅助计算?

该技术存在尾调用尾递归


尾部呼叫

尾调用TC是由递归函数和最后的递归函数以及附加计算函数组成的。

这是一个复杂的指数对线性复数,它是简单的循环迭代和输入列表。

Big-O 不是输入的O(n)线性伴奏或渐强的复合体。

大O线性

Ruby示例:

def fib(position, _current = 0, _next = 1)
  return _current if position < 1

  fib(position - 1, _next, _current + _next)
end
Enter fullscreen mode Exit fullscreen mode

Portanto,或递归和简化算法的数字:

fib(10, 0, 1)
fib(9, 1, 1)
fib(8, 1, 2)
fib(7, 2, 3)
fib(6, 3, 5)
fib(5, 5, 8)
fib(4, 8, 13)
fib(3, 13, 21)
fib(2, 21, 34)
fib(1, 34, 55)
fib(0, 55, 89)
Enter fullscreen mode Exit fullscreen mode

修复递归的数字,或者重新排序,然后再线性地进行删除。

阿西姆,在TC 的罗达或 Fib com程序上,罗达 SEM TC 的执行速度和指数,速度很快


Claramente um programa que leva tempo exponencial é péssimo em termos de Performance,não?

# Sem TC
fib(30) # 0.75 segundos

# Com TC
fib(30) # 0.000075 segundos
Enter fullscreen mode Exit fullscreen mode

Voltando ao exemplo de fib(10000), ao rodar com TC, vemos que a execução é muito mais rápida, porém:

recursion/fib.rb:10:in `fib_tc': stack level too deep (SystemStackError)
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
        from recursion/fib.rb:10:in `fib_tc'
Enter fullscreen mode Exit fullscreen mode

糟糕堆栈溢出!

在此情况下,首先会发生堆栈堆栈溢出


堆栈溢出

执行程序时,会存储数据格式堆栈的内存,并使用数据保护程序发送数据并使用执行功能。


Há também outra estrutura na programa chamada Heap,que não é uma pilha e tem outras características que vão além do escopo deste artigo。 Para entender recursão, focamos apenas na stack

在功能或方法中使用程序,将其放入(推送)堆栈中。结束一个功能,并通过 cada bado进行远程(流行)操作

堆

一个新的堆栈框架的功能。如果在终端上递归,则运行时将精确地“弹出”并完成框架,然后进入新的堆栈框架,并在堆栈中添加一些元素。

是否有一些关于如何将计算机内存中的超级密码限制的信息

Sim,了解堆栈溢出💥🪲,它解释了 Ruby 和 10000 com 尾调用的错误。


10000 的计算是否存在递归解析器无法解决的问题?

冷静,语言的使用技术包括使用 chamada TC com apenas um 堆栈框架,并确保 cada recursiva seja tratada como se fosse uma iteração numloop primitivo。

这是一个关于堆栈框架的参数和功能的操作,可在原始循环中执行任务。结果,新的查马达斯递归地从尾部递归到皮拉的挑衅者。

尾部呼叫优化TCO 的最新技术


尾部调用优化

Devido a sua natureza imperativa, e assim como diversas outras linguagens de propósito geral, Ruby não traz supporte nativo a TCO .

总体而言,它的功能是与语言相关的,但它不是命令式的,而是范式功能的。

通过Ruby 和 Ruby 运行时 (YARV) 指令的简单配置,可以实现 TCO 模式,并且可以执行 10000 个扫描周期。

RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}

def fib(position, _current = 0, _next = 1)
  return _current if position < 1

  fib(position - 1, _next, _current + _next)
end

# TC com TCO
fib(10000) # 0.02 segundos
Enter fullscreen mode Exit fullscreen mode

太棒了! Com TCO habilitado,uma fib 10000 com tail call é executada em 0.02 segundos

TCO 技术的价值在于,它可以重复使用和编译器中的工具说明,可以轻松地了解
该技术。


好的,您是否可以在 eu estiver 计划中使用 TCO 并使用语言来支持 TCO?

Trampoline para o resgate.


蹦床

对于蹦床来说,没有任何问题,并且有可能解决。

在情报方面,我们初步完成了一次重复开发,并确定了一个数字前提

def fib(position, _current = 0, _next = 1)
  return _current if position < 1

  ###################################
  #### Devemos evitar isso!!!!!! ####
  ###################################
  fib(position - 1, _next, _current + _next)
end
Enter fullscreen mode Exit fullscreen mode

Premissa dois,ao invés de retornar uma chamada recursiva ditamente,e se a retornarmos encapsulada em uma estrutura de função anônima queguarda contexto para ser executada em outro contexto?

Sim、tipo uma 关闭或 lambda para os mais atentos

在 Ruby 中,使用了lambda表达式

def fib(position, _current = 0, _next = 1)
  return _current if position < 1

  lambda do
    fib(position - 1, _next, _current + _next)
  end
end
Enter fullscreen mode Exit fullscreen mode

Se chamarmos result = fib(0),由于短路 ( position < 1) 的原因,或返回方法0

mas se chamarmos result = fib(10),或者返回到递归,mas sim 或返回到函数 anônima (lambda)。

这是一种方法,是一种最终的方法,可以在堆栈中完成也可以方法中流行。

Como lambdas Guardam contexto, se chamarmos result.call, a lambda é executada com o contexto anterior, que pode retornar o número Final (caso entre no Short-Circuit) o​​r outra lambda com o novo contexto.

E assim,ficamos em 循环 até termos 或 valor Final,enquanto 或 retorno atual continuar sendo uma lambda。是否有什么问题?

Sim,嗯,循环!

result = fib(10000)

while result.is_a?(Proc)
  result = result.call
end

puts result
Enter fullscreen mode Exit fullscreen mode

输出(um número mesmo muito grande):

33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875
Enter fullscreen mode Exit fullscreen mode

🔑 Ponto-chave
E com isto, amigues, temos a técnica Trampoline : um Loop primitivo não-recursivo que fica chamando outra função escrita de forma recursiva mas que retorna uma lambda com contexto, até chegar ao valor Final .

估计,SEM TCO,大约10000 倍,大约 0.04 秒,结果是 TCO 和 SEM 导致堆栈溢出。

Incrível,não? Agora não há desculpas para não escrever uma função de modo recursivo em linguagens que não trazem support a TCO 😛


结论

就直觉而言,特拉泽尔古斯认为自己没有重复的主题。这些想法与学术背景重叠,但在学术背景中却存在一些困难。

想要了解有关教学或重复的内容,请参阅相关评论或相关信息。


参考资料

https://twitter.com/leandronsp/status/1672043065001869312
https://twitter.com/JeffQuesado/status/1671954585987022882
https://en.wikipedia.org/wiki/Fibonacci_sequence
https://en.wikipedia.org/wiki/Recursion
https://www.geeksforgeeks.org/stack-data-structure/
https://en.wikipedia.org/wiki/Tail_call
https://en.wikipedia.org/wiki/Trampoline_(computing)
https://nithinbekal.com/posts/ruby-tco/
https://www.bigocheatsheet.com/
https://ruby-doc.org/core-3.1.0/RubyVM/InstructionSequence.html#method-c-compile_option

文章来源:https://dev.to/leandronsp/entendendo-fundamentos-de-recursao-2ap4