Calculando a Moda, Tenenbaum 1.2.1 b

Olá. Essa aqui é a resolução comentada do exercício 1.2.1 b da página 53 e 54 do livro Estrutura de dados em C que transcrevo aqui:

A MODA de um vetor de números é o número m no vetor que é repetido com maior frequência. Se mais de um número for repetido com frequência máxima igual, não existirá uma moda. Escreva uma função em C que aceite um vetor de números e retorne a moda ou uma indicação de que a moda não existe.

Tenenbaum, Langsam e Augesnstein (2013)

A solução desse aqui, é criar um array e contar o número de repetições que um elemento é inserido no array.

A maneira mais simples de representar isso, é considerar um array unidimensional de n elementos, digamos 5 elementos para facilitar a explicação.

Para agilizar a inserção de dados e não os precisar ficar gerando na mão, vamos nos valer de uma função aleatória que gere 5 números aleatórios e os insira no array.

Cada índice do array irá representar um número e o conteúdo do array irá representar a frequência que o número aparece na série.

int valores[5] = {6, 8, 12, 32, 7}

  • valores[0] = 6
  • valores[1] = 8
  • valores[2] = 12
  • valores[3] = 32
  • valores[0] = 7

Considerando o array acima, a moda será o valor 3, porque o valor 3 é o que teve a maior frequência, com 32 ocorrências.

Se tiver duas ou mais ocorrências de valor máximo, não há moda.

Antes de continuarmos:

Meu caro. Se esse artigo lhe ajudar e de alguma forma der um adianto para ti, vou ficar muito feliz se você clicar nos links de meus anunciantes e dar uma olhada no que eles tem para te oferecer. Grato :-).

Alguns pontos importantes:

O código dessa implementação, inclusive com os testes unitários, podem ser baixados diretamente de meu repositório no GitHub, clicando aqui.

Essa solução utiliza a biblioteca de teste unitário CuTest que é de código aberto e pode ser baixada diretamente no site do SourceForge, clicando aqui.

O legal dessa solução é que você pode apagar todo o código principal, deixar os testes unitários e ir implementando as funções no seu estilo, tendo o original para dar uma olhadinha caso queira. Basta remover o código principal, rodar o teste unitário, vai dar erro em tudo e voilá… Hora de sair implementando código e inclusive fazendo melhor que eu :-).

Conforme dito, para simplificar os testes e a execução do programa, eu automatizei a geração dos números aleatórios, o que facilita os testes e não precisamos ficar manualmente inventando valores para testar o programa ou para ver como ele funciona.

Como sempre, não estou esgotando o assunto, não é minha pretensão fazer a melhor e mais otimizada solução do mundo. É só um cara fazendo um passatempo e compartilhando até onde conseguiu chegar.

Se quiser falar comigo sobre, fique à vontade :-).

Estrutura da resolução:

Essa resolução ficou organizada no CodeBlocks em uma estrutura de diretórios de 5 arquivos, conforme print abaixo:

Estrutura de diretórios da resolução do exercício.

Temos o arquivo “main” e o arquivo “funcoes” que contém a solução do exercício, propriamente dito. Temos na pasta Teste_unitario os arquivos dos testes unitários “Teste_Moda_121b” e a biblioteca de testes open source chamada “CuTest” que foi obtida direto do site do SourceForge.

Essa estrutura que tenho utilizado, embora inicialmente pareça ser um pouco mais complicada, ela visa separar os trechos de código em arquivos individuais, melhorando a legibilidade do código. Para saber mais, basta dar uma olhadinha de como funciona a diretiva de pré processamento #include e #define, que serão utilizadas aqui.

Em outras palavras, a gente vai saindo do basicão e vai aprendendo coisas novas.

Descrição dos arquivos

main.c:

Conforme dito, o main.c é o arquivo de entrada e por onde o programa começa a rodar. Nele temos a lógica que controla se o programa irá rodar no modo normal ou irá rodar no modo “debug” e quando irá executar os testes unitários. Também temos a lógica indicando se o programa irá funcionar em modo de loop contínuo ou se irá rodar uma vez e parar.

