moving through variables in matrix with recursion - python

I'm trying to write a function which got a kind of a matrix - a list that consists of sub-lists
So I want to go through all the matrix members and print them out. Only in recursion!
But I dont know how to create a good "stop conditions" for my function.
So I get more numbers than I wanted.
def mission(mat):
def move(mat, i=0, j=0 ,k=0):
print(mat[i][j])
if j<(len(mat[0])-1):
move(mat,i,j+1)
if i<(len(mat)-1):
move(mat,i+1,0)
move(mat)
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
mission(mat)
*edit
I got another question - is there a way to decrease 2 list that looks like the mat function i did here (with the same length - just different numbers)
without using numpy or for ?

You can test for end condition on function start, and check if you have already crossed the last item in the last sublist. If so, you return.
if(i == len(mat) - 1 and j == len(mat[0]) - 1):
return
Then, you check whether you are at the last item in your current sublist, and if so, increment the sublist index i and set the item index back to 0.
if(j == len(mat[0]) - 1):
i += 1
j = 0
If you are neither at the end of the whole 2d matrix (list), nor the end of any sublist, you just need to increment the item index j.
else:
j += 1
Then, you can safely call your function recursively. The whole code ends up looking like this.
def move(mat, i=0, j=0):
print(mat[i][j])
if(i == len(mat) - 1 and j == len(mat[0]) - 1):
return
if(j == len(mat[0]) - 1):
i += 1
j = 0
else:
j += 1
move(mat, i, j)
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
move(mat)
Output::
1
0
0
3
0
0
0
2
3
0
2
0
0
2
0
0
1
2
3
3

as for the move function, simply change the 2nd if to elif
def move(mat, i=0, j=0):
print(mat[i][j])
if j<(len(mat[0])-1):
move(mat,i,j+1)
elif i<(len(mat)-1):
move(mat,i+1,0)

Related

if condition statment trigerred without condition being met for python

