簡體   English   中英

如何對紙漿中的二元變量應用二元約束?

[英]How to apply a binary constraint on a binary variable in pulp?

我正在使用 python 作為編程語言並嘗試在二進制變量上實現二進制約束。 但是約束沒有得到應用,結果也不是 100% 正確的。

在下面的代碼片段中,我想將具有相同長度的項目放在一個容器中。 (這只是大局的一個子問題,我只需要使用紙漿來解決它)。

import pulp
from itertools import product
import pandas as pd

# DataFrame of item, weight, and length
df_updated = pd.DataFrame([['item1', 10, 'A'], ['item2', 15, 'A'], ['item3',15, 'B'], ['item4',15, 'C']], columns = ['itemname', 'weight', 'length'])

# Max bin to use
max_bins = 2

# Max weightage per bin
max_weight = 30

problem = pulp.LpProblem("Grouping_lengths", pulp.LpMinimize)

# Variable to check, if we are using the bin or not
bin_used = pulp.LpVariable.dicts('is_bin_used', range(max_bins), cat='Binary')

# Possible combinations to put the item in the bin
possible_item_in_bin = [(item_index, bin_num) for item_index, bin_num in product(df_updated.index, range(max_bins))]
item_in_bin = pulp.LpVariable.dicts('is_item_in_bin', possible_item_in_bin, cat = 'Binary')

# Only one item in each bin
for item_index in df_updated.index:
    problem += pulp.lpSum([item_in_bin[item_index, bin_index] for bin_index in range(max_bins)]) == 1, f"Ensure that item {item_index} is only in one bin"

# Sum of quantity grouped in each bin must be less than max weight
for bin_index in range(max_bins):
    problem += pulp.lpSum(
            [item_in_bin[item_index, bin_index] * df_updated.loc[item_index, 'weight'] for item_index in df_updated.index]
        ) <= max_weight * bin_used[bin_index], f"Sum of items in bin {bin_index} should not exceed max weight {max_weight}"

# Length Variable
lengths = list(df_updated.length.unique())

# Possible combinations to put the lengths in the bin
possible_length_in_bin = [(length, bin_num) for length, bin_num in product(range(len(lengths)), range(max_bins))]

# Constraint to group similar lengths together on same bin
length_in_bin = pulp.LpVariable.dicts('LengthInBin', possible_length_in_bin, cat = 'Binary')
for item, length, bins_index in product(df_updated.index, range(len(lengths)), range(max_bins)):
    problem += pulp.lpSum(item_in_bin[(item, bins_index)] == length_in_bin[(length, bins_index)]), (f"Only place item {item} in bin {bins_index} if length number {length} is chosen for this bin")

# Objective function to minimize bins used
problem += pulp.lpSum(bin_used[bin_index] for bin_index in range(max_bins)), "Objective: Minimize Bins Used"

problem.solve(pulp.PULP_CBC_CMD(msg = False))

for val in problem.variables():
    if val.varValue == 1:
        print(val.name, val.varValue)

算法的結果:(所有項目都放在同一個 bin 中)

is_item_in_bin_(0,_0) 1.0
is_item_in_bin_(1,_1) 1.0
is_item_in_bin_(2,_1) 1.0
is_item_in_bin_(3,_0) 1.0

算法的預期結果:(相似長度的項目只能組合在一起)

is_item_in_bin_(0,_0) 1.0
is_item_in_bin_(1,_0) 1.0
is_item_in_bin_(2,_1) 1.0
is_item_in_bin_(3,_1) 1.0

因為我有一個長度限制,可以在垃圾箱上對相似長度進行分組。 你能幫忙讓我知道我在這里做錯了什么嗎?

EDIT:我已經更新了代碼以包含每個 bin 的最大大小。 現在我每個垃圾箱的最大容量為 30 個,最大時我只想使用 2 個垃圾箱。 我還添加了將相同長度的項目添加到 bin 的約束。 但是再次違反了約束,即使目標值相同,不同長度的項目也會被添加到同一個 bin 中。

正如我在評論中所寫的,反親和約束(傳播到不同的 bin)是非線性的,但通常可以線性化。 相比之下,親和力約束(對同一個 bin)是線性且簡單的。 這只是賦值變量的相等。
正如 Erwin Kalvelagen 所建議的,最好消除這些約束並減少變量的數量。 但無論如何,如果需要它們:

同一部分

import pulp
from itertools import product
import pandas as pd

# DataFrame of item, weight, and length
df_updated = pd.DataFrame([['item1', 10, 'A'], ['item2', 15, 'A'], ['item3',15, 'B'], ['item4',15, 'C']], columns = ['itemname', 'weight', 'length'])

# Max bin to use
max_bins = 2

# Max weightage per bin
max_weight = 30

problem = pulp.LpProblem("Grouping_lengths", pulp.LpMinimize)

# Variable to check, if we are using the bin or not
bin_used = pulp.LpVariable.dicts('is_bin_used', range(max_bins), cat='Binary')

# Possible combinations to put the item in the bin
possible_item_in_bin = [(item_index, bin_num) for item_index, bin_num in product(df_updated.index, range(max_bins))]
item_in_bin = pulp.LpVariable.dicts('is_item_in_bin', possible_item_in_bin, cat = 'Binary')

# Only one item in each bin
for item_index in df_updated.index:
    problem += pulp.lpSum([item_in_bin[item_index, bin_index] for bin_index in range(max_bins)]) == 1, f"Ensure that item {item_index} is only in one bin"

# Sum of quantity grouped in each bin must be less than max weight
for bin_index in range(max_bins):
    problem += pulp.lpSum(
            [item_in_bin[item_index, bin_index] * df_updated.loc[item_index, 'weight'] for item_index in df_updated.index]
        ) <= max_weight* bin_used[bin_index], f"Sum of items in bin {bin_index} should not exceed max weight {max_weight}"

不同的部分

 # Length Constraints
lengths = list(df_updated.length.unique())
for length in lengths:
    items_n = df_updated.index[df_updated['length'] == length].tolist()  # get items with given length
    if len(items_n) > 1:  # skip items with unique length
        for bin in range(max_bins - 1):  # for each bin except the last one because the last can be deduced
            for item in range(1, len(items_n)):  # set other item assignment equal to the first one.
                problem += item_in_bin[0, bin] == item_in_bin[item, bin]

同一部分

# Objective function to minimize bins used
problem += pulp.lpSum(bin_used[bin_index] for bin_index in range(max_bins)), "Objective: Minimize Bins Used"

problem.solve(pulp.PULP_CBC_CMD(msg = False))

for val in problem.variables():
    if val.varValue == 1:
        print(val.name, val.varValue)

算法結果:

is_bin_used_0 1.0
is_bin_used_1 1.0
is_item_in_bin_(0,_1) 1.0
is_item_in_bin_(1,_1) 1.0
is_item_in_bin_(2,_0) 1.0
is_item_in_bin_(3,_0) 1.0

相同長度的項目組合在一起。 由於max_weigh t 約束,其他人被放在不同的 bin 中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM