Not able to understand Python3 enumerate() - python

Question: Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
class Solution:
def twoSum(self, nums, target):
lookup={}
for cnt, num in enumerate (nums):
if target-num in lookup:
return lookup[target-num], cnt
lookup[num]=cnt
I am not able to understand the steps after for loop is used.I am new on Python, someone please help me.

Let me help you understand by explaining what the code does and how it solves the problem.
We need to find two numbers that sum to 9, to achieve this, we can iterate over every number in the array and then look if we already encountered a number that equals the target number minus the number we are currently on. If we haven't encountered such a number yet, we store the current number and its corresponding index.
Because we need to return the indices, we want to be able to look for the number-target pairs and immediately get the index. The solution uses a dictionary to store a number (key) and return an index as (value).
We iterate over every number, if we already encountered target-number before, we can return the current index and the index of the target-number, if we haven't encountered that number, we simply store the current number and its index.
The enumerate part, simply provides an index along with the value of the array that is being iterated, in the form of (id, item).
class Solution:
def twoSum(self, nums, target):
# Here a dictionary is created, which will store value, index as key, value pairs.
lookup={}
# For every number in the array, get the index (cnt) and number (num)
for cnt, num in enumerate (nums):
# If we find target-num, we know that num + target-num = target
if target-num in lookup:
# Hence we return the index of the target-num we stored in the dict, and the index of the current value (cnt)
return lookup[target-num], cnt
# Otherwise we store the current number as key with its index as value
lookup[num]=cnt

enumerate() method adds a counter to an iterable and returns it in a form of enumerate object. This enumerate object can then be used directly in for loops or be converted into a list of tuples using list() method.
For e.g.
>>>list(enumerate("abc"))
Gives
[(0, 'a'), (1, 'b'), (2, 'c')]
For easy understanding, I'm commenting your program. Go through it, you'll surely understand.
class Solution:
def twoSum(self, nums, target):
# lookup is a dictionary that stores the number and its index
# e.g. '{7:1}'
# number 7 at index 1
lookup={}
# As explained above cnt and num will receive values one by one along with index.
for cnt, num in enumerate (nums):
# We look if the number required to be added into the 'num' is present in dictionary
if target-num in lookup:
# if value found in lookup then we return the current index along with the index of number found in lookup.
return lookup[target-num], cnt
# After every loop insert the current value and its index into the lookup dictionary.
lookup[num]=cnt
Hope, I answered your query in the way you wanted. Please comment below, if anything is left unanswered, I'll surely try to answer that as well.

Related

Terminal returns nothing

this is the challenge :
This next function will give us the values from a list at every odd index. We will need to accept a list of numbers as an input parameter and loop through the odd indices instead of the elements. Here are the steps needed:
Define the function header to accept one input which will be our list of numbers
Create a new list which will hold our values to return
Iterate through every odd index until the end of the list
Within the loop, get the element at the current odd index and append it to our new list
Return the list of elements which we got from the odd indices.
and This is my solution :
def odd_indices(lst):
odd_list = []
index = 1
while index % 2 != 0 and index < len(lst):
odd_list.append(lst[index])
index +=1
return odd_list
print(odd_indices([4, 3, 7, 10, 11, -2]))
This doesnt return [3] only . can you help me figure out why ?
You check the parity in the loop control, instead of inside the loop.

Python - efficient way to find first occurences of multiple values

I have a following problem: I need to find first occurences in an array for values greater than or equal than multiple other values.
Example:
array_1 = [-3,2,8,-1,0,5]
array_2 = [5,1]
Script has to find where in array_1 is the first value greater than or equal to each value from array_2 so the expected result in that case would be [3,2] for 1-based indices
A simple loop won't be any good for my case as both array have close to million values and it has to execute quickly preferably under a minute.
Simple loop solution that has a run time of about half an hour:
for j in range(0, len(array_2)):
for i in range(0, len(array_1)):
if array_1[i] >= array_2[j]:
solution[j] = i
break
Edit: indices clarification as #Sergio Tulentsev correctly pointed out
First perform some preprocessing on the data: create a new list that only has the values that are greater than all predecessors in the original data, and combine them in a tuple with the 1-based position where they were found.
So for instance, for the example data [-3,2,8,-1,0,5], this would be:
[(-3, 1), (2, 2), (8, 3)]
Note how the answer to any query can only be 1, 2 or 3, as the values at the other positions are all smaller than 8.
Then for each query use a binary search to find the tuple whose left value is at least the queried value, and return the right value of the found tuple (the position). For the binary search you can rely on the bisect library:
import bisect
def solve(data, queries):
# preprocessing
maxima = []
greatest = float("-inf")
for i, val in enumerate(data):
if val > greatest:
greatest = val
maxima.append((val, i+1))
# main
return [maxima[bisect.bisect_left(maxima, (query,))][1]
for query in queries]
Example use:
data = [-3,2,8,-1,0,5]
queries = [5,1]
print(solve(data, queries)) # [3, 2]
I suggest using a loop over the first array and using max(array_2) for the second one.

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.

MemoryError using numeric range as dict index (Inefficient)

I have a need to define numeric ranges as a dictionary index such as:
SCHEDULE = {
(0, 5000): 1,
(5001, 22500): 2,
(22501, 999999999): 3
}
I search it by this function:
def range_index(table, val):
new_table = {k: v for tup, v in table.items() for k in range(tup[0], tup[1]+1)}
return new_table.get(int(val)) # int() is used to deal with floats.
which works good as long as the range isn't too big. The last entry in SCHEDULE which is 999999999 causes Python to throw MemoryError. If I decrease it to a smaller number, it's fine.
This obviously means we are building this whole table from the ranges. How can this be re-worked so that the entire ranges aren't enumerated for each search?
This is a job for an order-based data structure, not a hash-based data structure like a dict. Hashes are good for equality. They don't do range tests.
Your table should be a pair of lists. The first is sorted and represents range endpoints, and the second represents values associated with each range:
# I don't have enough information to give these better names.
endpoints = [0, 5001, 22501, 1000000000]
values = [1, 2, 3]
To find a value, perform a binary search for the index in the first list and look up the corresponding value in the second. You can use bisect for the binary search:
import bisect
def lookup(endpoints, values, key):
index = bisect.bisect_right(endpoints, key) - 1
if index < 0 or index >= len(values):
raise KeyError('{!r} is out of range'.format(key))
return values[index]
You can do a next on generator with a default value as 0 to handle StopIteration:
def range_index(table, val):
return next((v for k, v in table.items() if k[0] <= int(val) <= k[1]), 0)
This uses the usual less than, greater than checks to find the range of val and get the value corresponding.
Advantages:
No new dictionary creation for every search.
Exits immediately when the condition is satisfied.
Iterate over SCHEDULE and return the first value where val is in the associated range.
category = next(category
for (start, stop), category in SCHEDULE.items()
if val in range(start, stop + 1))
It would be a bit faster if you started off with a dict of ranges, not of tuples. It would be even faster if you made SCHEDULE into a binary tree, and did a binary search on it instead of a linear one. But this is good enough for majority of cases.
This assumes your SCHEDULE is exhaustive, and you'll get a StopIteration error if you submit a val that is not covered by any of the ranges, to signify a programmer error. If you wish an else value, put it as a second parameter to next, after wrapping the first parameter in parentheses.

Looping Through List Returns Negative Index

I'm solving a puzzle that takes in a list, loops through each index and sums the values to the left and to the right of the current index. If it finds an index at which the sum of the values to its left are equal to the sum of the values to its right, then return that index.
For example:
[1,2,3,4,3,2,1]
If I am at index 3 (value 4), we see that the sum of values of elements to its left and right are equal.
(1 + 2 + 3) = (3 + 2 + 1).
However, when the input is a list of negative values, it returns a negative index. This is my code:
def get_equal_sum(list):
for i in list:
if sum(list[:i]) == sum(list[i+1:]):
print i
list = [1,2,3,4,3,2,1]
get_equal_sum(list)
>>> 3
list = [-1,-2,-3,-4,-3,-2,-1]
get_equal_sum(list)
>>> -4
Why is it returning -4 and not 3?
Thank you!
When you do
for i in list:
if sum(list[:i]) == sum(list[i+1:]):
i is not the index of the list but the value. The fact that it doesn't crash with IndexError when you feed the slices with negative values is because negative indexing is supported in python (indexes from the end of the list) as long as absolute value is in range (which is the case here). That's probably why you missed that.
If you want indexes you have to use enumerate for instance:
for i,_ in enumerate(l):
if sum(l[:i]) == sum(l[i+1:]):
(and change list to l because list is the list type)
Note the i,_ notation to unpack index/value and discard the value which isn't needed here. You could also go with the classical:
for i in range(len(l)):
for i in list:
...
This goes through the values of list, not the index (hence you why you get -4), to go through index you must use xrange (or range for python 3) over the len(list)
for i in xrange(len(list)):
...
or use enumerate(list). enumerate returns a tuple of the index value pair on any iteratable object, so you would index over a list like this:
for index, value in enumerate(list):
...
In this situation index is likely your i. Additionally, you do not want to shadow names already used by python itself (ie list or max, or min abs etc...) this will overwrite those variables to be what you assign them to in your program. For example, list is no longer a function that can create a list from list(iterable), but is now what ever you assign it to, you can no longer use it the default python way. Here is a list of python 2.7 built function in names
Because -4 is at index -4. You can have negative indices in Python with index -1 being the last element in a list.

Categories