Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
5
respostas

Desafio: Calculadora de despesas - Proposta de Solução em Linguagem Natural e Pseudocódigo

Escopo do Problema

Desenvolver um sistema simples de controle financeiro pessoal. As funcionalidades desejadas, inclui permitir que o usuário informe diversas despesas do mês (como mercado, transporte, lazer etc.), e ao final, o sistema deve apresentar o total geral gasto, total por tipo de despesa, a contagem de despesas feitas por tipo, o maior, o menor, e o valor médio por cada tipo de despesa, bem como a indicação final da despesa de maior, menor e valor médio indicando o tipo correspondente, e o numero total de despesas feitas realizadas.

Algoritmo Proposto e seus requisitos funcionais

Criar um algoritmo, em linguagem natural e depois em pseudocódigo, que represente a seguinte lógica: o sistema deve somar automaticamente todos os valores informados pelo usuário, por tipo, contagem das despesas por tipo, sem que ele precise dizer quantas despesas fez. O processo deve continuar em loop até que, o valor digitado seja zero ou o usuário digite FIM. Nesse momento, deve ser mostrado o TITULO CONTROLE FINANCEIRO do demonstrativo a ser exibido, onde abaixo do titulo, incluindo ordenado crescentemente por nome (tipo) da despesa, os totais para cada tipo de despesa distinta, o nome da despesa, o valor total gasto da despesa, o menor valor, o maior valor e o valor médio da referida despesa, e o % dessa despesa em relação ao total geral gasto, além da contagem de gastos efetuados desta despesa. Após terminar de exibir o resumo por tipo de despesa de todas as despesas, incluir uma linha final com o resumo geral, contendo o total geral de despesas, a indicação da despesa com maior gasto, menor gasto, a contagem total geral de despesas realizadas, e o valor médio geral de gastos com despesas no período. Na linha de totais informar o nome da despesa de maior valor acumulado seguido do valor total do gasto desta despesa. O mesmo para o caso da despesa de menor valor total gasto.

Abaixo a Solução Proposta....

5 respostas
solução!

Algoritmo Detalhado em Linguagem Natural

Objetivo: registrar despesas do mês sem saber antecipadamente quantas serão, acumulando estatísticas por tipo, e ao final exibir um demonstrativo ordenado por nome do tipo, mais uma linha de resumo geral.

Fase 1 — Preparação

Criar cinco listas paralelas, todas alinhadas pelo mesmo índice, que representam cada tipo distinto de despesa encontrado:

nomeTipo — o nome do tipo (ex.: MERCADO, TRANSPORTE, LAZER);
totalTipo — soma acumulada dos valores daquele tipo;
qtdTipo — quantas despesas já foram lançadas naquele tipo;
menorTipo — menor valor individual já lançado naquele tipo;
maiorTipo — maior valor individual já lançado naquele tipo.

Zerar o contador de tipos distintos (qtdTipos), o total geral gasto (totalGeral) e a contagem geral de lançamentos (qtdGeral).

Fase 2 — Entrada de dados (laço de repetição, sem quantidade prévia)

Repetir indefinidamente:

Pedir ao usuário o tipo da despesa (ou a palavra FIM para encerrar).
Converter o texto digitado para letras maiúsculas e remover espaços em excesso, para que "mercado", "Mercado" e "MERCADO" sejam tratados como o mesmo tipo.
Se o texto for FIM, encerrar o laço.
Pedir o valor da despesa.
Se o valor for zero, encerrar o laço (o lançamento em andamento é descartado).
Se o valor for negativo, avisar que é inválido e voltar ao início do laço, sem gravar nada.
Procurar nomeTipo nas listas para descobrir se aquele tipo já existe:
Se não existe: abrir uma nova posição — incrementar qtdTipos, gravar o nome, iniciar o total com o valor informado, iniciar a contagem em 1, e iniciar tanto o menor quanto o maior com o próprio valor informado (é o único valor conhecido até agora).
Se já existe: somar o valor ao totalTipo daquela posição, somar 1 ao qtdTipo; se o valor for menor que menorTipo, substituir o menor; se for maior que maiorTipo, substituir o maior.
Somar o valor ao totalGeral e somar 1 ao qtdGeral.
Volta a Repetir

Fase 3 — Ordenação

Se nenhum lançamento foi feito (qtdTipos = 0), exibir a mensagem "Nenhuma despesa registrada." e terminar o programa.
Ordenar as listas em ordem crescente pelo nome do tipo (método da bolha ou equivalente). Sempre que dois nomes forem trocados de posição, trocar também os valores correspondentes de totalTipo, qtdTipo, menorTipo e maiorTipo, para não quebrar o alinhamento entre as listas.

Fase 4 — Apuração dos extremos globais

