将数字从十进制转换为六十进制。

8

最近,我在阅读有关古巴比伦文明的内容,他们所使用的数字系统是以60为基数而不是10。即使使用这种基数为60的数字系统,他们仍然能够在数千年前近似求得根号2!

enter image description here

我对此很好奇,想要了解如何将我们十进制(基数为10)的数字转换成六十进制(基数为60)。我使用R编程语言,在这个链接中找到了一个答案,可以将数字从一种基数转换为另一种基数。

然而,在这里看起来只能在2到36之间选择基数(我需要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” - 我想看到如何将十进制系统中的“1/8”转换为六十进制系统中的“7和30”。请问有人可以帮我吗?

gtools::baseOf 可能会有用(尽管这假设整数,可能需要一些技巧)。 - r2evans
5
就算价值有限,使用Unix内置计算器bc,“echo "obase = 60; 1/8" | bc -l”会得到.07 30 00 00 00 00 00 00 00 00 00 00的结果。我过去也用过这个方法,因为我懒得实现任意精度的进制转换,你可以参考https://rpubs.com/bbolker/4113。 - Ben Bolker
手动完成此操作:https://math.stackexchange.com/questions/1665591/converting-decimal-fractions-to-base-n/1665858 - Waldi
3个回答

11

给出的代码几乎可以工作。限制基数< 36仅存在,因为原始作者希望使用符号[0-9A-Z]来表示值。 除去该限制并将算法扩展到允许在小数点之后添加额外数字(或在基数60的情况下在“sexagesimal点”之后)我们可以得到几乎可以工作的东西(函数定义如下):

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 repeating)",这类似于进行十进制计算应该得到0.2,却得到了0.1999...。

适当设置“数字模糊”阈值似乎可以解决这个问题。

现在代码中缺少的另一部分是,由于它包括小数部分,所以结果应返回提供有关“小数”点位置的信息(最简单的是,在输出中包括 digits 的值)。

代码的其他方面也可以改进(例如,预先分配 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")
}

3
也许我们可以尝试下面的代码(假设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

3
这里是@BenBolker的base函数的向量化版本,我将其称为vbase。它消除了针对提供的数字向量xn个元素的循环,即使对于适度的n,在R中也可能相当昂贵。它比原始函数更通用,因为它支持正数和非正数xvbase返回一个具有1+digits行(一行用于符号位)和n列的矩阵。该矩阵结果易于观察,并方便进行模式查找。
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时,你就不用太担心内存耗尽的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接