Shuffle multiple javascript arrays in the same way - javascript

I've got two arrays
var mp3 = ['sing.mp3','song.mp3','tune.mp3','jam.mp3',etc];
var ogg = ['sing.ogg','song.ogg','tune.ogg','jam.ogg',etc];
i need to shuffle both arrays so that they come out the same way, ex:
var mp3 = ['tune.mp3','song.mp3','jam.mp3','sing.mp3',etc];
var ogg = ['tune.ogg','song.ogg','jam.ogg','sing.ogg',etc];
there's a few posts on stackoverflow that shuffle arrays in different ways--this one is pretty great--but none of them demonstrate how to shuffle two arrays in the same exact way.
thnx!

Add an extra argument to the Fisher-Yates shuffle. (assumes that your arrays are equal length)
var mp3 = ["sing.mp3", "song.mp3"];
var ogg = ["sing.ogg", "song.ogg"];
function shuffle(obj1, obj2) {
var index = obj1.length;
var rnd, tmp1, tmp2;
while (index) {
rnd = Math.floor(Math.random() * index);
index -= 1;
tmp1 = obj1[index];
tmp2 = obj2[index];
obj1[index] = obj1[rnd];
obj2[index] = obj2[rnd];
obj1[rnd] = tmp1;
obj2[rnd] = tmp2;
}
}
shuffle(mp3, ogg);
console.log(mp3, ogg);
UPDATE:
If you are going to support more arrays (as suggested in the comments), then you could modify the Fisher-Yates as follows (aswell as perform some checks to make sure that the arguments are of Array and that their lengths match).
var isArray = Array.isArray || function(value) {
return {}.toString.call(value) !== "[object Array]"
};
var mp3 = ["sing.mp3", "song.mp3", "tune.mp3", "jam.mp3"];
var ogg = ["sing.ogg", "song.ogg", "tune.ogg", "jam.ogg"];
var acc = ["sing.acc", "song.acc", "tune.acc", "jam.acc"];
var flc = ["sing.flc", "song.flc", "tune.flc", "jam.flc"];
function shuffle() {
var arrLength = 0;
var argsLength = arguments.length;
var rnd, tmp;
for (var index = 0; index < argsLength; index += 1) {
if (!isArray(arguments[index])) {
throw new TypeError("Argument is not an array.");
}
if (index === 0) {
arrLength = arguments[0].length;
}
if (arrLength !== arguments[index].length) {
throw new RangeError("Array lengths do not match.");
}
}
while (arrLength) {
rnd = Math.floor(Math.random() * arrLength);
arrLength -= 1;
for (argsIndex = 0; argsIndex < argsLength; argsIndex += 1) {
tmp = arguments[argsIndex][arrLength];
arguments[argsIndex][arrLength] = arguments[argsIndex][rnd];
arguments[argsIndex][rnd] = tmp;
}
}
}
shuffle(mp3, ogg, acc, flc);
console.log(mp3, ogg, acc, flc);

From that example, simply add a second parameter (your second array) and perform the operation on both arrays. You will just need to add and use a second temp, so you aren't overwriting your temps.
This should do the trick ASSUMING THE ARRAYS ARE THE SAME LENGTH:
function shuffle(array, array2) {
var counter = array.length, temp, temp2, index;
// While there are elements in the array
while (counter > 0) {
// Pick a random index
index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it
temp = array[counter];
temp2 = array2[counter];
array[counter] = array[index];
array2[counter] = array2[index];
array[index] = temp;
array2[index] = temp2;
}
}

