'list index out of range' in while loop designed to return two values from list that adds to a specific sum - python-3.5

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).

Related

Why is my python function not working properly when I call it recursively?

I'm doing a question from a previous Waterloo ccc competition (https://cemc.uwaterloo.ca/contests/computing/2020/ccc/juniorEF.pdf problem J5)
and my code isn't working the way I expected
Here's the sample input I'm using:
3
4
3 10 8 14
1 11 12 12
6 2 3 9
Here's my code so far
y_size = int(input())
x_size = int(input())
mat = []
"ok".split()
for i in range(y_size):
row = input().split()
mat.append(row)
pos_list = [[0, 0]]
current_num = int(mat[0][0])
a = 0
def canEscape():
global a
global mat
global pos_list
global current_num
end = y_size * x_size
if y_size -1 * x_size -1 == current_num:
return True
for i in range(y_size):
print("______")
for j in range(x_size):
v = (i + 1) * (j + 1)
print(v)
print(current_num)
if v == current_num:
print("ok")
if v == end:
print("ok")
a += 1
current_num = mat[i][j]
pos_list.append([i, j])
canEscape()
pos_list.pop(-1)
a -= 1
current_num = mat[pos_list[a][0]][pos_list[a][1]]
canEscape()
The problem I'm having is that I expect if v == current_num: to be true when I call it again. Both current_num and v are equal to 8 but the code seems to carry on with the for-in loop and break, without entering the if statement. I've made the output print v followed by current_num for every iteration of the for loop to try and figure out the problem but it seems that both variables == 8 so I really don't know what I did wrong. Did I make a silly mistake or did I structure my whole program wrong?
I'm having trouble following what your program is doing at all. This problem involves integer factoring, and I do not see where you're factoring integers. You definitely are not understanding that aspect of the problem.
When you calculate what cells you can go to you look at the value of your current cell. Lets say it is 6. 6 has the factors 1, 2, 3, and 6 because all of those numbers can be multiplied by another number to equal 6. So, you can go to the cells (1, 6), (6, 1), (2, 3), and (3, 2), because those are the pairs of numbers that can be multiplied together to equal 6.
Also, you never convert the lines of input into integers. When you append to the matrix, you are appending a list of strings that happen to be numbers. You must convert those into integers.
Anyways, this program will solve the problem. I copy and pasted the factoring algorithm from other threads:
n_rows = int(input())
n_cols = int(input())
mat = []
for i in range(n_rows):
mat.append(list(map(lambda x: int(x), input().split()))) # Convert input strings to integers.
def reduce(f, l):
# This is just needed for the factoring function
# It's not relevant to the problem
r = None
for e in l:
if r is None:
r = e
else:
r = f(r, e)
return r
def factors(n):
# An efficient function for calculating factors.
return set(reduce(list.__add__,
([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))
def get_pairs(items):
for i in range(len(items) // 2):
yield (items[i],items[len(items) - 1 - i]) # use yield to save memory
if(len(items) % 2 != 0): # This is for square numbers.
n = items[len(items) // 2]
yield (n,n)
checked_numbers = set()
def isPath(r=1, c=1):
# check if the testing row or column is to large.
if r > n_rows or c > n_cols:
return False
y = r - 1
x = c - 1
n = mat[y][x]
# If we've already checked a number with a certain value we dont need to check it again.
if n in checked_numbers:
return False
checked_numbers.add(n)
# Check if we've reached the exit.
if(r == n_rows and c == n_cols):
return True
# Calculate the factors of the number, and then find all valid pairs with those factors.
pairs = get_pairs(sorted(list(factors(n))))
# Remember to check each pair with both combinations of every pair of factors.
# If any of the pairs lead to the exit then we return true.
return any([isPath(pair[0], pair[1]) or isPath(pair[1], pair[0]) for pair in pairs])
if isPath():
print("yes");
else:
print("no");
This works and it is fast. However, it if you are limited on memory and/or have a large data input size your program could easily run out of memory. I think it is likely that this will happen with some of the testing inputs but I'm not sure.. It is surely possible to write this program in a way that would use a fraction of the memory, perhaps by converting the factors function to a function that uses iterators, as well as converting the get_pairs function to somehow iterate as well.
I would imagine that this solution solves most of the testing inputs they have but will not solve the ones towards the end, because they will be very large and it will run out of memory.

How can i sum co-prime numbers in a pair

I have this list
a = [1,2,3,4,5,6,7,8,9]
I want to find out that how many co-prime pair elements of the list add up to sum=9
Ex, (1+8) = 9 , (2+7) = 9 , (3+6)=9 , (4+5)=9, (5+4)=9 , (6+3)=9, (7+2)=9 , (8+1)=9
Note that i don't want (3+6) as they are prime numbers. And i also don't want (7+2)=9 as it has already occurred (means 2,7 has been already taken in account)
I tried this But it takes repeated values too.
a = [1,2,3,4,5,6,7,8,9]
count=0
for m in a:
for n in a:
total=m+n
if(total==9):
s=str(m) + '+'+ str(n) + "="
print(s , m+n)
count=count+1
print("Count =" ,count)
The result should have count=3
Your mistake is in the way of doing the loops, so you repeat values.
Try this:
#from math import gcd as bltin_gcd
a = [1,2,3,4,5,6,7,8,9]
count = 0
def __gcd(a, b):
# Everything divides 0
if (a == 0 or b == 0): return 0
# base case
if (a == b): return a
# a is greater
if (a > b):
return __gcd(a - b, b)
return __gcd(a, b - a)
# Only python 3
# def coprime(a, b):
# return bltin_gcd(a, b) == 1
for i in range(0,9):
for j in range(i+1,9):
if __gcd(a[i], a[j]) == 1 and a[i] + a[j] == 9:
count += 1
print str(a[i]) + ' ' + str(a[j])
print 'Count = ' + str(count)
In number theory, two integers a and b are said to be relatively prime, mutually prime, or coprime if the only positive integer that divides both of them is 1. Consequently, any prime number that divides one does not divide the other. This is equivalent to their greatest common divisor being 1.
for m in a:
for n in a:
You are not selecting pairs by using this loops, ie. you are picking the first element in both the outer and inner loop during your first iteration.
if(total==9):
You are not checking the condition if the selected pair of numbers are coprime. You are only verifying the sum.
A pythonic solution may be obtained with a one-liner:
from math import gcd
a = [1,2,3,4,5,6,7,8,9]
pairs = [(m,n) for m in a for n in a if n > m and m+n == 9 and gcd(m,n) == 1]
Result :
pairs --> [(1, 8), (2, 7), (4, 5)]
If you are sure to never, never need the pairs but only the number of pairs (as written in the OP), the most efficient solution may be:
count = len([1 for m in a for n in a if n > m and m+n == 9 and gcd(m,n) == 1])
EDIT : I inversed the three conditions in the if statement for improved benefit from lazy boolean evaluation
You can solve this if you have something that calculates your prime factorization in python:
from functools import lru_cache
# cached function results for pime factorization of identical nr
#lru_cache(maxsize=100)
def factors(nr):
# adapted from https://stackoverflow.com/a/43129243/7505395
i = 2
factors = []
while i <= nr:
if (nr % i) == 0:
factors.append(i)
nr = nr / i
else:
i = i + 1
return factors
start_at = 1
end_at = 9
total = 9
r = range(start_at, end_at+1)
# create the tuples we look for, smaller number first - set so no duplicates
tupls = set( (a,b) if a<b else (b,a) for a in r for b in r if a+b == total)
for n in tupls:
a,b = n
f_a = set(factors(a))
f_b = set(factors(b))
# if either set contains the same value, the f_a & f_b will be truthy
# so not coprime - hence skip it
if f_a & f_b:
continue
print(n)
Output:
(2, 7)
(1, 8)
(4, 5)

I wonder how is second 'return' works - return len(A) + 1

This is a coding lesson from Github.
I wonder how the second return, return len(A) + 1 works.
list A is made form 1 to N+1 with 1 number missing
N is number 0 - 100000
ever number in list A is different
The solution is find the missing number.
def solution(A):
index_dict = {index+1: value for index, value in enumerate(A)}
value_dict = {value: index+1 for index, value in enumerate(A)}
for k in index_dict:
if k not in value_dict:
return k
return len(A) + 1
I figured out until return k. wondering how the return len(A) + 1 works
Thank you in advance for any answers.
The last line, return len(A) + 1 is the value that will be returned if all of the values of index_dict can be found from value_dict. Thus it will fall back to returning the value of N + 1, because it had to be the one that was missing from the list.
The solution can actually be even simplified and made more efficient by using sets instead of dicts:
def solution(A):
index_set = {index + 1 for index, _ in enumerate(A)}
value_set = {value for value in A}
for k in index_set:
if k not in value_set:
return k
return len(A) + 1
Then we also notice that we can remove the set comphrenesion and just call set(A) and that we can replace the index_set with a range object:
def solution(A):
set_A = set(A) # store this, so it doesn't have to be recomputed on every loop
for k in range(1, len(A) + 1):
if k not in set_A:
return k
return len(A) + 1
We can also just increase the range by 1 and remove the last return completely. This solution is actually way more effiecient than the original one and is around 7x faster on my machine:
def solution(A):
set_A = set(A)
for k in range(1, len(A) + 2):
if k not in set_A:
return k
Not gonna get more efficient than that, but just for fun, we can make a set out of the range, and return the single element that's left over from the difference of that and set(A):
def solution(A):
return set(range(1, len(A) + 2)).difference(set(A)).pop()

Quick sort counting

Python questions again.
I want to count the number of comparison operations performed by quick sort. Because I use a recursive function, I do not think that assigning count = 0 to the beginning of the function body is inappropriate, so I made it as follows.
def QuickSort(lst, count = 0):
if len(lst) > 1:
pivot_idx = len(lst) // 2
smaller_nums, larger_nums = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
if num < lst[pivot_idx]:
smaller_nums.append(num)
else:
larger_nums.append(num)
count = QuickSort(smaller_nums, count + 1)[1]
count = QuickSort(larger_nums, count + 1)[1]
lst[:] = smaller_nums + [lst[pivot_idx]] + larger_nums
return lst, count
However, after counting, I confirmed the count which is much lower than my expectation. According to big o, the quick sort would have to show the calculation of n * log (n), but it showed a much lower count. For example, when sorting a list with 1000 random elements, we expected to see a count of 1000 * log (1000) = 6907, but actually only 1164 counts. I am wondering if I am misusing the count in the function or misunderstanding it.
Thank you.
Your post is mistaken on several points:
Big-O is allows arbitrary constant factors and also ignoring the values for "small" values of n, where "small" can be arbitrarily large for any given analysis. So your computations are meaningless.
Your counts are wrong. There's one comparison per loop iteration. You're counting something else.
This is a strange way to code the count. Just use a global variable.
Try this. Note really you're using twice as many comparisons as this reports. The check that the loop index isn't the pivot could be eliminated with a smarter implementation.
c = 0
def QuickSort(lst):
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
global c
c += 1
(larger, smaller)[num < lst[pivot_idx]].append(num)
return QuickSort(smaller) + [lst[pivot_idx]] + QuickSort(larger)
def Run(n):
lst = [random.randint(0,1000) for r in xrange(n)]
QuickSort(lst)
print c
Run(1000)
If you're aghast at the prospect of using a global variable, then you can just wrap the sort in a class:
import random
class QuickSort:
def __init__(self):
self.comparisons = 0
def sort(self, lst):
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
self.comparisons += 1
(larger, smaller)[num < lst[pivot_idx]].append(num)
return self.sort(smaller) + [lst[pivot_idx]] + self.sort(larger)
def Run(n):
lst = [random.randint(0,1000) for r in xrange(n)]
quicksort = QuickSort()
print quicksort.sort(lst)
print quicksort.comparisons
Run(100)
Building on the answer provided by Gene by adding print statements and a sort "error" range, his example was very helpful to my understanding of quicksort and an error term on the big O impact of operations performance comparison.
class QuickSort:
def __init__(self):
self.comparisons = 0
def sort(self, lst):
k_err = 0 # k << n, the value the sort array can be in error
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != (pivot_idx) :
self.comparisons += 1
try:
(larger, smaller)[(num - k_err) < lst[pivot_idx]].append(num)
except:
(larger, smaller)[(num + k_err) < lst[pivot_idx]].append(num)
print(pivot_idx,"larger", self.comparisons, larger)
print(pivot_idx, "smaller", self.comparisons, smaller, )
return self.sort(smaller) + [lst[pivot_idx]] + self.sort(larger)
def Run(n):
random.seed(100)
lst = [random.randint(0,round(100,0)) for r in range(n)]
quicksort = QuickSort()
print(len(lst), lst)
print(quicksort.sort(lst))
print(quicksort.comparisons, quicksort.comparisons/n, ((quicksort.comparisons/n)/math.log(n,10)), math.log(n,10) )

Printing on the same line different Permuations of a value

Hey guys so here is my question. I have written code that sums two prime numbers and prints the values less than or equal to 100 and even. How do I write it so that every combination of the number prints on the same line
like so
100 = 3 + 97 = 11 + 89
def isPrime(n):
limit = int(n ** 0.5) +1
for divisor in range (2, limit):
if (n % divisor == 0):
return False
return True
def main():
a = 0
b = 0
for n in range (4, 101):
if (n % 2 == 0):
for a in range (1, n + 1):
if isPrime(a):
for b in range (1, n + 1):
if isPrime(b):
if n == (a + b):
print ( n, "=", a, "+", b)
main()
any ideas?
I don't know too much about strings yet, but I was thinking we could set the string as n == a + b and some how repeat on the same line where n == n print the a + b statement or idk haha
One way to do this is to accumulate a and b pairs in some collection, then print a line containing all the pairs. Here's an example with some comments explaining whats going on and general Python tips:
def main():
for n in range (4, 101, 2): # range() can have third argument -> step
accumulator = []
for a in filter(isPrime, range(1, n + 1)): # filter() is useful if you want to skip some values
for b in filter(isPrime, range (1, n + 1)):
if n == (a + b):
accumulator.append((a,b)) # We accumulate instead of printing
str_accumulator = ["{} + {}".format(i[0], i[1]) for i in accumulator]
joined_accumulator = " = ".join(str_accumulator)
print("{} = {}".format(n, joined_accumulator))
Now, some explanation:
range(4, 101, 2) - as said in comment, it has an optional third argument. Some examples and explanations on how to use range in documentation.
filter() - Very useful generic iterator constructor. You pass a function that returns True/False, a collection, and you receive an iterator that spits out only those elements from the collection that are accepted by the function. See documentation.
str.format - For me, format is the best way to paste values into strings. It has PLENTY options and is very versatile. You should read the whole documentation here.
str.join - When you have a collection of string, and you want to make one string of them, join is what you want. It's much faster than str + str operation, and also you don't have to care if there is one or many elements in the collection. See documentation.

Resources