C语言中K-means算法实现代码

K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。

算法过程如下:

1)从N个样本随机选取K个样本作为质心
2)对剩余的每个样本测量其到每个质心的距离,并把它归到最近的质心的类
3)重新计算已经得到的各个类的质心
4)迭代2~3步直至新的质心与原质心相等或小于指定阈值,算法结束

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<math.h> 

#define DIMENSIOM  2    //目前只是处理2维的数据
#define MAX_ROUND_TIME 100   //最大的聚类次数 

typedef struct Item{
  int dimension_1;    //用于存放第一维的数据
  int dimension_2;    //用于存放第二维的数据
  int clusterID;     //用于存放该item的cluster center是谁
}Item;
Item* data; 

typedef struct ClusterCenter{
  double dimension_1;
  double dimension_2;
  int clusterID;
}ClusterCenter;
ClusterCenter* cluster_center_new; 

int isContinue; 

int* cluster_center;    //记录center
double* distanceFromCenter; //记录一个“点”到所有center的距离
int data_size;
char filename[200];
int cluster_count; 

void initial();
void readDataFromFile();
void initial_cluster();
void calculateDistance_ToOneCenter(int itemID, int centerID, int count);
void calculateDistance_ToAllCenter(int itemID);
void partition_forOneItem(int itemID);
void partition_forAllItem_OneCluster(int round);
void calculate_clusterCenter(int round);
void K_means();
void writeClusterDataToFile(int round);
void writeClusterCenterToFile(int round);
void compareNew_OldClusterCenter(double* new_X_Y);
void test_1(); 

int main(int argc, char* argv[]){
  if( argc != 4 )
  {
    printf("This application need other parameter to run:"
        "\n\t\tthe first is the size of data set,"
        "\n\t\tthe second is the file name that contain data"
        "\n\t\tthe third indicate the cluster_count"
        "\n");
    exit(0);
  }
  srand((unsigned)time(NULL));
  data_size = atoi(argv[1]);
  strcat(filename, argv[2]);
  cluster_count = atoi(argv[3]); 

  initial();
  readDataFromFile();
  initial_cluster();
  //test_1();
  //partition_forAllItem_OneCluster();
  //calculate_clusterCenter();
  K_means();
  return 0;
} 

/*
 * 对涉及到的二维动态数组根据main函数中传入的参数分配空间
 * */
void initial(){
  data = (Item*)malloc(sizeof(struct Item) * (data_size + 1));
  if( !data )
  {
    printf("malloc error:data!");
    exit(0);
  }
  cluster_center = (int*)malloc(sizeof(int) * (cluster_count + 1));
  if( !cluster_center )
  {
    printf("malloc error:cluster_center!\n");
    exit(0);
  }
  distanceFromCenter = (double*)malloc(sizeof(double) * (cluster_count + 1));
  if( !distanceFromCenter )
  {
    printf("malloc error: distanceFromCenter!\n");
    exit(0);
  }
  cluster_center_new = (ClusterCenter*)malloc(sizeof(struct ClusterCenter) * (cluster_count + 1));
  if( !cluster_center_new )
  {
    printf("malloc cluster center new error!\n");
    exit(0);
  }
} 

/*
 * 从文件中读入x和y数据
 * */
void readDataFromFile(){
  FILE* fread;
  if( NULL == (fread = fopen(filename, "r")))
  {
    printf("open file(%s) error!\n", filename);
    exit(0);
  }
  int row;
  for( row = 1; row <= data_size; row++ )
  {
    if( 2 != fscanf(fread, "%d %d ", &data[row].dimension_1, &data[row].dimension_2))
    {
      printf("fscanf error: %d\n", row);
    }
    data[row].clusterID = 0;
  }
} 

/*
 * 根据从主函数中传入的@cluster_count(聚类的个数)来随机的选择@cluster_count个
 * 初始的聚类的起点
 * */ 

void initial_cluster(){
  //辅助产生不重复的数
  int* auxiliary;
  int i;
  auxiliary = (int*)malloc(sizeof(int) * (data_size + 1));
  if( !auxiliary )
  {
    printf("malloc error: auxiliary");
    exit(0);
  }
  for( i = 1; i <= data_size; i++ )
  {
    auxiliary[i] = i;
  } 

  //产生初始化的cluster_count个聚类
  int length = data_size;
  int random;
  for( i = 1; i <= cluster_count; i++ )
  {
    random = rand()%length + 1;
    //printf("%d \n", auxiliary[random]);
    //data[auxiliary[random]].clusterID = auxiliary[random];
    cluster_center[i] = auxiliary[random];
    auxiliary[random] = auxiliary[length--];
  } 

  for( i = 1; i <= cluster_count; i++ )
  {
    cluster_center_new[i].dimension_1 = data[cluster_center[i]].dimension_1;
    cluster_center_new[i].dimension_2 = data[cluster_center[i]].dimension_2;
    cluster_center_new[i].clusterID = i;
    data[cluster_center[i]].clusterID = i;
  }
} 

