--- name: rng kind: function lang: cpp domain: datascience version: "1.0.0" purity: pure signature: "void rng_seed(Rng&, uint64 seed); uint64 rng_u64(Rng&); double rng_uniform(Rng&); double rng_normal(Rng&); uint64 rng_below(Rng&, uint64 n); int rng_categorical(Rng&, const double* weights, int n)" description: "Generador pseudoaleatorio xoshiro256++ con state inout (struct Rng). Helpers: uniform, normal (Box-Muller), below (Lemire sin sesgo), categorical (O(n) cumulative). Determinista dado seed — pareja CPU del gpu_rng_glsl." tags: [rng, xoshiro, uniform, normal, categorical, montecarlo, datascience] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: [cstdint, cmath] tested: false tests: [] test_file_path: "" file_path: "cpp/functions/datascience/rng.cpp" params: - name: seed desc: "Semilla maestra. 0 se sustituye por la constante de Knuth para evitar arranque degenerado." - name: r desc: "Estado RNG (struct con 4 uint64). Se muta in-place en cada llamada." - name: n desc: "(rng_below) limite superior exclusivo. (rng_categorical) numero de pesos." - name: weights desc: "(rng_categorical) array de pesos NO necesariamente normalizados. Pesos negativos o cero se ignoran." output: "Numeros pseudoaleatorios deterministas dado el state inicial. rng_seed inicializa los 4 lanes via SplitMix64. Mismo patron que glsl_rng_preamble (gpu) para reutilizar GLSL kernels en CPU." --- # rng Pareja CPU del `glsl_rng_preamble` (de `gpu_rng_glsl`). Misma semantica, mismas garantias. ## Calidad xoshiro256++ (Vigna 2018): periodo 2^256 - 1, 1.2 ns/u64 en x86, supera PractRand 32 TB. Reemplaza al `std::mt19937_64` (mas lento y mayor estado, sin ventaja para Monte Carlo). `rng_normal` usa Box-Muller polar (~30 ns por sample). Para volumen extremo (>10^9 normals CPU) considerar Ziggurat — no incluido aqui por complejidad. `rng_below` usa el metodo de Lemire (2019): rejection sampling sin division en el caso comun, sesgo cero. Mejor que `rng_u64() % n` que tiene sesgo modular. ## Uso ```cpp fn::ds::Rng r; fn::ds::rng_seed(r, 0xC0FFEE); double x = fn::ds::rng_normal(r); // ~ N(0, 1) double u = fn::ds::rng_uniform(r); // [0, 1) int k = fn::ds::rng_below(r, 100); // 0..99 sin sesgo double weights[] = {0.5, 0.3, 0.2}; int idx = fn::ds::rng_categorical(r, weights, 3); ``` ## Notas - Determinista bit-exacto dado el seed. Util para tests numericos y para comparar contra el GLSL kernel (que usa PCG32 — distinto stream pero misma semantica de uniform/normal). - No thread-safe en una unica Rng. Para Monte Carlo paralelo: una Rng por thread, sembrada con seeds derivados de un master via `rng_seed` con seeds distintos. - Pure: la mutacion del state es referencialmente transparente (la misma secuencia de llamadas produce los mismos resultados). Sin I/O, sin globals.