Flipping a Rusty Coin
Recently, my boss asked me for a server capacity planning model. This was a new ask for me. I can launch Excel with the best of ‘em: fire up Alfred and type “ex<enter>”. I can format columns and enter data and create graphs that show trend lines. I know about p90s and standard deviations and Excel formulas. What I don’t know how to do is create a capacity planning model. So I did what anyone else would do: I asked someone else in the organization to create the model. And I bought a book to learn statistics in Excel, so I’d be prepared for the next time.
Deciding which book to buy was easy: Statistical Analysis with Excel For Dummies, 5th Edition (not an affiliate link), by Joseph Schmuller, PhD. Why? I’ve known Joe for over 25 years. He’s who got me into authoring books, back in the day. He’s smart, knowledgeable, writes well, and has instructional experience. He’s clearly doing something right with this title to reach a fifth edition. Besides, I work with him, so if I get stuck I know how to reach him.
The book arrived, I flipped it open, and started reading. I got to page 15, on which he writes about null and alternative hypotheses. The example uses coin flips, and I felt compelled to write a coin flipping utility so I could create my own results. I called it “coinflip,” and pushed it to GitLab.
My premises:
- Accept an optional count of coin flips
- Display each result as a single character, and multiple results as a single string
Here’s an example:
$ coinflip --count 10
HHTTHTHHTT
I implemented that use case, then realized Joe may have me rolling dice next. I added a faces
parameter that would default to “HT,” but you could pass something like “123456” to represent a six-sided die. Now, you could roll a die, say, 10 times, and get something like this:
$ coinflip --faces 123456 --count 10
1564434663
One more thing: what if I didn’t care about the actual flips, and instead wanted the counts of the results (e.g., 51 heads and 49 tails out of 100 flips)? I started to implement totals in coinflip
, and then realized existing Unix tools could do that transformation.
First, we add a newline after each character in the output, so that:
HHTTHTHHTT
becomes
H
H
T
T
H
T
H
H
T
T
We use sed’s s
command, which performs search-and-replace operations using regular expressions. We match each character, since coinflip
accommodates only single-character faces. In the replacement, an ampersand character references the matched string. Our sed
command is:
sed ’s/./&\n/g'
This command matches each character (.
), replaces it with itself plus a new line (&\n
), and does it globally (g
).
The uniq
command can give us counts (uniq -c
), but to aggregate properly we must sort the results first:
sed ’s/./&\n/g’ | sort | uniq -c
Let’s see how this works:
$ coinflip -c 100 | sed 's/./&\n/g' | sort | uniq -c
47 H
53 T
One more thing: I used GitHub Copilot to simplify my flipping code. You can see that commit here. Here’s the before:
pub fn flip(&self, count: u32) -> String {
let mut result = String::new();
let mut rng = thread_rng();
for _ in 1..=count {
let face = rng.gen_range(0..self.faces.len());
result.push(self.faces.chars().nth(face).unwrap());
}
result
}
Here’s the after:
pub fn flip(&self, count: u32) -> String {
(0..count)
.map(|_| {
let face = thread_rng().gen_range(0..self.faces.len());
self.faces.chars().nth(face).unwrap()
})
.collect()
}
Much cleaner.
I’ve still got a lot of Joe’s book to read before I can whip up a capacity planning model. If my boss asks for another model soon, though, at least I can make one by flipping a coin.