Math - mapping numbers

2019-01-10 02:03发布

问题:

How do I map numbers, linearly, between a and b to go between c and d.

That is, I want numbers between 2 and 6 to map to numbers between 10 and 20... but I need the generalized case.

My brain is fried.

回答1:

If your number X falls between A and B, and you would like Y to fall between C and D, you can apply the following linear transform:

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

That should give you what you want, although your question is a little ambiguous, since you could also map the interval in the reverse direction. Just watch out for division by zero and you should be OK.



回答2:

Divide to get the ratio between the sizes of the two ranges, then subtract the starting value of your inital range, multiply by the ratio and add the starting value of your second range. In other words,

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

This evenly spreads the numbers from the first range in the second range.



回答3:

It would be nice to have this functionality in the java.lang.Math class, as this is such a widely required function and is available in other languages. Here is a simple implementation:

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

I am putting this code here as a reference for future myself and may be it will help someone.



回答4:

As an aside, this is the same problem as the classic convert celcius to farenheit where you want to map a number range that equates 0 - 100 (C) to 32 - 212 (F).



回答5:

Each unit interval on the first range takes up (d-c)/(b-a) "space" on the second range.

Pseudo:

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

How you handle the rounding is up to you.



回答6:

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

With checks on divide by zero, of course.



回答7:

In addition to @PeterAllenWebb answer, if you would like to reverse back the result use the following:

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


回答8:

if your range from [a to b] and you want to map it in [c to d] where x is the value you want to map use this formula (linear mapping)

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