Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Daniel Ribeiro

[DESAFIO] Caixa Eletrônico

Recommended Posts

Olá pessoal, como estão?

 

Indo diretamente ao assunto, proponho o seguinte desafio:

 

A implementação de um script que simule um Caixa Eletrônico (ATM). O objetivo é que o código receba um valor X requisitado e retorne a quantidade mínima de notas que o usuário requisitante deverá receber, considerando um vetor de notas disponíveis (não vamos nos preocupar com quantas notas de cada o caixa terá disponível).

 

Considere:

 

class ATM
{

/**
 * Notas disponíveis no caixa eletrônico.
 * @var int
 */
private $bills = array(2, 5, 10, 20, 50, 100);

/**
 * Retorna a quantidade mínima de notas necessárias para a determinada
 * quantia de dinheiro.
 *
 * @param int $amountOfCash
 * @return int $minimalNumberOfBills
 */
static public function getMinimalNumberOfBills($amountOfCash)
{
	$minimalNumberOfBills = 0;

	...

	return $minimalNumberOfBills;
}

}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então vamos lá (não deu pra revisar porque tenho que colocar as crianças pra dormir, então... :P):

 

<?php
class ATM {

/**
 * @var array Notas disponíveis no caixa eletrônico.
 */
private $notasDisponiveis = array (2, 5, 10, 20, 50, 100 );

/**
 * @var array qtde de cada nota para realizar o saque
 */
private $qtde---ota = array ();

/**
 * Retorna a quantidade mínima de notas necessárias para a determinada quantia de dinheiro.
 * 
 * @param int amountOfCash
 * @return int minimalNumberOfBills
 */
public function getMinimalNumberOfBills($valorSolicitado) {
	if (! is_numeric ( $valorSolicitado )) {
		throw new InvalidArgumentException ( 'O valor solicitado para saque não é um valor válido.' );
	}

	$qtdeNotas = 0;
	arsort ( $this->notasDisponiveis );
	foreach ( $this->notasDisponiveis as $nota ) {
		if ($valorSolicitado == 0) {
			break;
		}
		$qtdeDestaNota = floor ( $valorSolicitado / $nota );
		$valorSolicitado -= $nota * $qtdeDestaNota;
		$qtdeNotas += $qtdeDestaNota;
		$this->qtde---ota [$nota] = $qtdeDestaNota;
	}
	if ($valorSolicitado != 0) {
		throw new InvalidArgumentException ( 'Não é possível realizar o saque, pois com as notas disponíveis neste caixa eletrônico não é possível chegar ao valor solicitado, restando o valor de R$ '. number_format ( $valorSolicitado, 2, ',', '.' ) );
	}
	return $qtdeNotas;
}

/**
 * Retorna a quantidade de cada nota, onde o índice é a nota e o valor é a quantidade daquela nota.
 * @return array $qtde---ota
 */
public function getQtde---ota() {
	return $this->qtde---ota;
}
}
try {
$atm = new ATM ();
$valorSolicitado = 1725;
echo 'A quantidade mínima de notas para sacar R$ ', number_format ( $valorSolicitado, 2, ',', '.' ), ' é de ', $atm->getMinimalNumberOfBills ( $valorSolicitado ), ', sendo:<ul>';
foreach ( $atm->getQtde---ota () as $nota => $qtde ) {
	echo '<li>R$ ', number_format ( $nota, 2, ',', '.' ), ' -> ', $qtde, '</li>';
}
} catch ( Exception $e ) {
echo $e->getMessage ();
}

 

Saída:

A quantidade mí­nima de notas para sacar R$ 1.725,00 é de 19, sendo:

  • R$ 100,00 -> 17
  • R$ 50,00 -> 0
  • R$ 20,00 -> 1
  • R$ 10,00 -> 0
  • R$ 5,00 -> 1

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Matias Rezende

 

Show de bola Carlos! Parabéns, o código está funcionando perfeitamente!

 

OBS: A verificação que você faz para dizer ao usuário que o valor solicitado não pode ser alcançado, na verdade, deve ser feita antes da realização da lógica pesada. Assim, pode-se otimizar o algorítmo. A princípio, está funcionando. Depois vou dar uma olhada com mais calma.

 

Mais à frente vamos adicionar um problema a este desafio! Fique atento ao tópico!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Só por farra, aí vai uma outra solução:

 

<?php

$time = microtime( true );

class ATM
{

       /**
        * Notas disponíveis no caixa eletrônico.
        * @var int
        */
       // OBS: Adicionado a "nota" de 1 real para quantias ímpares
       private static $bills = array( 1, 2, 5, 10, 20, 50, 100 );

       /**
        * Retorna a quantidade mínima de notas necessárias para a determinada
        * quantia de dinheiro.
        *
        * @param int $amountOfCash
        * @return int $minimalNumberOfBills
        */
       static public function getMinimalNumberOfBills( $amountOfCash )
       {
               $minimalNumberOfBills = 0;

               $arrIndex = count( static::$bills ) - 1; // nota de maior valor
               while ( $amountOfCash > 0 )
               {
               	if ( $amountOfCash >= static::$bills[ $arrIndex ] )
               	{
               		$minimalNumberOfBills += (int)( $amountOfCash / static::$bills[ $arrIndex ] );
               		$amountOfCash %= static::$bills[ $arrIndex ];
               	}
               	else
               	{
               		$arrIndex--;
               	}
               }

               return $minimalNumberOfBills;
       }

}

echo '7: ' . ATM::getMinimalNumberOfBills( 7 ) . " notas\n";
echo '100: ' . ATM::getMinimalNumberOfBills( 100 ) . " notas\n";
echo '101: ' . ATM::getMinimalNumberOfBills( 101 ) . " notas\n";
echo '102: ' . ATM::getMinimalNumberOfBills( 102 ) . " notas\n";
echo '105: ' . ATM::getMinimalNumberOfBills( 105 ) . " notas\n";
echo '110: ' . ATM::getMinimalNumberOfBills( 110 ) . " notas\n";
echo '200: ' . ATM::getMinimalNumberOfBills( 200 ) . " notas\n";
echo '230: ' . ATM::getMinimalNumberOfBills( 230 ) . " notas\n";

var_dump( microtime( true ) - $time );

?>

 

Tornei $bills estático, já que o método é estático e precisa ter acesso a ele

Também adicionei a "nota" de 1 real. Mas outra solução seria só testar se o número é par

 

saída:

