Ruby 如何计算表示n美分的方法数

Ruby 如何计算表示n美分的方法数,ruby,algorithm,recursion,Ruby,Algorithm,Recursion,我正在研究以下算法,想知道我的实现是否正确: 给定无限多的四分之一硬币、一角硬币、五分镍币和一分钱, 编写代码来计算表示n美分的方式的数量 这是没有记忆的: def count_ways(n) return 0 if n < 0 return 1 if n == 0 count_ways(n-25) + count_ways(n-5) + count_ways(n-10) + count_ways(n-1) end def计数方式(n) 如果n=10 结果=结果+计数方

我正在研究以下算法,想知道我的实现是否正确:

给定无限多的四分之一硬币、一角硬币、五分镍币和一分钱, 编写代码来计算表示n美分的方式的数量

这是没有记忆的:

def count_ways(n)
  return 0 if n < 0 
  return 1 if n == 0 

  count_ways(n-25) + count_ways(n-5) + count_ways(n-10) + count_ways(n-1)
end
def计数方式(n)
如果n<0,则返回0
如果n==0,则返回1
计数方式(n-25)+计数方式(n-5)+计数方式(n-10)+计数方式(n-1)
终止

不,您将重复计算解决方案,因为您可以先选择一个25美分的硬币,然后再选择一个10美分的硬币或其他方式,但这些解决方案本质上是相同的

防止重复计算的最简单的方法是确保你从不挑选比你已经挑选的硬币大的硬币

代码:

def count_ways(n, max_coin)
  return 0 if n < 0 
  return 1 if n == 0 

  result = count_ways(n-1, 1)
  result = result + count_ways(n- 5,  5) if max_coin >=  5
  result = result + count_ways(n-10, 10) if max_coin >= 10
  result = result + count_ways(n-25, 25) if max_coin >= 25
  result
end
def计数方式(n,最大硬币)
如果n<0,则返回0
如果n==0,则返回1
结果=计数方式(n-1,1)
结果=结果+计数方式(n-5,5),如果最大硬币数>=5
结果=结果+计数方式(n-10,10),如果最大硬币数>=10
结果=结果+计数方式(n-25,25),如果最大硬币数>=25
后果
终止

并将其命名为25作为初始最大硬币

我们可以很容易地看出您的代码是否正确。让我们试着换一角钱。有四种方式:一角硬币、两个五分镍币、一个五分镍币和五个一分镍币,还有十个一分镍币,然而
count_-ways(10)#=>9

您可以使用递归按如下方式执行

代码

def count_ways(cents, coins)
  if coins.size == 1
    return (cents % coins.first) == 0 ? [cents/coins.first] : nil
  end 
  coin, *remaining_coins = coins
  (0..cents/coin).each_with_object([]) { |n, arr|
    count_ways(cents-n*coin, remaining_coins).each { |a| arr << [n, *a] } }
end 
解释

演示递归如何工作的最好方法是使用puts语句对代码进行加密,然后在一个简单的示例中运行它

INDENT = 8
@indentation = 0

def indent
 @indentation += INDENT
end

def undent
 @indentation = [@indentation-INDENT, 0].max
end

def ind
  ' '*@indentation
end

aa=[12]
arr>呼叫计数方式(12-1*5,剩余硬币)
**输入计数方式,分=7,硬币=[1]
呼叫计数方式(12-2*5,剩余硬币)
**输入计数方式,分=2,硬币=[1]

硬币的顺序无关紧要,所以
coins.min
在这种情况下对你没有帮助,因为事情太复杂了

首先,我们必须对硬币的种类和数量之间的关系有一种直觉

使用
n
不同种类的硬币来更改金额的方法的数量等于

  • 使用除第一种硬币以外的所有硬币更改金额的方法的数量,以及
  • 更改金额的方法的数量
    a− d
    使用所有
    n
    种硬币,其中
    d
    是第一种硬币的面额
资料来源:

顺序不重要

change_coins 11, [5, 1, 2]           # => 11
change_coins 2, [3]                  # => 0
change_coins 100, [50, 1, 25, 10, 5] # => 292