/*
 * 计算一个点(还没有划分到cluster center的点)到一个cluster center的distance
 *   @itemID:  不属于任何cluster中的点
 *   @centerID: center的ID
 *   @count:   表明在计算的是itemID到第几个@center的distance,并且指明了结果放在distanceFromCenter的第几号元素
 * */
void calculateDistance_ToOneCenter(int itemID,int centerID){
  distanceFromCenter[centerID] = sqrt( (data[itemID].dimension_1-cluster_center_new[centerID].dimension_1)*(double)(data[itemID].dimension_1-cluster_center_new[centerID].dimension_1) + (double)(data[itemID].dimension_2-cluster_center_new[centerID].dimension_2) * (data[itemID].dimension_2-cluster_center_new[centerID].dimension_2) );
} 

/*
 * 计算一个点(还没有划分到cluster center的点)到每个cluster center的distance
 * */
void calculateDistance_ToAllCenter(int itemID){
  int i;
  for( i = 1; i <= cluster_count; i++ )
  {
    calculateDistance_ToOneCenter(itemID, i);
  }
} 

void test_1()
{
  calculateDistance_ToAllCenter(3);
  int i;
  for( i = 1; i <= cluster_count; i++ )
  {
    printf("%f ", distanceFromCenter[i]);
  }
} 

/*
 * 在得到任一的点(不属于任一cluster的)到每一个cluster center的distance之后,决定它属于哪一个cluster center,即取距离最小的
 *   函数功能:得到一个item所属的cluster center
 * */
void partition_forOneItem(int itemID){
  //操作对象是 distanceFromCenter和cluster_center
  int i;
  int min_index = 1;
  double min_value = distanceFromCenter[1];
  for( i = 2; i <= cluster_count; i++ )
  {
    if( distanceFromCenter[i] < min_value )
    {
      min_value = distanceFromCenter[i];
      min_index = i;
    }
  } 

  data[itemID].clusterID = cluster_center_new[min_index].clusterID;
} 

/*
 * 得到所有的item所属于的cluster center , 在一轮的聚类中
 * */
void partition_forAllItem_OneCluster(int round){        //changed!!!!!!!!!!!!!!!!!!!!!!!!
  int i;
  for( i = 1; i <= data_size; i++ )
  {
    if( data[i].clusterID != 0 )
      continue;
    else
    {
      calculateDistance_ToAllCenter(i);  //计算i到所有center的distance
      partition_forOneItem(i);    //根据distance对i进行partition
    }
  } 

  //把聚类得到的数据写入到文件中
  writeClusterDataToFile(round);
} 

/*
 * 将聚类得到的数据写入到文件中,每一个类写入一个文件中
 *   @round: 表明在进行第几轮的cluster,该参数的另一个作用是指定了文件名字中的第一个项.
 * */
void writeClusterDataToFile(int round){
  int i;
  char filename[200];
  FILE** file;
  file = (FILE**)malloc(sizeof(FILE*) * (cluster_count + 1));
  if( !file )
  {
    printf("malloc file error!\n");
    exit(0);
  }
  for( i = 1; i <= cluster_count; i++ )
  {
    sprintf(filename, ".//ClusterProcess//round%d_cluster%d.data", round, i);
    if( NULL == (file[i] = fopen(filename, "w")))
    {
      printf("file open(%s) error!", filename);
      exit(0);
    }
  } 

  for( i = 1; i <= data_size; i++ )
  {
    //sprintf(filename, ".//ClusterProcess//round%d_cluster%d.data", round, data[i].clusterID);
    fprintf(file[data[i].clusterID], "%d\t%d\n", data[i].dimension_1, data[i].dimension_2);
  }
  for( i = 1; i <= cluster_count; i++ )
  {
    //sprintf(filename, ".//ClusterProcess//round%d_cluster%d.data", round, i);
    fclose(file[i]);
  }
} 

/*
 * 重新计算新的cluster center
 * */
