This is a react minesweeper and it involves setState when the user wants to choose a new grid size. When the user clicks the grid should be recreated, but there are some reference problems thanks to the prev state.
When I first console.log(array); it is not empty thanks to the prev state. When I console.log(array[m][n]['value']) it says all the values are 0, but when I console.log(array); right after it the array contains 'b' values which are set from the addBombsmethod which is invoked after the createMap.
What should I do?
createMap (sizeX, sizeY, bombNumber) {
let array = [];
while (array.length > 0) {
array.pop();
}
array.length = 0;
console.log(array); // not totally empty, but it says length 0.
for (var m = 0; m < sizeY; m++) {
array[m] = new Array();
for (var n = 0; n < sizeX; n++) {
array[m][n] = { value: 0, state: 'closed' };
console.log(array[m][n]['value']); // Prints 0 for all of them.
}
}
console.log(array); // There are 'b' values in the array.
return array.slice();
}
createTable(props) {
const sizeX = props.sizeX;
const sizeY = props.sizeY;
const bombNumber = props.bombNumber;
const map = this.createMap(sizeX, sizeY, bombNumber);
const newMap = map.slice();
const newestMap = this.addBombs(newMap, sizeX, sizeY, bombNumber);
const newestestMap = newestMap.slice();
const mMap = this.calculateFieldValues(newestestMap);
const nMap = mMap.slice();
return nMap;
}
addBombs (map, sizeX, sizeY, bombNumber) {
let bombFields = [];
while (bombFields.length < bombNumber) {
const randomNumber = Math.floor(Math.random() * (sizeY * sizeX - 1))
if (!bombFields.includes(randomNumber)) {
bombFields.push(randomNumber);
}
}
bombFields.forEach((number) => {
const i = Math.floor(number / sizeX);
let j = (number % sizeX);
if (j === -1) {
j = sizeX - 1;
}
map[i][j]['value'] = 'b';
});
return map;
}
Related
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;
}
My merge sort visualizer draws the array but does not show them being sorted and doesnt seem to be correctly merging them and displaying each step. instead of returning a sorted array, I see a quick flash of the array then a blank screen.
values is the array and states is supposed to change in value from -1 to 1 based on whether an index is currently in progress, waiting, or completed but I'm trying to get the sort and merge working first.
//array of values to be sorted
let values = [];
//width of values
let w = 2;
//hold the state of the array
let states = [];
function setup() {
createCanvas(windowWidth, windowHeight);
values = new Array(floor(random(250, width / w)));
for (i = 0; i < values.length; i++) {
values[i] = random(height);
states[i] = -1;
}
}
async function mergeSort(arr) {
if (arr.length <= 1) {
return arr;
}
//get midpoint
let mid = Math.round(arr.length / 2);
states[mid] = -1;
//split the array
temp1 = arr.slice(0, mid);
temp2 = arr.slice(mid, arr.length);
//merge the array
await Promise.all([
merge(mergeSort(temp1), mergeSort(temp2))
]);
}
async function merge(arr1, arr2) {
await sleep(25);
let sorted = [];
while (arr1.length > 0 && arr2.length > 0) {
(arr1[0] < arr2[0]) ? sorted.push(arr1.shift()):
sorted.push(arr2.shift());
}
while (arr1.length > 0) {
sorted.push(arr1.shift());
}
while (arr2.length > 0) {
sorted.push(arr2.shift());
}
values = sorted.slice();
}
function draw() {
background(0);
mergeSort(values);
for (let i = 0; i < values.length; i++) {
noStroke();
if (states[i] == 0) {
fill('#38e332');
} else if (states[i] == 1) {
fill('#c9c8c7');
} else {
fill(255);
}
//draw the array values at location x=i*w , y=height-array[i] with given width (w) and height(array[i])
rect(i * w, height - values[i], w, values[i]);
}
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
'
I am developing a Rubik cube app for fitbit versa and I run into the problem of removing duplicates from arrays as I get a NaN error when combining the arrays once the duplicates have been removed from the end of the list and it only happens when I splice at the end of the array and I cant figure out the reason why this isnt working
function getScramble(number_of_moves, faces, modifiers, scramble_faces, scramble_modifiers, scramble) {
for (var i = 0; i < number_of_moves; i++) {
var sample = faces[Math.floor(Math.random() * faces.length)];
var mod = modifiers[Math.floor(Math.random() * modifiers.length)];
scramble_faces[i] = sample;
scramble_modifiers[i] = mod;
if (scramble_faces[i] == scramble_faces[i - 1]) {
scramble_faces[i] = faces[Math.floor(Math.random() * faces.length)];
}
}
removeDuplicates(scramble_faces, scramble_modifiers)
for (var i = 0; i < number_of_moves - 2; i++) {
scramble[i] = scramble_faces[i] + scramble_modifiers[i]
}
console.log(scramble);
let demotext = document.getElementById("demotext");
demotext.text = scramble;
scramble = [];
scramble_faces = [];
scramble_modifiers = [];
}
function threebythree() {
var faces = ["U", "D", "L", "R", "F", "B"];
var modifiers = ["", "'", "2"];
var scramble_faces = [];
var scramble_modifiers = [];
var scramble = [];
var number_of_moves = 22;
let Title1 = document.getElementById("title");
Title1.text = "3x3"
getScramble(number_of_moves, faces, modifiers, scramble_faces, scramble_modifiers, scramble, Title1)
}
function removeDuplicates(arr, arr2, number_of_moves) {
var t = 0;
var new_arr = arr;
var new_arr2 = arr2;
for (var i = new_arr.length - 1; i >= 0; i--) {
if (new_arr[i] === new_arr[i - 1]) {
new_arr.splice(i, 1);
new_arr2.splice(i, 1);
}
}
arr = new_arr;
arr2 = new_arr2;
new_arr = [];
new_arr2 = [];
new_arr.pop();
new_arr2.pop();
console.log(arr);
console.log(arr2);
}
The lengths of scramble_faces and scramble_modifiers is initially number_of_moves. But after you remove duplicates from them, it can be shorter. But you still use number_of_moves in the limit in the next for loop. So when you try to add the elements that no longer exist you get undefined. undefined + undefined == NaN.
You should use the length of one of the arrays instead:
function getScramble(number_of_moves, faces, modifiers, scramble_faces, scramble_modifiers, scramble) {
for (var i = 0; i < number_of_moves; i++) {
var sample = faces[Math.floor(Math.random() * faces.length)];
var mod = modifiers[Math.floor(Math.random() * modifiers.length)];
scramble_faces[i] = sample;
scramble_modifiers[i] = mod;
if (scramble_faces[i] == scramble_faces[i - 1]) {
scramble_faces[i] = faces[Math.floor(Math.random() * faces.length)];
}
}
removeDuplicates(scramble_faces, scramble_modifiers)
for (var i = 0; i < scramble_faces.length - 2; i++) {
scramble[i] = scramble_faces[i] + scramble_modifiers[i]
}
console.log(scramble);
let demotext = document.getElementById("demotext");
demotext.text = scramble;
scramble = [];
scramble_faces = [];
scramble_modifiers = [];
}
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.
I want to get a random digit from 0-9 and have it popped so it doesn't get repeated but I find that after the the second number is pushed it doesn't have it's number popped. Instead, some other number not yet selected is popped giving room for a repeat.
var yourNum = [],
oppNum = [],
choose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function chooseRandomNumber() {
return choose[Math.floor(Math.random() * choose.length)];
}
for (var i = 0; i < 4; i++) {
if (i === 0) {
yourNum.push(chooseRandomNumber());
if (yourNum[yourNum.length - 1] === 9) {
choose.pop();
} else {
choose.splice(yourNum[0], 1);
}
} else if (i === 1) {
yourNum.push(chooseRandomNumber());
if (yourNum[yourNum.length - 1] === 9) {
choose.pop();
} else {
choose.splice(yourNum[1], 1);
}
} else if (i === 2) {
yourNum.push(chooseRandomNumber());
if (yourNum[yourNum.length - 1] === 9) {
choose.pop();
} else {
choose.splice(yourNum[2], 1);
}
} else if (i === 3) {
yourNum.push(chooseRandomNumber());
if (yourNum[yourNum.length - 1] === 9) {
choose.pop();
} else {
choose.splice(yourNum[3], 1);
}
}
}
console.log(choose);
console.log(yourNum);
function getRand(min, max, result) {
result = result || [];
if(result.length == 4) {
return result;
}
var rand = Math.floor(Math.random()*max) + min;
if(result.indexOf(rand) === -1) {
result.push(rand);
}
return getRand(min, max, result);
}
var result = getRand(1,9);
console.log(result);
Your whole approach is way to complicated and unperformant.
A better approach:
//first we need a shuffle function
function shuffle(array){
for(var i = array.length, j, tmp; i--; ){
j = 0|(Math.random() * i);
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
}
return array;
}
//now let's define a sequence of possible values
var numset = [0,1,2,3,4,5,6,7,8,9];
//shuffle the sequence and take the first 4 values
var fourRandomValues = shuffle(numset).slice(0,4);
console.log("four random values: " + fourRandomValues);
//doing this multiple times:
for(var values = []; values.length < 10;){
//shuffle again, and take the values that are now at the beginning of this sequence
values.push( shuffle(numset).slice(0,4) );
}
console.log("more random values: \n" + values.join("\n"));
Edit:
to address holi-java's approach by implementing sort of an Iterator, I'll add a way to do this with ES6 Iterators/Generators
Since Generators can be unlimited sequences we need to account for that. We do that by buffering a limited amount of values and returning them randomly; basically a shifiting frame of shuffled values.
function *shuffled(iterable, bufferSize = 256){
var buffer, numValues = 0, randomIndex;
if(Array.isArray(iterable) && iterable.length <= bufferSize){
//an optimization for (small) Arrays:
buffer = iterable.slice();
numValues = iterable.length;
}else{
buffer = Array( bufferSize )
for(var value of iterable){
//push value from the iterable to the buffer
buffer[numValues++] = value;
//buffer is full, yield a random value
if(numValues === bufferSize){
//choose a random value from the buffer
randomIndex = 0|(Math.random() * (numValues-1));
//yield it
yield buffer[randomIndex];
//overwrite the value with the last index
//that's cheaper than pop() and splice()
buffer[randomIndex] = buffer[--numValues];
}
}
}
//iterable doesn't provide any more values
//flush the buffer in a random order
while(numValues){
randomIndex = 0|(Math.random() * (numValues-1));
yield buffer[randomIndex];
buffer[randomIndex] = buffer[--numValues];
}
}
//every Array is a valid iterator
for(var v of shuffled([0,1,2,3,4,5,6,7,8,9]))
console.log(v);
That way we can shuffle a stream of values without first caching all the values in an array.
pro: memory efficient
possible problem: if the buffer's to small the result doesn't feel random anymore since values that are generated late in the sequence simply can not be shifted entirely to the start. You see some noise but it doesn't feel random anymore.
now let's take a jump into potentially infinite sequences:
// *shuffled again, for this snippet
function *shuffled(iterable, bufferSize = 256){
var buffer, numValues = 0, randomIndex;
if(Array.isArray(iterable) && iterable.length <= bufferSize){
buffer = iterable.slice();
numValues = iterable.length;
}else{
buffer = Array( bufferSize )
for(var value of iterable){
buffer[numValues++] = value;
if(numValues === bufferSize){
randomIndex = 0|(Math.random() * (numValues-1));
yield buffer[randomIndex];
buffer[randomIndex] = buffer[--numValues];
}
}
}
while(numValues){
randomIndex = 0|(Math.random() * (numValues-1));
yield buffer[randomIndex];
buffer[randomIndex] = buffer[--numValues];
}
}
//creates an infinite sequence of numbers
function *count(){
for(var index = 0; true;)
yield index++;
}
//like limits a iterator but for iterators
function *take(n, iterator){
for(var value of iterator){
if(n-- > 0) yield value;
else break;
}
}
//create an (infinite) counter and convert it into a generator of shuffled values
//with a bufferSize of 256 entries (play a bit with that value)
var shuffledSequence = shuffled(count(), 256);
//to convert that into an Array we take the first 1000 values generated from that generator
var array = [...take(1000, shuffledSequence)];
//and log it
console.log(array.toString());
what do you mean like this below:
function next() {
function all() {
return [].concat(Array(10).fill(null).map(function (_, index) {
return index;
}));
}
function random(start, end) {
return parseInt(Math.random() * (end + 1 - start)) + start;
}
var self = this;
self.all = self.all && self.all.length && self.all || all().sort(function(){
return random(-1, 1);
});
return self.all.shift();}
var results=Array(4).fill(null).map(next);
console.log(results);
my be this is better for browser to run.
var it = {
next: function () {
var self = this;
self._all = self._all && self._all.length && self._all || self.shuffle(self.all());
return self._all.shift();
},
all: function () {
return [0,1,2,3,4,5,6,7,8,9];
},
random: function (start, end) {
if (!end && end != 0) {
end = start;
start = 0;
}
return parseInt(Math.random() * (end + 1 - start)) + start;
},
shuffle: function (array) {
var self = this;
for (var i = array.length - 1; i > 0; --i) {
self.swap(array, i, self.random(i));
}
return array;
},
swap: function (array, i, j) {
var tmp = array[i];
array[i] = array[j];
array[j] = tmp;
},
reset: function () {
this._all = null;
}
};
for (var i = 0; i < 10; i++) {
var results=[];
for(var n=0;n<4;n++) results.push(it.next());
console.log("retain:"+it._all + '>>generated:' + results);
it.reset();
}
Edit
Takes four unique numbers out of the choose array. Is that what you're after?
var yourNum = [],
oppNum = [],
choose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function chooseRandomNumber() {
return choose[Math.floor(Math.random() * choose.length)];
}
var rand;
for (var i = 0; i < 4; i++) {
rand = chooseRandomNumber();
yourNum.push(rand);
choose.splice(choose.indexOf(rand), 1);
}
console.log(choose);
console.log(yourNum);
Old approach:
var start = 0, end = 9;
function generate(count) {
var nums = [],
random;
for (var i = 0; i < count; i++) {
while (!random || nums.indexOf(random) !== -1) {
random = Math.floor(Math.random() * (end + 1)) + start;
}
nums.push(random);
}
return nums;
}
console.log(
generate(4)
);
Demo:
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
var yourNums = [];
var choose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < 4; i++) {
yourNums = yourNums.concat(choose.splice(getRandomInt(0, choose.length), 1))
}
console.log("yourNums is:")
console.log(yourNums);
console.log("===")
console.log("choose is:")
console.log(choose);