Why isn't my insertion sort working? - python

I am trying to implement an insertion sort from reading the vague description, so I may very well be going about it the wrong way. However, from what I can see this should work:
import random
arr = []
c_num = 0
for i in range(0, 10):
arr.append(random.randint(0, 10))
def InsertionSort(unsorted_array, current_num):
smallest_num = unsorted_array[current_num]
smallest_num_index = 0
isSorted = True
for i in range(current_num, len(unsorted_array) - 1):
if unsorted_array[i] < smallest_num:
smallest_num = unsorted_array[i]
smallest_num_index = i
isSorted = False
if isSorted:
return unsorted_array
else:
temp = unsorted_array[current_num]
unsorted_array[current_num] = smallest_num
unsorted_array[smallest_num_index] = temp
current_num += 1
return InsertionSort(unsorted_array, current_num)
print(InsertionSort(arr, c_num))
It will sort the first few elements, and then just print out the now slightly-sorted array, but I can't see what I am missing to get the whole thing sorted.
Sample:
input = [9, 8, 5, 4, 5, 8, 2, 10, 7, 5]
output = [2, 4, 5, 8, 5, 8, 9, 10, 7, 5]

Related

How find all pairs equal to N in a list

I have a problem with this algorithm- I have to find pairs in list:
[4, 8, 9, 0, 12, 1, 4, 2, 12, 12, 4, 4, 8, 11, 12, 0]
which are equal to 12. The thing is that after making a pair those numbers (elements) can not be used again.
For now, I have code which you can find below. I have tried to delete numbers from the list after matching, but I feel that there is an issue with indexing after this.
It looks very easy but still not working. ;/
class Pairs():
def __init__(self, sum, n, arr ):
self.sum = sum
self.n = n
self.arr = arr
def find_pairs(self):
self.n = len(self.arr)
for i in range(0, self.n):
for j in range(i+1, self.n):
if (self.arr[i] + self.arr[j] == self.sum):
print("[", self.arr[i], ",", " ", self.arr[j], "]", sep = "")
self.arr.pop(i)
self.arr.pop(j-1)
self.n = len(self.arr)
i+=1
def Main():
sum = 12
arr = [4, 8, 9, 0, 12, 1, 4, 2, 12, 12, 4, 4, 8, 11, 12, 0]
n = len(arr)
obj_Pairs = Pairs(sum, n, arr)
obj_Pairs.find_pairs()
if __name__ == "__main__":
Main()
update:
Thank you guys for the fast answers!
I've tried your solutions, and unfortunately, it is still not exactly what I'm looking for. I know that the expected output should look like this: [4, 8], [0, 12], [1, 11], [4, 8], [12, 0]. So in your first solution, there is still an issue with duplicated elements, and in the second one [4, 8] and [12, 0] are missing. Sorry for not giving output at the beginning.
With this problem you need to keep track of what numbers have already been tried. Python has a Counter class that will hold the count of each of the elements present in a given list.
The algorithm I would use is:
create counter of elements in list
iterate list
for each element, check if (target - element) exists in counter and count of that item > 0
decrement count of element and (target - element)
from collections import Counter
class Pairs():
def __init__(self, target, arr):
self.target = target
self.arr = arr
def find_pairs(self):
count_dict = Counter(self.arr)
result = []
for num in self.arr:
if count_dict[num] > 0:
difference = self.target - num
if difference in count_dict and count_dict[difference] > 0:
result.append([num, difference])
count_dict[num] -= 1
count_dict[difference] -= 1
return result
if __name__ == "__main__":
arr = [4, 8, 9, 0, 12, 1, 4, 2, 12, 12, 4, 4, 8, 11, 12, 0]
obj_Pairs = Pairs(12, arr)
result = obj_Pairs.find_pairs()
print(result)
Output:
[[4, 8], [8, 4], [0, 12], [12, 0], [1, 11]]
Demo
Brief
If you have learned about hashmaps and linked lists/deques, you can consider using auxiliary space to map values to their indices.
Pro:
It does make the time complexity linear.
Doesn't modify the input
Cons:
Uses extra space
Uses a different strategy from the original. If this is for a class and you haven't learned about the data structures applied then don't use this.
Code
from collections import deque # two-ended linked list
class Pairs():
def __init__(self, sum, n, arr ):
self.sum = sum
self.n = n
self.arr = arr
def find_pairs(self):
mp = {} # take advantage of a map of values to their indices
res = [] # resultant pair list
for idx, elm in enumerate(self.arr):
if mp.get(elm, None) is None:
mp[elm] = deque() # index list is actually a two-ended linked list
mp[elm].append(idx) # insert this element
comp_elm = self.sum - elm # value that matches
if mp.get(comp_elm, None) is not None and mp[comp_elm]: # there is no match
# match left->right
res.append((comp_elm, elm))
mp[comp_elm].popleft()
mp[elm].pop()
for pair in res: # Display
print("[", pair[0], ",", " ", pair[1], "]", sep = "")
# in case you want to do further processing
return res
def Main():
sum = 12
arr = [4, 8, 9, 0, 12, 1, 4, 2, 12, 12, 4, 4, 8, 11, 12, 0]
n = len(arr)
obj_Pairs = Pairs(sum, n, arr)
obj_Pairs.find_pairs()
if __name__ == "__main__":
Main()
Output
$ python source.py
[4, 8]
[0, 12]
[4, 8]
[1, 11]
[12, 0]
To fix your code - few remarks:
If you iterate over array in for loop you shouldn't be changing it - use while loop if you want to modify the underlying list (you can rewrite this solution to use while loop)
Because you're iterating only once the elements in the outer loop - you only need to ensure you "popped" elements in the inner loop.
So the code:
class Pairs():
def __init__(self, sum, arr ):
self.sum = sum
self.arr = arr
self.n = len(arr)
def find_pairs(self):
j_pop = []
for i in range(0, self.n):
for j in range(i+1, self.n):
if (self.arr[i] + self.arr[j] == self.sum) and (j not in j_pop):
print("[", self.arr[i], ",", " ", self.arr[j], "]", sep = "")
j_pop.append(j)
def Main():
sum = 12
arr = [4, 8, 9, 0, 12, 1, 4, 2, 12, 12, 4, 4, 8, 11, 12, 0]
obj_Pairs = Pairs(sum, arr)
obj_Pairs.find_pairs()
if __name__ == "__main__":
Main()