你有测试用例吗?例如,你能手工计算出小n(比如n=1..10)的count_ways(n)的正确返回值,然后看看你的代码是否返回了你期望的结果吗?当你要求用Ruby编写的解决方案时,为什么要匆忙选择第一个答案,尤其是只提供伪代码的答案?此外,正如我在对答案的评论中指出的,这是不正确的。这听起来确实像是一个家庭作业问题。这本书真的很好读。也改为“。您的问题不符合有关代码问题的问题的标准,因为我们必须为其编写测试工具才能执行任何操作。您提供了有效的Ruby代码,但是
count\u ways(10,2)#=>9
,但应返回
3
(1角、1镍、5便士和10便士)。即使不运行Ruby代码,也可以通过跟踪查看它返回
9
。你用任何语言测试过吗?@CarySwoveland没有,不幸的是我手头没有伪代码解释器。但你是对的,我打错了,写了
=
,现在已经修好了。我不知道你为什么认为
count\u ways(10,2)
应该返回
3
,因为它显然应该返回
1
(确实如此):便士是唯一有价值的硬币
,现在看起来不错,只需要一点小小的改变:你必须在最后一行(
end
)之前添加一行
结果
。这是因为在Ruby中,
def doit;结果=1;如果为假,结果=结果+1;终止doit#=>nil
。忽略我之前提到的计数方式(10,2)
。我被第二个论点弄糊涂了。请注意计数方式(100,25)。大小#=>242
,与我的结果一致。虽然此代码是正确的,但它似乎远高于OP的技能水平。您可以添加一些解释,说明为什么此操作有效。此外,
如果coins.size==1,则返回[cents],隐式地假设最小的硬币的值为1,这降低了传递coins数组的有用性(我希望
计数方式(2[5])
返回0,而不是1)。这很好。我明天再加解释<代码>计数方式(x,[5])
永远不会被调用,只有
计数方式(x,[25,10,5,1])
计数方式(x,[10,5,1])
计数方式(x,[5,1])
计数方式(x,[1])
@VincentvanderWeele&Sunny,我对递归进行了解释。
def count_ways(cents, coins)
  puts "#{ind}** entering count_ways with cents=#{cents}, coins=#{coins}"
  if coins.size == 1
    puts "#{ind}<< returning [cents]=#{[cents]} as coins.size == 1" 
    undent
  end  
  return [cents] if coins.size == 1
  coin, *remaining_coins = coins
  puts "#{ind}coin=#{coin}. remaining_coins=#{remaining_coins}"
  puts "#{ind}0..cents/coin=#{0..cents/coin}"
  arr = (0..cents/coin).each_with_object([]) do |n, arr|
    puts "#{ind}  n=#{n}, arr=#{arr}"
    puts "#{ind}  >> calling count_ways(#{cents}-#{n}*#{coin}, remaining_coins)"
    indent
    aa = count_ways(cents-n*coin, remaining_coins)
    puts "#{ind}  aa=#{aa}"
    aa.each do |a|
      arr << [n, *a]
      puts "#{ind}    arr << [#{n}, *#{a}], arr=#{arr}"
    end
    puts "#{ind}  after all coins, arr=#{arr}"
  end
  puts "#{ind}<< returning arr=#{arr}"
  undent
  arr
end
count_ways(12, coins)
** entering count_ways with cents=12, coins=[25, 10, 5, 1]
coin=25. remaining_coins=[10, 5, 1]
0..cents/coin=0..0
  n=0, arr=[]
  >> calling count_ways(12-0*25, remaining_coins)
        ** entering count_ways with cents=12, coins=[10, 5, 1]
        coin=10. remaining_coins=[5, 1]
        0..cents/coin=0..1
          n=0, arr=[]
          >> calling count_ways(12-0*10, remaining_coins)
                ** entering count_ways with cents=12, coins=[5, 1]
                coin=5. remaining_coins=[1]
                0..cents/coin=0..2
                  n=0, arr=[]
                  >> calling count_ways(12-0*5, remaining_coins)
                        ** entering count_ways with cents=12, coins=[1]
                        << returning [cents]=[12] as coins.size == 1
                  aa=[12]
                    arr << [0, *12], arr=[[0, 12]]
                  after all coins, arr=[[0, 12]]
                  n=1, arr=[[0, 12]]
                  >> calling count_ways(12-1*5, remaining_coins)
                        ** entering count_ways with cents=7, coins=[1]
                        << returning [cents]=[7] as coins.size == 1
                  aa=[7]
                    arr << [1, *7], arr=[[0, 12], [1, 7]]
                  after all coins, arr=[[0, 12], [1, 7]]
                  n=2, arr=[[0, 12], [1, 7]]
                  >> calling count_ways(12-2*5, remaining_coins)
                        ** entering count_ways with cents=2, coins=[1]
                        << returning [cents]=[2] as coins.size == 1
                  aa=[2]
                    arr << [2, *2], arr=[[0, 12], [1, 7], [2, 2]]
                  after all coins, arr=[[0, 12], [1, 7], [2, 2]]
                << returning arr=[[0, 12], [1, 7], [2, 2]]
          aa=[[0, 12], [1, 7], [2, 2]]
            arr << [0, *[0, 12]], arr=[[0, 0, 12]]
            arr << [0, *[1, 7]], arr=[[0, 0, 12], [0, 1, 7]]
            arr << [0, *[2, 2]], arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
          after all coins, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
          n=1, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
          >> calling count_ways(12-1*10, remaining_coins)
                ** entering count_ways with cents=2, coins=[5, 1]
                coin=5. remaining_coins=[1]
                0..cents/coin=0..0
                  n=0, arr=[]
                  >> calling count_ways(2-0*5, remaining_coins)
                        ** entering count_ways with cents=2, coins=[1]
                        << returning [cents]=[2] as coins.size == 1
                  aa=[2]
                    arr << [0, *2], arr=[[0, 2]]
                  after all coins, arr=[[0, 2]]
                << returning arr=[[0, 2]]
          aa=[[0, 2]]
            arr << [1, *[0, 2]], arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
          after all coins, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
        << returning arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
  aa=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
    arr << [0, *[0, 0, 12]], arr=[[0, 0, 0, 12]]
    arr << [0, *[0, 1, 7]], arr=[[0, 0, 0, 12], [0, 0, 1, 7]]
    arr << [0, *[0, 2, 2]], arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2]]
    arr << [0, *[1, 0, 2]], arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
  after all coins, arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
<< returning arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
 => [[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]] 
### change_coins :: (Int, [Int]) -> Int
def change_coins amount, (x,*xs)
  if amount == 0
    1
  elsif amount < 0 or x.nil?
    0
  else
    change_coins(amount, xs) + change_coins(amount - x, [x,*xs])
  end
end

change_coins 11, [1, 2, 5]           # => 11
change_coins 2, [3]                  # => 0
change_coins 100, [1, 5, 10, 25, 50] # => 292
def cc amount, xs
  count = change_coins amount, xs
  if count == 0 then -1 else count end
end
change_coins 11, [5, 1, 2]           # => 11
change_coins 2, [3]                  # => 0
change_coins 100, [50, 1, 25, 10, 5] # => 292