$ php atm.php 
7: 2 notas
100: 1 notas
101: 2 notas
102: 2 notas
105: 2 notas
110: 2 notas
200: 2 notas
230: 4 notas
float(0.00010895729064941)

 

:thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Beraldo

 

Muito bom!

 

Gostei bastante do seu algorítmo, bem conciso e otimizado.

 

OBS: Sobre testar se o número é par, não entendi. O valor de 105 é ímpar e pode ser retirado do caixa eletrônico com duas (2) notas.

Compartilhar este post


Link para o post
Compartilhar em outros sites

OBS: Sobre testar se o número é par, não entendi. O valor de 105 é ímpar e pode ser retirado do caixa eletrônico com duas (2) notas.

 

No meu código, sim, pois adicionei nota de um real

Seu exemplo no post inicial não tinha nota de um, ou seja, só retiraria valores pares.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Beraldo

 

É justamente disso que estou falando. O número 105, por exemplo, é ímpar e pode ser retirado tranquilamente pelo caixa eletrônico com uma nota de 100 e uma nota de 5.

 

E sem precisar da nota de 1.

 

Também são ímpares e podem ser retirados os valores 107, 109, 111...

 

Entendeu?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Galera, achei um problema no meu código. Tentando sacar R$ 6,00 ele dispara o erro. Outros valores também deve acontecer o mesmo.

 

A quantidade mínima de notas para sacar R$ 6,00 é de Não é possível realizar o saque, pois com as notas disponíveis neste caixa eletrônico não é possível chegar ao valor solicitado, restando o valor de R$ 1,00

 

Mais tarde eu reviso a lógica e posto aqui a correção.

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Galera, achei um problema no meu código. Tentando sacar R$ 6,00 ele dispara o erro. Outros valores também deve acontecer o mesmo.

 

A quantidade mínima de notas para sacar R$ 6,00 é de Não é possível realizar o saque, pois com as notas disponíveis neste caixa eletrônico não é possível chegar ao valor solicitado, restando o valor de R$ 1,00

 

Mais tarde eu reviso a lógica e posto aqui a correção.

 

Carlos Eduardo

 

Interessante... ainda não analisei seu código, mas que bom que encontrou o problema.

 

Mais tarde, quando sair do trabalho, irei testar os dois códigos com mais calma, inclusive com essa situação.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Beraldo

 

É justamente disso que estou falando. O número 105, por exemplo, é ímpar e pode ser retirado tranquilamente pelo caixa eletrônico com uma nota de 100 e uma nota de 5.

 

E sem precisar da nota de 1.

 

Também são ímpares e podem ser retirados os valores 107, 109, 111...

 

Entendeu?

 

é verdade. acho que eu estava com muito sono quando postei isso e não percebi :P

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Beraldo

 

É justamente disso que estou falando. O número 105, por exemplo, é ímpar e pode ser retirado tranquilamente pelo caixa eletrônico com uma nota de 100 e uma nota de 5.

 

E sem precisar da nota de 1.

 

Também são ímpares e podem ser retirados os valores 107, 109, 111...

 

Entendeu?

 

é verdade. acho que eu estava com muito sono quando postei isso e não percebi :P

 

Não se preocupe... eu imaginei algo do tipo. Hehehe...

 

E aí galera, alguém mais se habilita?

 

Lembrando que em breve vou dar uma apimentada no desafio!

Compartilhar este post


Link para o post
Compartilhar em outros sites

No meu código, sim, pois adicionei nota de um real

 

Mas essa era a graça real do desafio.

 

Galera, achei um problema no meu código. Tentando sacar R$ 6,00 ele dispara o erro. Outros valores também deve acontecer o mesmo.

 

A quantidade mínima de notas para sacar R$ 6,00 é de Não é possível realizar o saque, pois com as notas disponíveis neste caixa eletrônico não é possível chegar ao valor solicitado, restando o valor de R$ 1,00

 

Mais tarde eu reviso a lógica e posto aqui a correção.

 

Carlos Eduardo

 

Na verdade não é bem um problema, pois foi previsto por você. A questão é que o código não cobre valores que poderiam ser pagos.

Vou facilitar, não é possível sacar apenas 1 e 3 R$. Qualquer outro valor é possível.

 

Segue a minha versão. A classe já está otimizada para uma futura implementação de verificação de cédulas disponíveis.

 

class ATM {
   private $ballots = array(
       2     => 0,
       5     => 0,
       10    => 0,
       20    => 0,
       50    => 0,
       100   => 0
   );

   public function withdraw($amount){
       $left = (integer)$amount;
       if($left == 1 || $left == 3) return trigger_error('Impossível conceder a quantia solicitada!');
       $values = array_keys($this->ballots);
       $send = array();
       for($value = end($values); $left; $value = prev($values)){
           $send[$value] = 0;
           while(($left - $value) >= 0){
               $partial = $left - $value;
               if($partial == 1 || $partial == 3) break;
               $left -= $value;
               $send[$value]++;
           }
           if(!$send[$value]) unset($send[$value]);
       }
       return $send;
   }
}

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então Evandro... O seu código só funciona se existirem todas estas notas (2,5,10,20,50 e 100). Se for retirada a nota de 2, por exemplo, o código entra em loop infinito.

 

Eu fiz um código aqui. Vou postar, mas antes já aviso. Ficou um monstrinho (ou monstrão)!!!!! Achei horrível, mas pelo menos ele atende a qualquer variação de notas com qualquer valor de saque.

 

Só funciona PHP 5.3+, porque eu usei um closure (só pra testar mesmo). Não to mais com saco de tirar ele dali, então vai ficar assim mesmo. Se alguém se habilitar, fique à vontade. :P

 

AVISO - AO CLICAR NO BOTÃO SPOILER VOCÊ ACEITA TODOS OS RISCOS DE LIBERAR ESTE MONSTRO GAMBIARRENTO. NÃO ME RESPONSABILIZO PELA SUA CURIOSIDADE. :lol:

 

 

 

<?php

