Skip to content

Commit 33e70b0

Browse files
committed
0.1.1 Gauss
1 parent 8270df9 commit 33e70b0

File tree

11 files changed

+225
-83
lines changed

11 files changed

+225
-83
lines changed

libraries/Gauss/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.1.1] - 2023-07-07
10+
- improve performance => reciprokeSD = 1.0/stddev
11+
- update readme.md
12+
- add performance section (UNO / ESP32)
13+
- elaborated future section
14+
- generated a more precise lookup table (8 decimals)
15+
- update unit tests
16+
- add default parameters to **bool begin(float mean = 0, float stddev = 1)**
17+
- allow negative **stddev** but return false if stddev <= 0.
18+
- add **float getMean()** convenience function.
19+
- add **float getStdDev()** convenience function.
20+
- clean up a bit
21+
22+
23+
924
## [0.1.0] - 2023-07-06
1025
- initial version
1126

libraries/Gauss/Gauss.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// FILE: Gauss.cpp
33
// AUTHOR: Rob Tillaart
4-
// VERSION: 0.1.0
4+
// VERSION: 0.1.1
55
// PURPOSE: Library for the Gauss probability math.
66
// DATE: 2023-07-06
77

libraries/Gauss/Gauss.h

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
//
33
// FILE: Gauss.h
44
// AUTHOR: Rob Tillaart
5-
// VERSION: 0.1.0
5+
// VERSION: 0.1.1
66
// PURPOSE: Library for the Gauss probability math.
77
// DATE: 2023-07-06
88

99

1010
#include "Arduino.h"
1111
#include "MultiMap.h"
1212

13-
#define GAUSS_LIB_VERSION (F("0.1.0"))
13+
#define GAUSS_LIB_VERSION (F("0.1.1"))
1414

1515

1616
class Gauss
@@ -20,34 +20,48 @@ class Gauss
2020
{
2121
_mean = 0;
2222
_stddev = 1;
23+
_reciprokeSD = 1;
2324
}
2425

25-
bool begin(float mean, float stddev)
26+
// stddev should be positive.
27+
bool begin(float mean = 0, float stddev = 1)
2628
{
2729
_mean = mean;
28-
_stddev = abs(stddev);
29-
return true;
30+
_stddev = stddev; // should be positive
31+
_reciprokeSD = 1.0 / _stddev;
32+
return (stddev > 0);
33+
}
34+
35+
36+
float getMean()
37+
{
38+
return _mean;
39+
}
40+
41+
42+
float getStdDev()
43+
{
44+
return _stddev;
3045
}
3146

3247

3348
float P_smaller(float value)
3449
{
3550
if (_stddev == 0) return NAN;
36-
return _P_smaller((value - _mean) / _stddev);
51+
// normalize(value)
52+
return _P_smaller((value - _mean) * _reciprokeSD);
3753
}
3854

3955

4056
float P_larger(float value)
4157
{
4258
return 1.0 - P_smaller(value);
43-
// if (_stddev == 0) return NAN;
44-
// optimize math division?
45-
// return _P_larger((value - _mean) / _stddev);
4659
}
4760

4861

4962
float P_between(float p, float q)
5063
{
64+
if (_stddev == 0) return NAN;
5165
if (p >= q) return 0;
5266
return P_smaller(q) - P_smaller(p);
5367
}
@@ -56,8 +70,8 @@ class Gauss
5670
float P_equal(float value)
5771
{
5872
if (_stddev == 0) return NAN;
59-
float n = (value - _mean)/_stddev;
60-
float c = 1.0 / (_stddev * sqrt(TWO_PI));
73+
float n = (value - _mean) * _reciprokeSD;
74+
float c = _reciprokeSD * (1.0 / sqrt(TWO_PI));
6175
return c * exp(-0.5 * n * n);
6276
}
6377

@@ -70,7 +84,7 @@ class Gauss
7084

7185
float normalize(float value)
7286
{
73-
return (value - _mean)/_stddev;
87+
return (value - _mean) * _reciprokeSD;
7488
}
7589

7690

@@ -85,27 +99,40 @@ class Gauss
8599

