I have the problem that I want to count the number of combinations that fulfill the following condition:
a < b < a+d < c < b+d
Where a, b, c are elements of a list, and d is a fixed delta.
Here is a vanilla implementation:
def count(l, d):
s = 0
for a in l:
for b in l:
for c in l:
if a < b < a + d < c < b + d:
s += 1
return s
Here is a test:
def testCount():
l = [0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 5, 7, 7, 8, 9, 9, 10, 10]
assert(32 == count(l, 4)) # Gone through everything by hand.
Question
How can I speed this up? I am looking at list sizes of 2 Million.
Supplementary Information
I am dealing with floats in the range of [-pi, pi]. For example, this limits a < 0.
What I have so far:
I have some implementation where I build indices that I use for b and c. However, the below code fails some cases. (i.e. This is wrong).
def count(l, d=pi):
low = lower(l, d)
high = upper(l, d)
s = 0
for indA in range(len(l)):
for indB in range(indA+1, low[indA]+1):
s += low[indB] + 1 - high[indA]
return s
def lower(l, d=pi):
'''Returns ind, s.t l[ind[i]] < l[i] + d and l[ind[i]+1] >= l[i] + d, for all i
Input must be sorted!
'''
ind = []
x = 0
length = len(l)
for elem in l:
while x < length and l[x] < elem + d:
x += 1
if l[x-1] < elem + d:
ind.append(x-1)
else:
assert(x == length)
ind.append(x)
return ind
def upper(l, d=pi):
''' Returns first index where l[i] > l + d'''
ind = []
x = 0
length = len(l)
for elem in l:
while x < length and l[x] <= elem + d:
x += 1
ind.append(x)
return ind
Original Problem
The original problem is from a well known math/comp-sci competition. The competition asks that you don't post solutions on the net. But it is from two weeks ago.
I can generate the list with this function:
def points(n):
x = 1
y = 1
for _ in range(n):
x = (x * 1248) % 32323
y = (y * 8421) % 30103
yield atan2(x - 16161, y - 15051)
def C(n):
angles = points(n)
angles.sort()
return count(angles, pi)
There is an approach to your problem that yields an O(n log n) algorithm. Let X be the set of values. Now let's fix b. Let A_b be the set of values { x in X: b - d < x < b } and C_b be the set of values { x in X: b < x < b + d }. If we can find |{ (x,y) : A_b X C_b | y > x + d }| fast, we solved the problem.
If we sort X, we can represent A_b and C_b as pointers into the sorted array, because they are contiguous. If we process the b candidates in non-decreasing order, we can thus maintain these sets using a sliding window algorithm. It goes like this:
sort X. Let X = { x_1, x_2, ..., x_n }, x_1 <= x_2 <= ... <= x_n.
Set left = i = 1 and set right so that C_b = { x_{i + 1}, ..., x_right }. Set count = 0
Iterate i from 1 to n. In every iteration we find out the number of valid triples (a,b,c) with b = x_i. To do that, increase left and right as much as necessary so that A_b = { x_left, ..., x_{i-1} } and C_b = { x_{i + 1}, ..., x_right } still holds. In the process, you basically add and remove elements from the imaginary sets A_b and C_b.
If you remove or add an element to one of the sets, check how many pairs (a, c) with c > a + d, a from A_b and c from C_b you add or destroy (this can be achieved by a simple binary search in the other set). Update count accordingly so that the invariant count = |{ (x,y) : A_b X C_b | y > x + d }| still holds.
sum up the values of count in every iteration. This is the final result.
The complexity is O(n log n).
If you want to solve the Euler problem with this algorithm, you have to avoid floating point issues. I suggest sorting the points by angle using a custom comparison function that uses integer arithmetics only (using 2D vector geometry). Implementing the |a-b| < d comparisons can also be done using integer operations only. Also, since you are working modulo 2*pi, you would probably have to introduce three copies of every angle a: a - 2*pi, a and a + 2*pi. You then only look for b in the range [0, 2*pi) and divide the result by three.
UPDATE OP implemented this algorithm in Python. Apparently it contains some bugs but it demonstrates the general idea:
def count(X, d):
X.sort()
count = 0
s = 0
length = len(X)
a_l = 0
a_r = 1
c_l = 0
c_r = 0
for b in X:
if X[a_r-1] < b:
# find boundaries of A s.t. b -d < a < b
while a_r < length and X[a_r] < b:
a_r += 1 # This adds an element to A_b.
ind = bisect_right(X, X[a_r-1]+d, c_l, c_r)
if c_l <= ind < c_r:
count += (ind - c_l)
while a_l < length and X[a_l] <= b - d:
a_l += 1 # This removes an element from A_b
ind = bisect_right(X, X[a_l-1]+d, c_l, c_r)
if c_l <= ind < c_r:
count -= (c_r - ind)
# Find boundaries of C s.t. b < c < b + d
while c_l < length and X[c_l] <= b:
c_l += 1 # this removes an element from C_b
ind = bisect_left(X, X[c_l-1]-d, a_l, a_r)
if a_l <= ind <= a_r:
count -= (ind - a_l)
while c_r < length and X[c_r] < b + d:
c_r += 1 # this adds an element to C_b
ind = bisect_left(X, X[c_r-1]-d, a_l, a_r)
if a_l <= ind <= a_r:
count += (ind - a_l)
s += count
return s
from bisect import bisect_left, bisect_right
from collections import Counter
def count(l, d):
# cdef long bleft, bright, cleft, cright, ccount, s
s = 0
# Find the unique elements and their counts
cc = Counter(l)
l = sorted(cc.keys())
# Generate a cumulative sum array
cumulative = [0] * (len(l) + 1)
for i, key in enumerate(l, start=1):
cumulative[i] = cumulative[i-1] + cc[key]
# Pregenerate all the left and right lookups
lefthand = [bisect_right(l, a + d) for a in l]
righthand = [bisect_left(l, a + d) for a in l]
aright = bisect_left(l, l[-1] - d)
for ai in range(len(l)):
bleft = ai + 1
# Search only the values of a that have a+d in range
if bleft > aright:
break
# This finds b such that a < b < a + d.
bright = righthand[ai]
for bi in range(bleft, bright):
# This finds the range for c such that a+d < c < b+d.
cleft = lefthand[ai]
cright = righthand[bi]
if cleft != cright:
# Find the count of c elements in the range cleft..cright.
ccount = cumulative[cright] - cumulative[cleft]
s += cc[l[ai]] * cc[l[bi]] * ccount
return s
def testCount():
l = [0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 5, 7, 7, 8, 9, 9, 10, 10]
result = count(l, 4)
assert(32 == result)
testCount()
gets rid of repeated, identical values
iterates over only the required range for a value
uses a cumulative count across two indices to eliminate the loop over c
cache lookups on x + d
This is no longer O(n^3) but more like O(n^2)`.
This clearly does not yet scale up to 2 million. Here are my times on smaller floating point data sets (i.e. few or no duplicates) using cython to speed up execution:
50: 0:00:00.157849 seconds
100: 0:00:00.003752 seconds
200: 0:00:00.022494 seconds
400: 0:00:00.071192 seconds
800: 0:00:00.253750 seconds
1600: 0:00:00.951133 seconds
3200: 0:00:03.508596 seconds
6400: 0:00:10.869102 seconds
12800: 0:00:55.986448 seconds
Here is my benchmarking code (not including the operative code above):
from math import atan2, pi
def points(n):
x, y = 1, 1
for _ in range(n):
x = (x * 1248) % 32323
y = (y * 8421) % 30103
yield atan2(x - 16161, y - 15051)
def C(n):
angles = sorted(points(n))
return count(angles, pi)
def test_large():
from datetime import datetime
for n in [50, 100, 200, 400, 800, 1600, 3200, 6400, 12800]:
s = datetime.now()
C(n)
elapsed = datetime.now() - s
print("{1}: {0} seconds".format(elapsed, n))
if __name__ == '__main__':
testCount()
test_large()
Since l is sorted and a < b < c must be true, you could use itertools.combinations() to do fewer loops:
sum(1 for a, b, c in combinations(l, r=3) if a < b < a + d < c < b + d)
Looking at combinations only reduces this loop to 816 iterations.
>>> l = [0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 5, 7, 7, 8, 9, 9, 10, 10]
>>> d = 4
>>> sum(1 for a, b, c in combinations(l, r=3))
816
>>> sum(1 for a, b, c in combinations(l, r=3) if a < b < a + d < c < b + d)
32
where the a < b test is redundant.
1) To reduce amount of iterations on each level you can remove elements from list that dont pass condition on each level
2) Using set with collections.counter you can reduce iterations by removing duplicates:
from collections import Counter
def count(l, d):
n = Counter(l)
l = set(l)
s = 0
for a in l:
for b in (i for i in l if a < i < a+d):
for c in (i for i in l if a+d < i < b+d):
s += (n[a] * n[b] * n[c])
return s
>>> l = [0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 5, 7, 7, 8, 9, 9, 10, 10]
>>> count(l, 4)
32
Tested count of iterations (a, b, c) for your version:
>>> count1(l, 4)
18 324 5832
my version:
>>> count2(l, 4)
9 16 7
The basic ideas are:
Get rid of repeated, identical values
Have each value iterate only over the range it has to iterate.
As a result you can increase s unconditionally and the performance is roughly O(N), with N being the size of the array.
import collections
def count(l, d):
s = 0
# at first we get rid of repeated items
counter = collections.Counter(l)
# sort the list
uniq = sorted(set(l))
n = len(uniq)
# kad is the index of the first element > a+d
kad = 0
# ka is the index of a
for ka in range(n):
a = uniq[ka]
while uniq[kad] <= a+d:
kad += 1
if kad == n:
return s
for kb in range( ka+1, kad ):
# b only runs in the range [a..a+d)
b = uniq[kb]
if b >= a+d:
break
for kc in range( kad, n ):
# c only rund from (a+d..b+d)
c = uniq[kc]
if c >= b+d:
break
print( a, b, c )
s += counter[a] * counter[b] * counter[c]
return s
EDIT: Sorry, I messed up the submission. Fixed.
Related
Is there a short way to detect the longest sublist with alternative signs within a list?
For instance:
my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
returning 4 starting from -0.5 to 4?
This is what I have written so far but I feel there is room for something much shorter.
import numpy
my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
# function that detects whether a list has alternate signs
# https://stackoverflow.com/questions/6451514/detect-alternating-signs
def is_alternating_signs(a):
return numpy.all(numpy.abs(numpy.diff(numpy.sign(a))) == 2)
# getting all sublists from the main list
sublists = []
for i in range(len(my_list) + 1):
for j in range(i + 1, len(my_list) + 1):
sublists.append(my_list[i:j])
# detecting the longest sublist with alternate signs
max_list = 0
for sublist in sublists:
if is_alternating_signs(sublist) and len(sublist) > max_list:
max_list = len(sublist)
print(max_list)
Use zip to compare the current element with the next one:
maxlen = 1
curlen = 1
for i, j in zip(l, l[1:]):
# if one conditions match
# increment curlen by 1
if (i < 0 and j > 0) or (i > 0 and j < 0):
curlen += 1
# break the alternative sign
# keep the highest value between maxlen and curlen
# reset curlen to 1
else:
maxlen = max(maxlen, curlen)
curlen = 1
maxlen = max(maxlen, curlen)
Output:
>>> maxlen
4
You can use zip to detect the positions of 'breaks' in the alternance. Then combine these breaks into ranges to find the longest streak of alternating values:
L = [-1, -0.5, 1, -3, 4, 5, 5, -1]
breaks = [i for i,(a,b) in enumerate(zip(L,L[1:]),1) if (a<0)==(b<0)]
longest = max((L[s:e] for s,e in zip([0]+breaks,breaks+[None])),key=len)
print(longest)
[-0.5, 1, -3, 4]
If you're only looking for the length of the streak, you could convert the zip result to a string of 1s and 0s, then split on 0s and measure the longest substring:
max(map(len,"".join("01"[a*b<0] for a,b in zip(L,L[1:])).split('0')))+1
4
What about a single loop?
def max_alt_subseq_size(seq):
last_x = seq[0]
size = max_size = 1
for x in seq[1:]:
# use the fact that x * y < 0 iff x > 0 and y < 0 or x < 0 and y > 0
if last_x * x < 0:
size += 1
else:
# once the size of the alternating subsequence is found, we need to check if it is the largest
if size > max_size:
max_size = size
size = 1
last_x = x
# check on the final subsequence to see if it is the largest
if size > max_size:
max_size = size
return max_size
my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
max_alt_subseq_size(my_list)
# 4
One could have a (number of) fully vectorized approach.
The code below assumes a NumPy 1D array as input.
For example, if one computes the run-length encoding (RLE) in a vectorized fashion, it would be simple to use RLE information on some array that represents where the signs change to compute the desired value
import numpy as np
def rle(arr):
n = len(arr)
if n == 0:
values = np.empty(0, dtype=arr.dtype)
lengths = np.empty(0, dtype=np.int_)
else:
positions = np.concatenate(
[[-1], np.nonzero(arr[1:] != arr[:-1])[0], [n - 1]])
lengths = positions[1:] - positions[:-1]
values = arr[positions[1:]]
return values, lengths
def max_alt_rle(arr):
values, lengths = rle(arr[1:] * arr[:-1] < 0)
subs_lengths = lengths[values]
return (1 if len(arr) > 0 else 0) + \
(np.max(subs_lengths) if len(subs_lengths) > 0 else 0)
Alternatively, one could make good use of the richer functionalities available to Strings/Bytes, notably str.split() to craft a very short, vectorized, but not very efficient solution:
def max_alt_np(arr):
return (1 if len(arr) > 0 else 0) + \
len(max((arr[1:] * arr[:-1] < 0).tobytes().split(b'\x00')))
If one is after raw speed, accelerating with Numba the single loop solution would be most efficient and fast solution:
import numba as nb
#nb.jit
def max_alt_nb(arr):
if len(arr):
last_x = arr[0]
size = max_size = 1
for x in arr[1:]:
if last_x * x < 0:
size += 1
else:
if size > max_size:
max_size = size
size = 1
last_x = x
if size > max_size:
max_size = size
return max_size
else:
return 0
Finally, here is reported an adaptation of the currently accepted answer, which is neither efficient nor fast, but it is relatively compact (but not as compact as max_alt_np and considerably slower) and can use lists without prior conversion to a NumPy array:
def max_alt_str(arr):
return (1 if len(arr) > 0 else 0) + len(max(
("".join(
"01"[1 if a * b < 0 else 0]
for a, b in zip(arr[:-1], arr[1:])))
.split("0")))
Here some benchmarks on random integer arrays of varying size:
(Full analysis here).
I have code where given a list of ordered integers I generate several new lists, and I need to be able to map these lists one-to-one with the integers from 1 to N choose k (or 0 to (N choose k)-1) in python).
So for example if I have a N=7, k=3, then I might start with the list [0,1,2] which I might enumerate by 0. Then I generate the lists [0,1,3], [0,1,5], and [3,4,6] which all need to be uniquely identified by an integer in the range 0,...,34.
I know that I could use itertools to generate the ordered lists,
itertools.combinations(range(7), 3)
but I need an inverse function which is more efficient than simply searching through a pregenerated list since I will be working with large N and each list references around 50 new lists.
You can use this recipe from the itertools recipe here:
def nth_combination(iterable, r, index):
'Equivalent to list(combinations(iterable, r))[index]'
pool = tuple(iterable)
n = len(pool)
if r < 0 or r > n:
raise ValueError
c = 1
k = min(r, n-r)
for i in range(1, k+1):
c = c * (n - k + i) // i
if index < 0:
index += c
if index < 0 or index >= c:
raise IndexError
result = []
while r:
c, n, r = c*r//n, n-1, r-1
while index >= c:
index -= c
c, n = c*(n-r)//n, n-1
result.append(pool[-1-n])
return tuple(result)
Example usage:
>>> nth_combination(range(7), 3, 5)
(0, 2, 3)
>>> nth_combination(range(7), 3, 34)
(4, 5, 6)
To reverse:
from math import factorial
def get_comb_index(comb, n):
k = len(comb)
rv = 0
curr_item = 0
n -= 1
for offset, item in enumerate(comb, 1):
while curr_item < item:
rv += factorial(n-curr_item)//factorial(k-offset)//factorial(n+offset-curr_item-k)
curr_item += 1
curr_item += 1
return rv
Example Usage:
>>> get_comb_index((4,5,6), 7)
34
>>> get_comb_index((0,1,2), 7)
0
>>> get_comb_index((0,2,4), 7)
6
Given an array of even and odd numbers, I want to get the number of (even-even) and (odd-odd) pairs whose XOR is greater than or equal to 4. I tried this with the code below but it runs in O(n^2), (yikes). Please can anyone suggest a means of optimization?
n = int(raw_input()) #array size
ar = map(int, raw_input().split()) #the array
cnt = 0
for i in xrange(len(ar)):
for j in xrange(i+1, len(ar)):
if ar[i] ^ ar[j] >= 4 and (not ar[i] & 1 and not ar[j] & 1):
cnt += 1; #print ar[i],ar[j],ar[i]^ar[j];
elif ar[i] ^ ar[j] >= 4 and (ar[i] & 1 and ar[j] & 1):
cnt += 1
print cnt
EDIT: I discovered something. any number x, which gives a remainder after % 4, i.e x % 4 != 0, will result to 2 when XORed to a number -2 itself. For example, 6. It is not divisible by 4, therefore, 6 XOR 6-2 (4),==> 2. 10 is not divisible by 4, hence, 10 XOR 10-2 (8) ==> 2. Can you please tell me how this could help me optimize my code? I just know now that I will just look for numbers divisible by 4 and find the count of their + 2.
For simplicity, let´s assume the array does not have duplicates. For the XOR between 2 numbers to be >= 4, they need to have any different bit (excluding lower 2 bits). Given that we already know they are even-even or odd-odd pairs, their lowest bit is the same.
Note that for any number X, X XOR (X + 4 + k) will always be >= 4. So the problem is considering what happens with X XOR (X + 1), X XOR (X + 2) and X XOR (X + 3).
X XOR (X + 1) will be >= 4 when the third lowest bit has changed by adding only 1. That means, we had X ending in 011 so X + 1 ends in 100 or we had X ending in 111 so X + 1 ends in 000. In both cases, this means X % 4 = 3. In any other case (X % 4 != 3), X XOR (X + 1) will be < 4.
For X XOR (X + 2) to be >= 4, the third lowest bit has changed by adding 2. This means, X ended in 011, 010, 111, or 110. So we now have X % 4 = 3 or X % 4 = 2.
For X Xor (X + 3) to be >= 4, the third lowest bit has changed by adding 3. This means, X ended in 011, 010, 001, 111, 110, 101. So we now have X % 4 = 3, X % 4 = 2 or X % 4 = 1.
Here is pseudocode:
for each element in array:
count[element] += 1
total += 1
for each X in sorted keys of count:
if X % 4 == 3:
answer += count[X + 1] + count[X + 2] + count[X + 3]
if X % 4 == 2:
answer += count[X + 2] + count[X + 3]
if X % 4 == 1:
answer += count[X + 3]
total -= count[X]
answer += total - (count[X + 1] + count[X + 2] + count[X + 3]) # all X + 4 + K work
To account for duplicates, we need to avoid counting a number against itself. You will need to keep the count of each number, and do the same as the above with the modification that the answer will be the count of that number * (all the others - the amount of X + 2 numebers)
You should work on separating your code, one improvement is the use of set to avoid repeating operations, although it may get more memory overhead.
import random
from operator import xor
import itertools
random.seed(10)
in_values = [random.randint(0, 10) for _ in range(100)]
def get_pairs_by(condition, values):
odds = set(filter(lambda x: x % 2 == 0, values))
evens = set(filter(lambda x: x % 2 == 1, values))
def filter_pairs_by_condition(values):
return (
(x, y) for x, y in set(
map(lambda x: tuple(sorted(x)),
itertools.product(iter(values), iter(values))))
if condition(xor(x, y))
)
return list(
itertools.chain.from_iterable(
(filter_pairs_by_condition(x) for x in (odds, evens))
)
)
print(get_pairs_by(lambda x: x >= 4, in_values))
The keypoint is:
set(map(lambda x: tuple(sorted(x)),
itertools.product(iter(values), iter(values)))))
What we are doing is that pairs of (5, 7) and (7, 5) should be evaluated as being the same, so we take rid of them there.
Here you have the live example
EDIT:
As a quick update to your code, you can use a dictionary to memoize previously computed pairs, hence:
n = int(raw_input()) #array size
ar = map(int, raw_input().split()) #the array
cnt = 0
prev_computed = {}
for i in xrange(len(ar)):
for j in xrange(i+1, len(ar)):
if any(x in prev_compued for x in ((ar[i], ar[j]), (ar[j], ar[i]))):
cnt += 1
continue
if ar[i] ^ ar[j] >= 4 and (not ar[i] & 1 and not ar[j] & 1):
cnt += 1; #print ar[i],ar[j],ar[i]^ar[j];
prev_computed[(ar[i], ar[j])] = True
prev_computed[(ar[j], ar[i])] = True
elif ar[i] ^ ar[j] >= 4 and (ar[i] & 1 and ar[j] & 1):
cnt += 1
prev_computed[(ar[i], ar[j])] = True
prev_computed[(ar[j], ar[i])] = True
print cnt
def xor_sum(lst)
even_dict = a dictionary with keys being all even numbers of lst and values being their frequencies
odd_dict = a dictionary with keys being all odd numbers of lst and values being their frequencies
total_even_freq = sum of all frequencies of even numbers
total_odd_freq = sum of all frequencies of odd numbers
even_res = process(even_dict, total_even_freq)
odd_res = process(odd_dict, total_odd_freq)
return even_res + odd_res
def process(dict, total_freq)
res = 0
for num in dict.keys
# LSB of XOR of 2 even numbers is always 0
# Let p = XOR of 2 even numbers; if p < 4 then p = 00000000 (minus_2) or 00000010 (plus_2)
plus_2 = num+2
minus_2 = num-2
count = 0
if( (plus_2 XOR num) < 4 and (plus_2 is a key of dict) )
count = count + frequency_of_plus_2
if( (minus_2 XOR num) < 4 and (minus_2 is a key of dict) )
count = count + frequency_of_minus_2
count = count + num
res = res + (total_freq+1-count)
return res
Complexity:
Assuming you have a good hash function for your dictionaries (a hashmap), the average time complexity is O(n)
So I'm trying to make a function that takes an integer 'n' and returns a list of lists, and this is all in the vein of making a 'magic square' (start at 1 in the top center, then go right and up to the next, all with a 'wraparound' effect). Anyway, I feel my code is super clunky but also I can't test if it works because well it doesn't.. I get a list index out of range message for the line msq[row][col] = v. Here's the code:
def magicsquare(n):
msq = [[0 for c in range(n)] for r in range(n)]
row, col= n-1, (n-1)/2
M = n*(n+1)/2
v, r, c = 1,0,0
msq[row][col] = v
while v != M:
v= v+1
if row+1 >= n:
r = 0
else: r = row + 1
if (col+1) < n:
c = col + 1
else: c = 0
if msq[r][c]:
if (row+1) < n:
r = row+1
c = col
grid[r][c] = v
row = r
col = c
return magicsquare(n)
oh and the test I'm trying to pass is magicsquare(3) == magicsquare([[4, 3, 8], [9, 5, 1], [2, 7, 6]]). Any help is appreciated, thank you!!!!!!!
I am trying to implement the merge sort algorithm described in these notes by Jeff Erickson on page 3. but even though the algorithm is correct and my implementation seems correct, I am getting the input list as output without any change. Can someone point out the anomalies, if any, in it.
def merge(appnd_lst, m):
result = []
n = len(appnd_lst)
i, j = 0, m
for k in range(0, n):
if j < n:
result.append(appnd_lst[i])
i += 1
elif i > m:
result.append(appnd_lst[j])
j += 1
elif appnd_lst[i] < appnd_lst[j]:
result.append(appnd_lst[i])
i += 1
else:
result.append(appnd_lst[j])
j += 1
return result
def mergesort(lst):
n = len(lst)
if n > 1:
m = int(n / 2)
left = mergesort(lst[:m])
right = mergesort(lst[m:])
appnd_lst = left
appnd_lst.extend(right)
return merge(appnd_lst, m)
else:
return lst
if __name__ == "__main__":
print mergesort([3, 4, 8, 0, 6, 7, 4, 2, 1, 9, 4, 5])
There are three errors in your merge function a couple of indexing errors and using the wrong comparison operator. Remember python list indices go from 0 .. len(list)-1.
* ...
6 if j > n-1: # operator wrong and off by 1
* ...
9 elif i > m-1: # off by 1
* ...