Skip to content

Commit

Permalink
Add an iterative rational implementation for exact results
Browse files Browse the repository at this point in the history
Adds an implementation in golang using rational number types to
get exact results, and an iterative algorithm for performance
improvement compared to recursive approaches.

Outputs to CSV for easy data integration with tooling.
  • Loading branch information
taywrobel committed Sep 17, 2019
1 parent dce3f27 commit beca90d
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Build go binary

frog
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
# frog_problem
Here is the frog problem code I wrote in an Edinburgh pub.

Feel free to make better version. This one has not changed since I wrote it. V1 was the same code but saved before I changed it to work for different distances so I have not bothered upload it as well (it was still very unifinished).
Feel free to make better version. This one has not changed since I wrote it. V1
was the same code but saved before I changed it to work for different distances
so I have not bothered upload it as well (it was still very unifinished).

Video is here: https://youtu.be/ZLTyX4zL2Fc

## Iterative Rational Implmentation

Written in Golang by Taylor Wrobel

Makes use of big rationals in golang to get exact expectation for all number of
pads, up to and including the specified target number of pads (including target
bank). Makes use of an interative approach, which performs better than its
recursive equivalent (calculating all results up to 1,000 pads takes ~12.5
seconds for me).

Outputs to STDOUT in CSV format with the following columns (output for `n=10`
displayed in table form):

n | numerator | denominator | float64 representation
-- | --------- | ----------- | ----------------------
1 | 1 | 1 | 1.000000
2 | 3 | 2 | 1.500000
3 | 11 | 6 | 1.833333
4 | 25 | 12 | 2.083333
5 | 137 | 60 | 2.283333
6 | 49 | 20 | 2.450000
7 | 363 | 140 | 2.592857
8 | 761 | 280 | 2.717857
9 | 7129 | 2520 | 2.828968
10 | 7381 | 2520 | 2.928968


Building: `go build`
Running: `./frog [n]` (`n` defaults to 10 if not provided)
Outputting to file (on a UNIX system): `./frog 100 > output.csv`
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module wrobel.dev/frog

go 1.12
48 changes: 48 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"encoding/csv"
"fmt"
"math/big"
"os"
"strconv"
"strings"
)

func main() {
// Parse input value from command line args. Do simple validation.
n := 10
if len(os.Args) > 1 {
parsed, err := strconv.ParseInt(os.Args[1], 10, 64)
if err != nil || parsed < 1 {
panic("Input target must be a positive integer")
}
n = int(parsed)
}

// Likelihoods is a slice that's built up iteratively, as the expected value
// on lillypad n is dependent on the expected value for all lillypads from
// 1 to n-1.
likelihoods := make([]*big.Rat, n+1)
likelihoods[0] = big.NewRat(0, 1)

w := csv.NewWriter(os.Stdout)

for i := 1; i <= n; i++ {
expected := big.NewRat(0, 1)
for j := 0; j < i; j++ {
value := big.NewRat(0, 1)
value.Add(big.NewRat(1, 1), likelihoods[j])
value.Mul(value, big.NewRat(1, int64(i)))

expected.Add(expected, value)
}
likelihoods[i] = expected

numDenom := strings.Split(expected.String(), "/")
f, _ := expected.Float64()
w.Write([]string{fmt.Sprintf("%d", i), numDenom[0], numDenom[1], fmt.Sprintf("%f", f)})
}

w.Flush()
}

0 comments on commit beca90d

Please sign in to comment.