Related
I want to add in recursion in Binary Search and Insertion Sort to have them displayed the same output but while compiling, could not troubleshoot it. Where could have gone wrong? Getting unexpected indent errors for both of them.
Recursion using Binary Search
def binarySearch(alist, item):
if len(alist) == 0:
return False
else:
midpoint = len(alist)//2
if alist[midpoint]==item:
return True
else if:
if item<alist[midpoint]:
return binarySearch(alist[:midpoint],item)
else:
return binarySearch(alist[midpoint+1:],item)
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binarySearch(testlist, 3))
print(binarySearch(testlist, 13))
Recursion using Insertion Sort (With a list)
def insertionSort(list):
if i >= len(list):
return list
if list[i-1] > list[i]:
temp = list[i]
for a in range(0, i):
if temp < list[a]:
list.insert(a,temp)
del list[i+1]
break
return insertionSort(list, i+1)
while position > 0 and list[position - 1] > currentvalue:
list[position] = list[position - 1]
position = position - 1
list[position] = currentvalue
list = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
insertionSort(list)
print(list)
Code blocks are defined by their indentation. By "code block", I mean functions, if statements, for loops, while loops, and so forth. Indenting starts a block and unindenting ends it. There are no explicit braces, brackets, or keywords. This means that whitespace is significant, and must be consistent. In this example, the function code (including the doc string) is indented four spaces. It doesn't need to be four spaces, it just needs to be consistent. The first line that is not indented is outside the function.
by Dive into Python
you have to know how to use indentation. in your binary search code there are multipe indentation errors and here is the fix:
def binarySearch(alist, item):
if len(alist) == 0:
return False
else:
midpoint = len(alist)//2
if alist[midpoint]==item:
return True
else:
if item<alist[midpoint]:
return binarySearch(alist[:midpoint],item)
else:
return binarySearch(alist[midpoint+1:],item)
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binarySearch(testlist, 3))
print(binarySearch(testlist, 13))
but your insertion sort is another story. you have undefined variables and etc. here is another implementation of insertion sort:
def insertionSort(list,i):
if i<=1:
return
insertionSort(list, i - 1)
currentvalue = list[i - 1]
position = i - 2
while (position >= 0 and list[position] > currentvalue):
list[position + 1] = list[position]
position = position - 1
list[position + 1] = currentvalue
list = [0, 1, 2, 8, 13, 11, 19, 9, 42,]
insertionSort(list,len(list))
print(list)
I am trying to implement a three way partitioning of an given array using python.
My take:
def partition3(alist, lower, heigher, size):
start = 0
end = size-1
for i in range(size):
if alist[i] < lower:
alist[i], alist[start] = alist[start], alist[i]
start = start+1
elif alist[i] > heigher:
alist[i], alist[end] = alist[end],alist[i]
end = end - 1
else:
pass
return alist
def sort(alist, low, high):
return partition3(alist, low, high, len(alist))
print sort([1, 14, 5, 20, 4, 2, 54, 20, 87, 98, 3, 1, 32], 10, 20)
The expected results should be,
1) All elements smaller than lowRange come first.
2) All elements in range lowVal to highRange come next.
3) All elements greater than highRange appear in the end.
This has to be done in an same array, shouldnt have three arrays.
Input:
List, lowRange and highRange.
but i am getting,
[1, 5, 4, 2, 14, 20, 32, 54, 87, 98, 3, 1, 20]
Need help on the things which i am missing here. Thanks in advance
Well you could do something like that (not optimal, most certainly there is a better way):
from itertools import chain
def sort_list_ranges(input_list, low, high):
sorted_list = [[] for _ in range(3)]
for elem in input_list:
if elem < low:
sorted_list[0].append(elem)
elif elem > high:
sorted_list[2].append(elem)
else:
sorted_list[1].append(elem)
print [item for sublist in sorted_list for item in sublist]
It will give you a list containing values lower than 'low', middle range, and values higher than 'high'.
def partition3(alist, lower, heigher):
n = len(alist)
i, j, k = 0, 0, n
while j < k:
if alist[j] < lower:
alist[i], alist[j] = alist[j], alist[i]
i += 1
j += 1
elif alist[j] > heigher:
k -= 1
alist[j], alist[k] = alist[k], alist[j]
else:
j += 1
def sort(alist, low, high):
partition3(alist, low, high)
a = [1, 14, 5, 25, 4, 2, 54, 17, 19, 87, 98, 3, 1, 32]
sort(a, 10, 20)
print(a)
a = [100, 99, 0, 1]
sort(a, 10, 20)
print(a)
Here's something based on your code:
def partition3(alist, lower, heigher, size):
start = 0
while alist[start] < lower:
start = start + 1
end = size-1
while alist[end] > heigher:
end = end - 1
i = start
while i <= end:
if (i > start) and (alist[i] < lower):
alist[i], alist[start] = alist[start], alist[i]
while alist[start] < lower:
start = start + 1
elif alist[i] > heigher:
alist[i], alist[end] = alist[end], alist[i]
while alist[end] > heigher:
end = end - 1
else:
i += 1
return alist
def sort(alist, low, high):
return partition3(alist, low, high, len(alist))
print sort([1, 14, 5, 20, 4, 2, 54, 20, 87, 98, 3, 1, 32], 10, 20)
It works on your example but I'm not 100% sure it's correct.
I think problems with your original version were
what if start points to an element that is already smaller or end points to an element that is already larger? then the swap doesn't fix anything and the misorder may survive
what if i runs past end? it will swap back all the correct upper third elements
This is also working on your example, but maybe not the best solution.
def partition3(alist, lower, heigher, size):
start = 0
end = size-1
i=0
while i < size-1:
if alist[i] < lower:
alist[i], alist[start] = alist[start], alist[i]
start = start+1
i=i+1
elif alist[i] > heigher:
if end < i+1:
break
alist[i], alist[end] = alist[end],alist[i]
end = end - 1
else:
i=i+1
return alist
def sort(alist, low, high):
return partition3(alist, low, high, len(alist))
print sort([1, 14, 5, 20, 4, 2, 54, 20, 87, 98, 3, 1, 32], 10, 20)
Output:
[1, 5, 4, 2, 1, 3, 14, 20, 20, 98, 87, 32, 54]
Thanks #PaulPanzer the if statement for end < i+1 has to be first
I wrote this code for a simple linear search:
def floor(array, target):
i = 0
while i < len(array):
if array[i] == target:
print("The target value can be found at index: " + str(array[i]))
break
else:
i = i + 1
floor([1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14], 5)
The problem I'm having is that it's not returning the values I want. For some reason it doesn't start counting at 0, so when I make the target 1 it tells me the index is 1. Likewise, if I make the target 4 it will give me the second value of 4 at index 4 rather than the one at index 3. Even when the target is a higher number, it only seems to count the value of 4 once. I'm not sure what I'm doing wrong, can anyone offer some advice?
You're printing out the value at the index ie array[i] instead of i.
You're printing the value of the array at the index instead of the index itself. This fixes it. Also consider a return instead of a print statement.
def floor(array, target):
i = 0
while i < len(array):
if array[i] == target:
print("The target value can be found at index: " + str(i))
return i
else:
i = i + 1
four_ind = floor([1, 2, 3, 4, 4, 5, 6, 7, 8, 9], 4) # returns 3
try this:
def floor(array, target):
for i, value in enumerate(array):
if value == target:
print('target is in:', i)
floor([1,2,3,4,5, 4, 4,6], 4)
This is a homework problem. I try to get a recursive function:
def problem_a(n):
answer.append(n)
if n == 1:
return answer
elif n % 2 == 0:
answer.append(n/2)
else :
answer.append(n*3 + 1)
problem_a(n*3 + 1)
This code obviously doesn't work as answer isn't defined as a list. With loops it would work, but I want to make a recursive function. I could just use as input a list, but I wonder if there exist something more elegant.
problem_a(7) should give as output:
[7, 22, 11, 34, 17, 52, 26, 13, 40 , 20, 10 ,5 ,16, 8, 4, 2, 1]
One alternative solution to the ones that have been suggested so far (which use an extra argument to pass the list up the recursive chain) is to build the final list as you return from the recursion. This is not terribly efficient, since concatenating lists requires copying both of them, but it will work:
def problem_a(n):
if n == 1:
return [n]
elif n % 2 == 0:
return [n] + problem_a(n // 2)
else:
return [n] + problem_a(3*n + 1)
You could try a generator:
def problem_a(n):
yield n
if n == 1:
return
elif n % 2 == 0:
x = n / 2
else:
x = n * 3 + 1
for y in problem_a(x):
yield y
print list(problem_a(7))
You can define a local variable answer and pass it around in recursive calls.
def problem_a(n, answer = None):
answer = [n] if answer is None else answer
if n == 1:
return answer
elif n % 2 == 0:
n = n/2
answer.append(n)
else:
n = n*3 + 1
answer.append(n)
return problem_a(n, answer)
print problem_a(7)
output:
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
There is a problem with your skeleton solution. You need to recurse when n % 2 == 0 as well as in the final else. The answer variable is given a default value so that it is initialized to [] when the function is first called without an argument.
def problem_a(n, answer=None):
if answer == None:
answer = []
answer.append(n)
if n == 1:
return answer
elif n % 2 == 0:
return problem_a(n/2, answer)
else :
return problem_a(n*3 + 1, answer)
>>> problem_a(7)
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
Edit
As per the comments, using a mutable default argument is a bad idea. Just set it to None like in the other posts and check if its None to create a new list. I changed the answer to reflect this.
The original bad code was as follows:
def problem_a(n, answer=[]):
answer.append(n)
...
You can also use a closure:
>>> def s():
ret = []
def f(n):
ret.append(n)
if n % 2 == 0:
f(int(n/2))
elif n != 1:
f(int(n*3 + 1))
return ret
return f
>>> s()
<function f at 0x00000000033A5848>
>>> s()(7)
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
I'd like to know if there is a simple (or already created) way of doing the opposite of this: Generate List of Numbers from Hyphenated.... This link could be used to do:
>> list(hyphen_range('1-9,12,15-20,23'))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 16, 17, 18, 19, 20, 23]:
I'm looking to do the opposite (note that 10 and 21 are included so it would be compatible with the range function, where range(1,10)=[1,2,3,4,5,6,7,8,9]):
>> list_to_ranges([1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 15, 16, 17, 18, 19, 20, 23])
'1-10,12,15-21,23'
Eventually, I would like to have the output also incorporate a step where the last number of the output indicates the step:
>> list_to_ranges([1, 3, 5, 7, 8, 9, 10, 11])
'1-13:2,8,10'
Essentially, this would end up being kind of like an "inverse" range function
>> tmp = list_to_ranges([1, 3, 5])
>> print tmp
'1-7:2'
>> range(1, 7, 2)
[1, 3, 5]
My guess is that there is no really easy/simple way to do this, but I thought I would ask on here before I go make some brute force, long method.
EDIT
Using the code from an answer to this post as an example, I came up with a simple way to do the first part. But I think that identifying the patterns to do steps would be a bit harder.
from itertools import groupby
from operator import itemgetter
data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
print data, '\n'
str_list = []
for k, g in groupby(enumerate(data), lambda (i,x):i-x):
ilist = map(itemgetter(1), g)
print ilist
if len(ilist) > 1:
str_list.append('%d-%d' % (ilist[0], ilist[-1]+1))
else:
str_list.append('%d' % ilist[0])
print '\n', ','.join(str_list)
EDIT 2
Here is my attempt at including the step size...it is pretty close, but the first numbers get repeated. I think that with a little bit of tweaking of this, it will be close to what I want - or at least good enough.
import numpy as np
from itertools import groupby
def list_to_ranges(data):
data = sorted(data)
diff_data = np.diff(data).tolist()
ranges = []
i = 0
for k, iterable in groupby(diff_data, None):
rng = list(iterable)
step = rng[0]
if len(rng) == 1:
ranges.append('%d' % data[i])
elif step == 1:
ranges.append('%d-%d' % (data[i], data[i+len(rng)]+step))
else:
ranges.append('%d-%d:%d' % (data[i], data[i+len(rng)]+step, step))
i += len(rng)
return ','.join(ranges)
data = [1, 3, 5, 6, 7, 11, 13, 15, 16, 17, 18, 19, 22, 25, 28]
print data
data_str = list_to_ranges(data)
print data_str
_list = []
for r in data_str.replace('-',':').split(','):
r = [int(a) for a in r.split(':')]
if len(r) == 1:
_list.extend(r)
elif len(r) == 2:
_list.extend(range(r[0], r[1]))
else:
_list.extend(range(r[0], r[1], r[2]))
print _list
print list(set(_list))
One approach could be "eating" piece by piece the input sequence and store the partial range results untill you've got them all:
def formatter(start, end, step):
return '{}-{}:{}'.format(start, end, step)
# return '{}-{}:{}'.format(start, end + step, step)
def helper(lst):
if len(lst) == 1:
return str(lst[0]), []
if len(lst) == 2:
return ','.join(map(str,lst)), []
step = lst[1] - lst[0]
for i,x,y in zip(itertools.count(1), lst[1:], lst[2:]):
if y-x != step:
if i > 1:
return formatter(lst[0], lst[i], step), lst[i+1:]
else:
return str(lst[0]), lst[1:]
return formatter(lst[0], lst[-1], step), []
def re_range(lst):
result = []
while lst:
partial,lst = helper(lst)
result.append(partial)
return ','.join(result)
I test it with a bunch of unit tests and it passed them all, it can handle negative numbers too, but they'll look kind of ugly (it's really anybody's fault).
Example:
>>> re_range([1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28])
'1,4-6:1,10,15-18:1,22,25-28:1'
>>> re_range([1, 3, 5, 7, 8, 9, 10, 11, 13, 15, 17])
'1-7:2,8-11:1,13-17:2'
Note: I wrote the code for Python 3.
Performance
I didn't put any performance effort in the solution above. In particular, every time a list get re-builded with slicing, it might take some time if the input list has a particular shape. So, the first simple improvement would be using itertools.islice() where possible.
Anyway here's another implementation of the same algorithm, that scan through the input list with a scan index instead of slicing:
def re_range(lst):
n = len(lst)
result = []
scan = 0
while n - scan > 2:
step = lst[scan + 1] - lst[scan]
if lst[scan + 2] - lst[scan + 1] != step:
result.append(str(lst[scan]))
scan += 1
continue
for j in range(scan+2, n-1):
if lst[j+1] - lst[j] != step:
result.append(formatter(lst[scan], lst[j], step))
scan = j+1
break
else:
result.append(formatter(lst[scan], lst[-1], step))
return ','.join(result)
if n - scan == 1:
result.append(str(lst[scan]))
elif n - scan == 2:
result.append(','.join(map(str, lst[scan:])))
return ','.join(result)
I stopped working on it once it got ~65% faster than the previous top solution, it seemed enough :)
Anyway I'd say that there might still be room for improvement (expecially in the middle for-loop).
This is a comparison of the 3 methods. Change the amount of data and the density via the values below...no matter what values I use, the first solution seems to be the quickest for me. For very large sets of data, the third solution becomes very slow.
EDITED
Edited to include comments below and add in a new solution. The last solution seems to be the quickest now.
import numpy as np
import itertools
import random
import timeit
# --- My Solution --------------------------------------------------------------
def list_to_ranges1(data):
data = sorted(data)
diff_data = np.diff(data)
ranges = []
i = 0
skip_next = False
for k, iterable in itertools.groupby(diff_data, None):
rng = list(iterable)
step = rng[0]
if skip_next:
skip_next = False
rng.pop()
if len(rng) == 0:
continue
elif len(rng) == 1:
ranges.append('%d' % data[i])
elif step == 1:
ranges.append('%d-%d' % (data[i], data[i+len(rng)]+step))
i += 1
skip_next = True
else:
ranges.append('%d-%d:%d' % (data[i], data[i+len(rng)]+step, step))
i += 1
skip_next = True
i += len(rng)
if len(rng) == 0 or len(rng) == 1:
ranges.append('%d' % data[i])
return ','.join(ranges)
# --- Kaidence Solution --------------------------------------------------------
# With a minor edit for use in range function
def list_to_ranges2(data):
onediff = np.diff(data)
twodiff = np.diff(onediff)
increments, breakingindices = [], []
for i in range(len(twodiff)):
if twodiff[i] != 0:
breakingindices.append(i+2) # Correct index because of the two diffs
increments.append(onediff[i]) # Record the increment for this section
# Increments and breakingindices should be the same size
str_list = []
start = data[0]
for i in range(len(breakingindices)):
str_list.append("%d-%d:%d" % (start,
data[breakingindices[i]-1] + increments[i],
increments[i]))
start = data[breakingindices[i]]
str_list.append("%d-%d:%d" % (start,
data[len(data)-1] + onediff[len(onediff)-1],
onediff[len(onediff)-1]))
return ','.join(str_list)
# --- Rik Poggi Solution -------------------------------------------------------
# With a minor edit for use in range function
def helper(lst):
if len(lst) == 1:
return str(lst[0]), []
if len(lst) == 2:
return ','.join(map(str,lst)), []
step = lst[1] - lst[0]
#for i,x,y in itertools.izip(itertools.count(1), lst[1:], lst[2:]):
for i,x,y in itertools.izip(itertools.count(1),
itertools.islice(lst, 1, None, 1),
itertools.islice(lst, 2, None, 1)):
if y-x != step:
if i > 1:
return '{}-{}:{}'.format(lst[0], lst[i]+step, step), lst[i+1:]
else:
return str(lst[0]), lst[1:]
return '{}-{}:{}'.format(lst[0], lst[-1]+step, step), []
def list_to_ranges3(lst):
result = []
while lst:
partial,lst = helper(lst)
result.append(partial)
return ','.join(result)
# --- Rik Poggi Solution 2 -----------------------------------------------------
def formatter(start, end, step):
#return '{}-{}:{}'.format(start, end, step)
return '{}-{}:{}'.format(start, end + step, step)
def list_to_ranges4(lst):
n = len(lst)
result = []
scan = 0
while n - scan > 2:
step = lst[scan + 1] - lst[scan]
if lst[scan + 2] - lst[scan + 1] != step:
result.append(str(lst[scan]))
scan += 1
continue
for j in xrange(scan+2, n-1):
if lst[j+1] - lst[j] != step:
result.append(formatter(lst[scan], lst[j], step))
scan = j+1
break
else:
result.append(formatter(lst[scan], lst[-1], step))
return ','.join(result)
if n - scan == 1:
result.append(str(lst[scan]))
elif n - scan == 2:
result.append(','.join(itertools.imap(str, lst[scan:])))
return ','.join(result)
# --- Test Function ------------------------------------------------------------
def test_data(data, f_to_test):
data_str = f_to_test(data)
_list = []
for r in data_str.replace('-',':').split(','):
r = [int(a) for a in r.split(':')]
if len(r) == 1:
_list.extend(r)
elif len(r) == 2:
_list.extend(range(r[0], r[1]))
else:
_list.extend(range(r[0], r[1], r[2]))
return _list
# --- Timing Tests -------------------------------------------------------------
# Generate some sample data...
data_list = []
for i in range(5):
# Note: using the "4000" and "5000" values below, the relative density of
# the data can be changed. This has a huge effect on the results
# (particularly on the results for list_to_ranges3 which uses recursion).
data_list.append(sorted(list(set([random.randint(1,4000) for a in \
range(random.randint(5,5000))]))))
testfuncs = list_to_ranges1, list_to_ranges2, list_to_ranges3, list_to_ranges4
for f in testfuncs:
print '\n', f.__name__
for i, data in enumerate(data_list):
t = timeit.Timer('f(data)', 'from __main__ import data, f')
#print f(data)
print i, data==test_data(data, f), round(t.timeit(200), 3)
This is most likely what you are looking for.
Edit: I see you already found the post. My apologies.
To help with the second part, I've tinkered a bit myself. This is what I came up with:
from numpy import diff
data = [ 1, 3, 5, 7, 8, 9, 10, 11, 13, 15, 17 ]
onediff, twodiff = diff(data), diff(diff(data))
increments, breakingindices = [], []
for i in range(len(twodiff)):
if twodiff[i] != 0:
breakingindices.append(i+2) # Correct index because of the two diffs
increments.append(onediff[i]) # Record the increment for this section
# Increments and breakingindices should be the same size
str_list = []
start = data[0]
for i in range(len(breakingindices)):
str_list.append("%d-%d:%d" % (start, data[breakingindices[i]-1], increments[i]))
start = data[breakingindices[i]]
str_list.append("%d-%d:%d" % (start, data[len(data)-1], onediff[len(onediff)-1]))
print str_list
For the given input list, this gives: ['1-7:2', '8-11:1', '13-17:2']. The code could do with a bit of cleanup, but this sorts with your problem assuming the grouping can be done sequentially.
{caution: for [1,2,3,5,6,7] this gives ['1-3:1', '5-5:2', '6-7:1'] instead of ['1-3:1', '5-7:1']}
This is similar to versions that handle the step-size-of-one case enumerated here but also handles the singleton (elements with no more than 2 elements in a sequence or repeated elements) and non-unitary step sizes (including negative step sizes). It also does not drop duplicates for lists like [1, 2, 3, 3, 4, 5].
As for runtime: it's done before you blink.
def ranges(L):
"""return a list of singletons or ranges of integers, (first, last, step)
as they occur sequentially in the list of integers, L.
Examples
========
>>> list(ranges([1, 2, 4, 6, 7, 8, 10, 12, 13]))
[1, (2, 6, 2), 7, (8, 12, 2), 13]
>>> list(ranges([1,2,3,4,3,2,1,3,5,7,11,1,2,3]))
[(1, 4, 1), (3, 1, -1), (3, 7, 2), 11, (1, 3, 1)]
"""
if not L:
return []
r = []
for i in L:
if len(r) < 2:
r.append(i)
if len(r) == 2:
d = r[1] - r[0]
else:
if i - r[1] == d:
r[1] = i
else:
if r[1] - r[0] == d:
yield(r.pop(0))
r.append(i)
d = r[1] - r[0]
else:
yield(tuple(r+[d]))
r[:] = [i]
if len(r) == 1:
yield(r.pop())
elif r[1] - r[0] == d:
for i in r:
yield i
else:
yield(tuple(r+[d]))
The raw output can be modified as desired, e.g. actual range instances can be created.
def sranges(i):
"""return pretty string for output of ranges.
Examples
========
>>> sranges([1,2,4,6,7,8,10,12,13,15,16,17])
'1, range(2, 8, 2), 7, range(8, 14, 2), 13, range(15, 18)'
"""
out = []
for i in ranges(i):
if type(i) is int:
out.append(str(i))
elif i[-1] == 1:
if i[0] == 0:
out.append('range(%s)'%(i[1] + 1))
else:
out.append('range(%s, %s)'%(i[0], i[1] + 1))
else:
out.append('range(%s, %s, %s)'%(i[0], i[1] + i[2], i[2]))
return ', '.join(out)
This function should do what you need without requiring any imports.
def listToRanges(self, intList):
ret = []
for val in sorted(intList):
if not ret or ret[-1][-1]+1 != val:
ret.append([val])
else:
ret[-1].append(val)
return ",".join([str(x[0]) if len(x)==1 else str(x[0])+"-"+str(x[-1]) for x in ret])