class ATM {

/**
 * @var array Notas disponíveis no caixa eletrônico.
 */
private $notasDisponiveis = array ();

/**
 * @var array qtde de cada nota para realizar o saque
 */
private $qtde---ota = array ();

/**
 * 
 * @var float valor solicitado para saque, definido no construtor da classe
 */
private $valorSolicitado = 0;


/**
 * Retorna a quantidade mínima de notas necessárias para a determinada quantia de dinheiro.
 * 
 * @param int $valorSolicitado
 * @return int $qtdeNotas
 */
public function getQtdeMinimaDeNotas() {
	if($this->valorSolicitado == 0) {
		throw new Exception('Não foi definido o valor a ser sacado.');
	}

	if(empty($this->notasDisponiveis)) {
		throw new Exception('Não existem notas disponíveis neste caixa eletrônico.');
	}
	// ordenando as notas disponíveis
	rsort($this->notasDisponiveis);

	$valorSolicitado = $this->valorSolicitado;

	// removendo as notas maiores do que o valor solicitado, evitando iterações desnecessárias
	$notasDisponiveis = array_filter($this->notasDisponiveis, function ($elemento) use ($valorSolicitado) { 
			return (bool)($elemento <= $valorSolicitado); 
	} );

	// caso não existam notas menores ou iguais ao valor solicitado, o saque não poderá ser efetuado. 
	if (count ( $notasDisponiveis ) > 0) {

		$qtdeNotas = 0;
		$iteracao = 0;

		$maximoIteracao = floor ( $valorSolicitado / end($notasDisponiveis) );
		rsort ( $notasDisponiveis );

		while ( $valorSolicitado != 0 && $iteracao < $maximoIteracao ) {
			foreach ( $notasDisponiveis as $chave => $nota ) {
				if ($valorSolicitado == 0) {
					break;
				}
				$qtdeDestaNota = floor ( $valorSolicitado / $nota );
				if ($qtdeDestaNota > 0 && $chave <= ($iteracao - 1)) {
					$qtdeDestaNota --;
				}
				$valorSolicitado -= $nota * $qtdeDestaNota;
				$qtdeNotas += $qtdeDestaNota;
				$this->qtde---ota [$nota] = $qtdeDestaNota;
			}
			if ($valorSolicitado != 0) {
				$qtdeNotas = 0;
				$iteracao ++;
				$valorSolicitado = $this->valorSolicitado;
				continue;
			}
		}
	}
	if ($valorSolicitado != 0) {
		return 0;
	} else {
		return $qtdeNotas;
	}
}

/**
 * Retorna a quantidade de cada nota, onde o índice é a nota e o valor é a quantidade daquela nota.
 * @return array $qtde---ota
 */
public function getQtde---ota() {
	return $this->qtde---ota;
}

/**
 * Retorna as notas disponíveis
 * @return array
 */
public function getNotasDisponiveis() {
	return $this->notasDisponiveis;
}

/**
 * Método que define quais notas estão disponiveis
 * @param array|float $nota
 */
public function addNotasDisponiveis ($nota) {
	if(is_array($nota)) {
		foreach ( $nota as $item ) {
			$this->addNotasDisponiveis ( $item );
		}
	} else {
		$nota = ( float ) $nota;
		if (! in_array ( $nota, $this->notasDisponiveis )) {
			array_push ( $this->notasDisponiveis, $nota );
		}
	}
}

/**
 * Método que define o valor solicitado
 * @param float $valor
 */
public function setValorSaque($valor) {
	if (! is_numeric ( $valor )) {
		throw new InvalidArgumentException ( 'O valor solicitado é inválido.' );
	}
	$this->valorSolicitado = $valor;
}
}

try {
// só para testar diversos valores
$valores = range ( 1, 1000 );

// só para testar com diversas sequências de notas
$sequenciaNotas = array (array (2, 5, 10, 20, 50, 100 ), array (20, 50, 100 ), array (10, 50, 100 ) );

foreach ( $valores as $valorSolicitado ) {
	shuffle ( $sequenciaNotas );

	$atm = new ATM ();

	$atm->setValorSaque ( $valorSolicitado );

	$atm->addNotasDisponiveis ( $sequenciaNotas [0] );

	$qtdeMinimadeNotas = $atm->getQtdeMinimaDeNotas ();

	if ($qtdeMinimadeNotas != 0) {
		echo 'A quantidade mínima de notas para sacar R$ ', number_format ( $valorSolicitado, 2, ',', '.' ), ' é de ', $qtdeMinimadeNotas, ', sendo:<ul>';
		foreach ( $atm->getQtde---ota () as $nota => $qtde ) {
			echo '<li>R$ ', number_format ( $nota, 2, ',', '.' ), ' -> ', $qtde, '</li>';
		}
		echo '</ul>';
	} else {
		printf ( 'Não é possível realizar o saque. Notas disponíveis : %s. Valor solicitado : R$ %s<br/>', implode ( ' - ', $atm->getNotasDisponiveis () ), number_format ( $valorSolicitado, 2, ',', '.' ) );
	}
	echo '<hr>';
}

} catch ( Exception $e ) {
echo $e->getMessage ();
}

 

 

 

Carlos Eduardo

 

PS : Cansei de refatorar o código, mas ainda achei muito confuso e já estou com sono. Se alguém quiser melhorar, fiquem à vontade.

 

@Evandro

Na verdade não é bem um problema, pois foi previsto por você. A questão é que o código não cobre valores que poderiam ser pagos.

Vou facilitar, não é possível sacar apenas 1 e 3 R$. Qualquer outro valor é possível.

Eu já tinha percebido isto, mas eu queria montar algo que funcionasse com qualquer combinação de notas.

 

-------------------------------------------------

 

EDIT: Modifiquei a classe, porque fiz um negócio errado. Nem executava. Nome do método errado. Editei o código lá em cima. Valeu @João Batista Neto

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom eu achei que seria mais difícil, mas acabou que foi fácil.

 

Tão fácil que acho que me excedi um pouquinho:

 

 

<?php

class ATM {

   /**
    * Locale Constants
    */

   // General Messages

   const NO_WITHDRAWAL = 'Please, enter the withdrawal amount!';
   const INSUFFICIENT   = "Sorry! At momment this ATM doesn't have sufficient funds to complete your request.";
   const BYE           = 'Thank you for use our service!';

   // Don't change the placeholders

   const INIT          = 'Withdrawal Request: %d in %s<br /><br />';
   const NOT_PERFECT   = '<br />Sorry, but your withdrawal was not entirely perfect. We could not deliver 1 %s<br />';
   const DELIVERED     = 'ATM has delivered %d bills of %d %s<br />';

   // Currency

   const CURRENCY                 = 'BRL';
   const CURRENCY_TEXT_SINGULAR   = 'Real';
   const CURRENCY_TEXT_PLURAL     = 'Reais';

   /**
    * Available bills
    * 
    * @var	array	$bills
    */
   private $bills = array( 2, 5, 10, 20, 50, 100 );

