How do I generate objects on a map, without them occupying the same space or overlapping on a HTML5 Canvas?
X coordinate is randomly generated, to an extent. I thought checking inside the array to see if it's there already, and the next 20 values after that (to account for the width), with no luck.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var positiony = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var positionx = (Math.random() * 4000) + 500 - (points/100);
var duplicatetest = 21;
for (var d = 0; d < duplicatetest; d++) {
var duplicate = $(jQuery.inArray((positionx + d), platforms));
if (duplicate > 0) {
var duplicateconfirmed = true;
}
}
if (duplicateconfirmed) {
var positionx = positionx + 20;
}
var duplicateconfirmed = false;
platforms[i] = new Platform(positionx,positiony,type);
}
}();
I originally made a cheat fix by having them generate in an area roughly 4000 big, decreasing the odds, but I want to increase the difficulty as the game progresses, by making them appear more together, to make it harder. But then they overlap.
In crude picture form, I want this
....[]....[].....[]..[]..[][]...
not this
......[]...[[]]...[[]]....[]....
I hope that makes sense.
For reference, here is the code before the array check and difficulty, just the cheap distance hack.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var position = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
platforms[i] = new Platform((Math.random() * 4000) + 500,position,type);
}
}();
EDIT 1
after some debugging, duplicate is returning as [object Object] instead of the index number, not sure why though
EDIT 2
the problem is the objects are in the array platforms, and x is in the array object, so how can I search inside again ? , that's why it was failing before.
Thanks to firebug and console.log(platforms);
platforms = [Object { image=img, x=1128, y=260, more...}, Object { image=img, x=1640, y=260, more...} etc
You could implement a while loop that tries to insert an object and silently fails if it collides. Then add a counter and exit the while loop after a desired number of successful objects have been placed. If the objects are close together this loop might run longer so you might also want to give it a maximum life span. Or you could implement a 'is it even possible to place z objects on a map of x and y' to prevent it from running forever.
Here is an example of this (demo):
//Fill an array with 20x20 points at random locations without overlap
var platforms = [],
platformSize = 20,
platformWidth = 200,
platformHeight = 200;
function generatePlatforms(k) {
var placed = 0,
maxAttempts = k*10;
while(placed < k && maxAttempts > 0) {
var x = Math.floor(Math.random()*platformWidth),
y = Math.floor(Math.random()*platformHeight),
available = true;
for(var point in platforms) {
if(Math.abs(point.x-x) < platformSize && Math.abs(point.y-y) < platformSize) {
available = false;
break;
}
}
if(available) {
platforms.push({
x: x,
y: y
});
placed += 1;
}
maxAttempts -= 1;
}
}
generatePlatforms(14);
console.log(platforms);
Here's how you would implement a grid-snapped hash: http://jsfiddle.net/tqFuy/1/
var can = document.getElementById("can"),
ctx = can.getContext('2d'),
wid = can.width,
hei = can.height,
numPlatforms = 14,
platWid = 20,
platHei = 20,
platforms = [],
hash = {};
for(var i = 0; i < numPlatforms; i++){
// get x/y values snapped to platform width/height increments
var posX = Math.floor(Math.random()*(wid-platWid)/platWid)*platWid,
posY = Math.floor(Math.random()*(hei-platHei)/platHei)*platHei;
while (hash[posX + 'x' + posY]){
posX = Math.floor(Math.random()*wid/platWid)*platWid;
posY = Math.floor(Math.random()*hei/platHei)*platHei;
}
hash[posX + 'x' + posY] = 1;
platforms.push(new Platform(/* your arguments */));
}
Note that I'm concatenating the x and y values and using that as the hash key. This is to simplify the check, and is only a feasible solution because we are snapping the x/y coordinates to specific increments. The collision check would be more complicated if we weren't snapping.
For large sets (seems unlikely from your criteria), it'd probably be better to use an exclusion method: Generate an array of all possible positions, then for each "platform", pick an item from the array at random, then remove it from the array. This is similar to how you might go about shuffling a deck of cards.
Edit — One thing to note is that numPlatforms <= (wid*hei)/(platWid*platHei) must evaluate to true, otherwise the while loop will never end.
I found the answer on another question ( Searching for objects in JavaScript arrays ) using this bit of code to search the objects in the array
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
I also ended up rewriting a bunch of the code to speed it up elsewhere and recycle platforms better.
it works, but downside is I have fewer platforms, as it really starts to slow down. In the end this is what I wanted, but its no longer feasible to do it this way.
var platforms = new Array();
var nrOfPlatforms = 7;
platformWidth = 20,
platformHeight = 20;
var positionx = 0;
var positiony = 0;
var arrayneedle = 0;
var duplicatetest = 21;
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
function generatePlatforms(ind){
roughx = Math.round((Math.random() * 2000) + 500);
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var duplicate = false;
for (var d = 0; d < duplicatetest; d++) {
arrayneedle = roughx + d;
var result = search(platforms, arrayneedle);
if (result >= 0) {
duplicate = true;
}
}
if (duplicate = true) {
positionx = roughx + 20;
}
if (duplicate = false) {
positionx = roughx;
}
platforms[ind] = new Platform(positionx,positiony,type);
}
var generatedplatforms = function(){
for (var i = 0; i < nrOfPlatforms; i++) {
generatePlatforms(i);
};
}();
you go big data, generate all possibilities, store each in an array, shuffle the array,
trim the first X items, this is your non heuristic algorithm.
Related
I have a while loop that is supposed to randomly plant numberOfTruth true values in a 2 dimensional. I am only to change them if they are not true already, and only do so for numberOfTruths times:
function truthfulArray(maxY, maxX, numberOfTruths, array) {
var x;
var y;
var counter = 0;
while (counter < numberOfTruths) {
x = generateRandomNumber(maxX);
y = generateRandomNumber(maxY);
if (!array[x][y]) {
array[x][y] = true;
counter++;
}
}
return array;
}
I could see the issue with its optimization in relation to the size of 2 dimensional array and as the numberOfTruths approaches the product of maxY and maxX it would waste alot of resources to complete the task. I was wondering what tweaks I can make to the function to make it more efficient. Thanks in advance!
*** generateRandomNumber(max) is a simple function that returns a random number from 0 to the max value entered.
Based on #Bergi's comment, here's one optimization. It assumes the array is empty to begin with (or rather, it doesn't care and just overwrites things).
It's slower when the fill factor (N / (X * Y)) is low (testing with X = Y = 500), but seems to win out over the original implementation at about 40%, and is decisively faster (like 3x) at 80%. (You might want to use that as a heuristic.)
The general idea is that we first fill random rows from the rows' start, then spread out each row using Fisher-Yates shuffle. I've transposed the y and x compared to the original, because that's just how I'm used to dealing with 2D arrays. :D
function shuffle(array) { // h/t https://bost.ocks.org/mike/shuffle/
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
function truthfulArrayFillRows(maxY, maxX, numberOfTruths, array) {
var nLeft = numberOfTruths;
var nSeededPerRow = new Array(maxY).fill(0);
// Seed rows randomly with trues starting from the left
while(nLeft > 0) {
var y = generateRandomNumber(maxY);
var x = nSeededPerRow[y];
if(x < maxX) {
array[y][x] = true;
nLeft --;
nSeededPerRow[y] ++;
}
}
// Shuffle the rows we seeded
for(var y = 0; y < maxY; y++) {
if(nSeededPerRow[y] > 0) {
shuffle(array[y]);
}
}
return array;
}
Instead of randomly selecting numberOfTruth many positions of all possible positions, you could randomly select numberOfTruth many of only the valid positions. To do that, you would have to find those positions first.
function shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
}
function chunkArray(myArray, chunk_size){
var results = [];
while (myArray.length) {
results.push(myArray.splice(0, chunk_size));
}
return results;
}
function flatten(array) {
var flattened=[];
for (var i=0; i<array.length; ++i) {
var current = array[i];
for (var j=0; j<current.length; ++j)
flattened.push(current[j]);
}
return flattened;
}
function truthfulArray(maxY, maxX, numberOfTruths, array){
var flatArray = flatten(array);
var validPositions = flatArray.map(function(e, i){if(!e) return i});
var randomPositions = shuffle(validPositions).slice(0, numberOfTruths);
randomPositions.forEach(function(i){flatArray[i] = true;});
return chunkArray(flatArray, maxY);
}
var maxX = 20, maxY = 30;
var array = Array(maxX).fill(Array(maxY).fill(false));
truthfulArray(maxY, maxX, 40, array);
If this approach is more efficient depends on your data. If there are many valid positions to choose from, your code should be totally fine and efficient. The more trues there are in the array the less likely it is that your code (randomly) hits them. In that case the approach I described will be more efficient.
I hope you find that helpful.
I am newbie in machine learning, but decided to make own js library for neural networks, everything went perfect until i tryed to train my NN. In My Mini Library i created some functions...
1) A Function That Creates My Neuron-Object:
this.Node = function (conns) {
var output = {};
output.b = hyth.Random({type: "TanH"});
output.w = [];
for (var a = 0; a < conns; a++){
output.w[a] = hyth.Random({type: "TanH"});
}
output.Value = function (i) {
if (i.length == conns) {
var sum = 0;
for (var a = 0; a < conns; a++){
sum += i[a] * output.w[a];
}
sum += output.b;
return myMath.Activate(sum, {type: "Sigmoid"});
}
}
return output;
}
This function has one argument , which is the amount of wanted weights from neuron, and it returns an object with two properties - "b" the float (bias), and "w" the 1D Array which contains floats, and one method - which calculates the activation of neuron-object.
2) A Function That Creates My Neural Net
this.Network = function () {
var p = arguments;
var arr = [];
for (var a = 0; a < p.length-1; a++){
arr[a] = [];
for (var b = 0; b < p[a+1]; b++){
arr[a][b] = this.Node(p[a]);
}
}
return arr;
}
This Function Returns A 2D Array with Neuron-Object as It's final value, using argument array as settings for layer count and node count for each layer.
3) A Function That Feeds Forward The NN
this.Forward = function (network, input) {
if (network[0][0].w.length == input.length) {
var activations = [];
for (var a = 0; a < network.length; a++){
activations[a] = [];
for (var c = 0; c < network[a].length; c++){
if (a == 0){
activations[0][c] = network[0][c].Value(input);
continue;
}
activations[a][c] = network[a][c].Value(activations[a-1]);
}
}
return activations;
}
}
This Function Returns 2D array with an activation float for every neuron as it's final value. It uses 2 agruments - the output of 2nd function, input array.
4) And Final Function That Backpropagates
this.Backward = function (network, input, target) {
if (network[0][0].w.length == input.length && network[network.length-1].length == target.length) {
var activations = this.Forward(network, input, true);
var predictions = activations[activations.length-1];
var errors = [];
for (var v = 0; v < network.length; v++) {
errors[v] = [];
}
for (var a = network.length-1; a > -1; a--){
for (var x = 0; x < network[a].length; x++) {
var deract = hyth.Deractivate(activations[a][x]);
if (a == network.length-1) {
errors[a][x] = (predictions[x] - target[x]) * deract;
} else {
errors[a][x] = 0;
for (var y = 0; y < network[a+1].length; y++) {
errors[a][x] += network[a+1][y].w[x] * errors[a+1][y];
}
errors[a][x] *= deract;
}
}
}
return errors;
}
}
This Function Returns 2D array with the rror float for every neuron as it's final value. Arguments are 3 - the nnet , input and wanted output.
So I can make a neural network, feed forward and and backpropagate, receive activations and errors, but i always fail to train my net with my errors and activations to work perfect , last time it was outputing same result for every type of input. I want to understand training algorithm from zero , so i need someone's help.
P.S. - i dont want someone say that i need to use famous libraries , i want to understand and make it myself.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I've been trying to evolve a neural network that prints values converging to one, using a genetic algorithm.
I've tried debugging the code but don't know what I've messed up.
I'm using fitness to chose the best "brains" and then cross them over (reproduce).
At the moment it is only trying to evolve "brains" that return the number. the fitness is a function of the difference between returned number and original number.
"use strict";
function sigmoid(x) {
return 1 / (1 + Math.E ** -x);
}
function random(min, max) {
return (max - min) * Math.random() + min
}
function toss() {
return random(-1, 1)
}
function Brain(inputs, hiddens, outputs) {
this.structure = [...arguments];
if (this.structure.length < 3) throw "Invalid layer count";
this.layers = [];
this.layers[this.structure.length - 1] = {
nodes: []
};
for (var i = this.structure.length - 1; i--;) this.layers[i] = {
bias: toss(),
nodes: []
};
for (var i = 1; i < this.structure.length; i++) {
var nodes = this.layers[i].nodes;;
for (var j = this.structure[i]; j--;) {
var node = nodes[j] = {
weights: []
};
for (var k = this.structure[i - 1]; k--;) node.weights[k] = toss();
}
};
}
Brain.prototype.compute = function() {
if (arguments[0] !== this.structure[0]) throw "Invalid input count";
for (var i = arguments.length; i--;) this.layers[0].nodes[i] = {
value: arguments[i]
};
for (var i = 1; i < this.layers.length - 1; i++) {
var layer = this.layers[i];
var feeder = this.layers[i - 1];
for (var j = layer.nodes.length; j--;) {
var node = layer.nodes[j];
var dot = 0;
for (var k = node.weights.length; k--;) dot += node.weights[k] * feeder.nodes[k].value;
node.value = sigmoid(dot + feeder.bias);
}
}
var result = [];
var layer = this.layers[this.layers.length - 1];
var feeder = this.layers[this.layers.length - 2];
for (var j = layer.nodes.length; j--;) {
var node = layer.nodes[j];
var dot = 0;
for (var k = node.weights.length; k--;) dot += node.weights[k] * feeder.nodes[k].value;
result[j] = sigmoid(dot + feeder.bias);
}
return result;
}
Brain.prototype.cross = function() {
var newBrain = new Brain(...this.structure);
var brains = [this, ...arguments];
for (var i = 1; i < newBrain.layers.length; i++) {
var layer = newBrain.layers[i];
for (var j = layer.nodes.length; j--;) {
var node = layer.nodes[j];
for (var k = node.weights.length; k--;) node.weights[k] = mutate() ||
brains[Math.floor(Math.random() * brains.length)]
.layers[i].nodes[j].weights[k];
}
}
for (var i = newBrain.layers.length - 1; i--;) newBrain.layers[i].bias = mutate() ||
brains[Math.floor(Math.random() * brains.length)]
.layers[i].bias;
return newBrain;
}
function mutate(key, nodes) {
if (Math.random() > 0.05) return toss();
}
var brain = new Brain(1, 5, 1);
var newBrain = new Brain(1, 5, 1)
var result = brain.compute(1);
var cross = brain.cross(newBrain);
var brains = [];
for (var node = 45; node--;) brains.push({
brain: new Brain(1, 5, 4, 3, 2, 1)
});
for (var count = 1000000; count--;) {
brains.push({
brain: new Brain(1, 5, 4, 3, 2, 1)
});
for (var node = brains.length; node--;) {
var brain = brains[node];
var number = 1;
var target = number;
brain.fitness = 1 / Math.abs(number - brain.brain.compute(number));
}
brains.sort((a, b) => a.fitness < b.fitness);
if (count % 10000 === 0) console.log(brains.length, brains[0].fitness);
var newBrains = [];
for (var node = 10; node--;)
for (var j = node; j--;) newBrains.push({
brain: brains[node].brain.cross(brains[j].brain)
});
brains = newBrains;
}
console.log(brains);
What will I need to improve/change?
Here is the console log:
46 1.468903884218341
46 1.1881817088540865
46 4.899728181582378
46 1.5494097713447523
46 2.4958253537304644
46 2.4091648830940953
46 1.4000955420478967
46 1.7560836401632383
46 3.3419380735652897
46 2.8290305398668245
46 2.951901023302089
46 2.9400525658126675
46 2.6769575714598948
46 1.55835425177616
As you can see, the fitness seems to be random
Some advice...
Neural networks usually take an input which should somehow be related to the output. I couldn't find any inputs for the network? If you can't come up with any good ideas just use the XOR problem and try to solve it.
When checking if your population is becoming better over time don't look at all brains of each generation. Remember, you are intentionally creating some randomized networks which may or may not be good at your task. Try printing the Top result of each generation and maybe the average score. In a working genetic algorithm both values should become better over time. (altough the top score is way more significant & important)
(Not directly adressing your issue) Don't use javascript. You could probably transscribe your current code to java/c#/c++. These languages execute way faster than JS.
I have a numeric 2D array (an array of arrays, or a matrix) and I need to do simple matrix operations like adding a value to each row, or multiplying every value by a single number. I have little experience with math operations in JavaScript, so this may be a bone-headed code snippet. It is also very slow, and I need to use it when the number of columns is 10,000 - 30,000. By very slow I mean roughly 500 ms to process a row of 2,000 values. Bummer.
var ran2Darray = function(row, col){
var res = [];
for (var i = 0 ; i < row; i++) {
res[i] = [];
for (var j = 0; j < col; j++) {
res[i][j] = Math.random();
}
}
return res;
}
var myArray = ran2Darray(5, 100);
var offset = 2;
for (i = 0; i < myArray.length; i++) {
aRow = myArray[i];
st = Date.now();
aRow.map(function addNumber(offset) {myArray[i] + offset*i; })
end = Date.now();
document.write(end - st);
document.write("</br>");
myArray[i] = aRow;
}
I want to avoid any added libraries or frameworks, unless of course, that is my only option. Can this code be made faster, or is there another direction I can go, like passing the calculation to another language? I'm just not familiar with how people deal with this sort of problem. forEach performs roughly the same, by the way.
You don't have to rewrite array items several times. .map() returns a new array, so just assign it to the current index:
var myArray = ran2Darray(5, 100000);
var offset = 2;
var performOperation = function(value, idx) {
return value += offset * idx;
}
for (i = 0; i < myArray.length; i++) {
console.time(i);
myArray[i] = myArray[i].map(performOperation)
console.timeEnd(i);
}
It takes like ~20ms to process.
Fiddle demo (open console)
Ok, Just a little modification and a bug fix in what you have presented here.
function addNumber(offset) {myArray[i] + offset*i; }) is not good.
myArray[i] is the first dimention of a 2D array why to add something to it?
function ran2Darray (row, col) {
var res = [];
for (var i = 0 ; i < row; i++) {
res[i] = [];
for (var j = 0; j < col; j++) {
res[i][j] = Math.random();
}
}
return res;
}
var oneMillion = 1000000;
var myArray = ran2Darray(10, oneMillion);
var offset = 2;
var startTime, endTime;
for (i = 0; i < myArray.length; i++) {
startTime = Date.now();
myArray[i] = myArray[i].map(function (offset) {
return (offset + i) * offset;
});
endTime = Date.now();
document.write(endTime - startTime);
document.write("</br>");
}
try it. It's really fast
https://jsfiddle.net/itaymer/8ttvzyx7/
I have a very big array which looks similar to this
var counts = ["gfdg 34243","jhfj 543554",....] //55268 elements long
this is my current loop
var replace = "";
var scored = 0;
var qgram = "";
var score1 = 0;
var len = counts.length;
function score(pplaintext1) {
qgram = pplaintext1;
for (var x = 0; x < qgram.length; x++) {
for (var a = 0, len = counts.length; a < len; a++) {
if (qgram.substring(x, x + 4) === counts[a].substring(0, 4)) {
replace = parseInt(counts[a].replace(/[^1-9]/g, ""));
scored += Math.log(replace / len) * Math.LOG10E;
} else {
scored += Math.log(1 / len) * Math.LOG10E;
}
}
}
score1 = scored;
scored = 0;
} //need to call the function 1000 times roughly
I have to loop through this array several times and my code is running slowly. My question is what the fastest way to loop through this array would be so I can save as much time as possible.
Your counts array appears to be a list of unique strings and values associated with them. Use an object instead, keyed on the unique strings, e.g.:
var counts = { gfdg: 34243, jhfj: 543554, ... };
This will massively improve the performance by removing the need for the O(n) inner loop by replacing it with an O(1) object key lookup.
Also, avoid divisions - log(1 / n) = -log(n) - and move loop invariants outside the loops. Your log(1/len) * Math.LOG10E is actually a constant added in every pass, except that in the first if branch you also need to factor in Math.log(replace), which in log math means adding it.
p.s. avoid using the outer scoped state variables for the score, too! I think the below replicates your scoring algorithm correctly:
var len = Object.keys(counts).length;
function score(text) {
var result = 0;
var factor = -Math.log(len) * Math.LOG10E;
for (var x = 0, n = text.length - 4; x < n; ++x) {
var qgram = text.substring(x, x + 4);
var replace = counts[qgram];
if (replace) {
result += Math.log(replace) + factor;
} else {
result += len * factor; // once for each ngram
}
}
return result;
}