OptaPlanner解決線性規劃問題

這是我參與更文挑戰的第18天,活動詳情查看: 更文挑戰java

內容概要

上一篇章咱們學習瞭如何使用OptaPlanner來求解最值的問題,今天咱們繼續經過一個例子來學習如何求解線性規劃問題,話很少說直接開始。api

問題描述

今天這個問題是從韓伯棠教授的《管理運籌學》書中的一個例題,咱們來嘗試使用OptaPlanner來解決這個線性規劃維問題。markdown

人力資源分配問題

例1 某晝夜服務的公交線路天天各時間段內所需司機和乘務人員人數如表 4-1 所示:dom

bustable.png

設:司機和乘務人員分別在各時間段開始時上班,並連續工做 8 h,問該公交線路應怎樣安排司機和乘務 人員,既能知足工做須要,又使配備司機和乘務人員的人數最少?ide

線性規劃求解

咱們來看下書中線性規劃的求解答案,線性規劃的求解重要三步驟,建模型,建約束,極值函數。函數

解:post

解 設x,表示第i班次時開始上班的司機和乘務人員人數,這樣能夠知道在第i班次工做的人數應包括第i-1班次時開始上班的人數和第i班次時開始上班的人數,若有x1+x2≥70.又要求這六個班次時開始上班的全部人員最少,即要求x1+x2+x3+x4+x5+x6。最小,這樣創建以下的數學模型∶學習

約束條件

min z = x1 + x2 + x3 + x4 + x5 + x5ui

x1+x6≥60,spa

x1+x2≥70,

x2+x3≥60,

x3+x4≥50,

x4+x5≥20,

x5+x6≥30,

x1,x2,x3,x4,x5,x6≥0

用"管理運籌學"軟件能夠求得此問題的最優解∶x1=50,x2=20,x3=50,x4=0,x5=20,x6=10,一共須要司機和乘務人員 150 人.

OptaPlanner求解

OptaPlanner的三步驟就是,建模型、寫約束評分。

buspr.png

模型如上圖所示。

模型創建

BusDriverSolution.java

@PlanningSolution
public class BusDriverSolution extends AbstractPersistable {

    @PlanningEntityCollectionProperty
    private List<BusShift> busShifts;

    @ProblemFactCollectionProperty
    private List<HourShift> hourShifts;

    @ProblemFactCollectionProperty
    private List<HourPeoplePattern> hourPeoplePatterns;

    @PlanningScore
    private HardMediumSoftScore score;

    @ValueRangeProvider(id = "peopleNumRange")
    public CountableValueRange<Integer> getPeopleNumRange() {
        // Range: 0, 1, 2, 3, ..., 69, 70
        return ValueRangeFactory.createIntValueRange(0, 100, 1);
    }
    ......
}
複製代碼

BusShift.java

@PlanningEntity(difficultyComparatorClass = BusShiftStrengthComparator.class)
public class BusShift extends AbstractPersistable implements Comparable<BusShift> {

    /** * assignment people */
    @PlanningVariable(valueRangeProviderRefs = "peopleNumRange")
    private Integer assignmentPeople;

    private HourShift hourShift;
}
複製代碼

HourShift.java

public class HourShift extends AbstractPersistable {

    /** * need people */
    private Integer needPeople;

    /** * start hour */
    private Integer start;

    /** * end hour */
    private Integer end;
}
複製代碼

HourPeoplePattern.java

public class HourPeoplePattern extends AbstractPersistable {

    private HourShift beforeHourShift;

    private HourShift afterHourShift;

    private Integer minxPeopleNum;
}
複製代碼
HourPeoplePattern

HourPeoplePattern對象屬性爲,beforeHourShiftafterHourShiftminPeopleNum,這個Pattern表明着,每兩個班次的人數最少爲多少人的數據。在後面進行約束匹配時,是很關鍵的。

約束評分

busDriverConstraints.drl

package org.optaplanner.examples.busdriver.solver;
    dialect "java"

import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScoreHolder
import org.optaplanner.examples.bus.domain.BusDriverSolution;
import org.optaplanner.examples.bus.domain.BusShift;
import org.optaplanner.examples.bus.domain.HourShift;
import org.optaplanner.examples.bus.domain.HourPeoplePattern;
import org.optaplanner.examples.bus.StaticKit;
import java.lang.Math;
import org.optaplanner.examples.bus.StaticKit;

global HardMediumSoftScoreHolder scoreHolder;




rule "Meeting the Needs"
    when HourPeoplePattern( $beforeHourShift : beforeHourShift, $afterHourShift : afterHourShift, $minxPeopleNum : minxPeopleNum ) $before : BusShift(hourShift == $beforeHourShift) $after : BusShift(hourShift == $afterHourShift) then scoreHolder.addMediumConstraintMatch(kcontext, -1 * StaticKit.calcMinScore($minxPeopleNum, $before, $after));
end

rule "Meeting the Needs lack"
    when HourPeoplePattern( $beforeHourShift : beforeHourShift, $afterHourShift : afterHourShift, $minxPeopleNum : minxPeopleNum ) $before : BusShift(hourShift == $beforeHourShift) $after : BusShift(hourShift == $afterHourShift, StaticKit.lackMinScore($minxPeopleNum, $before, $after) == true)
    then
        scoreHolder.addHardConstraintMatch(kcontext, -1 * StaticKit.calcMinScore($minxPeopleNum, $before, $after));
end



rule "Min People"
    when accumulate ( BusShift(assignmentPeople != null , $assignmentPeople : assignmentPeople);
            $busTotalSum : sum($assignmentPeople)
        )
    then
        scoreHolder.addSoftConstraintMatch(kcontext, - $busTotalSum.intValue());
end
複製代碼

求解結果

10:21:23.927 [main        ] INFO Local Search phase (1) ended: time spent (4087), best score (0hard/-10medium/-150soft), score calculation speed (25268/sec), step total (185). 10:21:23.927 [main ] INFO Solving ended: time spent (4087), best score (0hard/-10medium/-150soft), score calculation speed (20259/sec), phase total (2), environment mode (REPRODUCIBLE), move thread count (NONE). bus - 1, people : 60 bus - 2, people : 10 bus - 3, people : 50 bus - 4, people : 0 bus - 5, people : 30 bus - 6, people : 0 複製代碼

咱們能夠看到最後的求解結果是:

x1=60,x2=10,x3=50,x4=0,x5=30,x6=0,最少人數爲150人。

咱們再來看下例題的求解結果:

用"管理運籌學"軟件能夠求得此問題的最優解∶x1=50,x2=20,x3=50,x4=0,x5=20,x6=10,一共須要司機和乘務人員 150 人。

能夠看出雖然各個變量不一樣,可是其結果都是爲150人。由於個人變量都是x≥0,因此結果都不一樣的最優解。

當前結果的分數爲best score (0hard/-10medium/-150soft),咱們來嘗試下若是按照例子的結果,最終OptaPlanner的評分是多少呢:

---------------------do move-------------------
-- bus - 1, people : 50
-- bus - 2, people : 20
-- bus - 3, people : 50
-- bus - 4, people : 0
-- bus - 5, people : 20
-- bus - 6, people : 10
0hard/-10medium/-150soft Explanation of score (0hard/-10medium/-150soft): 複製代碼

咱們更改PlanningEntity的值後,從新計算分數,結果跟咱們求解結果分數是同樣的,也就是說這兩種結果都是最優解。

總結

經過這個例子,咱們學習了OptaPlanner如何解決線性規劃問題。

做業

那你們想一想如何更均衡的分配這些人數在班次時段上呢(由於每一個人工做8小時實際上是同樣的,只是爲了促進你們學習),以前的文章裏咱們講到過如何均衡工做量。

結束語

下一篇章咱們來學習一其它的例子。

創做不易,禁止未受權的轉載。若是個人文章對您有幫助,就請點贊/收藏/關注鼓勵支持一下吧💕💕💕💕💕💕

相關文章
相關標籤/搜索