Find max sum submatrix in 2D array/matrix - javascript

Please find my current implementation below:
function findMaxSumSubMatrix(matrix) {
var dim = matrix[0].length;
// initialize prefix sum matrix
var ps = new Array();
for (var _ = 0; _ < dim; _++) {
ps[_] = new Array();
}
// calculate vertical prefix sum matrix
for (var i = 0; i < dim; i++) {
for (var j = 0; j < dim; j++) {
if (j == 0) {
ps[j][i] = matrix[j][i];
} else {
ps[j][i] = matrix[j][i] + ps[j - 1][i];
}
}
}
// console.log(ps); // log prefix sum matrix
var maxSum = 0;
var min, temp;
// using the prefix sum matrix, iterate over all combinations and keep track of the max (Kadane's algorithm)
for (var i = 0; i < dim; i++) {
for (var j = i; j < dim; j++) {
min = 0;
temp = 0;
for (var k = 0; k < dim; k++) {
if (i == 0) {
temp += ps[j][k];
} else {
temp += ps[j][k] - ps[i - 1][k];
}
if (temp < min) {
min = temp;
}
if (temp - min > maxSum) {
maxSum = temp - min;
}
}
}
}
return maxSum;
}
var example1 = [
[1, -61, 5126, 612, 6],
[41, 6, 7, 2, -7],
[1, 73, -62, 678, 1],
[7, -616136, 61, -83, 724],
[-151, 6247, 872, 2517, 8135],
];
console.log(findMaxSumSubMatrix(example1)); // expected output: 18589
This works as expected, the output is correct.
However, I didn't write the code myself entirely.
What is unclear to me is the "min" and this part:
if (temp < min) {
min = temp;
}
if (temp - min > maxSum) {
maxSum = temp - min;
}
Can someone explain to me what's happening there, and why it's needed? I tried omitting it, giving incorrect results.
Thank you.

Think of this as a simple 1D array, where you have to find the maximum contiguous subsequence sum (exactly what Kadane's Algorithm does). For each prefix sum, you'll consider the lowest prefix sum that precedes it and calculate the difference (picking the lowest because you need to maximise the difference).
Similarly, the 2D array here also stores the prefix sum. We use min to keep a track of the lowest sum encountered in the current column. Since we need the maximum sum, we try to maximise the difference between current prefix sum (that is temp) and the minimum sum encountered (that is min).

Related

JavaScript algorithm question: get the find the contiguous subarray which has the largest sum from an array

The question originates from this leetcode question: https://leetcode.com/problems/maximum-subarray/
But instead of returning the largest sum, I want to return the subarray that has the largest sum. For example, [-2,1,-3,4,-1,2,1,-5,4], the largest sum is 6 as in [4,-1,2,1] . Here I want to return [4,-1,2,1] not 6 the number.
Here is my attempt:
var maxSubArray = function(nums) {
let max = -Infinity
let sum = 0
const results = []
for(const num of nums) {
results.push(num)
sum += num
max = Math.max(sum, max)
if(sum < 0) {
sum = 0
results.length = 0
}
}
return results
};
maxSubArray([-2,1,-3,4,-1,2,1,-5,4])
However it returns an incorrect answer - [ 4, -1, 2, 1, -5, 4 ]. I found it really hard to implement this since it is hard to determine whether or not we should keep adding the subsequent item in the results array.
Wondering if anyone would like to give it a try.
In this tutorial, by using Kadane’s algorithm and maintain indices whenever we get the maximum sum.
var maxSubArray = function(nums) {
var max_so_far = 0, max_ending_here = 0;
var startIndex = -1;
var endIndex = -1;
for(var i = 0; i < nums.length; i++) {
if (nums[i] > max_ending_here + nums[i]) {
startIndex = i;
max_ending_here = nums[i];
} else
max_ending_here = max_ending_here + nums[i];
if (max_so_far < max_ending_here) {
max_so_far = max_ending_here;
endIndex = i;
}
}
return nums.slice(startIndex, endIndex + 1);
};
console.log(maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))
Thanks baeldung's blog.
This page shows how to maintain indices whenever we get the maximum sum.
No JS, so I copy Java code here:
static void maxSubArraySum(int a[], int size)
{
int max_so_far = Integer.MIN_VALUE,
max_ending_here = 0,start = 0,
end = 0, s = 0;
for (int i = 0; i < size; i++)
{
max_ending_here += a[i];
if (max_so_far < max_ending_here)
{
max_so_far = max_ending_here;
start = s;
end = i;
}
if (max_ending_here < 0)
{
max_ending_here = 0;
s = i + 1;
}
}
System.out.println("Maximum contiguous sum is "
+ max_so_far);
System.out.println("Starting index " + start);
System.out.println("Ending index " + end);
}

