Entwickler-Ecke

WinForms - Zufallszahlen ohne Wiederholung


Adabei - So 24.08.14 20:30
Titel: Zufallszahlen ohne Wiederholung
Hallo nochmals. :wink2:

Schreibe gerade eine einfaches Programm, welches Zufallszahlen generieren soll(nicht mehr aber auch nicht weniger).

Das mit den Zufallszahlen habe ich ohne weiteres zusammengebracht(war auch teilweise im meinem Buch beschrieben), da aber einige Zahlen immer doppelt vorkommen, wollte ich wissen, wie es denn möglich ist, zufallszahlen ohne Wiederholung in einer lb. auszugeben?

Momentan sieht der Code so aus(halt manchmal noch immer mit Wiederholung...)


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
                Random r = new Random();

                int[] Zufall = new int[100];

                m_lbAusgabe.Items.Clear();

                for (int i = 0; i < 100; i++)
                {
                    Zufall[i] = r.Next(1500);
                    m_lbAusgabe.Items.Add(Zufall[i]);
                }


da ich mich hier schon mal etwas eingelesen habe in die beiträge von lottozahlen generieren(da es ja ähnlich ist nur mit anderen zahlen) habe ich gesehen, dass man in die for-schleife noch eine for-schleife reinpacken soll, aber wie soll denn das gehen?

grüße


jaenicke - So 24.08.14 20:40

Du kannst natürlich mit einer zweiten Schleife nachschauen, ob die Zahl schon enthalten ist und sie dann nicht nutzen.

Viel sinnvoller ist aber eine Liste der Zahlen zu benutzen, in deren Range die Zufallszahlen liegen sollen, und diese einfach zu schütteln, sprich zufällig zu sortieren, siehe Fisher-Yates:
http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Wenn du jetzt die ersten 100 dieser Zahlen benutzt, sind das 100 zufällige Zahlen aus diesem Bereich.


Ralf Jansen - So 24.08.14 20:58

Zitat:
da ich mich hier schon mal etwas eingelesen habe in die beiträge von lottozahlen generieren(da es ja ähnlich ist nur mit anderen zahlen) habe ich gesehen, dass man in die for-schleife noch eine for-schleife reinpacken soll, aber wie soll denn das gehen?


Wenn du es wie bei den Lotozahlen willst dann mach es doch genauso wie bei den Lottozahlen ;)

a.) Ein Liste mit den möglichen Zahlen erstellen
b.) Ein zufällig auswählen aus der Liste aus a. entfernen und irgendwo eintragen
c.) So oft b.) wiederholen bis man die gewünschte Menge hat


Adabei - Mo 25.08.14 10:41

das mit den Listen ist natürlich eine Möglichkeit, leider kenne ich mich damit zu wenig aus und würde es lieber mit 2 for-schelifen probieren. Nur verstehe ich leider auch diese Möglichkeit nicht ganz, ich meine von der theorie her schon, denn die zweite for-schleife muss einfach prüfen, ob die zahl schon mal vorgekommen ist, aber wie dass geht verstehe ich leider nicht


Th69 - Mo 25.08.14 10:49

Hallo Adabei,

unter C# Zufallszahlengenerator am Beispiel Lotto [http://www.itfacts.org/2011/11/c-zufallszahlengenerator-am-beispiel-lotto/] findest du entsprechenden Code, der dir weiterhelfen sollte.
Aber eigentlich solltest du so etwas simples selber hinkriegen...


Ralf Jansen - Mo 25.08.14 10:59

