Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie? (https://www.delphipraxis.net/155927-programm-zur-bestimmung-von-pi-schleife-auf-knopfdruck-abbrechen-aber-wie.html)

mabstudent 13. Nov 2010 14:13

Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,

Ich bin schon seit längerem in eurem Forum unterwegs und bisher mit der Suche ausgekommen, doch dieses mal reicht sie mir leider nicht ganz.
Vorab tut es mir leid dass ich euch das Problem nur aus der Sicht eines Maschinenbaustudenten, der ein Pflichtsemester Informatik absolvieren muss, darstellen kann.


Aufgabe ist folgende:

Grafische Ausgabe von Zufallszahlen --> Bestimmung von PI nach der Monte Carlo Methode


Meine Teilaufgaben sind:
1. Imagefeld erzeugen
2. Viertelkreis mit r=300 darauf zeichnen, Mittelpunkt(0,0)
3. auf Knopfdruck Pixelregen starten
4. auf Knopfdruck Pixelregen stoppen
5. Pixel die zufällig im Viertelkreis landen eine Farbe geben z.B. blau
6. Pixel die außerhalb des Viertelkreises landen eine andere Farbe geben z.B. rot
7. PI ist das Verhältnis der im Viertelkreis liegenden Pixel zu der Gesamtheit der Pixel (PI soll eine 12-stellige Genauigkeit haben)

Mein Struktogramm dazu: siehe Anhang
Mein bisheriger Quelltext:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Buttons, StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    BitBtn1: TBitBtn;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);

  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  b,h,x,y,u,i:integer;


implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
application.ProcessMessages;
u:=0;
Randomize();
image1.Canvas.Brush.color:=clwhite;
image1.Canvas.FillRect(Rect(0,0,b,h));
image1.Canvas.MoveTo(0,300);

  FOR x:=0 TO 300 DO                //Viertelkreis zeichnen
    begin
    y:=trunc(sqrt((300*300)-(x*x)));
    image1.canvas.LineTo(x,y);
    end;

end;

procedure TForm1.Button1Click(Sender: TObject);   //Zufallszahlen grafisch ausgeben

begin
  application.processmessages; //damit die Anwendung nicht hakt
  FOR i:=0 TO u:=1 DO //lass die Schleife laufen bis u:=1 ist
  x:=random(300); //zufallszahlen erzeugen von 0-300
  y:=random(300);
  IF (sqrt((x*x)+(y*y))<=300) //wurzel aus x^2+y^2 entspricht dem radius des VK
  THEN image1.canvas.pixels[x,y]:=clblue
  ELSE image1.Canvas.Pixels[x,y]:=clred;
end;

end;



procedure TForm1.Button2Click(Sender: TObject);
begin
u:=1;
end;

end.

Folgende Probleme treten hierbei auf:

1. Wie kann ich die Schleife welche Zufallszahlen erzeugt und diese als Pixel auf das Image regnen lässt solange laufen lassen bis ich einen Knopf drücke, der es stoppt?
2. Wie lautet der richtige Quellcode für die Berechnung von PI:=((4*Viertelkreispixel)/Gesamtzahlpixel)? Anders gefragt: Welche Typen müssen die Variablen haben?

rollstuhlfahrer 13. Nov 2010 14:29

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
1.) Ein Viertelkreis zeichnen kannst du bestimmt Windows überlassen, so brauchst du keine Schleife.
Delphi-Quellcode:
begin
  Image1.Picture.Bitmap.Canvas.Ellipse(0, -300, 600, 300);
