程序非常简单,作为当初我的第一个MFC程序,有纪念意义。程序最终界面:
一、问题:在MFC对话框界面输入任意类别和任意数目的样本进行C分类,并进行显示。
二、c均值聚类算法
1、c—平均算法
对一批没有标明类别及类数的模式样本集,根据模式间的相似程度,按照物以类聚、人以群分的思想,将相似的模式分为一类,不相似的分为另一类。c均值聚类算法根据欧式距离把最短距离的样本分为1类。
动态聚类框图
① 先选定某种距离作为样本间的相似性的度量;
② 确定评价聚类结果的准则函数;
③ 给出某种初始分类,用迭代法找出使准则函数取极值的最好的聚类结果。
2、算法步骤
① 根据输入的分类数和样本总数,生成坐标范围内的随机数
② 初始聚类中心
③ 根据欧式距离进行一次分类
④ 根据新分成的两建立新的聚类中心
⑤ 新旧聚类中心不等则转回③,相等结束计算
⑥ 绘图显示结果
三、动态存储点坐标的数据结构
由于要求输入任意类别和任意数目样本,所以存放数据不能放在一般的数组(因为必须进行初始化,不然编译不过)。而每个样本数据有几个特征,所以它实际是一个第一维未知、第二维已知(如这里的x,y坐标)的动态数组。有几种方法:
① 用new[]定义一个一维动态数组指针(结构体对象指针),在程序运行时按界面输入样本多少分配内存。第二维的样本特征可以用个结构体,如:
struct mypoint
{
int x;
int y;
int myclass;//类别
};
mypoint* ptr=new mypoint[num];
②用自带的cpoint类自己定义一个坐标对象,用new[]定义一个相应对象的一维数组指针
③容器。我另一个程序的一部分:
class CModel
{
public:
CModel();
virtual ~CModel();
int class_num; //样本数目
int value[3]; //第二维样本特征
};
struct sq
{
int xclass;
int ysq;
};
vector<sq>vec_p(classnum+2);
vector<CModel> vec_point(num);
vector<CModel>::size_type vector_size=num;
for (vector<CModel>::size_type it = (v-1)*num/classnum; it != v*num/classnum; ++it)
{……}
④定义个动态二维数组,第二维已知。比如我们一般的用法:
char (*a)[N];//指向数组的指针
a = new char[m][N];
delete[] a;
四、程序实现
① cmathDlg.h
#pragma once
#include <vector>
// CcmathDlg 对话框
class CcmathDlg : public CDialog
{
// 构造
public:
CcmathDlg(CWnd* pParent = NULL); // 标准构造函数
void DrawOnMem();
double random(double start, double end);
// 对话框数据
enum { IDD = IDD_CMATH_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedOk();
int num;
int classnum;
vector<int> vec_point;
};
struct mypoint
{
int x;
int y;
int myclass;
};
struct myxy
{
int x;
int y;
int k;
};
②cmathDlg.cpp
// cmathDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "cmath.h"
#include "cmathDlg.h"
#include "Resource.h"
#include"time.h"
#include "math.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CcmathDlg 对话框
CcmathDlg::CcmathDlg(CWnd* pParent /*=NULL*/)
: CDialog(CcmathDlg::IDD, pParent)
, num(0)
, classnum(0)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CcmathDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, num);
DDV_MinMaxInt(pDX, num, 2, 100);
DDX_Text(pDX, IDC_EDIT2, classnum);
DDV_MinMaxInt(pDX, classnum, 2, 100);
}
BEGIN_MESSAGE_MAP(CcmathDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDOK, &CcmathDlg::OnBnClickedOk)
END_MESSAGE_MAP()
// CcmathDlg 消息处理程序
BOOL CcmathDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CcmathDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
DrawOnMem();
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CcmathDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CcmathDlg::OnBnClickedOk()
{
InvalidateRect(NULL,TRUE);
UpdateWindow();
UpdateData(TRUE);
CClientDC dc(this);
int x1,x2,y1,y2,m;
int p=0;
int lg=30;
bool f;
mypoint* ptr=new mypoint[num];
myxy* double_ptr=new myxy[classnum];
myxy* double_ptr1=new myxy[classnum];
srand(unsigned(time(0)));
ptr[0].x=int(random(0,11));
ptr[0].y=int(random(0,11));
ptr[0].myclass=0;
for(int icnt = 1; icnt != num; icnt++)
{
do
{
f=true;
ptr[icnt].x=int(random(0,11));
ptr[icnt].y=int(random(0,11));
for(int j=0;j<icnt;j++)
{
if(ptr[icnt].x!=ptr[j].x||ptr[icnt].y!=ptr[j].y)
continue;
if(j=icnt-1)
f=false;
}
}while(f==false);
ptr[icnt].myclass=0;
}
//第一步
for(int icnt = 0; icnt != classnum; icnt++)
{
double_ptr[icnt].x=ptr[icnt].x;
double_ptr[icnt].y=ptr[icnt].y;
}
double_ptr1=double_ptr;
//第二步
do
{
double_ptr1=double_ptr;
for(int icnt = 0; icnt != classnum; icnt++)
{
double_ptr[icnt].k=0;
}
for(int i = 0; i!= num; i++)
{
for(int j = 0; j!= classnum; j++)
{
m= ptr[i].myclass;
ptr[i].myclass=(sqrt((double)((ptr[i].x-double_ptr[m].x)*(ptr[i].x-double_ptr[m].x)+(ptr[i].y-double_ptr[m].y)*(ptr[i].y-double_ptr[m].y)))>sqrt(((double)(ptr[i].x-double_ptr[j].x)*(ptr[i].x-double_ptr[j].x)+(ptr[i].y-double_ptr[j].y)*(ptr[i].y-double_ptr[j].y))))?j:m;
}
}
//第三步
for(int i = 0; i!= num; i++)
{
for(int j = 0; j!= classnum; j++)
{
if(ptr[i].myclass==j)
{
double_ptr[j].x+=ptr[i].x;
double_ptr[j].y+=ptr[i].y;
double_ptr[j].k++;
}
}
}
for(int j = 0; j!= classnum; j++)
{
if(double_ptr[j].k!=0)
{
double_ptr[j].x=double_ptr[j].x/double_ptr[j].k;
double_ptr[j].y=double_ptr[j].y/double_ptr[j].k;
}
else
{
double_ptr[j].x=0;
double_ptr[j].y=0;
}
}
++p;
}while(double_ptr1!=double_ptr);
for(int i=0; i< num; i++)
{
x1=20+ptr[i].x*lg-6;
x2=20+ptr[i].x*lg+6;
y1=360-ptr[i].y*lg+6;
y2=360-ptr[i].y*lg-6;
switch(ptr[i].myclass)
{
case 0:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(0,0,255));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 1:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(0,255,0));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 2:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(255,0,0));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 3:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(0,255,255));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 4:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(255,0,255));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 5:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(255,255,0));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 6:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(0,0,0));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 7:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(125,125,125));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 8:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(125,0,255));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
case 9:
{
CBrush brush,*oldbrush;
brush.CreateSolidBrush(RGB(125,125,255));
oldbrush=dc.SelectObject(&brush);
dc.Ellipse(x1,y1,x2,y2);
dc.SelectObject(oldbrush);
break;
}
}
}
}
void CcmathDlg::DrawOnMem()
{
CString str;
int i;
CClientDC dc(this);
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
CPen* pOldPen=dc.SelectObject(&pen);
dc.MoveTo(20, 20);
dc.LineTo(20, 360);
dc.LineTo(360, 360);
for(i = 0; i <= 10; i ++)
{
str.Format("%d", i);
dc.TextOut(17 + 30 * i, 365, str);
dc.MoveTo(i * 30 + 20, 360);
dc.LineTo(i * 30 + 20, 355);
}
str="x轴";
dc.TextOut(350,340,str);
for(i = 1; i <= 10; i ++)
{
str.Format("%d", i);
dc.TextOut(2, 360 - 30 * i - 5, str);
dc.MoveTo(25, 360 - 30 * i);
dc.LineTo(20, 360 - 30 * i);
}
str="y轴";
dc.TextOut(30,20,str);
dc.MoveTo(350, 357);
dc.LineTo(360, 360);
dc.LineTo(350, 363);
dc.MoveTo(17, 30);
dc.LineTo(20, 20);
dc.LineTo(23, 30);
dc.SelectObject(pOldPen);
}
double CcmathDlg::random(double start, double end)
{
return start+(end-start)*rand()/(RAND_MAX + 1.0);
}