谪仙阁-记录美好瞬间

超市智能商品推荐系统 - 逐句详细解析

作者头像
谪仙 本文作者

2025-6-25 阅读 121 约 8分钟读完

评论0

超市智能商品推荐系统 - 逐句详细解析

1. 头文件部分

#include <iostream>      // 这行代码告诉编译器我们要使用输入输出功能,比如在屏幕上显示文字和从键盘获取输入
#include <fstream>       // 这行代码让我们能够读写文件,比如保存商品数据和用户数据
#include <vector>       // 这行代码引入动态数组容器,可以存储一组数据并自动调整大小
#include <map>          // 这行代码引入映射容器,可以存储键值对,比如用商品ID查找商品信息
#include <algorithm>    // 这行代码引入算法库,提供排序、查找等常用操作
#include <sstream>      // 这行代码引入字符串流,方便字符串和其他类型之间的转换
#include <iomanip>      // 这行代码引入输出格式化工具,可以控制数字显示的小数位数等
#include <ctime>        // 这行代码引入时间相关函数,比如获取当前时间
#include <cstdlib>      // 这行代码引入标准库函数,比如随机数生成
#include <windows.h>    // 这行代码引入Windows特有功能,用来解决中文显示乱码问题

2. 编码设置函数

void SetConsoleGBK() {
    SetConsoleOutputCP(936);  // 这行设置控制台输出使用GBK编码(936是GBK的代码页编号)
    SetConsoleCP(936);        // 这行设置控制台输入使用GBK编码
}

3. 商品类(Product)

class Product {  // 定义一个名为Product的类,表示超市中的商品
public:         // 下面的成员都是公开的,可以在类外部访问
    int id;         // 定义一个整数变量id,用来存储商品的唯一编号
    string name;    // 定义一个字符串变量name,存储商品名称
    string category;// 定义一个字符串变量category,存储商品类别
    double price;   // 定义一个双精度浮点数price,存储商品价格
    int sales;      // 定义一个整数sales,存储商品销量
    double discount;// 定义一个双精度浮点数discount,存储商品折扣(0.0-1.0)

    // 构造函数,用于创建Product对象时初始化成员变量
    Product(int i, const string& n, const string& c, double p, int s = 0, double d = 1.0)
        : id(i), name(n), category(c), price(p), sales(s), discount(d) {
    }
    // 这个构造函数有6个参数,后两个有默认值
    // 冒号后面是成员初始化列表,直接初始化各个成员变量
    // 参数i赋值给id,n赋值给name,依此类推
};

4. 用户类(User)

class User {  // 定义一个名为User的类,表示系统用户
public:
    string username;  // 用户名,字符串类型

    // 购买历史,使用map容器存储,键是商品ID(int),值是购买次数(int)
    map<int, int> purchaseHistory;

    // 浏览历史,同样使用map存储,键是商品ID,值是浏览次数
    map<int, int> browseHistory;

    // 类别权重,键是类别名称(string),值是权重值(double)
    map<string, double> categoryWeights;

    // 默认构造函数,初始化username为空字符串
    User() : username("") {}

    // 带参数的构造函数,用给定的用户名初始化
    explicit User(const string& name) : username(name) {}
    // explicit关键字防止隐式转换

    // 更新用户对某类别的兴趣权重
    void UpdateCategoryWeights(const string& category, double weight) {
        categoryWeights[category] += weight;  // 找到对应类别,增加其权重值
    }
};

5. 推荐系统核心类(RecommendationSystem)

5.1 数据成员

private:  // 私有成员,只能在类内部访问
    vector<Product> products_;  // 存储所有商品的动态数组
    map<string, User> users_;   // 存储所有用户的映射,键是用户名,值是User对象
    string currentUser_;        // 当前登录用户的用户名

5.2 构造函数和析构函数

// 构造函数,在创建RecommendationSystem对象时自动调用
RecommendationSystem() {
    SetConsoleGBK();  // 调用之前定义的函数,设置控制台编码
    LoadProducts();   // 加载商品数据
    LoadUsers();      // 加载用户数据
}

