Inserting while itterating
Peter Otten
__peter__ at web.de
Thu Jan 15 19:22:14 EST 2004
Skip Montanaro wrote:
>>>>>> "Mark" == Mark McEahern <mark at mceahern.com> writes:
>
> Mark> On Wed, 2004-01-14 at 02:43, Thomas Guettler wrote:
> >> Hi,
> >>
> >> Simple excerise:
> >>
> >> You have a sorted list of integers:
> ...
> >> and you should fill all gaps:
> ...
> >> How would you code this?
> >>
> >> Constrain: The original list should be changed, don't create a
> >> copy.
>
> Mark> In the spirt of unit testing...
>
> Mark> #!/usr/bin/env python
> ...
>
> Here's a version which is much faster if you have large gaps and appears
> to be only marginally slower if you have small gaps.
>
> Skip
>
> #!/usr/bin/env python
>
> #!/usr/bin/env python
>
> def fillGaps1(seq):
if len(seq) == seq[-1]:
raise Exception("nothing to do")
> expectedLength = seq[-1] - seq[0] + 1
> i = 1
> while i < expectedLength:
> if seq[i] - seq[i-1] > 1:
> seq.insert(i, seq[i-1] + 1)
> i += 1
>
> def fillGaps(seq):
> expectedLength = seq[-1] - seq[0] + 1
> i = 1
> while i < expectedLength:
> if seq[i] - seq[i-1] > 1:
> gap = seq[i-1] - seq[i]
> fill = range(seq[i-1]+1, seq[i])
> seq[i:i] = fill
> i += len(fill)
> i += 1
>
> if __name__ == "__main__":
> import timeit
>
> print "timing with one large gap:"
> t = timeit.Timer(setup='from fillgaps import fillGaps1 as fillGaps',
> stmt='fillGaps([1, 5000])')
> print "old fillgaps:", t.timeit(number=100)
> t = timeit.Timer(setup='from fillgaps import fillGaps',
> stmt='fillGaps([1, 5000])')
> print "new fillgaps:", t.timeit(number=100)
>
> print "timing with many small gaps:"
> t = timeit.Timer(setup='from fillgaps import fillGaps1 as
> fillGaps;l=range(1,5001,2)',
> stmt='fillGaps(l)')
I think the list should be initialized in the statement instead of the
setup. Otherwise subsequent passes will measure for range(1, 5000).
> print "old fillgaps:", t.timeit(number=100)
> t = timeit.Timer(setup='from fillgaps import
> fillGaps;l=range(1,5001,2)',
> stmt='fillGaps(l)')
> print "new fillgaps:", t.timeit(number=100)
>
> import unittest
> class test(unittest.TestCase):
> def test(self):
> for fg in (fillGaps1, fillGaps):
> l = [1, 2, 4, 7, 8, 12]
> fg(l)
> self.assertEquals(l, range(1, 13))
>
> l = [1, 5000]
> fg(l)
> self.assertEquals(l, range(1, 5001))
>
> unittest.main()
Here's another fillgap version:
def fillGapsPeter(seq):
# inspired by anton muhin
lo = seq[0]
hi = seq[-1]
seq[1:1] = range(lo+1, hi+1)
while len(seq) > hi:
i = seq.pop()
seq[i-lo] = i
def fillGapsMark(seq):
expectedLength = seq[-1] - seq[0] + 1
i = 1
while i < expectedLength:
if seq[i] - seq[i-1] > 1:
seq.insert(i, seq[i-1] + 1)
i += 1
def fillGapsSkip(seq):
expectedLength = seq[-1] - seq[0] + 1
i = 1
while i < expectedLength:
if seq[i] - seq[i-1] > 1:
gap = seq[i-1] - seq[i]
fill = range(seq[i-1]+1, seq[i])
seq[i:i] = fill
i += len(fill)
i += 1
if __name__ == "__main__":
import timeit, fillgaps, sys
fnames = [fname for fname in dir(fillgaps) if callable(getattr(fillgaps,
fname))]
fnames.sort()
for header, lst in [
("original test case", [1, 2, 4, 7, 8, 12]),
("timing with one large gap:", [1, 5000]),
("timing with many small gaps:", range(1, 5001, 2))]:
print header
for fn in fnames:
t = timeit.Timer(setup='from fillgaps import %s as fillGaps' %
fn,
stmt='fillGaps(%s)' % (lst,))
print "\t%s:" % fn, t.timeit(number=100)
print
import unittest
class test(unittest.TestCase):
def test_all(self):
for fg in [getattr(fillgaps, fn) for fn in fnames]:
l = [1, 2, 4, 7, 8, 12]
fg(l)
self.assertEquals(l, range(1, 13))
l = [1, 5000]
fg(l)
self.assertEquals(l, range(1, 5001))
def test_mine(self):
""" Hard to prove I'm not cheating,
hope I got it right...
"""
class myint(int):
def __repr__(self):
return "my" + int.__repr__(self)
original = map(myint, [1, 2, 4, 7, 8, 12])
l = list(original)
fillGapsPeter(l)
self.assertEquals(l, range(1, 13))
for i in original:
self.assert_(i is l[i-1])
self.assert_(repr(i).startswith("my"))
unittest.main()
My findings:
original test case
fillGapsMark: 0.00151419639587
fillGapsPeter: 0.00127387046814
fillGapsSkip: 0.00162696838379
timing with one large gap:
fillGapsMark: 0.872708797455
fillGapsPeter: 0.0312719345093
fillGapsSkip: 0.0336079597473
timing with many small gaps:
fillGapsMark: 0.973310947418
fillGapsPeter: 0.412738800049
fillGapsSkip: 1.47315406799
Peter
More information about the Python-list
mailing list