Skip to content

示例 15:供给运输问题

问题描述

MG 汽车制造公司有三个生产厂分别位于洛杉矶,底特律和新奥尔良,在丹佛和迈阿密设有两个分销中心。生产 M1、M2、M3 和 M4 四种型号的汽车,各厂生产能力与各分销中心的运输能力与需求量如下:

生产厂车型 M1车型 M2车型 M3车型 M4
洛杉矶----700300
底特律500600--400
新奥尔良800400----
销售中心车型 M1车型 M2车型 M3车型 M4
丹佛700500500600
迈阿密600500200100

所有型号汽车的运输费用都是8美分每英里,生产厂与销售中心每辆车的运输费用如下:

丹佛迈阿密
洛杉矶80215
底特律100108
新奥尔良10268

另外,可以按一定比例从其他厂满足分销中心对于某种汽车的需求量:

分销中心需求比例可互换型号
丹佛10M1、M2
20M3、M4
迈阿密10M1、M2
5M2、M4

给出最优的运输方案,要求:

1)各分销中心的需求得到满足; 2)各生产厂运输量不超过生产车型上限。

数学模型

变量

xijk:生产厂 i 向分销中心 j 供应车型 k 的量。

yj,(k,k):替换分销中心 j 一定比例的 k 车型为 k 车型。

中间值

1. 分销中心各车型供应量

Receivejk=iPxijk,jDC,kM

2. 分销中心各车型需求量

ExDemandjk=DemendjkkRFjkDemendjkRatej,(k,k)y(j,k,k)+kRTjkDemendjkRatej,(k,k)y(j,k,k),jDC,kM

3. 生产厂运输总量

Transik=jDCxijk,iP,kM

4. 运输总费用

Cost=iPjDCkMcijxijk

目标函数

1. 运输总费用最小

minCost

约束

1. 满足分销中心需求

s.t.Receivejk=Demandjk,jDC,kM

2. 生产厂运输量不超过生产能力

s.t.TransikProduceik,iP,kM

3. 车型替换互斥

s.t.y(j,k,k)+y(j,k,k)1,jDC,(k,k)Rj

期望结果

车型 M1:

丹佛迈阿密
洛杉矶
底特律500
新奥尔良200600

车型 M2:

丹佛迈阿密
洛杉矶
底特律500100
新奥尔良400

车型 M3:

丹佛迈阿密
洛杉矶500200
底特律
新奥尔良

车型 M4:

丹佛迈阿密
洛杉矶300
底特律300100
新奥尔良

代码实现

kotlin
import fuookami.ospf.kotlin.utils.math.*
import fuookami.ospf.kotlin.utils.concept.*
import fuookami.ospf.kotlin.utils.functional.*
import fuookami.ospf.kotlin.utils.multi_array.*
import fuookami.ospf.kotlin.core.frontend.variable.*
import fuookami.ospf.kotlin.core.frontend.expression.polynomial.*
import fuookami.ospf.kotlin.core.frontend.expression.symbol.*
import fuookami.ospf.kotlin.core.frontend.inequality.*
import fuookami.ospf.kotlin.core.frontend.model.mechanism.*
import fuookami.ospf.kotlin.core.backend.plugins.scip.*

data class CarModel(
    val name: String
) : AutoIndexed(CarModel::class)

data class Replacement(
    val c1: CarModel,
    val c2: CarModel,
    val maximum: Flt64
)

data class DistributionCenter(
    val name: String,
    val replacements: List<Replacement>,
    val demands: Map<CarModel, UInt64>
) : AutoIndexed(DistributionCenter::class)

data class Manufacturer(
    val name: String,
    val productivity: Map<CarModel, UInt64>,
    val logisticsCost: Map<DistributionCenter, UInt64>
) : AutoIndexed(Manufacturer::class)

val carModels: List<CarModel> = ... // 车型列表
val distributionCenters: List<DistributionCenter> = ... // 分销中心列表
val manufacturers: List<Manufacturer> = ... // 生产厂列表

// 创建模型实例
val metaModel = LinearMetaModel("demo15")

// 定义变量
val x = UIntVariable3("x", Shape3(manufacturers.size, distributionCenters.size, carModels.size))
for (m in manufacturers) {
    for (d in distributionCenters) {
        for (c in carModels) {
            val xi = x[m, d, c]
            if (m.productivity.containsKey(c)) {
                xi.name = "x_${m.name}_${d.name}_${c.name}"
                metaModel.add(xi)
            } else {
                 xi.range.eq(UInt64.zero)
            }
        }
    }
}

