Solving for unknown exponent - javascript

I'trying to make a function creates a list of exponentially increasing numbers where the sum of the numbers is equal to some maximum.
For example:
/*
* What do I have to raise N by in order for the sum to be 40
*/
(1**x + 2**x + 3**x + 4**x + 5**x) === 40;
/*
* Wolframalpha tells me: x = 1.76445
* But how can I solve with JS.
*/
function listOfNumbers(n, maxSum) {
// implementation
}
var result = listOfNumbers(5, 40);
/*
* result === [1, 3.397..., 6.947..., 11.542..., 17.111...]
*
*/
result.reduce((acc, n) => acc += n) === 40

try https://en.wikipedia.org/wiki/Bisection_method
TOLERANCE = 1e-5;
let f = x => (1 ** x + 2 ** x + 3 ** x + 4 ** x + 5 ** x - 40);
let
min = 0,
max = 1,
x = 0;
// roughly locate the upper bound
while (f(max) < 0)
max *= 2;
// repeatedly bisect the interval [min, max]
while (1) {
x = (min + max) / 2;
let r = f(x);
if (Math.abs(r) < TOLERANCE)
break;
if (r > 0) {
max = x
} else {
min = x
}
}
// now, x is a "good enough" approximation of the root
console.log(x);

Related

How to retrieve the max value of a function output

I have a function f(x). I want to calculate x where f(x) = max. How can I do that in a script?
Given: x is a positive number, f(x) can be positive or negative.
I want to start with x = 1.
If f(1) is already negative I'm not interested anymore.
With increasing x, also f(x) returns increasing values, until a peak is reached, and then f(x) decreases. x at the peak is what I am interested in.
EDIT: Ok what I tried so far:
I tried starting with x = 1, then x *=2. If f(x) is smaller then the last result, I set back x to x/4.
Example: f(16) = 9, f(32) = 11, f(64) = 10. The peak can be between x=16 and x=64. So my new starting point is f(16). From there on, I want to continue in a similar way, but I cant find an algorithm.
Here is what I got so far:
let x = 1
let lasty = 0
let holder = 1
while (iteration++ < 20) {
let y = myFunction(x)
if(y < lasty ) {
x = x / 4
holder = 1
}
x += holder * 2
holder += 1
lasty = y
}
EDIT2: My improved version which works quite good but for sure not perfect:
let x = 1
let lasty = 0
let iteration = 0
let previousX = 1
let lastX = 1
let step = 2
let maxY = -Infinity
let bestX = 0
let randomInput = Math.random
while (iteration++ < 20) {
let y = myFunction(x, randomInput) //added randomInput to show the function does not rely on x alone
if (y < 0 && iteration == 1) break
if(y > maxY) {
maxY = y
bestX = x
}
if (y < lasty) {
x = previousX
step *= 0.8
lasty = 0
} else {
previousX = lastX
lastX = x
x = x * step
lasty = y
}
}
if(bestX > 0) {
console.log(`Got best x: ${bestX}`)
}
EDIT3: Added random additional parameter to emphasise the needed approach
EDIT4: I should also mention that the probability of f(x) = max is the highest when 0 < x < 100000
Assuming you want to maximise y, You can just store the x and y values where xy is highest:
let highestX = 1; // this is just the corresponding x value for the highest y
let highestY = -Infinity;
let x = 1
let lasty = 0
let holder = 1
while (iteration++ < 20) {
let y = myFunction(x);
// code for storing the highest y and corresponding x
if (y > highestY) {
highestY = y;
highestX = x;
}
if(y < lasty ) {
x = x / 4
holder = 1
}
x += holder * 2
holder += 1
lasty = y
}
console.log('f(x) is maximised when x =', highestX);
If you know the function has a single peak in a given range, and the closest value within a tolerance is acceptable, then a simple binary search should be all you need. Here we default the range to the entire safe integer range, but you can supply your own with findPeak (tolerance) (fn, rangeMin, rangeMax), perhaps something like findPeak (1e-8) (fn, 0, 100000).
const findPeak = (ε) => (
fn,
min = Number.MIN_SAFE_INTEGER,
max = Number.MAX_SAFE_INTEGER,
mid = min / 2 + max / 2
) =>
max - min < ε
? mid
: fn (min) < fn (max)
? findPeak (ε) (fn, mid, max)
: findPeak (ε) (fn, min, mid)
const fn = n => 600 + 50 * n - n ** 2
console .log (findPeak (1e-8) (fn)) //=> 24.99999991050572
This code is simple. It is less efficient than it might be if we took advantage of the fact that we will reuse fn (min) or fn (max) on each subsequent call, and probably would be faster with a while loop rather than the recursion. I'll leave such optimization to you. But I would first see if this is already fast enough, as simple code is much easier to work with.
It could be solved using recursive function calls.
// array of arguments
const args = [...Array(16).keys()];
// sample function
const sampleFunc = (x) => Math.sin(x);
// recursive function
function getElementWithFuncMaximum(arr, func, index = 0, currValue = 0) {
const currFuncValue = func(arr[index]);
return currFuncValue >= currValue
? getElementWithFuncMaximum(arr, func, index + 1, currFuncValue)
: arr[index - 1];
}
console.log(getElementWithFuncMaximum(args, sampleFunc));

