From 363ab58a6ec0b19981efa4e839fcf8fe4ba41f6f Mon Sep 17 00:00:00 2001 From: Taylor Wrobel Date: Mon, 16 Sep 2019 23:40:16 -0700 Subject: [PATCH 1/2] Add an iterative rational implementation for exact results 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. --- .gitignore | 3 +++ README.md | 35 ++++++++++++++++++++++++++++++++++- go.mod | 3 +++ main.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1720fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Build go binary + +frog \ No newline at end of file diff --git a/README.md b/README.md index fda46c1..c8ff352 100644 --- a/README.md +++ b/README.md @@ -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` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ea5fc00 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/standupmaths/frog + +go 1.12 diff --git a/main.go b/main.go new file mode 100644 index 0000000..0272b71 --- /dev/null +++ b/main.go @@ -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() +} From 44089aafd6a32c30fc7564c6eeb2a9a6885811a7 Mon Sep 17 00:00:00 2001 From: Taylor Wrobel Date: Sat, 21 Sep 2019 14:37:05 -0700 Subject: [PATCH 2/2] Switch to algorithm by @AustinTSchaffer --- README.md | 33 +-------------------------------- main.go | 26 +++----------------------- 2 files changed, 4 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index c8ff352..7e6cc56 100644 --- a/README.md +++ b/README.md @@ -5,35 +5,4 @@ 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` \ No newline at end of file +Video is here: https://youtu.be/ZLTyX4zL2Fc \ No newline at end of file diff --git a/main.go b/main.go index 0272b71..10b03c8 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,10 @@ package main import ( - "encoding/csv" "fmt" "math/big" "os" "strconv" - "strings" ) func main() { @@ -20,29 +18,11 @@ func main() { 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) + sum := big.NewRat(0, 1) 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)}) + sum = sum.Add(sum, big.NewRat(1, int64(i))) } - w.Flush() + fmt.Printf("Frog(%d) = %s == %s\n", n, sum.String(), sum.FloatString(10)) }