Fastest way to match 2d arrays of characters - python-3.x

There are two arrays that display a map(around 100x100) and a small template (part of this map less than 20x20). It is necessary to find all possible entrances of template (some boolean function for each possible matching).
For example some small map can be presented with:
#.#.#
.....
#####
Now I store it in list of strings. And nested loops works slowly.
What is the fastest way to work with such types of arrays? Is it possible to make generator of pairs and work with it? Or should I use built-in string functions for faster results?
def read():
n, m = map(int, input().split())
l = []
for _ in range(n):
l.append(input())
return n, m, l
n_m, m_m, base = read()
n_d, m_d, dust = read()
def check(i, j):
if i == '#' and j != '#':
return False
return True
def solve(y, x):
ans = True
for i in range(n_m):
for j in range(m_m):
ans &= check(base[i][j], dust[i + y][j + x])
return ans
cnt = 0
for i in range(n_d - n_m + 1):
for j in range(m_d - m_m + 1):
cnt += 1 if solve(i, j) else 0
print(cnt)

Related

Convert a number to binary in Python

Given a non-negative integer n, write a function to_binary/ToBinary which returns that number in a binary format.
This is my code but some tests don't pass.
I appreciate it if you help me fix my code:
def to_binary(n):
string = ""
if n > 2:
string = string + str(n % 2)
x = n // 2
while x >= 2:
i = x % 2
string = string + str(i)
x = x // 2
string = string + str(1)
l = len(string)
string[l::-1]
return int(string)
else:
if n == 1:
return 1
if n == 2:
return 10
Few points to note.
Changing the concatenation logic will generate the string in reverse. You won't have to reverse it in the end.
In [10]: s = ''
In [11]: for i in range(5):
...: s = s + str(i)
...:
In [12]: s
Out[12]: '01234'
In [13]: s = ''
In [14]: for i in range(5):
...: s = str(i) + s # add the existing string after i
...:
In [15]: s
Out[15]: '43210'
You don't require a different logic for numbers less than 2. You shouldn't have to hardcode anything unless you're using recursion. In which case, hardcoding is simply the base case.
You are not reversing the string at all.
s[::-1]
This does not reverse a string in-place. Strings in python are immutable. What you can do is,
s = s[::-1]
Not providing the limits in a slice syntax is the same as providing start and end values (0 and length). You don't have to explicitly write s[len(s)::-1].
Your logic is almost correct. Just remove everything and keep the while loop and the code will work.
def to_bin(x):
if x == 0:
return '0'
b = ''
while x > 0:
b = str(x%2) + b
x //= 2
return b
There are of course several ways to do this without writing code just by using the builtin features of python.
You could try the implementing the mathematical method of converting bases into Python. Every number can be expressed as the sum of the powers of a base. For binary, base 2, this would be N*2**n + ... + A*2**3 + B*2**2 + C*2**1 + D*2**0. The hardest part is finding N, but from there we can use divmod to solve for the other variables.
def get_highest_power(num, base=2):
n = 0
while True:
if base**(n+1) >= num:
break
n += 1
return n
def solve_for_coefficients(num, power, base=2):
coefficients = []
for p in range(power, -1, -1):
coef, num = divmod(num, base**p)
coefficients.append(coef)
return coefficients
leading_power = get_highest_power(1000)
coefficients = solve_for_coefficients(1000, leading_power)
In order to get the base conversion, just use something like int(''.join([str(i) for i in coefficients])). This method works for all bases less than 10, since bases after ten require letters (but then again, you could use look at the items in coefficients to get the letters).
If you just want to solve the problem "number to binary string", you could use simple python:
def to_binary(n):
return "{:b}".format(n)
Simply use bin() instead.
def to_binary(n):
return(bin(n)[2:])
to_binary(6)
'110'

I want to return the sum of all indexes in n via recursion. How do I do this?

def final_sum(n):
n = str(n)
if int(n) == 0:
return 0
else:
x = int(n[0])
return x + int(final_sum(n[1:]))
print(final_sum(123))
For example, if my n is 123, I should be getting 6. But I am having an error in this code. Can anyone help? I have to use recursive function. So tell me what's wrong with the code.
in return x + int(final_sum(n[1:])), n[1:] is str type
in the beginning of the function, with n = str(n), you assume the input is an int
Besides, you did not consider the case that n[1:] can be empty in return x + int(final_sum(n[1:])).
Here is an anwser based on your code
def final_sum(n):
if n == 0:
return 0
n = str(n)
x = int(n[0])
if len(n)==1:
return x
else:
return x + final_sum(int(n[1:]))
Here is another version using % operation
def final_sum(n):
if n < 10:
return n
return n % 10 + final_sum(n // 10)
First of all, at the beggining I would do this instead of casting back and forth:
def final_sum(n):
if n<10:
return n
You see, the problem is, in the last recursive iteration, you are passing this to the function:
final_sum("")
When you should be passing an int. I think this is happening because your last cast is backwards and you never check how many digits the number has. So the code should look like this:
def final_sum(n):
if n<10:
return n
n = str(n)
x = int(n[0])
return x + final_sum(int(n[1:]))
print(final_sum(123))

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

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

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

Resources