repetir rango numeros numero entre entero aleatorios aleatorio math range scaling max minimum

math - numeros - Cómo reducir un rango de números con un valor mínimo y máximo conocido



numeros aleatorios en javascript sin repetir (6)

Así que estoy tratando de averiguar cómo tomar un rango de números y escalar los valores para que se ajusten a un rango. La razón para querer hacer esto es que estoy tratando de dibujar puntos suspensivos en un jpanel de java swing. Quiero que la altura y el ancho de cada elipse estén en un rango de, por ejemplo, 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 máximo hasta el tiempo de ejecución. ¿Hay una forma fácil de hacer esto?


Aquí hay algunos JavaScript para facilitar copiar y pegar (esta es la respuesta de irritate):

function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) { return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed; }

Aplicado como tal, escalando el rango 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 utilizo 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 como tal:

[-4, 0, 5, 6, 9].scaleBetween(0, 100);

[0, 30.76923076923077, 69.23076923076923, 76.92307692307692, 100]


Así es como lo entiendo:

¿Qué porcentaje miente x en un rango?

Supongamos que tiene un rango de 0 a 100 . Dado un número arbitrario de ese rango, ¿en qué "porcentaje" de ese rango se encuentra? Esto debería ser bastante simple, 0 sería 0% , 50 sería 50% y 100 sería 100% .

Ahora, ¿qué pasaría si su rango fuera de 20 a 100 ? No podemos aplicar la misma lógica anterior (dividir por 100) porque:

20 / 100

no nos da 0 ( 20 debería ser 0% ahora). Esto debería ser fácil de arreglar, solo necesitamos hacer el numerador 0 para el caso de 20 . Podemos hacer eso restando:

(20 - 20) / 100

Sin embargo, esto ya no funciona para 100 porque:

(100 - 20) / 100

No nos da el 100% . Nuevamente, podemos arreglar esto restando también del denominador:

(100 - 20) / (100 - 20)

Una ecuación más general para averiguar qué% x encuentra en un rango sería:

(x - MIN) / (MAX - MIN)

Rango de escala a otro rango

Ahora que sabemos en qué porcentaje se encuentra un número en un rango, podemos aplicarlo para asignar el número a otro rango. Veamos un ejemplo.

old range = [200, 1000] new range = [10, 20]

Si tenemos un número en el rango anterior, ¿cuál sería el número en el rango nuevo? Digamos que el número es 400 . Primero, averigua qué porcentaje 400 está dentro del rango anterior. Podemos aplicar nuestra ecuación de arriba.

(400 - 200) / (1000 - 200) = 0.25

Así, 400 encuentra en el 25% de la gama anterior. Solo necesitamos averiguar qué número es el 25% del nuevo rango. Piensa en qué es el 50% de [0, 20] . Serían las 10 ¿no? ¿Cómo llegaste a esa respuesta? Bueno, solo podemos hacer:

20 * 0.5 = 10

Pero, ¿qué pasa con [10, 20] ? Necesitamos cambiar todo por 10 ahora. p.ej:

((20 - 10) * 0.5) + 10

Una fórmula más generalizada sería:

((MAX - MIN) * PERCENT) + MIN

Al ejemplo original de lo que el 25% de [10, 20] es:

((20 - 10) * 0.25) + 10 = 12.5

Entonces, 400 en el rango [200, 1000] se mapearían a 12.5 en el rango [10, 20]

TLDR

Para mapear x del rango anterior al nuevo rango:

OLD PERCENT = (x - OLD MIN) / (OLD MAX - OLD MIN) NEW X = ((NEW MAX - NEW MIN) * OLD PERCENT) + NEW MIN


Me encontré con esta solución, pero esto realmente no se ajusta a mi necesidad. Así que cavé un poco en el código fuente de d3. Yo personalmente recomendaría hacerlo como lo hace d3.scale.

Así que aquí se escala el dominio al rango. La ventaja es que puede voltear las señales a su rango objetivo. Esto es útil ya que el eje y en la pantalla de una computadora va hacia arriba, por lo que los valores grandes tienen una pequeña y.

public class Rescale { private final double range0,range1,domain0,domain1; public Rescale(double domain0, double domain1, double range0, double range1) { this.range0 = range0; this.range1 = range1; this.domain0 = domain0; this.domain1 = domain1; } private double interpolate(double x) { return range0 * (1 - x) + range1 * x; } private double uninterpolate(double x) { double b = (domain1 - domain0) != 0 ? domain1 - domain0 : 1 / domain1; return (x - domain0) / b; } public double rescale(double x) { return interpolate(uninterpolate(x)); } }