   /**
    * Available money in ATM
    * 
    * @var	integer	$availability
    */
   private $availability = 10000;

   /**
    * Withdrawal Amount
    * 
    * @var	integer	$withdrawal
    */
   private $withdrawal;

   /**
    * Distribution Results
    * 
    * @var	array	$distribution
    */
   private $distribution = array();

   /**
    * Is a perfect withdrawal?
    * 
    * @var	boolean	isPerfect
    */
   private $isPerfect = TRUE;

   /**
    * ATM Constructor
    * 
    * @param	mixed|integer	$withdrawal
    */
   public function __construct( $withdrawal ) {

       $this -> withdrawal = (integer) $withdrawal;

       // Proccessing...

       $this -> proccessWithdrawal();
   }

   /**
    * Get Request Withdrawal
    * 
    * @return	integer
    */
   public function getWithdrawal() {
       return $this -> withdrawal;
   }

   /**
    * Get bills distribution
    * 
    * @param	mixed|boolean	$fancy	If TRUE, displays the results more descriptively way
    * 									If FALSE, returns the Distribution Array
    * 
    * @return	array|string
    */
   public function getDistribution( $fancy = FALSE ) {

       if( (bool) $fancy ) {

           printf( self::INIT, $this -> withdrawal, self::CURRENCY );

           // Damn! I hope someday Closures work properly in objects' context :(

           $message   = self::DELIVERED;
           $singular  = self::CURRENCY_TEXT_SINGULAR;
           $plural    = self::CURRENCY_TEXT_PLURAL;

           array_walk(

               $this -> distribution,

               function( $amount, $value ) use( $message, $singular, $plural ) {

                   if( $amount != 0 ) {

                       $text = ( $value == 1 ? $singular : $plural );

                       printf( $message, $amount, $value, $text );
                   }
               }
           );

           // Additional message if the amount was not perfect

           if( ! $this -> isPerfect ) {
               printf( self::NOT_PERFECT, self::CURRENCY_TEXT_SINGULAR );
           }

           // Saying Goodbye :)

           printf( '<br />%s', self::BYE );

           return;
       }

       return $this -> distribution;
   }

   /**
    * Proccess the Withdrawal
    * 
    * @return	array
    */
   private function proccessWithdrawal() {

       if( $this -> withdrawal == 0 ) {
           throw new ATMException( self::NO_WITHDRAWAL );
       }

       if( $this -> withdrawal > $this -> availability ) {
           throw new ATMException( self::INSUFFICIENT );
       }

       // Duplicate Withdrawal Value

       $withdrawal = $this -> withdrawal;

       arsort( $this -> bills );

       $next = TRUE;

       while( $next ) {

           // Getting the current bill

           $currentBill = current( $this -> bills );

           // Computing number of bills needed

           $amount = floor( $withdrawal / $currentBill );

           // Subtracting it from withdrawal request

           $times = $amount * $currentBill;

           $withdrawal -= $times;

           // Recording Information

           $this -> distribution[ $currentBill ] = $times;

           if( next( $this -> bills ) === FALSE ) $next = FALSE;
       }

       if( $withdrawal != 0 ) {
           $this -> isPerfect = FALSE;
       }
   }
}

class ATMException extends RuntimeException {}

 

Para testar:

 

// Testing...

$start = microtime( TRUE );

$ATM = new ATM( mt_rand( 1, 10000 ) );

$ATM -> getDistribution( TRUE );

// Benchmarking End...

$end = microtime( TRUE );

echo '<br /><br />Runtime: ' . ( $end - $start );

Aqui executou, em média, em 0.0006 segundos. Na maior parte das vezes em 0.0005, mas as vezes rodava em 0.0007, quando gerava números que requeriam distribuição de mais notas.

 

Assim como o código do Matias tem um requerimento mínimo do PHP 5.3, mas isso é apenas quando getDistribution() receber o valor TRUE, já que também usei uma Closure para deixar mais elegante.

 

E não há o problema de algumas notas não estarem disponíveis, porém a nota de UM REAL é obrigatória.

 

Se ela não existir, ao gerar o valor e faltar dinheiro você verá, se habilitado o retorno mais descritivo, uma mensagem de desculpas quanto à esse valor não entregue.

Compartilhar este post


Link para o post
Compartilhar em outros sites

No meu código, sim, pois adicionei nota de um real

 

Mas essa era a graça real do desafio.

 

Galera, achei um problema no meu código. Tentando sacar R$ 6,00 ele dispara o erro. Outros valores também deve acontecer o mesmo.

 

A quantidade mínima de notas para sacar R$ 6,00 é de Não é possível realizar o saque, pois com as notas disponíveis neste caixa eletrônico não é possível chegar ao valor solicitado, restando o valor de R$ 1,00

 

Mais tarde eu reviso a lógica e posto aqui a correção.

 

Carlos Eduardo

 

Na verdade não é bem um problema, pois foi previsto por você. A questão é que o código não cobre valores que poderiam ser pagos.

Vou facilitar, não é possível sacar apenas 1 e 3 R$. Qualquer outro valor é possível.

 

Segue a minha versão. A classe já está otimizada para uma futura implementação de verificação de cédulas disponíveis.

 

class ATM {
private $ballots = array(
2 => 0,
5 => 0,
10 => 0,
20 => 0,
50 => 0,
100 => 0
);

public function withdraw($amount){
$left = (integer)$amount;
if($left == 1 || $left == 3) return trigger_error('Impossível conceder a quantia solicitada!');
$values = array_keys($this->ballots);
$send = array();
for($value = end($values); $left; $value = prev($values)){
$send[$value] = 0;
while(($left - $value) >= 0){
$partial = $left - $value;
if($partial == 1 || $partial == 3) break;
$left -= $value;
$send[$value]++;
}
if(!$send[$value]) unset($send[$value]);
}
return $send;
}
}

 

Evandro, notei o mesmo problema que o Matias no seu código. Ele acabou ficando amarrado à implementação.

 

E um outro problema que percebi, apesar de não ter feito a análise concreta, foi a performance. Me parece bem lento assimptoticamente. Veremos com calma isso mais tarde, com a análise.

 

Então Evandro... O seu código só funciona se existirem todas estas notas (2,5,10,20,50 e 100). Se for retirada a nota de 2, por exemplo, o código entra em loop infinito.

 

Eu fiz um código aqui. Vou postar, mas antes já aviso. Ficou um monstrinho (ou monstrão)!!!!! Achei horrível, mas pelo menos ele atende a qualquer variação de notas com qualquer valor de saque.

 

