I need to add a counter of total comparisons for my Insertion Sort program but I don't know why I'm getting a total of 0 comparisons!
I know that the comparisons output should be 15 (for my specific array) not 0.
This is my code so far:
def insertionSort(values):
k = 0
n = len(values) - 1
comparisons = 0
while k+1 <= n:
i = k
while values[i] > values[i+1]:
temp = values[i]
values[i] = values[i+1]
values[i+1] = temp
comparisons += 1 #I think this is wrong
k = k + 1
return comparisons, values
What am I doing wrong?
I just checked your code and its not serving the purpose for sorting [7,5,4,6].
Here's a modified version -
def insertionSort_mod(values):
k = 0
n = len(values) - 1
comparisons = 0
while k+1 <= n:
i = k+1
curr_val = values[i]
comparisons += 1
while i>0 and values[i-1] > curr_val:
values[i] = values[i-1]
i=i-1
comparisons += 1
values[i] = curr_val
k = k + 1
return comparisons, values
print insertionSort_mod( [1, 2, 3, 55, 5, 6, 8, 7, 9, 111])
Outputs this:
(15, [1, 2, 3, 5, 6, 7, 8, 9, 55, 111])
In your context k+1 should be the current index so i should be k+1 and should be compared to the previous value i-1
Hope this works
def insertion(a,length):
count=0
for i in range(1,length):
key=a[i]
jj=i
while(jj>0 and a[jj-1]>key):
a[jj]=a[jj-1]
jj=jj-1
count += 1
a[jj]=key
print count
The no. of swaps would be equal to the number of elements for which you shift the elements using the while loop. So, you could use a flag variable inside the while loop, to check if the loop runs for each element, and increase the counter variable by 1, every time the flag variable shows swapping.
Related
hello I am struggling with this problem for school and can't get my code to do what it needs to solve this. The question is: Define an element of a list of items to be a dominator if every element to its right (not just the one
element that is immediately to its right) is strictly smaller than that element. It wants me to count how many denominators are in the list.
def extract_increasing(digits):
countDem = 0
#check and see if there is anything in the list
if not digits:
return 0
#compare the first element to the one on the right of it
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
return countDem
The code below should check if a number in the list is a dominator.
def is_dominator(lst, idx):
for i in range(idx + 1, len(lst)):
if lst[i] >= lst[idx]:
return False
return True
digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in digits:
print(is_dominator(digits, i))
The error in your code is that you're adding one for the counter every time the next value meets the condition.
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
Every time digits[x] > digits[y] is met you add one to your counter. You should only add one once you checked that all values to the right meet the condition.
isDem = False
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
isDem = True
else:
isDem = False
#Once you went through all the values to the right you can add one to the counter
if isDem ==True:
countDem += 1
Hope that helps!
You start in the last element, and save always the max_element in every iteration, then you know always if exist some number grater than the current number. This is a little more efficient because it runs through the array only once.
def dominator(li: list):
sol = 0
max_number = -math.inf
for i in range(len(li)-1, -1,-1):
if li[i] > max_number:
sol+=1
max_number = li[i]
return sol
Try list comprehension
lst = [0, 10, 2, 6, 7]
new_lst = [v for k,v in enumerate(lst) if all(v > x for x in lst[k+1:])]
# [10, 7]
Update
def extract_increasing(digits: list) -> int:
countDem = 0
for x, y in enumerate(digits):
if all(y > a for a in digits[x+1:]):
countDem += 1
return countDem
lst = [0, 10, 2, 6, 7]
extract_increasing(lst) # -> 2
I was trying to count the number of shifts that happen with merge sort when I ran into a problem. When I run the code with multiple arrays, for some reason one of the arrays states that 3 shifts happened when in reality its 4. I will greatly appreciate it if anyone can help me figure out what the problem is. Thanks
def mergeSort(arr):
count = x = y = 0
result =[]
arrayLength = len(arr)
if arrayLength <= 1:
return count
middle = arrayLength // 2
left = arr[:middle]
right = arr[middle:]
leftLength = len(left)
rightLength = len(right)
count += mergeSort(left)
count += mergeSort(right)
while x < leftLength and y < rightLength:
if left[x] <= right[y]:
result.append(left[x])
x += 1
else:
result.append(right[y])
y += 1
count += len(left[x:])-x
return count
arr = [1,20,6,4,5]
print(mergeSort(arr))
arr2 = [4,3,2,1]
print(mergeSort(arr2))
arr3=[1, 1, 1, 2, 2]
print(mergeSort(arr3))
arr4=[2, 1, 3, 1, 2]
print(mergeSort(arr4))
arr5 = [12,15,1,5,6,14,11]
print(mergeSort(arr5))
arr6=[3, 5, 7, 11, 9]
print(mergeSort(arr6))
result = mergeSort(arr)
print(result)
You have two bugs:
Your len(left[x:])-x subtracts x twice.
You're not actually sorting the given array but just building a result that you never use. The sorting is important for the upper call levels to count correctly.
Fixed and with better testing (Try it online!):
from itertools import combinations
def mergeSort(arr):
count = x = y = 0
arrayLength = len(arr)
if arrayLength <= 1:
return count
middle = arrayLength // 2
left = arr[:middle]
right = arr[middle:]
leftLength = len(left)
rightLength = len(right)
count += mergeSort(left)
count += mergeSort(right)
for write in range(arrayLength):
if y == rightLength or x < leftLength and left[x] <= right[y]:
arr[write] = left[x]
x += 1
else:
arr[write] = right[y]
y += 1
count += len(left) - x
return count
def naive(arr):
return sum(a > b for a, b in combinations(arr, 2))
def test(arr):
expect = naive(arr)
result = mergeSort(arr)
print(result == expect, expect, result, arr)
test([1, 20, 6, 4, 5])
test([4, 3, 2, 1])
test([1, 1, 1, 2, 2])
test([2, 1, 3, 1, 2])
test([12, 15, 1, 5, 6, 14, 11])
test([3, 5, 7, 11, 9])
I presume your question really isn't about counting the recursion and more about figuring out why your algorithm is not correct. When you're checking for while x < leftLength and y < rightLength: you are dropping items at the end of one of the lists. This should be an or not an and to make sure you are doing ALL items in both left and right lists. Something like this:
while x < leftLength or y < rightLength:
if x == leftLength:
result.append(right[y])
y += 1
continue
if y == rightLength:
result.append(left[x])
x += 1
continue
if left[x] <= right[y]:
result.append(left[x])
x += 1
else:
result.append(right[y])
y += 1
return result
and you can't return the counts like Frank said because you stop doing the merge sort, and writing back to arr doesn't work in python as it's not an in/out variable. You would have to have a global variable outside the class to do the counting.
So basically I want to figure out how to reverse a counting sort. This is the code I found:
def countingSort(myList):
maxValue = 0
for i in range(len(myList)):
if myList[i] > maxValue:
maxValue = myList[i]
buckets = [0] * (maxValue + 1)
for i in myList:
buckets[i] += 1
i = 0
for j in range(maxValue + 1):
for a in range(buckets[j]):
myList[i] = j
i += 1
return myList
if __name__ == '__main__':
sortedList = countingSort([1,23,4,5,6,7,8])
print(sortedList)
So I decided to change line 16 to this:
i -= 1
The results are very close, but I get this:
[1, 23, 8, 7, 6, 5, 4]
My issue is that 1 is before 23 for some reason. I tried reverting some of the other signs, but it usually results in an out of bounds error. Any ways to work around it?
Reverse the range that j iterates through so you start from high numbers then end with low numbers:
for j in range(maxValue, -1, -1):
With i -= 1 you're writing at indices 0, -1, -2, -3 etc. That is, you write the smallest number (the 1) at the front, and then the remaining numbers from the back backwards. You can make it work by starting with i = -1.
A peak in an array is any value that is no smaller than its two adjacent neighbors. If its the first or last element of the array we only need to compare with one neighbor. I wrote some recursive code to find peaks in python which is fast in principle as it runs in O(log n) time:
def peak_recursive(A):
n = len(A)
if n == 1:
print("n == 1")
return 0
if n == 2:
return 0 if A[0] >= A[1] else 1
if A[n//2] >= A[n//2+1] and A[n//2] >= A[n//2 - 1]:
return n//2
elif A[n//2 - 1] >= A[n//2]:
return peak_recursive(A[0:n//2])
else:
return n//2 + 1 + peak_recursive(A[n//2+1:])
However, python isn't very good at recursion so I think it would be better iteratively. How can I convert this to iterative code?
Update
It turns out this code is very slow as A[n//2+1:] and A[0:n//2] make copies of the lists.
One simple solution is to iterate over the list and compare the previous and next values. You also need to consider the first element and last element situation:
# Import numpy to create random vector
import numpy as np
l = np.random.randint(0, 10, 20).tolist()
print(l)
# [6, 7, 2, 7, 1, 4, 2, 8, 9, 1, 3, 7, 0, 5, 4, 6, 9, 0, 5, 7]
def peak_iter(A):
out = [] # Output
n = len(A) # Number element A
for i, curr in enumerate(A): # Iterate over A
condi = True # Peak condition
if i > 0: condi = A[i-1] < curr # Update peak condition from previous value
if i < n - 1: condi = curr > A[i + 1] # Update peak condition from next value
if condi: out.append(curr) # If condition satisfied: add value to output
return out
print(peak_iter(l))
# [7, 7, 4, 9, 7, 5, 9, 7]
As well, you can easily get the index instead of the value (or the both) by replacing out.append(curr) with out.append(i) or out.append([curr, i]).
Update:
If you just want to get the one peak, you can exit the function after finding one element meeting condition. The following returns the first values:
def peak_iter_first(A):
out = None # Output
n = len(A) # Number element A
for i, curr in enumerate(A): # Iterate over A
condi = True # Peak condition
if i > 0: condi = A[i-1] < curr # Update peak condition from previous value
if i < n - 1: condi = curr > A[i + 1] # Update peak condition from next value
if condi: return curr # If condition satisfied: return value
return out
print(peak_iter_first(l))
# 7
Update 2:
The translation of the recursive function to an iterative one might looks something like this:
def peak_iterative(A):
n = len(A)
out = 0
while True:
if n == 1:
out += 0
break
if n == 2:
out += 0 if A[0] >= A[1] else 1
break
if A[n//2] >= A[n//2+1] and A[n//2] >= A[n//2 - 1]:
out += n//2
break
elif A[n//2 - 1] >= A[n//2]:
A = A[0:n//2]
else:
out += n//2 + 1
A = A[n//2+1:]
n = len(A)
return out
Who's the faster ?
The recursive one is a bit faster than the iterative method:
import timeit
import functools
# Bigger array (2000 elements)
l = np.random.randint(0, 10, 2000).tolist()
t = timeit.Timer(functools.partial(peak_recursive, l))
print (t.timeit(50))
# 3.950000000019216e-05
t = timeit.Timer(functools.partial(peak_iterative, l))
print (t.timeit(50))
# 7.049999999986234e-05
Hope that helps !
I am trying to write a selection sort algorithm for sorting lists of numbers from lowest to highest.
def sortlh(numList):
if type(numList) != list:
print("Input must be a list of numbers.")
else:
inf = float("inf")
sortList = [0]*len(numList)
count = 0
while count < len(numList):
index = 0
indexLowest = 0
lowest = numList[index]
while index < (len(numList) - 1):
if numList[index + 1] < numList[index]:
lowest = numList[index + 1]
indexLowest = index + 1
index = index + 1
else:
index = index + 1
sortList[count] = lowest
numList[indexLowest] = inf
count = count + 1
return sortList
When I run this code on:
sortlh([9,8,7,6,5,4,3,2,1])
I get (as expected):
[1, 2, 3, 4, 5, 6, 7, 8, 9]
However, when I try another example, I get:
sortlh([1,3,2,4,5,7,6,9,8])
[8, 6, 9, 2, 4, 5, 7, 1, 3]
Does anyone see what is going on here?
Here is how I would suggest rewriting your program.
def sortlh(lst_input):
lst = list(lst_input) # make a copy of lst_input
i = 0
while i < len(lst):
j = i + 1
i_lowest = i
lowest = lst[i_lowest]
while j < len(lst):
if lst[j] < lowest:
i_lowest = j
lowest = lst[i_lowest]
j += 1
lst[i], lst[i_lowest] = lst[i_lowest], lst[i] # swap
i += 1
return lst
test = [9,8,7,6,5,4,3,2,1]
assert sortlh(test) == sorted(test)
test = [1,3,2,4,5,7,6,9,8]
assert sortlh(test) == sorted(test)
We don't test the type of the input. Anything that acts like a list will work, and even an iterator will work.
We don't "mutate" the original input list. We only work on a copy of the data.
When we find the lowest number, we swap it with the first number, and then only look at the remaining numbers. Thus we have less work to do on each loop as we have fewer and fewer unsorted numbers.
EDIT:
If you are a beginner, this part might seem too tricky. If it confuses you or you don't like it, just ignore it for now.
This is a more-advanced way to solve this problem in Python. The inner loop simply finds the lowest number and the index of the lowest number. We can use the Python built-in function min() to do this!
We build a "generator expression" that loops over the list, yielding up tuples. Each tuple is the number and its position. Since we want lower numbers to sort lower, we put the number first in the tuple so that min() can properly compare the tuples. Then min() will find the lowest tuple and we get the value and index.
Also, the outer loop is now a for loop with enumerate rather than a while loop using indexing.
def sortlh(lst_input):
lst = list(lst_input) # make a copy of lst_input
for i, x in enumerate(lst):
lowest, i_lowest = min((n, j) for j, n in enumerate(lst) if j >= i)
lst[i], lst[i_lowest] = lst[i_lowest], lst[i] # swap
return lst
test = [9,8,7,6,5,4,3,2,1]
assert sortlh(test) == sorted(test)
test = [1,3,2,4,5,7,6,9,8]
assert sortlh(test) == sorted(test)