如何将十进制(以 10 为底)数字转换为三进制(以 3 为底)
How to convert decimal (base 10) numbers to ternary (base 3)
我想知道是否有办法将十进制数转换为三进制数,因为有一个函数 intToBits
用于转换为二进制数。
其实我需要转换一个字符串如
> S0 <- c("Hello Stac")
以 3 为基数。我想先用
将其转换为十进制
> S01 <- utf8ToInt(S0)
> S01
## [1] 72 101 108 108 111 32 83 116 97 99
然后将结果转换为基数 3。我想得到这样的东西:
> S1
## [1] 2200 10202 11000 11010 11022 1012 10002 11022 10121 10200
您可以使用 cwhmisc::int2B
:
library(cwhmisc)
int2B(utf8ToInt(S0), 3)[[1]] |> as.numeric()
# [1] 2200 10202 11000 11000 11010 1012 10002 11022 10121 10200
为了练习,我想你可以尝试像下面这样编写自己的转换器函数
f <- function(x, base = 3) {
q <- c()
while (x) {
q <- c(x %% base, q)
x <- x %/% base
}
# as.numeric(paste0(q, collapse = ""))
sum(q * 10^(rev(seq_along(q) - 1)))
}
或使用递归
f <- function(x, base = 3) {
ifelse(x < base, x, f(x %/% base) * 10 + x %% base)
}
那么你可以运行
> sapply(utf8ToInt(S0),f)
[1] 2200 10202 11000 11000 11010 1012 10002 11022 10121 10200
不错的编程练习。我对@ThomasIsCoding 的答案进行了向量化处理,以避免对字符串和字符串中的字符进行昂贵的循环。这个想法是循环遍历数字,因为 Unicode 代码点在任何基数中都不超过 21 位数字,而字符向量中的字符总数可以大几个数量级。
下面的函数将字符向量 x
、基数 b
(从 2 到 10)和逻辑标志 double
作为参数。它 returns 一个列表 res
使得 res[[i]]
是一个 nchar(x[i])
长度的向量,给出 x[i]
的基本 b
表示。列表元素是双精度向量或字符向量,具体取决于 double
.
utf8ToBase <- function(x, b = 10, double = TRUE) {
## Do some basic checks
stopifnot(is.character(x), !anyNA(x),
is.numeric(b), length(b) == 1L,
b %% 1 == 0, b >= 2, b <= 10)
## Require UTF-8 encoding
x <- enc2utf8(x)
## Operate on concatenation to avoid loop over strings
xx <- paste(x, collapse = "")
ixx <- utf8ToInt(xx)
## Handle trivial case early
if (length(ixx) == 0L) {
el <- if (double) base::double(0L) else character(0L)
res <- rep.int(list(el), length(x))
names(res) <- names(x)
return(res)
}
## Use common field width determined from greatest integer
width <- as.integer(floor(1 + log(max(ixx, 1), base = b)))
res <- rep.int(strrep("0", width), length(ixx))
## Loop over digits
pos <- 1L
pow <- b^(width - 1L)
while (pos <= width) {
quo <- ixx %/% pow
substr(res, pos, pos) <- as.character(quo)
ixx <- ixx - pow * quo
pos <- pos + 1L
pow <- pow %/% b
}
## Discard leading zeros
if (double) {
res <- as.double(res)
if (b == 2 && any(res > 0x1p+53)) {
warning("binary result not guaranteed due to loss of precision")
}
} else {
res <- sub("^0+", "", res)
}
## Return list
res <- split(res, rep.int(gl(length(x), 1L), nchar(x)))
names(res) <- names(x)
res
}
x <- c(foo = "Hello Stack Overflow!", bar = "Hello world!")
utf8ToBase(x, 2)
$foo
[1] 1001000 1100101 1101100 1101100 1101111 100000
[7] 1010011 1110100 1100001 1100011 1101011 100000
[13] 1001111 1110110 1100101 1110010 1100110 1101100
[19] 1101111 1110111 100001
$bar
[1] 1001000 1100101 1101100 1101100 1101111 100000
[7] 1110111 1101111 1110010 1101100 1100100 100001
utf8ToBase(x, 3)
$foo
[1] 2200 10202 11000 11000 11010 1012 10002 11022 10121 10200
[11] 10222 1012 2221 11101 10202 11020 10210 11000 11010 11102
[21] 1020
$bar
[1] 2200 10202 11000 11000 11010 1012 11102 11010 11020 11000
[11] 10201 1020
utf8ToBase(x, 10)
$foo
[1] 72 101 108 108 111 32 83 116 97 99 107 32 79 118 101
[16] 114 102 108 111 119 33
$bar
[1] 72 101 108 108 111 32 119 111 114 108 100 33
一些注意事项:
为了提高效率,该函数连接 x
中的字符串而不是遍历它们。如果连接超过 2^31-1
字节,它会抛出错误,这是 R 允许的最大字符串大小。
x <- strrep(letters[1:2], 0x1p+30)
log2(sum(nchar(x))) # 31
utf8ToBase(x, 3)
Error in paste(x, collapse = "") : result would exceed 2^31-1 bytes
最大的 Unicode 代码点是 0x10FFFF
。当解释为十进制时,此数字的二进制表示超过 2^53
,因此不能在不损失精度的情况下将其存储在双精度向量中:
x <- sub("^0+", "", paste(rev(as.integer(intToBits(0x10FFFF))), collapse = ""))
x
## [1] "100001111111111111111"
sprintf("%.0f", as.double(x))
## [1] "100001111111111114752"
作为防御措施,如果在 b = 2
和 double = TRUE
时超过 2^53
,函数会发出警告。
utf8ToBase("\U10FFFF", b = 2, double = TRUE)
[[1]]
[1] 1.000011e+20
Warning message:
In utf8ToBase("\U{10ffff}", b = 2, double = TRUE) :
binary result not guaranteed due to loss of precision
utf8ToBase("\U10FFFF", b = 2, double = FALSE)
[[1]]
[1] "100001111111111111111"
我想知道是否有办法将十进制数转换为三进制数,因为有一个函数 intToBits
用于转换为二进制数。
其实我需要转换一个字符串如
> S0 <- c("Hello Stac")
以 3 为基数。我想先用
将其转换为十进制> S01 <- utf8ToInt(S0)
> S01
## [1] 72 101 108 108 111 32 83 116 97 99
然后将结果转换为基数 3。我想得到这样的东西:
> S1
## [1] 2200 10202 11000 11010 11022 1012 10002 11022 10121 10200
您可以使用 cwhmisc::int2B
:
library(cwhmisc)
int2B(utf8ToInt(S0), 3)[[1]] |> as.numeric()
# [1] 2200 10202 11000 11000 11010 1012 10002 11022 10121 10200
为了练习,我想你可以尝试像下面这样编写自己的转换器函数
f <- function(x, base = 3) {
q <- c()
while (x) {
q <- c(x %% base, q)
x <- x %/% base
}
# as.numeric(paste0(q, collapse = ""))
sum(q * 10^(rev(seq_along(q) - 1)))
}
或使用递归
f <- function(x, base = 3) {
ifelse(x < base, x, f(x %/% base) * 10 + x %% base)
}
那么你可以运行
> sapply(utf8ToInt(S0),f)
[1] 2200 10202 11000 11000 11010 1012 10002 11022 10121 10200
不错的编程练习。我对@ThomasIsCoding 的答案进行了向量化处理,以避免对字符串和字符串中的字符进行昂贵的循环。这个想法是循环遍历数字,因为 Unicode 代码点在任何基数中都不超过 21 位数字,而字符向量中的字符总数可以大几个数量级。
下面的函数将字符向量 x
、基数 b
(从 2 到 10)和逻辑标志 double
作为参数。它 returns 一个列表 res
使得 res[[i]]
是一个 nchar(x[i])
长度的向量,给出 x[i]
的基本 b
表示。列表元素是双精度向量或字符向量,具体取决于 double
.
utf8ToBase <- function(x, b = 10, double = TRUE) {
## Do some basic checks
stopifnot(is.character(x), !anyNA(x),
is.numeric(b), length(b) == 1L,
b %% 1 == 0, b >= 2, b <= 10)
## Require UTF-8 encoding
x <- enc2utf8(x)
## Operate on concatenation to avoid loop over strings
xx <- paste(x, collapse = "")
ixx <- utf8ToInt(xx)
## Handle trivial case early
if (length(ixx) == 0L) {
el <- if (double) base::double(0L) else character(0L)
res <- rep.int(list(el), length(x))
names(res) <- names(x)
return(res)
}
## Use common field width determined from greatest integer
width <- as.integer(floor(1 + log(max(ixx, 1), base = b)))
res <- rep.int(strrep("0", width), length(ixx))
## Loop over digits
pos <- 1L
pow <- b^(width - 1L)
while (pos <= width) {
quo <- ixx %/% pow
substr(res, pos, pos) <- as.character(quo)
ixx <- ixx - pow * quo
pos <- pos + 1L
pow <- pow %/% b
}
## Discard leading zeros
if (double) {
res <- as.double(res)
if (b == 2 && any(res > 0x1p+53)) {
warning("binary result not guaranteed due to loss of precision")
}
} else {
res <- sub("^0+", "", res)
}
## Return list
res <- split(res, rep.int(gl(length(x), 1L), nchar(x)))
names(res) <- names(x)
res
}
x <- c(foo = "Hello Stack Overflow!", bar = "Hello world!")
utf8ToBase(x, 2)
$foo
[1] 1001000 1100101 1101100 1101100 1101111 100000
[7] 1010011 1110100 1100001 1100011 1101011 100000
[13] 1001111 1110110 1100101 1110010 1100110 1101100
[19] 1101111 1110111 100001
$bar
[1] 1001000 1100101 1101100 1101100 1101111 100000
[7] 1110111 1101111 1110010 1101100 1100100 100001
utf8ToBase(x, 3)
$foo
[1] 2200 10202 11000 11000 11010 1012 10002 11022 10121 10200
[11] 10222 1012 2221 11101 10202 11020 10210 11000 11010 11102
[21] 1020
$bar
[1] 2200 10202 11000 11000 11010 1012 11102 11010 11020 11000
[11] 10201 1020
utf8ToBase(x, 10)
$foo
[1] 72 101 108 108 111 32 83 116 97 99 107 32 79 118 101
[16] 114 102 108 111 119 33
$bar
[1] 72 101 108 108 111 32 119 111 114 108 100 33
一些注意事项:
为了提高效率,该函数连接
x
中的字符串而不是遍历它们。如果连接超过2^31-1
字节,它会抛出错误,这是 R 允许的最大字符串大小。x <- strrep(letters[1:2], 0x1p+30) log2(sum(nchar(x))) # 31 utf8ToBase(x, 3)
Error in paste(x, collapse = "") : result would exceed 2^31-1 bytes
最大的 Unicode 代码点是
0x10FFFF
。当解释为十进制时,此数字的二进制表示超过2^53
,因此不能在不损失精度的情况下将其存储在双精度向量中:x <- sub("^0+", "", paste(rev(as.integer(intToBits(0x10FFFF))), collapse = "")) x ## [1] "100001111111111111111" sprintf("%.0f", as.double(x)) ## [1] "100001111111111114752"
作为防御措施,如果在
b = 2
和double = TRUE
时超过2^53
,函数会发出警告。utf8ToBase("\U10FFFF", b = 2, double = TRUE)
[[1]] [1] 1.000011e+20 Warning message: In utf8ToBase("\U{10ffff}", b = 2, double = TRUE) : binary result not guaranteed due to loss of precision
utf8ToBase("\U10FFFF", b = 2, double = FALSE)
[[1]] [1] "100001111111111111111"