void calculate_clusterCenter(int round){          //changed!!!!!!!!!!!!!!!!!!!!!!
  int i;
  double* new_X_Y;  /*
          用来计算和保存新的cluster center的值,同样的,0号元素不用。1,2号元素分别用来
          存放第一个聚类的所有的项的x和y的累加和。3,4号元素分别用来存放第二个聚类的所有
          的项的x和y的累加和......
        */
  new_X_Y = (double*)malloc(sizeof(double) * (2 * cluster_count + 1));
  if( !new_X_Y )
  {
    printf("malloc error: new_X_Y!\n");
    exit(0);
  }
  //初始化为0
  for( i = 1; i <= 2*cluster_count; i++ )
    new_X_Y[i] = 0.0; 

  //用来统计属于各个cluster的item的个数
  int* counter;
  counter = (int*)malloc(sizeof(int) * (cluster_count + 1));
  if( !counter )
  {
    printf("malloc error: counter\n");
    exit(0);
  }
  //初始化为0
  for( i = 1; i <= cluster_count; i++ )
    counter[i] = 0; 

  for( i = 1; i <= data_size; i++ )
  {
    new_X_Y[data[i].clusterID * 2 - 1] += data[i].dimension_1;
    new_X_Y[data[i].clusterID * 2] += data[i].dimension_2;
    counter[data[i].clusterID]++;
  } 

  for( i = 1; i <= cluster_count; i++ )
  {
    new_X_Y[2 * i - 1] = new_X_Y[2 * i - 1] / (double)(counter[i]);
    new_X_Y[2 * i] = new_X_Y[2 * i] / (double)(counter[i]);
  } 

  //要将cluster center的值保存在文件中,后续作图
  writeClusterCenterToFile(round); 

  /*
   * 在这里比较一下新的和旧的cluster center值的差别。如果是相等的,则停止K-means算法。
   * */
  compareNew_OldClusterCenter(new_X_Y); 

  //将新的cluster center的值放入cluster_center_new
  for( i = 1; i <= cluster_count; i++ )
  {
    cluster_center_new[i].dimension_1 = new_X_Y[2 * i - 1];
    cluster_center_new[i].dimension_2 = new_X_Y[2 * i];
    cluster_center_new[i].clusterID = i;
  }
  free(new_X_Y);
  free(counter); 

  //在重新计算了新的cluster center之后,意味着我们要重新来为每一个Item进行聚类,所以data中用于表示聚类ID的clusterID
  //要都重新置为0。
  for( i = 1; i <= data_size; i++ )
  {
    data[i].clusterID = 0;
  }
} 

/*
 * 将得到的新的cluster_count个cluster center的值保存在文件中。以便于观察聚类的过程。
 * */
void writeClusterCenterToFile(int round){
  FILE* file;
  int i;
  char filename[200];
  sprintf(filename, ".//ClusterProcess//round%d_clusterCenter.data", round);
  if( NULL == (file = fopen(filename, "w")))
  {
    printf("open file(%s) error!\n", filename);
    exit(0);
  } 

  for( i = 1; i <= cluster_count; i++ )
  {
    fprintf(file, "%f\t%f\n", cluster_center_new[i].dimension_1, cluster_center_new[i].dimension_2);
  } 

  for( i = 1; i <= cluster_count; i++ )
  {
    fclose(file);
  }
} 

/*
 * 比较新旧的cluster center的差异
 * */
void compareNew_OldClusterCenter(double* new_X_Y){
  int i;
  isContinue = 0;       //等于0表示的是不要继续
  for( i = 1; i <= cluster_count; i++ )
  {
    if( new_X_Y[2 * i - 1] != cluster_center_new[i].dimension_1 || new_X_Y[2 * i] != cluster_center_new[i].dimension_2)
    {
      isContinue = 1;   //要继续
      break;
    }
  }
} 

/************************************************************************************************
 *         K-means算法            *
 ***********************************************************************************************/
