Circular range in Python - python

How can I implement a circular range object in Python?
e.g.
Let S is a circular space modulo 2^3 (range [0, 2^3)).
I want to generate a range object like this:
crange(3, 7, 2 ** 3) # => a range object [3, 4, 5, 6]
crange(7, 3, 2 ** 3) # => a range object [7, 0, 1, 2]
I tried this:
def crange(start, stop, modulo):
if start > stop:
return range(start, modulo) or range(stop)
else:
return range(start, stop)
But I can't input bigint to crange e.g. crange(8, 2, 2 ** 160).
OverflowError: Python int too large to convert to C ssize_t

Try this:
def crange(start, stop, modulo):
result = []
index = start
while index != stop:
result.append(index)
index = (index + 1) % modulo
return result
If you know that your list can be too long, you can use a generator instead that generates the necessage sequence:
def crange(start, stop, modulo):
index = start
while index != stop:
yield index
index = (index + 1) % modulo

I implemented crange which I want (in reference to #Ni and #J.F.Sebastian).
import math
class crange:
def __init__(self, start, stop, step=None, modulo=None):
if step == 0:
raise ValueError('crange() arg 3 must not be zero')
if step is None and modulo is None:
self.start = 0
self.stop = start
self.step = 1
self.modulo = stop
else:
self.start = start
self.stop = stop
if modulo is None:
self.step = 1
self.modulo = step
else:
self.step = step
self.modulo = modulo
def __iter__(self):
n = self.start
if n > self.stop:
while n < self.modulo:
yield n
n += 1
n = 0
while n < self.stop:
yield n
n += 1
def __contains__(self, n):
if self.start >= self.stop:
return self.start <= n < self.modulo or 0 <= n < self.stop
else:
return self.start <= n < self.stop
I got the following output:
>>> print(list(crange(start=7, stop=3, modulo=2 ** 4)))
[7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2]
>>> print(3 in crange(start=7, stop=3, modulo=2 ** 4))
False
>>> print(7 in crange(start=7, stop=3, modulo=2 ** 4))
True
>>> print(list(crange(start=3, stop=7, modulo=2 ** 4)))
[3, 4, 5, 6]
>>> print(3 in crange(start=3, stop=7, modulo=2 ** 4))
True
>>> print(7 in crange(start=3, stop=7, modulo=2 ** 4))
False

You can avoid the use of range and the storage of a huge list in memory by creating your own generator:
def crange(start, end, modulo):
if start > end:
while start < modulo:
yield start
start += 1
start = 0
while start < end:
yield start
start += 1
print list(crange(3, 7, 2 ** 3))
print list(crange(7, 3, 2 ** 3))
print next(crange(8, 2, 2 ** 160))
This code outputs:
[3, 4, 5, 6]
[7, 0, 1, 2]
8

An equivalent solution to that of #Fomalhaut but simpler:
def crange(start, end, modulo):
for i in range(start,end):
yield i % modulo

Added no reset to start and subscriptability. original
copied from answer by #sira
class Crange:
def __init__(self, start, stop, step=None, modulo=None, no_reset=True):
if step == 0:
raise ValueError('crange() arg 3 must not be zero')
self.no_reset = no_reset
if step is None and modulo is None:
self.start = 0
self.stop = start
self.step = 1
self.modulo = stop
else:
self.start = start
self.stop = stop
if modulo is None:
self.step = 1
self.modulo = step
else:
self.step = step
self.modulo = modulo
def __iter__(self):
n = self.start
if n > self.stop:
while n < self.modulo:
yield n
n += self.step
if n > self.modulo:
n = n-self.modulo if self.no_reset else 0
else: n=0
while n < self.stop:
yield n
n += self.step
def __getitem__(self, __name):
return [*self][__name]
def __contains__(self, n):
if self.start >= self.stop:
return self.start <= n < self.modulo or 0 <= n < self.stop
else:
return self.start <= n < self.stop

I made my own version that can slice forwards and backwards. What it can't do in this state is use negative integers and different step sizes than one though, so that would need to be implemented to cover the whole gamut of slicing options in python. I bet that's an easy addition though.
l = list(range(10))
def crange(l,a,b):
assert a >= 0
assert b >= 0
d = b-a
if d == 0:
return
elif d > 0:
increment = 1
elif d < 0:
increment = -1
start = a%len(l)
counter = 0
while counter <= abs(d):
index = abs(start + counter*increment)%len(l)
yield l[index]
counter += 1
print(list(l))
print(list(crange(l,0,6)))
print(list(crange(l,6,10)))
print(list(crange(l,10,25)))
print(list(crange(l,9,0)))
print(list(crange(l,10,5)))

Python 3.x's range objects provides more functionality than just iteration and membership testing: subscripting (indexing), reverse iteration, length testing (which implies boolean coercion), the .count and .index methods (all of these collectively being the Sequence protocol), plus __str__ and __repr__.
To emulate these, we can wrap a range object, delegating to its methods with modified arguments. We can also leverage collections.abc in order to avoid the need to delegate everything, relying on mixins from the Sequence ABC instead.
There's also an issue here in that the input specification is tricky to interpret in corner cases, and inflexible (for example, we can't represent an iterator over [3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6]). The simple way around this is to redefine: a crange(start, stop, step, modulo) iterates over the values start % modulo, (start + step) % modulo ... (start + n * step) % modulo, such that (start + n * step) is limited to stop. (That is: the same values as would be in the range, but applying the specified modulo value to each of them.)
This is fairly straightforward to implement, as we need only __getitem__ and __len__:
from collections.abc import Sequence
class crange(Sequence):
def __init__(self, start, stop, *, step=1, modulo=None):
self._modulo, self._impl = modulo, range(start, stop, step)
def __getitem__(self, index):
result = self._impl[index]
if self._modulo is not None:
result %= self._modulo
return result
def __len__(self):
return len(self._impl)
def __repr__(self):
return f'c{self._impl}'
Now we can do fun things like:
>>> list(reversed(crange(7, 19, step=3, modulo=8)))
[0, 5, 2, 7]

Related

Given two functions, find a threshold such that one is always bigger than the other

I've implemented the following two functions in Python:
def c(m, n):
if m == 0:
return 0
elif m == 1 and n >= 0:
return n**2+n+1
elif m > 1 and n == 0:
return c(m-1, 1)
elif m > 1 and n > 0:
return c(m-1, c(m, n-1))
def d(n):
exp_num = n-1
result = 2
while exp_num != -1:
result = result**2
exp_num -= 1
final_result = 2**result
return final_result
Where the inputs m and n are both natural numbers. I'm asked to find an x, such that for all numbers y >= x, c(y,y) > d(y).
Some inputs and outputs:
c(1, 1) = 3
c(2, 2) = 183
d(1) = 16
d(2) = 65536
d(3) = 115792089237316195423570985008687907853269984665640564039457584007913129639936
As you can see d grows extremely faster than c. How can I approach this problem? Any help would be much appreciated.
The function c is a variant of Ackermann–Péter function.
It claims to fame is that it is not Primitive Recursive which for purposes here means it quickly runs out of stack space in its computation as the numbers become very large.
The problem is to find min x, such that c(y, y) > d(y), for y >= x.
We believe this is c(3, 3) but it can't be computed. We did compute for 1 & 2:
d(1) = 16, d(2) = 65536, d(3) = 115792089237316195423570985008687907853269984665640564039457584007913129639936
c(1) = 3, c(2, 2) = 183, c(3, 3) = ?
Because it's not primitively-recursive, c(3, 3) is difficult to compute (i.e. run out of stack space). However, rather than an exact number, we can obtain a lower-bound by
by limiting the recursions in the function definition.
This is done as follows.
# Will use Memoization which eliminates repeated calculations in recursive functions
class Memoize:
def __init__(self, fn):
self.fn = fn
self.memo = {}
def __call__(self, *args):
if args not in self.memo:
self.memo[args] = self.fn(*args)
return self.memo[args]
#Memoize
def c(m, n, cnt = 0, MAX_RECURSION = 20):
" Refactor function c to have a max recursion depth "
if cnt > MAX_RECURSION:
return 0 # We know the return value is always >= 0, but normally
# much larger. By quitting early and returning zero we are
# ensuring our final computation will be smaller than it would
# otherwise if we had not limited the recurison depth
#
if m == 0:
return 0
elif m == 1 and n >= 0:
return n**2+n+1
elif m > 1 and n == 0:
return c(m-1, 1, cnt+1)
elif m > 1 and n > 0:
return c(m-1, c(m, n-1, cnt+1), cnt+1)
def d(n):
exp_num = n-1
result = 2
while exp_num != -1:
result = result**2
exp_num -= 1
final_result = 2**result
return final_result
value_d = d(3)
value_c = c(3, 3)
print('Number of digits in d(3) is {}'.format(len(str(value_d))))
#>>> Number of digits in d(3) is 78
print('Number of digits in c(3, 3) is {}'.format(len(str(value_c))))
#>>>Number of digits in c(3, 3) is 74176
Thus, we see that c(3, 3) has ~1K more digits than d(3). It will be even more since we stopped the recursion early in computing c(3, 3).

Python Quicksort implementation

I tried to implement the recursive quicksort in Python, but it doesn't work. I know that there is the problem that the array doesn't get sorted because the pivot is always higher than i, which results in the problem that i is always equals to m.
def partition(array):
pivot = array[-1]
m = 0
for i in range(len(array) - 1):
if array[i] < pivot:
array[i], array[m] = array[m], array[i]
m += 1
else:
continue
array[m], array[len(array)-1] = array[len(array)-1], array[m]
return m
def quicksort(array):
if len(array) > 1:
m = partition(array)
quicksort(array[:m])
quicksort(array[m+1:])
return array
def main():
testarray = [3,6,2,4,5,1,9,8,7,10,14]
print(quicksort(testarray))
if __name__ == '__main__':
main()
Two things. Firstly, you forgot to return array when it's of length 1, and secondly you aren't actually modifying array before returning. This will work.
def quicksort(array):
if len(array) > 1:
m = partition(array)
# return the concatenation of the two sorted arrays
return quicksort(array[:m]) + quicksort(array[m:])
else:
return array
For those looking for an iterative/non-recursive version of Quicksort, here's an implementation I came up with in Python:
from random import randint
def default_comparator_fn(a, b):
return -1 if a < b else (1 if a > b else 0)
def reverse_comparator_fn(a, b):
return default_comparator_fn(b, a)
def quick_sort(A, comparator_fn=default_comparator_fn):
n = len(A)
if n < 2:
# The list has only 1 element or does not have any.
return A
# There are at least 2 elements.
partitions = [[0, n - 1]] # [[start, end]]
while len(partitions):
partition = partitions.pop()
start = partition[0]
end = partition[1]
pivot_index = randint(start, end)
pivot = A[pivot_index]
A[pivot_index], A[start] = A[start], A[pivot_index]
breakpoint_index = start
k = start + 1
m = end
while k <= m:
res = comparator_fn(A[k], pivot)
if res < 0:
breakpoint_index = k
else:
while m > k:
res = comparator_fn(A[m], pivot)
if res < 0:
breakpoint_index = k
A[m], A[k] = A[k], A[m]
m -= 1
break
m -= 1
k += 1
A[start], A[breakpoint_index] = A[breakpoint_index], A[start]
if start < breakpoint_index - 1:
partitions.append([start, breakpoint_index - 1])
if breakpoint_index + 1 < end:
partitions.append([breakpoint_index + 1, end])
return A
# Example:
A = [4, 2, 5, 1, 3]
quick_sort(A) # Sort in ascending order ([1, 2, 3, 4, 5]).
quick_sort(A, reverse_comparator_fn) # Sort in descending order ([5, 4, 3, 2, 1]).
This implementation of Quicksort accepts an optional custom comparator function which defaults to a comparator which compares the elements of the list in ascending order.

