Programmeren 'Nulpuntsbepaling'

Het forum voor overige vragen betreffende wiskunde uit het hoger onderwijs.
Plaats reactie
BusinessMath
Vast lid
Vast lid
Berichten: 57
Lid geworden op: 23 feb 2014, 15:14

Programmeren 'Nulpuntsbepaling'

Bericht door BusinessMath » 12 mar 2014, 17:04

Hallo

ik heb weer een vraag over programmeren, ditmaal is het onderwerp: 'nulpuntsbepaling'
Hier de opdracht
Programma-opzet
Schrijf een programma dat een nulpunt van een functie benadert.

De gebruiker geeft de functie op door er één te kiezen uit een lijstje dat het programma geeft, en gebruiker geeft twee getallen a en b die voldoen aan de volgende voorwaarden:
• a<b
• de functie moet continu zijn op het interval [a,b]?? Hoe kun je dit invullen?
• ( f(a)<0 en f(b)>0) of (f(a)>0 en f(b)<0), kort gezegd: f(a) en f(b) zijn verschillend van teken.

De gebruiker kiest ook een maximale afwijking ε, in principe een getal dicht bij 0; ε spreek je uit als ‘epsilon’, de Griekse letter e, waarmee wordt genoteerd welke fout je ergens in maakt.
Het programma benadert het nulpunt x0, of één van de nulpunten als er meerdere tussen a en b liggen. De benadering is zodanig nauwkeurig dat . Het getal ε geeft aan hoeveel f(x0) naar boven of naar beneden van 0 mag afwijken. De gebruiker moet een ε >0 opgeven, want anders kan de computer geen x0 vinden die voldoet aan .

Als niet aan alle voorwaarden is voldaan, dan geeft de computer een melding aan de gebruiker.
Als wel aan de voorwaarden is voldaan, ligt er een nulpunt tussen a en b, en gaat de computer een nulpunt ertussen zoeken met het volgende algoritme.


Het bisectie-algoritme

Er zijn vele algoritmes voor een nulpuntsbenadering. Eén daarvan is de bisectie-methode, ofwel halveringsmethode. Het is de eenvoudigste methode voor nulpuntbenadering, al is het niet de meest efficiënte (dat betekent: andere methoden kunnen dezelfde nauwkeurigheid in minder rekentijd bereiken). De halveringsmethode bestaat uit stappen, waarbij in elke stap het interval [a,b] waarin het gezochte nulpunt kan liggen gehalveerd wordt. Dat gaat als volgt:
Het gemiddelde van a en b noemen we m, dus . Als , dan hebben we een goede benadering (namelijk m) van het nulpunt gevonden. Zo niet, dan kijk je of het nulpunt tussen a en m ligt, of tussen m en b. Daartoe pas je één van de intervalgrenzen a en b aan, zodat je verder gaat zoeken tussen a en m, of tussen m en b, afhankelijk van waar het nulpunt moet liggen. Je hebt het zoekinterval nu gehalveerd (vandaar de naam ‘halveringsmethode’). Herhaal de halvering van het interval totdat (ofwel zo lang als ). Na afloop is m de benadering van het nulpunt.

Voorbeeld.
Gegeven is de functie . De gebruiker geeft op: a=1 en b=2.
Dat voldoet aan de voorwaarden: f is een continue functie, f(1) = -1 en f(2) = 2, en zijn dus verschillend van teken. Er ligt dus een nulpunt in het interval [1,2]; je kunt eenvoudig narekenen dat dit nulpunt is, want , maar we kijken nu hoe je dit nulpunt met het bisectie-algoritme zou benaderen.

Stap 1 De computer berekent , en vervolgens f(1,5) = 0,25.
Omdat f(1) en f(1,5) een verschillend teken hebben, ligt er een nulpunt tussen 1 en 1,5.
Het nieuwe zoekinterval wordt [1; 1,5].
Stap 2 De computer berekent en f(1,25) = -0,4375.
Omdat f(1,25) en f(1,5) een verschillend teken hebben, ligt er een nulpunt tussen 1,25 en 1,5, dus het nieuwe zoekinterval wordt [1,25; 1,5].

Enzovoort, totdat de waarde van f(m) klein genoeg is. Zie onderstaande voorbeelduitvoer.
zo komt het eruit te zien als je waarden hebt ingevuld (in mijn uitwerking zijn een aantal formules anders.):
Afbeelding

en dit is een voorbeeld hoe je het programma kan gaan uitwerken
Eerste fase
Gebruik in deze fase steeds dezelfde functie f, bijvoorbeeld uit het voorbeeld; zet de hele berekening in de event-handler Button1Click.

