数据挖掘Java——Kmeans算法的实现

2023-05-16

一、K-means算法的前置知识

k-means算法,也被称为k-平均或k-均值,是一种得到最广泛使用的聚类算法。相似度的计算根据一个簇中对象的平均值来进行。算法首先随机地选择k个对象,每个对象初始地代表了一个簇的平均值或中心。对剩余的每个对象根据其与各个簇中心的距离,将它赋给最近的簇。然后重新计算每个簇的平均值。这个过程不断重复,直到准则函数收敛。

聚类就是将数据对象分组成多个类或簇,划分的原则是在同一个簇中的对象之间具有较高的相似度,而不同簇中的对象差别较大。与分类不同的是,聚类操作中要划分的类是事先未知的,类的形式完全是数据驱动的,属于一种无指导的学习方法。

聚类分析源于许多研究领域,包括数据挖掘、统计学、机器学习、模式识别等。它是数据挖掘中的一个功能,但也能作为一个独立的工具来获得数据分布的情况,概括出每个簇的特点,或者集中注意力对特定的某些簇作进一步分析。此外,聚类分析也可以作为其他分析算法(如关联规则、分类等)的预处理步骤,这些算法在生成的簇上进行处理。

聚类:聚类是一个将数据集中在某些方面相似的数据成员进行分类组织的过程,聚类就是一种发现这种内在结构的技术,聚类技术经常被称为无监督学习。
K-means聚类:K-means聚类是最著名的划分聚类算法,由于简洁和效率使得他成为所有聚类算法中最广泛使用的。给定一个数据点集合和需要的聚类数目k,k由用户指定,k均值算法根据某个距离函数反复把数据分入k个聚类中。

二、K-means算法的基本思想

K-means聚类算法是先随机选取K个对象作为初始的聚类中心。然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小。

三、K-means算法的例子

K-means算法例子
在这里插入图片描述
在这里插入图片描述

四、K-means算法的实现过程

实验内容
请对下表中的数据进行k-mean聚类,距离为欧氏距离,k=3
在这里插入图片描述
实验思路
(1)定义Point类,Point类中含横坐标x,纵坐标y等属性,包含静态方法getIsSame():判断两个Point类对象是否相同、calculateDistance()方法:计算两个Point类对象之间的距离(欧氏距离)、calculateMHDDistance()方法:计算两个Point类对象之间的距离(曼哈顿距离)。定义Cluster类,在Cluster类中包含属性核心点corePoint,簇内的所有点的集合sameList。
(2)定义初始数据集dataList,定义簇的数目k,调用initDataList()方法进行初始化数据集,调用getInitCluster()方法进行初始化簇。getInitCluster()方法主要作用是获取任意k个对象作为初始簇中心,将含有k个簇的集合返回。在getInitCluster()方法体内部,定义clusterList集合用于存放k个簇,调用getRandomArray()方法获取含有k个不重复随机数的数组randomArray,数据集中k个对象的下标存放在randomArray数组中,遍历数组randomArray,取出k个任意下标的Point类对象作为相应簇cluster的核心对象点,并将每一次循环定义和实例化后的cluster添加到clusterList中,最终将clusterList集合返回。
(3)进入while循环,遍历数据集dataList中的每一项point,调用getBelongCluster()方法获取point属于的那个簇在clusterList中的下标index,取出clusterList中指定下标index的簇,将点point加入到该簇的sameList中。然后遍历数据集结束后,调用calculateClusterCore()方法计算出新的簇中心并判断出簇集合中每个簇的点集合是否有发生变化,若未发生变化,则跳出while循环,表明K-means聚类结束,反之则进入下次while循环,在遍历数据集之前,要将clusterList集合中的每一项cluster的sameList集合清空。
(4)遍历clusetrList集合,将集合中的每一项cluster输出即可。
(5)getBelongCluster()方法主要作用是获取某个点属于哪个簇的下标。在方法体内部,定义了变量closestDistance和变量resultClusterIndex分别用于存放point距离簇中心最近的距离,以及point属于的哪个簇的下标。遍历簇集合clusterList,调用Point类内静态方法calculateDistance()计算点point距离簇cluster核心点的距离赋值给distance,将第一次遍历得到的distance值赋值给cloestDistance,后面的遍历如果distance小于closestDistance,就将distance赋值给closestDistance,同时将index赋值给resultClusterIndex,循环遍历结束,最终将resultClusterIndex返回。
(6)calculateClusterCore()方法主要作用是计算出新的簇中心并返回簇的点集合是否有变化。在方法体内部定义标志变量flag,然后遍历clusterList集合中的每一项cluster,定义变量sumX和变量sumY分别用于存放簇中点集合所有的x坐标之和,以及簇中点集合所有的y坐标之和,对sumX和sumY求均值后赋值给新的簇中心点clusterCore,调用Point类内静态方法getIsSame()判断clusterCore和原簇中心是否相同,若不相同则将flag赋值为true。当遍历簇集合循环结束后,将flag值返回。要注意的是这里形参类型是List集合,传的是List集合的地址,在方法体内对集合进行修改则会导致实参的值也发生改变。

