Last active
July 30, 2022 00:28
-
-
Save zed/5651186 to your computer and use it in GitHub Desktop.
Revisions
-
zed revised this gist
May 25, 2013 . 1 changed file with 8 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,7 +28,14 @@ def llistsort(head): [2]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html """ insize = 1 # lists of size 1 are always sorted while True: # merge adjacent pairs of `insize`-sized sorted lists if __debug__: # check the loop invariant pairwise = lambda L: zip(L, L[1:]) issorted = lambda L: all(x <= y for x, y in pairwise(L)) # all adjacent `insize`-sized lists are sorted lst = tolist(head) assert all(issorted(lst[i:i+insize]) for i in range(0, len(lst), insize)) p = head # head of the left list to be merged head, tail = empty, empty # head and tail of the output list nmerges = 0 # count number of merges we do in this pass -
zed revised this gist
May 25, 2013 . 1 changed file with 11 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,8 +15,14 @@ def __init__(self, value, next=empty): def llistsort(head): """Translation from C to Python of listsort() [1]. O(N log N) (best & worst) -time, O(1)-space inplace Mergesort algorithm for a singly linked linear list [2] >>> head = mklist([2, 3, 1]) >>> tolist(head) [2, 3, 1] >>> tolist(llistsort(head)) [1, 2, 3] [1]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c [2]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html @@ -105,4 +111,6 @@ def test(): head = llistsort(head) assert tolist(head) == sorted(L) if __name__ == "__main__": import doctest; doctest.testmod() test() -
zed revised this gist
May 25, 2013 . 1 changed file with 23 additions and 22 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,32 +5,13 @@ # Linked list is either empty or a value and a link to the next list empty = None # empty list class LL(object): __slots__ = "value", "next" def __init__(self, value, next=empty): self.value = value self.next = next def llistsort(head): """Translation from C to Python of listsort() [1]. @@ -95,13 +76,33 @@ def llistsort(head): else:# otherwise repeat, merging lists twice the size insize *= 2 def mklist(initializer): it = reversed(initializer) try: x = next(it) except StopIteration: return empty # empty list else: head = LL(x) for value in it: head = LL(value, head) return head def walk(head): while head is not empty: yield head.value head = head.next def tolist(head): return list(walk(head)) def test(): for n, r, k in product(range(10), repeat=3): L = list(range(n)) + [n//2]*r + [n-1]*k random.shuffle(L) head = mklist(L) assert tolist(head) == L head = llistsort(head) assert tolist(head) == sorted(L) test() -
zed revised this gist
May 25, 2013 . 1 changed file with 14 additions and 14 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,11 +3,11 @@ import random from itertools import product # Linked list is either empty or a value and a link to the next list empty = None # empty list class LL(object): __slots__ = "value", "next" def __init__(self, value, next=empty): self.value = value self.next = next @@ -16,15 +16,15 @@ def mklist(initializer): try: x = next(it) except StopIteration: return empty # empty list else: head = LL(x) for value in it: head = LL(value, head) return head def walk(head): while head is not empty: yield head.value head = head.next @@ -43,28 +43,28 @@ def llistsort(head): insize = 1 # lists of size 1 are always sorted while True: # merge adjacent pairs of `insize`-sized lists p = head # head of the left list to be merged head, tail = empty, empty # head and tail of the output list nmerges = 0 # count number of merges we do in this pass while p is not empty: nmerges += 1 # there exists a merge to be done # step `insize' places along from p or until the end of # the list, whichever comes first q = p # head of the right list to be merged for psize in range(1, insize + 1): q = q.next if q is empty: # the end of the list (q is empty list) break qsize = insize # merge a list starting at p, of length psize, with a list # starting at q of length *at most* qsize while psize > 0 or (qsize > 0 and q is not empty): # decide whether next element of merge comes from p or q if psize == 0: # p is empty e, q = q, q.next # e must come from q, pop q qsize -= 1 elif qsize == 0 or q is empty: # q is empty e, p = p, p.next # e must come from p, pop p psize -= 1 elif p.value <= q.value: # first element of p is lower or same @@ -77,17 +77,17 @@ def llistsort(head): qsize -= 1 # add e to the end of the output list if tail is not empty: tail.next = e else: # 1st iteration head = e # smallest item among two lists tail = e # now p has stepped `insize' places along, and q has too (or eol) p = q # move to the next pair of lists to merge #end of while p is not empty: if tail is not empty: tail.next = empty # terminate the output list # if we have done only one merge, we're finished if nmerges <= 1: # allow for nmerges==0, the empty list case -
zed revised this gist
May 25, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -96,7 +96,7 @@ def llistsort(head): insize *= 2 def test(): for n, r, k in product(range(10), repeat=3): L = list(range(n)) + [n//2]*r + [n-1]*k random.shuffle(L) head = mklist(L) -
zed revised this gist
May 25, 2013 . 1 changed file with 37 additions and 24 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,10 @@ #!/usr/bin/env python """Merge sort a singly linked linear list.""" import random from itertools import product # Linked list is either None (empty list) or a value and a link to the # next list class LL(object): __slots__ = "value", "next" def __init__(self, value, next=None): @@ -27,67 +31,76 @@ def walk(head): def tolist(head): return list(walk(head)) def llistsort(head): """Translation from C to Python of listsort() [1]. O(N log N) (best, average, worst) -time (Mergesort), O(1)-space inplace sorting algorithm for a singly linked linear list [2] [1]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c [2]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html """ insize = 1 # lists of size 1 are always sorted while True: # merge adjacent pairs of `insize`-sized lists p = head # head of the left list to be merged head, tail = None, None # head and tail of the output list nmerges = 0 # count number of merges we do in this pass while p is not None: nmerges += 1 # there exists a merge to be done # step `insize' places along from p or until the end of # the list, whichever comes first q = p # head of the right list to be merged for psize in range(1, insize + 1): q = q.next if q is None: # the end of the list (q is empty list) break qsize = insize # merge a list starting at p, of length psize, with a list # starting at q of length *at most* qsize while psize > 0 or (qsize > 0 and q is not None): # decide whether next element of merge comes from p or q if psize == 0: # p is empty e, q = q, q.next # e must come from q, pop q qsize -= 1 elif qsize == 0 or q is None: # q is empty e, p = p, p.next # e must come from p, pop p psize -= 1 elif p.value <= q.value: # first element of p is lower or same # choose p in the case of `p.value == q.value` to # maintain sort stability e, p = p, p.next # e must come from p, pop p psize -= 1 else: # p.value > q.value i.e., first element of q is lower e, q = q, q.next # e must come from q, pop q qsize -= 1 # add e to the end of the output list if tail is not None: tail.next = e else: # 1st iteration head = e # smallest item among two lists tail = e # now p has stepped `insize' places along, and q has too (or eol) p = q # move to the next pair of lists to merge #end of while p is not None: if tail is not None: tail.next = None # terminate the output list # if we have done only one merge, we're finished if nmerges <= 1: # allow for nmerges==0, the empty list case return head else:# otherwise repeat, merging lists twice the size insize *= 2 def test(): for n, r, k in product(range(14), repeat=3): L = list(range(n)) + [n//2]*r + [n-1]*k random.shuffle(L) head = mklist(L) head = llistsort(head) result = tolist(head) assert result == sorted(L) -
zed revised this gist
May 25, 2013 . 1 changed file with 16 additions and 20 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -35,37 +35,33 @@ def listsort(head): insize = 1 while True: p = head head, tail = None, None # head and tail of merged list nmerges = 0 # count number of merges we do in this pass while p is not None: nmerges += 1 # there exists a merge to be done # step `insize' places along from p q = p for psize in range(1, insize + 1): q = q.next if q is None: break qsize = insize # if q hasn't fallen off end, we have two lists to merge while psize > 0 or (qsize > 0 and q is not None): # decide whether next element of merge comes from p or q if psize == 0: # p is empty e, q = q, q.next # e must come from q qsize -= 1 elif qsize == 0 or q is None: # q is empty e, p = p, p.next # e must come from p psize -= 1 elif p.value <= q.value: # first element of p is lower (or same) e, p = p, p.next # e must come from p psize -= 1 else: # p.value > q.value i.e., first element of q is lower e, q = q, q.next # e must come from q qsize -= 1 # add the next element to the merged list -
zed revised this gist
May 25, 2013 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,5 @@ import random from itertools import product class LL(object): __slots__ = "value", "next" -
zed revised this gist
May 25, 2013 . 1 changed file with 2 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -31,9 +31,6 @@ def listsort(head): [1]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c """ insize = 1 while True: p = head @@ -80,7 +77,8 @@ def listsort(head): # now p has stepped `insize' places along, and q has too p = q if tail is not None: tail.next = None # if we have done only one merge, we're finished if nmerges <= 1: # allow for nmerges==0, the empty list case return head -
zed revised this gist
May 25, 2013 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -88,8 +88,8 @@ def listsort(head): insize *= 2 def test(): for n, r, k in product(range(14), repeat=3): L = list(range(n)) + [n//2]*r + [n-1]*k random.shuffle(L) head = mklist(L) head = listsort(head) -
zed created this gist
May 25, 2013 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,99 @@ import random class LL(object): __slots__ = "value", "next" def __init__(self, value, next=None): self.value = value self.next = next def mklist(initializer): it = reversed(initializer) try: x = next(it) except StopIteration: return None # empty list is None else: head = LL(x) for value in it: head = LL(value, head) return head def walk(head): while head is not None: yield head.value head = head.next def tolist(head): return list(walk(head)) def listsort(head): """Translation from C to Python of listsort() [1]. [1]: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c """ if head is None: # empty list is already sorted return head insize = 1 while True: p = head head, tail = None, None nmerges = 0 # count number of merges we do in this pass while p: nmerges += 1 # there exists a merge to be done q = p # step `insize' places along from p psize = 0 for _ in range(insize): psize += 1 q = q.next if q is None: break # if q hasn't fallen off end, we have two lists to merge qsize = insize # now we have two lists; merge them while psize > 0 or (qsize > 0 and q is not None): # decide whether next element of merge comes from p or q if psize == 0: # p is empty; e must come from q e, q = q, q.next qsize -= 1 elif qsize == 0 or q is None: # q is empty; e must come from p e, p = p, p.next psize -= 1 elif p.value <= q.value: # first element of p is lower (or same); e must come from p e, p = p, p.next psize -= 1 else: # first element of q is lower; e must come from q e, q = q, q.next qsize -= 1 # add the next element to the merged list if tail is not None: tail.next = e else: head = e tail = e # now p has stepped `insize' places along, and q has too p = q tail.next = None # if we have done only one merge, we're finished if nmerges <= 1: # allow for nmerges==0, the empty list case return head # otherwise repeat, merging lists twice the size insize *= 2 def test(): for L in [list(range(13)), [], [1]*3, list(range(13)) + [1]*3]: random.shuffle(L) head = mklist(L) head = listsort(head) result = tolist(head) assert result == sorted(L) test()