Javascript Random problem? - javascript

var swf=["1.swf","2.swf","3.swf"];
var i = Math.floor(Math.random()*swf.length);
alert(swf[i]); // swf[1] >> 2.swf
This case ,Random output One number.
How to Random output two different numbers ?

var swf = ['1.swf', '2.swf', '3.swf'],
// shuffle
swf = swf.sort(function () { return Math.floor(Math.random() * 3) - 1; });
// use swf[0]
// use swf[1]
Even though the above should work fine, for academical correctness and highest performance and compatibility, you may want to shuffle like this instead:
var n = swf.length;
for(var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = swf[i];
swf[i] = swf[j];
swf[j] = tmp;
}
Credits to tvanfosson and Fisher/Yates. :)

You can use splice to remove the chosen element, then simply select another randomly. The following leaves the original array intact, but if that's not necessary you can use the original and omit the copy. Shown using a loop to demonstrate how to select an arbitrary number of times upto the size of the original array.
var swf=["1.swf","2.swf","3.swf"];
var elementsToChoose = 2;
var copy = swf.slice(0);
var chosen = [];
for (var j = 0; j < elementsToChoose && copy.length; ++j) {
var i = Math.floor(Math.random()*copy.length);
chosen.push( copy.splice(i,1) );
}
for (var j = 0, len = chosen.length; j < len; ++j) {
alert(chosen[j]);
}

I would prefer this way as the bounds are known (you are not getting a random number and comparing it what you already have. It could loop 1 or 1000 times).
var swf = ['1.swf', '2.swf', '3.swf'],
length = swf.length,
i = Math.floor(Math.random() * length);
firstRandom = swf[i];
// I originally used `delete` operator here. It doesn't remove the member, just
// set its value to `undefined`. Using `splice` is the correct way to do it.
swf.splice(i, 1);
length--;
var j = Math.floor(Math.random() * length),
secondRandom = swf[j];
alert(firstRandom + ' - ' + secondRandom);
Patrick DW informed me of delete operator just leaving the value as undefined. I did some Googling and came up with this alternate solution.
Be sure to check Tvanfosson's answer or Deceze's answer for cleaner/alternate solutions.

This is what I would do to require two numbers to be different (could be better answer out there)
var swf=["1.swf","2.swf","3.swf"];
var i = Math.floor(Math.random()*swf.length);
var j;
do {
j = Math.floor(Math.random()*swf.length);
} while (j === i);
alert(swf[i]);
alert(swf[j]);
Edit: should be j===i

Related

Resetting Data in an Array If Any Elements Are Equal