86100
float _P_smaller(float x)
87101
{
88-
// TODO improve accuracy or reduce points.
102+
// NORM.DIST(mean, stddev, x, true)
89103
float __gauss[] = {
90-
0.5000, 0.5398, 0.5793, 0.6179, 0.6554, 0.6915, 0.7257, 0.7580,
91-
0.7881, 0.8159, 0.8413, 0.8643, 0.8849, 0.9032, 0.9192, 0.9332,
92-
0.9452, 0.9554, 0.9641, 0.9713, 0.9772, 0.9821, 0.9861, 0.9893,
93-
0.9918, 0.9938, 0.9953, 0.9965, 0.9974, 0.9981, 0.9987, 1.0000
104+
0.50000000, 0.53982784, 0.57925971, 0.61791142,
105+
0.65542174, 0.69146246, 0.72574688, 0.75803635,
106+
0.78814460, 0.81593987, 0.84134475, 0.86433394,
107+
0.88493033, 0.90319952, 0.91924334, 0.93319280,
108+
0.94520071, 0.95543454, 0.96406968, 0.97128344,
109+
0.97724987, 0.98213558, 0.98609655, 0.98927589,
110+
0.99180246, 0.99379033, 0.99533881, 0.99653303,
111+
0.99744487, 0.99813419, 0.99865010, 0.99996833,
112+
0.99999971, 1.00000000
94113
};
114+
115+
// 0..60000 uint16_t = 68 bytes less
95116
float __z[] = {
96117
0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7,
97118
0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5,
98119
1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3,
99-
2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 10.0
120+
2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 4,0,
121+
5.0, 6.0
100122
};
101123

102-
if (x < 0) return 1.0 - multiMap<float>(-x, __z, __gauss, 32);
103-
return multiMap<float>(x, __z, __gauss, 32);
124+
// a dedicated MultiMap could exploit the fact that
125+
// the __z[] array is largely equidistant.
126+
// that could remove the __z[] array (almost) completely.
127+
if (x < 0) return 1.0 - multiMap<float>(-x, __z, __gauss, 34);
128+
return multiMap<float>(x, __z, __gauss, 34);
104129
}
105130

106131
float _mean = 0;
107-
float _stddev = 1;
132+
float _stddev = 1; // not needed as _reciprokeSD holds same info?
133+
float _reciprokeSD = 1;
108134
};
109135

110136

111137
// -- END OF FILE --
138+

libraries/Gauss/README.md

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Library for the Gauss probability math.
1616
Gauss is an experimental Arduino library to approximate the probability that a value is
1717
smaller or larger than a given value.
1818
These under the premises of a Gaussian distribution with parameters **mean** and **stddev**
19-
(a.k.a. average / mu and standard deviation / sigma).
19+
(a.k.a. average / mu / µ and standard deviation / sigma / σ).
2020
If these parameters are not given, 0 and 1 are used by default (normalized Gaussian distribution).
2121

2222
The values are approximated with **MultiMap()** using a 32 points interpolated lookup.
@@ -28,12 +28,14 @@ Return values are given as floats, if one needs percentages, just multiply by 10
2828

2929
#### Accuracy
3030

31-
The lookup table used has 32 points with 4 significant digits.
32-
Do not expect a higher accuracy / precision.
31+
The lookup table has 34 points with 8 decimals.
32+
This matches the precision of float data type.
33+
Do not expect a very high accuracy / precision as interpolation is linear.
3334
For many applications this accuracy is sufficient.
3435

35-
(links to a table with more significant digits is welcome).
36+
Values of the table are calculated with ```NORM.DIST(mean, stddev, x, true)```.
3637

38+
Note: 0.1.0 was 32 points 4 decimals. Need to investigate reduction of points.
3739

3840
#### Applications
3941

@@ -42,9 +44,20 @@ For many applications this accuracy is sufficient.
4244
- compare population data with individual
4345

4446

47+
#### Character
48+
49+
| parameter | name | ALT-code | char |
50+
|:-----------:|:------:|:----------:|:-----:|
51+
| mean | mu | ALT-230 | µ |
52+
| stddev | sigma | ALT-229 | σ |
53+
54+
- https://altcodesguru.com/greek-alt-codes.html
55+
56+
4557
#### Related
4658

4759
- https://en.wikipedia.org/wiki/Normal_distribution
60+
- https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_probability/bs704_probability9.html
4861
- https://github.com/RobTillaart/Multimap
4962
- https://github.com/RobTillaart/Statistic (more stat links there).
5063

@@ -59,19 +72,32 @@ For many applications this accuracy is sufficient.
5972
#### Base
6073

6174
- **Gauss()** constructor. Uses mean = 0 and stddev = 1 by default.
62-
- **bool begin(float mean, float stddev)** set the mean and stddev.
63-
Returns true on success. If needed stddev is made positive.
75+
- **bool begin(float mean = 0, float stddev = 1)** set the mean and stddev.
76+
Returns true if stddev > 0 which should be so.
77+
Returns false if stddev <= 0, which could be a user choice.
78+
Note that if ```stddev == 0```, probabilities cannot be calculated
79+
as the distribution is not Gaussian.
80+
The default values (0,1) gives the normalized Gaussian distribution.
81+
**begin()** can be called at any time to change the mean or stddev.
82+
- **float getMean()** returns current mean.
83+
- **float getStddev()** returns current stddev.
84+
6485

6586
#### Probability
6687

88+
Probability functions return NAN if stddev == 0.
89+
6790
- **float P_smaller(float f)** returns probability **P(x < f)**.
6891
Multiply by 100.0 to get the value as a percentage.
92+
A.k.a. **CDF()** Cumulative Distribution Function.
6993
- **float P_larger(float f)** returns probability **P(x > f)**.
7094
Multiply by 100.0 to get the value as a percentage.
95+
As the distribution is continuous **P_larger(f) == 1 - P_smaller(f)**.
7196
- **float P_between(float f, float g)** returns probability **P(f < x < g)**.
7297
Multiply by 100.0 to get the value as a percentage.
7398
- **float P_equal(float f)** returns probability **P(x == f)**.
74-
This is the bell curve formula.
99+
This uses the bell curve formula.
100+
75101

