What is the purpose of b <-c() in this code? - r

I have written a code to find a positive integer that has more divisors than any smaller positive integer has. My code is right but I noticed that I wrote a step only because I had solved other questions similarly but I don't really understand the intuition of why we write this particular line:
b <- c()
Also, why is there a "b" in c(b, sum..) as in the below line:
b <- c(b, sum(p %% c(1:p) == 0))
Here is the full code:
code <- function(n) {
if (n < 1 | n %% 1 != 0)
print("Only positive integers allowed")
else if (n <= 2)
return(TRUE)
else {
a <- sum(n %% c(1:n) == 0)
b <- c()
for (p in 1:(n-1)) {
b <- c(b, sum(p %% c(1:p) == 0))
}
return(max(b) < a)
}
}
code(8)
code(6)
code(-7)

As already explained in comments the purpose of b<-c() is to initialise an empty vector and fill it in the loop. Also the reason why you are using b <- c(b,sum(p%%c(1:p)== 0)) is to append new values to already existing values of b.
For example,
b <- c()
b
#NULL
b <- c(b, 1)
b
#[1] 1
b <- c(b, 2)
b
#[1] 1 2
Usually, it is not a good practice to grow an object in a loop, it is highly inefficient to do that. If the size of output is fixed you can initialise a vector with fixed size and then fill it in the loop.
code <- function(n){
if (n<1 | n%%1!=0)
print("Only positive integers allowed")
else if (n <= 2)
return(TRUE)
else{
a <- sum(n%%c(1:n) == 0)
b <- integer(n-1) #Creates a vector with 0's of length n-1
for (p in 1:(n-1)) {
b[p] <- sum(p%%c(1:p)== 0)
}
return(max(b) < a)
}
}
Or in this case you can save only the max value of b since all other values are not important.
code <- function(n){
if (n<1 | n%%1!=0)
print("Only positive integers allowed")
else if (n <= 2)
return(TRUE)
else{
a <- sum(n%%c(1:n) == 0)
max_b <- 0
for (p in 1:(n-1)) {
val <- sum(p%%c(1:p)== 0)
if(val > max_b) max_b <- val
}
return(max_b < a)
}
}

Related

R: cumulative sum until certain value

I want to calculate how many values are taken until the cumulative reaches a certain value.
This is my vector: myvec = seq(0,1,0.1)
I started with coding the cumulative sum function:
cumsum_for <- function(x)
{
y = 1
for(i in 2:length(x)) # pardon the case where x is of length 1 or 0
{x[i] = x[i-1] + x[i]
y = y+1}
return(y)
}
Now, with the limit
cumsum_for <- function(x, limit)
{
y = 1
for(i in 2:length(x)) # pardon the case where x is of length 1 or 0
{x[i] = x[i-1] + x[i]
if(x >= limit) break
y = y+1}
return(y)
}
which unfortunately errors:
myvec = seq(0,1,0.1)
cumsum_for(myvec, 0.9)
[1] 10
Warning messages:
1: In if (x >= limit) break :
the condition has length > 1 and only the first element will be used
[...]
What about this? You can use cumsum to compute the cumulative sum, and then count the number of values that are below a certain threshold n:
f <- function(x, n) sum(cumsum(x) <= n)
f(myvec, 4)
#[1] 9
f(myvec, 1.1)
#[1] 5
You can put a while loop in a function. This stops further calculation of the cumsum if the limit is reached.
cslim <- function(v, l) {
s <- 0
i <- 0L
while (s < l) {
i <- i + 1
s <- sum(v[1:i])
}
i - 1
}
cslim(v, .9)
# [1] 4
Especially useful for longer vectors, e.g.
v <- seq(0, 3e7, 0.1)

Question about passing sapply index to function

