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


98

Як мені зіставити числа, лінійно, між a і b, щоб перейти між c і d.

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

Мій мозок смажений.


Відповіді:


210

Якщо ваше число X потрапляє між A і B, і ви хотіли б, щоб Y падало між C і D, ви можете застосувати наступне лінійне перетворення:

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

Це має дати вам те, що ви хочете, хоча ваше питання трохи неоднозначне, оскільки ви також можете відобразити інтервал у зворотному напрямку. Просто стежте за діленням на нуль, і у вас все буде в порядку.


47
Тоді, можливо, позначте цю відповідь як "прийняту", натиснувши позначку біля неї.
Конрад Рудольф

16
Для наочності мені подобається new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter

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щоб довести рівність між цим підходом і підходом Петра. це, по суті, недименсіоналізація X. ( t=(X-A)/(A-B))
Кріс Кіассон,

21

Розділіть, щоб отримати співвідношення між розмірами двох діапазонів, потім відніміть початкове значення вашого початкового діапазону, помножте на відношення і додайте початкове значення вашого другого діапазону. Іншими словами,

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

Це рівномірно розподіляє числа з першого діапазону у другий діапазон.


Це не працює. Мій діапазон становить від 1000000000 до 9999999999, а цифри можуть бути від 1 до 999999999.
Деджелл

@ Оделя Звичайно це працює. Це досить проста математична трансформація. Вам просто потрібно використовувати досить великий тип числа (bignum або подібний). Ваші числа занадто великі для 32-бітових цілих чисел, але, наприклад, 64-бітні цілі числа будуть працювати.
Конрад Рудольф

Вони типу подвійні. подвійний R = (20 - 10) / (6 - 2); подвійне y = (X - 2) * R + 10;
Dejell

@Odelya Та сама проблема. Вам слід прочитати про точність з плаваючою точкою. Насправді для цього потрібно прочитати: Що слід знати кожному вченому-комп’ютерщику про арифметику з плаваючою крапкою - якщо вам потрібен тип з плаваючою точкою з такими великими числами, можливо, вам доведеться використовувати тип числа з довільною точністю .
Конрад Рудольф

Чи можете ви порекомендувати тип Java, що я можу це зробити?
Деджелл

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 та не вважають, що рішення можна узагальнити до інших задач. Я намагався додати контекст до вихідного питання. На вихідне запитання вже було адекватно відповідено.
Метро

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

Звісно, ​​з чеками на ділення на нуль.


1

якщо ваш діапазон від [a до b], і ви хочете зіставити його в [c до 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.