-
Notifications
You must be signed in to change notification settings - Fork 0
/
proportional.ml
96 lines (78 loc) · 3.06 KB
/
proportional.ml
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
open Common
let empty: 'a proportions = [||],[||]
(* choose members of a hash at random proportionally to their value *)
let rangeLists: ('a -> 'a -> 'a) -> 'a -> 'a -> (user * 'a) E.t -> 'a proportions =
fun add smooth zero uvals ->
(* first, I wanted to work with enums, but then decided to keep lists inside...
otherwise, here's how I'd prepend a 0 valued pair to the input stream:
let uvals0 = E.append (E.singleton ("zeroKatzLazarsfeldWattsDodds",0)) uvals
E.push xs x <=> x::xs
E.empty <=> []
Array.of_enum
*)
let (_,ranges) = E.fold begin fun (tot,res) (u,v) ->
let v' = if v > zero then v else smooth in (* +1 smoothing *)
let tot' = add tot v' in
let uv' = (u,tot') in
(tot',uv'::res)
end (zero,[("-",zero)]) uvals in
(* this is Utils.unzip body without rev's, as we have ranges in rev from
the first fold! *)
(* unzip with reverse *)
let (ul,il) = L.fold_left (fun (xs,ys) (x,y) -> (x::xs,y::ys))
([],[]) ranges in
(A.of_list ul, A.of_list il)
let intRangeLists = rangeLists (+) 1 0
let floatRangeLists = rangeLists (+.) 1. 0.
(* find the first element greater or equal than x in a sorted array a *)
let binarySearch aux a ?from ?upto x =
let fromIndex = match from with Some x -> x | _ -> 0 in
let uptoIndex = match upto with Some x -> x | _ -> (A.length a) - 1 in
let fromValue = a.(fromIndex) in
let uptoValue = a.(uptoIndex) in
aux a fromIndex fromValue uptoIndex uptoValue x
let rec findGreaterOrEqual a fI fV uI uV x =
if x < fV || x > uV
then None
else if fV < x && x <= uV && (succ fI) >= uI
then Some uI
else
let mI = (fI + uI) / 2 in
let mV = a.(mI) in
if x = mV
then Some mI
else if x < mV
then findGreaterOrEqual a fI fV mI mV x
else findGreaterOrEqual a mI mV uI uV x
let rec findGreater a fI fV uI uV x =
if x >= uV then None
else if fV <= x && x < uV && (succ fI) >= uI then Some uI
else begin
let mI = (fI + uI) / 2 in
let mV = a.(mI) in
if x < mV then findGreater a fI fV mI mV x
else findGreater a mI mV uI uV x
end
let justGE a = binarySearch findGreaterOrEqual a
let justGreater a = binarySearch findGreater a
(* bound is precomputed as maximum of a,
i.e. a's last element *)
let maxRandomInt = 2.**30. |> Int.of_float |> pred
let pickInt a bound =
(* total mentiosn of friends of friends get so large they exceed 30 random bits,
so we need to handle a full int, which even on 64 bit platform requires a conversion *)
let r = randomInt bound |> succ in
justGE a r
let pickReal a bound =
let r = Random.float bound in
justGE a r
exception Proportional of string
let pick2 pickOne kind (names,vals) =
let bound = array_last vals in
let n = pickOne vals bound in
match n with
| Some n -> names.(n)
| _ -> raise (Proportional (sprintf "proportional %s arrays are misaligned" kind))
let pickInt2 x = pick2 pickInt "int" x
let pickFloat2 x = pick2 pickReal "float" x
let bound pair = pair |> snd |> array_last