web-dev-qa-db-fra.com

Math - numéros de correspondance

Comment mapper des nombres, linéairement, entre a et b pour aller entre c et d.

Autrement dit, je veux que les nombres entre 2 et 6 correspondent aux nombres entre 10 et 20 ... mais j'ai besoin du cas généralisé.

Mon cerveau est frit.

81
Sam

Si votre nombre X est compris entre A et B et que vous souhaitez que Y soit compris entre C et D, vous pouvez appliquer la transformation linéaire suivante:

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

Cela devrait vous donner ce que vous voulez, même si votre question est un peu ambiguë, car vous pouvez également cartographier l’intervalle dans le sens inverse. Faites juste attention à la division par zéro et ça devrait aller.

170
PeterAllenWebb

Divisez pour obtenir le rapport entre les tailles des deux plages, puis soustrayez la valeur initiale de votre plage initiale, multipliez-la par le ratio et ajoutez la valeur initiale de votre deuxième plage. En d'autres termes,

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

Cela répartit uniformément les nombres de la première plage de la deuxième plage.

18
Konrad Rudolph

Il serait bien d’avoir cette fonctionnalité dans la classe Java.lang.Math, car c’est une fonction largement requise et disponible dans d’autres langues . Voici une implémentation 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;
}

Je mets ce code ici comme référence pour le futur moi-même et peut-être que cela aidera quelqu'un.

5
Sourabh Bhat

En passant, c’est le même problème que le classique convertissant celcius en farenheit où vous voulez mapper une plage de nombres qui équivaut à 0 - 100 (C) à 32 - 212 (F).

3
Metro

Chaque intervalle d'unité sur la première plage occupe (d-c)/(b-a) "espace" sur la seconde plage.

Pseudo:

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

Comment vous gérez l'arrondi, c'est à vous de décider.

1
Chris Cudmore
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);

Avec des contrôles sur la division par zéro, bien sûr.

1
PhiLho

si votre plage va de [a à b] et que vous voulez la mapper dans [c à d] où x est la valeur que vous voulez mapper utilisez cette formule (mappage linéaire)

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

En plus de @PeterAllenWebb answer, si vous souhaitez inverser le résultat, utilisez les éléments suivants:

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