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).
- Média (média aritmética)
- Para um vetor de
números , a média (ou ) é a soma dos elementos dividida por :
- O que cada símbolo significa:
-
é o i-ésimo elemento do vetor. -
é o número total de elementos. -
é a soma de todos os elementos.
-
- 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.
- Variância populacional e amostral
- Variância populacional:
- Desvio padrão populacional:
- Variância amostral (estimador não enviesado quando estamos com uma amostra):
- Desvio padrão amostral:
- Quando usar qual:
- Use a fórmula com
se você interpreta o vetor como toda a população (todos os dados de interesse). - Use a fórmula com
se o vetor for uma amostra e você quer uma estimativa não enviesada da variância da população.
- Use a fórmula com
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
com . - Passo B: calcular
, dividir por (ou ) e tirar a raiz quadrada. - Complexidade:
tempo, duas passagens sobre o vetor, fácil de implementar e compreender.
Problema numérico do método direto:
- Se os
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).
- Função média
- Protótipo:
double mean(const double *v, int n); - Parâmetros:
v: ponteiro para o primeiro elemento do vetor (assumen>0).n: número de elementos.
- Retorno: a média
calculada por .
- Função variância populacional (duas passagens)
- Protótipo:
double variance_population(const double *v, int n); - Lógica:
- calcular
usandomean. - calcular
sumsq = sum (v[i] - mu)*(v[i] - mu). - retornar
.
- calcular
- Função desvio padrão populacional
- Protótipo:
double stddev_population(const double *v, int n); - Lógica:
- chamar
variance_populatione retornarsqrt(variance).
- chamar
- Função variância amostral (duas passagens)
- Protótipo:
double variance_sample(const double *v, int n); - Observação: exige
n >= 2. Sen < 2, não há como calcular (divisão por zero ou indefinido) — a função deve tratar esse caso (por exemplo, retornarNANou0conforme política). Vou fazer retornarNANe documentar isso.
- Função desvio padrão amostral
- Protótipo:
double stddev_sample(const double *v, int n); - Lógica:
- chamar
variance_samplee retornarsqrt(variance).
- chamar
- Função Welford (uma passagem)
- Protótipo:
double stddev_welford(const double *v, int n, int sample); - Parâmetros:
sampleé0para desvio populacional ( no denominador) e1para desvio amostral ( ).
- Lógica:
- inicializar
mean = 0.0,M2 = 0.0,count = 0. - para cada
xno vetor:count += 1delta = x - meanmean += delta / countdelta2 = x - meanM2 += delta * delta2
- ao final, se
sampleentãovariance = M2 / (count - 1)(secount >= 2), senãovariance = M2 / count(secount >= 1). - retornar
sqrt(variance).
- inicializar
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 usestddev_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:
. Aqui . - Média:
- Soma dos quadrados das diferenças:
- Variância populacional:
. Desvio padrão populacional: - Variância amostral:
Desvio padrão amostral: - 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;
}