Algorithm Lists with python - python

I am trying to find an algorithm to solve the following problem with python.
Given a list of integers, check if after dropping two elements of the list, is it possible to divide the list in 3 where the sum of the consecutive numbers are equals.
For example:
A = [1, 3, 4, 2, 2, 2, 1, 1, 2] should return TRUE because we would drop the elements in bold the list can be slit in [1, 3], [2, 2], [2, 1, 1] where all sum 4
B = [1, 1, 1, 1, 1, 1] should return FALSE
C = [1, 1, 3, 1, 1, 1034, 5, 9900, 1, 2] should also return FALSE because eventhough after droping the numbers in bold you can sum have the numbers to sum 5 (1,1,3), (5), (1, 1, 1, 2) the list should be sorted first and that's not allowed.
I have come with a solution that seems to work but it is very, very bad and not sure if always work, and the complexity is too high when should be O(n)
I don't know how to iterate removing 2 numbers from a list without having a complexity of O(n^2)
Thanks

It is indeed possible to solve this problem in O(n) (given that the integers are positive) where n is the size of your array A.
It can be surprising since the problem reminds of the bin packing, or subset sum problems, but the fact that we look for consecutive sum in the array makes the task much easier.
Here is a python code doing it:
def find_couple_list_sum(int_list):
n = len(int_list)
left_sum = [0 for _ in range(n)]
for i in range(1, n):
left_sum[i] = left_sum[i-1] + int_list[i-1]
right_sum = [0 for _ in range(n)]
for j in range(n-2, -1, -1):
right_sum[j] = right_sum[j+1] + int_list[j+1]
total_sum = sum(int_list)
print(left_sum)
print(right_sum)
print(total_sum)
i, j = 0, n-1
while True:
print(i, j)
mid_sum = total_sum - left_sum[i] - right_sum[j] - int_list[i] - int_list[j]
if left_sum[i] == right_sum[j] and left_sum[i] == mid_sum:
return i, j
elif i == j:
return None, None
elif left_sum[i] < right_sum[j]:
i += 1
else:
j -= 1
int_list = [1, 3, 4, 2, 2, 2, 1, 1, 2]
print(find_couple_list_sum(int_list))
The whole thing runs indeed in O(n).

Related

Cut a sequence of length N into subsequences such that the sum of each subarray is less than M and the cut minimizes the sum of max of each part

Given an integer array sequence a_n of length N, cut the sequence into several parts such that every one of which is a consequtive subsequence of the original sequence.
Every part must satisfy the following:
The sum of each part is not greater than a given integer M
Find a cut that minimizes the sum of the maximum integer of each part
For example:
input : n = 8, m = 17 arr = [2, 2, 2, 8, 1, 8, 2, 1]
output = 12
explanation: subarrays = [2, 2, 2], [8, 1, 8], [2, 1]
sum = 2 + 8 + 2 = 12
0 <= N <= 100000
each integer is between 0 and 1000000
If no such cut exists, return -1
I believe this is a dynamic programming question, but I am not sure how to approach this.
I am relatively new to coding, and came across this question in an interview which I could not do. I would like to know how to solve it for future reference.
Heres what I tried:
n = 8
m = 17
arr = [2, 2, 2, 8, 1, 8, 2, 1]
biggest_sum, i = 0, 0
while (i < len(arr)):
seq_sum = 0
biggest_in_seq = -1
while (seq_sum <= m and i < len(arr)):
if (seq_sum + arr[i] <= m ):
seq_sum += arr[i]
if (arr[i] > biggest_in_seq):
biggest_in_seq = arr[i]
i += 1
else:
break
biggest_sum += biggest_in_seq
if (biggest_sum == 0):
print(-1)
else:
print(biggest_sum)
This givens the result 16, and the subsequences are: [[2, 2, 2, 8, 1], [8, 2, 1]]
Problem is that you are filling every sequence from left to right up to the maximum allowed value m. You should evaluate different options of sequence lengths and minimize the result, which in the example means that the 2 8 values must be in the same sequence.
a possible solution could be:
n = 8
m = 17
arr = [2, 2, 2, 8, 1, 8, 2, 1]
def find_solution(arr, m, n):
if max(arr)>m:
return -1
optimal_seq_length = [0] * n
optimal_max_sum = [0] * n
for seq_start in reversed(range(n)):
seq_len = 0
seq_sum = 0
seq_max = 0
while True:
seq_len += 1
seq_end = seq_start + seq_len
if seq_end > n:
break
last_value_in_seq = arr[seq_end - 1]
seq_sum += last_value_in_seq
if seq_sum > m:
break
seq_max = max(seq_max, last_value_in_seq)
max_sum_from_next_seq_on = 0 if seq_end >= n else optimal_max_sum[seq_end]
max_sum = max_sum_from_next_seq_on + seq_max
if seq_len == 1 or max_sum < optimal_max_sum[seq_start]:
optimal_max_sum[seq_start] = max_sum
optimal_seq_length[seq_start] = seq_len
# create solution list of lists
solution = []
seg_start = 0
while seg_start < n:
seg_length = optimal_seq_length[seg_start]
solution.append(arr[seg_start:seg_start+seg_length])
seg_start += seg_length
return solution
print(find_solution(arr, m, n))
# [[2, 2, 2], [8, 1, 8], [2, 1]]
Key aspects of my proposal:
start from a small array (only last element), and make the problem array grow to the front:
[1]
[2, 1]
[8, 2, 1]
etc.
for each of above problem arrays, store:
the optimal sum of the maximum of each sequence (optimal_max_sum), which is the value to be minimized
the sequence length of the first sequence (optimal_seq_length) to achieve this optimal value
do this by: for each allowed sequence length starting at the beginning of the problem array:
calculate the new max_sum value and add it to previously calculated optimal_max_sum for the part after this sequence
keep the smallest max_sum, store it in optimal_max_sum and the associated seq_length in optimal_seq_length

