I have an array like this
[0,2,3]
The possible shuffling of this array are
[0,2,3], [2,3,0], [3,0,2], [3,2,0], [0,3,2], [2,0,3]
How can I get these combinations? The only idea I have in mind currently is
n = maximum num of possible combinations, coms = []
while( coms.length <= n )
temp = shuffle( original the array );
if temp is there in coms
return
else
coms.push(temp);
But I do not think this is efficient, as here we are blindly depending on uniform distribution of shuffle method.
Is there alternative findings for this problem?
The first thing to note is that the number of permutations increases very fast with regard to the number of elements (13 elements = 6 bilion permutations), so any kind of algorithm that generates them will deteriorate in performance for a large enough input array.
The second thing to note is that since the number of permutations is very large, storing them in memory is expensive, so you're way better off using a generator for your permutations and doing stuff with them as they are generated.
The third thing to note is that recursive algorithms bring a large overhead, so even if you find a recursive solution, you should strive to get a non-recursive one. Obtaining a non-recursive solution if a recursive one exists is always possible, but it may increase the complexity of the code.
I have written a non recursive implementation for you, based on the Steinhaus–Johnson–Trotter algorithm (http://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm)
function swap(arr, a,b){
var temp = arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
function factorial(n) {
var val = 1;
for (var i=1; i<n; i++) {
val *= i;
}
return val;
}
function permute(perm, func){
var total = factorial(perm.length);
for (var j=0, i=0, inc=1; j<total; j++, inc*=-1, i+=inc) {
for (; i<perm.length-1 && i>=0; i+=inc) {
func.call(perm);
swap (perm, i, i+1);
}
func.call(perm);
if (inc === 1) {
swap(perm, 0,1);
} else {
swap(perm, perm.length-1, perm.length-2);
}
}
}
console.clear();
count = 0;
permute([1,2,3,4,5,6], function(){console.log(this); count++;});
console.log('There have been ' + count + ' permutations');
http://jsbin.com/eXefawe/2/edit
Try a recursive approach. Here's a hint: every permutation of [0,2,3] is either
[0] plus a permutation of [2,3] or
[2] plus a permutation of [0,3] or
[3] plus a permutation of [0,2]
As zodiac mentioned the best solution to this problem is a recursive one:
var permute = (function () {
return permute;
function permute(list) {
return list.length ?
list.reduce(permutate, []) :
[[]];
}
function permutate(permutations, item, index, list) {
return permutations.concat(permute(
list.slice(0, index).concat(
list.slice(index + 1)))
.map(concat, [item]));
}
function concat(list) {
return this.concat(list);
}
}());
alert(JSON.stringify(permute([1,2,3])));
Hope that helps.
All permutations of a set can be found by selecting an element in the set and recursively permuting (rearranging) the remaining elements. Backtracking approach can be used for finding the solution.
Algorithm steps (source):
Pseudocode (source):
permute(i)
if i == N output A[N]
else
for j = i to N do
swap(A[i], A[j])
permute(i+1)
swap(A[i], A[j])
Javascript implementation (jsFiddle):
Array.prototype.clone = function () {
return this.slice(0);
};
var input = [1, 2, 3, 4];
var output = [];
function permute(i) {
if (i == input.length)
output.push(input.clone());
else {
for (var j = i; j < input.length; j++) {
swap(i, j);
permute(i + 1);
swap(i, j); // backtrack
}
}
};
function swap(i, j) {
var temp = input[i];
input[i] = input[j];
input[j] = temp;
}
permute(0);
console.log(output);
For an array of length n, we can precompute the number of possible permutations. It's n! (n factorial)
function factorial(n){ return n<=0?1:n*factorial(n-1);}
//There are better ways, but just for illustration's sake
And, we can create a function which maps an integer p between 0...n!-1 to a distinct permutation.
function map(p,orgArr){
var tempArr=orgArr.slice(); //Create a copy
var l=orgArr.length;
var permArr=[];
var pick;
do{
pick=p%l; //mod operator
permArr.push(tempArr.splice(pick,1)[0]); //Remove item number pick from the old array and onto the new
p=(p-pick)/l;
l--;
}while(l>=1)
return permArr;
}
At this point, all you need to do is create an array ordering=[0,1,2,3,...,factorial(n)-1] and shuffle that. Then, you can loop for(var i=0;i<=ordering.length;i++) doSomething(map(ordering[i],YourArray));
That just leaves the question of how to shuffle the ordering array. I believe that's well documented and outside the scope of your question since the answer depends on your application (i.e. is pseudo random good enough, or do you need some cryptographic strength, speed desired, etc...). See How to randomize (shuffle) a JavaScript array? and many others.
Or, if the number of permutations is so large that you don't want to create this huge ordering array, you just have to distinctly pick values of i for the above loop between 0-n!-1. If just uniformity is needed, rather than randomness, one easy way would be to use primitive roots: http://en.wikipedia.org/wiki/Primitive_root_modulo_n
you don't need to recurse. The demo should make the pattern pretty clear: http://jsfiddle.net/BGYk4/
function shuffle(arr) {
var output = [];
var n = arr.length;
var ways = [];
for(var i = 0, j = 1; i < n; ways.push(j *= ++i));
var totalWays = ways.pop();
for(var i = 0; i < totalWays; i++) {
var copy = arr.slice();
output[i] = [];
for(var k = ways.length - 1; k >= 0; k--) {
var c = Math.floor(i/ways[k]) % (k + 2);
output[i].push(copy.splice(c,1)[0]);
}
output[i].push(copy[0]);
}
return output;
}
this should output all possible "shuffles"
console.log(shuffle(['a', 'b', 'c', 'd', 'e']));
this one's cuter (but the output from the other example is much clearer): http://jsfiddle.net/wCnLf/
function shuffle(list) {
var shufflings = [];
while(true) {
var clone = list.slice();
var shuffling = [];
var period = 1;
while(clone.length) {
var index = Math.floor(shufflings.length / period) % clone.length;
period *= clone.length;
shuffling.push(clone.splice(index,1)[0]);
}
shufflings.push(shuffling);
if(shufflings.length == period) return shufflings;
}
}
and of course it still outputs all possible "shuffles"
console.log(shuffle(['a', 'b', 'c', 'd', 'e']));
tibos example above was exactly what I was looking for but I had some trouble running it, I made another solution as an npm module:
var generator = new Permutation([1, 2, 3]);
while (generator.hasNext()) {
snippet.log(generator.next());
}
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="http://rawgit.com/bcard/iterative-permutation/master/iterative-permutation.js"></script>
https://www.npmjs.com/package/iterative-permutation
https://github.com/bcard/iterative-permutation
Related
What is the simplest way to get 50 random unique elements from an array of 1000 elements ?
text = new Array();
for(i=0;i<1000;i++){ text[i]=i; } //array populated
// now I need to get 50 random unique elements from this array.
The obvious (to me) way is to shuffle the array, then take the first fifty elements. This question has a good way to shuffle an array, and you can then slice the first fifty elements. This guarantees the elements will be unique.
So, using the function there:
fisherYates(text);
text = text.slice(0, 50);
Good algorithms explained in this topic (in C but you can easily to do same in JS)
Look into the Fisher-Yates algorithm, I think this will work for you.
This assumes you mean random indexes and not indexes with unique values.
One way is to copy the array and prune off the ones you use:
function getRandomIndexes( arr, cnt){
var randomArr = [],
arrCopy = arr.slice(),
i,
randomNum ;
for (i=0;i<arrCopy.length;i++) {
randomNum = Math.floor( arrCopy.length * Math.random());
randomArr = randomArr.concat( arrCopy.splice(randomNum ,1) );
}
return randomArr;
}
var myNums = [], i, randSet;
for (i=0;i<10;i++){
myNums.push(i);
}
randSet = getRandomIndexes(myNums, 5);
Another way is to keep track of the indexes you use and keep looking until you find one you did not use. I find the while loop to be scary, and personally would not use this solution if random indexes needed approaches close to the array length.
function getRandomIndexes( arr, cnt){
var randomArr = [],
usedNums = {},
x;
while (randomArr.length<cnt) {
while (usedNums[x]===true || x===undefined) {
x = Math.floor( Math.random() * arr.length);
}
usedNums[x] = true;
randomArr.push( arr[x] );
}
return randomArr;
}
var myNums = [], i, randSet;
for (i=0;i<10;i++){
myNums.push(i);
}
randSet = getRandomIndexes(myNums, 5);
In case you meant unique values:
Demo
var old_arr = [0,1,2,3,4,5,6,7,8,9], new_array = [];
for (var i = 0; i < 5; i++) {
var rand_elem = old_arr[Math.floor(Math.random() * old_arr.length)];
if (arrIndex(old_arr[rand_elem], new_array) == -1) {
new_array.push(rand_elem);
} else {
i--;
}
}
function arrIndex(to_find, arr) {//own function for IE support
if (Array.prototype.indexOf) {
return arr.indexOf(to_find);
}
for (var i = 0, len = arr.length; i < len; i++) {
if (i in arr && arr[i] === to_find) {
return i;
}
}
return -1;
}
In case you meant unique indexs:
Generate random indexes and store the indexes in an array and make checks to prevent duplicates
Start removing the elements of the array after you get them, (you might have problems if you cache the length, so don't)
var arr = [];
while(arr.length < 51){
var ind = Math.floor(Math.random()*1000);
if(!(ind in arr))
arr.push(ind)
}
You'll have 50 random unique numbers in the array arr, which you could use as index
EDIT:
As #ajax333221 mentioned, the previous code doesn't do to get unique elements from the array, in case it contains duplicates. So this is the fix:
var result_arr = [];
while(result_arr.length < 51){
var ind = Math.floor(Math.random()*1000);
if(text[ind] && !(text[ind] in result_arr))
result_arr.push(text[ind]);
}
Being 'text' the array populated with 1000 values
Math.random() * 1000;
Generate 50 random numbers and use them as the position in the array.
Having the set {a,b,c} I want find all the subsets in recursive manner. I have already solved this problem using bitmasking but I want to understand the way that a person stated in this youtube video here
There are other stackoverflow threads about this problem but I have not found any that are solving the way she states in the video, she says,
"take the subsets of a and b, clone them and then add c to all the clones"
I am having trouble picturing the "simple" recursive method that would accomplish this. Is the recursive method, once exhausted, have all the subsets of A,B and the clones of A,B (duplicates at this point) and then propagates back up adding C to only the clones?
In other words, I start with a for loop on the set, I call my recursive function, I then do a for loop of n-1 and call my recursive method in that for loop, I cannot see how I can get C to be added to already existing subset clones in an array that is being built with recursion.
function SubsetBuilder(set) {
this.set = set;
}
SubsetBuilder.prototype.getSubsetsRecursive = function () {
//Set = {a,b,c}
//take the subsets of a and b, clone them and then add c to all the clones
//subsets of {a,b}=
//{}
//{a}
//{b}
//{a,b}
var n = this.set.length;
var result = [];
var recurseForSubsets = function (prefix, index) {
for (var i = index; i < n -1; i ++) {
result.push(prefix + this.set[i]);
recurseForSubsets(prefix + this.set[i], i + 1);
}
}
for (var j = 0; j < n; j++) {
recurseForSubsets("", j);
}
return result;
}
SubsetBuilder.prototype.printSubsets = function () {
var self = this;
if (!self.set)
return;
var n = this.set.length;
for (var i = 0; i < (1 << n) ; i++) {
var subset = [];
for (var j = 0; j < n; j++) {
if (((i >> j) & 1) === 1) { // bit j is on
subset.push(this.set[j]);
}
}
console.log(subset);
}
}
var set = ['a', 'b', 'c'];
var obj = new SubsetBuilder(set);
//obj.printSubsets();
console.log(obj.getSubsetsRecursive());
I gave this a try and came up with
function getSubsets(inp) {
if (inp.length == 1) {
// return the single item set plus the empty set
return [inp, []];
} else {
var e = inp.pop();
var s = getSubsets(inp);
// duplicate the elements of s into s1 without creating references.
// this might not be the best technique
var s1 = s.concat(JSON.parse(JSON.stringify(s)));
// add e to the second group of duplicates
for (var i=s.length; i < s1.length; i++) {
s1[i].push(e);
}
return s1;
}
}
var set = ['a', 'b', 'c'];
var list = getSubsets(set);
console.log(list);
// result
// [["a"], [], ["a", "b"], ["b"], ["a", "c"], ["c"], ["a", "b", "c"], ["b", "c"]]
The lady in the video said that all subsets of {a,b,c} can be formed from taking all the subsets of {a,b} and appending c to each one. Not entirely accurate (a valid subset of {a,b,c} does not have to include c), but a starting place for the algorithm. I changed the rule to all subsets of {a,b,c} can be formed from taking two copies of the subsets of {a,b} and appending c to each element of the second copy.
I think I could get rid of or simplify the if, because essentially the second block of code does the same as the first, so it's not ideal.
To me it makes sense that the algorithm runs in O(2^n) because the results vary in the same way (3 elements in the input array = 2^3 elements in the output array) - you might have to forgive my use of JSON to assume that complexity though. I'd find a better way to deep clone the array, even so, that might add more complexity.
Its a very small issue and for the life of me I can't figure out what it is. My brain has locked itself from thinking. I need someone else to have a look this code.
The output of the code should be: [1,0,0,0]
UPDATE:
The function should be able to read an array of numbers and if it finds any zeros within the array it should move them to the end of the array.
The output of the code keeps coming as: [0,1,0,0]
var arrNum = [0,0,0,1];
function test() {
for(var i=0; i<arrNum.length; i++){
if(arrNum[i] == 0){
arrNum.splice(i,1)
arrNum.splice(arrNum.length, 1, 0)
}
}
return alert(arrNum)
}
Here is a working plunker.
Apologies for this, I know the issue is something very small but my brain has stopped working now and I need a fresh pair of eyes.
With the way you have it written, you need to loop in the reverse order. You end up skipping indexes when you remove the index. Looping in the reverse direction keeps you from skipping them.
for(var i=arrNum.length-1; i>=0; i--){
You can use unshift() to insert at beginning of an array and push() to the end...
var arrNum = [0,0,0,1];
var output = [];
function test()
{
for(var i=0; i<arrNum.length; i++)
{
if(arrNum[i] == 0)
output.push(0);
else
output.unshift(arrNum[i]);
}
return alert(output)
}
var arrNum = [0,0,0,1];
var result = [];
arrNum.forEach(function(v) {
!!v ? result.unshift(v) : result.push(v);
});
console.log(result);
You are iterating with index i = 0,1,2,3 and at the same time removing first elements of array. So your iteration can not see the 1, it jumps over as it is moved to already iterated index. Easiest would be to just reverse the loop to bypass the issue.
var arrNum = [0,0,0,1];
function test() {
for(var i= arrNum.length; i >= 0; i--){
if(arrNum[i] == 0){
arrNum.splice(i,1)
arrNum.splice(arrNum.length, 1, 0)
}
}
return alert(arrNum)
}
Prefer built-in functions every time possible.
var output = [];
[0,0,0,1].forEach(function(num) {
if(num == 0) output.push(0);
else output.unshift(num)
})
Why don't you use a temporary array to help? The problem with your code is that the splice() function modifies the original array, and you are doing it inside the loop.
The code below produces what you need:
var arrNum = [0,0,0,1];
var arrResult = new Array();
function test() {
for(var i=arrNum.length-1; i>=0; i--)
{
arrResult.push(arrNum[i]);
}
arrNum = arrResult;
return alert(arrNum);
}
With another array to store the new values, you gain flexibility to do whatever you need with the data of the first array.
A nice little way using Objects - busy learning them so just posting a variation of deligation
var methods = {
moveZero: function(arr){
//console.log(arr);
var newArr = [];
for(var i = 0; i < arr.length; i++){
if(arr[i] === 0){
newArr.push(arr[i]);
}else{
newArr.unshift(arr[i]);
}
}
console.log(newArr);
}
}
var arrNum = Object.create(methods);
arrNum.moveZero([0,0,50,56,85,0,0,43,10,0,1]);
JSFiddle - https://jsfiddle.net/ToreanJoel/qh0xztgc/1/
The problem was you are modifying an array while looping over it in if statement.
Here is a working plunker of your example.
var len = arrNum.length;
var index = 0;
while(len) {
if(arrNum[index] == 0) {
arrNum.splice(index,1);
arrNum.push(0);
} else {
++index;
}
--len;
}
As the operation you want to do is actually sorting, for readability and compactness of code maybe you should be doing this instead:
var arrNum = [0,1,0,0];
arrNum.sort(function(a, b) {
return a == 0 ? 1 : 0;
});
It can contain any number and will keep order of others than 0
Ok, this might be easy for some genius out there but I'm struggling...
This is for a project I'm working on with a slider, I want an array the slider can use for snap points/increments... I'm probably going about this in a mental way but its all good practice! Please help.
var frootVals = [1,2,3,4,5];
var frootInc = [];
for (i=0; i<=frootVals.length; i++) {
if (i == 0){
frootInc.push(frootVals[i]);
}
else{
frootInc.push(frootInc[i-1] += frootVals[i])
}
};
What I'm trying to do is create the new array so that its values are totals of the array elements in frootVals.
The result I'm looking for would be this:
fruitInc = [1,3,6,10,15]
For a different take, I like the functional approach:
var frootVals = [1,2,3,4,5];
var frootInc = [];
var acc = 0;
frootVals.forEach(function(i) {
acc = acc + i;
frootInc.push(acc);
});
var frootVals = [1,2,3,4,5]
, frootInc = [];
// while i < length, <= will give us NaN for last iteration
for ( i = 0; i < frootVals.length; i++) {
if (i == 0) {
frootInc.push(frootVals[i]);
} else {
// rather than frootIne[ i-1 ] += ,
// we will just add both integers and push the value
frootInc.push( frootInc[ i-1 ] + frootVals[ i ] )
}
};
There were a few things wrong with your code check out the commenting in my code example. Hope it helps,
This will do:
var frootVals = [1,2,3,4,5];
var frootInc = [];
for (i=0; i < frootVals.length; i++) { // inferior to the length of the array to avoid iterating 6 times
if (i == 0) {
frootInc.push(frootVals[i]);
}
else {
frootInc.push(frootInc[i-1] + frootVals[i]) // we add the value, we don't reassign values
}
};
alert(JSON.stringify(frootInc));
jsfiddle here: http://jsfiddle.net/f01yceo4/
change your code to:
var frootVals = [1,2,3,4,5];
var frootInc = [frootvals[0]]; //array with first item of 'frootVals' array
for (i=1; i<frootVals.length; i++) {
frootInc.push(frootInc[i-1] + frootVals[i]); //remove '='
}
Here's a very simple pure functional approach (no vars, side-effects, or closures needed):
[1,2,3,4,5].map(function(a){return this[0]+=a;}, [0]);
// == [1, 3, 6, 10, 15]
if you name and un-sandwich the function, you can use it over and over again, unlike a hard-coded var name, property name, or for-loop...
I'm working on an application that uses nested arrays in Javascript. I have an array like this:
var a = ["Once upon a time there was a man."];
a[1] = ["He was tall."];
a[1][1] = ["He often bumped his head."];
a[2] = ["He was short."];
a[2][1] = ["He couldn't reach high shelves."]
Is there an easy way to figure out what level of an array a value is at? I'd like to be able to input "He often bumped his head" and have it return "a[1][1]" (or if I input "He was short" I'd get back "a[2]"). The arrays will be of indeterminate and fluctuating size.
Thanks in advance for any help with this. I'm new to javascript and jquery, so any explanation of your solution will be highly appreciated. Thanks again.
For a two level array, you could do it like this:
function findMultiArray(array, str) {
var innerArray;
for (var i = 0; i < array.length; i++) {
innerArray = array[i];
for (var j = 0; j < innerArray.length; j++) {
if (innerArray[j] == str) {
return([i, j]);
}
}
}
return null;
}
Note, I had it just return an actual array with the two indexes since that's a little easier to use if you need the data in data form.
Unlike in your request, it will never return the equivalent of a[1] because a[1] is an array, not a string so the match is with a[1][0], not with a[1]. In that case, it would return [1,0].
If you want the return value in a different form, there's just one line of code where is sees the match and i and j are the indexes that matched.
If the level of arrays can be arbitrarily deep with different depths in different places, then that would be a little more involved and would require interrogating the type of items to see if they contained nested arrays or just strings and would require some sort of stack to maintain the current position. It would be easiest to do with recursion.
Some recursion should do the trick:
Array.prototype.recursiveIndexOf = function(item, start) {
var i = this.indexOf(item);
var r, c;
start = start || [];
if(i > -1) {
return start.concat([i]);
}
for(i = 0; i < this.length; i++) {
c = this[i];
if(Object.prototype.toString.call(c) === '[object Array]') {
r = c.recursiveIndexOf(item, start.concat(i));
if(r !== null) {
return r;
}
}
}
return null;
};
Here's a demo.
Here is a much simpler answer. ;)
function array_search(ob,str)
{
for(var i=0;i<ob.length;++i)
{
if(typeof(ob[i])=='object'&&(ob[i] instanceof Array))
{
var foo=array_search(ob[i],str);
if(foo!=null)
return i+'->'+foo;
}else
if(typeof(ob[i])=='string'&&ob[i]==str)
{
return i;
}
}
return null;
}
var a = ["Once upon a time there was a man."];
a[1] = ["He was tall."];
a[1][1] = ["He often bumped his head."];
a[2] = ["He was short."];
a[2][1] = ["He couldn't reach high shelves."]
alert(array_search(a,'He was short.'));