Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Aus Bytefolge (zufällig) eine Realzahl bilden (https://www.delphipraxis.net/159014-aus-bytefolge-zufaellig-eine-realzahl-bilden.html)

Satty67 10. Mär 2011 19:42

Aus Bytefolge (zufällig) eine Realzahl bilden
 
Ich baue mir gerade ein Klasse, die mir mit Hilfe von CryptGenRandom (AdvAPI32.DLL) eine Zufallszahl liefern soll.

Für ganze Zahlen kein Problem. CryptGenRandom liefert beliebig lange Bytefolgen, die ich dann einfach in den Speicherbereich der jeweiligen Ganzzahl (Byte/Integer/Cardinal/Int64) schiebe.

Bei reelen Zahlen (Single/Double) bin ich aber nicht zufrieden. Die sind ja nicht ganz so statisch aufgebaut, wie ein Integer-Wert (Vorzeichen, Exponent, Mantisse). Folgende Versuche habe ich hinter mir:

1) Schiebe ich nun einfach 8 Byte in den Speicherbereich eines Double, kommen zwar zufällige Zahlen dabei raus, aber 70% liegt bei 0.0, Rest extrem hoch (liegt wohl am theoretischen Wertebereich). Zudem scheine ich beim Single-Typ auch ab und zu eine ungültige Bit-Belegung zu produzieren.

2) Ich nehme eine zufällige Int64-Zahl (geht ja problemlos) und setze die Kommastelle zufällig. Das Ergebnis ist ganz OK, allerdings recht umständlich, die Dezimalstellen zu ermitteln und ich brauche Delphi-Random zum setzen des Komma (Mein Zufallsgenerator kann ja erst nach ermitteln einer Fließkommazahl (für %-Wert) eine Range-Methode liefern)

3) Ich ermittel zwei ganze Zahlen... Int64 als Vorkomma- und Cardinal als Nachkommawert. Sind aber beide Werte hoch, komme ich deutlich über die sinnvollen 16 Stellen für ein Double.

4) Ich nehme einfach ein Festkomma (Currency) in das ich die Bytes schiebe und weise es danach einem Double zu. Ist zumindest schnell und der Wertebereich ist ganz OK. Meine derzeit verwendete Methode.

***

Wie kann ich am besten auf Basis einer zufälligen Bytefolge eine Single/Double Zufallszahl bilden?

BUG 10. Mär 2011 20:09

AW: Aus Bytefolge (zufällig) eine Realzahl bilden
 
Oft bekommt man doch zufällige Gleitkommazahlen im Bereich [0,1) (siehe random).
Da könntest du einfach die Mantisse/fraction zufällig wählen und den Exponenten (konstant) so setzen, das dieser Wertebereich herauskommt. (z.B. Aufteilung Double siehe hier)

Was sollte man mit einer Zufallszahl aus dem ganzen Double-Wertebereich anfangen?

Satty67 10. Mär 2011 20:15

AW: Aus Bytefolge (zufällig) eine Realzahl bilden
 
Für die Berechnung einer Range wie beim original Random brauche ich nur 0<x<1, das stimmt.

Für Rückgabe eines Double wären nur 16 Stellen (Summe Vorkomma/Nachkomma), nicht der ganze Wertebereich interessant. Man spart sich halt weitere Berechnungen, wenn man Zufallszahlen im Double-Wertebereich braucht (z.B. Datum etc.). Aber wie gesagt, 0<x<1 reicht mir auch.

Also nur die unteren 52 Bit (Mantisse) zufällig setzen und den Exponenten gezielt? Das klingt interessant, werde mich gleich mal mit dem genauen Aufbau auseinandersetzen.

€: Hier die aktuelle Unit (noch mit der alten Methode für Double... experimentiere noch)

Es ist nur die AnsiVersion importiert, da aber beide StringParameter nicht gebraucht werde, ist das erst mal unwichtig.
Delphi-Quellcode:
unit CryptRandom;

interface

uses
  Windows;

type
  HCryptProv = DWord;
  PCryptProv = ^HCryptProv;

  TCryptRandom = class
  private
    FProvider : HCryptProv;
    function GetContext: Boolean;
    procedure ReleaseContext;
  public
    constructor Create();

    procedure GetBytes(Bytes : PByte; Size : Integer);
    function GetByte: Byte;
    function GetInteger: Integer;
    function GetCardinal: Cardinal;
    function GetInt64: Int64;
    function GetASCIIString(Size : Integer): AnsiString;
    function GetDouble: Double;
    function Random : Double; overload;
    function Random(Range : Integer): Integer; overload;
  end;

