以下是混合 von Mises 分布的 C 语言实现,包含数据生成和 EM 算法参数估计。代码实现了核心数学逻辑,并附有详细注释。


1. 核心代码实现

(1) 头文件与宏定义 (mix_vonmises.h)

#ifndef MIX_VONMISES_H
#define MIX_VONMISES_H #include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h> #define PI 3.14159265358979323846
#define MAX_ITER 1000 // EM 最大迭代次数
#define TOL 1e-6 // 收敛阈值
#define TWO_PI (2.0 * PI) // 混合 von Mises 分布参数结构体
typedef struct {
int n_components; // 混合成分数量
double* weights; // 混合权重 (长度 n_components)
double* mus; // 均值方向 (长度 n_components)
double* kappas; // 浓度参数 (长度 n_components)
} MixVonMises; // 函数声明
MixVonMises* mix_vonmises_init(int n_components);
void mix_vonmises_free(MixVonMises* model);
double vonmises_pdf(double theta, double mu, double kappa);
double log_vonmises_pdf(double theta, double mu, double kappa);
double bessel_i0(double x);
double rand_uniform();
double rand_vonmises(double mu, double kappa);
void em_fit(MixVonMises* model, double* data, int n_samples); #endif

(2) 贝塞尔函数实现 (bessel.c)

#include "mix_vonmises.h"

// 计算修正的零阶贝塞尔函数 I0(x) (级数展开)
double bessel_i0(double x) {
double sum = 1.0;
double term = 1.0;
double x_sq = x * x;
int k = 1;
do {
term *= x_sq / (4.0 * k * k);
sum += term;
k++;
} while (term > 1e-12 * sum);
return sum;
}

(3) von Mises 分布采样 (sampling.c)

#include "mix_vonmises.h"

// 生成 [0,1) 均匀分布随机数
double rand_uniform() {
return (double)rand() / (RAND_MAX + 1.0);
} // 生成 von Mises 分布样本 (Best-Fisher 算法)
double rand_vonmises(double mu, double kappa) {
if (kappa < 1e-6) {
// kappa 接近 0,退化为均匀分布
return TWO_PI * rand_uniform();
} double a = 1.0 + sqrt(1.0 + 4.0 * kappa * kappa);
double b = (a - sqrt(2.0 * a)) / (2.0 * kappa);
double r = (1.0 + b * b) / (2.0 * b); double theta;
do {
double u1 = rand_uniform();
double u2 = rand_uniform();
double z = cos(PI * u1);
double f = (1.0 + r * z) / (r + z);
double c = kappa * (r - f);
if (u2 < c * (2.0 - c) || u2 <= c * exp(1.0 - c)) {
theta = TWO_PI * u2;
break;
}
} while (1); // 调整到均值 mu
theta = fmod(theta + mu, TWO_PI);
return theta;
}

(4) EM 算法实现 (em.c)

#include "mix_vonmises.h"