Du mußt prüfen ob das was du gewürfelt hast schon in m_lbAusgabe.Items ist. Dazu hat die Collection eine Contains [http://msdn.microsoft.com/de-de/library/system.windows.forms.listbox.objectcollection.contains.aspx] Methode. Dann nur etwas hinzufügen wenn es noch nicht enthalten ist.
Eine 2.te Schleife brauchst du nicht. Die die du hast muß nur solange laufen bis die benötigte Menge in m_lbAusgabe.Items ist.
Also aus der "mache etwas 100 mal"-Schleife muß eine "bis 100 Elemente in m_lbAusgabe.Items sind"-Schleife werden.

Sei dir aber bewußt das du so keine saubere Abbruchbedingung hast. Wie oft die Schleife durchlaufen wird ist nicht vorhersagbar und (kann zumindest theoretisch lange oder nahezu unendlich dauern).


Adabei - Mo 25.08.14 13:00

ich glaube ich habe die Frage so gestellt, dass sie falsch ankommt(oder vl. beudetet die Frage auch was falsches :nixweiss:), tut mir leid für meine schlechte ausdrucksweise :flehan:

ich möchte wenn der Benutzer auf den Button klickt, dass 1 Zufallszahl gemacht wird, und die dann in der l-Box ausgegeben wird, wenn er dann wieder draufklickt, dass dann wieder 1 Zufallszahl kommt, die halt nicht die gleiche sein soll, wie die eine, die schon mal vorkommt.

Danke für den Link, dort ist es wirklick schritt für schritt beschrieben, aber ich denke ich habe zuerst die Frage falsch gestellt und zur besseren Frage(hoffentlich) passt dann der Link nicht mehr;

ok, also keine 2. Schleife, dass habe ich jetzt schon geschnallt :D


C# - Mo 25.08.14 13:25

Also wenn ich dich richtig verstanden habe, willst du so ewtas in der Art machen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
List<int> rndNums = new List<int>();
for (int i = 1; i < 500; i++) rndNums.Add(i); // Liste erstellen, die deinem Zufallszahlenbereich entspricht.
Random rnd = new Random(); // Random generator erzeugen
int[] Zufall = rndNums.Sort((x1, x2) => rnd.Next(-12)).Take(100).ToArray(); // Zahlenliste durcheinander würfeln, anschließend die ersten 100 Zahlen nehmen und sie in ein Array umwandeln

foreach(int zahl in Zufall) m_lbAusgabe.Items.Add(zahl); // Zahlen durchgehen und in die Ausgabeliste eintragen
//oder, falls das geht (hab jetzt nicht nachgeschaut):
m_lbAusgabe.Items.AddRange(Zufall);


Adabei - Mo 25.08.14 15:12

nein so mein ich dass auch nicht, also ich glaube dass es so geht(habe ich mithilfe der link-anleitung gemacht):


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
for (int i = 0; i <= 1; i++)//da ich ja nicht 6 zahlen wie beim lotto benötige sondern nur 1
{
zahl = rnd.Next(1100);//zufallszahl zwischen 1 und 100
if (!myList.Contains(zahl)) 
{
myList.Add(zahl); 
}
else  
{
i--;
}
}


nach dieser variante ist diese 1 Zufallszahl in der Liste myList gespeichert. diese 1 Zufallsvariable brauche ich dann außerhalb dieser schleife als int. Da sie aber in einer Liste gespeichert ist funktioniert dass nicht.
bei einer zufallsvariable glaube ich dass ich sowieso keine schleife benötige oder?, weil ich brauch sie ja nur 1-mal.

edit: Blödsinn, natürlich brauche ich eine Schleife, weil sonst geht es ja nicht von i eins hinunterzuzählen)


Ralf Jansen - Mo 25.08.14 15:34

Zitat:
edit: Blödsinn, natürlich brauche ich eine Schleife, weil sonst geht es ja nicht von i eins hinunterzuzählen)


Bonusaufgabe. Welche Schleife eignet sich hier am besten? (zumindest besser als eine For Schleife)


Adabei - Mo 25.08.14 16:03

eine foreach schleife?


C# - Mo 25.08.14 16:08

Nächster Versuch ;)


Ralf Jansen - Mo 25.08.14 16:13

Tip: Die Schleife soll mindestens 1 mal ausgeführt werden und solange wiederholt werden bis (hier folgt die Schleifenbedingung) die in der schleife gewürfelte Zahl nicht in myList zu finden ist.


Adabei - Mo 25.08.14 16:13

dann wird es die while schleife sein


papa69 - Mo 25.08.14 16:15

@Adabei:

Ich glaub', du siehst den Wald vor lauter Bäumen nicht... wenn du "die" Zahl in der List(e) hast, kannst du sie doch auch auslesen (aus der List(e))


Adabei - Mo 25.08.14 16:22

ich denke auch dass ich gerade voll auf der leitung stehe
ich setze mich morgen nochmal in ruhe vor dem computer und dann werde ich es nochmals
versuchen


