Just learning Python and got on to the subject of sorting lists. Two types of algorithms were shown: insertion and selection. So, I had an idea and created this:
def DiffSort(lst):
lstDiff = [None] * len(lst)
i = 0
while i < len(lst):
lstDiff[i] = lst[i] - lst[i-1] if i != 0 else lst[0]
if lstDiff[i] < 0:
sbj, tmp = lst[i], lstDiff[i]
while tmp < 0:
i -= 1
tmp += lstDiff[i]
lst[i+1] = lst[i]
lst[i] = sbj
else:
i += 1
lst = [13,25,18,122,32,1,0.78,25,85,1,32,56,0.55,0.6,17]
print(lst)
DiffSort(lst)
print(lst)
Any good? Is there a similar method out there already?
list.sort() if you want to sort a list in-place.
sorted(list) if you want to return a sorted copy of the list.
The second option works with any iterable type, whereas the first is list-exclusive (although some other types may have the same or a similar function defined as well, but you can generally not expect that).
Since you seem to care about the algorithmic part of it, this may be of interest to you:
http://svn.python.org/projects/python/trunk/Objects/listsort.txt
Isn't lst.sort() good enough? It's bound to be much faster than a Python solution that has to run in O(n^2) time.
Related
I'm trying to find a way to enumerate all combinations of a list of numbers without recursion or using itertools. I came up with a solution that works but I think it turned into a recursive function after all. I'm new to Python and not sure how I would make this work without recursion.
Any help is appreciated as I think I'm still failing to see the difference between the two.
result = []
def permutation(li):
if len(li) == 1:
result.append(li[0])
print (result)
result.pop()
return
for i in range(0,len(li)):
result.append(li[i])
permutation(li[:i] + li[i+1:])
result.pop()
permutation([1,2,3])
It is not that intuitive to come up with an algorithm "just like that" that produces all permutations without the use of recursion.
But there exist several different such algorithms. Have look at Heap's algorithm for example:
def permutation(li):
n = len(li)
c = [0] * n
print(li)
i = 1
while i < n:
if c[i] < i:
j = c[i] if i % 2 else 0
li[j], li[i] = li[i], li[j]
print(li)
c[i] += 1
i = 1
else:
c[i] = 0
i += 1
I always liked this method:
Until the length of each variation in the queue equals the input's length: place the next input element in all positions of each variation
input [1,2,3]
queue [[1]]
insert 2 in all positions of each variation
queue [[2,1],[1,2]]
insert 3 in all positions of each variation
queue [[3,2,1],[2,3,1],[2,1,3],[3,1,2],[1,3,2],[1,2,3]]
I'm trying to write a Python function that counts the number of entries in a list that occur exactly once.
For example, given the list [17], this function would return 1. Or given [3,3,-22,1,-22,1,3,0], it would return 1.
** Restriction: I cannot import anything into my program.
The incorrect code that I've written so far: I'm going the double-loop route, but the index math is getting over-complicated.
def count_unique(x):
if len(x) == 1:
return 1
i = 0
j = 1
for i in range(len(x)):
for j in range(j,len(x)):
if x[i] == x[j]:
del x[j]
j+1
j = 0
return len(x)
Since you can't use collections.Counter or sorted/itertools.groupby apparently (one of which would usually be my go to solution, depending on whether the inputs are hashable or sortable), just simulate roughly the same behavior as a Counter, counting all elements and then counting the number of elements that appeared only once at the end:
def count_unique(x):
if len(x) <= 1:
return len(x)
counts = {}
for val in x:
counts[val] = counts.get(val, 0) + 1
return sum(1 for count in counts.values() if count == 1)
lst = [3,3,-22,1,-22,1,3,0]
len(filter(lambda z : z[0] == 1,
map(lambda x : (len(filter(lambda y : y == x, lst)), x), lst)))
sorry :)
Your solution doesn't work because you are doing something weird. Deleting things from a list while iterating through it, j+1 makes no sense etc. Try adding elements that are found to be unique to a new list and then counting the number of things in it. Then figure out what my solution does.
Here is the O(n) solution btw:
lst = [3,3,-22,1,-22,1,3,0,37]
cnts = {}
for n in lst:
if n in cnts:
cnts[n] = cnts[n] + 1
else:
cnts[n] = 1
count = 0
for k, v in cnts.iteritems():
if v == 1:
count += 1
print count
A more simple and understandable solution:
l = [3, 3, -22, 1, -22, 1, 3, 0]
counter = 0
for el in l:
if l.count(el) == 1:
counter += 1
It's pretty simple. You iterate over the items of the list. Then you look if the element is exactly one time in the list and then you add +1. You can improve the code (make liste comprehensions, use lambda expressions and so on), but this is the idea behind it all and the most understandable, imo.
you are making this overly complicated. try using a dictionary where the key is the element in your list. that way if it exists it will be unique
to add to this. it is probably the best method when looking at complexity. an in lookup on a dictionary is considered O(1), the for loop is O(n) so total your time complexity is O(n) which is desirable... using count() on a list element does a search on the whole list for every element which is basically O(n^2)... thats bad
from collections import defaultdict
count_hash_table = defaultdict(int) # i am making a regular dictionary but its data type is an integer
elements = [3,3,-22,1,-22,1,3,0]
for element in elements:
count_hash_table[element] += 1 # here i am using that default datatype to count + 1 for each type
print sum(c for c in count_hash_table.values() if c == 1):
There is method on lists called count.... from this you can go further i guess.
for example:
for el in l:
if l.count(el) > 1:
continue
else:
print("found {0}".format(el))
def remove_adjacent(nums):
i = 0
while i < len(nums):
if nums[i] == nums[i+1]:
nums.remove(nums[i])
i = i + 1
else: i = i + 1
return nums
IndexError: list index out of range
Who can tell me what's wrong with my code?
There are several issues with your code.
As AbhiP points out, you're looking at pairs of consecutive items in your list - there are len(nums)-1 pairs that you should be comparing, but you're trying to compare len(nums) pairs. That's one cause of the index error.
Secondly, as John mentions, you're removing items in your list while you loop through it. If you really want to keep your current structure, you need to not increment the loop variable when you remove an item.
Correction/Clarification: This second point won't cause an index error, but it will cause bugs by making the code skip the evaluation of certain pairs, e.g. for inputs such as [1, 1, 1, 2].
Taking these two points into consideration, your code will look like:
i = 0
while i < len(nums) - 1:
if nums[i] == nums[i+1]:
nums.remove(nums[i])
else:
i += 1
Which will remove the index error.
Thirdly, nums.remove(nums[i]) will also cause non-index error bugs. Try the above code with nums being [1, 2, 3, 1, 1]. You will see that the first 1 is removed, not the 4th or 5th item in the list. This is because remove on a list gets rid of the first instance that appears in the list. You should probably do del instead, as below:
i = 0
while i < len(nums) - 1:
if nums[i] == nums[i+1]:
del nums[i]
else:
i += 1
Finally, while not a bug, best practice would suggest that you shouldn't modify a list while looping through it - this makes the code hard to reason about, and can lead to subtle bugs. Instead it's usually better if you just create a new list and return that.
new_nums = []
for i in range(len(new_nums)-1):
if nums[i] != nums[i+1]:
new_nums.append(nums[i])
new_nums.append(nums[-1])
An alternative way of writing this using zip and list comprehensions, two of Python's cool features:
new_nums = [item1 for item1, item2 in zip(nums, nums[1:]) if item1 != item2]
new_nums.append(nums[-1])
Your problem is the line while i < len(nums):. It will iterate from 0 till len-1 but the next line you are doing if nums[i] == nums[i+1] so the index will go until len.
Change it to:
while i < len(nums) - 1:
You are removing items from the list while iterating through it. The for loop has no idea that the bounds have changed, thus the error.
I have implemented insertion sort in python and was wondering how to determine the complexity of the algorithm. Is this an inefficient way of implementing insertion sort? To me, this seems like the most readable algorithm.
import random as rand
source = [3,1,0,10,20,2,1]
target = []
while len(source)!=0:
if len(target) ==0:
target.append(source[0])
source.pop(0)
element = source.pop(0)
if(element <= target[0]):
target.reverse()
target.append(element)
target.reverse()
elif element > target[len(target)-1]:
target.append(element)
else:
for i in range(0,len(target)-1):
if element >= target[i] and element <= target[i+1]:
target.insert(i+1,element)
break
print target
Instead of:
target.reverse()
target.append(element)
target.reverse()
try:
target.insert(0, element)
Also, maybe use a for loop, instead of a while loop, to avoid source.pop()?:
for value in source:
...
In the final else block, the first part of the if test is redundant:
else:
for i in range(0,len(target)-1):
if element >= target[i] and element <= target[i+1]:
target.insert(i+1,element)
break
Since the list is already sorted, as soon as you find an element larger than the one you're inserting, you've found the insertion location.
I would say it is rather inefficient. How can you tell? Your approach creates a second array, but you don't need one in a selection sort. You use a lot of operations -- selection sort requires lookups and exchanges, but you have lookups, appends, pops, inserts, and reverses. So you know that you can probably do better.
def insertionsort( aList ):
for i in range( 1, len( aList ) ):
tmp = aList[i]
k = i
while k > 0 and tmp < aList[k - 1]:
aList[k] = aList[k - 1]
k -= 1
aList[k] = tmp
This code is taken from geekviewpoint.com. Clearly it's a O(n^2) algorithm since it's using two loops. If the input is already sorted, however, then it's O(n) since the while-loop would then always be skipped due to tmp < aList[k - 1] failing.
What is the most efficient way to sort a list, [0,0,1,0,1,1,0] whose elements are only 0 & 1, without using any builtin sort() or sorted() or count() function. O(n) or less than that
>>> lst = [0,0,1,0,1,1,0]
>>> l, s = len(lst), sum(lst)
>>> result = [0] * (l - s) + [1] * s
>>> result
[0, 0, 0, 0, 1, 1, 1]
There are many different general sorting algorithms that can be used. However, in this case, the most important consideration is that all the elements to sort belong to the set (0,1).
As other contributors answered there is a trivial implementation.
def radix_sort(a):
slist = [[],[]]
for elem in a:
slist[elem].append(elem)
return slist[0] + slist[1]
print radix_sort([0,0,1,0,1,1,0])
It must be noted that this is a particular implementation of the Radix sort. And this can be extended easily if the elements of the list to be sorted belong to a defined limited set.
def radix_sort(a, elems):
slist = {}
for elem in elems:
slist[elem] = []
for elem in a:
slist[elem].append(elem)
nslist = []
for elem in elems:
nslist += slist[elem]
return nslist
print radix_sort([2,0,0,1,3,0,1,1,0],[0,1,2,3])
No sort() or sorted() or count() function. O(n)
This one is O(n) (you can't get less):
old = [0,0,1,0,1,1,0]
zeroes = old.count(0) #you gotta count them somehow!
new = [0]*zeroes + [1]*(len(old) - zeroes)
As there are no Python loops, this may be the faster you can get in pure Python...
def sort_arr_with_zero_one():
main_list = [0,0,1,0,1,1,0]
zero_list = []
one_list = []
for i in main_list:
if i:
one_list.append(i)
else:
zero_list.append(i)
return zero_list + one_list
You have only two values, so you know in advance the precise structure of the output: it will be divided into two regions of varying lengths.
I'd try this:
b = [0,0,1,0,1,1,0]
def odd_sort(a):
zeroes = a.count(0)
return [0 for i in xrange(zeroes)] + [1 for i in xrange(len(a) - zeroes)]
You could walk the list with two pointers, one from the start (i) and from the end (j), and compare the values one by one and swap them if necessary:
def sort_binary_values(l):
i, j = 0, len(l)-1
while i < j:
# skip 0 values from the begin
while i < j and l[i] == 0:
i = i+1
if i >= j: break
# skip 1 values from the end
while i < j and l[j] == 1:
j = j-1
if i >= j: break
# since all in sequence values have been skipped and i and j did not reach each other
# we encountered a pair that is out of order and needs to be swapped
l[i], l[j] = l[j], l[i]
j = j-1
i = i+1
return l
I like the answer by JBernado, but will throw in another monstrous option (although I've not done any profiling on it - it's not particulary extensible as it relies on the order of a dictionary hash, but works for 0 and 1):
from itertools import chain, repeat
from collections import Counter
list(chain.from_iterable(map(repeat, *zip(*Counter(bits).items()))))
Or - slightly less convoluted...
from itertools import repeat, chain, islice, ifilter
from operator import not_
list(islice(chain(ifilter(not_, bits), repeat(1)), len(bits)))
This should keep everything at the C level - so it should be fairly optimal.
All you need to know is how long the original sequence is and how many ones are in it.
old = [0,0,1,0,1,1,0]
ones = sum(1 for b in old if b)
new = [0]*(len(old)-ones) + [1]*ones
Here is a Python solution in O(n) time and O(2) space.
Absolutely no need to create new lists and best time performance
def sort01(arr):
i = 0
j = len(arr)-1
while i < j:
while arr[i] == 0:
i += 1
while arr[j] == 1:
j -= 1
if i<j:
arr[i] = 0
arr[j] = 1
return arr