Mathe - Mapping Zahlen

Antworten:

210

Wenn Ihre Zahl X zwischen A und B liegt und Sie möchten, dass Y zwischen C und D liegt, können Sie die folgende lineare Transformation anwenden:

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

Das sollte Ihnen das geben, was Sie wollen, obwohl Ihre Frage etwas mehrdeutig ist, da Sie das Intervall auch in umgekehrter Richtung abbilden könnten. Achten Sie einfach auf die Division durch Null und Sie sollten in Ordnung sein.

PeterAllenWebb
quelle
47
Markieren Sie diese Antwort dann möglicherweise als "akzeptiert", indem Sie auf das Häkchen daneben klicken.
Konrad Rudolph
16
Aus Gründen der Klarheit mag ich new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter
1
Gibt es irgendwo eine Ableitung für diese Gleichung?
Shaveenk
@shaveenk sollte es die Gleichung einer Linie sein, mit der Y=f(X)=m*X+bm und b gleichzeitig aus den folgenden zwei Bedingungsgleichungen bestimmt wurden, die sich aus dem Ersetzen der Werte von X und Y an den erforderlichen Endpunkten ergeben: C=m*A+bundD=m*B+b
Chris Chiasson
Am Ende musste ich auch X=A+(A-B)*tdie Gleichheit zwischen diesem Ansatz und dem von Peter beweisen. t ist im Wesentlichen eine Nichtdimensionierung von X. ( t=(X-A)/(A-B))
Chris Chiasson
21

Teilen Sie, um das Verhältnis zwischen den Größen der beiden Bereiche zu erhalten, subtrahieren Sie dann den Startwert Ihres Anfangsbereichs, multiplizieren Sie ihn mit dem Verhältnis und addieren Sie den Startwert Ihres zweiten Bereichs. Mit anderen Worten,

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

Dadurch werden die Zahlen aus dem ersten Bereich im zweiten Bereich gleichmäßig verteilt.

Konrad Rudolph
quelle
Das funktioniert nicht. Mein Bereich ist 1000000000 bis 9999999999 und die Zahlen könnten von 1 bis 999999999 sein.
Dejell
@ Odelya Natürlich funktioniert es. Es ist eine recht einfache mathematische Transformation. Sie müssen nur einen ausreichend großen Zahlentyp (Bignum oder ähnliches) verwenden. Ihre Zahlen sind einfach zu groß für 32-Bit-Ganzzahlen - aber 64-Bit-Ganzzahlen funktionieren beispielsweise.
Konrad Rudolph
Sie sind vom Typ doppelt. doppeltes R = (20 - 10) / (6 - 2); doppeltes y = (X - 2) * R + 10;
Dejell
@ Odelya Gleiches Problem. Sie sollten sich über die Gleitkommapräzision informieren. Tatsächlich muss Folgendes gelesen werden: Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte - Wenn Sie einen Gleitkomma-Typ mit so großen Zahlen benötigen, müssen Sie möglicherweise einen Zahlentyp mit beliebiger Genauigkeit verwenden .
Konrad Rudolph
Kannst du einen Java-Typ empfehlen, den ich machen kann?
Dejell
7

Es wäre schön, diese Funktionalität in der java.lang.MathKlasse zu haben, da dies eine so häufig benötigte Funktion ist und in anderen Sprachen verfügbar ist. Hier ist eine einfache Implementierung:

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

Ich setze diesen Code hier als Referenz für die Zukunft selbst ein und kann jemandem helfen.

Sourabh Bhat
quelle
4

Abgesehen davon ist dies das gleiche Problem wie beim klassischen Konvertieren von Celcius in Farenheit, bei dem Sie einen Zahlenbereich abbilden möchten, der 0 - 100 (C) bis 32 - 212 (F) entspricht.

Metro
quelle
Wie ist das eine Antwort?
Shinzou
Es ist ein Beispiel für die Anwendung der Frage. Viele haben dieses einfache Problem in einführenden CS-Klassen und sind nicht der Ansicht, dass die Lösung auf andere Probleme verallgemeinert werden kann. Ich habe versucht, der ursprünglichen Frage einen Kontext hinzuzufügen. Die ursprüngliche Frage war bereits angemessen beantwortet worden.
Metro
1

Jedes Einheitsintervall im ersten Bereich nimmt (dc) / (ba) "Raum" im zweiten Bereich ein.

Pseudo:

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

Wie Sie mit der Rundung umgehen, liegt bei Ihnen.

Chris Cudmore
quelle
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);

Natürlich mit Überprüfungen der Division durch Null.

PhiLho
quelle
1

Wenn Ihr Bereich von [a bis b] reicht und Sie ihn in [c bis d] abbilden möchten, wobei x der Wert ist, den Sie abbilden möchten, verwenden Sie diese Formel (lineare Zuordnung).

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

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)
Amerrnath
quelle
0

Wenn Sie das Ergebnis rückgängig machen möchten, verwenden Sie zusätzlich zur Antwort @PeterAllenWebb Folgendes:

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