英文:
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] < 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"Combination {idx}:")
for center, products in combination:
for product, quantity in products.items():
print(f"Move {quantity} units of product {product} from Center {center}")
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("CenterSatisfactionProblem", 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"Center_{center}", cat="Binary") for center in product_quantities}
product_vars = {(center, product): pulp.LpVariable(f"Center_{center}_Product_{product}", lowBound=0, cat="Integer")
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'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] <= 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 > 0:
print(f"Move {quantity} units of product {product} from center {center}")
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] <= 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论