Shift and merge elements of a matrix - python

How can I shift and merge elements of a matrix to have the following result ?
Move right:
[[0,0,2,2,0,2],[8,4,2,2,0,2]] ==> [[0,0,0,0,4,2],[0,0,8,4,4,2]]
or
Move left:
[[0,0,2,2,0,2],[8,4,2,2,0,2]] ==> [[4,2,0,0,0,0],[8,4,4,2,0,0]]
It's like the 2048 game. For example, when the user do a left move, every numbers go to the left of the list and if 2 numbers side-by-side are equals, tere is an addition of the two numbers.
I would like to do it with loops.
I tried with some codes that I have found on the internet but as a begineer, I didn't found a straightforward code to understand how to do this.
Thanks in advance for the help.

If I don't misunderstand your meaning,I write some code,hope this helps:
a = [[0, 0, 2, 2, 0, 2], [8, 4, 2, 2, 0, 2]]
f = lambda x: [2 * x[0]] if x[0] == x[1] else x
def move_left(l):
c, l = [], l + [0] if len(l) % 2 else l
for i in range(0, len(l), 2):
c = c + f(l[i:i + 2])
c = list(filter(lambda x: x != 0, c))
return c + ([0] * (len(l) - len(c)))
def move_right(l):
c, l = [], l + [0] if len(l) % 2 else l
for i in range(len(l), 0, -2):
c = f(l[i - 2:i]) + c
c = list(filter(lambda x: x != 0, c))
return ([0] * (len(l) - len(c))) + c
for i in a:
print(move_left(i))
Output:
[4, 2, 0, 0, 0, 0]
[8, 4, 4, 2, 0, 0]
It seems that you're using Python3.x,so you should use list(filter(lambda x: x != 0, c)) to get the list.

Here is an example for moving the elements to the right:
def move_right(matrix):
for row in matrix:
for i, number in reversed(list(enumerate(row))):
if number == row[i-1]:
row[i] = number + row[i-1]
row[i-1] = 0
row.sort(key=lambda v: v != 0)
return matrix
Then doing:
matrix = [[0,0,2,2,0,2],[8,4,2,2,0,2]]
print(move_right(matrix))
Outputs:
[[0, 0, 0, 0, 4, 2], [0, 0, 8, 4, 4, 2]]
How it works:
First we loop through the matrix. During the first iteration: row = [0, 0, 2, 2, 0, 2]
Then we check loop over the numbers in that row using the enumerate() function. We then apply reversed() to go traverse the list backwards as to not combine a previous sum with another element in a single turn.
e.g: [4,4,8] => [0, 8, 8] => [0, 0, 16]. This should actually equal [0, 0, 8]
For the current row, this will output a result as so:
i number
5 2
4 0
3 2
2 2
1 0
0 0
Then we use the index (i) to refer to the previous number in the list. e.g. At index 2. The current number is 2, and the previous number (i-1) was 2. Since these are equal the code within the 'if' statement will execute.
The current element will then be assigned to the sum of itself and the previous element. row[i] = number + row[i-1]
The previous number will become 0, at it merged with the current number. row[i-1] = 0
Once we have looped over every number, the row will be: [0, 0, 0, 4, 0, 2]. The code row.sort(key=lambda v: v != 0) will then sort so that the zeroes are pushed to the left. See here for more details.
When moving elements to the left instead of the right, this would need to be changed to row.sort(key=lambda v: v != 0, reverse=True) to push the zeros in the other direction.

Related

recursive calling to print list of numbers

