Row reducing a matrix (not using sympy.rref) - python

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

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

Assigned 20 people to 4 tables four times without repetition and each person must be at each table only one time

I want to randomly assign 20 people to 4 tables to do 4 tasks without repetition, and each person must be at each table only once.
There are five people at each table.
There are four tables.
Five people must evenly rotate each table.
import random
# generate 1 to 20
members_list = [x for x in range(1, 21)]
# assign to 4 groups
chunks = [members_list[x:x + 5] for x in range(0, len(members_list), 5)]
print(chunks)
final_result = []
count = 0
start_assign = 4
# generate a new random list
while start_assign:
new_member_list = [x for x in range(1, 21)]
random.shuffle(new_member_list)
print(f"random List {start_assign}: {new_member_list} \n")
for i in chunks:
result = []
count += 1
print(f"The Original List {count}: {i}")
for x in new_member_list:
if x not in i:
result.append(x)
result = result[:5]
new_member_list = [x for x in new_member_list if x not in result]
result.sort()
if len(result) < 5:
result.extend(new_member_list)
print(f"Second List Result {count}: {result}")
result.extend(i)
final_result.append(result)
print(f"combine with the previous list: {count}: {final_result}\n")
chunks = []
chunks = final_result
start_assign -= 1
print(f"Final new list: {final_result}")
Let's say that you have a 20x4 binary matrix mapping people to tables. Rows are people, columns are iterations of the process. Each row contains some permutation of the numbers [0, 3] to indicate the order in which each person traverses the tables. Each column contains exactly five elements corresponding to each table, meaning that at each iteration, all tables are filled evenly. Here is a sample starting configuration:
np.array([
[0, 1, 2, 3], # Person 0 goes to tables 0, 1, 2, 3, in that order
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3],
[1, 2, 3, 0], # Person 5 goes to tables 1, 2, 3, 0, in that order
[1, 2, 3, 0],
[1, 2, 3, 0],
[1, 2, 3, 0],
[1, 2, 3, 0],
[2, 3, 0, 1],
[2, 3, 0, 1],
[2, 3, 0, 1],
[2, 3, 0, 1],
[2, 3, 0, 1],
[3, 0, 1, 2],
[3, 0, 1, 2],
[3, 0, 1, 2],
[3, 0, 1, 2],
[3, 0, 1, 2],
])
This is a valid configuration. Any configuration with 5 of each element in a column and unique elements in each row is a valid configuration. One way to move to another valid configuration is to swap any of the rows: two people will swap schedules, but the constraint on the rows and columns will not change. Similarly, you can swap any two columns: the order of visits will change, but everything will remain valid. Shuffling entire rows and columns like this is not very interesting: groups of five people will end up sticking together for all the steps, even if their paths will be somewhat randomized.
More generally, you can swap a single pair of elements in a given row or column. That will break the validity of the configuration, so you will have to swap other elements a bunch of times to restore it. Here is an example of what happens if you swap the first and last element of the first row:
3, 1, 2, 0
Now you need to find another row that starts with 3 and swap the 3 and the 0 in that row. That will restore the first column, but will possibly invalidate the column that now contains the 3. So you find another 3 elsewhere in the new column, and swap with 0 in the row that contains the 3. You keep doing that until the 3 ends up in the last column for some row, and order is restored.
You can implement something like a Fisher-Yates shuffle for each row, with the added step of resolving the conditions as you go. Here is a sample implementation. I'm using numpy arrays for storage and indexing convenience, but you can do this with lists just as well:
P = 20
M = 4
N = P // M
idx = np.arange(M)
# Start with a valid configuration, same as the array written out above
paths = ((idx + idx[:, None]) % M).repeat(N, axis=0)
for i in range(P):
# Shuffle each row
for j in range(M - 1):
swap = j + np.random.choice(M - j)
if swap == j:
continue
n0 = paths[i, j] # First number to swap
n1 = paths[i, swap] # Second number to swap
# fix up the consequences
ix = swap # Index of the other column
while j != swap:
# Swap the elements in the previous row
paths[i, [j, ix]] = paths[i, [ix, j]]
# Find a new row with n1 at column j
i = np.random.choice(np.flatnonzero(paths[:, j] == n1))
# Find the location of n0 in the new row
ix = j
j = np.flatnonzero(paths[i] == n0)[0]
paths[i, [j, ix]] = paths[i, [ix, j]]
# Check the results:
assert (np.sort(paths, axis=1) == idx).all(None)
assert (np.sum(paths, axis=0) == N * M * (M - 1) // 2).all()
Comments in the code should help you follow along. I can't prove that the solution is unbiased, but it seems pretty good (and fast) from the cursory tests I ran. The assertions at the end can be moved out of the loop to check the final result, or removed entirely if you are confident in the implementation.
With the conditions as you've specified, there are 6 possible paths for each person starting at a given table (3 other tables to visit). Since there are 5 people starting at each table, it works out that in almost all cases, one or more pairs of folks starting at a given table will still end up on the same path. I'm not sure if this just happens because it's likely (~91% for M=4, N=5 without priors [see here]), or because there is something in the conditions that effectively requires it.

Converting repetitive if statements into a loop

I have this code:
#!/usr/bin/python3
def contract(e, i, c, n):
l = len(e)
grid = [[0 for i in range(i + 1)] for x in range(l)]
for num1, row1 in enumerate(grid):
row1[0] = e[num1] #add exponents
for num2, row2 in enumerate(grid):
if 0 <= num2 < n[0]:
grid[num2][1] = c[num2]
if n[0] <= num2 < n[0] + n[1]:
grid[num2][2] = c[num2]
if n[0] + n[1] <= num2 < n[0] + n[1] + n[2]:
grid[num2][3] = c[num2]
for g in grid:
print(g)
e = [0, 1, 2, 3]
i = 3
c = [4, 5, 6, 7]
n = [1, 2, 1]
contract(e, i, c, n)
The idea of this code is that I have a 2 dimensional grid that has dimensions len(e) x (i + 1). The first column contains exponents e. The rest of the columns should contain coefficients c in such a way that n determines the positions of the coefficients in the grid. For example, since n[0] = 1, column 1, row 0 in the grid contains number 4. The next element in n is 2, so the next column in the grid (column 2) should contain 2 numbers, meaning numbers 5 and 6 in rows below the row that I used previously (meaning rows 1 and 2 because row 0 is already used). n[2] = 1 so grid[3][3] = 7, etc.
I implemented this with repetitive if-statements and the code works fine, the output is as it should be:
[0, 4, 0, 0]
[1, 0, 5, 0]
[2, 0, 6, 0]
[3, 0, 0, 7]
However, I would like to make an extensible program that can do this for any number of coefficients and exponents. How can I convert those repetitive if statements to a single loop?
I would convert it into a for loop that keeps track of the sum of the elements seen so far, adjusting the corresponding element if the inequality holds for that iteration:
for num2, row2 in enumerate(grid):
total = 0
for n_idx, n_elem in enumerate(n):
if total <= num2 < total + n_elem:
grid[num2][n_idx + 1] = c[num2]
total += n_elem
I would advise against using sum() in this loop, as it recomputes the sum from scratch on each iteration, which isn't very efficient.
Use a loop that sums successive slices of the n list.
for num2, row2 in enumerate(grid):
for idx in range(len(n)):
if sum(n[:idx]) <= num2 < sum(n[:idx+1]):
grid[num2][idx+1] = c[num2]
This is a direct mapping of the code you wrote to a loop, and reasonable if n doesn't get too large. BrokenBenchmark's answer is optimized to take advantage of the fact that the sum of each slice is the sum of the previous slice plus the current element.

Python: printing 1 row of Pascal Triangle

New to learning python and am having some trouble understanding a solution provided?
It has to do with Pascal Triangle and printing the rows when asking a user to "enter a row number"
There were bits of the solution provided and the rest I fit in (first for loop)..
n=int(input("Enter a row number: "))
a=[]
for i in range(n):
a.append([])
a[i].append(1)
for j in range(1,i):
a[i].append(a[i-1][j-1]+a[i-1][j])
if(n!=0):
a[i].append(1)
for i in range(n):
print(" "*(n-i),end=" ",sep=" ")
for j in range(0,i+1):
print('{:4}'.format(a[i][j]),end=" ")
print()
My question is which part of the code is printing the triangle structure? I assume the last for loop?
Also if I wanted to just print 1 row, what would I be changing?
EX: Input: 5 and output would be [1 4 6 4 1 ]
Thank you and any help/advice would be appreciated
You are right, the last loop is printing every row of the triangle. To print any specific row, jut run the second loop with specific value of i.
Before that, there is an easier way to more forward. Let's consider the output of below code:
n = 7
a = []
for i in range(n):
a.append([])
a[i].append(1)
for j in range(1, i):
a[i].append(a[i - 1][j - 1] + a[i - 1][j])
if (n != 0):
a[i].append(1)
print(a)
The output is:
[[1, 1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1], [1, 6, 15, 20, 15, 6, 1]]
From this 2d array, you can decide which single element you want to print. For example at index 4 you have [1, 4, 6, 4, 1]. From these values in the array a you can figure out which row to print.
Now for 5 if you want [1, 4, 6, 4, 1], you can simply do the following:
n = 7
a = []
for i in range(n):
a.append([])
a[i].append(1)
for j in range(1, i):
a[i].append(a[i - 1][j - 1] + a[i - 1][j])
if (n != 0):
a[i].append(1)
to_print = 5
for i in range(0, len(a[to_print-1])):
print(a[to_print-1][i], end=" ")
The output will be:
1 4 6 4 1
#riam_98, would you like to try this version: It's simplified the logic/flow to take advantage of Pascal key characteristics.
More reading can be found here - https://en.wikipedia.org/wiki/Pascal's_triangle
from typing import List
def getRow(index: int) -> List[int]:
row = [1] # firsts row
if index == 1: return row
for i in range(index-1):
for j in range(i, 0, -1):
row[j] = row[j] + row[j-1]
row.append(1)
return row
print(getRow(2))
print(getRow(3))
print(getRow(4))
print(getRow(5))
Outputs:
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1] # 5th
if I wanted to just print 1 row, what would I be changing?
I believe the answers given (and accepted) so far do too much work to obtain the values for an individual row. If we look at Calculating a row or diagonal by itself in the Wikipedia page that #DanielHao recommends, we can generate a simpler solution:
n = int(input("Enter a row number: "))
numbers = [1]
for k in range(1, n):
numbers.append(numbers[-1] * (n - k) // k)
print(numbers)
We don't need to generate the entire triangle up to the row we desire nor use nested loops to calculate it.
OUTPUT
> python3 test.py
Enter a row number: 5
[1, 4, 6, 4, 1]
> python3 test.py
Enter a row number: 10
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
>
which part of the code is printing the triangle structure? I assume
the last for loop?
Yes, but note that it is a fragile, limited solution due to the hardcoding of the number width:
print('{:4}'.format(a[i][j]),end=" ")
For smaller values, the triangle is lopsided, and for values larger than 16, it loses all its symmetry. Here's an example of a Pascal's triangle output that self adjusts Compare it's output to the solution you're asking about.

Algorithm Lists with 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).

Categories