不放回抽样_生成不重样菜单

任务背景:

某餐馆有猪肉菜、牛羊肉、家禽、鱼类等菜品分类,想制作每天中晚餐不重复菜单,要求每顿菜品由猪+其他组成,且最好不要连续都是猪肉的皮排骨、烧肉系列,要间隔开。

from 复制代码
from openpyxl.styles import Alignment
import random
from collections import deque


def generate_non_repeating_meals(total_days=56):
    """菜单生成器"""
    pork_categories = {
        'pork_1': [
            "红烧肉", "萝卜烧肉", "豆泡烧肉", "海带烧肉", "鹌鹑蛋烧肉",
            "莲藕烧肉", "毛芋仔烧肉", "香菇烧肉", "虎皮蛋烧肉", "干豆条烧肉",
            "板栗烧肉", "咸鱼烧肉", "鱼蛋烧肉", "鹿茸菇烧肉", "梅干菜烧肉", "鲍鱼烧肉"],
        'pork_2': [
            "粉蒸排骨", "糯米排骨", "萝卜烧排骨", "鹌鹑蛋烧排骨", "莲藕烧排骨",
            "香芋蒸排骨", "糖醋排骨", "红烧大排", "荔浦芋头烧排", "土豆焖排骨", "豉香排骨"],
        'pork_3': [
            "红烧猪脚", "梅菜猪脚", "黄豆猪脚"],
        'pork_4': [
            "米粉蒸肉", "红烧狮子头", "盐菜扣肉", "太阳蛋"]}

    other_categories = {
        'beef': ['红烧牛杂', '红烧牛腩', '牛腩烧萝卜', '牛腩腐竹', '回锅牛肉',
                 '杭椒牛柳', '黑椒牛仔骨', '牛腩烧牛肚', '牛腩烧牛筋', '羊肉烧红萝卜',
                 '红烧羊肉', '葱爆牛肉', '孜然羊排'],
        'poultry': ['香辣鸭三件', '辣子鸡', '红烧鸡', '香菇烧鸡', '白切鸡',
                    '香酥鸭', '虎皮凤爪', '鹌鹑蛋凤爪', '啤酒鸭', '红烧鸭',
                    '香菇烧鸣', '烤鸭', '板鸭炖豆腐', '板鸭烧萝卜', '红烧鸡腿',
                    '可乐鸡中翅', '香辣卤鸡腿', '糯米蒸鸡', '莲花血鸭', '仔姜烧鹅',
                    '大盘鸡'],
        'fish': ['红烧鱼块', '南昌小炒鱼', '红烧鲫鱼', '红烧带鱼', '酒糟鱼',
                 '酸菜鱼丸', '白灼基围虾', '深海鱼尾', '小黄鱼', '酸菜鱼',
                 '鲇鱼烧豆腐', '香酥椒盐虾', '蒜蓉基围虾']}

    # 初始化状态
    last_used = {cat: deque(maxlen=3) for cat in ['pork_1', 'pork_2', 'pork_3']}
    used_dishes = {cat: set() for cat in pork_categories}
    used_other_dishes = {cat: set() for cat in other_categories}

    # 记录各类别的使用次数
    usage_count = {cat: 0 for cat in pork_categories}
    # 总餐数
    total_meals = total_days * 2  

    def select_category(category_name):
        """选择指定类别的菜品"""
        available = [d for d in pork_categories[category_name] if d not in used_dishes[category_name]]
        if not available:
            # 重置该类别的已使用记录
            used_dishes[category_name] = set()
            available = pork_categories[category_name]
        dish = random.choice(available)
        used_dishes[category_name].add(dish)
        return dish

    def select_pork(meal_type, day):
        """选择猪肉菜品并更新状态"""
        nonlocal usage_count

        available_cats = []

        # 首先检查有间隔限制的类别 (pork_1, pork_2, pork_3)
        for cat in ['pork_1', 'pork_2', 'pork_3']:
            # 检查间隔限制 - 增加到3顿间隔
            if last_used[cat]:
                last_day, last_meal_type = last_used[cat][-1]
                # 检查是否间隔足够(至少间隔3顿)
                # 如果同一天,或者第二天,或者第三天同一餐次,都不能使用
                if (day == last_day) or \
                        (day == last_day + 1) or \
                        (day == last_day + 2 and meal_type == last_meal_type):
                    continue

            # 检查可用菜品
            available = [d for d in pork_categories[cat] if d not in used_dishes[cat]]
            if available:
                available_cats.append((cat, available))

        # 计算当前pork_4的使用率
        pork_4_usage_rate = usage_count['pork_4'] / total_meals if total_meals > 0 else 0

        # 决定是否使用pork_4 - 更严格的控制
        use_pork_4 = False

        # 只有在以下情况下才使用pork_4:
        # 1. 没有其他可用类别
        # 2. pork_4使用率低于25%且随机概率很低
        if not available_cats:
            use_pork_4 = True
        elif pork_4_usage_rate < 0.25 and random.random() < 0.05:  # 降低到5%概率
            use_pork_4 = True

        # 优先选择pork_1和pork_2
        if available_cats and not use_pork_4:
            # 给pork_1和pork_2更高的权重
            weighted_cats = []
            for cat, dishes in available_cats:
                if cat == 'pork_1':
                    weighted_cats.extend([(cat, dishes)] * 4)  # 增加pork_1权重为4
                elif cat == 'pork_2':
                    weighted_cats.extend([(cat, dishes)] * 3)  # 增加pork_2权重为3
                else:  # pork_3
                    weighted_cats.append((cat, dishes))  # pork_3权重为1

            cat, dishes = random.choice(weighted_cats)
            dish = random.choice(dishes)
            used_dishes[cat].add(dish)
            usage_count[cat] += 1

            # 更新使用记录
            if cat in ['pork_1', 'pork_2', 'pork_3']:
                last_used[cat].append((day, meal_type))

            return dish, cat
        else:
            # 使用pork_4
            available_pork_4 = [d for d in pork_categories['pork_4'] if d not in used_dishes['pork_4']]
            if available_pork_4:
                dish = random.choice(available_pork_4)
                used_dishes['pork_4'].add(dish)
                usage_count['pork_4'] += 1
                return dish, 'pork_4'
            else:
                # 如果pork_4不可用,尝试重置其他类别
                for cat in ['pork_1', 'pork_2', 'pork_3']:
                    if not [d for d in pork_categories[cat] if d not in used_dishes[cat]]:
                        used_dishes[cat] = set()  # 重置该类别的已使用记录

                # 重新检查可用类别
                available_cats = []
                for cat in ['pork_1', 'pork_2', 'pork_3']:
                    if last_used[cat]:
                        last_day, last_meal_type = last_used[cat][-1]
                        if (day == last_day) or \
                                (day == last_day + 1) or \
                                (day == last_day + 2 and meal_type == last_meal_type):
                            continue

                    available = [d for d in pork_categories[cat] if d not in used_dishes[cat]]
                    if available:
                        available_cats.append((cat, available))

                if available_cats:
                    # 使用权重选择
                    weighted_cats = []
                    for cat, dishes in available_cats:
                        if cat == 'pork_1':
                            weighted_cats.extend([(cat, dishes)] * 4)
                        elif cat == 'pork_2':
                            weighted_cats.extend([(cat, dishes)] * 3)
                        else:
                            weighted_cats.append((cat, dishes))

                    cat, dishes = random.choice(weighted_cats)
                    dish = random.choice(dishes)
                    used_dishes[cat].add(dish)
                    usage_count[cat] += 1

                    if cat in ['pork_1', 'pork_2', 'pork_3']:
                        last_used[cat].append((day, meal_type))

                    return dish, cat
                else:
                    # 紧急情况使用pork_4(重置)
                    used_dishes['pork_4'] = set()
                    dish = random.choice(pork_categories['pork_4'])
                    used_dishes['pork_4'].add(dish)
                    usage_count['pork_4'] += 1
                    return dish, 'pork_4'

    def select_other():
        """选择其他肉类"""
        available_cats = []
        for cat, dishes in other_categories.items():
            available = [d for d in dishes if d not in used_other_dishes[cat]]
            if available:
                available_cats.append((cat, available))

        if not available_cats:
            # 重置所有其他肉类池
            for cat in other_categories:
                used_other_dishes[cat] = set()
            available_cats = [(cat, dishes) for cat, dishes in other_categories.items()]

        cat, dishes = random.choice(available_cats)
        dish = random.choice(dishes)
        used_other_dishes[cat].add(dish)
        return dish

    # 生成菜单
    meals = []
    for day in range(total_days):
        # 午餐
        lunch_pork, lunch_pork_type = select_pork('lunch', day)
        lunch_other = select_other()

        # 晚餐
        dinner_pork, dinner_pork_type = select_pork('dinner', day)
        dinner_other = select_other()

        meals.append({
            'lunch': [lunch_pork, lunch_other],
            'dinner': [dinner_pork, dinner_other],
            'lunch_pork_type': lunch_pork_type,
            'dinner_pork_type': dinner_pork_type
        })

    return meals


