I'm writing this piece of code, in which I've used bisect_left function from the bisect module which is a first-party module of Python. I'm using it with two parameters only i.e. sorted_list and target(the one for which I have to find the suitable index value).
The issue is: If my target is greater than the sum of lowest value and highest value, the function is returning the index = len(sorted_li), due to which I'm getting index error. I can use try and except but more than that I'm curious to know why it is behaving like so.
Following is my code:
from bisect import bisect_left
li = [10,15,3,6,10]
k = 19
def binary_search(sorted_list,target):
index = bisect_left(sorted_list,target)
print(index)
if sorted_list[index] == target:
return index
else:
return False
def function(sorted_li,k):
"""
Given a list of numbers and a number k, return whether any two numbers from the list add up to k.
For example, given [10, 15, 3, 7] and k of 17, return true since 10 + 7 is 17.
"""
print(sorted_li)
for i in range(len(sorted_li)):
print('Next iteration')
print(sorted_li[i])
target = k - sorted_li[i]
j = binary_search(sorted_li,target)
if j:
if j != i:
print(sorted_li[i])
print(sorted_li[j])
return True
else:
if j + 1 < len(sorted_li):
if sorted_li[j+1] == target:
print(sorted_li[i])
print(sorted_li[j+1])
return True
if j - 1 > 0:
if sorted_li[j-1] == target:
print(sorted_li[i])
print(sorted_li[j-1])
return True
return False
if __name__ == "__main__":
li.sort()
a = function(li,k)
print(a)
It's output is as follows:
but when I'm changing k to 18, the code is working fine, the output is as follows:
I've tried with various sets of numbers for the same. The output remains the same.
You're using bisect_left which has next purpose: it looking for the insertion point for x (which is target in your case) in a to maintain sorted order.
So for your case when you call first binary_search first time for 16 (19 - 3), it compare your number with items in li list using binary algorithm and then it returns position for insert 5, because in your list [3, 6, 10, 10, 15] insertion point should be after 15 which is correct.
If you open documentation you can find next method in searching sorted list
which does exactly you need, it searching for the exact item in list and return position of it if it exists either it raises ValueError because item not found.
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
raise ValueError
Related
I've tried to put together a basic program to convert roman numbers to conventional numbers and I seem to have made some mistake and right now it's a hit or miss.Do take a look at the code.I've described it better in the code as comments.Thanks!
def roman(roman_num):
inter_dic = {
"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000
}
output = 0
num = roman_num.upper()
#this is to ensure that the number appears as uppercase alphabets
for numeral in num:
numeral_index = num.index(numeral)
#I've squeezed an exception here to handle the last digit in the number
try:
#for the additive part
if inter_dic[numeral] > inter_dic[num[numeral_index + 1]]:
output += inter_dic[numeral]
#for the subtraction part
elif inter_dic[numeral] < inter_dic[num[numeral_index + 1]]:
output -= inter_dic[numeral]
elif inter_dic[numeral] == inter_dic[num[numeral_index + 1]]:
output += inter_dic[numeral]
#the following line is actually dead code,but I've added it just in case.
else:
print("There was an error.")
#the IndexError will be called when the last digit is reached and so the last digit
#will be added
except IndexError:
output += inter_dic[numeral]
return output
assert roman("cxcix") == 199
#this returns an assertion error
#when the function is called,the output is 179
This should do what you want:
def roman(roman_num):
inter_dic = {
"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000
}
x = list(map(lambda x: inter_dic[x], roman_num.upper()))
for idx in range(len(x)-1):
if x[idx+1] > x[idx]:
x[idx] *= -1
return x
decimal = roman("cxcix")
print(decimal) # Output: [100, -10, 100, -1, 10]
print(sum(decimal)) # Output: 199
This works on the assumption that the numbers are properly structured. As in, the numbers represented should be in order of the biggest to the smallest.
The above code will work even if you just give it one character, because the loop is based on the range(len() - 1) of the given list that is created when we translate the letters into their integers.
If x == [100] then len(x)-1 == 0 which will just terminate the for-loop immediately, so we will not encounter any IndexError inside of the loop.
To explain what is happening in your code that differs from my version we can create a simple example here:
lst = list("abcabc")
for idx, letter in enumerate(lst):
print(f"Letter: {letter}, list.index: {lst.index(letter)}, Actual index: {idx}")
Output:
Letter: a, list.index: 0, Actual index: 0
Letter: b, list.index: 1, Actual index: 1
Letter: c, list.index: 2, Actual index: 2
Letter: a, list.index: 0, Actual index: 3
Letter: b, list.index: 1, Actual index: 4
Letter: c, list.index: 2, Actual index: 5
If we look at the documentation for list.index, we can see this description:
index(self, value, start=0, stop=2147483647, /)
Return first index of value.
Raises ValueError if the value is not present.
So because there are repeated values inside of lst when we call to check for it's index, it just returns the first value that matches the variable we give it.
I'm trying to create a function that will fill in any missing numbers in between two numbers in a list. The original list must be altered and cannot be new.
For example: [13,15,20] would return [13,14,15,16,17,18,19,20].
Note that I am not allowed to use a range function.
Here's my code:
def complete(list1):
i= 0
if len(list1) > 1:
for number in list1:
if number - list1[i+1] != -1:
number += 1
list1.insert(i + 1, number)
i += 1
return list1
else:
return list1
I got a "list index out of range" error.
Here is the source of your error:
...
for number in list1:
if number - list1[i+1] != -1:
...
i += 1
Basically, there comes a point (that point being the last number in list1) when i+1 gets you out of bounds and you are not doing anything to prevent that from happening. Indexing is tricky like that, so I would like to offer an indexing-free (well, almost) approach. By the way, from your comment to Bonfire's answer, I see that the task is to change original lists in-place. While mutating arguments is considered a very poor coding practice these days, here is a relatively efficient way of doing that:
import typing as t
def complete_sequence(partial: t.List[int]) -> t.List[int]:
# edge case
if len(partial) < 2:
return partial
# a lookup table for numbers we already have
observed = set(partial)
# append numbers we don't have
start = partial[0]
stop = partial[-1]
num = start + 1
while num < stop:
if not num in observed:
partial.append(num)
num += 1
# in-place sort
partial.sort()
return partial
As you see, instead of inserting values between existing numbers (paying O(n) time for each insertion), we can simply append everything (O(1) per insertion) and sort. This not only simplifies the logic (we no longer have to track those pesky indices), but also reduces computational time-complexity from O(n^2) to O(n*log(n)).
To achieve what you want to do I have made some changes to the logic:
def complete(list1):
if len(list1) < 2 : return list1
num = list1[0]
i = -1
while num < list1[-1]:
num += 1
i += 1
if num in list1: continue
if i < len(list1) - 1:
list1.insert(i + 1, num)
else:
list1.append(num)
return list1
print(complete([13, 14, 20]))
# [13, 14, 15, 16, 17, 18, 19, 20]
print(complete([13, 14, 15]))
# [13, 14, 15]
How to write if statements into python code, which rules are listed next. The code has to check is any 5x5 matrix is bingo or not. Here is my code:
#Rules are simple
table=[[12,17,34,48,70], #first column integers are allowed 1-15
[14,25,40,56,61], #second column integers are allowed 16-30
[6,30,43,46,72], #third column integers are allowed 31-45
[12,16,42,63,65], #fourth column integers are allowed 46-60
[4,31,34,49,67]] #fifth column integers are allowed 61-75
def is_right_matrix(table):
for i in range(len(table)):
for j in range(len(table[i])):
if
return False
return True
(I hope that i understood your problem correctly.)
You could go through all the numbers and check, whether there are in a range (specific to each column). If there are not, return False.
This (not so pretty) example works for NumPy arrays as well as for "nested" lists (like the one in your table example)
table=[[12,17,34,48,70],
[14,25,40,56,61],
[6,30,43,46,72],
[12,16,42,63,65],
[4,31,34,49,67]]
def is_valid_matrix(table):
for i, col in enumerate(table):
# i is the index of the column
# (so we know to which column each number belongs)
# col is the current column
# if the current column is the first column (indexes start at 0),
# check for numbers from 1 to 15:
if i == 0:
for n in col: # go through every number in that column
# check if that number is not in the range 1 to 15
# (range includes the first argument, but excludes the second)
if n not in range(1, 16):
# if so, immediately return False
# (and therefore exit out of the function)
return False
# if the current column is the second column,
# check for numbers from 16 to 30:
elif i == 1:
for n in col:
# check if that number is not in the range 16 to 31
if n not in range(16, 31):
return False
# and so on
elif i == 2:
for n in col:
if n not in range(31, 46):
return False
elif i == 3:
for n in col:
if n not in range(46, 61):
return False
elif i == 4:
for n in col:
if n not in range(61, 76):
return False
return True # if all "checks" passed/every number was valid: return True
This is overly complicated solution, but it consists additional uniqueness test (no duplicates allowed in bingo table).
def is_valid(matrix):
floor = range(1, 62, 15)
ceiling = range(15, 76, 15)
i = 0
while i < len(floor):
for low, high in zip(floor, ceiling):
for j in list(range(len(matrix)):
if high < matrix[j][i] < low:
return False
i += 1
flat = sum(matrix, [])
if len(set(flat) != len(flat):
return False
return True
Line 11 produces the error. Stepping through the code doesn't reveal a problem?
The code just points at from left and right ends of list, moving pointers toward per iteration until a target sum is found or not! Doesn't look like the loops can step on itself but seems to anyway.
def twoSum(num_array, sum):
'''1.twoSum
Given an array of integers, return indices of the two numbers that
add up to a specific target.
'''
array = sorted(num_array)
l = array[0]
r = array[len(array)-1]
indx_Dict = dict(enumerate(array))
while (l < r) :
if (array[l] + array[r]) == sum:
return [indx_Dict[l], indx_Dict[r]]
elif array[l] + array[r] < sum:
l += 1
else:
r -= 1
num_array1 = [2, 7, 11, 15,1,0]
target1 = 9
twoSum(num_array1, target1)
that is what i changed:
array[len(array)-1] -> len(array)-1 (that's what caused your IndexError)
indx_Dict: i changed it such that indx_Dict[sorted_index] = original_index
sum -> sum_: sum is a built-in. it is never a good idea to use one of those as variable name! (yes, the new name could be better)
this is the final code:
def two_sum(num_array, sum_):
'''1.twoSum
Given an array of integers, return indices of the two numbers that
add up to a specific target.
'''
array = sorted(num_array)
l = 0
r = len(array)-1
indx_Dict = {array.index(val): index for index, val in enumerate(num_array)} ##
while (l < r) :
if (array[l] + array[r]) == sum_:
return [indx_Dict[l], indx_Dict[r]]
elif array[l] + array[r] < sum_:
l += 1
else:
r -= 1
here is a discussion about this problem:
Find 2 numbers in an unsorted array equal to a given sum (which you seem to be aware of - looks like what you are trying to do). this is a python version of just that:
def two_sum(lst, total):
sorted_lst = sorted(lst)
n = len(lst)
for i, val0 in enumerate(sorted_lst):
for j in range(n-1, i, -1):
val1 = sorted_lst[j]
s = val0 + val1
if s < total:
break
if s == total:
return sorted((lst.index(val0), lst.index(val1)))
return None
this version is based on looping over the indices i and j.
now here is a version that i feel is more pythonic (but maybe a little bit harder to understand; but it does the exact same as the one above). it ignores the index j completely as it is not really needed:
from itertools import islice
def two_sum(lst, total):
n = len(lst)
sorted_lst = sorted(lst)
for i, val0 in enumerate(sorted_lst):
for val1 in islice(reversed(sorted_lst), n-i):
s = val0 + val1
if s < total:
break
if s == total:
return sorted((lst.index(val0), lst.index(val1)))
return None
aaaaand just for the fun of it: whenever there is a sorted list in play i feel the need to use the bisect module. (a very rudimentary benchmark showed that this may perform better for n > 10'000'000; n being the length of the list. so maybe not worth it for all practical purposes...)
def two_sum_binary(lst, total):
n = len(lst)
sorted_lst = sorted(lst)
for i, val0 in enumerate(sorted_lst):
# binary search in sorted_lst[i:]
j = bisect_left(sorted_lst, total-val0, lo=i)
if j >= n:
continue
val1 = sorted_lst[j]
if val0 + val1 == total:
return sorted((lst.index(val0), lst.index(val1)))
else:
continue
return None
for (a bit more) completeness: there is a dictionary based approach:
def two_sum_dict(lst, total):
dct = {val: index for index, val in enumerate(lst)}
for i, val in enumerate(lst):
try:
return sorted((i, dct[total-val]))
except KeyError:
pass
return None
i hope the code serves as its own explanation...
l and r are not your indices, but values from your array.
Say you have an array: [21,22,23,23]. l is 21, r is 23; therefore, calling array[21] is out of bounds.
Additionally, you would have a problem with your indx_Dict. You call enumerate on it, which returns [(0,21),...(3,23)]. Calling dict gives you {0:21,1:22,2:23,3:23}. There is no key equivalent to 21 or 23, which will also give you an error.
What you could try is:
def twoSum(num_array, asum):
'''1.twoSum
Given an array of integers, return indices of the two numbers that
add up to a specific target.
'''
array = sorted(num_array)
l = 0
r = len(array)-1
while (l < len(array)-1) :
while (r > l):
if (array[l] + array[r]) == asum:
return [num_array.index(array[l]),\
num_array.index(array[r])]
r -= 1
r = len(array)-1
l += 1
num_array1 = [2, 7, 11, 15,1,0]
target1 = 9
twoSum(num_array1, target1)
This way, your l and r are both indices of the sorted array. It goes through every possible combination of values from the array, and returns when it either has found the sum or gone through everything. It then returns the index of the original num_array that contains the correct values.
Also, as #hiro-protagonist said, sum is a built-in function in Python already, so it should be changed to something else (asum in my example).
I am working on some homework for a class and the assignment is in codingbat.com. The problem is as follows:
Return the sum of the numbers in the array, returning 0 for an empty array. Except the number 13 is very unlucky, so it does not count and numbers that come immediately after a 13 also do not count.
So far, I have this:
def sum13(nums):
sum = 0
i = 0
while i in range(len(nums)):
if i == 13:
i += 2
else:
i += 1
return sum
Also, I had this code which works better:
def sum13(nums):
sum = 0
for i in range(len(nums)):
if nums[i] != 13:
sum += nums[i]
return sum
I know I am supposed to use a while loop, but I just don't get this.
Looks like you're almost there; you just need to actually add the value of nums[i] to sum in the appropriate place. Also reconsider the indentation of your return sum line.
It is more usual to use the for loops without using the range(). That kind of loop (in Python) is usually used for looping through the element values, not through the index values. When you need the index, use the enumerate() function to get both the index and the element value, like this (but you do not need it in the case):
...
for i, e in enumerate(nums):
do something with index and/or the element
...
The problem is not related to index values. This way, the for / while solutions differ only in accessing the elements of the array. You need to use indexing with the while but you do not need indexing with the for.
The problem with your while approach also is that you cannot simply skip the index after the value 13 because the next element can also contain 13. You need to store the value of the previous element and to use it for decision whether the current value should be added to the sum or not. It will be the same in both for and while solutions. Something like this:
last = 0 # init; whatever value different from 13
sum = 0
the chosen kind of loop:
e ... # current element from nums
if e != 13 bool_operator_here last != 13: # think about what boolean operator is
add the element to the sum
remember e as the last element for the next loop
sum contains the result
[Edited later] OK, you gave up. Here is the code that solves the problem:
def sumNot13for(nums):
last = 0 # init; whatever value different from 13
sum = 0
for e in nums:
if e != 13 and last != 13:
sum += e # add the element to the sum
last = e # remember e as the last element for the next loop
return sum
def sumNot13while(nums):
last = 0 # init; whatever value different from 13
sum = 0
i = 0 # lists/arrays use zero-based indexing
while i < len(nums):
e = nums[i] # get the current element
if e != 13 and last != 13:
sum += e # add the element to the sum
last = e # remember e as the last element for the next loop
i += 1 # the index must be incremented for the next loop
return sum
if __name__ == '__main__':
print(sumNot13for([2, 5, 7, 13, 15, 19]))
print(sumNot13while([2, 5, 7, 13, 15, 19]))
print(sumNot13for([2, 5, 7, 13, 13, 13, 13, 13, 13, 15, 19]))
print(sumNot13while([2, 5, 7, 13, 13, 13, 13, 13, 13, 15, 19]))