组合优化在Python中

huangapple go评论66阅读模式
英文:

Combination Optimization in Python

问题

我需要从仓库中收集产品以进行捆绑,格式如下:{产品:数量}
center_a_products = {1: 8, 2: 4, 3: 12}

每个仓库有不同种类和数量的产品,格式如下:{仓库:{产品:数量}}
product_quantities = {1: {1: 10, 2: 3, 3: 15}, 2: {1: 5, 2: 8, 3: 10}, 3: {1: 12, 2: 6}}

我每次只能访问一个仓库,但可以从一个仓库收集多个产品。如果某种产品的总数量小于center_a_products,我必须访问包含该产品的每个仓库并收集该产品的总数量。有很多组合符合这些条件,但我需要知道访问最少数量仓库的最有效组合。

我尝试了这个代码,但失败了...

from itertools import combinations

# 定义仓库
centers = [1, 2, 3]

# 定义中心A所需的产品和数量
center_a_products = {
    1: 8,
    2: 4,
    3: 12
}

# 定义每个仓库的产品和数量
product_quantities = {
    1: {1: 10, 2: 3, 3: 15},
    2: {1: 5, 2: 8, 3: 10},
    3: {1: 12, 2: 6}
}

# 检查产品组合是否符合要求的函数
def meets_requirements(combination):
    product_counts = {product: 0 for product in center_a_products.keys()}
    for center, products in combination:
        for product, quantity in products.items():
            product_counts[product] += quantity
    for product, required_quantity in center_a_products.items():
        if product_counts[product] < required_quantity:
            return False
    return True

# 查找所有产品组合
combinations_to_move = []
for r in range(1, len(centers) + 1):
    for combination in combinations(zip(centers, [product_quantities[c] for c in centers]), r):
        if meets_requirements(combination):
            combinations_to_move.append(combination)

# 打印组合
for idx, combination in enumerate(combinations_to_move, start=1):
    print(f"Combination {idx}:")
    for center, products in combination:
        for product, quantity in products.items():
            print(f"Move {quantity} units of product {product} from Center {center}")

我期望的输出类似于这样:

Move 8 units of product 1 from Center 1
Move 3 units of product 2 from Center 1
Move 12 units of product 3 from Center 1
Move 1 units of product 3 from Center 2
英文:

I need to gather products from warehouse centers for product bundling
like this {product:quantity}
center_a_products = {1: 8, 2: 4, 3: 12}

Every centers has different kinds and quantities of products
like this {center:{product:quantity}} product_quantities = {1: {1: 10, 2: 3, 3: 15}, 2: {1: 5, 2: 8, 3: 10}, 3: {1: 12, 2: 6}}

I can only visit one center at a time but I can collect multiple product from one center.
If total quantity of some product is less than center_a_products, I must visit every center containing that product and collect total quantity of that product.
There are many combination meets these conditions but I need to know most efficient combination that visits the smallest number of centers

I tried this but it failed...

from itertools import combinations

# Define the centers
centers = [1, 2, 3]

# Define the products and quantities needed in Center A
center_a_products = {
    1: 8,
    2: 4,
    3: 12
}

# Define the products and quantities for each center
product_quantities = {
    1: {1: 10, 2: 3, 3: 15},
    2: {1: 5, 2: 8, 3: 10},
    3: {1: 12, 2: 6}
}

# Function to check if a product combination meets the requirements
def meets_requirements(combination):
    product_counts = {product: 0 for product in center_a_products.keys()}
    for center, products in combination:
        for product, quantity in products.items():
            product_counts[product] += quantity
    for product, required_quantity in center_a_products.items():
        if product_counts[product] &lt; required_quantity:
            return False
    return True

# Find all combinations of products
combinations_to_move = []
for r in range(1, len(centers) + 1):
    for combination in combinations(zip(centers, [product_quantities[c] for c in centers]), r):
        if meets_requirements(combination):
            combinations_to_move.append(combination)

# Print the combinations
for idx, combination in enumerate(combinations_to_move, start=1):
    print(f&quot;Combination {idx}:&quot;)
    for center, products in combination:
        for product, quantity in products.items():
            print(f&quot;Move {quantity} units of product {product} from Center {center}&quot;)

I expected something like this

Move 8 units of product 1 from Center 1
Move 3 units of product 2 from Center 1
Move 12 units of product 3 from Center 1
Move 1 units of product 3 from Center 2

答案1

得分: 4

问题描述适合使用[Mixed-Integer-Linear Program (MIP)]解决。

