1

I have a data like this

df <- seq(5,10)

I pick the first number

t <- df[1]

I want to do the following (Collatz sequence):

  • if it is odd then multiple by 3 and +1
  • if the answer is even divide by 2 otherwise multiply by 2
  • and keep doing this until the answer is 1

Examples:

  • 5 is odd then I multiple by 3 +1 will become 16 and 16 is even then I divide by 2 because 8 and 8 is even I divide by 2 become 4 and I divide by 2 become 2 and I divide by 2 become 1. Finished. Move on to the next number.
  • I pick 6, it is even I divide by 2 which become 3 then I multiple by 3 +1 which becomes 10...

I have done this but I don't know how to get it together

if( (df[1]%%2) == 0) {
mydf <- df[1]* 3+1
} else {
mydf <- df[1]/2

I found that this does the job for each number at the time

mydf <-  NULL
n <- 5
while (n != 1) {
  if (n %% 2 == 0) {
    n <- n / 2
  } else {
    n <- (n * 3) + 1
  }
  mydf <- c(mydf, cbind(n))
}

I should always give one number at the time, how can I make it to get numbers one after the other and save the intermediate results as rows?

smci
  • 32,567
  • 20
  • 113
  • 146
nik
  • 2,500
  • 5
  • 21
  • 48
  • 3
    You probably just want a `while` loop. What have you tried? – heds1 Aug 18 '21 at 02:27
  • @heds1 no worse than that :-) I have tried so many things I cannot figure how to get them into a loop – nik Aug 18 '21 at 02:30
  • @Ronak Shah you are correct – nik Aug 18 '21 at 02:32
  • Then why not just `rep(1, length(df))` if that's what you ultimately want. Why the intermediate steps are important? – Ronak Shah Aug 18 '21 at 02:33
  • @Ronak Shah so funny :-D of course it is important otherwise I wouldn't even ask the question. – nik Aug 18 '21 at 02:35
  • @Ronak Shah you are absolutely right, brilliant , I do try to save each multiplication or division but I didn't know how to mention because it sounds very complicated , if you know please please modify my question – nik Aug 18 '21 at 02:42
  • 2
    This is the [tag:collatz] sequence and there are tons of existing Q&A on it. – smci Aug 18 '21 at 02:49
  • @smci I added a better solution but it still dont give me what I am looking for – nik Aug 18 '21 at 02:59
  • Actually the *"if the answer is even... otherwise multiply by 2"* is not Collatz. Is this a modified Collatz-type sequence? – smci Aug 18 '21 at 22:47
  • @smci I am working on something :-D I got a loop that I needed so I am happy already who knows I might solve a big question :-D – nik Aug 18 '21 at 22:58

3 Answers3

3

You could use Recursion function:

fun <- function(x){
  if (x == 1) 1     # if 1 RETURN 1
  else if(x %% 2) Recall(x*3 + 1)    # If odd, multiply by 3 add 1 and repeat
  else Recall(x/2)     # otherwise divide by 2 and repeat
}

Your fun can now work on single digits, ie:

fun(5)
[1] 1

We can then vectorize it to make it work on a vector:

collatz <- Vectorize(fun)
collatz(seq(1, 10))
[1] 1 1 1 1 1 1 1 1 1 1

EDIT:

If you need the intermediate steps:

fun <- function(x){
  if (x[1] == 1) rev(x)
  else if(x[1] %% 2) Recall(c(x[1]*3 + 1, x))
  else Recall(c(x[1]/2, x))
}
collatz <- Vectorize(fun)

collatz(seq(5,10))
[[1]]
[1]  5 16  8  4  2  1

[[2]]
[1]  6  3 10  5 16  8  4  2  1

[[3]]
 [1]  7 22 11 34 17 52 26 13 40 20 10  5 16  8  4  2  1

[[4]]
[1] 8 4 2 1

[[5]]
 [1]  9 28 14  7 22 11 34 17 52 26 13 40 20 10  5 16  8  4  2  1

[[6]]
[1] 10  5 16  8  4  2  1
Onyambu
  • 67,392
  • 3
  • 24
  • 53
1

Using your code in the for loop for each number.

df <- seq(5,10)
mydf <- numeric(length(df))

for(i in seq_along(df)) {
  n <- df[i]
  while (n != 1) {
    if (n %% 2 == 0) {
      n <- n / 2
    } else {
      n <- (n * 3) + 1
    }
  }
  mydf[i] <- n
}

mydf
#[1] 1 1 1 1 1 1

To get the intermediate steps saved as well use a list.

df <- seq(5,10)
mydf <- vector('list', length(df))

for(i in seq_along(df)) {
  n <- df[i]
  mydf[[i]][1] <- n
  while (n != 1) {
    if (n %% 2 == 0) {
      n <- n / 2
    } else {
      n <- (n * 3) + 1
    }
    mydf[[i]] <- c(mydf[[i]], n)
  }
}

mydf
#[[1]]
#[1]  5 16  8  4  2  1

#[[2]]
#[1]  6  3 10  5 16  8  4  2  1

#[[3]]
# [1]  7 22 11 34 17 52 26 13 40 20 10  5 16  8  4  2  1

#[[4]]
#[1] 8 4 2 1

#[[5]]
# [1]  9 28 14  7 22 11 34 17 52 26 13 40 20 10  5 16  8  4  2  1

#[[6]]
#[1] 10  5 16  8  4  2  1
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
  • I am gonna accept your answer but is it possible to have the intermediate as each row ? if not I will accept your answer – nik Aug 18 '21 at 03:08
  • Yes, sure that is possible. See the updated answer I have created list of vectors to save them. – Ronak Shah Aug 18 '21 at 03:14
0

Here is another recursion option

f <- Vectorize(function(x) {
  if (x == 1) {
    return(1)
  }
  c(x, f(if (x %% 2) 3 * x + 1 else x / 2))
})

which gives

> f(c(5:10))
[[1]]
[1]  5 16  8  4  2  1

[[2]]
[1]  6  3 10  5 16  8  4  2  1

[[3]]
 [1]  7 22 11 34 17 52 26 13 40 20 10  5 16  8  4  2  1

[[4]]
[1] 8 4 2 1

[[5]]
 [1]  9 28 14  7 22 11 34 17 52 26 13 40 20 10  5 16  8  4  2  1

[[6]]
[1] 10  5 16  8  4  2  1
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81