// 析构函数,在对象销毁时自动调用
~RecommendationSystem() {
    SaveProducts();  // 保存商品数据到文件
    SaveUsers();     // 保存用户数据到文件
}

5.3 数据加载和保存

// 从文件加载商品数据
void LoadProducts() {
    ifstream file("products.txt");  // 创建输入文件流,打开products.txt文件
    if (!file) {  // 检查文件是否成功打开
        cerr << "无法打开商品文件!" << endl;  // 输出错误信息
        return;  // 返回
    }

    products_.clear();  // 清空当前商品列表
    string line;  // 定义一个字符串变量,用来存储从文件读取的每一行
    while (getline(file, line)) {  // 循环读取文件的每一行
        istringstream iss(line);  // 创建一个字符串流,方便分割行内容
        int id, sales;  // 定义变量存储商品ID和销量
        string name, category;  // 定义变量存储商品名称和类别
        double price, discount;  // 定义变量存储价格和折扣

        // 从字符串流中提取数据到各个变量
        if (iss >> id >> quoted(name) >> quoted(category) >> price >> sales >> discount) {
            products_.emplace_back(id, name, category, price, sales, discount);
            // 如果读取成功,使用emplace_back在vector末尾直接构造Product对象
        }
    }
    file.close();  // 关闭文件
}

// 保存商品数据到文件
void SaveProducts() {
    ofstream file("products.txt");  // 创建输出文件流,打开products.txt文件
    if (!file) {  // 检查文件是否成功打开
        cerr << "无法保存商品文件!" << endl;  // 输出错误信息
        return;  // 返回
    }

    // 遍历所有商品
    for (const auto& product : products_) {
        // 将商品信息写入文件,每个字段用空格分隔
        file << product.id << " " << quoted(product.name) << " " 
             << quoted(product.category) << " " << product.price << " " 
             << product.sales << " " << product.discount << endl;
        // quoted()函数处理字符串中的空格,确保正确读取
    }
    file.close();  // 关闭文件
}

5.4 用户数据加载和保存

// 从文件加载用户数据
void LoadUsers() {
    ifstream file("users.txt");  // 打开用户数据文件
    if (!file) {  // 如果文件不存在或无法打开
        return;  // 直接返回(首次运行时文件可能不存在)
    }

    users_.clear();  // 清空当前用户数据
    string line;  // 存储每行内容的变量
    while (getline(file, line)) {  // 逐行读取文件
        istringstream iss(line);  // 创建字符串流
        string username;  // 存储用户名
        iss >> username;  // 从行中提取用户名

        User user(username);  // 创建User对象
        int purchaseCount;  // 存储购买记录数量
        iss >> purchaseCount;  // 读取购买记录数量
        for (int i = 0; i < purchaseCount; ++i) {  // 循环读取每个购买记录
            int productId, count;  // 商品ID和购买次数
            iss >> productId >> count;  // 读取商品ID和购买次数
            user.purchaseHistory[productId] = count;  // 存入购买历史

            // 更新用户对商品类别的兴趣权重
            for (const auto& product : products_) {  // 遍历所有商品
                if (product.id == productId) {  // 找到对应的商品
                    // 每购买一次,该类别的权重增加0.2
                    user.UpdateCategoryWeights(product.category, count * 0.2);
                    break;  // 找到后退出循环
                }
            }
        }

        // 同样的方式处理浏览历史
        int browseCount;
        iss >> browseCount;
        for (int i = 0; i < browseCount; ++i) {
            int productId, count;
            iss >> productId >> count;
            user.browseHistory[productId] = count;

            for (const auto& product : products_) {
                if (product.id == productId) {
                    // 每浏览一次,该类别的权重增加0.1
                    user.UpdateCategoryWeights(product.category, count * 0.1);
                    break;
                }
            }
        }

        users_[username] = user;  // 将用户对象存入users_映射
    }
    file.close();  // 关闭文件
}