Getting values higher than average in Array - JS

I'm trying to get all the numbers that are higher than the average of a given Array.
(this goes into an HTML page so it's with document.write
this is what I wrote:
sumAndBigger(arrayMaker());
function sumAndBigger(array) {
for (i = 0; i < array.length; i++) {
sum += array;
}
var equalAndBigger = []
var avg = sum / array.length;
for (i = 0; i < array.length; i++) {
if (array[i] > avg) {
equalAndBigger.push(array[i])
}
}
document.write('The numbers are: ' + equalAndBigger)
}
function arrayMaker() {
var array = [];
for (i = 0; i < 5; i++) {
var entrie = +prompt('Enter a number: ');
array.push(entrie)
}
return array;
}
This doesn't seem to work.. what am I doing wrong here?
Thanks in advance!
Ok so here I am giving you a one-liner code to get all the elements from the array that are "strictly greater than" the average value
let array = [1, 2, 3, 4, 5]
let allNums = array.filter(v => v > array.reduce((x, y) => x + y) / array.length);
Explanation
array.reduce((x, y) => x + y) → sum of all elements in the array
array.reduce((x, y) => x + y) / array.length → getting the average
Output
[4, 5]
MORE DETAILED CODE
function getAverage(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum / arr.length;
}
function getGreaterThanAverage(arr) {
let avg = getAverage(arr);
let numbers = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] > avg) {
numbers.push(arr[i]);
}
}
return numbers;
}

Simple Feedforward Neural Network in JavaScript

