-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReservoir.fs
79 lines (65 loc) · 2.26 KB
/
Reservoir.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
namespace DeepKuhnPoker
open System
/// Reservoir sampler.
/// https://en.wikipedia.org/wiki/Reservoir_sampling
type Reservoir<'t> =
private {
/// Random number generator.
Random : Random
/// Maximum number of items this reservoir can hold.
Capacity : int
/// Items stored in this reservoir.
ItemMap : Map<int, 't>
/// Number of items this reservoir currently holds.
Count : int
/// Number of items this reservoir has seen.
NumSeen : int
}
/// Items held by this reservoir.
member this.Items = this.ItemMap.Values
module Reservoir =
/// Creates an empty reservoir.
let create rng capacity =
assert(capacity > 0)
{
Random = rng
Capacity = capacity
ItemMap = Map.empty
Count = 0
NumSeen = 0
}
/// Attempts to add the given item to the given reservoir,
/// possibly replacing an existing item.
let add item reservoir =
assert(reservoir.Count = reservoir.ItemMap.Count)
assert(reservoir.Count <= reservoir.Capacity)
assert(reservoir.Count <= reservoir.NumSeen)
let numSeen = reservoir.NumSeen + 1
let idxOpt, count =
if reservoir.Count < reservoir.Capacity then
assert(reservoir.Count = reservoir.NumSeen)
Some reservoir.Count, numSeen
else
let idx = reservoir.Random.Next(numSeen)
if idx < reservoir.Capacity then
Some idx, reservoir.Count
else None, reservoir.Count
assert(count <= reservoir.Capacity)
assert(count <= numSeen)
let itemMap =
idxOpt
|> Option.map (fun idx ->
Map.add idx item reservoir.ItemMap)
|> Option.defaultValue reservoir.ItemMap
{
reservoir with
ItemMap = itemMap
Count = count
NumSeen = numSeen
}
/// Attempts to add the given items to the given reservoir,
/// possibly replacing existing items.
let addMany items reservoir =
(reservoir, items)
||> Seq.fold (fun reservoir item ->
add item reservoir)