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.
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.
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.
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.
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).
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.
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.
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)
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