1/* Part of SWI-Prolog 2 3 Author: R.A. O'Keefe, V.S. Costa, L. Damas, Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2011-2016, Universidade do Porto, University of Amsterdam, 7 VU University Amsterdam. 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(random, 37 [ random/1, % -Float (0,1) 38 random_between/3, % +Low, +High, -Random 39 40 getrand/1, % -State 41 setrand/1, % +State 42 43 maybe/0, 44 maybe/1, % +P 45 maybe/2, % +K, +N 46 47 random_perm2/4, % A,B, X,Y 48 49 random_member/2, % -Element, +List 50 random_select/3, % ?Element, +List, -Rest 51 52 randseq/3, % +Size, +Max, -Set 53 randset/3, % +Size, +Max, -List 54 random_permutation/2, % ?List, ?Permutation 55 56 % deprecated interface 57 random/3 % +Low, +High, -Random 58 ]). 59:- use_module(library(pairs)). 60:- use_module(library(error)). 61:- use_module(library(lists)). 62 63/** <module> Random numbers 64 65This library is derived from the DEC10 library random. Later, the core 66random generator was moved to C. The current version uses the SWI-Prolog 67arithmetic functions to realise this library. These functions are based 68on the GMP library. 69 70@author R.A. O'Keefe, V.S. Costa, L. Damas, Jan Wielemaker 71@see Built-in function random/1: A is random(10) 72*/ 73 74check_gmp :- 75 current_arithmetic_function(random_float), 76 !. 77check_gmp :- 78 print_message(warning, random(no_gmp)). 79 80:- initialization check_gmp. 81 82 83 /******************************* 84 * PRIMITIVES * 85 *******************************/ 86 87%! random(-R:float) is det. 88% 89% Binds R to a new random float in the _open_ interval (0.0,1.0). 90% 91% @see setrand/1, getrand/1 may be used to fetch/set the state. 92% @see In SWI-Prolog, random/1 is implemented by the function 93% random_float/0. 94 95random(R) :- 96 R is random_float. 97 98%! random_between(+L:int, +U:int, -R:int) is semidet. 99% 100% Binds R to a random integer in [L,U] (i.e., including both L and 101% U). Fails silently if U<L. 102 103random_between(L, U, R) :- 104 integer(L), integer(U), 105 !, 106 U >= L, 107 R is L+random((U+1)-L). 108random_between(L, U, _) :- 109 must_be(integer, L), 110 must_be(integer, U). 111 112 113%! random(+L:int, +U:int, -R:int) is det. 114%! random(+L:float, +U:float, -R:float) is det. 115% 116% Generate a random integer or float in a range. If L and U are 117% both integers, R is a random integer in the half open interval 118% [L,U). If L and U are both floats, R is a float in the open 119% interval (L,U). 120% 121% @deprecated Please use random/1 for generating a random float 122% and random_between/3 for generating a random integer. Note that 123% random_between/3 includes the upper bound, while this 124% predicate excludes it. 125 126random(L, U, R) :- 127 integer(L), integer(U), 128 !, 129 R is L+random(U-L). 130random(L, U, R) :- 131 number(L), number(U), 132 !, 133 R is L+((U-L)*random_float). 134random(L, U, _) :- 135 must_be(number, L), 136 must_be(number, U). 137 138 139 /******************************* 140 * STATE * 141 *******************************/ 142 143%! setrand(+State) is det. 144%! getrand(-State) is det. 145% 146% Query/set the state of the random generator. This is intended 147% for restarting the generator at a known state only. The 148% predicate setrand/1 accepts an opaque term returned by 149% getrand/1. This term may be asserted, written and read. The 150% application may not make other assumptions about this term. 151% 152% For compatibility reasons with older versions of this library, 153% setrand/1 also accepts a term rand(A,B,C), where A, B and C are 154% integers in the range 1..30,000. This argument is used to seed 155% the random generator. Deprecated. 156% 157% @see set_random/1 and random_property/1 provide the SWI-Prolog 158% native implementation. 159% @error existence_error(random_state, _) is raised if the 160% underlying infrastructure cannot fetch the random state. 161% This is currently the case if SWI-Prolog is not compiled 162% with the GMP library. 163 164setrand(rand(A,B,C)) :- 165 !, 166 Seed is A<<30+B<<15+C, 167 set_random(seed(Seed)). 168setrand(State) :- 169 set_random(state(State)). 170 171:- if(current_predicate(random_property/1)). 172getrand(State) :- 173 random_property(state(State)). 174:- else. 175getrand(State) :- 176 existence_error(random_state, State). 177:- endif. 178 179 180 /******************************* 181 * MAYBE * 182 *******************************/ 183 184%! maybe is semidet. 185% 186% Succeed/fail with equal probability (variant of maybe/1). 187 188maybe :- 189 random(2) =:= 0. 190 191%! maybe(+P) is semidet. 192% 193% Succeed with probability P, fail with probability 1-P 194 195maybe(P) :- 196 must_be(between(0.0,1.0), P), 197 random_float < P. 198 199%! maybe(+K, +N) is semidet. 200% 201% Succeed with probability K/N (variant of maybe/1) 202 203maybe(K, N) :- 204 integer(K), integer(N), 205 between(0, N, K), 206 !, 207 random(N) < K. 208maybe(K, N) :- 209 must_be(nonneg, K), 210 must_be(nonneg, N), 211 domain_error(not_less_than_zero,N-K). 212 213 214 /******************************* 215 * PERMUTATION * 216 *******************************/ 217 218%! random_perm2(?A, ?B, ?X, ?Y) is semidet. 219% 220% Does X=A,Y=B or X=B,Y=A with equal probability. 221 222random_perm2(A,B, X,Y) :- 223 ( maybe 224 -> X = A, Y = B 225 ; X = B, Y = A 226 ). 227 228 229 /******************************* 230 * SET AND LIST OPERATIONS * 231 *******************************/ 232 233%! random_member(-X, +List:list) is semidet. 234% 235% X is a random member of List. Equivalent to random_between(1, 236% |List|), followed by nth1/3. Fails of List is the empty list. 237% 238% @compat Quintus and SICStus libraries. 239 240random_member(X, List) :- 241 must_be(list, List), 242 length(List, Len), 243 Len > 0, 244 N is random(Len), 245 nth0(N, List, X). 246 247%! random_select(-X, +List, -Rest) is semidet. 248%! random_select(+X, -List, +Rest) is det. 249% 250% Randomly select or insert an element. Either List or Rest must 251% be a list. Fails if List is the empty list. 252% 253% @compat Quintus and SICStus libraries. 254 255random_select(X, List, Rest) :- 256 ( '$skip_list'(Len, List, Tail), 257 Tail == [] 258 -> true 259 ; '$skip_list'(RLen, Rest, Tail), 260 Tail == [] 261 -> Len is RLen+1 262 ), 263 !, 264 Len > 0, 265 N is random(Len), 266 nth0(N, List, X, Rest). 267random_select(_, List, Rest) :- 268 partial_list(List), partial_list(Rest), 269 instantiation_error(List+Rest). 270random_select(_, List, Rest) :- 271 must_be(list, List), 272 must_be(list, Rest). 273 274%! randset(+K:int, +N:int, -S:list(int)) is det. 275% 276% S is a sorted list of K unique random integers in the range 277% 1..N. Implemented by enumerating 1..N and deciding whether or 278% not the number should be part of the set. For example: 279% 280% == 281% ?- randset(5, 5, S). 282% S = [1, 2, 3, 4, 5]. (always) 283% ?- randset(5, 20, S). 284% S = [2, 7, 10, 19, 20]. 285% == 286% 287% @see randseq/3. 288% @bug Slow if N is large and K is small. 289 290randset(K, N, S) :- 291 must_be(nonneg, K), 292 K =< N, 293 randset(K, N, [], S). 294 295randset(0, _, S, S) :- !. 296randset(K, N, Si, So) :- 297 random(N) < K, 298 !, 299 J is K-1, 300 M is N-1, 301 randset(J, M, [N|Si], So). 302randset(K, N, Si, So) :- 303 M is N-1, 304 randset(K, M, Si, So). 305 306 307%! randseq(+K:int, +N:int, -List:list(int)) is det. 308% 309% S is a list of K unique random integers in the range 1..N. The 310% order is random. Works as if defined by the following code. 311% 312% == 313% randseq(K, N, List) :- 314% randset(K, N, Set), 315% random_permutation(Set, List). 316% == 317% 318% @see randset/3. 319 320 321randseq(K, N, S) :- 322 randseq(K, N, L, []), 323 keysort(L, R), 324 pairs_values(R, S). 325 326randseq(0, _, S, S) :- !. 327randseq(K, N, [Y-N|Si], So) :- 328 random(N) < K, 329 !, 330 random(Y), 331 J is K-1, 332 M is N-1, 333 randseq(J, M, Si, So). 334randseq(K, N, Si, So) :- 335 M is N-1, 336 randseq(K, M, Si, So). 337 338%! random_permutation(+List, -Permutation) is det. 339%! random_permutation(-List, +Permutation) is det. 340% 341% Permutation is a random permutation of List. This is intended to 342% process the elements of List in random order. The predicate is 343% symmetric. 344% 345% @error instantiation_error, type_error(list, _). 346 347random_permutation(List1, List2) :- 348 is_list(List1), 349 !, 350 random_permutation_(List1, List2). 351random_permutation(List1, List2) :- 352 is_list(List2), 353 !, 354 random_permutation_(List2, List1). 355random_permutation(List1, List2) :- 356 partial_list(List1), partial_list(List2), 357 !, 358 instantiation_error(List1+List2). 359random_permutation(List1, List2) :- 360 must_be(list, List1), 361 must_be(list, List2). 362 363random_permutation_(List, RandomPermutation) :- 364 key_random(List, Keyed), 365 keysort(Keyed, Sorted), 366 pairs_values(Sorted, RandomPermutation). 367 368key_random([], []). 369key_random([H|T0], [K-H|T]) :- 370 random(K), 371 key_random(T0, T). 372 373%! partial_list(@Term) is semidet. 374% 375% True if Term is a partial list. 376 377partial_list(List) :- 378 '$skip_list'(_, List, Tail), 379 var(Tail). 380 381:- multifile 382 prolog:message//1. 383 384prologmessage(random(no_gmp)) --> 385 [ 'This version of SWI-Prolog is not compiled with GMP support.'-[], nl, 386 'Floating point random operations are not supported.'-[] 387 ]