def write_meat_to_all_sheets(file_path, output_path):
    """Excel写入器"""
    wb = load_workbook(file_path)
    all_meals = generate_non_repeating_meals(56)

    # 按周分组
    weekly_meals = [all_meals[i * 7:(i + 1) * 7] for i in range(8)]

    for sheet_idx, (sheet_name, week_meals) in enumerate(zip(wb.sheetnames, weekly_meals)):
        if sheet_idx >= len(weekly_meals):
            break

        ws = wb[sheet_name]

        # 指定写入数据列(A、C、E、G、I、K、M列)
        odd_columns = [1, 3, 5, 7, 9, 11, 13]

        # 写入数据
        for day, meals in enumerate(week_meals):
            col = odd_columns[day]

            # 午餐
            ws.cell(row=4, column=col, value=meals['lunch'][0])
            ws.cell(row=5, column=col, value=meals['lunch'][1])

            # 晚餐
            ws.cell(row=11, column=col, value=meals['dinner'][0])
            ws.cell(row=12, column=col, value=meals['dinner'][1])

        # 设置格式
        for row in [4, 5, 11, 12]:
            for col in odd_columns:
                ws.cell(row=row, column=col).alignment = Alignment(
                    horizontal='center',
                    vertical='center',
                    wrap_text=True)

        print(f"已处理 sheet【{sheet_name}】的肉类数据")

    wb.save(output_path)
    print(f"所有肉类数据已写入: {output_path}")

    # 打印统计信息
    print("\n菜单统计:")
    pork_types_count = {"pork_1": 0, "pork_2": 0, "pork_3": 0, "pork_4": 0}
    for meal in all_meals:
        pork_types_count[meal['lunch_pork_type']] += 1
        pork_types_count[meal['dinner_pork_type']] += 1

    total_pork_meals = sum(pork_types_count.values())
    for cat, count in pork_types_count.items():
        percentage = count / total_pork_meals * 100
        print(f"{cat}使用次数: {count} ({percentage:.1f}%)")

if __name__ == "__main__":
    input_path = r'D:\文档\WXWork\1688855493661308\Cache\File\2025-11\食堂菜单 - 副本.xlsm'
    output_path = r'D:\优化版食堂菜单.xlsx'

    write_meat_to_all_sheets(input_path, output_path)```
相关推荐
程序员小远6 小时前
Postman接口测试: Postman环境变量&全局变量设置,多接口顺序执行详解
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
程序员三藏6 小时前
Postman定义公共函数
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
小麦果汁吨吨吨7 小时前
Python:word(doc、docx)批量转pdf
python
深蓝电商API7 小时前
异步爬虫的终极形态:aiohttp + asyncio 实现万级并发实践
爬虫·python·aiohttp
用户8356290780517 小时前
Python创建PDF文档:解锁高效PDF创建的能力
后端·python
Howard在远程7 小时前
[编程农场][The Farmer Was Replaced]——Bones/Dinasour
python·游戏
vxiam14xxx7 小时前
【Java Swing 项目】java 聊天系统
java·开发语言·python
Howard在远程8 小时前
[编程农场][The Farmer Was Replaced]——Carrot & Pumpkin
python
PieroPc8 小时前
用python streamlit sqlite3 写一个聊天室
python·streamlit·聊天室