Algorithm that involves rounding and multiples - javascript

So I have a problem where I have an array of some length (usually long). I have an initial start index into that array and a skip value. So, if I have this:
var initial = 5;
var skip = 10;
Then I'd iterate over my array with indexes 5, 15, 25, 35, etc.
But then I may get a new start value and I need to find the closest value to the initial plus or minus a multiple of the skip and then start my skip. So if my new value is 23 then I'd iterate 25, 35, 45, etc.
The algorithm I have for this is:
index = (round((start - initial) / skip) * skip) + initial
And then I need a check to see if index has dropped below zero:
while(index < 0) index += skip;
So my first question is if there's a name for this? A multiple with random start?
My second question is if there's a better way? I don't think what I have is terribly complicated but if I'm missing something I'd like to know about it.
If it matters I'm using javascript.
Thanks!

Edit
Instead of
while(index < 0) index += skip;
if we assume that both initial and skip are positive you can use:
if (index < 0) index = initial % skip;

To get the closest multiple of a number to a test number: See if the modulo of your test number is greater than number/2 and if so, return number - modulo:
function closestMultiple(multipleTest,number)
{
var modulo = multipleTest%number;
if(0 == modulo )
{
return multipleTest;
}
else
{
var halfNumber = number/2;
if(modulo >= halfNumber)
{
return multipleTest + (number-modulo);
}
else
{
return multipleTest - modulo;
}
}
}
To check if a number is a multiple of another then compare their modulo to 0:
function isMultiple(multipleTest,number)
{
return 0 == multipleTest%number;
}
You might want to add some validations for 0 in case you expect any inside closestMultiple.

The value of index computed as you put it
index = round((start - initial)/skip) * skip + initial
is indeed the one that minimizes the distance between the sequence with general term
aj = j * skip + initial
and start.
Therefore, index can only be negative if start lies to the left of
(a-1 + a0)/2 = initial - skip/2
in other words, if
start < initial - skip/2.
So, only in that case you have to redefine index to 0. In pseudo code:
IF (start < (initial - skip/2))
index = 0
ELSE
index = round((start - initial)/skip) * skip + initial
Alternatively, you could do
index = round((start - initial)/skip) * skip + initial
IF index < 0 THEN index = 0
which is the same.