// 保存用户数据到文件
void SaveUsers() {
    ofstream file("users.txt");  // 打开用户数据文件
    if (!file) {  // 检查是否成功打开
        cerr << "无法保存用户文件!" << endl;
        return;
    }

    // 遍历所有用户
    for (const auto& userPair : users_) {
        const User& user = userPair.second;  // 获取User对象
        file << user.username << " ";  // 写入用户名

        // 写入购买历史
        file << user.purchaseHistory.size() << " ";  // 先写入购买记录数量
        for (const auto& purchase : user.purchaseHistory) {  // 遍历每个购买记录
            file << purchase.first << " " << purchase.second << " ";  // 写入商品ID和购买次数
        }

        // 写入浏览历史
        file << user.browseHistory.size() << " ";  // 先写入浏览记录数量
        for (const auto& browse : user.browseHistory) {  // 遍历每个浏览记录
            file << browse.first << " " << browse.second << " ";  // 写入商品ID和浏览次数
        }

        file << endl;  // 换行
    }
    file.close();  // 关闭文件
}

5.5 兴趣度计算

// 计算用户对商品的兴趣度
double CalculateInterest(const User& user, const Product& product) const {
    double interest = 0.0;  // 初始化兴趣度为0

    // 1. 基于类别的兴趣
    auto categoryIt = user.categoryWeights.find(product.category);  // 查找商品类别
    if (categoryIt != user.categoryWeights.end()) {  // 如果找到该类别
        interest += categoryIt->second * 0.5;  // 类别权重乘以0.5加到兴趣度
    }

    // 2. 基于购买历史的兴趣
    auto purchaseIt = user.purchaseHistory.find(product.id);  // 查找购买记录
    if (purchaseIt != user.purchaseHistory.end()) {  // 如果购买过该商品
        interest += purchaseIt->second * 0.3;  // 购买次数乘以0.3加到兴趣度
    }

    // 3. 基于浏览历史的兴趣
    auto browseIt = user.browseHistory.find(product.id);  // 查找浏览记录
    if (browseIt != user.browseHistory.end()) {  // 如果浏览过该商品
        interest += browseIt->second * 0.2;  // 浏览次数乘以0.2加到兴趣度
    }

    // 4. 考虑折扣因素
    interest *= (1.0 + (1.0 - product.discount));  // 折扣越低(1.0-product.discount越大),兴趣度越高

    // 5. 考虑销量因素
    interest *= (1.0 + product.sales * 0.001);  // 销量越高,兴趣度越高

    return interest;  // 返回最终计算出的兴趣度
}

这个函数综合多种因素计算用户对商品的兴趣度,是推荐系统的核心算法。

超市智能商品推荐系统详细解析(6-10部分)

6. 推荐系统核心类(续)

6.1 商品排序与推荐(详细解释)

// 这个函数的作用是把所有商品按照用户可能感兴趣的程度从高到低排序
vector<pair<double, Product>> GetSortedProductsByInterest(const User& user) const {
    // 创建一个能装"商品+兴趣分数"的容器,就像准备一个空篮子
    vector<pair<double, Product>> scoredProducts;  

    // 开始检查每一个商品,就像在超市里一个个看商品
    for (const auto& product : products_) {  
        // 计算用户对这个商品的兴趣分数(后面会详细解释怎么计算的)
        double score = CalculateInterest(user, product);  

        // 把这个商品和它的分数配对放进篮子里
        scoredProducts.emplace_back(score, product);  
    }

    // 现在要把篮子里的商品按分数从高到低排列
    // sort是排序函数,就像把东西按大小排队
    // [](...)这部分是排序规则,意思是比较两个商品的分数,分数大的排前面
    sort(scoredProducts.begin(), scoredProducts.end(),
        [](const auto& a, const auto& b) { return a.first > b.first; });

    // 最后把排好序的商品篮子返回给需要的人
    return scoredProducts;
}

6.2 分类商品获取(详细解释)

// 这个函数的作用是找出某个类别的所有商品
vector<Product> GetProductsByCategory(const string& category) const {
    // 准备一个空篮子装结果
    vector<Product> result;  

    // 检查每一个商品
    for (const auto& product : products_) {  
        // 如果这个商品的类别和我们要找的类别一样
        if (product.category == category) {  
            // 就把这个商品放进结果篮子里
            result.push_back(product);  
        }
    }
    // 把装好商品的篮子返回
    return result;
}

6.3 折扣组合生成(详细解释)