Row reducing a matrix (not using sympy.rref)

I have a matrix, G (size PxN), and I would like to make the first N/2 columns of G form the N/2×N/2 identity matrix by row operations. Any idea how I could write code for this? (not using sympy.rref).
Note P is a prime number slightly larger than N.
The matrix itself is a vandermonde matrix over the field of P i.e. all values are modulo P.
def ReedSolomon(k,p):
rr = np.arange(k).reshape((-1, 1))
cc = np.arange(p)
return cc**rr % p
An example would be:
ReedSolomon(4,5)
= [[1, 1, 1, 1, 1],
[0, 1, 2, 3, 4],
[0, 1, 4, 4, 1],
[0, 1, 3, 2, 4]]
And i would like to produce the following:
= [[1, 0, -1, -2, -3],
[0, 1, 2, 3, 4],
[0, 1, 4, 4, 1],
[0, 1, 3, 2, 4]]
In this case the N/2 x N/2 submatrix is the identity.
Using sympy.rref would lead to some rows being swapped around and for more rows to be reduced. I would like to stop once the N/2 columns have been turned into the identity. Hence writing custom code is preferred.
Thanks!
The following code works (adapted from: python built-in function to do matrix reduction)
def rref(V, tol = 1.0e-12):
m, n = V.shape
i, j = 0, 0
jb = []
while i < (n//2) and j < (n//2):
# Find value and index of largest element in the remainder of column j
k = np.argmax(np.abs(V[i:n, j])) + i
p = np.abs(V[k, j])
if p <= tol:
# The column is negligible, zero it out
V[i:n//2, j] = 0.0
j += 1
else:
# Remember the column index
jb.append(j)
if i != k:
# Do not swap the i-th and k-th rows
pass
# Divide the pivot row i by the pivot element A[i, j]
V[i, j:n] = V[i, j:n] / V[i, j]
# Subtract multiples of the pivot row from all the other rows
for k in range(n//2):
if k != i:
V[k, j:n] -= V[k, j] * V[i, j:n]
i += 1
j += 1
return V
The adaptations include removing any row switching capability and limiting to the first n//2 columns and rows.
If anybody knows a more efficient way please let me know. Thanks

How to reduce higher values to 1s and sort the ones from right to left

I want to make a python program that quickly reduces a number greater than 1 in an array/list and places it in an empty spot before it. Say we have:
li = [4,1,0,0,0,1,3,0]
we'd get:
rtr = [1,1,0,1,1,1,1,0]
Note how the 4 turns into a 1 because it's already to the left and then the 3 gets divided into 2 positions before the 1 that has already been taken. Can anyone help me with this problem?
You can iterate the list from end to start and keep track of the sum you collect from the values. When you have a non zero sum, take 1 from it to populate the result list, and otherwise put a 0 in the result list.
Here is how that could work:
def spread(lst):
carry = 0
res = []
for i in reversed(lst):
carry += i
res.append(int(carry > 0))
if carry:
carry -= 1
return list(reversed(res))
lst = [4, 1, 0, 0, 0, 1, 3, 0]
print(spread(lst)) # [1, 1, 0, 1, 1, 1, 1, 0]
Using numpy
def fun(l):
s = np.array(l[::-1])
for i in range(len(s)):
if s[i] != 1 and s[i] != 0:
x = s[i+1:]
x[(x == 0).nonzero()[0][:s[i]-1]] = 1
s[i] = 1
return s[::-1].tolist()
print (fun([4,1,0,0,0,1,3,0]))
print (fun([0, 10]))
Output:
[1, 1, 0, 1, 1, 1, 1, 0]
[1, 1]

Sum of specific elements in a list, if they are consecutive (python)

What question is asking for is, from a list of lists like the following, to return a tuple that contains tuples of all occurrences of the number 2 for a given index on the list. If there are X consecutive 2s, then it should appear only one element in the inside tuple containing X, just like this:
[[1, 2, 2, 1],
[2, 1, 1, 2],
[1, 1, 2, 2]]
Gives
((1,), (1,), (1, 1),(2,))
While
[[2, 2, 2, 2],
[2, 1, 2, 2],
[2, 2, 1, 2]]
Gives
((3,),(1, 1),(2,)(3,))
What about the same thing but not for the columns, this time, for the rows? is there a "one-line" method to do it? I mean:
[[1, 2, 2, 1],
[2, 1, 1, 2],
[1, 1, 2, 2]]
Gives
((2,), (1, 1), (2,))
While
[[2, 2, 2, 2],
[2, 1, 2, 2],
[2, 2, 1, 2]]
Gives
((4,),(1, 2),(2, 1))
I have tried some things, this is one of the things, I can't finish it, don't know what to do anymore, after it:
l = [[2,2,2],[2,2,2],[2,2,2]]
t = (((1,1),(2,),(2,)),((2,),(2,),(1,1)))
if [x.count(0) for x in l] == [0 for x in l]:
espf = []*len(l)
espf2 = []
espf_atual = 0
contador = 0
for x in l:
for c in x:
celula = x[c]
if celula == 2:
espf_atual += 1
else:
if celula == 1:
espf[contador] = [espf_atual]
contador += 1
espf_atual = 0
espf2 += [espf_atual]
espf_atual = 0
print(tuple(espf2))
output
(3, 3, 3)
this output is the correct one but if I change the list(l) it doesn't work
So, you have som emistakes in the code.
Indexing:
for c in x:
celula = x[c]
It should be celula = c as c already points to each element of x.
Intermediate results
For each column you store intermediate results as:
espf_atual = 0
...
espf_atual += 1
...
espf2 += [espf_atual]
but this will only allow to store the last occurrences of 2 for each column. This is, if a row is [2,1,2,2], then espf_actual = 2 and you will store only the last occurrence. You will override the first occurrence (before the 1).
To avoid this, you need to store intermediate results for each row. You got it halfway with espf = []*len(l), but you never used it properly later.
Find bellow a working example (not too different from your initial solution):
espf = []
for x in l:
# Restart counters for every row
espf_current = [] # Will store any sequences of 2
contador = 0 # Will count consecutive 2's
for c in x:
celula = c
if celula == 2:
contador += 1 # Count number of 2
elif celula == 1:
if contador > 0: # Store any 2 before 1
espf_current += [contador]
contador = 0
if contador > 0: # Check if the row ends in 2
espf_current += [contador]
# Store results of this row in the final results
espf += [tuple(espf_current)]
print tuple(espf)
The key to switch rows and columns, is to change the indexing method. Currently you are iterating along the elements of the list, and thus, this doesn't allow you to switch between rows and columns.
Another way to see the iteration is to iterate indexes of the matrix (i, j for rows and columns) as follows:
numRows = len(l)
numCols = len(l[0])
for i in range(numRows):
for j in range(numCols):
celula = l[i][j]
The above is the indexing equivalent to the previous code. It assumes all the rows have the same length (which is true in your examples). Changing it from rows to columns is straightforward (tip: switch the loops), I leave it to you :P

merge values in list

I have a list in which I have to merge values like this:
list: [1, 1, 2, 1, 1, 0]
result: [2, 2, 2, 0, 0, 0]
It merges from left to right, and only merges 2 of the same numbers per time.
the first two values are added up (1 + 1)
the 2 is shifted to the left
the other two values are added up as well (1 + 1) and shifted
the list is be padded from the right with zero's so the length of the list stays the same
(edit) any zero's in the list will be at the end, not in the middle
I'm quite new to Python, and don't have a clear idea on how to approach this. Any help is appreciated.
How about something simple and straight-forward:
def merge_pairs(a):
out = []
i = 0
while i < len(a):
if i < len(a) - 1 and a[i] == a[i + 1]:
out.append(a[i] + a[i + 1])
i += 2
else:
out.append(a[i])
i += 1
out += [0] * (len(a) - len(out))
return out
Not 100% sure this is what you require, but perhaps it can be a start.
The single test you provided seems to work:
>>> merge_pairs([1, 1, 2, 1, 1, 0])
[2, 2, 2, 0, 0, 0]

Categories