benfords-law/main.go

68 lines
2.5 KiB
Go
Raw Permalink Normal View History

2020-11-14 15:44:42 +00:00
package main
import (
"bufio"
"fmt"
"log"
"math"
"math/rand"
"os"
"time"
)
const (
2020-11-15 03:12:11 +00:00
randomMin = 1 // We specify a range which random numbers will be generated, we must start at the first possible left-most digit
2020-11-15 03:02:00 +00:00
randomMax = 999999999999999999 // int64 max value is 9223372036854775807. We use one digit less than that with all 9's in order to not give bias to any digits.
2020-11-15 03:12:11 +00:00
numSamples = 100000000 // A nice rounded human number
2020-11-14 15:44:42 +00:00
)
func main() {
2020-11-15 03:12:11 +00:00
// In results, we store a count of each left most leading digits as numbers are randomly genereated
results := [9]int{} // There are 9 possible leading digits and 0 does not count, offset by 1 for index to actual value. Examples: To access count for 1 use [0]. To access 5 use [4]. To access 9 use [8].
currentSample := 0 // A counter that increments each time a random number sample has been generated. Used for status messages
2020-11-14 15:44:42 +00:00
2020-11-15 03:12:11 +00:00
// Start a little goroutine to output status and attach a ticker to execute it each second
2020-11-14 15:44:42 +00:00
statusTicker := time.NewTicker(time.Second)
go func() {
for {
2020-11-15 03:12:11 +00:00
<-statusTicker.C // Wait for heartbeat from ticker channel
2020-11-14 15:44:42 +00:00
percentCompleted := (currentSample * 100) / numSamples
2020-11-14 17:25:49 +00:00
log.Printf("%d%% completed generating and analyzing samples", percentCompleted)
2020-11-14 15:44:42 +00:00
}
}()
log.Printf("generating numbers...")
rand.Seed(time.Now().UnixNano())
for currentSample = 0; currentSample < numSamples; currentSample++ {
2020-11-15 03:12:11 +00:00
results[firstDigit(rand.Intn(randomMax-randomMin+1)+randomMin)-1]++ // Generate a random number between randomMin and randomMax, get the first digit then increment the counter in results array, remember, it's offset by 1
2020-11-14 15:44:42 +00:00
}
2020-11-15 03:12:11 +00:00
// Done generating and counting digits, stop the status ticker
2020-11-14 15:44:42 +00:00
statusTicker.Stop()
log.Printf("done.")
2020-11-15 03:12:11 +00:00
// Output results
for digitMinusOne, digitCount := range results {
fmt.Printf("%d: %d (%f%%)\n", digitMinusOne+1, digitCount, float64(digitCount*100)/float64(numSamples))
2020-11-14 15:44:42 +00:00
}
2020-11-15 03:12:11 +00:00
// Wait indefinitely until Enter Key is pressed, avoid terminating terminal before viewing results if ran from a shell
2020-11-14 15:44:42 +00:00
fmt.Print("Press 'Enter' to continue...")
bufio.NewReader(os.Stdin).ReadBytes('\n')
}
2020-11-14 17:25:49 +00:00
// firstDigit returns the first/leftmost digit of the base10 representation of an integer
2020-11-14 15:44:42 +00:00
func firstDigit(x int) int {
return int(math.Abs(float64(x)) / math.Pow(10, float64(numDigits(x)-1)))
}
2020-11-14 17:25:49 +00:00
// numDigits returns the number of digits
2020-11-14 15:44:42 +00:00
func numDigits(x int) int {
if x == 0 {
return 1
}
return int(math.Floor(math.Log10(math.Abs(float64(x))))) + 1
}