Declareer f als functie in je programma, dus met als kopregel:

function f (x: real): real;
begin { hier komt de berekening van f}
end;

Bij de berekeningen in het bisectie-algoritme roep je dus de functie f aan met f(a), f(m), etc.

Tip: het bisectie-algoritme bevat een herhaling, waarin tenminste één stap moet worden uitgevoerd, want je moet minstens één keer m en f(m) berekenen, voordat de conclusie kan zijn dat het nulpunt gevonden is. Bij een dergelijk soort herhaling past heel goed het ‘repeat…until’ statement.

Neem een Memoveld op waarin je de tussenstappen laat zetten, zoals in het voorbeeld.
Je krijgt mooie uitvoer als je aan het begin de kopregel in het Memo laat zetten met:
Memo1.Clear;
Memo1.Lines.Add (' a ' +
' m ' +
' b ')

De waarden van a, b, etc, krijg je mooi op het scherm met:
Memo1.Lines.Add ( Format('%10.6f',[a]) + ' '
+ Format('%10.6f',[m]) + ' '
+ Format('%10.6f',));
Zelf even aanvullen met de functiewaarden.
Als je programma niet goed werkt, dan kun je met de uitvoer van deze waarden nagaan wat er mis gaat.


Tweede fase
Als het bisectie-algoritme goed werkt, dan breid je het programma uit en verander je het, zodat de gebruiker een functie kan kiezen uit een stuk of tien. Bedenk zelf een aantal geschikte van zo veel mogelijk verschillende soort.

De gebruiker selecteert de functie uit een listbox waarin de functievoorschriften met strings staan. In de functie f bereken je het functieresultaat, waarbij afhankelijk van de waarde van Listbox1.Itemindex het juiste voorschrift wordt gebruikt om het functieresultaat te berekenen. In de functie f hangt het gebruikte functievoorschrift dus af van de waarde van Listbox1.Itemindex. Hiervoor heb je een keuzestatement nodig, en omdat er uit een tiental varianten gekozen moet worden, is een case-statement erg geschikt.
Verwijs binnen de declaratie van f naar Itemindex met Form1.Listbox1.Itemindex, want zonder Form1 ervoor is Listbox1 een onbekende component.




Ik heb hier de hele opdracht neergezet, zodat jullie begrijpen waar het over gaat. Mijn vraag gaat maar over een deel van de opdracht.

ik zal gelijk even laten zien wat mijn uitwerking hiervan is

Code: Selecteer alles

 Function f (x:real):real;
begin
    case form1.ListBox1.itemindex of
      0: {formule (x^2)-2}
      begin
       result:=(power(x,2)-2 );
      end;

      1: {formule (3*x)-1}
      begin
        result:= ((3*x)-1);
      end;

      2: {formule x^3+x^2+x+1}
      begin
        result:= (power(x,3)+ power(x,2)+x+1);
      end;

      3: {formule x^2-4x+7}
      begin
        result:= (power(x,2)-(4*x)+7);
      end;

      

end;
End;

procedure TForm1.BerekenClick(Sender: TObject);
var a,b,m,ya,yb,ym,nulpunt,e: real;

begin
      e:= StrToFloat(edit3.text);   {maximale afwijking}
      a:= StrToFloat(edit1.text);
      b:= StrToFloat(edit2.Text);

      m:= ((a+b)/2);
      ya:= f(a);              {aanroepen van de functie}
      yb:= f(b);
      ym:= f(m);

      {berekening nulpunt}
      repeat
         m:= ((a+b)/2);
         ym:= f(m);
         if ym>0 then
            b:=m
         else a:=m;
      until (ym<e);

      nulpunt:= m;

      {uitvoer}
      {tussenstappen toevoegen in de memo}
      Memo1.Clear;
      Memo1.Lines.Add     ('     a            ' + '     m            '
                         + '     b            ' + '     f(a)         '
                         + '     f(m)         ' + '     f(b)         ');

      Memo1.Lines.Add ( Format('%7.6f',[a])   + '  '
                      + Format('%7.6f',[m])   + '  '
                      + Format('%7.6f',[b])   + '  '
                      + Format('%7.6f',[f(a)])  + '  '
                      + Format('%7.6f',[f(m)])  + '  '
                      + Format('%7.6f',[f(b)]));

      if (a<b) then
       edit4.text:= FloatToStr(nulpunt)
      else
       Showmessage ('vul juiste waarden a en b in');


end;