Check if value equals 0 regardless of decimal values?

I'm balancing the sigma of a Gaussian kernel. I desire to break the while loop once the first index of the array is equal to 0.(any decimal value).
I found that simply trying to match 0 won't match if the value also contains any decimals like 0.000013301.
var gaussianKernel1d = (function() {
var sqr2pi = Math.sqrt(2 * Math.PI);
return function gaussianKernel1d(size, sigma) {
'use strict';
// ensure size is even and prepare variables
var width = (size / 2) | 0,
kernel = new Array(width * 2 + 1),
norm = 1.0 / (sqr2pi * sigma),
coefficient = 2 * sigma * sigma,
total = 0,
x;
// set values and increment total
for (x = -width; x <= width; x++) {
total += kernel[width + x] = norm * Math.exp((-x * x) / coefficient);
}
// divide by total to make sure the sum of all the values is equal to 1
for (x = 0; x < kernel.length; x++) {
kernel[x] /= total;
}
return kernel;
};
})();
let i = 0.01;
let kernelArray = gaussianKernel1d(512, i);
while (kernelArray[0] === 0) {
i = i + 0.01;
kernelArray = gaussianKernel1d(512, i);
Same is also true when using Array[0] !== 0.0)
How can I match any values in the dataset that equal 0 regardless of the decimal values? And without converting the array to integers.
epascarello's solution is correct:
while (Math.floor(kernelArray[0]) === 0)
That will continue looping as long as kernelArray[0] is a value >= 0 and < 1.
Or of course, the other obvious approach is just to write the equation in the description above:
while (kernelArray[0]) >= 0 && kernelArray[0] < 1)
Try following
$b = 0;
while ( (string) Array[0] !== (string) $b) {
// code that just regenerates the random values
}

Split number into 4 random numbers

