Javascript. Two arrays, same size, drastically different performance. Why? - javascript

Method 1
const { performance } = require('perf_hooks');
performance.mark('A');
for (var i = 0; i < 100; i++) {
let x = new Array(1000);
x.fill(new Array(1000).fill(0));
}
performance.mark('B');
performance.measure('A to B', 'A', 'B');
const measure = performance.getEntriesByName('A to B')[0];
console.log(measure.duration); // 5.5ms
Method 2
performance.mark('C');
for (var i = 0; i < 100; i++) {
let x = new Array(1000000);
x.fill(0);
}
performance.mark('D');
performance.measure('C to D', 'C', 'D');
const measure2 = performance.getEntriesByName('C to D')[0];
console.log(measure2.duration); // 594ms
Method 1 is a million size array, but evenly distributed. To retrieve / store, i would treat this as content-addressable storage via some elementary hash to put and retrieve elements. 5.5ms for hundred million elements
Method 2 is a straight million size array. It is two orders of magnitude higher to initialize! 594ms for hundred million elements.
Can someone help explain whats going on here and shed some light on ideal array sizes / array configurations? I imagine this has to do with some kind of optimizations under the hood in v8/C++ land.

let x = new Array(1000);
x.fill(new Array(1000).fill(0));
That is two arrays. x is an array with 1000 references to one array that's filled with 1000 zeroes.
Total number of elements: 2000 (length of x is 1000 + the array it refers to has the length of 1000).
let x = new Array(1000000);
x.fill(0);
This is one array with million zeroes.
Total number of elements: 1000000.

Related

Creating an array that is consisted of unique numbers