I'm trying to sort an array and print the output recursively until the array is = [0,0,0,0,0] but it only prints [3,2,1,0,0] ,,,,, this is what i wrote can you help to fix this isuee ,, still learning
the answer should be
[4 3 2 1 0 0]
[3 2 1 0 0 0]
[2 1 0 0 0 0]
[1 0 0 0 0 0]
[0 0 0 0 0 0]
numbers=[5,4,3,2,1,1]
numbers.sort()
numbers.sort(reverse=True)
print('List sorted: ', numbers)
def list_arrays(numb):
level=numbers[0]
if len(numbers)-1 < level:
return 0
else:
numbers.pop(0);
print(numbers)
for i in range(len(numbers)):
numbers[i] -= 1
print(numbers)
#list_arrays(numbers)
if __name__=='__main__':
list_arrays(numbers)
You have a number of problems here. First, you defined a function, but you never called the function. That's why nothing printed.
Second, you don't need "sort" immediately followed by "sort(reverse)".
Third, I don't know what you were attempting by checking the level. That doesn't seem to be related to your problem.
Fourth, you're subtracting 1 from every number, but you should only be subtracting 1 if the number is greater than 0.
Finally, you're only subtracting once. You need to repeat it until all the numbers are zero.
numbers=[5,4,3,2,1,1]
numbers.sort(reverse=True)
print('List sorted: ', numbers)
def list_arrays(numb):
while any(numb):
for i in range(len(numb)):
if numb[i]:
numb[i] -= 1
print(numb)
list_arrays(numbers)
In most cases, rather than modify the list in place, I would suggest creating a new list during each loop, but for this simple assignment, this will work.
To remove the zeros, you really do want to create a new list, not modify in place.
numbers=[5,4,3,2,1,1]
numbers.sort(reverse=True)
print('List sorted: ', numbers)
def list_arrays(numb):
while numb:
new = []
for v in numb:
if v > 1:
new.append(v-1)
numb = new
print(numb)
list_arrays(numbers)
Or even:
numbers=[5,4,3,2,1,1]
numbers.sort(reverse=True)
print('List sorted: ', numbers)
def list_arrays(numb):
while numb:
numb = [v-1 for v in numb if v>1]
print(numb)
list_arrays(numbers)
In another comment you asked to remove all zeroes. Perhaps this is the solution you are looking for -
def run(t):
while(t):
yield t
t = [x - 1 for x in t if x > 1]
mylist = [5,4,3,2,1,1]
for t in run(mylist):
print(t)
[5, 4, 3, 2, 1, 1]
[4, 3, 2, 1]
[3, 2, 1]
[2, 1]
[1]
Or gather all lines in a single array using list -
mylist = [5,4,3,2,1,1]
print(list(run(mylist)))
[
[5, 4, 3, 2, 1, 1],
[4, 3, 2, 1],
[3, 2, 1],
[2, 1],
[1]
]
Maybe this is what you want?
def make_all_zero_and_print(numbers):
while numbers[0] > 0:
print(numbers)
for i in range(len(numbers)):
if numbers[i] > 0:
numbers[i] -= 1
print(numbers)
make_all_zero_and_print([5, 4, 3, 2, 1, 1])
Output:
[5, 4, 3, 2, 1, 1]
[4, 3, 2, 1, 0, 0]
[3, 2, 1, 0, 0, 0]
[2, 1, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]

I need the sum of numbers from a for-loop that is NOT in a list in Python

I'm having a problem returning the sum. I keep getting zero.
I commented the print results for better understanding. Again, my code is returning 0, and not 11.
A = [1, 5, 2, 1, 4, 0]
def solution(A):
start = [i - j for i, j in enumerate(A)]
start.sort() #[-4, -1, 0, 0, 2, 5]
for i in range(0, len(A)):
pair = 0
end = i + A[i] #1, 6, 4, 4, 8, 5
count = bisect_right(start, end) #4, 6, 5, 5, 6, 6
count_1 = count-1 #3, 5, 4, 4, 5, 5
count_2 = count_1 - i #3, 4, 2, 1, 1, 0
pair += count_2 #??????? I need the above added together like this 3+4+2+1+1+0 since
that's the answer.
return pair
print(solution(A))
As you've written in the comments, the last count2 is zero.
You're adding zero to zero, the loop ends, and you return zero.
You need to start the counter outside the loop, or you can sum outside too, like so
counts = []
for i, x in enumerate(A):
counts.append(bisect_right(start, i + x) - 1 - i)
return sum(counts)
Which could be rewritten
return sum(bisect_right(start, i + x) - 1 - i for for i, x in enumerate(A))

Combining array elements in a particular way, and recording it