C# - Mo 25.08.14 16:39

Also, mal abgesehen von deiner Idee mit der Schleife, guck dir nochmal meine Methode an. Wenn man diese etwas modifiziert brauchst du überhaupt keine Schleife.

C#-Quelltext
1:
2:
List<int> ZufallsZahlen = new List<int>();
for(int i = rndMin; i < rndMax; i++) ZufallsZahlen.Add(i);

Jetzt hast du alle möglichen Zahlen in einer Liste, sodass sie nur einmal vorkommen. rndMin und rndMax ist hier dein Mini- bzw. Maximum der Zufallszahlen.

C#-Quelltext
1:
2:
3:
4:
Random rdm = new Random();
int index = rdm.Next(0, ZufallsZahlen.Count);
myList.Add(ZufallsZahlen[index]);
ZufallsZahlen.RemoveAt(index);

Wenn du dir diesen Codeblock ansiehst, wirst du feststellen, dass hier eine beliebige Zahl aus der ZufallsZahlen-Liste genommen und in myList eingetragen wird. Anschließend wird diese Zahl aus der ZufallsZahlen-Liste gelöscht. Somit kann jede Zahl nur genau 1x vorkommen.


Adabei - Mo 25.08.14 19:55

nun ja, ich habe jetzt in Ruhe darüber nachgedacht, es funktioniert ohne Probleme so wie ihr es mir erklärt habt, danke dafür!!!

jetzt will ich allerdings mit dieser 1 Zufallszahl die ich in der Liste gespeichert habe arbeiten, also konkret will ich 5 dazuzählen;
dazu muss ich diese Zufallszahl in einer Variable speichern, in meinem Fall:

C#-Quelltext
1:
2:
int zufallszahl=//? 
int ergebnis=zufallszahl+5;


die zufallszahl ist in der Liste myList gespeichert.
Wie kann ich nun die Zufallszahl von der Liste myList in die neu erstellte int Variable zufallszahl rüberbringen?

zur info: myList habe ich als globale Variable erstellt(also auch außerhalb der for-schleife zu verwenden);


C# - Mo 25.08.14 20:02

Zeig mal deinen Code den du jetzt verwendest. Nach welcher Methode gehst du nun vor?
Wenn du die Zahl weiterverwenden willst, musst du sie wieder aus der Liste auslesen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
int zahl = myList[myList.Count - 1];

int ergebnis = zahl + 5;
// du kannst aber auch einfach die Zahl direkt erhöhen, also
zahl = zahl + 5;
// oder gekürzt
zahl += 5;


Palladin007 - Mo 25.08.14 20:02

