Select Git revision
ackwork_test.go
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
pi.go 3.67 KiB
// 29 april 2021
// An example for the second Uebung in Struktur und Simulation.
// For the sake of speed, we can do all the numbers at once, put
// them in arrays, rather than doing i/o on every step. This is actually
// necessary if we want to make plots. We have to store all the points.
// The plotting library I used does not draw to the screen. Bummer. One could
// use the canvas in vggio, although that pulls in all of the fyne library.
// There is an egregious bug in the clipping of lines in plotter, so do
// not use it. Use a scatter plot.
package main
import (
"errors"
"flag"
"fmt"
"math"
"math/rand"
"os"
"strconv"
)
const (
exitSuccess = iota
exitFailure
)
// usage prints out anything the caller gives us then bails.
func usage(e string) {
u := "usage: pi [flags] seed nsteps nstep_nprint"
if e != "" {
fmt.Fprintln(os.Stderr, e+"\n"+u)
}
flag.Usage()
os.Exit(exitFailure)
}
// getnums does the work of filling out the arrays of results
func getnums(cmdArgs cmdArgs) ([]float32, []float32) {
rand.Seed(cmdArgs.seed)
inCnt := make([]uint32, cmdArgs.nstep)
{ // The braces limit the lifetime of inPoint, to save a bit of memory
inPoint := make([]bool, cmdArgs.nstep)
for i := 0; i < cmdArgs.nstep; i++ { // Fill out the array with true/false
x := rand.Float32()
y := rand.Float32()
if (x*x + y*y) <= 1 {
inPoint[i] = true
}
}
if inPoint[0] {
inCnt[0] = 1 // avoid divide by zero errors in loop
}
for i := 1; i < cmdArgs.nstep; i++ {
inCnt[i] = inCnt[i-1]
if inPoint[i] {
inCnt[i]++
}
}
} // We have the counts. Now calculate the errors and standard error
pi := make([]float32, cmdArgs.nstep) // estimated pi
stdErr := make([]float32, cmdArgs.nstep) // standard error
for i := range inCnt {
ptmp := float64(inCnt[i]) / float64(i+1)
pi[i] = float32(4 * ptmp)
stdErr[i] = float32(4 * math.Sqrt((ptmp*(1-ptmp))/(float64(i+1))))
}
return pi, stdErr
}
// wrtTable prints out the table, as per the Uebung instructions
func wrtTable(cmdArgs cmdArgs, pi, stdErr []float32) {
fmt.Printf("%8s %7s %7s %7s\n", "step", "pi", "err", "stderr")
for i := 0; i < cmdArgs.nstep; i = i + cmdArgs.nstepPrint {
e := math.Pi - pi[i]
fmt.Printf("%8d %.5f %.5f %.5f\n", i+1, pi[i], e, stdErr[i])
}
}
type cmdArgs struct {
seed int64
nstep, nstepPrint int
plotName string
doStdout bool // don't print boring table
}
// cmdline gets our command line arguments and maybe a flag or two.
func cmdline(cmdArgs *cmdArgs) error {
var err error
var suppress bool
flag.StringVar(&cmdArgs.plotName, "p", "", "filename to Plot to. No name means no plofile")
flag.BoolVar(&suppress, "s", false, "Suppress stdout - the long table of numbers")
flag.Parse()
if flag.NArg() != 3 {
return errors.New("Wrong number of command line args")
}
a := flag.Args() // not necessary, but makes error messages cleaner
if cmdArgs.seed, err = strconv.ParseInt(a[0], 10, 64); err != nil {
return errors.New("Could not convert first arg " + a[1] + " to int")
}
if cmdArgs.nstep, err = strconv.Atoi(a[1]); err != nil {
return errors.New("Could not convert second arg " + a[2] + " to int")
}
if cmdArgs.nstepPrint, err = strconv.Atoi(a[2]); err != nil {
return errors.New("Could not convert third arg " + a[3] + " to int")
}
cmdArgs.doStdout = true
if suppress {
cmdArgs.doStdout = false
}
return nil
}
func main() {
var cmdArgs cmdArgs
if err := cmdline(&cmdArgs); err != nil {
usage(err.Error())
}
pi, stdErr := getnums(cmdArgs)
if cmdArgs.doStdout {
wrtTable(cmdArgs, pi, stdErr)
}
if err := doplot(pi, stdErr, cmdArgs.plotName); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(exitFailure)
}
os.Exit(exitSuccess)
}