Математика — сопоставление чисел

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

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

Мой мозг жарится.


person Sam    schedule 05.12.2008    source источник
comment
Для ясности мне нравится новое_значение = (старое_значение - старое_нижнее) / (старое_верхнее - старое_нижнее) * (новое_верхнее - новое_нижнее) + новое_нижнее;   -  person kennytm    schedule 06.03.2010


Ответы (10)


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

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

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

person PeterAllenWebb    schedule 05.12.2008
comment
Есть ли где-нибудь вывод этого уравнения? - person ftrotter; 02.06.2014
comment
@shaveenk это должно быть уравнение линии с _1_, где m и b были определены одновременно из следующих двух уравнений ограничений, которые являются результатом подстановки значений X и Y в требуемых конечных точках: _2_ и _3_ - person shaveenk; 29.10.2016
comment
Мне также пришлось использовать Y=f(X)=m*X+b, чтобы доказать равенство между этим подходом и подходом Питера. t по сути является обезразмериванием X. (C=m*A+b) - person Chris Chiasson; 09.03.2017
comment
Чтобы изменить направление, используется формула ((X-A)/(A-B) * (C-D)) * -1 + D - person Chris Chiasson; 09.03.2017
comment
Это не работает. Мой диапазон от 1000000000 до 9999999999, а числа могут быть от 1 до 999999999. - person Corey Levinson; 02.12.2018

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

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

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

person Konrad Rudolph    schedule 05.12.2008
comment
@Odelya Конечно, это работает. Это достаточно простое математическое преобразование. Вам просто нужно использовать достаточно большой числовой тип (bignum или аналогичный). Ваши числа просто слишком велики для 32-битных целых чисел, но, например, 64-битные целые числа будут работать. - person Dejell; 19.02.2013
comment
Они типа двойные. двойной Р = (20 - 10) / (6 - 2); двойной у = (Х - 2) * R + 10; - person Konrad Rudolph; 19.02.2013
comment
@Odelya Та же проблема. Вы должны прочитать о точности с плавающей запятой. На самом деле это обязательно к прочтению: Что должен знать каждый программист Об арифметике с плавающей запятой — если вам нужен тип с плавающей запятой с такими большими числами, возможно, вам придется использовать число произвольной точности. - person Dejell; 19.02.2013
comment
Можете ли вы порекомендовать тип java, который я могу сделать? - person Konrad Rudolph; 19.02.2013
comment
@Odelya В статье, на которую я ссылаюсь, упоминается соответствующий класс Java< /а> … - person Dejell; 20.02.2013
comment
Как это ответ? - person Konrad Rudolph; 20.02.2013

Было бы неплохо иметь эту функциональность в классе 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;
}

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

person Sourabh Bhat    schedule 11.12.2016

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

person Metro    schedule 05.12.2008
comment
Это пример применения вопроса. Многие имеют эту простую задачу на вводных занятиях по информатике и не считают, что решение можно обобщить на другие задачи. Я пытался добавить контекст к исходному вопросу. На исходный вопрос уже был дан адекватный ответ. - person shinzou; 26.10.2017
comment
Где X — это число, которое нужно сопоставить от AB до CD. , а Y — это результат: возьмите формулу линейной интерполяции, lerp(a,b,m)= a+(m*(b-a)) и поставить C и D вместо a и b, чтобы получить Y=C+( m*(D-C)). Затем вместо m поставьте (X-A)/(B-A< /strong>), чтобы получить Y=C+(((X-A)/( B-A))*(D-C)). Это неплохая функция карты, но ее можно упростить. Возьмите кусок (D-C) и поместите его внутрь делимого, чтобы получить Y=C+( ((X-A)*(D-C))/(B >-А)). Это дает нам еще одну часть, которую мы можем упростить, (X-A)*(D-C), которая равно (X*D)-(X*C)-(A >*D)+(A*C). Вставьте это, и вы получите Y=C+(((X*D)-( >X*C)-(A*D)+(A* C))/(B-A)). Следующее, что вам нужно сделать, это добавить бит +C. Для этого вы умножаете C на (B-A), чтобы получить ((B* C)-(A*C)), и переместите его в делимое, чтобы получить Y=(((X*D)-(X*C)-(A* D)+(A*C)+(B*C)-( >A*C))/(B-A)). Это избыточно, содержит как +(A*C), так и -(A*C), которые компенсируют друг друга. Удалите их, и вы получите окончательный результат: Y=((X*D)-(X *C)-(A*D)+(B*C) )/(В-А) - person Metro; 07.12.2017

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)
person Amerrnath    schedule 17.03.2020

Каждый единичный интервал в первом диапазоне занимает (d-c)/(b-a) «пространство» во втором диапазоне.

Псевдо:

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

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

person Chris Cudmore    schedule 05.12.2008

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

С проверками на деление на ноль, разумеется.

person PhiLho    schedule 05.12.2008

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

reverseX = (B-A)*(Y-C)/(D-C) + A
person Dejell    schedule 19.02.2013

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

double R = (d-c)/(b-a)
double y = c+(x*R)+R
return(y)
person Mohamed Ashraf    schedule 09.01.2019

TL;DR: стандартная функция карты, Y=C+(((X-A)/( B-A))*(D-C)), можно упростить до Y =((X*D)-(X*C)-( A*D)+(B*C))/(B- А)

Двухточечная форма. en.wikipedia.org/wiki/Linear_equation#Two-point_form

person JMoore2007    schedule 13.10.2020