How do I vectorize this is_prime function in R? - r

I have created this function that takes number and return TRUE or FALSE depending whether the number is prime or not.
is.prime <- function(num) {
if (num == 2) {
TRUE
} else if (any(num %% 2:(num-1) == 0)) {
FALSE
} else {
TRUE
}
}
However, this function takes only one value, for example this works fine:
> is_prime(17)
[1] TRUE
If I plug in a vector, I want to see TRUE or FALSE for each element. For example,
> is_prime(c(17,5,10,22,109,55))
[1] TRUE
Warning messages:
1: In if (x == 1) { :
the condition has length > 1 and only the first element will be used
2: In 2:(floor(x/2)) :
numerical expression has 6 elements: only the first used
3: In x%%2:(floor(x/2)) :
longer object length is not a multiple of shorter object length
This is evaluated for the first element, but I would want to see
TRUE TRUE FALSE FALSE TRUE FALSE
for the vector
is_prime(c(17,5,10,22,109,55))
How would I modify the function to vectorize using the same algorithm?

Half Vectorized
It is possible to vectorize some of the function by dealing with even numbers (and a few other numbers) in a vectorized fashion. The rest is taken care of using vapply.
helper <- function(x) {
for (k in seq(3, round(sqrt(x)) + 1, 2)) {
if (x %% k == 0)
return(FALSE)
}
return(TRUE)
}
is.prime <- function(v) {
out <- rep(TRUE, length(v))
out[v %% 2 == 0 | v %in% c(1)] <- FALSE
out[v %in% c(2, 3, 5)] <- TRUE
indices <- which(v > 5 && v == FALSE)
out[indices] <- vapply(v[indices], helper, logical(1))
return(out)
}
is.prime(c(17,5,10,22,109,55))
# [1] TRUE TRUE FALSE FALSE TRUE FALSE
Full Vectorized
If performance is at stake, you might consider using `Rcpp`:
c++ file
#include <Rcpp.h>
#include <math.h>
using namespace Rcpp;
bool is_prime(int n) {
if ((n == 2) || (n == 3) || (n == 5)) {
return true;
}
if ((n % 2 == 0) || (n == 1)) {
return false;
}
int i = 3;
while (i < round(sqrt(n)) + 1) {
if (n % i == 0) {
return false;
}
i += 2;
}
return true;
}
// [[Rcpp::export]]
LogicalVector is_prime(IntegerVector v) {
int n = v.length();
LogicalVector out = LogicalVector(n);
for (int i = 0; i < n; i++) {
out[i] = is_prime(v[i]);
}
return out;
}
R File
library(Rcpp)
sourceCpp('prime_fun.cpp') # if cpp file in same dir
is_prime(c(17,5,10,22,109,55))
# [1] TRUE TRUE FALSE FALSE TRUE FALSE

Technically, you cannot vectorize the function but you can use methods to apply is.prime function to vectors.
For example, use sapply :
sapply(c(17,5,10,22,109,55), is.prime)
#[1] TRUE TRUE FALSE FALSE TRUE FALSE
Or use Vectorize which is a wrapper around mapply :
vec.is.prime <- Vectorize(is.prime)
vec.is.prime(c(17,5,10,22,109,55))
#[1] TRUE TRUE FALSE FALSE TRUE FALSE

R is an inherently vectorized language, you just have to not break that when you write functions...
is.prime <- function(num){
res<-rep(TRUE, length(num))
for(i in 2:(max(num)-1) ){
res[(i < num) & ((num %% i) == 0) ] <- FALSE
}
return(res)
}
x<-c(2, 5, 17, 109, 10, 22, 55, 93)
print("Primes:")
print(x[ is.prime(x) ] )
Output:
> source('~/.active-rstudio-document')
[1] "Primes:"
[1] 2 5 17 109

Related

If statement condition error missing value in two sum problem?

I am working through practice problems for interviews and ran into a problem with an "if" statement that I do not know how to solve. The practice question is as follows:
Two sum. Given an array and a number N, return True if there are numbers A, B in the array such that A + B = N. Otherwise, return False.
Example:
[1, 2, 3, 4], 5 ⇒ True
[3, 4, 6], 6 ⇒ False
My code (below) returns the following error: Error in if (i + A[j] == N) { : missing value where TRUE/FALSE needed
fun = function(A,N) {
flag = FALSE
for (i in A) {
for (j in i:length(A)) {
if (i + A[j] == N) {
flag = TRUE
}
}
}
return(flag)
}
fun(as.integer(c(3, 4, 6)),6)
The problem in your code is your second for loop should be 1:length(A) not i:length(A).
fun <- function(A, N){
flag = FALSE
for (i in A) {
for (j in 1:length(A)) {
if (i + A[j] == N) {
flag = TRUE
}
}
}
return(flag)
}
fun(as.integer(c(3, 4, 6)), 6)
[1] TRUE
When i equals 4 and length(A) is fixed at 3 your posted code was sequencing 4:3. j will take on a value of 4 and then when you try to index A[4] you will get NA.
Update
To avoid double counting an element with your code you could do this:
fun <- function(A, N){
flag = FALSE
for (i in A) {
for (j in A[A != i]) { # select elements of A that aren't equal to i
if (i + j == N) {
flag = TRUE
}
}
}
return(flag)
}
Though this isn't super efficient because it will double check some pairs twice.
A vectorized solution without loops:
sumN <- function(A,N){
any(colSums(combn(A,2))==N)
}
sumN(c(1,2,3,4),5)
[1] TRUE
sumN(c(3,4,6),6)
[1] FALSE

IF ELSE function works in simple case, but my expanded function does not

I am trying to create functions that evaluates numbers and adds them to a category dependent on set criteria.
I wrote a "stupid" function with a lot of repetitions and lines, that can solve the job in a simple case:
# Findgroup (Everything manually typed out)
# Purpose of function:
#If 1 or 2: output A
#If 3 or 4: output B
#If 5 or 6: output random A/B (bonus if this can be balanced equally over the dataset)
# Else output "error"
findGroup <- function(x){ if (x == 1) {
"A"
} else if (x == 2) {
"A"
} else if (x == 3) {
"B"
} else if (x == 4) {
"B"
}else if (x == 5) {
sample(c("sA","sB"),1)
}else if (x == 6) {
sample(c("sA","sB"),1)
} else {
"Error"
}}
# Brief test: All matches expectations
findGroup(1) # Returns A
findGroup(3) # Returns B
findGroup(5) # Samples
findGroup(7) # Returns Error
This is okay, if I had to evaluate a few numbers. But what should I do if the list of numbers is much more elaborate? I tried to write a function that solves this in fewer lines, but the result does not work:
# Findgroup new
# Purpose of function:
#If 2:8: output A
#If 10:16: output B
#If 1,9: output random A/B (bonus if this can be balanced equally)
findGroupNew <- function(x){ if (x == 2|3|4|5|6|7|8) {
"A"
} else if (x == 10|11|12|13|14|15|16) {
"B"
} else if (x == 1||9) {
sample(c("sA","sB"),1)
} else {
"Error"
}}
# Brief test: All return A!!!
findGroupNew(1) # Should sample
findGroupNew(3) # Should Return A
findGroupNew(11) # Should Return B
findGroupNew(17) # Should Return Error
It may be a stupid mistake such as not having used the right sign for OR, but having tried solutions such as using || and & has not been successful.
I hope there is a quick fix to this issue and will appreciate your feedback.
Use %in% to check for multiple values.
findGroupNew <- function(x) {
if (x %in% 2:6) {
return("A")
} else if (x %in% 10:16) {
return("B")
} else if (x %in% c(1, 9)) {
return(sample(c("sA","sB"),1))
} else {
return("Error")
}
}
findGroupNew(1)
#[1] "sB"
findGroupNew(3)
#[1] "A"
findGroupNew(11)
#[1] "B"
findGroupNew(7)
#[1] "Error"

How to create a loop that returns the first N prime numbers in a vector?

i've been trying for a while now to write a function that has two arguments. The one an integer = n and the other a logical argument. How would i write a function that would return either the first n prime numbers in a vector if the logical argument is true or the n th prime number if the logical argument is false?
This is as far as i have gotten.
getprime <- function(n=0 , all=TRUE) {
if (n<=0) {
print("Not a valid number")
} else if (n>0) {
for (primen in 1:n) {
while (n %% 2:(n-1) == 0) {
n=n+1
print(n)
}
}
}
print(n)
}
The results that have to be displayed are shown below.
> genprime(7, all=TRUE)
[1] 2 3 5 7 11 13 17
> genprime(7, all=FALSE)
[1] 17
You could write a recursive function (a function that calls itself). In addition to the two arguments n and all, it would also take a counter i and a (empty) vector of prime numbers primes.
As long as the number of prime numbers found is less than n, the function calls itself and with each time it calls itself, it would increase the counter variable i. If i is a prime number, it gets added to the primes vector.
get_prime <- function(n, all = TRUE, i = 1, primes = c()){
if ( n <= 0) {
stop("Not a valid number")
}
if (length(primes) < n) {
if (i == 2L || all(i %% 2L:ceiling(sqrt(i)) != 0)) {
get_prime(n, all = all, i = i + 1, primes = c(primes, i))
} else {
get_prime(n, all = all, i = i + 1, primes = primes)
}
} else {
if (all) {
return(primes)
} else {
return(tail(primes, 1))
}
}
}
The results are:
get_prime(7, TRUE)
[1] 2 3 5 7 11 13 17
get_prime(7, FALSE)
[1] 17

Question regarding R code to do with rounding prime numbers

I need to have a function where if a number entered is a prime number, it must round it up to the next prime number and if it is not to round it down to the previous prime number.
I have this code to identify whether it is a prime number:
prime <- function(x) {
if (x == 2) {
print(3)
} else if (any(x %% 2:(x-1) == 0)) {
FALSE
} else {
TRUE
}
}
I want to add a while loop to the true and false where if the function is false, it must minus one until it is true and if it is true, it must add one until it is true again but I am not sure how to do this.
Using your prime checker,
prime <- function(x) {
if (x == 2) {
print(3)
} else if (any(x %% 2:(x-1) == 0)) {
FALSE
} else {
TRUE
}
}
We build the new function, we first check if our number is a prime, if so, add 1 until we reach the next prime. If it is not a prime, we minus 1 until it is.
new <- function(x){
if (isTRUE(prime(x))){
x = x+1
while(prime(x) == FALSE){
x = x+1
}
return(x)
} else {
while(prime(x) == FALSE){
x = x-1
}
return(x)
}
}
we get
> new(7)
[1] 11
> new(10)
[1] 7

Loop returns error: 'argument is of length zero'

i <- 2
j <- 0
for (i in 2:1000) {
if(return.prime(i)){j = j + 1}
i = i + 1
}
I want to check how many prime numbers there are in 1 to 1000 using my own function return.prime which returns TRUE when the number input is a prime and FALSE when the number input is not prime. The return.prime function is the function below and it is correct.
return.prime <- function(d){
if(d ==1 ){print(FALSE)}
if (d == 2){
print(TRUE)
}
if(d !=2 && d!=1){
if(any(d %% (2:(d-1)) == rep(0,d-2))==TRUE){
print(FALSE)}
else
print(TRUE)
}
}
The problem is when I run my program it says:
[1] TRUE
Error in if (return.prime(i)) { : argument is of length zero
I do not know what causes the length zero.
R doesn't work that way. You're just having the function print the word "TRUE" or "FALSE". Instead, you need to ?return TRUE or FALSE. Consider:
return.prime <- function(d){
if(d==1){ return(FALSE) }
if(d==2){ return(TRUE) }
if(d !=2 && d!=1){
if(any(d %% (2:(d-1)) == rep(0,d-2))==TRUE){
return(FALSE)
} else{
return(TRUE)
}
}
}
i <- 2
j <- 0
for (i in 2:1000) {
if(return.prime(i)){j = j + 1}
i = i + 1
}
j # [1] 168

Resources