I've been having an issue with some code in a project I'm doing at school. I have an array of length 4, where each element of the array is an integer. The values of the integers are determined through multiplying Math.random() by the length of another array. The goal of my code is to find some way of checking whether any of the elements are equal to each other, and if they are then I would want to "reset" (redefine) them until they aren't equal. Here's what I just described, albeit in a very basic form:
var originalArray=[a, b, c, d, e, f, g];
var or1=Math.floor(Math.random() * originalArray.length);
var or2=Math.floor(Math.random() * originalArray.length);
var or3=Math.floor(Math.random() * originalArray.length);
var or4=Math.floor(Math.random() * originalArray.length);
var numArray=[or1, or2, or3, or4];
function resetData(){
or1=Math.floor(Math.random() * originalArray.length);
or2=Math.floor(Math.random() * originalArray.length);
or3=Math.floor(Math.random() * originalArray.length);
or4=Math.floor(Math.random() * originalArray.length);
numArray=[or1, or2, or3, or4];
}
I then have another function, at the beginning of which I want to run resetData. However, if any of the elements are equal to each other then I want it to run again until none of them are.
function startOperations(){
resetData();
//insert other code here
}
I'm stuck on what I should do to make it only reset under the previously-mentioned conditions. My initial idea was to do a for-loop, kind of like this:
for(i=0; i<songNumArray.length-1; i++){
if(songNumArray[i]==songNumArray[i+1]){
resetData();
}
}
But I now realize that this won't work, since this only checks if the elements next to each other are equal. So I tried just an if-statement:
if(songNumArray[0]==songNumArray[1]||songNumArray[0]==songNumArray[2]||songNumArray[0]==songNumArray[3]){
resetData();
}
This doesn't work either, of course, which is why I'm here. Does anyone on here have an idea of what I could do?
Rather than declaring variables for each element of the array first and putting them into an array later, I'd suggest simply using a array to start with - that way you can use the array methods. You also don't need to reset all of the elements in the array and retry until something works - it would be more elegant to try to generate new elements and insert them only if they pass. For example:
function createArrayWithoutDuplicates(highestValMinusOne, length) {
const highestVal = highestValMinusOne + 1;
const arr = [];
while (arr.length < length) {
let val;
do {
val = Math.floor(Math.random() * highestVal);
} while (arr.includes(val));
arr.push(val);
}
return arr;
}
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
console.log(createArrayWithoutDuplicates(10, 7));
You can do it with 2 for loops where first one is from first element to second last element and the second loop always starts with the next element that of the first loop and ends with last element of the array.
for(i = 0; i < songNumArray.length - 1; i++){
for(j = i + 1; j < songNumArray.length; j++) {
if(songNumArray[i] == songNumArray[j]){
resetData();
}
}
}
You could use a Set like this to look for duplicates:
let dupes;
do {
let set = new Set();
dupes = false;
for (let i = 0; i < songNumArray.length; ++i) {
if (set.has(songNumArray[i]) {
resetData();
dupes = true;
break;
}
else {
set.add(songNumArray[i]);
}
}
} while (dupes);
I've added a getUniqueNumber function that generates a unique number if it's a duplicate.
Commented code below;
var originalArray = [1, 2, 3, 4, 5, 6, 7];
var or1 = Math.floor(Math.random() * originalArray.length);
var or2 = Math.floor(Math.random() * originalArray.length);
var or3 = Math.floor(Math.random() * originalArray.length);
var or4 = Math.floor(Math.random() * originalArray.length);
var numArray = [or1, or2, or3, or4];
function getUniqueNumber(index) {
var val = numArray[index];
// check if subsequent values are equal to current value.
// if not return current value.
// no need to check previous values as they are already unique.
if (numArray.indexOf(val, index + 1) === -1)
return val;
// repeat until unique number is generated
while (true) {
val = Math.floor(Math.random() * originalArray.length);
if (numArray.indexOf(val) === -1)
return val;
}
}
function resetData() {
for (let i = 0; i < 4; i++) {
numArray[i] = getUniqueNumber(i);
}
}
function startOperations() {
console.log("before: " + numArray);
resetData();
console.log("after: " + numArray);
//insert other code here
}
startOperations();
Just use another for loop to iterate again. Something like this:
for(i=0; i<songNumArray.length; i++){
for(j=i+1; j<songNumArray.length; j++){
if(songNumArray[i]==songNumArray[j]){
resetData();
}
}
}
I would use nested for-loops. That way, we can cross reference all elements of the array with each other.
function newData() {
for (var i = 0; i < originalArray.length; i++) {
for (var j = 0; j < originalArray.length; j++) {
if (i != j && numArray[i] == numArray[j]) {
console.log('find');
numArray[i] = Math.floor(Math.random() * originalArray.length);
i = 0;
}
}
}
}
If you look at the if-statement, you can see I first check to make sure it doesn't equal j, since a number will always equal itself, and we don't want to cross reference the same elements in the array. Next, I check if they're the same. If they are, I set i to 0. This acts similar to recursion, it "resets" or "re-runs" the loop. If a number gets set to an already existing number, it will go through the if statement again, modifying it until it meets the criteria. Hope this helps!

Undefined Element Javascript Array

I have a problem. When I shuffle a array starting from position 1 and ending on position 49, one element seems to be undefined when outputted in the console.
for (var i = 49; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = genNr[i];
genNr[i] = genNr[j];
genNr[j] = temp;
}
Please help me.
I'm thinking that genNr contains 49 elements. The first time the loop runs, i will be 49 so you're trying to access genNr[49]. But, since array indexes start at zero, genNr[49] is undefined.
You need to start the loop with i equal to the length of the array minus 1, and stop when i is less than zero.
for (var i = genNr.length - 1; i >= 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = genNr[i];
genNr[i] = genNr[j];
genNr[j] = temp;
}

Adding two indexed geometries to a BufferGeometry

At the moment I'm using a set of predefined data (containing the indices, vertices and colors) and multiple THREE.Geometry's to add objects to a scene. As you could imagine, this is pretty slow as it requires adding and removing many objects at once or merging many together.
However if I used a single THREE.BufferGeometry this would allow me to use _gl.bufferSubData to add and remove objects and in theory should have minimal affect on the performance.
The trouble I'm having is putting this into practice. I already have the bufferSubData function working, however I'm unable to add two sets of data in the same BufferGeometry. I'm guessing this is because the data does not follow on from each other (as they're two separate objects) so they're both using the same indices. This image shows the result.
I've created a JSFiddle which uses an array named section containing the chunk data. If anyone could take a look and change it so it adds both sets of data I would really appreciate it:
http://jsfiddle.net/dUqwT/
Also, I've been unable to find the purpose of the index offset. If someone could link or explain what it's used for, that would be very helpful.
Thanks for the help!
Alright, worked out how it's done and I've updated the JSFiddle:
http://jsfiddle.net/dUqwT/1/
It was more simple than I thought and it had nothing to do with the index offset (still no idea what that does). I just ensured that it appends to each array correctly so positions, indices and colors aren't overwritten. Did that by using two variables and setting them to the appropriate lengths.
Realistically, as the objects I'm adding to the BufferGeometry will be dynamic, I'll need to give each object a certain amount of the buffer it can use instead of setting two vars to the length. That will allow me to then remove and change each object using _gl.bufferSubData.
for (var chunkID = 0; chunkID < 2; chunkID++) {
var chunkIndices = section[chunkID].indices;
var chunkVertices = section[chunkID].vertices;
var chunkColors = section[chunkID].colors;
var sectionXPos = chunkID * 32;
var sectionYPos = 0;
var sectionZPos = 0;
// Add indices to BufferGeometry
for ( var i = 0; i < chunkIndices.length; i ++ ) {
var q = chunkIndices[i];
var j = i * 3 + iLength;
indices[ j ] = (q[0] + vLength / 3) % chunkSize;
indices[ j + 1 ] = (q[1] + vLength / 3) % chunkSize;
indices[ j + 2 ] = (q[2] + vLength / 3) % chunkSize;
}
// Add vertices to BufferGeometry
for ( var i = 0; i < chunkVertices.length; i ++ ) {
var q = chunkVertices[i];
var j = i * 3 + vLength;
// positions
positions[ j ] = q[0] + sectionXPos;
positions[ j + 1 ] = q[1] + sectionYPos;
positions[ j + 2 ] = q[2] + sectionZPos;
// colors
var hexColor = chunkColors[i / 4];
color.set(hexColor);
colors[ j ] = color.r;
colors[ j + 1 ] = color.g;
colors[ j + 2 ] = color.b;
}
iLength += chunkIndices.length * 3;
vLength += chunkVertices.length * 3;
}

