A skip list container class in Python
|
|
|
- Eustacia Lyons
- 10 years ago
- Views:
Transcription
1 A skip list container class in Python Abstract An alternative to balanced trees John W. Shipman :23 Describes a module in the Python programming language that implements a skip list, a data structure for storing and retrieving sorted elements in O(log n) time. This publication is available in Web form 1 and also as a PDF document 2. Please forward any comments to [email protected]. Table of Contents 1. Why skip lists? An implementation of skip lists in Python Definitions Creating a Python skip list Methods and attributes of the SkipList object Internals of SkipList The skip list data structure Skip list algorithms Iterators: toward a more Pythonic class Classes in this module The source code Declarations Verification functions The _SkipItem internal class The _SkipListIterator class The SkipList class SkipList. init () SkipList. estimatelevels() SkipList.insert() SkipList. keyof() SkipList. insertcutlist() SkipList. insertpoint() SkipList. insertionprecedes() SkipList. compareitemkey() SkipList. insertitem() SkipList. picklevel() SkipList. insertrelink() SkipList.delete()
2 4.18. SkipList. searchcutlist() SkipList. searchpoint() SkipList. searchprecedes() SkipList.match() SkipList. searchcutitem SkipList.find() SkipList. len () SkipList. iter () SkipList. contains () SkipList. delitem () SkipList. getitem () skiptest: A small test driver Performance testing Why skip lists? A skip list is a data structure for representing an ordered set of objects so that insertion and retrieval take minimal time. This ingenious data structure was described in: Pugh, William. Skip lists: a probabilistic alternative to balanced trees. In: Communications of the ACM, June 1990, 33(6) Like balanced tree structures, skip lists support search whose time complexity varies as the log of the number of elements. However, compared to 2-3 trees and other tree-oriented techniques, skip lists require much simpler algorithms and much less storage overhead within the data structure. There is also a probabilistic element in the construction of a skip list that makes it an interesting example of the use of randomness in algorithms. 2. An implementation of skip lists in Python The Python module is an implementation of Pugh's skip list data structure Definitions It is necessary to define a few terms before we explore the use and construction of skip lists The child domain A child object is any object that you want to store in a skip list. Typically all child objects are the same type, but there's nothing preventing you from using more than one child object type in a skip list, if you need to. By child domain we mean the type (or types) of child objects The key domain Since skip lists are designed to allow rapid lookup of random child objects, and because child objects are kept in sorted order, we must define how two child objects are to be compared so they can be placed in order. 2
3 By key we mean the value used to compare two child objects. Depending on the application, the key may be a value stored inside the child object, or the key may be the child object itself, or a value derived from the child object. For example, you might have a set of FishLicense objects representing fish licenses, each containing a unique ten-character license number. You might use a skip list to hold this set of objects, and designate the license number as the key. Then you could rapidly retrieve fish licenses by number. We use the term key domain to mean the set of possible key values Stability If duplicates are allowed, equal members will be kept in their order of insertion. For example, if you insert three equal elements a, b, and c into a skip list in that order, if you then iterate over the members of the list, those three elements will be returned in the same order. This property of a container class is called stability. The term comes from sorting theory: a sort that does not perturb the order of elements with equal keys is called a stable sort Creating a Python skip list Typically you will import the Python skip list module as: import pyskip To create a new, empty skip list, use this syntax: pyskip.skiplist ( keyfun=none, cmpfun=none, allowdups=0, count= ) where the arguments have these values: keyfun This argument is a function that takes a child argument and returns the corresponding key value. The default value, None, causes SkipList to treat the child elements themselves as keys. Suppose, for example, that you are building a skip list full of HockeyTeam objects, and each object has an attribute.teamname that contains the team's name as a string. You could write that key function as: def teamkey(hockeyteam): return hockeyteam.teamname Then to create the skip list you'd say something like: skip = SkipList ( keyfun=teamkey ) and the objects would be ordered alphabetically by team name. To make the ordering case-insensitive, you could write the key function like this, uppercasing the keys for comparison: def teamkey(hockeyteam): return hockeyteam.teamname.upper() cmpfun A function that defines the ordering relation on values in the list. The default value, None, means to use the usual Python cmp() function to compare values. You may supply your own function, 3
4 adhering to the usual convention for this function: when called to compare two elements a and b, cmp(a, b) should return: a negative value, if a should precede b; zero, if a and b are considered equal; or a positive value if a should follow b. allowdups If you use allowdups=1, you will be allowed to store multiple objects in the skip list even though they compare equal to each other. If, however, you set allowdups=0, the skip list object will raise an exception if you attempt to insert an element into the list that is equal to an existing element, according to the cmpfun function (or its default). count The purpose of this argument is to tune performance with very large skip lists. With the default value for count equal to a million, the skip list should provide access in logarithmic time for up to about a million elements. If you plan to store more than that, set count to an estimated number of elements. If the number of elements in the skip list greatly exceeds the count value, it will still function correctly, but the performance may suffer Methods and attributes of the SkipList object These attributes and methods are defined on a SkipList object:.insert ( c ) Inserts a new child element c into the skip list. If you created the skip list with allowdups=0 and the new element has the same key as an existing member of the list, this method will raise a KeyError exception. If duplicates are allowed (the allowdups=1 option to the constructor), and the new element's key duplicates one or more already in the list, the new child will be added in sequence after the other duplicates. This makes the ordering stable; see Section 2.1.3, Stability (p. 3)..delete ( k ) Deletes the first or only element whose key is equal to k, and returns the deleted child element (or None if there are no matching elements). If duplicates are allowed and there are multiple elements equal to e, only the first one will be deleted..match ( k ) Returns the first or only object whose key equals k. If there are no such objects, this method raises a KeyError exception..find ( k ) Searches for the first element whose key is equal to or greater than k, and returns an iterator that iterates over all those elements..first() If there are any elements in the skip list, this method returns the first element. If the skip list is empty, it raises KeyError.. len (self) This special method defines the meaning of the ordinary len() function when applied to a SkipList. It returns the number of child elements in the list. 4
5 . iter (self) Returns an iterator object that can be used to iterate over the elements of the skip list. You can have multiple independent iterators pointing at the same skip list, and each will move independently of the others. This allows constructs like: for i in s:... where s is a SkipList object. Such a loop will set i to each child value from s in turn.. contains ( self, k ) Defines the meaning of the Python in and not in operators in the usual way for container classes. If s is a SkipList, the expression k in s returns true if and only if k is equal to at least one member of s. Similarly, the expression k not in s returns true iff k is not equal to any member of s.. delitem ( self, k ) Same as.delete(k).. getitem ( self, k ) This method defines the usual meaning of s[k], where s is a SkipList. It has the same semantics as s.match[k]. This syntax is useful mainly for skip lists that don't allow duplicates. If you need to retrieve multiple members that all compare equal, use the.find() method..nsearches Read-only; equal to the number of times the list has been searched. Each call to the.match(),.find(), or.delete() method is counted as one search..ncompares Read-only; equal to the number of times two elements have been compared with the cmpfun function (or its default). Statistically, the ratio of.ncompares to.nsearches should be proportional to the log of the number of elements. For analysis, see Pugh's original article. 3. Internals of SkipList Before examining the actual code, let's discuss the data structure and some other implementation considerations The skip list data structure To implement the functionality of this ordered set container, we could just keep all the objects in an ordinary linked list. However, searching a linked list has a time complexity that varies linearly with the number of elements. To be competitive with balanced tree approaches such as 2-3 trees, we need to be able to search in log time. Pugh's idea was to set up a number of linked lists, numbered starting at 0, such that: Every object is in list 0, ordered from lowest to highest key. List i visits a subset of the objects in list (i-1), but still in order by key. In practice, when each new element is inserted, it is always added to list 0, and it is also added to a random number of higher-numbered lists, where each higher level is less likely. 5
6 With this structure, the higher-numbered linked lists are more and more sparse. Therefore, the algorithm for searching a skip list is: 1. Search the highest-numbered list until you find either the desired item or one that is beyond the desired item. 2. If the desired item is not in the highest-numbered list, back up to the preceding item, move down one list, and search that list. 3. Repeat until either the desired item is found or it is not found in list 0. So the search begins by skipping rapidly down the list using the highest-level, sparse linked list, then zeroes in on the desired item by moving down to denser and denser linked lists. Here is an example of a small skip list containing some short character strings in lexical order. This list has a maximum of 8 levels: maxlevels nlevels 1 0. heads "a" "c" "d" "d" "f". terminator This picture shows five strings, "a", "c", two copies of "d", and "f". The field names refer to members of the SkipList object. The item labeled. heads contains the heads of all the lists. Each list terminates with a pointer to the item labeled. terminator. Field. maxlevels is the maximum number of linked lists (8 in the figure), and. nlevels is the number of lists that are currently in use Skip list algorithms Now that we've seen the static structure of a skip list, let's move on to the basic operations: inserting a new child, finding a child, and deleting a child The insertion algorithm Here is a figure showing the insertion of a new element into a skip list. 6
7 . maxlevels. nlevels Links to be updated heads "a" "c" "d" "d" "f" child "k" "g" "o" "r" previtem nextitem. terminator insertion cut list The dashed lines show links that need to be updated The insertion cut list In the figure above, note the structure labeled insertion-cut-list is a list of pointers to the predecessors of the new element at each level. The first element of this list points to the item preceding the insertion in the level-0 list, which visits every element of the list. The second element in the insertion cut list points to the predecessor in the level-1 list, and so on. This insertion cut list shows the position of a child element relative to each of the stacked linked lists. It tells us which links have to be repaired to include the newly inserted child. We call it the insertion cut list to distinguish it from the search cut list; for the reasons behind this distinction, see Section 3.2.2, The search algorithm (p. 8) Picking a level count When we insert a new element, how many of the stacked linked lists should contain the new element? Pugh recommended using a random process based on a probability value p, where the number of levels used for each new element is given by this table: p p(1-p) 7
8 3... n p 2 (1-p)... p n-1 (1-p) For example, for p=0.25, an element has a 3 in 4 probability of being linked into only level 0, a 3/16 probability of being in levels 0 and 1, a 3/64 probability of being in levels 0 through 2, and so forth. The algorithm for deciding the number of levels is straightforward. For p=0.25, it's like throwing a 4- sided die. If it comes up 1, 2, or 3, use one level. If it comes up 4, roll again. If the second roll comes up 1, 2, or 3, use two levels. If it comes up 4, roll again, and so forth. We will incorporate the dirty hack described in Pugh's article, limiting the growth in the total number of levels in use to one per insertion. This prevents the pathological case where a series of bad rolls might create many new levels at once for only a single insertion. Otherwise we might get silly behavior like a 6-level skip list to store ten elements The search algorithm The process of searching for a given key value is nearly identical to the process of building the insert cut list (see Section , The insertion cut list (p. 7)). The only difference between the search cut list and the insert cut list is in the case where we have elements with duplicate keys (that is, when the constructor was called with allowdups=1). If we're inserting a new child whose key is a duplicate of a key already in the list, we want the new child to go after any duplicates. This is required for stability; see Section 2.1.3, Stability (p. 3). However, if we're searching for an element (or deleting an element see Section 3.2.3, Deleting a child (p. 8)), we want to position before any duplicate elements Deleting a child Deletion of a child starts by finding the search cut list. For a discussion of the search cut list, see Section 3.2.2, The search algorithm (p. 8). The search cut list contains all the predecessors whose links need to be updated when a child element is deleted. To delete a child, we work through the search cut list, repairing each predecessor link to point to the successor of the deleted child Iterators: toward a more Pythonic class Starting with version 2.2, the Python language now supports a powerful new concept called the iterator. An iterator is just an object that keeps track of a position within some sequence. The only requirement is that it have a.next() method that, when called: If there are any elements left in the sequence, the method returns the next element in the sequence, and the iterator also advances its own internal state to point to the following item, if there is one. When the sequence is exhausted, the method raises the StopIteration exception to signal that no more elements remain. Some container classes return themselves as the result of the. iter () special method, so that the required.next() method uses some internal state stored inside the container class instance. However, the drawback to this approach is that you can't have two or more iterators pointing at different places in the sequence at the same time. 8
9 When we want to return a pointer at a position within a SkipList object, we can do so by pointing at a _SkipItem object. When we need to advance through the rest of the sequence, we can just follow the forward links in that object. This means there can exist two or more iterators walking through a skip list at the same time Classes in this module When considering the class structure of the application, two classes are obvious: The user sees only the SkipList class, a classic container object. It makes sense to create a class to hold all the information for one node of the skip list data structure. This class is called _SkipItem; the single underbar in its name signifies that it is private to the module. However, we need a third class to represent the iterators that are returned by the.find() method in the SkipList class, and by that class's special. iter () method. So we define a third class, _SkipListIterator, that defines a.next() method. This object keeps track of its position in the list by pointing to a _SkipItem. How does this iterator know when it has reached the end of the skip list? It can't compare the _SkipItem to. terminator, which is private to the SkipList class. Even if we made that attribute public, a _SkipItem object doesn't contain a reference to its containing SkipList. The solution is to stipulate that the. terminator object's forward links all point to itself. That way, a _SkipListIterator instance can detect when it is pointing at the terminator. There is one more complication with this technique. What happens if someone has a copy of an iterator that points to a _SkipItem that has been deleted from its containing SkipList? In order that the _SkipItem object correctly detects this erroneous situation, it must know when it represents a deleted element. Therefore, we stipulate that in a _SkipItem instance the level-0 forward link must be set to None when it is deleted. That way, the.next() method will know to signal an error and not attempt to chase its forward link. 4. The source code The code for follows in literate programming style. The author is indebted to his colleague Dr. Allan M. Stavely for the concept of lightweight literate programming 3. Development used the Cleanroom 4 software development methodology Declarations We'll start with a rudimentary documentation string for the module, pointing back to the online documentation: : A container class for ordered sets in Python
10 Do not edit this file. It is extracted automatically from the documentation: Next we'll need to import one of the Python standard modules, the random number generator: import random 4.2. Verification functions The Cleanroom methodology relies on verification functions to describe notational shorthand. Here are all the verification functions used in this project, in alphabetical order. All verification function names contain a hyphen, so you can distinguish them from program objects children-are-ordered #================================================================ # Verification functions # # children-are-ordered ( x, y ) == # if self.allowdups -> # cmp ( key-of ( x ), key-of ( y ) ) <= 0 # else -> # cmp ( key-of ( x ), key-of ( y ) ) < 0 #-- # Similar to the keys-are-ordered predicate, but operates on # child objects insertion-cut-list # # insertion-cut-list ( key ) == # a list L of size self. maxlevels, such that each element # L[i] equals insertion-point ( i, key ) #-- # An ``insertion cut list'' is a list of the _SkipItem objects # that must be updated when a new elements is inserted. Element # [i] of the cut list points to the element whose forward # pointer has to be repaired in the (i)th list insertion-point # # insertion-point ( level, key ) == # the last element E in nth-list ( self. heads, level ) such # that insertion-precedes ( E, key ) is true #-- 10
11 # This describes the predecessor _SkipItem whose forward link # must be updated when a new item with key (key) is inserted insertion-point-after # # insertion-point-after ( level, key, searchitem ) == # the last element E in nth-list(searchitem,level) such that # insertion-precedes(e, key) is true #-- # Just like insertion-point(), except that it starts at an # arbitrary _SkipItem instead of self. heads insertion-precedes # # insertion-precedes ( skipitem, key ) == # if skipitem is self. terminator -> F # else if skipitem is self. heads -> T # else -> # keys-are-ordered ( key-of ( skipitem's child ), key ) #-- # This predicate is true when (skipitem) should be before # an item with key (key) in the level-0 list key-of # # key-of ( child ) == # if not self.keyfunction -> # child # else -> # self.keyfunction ( child ) keys-are-ordered # # keys-are-ordered ( x, y ) == # if self.allowdups -> # cmp ( x, y ) <= 0 # else -> # cmp ( x, y ) < 0 #-- # This is the ordering relation used in the key domain. # - If we don't allow duplicates, then each key must be # strictly less than its successor. # - If we allow duplicates, then two successive keys can # be equal. 11
12 nth-list # # nth-list ( root, n ) == # the linked list of _SkipItem objects rooted at (root) and # using _SkipItem.links[n] as the next-item method #-- # This define is used to describe positions in the linked list # of objects at one level of the structure. In particular, # nth-list ( self. heads, n ) # describes the entire skip list at level (n) search-cut-list # # search-cut-list ( key ) == # a list L of size self. maxlevels such that # L[i] := search-point ( i, key ) #-- # Like insert-cut-list(), but used for.delete() and.find() # operations search-point # # search-point ( level, key ) == # the last _SkipItem E in nth-list(self. heads, level) such that # search-precedes(e, key) is true #-- # The predecessor whose forward link must be updated when # the item with key (key) is deleted. Also used in #.find() search-point-after # # search-point-after ( level, key, searchitem ) == # the last element E in nth-list(searchitem, level) such that # search-precedes(e, key) is true #-- # Like search-point except that the search starts at some # given item rather than at self. heads search-precedes # # search-precedes ( skipitem, key ) == 12
13 # if skip-compare ( skipitem, key ) < 0 -> true # else -> false #-- # A predicate, true when the child in skipitem is before the # key (key) skip-compare # # skip-compare ( skipitem, key ) == # if skipitem is self. terminator -> 1 # else -> cmp ( key-of ( skipitem's child ), key ) #-- # Like cmp(), but we want to avoid trying to extract a key # from self. terminator, because it doesn't have one. If # skipitem is the terminator, we return 1 because the terminator # goes after all other elements. # The _SkipItem internal class Before we discuss the main SkipList class, we need a helper object to hold the structural bookkeeping information associated with one element of the skip list. This helper object doesn't have much to it: Its.child attribute is a pointer to the child object at that position in the skip list. Its.links attribute is a list containing the one or more forward links connecting this element of the skip list to the following element (or pointing to the. terminator if this element is the last one). Here, then, is the _SkipItem class: # c l a s s _ S k i p I t e m class _SkipItem: Represents one child element of a SkipList. Exports: _SkipItem ( child, nlevels ): [ (child is a child object) and (nlevels is an integer >= 1) -> return a new _SkipItem with that child object and (nlevels) forward links, each set to None ].child: [ as passed to constructor, read-only ].links: [ if self is an active SkipList element -> a list of nlevels elements, read-write, containing pointers to a _SkipItem instance else -> a list of at least 1 whose first element is None ] 13
14 slots = ('child', 'links') # _ S k i p I t e m. i n i t def init ( self, child, nlevels ): Constructor for _SkipItem self.child = child self.links = [None]*nLevels The final line uses the Python convention that an expression of the form [x]*n, where n is an integer, gives you a list containing n copies of x The _SkipListIterator class The _SkipListIterator class is used to represent an iterator that walks down a skip list. It has one piece of internal state: a pointer to the _SkipItem instance that marks the next item, or to the terminator instance if there are no more items. It has only three methods, a constructor and the. iter () and.next() methods required for Python iterators. The. iter () method just returns the instance; without this method, you can't use this object in constructs like for k in.... # c l a s s _ S k i p L i s t I t e r a t o r class _SkipListIterator: Represents an active iterator over a SkipList object. Exports: _SkipListIterator ( skipitem ): [ skipitem is a _SkipItem -> return a new iterator whose next item is skipitem, or which is at end of list if skipitem's forward link points to itself ].skipitem: [ if self is exhausted -> a terminator _SkipItem else -> a _SkipItem containing the value that will be returned next time ]. iter (self): [ returns self ].next(): [ if self.skipitem's level-0 link is None -> raise ValueError else if self.skipitem.links[0] == self.skipitem -> raise StopIteration else -> self.skipitem := self.skipitem.links[0] return self.skipitem.child ] # *Before* advancing! # _ S k i p L i s t I t e r a t o r. i n i t
15 def init ( self, skipitem ): Constructor for _SkipListIterator self.skipitem = skipitem # _ S k i p L i s t I t e r a t o r. n e x t def next ( self ): Return the next child item and advance if self.skipitem.links[0] is None: raise ValueError, "Iterator points to a deleted item." elif self.skipitem.links[0] is self.skipitem: raise StopIteration else: result = self.skipitem.child self.skipitem = self.skipitem.links[0] return result In order for a _SkipListIterator object to qualify as an iterator, and be useable in contexts such as for k in S, the object must have a special. iter () method that returns the iterator. # _ S k i p L i s t I t e r a t o r. i t e r def iter ( self ): Returns the iterator itself. return self 4.5. The SkipList class Now we turn to the actual SkipList class. First, the external interface with intended functions: # c l a s s S k i p L i s t class SkipList: Container class for ordered sets. Exports: SkipList ( keyfun=none, cmpfun=none, allowdups=0, count= ): [ (keyfun is a function that returns the key for a given child object) and (cmpfun is a function that compares two keys, using the usual Python convention for the cmp() function, or None use the built-in cmp() function) and (allowdups is 0 to refuse duplicate entries or 1 to allow them) and (count is a worst-case maximum element count) -> return a new, empty SkipList instance with those properties ].insert(c): [ c is a child object -> if ((self contains an object whose key equals the key of e) and (self.allowdups is false)) -> 15
16 raise KeyError else -> self := self with e added as a new child object ].delete(k): [ k is in the key domain -> if self contains any child objects with keys equal to k -> self := self with the first-inserted such child object deleted return that child object else -> return None ].match(k): [ k is in the key domain -> if self contains any child objects whose keys equal k -> return the first such child object else -> raise KeyError ].find(k): [ k is in the key domain -> if self contains any child objects whose keys are >= k -> return an iterator that will iterate over all child objects whose keys are >= k, in order ]. len (self): [ return the number of child objects in self ]. iter (self): [ return an iterator that will iterate over all the child objects in self in order ]. contains (self, k): [ if self contains any child objects whose keys equal k -> return 1 else -> return 0 ]. delitem (self, k): [ same as self.delete(k) ]. getitem (self, k): [ same as self.match(k) ].nsearches: [INVARIANT: Number of searches performed ].ncompares: [INVARIANT: Number of child pairs compared ] Next, we declare a manifest constant for the Pugh's p: #-- # Manifest constants #-- NEW_LEVEL_PROBABILITY = 0.25 # The `p' of Pugh's article Next, we need to declare the class's internal state and invariants. #================================================================ # State and invariants # #.keyfun: [ as passed to constructor, read-only ] #.cmpfun: [ as passed to constructor, read-only ] #.allowdups: [ as passed to constructor, read-only ] #. maxlevels: # [ 1 + ceiling ( log (base 4) of count argument to constructor ) ] 16
17 #. nlevels: # [ number of levels currently in use ] # INVARIANT: 1 <= self. nlevels <= self. maxlevels ] #. heads: # [ list of head pointers to each level ] # INVARIANT: self. heads.links[0] is the head of a linked list # of _SkipItem objects, terminated by a link to self. terminator, # and whose members contain all the child objects currently in # self, such that for any two adjacent objects (Xi, Xj), # children-are-ordered ( Xi, Xj ) is true. # # INVARIANT: For i in [0,self. maxlevels), the list rooted in # heads.links[i] is a linked list of _SkipItem objects, # terminated by a link to self.terminator, and containing # a subset of the linked list in heads.links[i-1] in the same order. #. terminator: [ terminator for all linked lists ] # INVARIANT: self.terminator is a _SkipItem object with # self. maxlevels forward links all pointing to itself #. nitems: [ INVARIANT: number of child items in self ] # SkipList. init () Here's the SkipList constructor. First we make local copies of all the constructor arguments except count, and set up the invariants for several internal state values: # S k i p L i s t. i n i t def init ( self, keyfun=none, cmpfun=none, allowdups=0, count= ): Constructor for SkipList # self.keyfun = keyfun self.cmpfun = cmpfun self.allowdups = allowdups self.nsearches = 0 self.ncompares = 0 self. nlevels = 1 self. nitems = 0 Then we compute self. maxlevels based on the count argument. Based on our p value of 0.25, we'd expect to need two levels when we have about four elements, three levels when we have sixteen, and so forth. To be generous, we'll use one plus the ceiling of the log (base 4) of count. See Section 4.7, SkipList. estimatelevels() (p. 18). # # [ self. maxlevels := (number of bits required to # represent count, rounded to the next even number)/2+1 ] self. maxlevels = self. estimatelevels ( count ) 17
18 Next we have to set up the. heads and. terminator elements. They are both special instances of the _SkipItem class; in the former, all links point to the latter, and in the latter, all links point to itself. # # [ self. terminator := a new _SkipItem object whose # links all point to itself # self. heads := a new _SkipItem object whose links # all point to that new terminator _SkipItem object ] self. terminator = _SkipItem ( None, self. maxlevels ) self. heads = _SkipItem ( None, self. maxlevels ) for i in xrange ( self. maxlevels ): self. terminator.links[i] = self. terminator self. heads.links[i] = self. terminator 4.7. SkipList. estimatelevels() Here's the. estimatelevels() function used in the constructor: # S k i p L i s t. e s t i m a t e L e v e l s def estimatelevels ( self, n ): Estimate how many levels of list we need for n child objects. [ n is a positive integer -> return ceiling(log base 4(n)) + 1 ] # result = 1 # # [ result +:= k, where k is the the number of bits required # to represent n, rounded to the next higher even number ] while n > 0: result += 1 n >>= 2 # return result In the while loop, we count the number of times we have to shift n to the right two bits before it goes to zero, effectively computing the ceiling of the log base 4 of n. This gives us a result of 1 for n=0, 2 for n in the range [1,4), 3 for n in the range [4,16), 4 for n in the range [16,256), and so on SkipList.insert() The insertion of a new child element is depicted above; see Section 3.1, The skip list data structure (p. 5). # S k i p L i s t. i n s e r t
19 def insert ( self, child ): Insert a new child element into the skip list. To link in a new child element, once we have decided how many levels of links it will use, we must insert it into each of those linked lists. So we define the idea of an insertion cut list: a list of all the predecessors whose forward links must be updated. The main complication here is that we must implement stability, that is, objects with equal keys must be ordered by insertion time. For example, if we have five objects all with a key value of 5, the first one must be the first inserted, and the last one must be the last inserted. So, when computing the insertion cut list, we must find the point in the child sequence after the last child with the same key. Refer to Section 4.2, Verification functions (p. 10) for the verification functions used here, and also see Section 4.9, SkipList. keyof() (p. 20). # # [ key := key-of ( child ) ] key = self. keyof ( child ) The next step is to build the insertion cut list. If there any elements with the same key as the new element, the insertion cut list depends on whether duplicates are allowed or not. We call the point of insertion the cut point, and the cut list is the list of links that cross the cut point. If duplicates aren't allowed, the cut point is positioned before any duplicate elements, so that when we check for duplicates, we check the new key against the successor of the cut point. But if duplicates are allowed, due to the stability constraint, the cut point should be after any duplicates. See Section 4.8, SkipList.insert() (p. 18) for a discussion of stability. # # [ cutlist := insertion-cut-list ( key ) ] cutlist = self. insertcutlist ( key ) # # [ previtem := first item from cutlist # nextitem := successor at level 0 of first item from # cutlist ] previtem = cutlist[0] nextitem = previtem.links[0] Now that we have nextitem pointing just after the cut point, we can check for duplicates when they're not allowed. See Section 4.13, SkipList. compareitemkey() (p. 23). # # [ if (not self.allowdups) and # (nextitem is not self.terminator) and # (cmp(key-of(nextitem.child, key)) == 0 ) -> # raise ValueError # else -> I ] if ( ( not self.allowdups ) and ( nextitem is not self. terminator ) and ( self. compareitemkey ( nextitem, key ) == 0 ) ): raise ValueError Having eliminated the only error case, now we can proceed with the actual insertion of the new element. See Section 4.14, SkipList. insertitem() (p. 24). 19
20 # # [ if cutlist is insertion-cut-list ( key ) -> # self := self with a new _SkipItem containing (child) # inserted after the items pointed at by the # the first n elements of cutlist, where # n is in [1,self. maxlevels] ] self. insertitem ( child, cutlist ) The next step maintains the invariant on. nitems, that is, we keep track of the number of items in the list. # self. nitems = self. nitems SkipList. keyof() This function finds the child's key. If there is no key function, use the child itself as the key. # S k i p L i s t. k e y O f def keyof ( self, child ): Return the child's key. [ child is in the child domain of self -> return key-of ( child ) ] if self.keyfun: return self.keyfun ( child ) else: return child SkipList. insertcutlist() This routine builds a list of the predecessors of the cut point for insertion. As Pugh's paper describes, we start searching at the highest-numbered list and proceed down to the level-0 list. # S k i p L i s t. i n s e r t C u t L i s t def insertcutlist ( self, key ): Form the insertion cut list. [ key is in self's key domain -> return insertion-cut-list(key) ] The first step is to build a list of pointers to self. heads, one for each possible level. # # [ result := a list of size self. maxlevels such that # each element is self. heads 20
21 # searchitem := self. heads ] result = [self. heads] * self. maxlevels searchitem = self. heads Next, we search all the levels currently in use from the top down. We find the cut point at each level, then for the next level we start at the predecessor of that cut point. The routine to find each cut point is Section 4.11, SkipList. insertpoint() (p. 21). ) # # [ if insertion-precedes ( searchitem, key ) -> # result := result modified so that for I in # [0,self.nLevels), result[i] is # insertion-point(i, key) # searchitem := <anything> ] for level in xrange ( self. nlevels - 1, -1, -1 ): #-- 2 body -- # [ if insertion-precedes(searchitem, key) -> # searchitem := insertion-point-after(level, # key, searchitem) # result[level] := <same as previous line> ] searchitem = self. insertpoint ( searchitem, level, key result[level] = searchitem Finally, we maintain the invariant on.nsearches, since every call to this routine counts as a search. # self.nsearches = self.nsearches + 1 # Count as a search return result SkipList. insertpoint() This routine searches down one level's linked list until it reaches the point where a new key is to be inserted. # S k i p L i s t. i n s e r t P o i n t def insertpoint ( self, searchitem, level, key ): Find the insertion point at a given level. [ insertion-precedes(searchitem, key) -> return insertion-point-after ( level, key, searchitem) ] First we find the predecessor and successor of the starting point. # # [ previtem := searchitem # nextitem := successor of searchitem in (level)th list ] previtem = searchitem nextitem = searchitem.links[level] Next, we move down the n th -level list until we reach the point where the insertion will go. 21
22 # # [ if nextitem is not self. heads -> # if not insertion-precedes(nextitem, key) -> I # else -> # previtem := last item E at or after nextitem # in the (level)th list such that # insertion-precedes(e, key) is true # nextitem := <anything> ] while self. insertionprecedes ( nextitem, key ): #-- 2 body -- # [ previtem := nextitem # nextitem := the succesor of nextitem in the # (level)th list ] previtem = nextitem nextitem = nextitem.links[level] Finally, we return the predecessor to that location. # return previtem SkipList. insertionprecedes() This routine tests whether a given _SkipItem is before a given key, assuming that we're performing an insertion of a child with that key value. # S k i p L i s t. i n s e r t i o n P r e c e d e s def insertionprecedes ( self, skipitem, key ): Does this _SkipItem precede this key for insertion? [ skipitem is not self. heads -> return keys-are-ordered ( key-of ( skipitem's child ), key ) ] The terminator never precedes anything: # if skipitem is self. terminator: return 0 Next, we compare the key of the _SkipItem to the new key. See Section 4.13, SkipList. compareitemkey() (p. 23). # # [ comparison := cmp ( key-of ( skipitem's child ), key ) ] comparison = self. compareitemkey ( skipitem, key ) 22
23 Whether we want to place the new key before or after duplicates depends on whether the skip list allows duplicates. # # # Note: if duplicates are disallowed, and there is an item that # duplicates (target), we want to point before the duplicate # item so that the.insert() method will see it and fail. # If duplicates are allowed, though, we want to point after # all matching items so that the list order will reflect # the insertion order. # # [ if self.allowdups -> # if comparison <= 0 -> return 1 # else -> return 0 # else -> # if comparison < 0 -> return 1 # else -> return 0 ] if self.allowdups: return ( comparison <= 0 ) else: return ( comparison < 0 ) SkipList. compareitemkey() This small routine compares two keys, one from a _SkipItem and one in the key domain. # S k i p L i s t. c o m p a r e I t e m K e y def compareitemkey ( self, skipitem, keyb ): Compare the key from a _SkipItem to a key in the key domain. [ return cmp ( key-of ( skipitem's child ), keyb ) ] First we get the key from skipitem; see Section 4.9, SkipList. keyof() (p. 20). # # [ keya := key-of ( skipitem's child ) ] keya = self. keyof ( skipitem.child ) We increment self.ncompares, the count of the number of comparison operations. Then we use Python's built-in cmp() function to do the comparison, returning its result as our result. # self.ncompares = self.ncompares + 1 return cmp ( keya, keyb ) 23
24 4.14. SkipList. insertitem() The responsibility of this method is to build a new _SkipItem to hold the newly inserted child, and then link it into one or more of the linked lists. # S k i p L i s t. i n s e r t I t e m def insertitem ( self, child, cutlist ): [ cutlist is insertion-cut-list(key-of(child)) -> self := self with a new _SkipItem, with child=(child), inserted after the items pointed at by the first n levels of cutlist, where n is in the range [1,self. maxlevels] ] First, using the random number generator, we decide into how many levels we should link the new item. If number of levels picked exceeds the current number of levels, we must adjust the value of self. nlevels to maintain the invariant that this variable tracks the maximum number of levels actually in use so far. See Section 4.15, SkipList. picklevel() (p. 25). # # [ levels := a random integer in [1,self. maxlevels] # self. nlevels := max ( self. nlevels, # that random integer ) ] levels = self. picklevel ( ) Then we construct the actual _SkipItem containing the new child. See Section 4.3, The _SkipItem internal class (p. 13). # # [ newitem := a new _SkipItem with child=(child) and # (levels) forward links all set to None ] newitem = _SkipItem ( child, levels ) Finally, the. insertrelink() method takes care of linking the item into the correct number of linked lists; see Section 4.16, SkipList. insertrelink() (p. 25). # # [ (cutlist is insertion-cut-list(key-of(child))) and # (newitem is a _SkipItem with at least (levels) links) -> # self := self with newitem linked into the first (levels) # lists, just after the element pointed at by the # corresponding element of cutlist ] self. insertrelink ( levels, cutlist, newitem ) 24
25 4.15. SkipList. picklevel() This method implements the algorithm for probabilistically deciding how many levels to use for linking in a new skip list entry. For a discussion of this algorithm, see Section 3.1, The skip list data structure (p. 5). # S k i p L i s t. p i c k L e v e l def picklevel ( self ): Into how many levels should an insertion be linked? [ self. nlevels := max ( self. nlevels, a randomly chosen integer in [1,self. maxlevels] ) return that same integer ] Here is what Pugh calls the dirty hack: we will never add more than one level to a skip list per insertion. For the details, see Section 3.1, The skip list data structure (p. 5). # result = 1 maxnewlevel = min ( self. nlevels + 1, self. maxlevels ) Roll the dice, figuratively. The random.random() function returns a real in the interval [0,1), and NEW_LEVEL_PROBABILITY is what Pugh calls p. The process is limited by the previously computed maxnewlevel. For details on the random module, see the Python library reference 5 under Miscellaneous Services. # # [ maxnewlevel >= result -> # result := a randomly chosen integer in the range # [result,maxnewlevel] ] while ( ( random.random() <= self.new_level_probability ) and ( result < maxnewlevel ) ): result = result + 1 Maintain the invariant on self. nlevels (a running max of the number of levels ever used in the skip list), and return the generated result. # self. nlevels = max ( self. nlevels, result ) # return result SkipList. insertrelink() This routine takes care of repairing all the linked lists that must contain the newly created _SkipItem. In general, insertion into a linked lists has this form, where P is the predecessor, S is the successor, and N is the new block:
26 N.link = S P.link = N So all we need to do is execute this relinking operation once for each level that contains the new _SkipItem. See also the diagram of this relinking in Section 3.1, The skip list data structure (p. 5). # S k i p L i s t. i n s e r t R e l i n k def insertrelink ( self, levels, cutlist, newitem ): Insert the new _SkipItem into all its linked lists. [ (cutlist is insertion-cut-list(key-of(child))) and (newitem is a _SkipItem with child=(child) and at least (levels) links) -> self := self with newitem linked into the first (levels) lists, just after the element pointed at by the corresponding element of cutlist ] # for i in xrange(levels): #-- 1 loop -- # [ i is an integer in [0,levels) -> # newitem.links[i] := cutlist[i].links[i] # cutlist[i].links[i] := newitem ] # # [ i is an integer in [0,levels) -> # previtem := the item pointed at by cutlist[i] # succitem := the (i)th link from the item # pointed at by cutlist[i] ] previtem = cutlist[i] succitem = previtem.links[i] # # [ i is an integer in [0,levels) -> # newitem := newitem with its (i)th link # pointing to succitem # previtem := previtem with its (i)th link # pointing to newitem ] newitem.links[i] = succitem previtem.links[i] = newitem SkipList.delete() Deletion, like insertion, also uses the idea of a cut list, that is, a list of all the predecessors whose links must be repaired to leave out the deleted item. However, deletion uses a slightly different concept of the cut list, the search cut list. The only difference comes when the skip list allows duplicate keys. When we're inserting a child with a duplicate key, we want it to go after the other children with that key, to guarantee stability. 26
27 For deletion and searching, however, we want the cut list to point before the first of any values whose keys are equal to the key we're deleting or searching for. # S k i p L i s t. d e l e t e def delete ( self, key ): Delete the first or only child with a given key value. First we find the search cut list for the given key. We also set local variables previtem and nextitem to point to the predecessor and successor of the cut point at level 0. See Section 4.18, SkipList. searchcutlist() (p. 28). # # [ cutlist := search-cut-list ( key ) # previtem := first element of search-cut-list ( key ) # nextitem := successor to first element of # search-cut-list ( key ) ] cutlist = self. searchcutlist ( key ) previtem = cutlist[0] nextitem = previtem.links[0] If the cut point is before the terminator, or the element after the cut point does not have the key value we're looking for, we're done, and we return None to signify failure to delete anything. See Section 4.13, SkipList. compareitemkey() (p. 23). # # [ if (nextitem is self. terminator) or # ( cmp ( key-of ( nextitem's child ), key ) ) > 0 ) -> # return None # else -> I ] if ( ( nextitem is self. terminator ) or ( self. compareitemkey ( nextitem, key ) > 0 ) ): return None We've found the item to be deleted, and nextitem points to it. Relink all the lists that point at that item so that the predecessor at that level in the cut list points at nextitem's successor at that level. # # [ self := self modified so that for all I in # [0,self. nlevels), if the skipitem pointed to by # cutlist[i] has an (I)th link that points to # nextitem, make that link point where nextitem's # (I)th link points ] for i in xrange(self. nlevels): #-- 3 body -- # [ if the _SkipItem pointed to by cutlist[i] has an # (i)th link that points to nextitem -> # that link is made to point where nextitem's (i)th # link points ] previtem = cutlist[i] if previtem.links[i] is nextitem: previtem.links[i] = nextitem.links[i] 27
28 Finally, we must adjust. nitems to reflect the deletion of one child, and return the deleted child value. We must also set the level-0 link in the deleted _SkipItem to None, for reasons discussed in Section 3.4, Classes in this module (p. 9). # self. nitems = self. nitems - 1 nextitem.links[0] = None return nextitem.child SkipList. searchcutlist() This method returns the "search cut list", a list of the predecessors of a given key at each level of the skip list. # S k i p L i s t. s e a r c h C u t L i s t def searchcutlist ( self, key ): Find predecessors of the item with a given key. [ key is in self's key domain -> return search-cut-list ( key ) ] We start by building a list called result containing pointers to the head element, and also point searchitem at the head element. # # [ result := a list of size self. maxlevels such that # each element is self. heads # searchitem := self. heads ] result = [self. heads] * self. maxlevels searchitem = self. heads This loop is similar to the one in Section 4.10, SkipList. insertcutlist() (p. 20). It starts at the highest level currently in use and searches forward to find the predecessor at that level. Then it backs up and goes down a level until it reaches level 0. See Section 4.19, SkipList. search- Point() (p. 29). # # [ if search-precedes ( searchitem, key ) -> # result := result modified so that for I in # [0,self. nlevels), # result[i] := search-point(i, key) # searchitem := <anything> ] for i in xrange ( self. nlevels-1, -1, -1 ): #-- 2 body -- # [ if search-precedes ( searchitem, key ) -> # result[i] := search-point-after(i, key, searchitem) # searchitem := <same as previous line> ] 28
29 searchitem = self. searchpoint ( searchitem, i, key ) result[i] = searchitem Finally, we increment the total count of searches and return the resulting cut-list. # self.nsearches = self.nsearches + 1 return result SkipList. searchpoint() This routine locates the predecessor of the first item after a given point that is before the given key value. # S k i p L i s t. s e a r c h P o i n t def searchpoint ( self, searchitem, level, key ): Search one level of the skip list for a given key. [ ( level is in [0,self. maxlevels ) ) and ( search-precedes ( searchitem, key ) ) -> return search-point-after ( level, key, searchitem ) ] First we set the local variables previtem and nextitem to the item where we start, and its successor, respectively. # # [ previtem := searchitem # nextitem := successor of searchitem in (level)th list ] previtem = searchitem nextitem = searchitem.links[level] Then we move both these variables down the linked list at the given level so long as nextitem still precedes the key we're searching for. See Section 4.20, SkipList. searchprecedes() (p. 30). # # [ if nextitem is not self. heads -> # if search-precedes ( nextitem, key ) -> # previtem := last item E in nth-list(nextitem, level) # such that search-precedes(e, key) is true # nextitem := <anything> # else -> I ] while self. searchprecedes ( nextitem, key ): previtem = nextitem nextitem = nextitem.links[level] Finally, we return the predecessor item. # return previtem 29
30 4.20. SkipList. searchprecedes() This is a predicate used to test whether a given _SkipItem precedes the item with a given key. # S k i p L i s t. s e a r c h P r e c e d e s def searchprecedes ( self, skipitem, key ): Does this item precede the item with a given key? [ ( skipitem is a _SkipItem ) and ( key is in self's key domain) -> if search-precedes ( skipitem, key ) -> return 1 else -> return 0 ] Eliminate the case where skipitem is the terminator, because that item never precedes anything. Then use the. compareitemkey() method to do the actual comparison. # if skipitem is self. terminator: return 0 # # [ if cmp ( key-of(skipitem's child), key ) < 0 -> # return 1 # else -> # return 0 ] if self. compareitemkey ( skipitem, key ) < 0: return 1 else: return SkipList.match() The.match() method is used to search for a specific, matching item. If there are multiple matching items, by definition it returns the first. This means we use the search cut list to locate the matching item, rather than the insert cut list. See Section 3.2.2, The search algorithm (p. 8). # S k i p L i s t. m a t c h def match ( self, key ): Return the first or only child with the given key. If there is a matching item, the level-0 element of the search cut list will precede it; see Section 4.2.9, search-cut-list (p. 12). So we could call the. searchcutlist() method and use element [0] of the returned list. However, for efficiency reasons, there is a simplified version of. searchcutlist() that doesn't build up the entire cut list, but instead just finds the level-0 predecessor: see Section 4.22, SkipList. searchcutitem (p. 31). 30
31 # # [ searchitem := successor of search-point ( 0, key ) ] previtem = self. searchcutitem(key) searchitem = previtem.links[0] If the user is looking for a child that isn't there, searchitem will point either at the terminator, or at a _SkipItem with a different key. In either of those cases, raise the KeyError exception. Otherwise, return the child item. See Section 4.13, SkipList. compareitemkey() (p. 23). # # [ (searchitem is a _SkipItem in self) -> # if (searchitem is not self. terminator) and # ( cmp ( key-of (searchitem's child), key ) ) == 0 ) -> # return searchitem's child # else -> # raise KeyError ] if ( ( searchitem is not self. terminator ) and ( self. compareitemkey ( searchitem, key ) == 0 ) ): return searchitem.child else: raise KeyError, "Key not found: %s" % key SkipList. searchcutitem The logic here is basically the same as in. searchcutlist(), but the caller only needs to know the level-0 element of the search cut list. So, to avoid a lot of extra storage allocator calls, we don't build the entire search cut list. For more detailed commentary, see Section 4.18, SkipList. searchcut- List() (p. 28). # S k i p L i s t. s e a r c h C u t I t e m def searchcutitem ( self, key ): Find the level-0 predecessor of the given key. [ key is in self's key domain -> return search-point(0, key).link[0] ] # # [ searchitem := self. heads ] searchitem = self. heads # # [ if search-precedes ( searchitem, key ) -> # searchitem := search-point ( 0, key ) ] for i in xrange ( self. nlevels-1, -1, -1 ): #-- 2 body -- # [ if search-precedes ( searchitem, key ) -> # searchitem := search-point-after(i, key, searchitem) ] searchitem = self. searchpoint ( searchitem, i, key ) 31
32 # self.nsearches = self.nsearches + 1 return searchitem SkipList.find() Unlike.match(), the.find() method returns not a child but a generator that visits all children at or after a given key value. # S k i p L i s t. f i n d def find ( self, key ): Return an iterator starting at a given position As with the.match() method (see Section 4.23, SkipList.find() (p. 32)), the first step is to find the level-0 predecessor of the desired position. See Section 4.22, SkipList. searchcutitem (p. 31). # # [ searchitem := search-point ( 0, key ).links[0] ] previtem = self. searchcutitem ( key ) searchitem = previtem.links[0] At this point, all we need to do is to pass the selected _SkipItem to the _SkipListIterator constructor, and return the new iterator to the caller. See Section 4.4, The _SkipListIterator class (p. 14). # return _SkipListIterator ( searchitem ) SkipList. len () The special method. len () is called when the len() function is applied to a _SkipItem. It returns the value of the internal. nitems. # S k i p L i s t. l e n def len ( self ): Returns the number of child elements. return self. nitems SkipList. iter () The special. iter () method returns an iterator that walks the entire skip list in order. The logic is trivial: we pass the successor of the level-0 list head to the _SkipListIterator constructor. See Section 4.4, The _SkipListIterator class (p. 14). # S k i p L i s t. i t e r
33 def iter ( self ): Iterator for the entire list return _SkipListIterator ( self. heads.links[0] ) SkipList. contains () This special method is called when the user uses the Python in or not whether a given key value matches any of the child objects in the skip list. # S k i p L i s t. c o n t a i n s def contains ( self, key ): Does self contain the given key? in operators to test First we call the.match() method to tell us whether there's a matching element. Then we convert the return values of that method to a Boolean. # try: child = self.match ( key ) return 1 except KeyError: return SkipList. delitem () The. delitem () method is another special method that is called when the user applies the Python del statement to one element of a skip list. It's really the same thing as the.delete() method, except the caller doesn't want the deleted item. In the case where there are multiple children with the same key, it's not obvious whether a user would expect to delete all of them. We arbitrary say it will delete only one. If one wishes to delete them all, one can always write a loop that calls.delete() until it returns a None to signify that no elements were deleted. # S k i p L i s t. d e l i t e m def delitem ( self, key ): Delete the first or only item with a given key. self.delete ( key ) SkipList. getitem () This special method name is called when a user retrieves an element from a skiplist using the syntax s[k]. Note that a KeyError exception will be raised if no child objects have a matching key. # S k i p L i s t. g e t i t e m def getitem ( self, key ): 33
34 Get the first or only item with a given key. return self.match ( key ) 5. skiptest: A small test driver Here is a small test script that exercises the module. #!/usr/bin/env python #================================================================ # skiptest: Test driver for # Do not edit this file. It is extracted automatically from # the documentation: # # skiptest import pyskip # m a i n def main(): '''Main program. ''' #-- # Test 1: Build a small list with no duplicates and print it. #-- print "=== Test 1, no duplicates ===" s=pyskip.skiplist() s.insert("foo") s.insert("bar") s.insert('klarn') s.insert('zoop') s.insert('aab') for k in s: print k, print print "=== len() test, should be 5 ===" print len(s) print "=== The list starting at 'foo':" for k in s.find("foo"): print k, print print "=== Match test, should find zoop:" print s.match("zoop") print "=== Match test, should print `Pass':" try: result = s.match("no-such") print "Fail, it found", result 34
35 except KeyError: print "Pass" print "=== Test of getitem ===" print "s['foo'] contains", s['foo'] print "=== Test of contains : should print True, then False" print ('foo' in s), ('no-such' in s) #-- # Test 2: Test stability with duplicates. #-- print "\n=== Test 2, duplicates, key function: Equal keys should" print "=== ascending serial numbers; serial #1 was deleted." s=pyskip.skiplist(allowdups=1, keyfun=serial.key) s.insert(serial("a")) s.insert(serial("x")) s.insert(serial("x")) s.insert(serial("x")) s.insert(serial("x")) s.insert(serial("x")) s.insert(serial("x")) s.insert(serial("a")) s.insert(serial("a")) s.insert(serial("a")) s.insert(serial("x")) s.delete("a") for k in s: print k, print # c l a s s S e r i a l class Serial: A class that adds a unique serial number to each instance. serialno = 0 def init ( self, child ): Constructor self.child = child Serial.serialNo += 1 self.serial = Serial.serialNo def str ( self ): String convert function return "%s [#%d]" % (self.child, self.serial) def key ( self ): Key extractor function return self.child 35
36 # E p i l o g u e if name == " main ": main() 6. Performance testing To test whether the implementation gives O(log n) performance, script bigotest generates random floating-point values, then searches for a given number of them, and accumulates statistics on the number of comparisons per search. Here is the raw data. For each n, five runs were made. The last column shows the mean of the five runs. n Samples Mean 1e e e e e Here is a plot of the mean number of comparisons as a function of the common log of n. 45 Skip list performance Number of compares per search log 10 n Here is the bigotest script. 36
37 #!/usr/bin/env python #================================================================ # bigotest: Test that pyskip has O(log n) time complexity #-- # Command line arguments: # bigotest [N] # where N is the number of floats to generate, default DEFAULT_N. # bigotest # I m p o r t s from future import print_function import sys import pyskip import random # M a n i f e s t c o n s t a n t s DEFAULT_N = 10**6 N_SAMPLES = 100 # Default number of items # How many lookups we try # m a i n def main(): Main program. [ sys.stdout +:= statistics on number of elements and number of compares per lookup on a set of HOW_MANY random floats ] #-- 1 # [ if there is one positive float argument -> # n := int(that argument) # else if there are no arguments-> # n := DEFAULT_N # else -> # sys.stderr +:= error message # stop execution ] n = checkargs() #-- 2 # [ skip := a new pyskip.skiplist instance that allows duplicates # and has no key or compare function and is optimized for at # least n elements ] skip = pyskip.skiplist(allowdups=1, count=n*4) #-- 3 # [ skip +:= HOW_MANY uniform random floats ] # samplelist := N_SAMPLES of those random floats ] for k in range(n-n_samples): skip.insert(random.random()) samplelist = [] 37
38 for k in range(n_samples): sample = random.random() samplelist.append(sample) skip.insert(sample) #-- 4 # [ sys.stdout +:= statistics on the number of compares required # to look up each element of samplelist once ] oldcompares = skip.ncompares for sample in samplelist: if sample not in skip: print("failed to find value {0:.5f}!".format(sample)) delta = skip.ncompares - oldcompares print("average of {0} random lookups out of {1} total " "elements:".format(n_samples, len(skip))) print("{0:.1f} compares per lookup.".format(float(delta)/n_samples)) # c h e c k A r g s def checkargs(): '''General command line argument processing. [ if there is one positive float argument -> return int(that argument) else if there are no arguments-> return DEFAULT_N else -> sys.stderr +:= error message stop execution ] ''' #-- 1 arglist = sys.argv[1:] #-- 2 if len(arglist) == 0: return DEFAULT_N #-- 3 try: n = int(float(arglist[0])) except ValueError: fatal("the argument must be a positive integer.") #-- 4 if n < N_SAMPLES: fatal("the argument must be at least {0}.".format(N_SAMPLES)) return n # f a t a l def fatal(*l): 38
39 '''Print a message and terminate. [ L is a list of strings -> sys.stderr +:= concatenated elements of L stop execution ] ''' print(''.join(l), file=sys.stderr) sys.exit(1) # r e p o r t def report(skip, label): '''Display accumulated statistics. ''' print("=== {0}".format(label)) print(" {0:9d} elements.".format(len(skip))) # E p i l o g u e if name == " main ": main() 39
40 40
CS 2112 Spring 2014. 0 Instructions. Assignment 3 Data Structures and Web Filtering. 0.1 Grading. 0.2 Partners. 0.3 Restrictions
CS 2112 Spring 2014 Assignment 3 Data Structures and Web Filtering Due: March 4, 2014 11:59 PM Implementing spam blacklists and web filters requires matching candidate domain names and URLs very rapidly
Symbol Tables. Introduction
Symbol Tables Introduction A compiler needs to collect and use information about the names appearing in the source program. This information is entered into a data structure called a symbol table. The
Why you shouldn't use set (and what you should use instead) Matt Austern
Why you shouldn't use set (and what you should use instead) Matt Austern Everything in the standard C++ library is there for a reason, but it isn't always obvious what that reason is. The standard isn't
Binary Heap Algorithms
CS Data Structures and Algorithms Lecture Slides Wednesday, April 5, 2009 Glenn G. Chappell Department of Computer Science University of Alaska Fairbanks [email protected] 2005 2009 Glenn G. Chappell
Regular Expressions and Automata using Haskell
Regular Expressions and Automata using Haskell Simon Thompson Computing Laboratory University of Kent at Canterbury January 2000 Contents 1 Introduction 2 2 Regular Expressions 2 3 Matching regular expressions
Analysis of Algorithms I: Binary Search Trees
Analysis of Algorithms I: Binary Search Trees Xi Chen Columbia University Hash table: A data structure that maintains a subset of keys from a universe set U = {0, 1,..., p 1} and supports all three dictionary
Data Structures Using C++ 2E. Chapter 5 Linked Lists
Data Structures Using C++ 2E Chapter 5 Linked Lists Doubly Linked Lists Traversed in either direction Typical operations Initialize the list Destroy the list Determine if list empty Search list for a given
Exercise 4 Learning Python language fundamentals
Exercise 4 Learning Python language fundamentals Work with numbers Python can be used as a powerful calculator. Practicing math calculations in Python will help you not only perform these tasks, but also
Exercise 1: Python Language Basics
Exercise 1: Python Language Basics In this exercise we will cover the basic principles of the Python language. All languages have a standard set of functionality including the ability to comment code,
Instant SQL Programming
Instant SQL Programming Joe Celko Wrox Press Ltd. INSTANT Table of Contents Introduction 1 What Can SQL Do for Me? 2 Who Should Use This Book? 2 How To Use This Book 3 What You Should Know 3 Conventions
Moving from CS 61A Scheme to CS 61B Java
Moving from CS 61A Scheme to CS 61B Java Introduction Java is an object-oriented language. This document describes some of the differences between object-oriented programming in Scheme (which we hope you
Big Data and Scripting. Part 4: Memory Hierarchies
1, Big Data and Scripting Part 4: Memory Hierarchies 2, Model and Definitions memory size: M machine words total storage (on disk) of N elements (N is very large) disk size unlimited (for our considerations)
Chapter Objectives. Chapter 9. Sequential Search. Search Algorithms. Search Algorithms. Binary Search
Chapter Objectives Chapter 9 Search Algorithms Data Structures Using C++ 1 Learn the various search algorithms Explore how to implement the sequential and binary search algorithms Discover how the sequential
1) The postfix expression for the infix expression A+B*(C+D)/F+D*E is ABCD+*F/DE*++
Answer the following 1) The postfix expression for the infix expression A+B*(C+D)/F+D*E is ABCD+*F/DE*++ 2) Which data structure is needed to convert infix notations to postfix notations? Stack 3) The
Lecture 9. Semantic Analysis Scoping and Symbol Table
Lecture 9. Semantic Analysis Scoping and Symbol Table Wei Le 2015.10 Outline Semantic analysis Scoping The Role of Symbol Table Implementing a Symbol Table Semantic Analysis Parser builds abstract syntax
A binary search tree or BST is a binary tree that is either empty or in which the data element of each node has a key, and:
Binary Search Trees 1 The general binary tree shown in the previous chapter is not terribly useful in practice. The chief use of binary trees is for providing rapid access to data (indexing, if you will)
root node level: internal node edge leaf node CS@VT Data Structures & Algorithms 2000-2009 McQuain
inary Trees 1 A binary tree is either empty, or it consists of a node called the root together with two binary trees called the left subtree and the right subtree of the root, which are disjoint from each
1. The memory address of the first element of an array is called A. floor address B. foundation addressc. first address D.
1. The memory address of the first element of an array is called A. floor address B. foundation addressc. first address D. base address 2. The memory address of fifth element of an array can be calculated
Sorting revisited. Build the binary search tree: O(n^2) Traverse the binary tree: O(n) Total: O(n^2) + O(n) = O(n^2)
Sorting revisited How did we use a binary search tree to sort an array of elements? Tree Sort Algorithm Given: An array of elements to sort 1. Build a binary search tree out of the elements 2. Traverse
Curriculum Map. Discipline: Computer Science Course: C++
Curriculum Map Discipline: Computer Science Course: C++ August/September: How can computer programs make problem solving easier and more efficient? In what order does a computer execute the lines of code
CSC 180 H1F Algorithm Runtime Analysis Lecture Notes Fall 2015
1 Introduction These notes introduce basic runtime analysis of algorithms. We would like to be able to tell if a given algorithm is time-efficient, and to be able to compare different algorithms. 2 Linear
Binary Trees and Huffman Encoding Binary Search Trees
Binary Trees and Huffman Encoding Binary Search Trees Computer Science E119 Harvard Extension School Fall 2012 David G. Sullivan, Ph.D. Motivation: Maintaining a Sorted Collection of Data A data dictionary
Persistent Data Structures
6.854 Advanced Algorithms Lecture 2: September 9, 2005 Scribes: Sommer Gentry, Eddie Kohler Lecturer: David Karger Persistent Data Structures 2.1 Introduction and motivation So far, we ve seen only ephemeral
Glossary of Object Oriented Terms
Appendix E Glossary of Object Oriented Terms abstract class: A class primarily intended to define an instance, but can not be instantiated without additional methods. abstract data type: An abstraction
Lecture 2 February 12, 2003
6.897: Advanced Data Structures Spring 003 Prof. Erik Demaine Lecture February, 003 Scribe: Jeff Lindy Overview In the last lecture we considered the successor problem for a bounded universe of size u.
File Management. Chapter 12
Chapter 12 File Management File is the basic element of most of the applications, since the input to an application, as well as its output, is usually a file. They also typically outlive the execution
each college c i C has a capacity q i - the maximum number of students it will admit
n colleges in a set C, m applicants in a set A, where m is much larger than n. each college c i C has a capacity q i - the maximum number of students it will admit each college c i has a strict order i
Persistent Binary Search Trees
Persistent Binary Search Trees Datastructures, UvA. May 30, 2008 0440949, Andreas van Cranenburgh Abstract A persistent binary tree allows access to all previous versions of the tree. This paper presents
Questions 1 through 25 are worth 2 points each. Choose one best answer for each.
Questions 1 through 25 are worth 2 points each. Choose one best answer for each. 1. For the singly linked list implementation of the queue, where are the enqueues and dequeues performed? c a. Enqueue in
Binary Search Trees. A Generic Tree. Binary Trees. Nodes in a binary search tree ( B-S-T) are of the form. P parent. Key. Satellite data L R
Binary Search Trees A Generic Tree Nodes in a binary search tree ( B-S-T) are of the form P parent Key A Satellite data L R B C D E F G H I J The B-S-T has a root node which is the only node whose parent
DNS LOOKUP SYSTEM DATA STRUCTURES AND ALGORITHMS PROJECT REPORT
DNS LOOKUP SYSTEM DATA STRUCTURES AND ALGORITHMS PROJECT REPORT By GROUP Avadhut Gurjar Mohsin Patel Shraddha Pandhe Page 1 Contents 1. Introduction... 3 2. DNS Recursive Query Mechanism:...5 2.1. Client
Efficient Data Structures for Decision Diagrams
Artificial Intelligence Laboratory Efficient Data Structures for Decision Diagrams Master Thesis Nacereddine Ouaret Professor: Supervisors: Boi Faltings Thomas Léauté Radoslaw Szymanek Contents Introduction...
Lab 2 : Basic File Server. Introduction
Lab 2 : Basic File Server Introduction In this lab, you will start your file system implementation by getting the following FUSE operations to work: CREATE/MKNOD, LOOKUP, and READDIR SETATTR, WRITE and
Binary Search Trees. Data in each node. Larger than the data in its left child Smaller than the data in its right child
Binary Search Trees Data in each node Larger than the data in its left child Smaller than the data in its right child FIGURE 11-6 Arbitrary binary tree FIGURE 11-7 Binary search tree Data Structures Using
CSC 221: Computer Programming I. Fall 2011
CSC 221: Computer Programming I Fall 2011 Python control statements operator precedence importing modules random, math conditional execution: if, if-else, if-elif-else counter-driven repetition: for conditional
The ADT Binary Search Tree
The ADT Binary Search Tree The Binary Search Tree is a particular type of binary tree that enables easy searching for specific items. Definition The ADT Binary Search Tree is a binary tree which has an
Instruction Set Architecture of Mamba, a New Virtual Machine for Python
Instruction Set Architecture of Mamba, a New Virtual Machine for Python David Pereira and John Aycock Department of Computer Science University of Calgary 2500 University Drive N.W. Calgary, Alberta, Canada
Previous Lectures. B-Trees. External storage. Two types of memory. B-trees. Main principles
B-Trees Algorithms and data structures for external memory as opposed to the main memory B-Trees Previous Lectures Height balanced binary search trees: AVL trees, red-black trees. Multiway search trees:
Binary Heaps * * * * * * * / / \ / \ / \ / \ / \ * * * * * * * * * * * / / \ / \ / / \ / \ * * * * * * * * * *
Binary Heaps A binary heap is another data structure. It implements a priority queue. Priority Queue has the following operations: isempty add (with priority) remove (highest priority) peek (at highest
Why? A central concept in Computer Science. Algorithms are ubiquitous.
Analysis of Algorithms: A Brief Introduction Why? A central concept in Computer Science. Algorithms are ubiquitous. Using the Internet (sending email, transferring files, use of search engines, online
Converting a Number from Decimal to Binary
Converting a Number from Decimal to Binary Convert nonnegative integer in decimal format (base 10) into equivalent binary number (base 2) Rightmost bit of x Remainder of x after division by two Recursive
Solutions to Homework 6
Solutions to Homework 6 Debasish Das EECS Department, Northwestern University [email protected] 1 Problem 5.24 We want to find light spanning trees with certain special properties. Given is one example
Load Balancing. Load Balancing 1 / 24
Load Balancing Backtracking, branch & bound and alpha-beta pruning: how to assign work to idle processes without much communication? Additionally for alpha-beta pruning: implementing the young-brothers-wait
Performance Tuning for the Teradata Database
Performance Tuning for the Teradata Database Matthew W Froemsdorf Teradata Partner Engineering and Technical Consulting - i - Document Changes Rev. Date Section Comment 1.0 2010-10-26 All Initial document
CS104: Data Structures and Object-Oriented Design (Fall 2013) October 24, 2013: Priority Queues Scribes: CS 104 Teaching Team
CS104: Data Structures and Object-Oriented Design (Fall 2013) October 24, 2013: Priority Queues Scribes: CS 104 Teaching Team Lecture Summary In this lecture, we learned about the ADT Priority Queue. A
Introduction to Python
Caltech/LEAD Summer 2012 Computer Science Lecture 2: July 10, 2012 Introduction to Python The Python shell Outline Python as a calculator Arithmetic expressions Operator precedence Variables and assignment
CS106A, Stanford Handout #38. Strings and Chars
CS106A, Stanford Handout #38 Fall, 2004-05 Nick Parlante Strings and Chars The char type (pronounced "car") represents a single character. A char literal value can be written in the code using single quotes
Python Loops and String Manipulation
WEEK TWO Python Loops and String Manipulation Last week, we showed you some basic Python programming and gave you some intriguing problems to solve. But it is hard to do anything really exciting until
Binary Heaps. CSE 373 Data Structures
Binary Heaps CSE Data Structures Readings Chapter Section. Binary Heaps BST implementation of a Priority Queue Worst case (degenerate tree) FindMin, DeleteMin and Insert (k) are all O(n) Best case (completely
Python Lists and Loops
WEEK THREE Python Lists and Loops You ve made it to Week 3, well done! Most programs need to keep track of a list (or collection) of things (e.g. names) at one time or another, and this week we ll show
6.02 Practice Problems: Routing
1 of 9 6.02 Practice Problems: Routing IMPORTANT: IN ADDITION TO THESE PROBLEMS, PLEASE SOLVE THE PROBLEMS AT THE END OF CHAPTERS 17 AND 18. Problem 1. Consider the following networks: network I (containing
We will learn the Python programming language. Why? Because it is easy to learn and many people write programs in Python so we can share.
LING115 Lecture Note Session #4 Python (1) 1. Introduction As we have seen in previous sessions, we can use Linux shell commands to do simple text processing. We now know, for example, how to count words.
A Comparison of Dictionary Implementations
A Comparison of Dictionary Implementations Mark P Neyer April 10, 2009 1 Introduction A common problem in computer science is the representation of a mapping between two sets. A mapping f : A B is a function
Random Fibonacci-type Sequences in Online Gambling
Random Fibonacci-type Sequences in Online Gambling Adam Biello, CJ Cacciatore, Logan Thomas Department of Mathematics CSUMS Advisor: Alfa Heryudono Department of Mathematics University of Massachusetts
Simple Document Management Using VFP, Part 1 Russell Campbell [email protected]
Seite 1 von 5 Issue Date: FoxTalk November 2000 Simple Document Management Using VFP, Part 1 Russell Campbell [email protected] Some clients seem to be under the impression that they need to
A Model-driven Approach to Predictive Non Functional Analysis of Component-based Systems
A Model-driven Approach to Predictive Non Functional Analysis of Component-based Systems Vincenzo Grassi Università di Roma Tor Vergata, Italy Raffaela Mirandola {vgrassi, mirandola}@info.uniroma2.it Abstract.
Lecture Notes on Binary Search Trees
Lecture Notes on Binary Search Trees 15-122: Principles of Imperative Computation Frank Pfenning André Platzer Lecture 17 October 23, 2014 1 Introduction In this lecture, we will continue considering associative
Appendix... B. The Object Constraint
UML 2.0 in a Nutshell Appendix B. The Object Constraint Pub Date: June 2005 Language The Object Constraint Language 2.0 (OCL) is an addition to the UML 2.0 specification that provides you with a way to
Introduction to Programming System Design. CSCI 455x (4 Units)
Introduction to Programming System Design CSCI 455x (4 Units) Description This course covers programming in Java and C++. Topics include review of basic programming concepts such as control structures,
- Easy to insert & delete in O(1) time - Don t need to estimate total memory needed. - Hard to search in less than O(n) time
Skip Lists CMSC 420 Linked Lists Benefits & Drawbacks Benefits: - Easy to insert & delete in O(1) time - Don t need to estimate total memory needed Drawbacks: - Hard to search in less than O(n) time (binary
Physical Design. Meeting the needs of the users is the gold standard against which we measure our success in creating a database.
Physical Design Physical Database Design (Defined): Process of producing a description of the implementation of the database on secondary storage; it describes the base relations, file organizations, and
CHAPTER 4 ESSENTIAL DATA STRUCTRURES
CHAPTER 4 ESSENTIAL DATA STRUCTURES 72 CHAPTER 4 ESSENTIAL DATA STRUCTRURES In every algorithm, there is a need to store data. Ranging from storing a single value in a single variable, to more complex
Fast Sequential Summation Algorithms Using Augmented Data Structures
Fast Sequential Summation Algorithms Using Augmented Data Structures Vadim Stadnik [email protected] Abstract This paper provides an introduction to the design of augmented data structures that offer
The following themes form the major topics of this chapter: The terms and concepts related to trees (Section 5.2).
CHAPTER 5 The Tree Data Model There are many situations in which information has a hierarchical or nested structure like that found in family trees or organization charts. The abstraction that models hierarchical
Unordered Linked Lists
Unordered Linked Lists Derive class unorderedlinkedlist from the abstract class linkedlisttype Implement the operations search, insertfirst, insertlast, deletenode See code on page 292 Defines an unordered
Distributed Computing over Communication Networks: Maximal Independent Set
Distributed Computing over Communication Networks: Maximal Independent Set What is a MIS? MIS An independent set (IS) of an undirected graph is a subset U of nodes such that no two nodes in U are adjacent.
Chapter 7: Termination Detection
Chapter 7: Termination Detection Ajay Kshemkalyani and Mukesh Singhal Distributed Computing: Principles, Algorithms, and Systems Cambridge University Press A. Kshemkalyani and M. Singhal (Distributed Computing)
D06 PROGRAMMING with JAVA
Cicles Formatius de Grau Superior Desenvolupament d Aplicacions Informàtiques D06 PROGRAMMING with JAVA Ch20 Data Structures I PowerPoint presentation, created by Angel A. Juan - ajuanp(@)gmail.com, for
09336863931 : provid.ir
provid.ir 09336863931 : NET Architecture Core CSharp o Variable o Variable Scope o Type Inference o Namespaces o Preprocessor Directives Statements and Flow of Execution o If Statement o Switch Statement
Python for Rookies. Example Examination Paper
Python for Rookies Example Examination Paper Instructions to Students: Time Allowed: 2 hours. This is Open Book Examination. All questions carry 25 marks. There are 5 questions in this exam. You should
Approximation Algorithms
Approximation Algorithms or: How I Learned to Stop Worrying and Deal with NP-Completeness Ong Jit Sheng, Jonathan (A0073924B) March, 2012 Overview Key Results (I) General techniques: Greedy algorithms
An Evaluation of Self-adjusting Binary Search Tree Techniques
SOFTWARE PRACTICE AND EXPERIENCE, VOL. 23(4), 369 382 (APRIL 1993) An Evaluation of Self-adjusting Binary Search Tree Techniques jim bell and gopal gupta Department of Computer Science, James Cook University,
Pseudo code Tutorial and Exercises Teacher s Version
Pseudo code Tutorial and Exercises Teacher s Version Pseudo-code is an informal way to express the design of a computer program or an algorithm in 1.45. The aim is to get the idea quickly and also easy
Hash-based Digital Signature Schemes
Hash-based Digital Signature Schemes Johannes Buchmann Erik Dahmen Michael Szydlo October 29, 2008 Contents 1 Introduction 2 2 Hash based one-time signature schemes 3 2.1 Lamport Diffie one-time signature
PL/SQL Overview. Basic Structure and Syntax of PL/SQL
PL/SQL Overview PL/SQL is Procedural Language extension to SQL. It is loosely based on Ada (a variant of Pascal developed for the US Dept of Defense). PL/SQL was first released in ١٩٩٢ as an optional extension
Web Data Extraction: 1 o Semestre 2007/2008
Web Data : Given Slides baseados nos slides oficiais do livro Web Data Mining c Bing Liu, Springer, December, 2006. Departamento de Engenharia Informática Instituto Superior Técnico 1 o Semestre 2007/2008
Lecture 4 Online and streaming algorithms for clustering
CSE 291: Geometric algorithms Spring 2013 Lecture 4 Online and streaming algorithms for clustering 4.1 On-line k-clustering To the extent that clustering takes place in the brain, it happens in an on-line
Innovative Techniques and Tools to Detect Data Quality Problems
Paper DM05 Innovative Techniques and Tools to Detect Data Quality Problems Hong Qi and Allan Glaser Merck & Co., Inc., Upper Gwynnedd, PA ABSTRACT High quality data are essential for accurate and meaningful
Programming Languages CIS 443
Course Objectives Programming Languages CIS 443 0.1 Lexical analysis Syntax Semantics Functional programming Variable lifetime and scoping Parameter passing Object-oriented programming Continuations Exception
SIMS 255 Foundations of Software Design. Complexity and NP-completeness
SIMS 255 Foundations of Software Design Complexity and NP-completeness Matt Welsh November 29, 2001 [email protected] 1 Outline Complexity of algorithms Space and time complexity ``Big O'' notation Complexity
Introduction to Python
WEEK ONE Introduction to Python Python is such a simple language to learn that we can throw away the manual and start with an example. Traditionally, the first program to write in any programming language
SQL INJECTION ATTACKS By Zelinski Radu, Technical University of Moldova
SQL INJECTION ATTACKS By Zelinski Radu, Technical University of Moldova Where someone is building a Web application, often he need to use databases to store information, or to manage user accounts. And
Iteration CHAPTER 6. Topic Summary
CHAPTER 6 Iteration TOPIC OUTLINE 6.1 while Loops 6.2 for Loops 6.3 Nested Loops 6.4 Off-by-1 Errors 6.5 Random Numbers and Simulations 6.6 Loop Invariants (AB only) Topic Summary 6.1 while Loops Many
MS Access Lab 2. Topic: Tables
MS Access Lab 2 Topic: Tables Summary Introduction: Tables, Start to build a new database Creating Tables: Datasheet View, Design View Working with Data: Sorting, Filtering Help on Tables Introduction
2.1 Complexity Classes
15-859(M): Randomized Algorithms Lecturer: Shuchi Chawla Topic: Complexity classes, Identity checking Date: September 15, 2004 Scribe: Andrew Gilpin 2.1 Complexity Classes In this lecture we will look
10CS35: Data Structures Using C
CS35: Data Structures Using C QUESTION BANK REVIEW OF STRUCTURES AND POINTERS, INTRODUCTION TO SPECIAL FEATURES OF C OBJECTIVE: Learn : Usage of structures, unions - a conventional tool for handling a
Using Edit-Distance Functions to Identify Similar E-Mail Addresses Howard Schreier, U.S. Dept. of Commerce, Washington DC
Paper 073-29 Using Edit-Distance Functions to Identify Similar E-Mail Addresses Howard Schreier, U.S. Dept. of Commerce, Washington DC ABSTRACT Version 9 of SAS software has added functions which can efficiently
Physical Data Organization
Physical Data Organization Database design using logical model of the database - appropriate level for users to focus on - user independence from implementation details Performance - other major factor
6.3 Conditional Probability and Independence
222 CHAPTER 6. PROBABILITY 6.3 Conditional Probability and Independence Conditional Probability Two cubical dice each have a triangle painted on one side, a circle painted on two sides and a square painted
Chapter 15: Dynamic Programming
Chapter 15: Dynamic Programming Dynamic programming is a general approach to making a sequence of interrelated decisions in an optimum way. While we can describe the general characteristics, the details
A Static Analyzer for Large Safety-Critical Software. Considered Programs and Semantics. Automatic Program Verification by Abstract Interpretation
PLDI 03 A Static Analyzer for Large Safety-Critical Software B. Blanchet, P. Cousot, R. Cousot, J. Feret L. Mauborgne, A. Miné, D. Monniaux,. Rival CNRS École normale supérieure École polytechnique Paris
A Programming Language for Mechanical Translation Victor H. Yngve, Massachusetts Institute of Technology, Cambridge, Massachusetts
[Mechanical Translation, vol.5, no.1, July 1958; pp. 25-41] A Programming Language for Mechanical Translation Victor H. Yngve, Massachusetts Institute of Technology, Cambridge, Massachusetts A notational
UIL Computer Science for Dummies by Jake Warren and works from Mr. Fleming
UIL Computer Science for Dummies by Jake Warren and works from Mr. Fleming 1 2 Foreword First of all, this book isn t really for dummies. I wrote it for myself and other kids who are on the team. Everything
QuickBooks 2010 Installation Guide
QuickBooks 2010 Installation Guide This article provides instructions for installing QuickBooks 2010 on a single computer and in a network environment. QuickBooks Elements Several elements are involved
Quiz 4 Solutions EECS 211: FUNDAMENTALS OF COMPUTER PROGRAMMING II. 1 Q u i z 4 S o l u t i o n s
Quiz 4 Solutions Q1: What value does function mystery return when called with a value of 4? int mystery ( int number ) { if ( number
16 Collection Classes
16 Collection Classes Collections are a key feature of the ROOT system. Many, if not most, of the applications you write will use collections. If you have used parameterized C++ collections or polymorphic
CPS122 - OBJECT-ORIENTED SOFTWARE DEVELOPMENT. Team Project
CPS122 - OBJECT-ORIENTED SOFTWARE DEVELOPMENT Team Project Due Dates: See syllabus for due dates for each milestone This project spans much of the semester, to be completed as a series of milestones, each
CSE 326, Data Structures. Sample Final Exam. Problem Max Points Score 1 14 (2x7) 2 18 (3x6) 3 4 4 7 5 9 6 16 7 8 8 4 9 8 10 4 Total 92.
Name: Email ID: CSE 326, Data Structures Section: Sample Final Exam Instructions: The exam is closed book, closed notes. Unless otherwise stated, N denotes the number of elements in the data structure
Operations: search;; min;; max;; predecessor;; successor. Time O(h) with h height of the tree (more on later).
Binary search tree Operations: search;; min;; max;; predecessor;; successor. Time O(h) with h height of the tree (more on later). Data strutcure fields usually include for a given node x, the following
