将数字从 10 进制转换为 60 进制
Converting Numbers from Base 10 to Base 60
最近,我 reading 了解到古巴比伦文明使用以 60 为底而不是以 10 为底的数字系统。即使这个数字系统以 60 为底,他们仍然能够近似平方根2 — 也是几千年前!
我对此很好奇,想看看我们的十进制(以 10 为底)的数字如何转换为六十进制(以 60 为底)。使用 R 编程语言,我发现 其中提供了将数字从一些基数转换为不同基数的答案。
不过这里好像base只能在2到36之间(我要base 60):
base <- function(b, base = 10)
{
base <- as.integer(base)
if(base > 36 | base < 2) stop("'base' must be between 2 and 36.")
structure(lapply(b, function(x)
{
n <- ceiling(log(x, base))
vec <- numeric()
val <- x
while(n >= 0)
{
rem <- val %/% base^n
val <- val - rem * base^n
vec <- c(vec, rem)
n <- n - 1
}
while(vec[1] == 0 & length(vec) > 1) vec <- vec[-1]
structure(x, base = base, representation = vec)
}), class = "base")
}
我链接到的文章的标题是“在这个以 60 为底的奇怪世界中,八分之一等于七和三十”- 我想看到这个并将“1 /8" 从十进制转换为六十进制的 "7 和 30"。
有人可以帮我解决这个问题吗?
给定的代码几乎 有效。对 bases < 36 的限制只是因为原作者想用符号 [0-9A-Z] 来表达这些值。删除该限制并扩展算法以允许额外的数字 'after the decimal point' (或 'after the sexagesimal point' 在 base 60 的情况下 :-) )我们得到一些 almost 有效的东西(下面的函数定义):
base(1/8, base = 60, digits = 6)
[[1]]
[1] 0.125
attr(,"base")
[1] 60
attr(,"representation")
[1] 7 29 59 59 59 59
attr(,"class")
[1] "base"
我们得到的不是“7 30”,而是“7 29(59 次重复)”,这类似于进行本应为 0.2 的小数计算却得到 0.1999....
这大概可以通过适当的 'numeric fuzz' 阈值来解决。
代码中缺少的另一件事是,现在它包含小数部分,结果应该 return 信息告诉您 'decimal' 点所在的位置(最简单的,在输出中包含 digits
的值)。
代码的其他方面可以改进(例如 pre-allocating vec
而不是迭代构建)。
base <- function(b, base = 10, digits = 0) {
base <- as.integer(base)
structure(lapply(b, function(x)
{
n <- ceiling(log(x, base))
vec <- numeric()
val <- x
while(n >= -1*digits ) {
rem <- val %/% base^n
val <- val - rem * base^n
vec <- c(vec, rem)
n <- n - 1
}
while(vec[1] == 0 & length(vec) > 1) vec <- vec[-1]
structure(x, base = base, representation = vec)
}), class = "base")
}
也许我们可以试试下面的代码(给定 x < 1
)
f <- function(x, base = 60, digits = 6) {
if (digits == 1) {
return(round(x))
}
p <- x * base
c(p %/% 1, Recall(p %% 1, digits = digits - 1))
}
这给出了
> f(1 / 8)
[1] 7 30 0 0 0 0
> f(1 / 7)
[1] 8 34 17 8 34 0
> f(1/3)
[1] 20 0 0 0 0 0
这是 @BenBolker 的 base
函数的矢量化版本,我称之为 vbase
。它消除了对提供的数字向量 x
的 n
元素的循环,即使对于适度的 n
,这在 R 中也是相当昂贵的。它恰好更通用,因为它同时支持正数和非正数 x
.
vbase
returns 具有 1+digits
行(一个符号位)和 n
列的矩阵。矩阵结果在视觉上很好看,也便于 pattern-finding.
vbase <- function(x, base = 10, digits = 6) {
stopifnot(is.numeric(x), is.finite(x),
is.numeric(base), length(base) == 1L, base >= 2,
is.numeric(digits), length(digits) == 1L, digits >= 1)
n <- length(x)
if (n == 0L) {
return(NULL)
}
base <- as.integer(base)
digits <- as.integer(digits)
m <- 1L + digits
nz <- x != 0
res <- matrix(0L, m, n)
if (any(nz)) {
nz <- which(nz)
y <- abs(x[nz])
e <- ceiling(log(max(y), base))
pow <- base^e
res[1L, ] <- sign(x)
i <- 2L
while (i <= m) {
quo <- y %/% pow
res[i, nz] <- quo
y <- y - quo * pow
pow <- pow / base
i <- i + 1L
}
} else {
e <- 0
}
ech <- sprintf("%d", seq.int(e, by = -1, length.out = digits))
dimnames(res) <- list(EXPONENT = c("sign", ech), ELEMENT = names(x))
attr(res, "x") <- x
attr(res, "base") <- base
attr(res, "digits") <- digits
res
}
x <- 60^(-2:2)
vbase(c(-x, x), base = 60, digits = 5)
ELEMENT
EXPONENT [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
sign -1 -1 -1 -1 -1 1 1 1 1 1
2 0 0 0 0 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 0 0
-1 0 1 0 0 0 0 1 0 0 0
-2 1 0 0 0 0 1 0 0 0 0
attr(,"x")
[1] -2.777778e-04 -1.666667e-02 -1.000000e+00 -6.000000e+01 -3.600000e+03
[6] 2.777778e-04 1.666667e-02 1.000000e+00 6.000000e+01 3.600000e+03
attr(,"base")
[1] 60
attr(,"digits")
[1] 5
vbase(1 / 8, base = 60, digits = 10)
ELEMENT
EXPONENT [,1]
sign 1
0 0
-1 7
-2 29
-3 59
-4 59
-5 59
-6 59
-7 59
-8 59
-9 59
attr(,"x")
[1] 0.125
attr(,"base")
[1] 60
attr(,"digits")
[1] 10
矩阵结果的一个限制是 x
的最大元素(绝对值)决定了使用什么指数序列来表示所有其他元素。例如:
vbase(2^c(-10, 1), base = 2, digits = 2)
ELEMENT
EXPONENT [,1] [,2]
sign 1 1
1 0 1
0 0 0
attr(,"x")
[1] 0.0009765625 2.0000000000
attr(,"base")
[1] 2
attr(,"digits")
[1] 2
此处,2^(-10)
被错误表示,因为指数从 1 开始倒数。您 可以 将 digits
增加到 12 以捕获两个 2^(-10)
和 2^1
,但这样做会在结果中引入许多零,这不是内存的最佳使用方式。也就是说,如果 length(x)
不是很大——当然如果 base = 60
——那么你不必太担心 运行 内存不足。
最近,我 reading 了解到古巴比伦文明使用以 60 为底而不是以 10 为底的数字系统。即使这个数字系统以 60 为底,他们仍然能够近似平方根2 — 也是几千年前!
我对此很好奇,想看看我们的十进制(以 10 为底)的数字如何转换为六十进制(以 60 为底)。使用 R 编程语言,我发现
不过这里好像base只能在2到36之间(我要base 60):
base <- function(b, base = 10)
{
base <- as.integer(base)
if(base > 36 | base < 2) stop("'base' must be between 2 and 36.")
structure(lapply(b, function(x)
{
n <- ceiling(log(x, base))
vec <- numeric()
val <- x
while(n >= 0)
{
rem <- val %/% base^n
val <- val - rem * base^n
vec <- c(vec, rem)
n <- n - 1
}
while(vec[1] == 0 & length(vec) > 1) vec <- vec[-1]
structure(x, base = base, representation = vec)
}), class = "base")
}
我链接到的文章的标题是“在这个以 60 为底的奇怪世界中,八分之一等于七和三十”- 我想看到这个并将“1 /8" 从十进制转换为六十进制的 "7 和 30"。
有人可以帮我解决这个问题吗?
给定的代码几乎 有效。对 bases < 36 的限制只是因为原作者想用符号 [0-9A-Z] 来表达这些值。删除该限制并扩展算法以允许额外的数字 'after the decimal point' (或 'after the sexagesimal point' 在 base 60 的情况下 :-) )我们得到一些 almost 有效的东西(下面的函数定义):
base(1/8, base = 60, digits = 6)
[[1]]
[1] 0.125
attr(,"base")
[1] 60
attr(,"representation")
[1] 7 29 59 59 59 59
attr(,"class")
[1] "base"
我们得到的不是“7 30”,而是“7 29(59 次重复)”,这类似于进行本应为 0.2 的小数计算却得到 0.1999....
这大概可以通过适当的 'numeric fuzz' 阈值来解决。
代码中缺少的另一件事是,现在它包含小数部分,结果应该 return 信息告诉您 'decimal' 点所在的位置(最简单的,在输出中包含 digits
的值)。
代码的其他方面可以改进(例如 pre-allocating vec
而不是迭代构建)。
base <- function(b, base = 10, digits = 0) {
base <- as.integer(base)
structure(lapply(b, function(x)
{
n <- ceiling(log(x, base))
vec <- numeric()
val <- x
while(n >= -1*digits ) {
rem <- val %/% base^n
val <- val - rem * base^n
vec <- c(vec, rem)
n <- n - 1
}
while(vec[1] == 0 & length(vec) > 1) vec <- vec[-1]
structure(x, base = base, representation = vec)
}), class = "base")
}
也许我们可以试试下面的代码(给定 x < 1
)
f <- function(x, base = 60, digits = 6) {
if (digits == 1) {
return(round(x))
}
p <- x * base
c(p %/% 1, Recall(p %% 1, digits = digits - 1))
}
这给出了
> f(1 / 8)
[1] 7 30 0 0 0 0
> f(1 / 7)
[1] 8 34 17 8 34 0
> f(1/3)
[1] 20 0 0 0 0 0
这是 @BenBolker 的 base
函数的矢量化版本,我称之为 vbase
。它消除了对提供的数字向量 x
的 n
元素的循环,即使对于适度的 n
,这在 R 中也是相当昂贵的。它恰好更通用,因为它同时支持正数和非正数 x
.
vbase
returns 具有 1+digits
行(一个符号位)和 n
列的矩阵。矩阵结果在视觉上很好看,也便于 pattern-finding.
vbase <- function(x, base = 10, digits = 6) {
stopifnot(is.numeric(x), is.finite(x),
is.numeric(base), length(base) == 1L, base >= 2,
is.numeric(digits), length(digits) == 1L, digits >= 1)
n <- length(x)
if (n == 0L) {
return(NULL)
}
base <- as.integer(base)
digits <- as.integer(digits)
m <- 1L + digits
nz <- x != 0
res <- matrix(0L, m, n)
if (any(nz)) {
nz <- which(nz)
y <- abs(x[nz])
e <- ceiling(log(max(y), base))
pow <- base^e
res[1L, ] <- sign(x)
i <- 2L
while (i <= m) {
quo <- y %/% pow
res[i, nz] <- quo
y <- y - quo * pow
pow <- pow / base
i <- i + 1L
}
} else {
e <- 0
}
ech <- sprintf("%d", seq.int(e, by = -1, length.out = digits))
dimnames(res) <- list(EXPONENT = c("sign", ech), ELEMENT = names(x))
attr(res, "x") <- x
attr(res, "base") <- base
attr(res, "digits") <- digits
res
}
x <- 60^(-2:2)
vbase(c(-x, x), base = 60, digits = 5)
ELEMENT
EXPONENT [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
sign -1 -1 -1 -1 -1 1 1 1 1 1
2 0 0 0 0 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 0 0
-1 0 1 0 0 0 0 1 0 0 0
-2 1 0 0 0 0 1 0 0 0 0
attr(,"x")
[1] -2.777778e-04 -1.666667e-02 -1.000000e+00 -6.000000e+01 -3.600000e+03
[6] 2.777778e-04 1.666667e-02 1.000000e+00 6.000000e+01 3.600000e+03
attr(,"base")
[1] 60
attr(,"digits")
[1] 5
vbase(1 / 8, base = 60, digits = 10)
ELEMENT
EXPONENT [,1]
sign 1
0 0
-1 7
-2 29
-3 59
-4 59
-5 59
-6 59
-7 59
-8 59
-9 59
attr(,"x")
[1] 0.125
attr(,"base")
[1] 60
attr(,"digits")
[1] 10
矩阵结果的一个限制是 x
的最大元素(绝对值)决定了使用什么指数序列来表示所有其他元素。例如:
vbase(2^c(-10, 1), base = 2, digits = 2)
ELEMENT
EXPONENT [,1] [,2]
sign 1 1
1 0 1
0 0 0
attr(,"x")
[1] 0.0009765625 2.0000000000
attr(,"base")
[1] 2
attr(,"digits")
[1] 2
此处,2^(-10)
被错误表示,因为指数从 1 开始倒数。您 可以 将 digits
增加到 12 以捕获两个 2^(-10)
和 2^1
,但这样做会在结果中引入许多零,这不是内存的最佳使用方式。也就是说,如果 length(x)
不是很大——当然如果 base = 60
——那么你不必太担心 运行 内存不足。