Python 3 Recursion for Fibonacci Sequence - Listed

I'm trying to make a list of numbers on a single line that follow the Fib sequence. I'm using the recursive method of Fib(n) = Fib(n-1)+Fib(n-2) and this gives me a single value of course, when I use:
return fib(n-1)+fib(n-2)
How can I make this loop and give me a list? For example:
[1,1,2,3,5,8,13] if I typed in: 7 for n.
OK, so in some ways I have fixed it.
I now ask the user to input a value, say x, which is then used in a while loop. It passes the x value through the recursive fib function, appends this new value to a list and then decrements f by 1.
Then I used list.reverse() to reverse the list so that the numbers appear in ascending order and I then print the list.
This list has spaces between each number though, and I don't want that.
Is there a solution to this?
Slightly more streamlined than d-coder's version:
def fib(n):
a,b = 1,1
for i in xrange(n):
yield a
a,b = b,a+b
>>> list(fib(11))
>>> [1,1,2,3,5,8,13,21,34,55,89]
An iterator is the "most Pythonic solution".
class Fib:
# init creates the iterator
def __init__(self, start=0, end=None):
self.now = 0 # the most recent number in the Fibonacci sequence
self.last = 0 # second most recent number in the Fibonacci sequence
self.i = 0 # current place in the sequence
self.end = end # last place before we stop
# loop through sequence until we get to start
for i in range(start):
void = self.next()
def __iter__(self):
return self
# next() for Python 2
def next(self):
# stop if end is reached
if self.end is not None and self.i > self.end:
raise StopIteration
# find the next Fibonacci number
next = self.now + self.last
self.last = self.now
self.now = next if next > 0 else 1
# keep track of how many numbers we've output
self.i += 1
# return the next number
return self.last
# __next__() for Python 3
def __next__(self):
return next(self)
Then use it like so:
# print starting at 0
for i in Fib(0, 5):
print i
0
1
1
2
3
# print starting at 1
for i in Fib(1, 6):
print i
1
1
2
3
5
# make a list
list(Fib(end=10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
The obvious solution would be:
l = []
for i in range(n):
l.append(fib(i))
However, this would be rather inefficient. Another approach would be modifying your recursive function to return a list of fibonacci numbers until n:
def fib(n):
if n <= 0:
raise ValueError
elif n == 1:
return [1]
elif n == 2:
return [1, 1]
else:
prev = fib(n-1)
return prev + [prev[-2] + prev[-1]]
Try the following code and let me know it helped you.About generators you can find help here and here
def fibonacci(n):
a, b, counter = 0, 1, 0
while True:
if (counter > n): return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(7) ## pass the value here
my_list =[]
for x in f:
my_list.append(x)
print my_list
Recursive version, using memoization for performance:
def fib(n, hash = {0:1, 1:1}):
if n not in hash:
hash[n] = fib(n-1) + fib(n-2)
return hash[n]
testing:
>>> print(list(fib(i) for i in range(7)))
[1, 1, 2, 3, 5, 8, 13]
>>> print(list(fib(i) for i in range(11)))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
def fib(n):
if n <= 0:
return 0
elif n == 1 or n == 2:
return 1
else:
return fib(n-1)+ fib(n-2)
n=10
l = []
for i in range(n):
l.append(fib(i))
print(l)

Finding turning points of an Array in python

If I for example have an array:
A = (0,2,3,4,5,2,1,2,3,4,5,6,7,8,7,6,5,4,5,6)
It can be seen that there are 4 turning points. (at A[4],A[6], A[13], A[17])
How can I use python to return the number of turning points?
import numpy as np
import scipy.integrate as SP
import math
def turningpoints(A):
print A
N = 0
delta = 0
delta_prev = 0
for i in range(1,19):
delta = A[i-1]-A[i] #Change between elements
if delta < delta_prev: #if change has gotten smaller
N = N+1 #number of turning points increases
delta_prev = delta #set the change as the previous change
return N
if __name__ == "__main__":
A = np.array([0,2,3,4,5,2,1,2,3,4,5,6,7,8,7,6,5,4,5,6])
print turningpoints(A)
Currently, this system is flawed and certainly not very elegant. Any ideas?
If you have numpy:
def turningpoints(lst):
dx = np.diff(lst)
return np.sum(dx[1:] * dx[:-1] < 0)
Or the non-numpy equivalent version:
def turningpoints(lst):
dx = [x - y for x, y in zip(lst[1:], lst[:-1])]
return sum(dx1 * dx2 < 0 for dx1, dx2 in zip(dx[1:], dx[:-1]))
And just for the love of one-liners:
def turningpoints(lst):
return sum(x0*x1 + x1*x2 < x1*x1 + x0*x2 for x0, x1, x2 in zip(lst[2:], lst[1:-1], lst[:-2]))
But the readability is arguably decreased on this one :)
I know it's an old question, but I just had the same problem and as Cardin stated in the comments of Malvolio's answer, the answer cannot handle successive points with the same value like [1, 2, 3, 4, 4, 4, 3, 2, 1]. My implementation can handle this problem.
Although, it returns two lists with the indices of the minimum and maximum turning points.
def turning_points(array):
''' turning_points(array) -> min_indices, max_indices
Finds the turning points within an 1D array and returns the indices of the minimum and
maximum turning points in two separate lists.
'''
idx_max, idx_min = [], []
if (len(array) < 3):
return idx_min, idx_max
NEUTRAL, RISING, FALLING = range(3)
def get_state(a, b):
if a < b: return RISING
if a > b: return FALLING
return NEUTRAL
ps = get_state(array[0], array[1])
begin = 1
for i in range(2, len(array)):
s = get_state(array[i - 1], array[i])
if s != NEUTRAL:
if ps != NEUTRAL and ps != s:
if s == FALLING:
idx_max.append((begin + i - 1) // 2)
else:
idx_min.append((begin + i - 1) // 2)
begin = i
ps = s
return idx_min, idx_max
To correctly answer the question, the number of turning points is then computed as:
sum(len(x) for x in turning_points(X))
Example
You're overthinking it. A "turning point" is one that is either higher than the points on both sides, or lower.
def turningpoints(x):
N=0
for i in range(1, len(x)-1):
if ((x[i-1] < x[i] and x[i+1] < x[i])
or (x[i-1] > x[i] and x[i+1] > x[i])):
N += 1
return N
>>> turningpoints([0,2,3,4,5,2,1,2,3,4,5,6,7,8,7,6,5,4,5,6])
4
>>> def turns(L):
... answer, delta = 0, -1 if L[1]<L[0] else 1
... i = 2
... while i < len(L):
... d = -1 if L[i]<L[i-1] else 1
... if d != delta:
... answer += 1
... delta = d
... i += 1
... return answer
...
>>> L = [0,2,3,4,5,2,1,2,3,4,5,6,7,8,7,6,5,4,5,6]
>>> turns(L)
4
def group_in_threes(slicable):
for i in range(len(slicable)-2):
yield slicable[i:i+3]
def turns(L):
for index, three in enumerate(group_in_threes(L)):
if (three[0] > three[1] < three[2]) or (three[0] < three[1] > three[2]):
yield index + 1
>>> list(turns([0,2,3,4,5,2,1,2,3,4,5,6,7,8,7,6,5,4,5,6]))
[4, 6, 13, 17]
>>> len(_)
4

Mergesort with Python

I couldn't find any working Python 3.3 mergesort algorithm codes, so I made one myself. Is there any way to speed it up? It sorts 20,000 numbers in about 0.3-0.5 seconds
def msort(x):
result = []
if len(x) < 2:
return x
mid = int(len(x)/2)
y = msort(x[:mid])
z = msort(x[mid:])
while (len(y) > 0) or (len(z) > 0):
if len(y) > 0 and len(z) > 0:
if y[0] > z[0]:
result.append(z[0])
z.pop(0)
else:
result.append(y[0])
y.pop(0)
elif len(z) > 0:
for i in z:
result.append(i)
z.pop(0)
else:
for i in y:
result.append(i)
y.pop(0)
return result
The first improvement would be to simplify the three cases in the main loop: Rather than iterating while some of the sequence has elements, iterate while both sequences have elements. When leaving the loop, one of them will be empty, we don't know which, but we don't care: We append them at the end of the result.
def msort2(x):
if len(x) < 2:
return x
result = [] # moved!
mid = int(len(x) / 2)
y = msort2(x[:mid])
z = msort2(x[mid:])
while (len(y) > 0) and (len(z) > 0):
if y[0] > z[0]:
result.append(z[0])
z.pop(0)
else:
result.append(y[0])
y.pop(0)
result += y
result += z
return result
The second optimization is to avoid popping the elements. Rather, have two indices:
def msort3(x):
if len(x) < 2:
return x
result = []
mid = int(len(x) / 2)
y = msort3(x[:mid])
z = msort3(x[mid:])
i = 0
j = 0
while i < len(y) and j < len(z):
if y[i] > z[j]:
result.append(z[j])
j += 1
else:
result.append(y[i])
i += 1
result += y[i:]
result += z[j:]
return result
A final improvement consists in using a non recursive algorithm to sort short sequences. In this case I use the built-in sorted function and use it when the size of the input is less than 20:
def msort4(x):
if len(x) < 20:
return sorted(x)
result = []
mid = int(len(x) / 2)
y = msort4(x[:mid])
z = msort4(x[mid:])
i = 0
j = 0
while i < len(y) and j < len(z):
if y[i] > z[j]:
result.append(z[j])
j += 1
else:
result.append(y[i])
i += 1
result += y[i:]
result += z[j:]
return result
My measurements to sort a random list of 100000 integers are 2.46 seconds for the original version, 2.33 for msort2, 0.60 for msort3 and 0.40 for msort4. For reference, sorting all the list with sorted takes 0.03 seconds.
Code from MIT course. (with generic cooperator )
import operator
def merge(left, right, compare):
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if compare(left[i], right[j]):
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while i < len(left):
result.append(left[i])
i += 1
while j < len(right):
result.append(right[j])
j += 1
return result
def mergeSort(L, compare=operator.lt):
if len(L) < 2:
return L[:]
else:
middle = int(len(L) / 2)
left = mergeSort(L[:middle], compare)
right = mergeSort(L[middle:], compare)
return merge(left, right, compare)
def merge_sort(x):
if len(x) < 2:return x
result,mid = [],int(len(x)/2)
y = merge_sort(x[:mid])
z = merge_sort(x[mid:])
while (len(y) > 0) and (len(z) > 0):
if y[0] > z[0]:result.append(z.pop(0))
else:result.append(y.pop(0))
result.extend(y+z)
return result
You can initialise the whole result list in the top level call to mergesort:
result = [0]*len(x) # replace 0 with a suitable default element if necessary.
# or just copy x (result = x[:])
Then for the recursive calls you can use a helper function to which you pass not sublists, but indices into x. And the bottom level calls read their values from x and write into result directly.
That way you can avoid all that poping and appending which should improve performance.
Take my implementation
def merge_sort(sequence):
"""
Sequence of numbers is taken as input, and is split into two halves, following which they are recursively sorted.
"""
if len(sequence) < 2:
return sequence
mid = len(sequence) // 2 # note: 7//2 = 3, whereas 7/2 = 3.5
left_sequence = merge_sort(sequence[:mid])
right_sequence = merge_sort(sequence[mid:])
return merge(left_sequence, right_sequence)
def merge(left, right):
"""
Traverse both sorted sub-arrays (left and right), and populate the result array
"""
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
# Print the sorted list.
print(merge_sort([5, 2, 6, 8, 5, 8, 1]))
As already said, l.pop(0) is a O(len(l)) operation and must be avoided, the above msort function is O(n**2). If efficiency matter, indexing is better but have cost too. The for x in l is faster but not easy to implement for mergesort : iter can be used instead here. Finally, checking i < len(l) is made twice because tested again when accessing the element : the exception mechanism (try except) is better and give a last improvement of 30% .
def msort(l):
if len(l)>1:
t=len(l)//2
it1=iter(msort(l[:t]));x1=next(it1)
it2=iter(msort(l[t:]));x2=next(it2)
l=[]
try:
while True:
if x1<=x2: l.append(x1);x1=next(it1)
else : l.append(x2);x2=next(it2)
except:
if x1<=x2: l.append(x2);l.extend(it2)
else: l.append(x1);l.extend(it1)
return l
Loops like this can probably be speeded up:
for i in z:
result.append(i)
z.pop(0)
Instead, simply do this:
result.extend(z)
Note that there is no need to clean the contents of z because you won't use it anyway.
A longer one that counts inversions and adheres to the sorted interface. It's trivial to modify this to make it a method of an object that sorts in place.
import operator
class MergeSorted:
def __init__(self):
self.inversions = 0
def __call__(self, l, key=None, reverse=False):
self.inversions = 0
if key is None:
self.key = lambda x: x
else:
self.key = key
if reverse:
self.compare = operator.gt
else:
self.compare = operator.lt
dest = list(l)
working = [0] * len(l)
self.inversions = self._merge_sort(dest, working, 0, len(dest))
return dest
def _merge_sort(self, dest, working, low, high):
if low < high - 1:
mid = (low + high) // 2
x = self._merge_sort(dest, working, low, mid)
y = self._merge_sort(dest, working, mid, high)
z = self._merge(dest, working, low, mid, high)
return (x + y + z)
else:
return 0
def _merge(self, dest, working, low, mid, high):
i = 0
j = 0
inversions = 0
while (low + i < mid) and (mid + j < high):
if self.compare(self.key(dest[low + i]), self.key(dest[mid + j])):
working[low + i + j] = dest[low + i]
i += 1
else:
working[low + i + j] = dest[mid + j]
inversions += (mid - (low + i))
j += 1
while low + i < mid:
working[low + i + j] = dest[low + i]
i += 1
while mid + j < high:
working[low + i + j] = dest[mid + j]
j += 1
for k in range(low, high):
dest[k] = working[k]
return inversions
msorted = MergeSorted()
Uses
>>> l = [5, 2, 3, 1, 4]
>>> s = msorted(l)
>>> s
[1, 2, 3, 4, 5]
>>> msorted.inversions
6
>>> l = ['e', 'b', 'c', 'a', 'd']
>>> d = {'a': 10,
... 'b': 4,
... 'c': 2,
... 'd': 5,
... 'e': 9}
>>> key = lambda x: d[x]
>>> s = msorted(l, key=key)
>>> s
['c', 'b', 'd', 'e', 'a']
>>> msorted.inversions
5
>>> l = [5, 2, 3, 1, 4]
>>> s = msorted(l, reverse=True)
>>> s
[5, 4, 3, 2, 1]
>>> msorted.inversions
4
>>> l = ['e', 'b', 'c', 'a', 'd']
>>> d = {'a': 10,
... 'b': 4,
... 'c': 2,
... 'd': 5,
... 'e': 9}
>>> key = lambda x: d[x]
>>> s = msorted(l, key=key, reverse=True)
>>> s
['a', 'e', 'd', 'b', 'c']
>>> msorted.inversions
5
Here is the CLRS Implementation:
def merge(arr, p, q, r):
n1 = q - p + 1
n2 = r - q
right, left = [], []
for i in range(n1):
left.append(arr[p + i])
for j in range(n2):
right.append(arr[q + j + 1])
left.append(float('inf'))
right.append(float('inf'))
i = j = 0
for k in range(p, r + 1):
if left[i] <= right[j]:
arr[k] = left[i]
i += 1
else:
arr[k] = right[j]
j += 1
def merge_sort(arr, p, r):
if p < r:
q = (p + r) // 2
merge_sort(arr, p, q)
merge_sort(arr, q + 1, r)
merge(arr, p, q, r)
if __name__ == '__main__':
test = [5, 2, 4, 7, 1, 3, 2, 6]
merge_sort(test, 0, len(test) - 1)
print test
Result:
[1, 2, 2, 3, 4, 5, 6, 7]
Many have answered this question correctly, this is just another solution (although my solution is very similar to Max Montana) but I have few differences for implementation:
let's review the general idea here before we get to the code:
Divide the list into two roughly equal halves.
Sort the left half.
Sort the right half.
Merge the two sorted halves into one sorted list.
here is the code (tested with python 3.7):
def merge(left,right):
result=[]
i,j=0,0
while i<len(left) and j<len(right):
if left[i] < right[j]:
result.append(left[i])
i+=1
else:
result.append(right[j])
j+=1
result.extend(left[i:]) # since we want to add each element and not the object list
result.extend(right[j:])
return result
def merge_sort(data):
if len(data)==1:
return data
middle=len(data)//2
left_data=merge_sort(data[:middle])
right_data=merge_sort(data[middle:])
return merge(left_data,right_data)
data=[100,5,200,3,100,4,8,9]
print(merge_sort(data))
here is another solution
class MergeSort(object):
def _merge(self,left, right):
nl = len(left)
nr = len(right)
result = [0]*(nl+nr)
i=0
j=0
for k in range(len(result)):
if nl>i and nr>j:
if left[i] <= right[j]:
result[k]=left[i]
i+=1
else:
result[k]=right[j]
j+=1
elif nl==i:
result[k] = right[j]
j+=1
else: #nr>j:
result[k] = left[i]
i+=1
return result
def sort(self,arr):
n = len(arr)
if n<=1:
return arr
left = self.sort(arr[:n/2])
right = self.sort(arr[n/2:] )
return self._merge(left, right)
def main():
import random
a= range(100000)
random.shuffle(a)
mr_clss = MergeSort()
result = mr_clss.sort(a)
#print result
if __name__ == '__main__':
main()
and here is run time for list with 100000 elements:
real 0m1.073s
user 0m1.053s
sys 0m0.017s
def merge(l1, l2, out=[]):
if l1==[]: return out+l2
if l2==[]: return out+l1
if l1[0]<l2[0]: return merge(l1[1:], l2, out+l1[0:1])
return merge(l1, l2[1:], out+l2[0:1])
def merge_sort(l): return (lambda h: l if h<1 else merge(merge_sort(l[:h]), merge_sort(l[h:])))(len(l)/2)
print(merge_sort([1,4,6,3,2,5,78,4,2,1,4,6,8]))
def merge(x):
if len(x) == 1:
return x
else:
mid = int(len(x) / 2)
l = merge(x[:mid])
r = merge(x[mid:])
i = j = 0
result = []
while i < len(l) and j < len(r):
if l[i] < r[j]:
result.append(l[i])
i += 1
else:
result.append(r[j])
j += 1
result += l[i:]
result += r[j:]
return result
A little late the the party, but I figured I'd throw my hat in the ring as my solution seems to run faster than OP's (on my machine, anyway):
# [Python 3]
def merge_sort(arr):
if len(arr) < 2:
return arr
half = len(arr) // 2
left = merge_sort(arr[:half])
right = merge_sort(arr[half:])
out = []
li = ri = 0 # index of next element from left, right halves
while True:
if li >= len(left): # left half is exhausted
out.extend(right[ri:])
break
if ri >= len(right): # right half is exhausted
out.extend(left[li:])
break
if left[li] < right[ri]:
out.append(left[li])
li += 1
else:
out.append(right[ri])
ri += 1
return out
This doesn't have any slow pop()s, and once one of the half-arrays is exhausted, it immediately extends the other one onto the output array rather than starting a new loop.
I know it's machine dependent, but for 100,000 random elements (above merge_sort() vs. Python built-in sorted()):
merge sort: 1.03605 seconds
Python sort: 0.045 seconds
Ratio merge / Python sort: 23.0229
def mergeSort(alist):
print("Splitting ",alist)
if len(alist)>1:
mid = len(alist)//2
lefthalf = alist[:mid]
righthalf = alist[mid:]
mergeSort(lefthalf)
mergeSort(righthalf)
i=0
j=0
k=0
while i < len(lefthalf) and j < len(righthalf):
if lefthalf[i] < righthalf[j]:
alist[k]=lefthalf[i]
i=i+1
else:
alist[k]=righthalf[j]
j=j+1
k=k+1
while i < len(lefthalf):
alist[k]=lefthalf[i]
i=i+1
k=k+1
while j < len(righthalf):
alist[k]=righthalf[j]
j=j+1
k=k+1
print("Merging ",alist)
alist = [54,26,93,17,77,31,44,55,20]
mergeSort(alist)
print(alist)
Glad there are tons of answers, I hope you find this one to be clear, concise, and fast.
Thank you
import math
def merge_array(ar1, ar2):
c, i, j= [], 0, 0
while i < len(ar1) and j < len(ar2):
if ar1[i] < ar2[j]:
c.append(ar1[i])
i+=1
else:
c.append(ar2[j])
j+=1
return c + ar1[i:] + ar2[j:]
def mergesort(array):
n = len(array)
if n == 1:
return array
half_n = math.floor(n/2)
ar1, ar2 = mergesort(array[:half_n]), mergesort(array[half_n:])
return merge_array(ar1, ar2)
After implementing different versions of solution,
I finally made a trade-off to achieve these goals based on CLRS version.
Goal
not using list.pop() to iterate values
not creating a new list for saving result, modifying the original one instead
not using float('inf') as sentinel values
def mergesort(A, p, r):
if(p < r):
q = (p+r)//2
mergesort(A, p, q)
mergesort(A, q+1, r)
merge(A, p, q, r)
def merge(A, p, q, r):
L = A[p:q+1]
R = A[q+1:r+1]
i = 0
j = 0
k = p
while i < len(L) and j < len(R):
if(L[i] < R[j]):
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
k += 1
if i < len(L):
A[k:r+1] = L[i:]
if __name__ == "__main__":
items = [6, 2, 9, 1, 7, 3, 4, 5, 8]
mergesort(items, 0, len(items)-1)
print items
assert items == [1, 2, 3, 4, 5, 6, 7, 8, 9]
Reference
[1] Book: CLRS
[2] https://github.com/gzc/CLRS/blob/master/C02-Getting-Started/exercise_code/merge-sort.py
Try this recursive version
def mergeList(l1,l2):
l3=[]
Tlen=len(l1)+len(l2)
inf= float("inf")
for i in range(Tlen):
print "l1= ",l1[0]," l2= ",l2[0]
if l1[0]<=l2[0]:
l3.append(l1[0])
del l1[0]
l1.append(inf)
else:
l3.append(l2[0])
del l2[0]
l2.append(inf)
return l3
def main():
l1=[2,10,7,6,8]
print mergeSort(breaklist(l1))
def breaklist(rawlist):
newlist=[]
for atom in rawlist:
print atom
list_atom=[atom]
newlist.append(list_atom)
return newlist
def mergeSort(inputList):
listlen=len(inputList)
if listlen ==1:
return inputList
else:
newlist=[]
if listlen % 2==0:
for i in range(listlen/2):
newlist.append(mergeList(inputList[2*i],inputList[2*i+1]))
else:
for i in range((listlen+1)/2):
if 2*i+1<listlen:
newlist.append(mergeList(inputList[2*i],inputList[2*i+1]))
else:
newlist.append(inputList[2*i])
return mergeSort(newlist)
if __name__ == '__main__':
main()
def merge(a,low,mid,high):
l=a[low:mid+1]
r=a[mid+1:high+1]
#print(l,r)
k=0;i=0;j=0;
c=[0 for i in range(low,high+1)]
while(i<len(l) and j<len(r)):
if(l[i]<=r[j]):
c[k]=(l[i])
k+=1
i+=1
else:
c[k]=(r[j])
j+=1
k+=1
while(i<len(l)):
c[k]=(l[i])
k+=1
i+=1
while(j<len(r)):
c[k]=(r[j])
k+=1
j+=1
#print(c)
a[low:high+1]=c
def mergesort(a,low,high):
if(high>low):
mid=(low+high)//2
mergesort(a,low,mid)
mergesort(a,mid+1,high)
merge(a,low,mid,high)
a=[12,8,3,2,9,0]
mergesort(a,0,len(a)-1)
print(a)
If you change your code like that it'll be working.
def merge_sort(arr):
if len(arr) < 2:
return arr[:]
middle_of_arr = len(arr) / 2
left = arr[0:middle_of_arr]
right = arr[middle_of_arr:]
left_side = merge_sort(left)
right_side = merge_sort(right)
return merge(left_side, right_side)
def merge(left_side, right_side):
result = []
while len(left_side) > 0 or len(right_side) > 0:
if len(left_side) > 0 and len(right_side) > 0:
if left_side[0] <= right_side[0]:
result.append(left_side.pop(0))
else:
result.append(right_side.pop(0))
elif len(left_side) > 0:
result.append(left_side.pop(0))
elif len(right_side) > 0:
result.append(right_side.pop(0))
return result
arr = [6, 5, 4, 3, 2, 1]
# print merge_sort(arr)
# [1, 2, 3, 4, 5, 6]
The following code pops at the end (efficient enough) and sorts inplace despite returning as well.
def mergesort(lis):
if len(lis) > 1:
left, right = map(lambda l: list(reversed(mergesort(l))), (lis[::2], lis[1::2]))
lis.clear()
while left and right:
lis.append(left.pop() if left[-1] < right[-1] else right.pop())
lis.extend(left[::-1])
lis.extend(right[::-1])
return lis
This is very similar to the "MIT" solution and a couple others above, but answers the question in a little more "Pythonic" manner by passing references to the left and right partitions instead of positional indexes, and by using a range in the for loop with slice notation to fill in the sorted array:
def merge_sort(array):
n = len(array)
if n > 1:
mid = n//2
left = array[0:mid]
right = array[mid:n]
print(mid, left, right, array)
merge_sort(left)
merge_sort(right)
merge(left, right, array)
def merge(left, right, array):
array_length = len(array)
right_length = len(right)
left_length = len(left)
left_index = right_index = 0
for array_index in range(0, array_length):
if right_index == right_length:
array[array_index:array_length] = left[left_index:left_length]
break
elif left_index == left_length:
array[array_index:array_length] = right[right_index:right_length]
break
elif left[left_index] <= right[right_index]:
array[array_index] = left[left_index]
left_index += 1
else:
array[array_index] = right[right_index]
right_index += 1
array = [99,2,3,3,12,4,5]
arr_len = len(array)
merge_sort(array)
print(array)
assert len(array) == arr_len
This solution finds the left and right partitions using Python's handy // operator, and then passes the left, right, and array references to the merge function, which in turn rebuilds the original array in place.
The trick is in the cleanup: when you have reached the end of either the left or the right partition, the original array is filled in with whatever is left over in the other partition.
#here is my answer using two function one for merge and another for divide and
#conquer
l=int(input('enter range len'))
c=list(range(l,0,-1))
print('list before sorting is',c)
def mergesort1(c,l,r):
i,j,k=0,0,0
while (i<len(l))&(j<len(r)):
if l[i]<r[j]:
c[k]=l[i]
i +=1
else:
c[k]=r[j]
j +=1
k +=1
while i<len(l):
c[k]=l[i]
i+=1
k+=1
while j<len(r):
c[k]=r[j]
j+=1
k+=1
return c
def mergesort(c):
if len(c)<2:
return c
else:
l=c[0:(len(c)//2)]
r=c[len(c)//2:len(c)]
mergesort(l)
mergesort(r)
return mergesort1(c,l,r)
def merge(arr, p, q, r):
left = arr[p:q + 1]
right = arr[q + 1:r + 1]
left.append(float('inf'))
right.append(float('inf'))
i = j = 0
for k in range(p, r + 1):
if left[i] <= right[j]:
arr[k] = left[i]
i += 1
else:
arr[k] = right[j]
j += 1
def init_func(function):
def wrapper(*args):
a = []
if len(args) == 1:
a = args[0] + []
function(a, 0, len(a) - 1)
else:
function(*args)
return a
return wrapper
#init_func
def merge_sort(arr, p, r):
if p < r:
q = (p + r) // 2
merge_sort(arr, p, q)
merge_sort(arr, q + 1, r)
merge(arr, p, q, r)
if __name__ == "__main__":
test = [5, 4, 3, 2, 1]
print(merge_sort(test))
Result would be
[1, 2, 3, 4, 5]
from run_time import run_time
from random_arr import make_arr
def merge(arr1: list, arr2: list):
temp = []
x, y = 0, 0
while len(arr1) and len(arr2):
if arr1[0] < arr2[0]:
temp.append(arr1[0])
x += 1
arr1 = arr1[x:]
elif arr1[0] > arr2[0]:
temp.append(arr2[0])
y += 1
arr2 = arr2[y:]
else:
temp.append(arr1[0])
temp.append(arr2[0])
x += 1
y += 1
arr1 = arr1[x:]
arr2 = arr2[y:]
if len(arr1) > 0:
temp += arr1
if len(arr2) > 0:
temp += arr2
return temp
#run_time
def merge_sort(arr: list):
total = len(arr)
step = 2
while True:
for i in range(0, total, step):
arr[i:i + step] = merge(arr[i:i + step//2], arr[i + step//2:i + step])
step *= 2
if step > 2 * total:
return arr
arr = make_arr(20000)
merge_sort(arr)
# run_time is 0.10300588607788086
Here is my attempt at the recursive merge_sort function in python. Note, this is my first python class and my first encounter with this problem so please bear with me if my code is rough, but it works.
def merge_sort(S):
temp = []
if len(S) < 2:
return S
split = len(S) // 2
left = merge_sort(S[:split])
right = merge_sort(S[split:])
finale = temp + merge(left, right)
return finale
def merge(left, right):
holder = []
while len(left) > 0 and len(right) > 0:
if left[0] < right[0]:
holder.append(left[0])
del left[0]
elif left[0] > right[0]:
holder.append(right[0])
del right[0]
if len(left) > 0:
holder.extend(left)
elif len(right) > 0:
holder.extend(right)
return holder
def splitArray(s):
return s[:len(s)//2], s[len(s)//2:]
# the idea here is i+j should sum to n as you increment i and j,
# but once out of bound, the next item of a or b is infinity
# therefore, the comparison will always switch to the other array
def merge(a, b, n):
result = [0] * n
a = a + [float('inf')]
b = b + [float('inf')]
result = [0] * n
i, j = 0, 0
for k in range(0, n):
if a[i] < b[j]:
result[k] = a[i]
i+=1
else:
result[k] = b[j]
j+=1
return result
def mergeSort(items):
n = len(items)
baseCase = []
if n == 0:
return baseCase
if n == 1:
baseCase.append(items[0])
return baseCase
if n == 2:
if items[0] < items[1]:
baseCase.append(items[0])
baseCase.append(items[1])
return baseCase
else:
baseCase.append(items[1])
baseCase.append(items[0])
return baseCase
left, right = splitArray(items)
sortedLeft = mergeSort(left)
sortedRight = mergeSort(right)
return merge(sortedLeft,sortedRight,n)
# Driver code to test above
arr = [12, 11, 13, 5, 6, 7]
n = len(arr)
print ("Given array is")
for i in range(n):
print ("%d" %arr[i]),
arr = mergeSort(arr)
print ("\n\nSorted array is")
for i in range(n):
print ("%d" %arr[i]),
def merge_sort(l):
if len(l) == 1:
if len(n)> 0:
for i in range(len(n)):
if n[i] > l[0]:
break
else:
i = i+1
n.insert(i, l[0])
else:
n.append(l[0])
else:
p = len(l)//2
a = l[:p]
b = l[p:]
merge_sort(a)
merge_sort(b)
m = [3,5,2,4,1]
n = []
merge_sort(m)
print(n)
first divide the array until it's size grater than 1(which is base condition) and do it by recursive function.
compare the left & right sub array value & place those value in your array.
check any item remain in left & right array...
def merge_sort(my_array):
base condition for recursively dividing the array...
if len(my_array) > 1:
middle = len(my_array) // 2
left_array = my_array[:middle]
right_array = my_array[middle:]
#recursive function
merge_sort(left_array)
merge_sort(right_array)
i = 0 # index of left array...
j = 0 # index of right array...
k = 0 # index of new array...
# conquer the array and sorted like below condition
while i < len(left_array) and j < len(right_array):
if left_array[i] < right_array[j]:
my_array[k] = left_array[i]
i += 1
else:
my_array[k] = right_array[j]
j += 1
k += 1
# checking any item remain in left sub array....
while i < len(left_array):
my_array[k] = left_array[i]
i += 1
j += 1
# checking any item remain in right sub array....
while j < len(right_array):
my_array[k] = right_array[j]
j += 1
k += 1
my_array = [11, 31, 7, 41, 101, 56, 77, 2]
print("Input Array: ",my_array)
merge_sort(my_array)
print("Sorted Array: ",my_array)
I would suggest to leverage Python3's protocols instead of passing a comparator here, there and everywhere.
Also a simple set of tests based Knuth's shuffle would be a decent idea to verify implementation correctness:
from abc import abstractmethod
from collections import deque
from typing import Deque, Protocol, TypeVar, List
from random import randint
class Comparable(Protocol):
"""Protocol for annotating comparable types."""
#abstractmethod
def __lt__(self: 'T', x: 'T') -> bool:
pass
#abstractmethod
def __gt__(self: 'T', x: 'T') -> bool:
pass
T = TypeVar('T', bound=Comparable)
def _swap(items: List[T], i: int, j: int):
tmp = items[i]
items[i] = items[j]
items[j] = tmp
def knuths_shuffle(items: List[T]):
for i in range(len(items) - 1, 1, -1):
j = randint(0, i)
_swap(items, i, j)
return items
def merge(items: List[T], low: int, mid: int, high: int):
left_q = deque(items[low: mid])
right_q = deque(items[mid: high])
def put(q: Deque[T]):
nonlocal low
items[low] = q.popleft()
low += 1
while left_q and right_q:
put(left_q if left_q[0] < right_q[0] else right_q)
def put_all(q: Deque[T]):
while q:
put(q)
put_all(left_q)
put_all(right_q)
return items
def mergesort(items: List[T], low: int, high: int):
if high - low <= 1:
return
mid = (low + high) // 2
mergesort(items, low, mid)
mergesort(items, mid, high)
merge(items, low, mid, high)
def sort(items: List[T]) -> List[T]:
"""
>>> for i in range(100):
... rand = knuths_shuffle(list(range(100)))
... assert sorted(rand) == sort(rand)
"""
mergesort(items, 0, len(items))
return items

Categories

Resources