Library: investigate usage of rand() and consider replacement
As it turns out, by far margin we mostly use rand() to generate test values within a limited interval, using the ''modulo trick'' and thus excluding the upper bound. Looking into the implementation of the distributions in the libStdC++ shows that ''constructing'' a distribution on-the-fly is cheap and boils down to checking and then storing the bounds; so basically there is no need to keep ''cached distribution objects'' around, because for all practical purposes these behave like free functions What is required occasionally is a non-zero HashValue, and sometimes an interval of floating-point number or a normal distribution seem useful. Providing these as free-standing convenience functions, implicitly accessing the default PRNG.
This commit is contained in:
parent
ce2116fccd
commit
2883a8619f
3 changed files with 209 additions and 32 deletions
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
|
||||
#include "lib/integral.hpp"
|
||||
#include "lib/hash-value.h"
|
||||
#include "lib/nocopy.hpp"
|
||||
|
||||
#include <random>
|
||||
|
|
@ -79,6 +80,11 @@ namespace lib {
|
|||
uint64_t u64() { return uniformU_(generator_); }
|
||||
double uni() { return uniformD_(generator_); }
|
||||
|
||||
friend int rani(uint);
|
||||
friend double ranRange(double,double);
|
||||
friend double ranNormal(double,double);
|
||||
friend HashVal ranHash();
|
||||
|
||||
/** inject controlled randomisation */
|
||||
void reseed (SeedNucleus&);
|
||||
|
||||
|
|
@ -119,7 +125,42 @@ namespace lib {
|
|||
|
||||
/* ===== convenience accessors ===== */
|
||||
|
||||
inline int rani() { return defaultGen.i32(); } ///< using default params: min ≡ 0, max ≡ numeric_limits
|
||||
/** @return a random integer ∈ [0 ... bound[ */
|
||||
inline int
|
||||
rani (uint bound =1u<<31)
|
||||
{
|
||||
if (!bound) ++bound;
|
||||
--bound;
|
||||
uint upper{std::numeric_limits<int>::max()};
|
||||
upper = bound < upper? bound : upper;
|
||||
std::uniform_int_distribution<int> dist(0, upper);
|
||||
return dist (defaultGen.generator_);
|
||||
}
|
||||
|
||||
/** @return a random double ∈ [start ... bound[ */
|
||||
inline double
|
||||
ranRange (double start, double bound)
|
||||
{
|
||||
std::uniform_real_distribution<double> dist{start,bound};
|
||||
return dist (defaultGen.generator_);
|
||||
}
|
||||
|
||||
inline double
|
||||
ranNormal (double mean =0.0, double stdev =1.0)
|
||||
{
|
||||
std::normal_distribution<double> dist{mean,stdev};
|
||||
return dist (defaultGen.generator_);
|
||||
}
|
||||
|
||||
/** @return a random *non-zero* HashVal */
|
||||
inline lib::HashVal
|
||||
ranHash()
|
||||
{
|
||||
static std::uniform_int_distribution<lib::HashVal> dist{lib::HashVal(1)};
|
||||
return dist (defaultGen.generator_);
|
||||
}
|
||||
|
||||
/// @deprecated
|
||||
inline uint64_t ranu() { return defaultGen.u64(); }
|
||||
inline double runi() { return defaultGen.uni(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@
|
|||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/random.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "lib/test/diagnostic-output.hpp"
|
||||
|
||||
using util::isLimited;
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
|
@ -45,6 +48,7 @@ namespace test {
|
|||
run (Arg)
|
||||
{
|
||||
simpleUsage();
|
||||
verify_distributionVariants();
|
||||
verify_reproducibleSequence();
|
||||
}
|
||||
|
||||
|
|
@ -62,10 +66,44 @@ namespace test {
|
|||
|
||||
int r2 = rani();
|
||||
CHECK (0 <= r2 and r2 < RAND_MAX);
|
||||
CHECK (r1 != r2);
|
||||
CHECK (r1 != r2); // may fail with very low probability
|
||||
}
|
||||
|
||||
|
||||
/** @test properties of predefined distributions provided for convenience
|
||||
* - the upper bound for `rani(bound)` is exclusive
|
||||
* - uniform distributions are sufficiently uniform
|
||||
* - spread of normal distribution is within expected scale
|
||||
*/
|
||||
void
|
||||
verify_distributionVariants()
|
||||
{
|
||||
double avg{0.0};
|
||||
const uint N = 1e6;
|
||||
for (uint i=0; i < N; ++i)
|
||||
avg += 1.0/N * rani (1000);
|
||||
|
||||
auto expect = 500;
|
||||
auto error = fabs(avg/expect - 1);
|
||||
CHECK (error < 0.005);
|
||||
|
||||
for (uint i=0; i < N; ++i)
|
||||
CHECK (isLimited(0, rani(5), 4));
|
||||
|
||||
for (uint i=0; i < N; ++i)
|
||||
CHECK (0 != ranHash());
|
||||
|
||||
auto sqr = [](double v){ return v*v; };
|
||||
|
||||
double spread{0.0};
|
||||
for (uint i=0; i < N; ++i)
|
||||
spread += sqr (ranNormal() - 0.5);
|
||||
spread = sqrt (spread/N);
|
||||
CHECK (spread < 1.12);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test demonstrate that random number sequences can be reproduced
|
||||
* - use a rigged SeedNucleus, always returning a fixed sees
|
||||
* - build two distinct random sequence generators, yet seeded
|
||||
|
|
|
|||
|
|
@ -15226,9 +15226,7 @@
|
|||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1518575730894" ID="ID_24825251" MODIFIED="1518575750656">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
nur <i>einen Satz</i> Klauseln
|
||||
|
|
@ -16653,9 +16651,7 @@
|
|||
<node CREATED="1488672665626" ID="ID_590886664" MODIFIED="1518487921076" TEXT="Grundlagen für Command-handling"/>
|
||||
<node CREATED="1487313769425" ID="ID_728232011" MODIFIED="1518487921076">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Grundlagen für <b>InteractionControl</b>
|
||||
|
|
@ -17123,9 +17119,7 @@
|
|||
</node>
|
||||
<node CREATED="1504308024282" ID="ID_1154461573" MODIFIED="1518487921077">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
wie <i>bestimmt?</i>
|
||||
|
|
@ -17738,9 +17732,7 @@
|
|||
<icon BUILTIN="hourglass"/>
|
||||
<node CREATED="1625070136274" ID="ID_104505251" MODIFIED="1625070160572">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
das heißt: weitgehend <i>custom drawing</i>
|
||||
|
|
@ -47254,9 +47246,7 @@
|
|||
</node>
|
||||
<node CREATED="1455669004941" ID="ID_853385575" MODIFIED="1575133324401">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
dies setzt volle Implementierung
|
||||
|
|
@ -47669,9 +47659,7 @@
|
|||
<node CREATED="1455928216420" ID="ID_662720483" MODIFIED="1464117064570" TEXT="further src elements available"/>
|
||||
<node CREATED="1470527053136" ID="ID_1383280265" MODIFIED="1470527075712">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
of questionable use
|
||||
|
|
@ -47818,9 +47806,7 @@
|
|||
<node CREATED="1461946965569" ID="ID_803034273" MODIFIED="1461946980872" TEXT="locate designated src element"/>
|
||||
<node CREATED="1455928275316" ID="ID_1937317223" MODIFIED="1512926191930">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<i>move</i> into target
|
||||
|
|
@ -57260,7 +57246,8 @@
|
|||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1731118832589" ID="ID_732098473" MODIFIED="1731119409643" TEXT="brauche eine reseed()-Operation um einen Seed wiederherzustellen">
|
||||
<node COLOR="#435e98" CREATED="1731118832589" ID="ID_732098473" MODIFIED="1731424338038" TEXT="brauche eine reseed()-Operation um einen Seed wiederherzustellen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1731118937690" ID="ID_876325939" MODIFIED="1731118961825" TEXT="der C++ - Standard bietet de-facto diese Operation auf allen enthaltenen Engines"/>
|
||||
<node CREATED="1731118962605" ID="ID_1749223447" MODIFIED="1731118982278" TEXT="notfalls könnte man aber auch ein in-place Destroy / re-Construct machen"/>
|
||||
<node COLOR="#338800" CREATED="1731119637225" ID="ID_955411605" MODIFIED="1731205571101" TEXT="reseed()-Operation auf unser RandomSequencer-Interface übernehmen">
|
||||
|
|
@ -57291,13 +57278,14 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731119762690" ID="ID_1637478235" MODIFIED="1731205613988" TEXT="Entwurf: Zufalls-Generator in lib::test::Test">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1731119762690" ID="ID_1637478235" MODIFIED="1731424344693" TEXT="Zufalls-Generator in lib::test::Test">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1731119858173" ID="ID_1447736454" MODIFIED="1731119870919" TEXT="verwendet einen eingebetteten SeedNucleus"/>
|
||||
<node COLOR="#5b280f" CREATED="1731120003113" ID="ID_1177767927" MODIFIED="1731176382320" TEXT="jeder Aufruf erzeugt ein neues RandomSequencer-Objekt">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
</node>
|
||||
<node CREATED="1731119876579" ID="ID_1129580767" MODIFIED="1731119987245" TEXT="sorgt dafür, daß der gezogene Seed dokumentiert wird">
|
||||
<node COLOR="#338800" CREATED="1731119876579" ID="ID_1129580767" MODIFIED="1731421441201" TEXT="sorgt dafür, daß der gezogene Seed dokumentiert wird">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1731120117194" ID="ID_1685729849" MODIFIED="1731120697456" TEXT="Problem: wo?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1731120134129" ID="ID_633936698" MODIFIED="1731120148682" TEXT="STDOUT und STDERR können mit Test-Definitionen interferieren"/>
|
||||
|
|
@ -57314,14 +57302,15 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731120702508" ID="ID_1798637658" MODIFIED="1731120716944" TEXT="es wird also bei jedem Ziehen eines Seed ein NOTICE-Log geschrieben">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1731120702508" ID="ID_1798637658" MODIFIED="1731421435241" TEXT="es wird also bei jedem Ziehen eines Seed ein NOTICE-Log geschrieben">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731120731502" ID="ID_1120851239" MODIFIED="1731120769966" TEXT="neue Kommandozeilen-Parameter: --seed <zahl>">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1731120731502" ID="ID_1120851239" MODIFIED="1731421432983" TEXT="neue Kommandozeilen-Parameter: --seed <zahl>">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1731176387690" ID="ID_94726091" MODIFIED="1731176394537" TEXT="Überlegungen zum API">
|
||||
<node COLOR="#435e98" CREATED="1731176387690" ID="ID_94726091" MODIFIED="1731421458019" TEXT="Überlegungen zum API">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1731176395547" ID="ID_37424726" MODIFIED="1731176788372" TEXT="in den meisten Fällen will man grade nicht ein separates Generator-Objekt">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
|
|
@ -57427,6 +57416,115 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1731424663557" ID="ID_1548490877" MODIFIED="1731424673834" TEXT="std::rand() ersetzen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1731424676911" ID="ID_432733421" MODIFIED="1731424687348" TEXT="was wird benötigt?">
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1731424690509" ID="ID_1222355432" MODIFIED="1731424715317" TEXT="test::randStr()"/>
|
||||
<node CREATED="1731424716055" ID="ID_1693891146" MODIFIED="1731431008272" TEXT="test::randTime()"/>
|
||||
<node CREATED="1731424784952" ID="ID_1485935359" MODIFIED="1731424795595" TEXT="woot! luid.c verwendet rand()"/>
|
||||
<node CREATED="1731424872438" ID="ID_512461088" MODIFIED="1731424875680" TEXT="Bereiche">
|
||||
<node CREATED="1731424876772" ID="ID_1889708776" MODIFIED="1731424890390" TEXT="[0 .. 1000 [">
|
||||
<node CREATED="1731425385582" ID="ID_1130852617" MODIFIED="1731425389674" TEXT="auch 10"/>
|
||||
<node CREATED="1731425390222" ID="ID_385805413" MODIFIED="1731425394394" TEXT="auch 10k"/>
|
||||
<node CREATED="1731425395102" ID="ID_1806459182" MODIFIED="1731425397729" TEXT="auch 100k"/>
|
||||
</node>
|
||||
<node CREATED="1731425040295" ID="ID_373464808" MODIFIED="1731425053800" TEXT="[0 .. <range> ["/>
|
||||
<node CREATED="1731424922446" ID="ID_583117295" MODIFIED="1731425038355" TEXT="[1 .. <range> ]"/>
|
||||
<node CREATED="1731425698987" ID="ID_509650285" MODIFIED="1731425705174" TEXT="beliebiger int-range">
|
||||
<node CREATED="1731426808023" ID="ID_69355296" MODIFIED="1731426817194" TEXT="nur wenige Fälle"/>
|
||||
</node>
|
||||
<node CREATED="1731425981949" ID="ID_658238858" MODIFIED="1731430895929" TEXT="reproducible HashVal ≠ 0"/>
|
||||
</node>
|
||||
<node CREATED="1731426958427" ID="ID_1269151803" MODIFIED="1731427004802" TEXT="Feststellungen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1731425150703" ID="ID_1821489280" MODIFIED="1731425164672" TEXT="Bereichsgrenze ist fast immer compile-time">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1731426971529" ID="ID_1219491527" MODIFIED="1731426983764" TEXT="nur ganz wenige negative Zufallszahlen"/>
|
||||
<node CREATED="1731427756457" ID="ID_1288279705" MODIFIED="1731427887939" TEXT="uniform_int_distribution speichert nur die Bereichsgrenzen (sonst stateless)">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Die klasse enthält diverse Checks, die aber (weitgehend?) constexpr sind. Der eigentliche operative Aufruf ist eine reine Funktion, die den Generator hereingereicht bekommt. Diese Funktion gibt es in zwei Ausprägungen, einmal für einen einzigen Wert, und einmal (wohl optimiert) für Ausgabe in einen Iterator-Range. Letztere heißt __generate und ist wohl nur für die STL intern gedacht
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1731428047688" ID="ID_35787864" MODIFIED="1731428068259" TEXT="per default ist der Bereich [0 ... numeric_limits::max]"/>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1731428069970" ID="ID_282614053" MODIFIED="1731428077830" TEXT="Achtung: closed-Interval">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1731428080621" ID="ID_168543776" MODIFIED="1731428095640" TEXT="im Gegensatz dazu liefert der Modulo-Trick ein offenes Interval">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
<node CREATED="1731428117560" ID="ID_1814881667" MODIFIED="1731428178152" TEXT="nahezu die gesamte codebasis verwendet den Modulo-Trick ⟹ Grenze exclusiv">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
an einigen wenigen Stellen wird eigens dafür gesorgt, daß die grenz inclusiv ist
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1731430747958" ID="ID_909943227" MODIFIED="1731430752006" TEXT="Zielvorgabe">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1731430755615" ID="ID_1824063380" MODIFIED="1731430926339" TEXT="drop-in-Replacement für Modulo">
|
||||
<node CREATED="1731433686599" ID="ID_97051869" MODIFIED="1731433762484">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<font face="Monospaced" color="#1312bf"><b>rani</b></font><font face="Monospaced" color="#50507b">(</font><font face="Monospaced" color="#69507b">max</font><font face="Monospaced" color="#50507b">)</font>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1731433623983" ID="ID_1594131908" MODIFIED="1731433628226" TEXT="es ist ein int"/>
|
||||
<node CREATED="1731433629215" ID="ID_1630650197" MODIFIED="1731433643792" TEXT="Obergrenze exclusiv"/>
|
||||
<node CREATED="1731433617520" ID="ID_423604881" MODIFIED="1731433655617" TEXT="principle of least surprise">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1731431617197" ID="ID_960451563" MODIFIED="1731433675731" TEXT="der i32 soll den gesamten int32-Bereich abdecken (auch negativ!)"/>
|
||||
<node COLOR="#5b280f" CREATED="1731434364363" ID="ID_202795829" MODIFIED="1731434407298" TEXT="ranu und runi werden entfernt (zu sonderbar....)">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1731434034464" ID="ID_676955985" MODIFIED="1731434037208" TEXT="convenience">
|
||||
<node CREATED="1731434572056" ID="ID_1921841184" MODIFIED="1731434593712" TEXT="ranHash() ≠ 0"/>
|
||||
<node CREATED="1731434043756" ID="ID_1935715464" MODIFIED="1731434328260" TEXT="ranRange(u,o) : double ∈ [u ... o[ "/>
|
||||
<node CREATED="1731434161079" ID="ID_1433291868" MODIFIED="1731434333379" TEXT="ranNormal(μ ≔0.0, σ ≔ 1.0) : Gauß"/>
|
||||
<node CREATED="1731434257216" ID="ID_1951992549" MODIFIED="1731434270484" TEXT="root-Import für Tests">
|
||||
<node CREATED="1731434271416" ID="ID_224185748" MODIFIED="1731434275587" TEXT="defaultGen"/>
|
||||
<node CREATED="1731434276112" ID="ID_1394389531" MODIFIED="1731434278979" TEXT="entropyGen"/>
|
||||
<node CREATED="1731434279632" ID="ID_1693749548" MODIFIED="1731434342786" TEXT="rani(), ranRange, ranNormal"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731434498664" ID="ID_33884738" MODIFIED="1731434636107" TEXT="Umstellung">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1731434503085" ID="ID_779294881" MODIFIED="1731441344340" TEXT="API-Umstellungen">
|
||||
<icon BUILTIN="pencil"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731434516153" ID="ID_1079673401" MODIFIED="1731434633528" TEXT="bestehende Verwendungen anpassen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731434530901" ID="ID_81651492" MODIFIED="1731434633527" TEXT="rand() ablösen in Core">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1731434548714" ID="ID_1401652131" MODIFIED="1731434633527" TEXT="rand() % MAX ablösen in Tests">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue