Calculating Pi in JavaScript using Gregory-Leibniz Series - javascript

I have to calculate value of Pi using Gregory-Leibniz series:
pi = 4 * ((1/1 - 1/3) + (1/5 - 1/7) + (1/9 - 1/11) + ...)
I want to write a function in JavaScript that would take the number of digits that needs to be displayed as an argument. But I'm not sure if my way of thinking is fine here.
This is what I got so far:
function pi(n) {
var pi = 0;
for (i=1; i <= n; i+2) {
pi = 4 * ((1/i) + (1/(i+2)))
}
return pi;
}
How do I write the pi calculation so it calculates values till n?

You could use an increment of 4 and multiply at the end of the function with 4.
n is not the number of digits, but the counter of the value of the series.
function pi(n) {
var v = 0;
for (i = 1; i <= n; i += 4) { // increment by 4
v += 1 / i - 1 / (i + 2); // add the value of the series
}
return 4 * v; // apply the factor at last
}
console.log(pi(1000000000));

You may also do as follows; The function will iterate 10M times and will return you PI with n significant digits after the decimal point.
function getPI(n){
var i = 1,
p = 0;
while (i < 50000000){
p += 1/i - 1/(i+2);
i += 4;
}
return +(4*p).toFixed(n);
}
var myPI = getPI(10);
console.log("myPI #n:100M:", myPI);
console.log("Math.PI :", Math.PI);
console.log("The Diff :", Math.PI-myPI);

Related

Rounding-off from whole numbers to whole numbers in JavaScript?

So I have some numbers x = 320232 y = 2301 z = 12020305. I want to round these numbers off using JavaScript so that they become x = 320000 y = 2300 z = 12000000.
I tried Math.round and Math.floor but turns out that they only work with decimal values like
a = 3.1; Math.round(a); // Outputs 3 and not whole numbers.
So my question is can we round of whole numbers using JavaScript and If yes then how?
Edit: I want it to the round of to the starting 3 digit places as seen in the variables above. Like If there was another variable called c = 423841 It should round off to become c = 424000.
You could work with the logarithm of ten and adjust the digits.
const
format = n => v => {
if (!v) return 0;
const l = Math.floor(Math.log10(Math.abs(v))) - n + 1;
return Math.round(v / 10 ** l) * 10 ** l;
};
console.log([0, -9876, 320232, 2301, 12020305, 123456789].map(format(3)));
The solution is to first calculate how many numbers need to be rounded away, and then use that in a round.
Math.round(1234/100)*100 would round to 1200 so we can use this to round. We then only need to determan what to replace 100 with in this example.
That is that would be a 1 followed by LENGTH - 3 zeros. That number can be calculated as it is 10 to the power of LENGTH - 3, in JS: 10 ** (length - 3).
var x = 320232;
var y = 2301;
var z = 12020305;
function my_round(number){
var org_number = number;
// calculate integer number
var count = 0;
if (number >= 1) ++count;
while (number / 10 >= 1) {
number /= 10;
++count;
}
// length - 3
count = Math.round(count) - 3;
if (count < 0){
count = 0;
}
// 10 to the power of (length - 3)
var helper = 10 ** count;
return Math.round(org_number/helper)*helper;
}
alert(my_round(x));
alert(my_round(y));
alert(my_round(z));
It is not the prettiest code, though I tried to make it explainable code.
This should work:
function roundToNthPlace(input, n) {
let powerOfTen = 10 ** n
return Math.round(input/powerOfTen) * powerOfTen;
}
console.log([320232, 2301,12020305, 423841].map(input => roundToNthPlace(input, 3)));
Output: [320000, 2000, 12020000, 424000]

Am I using the proper predicate for my lambda?

