Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Zufallszahl ziehen ohne wiederholung (https://www.delphipraxis.net/128822-zufallszahl-ziehen-ohne-wiederholung.html)

_frank_ 7. Feb 2009 21:06

Re: Zufallszahl ziehen ohne wiederholung
 
schöner Spaghetti-code :( und massig units...wozu die 3 leeren units (2-4)?
nachdem ich deine projektdatei geöffnet habe, vermute ich, dass die units auch nicht die richtigen sind (anderer Ordner).
lagere mal bitte die fragen+antworten in eine externe Datei (ini,xml,etc) aus, damit das Codesegment universeller und besser lesbarer wird.
36 Schwierigkeitsgrade? ich denke 3 reichen...ist sicher nur unglückliches benamen der variablen.

als kleiner tipp am rande...folgendes lässt sich auch entscheidend verkürzen:
Delphi-Quellcode:
      Case Loesung of
        1:begin
          Panel2.Color:=clgreen;
          Panel3.Visible:=false;
          Panel4.Visible:=false;
          Panel5.Visible:=false;
          end;
        2:begin
          Panel3.Color:=clgreen;
          Panel2.Visible:=false;
          Panel4.Visible:=false;
          Panel5.Visible:=false;
          end;
        3:begin
          Panel4.Color:=clgreen;
          Panel2.Visible:=false;
          Panel3.Visible:=false;
          Panel5.Visible:=false;
          end;
        4:begin
          Panel5.Color:=clgreen;
          Panel2.Visible:=false;
          Panel4.Visible:=false;
          Panel3.Visible:=false;
          end;
z.b. so:
Delphi-Quellcode:
var panel:TPanel
Panel2.Visible:=false;
Panel3.Visible:=false;
Panel4.Visible:=false;
Panel5.Visible:=false;
panel:=findcomponent('Panel'+IntToStr(Loesung+1));
if assigned(panel) then
begin
  panel.visible:=true;
  panel.color:=clGreen;
end;
oder
Delphi-Quellcode:
  case ZS of
    1:
    begin
      with mediaplayer1 do
      begin
        Filename:=Pfad+'\Mediendateien\richtigeAntwort\1.wav';
        mediaplayer1.Open;
        mediaplayer1.Play;
      end;
    end;
... //obiger code noch 3x und das bei jedem panelclick.
einfach so:
Delphi-Quellcode:
with mediaplayer1 do
begin
  Filename:=Pfad+'\Mediendateien\richtigeAntwort\'+IntToStr(ZS)+'.wav';
  mediaplayer1.Open;
  mediaplayer1.Play;
end;
du kannst mehreren Panels auch die gleiche Click-Prozedur zuweisen...so sparst du redundaten code...
wenn du die paar sachen machst und noch eine bessere Einrückung :) dann wird dein code schonmal viel besser lesbar.warum eigentlich 4 mediaplayer?

den Vergleich, ob frage schon gefragt wurde hab ich noch nicht gefunden...

Gruß Frank

jaenicke 7. Feb 2009 21:22

Re: Zufallszahl ziehen ohne wiederholung
 
Sag mal, kann das sein, dass da die falschen Dateien dabei sind? In dem Quelltext nimmst du ja noch eine beliebige Zufallszahl. Im Projekt wird verwiesen auf ein Verzeichnis '..\das was net ging\Delphi Projekt', aber diese Dateien sind leider nicht dabei...

Zum Konzept: Wie wäre es, wenn du die Fragen aus einer Datei lädst oder zumindest konstante Arrays benutzt? So ist das doch viel zu umständlich, wenn du Fragen hinzufügst, etc.

// EDIT:
Zur Frage "Wo ist der Sitz des Bundeshauses?":
Der Sitz des Bundestages ist Berlin und das Bundeshaus steht immer noch in Bonn, wird aber anderweitig verwendet.

walli18 7. Feb 2009 21:43

Re: Zufallszahl ziehen ohne wiederholung
 
Das habe ich auch schon gerade festgestellt, ich werde gleich noch mal die richtigen hochladen

jaenicke 7. Feb 2009 21:46

Re: Zufallszahl ziehen ohne wiederholung
 
Da reichen aber die Quelltextdateien, und die kannst du auch hier in den Anhang legen, wer die anderen Dateien dazu will, der kann ja den Link oben benutzen.

walli18 7. Feb 2009 22:18

Re: Zufallszahl ziehen ohne wiederholung
 
So das hab ich jetzt auch gemacht! Hier ist der Quelltext!
http://rapidshare.com/files/19529755...lli18.rar.html
Sorry, ich hätts auch hier hochladen können, is mir aber zu spät eingefallen!


Das Problem bleibt immer noch:
1. ich muss den Quelltext redundanter gestalten
2. wieso er in der unit 5 bei button 1 click das mit der zufallszahl z super macht und in der listbox dokumentiert aber genau dasselbe bei form5.button 2. click nicht macht und immer gleich die if bedingung erfüllt was dazu führt, dass er schreibt, sie haben das spiel erfolgreich beendet

jaenicke 7. Feb 2009 22:32

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Delphi-Quellcode:
procedure TForm5.Button2Click(Sender: TObject);

var p,z,i:integer;

    liste: string;
begin
////////////////////////////////////////////////////////////////////////
  Randomize;
 // liste:='';
  //for i:=1 to gesamt do
    //liste:=liste+chr(i);
if length(liste)<=gesamt-anzahl then
  begin
    showmessage('Sie haben das Spiel erfolgreich beendet!');
    exit;
  end;

Erstens hast du eine lokale Variable liste, deine globale gleichnamige Variable wird also gar nicht benutzt... :roll:
Zweitens ist der Quelltext auskommentiert, der die Variable füllt...

So geht es bei mir:
Delphi-Quellcode:
procedure TForm5.Button2Click(Sender: TObject);

var p,z,i:integer;

    //liste: string;
begin
////////////////////////////////////////////////////////////////////////
  Randomize;
  liste:='';
  for i:=1 to gesamt do
    liste:=liste+chr(i);
if length(liste)<=gesamt-anzahl then

walli18 7. Feb 2009 22:46

Re: Zufallszahl ziehen ohne wiederholung
 
Danke jaenicke! Ich bin einen großen Schritt weiter!
Es funktioniert jetzt! Ich hab nur noch das Problem, dass ich ja immer wieder wenn ich eine neue zufallszahl will und die brauche ich 15 mal auf button 2 clicke!
und mit dieser variante kommen dann die zufallszahlen doppelt vor, das möchte ich ja aber nicht, da ich keine doppelten fragen haben möchte! Ich denke das liegt daran, dass diese liste immer beim klicken auf button 2 neu erstellt wird, aber wie muss ich den quelltext ändern, damit die ersten 15 zufallszahlen nicht doppelt vorkommen??
Ich hab auch noch nen button für neues spiel, dabei sollten wieder neue zufallszahlen kommen! Was muss ich da eigeben??

jaenicke 7. Feb 2009 22:58

Re: Zufallszahl ziehen ohne wiederholung
 
Ach ja, die Initialisierung brauchst du da ja nicht. Die brauchst du dann aber für ein neues Spiel, womit deine zweite Frage beantwortet ist. ;-)
Delphi-Quellcode:
procedure TForm5.Button2Click(Sender: TObject);