I want to split 10 into an array of 4 random numbers, but neither can be 0 or higher than 4. For example [1,2,3,4], [1,4,4,1] or [4,2,3,1].
I think it's an easy question, but for some reason I can't think of how to do this. If someone has some instruction that would be very helpful!
Edit:
This is the code I have now, but I generates also a total number under 10:
let formation = [];
let total = 0;
for (let i = 0; i < 4; i ++) {
if (total < 9) {
formation[i] = Math.floor(Math.random() * 4) + 1;
} else {
formation[i] = 1;
}
}
You could create all possible combinations and pick a random array.
function get4() {
function iter(temp) {
return function (v) {
var t = temp.concat(v);
if (t.length === 4) {
if (t.reduce(add) === 10) {
result.push(t);
}
return;
}
values.forEach(iter(t));
};
}
const
add = (a, b) => a + b,
values = [1, 2, 3, 4],
result = [];
values.forEach(iter([]));
return result;
}
console.log(get4().map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
An algorithm for getting random values without a list of all possible combinations
It works by using a factor for the random value and an offset, based on the actual sum, index, minimum sum which is needed for the next index, and the maximum sum.
The offset is usually the minimum sum, or the greater value of the difference of sum and maximum sum. For getting the factor, three values are taken for the minimum for multiplying the random value.
The table illustrates all possible values of the sum and the needed iterations, based on a given value and the iteration for getting all values.
At the beginning the sum is the value for distribution in small parts. The result is the second block with a rest sum of 14 ... 10, because it is possible to take a value of 1 ... 5. The third round follows the same rules. At the end, the leftover sum is taken as offset for the value.
An example with 1, ..., 5 values and 5 elements with a sum of 15 and all possibilities:
min: 1
max: 5
length: 5
sum: 15
smin = (length - index - 1) * min
smax = (length - index - 1) * max
offset = Math.max(sum - smax, min)
random = 1 + Math.min(sum - offset, max - offset, sum - smin - min)
index sum sum min sum max random offset
------- ------- ------- ------- ------- -------
_ 0 15 4 20 5 1
1 14 3 15 5 1
1 13 3 15 5 1
1 12 3 15 5 1
1 11 3 15 5 1
_ 1 10 3 15 5 1
2 13 2 10 3 3
2 12 2 10 4 2
2 11 2 10 5 1
2 10 2 10 5 1
2 9 2 10 5 1
2 8 2 10 5 1
2 7 2 10 5 1
2 6 2 10 4 1
_ 2 5 2 10 3 1
3 10 1 5 1 5
3 9 1 5 2 4
3 8 1 5 3 3
3 7 1 5 4 2
3 6 1 5 5 1
3 5 1 5 4 1
3 4 1 5 3 1
3 3 1 5 2 1
_ 3 2 1 5 1 1
4 5 0 0 1 5
4 4 0 0 1 4
4 3 0 0 1 3
4 2 0 0 1 2
4 1 0 0 1 1
The example code takes the target 1, ..., 4 with a length of 4 parts and a sum of 10.
function getRandom(min, max, length, sum) {
return Array.from(
{ length },
(_, i) => {
var smin = (length - i - 1) * min,
smax = (length - i - 1) * max,
offset = Math.max(sum - smax, min),
random = 1 + Math.min(sum - offset, max - offset, sum - smin - min),
value = Math.floor(Math.random() * random + offset);
sum -= value;
return value;
}
);
}
console.log(Array.from({ length: 10 }, _ => getRandom(1, 4, 4, 10).join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
The simplest solution is brute force.
Make a while loop to nest your calculations in
In the loop, create an empty array and fill it with random values until length is reached
Check if the sum of the array is your desired value, and if it is then break the loop
The above should run until you have a result.
Two things worth considering though.
Your can easily test if a solution is at all possible by calculating, that length-of-array times minimum-value isn't more than the sum and length-of-array times maximum-value isn't less than the sum.
A loop based on random conditions could potentially run forever, so a maximum amount of iterations might be desirable.
Both of these points are considered in the snippet below:
function randomNumber(max, min) {
while (true) {
var r = Math.round(Math.random() * max);
if (r >= min) {
return r;
}
}
}
function splitXintoYComponentsBetweenMaxAndMin(numberToSplit, numberOfSplits, maxValue, minValue, onUpdate) {
if (minValue === void 0) {
minValue = 1;
}
//Test that a result can exist
if (maxValue * numberOfSplits < numberToSplit || minValue * numberOfSplits > numberToSplit) {
return new Promise(function(resolve, reject) {
resolve(false);
});
}
//Create returner array
var arr = [];
var accumulator = 0;
while (arr.length < numberOfSplits) {
var val = randomNumber(Math.floor(numberToSplit / numberOfSplits), minValue);
accumulator += val;
arr.push(val);
}
return new Promise(function(resolve, reject) {
function runTest() {
var d = Date.now();
var localMaxValue = Math.min(maxValue, Math.ceil((numberToSplit - accumulator) / 4));
//Combination loop
while (accumulator < numberToSplit && Date.now() - d < 17) {
var index = Math.round(Math.random() * (arr.length - 1));
if (arr[index] >= maxValue) {
continue;
}
var r = randomNumber(localMaxValue, minValue);
while (arr[index] + r > maxValue || accumulator + r > numberToSplit) {
if (Date.now() - d >= 17) {
break;
}
r = randomNumber(localMaxValue, minValue);
}
if (arr[index] + r > maxValue || accumulator + r > numberToSplit) {
continue;
}
arr[index] += r;
accumulator += r;
}
if (accumulator < numberToSplit) {
if (onUpdate !== void 0) {
onUpdate(arr);
}
requestAnimationFrame(runTest);
} else {
resolve(arr);
}
}
runTest();
});
}
//TEST
var table = document.body.appendChild(document.createElement('table'));
table.innerHTML = "<thead><tr><th>Number to split</th><th>Number of splits</th><th>Max value</th><th>Min value</th><th>Run</th></tr></thead>" +
"<tbody><tr><th><input id=\"number-to-split\" value=\"10\" type=\"number\" min=\"1\"/></th><th><input id=\"number-of-splits\" value=\"4\" type=\"number\" min=\"1\"/></th><th><input id=\"max-value\" type=\"number\" min=\"1\" value=\"4\"/></th><th><input id=\"min-value\" type=\"number\" min=\"1\" value=\"1\"/></th><th><input id=\"run\" type=\"button\" value=\"Run\"/></th></tr></tbody>";
var output = document.body.appendChild(document.createElement('pre'));
output.style.overflowX = "scroll";
document.getElementById("run").onclick = function() {
splitXintoYComponentsBetweenMaxAndMin(parseInt(document.getElementById("number-to-split").value, 10), parseInt(document.getElementById("number-of-splits").value, 10), parseInt(document.getElementById("max-value").value, 10), parseInt(document.getElementById("min-value").value, 10))
.then(function(data) {
if (data !== false) {
output.textContent += data.join("\t") + '\n';
} else {
output.textContent += 'Invalid data\n';
}
});
};
EDIT 1 - Big calculations
Using requestAnimationFrame and Promises the code can now execute asynchronously, which allows for longer calculation time without bothering the user.
I also made the random function scale with the remaining range, greatly reducing the amount of calculations needed for big numbers.
A litte late to the show, but I found this a fun task to think about so here you go. My approach does not need to create all partitions, it also does not rely on pure luck of finding a random match, it is compact and it should be unbiased.
It works efficiently even when large values are used, as long as max is not too limiting.
const len = 4;
const total = 10;
const max = 4;
let arr = new Array(len);
let sum = 0;
do {
// get some random numbers
for (let i = 0; i < len; i++) {
arr[i] = Math.random();
}
// get the total of the random numbers
sum = arr.reduce((acc, val) => acc + val, 0);
// compute the scale to use on the numbers
const scale = (total - len) / sum;
// scale the array
arr = arr.map(val => Math.min(max, Math.round(val * scale) + 1));
// re-compute the sum
sum = arr.reduce((acc, val) => acc + val, 0);
// loop if the sum is not exactly the expected total due to scale rounding effects
} while (sum - total);
console.log(arr);
Basically you need the partitions (See https://en.wikipedia.org/wiki/Partition_(number_theory)) of 10 and apply your conditions on the resulting set.
// Partition generator taken from
// https://gist.github.com/k-hamada/8aa85ac9b334fb89ac4f
function* partitions(n) {
if (n <= 0) throw new Error('positive integer only');
yield [n];
var x = new Array(n);
x[0] = n;
for (var i = 1; i < n; i++) x[i] = 1;
var m = 0, h = 0, r, t;
while (x[0] != 1) {
if (x[h] == 2) {
m += 1;
x[h] = 1;
h -= 1;
} else {
r = x[h] - 1;
x[h] = r;
t = m - h + 1;
while (t >= r) {
h += 1;
x[h] = r;
t -= r;
}
m = h + (t !== 0 ? 1 : 0);
if (t > 1) {
h += 1;
x[h] = t;
}
}
yield x.slice(0, m + 1);
}
}
results = [];
// Get all possible partitions for your number
for (var partition of partitions(10)) {
// Apply your conditions (must be 4 numbers, none of them greater than 4)
if(partition.length != 4 || partition.some((x) => x > 4)) continue;
results.push(partition);
}
console.log(results);
Given that:
In a collection of n positive numbers that sum up to S, at least one of them will be less than S divided by n (S/n)
and that you want a result set of exactly 4 numbers,
you could use the following algorithm:
Get a random number from range [1, floor(S/n)], in this case floor(10/4) = 2, so get a random number in the range of [1,2]. Lets mark it as x1.
Get a random number from range [1, floor((S - x1)/(n - 1))]. Lets mark it as x2.
Get a random number from range [1, floor((S - x1 - x2)/(n - 2))].
Continue until you get x(n-1).
Get the last number by doing S - x1 - x2 .... - x(n-1).
Finally, extend the above algorithm with a condition to limit the upper limit of the random numbers.
In n steps, you can get a collection.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRandomCollection(min, max, length, sum) {
var collection = [];
var leftSum = sum - (min - 1);
for(var i = 0; i < length - 1; i++) {
var number = getRandomInt(min, Math.min(Math.ceil(leftSum/(length - i)), max));
leftSum -= number;
collection.push(number);
}
leftSum += min - 1;
while(leftSum > max) {
var randomIndex = Math.floor(Math.random() * collection.length);
if(collection[randomIndex] < max) {
collection[randomIndex]++;
leftSum--;
}
}
collection.push(leftSum);
return collection;
}
console.log(getRandomCollection(1, 4, 4, 10).join(' + ') + ' = 10');
console.log(getRandomCollection(3, 20, 10, 100).join(' + ') + ' = 100');
Reference
My answer using the same algorithm for another question
Quick and simple but biased and nondeterministically terminating
function partition(sum, len, min, max) {
const a = Array(len).fill(min)
while (a.reduce((acc,val)=>acc+val) < sum) {
const i = Math.random()*len|0
if (a[i] < max) a[i]++
}
return a
}
console.log(Array(10).fill().map(_=>partition(10, 4, 1, 4).join(' ')))
.as-console-wrapper { max-height: 100% !important; top: 0; }
The while loop can loop forever with an infinitesimal probability. To prevent this, you can keep another array of "valid indexes" and delete keys of it when the value reaches max.
this calculates a random number from 1 to 4
wrap it on a function to your needs to generate the arrays
Math.floor(Math.random() * 4) + 1
var randomNumber = Math.floor(Math.random() * 4) + 1 ;
console.log(randomNumber);
It was too easy.
var values = null;
while(true) {
var currentSum = 0;
var expectedSum = 10;
values = [];
while(expectedSum !== currentSum) {
//var value = Math.floor(Math.random() * 9) + 1;
var value = Math.floor(Math.random() * 4) + 1;
if(value + currentSum > expectedSum) {
continue;
}
currentSum += value;
values.push(value);
}
if(values.length === 4) {
break;
} else {
console.log('false iteration')
}
}
console.log(values);

Convert a number into sum of two other numbers so the difference is minimum

In Mars, there are only two denominations of currency ,x and y. A
Marsian goes to a bar and the bill is "z". Using x and y he has to pay
the bill. But the bar doesn't tender change, any extra money will be
taken as tips.
So write a function in JavaScript that helps the marsian to reduce the
tips.
The function takes in x, y, z and returns the amount of tip he has to
pay.
Example 1
Input: 2, 5, 109
Output: 0
Explanation: 21 coins of 5, and 2 coins of 2
Example 2
Input: 5, 7, 43
Output: 0
Explanation: 4 coins of 7, and 3 coins of 5
Example 3
Input: 15, 19, 33
Output: 1
Explanation: 1 coin of 15 and 1 coin of 19
Solution: I think this is level one DP problem, something like subset sum. Like for finding the optimal tip for the larger number, knowing the optimal tip for all the below numbers would help.
const coinA = 2
const coinB = 5
const sum = 13
var arr = [];
arr[0] =0;
console.log(getMyTip(coinA, coinB, sum));
function getMyTip(){
for(var i=1; i<= sum; i++){
var minA, minB;
if( i < coinA){
minA = coinA - i;
}else{
minA = arr[i - coinA];
}
if( i < coinB){
minB = coinB - i;
}else{
minB = arr [i - coinB]
}
arr[i] = Math.min(minA, minB);
}
return arr[sum];
}
Jsfiddle: https://jsfiddle.net/7c4sbe46/
But I'm not sure why it is not getting accepted. Please let me know if I'm missing something with the logic here.
It is more related to diophantine equations, i.e. is there a solution to a.x+b.y=z ? The answer is yes if z is a multiple of the greatest common divisor of x and y (called it gcd). If not, your tip will be the difference between 1. the smaller number divisible by gcd and greater than z
and 2. z.
Once you know the value of the tip, you can even easily know the number of x and y that you need by slightly modifying the value of z to (z+tip).
#include <stdio.h>
int main()
{
int curr1, curr2, bill;
scanf("%d %d %d",&curr1,&curr2,&bill);
int gcd, tip=0;
int x=curr1;
int y=curr2;
while(x!=y)
{
if(x > y)
x -= y;
else
y -= x;
}
gcd=x;
if((bill%curr1==0) || (bill%curr2==0) || (bill%(curr1 + curr2)==0)){
tip = 0;
} else if(bill>(curr1 + curr2) && (bill % gcd==0)) {
tip = 0;
} else if((curr1 + curr2) > bill){
if(curr2 > curr1){
tip = (bill % (curr2-curr1));
}else{
tip = (bill % (curr1-curr2));
}
}
printf("%d",tip);
return 0;
}
There is no need to use dp for this. Here is the simple solution -
// x -> first currency denomination
// y -> second currency denomination
// z -> total bill
var calculateTip = function(x,y,z) {
var xMax = Math.floor(z/x);
var tip = y;
if(xMax == 0) {
tip = (x-z) < (Math.ceil(z/y)*y - z) ? (x-z) : (Math.ceil(z/y)*y - z);
}
while (xMax>=0) {
var tempTip = xMax*x + Math.ceil((z-xMax*x)/y)*y - z;
if(tempTip < tip) {
tip = tempTip;
}
xMax--;
}
return tip;
}
var minimumTip = function(x,y,z) {
if(x>y) {
return calculateTip(x,y,z);
} else {
return calculateTip(y,x,z);
}
}
console.log(minimumTip(2, 5, 109));
var findTip = function(x=2, y=5, z=13){
var x = x;
var y = y;
var z = z;
var tip ;
var temp1 = x;
var temp2 = y
function findNumber(num,total){
if(num > total){
return num-total;
}
else{
var q = Math.floor(total/num);
return ((q+1)*num)-total;
}
}
function findMin(a,b,c){
var min ;
if(a<b && a<c){
min = a
}else{
if(b<c){
min = b;
}else{
min = c;
}
}
return min;
}
while(temp1!=temp2)
{
if(temp1 > temp2)
temp1 -= temp2;
else
temp2 -= temp1;
}
var factor =temp1;
if(z%x == 0 || z%y == 0 || z%(x+y) == 0) {
tip = 0;
}else if(z%factor == 0 && z>=x*y - x -y){
tip = 0;
}
else {
var minX= findNumber(x,z);
var minY = findNumber(y,z);
var minXY = findNumber(x+y,z);
console.log(minX,minY,minXY)
tip = findMin(minX,minY,minXY);
}
alert('the tip is '+ tip.toString());
return tip;
}
findTip(21, 11, 109);

Math.ceil to nearest five at position 1

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;
}

Categories

Resources