how to get multiple random elements from an array list? - javascript

The title basically describes my problem... I wanted to get 3 elements from an array list without getting a duplicate. Because the others I have found uses the Math.floor((Math.random() * list.length)) which I think is only limited to one output.

n unique elements from a set are a combination.
Without getting in too much detail,
combinations = variations * permutations, which means we can just generate a variation (of the same length) and ignore the order.
The Fisher-Yates shuffle can do this for example:
function shuffled(elements){
// Return shuffled elements such that each variation has the same probability
const copy = [...elements];
for(let i = copy.length - 1; i >= 0; i--){
let j = Math.floor(Math.random() * (i + 1)); // 0 <= j <= i
let tmp = copy[i];
copy[i] = copy[j];
copy[j] = tmp;
}
return copy;
}
function choose(elements, n){
// Return a combination of n elements
return shuffled(elements).slice(0, n);
}
var elements = ['a', 'b', 'c', 'd'];
var N = 1000;
var results = {}; // how many times each element was chosen
for(let i = 0; i < N; i++){
for(let x of choose(elements, 3)){
results[x] = (results[x] || 0) + 1;
}
}
console.log(results); // 3/4 * N = 750

Related

How to stop random letter picking from doing duplicate letters [duplicate]

I need help with writing some code that will create a random number from an array of 12 numbers and print it 9 times without dupes. This has been tough for me to accomplish. Any ideas?
var nums = [1,2,3,4,5,6,7,8,9,10,11,12];
var gen_nums = [];
function in_array(array, el) {
for(var i = 0 ; i < array.length; i++)
if(array[i] == el) return true;
return false;
}
function get_rand(array) {
var rand = array[Math.floor(Math.random()*array.length)];
if(!in_array(gen_nums, rand)) {
gen_nums.push(rand);
return rand;
}
return get_rand(array);
}
for(var i = 0; i < 9; i++) {
console.log(get_rand(nums));
}
The most effective and efficient way to do this is to shuffle your numbers then print the first nine of them. Use a good shuffle algorithm.What Thilo suggested will give you poor results. See here.
Edit
Here's a brief Knuth Shuffle algorithm example:
void shuffle(vector<int> nums)
{
for (int i = nums.size()-1; i >= 0; i--)
{
// this line is really shorthand, but gets the point across, I hope.
swap(nums[i],nums[rand()%i]);
}
}
Try this once:
//Here o is the array;
var testArr = [6, 7, 12, 15, 17, 20, 21];
shuffle = function(o){ //v1.0
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
shuffle(testArr);
This is relatively simple to do, the theory behind it is creating another array which keeps track of which elements of the array you have used.
var tempArray = new Array(12),i,r;
for (i=0;i<9;i++)
{
r = Math.floor(Math.random()*12); // Get a random index
if (tempArray[r] === undefined) // If the index hasn't been used yet
{
document.write(numberArray[r]); // Display it
tempArray[r] = true; // Flag it as have been used
}
else // Otherwise
{
i--; // Try again
}
}
Other methods include shuffling the array, removing used elements from the array, or moving used elements to the end of the array.
If I understand you correctly, you want to shuffle your array.
Loop a couple of times (length of array should do), and in every iteration, get two random array indexes and swap the two elements there. (Update: if you are really serious about this, this may not be the best algorithm).
You can then print the first nine array elements, which will be in random order and not repeat.
Here is a generic way of getting random numbers between min and max without duplicates:
function inArray(arr, el) {
for(var i = 0 ; i < arr.length; i++)
if(arr[i] == el) return true;
return false;
}
function getRandomIntNoDuplicates(min, max, DuplicateArr) {
var RandomInt = Math.floor(Math.random() * (max - min + 1)) + min;
if (DuplicateArr.length > (max-min) ) return false; // break endless recursion
if(!inArray(DuplicateArr, RandomInt)) {
DuplicateArr.push(RandomInt);
return RandomInt;
}
return getRandomIntNoDuplicates(min, max, DuplicateArr); //recurse
}
call with:
var duplicates =[];
for (var i = 1; i <= 6 ; i++) {
console.log(getRandomIntNoDuplicates(1,10,duplicates));
}
const nums = [1,2,3,4,5,6,7,8,9,10,11,12];
for(var i = 1 ; i < 10; i++){
result = nums[Math.floor(Math.random()*nums.length)];
const index = nums.indexOf(result);
nums.splice(index, 1);
console.log(i+' - '+result);
}

Random array between given range of integers, containing at least one instance of each integer in the range

I'm trying to generate an array that contains a random set of integers between a range that will contain at least one instance of each possible integer. I can generate an array with random integers between a range, but cannot figure out how to generate one that contains at least one instance of each integer in said range. I'm trying to generate an array that contains 84 keys with a range between 0 and 40 (including 0 and 40). The array should contain at least one instance of each possible integer in said range. This is the code that I have right now:
<script>
ints = [];
function sortInt(a, b) {
return a - b;
}
for (i=0; i<84; i++) {
ints.push(Math.round(Math.random() * 40));
}
ints.sort(sortInt);
for (s=0; s<ints.length; s++) {
document.write(ints[s] + '<br />');
}
</script>
Please try the below snippet
function generateNumbers(number, startRange, endRange) {
if (startRange > endRange) return generateNumbers(number, endRange, startRange);
const arr = [];
for (let i = startRange; i <= endRange; ++i) arr.push(i);
while(number > arr.length) {
const newNumber = Math.round(Math.random() * (endRange - startRange));
arr.push(newNumber);
}
arr.sort((a, b) => a - b);
return arr;
}
console.log(generateNumbers(84, 0, 40));
I think if it needs all the integers between a range then you can start with generating between the range. Then all the other numbers can be random. Finally just need to use sort() on the array.
Like this:
const generate = (till, count) => {
const array = Array.from(new Array(till + 1).keys());
for (let i = till; i < count; i++) {
array.push(Math.round(Math.random() * till));
}
return array.sort((a,b) => a - b);
}
const result = generate(40, 84);
console.log(result);
I hope that helps!
You can create random index to insert a number from the desired range:
if (i == randomIndex)
ints.push(startRange);
else
ints.push(Math.floor(Math.random() * endRange) + startRange);
An example:
ints = [];
let startRange = 1, endRange = 84;
let randomIndex = Math.floor(Math.random() * endRange) + startRange;
for (i=0; i < 84; i++) {
if (i == randomIndex)
ints.push(startRange);
else
ints.push(Math.floor(Math.random() * endRange) + startRange);
}
ints.sort((a,b) => a - b);
console.log(ints)
I'd use randojs.com for this. Grab a shuffled array from 0 to 40, then add 43 more random numbers between 0 and 40 to the array and sort:
var ints = randoSequence(40);
for(var i = 0; i < 43; i++) ints.push(rando(40));
console.log( ints.sort((a, b) => {return a - b;}) );
<script src="https://randojs.com/1.0.0.js"></script>

Strange performance quirk in javascript

I experience a strange differences in performance of simple task depending of array I am working with. The task is to calculate sum of those elements in array which are greater than 5. Task is performed on arrays with equal lenghts.
I try the very same approach on three different array objects:
1) var hugeArray1 - array with all elemenets randomly picked from 0:10 range
2) var hugeArray2 - copy of hugeArray1 sorted with Array.prototype.sort()
3) var hugeArray3 - handcrafted but sorted array with values from 0:10 range, spread to equaly cover this interval.
I try to calculate sum of elements greater than 5 many times for each Array and then average them. What is strange, time needed varies a lot for those three arrays.
1) hugeArray1: 5.805ms
2) hugeArray2: 15.738ms
3) hugeArray3: 3.753ms
Result for array sorted with sort() is extreamly poor. Why is that? it looks like sort() returns some kind of wraper/proxy instead of 'native' Array, which affects performance. I tried it on 2 computers. Also i tried to change order of testing.
I include code below, please tell me what is happening here.
// random array with elements 0-10 of size n
function randomArray(n) {
var arr = [];
for (var i = 0; i < n; ++i) {
arr.push(Math.random() * 10);
}
return arr;
};
// measures time of execution
function measureTime(f) {
var start = new Date().getTime();
f();
var stop = new Date().getTime();
return stop - start;
};
// enumerate ofer array and calculate sum of elementsgreater than 5
function sumGreaterThan5(arr) {
var sum = 0;
for (var i = 0; i < arr.length; ++i) {
if (arr[i] > 5.0)
sum += arr[i];
}
return sum;
}
// generate array os size 'size' with elements with constant step to fill interval 0:10
function generateSortedArr(size) {
var arr = [];
for (var i = 0; i < size; ++i) {
arr.push(i * 10 / size);
}
return arr;
}
var huge = 1000000;
var hugeArray1 = randomArray(huge);
var hugeArray2 = hugeArray1.slice(0).sort();
var hugeArray3 = generateSortedArr(huge);
var hugeArrays = [hugeArray1, hugeArray2, hugeArray3];
hugeArrays.forEach(x=> {
var res = [];
for (var i = 0; i < 1000; ++i) {
res.push(measureTime(function () {
sumGreaterThan5(x);
}));
}
console.log(res.reduce((prev, curr)=> prev + curr) / res.length);
});
// random array with elements 0-10 of size n
function randomArray(n) {
var arr = [];
for (var i = 0; i < n; ++i) {
arr.push(Math.random() * 10);
}
return arr;
};
// measures time of execution
function measureTime(f) {
var start = new Date().getTime();
f();
var stop = new Date().getTime();
return stop - start;
};
// enumerate ofer array and calculate sum of elementsgreater than 5
function sumGreaterThan5(arr) {
var sum = 0;
for (var i = 0; i < arr.length; ++i) {
if (arr[i] > 5.0)
sum += arr[i];
}
return sum;
}
// generate array os size 'size' with elements with constant step to fill interval 0:10
function generateSortedArr(size) {
var arr = [];
for (var i = 0; i < size; ++i) {
arr.push(i * 10 / size);
}
return arr;
}
var huge = 1000000;
var hugeArray1 = randomArray(huge);
var hugeArray2 = hugeArray1.slice(0).sort();
var hugeArray3 = generateSortedArr(huge);
var hugeArrays = [hugeArray1, hugeArray2, hugeArray3];
hugeArrays.forEach(function(x){
var res = [];
for (var i = 0; i < 1000; ++i) {
res.push(measureTime(function () {
sumGreaterThan5(x);
}));
}
console.log(res.reduce(function(prev, curr){return prev + curr},0) / res.length);
});