实现源码

Cluster类
package com.data.mining.entity;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;

@Data
public class Cluster {
    private Point corePoint;
    private List<Point> sameList = new ArrayList<>();

    public Cluster(){}

    public Cluster(Point cp){
        corePoint = cp;
    }
}

Point类
package com.data.mining.entity;

import lombok.Data;

@Data
public class Point {
    private double x;
    private double y;

    public Point(){}

    public Point(double x, double y){
        this.x = x;
        this.y = y;
    }

    public static boolean getIsSame(Point p1, Point p2){
        if (p1.getX() == p2.getX() && p1.getY() == p2.getY()) return true;
        return false;
    }

    public static double calculateDistance(Point p1, Point p2){
        double xDistance = p1.getX() - p2.getX();
        double yDistance = p1.getY() - p2.getY();
        double tmp = xDistance * xDistance + yDistance * yDistance;
        return Math.sqrt(tmp);
    }

    public static double calculateMHDDistance(Point p1, Point p2){
        return Math.abs(p1.getX() - p2.getX()) + Math.abs(p1.getY() - p2.getY());
    }

}

K-means算法实现代码
package com.data.mining.main;

import com.data.mining.entity.Cluster;
import com.data.mining.entity.Point;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class Kmeans {
    //定义初始数据集
    public static List<Point> dataList = new ArrayList<>();
    //定义簇的数目
    public static Integer k = 3;

    public static void main(String[] args) {
        //初始化数据集和初始簇
        initDataList();
        List<Cluster> clusterList = getInitCluster();
        while(true){
            for (int j = 0; j < k; j++) {
                clusterList.get(j).getSameList().clear();
            }
            for (Point point : dataList) {
                int index = getBelongCluster(point, clusterList); //获取point属于的那个簇在clusterList中的下标
                clusterList.get(index).getSameList().add(point); //把point加入到clusterList的对应簇中;
            }
            if (!calculateClusterCore(clusterList)) break;
        }
        for (Cluster cluster : clusterList) {
            System.out.println(cluster);
        }
    }

    /**
     * 计算出新的簇中心并返回簇的点集合是否有变化
     * @param clusterList
     * @return
     */
    public static boolean calculateClusterCore(List<Cluster> clusterList){
        boolean flag = false;
        //遍历簇集合中的每一项,更新其簇中心
        for (Cluster cluster : clusterList) {
            List<Point> sameList = cluster.getSameList();
            double sumX = 0; //存放簇中点集合所有的X坐标之和
            double sumY = 0; //存放簇中点集合所有的Y坐标之和
            for (Point point : sameList) {
                sumX += point.getX();
                sumY += point.getY();
            }
            //更新簇的中心
            Point clusterCore = new Point(sumX * 1.0 / sameList.size(), sumY * 1.0 / sameList.size());
            if (!Point.getIsSame(clusterCore, cluster.getCorePoint())) flag = true;
            cluster.setCorePoint(clusterCore);
        }
        return flag;
    }

    /**
     * 获取某个点属于哪个簇的下标
     * @param point
     * @return
     */
    public static int getBelongCluster(Point point, List<Cluster> clusterList){
        double closestDistance = 0.0; //存放point距离簇中心最近的距离
        int resultClusterIndex = 0; //存放point属于的那个簇的下标
        int index = 0;
        //遍历簇集合,计算point到簇中心的距离,找出point属于的簇
        for (Cluster cluster : clusterList) {
            double distance = Point.calculateDistance(point, cluster.getCorePoint());
            if (index == 0) closestDistance = distance;
            if (distance < closestDistance){
                closestDistance = distance;
                resultClusterIndex = index;
            }
            index++;
        }
        return resultClusterIndex;
    }

    /**
     * 获取任意k个对象作为初始簇中心,将含有k个簇的集合返回
     * @return
     */
    public static List<Cluster> getInitCluster(){
        List<Cluster> clusterList = new ArrayList<>();
        int[] randomArray = getRandomArray();
        //任意选取k个对象作为初始簇中心,数据集中k个对象的下标存放在randomArray中
        for (int i = 0; i < randomArray.length; i++) {
            Point point = dataList.get(randomArray[i]);
            Cluster cluster = new Cluster(point);
            clusterList.add(cluster);
        }
        return clusterList;
    }

    /**
     * 获取含有k个不重复随机数的数组
     * @return
     */
    public static int[] getRandomArray(){
        Random random = new Random();
        int[] randomArray = new int[k];
        for (int i = 0; i < k; i++) {
            int randomItem = random.nextInt(12);
            //为保证randomArray中存放的随机数不重复
            while (Arrays.binarySearch(randomArray, randomItem) >= 0) randomItem = random.nextInt(12);
            randomArray[i] = randomItem;
        }
        return randomArray;
    }

    /**
     * 初始化数据集
     */
    public static void initDataList(){
        Point p1 = new Point(1, 2);
        Point p2 = new Point(2, 1);
        Point p3 = new Point(2, 4);
        Point p4 = new Point(4, 3);
        Point p5 = new Point(5, 8);
        Point p6 = new Point(6, 7);
        Point p7 = new Point(6, 9);
        Point p8 = new Point(7, 9);
        Point p9 = new Point(9, 5);
        Point p10 = new Point(1, 12);
        Point p11 = new Point(3, 12);
        Point p12 = new Point(5, 12);
        Point p13 = new Point(3, 3);

        dataList.add(p1);
        dataList.add(p2);
        dataList.add(p3);
        dataList.add(p4);
        dataList.add(p5);
        dataList.add(p6);
        dataList.add(p7);
        dataList.add(p8);
        dataList.add(p9);
        dataList.add(p10);
        dataList.add(p11);
        dataList.add(p12);
        dataList.add(p13);
    }
}


