[INCLUDE] Unique Random Number

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    Es gibt Neuigkeiten! Ab sofort könnt ihr dem Donators Club auf Lebenszeit beitreten.
    Weitere Infos im Thema Donator's Club ab heute wieder verfügbar!

    • [INCLUDE] Unique Random Number

      Hallo Brotfische,

      ich bin es mal wieder *nerv nerv* mit einem neuen Include.
      Inspiriert durch diesen Thread: Kennzeichen System / Polizei KFZ Akte

      Es geht, wie der Titel es schon vermuten lässt um "Eindeutig zufällige Zahlen".

      Aber was heißt das eigentlich?
      Nun, wenn man z.B. die Funktion random(n) verwendet, dann erhält man zufällige Zahlen im Intervall von [0, n-1]. (Also zum Beispiel random(100) gibt uns die Zahlen von 0-99.)
      So, verwenden wir diese Funktion jetzt noch einmal, dann erhalten wir mit Pech, die Selbe Zahl wie zuvor.
      Das ist natürlich ärgerlich, wenn man jetzt Telefonnummern vergibt oder eindeutige Kennzeichen haben will.

      Naive Lösung?
      Eine naive Lösung wäre jetzt, eine Zahl zu generieren und dann zu speichern.
      Anschließend alle Nachfolgenden generierten Zahlen durch die, die schon erstellt wurden zu iterieren und zu schauen, ob es bereits schon so eine Zahl gibt.
      Falls dem so ist, eine neue generieren. Ansonsten hat man ja seine eindeutige Zahl gefunden.
      Problem: Der Aufwand wird immer größer (je mehr Zahlen man generiert hat) und das will man gerade im Laufenden betrieb natürlich nicht. (O(n^x) n=Wie viele Zahlen es bereits gibt. x=Wie viele Versuche benötigt werden, eine zu finden, die noch nich existiert.)

      Meine Lösung?
      Es ist einfacher, einen Zahlen Pool abzubilden.
      Sprich man will eine zufällige Zahl von 0-1000, dann speichert man einfach alle 1000 Zahlen in einer Datenbank.
      Anschließend wird einfach eine zufällig ausgewählt und aus der Liste gelöscht (das heißt, je mehr verwendet werden, desto kleiner wird der Speicherplatz bedarf.)
      Das bedeutet, egal wie viele Zahlen man hat, es ist immer gleich schnell (O(1)).

      Das Gute hier ist noch, dass man Multi-Threaded querys nutzen kann (nur in MySQL, nicht SQLite), dann wird das eh ausgelagert alles und beeinflusst 0 den Server.

      MySQL VS SQLite
      Ich habe 2 Versionen geschrieben.
      Definitiv zu präferieren wäre hier die MySQL Version (aber für die, die kein MySQL haben und das benötigen, habe ich eine SQLite Version geschrieben).

      MySQL
      Wie oben schon erwähnt, können wir hier Multi-Threaded Querys nutzen und das Asynchron gestalten.
      Somit läuft hier alles sehr schnell und glatt.

      SQLite
      Hier belastet das die Server-Laufzeit. (Gerade der Server-Start, wenn die Pools inistialisiert werden, kann das (je nach Größe der Pools) bis zu einer Minute dauern.)
      Die spätere Generierung dauert ~0.5ms. Also nicht soo viel, aber doch schon etwas, gerade wenn man hintereinander mehrere Zahlen generiert.

      Beispiele
      MySQL Version
      Spoiler anzeigen

      Zuerst kann man sich die Pools anlegen, aus denen man dann die zufälligen Zahlen generiert:

      C-Quellcode

      1. enum //POOLS
      2. {
      3. NUMBER_PLATE_POOL,
      4. SMALL_EXAMPLE_POOL
      5. };


      Anschließend (kann man auch während der Server Laufzeit machen, allerdings sollte man dann etwas Zeit einplanen, bis man eine weitere Zahl generiert, da das asynchron abläuft belastet es den Server nicht, dauert trotzdem etwas) kann man die Pools erstellen:

      C-Quellcode

      1. /*
      2. Die Parameter sehen so aus:
      3. CreateRandomPool(MySQL_Handle, POOL, MIN_VALUE, MAX_VALUE);
      4. */
      5. CreateRandomPool(handle, NUMBER_PLATE_POOL, 0, 1000); //Erstellt einen Pool von 0-999
      6. CreateRandomPool(handle, SMALL_EXAMPLE_POOL, 250, 500); //Erstellt einen Pool von 250-499


      So und dann kann man eine zufällige Zahl anfangen zu generieren:

      C-Quellcode

      1. /*
      2. Die Parameter sehen so aus: (Wenn man playerid nicht nutzen will, kann man einfach 0 reinschreiben, ist nicht notwendig anzugeben)
      3. StartGenerateUniqueRandomNumber(playerid, MySQL_Handle, POOL, TOKEN);
      4. */
      5. StartGenerateUniqueRandomNumber(playerid, handle, SMALL_EXAMPLE_POOL, 0);


      Darauf hin, wird ein Callback aufgerufen:

      C-Quellcode

      1. public OnRandomNumberGenerated(playerid, const pool, const token, const status)
      2. {
      3. if(status == -1) return printf("Der Random Pool (%d), ist leer!",pool);
      4. switch(pool)
      5. {
      6. case SMALL_EXAMPLE_POOL:
      7. {
      8. switch(token)
      9. {
      10. case 0:
      11. {
      12. printf("Zufällige Nummer %d wurde aus SMALL_EXAMPLE_POOL generiert.", status);
      13. }
      14. }
      15. }
      16. }
      17. return 1;
      18. }
      Alles anzeigen

      Der TOKEN, dient dazu, zu differenzieren von wo aus der Callback aufgerufen wurde.
      Damit man dann wieder zu Funktionen leiten kann, wo der Code weiter gehen soll.

      SQLite Version
      Spoiler anzeigen

      Zuerst kann man sich die Pools anlegen, aus denen man dann die zufälligen Zahlen generiert:

      C-Quellcode

      1. enum //POOLS
      2. {
      3. NUMBER_PLATE_POOL,
      4. SMALL_EXAMPLE_POOL
      5. };


      Anschließend (Bitte nur in Callbacks beim Server-Start, wie OnGameModeInit. Nicht während der Server läuft.) kann man die Pools erstellen:

      C-Quellcode

      1. /*
      2. Die Parameter sehen so aus:
      3. CreateRandomPool(POOL, MIN_VALUE, MAX_VALUE);
      4. */
      5. CreateRandomPool(NUMBER_PLATE_POOL, 0, 1000); //Erstellt einen Pool von 0-999
      6. CreateRandomPool(SMALL_EXAMPLE_POOL, 250, 500); //Erstellt einen Pool von 250-499


      Und dann kann man einfach direkt eine Zahl generieren lassen:

      C-Quellcode

      1. /*
      2. Die Parameter sehen so aus:
      3. UniqueRandom(POOL);
      4. */
      5. new r = UniqueRandom(SMALL_EXAMPLE_POOL);
      6. printf("Eindeutig zufällige Zahl (%d) wurde generiert.",r);


      Allerdings hier sehen wir, im Gegensatz zu der MySQL Version, läuft das direkt im Code (ohne Callback).
      Das hat den Nachteil, dass dieser Prozess ~0.5ms dauert und nicht asynchron verarbeitet werden kann.


      Was passiert, wenn der Pool leer ist?
      Dann ist der status wie schon gezeigt -1.
      Allerdings, wenn beim Server Restart immer noch das CreateRandomPool steht, wird dieser automatisch wieder aufgefüllt.

      Download

      Wer die MySQL Version verwenden will, da ist die Neuste MySQL Version (R41) nötig!

      MySQL - Version - Links:
      Spoiler anzeigen

      Source-Code: Pastebin

      Direkt-Download: MediaFire


      SQLite - Version - Links:
      Spoiler anzeigen

      Source-Code: Pastebin

      Direkt-Download: MediaFire


      Wichtige Hinweise / Mögliche Errors
      Spoiler anzeigen

      1. Wenn ihr die SQLite-Version in einem Filterscript nutzen wollt, dann bitte #define FILTERSCRIPT ganz oben hin schreiben.
      2. Wenn ihr Errors bekommt wie, function "OnRandomNumberGenerated" is not implemented, das liegt daran, dass ihr in euer Skript die public Funktion OnRandomNumberGenerated schreiben müsst!
      3. Nochmal der Hinweis, bei der SQLite Version kann der Server-Start lange dauern, wenn ein Pool initialisiert werden muss, das ist normal.
      4. Falls MySQL Fehler kommen wie undefined function oder tag mismatch, dann habt ihr die falsche MySQL Version für das Include und müsst diese updaten ;)


      Schlusswort
      Ich hoffe das Include ist für den ein oder anderen nützlich.

      Bei Fragen oder Anregungen, stehe ich euch wie immer gerne zur Verfügung.

      Viel Spaß weiterhin beim skripten :)

      Mit freundlichen Grüßen
      Euer Kalle :P


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/
    • Hybris schrieb:

      Wenn man für die Zufallszahl einen Index erstellt, dann ist der Check auf Duplikate relativ schnell
      Wie willst du das machen?
      Du musst ja trotzdem alle vorherigen Zahlen durchgehen, dein Ansatz ist der naive Ansatz, wie oben erklärt.
      Und wenn du einen Pool von 10.000 Zahlen hast, dann viel Spaß. Dein Array musst du ja auch irgendwie deklarieren und das bezieht auch Speicher.


      Hybris schrieb:

      da es sonst zu viel Speicher frisst.
      Nah jede Zahl frisst nur 4 Bytes.
      Also selbst wenn du einen Pool aus 1.000.000 Zahlen hast, frisst das ((1.000.000*4)/1024) = 3.906,25kB = ~4MB

      Also das ist ein RIESEN Pool, ich wüsste nicht, wofür man so viele eindeutig zufällige Zahlen bräuchte und man benötigt nur 4MB, was jeder Rechner locker zur Verfügung hat, immerhin Reden wir hier nicht von RAM, sondern von Festplattenspeicher.
      Und wenn ich überlege, dass heute sogar Handys Teil schon 1TB haben (Galaxy S10 mit 1TB Speicher) sollte das mit den 4MB kein Problem sein ^^


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/
    • Hybris schrieb:

      Für die Kennzeichen/Telefonnummer würde ich einfach aufsteigende Zahlenfolge nutzen.
      Das freut mich :)

      Hybris schrieb:

      Selbst ein Index ist wahrscheinlich overkill, wenn wir von 1000 Zahlen reden, aber gut.
      Ja, das versuche ich dir ja gerade zu vermitteln.
      Desweiteren kannst du in Pawn nicht mal dynamisch Speicher allozieren, also würde das so gar nicht gehen.
      Schon gar nicht bei mehreren Pools.
      (Ja Memory Plugin außen vor, dennoch hat man dann noch O(n) Zugriffe auf das Array)


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/

    • wo soll ich jetzt mein handle (also db oder) angeben?
      Finde es nicht wo du es erklärst. Ich weiß nicht ob ich blind bin, mache es nicht extra :D

      Habe es ja noch nicht mal benutzt und kriege die Handle fehler.
      Spoiler anzeigen

      Quellcode

      1. public OnRandomNumberGenerated(playerid, const pool, const token, const status)
      2. {
      3. if(status == -1) return printf("![!]! Der Random Pool (%d), ist leer ![!]!",pool);
      4. switch(pool)
      5. {
      6. case NUMBER_PLATE_POOL:
      7. {
      8. switch(token)
      9. {
      10. case 0:
      11. {
      12. printf("Zufällige Nummer %d wurde aus NUMBER_PLATE_POOL generiert.", status);
      13. }
      14. }
      15. }
      16. }
      17. return 1;
      18. }
      Alles anzeigen

    • BrightLeaN schrieb:

      Habe es ja noch nicht mal benutzt und kriege die Handle fehler.
      Der Fehler kommt nur, weil ich im Include auch zufällig den Namen db verwendet habe.

      Nenne das einfach von new MySQL:db zu new MySQL:handle um.

      und Verwende dann in deinem Skript überall handle, anstatt db.


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/
    • Wo steht das?

      Wie sieht denn deine Funktion aus zum Erstellen des Pools?

      //Edit: Wie öffnest du auch die Datenbank? Das ist doch nicht phpmyadmin?

      Es kann auch sein, dass dir nur 1000 angezeigt werden.
      Kannst ja mal SELECT COUNT(*) FROM `t_0` machen, ich wette, da steht dann 8998.


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Kaliber ()

    • Ja richtig
      ich benutzte phpmyadmin nicht.
      Ich benutzte Navicat Lite damit komme ich besser klar.
      Habe jetzt erst gesehen, dass ich mehrere Seiten habe und nur 1000 pro Seite angezeigt werden, sorry :D

      Quellcode

      1. new KZ = StartGenerateUniqueRandomNumber(playerid, handle, NUMBER_PLATE_POOL, 0);

      Quellcode

      1. PCar[playerid][d][P_KZ] = KZ;
      Als Kennzeichen wird mir am Auto 0 gesetzt? In der Datenbank ist sie auch 0

      Aber in der Konsole wurde mir geprintet das eine Zahl generiert worden ist
    • Du hast nicht richtig gelesen.

      Das startet nur die Generierung.

      Du musst dass dann im Callback zuweisen.


      C-Quellcode

      1. //Da du noch d als Parameter hast, musst du den noch übergeben:
      2. SetPVarInt(playerid,"_tmp_veh",d);
      3. StartGenerateUniqueRandomNumber(playerid, handle, NUMBER_PLATE_POOL, 0);
      4. //Im Callback OnRandomNumberGenerated
      5. public OnRandomNumberGenerated(playerid, const pool, const token, const status)
      6. {
      7. if(status == -1) return printf("Der Random Pool (%d), ist leer!",pool);
      8. switch(pool)
      9. {
      10. case SMALL_EXAMPLE_POOL:
      11. {
      12. switch(token)
      13. {
      14. case 0:
      15. {
      16. new d = GetPVarInt(playerid,"_tmp_veh");
      17. DeletePVar(playerid, "_tmp_veh");
      18. PCar[playerid][d][P_KZ] = status;
      19. //Und dann hier eine Funktion aufrufen, wo es weiter gehen soll :)
      20. }
      21. }
      22. }
      23. }
      24. return 1;
      25. }
      Alles anzeigen
      Das ist asynchron, so wie eben ein mysql Query.

      Habe ich ja oben alles erklärt ^^


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/
    • Ja ich weiß aber irgendwie verstehe ich das nicht.
      So ist es ja falsch (habe unten keine Schleife gemacht)

      Oder wie leite ich die Funktion weiter, dass er bei Zeile 9. im ersten Code weiter macht. Habe das noch nie gemacht :d

      Quellcode

      1. {
      2. RemovePlayerFromVehicle(playerid);
      3. new SP = random(sizeof(SAHSpawn));
      4. new FB = random(127);
      5. for(new d = 0; d < sizeof(PCar[]); d++)
      6. {
      7. //Prüfe, ob Fahrzeug existiert. Wenn nicht, nutze den Slot und lege das Fahrzeug an.
      8. SetPVarInt(playerid,"_tmp_veh",d);
      9. StartGenerateUniqueRandomNumber(playerid, handle, NUMBER_PLATE_POOL, 0);
      10. if(PCar[playerid][d][P_MID] == 0)
      11. {
      12. PCar[playerid][d][P_uID] = pInfo[playerid][db_id];
      13. PCar[playerid][d][P_X] = SAHSpawn[SP][0];
      14. PCar[playerid][d][P_Y] = SAHSpawn[SP][1];
      15. PCar[playerid][d][P_Z] = SAHSpawn[SP][2];
      16. PCar[playerid][d][P_R] = SAHSpawn[SP][3];
      17. PCar[playerid][d][P_C1] = FB;
      18. PCar[playerid][d][P_C2] = FB;
      19. PCar[playerid][d][P_MID] = GetVehicleModel(Autohausauto1[i]);
      20. PCar[playerid][d][P_dbID] = pInfo[playerid][db_id];
      21. PCar[playerid][d][P_VID] = CreateVehicle(PCar[playerid][d][P_MID],PCar[playerid][d][P_X] , PCar[playerid][d][P_Y], PCar[playerid][d][P_Z], PCar[playerid][d][P_R], PCar[playerid][d][P_C1], PCar[playerid][d][P_C2], -1);
      22. new query[256];
      23. mysql_format(handle, query, sizeof(query), "INSERT INTO personalcars (P_X, P_Y, P_Z, P_R, P_C1, P_C2, P_MID, P_uID) VALUES ('%f','%f','%f','%f','%d','%d','%d','%d')",PCar[playerid][d][P_X],PCar[playerid][d][P_Y],PCar[playerid][d][P_Z],PCar[playerid][d][P_R],PCar[playerid][d][P_C1],PCar[playerid][d][P_C2],PCar[playerid][d][P_MID],PCar[playerid][d][P_uID]);
      24. mysql_pquery(handle,query);
      25. break;
      26. }
      27. }
      28. }
      Alles anzeigen


      Quellcode

      1. case NUMBER_PLATE_POOL:
      2. {
      3. switch(token)
      4. {
      5. case 0:
      6. {
      7. printf("Nummernschild. %d wurde aus NUMBER_PLATE_POOL generiert.", status);
      8. new d = GetPVarInt(playerid,"_tmp_veh");
      9. DeletePVar(playerid, "_tmp_veh");
      10. PCar[playerid][d][P_KZ] = status;
      11. new string[50];
      12. format(string, sizeof(string), "LS-%d",PCar[playerid][d][P_KZ]);
      13. SetVehicleNumberPlate(PCar[playerid][d][P_VID],string);
      14. }
      15. }
      16. }
      Alles anzeigen
    • Schreibst das dann einfach so:

      C-Quellcode

      1. for(new d; d < sizeof(PCar[]); d++)
      2. {
      3. if(PCar[playerid][d][P_MID] == 0)
      4. {
      5. //Prüfe, ob Fahrzeug existiert. Wenn nicht, nutze den Slot und lege das Fahrzeug an.
      6. SetPVarInt(playerid,"_tmp_veh",d);
      7. StartGenerateUniqueRandomNumber(playerid, handle, NUMBER_PLATE_POOL, 0);
      8. break;
      9. }
      10. }


      Und der ganze Rest, kommt dann in das Callback, z.B. so:

      C-Quellcode

      1. //Im Callback OnRandomNumberGenerated
      2. public OnRandomNumberGenerated(playerid, const pool, const token, const status)
      3. {
      4. if(status == -1) return printf("Der Random Pool (%d), ist leer!",pool);
      5. switch(pool)
      6. {
      7. case SMALL_EXAMPLE_POOL:
      8. {
      9. switch(token)
      10. {
      11. case 0: ApplyNumberPlate(playerid, status);
      12. }
      13. }
      14. }
      15. return 1;
      16. }
      Alles anzeigen
      Und dann packst das einfach alles in die Funktion:


      C-Quellcode

      1. stock ApplyNumberPlate(playerid, number) //number ist die eindeutig zufällige Zahl
      2. {
      3. new d = GetPVarInt(playerid,"_tmp_veh"), SP = random(sizeof(SAHSpawn)), FB = random(127);
      4. DeletePVar(playerid, "_tmp_veh");
      5. PCar[playerid][d][P_KZ] = number;
      6. PCar[playerid][d][P_uID] = pInfo[playerid][db_id];
      7. PCar[playerid][d][P_X] = SAHSpawn[SP][0];
      8. PCar[playerid][d][P_Y] = SAHSpawn[SP][1];
      9. PCar[playerid][d][P_Z] = SAHSpawn[SP][2];
      10. PCar[playerid][d][P_R] = SAHSpawn[SP][3];
      11. PCar[playerid][d][P_C1] = FB;
      12. PCar[playerid][d][P_C2] = FB;
      13. PCar[playerid][d][P_MID] = GetVehicleModel(Autohausauto1[i]);
      14. PCar[playerid][d][P_dbID] = pInfo[playerid][db_id];
      15. PCar[playerid][d][P_VID] = CreateVehicle(PCar[playerid][d][P_MID],PCar[playerid][d][P_X] , PCar[playerid][d][P_Y], PCar[playerid][d][P_Z], PCar[playerid][d][P_R], PCar[playerid][d][P_C1], PCar[playerid][d][P_C2], -1);
      16. new query[256];
      17. mysql_format(handle, query, sizeof(query), "INSERT INTO personalcars (P_X, P_Y, P_Z, P_R, P_C1, P_C2, P_MID, P_uID) VALUES ('%f','%f','%f','%f','%d','%d','%d','%d')",PCar[playerid][d][P_X],PCar[playerid][d][P_Y],PCar[playerid][d][P_Z],PCar[playerid][d][P_R],PCar[playerid][d][P_C1],PCar[playerid][d][P_C2],PCar[playerid][d][P_MID],PCar[playerid][d][P_uID]);
      18. mysql_pquery(handle,query);
      19. }
      Alles anzeigen


      Leute, lernt scripten und versucht mal lieber etwas selber zu schreiben, als es aus einem GF zu kopieren. X/