I need to loop through a simple array in a "weird" way.
The length of my array is always a odd squared number.
Let's say the length is 49. To make it clearer, my elements are the index of the array. So I have something like:
myArray = [0, 1, 2, 3, 4 ... 47, 48]
So, you can imagine the following square matrix:
0 7 14 21 28 35 42
1 8 15 22 29 36 43
2 9 16 23 30 37 44
3 10 17 24 31 38 45
4 11 18 25 32 39 46
5 12 19 26 33 40 47
6 13 20 27 34 41 48
I have to start with the center of this matrix (i.e. myArray[Math.floor(myArray.length / 2)])
In my example: 24
Then I have to alternate between left and right numbers until I pass through all the row.
In my example, for the first iteration : 17, 31, 10, 38, 3, 45
Once a row is done, I alternate between up then bottom numbers an reiterate the left/right logic for the given row.
In my example, for myArray as input I should loop in the following order:
24, 17, 31, 10, 38, 3, 45,
23, 16, 30, 9, 37, 2, 44,
25, 18, 32, 11, 39, 4, 46,
22, 15, 29, 8, 36, 1, 43,
26, 19, 33, 12, 40, 5, 47,
21, 14, 28, 7, 35, 42, 0,
27, 20, 34, 13, 41, 6, 48
Could you help me the achieved it properly?
Here is what I did so far: https://jsfiddle.net/6qzkk2zx/
I recommend nesting two ES6 generators:
for (let y of outwards(7)) {
for (let x of outwards(7)) {
var i = y*7+x;
use(arr[i]); // for one-dimensional array
use(arr[x][y]) // for two-dimensional array
}
}
function* outwards(n) {
console.assert(n%2 == 1);
var x = (n-1)/2; // starting in the middle
yield x;
for (var i=1; i<n;) {
x -= i++; // jumping to the left
yield x;
x += i++; // and right
yield x;
}
}
Starting from that, you could either manually translate it back to an equivalent ES5 construct, or just let a transpiler do the work for you: Demo.
I think I have it:
https://jsfiddle.net/2dbj68t3/2/
var start = (array.length - 1) / 2;
var square = Math.sqrt(array.length);
newArray.push(array[start]);
for (j = 1; j <= Math.floor((square / 2)); j++) {
newArray.push((array[start - (j * square)]));
newArray.push((array[start + (j * square)]));
}
for (i = 1; i <= Math.floor((square / 2)); i++) {
newArray.push((array[start - i]));
for (j = 1; j <= Math.floor((square / 2)); j++) {
newArray.push((array[start - i - (j * square)]));
newArray.push((array[start - i + (j * square)]));
}
newArray.push((array[start + i]));
for (j = 1; j <= Math.floor((square / 2)); j++) {
newArray.push((array[start + i - (j * square)]));
newArray.push((array[start + i + (j * square)]));
}
}
Another option:
// Define number of elements
var n = 49;
// Calculate center
var start = Math.floor(n / 2);
// Calculate solution
var result = [get_row(start, n)];
for (var l = 1; l < Math.sqrt(n) / 2; l++) {
result.push(get_row(start - l, n));
result.push(get_row(start + l, n));
}
// Print solution
for (var k = 0; k < result.length; k++) {
console.log(result[k]);
}
///////////////////////////////////////////////
function get_row(c, size) {
var a = [];
a.push(c);
for (var i = 1; i < Math.sqrt(size) / 2; i++) {
a.push(c - i * Math.sqrt(size));
a.push(c + i * Math.sqrt(size));
}
return a;
}
Here's another approach using nested loops, single resulting array.
JSfiddle Demo
var num = 7,
middleElement = Math.floor(num * num / 2); // Square number and get the center number
var result = []; // To store resulting array
for(var j = 1; j <= num; j++) {
// This will be the middle element, i.e. 24, 23, 25, 22, 26, 21, 27
// Will be calculated dynamically
element = j % 2 ? middleElement + Math.floor(j / 2) : middleElement - Math.floor(j / 2);
result.push(element); // Add middle element in the resulting array
for(var i = 1; i < num; i++) {
// For next six elements
// Get the number from current number
element = i % 2 ? element - num * i : element + num * i;
result.push(element);
}
}
var num = 7,
middleElement = Math.floor(num * num / 2); // Square number and get the center number
var result = []; // To store resulting array
for(var j = 1; j <= num; j++) {
// This will be the middle element, i.e. 24, 23, 25, 22, 26, 21, 27
// Will be calculated dynamically
element = j % 2 ? middleElement + Math.floor(j / 2) : middleElement - Math.floor(j / 2);
result.push(element); // Add middle element in the resulting array
for(var i = 1; i < num; i++) {
// For next six elements
// Get the number from current number
element = i % 2 ? element - num * i : element + num * i;
result.push(element);
}
}
console.log(result);
Another approach, with an array for the position of the numbers:
var array = [], // 3 2 4 1 5 0 6
length = 7,
lower = 0, upper = length - 1,
result;
while (lower < upper) { // generate array
array.unshift(upper--);
array.unshift(lower++);
}
lower === upper && array.unshift(upper);
result = Array.apply(null, { length: length }).map(function (_, j) {
return Array.apply(null, { length: length }).map(function (_, i) {
return array[i] * length + array[j];
});
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Numbers that follows the one you are processing (let's say 24) are always some subtraction of your number (24) by some multiple of the length of the matrix (in your example, this length is 7).
Let subject be the number you are processing and hop the length of the matrix.
So, iterate through the rows, two per iteration, always filling two cells at a time, the first with subject - hop * <counter of iteration>, and the second cell with subject + hop * <counter of iteration>
I hope I understand your algorithm, but this ought to work:
If the length is always an odd square, then your index minima are 0 and maxima are Sqrt(n+1)-1.
var verticalup=false;
var horizontalforward=false;
var initial=myArray[Math.floor(myArray.length / 2)];
var maximum=Math.sqrt(myArray.length+1);
var current={vertical:initial,horizontal:initial};
var continue=true;
while (continue) {
//do something here with the current selection
if (current.horizontal===0) {
verticalup=!verticalup;
current.vertical+=(verticalup?1:-1);
}
horizontalforward=!horizontalforward;
current.horizontal+=(horizontalforward?1:-1);
continue=current.vertical>=0 && current.vertical<maxima && current.horizontal>=0 && current.horizontal<maximum;
}
I think something in the lines of...
var center = Math.floor(myArray.length / 2;
var parity = function(p){
return (p%2)*2-1; //if the number is even, returns -1 otherwise 1
}
//adds to cetner location half on index, signed with its parity, so we get an alternating effect each time index grows bigger
//for 0,1,2,3,4,5,6... will return 0,1,-1,2,-2,3,-3...
var step = function(index){
return (Math.floor((index+1)/2))*parity(index);
}
for (var i=0;i<size;i++){
for (var j=0;j<size;j++){
a[center-step(i)][center-step(j)];
}
}
Basically it utilizes the parity of i and j to determine whether to go up or down, and or left or right from the (center, center) point.
Related
Does anyone have an idea how to make these 3 colored squares move around the perimeter of the board, and not like now, i.e. in lines? (lines 57-78) https://codepen.io/diana-larussa/pen/ExgpXzo
function timer() {
ttl--
divElement = document.querySelectorAll('div')
divElement[nr_boxu].style.backgroundColor = "#6d5dfc"
divElement[nr_boxu].value = 0
nr_boxu = nr_boxu + 1
divElement[nr_boxu].style.backgroundColor = "#F25270"
divElement[nr_boxu+1].style.backgroundColor = "#F25270"
divElement[nr_boxu+2].style.backgroundColor = "#F25270"
spanTimer.innerHTML = "TIME: " + ttl
//if (nr_boxu>gridDOMElement.value-4) stop()
/*if (divElement[nr_boxu] == gridDOMElement.value - 4) {
divElement[gridDOMElement.value + 1].style.backgroundColor = "#F25270"
}*/
if (ttl == 0) stop()
}
I think for each grid size it is possible to generate perimeter sequence of indices and timer() function could pick 4 of those indices to update.
For example, in this grid 5x4:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
perimeter array would be: [0, 1, 2, 3, 4, 9, 14, 19, 18, 17, 16, 15, 10, 5] (indices highlighted in bold)
timer function will pick 4 indices out of the array, for example [0, 1, 2, 3] at the start and will colour element at index 0 with #6d5dfc and 1, 2, 3 with #F25270.
It will later follow around the perimeter, for example picking 4 elements: [2, 3, 4, 9] and colouring element 2 with #6d5dfc and 3, 4, 9 with #F25270 (need to be careful when reaching end of array).
nr_boxu seems to be incremented starting from 0 on every timer() call.
It can be used to pick 4 elements from perimeter array starting from 0 till the end of array.
The following formula - nr_boxu % perimeter.length uses remainder operator to keep iterating from 0 till perimeter.length - 1.
perimeter and timer can be built like this:
function getPerimeter(cols, rows) {
let res = [];
// top line: 0, 1, 2, 3, 4
for (let i = 0; i < cols; i++) {
res.push(i);
}
// right line: 9, 14
for (let i = 1; i < rows - 1; i++) {
res.push(cols * (i + 1) - 1);
}
// bottom line: 19, 18, 17, 16, 15
for (let i = 0; i < cols; i++) {
res.push(cols * rows - 1 - i);
}
// left line: 10, 15
for (let i = rows - 2; i > 0; i--) {
res.push(i * cols);
}
return res;
}
let perimeter = getPerimeter(numOfColumns, numOfRows);
function timer() {
ttl--
const ind1 = perimeter[nr_boxu % perimeter.length];
const ind2 = perimeter[(nr_boxu + 1) % perimeter.length];
const ind3 = perimeter[(nr_boxu + 2) % perimeter.length];
const ind4 = perimeter[(nr_boxu + 3) % perimeter.length];
divElement = document.querySelectorAll('div')
divElement[ind1].style.backgroundColor = "#6d5dfc"
divElement[ind1].value = 0
nr_boxu = nr_boxu + 1
divElement[ind2].style.backgroundColor = "#F25270"
divElement[ind3].style.backgroundColor = "#F25270"
divElement[ind4].style.backgroundColor = "#F25270"
spanTimer.innerHTML = "TIME: " + ttl
//if (nr_boxu>gridDOMElement.value-4) stop()
/*if (divElement[nr_boxu] == gridDOMElement.value - 4) {
divElement[gridDOMElement.value + 1].style.backgroundColor = "#F25270"
}*/
if (ttl == 0) stop()
}
I want to get a random number from the Fibonacci series:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, ...
Here is my code:
var number = Math.floor(Math.random() * 100000);
var series_element = -1;
if (number < 1) {
series_element = 1;
} else {
if (number < 2) {
series_element = 2;
} else {
if (number < 3) {
series_element = 3;
} else {
if (number < 5) {
series_element = 5;
} else {
if (number < 8) {
series_element = 8;
} else {
if (number < 13) {
series_element = 13;
} else {
if (number < 21) {
series_element = 21;
}
////// series continues to 317811
}
}
}
}
}
}
alert(series_element);
But I never got the value of series_element less than 100. It always shows me higher values.
I think you mean that you're not getting a random number less than 100 from the Math.random() function. So you're not getting your variable series_element to be 11 or less (the first 11 terms of the Fibonacci sequence: 0 1 1 2 3 5 8 13 21 34 55 89).
In fact, it's a matter of probabilities.
100 / 1000000 = 0.0001
If you keep executing it you'll get a value less than 100 at some point... approximately 1 from 10000 times you do it.
There's nothing wrong with your code, but it could be improved so you don't have to put so much ifs.
First, let's define a function to calculate the fibonacci numbers. Details on how to do that can be find here: https://medium.com/developers-writing/fibonacci-sequence-algorithm-in-javascript-b253dc7e320e
function fibonacci(num){
var a = 1, b = 0, temp;
while (num >= 0){
temp = a;
a = a + b;
b = temp;
num--;
}
return b;
}
To get a random Fibonacci number you can call this function with a random number.
var number = Math.floor(Math.random()*100);
var result = fibonacci(number);
I don't recommend going after 100 as your computer may take too much time to process the result...
You are using with poorly structured code to generate fabonacci series. Try something like following, you will get value under 100 and 1000
Where N is position of Fibonacci number from 1 to N and X is actual number.
var n = function getRandomNum() {
return Math.floor(Math.random()*100) +1;
}
var result = [];
result[0] = 1;
result[1] = 1;
function fib(x) {
var ix, ixLen;
for(ix = 0, ixLen = x; ix < ixLen; ix++){
if(!result[ix]){
result[ix] = result[ix-2] + result[ix-1];
}
}
console.log('n:', x, ' result: ', result[ix-1]);
return result[ix-1];
}
console.log(fib(n()));
var alphaNumbersStr = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var hexaString = '0123456789abcdef';
var str32 = '4af27d7ef70b1263da25022af735508b';
var str = 'FoooooBar';
var arr = [];
for (var i = 0; i < str.length; i++) {
arr[i] = alphaNumbersStr.indexOf(str.charAt(i));
}
// --
// Start : arr ==> [ 31, 14, 14, 14, 14, 14, 27, 0, 17 ]
// --
for (var i = 0; i < 10000; i++) {
arr[i % str.length] += hexaString.indexOf(str32.charAt(i % 32)) ^ arr[(i + 1) % str.length] ^ arr[(i + 2) % str.length];
if (arr[i % str.length] > alphaNumbersStr.length - 1) {
arr[i % str.length] %= alphaNumbersStr.length;
}
}
// --
// End : after 10000 iterations : arr ==> [ 38, 8, 32, 4, 18, 24, 2, 48, 22 ]
// --
Given the following snippet, I'm trying to reverse this algorithm so that I could return to the original array state (step 0).
Any idea on how this could be solved?
Just do all steps backwards. The operations performed are in such a way that the modulo operation is possible to revert.
function revert(arr) {
for (let i = 9999; i >= 0; i--) {
//we know this as these values didnt get affected this iteration
let added = hexaString.indexOf(str32.charAt(i % 32)) ^ arr[(i + 1) % str.length] ^ arr[(i + 2) % str.length];
//the important difference in modulo
let modadd = added % alphaNumbersStr.length;
//single overflow is what we need to predict
//if we went over, we went smaller again
let overflow = modadd > arr[i % str.length];
//we need to undo the modulo
if (overflow) {
let diff = modadd - arr[i % str.length];
arr[i % str.length] = alphaNumbersStr.length - diff;
}
//Not much to do otherwise
else arr[i % str.length] -= modadd;
}
return arr;
}
//Just so it can be run
function change(){var result,value=document.getElementById("a").value;try{result=algorithm(eval(value))}catch(a){result=["error"]}document.getElementById("t").innerHTML="["+result.join(", ")+"]",document.getElementById("i").innerHTML="["+(result[0]==='error' ? result : revert(result)).join(", ")+"]"}function algorithm(a){for(var b=0;b<1e4;b++)a[b%str.length]+=hexaString.indexOf(str32.charAt(b%32))^a[(b+1)%str.length]^a[(b+2)%str.length],a[b%str.length]>alphaNumbersStr.length-1&&(a[b%str.length]%=alphaNumbersStr.length);return a}var alphaNumbersStr="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",hexaString="0123456789abcdef",str32="4af27d7ef70b1263da25022af735508b",str="FoooooBar";change();
Value of <code>arr</code>: <input onblur="change()" id="a" size="50" value="[31, 14, 14, 14, 14, 14, 27, 0, 17]">
<hr>
Through algorithm: <code id="t"></code><br>
Through revert: <code id="i"></code>
Hi I need to generate a random number in JavaScript between 15 and 225 that can only be by increments of 30. For example: 45, 75, 105 etc.
Any ideas on how this can be done? I'm aware of Math.floor(Math.random()*11) for example for a random number 1-10 but how can this be modified to fit in with what I'm trying to do?
Like so
Math.floor(Math.random()*8)*30 + 15
how about using:
[15, 45, 75, 105, 135, 165, 195, 225][Math.floor(Math.random()*8)]
Start with the table of values
x | y
=======
0 | 15
1 | 45
2 | 75
3 | 105
4 | 135
5 | 165
6 | 195
7 | 225
Set up the equation
y = ax + b
Plug in some initial values
15 = a(0) + b
Solve for any variables you can
15 = b
Plug in different initial values
225 = a(7) + 15
Solve for any remaining variables
210 = a(7)
30 = a
Restate the equation
y = 30x + 15
Randomize x between the limits
x = Math.floor(Math.random() * 8) //0-7
Use in function
function customRandomNumber() {
var x;
x = Math.floor(Math.random() * 8);
return 30 * x + 15;
}
Getting a random number within an interval with a step can be generalized in the following function:
function rand(min, max, step) {
var delta,
range,
rand;
if (arguments.length < 2) {
max = min;
min = 0;
}
if (!step) {
step = 1;
}
delta = max - min;
range = delta / step;
rand = Math.random();
rand *= range;
rand = Math.floor(rand);
rand *= step;
rand += min;
return rand;
}
Which would put your function as
rand(15, 225, 30);
I should also note, because I'd forgotten about the possible bounds issues, the generalized form will not include the upper bound of 225. This is because Math.random() returns a number greater than, or equal to zero and less than one (0 <= Math.random < 1). If you want to include the upper bound, you simply need to include one more step in the interval:
rand(15, 255, 30) //will return 15...225
You need to think about the final outcome that you want. Between 15 and 225 is what you want to aim for, but not what you need to be working with to get the result.
225/30 = 7.5. 15 is half of 30. Therefore, (225-15)/30 = 7. You want a random number between 1 and 7, which you will then multiply by 30 and add 15 to the final result.
var myNumber = Math.floor(Math.random()*7);
myNumber = myNumber *30 + 15;
console.log(myNumber);
Here's a function that precomputes all possible numbers, and randomly returns one.
function getRandomNumber(start, end, increments) {
var numbers = [];
for(var n = start; n <= end; n += increments) {
numbers.push(n);
}
var randomIndex = Math.floor(Math.random() * numbers.length);
return numbers[randomIndex];
}
Use as,
getRandomNumber(15, 225, 30)
Here's a generic version.
function randomIntInSteps(a, b, step) {
function randomInt(a, b) {
return Math.floor(Math.random() * (b - a + 1) + a);
}
if (a > b) {
// Ensure a is smaller.
var c = a;
a = b;
b = c;
}
step = Math.abs(step)
return a + randomInt(0, Math.floor((b - a)/step)) * step;
}
It can be used like this:
randomIntInSteps(1, 20, 5); // possible results: 1, 6, 11, 16
randomIntInSteps(1, 21, 5); // possible results: 1, 6, 11, 16, 21
It will also work with negatives:
randomIntInSteps(-10, 11, 5); // possible results: -10, -5, 0, 5, 10
randomIntInSteps(-20, -9, 5); // possible results: -20, -15, -10
Using floats can be problematic because of floating point errors:
randomIntInSteps(1.1, 5.5, 2.2); // possible results: 1.1, 3.3000000000000003, 5.5
To overcome the floating point problem, you can convert the numbers to integers before finding a random number, then convert back. Here's how (note: this depends on the randomIntInSteps function above, so you still need that):
function decimalPlaces(number) {
var parts = (number + '').split('.');
return parts.length > 1 ? parts[1].length : 0;
}
function wholeNumberMultiple(numbers) {
return Math.max(...numbers.map(n => decimalPlaces(n)));
}
function randomNumber(a, b, step) {
var multiple = Math.pow(10, wholeNumberMultiple([a, b]));
return randomIntInSteps(a * multiple, b * multiple, step * multiple) / multiple;
}
You'll now get the desired results:
randomNumber(1.1, 5.5, 2.2); // possible results: 1.1, 3.3, 5.5
Okay....
I have a lot of uncontrolled numbers i want to round:
51255 -> 55000
25 -> 25
9214 -> 9500
13135 -> 15000
25123 -> 30000
I have tried modifying the numbers as string and counting length....
But is there a simple way using some Math function maybe?
Here's my late answer. Uses no Math methods.
function toN5( x ) {
var i = 5;
while( x >= 100 ) {x/=10; i*=10;}
return ((~~(x/5))+(x%5?1:0)) * i;
}
DEMO: http://jsbin.com/ujamoj/edit#javascript,live
[51255, 24, 25, 26, 9214, 13135, 25123, 1, 9, 0].map( toN5 );
// [55000, 25, 25, 30, 9500, 15000, 30000, 5, 10, 0]
Or this is perhaps a bit cleaner:
function toN5( x ) {
var i = 1;
while( x >= 100 ) {x/=10; i*=10;}
return (x + (5-((x%5)||5))) * i;
}
DEMO: http://jsbin.com/idowan/edit#javascript,live
To break it down:
function toN5( x ) {
// v---we're going to reduce x to the tens place, and for each place
// v reduction, we'll multiply i * 10 to restore x later.
var i = 1;
// as long as x >= 100, divide x by 10, and multiply i by 10.
while( x >= 100 ) {x/=10; i*=10;}
// Now round up to the next 5 by adding to x the difference between 5 and
// the remainder of x/5 (or if the remainder was 0, we substitute 5
// for the remainder, so it is (x + (5 - 5)), which of course equals x).
// So then since we are now in either the tens or ones place, and we've
// rounded to the next 5 (or stayed the same), we multiply by i to restore
// x to its original place.
return (x + (5-((x%5)||5))) * i;
}
Or to avoid logical operators, and just use arithmetic operators, we could do:
return (x + ((5-(x%5))%5)) * i;
And to spread it out a bit:
function toN5( x ) {
var i = 1;
while( x >= 100 ) {
x/=10;
i*=10;
}
var remainder = x % 5;
var distance_to_5 = (5 - remainder) % 5;
return (x + distance_to_5) * i;
}
var numbers = [51255, 25, 9214, 13135, 25123, 3, 6];
function weird_round(a) {
var len = a.toString().length;
var div = len == 1 ? 1 : Math.pow(10, len - 2);
return Math.ceil(a / 5 / div) * div * 5;
}
alert(numbers.map(weird_round));
Also updated for numbers below 10. Won't work properly for negative numbers either, just mention if you need this.
DEMO
I'm not sure why, but I thought it would be fun with regular expressions:
var result = +(number.toString().replace(/([1-9])([0-9])(.+)/, function() {
return Math.ceil(+(arguments[1] + '.' + arguments[2])) * 10 - (+arguments[2] < 5?5:0) + arguments[3].replace(/./g, '0');
}));
Working Demo
with(Math) {
var exp = floor(log(number)/log(10)) - 1;
exp = max(exp,0);
var n = number/pow(10,exp);
var n2 = ceil(n/5) * 5;
var result = n2 * pow(10,exp);
}
http://jsfiddle.net/NvvGf/4/
Caveat: only works for the natural numbers.
function round(number) {
var numberStr = number + "",
max,
i;
if (numberStr[1] > '4') {
numberStr[0] = parseInt(numberStr[0]) + 1;
numberStr[1] = '0';
} else {
numberStr[1] = '5';
}
for (i = 2; max = numberStr.length; i < max; i += 1) {
numberStr += '0';
}
return parseInt(numberStr);
}
Strange coincidence, I wrote something really similar not so long ago!
function iSuckAtNames(n) {
var n = n.toString(), len = n.length, res;
//Check the second number. if it's less than a 5, round down,
//If it's more/equal, round up
//Either way, we'll need to use this:
var res = parseFloat(n[0]) * Math.pow(10, len - 1); //e.g. 5 * 10^4 = 50000
if (n[1] <= 5) {
//we need to add a 5 right before the end!
res += 5 * Math.pow(10, len - 2);
}
else {
//We need another number of that size
res += Math.pow(10, len - 1);
}
return res;
}