I'm solving a simple DSA problem and seem to grasp a general way to solve the question where Given an integer array called nums, I would move all 0's to the end of it while maintaining the relative order of the non-zero elements.
For example, nums = [0,1,0,3,12]
Then the expected output would be. [1,3,12,0,0]
My approach was as below.
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
count = 0
while count < len(nums):
if len(nums[count+1:]) == count:
return nums
elif nums[count] == 0:
nums.pop(nums[count])
nums.append(0)
else:
count += 1
print(nums)
I would expect this to work in my mind, but the output is not what I expected because stdout shows as
[1, 0, 3, 12, 0]
[1, 0, 3, 12, 0]
[0, 3, 12, 0, 0]
[0, 3, 12, 0, 0]
Now I understand why the firsto two prints show as such, because 0 was identifed in the first loop and popped
However I do not understand why in the third and forth iteration, the result shows as [0,3,12,0,0].
The if-conditiona says if nums[count]==0, then nums.pop[nums[count]].
How is the pop and append triggered when the condition is not met?
Am I missing something here?
I simplified in few lines:
def ceros_array(ar):
'''Given and array of integer return zeros at end of array'''
zeros = ar.count(0) # count zeros in array
new_array = [x for x in ar if x !=0] # new array with elements different that zero
for i in range(zeros): # cycle for zeros removed
new_array.append(0) # insert 0 at end of new array
return new_array
I tested with these arrays:
>>> ceros_array([1, 0, 3, 12, 0])
[1, 3, 12, 0, 0]
>>> ceros_array([0, 0, 3, 12, 0])
[3, 12, 0, 0, 0]
>>> ceros_array([0, 0, 3, 0, 0])
[3, 0, 0, 0, 0]
>>> ceros_array([0, 0, 0, 0, 1])
[1, 0, 0, 0, 0]
>>> ceros_array([0, 0, 0, 0, 0])
[0, 0, 0, 0, 0]
Inspired by dannisis's answer:
def shift1(nums: list[int]) -> list[int]:
"""Push all zeros in nums to the back (right) of the list."""
# Keep only non-zeroes
new_nums = [x for x in nums if x != 0]
# Append the correct number of zeroes
new_nums += [0] * nums.count(0)
return new_nums
for nums in [
[1, 0, 3, 12, 0],
[0, 0, 3, 12, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0],
]:
print(shift1(nums))
[1, 3, 12, 0, 0]
[3, 12, 0, 0, 0]
[3, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
Here's a more general, albeit less performant, solution:
def shift2(nums: list[int]):
_len = len(nums)
shift_ct = 0
for i in range(_len):
if nums[i] == 0:
shift_ct += 1
continue
else:
nums[i - shift_ct] = nums[i]
# "Back fill" end w/any zeroes
i = _len - shift_ct
while i < _len:
nums[i] = 0
i += 1
This mutates the list you pass in:
Nums = list[int] # input to function
Want = list[int] # what we expect in return
test_cases: list[tuple[Nums, Want]] = [
([0, 1], [1, 0]),
([0, 0, 1], [1, 0, 0]),
([0, 1, 2], [1, 2, 0]),
([0, 1, 0, 2], [1, 2, 0, 0]),
([0, 1, 0, 2, 0, 3], [1, 2, 3, 0, 0, 0]),
]
def test_shift2():
for nums, want in test_cases:
got = nums[:] # make copy to keep "input" separate from "result" in print-out, if test fails
shift2(got)
if got != want:
print(f"shift2({nums}) = {got}; want {want}")
I've run a number of timeit tests and profiled both, and I believe shift1 is slightly faster because even though it has two function calls (1 for the list comprehension, 1 for count()), it just has less lines to execute; shift2 only has the one function call (len()), but far more lines for the interpreter to step through.
You need to be very careful about changing structure of iterable objects WHILE iterating through them. Think about what you are doing. On your first loop, you hit 0 when i = 1, you pop that out and append it to the end. So now, all the elements from that point are shuffled up to an index that is one lower. The 2nd 0 was initially at index 2 but is shuffled up to index 1. You then ADD 1 to your index, meaning the next element is skipped and not processed.
Your best solution is to is to append non-zero elements to a second list and then fill with zeros:
def moveZeroes(nums) -> None:
new_list = []
zero_count = 0
for i in nums:
if i > 0:
new_list.append(i)
else:
zero_count += 1
new_list += [0] * zero_count
print(new_list)
moveZeroes([0, 1, 0, 3, 12])
I tested this and this works. (note that I used pop(count), not pop(nums[count]):
NOTE: After posting this answer, I noticed that it would fail if the initial list (my_numbers) starts with more than one zero. A quick and dirty fix for this would be to just call moveZeroes(my_numbers) twice.
def moveZeroes(nums):
count = 0
while count < len(nums):
if nums[count] == 0:
nums.pop(count)
nums.append(0)
count += 1
else:
count += 1
my_numbers = [0,1,0,2,12,0,4]
moveZeroes(my_numbers)
moveZeroes(my_numbers) # added as temporary fix for leading multiple zeroes
print(my_numbers) #prints [1,2,12,4,0,0,0]

Swapping elements with the same index between 2 or more lists

I would like to move to the left of the main list ( or top seen as 2d list ) all the 0s. My solution is very naive and it's swapping only one value and when it reaches the end it's bringing last value at the begining ( which I don't need ). I've tried with collections deque but with no good output. I was thinking to a while loop as well but I couldn't find a solution.
x = [[0,2,1], [4,2,0], [0,0,0], [3,0,0]]
0 2 1 0 0 0
Input -->> 4 2 0 Output -->> 0 0 0 OR x = [ [0,0,0], [0,0,0], [4,2,0], [3,2,1] ]
0 0 0 4 2 0
3 0 0 3 2 1
for i in range(len(x)):
for index, j in enumerate(x[i]):
if j == 0:
x[i][index] = x[i-1][index]
x[i-1][index] = 0
Let's start with the idea that you need to take a list or iterable and shuffle the zeros to the start -- this is needed for each column. Then you can do this is two transforms (each transposes the rows/columns) and a map via the shuffler:
x = zip(*map(shuffer, zip(*x)))
The shuffler can be implemented in any fashion, but I like generators; this specific implementation is two passes:
def shuffler(input):
for i in input:
if not i: yield 0
for i in input:
if i: yield i
Results seem correct to me:
>>> print zip(*map(shuffler, zip(*x)))
[(0, 0, 0), (0, 0, 0), (4, 2, 0), (3, 2, 1)]
Sorting columns based on being non-zero has the side effect of arranging the numbers in the desired order:
x = [[0, 2, 1], [4, 2, 0], [0, 0, 0], [3, 0, 0]]
print(list(map(list, zip(*(sorted(col, key=lambda n: n != 0) for col in zip(*x))))))
Output:
[[0, 0, 0], [0, 0, 0], [4, 2, 0], [3, 2, 1]]
>>>

Minimum swaping elements in a list to make it same like another list and count the swap in python

I have to list as my input,
a = [0,1,0,1] and
b = [1,0,1,0]
Note: The elements of both list will just 0 and 1.If it's not possible to make both of them same by swap then i will print -1.If it's same in beginning i will print 0 and if it's not same then,
i want to make a == b, In this case, i need 2 minimum swaps.
1st swap will be, 0th index of a and 1st index of a and
2nd swap will be, 2nd index of a and 3rd index of a.
After that a will same as b.
Here is my code:
def xx(a,b):
move = 0
if a == b:
print(0)
else:
if len(a) == 1 and len(a) == len(b):
print(-1)
else:
for i in range(len(a)):
if a[i] != b[i]:
j = a.index(b[i])
a[i] = a[j]
move += 1
count_swap = move // 2
print(count_swap)
a = list(map(int,input().split()))
b = list(map(int,input().split()))
xx(a,b)
Is there any efficient way to get swap count ?
Input:
0 1 0 1
1 0 1 0
Output:
2
Input:
0 1 0
1 0 0
Output:
1
Input:
0
1
Output:
-1
First, in order for swaps to make the lists equal, they must start with the same number of ones and zeros. So we can use a Counter to check for impossibility.
And second, a single swap necessarily fixes two differences. So we can just count the differences and divide by 2. We don't actually have to perform any of the swaps.
A demonstration:
from collections import Counter
def swap_count(xs, ys):
if xs == ys:
return 0
else:
cx = Counter(xs)
cy = Counter(ys)
if cx == cy:
n_diffs = sum(x != y for x, y in zip(xs, ys))
return n_diffs // 2
else:
return -1
def main():
tests = [
(2, [0, 1, 0, 1], [1, 0, 1, 0]),
(1, [0, 1, 0], [1, 0, 0]),
(-1, [0], [1]),
(0, [0, 1, 0, 1], [0, 1, 0, 1]),
]
for exp, xs, ys in tests:
n = swap_count(xs, ys)
print(n == exp, n, xs, ys)
main()
Output:
True 2 [0, 1, 0, 1] [1, 0, 1, 0]
True 1 [0, 1, 0] [1, 0, 0]
True -1 [0] [1]
True 0 [0, 1, 0, 1] [0, 1, 0, 1]
This should be an O(N) solution that iterates over the items and performs the swaps on list b. If we walk off the end of list b (IndexError) a solution can't be found and return -1.
def count_swaps(a, b):
swaps = 0
for idx in range(len(a)):
if a[idx] != b[idx]:
try:
b[idx], b[idx + 1] = b[idx + 1], b[idx]
swaps += 1
except IndexError:
return -1
return swaps
assert count_swaps([0, 1, 0, 1], [1, 0, 1, 0]) == 2
assert count_swaps([0, 1, 0], [1, 0, 0]) == 1
assert count_swaps([0], [1]) == -1

How to remove mirrors reflections values of itertools.product function?

I create a cartesian product using the itertools.product function:
from itertools import product
a = list(map(list, itertools.product(list(range(2)), repeat=3)))
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Then I get rid of mirrors reflections in the following way:
b = []
for k, v in enumerate(a):
if v[::-1] not in a[:k]:
b.append(v[::-1])
Output:
[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]]
But can I get the same effect step by step without saving all the results of itertools.product in the list? For example, with the usual approach on the for loop:
for i in list(map(list, itertools.product(list(range(2)), repeat=3))):
# blah blah blah
Because ultimately I will use large cartesian products, at least repeat = 18. And that is why I have to give up the approach on the lists. Unless there is another way to do it? I will be grateful for any tips.
import itertools
l = (list(i) for i in itertools.product(tuple(range(2)), repeat=3) if tuple(reversed(i)) >= tuple(i))
print list(l)
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 1], [1, 1, 1]]
Here is an idea for a recursive algorithm to generate only the necessary combinations (as opposed to generate the whole Cartesian product and discarding the unnecessary ones):
def noReflections(n, k, current=None, idx=0, symmetric=True):
# n: number of distinct elements
# k: sequences length
# current: sequence being generated
# idx: current generated index
# symmetric: true if the chosen elements up to now are symmetric
assert n >= 0 and k >= 0
if n == 0 or k == 0:
return
if idx == 0:
current = k * [0]
if idx < k // 2:
# Choose the value for current position (idx) and symmetric (idx2)
idx2 = k - idx - 1
for i in range(n):
# Value for current position
current[idx] = i
# If all previously selected values were symmetric,
# the symmetric position must have a value equal or greater
# than the current; otherwise it can take any value.
first = i if symmetric else 0
for j in range(first, n):
# Value for symmetric position
current[idx2] = j
# Recursive call
# Only keep symmetric flag if previously selected values
# and the ones selected now are symmetric.
yield from noReflections(n, k, current, idx + 1, symmetric and (i == j))
elif idx == k // 2 and (k % 2 == 1):
# In middle position of odd-length sequence
# Make one sequence with each possible value
for i in range(n):
current[idx] = i
yield tuple(current)
else:
# Even-length sequence completed
yield tuple(current)
print(list(noReflections(2, 3)))
>>> [(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]
I'm not sure this should perform better than the other answer though, because of the recursion and so on (in a couple of quick tests bot performed similarly in my machine).

Vectorizing an indexing operation in numpy

What is the best way to vectorize the following code in numpy?
from numpy import *
A = zeros(5, dtype='int')
I = [1, 1, 1, 3]
J = [2, 1, 1, 1]
for i, j in zip(I, J):
A[i] += j
print A
The result should be:
[0 4 0 1 0]
Here A is the original array, I stores the index at which we want to increment by the corresponding entry of J.
If one simply vectorizes the above by doing:
A[I] += J
print A
one gets the wrong answer
[0 1 0 1 0]
as, apparently, repeated indices are ignored. Is there an equivalent operation to += which does not ignore repeated indices?
You can use numpy.bincount():
A = numpy.zeros(5, dtype='int')
I = [1, 1, 1, 3]
J = [2, 1, 1, 1]
sums = numpy.bincount(I, J)
A[:len(sums)] += sums
print(A)
prints
[0 4 0 1 0]
In principle you can do it with numpy's bincount and unique, but I'd guess it'll only make the code much less readable without any sensible performance improvement.

Categories

Resources