159 lines
3.7 KiB
Go
159 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"git.nunosempere.com/NunoSempere/probppl/choose"
|
|
"math"
|
|
rand "math/rand/v2"
|
|
)
|
|
|
|
type src = *rand.Rand
|
|
type IntProbability struct {
|
|
N int64
|
|
p float64
|
|
}
|
|
type IntProbabilities = []IntProbability
|
|
type IntProbabilitiesWeights struct {
|
|
IntProb IntProbabilities
|
|
w int64
|
|
}
|
|
|
|
func generatePeopleKnownDistribution(r src) IntProbabilities {
|
|
|
|
var probabilities IntProbabilities
|
|
sum := 0.0
|
|
|
|
for i := 16; i <= 2048; i *= 2 {
|
|
p := r.Float64()
|
|
probabilities = append(probabilities, IntProbability{N: int64(i), p: p})
|
|
sum += p
|
|
}
|
|
|
|
for i := range probabilities {
|
|
probabilities[i].p = probabilities[i].p / sum
|
|
}
|
|
|
|
return probabilities
|
|
}
|
|
|
|
func chooseWrapper(n int64, k int64) int64 {
|
|
if n < k {
|
|
return 0
|
|
} else {
|
|
return choose.Choose(n, k)
|
|
}
|
|
}
|
|
|
|
func getProbabilityOfKBirthdayMatchesGivenNPeopleKnown(n int64, k int64) float64 {
|
|
return float64(chooseWrapper(n, k)) * math.Pow(1.0/365.0, float64(k)) * math.Pow(1.0-(1.0/365.0), float64(n-k))
|
|
}
|
|
|
|
func getMatchesDrawGivenNPeopleKnown(n int64, r src) int64 {
|
|
p0 := getProbabilityOfKBirthdayMatchesGivenNPeopleKnown(n, 0)
|
|
p1 := getProbabilityOfKBirthdayMatchesGivenNPeopleKnown(n, 1)
|
|
p2 := getProbabilityOfKBirthdayMatchesGivenNPeopleKnown(n, 2)
|
|
|
|
p := r.Float64()
|
|
if p < p0 {
|
|
return 0
|
|
} else if p < (p0 + p1) {
|
|
return 1
|
|
} else if p < (p0 + p1 + p2) {
|
|
return 2
|
|
} else {
|
|
return 3 // stands for 'greater than 3'
|
|
}
|
|
}
|
|
|
|
/*
|
|
Draw 148 times
|
|
How many people do you know that were born in the same day of the year as you?
|
|
0: 46.6% | 69
|
|
1: 31.1% | 46
|
|
2: 12.8% | 19
|
|
≥3: 9.5% | 14
|
|
*/
|
|
|
|
func drawFromDistributionWithReplacement(d IntProbabilities, r src) int64 {
|
|
pp := r.Float64()
|
|
sum := 0.0
|
|
for i := range d {
|
|
sum += d[i].p
|
|
if pp <= sum {
|
|
return int64(d[i].N) // this introduces some non-determinism, as order of maps in go isn't guaranteed
|
|
}
|
|
|
|
}
|
|
fmt.Printf("%f, %f\n", sum, pp)
|
|
fmt.Println(d)
|
|
panic("Probabilities should sum up to 1")
|
|
}
|
|
|
|
func aboutEq(a int64, b int64) bool {
|
|
h := int64(3)
|
|
return ((-h) <= (a - b)) && ((a - b) <= h)
|
|
}
|
|
|
|
func draw148PplFromDistributionAndCheck(d IntProbabilities, r src, show bool) int64 {
|
|
|
|
count := make(map[int64]int64)
|
|
count[0] = 0
|
|
count[1] = 0
|
|
count[2] = 0
|
|
count[3] = 0
|
|
for i := 0; i < 148; i++ {
|
|
person_i_ppl_known := drawFromDistributionWithReplacement(d, r)
|
|
person_i_num_birthday_matches := getMatchesDrawGivenNPeopleKnown(person_i_ppl_known, r)
|
|
count[person_i_num_birthday_matches]++
|
|
}
|
|
// if (count[0] == 69) && (count[1] == 46) && (count[2] == 19) && (count[3] == 14) {
|
|
if show {
|
|
// fmt.Println(count)
|
|
}
|
|
if aboutEq(count[0], 69) && aboutEq(count[1], 46) && aboutEq(count[2], 19) && aboutEq(count[3], 14) {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func getUnnormalizedBayesianUpdateForDistribution(d IntProbabilities, r src) int64 {
|
|
var sum int64 = 0
|
|
n := 1000
|
|
for i := 0; i < n; i++ {
|
|
/* if i%1000 == 0 {
|
|
fmt.Println(i)
|
|
} */
|
|
draw_result := draw148PplFromDistributionAndCheck(d, r, i == 0)
|
|
// fmt.Println(draw_result)
|
|
sum += draw_result
|
|
}
|
|
return sum // float64(sum) / float64(n)
|
|
}
|
|
|
|
func main() {
|
|
|
|
var r = rand.New(rand.NewPCG(uint64(1), uint64(2)))
|
|
|
|
var distribs []IntProbabilitiesWeights
|
|
|
|
sum_weights := int64(0)
|
|
for i := 0; i < 100; i++ {
|
|
|
|
people_known_distribution := generatePeopleKnownDistribution(r)
|
|
// fmt.Println(people_known_distribution)
|
|
result := getUnnormalizedBayesianUpdateForDistribution(people_known_distribution, r)
|
|
// fmt.Println(i)
|
|
if result > 0 {
|
|
fmt.Println(people_known_distribution)
|
|
fmt.Println(result)
|
|
distribs = append(distribs, IntProbabilitiesWeights{IntProb: people_known_distribution, w: result})
|
|
}
|
|
sum_weights += result
|
|
// fmt.Println(result)
|
|
}
|
|
// fmt.Println(distribs)
|
|
|
|
// Now calculate the posterior
|
|
}
|