将数字从 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。它消除了对提供的数字向量 xn 元素的循环,即使对于适度的 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——那么你不必太担心 运行 内存不足。