No while loop required:
function newNum(newstart, initial, skip) {
var xLow = newstart + Math.abs(newstart - initial) % skip;
var xHi = newstart + skip;
var result = (xLow + xHi) / 2 > newstart ? xLow : xHi;
if (result < 0) result += skip;
return result;
}
Take the distance between your new starting point and your initial value, and find out what the remainder would be if you marched towards that initial value (Modulus gives us that). Then you just need to find out if the closest spot is before or after the starting point (I did this be comparing the midpoint of the low and high values to the starting point).
Test:
newNum(1, 20, 7) = 6
newNum(-1, 20, 7) = 6
newNum(180, 10, 3) = 182
(Even though you stated in your comments that the range of the new starting point is within the array bounds, notice that it doesn't really matter).

Related

How does one, within a sequence of digits, count how many times a digit appears thats value is exactly one less than the previous digit's one?

code:
function OneDecremented(num) {
num = num.toString()
var count = 0
for(i = 1; i < num.length; i++) {
if(num[i - 1] - num[i] === 1){
count++
}
}
return count
}
console.log(OneDecremented(9876541110))
so I'm struggling to understand two things:
what's the difference between i and num[i]
I don't understand how the calculation is happening inside the if statement, could someone break it down?
sorry if these questions sound too silly, I'm new to JS and couldn't really get my head around the arithmetic calculations. Thank you for you time.
That code is poorly written for few reasons, but most importantly, it leaks the i reference globally so, let's start with a better version:
function OneDecremented(num) {
var str = num.toString();
var count = 0;
for(var i = 1; i < str.length; i++) {
if(str[i - 1] - str[i] === 1)
count++;
}
return count;
}
Strings, in modern JS, can be accessed like arrays, and the index returns the char at the index position:
if(str[i - 1] - str[i] === 1)
// is the same as
if ((str.charAt(i - 1) - str.charAt(i)) === 1)
Once retrieved each char, the code does an implicit "char to number" conversion, thanks to the - operator, but if it was a + instead, it would've concatenated the two chars as string instead (so, be careful).
It's always better to be explicit, but if you know how - works, it does the job for this task.
The loop starts from 1, and it checks that the char at i - 1, which is in the first iteration the char at index 0, minus the current char, is 1, meaning the current char is one less the previous.
When that's the case, the counter sums up.
Andrea and Mitya already nailed it.
The next step could be switching to a first class based approach like using a specific Array method such as reduce.
Such an approach, if implemented correctly, usually improves readability/maintainability of code and allows for better code-reuse.
For the example provided by the OP one could write two functions, the actual method, which gets the count and the above mentioned first class reducer functionality. Since reduce is a standard way of how arrays can be processed the arguments-precedence of the reducer/callback is well specified too ...
[/* ... */].reduce(function(accumulator, currentValue, currentIndex, currentlyProcessedArray) {
// implement reducer/aggregation/accumulator logic here.
// the return value serves as the
// new `accumulator` value within
// the next iteration step.
// thus, always return something! ... e.g ...
return (accumulator + currentValue);
});
function aggregatePrecursorAndDecrementedSuccessorCount(count, char, idx, arr) {
const precursorValue = Number(arr[idx - 1]);
const incrementedCurrentValue = (Number(char) + 1);
const isValidCount = (precursorValue === incrementedCurrentValue);
return (count + (isValidCount ? 1 : 0));
//return (count + Number(isValidCount)); // cast boolean value to either 1 or 0.
}
function getPrecursorAndDecrementedSuccessorCount(int) {
return String(int) // - assure/typecast always a/into string value.
.split('') // - split string value into an array of single characters.
.reduce(aggregatePrecursorAndDecrementedSuccessorCount, 0);
}
console.log(getPrecursorAndDecrementedSuccessorCount(9876541110));
.as-console-wrapper { min-height: 100%!important; top: 0; }
what's the difference between i and num[i]
i is the iteration key, i.e. 0, 1, 2 etc, for as many characters are in the stringified number. num[i] is the character at the index i in the string, i.e. num[i] where i is 0 == 9 (the character in the string at index 0).
I don't understand how the calculation is happening inside the if statement, could someone break it down?
That says: If the calculation of the number at index i-1 of the string, minus the current number being considered (at index i in the string) minus is 1, then increment count.
Step by step for the actual number used:
9 - has no previous character; calculation (undefined - 9) does not equate to 1
8 - previous char is 9; (9 - 8) == 1; increment count
7 - ditto
6 - ditto
5 - ditto
4 - ditto
1 - previous char is 4; calculation (4 - 1) does not equate to 1
1 - previous char is 1; calculation (1 - 1) does not equate to 1
1 - ditto
0 - previous char is 1; (1 - 0) == 1; increment count

Binary search - multiple searched values

I using binary search for searching rows range that should be rendered in my app. GridRow has 3 properties: top, height and bottom and list of rows is sorted.
Example:
I passing 30px to my first call of rowBinarySearch, in next line 140 and firstIndex to speedup search. I return lastIndex + 1 for fix visibility of last row in my array:
const firstIndex = rowBinarySearch(rows, 30);
const lastIndex = rowBinarySearch(rows, 140, firstIndex);
return range.slice(firstIndex, lastIndex + 1);
Search function implementation:
function rowBinarySearch(arr: GridRow[], val: number, start = 0, end = arr.length - 1): number {
const mid = Math.floor((start + end) / 2);
if (mid < 0)
return 0;
if (val === arr[mid].top)
return mid;
if (start >= end)
return mid; // original version should return -1 if element dont exist
return val < arr[mid].top
? rowBinarySearch(arr, val, start, mid - 1)
: rowBinarySearch(arr, val, mid + 1, end);
}
Expected behavior:
1. Remove hacky return commented in commented listing above
2. Find first row that top value of row is lower then searched value
3. I would be great if shouldnt increment lastIndex by 1
Thanks for any help :)
Remove hacky return commented in commented listing above
This is not a hacky return. A recursive algorithm always needs a base case, and start >= end is exactly the base case this recursion depends on.
It is the preceding return that is not conventional. If all integer values are allowed for val, then val === arr[mid].top is a rare case, and it does not need special treatment really. Think of what should happen when val is arr[mid].top + 1. It should be the same (assuming a height of more than 1).
Also the first return for when mid < 0 is not necessary, unless you plan to call this function with negative values for either start or end. And acutally you risk to do this for end in recursive calls, but see next point on how to avoid that.
Find first row that top value of row is lower then searched value
Currently, your algorithm is not correct: when instead of 140, you pass 120 in the second call, the return value is 2 units less, while you only "climb" one row.
I would suggest defining end as the first index after the current range. This is in line with how parameters are defined for functions like .slice.
I would be great if shouldnt increment lastIndex by 1
You should not strive to do that, since it is only logical that if you use the same function for both finding the starting row and the ending row, that you want one more row selected than lastIndex - firstIndex. So just add 1 and don't worry about it.
Here is a fixed algorithm:
function rowBinarySearch(arr, val, start = 0, end = arr.length) {
if (start >= end) return end - 1; // base case
const mid = (start + end) >> 1; // Use shift, so you don't need to `floor`.
return val < arr[mid].top
? rowBinarySearch(arr, val, start, mid)
: rowBinarySearch(arr, val, mid + 1, end);
}
// Demo
const rows = Array.from({length: 7}, (_, id) => ({
id, top: id*25, bottom: id*25+25, height: 25
}));
const firstIndex = rowBinarySearch(rows, 30);
const lastIndex = rowBinarySearch(rows, 140, firstIndex);
console.log(rows.slice(firstIndex, lastIndex + 1).map(JSON.stringify).join("\n"));

