Математика - отображение чисел

99

Как мне сопоставить числа, линейно, между a и b, чтобы перейти между c и d.

То есть я хочу, чтобы числа от 2 до 6 соответствовали числам от 10 до 20 ... но мне нужен обобщенный случай.

Мой мозг сгорел.

Сэм
источник
4
Двухточечная форма. en.wikipedia.org/wiki/Linear_equation#Two-point_form
kennytm 06

Ответы:

211

Если ваше число X находится между A и B, а вы хотите, чтобы Y находился между C и D, вы можете применить следующее линейное преобразование:

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

Это должно дать вам то, что вы хотите, хотя ваш вопрос немного двусмысленный, поскольку вы также можете отобразить интервал в обратном направлении. Просто следите за делением на ноль, и все будет в порядке.

ПитерАлленВебб
источник
16
Для наглядности мне нравится new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter 02
1
Есть ли где-нибудь вывод для этого уравнения?
shaveenk
@shaveenk, это должно быть уравнение линии, Y=f(X)=m*X+bгде m и b были определены одновременно из следующих двух уравнений ограничений, которые являются результатом подстановки значений X и Y в требуемых конечных точках: C=m*A+bиD=m*B+b
Крис
Мне также пришлось использовать, X=A+(A-B)*tчтобы доказать равенство между этим подходом и подходом Питера. t по сути является обезразмериванием X. ( t=(X-A)/(A-B))
Крис
Чтобы изменить направление, формула ((XA) / (AB) * (CD)) * -1 + D
Кори Левинсон
21

Разделите, чтобы получить соотношение между размерами двух диапазонов, затем вычтите начальное значение вашего начального диапазона, умножьте на соотношение и добавьте начальное значение вашего второго диапазона. Другими словами,

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

Это равномерно распределяет числа из первого диапазона во втором диапазоне.

Конрад Рудольф
источник
Это не работает. Мой диапазон - от 1000000000 до 9999999999, а числа могут быть от 1 до 999999999.
Dejell
@Odelya Конечно работает. Это достаточно простое математическое преобразование. Вам просто нужно использовать достаточно большой тип числа (bignum или аналогичный). Ваши числа просто слишком велики для 32-битных целых чисел, но, например, 64-битные целые числа будут работать.
Конрад Рудольф
Они двухместные. двойной R = (20-10) / (6-2); двойной y = (X - 2) * R + 10;
Dejell
@Odelya Та же проблема. Вы должны прочитать о точности с плавающей запятой. Фактически, это необходимо для чтения: Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой - если вам нужен тип с плавающей запятой с такими большими числами, вам, возможно, придется использовать числовой тип произвольной точности .
Конрад Рудольф
Можете ли вы порекомендовать тип java, который я могу сделать?
Dejell
7

Было бы неплохо иметь эту функциональность в java.lang.Mathклассе, так как это широко востребованная функция, доступная на других языках. Вот простая реализация:

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

Я сам помещаю этот код здесь в качестве ориентира на будущее, и, возможно, он кому-то поможет.

Сураб Бхат
источник
4

В стороне, это та же проблема, что и при классическом преобразовании целциуса в фаренгейт, когда вы хотите сопоставить диапазон чисел, который от 0 до 100 (C) до 32-212 (F).

метро
источник
Как это ответ?
shinzou
Это пример применения вопроса. Многие сталкиваются с этой простой проблемой во вводных классах CS и не считают, что решение может быть обобщено на другие проблемы. Я пытался добавить контекст к исходному вопросу. На исходный вопрос уже был дан адекватный ответ.
Metro
1

Каждый единичный интервал в первом диапазоне занимает (dc) / (ba) «пространство» во втором диапазоне.

Псевдо:

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

Как вы будете обрабатывать округление, зависит от вас.

Крис Кадмор
источник
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);

Конечно, с проверкой деления на ноль.

PhiLho
источник
1

если ваш диапазон от [a до b] и вы хотите отобразить его в [c to d], где x - значение, которое вы хотите сопоставить, используйте эту формулу (линейное сопоставление)

double R = (d-c)/(b-a)
double y = c+(x*R)+R
return(y)
Мохамед Ашраф
источник
0

В дополнение к ответу @PeterAllenWebb, если вы хотите отменить результат, используйте следующее:

reverseX = (B-A)*(Y-C)/(D-C) + A
Dejell
источник