Pituus (etäisyys)

egaga 22.04.02 21:49

Laskee nopeasti pisteiden (x, y) ja (0, 0) välisen etäisyyden

 Tekstiversio  Arvo: 2 (6 ääntä)  Äänestä: +  -
/*
______________________________________________________________________________________________________
 Välimatkan - tai pituuden - laskeminen ilman pythagoraan lausetta pisteestä (0, 0) -> (x, y), kun x>0 ja y>0.
 HUOM! x:n ja y:n sopivuutta ei tarkisteta algoritmissa.
 Likimääräisen algoritmin virheellisyys on - kokemuksen perusteella - korkeintaan 1.2 %
 Alla oleva algoritmi on hyödyllinen esimerkiksi sellaisissa peleissä, jossa tarkkuuden
 ei tarvitse olla äärimmäisen tarkka. Algoritmini on paljon nopeampi kuin sqrt():n käyttö
 pythagoraan menetelmässä, varsinkin, kun se on käännetty optimointiasetuksien kanssa.
 Itse asiassa algoritmini on melkein yhtä nopea kuin Andre LaMothen Inside Peliohjelmointi
 -kirjan lähdekoodeissa oleva bittioperaatioita käyttävä versio ja omani on paljon LaMothen
 vastaavaa tarkempi.
 
 Menetelmän kuvaus:
 Lasketaan yhteen suurin kateetti ja lisäys, joka tulee seuraavasti:
 Lähdetään pidemmän kateetin kulmasta hypotenuusaa pitkin ja kuljetaan pidemmän kateetin
 pituuden verran. Sitten vain piirretään siihen kohtaan hypotenuusalle normaali ja katsotaan,
 missä pisteessä normaali leikkaa pidemmän kateetin jatketun janan. Tämän pisteen x-koordinaatti
 (y-koordinaatti = 0) on likimääräisesti hypotenuusan pituus. MUTTA, koska tämä yllä mainittu
 systeemi ei onnistu ilmeisesti ilman trigonometristen funktioiden käänteisfunktioita (arkuksia),
 tein algoritmin hiukan eri tavalla..Huomasin nimittäin, että normaali leikkaa lyhyemmän kateetin
 noin puolivälissä. Laitoinkin uuden normaalin kulkemaan lyhyemmän kateetin puoliväliä alempaa
 (siitä tulee kerroin 0.429), ja leikkauskohta jatketun pidemmän kateetin kanssa on suurinpiirtein
 hypotenuusan pituus eli haluttu etäisyys.
 
 Toivottavasti en hämännyt pitkällä selostuksellani liikaa, sillä algoritmi on oikeasti
 hyvin yksinkertainen mutta tehokas :).
 
 Kääntäminen gcc:llä:
 gcc -o pituus.exe pituus.cc -liostream
 
 Copyright 2002, Henrik Huttunen a.k.a. egaga
______________________________________________________________________________________________________
*/


#include <fstream.h>
#include <math.h>

//Laskee (0, 0) ja (x, y) välimatkan pituuden (long)
// ~1% virhemarginaali!
long Pituus2Dl(const long &x, const long &y){

        long min, max;

        //Laskee suurimman ja pienimmän kahdesta arvosta
        if(x > y){
                max = x;
                min = y;
        }else{
                max = y;
                min = x;
        }

        //kaava: max + kerroin * min * min / max
        long temp = ((min * min) * 429) / (1000 * max);
        return ( temp + max );
}

//Laskee (0, 0) ja (x, y) välimatkan pituuden (float)
// ~1% virhemarginaali! Suurin virhe - joka löydetty - ~1.05% (kerroin = 0.429)
float Pituus2Df(const float &x, const float &y){

        float min, max;

        //Laskee suurimman ja pienimmän kahdesta arvosta
        if(x > y){
                max = x;
                min = y;
        }else{
                max = y;
                min = x;
        }

        float temp = 0.429 * min * min / max;
        return ( temp + max );
}

int main(){

        cout << "Alku.." << endl;
       
        ofstream avaa("tulos.txt");
        if(avaa.fail())
                exit(1);
        avaa << "_________________________________________________________________________________________" << endl;
        avaa << " Välimatkan laskeminen ilman pythagoraan lausetta pisteestä (0, 0) -> (x, y)" << endl;
        avaa << " Likimääräisen algoritmin virheellisyys on - kokemuksen perusteella - korkeintaan 1.2 %" << endl;
        avaa << " Copyright 2002, Henrik Huttunen a.k.a. eGaga" << endl;
        avaa << "_________________________________________________________________________________________" << endl << endl;

        long tx, ty;
        float oikea, apu;

        for(tx=10;tx<=1000;tx+=50){
        for(ty=10;ty<=1000;ty+=50){
                oikea=  sqrt(tx*tx+ty*ty);
                avaa << endl << "____________________________________________________________________________" << endl;
                avaa <<         "············································································" << endl;
                avaa << "  (X, Y) = (" << tx << ", " << ty << ")" << endl;
                avaa << "____________________________________________________________________________" << endl;
                avaa << "············································································" << endl << endl;
                avaa << oikea << " <--- OIKEA tulos" << endl;
                apu=Pituus2Df(tx, ty); //float
                avaa << apu << " <--- likimääräinen float tulos, virheprosentti: " << 100 * fabs((apu - oikea)) / oikea << "%" << endl;
                apu=(float)Pituus2Dl(tx, ty); //long
                avaa << apu << " <--- likimääräinen long tulos, virheprosentti: " << 100 * abs((apu - oikea)) / oikea << "%" << endl;
        }
        }
 
        avaa.close();
       
        cout << "Loppu.." << endl;

        return 0;
}

Sharlin 19:58 18.4.03 
Hmm, kiintoisaa. Voisikohan tuota yleistää kolmanteen ulottuvuuteen helpostikin?
egaga 08:35 23.4.03 
Enpäs ole tullut ajatelleeksi sitä. Pitääpäs pohtia, jos kesällä vaikka huvittaisi.
Niin ja tuossa koodissahan puuttuu tarkistus jos y = 0. En vain viitsinyt laittaa sitä hidastamaan :).
egaga 08:39 23.4.03 
Niin, ja tietenkin x>0 ja y>0.
Apina 23:51 14.10.03 
"..Tämän pisteen x-koordinaatti
(y-koordinaatti = 0) on likimääräisesti hypot.."?
Sehän on itse asiassa tasan hypotenuusan pituus.
Kolmanteen ulottuvuuteen tuon saisi yleistettyä esim. näin:
float Pituus3Df(float a,float b,float c)
{return(Pituus2Df(a,Pituus2Df(b,c)));}

Tuossa hieman viritelty versio:
float p2d(float a,float b) {
if(a<0)a=-a; if(b<0)b=-b; // a,b<0 ?
if(a<b){float c = a; a = b; b = c;}
if(a==0) // Tai esim. a<epsilon
return(0);
return(0.429*b*b/a+a);
}
kamistar 17:03 6.12.04 
joku voisi sanoa laskee (x, y) ja origon välisen etäisyyden
moshe 17:55 28.2.06 
jännä : ) en kyllä oikein tajua miten toi toimii...
moshe 17:23 2.3.06 
nyt mä tajusin : )

(c = hypotenuusa, a = pitempi kateetti, b = lyhyempi kateetti)

c = a + (0.429b² / a)
c² = a² + 0.858b² + (0.184b^4 / a²)

joka muistuttaa aikasen paljon pytagoraan lausetta (toi merkki on sitten potenssiin kaksi jos se ei näy teillä)