Для полинома найдите все его экстремумы и постройте его, выделив все монотонные части.

Кто-то задал мне этот интересный вопрос, и я думаю, что стоит опубликовать его здесь, поскольку на Stack Overflow не было соответствующей ветки.

Предположим, у меня есть полиномиальные коэффициенты в векторе длины n pc, где многочлен степени n - 1 для переменной x может быть выражен в его исходной форме:

pc[1] + pc[2] * x + pc[3] * x ^ 2 + ... + pc[n] * x ^ (n - 1)

Основная функция R polyroot может найти все корни этого многочлена в комплексной области. Но часто нас также интересуют экстремумы, так как для одномерной функции локальные минимумы и максимумы появляются попеременно, разбивая функцию на монотонные части.

Мои вопросы:

  1. Как получить все экстремумы (фактически все точки перевала) в реальной области полинома?
  2. Как нарисовать этот многочлен в двухцветной схеме: красный для восходящих фигур и зеленый для нисходящих?

Было бы хорошо записать это как функцию, чтобы мы могли легко исследовать / визуализировать многочлен.

В качестве примера рассмотрим многочлен 5-й степени:

pc <- c(1, -2.2, -13.4, -5.1, 1.9, 0.52)

person Zheyuan Li    schedule 05.10.2018    source источник


Ответы (2)


получить все седловые точки многочлена

Фактически, седловые точки можно найти, используя polyroot на 1-й производной полинома. Вот функция, которая это делает.

SaddlePoly <- function (pc) {
  ## a polynomial needs be at least quadratic to have saddle points
  if (length(pc) < 3L) {
    message("A polynomial needs be at least quadratic to have saddle points!")
    return(numeric(0))
    }
  ## polynomial coefficient of the 1st derivative
  pc1 <- pc[-1] * seq_len(length(pc) - 1)
  ## roots in complex domain
  croots <- polyroot(pc1)
  ## retain roots in real domain
  ## be careful when testing 0 for floating point numbers
  rroots <- Re(croots)[abs(Im(croots)) < 1e-14]
  ## note that `polyroot` returns multiple root with multiplicies
  ## return unique real roots (in ascending order)
  sort(unique(rroots))
  }

xs <- SaddlePoly(pc)
#[1] -3.77435640 -1.20748286 -0.08654384  2.14530617

вычислить многочлен

Нам нужно уметь вычислять многочлен, чтобы построить его. В этом моем ответе определена функция g, которая может оценивать многочлен и его произвольные производные. Здесь я копирую эту функцию и переименовываю ее в PolyVal.

PolyVal <- function (x, pc, nderiv = 0L) {
  ## check missing aruments
  if (missing(x) || missing(pc)) stop ("arguments missing with no default!")
  ## polynomial order p
  p <- length(pc) - 1L
  ## number of derivatives
  n <- nderiv
  ## earlier return?
  if (n > p) return(rep.int(0, length(x)))
  ## polynomial basis from degree 0 to degree `(p - n)`
  X <- outer(x, 0:(p - n), FUN = "^")
  ## initial coefficients
  ## the additional `+ 1L` is because R vector starts from index 1 not 0
  beta <- pc[n:p + 1L]
  ## factorial multiplier
  beta <- beta * factorial(n:p) / factorial(0:(p - n))
  ## matrix vector multiplication
  base::c(X %*% beta)
  }

Например, мы можем вычислить многочлен во всех его седловых точках:

PolyVal(xs, pc)
#[1]  79.912753  -4.197986   1.093443 -51.871351

нарисуйте многочлен с двухцветной схемой для монотонных фигур

Вот функция для просмотра / исследования полинома.