Difference/sum in an array to check for the two numbers generated that result in another array

For clarity sake, this is what I mean. I want to look for the two least numbers in an array(sorted) that will generate a particular number. The steps go thus:
Loop through the array and each time set a current value that other
numbers will be deducted from.
Keep doing that till you find the numbers that match the problem and return them.
Example. I need two numbers that when subtracted from the array will give a result of 2.
let givenArray = [1, 4, 8, 10];
The subtraction should go thus: 4 - 1 = 3(doesn't match); //continue
8 - 4 = 1(doesn't match);// continue
8 - 1 = 7(doesn't match); //continue
10 - 8 = 2(match found); //stop and return 8, 10.
NOTE: This same array may contain a 6 and 8 or 8 and 10 that will both yield 2 but 6 and 8 should be returned instead. The way the array is generated isn't of much importance.
P.S: I eventually solved it yesterday but I won't mind other ideas on how to go about it.
This solution takes the advantage of a hash table and uses a single loop approach for getting the two values out of an array to balance two values.
First, take the absolute delta of the two values of arrayA and take this for getting the values out of the greater array.
Then reduce the greater array arrayB by checking if the needed value exist and if the sum is smaller then a previously found set.
The argument for checking is build out of the absolute delta of delta and v, the actual value of the array or by taking the sum of delta and v.
The last point, and to make this all working, the actual value v is included into the hash table, for a later look up.
The result is either an array of two values, which balance the other two values or undefined, if no values are found.
var arrayA = [3, 5],
arrayB = [2, 9, 5, 4],
delta = Math.abs(arrayA[0] - arrayA[1]),
values = {},
result = arrayB.reduce((r, v) => {
function check(w) {
if (!values[w] || r && r[0] + r[1] < v + w) return;
r = [w, v];
}
check(Math.abs(delta - v));
check(delta + v);
values[v] = true;
return r;
}, undefined);
console.log(result);
I'm not sure I understood correctly, but perhaps this is what you need:
let result = arrayA[1] - arrayA[0];
let i, j;
for (i = arrayB.length - 1; i >= 1; i--) { // Set the first value
for (j = arrayB.length - 1; j >= 1; j--) { // Set the second value
if (i !== j) {
if (
arrayB[i] - arrayB[j] === result // Check substraction
|| arrayB[i] + arrayB[j] === result // Check addition
) return [arrayB[i], arrayB[j]];
}
}
}

Addition with carryover in arrays in pure Javascript

I want to create simple addition of array with carryover. Also need carryover and result value for display.
Something like this:-
e.g var input = [[0,0,9],[0,9,9]];
var carryover = [];
var result = [];
Thanks...
The two parts you might have been struggling with, I assume would be how you get the carry, and how you get the result..
result [diget] = t % 10;
The % 10 part is what is called modulus, here I'm doing a modulus by 10, so that gets you the 10's unit value.
carryover [diget] = Math.trunc(t / 10);
For the carryover, you just then divide by 10, and then we strip the decimals,. That's what Math.trunc does.
var input = [[0,0,0,9],[0,9,9]];
var carryover = [];
var result = [];
var digetlength = Math.max(input[0].length, input[1].length);
//lets padd inputs to be same size
input[0].unshift(
...new Array(digetlength - input[0].length).fill(0));
input[1].unshift(
...new Array(digetlength - input[1].length).fill(0));
for (var diget = digetlength - 1; diget >= 0; diget -= 1) {
var t = input[0][diget] + input[1][diget];
if (diget < digetlength - 1)
t += carryover[diget + 1];
result [diget] = t % 10;
carryover [diget] = Math.trunc(t / 10);
}
result.unshift(carryover[0]);
console.log('result: ' + result.join(', '));
console.log('carry: ' + carryover.join(', '));
1.turn both numbers into array of digits, reverse them.
2.determine the end index of the for-loop with max length of above 2 arrays.
3.create the 3rd carryover digits array of zeros (don't forget the extra digit).
4.Add the respective digits from step1 and step3,
as you iterate through each of digits from right to left,
4.1 if the sum is greater than 9 then add 1 into next carryover slot.
5. you should have array of carried over digits when the for-loop is done
count the number of 1s you have in them.
function numberOfCarryOperations(num1, num2) {
const dd1 = [...num1.toString()].reverse()
const dd2 = [...num2.toString()].reverse()
const end = Math.max(dd1.length, dd2.length)
const carry = Array(end+1).fill(0)
for (let i = 0; i < end; i++) {
//console.log(i,(Number(dd1[i]?dd1[i]:0)),Number(dd2[i]?dd2[i]:0),carry)
if (((Number(dd1[i]?dd1[i]:0)) + Number(dd2[i]?dd2[i]:0) + carry[i]) > 9) {
carry[i+1] = 1
}
//console.log('-----',carry)
}
//console.log(num1, num2,carry)
return carry.reduce((sum,curr)=>sum+curr)
}
Here is my attempt. It will accept the following as input:
Any number of input arrays
The input arrays don't all need to have the same number of items
I've added code comments to explain what goes on, I hope they're informative enough to explain the answer.
const
input = [
[0,0,9],
[0,9,9],
[1,0,9,9]
];
function getMaxArrayLength(values) {
// Determine the number of items in the longest array. Initialize the reduce with 0.
return values.reduce((maxLength, array) => {
// Return the largets number between the last largest number and the
// length of the current array.
return Math.max(maxLength, array.length);
}, 0);
}
function sumValues(values) {
const
// Determine the number of items in the longest array.
maxLength = getMaxArrayLength(values),
result = [],
carry = [];
// Loop over the length of the longest array. The value of index will be substracted from
// the length of the input arrays. Therefore it is easier to start at 1 as this will
// return a proper array index without needing to correct it.
for (let index = 1; index <= maxLength; index++) {
const
// Get the carryover value from the last sum or 0 in case there is no previous value.
carryValue = (carry.length === 0) ? 0 : carry[carry.length-1],
// Sum up all the values at the current index of all the input arrays. After summing up
// all the values, also add the carry over from the last sum.
sum = values.reduce((sum, array) => {
// Determine the index for the current array. Start at the end and substract the
// current index. This way the values in the array are processed last to first.
const
arrayIndex = array.length - index;
// It could be the current array doesn't have as many items as the longest array,
// when the arrayIndex is less than 0 just return the current result.
if (arrayIndex < 0) {
return sum;
}
// Return the accumulated value plus the value at the current index of the
// current source array.
return sum + array[arrayIndex];
}, 0) + carryValue;
// The carry over value is the number of times 10 fits into the sum. This should be rounded
// down so for instance 5/10=.5 becomes 0.
carry.push(Math.floor(sum / 10));
// Push the remainder of the sum divided by 10 into the result so 15 % 10 = 5.
result.push(sum % 10);
}
// Return the carry over and the result, reverse the arrays before returning them.
return {
carryOver: carry.reverse(),
result: result.reverse()
};
}
const
result = sumValues(input);
console.log(`Carry over: ${result.carryOver}`);
console.log(`Result: ${result.result}`);

How to add a value randomly to an array using JavaScript

I am new to JavaScript and i am going through a task where i have to randomly disperse an input value between 12 loads. The other side is that each element of the array cannot differ from the next by more than one.
so for example if i have an amount of 30 i need to distribute this amount between 12 camels. I have so far written the code below, but i am using TextPad as requested i am not sure how to print out the result on the same line.
var amount = 30;
var camels = [0,0,0,0,0,0,0,0,0,0,0,0]
var div = amount/12;
var mod = amount%12;
var x = mod / 12;
for(i=0;i<camels.length;i++){
WScript.echo(camels[i] + "|" + Math.floor(div) + "|" + mod + "|" + x)
}
please comment if you need anymore info , Thanks
Here's my take on it. Note that for the requirement that states that the array values cannot differ from the next one by more than one, I consider the array to be looping, i.e. the value after the last value is the first value again.
var amount = 30;
var camels = [0,0,0,0,0,0,0,0,0,0,0,0];
while (amount > 0) {
var index = Math.floor(Math.random() * camels.length);
var previous = (camels.length + index - 1) % camels.length;
var next = (index + 1) % camels.length;
if (Math.abs(camels[index] + 1 - camels[previous]) <= 1
&& Math.abs(camels[index] + 1 - camels[next]) <= 1) {
camels[index]++;
amount--;
}
}
Update
As requested by the OP, here's an annotated version:
// the amount that needs to be distributed among the camels
var amount = 30;
// the actual values for all 12 camels, initially all zero
var camels = [0,0,0,0,0,0,0,0,0,0,0,0];
// as long as we have something to distribute
while (amount > 0) {
// get a random current index in the array, i.e. a value between 0 and 11
var index = Math.floor(Math.random() * camels.length);
// calculate the index previous to the current index;
// in case the current index is 0, the previous index will be 11
var previous = (camels.length + index - 1) % camels.length;
// calculate the index next to the current index;
// in case the current index is 11, the next index will be 0
var next = (index + 1) % camels.length;
// if adding 1 to the camel at the current index makes it so that
// the difference with the camel at the previous index is 1 or lower
// the difference with the camel at the next index is 1 or lower
if (Math.abs(camels[index] + 1 - camels[previous]) <= 1
&& Math.abs(camels[index] + 1 - camels[next]) <= 1) {
// go ahead and add 1 to that camel
camels[index]++;
// and decrement the amount accordingly
amount--;
}
}
By adding an outer cycle you can correctly add the remaining part of the amount.
while(amount > 0){
//add amount to camels
}
Check if this Fiddle is what you want to achieve.

Categories