Percorrer as listas guardando:

a posição do tipo com maior total acumulado (posMaiorTotal);
a posição do tipo com menor total acumulado (posMenorTotal);
a posição do tipo com maior valor médio (posMaiorMedia), onde a média de um tipo é totalTipo ÷ qtdTipo.

Calcular a média geral como totalGeral ÷ qtdGeral.

Fase 5 — Saída

Exibir o título CONTROLE FINANCEIRO e o cabeçalho das colunas.

Para cada tipo, do primeiro ao último (já em ordem alfabética), exibir em uma linha:

nome do tipo,
total gasto,
quantidade de lançamentos,
menor valor,
maior valor,
valor médio (total ÷ quantidade) e
o percentual sobre o total geral (total ÷ totalGeral × 100).

Exibir a linha de RESUMO GERAL contendo:

total geral gasto;
contagem total de despesas realizadas;
valor médio geral;
maior gasto: nome do tipo de maior total acumulado seguido do seu valor total;
menor gasto: nome do tipo de menor total acumulado seguido do seu valor total;
maior média: nome do tipo de maior valor médio seguido da média correspondente.

Encerrar.

devido a limitação de 5000 caracteres a solução continua a seguir....

Algoritmo em PSEUDOCODIGO (linguagem Natural Estruturada)

//  Algoritmo CONTROLE_FINANCEIRO_PESSOAL
Var
   
   // ---- listas paralelas: uma posição por TIPO distinto de despesa ----
   nomeTipo  : vetor[1..100] de caractere
   totalTipo : vetor[1..100] de real
   qtdTipo   : vetor[1..100] de inteiro
   menorTipo : vetor[1..100] de real
   maiorTipo : vetor[1..100] de real
   
   // ---- controle ----
   qtdTipos      : inteiro     // quantos tipos distintos existem
   totalGeral    : real        // soma de todas as despesas
   qtdGeral      : inteiro     // quantidade total de lancamentos
   mediaGeral    : real
   valor         : real
   tipo          : caractere
   i, j, pos     : inteiro
   posMaiorTotal : inteiro
   posMenorTotal : inteiro
   posMaiorMedia : inteiro
   mediaI, mediaJ: real
   percentual    : real
   encerrar      : logico

   // ---- auxiliares de troca (ordenacao) ----
   auxNome : caractere
   auxReal : real
   auxInt  : inteiro

Inicio

   qtdTipos   = 0
   totalGeral = 0
   qtdGeral   = 0
   encerrar   = FALSO
   Escreval("=== LANCAMENTO DE DESPESAS ===")
   Escreval("Digite FIM no tipo, ou 0 no valor, para encerrar.")

    // ================== FASE 2: ENTRADA EM LACO ==================
   Enquanto (encerrar = FALSO) Faca
      Escreva("Tipo da despesa (ou FIM): ")
      Leia(tipo)
      tipo <- MAIUSC(tipo)                  // normaliza: mercado = MERCADO
      Se (tipo = "FIM") Entao
         encerrar <- VERDADEIRO
      Senao
         Escreva("Valor de ", tipo, ": R$ ")
         Leia(valor)
         Se (valor = 0) Entao
            encerrar <- VERDADEIRO
         Senao
            Se (valor < 0) Entao
               Escreval(">> Valor invalido. Informe um valor positivo.")
            Senao
               // ---- procura se o tipo ja foi cadastrado ----
               pos <- 0
               Para i de 1 ate qtdTipos Faca
                  Se (nomeTipo[i] = tipo) Entao
                     pos <- i
                  FimSe
               FimPara
               Se (pos = 0) Entao
                  // ---- tipo novo: abre uma posicao ----
                  qtdTipos <- qtdTipos + 1
                  pos <- qtdTipos
                  nomeTipo[pos]  <- tipo
                  totalTipo[pos] <- valor
                  qtdTipo[pos]   <- 1
                  menorTipo[pos] <- valor
                  maiorTipo[pos] <- valor
               Senao
                  // ---- tipo existente: acumula ----
                  totalTipo[pos] <- totalTipo[pos] + valor
                  qtdTipo[pos]   <- qtdTipo[pos] + 1
                  Se (valor < menorTipo[pos]) Entao
                     menorTipo[pos] <- valor
                  FimSe
                  Se (valor > maiorTipo[pos]) Entao
                     maiorTipo[pos] <- valor
                  FimSe
               FimSe
               // ---- acumuladores gerais ----
               totalGeral <- totalGeral + valor
               qtdGeral   <- qtdGeral + 1
            FimSe
         FimSe
      FimSe
   FimEnquanto
   
   // ================== FASE 3: VALIDACAO E ORDENACAO ==================
   Se (qtdTipos = 0) Entao
      Escreval("")
      Escreval("Nenhuma despesa registrada.")
   Senao
   
      // ---- ordenacao crescente pelo NOME do tipo (metodo da bolha) ----
      Para i de 1 ate (qtdTipos - 1) Faca
         Para j de 1 ate (qtdTipos - i) Faca
            Se (nomeTipo[j] > nomeTipo[j+1]) Entao
               auxNome        <- nomeTipo[j]
               nomeTipo[j]    <- nomeTipo[j+1]
               nomeTipo[j+1]  <- auxNome
               auxReal        <- totalTipo[j]
               totalTipo[j]   <- totalTipo[j+1]
               totalTipo[j+1] <- auxReal
               auxInt         <- qtdTipo[j]
               qtdTipo[j]     <- qtdTipo[j+1]
               qtdTipo[j+1]   <- auxInt
               auxReal        <- menorTipo[j]
               menorTipo[j]   <- menorTipo[j+1]
               menorTipo[j+1] <- auxReal
               auxReal        <- maiorTipo[j]
               maiorTipo[j]   <- maiorTipo[j+1]
               maiorTipo[j+1] <- auxReal
            FimSe
         FimPara
      FimPara

