I'm not sure that the name for what I need is probability density but anyway.
I'd like to find a function or algorithm for generating random numbers in the specified range with specified chance and linear change.
For example, specified range and chances are:
Range from -10 to 15.
Chaces are: [[-10, 5], [-5, 0], [3, 5], [10, 30], [15, 0]]
The result after generating a number could be any numbers from that range except -5 and 15. So could be -10, -9, -8, -7 ... 13, 14.
And chance to get -10 is 5 units.
-5 is 0 units.
3 is 5 units.
So -9 will appear with 4 units chance and -8 with 3 units chance etc.
And in this case, the most common numbers should appear about 10 because 10 has 30 units chance.
You could turn the probabilities into numbers from 0 to 1 that sum up to 1:
const chances = [[-10, 5], [-5, 0], [3, 5], [10, 30], [15, 0]];
const total = chances.reduce((total, [_, v]) => total + v, 0);
chances.forEach(it => it[1] /= total);
Now you can generate a random number, and find the first value where the sum of probabilities before is bigger than that number:
let random = Math.random();
const result = chances.find(([_, p]) => (random -= p) < 0)[0];
Related
Using JavaScript, an array of numbers is going to be charted on a graph (for my purposes it is a spline chart):
[12, 22, 25, 38, 47]
I want all the Y axis values to be multiples of 5. I have the Y axis capped at the next multiple of 5 that occurs after the highest number in the array. Since 47 is the highest number, the next multiple of 5 is 50 (call that value the "cap"), and that is the top value ("tick") on the chart's Y axis. After figuring that out, I know that the Y axis should be 0 a the bottom, and 50 at the top, but I want to override the default behavior and tell it exactly how many ticks to show in between, and what the values should be for those ticks.
This is where it gets tricky, because of the following restrictions:
Use the fewest number of ticks possible (0, the max value, and at least one tick in between)
Bottom value is always zero
All Y tick values are multiples of 5
Y ticks are evenly spaced on the axis
For the previous example, fifty is the cap, which is divisible by two, so the Y axis would only need one tick in between the bottom and top, resulting in three tick values of 0, 25, 50. The function I am trying to build would receive 50 as an argument, and output 3 as the result. Then I would know the chart needs 3 ticks, and I could generate it like so:
My question is, given that a charted value can be any multiple of 5, how can I calculate the fewest number of ticks needed on the Y axis, using only increments that are multiples of 5? It may be easiest to just show the first few scenarios to illustrate how the pattern is not (at least to me) obvious:
value = tick1, tick2, tick3, etc. >> return count of ticks
05 = 5, 4, 3, 2, 1, 0 >> return 6;// This case is an outlier and can be set manually
10 = 10, 5, 0 >> return 3;
15 = 15, 10, 5, 0 >> return 4;
20 = 20, 10, 0 >> return 3;
25 = 25, 20, 15, 10, 5, 0 >> return 6;
30 = 30, 15, 0 >> return 3;
35 = 35, 30, 25, 20, 15, 10, 5, 0 >> return 8;
40 = 40, 20, 0 >> return 3;
45 = 45, 30, 15, 0 >> return 4;
50 = 50, 25, 0 >> return 3;
55 = 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0 >> return 12;
It was at this point that I realized there is probably an equation or function that exists to address this dilemma, maybe even something to do with the Fibonacci sequence or Dan Brown. I couldn't find any related SO questions, and my use of "increments of 5" may make this use case too specific to return google results on the general principle, so any advice is appreciated.
You could take an iterative approach by using the fifth of the value an checke the abulity for the division by 2, 3, 5, 7 and so on and return this value incremented by one.
const
fn = v => {
v /= 5;
if (v === 1) return 6;
if (v % 2 === 0) return 3;
var i = 1;
while ((i += 2) < v) if (v % i === 0) return i + 1;
return v + 1;
},
format = s => s.toString().padStart(2);
var values = Array.from({ length: 12 }, (_, i) => (i + 1) * 5),
data = values.map(fn);
console.log(...values.map(format));
console.log(...data.map(format));
I have a range of values like, for example, [0, 100] = [minValue, maxValue] and the number of bands, for example BANDS_NUMBER = 5.
So I can obtain these bands:
[0 - 20]
[21 - 40]
[41 - 60]
[61 - 80]
[81 - 100]
Then I want to associate a scale value at each range:
i: 0 --> [0 - 20] --> 0.2
i: 1 --> [21 - 40] --> 0.4
i: 2 --> [41 - 60] --> 0.6
i: 3 --> [61 - 80] --> 0.8
i: 4 --> [81 - 100] --> 1
This value is computed in this way: (i + 1) / BANDS_NUMBER where i is the index of a hypothetical loop.
Then I have an input n whose value is in range [minValue, maxValue] = [0, 100].
What I want is the scale value related to this number.
So, for example, if:
n = 0 --> scaleValue = 0.2
n = 10 --> scaleValue = 0.2
n = 20 --> scaleValue = 0.2
n = 35 --> scaleValue = 0.4
n = 68 --> scaleValue = 0.8
n = 99 --> scaleValue = 1
...
How can I create a function like that? I imagine a function like that:
function map(n, minValue, maxValue, bandsNumber) {
const scaleValue = ...
return scaleValue
}
All the values here are examples, I want that all works with any other values.
I don't know how to do to that. I need some help...
Nina Scholz's answer is wrong. Her normalize function returns 0.4 instead of 0.2 for the value 20:
function normalize(min, max, bands, n) {
return n === max
? 1
: Math.floor(1 + ((n - min) / (max - min)) * bands) / bands;
}
console.log(normalize(0, 100, 5, 20)); // expected 0.2, actual 0.4
Because 20 is in the first band, it should have the value 0.2:
i: 0 --> [0 - 20] --> 0.2
i: 1 --> [21 - 40] --> 0.4
i: 2 --> [41 - 60] --> 0.6
i: 3 --> [61 - 80] --> 0.8
i: 4 --> [81 - 100] --> 1
The correct answer is:
const index = (min, max, bands, n) =>
Math.floor(bands * (n - min) / (max - min + 1));
const band = n => index(0, 100, 5, n);
console.log(band(0), band(20)); // 0 0
console.log(band(21), band(40)); // 1 1
console.log(band(41), band(60)); // 2 2
console.log(band(61), band(80)); // 3 3
console.log(band(81), band(100)); // 4 4
As you can see, the edge cases are handled correctly. How did we get to this answer?
First, we find the length of the range which is max - min + 1. The + 1 is important because there are 101 elements in the range [0 - 100] inclusive.
Next, we get the index of the number n in the given range (i.e. n - min).
Then, we divide the index of n by the number of elements in the range to get a value in the range [0 - 1). Note that 1 is not in the range.
Finally, we multiply this value by the number of bands and discard the fractional part. The result is our index.
Note that if the length of the range is not divisible by the number of bands then the first x bands will have one additional element, where x is the remainder of dividing the length of the range by the number of bands.
Finally, we can get the value you want by incrementing the resulting index and then dividing it by the number of bands:
const index = (min, max, bands, n) =>
Math.floor(bands * (n - min) / (max - min + 1));
const value = (min, max, bands, n) =>
(index(min, max, bands, n) + 1) / bands;
const result = n => value(0, 100, 5, n);
console.log(result(0), result(20)); // 0.2 0.2
console.log(result(21), result(40)); // 0.4 0.4
console.log(result(41), result(60)); // 0.6 0.6
console.log(result(61), result(80)); // 0.8 0.8
console.log(result(81), result(100)); // 1 1
Hope that helps.
You could take a formula, which take the range and the slot and returns a normalized value.
Because of the range, which is a bit too long (the last value is included in the interval), you need a check for the last value and prevent getting the next value, outside of the wanted interval.
function normalize(min, max, bands, n) {
return n === max
? 1
: Math.floor(1 + ((n - min) / (max - min)) * bands) / bands;
}
// 0.2 0.2 0.4 0.4 0.8 1 1
console.log(...[0, 10, 20, 35, 68, 99, 100].map(normalize.bind(null, 0, 100, 5)));
You can use a native Array.map function to map each value.
Something like this:
const vals = [
[0, 20],
[21, 40],
[41, 60],
[61, 80],
[81, 100],
];
const BANDS_NUMBER = 5;
const result = vals.map((range, index) => (index + 1) / BANDS_NUMBER);
console.log(result);
I have a large array of numbers that I need to distribute into x ranges of numbers, such that each range contains an equal number of elements from the original array.
For example, when x = 4 , the following array
[1, 1, 2.5, 3, 6, 7, 10, 11.2, 14, 25, 35, 50, 75, 85.5, 100, 120, 128.9, 150, 200, 260]
would produce an array of length x + 1 with the following
[1, 6.5, 30, 110, 260]
1 is the lowest value in the array
6.5 is the midpoint of 6 and 7
30 is the midpoint of 25 and 35
110 is the midpoint of 100 and 120
260 is the highest value
Essentially, this will give me 4 ranges of numbers 1-6.5, 6.5-30, 30-110, and 110-260. Each range would contain 5 numbers of the original array.
Needs to be able to handle a dynamic number of elements that will not necessarily be divided evenly by x.
I asked this question on Mathematics but was told it was more of a programming question.
Based on your question , the following javascript code should satisfy your requirements, I've tested its correctness with a few values of x, but you might want to check if this code applies to all possible test cases for your problem.
var arr = [1, 1, 2.5, 3, 6, 7, 10, 11.2, 14, 25, 35, 50, 75, 85.5, 100, 120, 128.9, 150, 200, 260, 261, 262, 263, 264, 265];
total_elem = arr.length;
var x = 5;
var each_set_elem = total_elem / x;
var i = 1;
var temp = [];
temp.push(arr[0]);
for (i = each_set_elem; i < total_elem; i += each_set_elem) {
this_elem = (arr[i] + arr[i - 1]) / 2;
temp.push(this_elem);
}
temp.push(arr[total_elem - 1]);
console.log(temp);
This code satisfies the test case in the question as well as for x=5 it spits out the correct 6 points so that the 5 ranges each have 4 elements from the given array set.
I need to calculate Logarithmic, Power Regression points(Trend line) for my array [X, Y] values below here using java script. Example:
X: [1, 2, 3, 4, 5, 6]
Y: [10, 40, 35, 50, 55, 65]
I referred more sites. But they are only given formula and its description. There is no example how to calculate X,Y values using that equation. Any one please help me on how to calculate Logarithmic, Power Regression.
Thanks,
Bharathi
This question already has answers here:
Google Charts vertical axis in whole numbers
(4 answers)
Closed 9 years ago.
How to set the vAxis value format in google Line Chart in order to not get multiple lines with same value? Let's say I have lines with values: 1.5 1.0 0.5 and 0, after I set the format to '#', I got 0, 0, 1, 1...
Possible duplicate of this question
Copy-pasted answer:
If you just want numbers to display as whole numbers, then it's
easy:
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['x', 'Cats', 'Blanket 1', 'Blanket 2'],
['A', 1, 1, 0.5],
['B', 2, 0.5, 1],
['C', 4, 1, 0.5],
['D', 8, 0.5, 1],
['E', 7, 1, 0.5],
['F', 7, 0.5, 1],
['G', 8, 1, 0.5],
['H', 4, 0.5, 1],
['I', 2, 1, 0.5],
['J', 3.5, 0.5, 1],
['K', 3, 1, 0.5],
['L', 3.5, 0.5, 1],
['M', 1, 1, 0.5],
['N', 1, 0.5, 1]
]);
// Create and draw the visualization.
new google.visualization.LineChart(document.getElementById('visualization')).
draw(data, {curveType: "function",
width: 500, height: 400,
vAxis: {maxValue: 10, format: '0'}}
);
}
If you go to [google playground][1] you will note that the axis labels
are 0, 2.5, 5, 7.5, 10. By adding the format: '0' to the vAxis, it
will display only whole numbers, so my labels become 0, 3, 5, 8, 10.
But obviously this is not ideal since 8 displays as being halfway
between 5 and 10, which it isn't, because the number is actually 7.5
and just being rounded.
Your ability to change the axis scale/labels is restricted. And to do
what you're asking would take a special bit of javascript to create an
appropriate scale and number of gridlines to prevent breaking things
down in to funky numbers.
Basically, you want to make sure that your maximum and minimum values,
and number of gridlines, allow for easy division such that you will
only get whole numbers. To do that, you need to create some funky new
logic. Here is some sample code that will allow you to get an
appropriate min/max axis value:
// Take the Max/Min of all data values in all graphs
var totalMax = 345;
var totalMin = -123;
// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));
// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);
// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;
// Determine the range of your values
var range = newMax - newMin;
// Define the number of gridlines (default 5)
var gridlines = 5;
// Determine an appropriate gap between gridlines
var interval = range / (gridlines - 1);
// Round that interval up to the exponent of 10
var newInterval = Math.ceil(interval/roundingDec)*roundingDec;
// Re-round your max and min to the new interval
var finalMax = Math.ceil(totalMax/newInterval)*newInterval;
var finalMin = Math.floor(totalMin/newInterval)*newInterval;
There are a couple issues here (unfortunately). The first is that I am
using the number of gridlines to determine the min/max values -- you
want to figure out how many gridlines you should have to use nice
whole numbers. The easiest way to do this, I think, would be as
follows (pseudo-code only):
// Take the Max/Min of all data values in all graphs
var totalMax = 3;
var totalMin = -1;
// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));
// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);
// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;
// Determine the range of your values
var range = newMax - newMin;
// Calculate the best factor for number of gridlines (2-5 gridlines)
// If the range of numbers divided by 2 or 5 is a whole number, use it
for (var i = 2; i <= 5; ++i) {
if ( Math.round(range/i) = range/i) {
var gridlines = i
}
}
Then you set the gridlines option (vAxis.gridlines) to the above
variable, and your max to newMax, and your min to newMin. That should
give you whole number axis labels.
Note: if your numbers are really small then the above function may not
work. The function is also not tested so please check it against some
examples on your own time and let me know if it doesn't work properly.
[1]:
http://code.google.com/apis/ajax/playground/?type=visualization#line_chart