Equal step-slicing a list by keeping minimum and maximum

I need to get an "equal" step slicing from a linear integer list (which could not start from 0), but with the following requirements:
the last value (the maximum) always has to appear
there must not be an interval lower than the step (most importantly, between the next to last and last values)
given the point above, some intervals might be higher than the step, and those intervals must be placed equally between the resulting list
only standard library functions should be used (no numpy)
Some examples:
with a list from range(10) and step 2, the result should be one of the following:
[0, 2, 5, 7, 9]
[0, 2, 4, 7, 9]
with range(21) and step 3:
[0, 3, 7, 10, 14, 17, 20]
with range(1, 22) and step 3:
[1, 4, 8, 11, 15, 18, 21]
Right now I've got something similar to this, which obviously does not work properly:
def getSlices(l, s):
skipCount = (len(l) - 1) % s
divCount = int(len(l) / (skipCount + 1))
o = []
for delta, skip in enumerate(range(skipCount + 1)):
o.extend(l[skip * divCount + delta:(skip + 1) * divCount + delta:s])
return o
>>> getSlices(list(range(21)), 3)
[0, 3, 6, 8, 11, 14, 16, 19]
I know I could just cycle through all values, skip by correlating enumerate indexes and steps, and add a "delta" as soon as a new "portion" of the list is reached, but that doesn't seem the most performing solution.
I think this could go the way you want. Hope it helps.
def getSlices(l, step):
init = l[0]
last = l[-1] # exclude last element (remove as you want)
slices = (last-init) // step + 1
mod = (last-init) % step
even = mod // 2
mid = slices // 2 - 1
even_start = mid - even
even_end = mid + mod - even
final = []
val = init
for i in range(slices):
final.append(val)
val += step
# Distribute mod unitary in the middle
if slices-1 >= mod:
if mod > 0 and (even_start <= i <= even_end):
val += 1
# In case is the middle don't change it
if i == mid:
val += - 1
# Distribute mod evenly all across the slices
else:
val += mod // (slices-1)
# In case a there is mod left, place it just in the middle
if i == mid:
val += mod % (slices-1)
return final
# Examples:
#
# getSlices(list(range(10)), 2)
# [0, 2, 4, 7, 9]
#
# getSlices(list(range(21)), 3)
# [0, 3, 7, 10, 14, 17, 20]
#
# getSlices(list(range(1, 22)), 3)
# [1, 4, 8, 11, 15, 18, 21]
#
# getSlices(list(range(36)), 10)
# [0, 11, 24, 35]

Linear sort by swapping adjacent numbers

I've been trying to accomplish a simple linear sort that will, in this case, make a swap at every index except for when it reaches the end. Kindly help. (the while loop might be unnecessary at this point)
array = list(range(9, -1, -1))
has_flipped = True
while has_flipped:
for num in array:
if array.index(num) == (len(array) - 1):
continue
if num > array[array.index(num) + 1]:
container = array[array.index(num) + 1]
array[array.index(num) + 1] = num
num = container
has_flipped = False
has_flipped = not has_flipped
I expect a list with the numbers 0 through 9 but I instead get 9, 9, 7, 7, 5, 5, 3, 3, 1, 1.
You do not swap rightly. You never assign to array locations in right manner. As the other answerer explains...
num = container
...does not assign to an array location.
Moreover, the while loop is not required. Here is a more compact way of doing the same:
array = list(range(9, -1, -1))
ln = len(array)
for num in array:
if num > array[ln-1]:
container = array[ln-1]
array[ln-1] = num
array[array.index(num)] = container
ln -= 1
print(array)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

efficiently converting a bijection to cycle notation

Consider the following problem. Given is an array P of length n representing a bijection. That is element 0 <= i < n is mapped to P[i].
Given that P is a permutation, it can be written in cycle notation as described for example here.
For example if
P = [16, 3, 10, 6, 5, 9, 1, 19, 13, 14, 7, 0, 2, 8, 12, 4, 17, 15, 11, 18]
then the result in cycle notation would be
[(16, 17, 15, 4, 5, 9, 14, 12, 2, 10, 7, 19, 18, 11, 0), (3, 6, 1), (13, 8)]
Following is a Python method accomplishing this
def toCycle(p):
covered = cur = 0
perm = []
n = len(p)
done = [0]*n
while covered < n:
while cur < n and done[cur] == -1:
cur+=1
cycle = [p[cur]]
sec = p[p[cur]]
done[p[cur]] = -1
done[cur] = -1
covered+=1
while sec != cycle[0]:
cycle.append(sec)
done[sec] = -1
sec = p[sec]
covered+=1
perm+=[tuple(cycle)]
return perm
The algorithm clearly runs in linear time (each element of done/p is accessed a constant number of times) and hence there is not much that can be done asymptotically.
As I have to use this method on a large number of large permutations I was wondering
Can you make it faster? Do you have any suggestions for performance
improvement?
def cycling(p, start, done):
while not done[e]:
done[e] = True
e = p[e]
yield e
def toCycle(p):
done = [False]*len(p)
cycles = [tuple(cycling(p, 0, done))]
while not all(done):
start = done.index(False)
cycles.append(tuple(cycling(p, start, done)))
return cycles
With your example my code runs about 30% faster than yours.

Python: A program to find the LENGTH of the longest run in a given list?

Q: A run is a sequence of adjacent repeated values. Given a list, write a function to
determine the length of the longest run. For example, for the sequence [1, 2, 5, 5, 3, 1, 2, 4, 3, 2, 2, 2, 2, 3, 6, 5, 5, 6, 3, 1], the longest run is 4.
I am having trouble with this, I've written a code that finds the longest run consist of the number '2' but have yet to get the length of the run which is 4.
Here is my code so far (i've commented out a part that i was working on but don't pay attention to it):
# longestrun.py
# A function to determine the length of the longest run
# A run is a sequence of adjacent repeated values.
def longestrun(myList):
result = None
prev = None
size = 0
max_size = 0
for i in myList:
if i == prev:
size += 1
if size > max_size:
result = i
max_size = size
else:
size = 0
prev = i
return result
def main():
print("This program finds the length of the longest run within a given list.")
print("A run is a sequence of adjacent repeated values.")
myString = input("Please enter a list of objects (numbers, words, etc.) separated by
commas: ")
myList = myString.split(',')
longest_run = longestrun(myList)
print(">>>", longest_run, "<<<")
main()
Help please!!! :(((
You can do this in one line using itertools.groupby:
import itertools
max(sum(1 for _ in l) for n, l in itertools.groupby(lst))
This should work if you do not want to use itertools and imports.
a=[1, 2, 5, 5, 3, 1, 2, 4, 3, 2, 2, 2, 2, 3, 6, 5, 5, 6, 3, 1]
def longestrun(myList):
result = None
prev = None
size = 0
max_size = 0
for i in myList:
if i == prev:
print (i)
size += 1
if size > max_size:
print ('******* '+ str(max_size))
max_size = size
else:
size = 0
prev = i
print (max_size+1)
return max_size+1
longestrun(a)
Just another way of doing it:
def longestrun(myList):
sett = set()
size = 1
for ind, elm in enumerate(myList):
if ind > 0:
if elm == myList[ind - 1]:
size += 1
else:
sett.update([size])
size = 1
sett.update([size])
return max(sett)
myList = [1, 2, 5, 5, 3, 1, 2, 4, 3, 2, 2, 2, 2, 3, 6, 5, 5, 6, 3, 1]
print longestrun(myList)
def getSublists(L,n):
outL=[]
for i in range(0,len(L)-n+1):
outL.append(L[i:i+n])
return outL
def longestRun(L):
for n in range(len(L), 0, -1):
temp=getSublists(L,n)
for subL in temp:
if subL==sorted(subL):
return len(subL)
def longestrun(myList):
size = 1
max_size = 0
for i in range(len(myList)-1):
if myList[i+1] = myList[i]:
size += 1
else:
size = 1
if max_size<size:
max_size = size
return size
Remove the .split() from myList in main() and you're good to go with this.
As an update to David Robinson's answer, it is now (Python 3.4) possible to return 0 on an empty sequence (instead of raising ValueError):
import itertools
max((sum(1 for _ in l) for n, l in itertools.groupby(lst)), default=0)

Categories

Resources