quarta-feira, 27 de janeiro de 2010

Algoritmos: Caixa de Banco Simulado no PostgreSQL

Ao retirar dinheiro de um caixa eletrônico, solicitamos uma quantia e o caixa decide quantas notas de cada tipo disponível nós receberemos. O algoritmo que faz esta decisão é relativamente simples.

Abaixo coloco uma função que recebe uma solicitação de dinheiro e calcula quantas notas de 100, 50, 10, 5 e 1 serão retornadas ao solicitante:

--Retornando notas do caixa eletrônico
--Notas de 1, 5, 10, 50 e 100
CREATE OR REPLACE FUNCTION caixa_elet (pvalor integer) RETURNS text AS $$
DECLARE
sretorno text;
qnota1 integer;
qnota5 integer;
qnota10 integer;
qnota50 integer;
qnota100 integer;
BEGIN
sretorno := '';
qnota100 := (pvalor - (pvalor % 100))/100;
qnota50 := ((pvalor % 100) - (pvalor % 50)) /50;
qnota10 := ((pvalor % 50) - (pvalor % 10)) /10;
qnota5 := ((pvalor % 10) - (pvalor % 5)) /5;
qnota1 := ((pvalor % 5) - (pvalor % 1)) /1;
sretorno := 'Total: ' || pvalor || chr(10) || 'Notas de 100:' || qnota100 || chr(10) || 'Notas de 50:' || qnota50 || chr(10) || 'Notas de 10:' || qnota10 || chr(10) || 'Notas de 5:' || qnota5 || chr(10) || 'Notas de 1:' || qnota1;
RETURN sretorno; -- Retorna as linhas
END;
$$ LANGUAGE plpgsql;

Chamada da função e resultado apresentado:

SELECT caixa_elet(1078);

"Total: 1078
Notas de 100:10
Notas de 50:1
Notas de 10:2
Notas de 5:1
Notas de 1:3"

SELECT caixa_elet(2189);

"Total: 2189
Notas de 100:21
Notas de 50:1
Notas de 10:3
Notas de 5:1
Notas de 1:4"

Agora, gostaria de fazer algumas perguntas para os programadores de plantão:
- O código da função caixa_elet está correto?
- O código da função caixa_elet pode ser melhorado de que formas?
- Que alterações seriam necessárias para acrescentar notas de 20?

Aguardo suas contribuições nos comentários!

4 comentários:

Fabrízio de Royes Mello disse...

Caro Cláudio,

Bem legal o algoritmo... a minha sugestão para incrementar a função seria um controle do número de notas disponíveis no caixa eletrônico, até mesmo para poder saber se é possível efetuar a transação.

Para o caso de adicionar a nota de R$20, ou até mesmo de R$2 creio que o mais adequado seria criar uma matriz multidimensional com as notas disponiveis e o valor que cada uma representa para poder fazer o cálculo das quantidades... e até mesmo a qtd de notas disponíveis é importante pois pode ser que nao tenhamos notas de 50 disponiveis e o usuário quer sacar 50 e teremos de disponibilizar 2 de 20 e 1 de 10, sempre dependendo da disponibilidade...

Podemos trocar mais alguma idéia e colocar em prática se vc desejar!

Abraço,

Fabrízio Mello

Cláudio Leopoldino disse...

Oi, Fabrízio!
Realmente teríamos de acrescentar duas verificações:
- primeira, se a quantidade solicitada é maior que a quantidade armazenada no caixa
- segunda, se a quantidade de notas de cada tipo é maior que a quantidade de notas disponíveis.

Anônimo disse...

Nice dispatch and this enter helped me alot in my college assignement. Thanks you for your information.

Datamais.com disse...

Este algoritmo funciona perfeitamente para as notas {1,5,10,50,100}. Adicionei outras possíveis variáveis {2,20} e aí surgiu o problema de apropriação de notas que não funcionou. Tomei a liberdade de criar uma nova função (talvez não com a mesma performance da função inicial) porém facilmente adaptável às possíveis notas existentes em determinada Economia.
Para tanto, usei as possibilidades existentes em nossa Economia (desde moedas de R$ 0,01 até notas de R$ 100,00) e funcinou perfeitamente.

CREATE OR REPLACE FUNCTION calcula_moeda (valor NUMERIC) RETURNS text AS $$ DECLARE
notas NUMERIC[12];
quantidade INTEGER[12];
processado NUMERIC;
retorno TEXT;
BEGIN
notas[0] := 0.01;
notas[1] := 0.05;
notas[2] := 0.10;
notas[3] := 0.25;
notas[4] := 0.50;
notas[5] := 1.00;
notas[6] := 2.00;
notas[7] := 5.00;
notas[8] := 10.00;
notas[9] := 20.00;
notas[10] := 50.00;
notas[11] := 100.00;
processado := 0.00;
retorno := 'Total: ' || valor || chr(10) || '----------------------------------';
FOR laco IN REVERSE 11..0 LOOP
quantidade[laco] := 0;
quantidade[laco] := CAST((valor - processado - ((valor - processado) % notas[laco])) / notas[laco] AS INTEGER);
processado := processado + (quantidade[laco] * notas[laco]);
retorno := retorno || chr(10) || 'Especie de ' || notas[laco] || ': ' || quantidade[laco] || ' unidade(s)';
END LOOP;
RETURN retorno;
END;
$$ LANGUAGE plpgsql;


Podemos ver em um exemplo de execução da função:
SELECT calcula_moeda(193.83);

Total: 193.83
----------------------------------
Especie de 100.00: 1 unidade(s)
Especie de 50.00: 1 unidade(s)
Especie de 20.00: 2 unidade(s)
Especie de 10.00: 0 unidade(s)
Especie de 5.00: 0 unidade(s)
Especie de 2.00: 1 unidade(s)
Especie de 1.00: 1 unidade(s)
Especie de 0.50: 1 unidade(s)
Especie de 0.25: 1 unidade(s)
Especie de 0.10: 0 unidade(s)
Especie de 0.05: 1 unidade(s)
Especie de 0.01: 3 unidade(s)