symmetric matrix half vectorization - java

I am facing an issue concerning how to store the lower triangular factor of a given symmetric matrix (it's a distance matrix) into a vector.
Generally, I would like directly generating the lower triangular entries by only giving the coordinates (Y,Z) of a set of points on a rectangular grid: and actually it's where I got terribly stuck.
So that, I started thinking to attack the problem from a slightly different point of view: generating the full distance matrix (again given the (Y,Z) couples) and then half vectorize the distance matrix.
Nevertheless, I don't really have a sound idea on how to achieve the goal by means of for loops.
Besides I also know that there may be any external Java library which implements the vech function: vech returns the vector obtained by eliminating all supradiagonal elements of the square matrix X and stacking the result one column above the other. This has uses in matrix calculus where the underlying matrix is symmetric and it would be pointless to keep values above the main diagonal.
Substantially, given a matrix A = {{a,c},{b,d}}, by applying vech(A), the result will be
vech(A) = {a,b,d}.
EDIT
I mean something like the following:
a11 a12 a13 a14
a22 a23 a24
A= a33 a34 (aij = aji)
a44
Packed storage of the upper triangle of A:
AP = { a11, a12, a22, a13, a23, a33, a14, a24, a34, a44 }

public static double[] vech(double[][] a) {
int na = Math.min(a.length, a[0].length); // Dimension of the matrix
int nv = na * (na + 1) / 2; // 1 + 2 + 3 + .. + na
double[] v = new double[nv];
int k = 0; // index in v.
for (int i = 0; i < na; ++i) {
for (int j = 0; j <= i; ++j) {
v[k] = a[i][j];
++k;
}
}
return v;
}
Case 2x2 Matrix:
Picks [0][0], [1][0], [1][1] (skipping [0][1])
Row-major order: (C, C#, Java) a[i][j] is element at row i, column j.
The code flattens the bottom left triangle.
Column-major order: (MATLAB, SciLab) a[i][j] is element at column i, row j.
The code flattens the upper right triangle.
Other sequence
The other triangle would be given as:
for (int j = i; j < na; ++j) {
Combined with mirroring in the main diagonal, one receives the orignal triangle again:
a[j][i]

There is another interesting thing about all this packing. You can also define a mapping algorithm to convert (i, j) positon in symmetric matrix into equivalent offset in flatten array (just like you described). You can use ideas of Arithmetic progression to define such mapping. I did this while working on RandomSymmetricMatrixSource.java class in la4j. So, you can use these formulas (it doesn't handle the case when i == j):
int flatten(int i, int j) {
int offset = -1;
if (i < j) {
offset = j - (i + 1) + (int)((((size - 1) + (size - i)) / 2.0) * i);
} else {
offset = i - (j + 1) + (int)((((size - 1) + (size - j)) / 2.0) * j);
}
return offset;
}
, where size is the size of symmetric matrix.

I can't think of a library that would let you do this, although i'm sure there is one somewhere, but you could use a for loop as follows:
ArrayList<ArrayList<int>> matrix = new ArrayList<ArrayList<int>>();
// add other arraylists to your matrix here i.e.:
ArrayList<Integer> first = new ArrayList<Integer>();
first.add(1);
first.add(2);
first.add(3);
ArrayList<Integer> second = new ArrayList<Integer>();
second.add(4);
second.add(5);
second.add(6);
ArrayList<Integer> third = new ArrayList<Integer>();
third.add(7);
third.add(8);
third.add(9);
matrix.add(first);
matrix.add(second);
matrix.add(third);
ArrayList<int> finalArray = new ArrayList<int>();
for(int i=0; i<matrix.size(); i++)
{
ArrayList<Integer> inner = matrix.get(i);
for(int j=0; j<i+1; j++)
{
finalArray.add(inner.get(j));
}
}
This gives: matrix=[[1, 2, 3], [4, 5, 6], [7, 8, 9]] and finalArray=[1, 4, 5, 7, 8, 9]
Of course, this is assuming your matrix is structured using arraylists.

Related

Project Euler Problem 18 - Pascal's Triangle

EDIT 2
I separated out the offset code into a new method at Gavin's suggestion:
private static int getOffset(int offset, int row, int col, ArrayList<ArrayList<Integer>> triangle, ArrayList<ArrayList<Integer>> p_triangle, ArrayList<Integer> sums) {
int row_num = (row+1); //= 1-indexed row #
int p_value = p_triangle.get(row).get(col); // number from pascal's triangle
if (col > 1) {
// element is in the left half of Pascal's Triangle
if (col <= (row_num/2)) offset++;
// penultimate element
else if (col == row_num - 2) offset = sums.size() - p_value;
// elements halfway until penultimate;
// [-2, -3] all work up until row 10 and fail thereafter
else offset = sums.size() - p_value - (row_num - col - 2);
}
return offset;
}
And found that, oddly enough, subtracting 2 or 3 both work when calculating the offset for an element in the latter half of the given row (between halfway and antepenultimate). And I have no idea why that's the case.
Even stranger is that I modified Oleg's answer
public static int findMaxSum(ArrayList<ArrayList<Integer>> data) {
for (int row = data.size() - 2; row >= 0; row--)
for (int col = 0; col < data.get(row).size(); col++)
data.get(row).set(col, data.get(row).get(col) + Math.max(data.get(row + 1).get(col), data.get(row + 1).get(col + 1)));
return data.get(0).get(0);
}
and found that the behavior of algorithm appears to be correct up to a triangle of size 10. However, it starts to breakdown after that with the following discrepancies in rows 11-15:
size = 11 [correct:772 | mine:752]
size = 12 [correct:850 | mine:830]
size = 13 [correct:908 | mine:921]
size = 14 [correct:981 | mine:961]
size = 15 [correct:1074 | mine:1059]
Unfortunately, I still can't discern a pattern from this.
EDIT
I'd like to emphasize that I'm not looking for a better way to solve this particular Project Euler problem; instead, I just want to know if it's possible to use Pascal's Triangle to do it in the way I described (or in some slightly modified way) and if someone can see the logic in my code I may be blind to.
ORIGINAL QUESTION
I am trying to solve Project Euler problem 18.
The goal is to find the max sum of all the 2^14 paths down a triangle of numbers.
I was struck by the similarity with Pascal's Triangle and wondered if it could be used to solve the problem.
My logic is as follows:
1.) Calculate the sums by row.
2.) Use Pascal's triangle to determine how many there must be (as each row adds up to a power of two) and to determine the offset from the start of the of the previous rows sums.
Ex.
Pascal's Triangle
1
1 1
1 2 1
1 3 3 1
Triangle To Process
3
7 4
2 4 6
8 5 9 3
Sums
[3]
[10, 7]
[12, 14, 11, 13]
[20, 17, 19, 16, 23, 20, 22, 16]
For row 3, we see Pascal's Triangle informs us that there will be 1 + 2 + 1 or 4 values. Furthermore, it describes how to build the sums because it's the first and last element added to the sum directly preceding them and the middle value added to both of those sums as it has contact with both the preceding chains.
By extension, the fourth row shows that the second number in the Triangle to Process should be added to the first three sums from row three and the third number should be added to the final three.
The way I get the offset is kind of ugly (and maybe the source of the trouble):
if (i > 1) {
if (i < (p_triangle.get(row).size()/2)) offset++;
else if (i == triangle.get(row).size()-2) offset = sums.size() - p_triangle.get(row).get(i);
else offset = sums.size() - p_triangle.get(row).get(i) - (p_triangle.get(row).size() - i - 2);
}
Where p_triangle.get(row) is the current Pascal's Triangle row being used, sums is the array of cumulative sums (2^(row-1) in length), offset is where to start the summations from, and the Pascal's Triangle number is how many elements from the sum list starting at the offset to sum the number at index i in the Triangle to Process, i.e., triangle.get(row).get(i).
I know this may not be the most efficient algorithm to solve the problem, but it seems like it could be a nice one. The thing is, I can't get it to work.
SPOILER ALERT ON THE ANSWER TO THE PROBLEM
The correct answer is apparently 1074
Can anyone tell me where in the code or in my logic for using Pascal's Triangle, I might have messed up?
THE FULL CODE:
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.lang.Math;
public class MaxPathSum {
private static ArrayList<ArrayList<Integer>> pascalsTriangle(int n_rows) {
ArrayList<ArrayList<Integer>> triangle = new ArrayList<>();
triangle.add(new ArrayList<Integer>(){{add(1);}});
triangle.add(new ArrayList<Integer>(){{add(1); add(1);}});
for (int row = 2; row < n_rows; row++) {
ArrayList<Integer> next_row = new ArrayList<>();
next_row.add(1);
for (int i = 1; i < triangle.get(row-1).size(); i++) {
next_row.add(triangle.get(row-1).get(i-1) + triangle.get(row-1).get(i));
}
next_row.add(1);
triangle.add(next_row);
}
return triangle;
}
private static ArrayList<ArrayList<Integer>> buildTriangle(int n_rows) {
Scanner sc = new Scanner(System.in);
ArrayList<ArrayList<Integer>> triangle = new ArrayList<>();
for (int row = 1; row <= n_rows; row++) {
ArrayList<Integer> row_arr = new ArrayList<>();
for (int elem = 1; elem <= row; elem++) {
row_arr.add(sc.nextInt());
}
triangle.add(row_arr);
}
return triangle;
}
private static int findLargestSum(ArrayList<ArrayList<Integer>> triangle, ArrayList<ArrayList<Integer>> p_triangle) {
ArrayList<Integer> sums = new ArrayList<>();
sums.add(triangle.get(0).get(0));
// traverse the rows
for (int row = 1, offset = 0; row < triangle.size(); row++, offset = 0) {
ArrayList<Integer> new_sums = new ArrayList<>();
// traverse each element in each row
new_sums.add(sums.get(0) + triangle.get(row).get(0));
for (int i = 1; i < triangle.get(row).size()-1; i++) {
int n_times = p_triangle.get(row).get(i);
for (int j = 0; j < n_times; j++) {
new_sums.add(triangle.get(row).get(i) + sums.get(j+offset));
}
if (i > 1) {
if (i < (p_triangle.get(row).size()/2)) offset++;
else if (i == triangle.get(row).size()-2) offset = sums.size() - p_triangle.get(row).get(i);
else offset = sums.size() - p_triangle.get(row).get(i) - (p_triangle.get(row).size() - i - 2);
System.out.println("Row: " + row + " | Offset: " + offset);
}
}
new_sums.add(sums.get(sums.size()-1) + triangle.get(row).get(triangle.get(row).size()-1));
sums = new_sums;
}
Collections.sort(sums);
return sums.get(sums.size() - 1);
}
public static void main(String[] args) {
int n_rows = Integer.parseInt(args[0]);
// build pascalsTriangle
ArrayList<ArrayList<Integer>> p_triangle = pascalsTriangle(n_rows);
// build triangle from input
ArrayList<ArrayList<Integer>> triangle = buildTriangle(n_rows);
// traverse triangle finding largest sum
int largest_sum = findLargestSum(triangle, p_triangle);
// display results
System.out.println(largest_sum);
}
}
Just be simple!
public static int findMaxSum(int[][] data) {
for (int row = data.length - 2; row >= 0; row--)
for (int col = 0; col < data[row].length; col++)
data[row][col] += Math.max(data[row + 1][col], data[row + 1][col + 1]);
return data[0][0];
}

Fill One Array with Other Array but the Rest with 0

I am creating a small java program that does some vector math, and I'm getting stuck with addition and subtraction.
We have to make our own vector class, and all it contains is an Array with its values, so
Vector a = new Vector(1, 2, 3) //this would make a vector with [1, 2, 3]
Vector b = new Vector(4, 3) //this would make a vector with [4, 3]
I cannot do vector addition a + b with what I have now, because if I loop over every index in Vector a, I would get an out of bounds error with Vector b.
How do I make a new array that has all the values of vector b with 0's for the rest, so
//a is [1, 2, 3]
//b is [4, 3]
fix = [4, 3, 0]; //same as b but with trailing 0's so a.length == fix.length
Pseudocode that fulfils the "don't use any library" requirement of your prof:
int maxlength = max(a.length, b.length)
Vector c = ... // create Vector of maxlength length
for (int i = 0; i < maxlength; i++)
{
if (i < a.length)
{
c[i] += a[i];
}
if (i < b.length)
{
c[i] += b[i];
}
}
get the length of Vector a , in your case it is 3
get the length of vector b, in your case it is 2
so number of 0 to put in Vector b 3-2=1
use for loop starting with index 2 that is length of Vector b.
and loop until the length of Vector a
for(int i=b.length;i<a.length;i++){
b[i]=0;
}
I think you should be able to do this without help. (And I'm sure that's what your teacher wants!)
But here are a couple of hints. You say:
I cannot do vector addition a + b with what I have now, because if I loop over every index in Vector a, I would get an out of bounds error with Vector b.
Step #1: test for the case(s) where you would get the bounds error ... and only do the assignment if you wouldn't get a bounds error; e.g.
// pseudo-code ... for illustration purposes only
int len = /* calculate length in source array */
for (int i = 0; i < len; i++) {
int j = /* calculate index in target array */
if (j < targetArray.length) { // test that j is "in bounds"
targetArray[j] = ...
}
}
Step #2: once you have figured that out Step #1, you should be able to hoist the if test out of the loop body by incorporating it into the calculation of len. The Math.max(int, int) function will be useful.

Divide an Array in equal size, such that value of given function is minimum

I've came across the following problem statement.
You have a list of natural numbers of size N and you must distribute the values in two lists A and B of size N/2, so that the squared sum of A elements is the nearest possible to the multiplication of the B elements.
Example:
Consider the list 7 11 1 9 10 3 5 13 9 12.
The optimized distribution is:
List A: 5 9 9 12 13
List B: 1 3 7 10 11
which leads to the difference abs( (5+9+9+12+13)^2 - (1*3*7*10*11) ) = 6
Your program should therefore output 6, which is the minimum difference that can be achieved.
What I've tried:
I've tried Greedy approach in order to solve this problem. I took two variables sum and mul. Now I started taking elements from the given set one by one and tried adding it in both the variables and calculated current
square of sum and multiplication. Now finalize the element in one of the two sets, such that the combination gives minimum possible value.
But this approach is not working in the given example itselt. I can't figure out what approach could be used here.
I'm not asking for exact code for the solution. Any possible approach and the reason why it is working, would be fine.
EDIT:
Source: CodinGame, Community puzzle
Try out this:
import java.util.Arrays;
public class Test {
public static void main(String [] args){
int [] arr = {7, 11, 1, 9, 10, 3, 5, 13, 9, 12};
int [][] res = combinations(5, arr);
int N = Arrays.stream(arr).reduce(1, (a, b) -> a * b);
int min = Integer.MAX_VALUE;
int [] opt = new int [5];
for (int [] i : res){
int k = (int) Math.abs( Math.pow(Arrays.stream(i).sum(), 2) - N/(Arrays.stream(i).reduce(1, (a, b) -> a * b)));
if(k < min){
min = k;
opt = i;
}
}
Arrays.sort(opt);
System.out.println("minimum difference is "+ min + " with the subset containing this elements " + Arrays.toString(opt));
}
// returns all k-sized subsets of a n-sized set
public static int[][] combinations(int k, int[] set) {
int c = (int) binomial(set.length, k);
int[][] res = new int[c][Math.max(0, k)];
int[] ind = k < 0 ? null : new int[k];
for (int i = 0; i < k; ++i) {
ind[i] = i;
}
for (int i = 0; i < c; ++i) {
for (int j = 0; j < k; ++j) {
res[i][j] = set[ind[j]];
}
int x = ind.length - 1;
boolean loop;
do {
loop = false;
ind[x] = ind[x] + 1;
if (ind[x] > set.length - (k - x)) {
--x;
loop = x >= 0;
} else {
for (int x1 = x + 1; x1 < ind.length; ++x1) {
ind[x1] = ind[x1 - 1] + 1;
}
}
} while (loop);
}
return res;
}
// returns n choose k;
// there are n choose k combinations without repetition and without observance of the sequence
//
private static long binomial(int n, int k) {
if (k < 0 || k > n) return 0;
if (k > n - k) {
k = n - k;
}
long c = 1;
for (int i = 1; i < k+1; ++i) {
c = c * (n - (k - i));
c = c / i;
}
return c;
}
}
Code taken from this stackoverflow answer, also take a look at this wikipedia article about Combinations.
I am not sure if there is any exact solution in polynomial time. But you could try a simulated annealing based approach.
My approach would be:
Initialize listA and listB to a random state
With probability p run greedy step, otherwise run a random step
Keep track of the state and corresponding error (with a HashMap)
Greedy step: Find one element you can move between the list that optimizes the error.
Random Step: Pick a random element from either of these two sets and calculate the error. If the error is better, keep it. Otherwise with probability of q keep it.
At either of these two steps make sure that the new state is not already explored (or at least discourage it).
Set p to a small value (<0.1) and q could depend on the error difference.

Java how to find the middle of a 2d array

I'm writing a function that tries to find the middle of a 2d array, and here's what I have so far:
int findMiddle(int[][] grid,int [] m) {
int[] list = new int[grid.length*grid[0].length];
int listPos = 0;
for(int i = 0 ; i < grid.length; i++) {
for(int j = 0; j < grid.length; j++) {
list[listPos++] = grid[i][j];
}
}
int middle = m.length/2;
if (m.length%2 == 1) {
return m[middle];
} else {
return (m[middle-1] + m[middle]) / 2.0;
}
}
Suppose I have an array of
{{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 0, 1}}
It should return 6, as it is integer division.
Also, the definition of middle in this code is the middle integer of the whole original array (needless to say if it is sorted or not).
How would I do this? ( my code also doesn't compile)
This compiles, and will give you the middle number in your sequence, or the average of the middle two if it splits the middle:
static double findMiddle(int[][] grid) {
int[] list = new int[grid.length*grid[0].length];
int listPos = 0;
for(int i = 0 ; i < grid.length; i++) {
for(int j = 0; j < grid[i].length; j++) {
list[listPos++] = grid[i][j];
}
}
int middle = list.length/2;
if ((list.length%2) == 1) {
return list[middle];
}
return (list[middle-1] + list[middle]) / 2.0;
}
However, I suspect you'll want to sort you numbers after you combine them into a single array, before you find the middle numbers. (as piyush121 commented)
Assuming your definition of 'median' is correct (the middle number of an odd length set or the average of the two middle numbers of an even length set), and if grid[][] is sorted, and if grid[][] is a square array (i.e. grid.length = grid[i].length for all i in 0..length-1, then you don't need to copy data to another array. The following should suffice:
static int findMiddle(int[][] grid) {
int l = grid.length;
if (l%2 == 1) {
return grid[l/2][l/2];
} else {
return (grid[l/2-1][l-1]+grid[l/2][0])/2;
};
Looking at your existing code it seems you are defining 'median' as the middle value in the matrix if all values were put into a single row-wise list. In other words you don't need to cope with odd numbers of rows (when two numbers from the same column are in the middle) or odd numbers of rows and columns (when there are four numbers in the middle).
If that definition is correct then you can cope with all the complexity of uneven rows by streaming all values and then selecting the middle ones.
For your interest, here's a Java 8 solution that does that:
int[] flat = Arrays.stream(grid).flatMapToInt(Arrays::stream).toArray();
double middle = Arrays.stream(flat).skip(flat.length / 2).limit(1 + flat.length % 2)
.average().getAsDouble();

Understanding Kadane's Algorithm for 2-D Array

I'm trying to write a program which solves the maximum subarray problem. I can understand the intuition behind Kadane's Algorithm on a 1-D array as well as the O(N^4) implementation on a 2-D array. However, I am having some trouble understanding the O(N^3) implementation on a 2-D array.
1) Why do we add up the elements with those from the previous rows within the same column?
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++)
array[i][j] += array[i-1][j];
}
2) I have no understanding of the second part of the algorithm
Tried looking for an explanation on the web but to no avail. Hope to get some help here!
Thanks in advance!
You know how to compute maximum sum sub-array on a 1D array using Kadane's algorithm. Now we want to extend this algorithm for the 2D array. For an O(N^3) algorithm, we have an intuition. If we somehow create N^2 sub problems and then try to run our O(N) Kadane's algorithm, we can solve the maximum sub array problem.
So basically how we create the N^2 sub problems is by iterating over all the top and bottom rows of the matrix. Then we try to find the optimal columns between which the sub array exists by applying kadane's 1D algorithm. We thus sum the numbers between these two rows column wise and then apply kadane's 1D algorithm on this newly formed 1D array.
But we have a problem here. Computing the sums for all the O(n^2) ranges of the top and bottom rows will itself be O(n^4). This bottle neck can be overcome by modifying our matrix by replacing each element with the sum of all the numbers that are above it in that element's column. Thus, now we can find out the sum of numbers between any two rows in O(n) time by subtracting the appropriate arrays in the matrix.
The java pseudo code -
int kadane2D(int array[N][M]){
// Modify the array's elements to now hold the sum
// of all the numbers that are above that element in its column
for (int i = 1; i < N; i++) {
for (int j = 0; j < M; j++){
array[i][j] += array[i-1][j];
}
}
int ans = 0; // Holds the maximum sum matrix found till now
for(int bottom = 0; bottom < N; bottom++){
for(int top = bottom; top < N; top++){
// loop over all the N^2 sub problems
int[] sums = new int[N];
// store the sum of numbers between the two rows
// in the sums array
for(int i = 0; i < M; i++){
if (bottom > 0) {
sums[i] = array[top][i] - array[bottom-1][i];
} else {
sums[i] = array[top][i];
}
}
// O(n) time to run 1D kadane's on this sums array
ans = Math.max(ans, kadane1d(sums));
}
}
return ans;
}
For people who understand the Kadane's 1D algorithm, below should be easy to understand. Basically we try to convert the 2D matrix into 1D by using the prefix sum for each rows. And for each prefix sum row, we just apply the Kadane's 1D algorithm.
Just posting the working Python code:
class Kadane2D:
def maxSumRetangle(self, grid):
def kadane1D(arr):
curmax, maxsofar = 0, float('-inf')
for a in arr:
curmax = max(a, curmax + a)
maxsofar = max(curmax, maxsofar)
return maxsofar
m, n, ans = len(grid), len(grid[0]), float('-inf')
colCum = [[0] * n]
for row in grid:
colCum.append([pre + now for pre, now in zip(colCum[-1], row)])
for top in range(1, m + 1):
for bottom in range(top, m + 1):
sums = [b - t for b, t in zip(colCum[bottom], colCum[top - 1])]
ans = max(ans, kadane1D(sums))
return ans
grid = [[1, 2, - 3], [3, 4, -6]]
assert Kadane2D().maxSumRetangle(grid) == 10
grid = [[1, 2, -1, -4, -20],
[-8, -3, 4, 2, 1],
[3, 8, 10, 1, 3],
[-4, -1, 1, 7, -6]]
assert Kadane2D().maxSumRetangle(grid) == 29
I know it's an old question. But Google doesn't have the right answers, or they're overworked.
No, this is no correct way. Working example, on O(N^2):
/**
* Kadane 1d
* #return max sum
*/
public static int maxSum(int[] a) {
int result = a[0]; //get first value for correct comparison
int sum = a[0];
for (int i = 1; i < a.length; i++) {
sum = Math.max(sum + a[i], a[i]); //first step getting max sum, temporary value
result = Math.max(result, sum);
}
return result;
}
/**
* Kadane 2d
* #param array
* #return max sum
*/
public static int maxSum2D(int array[][]){
int result = Integer.MIN_VALUE; //result max sum
for (int i = 0; i < array.length; i++) {
int sum = maxSum(array[i]);
result = Math.max(result, sum);
}
return result;
}
Fully examples:
Easy: https://pastebin.com/Qu1x0TL8
Supplemented: https://pastebin.com/Tjv602Ad
With indexes: https://pastebin.com/QsgPBfY6

Categories

Resources