Plsql random numbers

De wiki.nexiat.fr
Aller à la navigation Aller à la recherche
Fiche express
Type Package PL/SQL
Objet Génération de nombres pseudo-aléatoires
Voir aussi Plsql webdba utl pkg

Plsql random numbers est un package PL/SQL implémentant un générateur de nombres pseudo-aléatoires par la méthode du générateur congruentiel linéaire (LCG). Il illustre une technique historique utile sur des versions d'Oracle anciennes ou dans des contextes où l'on veut un algorithme reproductible et auto-contenu, sans dépendance externe.

⚠️ Depuis longtemps, Oracle fournit le package standard DBMS_RANDOM (fonctions VALUE, NORMAL, STRING...), à préférer dans l'immense majorité des cas. Ce script garde un intérêt pédagogique (comprendre le principe d'un LCG) mais ne doit en aucun cas servir à générer des valeurs sensibles (mots de passe, jetons, sel de hachage) : un LCG est totalement prévisible dès que quelques valeurs de sortie sont connues, donc impropre à tout usage cryptographique.

Script

CREATE OR REPLACE PACKAGE random IS

  -- Retourne un entier aléatoire dans [0, r-1]
  FUNCTION rndint(r IN NUMBER) RETURN NUMBER;

  -- Retourne un réel aléatoire dans [0, 1]
  FUNCTION rndflt RETURN NUMBER;

END random;
/

CREATE OR REPLACE PACKAGE BODY random IS

  m         CONSTANT NUMBER := 100000000;  -- conditions initiales
  m1        CONSTANT NUMBER := 10000;      -- (pour de meilleurs résultats)
  b         CONSTANT NUMBER := 31415821;
  a         NUMBER;                        -- graine (seed)
  the_date  DATE;
  days      NUMBER;                        -- pour générer la graine initiale
  secs      NUMBER;

  -- Fonction utilitaire privée : multiplication modulaire évitant
  -- le dépassement de précision NUMBER sur de grands produits
  FUNCTION mult(p IN NUMBER, q IN NUMBER) RETURN NUMBER IS
    p1 NUMBER;
    p0 NUMBER;
    q1 NUMBER;
    q0 NUMBER;
  BEGIN
    p1 := TRUNC(p / m1);
    p0 := MOD(p, m1);
    q1 := TRUNC(q / m1);
    q0 := MOD(q, m1);
    RETURN (MOD((MOD(p0 * q1 + p1 * q0, m1) * m1 + p0 * q0), m));
  END mult;

  -- Retourne un entier aléatoire dans [0, r-1]
  FUNCTION rndint(r IN NUMBER) RETURN NUMBER IS
  BEGIN
    -- Génère un nouveau nombre aléatoire et le fixe comme nouvelle graine
    a := MOD(mult(a, b) + 1, m);

    -- Le convertit en entier dans [0, r-1]
    RETURN (TRUNC((TRUNC(a / m1) * r) / m1));
  END rndint;

  -- Retourne un réel aléatoire dans [0, 1]
  FUNCTION rndflt RETURN NUMBER IS
  BEGIN
    a := MOD(mult(a, b) + 1, m);
    RETURN (a / m);
  END rndflt;

BEGIN
  -- Initialise la graine "a" à partir de la date système
  the_date := SYSDATE;
  days     := TO_NUMBER(TO_CHAR(the_date, 'J'));
  secs     := TO_NUMBER(TO_CHAR(the_date, 'SSSSS'));
  a        := days * 24 * 3600 + secs;
END random;
/

Utilisation

SELECT random.rndint(100) FROM dual;   -- entier entre 0 et 99
SELECT random.rndflt      FROM dual;   -- réel entre 0 et 1

Voir aussi