CS 171 & 171L Introduction to Computer Science I & Lab Fall 2020 

Lab 9: Polynomials

In this lab you will familiarize yourself with the java representation of objects and arrays by working with a class which supports polynomial functions.

Polynomial functions

A polynomial \(p\) is a function of the form \[ \begin{align} p(x) &= a_nx^n + a_{n-1}x^{n-1} + \cdots + a_i x^i + \cdots + a_2 x^2 + a_1 x + a_0 \\ &= \underbrace{a_0 + a_1 x + a_2 x^2 + \cdots + a_i x^i + \cdots + a_{n-1}x^{n-1} +a_nx^n}_{(n+1) \text{ terms}} \\ &= \sum_{i=0}^n a_i x^i \quad(\text{recall } x^0 = 1) \end{align} \] where the values \(a_0\), \(a_1\), \(\ldots\), \(a_n \ne 0\) are constants called the coefficients of \(p\), with \(a_i\) being the \(i\)th coefficient. Here we will assume the constants are real numbers, but they could be complex numbers or other mathematical entities. The degree of \(p\) is \(n\).

The zero polynomial is just \(p(x) = 0\) and its degree is undefined (or sometimes -1).

Negating a polynomial multiplies all coefficients by -1. \[\begin{align} -p(x) &= -a_0 + -a_1 x + -a_2 x^2 + \cdots + -a_i x^i + \cdots + -a_{n-1}x^{n-1} + -a_nx^n \\ &= -a_0 - a_1 x - a_2 x^2 - \cdots - a_i x^i - \cdots - a_{n-1}x^{n-1} - a_nx^n. \end{align}\]

Mulitplying a polynomial by a constant \(b\) multiplies all coefficients by \(b\). \[\begin{align} b p(x) &= b a_0 + b a_1 x + b a_2 x^2 + \cdots + b a_i x^i - \cdots + b a_{n-1}x^{n-1} + b a_nx^n. \end{align}\]

In evaluating the a polynomial \(p\), you should try and be as efficient as possible. If you compute all the powers of \(x\) and don't save any intermediate results, that is not very efficient. Instead use an additional variable to store \(x^i\) and just multiply by \(x\) every pass of the loop. Another way is called Horner's method, which is related to synthetic division.

Given two polynomials p and q with degrees \(n\) and \(m\) respectively with \(n < m\). Thus \[p(x) = a_0 + a_1 x + a_2 x^2 + \cdots + a_i x^i + \cdots + a_{n-1}x^{n-1} + a_nx^n \] and \[q(x) = b_0 + b_1 x + b_2 x^2 + \cdots + b_i x^i + \cdots + b_{n-1}x^{n-1} + b_nx^n + \cdots + b_{m-1}x^{m-1} + b_mx^m. \] then \[ p(x) + q(x) = (a_0 + b_0) + (a_1 + b_1) x + (a_2 + b_2) x^2 + \cdots + (a_{n}+ b_{n}) x^{n} + b_{n+1} x^{n+1} + \cdots + b_{m-1}x^{m-1} + b_mx^m. \] Multiplying two polynomials is more challenging. \[ r(x) = p(x)q(x) = a_0 q(x) + a_1 q(x) + \cdots + a_n q(x) \] Note that the resulting polynomial \(r\) has degree \(m+n\) with \(c_i\) being the \(i\)th coefficient of \(r\), which results from adding all \(a_j\) and \(b_k\) such that \(i = j+k\), so \[c_i = a_0 b_i + a_1 b_{i-1} + \cdots + a_{i-1} b_1 + a_i b_0 = \sum_{j = 0}^i a_j b_{(i-j)}.\] You will need two nested loops for this.

The derivative of a polynomial, denoted \(p'(x)\) is given by \[p'(x) = a_1 + 2 a_2 x + 3 a_3 x^2 + \cdots + i a_i x^{i-1} + \cdots + n a_n x^{n-1} = \sum_{i=1}^{n} i a_{i} x^{i-1}.\] This new polynomial has degree \(n-1\). Note the limits on the summation.