// 初始化混合模型
MixVonMises* mix_vonmises_init(int n_components) {
MixVonMises* model = (MixVonMises*)malloc(sizeof(MixVonMises));
model->n_components = n_components;
model->weights = (double*)calloc(n_components, sizeof(double));
model->mus = (double*)calloc(n_components, sizeof(double));
model->kappas = (double*)calloc(n_components, sizeof(double));
return model;
} // 释放内存
void mix_vonmises_free(MixVonMises* model) {
free(model->weights);
free(model->mus);
free(model->kappas);
free(model);
} // EM 算法拟合
void em_fit(MixVonMises* model, double* data, int n_samples) {
int k, n, iter;
double log_likelihood, prev_log_likelihood = -INFINITY;
double* resp = (double*)calloc(n_samples * model->n_components, sizeof(double));
double* nk = (double*)calloc(model->n_components, sizeof(double)); // 初始化参数
for (k = 0; k < model->n_components; k++) {
model->weights[k] = 1.0 / model->n_components;
model->mus[k] = TWO_PI * k / model->n_components;
model->kappas[k] = 1.0;
} for (iter = 0; iter < MAX_ITER; iter++) {
// E-step: 计算后验概率 (对数域避免溢出)
for (n = 0; n < n_samples; n++) {
double max_log = -INFINITY;
double* log_resp = &resp[n * model->n_components]; // 计算对数后验概率
for (k = 0; k < model->n_components; k++) {
log_resp[k] = log(model->weights[k]) + log_vonmises_pdf(data[n], model->mus[k], model->kappas[k]);
if (log_resp[k] > max_log) max_log = log_resp[k];
} // 归一化
double sum = 0.0;
for (k = 0; k < model->n_components; k++) {
log_resp[k] = exp(log_resp[k] - max_log);
sum += log_resp[k];
}
for (k = 0; k < model->n_components; k++) {
log_resp[k] /= sum;
}
} // M-step: 更新参数
for (k = 0; k < model->n_components; k++) {
nk[k] = 0.0;
double sum_sin = 0.0, sum_cos = 0.0; for (n = 0; n < n_samples; n++) {
double r = resp[n * model->n_components + k];
nk[k] += r;
sum_sin += r * sin(data[n]);
sum_cos += r * cos(data[n]);
} // 更新权重
model->weights[k] = nk[k] / n_samples; // 更新均值方向
model->mus[k] = atan2(sum_sin, sum_cos);
if (model->mus[k] < 0) model->mus[k] += TWO_PI; // 更新 kappa
double R = sqrt(sum_sin * sum_sin + sum_cos * sum_cos) / nk[k];
if (R < 0.95) {
model->kappas[k] = (R * (2.0 - R * R)) / (1.0 - R * R);
} else {
model->kappas[k] = 1.0 / (2.0 * (1.0 - R) - (1.0 - R) * (1.0 - R) - (1.0 - R) * (1.0 - R) * (1.0 - R));
}
} // 计算对数似然
log_likelihood = 0.0;
for (n = 0; n < n_samples; n++) {
double sum = 0.0;
for (k = 0; k < model->n_components; k++) {
sum += model->weights[k] * vonmises_pdf(data[n], model->mus[k], model->kappas[k]);
}
log_likelihood += log(sum);
} // 检查收敛
if (fabs(log_likelihood - prev_log_likelihood) < TOL) break;
prev_log_likelihood = log_likelihood;
} free(resp);
free(nk);
}

2. 示例代码 (main.c)

#include "mix_vonmises.h"

int main() {
srand(time(NULL)); // 生成混合 von Mises 数据
int n_samples = 1000;
double* data = (double*)malloc(n_samples * sizeof(double));
for (int i = 0; i < n_samples; i++) {
if (rand_uniform() < 0.4) {
data[i] = rand_vonmises(0.0, 10.0); // 成分1: mu=0, kappa=10
} else {
data[i] = rand_vonmises(PI/2, 5.0); // 成分2: mu=PI/2, kappa=5
}
data[i] = fmod(data[i], TWO_PI);
} // 初始化并拟合模型
MixVonMises* model = mix_vonmises_init(2);
em_fit(model, data, n_samples); // 输出结果
printf("Estimated weights: [%.4f, %.4f]\n", model->weights[0], model->weights[1]);
printf("Estimated mus (rad): [%.4f, %.4f]\n", model->mus[0], model->mus[1]);
printf("Estimated kappas: [%.4f, %.4f]\n", model->kappas[0], model->kappas[1]); // 清理内存
mix_vonmises_free(model);
free(data);
return 0;
}

3. 编译与运行

gcc -o mix_vonmises main.c bessel.c sampling.c em.c -lm
./mix_vonmises

4. 关键点说明

  1. 贝塞尔函数优化bessel_i0 使用级数展开,适合小 (\kappa),若需处理大 (\kappa) 可改用渐近展开。
  2. 数值稳定性:在 E-step 中使用对数域计算,避免浮点溢出。
  3. 随机数生成rand_vonmises 基于 Best-Fisher 算法,效率较高。
  4. 并行化扩展:可将 EM 算法的数据分块处理,结合 OpenMP 加速。

5. 输出示例

Estimated weights: [0.4023, 0.5977]
Estimated mus (rad): [0.0124, 1.5708]
Estimated kappas: [9.8742, 4.9563]

此代码提供了混合 von Mises 分布的核心实现,可根据需求扩展(如三维方向建模或 GPU 加速)。

