I need to build 2d matrix 50x50 representing boxes with random colors, but if the boxes which are close to each other have the same colors, they should get different random color from each other, until it's different and then continue building.
Here I made matrix with boxes inside it works fine, but colors sometimes do match:
function onLoad(evt)
{
var matrix = [];
for (var i = 0; i < 50; i++) {
var row = [];
for (var j = 0; j < 50; j++) {
var randColor = Math.floor(Math.random()*16777215).toString(16);
row.push(MyComponent(randColor));
}
matrix.push(row);
}
var newData = matrix.map(function(row) {
return row.map(function(x) {
return x;
})})
}
You need a way to determine whether a particular color is too close to another. One way to do this is with rgb-lab (or, less accurately, euclidean distance). Say you use rgb-lab's deltaE function, which takes two arguments, where each argument is a 3-item array of RGB amounts.
Generate your random colors such that you can get their components' decimal values easily, and so that you can also get their hex string representation easily. Then iterate over the filled adjacent indicies and compare the colors. If they're too similar, try again. Something along the lines of:
const MINIMUM_DISTANCE = 25;
const getColor = () => {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
const str = r.toString(16) + g.toString(16) + b.toString(16);
return { rgb: [r, g, b], str };
};
const matrix = [];
for (let i = 0; i < 50; i++) {
const row = [];
for (let j = 0; j < 50; j++) {
let color;
let tooClose = false;
do {
color = getColor();
tooClose =
(row[j - 1] && deltaE(color.rgb, row[j - 1].rgb) < 25) ||
(matrix[i - 1] && deltaE(color.rgb, row[i - 1][j].rgb < 25));
} while (tooClose);
row.push(color);
}
}
Change the MINIMUM_DISTANCE as desired. See here for an explanation of the numbers.
Then you'll need to turn the color objects into an array of components with color strings at the end.
const arrayOfComponents = matrix.map(
row => row.map(
({ str }) => MyComponent(str)
)
);
Related
I have a nested array like this:
[ [5, 10, 5, 15, 5, 10], [10, 15, 50, 200] ]
So basically I have index 0 that also has an array with the values [5,10,5,15,5,10] and index 1 with an array with values [10,15,50,350].
let array = [[],[]];
let x = 0;
let y = 0;
for (let i = 0; i < 10; i++) {
x = ... generate random number ...
y = ... generate random number ...
while (array[0].includes(x,y) {
x = ... generate new random number ...
y = ... generate new random number ...
}
array[0].push(x,y);
}
Is there a way for me to find if for example index 0 in the array already contains the two generated values in the same order they are being pushed?
For example, I'm adding value 5,15 to the array. And the array already contains index 0 with 5 and index 1 with, 15 in that order. So I want to generate a new number if the numbers already exist in that order.
I've tried looking for solutions, but haven't found anything that helps me with what I wanna do.
I recommend making a function that checks for a specific sequence of numbers, this way if you need it to find a X number sequence you're ready.
const max = 100;
let array = [[], []];
let x = 0,
y = 0;
for (let i = 0; i < 1000; i++) {
x = Math.floor(Math.random() * max);
y = Math.floor(Math.random() * max);
while (containsSequence(array[0], [x, y])) {
console.log(`Sequence ${x},${y} already exists.`);
x = Math.floor(Math.random() * max);
y = Math.floor(Math.random() * max);
}
array[0].push(x, y);
}
/**
* #param {Array<Number>} arr
* #param {Array<Number>} sequence */
function containsSequence(arr, sequence) {
if(arr.length === 0) { return false; }
const first = sequence[0];
let index = arr.indexOf(first, 0);
while(index > -1) {
if(sequence.every((v, i) => arr[index + i] === v)) {
return true;
}
index = arr.indexOf(first, index);
if(index == -1) { return false; }
index++;
}
return false;
}
You are putting x and y one after each other in the list, if you connect them to an object or an array, you can iterate over the outer array more easily:
for (let i = 0; i < 10; i++) {
x = ... generate random number ...
y = ... generate random number ...
while (array[0].filter(i=>i.x == x && i.y == y).length) {
x = ... generate new random number ...
y = ... generate new random number ...
}
array[0].push({x,y});
}
it will also make it more simle. Of course,only if you can make this change to the array structure
Assuming that this is just a general structural question as mentioned in the comments, you can adapt your code as below.
for (let i = 0; i < 10; i++) {
int index = 1;
while (index < array[0].length){
if ((array[0][i][index-1].includes(x) && array[0][i][index].includes(y)) {
x = ... generate new random number ...
y = ... generate new random number ...
index = 0;
}
else{
index++;
}
}
array[0].push(x,y);
}
}
I was trying to make simple script that uses GPU for multiplying arrays, but when I turn on the code, it shows error as in title. I don't know if it's my fault and I didn't installed every library or its a bug.
Code is from gpu-js github example:
const { GPU } = require('gpu.js');
const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function(a, b) {
let sum = 0;
for (let i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([512, 512]);
const c = multiplyMatrix(a, b);
Thanks in advance.
A and B are not defined, you need to define your matrices first and then call the function. Here's the full example from their website, comments mine:
// Function to create the 512x512 matrix
const generateMatrices = () => {
const matrices = [[], []]
for (let y = 0; y < 512; y++){
matrices[0].push([])
matrices[1].push([])
for (let x = 0; x < 512; x++){
matrices[0][y].push(Math.random())
matrices[1][y].push(Math.random())
}
}
return matrices
}
//Define the function to be ran on GPU
const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function(a, b) {
let sum = 0;
for (let i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([512, 512])
// Create the matrices
const matrices = generateMatrices()
// Run multiplyMatrix using the 2 matrices created.
const out = multiplyMatrix(matrices[0], matrices[1])
I have a very basic implementation of k-means in javascript (I know but it needs to run in the browser). What I would like to understand is - how could one make this more functional?
It is currently full of loops, and extremely difficult to follow / reason about, code below:
export default class KMeans {
constructor(vectors, k) {
this.vectors = vectors;
this.numOfVectors = vectors.length;
this.k = k || bestGuessK(this.numOfVectors);
this.centroids = randomCentroids(this.vectors, this.k);
}
classify(vector, distance) {
let min = Infinity;
let index = 0;
for (let i = 0; i < this.centroids.length; i++) {
const dist = distance(vector, this.centroids[i]);
if (dist < min) {
min = dist;
index = i;
}
}
return index;
}
cluster() {
const assigment = new Array(this.numOfVectors);
const clusters = new Array(this.k);
let movement = true;
while (movement) {
// update vector to centroid assignments
for (let i = 0; i < this.numOfVectors; i++) {
assigment[i] = this.classify(this.vectors[i], euclidean);
}
// update location of each centroid
movement = false;
for (let j = 0; j < this.k; j++) {
const assigned = [];
for (let i = 0; i < assigment.length; i++) {
if (assigment[i] === j) assigned.push(this.vectors[i]);
}
if (!assigned.length) continue;
const centroid = this.centroids[j];
const newCentroid = new Array(centroid.length);
for (let g = 0; g < centroid.length; g++) {
let sum = 0;
for (let i = 0; i < assigned.length; i++) {
sum += assigned[i][g];
}
newCentroid[g] = sum / assigned.length;
if (newCentroid[g] !== centroid[g]) {
movement = true;
}
}
this.centroids[j] = newCentroid;
clusters[j] = assigned;
}
}
return clusters;
}
}
It certainly can.
You could start with this:
classify(vector, distance) {
let min = Infinity;
let index = 0;
for (let i = 0; i < this.centroids.length; i++) {
const dist = distance(vector, this.centroids[i]);
if (dist < min) {
min = dist;
index = i;
}
}
return index;
}
Why is this a member function? Wouldn't a pure function const classify = (centroids, vector, distance) => {...} be cleaner?
Then for an implementation, let's change the distance signature a bit. If we curry it to const distance = (vector) => (centroid) => {...}, we can then write
const classify = (centroids, vector, distance) =>
minIndex (centroids .map (distance (vector)))
And if that distance API is out of our control, it's not much harder:
const classify = (centroids, vector, distance) =>
minIndex (centroids .map (centroid => distance (vector, centroid)))
Granted, we haven't written minIndex yet, but we've already broken the problem down to use a more meaningful abstraction. And minIndex isn't hard to write. You can do it imperatively as the original classify function did, or with something like this:
const minIndex = (xs) => xs.indexOf (Math.min (...xs))
Note that distance is a slightly misleading name here. I had to read it more carefully because I assumed a name like that would represent..., well a distance. Instead it's a function used to calculate distance. Perhaps the name metric or something like distanceFunction, distanceFn, or distanceImpl would be more obvious.
Now let's move on to this bit:
const newCentroid = new Array(centroid.length);
for (let g = 0; g < centroid.length; g++) {
let sum = 0;
for (let i = 0; i < assigned.length; i++) {
sum += assigned[i][g];
}
newCentroid[g] = sum / assigned.length;
if (newCentroid[g] !== centroid[g]) {
movement = true;
}
}
This code has two responsibilities: creating the newCentroid array, and updating the value of movement if any value has changed.
Let's separate those two.
First, creating the new centroid. We can clean up that nested for-loop to something like this:
const makeNewCentroid = (centroid, assigned) =>
centroid .map ((c, g) => mean (assigned .map ((a) => a[g])))
This depends on a mean function, which we'll write along with its required sum function like this:
const sum = (ns) => ns .reduce ((t, n) => t + n, 0)
const mean = xs => sum (xs) / xs.length
Then we need to update movement. We can do that easily based on centroids and newCentroids:
movement = centroids.some((c, i) => c !== newCentroids[i])
Obviously, you can continue in this manner. Each for loop should have a fundamental purpose. Find that purpose and see if one of the Array.prototype methods could better express it. For the second section we worked with above, we found two purposes, and just split them into two separate blocks.
This should give you a good start on making this more functional. There is no magic bullet. But if you think in terms of pure functions on immutable data, and on strong separation of concerns, you can usually move in a functional direction.
I want to group together adjacent (vertically and horizontally) characters in an array, in this case all the adjacent "*" belong to one group, see below. And then I want to be able to count how many groups of "*" there are, in this case the answer is 3.
var x = ["...***....",
"..*****...",
"...***....",
"........*.",
".......***",
"..*.....*.",
".***......"];
The code:
function compareRows(){
var totalGroups = 0;
for (i = 0; i < x.length; i++) {
var array = x[i];
for (j = 0; j < array.length; j++) {
var row = array[j];
for (k = 0; k < row.length; k++) {
var char1 = row[k];
var nextRow = j+1;
var char2 = row[nextRow];
if(char1== "*"){
if(char1 != char2) {
totalGroups+=1;
}
} else {
//console.log("Keep searching..");
}
}
}
} console.log(totalGroups);
}
compareRows();
So basically for each row I'm searching for the character "*" and when it's found, if the character at the same index on the row below isn't a "*", then one group is found. However, at the moment totalGroups is 20, the total amout of "*" found in the whole array. I feel a bit stuck and don't know how to proceed.
I believe you'll need to walk the grid at any point you find an asterisk and keep track of cells you have already visited. You can do so with a visited array to avoid walking the same cell twice and a recursive walk function. Here's what I was able to come up with:
const grid = [
"...***....",
"..*****...",
"...***....",
"........*.",
".......***",
"..*.....*.",
".***......"
];
function getGroups(grid) {
// split the grid into a 2d array of objects (cells)
const cellGrid = grid.map((s, y) => s.split('').map((value, x) => ({value, x, y})));
const height = cellGrid.length;
const width = cellGrid[0].length;
const visited = []; // keep track of visited cells
const groups = [];
let currentGroup = [];
// walk each cell left-to-right top-to-bottom
for(let y = 0; y < height; y++) {
for(let x = 0; x < width; x++) {
walkFromCell(x, y, true);
}
}
return groups;
function walkFromCell(x, y, groupStart) {
const cell = getCell(x, y);
// ignore visited and non-group cells
if(!cell || cell.value !== '*' || visited.includes(cell)) return;
currentGroup.push(cell);
visited.push(cell);
walkFromCell(x + 1, y, false);
walkFromCell(x - 1, y, false);
walkFromCell(x, y + 1, false);
// groupStart is only true for the first cell in a group
if(groupStart) {
groups.push(currentGroup);
currentGroup = [];
}
}
function getCell(x, y) {
return cellGrid[y] ? cellGrid[y][x] : null;
}
}
const groups = getGroups(grid);
const groupCount = groups.length;
console.log(`Count = ${groupCount}`);
console.log('Groups =', groups);
My goal is to make a randomly generated 2D Array in Javascript, that has an X amount of the same one character value while the rest of the values are equal to another character.
In this example, there are 10 rows and 10 columns for the 2D Array. 20 out of the possible 100 values of the Array should be equal to 'Y' (for yes) and the 80 others should be 'N' (for no). I want the 'Y's to be randomly placed all over the Array, and I absolute need exactly 20 of them to be 'Y's and the rest 'N's.
I had a less efficient way before, and I thought to try this approach, where after I define the Array, I make the first X amount of values a 'Y' and then the rest all 'N's. Then I shuffle the array, (using the shuffle from the underscore library) so that the 'Y's are all spread out randomly everywhere.
Is this an efficient way of getting what I need done? Are there any better solutions? I tried making a JSFiddle with my example, but the site appears to be down at the moment.
(I was unable to test my code yet to see if the shuffle worked correctly on my 2D array)
var rows = 10;
var cols = 10;
var elements = 20;
//Define Empty Array
var test = new Array(rows);
for (var k = 0; k < rows; k++)
{
test[k] = Array(cols);
}
var i = 1;
for (var x = 0; x < rows; x++)
{
for (var y = 0; y < cols; y++)
{
if (i <= elements)
{
test[x][y] = "Y";
}
else
{
test[x][y] = "N";
}
}
}
//Shuffle all those values so they're no longer in order
var shuffledTest = _.shuffle(test);
//Print in rows
for (var x = 0; x < rows; x++)
{
console.log(shuffledTest[x]);
}
A very simple solution is to first create an array, fill it with a number of "N"s, insert the "Y"s at random indexes, and then finally splitting it into the 2-dimensional array that you want:
var tmpArr = [], // Temporary 1-dimensional array to hold all values
arr = [], // The final 2-dimensional array
rows = 10,
cols = 10,
elements = 20; // Number of "Y"s
// 1. Fill temporary array with "N"s
for (var i = 0; i < rows * cols - elements; i += 1) {
tmpArr.push("N");
}
// 2. Insert "Y"s at random indexes in the temporary array
for (var i = 0; i < elements; i += 1) {
var index = Math.round(Math.random() * (tmpArr.length + 1));
tmpArr.splice(index, 0, "Y");
}
// 3. Split temporary array into 10 seperate arrays
// and insert them into the final array
for (var i = 0; i < rows; i += 1) {
var row = tmpArr.slice(i * cols, (i + 1) * cols);
arr.push(row);
}
JSBin to illustrate: http://jsbin.com/luyacora/1/edit
You can try this solution, it uses underscores range to create a pair of arrays to use as iterators, though their values don't matter.
Play around with the randomizer function to get an even distribution of 'y's
JSBIN: http://jsbin.com/yaletape/1/
var rows = _.range(0, 10, 0);
var columns = _.range(0, 10, 0);
function randomizer(mult){
return Math.floor((Math.random()*mult)+1);
}
var y_count = 0;
var matrix = _.map(rows, function(){
return _.map(columns, function(v, i){
var value;
var y_allowed = randomizer(3);
var current_y_count = 0;
if(y_count < 20 && current_y_count < y_allowed){
var rand = randomizer(5);
if(rand > 4){
value = 'y';
current_y_count++;
y_count++;
}
}
if(!value){
value = 'n';
}
return value;
});
});
//The above could be simplified to
var matrix = _.range(0,10,0).map(function(){
return _.range(0,10,0).map(function(){
//put the logic code from above here
});
});
Maybe shuflle a 2D array is not the best way. As #Zeb mentioned, here is some code that fill random positions with the 'Y' value. After that, the other positions are filled with 'N'.
http://plnkr.co/edit/avyKfgsgOSdAkRa1WOsk
var arr = [];
var cols = 10;
var rows = 10;
var positions = rows*cols; // 100
var YQty = 10; // only 10 'Y' are needed
// 'Y' values.
for(i = 0; i < YQty; i++)
{
do
{
x = parseInt(Math.random() * cols);
y = parseInt(Math.random() * rows);
filled = false;
if (typeof(arr[x]) == "undefined")
{
arr[x] = [];
}
if (typeof(arr[x][y]) == "undefined")
{
arr[x][y] = 'Y';
filled = true;
}
}
while (!filled);
}
// 'N' values.
for (x = 0; x < cols; x++)
{
if (typeof(arr[x]) == "undefined")
{
arr[x] = [];
}
for (y = 0; y < rows; y++)
{
if (arr[x][y] != 'Y')
{
arr[x][y] = 'N';
}
}
}
Shuffling the multidimensional array is not the best approach. Seeing as any sort is worse than linear time complexity. The easiest solution would be to create your multidimensional array and then set each index value to the char you want the 'rest' of the values to be. Then for 1 -> the number of other char value choose a random index and set that to the char.
Note: If the randomly picked spot has already been changed you need to choose a new one to make sure you have the right amount at the end.