解决混合整数问题的便捷库是PuLP,它附带了内置的Coin-OR suite和特定的整数求解器CBC。

我们制定了描述问题的模型:

import pulp

# 省略部分代码...

# 满足所有需求
for product in center_a_products:
    problem += pulp.lpSum(product_vars[center, product] for center in product_quantities) == center_a_products[product]

# 我们只能从一个中心获取物品,如果我们访问它
# 还有:不能从一个中心拿取比库存更多的物品
for center in product_quantities:
    for product in product_quantities[center]:
        problem += product_vars[center, product] <= center_vars[center] * product_quantities[center][product]

# 最小化访问的中心数量
problem += pulp.lpSum(center_vars.values())
problem.solve()

打印解决方案:

for center in product_quantities:
    if center_vars[center].varValue == 1:
        for product in product_quantities[center]:
            quantity = product_vars[center, product].varValue
            if quantity > 0:
                print(f"Move {quantity} units of product {product} from center {center}")

输出:

Move 8.0 units of product 1 from center 3
Move 4.0 units of product 2 from center 3
Move 12.0 units of product 3 from center 3

目前您提供的示例非常简单。可以轻松添加附加约束,但您需要提供问题和预期输出的更好描述。

将问题描述转换为线性程序可能需要一些实践。这个程序中最棘手的一行是:

problem += product_vars[center, product] <= center_vars[center] * product_quantities[center][product]

目标是最小化 center_vars,但满意度约束会强制其中一些为1。

要获得描述问题的思路,而不是考虑解决方案,我建议观看Raymond Hettinger在PyCon 2019上的精彩演讲 - Modern solvers: Problems well-defined are problems solved

安装 PuLP

pip install pulp
英文:

The problem description lends itself to solving with a Mixed-Integer-Linear Program (MIP).

A convenient library for solving mixed integer problems is PuLP that ships with the built-in Coin-OR suite and in particular the integer solver CBC.

We formulate a model that describes your problem:

import pulp


center_a_products = {
    1: 8,
    2: 4,
    3: 12
}

product_quantities = {
    1: {1: 10, 2: 5, 3: 15},
    2: {1: 5, 2: 8, 3: 10},
    3: {1: 12, 2: 6, 3: 20}
}

problem = pulp.LpProblem(&quot;CenterSatisfactionProblem&quot;, pulp.LpMinimize)

# Center vars are binary variables that we minimize in the objective. They indicate whether a center was visited or not
center_vars = {center: pulp.LpVariable(f&quot;Center_{center}&quot;, cat=&quot;Binary&quot;) for center in product_quantities}
product_vars = {(center, product): pulp.LpVariable(f&quot;Center_{center}_Product_{product}&quot;, lowBound=0, cat=&quot;Integer&quot;)
                for center in product_quantities
                for product in product_quantities[center]}


# Satisfy all demand
for product in center_a_products:
    problem += pulp.lpSum(product_vars[center, product] for center in product_quantities) == center_a_products[product]

# We can only take stuff from a center if we visit it 
# Also: can&#39;t take more stuff from a center than is in stock

for center in product_quantities:
    for product in product_quantities[center]:
        problem += product_vars[center, product] &lt;= center_vars[center] * product_quantities[center][product]
# Minimize amount of centers visited
problem += pulp.lpSum(center_vars.values())
problem.solve()

Print solution:

for center in product_quantities:
    if center_vars[center].varValue == 1:
        for product in product_quantities[center]:
            quantity = product_vars[center, product].varValue
            if quantity &gt; 0:
                print(f&quot;Move {quantity} units of product {product} from center {center}&quot;)

Output:

Move 8.0 units of product 1 from center 3
Move 4.0 units of product 2 from center 3
Move 12.0 units of product 3 from center 3

Currently the example you provided is pretty trivial. It's easily possible to add additional constraints, but you need to provide better description of the problem and the expected output.

Translating a problem description into a linear program can take some practice. The trickiest line in this program is

problem += product_vars[center, product] &lt;= center_vars[center] * product_quantities[center][product]

The objective minimizes center_vars, but the satisfaction constraint forces some of them to be 1.

To get the mindset of describing problems, rather than thinking of solutions I recommend Raymond Hettinger's excellent talk on PyCon 2019 - Modern solvers: Problems well-defined are problems solved

to install PuLP

pip install pulp

huangapple
  • 本文由 发表于 2023年7月3日 19:18:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76604223.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定