#P2898. 第2题-电子商务平台
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 20
            Accepted: 2
            Difficulty: 6
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年4月24日-阿里云(算法岗)
                              
                      
          
 
- 
                        算法标签>机器学习算法          
 
第2题-电子商务平台
题解思路
构建用户-商品矩阵
首先将输入的用户购买历史转换为用户–商品的二值矩阵。行表示用户,列表示商品,如果用户购买过该商品则对应位置为1,否则为0。
计算Cosine相似度与KNN邻居
使用 scikit-learn 的 NearestNeighbors,设置
metric='cosine'(返回的距离为 1−cosine_similarity)algorithm='brute'n_neighbors=4(包括自身在内共4个邻居)
对每个用户调用 kneighbors 得到最近4个邻居,排除自身后实际使用3个邻居,并将距离转换为相似度:
sim = 1.0 - distance
生成推荐
对于每个用户:
- 遍历3个邻居,将邻居购买但当前用户未购买的商品作为候选
 - 对每个候选商品,记录其与当前用户之间的最大相似度
 - 从候选集中选出相似度最高的商品;若存在多件,则取商品ID最小者
 - 若无任何候选商品,则输出 
None,相似度设为0.000 
复杂度分析
- 构建矩阵耗时 O(NM),其中 N 为用户数,M 为商品数。
 - KNN 基于 brute force,计算所有用户间距离,时间复杂度 O(N2M)。
 - 最后生成推荐阶段,遍历每个用户的3个邻居并筛选商品,时间 O(NKQ)(K=3,Q 为平均邻居商品数)。
 - 整体时间复杂度以 O(N2M) 为主;空间复杂度为 O(NM)。
 
Python代码
```python
import sys
import numpy as np
from sklearn.neighbors import NearestNeighbors
def main():
    raw = eval(sys.stdin.read())  # 读取输入
    uids = [u[0] for u in raw]
    its = [u[1] for u in raw]
    # 构建商品ID到列索引的映射
    unis = sorted({x for lst in its for x in lst})
    m = {v: i for i, v in enumerate(unis)}
    # 构造用户-商品二值矩阵
    X = np.zeros((len(raw), len(unis)), dtype=int)
    for i, lst in enumerate(its):
        for item in lst:
            X[i, m[item]] = 1
    # KNN模型:包括自身共4个邻居,排除自身后实际使用3个
    knn = NearestNeighbors(n_neighbors=4, metric='cosine', algorithm='brute')
    knn.fit(X)
    dists, idxs = knn.kneighbors(X)
    # 对每个用户生成推荐
    for i, uid in enumerate(uids):
        nbrs = idxs[i][1:]      # 排除自身
        sims = 1 - dists[i][1:] # 转换为Cosine相似度
        seen = set(its[i])
        cand = {}
        # 收集候选商品并记录最大相似度
        for nj, sim in zip(nbrs, sims):
            for item in its[nj]:
                if item not in seen:
                    # 如果商品还没在 cand 中,或者相似度更高,就更新
                    if item not in cand or sim > cand[item]:
                        cand[item] = sim
        # 选择推荐或输出None
        if cand:
            max_sim = max(cand.values())
            rec = min([it for it, s in cand.items() if s == max_sim])
            sc = max_sim
        else:
            rec, sc = None, 0.0
        # 输出格式:[用户ID,推荐商品ID或None,相似度保留3位]
        print(f"[{uid}, {rec}, {sc:.3f}]")
if __name__ == '__main__':
    main()
        题目内容
某电子商务平台希望使用机器学习来改善用户的购物体验。他们收集了用户的购买历史数据,希望你 能构建一个推荐系统,为用户推荐他们可能喜欢的商品。
你的任务是,使用scikit−learn库,基于用户的购买历史数据,构建一个基于K最近邻(KNN)的推荐系统,并使用Cosine相似度来评估模型的性能。
要求:
1.首先要计算用户之间的Cosine相似度矩阵,然后在推荐系统中使用基于KNN的方法,并且将 KNN的距离度量设置为cosine。
2.KNN的邻居数量n_neighbors参数设为4,其中包括用户自己,最后在生成推荐时排除自己(即实 际使用3个邻居)。
3.推荐的商品必须是用户尚未购买过的商品(即排除已购买的商品)。
4.如果没有合适的商品推荐(例如用户的邻居没有新商品),输出结果中应显示None和相似度为0.0。
5.相同相似度情况下,取ID最小的商品。
输入描述
输入是一个list,每个元素是一个包含两个元素的list,第一个元素是用户ID,第二个元素是该用户 购买过的商品ID的列表。
输出描述
输出是一个list,包含三个元素,第一个元素是用户ID,第二个元素是预测的用户可能喜欢的商品 ID,第三个元素是预测的Cosine相似度,保留小数点后3位有效数字。
补充说明
(1)可以使用形如numpy、pandas、sklearn的第三方代码库。
(2)为了保证唯一性,请严格遵循输入输出描述进行作答。
(3)形如 sys.stdin等方法结合for循环和eval函数即可读取并还原输入数据。
(4)算法使用brute,最近邻数量设置为3。
样例1
输入
[[1, [101, 102, 103]], [2, [101, 104]], [3, [102, 103, 105]], [4, [101, 103, 105]]]
输出
[1, 105, 0.667]
[2, 102, 0.408]
[3, 101, 0.667]
[4, 102, 0.667]