[C] iterative Funktion in rekursive umschreiben



  • Hallo Leute,

    bin neu hier. Hab mich angemeldet, weil ich gerade angefangen habe Informatik zu studieren und ich wohl öfter nochd den einen oder anderen Ratschlag brauchen werde... und vielleicht auch irgendwann mal geben kann 😉

    also mein Problem ist folgendes:
    Wir haben die Aufgabe bekommen, eine kleine Funktion in C zu implementieren, die mittels Intervall-Schachtelung die Wurzel einer ganzen positiven Zahl berechnet. Dies soll in 4 Versionen geschehen: eine rekursiv deterministisch, eine rekursiv nichtdeterministisch, eine iterativ deterministisch und eine iterativ nichtdeterministisch.

    Die iterativ deterministische ist kein Problem, allerdings hab ich nich die geringste vorstellung, wie ich die Funktion nichtdeterministisch und vor allem rekursiv darstellenn soll. Mein bisheriger Code sieht so aus:

    double wurzel(int a) {
    	double li = 1.0, re = a, y;
    	while(li<re) {
    		y = (li+re)/2;
    		if(y*y<a) {li=li+0.0001;}
    		if(y*y>a) {re=re-0.0001;}
    	}
    	return y;
    }
    

    Ich würde mich freuen, wenn mir jemand helfen könnte!
    Liebe Grüße
    Randy 🙂



  • Okay ne, der Code von vorhin war viel zu ineffizient.... so ist es besser:

    double wurzel(int a) {
    	double li, re = a, y;
    	while(li<re-0.000001) {
    		y = (li+re)/2;
    		if(y*y<a) {li=y;}
    		if(y*y>a) {re=y;}
    	}
    	return y;
    }
    

    meine frage ist aber immernoch die gleiche 😉

    grüße
    Randy



  • Bei rekursiven Funktionen ist die Schleife im Prinzip die ganze Funktion, d.h. z.b. kannst du deine Schleife momentan so schreiben:

    // Berechne Wurzel von a anhand von li und re
        while(1) {
            if (li<re-0.000001) {
              break;
            }
            y = (li+re)/2;
            if(y*y<a) {
                  // Berechne Wurzel von a anhand von y und re
               li=y;
         //Durchlaufe GENAU den gleichen Code wieder (nur mit anderen Werten für li)
               continue;
           }
            if(y*y>a) {
             // Berechne Wurzel von a anhand von li und y
               re=y;
               //Durchlaufe GENAU den gleichen Code wieder (nur mit anderen Werten für re)
               continue;
           }
        }
    

    Und das machst du jetzt nicht als Schleife, sondern als Funktion. D.h. anstelle die Variablen li und re in der Funktion zu ändern und dann die Funktion "im Prinzip" wieder neuzustarten, rufst du die Funktion gleich richtig neu auf und übergibst ihr die geänderten Parameter li und re. (Schau mal ob du damit zu recht kommst. Rekursion muss man verstehen, sonst wird das nie was. Deshalb sag ich erstmal nicht mehr dazu)

    Und nichtdeterministisch?
    Pack an einer Stelle ne Zufallszahl rein mit der du weiterarbeitest. (Is dann halt lang nicht so effizient)

    Mfg
    DerBaer



  • DerBaer schrieb:

    Rekursion muss man verstehen, sonst wird das nie was. Deshalb sag ich erstmal nicht mehr dazu

    Muss man nicht erst Rekursion verstehen?



  • Schon,danke für die schnellen antworten... also mir is aufgefallen, dass die eh schon nichtdeterministisch ist, weil li nicht initialisiert wird. Komischerweise funktioniert die funktion, wenn man li nicht mit = 1 initialisiert für alle zahlen außer quadratzahlen von 16 und vielfachen (256,512,1024,etc.), warum?! keine ahnung....

    jo, also was rekursion ist hab ich verstaden, is ja jetz auch nich allzu schwer... ich kapier allerdings nicht, wie ich re und li beim nächsten aufruf manipulieren kann...
    der prototyp ist ja int wurzel(int a), da kann man ja beim aufruf nur das a manipulieren, oder geht das auch irgendwo anders?

    danke im voraus

    Grüße Randy



  • RandyK schrieb:

    ich kapier allerdings nicht, wie ich re und li beim nächsten aufruf manipulieren kann...y

    Die beiden einfach als Parameter der rekursiven Funktion einführen. Bei jedem rekursiven Aufruf kannst du dann die beiden neuen Werte als Argumente übergeben.



  • ok, danke jetz hab ichs...

    #include <stdio.h>
    
    double wurzel(double li,double re,int a) {
    	double y;
    	y = (li+re)/2;
    	if(li>=re-0.000001) {return y;}
    	if(y*y<a) {return wurzel(y,re,a);}
    	if(y*y>a) {return wurzel(li,y,a);}
    }
    
    int main() {
    	int a;
    	double b;
    	printf("bitte positive ganze zahl eingeben:\n");
    	scanf("%i",&a);
    	b = wurzel(1,a,a);
    	printf("%.4f",b);
    	return 0;
    }
    

    Also, vielen Dank an alle Beteiligten und bis bald!
    Grüße Randy



  • ok, sorry jetz bin ichs nochmal...
    hätt ich fast vergessen: hat von euch einer ne ahnung, wie ich die jetzt noch nichtdeterministisch machen kann? Wär für jeden ratschlag dankbar.

    ach ja, oben muss an die funktion als re a/2 übergeben werden und nicht a.... sonst funktionierts iwie nich...

    grüße Randy



  • Ich möchte dich ungern in deiner Euphorie bremsen, aber hast du dir mal die Ergebnisse ausgeben lassen und verglichen?
    sqrt_bisection1 ist hier zu finden.

    typedef union
    {
    	double f;
    	struct
    	{
    		unsigned long long    frac:52;   // Mantisse
    		unsigned long long    exp:11;    // Exponent
    		unsigned long long    sign:1;    // Vorzeichen
    	}b;
    } IEEE754_double;
    
    double sqrt_bisection1(double a)
    {
    	IEEE754_double s;
    	s.f = a;
    	s.b.exp = (s.b.exp - 1023) / 2 + 1022;
    	double u = s.f;
    	s.b.exp += 2;
    	double o = s.f;
    	double x=0;
    	int i;
    	for (i = 1; i <= 30; ++i)
    	{
    		x = 0.5 * (u + o);
    		if (x*x > a)
    		{
    			o = x;
    		}
    		else
    		{
    			u = x;
    		}
    	}
    	return x;
    }
    
    double wurzel(double li,double re,int a) {
    	double y;
    	y = (li+re)/2;
    	if(li>=re-0.000001) {return y;}
    	if(y*y<a) {return wurzel(y,re,a);}
    	if(y*y>a) {return wurzel(li,y,a);}
    }
    
    int main()
    {
        int i;
        for(i=1;i<=100;++i)
    		printf("%f\t%f\t%f\t%f\n",i/5.0,wurzel(1,i/5.0,i/5.0),sqrt_bisection1(i/5.0),sqrt(i/5.0));
        return 0;
    }
    

    Ausgabe (eingegrenzt)

    Zahl        wurzel      bisection1  sqrt
    --------------------------------------------
    4.000000	2.000000	2.000000	2.000000
    4.200000	2.000000	2.049390	2.049390
    4.400000	2.000000	2.097618	2.097618
    4.600000	2.000000	2.144761	2.144761
    4.800000	2.000000	2.190890	2.190890
    5.000000	2.236068	2.236068	2.236068
    5.200000	2.236068	2.280351	2.280351
    5.400000	2.236068	2.323790	2.323790
    5.600000	2.236068	2.366432	2.366432
    5.800000	2.236068	2.408319	2.408319
    6.000000	2.449490	2.449490	2.449490
    6.200000	2.449489	2.489980	2.489980
    6.400000	2.449489	2.529822	2.529822
    6.600000	2.449489	2.569047	2.569047
    6.800000	2.449490	2.607681	2.607681
    7.000000	2.645752	2.645751	2.645751
    7.200000	2.645751	2.683282	2.683282
    7.400000	2.645752	2.720294	2.720294
    7.600000	2.645751	2.756810	2.756810
    7.800000	2.645751	2.792848	2.792848
    8.000000	2.828427	2.828427	2.828427
    8.200000	2.828428	2.863564	2.863564
    8.400000	2.828427	2.898275	2.898275
    8.600000	2.828427	2.932576	2.932576
    8.800000	2.828427	2.966479	2.966479
    9.000000	nan	3.000000	3.000000
    


  • Ich hab vielleicht vergessen zu erwähnen, dass die ausgabe auf 4 nachkommastellen genau und die eingabe nur ganze zahlen sein sollen... aber trotzdem danke 😃

    EDIT: Achso und wegen der nan meldung bei 9... deshalb hab ich ja gemeint, als re muss a/2 übergeben werden und nicht a...



  • RandyK schrieb:

    Ich hab vielleicht vergessen zu erwähnen, dass die ausgabe auf 4 nachkommastellen genau und die eingabe nur ganze zahlen sein sollen... aber trotzdem danke 😃

    Wenn's für ganze double's geht, wird's auch für nichtganze double's gehen.

    RandyK schrieb:

    Achso und wegen der nan meldung bei 9... deshalb hab ich ja gemeint, als re muss a/2 übergeben werden und nicht a...

    Deine Schuld. Ich hätte einen Wrapper erwartet, etwa so:

    inline double wrapper(double x)
    {
        return wurzel(1, x / 2, x);
    }
    

Anmelden zum Antworten