Matemática - mapeando números


Respostas:


210

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.


47
Em seguida, marque essa resposta como “aceita” clicando na marca de seleção ao lado dela.
Konrad Rudolph

16
Para maior clareza, eu gosto new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter

1
Existe uma derivação para esta equação em algum lugar?
shaveenk

@shaveenk deve ser a equação de uma linha, com 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+beD=m*B+b
Chris Chiasson

Também acabei precisando usar X=A+(A-B)*tpara provar a igualdade entre essa abordagem e a de Peter. t é essencialmente uma não dimensionalização de X. ( t=(X-A)/(A-B))
Chris Chiasson,

21

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.


Isso não funciona. Meu intervalo é 1000000000 a 9999999999 e os números podem ser de 1 a 999999999.
Dejell

@Odelya Claro que funciona. É uma transformação matemática bastante simples. Você só precisa usar um tipo de número grande o suficiente (bignum ou semelhante). Seus números são simplesmente muito grandes para inteiros de 32 bits - mas inteiros de 64 bits, por exemplo, funcionarão.
Konrad Rudolph

Eles são do tipo duplo. duplo R = (20 - 10) / (6 - 2); duplo y = (X - 2) * R + 10;
Dejell

@Odelya Mesmo problema. Você deve ler sobre precisão de ponto flutuante. Na verdade, essa é uma leitura obrigatória: O que todo cientista da computação deve saber sobre aritmética de ponto flutuante - se você precisa de um tipo de ponto flutuante com números tão grandes, talvez precise usar um tipo de número de precisão arbitrária .
Konrad Rudolph

Você pode me recomendar um tipo de java que eu possa fazer?
Dejell de

7

Seria bom ter essa funcionalidade na java.lang.Mathaula, 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.


4

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


Como isso é uma resposta?
shinzou

É um exemplo de aplicação da pergunta. Muitos têm esse problema simples em aulas introdutórias de CS e não consideram que a solução possa ser generalizada para outros problemas. Eu estava tentando adicionar contexto à pergunta original. A pergunta original já havia sido respondida adequadamente.
Metrô

1

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


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

Com verificações de divisão por zero, é claro.


1

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)


0

Além da resposta de @PeterAllenWebb, se desejar reverter o resultado, use o seguinte:

reverseX = (B-A)*(Y-C)/(D-C) + A
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.