Skip to content

示例 16:跨月生产与库存 ​

问题与数据 ​

四个时期为三月、四月、五月和六月,其生产能力与需求量如下:

月份三月四月五月六月
生产能力50180280270
需求量100200180300

源码参数为单位生产成本 Cp=40、延迟交付成本 Cd=2 和库存成本 Cs=0.5。总生产能力和总需求都为 780,但源码约束本身仍是不等式。

集合与参数 ​

令 M 为有序时期集合。Produce 上的 productivity 和 demand 分别是 Productivityi 与 Demandi;i<j 表示时期 i 早于时期 j。

决策变量 ​

对所有 (i,j)∈M×M,xij∈Z≥0 表示在时期 i 生产、用于满足时期 j 需求的数量。源码使用 UIntVariable2,没有固定非对角方向的变量。

中间值 ​

Producei=∑j∈Mxij,Supplyi=∑j∈Mxji.

当前成本符号严格为:

Costd=Cd∑i<j(j−i)2xji,Costs=Cs∑i<j(j−i)xij,Costp=Cp∑i∈Mxii.

因此,晚生产后补早期需求使用正的平方延迟成本,早生产并向后存储使用正的线性库存成本,只有对角线生产量计入生产成本。最后一点是当前实现事实,并不是通常的“所有生产量都计生产成本”。

目标 ​

minCostd+Costs+Costp.

约束 ​

Supplyi≥Demandi(∀i∈M),Producei≤Productivityi(∀i∈M).

旧页面的 (i−j)xji 符号错误;其 ∑iProducei 生产成本也遗漏了 Cp 和源码只计对角线的限制。

实现说明与预期结果 ​

Demo16 使用 UIntVariable2、LinearIntermediateSymbols1<Flt64>、LinearExpressionSymbol 和当前 LinearMetaModel<Flt64>。应按此目标重新生成数值分配;旧表格不是当前“仅对角线生产计费”实现经过验证的最优解。结果至少应是非负整数矩阵,列和满足需求、行和不超过生产能力。

当前 Kotlin 最小示例 ​

kotlin
import fuookami.ospf.kotlin.multiarray.*
import fuookami.ospf.kotlin.math.*
import fuookami.ospf.kotlin.math.algebra.number.*
import fuookami.ospf.kotlin.math.symbol.operation.*
import fuookami.ospf.kotlin.core.model.intermediate.*
import fuookami.ospf.kotlin.core.model.mechanism.*
import fuookami.ospf.kotlin.core.solver.scip.*
import fuookami.ospf.kotlin.core.symbol.*
import fuookami.ospf.kotlin.core.variable.*
import fuookami.ospf.kotlin.example.solveLinearMetaModel

val model = LinearMetaModel<Flt64>("demo16", converter = flt64Converter)
val x = UIntVariable2("x", Shape2(produces.size, produces.size))
val produce = LinearIntermediateSymbols1<Flt64>("produce", Shape1(produces.size)) { i, _ ->
    LinearExpressionSymbol(sum(x[produces[i], _a]), name = "produce_${produces[i].month}")
}
val supply = LinearIntermediateSymbols1<Flt64>("supply", Shape1(produces.size)) { i, _ ->
    LinearExpressionSymbol(sum(x[_a, produces[i]]), name = "supply_${produces[i].month}")
}
val delay = LinearExpressionSymbol(
    sum(produces.withIndex().flatMap { (i, _) -> produces.withIndex().mapNotNull { (j, _) ->
        if (i < j) Flt64(j - i).sqr() * delayDeliveryPrice * x[produces[j], produces[i]] else null
    } }),
    name = "delay_delivery_cost"
)
val storage = LinearExpressionSymbol(
    sum(produces.withIndex().flatMap { (i, _) -> produces.withIndex().mapNotNull { (j, _) ->
        if (i < j) Flt64(j - i) * stowagePrice * x[produces[i], produces[j]] else null
    } }),
    name = "storage_cost"
)
val production = LinearExpressionSymbol(productPrice * sum(x[_a, _a]), name = "produce_cost")
model.add(x)
model.add(produce)
model.add(supply)
model.add(delay)
model.add(storage)
model.add(production)
model.minimize(delay + storage + production, "cost")
for (p in produces) {
    model.addConstraint(supply[p] geq p.demand)
    model.addConstraint(produce[p] leq p.productivity)
}

suspend fun solve() = solveLinearMetaModel(ScipLinearSolver(), model)

源码与验证 ​

Kotlin/Rust 对照 ​

两端 API 独立且目标不完全相同:Rust 对所有 x_ij 收取生产成本,而 Kotlin 当前仅对角线生产量收取生产成本。

kotlin
// See the linked Kotlin implementation for the complete model.
`` 

```rust [Rust]
// See the linked Rust implementation for the equivalent model.
``