devido a limitação de 5000 caracteres a solução continua a seguir....

     // ================== FASE 4: EXTREMOS GLOBAIS ==================
      posMaiorTotal <- 1
      posMenorTotal <- 1
      posMaiorMedia <- 1
      Para i de 2 ate qtdTipos Faca
         Se (totalTipo[i] > totalTipo[posMaiorTotal]) Entao
            posMaiorTotal <- i
         FimSe
         Se (totalTipo[i] < totalTipo[posMenorTotal]) Entao
            posMenorTotal <- i
         FimSe
         mediaI <- totalTipo[i] / qtdTipo[i]
         mediaJ <- totalTipo[posMaiorMedia] / qtdTipo[posMaiorMedia]
         Se (mediaI > mediaJ) Entao
            posMaiorMedia <- i
         FimSe
      FimPara
      mediaGeral <- totalGeral / qtdGeral
      
      // ================== FASE 5: DEMONSTRATIVO ==================
      Escreval("")
      Escreval("==========================================================================")
      Escreval("                          CONTROLE FINANCEIRO                             ")
      Escreval("==========================================================================")
      Escreval("TIPO           QTDE     TOTAL      MENOR      MAIOR      MEDIA       %   ")
      Escreval("--------------------------------------------------------------------------")
      Para i de 1 ate qtdTipos Faca
         mediaI     <- totalTipo[i] / qtdTipo[i]
         percentual <- (totalTipo[i] / totalGeral) * 100
         Escreval(nomeTipo[i]:14,
                  qtdTipo[i]:6,
                  totalTipo[i]:11:2,
                  menorTipo[i]:11:2,
                  maiorTipo[i]:11:2,
                  mediaI:11:2,
                  percentual:8:2, "%")
      FimPara
      Escreval("--------------------------------------------------------------------------")
      Escreval("RESUMO GERAL")
      Escreval("  TOTAL GERAL GASTO ............: R$ ", totalGeral:0:2)
      Escreval("  QTDE TOTAL DE DESPESAS .......: ",    qtdGeral)
      Escreval("  VALOR MEDIO GERAL ............: R$ ", mediaGeral:0:2)
      Escreval("  MAIOR GASTO (tipo/valor) .....: ",    nomeTipo[posMaiorTotal],
                                              " - R$ ", totalTipo[posMaiorTotal]:0:2)
      Escreval("  MENOR GASTO (tipo/valor) .....: ",    nomeTipo[posMenorTotal],
                                              " - R$ ", totalTipo[posMenorTotal]:0:2)
      Escreval("  MAIOR MEDIA (tipo/valor) .....: ",    nomeTipo[posMaiorMedia],
                                              " - R$ ", (totalTipo[posMaiorMedia] / qtdTipo[posMaiorMedia]):0:2)
      Escreval("==========================================================================")
   FimSe
Fimalgoritmo

Exemplo de Execução do Algoritmo

*Entradas do Usuário:

  • MERCADO 250,00
  • TRANSPORTE 40,00
  • LAZER 120,00
  • MERCADO 180,50
  • TRANSPORTE 15,00
  • MERCADO 90,00
  • LAZER 60,00
  • TRANSPORTE 25,00
  • FIM

Saídas do Algoritmo:

CONTROLE FINANCEIRO

Demonstrativo por tipo de despesa

(ordenado crescentemente por nome do tipo)

