K-means聚类是一种广泛使用的无监督学习算法,用于将数据点分成 K个聚类。它的主要目标是最小化每个聚类内数据点到聚类中心的距离之和,从而使得每个聚类内的数据点相似性最大,而不同聚类之间的差异性最大。
目录
1. K-means聚类的基本步骤
K-means聚类算法的执行过程通常包括以下几个步骤:
1.1 选择K个初始中心点
首先,选择 KKK 个数据点作为初始聚类中心。这些中心可以随机选择,也可以使用某种启发式方法确定。
1.2 将每个数据点分配给最近的聚类中心
对于每个数据点,计算其到每个聚类中心的欧几里得距离(或其他距离度量),然后将该数据点分配给距离最近的聚类。
1.3 计算每个聚类的新中心
一旦所有数据点都被分配到某个聚类中,重新计算每个聚类的中心。新中心是聚类内所有数据点的平均值。
1.4 检查是否收敛
检查聚类中心是否发生变化。如果聚类中心不再发生变化,或者变化量小于某个阈值,算法收敛并终止。否则,返回步骤1.2,继续迭代。
2. K-means算法的数学表述
K-means算法的目标是最小化以下目标函数:
其中:
- K是聚类的数量。
- Ci 表示第 i个聚类。
- xj 表示属于聚类 Ci 的第 j 个数据点。
- μi 是聚类 Ci 的中心。
- ∥⋅∥表示欧几里得距离。
目标函数 J 的含义是在所有聚类中最小化数据点到各自聚类中心的距离平方和。
3. K-means聚类的优点
- 简单易懂:K-means算法简单易于实现,计算速度快。
- 高效:对于大数据集,K-means算法的计算效率较高。
- 可伸缩性:K-means可以扩展到较大的数据集。
4. K-means聚类的缺点
- 对初始值敏感:K-means的结果依赖于初始聚类中心的选择,可能会陷入局部最优解。
- 需要指定K值:在使用K-means算法时,需要预先指定聚类的数量 KKK,这对于未知数据的聚类来说可能比较困难。
- 只适用于球形聚类:K-means假设每个聚类是球形的,这使得它在处理复杂形状的聚类时效果不佳。
5. K-means的应用
K-means聚类广泛应用于各种领域,如图像分割、客户分群、模式识别、市场营销等。在这些领域中,K-means可以帮助识别数据的内在结构,从而进行进一步的分析或决策。
6. K-means在魔方机器人的实际应用
K-means聚类是一种常见的无监督学习算法,旨在将数据点分成K个聚类。以下是K-means聚类在魔方机器人中如何实现的详细解释:
6.1. 初始化:
代码首先将所有点的分类索引设为-1。
使用魔方数据的最后6个点(第48到53列)作为初始聚类中心。
6.2. 聚类过程:
逐次进行9轮,每轮为每一类选择一个点:
对于每一个未分类的点,计算其到当前聚类中心的欧几里得距离。
将距离当前聚类中心最近的点归类到该聚类。
6.3. 更新聚类中心:
如果`CenterMove`为真,则重新计算每个聚类的中心:
计算新中心时,使用逐次平均法,即新的中心是当前中心与新加入点的平均值。
6.4. 重复上述过程:
重复选择最近点和更新聚类中心的步骤,直到所有点都被分配到某个聚类。
这个过程正是K-means聚类的核心思想,即通过不断迭代更新聚类中心和分配点,使得每个点到其所属聚类中心的距离最小化。
7. K-means在魔方机器人的详细代码解释
这段代码实现了一个聚类算法,旨在为魔方的色片进行分类,详细代码如下:
Cube CenterGrab(Cube cube, bool CenterMove)
{
// 清空Idx数组,将所有点的分类索引设为-1
for (int i = 0; i < 54; i++)
{
cube.Idx[i] = -1;
}
// 初始化聚类中心
int Center[3][6];
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 3; j++)
{
// 将CubeData的第48到53列作为初始聚类中心
Center[j][i] = cube.CubeData[j][48 + i];
}
}
// 逐次抢占9个点,每次为每一类选择一个点
for (int i = 0; i < 9; i++)
{
for (int c = 0; c < 6; c++)
{
int minDis = 1000000; // 初始化最小距离为一个很大的数
int p = -1; // 初始化点索引为-1
// 遍历所有点,找到距离当前聚类中心最近的点
for (int j = 0; j < 54; j++)
{
if (cube.Idx[j] == -1) // 只考虑尚未分类的点
{
int dis = 0; // 计算距离
for (int k = 0; k < 3; k++)
{
dis += (cube.CubeData[k][j] - Center[k][c]) * (cube.CubeData[k][j] - Center[k][c]);
}
if (dis < minDis)
{
minDis = dis;
p = j; // 更新最近点的索引
}
}
}
cube.Idx[p] = c; // 将最近点归类到当前类别
}
// 重新调整聚类中心
if (CenterMove)
{
int count[6] = { 0, 0, 0, 0, 0, 0 }; // 每类点的计数器
for (int i = 0; i < 54; i++)
{
int Class = cube.Idx[i]; // 获取点所属的类
if (Class != -1)
{
count[Class]++; // 更新类别计数
for (int j = 0; j < 3; j++)
{
// 计算新的聚类中心,逐次平均法
Center[j][Class] = (Center[j][Class] * (count[Class] - 1) + cube.CubeData[j][i]) / count[Class];
}
}
}
}
}
return cube; // 返回更新后的魔方
}
代码解释和注释
1. 清空`Idx`数组:
清空Idx数组,将所有点的分类索引设为-1
for (int i = 0; i < 54; i++)
{
cube.Idx[i] = -1;
}
将`Idx`数组中的所有值初始化为-1,表示所有的点尚未被分类。
2. 初始化聚类中心:
初始化聚类中心
int Center[3][6];
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 3; j++)
{
// 将CubeData的第48到53列作为初始聚类中心
Center[j][i] = cube.CubeData[j][48 + i];
}
}
`Center`数组用来存储每个类的中心点。
初始化时将`cube.CubeData`的第48到53列作为初始聚类中心。
3. 逐次抢占9个点:
逐次抢占9个点,每次为每一类选择一个点
for (int i = 0; i < 9; i++)
{
for (int c = 0; c < 6; c++)
{
int minDis = 1000000; // 初始化最小距离为一个很大的数
int p = -1; // 初始化点索引为-1
// 遍历所有点,找到距离当前聚类中心最近的点
for (int j = 0; j < 54; j++)
{
if (cube.Idx[j] == -1) // 只考虑尚未分类的点
{
int dis = 0; // 计算距离
for (int k = 0; k < 3; k++)
{
dis += (cube.CubeData[k][j] - Center[k][c]) * (cube.CubeData[k][j] - Center[k][c]);
}
if (dis < minDis)
{
minDis = dis;
p = j; // 更新最近点的索引
}
}
}
cube.Idx[p] = c; // 将最近点归类到当前类别
}
// 重新调整聚类中心
if (CenterMove)
{
int count[6] = { 0, 0, 0, 0, 0, 0 }; // 每类点的计数器
for (int i = 0; i < 54; i++)
{
int Class = cube.Idx[i]; // 获取点所属的类
if (Class != -1)
{
count[Class]++; // 更新类别计数
for (int j = 0; j < 3; j++)
{
// 计算新的聚类中心,逐次平均法
Center[j][Class] = (Center[j][Class] * (count[Class] - 1) + cube.CubeData[j][i]) / count[Class];
}
}
}
}
}
外层循环抢占9个点,每次为每一类选择一个点。
内层循环遍历所有点,找到距离当前聚类中心最近的未分类点。
将最近的点归类到当前类别。
若`CenterMove`为真,则重新调整聚类中心,采用逐次平均法更新中心点位置。
这个算法使用了一种类似于K-means的聚类方法,将魔方上的色片分成6类,并且在每次迭代后根据新的分类中心重新计算中心位置。
标签:cube,中心,means,int,魔方,Kmeans,++,色片,聚类 From: https://blog.csdn.net/bwwjsbzdwhy/article/details/139484746