{ BOOL WINAPI CryptAcquireContext( __out HCRYPTPROV *phProv,
                                   __in  LPCTSTR pszContainer,
                                   __in  LPCTSTR pszProvider,
                                   __in  DWORD dwProvType,
                                   __in  DWORD dwFlags );
}
function CryptAcquireContext(phProv : PProvider;
                             pszContainer :PAnsiChar;
                             pszProvider : PAnsiChar;
                             dwProvType : DWord;
                             dwFlags : DWord) : BOOL; stdcall;

{ BOOL WINAPI CryptReleaseContext( __in HCRYPTPROV hProv,
                                   __in DWORD dwFlags );
}
function CryptReleaseContext(hProv : HCryptProv; dwFlags : DWord) :BOOL;stdcall;

{ BOOL WINAPI CryptGenRandom( __in    HCRYPTPROV hProv,
                              __in    DWORD dwLen,
                              __inout BYTE *pbBuffer );
}
function CryptGenRandom(hProv : HCryptProv;
                        dwLen : DWord;
                        pbBuffer : PByte) : BOOL; stdcall;

const
  CRYPT_VERIFYCONTEXT = $F0000000; // using ephemeral keys [default]
  CRYPT_NEWKEYSET    = $8;       // Creates a new key container "pszContainer"
  CRYPT_DELETEKEYSET = $10;      // Delete the key container "pszContainer"

  PROV_RSA_FULL   = 1;
{ PROV_RSA_AES
  PROV_RSA_SIG
  PROV_RSA_SCHANNEL
  PROV_DSS
  PROV_DSS_DH
  PROV_DH_SCHANNEL
  PROV_FORTEZZA
  PROV_MS_EXCHANGE
  PROV_SSL
}
  AdvApiDll = 'AdvAPI32.DLL';

implementation

function CryptAcquireContext; external AdvApiDll Name 'CryptAcquireContextA';
function CryptReleaseContext; external AdvApiDll Name 'CryptReleaseContext';
function CryptGenRandom;     external AdvApiDll Name 'CryptGenRandom';

{ TCryptRandom }

constructor TCryptRandom.Create();
begin
  FProvider := 0;
end;

function TCryptRandom.GetContext: Boolean;
begin
  CryptAcquireContext(@FProvider, NIL, NIL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
  Result := GetLastError = 0;
end;

procedure TCryptRandom.ReleaseContext;
begin
  if FProvider <> 0 then
  begin
    CryptReleaseContext(FProvider, 0);
    FProvider := 0;
  end;
end;

procedure TCryptRandom.GetBytes(Bytes : PByte; Size : Integer);
begin
  if GetContext then
  begin
    try
      CryptGenRandom(FProvider, size, Bytes);
    finally
      ReleaseContext;
    end;
  end;
end;

function TCryptRandom.GetByte: Byte;
begin
  GetBytes(@Result, SizeOf(Result));
end;

function TCryptRandom.GetInteger: Integer;
begin
  GetBytes(@Result, SizeOf(Result));
end;

function TCryptRandom.GetCardinal: Cardinal;
begin
  GetBytes(@Result, SizeOf(Result));
end;

function TCryptRandom.GetInt64: Int64;
begin
  GetBytes(@Result, SizeOf(Result));
end;

function TCryptRandom.GetASCIIString(Size : Integer): AnsiString;
var
  i : Integer;
  b : Byte;
begin
  SetLength(Result, Size);
  GetBytes(@Result[1], Size);

  for i := 1 to Size do
  begin
    b := Ord(Result[i]);
    Result[i] := Chr((b div 4) + 33);
  end;
end;

function TCryptRandom.GetDouble: Double;
var
  c : Currency;
begin
  GetBytes(@c, SizeOf(c));
  Result := c;
end;

function TCryptRandom.Random : Double;
begin
  // 0 <= Result < 1, max 18 decimal digits
  Result := Frac(Abs(GetInt64 / 1000000000000000000));
end;

function TCryptRandom.Random(Range : Integer): Integer;
begin
  Result := Trunc(Random * Range);
end;

end.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:03 Uhr.

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