Selection_sort (python) - python

I came across this code on selection sort algorithm:
ls = [2, 5, 1, -9, 10, 13, 7, 2]
def selection_sort(ls):
for i in range(len(ls)):
imin = min(range(i,len(ls)), key = lambda x: ls[x])
ls[i], ls[imin] = ls[imin], ls[i]
I know the typical selection_sort with the if block, but this one is hard to understand. I tried to print imin with all possible i's and the result was 33337777 which doesn't make sense to me. I think my problem is that I don't know how this specific key works. Does anyone have any insight on this?

function declaration
The statement is the function definition, Which takes one argument i.e. list
def selection_sort(ls):
Loop Initialization
A for-loop is defined that will iterate the list from i = 0 to the len(ls).
for i in range(len(ls)):
Main Logic
Inside the for-loop, there are 2 statements
imin = min(range(i,len(ls)), key = lambda x: ls[x])
The above code uses python's min function taking 2 arguments, an iterator and a function to find the minimum value from the list starting from index i to len(ls) and return the item's index using the lambda function passed as the second argument.
ls[i], ls[imin] = ls[imin], ls[i]
The above code is responsible for swapping the minimum item with the item at the index i of the list.

Related

dictionary, comps and has maps

I have written a code and run it, and it works fine. But I wanted to understand what is happening in the following:
nums = [4, 5, 1, 8]
target = 12
def TwoSum(nums, target):
comps = dict()
for i in range(len(nums)):
comp = target - nums[i]
if nums[i] in comps:
return [comps[nums[i]], i]
else:
comps[comp] = i
print(TwoSum(nums, target))
I understand that this is using a dict, and the idea is to add elements from nums to it, then doing target - nums[i] and then checking if this is in the dict, and if it is, then returning the indices of the two numbers that sum to the target.
But how is comps = dict() used? Is it necessary? Because in the code it doesn't seem to be storing anything! Except for the last line it is used- but I don't understand what it does- can someone please explain?
First, your code was using self as first argument of TwoSum. It should be eliminated given that this is a static function, not a class method. (Fixed).
The line comp = dict() is an initialization of comp to an empty dict. It could be written in a more pythonic way: comp = {}.
comp appears to store the complement (the difference between target and nums[i]) as comps[diff] = i. Thereafter, when you examine a number nums[j], if the complement is already in comps, that j and the corresponding previous i is the pair of indices you are looking for.
In our case, it stores number as a key thats required to find a target, and index of opposit number in nums as value.
So, for example, when we have target at 12 and nums [4,5,1,8] , at the iteration on the number 4, 8->0 will be added to comps, when iteration will get to the number 8, we will check if we have number 8 in comps, and then return value of that number in comps, which is 0, and index of current iterating number, which is 3.

Returning the index of the largest element in an array in Python

