Skip to content

示例 9:选址问题

问题描述

在一个按照东西和南北方向划分成规整街区的城市里,居民点散乱地分布在不同的街区中。用 x 坐标表示东西向,用 y 坐标表示南北向。各居民点的位置可以由坐标 (x,y) 表示。

为建邮局选址,使得居民点到邮局之距离的总和最小。距离使用曼哈顿距离。

居民点 A居民点 B居民点 C居民点 D居民点 E居民点 F
x9km2km3km3km5km4km
y2km1km8km2km9km2km

数学模型

变量

x,y :邮局坐标。

中间值

1. 东西向距离

dxs=Slack(x,xs)=|xxs|,sS

2. 南北向距离

dys=Slack(y,ys)=|yys|,sS

3. 距离

Distances=dxs+dys,sS

目标函数

1. 距离之和最小

minsSDistances

期望结果

邮局应该设置在 (3,1) 处。

代码实现

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.monomial.*
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 Settlement(
    val x: Flt64,
    val y: Flt64
) : AutoIndexed(Settlement::class)

val settlements: List<Settlement> = ... // 居民点列表

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

// 定义变量
val x = IntVar("x")
val y = IntVar("y")
metaModel.add(x)
metaModel.add(y)

// 定义中间值
val dx = LinearIntermediateSymbols1("dx", Shape1(settlements.size)) { i, _ ->
    SlackFunction(
        type = UInteger,
        x = LinearPolynomial(x),
        y = LinearPolynomial(settlements[i].x),
        name = "dx_$i"
    )
}
metaModel.add(dx)

val dy = LinearIntermediateSymbols1("dy", Shape1(settlements.size)) { i, _ ->
    SlackFunction(
        type = UInteger,
        x = LinearPolynomial(y),
        y = LinearPolynomial(settlements[i].y),
        name = "dy_$i"
    )
}
metaModel.add(dy)

val distance = LinearIntermediateSymbols1("distance", Shape1(settlements.size)) { i, _ ->
    LinearExpressionSymbol(
        dx[i] + dy[i],
        name = "distance_$i"
    )
}
metaModel.add(distance)

// 定义目标函数
metaModel.minimize(sum(distance[_a]))

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

    is Failed -> {}
}

// 解析结果
val position = ArrayList<Flt64>()
for (token in metaModel.tokens.tokens) {
    if (token.variable.belongsTo(x)) {
        position.add(token.result!!)
    }
}
for (token in metaModel.tokens.tokens) {
    if (token.variable.belongsTo(y)) {
        position.add(token.result!!)
    }
}

完整实现请参考: