mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 12:53:53 +00:00
Estimated hours taken: 2 Branches: main Avoid the use of abstract equivalence types or abstract foreign types in a couple of places in the standard library where they cause type errors (for lcc) or warnings (for gcc) in --high-level-code grades for some test cases. library/random.m: Use a no-tag type rather than an abstract equivalence type. library/time.m: Fix an old XXX about representing time_t as a int: use a pragma foreign_type instead. Then wrap this in a no-tag type, to avoid making time.time_t an abstract foreign_type.
218 lines
8.2 KiB
Mathematica
218 lines
8.2 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1998,2001-2003 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% file: random.m
|
|
% main author: conway
|
|
% stability: low
|
|
%
|
|
% Define a set of random number generator predicates. This implementation
|
|
% uses a threaded random-number supply. The supply can be used in a
|
|
% non-unique way, which means that each thread returns the same list of
|
|
% random numbers. However, this may not be desired so in the interests
|
|
% of safety it is also declared with (backtrackable) unique modes.
|
|
%
|
|
% The coefficients used in the implementation were taken from Numerical
|
|
% Recipes in C (Press et al), and are originally due to Knuth. These
|
|
% coefficients are described as producing a "Quick and Dirty" random number
|
|
% generator, which generates the numbers very quickly but not necessarily
|
|
% with a high degree of quality. As with all random number generators,
|
|
% the user is advised to consider carefully whether this generator meets
|
|
% their requirements in terms of "randomness". For applications which have
|
|
% special needs (e.g. cryptographic key generation), a generator such as
|
|
% this is unlikely to be suitable.
|
|
%
|
|
% Note that random number generators of this type have several known
|
|
% pitfalls which the user may need to avoid:
|
|
%
|
|
% 1) The high bits tend to be more random than the low bits. If
|
|
% you wish to generate a random integer within a given range, you
|
|
% should something like 'div' to reduce the random numbers to the
|
|
% required range rather than something like 'mod' (or just use
|
|
% random__random/5).
|
|
%
|
|
% 2) Similarly, you should not try to break a random number up into
|
|
% components. Instead, you should generate each number with a
|
|
% separate call to this module.
|
|
%
|
|
% 3) There can be sequential correlation between successive calls,
|
|
% so you shouldn't try to generate tuples of random numbers, for
|
|
% example, by generating each component of the tuple in sequential
|
|
% order. If you do, it is likely that the resulting sequence will
|
|
% not cover the full range of possible tuples.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module random.
|
|
|
|
:- interface.
|
|
|
|
:- import_module list.
|
|
|
|
% The type `random__supply' represents a supply of random numbers.
|
|
:- type random__supply.
|
|
|
|
% random__init(Seed, RS): creates a supply of random numbers RS
|
|
% using the specified Seed.
|
|
:- pred random__init(int, random__supply).
|
|
:- mode random__init(in, uo) is det.
|
|
|
|
% random__random(Num, RS0, RS): extracts a number Num in the
|
|
% range 0 .. RandMax from the random number supply RS0, and
|
|
% binds RS to the new state of the random number supply.
|
|
:- pred random__random(int, random__supply, random__supply).
|
|
:- mode random__random(out, mdi, muo) is det.
|
|
:- mode random__random(out, in, out) is det.
|
|
|
|
% random__random(Low, Range, Num, RS0, RS): extracts a number Num
|
|
% in the range Low .. (Low + Range - 1) from the random number
|
|
% supply RS0, and binds RS to the new state of the random number
|
|
% supply. For best results, the value of Range should be no greater
|
|
% than about 100.
|
|
:- pred random__random(int, int, int, random__supply, random__supply).
|
|
:- mode random__random(in, in, out, mdi, muo) is det.
|
|
:- mode random__random(in, in, out, in, out) is det.
|
|
|
|
% random__randmax(RandMax, RS0, RS): binds RandMax to the maximum
|
|
% random number that can be returned from the random number
|
|
% supply RS0, and returns RS = RS0.
|
|
:- pred random__randmax(int, random__supply, random__supply).
|
|
:- mode random__randmax(out, mdi, muo) is det.
|
|
:- mode random__randmax(out, in, out) is det.
|
|
|
|
% random__randcount(RandCount, RS0, RS): binds RandCount to the
|
|
% number of distinct random numbers that can be returned from the
|
|
% random number supply RS0, and returns RS = RS0. This will be one
|
|
% more than the number returned by randmax/3.
|
|
:- pred random__randcount(int, random__supply, random__supply).
|
|
:- mode random__randcount(out, mdi, muo) is det.
|
|
:- mode random__randcount(out, in, out) is det.
|
|
|
|
% random__permutation(List0, List, RS0, RS):
|
|
% binds List to a random permutation of List0,
|
|
% and binds RS to the new state of the random number supply.
|
|
:- pred random__permutation(list(T), list(T), random__supply, random__supply).
|
|
:- mode random__permutation(in, out, mdi, muo) is det.
|
|
:- mode random__permutation(in, out, in, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
% Everything after the first `:- implementation' does not appear
|
|
% in the Mercury Library Reference Manual.
|
|
:- interface.
|
|
|
|
% The following predicate was just for test purposes.
|
|
% It should not be used by user programs.
|
|
:- pragma obsolete(random__test/4).
|
|
:- pred random__test(int, int, list(int), int).
|
|
:- mode random__test(in, in, out, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module int, array.
|
|
|
|
:- type random__supply ---> rs(int). % I(j)
|
|
|
|
:- pred random__params(int, int, int). % a, c, m
|
|
:- mode random__params(out, out, out) is det.
|
|
|
|
random__params(9301, 49297, 233280).
|
|
|
|
random__init(I0, rs(RS)) :-
|
|
copy(I0, RS).
|
|
|
|
random__random(I, rs(RS0), rs(RS)) :-
|
|
RS0 = I0,
|
|
random__params(A, C, M),
|
|
I = ((I0 * A) + C) mod M,
|
|
copy(I, RS).
|
|
|
|
% We could make this more robust by checking whether the range is
|
|
% less than a certain threshold, and using a more sophisticated
|
|
% algorithm if the threshold is exceeded. But that would defeat
|
|
% the purpose of having a "quick and dirty" random number generator,
|
|
% so we don't do that.
|
|
random__random(Low, Range, Num) -->
|
|
random__random(R),
|
|
random__randcount(M),
|
|
% With our current set of parameters and a reasonable choice of Range,
|
|
% the following should never overflow.
|
|
{ Num = Low + (Range * R) // M }.
|
|
|
|
random__randmax(M1, RS, RS) :-
|
|
random__params(_A, _C, M),
|
|
M1 = M - 1.
|
|
|
|
random__randcount(M, RS, RS) :-
|
|
random__params(_A, _C, M).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The random permutation is implemented via a "sampling without
|
|
% replacement" method. In init_record, we build up an array in which
|
|
% every integer in the range 0 .. Length - 1 is mapped to the
|
|
% corresponding element in the list. The sampling stage
|
|
% iterates from Length - 1 down to 0. The invariant being
|
|
% maintained is that at iteration I, the elements in the image of
|
|
% the part of the map indexed by 0 .. I-1 are the elements that have
|
|
% not been selected yet. At each iteration, perform_sampling generates
|
|
% a random number Index in the range 0 .. I-1, adds the element that
|
|
% Index is mapped to, Next, to the permutation, and then ensures that
|
|
% Next is not generated again by swapping it with the image of I-1.
|
|
|
|
random__permutation(List0, List, RS0, RS) :-
|
|
Samples = array(List0),
|
|
Len = array__size(Samples),
|
|
perform_sampling(Len, Samples, [], List, RS0, RS).
|
|
|
|
:- pred perform_sampling(int, array(T), list(T), list(T),
|
|
random__supply, random__supply) is det.
|
|
|
|
:- mode perform_sampling(in, array_di, in, out, mdi, muo) is det.
|
|
:- mode perform_sampling(in, array_di, in, out, in, out) is det.
|
|
|
|
perform_sampling(I, Record0, Order0, Order, RS0, RS) :-
|
|
( I =< 0 ->
|
|
Order = Order0,
|
|
RS = RS0
|
|
;
|
|
I1 = I - 1,
|
|
random__random(0, I, Index, RS0, RS1),
|
|
array__lookup(Record0, Index, Next),
|
|
array__lookup(Record0, I1, MaxImage),
|
|
Order1 = [Next | Order0],
|
|
array__set(Record0, Index, MaxImage, Record1),
|
|
array__set(Record1, I1, Next, Record2),
|
|
perform_sampling(I1, Record2, Order1, Order, RS1, RS)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
random__test(Seed, N, Nums, Max) :-
|
|
random__init(Seed, RS),
|
|
random__randmax(Max, RS, RS1),
|
|
random__test_2(N, Nums, RS1, _RS2).
|
|
|
|
:- pred random__test_2(int, list(int), random__supply, random__supply).
|
|
:- mode random__test_2(in, out, mdi, muo) is det.
|
|
:- mode random__test_2(in, out, in, out) is det.
|
|
|
|
random__test_2(N, Is, RS0, RS) :-
|
|
(
|
|
N > 0
|
|
->
|
|
N1 = N - 1,
|
|
random__random(I, RS0, RS1),
|
|
random__test_2(N1, Is0, RS1, RS),
|
|
Is = [I|Is0]
|
|
;
|
|
Is = [],
|
|
RS = RS0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|