Javascript generate random unique number every time

Ok so i need to create four randomly generated numbers between 1-10 and they cannot be the same. so my thought is to add each number to an array but how can I check to see if the number is in the array, and if it is, re-generate the number and if it isnt add the new number to the array?
so basically it will go,
1.create new number and add to array
2.create second new number, check to see if it exist already, if it doesn't exist, add to array. If it does exist, re-create new number, check again etc...
3.same as above and so on.
You want what is called a 'random grab bag'. Consider you have a 'bag' of numbers, each number is only represented once in this bag. You take the numbers out, at random, for as many as you need.
The problem with some of the other solutions presented here is that they randomly generate the number, and check to see if it was already used. This will take longer and longer to complete (theoretically up to an infinite amount of time) because you are waiting for the random() function to return a value you don't already have (and it doesn't have to do that, it could give you 1-9 forever, but never return 10).
There are a lot of ways to implement a grab-bag type solution, each with varying degrees of cost (though, if done correctly, won't ever be infinite).
The most basic solution to your problem would be the following:
var grabBag = [1,2,3,4,5,6,7,8,9,10];
// randomize order of elements with a sort function that randomly returns -1/0/1
grabBag.sort(function(xx,yy){ return Math.floor(Math.random() * 3) - 1; })
function getNextRandom(){
return grabBag.shift();
};
var originalLength = grabBag.length;
for(var i = 0; i < originalLength; i++){
console.log(getNextRandom());
}
This is of course destructive to the original grabBag array. And I'm not sure how 'truly random' that sort is, but for many applications it could be 'good enough'.
An slightly different approach would be to store all the unused elements in an array, randomly select an index, and then remove the element at that index. The cost here is how frequently you are creating/destroying arrays each time you remove an element.
Here are a couple versions using Matt's grabBag technique:
function getRandoms(numPicks) {
var nums = [1,2,3,4,5,6,7,8,9,10];
var selections = [];
// randomly pick one from the array
for (var i = 0; i < numPicks; i++) {
var index = Math.floor(Math.random() * nums.length);
selections.push(nums[index]);
nums.splice(index, 1);
}
return(selections);
}
You can see it work here: http://jsfiddle.net/jfriend00/b3MF3/.
And, here's a version that lets you pass in the range you want to cover:
function getRandoms(numPicks, low, high) {
var len = high - low + 1;
var nums = new Array(len);
var selections = [], i;
// initialize the array
for (i = 0; i < len; i++) {
nums[i] = i + low;
}
// randomly pick one from the array
for (var i = 0; i < numPicks; i++) {
var index = Math.floor(Math.random() * nums.length);
selections.push(nums[index]);
nums.splice(index, 1);
}
return(selections);
}
And a fiddle for that one: http://jsfiddle.net/jfriend00/UXnGB/
Use an array to see if the number has already been generated.
var randomArr = [], trackingArr = [],
targetCount = 4, currentCount = 0,
min = 1, max = 10,
rnd;
while (currentCount < targetCount) {
rnd = Math.floor(Math.random() * (max - min + 1)) + min;
if (!trackingArr[rnd]) {
trackingArr[rnd] = rnd;
randomArr[currentCount] = rnd;
currentCount += 1;
}
}
alert(randomArr); // Will contain four unique, random numbers between 1 and 10.
Working example: http://jsfiddle.net/FishBasketGordo/J4Ly7/
var a = [];
for (var i = 0; i < 5; i++) {
var r = Math.floor(Math.random()*10) + 1;
if(!(r in a))
a.push(r);
else
i--;
}
That'll do it for you. But be careful. If you make the number of random numbers generated greater than the may number (10) you'll hit an infinite loop.
I'm using a recursive function. The test function pick 6 unique value between 1 and 9.
//test(1, 9, 6);
function test(min, max, nbValue){
var result = recursValue(min, max, nbValue, []);
alert(result);
}
function recursValue(min, max, nbValue, result){
var randomNum = Math.random() * (max-min);
randomNum = Math.round(randomNum) + min;
if(!in_array(randomNum, result)){
result.push(randomNum);
nbValue--;
}
if(nbValue>0){
recursValue(min, max, nbValue, result);
}
return result;
}
function in_array(value, my_array){
for(var i=0;i< my_array.length; i++){
if(my_array[i] == value){
console.log(my_array+" val "+value);
return true;
}
}
return false;
}
Here is a recursive function what are you looking for.
"howMany" parameter is count of how many unique numbers you want to generate.
"randomize" parameter is biggest number that function can generate.
for example : rand(4,8) function returns an array that has 4 number in it, and the numbers are between 0 and 7 ( because as you know, Math.random() function generates numbers starting from zero to [given number - 1])
var array = [];
var isMatch= false;
function rand(howMany, randomize){
if( array.length < howMany){
var r = Math.floor( Math.random() * randomize );
for( var i = 0; i < howMany; i++ ){
if( array[i] !== r ){
isMatch= false;
continue;
} else {
isMatch= true;
break;
}
}
if( isMatch == false ){
array.push(r);
ran(howMany, randomize);
}
ran(howMany, randomize);
return array;
}
}
In your answer earlier, you do have a small bug. Instead of
var originalLength = grabBag.length;
for(var i = 0; i < originalLength .length; i++){
console.log(getNextRandom());
}
I believe you meant:
var originalLength = grabBag.length;
for(var i = 0; i < originalLength; i++){
console.log(getNextRandom());
}
Thanks.

Sorting function?

I need to organize an array of strings of random length into the least number of new strings with a max size. Is there a function or something in javascript, or something that can be translated to javascript, that will do this?
For example, the new strings might have max lengths of 1000 characters. The array might have strings of lengths 100, 48, 29, etc. I would want to combine those strings into as few new strings as possible.
edit: Sorry if this doesn't make sense, I tried my best.
No standard method in Javascript, but plenty of theoretical work has been done on this (i.e. the bin packing problem).
http://en.wikipedia.org/wiki/Bin_packing_problem
Some sample pseudo code in the link - should be trivial to translate to javascript.
The algorithm shown isn't going to be optimal in every case. To find the optimal solution to your example you'll just need to iterate over every possibility which might not be that bad depending on how many strings you have.
For my own entertainment, I wrote a simple bin packing algorithm. I picked a simple algorithm which is to sort the input strings by length. Create a new bin. Put the first (longest remaining) string into the bin and then keep filling it up with the longest strings that will fit until no more strings will fit. Create a new bin, repeat. To test it, I allocate an array of strings of random lengths and use that as input. You can see the output visually here: http://jsfiddle.net/jfriend00/FqPKe/.
Running it a bunch of times, it gets a fill percentage of between 91-98%, usually around 96%. Obviously the fill percentage is higher if there are more short strings to fill with.
Here's the code:
function generateRandomLengthStringArrays(num, maxLen) {
var sourceChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY1234567890";
var sourceIndex = 0;
var result = [];
var len, temp, fill;
function getNextSourceChar() {
var ch = sourceChars.charAt(sourceIndex++);
if (sourceIndex >= sourceChars.length) {
sourceIndex = 0;
}
return(ch);
}
for (var i = 0; i < num; i++) {
len = Math.floor(Math.random() * maxLen);
temp = new String();
fill = getNextSourceChar();
// create string
for (var j = 0; j < len; j++) {
temp += fill;
}
result.push(temp);
}
return(result);
}
function packIntoFewestBins(input, maxLen) {
// we assume that none of the strings in input are longer than maxLen (they wouldn't fit in any bin)
var result = [];
// algorithm here is to put the longest string into a bin and
// then find the next longest one that will fit into that bin with it
// repeat until nothing more fits in the bin, put next longest into a new bin
// rinse, lather, repeat
var bin, i, tryAgain, binLen;
// sort the input strings by length (longest first)
input.sort(function(a, b) {return(b.length - a.length)});
while (input.length > 0) {
bin = new String(); // create new bin
bin += input.shift(); // put first one in (longest we have left) and remove it
tryAgain = true;
while (bin.length < maxLen && tryAgain) {
tryAgain = false; // if we don't find any more that fit, we'll stop after this iteration
binLen = bin.length; // save locally for speed/convenience
// find longest string left that will fit in the bin
for (i = 0; i < input.length; i++) {
if (input[i].length + binLen <= maxLen) {
bin += input[i];
input.splice(i, 1); // remove this item from the array
tryAgain = true; // try one more time
break; // break out of for loop
}
}
}
result.push(bin);
}
return(result);
}
var binLength = 60;
var numStrings = 100;
var list = generateRandomLengthStringArrays(numStrings, binLength);
var result = packIntoFewestBins(list, binLength);
var capacity = result.length * binLength;
var fillage = 0;
for (var i = 0; i < result.length; i++) {
fillage += result[i].length;
$("#result").append(result[i] + "<br>")
}
$("#summary").html(
"Fill percentage: " + ((fillage/capacity) * 100).toFixed(1) + "%<br>" +
"Number of Input Strings: " + numStrings + "<br>" +
"Number of Output Bins: " + result.length + "<br>" +
"Bin Legnth: " + binLength + "<br>"
);

Categories

Resources