I am sure this is a simple problem. But I am new to programming so I am struggling. I think what I am trying to accomplish should be pretty clear from the code. Essentially, I want to generate a vector of random numbers of length i, check if there is less than i unique numbers. And I want to do this a bunch of times as a sort of simulation. When I do it i by i manually using the following code:
experiment<- function() {
ab <- rdunif(i, 1, 365)
ab <- data.frame(ab)
count <- uniqueN(ab)
if (count < i)
return(1)
else
return(0)
}
vector <- replicate(10, experiment(), simplify=FALSE)
sum <- sum(as.data.frame((vector)))
probability <- sum/(10)
It works fine. But I need to run this simulation 40 times and I would rather not do it by hand. However, I can't seem to get sapply to work for me and I cannot figure out what I am doing wrong:
i<-10:50
experiment<- function(i) {
ab <- rdunif(i, 1, 365)
ab <- data.frame(ab)
count <- uniqueN(ab)
if (count < i)
return(1)
else
return(0)
}
complete <- function(i) {
vector <- replicate(10, experiment(i), simplify=FALSE)
sum <- sum(as.data.frame((vector)))
probability <- sum/(10)
return(probability)
}
sapply(i, complete(i), simplify=FALSE)
This is the error I am currently experiencing:
Error in match.fun(FUN) :
'complete(i)' is not a function, character or symbol
In addition: Warning messages:
1: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
2: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
3: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
4: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
5: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
6: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
7: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
8: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
9: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
10: In if (count < i) return(1) else return(0) :
the condition has length > 1 and only the first element will be used
I figured it out:
experiment<- function(i) {
ab <- rdunif(i, 1, 365)
count <- length(unique(ab))
if (count < i) return(1)
else return(0)
}
i <- 10:50
replication <- function(i) {
replicate(100, experiment(i))
}
data<- sapply(i, replication)
colMeans(data)

Why am I getting 46 errors with this for loop?

I am looking for all the even numbers from 1 through 100.
n <- seq(from = 1, to = 100)
for (x in n) {if(n %% 2 == 0) print(n)}
Here, we need 'x' instead of 'n' as 'n' is the whole vector
for(x in n) if(x %% 2 == 0) print(x)
data
n <- seq(from = 1, to = 100)
As Akrun has already pointed out, you needed x %% 2, not n %% 2... note I've use i
n <- 1:100
for(i in n){
if(i %% 2 == 0){
print(i)
}
}
However, if you want the even numbers in your sequence you could just use:
n[n %% 2 == 0]
Note also that you got warnings not errors, they're subtly different

Function for simulation game in R

