You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pulp线性规划中约束生成式表达式失效及新增约束需求

Fixing Mango Pack Optimization Constraints in Pulp

I see where you went wrong with your initial constraint—let's break this down step by step, then fix both the category and age constraints properly.

Why Your Original Constraint Didn't Work

You tried using Python's len(set(...)) and checking lp_pack_vars[name] == 1 directly in a Pulp constraint, but that's a common mistake. Pulp works with symbolic variables during model setup—these variables don't have numerical values until the solver runs. Your code was trying to compute a set based on unassigned variables, which leads to NoneType errors and constraints the solver can't interpret at all.


Correct Implementation of Constraints

1. Limit Selected Packs to ≤2 Categories

To model this, we need to:

  • Track which categories are being used with binary variables
  • Ensure that if a pack is selected, its corresponding category is marked as "used"
  • Restrict the total number of used categories to 2 or fewer

2. Limit Age Difference to ≤25

For this, we'll use the "big M" method to handle conditional constraints (only apply age bounds to selected packs):

  • Introduce variables for the minimum and maximum age of selected packs
  • Ensure every selected pack's age falls between these bounds
  • Enforce that the difference between max and min age is ≤25

Full Working Code

Here's the updated optimise function with both constraints properly implemented:

import pulp

def optimise(mango_packs, mango_count):
    pack_names = list(mango_packs.keys())
    prob = pulp.LpProblem("MangoPacks", pulp.LpMinimize)
    
    # Binary variable: 1 if we open the pack, 0 otherwise
    lp_pack_vars = pulp.LpVariable.dicts("OpenPack", pack_names, 0, 1, "Integer")
    
    # --- Objective Function ---
    # Prioritize minimizing leftover mangoes (weighted by a large factor)
    # Then minimize the number of packs opened
    total_mangoes = pulp.lpSum([mango_packs[name]["count"] * lp_pack_vars[name] for name in pack_names])
    leftover_weight = len(mango_packs)  # Large enough to prioritize leftover reduction
    prob += leftover_weight * total_mangoes + pulp.lpSum(lp_pack_vars.values())
    
    # --- Base Constraint: Meet Demand ---
    prob += total_mangoes >= mango_count, "MeetMangoDemand"
    
    # --- Constraint 1: Max 2 Categories ---
    # Get unique categories from packs
    categories = list({pack["category"] for pack in mango_packs.values()})
    # Binary variable: 1 if the category is used, 0 otherwise
    cat_use_vars = pulp.LpVariable.dicts("UseCategory", categories, 0, 1, "Integer")
    
    # For each pack: if opened, its category must be marked as used
    for name in pack_names:
        pack_cat = mango_packs[name]["category"]
        prob += lp_pack_vars[name] <= cat_use_vars[pack_cat], f"Pack{name}_UsesCat{pack_cat}"
    
    # Limit total used categories to ≤2
    prob += pulp.lpSum(cat_use_vars.values()) <= 2, "Max2Categories"
    
    # --- Constraint 2: Age Difference ≤25 ---
    # Variables for min and max age of selected packs
    min_age = pulp.LpVariable("MinAge", lowBound=0, cat="Continuous")
    max_age = pulp.LpVariable("MaxAge", lowBound=0, cat="Continuous")
    
    # Big M: A value larger than the maximum possible age difference
    # Here, max age in sample data is 20, so M=30 is safe
    M = 30
    
    # For each pack: if opened, its age must be between min_age and max_age
    for name in pack_names:
        pack_age = mango_packs[name]["age"]
        # If pack is opened (lp_pack_vars[name] =1), pack_age >= min_age
        prob += pack_age >= min_age - M * (1 - lp_pack_vars[name]), f"Pack{name}_AgeLowerBound"
        # If pack is opened, pack_age <= max_age
        prob += pack_age <= max_age + M * (1 - lp_pack_vars[name]), f"Pack{name}_AgeUpperBound"
    
    # Enforce age difference limit
    prob += max_age - min_age <= 25, "MaxAgeDifference"
    
    # Solve the problem (suppress solver logs with msg=False)
    prob.solve(pulp.PULP_CBC_CMD(msg=False))
    
    # Print results
    print("Status:", pulp.LpStatus[prob.status])
    selected_packs = []
    total_selected = 0
    total_mango = 0
    for v in prob.variables():
        if v.name.startswith("OpenPack") and int(v.varValue) == 1:
            pack_name = v.name.replace("OpenPack_", "")
            selected_packs.append(pack_name)
            total_selected +=1
            total_mango += mango_packs[pack_name]["count"]
    
    print(f"Selected packs: {selected_packs}")
    print(f"Total mangoes: {total_mango} (Demand: {mango_count}, Leftover: {total_mango - mango_count})")
    print(f"Number of packs opened: {total_selected}")
    # Verify constraints are satisfied
    selected_cats = {mango_packs[name]["category"] for name in selected_packs}
    selected_ages = [mango_packs[name]["age"] for name in selected_packs]
    print(f"Used categories: {selected_cats} (Count: {len(selected_cats)})")
    print(f"Age range: {min(selected_ages)} to {max(selected_ages)} (Difference: {max(selected_ages)-min(selected_ages)})")

# Test with your sample data
mango_packs = {
    "pack_1": {"count": 5, "category": "pack", "age": 10},
    "pack_2": {"count": 9, "category": "pack", "age": 10},
    "bag_2": {"count": 5, "category": "bag", "age": 20},
    "sack_1": {"count": 5, "category": "sack", "age": 5},
}
optimise(mango_packs, 15)

Key Explanations

  • Objective Function: We kept your priority logic (minimize leftovers first, then pack count) by weighting the total mangoes with a large factor relative to the number of packs.
  • Category Constraint: Binary cat_use_vars track active categories, and we link each pack to its category with a constraint that ensures opening a pack marks its category as used.
  • Age Constraint: The "big M" method lets us ignore age bounds for unopened packs—when a pack isn't selected, the M*(1 - lp_pack_vars[name]) term makes the constraint trivially true.
  • Result Validation: We added checks to confirm the solution meets both new constraints.

Note for Your mango_packs_cat Structure

If you want to use your nested category structure instead of the flat mango_packs, flatten it first to match the function's input format:

# Flatten mango_packs_cat to mango_packs format
flattened_packs = {}
for cat_name, cat_data in mango_packs_cat.items():
    for pack_name, pack_data in cat_data["packets"].items():
        flattened_packs[pack_name] = {
            "count": pack_data["count"],
            "category": cat_name,
            "age": cat_data["age"]
        }
# Run optimization with flattened data
optimise(flattened_packs, 15)

内容的提问来源于stack exchange,提问作者Codeformer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:43:16