C 语言实现独立样本 t 检验和配对样本 t 检验

源代码链接:https://github.com/Illusionna/tiny-stats/blob/main/t-test.c

cpp 复制代码
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>


// Box-Muller Transform.
double random_normal(double mu, double sigma) {
    static double U;
    static double V;
    static int phase = 0;
    double Z;

    if (phase == 0) {
        U = (rand() + 1.0) / (RAND_MAX + 1.0);
        V = (rand() + 1.0) / (RAND_MAX + 1.0);
        Z = sqrt(-2.0 * log(U)) * sin(2.0 * M_PI * V);
    } else {
        Z = sqrt(-2.0 * log(U)) * cos(2.0 * M_PI * V);
    }
    phase = 1 - phase;

    return Z * sigma + mu;
}


double incbeta(double a, double b, double x) {
    static const double epsilon = 1.0e-30;
    static const double condition = 1.0e-8;

    if (x < 0.0 || x > 1.0) return INFINITY;
    if (x > (a + 1.0) / (a + b + 2.0)) return 1.0 - incbeta(b, a, 1.0 - x);

    double f = 1.0;
    double c = 1.0;
    double d = 0.0;
    const double lbeta_ab = lgamma(a) + lgamma(b) - lgamma(a + b);
    const double front = exp(a * log(x) + b * log(1.0 - x) - lbeta_ab) / a;

    for (int i = 0; i <= 200; ++i) {
        int m = i / 2;
        double numerator;

        if (i == 0) numerator = 1.0;
        else if (i % 2 == 0) numerator = (m * (b - m) * x) / ((a + 2.0 * m - 1.0) * (a + 2.0 * m));
        else numerator = - ((a + m) * (a + b + m) * x) / ((a + 2.0 * m) * (a + 2.0 * m + 1));

        d = 1.0 + numerator * d;
        if (fabs(d) < epsilon) d = epsilon;
        d = 1.0 / d;

        c = 1.0 + numerator / c;
        if (fabs(c) < epsilon) c = epsilon;

        double cd = c * d;
        f = f * cd;
        if (fabs(1.0 - cd) < condition) return front * (f - 1.0);
    }
    return INFINITY;
}


double cdf_student_t(double t, double df) {
    /*
    double x = df / (t * t + df);
    double p = 0.5 * incbeta(df / 2.0, 0.5, x);
    return (t > 0) ? (1.0 - p) : p;
    */
    if (df <= 0.0) return NAN;
    if (t == 0.0) return 0.5;
    double cache = sqrt(t * t + df);
    return incbeta(df / 2.0, df / 2.0, (t + cache) / (2.0 * cache));
}


double ttest_independent(double *group1, double *group2, int n1, int n2, int equal_var) {
    double sum1 = 0.0;
    double sum2 = 0.0;
    for (int i = 0; i < n1; ++i) sum1 = sum1 + group1[i];
    for (int i = 0; i < n2; ++i) sum2 = sum2 + group2[i];
    double mean1 = sum1 / n1;
    double mean2 = sum2 / n2;

    double var1 = 0.0;
    double var2 = 0.0;
    for (int i = 0; i < n1; ++i) var1 = var1 + (group1[i] - mean1) * (group1[i] - mean1);
    for (int i = 0; i < n2; ++i) var2 = var2 + (group2[i] - mean2) * (group2[i] - mean2);
    var1 = var1 / (n1 - 1);
    var2 = var2 / (n2 - 1);

    double t;
    double df;

    if (equal_var) {
        df = n1 + n2 - 2.0;
        double pooled_var = ((n1 - 1.0) * var1 + (n2 - 1.0) * var2) / df;
        t = (mean1 - mean2) / sqrt(pooled_var * (1.0 / n1 + 1.0 / n2));
    } else {
        double satterthwaite1 = var1 / n1;
        double satterthwaite2 = var2 / n2;
        t = (mean1 - mean2) / sqrt(satterthwaite1 + satterthwaite2);
        double numerator = (satterthwaite1 + satterthwaite2) * (satterthwaite1 + satterthwaite2);
        double denominator = (satterthwaite1 * satterthwaite1) / (n1 - 1.0) + (satterthwaite2 * satterthwaite2) / (n2 - 1.0);
        df = numerator / denominator;
    }

    double cdf = cdf_student_t(t, df);
    return 2.0 * (cdf > 0.5 ? 1.0 - cdf : cdf);
}


