Matematica: mappatura dei numeri


Risposte:


210

Se il tuo numero X è compreso tra A e B e desideri che Y sia compreso tra C e D, puoi applicare la seguente trasformazione lineare:

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

Questo dovrebbe darti quello che vuoi, anche se la tua domanda è un po 'ambigua, poiché potresti anche mappare l'intervallo nella direzione opposta. Fai attenzione alla divisione per zero e dovresti stare bene.


47
Quindi forse contrassegnare questa risposta come "accettata" facendo clic sul segno di spunta accanto ad essa.
Konrad Rudolph

16
Per chiarezza, mi piace new_value = (old_value - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
ftrotter

1
C'è una derivazione per questa equazione da qualche parte?
shaveenk

@shaveenk dovrebbe essere l'equazione di una retta, con Y=f(X)=m*X+b, dove m e b sono stati determinati simultaneamente dalle seguenti due equazioni dei vincoli che risultano dalla sostituzione dei valori di X e Y agli estremi richiesti: C=m*A+beD=m*B+b
Chris Chiasson

Ho anche finito per aver bisogno di utilizzare X=A+(A-B)*tper dimostrare l'uguaglianza tra questo approccio e quello di Peter. È essenzialmente una non dimensionalizzazione di X. ( t=(X-A)/(A-B))
Chris Chiasson

21

Dividi per ottenere il rapporto tra le dimensioni dei due intervalli, quindi sottrai il valore iniziale dell'intervallo iniziale, moltiplicalo per il rapporto e aggiungi il valore iniziale del secondo intervallo. In altre parole,

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

Questo distribuisce uniformemente i numeri dal primo intervallo nel secondo intervallo.


Questo non funziona. Il mio intervallo è da 1000000000 a 9999999999 e i numeri potrebbero essere da 1 a
999999999.

@ Odelya Certo che funziona. È una trasformazione matematica abbastanza semplice. Devi solo usare un tipo di numero abbastanza grande (bignum o simile). I tuoi numeri sono semplicemente troppo grandi per gli interi a 32 bit, ma ad esempio gli interi a 64 bit funzioneranno.
Konrad Rudolph

Sono di tipo doppio. doppia R = (20 - 10) / (6 - 2); doppio y = (X - 2) * R + 10;
Dejell

@ Odelya Lo stesso problema però. Dovresti leggere sulla precisione in virgola mobile. In effetti, è necessario leggere: Quello che ogni scienziato informatico dovrebbe sapere sull'aritmetica in virgola mobile : se hai bisogno di un tipo a virgola mobile con numeri così grandi potresti dover usare un tipo di numero a precisione arbitraria .
Konrad Rudolph

Mi può consigliare un tipo Java che posso farlo?
Dejell

7

Sarebbe bello avere questa funzionalità nella java.lang.Mathclasse, poiché è una funzione così ampiamente richiesta ed è disponibile in altre lingue. Ecco una semplice implementazione:

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

Metto questo codice qui come riferimento per il futuro me stesso e potrebbe essere che aiuterà qualcuno.


4

Per inciso, questo è lo stesso problema del classico converti celcius in farenheit in cui vuoi mappare un intervallo di numeri che equivale a 0-100 (C) a 32-212 (F).


Come è questa una risposta?
shinzou

È un esempio dell'applicazione della domanda. Molti hanno questo semplice problema nelle classi CS introduttive e non ritengono che la soluzione possa essere generalizzata ad altri problemi. Stavo cercando di aggiungere contesto alla domanda originale. La domanda originale aveva già ricevuto una risposta adeguata.
Metro

1

Ogni intervallo unitario sul primo intervallo occupa (dc) / (ba) "spazio" sul secondo intervallo.

Pseudo:

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

Come gestire l'arrotondamento dipende da te.


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

Con controlli su dividi per zero, ovviamente.


1

se il tuo intervallo da [a a b] e vuoi mapparlo in [c a d] dove x è il valore che vuoi mappare usa questa formula (mappatura lineare)

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


0

Oltre alla risposta di @PeterAllenWebb, se desideri invertire il risultato usa quanto segue:

reverseX = (B-A)*(Y-C)/(D-C) + A
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.