Trouble implementing a counting sort algorithm - python

I'm trying to teach myself a few sorting algorithms in python and I'm having a little bit of trouble with the output. I'm attempting to implement a counting sort algorithm and I've gotten this far:
def counting_sort(l):
nums = l
highest = max(nums) + 1
helper_list = [0] * highest
s_list = []
for i in range(len(nums)):
value = nums[i]
helper_list[value] += 1
for j in range(len(helper_list)):
s_list.append([j] * helper_list[j])
return s_list
Everything is going almost fine, but when I give an input such as [5, 2, 2, 3, 1, 2].
I get an output like: [[], [1], [2, 2, 2], [3], [5]].

You just have to change the "append" for "extend". The append function adds an element to your list, in this case, another list. The extend function concatenates your list with the one given as a parameter.
Your function should be like the following:
def counting_sort(elements):
highest = max(elements) + 1
helper_list = [0] * highest
s_list = []
for value in elements:
helper_list[value] += 1
for j in range(len(helper_list)):
s_list.extend([j] * helper_list[j])
return s_list

def counting_sort(unordered_list, k, desc=False):
'''
unordered_list is the input array to be sorted.
k is the range of non-negative key values.
desc lets the algorithm to sort the array in descending order.
time complexity is the sum of the times for two steps, O(n + k).
'''
count_list = [0] * k
for element in unordered_list:
count_list[element] += 1
if desc:
enumerator = reversed(list(enumerate(count_list)))
else:
enumerator = enumerate(count_list)
sorted_list = []
for idx, count in enumerator:
sorted_list += [idx] * count
return sorted_list

The problem is the line:
s_list.append([j] * helper_list[j])
This says to append a list ([j]*helper_list[j]) to s_list, adding that list as new element or s_list.
Instead, you want add one list onto the other, which can be done like so:
s_list.append += ([j] * helper_list[j])

Related

Save list number within a list only if it contains elements in python

I have list of lists such as :
my_list_of_list=[['A','B','C','E'],['A','B','C','E','F'],['D','G','A'],['X','Z'],['D','M'],['B','G'],['X','Z']]
as you can see, the list 1 and 2 share the most elements (4). So, I keep a list within my_list_of_list only if the 4 shared elements (A,B,C or E) are present within that list.
Here I then save within the list_shared_number[], only the lists 1,2,3 and 6 since the other does not contain (A,B,C or E).
Expected output:
print(list_shared_number)
[0,1,2,5]
Probably sub optimal because I need to iterate 3 times over lists but it's the expect result:
from itertools import combinations
from functools import reduce
common_elements = [set(i).intersection(j)
for i, j in combinations(my_list_of_list, r=2)]
common_element = reduce(lambda i, j: i if len(i) >= len(j) else j, common_elements)
list_shared_number = [idx for idx, l in enumerate(my_list_of_list)
if common_element.intersection(l)]
print(list_shared_number)
# Output
[0, 1, 2, 5]
Alternative with 2 iterations:
common_element = {}
for i, j in combinations(my_list_of_list, r=2):
c = set(i).intersection(j)
common_element = c if len(c) > len(common_element) else common_element
list_shared_number = [idx for idx, l in enumerate(my_list_of_list)
if common_element.intersection(l)]
print(list_shared_number)
# Output
[0, 1, 2, 5]
You can find shared elements by using list comprehension. Checking if index 0 and index 1:
share = [x for x in my_list_of_list[0] if x in my_list_of_list[1]]
print(share)
Assume j is each item so [j for j in x if j in share] can find shared inner elements. if the length of this array is more than 0 so it should include in the output.
So final code is like this:
share = [x for x in my_list_of_list[0] if x in my_list_of_list[1]]
my_list = [i for i, x in enumerate(my_list_of_list) if len([j for j in x if j in share]) > 0]
print(my_list)
You can use itertools.combinations and set operations.
In the first line, you find the intersection that is the longest among pairs of lists. In the second line, you iterate over my_list_of_list to identify the lists that contain elements from the set you found in the first line.
from itertools import combinations
comparison = max(map(lambda x: (len(set(x[0]).intersection(x[1])), set(x[0]).intersection(x[1])), combinations(my_list_of_list, 2)))[1]
out = [i for i, lst in enumerate(my_list_of_list) if comparison - set(lst) != comparison]
Output:
[0, 1, 2, 5]
Oh boy, so mine is a bit messy, however I did not use any imports AND I included the initial "finding" of the two lists which have the most in common with one another. This can easily be optimised but it does do exactly what you wanted.
my_list_of_list=[['A','B','C','E'],['A','B','C','E','F'],['D','G','A'],['X','Z'],['D','M'],['B','G'],['X','Z']]
my_list_of_list = list(map(set,my_list_of_list))
mostIntersects = [0, (None,)]
for i, IndSet in enumerate(my_list_of_list):
for j in range(i+1,len(my_list_of_list)):
intersects = len(IndSet.intersection(my_list_of_list[j]))
if intersects > mostIntersects[0]: mostIntersects = [intersects, (i,j)]
FinalIntersection = set(my_list_of_list[mostIntersects[1][0]]).intersection(my_list_of_list[mostIntersects[1][1]])
skipIndexes = set(mostIntersects[1])
for i,sub_list in enumerate(my_list_of_list):
[skipIndexes.add(i) for char in sub_list
if i not in skipIndexes and char in FinalIntersection]
print(*map(list,(mostIntersects, FinalIntersection, skipIndexes)), sep = '\n')
The print provides this :
[4, (0, 1)]
['E', 'C', 'B', 'A']
[0, 1, 2, 5]
This works by first converting the lists to sets using the map function (it has to be turned back into a list so i can use len and iterate properly) I then intersect each list with the others in the list of lists and count how many elements are in each. Each time i find one with a larger number, i set mostIntersections equal to the len and the set indexes. Once i go through them all, i get the lists at the two indexes (0 and 1 in this case) and intersect them to give a list of elements [A,B,C,E] (var:finalIntersection). From there, i just iterate over all lists which are not already being used and just check if any of the elements are found in finalIntersection. If one is, the index of the list is appended to skipIndexes. This results in the final list of indexes {indices? idk} that you were after. Technically the result is a set, but to convert it back you can just use list({0,1,2,5}) which will give you the value you were after.

How to multiply two sublists within the same list?

I am new to python and SO. I want to multiply each element of lists in a list of lists to another list lying in the same list of lists and compare it with a reference value. This will be more clear with an example:
for L = [[1,2,3,4,5,10],[3,2,4,1,5,10]] ##there can be more than 2 lists in this list of lists
I want only those pairs whose product results in 10.
Out: L = [[1,10],[2,5]]
Edit: I prefer a method without any imports, since I am doing this for building logic and my editor doesn't contain any modules to import. Also, if there are more than 2 lists in a list of lists For eg. there are 3 Lists in a list of lists: then I want triplets for that sake. ANd if 4 lists then I need quadruples.
Here's my code attempt as requested.
N = []
J = []
F = []
Z = []
S = []
O = []
num = input("Enter no. of elements in list")
print ('Enter numbers')
prod = 1
for i in range(int(num)):
n = input("num :")
N.append(int(n))
for x in N:
prod = prod*x
print (prod)
k = int(input("Enter no. of splits:"))
##d = [[] for x in xrange(k)]
##var_dict = []
for o in range(1,prod+1):
if prod%o == 0:
J.append(o)
print (J)
for g in range(k):
O.append(J*(k-1))
print (O)
for a in range(len(O)):
for b in range(len(O)):
if O[i]*O[i+1] == prod:
Z.extend(O)
##Z = [[a, b] for a in J for b in F if a*b == prod] ##imp
print (Z)
for e in Z:
if e not in S and sorted(e) not in S:
S.append(e)
print (S)
You can use itertools.product to find groups of numbers and filter them based on their product
>>> from itertools import product
>>> from functools import reduce
>>> lst = [[1,2,3,4,5,10],[3,2,4,1,5,10]]
>>> ans = [p for p in product(*lst) if reduce(lambda x,y:x*y, p) == 10]
>>> # Remove duplicates
>>> ans = list(map(list, set(map(frozenset, ans))))
>>> print (ans)
[[1, 10], [2, 5]]
>>>
In a comment you say you say you want code without any imports. Importing product from itertools and reduce do simplify and speed the code, but the job can be done without imports. Here is a recursive solution. I am not quite satisfied with this code but it seems to work and passes all my tests. I generalized the problem somewhat so that the input can have tuples or other sequences and not just lists.
def prod_is_target(seq_of_seqs, target):
"""Return a list of lists, where each sublist contains one member
of each input subsequence in order, and the product of those
members equals the target value. If there is no such list possible,
return None. If seq_of_seqs is empty, return an empty list if the
target is 1 and return None otherwise.
"""
if not seq_of_seqs: # if is empty sequence
return [] if target == 1 else None
result = []
for val in seq_of_seqs[0]:
if target % val: # skip if target is not a multiple of val
continue
prodlists = prod_is_target(seq_of_seqs[1:], target // val)
if prodlists is None: # skip if failed with later sublists
continue
if not prodlists: # if is empty list
result.append([val])
else:
for prodlist in prodlists:
result.append([val] + prodlist)
return result if result else None
print(prod_is_target([[1,2,3,4,5,10], [3,2,4,1,5,10]], 10))
The printout from that code is
[[1, 10], [2, 5], [5, 2], [10, 1]]
I'll leave it to you to remove what you consider to be duplicates. I can think of situations where you would want this full list, however.

Python# rearranging array with high and low element in list?

I am new in python i would like to rearranging list with sequence of high and low value. Example Input is a=[1,2,3,4,5] then output should be
a=[5,1,4,2,3].i solved this way any one have better solution? please guide me.Thank you in Advance.
number=int(input("how many number do you want to input?"))
number_list=[]
for i in range(number):
array=int(input())
number_list.append(array)
print(number_list)
# empty array for adding value in list
tmp=[]
i=0
m=len(number_list)
while i<m:
# when last element left in array it will add on our tmp list
if i > len(number_list):
tmp.extend(number_list)
# once all value add in array it will break the program
if len(tmp) == m:
break
else:
high=max(number_list)
low=min(number_list)
tmp.append(high)
tmp.append(low)
#number_list.remove(high)
#number_list.remove(low)
# remove the element after added in the list
number_list.remove(high)
number_list.remove(low)
#print(len(number_list))
#print(tmp)
i +=1
print(tmp)
It is really easy, using a basic for-loop and a helper list. You are overthinking it:
list1 = [1,2,3,4,5]
list1.sort()
resultList = []
for i in range(len(list1)):
resultList.append(list1[len(list1)-1-i])
resultList.append(list1[i])
resultList = resultList[:len(resultList)//2]
list1 = resultList
Now, if you try to print it:
print(list1) # [5, 1, 4, 2, 3]
Note: This only works for Python 3, you have to do minor adjustments for it to work with Python 2 as well
My adea is to combine two lists alternatively b and c, the first is an order descending of list a, and the scond in order ascending of a
import itertools
b=sorted(a, reverse=True)
c=sorted(a)
print [x for x in itertools.chain.from_iterable(itertools.izip_longest(b,c)) if x][:len(a)]
Aother approach using min(), max() and a generator:
# list passed in argument must be sorted
def get_new_list(a):
for k in range(int(len(a)/2)+1):
aa = max(a[k:len(a)-k])
bb = min(a[k:len(a)-k])
if aa != bb:
yield aa
yield bb
else:
yield aa
a = [1,2,3,4,5]
a.sort()
final = list(get_new_list(a))
print(final)
Another approach using for loop and list slicing. This approach is more efficient than using min() and max():
def get_new_list(a):
for k in range(int(len(a)/2)+1):
aa = a[k:len(a)-k]
if len(aa) > 1:
yield aa[-1]
yield aa[0]
else:
yield aa[0]
a = [5,1,2,3,4]
# list must be sorted
a.sort()
final = list(get_new_list(a))
print(final)
Both will output:
[5, 1, 4, 2, 3]

How to find second smallest UNIQUE number in a list?

I need to create a function that returns the second smallest unique number, which means if
list1 = [5,4,3,2,2,1], I need to return 3, because 2 is not unique.
I've tried:
def second(list1):
result = sorted(list1)[1]
return result
and
def second(list1):
result = list(set((list1)))
return result
but they all return 2.
EDIT1:
Thanks guys! I got it working using this final code:
def second(list1):
b = [i for i in list1 if list1.count(i) == 1]
b.sort()
result = sorted(b)[1]
return result
EDIT 2:
Okay guys... really confused. My Prof just told me that if list1 = [1,1,2,3,4], it should return 2 because 2 is still the second smallest number, and if list1 = [1,2,2,3,4], it should return 3.
Code in eidt1 wont work if list1 = [1,1,2,3,4].
I think I need to do something like:
if duplicate number in position list1[0], then remove all duplicates and return second number.
Else if duplicate number postion not in list1[0], then just use the code in EDIT1.
Without using anything fancy, why not just get a list of uniques, sort it, and get the second list item?
a = [5,4,3,2,2,1] #second smallest is 3
b = [i for i in a if a.count(i) == 1]
b.sort()
>>>b[1]
3
a = [5,4,4,3,3,2,2,1] #second smallest is 5
b = [i for i in a if a.count(i) == 1]
b.sort()
>>> b[1]
5
Obviously you should test that your list has at least two unique numbers in it. In other words, make sure b has a length of at least 2.
Remove non unique elements - use sort/itertools.groupby or collections.Counter
Use min - O(n) to determine the minimum instead of sort - O(nlongn). (In any case if you are using groupby the data is already sorted) I missed the fact that OP wanted the second minimum, so sorting is still a better option here
Sample Code
Using Counter
>>> sorted(k for k, v in Counter(list1).items() if v == 1)[1]
1
Using Itertools
>>> sorted(k for k, g in groupby(sorted(list1)) if len(list(g)) == 1)[1]
3
Here's a fancier approach that doesn't use count (which means it should have significantly better performance on large datasets).
from collections import defaultdict
def getUnique(data):
dd = defaultdict(lambda: 0)
for value in data:
dd[value] += 1
result = [key for key in dd.keys() if dd[key] == 1]
result.sort()
return result
a = [5,4,3,2,2,1]
b = getUnique(a)
print(b)
# [1, 3, 4, 5]
print(b[1])
# 3
Okay guys! I got the working code thanks to all your help and helping me to think on the right track. This code works:
`def second(list1):
if len(list1)!= len(set(list1)):
result = sorted(list1)[2]
return result
elif len(list1) == len(set(list1)):
result = sorted(list1)[1]
return result`
Okay, here usage of set() on a list is not going to help. It doesn't purge the duplicated elements. What I mean is :
l1=[5,4,3,2,2,1]
print set(l1)
Prints
[0, 1, 2, 3, 4, 5]
Here, you're not removing the duplicated elements, but the list gets unique
In your example you want to remove all duplicated elements.
Try something like this.
l1=[5,4,3,2,2,1]
newlist=[]
for i in l1:
if l1.count(i)==1:
newlist.append(i)
print newlist
This in this example prints
[5, 4, 3, 1]
then you can use heapq to get your second largest number in your list, like this
print heapq.nsmallest(2, newlist)[-1]
Imports : import heapq, The above snippet prints 3 for you.
This should to the trick. Cheers!

Get list based on occurrences in unknown number of sublists

I'm looking for a way to make a list containing list (a below) into a single list (b below) with 2 conditions:
The order of the new list (b) is based on the number of times the value has occurred in some of the lists in a.
A value can only appear once
Basically turn a into b:
a = [[1,2,3,4], [2,3,4], [4,5,6]]
# value 4 occurs 3 times in list a and gets first position
# value 2 occurs 2 times in list a and get second position and so on...
b = [4,2,3,1,5,6]
I figure one could do this with set and some list magic. But can't get my head around it when a can contain any number of list. The a list is created based on user input (I guess that it can contain between 1 - 20 list with up 200-300 items in each list).
My trying something along the line with [set(l) for l in a] but don't know how to perform set(l) & set(l).... to get all matched items.
Is possible without have a for loop iterating sublist count * items in sublist times?
I think this is probably the closest you're going to get:
from collections import defaultdict
d = defaultdict(int)
for sub in outer:
for val in sub:
d[val] += 1
print sorted(d.keys(), key=lambda k: d[k], reverse = True)
# Output: [4, 2, 3, 1, 5, 6]
There is an off chance that the order of elements that appear an identical number of times may be indeterminate - the output of d.keys() is not ordered.
import itertools
all_items = set(itertools.chain(*a))
b = sorted(all_items, key = lambda y: -sum(x.count(y) for x in a))
Try this -
a = [[1,2,3,4], [2,3,4], [4,5,6]]
s = set()
for l in a:
s.update(l)
print s
#set([1, 2, 3, 4, 5, 6])
b = list(s)
This will add each list to the set, which will give you a unique set of all elements in all the lists. If that is what you are after.
Edit. To preserve the order of elements in the original list, you can't use sets.
a = [[1,2,3,4], [2,3,4], [4,5,6]]
b = []
for l in a:
for i in l:
if not i in b:
b.append(i)
print b
#[1,2,3,4,5,6] - The same order as the set in this case, since thats the order they appear in the list
import itertools
from collections import defaultdict
def list_by_count(lists):
data_stream = itertools.chain.from_iterable(lists)
counts = defaultdict(int)
for item in data_stream:
counts[item] += 1
return [item for (item, count) in
sorted(counts.items(), key=lambda x: (-x[1], x[0]))]
Having the x[0] in the sort key ensures that items with the same count are in some kind of sequence as well.

Categories