var p,z,i:integer;

begin
////////////////////////////////////////////////////////////////////////
  Randomize;
if length(liste)<=gesamt-anzahl then
Und einen Fehler: Wenn ich auf Weiter statt eine Antwort klicke wird trotzdem weitergezählt...

walli18 7. Feb 2009 23:06

Re: Zufallszahl ziehen ohne wiederholung
 
dankeschön, soweit habe ich das auch jetzt endlich hinbekommen! Es bleibt noch eine frage! mit neues spiel starten klappt! Das problem ist nur, dass ich button2. click in form5 habe und neues spiel starten in form6 habe! Der quelltext funktioniert (ich hab ihn mal zum neuen Spiel starten in form5 gehauen) aber in form6 kann ich die liste aus form5 net löschen! Was muss ich dafür tun??

Danke für die Antwort!

jaenicke 7. Feb 2009 23:08

Re: Zufallszahl ziehen ohne wiederholung
 
Weil du die Variable Liste in der implementation Sektion deklariert hast. Damit ist diese nur innerhalb dieser Unit sichtbar. Du kannst die Variable unter public deklarieren, dann kannst du mit Form5.liste auch darauf zugreifen.

globetrotter77 7. Feb 2009 23:36

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Zitat von jaenicke
Weil du die Variable Liste in der implementation Sektion deklariert hast. Damit ist diese nur innerhalb dieser Unit sichtbar. Du kannst die Variable unter public deklarieren, dann kannst du mit Form5.liste auch darauf zugreifen.

Lieber nicht!
Wenn das Teil in zwei oder mehr Formularen benutzt wird, sollte es in einer extra Unit und nur dort definiert werden, das von allen mit uses aufgenommen wird. Dort und ebenfalls nur dort sollte dann auch eine Procedure für den Neuaufbau implementiert werden.
Außerdem am besten auch gleich eine Function, die die gezogene Nummer zurückgibt.
Alle anderen Vorkommen von liste gehören dann rausgeschmissen!

PS: wenn eine Deklaration so wie beschrieben in eine untergeordnete Unit ausgelagert wird, sollte sie einen etwas aussagekräftigeren Namen als liste bekommen. Damit vermeidet man Mehrfach-Deklarationen, zumindest, wenn man nicht weiter in alle Himmelsrichtungen rumkopiert.

jaenicke 7. Feb 2009 23:38

Re: Zufallszahl ziehen ohne wiederholung
 
Diese Stilfragen sind bei dem Quelltext auch schon egal...

_frank_ 7. Feb 2009 23:43

Re: Zufallszahl ziehen ohne wiederholung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von jaenicke
Weil du die Variable Liste in der implementation Sektion deklariert hast. Damit ist diese nur innerhalb dieser Unit sichtbar. Du kannst die Variable unter public deklarieren, dann kannst du mit Form5.liste auch darauf zugreifen.

ist aber ein unsauberer Codestil von außen auf Variablen einer Klasse direkt zuzugreifen, dafür gibts Methoden oder zumindest properties. man sollte so wenig wie nötig von außen setzen, nach möglichkeit sollte sich die Klasse autark verhalten. z.b. eine Methode erstellen der man die nötigen Parameter übergibt, die die klasse für sich anwendet und weiterverabreitet.

er soll etwas sinnvolles lernen und nicht seinen Stil noch schlimmer machen...nebenbei bemerkt, hab ich ihm mal ein Beispielprogramm geschrieben, welches ihm zeigt, wie man das ganze mit ~250 Zeilen Quellcode hinbekommt (+externe Fragenaufbewahrung).

und da solche Frage-Antwort-Programme schon häufiger ein Thema waren, mal mein Programm im Anhang.
aber an alle Anfänger: nicht nur blind kopieren, sondern verstehen :)

Gruß Frank

globetrotter77 7. Feb 2009 23:47

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Zitat von jaenicke
Diese Stilfragen sind bei dem Quelltext auch schon egal...

nun ja! über den Stil müssen wir sicher nicht reden ... :)
aber vielleicht hilft es ja trotzdem, ein paar Tipps zu geben, wie es besser wäre

mr_emre_d 8. Feb 2009 03:44

Re: Zufallszahl ziehen ohne wiederholung
 
Delphi-Quellcode:
type TIntArr = Array of Integer;

...

function _Random( Range: Integer; var List: TIntArr ): Integer;
var
  i, x: Integer;
begin
  if Length(List) = 0 then
  begin
    SetLength(List, Range);
    for i := 0 to High(List) do
      List[i] := i;
  end;
  x := Random(Length(List));
  Result := List[x];
  for I := x+1 to High(List) do
    list[i-1] := list[i];
  SetLength( List, High(List) );
end;

...

// der wert "range" wird nur beim ersten aufruf benutzt, und wenn die liste leer ist
// wird sie halt neu initialisiert :P
// --Klatsch ne Memo und nen Button auf die Form und im OnClick Ereignis schreibst du
// --folgendes
var
  x: Integer;
  i: Integer;
begin
  x := _Random( StrToInt(Edit1.Text), Numbers );
  Memo1.Clear;
  for i := 0 to High(Numbers) do
    Memo1.Lines.Add( inttostr(i+1) + '.'#9 + IntToStr(Numbers[i]) );
  Caption := 'Random Number - ' + IntToStr(X);

Marc. 14. Feb 2009 20:24

Re: Zufallszahl ziehen ohne wiederholung
 
@ mr_emre_d:
Auch eine Lösung, aber da finde ich meine doch persönlich viel eleganter, auch wenn sie hier irgendwie absolut keinen Nachklang fand. ;)

calculon 14. Feb 2009 21:25

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Zitat von walli18
Ich brauche dringend eure Hilfe!
Für ein Quiz was ich erstellt habe möchte ich eine zufallszahl bekommen wenn ich auf einen button klicke! Diese sollte von 1-40 gehen! Beim erneuten klicken auf den button sollte auch eine zufallszahl von 1-40 kommen, außer der, die ich vorher schon hatte! Das ganze will ich 15 mal ausführen, so dass jede frage nur einmal kommt! Jeder zahl von 1-40 ist eine Frage zugeordnet!
Doch wie mache ich das???
Ich bin ein echter anfänger und bräuchte deswegen ne einfache lösung oder nen kompletten Quelltext!
Für die antworten bedanke ich mich schonmal!!

