Optimization (CodeWars Integers: Recreation One) - javascript

I managed to find two algos for this CodeWars challenge. Unfortunately, they are not fast enough (> 12000ms).
Any suggestions on how to improve my code?
v1 :
const listSquared = (m, n) => {
const result = [];
for (let i = m; i <= n; i++) {
const divisorsOfi = [];
for (let j = 0; j <= i; j++) {
if (i % j === 0) {
divisorsOfi.push(Math.pow(j, 2))
}
}
let sumOfDivisorsOfi = 1;
if (divisorsOfi.length > 1) {
sumOfDivisorsOfi = divisorsOfi.reduce((a, b) => a + b);
}
if (Number.isInteger(Math.sqrt(sumOfDivisorsOfi))) {
result.push([i, sumOfDivisorsOfi]);
}
}
return result;
}
v2:
const listSquared = (m, n) => {
const result = [];
for (let i = m; i <= n; i++) {
let sumOfSqrtDivisorsOfi = divisors(i);
if (Number.isInteger(Math.sqrt(sumOfSqrtDivisorsOfi))) {
result.push([i, sumOfSqrtDivisorsOfi]);
}
}
return result;
}
const divisors = (n) => [...Array(n + 1).keys()].slice(1)
.reduce((s, a) => s + (!(n % (a)) && Math.pow(a, 2)), 0);

I used v1 as a basis and modified it in the following way
const listSquared = (m, n) => {
const result = [];
for (let i = m; i <= n; i++) {
const divisorsOfi = [];
for (let j = 0; j <= Math.sqrt(i); j++) {
if (i % j === 0) {
divisorsOfi.push(Math.pow(j, 2));
if (i/j != j)
divisorsOfi.push(Math.pow(i/j, 2));
}
}
let sumOfDivisorsOfi = 1;
if (divisorsOfi.length > 1) {
sumOfDivisorsOfi = divisorsOfi.reduce((a, b) => a + b);
}
if (Number.isInteger(Math.sqrt(sumOfDivisorsOfi))) {
result.push([i, sumOfDivisorsOfi]);
}
}
return result;
}
The idea here is quite simple: if a could be divided by j then it's also could be divided by a/j so we need to check only sqrt(a) first numbers. The only thing you need to be careful is when you count result twice in case a/j and j is the same number.

Related

Storing instances of an array using the slice method in javascript

I have an array list I loop over and modify it each time. I want to store all instances of my list array in an other array I named allLists, and to do so, I'm using the slice method.
It seems to work in the simple example below:
let list=[1,2,3,4];
let allList = [];
allList.push(list.slice());
list[2]=6;
allList.push(list.slice());
console.log(allList);// returns [1,2,3,4] [1,2,6,4]
But it doesn't work in the following code. Instead, allLists is filled with the last instance of the list array.
let list = Array.from({
length: 9
}, () => Array.from({
length: 9
}, () => [1, 2, 3, 4, 5, 6, 7, 8, 9]));
let allLists = [list.slice()];
let indexList = [];
let lengthList = [];
let key = true;
function handleNewIndex(list) {
let newIndex = [0, 0];
let maxLength = 9;
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < list.length; j++) {
if (list[i][j].length < maxLength && list[i][j].length > 0) {
maxLength = list[i][j].length;
newIndex = [i, j];
}
}
}
return newIndex;
}
function isSudokuValid(list) {
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < list.length; j++) {
if (list[i][j].length === 0) {
return false;
}
}
}
return true;
}
function handleWrongSudoku(allLists, indexList, lengthList) {
let counter = 1;
while (lengthList[lengthList.length - counter] <= 1) {
counter = counter + 1;
allLists.pop();
indexList.pop();
}
let wrongList = allLists.pop();
list = allLists.pop();
indexLine = indexList[indexList.length - 1][0];
indexColumn = indexList[indexList.length - 1][1];
let wrongNumber = wrongList[indexLine][indexColumn];
for (let i = 0; i < list[indexLine][indexColumn].length; i++) {
if (list[indexLine][indexColumn][i] != wrongNumber) {
list[indexLine][indexColumn] = list[indexLine][indexColumn][i];
}
}
allLists.push(list.slice());
indexLine = handleNewIndex(list)[0];
indexColumn = handleNewIndex(list)[1];
}
function generateSudoku() {
let indexLine = Math.floor(Math.random() * 9);
let indexColumn = Math.floor(Math.random() * 9);
let counter = 0;
while (counter < 81) {
indexList.push([indexLine, indexColumn]);
let bigSquareIndex = 3 * Math.floor(indexLine / 3) + Math.floor(indexColumn / 3);
lengthList.push(list[indexLine][indexColumn].length);
list[indexLine][indexColumn] = list[indexLine][indexColumn][Math.floor(Math.random() * list[indexLine][indexColumn].length)];
counter = counter + 1;
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (3 * Math.floor(i / 3) + Math.floor(j / 3) === bigSquareIndex) {
let k = 0;
let n = list[i][j].length;
while (list[i][j][k] != list[indexLine][indexColumn] && k < n) {
k = k + 1;
}
if (k < n) {
list[i][j].splice(k, 1);
}
} else if (i === indexLine || j === indexColumn) {
let k = 0;
let n = list[i][j].length;
while (list[i][j][k] != list[indexLine][indexColumn] && k < n) {
k = k + 1;
}
if (k < n) {
list[i][j].splice(k, 1);
}
}
}
}
allLists.push(list.slice());
key = isSudokuValid(list);
if (key === false) { //ignore this scenario, not done yet, assume key = true at all time
console.log(key, lengthList, indexList, allLists);
handleWrongSudoku(allLists, indexList, lengthList);
key = true;
//return;
} else {
indexLine = handleNewIndex(list)[0];
indexColumn = handleNewIndex(list)[1];
}
}
}
generateSudoku();
console.log(allLists); // returns 81 times the same 9x9 array instead of the 81 different instances of the list array
I don't know what I'm doing wrong here.
Thanks for the hint Code Maniac.
I used the JSON method instead to create a deep copy and it's working fine now.
allLists.push(JSON.parse(JSON.stringify(list)));
This post explains the difference between shallow and deep copy:
https://www.freecodecamp.org/news/how-to-clone-an-array-in-javascript-1d3183468f6a/