end. 
Mijn vraag gaat over het nulpunt berekenen. Klopt het wat ik heb ingevuld?
Omdat je steeds weer verschillende waardes van a en b krijgt (de ene stap voor b en de andere stap weer voor a), omdat het interval verandert. Maar ik weet niet zo goed hoe je dat invult, zodat je in de memo, alle tussenstappen krijgt te zien.
Nu ik dit zo heb ingevuld, krijg ik ook maar één tussenstap te zien en een verkeerd nulpunt.
Wat doe ik verkeerd??
Durf te vragen!

arie
Moderator
Moderator
Berichten: 3911
Lid geworden op: 09 mei 2008, 09:19

Re: Programmeren 'Nulpuntsbepaling'

Bericht door arie » 12 mar 2014, 21:09

Je bent er al bijna, alleen de volgorde klopte nog niet helemaal.

De procedure BerekenClick() moet het volgende doen:

[1] neem de door de gebruiker ingevoerde waarden over

[2] zet Memo1 klaar voor tonen van de resultaten (clear memo en toon titelbalk)

[3] bereken en toon de tussenresultaten (= repeat zolang nodig):
- - berekening waarden bisectiemethode
- - toon resultaat in Memo1
- - update a of b

[4] toon eindresultaat

Deze stappen kan je in je code overnemen als commentaar en direct daaronder je code plaatsen.
In eerste opzet krijg je dan deze code:

Code: Selecteer alles

procedure TForm1.BerekenClick(Sender: TObject);
var a,b,m,ya,yb,ym,nulpunt,e: real;

begin
      { neem de door de gebruiker ingevoerde waarden over: }
      e:= StrToFloat(edit3.text);   {maximale afwijking}
      a:= StrToFloat(edit1.text);
      b:= StrToFloat(edit2.Text);

      { zet Memo1 klaar voor tonen van de resultaten: }
      Memo1.Clear;
      Memo1.Lines.Add     ('     a            ' + '     m            '
                         + '     b            ' + '     f(a)         '
                         + '     f(m)         ' + '     f(b)         ');


      { bereken en toon de tussenresultaten: }
      repeat
         { berekening waarden bisectiemethode: }
         m:= ((a+b)/2);
         ya:= f(a);
         ym:= f(m);
         yb:= f(b);
        
        { toon tussenresultaat: } 
        Memo1.Lines.Add ( Format('%7.6f',[a])   + '  '
                      + Format('%7.6f',[m])   + '  '
                      + Format('%7.6f',[b])   + '  '
                      + Format('%7.6f',[f(a)])  + '  '
                      + Format('%7.6f',[f(m)])  + '  '
                      + Format('%7.6f',[f(b)]));

         { bepaal nieuwe waarde van a of b: }
         if ym>0 then
             b:=m
         else 
             a:=m;

      until ( Abs(ym) < e);

      { toon eindresultaat: }
      nulpunt:= m;

end;

end. 
LET OP: de absolute waarde van ym moet kleiner worden dan e, waarom?

Noot: deze code moet je nog wel verder uitwerken, met name de oplossing voor de beginwaarden waarbij f(a) > 0 > f(b).
Voorbeeld: voor de parabool met a=-2 en b=-1

BusinessMath
Vast lid
Vast lid
Berichten: 57
Lid geworden op: 23 feb 2014, 15:14

Re: Programmeren 'Nulpuntsbepaling'

Bericht door BusinessMath » 15 mar 2014, 17:44

Bedankt!
Ik heb de volgorde verandert. Als ik het nu wil uitvoeren lukt het voor de eerste formule, gelukkig.
Maar voor de andere formules krijg ik nog geen antwoord. Dit komt zeker doordat ik alle voorwaarden er nog niet in heb staan.

• De voorwaarde dat f(a) en f(b) verschillend moeten zijn van teken.
Kun je dat zo noteren: (ya<0>yb) OR (ya>0<yb)?
Maar waar moet dat dan komen te staan? In een aparte if … then? Of kun je dat samenvoegen bij de andere voorwaarde (a<b).

En wat ik ook niet helemaal begrijp: hoe kun je invoeren dat het om een interval gaat. Dat de functie continu moet zijn op interval [a,b]??
Durf te vragen!

arie
Moderator
Moderator
Berichten: 3911
Lid geworden op: 09 mei 2008, 09:19

Re: Programmeren 'Nulpuntsbepaling'

Bericht door arie » 16 mar 2014, 12:57

De voorwaarden moeten we controleren tussen stap 1 en stap 2 van de code:

[1] neem de door de gebruiker ingevoerde waarden over

en controleer de voorwaarden:
- - bepaal ya en yb
- - als ya = 0 dan is a de oplossing en zijn we klaar: toon a als eindresultaat
- - als yb = 0 dan is b de oplossing en zijn we klaar: toon b als eindresultaat
- - als het teken van ya gelijk is aan het teken van yb: geef foutmelding
- - anders: mogen we het bisectie-algoritme toepassen, dat codeblok hadden we al:

{----------------------------------------------------------------------------------------------------------}
[2] zet Memo1 klaar voor tonen van de resultaten (clear memo en toon titelbalk)
[3] bereken en toon de tussenresultaten (= repeat zolang nodig):
- - berekening waarden bisectiemethode
- - toon resultaat in Memo1
- - update a of b
[4] toon eindresultaat
{----------------------------------------------------------------------------------------------------------}


De condities mag je niet samentrekken zoals jij doet:
(ya<0>yb) OR (ya>0<yb)
maar moet je volledig uitwerken:
(ya<0 AND yb>0) OR (ya>0 AND yb<0)

Maar kijk ook eens hoe je hier de math functie Sign() hier zou kunnen gebruiken:
wat moet gelden voor Sign(ya) en Sign(yb) ?

Diezelfde Sign() kan je ook gebruiken om in de lus (van stap [3]) a of b te updaten: er zijn daar 4 mogelijkheden:
als ya<0 en yb>0 en ym>0 dan moet b = m worden
als ya<0 en yb>0 en ym<0 dan moet a = m worden
als ya>0 en yb<0 en ym>0 dan moet a = m worden
als ya>0 en yb<0 en ym<0 dan moet b = m worden
Maar die 4 mogelijkheden kan je ook zo samenvatten:
Als het teken van ym gelijk is aan het teken van yb, dan moet b gelijk worden aan m, anders moet a gelijk worden aan m. En voor "het teken van" hebben we Sign().

Kom je hiermee verder?

PS:
Jouw functie f geeft andere functies dan die op het voorbeeld-plaatje staan.

PPS:
Testen op continuiteit is volgens mij niet mogelijk, tenzij je een programma schrijft dat functies kan analyseren.
Maar als iemand hier een eenvoudige oplossing voor heeft hoor ik dat ook graag.

David
Moderator
Moderator
Berichten: 4927
Lid geworden op: 14 mei 2009, 16:22

Re: Programmeren 'Nulpuntsbepaling'

Bericht door David » 16 mar 2014, 19:16

de opdracht schreef:De gebruiker geeft de functie op door er één te kiezen uit een lijstje dat het programma geeft, en gebruiker geeft twee getallen a en b die voldoen aan de volgende voorwaarden:
• a<b
• de functie moet continu zijn op het interval [a,b]?? Hoe kun je dit invullen?
• ( f(a)<0 en f(b)>0) of (f(a)>0 en f(b)<0), kort gezegd: f(a) en f(b) zijn verschillend van teken.
voorbeeld hoe je het programma kan gaan uitwerken schreef:Als het bisectie-algoritme goed werkt, dan breid je het programma uit en verander je het, zodat de gebruiker een functie kan kiezen uit een stuk of tien. Bedenk zelf een aantal geschikte van zo veel mogelijk verschillende soort.
De gebruiker geeft zelf geen functie aan maar maakt een selectie uit de door jou gekozen functies. Als je kiest om tan(x) te plaatsen in de lijst, kan je zelf van tevoren bepalen op welke intervallen tan(x) continu is. Als een gebruiker dan bijvoorbeeld (a, b) = (0, 3) geeft, dan kan je iets zeggen als: "tan(x) is niet continu op het interval [0,3], kies een ander interval" (met suggesties, o.i.d.), op basis van je analyse. Voor sommige functies geeft dat in je code geen extra controle hiervoor, bijvoorbeeld x^2 - 2.

Je kan ook nog kijken naar ya * yb. Wat als ya * yb < 0? Wat als ya * yb = 0? Wat als ya * yb > 0?

Het aantal stappen van de bisectiemethode is vooraf te bepalen. Stel dat de gebruiker een interval geeft van 100 breed, bijv. [0, 100]. en epsilon = 0,01. Dan moet het interval waarin een nulpunt ligt kleiner worden dan 0,01. Elke stap wordt het interval twee keer zo klein. Je kan dus oplossen:
100 * 0.5^s < 0,01 met s het aantal stappen. Als je wilt.
Stap 1 van het oplossen van een probleem is te erkennen dat je een probleem hebt.
(Raffiek Torreman)

BusinessMath
Vast lid
Vast lid
Berichten: 57
Lid geworden op: 23 feb 2014, 15:14

Re: Programmeren 'Nulpuntsbepaling'

Bericht door BusinessMath » 17 mar 2014, 11:46

Bedankt!