// 这个函数的作用是随机生成打折商品的组合推荐
vector<pair<Product, Product>> GetDiscountCombinations() const {
    // 准备一个篮子装最终的商品组合
    vector<pair<Product, Product>> combinations;  

    // 先准备一个篮子专门装所有打折商品
    vector<Product> discountProducts;  

    // 找出所有正在打折的商品
    for (const auto& product : products_) {
        // 如果折扣小于1表示有打折(1表示原价,0.8表示打8折)
        if (product.discount < 1.0) {  
            discountProducts.push_back(product);  // 加入打折商品篮子
        }
    }

    // 设置随机数种子,就像洗牌前先洗一下手
    srand(static_cast<unsigned int>(time(nullptr)));

    // 准备生成5个随机组合
    for (int i = 0; i < min(5, static_cast<int>(discountProducts.size())); ++i) {
        // 随机抓一个商品的编号(就像闭着眼睛从篮子里摸一个)
        int idx1 = rand() % discountProducts.size();  
        // 再随机抓另一个
        int idx2 = rand() % discountProducts.size();  

        // 确保不是同一个商品(不能自己和自己组合)
        if (idx1 != idx2) {  
            // 把这两个商品配成一对放进最终篮子
            combinations.emplace_back(discountProducts[idx1], discountProducts[idx2]);
        }
    }

    // 把装好组合的篮子返回
    return combinations;
}

7. 显示功能实现(详细解释)

7.1 商品显示基础版

// 这个函数的作用是把商品列表整齐地显示在屏幕上
void DisplayProducts(const vector<Product>& products) const {
    // 先打印表头,\t是制表符,让各列对齐
    cout << "ID\t名称\t类别\t价格\t折扣\t折后价" << endl;
    // 打印一条分隔线
    cout << "------------------------------------------------" << endl;

    // 打印每一个商品的信息
    for (const auto& product : products) {
        // fixed和setprecision(2)保证价格显示两位小数
        // 依次打印:ID、名称、类别、原价、折扣比例、折后价
        cout << product.id << "\t" 
             << product.name << "\t" 
             << product.category << "\t"
             << fixed << setprecision(2) << product.price << "\t"
             << product.discount * 100 << "%\t" 
             << product.price * product.discount << endl;
    }
}

7.2 带兴趣分数的商品显示

// 这个函数比基础版多了兴趣分数的显示
void DisplayProductsWithInterest(const vector<pair<double, Product>>& scoredProducts) const {
    // 表头多了一列"兴趣度"
    cout << "兴趣度\tID\t名称\t类别\t价格\t折扣\t折后价" << endl;
    cout << "--------------------------------------------------------" << endl;

    // 打印每个商品及其兴趣分数
    for (const auto& scoredProduct : scoredProducts) {
        const Product& product = scoredProduct.second;  // 取出商品
        // 先打印兴趣分数(保留2位小数)
        cout << fixed << setprecision(2) << scoredProduct.first << "\t"
            // 再打印商品的其他信息
            << product.id << "\t" 
            << product.name << "\t" 
            << product.category << "\t"
            << product.price << "\t"
            << product.discount * 100 << "%\t" 
            << product.price * product.discount << endl;
    }
}

8. 用户交互功能(详细解释)

8.1 登录系统

// 处理用户登录
bool Login(const string& username) {
    // 检查用户是否已经存在(在用户名单里找)
    if (users_.find(username) == users_.end()) {
        // 如果没找到,就创建一个新用户
        users_[username] = User(username);
    }
    // 把当前用户设置为这个用户名
    currentUser_ = username;  
    // 返回登录成功
    return true;  
}

8.2 记录购买行为

// 记录用户买了什么商品
void RecordPurchase(int productId, int quantity = 1) {
    // 获取当前用户的信息
    User& user = GetCurrentUser();  
    // 在用户的购买记录里增加这个商品的购买次数
    user.purchaseHistory[productId] += quantity;  

    // 更新商品的销量和用户兴趣
    for (auto& product : products_) {
        // 找到用户购买的那个商品
        if (product.id == productId) {
            // 增加销量
            product.sales += quantity;  
            // 更新用户对这个商品类别的兴趣(买得越多兴趣越大)
            user.UpdateCategoryWeights(product.category, quantity * 0.2);
            break;  // 找到了就可以退出循环了
        }
    }
}

