如何高效计算预定义产品选项所有组合的销量总和?
产品选项组合销量统计优化方案
问题背景
需要统计预定义产品选项所有可能组合的销量总和,销售列表中可能包含无关产品或无效数据。现有代码能实现基本功能,但当组合数量过大(如1-3亿)时运行极慢或崩溃,同时不确定如何处理选项中的None值。
现有尝试代码
# 检查组合中的所有选项是否存在于字符串中 def check_code_in_string(combinations, string): for element in combinations: if element is None: return False element = element # + r'(-|$)' if not re.search(re.escape(element), string): return False return True # 选项列表 product_type = ['ProductA', 'ProductB', 'ProductC'] product_number = ['1234', '5678', '9012'] option_a = ['-A1', '-A2', '-A3', None] option_b = ['-B1', '-B2', '-B3', None] # 销售列表 sales = [['ProductA-1234-A1-B3', 25], ['ProductA-5678', 18], ['ProductC-9012-A3-B3', 28], ['ProductB-5678-A3-B2', 57], ['ProductA-9012-A1-B3', 22], ['ProductB-9012-A3-B3', 114], ['ProductB-1234-B2', 7], ['ProductA-5678', 3], ['ProductB-1234-B2', 15], ['ProductA-1234-A1-B3', 12], ['ProductA-9012-A1-B3', 62], ['ProductB-9012-A3-B3', 215]] # 生成所有可能的组合列表 lst = [product_type, product_number, option_a, option_b] combinations = itertools.product(*lst) # 添加销量列,初始值为0 tmp = [] for combination in combinations: tmp.append([combination[0], combination[1], combination[2], combination[3], 0]) combinations = tmp # 遍历所有产品和组合,累加销量 for i, combination in enumerate(combinations): for product in sales: if check_code_in_string(combination[:-1], product[0]): combinations[i][4] = float(combinations[i][4]) + product[1] # 移除销量为0的组合 tmp = [] for combination in combinations: if combination[4] > 0: tmp.append(combination) reduced_combinations = tmp print(*reduced_combinations, sep='\n')
优化方案
1. 核心思路转变:反向统计,避免海量组合生成
原代码先生成所有可能组合再匹配销售数据,亿级组合场景下直接触发内存溢出。正确逻辑是从销售数据出发,解析每条记录并匹配符合条件的预定义组合,仅保留有销量的组合,彻底规避无效内存占用。
2. 明确None值的业务逻辑
None代表该选项未填充,即销售记录中可以没有对应选项标识。比如组合中option_a为None时,只要销售记录包含该组合的产品类型、编号、option_b(若有要求),无论是否有option_a的标识,都视为匹配。
3. 高效解析与匹配
提前拆分销售字符串提取特征,避免重复正则匹配;用字典存储销量,实现O(1)的累加效率。
完整优化代码
import itertools from collections import defaultdict # 预定义选项列表 product_types = ['ProductA', 'ProductB', 'ProductC'] product_numbers = ['1234', '5678', '9012'] option_as = ['-A1', '-A2', '-A3', None] option_bs = ['-B1', '-B2', '-B3', None] # 销售数据 sales = [['ProductA-1234-A1-B3', 25], ['ProductA-5678', 18], ['ProductC-9012-A3-B3', 28], ['ProductB-5678-A3-B2', 57], ['ProductA-9012-A1-B3', 22], ['ProductB-9012-A3-B3', 114], ['ProductB-1234-B2', 7], ['ProductA-5678', 3], ['ProductB-1234-B2', 15], ['ProductA-1234-A1-B3', 12], ['ProductA-9012-A1-B3', 62], ['ProductB-9012-A3-B3', 215]] # 构建所有有效组合的集合(用tuple作为字典键) all_valid_combinations = set(itertools.product(product_types, product_numbers, option_as, option_bs)) # 初始化销量统计字典 sales_stats = defaultdict(int) # 处理每条销售记录 for sale_str, quantity in sales: parts = sale_str.split('-') sale_type = parts[0] sale_number = parts[1] if len(parts) >=2 else None # 过滤无关产品 if sale_type not in product_types or sale_number not in product_numbers: continue # 提取销售记录中的选项标识 sale_options = set(f'-{p}' for p in parts[2:]) # 匹配所有符合当前产品类型、编号的组合 for opt_a in option_as: for opt_b in option_bs: combo = (sale_type, sale_number, opt_a, opt_b) if combo not in all_valid_combinations: continue # 判断选项是否匹配:None表示无要求,否则必须存在于销售选项中 match = True if opt_a is not None and opt_a not in sale_options: match = False if opt_b is not None and opt_b not in sale_options: match = False if match: sales_stats[combo] += quantity # 转换为目标格式并过滤无销量组合 result = [[*combo, count] for combo, count in sales_stats.items() if count > 0] # 输出结果 for item in result: print(item)
优化效果说明
- 内存优化:仅存储有销量的组合,亿级组合场景下内存占用从GB级降至KB/MB级。
- 性能提升:将原O(MN)的双重循环(M为组合数,N为销售数)改为O(NO1*O2)(O1、O2为选项列表长度,远小于M),运行效率呈数量级提升。
- 逻辑严谨:明确
None值的处理规则,符合业务预期;提前过滤无关产品,减少无效计算。
内容的提问来源于stack exchange,提问作者user3662357
相关产品推荐
相关产品推荐

