Lottozahlen Gen. C

01/25/2013 15:22 Rullx3#1
Hallo , habe ein kleines Problem .
Habe einen Lottozahlen Generator geschrieben der 6 Zahlen random von 1-49 ausgibt und diese aufsteigen Sortiert. Leider bekomme ich es nicht gebacken das er keine Zahlen doppelt ausgibt und mir fällt leider auch keine idee ein wie ich dies umsetzen könnte.
Für Tipps wäre ich sehr Dankbar.!
Code:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main (void){
	int lotto[7];
	int i,x;
	int a,b,c,zz;
	srand(time(NULL));
	for(a=0;a<7;a++){
		zz=rand()%49+1;
		lotto[a]=zz;
	}
	for(i=0;i<7;i++){
		for(c=0;c<7;c++){
			if(lotto[i]<lotto[c]){
			x=lotto[i];
			lotto[i]=lotto[c];
			lotto[c]=x;
			}
		}
	}
	for(b=0;b<7;b++){
	printf("%i ",lotto[b]);
	}
	system("PAUSE");
	return 0;
}
01/25/2013 15:51 nkkk#2
du könntest eine liste mit alle zahlen zwischen 1 und 49 machen, aus der du immer random eine ziehst und diese dann aus der liste entfernst.
hier ist mna ein C# code der das macht hoffe du kannst damit was anfangen(die zahlen werden noch nicht sortiert):
Code:
List<int> numbers = Enumerable.Range(1,49); //(.ToList))
int[] ergebniss = new ergebniss[7];
for(int i = 0; i < 7; i++)
{
int r = rand()%numbers.Count;
ergebniss[i] = numbers[i];
numbers.RemoveAt(r);
}
01/25/2013 16:05 Jeoni#3
Als Alternative zur schon erwähnten Liste (std::vector in C++?), kann man auch checken, ob sich die generierte Zahl schon im Array befindet und wenn das der Fall ist, dann einfach nochmal eine Zahl generieren bzw. solange eine Zahl generieren, bis eine generierte Zahl noch nicht im Array steht (Do-While-Schleife). Doch gerade bei größeren Arrays könnte es hier zu Problemen bzw. Verzögerungen kommen, da das Array beim checken, ob sich die generierte Zahl schon darin befindet, vollständig durchgegangen werden muss.
Trotzdem hier mal der Code für diese Variante:
Code:
	for(a=0;a<7;a++){
		do
		{
			zz=rand()%49+1;
		} while (AlreadyInside(lotto, zz, 7)); // solange neue zahlen generieren, bis eine neue dabei ist
		lotto[a]=zz;
	}
(...)
bool AlreadyInside(int* array, int number, unsigned int arraylength) // prüft ob eine gegebene zahl innerhalb eines arrays ist
{
	for (int i = 0; i < arraylength; i++) // das array durchgehen
	{
		if (array[i] == number) // number ist im array
		{
			return true;
		}
	}

	return false;
}
(vergib mir etwaige Fehler, ich hab nur im epvp-editor gemacht)

Ich hoffe, ich konnte helfen ;)
Jeoni
01/25/2013 16:16 nkkk#4
Quote:
Originally Posted by Jeoni View Post
Als Alternative zur schon erwähnten Liste (std::vector in C++?), kann man auch checken, ob sich die generierte Zahl schon im Array befindet und wenn das der Fall ist, dann einfach nochmal eine Zahl generieren bzw. solange eine Zahl generieren, bis eine generierte Zahl noch nicht im Array steht
Jeoni
ja das kann man in dem fall machen. Ist aber im allgemeinen keine schöne lösung, da wenn man z.B. 999 zahlen aus 1000 ziehen will (unter beachtung der reihenfolge).
muss man z.B. bei der 999 zahl durchschnittlich 500 mal versuchen muss zu ziehen bevor man die eine der beiden zahlen zieht die noch nicht gezogen ist. Da die zufallsgeneratorn oft nicht ganz so gut sind eher mehr. D.h wie schnell der algoritmus ist hängt sehr stak vom zufall ab.
01/25/2013 16:44 snow#5
Ganz primitive Lösung:

Array mit 49 Feldern. Allen Feldern den Wert 0 zuweisen
-> int zahlen[49];
for (int i = 0; i < 49; i++) {
zahlen[i] = 0;
}

Und dann 6x eine random-Zahl auswerfen:

for (int i = 0; i < 6; i++) {
int random = rand() %49; // somit sind die Zahlen von 0 - 48, also unser Array abgedeckt
if (zahlen[random] == 0)
zahlen[random] = random + 1; // der Zahl am n. Index den Wert n + 1 zuweisen (zahlen[9] = 10 z.B.)
else
--i; // wenn die Zahl noch nicht gesetzt wurde, Zahl setzen, ansonsten i um eins reduzieren, damit es noch einen Durchlauf macht.
}

Und jetzt kannst du die Lottozahlen ausgeben lassen & sie sind sortiert:

for (int i = 0; i < 49; i++) {

if (zahlen[i] != 0)
printf("Lottozahl: %d\n", zahlen[i]);
}

Wir mussten so eine Aufgabe für die Vorlesung machen und ich hab es auch über Sortieren und Durchläufe und was weiß ich gelöst, aber die Lösung hier oben stammt von einem Kommilitonen - finde ich ziemlich gut.
01/25/2013 18:27 Tasiro#6
Du könntest ein Feld der möglichen Werte erstellen und dann das gezogene Element entfernen, wie nkkk vorschlug. Dabei könntest du das Element entfernen, indem du das Element mit dem gezogenen Index durch das letzte Element der Liste ersetzt und die "Länge" des Feldes dekrementierst. Damit ist das Feld danach zwar nicht mehr sortiert, aber das war ja auch nicht gefordert.
In C könnte das etwa so aussehen:
Code:
const int Länge = 49, Anzahl = 7;
int Feld [Länge];
int Ergebnis [Anzahl];
int gezogen, i = 0, Länge_übrig = Länge;

//das Feld füllen und undefiniertes Verhalten vermeiden
while (i < Länge) {
	Feld [i] = i + 1;
	++i;
}

//die Zahlen auswählen
for (i = 0; i < Anzahl; ++i) {
	gezogen = rand () % Länge_übrig;
	Ergebnis [i] = Feld [gezogen];
	--Länge_übrig;
	Feld [gezogen] = Feld [Länge_übrig];
}
Es wäre auch ganz sinnvoll, nicht Bubblesort zum Sortieren zu nutzen, wenn es mal viele Elemente werden sollten, die sortiert werden sollen.
01/29/2013 00:55 Raz9r#7
Hier sind noch einmal zwei vollständige Lottozahlen-Generatoren: Einer in C ("lottery_c") und einer in C++ ("lottery_cpp").

Code:
// cpp-version
#include <iostream>
#include <random>
#include <set>

// c-version
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// cpp-version
void lottery_cpp();

// c-version
void lottery_c();
void quicksort_c(int *data, int count);

int main(int argc, const char *argv[]) {
	// Test it!
	lottery_cpp();
	lottery_c();
	
	return 0;
}

void lottery_cpp() {
	// Using-Direktiven
	using std::cout; using std::endl;
	using std::size_t; using std::set; using std::less;
	using std::mt19937;	using std::random_device;
	using std::uniform_int_distribution;
	
	// Einstellung für 7 aus 49
	constexpr size_t lottery_numbers_to_draw = 7;
	constexpr size_t lottery_numbers_range_min = 1;
	constexpr size_t lottery_numbers_range_max = 49;
	
	// Ein std::set<T> mit der Sortierung std::less<T> und T = std::size_t für
	// die Lottozahlen.
	set<size_t, less<size_t>> lottery_numbers;
	
	// Ein simpler Zufallsgenerator für Integer-Werte im gewählten Wertebereich
	// und ein Lambda-Ausdruck, der einen solchen Wert berechnet.
	mt19937 random_generator({ random_device()() });
	uniform_int_distribution<> lottery_distribution(lottery_numbers_range_min,
													lottery_numbers_range_max);
	auto draw_lottery_number = [&random_generator, &lottery_distribution] {
		return lottery_distribution(random_generator);
	};
	
	// Wir fügen solange zufällige Werte hinzu, bis wir die gewollte Anzahl an
	// Werten im std::set lottery_numbers haben. std::set().insert().second ist
	// true, wenn der Wert erfolgreich eingefügt wurde.
	for (size_t draw_count = 0;
		 draw_count < lottery_numbers_to_draw;
		 ++draw_count) {
		while (!lottery_numbers.insert(draw_lottery_number()).second) {
		}
	}
		
	// Gibt die Lottozahlen auf der Konsole aus
	for (auto n: lottery_numbers) {
		cout << n << ' ';
	}
	cout << endl;
}

void lottery_c() {
	// Initialisierung von rand()
	srand((unsigned)time(0));
	
	// Array, in dem die Zahlen gespeichert und dann "In-Place" sortiert werden
	int lottery_numbers[7];
	
	// Berechnet zufällige Werte zwischen aus [1,49], wenn diese noch nicht in
	// lottery_numbers vorhanden sind, werden sie hinzugefügt.
	// Am Ende der Schleife ist garantiert, dass lottery_numbers 7 verschiedene
	// Elemente enthält.
	for (int draw_count = 0; draw_count < 7; ++draw_count) {
		bool already_drawn;
		int drawn_number;
		do {
			already_drawn = false;
			drawn_number = rand() % 49 + 1;
			for (int i = 0; i < 7; ++i) {
				if (lottery_numbers[i] == drawn_number) {
					already_drawn = true;
					break;
				}
			}
		} while (already_drawn == true);
		
		lottery_numbers[draw_count] = drawn_number;
	}
	
	// Sortiert die 7 Werte in lottery_numbers aufsteigend mit einem
	// "In-Place"-Quicksort Verfahren (Implementation siehe unten)
	quicksort_c(lottery_numbers, 7);
	
	// Gibt die Lottozahlen auf der Konsole aus
	for (int i = 0; i < 7; ++i) {
		printf("%d ", lottery_numbers[i]);
	}
	printf("\n");
}

// In-Place-Quicksort, rekursiv
// Funktionsweise: http://en.wikipedia.org/wiki/Quicksort
void quicksort_c(int *data, int count) {
    if (count < 2) {
        return;
	}
	
    int pivot = data[count / 2];
	
    int *left = data;
    int *right = data + count - 1;

    while (left <= right) {
        if (*left < pivot) {
            left++;
            continue;
        }
        
		if (*right > pivot) {
            right--;
            continue;
        }
		
        int temp = *left;
        *left++ = *right;
        *right-- = temp;
    }
	
    quicksort_c(data, (int)(right - data + 1));
    quicksort_c(left, (int)(data + count - left));
}
Ein möglicher Output wäre:
Code:
21 26 27 36 37 43 46 
6 28 32 35 37 42 45
Offensichtlicher Nachteil der C-Variante: Bei größeren Arrays wird es mit Komplexität O(n) langsamer zu überprüfen, ob bereits Werte existieren. Das kann jedoch elegant umgangen werden, wenn man die von snow911 genannte Methode verwendet. Die wird übrigens dann ineffizient, wenn man wenige Elemente aus einer großen Range aussuchen soll. Bestenfalls benutzt man diese also nicht, wenn die Range groß ist, sondern wenn die gesuchte Anzahl an Elementen nicht sehr viel kleiner als die Range ist.

Bei der C++-Variante kann man anstelle des Lambdas auch std::bind benutzen, das mag clang++ aber manchmal nicht (Ist nicht so ganz fehlerfrei im aktuellen Xcode Dev Preview):
Code:
auto draw_lottery_number = std::bind(std::uniform_int_distribution<>{1, 49}, std::mt19937{std::random_device{}()});
Die weitere Optimierung überlässt man dann std::set. Übrigens kann man hier auch anders sortieren, man ist ja nicht an std::less<T> gebunden.
02/09/2013 00:18 Hacker41822#8
hat mir auch weitergeholfen ^^ danke....
02/09/2013 00:29 Rullx3#9
Habe es so gelöst falls es jemanden interessiert :)
Code:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main (void){
	int lotto[7];
	int i,j,x,temp;
	int a,b,c,zz;
	srand(time(NULL));
	for(i=0;i<7;i++){
		lotto[i]=rand()%49+1;
		for(j=0;j<=i-1;j++){
			if(lotto[j]==lotto[i]){
				j=i;
				i--;
			}
		}
	}
	for(i=0;i<7;i++){
		for(c=0;c<7;c++){
			if(lotto[i]<lotto[c]){
			x=lotto[i];
			lotto[i]=lotto[c];
			lotto[c]=x;
			}
		}
	}
	for(b=0;b<7;b++){
	printf("Zahl %i = %i\n ",b+1,lotto[b]);
	}
	system("PAUSE");
	return 0;
}
02/09/2013 01:31 Schlüsselbein#10
Sieht beim Überfliegen gut aus, nur lass das system("PAUSE") weg. Das ist Pfui!
02/09/2013 16:53 Rullx3#11
Quote:
Originally Posted by Schlüsselbein View Post
Sieht beim Überfliegen gut aus, nur lass das system("PAUSE") weg. Das ist Pfui!
Nein ist es nicht sonst schließt sich die Konsole bei mir immer direkt nach dem öffnen des pause verhindert dies :)
02/09/2013 17:23 Dr. Coxxy#12
Quote:
Originally Posted by Rullx3 View Post
Nein ist es nicht sonst schließt sich die Konsole bei mir immer direkt nach dem öffnen des pause verhindert dies :)
nope, er meint, dass system("pause"); bzw. allg. system "böse" wäre.
das stimmt zwar zu einem gewissen grad, ist aber vollkommen irrelevant für dieses programm.
02/09/2013 17:35 Schlüsselbein#13
Genauso wie es für viele Programme irrelevant ist, Speicher wieder frei zu geben etc. Von der Relevanz abgesehen ist es vorteilhaft, wenn ein Anfänger auch zu Beginn lernt, sauber zu programmieren. Und dazu gehört m.E. kein system("PAUSE").

Quote:
Nein ist es nicht sonst schließt sich die Konsole bei mir immer direkt nach dem öffnen des pause verhindert dies
Je nach IDE kannst du die Konsole auch nach Beendigung des Programms offenthalten. Beim Visual Studio gehts mit STRG+F5.