Tipo da DespesaQtdeTotal GastoMenor ValorMaior ValorValor Médio% do Total
LAZER2R$ 180,00R$ 60,00R$ 120,00R$ 90,0023,06%
MERCADO3R$ 520,50R$ 90,00R$ 250,00R$ 173,5066,69%
TRANSPORTE3R$ 80,00R$ 15,00R$ 40,00R$ 26,6710,25%

Resumo geral

IndicadorTipo correspondenteValor
Total geral gastoR$ 780,50
Qtde total de despesas8
Valor médio geralR$ 97,56
Despesa de maior gastoMERCADOR$ 520,50
Despesa de menor gastoTRANSPORTER$ 80,00
Despesa de maior valor médioMERCADOR$ 173,50

Observacões que valem destaque na melhoria e defesa contra erros na Execuçao do Algoritmo

i. Não se pergunta a quantidade de despesas: o laço Enquanto é controlado por sentinela dupla (FIM no tipo ou 0 no valor), e os contadores qtdGeral e qtdTipo[] são incrementados a cada lançamento válido.

ii. Descoberta dinâmica de tipos: a busca linear (pos <- 0 … Para i de 1 ate qtdTipos) decide entre criar uma nova posição ou acumular numa existente — por isso o usuário nunca precisa declarar antecipadamente quais tipos vai usar.

iii. Menor e maior inicializados com o próprio valor no primeiro lançamento do tipo. Inicializar menorTipo com 0 seria um erro clássico: nenhum valor positivo posterior ficaria abaixo de 0 e o menor jamais seria atualizado.

iv. Troca em bloco na ordenação: as cinco listas são paralelas, então toda troca precisa mover as cinco simultaneamente, senão os totais passam a pertencer ao tipo errado.

v Normalização com MAIUSC evita que "Mercado" e "MERCADO" virem dois tipos distintos.

vi. Proteção contra divisão por zero: o cálculo de mediaGeral e dos percentuais só ocorre dentro do Senao do teste qtdTipos = 0, ou seja, quando há pelo menos um lançamento.

Ajuste na Fase 5 do pseudocódigo para emitir Markdown

      // ================== FASE 5: DEMONSTRATIVO EM MARKDOWN ==================
      Escreval("# CONTROLE FINANCEIRO")
      Escreval("")
      Escreval("### Demonstrativo por tipo de despesa")
      Escreval("*(ordenado crescentemente por nome do tipo)*")
      Escreval("")
      Escreval("| Tipo da Despesa | Qtde | Total Gasto | Menor Valor | Maior Valor | Valor Medio | % do Total |")
      Escreval("|:----------------|-----:|------------:|------------:|------------:|------------:|-----------:|")

      Para i de 1 ate qtdTipos Faca
         mediaI     <- totalTipo[i] / qtdTipo[i]
         percentual <- (totalTipo[i] / totalGeral) * 100

         Escreval("| ", nomeTipo[i],
                  " | ", qtdTipo[i],
                  " | R$ ", totalTipo[i]:0:2,
                  " | R$ ", menorTipo[i]:0:2,
                  " | R$ ", maiorTipo[i]:0:2,
                  " | R$ ", mediaI:0:2,
                  " | ",    percentual:0:2, "% |")
      FimPara

      Escreval("")
      Escreval("### Resumo geral")
      Escreval("")
      Escreval("| Indicador | Tipo correspondente | Valor |")
      Escreval("|:----------|:--------------------|------:|")
      Escreval("| **Total geral gasto** | - | **R$ ", totalGeral:0:2, "** |")
      Escreval("| **Qtde total de despesas** | - | **", qtdGeral, "** |")
      Escreval("| **Valor medio geral** | - | **R$ ", mediaGeral:0:2, "** |")
      Escreval("| **Despesa de maior gasto** | **", nomeTipo[posMaiorTotal],
               "** | **R$ ", totalTipo[posMaiorTotal]:0:2, "** |")
      Escreval("| **Despesa de menor gasto** | **", nomeTipo[posMenorTotal],
               "** | **R$ ", totalTipo[posMenorTotal]:0:2, "** |")
      Escreval("| **Despesa de maior valor medio** | **", nomeTipo[posMaiorMedia],
               "** | **R$ ", (totalTipo[posMaiorMedia] / qtdTipo[posMaiorMedia]):0:2, "** |")

Observações sobre a saída em Markdown

  • A linha de alinhamento (|:---|---:|) deve vir imediatamente após o cabeçalho — é ela que define o alinhamento (:--- à esquerda para textos, ---: à direita para números e valores monetários).

  • A quantidade de colunas do cabeçalho, da linha de alinhamento e de cada linha de dados precisa ser idêntica, senão a tabela não renderiza.

  • O laço Para gera uma linha por tipo distinto, então a tabela cresce automaticamente conforme os tipos que o usuário cadastrar — nada é fixado no código.