K-Means++算法 及应用

k-means算法是一种基本的聚类算法,这个算法的先决条件是

  1)必须选择最终结果需要聚为几类,就是k的大小。

  2)初始化聚类中心点,也就是seeds。

  当然,我们可以在输入的数据集中随机的选择k个点作为seeds,但是随机选择初始seeds可能会造成聚类的结果和数据的实际分布相差很大。既然选择初始的seeds这么重要,那有什么算法可以帮助选择初始的seeds吗?当然有,k-means++就是选择初始seeds的一种算法。

      k-means++算法选择初始seeds的基本思想就是:初始的聚类中心之间的相互距离要尽可能的远。wiki上对该算法的描述是如下:

  1. 从输入的数据点集合中随机选择一个点作为第一个聚类中心
  2. 对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x)
  3. 选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大
  4. 重复2和3直到k个聚类中心被选出来
  5. 利用这k个初始的聚类中心来运行标准的k-means算法

      从上面的算法描述上可以看到,算法的关键是第3步,如何将D(x)反映到点被选择的概率上,一种算法如下(详见此地):

  1. 先从我们的数据库随机挑个随机点当“种子点”
  2. 对于每个点,我们都计算其和最近的一个“种子点”的距离D(x)并保存在一个数组里,然后把这些距离加起来得到Sum(D(x))。
  3. 然后,再取一个随机值,用权重的方式来取计算下一个“种子点”。这个算法的实现是,先取一个能落在Sum(D(x))中的随机值Random,然后用Random -= D(x),直到其<=0,此时的点就是下一个“种子点”。
  4. 重复2和3直到k个聚类中心被选出来
  5. 利用这k个初始的聚类中心来运行标准的k-means算法

      可以看到算法的第三步选取新中心的方法,这样就能保证距离D(x)较大的点,会被选出来作为聚类中心了。至于为什么原因很简单,如下图 所示:  

                                                

      假设A、B、C、D的D(x)如上图所示,当算法取值Sum(D(x))*random时,该值会以较大的概率落入D(x)较大的区间内,所以对应的点会以较大的概率被选中作为新的聚类中心。

以上内容转自:这位犇的博客

------------------------------低调的分割线-----------------------------------------


在看k-means 的时候,看了部分资料,个人觉得这位仁兄写的通俗易懂。以下是根据这个上面描述所实现的一个例子,这段代码传入若干条直线,用k、b值表示(y = k*x + b),选出line_num(为了和斜率k区分这里就不用k了)条区别最大 的线:

struct Line {
     float k;
     float b;
};
#define INF 0xfffffff
#define rootX2Y2(x,y) sqrt(((x)*(x))+((y)*(y)))
//对选出的相似线进行筛选剔除
void  clusteringLine(std::vector<Line> &vecLines ,int line_num){
	if(!vecLines.size()){
		std::cout<<"no lines date"<<std::endl;
		return ;
	}
	//把k b转化为 进行聚合的 x,y值,这里的x y 分别是 (θ,b * cosθ)
	std::vector<std::vector<float> > angleCoor;
	for(auto x:vecLines){
		std::vector<float> temp;
		temp.push_back(180.0*atan(x.k)/PI);
		temp.push_back(x.b*cos(atan(x.k)));
		angleCoor.push_back(temp);
	}

	srand(unsigned(time(0)));
	//聚类中心的索引
	std::vector<int > selectedLine;   
	selectedLine.push_back(random(vecLines.size()));
	//判断是否被选
	std::vector<bool> isSelected;
	for(auto x:angleCoor) isSelected.push_back(false);
	isSelected[selectedLine[0]] = true;

	//每个点与最近聚类中心的距离,初始化为无穷大
	std::vector<float > distance;
	for(auto x:angleCoor) distance.push_back(INF);

	while(selectedLine.size() < line_num){
		//计算每个点与最近聚类中心的距离
		for(int i = 0;i<angleCoor.size();++i){
			for(int j = 0;j<selectedLine.size();++j){
				float dis  = rootX2Y2((angleCoor[i][0] - angleCoor[selectedLine[j]][0]),(angleCoor[i][1] - angleCoor[selectedLine[j]][1]));
				distance[i] = MIN(distance[i],dis);
			}
		}

		//添加一个新的聚类中心,选取的原则是:距离大的被选概率大
		int selectId  = -1;

		float sum = 0 ;
		for(int i = 0;i<distance.size();++i){
			if(!isSelected[i])  sum += distance[i];
		}
		float selectValue = sum * random(1000)/1000.0;
		for(int i = 0;i<distance.size();++i){
			if(!isSelected[i]){
				selectValue -= distance[i];
			}
			if(selectValue < 0){
				selectId = i;
				break;
			}
		}

		isSelected[selectId] = true;
		selectedLine.push_back(selectId);
	}

	std::cout<<"select line's id:";
	for(auto x:selectedLine) std::cout<<x<<",";
	std::cout<<std::endl;

	//对vecLines进行清空重新复制,直接在原vec删除的话,数据量大效率很低
	std::vector<Line> temp;
	for(int i=0;i<vecLines.size();++i){
		if(isSelected[i])
			temp.push_back(vecLines[i]);
	}
	vecLines.clear();
	for(int i=0;i<temp.size();++i){
			vecLines.push_back(temp[i]);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值