I'm trying to create a function that returns the largest element of an array, I feel I have the correct code but my syntax is in the wrong order, I'm trying to use a for/while loop in order to do so. So far I have the following:
def manindex(arg):
ans = 0
while True:
for i in range (len(arg)):
if arg[i] > arg[ans]:
pass
ans = i
return ans
Not sure where I'm going wrong if anyone could provide some guidance, thanks
EDIT: So it's been pointing out I'm causing an infinite loop so if I take out the while statement I'm left with
def manindex(arg):
ans = 0
for i in range (len(arg)):
if arg[i] > arg[ans]:
ans = i
return ans
But I have a feeling it's still not correct
When you say array I think you mean list in Python, you don't need a for/loop or while/loop to achieve this at all.
You can also use index with max, like so:
xs.index(max(xs))
sample:
xs = [1,123,12,234,34,23,42,34]
xs.index(max(xs))
3
You could use max with the key parameter set to seq.__getitem__:
def argmax(seq):
return max(range(len(seq)), key=seq.__getitem__)
print(argmax([0,1,2,3,100,4,5]))
yields
4
The idea behind finding the largest index is always the same, iterating over the elements of the array, compare to the max value we have at the moment, if it's better, the index of the current element is the maximum now, if it's not, we keep looking for it.
enumerate approach:
def max_element_index(items):
max_index, max_value = None, None
for index, item in enumerate(items):
if item > max_value:
max_index, max_value = index, item
return max_index
functional approach:
def max_element_index(items):
return reduce(lambda x,y: x[1] > y[1] and x or y,
enumerate(items), (None, None))[0]
At the risk of looking cryptic, the functional approach uses the reduce function which takes two elements and decides what is the reduction. Those elements are tuples (index, element), which are the result of the enumerate function.
The reduce function, defined on the lambda body takes two elements and return the tuple of the largest. As the reduce function reduces until only one element in the result is encountered, the champion is the tuple containing the index of the largest and the largest element, so we only need to access the 0-index of the tuple to get the element.
On the other hand if the list is empty, None object is returned, which is granted on the third parameter of the reduce function.
Before I write a long winded explanation, let me give you the solution:
index, value = max(enumerate(list1), key=lambda x: x[1])
One line, efficient (single pass O(n)), and readable (I think).
Explanation
In general, it's a good idea to use as much of python's incredibly powerful built-in functions as possible.
In this instance, the two key functions are enumerate() and max().
enumerate() converts a list (or actually any iterable) into a sequence of indices and values. e.g.
>>> list1 = ['apple', 'banana', 'cherry']
>>> for tup in enumerate(list1):
... print tup
...
(0, 'apple')
(1, 'banana')
(2, 'cherry')
max() takes an iterable and returns the maximum element. Unfortunately, max(enumerate(list1)) doesn't work, because max() will sort based on the first element of the tuple created by enumerate(), which sadly is the index.
One lesser-known feature of max() is that it can take a second argument in the form max(list1, key=something). The key is a function that can be applied to each value in the list, and the output of that function is what gets used to determine the maximum. We can use this feature to tell max() that it should be ranking items by the second item of each tuple, which is the value contained in the list.
Combining enumerate() and max() with key (plus a little help from lambda to create a function that returns the second element of a tuple) gives you this solution.
index, value = max(enumerate(list1), key=lambda x: x[1])
I came up with this recently (and am sprinkling it everywhere in my code) after watching Raymond Hettinger's talk on Transforming Code into Beautiful, Idiomatic Python, where he suggests exorcising the for i in xrange(len(list1)): pattern from your code.
Alternatively, without resorting to lambda (Thanks #sweeneyrod!):
from operator import itemgetter
index, value = max(enumerate(list1), key=itemgetter(1))
I believe if you change your for loop to....
for i in range (len(arg)):
if arg[i] > ans:
ans = arg[i]
it should work.
You could try something like this. If the list is empty, then the function will return an error.
m is set to the first element of the list, we then iterate over the list comparing the value at ever step.
def findMax(xs):
m = xs[0]
for x in xs:
if x > m:
m = x
return m
findMax([]) # error
findMax([1]) # 1
findMax([2,1]) # 2
if you wanted to use a for loop and make it more generic, then:
def findGeneric(pred, xs):
m = xs[0]
for x in xs:
if pred(x,m):
m = x
return m
findGeneric(lambda a,b: len(a) > len(b), [[1],[1,1,1,1],[1,1]]) # [1,1,1,1]

For Loop to While Loop using IN for while loops

I am quite new to Python 2.7 so I had a couple of questions regarding using for loops to while loops.
For example: I am writing this definition
def missingDoor(trapdoor,roomwidth,roomheight,step):
safezone = []
hazardflr = givenSteps(roomwidth,step,True)
safetiles = []
for m in hazardflr:
safetiles.append((m,step))
i = 0
while i < len(safetiles):
nextSafe = safetiles[i]
if knownSafe(roomwidth, roomheight, nextSafe[0], nextSafe[1]):
if trapdoor[nextSafe[0]/roomwidth][nextSafe[0]%roomwidth] is "0":
if nextSafe[0] not in safezone:
safezone.append(nextSafe[0])
for e in givenSteps(roomwidth,nextSafe[0],True):
if knownSafe(roomwidth, roomheight, e, nextSafe[0]):
if trapdoor[e/roomwidth][e%roomwidth] is "0" and (e,nextSafe[0]) not in safetiles:
safetiles.append((e,nextSafe[0]))
i += 1
return sorted(safezone)
I am trying to turn all the for loops to a while loops, so this is currently what I have written so far. I actually dont know if we say "While e in " works near the middle of the code. But using the while loop rules, will this code do the same as the for loop one?
safezone = []
hazardflr = givenSteps(roomwidth,step,True)
safetiles = []
m=0
while m < hazardflr:
safetiles.append((m,step))
i = 0
while i < len(safetiles):
nextSafe = safetiles[i]
if knownSafe(roomwidth, roomheight, nextSafe[0], nextSafe[1]):
if trapdoor[nextSafe[0]/roomwidth][nextSafe[0]%roomwidth] is "0":
if nextSafe[0] not in safezone:
safezone.append(nextSafe[0])
e=0
while e in givenSteps(roomwidth,nextSafe[0],True):
if knownSafe(roomwidth, roomheight, e, nextSafe[0]):
if trapdoor[e/roomwidth][e%roomwidth] is "0" and (e,nextSafe[0]) not in safetiles:
safetiles.append((e,nextSafe[0]))
e+=1
i += 1
m+=1
return sorted(safezone)
thanks for any advice or help!
No, your code isn't identical.
While they look similar, for item in list and while item in list will do wildly different things.
for item in list is a syntactic way of saying for every item in the list - do something with is.
while item in list is different - a while loop iterates as long as the condition is true. The condition in this case being item in list. It doesn't update the item each iteration and if you never change what item or list are, it might never terminate. Additionally, if any given item isn't in the list it may terminate prematurely.
If you want to iterate through a list and keep a count, using while is the wrong way to go about it. Use the enumerate() function instead.
enumerate() takes a list, and returns a list of tuples, with each item from the list in order with its index, like so:
for i,m in enumerate(hazardflr):
safetiles.append((m,step))
This small change means you no longer have to track your indices manually.
If you are iterating through every item in a list in Python - use for that's what it is designed to do.
It depends on exactly what givenSteps returns, but in general, no. for x in foo evaluates foo once and then assigns x to be each element of foo in turn. while x in foo: ... x += 1, on the other hand, evaluates foo on every iteration and will end early if foo is not a contiguous sequence. For example, if foo = [0, 1, 2, 5, 6], for will use every value of foo, but while will end after 2, because 3 is not in foo. while will also differ from for if foo contains any non-integral values or values below the starting value.
while aList:
m= hazardflr.pop()
# ...
should be roughly equivelent to your other loop

Python aggregate on a generator

I have a generator that returns a list in each iteration. Each element of the list could be either 0 or 1. I want to count the total number of elements returned (including both 0 and 1) and the total number of 1 returned. I tried to implement this using reduce function like this :
t = reduce( (lambda x,y:(y[0]+1,y[1]+x)), gen_fn(), (0,0))
gen_fn() above is the generator that returns part of the list in each yield statement. I wanted to implement it by initializing with a tuple (0,0) for count. Given that the elements returned from generator are following :
[0, 1, 1, 0, 1]
My expected output for t is (5,3). But my code is failing with this error message :
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
Can anybody help me identify the problem? My lack of experience with reduce and lambda functions is preventing me from figuring out what I am doing wrong. Thanks in advance.
I think the best answer here is to keep it simple:
count = 0
total = 0
for item in gen_fn():
count += 1
total += item
Using reduce() here only makes your code less readable.
If your question is code golf and you want a one liner (while keeping lazy evaluation), then you want:
count, total = collections.deque(zip(itertools.count(1), itertools.accumulate(gen_fn())), maxlen=1).pop()
Of course, you'd be mad to pick such a construction over the simple solution.
Edit:
If the generator yields multiple smaller parts, then simply use itertools.chain.from_iterable(gen_fn()) to flatten it.
You have the lambda arguments the wrong way around; the first argument (x) is the total so far (the tuple) and the second (y) is the new value (the integer). Try:
t = reduce((lambda x, y: (x[0]+1, x[1]+y)), gen_fn(), (0,0))
Using a dummy function:
def gen_fn():
for x in [0, 1, 1, 0, 1]:
yield x
I get (5, 3).
This equivalent implementation of reduce from the docs might make things clearer:
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
try:
initializer = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = initializer
for x in it:
accum_value = function(accum_value, x) # note value so far is first arg
return accum_value
As jonrsharpe has pointed out, you are using your lambda arguments backwards, given the way reduce works. However, there may be a further issue with how you're adding things up, if each item yielded from your generator is a list.
This issue is that your y value (the item yielded by the generator) is not a single number, but a list. You need to count its length and the number of 1s it has, so you probably want your lambda function to be:
lambda x, y: (x[0]+len(y), x[1]+sum(y))
How about taking a completely different approach?
t = [(len(row), len(filter(lambda x: x == 1, row))) for row in gen_fn()]

A double preceding function with bug

It is a homework question that I am stuck on:
Your classmate claims to have written a function that replaces each value in a list with twice the preceding value (and the first value with 0). For example, if the list [1, 3, 7, 11] is passed as a parameter, the function is supposed to return [0, 2, 6, 14] -- Note: 22 is not part of the output. Here's the code:
def double_preceding(values):
if (values != []):
temp = values[0]
values[0] = 0
for i in range(1, len(values)):
values[i] = 2 * temp
temp = values[i]
Analyse this function and rewrite it so that it works as intended.
I couldn't even follow what the code was doing, can someone explain to me please
Here's an explanation of the given code, and the errors I see within it:
def double_preceding(values):
if (values != []): # this if checks for an empty list, to avoid exceptions
temp = values[0]
values[0] = 0 # but this line is not indented, so you may get an IndexError anyway
for i in range(1, len(values)): # this loops over all indexes but the first (0)
values[i] = 2 * temp # this replaces the current value with double temp
temp = values[i] # this line however saves the same doubled value as the new temp
So, the two errors I see is incorrect handling of empty lists, and a logic error in the assignment code that will cause the loop to replace values after the first with the list's original first value times successive powers of two.
A good way to solve the second issue is to do both of the assignments in the loop with a single statement. This is a neat thing that Python can do that many other languages cannot. Here's what a basic version would look like:
values[i], temp = temp*2, values[i]
The commas are the key things to pay attention to. The one on the right side of the assignment makes a tuple out of temp*2 and values[i]. The comma on the left hand side tells Python to unpack the tuple being assigned into the variables values[i] and temp. And the two parts are evaluated in that order (first the expression on the right side, then the unpacking and assignments). This means that the "old" values of temp and values[i] are used to build the tuple and it doesn't matter that they're both reassigned later.
If we're doing the assignments that way, we can solve the empty list situation elegantly too. Rather than treating the first value specially and needing a check to make sure values[0] is a valid expression, why not just set temp to 0 at the start and let the loop handle the first value as well as the later ones? Here's a fully fixed function:
def double_preceeding(values):
temp = 0
for i in range(len(values)): # loop over all indexes, not skipping the first
values[i], temp = temp*2, values[i]
The loop will do nothing if values is an empty list, since len([]) is 0 and range(0) is empty itself.
Example output:
>>> L=[]
>>> double_preceeding(L)
>>> L
[]
>>> L=[1, 3, 7, 11]
>>> double_preceeding(L)
>>> L
[0, 2, 6, 14]
If I guessed the indentation of the program correctly. See comments below:
Code:
def double_preceding(v):
values = v[:] # Make a copy of the list passed as argument
if (values != []): # If list is not empty, store the first value in 'temp'
temp = values[0]
else:
return
v[0] = 0 # Set the first value of the list as '0' (as the problem says it)
for i in range(1, len(values)): # Iterate 'n - 1' times, where n is the length of the list
v[i] = 2 * temp # Set the corresponding value to twice the precedent (the precedent is stored in 'temp')
temp = values[i]
Test:
v = [1, 3, 7, 11]
double_preceding(v)
print v
Output:
[0, 2, 6, 14, 22]

Categories