I'm developing a simple game that allows user to generate from 1 to 5 Cat images from certain Cat Api. Then, after clicking start button the app generates shadow copies of those cats(with low opacity). Game will be later about dragging bottom images and fiting them to their shadow copies, that are randomly positioned(only then game makes sense). Then I'm planning make some futher features like time counter, points etc. etc. just for learning purposes.
But what am struggling with is creating a unique random number(that'll be index of particular cat) an will not be repeated during iteration...
Here is the code
const newArray = []; //
const catsArrayList = [...catBoardCopy.querySelectorAll('.cat')] //Array with cat images
function randomizeIndex() { // randomize an index number
let randomIndex = Math.floor((Math.random() * catsArrayList.length - 1) + 1);
console.log(randomIndex);
return randomIndex;
}
catsArrayList.forEach(catElement => { // here I am iterating over an array with cats which length is for example 5(this is max actually)
newArray.push(catsArrayList[randomizeIndex()]); // and pushing those elements with randomly generated index to the new array
})
newArray.forEach(newCat => {
shadowCatsContainer.appendChild(newCat); // here random cats are finally put to html container
})
And all of this work until the point when one of those random numbers is at least one time repeated... of course this happens actually 90% of time.
Im supposing it won't be simple solution to that. I tried so hard to make it work with different techniques, different loops, different array methods and nothing :( Also please take note that Im beginner so I need exhaustive guidance of what is going on :)
Have a nice day.
Your code is close; you can just remove the items that you're assigning to the new array from the source array so you don't use it twice.
const cats = [...catBoardCopy.querySelectorAll('.cat')]
function randomIndex() {
return Math.floor(Math.random() * cats.length);
}
cats.forEach(catElement => {
const index = randomIndex();
shadowCatsContainer.appendChild(cats[index]);
cats.splice(index, 1);
})
One option is to simply shuffle an array:
const cats = ['Tigger', 'Smokey', 'Kitty', 'Simba', 'Sassy'];
function shuffle(array, n = 500) {
const output = array.slice();
for (let i = 0; i < n; ++i) {
swap(output, getRandomInt(0, output.length), getRandomInt(0, output.length))
}
return output;
}
function swap(array, i, j) {
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
const shadowedCats = shuffle(cats);
console.log(cats);
console.log(shadowedCats);
console.log(shuffle(cats));
An example using array. Here I created an array with the possible numbers, it goes from 0 to the number of elements contained in the 'catsArrayList' array. If for example 'catsArrayList' has 3 elements, then the array with the possible numbers will be equal to: [0, 1, 2]
The idea now is to draw a random number from that array and then remove it from the list, and then we can go on repeating the process without getting repeated values.
e.g.
let catsArrayList = ['value1', 'value2', 'value3', 'value4', 'value5', 'value6'] // example
let numbers = [...Array(catsArrayList.length).keys()]
let lengthnumbers = numbers.length
for(let i = 1; i <= lengthnumbers; i++) {
let randoms = Math.floor(Math.random() * numbers.length)
console.log(i + 'ยบ number: ' + numbers.splice(randoms, 1))
}
Click on 'Run code snippet' a few times and you will see that you will get different, non-repetitive random numbers

Is there a way to see if a value in a 2D array matches any value in another 2D array?

I'm building a battleship game in Javascript and React and I've been stuck on this issue for a while now even after much Googling and StackOverflowing.
Basically my board is a 2D array, with 10 arrays inside of one array. I'm trying to randomly place ships and I'm having difficulties checking if a ship intersects another ship.
Here's what I have for my ships:
placeShips = () => {
// Logic to place boats randomly below
// Checks required before placing a boat:
// 1. Does the boat go off the board
// 2. Does the boat overlap another boat
// 3. If checks above pass then place boat
let placedPosition = []
let board = this.state.board.slice()
let i
for (i = 0; i < this.state.ships.length; i++) {
// First randomly select coordinates for where the boat will start
let xcoord = Math.floor(Math.random() * 10)
let ycoord = Math.floor(Math.random() * 10)
// Get positions in array where a boat will be
let potentialBoat = []
let newCoords
let j
for (j = 0; j < this.state.ships[i].getLength(); j++) {
newCoords = [xcoord, ycoord + j]
potentialBoat.push(newCoords)
The first for loop repeats for each ship left in my state to place and the second for loop takes a ship's length, gets the intended coordinate ([[0, 1], [0,2]] for a 2 length ship for example) and stores it in the potentialBoat array.
My idea is to use this potentialBoat array and see if there's any [xcoordinate, ycoordinate] that exists already in the placedPosition array and if so, to loop again for the current boat and get new coordinates until they don't intersect.
Is this possible? Or should I rethink my entire implementation? Thanks!
Inside the inner loop, when in the process of creating a ship, consider creating a string representing the coordinates. Eg, for newCoords of 1, 3, create a string 1_3. To validate the location, check to see if that string exists in an array (or Set) of the locations of the validated ships. At the end of the inner loop, once all positions for the length of the ship have been validated, combine the possible-locations into the validated-locations array:
placeShips = () => {
const placedPosition = [];
const board = this.state.board.slice();
const validatedPositionStrings = []; // <---- Create this array
for (const ship of this.state.ships) {
const thisShipLength = ship.getLength();
tryShip:
while (true) {
const thisBoatPossiblePositionStrings = [];
// Generate ship positions until valid
const xcoord = Math.floor(Math.random() * 10);
const ycoord = Math.floor(Math.random() * 10);
const potentialBoat = [];
for (let j = 0; j < thisShipLength; j++) {
// Then check to see if the below position is already in it
const thisCoordinateString = `${x}_${y}`;
if (validatedPositionStrings.includes(thisCoordinateString)) {
// Invalid
continue tryShip;
}
thisBoatPossiblePositionStrings.push(thisCoordinateString);
// If this point is reached, then this particular coordinate is valid
// do whatever you need to do:
const newCoords = [xcoord, ycoord + j];
potentialBoat.push(newCoords);
}
// All positions for ship are valid
// do something with potentialBoat here?
// push positions to placedPosition?
validatedPositionStrings.push(...thisBoatPossiblePositionStrings);
break;
}
}
}
It could be made less computationally complex by using a Set instead of an array, but that probably doesn't matter unless there are a very large number of iterations.
It would also be possible to search your array of arrays to see if the position has already been placed, but that would require an unnecessary amount of code IMO.
If possible, you might consider changing your data structure around so that rather than an array of arrays, you have just a single object representing the coordinates, whose values indicate the ship at that position (and possibly other attributes needed for a particular point), eg:
{
1_3: { ship: 'destroyer', 'attackedYet': 'false' }
// ...
Such an object would probably be easier to look up and work through than an array of arrays of X-Y pairs.

sampling values in array with good performance

I have a function which takes an array of values and a sampling rate. The function should remove values randomly by the sampling rate. For instance a sampling rate of 20% should remove 20% of the values. How can I achieve this with a very good performance, because I will iterate over more than 10.000 values?
My idea is something like
for(var i = values.length-1; i >= 0; i--){
var rnd = Math.floor((Math.random() * 100) + 1);
if(rnd < samplingRate)
values.splice(i,1);
}
but I think the Math.random() function is no performant choice.
There's really no need to iterate over the whole array if you only want to operate on 20% of it.
Loop for n times where n = Math.floor(0.20 * originalArray.length) - 1 and on each iteration get a random element from the array and remove it.
Unless you need to support older browsers use the .filter() method:
var samplingPercentage = samplingRate / 100;
var filtered = values.filter(function() {
return Math.random() < samplingPercentage;
});

Calculate maximum available rows and columns to fill with N amount of items

By reviewing this and this, I've come up with a function, that's probably more complex than it should be, but, man, my math sux:
function tablize(elements)
{
var root = Math.floor(Math.sqrt(elements));
var factors = [];
for (var i = 1; i <= root; i++)
{
if (elements % i === 0)
{
factors.push([i, elements / i]);
}
}
var smallest = null;
for (var f = 0; f < factors.length; f++)
{
var factor = factors[f];
var current = Math.abs(factor[0] - factor[1]);
if (!smallest || factors[smallest] > factor)
{
smallest = f;
}
}
return factors[smallest];
}
While this does work, it provides results I'm not satisfied with.
For instance - 7, it's divided in 1x7, where I'd like it to be 3x3. That's the minimum, optimal, grid size needed to fill with 7 elements.
Also - 3, it's divided in 1x3, where I'd like it to be 2x2.
I need this for a live camera feed frame distribution on a monitor, but I'm totally lost. The only way I can think of is building an extra function to feed with previously generated number and divide again, but that seems wrong.
What is the optimal solution to solve this?
For squares:
function squareNeeded(num) {
return Math.ceil(Math.sqrt(num));
}
http://jsfiddle.net/aKNVq/
(I think you mean the smallest square of a whole number that is bigger than the given amount, because if you meant a rectangle, then your example for seven would be 2*4 instead of 3*3.)

Fetch Z random items from array of size N in Z time?

I've got an array of size N which might be ordered in a certain way. I would like to get Z random items from this array in < O(N) time.
My understanding is that if I shuffle my array using Underscore's _.shuffle() that will take O(N) time. So, shuffling and then grabbing the 1st Z items is out.
If I generate Z random numbers between N, I think I can get into really ugly worst-case scenarios. This is because if N is something like 105 and Z is 100.. well, there will be a lot of overlap and maybe I'll reroll Z several hundred times.
I was wondering if there was a simple solution to this issue? I didn't see any Underscore methods specifically up to the task.
Here are a few algorithms to consider:
A. Shuffle
Shuffle array ; O(N)
Pick first Z items ; O(Z) or better
Overall complexity: O(N)
function A(array, z) {
return _.first(_.shuffle(array), z);
}
B. Random Selection with Re-rolls
Pick a random number from 0..N-1 ; O(1)
If the number has been picked before, go to step 1
Record the picked number ; O(1)
Pick an item from the array at the given index ; O(1)
If we've picked less than Z items, go to step 1
Overall complexity:
For Z << N, O(Z) average case
For Z = N, O(N^2) average case
function B(array, z) {
var pickedIndices = {};
var result = [];
while (result.length < z) {
var randomIndex = Math.floor(Math.random() * array.length);
if (!(randomIndex in pickedIndices)) {
pickedIndices[randomIndex] = 1;
result.push(array[randomIndex]);
}
}
return result;
}
C. Random Selection with Removal
Make a copy of the array ; O(N)
Pick a random item from the array ; O(1)
Remove the item from the array ; O(N)
If we've picked less than Z items, go to step 2
Overall complexity: O(Z*N)
function C(array, z) {
var result = [];
array = array.slice(0);
for (var i = 0; i < z; i++) {
var randomIndex = Math.floor(Math.random() * array.length);
result.push(array.splice(randomIndex, 1)[0]);
}
return result;
}
Performance Testing
http://jsperf.com/fetch-z-random-items-from-array-of-size-n
With N = 100 and Z = 10, algorithm C was the fastest (probably because most of the logic uses native functions and/or is easy to optimize, which for small values of N and Z is more important than the algorithmic complexity).
With N = 100 and Z = 100, algorithm A was the fastest.
With N = 1000 and Z = 100, algorithm B was the fastest.
Conclusion
There's no one best algorithm among those I considered; it depends on the characteristics of your data. If the characteristics of your data can vary, it might be worthwhile to do further testing and create some criteria based on the values of N and Z to selectively choose the best algorithm.
For example, if Z <= N/2, you might use algorithm B; otherwise, algorithm A.
In short, there's no "simple" solution that always has great performance.
I'm don't think I fully understand your problem, but if you want to get a random element from an array and for it not to be repeated and hence you are limited to rolling fewer times than there are elements, then you can try this
function shuffle(obj, rounds, deep) {
var length = obj.length;
if (length < 2) {
return;
}
var rounds32 = rounds >>> 0 || 1;
var deepBool = deep === true;
var roundCount = 0;
var index, rnd, tmp;
while (roundCount < rounds32) {
index = length;
while (index) {
if (Array.isArray(obj[index - 1])) {
shuffle(obj[i], rounds32, deepBool);
}
rnd = Math.floor(Math.random() * index);
index -= 1;
tmp = obj[index];
obj[index] = obj[rnd];
obj[rnd] = tmp;
}
roundCount += 1;
}
}
var array = [];
for (var count = 0; count < 100; count += 1) {
array.push(count);
}
shuffle(array);
var rolls = 10;
console.log(array.slice(0, rolls));

Categories

Resources