I've been working on this program for a few hours, and I finally got it to output - NaN. I dont know how this could be, I'm pushing a product of real numbers into the array... Somebody help! What did I miss? The problem is to find the largest product produced by 13 adjacent digits within the 1000 digit number assigned to _1000digits.
// what is the largest product of 13 adjacent digits within this 1000 digit number
function largestProduct() {
_1000digits = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450;
separateDigits = _1000digits.toString().split("");
products = [];
var a = 0;
var b = 1;
var c = 2;
var d = 3;
var e = 4;
var f = 5;
var g = 6;
var h = 7;
var i = 8;
var j = 9;
var k = 10;
var l = 11;
var m = 12;
while (m <= 999) {
products.push(
separateDigits[a] *
separateDigits[b] *
separateDigits[c] *
separateDigits[d] *
separateDigits[e] *
separateDigits[f] *
separateDigits[g] *
separateDigits[h] *
separateDigits[i] *
separateDigits[j] *
separateDigits[k] *
separateDigits[l] *
separateDigits[m]
);
a++;
b++;
c++;
d++;
e++;
f++;
g++;
h++;
i++;
j++;
k++;
l++;
m++;
}
products.sort((a, b) => a - b);
console.log(products.pop());
}
largestProduct();
Short:
To work with such huge numbers you'll want to use a special data structure, like BigInt.
Long:
There are a few issues with your code, the first one is trying to store such a huge number in a variable without any treatment. A JavaScript number can only store values up to 25^3 - 1, your number is a lot bigger than that.
If you run:
_1000digits = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450
console.log(_1000digits)
You'll see the output is "Infinity" because that's such a huge number JavaScript doesn't know how to store it entirely.
You're also not checking if the numbers you're accessing actually exist, so if you put a smaller number in _1000digits you'll end up multiplying by undefined, which will result in NaN:
_1000digits = 700
separateDigits = _1000digits.toString().split("")
var f = 5
console.log(separateDigits[f])

How to divide number n in javascript into x parts, where the sum of all the parts equals the number?

I have a number which I need to divide into 5 parts. However, I want each part to be a random number. But when all the parts are added together, they equal the original number. I am unsure of how to do this with JavaScript. Furthermore, I don't want the min of the divided parts to be 0 or 1, I want to set the min myself.
For example, the number is 450. I want the divided parts to be no less than 60. So to start, the array would be [60,60,60,60,60]. But I want to randomize so that they all add up to 450. What would be the best way to go about doing this?
Thank you!
This is what I've tried so far:
let i = 0;
let number = 450;
let numArray = [];
while(i <= 5){
while(number > 0) {
let randomNum = Math.round(Math.random() * number) + 1;
numArray.push(randomNum);
number -= randomNum;
}
i += 1;
}
let your number be N, and let pn be the nth part. To get 5 parts:
p1 = random number between 0 and N
p2 = random number between 0 and N - p1
p3 = random number between 0 and N - p2 - p1
p4 = random number between 0 and N - p3 - p2 - p1
p5 = N - p4 - p3 - p2 - p1
Edit 2017
To make it seem more random, shuffle the numbers after you generate them
Edit 2020
I guess some code wouldn't hurt. Using ES7 generators:
function* splitNParts(num, parts) {
let sumParts = 0;
for (let i = 0; i < parts - 1; i++) {
const pn = Math.ceil(Math.random() * (num - sumParts))
yield pn
sumParts += pn
}
yield num - sumParts;
}
Fiddle Link
Sum the five minimums (eg min = 60) up:
var minSum = 5 * min
Then get the difference between your original number (orNumber = 450) and minSum.
var delta = orNumber - minSum
Now you get 4 different random numbers in the range from 0 to exclusive 1.
Sort these numbers ascending.
Foreach of these randoms do the following:
Subtract it from the last one (or zero for the first)
Multiply this number with the delta and you get one of the parts.
The last part is the delta minus all other parts.
Afterwards you just have to add your min to all of the parts.
This function generates random numbers from 0 to 1, adds them together to figure out what they need to be multiplied by to provide the correct range. It has the benefit of all the numbers being fairly distributed.
function divvy(number, parts, min) {
var randombit = number - min * parts;
var out = [];
for (var i=0; i < parts; i++) {
out.push(Math.random());
}
var mult = randombit / out.reduce(function (a,b) {return a+b;});
return out.map(function (el) { return el * mult + min; });
}
var d = divvy(450, 6, 60)
console.log(d);
console.log("sum - " + d.reduce(function(a,b){return a+b}));
You can use a do..while loop to subtract a minimum number from original number, keep a copy of original number for subtraction at conclusion of loop to push the remainder to the array
let [n, total, m = n] = [450, 0];
const [min, arr] = [60, []];
do {
n -= min; // subtract `min` from `n`
arr.push(n > min ? min : m - total); // push `min` or remainder
total += arr[arr.length - 1]; // keep track of total
} while (n > min);
console.log(arr);
To randomize output at resulting array select a number greater than min and less than n to create a random number within a specific range
let [n, total, m = n] = [450, 0];
const [min, arr, range = min + min / 2] = [60, []];
do {
let r = Math.random() * (range - min) + min; // random number in our range
n -= r; // subtract `min` from `n`
arr.push(n > min ? r : m - total); // push `r` or remainder
total += arr[arr.length - 1]; // keep track of total
} while (n > min);
console.log(arr);
I made a longer version for beginners.
const n = 450;
const iterations = 5;
const parts = [];
// we'll use this to store what's left on each iteration
let remainder = n;
for (let i = 1; i <= iterations; i += 1) {
// if it's the last iteration, we should just use whatever
// is left after removing all the other random numbers
// from our 450
if (i === iterations) {
parts.push(remainder);
break;
}
// every time we loop, a random number is created.
// on the first iteration, the remainder is still 450
const part = Math.round(Math.random() * remainder);
parts.push(part);
// we must store how much is left after our random numbers
// are deducted from our 450. we will use the lower number
// to calculate the next random number
remainder -= part;
}
// let's print out the array and the proof it still adds up
const total = totalFromParts(parts);
console.log(parts);
console.log('Total is still ' + total);
// this function loops through each array item, and adds it to the last
// just here to test the result
function totalFromParts(parts) {
return parts.reduce((sum, value) => sum + value, 0);
}
There are much more efficient ways to code this, but in the interest of explaining the logic of solving the problem, this walks through that step by step, transforming the values and explaining the logic.
// Set start number, number of fragments
// minimum fragment size, define fragments array
var n = 450
var x = 5
var minNumber = 60
var fragment = n / x
// stuff array with equal sized fragment values
var fragments = []
for (i = 0; i < x; i++) {
fragments[i] = fragment;
}
document.write("fragments: " + fragments);
var delta = [];
// iterate through fragments array
// get a random number each time between the fragment size
// and the minimum fragment sized defined above
// for even array slots, subtract the value from the fragment
// for odd array slots, add the value to the fragment
// skip the first [0] value
for (i = 1; i< x; i++) {
delta[i] = Math.floor(Math.random() * (fragment - minNumber));
document.write("<br />delta: " + delta[i]);
if((i % 2) == 1) {
fragments[i] -= delta[i]
}
else {
fragments[i] += delta[i]
}
}
// set the initial fragment value to 0
fragments[0] = 0
// defines a function we can use to total the array values
function getSum(total, num) {
return total + num;
}
// get the total of the array values, remembering the first is 0
var partialTotal = fragments.reduce(getSum)
document.write("<br />partial sum: " + partialTotal);
// set the first array value to the difference between
// the total of all the other array values and the original
// number the array was to sum up to
fragments[0] = (n - partialTotal)
// write the values out and profit.
document.write("<br />fragments: " + fragments);
var grandTotal = fragments.reduce(getSum)
document.write("<br />Grand total: " + grandTotal);
https://plnkr.co/edit/oToZe7LGpQS4dIVgYHPi?p=preview