O corpo do programa é bem simples. Ele simplesmente chama a função para gerar os números aleatórios e popular o array valores, faz o cálculo da moda e limpa o array valores com zeros, para o programa poder rodar novamente, lembrando que o no comentário do arquivo main.c, temos informações adicionais sobre como configurar o funcionamento do programa, lá no arquivo “funcoes.h”.

funcoes.h e funcoes.c:

O arquivo de header “funcoes.h” possui os protótipos das funções do arquivo funcoes.c e também possui as definições de processamento listadas abaixo:

  • “QUANTIDADE”: Número de elementos existentes na série (10).
  • “DADOS”: Número de dados que serão gerados para calcular a moda (100).
  • “DEBUG”: 0 – Operação normal, 1- Modo de testes unitários (0).
  • “CONTINUO”: 0 – Executa uma vez – Executa continuamente o programa (0).
  • “ITENS”: Número de itens a serem gerados (5).
  • “TAMANHO” : Número de valores da série a serem gerados (100).

Caso queira, altere os valores um por vez e veja o que acontece.

O arquivo “funcoes.c” contém a implementação do código com as funções:

  • gera_aleatorio: Responsável para popular aleatoriamente os elementos a serem analisados
  • zera_valores: Responsável por inicializar o array valores com zeros.
  • calcula_moda: Que faz o cálculo da moda, somente números positivos. Essa função retorna o valor inteiro da moda obtida ou -1 quando não há moda.
  • Por fim temos as impressões de tela com as funções printf.

Teste_Moda_121b.h e Teste_Moda_121b.c:

Esses arquivos implementam os testes unitários, descritos abaixo:

  • Teste_Gera_Aleatorio: Roda um teste de geração de números aleatórios. Ele verifica se a quantidade gerada coincide com a quantidade solicitada de números aleatórios.
  • Teste_Zera_Valores: Esse teste fornece um array populado com números aleatórios, chama a função zera_valores e confere se ela zerou de fato os valores do array.
  • Teste_Calcula_Moda: Esse teste fornece um array conhecido, solicita o cálculo da moda e confere com o valor esperado.
  • Teste_Calcula_Moda_Erro: Esse teste fornece um array conhecido, solicita o cálculo da moda e confere com o valor esperado, que deve ser uma indicação de erro.

Para rodar os testes unitários é só colocar no modo DEBUG = 1, compilar e executar o programa. Ou compilar e entrar com o depurador de código, executando passo a passo o código.

O programa:

main.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "funcoes.h"
#include "Teste_unitario/Teste_Moda_121b.h"
/*
Exercício 1.2.1 b.
A MODA de um vetor de números é o número m no vetor que é  repetido  com  maior
frequencia. Se mais de um número for repetido com freqüência máxima igual,  não
existirá uma moda. Escreva uma função em C que aceite um  vetor  de  números  e
retorne a moda ou uma indicação de que a moda não existe.

Fonte: Estruturas de Dados Usando C - Tenembaum, pg53 - 54.

Autor da solução: Renato de Pierri
https://github.com/renatopierri/algoritmos/tree/main/Tenembaum_moda_1_2_1_b

Para simplificar, ao inves de inserir manualmente os valores no array optei por
gerar automaticamente os valores do array.

Essa funcao "calcula_moda" faz o calculo apenas de numeros positivos.
Caso a serie nao tenha um valor para a moda, a funcao retornara o valor "-1".

funcoes.h tem as configuracoes da aplicacao e de teste, conforme abaixo:

QUANTIDADE: Nr de elementos na serie.
DADOS:      Nr de dados que serao gerados aleatoriamente para calcular a moda.
DEBUG:      0 - Operacao normal, 1 - Modo de testes.
CONTINUO:   0 - Roda uma vez,    1 - Modo continuo.

//Parametros de teste
#define ITENS 5     - Numero de itens a serem gerados.
#define TAMANHO 100 - Número de valores da serie a serem analisados.
*/

int main(){

    int limite, tamanho_valores, moda = 0;
    int valores[QUANTIDADE]={0};
    moda--;
    limite = QUANTIDADE - 1;
    tamanho_valores = sizeof(valores)/sizeof(int);
    do{
        if(DEBUG == 0){
            gera_aleatorio(valores,tamanho_valores,DADOS);
            moda = calcula_moda(valores,limite);
            zera_valores(valores,tamanho_valores);
        }else{
            printf("Rodando testes\n");
            RunAllTests();
            system("pause");
        }
    }while(CONTINUO == 1);
    return 0;
}

funcoes.h:

#ifndef FUNCOES_H_INCLUDED
#define FUNCOES_H_INCLUDED

/*
funcoes.h tem as configuracoes da aplicacao e de teste, conforme abaixo:

QUANTIDADE: Nr de elementos na serie.
DADOS:      Nr de dados que serao gerados aleatoriamente para calcular a moda.
DEBUG:      0 - Operacao normal, 1 - Modo de testes.
CONTINUO:   0 - Roda uma vez,    1 - Modo continuo.

//Parametros de teste
#define ITENS 5     - Numero de itens a serem gerados.
#define TAMANHO 100 - Número de valores da serie a serem analisados.
*/

#define QUANTIDADE 10
#define DADOS 100
#define DEBUG 0
#define CONTINUO 1

#define ITENS 5
#define TAMANHO 100

void gera_aleatorio(int valores[],int tamanho, int quantidade);
void zera_valores(int valores[],int tamanho);
int calcula_moda(int valores[],int limite);

#endif // FUNCOES_H_INCLUDED

funcoes.c:

#ifndef FUNCOES_C_INCLUDED
#define FUNCOES_C_INCLUDED

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "funcoes.h"
#include "Teste_unitario/CuTest.h"
#include "Teste_unitario/Teste_Moda_121b.h"

void gera_aleatorio(int valores[],int tamanho, int quantidade){
    int controle;
    int temp;
    for(controle = 0;controle<quantidade;controle++){
        temp = rand() % tamanho;
        valores[temp]=(valores[temp])+1;
    }
}

void zera_valores(int valores[],int tamanho){
    int controle;
    for (controle = 0;controle<tamanho;controle++){
        valores[controle] = 0;
    }
}

int calcula_moda(int valores[],int limite){
    int contagem, controle, moda = 0;
    char status[11];
    moda = 0;
    contagem = valores[0];

    //Calculando a moda
    for (controle = 1;controle <=limite ;controle ++){
        if(contagem == valores[controle]){
            strcpy(status,"nao existe");
        }
        if(contagem < valores[controle]){
            contagem = valores[controle];
            moda = controle;
            strcpy(status,"valida");
        }
    }

    // Imprimindo o resultado
    printf("Os valores sao: ");
    for(controle = 0; controle <=limite;controle++){
        printf("%03i\t",controle);
    }
    printf("\n");

    printf("A contagem  eh: ");
    for(controle = 0; controle <=limite;controle++){
        printf("%03i\t",valores[controle]);
    }
    printf("\n\n");

    if(strcmp(status,"nao existe")){
        printf("A moda eh o valor %02i, com %03i contagens, status: %s\n",moda,contagem,status);
    }else{
        printf("Moda %s.\n", status);
        moda = -1;
    }

    printf("\n\n");
    if(DEBUG == 0){
        system("pause");
    }
    system("cls");
    return  moda;
}
#endif // FUNCOES_C_INCLUDED

Teste_Moda_121b.h:

#ifndef TESTE_MODA_121B_H_INCLUDED
#define TESTE_MODA_121B_H_INCLUDED
#include "../funcoes.h"
#include "CuTest.h"
CuSuite*StrUtilGetSuite();
void RunAllTests(void);

// Testes unitarios

void Teste_Gera_Aleatorio(CuTest *tc);
void Teste_Zera_Valores(CuTest *tc);
void Teste_Calcula_Moda(CuTest *tc);
void Teste_Calcula_Moda_Erro(CuTest *tc);

#endif // TESTE_MODA_121B_H_INCLUDED

Teste_moda_121b.c:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Teste_Moda_121b.h"
#include "../funcoes.h"

CuSuite* StrUtilGetSuite();

void RunAllTests(void){
    CuString* output = CuStringNew();
    CuSuite* suite = CuSuiteNew();
    CuSuiteAddSuite(suite, StrUtilGetSuite());
    CuSuiteRun(suite);
    CuSuiteSummary(suite, output);
    CuSuiteDetails(suite, output);
    printf ("%s\n\n", output->buffer);
}

void Teste_Gera_Aleatorio(CuTest *tc){
    CuString* str = CuStringNew();
    int valores[ITENS];
    int tamanho = sizeof(valores)/sizeof(valores[0]) ;
    int quantidade = 100;
    int total;
    int controle;
    char resp1[10];
    char resp2[10];
    for (controle = 0;controle < tamanho; controle ++){
        valores[controle] = 0;
    }
    gera_aleatorio(valores, tamanho, quantidade);
    for (controle = 0;controle < tamanho; controle ++){
        total = total + valores[controle];
    }
    sprintf(resp1,"%i",(int)total);
    sprintf(resp2,"%i",(int)quantidade);
    CuStringAppend(str,resp1);
    CuAssertStrEquals(tc,resp2 , str->buffer);
}

void Teste_Zera_Valores(CuTest *tc){
    CuString* str = CuStringNew();
    int valores[TAMANHO];
    int tamanho = sizeof(valores)/sizeof(valores[0]);
    int controle;
    char resp1[TAMANHO+1]={'\0'};
    char resp2[TAMANHO+1]={'\0'};
    char temp[2];
    gera_aleatorio(valores, tamanho, 200);
    zera_valores(valores,tamanho);
    for(controle = 0;controle < tamanho;controle++){
        sprintf(temp,"%i",(int)valores[controle]);
        strcat(resp1,temp);
        strcat(resp2,"0");
    }
    CuStringAppend(str,resp1);
    CuAssertStrEquals(tc,resp2 , str->buffer);
}

void Teste_Calcula_Moda(CuTest *tc){
    CuString* str = CuStringNew();
    int valores[10]={5,8,6,3,1,1,20,3,4,4};
    int limite = 9;
    int resultado = 0;
    char resp1[3]={'\0'};
    resultado = calcula_moda(valores,limite);
    sprintf(resp1,"%i",(int)resultado);
    CuStringAppend(str,resp1);
    CuAssertStrEquals(tc, "6", str->buffer);
}

void Teste_Calcula_Moda_Erro(CuTest *tc){
    CuString* str = CuStringNew();
    int valores[10]={5,8,6,20,1,1,20,3,4,4};
    int limite = 9;
    int resultado = 0;
    char resp1[3]={'\0'};
    resultado = calcula_moda(valores,limite);
    sprintf(resp1,"%i",(int)resultado);
    CuStringAppend(str,resp1);
    CuAssertStrEquals(tc, "-1", str->buffer);
}

CuSuite*StrUtilGetSuite(){
    CuSuite* suite = CuSuiteNew();

    SUITE_ADD_TEST(suite,Teste_Gera_Aleatorio);
    SUITE_ADD_TEST(suite,Teste_Zera_Valores);
    SUITE_ADD_TEST(suite,Teste_Calcula_Moda);
    SUITE_ADD_TEST(suite,Teste_Calcula_Moda_Erro);
}

Como sempre, muito obrigado e não esqueça de clicar nos anúncios e ver o que os anunciantes tem de bom para te oferecer.

Publicado em 24/01/2023 por Renato de Pierri.

Bibliografia:

TENENBAUM, Aaron M.; LANGSAM, Yedidyah; AUGESNSTEIN, Moshe J.. Estruturas de Dados Usando C. São Paulo: Pearson, 2013. 884 p. ISBN 13: 978-85-346-0348-5, pg 53 – 54.

Last updated by at .