问题
How to seed SML/NJ's random number generator on a Windows machine?
The function Random.rand() takes a pair of integers and uses them to seed the random number generator. Based on my experience with other porgramming languages, I would expect there to be a relatively easy way to seed it based on the system clock (something like srand(time(null));
in C). Unless I am overlooking something obvious, there doesn't seem to be any straightforward way, at least if you are using Windows.
The closest I can find to time(null)
in SML is Posix.ProcEnv.time, which returns Unix epoch time. Unfortunately, the Posix
structures are not part of the Windows download, and the Windows structure (which is) doesn't seem to include any direct analogue of time
.
The Timer structure does have ways of determining elapsed real time. I could write a function which does about half a second of meaningless calculation, time how long it takes, and figure out a way to extract a couple of integers from that. But: 1) this is an awful lot of work for something which is trivial in most languages, 2) more importantly -- it seems likely to result in the same seed being reused a non-trivial percentage of the times.
Another idea I had is that if I could access the Windows environment variable "TIME"
I could use that. The following prints the time to the repl:
OS.Process.system "TIME/T";
but doesn't give any programatic access to the printed string.
OS.Process.getEnv "TIME";
sounds promising, but returns NONE
.
If there really is no easy solution in SML/NJ -- are there options which work for some of the other implementations of SML such as Poly/ML?
回答1:
The Basis Library's TIME signature has a function for returning the current time.
val now: unit -> t
回答2:
@matt answered the question itself as far as getting the system clock reading in a portable way. To complement his answer, here is a seed function. As a technical problem, the number of elapsed seconds since 1970 is too large for an SML/NJ 31-bit int. I could use large ints of course, but a simple solution seemed to be to just reduce by 1.48 billion before converting to an int (and to use the decimal part of the time to get the second int seed parameter):
fun seed () =
let
val r = Time.toReal(Time.now()) - 1.48e9
val f = Real.realFloor(r)
val d = r - f
val i = Real.floor(f)
val j = Real.floor(1000.0*d)
in
Random.rand(i,j)
end;
There is almost definitely a more principled way to do this, but the above works:
- val s = seed ();
val s =
RND
{borrow=ref false,congx=ref 0wx4B7CD4CA,index=ref 0,
vals=[|0wx40E9888B,0wx6F1B97FD,0wx4011C479,0wx2012F528,0wx3CDC0237,
0wx7C36E91D,0wx5361B64D,0wx4B61A297,0wx61823821,0wx7C6CD6BD,
0wx1683CA4D,0wx670A75AF,...|]} : Random.rand
- Random.randRange(1,100) s;
val it = 35 : int
- val s = seed ();
val s =
RND
{borrow=ref false,congx=ref 0wx512EBCFC,index=ref 0,
vals=[|0wx456E115A,0wx27817499,0wx46A6BE48,0wx2C79BB3,0wx3FF47B4D,
0wx5B48FC93,0wx53C3647F,0wx32E40F5A,0wx157AB4C8,0wx16E750D,
0wx78BD3EA3,0wx7885CA23,...|]} : Random.rand
- Random.randRange(1,100) s;
val it = 73 : int
Not very exciting, but successive seedings sepearated by just a few seconds produced different outputs, as expected.
来源:https://stackoverflow.com/questions/42351292/seeding-sml-njs-rng-on-a-windows-machine