R语言 计算数字和的矢量化函数

wsxa1bj1  于 2023-03-05  发布在  其他
关注(0)|答案(4)|浏览(132)

我有一个数的向量(例如c(1, 11, 1232, 4221, 2)),我需要每个元素的数字和的对应向量(c(1, 2, 8, 9, 2),在前面的例子中)。
我发现了一些很好的解决方案,最好的(从Digit sum function in R)是:

digitsum <- function(x) sum(floor(x / 10^(0:(nchar(x) - 1))) %% 10)

然而,这个解决方案不是矢量化的,它一次只能处理一个元素。
那么,有没有一种方法可以向量化这个解,并创建一个类似的函数来处理向量?(而不是循环遍历所有元素)

m3eecexj

m3eecexj1#

另一种可能性,灵感来自GitHub的要点,它不需要分割或转换为字符:

dsum <- function(n) ifelse(n < 10, n, n %% 10 + dsum(floor(n / 10)))
dsum(x)
#[1] 1 2 8 9 2

基准(向量大小为100,000):该解决方案是最快的。

set.seed(1)
s <- sample(1e5)
dsum <- function(n) ifelse(n < 10, n, (n %% 10) + dsum(floor(n / 10)))
sap <- function(x) sapply(strsplit(as.character(x),""),function(y){sum(as.numeric(y))})
digitsum <- function(x) vapply(strsplit(as.character(x), "", TRUE), function(x) sum(as.integer(x)), 0L)
bench::mark(Maël = dsum(s), GKi = digitsum(s), user2974951 = sap(s))

#  expression      min  median itr/s…¹ mem_a…² gc/se…³ n_itr  n_gc total…⁴
#  <bch:expr>  <bch:t> <bch:t>   <dbl> <bch:b>   <dbl> <int> <dbl> <bch:t>
#1 Maël         40.9ms  48.6ms   15.3  38.94MB   17.2      8     9   522ms
#2 GKi         320.6ms   331ms    3.02  1.91MB    3.02     2     2   662ms
#3 user2974951 326.1ms 340.3ms    2.94  4.82MB    2.94     2     2   681ms
rt4zxlrg

rt4zxlrg2#

> x=c(1, 11, 1232, 4221, 2)
> sapply(strsplit(as.character(x),""),function(y){sum(as.numeric(y))})
[1] 1 2 8 9 2
bksxznpy

bksxznpy3#

您可以循环最大位数1 + floor(log10(max(x))),使用x %% 10L获取最后一位,使用x %% 10L删除最后一位,而不是循环所有元素

digitsum <- function(x) {
  r <- x %% 10L
  for(i in seq_len(floor(log10(max(x))))) {
    x <- x %/% 10L
    r <- r + x %% 10L
  }
  r
}
digitsum(c(1, 11, 1232, 4221, 2))
#[1] 1 2 8 9 2

使用RCPP可能有助于提高速度。

Rcpp::cppFunction("
Rcpp::IntegerVector sod(const Rcpp::IntegerVector& x) { //sum of digits
  IntegerVector r(no_init(x.size()));
  for(int i=0; i<x.size(); ++i) {
    int s = x[i];
    r[i] = s % 10;
    while(s > 9) {
      s /= 10;
      r[i] += s % 10;
    }
  }
  return r;
}")
sod(c(1, 11, 1232, 4221, 2))
#[1] 1 2 8 9 2

基准(摘自@Maël)

set.seed(1)
s <- sample(1e5)
dsum <- function(n) ifelse(n < 10, n, (n %% 10) + dsum(floor(n / 10)))
sap <- function(x) sapply(strsplit(as.character(x),""),function(y){sum(as.numeric(y))})
digitsum <- function(x) {r <- x %% 10L;
  for(i in seq_len(floor(log10(max(x))))) {x <- x %/% 10L; r <- r + x %% 10L;}
  r}
Rcpp::cppFunction("Rcpp::IntegerVector sod(const Rcpp::IntegerVector& x) {
  IntegerVector r(no_init(x.size()));
  for(int i=0; i<x.size(); ++i) {int s = x[i]; r[i] = s % 10;
    while(s > 9) {s /= 10; r[i] += s % 10;}
  }
  return r; }")
tic <- function(v) unlist(lapply(as.character(v), function(x) sum(utf8ToInt(x) - 48)))
bench::mark(Maël = dsum(s), user2974951 = sap(s), Thomas = tic(s), GKi = digitsum(s), GKi2 = sod(s) )
#  expression       min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total…¹
#  <bch:expr>  <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl> <bch:t>
#1 Maël         15.69ms  18.31ms     42.3    38.91MB     77.3    23    42   543ms
#2 user2974951 134.61ms 154.97ms      5.74    4.82MB     14.3     4    10   697ms
#3 Thomas      166.84ms 172.98ms      5.73    2.29MB     17.2     3     9   524ms
#4 GKi           3.01ms   3.11ms    219.       4.2MB     26.8   114    14   522ms
#5 GKi2        546.48µs 564.06µs   1616.    393.16KB     14.0   809     7   501ms

在这种情况下,C++版本是最快的,使用的内存量最少。

hmtdttj4

hmtdttj44#

您可以尝试utf8ToInt如下

> v <- c(1, 11, 1232, 4221, 2)

> unlist(lapply(as.character(v), function(x) sum(utf8ToInt(x) - 48)))
[1] 1 2 8 9 2

相关问题