This question already has answers here:
Javascript random ordering with seed
(4 answers)
Closed 1 year ago.
I'm trying to predictably shuffle javascript arrays the same way each time the webpage is loaded.
I can shuffle the arrays randomly, but every time i reload the page it's a different sequence.
I'd like it to shuffle the arrays the same way every time the page loads. There are many arrays and they are part of a procedurally generated world.
Chance.js worked perfectly. Thank you Billy Moon.
My Example:
<script type="text/javascript" src="assets/js/chance.js"></script>
var chance1 = new Chance(124); // you can choose a seed here, i chose 124
console.log(chance1.shuffle(['alpha', 'bravo', 'charlie', 'delta', 'echo']));
// Array [ "alpha", "delta", "echo", "charlie", "bravo" ]
As long as you set the seed with new Chance(xxx) you get the same result every time.
Take a look at chancejs.com's seed function.
In order to shuffle an array in a seemingly random and predetermined way, you can break the problem into two parts.
1. Generate pseudo random numbers
You could use a different PRNG, but the Xorshift is very simple, fast to both initialise and step through, and evenly distributed.
This function takes an integer as a seed value, and returns a random function that always returns the same floating point values in the range 0 to 1.
const xor = seed => {
const baseSeeds = [123456789, 362436069, 521288629, 88675123]
let [x, y, z, w] = baseSeeds
const random = () => {
const t = x ^ (x << 11)
;[x, y, z] = [y, z, w]
w = w ^ (w >> 19) ^ (t ^ (t >> 8))
return w / 0x7fffffff
}
;[x, y, z, w] = baseSeeds.map(i => i + seed)
;[x, y, z, w] = [0, 0, 0, 0].map(() => Math.round(random() * 1e16))
return random
}
2. Shuffle using configurable random function
The Fisher Yates shuffle is an efficient shuffle algorithm with even distribution.
const shuffle = (array, random = Math.random) => {
let m = array.length
let t
let i
while (m) {
i = Math.floor(random() * m--)
t = array[m]
array[m] = array[i]
array[i] = t
}
return array
}
Putting it together
// passing an xor with the same seed produces same order of output array
console.log(shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], xor(1))) // [ 3, 4, 2, 6, 7, 1, 8, 9, 5 ]
console.log(shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], xor(1))) // [ 3, 4, 2, 6, 7, 1, 8, 9, 5 ]
// changing the seed passed to the xor function changes the output
console.log(shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9], xor(2))) // [ 4, 2, 6, 9, 7, 3, 8, 1, 5 ]
Related
The problem i try to solve is this using js :
A format for expressing an ordered list of integers is to use a comma separated list of either:
-individual integers
-or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. The range includes all integers in the interval including both endpoints. It is not considered a range unless it spans at least 3 numbers. For example "12,13,15-17"
Complete the solution so that it takes a list of integers in increasing order and returns a correctly formatted string in the range format.
Example:
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
// returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
so my idea was to use 3 functions :
1- newRangeStart: creates a new Array in results to store the range numbers and puts in it the first element of the range (RangeStarter).
2-olSupp : deletes elements from the list that were used by the 3rd function RangeArr so that we get a new Arr with a new RangeStarter using 1st function.
3-RangeArr : uses the 1st function than adds elements from the list to the array created by it which are consecutive starting from the Range starter, and then uses the 2nd function to delete the elements used from the ol so the next time we use the RangeArr function it creates another range.
By repeating the RangeArr function with a while loop that runs until ol becomes empty we will have a resuts array with arrays inside of it that contains ranges.
now the poblem is when i run RangeArr function it doesn't delete the used elements from the ol as i want i tried to fix the olSupp function several times but it just doesn't work i think there is a problem in my entire code pls someone help me to fix it here is my code:
function solution(list){
// TODO: complete solution
let ol = [...list];
let results = [];
/*This adds a new array for a range by adding the first number of the range to
an array (2D array) and stores it in the resuts array */
function newRangeStart(orderedlist,result){
result.push([orderedlist[0]]);
return result;
}
/*This functions takes the ol and deletes elements that are found in the results
so that the next time we run the newRangeStart function it creates an other array
for another range with a different start number*/
function olSupp(orderedlist,result){
let toRemove = result.flat();
let newList = [];
for (let i = 0; i < orderedlist.length; i++) {
if(!toRemove.includes(orderedlist[i])){
newList.push(orderedlist[i]);
}
}
orderedlist = [...newList];
return orderedlist;
}
/*Finally RangeArr function creates a range from the ol (ordered list)
starting by the first element of the results array and then uses olSupp to delete
the used numbers from the ol */
function RangeArr (orderedlist,result){
newRangeStart(orderedlist,result);
let i = 0;
while(orderedlist[i+1]- orderedlist[i] == 1 && orderedlist[i+2]- orderedlist[i+1]== 1) {
result[i].push(orderedlist[i+1],orderedlist[i+2]);
i = i+1;
}
olSupp(orderedlist,result);
return result;
}
/*we execute the RangeArr function until ol becomes emepty
and this will give us multiple arrays in the result array containing
the elements of each range found in the ol */
//PS: i didnt put the code beacuse it causes an infinte loop using while
RangeArr(ol,results);
console.log(ol,results);
}
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
const solution = arr => {
let s = null, d = 0;
const result = arr.sort((a, b) => a - b).reduce((p, c, i, arr) => {
d++;
if (!s) s = c;
if (arr[i + 1] - s > d) {
s === c ? p.push(s) : d < 3 ? p.push(s, c) : p.push(`${s}-${c}`);
s = null;
d = 0;
}
if (arr[i + 1] === undefined) s === c ? p.push(s) : d < 3 ? p.push(s, c) : p.push(`${s}-${c}`);
return p;
}, []);
return result;
}
console.log(solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]));
Variables:
s (start) for saving the number that starts a range.
d (distance) for counting the distance between the start and end of a range.
for a and b read the doc for javascript sort method to learn more.
same with p (pervious), c (current), i (index) and arr (original array) read the doc for javascript reduce method
Logic:
we can determine a range (for example: [1, 2, 3, 4, 5] is equal to "1-5") by calculate the distance between the starting number 1 and the ending number 5. so the distance between 1 and 5 is 4 because 1 needs to increment by 1 four times to reach 5 and the distance inside the array between the starting number 1 (index 0) and the ending number 5 (index 4) is also 4. so if we take the end minus the start 5 - 1 = 4 if it matches to the correct distance inside the array which is 4 then it is a range "1-5". let's have another example [1, 2, 3, 6, 7, 8], this should be "1-3" and "6-8". when we calculate the distance with 1 and 6 (6 - 1 = 5) we get 5 which is incorrect, because it doesn't match the correct distance inside the array (1 is at index 0 and 6 is at index 3, 3 - 0 = 3. the distance between 1 and 6 is only 3 index apart and not 5, that isn't a range). but if we do the calculate with 1 and 3 it matches our criteria and it's a range "1-3".
Code:
we have to do the calculation inside a loop (i'm using reduce method because it's convenient). first thing i do in the loop is d++ to track the distance of index(s) that the loop had travel inside the array. if (!s) s = c; is for check we've saved a starting number or not. if (arr[i + 1] - s > d) { ... } this is where we do the calculation to see if the current element inside the array minus s is greater than the distance index we've travel or not. if it's true then it means s and the last element must be a range and we push that range in the result array. and then we reset s and d to let them work on the next range.
Update
const solution = arr => {
let s = null;
return arr.sort((a, b) => a - b).reduce((p, c, i, arr) => {
if (!s) s = c;
if (c + 1 !== arr[i + 1]) {
s === c ? p.push(s) : c - 1 === s ? p.push(s, c) : p.push(`${s}-${c}`);
s = null;
}
return p
}, [])
}
console.log(solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]));
I'm working through a problem that involves admirable numbers, which are defined as
A number that is equal to the sum of all its proper divisors --
provided that one of them is negative.
For example, the proper divisors of 12 are 1, 2, 3, 4, 6, totaling
16. However, if 2 is negative, the total would be 12, the number itself. 12 is therefore an admirable number.
12 = 1 - 2 + 3 + 4 + 6
Essentially, I have to figure out what factor should be negative to get the admirable number.
I've worked through a similar problem that deals with perfect numbers
and am using a similar approach to figure solve this problem. Basically, I start out by creating an array of factors for a number that doesn't include the number itself. Then, I create an array of identical arrays that include all the factors for a number.
function admirable(n) {
function factors(n) {
let factors = []
for(let i = 1; i <= n; i++) {
if(n % i == 0) {
factors.push(i);
}
}
return factors
}
let arr = []
for (let i=0; i<baseArr.length; i++) {
arr.push(baseArr)
}
return arr
}
admirable(6) // [ [ 1, 2, 3 ], [ 1, 2, 3 ], [ 1, 2, 3 ] ]
admirable(12) // [
[ 1, 2, 3, 4, 6 ],
[ 1, 2, 3, 4, 6 ],
[ 1, 2, 3, 4, 6 ],
[ 1, 2, 3, 4, 6 ],
[ 1, 2, 3, 4, 6 ]
]
Once I've done this, I want to go through each array and multiply one number by -1, such that I get the result:
admirable(6) // [ [ -1, 2, 3 ], [ 1, -2, 3 ], [ 1, 2, -3 ] ]
admirable(12) //
// [
// [ -1, 2, 3, 4, 6 ],
// [ 1, -2, 3, 4, 6 ],
// [ 1, 2, -3, 4, 6 ],
// [ 1, 2, 3, -4, 6 ],
// [ 1, 2, 3, 4, -6 ]
// ]
Once I get the arrays in this configuration, I figure I can complete the last part of the problem:
If n is admirable, return the proper divisor that must be rendered
negative to make the sum of the proper divisors equal to n.
(I'll gladly accept any insight into ways to solve this part of the problem, too)
Is there a way to iterate over each array and make each subsequent number negative, e.g.
arr[0][0] * -1, arr[1][1] * -1 ...arr[n][n] *-1 ?
I spent a lot of time working on this yesterday and couldn't get my brain around it!
Your Question
If you have some array like this:
let arr = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
Then you can do:
for (let i = 0; i < arr.length; i++) {
arr[i][i] *= -1;
}
Which gives
[[-1, 2, 3], [1, -2, 3], [1, 2, -3]]
Alternatively...
Suppose you want to check if n is admirable. Let s be the sum of the divisors of n and d be a divisor of n. If you had computed s but made d negative, the total you would have obtained is s - 2d. So n is admirable if and only if n = s - 2d for some divisor d (and then d is the number you're looking for). So you can just check if d = (s - n) / 2 is an integer and a divisor of n.
I have an array of arrays of different sizes. The goal is to generate "rows" where each row can contain a max of 12 elements.
For example:
Input data can be something like this:
const groups = [[1,2,3,4],[1,2,3,4,5,6], [1,2,3,4,5,6,7,8,9,10,11,12], [1,2,3,4,5,6,7], [1,2,3],[1,2,3]]
groups[0].length + groups[1].length = 10 -> row0
groups[2].length = 12 -> row1
groups[3].length + groups[4].length = 10 -> row3
groups[5].length = 3 -> row4
Output for such array should be:
[[[1,2,3,4], [1,2,3,4,5,6]], [[1,2,3,4,5,6,7,8,9,10,11,12]], [[1,2,3,4,5,6,7], [1,2,3]], [[1,2,3]]]
I was thinking of a recursive function for this but couldn't figure out how to solve it.
You can use Array#reduce() to do this.
The code first checks if the current last element (last "row") has more than 12 numbers in it if you add the next group:
(acc[acc.length - 1].flat().length + cv.length) <= 12
if it will be less than 12, the elements will get pushed into the "row":
acc[acc.length - 1].push(cv)
and if not, a new "row" will be added to the outer array:
acc.push([cv])
const groups = [[1, 2, 3, 4],[1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],[1, 2, 3, 4, 5, 6, 7],[1, 2, 3],[1, 2, 3]];
const rows = groups.reduce((acc, cv) => {
(acc[acc.length - 1].flat().length + cv.length) <= 12 ?
acc[acc.length - 1].push(cv) :
acc.push([cv])
return acc
}, [[]]);
console.log(JSON.stringify(rows))
Here's one way to solve it recursively:
const regroup = (max, [g, ...gs], filled = [], curr = [], cc = 0) =>
g == undefined
? filled .concat ([curr])
: g .length + cc <= max
? regroup (max, gs, filled, curr .concat ([g]), cc + g.length)
: regroup (max, gs, filled .concat ([curr]), [g], g .length)
const groups = [[1, 2, 3, 4], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[1, 2, 3, 4, 5, 6, 7], [1, 2, 3], [1, 2, 3]]
console .log (regroup (12, groups))
.as-console-wrapper {max-height: 100% !important; top: 0}
We pass the maximum size and the list of items, and then we default three parameters:
filled will track the output rows we've filled; it starts with an empty array
curr stores the row we're working on; it also starts with an empty array
cc stores the count of all elements in the current row; it starts with zero
On each recursive call, we have one of three possibilities:
There are no more arrays to process, and we return all the filled rows with the current row appended.
The next array is small enough to fit in the current row, and we update the current to include it, and the current count to accommodate it.
The next array is too large, and we add the existing current row to the filled ones, and start a new current row with this array, setting the count appropriately.
I was presented with the below problem and I am not sure how to go about it due to the grid laylout and inexperience with math functions.
Write a function to create permutations from the following set of numbers such that the sum of three or less numbers equals to 1 or 2 or 3 or 4 or 5. Then group the permutations based on their sums.
For example:
[z,b];[y,a];[x,a];... belong to the permutation set 1
[z,b][y,a];[z,e];[y,a][x,a].... belong to the permutation set 2
a, b, c, d, e
z 3, 1, 5, 4, 2
y 1, 3, 2, 5, 4
x 1, 4, 3, 5, 2
w 3, 2, 5, 1, 4
v 4, 1, 5, 2, 3
u 3, 2, 4, 5, 1
t 1, 5, 4, 2, 3
s 5, 2, 1, 4, 3
r 5, 3, 2, 4, 1
q 5, 3, 4, 1, 2
The sum of 3 or fewer numbers is either 1,2,3,4 or 5
For example:
[z,a]=>3
[z,b]=>1 + [z,e]=>2 = 3
So, [ [[z,a]], [[z,b],[z,e]] ] is a sample solution set of a permutation of elements whose sum is 3. Also, each of the sets in the solution set contains 3 or fewer elements.
Group the permutations based on their sum
From the above example,
[ [[z,a]], [[z,b],[z,e]] ] is a permutation set of elements whose sum is 3.
Similarly, there will be more elements in the permutation set of elements whose sum is 3. Also, there will be permutation sets of elements whose sum is either 1,2,4 or 5.
Rather than been a math's problem, this is more an coding problem.
Below I've first flattered your array, this just makes it easier for the looping. And then when I want the labels, I just get that by working it out by the index.
After doing this, I've just created a multi-level loop,.. This will iterate every combination 50*50*50, 125000 combinations, at each level it does a check, combination 1, combination with 2, etc. There is also a check to make sure it doesn't duplicate the indexes, eg. 1 1 1, is invalid.
The forEach part is just first looping the inputs, it then passes the value, and what indexes made up that value into the check function. Within this forEach, it does another forEach for the 2nd number, and so forth. So what you end up with on the first 3 samples (excluding dups) would be -> 3 [0], 3 + 1 [0, 1], 3 + 1 + 5 [0,1,2] So this would then get 3:za 4:[za,zb], the last would be skipped as that equals 9.
The if (l1ix <= l0ix) return; is just to make sure we don't get duplicates,.. eg. [za,zb] and [zb,za] are classed as the same, this works as I'm making sure the next level index is always greater than the previous levels index.
Below is an example snippet.
const input = [
3, 1, 5, 4, 2 ,
1, 3, 2, 5, 4 ,
1, 4, 3, 5, 2 ,
3, 2, 5, 1, 4 ,
4, 1, 5, 2, 3 ,
3, 2, 4, 5, 1 ,
1, 5, 4, 2, 3 ,
5, 2, 1, 4, 3 ,
5, 3, 2, 4, 1 ,
5, 3, 4, 1, 2];
const topLabels = "abcde";
const sideLabels = "zyxwvutsrq";
function labelForPos(p) {
return {
top: topLabels[p % 5],
side: sideLabels[Math.trunc(p / 5)]
}
}
const members = {
1: [],
2: [],
3: [],
4: [],
5: []
};
function check(sum, ixlist) {
//make sure all are unique indexes
if ((new Set(ixlist)).size !== ixlist.length) return;
if (sum >= 1 && sum <= 5) {
members[sum].push("[" +
ixlist.map(ix => {
const lb = labelForPos(ix);
return `[${lb.side},${lb.top}]`;
}).join(",") + "]"
);
}
}
input.forEach((l0, l0ix) => {
check(l0, [l0ix]);
input.forEach((l1, l1ix) => {
if (l1ix <= l0ix) return;
check(l0 + l1, [l0ix, l1ix]);
input.forEach((l2, l2ix) => {
if (l2ix <= l1ix) return;
check(l0 + l1 + l2, [l0ix, l1ix, l2ix]);
});
});
});
Object.entries(members).forEach(([key, v]) => {
console.log(`${key} (${v.length}) = [${v.join(", ")}]`);
});
I have an array of x/y/z positions from a BufferGeometry, there are nearly 60000 points (18000 values),
[3, 2, 1, 3, 2, 1, 3, 2, 1, ...]
I need to shuffle those positions and then get the 30000 first to get random points. I am thinking of converting this array to an array of Vector 3 before shuffling to not lose all the "trio" values.
[new THREE.Vector3(3, 2, 1), new THREE.Vector3(3, 2, 1), new THREE.Vector3(3, 2, 1), ...]
I need vocabulary help.
Is there specific names for those two arrays please ? (that would help me found out about the next questions).
Is there specific methods to convert an array to another ?
And is there a best way to shuffle the raw positions array ?
The short answer is: you don't need to convert to THREE.Vector3 in order to extract n random points from the array. There is no specific name for both arrays.
Below I provide the functions to execute the operations you would like to perform:
And is there a best way to shuffle the raw positions array ? (yes, see below)
var points = [5, 6, 7, 3, 2, 1, 5, 6, 7, 3, 2, 1, 5, 6, 7]
// Assuming there are more points than the number of points you need to get back
function getRandomPoints (points, numberOfPoints) {
var resultPoints = []
for (var i = 0; i < numberOfPoints; i++) {
// Get a random index
var randomIndex = Math.floor(Math.random() * points.length);
var index = (randomIndex - randomIndex % 3)
resultPoints.push(points[index])
resultPoints.push(points[index + 1])
resultPoints.push(points[index + 2])
points.splice(index, index + 3);
}
return resultPoints;
}
var randomPoints = getRandomPoints(points, 2);
Is there specific methods to convert an array to another ? (yes, see below)
var points = [5, 6, 7, 3, 2, 1, 5, 6, 7, 3, 2, 1, 5, 6, 7]
function convertToVector (points) {
var resultPoints = []
for (var i = 0; i < points.length; i = i + 3) {
var vectorPoint = new THREE.Vector3(points[i], points[i + 1], points[i + 2])
resultPoints.push(vectorPoint)
}
return resultPoints
}
var convertedPoints = convertToVector(points)