efficient idiomatic queue?

David Eppstein eppstein at ics.uci.edu
Mon Jan 14 23:19:05 EST 2002


In article <mailman.1011063021.30100.python-list at python.org>,
 "Tim Peters" <tim.one at home.com> wrote:

> It seems that your only objection to your original idea was that it's "very
> inefficient for long lists".  Why?  That is, if you're only trying to get
> across the idea of the algorithm, speed doesn't seem relevant ("assume these
> primitives take constant time; then here's the interesting result: ...").
> If you're actually worried about efficiency implications of details, I'd
> expect you'd be keen to teach about those too.

It's an algorithms design and analysis class.  If I'm going to tell the 
students that some algorithm takes O(n log n) time, it would be helpful if 
the code I show them actually takes O(n log n) instead of something slower 
that one could make efficient only with more detailed knowledge of Python's 
implementation details.

I realize implementation speed is not the primary concern of Python 
language designers, but for Python programmers I think Python's relatively 
slow speeds make it especially important to be careful about asymptotics: 
there's less slack for using a bad algorithm and letting today's gigahertz 
computers hide your inefficiencies.

> BTW, is it necessary to destory the input lists?  Rather than remove the
> element at the front of a list, most people <wink> would just increment an
> index vrbl.  Then all mutation occurs only via .append() on the output list,
> and your efficiency worry goes away.
> 
> saving-an-index-vrbl-isn't-worth-much-thought-ly y'rs  - tim

List destruction is not necessary.  The index variable approach is 
probably best in this example, since merge sort doesn't need the full 
power of a queue (each list has all its appends before it is scanned). I 
was just hoping that Python's powerful list processing primitives would 
let me simplify the code by avoiding those extra index variables.  And a 
queue is such a basic data structure that I thought surely Python already 
allowed it to be implemented efficiently with its built-in list 
operations.

Actually, the merge step in merge sort is a great example for simple 
generators, or would be if not for the difficulty of keeping track of the 
first item of each input iterator:

def merge(A,B):
        try:
                Afirst=A.next()
                Aempty=0
        except StopIteration:
                Aempty=1
        try:
                Bfirst=B.next()
                Bempty=0
        except StopIteration:
                Bempty=1
        while not Aempty and not Bempty:
                if Afirst < Bfirst:
                        yield Afirst
                        try:
                                Afirst=A.next()
                        except StopIteration:
                                Aempty=1
                else:
                        yield Bfirst
                        try:
                                Bfirst=B.next()
                        except StopIteration:
                                Bempty=1
        if not Aempty:
                yield Afirst
                while 1:
                        yield A.next()
        if not Bempty:
                yield Bfirst
                while 1:
                        yield B.next()

def mergesort(L):
        if len(L) < 2:
                return iter(L)
        else:
                return merge(mergesort(L[:len(L)//2]),
                             mergesort(L[len(L)//2:]))

This (like the closely related heap sort) has the advantage that if you 
start sorting n items, but only end up looking at the first k items of 
sorted output, it takes time O(n + k log n), so the time is nonlinear only 
when k is large. But this is way too much code for a blackboard, and the 
students are confused enough by slices...
-- 
David Eppstein       UC Irvine Dept. of Information & Computer Science
eppstein at ics.uci.edu http://www.ics.uci.edu/~eppstein/



More information about the Python-list mailing list