void K_means(){
  int times_cluster;
  for( times_cluster = 1; times_cluster <= MAX_ROUND_TIME; times_cluster++ )
  {
    printf("\n            times : %d             \n", times_cluster);
    partition_forAllItem_OneCluster(times_cluster);
    calculate_clusterCenter(times_cluster);
    if( 0 == isContinue )
    {
      break;
      //printf("\n\nthe application can stop!\n\n");
    }
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • C语言实现K-Means算法
(0)

相关推荐

  • C语言实现K-Means算法

    一.聚类和聚类算法 聚类,就是将数据对象划分成若干个类,在同一个类中的对象具有较高的相似度,而不同的类相似度较小.聚类算法将数据集合进行划分,分成彼此相互联系的若干类,以此实现对数据的深入分析和数据价值挖掘的初步处理阶段.例如在现代商业领域,聚类分析算法可以从庞大的数据集合中对消费者的消费习惯.消费倾向,以方便决策者制订消费策略.总之,作为数据挖掘中的一个模块,聚类分析算法可以作为一个单独的工具已发现数据库中分布的一些深层信息,并概括出每一类的特点.聚类分析算法也可作为数据挖掘算法中其他分析算法

  • C语言中的socket编程实例代码

    前不久刚看完<c primer plus>,收获颇丰,对于C语言也有了更全面的认识,对于模块化和数据结构也有了更多的想法,之前学过C语言,但很多已经记不起了,知识很零散,这也是我看这本书的原因. 之后一段时间都会在进一步学习编程的同时研究socket通讯,目标是要将socket研究透,设计出自己的框架,以后从事服务器开发和构架应该也会大有裨益. 好了,废话不多说,奉上网上找的源码. /* window socket 服务端编程测试 */ #include <stdio.h> //用

  • R语言中的fivenum与quantile()函数算法详解

    fivenum()函数: 返回五个数据:最小值.下四分位数数.中位数.上四分位数.最大值 对于奇数个数字=5,fivenum()先排序,依次返回最小值.下四分位数.中位数.上四分位数.最大值 > fivenum(c(1,12,40,23,13)) [1] 1 12 13 23 40 对于奇数个数字>5,fivenum()先排序,我们可以求取最小值,最大值,中位数.在排序中,最小值与中位数中间,若为奇数,取其中位数为下四分位数,若为偶数,取最中间两个数的平均值为下四分位数:在排序中,中位数与最大

  • k均值算法c++语言实现代码

    复制代码 代码如下: //k-mean.h #ifndef KMEAN_HEAD #define KMEAN_HEAD #include <vector> #include <map> //空间点的定义 class Node {     public:        double pos_x;        double pos_y;        double pos_z;      Node()      {          pos_x = 0.0;          pos

  • Go语言中strings和strconv包示例代码详解

    前缀和后缀 HasPrefix判断字符串s是否以prefix开头: strings.HaxPrefix(s string, prefix string) bool 示例: package main import ( "fmt" "strings" ) func main() { pre := "Thi" str1 := "This is a Go program!" fmt.Println(strings.HasPrefix(

  • c语言中static修饰函数的方法及代码

    1.静态函数只能在声明它的文件中可见,其他文件不能引用该函数. 2.不同的文件可以使用相同名字的静态函数,互不影响. 3.使用static声明的函数不能被另一个文件引用. 实例 /* file1.c */ #include <stdio.h> static void fun(void) { printf("hello from fun.\n"); } int main(void) { fun(); fun1(); return 0; } /* file2.c */ #inc

  • C语言中#define定义的标识符和宏实例代码

    目录 1.#define定义标识符 语法 举个栗子 2.#define定义宏 #define替换的规则 #与###的用法: 宏的缺点 宏和函数的优缺点 总结 1.#define定义标识符 在C语言程序中,有时候会包含#define #define可以定义标识符 也就是说可以对字符重新定义,实现代替的作用 语法 #define  name  stuff 就比如说: #define MAX 1000(用MAX代替1000) #define reg   register (用reg代替register

  • 用python实现k近邻算法的示例代码

    K近邻算法(或简称kNN)是易于理解和实现的算法,而且是你解决问题的强大工具. 什么是kNN kNN算法的模型就是整个训练数据集.当需要对一个未知数据实例进行预测时,kNN算法会在训练数据集中搜寻k个最相似实例.对k个最相似实例的属性进行归纳,将其作为对未知实例的预测. 相似性度量依赖于数据类型.对于实数,可以使用欧式距离来计算.其他类型的数据,如分类数据或二进制数据,可以用汉明距离. 对于回归问题,会返回k个最相似实例属性的平均值.对于分类问题,会返回k个最相似实例属性出现最多的属性. kNN

  • 详解go语言中sort如何排序

    目录 sort包源码解读 前言 如何使用 基本数据类型切片的排序 自定义Less排序比较器 自定义数据结构的排序 分析下源码 不稳定排序 稳定排序 查找 Interface 总结 参考 sort 包源码解读 前言 我们的代码业务中很多地方需要我们自己进行排序操作,go 标准库中是提供了 sort 包是实现排序功能的,这里来看下生产级别的排序功能是如何实现的. go version go1.16.13 darwin/amd64 如何使用 先来看下 sort 提供的主要功能 对基本数据类型切片的排序

  • Java语言中cas指令的无锁编程实现实例

    最开始接触到相关的内容应该是从volatile关键字开始的吧,知道它可以保证变量的可见性,而且利用它可以实现读与写的原子操作...但是要实现一些复合的操作volatile就无能为力了...最典型的代表是递增和递减的操作.... 我们知道,在并发的环境下,要实现数据的一致性,最简单的方式就是加锁,保证同一时刻只有一个线程可以对数据进行操作....例如一个计数器,我们可以用如下的方式来实现: public class Counter { private volatile int a = 0; pub

  • 详解C语言中rand函数的使用

    前言 我们在编程实现算法的过程中,往往需要使用到随机数.由于计算机是一台以逻辑为基础的机器,没法做到真正的随机(大概量子计算机可以?).所以计算机生成的是伪随机数,供我们使用. 我们使用C语言的rand函数,生成的也是伪随机数. c语言之rand函数的使用 1.写入头文件 #include <stdlib.h> #include <stdio.h> #include <time.h> 2.变量的定义 void main( void ) { int i,k; 3.sran

随机推荐