I would like to fill an array with numbers using javascript with a for loop

"But I always double the numbers. so [1,1,2,2,3,3 .......] how can you do that? I am looking forward to the answer. "
Continuous with a number, the for loop is good. But how do I do it with double numbers?
To get an array to N, use:
let N = 10;
Array.from(Array(N).keys())
To double each value in any array:
[...yourArray.map(n => [n, n])].flat()
So, your solution:
let n = 10;
const a = [...Array.from(Array(n).keys()).map(k => [k, k])].flat()
console.log(a)
To have it starting from 0, not 1, alter the mapping accordingly:
let n = 10;
const a = [
...Array.from(Array(n).keys())
.map(k => [k + 1, k + 1])
].flat()
console.log(a)
Arguably, the cleanest solution:
function doubleArrayTo(n) {
const a = [];
for (i = 1; i <= n; i++) {
a.push(i, i);
}
return a;
}
console.log(doubleArrayTo(10))
Out of curiosity, I tested their performance. Unsurprisingly, the for loop wins hands down over the spread syntax:
function doubleFor(n) {
const a = [];
for (i = 1; i <= n; i++) {
a.push(i, i);
}
return a;
}
function doubleSpread(n) {
return [
...Array.from(Array(n).keys())
.map(k => [k + 1, k + 1])
].flat()
}
function run_test(fn, value) {
const t0 = performance.now();
fn(value);
return performance.now() - t0;
}
[1e4, 1e5, 1e6, 1e7].map(value => {
console.log(
`(${value}): for => ${
run_test(doubleFor, value)
} | spread => ${
run_test(doubleSpread, value)
}`);
});
follow this code
var array = [];
for(int i = 0;i <= 10;i++) {
array.push(i);
array.push(i);//again
}
var array = [];
for (let i = 0; i <= 10; i++) {
array.push(i,i);
}
console.log(array);
Edit
You can use multi input for array.push(i,i,i,....)

how can I better run my code in javascript?

The exercise is as follows, given an array with multiple arrays inside, return the sum of the product subtraction with the LMC. I managed to create the code, but is passing 12000ms of response and wanted to optimize my code, I still have difficulty with it, can you help me? Follow the code below.
The expected result is 840.
let pairs = [[15,18], [4,5], [12,60]];
function sumDifferencesBetweenProductsAndLCMs (pairs)
{
let product = [];
let prodResult = 1;
let LMC = [];
let result = 0;
// take a product
for(let i = 0; i < pairs.length;i++)
{
for(let j = 0; j < pairs[i].length;j++)
{
prodResult *= pairs[i][j]
}
product.push(prodResult);
if(prodResult != 0)
{
prodResult = 1
}
}
// take a LMC
for(let i = 0; i < pairs.length;i++)
{
let m = pairs[i][0];
let n = pairs[i][1];
let a = pairs[i][0];
let b = pairs[i][1];
let mmc = 0;
let r = 0
do
{
r = m%n;
m=n;
n=r;
} while (r != 0);
mmc = (a * b)/m
LMC.push(mmc)
}
for(let i = 0; i < product.length; i++){
result += product[i]-LMC[i]
}
return result
}
console.log(sumDifferencesBetweenProductsAndLCMs(pairs));
You can do all calculation in single loop only which will reduce your run time complexity.
let pairs = [[15,18], [4,5], [12,60]];
function sumDifferencesBetweenProductsAndLCMs (pairs) {
let result = 0;
// take a product
for(let i = 0; i < pairs.length;i++) {
let prodResult = 1;
for(let j = 0; j < pairs[i].length;j++) {
prodResult *= pairs[i][j]
}
const lcmResult = lcm(pairs[i][0], pairs[i][1]);
result += prodResult - lcmResult;
}
return result
}
function lcm(a, b) {
return (a / gcd(a, b)) * b;
}
function gcd(a, b) {
if (a === 0) {
return b;
}
return gcd(b % a, a);
}
console.log(sumDifferencesBetweenProductsAndLCMs(pairs));
.as-console-wrapper {
top: 0;
}

Strange behavior of the neutral network written in plain JS

Impressed by the possibilities of neural networks, I've decided that before using any library I want to understand how they work. So I wrote a simple training app, which used 3 layer network with 2 neurons each. There was a canvas 400x400. Given the coordinates of x,y of the mouse over the canvas <0;399> it was supposed to give as the result coordinate/400 <0;1> (So for 100,300 it is supposed to give 0.25,0.75).
The training looked reasonable.
But when I switch to the prediction mode the network gives the same result all the time for each training batch. It gives the same results no matter what the input is.
Then after more training the output changes, but it’s still the same for each input.
It's written in TypeScript.
Instead of pasting the whole web training page I just made the training script so you can see more clearly what's going on.
index.ts
let sigmoid: ActivationFunction = {
func: (x: number) => (1 / (1 + Math.exp(-x))),
derivative: (z: number) => {
return sigmoid.func(z) * (1 - sigmoid.func(z));
}
};
import Matrix from './matrix';
class NeutralNetwork {
layers: Array<number>;
weights: Matrix[];
biases: Matrix[];
activation_function: ActivationFunction;
learning_rate: number;
constructor(...layers: Array<number>) {
this.layers = layers;
this.activation_function = sigmoid;
//Initialize neural network with random weigths and biases [-1;1]
this.weights = [];
for(let i=0; i<this.layers.length - 1; i++){
this.weights.push(new Matrix(this.layers[i+1], this.layers[i]));
this.weights[i].randomize();
}
this.biases = [];
for(let i=1; i<this.layers.length; i++){
this.biases.push(new Matrix(this.layers[i], 1));
this.biases[i-1].randomize();
}
this.setActivationFunction();
this.setLearningRate();
}
feedForward(originalInput: Array<number>): Array<number> {
if(originalInput.length != this.layers[0]) throw new Error("corrupt input data");
let input : Matrix = Matrix.createFromArray(originalInput);
for(let i = 0; i < this.layers.length - 1; i++){
let output = Matrix.multiply(this.weights[i], input);
output.add(this.biases[i]);
output.map(this.activation_function.func);
input = output;
}
return input.toArray();
}
train(originalInput: Array<number>, originalTarget: Array<number>) {
if(originalInput.length != this.layers[0]) throw new Error("corrupt training data");
if(originalTarget.length != this.layers[this.layers.length - 1]) throw new Error("corrupt training data");
let outputs : Matrix[] = [];
let input : Matrix = Matrix.createFromArray(originalInput);
for(let i = 0; i < this.layers.length - 1; i++){
let output = Matrix.multiply(this.weights[i], input);
output.add(this.biases[i]);
output.map(this.activation_function.func);
input = output;
outputs.push(output);
}
let target = Matrix.createFromArray(originalTarget);
let errors = Matrix.subtract(target, outputs[this.layers.length - 2]);
for(let i = this.layers.length - 2; i>=0; i--){
let gradients = Matrix.map(outputs[i], this.activation_function.derivative);
gradients.multiply(errors);
gradients.multiply(this.learning_rate);
let outputsOfLayerBeforeTransposed = Matrix.transpose(i > 0 ? outputs[i-1] : Matrix.createFromArray(originalInput));
let deltas = Matrix.multiply(gradients, outputsOfLayerBeforeTransposed);
this.weights[i].add(deltas);
this.biases[i].add(gradients);
let weightsTransposed = Matrix.transpose(this.weights[i]);
errors = Matrix.multiply(weightsTransposed, errors);
}
return outputs[outputs.length - 1].toArray();
}
setActivationFunction(activationFunction = sigmoid) {
this.activation_function = activationFunction;
}
setLearningRate(learning_rate = 0.1) {
this.learning_rate = learning_rate;
}
}
interface ActivationFunction {
func(x: number): number;
derivative(x: number): number;
}
export = NeutralNetwork;
training.ts
let NN = require('./index');
let n = new NN(2,2,2);
let data = generateTrainingData();
data.forEach(d => n.train(d.i, d.o));
//check how well is it trained
let index = 0
let t = setInterval(()=>{
let pred = n.feedForward(data[index].i);
console.log(`PREDICTED - ${pred} EXPECTED = ${data[index].o} COST - ${Math.pow(pred[0]-data[index].o[0],2)+Math.pow(pred[1]-data[index].o[1],2)}`)
if(index++ == 1000) clearInterval(t);
}, 500);
function generateTrainingData(){
let data = [];
for(let i=0;i<1000;i++){
let x = Math.floor(Math.random() * 400);
let y = Math.floor(Math.random() * 400);
data.push({
i : [x,y],
o : [x/400, y/400]
})
}
return data;
}
matrix.ts
export default class Matrix {
rows;
columns;
data: Array<Array<number>>;
constructor(rows, columns) {
this.rows = rows;
this.columns = columns;
this.data = new Array(this.rows).fill().map(() => Array(this.columns).fill(0));
}
static map(matrix, f) : Matrix{
let m = new Matrix(matrix.rows, matrix.columns);
m.map((v,i,j) => f(matrix.data[i][j], i, j));
return m;
}
map(f) {
for (let i = 0; i < this.rows; i++) {
for (let j = 0; j < this.columns; j++) {
this.data[i][j] = f(this.data[i][j], i, j);
}
}
}
randomize() {
this.map(() => Math.random() * 2 - 1);
}
add(n) {
if (n instanceof Matrix) {
if (this.rows !== n.rows || this.columns !== n.columns) {
throw new Error('Size of both matrices must match!');
}
return this.map((v, i, j) => v + n.data[i][j]);
} else {
return this.map(v => v + n);
}
}
static subtract(a, b) : Matrix{
if (a.rows !== b.rows || a.columns !== b.columns) {
throw new Error('Size of both matrices must match!');
}
let m = new Matrix(a.rows, a.columns);
m.map((_, i, j) => a.data[i][j] - b.data[i][j]);
return m;
}
static multiply(a, b) {
if (a.columns !== b.rows) {
throw new Error('a.columns !== b.rows');
}
let m = new Matrix(a.rows, b.columns)
m.map((_, i, j) => {
let sum = 0;
for (let k = 0; k < a.cols; k++) {
sum += a.data[i][k] * b.data[k][j];
}
return sum;
});
return m;
}
multiply(n) {
if (n instanceof Matrix) {
if (this.rows !== n.rows || this.columns !== n.columns) {
throw new Error('Size of both matrices must match!');
}
return this.map((v, i, j) => v * n.data[i][j]);
} else {
return this.map(v => v * n);
}
}
toArray() {
let arr = [];
for (let i = 0; i < this.rows; i++) {
for (let j = 0; j < this.columns; j++) {
arr.push(this.data[i][j]);
}
}
return arr;
}
static transpose(matrix) : Matrix {
let m = new Matrix(matrix.columns, matrix.rows)
m.map((_, i, j) => matrix.data[j][i]);
return m;
}
static createFromArray(arr): Matrix {
let m = new Matrix(arr.length, 1);
m.map((v, i) => arr[i]);
return m;
}
}
I'm not really sure what the cause of that. I've been trying to debug this for days now, but I think my lack of experience doesn't let me see the issue here. Thank you so much for all of your help.
There is a mistake in Matrix.multiply class method. It should be a.columns rather than a.cols. Because of this, gradients and deltas are not updating properly.

Combinations of n words from array of m words - Javascript

Given arr = ['mat','cat','fat']
A function getComb(arr, n = 2) where n is the number of words each combination must have.
Expected results:
mat cat
mat fat
cat fat
I could not modify the code below any further to get the desired results. Any idea? thx
Thanks to Knskan3:
'getCombinations': (arr, n) => {
let i, j, k, elem, l = arr.length, childperm, ret = [];
if (n === 1) {
for (let i = 0; i < arr.length; i++) {
ret.push([arr[i]]);
}
return ret;
}
else {
for (i = 0; i < l; i++) {
elem = arr.shift();
for (j = 0; j < elem.length; j++) {
childperm = lib.getCombinations(arr.slice(), n - 1);
for (k = 0; k < childperm.length; k++) {
ret.push([elem[j]].concat(childperm[k]));
}
}
}
return ret;
}
},
I suggest a space-efficient generator function:
// Generate all k-combinations of first n array elements:
function* combinations(array, k, n = array.length) {
if (k < 1) {
yield [];
} else {
for (let i = --k; i < n; i++) {
for (let combination of combinations(array, k, i)) {
combination.push(array[i]);
yield combination;
}
}
}
}
// Example:
console.log(...combinations(['mat', 'cat', 'fat'], 2));

Categories