ViewPoly <- function (pc, extend = 0.1) {
  ## get saddle points
  xs <- SaddlePoly(pc)
  ## number of saddle points (if 0 the whole polynomial is monotonic)
  n_saddles <- length(xs)
  if (n_saddles == 0L) {
    message("the polynomial is monotonic; program exits!")
    return(NULL)
    }
  ## set a reasonable xlim to include all saddle points
  if (n_saddles == 1L) xlim <- c(xs - 1, xs + 1)
  else xlim <- extendrange(xs, range(xs), extend)
  x <- c(xlim[1], xs, xlim[2])
  ## number of monotonic pieces
  k <- length(xs) + 1L 
  ## monotonicity (positive for ascending and negative for descending)
  y <- PolyVal(x, pc)
  mono <- diff(y)
  ylim <- range(y)
  ## colour setting (red for ascending and green for descending)
  colour <- rep.int(3, k)
  colour[mono > 0] <- 2
  ## loop through pieces and plot the polynomial
  plot(x, y, type = "n", xlim = xlim, ylim = ylim)
  i <- 1L
  while (i <= k) {
    ## an evaluation grid between x[i] and x[i + 1]
    xg <- seq.int(x[i], x[i + 1L], length.out = 20)
    yg <- PolyVal(xg, pc)
    lines(xg, yg, col = colour[i])
    i <- i + 1L
    }
  ## add saddle points
  points(xs, y[2:k], pch = 19)
  ## return (x, y)
  list(x = x, y = y)
  }

Мы можем визуализировать пример полинома в вопросе следующим образом:

ViewPoly(pc)
#$x
#[1] -4.07033952 -3.77435640 -1.20748286 -0.08654384  2.14530617  2.44128930
#
#$y
#[1]  72.424185  79.912753  -4.197986   1.093443 -51.871351 -45.856876

person Zheyuan Li    schedule 05.10.2018

Альтернативное решение, повторная реализация SaddlePoly и PolyVal с пакетом R polynom.

library(polynom)

SaddlePoly <- function (pc) {
  ## a polynomial needs be at least quadratic to have saddle points
  if (length(pc) < 3L) {
    message("A polynomial needs be at least quadratic to have saddle points!")
    return(numeric(0))
    }
  ## polynomial coefficient of the 1st derivative
  ## pc1 <- pc[-1] * seq_len(length(pc) - 1)  ## <- removed
  ## roots in complex domain
  croots <- solve(deriv(polynomial(pc)))      ## <- use package "polynom"
  ## retain roots in real domain
  ## be careful when testing 0 for floating point numbers
  rroots <- Re(croots)[abs(Im(croots)) < 1e-14]
  ## note that `polyroot` returns multiple root with multiplicies
  ## return unique real roots (in ascending order)
  sort(unique(rroots))
  }

xs <- SaddlePoly(pc)
#[1] -3.77435640 -1.20748286 -0.08654384  2.14530617

## a complete re-implementation using package "polynom"
PolyVal <- function (x, pc, nderiv = 0L) {
  ## check missing aruments
  if (missing(x) || missing(pc)) stop ("arguments missing with no default!")
  ## create "polynomial" object
  p <- polynomial(pc)
  ## take derivatives
  i <- 0
  while (i < nderiv) {
    p <- deriv(p)
    i <- i + 1
    }
  ## evaluate "polynomial" with "predict"
  predict(p, x)
  }

PolyVal(xs, pc)
#[1]  79.912753  -4.197986   1.093443 -51.871351

## use `ViewPoly` as it is
ViewPoly(pc)
#$x
#[1] -4.07033952 -3.77435640 -1.20748286 -0.08654384  2.14530617  2.44128930
#
#$y
#[1]  72.424185  79.912753  -4.197986   1.093443 -51.871351 -45.856876


На мой взгляд, пакет polynom упрощает построение многочлена. Функция poly.calc позволяет построить многочлен из его корней или интерполяции Лагранжа.

## (x - 1) ^ 3
p1 <- poly.calc(rep(1, 3))

## x * (x - 1) * (x - 2) * (x - 3)
p2 <- poly.calc(0:3)

## Lagrange interpolation through 0:4 and rnorm(5, 0:4, 1)
set.seed(0); x <- 0:4; y <- rnorm(5, 0:4, 1)
p3 <- poly.calc(x, y)

Чтобы просмотреть эти многочлены, мы можем использовать функцию plot.polynomial из polynom или PolyView. Однако эти две функции имеют разную логику при выборе xlim для графика.

par(mfrow = c(3, 2), mar = c(4, 4, 1, 1))
## plot `p1`
plot(p1)
ViewPoly(unclass(p1))
## plot `p2`
plot(p2)
ViewPoly(unclass(p2))
## plot `p3`
plot(p3)
ViewPoly(unclass(p3))

person Zheyuan Li    schedule 05.10.2018