Cómo reducir un rango de números con un valor mínimo y máximo conocido
Así que estoy tratando de descubrir cómo tomar un rango de números y reducir los valores para que se ajusten a un rango. La razón para querer hacer esto es que estoy intentando dibujar elipses en un jpanel java swing. Quiero que la altura y el ancho de cada elipse estén en un rango de, digamos, 1-30. Tengo métodos que encuentran los valores mínimo y máximo de mi conjunto de datos, pero no tendré el mínimo y el máximo hasta el tiempo de ejecución. ¿Hay una forma fácil de hacer esto?
Supongamos que desea escalar un rango [min,max]
a [a,b]
. Estás buscando una función (continua) que satisfaga
f(min) = a
f(max) = b
En su caso, a
sería 1 y b
sería 30, pero comencemos con algo más simple e intentemos mapearlo [min,max]
en el rango [0,1]
.
Poner min
en una función y sacar 0 se podría lograr con
f(x) = x - min ===> f(min) = min - min = 0
Entonces eso es casi lo que queremos. Pero incluirlo max
nos daría max - min
cuando realmente queremos 1. Así que tendremos que escalarlo:
x - min max - min
f(x) = --------- ===> f(min) = 0; f(max) = --------- = 1
max - min max - min
que es lo que queremos. Entonces necesitamos hacer una traducción y una escala. Ahora, si queremos obtener valores arbitrarios de a
y b
, necesitamos algo un poco más complicado:
(b-a)(x - min)
f(x) = -------------- + a
max - min
Puedes comprobar que poner min
por x
ahora da a
, y poner max
da b
.
También puede notar que (b-a)/(max-min)
hay un factor de escala entre el tamaño del nuevo rango y el tamaño del rango original. Entonces, en realidad, primero estamos traduciendo x
por -min
, escalando al factor correcto y luego traduciéndolo nuevamente al nuevo valor mínimo de a
.
Aquí hay algo de JavaScript para copiar y pegar fácilmente (esta es la respuesta de irritate):
function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed;
}
Aplicado así, escalando el rango de 10-50 a un rango entre 0-100.
var unscaledNums = [10, 13, 25, 28, 43, 50];
var maxRange = Math.max.apply(Math, unscaledNums);
var minRange = Math.min.apply(Math, unscaledNums);
for (var i = 0; i < unscaledNums.length; i++) {
var unscaled = unscaledNums[i];
var scaled = scaleBetween(unscaled, 0, 100, minRange, maxRange);
console.log(scaled.toFixed(2));
}
0,00, 18,37, 48,98, 55,10, 85,71, 100,00
Editar:
Sé que respondí esto hace mucho tiempo, pero aquí hay una función más limpia que uso ahora:
Array.prototype.scaleBetween = function(scaledMin, scaledMax) {
var max = Math.max.apply(Math, this);
var min = Math.min.apply(Math, this);
return this.map(num => (scaledMax-scaledMin)*(num-min)/(max-min)+scaledMin);
}
Aplicado así:
[-4, 0, 5, 6, 9].scaleBetween(0, 100);
[0, 30.76923076923077, 69.23076923076923, 76.92307692307692, 100]