我建议使用 priority queue 存储 (estimated processing time, worker) 对和按该顺序进行比较的自定义比较器。
在伪代码中,assign to work 的主体如下所示:
(estimated_time, i) = queue.pop()
queue.push((estimated_time + worker_time[i], i))
return i
这是确定性的,需要O(N) 内存,并且每次分配都需要时间O(log(N))。
当然,您设置了worker_time[i] = 1.0/worker_speed[i],现在分配给i 的工人数量与其速度成正比。
对于查询界面,我们可以通过重建历史中的单个点然后向前播放的简单技巧来避免重播所有历史。为此,当时(i_work-1)/total_speed 不超过 i_work-1 可以产生。此外,不少于 i_work-N 可以产生。 (为什么?每个工人都未能产生小于理论 elapsed_time / worker_speed[i] 限制的 1 的分数。因此,N 工人在 N 下的产量低于该理论限制。由于它必须是整数,因此将其置于大多数 N-1 落后。和 i_work-1 - (N-1) = i_work-N。)那时,对于每个工人,我们知道该工人生产了多少,我们知道下一个工人何时会生产另一个单位。
这足以在那个时刻产生优先级队列。然后我们向前播放。在不超过N 的步骤中,k'th 将弹出并正确分配。
查询版本的总运行时间为O(N log(N))。俗话说,“从所有意图和目的来看,log(N) 是一个常数。对于 Google,它是一个更大的常数。”
(以相当复杂的代价为代价,我认为您可以使查询界面真正成为O(N)。)
在几乎是有效 Python 的伪代码中:
total_worker_speed = sum(1/t for t in worker_time)
t = (i_work-1) / total_worker_speed
total_done = 0
todo = []
for i in 0..count_workers:
# At time t, this is how many are left.
done = floor(t/worker_time[i])
# This is how long until this worker produces the next unit
time_left = (done+1)*worker_time[i] - t
todo.push([time_left, i])
total_done += done
queue = heapify(todo) # turning array into priority queue is O(N)
while total_done < iwork:
# Get the next one.
(estimated_time, i) = queue.pop()
queue.push((estimated_time + worker_time[i], i))
total_done += 1
# i now has the job that produced the iwork job.
return i