Need to increase performance in function that finds earliest pair that equals a given sum

I have a function that takes an array and a number. It scans the array for the two numbers that appear earliest in the array that add up to the number. I would like to know, performance-wise, what could help this function run faster. It has to process a list of like 10,000,000 items in under 6 seconds. I have refactored it a few times now, but still not getting there.
What is the best array iteration method for speed? I assumed for loops would be the slowest so I chose map. Is there a faster way? every()?
NOTE: the provided array could have duplicate, positive, or negative numbers (let's say up to 1000000...for now).
var low_pair = function (ints, s) {
var lowNum = ints.length, lowMatch, highNum, clone = [], i;
for (i = 0; i < ints.length; i++) {
clone[i] = ints.map(function (n, ind) {
if (ind !== i && ints[i] + n == s) {
i > ind ? highNum = i : highNum = ind;
if (highNum < lowNum) {
lowNum = highNum;
lowMatch = [ints[i], ints[ind]];
}
}
});
}
return lowMatch;
};
We are going to create a function that returns the earliest pair of elements that add up to the needed sum:
function find_pair(l, s) {
var min_indexes = {};
for (var i = 0; i < l.length; i++) {
var a = l[i];
if ((s - a) in min_indexes)
return [s - a, a];
if (!(a in min_indexes))
min_indexes[a] = i;
}
}
For this purpose, for every number we process, we store its minimum index. If we currently process number a, we check if s - a has its minimum index set. If yes, this means we found our wanted sum and we return both elements.
For example:
> var l = [2, 3, 4, 5, 5, 7, 8]
> find_pair(l, 10)
[5, 5]
> find_pair(l, 6)
[2, 4]
> find_pair(l, 5)
[2, 3]
> find_pair(l, 15)
[7, 8]
> find_pair([5, 9, 13, -3], 10)
[13, -3]
What is the best array iteration method for speed?
See What's the fastest way to loop through an array in JavaScript? for that. But notice the answers there might be deprecated, and current engines are better at optimising different things. You should always benchmark yourself, in your own target environment.
However, instead of looking for raw speed and microoptimisations, you should try to improve your algorithm. In your case, you can double the speed of your function by simply starting the inner loop at i so you don't visit all combinations twice. Also, by returning early from the function you can speed up the average case (depending on your data). To find the "earliest pair" you don't have to loop through the entire array and calculate a minimum, you just have to iterate the pairs in the chosen order. If the data is ordered (or at least skewed to some distribution) you could take advantage of that as well.
I'd use
function firstPair(ints, s) {
var len = ints.length;
for (var end = 0; end < len; end++)
for (var i=0, j=end; i<end; i++)
if (i != --j && ints[i]+ints[j] == s)
return [i, j];
for (var start = 0; start < len; start++)
for (var i=start, j=len; i<len; i++)
if (i != --j && ints[i]+ints[j] == s)
return [i, j];
return null;
}
As suggested by the other answers, if the range of the values in your array is limited, you could drastically reduce the complexity of your algorithm by using a lookup table - trading memory for performance. Using a bitmap for already-occured integers, it could look like this:
function firstPair(ints, s) {
var map = []; // or, if domain is known and typed arrays are supported, use
// var map = new Uint16Array(Math.ceil(domainSize / 16));
for (var i=0; i<ints.length; i++) {
var x = ints[i],
r = s - x;
if (map[r >> 4] & (1 << (r & 0xF))) // get
return [r, x];
map[x >> 4] |= 1 << (x & 0xF); // set
}
return null;
}
The main problem is that your current function complexity is O(n^2) which is way too high for a 10000000 element array. The map function iterates through the entire array. So you make 10000000 * 10000000 = 100 trillion "operations". The complexity needs to be decreased. My best guess -> use a hash table within a linear loop. Below is my example code with a worst case test of 10 million elements that runs in around 8 seconds on my old machine. It makes only 10 million runs instead of 100 trillion.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var low_pair = function (ints, s) {
var found = {};
var lowMatch;
for (var i = 0; i < ints.length; i++) {
var num = ints[i];
var prevNum = s-num;
if (found[prevNum] === true){
if (prevNum>num){
lowMatch = [num, prevNum];
} else {
lowMatch = [prevNum, num];
}
break;
} else {
found[num] = true;
}
}
return lowMatch;
};
var test_array_size = 10000000;
var test_array = new Array(test_array_size);
for (var i=0;i<test_array_size;++i){
test_array[i] = test_array_size-i;
}
console.log("Array initialized");
var start = new Date().getTime();
console.log(low_pair(test_array, 12));
var end = new Date().getTime();
console.log("Running time: "+(end-start)+" ms");
</script>
<head>
<body>
</body>
</html>
This function consistently runs a 50,000,000 items array in 0ms:
var low_pair = function (s) {
var ints = [];
for(var i = 0; i < 50000000; i++) {
ints.push(Math.floor(Math.random() * 9));
}
console.time('pair');
var counter = 1;
for (var i = 0; i < ints.length; i++) {
var sum = ints[i] + ints[counter];
if (i !== counter) {
if (sum === s) {
console.timeEnd('pair');
return console.log([ints[i], ints[counter]]);
}
}
if (i == counter) {
counter++;
i = -1;
}
}
console.time('pair');
console.log( undefined);
};

Fastest way to loop through this array in Javascript on Chrome 36

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;
}

Categories