无k聚类法
前言
众所周知的k-means聚类算法必须在k给定的条件下才能给出无监督学习结果,简单讲就是必须事先知道有几个类别方能完成聚类,而我们大多时候不能知道或者很难知道具体数据中有多少个类。因此寻找一个在无给定k条件下(即不知道有多少类别的情况下)的聚类算法显得尤为重要。
在这里本人试探性地给出一种自认为可行的方法(希望读者与我一同探讨该方法的正确性)。
算法简析
首先我们必须先给出一个限定条件,该限定条件限定了,每组分类中分类点(简单讲就是k-mean中的均值点,某种意义上讲也可认为是质点)到该分组个点的最大距离。
有了以上给定前提后我们就可以往下分析问题了:
简单二分法:
1.首先考虑,当前数据组是否可以作为一个组,若可以则将该分组插入结果分组集。(其考虑标准为,先算出分类点坐标,然后把各个点到该分类点的距离逐一计算,并比较得是否所有数据点到分类点的距离都小于给定的最大距离,若是则该为一个可行分组,若否则不可)。
2.把所有数据点利用2-means算法二等分,则得到2组数据,每组数据递归地调用该方法(也就是把这两组数据集分别从第1步开始迭代)。
3.输出结果分组集。
初步解释:
由于任何的非负整数都可以通过二进制表示,因此能保证所有可能分组数可达。而以上的过程实际上是一个二进制表示的过程,我们可以吧递给看成是二叉树的模样,而其分类种也就是叶子节点数量,如图1。
图1
其中可以清晰的看到第一次迭代吧数据集分成了2组,而后再前一组(A1)中由于已经达到标准,所以作为结果集的一项被保留了下来,而后一组(A2),由于仍然不满足要求仍需迭代,后面一次迭代把改组分成了2个合格的可行组(B1,B2)
由此迭代结束,输出结果集({A1,B1,B2})。
并组优化:
在得到以上结果集后,我们会发现有些组本可以合并为一组但却因为上述做法而分别对应到了两个分组,若下图2。
图 2
在这里我们本应该吧中间两组作为一组,然而由于随机选择初始迭代点的原因,会导致如上这种过度分组的现象,为了解决上述现象,我做了一个优化,其思路待我慢慢讲来。
我们先把以上的结果集中的分组尝试进行合并。任意取2个结果集进行如下操作。
1判断结果集中是否存在两个可以合并的分组(即把该两个分组合并为一个后仍然满足最大距离要求)若有则转2,若无则输出结果,算法终止。
2.把两个分组S1.,S2合并为一个分组S,用S替代S1,S2,并更新结果集,转1。
完成这样的任务后我们就解决了上面的问题了,很开心得到如下结果,如图3
图3
哈哈哈!还没完呢?优化继续。。。。。
分组优化:
这里有这样的问题:是否某些组可以拆解为其他分组的一部分呢?这样的话这个分组就不复存在了完全归为其他分组的一部分,这样也可以减少分组数量。
基于这个思想,我们得出这样的算法:
1、 判断是否存在某个分组其完全可以拆解为其他分组的一部分(这个判断略微复杂,你需要做的可不只是所把每一个试着放入其他分组中这么简单,你要考虑到某一元素放入其他分组后整个结果集也变了,该被放入分组的分组点也变了,所以简单判断一个分组各个元素独立的属于其他分组是毫无意义的,这其中是一个动态变化着的问题,不能静态考虑),若没有则输出结果。若有转2。
2、 将该分组解开,依次放入结果集的其他分组中,并从结果集中删除该分组。转1。
其现象大致若下图4
实验代码及运行结果:
实验数据为如下:
{ {0.4,0},{1,0},{2.5,0},{3.1,0},{1.5,0},{ 1.55,0 },{2,0},{1.95,0}}
#include <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <functional>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <vector>
#include <limits>
#include <unordered_set>
#include <memory>
using namespace std;
const double eps = 0.00001;
unordered_set<shared_ptr<vector<double>>> tos;
unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>> midres;
unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>> realres;
unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>> nextres;
unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>> finalres;
double stddis;
double maxdis(const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &s, shared_ptr<vector<double>> &p1, shared_ptr<vector<double>> &p2);
double dis(const shared_ptr<vector<double>> &p1, const shared_ptr<vector<double>> &p2);
shared_ptr<vector<double>> getmid(shared_ptr<unordered_set<shared_ptr<vector<double>>>> s);
double nrom(const shared_ptr<vector<double>> &p);
shared_ptr<vector<double>> operator-(const shared_ptr<vector<double>> &p);
shared_ptr<vector<double>> operator+(const shared_ptr<vector<double>> &p1, const shared_ptr<vector<double>> &p2);
shared_ptr<vector<double>> operator-(const shared_ptr<vector<double>> &p1, const shared_ptr<vector<double>> &p2);
ostream& operator<<(ostream& o, const shared_ptr<vector<double>> &p);
void get2(const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &s);
bool getClass();
double dis(const shared_ptr<vector<double>> &p1, const shared_ptr<vector<double>> &p2) {
double res = 0;
for (int i = 0; i < p1->size(); ++i) {
res += ((*p1)[i] - (*p2)[i])*((*p1)[i] - (*p2)[i]);
}
return sqrt(res);
}
double maxdis(const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &s, shared_ptr<vector<double>> &p1, shared_ptr<vector<double>> &p2) {
double mmax = 0;
double res = 0;
shared_ptr<vector<double>> mid = getmid(s);
for (shared_ptr<vector<double>> pv : *s) {
for (shared_ptr<vector<double>> pn : *s) {
double tem = dis(pv, pn);
if (tem > mmax) {
mmax = tem;
p1 = pv;
p2 = pn;
}
}
res = max(res, dis(mid, pv));
}
return res;
}
shared_ptr<vector<double>> getmid(shared_ptr<unordered_set<shared_ptr<vector<double>>>> s) {
shared_ptr<vector<double>> res = shared_ptr<vector<double>>(new vector<double>((*(s->begin()))->size()));
for (const shared_ptr<vector<double>> &p : *s) {
for (int i = 0; i < p->size(); ++i) {
(*res)[i] += (*p)[i]/s->size();
}
}
return res;
}
double nrom(const shared_ptr<vector<double>> &p) {
double res = 0;
for (double td : *p) {
res += td*td;
}
return sqrt(res);
}
shared_ptr<vector<double>> operator-(const shared_ptr<vector<double>> &p) {
shared_ptr<vector<double>> res = shared_ptr<vector<double>>(new vector<double>(p->size()));
for (int i = 0; i < p->size(); ++i) {
(*res)[i] = -(*p)[i];
}
return res;
}
shared_ptr<vector<double>> operator+(const shared_ptr<vector<double>> &p1, const shared_ptr<vector<double>> &p2) {
if (p1->size() != p2->size())
return nullptr;
shared_ptr<vector<double>> res = shared_ptr<vector<double>>(new vector<double>(p1->size()));
for (int i = 0; i < p1->size(); ++i) {
(*res)[i] = (*p1)[i]+ (*p2)[i];
}
return res;
}
shared_ptr<vector<double>> operator-(const shared_ptr<vector<double>> &p1, const shared_ptr<vector<double>> &p2) {
return p1 + (-p2);
}
ostream& operator<<(ostream& o, const shared_ptr<vector<double>> &p) {
for (double td : *p) {
o << td << " ";
}
return o;
}
void get2(const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &s) {
shared_ptr<vector<double>> p1;
shared_ptr<vector<double>> p2;
if (maxdis(s,p1,p2) <= stddis) {
midres.insert(s);
return;
}
shared_ptr<unordered_set<shared_ptr<vector<double>>>> s1 = shared_ptr<unordered_set<shared_ptr<vector<double>>>>(new unordered_set<shared_ptr<vector<double>>>());
shared_ptr<unordered_set<shared_ptr<vector<double>>>> s2 = shared_ptr<unordered_set<shared_ptr<vector<double>>>>(new unordered_set<shared_ptr<vector<double>>>());
for (shared_ptr<vector<double>> p : *s) {
if (dis(p, p1) < dis(p, p2)) {
s1->insert(p);
}
else {
s2->insert(p);
}
}
shared_ptr<vector<double>> np1 = getmid(s1);
shared_ptr<vector<double>> np2 = getmid(s2);
while (nrom(np1 - p1) > eps || nrom(np2 - p2) > eps) {
s1->clear();
s2->clear();
for (shared_ptr<vector<double>> p : *s) {
if (dis(p, p1) < dis(p, p2)) {
s1->insert(p);
}
else {
s2->insert(p);
}
}
p1 = np1;
p2 = np2;
shared_ptr<vector<double>> np1 = getmid(s1);
shared_ptr<vector<double>> np2 = getmid(s2);
}
get2(s1);
get2(s2);
}
bool isSum(const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &ps, const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &psn, shared_ptr<unordered_set<shared_ptr<vector<double>>>> &outping, shared_ptr<unordered_set<shared_ptr<vector<double>>>> &outfloat) {
shared_ptr<unordered_set<shared_ptr<vector<double>>>> tempres = shared_ptr<unordered_set<shared_ptr<vector<double>>>>(new unordered_set<shared_ptr<vector<double>>>(*ps));
tempres->insert(psn->begin(), psn->end());
shared_ptr<vector<double>> mid = getmid(tempres);
bool flag = true;
for (shared_ptr<vector<double>> ptd : *tempres) {
if (dis(ptd, mid) > stddis) {
outfloat->insert(ptd);
flag = false;
}
else {
outping->insert(ptd);
}
}
return flag;
}
int pingClass(const shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>> &s) {
shared_ptr<unordered_set<shared_ptr<vector<double>>>> floatTemp = nullptr;
shared_ptr<unordered_set<shared_ptr<vector<double>>>> pingTemp = nullptr;
shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>> tempres = shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>>(new unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>(*s));
shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>> firstout = shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>>(new unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>());
while (!tempres->empty()) {
bool flag = false;
shared_ptr<unordered_set<shared_ptr<vector<double>>>> spt = *(tempres->begin());
for (const shared_ptr<unordered_set<shared_ptr<vector<double>>>> sptn : *tempres) {
if (spt == sptn)
continue;
floatTemp = shared_ptr<unordered_set<shared_ptr<vector<double>>>>(new unordered_set<shared_ptr<vector<double>>>());
pingTemp = shared_ptr<unordered_set<shared_ptr<vector<double>>>>(new unordered_set<shared_ptr<vector<double>>>());
if (isSum(spt, sptn, pingTemp, floatTemp)) {
spt->insert(sptn->begin(), sptn->end());
tempres->erase(sptn);
flag = true;
break;
}
}
if (!flag) {
tempres->erase(spt);
firstout->insert(spt);
}
}
nextres = *firstout;
return nextres.size();
}
bool add(const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &ps, const shared_ptr<vector<double>>& pd) {
ps->insert(pd);
shared_ptr<vector<double>> mid = getmid(ps);
for (shared_ptr<vector<double>> ptd : *ps) {
if (dis(ptd, mid) > stddis) {
ps->erase(pd);
return false;
}
}
return true;
}
bool openClass(shared_ptr<unordered_set<shared_ptr<vector<double>>>> pt,shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>> &s) {
shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>> b = shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>>(new unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>());
for (shared_ptr<unordered_set<shared_ptr<vector<double>>>> apt : *s) {
shared_ptr<unordered_set<shared_ptr<vector<double>>>> tpt(new unordered_set<shared_ptr<vector<double>>>(*apt));
if (apt == pt) {
pt = tpt;
}
b->insert(shared_ptr<unordered_set<shared_ptr<vector<double>>>>(tpt));
}
b->erase(pt);
while (!pt->empty()){
shared_ptr<vector<double>> nowpt = *(pt->begin());
bool flag = false;
for (const shared_ptr<unordered_set<shared_ptr<vector<double>>>> &ptd : *b) {
if (add(ptd,nowpt)) {
flag = true;
break;
}
}
if (flag) {
pt->erase(nowpt);
}
else {
return false;
}
}
s = b;
return true;
}
void deepPingClass(shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>> s) {
while (true) {
bool flag = false;
for (shared_ptr<unordered_set<shared_ptr<vector<double>>>> pt : *s) {
if (openClass(pt, s)) {
flag = true;
break;
}
}
if (!flag) {
break;
}
}
finalres = *s;
}
bool getClass() {
get2(shared_ptr<unordered_set<shared_ptr<vector<double>>>>(new unordered_set<shared_ptr<vector<double>>>(tos)));
pingClass(shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>>(new unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>(midres)));
deepPingClass(shared_ptr<unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>>(new unordered_set<shared_ptr<unordered_set<shared_ptr<vector<double>>>>>(nextres)));
return false;
}
int main() {
vector<vector<double>> solve = { {0.4,0},{1,0},{2.5,0},{3.1,0} ,{1.5,0},{ 1.55,0 },{2,0},{1.95,0} };
for (const vector<double> & v : solve) {
tos.insert(shared_ptr<vector<double>>(new vector<double>(v)));
}
stddis = 0.6;
getClass();
for (shared_ptr<unordered_set<shared_ptr<vector<double>>>> p : finalres) {
for(shared_ptr<vector<double>> pv:*p) {
for (double i : *pv) {
cout << i << " ";
}
cout << endl;
}
cout << "==========" <<endl;
}
cin >> stddis;
return 0;
}
测试数据一
1000组二维数据(数据大小在0-100之间,保留2位有效数字,组内距分类点最大距离为6.0)
=============================
mid point:12.209 94.258
14.03 93.69
9.69 90.99
13.78 92.69
13.5 97.62
13.84 92.64
13.51 94.43
11.23 96.17
9.91 91.65
12.15 95.44
10.45 97.26
=============================
=============================
mid point:51.5286 42.4957
49.07 47.69
45.68 43.05
50.46 40.93
48.76 45.24
56.92 40.17
53.57 37.7
56.24 42.69
=============================
=============================
mid point:88.514 49.39
85.46 51.22
87.43 48.82
90.91 47.38
86.19 51.62
92.58 47.91
=============================
=============================
mid point:65.6171 42.2271
65.95 44.81
66.23 42.05
62.58 38.42
68.85 41.01
69.01 40.58
62.29 41.27
64.41 47.45
=============================
=============================
mid point:26.6288 79.9513
23.75 80.82
30.54 75.47
25.12 74.52
30.09 78.75
24.73 84.07
29.52 82.04
24.46 84.21
24.82 79.73
=============================
=============================
mid point:46.44 11.5178
50.25 7.07
41.39 11.9
44.82 7.61
45.21 15.14
47.81 13.34
49.52 14.02
47.14 10.29
43.89 11.28
47.93 13.01
=============================
=============================
mid point:32.8633 33.6
33.86 33.05
30.16 29.04
34.57 38.71
=============================
=============================
mid point:43.6025 29.5713
47.34 29.55
41.8 29.68
45.83 27.85
41.31 32.09
43.45 34.03
41.84 31.38
46.66 26.75
40.59 25.24
=============================
=============================
mid point:87.4122 43.7522
89.96 39.45&nbs