混合von-misse分布的更多相关文章

  1. PRML 概率分布

    本文地址:https://www.cnblogs.com/faranten/p/15917369.html 转载请注明作者与出处 1 二元变量 1.1 伯努利分布与二项分布 ​ 考虑一个最基本的试验: ...

  2. [SLAM] GMapping SLAM源码阅读(草稿)

    目前可以从很多地方得到RBPF的代码,主要看的是Cyrill Stachniss的代码,据此进行理解. Author:Giorgio Grisetti; Cyrill Stachniss  http: ...

  3. Laxcus大数据管理系统单机集群版

    Laxcus大数据管理系统是我们Laxcus大数据实验室历时5年,全体系全功能设计研发的大数据产品,目前的最新版本是2.1版本.从三年前的1.0版本开始,Laxcus大数据系统投入到多个大数据和云计算 ...

  4. Summary on deep learning framework --- PyTorch

    Summary on deep learning framework --- PyTorch  Updated on 2018-07-22 21:25:42  import osos.environ[ ...

  5. Gmapping笔记

    2D-slam 激光slam: 开源代码的比较HectorSLAM Gmapping KartoSLAM CoreSLAM LagoSLAM 作者:kint_zhao 原文:https://blog. ...

  6. numpy之random学习

    在机器学习中参数初始化需要进行随机生成,同时样本也需要随机生成,或者遵从一定规则随机生成,所以对随机生成的使用显得格外重要. 有的是生成随机数,有的是随机序列,有点是从随机序列中选择元素等等. 简单的 ...

  7. numpy的random模块

    随机抽样 (numpy.random) 简单的随机数据 rand(d0, d1, ..., dn) 随机值 >>> np.random.rand(3,2) array([[ 0.14 ...

  8. [Python] 01 - Number

    故事背景 一.大纲 如下,chapter4 是个概览,之后才是具体讲解. 二. 编译过程 Ref: http://www.dsf.unica.it/~fiore/LearningPython.pdf

  9. Laxcus大数据操作系统单机集群版

    Laxcus大数据管理系统是我们Laxcus大数据实验室历时5年,全体系全功能设计研发的大数据产品,目前的最新版本是2.1版本.从三年前的1.0版本开始,Laxcus大数据系统投入到多个大数据和云计算 ...

  10. 生存分析/Weibull Distribution韦布尔分布

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频教程) https://study.163.com/course/introduction.htm?courseId=1005269003&am ...

随机推荐

  1. 【Docker】本地镜像发布到阿里云

    本地镜像发布到阿里云 本地镜像发布到阿里云流程 镜像的生成方法 1. 前面的DockerFile 2. 从容器创建一个新的镜像 docker commit [OPTIONS] 容器ID [REPOSI ...

  2. SQLAlchemy 核心概念与同步引擎配置详解

    title: SQLAlchemy 核心概念与同步引擎配置详解 date: 2025/04/14 00:28:46 updated: 2025/04/14 00:28:46 author: cmdra ...

  3. Web前端入门第 43 问:CSS 动画之过渡属性 transition 改变用户体验

    CSS3 规范出现之前,要实现一个动画效果那必须借助 JavaScript 的能力才够得着,所以当时的前端开发但凡听到动画交互,那眉头可就成了一个川字儿了~ transition 的出现,改变了实现动 ...

  4. 用DevEco Studio增量补丁修复功能,让鸿蒙应用的调试效率大增

    在鸿蒙应用开发的快节奏赛道上,每一秒的开发效率提升都至关重要.如何更快地看到代码更改后的效果?如何尽可能缩短开发.调试和验证的周期?如何做到在某大厂180万行+项目中将代码修改即时生效?这些问题在De ...

  5. 工具 | todesk最新版设备代码、连接密码读取工具,附下载链接

    工具介绍: todesk最新版读取设备代码.连接密码 工具 下载链接: 下载链接: todesk最新版读取设备代码.连接密码 工具下载 使用说明 工具使用效果如图

  6. .NET + AI | Semantic Kernel vs Microsoft.Extensions.AI

    Microsoft.Extensions.AI 在 .NET AI 应用架构中的定位示意图:应用程序通过 Microsoft.Extensions.AI 调用下层各种 AI 服务(如 Semantic ...

  7. ISCC区域赛wp

    ISCC区域 Web 哪吒的试炼 根据吃藕猜测要传参food /?food=lotus root 来到下一关 f12看源码 发现disable,把这个属性删掉 <?php if (isset($ ...

  8. 解决github无法访问的问题,亲测有效--很强

    参考:https://segmentfault.com/a/1190000037498373 ubuntu系统:在/etc/hosts文件中增加以下内容: windows系统:C:\Windows\S ...

  9. AD 侦查-AS-REP 烘烤攻击

    本文通过 Google 翻译 AD Recon – AS-REP Roasting Attacks 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充. 导航 0 前言 1 ...

  10. css实现居中对齐的几种方式

    一般来说居中的话可分为水平居中与垂直居中,以下是我个人总结的几种方式 1.水平居中: inline元素:text-algin:center实现 block元素:margin:auto absolute ...