Get a random number focused on center

Is it possible to get a random number between 1-100 and keep the results mainly within the 40-60 range? I mean, it will go out of that range rarely, but I want it to be mainly within that range... Is it possible with JavaScript/jQuery?
Right now I'm just using the basic Math.random() * 100 + 1.
The simplest way would be to generate two random numbers from 0-50 and add them together.
This gives a distribution biased towards 50, in the same way rolling two dice biases towards 7.
In fact, by using a larger number of "dice" (as #Falco suggests), you can make a closer approximation to a bell-curve:
function weightedRandom(max, numDice) {
let num = 0;
for (let i = 0; i < numDice; i++) {
num += Math.random() * (max/numDice);
}
return num;
}
JSFiddle: http://jsfiddle.net/797qhcza/1/
You have some good answers here that give specific solutions; let me describe for you the general solution. The problem is:
I have a source of more-or-less uniformly distributed random numbers between 0 and 1.
I wish to produce a sequence of random numbers that follow a different distribution.
The general solution to this problem is to work out the quantile function of your desired distribution, and then apply the quantile function to the output of your uniform source.
The quantile function is the inverse of the integral of your desired distribution function. The distribution function is the function where the area under a portion of the curve is equal to the probability that the randomly-chosen item will be in that portion.
I give an example of how to do so here:
http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/
The code in there is in C#, but the principles apply to any language; it should be straightforward to adapt the solution to JavaScript.
Taking arrays of numbers, etc. isn't efficient. You should take a mapping which takes a random number between 0 to 100 and maps to the distribution you need. So in your case, you could take f(x)=-(1/25)x2+4x to get a distribution with the most values in the middle of your range.
I might do something like setup a "chance" for the number to be allowed to go "out of bounds". In this example, a 20% chance the number will be 1-100, otherwise, 40-60:
$(function () {
$('button').click(function () {
var outOfBoundsChance = .2;
var num = 0;
if (Math.random() <= outOfBoundsChance) {
num = getRandomInt(1, 100);
} else {
num = getRandomInt(40, 60);
}
$('#out').text(num);
});
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Generate</button>
<div id="out"></div>
fiddle: http://jsfiddle.net/kbv39s9w/
I needed to solve this problem a few years ago and my solution was easier than any of the other answers.
I generated 3 randoms between the bounds and averaged them. This pulls the result towards the centre but leaves it completely possible to reach the extremities.
It looks stupid but you can use rand twice:
var choice = Math.random() * 3;
var result;
if (choice < 2){
result = Math.random() * 20 + 40; //you have 2/3 chance to go there
}
else {
result = Math.random() * 100 + 1;
}
Sure it is possible. Make a random 1-100. If the number is <30 then generate number in range 1-100 if not generate in range 40-60.
There is a lot of different ways to generate such random numbers. One way to do it is to compute the sum of multiple uniformly random numbers. How many random numbers you sum and what their range is will determine how the final distribution will look.
The more numbers you sum up, the more it will be biased towards the center. Using the sum of 1 random number was already proposed in your question, but as you notice is not biased towards the center of the range. Other answers have propose using the sum of 2 random numbers or the sum of 3 random numbers.
You can get even more bias towards the center of the range by taking the sum of more random numbers. At the extreme you could take the sum of 99 random numbers which each were either 0 or 1. That would be a binomial distribution. (Binomial distributions can in some sense be seen as the discrete version of normal distributions). This can still in theory cover the full range, but it has so much bias towards the center that you should never expect to see it reach the endpoints.
This approach means you can tweak just how much bias you want.
What about using something like this:
var loops = 10;
var tries = 10;
var div = $("#results").html(random());
function random() {
var values = "";
for(var i=0; i < loops; i++) {
var numTries = tries;
do {
var num = Math.floor((Math.random() * 100) + 1);
numTries--;
}
while((num < 40 || num >60) && numTries > 1)
values += num + "<br/>";
}
return values;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
The way I've coded it allows you to set a couple of variables:
loops = number of results
tries = number of times the function will try to get a number between 40-60 before it stops running through the while loop
Added bonus: It uses do while!!! Awesomeness at its best
You can write a function that maps random values between [0, 1) to [1, 100] according to weight. Consider this example:
Here, the value 0.95 maps to value between [61, 100].
In fact we have .05 / .1 = 0.5, which, when mapped to [61, 100], yields 81.
Here is the function:
/*
* Function that returns a function that maps random number to value according to map of probability
*/
function createDistributionFunction(data) {
// cache data + some pre-calculations
var cache = [];
var i;
for (i = 0; i < data.length; i++) {
cache[i] = {};
cache[i].valueMin = data[i].values[0];
cache[i].valueMax = data[i].values[1];
cache[i].rangeMin = i === 0 ? 0 : cache[i - 1].rangeMax;
cache[i].rangeMax = cache[i].rangeMin + data[i].weight;
}
return function(random) {
var value;
for (i = 0; i < cache.length; i++) {
// this maps random number to the bracket and the value inside that bracket
if (cache[i].rangeMin <= random && random < cache[i].rangeMax) {
value = (random - cache[i].rangeMin) / (cache[i].rangeMax - cache[i].rangeMin);
value *= cache[i].valueMax - cache[i].valueMin + 1;
value += cache[i].valueMin;
return Math.floor(value);
}
}
};
}
/*
* Example usage
*/
var distributionFunction = createDistributionFunction([
{ weight: 0.1, values: [1, 40] },
{ weight: 0.8, values: [41, 60] },
{ weight: 0.1, values: [61, 100] }
]);
/*
* Test the example and draw results using Google charts API
*/
function testAndDrawResult() {
var counts = [];
var i;
var value;
// run the function in a loop and count the number of occurrences of each value
for (i = 0; i < 10000; i++) {
value = distributionFunction(Math.random());
counts[value] = (counts[value] || 0) + 1;
}
// convert results to datatable and display
var data = new google.visualization.DataTable();
data.addColumn("number", "Value");
data.addColumn("number", "Count");
for (value = 0; value < counts.length; value++) {
if (counts[value] !== undefined) {
data.addRow([value, counts[value]]);
}
}
var chart = new google.visualization.ColumnChart(document.getElementById("chart"));
chart.draw(data);
}
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(testAndDrawResult);
<script src="https://www.google.com/jsapi"></script>
<div id="chart"></div>
Here's a weighted solution at 3/4 40-60 and 1/4 outside that range.
function weighted() {
var w = 4;
// number 1 to w
var r = Math.floor(Math.random() * w) + 1;
if (r === 1) { // 1/w goes to outside 40-60
var n = Math.floor(Math.random() * 80) + 1;
if (n >= 40 && n <= 60) n += 40;
return n
}
// w-1/w goes to 40-60 range.
return Math.floor(Math.random() * 21) + 40;
}
function test() {
var counts = [];
for (var i = 0; i < 2000; i++) {
var n = weighted();
if (!counts[n]) counts[n] = 0;
counts[n] ++;
}
var output = document.getElementById('output');
var o = "";
for (var i = 1; i <= 100; i++) {
o += i + " - " + (counts[i] | 0) + "\n";
}
output.innerHTML = o;
}
test();
<pre id="output"></pre>
Ok, so I decided to add another answer because I felt like my last answer, as well as most answers here, use some sort of half-statistical way of obtaining a bell-curve type result return. The code I provide below works the same way as when you roll a dice. Therefore, it is hardest to get 1 or 99, but easiest to get 50.
var loops = 10; //Number of numbers generated
var min = 1,
max = 50;
var div = $("#results").html(random());
function random() {
var values = "";
for (var i = 0; i < loops; i++) {
var one = generate();
var two = generate();
var ans = one + two - 1;
var num = values += ans + "<br/>";
}
return values;
}
function generate() {
return Math.floor((Math.random() * (max - min + 1)) + min);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
I'd recommend using the beta distribution to generate a number between 0-1, then scale it up. It's quite flexible and can create many different shapes of distributions.
Here's a quick and dirty sampler:
rbeta = function(alpha, beta) {
var a = 0
for(var i = 0; i < alpha; i++)
a -= Math.log(Math.random())
var b = 0
for(var i = 0; i < beta; i++)
b -= Math.log(Math.random())
return Math.ceil(100 * a / (a+b))
}
var randNum;
// generate random number from 1-5
var freq = Math.floor(Math.random() * (6 - 1) + 1);
// focus on 40-60 if the number is odd (1,3, or 5)
// this should happen %60 of the time
if (freq % 2){
randNum = Math.floor(Math.random() * (60 - 40) + 40);
}
else {
randNum = Math.floor(Math.random() * (100 - 1) + 1);
}
The best solution targeting this very problem is the one proposed by BlueRaja - Danny Pflughoeft but I think a somewhat faster and more general solution is also worth mentioning.
When I have to generate random numbers (strings, coordinate pairs, etc.) satisfying the two requirements of
The result set is quite small. (not larger than 16K numbers)
The result set is discreet. (like integer numbers only)
I usually start by creating an array of numbers (strings, coordinate pairs, etc.) fulfilling the requirement (In your case: an array of numbers containing the more probable ones multiple times.), then choose a random item of that array. This way, you only have to call the expensive random function once per item.
Distribution
5% for [ 0,39]
90% for [40,59]
5% for [60,99]
Solution
var f = Math.random();
if (f < 0.05) return random(0,39);
else if (f < 0.95) return random(40,59);
else return random(60,99);
Generic Solution
random_choose([series(0,39),series(40,59),series(60,99)],[0.05,0.90,0.05]);
function random_choose (collections,probabilities)
{
var acc = 0.00;
var r1 = Math.random();
var r2 = Math.random();
for (var i = 0; i < probabilities.length; i++)
{
acc += probabilities[i];
if (r1 < acc)
return collections[i][Math.floor(r2*collections[i].length)];
}
return (-1);
}
function series(min,max)
{
var i = min; var s = [];
while (s[s.length-1] < max) s[s.length]=i++;
return s;
}
You can use a helper random number to whether generate random numbers in 40-60 or 1-100:
// 90% of random numbers should be between 40 to 60.
var weight_percentage = 90;
var focuse_on_center = ( (Math.random() * 100) < weight_percentage );
if(focuse_on_center)
{
// generate a random number within the 40-60 range.
alert (40 + Math.random() * 20 + 1);
}
else
{
// generate a random number within the 1-100 range.
alert (Math.random() * 100 + 1);
}
If you can use the gaussian function, use it. This function returns normal number with average 0 and sigma 1.
95% of this number are within average +/- 2*sigma. Your average = 50, and sigma = 5 so
randomNumber = 50 + 5*gaussian()
The best way to do that is generating a random number that is distributed equally in a certain set of numbers, and then apply a projection function to the set between 0 and a 100 where the projection is more likely to hit the numbers you want.
Typically the mathematical way of achieving this is plotting a probability function of the numbers you want. We could use the bell curve, but let's for the sake of easier calculation just work with a flipped parabola.
Let's make a parabola such that its roots are at 0 and 100 without skewing it. We get the following equation:
f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x
Now, all the area under the curve between 0 and 100 is representative of our first set where we want the numbers generated. There, the generation is completely random. So, all we need to do is find the bounds of our first set.
The lower bound is, of course, 0. The upper bound is the integral of our function at 100, which is
F(x) = -x^3/3 + 50x^2
F(100) = 500,000/3 = 166,666.66666 (let's just use 166,666, because rounding up would make the target out of bounds)
So we know that we need to generate a number somewhere between 0 and 166,666. Then, we simply need to take that number and project it to our second set, which is between 0 and 100.
We know that the random number we generated is some integral of our parabola with an input x between 0 and 100. That means that we simply have to assume that the random number is the result of F(x), and solve for x.
In this case, F(x) is a cubic equation, and in the form F(x) = ax^3 + bx^2 + cx + d = 0, the following statements are true:
a = -1/3
b = 50
c = 0
d = -1 * (your random number)
Solving this for x yields you the actual random number your are looking for, which is guaranteed to be in the [0, 100] range and a much higher likelihood to be close to the center than the edges.
This answer is really good. But I would like to post implementation instructions (I'm not into JavaScript, so I hope you will understand) for different situation.
Assume you have ranges and weights for every range:
ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}
Initial Static Information, could be cached:
Sum of all weights (108 in sample)
Range selection boundaries. It basically this formula: Boundary[n] = Boundary[n - 1] + weigh[n - 1] and Boundary[0] = 0. Sample has Boundary = {0, 1, 3, 103, 108}
Number generation:
Generate random number N from range [0, Sum of all weights).
for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
Take ith range and generate random number in that range.
Additional note for performance optimizations. Ranges don't have to be ordered neither ascending nor descending order, so for faster range look-up range that has highest weight should go first and one with lowest weight should go last.

javascript - generate a new random number

I have a variable that has a number between 1-3.
I need to randomly generate a new number between 1-3 but it must not be the same as the last one.
It happens in a loop hundreds of times.
What is the most efficient way of doing this?
May the powers of modular arithmetic help you!!
This function does what you want using the modulo operator:
/**
* generate(1) will produce 2 or 3 with probablity .5
* generate(2) will produce 1 or 3 with probablity .5
* ... you get the idea.
*/
function generate(nb) {
rnd = Math.round(Math.random())
return 1 + (nb + rnd) % 3
}
if you want to avoid a function call, you can inline the code.
Here is a jsFiddle that solves your problem : http://jsfiddle.net/AsMWG/
I've created an array containing 1,2,3 and first I select any number and swap it with the last element. Then I only pick elements from position 0 and 1, and swap them with last element.
var x = 1; // or 2 or 3
// this generates a new x out of [1,2,3] which is != x
x = (Math.floor(2*Math.random())+x) % 3 + 1;
You can randomly generate numbers with the random number generator built in to javascript. You need to use Math.random().
If you're push()-ing into an array, you can always check if the previously inserted one is the same number, thus you regenerate the number. Here is an example:
var randomArr = [];
var count = 100;
var max = 3;
var min = 1;
while (randomArr.length < count) {
var r = Math.floor(Math.random() * (max - min) + min);
if (randomArr.length == 0) {
// start condition
randomArr.push(r);
} else if (randomArr[randomArr.length-1] !== r) {
// if the previous value is not the same
// then push that value into the array
randomArr.push(r);
}
}
As Widor commented generating such a number is equivalent to generating a number with probability 0.5. So you can try something like this (not tested):
var x; /* your starting number: 1,2 or 3 */
var y = Math.round(Math.random()); /* generates 0 or 1 */
var i = 0;
var res = i+1;
while (i < y) {
res = i+1;
i++;
if (i+1 == x) i++;
}
The code is tested and it does for what you are after.
var RandomNumber = {
lastSelected: 0,
generate: function() {
var random = Math.floor(Math.random()*3)+1;
if(random == this.lastSelected) {
generateNumber();
}
else {
this.lastSelected = random;
return random;
}
}
}
RandomNumber.generate();

Categories

Resources