I'm new to this site, so I apologize in advance if I'm doing anything wrong in this post.
I'm currently trying out machine learning, and I'm learning neural networks. I'm currently using http://neuralnetworksanddeeplearning.com/. However, I don't fully understand everything, and all of the code is written in Python (I'm more comfortable with JavaScript).
I've created a program that works for simple data. However, for more complicated data (handwritten digits recognition with MNIST data), the accuracy rate isn't nearly as high as the website above says it will be, by using a neural network of 784 input neurons, 10-400 hidden neurons in the hidden layer (only one hidden layer and tried several possible number of neurons), and 10 output neurons with hundreds of iterations. I think that there is an error with my back propagation step (i.e. the train step, I'm including the other functions here as reference) that prevents it from learning fast enough (BTW, I'm using the cross-entropy as my cost function). I would really appreciate if anyone can help me find the error. Thanks in advance.
Below is the code. The weights are arranged in an array of arrays of arrays (weight[i][j][k] is the weight between the jth neurons in the ith layer and the kth neuron in the (i+1)th layer). Similarly, bias[i][j] is the bias of the (i+1)th layer for the jth neuron. The training data is formatted as an array of objects with keys of inputs and outputs (see example below).
class NeuralNetwork {
constructor(layers) {
// Check if layers is a valid argument
// Initialize neural network
if (!Array.isArray(layers) || layers.length < 2) {
throw Error("Layers must be specified as an array of length at least 2");
}
this.weights = [];
this.biases = [];
for (let i = 0, l = layers.length; i < l; ++i) {
let currentLayer = layers[i];
if (typeof currentLayer === "number" && Number.isInteger(currentLayer) && currentLayer > 0) {
let numWeights = layers[i + 1];
if (i < l - 1) {
this.weights.push([]);
}
if (i) {
this.biases.push([]);
}
// Seed weights and biases
for (let j = 0; j < currentLayer; ++j) {
if (i < l - 1) {
let weights = [];
for (let k = 0; k < numWeights; ++k) {
weights.push(Math.random() * 2 - 1);
}
this.weights[i].push(weights);
}
if (i) {
this.biases[i - 1].push(Math.random() * 2 - 1);
}
}
} else {
throw Error("Array used to specify NeuralNetwork layers must consist solely of positive integers");
}
}
this.activation = (x) => 1 / (1 + Math.exp(-x));
this.activationDerivative = (x) => this.activation(x) * (1 - this.activation(x));
Object.freeze(this);
console.log("Successfully initialized NeuralNetwork");
return this;
}
run(input, training) {
// Forward propagation
let currentInput;
if (training) {
currentInput = [input.map((a) => {return {before: a, after: a}})];
} else {
currentInput = [...input];
}
for (let i = 0, l = this.weights.length; i < l; ++i) {
let newInput = [];
for (let j = 0, m = this.weights[i][0].length, n = (training ? currentInput[i] : currentInput).length; j < m; ++j) {
let sum = this.biases[i][j];
for (let k = 0; k < n; ++k) {
sum += (training ? currentInput[i][k].after : currentInput[k]) * this.weights[i][k][j];
}
if (training) {
newInput.push({
before: sum,
after: this.activation(sum)
});
} else {
newInput.push(this.activation(sum));
}
}
if (training) {
currentInput.push(newInput);
} else {
currentInput = newInput;
}
}
return currentInput;
}
train(data, learningRate = 0.1, batch = 50, iterations = 10000) {
// Backward propagation
console.log("Initialized training");
let length = data.length,
totalCost = 0,
learningRateFunction = typeof learningRate === "function",
batchCount = 0,
weightChanges = [],
biasChanges = [];
for (let i = 0; i < iterations; ++i) {
let rate = learningRateFunction ? learningRate(i, totalCost) : learningRate;
totalCost = 0;
for (let j = 0, l = length; j < l; ++j) {
let currentData = data[j],
result = this.run(currentData.input, true),
outputLayer = result[result.length - 1],
outputLayerError = [],
errors = [];
for (let k = 0, m = outputLayer.length; k < m; ++k) {
let currentOutputNeuron = outputLayer[k];
outputLayerError.push(currentOutputNeuron.after - currentData.output[k]);
totalCost -= Math.log(currentOutputNeuron.after) * currentData.output[k] + Math.log(1 - currentOutputNeuron.after) * (1 - currentData.output[k]);
}
errors.push(outputLayerError);
for (let k = result.length - 1; k > 1; --k) {
let previousErrors = errors[0],
newErrors = [],
currentLayerWeights = this.weights[k - 1],
previousResult = result[k - 1];
for (let i = 0, n = currentLayerWeights.length; i < n; ++i) {
let sum = 0,
currentNeuronWeights = currentLayerWeights[i];
for (let j = 0, o = currentNeuronWeights.length; j < o; ++j) {
sum += currentNeuronWeights[j] * previousErrors[j];
}
newErrors.push(sum * this.activationDerivative(previousResult[i].before));
}
errors.unshift(newErrors);
}
for (let k = 0, n = this.biases.length; k < n; ++k) {
if (!weightChanges[k]) weightChanges[k] = [];
if (!biasChanges[k]) biasChanges[k] = [];
let currentLayerWeights = this.weights[k],
currentLayerBiases = this.biases[k],
currentLayerErrors = errors[k],
currentLayerResults = result[k],
currentLayerWeightChanges = weightChanges[k],
currentLayerBiasChanges = biasChanges[k];
for (let i = 0, o = currentLayerBiases.length; i < o; ++i) {
let change = rate * currentLayerErrors[i];
for (let j = 0, p = currentLayerWeights.length; j < p; ++j) {
if (!currentLayerWeightChanges[j]) currentLayerWeightChanges[j] = [];
currentLayerWeightChanges[j][i] = (currentLayerWeightChanges[j][i] || 0) - change * currentLayerResults[j].after;
}
currentLayerBiasChanges[i] = (currentLayerBiasChanges[i] || 0) - change;
}
}
++batchCount;
if (batchCount % batch === 0 || i === iterations - 1 && j === l - 1) {
for (let k = 0, n = this.weights.length; k < n; ++k) {
let currentLayerWeights = this.weights[k],
currentLayerBiases = this.biases[k],
currentLayerWeightChanges = weightChanges[k],
currentLayerBiasChanges = biasChanges[k];
for (let i = 0, o = currentLayerWeights.length; i < o; ++i) {
let currentNeuronWeights = currentLayerWeights[i],
currentNeuronWeightChanges = currentLayerWeightChanges[i];
for (let j = 0, p = currentNeuronWeights.length; j < p; ++j) {
currentNeuronWeights[j] += currentNeuronWeightChanges[j] / batch;
}
currentLayerBiases[i] += currentLayerBiasChanges[i] / batch;
}
}
weightChanges = [];
biasChanges = [];
}
}
totalCost /= length;
}
console.log(`Training ended due to iterations reached\nIterations: ${iterations} times\nTime spent: ${(new Date).getTime() - startTime} ms`);
return this;
}
}
Example
Tests if a point is inside a circle. For this example, the neural network performs well. However, for more complicated examples such as handwriting recognition, the neural network performs really badly (best I can get for a single neural network is 70% accuracy, compared to the 96% accuracy stated in the website even when using similar parameters).
let trainingData = [];
for (let i = 0; i < 1000; ++i) {
let [x, y] = [Math.random(), Math.random()];
trainingData.push({input: [x, y], output: [Number(Math.hypot(x,y) < 1)]});
}
let brain = new NeuralNetwork([2, 5, 5, 1]);
brain.train(trainingData.slice(0,700), 0.1, 10, 500); // Accuracy rate 95.33% on the remaining 300 entries in trainingData
Ok, I guess I'm going to answer my own question. So, I don't think there is an error in my code and it's perfectly fine to use (albeit really, really inefficient) if anyone wants to.
The reason why my runs on the MNIST data did not give accurate answers come from the fact that I did not process the data at first. The raw data gave the darkness of the 28*28 pixels in the range of [0, 255], which I used directly as the input for each of the training data. The correct procedure here would be to convert this into the range of [0, 1] or [-1, 1].
The reason that the [0, 255] range does not work as well is due to the fact that the second hidden layer of neurons will receive really positive or negative inputs.
When the backpropagation algorithm computes the gradient, the change computed for each weight will be really small as it is proportional to the slope of the activation function at the input to the neuron (the derivative of the logistic function is exp(-x)/(1+exp(-x)), which is close to 0 for really positive and negative values of x). Thus, the neural network will take really long to train and, in my case, was not able to learn the data well.
With the correct method, I am able to achieve around 90% accuracy for a 784*200*10 neural network in a fairly short time, though it still is not nearly as accurate as what the author says he is able to achieve using an even simpler algorinthm in the link mentioned in the question.

Array Helpers javascript exercise

I am trying to solve an exercise about array helpers in Javascript, this is my code.
var numbers = [1, 2, 3, 4, 5];
function square() {
var arraySquare = [];
for (i = 0; i < numbers.length; i++) {
arraySquare[i] = numbers[i] * numbers[i];
arraySquare.push(arraySquare[i]);
}
return arraySquare;
}
console.log(square());
function cube() {
var arrayCube = [];
for (i = 0; i < numbers.length; i++) {
arrayCube[i] = numbers[i] * numbers[i] * numbers[i];
arrayCube.push(arrayCube[i]);
}
return arrayCube;
}
console.log(cube());
function arrayAverage() {
var sum = 0;
var average = 0;
if (numbers === []) {
return NaN;
}
else {
for (i = 0; i < numbers.length; i++) {
sum = sum + numbers[i];
}
average = sum / i;
}
return average;
}
console.log(arrayAverage());
function arraySum() {
var sum = 0;
for (i = 0; i < numbers.length; i++) {
sum = sum + numbers[i];
}
return sum;
}
console.log(arraySum());
function even() {
var arrayEven = [];
for (i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
arrayEven.push(numbers[i]);
}
}
return arrayEven;
}
console.log(even());
function odd() {
var arrayOdd = [];
for (i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 !== 0) {
arrayOdd.push(numbers[i]);
}
}
return arrayOdd;
}
console.log(odd());
For some reason, the square() and cube() function, push the last element in the new arrays twice. Do you have any idea why this could happen?
Aside from this, the code seems to work just fine. If you notice any other problem in the code please mention it!
Any help will be really appreciated!
Because you are setting the ith element, and after that you are pushing a new value onto the array:
arrayCube[i] = numbers[i] * numbers[i] * numbers[i];
arrayCube.push(arrayCube[i]);
You should probably just do:
arrayCube.push(numbers[i] * numbers[i] * numbers[i]);
The problem lies here
arraySquare[i] = numbers[i] * numbers[i];
arraySquare.push(arraySquare[i]);
You are updating the array two times, your function doesn't just add an extra final number, but it adds two numbers, one at i and one at i+1 every time, the one at i+1 get overwritten the next iteration that's why only the final one stays.
you should just keep the first line
Checked for Square function. It worked for me.
var numbers = [1, 2, 3, 4, 5];
function square() {
var arraySquare = []; var a ;
for (i = 0; i < numbers.length; i++) {
a = numbers[i] * numbers[i];
arraySquare.push(a);
}
return arraySquare;
}
console.log(square());
Hope this works for both functions.
Regards,
Eby J

least common multiple: What is wrong with my code?

function lcm(arr) {
arr = arr.sort(function(a, b) {
return a - b;
});
var j = 1;
var num = arr[0];
for (i = 1; i < arr.length; i++) {
while (num % arr[i] !== 0) {
j = j + 1;
num = j * arr[0];
}
arr[0] = num;
}
return num;
}
console.log(lcm([3, 5, 6, 10]));
I am trying to find the least common multiple for a range of numbers in an array. The code works fine for array with two items, however the output for arrays with more than two items seems to exceed the value expected.
Can anyone help me find the bug in my code ?
Thank you
Set j back to 1 each time through the loop through the array elements. Otherwise, when you process the next number, you start with a high multiplier.
// function that find the least common multiple
function lcm(arr) {
arr = arr.sort(function(a, b) {
return a - b;
});
var num = arr[0];
for (i = 1; i < arr.length; i++) {
var j = 1;
while (num % arr[i] !== 0) {
j = j + 1;
num = j * arr[0];
}
arr[0] = num;
}
return num;
}
console.log(lcm([3, 5, 6, 10]));

Categories

Resources