The indefinite integral of a polynomial, denoted by \(P(x)\) or \(\int p(x)\,dx\), is given by \[P(x) = \int p(x)\,dx = C + a_0 x + \dfrac{a_1}{2} x^2 + \dfrac{a_2}{3} x^3 + \cdots + \dfrac{a_{i-1}}{i} x^{i} + \cdots + \dfrac{a_{n}}{n+1} x^{n+1} = C + \sum_{i=1}^{n+1} \dfrac{a_{i-1}}{i} x^{i}. \] This new polynomial has degree \(n+1\). In our case we are assuming \(C = 0\). Note the limits on the summation.

The definite integral of the function \(p\) from \(x_0\) to \(x1\) , is given by \[\int_{x_0}^{x_1} p(x)\,dx = P(x_1) - P(x_0) \] where \(P(x)\) is the indefinite integral defined above.

Task

Complete the methods in Polynomial.pde, as shown below. After you complete each method, demonstrate it to your instructor. Change the driver/navigator roles after each method.

// Lab 9

// Globals to allow persistence

// setting bounds on our "world" coordinates
float xmin = -10;
float xmax = 10;
float ymin = -10;
float ymax = 10;

// screen bound coordinates
float umin = 0;
float umax = 900;
float vmin = 900;
float vmax = 0;


Polynomial p[] = new Polynomial[10];


void settings() {
  size(900, 900);
}

void setup() {
  reset();
  testing();
  noLoop(); // no animation
}

void reset() {
  // setting bounds on our "world" coordinates
  xmin = -10;
  xmax = 10;
  ymin = -10;
  ymax = 10.0;
}

void testing() {
  int n = 0;
  p[n++] = new Polynomial();                                   // p(x) = 0;
  p[n++] = new Polynomial(new double[] {1.0});                 // p(x) = 1;
  p[n++] = new Polynomial(new double[] {2.0, -1.0});           // p(x) = -x + 2;
  p[n++] = new Polynomial(new double[] {6.0, 5.0, 1.0});       // p(x) = x^2 + 5x + 6;
  p[n++] = new Polynomial(new double[] {1.0, 5.0, 7.0, 3.0});  // p(x) = 3x^3 + 7x^2 + 5x + 1

  for (int i = 0; i < n; i++) {
    System.out.println("p(x) = " + p[i]);
    System.out.println("degree: " + p[i].degree());
    Polynomial r = new Polynomial(p[i]);
    System.out.println("r(x) = " + r);
    System.out.println("degree: " + r.degree());

    System.out.printf("p(%f) = %f = %f\n", 0.0, p[i].f(0.0), p[i].coeff(0));
    System.out.printf("p(%f) = %f\n", 1.0, p[i].f(1.0));
    System.out.printf("p(%f) = %f\n", -1.0, p[i].f(-1.0));

    Polynomial q = p[i].derivative();
    System.out.println("p'(x) = " + q);
    System.out.println("p' degree: " + q.degree());

    q = p[i].integrate();
    System.out.println("P(x) = " + q);
    System.out.println("P degree: " + q.degree());

    double v = p[i].integral(1.0, 2.0);
    System.out.println("integral from 1.0 to 2.0 = " + v);

    r = p[i].negate();
    System.out.println("-p(x) = " + r);
    System.out.println("degree: " + r.degree());

    r = p[i].multiply(2.0);
    System.out.println("p(x) * 2 = " + r);
    System.out.println("degree: " + r.degree());

    r = Polynomial.multiply(p[i], p[(i-1+n)%n]);
    System.out.println("(" + p[i] + ") * (" + p[(i-1+n)%n] + ") = " + r);
    System.out.println("degree: " + r.degree());

    r = Polynomial.add(p[i], p[(i-1+n)%n]);
    System.out.println("(" + p[i] + ") + (" + p[(i-1+n)%n] + ") = " + r);
    System.out.println("degree: " + r.degree());
    System.out.println("--------------------------------------------------------");
  }
}