Só funciona PHP 5.3+, porque eu usei um closure (só pra testar mesmo). Não to mais com saco de tirar ele dali, então vai ficar assim mesmo. Se alguém se habilitar, fique à vontade. :P

 

AVISO - AO CLICAR NO BOTÃO SPOILER VOCÊ ACEITA TODOS OS RISCOS DE LIBERAR ESTE MONSTRO GAMBIARRENTO. NÃO ME RESPONSABILIZO PELA SUA CURIOSIDADE. :lol:

 

 

 

<?php

class ATM {

/**
* @var array Notas disponíveis no caixa eletrônico.
*/
private $notasDisponiveis = array ();

/**
* @var array qtde de cada nota para realizar o saque
*/
private $qtde---ota = array ();

/**
* 
* @var float valor solicitado para saque, definido no construtor da classe
*/
private $valorSolicitado = 0;


/**
* Retorna a quantidade mínima de notas necessárias para a determinada quantia de dinheiro.
* 
* @param int $valorSolicitado
* @return int $qtdeNotas
*/
public function getQtdeMinimaDeNotas() {
	if($this->valorSolicitado == 0) {
		throw new Exception('Não foi definido o valor a ser sacado.');
	}

	if(empty($this->notasDisponiveis)) {
		throw new Exception('Não existem notas disponíveis neste caixa eletrônico.');
	}
	// ordenando as notas disponíveis
	rsort($this->notasDisponiveis);

	$valorSolicitado = $this->valorSolicitado;

	// removendo as notas maiores do que o valor solicitado, evitando iterações desnecessárias
	$notasDisponiveis = array_filter($this->notasDisponiveis, function ($elemento) use ($valorSolicitado) { 
			return (bool)($elemento <= $valorSolicitado); 
	} );

	// caso não existam notas menores ou iguais ao valor solicitado, o saque não poderá ser efetuado. 
	if (count ( $notasDisponiveis ) > 0) {

		$qtdeNotas = 0;
		$iteracao = 0;

		$maximoIteracao = floor ( $valorSolicitado / end($notasDisponiveis) );
		rsort ( $notasDisponiveis );

		while ( $valorSolicitado != 0 && $iteracao < $maximoIteracao ) {
			foreach ( $notasDisponiveis as $chave => $nota ) {
				if ($valorSolicitado == 0) {
					break;
				}
				$qtdeDestaNota = floor ( $valorSolicitado / $nota );
				if ($qtdeDestaNota > 0 && $chave <= ($iteracao - 1)) {
					$qtdeDestaNota --;
				}
				$valorSolicitado -= $nota * $qtdeDestaNota;
				$qtdeNotas += $qtdeDestaNota;
				$this->qtde---ota [$nota] = $qtdeDestaNota;
			}
			if ($valorSolicitado != 0) {
				$qtdeNotas = 0;
				$iteracao ++;
				$valorSolicitado = $this->valorSolicitado;
				continue;
			}
		}
	}
	if ($valorSolicitado != 0) {
		return 0;
	} else {
		return $qtdeNotas;
	}
}

/**
* Retorna a quantidade de cada nota, onde o índice é a nota e o valor é a quantidade daquela nota.
* @return array $qtde---ota
*/
public function getQtde---ota() {
	return $this->qtde---ota;
}

/**
* Retorna as notas disponíveis
* @return array
*/
public function getNotasDisponiveis() {
	return $this->notasDisponiveis;
}

/**
* Método que define quais notas estão disponiveis
* @param array|float $nota
*/
public function addNotasDisponiveis ($nota) {
	if(is_array($nota)) {
		foreach ( $nota as $item ) {
			$this->addNotasDisponiveis ( $item );
		}
	} else {
		$nota = ( float ) $nota;
		if (! in_array ( $nota, $this->notasDisponiveis )) {
			array_push ( $this->notasDisponiveis, $nota );
		}
	}
}

/**
* Método que define o valor solicitado
* @param float $valor
*/
public function setValorSaque($valor) {
	if (! is_numeric ( $valor )) {
		throw new InvalidArgumentException ( 'O valor solicitado é inválido.' );
	}
	$this->valorSolicitado = $valor;
}
}

try {
// só para testar diversos valores
$valores = range ( 1, 1000 );

// só para testar com diversas sequências de notas
$sequenciaNotas = array (array (2, 5, 10, 20, 50, 100 ), array (20, 50, 100 ), array (10, 50, 100 ) );

foreach ( $valores as $valorSolicitado ) {
	shuffle ( $sequenciaNotas );

	$atm = new ATM ();

	$atm->setValorSaque ( $valorSolicitado );

	$atm->addNotasDisponiveis ( $sequenciaNotas [0] );

	$qtdeMinimadeNotas = $atm->getQtdeMinimaDeNotas ();

	if ($qtdeMinimadeNotas != 0) {
		echo 'A quantidade mínima de notas para sacar R$ ', number_format ( $valorSolicitado, 2, ',', '.' ), ' é de ', $qtdeMinimadeNotas, ', sendo:<ul>';
		foreach ( $atm->getQtde---ota () as $nota => $qtde ) {
			echo '<li>R$ ', number_format ( $nota, 2, ',', '.' ), ' -> ', $qtde, '</li>';
		}
		echo '</ul>';
	} else {
		printf ( 'Não é possível realizar o saque. Notas disponíveis : %s. Valor solicitado : R$ %s<br/>', implode ( ' - ', $atm->getNotasDisponiveis () ), number_format ( $valorSolicitado, 2, ',', '.' ) );
	}
	echo '<hr>';
}

} catch ( Exception $e ) {
echo $e->getMessage ();
}

 

 

 

Carlos Eduardo

 

PS : Cansei de refatorar o código, mas ainda achei muito confuso e já estou com sono. Se alguém quiser melhorar, fiquem à vontade.

 

@Evandro

Na verdade não é bem um problema, pois foi previsto por você. A questão é que o código não cobre valores que poderiam ser pagos.

Vou facilitar, não é possível sacar apenas 1 e 3 R$. Qualquer outro valor é possível.

Eu já tinha percebido isto, mas eu queria montar algo que funcionasse com qualquer combinação de notas.

 

-------------------------------------------------

 

EDIT: Modifiquei a classe, porque fiz um negócio errado. Nem executava. Nome do método errado. Editei o código lá em cima. Valeu @João Batista Neto

Matias, seu código realmente ficou um monstrinho gambiarrado, mas resolveu o problema. Pelo que percebi, também está bem lento.

 