Alternativ zu Marc.'s Vorschlag kannst du auch 'ne StringList nehmen. Diese auffüllen -> Zufällige Indizes generieren -> Den "gezogenen" Eintrag entfernen
Wie beim Lotto halt...
Delphi-Quellcode:
Procedure Sproideldoing;
var
  Pottich: TStringList;
  i, z: integer;
begin
  Pottich := TStringlist.Create;
  try
    for i := 1 to 40 do
      Pottich.Add(IntToStr(i));
    for i := 1 to 15 do
      begin
        z := random(Pottich.Count);
        ShowMessage(IntToStr(Pottich[z]));
        Pottich.Delete(z);
      end;
  finally
      Pottich.Free;
  end;
end;

_frank_ 14. Feb 2009 23:16

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Zitat von calculon
Alternativ zu Marc.'s Vorschlag kannst du auch 'ne StringList nehmen. Diese auffüllen -> Zufällige Indizes generieren -> Den "gezogenen" Eintrag entfernen

wenn man es langsam möchte...
bitte zahlen nicht in stringlisten verwalten. das ist performancemäßig das ungünstigste, was man machen kann...
das sinnvollste der bisherigen Vorschläge ist imho das vermischte array, da es ohne löschen auskommt. da dürfte noch nichtmal eine TList (mit auf TObject gecasteten Integern) rankommen, da es egal ist, ob die Integer-Daten selbst oder Pointer darauf (beides 32-bit-Werte) vertauscht werden. Bei anderen Datentypen dürfte TList schneller sein.

Gruß Frank

calculon 14. Feb 2009 23:49

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Zitat von _frank_
Zitat:

Zitat von calculon
Alternativ zu Marc.'s Vorschlag kannst du auch 'ne StringList nehmen. Diese auffüllen -> Zufällige Indizes generieren -> Den "gezogenen" Eintrag entfernen

wenn man es langsam möchte...
bitte zahlen nicht in stringlisten verwalten. das ist performancemäßig das ungünstigste, was man machen kann...
das sinnvollste der bisherigen Vorschläge ist imho das vermischte array, da es ohne löschen auskommt. da dürfte noch nichtmal eine TList (mit auf TObject gecasteten Integern) rankommen, da es egal ist, ob die Integer-Daten selbst oder Pointer darauf (beides 32-bit-Werte) vertauscht werden. Bei anderen Datentypen dürfte TList schneller sein.

Gruß Frank

Tja, manche passen den Lösungsweg elegant der Aufgabenstellung an, die da lautet 15 Zufallszahlen aus 40 Einträgen jeweils genau einmal, zu ziehen ("Ergebnisorientiert"). Und andere wiederum ("Experten" ;-)) machen sich halt einfach gerne das Leben schwer. Wenn du tatsächlich in der Praxis einen Unterschied in der Geschwindigkeit merkst (für diese Aufgabenstellung wohlgemerkt), dann und nur dann geb' ich dir Recht. Aber ich weiß' schon, könnte ja mal erweitert werden müssen auf 10.000.000 einzigartigen Ziehungen aus 50.000.000.000 Einträgen...

Same old story, same old song! Don't go right, don't go wrong ;-)

_frank_ 15. Feb 2009 00:37

Re: Zufallszahl ziehen ohne wiederholung
 
das mit der Performance war nur als Randbemerkung gemeint...es geht hauptsächlich darum, einen passenden Datentyp zu wählen.und zur Verwaltung von Zahlen ist Stringlist nun mal ungeeignet.
Findest du, dass der Ansatz mit dem vermischten array schwerer war?
Bei der Verwaltung in einer TList wäre der Code fast genauso wie deiner, nur dass du die Konvertierung sparst und es wesentlich schneller gehen dürfte. Der Lösungsansatz an sich war ja nicht falsch, nur das falsche Werkzeug.
du nimmst ja auch nicht die Blumenschaufel (lag gerade griffbereit), um Schnee zu schippen, oder? auch wenns nur die Ausfahrt ist...geht auch, dauert halt nur bisschen länger (blöder Vergleich, aber mir fiel grade nix besseres ein :D)

Deine Bemühungen zu helfen möchte ich natürlich anerkennen :)

ich bin kein Experte (programmiere auch nur als Hobby),aber das sollte Grundwissen sein. Gerade gegenüber Anfängern sollte man da schon bisschen drauf achten, finde ich...es muss nicht der optimalste Code sein, aber unnötige Typ-Konvertierungen verwirren mehr als das sie nutzen.

Gruß Frank

calculon 15. Feb 2009 01:08

Re: Zufallszahl ziehen ohne wiederholung
 