Het programma werkt. ik heb in totaal 7 formules ingevoerd en in kan bij alle het nulpunt berekenen!

Dit is hoe mijn programma er nu uitziet!::

Code: Selecteer alles

Function f (x:real):real;
var a,b:real;
begin
     a:= StrToFloat(form1.edit1.text);
     b:= StrToFloat(form1.edit2.Text);
    case form1.ListBox1.itemindex of
      0: {formule (x^2)-2}
      begin
       result:=(power(x,2)-2 );
      end;

      1: {formule (3*x)-1}
      begin
        result:= ((3*x)-1);
      end;

      2: {formule x^3+x^2+x+1}
      begin
        result:= (power(x,3)+ power(x,2)+x+1);
      end;

      3: {formule x^2-(5*x)+6}
      begin
        result:= (power(x,2)-(5*x)+6);
      end;

      4:  {tan(x)}
      begin
        if (a<-2) AND (b>1) then
          showmessage ('vul waarde a>-2 en b<1 in')
        else
          result:= tan(x);
      end;

      5: {sin(x)}
      begin
        if (a<-2) AND (b>1) then
          showmessage ('vul waarde a>-2 en b<1 in')
        else
        result:= sin(x);
      end;

      6: {cos(x)}
      begin
        if (a<-3) AND (b>2) then
          showmessage ('vul waarde a>-2 en b<1 in')
        else
        result:= cos(x);
      end;
end;
End;

procedure TForm1.BerekenClick(Sender: TObject);
var a,b,m,ya,yb,ym,nulpunt,e: real;

begin {1. invoer gebruiker}
  e:= StrToFloat(edit3.text);   {maximale afwijking}
  a:= StrToFloat(edit1.text);
  b:= StrToFloat(edit2.Text);
  ya:= f(a);
  yb:= f(b);

  begin
    if ya=0 then
      edit4.text:=FloatToStr(a);
    if yb=0 then
      edit4.text:=FloatToStr(b);
    if Sign(ya)=Sign(yb) then
      Showmessage ('vul andere waarden a en b in')
    else
      Begin
     {2.}
      Memo1.Clear;
      Memo1.Lines.Add     ('     a            ' + '     m            '
                         + '     b            ' + '     f(a)         '
                         + '     f(m)         ' + '     f(b)         ');

    {3}
    {bereken en toon de tussenresultaten}
     repeat
         {berekening waarden bisectiemethode}
         m:= ((a+b)/2);
         ya:= f(a);
         ym:= f(m);
         yb:= f(b);

        {toon tussenresultaat}
        Memo1.Lines.Add ( Format('%7.6f',[a])   + '    '
                      + Format('%7.6f',[m])   + '    '
                      + Format('%7.6f',[b])   + '    '
                      + Format('%7.6f',[f(a)])  + '    '
                      + Format('%7.6f',[f(m)])  + '    '
                      + Format('%7.6f',[f(b)]));

       {bepaal nieuwe waarde van a of b}
         if Sign(ym) = Sign(yb) then
             b:=m
         else
             a:=m;

      until (Abs(ym) < e);

      {4}
      {toon eindresultaat}
      nulpunt:= m;


      if (a<b) then
       edit4.text:= FloatToStr(nulpunt)
      else
       Showmessage ('vul juiste waarden a en b in');
       end;
    end;
      edit4.enabled := false;

end;

end.
Durf te vragen!

David
Moderator
Moderator
Berichten: 4927
Lid geworden op: 14 mei 2009, 16:22

Re: Programmeren 'Nulpuntsbepaling'

Bericht door David » 17 mar 2014, 12:15

Goed bezig :idea:

Maar sin(x) en cos(x) zijn continu toch (dus ook op alle intervallen)?
voor tan(x) kan ook een nulpunt worden gevonden met a = 8, b = 10. Die zou je ook toe kunnen laten. Hoe kan je weten dat de bisectiemethode voor (a, b) = (8, 10) wel kan maar voor (a, b) = (8, 11) niet?
BusinessMath schreef:

Code: Selecteer alles

6: {cos(x)}
      begin
        if (a<-3) AND (b>2) then
          showmessage ('vul waarde a>-2 en b<1 in')
        else
        result:= cos(x);
      end;
Nadat een gebruiker voor a -4 en voor b 3 heeft ingevuld geef je de melding waarna je a = -2.5, b = 2000 zou accepteren, door AND.

Evenzo zijn voor tan(x) voor a -1000 en b 0 geaccepteerde waarden.
Stap 1 van het oplossen van een probleem is te erkennen dat je een probleem hebt.
(Raffiek Torreman)

Plaats reactie