我正在尝试解决以下问题,我非常肯定有一个解决方案。我无法透露太多信息,因此我将其制定为一般术语并提出以下寓言。我在这里介绍你 沉睡的僧侣问题。
想象一个修道院,12名僧人在那里睡觉,吃饭,祈祷或在外面工作 修道院。每个和尚,每晚需要睡8或6个小时。每 和尚有X金额,他必须在花园,摊位或工作 羊群(例如,修道院外的活动)和Y小时 在这些Y小时内,在修道院的墙壁上闲逛 每个和尚要么徘徊(例如祈祷,吃饭,喝啤酒) 或者他正在睡觉S小时(Y总是大于S)。
这个修道院的负责人问是否有可能找到一个 最好的睡眠安排,使床的数量可以 减少了,每个僧侣都得到了必要的睡眠。
我对该问题的输入数据以下列形式给出:
time_slots_in_monastery = [
# time of the day
#14 16 18 20 22 24 2 4 6 8 10 12
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
]
每行代表一名僧侣。数字1表示修道院内的时间段,0表示僧侣在外面的时间。
另外,我有一个包含每个睡眠要求的向量 僧。
required_sleeping = [4, # number of sleeping slots , each one is 2 hours.
3,
3,
4,
4,
4,
4,
3,
3,
3,
3,
3]
当前,并且真正不满意的睡眠解决方案是:
solution = [
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
]
# total beds:
# 0, 0, 12, 12, 12, 5, 0, 0 , 0, 0, 0, 0
所有僧侣都在下午6点睡觉。但是如果我们让僧侣11和12使用相同的床,我们可以将床的数量减少到11个。更好的是,僧侣1和2也可以共用床,然后我们将床的数量减少到10个。
better_solution = [
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
]
现在有12名僧侣,问题可能是暴力迫使,但实际上我有大约50名僧侣,时间分辨率是5分钟,而不是2小时。因此,我正在寻找一种方法来找到一种方法来解决f-最小搜索风格或任何其他非暴力的问题。
我在这里介绍我在Python中的暴力解决方案:
import pprint
time_slots_in_monastery = [
# time of the day
#14 16 18 20 22 24 2 4 6 8 10 12
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
]
required_sleeping = [4,
3,
3,
4,
4,
4,
4,
3,
3,
3,
3,
3]
def make_new_bed(rl):
bed = '0' * rl
return bed
def search_in_beds(beds, rl, sleep_time, first, last):
sleep_slots = '0' * sleep_time
sleep = '1' * sleep_time
index = False
for cn, bed in enumerate(beds):
try:
index = bed[first:last+1].index(sleep_slots)
index += first
bed = bed[:first] + bed[first:last+1].replace(sleep_slots, sleep, 1) + bed[last+1:]
beds[cn] = bed
print()
print('monastery time from: %s to: %s' % (first, last))
print('this monk found a place in bed(%s) from (%s) to (%s)' % (cn, index, index + sleep_time-1))
print('bed(%s) time: %s ' % (cn, bed))
except:
"""
I did not found a free time in this bed
"""
pass
if index:
return index, index + sleep_time - 1
#adding a bed and searching again
beds.append(make_new_bed(rl))
return search_in_beds(beds, rl, sleep_time, first, last)
def monks_beds(t, rsleep, rl=12):
beds = []
output = []
for cn, i in enumerate(t):
sleep_time = rsleep[cn]
first = i.index(1)
last = len(i) - 1 - i[::-1].index(1)
first, last = search_in_beds(beds, rl, sleep_time, first, last)
out = rl * '0'
out = out[:first] + sleep_time * '1' + out[last:]
output.append([int(x) for x in out])
return beds
print('time_slots_in_monastery:')
pprint.pprint(time_slots_in_monastery)
print('required_sleeping')
pprint.pprint(required_sleeping)
pprint.pprint(monks_beds(time_slots_in_monastery, required_sleeping))
答案 0 :(得分:1)
我能想到的一个解决方案没有考虑到“在修道院的时间”总是确保两个相邻僧侣的睡眠时间永远不会重叠,模仿我们一天的长度:
monks = numpy.array([
(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0),
(1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # Pattern wraps around at midnight
...
])
我相信(但现在无法正式证明)此解决方案是最佳的,如:
所以我们需要解决的是如何以编程方式转换两个相邻时间不重叠的和尚睡眠时间:
import numpy
monks = numpy.array([
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
])
# Hours of sleep required is sum along columns
hours = numpy.sum(monks, axis=1)
# Reset all sleeping times to first column
monks = numpy.sort(monks, axis=1)[:, ::-1]
# Generate sleeping pattern without overlap of two neighboring monks. When
# one monk rises, the next one goes to bed.
hours = numpy.cumsum(hours)
# Insert 0 shift for first monk
hours = numpy.insert(hours, 0, 0)
for s, i in zip(hours, range(monks.shape[0])):
monks[i, :] = numpy.roll(monks[i, :], s)
beds = numpy.max(numpy.sum(monks, axis=0))
print monks
# [[1 1 1 1 0 0 0 0 0 0 0 0]
# [0 0 0 0 1 1 1 0 0 0 0 0]
# [0 0 0 0 0 0 0 1 1 1 0 0]
# [1 0 0 0 0 0 0 0 0 0 1 1]
# [0 1 1 1 1 0 0 0 0 0 0 0]
# [0 0 0 0 0 1 1 1 1 0 0 0]
# [1 0 0 0 0 0 0 0 0 1 1 1]
# [0 1 1 1 1 0 0 0 0 0 0 0]
# [0 0 0 0 0 1 1 1 0 0 0 0]
# [0 0 0 0 0 0 0 0 1 1 1 0]
# [1 1 0 0 0 0 0 0 0 0 0 1]
# [0 0 1 1 1 0 0 0 0 0 0 0]
# [0 0 0 0 0 1 1 1 0 0 0 0]]
print beds
# 4
第二个解决方案我“可以想象工作”(但绝对不能证明是最优的)是使用相同的算法,限制在“修道院时间”时间,排序“修道院时间”矩阵。
首先,我会对矩阵进行排序,以便最早进入的僧侣出现:
time_slots_in_monastery = numpy.array([
(0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0), # monk 0 enters earlier than 1
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0),
])
然后对绑定的条目(同时进入的僧侣)进行排序,以便最早离开的僧侣出现:
time_slots_in_monastery = numpy.array([
(0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0), # monk 2 and 3 enter at same time, but 2 leaves earlier than 3
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0),
])
应用以前的算法,但仅限于time_slots_in_monastery
二进制掩码:
0
开始在Monk 0
掩码索引0
1
停止时,Monk 0
开始睡觉。从Monk 1
掩码索引0
这个想法是让两个相邻的僧侣尽可能多地重叠并尽早填充第一个插槽。
def rangemod(val, start, stop):
""" Modulo in range between start and stop
Adjusted from
http://stackoverflow.com/questions/3057640/math-looping-between-min-and-max-using-mod
"""
p = stop - start
mod = (val - start) % p
if mod < 0:
mod += p
return start + mod;
timeslots = numpy.array([
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0),
(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0),
(0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0),
])
sleephours = numpy.array([4, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3])
# Sort timeslots
startidx = numpy.array([list(row).index(1) for row in timeslots])
endidx = numpy.array([list(row)[::-1].index(1) for row in timeslots])
sortidx = numpy.array([
x for _, _, x in sorted(
zip(startidx, endidx, range(len(startidx))),
key=lambda (x, y, z): (x, -y)
)
])
# Put all indices and sleephours in new order
startidx = startidx[sortidx]
endidx = endidx[sortidx]
endidx = timeslots.shape[1] - endidx
sleephours = sleephours[sortidx]
timeslots = timeslots[sortidx, :]
print timeslots
# [[0 1 1 1 1 1 0 0 0 0 0 0]
# [0 0 1 1 1 1 1 1 0 0 0 0]
# [0 0 1 1 1 1 1 1 0 0 0 0]
# [0 0 1 1 1 1 1 1 1 0 0 0]
# [0 0 1 1 1 1 1 1 1 0 0 0]
# [0 0 1 1 1 1 1 1 1 0 0 0]
# [0 0 1 1 1 1 1 1 1 1 0 0]
# [0 0 0 1 1 1 1 1 1 0 0 0]
# [0 0 0 1 1 1 1 1 1 0 0 0]
# [0 0 0 1 1 1 1 1 1 0 0 0]
# [0 0 0 1 1 1 1 1 1 1 0 0]
# [0 0 0 0 0 1 1 1 1 1 0 0]]
out = numpy.zeros_like(timeslots)
# Fill sleep schedule
prevstop = startidx[0]
for r, s, e, t, row in zip(sleephours, startidx, endidx, timeslots, out):
sleeprange = numpy.arange(s, e)
sleeppattern = numpy.zeros(e - s)
sleeppattern[:r] = 1
sleeppattern = numpy.roll(sleeppattern, prevstop - s)
row[sleeprange] = sleeppattern
prevstop += r
prevstop = rangemod2(prevstop, s, e)
print out
# [[0 1 1 1 1 0 0 0 0 0 0 0]
# [0 0 1 0 0 1 1 1 0 0 0 0]
# [0 0 0 1 1 1 1 0 0 0 0 0]
# [0 0 1 0 0 0 0 1 1 0 0 0]
# [0 0 0 1 1 1 0 0 0 0 0 0]
# [0 0 0 0 0 0 1 1 1 0 0 0]
# [0 0 1 1 1 0 0 0 0 0 0 0]
# [0 0 0 0 0 1 1 1 1 0 0 0]
# [0 0 0 1 1 1 0 0 0 0 0 0]
# [0 0 0 0 0 0 1 1 1 0 0 0]
# [0 0 0 1 1 1 1 0 0 0 0 0]
# [0 0 0 0 0 0 0 1 1 1 0 0]]
print numpy.max(numpy.sum(sleeping_slots, axis=0))
# 6
答案 1 :(得分:1)
这是与上述方案略有不同的解决方案。我已经改组了僧侣的索引,我确信重建索引不会有问题。我相信解决方案是最佳的。
以下是算法:
首先,如果4和3,我们将僧侣分组为单位。
现在我们需要计算这些单位的数量,并为每个单位分配一张单人床。
现在关于剩下的僧侣:
我们肯定会把剩下4个时间单位的僧侣带到这个单位,并继续增加僧侣(他们需要3个单位时间),直到我们达到12个单位时间。我们为这个组分配一张床。
我们现在必须为其余不属于前一单位的僧侣分配一张床。
该计划如下所示:
if __name__ == '__main__':
reqSleep = [4, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3]
num4 = len([r for r in reqSleep if r==4])
num3 = len([r for r in reqSleep if r==3])
group4, left4 = num4/3, num4%3
group3, left3 = num3/4, num3%4
groups = {}
groups['group3'] = [
(1,1,1,0,0,0,0,0,0,0,0,0),
(0,0,0,1,1,1,0,0,0,0,0,0),
(0,0,0,0,0,0,1,1,1,0,0,0),
(0,0,0,0,0,0,0,0,0,1,1,1),
]
groups['group4'] = [
(1,1,1,1,0,0,0,0,0,0,0,0),
(0,0,0,0,1,1,1,1,0,0,0,0),
(0,0,0,0,0,0,0,0,1,1,1,1),
]
print num4, '-->', (group4, left4)
print num3, '-->', (group3, left3)
alreadyIn = []
for i in range(group4):
alreadyIn += groups['group4']
for i in range(group3):
alreadyIn += groups['group3']
# we add the number of monks who
# sleep for 4 hours
alreadyIn += groups['group4'][:left4]
# Now we are left with `left3`. How many can
# we add to this ? We take until they consume
# 12 time units.
num3ToFill = 0
currTotal = left4*4
for i in range(left3):
if currTotal + 3 > 12: break
num3ToFill += 1
currTotal += 3
# Fill in the number of groups
alreadyIn += groups['group3'][-num3ToFill:]
# Finally add the leftover monks at the end
if left3 > num3ToFill:
alreadyIn += groups['group3'][: (left3-num3ToFill)]
print 'Monk sleep specs ...'
for x in alreadyIn:
print x
print 'Beds used ...'
numBeds = map(sum, zip(*alreadyIn))
print numBeds
print 'done'
结果如下:
5 --> (1, 2)
7 --> (1, 3)
Monk sleep specs ...
(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
(0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0)
(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1)
(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
(0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0)
(0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1)
(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
(0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1)
(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
(0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0)
Beds used ...
[4, 4, 4, 4, 4, 4, 3, 3, 2, 3, 3, 3]
通过更仔细地查看“所需睡眠”时间,可以优化添加更多僧侣和不同时间单位。所有算法都可能解决优化该阵列的问题。