void draw() {
  background(224);
  axes(); // draw x and y axes
  
  strokeWeight(0.5);
  stroke(128);
  grid(1, 1); // draw grid
  
  strokeWeight(1);
  stroke(0);
  grid(5, 5); // draw grid
  
  drawLabels(5, 5);

  float x = map(mouseX, umin, umax, xmin, xmax);
  strokeWeight(4);
  for (int i = 0; i < p.length; i++) {
    if (p[i] == null) continue;
    stroke(random(1), random(1), random(1));
    plot(p[i]);
    Line L = p[i].tangent(x);
    strokeWeight(3);
    plot(L);
  }
}


void plot(Polynomial p) {
  if (p == null) return;
  float x = xmin;
  float dx = (xmax-xmin)/width;
  float u1 = 0;
  float v1 = 0;
  float u2 = 0; 
  float v2 = 0;
  boolean first = true;
  while (x <= xmax) {
    float y = (float) p.f(x);
    // x to u
    u2 = map(x, xmin, xmax, umin, umax);
    // y to v
    v2 = map(y, ymin, ymax, vmin, vmax);
    point(u1, v1);
    if (!first) {
      line(u1, v1, u2, v2);
    } else {
      first = false;
    }
    x += dx;
    // saving for next iteration
    u1 = u2;
    v1 = v2;
  }
}


void plot(Point2D p) {
  if (p == null) return; // no point to plot
  strokeWeight(6);
  float u = map(p.x, xmin, xmax, umin, umax);
  float v = map(p.y, ymin, ymax, vmin, vmax);
  ellipse(u, v, 6, 6);
}

void plot(Line L) {
  if (L == null) return;
  strokeWeight(4);
  float x = xmin;
  float dx = (xmax-xmin)/width;
  float u1 = 0;
  float v1 = 0;
  float u2 = 0; 
  float v2 = 0;
  boolean first = true;
  while (x <= xmax) {
    float y = L.f(x);
    // x to u
    u2 = map(x, xmin, xmax, umin, umax);
    // y to v
    v2 = map(y, ymin, ymax, vmin, vmax);
    point(u1, v1);
    if (!first) {
      line(u1, v1, u2, v2);
    } else {
      first = false;
    }
    x += dx;
    // saving for next iteration
    u1 = u2;
    v1 = v2;
  }
}

 void axes() {
  strokeWeight(2);
  stroke(0);
  // x-axis is a horizontal line at y = 0 and from xmin to xmax
  // need: line(xmin, y, xmax, y) 
  // map from world coordinates onto screen coordinates
  float v = map(0, ymin, ymax, vmin, vmax);
  line(umin, v, umax, v);
  // y-axis is a verical line at x = 0 and from ymin to ymax
  // need: line(ymin, x, ymax, x) 
  float u = map(0, xmin, xmax, umin, umax);
  line(u, vmin, u, vmax);
}

// draw a grid with dx and dy spacing
void grid(float dx, float dy) {
  // vertical lines
  float x = 0;
  while (x <= xmax) {
    float u = map(x, xmin, xmax, umin, umax); // map x to u
    line(u, vmin, u, vmax); // draw vertical line
    x += dx;  //increment x
  }
  x = 0;
  while (x >= xmin) {
    float u = map(x, xmin, xmax, umin, umax); // map x to u
    line(u, vmin, u, vmax); // draw vertical line
    x -= dx;  //decrement x
  }
  
  // horizontal lines
  for (float y = 0;  y <= ymax;  y += dy) {
    float v = map(y, ymin, ymax, vmin, vmax); // map y to v
    line(umin, v, umax, v); // draw horizontal line
  }
  for (float y = 0;  y >= ymin;  y -= dy) {
    float v = map(y, ymin, ymax, vmin, vmax); // map y to v
    line(umin, v, umax, v); // draw horizontal line
  }
}

