Matemáticas: mapeo de números


Respuestas:


210

Si su número X se encuentra entre A y B, y desea que Y esté entre C y D, puede aplicar la siguiente transformación lineal:

Y = (X-A)/(B-A) * (D-C) + C

Eso debería darle lo que desea, aunque su pregunta es un poco ambigua, ya que también podría mapear el intervalo en la dirección inversa. Solo tenga cuidado con la división por cero y debería estar bien.


47
Entonces tal vez marque esta respuesta como "aceptada" haciendo clic en la marca de verificación junto a ella.
Konrad Rudolph

16
Para mayor claridad, me gusta new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter

1
¿Existe una derivación para esta ecuación en alguna parte?
shaveenk

@shaveenk debería ser la ecuación de una línea, con Y=f(X)=m*X+b, donde myb se han determinado simultáneamente a partir de las siguientes dos ecuaciones de restricción que resultan de la sustitución de los valores de X e Y en los puntos finales requeridos: C=m*A+byD=m*B+b
Chris Chiasson

También terminé necesitando usar X=A+(A-B)*tpara demostrar la igualdad entre este enfoque y el de Peter. Es esencialmente una no dimensionalización de X. ( t=(X-A)/(A-B))
Chris Chiasson

21

Divida para obtener la proporción entre los tamaños de los dos rangos, luego reste el valor inicial de su rango inicial, multiplique por la proporción y agregue el valor inicial de su segundo rango. En otras palabras,

R = (20 - 10) / (6 - 2)
y = (x - 2) * R + 10

Esto distribuye uniformemente los números del primer rango en el segundo rango.


Esto no funciona. Mi rango es 1000000000 a 9999999999 y los números pueden ser del 1 al 999999999.
Dejell

@Odelya Por supuesto que funciona. Es una transformación matemática bastante simple. Solo necesita usar un tipo de número lo suficientemente grande (bignum o similar). Sus números son simplemente demasiado grandes para enteros de 32 bits, pero los enteros de 64 bits, por ejemplo, funcionarán.
Konrad Rudolph

Son de tipo doble. doble R = (20 - 10) / (6 - 2); doble y = (X - 2) * R + 10;
Dejell

@Odelya Sin embargo, el mismo problema. Debería leer sobre la precisión del punto flotante. De hecho, esta es una lectura obligatoria: Lo que todo informático debe saber sobre la aritmética de punto flotante : si necesita un tipo de punto flotante con números tan grandes, es posible que deba utilizar un tipo de número de precisión arbitraria .
Konrad Rudolph

¿Puede recomendarme de un tipo java que pueda hacerlo?
Dejell

7

Sería bueno tener esta funcionalidad en la java.lang.Mathclase, ya que es una función muy requerida y está disponible en otros idiomas. Aquí hay una implementación simple:

final static double EPSILON = 1e-12;

public static double map(double valueCoord1,
        double startCoord1, double endCoord1,
        double startCoord2, double endCoord2) {

    if (Math.abs(endCoord1 - startCoord1) < EPSILON) {
        throw new ArithmeticException("/ 0");
    }

    double offset = startCoord2;
    double ratio = (endCoord2 - startCoord2) / (endCoord1 - startCoord1);
    return ratio * (valueCoord1 - startCoord1) + offset;
}

Estoy poniendo este código aquí como referencia para el futuro y puede que ayude a alguien.


4

Aparte, este es el mismo problema que el clásico convertir celcius a farenheit donde desea mapear un rango de números que equivale 0 - 100 (C) a 32 - 212 (F).


¿Cómo es esta una respuesta?
Shinzou

Es un ejemplo de la aplicación de la pregunta. Muchos tienen este simple problema en clases introductorias de CS y no consideran que la solución se pueda generalizar a otros problemas. Estaba tratando de agregar contexto a la pregunta original. La pregunta original ya había sido respondida adecuadamente.
Metro

1

Cada intervalo de unidad en el primer rango ocupa (dc) / (ba) "espacio" en el segundo rango.

Seudo:

var interval = (d-c)/(b-a)
for n = 0 to (b - a)
    print c + n*interval

La forma en que maneja el redondeo depende de usted.


1
int srcMin = 2, srcMax = 6;
int tgtMin = 10, tgtMax = 20;

int nb = srcMax - srcMin;
int range = tgtMax - tgtMin;
float rate = (float) range / (float) nb;

println(srcMin + " > " + tgtMin);
float stepF = tgtMin;
for (int i = 1; i < nb; i++)
{
  stepF += rate;
  println((srcMin + i) + " > " + (int) (stepF + 0.5) + " (" + stepF + ")");
}
println(srcMax + " > " + tgtMax);

Con cheques en dividir por cero, por supuesto.


1

si su rango de [a a b] y desea mapearlo en [c ad] donde x es el valor que desea mapear, use esta fórmula (mapeo lineal)

double R = (d-c)/(b-a)
double y = c+(x*R)+R
return(y)


0

Además de la respuesta de @PeterAllenWebb, si desea revertir el resultado, use lo siguiente:

reverseX = (B-A)*(Y-C)/(D-C) + A
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.