76102
#### Other
77103

@@ -82,43 +108,74 @@ E.g if mean == 50 and stddev == 14, then 71 ==> +1.5 sigma.
82108
- **float bellCurve(float f)** returns probability **P(x == f)**.
83109

84110

111+
## Performance
112+
113+
Indicative numbers for 1000 calls, timing in micros.
114+
115+
Arduino UNO, 16 MHz, IDE 1.8.19
116+
117+
| function | 0.1.0 | 0.1.1 | notes |
118+
|:--------------|:--------:|:--------:|:--------|
119+
| P_smaller | 375396 | 365964 |
120+
| P_larger | 384368 | 375032 |
121+
| P_between | 265624 | 269176 |
122+
| normalize | 44172 | 23024 |
123+
| bellCurve | 255728 | 205460 |
124+
| approx.bell | 764028 | 719184 | see examples
125+
126+
127+
ESP32, 240 MHz, IDE 1.8.19
128+
129+
| function | 0.1.0 | 0.1.1 | notes |
130+
|:--------------|:--------:|:--------:|:--------|
131+
| P_smaller | - | 4046 |
132+
| P_larger | - | 4043 |
133+
| P_between | - | 3023 |
134+
| normalize | - | 592 |
135+
| bellCurve | - | 13522 |
136+
| approx.bell | - | 7300 |
137+
138+
85139
## Future
86140

87141
#### Must
88142

89143
- documentation
90-
- mu + sigma character
91-
- unit tests
144+
92145

93146
#### Should
94147

95-
- optimize performance
96-
- remove division by stddev
97148
- optimize accuracy
98149
- revisit lookup of MultiMap
99150
- (-10 .. 0) might be more accurate (significant digits)?
100151
- double instead of floats? (good table?)
101-
152+
- make use of equidistant \_\_z\[] table
102153

103154

104155
#### Could
105156

106-
- **void setMean(float f)**
107-
- **float getMean()**
108-
- **void setStddev(float f)**
109-
- **float getStddev()**
110-
- default values for **begin(0,1)**
111157
- add examples
112158
- e.g. temperature (DS18B20 or DHT22)
113159
- e.g. loadcell (HX711)
114-
- does the stddev needs to be positive,
115-
- what happens if negative values are allowed?
116-
- equality test Gauss objects
117-
- move code to .cpp file? (rather small lib).
118160
- embed MultiMap hardcoded instead of library dependency
119-
- **bellCurve()** => **Z()**?
161+
- add unit tests
162+
- remove **\_stddev** as **\_reciprokeSD** holds same information.
163+
- reverse normalization
164+
- G(100,25) which value has stddev 0.735?
165+
- **VAL(probability = 0.75)** ==> 134 whatever
166+
- Returns the value of the distribution for which the **CDF()** is at least probability.
167+
- Inverse of **P_smaller()**
168+
- **float P_outside(float f, float g)** returns probability **P(x < f) + P(g < x)**.
169+
- assuming no overlap. Use **P_outside() = 1 - P_between()**
120170

121171

122172
#### Won't (unless requested)
123173

174+
- equality test Gauss objects
175+
- does the stddev needs to be positive? Yes.
176+
- what happens if negative values are allowed? P curve is reversed.
177+
- move code to .cpp file? (rather small lib).
178+
- **void setMean(float f)** can be done with begin()
179+
- **void setStddev(float f)** can be done with begin()
180+
124181

libraries/Gauss/examples/Gauss_performance/Gauss_performance.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ void setup(void)
2020
Serial.print("GAUSS_LIB_VERSION: ");
2121
Serial.println(GAUSS_LIB_VERSION);
2222
Serial.println();
23-
Serial.println("Timing in micros");
23+
Serial.println("Timing in micros (1000 calls)");
2424
Serial.println();
2525

2626
test_1();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Arduino UNO
2+
IDE 1.8.19
3+
4+
Gauss_performance.ino
5+
GAUSS_LIB_VERSION: 0.1.1
6+
7+
Timing in micros (1000 calls)
8+
9+
P_smaller: 365964
10+
P_larger: 375032
11+
P_between: 269176
12+
normalize: 23024
13+
bellCurve: 205460
14+
approx.bell: 719184
15+
16+
done...

libraries/Gauss/examples/Gauss_test/Gauss_test.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ void setup(void)
1717
Serial.println();
1818

1919
test_1();
20-
test_2();
21-
test_3();
22-
test_4();
20+
// test_2();
21+
// test_3();
22+
// test_4();
2323

2424
Serial.println("\ndone...");
2525
}

libraries/Gauss/keywords.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Gauss KEYWORD1
66
# Methods and Functions (KEYWORD2)
77

88
begin KEYWORD2
9+
getMean KEYWORD2
10+
getStdDev KEYWORD2
911

1012
P_smaller KEYWORD2
1113
P_larger KEYWORD2

0 commit comments

Comments
 (0)