2012-12-19 54 views
3

我將在一個日曆月內隨機分配8天的工作人員。如何使用python在一個月內平均休息幾天?

我想隨機選擇8天,並且休息日的分配應該儘可能均勻。例如,我的意思是,所有的8天休息都不應該在一個月的前8天收集。

例如:[1,5,8,14,18,24,27,30]是一個很好的分佈。 [1,2,3,4,26,27,28,29]不是一個很好的分佈。

其實,船員連續7天無法工作。每7天,必須有1天休息。

所有日子都是平等對待的,即星期天不是自己休息。船員也可以在週末工作。

我想一個一個地選擇休息日。一次不能把它們中的8個放在一起。

你能推薦一個使用python來實現這個的算法嗎?

並非所有月份的所有日子都可以休息。

問候

+0

你是什麼意思的「分佈應儘可能」?你的意思是像週末的相同數量等?或者你的意思是整個月的密度大致相同?或者是其他東西? – Bakuriu

+0

請參閱我更新的問題。 – alwbtc

+2

@alwbtc什麼使一個分配好,另一個不好?你可以定義一些函數或者一些數學啓發式,可以分辨好的和不好的分佈之間的區別嗎? –

回答

3

這是這裏的關鍵:

Actually, a crew can't work 7 consecutive days. In every 7 days, there must be 1 day-off.

換個字眼的問題,說隨機2天每7天(或每月分成的適當時間四種長度)。然後你保證一個平均分配。正如Martijn Pieters所建議的那樣使用random.sample()

您可以使用這種技術從第一週開始生成兩個值,然後按順序產生它們,如果您想要它們一個接一個。

編輯:

正如tcaswell觀察到,仍然有一些情況下,你最終在值班連續十天。爲了解決這個問題,您可以每三天分配一天,創建一個十個列表,並從不會使連續7天標準失效的天數子集中隨機移除兩天。

或者,您可以繼續使用原始算法生成列表,直到它符合條件,因爲無論如何您很可能會得到有效的解決方案。你必須編寫某種類型的驗證函數,但這樣做很容易,因爲你只需要計算最長的連續日期。

CODE:

第二個選項的實現。

import random 
from itertools import chain 
from itertools import count 

def candidate(m): 
    ''' Returns 2 days per week, in m days, where m is the length of the month. ''' 
    weeks = weeksmaker(m) 
    return sorted(list(chain(*[random.sample(week, 2) for week in weeks]))) 

def weeksmaker(m): 
    ''' Divides a month up into four weeks, randomly assigning extra days to weeks. ''' 
    weeks = [range(i, i+7) for i in xrange(1,29,7)] 
    for i in range(m - 28): 
     weeks[random.randint(1, len(weeks))-1].append(i) 
    c = count(1) 
    return [[c.next() for day in week] for week in weeks] 

def valid(days, c): 
    ''' Validity check. Cant work more than c consecutive days. ''' 
    for i in xrange(1, len(days)): 
     if days[i] - days[i-1] > c: 
      return False 
    else: 
     return True 

def daysoff(m, n, c): 
    ''' In month length m, need n days off, cant work more than c consecutive days. ''' 
    while True: 
     days = candidate(n) 
     if valid(days, c): 
      return days 

>>> for i in range(28, 32): 
...  daysoff(i, 8, 7) 
... 
[6, 7, 10, 14, 18, 20, 27, 28] 
[4, 7, 10, 13, 19, 21, 23, 24] 
[2, 4, 9, 13, 15, 20, 25, 27] 
[1, 3, 9, 12, 18, 19, 24, 28] 
+0

不錯。非常好的主意。 – alwbtc

+1

這仍然是'xxooooooooooxx'的獵物,它給你連續10天。 – tacaswell

+0

嗯,你是對的! – alwbtc

8

使用random.sample()擺脫序列的隨機組。列出可用的天,然後傳遞到.sample()功能:

import sample 
daysoff = [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20] 

picked = random.sample(daysoff, 8) 

在我用了一個月的第一天起上面的例子,這樣的例子,忽略了某些日子(比如,週日和最近10天的這個月),然後我們從這個人口中隨機挑選8天。

+0

謝謝,但確保挑選的日子均勻分佈嗎?它可能會選擇[1,2,3,4,17,18,19,20],這是不好的。你知道任何統計函數,可能會使用標準偏差等,這將確保挑選的休假均勻選擇? – alwbtc

+2

@alwbtc:然後定義*明確*均勻分佈的含義?隨機和我認爲你的意思不一定相容。 –

+0

好的,請看我更新的問題 – alwbtc

1

您應該分割總天數。

這段代碼無論需要多少天,也不管總共有幾天。

from random import randint 
def foo(l, n): 
    dist = round(len(l)/n) 
    return [randint(l[i*dist], l[(i+1)*dist-1]) for i in range(n)] 

In [1]: days = [i for i in range(1,31)] 
In [2]: foo(days, 8) 
Out[2]: [1, 4, 6, 9, 13, 16, 20, 27] 

In [3]: mylist = [i for i in range(500)] 
In [4]: foo(mylist, 5) 
Out[4]: [80, 147, 250, 346, 448] 

在四捨五入的過程中會出現一些問題,列表索引可能會超出範圍左右。

0

生成(並存儲)所有有效工作時間表的清單(通過蠻力...只有30C8的方式來做到這一點)。然後您可以安全快速地從該列表中選擇。

import itertools 
import numpy as np 
good_lst = [] 
for days_off in itertools.combinations(range(30),8): 
    if np.max(np.diff((0,) + days_off + (30,))) < 7: 
     good_lst.append(days_off) 

(有可能是在有一些somplace斷接一個錯誤)

這〜5min後跑體面的機器上。 (0,1,2,3,6,12,18,24)是一個有效的工作時間表,但涉及6個工作日中的4個部分,您可能想要進行更多的修剪。

1

這(我想)做什麼@Martijn沒有和具有不包括連續兩天的額外收益(例如,如果你不想8休息日連續):

#Day selector 

import random 

Ndays = 8 
daysoff = range(1,25) 
concurrent_tol = 3 

while True: 
    cntr = 0 
    sample = random.sample(daysoff, Ndays) 
    sample.sort() 
    for i in range(1,Ndays-1): 
     if abs(sample[i]-sample[i-1]) == 1: 
      cntr +=1 
     if abs(sample[i]-sample[i+1]) == 1: 
      cntr +=1 

    if cntr<concurrent_tol: 
     print "Found a good set of off-days :" 
     print sample 
     break 
    else: 
     print "Didn't find a good set, trying again" 
     print sample 

輸出示例:

Didn't find a good set, trying again 
[3, 4, 5, 6, 7, 8, 9, 11] 
Didn't find a good set, trying again 
[1, 5, 6, 7, 12, 14, 19, 20] 
Didn't find a good set, trying again 
[4, 5, 7, 9, 11, 15, 16, 20] 
Didn't find a good set, trying again 
[3, 4, 6, 7, 12, 13, 14, 23] 
Didn't find a good set, trying again 
[1, 7, 10, 12, 15, 16, 17, 22] 
Didn't find a good set, trying again 
[5, 7, 8, 11, 17, 18, 19, 23] 
Didn't find a good set, trying again 
[3, 8, 11, 12, 13, 15, 17, 21] 
Didn't find a good set, trying again 
[2, 5, 7, 8, 9, 12, 13, 21] 
Found a good set of off-days : 
[1, 2, 5, 12, 15, 17, 19, 20] 

這也有看起來醜陋的額外好處。請注意,可能的日期是1-24天,如daysoff中所定義。

相關問題