8.3 记录浏览行为

// 记录用户浏览了什么商品
void RecordBrowse(int productId) {
    // 获取当前用户
    User& user = GetCurrentUser();
    // 在浏览记录中增加这个商品的浏览次数
    user.browseHistory[productId] += 1;  

    // 更新用户兴趣
    for (const auto& product : products_) {
        // 找到浏览的那个商品
        if (product.id == productId) {
            // 浏览一次,这个类别的兴趣增加0.1
            user.UpdateCategoryWeights(product.category, 0.1);
            break;
        }
    }
}

9. 主菜单界面(详细解释)

// 显示系统的主菜单
void DisplayMainMenu() {
    // 打印系统标题
    cout << "\n===== 超市智能商品推荐系统 =====" << endl;
    // 打印7个功能选项
    cout << "1. 浏览所有商品" << endl;       // 查看所有商品
    cout << "2. 查看个性化推荐" << endl;     // 根据兴趣推荐
    cout << "3. 分类浏览商品" << endl;       // 按类别查看
    cout << "4. 查看优惠搭配" << endl;       // 查看打折组合
    cout << "5. 模拟购买商品" << endl;       // 假装购买商品
    cout << "6. 模拟浏览商品" << endl;       // 假装浏览商品
    cout << "7. 查看我的兴趣模型" << endl;   // 查看个人兴趣数据
    cout << "0. 退出系统" << endl;          // 退出程序
    // 提示用户输入选择
    cout << "请选择操作: ";
}

10. 主程序逻辑(详细解释)

// 程序的主函数,就像故事的开始
int main() {
    // 设置控制台能正确显示中文
    SetConsoleGBK(); 

    // 创建一个推荐系统,就像开了一家新超市
    RecommendationSystem system;  
    cout << "欢迎使用超市智能商品推荐系统!" << endl;

    // 让用户登录
    string username;
    cout << "请输入您的用户名: ";
    cin >> username;  // 获取用户输入的名字

    // 尝试登录
    if (!system.Login(username)) {
        cout << "登录失败!" << endl;
        return 1;  // 登录失败就退出程序
    }

    // 主循环:一直运行直到用户选择退出
    int choice = 0;  // 记录用户的选择
    do {
        DisplayMainMenu();  // 显示菜单
        cin >> choice;     // 获取用户输入的数字

        // 根据用户选择执行不同功能
        switch (choice) {
        case 1: {  // 浏览所有商品
            system.DisplayProducts(system.GetAllProducts());
            break;
        }
        case 2: {  // 查看个性化推荐
            auto recommendations = system.GetPersonalizedRecommendations();
            cout << "\n根据您的兴趣为您推荐以下商品:" << endl;
            system.DisplayProductsWithInterest(recommendations);
            break;
        }
        // ...(其他case类似,都是调用对应的功能函数)
        case 0:  // 退出
            cout << "感谢使用,再见!" << endl;
            break;
        default:  // 输入了不在菜单中的数字
            cout << "无效的选择,请重新输入!" << endl;
        }
    } while (choice != 0);  // 只要不选0就一直循环

    return 0;  // 程序正常结束
}

总结理解技巧

  1. 比喻理解法

    • vector想象成购物篮
    • map想象成登记表
    • 函数就像是超市里的各种服务台
  2. 执行流程

    • 程序启动 → 用户登录 → 显示菜单 → 根据选择执行功能 → 返回菜单 → ... → 退出
  3. 数据流动

    • 用户操作会影响数据(购买/浏览记录)
    • 数据变化会影响推荐结果
    • 推荐结果又会影响用户看到的内容
  4. 学习建议

    • 先理解每个小功能的作用
    • 再看它们如何组合在一起
    • 最后研究具体实现细节

这个系统就像是一个智能的超市导购员,它会记住你喜欢什么,然后给你推荐合适的商品!



手机扫码阅读
上一篇 没有了 下一篇 C语言课设———学生信息管理系统
评论