Toán - ánh xạ số


Câu trả lời:


210

Nếu số X của bạn nằm giữa A và B, và bạn muốn Y nằm giữa C và D, bạn có thể áp dụng phép biến đổi tuyến tính sau:

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

Điều đó sẽ cung cấp cho bạn những gì bạn muốn, mặc dù câu hỏi của bạn hơi mơ hồ, vì bạn cũng có thể lập bản đồ khoảng thời gian theo hướng ngược lại. Chỉ cần chú ý đến phép chia cho số 0 và bạn sẽ ổn.


47
Sau đó, có thể đánh dấu câu trả lời này là “được chấp nhận” bằng cách nhấp vào dấu tích bên cạnh nó.
Konrad Rudolph

16
Để rõ ràng, tôi thích new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter

1
Có một dẫn xuất cho phương trình này ở đâu đó?
shaveenk

@shaveenk nó nên là phương trình của một dòng, với Y=f(X)=m*X+b, trong đó m và b đã được xác định cùng một lúc từ hai phương trình ràng buộc sau đó kết quả từ thay thế các giá trị của X và Y tại điểm cuối yêu cầu: C=m*A+bD=m*B+b
Chris Chiasson

Cuối cùng tôi cũng cần phải sử dụng X=A+(A-B)*tđể chứng minh sự bình đẳng giữa cách tiếp cận này và của Peter. t bản chất là một nondimensionalization của X. ( t=(X-A)/(A-B))
Chris Chiasson

21

Chia để có tỷ lệ giữa các kích thước của hai phạm vi, sau đó trừ giá trị bắt đầu của phạm vi đầu tiên của bạn, nhân với tỷ lệ và cộng giá trị bắt đầu của phạm vi thứ hai. Nói cách khác,

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

Điều này trải đều các số từ phạm vi đầu tiên trong phạm vi thứ hai.


Điều này không hoạt động. Phạm vi của tôi là 1000000000 đến 9999999999 và các con số có thể từ 1 đến 999999999.
Dejell

@Odelya Tất nhiên là nó hoạt động. Đó là một phép biến đổi toán học đủ đơn giản. Bạn chỉ cần sử dụng loại số đủ lớn (bignum hoặc tương tự). Các số của bạn chỉ đơn giản là quá lớn đối với số nguyên 32 bit - nhưng ví dụ như số nguyên 64 bit sẽ hoạt động.
Konrad Rudolph

Chúng thuộc loại đôi. đôi R = (20 - 10) / (6 - 2); nhân đôi y = (X - 2) * R + 10;
Dejell

@Odelya Tuy nhiên, cùng một vấn đề. Bạn nên đọc về độ chính xác của dấu phẩy động. Trên thực tế, đây là bài đọc bắt buộc: Những gì mọi nhà khoa học máy tính nên biết về số học dấu phẩy động - nếu bạn cần kiểu dấu phẩy động với các số lớn như vậy, bạn có thể phải sử dụng kiểu số có độ chính xác tùy ý .
Konrad Rudolph

Bạn có thể giới thiệu loại java mà tôi có thể làm được không?
Dejell

7

Sẽ rất tuyệt nếu có chức năng này trong java.lang.Mathlớp, vì đây là một chức năng được yêu cầu rộng rãi và có sẵn trong các ngôn ngữ khác. Đây là một cách triển khai đơn giản:

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

Tôi đang đặt mã này ở đây như một tài liệu tham khảo cho bản thân trong tương lai và có thể nó sẽ giúp ích cho ai đó.


4

Ngoài ra, đây là vấn đề tương tự như chuyển đổi celcius cổ điển thành giá trị mà bạn muốn ánh xạ một dải số từ 0 - 100 (C) đến 32 - 212 (F).


Đây là một câu trả lời như thế nào?
shinzou

Nó là một ví dụ về ứng dụng của câu hỏi. Nhiều người gặp vấn đề đơn giản này trong các lớp CS mới bắt đầu và không cho rằng giải pháp có thể được tổng quát hóa cho các vấn đề khác. Tôi đang cố gắng thêm ngữ cảnh vào câu hỏi ban đầu. Câu hỏi ban đầu đã được trả lời đầy đủ.
Tàu điện ngầm

1

Mỗi khoảng đơn vị trên phạm vi đầu tiên chiếm (dc) / (ba) "không gian" trên phạm vi thứ hai.

Pseudo:

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

Cách bạn xử lý việc làm tròn là tùy thuộc vào bạn.


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

Tất nhiên với séc chia cho 0.


1

nếu phạm vi của bạn từ [a đến b] và bạn muốn ánh xạ nó trong [c đến d] trong đó x là giá trị bạn muốn ánh xạ, hãy sử dụng công thức này (ánh xạ tuyến tính)

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


0

Ngoài câu trả lời @PeterAllenWebb, nếu bạn muốn đảo ngược kết quả, hãy sử dụng cách sau:

reverseX = (B-A)*(Y-C)/(D-C) + A
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.