Bom eu achei que seria mais difícil, mas acabou que foi fácil.

 

Tão fácil que acho que me excedi um pouquinho:

 

 

<?php class ATM { /** * Locale Constants */ // General Messages const NO_WITHDRAWAL = 'Please, enter the withdrawal amount!'; const INSUFFICIENT = "Sorry! At momment this ATM doesn't have sufficient funds to complete your request."; const BYE = 'Thank you for use our service!'; // Don't change the placeholders const INIT = 'Withdrawal Request: %d in %s<br /><br />'; const NOT_PERFECT = '<br />Sorry, but your withdrawal was not entirely perfect. We could not deliver 1 %s<br />'; const DELIVERED = 'ATM has delivered %d bills of %d %s<br />'; // Currency const CURRENCY = 'BRL'; const CURRENCY_TEXT_SINGULAR = 'Real'; const CURRENCY_TEXT_PLURAL = 'Reais'; /** * Available bills * * @var	array	$bills */ private $bills = array( 2, 5, 10, 20, 50, 100 ); /** * Available money in ATM * * @var	integer	$availability */ private $availability = 10000; /** * Withdrawal Amount * * @var	integer	$withdrawal */ private $withdrawal; /** * Distribution Results * * @var	array	$distribution */ private $distribution = array(); /** * Is a perfect withdrawal? * * @var	boolean	isPerfect */ private $isPerfect = TRUE; /** * ATM Constructor * * @param	mixed|integer	$withdrawal */ public function __construct( $withdrawal ) { $this -> withdrawal = (integer) $withdrawal; // Proccessing... $this -> proccessWithdrawal(); } /** * Get Request Withdrawal * * @return	integer */ public function getWithdrawal() { return $this -> withdrawal; } /** * Get bills distribution * * @param	mixed|boolean	$fancy	If TRUE, displays the results more descriptively way * 									If FALSE, returns the Distribution Array * * @return	array|string */ public function getDistribution( $fancy = FALSE ) { if( (bool) $fancy ) { printf( self::INIT, $this -> withdrawal, self::CURRENCY ); // Damn! I hope someday Closures work properly in objects' context :( $message = self::DELIVERED; $singular = self::CURRENCY_TEXT_SINGULAR; $plural = self::CURRENCY_TEXT_PLURAL; array_walk( $this -> distribution, function( $amount, $value ) use( $message, $singular, $plural ) { if( $amount != 0 ) { $text = ( $value == 1 ? $singular : $plural ); printf( $message, $amount, $value, $text ); } } ); // Additional message if the amount was not perfect if( ! $this -> isPerfect ) { printf( self::NOT_PERFECT, self::CURRENCY_TEXT_SINGULAR ); } // Saying Goodbye :) printf( '<br />%s', self::BYE ); return; } return $this -> distribution; } /** * Proccess the Withdrawal * * @return	array */ private function proccessWithdrawal() { if( $this -> withdrawal == 0 ) { throw new ATMException( self::NO_WITHDRAWAL ); } if( $this -> withdrawal > $this -> availability ) { throw new ATMException( self::INSUFFICIENT ); } // Duplicate Withdrawal Value $withdrawal = $this -> withdrawal; arsort( $this -> bills ); $next = TRUE; while( $next ) { // Getting the current bill $currentBill = current( $this -> bills ); // Computing number of bills needed $amount = floor( $withdrawal / $currentBill ); // Subtracting it from withdrawal request $times = $amount * $currentBill; $withdrawal -= $times; // Recording Information $this -> distribution[ $currentBill ] = $times; if( next( $this -> bills ) === FALSE ) $next = FALSE; } if( $withdrawal != 0 ) { $this -> isPerfect = FALSE; } } } class ATMException extends RuntimeException {}

 

Para testar:

 

// Testing... $start = microtime( TRUE ); $ATM = new ATM( mt_rand( 1, 10000 ) ); $ATM -> getDistribution( TRUE ); // Benchmarking End... $end = microtime( TRUE ); echo '<br /><br />Runtime: ' . ( $end - $start );

Aqui executou, em média, em 0.0006 segundos. Na maior parte das vezes em 0.0005, mas as vezes rodava em 0.0007, quando gerava números que requeriam distribuição de mais notas.

 

Assim como o código do Matias tem um requerimento mínimo do PHP 5.3, mas isso é apenas quando getDistribution() receber o valor TRUE, já que também usei uma Closure para deixar mais elegante.

 

E não há o problema de algumas notas não estarem disponíveis, porém a nota de UM REAL é obrigatória.

 

Se ela não existir, ao gerar o valor e faltar dinheiro você verá, se habilitado o retorno mais descritivo, uma mensagem de desculpas quanto à esse valor não entregue.

Bruno, o seu código ficou muito bom a nível de performance. Foi muito bem otimizado.

 

O problema é que foge totalmente da realidade. Você não pode basear um algorítmo em algo hipotético ou não-concreto. No seu caso, depender da nota de 1 real é totalmente contrário ao princípio do desafio.

 

Tente implementar algo sob a ideia inicial: trabalhar sem depedência de notas, sem considerar a quantidade de cada notas, considerando as notas de 2, 5, 10, 20, 50 e 100.

 

@todos

 

Logo vou postar a minha solução, e quem sabe faremos uma análise conjunta da performance de todas as soluções.

 

O tópico está bem bacana!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Matias, seu código realmente ficou um monstrinho gambiarrado, mas resolveu o problema.

Hehehehe... :P

 

Pelo que percebi, também está bem lento.

Olha... lento não tá não. Tá gambiarrento, mas lento... Vejamos:

 

 

<?php

$inicio = microtime ( true );
class ATM {

/**
 * @var array Notas disponíveis no caixa eletrônico.
 */
private $notasDisponiveis = array ();

/**
 * @var array qtde de cada nota para realizar o saque
 */
private $qtde---ota = array ();

/**
 * 
 * @var float valor solicitado para saque, definido no construtor da classe
 */
private $valorSolicitado = 0;

/**
 * Retorna a quantidade mínima de notas necessárias para a determinada quantia de dinheiro.
 * 
 * @param int $valorSolicitado
 * @return int $qtdeNotas
 */
public function getQtdeMinimaDeNotas() {
	if ($this->valorSolicitado == 0) {
		throw new Exception ( 'Não foi definido o valor a ser sacado.' );
	}

	if (empty ( $this->notasDisponiveis )) {
		throw new Exception ( 'Não existem notas disponíveis neste caixa eletrônico.' );
	}
	// ordenando as notas disponíveis
	rsort ( $this->notasDisponiveis );

	$valorSolicitado = $this->valorSolicitado;

	// removendo as notas maiores do que o valor solicitado, evitando iterações desnecessárias
	$notasDisponiveis = array_filter ( $this->notasDisponiveis, function ($elemento) use($valorSolicitado) {
		return ( bool ) ($elemento <= $valorSolicitado);
	} );

	// caso não existam notas menores ou iguais ao valor solicitado, o saque não poderá ser efetuado. 
	if (count ( $notasDisponiveis ) > 0) {

		$qtdeNotas = 0;
		$iteracao = 0;

		$maximoIteracao = floor ( $valorSolicitado / end ( $notasDisponiveis ) );
		rsort ( $notasDisponiveis );

		while ( $valorSolicitado != 0 && $iteracao < $maximoIteracao ) {
			foreach ( $notasDisponiveis as $chave => $nota ) {
				if ($valorSolicitado == 0) {
					break;
				}
				$qtdeDestaNota = floor ( $valorSolicitado / $nota );
				if ($qtdeDestaNota > 0 && $chave <= ($iteracao - 1)) {
					$qtdeDestaNota --;
				}
				$valorSolicitado -= $nota * $qtdeDestaNota;
				$qtdeNotas += $qtdeDestaNota;
				$this->qtde---ota [$nota] = $qtdeDestaNota;
			}
			if ($valorSolicitado != 0) {
				$qtdeNotas = 0;
				$iteracao ++;
				$valorSolicitado = $this->valorSolicitado;
				continue;
			}
		}
	}
	if ($valorSolicitado != 0) {
		return 0;
	} else {
		return $qtdeNotas;
	}
}

/**
 * Retorna a quantidade de cada nota, onde o índice é a nota e o valor é a quantidade daquela nota.
 * @return array $qtde---ota
 */
public function getQtde---ota() {
	return $this->qtde---ota;
}

/**
 * Retorna as notas disponíveis
 * @return array
 */
public function getNotasDisponiveis() {
	return $this->notasDisponiveis;
}

/**
 * Método que define quais notas estão disponiveis
 * @param array|float $nota
 */
public function addNotasDisponiveis($nota) {
	if (is_array ( $nota )) {
		foreach ( $nota as $item ) {
			$this->addNotasDisponiveis ( $item );
		}
	} else {
		$nota = ( float ) $nota;
		if (! in_array ( $nota, $this->notasDisponiveis )) {
			array_push ( $this->notasDisponiveis, $nota );
		}
	}
}

/**
 * Método que define o valor solicitado
 * @param float $valor
 */
public function setValorSaque($valor) {
	if (! is_numeric ( $valor )) {
		throw new InvalidArgumentException ( 'O valor solicitado é inválido.' );
	}
	$this->valorSolicitado = $valor;
}
}

try {
// só para testar diversos valores
$valores = range ( 1, 1000 );

// só para testar com diversas sequências de notas
$sequenciaNotas = array (array (2, 5, 10, 20, 50, 100 ), array (20, 50, 100 ), array (10, 50, 100 ) );

foreach ( $valores as $valorSolicitado ) {
	shuffle ( $sequenciaNotas );

	$atm = new ATM ();

	$atm->setValorSaque ( $valorSolicitado );

	$atm->addNotasDisponiveis ( $sequenciaNotas [0] );

	$qtdeMinimadeNotas = $atm->getQtdeMinimaDeNotas ();

	if ($qtdeMinimadeNotas != 0) {
		echo 'A quantidade mínima de notas para sacar R$ ', number_format ( $valorSolicitado, 2, ',', '.' ), ' é de ', $qtdeMinimadeNotas, ', sendo:<ul>';
		foreach ( $atm->getQtde---ota () as $nota => $qtde ) {
			echo '<li>R$ ', number_format ( $nota, 2, ',', '.' ), ' -> ', $qtde, '</li>';
		}
		echo '</ul>';
	} else {
		printf ( 'Não é possível realizar o saque. Notas disponíveis : %s. Valor solicitado : R$ %s<br/>', implode ( ' - ', $atm->getNotasDisponiveis () ), number_format ( $valorSolicitado, 2, ',', '.' ) );
	}
	echo '<hr>';
}
$tempo = microtime ( true ) - $inicio;
echo 'O tempo total de execução de ', count ( $valores ), ' saques foi de ', $tempo , ' milisegundos. Na média de cada saque tivemos ',$tempo / count($valores) ;
} catch ( Exception $e ) {
echo $e->getMessage ();
}

 

 

Ao executar assim, realizando um shuffle desnecessário, só para dificultar mais ainda o processo, temos como resultado (5 vezes o mesmo código - vou copiar só a mensagem do benchmark).

 

  • O tempo total de execução de 1000 saques foi de 0.19138383865356 milisegundos. Na média de cada saque tivemos 0.00019138383865356
  • O tempo total de execução de 1000 saques foi de 0.19974207878113 milisegundos. Na média de cada saque tivemos 0.00019974207878113
  • O tempo total de execução de 1000 saques foi de 0.22120404243469 milisegundos. Na média de cada saque tivemos 0.00022120404243469
  • O tempo total de execução de 1000 saques foi de 0.20741605758667 milisegundos. Na média de cada saque tivemos 0.00020741605758667
  • O tempo total de execução de 1000 saques foi de 0.22686100006104 milisegundos. Na média de cada saque tivemos 0.00022686100006104

 

Vejam, está sendo realizado o saque de valores entre 1 e 1000. Além disto, estou adicionando as notas de forma aleatória, de acordo com opções pré definidas. Então, considero que o código está bem rápido. Está gambiarrento, isto não há dúvida. Mas lento ele não está não.

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites
Vejam, está sendo realizado o saque de valores entre 1 e 1000. Além disto, estou adicionando as notas de forma aleatória, de acordo com opções pré definidas. Então, considero que o código está bem rápido. Está gambiarrento, isto não há dúvida. Mas lento ele não está não.

 

Veja bem, Matias, quando me refiro à lentidão no processo do algorítmo, falo sob a perspectiva da análise assimptótica, e não sob a perspectiva de tempo real (em milisegundos). Seu código, é claro, vai rodar rapidamente em qualquer máquina moderna trabalhando com entradas normais, regulares.

 

O que eu vejo bastante e costumo lutar contra são essas análises de performance feitas em cima de microtimes, que, na realidade, não dizem nada a não ser uma simulação de tempo de execução baseada em hardware e estabilidade da linguagem.

 

Quero que vocês entendam que o conceito de análise de performance vai além da linguagem e do hardware sob onde irá trabalhar.

 

Seu algorítmo depende diretamente de uma entrada N, e cresce constantemente sob essa base N. Assim, temos um algorítmo de ordem constante O(N), ou seja, o algorítmo cresce em função de N. Isso é aceitável e relativamente rápido para entradas pequenas, normais. Porém, à medida que temos um crescimento dessa ordem constante, temos, respectivamente, uma perda enorme de performance do algorítmo.

 

OBS: Essa análise é bastante simples e precária, e está em um nível hipotético, visto que não considerei absolutamente nenhum processo interno da função, mas somente o valor de entrada dessa função.

 

É claro que isso tudo é muito teórico e não representa nada para nós na realidade em que trabalhamos ou estudamos. Só estou explicando para que entendam o que quero dizer quando falo em lentidão e otimização.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Daniel, perceba que no meu código postado NÃO HÁ a nota de um real sendo considerada, logo ele não é inteiramente dependente dela. Porém ele trabalha com a indisponibilidade da nota, apenas alertando o retirante que a transação não foi perfeita.

 

Essa consideração poderia ser feita pelo próprio usuário, se ele lê-se (ou lesse?) a descrição de disponibilidade das notas da máquina em questão. Ele não vai nem tentar tirar 10 reais de uma máquina que só trabalha com 50 e 100. ;)

 

