I am trying my hand at algorithms for the first time and tried to create a sequential sorting algorithm. I have come up with the code below.
def SelectionSort(my_list):
prev_num = None
counter = 0
new_list = []
index_num = 0
new_list_counter = 0
for i in my_list:
if my_list.index(i) == 0:
prev_num = i
counter += 1
new_list.append(i)
else:
if i > prev_num:
prev_num = i
counter += 1
new_list.append(i)
else:
prev_num = i
for n in new_list:
if i >= n:
new_list_counter += 1
pass
else:
new_list.insert(new_list_counter, i)
index_num = new_list.index(i)
new_list_counter = 0
my_list.remove(i)
my_list.insert(index_num, i)
break
counter += 1
prev_num = i
return my_list
print(SelectionSort([1, 3, 4, 2, 6, 5]))
print(SelectionSort([3, 4, 2, 6]))
print(SelectionSort([3, 7, 4, 1]))
print(SelectionSort([3, 10, 2, 5]))
Whilst it seems to sort the first 3 lists just fine, when it comes to the last list it outputs [2, 3, 10, 5].
Can anyone possible lend a hand?
I am sure the logic of your code could be improved and simplified. But with a small change it's working, at least with your 4 test cases.
My solution is to save "prev_num" with the higest existing number in the list using max(). And only save this value at the end of each loop.
def SelectionSort(my_list):
prev_num = None
counter = 0
new_list = []
index_num = 0
new_list_counter = 0
for i in my_list:
if my_list.index(i) == 0:
prev_num = i
counter += 1
new_list.append(i)
else:
if i > prev_num:
counter += 1
new_list.append(i)
else:
for n in new_list:
if i >= n:
new_list_counter += 1
else:
new_list.insert(new_list_counter, i)
index_num = new_list.index(i)
new_list_counter = 0
my_list.remove(i)
my_list.insert(index_num, i)
break
counter += 1
prev_num = i
prev_num = max(new_list) # <-- This is the key: Save at the end of each loop the highest existing number
return my_list
Improvements:
The renaming of the "prev_num" variable to something like "highest_num".
"elif" could be used instead of "else - if"
Comment the code, not only for the reviewrs, it's useful for yourself too.
Related
Here's the problem. The input is a list of integers. If more than three adjacent numbers appear next to each other they should be dropped and the operation goes again. Kind of similar to the Iphone game, where player needs to pop lines of three or more balls of the same colors. The output should be the count of the balls that will be removed.
The algorithm is as follows. Starting with a sample list of say [3,3,4,4,4,4,3,2].
First iteration should remove the 4,4,4,4 - so the list would become [3,3,3,2], and the intermediary output of removed numbers will be 4.
Second iteration should remove 3,3,3 - so the final list would be [2] and final count of removed numbers - 7.
The first implementation for three consecutive items came from another stackoverflow thread - Remove triplets of adjacent numbers from the list
Here's the working function implementation for exactly 3 consecutive numbers:
def balls(l):
values = l
while len(values) >= 3:
print(values) #for demonstrative purposes of this question
for x in range(0,len(values)-2):
if values[x] == values[x+1] and values[x] == values[x+2]:
values = values[:x] + values[x+3:]
break
else:
break
print(values) #for demonstrative purposes of this question
return len(l) - len(values)
balls([3, 3, 4, 4, 4, 3, 4])
Output:
[3, 3, 4, 4, 4, 3, 4]
[3, 3, 3, 4]
[4]
6
How could I update the implementation to include the more general solution of removing 3+ consecutive numbers. I am thinking about tracking the start and end index of the consecutive duplicates, then subsetting the list. However, not sure how to implement that. Here are the tests that should work.
if __name__ == "__main__":
test1 = [3, 3, 4, 4, 4, 3, 4]
print(balls(test1))
#Output should be 6
test2 = [5, 5, 5, 5, 5, 5, 5, 5]
print(balls(test2))
#Output should be 8
test3 = [5, 7, 8, 3]
print(balls(test3))
#Output should be 0
def remove_consecutive(l, length):
amount = len(l)
count = 1
start = 0
current = l[0]
i = 1
while i < len(l):
if l[i] == current:
count += 1
else:
if count >= length:
for i in range(count):
l.pop(start)
start = 0
i = 0
current = l[0]
else:
start = i
current = l[i]
count = 1
i+=1
if count >= length:
for i in range(count):
l.pop(start)
return amount - len(l)
Wuff, i got it. My brain is kinda stinky lately so it took so long.
Here is my code, it works well. But I think there may be better ways to achieve higher efficiency.
def remove_consecutive(lst):
len_init = len(lst)
contain_tuplets = True
while contain_tuplets:
for i in range(len(lst)-2):
indices_to_pop = []
if lst[i]==lst[i+1]==lst[i+2]:
indices_to_pop.extend([i, i+1, i+2])
for j in range(i+3,len(lst)):
if lst[j] == lst[i]:
indices_to_pop.append(j)
else:
break
[lst.pop(i) for _ in indices_to_pop]
contain_tuplets = True
break
else:
contain_tuplets = False
count_removed_numbers = len_init - len(lst)
return count_removed_numbers, lst
test case1:
lst = [3,3,4,4,4,4,3,2]
remove_consecutive(lst)
output
(7, [2])
test case 2:
lst = [2, 2, 1, 1, 1, 2, 1]
remove_consecutive(lst)
output:
(6, [1])
def remove_consecutive(l, length):
amount = 0
count = 1
current = l[0]
for i in range(1, len(l)):
if l[i] == current:
count += 1
if count > length:
amount += 1
elif count == length:
amount += length
else:
current = l[i]
count = 1
return amount
I am basically trying to return the starting index of a DNA string where A and G form the majority over the next five letters.
See code
def a_g_majority_positions(dna):
### BEGIN SOLUTION
list=[]
for index, item in enumerate(dna):
count=0
x=0
index1=index
while count < 5:
letter=dna[index1]
index1+=1
count+=1
if letter == 'A':
x+=1
elif letter == 'G':
x+=1
if x>=3:
list.append(index)
return list
### END SOLUTION
a_g_majority_positions("AACCGGTTAACCGGTT")
I always get a string index out of range error. The correct answer for the dna at the end is [0,1,4,5,8,9]
Use the count method to count the letters of interest. Start the run of five up until you don't have enough positions left:
def a_g_majority_positions(dna):
lst = []
for start5 in range(len(dna)-4):
five = dna[start5:start5+5]
if five.count('A') + five.count('G') >= 3:
lst.append(start5)
return lst
Or, for the one-statement version, checking each character for being in "AG":
lst = [start5 for start5 in range(len(dna)-4)
if sum(char in "AG"
for char in dna[start5:start5+5]) >= 3]
Output in either case is
[0, 1, 4, 5, 8, 9]
You need to break out of the for loop when there are less than 5 letters left:
def a_g_majority_positions(dna):
result = list()
for index, item in enumerate(dna):
if index + 5 >= len(dna):
break
count = 0
x = 0
while count < 5:
letter = dna[index + count]
count += 1
if letter == 'A':
x += 1
elif letter == 'G':
x += 1
if x >= 3:
result.append(index)
return result
print(a_g_majority_positions("AACCGGTTAACCGGTT"))
Output
[0, 1, 4, 5, 8, 9]
Note
Don't use list as a variable name. It's built-in class and you will introduce hard to find errors by using it as a variable name.
You need to break early from the function when index is greater than len(dna) - 5. Otherwise you try to access dna[len(dna)], which is out of range.
def a_g_majority_positions(dna):
### BEGIN SOLUTION
list=[]
for index, item in enumerate(dna):
count=0
x=0
index1=index
if index > len(dna) - 5:
break
while count < 5:
letter=dna[index1]
index1+=1
count+=1
if letter == 'A':
x+=1
elif letter == 'G':
x+=1
if x>=3:
list.append(index)
return list
### END SOLUTION
a_g_majority_positions("AACCGGTTAACCGGTT")
# Result [0, 1, 4, 5, 8, 9]
I want to eliminate the third argument in my code below which is just an empty array that I think I should be able to create as a local variable within the function itself.
As a bonus, I would also like to build this into a single function although I don't think this can be achieved with the current recursive structure of the code.
I tried creating an empty array as a local variable (See the commented out integers list)
I also tried to create a count variable to increment with each combination found (See the commented out count variable)
def count_combinations(number, integers_available, integers):
coin_set = []
# integers = []
# count = 0
if sum(integers) == number:
coin_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers + [integers_available[0]]):
coin_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
coin_set.append(c)
# count += 1
# return count += 1
return coin_set
def count_total(number, integers_available, integers):
return len(count_combinations(number, integers_available, integers))
# Testing the code
number = 15
integers_available = [1, 5, 10]
print(count_total(number, integers_available, []))
I expect to get the same results but with fewer arguments in the function since one of the arguments will have been switched to a local variable instead.
As discussed in the comments, a dynamic programming approach may be more Pythonic here.
from collections import Counter
def ways(total, coins=(1,2,5,10,20,50,100)):
counts = [[Counter()]] + [[] for _ in range(total)]
for coin in coins:
for i in range(coin, total + 1):
counts[i] += [c + Counter({coin: 1}) for c in counts[i-coin]]
return counts[total]
Demo:
>>> ways(15, coins=(1,5,10))
[Counter({1: 15}),
Counter({1: 10, 5: 1}),
Counter({1: 5, 5: 2}),
Counter({5: 3}),
Counter({1: 5, 10: 1}),
Counter({5: 1, 10: 1})]
>>> len(ways(15, coins=(1,5,10)))
6
Without regard to the algorithm or the goal of the code, here's how you can tidy it up a little by making it into a single function and giving integers parameter a default value:
def count_total(number, integers_available):
def count_combinations(number, integers_available, integers=[]):
coin_set = []
# integers = []
# count = 0
if sum(integers) == number:
coin_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers + [integers_available[0]]):
coin_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
coin_set.append(c)
# count += 1
# return count += 1
return coin_set
return len(count_combinations(number, integers_available))
# Testing the code
number = 15
integers_available = [1, 5, 10]
print(count_total(number, integers_available))
I am trying to find out smallest positive number not present in the list a
def smallitem(a):
a = sorted(set(a))
lst = []
for item in a:
item + = 1
if item not in a:
lst.append(item)
continue
mst = []
for item in lst:
if item < 1:
item += 1
if item not in a:
mst.append(item)
continue
n = 0
if mst:
n = min(mst)
return n or min(lst)
I think I have got the solution but it doesnt look correct to me the way I have done it.
for example:
smallitem([-1, -3]) # 1
smallitem([1,3,6,4,1,2, 87]) # 5
You can convert the list to a set and then keep incrementing a positive integer from 1 until it is not found in the set:
def smallitem(a):
set_a = set(a)
i = 1
while i in set_a:
i += 1
return i
Perhaps there's a lighter way do this.
The time complexity is always O(n).
def small_item(a):
s = set(a)
for i in range(1, max(s)):
if i not in s:
return i
return max(max(s) + 1, 1)
print small_item([1,3,6,4,1,2, 87])
print small_item([-1, -3])
Here's anther way to do this:
def smallitem(l):
l = list(set(sorted(l)))
x = l[0]
for i in l:
if i != x and x >= 1:return x
x += 1
return 1
Testing:
>>> smallitem([-3, -1])
1
>>> smallitem([1, 3, 6, 4, 1, 2, 87])
5
>>>
def sum_evens_2d(xss):
i = 0
counter = 0
while (i < len(xss)):
if(xss[i]%2 == 0):
counter += xss[i]
i= i+1
else:
i = i+1
return(counter)
I am trying to find the sum of the evens in the list xss. My restrictions are that I can not use sum(), but recursion only.
Provided code works fine.
So try to use sum as discribed below:
xss = range(5)
print sum(el for el in xss if el % 2 == 0)
Just tested this one out, it should work:
def even_sum(a):
if not a:
return 0
n = 0
if a[n] % 2 == 0:
return even_sum(a[1:]) + a[n]
else:
return even_sum(a[1:])
# will output 154
print even_sum([1, 2, 3, 4, 5, 6, 7, 8, 23, 55, 45, 66, 68])
if you cant use sum and must have recursion you can do:
def s(xss):
if not xss:
return 0 # for when the list is empty
counter = 0 if xss[0] % 2 != 0 else xss[0]
return counter + s(xss[1:])