I am given a 1D array of numbers.
I need to go through the array adding each consecutive element to form a sum. Once this sum reaches a certain value, it forms the first element of a new array. The sum is then reset and the process repeats, thus iterating over the whole array.
For example if given:
[1, 3, 4, 5, 2, 5, 3]
and requiring the minimum sum to be 5,
the new array would be:
[8, 5, 7]
Explicity: [1 + 3 + 4, 5, 2 + 5]
I then also need to keep a record of the way the elements were combined for that particular array: I need to be to take a different array of the same length and combine the elements in the same way as above.
e.g. give the array
[1, 2, 1, 1, 3, 2, 1]
I require the output
[4, 1, 5]
Explicity: [1 + 2 + 1, 1, 3 + 2]
I have accomplished this with i loops and increment counters, but it is very ugly. The array named "record" contains the number of old elements summed to make each element of the new array i.e. [3, 1, 2]
import numpy as np
def bin(array, min_sum):
num_points = len(array)
# Create empty output.
output = list()
record = list()
i = 0
while i < num_points:
sum = 0
j = 0
while sum < min_sum:
# Break out if it reaches end of data whilst in loop.
if i+j == num_points:
break
sum += array[i+j]
j += 1
output.append(sum)
record.append(j)
i += j
# The final data point does not reach the min sum.
del output[-1]
return output
if __name__ == "__main__":
array = [1, 3, 4, 5, 2, 5, 3]
print bin(array, 5)
I would advice you to simply walk through the list. Add it to an accumulator like the_sum (do not use sum, since it is a builtin), and in case the_sum reaches a number higher than the min_sum, you add it, and reset the_sum to zero. Like:
def bin(array, min_sum):
result = []
the_sum = 0
for elem in array:
the_sum += elem
if the_sum >= min_sum:
result.append(the_sum)
the_sum = 0
return result
The lines where the accumulator is involved, are put in boldface.
I leave combining the other array the same way as an exercise, but as a hint: use an additional accumulator and zip to iterate over both arrays concurrently.
Here is a straightforward solution. which computes a list of boolean values where the value is true when accumulated element equals or exceeds the target value and calc computes an accumulation using this list.
def which(l, s):
w, a = [], 0
for e in l:
a += e
c = (a >= s)
w.append(c)
if c:
a = 0
return w
def calc(l, w):
a = 0
for (e, c) in zip(l, w):
a += e
if c:
yield a
a = 0
here is an interactive demonstration
>>> l1 = [1, 3, 4, 5, 2, 5, 3]
>>> w = which(l1, 5)
>>> w
[False, False, True, True, False, True, False]
>>> list(calc(l1, w))
[8, 5, 7]
>>> l2 = [1, 2, 1, 1, 3, 2, 1]
>>> list(calc(l2, w))
[4, 1, 5]
You can use short solutions I found out after a long struggle with flattening arrays.
For getting bounded sums use:
f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])<x]
This outputs:
>>> f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])< x]
>>> a= [1, 3, 4, 5, 2, 5, 3]
>>> f(a,5,0,7)
[1, 3, 4]
>>> sum(f(a,5,0,7))
8
>>> sum(f(a,5,3,7))
5
>>> sum(f(a,5,4,7))
7
>>>
To get your records use the function:
>>> y = lambda a,x,f,j,l: [] if j>=l else list(np.append(j,np.array(y(a,x,f,j+len(f(a,x,j,l)),l))))
From here, you can get both array of records and sums:
>>> listt=y(a,5,f,0,len(a))
>>> listt
[0.0, 3.0, 4.0, 6.0]
>>> [sum(f(a,5,int(listt[u]),len(a))) for u in range(0,len(listt)-1)]
[8, 5, 7]
>>>
Now, the bit of magic you can even use it as an index-conditional boundary for the second vector:
>>> b=[1, 2, 1, 1, 3, 2, 1]
>>> [sum(f(b,5,int(listt[u]),int(listt[u+1]))) for u in range(0,len(listt)-1)]
[4, 1, 5]
>>>

Rotating a list without using collection.deque

I want to make a script. The program should get some list L with values, and natural number N.
If N>0, the list's objects move N steps to left.
If N<0, the list's objects move abs(N) to the right.
If N=0 the list remain the same...
For example: For N=1 and L=[1,2,3,4,5], the output is [2,3,4,5,1].
For same list and N=-1 the output is [5,1,2,3,4]
I actually did it using collection.deque, but I want to do this with nothing but lists, for loop and 'if'.
I have problem to understand how to make the objects move.
l = input("enter list:")
N = input("enter number of rotations:")
import collections
d = collections.deque(l)
d.rotate(-N)
print d
You can use list slicing:
def rotate(L, N):
if not L or N % len(L) == 0:
return L
return L[N % len(L):] + L[:N % len(L)]
L = [1, 2, 3, 4, 5]
for N in range(-3, 4):
print(rotate(L, N))
Output:
[3, 4, 5, 1, 2]
[4, 5, 1, 2, 3]
[5, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 1]
[3, 4, 5, 1, 2]
[4, 5, 1, 2, 3]
Note that if you use a list, the time complexity of a rotation is linear to the number of elements in the list in general, since you have to move all existing elements. deque.rotate(), on the other hand, is O(k), with k being the number of steps. Therefore, if you need to rotate more than once deque.rotate() is the way to go.
This works:
result = l[N % len(l):]+l[:N % len(l)]
If using numpy is an option for you, there is a method called roll, which you might want to have a look at:
import numpy as np
array = np.arange(1, 6) # 1 to 5
print array
print np.roll(array, 0)
print np.roll(array, 2)
print np.roll(array, -2)
print np.roll(array, 17)
The result is as expected:
[1 2 3 4 5]
[1 2 3 4 5]
[4 5 1 2 3]
[3 4 5 1 2]
[4 5 1 2 3]
If you're after simple lists here's how you can do it in Python 3 (based on your P2 example).
L = list(map(int, input("Enter numbers: ").split(" ")))
N = int(input("Enter how many rotations to perform: "))
print(L[N:] + L[:N])
If you don't want to create new list, you can try in-place reverse,
def reverse(nums, start, end):
i = start
j = end - 1
while i < j:
tmp = nums[i]
nums[i] = nums[j]
nums[j] = tmp
i += 1
j -= 1
def rotate(nums, n):
if n == 0:
return nums
length = len(nums)
if n < 0:
n = length + n
reverse(nums, 0, n)
reverse(nums, n, length)
reverse(nums, 0, length)
return nums
>>> rotate([1, 2, 3, 4, 5], 1)
[2, 3, 4, 5, 1]
>>> rotate([1, 2, 3, 4, 5], -1)
[5, 1, 2, 3, 4]
thank you all ! :)
your answers are great and very useful for me.
here is my solution for this question (given that I do these scripts for a basic python course for biologists):
L = input('Enter list of numbers, please: ')
N = input('Enter how many places to rotate: ')
L1 = L[N:]
L2 = L[:N]
L = L1 + L2
print L

