Comment mapper les 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.
Comment mapper les 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.
Réponses:
Si votre nombre X se situe entre A et B, et que vous souhaitez que Y tombe 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, bien que votre question soit un peu ambiguë, car vous pouvez également mapper l'intervalle dans le sens inverse. Faites juste attention à la division par zéro et vous devriez être OK.
Y=f(X)=m*X+b
, où m et b ont été déterminés simultanément à partir des deux équations de contraintes suivantes qui résultent de la substitution des valeurs de X et Y aux points d'extrémité requis: C=m*A+b
etD=m*B+b
X=A+(A-B)*t
pour prouver l'égalité entre cette approche et celle de Peter. t est essentiellement une non dimensionnalisation de X. ( t=(X-A)/(A-B)
)
Divisez pour obtenir le rapport entre les tailles des deux plages, puis soustrayez la valeur de départ de votre plage initiale, multipliez par le rapport et ajoutez la valeur de départ 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 dans la seconde plage.
Ce serait bien d'avoir cette fonctionnalité dans la java.lang.Math
classe, car c'est une fonction si 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 l'avenir moi-même et cela aidera peut-être quelqu'un.
En passant, c'est le même problème que le classique convertir celcius en farenheit où vous voulez mapper une plage de nombres qui équivaut à 0 - 100 (C) à 32 - 212 (F).
Chaque intervalle unitaire sur la première plage occupe (dc) / (ba) "espace" sur la seconde plage.
Pseudo:
var interval = (d-c)/(b-a)
for n = 0 to (b - a)
print c + n*interval
La manière dont vous gérez les arrondis dépend de vous.
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 de [a à b] et que vous voulez la mapper en [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)
https://rosettacode.org/wiki/Map_range
[a1, a2] => [b1, b2]
if s in range of [a1, a2]
then t which will be in range of [b1, b2]
t= b1 + ((s- a1) * (b2-b1))/ (a2-a1)