I have a classic dice simulation problem, which I'm struggling to implement since I'm new with R syntax. The function (which I have called simu) works as follows:
Start with 0 points
Simulate n random draws of three six-sided dice
For each draw:
If sum of three dice >12 --> +1 point
If sum of three dice <6 --> -1 point
Otherwise (ie sum between 6 and 12):
If three dice have same number --> +5 points
Otherwise --> 0 points
Return total # of points obtained at the end of n simulations
Having tried a number of different methods I seem to be pretty close with:
simu <- function(n){
k <- 0
for(i in 1:n) {
a <- sample(y,1,replace=TRUE)
b <- sample(y,1,replace=TRUE)
c <- sample(y,1,replace=TRUE)
if ((a + b + c) > 12) {
k <- k+1
} else if ((a + b + c) < 6) {
k <- k-1
} else if ((a == b) & (b == c)) {
k <- k+5
} else k <- 0
}
return(k)
}
The problem seems to be that I am failing to iterate over new simulations (for a, b, c) for each "i" in the function.
I have commented the only issue I have found... The last else that always re-initialize k to 0. Instead it should have been k <- k + 0 but it does not change anything to remove it.
y <- seq(1,6) # 6-sided dice
simu <- function(n){
k <- 0
for(i in 1:n) {
a <- sample(y,1,replace=TRUE)
b <- sample(y,1,replace=TRUE)
c <- sample(y,1,replace=TRUE)
if ((a + b + c) > 12) {
k <- k+1
} else if ((a + b + c) < 6) {
k <- k-1
} else if ((a == b) & (b == c)) {
k <- k+5
} #else k <- 0
}
return(k)
}
The results look quite fine :
> simu(1000)
[1] 297
> simu(100)
[1] 38
If you are going to use R, then you should learn to create vectorized operations instead of 'for' loops. Here is a simulation of 1 million rolls of the dice that took less than 1 second to calculate. I am not sure how long the 'for' loop approach would have taken.
n <- 1000000 # trials
start <- proc.time() # time how long it takes
result <- matrix(0L, ncol = 6, nrow = n)
colnames(result) <- c('d1', 'd2', 'd3', 'sum', 'same', 'total')
# initial the roll of three dice
result[, 1:3] <- sample(6L, n * 3L, replace = TRUE)
# compute row sum
result[, 'sum'] <- as.integer(rowSums(result[, 1:3]))
# check for being the same
result[, 'same'] <- result[,1L] == result[, 2L] & result[, 2L] == result[, 3L]
result[, 'total'] <- ifelse(result[, 'sum'] > 12L,
1L,
ifelse(result[, 'sum'] < 6L,
-1L,
ifelse(result[, 'same'] == 1L,
5L,
0L
)
)
)
table(result[, 'total'])
-1 0 1 5
46384 680762 259083 13771
cat("simulation took:", proc.time() - start, '\n')
simulation took: 0.7 0.1 0.8 NA NA
I am not sure that's what you need, but you can try something like that:
# Draw the dice(s) - returns vector of length == n_dices
draw <- function(sides = 6, dices = 3){
sample(1:sides, dices, replace = T)
}
# test simulation x and return -1, 0, 1, 1 or 5
test <- function(x){
(sum(x) > 12)*1 + (sum(x) < 6)*(-1) + (sum(x) >= 6 &
sum(x) <= 12 &
var(x) == 0)*5
}
# simulate n draws of x dices with y sides
simu <- function(sides = 6, dices = 3, n = 100){
sum(replicate(n, test(draw(sides, dices))))
}
# run simulations of 100 draws for 1, 2, ..., 11, 12-side dices (3 dices each simulation)
dt <- lapply(1:12, function(side) replicate(100, simu(side, 3, 100)))
# plot dicstribution of scores
par(mfrow = c(3,4))
lapply(1:length(dt), function(i) hist(dt[[i]],
main = sprintf("%i sides dice", i),
xlab = "Score"
)
)

How to pass variables by reference in R?

I would like to ask how I can overwrite the variables v and ind in the following function:
repcomb <- function(v,n,ind)
{
k <- length(v)
if(ind == 0)
{
for (i in 1:k) v[i] <- 1
ind <- 1
return
}
for (i in k:1)
{
if(v[i] != n)
{
for (j in k:i) v[j] <- v[i] + 1
return
}
}
ind = 0
}
What is the easiest way for updating v and ind?
This is the easiest way:
repcomb <- function(v,n,ind)
{
k <- length(v)
if(ind == 0)
{
for (i in 1:k) v[i] <- 1
ind <- 1
return(list(v=v, ind=ind))
}
for (i in k:1)
{
if(v[i] != n)
{
for (j in i+1:k) v[j] <- v[i] + 1
v[i] <- v[i] + 1
return(list(v=v, ind=ind))
}
}
ind = 0
return(list(v=v, ind=ind))
}
res <- repcomb(1:5, 4, 2)
v <- res$v
ind <- res$ind
If you want to get back the value of a parameter set inside a function, you can use eval.parent(substitute(val<-new_val)), for example:
f_sqr <- function(val){
new_val <- val^2
eval.parent(substitute(val<-new_val))
}
If you call it:
val <- 5
f_sqr(val)
val
#[1] 25
Please bear in mind that you should not change the value of the parameter inside the function, instead, you copy it to a new variable, do what you want to do in your code and finally set the value of new variable into your parameter variable as it is the case inside the function.
For your own function, this is what you have to do for your first if:
repcomb <- function(v, n, ind)
{
k <- length(v)
if (ind == 0)
{
new_v <- v
for (i in 1:k) new_v[i] <- 1
# ind <- 1
new_ind <- 1
eval.parent(substitute(ind<-new_ind))
eval.parent(substitute(v<-new_v))
}
}
Then if you call it, you will get the changes back:
v <- 1:5
n <- 3
ind <- 0
repcomb(v, n, ind)
v
#[1] 1 1 1 1 1
ind
#[1] 1
Correspondingly, the other part can be changed to meet what you want.

Resources