############################################################################# ## ## This file is part of GAP, a system for computational discrete algebra. ## This file's authors include Frank Lübeck, Max Neunhöffer. ## ## Copyright of GAP belongs to its developers, whose names are too numerous ## to list here. Please refer to the COPYRIGHT file for details. ## ## SPDX-License-Identifier: GPL-2.0-or-later ## ## This file implements the basic operations for some types of random ## sources. ##
########################################################################### ## Generic methods for random sources. ## # The generic initializer of a random source creates a dummy object of the # right type and then calls 'Init'.
InstallMethod(RandomSource, [IsOperation, IsObject], function(rep, seed)
local res;
res := Objectify(NewType(RandomSourcesFamily, rep), rec());
return Init(res, seed); end);
InstallMethod(RandomSource, [IsOperation], function(rep)
return RandomSource(rep, 1); end);
# A generic Reset if no seed is given (then seed=1 is chosen).
InstallMethod(Reset, [IsRandomSource], function(rs)
return Reset(rs, 1); end);
# Generic fallback methods, such that it is sufficient to install Random for # lists or for pairs of integers.
InstallMethod(Random, [IsRandomSource, IsInt, IsInt], function(rs, a, b)
local d, x, r, y;
d := b - a; if d < 0then
return fail; elif a = b then
return a; else
x := LogInt( d, 2 ) + 1;
r := 0; while0 < x do
y := Minimum( 10, x );
x := x - y;
r := r * 2 ^ y + Random( rs, [ 0 .. (2 ^ y - 1) ] );
od; if d < r then
return Random( rs, a, b ); else
return a + r; fi; fi; end);
# A print method.
InstallMethod(PrintObj, [IsRandomSource], function(rs)
local cat; cat := Difference(CategoriesOfObject(rs), [ "IsRandomSource" ]);
Print("<RandomSource in ", JoinStringsWithSeparator(cat, " and "), ">"); end);
############################################################################ ## The classical GAP random generator as independent random sources. ##
# We need to compute modulo 2^28 repeatedly. If we just write 2^28 into the # code, though, GAP needs to evaluate it to 268435456 each time it executes # the function. To avoid this, we put it into a constant. As an additional # trick, we actually store -2^28, which gives identical results, but has the # benefit of being an immediate integer even on 32 bit systems.
BIND_CONSTANT("R_228", -2^28);
InstallMethod(Init, [IsGAPRandomSource, IsObject], function(rs, seed)
local R_N, R_X, i; if seed = 1then
rs!.R_N := 45;
rs!.R_X := [ 66318732, 86395905, 22233618, 21989103, 237245480, 264566285, 240037038, 264902875, 9274660, 180361945, 94688010, 24032135, 106293216, 27264613, 126456102, 243761907, 80312412, 2522186, 59575208, 70682510, 228947516, 173992210, 175178224, 250788150, 73030390, 210575942, 128491926, 194508966, 201311350, 63569414, 185485910, 62786150, 213986102, 88913350, 94904086, 252860454, 247700982, 233113990, 75685846, 196780518, 74570934, 7958751, 130274620, 247708693, 183364378, 82600777, 28385464, 184547675, 20423483, 75041763, 235736203, 54265107, 49075195, 100648387, 114539755 ]; elif IsInt(seed) then
R_N := 1;
R_X := [ seed mod R_228 ]; for i in [2..55] do
R_X[i] := (1664525 * R_X[i-1] + 1) mod R_228;
od; for i in [1..99] do
R_N := R_N mod 55 + 1;
R_X[R_N] := (R_X[R_N] + R_X[(R_N+30) mod 55+1]) mod R_228;
od;
rs!.R_N := R_N;
rs!.R_X := R_X; else
rs!.R_N := seed[1];
rs!.R_X := ShallowCopy(seed[2]); fi;
return rs; end);
InstallMethod(Reset, [IsGAPRandomSource, IsObject], function(rs, seed)
local old;
old := State(rs);
Init(rs, seed);
return old; end);
InstallMethod(Random, [IsGAPRandomSource, IsList and IsDenseList], function(rs, list)
local rx, rn; if Length(list) < 2^28then
rx := rs!.R_X;
rn := rs!.R_N mod 55 + 1;
rs!.R_N := rn;
rx[rn] := (rx[rn] + rx[(rn+30) mod 55+1]) mod R_228;
return list[ QUO_INT( rx[rn] * LEN_LIST(list), -R_228 ) + 1 ]; else
return list[Random(rs, 1, Length(list))]; fi; end);
############################################################################ ## We provide the "classical" GAP random generator via a random source. ## if IsHPCGAP then
BIND_GLOBAL("RANDOM_SEED_COUNTER", FixedAtomicList(1, 0));
BIND_GLOBAL("GET_RANDOM_SEED_COUNTER", {} ->
ATOMIC_ADDITION(RANDOM_SEED_COUNTER, 1, 1) );
# HACK to enforce backwards compatibility: when reading this file, # we are on the main thread, and want to initialize GlobalRandomSource # with seed 1, without modifying the RANDOM_SEED_COUNTER, to get # behavior identical with that of prior GAP versions, and also identical # with regular GAP (on a single thread).
BIND_GLOBAL("GlobalRandomSource", RandomSource(IsGAPRandomSource, 1));
############################################################################## ## Random source using the Mersenne twister kernel functions. ##
InstallMethod(Init, [IsMersenneTwister, IsObject], function(rs, seed)
local st, endianseed, endiansys, perm, tmp, i; if IsPlistRep(seed) and IsString(seed[1]) and Length(seed[1]) = 2504then
st := ShallowCopy(seed[1]); # maybe adjust endianness if seed comes from different machine:
endianseed := st{[2501..2504]};
endiansys := GlobalMersenneTwister!.state{[2501..2504]}; if endianseed <> endiansys then
perm := List(endiansys, c-> Position(endianseed, c));
tmp := ""; for i in [0..625] do
tmp{[4*i+1..4*i+4]} := st{4*i+perm};
od;
st := tmp; fi;
rs!.state := st; else if not IsString(seed) then
seed := ShallowCopy(String(seed)); # padding such that length is positive and divisible by 4 while Length(seed) = 0 or Length(seed) mod 4 <> 0do
Add(seed, CHAR_INT(0));
od; fi;
rs!.state := InitRandomMT(seed); fi;
return rs; end);
InstallMethod(Reset, [IsMersenneTwister, IsObject], function(rs, seed)
local old;
old := State(rs);
Init(rs, seed);
return old; end);
# We do not need a 'Random' method for 'IsMersenneTwister' and a dense list, # the default method for 'IsRandomSource' is enough.
InstallMethod(Random, [IsMersenneTwister, IsInt, IsInt], function(rs, a, b)
local d, nrbits, res;
d := b-a+1; if d <= 0then
return fail; fi; # Here we could return 'a' in the case 'd = 1'. # However, this would change the sequence of random numbers # w.r.t. earlier GAP versions.
nrbits := Log2Int(d) + 1;
repeat
res := RandomIntegerMT(rs!.state, nrbits);
until res < d;
return res + a; end);
# One global Mersenne twister random source, can be used to overwrite # the library Random(list) and Random(a,b) methods. if IsHPCGAP then
BindThreadLocalConstructor("GlobalMersenneTwister", {} ->
RandomSource(IsMersenneTwister, String(GET_RANDOM_SEED_COUNTER()))); else
BindGlobal("GlobalMersenneTwister", RandomSource(IsMersenneTwister, "1")); fi;
# default random method for lists and pairs of integers using the Mersenne # twister
InstallMethod( Random, "for a dense internal list",
[ IsList and IsDenseList and IsInternalRep ], 100, function(l)
return l[Random(GlobalMersenneTwister, 1, Length(l))]; end );
InstallMethod( Random, "for two integers",
IsIdenticalObj,
[ IsInt,
IsInt ], 0, function(low, high)
return Random(GlobalMersenneTwister, low, high); end );
(function()
local func;
func := function(installType)
return function(args...)
local filterpos, i, func, info;
# Check we understand arguments # Second value must be an info string if not IsString(args[2]) then
ErrorNoReturn("Second argument must be an info string"); fi;
# Info strings always tend to begin 'for ', and here we want # to be able to edit it, so we check. if args[2]{[1..23]} <> "for a random source and"then
ErrorNoReturn("Info string must begin 'for a random source and'"); fi;
# Filters must start with 'IsRandomSource' for i in [1..Length(args)] do if IsList(args[i]) and args[i][1] = IsRandomSource then
filterpos := i; fi;
od;
if not IsBound(filterpos) then
ErrorNoReturn("Must use a list of filters beginning 'IsRandomSource'"); fi;
# Last argument must be the actual method if not IsFunction(Last(args)) then
ErrorNoReturn("Argument list must end with the method"); fi;
# Install
CallFuncList(installType, args);
# Install random, wrapping random source argument
# Remove 'IsRandomSource' from the filter list
Remove(args[filterpos], 1);
# Correct info string by removing 'a random source and'
info := "for";
APPEND_LIST(info, args[2]{[24..Length(args[2])]});
args[2] := info;
func := Remove(args); if Length(args[filterpos]) = 1then
Add(args, x -> func(GlobalMersenneTwister,x)); elif Length(args[filterpos]) = 2then
Add(args, {x,y} -> func(GlobalMersenneTwister,x,y)); else
Error("Only 2 or 3 argument methods supported"); fi;
# This method must rank below Random(SomeRandomSource, IsList) # for any random source SomeRandomSource, to avoid an infinite loop.
InstallMethodWithRandomSource( Random, "for a random source and a (finite) collection",
[ IsRandomSource, IsCollection and IsFinite ],
{} -> -RankFilter(IsCollection and IsFinite),
{rs, C} -> RandomList(rs, Enumerator( C ) ) );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.