Como mapeio números, linearmente, entre a e b para ir entre c e d.
Ou seja, quero números entre 2 e 6 para mapear números entre 10 e 20 ... mas preciso do caso generalizado.
Meu cérebro está frito.
Como mapeio números, linearmente, entre a e b para ir entre c e d.
Ou seja, quero números entre 2 e 6 para mapear números entre 10 e 20 ... mas preciso do caso generalizado.
Meu cérebro está frito.
Respostas:
Se o seu número X ficar entre A e B, e você quiser que Y fique entre C e D, você pode aplicar a seguinte transformação linear:
Y = (X-A)/(B-A) * (D-C) + C
Isso deve dar a você o que deseja, embora sua pergunta seja um pouco ambígua, já que você também pode mapear o intervalo na direção inversa. Apenas tome cuidado com a divisão por zero e você estará bem.
Y=f(X)=m*X+b
, onde m e b foram determinados simultaneamente a partir das duas equações de restrição a seguir que resultam da substituição dos valores de X e Y nos pontos finais necessários: C=m*A+b
eD=m*B+b
X=A+(A-B)*t
para provar a igualdade entre essa abordagem e a de Peter. t é essencialmente uma não dimensionalização de X. ( t=(X-A)/(A-B)
)
Divida para obter a proporção entre os tamanhos dos dois intervalos, depois subtraia o valor inicial do intervalo inicial, multiplique pela proporção e some o valor inicial do segundo intervalo. Em outras palavras,
R = (20 - 10) / (6 - 2)
y = (x - 2) * R + 10
Isso distribui uniformemente os números da primeira faixa na segunda faixa.
Seria bom ter essa funcionalidade na java.lang.Math
aula, já que é uma função amplamente necessária e está disponível em outros idiomas. Aqui está uma implementação simples:
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;
}
Estou colocando este código aqui como uma referência para mim mesmo no futuro e pode ser que ajude alguém.
Como um aparte, este é o mesmo problema que o clássico converter celcius em farenheit onde você deseja mapear um intervalo de números que equivale a 0 - 100 (C) a 32 - 212 (F).
Cada intervalo de unidade no primeiro intervalo ocupa (dc) / (ba) "espaço" no segundo intervalo.
Pseudo:
var interval = (d-c)/(b-a)
for n = 0 to (b - a)
print c + n*interval
Como você lida com o arredondamento depende de você.
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);
Com verificações de divisão por zero, é claro.
se o seu intervalo de [a até b] e você deseja mapeá-lo em [c a d], onde x é o valor que você deseja mapear, use esta fórmula (mapeamento linear)
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)