end;
2.) deine For-Schleife ist wahrhaftig etwas sonderbar. Delphi-Pascal != C, deshalb kannst du das nur mit einer WHILE-Struktur machen (Außerdem gibt es einen eleganteren Weg für die Abbrechung (u => Abbr (Boolean):
Delphi-Quellcode:
var i: Integer;
begin
  Abbr := false;
  while not Abbr do
  begin
    Application.ProcessMessages;
    //...
  end;
end;
Dein Abbrechen sieht dann einfach so aus, dass du Abbr auf TRUE setzt.

Zitat:

Zitat von mabstudent (Beitrag 1061253)
2. Wie lautet der richtige Quellcode für die Berechnung von PI:=((4*Viertelkreispixel)/Gesamtzahlpixel)? Anders gefragt: Welche Typen müssen die Variablen haben?

PI sollte dann mindestens Single sein (Das reicht IMHO für 12 Nachkommastellen, wenn nicht Double).
Die Variable Viertelkreispixel reicht, wenn sie Integer ist. So viele Pixel und vor allem Zeit hast du gar nicht zur Verfügung (Integer geht bis 2 Mrd). Für Gesamtpixel reicht dann auch Integer. Du kannst diese allerdings beide nicht errechnen, sondern darfst deine Pixelregen-Funktion dahingehend umschreiben, dass die automatisch die entsprechenden Werte aktualisiert.

Bernnhard

sx2008 13. Nov 2010 15:03

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Delphi-Quellcode:
var
  b,h,x,y,u,i:integer;  // Hilfe, globale Variablen mit nur einem Buchstaben
Mach' dich mal mit lokalen Variablen vertraut.
Globale Variablen mit nur einem Buchstaben sind eine "Todsünde" für jeden Programmierer.
Beispiel wie man's besser macht:
Delphi-Quellcode:
procedure TForm1.ZeichneMonteCarlo;
var
  x ,y : integer; // Lokale Variablen
begin
  x:=random(300); //zufallszahlen erzeugen von 0-299
  y:=random(300);
  if (sqrt((x*x)+(y*y))<=300) then //wurzel aus x^2+y^2 entspricht dem radius des VK
    image1.canvas.pixels[x,y]:=clblue
  else
    image1.Canvas.Pixels[x,y]:=clred;
end;

mabstudent 13. Nov 2010 15:08

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Vielen vielen Dank, hätte ich mich mal eher an euch gewandt, an meiner "WirrWarr Schleife" saß ich nämlich mehrere Stunden :wink:

Danke auch für den Tipp von sx2008


Deshalb stelle ich jetzt gleich mein nächstes Problem bei dem Programm vor, auch wenn ich beim Rumprobieren viel gelernt hab:

Um PI zu berechnen muss ich ja die Pixel im Viertelkreis zählen und die Pixel im Quadrat, also die Gesamtzahl der Pixel.
Wie kann ich das machen?
Kann ich in meiner Schleife welche den Pixelregen erzeugt folgendes machen:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);

begin
  abbr:=false;
  VKhits:=0;  //Anzahl der Pixel im Viertelkreis, zu Anfang :=0
  NVKhits:=0; //Anzahl der Pixel Nicht im Viertelkreis, zu Anfang:=0
  WHILE NOT abbr DO
  begin
  Application.ProcessMessages;
  x:=random(300);
  y:=random(300);
  IF (sqrt((x*x)+(y*y))<=300)
  THEN image1.canvas.pixels[x,y]:=clblue AND inc(VKhits) //Pixel blau färben UND Anzahl aufsummieren
  ELSE image1.Canvas.Pixels[x,y]:=clred AND inc(NVKhits); //Pixel rot färben UND Anzahl aufsummieren
  end;

label1.Caption:=(4*VKhits/(NVKhits+VKhits));  //ausgabe PI




end;
Klar, das habe ich schon probiert und es funktioniert nicht, aber hier könnt ihr denk ich am besten sehen was ich vorhabe: Um PI zu berechnen muss ich irgendwie an die Anzahl der Pixel im Viertelkreis und an die außerhalb kommen. über die suche habe ich rausgefunden das inc() eine funktion zum zählen ist.

jfheins 13. Nov 2010 15:19

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Mehrere Anweisungen kannst du immer mit begin ... end; zusammenfassen.
Also:
Delphi-Quellcode:
if xyz then
begin
  //Anweisungen
  //noch mehr Anweisungen
end;

mabstudent 13. Nov 2010 15:49

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
...dann bekomme ich den fehler:

'END' erwartet aber 'ELSE' gefunden

Delphi-Quellcode:
begin
  abbr:=false;
  VKhits:=0;
  NVKhits:=0;
  WHILE NOT abbr DO
  begin
  Application.ProcessMessages;
  x:=random(301);
  y:=random(301);
  IF (sqrt((x*x)+(y*y))<=300)
  THEN image1.canvas.pixels[x,y]:=clblue; //Anweisung 1a
       VKhits:=VKhits+1                    //weitere Anweisung
  ELSE image1.Canvas.Pixels[x,y]:=clred;  //Anweisung 1b
       NVKhits:=NVKhits+1;                //weitere Anweisung

  end;
end;

Bummi 13. Nov 2010 16:35

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Delphi-Quellcode:
  THEN
BEGIN
       image1.canvas.pixels[x,y]:=clblue; //Anweisung 1a
       VKhits:=VKhits+1 //weitere Anweisung
END
  ELSE
BEGIN
       image1.Canvas.Pixels[x,y]:=clred; //Anweisung 1b
       NVKhits:=NVKhits+1; //weitere Anweisung
END

Sir Rufo 13. Nov 2010 17:55

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Hier haben wir doch ein perfektes Beispiel dafür, warum man Code und Ausgabe trennen sollte.

Die ermittelten Zufallspunkte werden in einem Bitmap gepseichert und nun drängt sich die Frage auf, wie man die Anzahl der Zufallspixel bekommt und die Anzahl der Pixel im Kreissegment.
Das Bitmap beinhaltet ja auch noch die Kreislinie.

Antwort: Geht gar nicht. Durch die Zufallsmethode können auf einem Pixelpunkt des Bitmaps n Zufallspunkte liegen. Wie soll das im Nachhinein gezählt werden? Pixel ist blauer als der andere Blaue?

Also trennen wir das Ganze mal, denn die Berechnung muss auch ohne grafische Ausgabe erfolgen und selbige dient ja nur der Visualisierung.

Zunächst brauchen wir ja einen Speicher für einen Zufallspunkt. Da bietet und Delphi ja passend
Delphi-Quellcode:
TPoint
an. Da können wir einen x und y Wert abspeichern (was wollen wir mehr).
Wir haben mehrere Punkte, gut wir brauchen eine Liste. Das ist jetzt Geschmackssache, ob man das mit einer verketteten Liste (Pointer) selber baut, ein variables Array benutzt oder das fertige
Delphi-Quellcode:
TList
oder auch
Delphi-Quellcode:
TObjectList
.

Jede Liste hat eine Eigenschaft
Delphi-Quellcode:
Count
und schon weiß man, wieviele Punkte man insgesamt erzeugt hat.

Jetzt fehlt und ein Algorithmus, der berechnen kann, ob ein Punkt innerhalb oder ausserhalb des Kreissegments liegt.

Die Ausgabe erfolgt jetzt anhand genau dieser Liste. Also jeden Pixel wie gewohnt auf das Bitmap klatschen.
Über den Algorithmus, weiß man, ob der Punkt blau oder rot gepinselt werden soll.

BTW das Ergebnis wird umso genauer, je größer man das Zielgebiet macht.
Ein Gebiet mit 10x10 Pixel ergibt ein ungenaues Ergebnis.
1000x1000 wäre schon besser

Bei einer Auflösung mit 10.000x10.000 habe ich aber Probleme mit der Darstellung. Darum hilft jetzt auch die Trennung zwischen Code und Ausgabe, denn die Pixel-Koordinaten werden für die Ausgabe einfach skaliert.
Code:
AusgabeX = Runden( WertX / WertBreite * AusgabeBreite )

Aphton 13. Nov 2010 18:02

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hoi,
Ich war mal so nett und habe es programmiert.
Seid nicht böse, weil ich es ihm vorkaue. Mir war sehr langweilig und ich wollte es einmal programmiert haben.
Übrigens finde ich die Monte-Carlo Methode sehr interessant! Sie zeigt, was sich so alles mit Wahrscheinlichkeiten machen lässt xD

@ThreadStarter
Es bringt zwar nicht direkt viel, ne Komplettlösung zu posten, aber immerhin. Wenn du evt. nicht weiterkommst, kannste ja im Quellcode ggf. nachkucken!

Edit:
Übrigens - Codestil = Spaghetticode.

MfG

mabstudent 15. Nov 2010 12:45

AW: Programm zur Bestimmung von PI - Schleife auf Knopfdruck abbrechen, aber wie?
 
Vielen Dank für eure Hilfe!!


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:43 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz