QRandom - eigener Zufallszahlengenerator

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
BlackFish
Beiträge: 9
Registriert: 31.12.2012 15:29

QRandom - eigener Zufallszahlengenerator

Beitrag von BlackFish »

Hallo,
hier ein kleines Beispiel für einen Zufallszahlengenerator.
Ich habe vor allem deswegen programmiert, weil der Random()-Befehl von PureBasic viel zu unsicher ist und das Ergebnis reproduzierbar sein sollte(daher ist auch kein CryptRandom () möglich).
Der folgende Code sollte relativ sicher sein (zumindest habe ich mein Bestes geben), ich bin aber kein Kryptografie-Experte.
Evlt. kann ein Kryptografie-Experte hier im Forum den Code überprüfen und mir Feedback geben? Das wäre sehr nett.
Der Code darf frei verwendet werden.

Code: Alles auswählen

;================================================================================================================================
;SUBJECT: Generating random numbers with a string as seed
;         This should fill the gap between Random() and CryptRandom()
;AUTHOR:  codeprof aka BlackFish
;LICENSE: PUBLIC DOMAIN (can be used without any restriction)
;         This software is provided 'as-is', without any express or implied warranty.
;         In the author cannot be held liable for any damages arising from the use of this software.
;         Use this software at your own risk. 
;DATE:    2013-03-10
;================================================================================================================================

;Advantages:
; - String can be used as seed!
; - more secure than Random()
; - configurable whether it should be fast or more secure
; - supports Unicode (and returns the same values like in ANSI mode)
; - support for quad random numbers (up to 63 bits instead of 31 bits with Random())
; - works for Windows, Linux and MacOSX

;Disadvantages:
; - probably not as secure as CryptRandom()
; - not thread safe  ( as the "Threaded" keyword of PureBasic cannot be used inside procedures
;                      To make it thread safe move the "Static" variables otside of the procedure as
;                      global variables and use the "Threaded" keyword)



;================================================================================================================================
;Initializes the random number generator with an seed string.
;Parameters:
; seed                      : string with which the random number generator is initialized (max 128 chars)
; num_iterations [optional] : Number of times the result should be hashed with MD5
;                             The result should be at least hashed once for security reason (prevents known plaintext attacks...)
;                             If security is not important "num_iterations" can be set to 0 to improve performace.
;                             A high number will result in very slow number generation.
;                             This could be used to prevent timing attacks (make brute force attacks harder).
;================================================================================================================================
Procedure.q QRandomSeed(seed.s, num_iterations.i = 1)
  Static Dim key.q(1)
  Static Dim current.q(1)    
  Static Dim tmp.q(1)
  Static num_md5_iterations = 0
  Protected md5.s, i.i
  If seed <> ""
    ;Initalize random number generator
    Protected Dim seed_string.UNICODE(127 + 1)
    If Len(seed.s) > 128
      seed = Left(seed, 128)
    EndIf  
    PokeS(@seed_string(0), seed, 128, #PB_Unicode)    
    ;We use the MD5 of "seed.s" to generate a 128 bit key.
    md5.s = MD5Fingerprint(@seed_string(0), SizeOf(UNICODE) * 128)      
    key(0) = Val("$" + Left(md5, 16))
    key(1) = Val("$" + Right(md5, 16))   
    ;We use MD5(MD5(seed)) as initialisation value.
    ;The MD5 of the MD5 is used here so it is not possible to restore the key out of it (as reverting the MD5 is not possible).
    md5.s = MD5Fingerprint(@key(0), 16)      
    current(0) = Val("$" + Left(md5, 16))
    current(1) = Val("$" + Right(md5, 16))
    num_md5_iterations = num_iterations
  Else
    ;generate the next random number
    AESEncoder(@current(0), @tmp(0), 128 / 8, @key(0), 128, #Null, #PB_Cipher_ECB)
    CopyMemory(@tmp(0), @current(0), 128 / 8)     
    ;To prevent short cycles we increment the 128 bit key by 1.
    key(0) + 1
    If key(0) = 0:key(1) + 1:EndIf
    ;hash the result with MD5
    For i = 1 To num_md5_iterations
      md5.s = MD5Fingerprint(@tmp(0), 16)      
      tmp(0) = Val("$" + Left(md5, 16))
      tmp(1) = Val("$" + Right(md5, 16))      
    Next  
  EndIf  
  ProcedureReturn tmp(0) ! tmp(1)   
EndProcedure  


;================================================================================================================================
;Random number between "min" and "max" (including "min" and "max")
;The functions supports random numbers up to 63 bits.
; max                       : maximum number (must be greater than "min")
; min                       : minimum number (must be less than "max")
;================================================================================================================================
Procedure.q QRandom(max.q, min.q = 0)
  Protected val.q
  val.q = QRandomSeed("") & $7FFFFFFFFFFFFFFF
  val = val % ( max - min + 1)
  ProcedureReturn val + min
EndProcedure  





;Example

QRandomSeed("MY TOP SECRET MASTER PASSWORD", 100)
For j = 1 To 10
  passwd$ = ""
  For i = 0 To 30
    passwd$ + Chr(QRandom(126,33))
  Next   
  Debug "Password("+Str(j)+"): " + passwd$
Next  
/edit: kleine Verbesserung bzgl. Var. Deklaration. Danke an ts-soft
Zuletzt geändert von BlackFish am 10.03.2013 15:22, insgesamt 1-mal geändert.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: QRandom - eigener Zufallszahlengenerator

Beitrag von cxAlex »

Also jetzt mal rein statistisch analysiert:

Code: Alles auswählen

#area = 1000
#samples = #area*1000
Define norm = #samples/#area
Dim results_qrand(#area)
Dim results_rand(#area)

Define i, x
For i = 1 To #samples
  x = QRandom(#area)
  results_qrand(x) + 1
  x = Random(#area)
  results_rand(x) + 1
Next
Define ew_rand.f, ew_qrand.f
For i = 0 To #area
  ew_rand + results_rand(i)
  ew_qrand + results_qrand(i)
Next
ew_rand/#area
ew_qrand/#area

Define ma_rand.f, ma_qrand.f
Define sa_rand.f, sa_qrand.f
For i = 0 To #area
  ma_rand + Abs(ew_rand - results_rand(i))
  ma_qrand + Abs(ew_qrand - results_qrand(i))
    sa_rand + Pow(Abs(ew_rand - results_rand(i)),2)
  sa_qrand + Pow(Abs(ew_qrand - results_qrand(i)),2)
Next
ma_rand/#area
ma_qrand/#area
sa_rand = Sqr(sa_rand/#area)
sa_qrand = Sqr(sa_qrand/#area)

 
MessageRequester("","Random() : " + #LF$ + "Erwartungswert: " + StrF(ew_rand, 2) + " ;Mittl. Abweichung: " + StrF(ma_rand, 2) + " ;Stand. Abweichung: " + StrF(sa_rand, 2) +
            #LF$ +  "QRandom() : " + #LF$ + "Erwartungswert: " + StrF(ew_qrand, 2) + " ;Mittl. Abweichung: " + StrF(ma_qrand, 2) + " ;Stand. Abweichung: " + StrF(sa_qrand, 2)) 
Beispiel für eine Bereich von 0-100 (#area = 100)

Code: Alles auswählen

Random() : 
Erwartungswert: 1000.00 ;Mittl. Abweichung: 25.34 ;Stand. Abweichung: 32.47
QRandom() : 
Erwartungswert: 1000.00 ;Mittl. Abweichung: 27.94 ;Stand. Abweichung: 35.09

Deine Implementierung und Random nehmen sich da nichts, mal ergibt Random() eine etwas bessere Verteilung, mal QRandom(), wobei der Unterschied minimal ist.

Gruß, Alex

//Edit: kleine Änderung aufgrund dieses Bugs (http://www.purebasic.fr/english/viewtop ... =4&t=53869) (und ja, das ist ein Bug in meinen Augen ... ) Ändert aber nichts gravierendes am Ergebniss ...
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
BlackFish
Beiträge: 9
Registriert: 31.12.2012 15:29

Re: QRandom - eigener Zufallszahlengenerator

Beitrag von BlackFish »

@cxAlex

Gute Idee. Ich habe dein Code mal etwas angepasst, da die falschen Werte herauskamen.

Code: Alles auswählen

QRandomSeed(Str(ElapsedMilliseconds()), 1)
OpenCryptRandom()

Define i, x
Define ew_rand.d, ew_qrand.d, var_rand.d, var_qrand.d, sa_rand.d, sa_qrand.d
#area = 1000
#samples = #area*1000

For i = 1 To #samples
  x = QRandom(#area)
  ew_rand + x
  var_rand + x * x
  x = CryptRandom(#area)
  ew_qrand + x
  var_qrand + x*x
Next

ew_rand/#samples
ew_qrand/#samples
var_rand/#samples
var_qrand/#samples

sa_rand = Sqr(var_rand - ew_rand  * ew_rand)
sa_qrand = Sqr(var_rand - ew_qrand * ew_qrand)

 
MessageRequester("","CryptRandom() : " + #LF$ + "Erwartungswert: " + StrF(ew_rand, 2) + " ;Stand. Abweichung: " + StrF(sa_rand, 2) +
            #LF$ +  "QRandom() : " + #LF$ + "Erwartungswert: " + StrF(ew_qrand, 2) + " ;Stand. Abweichung: " + StrF(sa_qrand, 2))
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: QRandom - eigener Zufallszahlengenerator

Beitrag von cxAlex »

Hm? Nein, wir haben nur andere Herangehensweisen ... mein Code zeigt die Standardabweichung für jedes einzelne Ergebnis an, also für jedes Element 0-n. Jedes Dieser Elemente sollte idealverteilt gleich oft auftreten, daher ist in meinem Beispiel die Standardabweichung desto besser desto näher an 0.

Also Standardabweichung = Wurzel(Varianz); Varianz: (http://de.wikipedia.org/wiki/Stichprobe ... Berechnung) (korrigierte Stichprobenvarianz)

In deinem Fall bildest du die Abweichung für Über den gesamten Schnitt (Ist die formel Überhaupt korrekt, du Quadrierst den Schnitt und ziehst ihn von der Summe der Quadrate ab und bildest die Wurzel, kommt mir jetzt irgenwie komisch vor), das sind grundsätzlich andere Werte.

Gruß, Alex
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Antworten