double ttest_paired(double *state1, double *state2, int n) {
    if (n < 2) return NAN;

    double sum_diff = 0.0;
    double sum_square_diff = 0.0;
    for (int i = 0; i < n; ++i) {
        double diff = state1[i] - state2[i];
        sum_diff = sum_diff + diff;
        sum_square_diff = sum_square_diff + diff * diff;
    }

    double mean_diff = sum_diff / n;
    double var_diff = (sum_square_diff - (sum_diff * sum_diff / n)) / (n - 1);
    double std_error = sqrt(var_diff / n);

    double t = mean_diff / std_error;
    double cdf = cdf_student_t(t, n - 1.0);
    double p = 2.0 * (cdf > 0.5 ? 1.0 - cdf : cdf);
    return p;
}



int main(int argc, char *argv[], char *envs[]) {
    double p;
    srand((unsigned int)time(NULL));

    int n1 = 6;
    int n2 = 4;
    double mu1 = 75.0;
    double mu2 = 82.0;
    double sigma1 = 8.0;
    double sigma2 = 8.0;

    double category1[n1];
    double category2[n2];

    for (int i = 0; i < n1; ++i) category1[i] = random_normal(mu1, sigma1);
    for (int i = 0; i < n2; ++i) category2[i] = random_normal(mu2, sigma2);
    p = ttest_independent(category1, category2, n1, n2, sigma1 == sigma2);
    printf("Independent Samples T-test:\x1b[32m p = %.12lf\x1b[0m\n", p);

    double before[] = {22, 20, 19, 24, 25, 25, 28, 22, 30, 27, 24, 18, 16, 19, 19, 28, 24, 25, 25, 23};
    double after[] = {24, 22, 19, 22, 28, 26, 28, 24, 30, 29, 25, 20, 17, 18, 18, 28, 26, 27, 27, 24};
    p = ttest_paired(before, after, sizeof(after) / sizeof(after[0]));
    printf("Paired Sample T-test:\x1b[32m p = %.12lf\x1b[0m\n", p);

    return 0;
}
相关推荐
郝学胜-神的一滴41 分钟前
深入解析C/S模型下的TCP通信流程:从握手到挥手的技术之旅
linux·服务器·c语言·网络·网络协议·tcp/ip
island13142 小时前
CANN GE(图引擎)深度解析:计算图优化管线、内存静态规划与异构 Stream 调度机制
c语言·开发语言·神经网络
Once_day3 小时前
C++之《程序员自我修养》读书总结(1)
c语言·开发语言·c++·程序员自我修养
坚果派·白晓明4 小时前
在鸿蒙设备上快速验证由lycium工具快速交叉编译的C/C++三方库
c语言·c++·harmonyos·鸿蒙·编程语言·openharmony·三方库
风指引着方向4 小时前
图编译优化全链路:CANN graph-engine 仓库技术拆解
c语言
C++ 老炮儿的技术栈5 小时前
VS2015 + Qt 实现图形化Hello World(详细步骤)
c语言·开发语言·c++·windows·qt
Once_day5 小时前
C++之《Effective C++》读书总结(4)
c语言·c++·effective c++
晓13135 小时前
第七章 【C语言篇:文件】 文件全面解析
linux·c语言·开发语言
梵刹古音5 小时前
【C语言】 指针基础与定义
c语言·开发语言·算法