我正在尝试为最大的回文产品problem实施递归解决方案
我想做的是将两个数字都从999开始,然后将num1向下迭代到100,然后在999处重新启动num1,然后将num2向下迭代1。
目标基本上是模仿嵌套的for循环。
def largest_palindrome_prod(num1 = 999, num2 = 999, largest_so_far = 0)
prod = num1 * num2
largest_so_far = prod if prod > largest_so_far && check_pal(prod)
if num2 == 100
return largest_so_far
elsif num1 == 100
largest_palindrome_prod(num1 = 999, num2 -= 1, largest_so_far)
else
largest_palindrome_prod(num1 -= 1, num2, largest_so_far)
end
end
#I know this function works, just here for reference
def check_pal(num)
num = num.to_s if num.is_a? Integer
if num.length < 2
true
else
num[0] == num[-1] ? check_pal(num[1..-2]) : false
end
end
rb:10:in
largest_palindrome_prod':堆栈级别太深`
我遇到了这个错误,该错误指的是maximum_palindrome_prod函数中的else语句,但是我不知道这是否会导致堆栈错误。
答案 0 :(得分:4)
您没有无限递归错误。由于您输入的大小,堆栈即将耗尽空间。为了证明这一点,您可以使用2位数字范围而不是3位数字范围运行相同的功能。它返回的很好,表明您的逻辑没有缺陷。
如何解决这个问题?两种选择。
选项1:您根本不能在此处使用递归(而是使用常规的嵌套循环)
选项2:保留相同的代码并启用尾部调用优化:
# run_code.rb
RubyVM::InstructionSequence.compile_option = {
tailcall_optimization: true,
trace_instruction: false
}
require './palindrome_functions.rb'
puts largest_palindrome_prod
# => 906609
请注意,由于我不完全了解的原因,必须在与正在运行的代码不同的文件中启用尾调用优化。因此,如果仅将compile_option行移动到palindrome_functions.rb文件,则它将行不通。
我无法真正为您提供尾部调用优化的完整说明(请在Wikipedia上查找),但据我所知,它是对递归函数的沉重优化,仅在递归调用位于端时有效函数主体的em>。您的功能符合此条件。
答案 1 :(得分:2)
@maxpleaner回答了您的问题,并说明了如何使用递归来避免堆栈级错误。他还提到了简单循环(而不是采用递归)的选项(我希望他赞成)。以下是一种循环解决方案。搜索 1 中使用以下方法。
def check_ranges(range1, range2 = range1)
range1.flat_map do |n|
[n].product((range2.first..[n, range2.last].min).to_a)
end.map { |x,y| x*y }.
sort.
reverse_each.
find do |z|
arr = z.digits
arr == arr.reverse
end
end
让我们首先找到介于960和999之间的两个数字(如果有)的乘积中最大的回文数:
check_ranges(960..999)
#=> nil
没有。请注意,此计算非常便宜,只需要检查40*40/2 #=> 800
个产品。接下来,找到最大回文,该回文等于920和999之间两个数字的乘积。
check_ranges(920..999)
#=> 888888
成功!请注意,此方法会重新检查我们之前检查的800
个产品。仅检查以下两个对brute_force
的调用所代表的情况更有意义:
check_ranges(960..999, 920..959)
#=> 888888
check_ranges(920..959)
#=> 861168
第一个调用计算40*40 #=> 1600
个产品;第二个800
产品。
当然,我们尚未找到最大的回文产品。但是,我们对最大的产品确实有一个下限,我们可以利用它的优势。自
888888/999
#=> 889
我们推断,如果两个数字的乘积大于888888
,则两个数字都必须至少为889。因此,我们仅需检查:
check_ranges(889..999, 889..919)
#=> 906609
check_ranges(889..919)
#=> 824428
我们完成了。这告诉我们906609
是两个3位数字(即回文)的最大乘积。
该问题并没有询问其乘积最大的回文中的两个数字是什么,但是我们可以轻松找到它们:
(889..999).to_a.product((889..919).to_a).find { |x,y| x*y == 906609 }
#=> [993, 913]
993*913
#=> 906609
此外,让:
a = (889..999).to_a.product((889..919).to_a).map { |x,y| x*y }.
sort.
reverse
然后:
a.index { |n| n == 906609 }
#=> 84
告诉我们,在发现回文(84
)之前,只需要检查111*31 #=> 3441
这类产品中最大的906609
元素。
所有这些都需要组织成一个方法。尽管对于新手来说具有挑战性,但它应该是一个很好的学习经历。
1。测试更快的arr = z.digits; arr == arr.reverse
或s = z.to_s; s == s.reverse
会很有用。
答案 2 :(得分:0)
@maxpleaner已经回答,@ Cary Swoveland已经显示出使用ranges
和product
的一种蛮力方式。我想使用嵌套循环显示另一种蛮力,更易于理解(IMO):
n = 9999
res = [0]
bottom = 10**(n.digits.size - 1)
n.downto(bottom) do |k|
k.downto(bottom) do |j|
# puts "#{k}, #{j}"
res = [k, j, k * j] if check_pal(k * j) && k * j > res.last
end
end
res
#=> [9999, 9901, 99000099]
n.downto(n*99/100) do |k|
k.downto(k*99/100) do |j|
在0.7秒内返回了[99979, 99681, 9966006699]
。
def check_pal(num)
word = num.to_s
word.reverse == word
end