添加到列表时对日期列表进行排序的最快方法

时间:2012-04-24 14:11:29

标签: python list datetime sorting

我正在写一些类似于任务调度程序的东西。我有两组任务,一些是固定的(它们有一个开始和结束日期和时间),一些没有固定(它们有一个开始日期和时间以及持续时间)。

非固定任务受固定任务的影响,因此如果非固定任务与固定任务重叠,则非固定任务会将其持续时间延长重叠量。

我从一个元组列表开始,其中第一个项目是开始日期,第二个项目是该固定任务的ID,如下所示:

[(2012-04-30, 1), (2012-05-01, 5), (2012-05-04, 2)]

然后我有另一个列表,由用户订购,非固定任务。我的想法是循环遍历这个列表,并且在该循环内部我将循环遍历第一个列表以找到可能与此任务重叠的任务,并且可以确定扩展非固定任务的数量

这是我要求你帮助的地方。现在我知道了这个非固定任务的计算开始和结束时间,我需要将其视为“固定”,以便它影响其他非固定任务。

我可以将此任务添加到第一个固定任务列表并再次对其进行排序,但这意味着每次向其添加任务时我都会对列表进行排序。

我可以遍历第一个列表并找到应该插入此任务的点,然后将其插入其中。但是,如果它的位置在列表的早期,则花费时间将所有其他项目转移到一个位置。如果它的位置在列表的后面,我将不得不循环通过很多元素来到达正确的位置。

所以,我并没有使用其中任何一个选项。这里真正的问题是:在向其添加内容时对列表进行排序的最佳方法是什么?或者有更好的方法来做所有这些吗?

4 个答案:

答案 0 :(得分:3)

以下是使用bisect和使用部分排序列表的排序进行比较的示例。 bisect解决方案明显胜出:

import bisect
import random
import timeit


def bisect_solution(size=10000):
    lst = []
    for n in xrange(size):
        value = random.randint(1, 1000000)
        bisect.insort_left(lst, value)
    return lst


# Cut out of the bisect module to be used in bisect_solution2()
def insort_left(a, x, lo=0, hi=None):
    """Insert item x in list a, and keep it sorted assuming a is sorted.

    If x is already in a, insert it to the left of the leftmost x.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    a.insert(lo, x)


def bisect_solution2(size=10000):
    lst = []
    for n in xrange(size):
        value = random.randint(1, 1000000)
        insort_left(lst, value)
    return lst


def sort_solution(size=10000):
    lst = []
    for n in xrange(size):
        value = random.randint(1, 1000000)
        lst.append(value)
        lst.sort()
    return lst


t = timeit.timeit('bisect_solution()', 'from __main__ import bisect_solution', number = 10)
print "bisect_solution: ", t

t = timeit.timeit('bisect_solution2()', 'from __main__ import bisect_solution2', number = 10)
print "bisect_solution2: ", t

t = timeit.timeit('sort_solution()', 'from __main__ import sort_solution', number = 10)
print "sort_solution: ", t

bisect_solution2()与bisect_solution()几乎相同 - 只是复制了模块的代码。别人应该解释为什么需要更多时间:)

bisect_solution2()在这里被修改为cmp()函数,以便能够比较元组。

它在我的电脑上显示以下结果:

bisect_solution:  0.637892403587
bisect_solution2:  0.988893038133
sort_solution:  15.3521410901

这是元组采用的二元解决方案,其中date是一个字符串:

import random
import timeit


def random_date_tuple():
    s1 = '{0}-{1:02}-{2:02}'.format(random.randint(2000, 2050),
                                    random.randint(1, 12),
                                    random.randint(1, 31))
    e2 = random.randint(1,50)
    return (s1, e2)


def my_cmp(a, b):
    result = cmp(a[0], b[0])   # comparing the date part of the tuple
    if result == 0:
        return cmp(a[1], b[1]) # comparint the other part of the tuple
    return result


def my_insort_left(a, x, cmp=my_cmp, lo=0, hi=None):
    """The bisect.insort_left() modified for comparison of tuples."""

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if cmp(a[mid], x) < 0: 
            lo = mid+1
        else: 
            hi = mid
    a.insert(lo, x)


def bisect_solution3(size=1000):
    lst = []
    for n in xrange(size):
        value = random_date_tuple()
        my_insort_left(lst, value)
    return lst


def sort_solution(size=1000):
    lst = []
    for n in xrange(size):
        value = random_date_tuple()
        lst.append(value)
        lst.sort(cmp=my_cmp)
    return lst


t = timeit.timeit('bisect_solution3()', 'from __main__ import bisect_solution3', number = 10)
print "bisect_solution3: ", t

t = timeit.timeit('sort_solution()', 'from __main__ import sort_solution', number = 10)
print "sort_solution: ", t

print bisect_solution3()[:10]

请注意,由于排序解决方案非常慢,因此列表大小比之前减少了10倍。它打印:

bisect_solution3:  0.223602245968
sort_solution:  3.69388944301
[('2000-02-01', 20), ('2000-02-13', 48), ('2000-03-11', 25), ('2000-03-13', 43),
 ('2000-03-26', 48), ('2000-05-04', 17), ('2000-06-06', 23), ('2000-06-12', 31),
 ('2000-06-15', 15), ('2000-07-07', 50)]

答案 1 :(得分:1)

  

这里真正的问题是:在添加内容时对列表进行排序的最佳方法是什么?

Insertion Sort是要走的路。但你可能不喜欢它,因为已经知道这一点。接下来你可以做的就是这个,

  1. 添加时不要排序。
  2. 当您获取项目时对其进行排序并对其进行缓存。当其下次请求从之前的缓存中显示时。
  3. 添加任何新项目时,缓存无效。
  4. 我不是python程序员但是我可以用PHP类给你一些想法。

    class SortedList(){
        public $list = array();
        private $cached_list;
    
        public function add($item){
            array_push($this->list, $item);
            $this->sorted = false;
        }
        public function get(){
            if($this->sorted==true){
                return $this->cached_list;
            }
    
            // sort the array;
    
            // copying the list to cached list and sort it
            $this->cached_list = $this->list;
            sort($this->cached_list);
    
            // set the flag
            $this->sorted = true;
            return $this->cached_list
        }
    
    }
    

答案 2 :(得分:1)

  

我可以遍历第一个列表并找到执行此任务的点   应该插入,然后插入它。但是,如果它的位置是   在列表的早期,时间用于转移所有其他项目之一   地点。如果它的位置在列表的后期,我将不得不循环   通过很多元素来到达正确的地方。

可以使用binary search在O(log n)中找到将某些内容插入到排序列表中的正确位置。插入仍然是O(n)。

有更复杂的数据结构,如B-Trees,允许在O(log n)中插入和搜索。请查看thisthis

答案 3 :(得分:0)

Heap Queue是你的朋友。来自维基百科:

通常使用堆执行的操作是:

  • create-heap :创建一个空堆
  • find-max :查找max-heap的最大项目
  • delete-max :删除max-heap的根节点
  • 增加密钥:更新max-heap中的密钥
  • 插入:向堆中添加新密钥
  • 合并:加入两个堆以形成一个包含所有内容的有效新堆 两者的要素。

有一个内置Python Heap Queue实现。堆优化用于1)删除max元素,2)插入新元素以维持堆排序。