ATM现金期权(票据)算法

时间:2019-04-13 19:18:05

标签: ruby

我正在Ruby中进行ATM练习,该功能之一是在请求现金价值后,系统可以显示可能的现金流的选项。

例如:R $ 120

  • 2x R $ 50 e 1x R $ 20
  • 2x R $ 50 e 2x R $ 10
  • 1x R $ 100 e 2x R $ 20
  • 6x R $ 20
  • 12x R $ 10
  • 等...

这些便笺是有限的,也就是说,如果在框中我只有10张便笺,每张10张,则不会出现12x $ 10的选项。

我在Java中找到了一种可能的算法,但是我觉得它太复杂了...

ATM algorithm of giving money with limited amount of bank notes

到目前为止,我的代码仅仅是:

main.rb

# Constantes
LIMITE_SAQUE = 2000

# Variáveis
opcao = nil

saldo_atual = 5000

qtd_notas_cem = 20
qtd_notas_cinquenta = 100
qtd_notas_dez = 200
qtd_notas_vinte = 50


while opcao != 4 

# Exibe as opções do Caixa Eletrônico
puts "Qual operação você deseja?"
puts "1 - Saque"
puts "2 - Recarga de Notas"
puts "3 - Exibir Informações"
puts "4 - Sair"
puts ""

opcao = gets.to_i

case opcao
  when 1
    puts "Você escolheu a opção de Saque"
    puts "Infome o valor para saque: "
    valor = gets.to_i

    if valor <= saldo_atual && valor <= LIMITE_SAQUE
      saldo_atual = saldo_atual - valor
      puts ""
      puts "Saque efetuado no valor de R$#{valor}"
      puts "Saldo atual diponível: R$#{saldo_atual}"
      puts "" 
    elsif valor >= LIMITE_SAQUE
      puts ""
      puts "Limite de saque é superior a R$2000,00"
      puts ""
    else
      puts ""
      puts "Não existe valor dispónivel para saque."
      puts ""
    end

  when 2
    puts "Você escolheu a opção de Recarga"
    puts "Infome o valor da nota: "
    valorNota = gets.to_i

    puts "Informe agora a quantidade de notas: "
    qtdNotas = gets.to_i

  when 3
    puts "Você escolheu a opção de Informações"
  when 4
    puts "Sair..."
    %x(exit)
  else
    puts ""
    puts "!!!!!! Opçao escolhida inválida. !!!!!!"
    puts ""
  end
end

1 个答案:

答案 0 :(得分:2)

广告资源和优先级

首先,让我们假设数组avail包含机器中存储的每个音符的编号。例如,

avail = [[100, 3], [50, 4], [20, 8], [10, 2], [5, 4], [1, 12]] 

此外,我们可以将其解释为优先分配纸币。元素的此顺序意味着应该分配尽可能多的R100钞票(最多3张),然后分配尽可能多的R50钞票,最多4张,依此类推。另一方面,如果仅在$ R50纸币数量不足的情况下才分发$ R100纸币,则可以写

avail = [[50, 4], [100, 3], [20, 8], [10, 2], [5, 4], [1, 12]] 

但是,在某些情况下,无法使用所有可用的音符。例如,如果

avail = [[50, 3], [100, 1]]

需要$ R200,即使可以使用3张,也只能分配2张$ R50钞票。

客户选择注释组合首选项

根据客户的选择,需要从avail派生一个数组,我在下面将其称为avail_mod。以下是一些示例(所有示例均假设可以获得有效的组合)。

1张R100纸币:

[[100, 1], [50, 4], [20, 8], [10, 2], [5, 4], [1, 12]]

3张50美元的钞票:

[[50, 3], [100, 3], [20, 8], [10, 2], [5, 4], [1, 12]] 

5张R20和2张5美元钞票:

[[20, 8], [5, 2], [100, 3], [50, 4], [10, 2], [1, 12]] 

例如,可以从avail派生出最后一个数组,如下所示。

[[20, 8], [5, 2]] + avail.reject { |k,_| [20, 5].include?(k) }
  #=> [[20, 8], [5, 2], [100, 3], [50, 4], [10, 2], [1, 12]] 

由您决定向客户显示的清单中有哪些可能性以及如何修改每个选择avail

用于根据给定的客户偏好计算注释组合的代码

鉴于所需的现金总量amt和经过修改的数组avail_mod,可以使用以下方法确定要分配的每个钞票的数量。

def dispense(amt, avail_mod)
  (d, n), *rest = avail_mod
  if rest.empty?
    return { d=>0 } if amt.zero?
    return nil if (amt % d) > 0 || d*n < amt
    return { d=>amt/d }
  end 
  last = nil
  m = [n, amt/d].min.downto(0).find { |m| last = dispense(amt-m*d, rest) }
  m.nil? ? nil : { d=>m }.merge(last)
end

此方法采用称为Dynamic Programming的技术。保证找到总计为amt的音符组合(如果存在的话)受数组avail_mod约束。我写此书的目的是要优先使用更多数量的面额较高的纸币。例如,如果需要$ R588且avail_mod给出的$ R100和$ R50钞票的数目分别为4和6,则它将首先寻找使用4个$ R100钞票,然后是3个$ R50钞票的解决方案, 等等。

示例

假设:

avail =     [[100, 3], [50, 4], [20, 8], [10, 2], [5, 4], [1, 12]] 
avail_mod = [[100, 2], [50, 4], [20, 8], [10, 2], [5, 4], [1, 12]

然后

spew_out = dispense(533, avail_mod)
  #=> {100=>2, 50=>4, 20=>6, 10=>1, 5=>0, 1=>3}  
spew_out.sum { |k,v| k*v }  
  #=> 533

dispense(300, avail_mod)
  #=> {100=>2, 50=>2, 20=>0, 10=>0, 5=>0, 1=>0} 

dispense(0, avail_mod)
  #=> {100=>0, 50=>0, 20=>0, 10=>0, 5=>0, 1=>0}

dispense(827, avail_mod)
  #=> nil

dispense(200, [[50,3], [100, 3]])
  #=> {50=>2, 100=>1}

更新avail

avail.map! { |d,n| [d, n-spew_out[d]] }
  #=> [[100, 1], [50, 0], [20, 2], [10, 1], [5, 4], [1, 9]]