1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
/* This script is part of radix_info.
Copyright (C) 2023 Adrien Hopkins
This program is free software: you can redistribute it and/or modify
it under the terms of version 3 of the GNU General Public License
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package factors
import (
"math"
"math/big"
)
/*
Score returns a "factor score" equal to the sum of the reciprocoals
of the number n's factors.
Rationale:
A number's factors are one of the most important things that determines
how well it functions as a number radix. An easy way to determine how
good these factors are is to simply count them, but that comes with
the problem that all factors are considered equal when they really aren't
- divisibility by 2 (which ensures 1/2 is a terminating fraction and
you can tell whether a number is even or odd by looking at the last digit)
is more important than divisibility by 23. The most obvious way of
accounting for this is by giving each factor a score and adding those
scores to get the overall score. I chose score(f) = 1/f because it is
the simplest function that captures the intuition that smaller factors
are more valuable than larger ones. It also gives the score a meaning:
a factor's score is the probability a random number is divisible by it.
Special cases:
Score(0) = NaN
Score(1) = 1.0
*/
func Score(n uint) float64 {
if n == 0 {
return math.NaN()
} else if n > maxSmall {
return bigScore(n)
}
factorSum := uint(0)
for _, f := range Factors(n) {
factorSum += f
}
return float64(factorSum) / float64(n)
}
const maxSmall = 1<<28 - 1
func bigScore(n uint) float64 {
factorSum := new(big.Int)
// initialize bigFactor like this to avoid reallocating memory
bigFactor := new(big.Int).SetUint64(uint64(n))
for _, f := range Factors(n) {
bigFactor.SetUint64(uint64(f))
factorSum.Add(factorSum, bigFactor)
}
bigN := new(big.Int).SetUint64(uint64(n))
score := new(big.Rat).SetFrac(factorSum, bigN)
score64, _ := score.Float64()
return score64
}
/*
BasicRank returns a rank describing how well a radix handles the simplest
fractions (1/2, 1/3, 1/4, 1/5). It consists of two parts: a letter (A-F)
and a sign (+, -, ~). It is also known as the 2345 Rank.
The letter determines how well the first three fractions are handled
(specifically, what their decimal expansions look like):
- A means all three terminate with one digit.
- B means 1/2 and 1/3 terminate with one digit,
and 1/4 terminates with two digits.
- C means 1/2 and 1/4 terminate with one digit,
but 1/3 is infinitely repeating (pattern length 1-2 digits).
- D means 1/2 terminates with one digit,
but 1/3 is infinitely repeating (pattern length 1-2 digits),
and 1/4 terminates with two digits.
- E means 1/3 terminates with one digit,
but 1/2 and 1/4 are infinitely repeating
(pattern lengths 1 and 1-2 respectively).
- F means all three are infinitely repeating
(pattern lengths 1, 1-2 and 1-2 respectively).
The sign determines how well 1/5 is handled:
- A plus means it terminates with one digit.
- A tilde means it repeats with a short (1-2 digit) pattern.
- A minus means it repeats with a long (4-digit) pattern.
Because the rank only depends on radix % 60, 0 and 1 are given the same
ranks as 60 and 61 (A+, F~).
*/
func BasicRank(radix uint) string {
var firstRank, secondRank string
switch uint(0) {
case radix % 12:
firstRank = "A"
case radix % 6:
firstRank = "B"
case radix % 4:
firstRank = "C"
case radix % 2:
firstRank = "D"
case radix % 3:
firstRank = "E"
default:
firstRank = "F"
}
switch radix % 5 {
case 0:
secondRank = "+"
case 1, 4:
secondRank = "~"
case 2, 3:
secondRank = "-"
}
return firstRank + secondRank
}
|