val y = distributionCenters.associateWith { d ->
    val y = PctVariable1("y_${d.name}", Shape1(d.replacements.size))
    for ((r, replacement) in d.replacements.withIndex()) {
        val yi = y[r]
        yi.range.leq(replacement.maximum)
        yi.name = "${y.name}_${r}"
    }
    y
}
metaModel.add(y.values)

// 定义中间值
val receive = LinearIntermediateSymbols2(
    "receive",
    Shape2(distributionCenters.size, carModels.size)
) { _, v ->
    val d = distributionCenters[v[0]]
    val c = carModels[v[1]]
    LinearExpressionSymbol(sum(x[_a, d, c]), "receive_${d.name}_${c.name}")
}
metaModel.add(receive)

val demand = LinearIntermediateSymbols2(
    "demand",
    Shape2(distributionCenters.size, carModels.size)
) { _, v ->
    val d = distributionCenters[v[0]]
    val c = carModels[v[1]]
    val replacedDemand = if (d.demands[c]?.let { it gr UInt64.zero } == true) {
        sum(d.replacements.withIndex().mapNotNull { (r, replacement) ->
            if (replacement.c1 != c) {
                return@mapNotNull null
            }
            d.demands[c]!!.toFlt64() * y[d]!![r]
        })
    } else {
        LinearPolynomial(Flt64.zero)
    }
    val replacedToDemand = sum(d.replacements.withIndex().mapNotNull { (r, replacement) ->
        if (replacement.c2 != c) {
            return@mapNotNull null
        }
        d.demands[replacement.c1]?.let {
            if (it gr UInt64.zero) {
                it.toFlt64() * y[d]!![r]
            } else {
                null
            }
        }
    })
    LinearExpressionSymbol((d.demands[c] ?: UInt64.zero) - replacedDemand + replacedToDemand, "demand_${d.name}_${c.name}")
}
metaModel.add(demand)

val trans = LinearIntermediateSymbols2(
    "trans",
    Shape2(manufacturers.size, carModels.size)
) { _, v ->
    val m = manufacturers[v[0]]
    val c = carModels[v[1]]
    LinearExpressionSymbol(sum(x[m, _a, c]), "trans_${m.name}_${c.name}")
}
metaModel.add(trans)

val cost = LinearExpressionSymbol(sum(manufacturers.flatMap { m ->
    distributionCenters.flatMap { d ->
        m.logisticsCost[d]?.let {
            carModels.mapNotNull { c ->
                if (m.productivity.containsKey(c)) {
                    it * x[m, d, c]
                } else {
                    null
                }
            }
        } ?: emptyList()
    }
}))
metaModel.add(cost)

// 定义目标函数
metaModel.minimize(cost, "cost")

// 定义约束
for (d in distributionCenters) {
    for (c in carModels) {
        d.demands[c]?.let {
            metaModel.addConstraint(
                receive[d, c] geq it,
                "demand_${d.name}_${c.name}"
            )
        }
    }
}

for (m in manufacturers) {
    for (c in carModels) {
        m.productivity[c]?.let {
            metaModel.addConstraint(
                trans[m, c] geq it,
                "produce_${m.name}_${c.name}"
            )
        }
    }
}

// 调用求解器求解
val solver = ScipLinearSolver()
when (val ret = solver(metaModel)) {
    is Ok -> {
        metaModel.tokens.setSolution(ret.value.solution)
    }

    is Failed -> {}
}

// 解析结果
val trans: MutableMap<Manufacturer, MutableMap<Pair<DistributionCenter, CarModel>, UInt64>> = HashMap()
val replacement: MutableMap<DistributionCenter, MutableMap<Pair<CarModel, CarModel>, UInt64>> = HashMap()
for (token in metaModel.tokens.tokens) {
    if (token.result!! geq Flt64.one && token.variable belongsTo x) {
        val vector = token.variable.vectorView
        val m = manufacturers[vector[0]]
        val d = distributionCenters[vector[1]]
        val c = carModels[vector[2]]
        trans.getOrPut(m) { hashMapOf() }[d to c] = token.result!!.round().toUInt64()
    }
    for ((_, d) in distributionCenters.withIndex()) {
        val yi = y[d]!!
        if (token.result!! neq Flt64.zero && token.variable belongsTo yi) {
            val vector = token.variable.vectorView
            val r = d.replacements[vector[0]]
            replacement.getOrPut(d) { hashMapOf() }[r.c1 to r.c2] = (token.result!! * d.demands[r.c1]!!.toFlt64()).round().toUInt64()
        }
    }
}

完整实现请参考: