tiny_outcome 2.1.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tiny_outcome.rb +58 -21
  3. metadata +2 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccac5bae17f87452576130e604477b25fbaaa70785e0a41894077e8896521a28
4
- data.tar.gz: f108cdf6533898223d088a169645d2b0ddc897c857c725c713086f42519a9d74
3
+ metadata.gz: 7e887122fe969136656c57bf1e8a240df3500f0120694891be4f9ba26cf9c694
4
+ data.tar.gz: 92e30c4c3c72afc4942181ede0badc1865b297c0444a1c7ec7cce272e4a7dac8
5
5
  SHA512:
6
- metadata.gz: 12b256504992f76ca4e6a22af2b5a9e24699062765f3bf1acb6f9d1e24f9fceefcbf865e856371ab4bf85c9a20fef6923e8582bb9c87b37571bbdba4d1271752
7
- data.tar.gz: fedc09804f2e2c2007cf05e83f0971a2f54a5e9dcb18a2cf245fd5d907e60813f83de23e55024b323bd065cda7acc08d6504928ea1706871b465831326ac33fe
6
+ metadata.gz: 31bf45e93abd24309b440599f7006573dd6b2beb19a588234b6f15aaed4334bde062b252f8bdecf2a7f8b72fe3a42def7a55d38a225b8e37cb19af2dfc375977
7
+ data.tar.gz: b828c61ee05e08de9041bdb5b17a95005ef2cf315419bd3806dc9feec2efb5ba2138231bd8ff9d4bc74e5d02079848c4356d0dc779d02709a12623cccc6ce66d
data/lib/tiny_outcome.rb CHANGED
@@ -30,7 +30,12 @@ class TinyOutcome
30
30
  :samples,
31
31
  :warmth,
32
32
  :warmup,
33
- :value
33
+ :probability,
34
+ :one_count,
35
+ :value,
36
+ :min,
37
+ :max,
38
+ :avg
34
39
 
35
40
  WARM_FULL = :full
36
41
  WARM_TWO_THIRDS = :two_thirds
@@ -44,9 +49,15 @@ class TinyOutcome
44
49
  # samples that we can trust the probability output
45
50
  def initialize(precision, warmup=WARM_FULL)
46
51
  @precision = precision
52
+ @probability = 0.0
53
+ @one_count = 0
47
54
  @samples = 0
55
+ @min = -1.0
56
+ @max = -1.0
57
+ @avg = -1.0
48
58
  @warmth = 0
49
- @value = 0
59
+ @value = [0] * @precision
60
+ @value_index = 0
50
61
  @warmup = case warmup
51
62
  when WARM_FULL then precision
52
63
  when WARM_TWO_THIRDS then (precision / 3) * 2
@@ -59,35 +70,37 @@ class TinyOutcome
59
70
  end
60
71
  end
61
72
 
62
- # add a sample to the historic outcomes. the new sample is added to the
63
- # low-order bits. the new sample is literally left-shifted into the value. the
64
- # only reason this is a custom method is because some metadata needs to be
65
- # updated when a new sample is added
73
+ def numeric_value
74
+ (full? ? @value.rotate(@value_index) : @value)[..(samples-1)].join.to_i(2)
75
+ end
76
+
77
+ # add a sample to the historic outcomes
66
78
  def <<(sample)
67
79
  raise "Invalid sample: #{sample}" unless sample == 0 || sample == 1
68
80
 
69
- @value = ((value << 1) | sample) & (2**precision - 1)
81
+ removing_one = full? && @value[(@value_index + 1) % @precision] == 1
82
+
83
+ @value[@value_index] = sample
84
+ @value_index = (@value_index + 1) % @precision
70
85
  @warmth += 1 unless warmth == warmup
71
- @samples += 1 unless samples == precision
86
+ @samples += 1 unless full?
87
+
88
+ # percentage of 1s out of the existing samples
89
+ #
90
+ # number of 1s
91
+ # probabilty = ---------------
92
+ # total samples
93
+ @one_count -= 1 if removing_one
94
+ @one_count += 1 if sample == 1
95
+ @probability = @one_count / samples.to_f
72
96
 
73
- value
97
+ @value
74
98
  end
75
99
 
76
100
  # true if #probability is >= percentage
77
101
  # false otherwise
78
102
  def winner_at?(percentage)
79
- probability >= percentage
80
- end
81
-
82
- # float: 0.0-1.0
83
- # percentage of 1s out of the existing samples
84
- #
85
- # number of 1s
86
- # probabilty = ---------------
87
- # total samples
88
- def probability
89
- return 0.0 if samples == 0
90
- value.to_s(2).count('1') / samples.to_f
103
+ @probability >= percentage
91
104
  end
92
105
 
93
106
  # true if we've received at least warmup number of samples
@@ -107,6 +120,30 @@ class TinyOutcome
107
120
  samples == precision
108
121
  end
109
122
 
123
+ # updates, and memoizes, the min/max/avg numbers. if you read the min/max/avg
124
+ # attributes you are getting the MEMOIZED values.
125
+ def update_stats!
126
+ return if @samples == 0
127
+
128
+ @min = 1.0
129
+ @max = 0.0
130
+ @avg = 0.0
131
+
132
+ sum = 0.0
133
+ raw = (full? ? @value.rotate(@value_index) : @value)[..(samples-1)]
134
+ group_size = [@samples, 100].min
135
+ num_groups = @samples - group_size
136
+ raw.each_cons(group_size) do |samples_group|
137
+ curr = samples_group.count(1) / samples.to_f
138
+ sum += curr
139
+
140
+ @min = curr if curr < @min
141
+ @max = curr if curr > @max
142
+ end
143
+
144
+ @avg = sum / (@num_groups.to_f + 1.0)
145
+ end
146
+
110
147
  # convenient way to see what's up
111
148
  def to_hash
112
149
  [:value,
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_outcome
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Lunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-03 00:00:00.000000000 Z
11
+ date: 2023-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: m
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: minitest
29
15
  requirement: !ruby/object:Gem::Requirement