""" Módulo de Simulação de Caixa Eletrônico (Saque) Este script simula a lógica de um caixa eletrônico para saques em dinheiro. Ele recebe um valor do usuário, valida a entrada de forma segura e calcula a menor quantidade possível de cédulas utilizando um Algoritmo Guloso otimizado (complexidade de tempo e espaço O(1)). """ # Constantes de configuração do caixa eletrônico CEDULAS_DISPONIVEIS = (100, 50, 20, 10, 5, 2) # Cédulas no cofre LIMITE_SAQUE = 20_000 # Valor máximo permitido por saque MAX_TENTATIVAS = 3 # Número máximo de tentativas de entrada def formatar_reais(valor): """ Formata um valor inteiro para o padrão de moeda brasileiro (Real). Args: valor (int): O valor numérico a ser formatado. Returns: str: String formatada (ex: 1500 -> "R$ 1.500,00"). """ return f"R$ {valor:,}".replace(",", ".") + ",00" def calcular_cedulas(valor): """ Calcula a menor quantidade de cédulas utilizando um Algoritmo Guloso. Como a nota de R$ 5 é a única cédula ímpar, ela é separada logo no início se o valor total for ímpar. O restante (que obrigatoriamente será par) é dividido de forma sequencial pelas notas pares disponíveis, garantindo uso de memória constante e execução instantânea. Args: valor (int): O valor desejado para o saque. Returns: dict: Um dicionário onde as chaves são os valores das cédulas e os valores são as quantidades necessárias. None: Se for impossível compor o valor (ex: R$ 1 ou R$ 3). """ # 1 e 3 são os únicos valores inteiros matematicamente impossíveis # de serem formados com as cédulas de 100, 50, 20, 10, 5 e 2. if valor == 1 or valor == 3: return None resultado = {} # Se o valor for ímpar, obrigatoriamente usamos uma (e apenas uma) nota de 5. if valor % 2 != 0: resultado[5] = 1 valor -= 5 # Com o valor par garantido, aplicamos divisões exatas do maior para o menor. cedulas_pares = (100, 50, 20, 10, 2) for cedula in cedulas_pares: if valor >= cedula: # Pega a maior quantidade inteira possível desta cédula quantidade = valor // cedula resultado[cedula] = quantidade # O valor restante passa a ser o resto dessa divisão valor %= cedula return resultado def validar_saque(entrada): """ Valida a entrada do usuário e tenta processar o pedido de saque. Args: entrada (str): O input bruto digitado pelo usuário. Returns: tuple: Retorna uma tupla contendo (valor, quantidades, erro): - valor (int ou None): O valor convertido para inteiro (se válido). - quantidades (dict ou None): Dicionário com as cédulas. - erro (str ou None): Mensagem de erro caso a validação falhe. """ entrada = entrada.strip() # Validação básica de tipo e formato (evita letras, símbolos, decimais ou negativos) if not entrada or not entrada.isascii() or not entrada.isdigit(): return None, None, "Formato inválido. Digite apenas números inteiros." # Remove zeros à esquerda. Se a string ficar vazia, assume "0" entrada = entrada.lstrip("0") or "0" limite = str(LIMITE_SAQUE) # Validação de limite via strings # Evita sobrecarga de memória caso o usuário insira uma string colossal if ( len(entrada) > len(limite) or (len(entrada) == len(limite) and entrada > limite) ): return None, None, ( f"Saque negado. Para valores acima de " f"{formatar_reais(LIMITE_SAQUE)}, " "realize o saque no caixa interno." ) valor = int(entrada) if valor == 0: return None, None, "O valor deve ser maior que zero." # Se a entrada é válida, tenta calcular as cédulas quantidades = calcular_cedulas(valor) # Verifica se a matemática permitiu a combinação if quantidades is None: return None, None, ( "Não é possível sacar esse valor com as cédulas disponíveis." ) return valor, quantidades, None def main(): """ Função principal que gerencia o fluxo de interação no terminal. Controla as tentativas, exibe os resultados formatados e trata interrupções inesperadas (como Ctrl+C) de forma amigável. """ for tentativa in range(1, MAX_TENTATIVAS + 1): try: entrada = input( "\nDigite o valor do saque em reais inteiros (ex.: 1500): " ) except (EOFError, KeyboardInterrupt): # Encerra graciosamente se o usuário fechar o terminal de repente print("\nOperação cancelada.") return # Executa a validação e o cálculo matemático valor, quantidades, erro = validar_saque(entrada) # Transação aprovada if erro is None: print("\nQuantidade de cédulas:") # Exibe as cédulas sempre ordenadas da nota maior para a menor for cedula in sorted(quantidades, reverse=True): print( f"{quantidades[cedula]} cédula(s) " f"de {formatar_reais(cedula)}" ) print(f"\nTotal do saque: {formatar_reais(valor)}") print("Valor restante: R$ 0,00") return # Transação negada (erro de digitação, valor impossível ou acima do limite) print(erro) # Informa o saldo de tentativas para o usuário restantes = MAX_TENTATIVAS - tentativa if restantes: print(f"Tentativas restantes: {restantes}") # Bloqueio após falhas consecutivas print( "\nPor motivo de segurança, a operação será abortada. " "Você deverá tentar de novo." ) if __name__ == "__main__": main()