基于JavaSE实现协同过滤算法_电商商品推荐

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class ECommerceRecommender {
    // 商品类
    static class Product {
        private int id;
        private String name;
        private String category;
        private double price;
        private String description;

        public Product(int id, String name, String category, double price, String description) {
            this.id = id;
            this.name = name;
            this.category = category;
            this.price = price;
            this.description = description;
        }

        public int getId() { return id; }
        public String getName() { return name; }
        public String getCategory() { return category; }
        public double getPrice() { return price; }
        public String getDescription() { return description; }

        @Override
        public String toString() {
            return String.format("商品ID: %d, 名称: %s, 类别: %s, 价格: %.2f元",
                    id, name, category, price);
        }
    }

    // 用户行为类
    static class UserBehavior {
        private int userId;
        private int productId;
        private double rating;
        private String timestamp;

        public UserBehavior(int userId, int productId, double rating, String timestamp) {
            this.userId = userId;
            this.productId = productId;
            this.rating = rating;
            this.timestamp = timestamp;
        }

        public int getUserId() { return userId; }
        public int getProductId() { return productId; }
        public double getRating() { return rating; }
        public String getTimestamp() { return timestamp; }
    }

    // 推荐系统核心类
    static class RecommendationSystem {
        protected List<Product> products;
        protected List<UserBehavior> userBehaviors;
        protected Map<Integer, Map<Integer, Double>> userProductMatrix;
        protected Map<Integer, Map<Integer, Double>> similarityMatrix;

        private final Random random = new Random();
        private final double timeFactor = 0.1;
        private final double categoryDiversityFactor = 0.2;
        private final double randomnessFactor = 0.2;
        private final double categoryBoostFactor = 0.15;
        private final int candidatePoolSize = 15;
        private final double similarityThreshold = 0.5;
        private final double timeDecayFactor = 0.05;

        public RecommendationSystem() {
            this.products = new ArrayList<>();
            this.userBehaviors = new ArrayList<>();
            this.userProductMatrix = new HashMap<>();
            this.similarityMatrix = new HashMap<>();
        }

        public void addProduct(Product product) {
            products.add(product);
        }

        public List<Product> getProducts() {
            return products;
        }

        public void addUserBehavior(UserBehavior behavior) {
            userBehaviors.add(behavior);
            userProductMatrix.computeIfAbsent(behavior.getUserId(), k -> new HashMap<>())
                    .put(behavior.getProductId(), behavior.getRating());
        }

        public void calculateSimilarityMatrix() {
            for (Product p1 : products) {
                similarityMatrix.put(p1.getId(), new HashMap<>());
                for (Product p2 : products) {
                    if (p1.getId() != p2.getId()) {
                        double similarity = calculateProductSimilarity(p1.getId(), p2.getId());
                        similarityMatrix.get(p1.getId()).put(p2.getId(), similarity);
                    }
                }
            }
        }

        private double calculateProductSimilarity(int product1Id, int product2Id) {
            List<Double> ratings1 = new ArrayList<>();
            List<Double> ratings2 = new ArrayList<>();

            for (Map.Entry<Integer, Map<Integer, Double>> userRatings : userProductMatrix.entrySet()) {
                Double rating1 = userRatings.getValue().get(product1Id);
                Double rating2 = userRatings.getValue().get(product2Id);
                if (rating1 != null && rating2 != null) {
                    ratings1.add(rating1);
                    ratings2.add(rating2);
                }
            }

            if (ratings1.isEmpty()) return 0.0;

            // 计算余弦相似度
            double dotProduct = 0.0;
            double norm1 = 0.0;
            double norm2 = 0.0;

            for (int i = 0; i < ratings1.size(); i++) {
                dotProduct += ratings1.get(i) * ratings2.get(i);
                norm1 += ratings1.get(i) * ratings1.get(i);
                norm2 += ratings2.get(i) * ratings2.get(i);
            }

            if (norm1 == 0.0 || norm2 == 0.0) return 0.0;

            // 添加类别相似度权重
            double similarity = dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
            Product p1 = getProductById(product1Id);
            Product p2 = getProductById(product2Id);

            if (p1.getCategory().equals(p2.getCategory())) {
                similarity *= 1.2; // 同类别商品相似度提升
            }

            return similarity;
        }

        public List<Product> recommendProducts(int userId, int numRecommendations) {
            Map<Integer, Double> userRatings = userProductMatrix.getOrDefault(userId, new HashMap<>());
            Set<Integer> ratedProducts = new HashSet<>(userRatings.keySet());
            Map<Integer, Double> predictedRatings = new HashMap<>();
            Map<String, Integer> userCategories = getUserCategoryDistribution(userId);

            // 计算所有未评分商品的预测评分
            for (Product product : products) {
                if (!ratedProducts.contains(product.getId())) {
                    double predictedRating = predictRating(userId, product.getId());
                    if (predictedRating > 0) {
                        predictedRating = applyTimeDecay(predictedRating, product.getId(), userId);
                        predictedRating = applyCategoryDiversity(predictedRating,
                                product.getCategory(), userCategories);
                        predictedRating = applyRandomness(predictedRating);
                        predictedRatings.put(product.getId(), predictedRating);
                    }
                }
            }

            return getTopRecommendationsWithDiversity(predictedRatings,
                    numRecommendations, userCategories);
        }

        private double predictRating(int userId, int productId) {
            Map<Integer, Double> userRatings = userProductMatrix.get(userId);
            if (userRatings == null || userRatings.isEmpty()) return 0.0;

            // 获取相似商品的评分
            List<AbstractMap.SimpleEntry<Integer, Double>> similarProducts = new ArrayList<>();
            for (Map.Entry<Integer, Double> entry : userRatings.entrySet()) {
                double similarity = similarityMatrix.get(productId)
                        .getOrDefault(entry.getKey(), 0.0);
                if (similarity > similarityThreshold) {
                    similarProducts.add(new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue()));
                }
            }

            if (similarProducts.isEmpty()) return 0.0;

            // 计算加权平均评分
            double weightedSum = 0.0;
            double similaritySum = 0.0;

            for (AbstractMap.SimpleEntry<Integer, Double> entry : similarProducts) {
                double similarity = similarityMatrix.get(productId)
                        .getOrDefault(entry.getKey(), 0.0);
                weightedSum += similarity * entry.getValue();
                similaritySum += Math.abs(similarity);
            }

            return similaritySum == 0.0 ? 0.0 : weightedSum / similaritySum;
        }

        // 在RecommendationSystem类中修改applyTimeDecay方法
        private double applyTimeDecay(double rating, int productId, int userId) {
            final int finalUserId = userId; // 创建final变量用于lambda表达式
            final int finalProductId = productId;

            Optional<String> lastBehaviorTime = userBehaviors.stream()
                    .filter(b -> b.getUserId() == finalUserId &&
                            getProductById(b.getProductId()).getCategory()
                                    .equals(getProductById(finalProductId).getCategory()))
                    .map(UserBehavior::getTimestamp)
                    .max(String::compareTo);

            if (lastBehaviorTime.isPresent()) {
                try {
                    Date lastTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                            .parse(lastBehaviorTime.get());
                    long daysDiff = TimeUnit.MILLISECONDS
                            .toDays(System.currentTimeMillis() - lastTime.getTime());
                    return rating * Math.exp(-timeDecayFactor * daysDiff / 30.0);
                } catch (ParseException e) {
                    return rating;
                }
            }
            return rating;
        }

        private double applyCategoryDiversity(double rating, String category,
                                              Map<String, Integer> userCategories) {
            int categoryCount = userCategories.getOrDefault(category, 0);
            int totalBehaviors = userCategories.values().stream()
                    .mapToInt(Integer::intValue).sum();

            if (totalBehaviors == 0) return rating;

            double categoryRatio = categoryCount / (double) totalBehaviors;

            if (categoryRatio > 0.3) {
                return rating * (1.0 - categoryDiversityFactor * categoryRatio);
            } else {
                return rating * (1.0 + categoryDiversityFactor * (0.3 - categoryRatio));
            }
        }

        private double applyRandomness(double rating) {
            return rating * (1 + (random.nextDouble() - 0.5) * randomnessFactor);
        }

        private List<Product> getTopRecommendationsWithDiversity(
                Map<Integer, Double> predictedRatings,
                int numRecommendations,
                Map<String, Integer> userCategories) {

            // 创建候选池
            List<Map.Entry<Integer, Double>> candidatePool = predictedRatings.entrySet().stream()
                    .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
                    .limit(candidatePoolSize)
                    .collect(Collectors.toList());

            List<Product> recommendations = new ArrayList<>();
            Set<String> selectedCategories = new HashSet<>();

            while (recommendations.size() < numRecommendations && !candidatePool.isEmpty()) {
                Map<Integer, Double> scores = new HashMap<>();

                for (Map.Entry<Integer, Double> entry : candidatePool) {
                    Product product = getProductById(entry.getKey());
                    double score = entry.getValue();

                    // 类别多样性调整
                    if (selectedCategories.contains(product.getCategory())) {
                        score *= (1 - categoryBoostFactor);
                    }

                    // 添加随机因素
                    score *= (1 + (random.nextDouble() - 0.5) * 0.2);
                    scores.put(entry.getKey(), score);
                }

                // 选择最高分的商品
                Map.Entry<Integer, Double> selected = scores.entrySet().stream()
                        .max(Map.Entry.comparingByValue())
                        .orElse(null);

                if (selected != null) {
                    Product selectedProduct = getProductById(selected.getKey());
                    recommendations.add(selectedProduct);
                    selectedCategories.add(selectedProduct.getCategory());
                    candidatePool.removeIf(entry -> entry.getKey().equals(selected.getKey()));
                }
            }

            return recommendations;
        }

        // 在RecommendationSystem类中修改getUserCategoryDistribution方法
        private Map<String, Integer> getUserCategoryDistribution(int userId) {
            final int finalUserId = userId; // 创建final变量用于lambda表达式
            return userBehaviors.stream()
                    .filter(b -> b.getUserId() == finalUserId)
                    .map(b -> getProductById(b.getProductId()))
                    .filter(Objects::nonNull)
                    .map(Product::getCategory)
                    .collect(Collectors.groupingBy(
                            category -> category,
                            Collectors.collectingAndThen(Collectors.counting(), Long::intValue)
                    ));
        }

        private Product getProductById(int productId) {
            return products.stream()
                    .filter(p -> p.getId() == productId)
                    .findFirst()
                    .orElse(null);
        }
    }

    // 行为数据生成器
    static class BehaviorGenerator {
        private final Random random = new Random();
        private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        private final Map<String, List<String>> userProfiles = new HashMap<String, List<String>>() {{
            put("APPLE_FAN", Arrays.asList("Apple", "iPhone", "MacBook", "iPad", "AirPods"));
            put("ANDROID_USER", Arrays.asList("小米", "三星", "OPPO", "vivo", "realme"));
            put("HUAWEI_FAN", Arrays.asList("华为", "荣耀", "HUAWEI", "FreeBuds"));
            put("PROFESSIONAL", Arrays.asList("索尼", "佳能", "富士", "尼康", "戴尔", "ThinkPad"));
            put("GAMER", Arrays.asList("ROG", "天选", "枪神", "游戏本"));
            put("SMART_HOME", Arrays.asList("智能", "米家", "戴森", "科沃斯"));
            put("LUXURY", Arrays.asList("Pro", "旗舰", "专业", "高端"));
            put("BUDGET", Arrays.asList("Redmi", "realme", "小新", "入门"));
            put("AUDIO", Arrays.asList("耳机", "音响", "降噪", "HIFI"));
            put("SPORTS", Arrays.asList("运动", "Watch", "Garmin", "手表"));
        }};

        private final Map<String, Double> categoryWeights = new HashMap<String, Double>() {{
            put("手机", 0.3);
            put("笔记本电脑", 0.2);
            put("耳机", 0.15);
            put("智能手表", 0.1);
            put("平板电脑", 0.1);
            put("智能家居", 0.08);
            put("相机", 0.07);
        }};

        public List<UserBehavior> generateBehaviors(List<Product> products, int userCount,
                                                    int minBehaviorsPerUser, int maxBehaviorsPerUser) {
            List<UserBehavior> behaviors = new ArrayList<>();
            Map<Integer, String> userProfileMap = assignUserProfiles(userCount);

            for (int userId = 1; userId <= userCount; userId++) {
                String userProfile = userProfileMap.get(userId);
                int behaviorCount = minBehaviorsPerUser +
                        random.nextInt(maxBehaviorsPerUser - minBehaviorsPerUser + 1);

                List<Product> preferredProducts = getPreferredProducts(products, userProfile);
                Set<Integer> ratedProducts = new HashSet<>();

                for (int i = 0; i < behaviorCount; i++) {
                    Product product = selectProduct(products, preferredProducts, ratedProducts);
                    if (product == null) continue;

                    ratedProducts.add(product.getId());
                    double rating = generateRating(product, userProfile);
                    String timestamp = generateTimestamp();

                    behaviors.add(new UserBehavior(userId, product.getId(), rating, timestamp));
                }
            }

            behaviors.sort((b1, b2) -> {
                try {
                    return sdf.parse(b1.getTimestamp()).compareTo(sdf.parse(b2.getTimestamp()));
                } catch (ParseException e) {
                    return 0;
                }
            });

            return behaviors;
        }

        private Map<Integer, String> assignUserProfiles(int userCount) {
            Map<Integer, String> userProfileMap = new HashMap<>();
            List<String> profiles = new ArrayList<>(userProfiles.keySet());

            for (int userId = 1; userId <= userCount; userId++) {
                String profile = profiles.get((userId - 1) % profiles.size());
                userProfileMap.put(userId, profile);
            }
            return userProfileMap;
        }

        private List<Product> getPreferredProducts(List<Product> products, String userProfile) {
            List<String> preferences = userProfiles.get(userProfile);
            return products.stream()
                    .filter(p -> preferences.stream()
                            .anyMatch(pref -> p.getName().contains(pref) ||
                                    p.getDescription().contains(pref)))
                    .collect(Collectors.toList());
        }

        private Product selectProduct(List<Product> allProducts, List<Product> preferredProducts,
                                      Set<Integer> ratedProducts) {
            boolean selectPreferred = random.nextDouble() < 0.8 && !preferredProducts.isEmpty();
            List<Product> candidateProducts = selectPreferred ? preferredProducts : allProducts;

            List<Product> availableProducts = candidateProducts.stream()
                    .filter(p -> !ratedProducts.contains(p.getId()))
                    .collect(Collectors.toList());

            if (availableProducts.isEmpty()) return null;

            double totalWeight = availableProducts.stream()
                    .mapToDouble(p -> categoryWeights.getOrDefault(p.getCategory(), 0.1))
                    .sum();

            double randomValue = random.nextDouble() * totalWeight;
            double currentWeight = 0.0;

            for (Product product : availableProducts) {
                currentWeight += categoryWeights.getOrDefault(product.getCategory(), 0.1);
                if (currentWeight >= randomValue) {
                    return product;
                }
            }

            return availableProducts.get(availableProducts.size() - 1);
        }

        private double generateRating(Product product, String userProfile) {
            boolean isPreferred = userProfiles.get(userProfile).stream()
                    .anyMatch(pref -> product.getName().contains(pref) ||
                            product.getDescription().contains(pref));

            if (isPreferred) {
                return 4.0 + Math.pow(random.nextDouble(), 2);
            } else {
                return Math.max(2.0, Math.min(5.0, random.nextGaussian() * 0.5 + 3.5));
            }
        }

        private String generateTimestamp() {
            Calendar cal = Calendar.getInstance();
            long currentTime = cal.getTimeInMillis();
            long ninetyDaysInMillis = 90L * 24 * 60 * 60 * 1000;
            long randomOffset = (long)(random.nextDouble() * ninetyDaysInMillis);
            long randomTime = currentTime - randomOffset;
            return sdf.format(new Date(randomTime));
        }
    }

    // 主方法
    public static void main(String[] args) {
        // 创建推荐系统实例
        RecommendationSystem rs = new RecommendationSystem();

        // 添加商品数据
        Product[] products = {
                // 手机品类
                new Product(1, "Apple iPhone 14 Pro", "手机", 8999.00, "iOS旗舰智能手机"),
                new Product(2, "小米13", "手机", 3999.00, "安卓旗舰智能手机"),
                new Product(3, "三星 Galaxy S23", "手机", 6999.00, "安卓旗舰手机"),
                new Product(4, "OPPO Find X6", "手机", 4999.00, "影像旗舰手机"),
                new Product(5, "vivo X90 Pro", "手机", 5499.00, "专业影像旗舰"),
                new Product(6, "荣耀 Magic5", "手机", 4299.00, "全能旗舰手机"),
                new Product(7, "一加 11", "手机", 3999.00, "性能旗舰手机"),
                new Product(8, "Redmi K60", "手机", 2499.00, "性价比旗舰"),
                new Product(9, "iPhone 13", "手机", 5999.00, "iOS经典机型"),
                new Product(10, "realme GT Neo5", "手机", 2699.00, "年轻人首选"),

                // 笔记本电脑品类
                new Product(11, "华为 MateBook X Pro", "笔记本电脑", 8999.00, "轻薄商务本"),
                new Product(12, "戴尔 XPS 15", "笔记本电脑", 12999.00, "性能商务本"),
                new Product(13, "MacBook Pro 14", "笔记本电脑", 14999.00, "专业创作本"),
                new Product(14, "联想 小新Pro16", "笔记本电脑", 5999.00, "全能学生本"),
                new Product(15, "ROG 枪神7", "笔记本电脑", 13999.00, "电竞游戏本"),
                new Product(16, "惠普 星耀7", "笔记本电脑", 4999.00, "入门商务本"),
                new Product(17, "戴尔 灵越16", "笔记本电脑", 6499.00, "高性价比本"),
                new Product(18, "ThinkPad X1", "笔记本电脑", 11999.00, "商务旗舰本"),
                new Product(19, "MacBook Air M2", "笔记本电脑", 9499.00, "轻薄办公本"),
                new Product(20, "华硕 天选3", "笔记本电脑", 7999.00, "游戏影音本"),

                // 耳机品类
                new Product(21, "索尼 WH-1000XM4", "耳机", 2699.00, "无线降噪耳机"),
                new Product(22, "Apple AirPods Pro", "耳机", 1999.00, "无线降噪耳机"),
                new Product(23, "华为 FreeBuds Pro2", "耳机", 1099.00, "无线耳机"),
                new Product(24, "Bose QC45", "耳机", 2499.00, "头戴降噪耳机"),
                new Product(25, "小米 Buds 4 Pro", "耳机", 999.00, "无线耳机"),
                new Product(26, "AirPods 3", "耳机", 1399.00, "无线耳机"),
                new Product(27, "OPPO Enco X2", "耳机", 999.00, "无线耳机"),
                new Product(28, "Beats Studio3", "耳机", 2299.00, "头戴耳机"),
                new Product(29, "森海塞尔 HD660S", "耳机", 3399.00, "HIFI耳机"),
                new Product(30, "JBL LIVE660NC", "耳机", 899.00, "蓝牙耳机"),

                // 智能手表品类
                new Product(31, "华为 Watch GT3", "智能手表", 1488.00, "运动智能手表"),
                new Product(32, "Apple Watch Series 8", "智能手表", 3199.00, "智能手表"),
                new Product(33, "小米手表S2", "智能手表", 999.00, "运动手表"),
                new Product(34, "OPPO Watch 3 Pro", "智能手表", 1999.00, "智能手表"),
                new Product(35, "Garmin 255", "智能手表", 2899.00, "专业运动表"),

                // 平板电脑品类
                new Product(36, "iPad Pro 12.9", "平板电脑", 7999.00, "专业平板"),
                new Product(37, "华为 MatePad Pro", "平板电脑", 4999.00, "办公平板"),
                new Product(38, "小米平板6 Pro", "平板电脑", 2999.00, "娱乐平板"),
                new Product(39, "三星 Tab S8", "平板电脑", 5999.00, "旗舰平板"),
                new Product(40, "联想小新Pad", "平板电脑", 1799.00, "学习平板"),

                // 智能家居品类
                new Product(41, "小米空气净化器4", "智能家居", 999.00, "空气净化"),
                new Product(42, "戴森 V15", "智能家居", 4499.00, "无线吸尘器"),
                new Product(43, "科沃斯 T10", "智能家居", 3999.00, "扫地机器人"),
                new Product(44, "米家投影仪2", "智能家居", 2799.00, "智能投影"),
                new Product(45, "华为智慧屏V75", "智能家居", 7999.00, "智能电视"),

                // 相机品类
                new Product(46, "索尼 A7M4", "相机", 15999.00, "全画幅微单"),
                new Product(47, "佳能 R6", "相机", 14999.00, "专业微单"),
                new Product(48, "富士 X-T5", "相机", 12999.00, "文艺复古相机"),
                new Product(49, "尼康 Z6II", "相机", 13999.00, "专业微单"),
                new Product(50, "松下 GH6", "相机", 16999.00, "视频微单")
        };

        // 添加商品数据到推荐系统
        for (Product product : products) {
            rs.addProduct(product);
        }

        // 使用行为生成器生成用户行为数据
        BehaviorGenerator generator = new BehaviorGenerator();
        List<UserBehavior> behaviors = generator.generateBehaviors(
                rs.getProducts(),  // 所有商品
                100,              // 100个用户
                20,               // 每个用户最少20个行为
                50                // 每个用户最多50个行为
        );

        // 添加生成的行为数据
        for (UserBehavior behavior : behaviors) {
            rs.addUserBehavior(behavior);
        }

        // 计算相似度矩阵
        rs.calculateSimilarityMatrix();

        // 输出行为数据统计
        System.out.println("生成的用户行为总数: " + behaviors.size());

        // 为多个用户获取推荐并验证多样性
        // 为同一个用户多次获取推荐,验证结果的多样性
        for (int userId = 1; userId <= 5; userId++) {
            final int finalUserId = userId; // 创建final变量用于lambda表达式
            System.out.println("\n用户 " + finalUserId + " 的推荐商品:");

            // 每个用户获取3次推荐
            for (int i = 0; i < 3; i++) {
                System.out.println("\n第 " + (i + 1) + " 次推荐:");
                List<Product> recommendations = rs.recommendProducts(finalUserId, 5);
                recommendations.forEach(System.out::println);
            }

            // 创建final引用用于lambda表达式
            final RecommendationSystem finalRs = rs;

            // 输出用户的类别偏好统计
            Map<String, Long> categoryStats = behaviors.stream()
                    .filter(b -> b.getUserId() == finalUserId)
                    .map(b -> finalRs.getProductById(b.getProductId()).getCategory())
                    .collect(Collectors.groupingBy(
                            category -> category,
                            Collectors.counting()
                    ));

            System.out.println("\n用户 " + finalUserId + " 的历史行为类别统计:");
            categoryStats.forEach((category, count) ->
                    System.out.println(category + ": " + count + " 次"));
        }
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值