7

I want to write a function to reverse the order of any numbers. Here is what I have but it doesn't work. Please help me!

n=123
rev_number=function(n){
 m=strsplit(as.character(n),"")
 if (m==rev(m)) print("reversed number")
}

The desired output is n=321

Cypress
  • 357
  • 1
  • 2
  • 10
  • Could you consider posting sample input and the desired output? Isn't reversing simply concerned with changing the order? – Konrad Mar 06 '16 at 09:48
  • 1
    Although it's probably not the best way, I'd guess you are trying to do `paste(rev(strsplit(as.character(n),"")[[1]]), collapse = "")`. Though as mentioned, it's better to provide a reproducible example and desired output. – David Arenburg Mar 06 '16 at 09:51
  • Thank you guys. I have just updated my question wit input and desired output. – Cypress Mar 06 '16 at 10:30
  • For questions like these, I guess the first place to look is the ["mathematics"](http://math.stackexchange.com/questions/480068/how-to-reverse-digits-of-an-integer-mathematically) community - so, you could use `ff = function(x) x * 10^floor(log10(x)) - 99 * sum(floor(x * 10^(-seq_len(floor(log10(x))))) * 10^(floor(log10(x)) - seq_len(floor(log10(x))))) `; `ff(4568742)`. You could store some computations to avoid repeting them. – alexis_laz Mar 06 '16 at 11:25

5 Answers5

12

I feel like reverse an integer should stay in the integer world instead of getting into the string manipulation world. It seems there isn't a built in function for such task in R, so we can create one, using the Rcpp package for instance. Here's an example

library(Rcpp)
cppFunction('int Reverse_CPP(int x) {
  int reverse = 0;
  while(x != 0) {
        int remainder = x%10;
        reverse = reverse*10 + remainder;
        x/= 10;
    }
  return reverse ; 
}')

Reverse_CPP(1234)
# [1] 4321

And here's a vectorized version

cppFunction('IntegerVector Reverse_CPP2(IntegerVector x) {
  int n = x.size();
  IntegerVector out(n);
  IntegerVector xx = clone(x); // Will need this if you don"t want to modify x in place

  for (int i = 0; i < n; ++i){
    int reverse = 0;
    while(xx[i] != 0) {
       int remainder = xx[i]%10;
       reverse = reverse*10 + remainder;
       xx[i]/= 10;
    }
    out[i] = reverse;
   }

   return out;

}')

Reverse_CPP2(c(12345, 21331, 4324234, 4243))
# [1]   54321   13312 4324234    3424

Note that I had to add IntegerVector xx = clone(x); and hence slow the function drastically (see @alexis_laz comment) as Rcpp will modify the original x by reference otherwise. You don't need that if you are passing a bare vector or if you don't care if the original vector is being modifyied


Some benchmarks against other vectorized string manipulation functions

Stringi <- function(x) as.integer(stringi::stri_reverse(x))

Base <- function(x) {
  as.integer(vapply(lapply(strsplit(as.character(x), "", fixed = TRUE), rev),
                    paste, collapse = "", FUN.VALUE = character(1L)))
}


library(microbenchmark)
set.seed(123)
x <- sample(1e3L:1e7L, 1e5, replace = TRUE)

microbenchmark(
               Base(x),
               Stringi(x),
               Reverse_CPP2(x)
)

# Unit: milliseconds
#            expr        min         lq      mean     median          uq         max neval cld
#         Base(x) 855.985729 913.602215 994.60640 976.836206 1025.482170 1867.448511   100   c
#      Stringi(x)  86.244426  94.882566 105.58049 102.962924  110.334702  179.918461   100  b 
# Reverse_CPP2(x)   1.842699   1.865594   2.06674   1.947703    2.076983    6.546552   100 a  
David Arenburg
  • 91,361
  • 17
  • 137
  • 196
  • Just a note in the second function - if input 'x' is indeed "integer" (I assume a "double" will be automatically coerced before passed to the function) then the original 'x' will be modified. – alexis_laz Mar 06 '16 at 12:26
  • @alexis_laz I'm not sure what you mean. How can you reverse `12.3`? Or in which case your `ff` work and this won't? Also `Reverse_CPP2(as.double(c(12, 24)))` will work fine. While `Reverse_CPP2(as.double(12.3))` indeed will convert to an integer while `ff(12.3)` will return some other result. Either way, I didn't understand your comment. – David Arenburg Mar 06 '16 at 12:30
  • I was refering to the 'typeof's; see -for example- `x1 = 12; Reverse_CPP2(x1); x1; x2 = 12L; Reverse_CPP2(x2); x2` – alexis_laz Mar 06 '16 at 12:34
  • @alexis_laz wow, I have no idea what happened there. Can you point out to some documentation on that matter? So basically saying the function modifies x in place? – David Arenburg Mar 06 '16 at 12:38
  • I guess "Writing R Extensions" warns about modifying input. In this case I believe, "Rcpp" automatically uses (?) sort of `coerceVector` (which returns a different object) when input's 'typeof' does not match the expected, while -when it matches- the same pointer to the R object is manipulated. (I guess in this case just saving `x[i]` to a `tmp` will fix this). – alexis_laz Mar 06 '16 at 12:45
  • @alexis_laz hmm.. So I guess I will need to add `IntegerVector xx = Rcpp::clone(x) ;` or something... – David Arenburg Mar 06 '16 at 12:48
  • No need here to duplicate at first; just save `x[i]` to a temporary "int" in each iteration and modify that. – alexis_laz Mar 06 '16 at 12:57
  • @alexis_laz I found [this](http://stackoverflow.com/questions/11300048/rcpp-pass-by-reference-vs-by-value) where Dirk addresses this issue. I will try your suggestion too, thanks. – David Arenburg Mar 06 '16 at 13:00
2

Here's my attempt by extending your code

n=-123
rev_number=function(n){
  if(n>0)
  return(as.integer(paste0(rev(unlist(strsplit(as.character(n), ""))), collapse = "")))
  else{
    return(-as.integer(paste0(rev(unlist(strsplit(as.character(abs(n)), ""))), collapse = "")))
  }
}
rev_number(n)

If else is to handle negative numbers. First it converts the integer to characters and then uses strsplit to split the number. Then unlist is used to convert the list to vector. Then we can use rev to reverse the vector and collapse the vector using paste. Finally as.integer is used to convert the string in to int.

If you do not wish to convert the number to characters, below one does not need any additional packages but only works with positive integers

reverse_number <- function(n){
rev <- 0

while (n > 0) {
  r <- n %% 10
  rev <- rev * 10 + r
  n <- n %/% 10
}

return(rev)
}
reverse_number(134576)
1

For integers n > 9 this function can be used:

reverse_int <- function(n) {
  t1 <- floor(log10(n))
  t2 <- 0
  for (i in t1:1) t2 <- t2 + floor(n/10^i) * 10^(t1-i)
  return(n*10^t1 - 99*t2)
}
reverse_int(678754)
#[1] 457876

Note that the function is not vectorized; it only takes one parameter n as input.

Community
  • 1
  • 1
RHertel
  • 23,412
  • 5
  • 38
  • 64
1

An R function to reverse numbers based on integer division with successive powers of 10. This came up in a school project related to palindrome numbers.

Reverse_number <- function(x){ 
  n <- trunc(log10(x)) # now many powers of 10 are we dealing with 
  x.rem <- x # the remaining numbers to be reversed 
  x.out <- 0 # stores the output 
  for(i in n:0){
  x.out <- x.out + (x.rem %/% 10^i)*10^(n-i) # multiply and add 
  x.rem <- x.rem - (x.rem %/% 10^i)*10^i # multiply and subtract 
  } 
return(x.out) 
}
Tony Ladson
  • 3,539
  • 1
  • 23
  • 30
1

Here are some other base R options using utf8ToInt or substring

> n <- 123478634

> as.integer(intToUtf8(rev(utf8ToInt(as.character(n)))))
[1] 436874321

> as.integer(paste0(substring(n, nchar(n):1, nchar(n):1), collapse = ""))
[1] 436874321
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81