Y aquí está la prueba donde puedes ver lo que quiero decir.

public class RescaleTest { @Test public void testRescale() { Rescale r; r = new Rescale(5,7,0,1); Assert.assertTrue(r.rescale(5) == 0); Assert.assertTrue(r.rescale(6) == 0.5); Assert.assertTrue(r.rescale(7) == 1); r = new Rescale(5,7,1,0); Assert.assertTrue(r.rescale(5) == 1); Assert.assertTrue(r.rescale(6) == 0.5); Assert.assertTrue(r.rescale(7) == 0); r = new Rescale(-3,3,0,1); Assert.assertTrue(r.rescale(-3) == 0); Assert.assertTrue(r.rescale(0) == 0.5); Assert.assertTrue(r.rescale(3) == 1); r = new Rescale(-3,3,-1,1); Assert.assertTrue(r.rescale(-3) == -1); Assert.assertTrue(r.rescale(0) == 0); Assert.assertTrue(r.rescale(3) == 1); } }


Para mayor comodidad, aquí está el algoritmo de Irritate en forma de Java. Agregue la verificación de errores, el manejo de excepciones y modifique según sea necesario.

public class Algorithms { public static double scale(final double valueIn, final double baseMin, final double baseMax, final double limitMin, final double limitMax) { return ((limitMax - limitMin) * (valueIn - baseMin) / (baseMax - baseMin)) + limitMin; } }

Ensayador:

final double baseMin = 0.0; final double baseMax = 360.0; final double limitMin = 90.0; final double limitMax = 270.0; double valueIn = 0; System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax)); valueIn = 360; System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax)); valueIn = 180; System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax)); 90.0 270.0 180.0


Tomé la respuesta de Irritate y la reformulé para minimizar los pasos computacionales para los cómputos subsiguientes, factorizándola en la menor cantidad de constantes. La motivación es permitir que un escalador sea capacitado en un conjunto de datos y luego se ejecute en datos nuevos (para un ML algo). En efecto, se parece mucho al uso del MinMaxScaler de preprocesamiento de SciKit para Python.

Por lo tanto, x'' = (ba)(x-min)/(max-min) + a (donde b! = A) se convierte en x'' = x(ba)/(max-min) + min(-b+a)/(max-min) + a que puede reducirse a dos constantes en la forma x'' = x*Part1 + Part2 .

Aquí hay una implementación de C # con dos constructores: uno para entrenar y otro para recargar una instancia entrenada (por ejemplo, para soportar la persistencia).

public class MinMaxColumnSpec { /// <summary> /// To reduce repetitive computations, the min-max formula has been refactored so that the portions that remain constant are just computed once. /// This transforms the forumula from /// x'' = (b-a)(x-min)/(max-min) + a /// into x'' = x(b-a)/(max-min) + min(-b+a)/(max-min) + a /// which can be further factored into /// x'' = x*Part1 + Part2 /// </summary> public readonly double Part1, Part2; /// <summary> /// Use this ctor to train a new scaler. /// </summary> public MinMaxColumnSpec(double[] columnValues, int newMin = 0, int newMax = 1) { if (newMax <= newMin) throw new ArgumentOutOfRangeException("newMax", "newMax must be greater than newMin"); var oldMax = columnValues.Max(); var oldMin = columnValues.Min(); Part1 = (newMax - newMin) / (oldMax - oldMin); Part2 = newMin + (oldMin * (newMin - newMax) / (oldMax - oldMin)); } /// <summary> /// Use this ctor for previously-trained scalers with known constants. /// </summary> public MinMaxColumnSpec(double part1, double part2) { Part1 = part1; Part2 = part2; } public double Scale(double x) => (x * Part1) + Part2; }


Digamos 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 y tratemos de asignar [min,max] al rango [0,1] .

Poner min en una función y salir 0 se podría lograr con

f(x) = x - min ===> f(min) = min - min = 0

Así que eso es casi lo que queremos. Pero poner el max nos daría el 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. Así que tenemos que hacer una traducción y una escala. Ahora, si en cambio queremos obtener valores arbitrarios de a y b , necesitamos algo un poco más complicado:

(b-a)(x - min) f(x) = -------------- + a max - min

Puede verificar que poner min en x ahora da a , y poner en max da b .

También puede observar que (ba)/(max-min) es un factor de escala entre el tamaño del nuevo rango y el tamaño del rango original. Entonces realmente estamos traduciendo primero x por -min , escalando al factor correcto, y luego volviéndolo a traducir al nuevo valor mínimo de a .

Espero que esto ayude.