打入预选赛十强赛赋予40,预选赛小组未出线的赋予50。对于亚洲杯,前四名取其排名,八强赋予5,十六强赋予9,预选赛没出线的赋予17。
首先需要做数据预处理:
归一化(Normalization):是为了将数据映射到0~1之间,去掉量纲的过程,让计算更加合理,不会因为量纲问题导致1米与100mm产生不同。
有了数据后,就可以开始着手写代码了:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
/*
k-means算法描述(聚类-无监督学习):
k-means算法的具体步骤:
1)给定大小为n的数据集,令I=1,选取k个初始聚类中心
Zj(I),j=1,2,3,…,k;
2)计算每个数据对象与聚类中心的距离D(xi,Zj(I)),
i=1,2,3…n,j=l,2,3,…,k,
如果满足
D(xi,Zk(I)) =min{D(xi,Zj(I)),
i=l,2,3,…n}
则 xi∈C k;
3) 计算k个新的聚类中心:
即取聚类中所有元素各自维度的算术平均数;
4) 判断:若Zj(I+1)≠Zj(I),
j=l,2,3,…,k,则I=I+1,
返回(2);否则算法结束。
*/
//本次数据采用三维数据,可以理解为立体空间的XYZ坐标进行聚类 我们选择的数据是判断中国男足在亚洲处于几流水平
typedef struct Teams{
double X;
double Y;
double Z;
}team,*Team; //typedef毕竟是条语句,所以这里要加;
//结构体初始化好像不能使用指针去初始化(因为指针需要指向一个东西(不然叫野指针),注意下面函数必须要加引用,否则函数域并不能改变其值,因为C语言本身是值传参而不是引用传参;
void iniTeams(team& t,double x,double y,double z){
t.X=x;
t.Y=y;
t.Z=z;
}
void printfTeams(Team t){
printf("其X值为%f\n",t->X);
printf("其Y值为%f\n",t->Y);
printf("其Z值为%f\n",t->Z);
}
//首先需要定义一个计算欧几里得距离的函数 参数为结构体指针
double ED(Team team1,Team team2){
return sqrt(pow((team1->X-team2->X),2)+pow((team1->Y-team2->Y),2)+pow((team1->Z-team2->Z),2));
}
int belongs(team t,team teams[]){
double d[3]={0};
double min=65535; //用double接收赋值double
int position=0;
for(int i=0;i<3;i++){
d[i]=ED(&t,&teams[i]); //这里写&t和参数写Team t哪个可能更好呢?
// printf("%f\n",d[i]);
if(d[i]<min){
min=d[i];
position=i; //存储下标
}
}
return position;
}
team average(team a[],int numlen){
team t;
double averageX=0;
double averageY=0;
double averageZ=0;
for(int i=0;i<numlen;i++){
averageX+=(a[i].X/(double)numlen);
averageY+=(a[i].Y/(double)numlen);
averageZ+=(a[i].Z/(double)numlen);
}
t.X=averageX;t.Y=averageY;t.Z=averageZ;
return t;
}
//team*不等于Team Team=&team
team* CaclulateC(team teams[],int a[],int count1,int b[],int count2,int c[],int count3){ //参数为分别在三个簇的队在teams中的下标数组
team *newC=(team*)malloc(sizeof(team)*3); //计算算术平均 直接搞指针写法,因为数组会随着函数的生命周期结束而结束,而指针不会 这里需要数组写法防止野指针
team A[count1]={0}; team B[count2]={0}; team C[count3]={0}; //一定要注意变量名的命名重复问题 熟记语法很重要
for(int i=0;i<count1;i++){
A[i]=teams[a[i]];
}
newC[0]=average(A,count1);
for(int i=0;i<count2;i++){
B[i]=teams[b[i]];
}
newC[1]=average(B,count2);
for(int i=0;i<count3;i++){
C[i]=teams[c[i]];
}
newC[2]=average(C,count3);
//分别计算三个簇中心
// team* newc=newC; //防止warning 返回赋值指针而不是数组首地址(数组名)
return newC;
}
//自定义team元素等价函数
bool teamequal(team t1,team t2){
if(t1.X==t2.X and t1.Y==t2.Y and t1.Z==t2.Z){
return true;
}
return false;
}
int main(){
const char* name[15]={"中国","日本","韩国","伊朗","沙特","伊拉克","卡塔尔","阿联酋","乌兹别克斯坦","泰国","越南","阿曼","巴林","朝鲜","印尼"};
double data[15][3]={
1,1,0.5,
0.3,0,0.19,
0,0.15,0.13,
0.24,0.76,0.25,
0.3,0.76,0.06,
1,1,0,
1,0.76,0.5,
1,0.76,0.5,
0.7,0.76,0.25,
1,1,0.5,
1,1,0.25,
1,1,0.5,
0.7,0.76,0.5,
0.7,0.68,1,
1,1,0.5
};
//初始化15个队伍
team teams[15]={0};//用数组来创建多个变量
// Team Teams[15]={0}; //定义指针可以略过,反正C语言值传参,直接传&teams[i]即可
for(int i=0;i<15;i++){ //每一个单独循环一直用i是可以的,但是多个循环嵌套就不能一直用i,因为内层循环依然处于外层循环的函数域内
iniTeams(teams[i],data[i][0],data[i][1],data[i][2]);
}
// for(int j=0;j<15;j++){
// Teams[j]=&teams[j];
// }
//选择k=3个聚类中心,这里选择和PPT相同,日本、巴林和泰国的值:A:{0.3, 0, 0.19},B:{0.7, 0.76, 0.5}和C:{1, 1, 0.5}。 C语言没有%C
// team C[3]={0};
// iniTeams(C[0],0.3,0,0.19);iniTeams(C[1],0.7,0.76,0.5);iniTeams(C[2],1,1,0.5);
// printf("中国属于%c类",'A'+belongs(&teams[0],C));
// exit(0);
team C[3]={0};
//初始化聚类中心
iniTeams(C[0],0.3,0,0.19);iniTeams(C[1],0.7,0.76,0.5);iniTeams(C[2],1,1,0.5);
// for(int i=0;i<15;i++){
// printf("%d",belongs(teams[i],C));
// }
int Count1=0;int Count2=0;int Count3=0;
int *a=nullptr; int *b=nullptr; int *c=nullptr;
// int A[15]={0}; int B[15]={0}; int C[15]={0};
while(true){
//建立k=3个簇-用数组(取maxsize=15)表示存储它们在teams中的下标 很多东西用malloc申请空间的机制都更加灵活 数组没有指针灵活对于C语言来说
a=(int*)malloc(sizeof(int)*15); b=(int*)malloc(sizeof(int)*15); c=(int*)malloc(sizeof(int)*15); int j=0,k=0,l=0;
int count1=0,count2=0,count3=0;//存储每个簇中目前元素的数量,方便计算 注意连续定义多个变量不能写int count1,count2,count3=0;
//划分15个队所属的簇
for(int i=0;i<15;i++){
if(belongs(teams[i],C)==0){
a[j++]=i;
count1++;
}
if(belongs(teams[i],C)==1){
b[k++]=i;
count2++;
}
if(belongs(teams[i],C)==2){
c[l++]=i;
count3++;
}
}
//重新计算聚类中心
team* newC=CaclulateC(teams,a,count1,b,count2,c,count3);
//保存聚类结果供打印
Count1=count1;Count2=count2;Count3=count3;
//判断结束条件
if(teamequal(newC[0],C[0]) and teamequal(newC[1],C[1]) and teamequal(newC[2],C[2])){
break;
}
//更新聚类中心
for(int i=0;i<3;i++){
C[i].X=newC[i].X;
C[i].Y=newC[i].Y;
C[i].Z=newC[i].Z;
}
free(a); free(b); free(c);
}
printf("经过聚类后:\n");
//输出各个球队所属于的类
printf("亚洲一流队伍有%d支分别是 ",Count1);
for(int i=0;i<Count1;i++){
printf("%s ",name[a[i]]);
}
printf("\n");
printf("亚洲二流队伍有%d支分别是 ",Count2);
for(int i=0;i<Count2;i++){
printf("%s ",name[b[i]]);
}
printf("\n");
printf("亚洲三流队伍有%d支分别是 ",Count3);
for(int i=0;i<Count3;i++){
printf("%s ",name[c[i]]);
}
printf("\n");
// printf("%f",C[0].X);
// // printfTeams(Teams[0]);
// // printf("中国与某某队的欧式距离为:%f",ED(Teams[0],Teams[1]));
return 0;
}