首页 > 试题广场 >

用户分群

[编程题]用户分群
  • 热度指数:88 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
某电商平台希望根据用户的购物行为对用户进行分群,以便制定差异化的运营策略。
每位用户有三个特征指标: purchase_amount(月均消费金额) visit_frequency(月均访问次数) return_rate(退货率,已归一化) 你需要实现 KMeans 聚类算法,将用户划分为若干个群体。
KMeans 算法的流程如下:给定 K 个初始聚类中心,计算每个数据点到各聚类中心的欧氏距离,将数据点分配到距离最近的聚类中心所在的组。然后对每个组重新计算中心点(即该组内所有数据点各维度的算术平均值),完成一轮迭代。 重复上述过程指定的迭代次数后,输出最终的 K 个聚类中心,每个维度的值保留两位小数(四舍五入)。 
欧氏距离的计算公式为: d=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}

输入描述:
第一行一个正整数 K,表示聚类中心的个数。
接下来 K 行,每行三个浮点数,表示初始聚类中心的三个特征值。
下一行一个正整数,表示迭代次数。
下一行一个正整数 m,表示数据点的个数。
接下来 m 行,每行三个浮点数,表示一个数据点的三个特征值。


输出描述:
输出 K 行,每行三个数值,表示迭代结束后各聚类中心的三个特征值,保留两位小数,四舍五入。
示例1

输入

2
10 20 30
40 50 60
2
6
8 18 25
12 22 35
42 48 58
38 52 62
45 55 65
5 15 28

输出

8.33 18.33 29.33
41.67 51.67 61.67

说明

初始中心为 [10,20,30] 和 [40,50,60],共 6 个数据点,迭代 2 次。
第 1 轮:前三个点 (8,18,25)、(12,22,35)、(5,15,28) 距离中心 [10,20,30] 更近,分到第一组;后三个点 (42,48,58)、(38,52,62)、(45,55,65) 距离中心 [40,50,60] 更近,分到第二组。更新中心为 [8.33,18.33,29.33] 和 [41.67,51.67,61.67]。
第 2 轮:分配结果不变,中心保持不变。
示例2

输入

3
5 5 5
15 15 15
25 25 25
1
4
4 4 4
6 6 6
14 16 14
26 24 26

输出

5.00 5.00 5.00
14.00 16.00 14.00
26.00 24.00 26.00

说明

初始中心为 [5,5,5]、[15,15,15]、[25,25,25],共 4 个点,迭代 1 次。
(4,4,4) 和 (6,6,6) 距离中心 [5,5,5] 最近,分到第一组,新中心为 [(4+6)/2,(4+6)/2,(4+6)/2]=[5,5,5]。
(14,16,14) 距离中心 [15,15,15] 最近,分到第二组,新中心为 [14,16,14]。
(26,24,26) 距离中心 [25,25,25] 最近,分到第三组,新中心为 [26,24,26]。

备注:
本题由牛友@Charles 整理上传
#include <bits/stdc++.h>
#include <memory>
#include <vector>
using namespace std;
struct point{
    double p1,p2,p3;
};

int main() {
    int k;//聚类中心个数
    cin>>k;
    vector<point> pc(k);//聚类中心点
    for(int i=0;i<k;i++)
    {
        cin>>pc[i].p1>>pc[i].p2>>pc[i].p3;
    }
    int g;//迭代次数
    cin>>g;
    int m;//数据点个数
    cin>>m;
    vector<point> pd(m);//数据点
    for(int i=0;i<m;i++)
    {
        cin>>pd[i].p1>>pd[i].p2>>pd[i].p3;
    }
   
    for(int i=0;i<g;i++)//迭代g次
    {
        vector<vector<int>> dg(k);//m个数据点归于某一中心点,与该中心点距离
        for(int j=0;j<m;j++)//每个数据点迭代
        {
            vector<double> dx(k);
            vector<double> dy(k);
            vector<double> dz(k);
            vector<double> d(k);
            for(int e=0;e<k;e++)//与每个中心计算距离,用于决定属于哪个中心点
            {
                dx[e]=pd[j].p1-pc[e].p1;
                dy[e]=pd[j].p2-pc[e].p2;
                dz[e]=pd[j].p3-pc[e].p3;
                d[e]=dx[e]*dx[e]+dy[e]*dy[e]+dz[e]*dz[e];
            }      
            double mind=d[0];    
            int minc=0;
            for(int e=0;e<k;e++)//决定属于哪个中心点
            {
                if(d[e]<mind)
                {
                    minc=e;
                    mind=d[e];
                }
            }
            dg[minc].push_back(j);//记录第j个数据点属于minc的中心
        }
        for(int zx=0;zx<k;zx++)//每个中心点更新
        {
            if(dg[zx].empty())
            continue;
            pc[zx].p1=0;
            pc[zx].p2=0;
            pc[zx].p3=0;//首先将原储存清零
            for(int j=0;j<dg[zx].size();j++)//将新三维全部累加
            {
                pc[zx].p1=pc[zx].p1+pd[dg[zx][j]].p1;
                pc[zx].p2=pc[zx].p2+pd[dg[zx][j]].p2;
                pc[zx].p3=pc[zx].p3+pd[dg[zx][j]].p3;
            }
            pc[zx].p1=pc[zx].p1/dg[zx].size();
            pc[zx].p2=pc[zx].p2/dg[zx].size();
            pc[zx].p3=pc[zx].p3/dg[zx].size();
        }
    }
    for(int i=0;i<k;i++)
    {
        printf("%.2f %.2f %.2f",pc[i].p1,pc[i].p2,pc[i].p3);
        if(i!=k-1)
        cout<<endl;
    }
    return 0;
}
// 64 位输出请用 printf("%lld")
发表于 2026-03-11 11:15:28 回复(0)
package main

import (
	"fmt"
	"math"
)

func main() {
	k := 0

	for {
		n, err := fmt.Scan(&k)
		if n == 0 {
			break
		}
		if err != nil {
			return
		}
		if k == 0 {
			break
		}
		specialList := make([][3]float64, k)
		for i := 0; i < k; i++ {
			fmt.Scan(&specialList[i][0], &specialList[i][1], &specialList[i][2])
		}
		iteCnt := 0
		fmt.Scan(&iteCnt)

		dataCnt := 0
		fmt.Scan(&dataCnt)

		dataList := make([][3]float64, dataCnt)
		for i := 0; i < dataCnt; i++ {
			fmt.Scan(&dataList[i][0], &dataList[i][1], &dataList[i][2])
		}

		for i := 0; i < iteCnt; i++ {
			groupMap := map[int][][3]float64{}

			for j := 0; j < dataCnt; j++ {
				// 计算与特征值距离 划分组别
				minVal := math.MaxFloat64
				minIdx := 0
				for idx, special := range specialList {
					val := (dataList[j][0]-special[0])*(dataList[j][0]-special[0]) +
						(dataList[j][1]-special[1])*(dataList[j][1]-special[1]) +
						(dataList[j][1]-special[1])*(dataList[j][1]-special[1])
					if val < minVal {
						minIdx = idx
						minVal = val
					}
				}
				groupMap[minIdx] = append(groupMap[minIdx], dataList[j])

			}
			// 更新特征值
			for idx, list := range groupMap {
				sum := [3]float64{}
				for _, item := range list {
					sum[0] += item[0]
					sum[1] += item[1]
					sum[2] += item[2]
				}
				specialList[idx] = [3]float64{sum[0] / float64(len(list)), sum[1] / float64(len(list)), float64(sum[2] / float64(len(list)))}
			}
		}
		for _, special := range specialList {
			fmt.Printf("%.2f %.2f %.2f\n", special[0], special[1], special[2])
		}
	}
}

发表于 2026-03-09 23:54:54 回复(0)