Python: Index in a list of lists

My list looks like this:
G = [ [0,3,4],[1,0,0],[9,5,4],[4,3,2],[3,2,3],[0,1,4],[1,0,0],[1,0,0],[1,0,0],[1,0,0] ]
and I want to find the index, from which only [1, 0, 0] follows consecutively. In this case [1,0,0] occurs 4 times consecutively, i.e if [1,0,0] occurs 4 times, then the output should be 7. The output of my expression is wrong!
The output of
index =[i for i, j in enumerate(G) if j == [1,0,0]]
is
index = [1, 6, 7, 8, 9]
How can i solve this problem?
Edited
Very important was in this question to find the index of the pattern [1,0,0], if after this no another pattern occurs. In list G is the index 6 not 7!
Given the application, it's probably best to iterate through the list backwards using reversed and find the first non-target element.
I'm also going to use enumerate to iterate through the elements and indices of the reversed list at the same time. The first time we see a non-target element, we can break out of the loop. At this point, subtracting the index counter from the length of the list will give us the index of the last non-target element.
for ind, el in enumerate(reversed(G)):
if el != [1, 0, 0]:
break
else: # This block is only run when no breaks are encountered in the for loop
ind = ind + 1 # Adjust for when the entire list is the target
result = len(G) - ind
# 6
Note that Python indexing starts at 0, so 6 is actually the correct answer here, not 7. If you need the 1-indexed version, you can simply add 1.
>>> G = [ [0,3,4],[1,0,0],[9,5,4],[4,3,2],[3,2,3],[0,1,4],
... [1,0,0],[1,0,0],[1,0,0],[1,0,0] ]
>>> next(i for i, (x,y) in enumerate(zip(G, G[1:])) if x == y)
6
(Use .. if x == y == [1, 0, 0] if you want to only check for [1, 0, 0])
This will raise a StopIteration if there's no consecutive items.
>>> G = [ [1,2,3] ]
>>> next(i for i, (x,y) in enumerate(zip(G, G[1:])) if x == y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
To prevent that, pass default value:
>>> next((i for i, (x,y) in enumerate(zip(G, G[1:])) if x == y), -1)
-1
UPDATE according to the OP's comment:
import itertools
def find_target(lst, target):
target = tuple(target)
i, maxidx, maxcnt = 0, -1, 0
for key, grp in itertools.groupby(lst, key=tuple):
cnt = sum(1 for _ in grp)
if key == target and cnt > maxcnt:
maxidx, maxcnt = i, cnt
i += cnt
return maxidx
Usage:
>>> G = [[0,3,4], [1,0,0], [9,5,4], [1,0,0], [1,0,0],
... [1,0,0], [1,0,0], [23,4,1], [1,0,0], [1,0,0],
... [1,0,0], [1,0,0],[1,0,0], [1,0,0], [1,0,0],
... [1,0,0]]
>>>
>>> find_target(G, [1, 0, 0])
8
>>> find_target([], [1, 0, 0])
-1
>>> find_target([[1, 0, 0]], [1, 0, 0])
0
>>> find_target([[2, 3, 4], [1, 0, 0], [1, 0, 0]], [1, 0, 0])
1
Something like this?
for ii in range(len(G)-1, 0, -1):
if G[ii-1] != [1,0,0]:
break
And the resulting ii is the desired index
If you mean the last element, and not expecifically [1,0,0] obviously you should replace [1,0,0] with G[-1], that would work for:
G = [ [2,3,4], [1,3,4], [1,3,4]]
giving 1 as an answer

Categories

Resources