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.

PeterAllenWebb
fuente
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.

Konrad Rudolph
fuente
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.

Sourabh Bhat
fuente
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).

Metro
fuente
¿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.

Chris Cudmore
fuente
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.

PhiLho
fuente
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)
Mohamed Ashraf
fuente
0

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

reverseX = (B-A)*(Y-C)/(D-C) + A
Dejell
fuente