实验结果
在这里插入图片描述
输出结果无疑是3个簇,因为k的值就为3。这里笔者进行了多次测试,发现随着测试次数增多,会出现测试结果不同的情况,在搜集过资料后,笔者个人认为这种情况是正常的,原因是由于初始时是随机选取的簇中心点,可能开始选取的簇中心点位置过于紧凑或者过于疏散,都会影响到最后的输出结果,经过多次测试后,笔者发现有一组输出结果的出现频率是最高的,这组输出结果如图所示。这图片不知道为啥字这么小,反正我是看不清,所以用表格盛一下:
在这里插入图片描述

五、实验总结

本实验结果笔者并不保证一定是正确的,笔者仅仅是提供一种使用Java语言实现K-means算法的思路。因为实验并没有给答案,笔者已将书上有答案的实验数据输入程序后,程序输出的结果和答案一致,所以问题应该不大。若有写的不到位的地方,还请各位多多指点!
笔者主页还有其他数据挖掘算法的总结,欢迎各位光顾!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据挖掘Java——Kmeans算法的实现 的相关文章

  • 数据结构——校园导游系统

    校园导游系统 1 要求 大二下学期修了数据结构这门课 xff0c 课设的要求是做一个校园导航系统 详细的要求如下 问题描述 xff1a 当我们参观校园时 xff0c 会遇到如下问题 xff1a 从当前所处位置去校园另外一个位置 xff0c
  • 平衡小车实现

    平衡小车 1 前期准备 1 1 I2C通讯协议 在与MPU6050进行数据的读取时需要用到I2C通讯协议来进行通信 物理层 IIC一共有只有两个总线 xff1a 一条是双向的串行数据线SDA xff0c 一条是串行时钟线SCL SDA Se
  • 关于STM32CubeMX生成不了Keil代码的解决办法

    关于STM32CubeMX生成Keil代码时弹出but MDK ARM project generation have a problem的问题 有两种可能 xff1a 1 输出路径或文件名包含中文 2 Java环境版本不匹配 下载 xff
  • 2020电赛D题绕组飞行器

    在准备电赛的过程中 xff0c 做了一下去年的题 xff0c 本文将介绍我的方案及部分代码 xff0c 希望可以帮助到大家 一 我的装备 由于初学飞控所以主控用的是匿名的拓空者 xff0c 还有匿名的光流传感器 xff0c 北醒的激光雷达
  • nuxtjs常见问题

    1 在服务器端部署 xff0c 需要再服务器端安装node modules 2 本地忽略 nuxt文件夹 xff0c 这个需要在服务器端上执行npm run build生成 xff0c 然后执行 pm2 start npm name 34
  • Ubuntu学习笔记:sudo:vim:command not found

    Ubuntu学习笔记 xff1a sudo xff1a vim xff1a command not found 完成 xff01
  • Ubuntu学习笔记:查看所有用户

    Ubuntu学习笔记 xff1a 查看所有用户 输入 cat etc passwd cut f 1 d 注意 xff01 结尾有一个 xff1a 效果如下 xff1a
  • Ubuntu学习笔记:cd命令

    Ubuntu学习笔记 xff1a cd命令 命令顺序 xff1a 创建一个名为aaa的文件夹 进入指定文件夹 返回上一级文件夹 进入指定文件夹 返回上一级文件夹 退回上一次操作的文件夹 显示上一次操作的文件夹所在的路径 退回多级文件夹 退回
  • Ubuntu学习笔记:swapon 失败:设备或资源忙

    swapon 失败 xff1a 设备或资源忙 用命令swapoff xff0f 交换分区 将交换分区停止 然后再用swapon命令重新加载即可
  • Ubuntu学习笔记:使用命令查看当前登录系统的用户信息

    Ubuntu学习笔记 xff1a 使用命令查看当前登录系统的用户信息 1 查看当前登录的用户名 2 查看当前登录的用户名 终端类型 时间 IP地址 3 服务器连接的所有用户及正在使用的进程 4 显示系统中有哪些使用者正在上面 xff0c 显
  • Ubuntu学习笔记:使用命令查看系统资源,内存使用情况

    Ubuntu学习笔记 xff1a 使用命令查看系统资源 xff0c 内存使用情况 方法1 打开资源管理器 资源 gnome system monitor 方法2 top命令 方法3 下载htop apt get install htop h
  • Ubuntu学习笔记:使用命令修改 root 用户的密码

    Ubuntu学习笔记 xff1a 使用命令修改 root 用户的密码 Ubuntu 每次开机都有一个随机的新的 root 密码 在不知道密码的情况下 xff0c 要重新修改root密码 方法 xff1a sudo passwd 输入用户登录
  • C语言的特点

    1 语言简洁 紧凑 xff0c 使用方便 灵活 xff1b 2 运算符丰富 xff1b 3 数据类型丰富 xff1b 4 具有结构化的控制语句 xff08 例如if else语句 while语句 do while语句 switch语句和fo
  • Win11 更新绕过TPM2.0 方法 最新最简单 亲测有效 Win11系统更新 DEV方式

    最新的win11内测把不符合硬件规定的人都排除出去了 xff0c 虽然有注册表导入可以挤到DEV通道 xff0c 不过在更新到8 会弹出显示设备不支持提示 xff0c 关闭窗口后升级被取消 因此特在实践后教大家如何绕过TPM2 0 更新的方
  • iview常见问题

    1 radio组 label如果为字符串可以默认选中 xff0c 如果为数字 xff0c 却没有反应 答 xff1a label为数字时 xff0c 需要在label前加 xff1a 来绑定 xff0c 这样就可以实现默认选中了
  • 【通信协议】IIC通信协议详解

    IIC的基本介绍 IIC总线的发展 xff1a 芯片间总线 xff08 Inter Interface Circuit xff0c IIC xff09 xff0c 是应用广泛的芯片间串行扩展总线 目前世界上采用的IIC总线一共有两个规范 x
  • 【通信协议】单总线协议详解——以DHT11为例

    单总线概述 1 单总线的介绍 xff08 1 xff09 单总线也称为1 Wire bus xff0c 它是由美国DALLAS xff08 达尔斯 xff09 公司推出的外围串行扩展总线 单总线系统中配置的各种器件 xff0c 由DALLA
  • 【STM32学习笔记】(4)—— STM32工程文件详解

    STM32工程文件构成 从下图可以看出我们的工程目录是由CORE OBJ STM32F10x FWLib USER SYSTEM以及HARDWARE文件夹组成的 此外还有一个文本文档README TXT 以及一个Windows 批处理文件
  • 【STM32学习笔记】(6)—— 跑马灯实验详解

    跑马灯实验 在前面五篇STM32学习笔记中 xff0c 我们已经初步认识了STM32芯片 xff0c 并且了解STM32的常用寄存器 xff0c 介绍了STM32的GPIO模式 xff0c STM32工程文件 xff0c 以及最终讲解了如何
  • 【STM32学习笔记】(9)——串口通讯(USART)详解

    本文主要参考了野火的零死角玩转STM32和正点原子的STM32F1 开发指南 V1 1 xff08 精英板 库函数版本 xff09 xff0c 文章中大部分知识都是从两本书中提取出来 xff0c 串口通信协议的知识主要参考野火的书籍 xff

随机推荐

  • 【STM32学习笔记】(12)——NVIC(嵌套向量中断控制器)详解

    NVIC xff08 嵌套向量中断控制器 xff09 简介 在讲如何配置中断优先级之前 xff0c 我们需要先了解下 NVIC NVIC 是嵌套向量中断控制器 xff0c 控制着整个STM32芯片中断相关的功能 xff0c 它跟Cortex
  • 【STM32学习笔记】(15)——窗口看门狗(WWDG)详解

    窗口看门狗 WWDG 概述 窗口看门狗通常被用来监测 xff0c 由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障 除非递减计数器的值在T6位变成0前被刷新 xff0c 否则看门狗电路在达到预置的时间周期时 x
  • 【元器件学习笔记—电阻】(6)——电阻并联电路

    电阻串联和并联电路 任何复杂的电路经过各种等效和简化后都可以归纳为两种电路 xff1a 一是串联电路 xff0c 二是并联电路 电阻并联电路 并联电路与串联电路是完全不同的电路 xff0c 它们之间不能相互等效 xff0c 并联电路的一些特
  • 【元器件学习笔记—电阻】(7)——电阻串并联电路

    电阻串并联电路 电阻串并联电路是电阻串联电路与电阻并联电路的组合电路 下图所示是由 3 只电阻器构成的电阻器串并联电路 电路中的电阻 R1 和 R2 并联 xff0c 然后再与电阻 R3 串联 xff0c 这就是纯电阻的串并联电路 纯电阻器
  • 【元器件学习笔记—电阻】(8)——电阻分压电路

    电阻分压电路 电阻分压电路工作原理 下图所示是典型的电阻分压电路 xff08 没有接入负载电路 xff09 xff0c 电阻分压电路由 和 两只电阻构成 电路中有电压输入端和电压输出端 1 电路结构 输入电压 加在电阻 和 上 xff0c
  • 小程序验证手机号和身份证号码

    if isPhone params mobile Toast content 39 请填写正确的手机号 39 type 39 error 39 return false var idCardMsg 61 identityIDCard par
  • 神州战神笔记本清灰+换硅脂-记录

    文章目录 Introduction拆清灰涂抹硅脂安装开机测试 Introduction 笔记本购买于2020年4月份左右 xff0c 至今已使用2年半时间 CPU是i7 9750H xff0c 基准频率是2 6GHz 用control ce
  • 【PADS VX2.4下载与安装】

    PADS VX2 4下载与安装 电脑 xff1a Windows10 64bit 一 下载地址 链接 xff1a https pan baidu com s 1yTAU5Hymrc1i8MhALwbsrA 提取码 xff1a hljd 二
  • 【FreeRTOS】详细讲解FreeRTOS中消息队列并通过示例讲述其用法

    讲解FreeRTOS中消息队列及其用法 使用消息队列的原因消息队列函数解析示例遇到的问题 使用消息队列的原因 在裸机系统中 xff0c 两个程序间需要共享某个资源通常使用全局变量来实现 xff1b 但在含操作系统 下文就拿FreeRTOS举
  • 【FreeRTOS】详细讲解FreeRTOS里中断管理并通过示例讲述其用法

    文章目录 中断函数解析FreeRTOS中断使用示例 中断 大家看到中断后 xff0c 有没有想到一个名词 异常呢 xff1f 若大家想到了 xff0c 但是记不起相关概念 xff1b 或者是 xff0c 大家没想到这个名词 xff0c 没关
  • 【嵌入式软件开发实习】个人面试记录及其总结(一)

    文章目录 问题一 xff1a 使用宏定义完成两个数据的交换问题二 xff1a 制作一个函数接口判断函数参数输入是否符合要求 xff0c 如果符合要求就返回部分输入 xff0c 如果不符合就返回no result问题三 xff1a 什么是结构
  • 嵌入式经典通信总线协议——RS232和RS485

    UART 通信的不足 注意 xff1a TTL电平信号通信距离应该 lt 61 1 5米 两种电平标准 RS232协议 因为控制器一般使用 TTL 电平标准 xff0c 所以常常会使用 MA3232 芯片对 TTL 及 RS 232电平的信
  • 把所阅读的文章背景/主题变成白色

    今天在CSDN找SVD分解的资料 xff0c 找到了一篇写的很好的文章 xff0c 但是它的主题是黑色的 xff0c 是黑色的 xff01 作为黑色主题深恶痛绝人士 xff0c 于是我便想把这篇文章的主题改成白色 我们作为读者似乎并没有这个
  • 计算机保研专业课必备之数据结构

    数据结构保研面试准备 算法的五大特征 有穷性 有限的步骤确定性 不可二义性可行性 每一步都是通过执行有限次数完成的输入 零个或多个输入输出 至少有一个或多个输出 O n 的大O是什么意思 xff1f 什么是时间复杂度 大O表示的是最坏情况下
  • Go语言快速的一键生成一个gRPC服务

    目录 前言 介绍 使用命令行工具 micro 生成 gRPC 服务 安装 xff1a 创建项目 xff1a 安装 protobuf 和依赖项 xff1a 构建并运行服务 xff1a 总结 前言 由于近期因为一些事情很久没更了今天带来一个Go
  • 编程实现在str1中查找str2的初始位置

    问题描述 有两个字符串str1和str2 xff0c 它们的长度都不超过100个字符 请编程实现在str1中查找str2的初始位置 输入形式 有两行输入 xff0c 第一行输入字串str1 xff0c 第二行输入字串str2 输出形式 一个
  • 数据挖掘Java——KNN算法的实现

    一 KNN算法的前置知识 k 近邻 xff08 kNN k NearestNeighbor xff09 是在训练集中选取离输入的数据点最近的k个邻居 xff0c 根据这个k个邻居中出现次数最多的类别 xff08 最大表决规则 xff09 x
  • thinkPHP生成微信支付平台证书

    1 先安装微信支付V3版本 xff1b 2 在目录 vendor wechatpay wechatpay下执行以下代码 xff1b 3 在本地VScode编辑器的终端运行命令行 xff0c 执行以下代码 xff1a composer exe
  • 数据挖掘Java——DBSCAN算法的实现

    一 DBSCAN算法的前置知识 DBSCAN算法 xff1a 如果一个点q的区域内包含多于MinPts个对象 xff0c 则创建一个q作为核心对象的簇 然后 xff0c 反复地寻找从这些核心对象直接密度可达的对象 xff0c 把一些密度可达
  • 数据挖掘Java——Kmeans算法的实现

    一 K means算法的前置知识 k means算法 xff0c 也被称为k 平均或k 均值 xff0c 是一种得到最广泛使用的聚类算法 相似度的计算根据一个簇中对象的平均值来进行 算法首先随机地选择k个对象 xff0c 每个对象初始地代表