土地利用优化,选项可以使用一种或其他土地类型来满足他们的要求

Land-use optimisation where options can use one OR other land type to satisfy their requirements

我想解决一个简单的土地利用优化问题,以最大化每个潜在土地利用提供的数量。

土地分为类别 1-3(Cat1、Cat2、Cat3)。 有:

土地利用有 2 个选项(op1、op2)

op1 提供 6 个单位,而 op2 提供 8 个单位。

我正在尝试解决这个问题,但答案不符合约束条件。任何人都可以帮助找到我在我的配方中做错了什么,我是 Pulp 的新手,不知道为什么它不起作用。

定义变量和参数:

land_uses = list(["op1","op2"])
provides = dict(zip(land_uses, [6,8]))
usage1 = dict(zip(land_uses, [10,5]))
usage2 = dict(zip(land_uses, [5,10]))  
cat1 = LpVariable.dicts("cat1", land_uses, lowBound = 0, cat = 'Integer')
cat2 = LpVariable.dicts("cat2", land_uses, lowBound = 0, cat = 'Integer')
cat3 = LpVariable.dicts("cat3", land_uses, lowBound = 0, cat = 'Integer')
land_vars = LpVariable.dicts("Land_use", land_uses, lowBound = 0, cat = 'Integer')

定义问题和objective函数:

simple = LpProblem("land_optimisation", LpMaximize)
simple += lpSum([provides[i] * land_vars[i] for i in land_vars])

定义约束:

simple += lpSum([(usage1[i]*land_vars[i]) - (cat1[i] + cat2[i] + cat3[i] ) for i in land_vars])  == 0
simple += lpSum([(usage2[i]*land_vars[i]) - (cat1[i] + cat2[i]) for i in land_vars])  == 0 
simple += lpSum([cat1[i] for i in land_vars]) <= 7
simple += lpSum([cat2[i] for i in land_vars]) <= 15
simple += lpSum([cat3[i] for i in land_vars]) <= 10

求解及结果:

simple.solve()
for v in simple.variables():
print(v.name, "=", v.varValue)

Land_use_op1 = 2
Land_use_op2 = 1
cat1_op1 = 7
cat1_op2 = 0
cat2_op1 = 0
cat2_op2 = 13
cat3_op1 = 0
cat3_op2 = 0

好像是说对于选项 1,例如,它只使用 7 个 Cat3 或以上,而应该使用 20 个(因为选择了 2 个 op1)。我不知道这是否可以在纸浆中完成,但我想不出任何其他表达方式!真的很想 help/pointers,我相信这应该比想象中容易。非常感谢!

欢迎访问本站。

你的公式中有 2 件事导致了你现在的问题....让我们讨论一下,然后进行有效的修复。

  1. 在你的约束中:
simple += lpSum([(usage1[i]*land_vars[i]) - (cat1[i] + cat2[i] + cat3[i] ) for i in land_vars])  == 0

您同时对这两种用途求和,因此您得到的是汇总结果,而不是您想要的结果,即“按每次”使用。您应该将该约束放在 for 循环中,并为每次使用分别构建一个。下一个问题使情况变得更加复杂...

  1. 在您的公式中,您根据 2 种不同的要求对分配进行总计,并且不清楚承诺针对的是哪种要求,因此当您为 (cat 1, 2, 3) 和(cat 1, 2) cat1[i] 可以计入 both 的要求,这显然是不正确的。

因此,您需要一种方法来跟踪土地对 (1) 特定用途的承诺,以及 (2) 根据您的两个要求中的哪一个来避免这种情况。这可以通过双索引变量(use,reqt)来完成。我这样做了,又走了 1 步将不同类别的土地汇总到索引中(这是可选的,但会阻止您为每个命名变量编写单独的约束)。

因此,上面的两点:您需要单独处理“for each”约束,这可以通过循环来完成。由于只需要根据一项要求跟踪一项承诺,您应该将其拆分为一个索引。

示例:

from pulp import *

### DATA
cats = [1, 2, 3]  # categories of land
land_uses = ['op1', 'op2']
reqts = ['regular', 'premium']  # regular = cat 3+, premium = cat 2+
provides = dict(zip(land_uses, [6, 8]))
reqt_use = {    ('op1', 'regular') : 10,
                ('op1', 'premium') : 5,
                ('op2', 'regular') : 5,
                ('op2', 'premium') : 10}
# need some way to indicate which cats are tied to which reqt...
cat_pool = {    'regular' : [1, 2, 3],
                'premium' : [1, 2]}
avail = {   1 : 7,
            2 : 15,
            3 : 10}

### SETUP
# decision variable to commit x amount of "cat" land to "use" against "reqt"
x = LpVariable.dicts("commit", [(cat, use, reqt) 
    for cat in cats 
    for use in land_uses
    for reqt in reqts], 
    lowBound=0, cat='Integer')

# decision variable to count what is built
build = LpVariable.dicts("build", land_uses, lowBound=0, cat='Integer')

### OBJ

simple = LpProblem("land_optimisation", LpMaximize)
# collect value, subtract tiny penalty for committed land prevents extra
# commitments that don't credit a build
simple += lpSum([provides[i] * build[i] for i in land_uses]) - \
            lpSum(0.01*x[cat, use, reqt] for cat in cats for use in land_uses for reqt in reqts)

### CONSTRAINTS

# only use what is available in EACH category, across all uses and reqts
for cat in cats:
    simple += lpSum(x[cat, use, reqt] for use in land_uses for reqt in reqts) <= avail[cat]

# limit the build to what is committed, by individual requirement.
# This is a double "for each" ...uses and reqts
for use in land_uses:
    for reqt in reqts:
        simple += build[use]*reqt_use[use, reqt] <= lpSum(x[cat, use, reqt] for cat in cat_pool[reqt])

simple.solve()
for v in simple.variables():
    print(v.name, "=", v.varValue)

产量

Result - Optimal solution found

Objective value:                15.70000000
Enumerated nodes:               0
Total iterations:               0
Time (CPU seconds):             0.00
Time (Wallclock seconds):       0.00

Option for printingOptions changed from normal to all
Total time (CPU seconds):       0.00   (Wallclock seconds):       0.00

build_op1 = 0.0
build_op2 = 2.0
commit_(1,_'op1',_'premium') = 0.0
commit_(1,_'op1',_'regular') = 0.0
commit_(1,_'op2',_'premium') = 7.0
commit_(1,_'op2',_'regular') = 0.0
commit_(2,_'op1',_'premium') = 0.0
commit_(2,_'op1',_'regular') = 0.0
commit_(2,_'op2',_'premium') = 13.0
commit_(2,_'op2',_'regular') = 0.0
commit_(3,_'op1',_'premium') = 0.0
commit_(3,_'op1',_'regular') = 0.0
commit_(3,_'op2',_'premium') = 0.0
commit_(3,_'op2',_'regular') = 10.0
[Finished in 175ms]