簡體   English   中英

SWI Prolog CLP(FD)計划

[英]SWI Prolog CLP(FD) scheduling

我正在使用CLPFD庫解決SWI Prolog中的計划任務。 由於這是我第一次解決比情感問題更嚴重的事情,因此我可能需要經驗豐富的用戶提供一些好的建議。 讓我簡要地描述領域/任務。

我有一個月歷。 每天一整天有2個,整夜有2個(長期12小時服務)。 另外,只有周一至周五10個工人8小時(短期服務)。

域約束顯然是:

  1. 沒有連續的服務(晚上無夜服務,晚上無夜服務)
  2. 上班族可以連續最多連續2個夜間服務
  3. 每個工人一個月的工作時間有限
  4. 有19位工人

我的方法如下:

變量

對於日歷中的每個字段,我都有一個定義的變量:

  • DxD_y ,其中x是一天的日期, y是1或2(對於長日服務)
  • DxN_y ,其中x是一天中的天數, y是1或2(對於長夜服務)
  • DxA_y ,其中x是日數, y是0 .. 9對於短日服務
  • SUM_x ,其中x是表示工作時間的總和的工作人員編號(1..19)

每個D變量都有一個域1..19 為簡化起見,每個X SUM_X #=< 200

約束

  • 同一天每個變量的all_distinct() -每個工作人員每天只能提供一項服務
  • global_cardinality()來計算的出現次數為短服務和長期服務列表中的每個數字1..19 -這個定義變量LSUM_XSSUM_X -工人的出現次數XL翁或S園藝服務
  • SUM_X #= 12*LSUM_X + 8*SSUM_X每個工作人員SUM_X #= 12*LSUM_X + 8*SSUM_X
  • DxN_y #\\= Dx+1D_z避免在一夜之后進行全天服務
    • 一堆類似的約束,例如上面的約束,涵蓋了所有域約束
  • DxNy #= Dx+1Ny #==> DxNy #\\= Dx+2Ny為避免連續三個夜間服務, xy每種組合都有約束

筆記

所有變量和約束條件都直接在pl腳本中說明。 我不使用prolog謂詞來生成約束-因為我在.NET應用程序(前端)中有一個模型,並且可以輕松地將所有內容從.NET代碼生成為prolog代碼。

我認為我的方法總體上是好的。 在一些較小的示例上運行調度程序效果很好(7天,4個長期服務,1個短期服務,8個工人)。 同樣,在完整的案例中,我也能得到一些有效的結果-30天,19名工人,每天4個長期服務和10個短期服務。

但是,我對目前的狀態並不完全滿意。 讓我解釋一下原因。

問題

  1. 我閱讀了一些有關對調度問題進行建模的文章,其中一些文章使用了一些不同的方法-對於變量(日歷字段)和工作者的每種組合,僅介紹布爾變量,以在將工作人員分配給特定日歷字段時進行標記。 這是更好的方法嗎?
  2. 如果計算日歷中的總工作量限制和總時數,您會發現工人並非100%被利用。 但是求解器很可能以這種方式創建解決方案: utilize the first worker for 100% and then grab the next one 因此,解決方案中的SUM看起來像[200,200,200...200,160,140,80,50,0,] 如果工人或多或少地得到同等的利用,我將感到高興。 是否有一些簡單/有效的方法來實現這一目標? 我曾考慮過定義一些定義,例如定義工作人員之間的差異並將其最小化,但這對我來說聽起來很復雜,而且我擔心我會花很多時間來計算出來。 我使用labeling([random_variable(29)], Vars) ,但它僅對變量進行重新排序,因此仍然存在這些差異,只是順序不同。 可能我想labeling過程將以不同於updown (以某種偽隨機方式)的其他順序來獲取值。
  3. 我應該如何訂購約束? 我認為約束的順序與標簽的效率有關。
  4. 如何調試/優化標簽性能? 我希望解決這種類型的任務將花費幾秒鍾或最多幾分鍾的時間,以防求和條件非常嚴格。 例如,使用bisect選項標記需要很長時間。

如果需要,我可以提供更多代碼示例。

有很多問題,讓我嘗試解決一些問題。

...僅針對我的變量(日歷字段)和worker的每種組合引入布爾變量,以標記是否將worker分配給特定的日歷字段。 這是更好的方法嗎?

通常,當使用MILP(混合整數線性規划)求解器時,必須將較高級別的概念(如alldifferent等)表示為線性不等式,才能完成此操作。 這樣的公式通常需要大量的布爾變量。 約束編程在這里更加靈活,並且提供了更多的建模選擇,但是不幸的是,沒有簡單的答案,這取決於問題。 您對變量的選擇會影響表達問題約束的難度以及解決效率。

因此,解決方案中的SUM看起來像[200,200,200 ... 200,160,140,​​80,50,0,]。 如果工人或多或少地得到同等的利用,我將感到高興。 是否有一些簡單/有效的方法來實現這一目標?

您已經提到了最小化差異的想法,這就是通常要實現這種平衡要求的方式。 它不需要復雜。 如果最初我們有這個不平衡的第一個解決方案:

?- length(Xs,5), Xs#::0..9, sum(Xs)#=20, labeling(Xs).
Xs = [0, 0, 2, 9, 9]

那么只需最小化列表元素的最大值,便已經為您(結合總和約束)提供了一個平衡的解決方案:

?- length(Xs,5), Xs#::0..9, sum(Xs)#=20, Cost#=max(Xs), minimize(labeling(Xs),Cost).
Xs = [4, 4, 4, 4, 4]
Cost = 4

您還可以最小化最大值和最小值之間的差異:

?- length(Xs,5), Xs#::0..9, sum(Xs)#=20, Cost#=max(Xs)-min(Xs), minimize(labeling(Xs),Cost).
Xs = [4, 4, 4, 4, 4]
Cost = 0

甚至平方和 [對不起,我的示例是針對ECLiPSe的,而不是SWI / clpfd的,但應顯示出大致的思路。]

我應該如何訂購約束? 我認為約束的順序與標簽的效率有關。

您不必為此擔心。 盡管它可能會產生一些影響,但是它太不可預測了,並且在很大程度上取決於實現細節,因此無法提出任何一般性建議。 這確實是求解程序實現者的工作。

如何調試/優化標簽性能?

對於實際問題,您通常需要(a)針對特定問題的標簽啟發式方法,以及(b)各種不完整的搜索。 搜索樹或搜索進度的可視化可以幫助定制啟發式方法。 您可以在本在線課程的第6章中找到有關這些問題的一些討論。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM