Escreva funções que calculam:
  • o desvio padrão dos elementos de um vetor.
Estas funções devem receber como parâmetro um vetor de números reais e um número inteiro indicando o tamanho deste vetor. Tente criar funções auxiliares para facilitar a organização do código.

Passo 1

O que o enunciado pede:
  • Escrever funções que calculem o desvio padrão dos elementos de um vetor.
Quais são as informações dadas (o que você terá disponível em cada chamada):
  • Um vetor de números reais: representado por um ponteiro/função que aponta para os elementos (por exemplo, double *v).
  • Um número inteiro indicando o tamanho do vetor: representado por int n.
O que as funções devem receber e retornar:
  • Cada função recebe o vetor e o inteiro com o tamanho.
  • As funções retornam um número real com o desvio padrão dos elementos (por exemplo, double).
Observações importantes que vêm do enunciado:
  • O enunciado permite criar funções auxiliares — vamos usar isso para organizar o código.
  • O enunciado não especifica se quer o desvio padrão populacional ou amostral, então vou fornecer as duas variações (populacional e amostral) e também uma versão numericamente estável (Welford) para casos práticos. Assim você terá todas as opções para a prova ou trabalho.

Passo 2

Conceitos matemáticos que precisamos entender (com fórmulas explicadas passo a passo).
  1. Média (média aritmética)
  • Para um vetor de n números x_1,x_2,\dots,x_n, a média \mu (ou \bar{x}) é a soma dos elementos dividida por n:
\mu = \frac{1}{n}\sum_{i=1}^{n} x_i.
  • O que cada símbolo significa:
    • x_i é o i-ésimo elemento do vetor.
    • n é o número total de elementos.
    • \sum_{i=1}^{n} x_i é a soma de todos os elementos.
  1. Variância e desvio padrão (intuição)
  • A variância mede o quão espalhados estão os valores em relação à média.
  • O desvio padrão é a raiz quadrada da variância; dá a medida de dispersão na mesma unidade dos dados.
  1. Variância populacional e amostral
  • Variância populacional:
\sigma^2 = \frac{1}{n}\sum_{i=1}^{n} (x_i - \mu)^2.
  • Desvio padrão populacional:
\sigma = \sqrt{\sigma^2} = \sqrt{\frac{1}{n}\sum_{i=1}^{n} (x_i - \mu)^2}.
  • Variância amostral (estimador não enviesado quando estamos com uma amostra):
s^2 = \frac{1}{n-1}\sum_{i=1}^{n} (x_i - \bar{x})^2.
  • Desvio padrão amostral:
s = \sqrt{s^2} = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n} (x_i - \bar{x})^2}.
  • Quando usar qual:
    • Use a fórmula com n se você interpreta o vetor como toda a população (todos os dados de interesse).
    • Use a fórmula com n-1 se o vetor for uma amostra e você quer uma estimativa não enviesada da variância da população.

Passo 3

Como transformar essas fórmulas em código — estratégia e escolhas.
Método direto (duas passagens):
  • Passo A: calcular a média \mu com \mu = \dfrac{1}{n}\sum x_i.
  • Passo B: calcular \sum (x_i - \mu)^2, dividir por n (ou n-1) e tirar a raiz quadrada.
  • Complexidade: O(n) tempo, duas passagens sobre o vetor, fácil de implementar e compreender.
Problema numérico do método direto:
  • Se os x_i forem grandes ou muito próximos, somas e diferenças podem perder precisão de ponto flutuante.
  • Para casos que exigem robustez numérica, há o algoritmo de Welford (uma passagem, estável numericamente).
Algoritmo de Welford (uma passagem, numericamente estável):
  • Atualiza média e soma dos quadrados das diferenças incrementalmente ao percorrer os dados.
  • Ao final fornece a variância sem necessitar de duas passagens.
  • Útil quando quiser robustez ou streaming (dados chegam um a um).
Escolha e organização:
  • Vou implementar:
    • Função auxiliar para média (usada pelo método direto).
    • Funções para variância/desvio populacional e amostral usando o método direto (duas passagens).
    • Função alternativa para desvio padrão usando Welford (uma passagem, estável).
  • Dessa forma você tem a versão simples para a prova e a versão robusta para aplicações reais.

Passo 4

Explicando passo a passo as funções auxiliares que vamos criar (o que cada função faz, parâmetros e retorno).
  1. Função média
  • Protótipo: double mean(const double *v, int n);
  • Parâmetros:
    • v: ponteiro para o primeiro elemento do vetor (assume n>0).
    • n: número de elementos.
  • Retorno: a média \mu calculada por \mu = \dfrac{1}{n}\sum_{i=1}^{n} v[i].
  1. Função variância populacional (duas passagens)
  • Protótipo: double variance_population(const double *v, int n);
  • Lógica:
    • calcular \mu usando mean.
    • calcular sumsq = sum (v[i] - mu)*(v[i] - mu).
    • retornar \dfrac{\text{sumsq}}{n}.
  1. Função desvio padrão populacional
  • Protótipo: double stddev_population(const double *v, int n);
  • Lógica:
    • chamar variance_population e retornar sqrt(variance).
  1. Função variância amostral (duas passagens)
  • Protótipo: double variance_sample(const double *v, int n);
  • Observação: exige n >= 2. Se n < 2, não há como calcular (divisão por zero ou indefinido) — a função deve tratar esse caso (por exemplo, retornar NAN ou 0 conforme política). Vou fazer retornar NAN e documentar isso.
  1. Função desvio padrão amostral
  • Protótipo: double stddev_sample(const double *v, int n);
  • Lógica:
    • chamar variance_sample e retornar sqrt(variance).
  1. Função Welford (uma passagem)
  • Protótipo: double stddev_welford(const double *v, int n, int sample);
  • Parâmetros:
    • sample é 0 para desvio populacional ( n no denominador) e 1 para desvio amostral ( n-1).
  • Lógica:
    • inicializar mean = 0.0, M2 = 0.0, count = 0.
    • para cada x no vetor:
      • count += 1
      • delta = x - mean
      • mean += delta / count
      • delta2 = x - mean
      • M2 += delta * delta2
    • ao final, se sample então variance = M2 / (count - 1) (se count >= 2), senão variance = M2 / count (se count >= 1).
    • retornar sqrt(variance).

Passo 5

Código em C com todas as funções comentadas. Leia com atenção: este é o resultado que você pode usar diretamente na sua prova/trabalhos. O código trata casos de entrada inválida (como n <= 0 ou n < 2 no caso amostral) retornando NAN quando o resultado não é definido.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* Função auxiliar: calcula a média de um vetor v de tamanho n.
   Retorna NAN se n <= 0. */
double mean(const double *v, int n) {
    if (n <= 0) return NAN;
    double s = 0.0;
    for (int i = 0; i < n; ++i) s += v[i];
    return s / (double)n;
}

/* Variância populacional: denomina por n.
   Retorna NAN se n <= 0. */
double variance_population(const double *v, int n) {
    if (n <= 0) return NAN;
    double mu = mean(v, n);
    double sumsq = 0.0;
    for (int i = 0; i < n; ++i) {
        double d = v[i] - mu;
        sumsq += d * d;
    }
    return sumsq / (double)n;
}

/* Desvio padrão populacional: raiz da variância populacional.
   Retorna NAN se n <= 0. */
double stddev_population(const double *v, int n) {
    double var = variance_population(v, n);
    if (isnan(var)) return NAN;
    return sqrt(var);
}

/* Variância amostral (não enviesada): denomina por n-1.
   Retorna NAN se n < 2. */
double variance_sample(const double *v, int n) {
    if (n < 2) return NAN; /* não definido para n < 2 */
    double mu = mean(v, n);
    double sumsq = 0.0;
    for (int i = 0; i < n; ++i) {
        double d = v[i] - mu;
        sumsq += d * d;
    }
    return sumsq / (double)(n - 1);
}

/* Desvio padrão amostral: raiz da variância amostral.
   Retorna NAN se n < 2. */
double stddev_sample(const double *v, int n) {
    double var = variance_sample(v, n);
    if (isnan(var)) return NAN;
    return sqrt(var);
}

/* Welford: algoritmo numericamente estável (uma passagem).
   Se sample == 0 => calcula desvio populacional (divide por n).
   Se sample != 0 => calcula desvio amostral (divide por n-1).
   Retorna NAN para entradas inválidas (n <= 0, ou n < 2 quando sample != 0). */
double stddev_welford(const double *v, int n, int sample) {
    if (n <= 0) return NAN;
    if (sample && n < 2) return NAN;

    double mean = 0.0;
    double M2 = 0.0;
    int count = 0;

    for (int i = 0; i < n; ++i) {
        double x = v[i];
        count += 1;
        double delta = x - mean;
        mean += delta / (double)count;
        double delta2 = x - mean;
        M2 += delta * delta2;
    }

    double variance;
    if (sample) {
        variance = M2 / (double)(count - 1); /* n-1 */
    } else {
        variance = M2 / (double)count;       /* n */
    }
    return sqrt(variance);
}

/* Exemplo de uso (main) */
int main(void) {
    double v[] = {1.0, 2.0, 3.0, 4.0};
    int n = sizeof(v) / sizeof(v[0]);

    double mu = mean(v, n);
    double sd_pop = stddev_population(v, n);
    double sd_samp = stddev_sample(v, n);
    double sd_welford_pop = stddev_welford(v, n, 0);
    double sd_welford_samp = stddev_welford(v, n, 1);

    printf("Média = %f\n", mu);
    printf("Desvio padrão populacional = %f\n", sd_pop);
    printf("Desvio padrão amostral = %f\n", sd_samp);
    printf("Welford (pop) = %f\n", sd_welford_pop);
    printf("Welford (samp) = %f\n", sd_welford_samp);

    return 0;
}
Comentários sobre o código e recomendações para a prova:
  • Para provas teóricas onde pedem “desvio padrão dos elementos do vetor” e não especificam, verifique o enunciado: se o vetor representa a população inteira, use stddev_population; se for uma amostra use stddev_sample.
  • O método das duas passagens (média depois soma de quadrados das diferenças) é simples e suficiente para a maioria dos exercícios de prova.
  • Se a questão envolve dados muito grandes em magnitude ou pede algoritmo em streaming, diga que usaria Welford — na prova, explique rapidamente como funciona (ver Passo 4) e escreva a versão apropriada.

Passo 6

Exemplo numérico rápido (para treinar e confimar resultados):
  • Vetor: x = (1,2,3,4). Aqui n=4.
  • Média:
\mu = \frac{1+2+3+4}{4} = \frac{10}{4} = 2,5.
  • Soma dos quadrados das diferenças:
\sum (x_i-\mu)^2 = (1-2{,}5)^2 + (2-2{,}5)^2 + (3-2{,}5)^2 + (4-2{,}5)^2 = 2{,}25 + 0{,}25 + 0{,}25 + 2{,}25 = 5.
  • Variância populacional: \sigma^2 = \dfrac{5}{4} = 1{,}25. Desvio padrão populacional: \sigma = \sqrt{1{,}25} \approx 1{,}11803.
  • Variância amostral: s^2 = \dfrac{5}{3} \approx 1{,}6667. Desvio padrão amostral: s \approx 1{,}29099.
  • O programa de exemplo (main) imprime esses valores; compare para checar que tudo está certo.

Resposta

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* Função auxiliar: calcula a média de um vetor v de tamanho n.
   Retorna NAN se n <= 0. */
double mean(const double *v, int n) {
    if (n <= 0) return NAN;
    double s = 0.0;
    for (int i = 0; i < n; ++i) s += v[i];
    return s / (double)n;
}

/* Variância populacional: denomina por n.
   Retorna NAN se n <= 0. */
double variance_population(const double *v, int n) {
    if (n <= 0) return NAN;
    double mu = mean(v, n);
    double sumsq = 0.0;
    for (int i = 0; i < n; ++i) {
        double d = v[i] - mu;
        sumsq += d * d;
    }
    return sumsq / (double)n;
}

/* Desvio padrão populacional: raiz da variância populacional.
   Retorna NAN se n <= 0. */
double stddev_population(const double *v, int n) {
    double var = variance_population(v, n);
    if (isnan(var)) return NAN;
    return sqrt(var);
}

/* Variância amostral (não enviesada): denomina por n-1.
   Retorna NAN se n < 2. */
double variance_sample(const double *v, int n) {
    if (n < 2) return NAN; /* não definido para n < 2 */
    double mu = mean(v, n);
    double sumsq = 0.0;
    for (int i = 0; i < n; ++i) {
        double d = v[i] - mu;
        sumsq += d * d;
    }
    return sumsq / (double)(n - 1);
}

/* Desvio padrão amostral: raiz da variância amostral.
   Retorna NAN se n < 2. */
double stddev_sample(const double *v, int n) {
    double var = variance_sample(v, n);
    if (isnan(var)) return NAN;
    return sqrt(var);
}

/* Welford: algoritmo numericamente estável (uma passagem).
   Se sample == 0 => calcula desvio populacional (divide por n).
   Se sample != 0 => calcula desvio amostral (divide por n-1).
   Retorna NAN para entradas inválidas (n <= 0, ou n < 2 quando sample != 0). */
double stddev_welford(const double *v, int n, int sample) {
    if (n <= 0) return NAN;
    if (sample && n < 2) return NAN;

    double mean = 0.0;
    double M2 = 0.0;
    int count = 0;

    for (int i = 0; i < n; ++i) {
        double x = v[i];
        count += 1;
        double delta = x - mean;
        mean += delta / (double)count;
        double delta2 = x - mean;
        M2 += delta * delta2;
    }

    double variance;
    if (sample) {
        variance = M2 / (double)(count - 1); /* n-1 */
    } else {
        variance = M2 / (double)count;       /* n */
    }
    return sqrt(variance);
}

/* Exemplo de uso (main) */
int main(void) {
    double v[] = {1.0, 2.0, 3.0, 4.0};
    int n = sizeof(v) / sizeof(v[0]);

    double mu = mean(v, n);
    double sd_pop = stddev_population(v, n);
    double sd_samp = stddev_sample(v, n);
    double sd_welford_pop = stddev_welford(v, n, 0);
    double sd_welford_samp = stddev_welford(v, n, 1);

    printf("Média = %f\n", mu);
    printf("Desvio padrão populacional = %f\n", sd_pop);
    printf("Desvio padrão amostral = %f\n", sd_samp);
    printf("Welford (pop) = %f\n", sd_welford_pop);
    printf("Welford (samp) = %f\n", sd_welford_samp);

    return 0;
}

Exercícios relacionados