Gute Antwort :-D

Dann gib doch mal ein Beispiel wie mein Code statt einer TStringList mit TList aussähe. Habe noch nie damit gearbeitet. Du hast mich schon fast überzeugt

Sunlight7 15. Feb 2009 01:22

Re: Zufallszahl ziehen ohne wiederholung
 
Delphi-Quellcode:
rocedure Sproideldoing;
var
  Pottich: TList;
  i, z: integer;
begin
  Pottich := TList.Create;
  try
    for i := 1 to 40 do
      Pottich.Add(TObject(i));
    for i := 1 to 15 do
      begin
        z := random(Pottich.Count);
        ShowMessage(IntToStr(Integer(Pottich[z])));
        Pottich.Delete(z);
      end;
  finally
      Pottich.Free;
  end;
end;
Wenns mich net täuscht so, nur getippt, nicht propiert :mrgreen:

_frank_ 15. Feb 2009 01:26

Re: Zufallszahl ziehen ohne wiederholung
 
aus deinem code mal fix geändert (wie gesagt, viel anders ists nicht):

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Pottich: TList;
  i, z: integer;
begin
  Pottich := Tlist.Create;
  try
    for i := 1 to 40 do
      Pottich.Add(TObject(i));
    for i := 1 to 15 do
      begin
        z := random(Pottich.Count);
        ShowMessage(IntToStr(Integer(Pottich[z])));
        Pottich.Delete(z);
      end;
  finally
      Pottich.Free;
  end;
end;
dein code war übrigends nicht ganz lauffähig... das IntToStr bei der showmessage war zuviel :)

Mist...da war jemand schneller...und das um die Zeit

Gute Nacht

calculon 15. Feb 2009 09:00

Re: Zufallszahl ziehen ohne wiederholung
 
Zitat:

Zitat von _frank_
aus deinem code mal fix geändert (wie gesagt, viel anders ists nicht):

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Pottich: TList;
  i, z: integer;
begin
  Pottich := Tlist.Create;
  try
    for i := 1 to 40 do
      Pottich.Add(TObject(i));
    for i := 1 to 15 do
      begin
        z := random(Pottich.Count);
        ShowMessage(IntToStr(Integer(Pottich[z])));
        Pottich.Delete(z);
      end;
  finally
      Pottich.Free;
  end;
end;
dein code war übrigends nicht ganz lauffähig... das IntToStr bei der showmessage war zuviel :)

Mist...da war jemand schneller...und das um die Zeit

Gute Nacht

Ist wirklich sinnvoller und genauso leicht. TList kannte ich davor jedoch nicht. Hast mich überzeugt ;-)

Gruß
--

Sunlight7 15. Feb 2009 15:37

Re: Zufallszahl ziehen ohne wiederholung
 
Das selbe Spielchen würde mit einer TStringList auch funktionieren, wenn man Delphi-Referenz durchsuchenAddObject verwendest :mrgreen:

Blup 17. Feb 2009 11:51

Re: Zufallszahl ziehen ohne wiederholung
 
Ein Variante von mir, schafft auf meinem Rechner 3 Millionen Ziehungen von 6 aus 49 pro Sekunde.
Delphi-Quellcode:
const
  CRLF = #13#10;

function Lottozahlen(Min, Max: Integer; Count: Word): TIntegerDynArray;
{---}
  procedure Swap(var Value1, Value2: Integer); inline;
  var
    Temp: Integer;
  begin
    Temp  := Value1;
    Value1 := Value2;
    Value2 := Temp;
  end;
{---}
var
  diff, i1, i2, r1, r2: Integer;
  SortArray: TIntegerDynArray;
begin
  diff := Max - Min + 1;
  if diff < Count then
    raise Exception.Create(Format('Ungültige Parameter' + CRLF +
                                  'Lottozahlen(%d, %d, %d)',
                                  [Min, Max, Count]));
  SetLength(Result,   Count);
  SetLength(SortArray, Count);
  for i1 := 0 to Count - 1 do
  begin
    r1 := Random(diff) + Min;
    r2 := r1;
    for i2 := 0 to i1 - 1 do
    begin
      if (r1 = r2) and (SortArray[i2] <= r2) then
      begin
        Inc(r1);
        Inc(r2);
      end
      else
        Swap(r2, SortArray[i2]);
    end;
    Result[i1]   := r1;
    SortArray[i1] := r2;
    Dec(diff);
  end;
end;
Wenn die Ergebnisse sortiert sein dürften, sähe das noch etwas einfacher aus.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:14 Uhr.
Seite 2 von 2     12   

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