Obviamente, a chave da solução é a nota de cinco reais já que ela é a única responsável por gerar valores ímpares:

 

13 reais => Uma de cinco e 4 de dois.

21 reais => Uma de 10, uma de cinco e três de dois.

47 reais => Duas de vinte, uma de cinco e uma de dois.

99 reais => Uma de cinquenta, duas de vinte, uma de cindo e duas de dois.

 

Todos os exemplos são dependentes da nota de cinco reais. Não vejo como, porém, executar a mesma rotina de distribuição sem ter a dependência de notas.

 

Tanto é verdade que se você retirar da matriz a nota de cinco, e não considerar a de um, jamais poderá entregar uma quantia ímpar.

 

Então, eu optei por uma solução mais simples e menos "dolorosa" na relação fictícia entre o ATM e o usuário retirante: Deixar de entregar UM REAL é melhor do que deixar de entregar CINCO, não?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Daniel, perceba que no meu código postado NÃO HÁ a nota de um real sendo considerada, logo ele não é inteiramente dependente dela. Porém ele trabalha com a indisponibilidade da nota, apenas alertando o retirante que a transação não foi perfeita.

 

Essa consideração poderia ser feita pelo próprio usuário, se ele lê-se (ou lesse?) a descrição de disponibilidade das notas da máquina em questão. Ele não vai nem tentar tirar 10 reais de uma máquina que só trabalha com 50 e 100. ;)

 

Obviamente, a chave da solução é a nota de cinco reais já que ela é a única responsável por gerar valores ímpares:

 

13 reais => Uma de cinco e 4 de dois.

21 reais => Uma de 10, uma de cinco e três de dois.

47 reais => Duas de vinte, uma de cinco e uma de dois.

99 reais => Uma de cinquenta, duas de vinte, uma de cindo e duas de dois.

 

Todos os exemplos são dependentes da nota de cinco reais. Não vejo como, porém, executar a mesma rotina de distribuição sem ter a dependência de notas.

 

Tanto é verdade que se você retirar da matriz a nota de cinco, e não considerar a de um, jamais poderá entregar uma quantia ímpar.

 

Então, eu optei por uma solução mais simples e menos "dolorosa" na relação fictícia entre o ATM e o usuário retirante: Deixar de entregar UM REAL é melhor do que deixar de entregar CINCO, não?

 

Bruno, sua reflexão é bastante pertinente do ponto de vista funcional, visto que, realmente, a operação de saque depende diretamente da disponibilidade de notas de um caixa eletrônico.

 

Concordo com você quando fala que o usuário irá "censurar" o funcionamento da máquina, no sentido de obter a informação sobre a disponibilidade e optar por um valor diferente do requerido inicialmente.

 

Do ponto de vista estrutural, todavia, se pensarmos em disponibilidade, caímos no maior problema que um caixa eletrônico pode enfrentar. Em linguagem de análise, chamaríamos isso de cenário de pior caso:

 

Imagine que um usuário tenha 47 reais na sua conta. Ele faz uma requisição inicial de 40 reais e o caixa eletrônico retorna uma mensagem de erro, alertando-o de que somente estão disponíveis notas de 50 e 100. E agora? Como proceder?

 

Esse caso representa exatamente o que falaste no teu comentário sobre a questão da rotina de distribuição. Aliás, foi um comentário bastante pertinente ao desafio em geral.

 

E que bom que chegamos nesse nível conceitual de discussão: esse caso cria obrigatoriamente um elo de dependência entre disponibilidade de notas e implementação do programa de saque.

 

OBS: Realmente não tinha entendido o real sentido da sua lógica, nem visto que não utilizava a nota de 1 real. Depois de analisar com calma seu código e sua análise, pude entender melhor o processo.

 

Aliás, parabéns. Seu código ficou muito bom. :D

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas o código do Bruno não funciona direito (ou eu fiz algo errado).

$ATM = new ATM( 6 );

$ATM -> getDistribution( TRUE );

 

Saída:

 

Withdrawal Request: 6 in BRL

 

ATM has delivered 5 bills of 5 Reais

 

Sorry, but your withdrawal was not entirely perfect. We could not deliver 1 Real

 

Thank you for use our service!

 

Veja que é possível sacar 6 reais com as notas disponíveis ali. (2,5,10,20,50,100), retirando 3 notas de 2 reais. Pelo que a mensagem está dizendo, você retirou 5 notas de 5 reais para sacar 6 reais... é isto mesmo ou a mensagem está errada? Se for isto mesmo, eu to torcendo pra você fazer o sistema do caixa eletrônico do meu banco... :D

 

Até agora o único código que funcionou para qualquer combinação de notas foi o meu monstrinho!!!! O código do Evandro retorna bem certinho a quantidade de notas, mas depende do usuário. Se colocar um valor que não seja possível ele não sai do loop. Falta ver o do Daniel e o do João (que disse que já terminou, mas vai esperar um pouco pra postar pra não estragar a brincadeira... :P)

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.