I would seriously consider restructuring the way you're keeping track of the information, but in general you can separate out the shuffle itself from the stuff being shuffled. You need a function to generate a random permutation, and then a function to apply a permutation to an array.
function shuffle(o) { //v1.0
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
function permutation( length ) {
var p = [], i;
for (i = 0; i < length; ++i) p[i] = i;
return shuffle(p);
}
function permute( a, p ) {
var r = [];
for (var i = 0; i < a.length; ++i)
r.push(a[p[i]]);
for (i = 0; i < a.length; ++i)
a[i] = r[i];
}
Then you can create a single random permutation and apply it to any list (of the right length) you want.
var p = permutation( mp3.length );
permute(mp3, p);
permute(ogg, p);
permute(aac, p);
// etc
(Shuffle function taken from the SO question linked in the OP.)

If you have two arrays of length 2, #Xotic750s function always returns the same value. UseMath.round instead of Math.floor.
function shuffle_two_arrays_identically(arr1, arr2){
"use strict";
var l = arr1.length,
i = 0,
rnd,
tmp1,
tmp2;
while (i < l) {
rnd = Math.round(Math.random() * i)
tmp1 = arr1[i]
tmp2 = arr2[i]
arr1[i] = arr1[rnd]
arr2[i] = arr2[rnd]
arr1[rnd] = tmp1
arr2[rnd] = tmp2
i += 1
}}

<script>
var arrayList= ['a','b','c','d','e','f','g'];
arrayList.sort(function(){
return 0.5 - Math.random()
})
document.getElementById("output").innerHTML = arrayList;
<script>

Related

Infinity loop with generating random numbers?

I'm working on a script that creates random mathematical problems (simple questions). The problem is that there seems to be an infinite loop, and I can't figure out where or how my script can run without this part of the code.
https://codepen.io/abooo/pen/GyJKwP?editors=1010
var arr = [];
var lastArr = [];
while(lastArr.length<122){
arr.push('<br>'+Math.round(Math.random() * 10)+'+'+Math.round(Math.random() * 10)+'=');
lastArr=removeDuplicates(arr);
}
document.write(lastArr.join(' '));
alert(arr.length);
function removeDuplicates(arr){
let unique_array = []
for(let i = 0;i < arr.length; i++){
if(unique_array.indexOf(arr[i]) == -1){
unique_array.push(arr[i])
}
}
return unique_array
}
This is the working snippet without any infinity loop.
var arr = [];
while(arr.length < 121){
var randValue = '<br>'+Math.round(Math.random() * 10)+'+'+Math.round(Math.random() * 10)+'='
arr.push();
// Check duplicate elements before push
if ( arr.indexOf(randValue) == -1 ) arr.push(randValue);
}
document.write(arr.join(' '));
alert(arr.length);
It looks like you are trying to shuffle every possible outcome of adding two numbers from 0 to 10. Why not do that, rather than your attempt of "throw missiles into pidgeonholes and hope for the best"?
function generateArray(maxA, maxB) {
var arr = [],
a, b;
for (a = 0; a <= maxA; a++) {
for (b = 0; b <= maxB; b++) {
arr.push([a, b]);
}
}
return arr;
}
function shuffleArray(arr) {
// simple Fisher-Yates shuffle, modifies original array
var l = arr.length,
i, j, t;
for (i = l - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
function outputArray(arr) {
var i, l = arr.length;
for (i = 0; i < l; i++) {
document.write("<br />" + arr[i][0] + " + " + arr[i][1] + " = ");
}
}
var arr = generateArray(10, 10);
shuffleArray(arr);
outputArray(arr);
The line Math.round(Math.random() * 10) will give you 11 possible outcomes (from 0 to 10). That means there are 11*11 non-duplicates lastArr can hold.
As mentioned in the comments, it does not only take a long time for every possibility to occur, it is also impossible for lastArr to be longer than 121 (11*11), which means your loop cannot end, due to the condition while(lastArr.length<122).
Besides that there are better ways to achieve the desired result, changing your code to this will make it work:
var arr = [];
var lastArr = [];
while(lastArr.length<121){ // Here I change 122 to 121
arr.push('<br>'+Math.round(Math.random() * 10)+'+'+Math.round(Math.random() * 10)+'=');
lastArr=removeDuplicates(arr);
}
document.write(lastArr.join(' '));
alert(arr.length);
function removeDuplicates(arr){
let unique_array = []
for(let i = 0;i < arr.length; i++){
if(unique_array.indexOf(arr[i]) == -1){
unique_array.push(arr[i])
}
}
return unique_array
}

How to generate an addition equation for a number using only required set of numbers?

I want to generate an addition equation for a random number which is look likes
5+10+2 from a set of numbers i.e [1,2,5,10,50].
var randomNumber = Math.floor(Math.random() * (10 - 1 + 1)) + 1;
var setOfNums = [1,2,5,10,50];
var additionEquation; //ex: for randomNumber = 28; additionEquation = 10+10+5+2+1;
And the maximum number of elements in equation is 5.Is it possible in java script?Thanks in advance.
You may have multiple solutions. A dynamical programming approach could efficiently solve this;
function getCombos(a,t){
var h = {},
len = a.length,
n = 0;
for (var i = 0; i < len; i++){
n = a[i];
h[n] ? h[n].push([n]) : h[n] = [[n]];
for(var j = a[0]; j <= t-n; j++){
h[j] && (h[j+n] = h[j+n] ? h[j+n].concat(h[j].map(s => s.concat(n)))
: h[j].map(s => s.concat(n)));
}
}
return h[t] || [];
}
var arr = [1,2,5,10,50],
target = 28,
result = [];
console.time("combos");
result = getCombos(arr,target);
console.timeEnd("combos");
console.log(`${result.length} solutions found`);
console.log(JSON.stringify(result));
Then you may choose the shortest one among the results set. However calculating according to a maxlen will of couse save us from calculating excess results just to be filtered out later. So the following code only works up until the maxlen is achieved.
function getCombos(a,t,l){
var h = {},
len = a.length,
n = 0;
for (var i = 0; i < len; i++){
n = a[i];
h[n] ? h[n].push([n]) : h[n] = [[n]];
for(var j = a[0]; j <= t-n; j++){
h[j] && (h[j+n] = h[j+n] ? h[j+n].concat(h[j].reduce((r,s) => s.length < l ? (r.push(s.concat(n)),r) : r, []))
: h[j].reduce((r,s) => s.length < l ? (r.push(s.concat(n)),r) : r, []));
}
}
return h[t] || [];
}
var arr = [1,2,5,10,50],
target = 28,
maxlen = 5,
result = [];
console.time("combos");
result = getCombos(arr,target,maxlen);
console.timeEnd("combos");
console.log(result.length)
console.log(JSON.stringify(result));
I believe performance wise this can not be beaten easily. It takes only .190ms to get to the result [[1,2,5,10,10]].
This problem can be solved with a recursive combinations of all possible sums until we reach the target.
One solution contains maximum 5 as a length where 5 is the length of the setOfNums array.
var randomNumber = Math.floor(Math.random() * 30) + 1;
console.log(randomNumber);
var setOfNums = [1,2,5,10,50];
setOfNums=setOfNums.sort(function(a,b){
return b-a;
});
subset_sum(setOfNums,randomNumber);
function subset_sum(numbers, target, partial){
partial = partial || [];
s = partial.reduce(function(contor,elem){
return contor+elem;
},0);
if(s == target){
partial=partial.sort(function(a,b){
return a-b;
});
console.log("sum"+JSON.stringify(partial)+"="+ target);
}
if(s >= target)
return;
if(partial.length>numbers.length)
return;
numbers.forEach(function(number,index){
n = numbers[index];
subset_sum(numbers, target, partial.concat(n));
});
}

Javascript: Given an array, return a number of arrays with different ordered combinations of elements

I need code that takes an array, counts the number of elements in it and returns a set of arrays, each displaying a different combination of elements. However, the starting element should be the same for each array. Better to explain with a few examples:
var OriginalArray = ['a','b','c']
should return
results: [['a','b','c'], ['a','c','b']]
or for example:
var originalArray = ['a','b','c','d']
should return
[['a','b','c','d'], ['a','b','d', 'c'], ['acbd', 'acdb', 'adbc', 'adcb']]
Again note how the starting element, in this case 'a' should always be the starting element.
You can use Heap's algorithm for permutations and modify it a bit to add to result only if first element is equal to first element of original array.
var arr = ['a', 'b', 'c', 'd']
function generate(data) {
var r = [];
var first = data[0];
function swap(x, y) {
var tmp = data[x];
data[x] = data[y];
data[y] = tmp;
}
function permute(n) {
if (n == 1 && data[0] == first) r.push([].concat(data));
else {
for (var i = 0; i < n; i++) {
permute(n - 1);
swap(n % 2 ? 0 : i, n - 1);
}
}
}
permute(data.length);
return r;
}
console.log(generate(arr))
You have to do a .slice(1) to feed the rest of the array to a permutations function. Then you can use .map() to stick the first item to the front of each array in the result of permutations function.
If you will do this job on large sets and frequently then the performance of the permutations function is important. The following uses a dynamical programming approach and to my knowledge it's the fastest.
function perm(a){
var r = [[a[0]]],
t = [],
s = [];
if (a.length <= 1) return a;
for (var i = 1, la = a.length; i < la; i++){
for (var j = 0, lr = r.length; j < lr; j++){
r[j].push(a[i]);
t.push(r[j]);
for(var k = 1, lrj = r[j].length; k < lrj; k++){
for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj];
t[t.length] = s;
s = [];
}
}
r = t;
t = [];
}
return r;
}
var arr = ['a','b','c','d'],
result = perm(arr.slice(1)).map(e => [arr[0]].concat(e));
console.log(JSON.stringify(result));

JavaScript Permutations

I am trying to count the number of permutations that do not contain consecutive letters. My code passes tests like 'aabb' (answer:8) and 'aab' (answer:2), but does not pass cases like 'abcdefa'(my answer: 2520; correct answer: 3600). Here's my code:
function permAlone(str) {
var totalPerm = 1;
var result = [];
//assign the first letter
for (var i = 0; i < str.length; i++) {
var firstNum = str[i];
var perm = firstNum;
//create an array from the remaining letters in the string
for (var k = 0; k < str.length; k++) {
if (k !== i) {
perm += str[k];
}
}
//Permutations: get the last letter and change its position by -1;
//Keep changing that letters's position by -1 until its index is 1;
//Then, take the last letter again and do the same thing;
//Keep doing the same thing until the total num of permutations of the number of items in the string -1 is reached (factorial of the number of items in the string -1 because we already established what the very first letter must be).
var permArr = perm.split("");
var j = permArr.length - 1;
var patternsLeft = totalNumPatterns(perm.length - 1);
while (patternsLeft > 0) {
var to = j - 1;
var subRes = permArr.move(j, to);
console.log(subRes);
if (noDoubleLettersPresent(subRes)) {
result.push([subRes]);
}
j -= 1;
if (j == 1) {
j = perm.length - 1;
}
patternsLeft--;
}
}
return result.length;
}
Array.prototype.move = function(from, to) {
this.splice(to, 0, (this.splice(from, 1))[0]);
return this.join("");
};
function totalNumPatterns(numOfRotatingItems) {
var iter = 1;
for (var q = numOfRotatingItems; q > 1; q--) {
iter *= q;
}
return iter;
}
function noDoubleLettersPresent(str) {
if (str.match(/(.)\1/g)) {
return false;
} else {
return true;
}
}
permAlone('abcdefa');
I think the problem was your permutation algorithm; where did you get that from? I tried it with a different one (after Filip Nguyen, adapted from his answer to this question) and it returns 3600 as expected.
function permAlone(str) {
var result = 0;
var fact = [1];
for (var i = 1; i <= str.length; i++) {
fact[i] = i * fact[i - 1];
}
for (var i = 0; i < fact[str.length]; i++) {
var perm = "";
var temp = str;
var code = i;
for (var pos = str.length; pos > 0; pos--) {
var sel = code / fact[pos - 1];
perm += temp.charAt(sel);
code = code % fact[pos - 1];
temp = temp.substring(0, sel) + temp.substring(sel + 1);
}
console.log(perm);
if (! perm.match(/(.)\1/g)) result++;
}
return result;
}
alert(permAlone('abcdefa'));
UPDATE: In response to a related question, I wrote an algorithm which doesn't just brute force all the permutations and then skips the ones with adjacent doubles, but uses a logical way to only generate the correct permutations. It's explained here: Permutations excluding repeated characters and expanded to include any number of repeats per character here: Generate all permutations of a list without adjacent equal elements
I agree with m69, the bug seems to be in how you are generating permutations. I got 3600 for 'abcdefa' by implementing a different algorithm for generating permutations. My solution is below. Since it uses recursion to generate the permutations the solution is not fast, however you may find the code easier to follow, if speed is not important.
The reason for having a separate function to generate the array index values in the permutations was to verify that the permutation code was working properly. Since there are duplicate values in the input strings it's harder to debug issues in the permutation algorithm.
// Simple helper function to compute all permutations of string indices
function permute_indices_helper(input) {
var result = [];
if (input.length == 0) {
return [[]];
}
for(var i = 0; i < input.length; i++) {
var head = input.splice(i, 1)[0];
var tails = permute_indices_helper(input);
for (var j = 0; j < tails.length; j++) {
tails[j].splice(0, 0, head);
result.push(tails[j]);
}
input.splice(i, 0, head); // check
}
return result;
};
// Given an array length, generate all permutations of possible indices
// for array of that length.
// Example: permute_indices(2) generates:
// [[0,1,2], [0,2,1], [1,0,2], ... , [2, 0, 1]]
function permute_indices(array_length) {
var result = [];
for (var i = 0; i < array_length; i++) {
result.push(i);
}
return permute_indices_helper(result);
}
// Rearrange letters of input string according to indices.
// Example: "car", [2, 1, 0]
// returns: "rac"
function rearrange_string(str, indices) {
var result = "";
for (var i = 0; i < indices.length; i++) {
var string_index = indices[i];
result += str[string_index];
}
return result;
}
function permAlone(str) {
var result = 0;
var permutation_indices = permute_indices(str.length);
for (var i = 0; i < permutation_indices.length; i++) {
var permuted_string = rearrange_string(str, permutation_indices[i]);
if (! permuted_string.match(/(.)\1/g)) result++;
}
return result;
}
You can see a working example on JSFiddle.

Interleave array elements

What is a fast and simple implementation of interleave:
console.log( interleave([1,2,3,4,5,6] ,2) ); // [1,4,2,5,3,6]
console.log( interleave([1,2,3,4,5,6,7,8] ,2) ); // [1,5,2,6,3,7,4,8]
console.log( interleave([1,2,3,4,5,6] ,3) ); // [1,3,5,2,4,6]
console.log( interleave([1,2,3,4,5,6,7,8,9],3) ); // [1,4,7,2,5,8,3,6,9]
This mimics taking the array and splitting it into n equal parts, and then shifting items off the front of each partial array in sequence. (n=2 simulates a perfect halving and single shuffle of a deck of cards.)
I don't much care exactly what happens when the number of items in the array is not evenly divisible by n. Reasonable answers might either interleave the leftovers, or even "punt" and throw them all onto the end.
function interleave( deck, step ) {
var copyDeck = deck.slice(),
stop = Math.floor(copyDeck.length/step),
newDeck = [];
for (var i=0; i<step; i++) {
for (var j=0; j<stop; j++) {
newDeck[i + (j*step)] = copyDeck.shift();
}
}
if(copyDeck.length>0) {
newDeck = newDeck.concat(copyDeck);
}
return newDeck;
}
It could be done with a counter instead of shift()
function interleave( deck, step ) {
var len = deck.length,
stop = Math.floor(len/step),
newDeck = [],
cnt=0;
for (var i=0; i<step; i++) {
for (var j=0; j<stop; j++) {
newDeck[i + (j*step)] = deck[cnt++];
}
}
if(cnt<len) {
newDeck = newDeck.concat(deck.slice(cnt,len));
}
return newDeck;
}
And instead of appending the extras to the end, we can use ceil and exit when we run out
function interleave( deck, step ) {
var copyDeck = deck.slice(),
stop = Math.ceil(copyDeck.length/step),
newDeck = [];
for (var i=0; i<step; i++) {
for (var j=0; j<stop && copyDeck.length>0; j++) {
newDeck[i + (j*step)] = copyDeck.shift();
}
}
return newDeck;
}
can i has prize? :-D
function interleave(a, n) {
var i, d = a.length + 1, r = [];
for (i = 0; i < a.length; i++) {
r[i] = a[Math.floor(i * d / n % a.length)];
}
return r;
}
according to my tests r.push(... is faster than r[i] = ... so do with that as you like..
note this only works consistently with sets perfectly divisible by n, here is the most optimized version i can come up with:
function interleave(a, n) {
var i, d = (a.length + 1) / n, r = [a[0]];
for (i = 1; i < a.length; i++) {
r.push(a[Math.floor(i * d) % a.length]);
}
return r;
}
O(n-1), can anyone come up with a log version? to the mathmobile! [spinning mathman logo]
Without for loops (I've added some checkup for the equal blocks):
function interleave(arr, blocks)
{
var len = arr.length / blocks, ret = [], i = 0;
if (len % 1 != 0) return false;
while(arr.length>0)
{
ret.push(arr.splice(i, 1)[0]);
i += (len-1);
if (i>arr.length-1) {i = 0; len--;}
}
return ret;
}
alert(interleave([1,2,3,4,5,6,7,8], 2));
And playground :) http://jsfiddle.net/7tC9F/
how about functional with recursion:
function interleave(a, n) {
function f(a1, d) {
var next = a1.length && f(a1.slice(d), d);
a1.length = Math.min(a1.length, d);
return function(a2) {
if (!a1.length) {
return false;
}
a2.push(a1.shift());
if (next) {
next(a2);
}
return true;
};
}
var r = [], x = f(a, Math.ceil(a.length / n));
while (x(r)) {}
return r;
}
Phrogz was pretty close, but it didn't interleave properly. This is based on that effort:
function interleave(items, parts) {
var len = items.length;
var step = len/parts | 0;
var result = [];
for (var i=0, j; i<step; ++i) {
j = i
while (j < len) {
result.push(items[j]);
j += step;
}
}
return result;
}
interleave([0,1,2,3], 2); // 0,2,1,3
interleave([0,1,2,3,4,5,6,7,8,9,10,11], 2) // 0,6,1,7,2,8,3,9,4,10,5,11
interleave([0,1,2,3,4,5,6,7,8,9,10,11], 3) // 0,4,8,1,5,9,2,6,10,3,7,11
interleave([0,1,2,3,4,5,6,7,8,9,10,11], 4) // 0,3,6,9,1,4,7,10,2,5,8,11
interleave([0,1,2,3,4,5,6,7,8,9,10,11], 5) // 0,2,4,6,8,10,1,3,5,7,9,11
Since I've been pushed to add my own answer early (edited to fix bugs noted by RobG):
function interleave(items,parts){
var stride = Math.ceil( items.length / parts ) || 1;
var result = [], len=items.length;
for (var i=0;i<stride;++i){
for (var j=i;j<len;j+=stride){
result.push(items[j]);
}
}
return result;
}
try this one:
function interleave(deck, base){
var subdecks = [];
for(count = 0; count < base; count++){
subdecks[count] = [];
}
for(var count = 0, subdeck = 0; count < deck.length; count++){
subdecks[subdeck].push(deck[count]);
subdeck = subdeck == base - 1? 0 : subdeck + 1;
}
var newDeck = [];
for(count = 0; count < base; count++){
newDeck = newDeck.concat(subdecks[count]);
}
return newDeck;
}

Categories

Resources