You are given a matrix, consisting of three rows and three columns, represented as an array A of nine integers. The rows of the matrix are numbered from 0 to 2 (from top to bottom) and the columns are numbered from 0 to 2 (from left to right). The matrix element in the J-th row and K-th column corresponds to the array element A[J*3 + K]. For example, the matrix below corresponds to array [0, 2, 3, 4, 1, 1, 1, 3, 1].
In one move you can increment any element by 1.
Your task is to find a matrix whose elements in each row and each column sum to an equal value, which can be constructed from the given matrix in a minimal number of moves.
Write a function:
function solution(A);
that, given an array A of nine integers, returns an array of nine integers, representing the matrix described above. If there are several possible answers, the function may return any of them.
Examples:
1. Given A = [0, 2, 3, 4, 1, 1, 1, 3, 1], the function could return [1, 2, 3, 4, 1, 1, 1, 3, 2]. The sum of elements in each row and each column of the returned matrix is 6. Two increments by 1 are enough. You can increment A[0] and A[8] (top-left and bottom-right matrix elements). This gives [1, 2, 3, 4, 1, 1, 1, 3, 2], which satisfies the statement's conditions. Alternatively, you can increment A[2] and A[6] (top-right and bottom-left matrix elements). This gives another correct solution: [0, 2, 4, 4, 1, 1, 2, 3, 1].
2. Given A = [1, 1, 1, 2, 2, 1, 2, 2, 1], the function should return [1, 1, 3, 2, 2, 1, 2, 2, 1]. The sum of elements in each row and each column of the returned matrix is 5. Two increments by 1 are enough. You can increment A[2] (top-right matrix element) twice. In this case, there are no other correct solutions.
Write an efficient algorithm for the following assumptions:
- array A contains nine elements;
- each element of array A is an integer within the range [0..100,000,000].
// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');
let getSum = (arr) => {
return arr.reduce((acc, val) => {
return acc + val;
}, 0);
};
let findMaxSum = (data) => {
let max = 0;
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
if (thisSum > max) {
max = thisSum;
}
}
return max;
};
let analyzeCollection = (data, MAX) => {
let minIndex = 0;
// max possible sum
let min = 3 * 100000000;
let tracker = [];
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
tracker.push(thisSum);
if (thisSum <= min) {
min = thisSum;
minIndex = i;
}
}
const isSame = tracker[0] === tracker[1] && tracker[1] === tracker[2];
tracker.sort((a, b) => a - b);
return {
min,
minIndex,
isSame,
minDiff: tracker[1] - tracker[0] || tracker[2] - tracker[1] || MAX - tracker[0]
};
};
let organizeArray = (data) => {
// by far the fastest way to organize rows/cols
return {
rows: [
[data[0], data[1], data[2]],
[data[3], data[4], data[5]],
[data[6], data[7], data[8]]
],
cols: [
[data[0], data[3], data[6]],
[data[1], data[4], data[7]],
[data[2], data[5], data[8]]
]
};
};
function getBestNumToIncrease(rows, cols, MAX) {
// best number to increase => benti
// find lowest row sum and col sum
// best number to increase is the intersection of those two collections
let { minIndex: minRowIndex, isSame: rowsSame, minDiff: minDiffRow } = analyzeCollection(rows, MAX);
let { minIndex: minColIndex, isSame: colsSame, minDiff: minDiffCol } = analyzeCollection(cols, MAX);
// calculate largest safest increment for benti
return [minRowIndex, minColIndex, rowsSame && colsSame, Math.min(minDiffRow, minDiffCol)];
}
function solveData([rows, cols], MAX) {
let [r_benti, c_benti, isSolved, minimalIncrement] = getBestNumToIncrease(rows, cols, MAX);
if (isSolved) {
return [...rows[0], ...rows[1], ...rows[2]];
}
rows[r_benti][c_benti] += minimalIncrement;
cols[c_benti][r_benti] += minimalIncrement;
return solveData([rows, cols], MAX);
}
let solution = (A) => {
let { rows, cols } = organizeArray(A);
let SUMS = [...rows, ...cols].map(getSum);
let MAX = Math.max(...SUMS);
return solveData([rows, cols], MAX);
};
// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');
let getSum = (arr) => {
return arr.reduce((acc, val) => {
return acc + val;
}, 0);
};
let findMaxSum = (data) => {
let max = 0;
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
if (thisSum > max) {
max = thisSum;
}
}
return max;
};
let analyzeCollection = (data, MAX) => {
let minIndex = 0;
// max possible sum
let min = 3 * 100000000;
let tracker = [];
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
tracker.push(thisSum);
if (thisSum <= min) {
min = thisSum;
minIndex = i;
}
}
const isSame = tracker[0] === tracker[1] && tracker[1] === tracker[2];
tracker.sort((a, b) => a - b);
return {
min,
minIndex,
isSame,
minDiff: tracker[1] - tracker[0] || tracker[2] - tracker[1] || MAX - tracker[0]
};
};
let organizeArray = (data) => {
// by far the fastest way to organize rows/cols
return {
rows: [
[data[0], data[1], data[2]],
[data[3], data[4], data[5]],
[data[6], data[7], data[8]]
],
cols: [
[data[0], data[3], data[6]],
[data[1], data[4], data[7]],
[data[2], data[5], data[8]]
]
};
};
function getBestNumToIncrease(rows, cols, MAX) {
// best number to increase => benti
// find lowest row sum and col sum
// best number to increase is the intersection of those two collections
let { minIndex: minRowIndex, isSame: rowsSame, minDiff: minDiffRow } = analyzeCollection(rows, MAX);
let { minIndex: minColIndex, isSame: colsSame, minDiff: minDiffCol } = analyzeCollection(cols, MAX);
// calculate largest safest increment for benti
return [minRowIndex, minColIndex, rowsSame && colsSame, Math.min(minDiffRow, minDiffCol)];
}
function solveData([rows, cols], MAX) {
let [r_benti, c_benti, isSolved, minimalIncrement] = getBestNumToIncrease(rows, cols, MAX);
if (isSolved) {
return [...rows[0], ...rows[1], ...rows[2]];
}
rows[r_benti][c_benti] += minimalIncrement;
cols[c_benti][r_benti] += minimalIncrement;
return solveData([rows, cols], MAX);
}
let solution = (A) => {
let { rows, cols } = organizeArray(A);
let SUMS = [...rows, ...cols].map(getSum);
let MAX = Math.max(...SUMS);
return solveData([rows, cols], MAX);
};
// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');
let getSum = (arr) => {
return arr.reduce((acc, val) => {
return acc + val;
}, 0);
};
let findMaxSum = (data) => {
let max = 0;
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
if (thisSum > max) {
max = thisSum;
}
}
return max;
};
let analyzeCollection = (data, MAX) => {
let minIndex = 0;
// max possible sum
let min = 3 * 100000000;
let tracker = [];
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
tracker.push(thisSum);
if (thisSum <= min) {
min = thisSum;
minIndex = i;
}
}
const isSame = tracker[0] === tracker[1] && tracker[1] === tracker[2];
tracker.sort((a, b) => a - b);
return {
min,
minIndex,
isSame,
minDiff: tracker[1] - tracker[0] || tracker[2] - tracker[1] || MAX - tracker[0]
};
};
let organizeArray = (data) => {
// by far the fastest way to organize rows/cols
return {
rows: [
[data[0], data[1], data[2]],
[data[3], data[4], data[5]],
[data[6], data[7], data[8]]
],
cols: [
[data[0], data[3], data[6]],
[data[1], data[4], data[7]],
[data[2], data[5], data[8]]
]
};
};
function getBestNumToIncrease(rows, cols, MAX) {
// best number to increase => benti
// find lowest row sum and col sum
// best number to increase is the intersection of those two collections
let { minIndex: minRowIndex, isSame: rowsSame, minDiff: minDiffRow } = analyzeCollection(rows, MAX);
let { minIndex: minColIndex, isSame: colsSame, minDiff: minDiffCol } = analyzeCollection(cols, MAX);
// calculate largest safest increment for benti
return [minRowIndex, minColIndex, rowsSame && colsSame, Math.min(minDiffRow, minDiffCol)];
}
function solveData([rows, cols], MAX) {
let [r_benti, c_benti, isSolved, minimalIncrement] = getBestNumToIncrease(rows, cols, MAX);
if (isSolved) {
return [...rows[0], ...rows[1], ...rows[2]];
}
rows[r_benti][c_benti] += minimalIncrement;
cols[c_benti][r_benti] += minimalIncrement;
return solveData([rows, cols], MAX);
}
let solution = (A) => {
let { rows, cols } = organizeArray(A);
let SUMS = [...rows, ...cols].map(getSum);
let MAX = Math.max(...SUMS);
return solveData([rows, cols], MAX);
};
// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');
let getSum = (arr) => {
return arr.reduce((acc, val) => {
return acc + val;
}, 0);
};
let findMaxSum = (data) => {
let max = 0;
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
if (thisSum > max) {
max = thisSum;
}
}
return max;
};
let analyzeCollection = (data, MAX) => {
let minIndex = 0;
// max possible sum
let min = 3 * 100000000;
let tracker = [];
for (let i = 0; i < data.length; i++) {
let thisSum = getSum(data[i]);
tracker.push(thisSum);
if (thisSum <= min) {
min = thisSum;
minIndex = i;
}
}
const isSame = tracker[0] === tracker[1] && tracker[1] === tracker[2];
tracker.sort((a, b) => a - b);
return {
min,
minIndex,
isSame,
minDiff: tracker[1] - tracker[0] || tracker[2] - tracker[1] || MAX - tracker[0]
};
};
let organizeArray = (data) => {
// by far the fastest way to organize rows/cols
return {
rows: [
[data[0], data[1], data[2]],
[data[3], data[4], data[5]],
[data[6], data[7], data[8]]
],
cols: [
[data[0], data[3], data[6]],
[data[1], data[4], data[7]],
[data[2], data[5], data[8]]
]
};
};
function getBestNumToIncrease(rows, cols, MAX) {
// best number to increase => benti
// find lowest row sum and col sum
// best number to increase is the intersection of those two collections
let { minIndex: minRowIndex, isSame: rowsSame, minDiff: minDiffRow } = analyzeCollection(rows, MAX);
let { minIndex: minColIndex, isSame: colsSame, minDiff: minDiffCol } = analyzeCollection(cols, MAX);
// calculate largest safest increment for benti
return [minRowIndex, minColIndex, rowsSame && colsSame, Math.min(minDiffRow, minDiffCol)];
}
function solveData([rows, cols], MAX) {
let [r_benti, c_benti, isSolved, minimalIncrement] = getBestNumToIncrease(rows, cols, MAX);
if (isSolved) {
return [...rows[0], ...rows[1], ...rows[2]];
}
rows[r_benti][c_benti] += minimalIncrement;
cols[c_benti][r_benti] += minimalIncrement;
return solveData([rows, cols], MAX);
}
let solution = (A) => {
let { rows, cols } = organizeArray(A);
let SUMS = [...rows, ...cols].map(getSum);
let MAX = Math.max(...SUMS);
return solveData([rows, cols], MAX);
};
The solution obtained perfect score.
Random matrices with medium value range, max - min <= 2000, and two zeros.