0% found this document useful (0 votes)
3 views6 pages

3080project2 Loops

The document outlines a lab project for MATH 3080, detailing three problems related to programming in R. Problem 1 involves creating a function to print arguments with new lines, Problem 2 requires writing a logical XOR operator, and Problem 3 focuses on implementing Newton's method for finding roots of functions. The document includes code examples and specifications for each problem, along with testing requirements.

Uploaded by

nhtmai3105
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views6 pages

3080project2 Loops

The document outlines a lab project for MATH 3080, detailing three problems related to programming in R. Problem 1 involves creating a function to print arguments with new lines, Problem 2 requires writing a logical XOR operator, and Problem 3 focuses on implementing Newton's method for finding roots of functions. The document includes code examples and specifications for each problem, along with testing requirements.

Uploaded by

nhtmai3105
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

MATH 3080 Lab Project 2

Amy Nguyen

January 2025

Contents
Problem 1 1

Problem 2 1

Problem 3 2

Problem 1
Write a function that takes a variable number of arguments and prints them with each of the inputs separated
by a new line. (Hint: There is a special character, \n, that represents new lines. The following code should
suggest what to do: cat("hello", "awesome", "world", sep = "\n"))

# Your code here


print_with_newlines <- function(a) {
cat(a, sep = "\n")
}
print_with_newlines(c("hello", "awesome", "world"))

## hello
## awesome
## world

Problem 2
Write an infix operator that represents logical XOR. In logic, x xor y is true if only one of either x or y are
true; if neither are true or both are true, then it’s false. The following function implements XOR:

'%xor%' <- function(x, y) {


(x | y) & (!x | !y)
}

Write the infix operator %xor% that allows for the syntax x %xor% y.

1
# Your code here
TRUE %xor% TRUE

## [1] FALSE

FALSE %xor% TRUE

## [1] TRUE

TRUE %xor% FALSE

## [1] TRUE

FALSE %xor% FALSE

## [1] FALSE

The following should work as anticipated:

TRUE %xor% TRUE # Should be FALSE

## [1] FALSE

FALSE %xor% TRUE # Should be TRUE

## [1] TRUE

TRUE %xor% FALSE # Should be TRUE

## [1] TRUE

FALSE %xor% FALSE # Should be FALSE

## [1] FALSE

Problem 3
Newton’s method is a numerical root-finding technique; that is, given a function f , the objective of the method
is to find an input x such that f (x) = 0. We call such an x a root. The method is iterative. We start with
an initial guess x0 . The algorithm then produces new approximations for the root x via the formula:

f (xn )
xn+1 = xn − .
f ′ (xn )

We need a rule for stopping the algorithm, and we could either stop at some fixed N or when |xn+1 − xn | < ϵ
for some user-selected ϵ > 0. (This represents some tolerable numerical error.)
In this project you will write a function implementing Newton’s method; call the function newton_solver().
Based on the above description this function must take at least the following inputs:

2
• An initial x0 ;
• A function f ;
• The function’s derivative f ′ ;
• A maximum number of iterations N ; and
• A desired numerical tolerance ϵ.

(One may think we need either ϵ or N but in practice we should always have N to ensure the algorithm
terminates.)
We will add additional behavioral constraints to the function.

• There will be a loop where the update algorithm is applied. This loop should terminate immediately if
the numerical tolerance threshold is met; this can be achieved via an if statement and break. But if
the loop hits N iterations, the function should throw a warning.
• f and f ′ should be functions. They should return univariate numeric values. If there ever comes a
time where the input functions don’t return a single number, then newton_solver() should throw an
error.
• It’s possible that f ′ (xn ) could become zero and then a division-by-zero error will occur. newton_solver()
should stop with an error informing the user that the derivative became zero.
• We could have our function return a list with detailed information not just with the obtained root but
also with the value of f at the root or how many iterations of the algorithm went through. But instead,
we will just have the function return the obtained root.
• The maximum number of iterations N should be a positive number; the same should be said for ϵ. If
not, an error should be thrown.

1. Write newton_solver() based on the description above.

#' Newton's Method for Finding Roots


#'
#' Implements Newton's method for finding roots of functions numerically
#'
#' This function implements Newton's method, a numerical root finding technique.
#' Given a function \eqn{f}, its derivative \eqn{f'}, and an initial guess for
#' the root \eqn{x_0}, the function finds the root via the iterative formula
#'
#' \deqn{x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}}
#'
#' @param f The function for which a root is sought
#' @param fprime The function representing the derivative of \code{f}
#' @param x0 The initial guess of the root
#' @param N The maximum number of iterations
#' @param eps The tolerable numerical error \eqn{\epsilon}
#' @return The root of \code{f}
#' @examples
#' f <- function(x) {xˆ2}
#' fprime <- function(x) {2 * x}
#' newton_solver(f, fprime, x0 = 10, eps = 10ˆ(-4))
newton_solver <- function(f, fprime, x0, N = 1000, eps = 10e-4) {

# Your code here


# Checking if the inputs for N and eps are valid
if(N <= 0 || eps <= 0){
stop("N and eps must be postitive")
}

3
x_current <- x0 #Initialize the starting guess

# Loop to apply the Newtons's method iteration for N times


for (i in 1:N){
fx <- f(x_current)
fpx <- fprime(x_current)

# Checking if the derivative is zero


if (fpx == 0){
stop(" The derivative became zero. Cannot continue.")
}

# Compute the next guess using Newton's method formula


x_next <- x_current - fx / fpx

# If the change is less than epsilon, return the current guess


if(abs(x_next - x_current) < eps){
return(x_next)
}
# Update the current guess
x_current <- x_next
}

# If maximum iterations reached, make the warning


warning("Maximum iterations reached without finding a root.")
return(x_current)
}

The following code tests whether newton_solver() works as specified. BEWARE: IF THIS CODE
DOES NOT RUN AS ANTICIPATED OR TAKES LONGER THAN 10 SECONDS TO RUN,
YOU WON’T RECEIVE CREDIT!

# Test code for newton_solver(); DO NOT EDIT


f1 <- function(x) {xˆ2}
fprime1 <- function(x) {2 * x}
fprime2 <- function(x) {0}
fprime3 <- function(x) {1}
f2 <- function(x) {c(1, xˆ2)}
fprime4 <- function(x) {c(1, 2 * x)}
f3 <- function(x) {"oopsie!"}
fprime5 <- function(x) {"dasies!"}

# The following should execute without warning or error


newton_solver(f1, fprime1, x0 = 10, eps = 10e-4) # Should be close to zero

## [1] 0.0006103516

newton_solver(f1, fprime1, x0 = -10, eps = 10e-4) # Should be close to zero

## [1] -0.0006103516

4
# The following code should produce errors if the function was written correctly
newton_solver(f1, fprime2, x0 = 10)

## Error in newton_solver(f1, fprime2, x0 = 10): The derivative became zero. Cannot continue.

newton_solver(f2, fprime1, x0 = 10)

## Error in if (abs(x_next - x_current) < eps) {: the condition has length > 1

newton_solver(f1, fprime4, x0 = 10)

## Error in if (fpx == 0) {: the condition has length > 1

newton_solver(f3, fprime1, x0 = 10)

## Error in fx/fpx: non-numeric argument to binary operator

newton_solver(f1, fprime5, x0 = 10)

## Error in fx/fpx: non-numeric argument to binary operator

# The following code should produce warnings if the function was written
# correctly
newton_solver(f1, fprime3, x0 = 10, N = 2)

## Warning in newton_solver(f1, fprime3, x0 = 10, N = 2): Maximum iterations


## reached without finding a root.

## [1] -8190

2. Use newton_solver() to maximize the function g(x) = 1 − x2 . Simple calculus should reveal that
the maximum is g(0) = 1. Maximizing g requires finding a root for g ′ , since the maxima/minima of
differentiable functions occurs where g ′ (x) = 0. Compare the answer obtained by newton_solver() to
the known analytical result.

# Your code here

# Function to maximize
g <- function(x) {1 - xˆ2}

# Derivative of the function g(x)


g_prime <- function(x) {-2*x}

# Aplly Newton's solver to find the root of g'


root <- newton_solver(g, g_prime, x0 = 10, N = 100, eps = 1e-4)

# Print the root


print(root)

## [1] 1

5
# Calculating the maximum value of g at the root
max_value <- g(root)
print(max_value)

## [1] -2.79794e-11

3. Use newton_solver() to solve the equation:

ex = −x

(This equation doesn’t have a known analytical solution.)

# Your code here

# Defining the function and its derivative for solving eˆx = -x


f <- function(x) {exp(x) + x}
f_prime <- function(x) {exp(x) + 1}

root <- newton_solver(f, f_prime, x0 = -1, N = 100, eps = 1e-4)

print(root)

## [1] -0.5671433

print(f(root))

## [1] 0

You might also like