void drawLabels(float dx, float dy) {
  textSize(16);
  fill(0);
  float u = map(0, xmin, xmax, umin, umax);
  float v = map(0, ymin, ymax, vmin, vmax);
  text("0", u, v);
  for (float x = dx; x <= xmax;  x+= dx) {
    u = map(x, xmin, xmax, umin, umax);
    // need better formatting
    text(String.format("%.0f",x), u, v);
  }
  for (float x = -dx; x >= xmin;  x-= dx) {
    u = map(x, xmin, xmax, umin, umax);
    // need better formatting
    text(String.format("%.0f",x), u, v);
  }
  
  u = map(0, xmin, xmax, umin, umax);
  for (float y = dy; y <= ymax;  y+= dy) {
    v = map(y, ymin, ymax, vmin, vmax);
    // need better formatting
    text(String.format("%.0f",y), u, v);
  }
  for (float y = -dy; y >= ymin;  y-= dy) {
    v = map(y, ymin, ymax, vmin, vmax);
    // need better formatting
    text(String.format("%.0f",y), u, v);
  }
}

// zoom by a factor of 2 on the point clicked by the user
// reduce (xmax-xmin) and (ymax-ymix) by a factor of 2
void mouseClicked() {
  // (mouseX, mouseY) is in screen (u,v) coordinates
  // map that to world (x,y) coordinates
  float x = map(mouseX, umin, umax, xmin, xmax);
  float y = map(mouseY, vmin, vmax, ymin, ymax);
  float dx1 = xmax-xmin; // computing the x range
  float dy1 = ymax-ymin; // comuting the y range
  float dx2 = dx1/2; // reduce the x range by a factor of 2
  float dy2 = dy1/2; // reduce the y range by a factor of 2
  xmin = x - dx2/2;
  xmax = x + dx2/2;
  ymin = y - dy2/2;
  ymax = y + dy2/2;
  redraw();
}


void keyPressed() {
  if (key=='r') {
    reset();
    redraw(); // a processing defined function 
  }
  if (key == 's') {
    save("plot.jpg");
  }
}



// YOUR NAME(S) HERE!!!!

public static class Polynomial {
  double[] a;  // array of coefficients
  
  
  // default Constructor creates the zero polynomial
  public Polynomial(){
    a = new double[1];
    a[0] = 0.0D;
  }
  
  
  // Constructor based on an array of coefficents
  public Polynomial(double[] A){
    a = new double[1]; // REPLACE
    a[0] = 0.0D;       // REPLACE
  }
  
  
  // Copy Constructor
  public Polynomial(Polynomial p){
    a = new double[1]; // REPLACE
    a[0] = 0.0D;       // REPLACE
  }
  
  
  // return ith coefficient (0 returns constant term)
  public double coeff(int i){
    return 0.0;  // REPLACE
  }
  
  
  // returns the degree of this polynomial based on array length
  // return -1 if p(x)=0
  public int degree() {
    return -12345;  // REPLACE
  }
  
  
  // toString method - works as is!
  public String toString(){
    String s="";
    for(int i=a.length-1; i>=0; i--){
      s+=a[i];
      if (i!=0) s+="x^"+i+" + ";
    }
    return s;
  }
  
  
  // Evaluate this polynomial at x
  // f(x) = a[0] + a[1]*x + a[2]*x*x + ...
  //     OR
  // consider using Horner's method: 
  //   f(x) = a[0] + x(a[1] + x(a[2] + x(... + x(a[n-1]+ xa[n])...)))
  public double f(double x){
    return 0.0;  // REPLACE
  }
  
  
  // multiply each term of this by the constant -1
  // don't change p!
  public Polynomial negate(){
    return new Polynomial();  // REPLACE
  }
  
  
  // multiply each term of this by the constant b
  // don't change p!
  public Polynomial multiply(double b){
    return new Polynomial();  // REPLACE
  }
  
  
  // add two polynomials
  // don't change p1 or p2!
  // q(x) = p1(x) + p2(x)
  // which of p1 and p2 has the smaller degree?
  public static Polynomial add(Polynomial p1, Polynomial p2){
    return new Polynomial();  // REPLACE
  }
  
  
  // multiply two polynomials (probably hardest)
  // don't change p1 or p2!
  // q(x) = p1(x) * p2(x)
  // Hint: use 2 nested loops
  public static Polynomial multiply(Polynomial p1, Polynomial p2){
    return new Polynomial();  // REPLACE
  }
  
  
  // Use the power rule to differentiate p(x)
  // be careful when degree is zero
  // return q(x) = a[1] + 2*a[2]*x + 3*a[3]*x*x + ... 
  public Polynomial derivative(){
    return new Polynomial();  // REPLACE
  }
  
  
  // Use the power rule to differentiate p(x)
  // be careful when degree is zero
  // return L = m*x + b 
  // where m is the derivative evaluated at x
  // and the line contains the point (x,f(x))
  public Line tangent(double x){
    return new Line(0, new Point2D(0,0));  // REPLACE
  }
  
  
  // Use the power rule to integrate p(x), assume constant C is zero
  public Polynomial integrate(){
    return new Polynomial();  // REPLACE
  }
  
  
  // Integrate this polynomial from x0 to x1
  public double integral(double x0, double x1) {
    Polynomial p = integrate();  // REPLACE
    return 0.0;  // REPLACE
  }
}



public static class Line {
  // the line y=mx+b in the plane 
  // f(x) = mx+b
  float m; // slope
  float b; // intercept
  
  // send in a slope and intercept
  public Line(float m$, float b$) {
    m = m$;
    b = b$;
  } 
  
  // copy constructor
  public Line(Line L) {
    m = L.m;
    b = L.b;
  }
  
  // send in a slope and point
  public Line(float m$, Point2D p) {
    m = m$;
    b = p.y - m*p.x;
  }
  
  // send in a two points
  public Line(Point2D p1, Point2D p2) {
    // (p1.y-p2.y)/(p1.x-p2.x) == dy/dx
    this((p1.y-p2.y)/(p1.x-p2.x), p1); // invoke the slope-point constructor
  }
  
  public String toString() {
    return "y = " + m + "x + " + b;
  }
  
  public float f(float x) {
    float y = m*x + b;
    return y;
    // return m*x+b; // more compact
  }
  
  // compute the intersection point where this Line intersections L
  public Point2D intersection(Line L) {
    if (m==L.m) return null; // lines are parallel and no intersection point exists
    float x = (L.b-b)/(m-L.m);
    float y = f(x);
    return new Point2D(x,y);
  }
  
  public float getSlope() {
    return m;
  }
  
  public float getYIntercept() {
    return b;
  }
  
  public float getXIntercept() {
    return -b/m;
  }
}



// a class to represent a point on the plane
public static class Point2D {
  // this is a speacial keyword that refers to this instance
  // similar to "my name" OR "my address"
  float x, y; // this.x, this.y
  
  // a constructor for the point (x$, y$)
  public Point2D(float x, float y) {
    this.x = x;
    this.y = y;
  }
  // a default constructor to construct the point (0,0)
  public Point2D() {
    x = 0;
    y = 0;
  }
  // a copy constructor to construct the point identical to p
  public Point2D(Point2D p) {
    x = p.x;
    y = p.y;
  }
  
  // an equals method --- x coordinates are equal and the y coordinates are equal
  public boolean equals(Point2D p) {
     //if (x == p.x && y == p.y) return true;
     //else return false;
     return x == p.x && y == p.y;
  }
  
  // distance from this point to the origin
  float distance() {
    return sqrt(x*x+y*y);
  }
  
  // distance from this point to the point p
  float distance(Point2D p) {
    float dx = x-p.x; // this point's x - p's x
    float dy = y-p.y; // this point's y - p's y
    return sqrt(dx*dx+dy*dy);
  }
  
  // a toString method is called implicitly when a printable version is needed
  public String toString() {
    return "(" +  x + "," + y + ")";
  }
}

When you are finished, email your lab files to your lab partner and instructor.


Copyright © 2020, David A. Reimann. All rights reserved.