So wie du sie auch in die Add-Methode bekommst. (Ich gehe von dem Beispiel von C# aus)
Schau dir dazu mal das Stichwort Indexer an, List hat so einen.


C#-Quelltext
1:
var zufallszahl = ZufallsZahlen[index];                    


Das dann an die Add-Methode übergeben und anschließend weiter verarbeiten.


PS:
C#, dein Name irritiert mich immer und immer wieder :D


Adabei - Mo 25.08.14 20:11


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
for (int i = 0; i < 1; i++)
            {
                int zahl = r.Next(153);
                if (!myList.Contains(zahl))
                {
                    myList.Add(zahl);
                }
                else
                {
                    i--;
                }
            }


Diese Methode verwende ich jetzt;


C# - Mo 25.08.14 20:24

Dann definiere deine Variable einfach außerhalb der for-Schleife.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
int zahl;
for (int i = 0; i < 1; i++)
    {
       zahl = r.Next(153);
       if (!myList.Contains(zahl)) myList.Add(zahl);
       else i--;
    }
int ergebnis = zahl + 5;

Aber guck dir mal meine Variante an. Dafür brauchst du keine (theoretische) Endlosschleife. Und eine For-Schleife ist hier nicht sehr sinnvoll. Überlege dir nochmal wie eine alternative Lösung aussehen könnte.

P.S.: Palladin wieso :D ?


Palladin007 - Mo 25.08.14 20:36

Tipp:

Schau dir nochmal den dritten Beitrag von Ralf an.
Da steht im Prinzip genau das drin, was du brauchst und die wahrscheinlich einzig sinnvolle, bzw. die beste Herangehensweise.


Weil die Sprache so heißt, das ist irgendwie kompisch wenn ich auf einen Beitrag von dir verweise :D


Adabei - Di 26.08.14 11:03

user profile iconC# hat folgendes geschrieben Zum zitierten Posting springen:
Dann definiere deine Variable einfach außerhalb der for-Schleife.

Aber guck dir mal meine Variante an. Dafür brauchst du keine (theoretische) Endlosschleife. Und eine For-Schleife ist hier nicht sehr sinnvoll. Überlege dir nochmal wie eine alternative Lösung aussehen könnte.


Also dass habe ich auch schon gemacht, dass ich einfach die variablöe zahl als globale variable erstellt habe, aber wenn ich dass so mache, dann kommen die zahlen manchmal immer noch doppelt

deine variante ist zwar sehr gut, ist aber auch eine for-schleife drinnen;

@Palladin: ja werde ich machen


Palladin007 - Di 26.08.14 11:12

Ohne Schleife gehts nicht, man sollte aber auch die richtige Schleifen-Form für sein Vorhaben nutzen. Performance-Unterschiede gibt es auch kaum.
Grundsätzlich geht jedes Vorhaben auch in jeder Schleife, die frage ist nur, wie übersichtlich und angenehm zum lesen das ist.


Adabei - Di 26.08.14 11:26

naja ich denke performence unterschiede gibt es bei 100 Zahlen nicht, und wenn dass das problem ist nehme ich halt nur 50.

aber ich probiere es nochmal und wenn ich keine lösung habe, dann bitte ich euch, mir das nochmals zu erklären, und sonst poste ich die lösung selbstverständlich


C# - Di 26.08.14 12:18

@Adabei
Meine Schleife läuft aber genau 100x durch. Deine Schleife könnte theoretisch unendlich oft durchlaufen - wie Ralf Jansen es bereits gesagt hat.
Noch eine Frage: was verstehst du unter "global"? Wenn die Zahl außerhalb der Schleife, aber innerhalb der Methode definiert wird, ist sie NICHT global.

Und wegen Performance musst du dir bei solchen Sachen wirklich noch keine Gedanken machen (außer dass deine Schleife endlos laufen könnte :mrgreen:)

@Palladin: meinst du sowas :mrgreen::
Zitat:
user profile iconC# hat folgendes geschrieben:

C#-Quelltext
1:
                    


Adabei - Di 26.08.14 12:22

so, ich denke ich habe die Lösung gefunden:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
int a = r.Next(1100);
myList.Add(a);
if (!myList.Contains(a))
{
myList.Add(a);
}
else
{
a--;
}


und ich denke dass ich nach diesem Ausdruck mit a weiterarbeiten kann.

ich dachte globale Variable ist es, wenn ich so mache(also dass schreibe ich immer oberhalb vom konstruktor):

C#-Quelltext
1:
private string alles;                    


und nachher kann ich immer schreiben:

alles=.... und brauche nicht immer string alles in jeder methode deklarieren


Palladin007 - Di 26.08.14 12:32

Lass sie 1000 mal durch laufen und du kriegst es kaum mit.
Das sind DImensionen, da langweilt sich ein moderner PC einfach nur.


Globale Variablen werden außerhalb der Methoden in der Klasse deklariert.
Lokale Variablen liegen in der Methode und sind außerhalb nicht zugänglich.
Du solltest dir immer überlegen, wo du eine Variable brauchst und ob ein Parameter nicht sinnvoller ist, als eine globale Variable.


Was deinen Code angeht:
Was genau tut das?
Es erstellt eine Zufallszahl von 1 bis 100 und legt diese Zahl in myList ab.
Dann prüfst du, ob diese Zahl nicht drin steht (was definitiv nie der Fall sein wird) und fügst sie erneut hinzu, ansonsten wird 1 von der Zahl abgezogen.
Wo ist da der Sinn?

Du möchtest eine Reihe Zufallszahlen haben, die alle garantiert unterschiedlich sind.
Oder anders formuliert: Du hast eine Liste Zahlen und möchtest dort zufällig Zahlen raus holen, ohne zurück legen.
Die meiner Meinung nach beste (und vermutlich auch die schnellste) Herangehensweise hat Ralf oben genannt, warum versuchst du nicht, das umzusetzen?
C# hat dir den Code dafür gezeigt, du musst ihn nur noch anpassen (für mehrere Zahlen) und nutzen.



@C#:

Genau das und dieses "@C#" auch :D


Adabei - Di 26.08.14 12:56

ok, habe ich mir fast gedacht...

ja, dass weiß ich eh;

ja, diesen Code habe ich auch schon mal probiert, verstehe ich auch was er macht, aber das funktioniert auch nicht, es kommen auch gleiche zahlen :nixweiss:


Palladin007 - Di 26.08.14 13:06

Welcher Code?


Adabei - Di 26.08.14 14:51

den Code von C# habe ich genommen und mit meinen Zahlen und Varibalen ausgefüllt


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
            List<int> zufallszahlen = new List<int>();
            for (int i = 1; i < 100; i++)
            {
                zufallszahlen.Add(i);
            }
            int a = r.Next(1,zufallszahlen.Count);
            myList.Add(zufallszahlen[a]);
            zufallszahlen.RemoveAt(a);
            int b=5;
            int ergebnis=a+b;


Palladin007 - Di 26.08.14 15:23

Wie sollen da gleiche Zahlen kommen?
Du holst doch nur eine raus, nicht mehrere.

Oder hast du den ganzen Code in eine Schleife gepackt, die dann so oft durch läuft, wie du Zahlen haben willst?


jaenicke - Di 26.08.14 15:24

Die Liste musst du dir natürlich merken und nicht bei jedem Anfordern einer Zufallszahl neu erzeugen...


Adabei - Di 26.08.14 15:28

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Die Liste musst du dir natürlich merken und nicht bei jedem Anfordern einer Zufallszahl neu erzeugen...


ja genau dass ist das Problem;

nein habe ich nciht in eine schleife gepackt, aber ich hab ja immer 1 Zahl zwischen 1-100 wenn ich auf dem Button klicke, und wenn ich dann nochmal auf den Button klicke durchläuft er diesen Code nochmals, also wird es neu erstellt(und dass darf wie janicke sagt naütlrich nicht sein)


Palladin007 - Di 26.08.14 15:44

Achso, ich bin davon aus gegangen, dass du dir die Zahlen erst einmal alle sammelst und dann auf einmal aus gibst.
Dann ließe sich das in einer Methode ohne globale Liste regeln.

Der Grund, weshalb das nicht sein darf, ist der, dass die Liste dir ja das Spektrum der Zahlen vor gibt, also der Topf, wo du die Zahlen heraus nimmst.
Wie der Topf, aus dem Lose bei irgendeinem Gewinnspiel heraus genommen werden.
Wenn du diesen Topf bei jeder neuen Ziehung erst einmal neu befüllst, ist natürlich klar, dass auch vorher schon einmal entfernte Zahlen wieder gezogen werden können.


C# - Di 26.08.14 15:54

Wenn du mein Beispiel verwendest, musst du die Liste zufallszahlen global definieren. Die For-Schleife in meinem Beispiel packst du in den Konstruktor und gut ist. Dann sollte es laufen.


Adabei - Di 26.08.14 16:01

danke funktioniert!


Svas99 - Di 13.01.15 18:23

Hallo alle miteinander :wave:
also zur info vorneweg mal, ich bin neu hier, also wenn ich schon am anfang einen blödsinn mache einfach direkt sagen bitte.
ich dachte mir, ich führe diesen thread weiter und öffne keinen neuen, da meine frage im prinzip die gleiche ist wie jene von diesem Thread.
und zwar habe ich mal gleich wie ihr es ihm gesagt habt, auch etwas programmiert:

C#-Quelltext
1:
2:
3:
4:
for (int i = 0; i < 50; i++)
            {
                zufallszahlen.Add(i);
            }

Das, was Ihr da oben seht, habe ich wie C# meinte in den Konstruktor reinprogrammiert, dass verstehe ich ja auch noch es erzeugt eine liste namens zufallszahlen mit 50 zahlen.
So nun habe ich einen button, namnes nächste zufallszahl, wo folgendes von mir reinprogrammiert wurde:

C#-Quelltext
1:
2:
3:
int zufallszahl = r.Next(1,zufallszahlen.Count);
            meineListe.Add(zufallszahlen[zufallszahl]);
            zufallszahlen.RemoveAt(zufallszahl);

Also ich verstehe diese Zeilen eig. ja auch noch. Es wird eine Zufallszahl erstellt zwischen 1 und 50(weil die liste ja 50 lang ist) und diese wird dann in der integer Variable zufallszahl gespeichert. In der zweiten Zeile dann wird in die Liste meineListe die zufallszahl reingespeichert. Und am Ende wird der Liste zufallszahlen diese Zahl gelöscht.
Nun zur meine Frage, im Prinzip die gleiche oder ähnliche wie die von Adabi, was muss ich tun, damit diese Zahl nicht noch einmal vorkommt?Wenn ich auf den Button nächste zufallszahl klicke, dann kommen einige andere Zahlen aber es sind doch noch immer viele identisch. Wieso funktionierts bei ihm und bei mir nicht?
glg


Palladin007 - Di 13.01.15 19:38

Vorweg: Ich habe überhaupt keinen Plan mehr, was wir vorher geschrieben haben und Lust, das zu lesen, habe ich auch keine :D
Das ist der Nachteil, wenn alte Threads weiter geführt werden, ich denke daher, ein neuer Thread ist besser, wenn du die Infos aus anderen Quellen, auf die du dich beziehst, erklärst.


Zum Code:
Bedenke, dass der Computer immer bei 0 anfängt zu zählen, nie bei 1. Es gibt A-Programmiersprachen, die umgehen das, damit der Programmierer bei 1 anfangen kann, die breite Masse hält sich aber an die Konvention: Zählbeginn ist 0.
Soll heißen: Eine Liste mit 50 Items hat einen Index-Bereich von 0 bis 49.
Dass das bei dir keine IndexOutOfRangeException geworfen hat, war reines Glück.

Was dein Zufallszahlenproblem angeht, kann ich keinen Fehler entdecken.
Da kann keine Zahl doppelt auftreten, da jede ausgewählte Zahl auch direkt aus der Quell-Liste entfernt wird.
Eventuell hast du einen Fehler wo anders, z.B. beim Anzeigen der Zahlen?
Vielleicht hast du aber auch den Fehler gemacht, den jaenicke anspricht:
Zitat:
Die Liste musst du dir natürlich merken und nicht bei jedem Anfordern einer Zufallszahl neu erzeugen...



Allgemein noch zwei Tipps:
1.
Überdenke deine Namen genau. Die Variable "zufallszahl" ist zwar eine Zufallszahl, in deinem Kontext verwendest du sie aber nicht als Solche, sondern als zufälligen Index. Eventuell reicht auch einfach nur "index", weil die Herkunft eigentlich ziemlich egal für den Rest des Codes ist.
Die Liste "zufallszahlen" ist auch keine Liste mit Zufallszahlen, sondern nur einfachen Zahlen, die ursprünglich nur hoch gezählt wurden. Außerdem (so wie ich es verstanden habe) verwendest du sie nicht als die Liste, wo deine Zufallszahlen drin sind, diese AUfgabe übernimmt "meineListe", oder? Also müsste "MeineListe" deine Zufallsliste sein. "zufallszahlen" dagegen ist deine Quell-Liste, da du aus ihr die Zahlen auswählst, also würde ich sie auch so benennen. Eventuell ist es noch sinnvoll, im Namen mit einzufassen, dass die Zahlen in der Liste eindeutig sein müssen. Beispiel (auf Englisch):
"r" => "random" (heufig sehe ich auch einfach "rnd", ich bin aber ein Fan von ganzen Wörtern in Variablen)
"meineListe" => "randomNumbers" oder "selectedNumbers", da es ja auch ausgewählte Nummern sind
"zufallszahlen" => "sourceNumbers"
"zufallszahl" => "randomIndex" oder einfach nur "index"
Solche Regeln gestalten es sowohl den Helfern hier als auch dir selber, wenn du später mal deinen Code anschaust, deutlich einfacher, ihn zu verstehen. Ich merke teilweise schon während der Arbeit, dass ich in einzelne Code-Abschnitte ohne gut gewählte Namen nicht den Überblick behalten könnte.


2.
Schau mal hier [http://openbook.rheinwerk-verlag.de/visual_csharp_2012/] vorbei. Ich habe den Vorgänger gelesen, das war ganz gut, erfordert aber Ausdauer, bringt dafür aber auch viel nötiges KnowHow für alles Weitere mit sich. Wenn du auch WinForms lernen willst, in der Version 2008 gibt es dazu aber auch noch Artikel. Besser wäre aber WPF, das kann genauso einfach eventbasiert arbeiten wie WinForms (auch wenn es das Potential um längen nicht ausschöpft), bringt darüber hinaus aber auch eine lange Liste sehr cooler Dinge mit sich.


Svas99 - Di 13.01.15 21:03

danke erstmals für die allgemeinen tipps:
das mit der namenswahl scheint bei vielen anfängern ein problem zu sein wenn ich mir die forenbeiträge so anschaue :D , dafür entschuldige ich mich gleich mal und versuche es bei den nächsten fragen und programmen besser zu machen.
dass mit dem neuen forenbeitrag mache ich das nächste mal auch, danke für den tipp und entschuldigung dass ich hier weitergeschrieben habe, aber ich denke jetzt soll ich gleich hier weitermachen oder?
dass mit der for-schleife habe ich schon geändert und dass er immer bei 0 anfängt zu zähöen wusste ich, wusste aber nicht dass das zu problemen führen kann, danke für den tipp!!
welche Liste sollte ich mir da merken und was meint er damit?
meint er dass ich randomNumbers(also früher meineListe) auch woanders hinpacken sollte und nicht in den button nächste zufallszahl?
zur weiterverarbeitung: es ist nur mehr folgende Codezeile hier(diese Codezeile ist auch im Button nächste zufallszahl):

C#-Quelltext
1:
m_lbausgabezufallszahlen.Items.Add(index.ToString());                    

wo könnte hier also das problem sein?


C# - Di 13.01.15 22:15

Hallo,

@Palladin007

Eine IndexOutOfRange Exception wird er mit Sicherheit nicht bekommen. Random.Next() hat eine exklusive Obergrenze und keine inklusive. Das einzige was er nie bekommen wird, ist die 0.



@Svas99

Probiere es mal so:

Lege dir eine Liste direkt in der Klasse an und nicht in der Methode.

C#-Quelltext
1:
List<int> rndNums = new List<int>();                    


Nun erzeugst du im Konstruktor die Zufallszahlen

C#-Quelltext
1:
2:
3:
for (int i = 0; i < 50; i++) rndNums.Add(i); // Zahlen von 0 bis einschließlich 49 in die Liste schreiben
Random rnd = new Random(); // Random generator erzeugen
rndNums.Sort((x1, x2) => rnd.Next(-12)); // Zahlenliste durcheinander würfeln


Eine Zufallszahl holst du nun mit:

C#-Quelltext
1:
2:
int randomNumber = rndNums[0];
rndNums.RemoveAt(0);


Palladin007 - Mi 14.01.15 00:46

Das mit der Obergrenze hab ich total vergessen - obwohl, ich verwechsle das eigentlich immer, wenn ich es nicht gerade nach lese :D

Dass die Liste in der Methode deklariert war, daran habe ich gar nicht gedacht, ich schäme mich -.-


Allerdings fand ich sein vorheriges System besser, als das "Durchwürfeln".
Zum Einen ist das in meinen Augen so ein gefaketer Zufall, die Zahlen stehen ja schon lange vor der ersten "Ziehung" fest.
Außerdem ist der Gesamt-Aufwand beim Auswählen der Zufallszahlen geringer, das würde beim Umfang des Codes auffallen.

Mein Favorit daher:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
public class Test
{
    private readonly Random _random = new Random();
    private readonly List<int> _numbers = new List<int>();

    public Test()
    {
        ResetSource();
    }

    public void ResetSource()
    {
        _numbers.Clear();
        for (int i = 0; i < 50; i++)
            _numbers.Add(i);
    }

    public int TakeRandomNumber(bool removeNumber)
    {
        var randomIndex = _random.Next(0, _numbers.Count);
        var result = _numbers[randomIndex];

        if (removeNumber)
            _numbers.RemoveAt(randomIndex);

        return result;
    }
}


Ist vermutlich Geschmackssache, ich versuche dabei nur immer, möglichst wenig Code zu produzieren, ohne dass die Übersicht flöten geht. Ob mir das jetzt besser gelungen ist, können vermutlich Dritte besser bestimmen :D



PS @ Svas99:

Ein Random-Objekt nie niemals lokal deklarieren, außer du hast einen sehr guten Grund. Wenn du einen so guten Grund hast, sag mir bitte Bescheid, damit ich dich davon abbringen kann ;)
Grund ist der, dass die Random-Klasse dann am besten sicher stellen kann, dass die Zahlen möglichst nahe an den Zufall heran kommen, wenn es die selbe Instanz ist. Ich würde sogar so weit gegen, global und statisch eine einzige Instanz im ganzen Programm zu nutzen, die sich dann nie ändert.


C# - Mi 14.01.15 12:41

Okay dann hier zum Vergleich meine Version:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
    class Test
    {
        private readonly Random random = new Random();
        private readonly List<int> numbers = new List<int>();

        public Test()
        {
            ResetSource(050);
        }

        public void ResetSource(int minimum, int maximum)
        {
            numbers.Clear();
            for (int i = minimum; i < maximum; i++)
                numbers.Add(i);

            numbers.Sort((n1, n2) => random.Next(-12));
        }

        public int TakeRandomNumber()
        {
            if (numbers.Count == 0throw new Exception("No more numbers in specified range left");
            int num = numbers[0];
            numbers.RemoveAt(0);
            return num;
        }
    }


@Palladin

Ich weiß nicht on dein removeNumber Parameter wirklich Sinn macht, denn wenn du ihn auf false setzt, hast du nur eine umständliche Random.Next()-Funktion gebaut :D


Nachtrag:

Ich habe aus Spaß mal ein Performance Test der beiden Klassen gemacht. Das Resultat:
Bild

Ich gebe mich geschlagen Palladin007 :flehan:

Und hier nochmal mit meiner modifizierten Version (int[], statt List<int> und OrderBy(), statt Sort()):
Bild2

Und hier nochmal mit einer weiteren Modifikation meiner Seits (Zahlen manuell durchwürfeln und nicht mit Sort):
Bild3

Palladin007 bei dir kam bei 100.000.000 Zahlen leider OutOfMemory. Bei mir kam die dann auch bei 1.000.000.000 Einträgen.


Palladin007 - Mi 14.01.15 17:18

Setz mal Capacity auf 100 Millionen ^^
Du wirst fest stellen, die Performance wird besser und vielleicht verhindert das auch die OutOfMemory.
Warum die Exception kommt, weiß ich nicht, kann ich nur spekulieren.


Mein removeNumber-Parameter macht in dieser konkreten Situation vielleicht keinen Sinn, aber wenn dann der Inhalt der Quell-Liste nicht mehr aufgezählte Werte enthält, sondern irgendwelche anderen Werte, dann ist das gar nicht mehr so unpraktisch. Mir ging es dabei aber einfach nur darum, die STochastik zu simulieren, "Ziehen mit zurück legen" und "Ziehen ohne zurück legen".


Was die Performance angeht:

Sort ist aufwendig, da die ganze Liste mehrfach durchlaufen und umgewäzt werden muss.
Daher wird die meiste Zeit vermutlich die ResetSource-Methode brauchen. TakeRandomNumber ist daher vermutlich minnimal schneller, als bei mir, da kein zufälliger Index gewählt werden muss, der Rest aber gleich bleibt.


PS:
Du hast bei 1 Milliarde Zahlen dann auch eine OutOfMemory bekommen, weil das dann grob über den Daumen gerechnet 4 GB im RAM sind, das Maximum liegt aber bei 2 GB. Versuch mal 500 Millionen, bzw. etwas weniger, damit noch Platz für andere Inhalte bleibt.


Ralf Jansen - Mi 14.01.15 17:29

Zitat:
das Maximum liegt aber bei 2 GB.

<gcAllowVeryLargeObjects> Element [http://msdn.microsoft.com/en-us/windows/apps/hh285054.aspx]

Moderiert von user profile iconTh69: URL-Titel hinzugefügt.


Svas99 - Mi 14.01.15 18:26

danke für die vielen antworten!!!
ich habe mich für C#s-Methode entschieden, weil ich es so besser verstehe.
problem gelöst...