优先级队列:并行处理

时间:2016-12-16 14:24:56

标签: python algorithm python-3.x

我正在尝试解决关于Coursera上的优先级队列的这个算法问题,但是他们网站上的评分者一直说我的程序因时间限制超出错误而失败。事实上,当我在我的PC上运行它有大量输入(5000个线程,100000个作业)时,它可以顺利运行并在不超过1秒的时间内打印出正确的结果。

这是问题描述:

enter image description here

这是我在Github上的代码的链接:https://gist.github.com/giantonia/3ddbacddc7bd58b220ab592f802d9602

任何帮助表示赞赏!

2 个答案:

答案 0 :(得分:1)

代码中最薄弱的部分在下面,

while len(jobs) > 0:
    if threads[0][1] <= time:
      ...
    else:
        time += 1

此循环将与时间一起执行,而不是必须完成的作业数量。它需要O(MAX_T)成本!太慢了!

这是我解决这个问题的方法。它需要O(N + MlgN))。

这个想法非常简单。

  1. 构建priority_queue以及最早完成的时间。
  2. 从priority_queue中选择next_thread并更新完成作业的时间。
  3. 将其插入优先级队列
  4. 这是代码,

    # python3
    
    def parent_key_cal(key):
        if key % 2 == 0:
            parent_key = key//2
        else:
            parent_key = (key - 1)//2
        return parent_key
    
    def swap(alist, key1, key2):
        temp = alist[key1]
        alist[key1] = alist[key2]
        alist[key2] = temp
    
    def return_min_key(alist, parent, left, right, criteria):
    
        min_value = parent
        if alist[parent][criteria] > alist[left][criteria]:
            min_value = left
            if right != -1 and alist[min_value][criteria] > alist[right][criteria]:
                min_value = right
        elif alist[parent][criteria] < alist[left][criteria]:
            if right != -1 and alist[min_value][criteria] > alist[right][criteria]:
                min_value = right
    
        return min_value
    
    def shift_up(alist, key):
    
        while key > 1:
    
            parent = parent_key_cal(key)
            if alist[parent][1] != alist[key][1]:
                if alist[parent][1] > alist[key][1]:
                    swap(alist, parent, key)
                    key = parent
                else:
                    break
            else:
                if alist[parent][0] > alist[key][0]:
                    swap(alist, parent, key)
                    key = parent
                else:
                    break
    
    def shift_down(alist, key):
    
        if 2*key >= len(alist):
            return
    
        parent = key
        left = 2*key
        right = 2*key + 1
    
        if right >= len(alist):
    
            if (alist[parent] == alist[left]) == True:
                min_value = return_min_key(alist, parent, left, -1, 0)
            else:
                min_value = return_min_key(alist, parent, left, -1, 1)
    
        else:
    
            if (alist[parent] == alist[left] == alist[right]) == True:
                min_value = return_min_key(alist, parent, left, right, 0)
            else:
                min_value = return_min_key(alist, parent, left, right, 1)
    
        if min_value != parent:
            swap(alist, parent, min_value)
            shift_down(alist, min_value)     
    
    
    def min_heap(alist):
        # Index 0 element is dummy. minimum element's index is 1
        min = alist[1]
        alist.pop(1)
    
        # Maintain heap structure
        parent_last_element = parent_key_cal(len(alist)-1)
        for key in reversed(range(1, parent_last_element + 1)):
            shift_down(alist, key)
    
        return min
    
    def heap_insert(alist, value):
        alist.append(value)
        shift_up(alist, len(alist)-1)
    
    line1 = input().split()
    n = int(line1[0])
    m = int(line1[1])
    jobs = list(map(int, input().split()))
    threads = []
    for i in range(n):
        threads.append([i, 0])
    
    # Insert dummy element to make heap calculation easier
    threads.insert(0,[-1,-1])
    
    record = []
    # O(M)
    while len(jobs) > 0:
        # Allocate a job to a thread and record it this moment
        # "threads" is min_heap along with time to finish a allocated job. 0 -> thread order, 1 ->  time to finish the job
        next_thread = min_heap(threads)  # O(lgN)
        record.append([next_thread[0], next_thread[1]])  
    
        # Updated poped thread as much as time to finish the next job
        next_thread[1] += jobs.pop(0) 
    
        # Insert this into min_heap
        heap_insert(threads, next_thread)
    
    for i in range(len(record)):
        print(str(record[i][0]) + ' ' + str(record[i][1]))
    

答案 1 :(得分:0)

首先,我建议在本地进行最大测试时运行解决方案(即n = 100000和m = 100000)(是的,5000和100000是一个很大的考验,但是你会在那里停下来吗?为什么不呢?使用最大可能的测试用例?)。

其次,您的解决方案中至少存在两个缺陷:

  1. 它将时间增加一而不是跳到下一个事件:

    while len(jobs) > 0:
        if threads[0][1] <= time:
            record.append([threads[0][0], time])
            ...
        else:
            time += 1
    

    需要O(MAX_T)次操作。如果最长时间是10 ^ 9那就太多了。

  2. jobs.pop(0)可能在O(n)中工作(它取决于python实现,但如果它像C ++ vector一样工作,许多解释器就是这种情况),这会产生{{1}在最坏的情况下操作。这太过分了。

  3. 你的解决方案中可能还有其他缓慢的部分(我立即看到了这两个部分,所以我只写了它们)。

    我建议你重新设计算法,证明它足够快(提示:它应该类似于O(n^2)),并且只有在实现它之后。