lots of cleanup
This commit is contained in:
1 parent
ae461659e0
commit
253295ff6e
91 files changed
+18268
-7856
No files matched your search
@@ -1,4 +1,5 @@
|
||||
[deps]
|
||||
AbbreviatedStackTraces = "ac637c84-cc71-43bf-9c33-c1b4316be3d4"
|
||||
CalculusWithJulia = "a2e0e22d-7d4c-5312-9169-8b992201a882"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
|
||||
|
||||
@@ -15,7 +15,6 @@ plotly()
|
||||
|
||||
---
|
||||
|
||||
|
||||
The family of exponential functions is used to model growth and decay. The family of logarithmic functions is defined here as the inverse of the exponential functions, but have reach far outside of that.
|
||||
|
||||
|
||||
@@ -45,7 +44,7 @@ For $a \neq 0$, $a^0$ is defined to be $1$.
|
||||
For positive, integer values of $n$, we have by definition that $a^{-n} = 1/a^n$.
|
||||
|
||||
|
||||
For $n$ a positive integer, we can define $a^{1/n}$ to be the unique positive solution to $x^n=a$.
|
||||
For $n$ a positive integer, we can define $a^{1/n}$ to be the unique, positive, real solution to $x^n=a$.
|
||||
|
||||
|
||||
Using the key properties of exponents we can extend this to a definition of $a^x$ for any rational $x$.
|
||||
@@ -57,31 +56,45 @@ Defining $a^x$ for any real number requires some more sophisticated mathematics.
|
||||
One method is to use a [theorem](http://tinyurl.com/zk86c8r) that says a *bounded* monotonically increasing sequence will converge. (This uses the [Completeness Axiom](https://en.wikipedia.org/wiki/Completeness_of_the_real_numbers).) Then for $a > 1$ we have if $q_n$ is a sequence of rational numbers increasing to $x$, then $a^{q_n}$ will be a bounded sequence of increasing numbers, so will converge to a number defined to be $a^x$. Something similar is possible for the $0 < a < 1$ case.
|
||||
|
||||
|
||||
This definition can be done to ensure the rules of exponents hold for $a > 0$:
|
||||
This definition can be done to ensure the rules of exponents hold.
|
||||
|
||||
::: {.relationship title="Rules of exponents"}
|
||||
For $a > 0$:
|
||||
|
||||
|
||||
$$
|
||||
a^{x + y} = a^x \cdot a^y, \quad (a^x)^y = a^{x \cdot y}.
|
||||
\begin{align*}
|
||||
a^{x + y} &= a^x \cdot a^y,\\
|
||||
(a^x)^y &= a^{x \cdot y}.
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
|
||||
In `Julia` these functions are implemented using `^`. A special value of the base, $e$, may be defined as well in terms of a limit. The exponential function $e^x$ is implemented in `exp`.
|
||||
|
||||
|
||||
::: {#fig-family-of-exponential-functions}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
plot(x -> (1/2)^x, -2, 2, label="1/2")
|
||||
plot!(x -> 1^x, label="1")
|
||||
plot!(x -> 2^x, label="2")
|
||||
plot!(x -> exp(x), label="e")
|
||||
```
|
||||
|
||||
Plots of different exponential functions, when $0 < a < 1$ there is decay; when $a=1$ a constant; and when $a > 1$ exponential growth.
|
||||
:::
|
||||
|
||||
We see examples of some general properties:
|
||||
|
||||
|
||||
* The domain is all real $x$ and the range is all *positive* $y$ (provided $a \neq 1$).
|
||||
* For $0 < a < 1$ the functions are monotonically decreasing.
|
||||
* For $a > 1$ the functions are monotonically increasing.
|
||||
* If $1 < a < b$ and $x > 0$ we have $a^x < b^x$.
|
||||
* The domain is all real $x$ and the range is all *positive* $y$ (provided $a \neq 1$).
|
||||
|
||||
* For $0 < a < 1$ the functions are monotonically decreasing.
|
||||
|
||||
* For $a > 1$ the functions are monotonically increasing.
|
||||
|
||||
* If $1 < a < b$ and $x > 0$ we have $a^x < b^x$.
|
||||
|
||||
|
||||
##### Example
|
||||
@@ -102,6 +115,7 @@ P0 * exp(r2*t), P0 * exp(r8*t)
|
||||
|
||||
As can be seen, there is quite a bit of difference.
|
||||
|
||||
##### Example: Rule of 72
|
||||
|
||||
In $1494$, [Pacioli](http://tinyurl.com/gsy939y) gave the "Rule of $72$", stating that to find the number of years it takes an investment to double when continuously compounded one should divide the interest rate into $72$.
|
||||
|
||||
@@ -138,7 +152,7 @@ n = 2 * 24
|
||||
That would be an enormous growth. Don't worry: "Exponential growth cannot continue indefinitely, however, because the medium is soon depleted of nutrients and enriched with wastes."
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
::: {.callout-note}
|
||||
## Note
|
||||
The value of `2^n` and `2.0^n` is different in `Julia`. The former remains an integer and is subject to integer overflow for `n > 62`. As used above, `2^(n/6)` will not overflow for larger `n`, as when the exponent is a floating point value, the base is promoted to a floating point value.
|
||||
|
||||
@@ -156,7 +170,7 @@ That is evidence that the $F_n \approx c\cdot 1.618^n$. (See [Relation to golden
|
||||
##### Example
|
||||
|
||||
|
||||
In the previous example, the exponential family of functions is used to describe growth. Polynomial functions also increase. Could these be used instead? If so that would be great, as they are easier to reason about.
|
||||
In a previous example, the exponential family of functions is used to describe growth. Polynomial functions also increase. Could these be used instead? If so that would be great, as they are easy to reason about.
|
||||
|
||||
|
||||
The key fact is that exponential growth is much greater than polynomial growth. That is for large enough $x$ and for any fixed $a>1$ and positive integer $n$ it is true that $a^x \gg x^n$.
|
||||
@@ -171,34 +185,6 @@ Later we will see an easy way to certify this statement.
|
||||
Euler's number, $e$, may be defined several ways. One way is to define $e^x$ by the limit as $n$ grows infinitely large of $(1+x/n)^n$. Then $e=e^1$. The value is an irrational number. This number turns up to be the natural base to use for many problems arising in calculus. In `Julia` there are a few mathematical constants that get special treatment, so that when needed, extra precision is available. The value `e` is not immediately assigned to this value, rather `ℯ` is. This is typed `\euler[tab]`. The label `e` is thought too important for other uses to reserve the name for representing a single number. However, users can issue the command `using Base.MathConstants` and `e` will be available to represent this number. When the `CalculusWithJulia` package is loaded, the value `e` is defined to be the floating point number returned by `exp(1)`. This loses the feature of arbitrary precision, but has other advantages.
|
||||
|
||||
|
||||
A [cute](https://www.mathsisfun.com/numbers/e-eulers-number.html) appearance of $e$ is in this problem: Let $a>0$. Cut $a$ into $n$ equal pieces and then multiply them. What $n$ will produce the largest value? Note that the formula is $(a/n)^n$ for a given $a$ and $n$.
|
||||
|
||||
|
||||
Suppose $a=5$ then for $n=1,2,3$ we get:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
a = 5
|
||||
(a/1)^1, (a/2)^2, (a/3)^3
|
||||
```
|
||||
|
||||
We'd need to compare more, but at this point $n=2$ is the winner when $a=5$.
|
||||
|
||||
|
||||
With calculus, we will be able to see that the function $f(x) = (a/x)^x$ will be maximized at $a/e$, but for now we approach this in an exploratory manner. Suppose $a=5$, then we have:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
a = 5
|
||||
n = 1:10
|
||||
f(n) = (a/n)^n
|
||||
@. [n f(n) (a/n - e)] # @. just allows broadcasting
|
||||
```
|
||||
|
||||
We can see more clearly that $n=2$ is the largest value for $f$ and $a/2$ is the closest value to $e$. This would be the case for any $a>0$, pick $n$ so that $a/n$ is closest to $e$.
|
||||
|
||||
|
||||
##### Example: The limits to growth
|
||||
|
||||
@@ -206,45 +192,48 @@ We can see more clearly that $n=2$ is the largest value for $f$ and $a/2$ is the
|
||||
The $1972$ book [The limits to growth](https://donellameadows.org/wp-content/userfiles/Limits-to-Growth-digital-scan-version.pdf) by Meadows et. al. discusses the implications of exponential growth. It begins stating their conclusion (emphasis added): "If the present *growth* trends in world population, industrialization, pollution, food production, and resource depletion continue *unchanged*, the limits to growth on this planet will be reached sometime in the next *one hundred* years." They note it is possible to alter these growth trends. We are now half way into this time period.
|
||||
|
||||
|
||||
Let's consider one of their examples, the concentration of carbon dioxide in the atmosphere. In their Figure $15$ they show data from $1860$ onward of CO$_2$ concentration extrapolated out to the year $2000$. At [climate.gov](https://www.climate.gov/news-features/understanding-climate/climate-change-atmospheric-carbon-dioxide) we can see actual measurements from $1960$ to $2020$. Numbers from each graph are read from the graphs, and plotted in the code below:
|
||||
|
||||
Let's consider one of their examples, the concentration of carbon dioxide in the atmosphere. In their Figure $15$ they show data from $1860$ onward of CO$_2$ concentration extrapolated out to the year $2000$. At [climate.gov](https://www.climate.gov/news-features/understanding-climate/climate-change-atmospheric-carbon-dioxide) we can see actual measurements from $1960$ to $2020$. Numbers from each graph are read from the graphs, and plotted in the code below. See @fig-plot-of-climate-data-1960-2020.
|
||||
|
||||
::: {#fig-plot-of-climate-data-1960-2020}
|
||||
```{julia}
|
||||
co2_1970 = [(1860, 293), (1870, 293), (1880, 294), (1890, 295), (1900, 297),
|
||||
(1910, 298), (1920, 300), (1930, 303), (1940, 305), (1950, 310),
|
||||
(1960, 313), (1970, 320), (1980, 330), (1990, 350), (2000, 380)]
|
||||
co2_2021 = [(1960, 318), (1970, 325), (1980, 338), (1990, 358), (2000, 370),
|
||||
(2010, 390), (2020, 415)]
|
||||
co2_1970 = [(1860, 293), (1870, 293), (1880, 294), (1890, 295),
|
||||
(1900, 297), (1910, 298), (1920, 300), (1930, 303),
|
||||
(1940, 305), (1950, 310), (1960, 313), (1970, 320),
|
||||
(1980, 330), (1990, 350), (2000, 380)]
|
||||
co2_2021 = [(1960, 318), (1970, 325), (1980, 338), (1990, 358),
|
||||
(2000, 370), (2010, 390), (2020, 415)]
|
||||
|
||||
plot(co2_1970, legend=false) # vector of points interface
|
||||
plot!(co2_2021)
|
||||
|
||||
exp_model(;r, x0, P0) = x -> P0 * exp(r * (x - x0))
|
||||
|
||||
# add two exponential models
|
||||
r = 0.002
|
||||
x0, P0 = 1960, 313
|
||||
plot!(exp_model(; r, x0, P0), 1950, 1990, linewidth=5, alpha=0.25)
|
||||
m1(x) = P0 * exp(r * (x - x0))
|
||||
plot!(m1, 1950, 1990, line=(:blue, 5, 0.25))
|
||||
|
||||
r = 0.005
|
||||
x0, P0 = 2000, 370
|
||||
|
||||
plot!(exp_model(; r, x0, P0), 1960, 2020, linewidth=5, alpha=0.25)
|
||||
m2(x) = P0 * exp(r * (x - x0))
|
||||
plot!(m2, 1960, 2020, line=(:blue, 5, 0.25))
|
||||
```
|
||||
|
||||
Plot of $CO_2$ concentration over time with two exponential models layered on
|
||||
:::
|
||||
|
||||
We can see that the projections from the year $1970$ hold up fairly well.
|
||||
|
||||
On this plot we added two *exponential* models. at $1960$ we added a *roughly* $0.2$ percent per year growth (a rate mentioned in an accompanying caption) and at $2000$ a roughly $0.5$ percent per year growth. The former barely keeping up with the data. (To do so, we used a parameterized function making for easier code reuse.)
|
||||
On this plot we added two *exponential* models. at $1960$ we added a *roughly* $0.2$ percent per year growth (a rate mentioned in an accompanying caption) and at $2000$ a roughly $0.5$ percent per year growth. The former barely keeping up with the data.
|
||||
|
||||
|
||||
The word **roughly** above could be made exact. Suppose we knew that between $1960$ and $1970$ the rate went from $313$ to $320$. If this followed an exponential model, then $r$ above would satisfy:
|
||||
The word *roughly* above could be made exact. Suppose we knew that between $1960$ and $1970$ the rate went from $313$ to $320$. If this followed an exponential model, then $r$ above would satisfy:
|
||||
|
||||
|
||||
$$
|
||||
P_{1970} = P_{1960} e^{r \cdot (1970 - 1960)}
|
||||
$$
|
||||
|
||||
or on division $320/313 = e^{r\cdot 10}$. Solving for $r$ can be done – as explained next – and yields $0.002211\dots$.
|
||||
or on division $320/313 = e^{r\cdot 10}$. Solving for $r$ can be done---as explained next---and yields $0.002211\dots$.
|
||||
|
||||
|
||||
## Logarithmic functions
|
||||
@@ -262,9 +251,10 @@ That is $a^{\log_a(x)} = x$ for $x > 0$ and $\log_a(a^x) = x$ for all $x$.
|
||||
To see how a logarithm is mathematically defined will have to wait, though the family of functions---one for each $a>0$---are implemented in `Julia` through the function `log(a,x)`. There are special cases requiring just one argument: `log(x)` will compute the natural log, base $e$---the inverse of $f(x) = e^x$; `log2(x)` will compute the log base $2$---the inverse of $f(x) = 2^x$; and `log10(x)` will compute the log base $10$- the inverse of $f(x)=10^x$. (Also `log1p` computes an accurate value of $\log(1 + p)$ when $p \approx 0$.)
|
||||
|
||||
|
||||
To see this in an example, we plot for base $2$ the exponential function $f(x)=2^x$, its inverse, and the logarithm function with base $2$:
|
||||
To see this in an example, we plot for base $2$ the exponential function $f(x)=2^x$, its inverse, and the logarithm function with base $2$. Though we made three graphs, only two are seen in @fig-plot-2-to-x-inverse-log2, as the graph of `log2` matches that of the inverse function.
|
||||
|
||||
|
||||
::: {#fig-plot-2-to-x-inverse-log2}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = 2^x
|
||||
@@ -277,7 +267,8 @@ xs = range(1/4, stop=4, length=100)
|
||||
plot!(xs, log2.(xs), color=:green, label="log₂") # plot log2
|
||||
```
|
||||
|
||||
Though we made three graphs, only two are seen, as the graph of `log2` matches that of the inverse function.
|
||||
Plot of $f(x) = 2^x$, its inverse function (e.g. `plot(ys,xs)`), and the function $\log_2(x)$.
|
||||
:::
|
||||
|
||||
|
||||
Note that we needed a bit of care to plot the inverse function directly, as the domain of $f$ is *not* the domain of $f^{-1}$. Again, in this case the domain of $f$ is all $x$, but the domain of $f^{-1}$ is only all *positive* $x$ values.
|
||||
@@ -318,7 +309,7 @@ If $1/10$ of the original carbon $14$ remains, how old is the item? This amounts
|
||||
-5730 * log2(1/10)
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
::: {.callout-note}
|
||||
## Note
|
||||
(Historically) Libby and James Arnold proceeded to test the radiocarbon dating theory by analyzing samples with known ages. For example, two samples taken from the tombs of two Egyptian kings, Zoser and Sneferu, independently dated to $2625$ BC plus or minus $75$ years, were dated by radiocarbon measurement to an average of $2800$ BC plus or minus $250$ years. These results were published in Science in $1949$. Within $11$ years of their announcement, more than $20$ radiocarbon dating laboratories had been set up worldwide. Source: [Wikipedia](http://tinyurl.com/p5msnh6).
|
||||
|
||||
@@ -327,15 +318,19 @@ If $1/10$ of the original carbon $14$ remains, how old is the item? This amounts
|
||||
### Properties of logarithms
|
||||
|
||||
|
||||
The basic graphs of logarithms ($a > 1$) are all similar, though as we see larger bases lead to slower growing functions, though all satisfy $\log_a(1) = 0$:
|
||||
|
||||
The basic graphs of logarithms ($a > 1$) are all similar with growth from $-\infty$ to $\infty$ as $x$ goes from just bigger than $0$ towards $\infty$. @fig-basic-logarithmic-graphs shows larger bases lead to slower growing functions, though all satisfy $\log_a(1) = 0$:
|
||||
|
||||
::: {#fig-basic-logarithmic-graphs}
|
||||
```{julia}
|
||||
plot(log2, 1/2, 10, label="2") # base 2
|
||||
plot!(log, 1/2, 10, label="e") # base e
|
||||
plot!(log10, 1/2, 10, label="10") # base 10
|
||||
#| echo: false
|
||||
plot(log2, 1/5, 10, label="log2") # base 2
|
||||
plot!(log, 1/5, 10, label="log") # base e
|
||||
plot!(log10, 1/5, 10, label="log10") # base 10
|
||||
```
|
||||
|
||||
Graphs of `log2`, `log` and `log10`
|
||||
:::
|
||||
|
||||
Now, what do the properties of exponents imply about logarithms?
|
||||
|
||||
|
||||
@@ -384,7 +379,11 @@ a^{(\log_b(x)/\log_b(a))} = (b^{\log_b(a)})^{(\log_b(x)/\log_b(a))} =
|
||||
b^{\log_b(a) \cdot \log_b(x)/\log_b(a) } = b^{\log_b(x)} = x.
|
||||
$$
|
||||
|
||||
In short, we have these three properties of logarithmic functions when $a, b$ are positive bases; $u,v$ are positive numbers; and $x$ is any real number:
|
||||
In short, we have three properties of logarithmic functions.
|
||||
|
||||
::: {.relationship title="Basic properties of logarithmic functions"}
|
||||
|
||||
If $a, b$ are positive bases; $u,v$ are positive numbers; and $x$ is any real number, then
|
||||
|
||||
|
||||
$$
|
||||
@@ -394,6 +393,7 @@ $$
|
||||
\log_a(u) &= \log_b(u)/\log_b(a).
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -534,7 +534,7 @@ radioq(choices, answ, keep_order=true)
|
||||
###### Question
|
||||
|
||||
|
||||
The [Loudest band](https://en.wikipedia.org/wiki/Loudest_band) can possibly be measured in [decibels](https://en.wikipedia.org/wiki/Decibel). In $1976$ the Who recorded $126$ db and in $1986$ Motorhead recorded $130$ db. Suppose both measurements record power through the formula $db = 10 \log_{10}(P)$. What is the ratio of the Motorhead $P$ to the $P$ for the Who?
|
||||
The [Loudest band](https://en.wikipedia.org/wiki/Loudest_band) can possibly quantified by measuring in [decibels](https://en.wikipedia.org/wiki/Decibel). In $1976$ the Who recorded $126$ db and in $1986$ Motorhead recorded $130$ db. Suppose both measurements record power through the formula $db = 10 \log_{10}(P)$. What is the ratio of the Motorhead $P$ to the $P$ for the Who?
|
||||
|
||||
|
||||
```{julia}
|
||||
|
||||
+112
-85
@@ -12,32 +12,34 @@ using Plots
|
||||
plotly()
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
A mathematical [function](http://en.wikipedia.org/wiki/Function_(mathematics)) is defined abstractly by:
|
||||
|
||||
|
||||
> **Function:** A function is a *relation* which assigns to each element in the domain a *single* element in the range. A **relation** is a set of ordered pairs, $(x,y)$. The set of first coordinates is the domain, the set of second coordinates the range of the relation.
|
||||
|
||||
::: {.definition title="A function"}
|
||||
A function is a *relation* which assigns to each element in the domain a *single* element in the range. A **relation** is a set of ordered pairs, $(x,y)$. The set of first coordinates is the domain, the set of second coordinates the range of the relation.
|
||||
:::
|
||||
|
||||
|
||||
That is, a function gives a correspondence between values in its domain with values in its range.
|
||||
|
||||
|
||||
This definition is abstract, as functions can be very general. With single-variable calculus, we generally specialize to real-valued functions of a single variable (*univariate, scalar functions*). These typically have the correspondence given by a rule, such as $f(x) = x^2$ or $f(x) = \sqrt{x}$. The function's domain may be implicit (as in all $x$ for which the rule is defined) or may be explicitly given as part of the rule. The function's range is then the image of its domain, or the set of all $f(x)$ for each $x$ in the domain ($\{f(x): x \in \text{ domain}\}$).
|
||||
This definition is abstract, as functions can be very general. With single-variable calculus, we generally specialize to real-valued functions of a single variable (*univariate, scalar-valued functions*). These typically have the correspondence given by a rule, such as $f(x) = x^2$ or $f(x) = \sqrt{x}$. The function's domain may be implicit (as in all $x$ for which the rule is defined) or may be explicitly given as part of the rule. The function's range is then the image of its domain, or the set of all $f(x)$ for each $x$ in the domain---$\{f(x): x \in \text{ domain}\}$.
|
||||
|
||||
|
||||
Some examples of mathematical functions are:
|
||||
|
||||
|
||||
$$
|
||||
f(x) = \cos(x), \quad g(x) = x^2 - x, \quad h(x) = \sqrt{x}, \quad
|
||||
s(x) = \begin{cases} -1 & x < 0\\1&x>0\end{cases}.
|
||||
\begin{array}{rclrcl}
|
||||
f(x) &=& \cos(x),& g(x) &=& x^2 - x, \\
|
||||
h(x) &=& \sqrt{x},&
|
||||
s(x) &=& \begin{cases} -1 & x < 0\\1&x>0\end{cases}.
|
||||
\end{array}
|
||||
$$
|
||||
|
||||
For these examples, the domain of both $f(x)$ and $g(x)$ is all real values of $x$, where as for $h(x)$ it is implicitly just the set of non-negative numbers, $[0, \infty)$. Finally, for $s(x)$, we can see that the domain is defined for every $x$ but $0$.
|
||||
For these examples, the domain for both $f(x)$ and $g(x)$ is all real values of $x$, whereas for $h(x)$ it is implicitly just the set of non-negative numbers, $[0, \infty)$. Finally, for $s(x)$, we can see that the domain is defined for every $x$ but $0$.
|
||||
|
||||
|
||||
In general the range is harder to identify than the domain, and this is the case for these functions too. For $f(x)$ we may know the $\cos$ function is trapped in $[-1,1]$ and it is intuitively clear than all values in that set are possible. The function $h(x)$ would have range $[0,\infty)$. The $s(x)$ function is either $-1$ or $1$, so only has two possible values in its range. What about $g(x)$? It is a parabola that opens upward, so any $y$ values below the $y$ value of its vertex will not appear in the range. In this case, the symmetry indicates that the vertex will be at $(1/2, -1/4)$, so the range is $[-1/4, \infty)$.
|
||||
@@ -145,11 +147,11 @@ This figure shows that the domain of a function may be a collection of intervals
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
**Thanks to Euler (1707-1783):** The formal idea of a function is a relatively modern concept in mathematics. According to [Dunham](http://www.maa.org/sites/default/files/pdf/upload_library/22/Ford/dunham1.pdf),
|
||||
Euler defined a function as an "analytic expression composed in any way whatsoever of the variable quantity and numbers or constant quantities." He goes on to indicate that as Euler matured, so did his notion of function, ending up closer to the modern idea of a correspondence not necessarily tied to a particular formula or “analytic expression.” He finishes by saying: "It is fair to say that we now study functions in analysis because of him."
|
||||
|
||||
|
||||
:::
|
||||
|
||||
Euler defined a function as an "analytic expression composed in any way whatsoever of the variable quantity and numbers or constant quantities." He goes on to indicate that as Euler matured, so did his notion of function, ending up closer to the modern idea of a correspondence not necessarily tied to a particular formula or “analytic expression.” He finishes by saying: "It is fair to say that we now study functions in analysis because of him."
|
||||
|
||||
|
||||
We will see that defining functions within `Julia` can be as simple a concept as Euler started with, but that the more abstract concept has a great advantage that is exploited in the design of the language.
|
||||
|
||||
@@ -181,9 +183,23 @@ For typical cases like the three above, there isn't really much new to learn.
|
||||
|
||||
:::{.callout-note}
|
||||
## The equals sign is used differently between math and Julia
|
||||
The equals sign in `Julia` always indicates either an assignment or a mutation of the object on the left side. The definition of a function above is an *assignment*, in that a function is added (or modified) in a table holding the methods associated with the function's name.
|
||||
|
||||
The equals sign restricts the expressions available on the *left*-hand side to a) a variable name, for assignment; b) mutating an object at an index, as in `xs[1]`; c) mutating a property of a struct; or d) a function assignment following this form `function_name(args...)`.
|
||||
In math, the equals sign is often associated with an equation.
|
||||
However, the equals sign in `Julia` always indicates either an
|
||||
assignment or a mutation of the object on the left side. The
|
||||
definition of a function above is an *assignment*, in that a function
|
||||
is added (or modified) in a table holding the methods associated with
|
||||
the function's name.
|
||||
|
||||
The equals sign restricts the expressions available on the *left*-hand side to either
|
||||
|
||||
* a variable name, for assignment;
|
||||
|
||||
* mutating an object at an index, as in `xs[1]`;
|
||||
|
||||
* mutating a property of a struct; or
|
||||
|
||||
* a function assignment following this form `function_name(args...)`.
|
||||
|
||||
Whereas function definitions and usage in `Julia` mirrors standard math notation; equations in math are not so mirrored in `Julia`. In mathematical equations, the left-hand of an equation is typically a complicated algebraic expression. Not so with `Julia`, where the left hand side of the equals sign is prescribed and quite limited.
|
||||
|
||||
@@ -200,7 +216,7 @@ Functions in `Julia` have an implicit domain, just as they do mathematically. In
|
||||
h(-1)
|
||||
```
|
||||
|
||||
The `DomainError` is one of many different error types `Julia` has, in this case it is quite apt: the value $-1$ is not in the domain of the function.
|
||||
The `DomainError` is one of many different error types `Julia` has, in this case it is quite apt---the value $-1$ is not in the domain of the function.
|
||||
|
||||
|
||||
### Equations, functions, calling a function
|
||||
@@ -344,7 +360,7 @@ else
|
||||
end
|
||||
```
|
||||
|
||||
The conditions for the `if` statements are expressions that evaluate to either `true` or `false`, such as generated by the Boolean operators `<`, `<=`, `==`, `!=`, `>=`, and `>`.
|
||||
The conditions for the `if` statements are expressions that evaluate to either `true` or `false`, such as generated by the comparison operators `<`, `<=`, `==`, `!=`, `>=`, and `>`.
|
||||
|
||||
|
||||
If familiar with `if` conditions, they are natural to use. However, for simpler cases of "if-else" `Julia` provides the more convenient *ternary* operator: `cond ? if_true : if_false`. (The name comes from the fact that there are three arguments specified.) The ternary operator checks the condition and if true returns the first expression, whereas if the condition is false the second condition is returned. (Another useful control flow construct is [short-circuit](https://docs.julialang.org/en/v1/manual/control-flow/#Short-Circuit-Evaluation) evaluation.)
|
||||
@@ -400,8 +416,10 @@ The function `s(x)` isn't quite so easy to implement, as there isn't an "otherwi
|
||||
|
||||
|
||||
```{julia}
|
||||
s(x) = x < 0 ? -1 :
|
||||
x > 0 ? 1 : error("0 is not in the domain")
|
||||
s(x) =
|
||||
x < 0 ? -1 :
|
||||
x > 0 ? 1 :
|
||||
error("0 is not in the domain")
|
||||
```
|
||||
|
||||
With nested ternary operators, the advantage over the `if` condition is not always compelling, but for simple cases the ternary operator is quite useful.
|
||||
@@ -469,12 +487,11 @@ trajectory(100)
|
||||
By using a multi-line function our work is much easier to look over for errors.
|
||||
|
||||
|
||||
##### Example: the secant method for finding a solution to $f(x) = 0$.
|
||||
|
||||
|
||||
This next example, shows how using functions to collect a set of computations for simpler reuse can be very helpful.
|
||||
|
||||
|
||||
##### Example: the secant method for finding a solution to $f(x) = 0$.
|
||||
|
||||
An old method for finding a zero of an equation is the [secant method](https://en.wikipedia.org/wiki/Secant_method). We illustrate the method with the function $f(x) = x^2 - 2$. In an upcoming example we see how to create a function to evaluate the secant line between $(a,f(a))$ and $(b, f(b))$ at any point. In this example, we define a function to compute the $x$ coordinate of where the secant line crosses the $x$ axis. This can be defined as follows:
|
||||
|
||||
|
||||
@@ -556,38 +573,41 @@ end
|
||||
|
||||
Now our guess $c$ is basically the same as `sqrt(2)`. Repeating the above leads to only a slight improvement in the guess, as we are about as close as floating point values will allow.
|
||||
|
||||
In most cases, this method can fairly quickly find a zero provided two good starting points are used.
|
||||
|
||||
Here we see a visualization with all these points. As can be seen, it quickly converges at the scale of the visualization, as we can't see much closer than `1e-2`.
|
||||
|
||||
@fig-secant-method-illustration shows a visualization with all these points. As can be seen, it quickly converges at the scale of the visualization, as we can't see much closer than `1e-2`.
|
||||
|
||||
::: {#fig-secant-method-illustration}
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
f(x) = x^2 - 2
|
||||
a, b = 1, 2
|
||||
c = secant_intersection(f, a, b)
|
||||
|
||||
p = plot(f, a, b, linewidth=5, legend=false)
|
||||
plot!(p, zero, a, b)
|
||||
scatter!([a,b], [f(a), f(b)]; marker=(:square,))
|
||||
plt = plot(f, a, b, linewidth=5, legend=false)
|
||||
plot!(plt, zero, a, b)
|
||||
scatter!(plt, [(a, f(a)), (b, f(b))]; marker=(:square,))
|
||||
|
||||
plot!(p, [a,b], f.([a,b]));
|
||||
scatter!(p, [c], [f(c)])
|
||||
plot!(plt, [(a, f(a)), (b, f(b))])
|
||||
scatter!(plt, [(c, f(c))])
|
||||
|
||||
a, b = b, c
|
||||
c = secant_intersection(f, a, b)
|
||||
plot!(p, [a,b], f.([a,b]));
|
||||
scatter!(p, [c], [f(c)])
|
||||
plot!(plt, [(a, f(a)), (b, f(b))])
|
||||
scatter!(plt, [(c, f(c))])
|
||||
|
||||
|
||||
a, b = b, c
|
||||
c = secant_intersection(f, a, b)
|
||||
plot!(p, [a,b], f.([a,b]));
|
||||
scatter!(p, [c], [f(c)])
|
||||
p
|
||||
plot!(plt, [(a, f(a)), (b, f(b))])
|
||||
scatter!(plt, [(c, f(c))])
|
||||
plt
|
||||
```
|
||||
:::
|
||||
|
||||
In most cases, this method can fairly quickly find a zero provided two good starting points are used.
|
||||
|
||||
|
||||
## Parameters, function context (scope), keyword arguments
|
||||
@@ -605,7 +625,9 @@ learn that this is just a dummy variable to be substituted for and so
|
||||
could have any name. Both also share a variable $m$ for a slope.
|
||||
Where does the value for $m$ come from?
|
||||
|
||||
In practice, there is a context that gives an answer. Despite the same name, there is no expectation that the slope will be the same for each function if the context is different. So when parameters are involved, a function involves a rule and a context to give specific values to the parameters. Euler had said initially that functions composed of "the variable quantity and numbers or constant quantities." The term "variable," we still use, but instead of "constant quantities," we use the name "parameters." In computer language, instead of context, we use the word *scope*.
|
||||
In practice, there is a context that gives an answer. Despite the same name, there is no expectation that the slope will be the same for each function if the context is different. So when parameters are involved, a function involves a rule and a context to give specific values to the parameters.
|
||||
|
||||
Euler had said initially that functions composed of "the variable quantity and numbers or constant quantities." The term "variable," we still use, but instead of "constant quantities," we use the name "parameters." In computer language, instead of context, we use the word *scope*.
|
||||
|
||||
|
||||
Something similar is also true with `Julia`. Consider the example of writing a function to model a linear equation with slope $m=2$ and $y$-intercept $3$. A typical means to do this would be to define constants, and then use the familiar formula:
|
||||
@@ -675,10 +697,12 @@ mxplusb(0; m=3, b=2)
|
||||
Keywords are used to mark the parameters whose values are to be changed from the default. Though one can use *positional arguments* for parameters---and there are good reasons to do so---using keyword arguments is a good practice if performance isn't paramount, as their usage is more explicit yet the defaults mean that a minimum amount of typing needs to be done.
|
||||
|
||||
|
||||
Keyword arguments are widely used with plotting commands, as there are numerous options to adjust, but typically only a handful adjusted per call. The `Plots` package whose commands we illustrate throughout these notes starting with the next section has this in its docs: `Plots.jl` follows two simple rules with data and attributes:
|
||||
Keyword arguments are widely used with plotting commands, as there are numerous options to adjust, but typically only a handful adjusted per call. The `Plots` package whose commands we illustrate throughout these notes starting with the next section has this in its docs:
|
||||
|
||||
* Positional arguments correspond to input data
|
||||
* Keyword arguments correspond to attributes
|
||||
> `Plots.jl` follows two simple rules with data and attributes:
|
||||
>
|
||||
> * Positional arguments correspond to input data
|
||||
> * Keyword arguments correspond to attributes
|
||||
|
||||
|
||||
|
||||
@@ -732,7 +756,7 @@ The *big* advantage of bundling parameters into a container is consistency-–-t
|
||||
::: {.callout-note}
|
||||
## Avoid global variables
|
||||
|
||||
Referring to a global parameter is common in math, but has a significant performance impact in `Julia`. Save for the simplest usage, it is much better to pass parameters to the function through one of several means that too rely on the value of the global state.
|
||||
Referring to a global parameter is common in math, but has a significant performance impact in `Julia`. Save for the simplest usage, it is much better to pass parameters to the function through one of means just illustrated.
|
||||
|
||||
:::
|
||||
|
||||
@@ -749,22 +773,22 @@ Volume(r, h) = pi * r^2 * h # of a cylinder
|
||||
SurfaceArea(r, h) = pi * r * (r + sqrt(h^2 + r^2)) # of a right circular cone, including the base
|
||||
```
|
||||
|
||||
The right-hand sides may or may not be familiar, but it should be reasonable to believe that if push came to shove, the formulas could be looked up. However, the left-hand sides are subtly different---they have two arguments, not one. In `Julia` it is trivial to define functions with multiple arguments---we just did.
|
||||
The right-hand sides may or may not be familiar, but it should be reasonable to believe that if push came to shove, the formulas could be looked up. However, the left-hand sides are subtly different---they have two arguments, not one. In `Julia` it is non-eventful to define functions with multiple arguments---we just did.
|
||||
|
||||
|
||||
Earlier we saw the `log` function can use a second argument to express the base. This function is basically defined by `log(b,x)=log(x)/log(b)`. The `log(x)` value is the natural log, and this definition just uses the change-of-base formula for logarithms.
|
||||
In a previous section we saw the `log` function can use a second argument to express the base. This function is basically defined by `log(b,x)=log(x)/log(b)`. The `log(x)` value is the natural log, and this definition just uses the change-of-base formula for logarithms.
|
||||
|
||||
|
||||
But not so fast, on the left side is a function with two arguments and on the right side the functions have one argument---yet they share the same name. How does `Julia` know which to use? `Julia` uses the number, order, and *type* of the positional arguments passed to a function to determine which function definition to use. This is technically known as [multiple dispatch](http://en.wikipedia.org/wiki/Multiple_dispatch) or **polymorphism**. As a feature of the language, it can be used to greatly simplify the number of functions the user must learn. The basic idea is that many functions are "generic" in that they have methods which will work differently in different scenarios.
|
||||
But not so fast, on the left side is a function with two arguments and on the right side the functions have one argument---yet they share the same name. How does `Julia` know which to use? `Julia` uses the number, order, and *type* of the positional arguments passed to a function to determine which function definition to use. This is technically known as [multiple dispatch](http://en.wikipedia.org/wiki/Multiple_dispatch) or *polymorphism*. As a feature of the language, it can be used to greatly simplify the number of functions the user must learn. The basic idea is that many functions are "generic" in that they have methods which will work differently in different scenarios but still compute the same general thing.
|
||||
|
||||
|
||||
:::{.callout-warning}
|
||||
## Warning
|
||||
:::{.callout-note}
|
||||
## Multiple dispatch is familiar
|
||||
Multiple dispatch is very common in mathematics. For example, we learn different ways to add: integers (fingers, carrying), real numbers (align the decimal points), rational numbers (common denominators), complex numbers (add components), vectors (add components), polynomials (combine like monomials), ... yet we just use the same `+` notation for each operation. The concepts are related, the details different.
|
||||
|
||||
:::
|
||||
|
||||
`Julia` is similarly structured. `Julia` terminology would be to call the operation "`+`" a *generic function* and the different implementations *methods* of "`+`". This allows the user to just need to know a smaller collection of generic concepts yet still have the power of detail-specific implementations. To see how many different methods are defined in the base `Julia` language for the `+` operator, we can use the command `methods(+)`. As there are so many (well over $100$ when `Julia` is started), we illustrate how many different logarithm methods are implemented for "numbers:"
|
||||
`Julia` is similarly structured. `Julia` terminology would be to call the operation "`+`" a *generic function* and the different implementations *methods* of "`+`". This allows the user to just need to know a smaller collection of generic concepts yet still have the power of detail-specific implementations. To see how many different methods are defined in the base `Julia` language for the `+` operator, we can use the command `methods(+)`. As there are so many (well over $100$ when `Julia` is started), we illustrate how many different logarithm methods are implemented for "numbers":
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -818,35 +842,36 @@ But the other fact about this problem---that the perimeter is $20$---means that
|
||||
height(w) = (20 - 2*w)/2
|
||||
```
|
||||
|
||||
By hand we would substitute this last expression into that for the area and simplify (to get $A=w\cdot (20-2 \cdot w)/2 = -w^2 + 10w$). However, within `Julia` we can let *composition* do the substitution and leave the algebraic simplification for `Julia` to do:
|
||||
By hand we would *substitute* this last expression into that for the area and simplify (to get $A=w\cdot (20-2 \cdot w)/2 = -w^2 + 10w$). However, within `Julia` we can let *composition* do the substitution and leave the algebraic simplification for `Julia` to do:
|
||||
|
||||
|
||||
```{julia}
|
||||
Area(w) = Area(w, height(w))
|
||||
```
|
||||
|
||||
|
||||
This might seem odd, just like with `log`, we now have two *different* but related functions named `Area`. Julia will decide which to use based on the number of arguments when the function is called. This setup allows both to be used on the same line, as above. This usage style is not so common with many computer languages, but is a feature of `Julia` which is built around the concept of *generic* functions with multiple dispatch rules to decide which rule to call.
|
||||
|
||||
|
||||
For example, jumping ahead a bit, the `plot` function of `Plots` expects functions of a single numeric variable. Behind the scenes, then the function `A(w)` will be used in this graph:
|
||||
For example, jumping ahead a bit, the `plot` function of `Plots` expects functions of a single numeric variable. Behind the scenes, then the function `A(w)` will be used in forming @fig-plot-of-area-to-see-how-easy-plotting-is. From the graph of the `Area` function we can see that the width for maximum area is $w=5$ and so $h=5$ as well.
|
||||
|
||||
|
||||
::: {#fig-plot-of-area-to-see-how-easy-plotting-is}
|
||||
```{julia}
|
||||
plot(Area, 0, 10)
|
||||
```
|
||||
|
||||
From the graph, we can see that the width for maximum area is $w=5$ and so $h=5$ as well.
|
||||
Plot of the `Area` function (of a single variable) over $[0,10]$
|
||||
:::
|
||||
|
||||
|
||||
|
||||
## Other types of functions
|
||||
|
||||
## Anonymous functions
|
||||
|
||||
|
||||
`Julia` has both *generic* functions *and* *anonymous* functions. Generic functions participate in *multiple dispatch*, a central feature of `Julia`. Anonymous functions are very useful with higher-order programming (passing functions as arguments). These notes occasionally take advantage of anonymous functions for convenience.
|
||||
|
||||
|
||||
### Anonymous functions
|
||||
|
||||
|
||||
Simple mathematical functions have a domain and range which are a subset of the real numbers, and generally have a concrete mathematical rule. However, the definition of a function is much more abstract. We've seen that functions for computer languages can be more complicated too, with, for example, the possibility of multiple input values. Things can get more abstract still.
|
||||
|
||||
@@ -917,7 +942,9 @@ Generic versus anonymous functions. Julia has two types of functions, generic on
|
||||
|
||||
This comes up when we use functions that return functions as we have different styles that can be used: When we defined `l = shift_right(f, c=3)` the value of `l` is assigned to name an anonymous function for later use. This binding can be reused to define other variables.
|
||||
|
||||
However, we could have defined the function `l` through `l(x) = shift_right(f, c=3)(x)`, being explicit about what happens to the variable `x`. This would add a method to the generic function `l`. Meaning, we get an error if we tried to assign a variable to `l`, such as an expression like `l=3`. The latter style is inefficient, so is not preferred.
|
||||
However, we could have defined the function `l` through `l(x) = shift_right(f, c=3)(x)`, being explicit about what happens to the variable `x`. This would add a method to the generic function `l`. Meaning, we get an error if we tried to assign a variable to `l`, such as an expression like `l=3`.
|
||||
|
||||
The latter style is inefficient, so is not preferred.
|
||||
|
||||
:::
|
||||
|
||||
@@ -975,7 +1002,7 @@ specific_line(m,b) = x -> mxplusb(x; m=m, b=b)
|
||||
The returned object will have its parameters (`m` and `b`) fixed when used.
|
||||
|
||||
|
||||
In `Julia`, the functions `Base.Fix1` and `Base.Fix2` are provided to take functions of two variables and create callable objects of just one variable, with the other argument fixed. This partial function application is provided by a some of the logical comparison operators, which can be useful with filtering, say.
|
||||
In `Julia`, the functions `Base.Fix1` and `Base.Fix2` are provided to take functions of two variables and create callable objects of just one variable, with the other argument fixed. This partial function application is provided by some of the logical comparison operators, which can be useful with filtering, say.
|
||||
|
||||
|
||||
For example, `<(2)` is a funny looking way of expressing the function `x -> x < 2`. (Think of `x < y` as `<(x,y)` and then "fix" the value of `y` to be `2`.) This is useful with filtering by a predicate function, for example:
|
||||
@@ -1001,7 +1028,7 @@ In Julia v1.12 the `Fix` constructor can fix an arbitrary position of a variadic
|
||||
Many functions in `Julia` accept a function as the first argument. A common pattern for calling some function is `action(f, args...)` where `action` is the function that will act on another function `f` using the value(s) in `args...`. There `do` notation is syntactical sugar for creating an anonymous function which is useful when more complicated function bodies are needed.
|
||||
|
||||
|
||||
Here is an artificial example to illustrate of a task we won't have cause to use in these notes, but is an important skill in some contexts. The `do` notation can be confusing to read, as it moves the function definition to the end and not the beginning, but is convenient to write and is used very often with the task of this example.
|
||||
Here is an artificial example to illustrate of a task we won't have cause to use in these notes. The `do` notation can be confusing to read, as it moves the function definition to the end and not the beginning, but is convenient to write and is used very often with the task of this example.
|
||||
|
||||
|
||||
To save some text to a file requires a few steps: opening the file; writing to the file; closing the file. The `open` function does the first. One method has this signature `open(f::Function, args...; kwargs....)` and is documented to "Apply the function f to the result of `open(args...; kwargs...)` and close the resulting file descriptor upon completion." Which is great, the open and close stages are handled by `Julia` and only the writing is up to the user.
|
||||
@@ -1051,7 +1078,7 @@ For heavy use of chaining through function application there are various package
|
||||
## Questions
|
||||
|
||||
|
||||
##### Question
|
||||
###### Question
|
||||
|
||||
|
||||
State the domain and range of $f(x) = |x + 2|$.
|
||||
@@ -1066,11 +1093,11 @@ choices = [
|
||||
"Domain is all non-negative numbers, range is all real numbers",
|
||||
"Domain is all non-negative numbers, range is all non-negative numbers"
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
##### Question
|
||||
###### Question
|
||||
|
||||
|
||||
State the domain and range of $f(x) = 1/(x-2)$.
|
||||
@@ -1085,11 +1112,11 @@ L"Domain is all real numbers except $2$, range is all real numbers except $0$",
|
||||
L"Domain is all non-negative numbers except $0$, range is all real numbers except $2$",
|
||||
L"Domain is all non-negative numbers except $-2$, range is all non-negative numbers except $0$"
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
##### Question
|
||||
###### Question
|
||||
|
||||
|
||||
Which of these functions has a domain of all real $x$, but a range of $x > 0$?
|
||||
@@ -1103,11 +1130,11 @@ raw"``f(x) = 2^x``",
|
||||
raw"``f(x) = 1/x^2``",
|
||||
raw"``f(x) = |x|``",
|
||||
raw"``f(x) = \sqrt{x}``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
##### Question
|
||||
###### Question
|
||||
|
||||
::: {#fig-floor-function}
|
||||
|
||||
@@ -1172,7 +1199,7 @@ choices = [
|
||||
"The integers"
|
||||
]
|
||||
answer = 1
|
||||
radioq(choices, answ)
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
From the graph, what is the range of the function?
|
||||
@@ -1187,7 +1214,7 @@ choices = [
|
||||
"The integers"
|
||||
]
|
||||
answer = 3
|
||||
radioq(choices, answ)
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
(This graphic uses the convention that a filled in point is present, but an open point is not, hence each bar represents some $[k, k+1)$.)
|
||||
@@ -1206,8 +1233,8 @@ q"function f(x) = sin(x + pi/3)",
|
||||
q"f(x) = sin(x + pi/3)",
|
||||
q"f: x -> sin(x + pi/3)",
|
||||
q"f x = sin(x + pi/3)"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1225,8 +1252,8 @@ q"f(x) := (1 + x^2)^(-1)",
|
||||
q"f[x] = (1 + x^2)^(-1)",
|
||||
q"def f(x): (1 + x^2)^(-1)"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1386,8 +1413,8 @@ Will the call `C(1, mu=70)` use a value of `70` for `mu`?
|
||||
#| echo: false
|
||||
choices = ["Yes, this will work just as it does for keyword arguments",
|
||||
"No, there will be an error that the function does not accept keyword arguments"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1411,8 +1438,8 @@ choices = [
|
||||
"If `x` is in `[a,b]` it returns `x`, otherwise it returns `NaN`",
|
||||
"`x` is the larger of the minimum of `x` and `a` and the value of `b`, aka `max(min(x,a),b)`"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1446,8 +1473,8 @@ L"You still get $0.649...$",
|
||||
"You get a `MethodError`, as `cos(pi/4)` is evaluated as a number and `∘` is not defined for functions and numbers",
|
||||
"You get a `generic` function, but this won't be callable. If tried, it will give an method error."
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
@@ -1468,8 +1495,8 @@ choices = [
|
||||
"It is `0.6663667453928805`, the same as `cos(sin(1))`",
|
||||
"It is `0.5143952585235492`, the same as `sin(cos(1))`",
|
||||
"It gives an error"]
|
||||
answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 1
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1488,8 +1515,8 @@ fn(3)
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["`true`","`false`"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1516,8 +1543,8 @@ Repeat the secant method two more times to find a better approximation for $\sqr
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = [q"4//3", q"7//5", q"58//41", q"816//577"]
|
||||
answ = 3
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 3
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
How small is the value of $f(c)$ for this value?
|
||||
@@ -1537,8 +1564,8 @@ How close is this answer to the true value of $\sqrt{2}$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = [L"about $8$ parts in $100$", L"about $1$ parts in $100$", L"about $4$ parts in $10,000$", L"about $2$ parts in $1,000,000$"]
|
||||
answ = 3
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 3
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
(Finding a good approximation to $\sqrt{2}$ would be helpful to builders, for example, as it could be used to verify the trueness of a square room, say.)
|
||||
@@ -1557,8 +1584,8 @@ choices = ["Just use `f = h - g`",
|
||||
"Define `f(x) = h(x) - g(x)`",
|
||||
"Use `x -> h(x) - g(x)` when the difference is needed"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
|
||||
{{< include ../_common_code.qmd >}}
|
||||
|
||||
In this section we will use these add-on packages:
|
||||
In this section we will use this add-on package:
|
||||
|
||||
|
||||
```{julia}
|
||||
using CalculusWithJulia
|
||||
using Plots
|
||||
plotly()
|
||||
plotly();
|
||||
```
|
||||
|
||||
---
|
||||
@@ -37,8 +36,9 @@ Why is this useful? When available, it can help us solve equations. If we can wr
|
||||
Let's explore when we can "solve" for an inverse function.
|
||||
|
||||
|
||||
Consider this graph of the function $f(x) = 2^x$
|
||||
Consider @fig-plot-2-x-showing-x-to-y-and-y-to-x showing a graph of the function $f(x) = 2^x$.
|
||||
|
||||
::: {#fig-plot-2-x-showing-x-to-y-and-y-to-x}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
p = let
|
||||
@@ -84,12 +84,15 @@ plotly()
|
||||
p
|
||||
```
|
||||
|
||||
The graph of a function is a representation of points $(x,f(x))$, so to *find* $y = f(c)$ from the graph, we begin on the $x$ axis at $c$, move vertically to the graph (the point $(c, f(c))$), and then move horizontally to the $y$ axis, intersecting it at $y = f(c)$. The figure shows this for $c=2$, from which we can read that $f(c)$ is about $4$. This is how an $x$ is associated to a single $y$.
|
||||
Plot of $f(x) = 2^x$ illustrating how to graphically start from $x=c$ to find $f(c)$ and how to start from $y=f(d)$ to find $d$
|
||||
:::
|
||||
|
||||
The graph of a function is a representation of points $(x,f(x))$, so to *find* $y = f(c)$ from the graph, we begin on the $x$ axis at $c$, move vertically to the graph (the point $(c, f(c))$), and then move horizontally to the $y$ axis, intersecting it at $y = f(c)$. This is how an $x$ is associated to a single $y$.
|
||||
|
||||
|
||||
If we were to *reverse* the direction, starting at $y = f(d)$ on the $y$ axis and then moving horizontally to the graph, and then vertically to the $x$-axis we end up at a value $d$ with the correct value of $f(d)$. This allows solving for $x$ knowing $y$ in $y=f(x)$.
|
||||
|
||||
The operation described will form a function **if** the initial movement horizontally is guaranteed to find *no more than one* value on the graph. That is, to have an inverse function, there can not be two $x$ values corresponding to a given $y$ value. This observation is often visualized through the "horizontal line test" - the graph of a function with an inverse function can only intersect a horizontal line at most in one place.
|
||||
The operation described will form a function **if** the initial movement horizontally is guaranteed to find *no more than one* value on the graph. That is, to have an inverse function, there can not be two $x$ values corresponding to a given $y$ value. This observation is often visualized through the "horizontal line test"---the graph of a function with an inverse function can only intersect a horizontal line at most in one place.
|
||||
|
||||
|
||||
More formally, a function is called *one-to-one* *if* for any two $a \neq b$, it must be that $f(a) \neq f(b)$. Many functions are one-to-one, many are not. Familiar one-to-one functions are linear functions ($f(x)=a \cdot x + b$ with $a\neq 0$), odd powers of $x$ ($f(x)=x^{2k+1}$), and functions of the form $f(x)=x^{1/n}$ for $x \geq 0$. In contrast, all *even* functions are *not* one-to-one, as $f(x) = f(-x)$ for any nonzero $x$ in the domain of $f$.
|
||||
@@ -183,7 +186,7 @@ y - 2 &= (x-1)^5\\
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
We see that $f^{-1}(x) = 1 + (x - 2)^{1/5}$. The fact that the power $5$ is an odd power is important, as this ensures a unique (real) solution to the fifth root of a value, in the above $y-2$.
|
||||
We see that $f^{-1}(x) = (x - 2)^{1/5} + 1$. The fact that the power $5$ is an odd power is important, as this ensures a unique (real) solution to the fifth root of a value, in the above $y-2$.
|
||||
|
||||
|
||||
In the section on [polynomial roots](../precalc/polynomial_roots.html) we introduce the `solve` function of `SymPy`, which can algebraically solve for inverse functions in easier cases.
|
||||
@@ -196,7 +199,7 @@ In the section on [polynomial roots](../precalc/polynomial_roots.html) we intro
|
||||
The function $f(x) = x^x, x \geq 1/e$ is strictly increasing. However, trying to algebraically solve for an inverse function will quickly run into problems (without using specially defined functions). The existence of an inverse does not imply there will always be luck in trying to find a mathematical rule defining the inverse.
|
||||
|
||||
|
||||
In the section on the [intermediate value theorem](../limits/intermediate_value_theorem.html#the-find_zero-function) we will see how to *numerically* solve for an inverse function.
|
||||
In the section on the intermediate value theorem we will see how to *numerically* solve for an inverse function.
|
||||
|
||||
|
||||
## Functions which are not always invertible
|
||||
@@ -205,7 +208,7 @@ In the section on the [intermediate value theorem](../limits/intermediate_value_
|
||||
Consider the function $f(x) = x^2$. The graph---a parabola---is clearly not *monotonic*. Hence no inverse function exists. Yet, we can solve equations $y=x^2$ quite easily: $y=\sqrt{x}$ *or* $y=-\sqrt{x}$. We know the square root undoes the squaring, but we need to be a little more careful to say the square root is the inverse of the squaring function.
|
||||
|
||||
|
||||
The issue is there are generally *two* possible answers. To avoid this, we might choose to only take the *non-negative* answer. To make this all work as above, we restrict the domain of $f(x)$ and now consider the related function $f(x)=x^2, x \geq 0$. This is now a monotonic function, so will have an inverse function. This is clearly $f^{-1}(x) = \sqrt{x}$. (The $\sqrt{x}$ being defined as the principle square root or the unique *non-negative* answer to $u^2-x=0$.)
|
||||
The issue in this case is there are generally *two* possible answers. To avoid this, we might choose to only take the *non-negative* answer. To make this all work as above, we restrict the domain of $f(x)$ and now consider the related function $f(x)=x^2, x \geq 0$. This is now a monotonic function, so will have an inverse function. This is clearly $f^{-1}(x) = \sqrt{x}$. (The $\sqrt{x}$ being defined as the principle square root or the unique *non-negative* answer to $u^2-x=0$.)
|
||||
|
||||
|
||||
The [inverse function theorem](https://en.wikipedia.org/wiki/Inverse_function_theorem) basically says that if $f$ is *locally* monotonic, then an inverse function will exist *locally*. By "local" we mean in a neighborhood of $c$.
|
||||
@@ -234,7 +237,7 @@ Then $f^{-1}(x) = \sqrt{(1-x)/x}$ where $0 < x \leq 1$. The somewhat complicated
|
||||
|
||||
Consider again the graph of a monotonic function, in this case $f(x) = x^2 + 2, x \geq 0$:
|
||||
|
||||
|
||||
::: {#fig-plot-xsquared-plus-2-domain-range}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x^2 + 2
|
||||
@@ -243,25 +246,31 @@ plot(f, 0, 4; yticks=[2,4,8,16],
|
||||
plot!([(2,0), (2, f(2)), (0, f(2))])
|
||||
```
|
||||
|
||||
Plot of $f(x) = x^2 + 2$ over $[0,4]$
|
||||
:::
|
||||
|
||||
The graph is shown over the interval $(0,4)$, but the *domain* of $f(x)$ is all $x \geq 0$. The *range* of $f(x)$ is clearly $2 \leq y \leq \infty$.
|
||||
|
||||
|
||||
The lines layered on the plot show how to associate an $x$ value to a $y$ value or vice versa (as $f(x)$ is one-to-one). The domain then of the inverse function is all the $y$ values for which a corresponding $x$ value exists: this is clearly all values bigger or equal to $2$. The *range* of the inverse function can be seen to be all the images for the values of $y$, which would be all $x \geq 0$. This gives the relationship:
|
||||
|
||||
|
||||
> * the *domain* of $f^{-1}(x)$ is the *range* of $f(x)$;
|
||||
> * the *range* of $f^{-1}(x)$ is the *domain* of $f(x)$;
|
||||
::: {.relationship title="The domain and range"}
|
||||
The *domain* of $f^{-1}(x)$ is the *range* of $f(x)$.
|
||||
|
||||
The *range* of $f^{-1}(x)$ is the *domain* of $f(x)$.
|
||||
:::
|
||||
|
||||
From this we can see if we start at $x$, apply $f$ we get $y$, if we then apply $f^{-1}$ we will get back to $x$ so we have:
|
||||
|
||||
|
||||
> For all $x$ in the domain of $f$: $f^{-1}(f(x)) = x$.
|
||||
::: {.relationship title = "Composition with an inverse"}
|
||||
For all $x$ in the domain of $f$: $f^{-1}(f(x)) = x$.
|
||||
|
||||
Similarly, were we to start on the $y$ axis, we would see:
|
||||
|
||||
> For all $x$ in the domain of $f^{-1}$: $f(f^{-1}(x)) = x$.
|
||||
For all $x$ in the domain of $f^{-1}$: $f(f^{-1}(x)) = x$.
|
||||
|
||||
In short $f^{-1} \circ f$ and $f \circ f^{-1}$ are both identity functions, though on possibly different domains.
|
||||
:::
|
||||
|
||||
|
||||
## The graph of the inverse function
|
||||
@@ -269,37 +278,41 @@ In short $f^{-1} \circ f$ and $f \circ f^{-1}$ are both identity functions, thou
|
||||
|
||||
The graph of $f(x)$ is a representation of all values $(x,y)$ where $y=f(x)$. As the inverse flips around the role of $x$ and $y$ we have:
|
||||
|
||||
|
||||
> If $(x,y)$ is a point on the graph of $f(x)$, then $(y,x)$ will be a point on the graph of $f^{-1}(x)$.
|
||||
|
||||
::: {.relationship title="Mirror points"}
|
||||
If $(x,y)$ is a point on the graph of $f(x)$, then $(y,x)$ will be a point on the graph of $f^{-1}(x)$.
|
||||
:::
|
||||
|
||||
|
||||
Let's see this in action. Take the function $2^x$. We can plot it by generating points to plot as follows:
|
||||
|
||||
|
||||
::: {#fig-plot-2-to-x-and-inverse-with-xs-ys}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = 2^x
|
||||
xs = range(0, 2, length=50)
|
||||
ys = f.(xs)
|
||||
plot(xs, ys; color=:blue, label="f",
|
||||
aspect_ratio=:equal, framestyle=:origin, xlims=(0,4))
|
||||
plot(xs, ys; aspect_ratio=:equal, framestyle=:origin,
|
||||
xlims=(0,4),
|
||||
color=:blue, label="f")
|
||||
plot!(ys, xs; color=:red, label="f⁻¹") # the inverse
|
||||
```
|
||||
|
||||
Plot of $f(x) = 2^x$ using `plot(xs, ys)` and its inverse produced with `plot!(ys, xs)`
|
||||
:::
|
||||
|
||||
By flipping around the $x$ and $y$ values in the `plot!` command, we produce the graph of the inverse function---when viewed as a function of $x$. We can see that the domain of the inverse function (in red) is clearly different from that of the function (in blue).
|
||||
|
||||
|
||||
The inverse function graph can be viewed as a symmetry of the graph of the function. Flipping the graph for $f(x)$ around the line $y=x$ will produce the graph of the inverse function: Here we see for the graph of $f(x) = x^{1/3}$ and its inverse function:
|
||||
|
||||
The inverse function graph can be viewed as a symmetry of the graph of the function. Flipping the graph for $f(x)$ around the line $y=x$ will produce the graph of the inverse function: @fig-plot-cbrt-and-its-inverse show the graph of $f(x) = x^{1/3}$ and its inverse function:
|
||||
|
||||
::: {#fig-plot-cbrt-and-its-inverse}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = cbrt(x)
|
||||
xs = range(-2, 2, length=150)
|
||||
ys = f.(xs)
|
||||
plot(xs, ys; color=:blue,
|
||||
aspect_ratio=:equal, legend=false)
|
||||
plot(xs, ys; aspect_ratio=:equal, legend=false,
|
||||
line=(:blue,))
|
||||
plot!(ys, xs; line=(:red,))
|
||||
plot!(identity; line=(:green, :dash))
|
||||
x = 1/4
|
||||
@@ -307,11 +320,15 @@ y = f(x)
|
||||
plot!([(x,y), (y,x)]; line=(:green, :dot))
|
||||
```
|
||||
|
||||
We drew a line connecting $(1/4, f(1/4))$ to $(f(1/4),1/4)$. We can see that it crosses the line $y=x$ perpendicularly, indicating that points are symmetric about this line. (The plotting argument `aspect_ratio=:equal` ensures that the $x$ and $y$ axes are on the same scale, so that this type of line will look perpendicular.)
|
||||
Plot of $f(x) = x^{1/3}$ and its inverse
|
||||
:::
|
||||
|
||||
In @fig-plot-cbrt-and-its-inverse we drew a line connecting $(1/4, f(1/4))$ to $(f(1/4),1/4)$. We can see that this line crosses the line $y=x$ perpendicularly, indicating that points are symmetric about the $y=x$ line. (The plotting argument `aspect_ratio=:equal` ensures that the $x$ and $y$ axes are on the same scale, so that this type of line will look perpendicular.)
|
||||
|
||||
One consequence of this symmetry, is that if $f$ is strictly increasing, then so is its inverse.
|
||||
|
||||
::: {.callout-note}
|
||||
## `cbrt(x)` is different from `x^(1/3)`
|
||||
In the above we used `cbrt(x)` and not `x^(1/3)`. The latter usage assumes that $x \geq 0$ as it isn't guaranteed that for all real exponents the answer will be a real number. The `cbrt` function knows there will always be a real answer and provides it.
|
||||
:::
|
||||
|
||||
@@ -321,12 +338,12 @@ In the above we used `cbrt(x)` and not `x^(1/3)`. The latter usage assumes that
|
||||
The slope of $f(x) = 9/5 \cdot x + 32$ is clearly $9/5$ and the slope of the inverse function $f^{-1}(x) = 5/9 \cdot (x-32)$ is clearly $5/9$ - or the reciprocal. This makes sense, as the slope is the rise over the run, and by flipping the $x$ and $y$ values we merely flip over the rise and the run.
|
||||
|
||||
|
||||
Now consider the graph of the *tangent line* to a function. This concept will be better defined later, for now, it is a line "tangent" to the graph of $f(x)$ at a point $x=c$.
|
||||
Now consider the graph of the *tangent line* to a function. This concept will be better defined later.
|
||||
|
||||
|
||||
For concreteness, we consider $f(x) = \sqrt{x}$ at $c=2$. The tangent line will have slope $1/(2\sqrt{2})$ and will go through the point $(2, f(2))$. We graph the function, its tangent line, and their inverses:
|
||||
|
||||
For concreteness, we consider $f(x) = \sqrt{x}$ at $c=2$. The tangent line will have slope $1/(2\sqrt{2})$ and will go through the point $(2, f(2))$. In @fig-plot-sqrt-x-its-inverse-two-tangent-lines we graph the function, its tangent line, and their inverses.
|
||||
|
||||
::: {#fig-plot-sqrt-x-its-inverse-two-tangent-lines}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = sqrt(x)
|
||||
@@ -339,8 +356,13 @@ plot(xs, ys, color=:blue, legend=false)
|
||||
plot!(xs, zs, color=:blue) # the tangent line
|
||||
plot!(ys, xs, color=:red) # the inverse function
|
||||
plot!(zs, xs, color=:red) # inverse of tangent line
|
||||
scatter!([(c, f(c))], marker=(:blue, 3))
|
||||
scatter!([(f(c),c)], marker=(:red, 3))
|
||||
```
|
||||
|
||||
Plot of $f(x) = \sqrt{x}$, its inverse, and two related tangent lines
|
||||
:::
|
||||
|
||||
What do we see? In blue, we can see the familiar square root graph along with a "tangent" line through the point $(2, f(2))$. The red graph of $f^{-1}(x) = x^2, x \geq 0$ is seen and, perhaps surprisingly, a tangent line. This is at the point $(f(2), 2)$. We know the slope of this tangent line is the reciprocal of the slope of the red tangent line. This gives this informal observation:
|
||||
|
||||
|
||||
@@ -493,8 +515,8 @@ Is it possible that a function have two different inverses?
|
||||
#| echo: false
|
||||
choices = [L"No, for all $x$ in the domain and an inverse, the value of any inverse will be the same, hence all inverse functions would be identical.",
|
||||
L"Yes, the function $f(x) = x^2, x \geq 0$ will have a different inverse than the same function $f(x) = x^2, x \leq 0$"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -509,8 +531,8 @@ A function takes a value $x$ adds $1$, divides by $2$, and then subtracts $1$. I
|
||||
choices = [L"Yes, the function is the linear function $f(x)=(x+1)/2 - 1$ and so is monotonic.",
|
||||
L"No, the function is $1$ then $2$ then $1$, but not \"one-to-one\""
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -525,8 +547,8 @@ Is the function $f(x) = x^5 - x - 1$ one-to-one?
|
||||
choices=[L"Yes, a graph over $(-100, 100)$ will show this.",
|
||||
L"No, a graph over $(-2,2)$ will show this."
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -560,15 +582,17 @@ yesnoq(false)
|
||||
|
||||
A function is defined by its graph.
|
||||
|
||||
|
||||
::: {#fig-function-x-sinx-defined-by-graph}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
f(x) = x - sin(x)
|
||||
plot(f, 0, 6pi)
|
||||
```
|
||||
Plot of $f(x)$ over $[0, 6\pi]$
|
||||
:::
|
||||
|
||||
Over the domain shown, is the function one-to-one?
|
||||
Over the domain shown in @fig-function-x-sinx-defined-by-graph , is the function one-to-one?
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -590,8 +614,8 @@ What is $g(x) = (f(x))^{-1}$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["``g(x) = x``", "``g(x) = x^{-1}``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
What is $g(x) = f^{-1}(x)$?
|
||||
@@ -601,8 +625,8 @@ What is $g(x) = f^{-1}(x)$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["``g(x) = x``", "``g(x) = x^{-1}``"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -674,8 +698,8 @@ L"The function that multiplies by $2$, subtracts $1$ and then squares the value.
|
||||
L"The function that divides by $2$, adds $1$, and then takes the square root of the value.",
|
||||
L"The function that takes square of the value, then subtracts $1$, and finally multiplies by $2$."
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -744,8 +768,8 @@ choices = [
|
||||
"``f^{-1}(x) = (5x-4)^3``",
|
||||
"``f^{-1}(x) = 5/(x^3 + 4)``"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -762,8 +786,8 @@ raw"``f^{-1}(x) = (x-e)^{1/\pi}``",
|
||||
raw"``f^{-1}(x) = (x-\pi)^{e}``",
|
||||
raw"``f^{-1}(x) = (x-e)^{\pi}``"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -779,8 +803,8 @@ choices = [
|
||||
raw"``[7, \infty)``",
|
||||
raw"``(-\infty, \infty)``",
|
||||
raw"``[0, \infty)``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -796,8 +820,8 @@ choices = [
|
||||
raw"``[7, \infty)``",
|
||||
raw"``(-\infty, \infty)``",
|
||||
raw"``[0, \infty)``"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -805,7 +829,7 @@ radioq(choices, answ)
|
||||
|
||||
From the plot, are blue and red inverse functions?
|
||||
|
||||
|
||||
::: {#fig-plot-of-blue-red-inverse-maybe-1}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -816,6 +840,8 @@ plot(xs, ys, color=:blue, legend=false)
|
||||
plot!(ys, xs, color=:red)
|
||||
plot!(x->x, linestyle=:dash)
|
||||
```
|
||||
Plot of two functions, one in blue, one in red along with the line $y=x$.
|
||||
:::
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
@@ -825,7 +851,7 @@ yesnoq(true)
|
||||
|
||||
From the plot, are blue and red inverse functions?
|
||||
|
||||
|
||||
::: {#fig-plot-of-blue-red-inverse-maybe-2}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -836,6 +862,8 @@ plot(xs, ys, color=:blue, legend=false)
|
||||
plot!(-xs, -ys, color=:red)
|
||||
plot!(x->x, linestyle=:dash)
|
||||
```
|
||||
Plot of two functions, one in blue, one in red along with the line $y=x$.
|
||||
:::
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
@@ -849,10 +877,13 @@ yesnoq(false)
|
||||
The function $f(x) = (ax + b)/(cx + d)$ is known as a [Mobius](http://tinyurl.com/oemweyj) transformation and can be expressed as a composition of $4$ functions, $f_4 \circ f_3 \circ f_2 \circ f_1$:
|
||||
|
||||
|
||||
* where $f_1(x) = x + d/c$ is a translation,
|
||||
* where $f_2(x) = x^{-1}$ is inversion and reflection,
|
||||
* where $f_3(x) = ((bc-ad)/c^2) \cdot x$ is scaling,
|
||||
* and $f_4(x) = x + a/c$ is a translation.
|
||||
* where $f_1(x) = x + d/c$ is a translation,
|
||||
|
||||
* where $f_2(x) = x^{-1}$ is inversion and reflection,
|
||||
|
||||
* where $f_3(x) = ((bc-ad)/c^2) \cdot x$ is scaling,
|
||||
|
||||
* and $f_4(x) = x + a/c$ is a translation.
|
||||
|
||||
|
||||
For $x=10$, $a=1$, $b=2$, $c=3$ and $d=5$, what is $f(10)$?
|
||||
@@ -886,8 +917,8 @@ choices = [
|
||||
L"As $f_4(f_3(f_2(f_1(x))))=(f_1 \circ f_2 \circ f_3 \circ f_4)(x)$",
|
||||
"As the latter is more complicated than the former."
|
||||
]
|
||||
answ=1
|
||||
radioq(choices, answ)
|
||||
answer=1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
Let $g_1$, $g_2$, $g_3$, and $g_4$ denote the inverse functions. Clearly, $g_1(x) = x- d/c$ and $g_4(x) = x - a/c$, as the inverse of adding a constant is subtracting the constant.
|
||||
@@ -900,8 +931,8 @@ What is $g_2(x)=f_2^{-1}(x)$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["``g_2(x) = x^{-1}``", "``g_2(x) = x``", "``g_2(x) = x -1``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
What is $g_3(x)=f_3^{-1}(x)$?
|
||||
@@ -914,8 +945,8 @@ choices = [
|
||||
raw"``c^2/(b\cdot c - a\cdot d) \cdot x``",
|
||||
raw"``(b\cdot c-a\cdot d)/c^2 \cdot x``",
|
||||
raw"``c^2 x``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
Given these, what is the value of $g_4(g_3(g_2(g_1(f_4(f_3(f_2(f_1(10))))))))$?
|
||||
|
||||
@@ -567,54 +567,67 @@ plotly() # optionally change the backend from the default
|
||||
|
||||
With `Plots` loaded, we can plot a function by passing the function object by name to `plot`, specifying the range of `x` values to show, as follows:
|
||||
|
||||
|
||||
::: {#fig-plot-sin-0-to-2pi-illustration}
|
||||
```{julia}
|
||||
plot(sin, 0, 2pi)
|
||||
```
|
||||
|
||||
Plot of $f(x) = \sin(x)$ over $[0, 2\pi]$
|
||||
:::
|
||||
|
||||
::: {.callout-note}
|
||||
This is in the form of **the** basic pattern employed: `verb(function_object, arguments...)`. The verb in this example is `plot`, the object `sin`, the arguments `0, 2pi` to specify `[a,b]` domain to plot over.
|
||||
This plot command is in the form of **the** basic pattern employed throughout: `verb(function_object, arguments...)`. The verb in this example is `plot`, the object `sin`, the arguments `0, 2pi` to specify `[a,b]` domain to plot over.
|
||||
:::
|
||||
|
||||
Plotting more than one function over `[a,b]` is achieved through the `plot!` function, which modifies the existing plot (`plot` creates a new one) by adding a new layer:
|
||||
|
||||
|
||||
::: {#fig-plot-example-sin-cos-zero}
|
||||
```{julia}
|
||||
plot(sin, 0, 2pi)
|
||||
plot!(cos, 0, 2pi)
|
||||
plot!(zero, 0, 2pi) # add the line y=0
|
||||
plot!(cos)
|
||||
plot!(zero) # add the line y=0
|
||||
```
|
||||
|
||||
Plot of $f(x) = \sin(x)$, $g(x) = \cos(x)$ and $h(x) = 0 over $[0, 2\pi]$
|
||||
:::
|
||||
|
||||
(There are alternatives to plot functions or other traces all at once.)
|
||||
|
||||
Individual points are added with `scatter` or `scatter!`:
|
||||
|
||||
Individual points are added with `scatter` or `scatter!`. Either two vectors `xs` and `ys` with the coordinates in each *or* a vector of tuples, each tuple representing a point can be used.
|
||||
|
||||
::: {#fig-plot-of-sin-cos-scattered-points}
|
||||
```{julia}
|
||||
plot(sin, 0, 2pi, legend=false)
|
||||
plot!(cos, 0, 2pi)
|
||||
scatter!([pi/4, pi+pi/4], [sin(pi/4), sin(pi + pi/4)])
|
||||
scatter!([(pi/4, sin(pi/4)), ( pi+pi/4, sin(pi + pi/4))])
|
||||
```
|
||||
Plot of $f(x) = \sin(x)$ and $g(x) = \cos(x)$ over the $[0, 2\pi]$ with intersection points marked with markers through `scatter`
|
||||
:::
|
||||
|
||||
|
||||
(The extra argument `legend=false` suppresses the automatic legend drawing. There are many other useful keyword arguments to adjust attributes of a trace of a graphic. For example, passing `markersize=10` to the `scatter!` command would draw the points larger than the default.)
|
||||
|
||||
|
||||
Plotting an *anonymous* function is a bit more immediate than the two-step approach of defining a named function then calling `plot` with this as an argument:
|
||||
|
||||
|
||||
::: {#fig-plot-of-anonymous-function-exp-minusx-over-pi-times-sinx}
|
||||
```{julia}
|
||||
plot( x -> exp(-x/pi) * sin(x), 0, 2pi)
|
||||
```
|
||||
Plot of $f(x) = e^{-x/\pi}\sin(x)$ over $[0, 2\pi]$ using an anonymous function
|
||||
:::
|
||||
|
||||
The `scatter!` function used above takes two vectors of values to describe the points to plot, one for the $x$ values and one for the matching $y$ values. The `plot` function can also produce plots with this interface. For example, here we use a comprehension to produce `y` values from the specified `x` values:
|
||||
|
||||
|
||||
::: {#fig-plot-sine-functions-using-xs-ys-approach}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
xs = range(0, 2pi, length=251)
|
||||
ys = [sin(2x) + sin(3x) + sin(4x) for x in xs]
|
||||
plot(xs, ys)
|
||||
```
|
||||
Plot of $f(x) = \sin(2x) + \sin(3x) + \sin(4x)$ over $[0, 2\pi]$ made by constructing vectors `xs` , `ys` holding $x$ and $y$ coordiinates of points to include
|
||||
:::
|
||||
|
||||
There are different plotting interfaces. Though not shown, all of these `plot` commands produce a plot of `f`, though with minor differences:
|
||||
|
||||
@@ -679,12 +692,14 @@ p(x=>2), p(x=>2, a=>3, b=>4, c=>1)
|
||||
This is convenient notation for calling the `subs` function for `SymPy`.
|
||||
|
||||
|
||||
SymPy expressions of a single free variable can be plotted directly:
|
||||
|
||||
SymPy expressions of a single free variable can be plotted directly.
|
||||
|
||||
::: {#fig-plot-64-16-xsquared-using-sympy-recipe}
|
||||
```{julia}
|
||||
plot(64 - (1/2)*32 * x^2, 0, 2)
|
||||
```
|
||||
Plot of $f(x) = 64 - 16x^2$ over $[0, 2]$ using a plot recipe
|
||||
:::
|
||||
|
||||
* SymPy has functions for manipulating expressions: `simplify`, `expand`, `together`, `factor`, `cancel`, `apart`, $...$
|
||||
* SymPy has functions for basic math: `factor`, `roots`, `solve`, `solveset`, $\dots$
|
||||
|
||||
+289
-188
@@ -30,10 +30,11 @@ A scalar, univariate function, such as $f(x) = 1 - x^2/2$, can be thought of in
|
||||
|
||||
|
||||
* It can be represented through a rule of what it does to $x$, as above. This is useful for computing numeric values.
|
||||
* it can be interpreted verbally, as in *square* $x$, take half then *subtract* from one. This can give clarity to what the function does.
|
||||
* it can be interpreted verbally, as in *square* $x$, take half, then *subtract* from one. This can give clarity to what the function does.
|
||||
* It can be thought of in terms of its properties: a polynomial, continuous, upside down $U$-shaped, an approximation for $\cos(x)$ near $0$, $\dots$
|
||||
* it can be visualized graphically. This is useful for seeing the qualitative behavior of a function.
|
||||
|
||||
The last one is the focus of this section.
|
||||
|
||||
The graph of a univariate function is just a set of points in the Cartesian plane. These points come from the relation $(x,f(x))$ that defines the function. Operationally, a sketch of the graph will consider a handful of such pairs and then the rest of the points will be imputed.
|
||||
|
||||
@@ -76,7 +77,7 @@ using Plots
|
||||
|
||||
:::
|
||||
|
||||
Some backends require installation, such as `PyPlot` and `PlotlyJS`. We use `plotly` in these notes, for the most part, which is not the default, so requires an additional command to set the backend:
|
||||
Some backends require installation, such as `PyPlot` and `PlotlyJS`. We use `plotly` in these notes, for the most part, which is not the default, so requires an additional command to set the backend:^[The `plotly` backend has some interactive features that make non-static graphics.]
|
||||
|
||||
```{julia}
|
||||
plotly()
|
||||
@@ -89,12 +90,15 @@ With `Plots` loaded and a backend chosen, it is straightforward to graph a funct
|
||||
|
||||
For example, to graph $f(x) = 1 - x^2/2$ over the interval $[-3,3]$ we have:
|
||||
|
||||
|
||||
::: {#fig-one-minus-xsquared-over-2}
|
||||
```{julia}
|
||||
f(x) = 1 - x^2/2
|
||||
plot(f, -3, 3)
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1 - x^2/2$ over $[-3,3]$
|
||||
:::
|
||||
|
||||
The `plot` command does the hard work behind the scenes. It needs $2$ pieces of information declared:
|
||||
|
||||
|
||||
@@ -116,48 +120,64 @@ Let's see some other graphs.
|
||||
|
||||
The `sin` function over one period is plotted through:
|
||||
|
||||
|
||||
::: {#fig-plot-sin-0-2pi}
|
||||
```{julia}
|
||||
plot(sin, 0, 2pi)
|
||||
```
|
||||
|
||||
We can make a graph of $f(x) = (1+x^2)^{-1}$ over $[-3,3]$ with
|
||||
Plot of $f(x) = \sin(x)$ over $[0, 2\pi]$
|
||||
:::
|
||||
|
||||
We can make a graph of $f(x) = (1+x^2)^{-1}$ over $[-3,3]$ with:
|
||||
|
||||
::: {#fig-plot-1-over-1-plus-xsquared}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = 1 / (1 + x^2)
|
||||
plot(f, -3, 3)
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1 / (1 + x^2)$ over $[-3, 3]$
|
||||
:::
|
||||
|
||||
A graph of $f(x) = e^{-x^2/2}$ over $[-2,2]$ is produced with:
|
||||
|
||||
|
||||
::: {#fig-plot-exp-minus-xsquared-over-2}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = exp(-x^2/2)
|
||||
plot(f, -2, 2)
|
||||
```
|
||||
|
||||
Plot of $f(x) = e^{-x^2/2}$ over $[-2, 2]$
|
||||
:::
|
||||
|
||||
We could skip the first step of defining a function by using an *anonymous function*. For example, to plot $f(x) = \cos(x) - x$ over $[0, \pi/2]$ we could do:
|
||||
|
||||
|
||||
::: {#fig-plot-cosx-minus-xusing-anonymous-function}
|
||||
```{julia}
|
||||
plot(x -> cos(x) - x, 0, pi/2)
|
||||
```
|
||||
|
||||
Plot of $f(x) = \cos(x)-x$ over $[0, \pi/2]$ using an anonymous function
|
||||
:::
|
||||
|
||||
Anonymous functions are especially helpful when parameterized functions are involved:
|
||||
|
||||
|
||||
::: {#fig-plot-mxplusb-with-anonymous-function}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
mxplusb(x; m=1, b=0) = m*x + b
|
||||
plot(x -> mxplusb(x; m=-1, b=1), -1, 2)
|
||||
```
|
||||
|
||||
Plot of $f(x) = -x + 1$ over $[-1,2]$ using an anonymous function
|
||||
:::
|
||||
|
||||
Had we parameterized using the `f(x,p)` style, the result would be similar:
|
||||
|
||||
|
||||
::: {#fig-plot-mxplusb-with-anonymous-function-fxp-style}
|
||||
```{julia}
|
||||
function mxplusb(x, p)
|
||||
m, b = p.m, p.b
|
||||
@@ -166,6 +186,9 @@ end
|
||||
plot(x -> mxplusb(x, (m=-1, b=1)), -1, 2)
|
||||
```
|
||||
|
||||
Plot of $f(x) = -x + 1$ over $[-1,2]$ using an anonymous function and the "`f(x,p)`" style of passing parameters
|
||||
:::
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
The function object in the general pattern `action(function, args...)` is commonly specified in one of three ways: by a name, as with `f`; as an anonymous function; or as the return value of some other action through composition.
|
||||
@@ -174,12 +197,14 @@ The function object in the general pattern `action(function, args...)` is common
|
||||
|
||||
Anonymous functions are also created by `Julia's` `do` notation, which is useful when the first argument to function (like `plot`) accepts a function:
|
||||
|
||||
|
||||
::: {#fig-plot-cosx-minus-x-using-do-notation}
|
||||
```{julia}
|
||||
plot(0, pi/2) do x
|
||||
cos(x) - x
|
||||
end
|
||||
```
|
||||
Plot of $f(x) = \cos(x) -x$ over $[0,\pi/2]$ illustrating use of `Julia`'s `do` notation
|
||||
:::
|
||||
|
||||
The `do` notation can be a bit confusing to read when unfamiliar, though its convenience makes it appealing.
|
||||
|
||||
@@ -195,7 +220,7 @@ Some types we will encounter, such as the one for symbolic values or the special
|
||||
|
||||
The default style for `Plots.jl` is to use a frame style where the viewing window is emphasized. This is a rectangular region, $[x_0, x_1] \times [y_0, y_1]$, which is seen through the tick labeling, the bounding scales on the left and bottom, and emphasized through the grid.
|
||||
|
||||
This choices does *not* show the $x-y$ axes. As such, we might layer on the axes when these are of interest.
|
||||
This choice does *not* show the $x-y$ axes. As such, we might layer on the axes when these are of interest.
|
||||
|
||||
To emphasize concepts, we may stylize a function graph, rather than display the basic graphic. For example, in this graphic highlighting the amount the function goes up as it moves from $1$ to $x$:
|
||||
|
||||
@@ -239,17 +264,12 @@ plt = let
|
||||
((1 + x)/2, f(1), text(L"\Delta x", 10, :top)),
|
||||
(x, (f(1) + f(x))/2, text(L"\Delta y", 10, :left))
|
||||
])
|
||||
plotly()
|
||||
current()
|
||||
end
|
||||
plt
|
||||
```
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
plotly()
|
||||
nothing
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -260,12 +280,15 @@ nothing
|
||||
Making a graph with `Plots` is easy, but producing a graph that is informative can be a challenge, as the choice of a viewing window can make a big difference in what is seen. For example, trying to make a graph of $f(x) = \tan(x)$, as below, will result in a bit of a mess---the chosen viewing window crosses several places where the function blows up:
|
||||
|
||||
|
||||
::: {#fig-plot-tan-over-minus10-10-poor-choice}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = tan(x)
|
||||
plot(f, -10, 10)
|
||||
```
|
||||
|
||||
Plot of $f(x) = \tan(x)$ over $[-10,10]$. The vertical asymptotes of the function result in a poor graphic.
|
||||
:::
|
||||
|
||||
Though this graph shows the asymptote structure and periodicity, it doesn't give much insight into each period or even into the fact that the function is periodic.
|
||||
|
||||
|
||||
@@ -275,22 +298,30 @@ Though this graph shows the asymptote structure and periodicity, it doesn't give
|
||||
The actual details of making a graph of $f$ over $[a,b]$ are pretty simple and follow the steps in making a "T"-table:
|
||||
|
||||
|
||||
* A set of $x$ values are created between $a$ and $b$.
|
||||
* A corresponding set of $y$ values are created.
|
||||
* The pairs $(x,y)$ are plotted as points and connected with straight lines.
|
||||
* A set of $x$ values are created between $a$ and $b$.
|
||||
|
||||
* A corresponding set of $y$ values are created.
|
||||
|
||||
* The pairs $(x,y)$ are plotted as points and connected with straight lines.
|
||||
|
||||
|
||||
If the first two lines create values `xs` and `ys` then the last is done by calling `plot(xs, ys)`.
|
||||
|
||||
|
||||
The only real difference is that when drawing by hand, we might know to curve the lines connecting points based on an analysis of the function. As `Julia` doesn't consider this, the points are connected with straight lines – like a dot-to-dot puzzle.
|
||||
|
||||
|
||||
In general, the `x` values are often generated by `range` or the `colon` operator and the `y` values produced by mapping or broadcasting a function over the generated `x` values.
|
||||
In general, the `x` values are often generated by the `range` or the `colon` operator and the `y` values produced by mapping or broadcasting a function over the generated `x` values.
|
||||
|
||||
|
||||
However, the plotting directive `plot(f, xmin, xmax)` calls an adaptive algorithm to use more points where needed, as judged by `PlotUtils.adapted_grid(f, (xmin, xmax))`. It computes both the `x` and `y` values. This algorithm is wrapped up into the `unzip(f, xmin, xmax)` function from `CalculusWithJulia`. The algorithm adds more points where the function is more "curvy" and uses fewer points where it is "straighter." Here we see the linear function is identified as needing far fewer points than the oscillating function when plotted over the same range:
|
||||
However, the plotting directive `plot(f, xmin, xmax)`, in computing values for `x` to plot, calls an adaptive algorithm to use more points where needed, as judged by `PlotUtils.adapted_grid(f, (xmin, xmax))`. It computes both the `x` and `y` values. This algorithm is wrapped up into the `unzip(f, xmin, xmax)` function from `CalculusWithJulia`. The algorithm adds more points where the function is more "curvy" and uses fewer points where it is "straighter." Here we see the linear function is identified as needing far fewer points than the oscillating function when plotted over the same range:
|
||||
|
||||
|
||||
```{julia}
|
||||
pts_needed(f, xmin, xmax) = length(unzip(f, xmin, xmax)[1])
|
||||
function pts_needed(f, xmin, xmax)
|
||||
xs, ys = unzip(f, xmin, xmax)
|
||||
length(xs)
|
||||
end
|
||||
pts_needed(x -> 10x, 0, 10), pts_needed(x -> sin(10x), 0, 10)
|
||||
```
|
||||
|
||||
@@ -300,22 +331,19 @@ pts_needed(x -> 10x, 0, 10), pts_needed(x -> sin(10x), 0, 10)
|
||||
---
|
||||
|
||||
|
||||
For instances where a *specific* set of $x$ values is desired to be used, the `range` function or colon operator can be used to create the $x$ values and broadcasting used to create the $y$ values. For example, if we were to plot $f(x) = \sin(x)$ over $[0,2\pi]$ using $10$ points, we might do:
|
||||
|
||||
For instances where a *specific* set of $x$ values is desired to be used, the `range` function or colon operator can be used to create the $x$ values and broadcasting used to create the $y$ values. For example, if we were to plot $f(x) = \sin(x)$ over $[0,2\pi]$ using $10$ points, we might do the three steps, passing the values to `plot` as follows:
|
||||
|
||||
::: {#fig-plot-xs-ys-sin}
|
||||
```{julia}
|
||||
xs = range(0, 2pi, length=10)
|
||||
xs = range(0, 2pi, 10)
|
||||
ys = sin.(xs)
|
||||
```
|
||||
|
||||
Finally, to plot the set of points and connect with lines, the $x$ and $y$ values are passed along as vectors:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(xs, ys)
|
||||
```
|
||||
|
||||
This plots the points as pairs and then connects them in order using straight lines. Basically, it creates a dot-to-dot graph. The above graph looks primitive, as it doesn't utilize enough points.
|
||||
Plot of $f(x) = \sin(x)$ over $[0,2\pi]$ using the `f(xs, ys)` style
|
||||
:::
|
||||
|
||||
The `plot` function plots the points as pairs and then connects them in order using straight lines. Basically, it creates a dot-to-dot graph. The above graph looks primitive, as it doesn't utilize enough points.
|
||||
|
||||
|
||||
|
||||
@@ -325,96 +353,99 @@ This plots the points as pairs and then connects them in order using straight li
|
||||
The graph of a function may be reflected through a line, as those seen with a mirror. For example, a reflection through the $y$ axis takes a point $(x,y)$ to the point $(-x, y)$. We can easily see this graphically, when we have sets of $x$ and $y$ values through a judiciously placed minus sign.
|
||||
|
||||
|
||||
For example, to plot $\sin(x)$ over $(-\pi,\pi)$ we might do:
|
||||
For example, to plot $\sin(x)$ over $(-\pi,\pi)$ we might specify `xs`, generate `ys` and then call `plot(xs, ys)`.
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
xs = range(-pi, pi, length=100)
|
||||
ys = sin.(xs)
|
||||
plot(xs, ys)
|
||||
```
|
||||
|
||||
To reflect this graph through the $y$ axis, we only need to plot `-xs` and not `xs`:
|
||||
|
||||
To reflect this graph through the $y$ axis, we only need to plot `-xs` and not `xs`.
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
plot(-xs, ys)
|
||||
```
|
||||
|
||||
Looking carefully we see there is a difference. (How?)
|
||||
::: {#fig-plot-xs-ys-xs layout-ncol=1}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
xs = range(-pi, pi, length=100)
|
||||
ys = sin.(xs)
|
||||
p1 = plot(xs, ys)
|
||||
p2 = plot(-xs, ys)
|
||||
plot(p1, p2)
|
||||
```
|
||||
|
||||
Plot of `xs, ys` on left, `-xs, ys` on right. The right graph is the reflection of the left one through the $x$ axis.
|
||||
:::
|
||||
|
||||
|
||||
There are four very common reflections:
|
||||
|
||||
* reflection through the $y$-axis takes $(x,y)$ to $(-x, y)$.
|
||||
|
||||
* reflection through the $y$-axis takes $(x,y)$ to $(-x, y)$.
|
||||
* reflection through the $x$-axis takes $(x,y)$ to $(x, -y)$.
|
||||
* reflection through the origin takes $(x,y)$ to $(-x, -y)$.
|
||||
* reflection through the line $y=x$ takes $(x,y)$ to $(y,x)$.
|
||||
* reflection through the $x$-axis takes $(x,y)$ to $(x, -y)$.
|
||||
|
||||
* reflection through the origin takes $(x,y)$ to $(-x, -y)$.
|
||||
|
||||
* reflection through the line $y=x$ takes $(x,y)$ to $(y,x)$.
|
||||
|
||||
|
||||
For the $\sin(x)$ graph, we see that reflecting through the $x$ axis produces the same graph as reflecting through the $y$ axis:
|
||||
@fig-reflections shows the different graphs. For the $\sin(x)$ graph, we see that reflecting through the $x$ axis produces the same graph as reflecting through the $y$ axis. Doing both reflections (e.g., through the $x$ axis, then the $y$ axis) is the same as reflecting through the origin and for this function leaves the graph unchanged.
|
||||
|
||||
> An *even function* is one where reflection through the $y$ axis leaves the graph unchanged. That is, $f(-x) = f(x)$. An *odd function* is one where a reflection through the origin leaves the graph unchanged, or $f(-x) = -f(x)$.
|
||||
|
||||
|
||||
::: {#fig-reflections}
|
||||
|
||||
```{julia}
|
||||
plot(xs, -ys)
|
||||
#| echo: false
|
||||
let
|
||||
xs = range(-pi, pi, length=100)
|
||||
ys = sin.(xs)
|
||||
p_xy = plot(-xs, ys; title="plot(-xs, ys)", legend=false)
|
||||
hline!(p_xy, [0]; line=(1, :gray25))
|
||||
plot!(p_xy, xs, ys; line=(:dash, :gray75))
|
||||
|
||||
px_y = plot(xs, -ys; title="plot(xs, -ys)", legend=false)
|
||||
vline!(px_y, [0]; line=(1, :gray))
|
||||
plot!(px_y, xs, ys; line=(:dash, :gray75))
|
||||
|
||||
p_x_y = plot(xs, ys; title="plot(-xs, -ys)", legend=false)
|
||||
scatter!(p_x_y, [(0,0)]; marker=(3, :gray))
|
||||
plot!(p_x_y, xs, ys; line=(:dash, :gray75))
|
||||
|
||||
pyx = plot(ys, xs; title="plot(ys, xs)", legend=false, aspect_ratio=:equal)
|
||||
plot!(pyx, [(-pi,-pi), (pi, pi)]; line=(1, :gray))
|
||||
plot!(pyx, xs, ys; line=(:dash, :gray75))
|
||||
|
||||
plot(p_xy, px_y, p_x_y, pyx)
|
||||
end
|
||||
```
|
||||
|
||||
However, reflecting through the origin leaves this graph unchanged:
|
||||
Plot of the four common reflections for $f(x) = \sin(x)$ along with a line or point indicated what the reflection was through. This function is an odd function, so the graph is left unchanged by reflection through the origin (lower left).
|
||||
:::
|
||||
|
||||
|
||||
The reflection of $f(x) = \sin(x)$ through the line $y=x$ leads to a graph of the equation $x = \sin(y)$. The result is not the graph of a function as the same $x$ can map to more than one $y$ value. (The new graph does not pass the "vertical line" test.) However, for the sine function we can get a function from this reflection if we choose a narrower viewing window:
|
||||
|
||||
::: {#fig-plot-arcsin-by-generating-points-over-minuspi-over-2-to-pi-over-2}
|
||||
```{julia}
|
||||
plot(-xs, -ys)
|
||||
```
|
||||
|
||||
> An *even function* is one where reflection through the $y$ axis leaves the graph unchanged. That is, $f(-x) = f(x)$. An *odd function* is one where a reflection through the origin leaves the graph unchanged, or symbolically $f(-x) = -f(x)$.
|
||||
|
||||
|
||||
|
||||
If we try reflecting the graph of $\sin(x)$ through the line $y=x$, we have:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(ys, xs)
|
||||
```
|
||||
|
||||
This is the graph of the equation $x = \sin(y)$, but is not the graph of a function as the same $x$ can map to more than one $y$ value. (The new graph does not pass the "vertical line" test.)
|
||||
|
||||
|
||||
However, for the sine function we can get a function from this reflection if we choose a narrower viewing window:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
xs = range(-pi/2, pi/2, length=100)
|
||||
ys = sin.(xs)
|
||||
plot(ys, xs)
|
||||
```
|
||||
|
||||
Inverse plot of $f(x)=\sin(x)$ over $[-\pi/2, \pi/2]$ using `plot(ys, xs)`
|
||||
:::
|
||||
|
||||
The graph is that of the "inverse function" for $\sin(x), x \text{ in } [-\pi/2, \pi/2]$.
|
||||
|
||||
|
||||
#### The `plot(xs, f)` syntax
|
||||
|
||||
|
||||
When plotting a univariate function there are three basic patterns that can be employed. We have examples above of:
|
||||
|
||||
|
||||
* `plot(f, xmin, xmax)` uses a recipe implementing an adaptive algorithm to identify values for $x$ in the interval `[xmin, xmas]`,
|
||||
|
||||
* `plot(xs, f.(xs))` to manually choose the values of $x$ to plot points for, and
|
||||
|
||||
Finally, there is a merging of the first two following the pattern:
|
||||
|
||||
* `plot(xs, f)`
|
||||
|
||||
|
||||
All require a manual choice of the values of the $x$-values to plot, but the broadcasting is carried out in the `plot` command. This style is convenient, for example, to down sample the $x$ range to see the plotting mechanics, such as:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(0:pi/4:2pi, sin)
|
||||
```
|
||||
|
||||
#### NaN values
|
||||
|
||||
@@ -424,22 +455,29 @@ At times it is not desirable to draw lines between each successive point. For ex
|
||||
|
||||
For example,what happens at $0$ with $f(x) = 1/x$. The most straightforward plot is dominated by the vertical asymptote at $x=0$:
|
||||
|
||||
|
||||
::: {#fig-plot-1-over-x-over-minus1-1}
|
||||
```{julia}
|
||||
q(x) = 1/x
|
||||
plot(q, -1, 1)
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1/x$ over $[-1,1]$ showing effect on graphic of vertical asymptote
|
||||
:::
|
||||
|
||||
We can attempt to improve this graph by adjusting the viewport. The *viewport* of a graph is the $x$-$y$ range of the viewing window. By default, the $y$-part of the viewport is determined by the range of the function over the specified interval, $[a,b]$. As just seen, this approach can produce poor graphs. The `ylims=(ymin, ymax)` argument can modify what part of the $y$ axis is shown. (Similarly `xlims=(xmin, xmax)` will modify the viewport in the $x$ direction.)
|
||||
|
||||
|
||||
As we see, even with this adjustment, the spurious line connecting the points with $x$ values closest to $0$ is still drawn:
|
||||
|
||||
|
||||
::: {#fig-plot-1-over-x-over-minus1-1-using-ylims}
|
||||
```{julia}
|
||||
plot(q, -1, 1, ylims=(-10,10))
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1/x$ over $[-1,1]$ using `ylims` to reduce effect on graphic of vertical asymptote
|
||||
|
||||
:::
|
||||
|
||||
The dot-to-dot algorithm, at some level, assumes the underlying function is *continuous*; here $q(x)=1/x$ is not.
|
||||
|
||||
|
||||
@@ -448,7 +486,7 @@ There is a convention for most plotting programs that **if** the $y$ value for a
|
||||
|
||||
Here is one way to plot $q(x) = 1/x$ over $[-1,1]$ taking advantage of this convention:
|
||||
|
||||
|
||||
::: {#fig-plot-1-over-x-over-minus1-1-using-NaN}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
xs = range(-1, 1, length=251)
|
||||
@@ -457,29 +495,35 @@ ys[xs .== 0.0] .= NaN
|
||||
plot(xs, ys)
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1/x$ after setting `y` values to `NaN` when `x` is zero
|
||||
:::
|
||||
|
||||
By using an odd number of points, we should have that $0.0$ is amongst the `xs`. The next to last line replaces the $y$ value that would be infinite with `NaN`.
|
||||
|
||||
|
||||
The above is fussy. As a recommended alternative, we might modify the function so that if it is too large, the values are replaced by `NaN`. Here is one such function consuming a function and returning a modified function put to use to make this graph:
|
||||
|
||||
The above is fussy. As a recommended alternative, we might modify the function so that if it is too large, the values are replaced by `NaN`. Here is one such function consuming a function and returning a modified function put to use to make this graph]:
|
||||
|
||||
::: {#fig-plot-1-over-x-over-minus1-1-using-CalculusWithJulias-rangeclamp}
|
||||
```{julia}
|
||||
rangeclamp(f, hi=20, lo=-hi; replacement=NaN) = x -> lo < f(x) < hi ? f(x) : replacement
|
||||
plot(rangeclamp(x -> 1/x), -1, 1)
|
||||
```
|
||||
|
||||
(The `clamp` function is a base `Julia` function which clamps a number between `lo` and `hi`, returning `lo` or `hi` if `x` is outside that range.)
|
||||
Plot of $f(x) = 1/x$ over $[-1, 1]$ after wrapping in `rangclamp` to replace large `y` values in (absolute value) with `NaN`.
|
||||
:::
|
||||
|
||||
(The `clamp` function is a base `Julia` function and has slightly different semantics. It clamps a number between `lo` and `hi`, returning `lo` or `hi` (not `NaN`) if `x`is outside that range. The `rangeclamp` function is part of the `CalculusWithJulia` package and need not be defined if that package is loaded.)
|
||||
|
||||
|
||||
## Layers
|
||||
|
||||
|
||||
Graphing more than one function over the same viewing window is often desirable. Though this is easily done all at once in `Plots` by specifying a vector of functions as the first argument to `plot` instead of a single function object, we instead focus on building the graph layer by layer.^[The style of `Plots` is to combine multiple *series* to plot into one object and let `Plots` sort out which (every column is treated as a separate series). This can be very efficient from a programming perspective, but we leave it for power users. The use of layers, seems much easier to understand.]
|
||||
Graphing more than one function over the same viewing window is often desirable. Though this is easily done all at once in `Plots`, we instead focus on building the graph layer by layer.^[The style of `Plots` is to combine multiple *series* to plot into one object and let `Plots` sort out which (every column is treated as a separate series). This can be very efficient from a programming perspective, but we leave it for power users. The use of layers, seems much easier to understand.]
|
||||
|
||||
|
||||
For example, to see that a polynomial and the cosine function are "close" near $0$, we can plot *both* $\cos(x)$ and the function $f(x) = 1 - x^2/2$ over $[-\pi/2,\pi/2]$:
|
||||
|
||||
|
||||
::: {#fig-plot-cos-quadratic-approx-using-layers}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = 1 - x^2/2
|
||||
@@ -487,9 +531,13 @@ plot(cos, -pi/2, pi/2, label="cos")
|
||||
plot!(f, -pi/2, pi/2, label="f")
|
||||
```
|
||||
|
||||
Another useful function to add to a plot is one to highlight the $x$ axis. This makes identifying zeros of the function easier. The anonymous function `x -> 0` will do this. But, perhaps less cryptically, so will the base function `zero`. For example
|
||||
Plot of $f(x) = \cos(x)$ and a quadratic polynomial
|
||||
:::
|
||||
|
||||
Another useful function to add to a plot is one to highlight the $x$ axis. This makes identifying zeros of the function easier.^[The default frame style with `Plots` shows a scale around the boundaries of the graph. The specification `framestyle=:origin` labels the $x$ and $y$ axes] The anonymous function `x -> 0` will do this. But, perhaps less cryptically, so will the base function `zero`. For example
|
||||
|
||||
|
||||
::: {#fig-plot-of-x5-x-plus-1-over-I-add-zero-layer}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x^5 - x + 1
|
||||
@@ -497,6 +545,9 @@ plot(f, -1.5, 1.4, label="f")
|
||||
plot!(zero, label="zero")
|
||||
```
|
||||
|
||||
Plot of a polynomial with a layer showing `zero` (when $y=0$)
|
||||
:::
|
||||
|
||||
(The job of `zero` is to return "$0$" in the appropriate type. There is also a similar `one` function in base `Julia`.)
|
||||
|
||||
|
||||
@@ -505,21 +556,25 @@ The `plot!` call adds a layer. We could still specify the limits for the plot, t
|
||||
|
||||
For another example, suppose we wish to plot the function $f(x)=x\cdot(x-1)$ over the interval $[-1,2]$ and emphasize with points the fact that $0$ and $1$ are zeros. We can do this with three layers: the first to graph the function, the second to emphasize the $x$ axis, the third to graph the points.
|
||||
|
||||
|
||||
::: {#fig-plot-polynomial-zero-zeros}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x * (x-1)
|
||||
plot(f, -1, 2; legend=false) # turn off legend
|
||||
plot!(zero)
|
||||
scatter!([0,1], [0,0])
|
||||
zs = [(0,0), (1,0)] # vector of tuples
|
||||
scatter!(zs)
|
||||
```
|
||||
|
||||
The $3$ main functions used in these notes for adding layers are:
|
||||
Plot of a polynomial with the zero line and the zeros of the polynomial added as layers
|
||||
:::
|
||||
|
||||
The three main functions used in these notes for adding layers are:
|
||||
|
||||
|
||||
* `plot!(f, a, b)` to add the graph of the function `f`; also `plot!(xs, ys)`
|
||||
|
||||
* `scatter!(xs, ys)` to add points $(x_1, y_1), (x_2, y_2), \dots$.
|
||||
* `scatter!([(x1, y1), (x2, y2), ...])` or `scatter!(xs, ys)` to add points $(x_1, y_1), (x_2, y_2), \dots$.
|
||||
|
||||
* `annotate!((x,y, label))` to add a label at $(x,y)$
|
||||
|
||||
@@ -536,23 +591,35 @@ The `Plots` package uses positional arguments for input data and keyword argumen
|
||||
The `Plots` package provides many such arguments for adjusting a graphic, here we mention just a few:
|
||||
|
||||
|
||||
* `plot(...; title="main title", xlabel="x axis label", ylabel="y axis label")`: add title and label information to a graphic
|
||||
* `plot(...; label="a label")` the `label` attribute will show up when a legend is present. Using an empty string, `""`, will suppress add the layer to the legend.
|
||||
* `plot(...; legend=false)`: by default, different layers will be indicated with a legend, this will turn off this feature
|
||||
* `plot(...; xlims=(a,b), ylims=(c,d))`: either or both `xlims` and `ylims` can be used to control the viewing window
|
||||
* `plot(...; xticks=[xs..], yticks=[ys...]: either or both `xticks` and `yticks` can be used to specify where the tick marks are to be drawn
|
||||
* `plot(...; aspect_ratio=:equal)`: will keep $x$ and $y$ axis on same scale so that squares look square.
|
||||
* `plot(...; framestyle=:origin)`: The default `framestyle` places $x$-$y$ guides on the edges; this specification places them on the $x-y$ plane.
|
||||
* `plot(...; color="green")`: this argument can be used to adjust the color of the drawn figure (color can be a string,`"green"`, or a symbol, `:green`, among other specifications)
|
||||
* `plot(...; linewidth=5)`: this argument can be used to adjust the width of drawn lines
|
||||
* `plot(...; linestyle=:dash)`: will change the line style of the plotted lines to dashed lines. Also `:dot`, ...
|
||||
* `plot(...; title="main title", xlabel="x axis label", ylabel="y axis label")`: add a title to a graphic
|
||||
|
||||
* `plot(...; xlabel="x axis label", ylabel="y axis label")`: add label information to a graphic
|
||||
|
||||
* `plot(...; label="a label")` the `label` attribute will show up when a legend is present. Using an empty string, `""`, will suppress add the layer to the legend.
|
||||
|
||||
* `plot(...; legend=false)`: by default, different layers will be indicated with a legend, this will turn off this feature
|
||||
|
||||
* `plot(...; xlims=(a,b), ylims=(c,d))`: either or both `xlims` and `ylims` can be used to control the viewing window
|
||||
|
||||
* `plot(...; xticks=[xs..], yticks=[ys...]` either or both `xticks` and `yticks` can be used to specify where the tick marks are to be drawn
|
||||
|
||||
* `plot(...; aspect_ratio=:equal)`: will keep $x$ and $y$ axis on same scale so that squares look square.
|
||||
|
||||
* `plot(...; framestyle=:origin)`: The default `framestyle` places $x$-$y$ guides on the edges; this specification places them on the $x-y$ plane.
|
||||
|
||||
* `plot(...; color="green")`: this argument can be used to adjust the color of the drawn figure (color can be a string,`"green"`, or a symbol, `:green`, among other specifications)
|
||||
|
||||
* `plot(...; linewidth=5)`: this argument can be used to adjust the width of drawn lines
|
||||
|
||||
* `plot(...; linestyle=:dash)`: will change the line style of the plotted lines to dashed lines. Also `:dot`, ...
|
||||
|
||||
|
||||
For plotting points with `scatter`, or `scatter!` the markers can be adjusted via
|
||||
|
||||
|
||||
* `scatter(...; markersize=5)`: increase marker size
|
||||
* `scatter(...; marker=:square)`: change the marker (uses a symbol, not a string to specify)
|
||||
* `scatter(...; markersize=5)`: increase marker size
|
||||
|
||||
* `scatter(...; marker=:square)`: change the marker (uses a symbol, not a string to specify)
|
||||
|
||||
|
||||
Of course, zero, one, or more of these can be used on any given call to `plot`, `plot!`, `scatter`, or `scatter!`.
|
||||
@@ -562,10 +629,55 @@ Of course, zero, one, or more of these can be used on any given call to `plot`,
|
||||
There are also several *shorthands* in `Plots` that allows several related attributes to be specified to a single argument that is disambiguated using the type of the value. A few used herein are:
|
||||
|
||||
* `line`. For example, `line=(5, 0.25, "blue")` will specify `linewidth=5` (integer), `linecolor="blue"` (string or symbol), `linealpha=0.25` (floating point)
|
||||
|
||||
* `marker`. For example `marker=(:star, 5)` will specify `markerstyle=:star` (symbol) and `markersize=5` (integer).
|
||||
|
||||
* `fill`. For example `fill=(:blue, 0.25)` will specify `fillcolor=:blue` (string or symbol) and `fillalpha=0.25` (floating point).
|
||||
|
||||
#### Example: Bresenham's algorithm
|
||||
|
||||
## Points, lines, polygons
|
||||
|
||||
Two basic objects to graph are points and lines. Add to these polygons.
|
||||
|
||||
A point in two-dimensional space has two coordinates, often denoted by $(x,y)$. In `Julia`, the same notation produces a `tuple`. Using square brackets, as in `[x,y]`, produces a vector. Vectors are are more commonly used in these notes, as we have seen there are algebraic operations defined for them. However, tuples have other advantages and are how `Plots` designates a point.
|
||||
|
||||
The plot command `plot(xs, ys)` plots the points $(x_1,y_1), \dots, (x_n, y_n)$ and then connects adjacent points with lines. The command `scatter(xs, ys)` just plots the points.
|
||||
|
||||
However, the points might be more naturally specified as coordinate pairs. If tuples are used to pair them off, then `Plots` will plot a vector of tuples as a sequence of points through `plot([(x1,y1), (x2, y2), ..., (xn, yn)])`:
|
||||
|
||||
::: {#fig-scatter-plot-vector-of-tuples}
|
||||
```{julia}
|
||||
pts = [( 1, 0), ( 1/4, 1/4), (0, 1), (-1/4, 1/4),
|
||||
(-1, 0), (-1/4, -1/4), (0, -1), ( 1/4, -1/4)]
|
||||
scatter(pts; legend=false)
|
||||
```
|
||||
Scatter plot produced from a vector of tuples
|
||||
:::
|
||||
|
||||
A line segment simply connects two points. While these can be specified as vectors of $x$ and $y$ values, again it may be more convenient to use coordinate pairs to specify the points. Continuing the above, we can connect adjacent points with line segments:
|
||||
|
||||
::: {#fig-scatter-plot-vector-of-tuples-and-connecting-lines}
|
||||
```{julia}
|
||||
plot!(pts; line=(:gray, 0.5, :dash))
|
||||
```
|
||||
|
||||
Previous scatter plot with points connected with lines
|
||||
:::
|
||||
|
||||
This uses the shorthand notation of `Plots` to specify `linecolor=:gray, linealpha=0.5, linestyle=:dash`. To plot just a line segment, just specifying two points suffices.
|
||||
|
||||
The four-pointed star is not closed off, as there isn't a value from the last point to the first point. A polygon closes itself off. The `Shape` function can take a vector of points or a pair of `xs` and `ys` to specify a polygon. When these are plotted, the arguments to `fill` describe the interior of the polygon, the arguments to `line` the boundary:
|
||||
|
||||
::: {#fig-Shape-example-with-vector-of-tuples}
|
||||
```{julia}
|
||||
plot(Shape(pts); fill=(:gray, 0.25), line=(:black, 2), legend=false)
|
||||
scatter!(pts)
|
||||
```
|
||||
|
||||
Plot of a `Shape` using vector of tuples to specify vertices
|
||||
:::
|
||||
|
||||
##### Example: Bresenham's algorithm
|
||||
|
||||
|
||||
In plotting a primitive, like a line, some mapping of the mathematical object to a collection of pixels must be made. For the case of a line [Bresenhams's line algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm) can be used.
|
||||
@@ -573,8 +685,10 @@ In plotting a primitive, like a line, some mapping of the mathematical object to
|
||||
In the simplest case, let's assume a few things:
|
||||
|
||||
* we have a line with slope $-1 < m < 0$.
|
||||
|
||||
* the pixels have integer coordinates (e.g., the pixel $(1, -1)$ would cover the region $[1,2] \times [-1, -2]$ when lit.)
|
||||
* we start at point $(x_0, y_0)$, $f(x_0) = y_0$, with integer coordinates and end a point $(x_1, y_1)$, also with integer coordinates. The pixel $(x_0,y_0)$ is lit.
|
||||
|
||||
* we start at point $(x_0, y_0)$, $f(x_0) = y_0$, with integer coordinates and end at point $(x_1, y_1)$, also with integer coordinates. The pixel $(x_0,y_0)$ is lit.
|
||||
|
||||
With these assumptions, we have an initial decision to make:
|
||||
|
||||
@@ -600,13 +714,13 @@ A,B,C = -(y₁ - y₀), (x₁-x₀), -x₁*y₀ + x₀*y₁
|
||||
|
||||
f(x,y) = A*x + B*y + C
|
||||
|
||||
xs = [(x₀, y₀)]
|
||||
for i ∈ 1:(x₁ - 1)
|
||||
xs = [(x₀, y₀)] # initial point
|
||||
for i in 1:(x₁ - 1)
|
||||
xᵢ, yᵢ = xs[end]
|
||||
xᵢ₊₁ = xᵢ + 1
|
||||
Δ = f(xᵢ+1, yᵢ-1/2) > 0 ? 1 : 0
|
||||
yᵢ₊₁ = yᵢ - Δ
|
||||
push!(xs, (xᵢ₊₁, yᵢ₊₁))
|
||||
push!(xs, (xᵢ₊₁, yᵢ₊₁)) # add another point
|
||||
end
|
||||
|
||||
xs
|
||||
@@ -618,11 +732,11 @@ We can visualize with the following:
|
||||
p = plot(f, x₀, x₁; legend=false, aspect_ratio=:equal,
|
||||
xticks=0:x₁, yticks = (floor(Int, f(x₁))-1):(1 + ceil(Int, f(x₀))))
|
||||
col = RGBA(.64,.64,.64, 0.25)
|
||||
for xy ∈ xs
|
||||
x, y = xy
|
||||
for (x, y) in xs
|
||||
scatter!([x], [y]; marker=(5,))
|
||||
scatter!([x+1], [y - 1/2]; marker=(5,:star))
|
||||
plot!(Shape(x .+ [0, 1, 1, 0], y .+ [0, 0, -1, -1]); color=col)
|
||||
pixel = Shape([x, x+1, x+1, x], [y, y, y-1, y-1])
|
||||
plot!(pixel; fill=(col,))
|
||||
end
|
||||
p
|
||||
```
|
||||
@@ -631,39 +745,6 @@ We see a number of additional arguments used: different marker sizes and shapes
|
||||
|
||||
Of course, generalizations for positive slope and slope with magnitude greater than $1$ are needed. As well, this basic algorithm could be optimized, especially if it is part of a lower-level drawing primitive. But this illustrates the considerations involved.
|
||||
|
||||
## Points, lines, polygons
|
||||
|
||||
Two basic objects to graph are points and lines. Add to these polygons.
|
||||
|
||||
A point in two-dimensional space has two coordinates, often denoted by $(x,y)$. In `Julia`, the same notation produces a `tuple`. Using square brackets, as in `[x,y]`, produces a vector. Vectors are are more commonly used in these notes, as we have seen there are algebraic operations defined for them. However, tuples have other advantages and are how `Plots` designates a point.
|
||||
|
||||
The plot command `plot(xs, ys)` plots the points $(x_1,y_1), \dots, (x_n, y_n)$ and then connects adjacent points with lines. The command `scatter(xs, ys)` just plots the points.
|
||||
|
||||
However, the points might be more naturally specified as coordinate pairs. If tuples are used to pair them off, then `Plots` will plot a vector of tuples as a sequence of points through `plot([(x1,y1), (x2, y2), ..., (xn, yn)])`:
|
||||
|
||||
```{julia}
|
||||
pts = [( 1, 0), ( 1/4, 1/4), (0, 1), (-1/4, 1/4),
|
||||
(-1, 0), (-1/4, -1/4), (0, -1), ( 1/4, -1/4)]
|
||||
scatter(pts; legend=false)
|
||||
```
|
||||
|
||||
A line segment simply connects two points. While these can be specified as vectors of $x$ and $y$ values, again it may be more convenient to use coordinate pairs to specify the points. Continuing the above, we can connect adjacent points with line segments:
|
||||
|
||||
```{julia}
|
||||
plot!(pts; line=(:gray, 0.5, :dash))
|
||||
```
|
||||
|
||||
This uses the shorthand notation of `Plots` to specify `linecolor=:gray, linealpha=0.5, linestyle=:dash`. To plot just a line segment, just specifying two points suffices.
|
||||
|
||||
The four-pointed star is not closed off, as there isn't a value from the last point to the first point. A polygon closes itself off. The `Shape` function can take a vector of points or a pair of `xs` and `ys` to specify a polygon. When these are plotted, the arguments to `fill` describe the interior of the polygon, the arguments to `line` the boundary:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(Shape(pts); fill=(:gray, 0.25), line=(:black, 2), legend=false)
|
||||
scatter!(pts)
|
||||
```
|
||||
|
||||
|
||||
## Graphs of parametric equations
|
||||
|
||||
|
||||
@@ -678,13 +759,16 @@ A different graph can be made to compare the two functions side-by-side. This is
|
||||
|
||||
The most "famous" parametric graph is one that is likely already familiar, as it follows the parametrization of points on the unit circle by the angle made between the $x$ axis and the ray from the origin through the point. (If not familiar, this will soon be discussed in these notes.)
|
||||
|
||||
|
||||
::: {#fig-plot-cos-sin-parametrically-aspect-ratio-equal}
|
||||
```{julia}
|
||||
f(x) = cos(x); g(x) = sin(x)
|
||||
ts = range(0, 2pi, length=100)
|
||||
plot(f.(ts), g.(ts), aspect_ratio=:equal) # make equal axes
|
||||
```
|
||||
|
||||
Parametric plot of circle
|
||||
:::
|
||||
|
||||
Any point $(a,b)$ on this graph is represented by $(\cos(t), \sin(t))$ for some value of $t$, and in fact multiple values of $t$, since $t + 2k\pi$ will produce the same $(a,b)$ value as $t$ will.
|
||||
|
||||
|
||||
@@ -713,17 +797,20 @@ scatter!(f.(θs), g.(θs))
|
||||
|
||||
As with the plot of a univariate function, there is a convenience interface for these plots---just pass the two functions in:
|
||||
|
||||
|
||||
::: {#fig-parametric-plot-passing-two-functions}
|
||||
```{julia}
|
||||
plot(f, g, 0, 2pi, aspect_ratio=:equal)
|
||||
```
|
||||
|
||||
Illustration of `Plots` recipe for parametric plots
|
||||
:::
|
||||
|
||||
##### Example
|
||||
|
||||
|
||||
Looking at growth. Comparing $x^2$ with $x^3$ can run into issues, as the scale gets big:
|
||||
|
||||
|
||||
::: {#fig-compare-growth-by-layers}
|
||||
```{julia}
|
||||
x²(x) = x^2
|
||||
x³(x) = x^3
|
||||
@@ -731,14 +818,20 @@ plot(x², 0, 25)
|
||||
plot!(x³, 0, 25)
|
||||
```
|
||||
|
||||
Plotting layers for $x^2$ and $x^3$ to compare growth
|
||||
:::
|
||||
|
||||
In the above, `x³` is already $25$ times larger on the scale of $[0,25]$ and this only gets worse if the viewing window were to get larger. However, the parametric graph is quite different:
|
||||
|
||||
|
||||
::: {#fig-compare-growth-by-a-parametric-plot}
|
||||
```{julia}
|
||||
plot(x², x³, 0, 25)
|
||||
```
|
||||
|
||||
In this graph, as $x^3/x^2 = x$, as $x$ gets large, the ratio stays reasonable.
|
||||
Plotting parametrically to compare $x^2$ and $x^3$
|
||||
:::
|
||||
|
||||
In @fig-compare-growth-by-a-parametric-plot as $x$ gets large, the ratio stays reasonable, it being $x^3/x^2 = x$.
|
||||
|
||||
|
||||
##### Example
|
||||
@@ -749,7 +842,7 @@ Parametric plots are useful to compare the ratio of values near a point. In the
|
||||
|
||||
Plot $f(x) = x^3$ and $g(x) = x - \sin(x)$ around $x=0$:
|
||||
|
||||
|
||||
::: {#fig-compare-x-minus-sinx-to-x-cubed}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x^3
|
||||
@@ -757,7 +850,10 @@ g(x) = x - sin(x)
|
||||
plot(f, g, -pi/2, pi/2)
|
||||
```
|
||||
|
||||
This graph is *nearly* a straight line. At the point $(0,0)=(f(0), g(0))$, we see that both functions are behaving in a similar manner, though the slope is not $1$, so they do not increase at exactly the same rate.
|
||||
Parametric plot to compare $f(x) = x^3$ with $g(x) = x - \sin(x)$ near $0$
|
||||
:::
|
||||
|
||||
The graph in @fig-compare-x-minus-sinx-to-x-cubed is *nearly* a straight line. At the point $(0,0)=(f(0), g(0))$, we see that both functions are behaving in a similar manner, though the slope is not $1$, so they do not increase at exactly the same rate.
|
||||
|
||||
|
||||
##### Example: Etch A Sketch
|
||||
@@ -769,10 +865,13 @@ This graph is *nearly* a straight line. At the point $(0,0)=(f(0), g(0))$, we se
|
||||
Playing with the toy makes a few things become clear:
|
||||
|
||||
|
||||
* Twisting just the left knob (the horizontal or $x$ motion) will move the pointer left or right, leaving a horizontal line. Parametrically, this would follow the equations $f(t) = \xi(t)$ for some $\xi$ and $g(t) = c$.
|
||||
* Twisting just the right knob (the vertical or $y$ motion) will move the pointer up or down, leaving a vertical line. Parametrically, this would follow the equations $f(t) = c$ and $g(t) = \psi(t)$ for some $\psi$.
|
||||
* Drawing a line with a slope different from $0$ or $\infty$ requires moving both knobs at the same time. A $45$$^\circ$ line with slope $m=1$ can be made by twisting both at the same rate, say through $f(t) = ct$, $g(t)=ct$. It doesn't matter how big $c$ is, just that it is the same for both $f$ and $g$. Creating a different slope is done by twisting at different rates, say $f(t)=ct$ and $g(t)=dt$. The slope of the resulting line will be $d/c$.
|
||||
* Drawing a curve is done by twisting the two knobs with varying rates.
|
||||
* Twisting just the left knob (the horizontal or $x$ motion) will move the pointer left or right, leaving a horizontal line. Parametrically, this would follow the equations $f(t) = \xi(t)$ for some $\xi$ and $g(t) = c$.
|
||||
|
||||
* Twisting just the right knob (the vertical or $y$ motion) will move the pointer up or down, leaving a vertical line. Parametrically, this would follow the equations $f(t) = c$ and $g(t) = \psi(t)$ for some $\psi$.
|
||||
|
||||
* Drawing a line with a slope different from $0$ or $\infty$ requires moving both knobs at the same time. A $45$$^\circ$ line with slope $m=1$ can be made by twisting both at the same rate, say through $f(t) = ct$, $g(t)=ct$. It doesn't matter how big $c$ is, just that it is the same for both $f$ and $g$. Creating a different slope is done by twisting at different rates, say $f(t)=ct$ and $g(t)=dt$. The slope of the resulting line will be $d/c$.
|
||||
|
||||
* Drawing a curve is done by twisting the two knobs with varying rates.
|
||||
|
||||
|
||||
These all apply to parametric plots, as the Etch A Sketch trace is no more than a plot of $(f(t), g(t))$ over some range of values for $t$, where $f$ describes the movement in time of the left knob and $g$ the movement in time of the right.
|
||||
@@ -790,7 +889,7 @@ Parametric plots can describe a richer set of curves than can plots of functions
|
||||
|
||||
Here is an example using a parameterization provided on the Wikipedia page where $R$ is the radius of the larger disc, $r$ the radius of the smaller disc and $\rho < r$ indicating the position of the pencil within the smaller disc.
|
||||
|
||||
|
||||
::: {#fig-spirograph-formula-parametric-plot}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
R, r, rho = 1, 1/4, 1/4
|
||||
@@ -800,8 +899,10 @@ g(t) = (R-r) * sin(t) - rho * sin((R-r)/r * t)
|
||||
plot(f, g, 0, max((R-r)/r, r/(R-r))*2pi)
|
||||
```
|
||||
|
||||
In the above, one can fix $R=1$. Then different values for `r` and `rho` will produce different graphs. These graphs will be periodic if $(R-r)/r$ is a rational. (Nothing about these equations requires $\rho < r$.)
|
||||
Plot of two circles moving about each other
|
||||
:::
|
||||
|
||||
In the above, one can fix $R=1$. Then different values for `r` and `rho` will produce different graphs. These graphs will be periodic if $(R-r)/r$ is a rational. (Nothing about these equations requires $\rho < r$.)
|
||||
|
||||
|
||||
|
||||
@@ -821,8 +922,8 @@ choices = ["`(-Inf, -1)` and `(0,1)`",
|
||||
"`(-Inf, -0.577)` and `(0.577, Inf)`",
|
||||
"`(-1, 0)` and `(1, Inf)`"
|
||||
];
|
||||
answ=3;
|
||||
radioq(choices, answ)
|
||||
answer=3;
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -852,8 +953,8 @@ choices = ["`(-Inf, -3)` and `(0, 1)`",
|
||||
"`(-3, 0)` and `(1, Inf)`",
|
||||
"`(-Inf, -4.1)` and `(1.455, Inf)`"
|
||||
];
|
||||
answ=2;
|
||||
radioq(choices, answ)
|
||||
answer=2;
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -912,8 +1013,8 @@ choices = [
|
||||
"`f(x) = x <= 4 ? 35.0 : 35.0 + 10.0 * (x-4)`",
|
||||
"`f(x) = x <= 10 ? 35.0 : 35.0 + 4.0 * (x-10)`"
|
||||
]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
Make a plot of the model. Graphically estimate how many bags of trash will cost 55 dollars.
|
||||
@@ -922,8 +1023,8 @@ Make a plot of the model. Graphically estimate how many bags of trash will cost
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
answ = 15
|
||||
numericq(answ, .5)
|
||||
answer = 15
|
||||
numericq(answer, .5)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -966,8 +1067,8 @@ What is seen?
|
||||
choices = [L"It oscillates wildly, as the period is $T=2\pi/(500 \pi)$ so there are 250 oscillations.",
|
||||
"It should oscillate evenly, but instead doesn't oscillate very much near 0 and 1",
|
||||
L"Oddly, it looks exactly like the graph of $f(x) = \sin(2\pi x)$."]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
The algorithm to plot a function works to avoid aliasing issues. Does the graph generated by `plot(f, 0, 1)` look the same, as the one above?
|
||||
@@ -980,8 +1081,8 @@ choices = ["Yes",
|
||||
"No, but is still looks pretty bad, as fitting 250 periods into a too small number of pixels is a problem.",
|
||||
"No, the graph shows clearly all 250 periods."
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1027,8 +1128,8 @@ choices = [
|
||||
"An ellipse",
|
||||
"A straight line"
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1065,8 +1166,8 @@ choices = [
|
||||
"A straight line",
|
||||
"None of the above"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
Make this plot for the following specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
|
||||
@@ -1088,8 +1189,8 @@ choices = [
|
||||
"A straight line",
|
||||
"None of the above"
|
||||
]
|
||||
answ = 3
|
||||
radioq(choices, answ,keep_order=true)
|
||||
answer = 3
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
Make this plot for the specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
|
||||
@@ -1111,8 +1212,8 @@ choices = [
|
||||
"A straight line",
|
||||
"None of the above"
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
Make this plot for the specific values of the parameters `R`, `r`, and `rho`. What shape best describes it?
|
||||
@@ -1134,6 +1235,6 @@ choices = [
|
||||
"A straight line",
|
||||
"None of the above"
|
||||
]
|
||||
answ = 5
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 5
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
@@ -1,7 +1,6 @@
|
||||
# Polynomials
|
||||
|
||||
|
||||
Now that basic properties of functions have been discussed, we move to various types of related functions beginning with polynomial functions.
|
||||
With the basic properties of functions have been discussed, we move to various types of related functions beginning with polynomial functions.
|
||||
|
||||
|
||||
{{< include ../_common_code.qmd >}}
|
||||
@@ -12,7 +11,7 @@ In this section we use the following add-on packages:
|
||||
```{julia}
|
||||
using SymPy
|
||||
using Plots
|
||||
plotly()
|
||||
plotly();
|
||||
```
|
||||
|
||||
```{julia}
|
||||
@@ -26,13 +25,13 @@ nothing
|
||||
|
||||
|
||||
|
||||
Polynomials are a particular class of expressions that are simple enough to have many properties that can be analyzed. In particular, the key concepts of calculus: limits, continuity, derivatives, and integrals are all relatively trivial for polynomial functions. However, polynomials are flexible enough that they can be used to approximate a wide variety of functions. Indeed, though we don't pursue this, we mention that `Julia`'s `ApproxFun` package exploits this to great advantage.
|
||||
Polynomials are a particular class of expressions that are simple enough to have many properties that can be analyzed. In particular, the key concepts of calculus: limits, continuity, derivatives, and integrals are all relatively straightforward for polynomial functions. However, polynomials are flexible enough that they can be used to approximate a wide variety of functions. Indeed, though we don't pursue this, we mention that `Julia`'s `ApproxFun` package exploits this to great advantage.
|
||||
|
||||
|
||||
Here we discuss some vocabulary and basic facts related to polynomials and show how the add-on `SymPy` package can be used to model polynomial expressions within `SymPy`. `SymPy` provides a Computer Algebra System (CAS) for `Julia`. In this case, by leveraging a mature `Python` package [SymPy](https://www.sympy.org/). Later we will discuss the `Polynomials` package for polynomials.
|
||||
Here we discuss some vocabulary and basic facts related to polynomials and show how the add-on `SymPy` package can be used to model polynomial expressions within `SymPy`. `SymPy` provides a Computer Algebra System (CAS) for `Julia`. In this case, by leveraging a mature `Python` package [SymPy](https://www.sympy.org/). In the next section we will discuss the `Polynomials` package for representing polynomials.
|
||||
|
||||
|
||||
For our purposes, a *monomial* is simply a non-negative integer power of $x$ (or some other indeterminate symbol) possibly multiplied by a scalar constant. For example, $5x^4$ is a monomial, as are constants, such as $-2$ (it being $-2x^0$) and the symbol $x$ itself (it begin $x^1$. In general, one may consider restrictions on where the constants can come from, and consider more than one symbol, but we won't pursue this here, restricting ourselves to the case of a single variable and real coefficients.
|
||||
For our purposes, a *monomial* is simply a non-negative integer power of $x$ (or some other indeterminate symbol) possibly multiplied by a scalar constant. For example, $5x^4$ is a monomial, as are constants, such as $-2$ (it being $-2x^0$) and the symbol $x$ itself (it being $x^1$). In general, one may consider restrictions on where the constants can come from, and consider more than one symbol, but we won't pursue this here, restricting ourselves to the case of a single variable and real coefficients.
|
||||
|
||||
|
||||
A *polynomial* is a sum of monomials. After combining terms with same powers, a non-zero polynomial may be written uniquely as:
|
||||
@@ -42,6 +41,9 @@ $$
|
||||
a_n x^n + a_{n-1}x^{n-1} + \cdots + a_1 x + a_0, \quad a_n \neq 0
|
||||
$$
|
||||
|
||||
The zero polynomial is just $0$.
|
||||
|
||||
::: {#fig-various-even-degree-monic-monomials}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -61,13 +63,16 @@ end
|
||||
|
||||
imgfile = tempname() * ".gif"
|
||||
gif(anim, imgfile, fps = 1)
|
||||
caption = "Polynomials of varying even degrees over ``[-1,1]``."
|
||||
caption = ""
|
||||
|
||||
plotly()
|
||||
ImageFile(imgfile, caption)
|
||||
```
|
||||
|
||||
The numbers $a_0, a_1, \dots, a_n$ are the **coefficients** of the polynomial in the standard basis. With the identifications that $x=x^1$ and $1 = x^0$, the monomials above have their power match their coefficient's index, e.g., $a_ix^i$. Outside of the coefficient $a_n$, the other coefficients may be negative, positive, *or* $0$. Except for the zero polynomial, the largest power $n$ is called the [degree](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). The degree of the [zero](http://tinyurl.com/he6eg6s) polynomial is typically not defined or defined to be $-1$, so as to make certain statements easier to express. The term $a_n$ is called the **leading coefficient**. When the leading coefficient is $1$, the polynomial is called a **monic polynomial**. The monomial $a_n x^n$ is the **leading term**.
|
||||
Polynomials of varying even degrees over $[-1,1]$
|
||||
:::
|
||||
|
||||
The numbers $a_0, a_1, \dots, a_n$ are the *coefficients* of the polynomial in the standard basis. With the identifications that $x=x^1$ and $1 = x^0$, the monomials above have their power match their coefficient's index, e.g., $a_ix^i$. Outside of the coefficient $a_n$, the other coefficients may be negative, positive, *or* $0$. Except for the zero polynomial, the largest power $n$ is called the [degree](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). The degree of the [zero](http://tinyurl.com/he6eg6s) polynomial is typically not defined or defined to be $-1$, so as to make certain statements easier to express. The term $a_n$ is called the *leading coefficient*. When the leading coefficient is $1$, the polynomial is called a *monic polynomial*. The monomial $a_n x^n$ is the *leading term*.
|
||||
|
||||
|
||||
For example, the polynomial $-16x^2 - 32x + 100$ has degree $2$, leading coefficient $-16$ and leading term $-16x^2$. It is not monic, as the leading coefficient is not $1$.
|
||||
@@ -86,13 +91,14 @@ $$
|
||||
a_1 x + a_0, \quad a_1 \neq 0,
|
||||
$$
|
||||
|
||||
is often written as $mx + b$, which is the **slope-intercept** form. The slope of a line determines how steeply it rises. The value of $m$ can be found from two points through the well-known formula:
|
||||
is often written as $mx + b$, which is the *slope-intercept* form. The slope of a line determines how steeply it rises. The value of $m$ can be found from two points through the well-known formula:
|
||||
|
||||
|
||||
$$
|
||||
m = \frac{y_1 - y_0}{x_1 - x_0} = \frac{\text{rise}}{\text{run}}
|
||||
$$
|
||||
|
||||
::: {#fig-graph-linear-polynomial-different-m}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -111,15 +117,17 @@ end
|
||||
|
||||
imgfile = tempname() * ".gif"
|
||||
gif(anim, imgfile, fps = 1)
|
||||
caption = "Graphs of y = mx for different values of m"
|
||||
caption = ""
|
||||
plotly()
|
||||
ImageFile(imgfile, caption)
|
||||
```
|
||||
Graphs of y = mx for different values of m
|
||||
:::
|
||||
|
||||
The intercept, $b$, comes from the fact that when $x=0$ the expression is $b$. That is the graph of the function $f(x) = mx + b$ will have $(0,b)$ as a point on it.
|
||||
The intercept, $b$, comes from the fact that when $x=0$ the expression is $b$. That is, the graph of the function $f(x) = mx + b$ will have $(0,b)$ as a point on it.
|
||||
|
||||
|
||||
More generally, we have the **point-slope** form of a line, written as a polynomial through
|
||||
More generally, we have the *point-slope* form of a line, written as a polynomial through
|
||||
|
||||
|
||||
$$
|
||||
@@ -138,11 +146,11 @@ Thinking in terms of transformations, this looks like the function $f(x) = x$ (w
|
||||
The indeterminate value `x` (or some other symbol) in a polynomial, is like a variable in a function and unlike a variable in `Julia`. Variables in `Julia` are identifiers, just a means to look up a specific, already determined, value. Rather, the symbol `x` is not yet determined, it is essentially a place holder for a future value. Although we have seen that `Julia` makes it very easy to work with mathematical functions, it is not the case that base `Julia` makes working with expressions of algebraic symbols easy. This makes sense, `Julia` is primarily designed for technical computing, where numeric approaches rule the day. However, symbolic math can be used from within `Julia` through add-on packages.
|
||||
|
||||
|
||||
Symbolic math programs include well-known ones like the commercial programs Mathematica and Maple. Mathematica powers the popular [WolframAlpha](www.wolframalpha.com) website, which turns "natural" language into the specifics of a programming language. The open-source [Sage](https://www.sagemath.org/) project is an alternative to these two commercial giants. It includes a wide-range of open-source math projects available within its umbrella framework. (`Julia` can even be run from within the free service [cloud.sagemath.com](https://cloud.sagemath.com/projects).) A more focused project for symbolic math, is the [SymPy](www.sympy.org) Python library. SymPy is also used within Sage. However, SymPy provides a self-contained library that can be used standalone within a Python session.
|
||||
Symbolic math programs include well-known ones like the commercial programs Mathematica and Maple. Mathematica powers the popular [WolframAlpha](www.wolframalpha.com) website, which turns "natural" language into the specifics of a programming language. The open-source [Sage](https://www.sagemath.org/) project is an alternative to these two commercial giants. It includes a wide-range of open-source math projects available within its umbrella framework. A more focused project for symbolic math, is the [SymPy](www.sympy.org) Python library. SymPy is also used within Sage. However, SymPy provides a self-contained library that can be used standalone within a Python session.
|
||||
|
||||
The [Symbolics](https://github.com/JuliaSymbolics/Symbolics.jl) package for `Julia` provides a "fast and modern CAS for fast and modern language." It is described further in [Symbolics.jl](../alternatives/symbolics.qmd).
|
||||
The [Symbolics](https://github.com/JuliaSymbolics/Symbolics.jl) package for `Julia` provides a "fast and modern CAS for fast and modern language." It is described further in [Symbolics.jl](../alternatives/symbolics.qmd). The [Giac.jl](https://github.com/s-celles/Giac.jl/) package is another alternative.
|
||||
|
||||
As `SymPy` has some features not yet implemented in `Symbolics`, we use `SymPy` in these notes. The `PyCall` and `PythonCall` packages are available to glue `Julia` to Python in a seamless manner. These allow the `Julia` package `SymPy` (or `SymPyPythonCall`) to provide functionality from SymPy within `Julia`.
|
||||
As `SymPy` has some features not yet implemented in `Symbolics`, we use `SymPy` in these notes. The `PyCall` and `PythonCall` packages are available to glue `Julia` to Python in a seamless manner. These allow the `Julia` package `SymPy` (or `SymPyPythonCall`) to provide functionality from SymPy within `Julia`. (`Giac` calls a `C++` library and is an alternative that may not be easier to install.)
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
@@ -152,9 +160,8 @@ When `SymPy` is installed through the package manager, the underlying `Python` l
|
||||
:::
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
The [`Symbolics`](../alternatives/symbolics) package is a rapidly developing `Julia`-only package that provides symbolic math options.
|
||||
|
||||
## Alternative
|
||||
The [Symbolics](../alternatives/symbolics) package is a rapidly developing `Julia`-only package that provides symbolic math options. The [Giac package](https://github.com/s-celles/Giac.jl/) is another good alternative to `SymPy`. Both packages are described further when alternative packages are discussed.
|
||||
:::
|
||||
|
||||
---
|
||||
@@ -167,7 +174,7 @@ To use `SymPy`, we create symbolic objects to be our indeterminate symbols. The
|
||||
@syms a, b, c, x::real, zs[1:10]
|
||||
```
|
||||
|
||||
The above shows that multiple symbols can be defined at once. The annotation `x::real` instructs `SymPy` to assume the `x` is real, as otherwise it assumes it is possibly complex. There are many other [assumptions](http://docs.sympy.org/dev/modules/core.html#module-sympy.core.assumptions) that can be made. The `@syms` macro documentation lists them. The `zs[1:10]` tensor notation creates a container with $10$ different symbols. The *macro* `@syms` does not need assignment, as the variable(s) are created behind the scenes by the macro.
|
||||
The above shows that multiple symbols can be defined at once. The annotation `x::real` instructs `SymPy` to assume the `x` is real, as otherwise it assumes it is possibly complex. There are many other [assumptions](http://docs.sympy.org/dev/modules/core.html#module-sympy.core.assumptions) that can be made. The `@syms` macro documentation lists them. The `zs[1:10]` "tensor notation" creates a container with $10$ different symbols. The *macro* `@syms` does not need assignment, as the variable(s) are created behind the scenes by the macro.
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
@@ -179,9 +186,11 @@ Macros in `Julia` are just transformations of the syntax into other syntax. The
|
||||
The `SymPy` package does three basic things:
|
||||
|
||||
|
||||
* It imports some of the functionality provided by `SymPy`, including the ability to create symbolic variables.
|
||||
* It overloads many `Julia` functions to work seamlessly with symbolic expressions. This makes working with polynomials quite natural.
|
||||
* It gives access to a wide range of SymPy's functionality through the `sympy` object.
|
||||
* It imports some of the functionality provided by `SymPy`, including the ability to create symbolic variables.
|
||||
|
||||
* It add methods for many `Julia` functions to work seamlessly with symbolic expressions. This makes working with polynomials quite natural.
|
||||
|
||||
* It gives access to a wide range of SymPy's functionality through the `sympy` object.
|
||||
|
||||
|
||||
To illustrate, using the just defined `x`, here is how we can create the polynomial $-16x^2 + 100$:
|
||||
@@ -263,13 +272,6 @@ The result will always be of a symbolic type, even if the answer is just a numbe
|
||||
typeof(y)
|
||||
```
|
||||
|
||||
If there is just one free variable in an expression, the pair notation can be dropped:
|
||||
|
||||
|
||||
```{julia}
|
||||
p(4) # substitutes x=>4
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
|
||||
@@ -298,28 +300,25 @@ In the above, we substituted `2` in for `x` to get `y`:
|
||||
```{julia}
|
||||
#| hold: true
|
||||
p = -16x^2 + 100
|
||||
y = p(2)
|
||||
y = p(x => 2)
|
||||
```
|
||||
|
||||
The value, $36$ is still symbolic, but clearly an integer. If we are just looking at the output, we can easily translate from the symbolic value to an integer, as they print similarly. However the conversion to an integer, or another type of number, does not happen automatically. If a number is needed to pass along to another `Julia` function, it may need to be converted. In general, conversions between different types are handled through various methods of `convert`.
|
||||
|
||||
For real numbers, an easy to call conversion is available through the `float` method:
|
||||
For real numbers, an easy to call conversion is available through the generic `float` method, which converts a value to a floating point type::
|
||||
|
||||
```{julia}
|
||||
float(y)
|
||||
```
|
||||
|
||||
|
||||
|
||||
The use of the generic `float` method returns a floating point number. (The `.evalf()` method of `SymPy` objects uses `SymPy` to produce floating point versions of symbolic values.
|
||||
|
||||
`SymPy` objects have their own internal types. To preserve these on conversion to a related `Julia` value, the `N` function from `SymPy` is useful:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
p = -16x^2 + 100
|
||||
N(p(2))
|
||||
N(p(x=>2))
|
||||
```
|
||||
|
||||
Where `convert(T, x)` requires a specification of the type to convert `x` to, `N` attempts to match the data type used by SymPy to store the number. As such, the output type of `N` may vary (rational, a BigFloat, a float, etc.) Conversion by `N` will fail if the value to be converted contains free symbols, as would be expected.
|
||||
@@ -333,7 +332,7 @@ Evaluating a symbolic expression and returning a numeric value can be done by co
|
||||
|
||||
```{julia}
|
||||
p = 200 - 16x^2
|
||||
N(p(2))
|
||||
N(p(x=>2))
|
||||
```
|
||||
|
||||
This approach is direct, but can be slow *if* many such evaluations were needed (such as with a plot). An alternative is to turn the symbolic expression into a `Julia` function and then evaluate that as usual.
|
||||
@@ -358,26 +357,26 @@ pp = lambdify(p)
|
||||
pp(1,2,3)
|
||||
```
|
||||
|
||||
This evaluation matches `a` with `1`, `b` with`2`, and `x` with `3` as that is the order returned by the function call `free_symbols(p)`. To adjust that, a second `vars` argument can be given:
|
||||
This evaluation matches `a` with `1`, `b` with`2`, and `x` with `3` as that is the order returned by the function call `free_symbols(p)`. Leaving the order to an underlying function is not a great idea, rather, explicitly passing the variables is recommended:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
pp = lambdify(p, (x,a,b))
|
||||
pp = lambdify(p, (x,a,b)) # specify the variables
|
||||
pp(1,2,3) # computes 2*1^2 + 3
|
||||
```
|
||||
|
||||
(We suggest using the pair notation when there is more than one variable.)
|
||||
|
||||
## Graphical properties of polynomials
|
||||
|
||||
|
||||
Consider the graph of the polynomial `x^5 - x + 1`:
|
||||
|
||||
Consider the graph of the polynomial `x^5 - x + 1` in @fig-plot-polynomial-using-sympy-plot-recipe:
|
||||
|
||||
::: {#fig-plot-polynomial-using-sympy-plot-recipe}
|
||||
```{julia}
|
||||
plot(x^5 - x + 1, -3/2, 3/2)
|
||||
```
|
||||
Plot of $f(x) = x^5 - x + 1$ using the recipe for `SymPy` expression
|
||||
:::
|
||||
|
||||
(Plotting symbolic expressions with `Plots` is similar to plotting a function, in that the expression is passed in as the first argument. The expression must have only one free variable, as above, or an error will occur. This happens, as there is a `Plots` "recipe" for `SymPy` defined.)
|
||||
|
||||
@@ -385,14 +384,16 @@ plot(x^5 - x + 1, -3/2, 3/2)
|
||||
This graph illustrates the key features of polynomial graphs:
|
||||
|
||||
|
||||
* there may be values for `x` where the graph crosses the $x$ axis (real roots of the polynomial);
|
||||
* there may be peaks and valleys (local maxima and local minima);
|
||||
* except for constant polynomials, the ultimate behaviour for large values of $|x|$ is either both sides of the graph going to positive infinity, or negative infinity, or as in this graph one to the positive infinity and one to negative infinity. In particular, there is no *horizontal asymptote*.
|
||||
* there may be values for `x` where the graph crosses the $x$ axis (real roots of the polynomial);
|
||||
|
||||
* there may be peaks and valleys (local maxima and local minima);
|
||||
|
||||
* except for constant polynomials, the ultimate behaviour for large values of $|x|$ is either both sides of the graph going to positive infinity, or negative infinity, or as in this graph one to the positive infinity and one to negative infinity. In particular, there is no *horizontal asymptote*.
|
||||
|
||||
|
||||
To investigate this last point, let's consider the case of the monomial $x^n$. When $n$ is even, the following animation shows that larger values of $n$ have greater growth once outside of $[-1,1]$:
|
||||
|
||||
To investigate this last point, let's consider the case of the monomial $x^n$. When $n$ is even, the animation in @fig-faster-growing-monomials-animation shows that larger values of $n$ have greater growth once outside of $[-1,1]$:
|
||||
|
||||
::: {#fig-faster-growing-monomials-animation}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -411,30 +412,36 @@ end
|
||||
|
||||
imgfile = tempname() * ".gif"
|
||||
gif(anim, imgfile, fps = 1)
|
||||
caption = L"Demonstration that $x^{10}$ grows faster than $x^8$, ... and $x^2$ grows faster than $x^0$ (which is constant)."
|
||||
caption = L""
|
||||
plotly()
|
||||
ImageFile(imgfile, caption)
|
||||
```
|
||||
Demonstration that $x^{10}$ grows faster than $x^8$, $\dots$, and $x^2$ grows faster than $x^0$ (which is constant)
|
||||
:::
|
||||
|
||||
Of course, this is expected, as, for example, $2^2 < 2^4 < 2^6 < \cdots$. The general shape of these terms is similar - $U$ shaped, and larger powers dominate the smaller powers as $|x|$ gets big.
|
||||
Of course, this is expected, as, for example, $2^2 < 2^4 < 2^6 < \cdots$. The general shape of these terms is similar---$U$ shaped, and larger powers dominate the smaller powers as $|x|$ gets big.
|
||||
|
||||
|
||||
For odd powers of $n$, the graph of the monomial $x^n$ is no longer $U$ shaped, but rather constantly increasing. This graph of $x^5$ is typical:
|
||||
|
||||
For odd powers of $n$, the graph of the monomial $x^n$ is no longer $U$ shaped, but rather constantly increasing. This graph of $x^5$ is typical; for larger powers the shape is similar, but the growth is faster.
|
||||
|
||||
::: {#fig-show-odd-power-shape}
|
||||
```{julia}
|
||||
plot(x^5, -2, 2)
|
||||
```
|
||||
|
||||
Again, for larger powers the shape is similar, but the growth is faster.
|
||||
Plot of odd degree monomial showing the general shape for large $x$.
|
||||
:::
|
||||
|
||||
|
||||
|
||||
### Leading term dominates
|
||||
|
||||
|
||||
To see the roots and/or the peaks and valleys of a polynomial requires a judicious choice of viewing window, as ultimately the leading term will dominate the graph. The following animation of the graph of $(x-5)(x-3)(x-2)(x-1)$ illustrates. Subsequent images show a widening of the plot window until the graph appears U-shaped.
|
||||
To see the roots and/or the peaks and valleys of a polynomial requires a judicious choice of viewing window, as ultimately the leading term will dominate the graph. @fig-leading-term-dominates shows the graph of the fourth-degree polynomial $(x-5)(x-3)(x-2)(x-1)$ over different domains. Subsequent images show a widening of the plot window until the graph appears U-shaped. The leading term in the animation is $x^4$, of even degree, so the graphic is U-shaped, were the leading term of odd degree the left and right sides would each head off to different signs of infinity.
|
||||
|
||||
|
||||
::: {#fig-leading-term-dominates}
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -457,14 +464,14 @@ anim = @animate for n in 1:6
|
||||
end
|
||||
end
|
||||
|
||||
caption = "The previous graph is highlighted in red. Ultimately the leading term (\$x^4\$ here) dominates the graph."
|
||||
imgfile = tempname() * ".gif"
|
||||
gif(anim, imgfile, fps=1)
|
||||
plotly()
|
||||
ImageFile(imgfile, caption)
|
||||
ImageFile(imgfile, "")
|
||||
```
|
||||
|
||||
The leading term in the animation is $x^4$, of even degree, so the graphic is U-shaped, were the leading term of odd degree the left and right sides would each head off to different signs of infinity.
|
||||
The previous graph is highlighted in red. Ultimately the leading term ($x^4$ here) dominates the graph.
|
||||
:::
|
||||
|
||||
|
||||
To illustrate analytically why the leading term dominates, consider the polynomial $2x^5 - x + 1$ and then factor out the largest power, $x^5$, leaving a product:
|
||||
@@ -474,15 +481,15 @@ $$
|
||||
x^5 \cdot (2 - \frac{1}{x^4} + \frac{1}{x^5}).
|
||||
$$
|
||||
|
||||
For large $|x|$, the last two terms in the product on the right get close to $0$, so this expression is *basically* just $2x^5$ - the leading term.
|
||||
For large $|x|$, the last two terms in the product on the right get close to $0$, so this expression is *basically* just $2x^5$---the leading term.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
The following graphic illustrates the $4$ basic *overall* shapes that can result when plotting a polynomials as $x$ grows without bound:
|
||||
|
||||
@fig-four-basic-polynomial-shapes illustrates the $4$ basic *overall* shapes that can result when plotting a polynomials as $x$ grows without bound:
|
||||
|
||||
::: {#fig-four-basic-polynomial-shapes}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
let
|
||||
@@ -501,12 +508,8 @@ plotly()
|
||||
nothing
|
||||
```
|
||||
|
||||
##### Example
|
||||
|
||||
This graphic shows some of the above:
|
||||
|
||||
[](https://m.youtube.com/watch?v=OFzqDatEvCo)
|
||||
|
||||
Figure illustrating the four basic shapes a polynomial may take for large values of $x$ or $-x$.
|
||||
:::
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -536,7 +539,7 @@ This observation is the start of Descartes' rule of [signs](http://sepwww.stanfo
|
||||
## Factoring polynomials
|
||||
|
||||
|
||||
Among numerous others, there are two common ways of representing a non-zero polynomial:
|
||||
Among others, there are two common ways of representing a non-zero polynomial:
|
||||
|
||||
|
||||
* expanded form, as in $a_n x^n + a_{n-1}x^{n-1} + \cdots + a_1 x + a_0,\quad a_n \neq 0$; or
|
||||
@@ -547,7 +550,7 @@ The former uses the *standard basis* to represent the polynomial $p$.
|
||||
The latter writes $p$ as a product of linear factors, though this is only possible in general if we consider complex roots. With real roots only, then the factors are either linear or quadratic, as will be discussed later.
|
||||
|
||||
|
||||
There are values to each representation. One value of the expanded form is that polynomial addition and scalar multiplication is much easier than in factored form. For example, adding polynomials just requires matching up the monomials of similar powers. (These can be realized easily as vector operations.) For the factored form, polynomial multiplication is much easier than expanded form. For the factored form it is easy to read off *roots* of the polynomial (values of $x$ where $p$ is $0$), as a product is $0$ only if a term is $0$, so any zero must be a zero of a factor. Factored form has other technical advantages. For example, the polynomial $(x-1)^{1000}$ can be compactly represented using the factored form, but would require $1001$ coefficients to store in expanded form. (As well, due to floating point differences, the two would evaluate quite differently as one would require over a $1000$ operations to compute, the other just two.)
|
||||
There are values to each representation. One value of the expanded form is that polynomial addition and scalar multiplication is much easier than in factored form. For example, adding polynomials just requires matching up the monomials of similar powers. For the factored form, polynomial multiplication is much easier than expanded form. For the factored form it is easy to read off *roots* of the polynomial (values of $x$ where $p$ is $0$), as a product is $0$ only if a term is $0$, so any zero must be a zero of a factor. Factored form has other technical advantages. For example, the polynomial $(x-1)^{1000}$ can be compactly represented using the factored form, but would require $1001$ coefficients to store in expanded form. (As well, due to floating point differences, the two would evaluate quite differently as one would require over a $1000$ operations to compute, the other just two.)
|
||||
|
||||
|
||||
Translating from factored form to expanded form can be done by carefully following the distributive law of multiplication. For example, with some care it can be shown that:
|
||||
@@ -594,10 +597,7 @@ The factoring $(x-\sqrt{2})\cdot(x + \sqrt{2})$ is not found, as $\sqrt{2}$ is n
|
||||
### Polynomial functions and polynomials.
|
||||
|
||||
|
||||
Our definition of a polynomial is in terms of algebraic expressions which are easily represented by `SymPy` objects, but not objects from base `Julia`. (Later we discuss the `Polynomials` package for representing polynomials. There is also the `AbstractAlbegra` package for a more algebraic treatment of polynomials.)
|
||||
|
||||
|
||||
However, *polynomial functions* are easily represented by `Julia`, for example,
|
||||
Our definition of a polynomial is in terms of algebraic expressions which are easily represented by `SymPy` objects, but not objects from base `Julia`. However, *polynomial functions* are easily represented by `Julia`, for example:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -607,22 +607,16 @@ f(x) = -16x^2 + 100
|
||||
The distinction is subtle, the expression is turned into a function just by adding the "`f(x) =`" preface. But to `Julia` there is a big distinction. The function form never does any computation until after a value of $x$ is passed to it. Whereas symbolic expressions can be manipulated quite freely before any numeric values are specified.
|
||||
|
||||
|
||||
It is easy to create a symbolic expression from a function - just evaluate the function on a symbolic value:
|
||||
It is easy to create a symbolic expression from a function---just evaluate the function on a symbolic value:
|
||||
|
||||
|
||||
```{julia}
|
||||
f(x)
|
||||
```
|
||||
|
||||
This is easy---but can also be confusing. The function object is `f`, the expression is `f(x)`---the function evaluated on a symbolic object. Moreover, as seen, the symbolic expression can be evaluated using the same syntax as a function call:
|
||||
This is easy---but can also be confusing. The function object is `f`, the expression is `f(x)`---the function evaluated on a symbolic object.
|
||||
|
||||
|
||||
```{julia}
|
||||
p = f(x)
|
||||
p(2)
|
||||
```
|
||||
|
||||
For many uses, the distinction is unnecessary to make, as the many functions will work with any callable expression. For `Plots` there is a recipe – either `plot(f, a, b)` or `plot(f(x), a, b)` will produce the same plot using the `Plots` package.
|
||||
For many uses, the distinction is unnecessary to make, as the many functions will work with any callable expression. For `Plots` there is a recipe---either `plot(f, a, b)` or `plot(f(x), a, b)` will produce the same plot using the `Plots` package.
|
||||
|
||||
|
||||
## Questions
|
||||
@@ -690,8 +684,8 @@ What is the leading term of $p$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["``3``", "``3x^2``", "``-2x``", "``5``"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -734,8 +728,8 @@ The linear polynomial $p = 2x + 3$ is written in which form:
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["point-slope form", "slope-intercept form", "general form"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -758,8 +752,8 @@ What command will return the value of the polynomial when $x=2$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = [q"p*2", q"p[2]", q"p_2", q"p(x=>2)"]
|
||||
answ = 4
|
||||
radioq(choices, answ)
|
||||
answer = 4
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -776,8 +770,8 @@ L"Be $U$-shaped, opening upward",
|
||||
L"Be $U$-shaped, opening downward",
|
||||
L"Overall, go upwards from $-\infty$ to $+\infty$",
|
||||
L"Overall, go downwards from $+\infty$ to $-\infty$"]
|
||||
answ = 3
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 3
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -794,8 +788,8 @@ L"Be $U$-shaped, opening upward",
|
||||
L"Be $U$-shaped, opening downward",
|
||||
L"Overall, go upwards from $-\infty$ to $+\infty$",
|
||||
L"Overall, go downwards from $+\infty$ to $-\infty$"]
|
||||
answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 1
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -812,8 +806,8 @@ L"Be $U$-shaped, opening upward",
|
||||
L"Be $U$-shaped, opening downward",
|
||||
L"Overall, go upwards from $-\infty$ to $+\infty$",
|
||||
L"Overall, go downwards from $+\infty$ to $-\infty$"]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -859,8 +853,8 @@ choices = [q"x^3 - 3x^2 + 2x",
|
||||
q"x^3 - x^2 - 2x",
|
||||
q"x^3 + x^2 - 2x",
|
||||
q"x^3 + x^2 + 2x"]
|
||||
answ = 2
|
||||
radioq(choices, 2)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -877,6 +871,6 @@ q"-h^2 + 3hx - 3x^2",
|
||||
q"h^3 + 3h^2x + 3hx^2 + x^3 -x^3/h",
|
||||
q"x^3 - x^3/h",
|
||||
q"0"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
+147
-117
@@ -1,6 +1,5 @@
|
||||
# Roots of a polynomial
|
||||
|
||||
|
||||
{{< include ../_common_code.qmd >}}
|
||||
|
||||
In this section we use the following add on packages:
|
||||
@@ -25,15 +24,18 @@ nothing
|
||||
---
|
||||
|
||||
|
||||
The [roots](http://en.wikipedia.org/wiki/Properties_of_polynomial_roots) of a polynomial are the values of $x$ that when substituted into the expression yield $0$. For example, the polynomial $x^2 - x$ has two roots, $0$ and $1$. A simple graph verifies this:
|
||||
The [roots](http://en.wikipedia.org/wiki/Properties_of_polynomial_roots) of a polynomial are the values of $x$ that when substituted into the polynomial yield $0$. For example, the polynomial $x^2 - x$ has two roots, $0$ and $1$. A simple graph verifies this:
|
||||
|
||||
|
||||
::: {#fig-plot-parabola-highlight-zero-line}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x^2 - x
|
||||
plot(f, -2, 2)
|
||||
plot!(zero, -2, 2)
|
||||
```
|
||||
Plot of $f(x) = x^2 - x$ over $[-2, 2]$ showing the graph crosses the $x$ axis twice
|
||||
:::
|
||||
|
||||
The graph crosses the $x$-axis at both $0$ and $1$.
|
||||
|
||||
@@ -41,9 +43,11 @@ The graph crosses the $x$-axis at both $0$ and $1$.
|
||||
What is known about polynomial roots? Some simple questions might be:
|
||||
|
||||
|
||||
* Will a polynomial always have a root?
|
||||
* How many roots can there be?
|
||||
* How large can the roots be?
|
||||
* Will a polynomial always have a root? A *real* root?
|
||||
|
||||
* How many roots can there be?
|
||||
|
||||
* How large can the roots be?
|
||||
|
||||
|
||||
We look at such questions here.
|
||||
@@ -52,10 +56,12 @@ We look at such questions here.
|
||||
### The factor theorem
|
||||
|
||||
|
||||
We begin with a comment that ties together two concepts related to polynomials. It allows us to speak of roots or factors interchangeably:
|
||||
We begin with a comment that ties together two concepts related to polynomials.
|
||||
|
||||
|
||||
> The [factor theorem](http://en.wikipedia.org/wiki/Factor_theorem) relates the *roots* of a polynomial with its *factors*: $r$ is a root of $p$ if *and* only if $(x-r)$ is a factor of the polynomial $p$.
|
||||
::: {.theorem title="The factor theorem"}
|
||||
The [factor theorem](http://en.wikipedia.org/wiki/Factor_theorem) states that
|
||||
$r$ is a root of the polynomial $p$ if *and* only if $(x-r)$ is a factor of $p$.
|
||||
:::
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +94,7 @@ From this, we see that $f(c) = r$. Hence, when $c$ is a root of $f(x)$, then it
|
||||
---
|
||||
|
||||
|
||||
The division algorithm for the case of linear term, $(x-c)$, can be carried out by the [synthetic division](http://en.wikipedia.org/wiki/Synthetic_division) algorithm. This algorithm produces $q(x)$ and $r$, a.k.a $f(c)$. The Wikipedia page describes the algorithm well.
|
||||
The division algorithm for the case of a linear term, $(x-c)$, can be carried out by the [synthetic division](http://en.wikipedia.org/wiki/Synthetic_division) algorithm. This algorithm produces $q(x)$ and $r$, a.k.a $f(c)$. The Wikipedia page describes the algorithm well.
|
||||
|
||||
|
||||
The following is an example where $f(x) = x^4 + 2x^2 + 5$ and $g(x) = x-2$:
|
||||
@@ -101,7 +107,7 @@ The following is an example where $f(x) = x^4 + 2x^2 + 5$ and $g(x) = x-2$:
|
||||
1 2 6 12 29
|
||||
```
|
||||
|
||||
The polynomial $f(x)$ is coded in terms of its coefficients ($a_n$, $a_{n-1}$, $\dots$, $a_1$, $a_0$) and is written on the top row. The algorithm then proceeds from left to right. The number just produced on the bottom row is multiplied by $c$ and placed under the coefficient of $f(x)$. Then values are then added to produce the next number. The sequence produced above is `1 2 6 12 29`. The last value (`29`) is $r=f(c)$, the others encode the coefficients of `q(x)`, which for this problem is $q(x)=x^3 + 2x^2 + 6x + 12$. That is, we have written:
|
||||
The polynomial $f(x)$ is coded in terms of its coefficients ($a_n$, $a_{n-1}$, $\dots$, $a_1$, $a_0$) and is written on the top row. The algorithm then proceeds from left to right. The number just produced on the bottom row is multiplied by $c$ and placed under the coefficient of $f(x)$. Then values are then added to produce the next number. The sequence produced above is `1 2 6 12 29`. The last value (`29`) is $r=f(c)$, the others encode the coefficients of `q(x)`, which for this problem is $q(x)=1x^3 + 2x^2 + 6x + 12$. That is, we have written:
|
||||
|
||||
|
||||
$$
|
||||
@@ -171,48 +177,38 @@ This naive attempt to divide won't "just work" though:
|
||||
(x^4 + 2x^2 + 5) / (x-2)
|
||||
```
|
||||
|
||||
`SymPy` is fairly conservative in how it simplifies answers, and, as written, there is no compelling reason to change the expressions, though in our example we want it done.
|
||||
`SymPy` is fairly conservative in how it simplifies answers, and, as written, there is no compelling reason to change the expression, though in this example we want it done.
|
||||
|
||||
|
||||
For this task, `divrem` is available:
|
||||
For this task, `divrem` is available:^[
|
||||
For those who have worked with SymPy within Python, `divrem` is the `div` method renamed, as `Julia`'s `div` method has the generic meaning of returning the quotient.]
|
||||
|
||||
|
||||
```{julia}
|
||||
quotient, remainder = divrem(x^4 + 2x^2 + 5, x - 2)
|
||||
```
|
||||
|
||||
The answer is a tuple containing the quotient and remainder. The quotient itself could be found with `div` or `÷` and the remainder with `rem`.
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
For those who have worked with SymPy within Python, `divrem` is the `div` method renamed, as `Julia`'s `div` method has the generic meaning of returning the quotient.
|
||||
|
||||
:::
|
||||
|
||||
As well, the `apart` function could be used for this task. This function computes the [partial fraction](http://en.wikipedia.org/wiki/Partial_fraction_decomposition) decomposition of a ratio of polynomial functions.
|
||||
|
||||
|
||||
```{julia}
|
||||
apart((x^4 + 2x^2 + 5) / (x-2))
|
||||
```
|
||||
|
||||
The function `together` would combine such terms, as an "inverse" to `apart`. This isn't so much of interest at the moment, but will be when techniques of integration are looked at.
|
||||
The answer is a tuple containing the quotient and remainder. The quotient itself could be found with `div` or `÷` and the remainder with `rem`.^[There is also the `apart` function that could be used for this task. This function computes the [partial fraction](http://en.wikipedia.org/wiki/Partial_fraction_decomposition) decomposition of a ratio of polynomial functions. The function `together` would combine such terms, as an "inverse" to `apart`. This isn't so much of interest at the moment, but will be when techniques of integration are looked at.]
|
||||
|
||||
|
||||
### The rational root theorem
|
||||
|
||||
|
||||
Factoring polynomials to find roots is a task that most all readers here will recognize, and, perhaps, remember not so fondly. One helpful trick to find possible roots *by hand* is the [rational root theorem](http://en.wikipedia.org/wiki/Rational_root_theorem): if a polynomial has integer coefficients with $a_0 \neq 0$, then any rational root, $p/q$, must have $p$ dividing the constant $a_0$ and $q$ dividing the leading term $a_n$.
|
||||
Factoring polynomials to find roots is a task that most all readers here will recognize, and, perhaps, remember not so fondly. One helpful trick to find possible roots *by hand* is the rational-root theorem.
|
||||
|
||||
::: {.theorem title="The rational root theorem"}
|
||||
The [rational root theorem](http://en.wikipedia.org/wiki/Rational_root_theorem) states that
|
||||
if a polynomial has integer coefficients with $a_0 \neq 0$, then any rational root, $p/q$, must have $p$ dividing the constant $a_0$ and $q$ dividing the leading term $a_n$.
|
||||
:::
|
||||
|
||||
|
||||
To glimpse why, suppose we have a polynomial with a rational root and integer coefficients. With this in mind, a polynomial with identical roots may be written as $(qx -p)(a_{n-1}x^{n-1}+\cdots a_1 x + a_0)$, where each coefficient is an integer. Multiplying through, we get that the polynomial is $qa_{n-1}x^n + \cdots + pa_0$. So $q$ is a factor of the leading coefficient and $p$ is a factor of the constant.
|
||||
To glimpse why this is true, suppose we have a polynomial with a rational root and integer coefficients. With this in mind, a polynomial with identical roots may be written as $(qx -p)(a_{n-1}x^{n-1}+\cdots a_1 x + a_0)$, where each coefficient is an integer. Multiplying through, we get that the polynomial is $qa_{n-1}x^n + \cdots + pa_0$. So $q$ is a factor of the leading coefficient and $p$ is a factor of the constant.
|
||||
|
||||
|
||||
An immediate consequence is that if the polynomial with integer coefficients is monic, then any rational root must be an integer.
|
||||
|
||||
|
||||
This gives a finite - though possibly large - set of values that can be checked to exhaust the possibility of a rational root. By hand this process can be tedious, though may be speeded up using synthetic division. This task is one of the mainstays of high school algebra where problems are chosen judiciously to avoid too many possibilities.
|
||||
This gives a finite---though possibly large---set of values that can be checked to exhaust the possibility of a rational root. By hand this process can be tedious, though may be speeded up using synthetic division. This task is one of the mainstays of high school algebra where problems are chosen judiciously to avoid too many possibilities.
|
||||
|
||||
|
||||
However, one of the great triumphs of computer algebra is the ability to factor polynomials with integer (or rational) coefficients over the rational numbers. This is typically done by first factoring over modular numbers (akin to those on a clock face) and has nothing to do with the rational root test.
|
||||
@@ -221,7 +217,7 @@ However, one of the great triumphs of computer algebra is the ability to factor
|
||||
`SymPy` can quickly find such a factorization, even for quite large polynomials with rational or integer coefficients.
|
||||
|
||||
|
||||
For example, factoring $p = 2x^4 + x^3 -19x^2 -9x +9$. This has *possible* rational roots of plus or minus $1$ or $2$ divided by $1$, $3$, or $9$ - $12$ possible answers for this modest question. By hand that can be a bit of work, but `factor` does it without fuss:
|
||||
For example, factoring $p = 2x^4 + x^3 -19x^2 -9x +9$. This has *possible* rational roots of plus or minus $1$ or $2$ divided by $1$, $3$, or $9$---twelve possible answers for this modest question. By hand that can be a bit of work, but `factor` does it without fuss:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -235,14 +231,12 @@ factor(p)
|
||||
|
||||
There is a basic fact about the roots of a polynomial of degree $n$. Before formally stating it, we consider the earlier observation that a polynomial of degree $n$ for large values of $x$ has a graph that looks like the leading term. However, except at $0$, monomials do not cross the $x$ axis, the roots must be the result of the interaction of lower order terms. Intuitively, since each term can contribute only one basic shape up or down, there can not be arbitrarily many roots. In fact, a consequence of the [Fundamental Theorem of Algebra](http://en.wikipedia.org/wiki/Fundamental_theorem_of_algebra) (Gauss) is:
|
||||
|
||||
|
||||
> A polynomial of degree $n$ with real or complex coefficients has at most $n$ real roots.
|
||||
|
||||
|
||||
::: {.theorem title="The fundamental theorem of algebra"}
|
||||
A polynomial of degree $n$ with real or complex coefficients has at most $n$ real roots.
|
||||
:::
|
||||
|
||||
This statement can be proved with the factor theorem and the division algorithm.
|
||||
|
||||
|
||||
In fact the fundamental theorem states that there are exactly $n$ roots, though, in general, one must consider multiple roots and possible complex roots to get all $n$. (Consider $x^2$ to see why multiplicity must be accounted for and $x^2 + 1$ to see why complex values may be necessary.)
|
||||
|
||||
|
||||
@@ -265,16 +259,21 @@ $$
|
||||
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}.
|
||||
$$
|
||||
|
||||
The discriminant is defined as $b^2 - 4ac$. When this is negative, the square root requires the concept of complex numbers to be defined, and the formula shows the two complex roots are conjugates. When the discriminant is $0$, then the root has multiplicity two, e.g., the polynomial will factor as $a_2(x-r)^2$. Finally, when the discriminant is positive, there will be two distinct, real roots. This figure shows the $3$ cases, that are illustrated by $x^2 -1$, $x^2$ and $x^2 + 1$:
|
||||
The discriminant is defined as $b^2 - 4ac$. When this is negative, the square root requires the concept of complex numbers to be defined, and the formula shows the two complex roots are conjugates. When the discriminant is $0$, then the root has multiplicity two, e.g., the polynomial will factor as $a_2(x-r)^2$. Finally, when the discriminant is positive, there will be two distinct, real roots. @fig-three-cases-of-quadratic shows the $3$ cases illustrated by $x^2 -1$, $x^2$ and $x^2 + 1$.
|
||||
|
||||
|
||||
::: {#fig-three-cases-of-quadratic}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
plot(x^2 - 1, -2, 2, legend=false) # two roots
|
||||
plot!(x^2, -2, 2) # one (double) root
|
||||
plot!(x^2 + 1, -2, 2) # no real root
|
||||
plot!(zero, -2, 2)
|
||||
```
|
||||
|
||||
Three simple quadratic functions, one with two real roots; one with one; one with none
|
||||
:::
|
||||
|
||||
There are similar formulas for the [cubic](http://en.wikipedia.org/wiki/Cubic_function#General_formula_for_roots) and [quartic](http://en.wikipedia.org/wiki/Quartic_function#General_formula_for_roots) cases. (The [cubic formula](http://arxiv.org/pdf/math/0005026v1.pdf) was known to Cardano in $1545$, though through Tartagli, and the quartic was solved by Ferrari, Cardano's roommate.)
|
||||
|
||||
|
||||
@@ -283,8 +282,9 @@ In general, there is no such formula using radicals for $5$th degree polynomials
|
||||
|
||||
The `factor` function of `SymPy` only finds factors of polynomials with integer or rational coefficients corresponding to rational roots. There are alternatives.
|
||||
|
||||
### The solve and solveset functions
|
||||
|
||||
Finding roots with `SymPy` can also be done through its `solve` function, a function which also has a more general usage, as it can solve simple expressions or more than one expression. Here we illustrate that `solve` can easily handle quadratic expressions:
|
||||
Finding roots with `SymPy` can also be done through its `solve` function, a function which also has a more general usage, as it can solve simple expressions or more than one expression for unknown variables. Here we illustrate that `solve` can easily handle quadratic expressions:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -293,10 +293,9 @@ solve(x^2 + 2x - 3 ~ 0, x)
|
||||
|
||||
The answer is a vector of values that when substituted in for the free variable `x` produce $0.$
|
||||
|
||||
We use the `~` notation to define an equation to pass to `solve`. This convention is not necessary here, as `SymPy` will assume an expression passed to solve is an equation set to `0`, but is pedagogically useful. Equations do not have an equals sign, which is reserved for assignment. To solve a more complicated expression of the type $f(x) = g(x),$ one can solve $f(x) - g(x) = 0,$ use the `Eq` function, or use `f ~ g`.
|
||||
We use the `~` notation to define an equation to pass to `solve`.^[The `solve` function applied to a simple case need not have an equation specified with `~` and need not have a variable to solve for specified. However, we try to be explicit in each example so that exactly what is being asked is as clear as can be.] Equations can not be specified with an equals sign, which is reserved for assignment. To solve a more complicated expression of the type $f(x) = g(x),$ one can solve $f(x) - g(x) = 0$ or use `f ~ g`.
|
||||
|
||||
|
||||
When the expression to solve has more than one free variable, the variable to solve for should be explicitly stated with a second argument. (The specification above is unnecessary.) For example, here we show that `solve` is aware of the quadratic formula:
|
||||
For example, here we show that `solve` is aware of the quadratic formula:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -348,9 +347,9 @@ Third- and fourth-degree polynomials can be solved in general, with increasingly
|
||||
```{julia}
|
||||
#| hold: true
|
||||
@syms a[0:3]
|
||||
p = sum(a*x^(i-1) for (i,a) in enumerate(a))
|
||||
p = sum(aᵢ*x^(i-1) for (i,aᵢ) in enumerate(a))
|
||||
rts = solve(p ~ 0, x)
|
||||
rts[1] # there are three roots
|
||||
first(rts) # there are three roots
|
||||
```
|
||||
|
||||
Some fifth degree polynomials are solvable in terms of radicals, however, `solve` will not seem to have luck with this particular fifth degree polynomial:
|
||||
@@ -366,42 +365,39 @@ solve(x^5 - x + 1 ~ 0, x)
|
||||
### The `roots` function
|
||||
|
||||
|
||||
Related to `solve` is the specialized `roots` function for identifying roots, Unlike solve, it will identify multiplicities.
|
||||
|
||||
Related to `solve` is the specialized `roots` function for identifying roots of a polynomial, Unlike solve, it will identify multiplicities, returning a dictionary as its output.
|
||||
|
||||
For a polynomial with only one indeterminate the usage is straight forward:
|
||||
|
||||
|
||||
```{julia}
|
||||
roots((x-1)^2 * (x-2)^2) # solve doesn't identify multiplicities
|
||||
roots((x-1)^2 * (x-2)^3, x) # solve doesn't identify multiplicities
|
||||
```
|
||||
|
||||
For a polynomial with symbolic coefficients, the difference between the symbol and the coefficients must be identified. `SymPy` has a `Poly` type to do so. The following call illustrates:
|
||||
For a polynomial with symbolic coefficients, the difference between the symbol and the coefficients *must* be identified. This can be done with the `Poly` type of `SymPy`,^[The use of `Poly` would look like `q1 = sympy.Poly(p, x); roots(q1)`. The `Poly` function is not exported, so is called by qualifying it with the `sympy` object. This is common when using `SymPy`, as only a small handful of the many functions available are turned into `Julia` functions, the rest are used as would be done in Python. (This is similar, but different than qualifying by a `Julia` module when there are two conflicting names. An example will be the use of the name `roots` in both `SymPy` and `Polynomials` to refer to a function that finds the roots of a polynomial. If both functions were loaded, then the last line in the above example would need to be `SymPy.roots(q)` (note the capitalization.)] or by specifying the variable, as above (which is optional when there is only one free variable):
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
@syms a b c
|
||||
@syms a b c x
|
||||
p = a*x^2 + b*x + c
|
||||
q1 = sympy.Poly(p, x) # identify `x` as indeterminate; alternatively p.as_poly(x)
|
||||
roots(q1)
|
||||
roots(p, x)
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
The sympy `Poly` function must be found within the underlying `sympy` module, a Python object, hence is qualified as `sympy.Poly`. This is common when using `SymPy`, as only a small handful of the many functions available are turned into `Julia` functions, the rest are used as would be done in Python. (This is similar, but different than qualifying by a `Julia` module when there are two conflicting names. An example will be the use of the name `roots` in both `SymPy` and `Polynomials` to refer to a function that finds the roots of a polynomial. If both functions were loaded, then the last line in the above example would need to be `SymPy.roots(q)` (note the capitalization.)
|
||||
|
||||
:::
|
||||
|
||||
### Numerically finding roots
|
||||
|
||||
|
||||
The `solve` function can be used to get numeric approximations to the roots. It is as easy as calling `N` on the solutions:
|
||||
The output of this `solve` call finds $1$ roots, but it is hidden behind a `CRootOf` wrapper:
|
||||
|
||||
```{julia}
|
||||
@syms x::real
|
||||
rts = solve(x^5 - x + 1 ~ 0, x)
|
||||
```
|
||||
|
||||
Such values can be identified with numeric approximations. It is as easy as calling `N` on the solutions, which are held in a vector:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
rts = solve(x^5 - x + 1 ~ 0, x)
|
||||
N.(rts) # note the `.(` to broadcast over all values in rts
|
||||
```
|
||||
|
||||
@@ -416,13 +412,15 @@ ex = x^7 -3x^6 + 2x^5 -1x^3 + 2x^2 + 1x^1 - 2
|
||||
solve(ex ~ 0, x)
|
||||
```
|
||||
|
||||
This finds two of the seven possible roots, the remainder of the real roots can be found numerically:
|
||||
This identifies exactly two of the up-to-seven possible roots, the remainding real root can be found numerically:
|
||||
|
||||
|
||||
```{julia}
|
||||
N.(solve(ex ~ 0, x))
|
||||
```
|
||||
|
||||
This approach can also be used to find complex answers.
|
||||
|
||||
### The solveset function
|
||||
|
||||
|
||||
@@ -441,14 +439,13 @@ The `p_rts` object, a `Set`, does not allow indexed access to its elements. For
|
||||
collect(p_rts)
|
||||
```
|
||||
|
||||
To get the numeric approximation, we can broadcast:
|
||||
To get the numeric approximation, we can broadcast:^[There is no need to call `collect` before broadcasting, as broadcasting over a set falls back to broadcasting over the iteration of the set and in this case returns a vector.]
|
||||
|
||||
|
||||
```{julia}
|
||||
N.(solveset(p ~ 0, x))
|
||||
```
|
||||
|
||||
(There is no need to call `collect`---though you can---as broadcasting over a set falls back to broadcasting over the iteration of the set and in this case returns a vector.)
|
||||
|
||||
## Do numeric methods matter when you can just graph?
|
||||
|
||||
@@ -456,46 +453,56 @@ N.(solveset(p ~ 0, x))
|
||||
It may seem that certain practices related to roots of polynomials are unnecessary as we could just graph the equation and look for the roots. This feeling is perhaps motivated by the examples given in textbooks to be worked by hand, which necessarily focus on smallish solutions. But, in general, without some sense of where the roots are, an informative graph itself can be hard to produce. That is, technology doesn't displace thinking---it only supplements it.
|
||||
|
||||
|
||||
For another example, consider the polynomial $(x-20)^5 - (x-20) + 1$. In this form we might think the roots are near $20$. However, were we presented with this polynomial in expanded form: $x^5 - 100x^4 + 4000x^3 - 80000x^2 + 799999x - 3199979$, we might be tempted to just graph it to find roots. A naive graph might be to plot over $[-10, 10]$:
|
||||
|
||||
For another example, consider the polynomial $(x-20)^5 - (x-20) + 1$. In this form we might think the roots are near $20$. However, were we presented with this polynomial in expanded form: $x^5 - 100x^4 + 4000x^3 - 80000x^2 + 799999x - 3199979$, we might be tempted to just graph it to find roots.^[Or ask AI for which google gemini gave a wrong answer the first time.] A naive graph might be to plot over $[-10, 10]$:
|
||||
|
||||
::: {#fig-plot-expanded-polynomial-naively}
|
||||
```{julia}
|
||||
p = x^5 - 100x^4 + 4000x^3 - 80000x^2 + 799999x - 3199979
|
||||
plot(p, -10, 10)
|
||||
```
|
||||
Simple plot of expanded polynomial over $[-10,10]$ domain showing this is a poor choice of viewing window
|
||||
:::
|
||||
|
||||
This seems to indicate a root near $10$. But look at the scale of the $y$ axis. The value at $-10$ is around $-25,000,000$ so it is really hard to tell if $f$ is near $0$ when $x=10$, as the range is too large.
|
||||
@fig-plot-expanded-polynomial-naively maybe seems to indicate a root near $10$. But look at the scale of the $y$ axis. The value at $-10$ is around $-25,000,000$ so it is really hard to tell if $f$ is near $0$ when $x=10$, as the range is too large.
|
||||
|
||||
|
||||
A graph over $[10,20]$ is still unclear:
|
||||
|
||||
A graph over $[10,20]$ is still unclear, as seen in @fig-plot-expanded-polynomial-a-bit-less-naively.
|
||||
|
||||
::: {#fig-plot-expanded-polynomial-a-bit-less-naively}
|
||||
```{julia}
|
||||
plot(p, 10,20)
|
||||
```
|
||||
|
||||
Plot of expanded polynomial over $[10, 20]$. Even though there is a zero, it can't be identified from this graph.
|
||||
:::
|
||||
|
||||
We see that what looked like a zero near $10$, was actually a number around $-100,000$.
|
||||
|
||||
|
||||
Continuing, a plot over $[15, 20]$ still isn't that useful. It isn't until we get close to $18$ that the large values of the polynomial allow a clear vision of the values near $0$. That being said, plotting anything bigger than $22$ quickly makes the large values hide those near $0$, and might make us think where the function dips back down there is a second or third zero, when only $1$ is the case. (We know that, as this is the same $x^5 - x + 1$ shifted to the right by $20$ units.)
|
||||
|
||||
|
||||
::: {#fig-plot-expanded-polynomial-a-good-frame}
|
||||
```{julia}
|
||||
plot(p, 18, 22)
|
||||
```
|
||||
|
||||
Plot of expanded polynomial using a good domain
|
||||
:::
|
||||
|
||||
Not that it can't be done, but graphically solving for a root here can require some judicious choice of viewing window. Even worse is the case where something might graphically look like a root, but in fact not be a root. Something like $(x-100)^2 + 0.1$ will demonstrate.
|
||||
|
||||
|
||||
For another example, the following polynomial when plotted over $[-5,7]$ appears to have two real roots:
|
||||
|
||||
For another example, the following polynomial when plotted over $[-5,7]$ appears to have two real roots in @fig-mignotte-poly-2-or-3-roots.
|
||||
|
||||
::: {#fig-mignotte-poly-2-or-3-roots}
|
||||
```{julia}
|
||||
h = x^7 - 16129x^2 + 254x - 1
|
||||
plot(h, -5, 7)
|
||||
```
|
||||
Plot of polynomial that appears to have two real roots
|
||||
:::
|
||||
|
||||
in fact there are three, two are *very* close together:
|
||||
In fact there are three roots of this polynomial, two are *very* close together:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -522,9 +529,17 @@ A polynomial with real coefficients may or may not have real roots. The followin
|
||||
|
||||
The study of polynomial roots is an old one. In $1637$ Descartes published a *simple* method to determine an upper bound on the number of *positive* real roots of a polynomial.
|
||||
|
||||
|
||||
> [Descartes' rule of signs](http://en.wikipedia.org/wiki/Descartes%27_rule_of_signs): if $p=a_n x^n + a_{n-1}x^{n-1} + \cdots + a_1x + a_0$ then the number of positive real roots is either equal to the number of sign differences between consecutive nonzero coefficients, or is less than it by an even number. Repeated roots are counted separately.
|
||||
|
||||
::: {.theorem title="Descartes' rule of signs"}
|
||||
[Descartes' rule of signs](http://en.wikipedia.org/wiki/Descartes%27_rule_of_signs) states that if
|
||||
$$
|
||||
p=a_n x^n + a_{n-1}x^{n-1} + \cdots + a_1x + a_0, \quad a_n \neq 0
|
||||
$$
|
||||
then the number of positive real roots is either equal to the number
|
||||
of sign differences between consecutive coefficients
|
||||
(omitting zero coefficients),
|
||||
or is less than this count by an even number.
|
||||
Repeated roots are counted separately.
|
||||
:::
|
||||
|
||||
|
||||
One method of proof (sketched at the end of this section) first shows that in synthetic division by $(x-c)$ with $c > 0$, we must have that any sign change in $q$ is related to a sign change in $p$ and there must be at least one more in $p$. This is then used to show that there can be only as many positive roots as sign changes. That the difference comes in pairs is related to complex roots of real polynomials always coming in pairs.
|
||||
@@ -533,13 +548,13 @@ One method of proof (sketched at the end of this section) first shows that in sy
|
||||
An immediate consequence, is that a polynomial whose coefficients are all non-negative will have no positive real roots.
|
||||
|
||||
|
||||
Applying this to the polynomial $x^5 -x + 1$ we get That the coefficients have signs: `+ 0 0 0 - +` which collapses to the sign pattern `+`, `-`, `+`. This pattern has two changes of sign. The number of *positive* real roots is either $2$ or $0$. In fact there are $0$ for this case.
|
||||
Applying this to the polynomial $x^5 -x + 1$ we get that the coefficients have signs: `+ 0 0 0 - +` which collapses to the sign pattern `+ - +`. This pattern has two changes of sign. The number of *positive* real roots is either $2$ or $0$. In fact there are $0$ for this case.
|
||||
|
||||
|
||||
What about negative roots? Clearly, any negative root of $p$ is a positive root of $q(x) = p(-x)$, as the graph of $q$ is just that of $p$ flipped through the $y$ axis. But the coefficients of $q$ are the same as $p$, except for the odd-indexed coefficients ($a_1, a_3, \dots$) have a changed sign. Continuing with our example, for $q(x) = -x^5 + x + 1$ we get the new sign pattern `-`, `+`, `+` which yields one sign change. That is, there *must* be a negative real root, and indeed there is, $x \approx -1.1673$.
|
||||
What about negative roots? Clearly, any negative root of $p$ is a positive root of $q(x) = p(-x)$, as the graph of $q$ is just that of $p$ flipped through the $y$ axis. But the coefficients of $q$ are the same as $p$, except for the odd-indexed coefficients ($a_1, a_3, \dots$) have a changed sign. Continuing with our example, for $q(x) = -x^5 + x + 1$ we get the new sign pattern `- + +` which yields one sign change. That is, there *must* be a negative real root, and indeed there is, $x \approx -1.1673$.
|
||||
|
||||
|
||||
With this knowledge, we could have known that in an earlier example the graph of `p = x^7 - 16129x^2 + 254x - 1` – which indicated two positive real roots – was misleading, as there must be $1$ or $3$ by a count of the sign changes.
|
||||
With this knowledge, we could have known that in an earlier example the graph of `p = x^7 - 16129x^2 + 254x - 1`---which suggested two positive real roots---was misleading, as there must be $1$ or $3$ by a count of the sign changes.
|
||||
|
||||
|
||||
For another example, if we looked at $f(x) = x^5 - 100x^4 + 4000x^3 - 80000x^2 + 799999x - 3199979$ again, we see that there could be $1$, $3$, or $5$ *positive* roots. However, changing the signs of the odd powers leaves all "-" signs, so there are $0$ negative roots. From the graph, we saw just $1$ real root, not $3$ or $5$. We can verify numerically with:
|
||||
@@ -553,7 +568,7 @@ N.(solve(j ~ 0, x))
|
||||
### Cauchy's bound on the magnitude of the real roots.
|
||||
|
||||
|
||||
Descartes' rule gives a bound on how many real roots there may be. Cauchy provided a bound on how large they can be. Assume our polynomial is monic (if not, divide by $a_n$ to make it so, as this won't effect the roots). Then any real root is no larger in absolute value than $h = 1 + |a_0| + |a_1| + |a_2| + \cdots + |a_{n-1}|$, (this is expressed in different ways.)
|
||||
Descartes' rule gives a bound on how many real roots there may be. Cauchy provided a bound on how large any real roots can be. Assume our polynomial is monic (if not, divide by $a_n$ to make it so, as this won't effect the roots). Then any real root is no larger in absolute value than $h = 1 + |a_0| + |a_1| + |a_2| + \cdots + |a_{n-1}|$, (this is expressed in different ways.)
|
||||
|
||||
|
||||
To see precisely [why](https://captainblack.wordpress.com/2009/03/08/cauchys-upper-bound-for-the-roots-of-a-polynomial/) this bound works, suppose $x$ is a root with $|x| > 1$ and let $h$ be the bound. Then since $x$ is a root, we can solve $a_0 + a_1x + \cdots + 1 \cdot x^n = 0$ for $x^n$ as:
|
||||
@@ -567,20 +582,23 @@ Which after taking absolute values of both sides, yields by the triangle inequal
|
||||
|
||||
|
||||
$$
|
||||
|x^n| \leq |a_0| + |a_1||x| + |a_2||x^2| + \cdots |a_{n-1}| |x^{n-1}| \leq (h-1) (1 + |x| + |x^2| + \cdots |x^{n-1}|).
|
||||
\begin{align*}
|
||||
\lvert x^n\rvert &\leq \lvert a_0\rvert + \lvert a_1\rvert\lvert x\rvert + \lvert a_2\rvert\lvert x^2\rvert + \cdots \lvert a_{n-1}\rvert \lvert x^{n-1}\rvert \\
|
||||
& \leq (h-1) (1 + \lvert x\rvert + \lvert x^2\rvert + \cdots \lvert x^{n-1}\rvert).\\
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
The last sum can be computed using a formula for geometric sums, $(|x^n| - 1)/(|x|-1)$. Rearranging, gives the inequality:
|
||||
The last sum can be computed using a formula for geometric sums, $(\lvert x^n\rvert - 1)/(\lvert x\rvert-1)$. Rearranging, gives the inequality:
|
||||
|
||||
|
||||
$$
|
||||
|x| - 1 \leq (h-1) \cdot (1 - \frac{1}{|x^n|} ) \leq (h-1)
|
||||
\lvert x\rvert - 1 \leq (h-1) \cdot (1 - \frac{1}{\lvert x^n\rvert} ) \leq (h-1)
|
||||
$$
|
||||
|
||||
from which it follows that $|x| \leq h$, as desired.
|
||||
from which it follows that $\lvert x\rvert \leq h$, as desired.
|
||||
|
||||
|
||||
For our polynomial $x^5 -x + 1$ we have the sum above is $3$. The lone real root is approximately $-1.1673$ which satisfies $|-1.1673| \leq 3$.
|
||||
For our polynomial $x^5 -x + 1$ we have the sum above is $3$. The lone real root is approximately $-1.1673$ which satisfies $\lvert -1.1673\rvert \leq 3$.
|
||||
|
||||
|
||||
|
||||
@@ -605,8 +623,8 @@ choices = [
|
||||
"``6``",
|
||||
"``0``"
|
||||
]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -623,8 +641,8 @@ choices = [
|
||||
"``x^2 - 2x + 2``",
|
||||
"``2``"
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -644,8 +662,8 @@ choices = [
|
||||
"``x^3 + x^2 - 1``",
|
||||
"``-2x + 2``"
|
||||
]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -676,8 +694,8 @@ choices = [
|
||||
"``x^5 + 2x^4 + 4x^3 + 8x^2 + 15x + 31``",
|
||||
"``x^4 +2x^3 + 4x^2 + 8x + 15``",
|
||||
"``31``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
What is $q(x)$?
|
||||
@@ -692,8 +710,8 @@ choices = [
|
||||
"``x^5 + 2x^4 + 4x^3 + 8x^2 + 15x + 31``",
|
||||
"``x^4 +2x^3 + 4x^2 + 8x + 15``",
|
||||
"``31``"]
|
||||
answ = 4
|
||||
radioq(choices, answ)
|
||||
answer = 4
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
What is $r$?
|
||||
@@ -708,8 +726,8 @@ choices = [
|
||||
"``x^5 + 2x^4 + 4x^3 + 8x^2 + 15x + 31``",
|
||||
"``x^4 +2x^3 + 4x^2 + 8x + 15``",
|
||||
"``31``"]
|
||||
answ = 5
|
||||
radioq(choices, answ)
|
||||
answer = 5
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -728,8 +746,8 @@ choices = [
|
||||
L" $2$ and $3$",
|
||||
L" $(x-2)$ and $(x-3)$",
|
||||
L" $(x+2)$ and $(x+3)$"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -785,8 +803,8 @@ q"[-0.434235, -0.434235, 0.188049, 0.188049, 0.578696, 4.91368]",
|
||||
q"[-0.434235, -0.434235, 0.188049, 0.188049]",
|
||||
q"[0.578696, 4.91368]",
|
||||
q"[-0.434235+0.613836im, -0.434235-0.613836im]"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -863,8 +881,8 @@ Let $f(x) = x^5 - 4x^4 + x^3 - 2x^2 + x$. What does Cauchy's bound say is the la
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
answ = 1 + 4 + 1 + 2 + 1
|
||||
numericq(answ)
|
||||
answer = 1 + 4 + 1 + 2 + 1
|
||||
numericq(answer)
|
||||
```
|
||||
|
||||
What is the largest magnitude of a real root?
|
||||
@@ -875,14 +893,14 @@ What is the largest magnitude of a real root?
|
||||
#| echo: false
|
||||
f(x) = x^5 - 4x^4 + x^3 - 2x^2 + x
|
||||
rts = find_zeros(f, -5..5)
|
||||
answ = maximum(abs.(rts))
|
||||
numericq(answ)
|
||||
answer = maximum(abs.(rts))
|
||||
numericq(answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
|
||||
|
||||
As $1 + 2 + 3 + 4$ is $10$, Cauchy's bound says that the magnitude of the largest real root of $x^3 - ax^2 + bx - c$ is $10$ where $a,b,c$ is one of $2,3,4$. By considering all 6 such possible polynomials (such as $x^3 - 3x^2 + 2x - 4$) what is the largest magnitude or a root?
|
||||
As $1 + 2 + 3 + 4$ is $10$, Cauchy's bound says that the magnitude of the largest real root of $x^3 - ax^2 + bx - c$ is $10$ where $a,b,c$ is one of $2,3,4$. By considering all 6 such possible polynomials (such as $x^3 - 3x^2 + 2x - 4$) what is the largest magnitude of a root?
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -921,8 +939,8 @@ choices = [
|
||||
"``2x^2``",
|
||||
"``x``",
|
||||
"``2x``"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
* True or false, the $degree$ of $T_n(x)$ is $n$: (Look at the defining relation and reason this out).
|
||||
@@ -950,19 +968,22 @@ The Chebyshev polynomials have the property that in fact all $n$ roots are real,
|
||||
@syms x
|
||||
p = 16x^5 - 20x^3 + 5x
|
||||
rts = N.(solve(p))
|
||||
answ = maximum(norm.(rts))
|
||||
numericq(answ)
|
||||
answer = maximum(norm.(rts))
|
||||
numericq(answer)
|
||||
```
|
||||
|
||||
* Plotting `p` over the interval $[-2,2]$ does not help graphically identify the roots:
|
||||
|
||||
|
||||
::: {#fig-plot-polynomial-over-minus2-2-not-roots-identifiable}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
plot(16x^5 - 20x^3 + 5x, -2, 2)
|
||||
```
|
||||
Plot of $p = 16x^5 - 20x^3 + 5x$ over $[-2, 2]$
|
||||
:::
|
||||
|
||||
Does graphing over $[-1,1]$ show clearly the $5$ roots?
|
||||
|
||||
Does graphing $p$ over $[-1,1]$ show clearly the $5$ roots?
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -986,19 +1007,19 @@ Let `var(p)` be the number of sign changes and `pos(p)` the number of positive r
|
||||
First: For a monic $p$ if $p_0 < 0$ then `var(p)` is odd and if $p_0 > 0$ then `var(p)` is even.
|
||||
|
||||
|
||||
This is true for degree $n=1$ the two sign patterns under the assumption are `+-` ($p_0 < 0$) or `++` ($p_0 > 0$). If it is true for degree $n-1$, then the we can consider the sign pattern of such an $n$ degree polynomial having one of these patterns: `+...+-` or `+...--` (if $p_0 < 0$) or `+...++` or `+...-+` if ($p_0>0$). An induction step applied to all but the last sign for these four patterns leads to even, odd, even, odd as the number of sign changes. Incorporating the last sign leads to odd, odd, even, even as the number of sign changes.
|
||||
This is true for degree $n=1$ the two sign patterns under the assumption are `+-` ($p_0 < 0$) or `++` ($p_0 > 0$). If it is true for degree $n-1$, then the we can consider the sign pattern of such an $n$ degree polynomial having one of these patterns: `+...+(0?)-` or `+...-(0?)-` (if $p_0 < 0$) or `+...+(0?)+` or `+...-(0?)+` if ($p_0>0$). The presence of `(0?)` is to show there may be zero, one, or more zeros in that position. An induction step applied to all but the last sign for these four patterns (that is the polynomial after dropping the constant and any of the `0?` terms) leads to even, odd, even, odd as the number of sign changes. Incorporating the last sign leads to odd, odd, even, even as the number of sign changes.
|
||||
|
||||
|
||||
Second: For a monic $p$ if $p_0 < 0$ then `pos(p)` is *odd*, if $p_0 > 0$ then `pos(p)` is even.
|
||||
|
||||
|
||||
This is clearly true for **monic** degree $1$ polynomials: if $c$ is positive $p = x - c$ has one real root (an odd number) and $p = x + c$ has $0$ real roots (an even number). Now, suppose $p$ has degree $n$ and is monic. Then as $x$ goes to $\infty$, it must be $p$ goes to $\infty$.
|
||||
This is clearly true for *monic* degree $1$ polynomials: if $c$ is positive $p = x - c$ has one positive, real root (an odd number) and $p = x + c$ has zero *positive*, real roots (an even number). Now, suppose $p$ has degree $n$ and is monic. Then as $x$ goes to $\infty$, it must be $p$ goes to $\infty$.
|
||||
|
||||
|
||||
If $p_0 < 0$ then there must be a positive real root, say $r$, (Bolzano's intermediate value theorem). Dividing $p$ by $(x-r)$ to produce $q$ requires $q_0$ to be *positive* and of lower degree. By *induction* $q$ will have an even number of roots. Add in the root $r$ to see that $p$ will have an **odd** number of roots.
|
||||
If $p_0 < 0$ then there must be a positive real root, say $r$, (Bolzano's intermediate value theorem). Dividing $p$ by $(x-r)$ to produce $q$ requires $q_0$ to be *positive* and of lower degree. By *induction* $q$ will have an even number of roots. Add in the root $r$ to see that $p$ will have an *odd* number of roots.
|
||||
|
||||
|
||||
Now consider the case $p_0 > 0$. There are two possibilities either `pos(p)` is zero or positive. If `pos(p)` is $0$ then there are an even number of roots. If `pos(p)` is positive, then call $r$ one of the real positive roots. Again divide by $x-r$ to produce $p = (x-r) \cdot q$. Then $q_0$ must be *negative* for $p_0$ to be positive. By *induction* $q$ must have an odd number of roots, meaning $p$ must have an even numbers.
|
||||
Now consider the case $p_0 > 0$. There are two possibilities either `pos(p)` is zero or positive. If `pos(p)` is $0$ then there are an even number of roots. If `pos(p)` is positive, then call $r$ one of the real positive roots. Again divide by $x-r$ to produce $p = (x-r) \cdot q$. Then $q_0$ must be *negative* for $p_0$ to be positive. By *induction* $q$ must have an odd number of roots, meaning $p$ must have an even number.
|
||||
|
||||
|
||||
So there is parity between `var(p)` and `pos(p)`: if $p$ is monic and $p_0 < 0$ then both `var(p)` and `pos(p)` are both odd; and if $p_0 > 0$ both `var(p)` and `pos(p)` are both even.
|
||||
@@ -1017,7 +1038,7 @@ As $p = (x-c)q$ we must have the leading term is $p_nx^n = x \cdot q_{n-1} x^{n-
|
||||
+ - - - + - + + 0
|
||||
```
|
||||
|
||||
But actually, we can fill in more, as the second row is formed by multiplying a positive $c$:
|
||||
The pattern for `q` only has a leading `+` and ending `0`, the pattern shown is just an arbitrary pattern to make a general point. From any pattern for `q`, we can we can fill in more. As the second row is formed by multiplying a positive $c$ the sign in the second row is the same as the sign in the bottom row shifted over 1 to the left.
|
||||
|
||||
|
||||
```{verbatim}
|
||||
@@ -1030,6 +1051,15 @@ But actually, we can fill in more, as the second row is formed by multiplying a
|
||||
What's more, using the fact that to get `0` the two summands must differ in sign and to have a `?` plus `+` yield a `-`, the `?` must be `-` (and reverse), the following must be the case for the signs of `p`:
|
||||
|
||||
|
||||
```{verbatim}
|
||||
+ - ? ? ? ? ? ? -
|
||||
+ + - - - + - + +
|
||||
-----------------
|
||||
+ - - - + - + + 0
|
||||
```
|
||||
|
||||
Going further, to get a `+` in the bottom row with a `-` in the middle row, the top row must be `+` and similarly if `-` and `+`. Filling these in we have this pattern determined.
|
||||
|
||||
```{verbatim}
|
||||
+ - ? ? + - + ? -
|
||||
+ + - - - + - + +
|
||||
|
||||
@@ -18,13 +18,13 @@ import SymPy # imported only: some functions, e.g. degree, need qualification
|
||||
---
|
||||
|
||||
|
||||
While `SymPy` can be used to represent polynomials, there are also native `Julia` packages available for this and related tasks. These packages include `Polynomials`, `MultivariatePolynomials`, and `AbstractAlgebra`, among many others. (A search on [juliahub.com](https://juliahub.com) found almost $100$ packages matching "polynomial".) We will look at the `Polynomials` package in the following, as it is straightforward to use and provides the features we are looking at for *univariate* polynomials.
|
||||
While `SymPy` can be used to represent polynomials, there are also native `Julia` packages available for this and related tasks. These packages include `Polynomials`, `MultivariatePolynomials`, and `AbstractAlgebra`, among many others.^[A search on [juliahub.com](https://juliahub.com) found over $50$ packages matching "polynomial".] In this section, we look at the `Polynomials` package, as it is straightforward to use and provides the features we are looking at for *univariate* polynomials.
|
||||
|
||||
|
||||
## Construction
|
||||
|
||||
|
||||
The polynomial expression $p = a_0 + a_1\cdot x + a_2\cdot x^2 + \cdots + a_n\cdot x^n$ can be viewed mathematically as a vector of numbers with respect to some "basis", which for standard polynomials, as above, is just the set of monomials, $1, x, x^2, \dots, x^n$. With this viewpoint, the polynomial $p$ can be identified with the vector `[a0, a1, a2, ..., an]`. The `Polynomials` package provides a wrapper for such an identification through the `Polynomial` constructor. We have previously loaded this add-on package.
|
||||
The polynomial expression $p = a_0 + a_1\cdot x + a_2\cdot x^2 + \cdots + a_n\cdot x^n$ can be viewed mathematically as a collection of numbers ($a_0$, $a_1$, $\dots$, $a_n$) with respect to some "basis", which for standard polynomials, as above, is just the set of monomials, $1, x, x^2, \dots, x^n$. With this viewpoint, the polynomial $p$ can be identified with the vector `[a0, a1, a2, ..., an]`. The `Polynomials` package provides a wrapper for such an identification through the `Polynomial` constructor. We have previously loaded this add-on package.
|
||||
|
||||
|
||||
To illustrate, the polynomial $p = 3 + 4x + 5x^2$ is constructed with
|
||||
@@ -34,7 +34,7 @@ To illustrate, the polynomial $p = 3 + 4x + 5x^2$ is constructed with
|
||||
p = Polynomial([3,4,5])
|
||||
```
|
||||
|
||||
where the vector `[3,4,5]` represents the coefficients. The polynomial $q = 3 + 5x^2 + 7x^4$ has some coefficients that are $0$, these too must be indicated on construction, so we would have:
|
||||
where the vector `[3,4,5]` represents the coefficients. The polynomial $q = 3 + 5x^2 + 7x^4$ differs from $p$ in that some coefficients that are $0$. These must be indicated on construction. To represent $q$, we have:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -87,7 +87,7 @@ x = variable(p)
|
||||
```
|
||||
|
||||
This variable is a `Polynomial` object that prints as `x`.
|
||||
These variables can be manipulated as any polynomial. We can then construct polynomials through expressions like:
|
||||
These variables can be algebraically manipulated as any polynomial. For example, wWe could construct polynomials through expressions like:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -150,19 +150,23 @@ r + s
|
||||
## Graphs
|
||||
|
||||
|
||||
Polynomial objects have a plot recipe defined – plotting from the `Plots` package should be as easy as calling `plot`:
|
||||
|
||||
Polynomial objects have a plot recipe defined---plotting from the `Plots` package should be as easy as calling `plot` passing the polynomial as the first argument:
|
||||
|
||||
::: {#fig-plot-polynomial-using-basic-recipe}
|
||||
```{julia}
|
||||
plot(r, legend=false) # suppress the legend
|
||||
plot(r; legend=false) # suppress the legend
|
||||
```
|
||||
Plot of polynomial `r` using the basic plot recipe
|
||||
:::
|
||||
|
||||
The choice of domain is heuristically identified; it can be manually adjusted, as with:
|
||||
|
||||
|
||||
::: {#fig-plot-polynomial-using-specified-interval-1point5-to-2point5}
|
||||
```{julia}
|
||||
plot(r, 1.5, 2.5; legend=false)
|
||||
```
|
||||
Plot of polynomial `r` over the interval $[3/2, 5/2]$
|
||||
:::
|
||||
|
||||
## Roots
|
||||
|
||||
@@ -177,7 +181,7 @@ p = x^5 - x - 1
|
||||
roots(p)
|
||||
```
|
||||
|
||||
A consequence of the fundamental theorem of algebra and the factor theorem is that any fifth degree polynomial with integer coefficients has $5$ roots, where possibly some are complex. For real coefficients, these complex values must come in conjugate pairs, which can be observed from the output. The lone real root is approximately `1.1673039782614187`. This value being a numeric approximation to the irrational root.
|
||||
A consequence of the fundamental theorem of algebra and the factor theorem is that any fifth degree polynomial with integer coefficients has $5$ roots, where possibly some are complex. For real coefficients, these complex values must come in conjugate pairs, which can be observed from the output. The lone real root (with imaginary part that is `0.0im`) is approximately `1.16730397⋯`. This value being a numeric approximation to the irrational root.
|
||||
|
||||
|
||||
:::{.callout-note}
|
||||
@@ -196,17 +200,9 @@ p = (x-1)^5
|
||||
roots(p)
|
||||
```
|
||||
|
||||
The `Polynomials` package has the `multroot` function to identify roots of polynomials when there are multiplicities expected. This function is not exported, so is called through:
|
||||
|
||||
Internally, the `Polynomials` package has a routine^[`Polynomials.Multroot.multroot`] to identify multiple roots and this is used on conversion to a `FactoredPolynomial`:
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
x = variable()
|
||||
p = (x-1)^5
|
||||
Polynomials.Multroot.multroot(p)
|
||||
```
|
||||
|
||||
Converting to the `FactoredPolynomial` type also does this work:
|
||||
|
||||
```{julia}
|
||||
convert(FactoredPolynomial, p)
|
||||
@@ -248,15 +244,36 @@ The line is then given from the *point-slope* form by, say, $y= y_0 + m\cdot (x-
|
||||
|
||||
A line, $y=mx+b$ can be a linear polynomial or a constant depending on $m$, so we could say $2$ points determine a polynomial of degree $1$ or less. Similarly, $3$ distinct points determine a degree $2$ polynomial or less, $\dots$, $n+1$ distinct points determine a degree $n$ or less polynomial. Finding a polynomial, $p$ that goes through $n+1$ points (i.e., $p(x_i)=y_i$ for each $i$) is called [polynomial interpolation](https://en.wikipedia.org/wiki/Polynomial_interpolation). The main theorem is:
|
||||
|
||||
::: {.theorem title="Polynomial interpolation theorem"}
|
||||
|
||||
> *Polynomial interpolation theorem*: There exists a unique polynomial of degree $n$ or less that interpolates the points $(x_0,y_0), (x_1,y_1), \dots, (x_n, y_n)$ when the $x_i$ are distinct.
|
||||
For $n+1$ points $(x_0,y_0), (x_1,y_1), \dots, (x_n, y_n)$ with distinct $x_i$ there exists a *unique* polynomial. $p$, of degree $n$ or less that interpolates each point. That is, $p(x_i) = y_i$ for every $i$.
|
||||
:::
|
||||
|
||||
|
||||
|
||||
(Uniqueness follows as suppose $p$ and $q$ satisfy the above, then $(p-q)(x) = 0$ at each of the $x_i$ and is of degree $n$ or less, so must be the $0$ polynomial. Existence comes by construction. See the Lagrange basis in the questions.)
|
||||
|
||||
|
||||
Knowing we can succeed, we approach the problem of $3$ points, say $(x_0, y_0)$, $(x_1,y_1)$, and $(x_2, y_2)$. There is a polynomial $p = a\cdot x^2 + b\cdot x + c$ with $p(x_i) = y_i$. This gives $3$ equations for the $3$ unknown values $a$, $b$, and $c$:
|
||||
|
||||
Suppose three points on a polynomial are $(1,3)$, $(2,1)$, $(3,2)$. Find the degree $2$ (or less) polynomial that interpolates the points.
|
||||
|
||||
Numerically, the `fit` function from the `Polynomials` package will return the interpolating polynomial.
|
||||
|
||||
|
||||
```{julia}
|
||||
p = fit(Polynomial, [1,2,3], [3,1,2])
|
||||
```
|
||||
|
||||
And note:
|
||||
|
||||
|
||||
```{julia}
|
||||
p(1) == 3, p(2) == 1, p(3) == 2
|
||||
```
|
||||
|
||||
We *can* find a general formula for the interpolating polynomial using `SymPy` for three points, say $(x_0, y_0)$, $(x_1,y_1)$, and $(x_2, y_2)$. The theorem says there is a polynomial $p = a\cdot x^2 + b\cdot x + c$, with any of $a$, $b$, or $c$ *possibly* $0$, satisfying $p(x_i) = y_i$.
|
||||
|
||||
The interpolating condition leads to three equations:
|
||||
|
||||
|
||||
$$
|
||||
@@ -268,69 +285,69 @@ a\cdot x_2^2 + b\cdot x_2 + c &= y_2\\
|
||||
$$
|
||||
|
||||
|
||||
Solving this with `SymPy` is tractable. A generator is used below to create the $3$ equations; the `zip` function is a simple means to iterate over $2$ or more iterables simultaneously:
|
||||
Solving this with `SymPy` is tractable. A generator is used below to create the three equations:
|
||||
|
||||
|
||||
```{julia}
|
||||
SymPy.@syms a b c xs[0:2] ys[0:2]
|
||||
eqs = tuple((a*xi^2 + b*xi + c ~ yi for (xi,yi) in zip(xs, ys))...)
|
||||
eqs = [a*xi^2 + b*xi + c ~ yi for (xi,yi) in zip(xs, ys)]
|
||||
abc = SymPy.solve(eqs, (a,b,c))
|
||||
```
|
||||
|
||||
As can be seen, the terms do get quite unwieldy when treated symbolically. Numerically, the `fit` function from the `Polynomials` package will return the interpolating polynomial. To compare,
|
||||
|
||||
|
||||
```{julia}
|
||||
fit(Polynomial, [1,2,3], [3,1,2])
|
||||
```
|
||||
|
||||
and we can compare that the two give the same answer with, for example:
|
||||
As can be seen, the terms do get quite unwieldy when treated symbolically.
|
||||
We can compare with the numeric fit. For example, the value of `b` numerically was `6.5` and symbolically is identical and can be found as follows:
|
||||
|
||||
|
||||
```{julia}
|
||||
abc[b]((xs .=> [1,2,3])..., (ys .=> [3,1,2])...)
|
||||
```
|
||||
|
||||
(Ignore the tricky way of substituting in each value of `xs` and `ys` for the symbolic values in `x` and `y`.)
|
||||
(Ignore the tricky way of substituting in each value of `xs` and `ys` for the symbolic values in `x` and `y`, it is shortcut to `xs₀=>1, xs₁=>2, xs₂=>3, ...`.)
|
||||
|
||||
|
||||
##### Example Inverse quadratic interpolation
|
||||
##### Example: Inverse quadratic interpolation
|
||||
|
||||
|
||||
A related problem, that will arise when finding iterative means to solve for zeros of functions, is *inverse* quadratic interpolation. That is finding $q$ that goes through the points $(x_0,y_0), (x_1, y_1), \dots, (x_n, y_n)$ satisfying $q(y_i) = x_i$. (That is $x$ and $y$ are reversed, as with inverse functions.) For the envisioned task, where the inverse quadratic function intersects the $x$ axis is of interest, which is at the constant term of the polynomial (as it is like the $y$ intercept of typical polynomial). Let's see what that is in general by replicating the above steps (though now the assumption is the $y$ values are distinct):
|
||||
A related problem, that will arise when finding iterative means to solve for zeros of functions, is *inverse* quadratic interpolation. That is finding $q$ that goes through the points $(x_0,y_0), (x_1, y_1), \dots, (x_n, y_n)$ satisfying $q(y_i) = x_i$. (That is $x$ and $y$ are reversed, as with inverse functions.) For the envisioned task, where the inverse quadratic function intersects the $x$ axis is of interest, which is at the constant term of the polynomial (as it is like the $y$ intercept of typical polynomial).
|
||||
|
||||
To find this for the points $(1,3)$, $(2,1)$, $(3,2)$ we flip around the order above and use a `y` variable to distinguish:
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
SymPy.@syms a b c xs[0:2] ys[0:2]
|
||||
eqs = tuple((a*yi^2 + b*yi + c ~ xi for (xi, yi) in zip(xs,ys))...)
|
||||
abc = SymPy.solve(eqs, (a,b,c))
|
||||
abc[c]
|
||||
ip = fit(Polynomial, [3,1,2], [1,2,3]; var= :y)
|
||||
```
|
||||
|
||||
We can graphically see the result for the specific values of `xs` and `ys` as follows:
|
||||
We see that $-2$ (or `ip(0)`) is the $x$ intercept.
|
||||
|
||||
|
||||
@fig-inverse-quadratic-interpolation shows the $x$ intercept of the inverse quadratic interpolating polynomial for the specific values of `xs` and `ys` as follows:
|
||||
|
||||
::: {#fig-inverse-quadratic-interpolation}
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
SymPy.@syms a b c xs[0:2] ys[0:2]
|
||||
eqs = tuple((a*yi^2 + b*yi + c ~ xi for (xi, yi) in zip(xs,ys))...)
|
||||
abc = SymPy.solve(eqs, (a,b,c))
|
||||
abc[c]
|
||||
let
|
||||
SymPy.@syms a b c xs[0:2] ys[0:2]
|
||||
eqs = tuple((a*yi^2 + b*yi + c ~ xi for (xi, yi) in zip(xs,ys))...)
|
||||
abc = SymPy.solve(eqs, (a,b,c))
|
||||
|
||||
𝒙s, 𝒚s = [1,2,3], [3,1,2]
|
||||
q = fit(Polynomial, 𝒚s, 𝒙s) # reverse
|
||||
# plot
|
||||
us = range(-1/4, 4, length=100)
|
||||
vs = q.(us)
|
||||
plot(vs, us, legend=false)
|
||||
scatter!(𝒙s, 𝒚s)
|
||||
plot!(zero)
|
||||
x0 = abc[c]((xs .=> 𝒙s)..., (ys .=> 𝒚s)...)
|
||||
scatter!([SymPy.N(x0)], [0], markershape=:star)
|
||||
𝒙s, 𝒚s = [1,2,3], [3,1,2]
|
||||
q = fit(Polynomial, 𝒚s, 𝒙s; var=:y) # reverse
|
||||
# plot
|
||||
us = range(-1/8, 3.5, length=100)
|
||||
vs = q.(us)
|
||||
plot(vs, us; xticks=-4:3, legend=false)
|
||||
scatter!(𝒙s, 𝒚s; marker=(3, :blue) )
|
||||
plot!(zero)
|
||||
scatter!([(q(0), 0)], marker=(6,:star,:red))
|
||||
end
|
||||
```
|
||||
|
||||
Plot of inverse quadratic interpolating polynomial through three points with the $x$ intercept marked with a star
|
||||
|
||||
:::
|
||||
|
||||
|
||||
## Questions
|
||||
|
||||
|
||||
@@ -382,15 +399,16 @@ numericq(length(st))
|
||||
###### Question
|
||||
|
||||
|
||||
Mathematically we say the $0$ polynomial has no degree. What convention does `Polynomials` use? (Look at `degree(zero(Polynomial))`.)
|
||||
Mathematically we say the $0$ polynomial has no degree. What convention does `Polynomials` use?
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["`nothing`", "`-1`", "`0`", "`Inf`", "`-Inf`"]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
explanation = "Look at `degree(zero(Polynomial))`"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -424,7 +442,7 @@ Make graphs of both `q` and `sin`. Over which interval is the approximation (vis
|
||||
choices = ["``[0,1]``",
|
||||
"``[0,\\pi]``",
|
||||
"``[0,2\\pi]``"]
|
||||
radioq(choices, 1, keep_order=true)
|
||||
buttonq(choices, 1)
|
||||
```
|
||||
|
||||
(This [blog post](https://www.nullhardware.com/blog/fixed-point-sine-and-cosine-for-embedded-systems/) shows how this approximation is valuable under some specific circumstances.)
|
||||
@@ -459,8 +477,10 @@ A basis used by [Lagrange](https://en.wikipedia.org/wiki/Lagrange_polynomial) is
|
||||
|
||||
|
||||
$$
|
||||
l_i(x) = \prod_{0 \leq j \leq n; j \ne i} \frac{x-x_j}{x_i - x_j} =
|
||||
\frac{(x-x_0)\cdot(x-x_1)\cdot \cdots \cdot (x-x_{i-1}) \cdot (x-x_{i+1}) \cdot \cdots \cdot (x-x_n)}{(x_i-x_0)\cdot(x_i-x_1)\cdot \cdots \cdot (x_i-x_{i-1}) \cdot (x_i-x_{i+1}) \cdot \cdots \cdot (x_i-x_n)}.
|
||||
\begin{align*}
|
||||
l_i(x) &= \prod_{0 \leq j \leq n; j \ne i} \frac{x-x_j}{x_i - x_j} \\
|
||||
&= \frac{(x-x_0)\cdot(x-x_1)\cdot \cdots \cdot (x-x_{i-1}) \cdot (x-x_{i+1}) \cdot \cdots \cdot (x-x_n)}{(x_i-x_0)\cdot(x_i-x_1)\cdot \cdots \cdot (x_i-x_{i-1}) \cdot (x_i-x_{i+1}) \cdot \cdots \cdot (x_i-x_n)}.
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
That is $l_i(x)$ is a product of terms like $(x-x_j)/(x_i-x_j)$ *except* when $j=i$.
|
||||
@@ -486,7 +506,7 @@ All terms like ``(x-x_j)/(x_0 - x_j)`` will be ``1`` when ``x=x_0`` and these ar
|
||||
""",
|
||||
"The term ``(x_0-x_0)`` will be ``0``, so the product will be zero"
|
||||
]
|
||||
radioq(choices, 1)
|
||||
buttonq(choices, 1)
|
||||
```
|
||||
|
||||
What is the value of $l_i(x_i)$?
|
||||
@@ -518,7 +538,7 @@ The term like ``(x-x_1)/(x_0 - x_1)`` will be ``0`` when ``x=x_1`` and so the pr
|
||||
""",
|
||||
"The term ``(x-x_1)/(x_0-x_1)`` is omitted from the product, so the answer is non-zero."
|
||||
]
|
||||
radioq(choices, 1)
|
||||
buttonq(choices, 1)
|
||||
```
|
||||
|
||||
What is the value of $l_i(x_j)$ *if* $i \ne j$?
|
||||
@@ -544,12 +564,44 @@ What is the value of $p(x_j)$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["``0``", "``1``", "``y_j``"]
|
||||
radioq(choices, 3)
|
||||
buttonq(choices, 3)
|
||||
```
|
||||
|
||||
This last answer is why $p$ is called an *interpolating* polynomial and this question shows an alternative way to identify interpolating polynomials from solving a system of linear equations.
|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
Symbolically, the inverse quadratic polynomial through three points can be identified symbolically using a pattern similar to that shown for polynomial interpolation. This shows the formula for the $x$ intercept:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
SymPy.@syms a b c xs[0:2] ys[0:2]
|
||||
eqs = [a*yi^2 + b*yi + c ~ xi for (xi, yi) in zip(xs,ys)]
|
||||
abc = SymPy.solve(eqs, (a,b,c))
|
||||
abc[c]
|
||||
```
|
||||
|
||||
As with the example, plug in the following values for the $x$ and $y$ coordinates of the three points in the example above:
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
abc[c]((xs .=> [1,2,3])..., (ys .=> [3,1,2])...) # answer not shown
|
||||
```
|
||||
|
||||
What value is identified?
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
val = -2
|
||||
numericq(val)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
|
||||
@@ -581,7 +633,7 @@ It is ``0\cdot T_0(x) + 1\cdot T_1(x) + 2\cdot T_2(x) + 3\cdot T_3(x) = 0``
|
||||
raw"""
|
||||
It is ``0\cdot T_0(x) + 1\cdot T_1(x) + 2\cdot T_2(x) + 3\cdot T_3(x) = -2 - 8\cdot x + 4\cdot x^2 + 12\cdot x^3``
|
||||
"""]
|
||||
radioq(choices, 3)
|
||||
buttonq(choices, 3)
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
|
||||
@@ -34,17 +34,21 @@ The rational numbers are simply ratios of integers, of the form $p/q$ for non-ze
|
||||
We know that polynomials have nice behaviors due to the following facts:
|
||||
|
||||
|
||||
* Behaviors at $-\infty$, $\infty$ are known just from the leading term.
|
||||
* There are possible wiggles up and down, the exact behavior depends on intermediate terms, but there can be no more than $n-1$ wiggles.
|
||||
* The number of real zeros is no more than $n$, the degree of the polynomial.
|
||||
* Behaviors at $-\infty$, $\infty$ are known just from the leading term.
|
||||
|
||||
* There are possible wiggles up and down, the exact behavior depends on intermediate terms, but there can be no more than $n-1$ wiggles.
|
||||
|
||||
* The number of real zeros is no more than $n$, the degree of the polynomial.
|
||||
|
||||
|
||||
Rational functions are not quite so nice:
|
||||
|
||||
|
||||
* behavior at $-\infty$ and $\infty$ can be like a polynomial of any degree, including constants
|
||||
* behaviour at any value x can blow up due to division by $0$ - rational functions, unlike polynomials, need not be always defined
|
||||
* The function may or may not cross zero, even if the range includes every other point, as the graph of $f(x) =1/x$ will show.
|
||||
* behavior at $-\infty$ and $\infty$ can be like a polynomial of any degree, including constants
|
||||
|
||||
* behaviour at any value x can blow up due to division by $0$---rational functions, unlike polynomials, need not be always defined
|
||||
|
||||
* The function may or may not cross zero, even if the range includes every other point, as the graph of $f(x) =1/x$ illustrates.
|
||||
|
||||
|
||||
Here, as with our discussion on polynomials, we are interested for now in just a few properties:
|
||||
@@ -65,28 +69,34 @@ $$
|
||||
f(x) = \frac{(x-1)^2 \cdot (x-2)}{(x+3) \cdot (x-3)}.
|
||||
$$
|
||||
|
||||
::: {#fig-plot-rational-f-over-minus10-10-bad-choice}
|
||||
```{julia}
|
||||
f(x) = (x-1)^2 * (x-2) / ((x+3)*(x-3) )
|
||||
plot(f, -10, 10)
|
||||
```
|
||||
|
||||
We would be hard pressed to answer any of the three questions above from the graph, though, on inspection, we might think the strange spikes have something to do with $x$ values where $q(x)=0$.
|
||||
Basic plot of the rational function $f(x)$ over $[-10,10]$. As no extra effort was made to adjust the $y$-viewing window, the graphic is not to useful for identifying basic properties of the function.
|
||||
:::
|
||||
|
||||
We would be hard pressed to answer any of the three questions above from the @fig-plot-rational-f-over-minus10-10-bad-choice, though, on inspection, we might think the strange spikes have something to do with $x$ values where $q(x)=0$.
|
||||
|
||||
|
||||
The question of big or small $x$ is not answered well with this graph, as the spikes dominate the scale of the $y$-axis. Setting a much larger viewing window illuminates this question:
|
||||
|
||||
|
||||
::: {#fig-plot-rational-f-over-minus100-100-can-see-slant-asymptote}
|
||||
```{julia}
|
||||
plot(f, -100, 100)
|
||||
```
|
||||
|
||||
Plot of rational function $f(x)$ over $[-100, 100]$ allowing the slant asymptote to be identified
|
||||
:::
|
||||
We can see from this, that the function eventually looks like a slanted straight line. The *eventual* shape of the graph is something that can be determined just from the two leading terms.
|
||||
|
||||
|
||||
The spikes haven't vanished completely. It is just that with only a few hundred points to make the graph, there aren't any values near enough to the problem to make a large spike. The spikes happen because the function has a *vertical asymptote* at these values. Though not quite right, it is reasonable to think of the graph being made by selecting a few hundred points in the specified domain, computing the corresponding $y$ values, plotting the pairs, and finally connecting the points with straight line segments. Near a vertical asymptote the function values can be arbitrarily large in absolute values, though at the vertical asymptote the function is undefined. This graph doesn't show such detail.
|
||||
|
||||
|
||||
The spikes will be related to the points where $q(x) = 0$, though not necessarily all of them – not all such points will produce a vertical asymptote.
|
||||
The spikes will be related to the points where $q(x) = 0$, though not necessarily all of them.
|
||||
|
||||
|
||||
Where the function crosses $0$ is very hard to tell from these two graphs. As well, other finer features, such as local peaks or valleys, when present, can be hard to identify as the $y$-scale is set to accommodate the asymptotes. Working around the asymptotes requires some extra effort. Strategies are discussed herein.
|
||||
@@ -146,12 +156,14 @@ h = g(x) # a symbolic expression
|
||||
apart(h)
|
||||
```
|
||||
|
||||
This decomposition breaks the rational expression into two pieces: $x-4$ and $40/(3x+9) + 2/(3x-9)$. The first piece would have a graph that is the line with slope $1$ and $y$-intercept $4$. As $x$ goes to $\infty$, the second piece will clearly go towards $0,$ as this simple graph shows:
|
||||
|
||||
This decomposition breaks the rational expression into two pieces: $x-4$ and $40/(3x+9) + 2/(3x-9)$. The first piece would have a graph that is the line with slope $1$ and $y$-intercept $4$. As $x$ goes to $\infty$, the second piece will clearly go towards $0,$ as @fig-plot-aparth-minus-x-minus-4-over-10-100 shows.
|
||||
|
||||
::: {#fig-plot-aparth-minus-x-minus-4-over-10-100}
|
||||
```{julia}
|
||||
plot(apart(h) - (x - 4), 10, 100)
|
||||
```
|
||||
Plot of just remainder term of the rational function $g(x)$ after reduction showing the decay towards $0$ as $x$ gets large
|
||||
:::
|
||||
|
||||
Similarly, a plot over $[-100, -10]$ would show decay towards $0$, though in that case from below. Combining these two facts then, it is now no surprise that the graph of the rational function $f(x)$ should approach a straight line, in this case $y=x-4$, as $x \rightarrow \pm \infty$.
|
||||
|
||||
@@ -176,16 +188,19 @@ $$
|
||||
The terms $(1 - 4/x + 5/x^2 - 2/x^3)$ and $(1 - 9/x^2)$ go towards $1$ as $x \rightarrow \pm \infty$, as each term with $x$ goes towards $0$. So the dominant terms comes from the ratio of the leading terms, $x^3$ and $x^2$. This ratio is $x$, so their will be an asymptote around a line with slope $1$. (The fact that the asymptote is $y=x-4$ takes a bit more work, as a division step is needed.)
|
||||
|
||||
|
||||
Just by looking at the ratio of the two leading terms, the behaviour as $x \rightarrow \pm \infty$ can be discerned. If this ratio is of:
|
||||
Just by looking at the ratio of the two leading terms, the behaviour as $x \rightarrow \pm \infty$ can be discerned. If this ratio is of the form:
|
||||
|
||||
|
||||
* the form $c x^m$ with $m > 1$ then the shape will follow the polynomial growth of the monomial $c x^m$.
|
||||
* the form $c x^m$ with $m=1$ then there will be a line with slope $c$ as a *slant asymptote*.
|
||||
* the form $cx^0$ with $m=0$ (or just $c$) then there will be a *horizontal asymptote* $y=c$.
|
||||
* the form $c/x^{m}$ with $m > 0$ then there will be a horizontal asymptote $y=0$, or the $y$ axis.
|
||||
* $c x^m$ with $m > 1$ then the shape will follow the polynomial growth of the monomial $c x^m$;
|
||||
|
||||
* $c x^m$ with $m=1$ then there will be a line with slope $c$ as a *slant asymptote*;
|
||||
|
||||
* $cx^0$ with $m=0$ (or just $c$) then there will be a *horizontal asymptote* $y=c$;
|
||||
|
||||
* $c/x^{m}$ with $m > 0$ then there will be a horizontal asymptote $y=0$, or the $y$ axis.
|
||||
|
||||
|
||||
To expand on the first points where the degree of the numerator is greater than that of the denominator, we have from the division algorithm that if $a(x)$ is the numerator and $b(x)$ the denominator, then $a(x)/b(x) = q(x) + r(x)/b(x)$ where the degree of $b(x)$ is greater than the degree of $r(x)$, so the right-most term will have a horizontal asymptote of $0$. This says that the graph will eventually approach the graph of $q(x)$, giving more detail than just saying it follows the shape of the leading term of $q(x)$, at the expense of the work required to find $q(x)$.
|
||||
To expand on the first point, where the degree of the numerator is greater than that of the denominator, we have from the division algorithm that if $a(x)$ is the numerator and $b(x)$ the denominator, then $a(x)/b(x) = q(x) + r(x)/b(x)$ where the degree of $b(x)$ is greater than the degree of $r(x)$, so the right-most term will have a horizontal asymptote of $0$. This says that the graph will eventually approach the graph of $q(x)$, giving more detail than just saying it follows the shape of the leading term of $q(x)$, at the expense of the work required to find $q(x)$.
|
||||
|
||||
|
||||
### Examples
|
||||
@@ -217,8 +232,7 @@ Then we can see that the ratio of the leading terms is $x^5 / (5x^4) = (1/5)x$.
|
||||
```{julia}
|
||||
#| hold: true
|
||||
p = (x^5 - 2x^4 + 3x^3 - 4x^2 + 5) / (5x^4 + 4x^3 + 3x^2 + 2x + 1)
|
||||
quo, rem = divrem(numerator(p), denominator(p)) # or apart(p)
|
||||
quo
|
||||
div(numerator(p), denominator(p)) # or apart(p)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -240,23 +254,23 @@ bottom = x-1
|
||||
quo, rem = divrem(top, bottom)
|
||||
```
|
||||
|
||||
The graph has nothing in common with the graph of the quotient for small $x$
|
||||
|
||||
@fig-asymptote-matches-for-large-x shows that for small $x$ ($\lvert x\rvert \approx 0$) the graph of the rational function and `quo` are not similar, but that they are when $x$ is larger.
|
||||
|
||||
::: {#fig-asymptote-matches-for-large-x}
|
||||
|
||||
```{julia}
|
||||
plot(top/bottom, -3, 3)
|
||||
plot!(quo, -3, 3)
|
||||
#| echo: false
|
||||
p1 = plot(top/bottom, -3, 3; title="small x", label="p/q")
|
||||
plot!(quo, -3, 3; line=(3, :red), label="quo")
|
||||
|
||||
p2 = plot(top/bottom, 5, 10; title="larger x", label="p/q")
|
||||
plot!(quo, 5, 10; label="quo")
|
||||
|
||||
plot(p1, p2)
|
||||
```
|
||||
|
||||
But the graphs do match for large $x$:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(top/bottom, 5, 10)
|
||||
plot!(quo, 5, 10)
|
||||
```
|
||||
|
||||
---
|
||||
The asymptotic nature is similar to the function for larger $x$ values.
|
||||
:::
|
||||
|
||||
|
||||
Finally, consider this rational expression in factored form:
|
||||
@@ -272,7 +286,7 @@ By looking at the powers we can see that the leading term of the numerator will
|
||||
#### Partial fractions
|
||||
|
||||
|
||||
The `apart` function was useful to express a rational function in terms of a polynomial plus additional rational functions whose horizontal asymptotes are $0$. This function computes the partial fraction [decomposition](https://en.wikipedia.org/wiki/Partial_fraction_decomposition) of a rational function. Outside of the initial polynomial, this decomposition is a reexpression of a rational function into a sum of rational functions, where the denominators are *irreducible*, or unable to be further factored (non-trivially) and the numerators have lower degree than the denominator. Hence the horizontal asymptotes of $0$.
|
||||
The `apart` function is used to express a rational function in terms of a polynomial plus additional rational functions whose horizontal asymptotes are $0$. This function computes the partial fraction [decomposition](https://en.wikipedia.org/wiki/Partial_fraction_decomposition) of a rational function. Outside of the initial polynomial, this decomposition is a reexpression of a rational function into a sum of rational functions, where the denominators are *irreducible*, or unable to be factored further over the set of rational numbers and the numerators have lower degree than the denominator. Hence the horizontal asymptotes of $0$.
|
||||
|
||||
|
||||
To see another example we have:
|
||||
@@ -291,28 +305,35 @@ The denominator, $q$, has factors $x-3$ and $x^2 - x - 1$, each irreducible. The
|
||||
### Vertical asymptotes
|
||||
|
||||
|
||||
As just discussed, the graph of $1/x$ will have a horizontal asymptote. However it will also show a spike at $0$:
|
||||
|
||||
As just discussed, the graph of $1/x$ will have a horizontal asymptote. However its graph (@fig-plot-1-over-x-over-minus1-1-showing-vertical-asymptote) also shows a spike at $0$.
|
||||
|
||||
::: {#fig-plot-1-over-x-over-minus1-1-showing-vertical-asymptote}
|
||||
```{julia}
|
||||
plot(1/x, -1, 1)
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1/x$ over $[-1,1]$. The vertical asymptote at $0$ dominates the display, preventing other features to be identified.
|
||||
:::
|
||||
|
||||
Again, this spike is an artifact of the plotting algorithm. The $y$ values for $x$-values just smaller than $0$ are large negative values and the $x$ values just larger than $0$ produce large, positive $y$ values.
|
||||
|
||||
|
||||
The two points with $x$ components closest to $0$ are connected with a line, though that is misleading. Here we deliberately use far fewer points to plot $1/x$ to show how this happens:
|
||||
The two points with $x$ components closest to $0$ are connected with a line, though that is misleading. @fig-plot-of-1-over-x-few-points we deliberately use far fewer points to plot $1/x$ to show how this happens:
|
||||
|
||||
::: {#fig-plot-of-1-over-x-few-points}
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = 1/x
|
||||
xs = range(-1, 1, length=12)
|
||||
ys = f.(xs)
|
||||
plot(xs, ys)
|
||||
plot(xs, ys; legend=false)
|
||||
scatter!(xs, ys)
|
||||
```
|
||||
|
||||
Plot of $f(x) = 1/x$ with few points to illustrate how "spikes" can be artifacts of the plotting algorithm
|
||||
:::
|
||||
|
||||
The line $x = 0$ is a *vertical asymptote* for the graph of $1/x$. As $x$ values get close to $0$ from the right, the $y$ values go towards $\infty$ and as the $x$ values get close to $0$ on the left, the $y$ values go towards $-\infty$.
|
||||
|
||||
|
||||
@@ -328,12 +349,12 @@ $$
|
||||
|
||||
where both $r(c)$ and $s(c)$ are non zero. Knowing $m$ and $n$ (the multiplicities of the root $c$) allows the following to be said:
|
||||
|
||||
* If $m < n$ then $x=c$ will be a vertical asymptote.
|
||||
|
||||
* If $m < n$ then $x=c$ will be a vertical asymptote.
|
||||
* If $m \geq n$ then $x=c$ will not be vertical asymptote. (The value $c$ will be known as a removable singularity). In this case, the graph of $p(x)/q(x)$ and the graph of $(x-c)^{m-n}r(x)/s(x)$ will differ, though very slightly, as the latter will include a value for $x=c$, whereas $x=c$ is not in the domain of $p(x)/q(x)$.
|
||||
* If $m \geq n$ then $x=c$ will not be vertical asymptote. (The value $c$ will be known as a removable singularity). In this case, the graph of $p(x)/q(x)$ and the graph of $(x-c)^{m-n}r(x)/s(x)$ will differ, though very slightly, as the latter will include a value for $x=c$, whereas $x=c$ is not in the domain of $p(x)/q(x)$.
|
||||
|
||||
|
||||
Finding the multiplicity may or may not be hard, but there is a very kludgy quick check that is often correct. With `Julia`, if you have a rational function that has `f(c)` evaluate to `Inf` or `-Inf` then there will be a vertical asymptote. If the expression evaluates to `NaN`, more analysis is needed. (The value of `0/0` is `NaN`, where as `1/0` is `Inf`.)
|
||||
Finding the multiplicity may or may not be hard, but there is a very kludgy quick check that is often correct. With `Julia`, if you have a rational function that has `f(c)` evaluate to `Inf` or `-Inf` then there will be a vertical asymptote. If the expression evaluates to `NaN`, more analysis is needed. (The value of `0/0` is `NaN`, whereas `1/0` is `Inf`.)
|
||||
|
||||
|
||||
For example, the function $f(x) = ((x-1)^2 \cdot (x-2)) / ((x+3) \cdot(x-3))$ has vertical asymptotes at $-3$ and $3$, as its graph illustrated. Without the graph we could see this as well:
|
||||
@@ -353,45 +374,62 @@ As seen in several graphs, the basic plotting algorithm does a poor job with ver
|
||||
|
||||
Consider again the function $f(x) = ((x-1)^2 \cdot (x-2)) / ((x+3) \cdot(x-3))$. Without much work, we can see that $x=3$ and $x=-3$ will be vertical asymptotes and there will be a slant asymptote with slope $1$. How to graph this?
|
||||
|
||||
::: {#fig-rational-function-avoiding-spikes}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
f(x) = (x-1)^2 * (x-2) / ((x+3)*(x-3) )
|
||||
p1 = plot(f, -5, 5; legend=false,
|
||||
title="no effort", xlabel="(a)")
|
||||
p2 = plot(f, -2.9, 2.9; legend=false,
|
||||
title = "restrict domain", xlabel="(b)")
|
||||
p3 = plot(f, -20, 20; ylims=(-20, 20),
|
||||
legend=false,
|
||||
title ="set ylims", xlabel="(c)")
|
||||
p4 = plot(rangeclamp(f, 30), -20, 20; legend=false,
|
||||
title="rangeclamp", xlabel="(d)")
|
||||
|
||||
We can avoid the vertical asymptotes in our viewing window. For example we could look at the area between the vertical asymptotes, by plotting over $(-2.9, 2.9)$, say:
|
||||
plot(p1, p2, p3, p4; layout = 4)
|
||||
|
||||
```
|
||||
|
||||
Four ways to plot the rational function $f(x)$: (a) has no effort, (b) restricts the plot domain, (c) uses `ylims` to restrict the viewing window, (d) calls `rangeclamp` to break lines when there are larger values.
|
||||
:::
|
||||
|
||||
|
||||
@fig-rational-function-avoiding-spikes shows four graphics, produced along the lines of:
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
f(x) = (x-1)^2 * (x-2) / ((x+3)*(x-3))
|
||||
plot(f, -5, 5)
|
||||
```
|
||||
|
||||
This no effort approach shows the reason extra effort should be taken. This extra effort can come in terms of limiting the $x$ viewing window by backing off from the boundaries $(-3, 3)$:
|
||||
|
||||
|
||||
```{julia}
|
||||
f(x) = (x-1)^2 * (x-2) / ((x+3)*(x-3) )
|
||||
#| eval: false
|
||||
plot(f, -2.9, 2.9)
|
||||
```
|
||||
|
||||
This backs off by $\delta = 0.1$. As we have that $3 - 2.9$ is $\delta$ and $1/\delta$ is 10, the $y$ axis won't get too large, and indeed it doesn't.
|
||||
|
||||
|
||||
This graph doesn't show well the two zeros at $x=1$ and $x=2$, for that a narrower viewing window is needed. By successively panning throughout the interesting part of the graph, we can get a view of the function.
|
||||
|
||||
|
||||
We can also clip the `y` axis. The `plot` function can be passed an argument `ylims=(lo, hi)` to limit which values are plotted. With this, we can have:
|
||||
|
||||
Extra effort can come by limiting the $y$ viewing window through `ylims`:
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
plot(f, -5, 5, ylims=(-20, 20))
|
||||
#| eval: false
|
||||
plot(f, -20, 20, ylims=(-20, 20))
|
||||
```
|
||||
|
||||
This isn't ideal, as the large values are still computed, just the viewing window is clipped. This leaves the vertical asymptotes still effecting the graph.
|
||||
|
||||
|
||||
There is another way, we could ask `Julia` to not plot $y$ values that get too large. This is not a big request. If instead of the value of `f(x)` - when it is large - we use `NaN` instead, then the connect-the-dots algorithm will skip those values.
|
||||
|
||||
|
||||
This was discussed in an earlier section where the `rangeclamp` function was introduced to replace large values of `f(x)` (in absolute values) with `NaN`.
|
||||
Finally, we show the use of `rangeclamp` from the `CalculusWithJulia` package which replaces the plotted values with `NaN` when they are large (in absolute value). This breaks the lines.
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(rangeclamp(f, 30), -25, 25) # rangeclamp is in the CalculusWithJulia package
|
||||
#| eval: false
|
||||
plot(rangeclamp(f, 30), -20, 20)
|
||||
```
|
||||
|
||||
We can see the general shape of $3$ curves broken up by the vertical asymptotes. The two on the side heading off towards the line $x-4$ and the one in the middle. We still can't see the precise location of the zeros, but that wouldn't be the case with most graphs that show asymptotic behaviors. However, we can clearly tell where to "zoom in" were those of interest.
|
||||
|
||||
|
||||
### Sign charts
|
||||
|
||||
|
||||
@@ -410,7 +448,7 @@ The usual recipe for construction follows these steps:
|
||||
* Identify "test points" within each implied interval (these are $(-\infty, -1)$, $(-1,0)$, $(0,1)$, and $(1, \infty)$ in the example) and check for the sign of $f(x)$ at these test points. Write in `-`, `+`, `0`, or `*`, as appropriate. The value comes from the fact that "continuous" functions may only change sign when they cross $0$ or are undefined.
|
||||
|
||||
|
||||
With the computer, where it is convenient to draw a graph, it might be better to emphasize the sign on the graph of the function, but at times numeric values are preferred. The `sign_chart` function from `CalculusWithJulia` does this analysis by numerically identifying points where the function is $0$ or $\infty$ and indicating the sign as $x$ crosses over these points.
|
||||
With the computer, where it is convenient to draw a graph, it might be better to emphasize the sign on the graph of the function, but at times numeric values are preferred. The `sign_chart` function from `CalculusWithJulia` does this analysis by numerically identifying points where the function is $0$ or $\infty$ and indicating any sign change as $x$ crosses over these points.
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -419,7 +457,7 @@ f(x) = x^3 - x
|
||||
sign_chart(f, -3/2, 3/2)
|
||||
```
|
||||
|
||||
This format is a bit different from above, but shows to the left of $-1$ a minussign; between $-1$ and $0$ a plus sign; between $0$ and $1$ a minus sign; and between $1$ and $3/2$ a plus sign.
|
||||
This format is a bit different from above, but shows from $-3/2$ to the left of $-1$ a minus sign; between $-1$ and $0$ a plus sign; between $0$ and $1$ a minus sign; and between $1$ and $3/2$ a plus sign.
|
||||
|
||||
## Pade approximate
|
||||
|
||||
@@ -441,20 +479,26 @@ $$
|
||||
\tan(x) \approx \frac{x - 1/15 \cdot x^3}{1 - 2/5 \cdot x^2}
|
||||
$$
|
||||
|
||||
We can look graphically at these approximations:
|
||||
@fig-pade-approximations show these approximations through a graph.
|
||||
|
||||
::: {#fig-pade-approximations}
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
sin_p(x) = (x - (7/60)*x^3) / (1 + (1/20)*x^2)
|
||||
tan_p(x) = (x - (1/15)*x^3) / (1 - (2/5)*x^2)
|
||||
plot(sin, -pi, pi)
|
||||
plot!(sin_p, -pi, pi)
|
||||
p1 = plot(sin, -pi, pi; label="sin")
|
||||
plot!(p1, sin_p, -pi, pi; label="pade approx")
|
||||
|
||||
Δ = 0.1
|
||||
p2 = plot(tan, -pi/2 + Δ, pi/2 - Δ; label="tan")
|
||||
plot!(p2, tan_p, -pi/2 + Δ, pi/2 - Δ; label="pade approx")
|
||||
|
||||
plot(p1, p2)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
plot(tan, -pi/2 + 0.2, pi/2 - 0.2)
|
||||
plot!(tan_p, -pi/2 + 0.2, pi/2 - 0.2)
|
||||
```
|
||||
Small-degree Pade approximations for $\sin(x)$ and $\tan(x)$.
|
||||
:::
|
||||
|
||||
## The `Polynomials` package for rational functions
|
||||
|
||||
@@ -530,26 +574,32 @@ One difference is the rational number `3//1` also represents other expressions,
|
||||
Rational functions also have a plot recipe defined for them that attempts to ensure the basic features are identifiable. As previously discussed, a plot of a rational function can require some effort to avoid the values associated to vertical asymptotes taking up too many of the available vertical pixels in a graph.
|
||||
|
||||
|
||||
For the polynomial `pq` above, we have from observation that $1$ and $2$ will be zeros and $x=3$ a vertical asymptote. We also can identify a slant asymptote with slope $1$. These are hinted at in this graph:
|
||||
For the polynomial `pq` above, we have from observation that $1$ and $2$ will be zeros and $x=3$ a vertical asymptote. We also can identify a slant asymptote with slope $1$. These are hinted at in @fig-basic-plot-recipe-rational-function
|
||||
|
||||
|
||||
::: {#fig-basic-plot-recipe-rational-function}
|
||||
```{julia}
|
||||
plot(pq)
|
||||
```
|
||||
|
||||
Plot of rational function `pq` using the basic plot recipe. The vertical asymptote is clear, but the slant asymptote requires a trained eye.
|
||||
:::
|
||||
|
||||
To better see the zeros, a plot over a narrower interval, say $[0,2.5]$, would be encouraged; to better see the slant asymptote, a plot over a wider interval, say $[-10,10]$, would be encouraged.
|
||||
|
||||
|
||||
For one more example of the default plot recipe, we redo the graphing of the rational expression we earlier plotted with `rangeclamp`:
|
||||
|
||||
For one more example of the default plot recipe, we redo the graphing of the rational expression we earlier plotted with `rangeclamp` in @fig-plot-rational-function-with-asymptotes-at-minus3-3-from-pq-recipe.
|
||||
|
||||
::: {#fig-plot-rational-function-with-asymptotes-at-minus3-3-from-pq-recipe}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
p,q = fromroots([1,1,2]), fromroots([-3,3])
|
||||
plot(p//q)
|
||||
plot(p//q; legend=false)
|
||||
```
|
||||
A plot of a rational function with vertical asymptotes at $x= \pm 3$. The plot recipe avoids showing artifacts from the vertical asymptotes.
|
||||
:::
|
||||
|
||||
##### Example: transformations of polynomials; real roots
|
||||
##### Example: Transformations of polynomials; real roots
|
||||
|
||||
|
||||
We have seen some basic transformations of functions such as shifts and scales. For a polynomial expression we can implement these as follows, taking advantage of polynomial evaluation:
|
||||
@@ -633,7 +683,18 @@ numerator(lowest_terms( (x + 1)^d * pq((a*x + b)/(x + 1))))
|
||||
Now, why is this of any interest?
|
||||
|
||||
|
||||
Mobius transforms are used to map regions into other regions. In this special case, the transform $\phi(x) = (ax + b)/(x + 1)$ takes the interval $[0,\infty]$ and sends it to $[a,b]$ ($0$ goes to $(a\cdot 0 + b)/(0+1) = b$, whereas $\infty$ goes to $ax/x \rightarrow a$). Using this, if $p(u) = 0$, with $q(x) = (x+1)^d p(\phi(x))$, then setting $u = \phi(x)$ we have $q(x) = (\phi^{-1}(u)+1)^d p(\phi(\phi^{-1}(u))) = (\phi^{-1}(u)+1)^d \cdot p(u) = (\phi^{-1}(u)+1)^d \cdot 0 = 0$. That is, a zero of $p$ in $[a,b]$ will appear as a zero of $q$ in $[0,\infty)$ at $\phi^{-1}(u)$.
|
||||
Mobius transforms are used to map regions into other regions. In this special case, the transform $\phi(x) = (ax + b)/(x + 1)$ takes the interval $[0,\infty]$ and sends it to $[a,b]$ ($0$ goes to $(a\cdot 0 + b)/(0+1) = b$, whereas $\infty$ goes to $ax/x \rightarrow a$). Using this, if $p(u) = 0$, with $q(x) = (x+1)^d p(\phi(x))$, then setting $u = \phi(x)$ we have
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
q(x) &= (x+1)^d p(\phi(x))\\
|
||||
&= (\phi^{-1}(u)+1)^d p(\phi(\phi^{-1}(u))) \\
|
||||
& = (\phi^{-1}(u)+1)^d \cdot p(u) \\
|
||||
& = (\phi^{-1}(u)+1)^d \cdot 0 = 0.\\
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
That is, a zero of $p$ in $[a,b]$ will appear as a zero of $q$ in $[0,\infty)$ at $x = \phi^{-1}(u)$.
|
||||
|
||||
|
||||
The Descartes rule of signs applied to $q$ then will give a bound on the number of possible roots of $p$ in the interval $[a,b]$. In the example we did, the Mobius transform for $a=4, b=6$ is $15 - x - 11x^2 - 3x^3$ with $1$ sign change, so there must be exactly $1$ real root of $p=(x-1)(x-3)(x-5)$ in the interval $[4,6]$, as we can observe from the factored form of $p$.
|
||||
@@ -653,12 +714,14 @@ This observation, along with a detailed analysis provided by [Kobel, Rouillier,
|
||||
The basic algorithm, as presented next, is fairly simple to understand, and hints at the bisection algorithm to come. It is due to Akritas and Collins. Suppose you know the only possible positive real roots are between $0$ and $M$ *and* no roots are repeated. Find the transformed polynomial over $[0,M]$:
|
||||
|
||||
|
||||
* If there are no sign changes, then there are no roots of $p$ in $[0,M]$.
|
||||
* If there is one sign change, then there is a single root of $p$ in $[0,M]$. The interval $[0,M]$ is said to isolate the root (and the actual root can then be found by other means)
|
||||
* If there is more than one sign change, divide the interval in two ($[0,M/2]$ and $[M/2,M]$, say) and apply the same consideration to each.
|
||||
* If there are no sign changes, then there are no roots of $p$ in $[0,M]$.
|
||||
|
||||
* If there is one sign change, then there is a single root of $p$ in $[0,M]$. The interval $[0,M]$ is said to isolate the root (and the actual root can then be found by other means)
|
||||
|
||||
* If there is more than one sign change, divide the interval in two ($[0,M/2]$ and $[M/2,M]$, say) and apply the same consideration to each.
|
||||
|
||||
|
||||
Eventually, **mathematically** this will find isolating intervals for each positive real root. (The negative ones can be similarly isolated.)
|
||||
Eventually, *mathematically* this will find isolating intervals for each positive real root. (The negative ones can be similarly isolated.)
|
||||
|
||||
|
||||
Applying these steps to $p$ with an initial interval, say $[0,9]$, we would have:
|
||||
@@ -686,7 +749,7 @@ The `ANewDsc` function takes a collection of coefficients representing a polynom
|
||||
|
||||
|
||||
```{julia}
|
||||
p₀ = fromroots([1,3,5])
|
||||
p₀ = fromroots([1, 3, 5])
|
||||
st = ANewDsc(coeffs(p₀))
|
||||
```
|
||||
|
||||
@@ -701,7 +764,7 @@ More challenging problems can be readily handled by this package. The following
|
||||
|
||||
|
||||
```{julia}
|
||||
s = Polynomial([0,1]) # also just variable(Polynomial{Int})
|
||||
s = variable(:s)
|
||||
u = -1 + 254*s - 16129*s^2 + s^15
|
||||
```
|
||||
|
||||
@@ -719,6 +782,7 @@ and
|
||||
refine_roots(st)
|
||||
```
|
||||
|
||||
The `Hecke.jl` package has a `roots` function that handles the task of root isolation more performantly than `ANewDsc`.
|
||||
The SymPy package (`sympy.real_roots`) can accurately identify the three roots but it can take a **very** long time. The `Polynomials.roots` function from the `Polynomials` package identifies the cluster as complex valued. Though the implementation in `RealPolynomialRoots` doesn't handle such large polynomials, the authors of the algorithm have implementations that can quickly solve polynomials with degrees as high as $10,000$.
|
||||
|
||||
|
||||
@@ -737,8 +801,9 @@ The rational expression $(x^3 - 2x + 3) / (x^2 - x + 1)$ would have
|
||||
choices = [L"A horizontal asymptote $y=0$",
|
||||
L"A horizontal asymptote $y=1$",
|
||||
L"A slant asymptote with slope $m=1$"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
explanation = L"The ratio of the two leading terms is $x$"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -753,8 +818,9 @@ The rational expression $(x^2 - x + 1)/ (x^3 - 2x + 3)$ would have
|
||||
choices = [L"A horizontal asymptote $y=0$",
|
||||
L"A horizontal asymptote $y=1$",
|
||||
L"A slant asymptote with slope $m=1$"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
explanation = L"The ratio of the two leading terms is $1/x$"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -769,8 +835,9 @@ The rational expression $(x^2 - x + 1)/ (x^2 - 3x + 3)$ would have
|
||||
choices = [L"A horizontal asymptote $y=0$",
|
||||
L"A horizontal asymptote $y=1$",
|
||||
L"A slant asymptote with slope $m=1$"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
explanation = L"The ratio of the two leading terms can be seen to be $1$"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -792,8 +859,9 @@ would have
|
||||
choices = [L"A horizontal asymptote $y=0$",
|
||||
L"A horizontal asymptote $y=1$",
|
||||
L"A slant asymptote with slope $m=1$"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
explanation = L"the ratio of the leading terms is $1$"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -816,8 +884,9 @@ choices = [L"A vertical asymptote $x=1$",
|
||||
L"A slant asymptote with slope $m=1$",
|
||||
L"A vertical asymptote $x=5$"
|
||||
]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
explanation = L"the ratio of the leading terms is $1$, not $x$ and there are vertical asymptotes at $x=4,5,6$"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -841,8 +910,9 @@ choices = [
|
||||
"``y = (1/3)x``",
|
||||
"``y = (1/3)x - (1/3)``"
|
||||
]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
explanation = "Try `div(x^3 - 3x^2+ 2x, 3x^2 - 6x +2)`"
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -871,8 +941,8 @@ Is the following common conception true: "The graph of a function never crosses
|
||||
#| echo: false
|
||||
choices = ["No, the graph clearly crosses the drawn asymptote",
|
||||
"Yes, this is true"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
(The wikipedia page indicates that the term "asymptote" was introduced by Apollonius of Perga in his work on conic sections, but in contrast to its modern meaning, he used it to mean any line that does not intersect the given curve. It can sometimes take a while to change perception.)
|
||||
@@ -883,18 +953,17 @@ radioq(choices, answ)
|
||||
|
||||
Consider the two graphs of $f(x) = 1/x$ over $[10,20]$ and $[100, 200]$:
|
||||
|
||||
|
||||
::: {#fig-plot-1-over-x-10-to-20-100-to-200}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
plot(x -> 1/x, 10, 20)
|
||||
p1 = plot(x -> 1/x, 10, 20)
|
||||
p2 = plot(x -> 1/x, 100, 200)
|
||||
plot(p1, p2)
|
||||
```
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
plot(x -> 1/x, 100, 200)
|
||||
```
|
||||
The graph of $f(x) = 1/x$ over different viewing windows
|
||||
:::
|
||||
|
||||
The two shapes are basically identical and do not look like straight lines. How does this reconcile with the fact that $f(x)=1/x$ has a horizontal asymptote $y=0$?
|
||||
|
||||
@@ -906,8 +975,8 @@ choices = ["The horizontal asymptote is not a straight line.",
|
||||
L"The $y$-axis scale shows that indeed the $y$ values are getting close to $0$.",
|
||||
L"The graph is always decreasing, hence it will eventually reach $-\infty$."
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -955,8 +1024,8 @@ choices = ["between ``0`` and ``8`` hours",
|
||||
"between ``8`` and ``16`` hours",
|
||||
"between ``16`` and ``24`` hours",
|
||||
"after one day"]
|
||||
answ = 1
|
||||
radioq(choices, answ)
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
This graph has
|
||||
@@ -969,8 +1038,8 @@ choices = [L"a slant asymptote with slope $50$",
|
||||
L"a horizontal asymptote $y=20$",
|
||||
L"a horizontal asymptote $y=0$",
|
||||
L"a vertical asymptote with $x = 20^{1/3}$"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -996,6 +1065,6 @@ L"The $\sin(x)$ oscillates, but the rational function eventually follows $7/60 \
|
||||
L"The $\sin(x)$ oscillates, but the rational function has a slant asymptote",
|
||||
L"The $\sin(x)$ oscillates, but the rational function has a non-zero horizontal asymptote",
|
||||
L"The $\sin(x)$ oscillates, but the rational function has a horizontal asymptote of $0$"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
+234
-160
@@ -1,22 +1,14 @@
|
||||
# Function manipulations
|
||||
# Function transformations
|
||||
|
||||
|
||||
{{< include ../_common_code.qmd >}}
|
||||
|
||||
In this section we will use these add-on packages:
|
||||
In this section we will use this add-on package:
|
||||
|
||||
|
||||
```{julia}
|
||||
using CalculusWithJulia
|
||||
using Plots
|
||||
plotly()
|
||||
```
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
#| results: "hidden"
|
||||
using DataFrames
|
||||
nothing
|
||||
plotly();
|
||||
```
|
||||
|
||||
---
|
||||
@@ -38,25 +30,21 @@ $$
|
||||
We have given meaning to a new function $f+g$ by defining what is does to $x$ with the rule on the right hand side. Similarly, we can define operations for subtraction, multiplication, addition, and powers.
|
||||
|
||||
|
||||
These mathematical concepts aren't defined for functions in base `Julia`, though they could be if desired, by a commands such as:
|
||||
|
||||
These mathematical concepts aren't defined for function instances in base `Julia`. For example, a common mistake in computing $\sin^2(1)$ would be to try that syntax exactly:
|
||||
|
||||
```{julia}
|
||||
import Base: +
|
||||
f::Function + g::Function = x -> f(x) + g(x)
|
||||
#| error: true
|
||||
sin^2(1)
|
||||
```
|
||||
|
||||
This adds a method to the generic `+` function for functions. The type annotations `::Function` ensure this applies only to functions. To see that it would work, we could do odd-looking things like:
|
||||
The error message might seem cryptic but it comes from there not being a power operation (`^`) for `sin` defined. Similarly, out of the box there are not operations for `+`, `-`, `*`, or `/` defined for functions.^[There *could* be such operations defined. For example, if `+` is imported from `Base`, then this command `f::Function + g::Function = x -> f(x) + g(x)` would define such a operation. However, this definition in general is kind of limiting, as functions in mathematics and Julia can be much more varied than just the univariate functions we have defined addition for. Further, users shouldn't be modifying base methods on types they don't control, as that can lead to really unexpected and undesirable behaviours. This is called *type piracy*.]
|
||||
|
||||
To define such a function, we would specify what it does to each value of `x`, along the lines of:
|
||||
|
||||
```{julia}
|
||||
ss = sin + sqrt
|
||||
ss(4)
|
||||
fplusg(x) = f(x) + g(x)
|
||||
```
|
||||
|
||||
Doing this works, as Julia treats functions as first class objects, lending itself to [higher](https://en.wikipedia.org/wiki/Higher-order_programming) order programming. However, this definition in general is kind of limiting, as functions in mathematics and Julia can be much more varied than just the univariate functions we have defined addition for. Further, users shouldn't be modifying base methods on types they don't control, as that can lead to really unexpected and undesirable behaviours. This is called *type piracy*. We won't pursue this possibility further. Rather we will define new function by what they do to their values, such as `h(x) = f(x) + g(x)`.
|
||||
|
||||
|
||||
### Composition of functions
|
||||
|
||||
|
||||
@@ -82,26 +70,29 @@ $$
|
||||
|
||||
Though they may be *typographically* similar don't be fooled, the following graph shows that the two functions aren't even close except for $x$ near $0$ (for example, one composition is always non-negative, whereas the other is not):
|
||||
|
||||
|
||||
::: {#fig-plot-sin-xsquared-sinsquared-x}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x^2
|
||||
g(x) = sin(x)
|
||||
fg = f ∘ g # typed as f \circ[tab] g
|
||||
gf = g ∘ f # typed as g \circ[tab] f
|
||||
fg(x) = f(g(x))
|
||||
gf(x) = g(f(x))
|
||||
plot(fg, -2, 2, label="f∘g")
|
||||
plot!(gf, label="g∘f")
|
||||
```
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
Unlike how the basic arithmetic operations are treated, `Julia` defines the infix Unicode operator `\circ[tab]` to represent composition of functions, mirroring mathematical notation. This infix operations takes in two functions and returns a composed function. It can be useful and will mirror standard mathematical usage up to issues with precedence rules.
|
||||
|
||||
Plot of $f(x) = \sin^2(x)$ and $g(x) = \sin(x^2)$, both written as compositions, illustrating the order of composition is important
|
||||
:::
|
||||
|
||||
Starting with two functions and composing them requires nothing more than a solid grasp of knowing the rules of function evaluation. If $f(x)$ is defined by some rule involving $x$, then $f(g(x))$ just replaces each $x$ in the rule with a $g(x)$.
|
||||
|
||||
|
||||
::: {.callout-note}
|
||||
## Infix operator
|
||||
Composition of two functions does have an infix operator, `∘`, entered as `\circ[tab]`. This mirrors the mathematical usage of this syntax, though the order of operations are such that calling the composed function on a value requires an extra set of parentheses: `(f∘g)(x)`, as the expresssion `f∘g(x)` evaluates `g(x)` before the composition.
|
||||
:::
|
||||
|
||||
|
||||
So if $f(x) = x^2 + 2x - 1$ and $g(x) = e^x - x$ then $f \circ g$ would be (before any simplification)
|
||||
|
||||
|
||||
@@ -123,10 +114,11 @@ $$
|
||||
|
||||
Here we look at a few compositions:
|
||||
|
||||
* The function $h(x) = \sqrt{1 - x^2}$ can be seen as $f\circ g$ with $f(x) = \sqrt{x}$ and $g(x) = 1-x^2$.
|
||||
|
||||
* The function $h(x) = \sqrt{1 - x^2}$ can be seen as $f\circ g$ with $f(x) = \sqrt{x}$ and $g(x) = 1-x^2$.
|
||||
* The function $h(x) = \sin(x/3 + x^2)$ can be viewed as $f\circ g$ with $f(x) = \sin(x)$ and $g(x) = x/3 + x^2$.
|
||||
* The function $h(x) = e^{-1/2 \cdot x^2}$ can be viewed as $f\circ g$ with $f(x) = e^{-x}$ and $g(x) = (1/2) \cdot x^2$.
|
||||
* The function $h(x) = \sin(x/3 + x^2)$ can be viewed as $f\circ g$ with $f(x) = \sin(x)$ and $g(x) = x/3 + x^2$.
|
||||
|
||||
* The function $h(x) = e^{-1/2 \cdot x^2}$ can be viewed as $f\circ g$ with $f(x) = e^{-x}$ and $g(x) = (1/2) \cdot x^2$.
|
||||
|
||||
|
||||
Decomposing a function into a composition of functions is not unique, other compositions could have been given above. For example, the last function is also $f(x) = e^{-x/2}$ composed with $g(x) = x^2$.
|
||||
@@ -144,19 +136,19 @@ The real value of composition is to break down more complicated things into a se
|
||||
It is very useful to mentally categorize functions within families. The difference between $f(x) = \cos(x)$ and $g(x) = 12\cos(2(x - \pi/4))$ is not that much---both are cosine functions, one is just a simple enough transformation of the other. As such, we expect bounded, oscillatory behaviour with the details of how large and how fast the oscillations are to depend on the specifics of the function. Similarly, both these functions $f(x) = 2^x$ and $g(x)=e^x$ behave like exponential growth, the difference being only in the rate of growth. There are families of functions that are qualitatively similar, but quantitatively different, linked together by a few basic transformations.
|
||||
|
||||
|
||||
There is a set of operations of functions, which does not really change the type of function. Rather, it basically moves and stretches how the functions are graphed. We discuss these four main transformations of $f$:
|
||||
There is a set of operations of functions, which does not really change the type of function. Rather, it basically moves and stretches how the functions are graphed. We discuss the four main transformations of $f$ from @tbl-four-main-transformations.
|
||||
|
||||
::: {#tbl-four-main-transformations .striped .hover tbl-colwidths="[25,75]"}
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
| Transformation | Description |
|
||||
|:--------------------|:---------------|
|
||||
| *vertical shifts* | The function $h(x) = k + f(x)$ will have the same graph as $f$ shifted **up** $k$ units.|
|
||||
| *horizontal shifts* | The function $h(x) = f(x - k)$ will have the same graph as $f$ shifted **over** right by $k$ units. |
|
||||
| *stretching* | The function $h(x) = kf(x)$ will have the same graph as $f$ **stretch**ed by a factor $k$ of in the $y$ direction. |
|
||||
| *scaling* |The function $h(x) = f(kx)$ will have the same graph as $f$ **scale**d horizontally by a factor of $1$ over $k$ |
|
||||
|
||||
nms = ["*vertical shifts*","*horizontal shifts*","*stretching*","*scaling*"]
|
||||
acts = [L"The function $h(x) = k + f(x)$ will have the same graph as $f$ shifted up by $k$ units.",
|
||||
L"The function $h(x) = f(x - k)$ will have the same graph as $f$ shifted right by $k$ units.",
|
||||
L"The function $h(x) = kf(x)$ will have the same graph as $f$ stretched by a factor of $k$ in the $y$ direction.",
|
||||
L"The function $h(x) = f(kx)$ will have the same graph as $f$ compressed horizontally by a factor of $1$ over $k$."]
|
||||
table(DataFrame(Transformation=nms, Description=acts))
|
||||
```
|
||||
: Four main transformations of a function.
|
||||
:::
|
||||
|
||||
The functions $h$ are derived from $f$ in a predictable way. To implement these transformations within `Julia`, we define operators (functions which transform one function into another). As these return functions, the function bodies are anonymous functions. The basic definitions are similar, save for the `x -> ...` part that signals the creation of an anonymous function to return:
|
||||
|
||||
@@ -175,44 +167,58 @@ To illustrate, let's define a hat-shaped function as follows:
|
||||
f(x) = max(0, 1 - abs(x))
|
||||
```
|
||||
|
||||
A plot over the interval $[-2,2]$ is shown here:
|
||||
A plot over the interval $[-2,2]$ is shown in @fig-plot-hat-function:
|
||||
|
||||
|
||||
::: {#fig-plot-hat-function}
|
||||
```{julia}
|
||||
plot(f, -2,2)
|
||||
plot(f, -2, 2; aspect_ratio=:equal)
|
||||
```
|
||||
Plot of $f(x) = \max(0, 1 - \lvert x\rvert)$ over $[-2,2]$
|
||||
:::
|
||||
|
||||
The same graph of $f$ and its image shifted up by $2$ units would be given by:
|
||||
|
||||
|
||||
::: {#fig-plot-hat-function-also-up-2}
|
||||
```{julia}
|
||||
plot(f, -2, 2, label="f")
|
||||
plot!(up(f, 2), label="up")
|
||||
plot(f, -2, 2; aspect_ratio=:equal, label="f")
|
||||
plot!(up(f, 2); label="up")
|
||||
```
|
||||
|
||||
A graph of $f$ and its shift over by $2$ units would be given by:
|
||||
Plot of $f(x) = \max(0, 1 - \lvert x\rvert)$ and its transformation shifted up by $2$ units
|
||||
:::
|
||||
|
||||
A graph of $f$ and its shift over by $2$ units would be generated by:
|
||||
|
||||
::: {#fig-plot-hat-function-shifted-over-2}
|
||||
```{julia}
|
||||
plot(f, -2, 4, label="f")
|
||||
plot!(over(f, 2), label="over")
|
||||
plot(f, -2, 4; aspect_ratio=:equal, label="f")
|
||||
plot!(over(f, 2); label="over")
|
||||
```
|
||||
Plot of $f(x) = \max(0, 1 - \lvert x\rvert)$ and its transformation shifted over by $2$ units
|
||||
:::
|
||||
|
||||
A graph of $f$ and it being stretched by $2$ units would be generated by:
|
||||
|
||||
::: {#fig-plot-hat-function-stretched-by-2}
|
||||
```{julia}
|
||||
plot(f, -2, 2; aspect_ratio=:equal, label="f")
|
||||
plot!(stretch(f, 2); label="stretch")
|
||||
```
|
||||
|
||||
A graph of $f$ and it being stretched by $2$ units would be given by:
|
||||
Plot of $f(x) = \max(0, 1 - \lvert x\rvert)$ and its transformation stretched in #y# direction by $2$ units
|
||||
:::
|
||||
|
||||
|
||||
Finally, a graph of $f$ and it being scaled by $2$ would be generated by:
|
||||
|
||||
::: {{{{{{{#fig-plot-hat-function-scaled-by-2}
|
||||
```{julia}
|
||||
plot(f, -2, 2, label="f")
|
||||
plot!(stretch(f, 2), label="stretch")
|
||||
```
|
||||
|
||||
Finally, a graph of $f$ and it being scaled by $2$ would be given by:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(f, -2, 2, label="f")
|
||||
plot!(scale(f, 2), label="scale")
|
||||
plot(f, -2, 2; aspect_ratio=:equal, label="f")
|
||||
plot!(scale(f, 2); label="scale")
|
||||
```
|
||||
Plot of $f(x) = \max(0, 1 - \lvert x\rvert)$ and its transformation scaled in $x$ by $2$ units
|
||||
:::
|
||||
|
||||
Scaling by $2$ shrinks the non-zero domain, scaling by $1/2$ would stretch it. If this is not intuitive, the definition `x-> f(x/c)` could have been used, which would have opposite behaviour for scaling.
|
||||
|
||||
@@ -220,37 +226,69 @@ Scaling by $2$ shrinks the non-zero domain, scaling by $1/2$ would stretch it. I
|
||||
---
|
||||
|
||||
|
||||
More exciting is what happens if we compose these operations.
|
||||
|
||||
|
||||
A shift right by $2$ and up by $1$ is achieved through
|
||||
|
||||
More exciting is what happens if we compose these operations. Before doing so, let's note how the syntax of composition would be:
|
||||
|
||||
```{julia}
|
||||
plot(f, -2, 4, label="f")
|
||||
plot!(up(over(f,2), 1), label="over and up")
|
||||
#| eval: false
|
||||
up(over(f, 2), 1)
|
||||
```
|
||||
|
||||
Shifting and scaling can be confusing. Here we graph `scale(over(f,2),1/3)`:
|
||||
|
||||
The `1` is the value for `up` and can get lost without careful parsing. It might be better to see something like this instead:
|
||||
|
||||
```{julia}
|
||||
plot(f, -1,9, label="f")
|
||||
plot!(scale(over(f,2), 1/3), label="over and scale")
|
||||
#| eval: false
|
||||
f |> over(2) |> up(1)
|
||||
```
|
||||
|
||||
This graph is over by $6$ with a width of $3$ on each side of the center. Mathematically, we have $h(x) = f((1/3)\cdot x - 2)$
|
||||
Meaning, take `f`, shift it over, `2` and then up `1`. To make this happen, we need to pass in a function and return a function from both `over` and `up`. We define another set of functions using multiple dispatch to sort out which is which:
|
||||
|
||||
```{julia}
|
||||
up(k) = f -> up(f, k)
|
||||
over(k) = f -> over(f, k)
|
||||
stretch(k) = Base.Fix2(stretch, k)
|
||||
scale(k) = Base.Fix2(scale, k)
|
||||
```
|
||||
|
||||
We did this two ways, the last two using `Fix2` to fix the second argument, leaving a function of just the first argument.
|
||||
|
||||
|
||||
Now, a shift right by $2$ and up by $1$ is achieved through
|
||||
|
||||
::: {#fig-plot-hat-over-up-composed-call}
|
||||
```{julia}
|
||||
plot(f, -2, 4; aspect_ratio=:equal, label="f")
|
||||
plot!(f |> over(2) |> up(1); label="over and up")
|
||||
```
|
||||
|
||||
Plot of $f(x) = \max(0, 1 - \lvert x\rvert)$ and it transform after moving over by $2$ and up by $1$ using chaining notation
|
||||
:::
|
||||
|
||||
Shifting and scaling can be confusing. Here we graph the action of `over` by $2$ and then `scale` by $1/3$:
|
||||
|
||||
::: {#fig-plot-hat-over-scale-composed}
|
||||
```{julia}
|
||||
plot(f, -1, 9; aspect_ratio=:equal, label="f")
|
||||
plot!(f |> over(2) |> scale(1/3); label="over and scale")
|
||||
```
|
||||
|
||||
Plot of $f(x)$ and it transform being over $2$ and then scaled by $1/3$
|
||||
:::
|
||||
|
||||
@fig-plot-hat-over-scale-composed show $f(x)$ after being moved over by $2$ and *then$ scaled by $1/3$. It's center moves to $6$ and instead of stretching from $6-1$ to $6+1$ it stretches from $6-3$ to $6+3$. Mathematically, we have $h(x) = f((1/3)\cdot x - 2)$
|
||||
|
||||
|
||||
Compare this to the same operations in opposite order:
|
||||
|
||||
|
||||
::: {#fig-plot-hat-scale-over-composed}
|
||||
```{julia}
|
||||
plot(f, -1, 5, label="f")
|
||||
plot!(over(scale(f, 1/3), 2), label="scale and over")
|
||||
plot(f, -1, 5; aspect_ratio=:equal, label="f")
|
||||
plot!(f |> scale(1/3) |> over(2); label="scale and over")
|
||||
```
|
||||
|
||||
This graph first scales the symmetric graph, stretching from $-3$ to $3$, then shifts over right by $2$. The resulting function is $f((1/3)\cdot (x-2))$.
|
||||
Plot of $f(x)$ and it transform being first scaled by $1/3$ and then shifted over $2$
|
||||
:::
|
||||
|
||||
@fig-plot-hat-scale-over-composed shows the transform of $f(x)$ first scaled, stretching from $-3$ to $3$, then shifted over right by $2$. The resulting function is $f((1/3)\cdot (x-2))$.
|
||||
|
||||
|
||||
As a last example, following up on the last example, a common transformation mathematically is
|
||||
@@ -260,19 +298,36 @@ $$
|
||||
h(x) = \frac{1}{a}f(\frac{x - b}{a}).
|
||||
$$
|
||||
|
||||
We can view this as a composition of "scale" by $1/a$, then "over" by $b$, and finally "stretch" by $1/a$:
|
||||
|
||||
We can view this as a composition of "scale" by $1/a$, then "over" by $b$, and finally "stretch" by $1/a$, as seen in @fig-plot-hat-function-wavelet-transform:
|
||||
|
||||
::: {#fig-plot-hat-function-wavelet-transform}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
a = 2; b = 5
|
||||
h(x) = stretch(over(scale(f, 1/a), b), 1/a)(x)
|
||||
plot(f, -1, 8, label="f"; xticks=-1:8)
|
||||
plot!(h, label="h")
|
||||
plot(f, -1, 8; aspect_ratio=:equal, label="f", xticks=-1:8)
|
||||
plot!(f |> scale(1/a) |> over(b) |> stretch(1/a); label="h")
|
||||
```
|
||||
|
||||
(This transformation keeps the same amount of area in the triangles, can you tell from the graph?)
|
||||
Plot showing transform $\frac{1}{a}f(\frac{x - b}{a})$
|
||||
:::
|
||||
|
||||
This transformation keeps the same amount of area in the triangles, can you tell from the graph?
|
||||
|
||||
##### Example: a growth model in fisheries
|
||||
|
||||
|
||||
The von Bertalanffy growth [equation](https://en.wikipedia.org/wiki/Von_Bertalanffy_function) is $L(t) =L_\infty \cdot (1 - e^{-k\cdot(t-t_0)})$. This family of functions can be viewed as a transformation of the exponential function $f(t)=e^t$. Part is a scaling and shifting (the $e^{-k \cdot (t - t_0)}$) along with some shifting and stretching. The various parameters have physical importance which can be measured: $L_\infty$ is a carrying capacity for the species or organism, and $k$ is a rate of growth. These parameters may be estimated from data by finding the "closest" curve to a given data set. For this set of parameters^[From [Campbell and Phillips](https://doi.org/10.1093/icesjms/34.2.295) shows steady but decelerating growth of a whelk population.]
|
||||
|
||||
::: {#fig-von-Bertalanffy-plot}
|
||||
```{julia}
|
||||
L, k = 53.1120, 0.0335 # from https://doi.org/10.1093/icesjms/34.2.295
|
||||
t0 = 1968
|
||||
u = exp |> scale(-k) |> over(t0) |> stretch(-1) |> up(1) |> stretch(L)
|
||||
plot(u, t0, t0+200; legend=false)
|
||||
```
|
||||
|
||||
Plot of von-Bertalanffy model applied to a whelk population
|
||||
:::
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -308,12 +363,15 @@ c = 80
|
||||
|
||||
Putting this together, we have our graph is "scaled" by $d$, "over" by $c$, "stretched" by $b$ and "up" by $a$. Here we plot it over slightly more than one year so that we can see that the shortest day of light is in late December ($x \approx -10$ or $x \approx 355$).
|
||||
|
||||
|
||||
::: {#fig-plot-newyork-daylight-sinusoidal-model}
|
||||
```{julia}
|
||||
newyork(t) = up(stretch(over(scale(sin, d), c), b), a)(t)
|
||||
newyork = sin |> scale(d) |> over(c) |> stretch(b) |> up(a)
|
||||
plot(newyork, -20, 385)
|
||||
```
|
||||
|
||||
Plot of a sinusoidal model for length of daylight in New York City
|
||||
:::
|
||||
|
||||
To test, if we match up with the model powering [dateandtime.info](http://dateandtime.info/citysunrisesunset.php?id=5128581) we note that it predicts "$12$h $10$m $38$s" on September $23$th, $2015$. This is day $266$ (`Date(2015, 9, 23) - Date(2015, 1, 1) + Day(1)`). Our model prediction has a difference of
|
||||
|
||||
|
||||
@@ -325,43 +383,28 @@ delta = (newyork(266) - datetime) * 60
|
||||
This is off by a fair amount---almost $8$ minutes. Clearly a trigonometric model, based on the assumption of circular motion of the earth around the sun, is not accurate enough for precise work, but it does help one understand how summer days are longer than winter days and how the length of a day changes fastest at the spring and fall equinoxes.
|
||||
|
||||
|
||||
##### Example: the pipeline operator
|
||||
|
||||
##### Example: Representing data visually
|
||||
|
||||
In the last example, we described our sequence as scale, over, stretch, and up, but code this in reverse order, as the composition $f \circ g$ is done from right to left. A more convenient notation would be to have syntax that allows the composition of $g$ then $f$ to be written $x \rightarrow g \rightarrow f$. `Julia` provides the [pipeline](https://docs.julialang.org/en/v1/manual/functions/#Function-composition-and-piping) operator for chaining function calls together.
|
||||
The `Plots.jl` package also uses transformations to display different shapes on a graphic. This next example shows how scale can be used to display a third piece of information to augment the two pieces shown through location.
|
||||
|
||||
Suppose we have a data set like @tbl-palmer-penguins-data:^[Which comes from the "Palmer Penguins" data set]
|
||||
|
||||
For example, if $g(x) = \sqrt{x}$ and $f(x) =\sin(x)$ we could call $f(g(x))$ through:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
g(x) = sqrt(x)
|
||||
f(x) = sin(x)
|
||||
pi/2 |> g |> f
|
||||
```
|
||||
|
||||
The output of the preceding expression is passed as the input to the next. This notation is especially convenient when the enclosing function is not the main focus. (Some programming languages have more developed [fluent interfaces](https://en.wikipedia.org/wiki/Fluent_interface) for chaining function calls. Julia has more powerful chaining macros provided in packages, such as `DataPipes.jl` or `Chain.jl`.)
|
||||
|
||||
##### Example: a growth model in fisheries
|
||||
|
||||
|
||||
The von Bertalanffy growth [equation](https://en.wikipedia.org/wiki/Von_Bertalanffy_function) is $L(t) =L_\infty \cdot (1 - e^{k\cdot(t-t_0)})$. This family of functions can be viewed as a transformation of the exponential function $f(t)=e^t$. Part is a scaling and shifting (the $e^{k \cdot (t - t_0)}$) along with some shifting and stretching. The various parameters have physical importance which can be measured: $L_\infty$ is a carrying capacity for the species or organism, and $k$ is a rate of growth. These parameters may be estimated from data by finding the "closest" curve to a given data set.
|
||||
|
||||
##### Example: Representing data visually.
|
||||
|
||||
Suppose we have a data set like the following:^[Which comes from the "Palmer Penguins" data set]
|
||||
|
||||
::: {#tbl-palmer-penguins-data .striped .hover}
|
||||
|flipper length | bill length | body mass | gender | species |
|
||||
|---------------|-------------|-----------|--------|:--------|
|
||||
|:--------------|:------------|-----------|--------|:--------|
|
||||
| 38.8 | 18.3 | 3701 | male | Adelie |
|
||||
| 48.8 | 18.4 | 3733 | male | Chinstrap |
|
||||
| 47.5 | 15.0 | 5076 | male | Gentoo |
|
||||
|
||||
We might want to plot on an $x$-$y$ axis flipper length versus bill length but also indicate body size with a large size marker for bigger sizes.
|
||||
: Data set on sampled penguin attributes
|
||||
:::
|
||||
|
||||
We could do so by transforming a marker: scaling by size, then shifting it to an `x-y` position; then plotting. Something like this:
|
||||
We might want to plot on an $x$-$y$ axis flipper length versus bill length but also indicate body size with a larger-sized marker for bigger sizes.
|
||||
|
||||
We could do so by transforming a marker: scaling by size, then shifting it to an `x-y` position; then plotting. @fig-plot-palmer-penguin-shapes illustrates.
|
||||
|
||||
::: {#fig-plot-palmer-penguin-shapes}
|
||||
```{julia}
|
||||
flipper = [38.8, 48.8, 47.5]
|
||||
bill = [18.3, 18.4, 15.0]
|
||||
@@ -369,7 +412,7 @@ bodymass = [3701, 4733, 5076]
|
||||
|
||||
shape = Shape(:star5)
|
||||
p = plot(; legend=false)
|
||||
for (x,y,sz) in zip(flipper, bill, bodymass)
|
||||
for (x, y, sz) in zip(flipper, bill, bodymass)
|
||||
sz = (sz - 2000) ÷ 1000
|
||||
|
||||
new_shape = Plots.translate(Plots.scale(shape, sz, sz), x, y);
|
||||
@@ -379,36 +422,18 @@ end
|
||||
p
|
||||
```
|
||||
|
||||
Plot of penguin data using size of marker to represent flipper length
|
||||
:::
|
||||
|
||||
While some of the commands in this example are unfamiliar and won't be explained further, the use of `translate` and `scale` for shapes is very similar to how transformations for functions are being described (Though this `translate` function combines `up` and `over`; and this `scale` function allows different values depending on direction.) In the above, the function names are qualified, as they are not exported by the `Plots.jl` package. More variables from the data set could be encoded through colors, different shapes etc. allowing very data-rich graphics.
|
||||
|
||||
|
||||
### Operators
|
||||
|
||||
|
||||
The functions `up`, `over`, etc. are operators that take a function as an argument and return a function. The use of operators fits in with the template `action(f, args...)`. The action is what we are doing, such as `plot`, `over`, and others to come. The function `f` here is just an object that we are performing the action on. For example, a plot takes a function and renders a graph using the additional arguments to select the domain to view, etc.
|
||||
In computer science a *higher order function* is one that either takes a function as input, returns a function as its output, or both. We prefer the less standard, but more mathematical sounding *operator* to describe higher-order functions like `up` and `over`. The use of operators fits in with the template `action(f, args...)`. The action is what we are doing, such as `plot`, `over`, and others to come. The function `f` here is just an object that we are performing the action on. For example, a plot takes a function and renders a graph using the additional arguments to select the domain to view, etc.
|
||||
|
||||
|
||||
Creating operators that return functions involves the use of anonymous functions, using these operators is relatively straightforward. Two basic patterns are
|
||||
|
||||
|
||||
* Storing the returned function, then calling it:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
l(x) = action1(f, args...)(x)
|
||||
l(10)
|
||||
```
|
||||
|
||||
* Composing two operators:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| eval: false
|
||||
action2( action1(f, args..), other_args...)
|
||||
```
|
||||
|
||||
Composition like the above is convenient, but can get confusing if more than one composition is involved.
|
||||
Creating operators that return functions typically involves the use of anonymous functions, using these operators is relatively straightforward. In this next example, we create two new operators that will be prototypes for two foundational operator in calculus.
|
||||
|
||||
|
||||
##### Example: two operators
|
||||
@@ -420,22 +445,31 @@ Composition like the above is convenient, but can get confusing if more than one
|
||||
```{julia}
|
||||
D(f::Function) = k -> f(k) - f(k-1)
|
||||
```
|
||||
As written, `D` takes a function, `f`, and then returns a function that finds the difference of `f` at `k` and `k-1`.
|
||||
|
||||
To see that it works, we take a typical function
|
||||
To see how `D` works, we take a typical function:
|
||||
|
||||
|
||||
```{julia}
|
||||
f(k) = 1 + k^2
|
||||
```
|
||||
|
||||
and check:
|
||||
|
||||
Then we have:
|
||||
|
||||
```{julia}
|
||||
D(f)(3), f(3) - f(3-1)
|
||||
```
|
||||
|
||||
That the two are the same value is no coincidence. (Again, pause for a second to make sure you understand why `D(f)(3)` makes sense. If this is unclear, you could name the function `D(f)` and then call this with a value of `3`.)
|
||||
That the two are the same value is by design.
|
||||
|
||||
The calling syntax `D(f)(3)` is a bit awkward and is read as two steps: the first finds `D(f)` the second calls this function at `3`. The first could have been stored and then called, as with:
|
||||
|
||||
```{julia}
|
||||
df = D(f)
|
||||
df(3)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Now we want a function to cumulatively *sum* the values $S(f)(k) = f(1) + f(2) + \cdots + f(k-1) + f(k)$, as a function of $k$. Adding up $k$ terms is easy to do with a generator and the function `sum`:
|
||||
@@ -457,7 +491,8 @@ So one function adds, the other subtracts. Addition and subtraction are somehow
|
||||
|
||||
```{julia}
|
||||
k = 10 # some arbitrary value k >= 1
|
||||
D(S(f))(k), f(k)
|
||||
λ = D(S(f))
|
||||
λ(k), f(k)
|
||||
```
|
||||
|
||||
Any positive integer value of `k` will give the same answer (up to overflow). This says the difference of the accumulation process is just the last value to accumulate.
|
||||
@@ -467,10 +502,11 @@ Adding after subtracting also leaves the function alone, save for a vestige of $
|
||||
|
||||
|
||||
```{julia}
|
||||
S(D(f))(15), f(15) - f(0)
|
||||
γ = S(D(f))
|
||||
γ(15), f(15) - f(0)
|
||||
```
|
||||
|
||||
That is the accumulation of differences is just the difference of the end values.
|
||||
That is, the accumulation of differences is just the difference of the end values.
|
||||
|
||||
|
||||
These two operations are discrete versions of the two main operations of calculus---the derivative and the integral. This relationship will be known as the "fundamental theorem of calculus."
|
||||
@@ -489,8 +525,8 @@ If $f(x) = 1/x$ and $g(x) = x-2$, what is $g(f(x))$?
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices=["``1/(x-2)``", "``1/x - 2``", "``x - 2``", "``-2``"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -504,8 +540,8 @@ If $f(x) = e^{-x}$ and $g(x) = x^2$ and $h(x) = x-3$, what is $f \circ g \circ h
|
||||
#| echo: false
|
||||
choices=["``e^{-x^2 - 3}``", "``(e^x -3)^2``",
|
||||
"``e^{-(x-3)^2}``", "``e^x+x^2+x-3``"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -520,8 +556,8 @@ If $h(x) = (f \circ g)(x) = \sin^2(x)$ which is a possibility for $f$ and $g$:
|
||||
choices = [raw"``f(x)=x^2; \quad g(x) = \sin^2(x)``",
|
||||
raw"``f(x)=x^2; \quad g(x) = \sin(x)``",
|
||||
raw"``f(x)=\sin(x); \quad g(x) = x^2``"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -538,7 +574,7 @@ choices = [
|
||||
raw"``h(x) = 6 + \sin(x + 4)``",
|
||||
raw"``h(x) = 6 + \sin(x-4)``",
|
||||
raw"``h(x) = 6\sin(x-4)``"]
|
||||
answ = 3
|
||||
answer = 3
|
||||
radioq(choices, 3)
|
||||
```
|
||||
|
||||
@@ -554,10 +590,46 @@ Let $h(x) = 4x^2$ and $f(x) = x^2$. Which is **not** true:
|
||||
choices = [L"The graph of $h(x)$ is the graph of $f(x)$ stretched by a factor of ``4``",
|
||||
L"The graph of $h(x)$ is the graph of $f(x)$ scaled by a factor of ``2``",
|
||||
L"The graph of $h(x)$ is the graph of $f(x)$ shifted up by ``4`` units"]
|
||||
answ = 3
|
||||
radioq(choices, answ)
|
||||
answer = 3
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
|
||||
Consider the function
|
||||
|
||||
$$
|
||||
g(x) = f(\frac{x - a}{b})
|
||||
$$
|
||||
|
||||
This is
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
choices = ["`over` by `a` and then `scale` by `1/b`",
|
||||
"`scale` by `1/b` and then `over` by `a`"]
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
Consider the function
|
||||
|
||||
$$
|
||||
g(x) = f(\frac{x}{b} - a)
|
||||
$$
|
||||
|
||||
This is
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
choices = ["`over` by `a` and then `scale` by `1/b`",
|
||||
"`scale` by `1/b` and then `over` by `a`"]
|
||||
answer = 1
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
|
||||
|
||||
###### Question
|
||||
|
||||
|
||||
@@ -579,15 +651,17 @@ radioq(choices, answ)
|
||||
|
||||
This is the graph of a transformed sine curve.
|
||||
|
||||
|
||||
::: {#fig-transformed-sine-graph-question}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
f(x) = 2*sin(pi*x)
|
||||
p = plot(f, -2,2)
|
||||
```
|
||||
Plot of a transformation of the sine function
|
||||
:::
|
||||
|
||||
What is the period of the graph?
|
||||
What is the period of the graph in @fig-transformed-sine-graph-question?
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -597,7 +671,7 @@ val = 2
|
||||
numericq(val)
|
||||
```
|
||||
|
||||
What is the amplitude of the graph?
|
||||
What is the amplitude of the graph in @fig-transformed-sine-graph-question?
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -607,7 +681,7 @@ val = 2
|
||||
numericq(val)
|
||||
```
|
||||
|
||||
What is the form of the function graphed?
|
||||
What is the form of the function graphed in @fig-transformed-sine-graph-question?
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -619,8 +693,8 @@ raw"``\sin(2x)``",
|
||||
raw"``\sin(\pi x)``",
|
||||
raw"``2 \sin(\pi x)``"
|
||||
]
|
||||
answ = 4
|
||||
radioq(choices, answ)
|
||||
answer = 4
|
||||
radioq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -647,8 +721,8 @@ choices = [
|
||||
q"D(S(f))(n) = f(n)",
|
||||
q"S(D(f))(n) = f(n) - f(0)"
|
||||
]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -671,6 +745,6 @@ choices = [
|
||||
q"D(S(f))(n) = f(n)",
|
||||
q"S(D(f))(n) = f(n) - f(0)"
|
||||
]
|
||||
answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 1
|
||||
radioq(choices, answer, keep_order=true)
|
||||
```
|
||||
@@ -30,6 +30,7 @@ We measure angles in radians, where $360$ degrees is $2\pi$ radians. By proporti
|
||||
|
||||
For a right triangle with angles $\theta$, $\pi/2 - \theta$, and $\pi/2$ ($0 < \theta < \pi/2$) we call the side opposite $\theta$ the "opposite" side, the shorter adjacent side the "adjacent" side and the longer adjacent side the hypotenuse.
|
||||
|
||||
::: {#fig-right-triangle-soh-cah-toa}
|
||||
|
||||
```{julia}
|
||||
#| hide: true
|
||||
@@ -67,10 +68,12 @@ plotly()
|
||||
nothing
|
||||
```
|
||||
|
||||
With these, the basic definitions for the primary trigonometric functions are
|
||||
Labeling of a right triangle lengths in relation to a designated angle, $\theta$.
|
||||
:::
|
||||
|
||||
::: {.callout-note icon=false}
|
||||
## Trigonometric definitions
|
||||
With the labelings in @fig-right-triangle-soh-cah-toa, the basic definitions for the primary trigonometric functions are
|
||||
|
||||
::: {.definition title="Trigonometric definitions from a right triangle"}
|
||||
$$
|
||||
\begin{align*}
|
||||
\sin(\theta) &= \frac{\text{opposite}}{\text{hypotenuse}} &\quad(\text{the sine function})\\
|
||||
@@ -78,20 +81,16 @@ $$
|
||||
\tan(\theta) &= \frac{\text{opposite}}{\text{adjacent}} &\quad(\text{the tangent function})
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
|
||||
:::{.callout-note}
|
||||
## Note
|
||||
Many students remember these through [SOH-CAH-TOA](http://mathworld.wolfram.com/SOHCAHTOA.html).
|
||||
|
||||
:::
|
||||
|
||||
Some algebra shows that $\tan(\theta) = \sin(\theta)/\cos(\theta)$. There are also $3$ reciprocal functions, the cosecant, secant and cotangent.
|
||||
|
||||
|
||||
These definitions in terms of sides only apply for $0 \leq \theta \leq \pi/2$. More generally, if we relate any angle taken in the counter clockwise direction for the $x$-axis with a point $(x,y)$ on the *unit* circle, then we can extend these definitions - the point $(x,y)$ is also $(\cos(\theta), \sin(\theta))$.
|
||||
|
||||
These definitions in terms of sides only apply for $0 \leq \theta \leq \pi/2$. More generally, if we relate any angle taken in the counter clockwise direction for the $x$-axis with a point $(x,y)$ on the *unit* circle, then we can extend these definitions - the point $(x,y)$ is also $(\cos(\theta), \sin(\theta))$, cf. @fig-trig-values-along-unit-circle.
|
||||
|
||||
::: {#fig-trig-values-along-unit-circle}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
@@ -136,38 +135,46 @@ end
|
||||
|
||||
imgfile = tempname() * ".gif"
|
||||
gif(anim, imgfile, fps = 1)
|
||||
caption = "An angle in radian measure corresponds to a point on the unit circle, whose coordinates define the sine and cosine of the angle. That is ``(x,y) = (\\cos(\\theta), \\sin(\\theta))``."
|
||||
caption = ""
|
||||
|
||||
plotly()
|
||||
ImageFile(imgfile, caption)
|
||||
```
|
||||
|
||||
An angle in radian measure corresponds to a point on the unit circle, whose coordinates define the sine and cosine of the angle. That is $(x,y) = (\cos(\theta), \sin(\theta))$.
|
||||
:::
|
||||
|
||||
### The trigonometric functions in Julia
|
||||
|
||||
|
||||
Julia has the $6$ basic trigonometric functions defined through the functions `sin`, `cos`, `tan`, `csc`, `sec`, and `cot`.
|
||||
|
||||
|
||||
Two right triangles - the one with equal, $\pi/4$, angles; and the one with angles $\pi/6$ and $\pi/3$ can have the ratio of their sides computed from basic geometry. In particular, this leads to the following values, which are usually committed to memory:
|
||||
Two right triangles - the one with equal, $\pi/4$, angles; and the one with angles $\pi/6$ and $\pi/3$ can have the ratio of their sides computed from basic geometry. In particular, this leads to @tbl-basic-sin-cosine-values, values which are usually committed to memory:
|
||||
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\sin(0) &= 0, \quad \sin(\pi/6) = \frac{1}{2}, \quad \sin(\pi/4) = \frac{\sqrt{2}}{2}, \quad\sin(\pi/3) = \frac{\sqrt{3}}{2},\text{ and } \sin(\pi/2) = 1\\
|
||||
\cos(0) &= 1, \quad \cos(\pi/6) = \frac{\sqrt{3}}{2}, \quad \cos(\pi/4) = \frac{\sqrt{2}}{2}, \quad\cos(\pi/3) = \frac{1}{2},\text{ and } \cos(\pi/2) = 0.
|
||||
\end{align*}
|
||||
$$
|
||||
::: {#tbl-basic-sin-cosine-values .striped .hover}
|
||||
|
||||
| $\theta$ | $\sin(\theta)$ | $\cos(\theta)$ |
|
||||
|:---------:|:--------------:|:--------------:|
|
||||
| $0$ | $0$ | $1$ |
|
||||
| $\pi/6$ | $1/2$ | $\sqrt{3}/2$ |
|
||||
| $\pi/4$ | $\sqrt{2}/2$ | $\sqrt{2}/2$ |
|
||||
| $\pi/3$ | $\sqrt{3}/2$ | $1/2$ |
|
||||
| $\pi/2$ | $1$ | $0$ |
|
||||
|
||||
: Table of sine and cosine values that can be derived from different triangles
|
||||
:::
|
||||
|
||||
|
||||
Using the circle definition allows these basic values to inform us of values throughout the unit circle.
|
||||
|
||||
Using the circle definition allows these basic values to inform us of values throughout the unit circle:
|
||||
|
||||
These all follow from the definition involving the unit circle:
|
||||
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then the angle $-\theta$ corresponds to $(x, -y)$. So $\sin(\theta) = - \sin(-\theta)$ (an odd function), but $\cos(\theta) = \cos(-\theta)$ (an even function).
|
||||
|
||||
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then rotating by $\pi$ moves the points to $(-x, -y)$. So $x = \cos(\theta) = - \cos(\theta + \pi)$, and $y = \sin(\theta) = -\sin(\theta + \pi)$.
|
||||
|
||||
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then the angle $-\theta$ corresponds to $(x, -y)$. So $\sin(\theta) = - \sin(-\theta)$ (an odd function), but $\cos(\theta) = \cos(-\theta)$ (an even function).
|
||||
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then rotating by $\pi$ moves the points to $(-x, -y)$. So $\cos(\theta) = x = - \cos(\theta + \pi)$, and $\sin(\theta) = y = -\sin(\theta + \pi)$.
|
||||
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then rotating by $\pi/2$ moves the points to $(-y, x)$. So $\cos(\theta) = x = \sin(\theta + \pi/2)$.
|
||||
* If the angle $\theta$ corresponds to a point $(x,y)$ on the unit circle, then rotating by $\pi/2$ moves the points to $(-y, x)$. So $x = \cos(\theta) = \sin(\theta + \pi/2)$.
|
||||
|
||||
|
||||
The fact that $x^2 + y^2 = 1$ for the unit circle leads to the "Pythagorean identity" for trigonometric functions:
|
||||
@@ -232,7 +239,7 @@ opposite = adjacent * tan(theta)
|
||||
Having some means to compute an angle and then a tangent of that angle handy is not a given, so the linked to article provides a few other methods taking advantage of similar triangles.
|
||||
|
||||
|
||||
You can also measure distance with your [thumb](http://www.vendian.org/mncharity/dir3/bodyruler_angle/) or fist. How? The fist takes up about $10$ degrees of view when held straight out. So, pacing off backwards until the fist completely occludes the tree will give the distance of the adjacent side of a right triangle. If that distance is $30$ paces what is the height of the tree? Well, we need some facts. Suppose your pace is $3$ feet. Then the adjacent length is $90$ feet. The multiplier is the tangent of $10$ degrees, or:
|
||||
You can also measure distance with your [thumb](http://www.vendian.org/mncharity/dir3/bodyruler_angle/) or fist. How? The fist takes up about $10$ degrees of view when held straight out. So, pacing off backwards until the fist completely occludes the tree can give the distance of the adjacent side of a right triangle. If that distance is $30$ paces what is the height of the tree? Well, we need some facts. Suppose your pace is $3$ feet. Then the adjacent length is $90$ feet. The multiplier is the tangent of $10$ degrees, or:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -259,40 +266,52 @@ This could be reversed. If you know the height of something a distance away that
|
||||
### Basic properties
|
||||
|
||||
|
||||
The sine function is defined for all real $\theta$ and has a range of $[-1,1]$. Clearly as $\theta$ winds around the $x$-axis, the position of the $y$ coordinate begins to repeat itself. We say the sine function is *periodic* with period $2\pi$. A graph will illustrate:
|
||||
The sine function is defined for all real $\theta$ and has a range of $[-1,1]$. Clearly as $\theta$ winds around the $x$-axis, the position of the $y$ coordinate begins to repeat itself. We say the sine function is *periodic* with period $2\pi$. @fig-sin-over-0-4-pi illustrates. The graph shows two periods. The wavy aspect of the graph is why this function is used to model periodic motions, such as the amount of sunlight in a day, or the alternating current powering a computer.
|
||||
|
||||
|
||||
::: {#fig-sin-over-0-4-pi}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
plot(sin, 0, 4pi)
|
||||
```
|
||||
|
||||
The graph shows two periods. The wavy aspect of the graph is why this function is used to model periodic motions, such as the amount of sunlight in a day, or the alternating current powering a computer.
|
||||
Graph of $f(x) = \sin(x)$ over $[0, 4\pi]$
|
||||
:::
|
||||
|
||||
|
||||
From this graph---or considering when the $y$ coordinate is $0$---we see that the sine function has zeros at any integer multiple of $\pi$, or $k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots$.
|
||||
|
||||
From the graph of $\sin(x)$---or considering when the $y$ coordinate is $0$ on the unit circle---we see that the sine function has zeros at any integer multiple of $\pi$, or $k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots$.
|
||||
|
||||
|
||||
The cosine function is similar, in that it has the same domain and range, but is "out of phase" with the sine curve. A graph of both shows the two are related:
|
||||
|
||||
The cosine function is similar, in that it has the same domain and range, but is "out of phase" with the sine curve. @fig-sin-cosine-graph illustrates.
|
||||
|
||||
::: {#fig-sin-cosine-graph}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
plot(sin, 0, 4pi, label="sin")
|
||||
plot!(cos, 0, 4pi, label="cos")
|
||||
```
|
||||
|
||||
: Graph of $\sin(x)$ and $\cos(x)$ over $[0, 4\pi]$. The cosine graph lags the sine graph by $\pi/2$, or $\cos(x) = \sin(x + \pi/2)$.
|
||||
:::
|
||||
|
||||
The cosine function is just a shift of the sine function (or vice versa). We see that the zeros of the cosine function happen at points of the form $\pi/2 + k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots.$
|
||||
|
||||
|
||||
The tangent function does not have all $\theta$ for its domain, rather those points where division by $0$ occurs are excluded. These occur when the cosine is $0$, or, again, at $\pi/2 + k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots.$ The range of the tangent function will be all real $y$.
|
||||
The tangent function does not have all $\theta$ for its domain, rather those points where division by $0$ occurs are excluded; when the cosine is $0$, or, again, at $\pi/2 + k\pi$, $k$ in $\dots,-2,-1, 0, 1, 2, \dots.$ The range of the tangent function will be all real $y$.
|
||||
|
||||
|
||||
The tangent function is also periodic, but not with period $2\pi$, but rather just $\pi$. A graph will show this. Here we avoid the vertical asymptotes using `rangeclamp`:
|
||||
|
||||
The tangent function is also periodic, but not with period $2\pi$, but rather just $\pi$. @fig-graph-of-tangent-minus10-10 illustrates.
|
||||
|
||||
::: {#fig-graph-of-tangent-minus10-10}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
plot(rangeclamp(tan), -10, 10, label="tan")
|
||||
```
|
||||
|
||||
Graph of $f(x) = \tan(x)$ over $[-10, 10]$ showing the periodic nature of the function. The vertical asymptotes were avoided by plotting `rangeclamp(tan)`.
|
||||
:::
|
||||
|
||||
##### Example sums of sines
|
||||
|
||||
|
||||
@@ -303,34 +322,43 @@ $$
|
||||
g(x) = a + b \sin((2\pi n)x)
|
||||
$$
|
||||
|
||||
That is a graph of $g$ will be the sine curve shifted up by $a$ units, scaled vertically by $b$ units and has a period of $1/n$. We see a simple plot here where we can verify the transformation:
|
||||
That is a graph of $g$ will be the sine curve shifted up by $a$ units, scaled vertically by $b$ units and has a period of $1/n$.
|
||||
|
||||
@fig-plot-of-2sin-2pi-n-x verifies the transformation:
|
||||
|
||||
::: {#fig-plot-of-2sin-2pi-n-x}
|
||||
|
||||
```{julia}
|
||||
g(x; b=1, n=1) = b*sin(2pi*n*x)
|
||||
g1(x) = 1 + g(x, b=2, n=3)
|
||||
plot(g1, 0, 1)
|
||||
plot(g1, 0, 1; xticks = (0:1/3:1, ["0", "1/3", "2/3", "1"]))
|
||||
```
|
||||
Plot of $g(x) = a + b \sin((2\pi n)x)$ with $n=1/3$ so the period of the sinusoical function is $1/3
|
||||
:::
|
||||
|
||||
We can consider the sum of such functions, for example
|
||||
@fig-sum-two-gs shows the sum of two such functions. Though still periodic, we can see with this simple example that sums of different sine functions can have somewhat complicated graphs.
|
||||
|
||||
|
||||
::: {#fig-sum-two-gs}
|
||||
|
||||
```{julia}
|
||||
g2(x) = 1 + g(x, b=2, n=3) + g(x, b=4, n=5)
|
||||
plot(g2, 0, 1)
|
||||
```
|
||||
|
||||
Though still periodic, we can see with this simple example that sums of different sine functions can have somewhat complicated graphs.
|
||||
Graph of sum of two sine functions, one with period $1/3$ one with period $1/5$. The resulting function has period $1$.
|
||||
:::
|
||||
|
||||
|
||||
|
||||
Sine functions can be viewed as the `x` position of a point traveling around a circle so `g(x, b=2, n=3)` is the `x` position of point traveling around a circle of radius $2$ that completes a circuit in $1/3$ units of time.
|
||||
|
||||
|
||||
The superposition of the two sine functions that `g2` represents could be viewed as the position of a circle moving around a point that is moving around another circle. The following graphic, with $b_1=1/3, n_1=3, b_2=1/4$, and $n_2=4$, shows an example that produces the related cosine sum (moving right along the $x$ axis), the sine sum (moving down along the $y$ axis, *and* the trace of the position of the point generating these two plots.
|
||||
The superposition of the two sine functions that `g2` represents could be viewed as the position of a circle moving around a point that is moving around another circle. @fig-superposition-of-sines-cosines, with $b_1=1/3, n_1=3, b_2=1/4$, and $n_2=4$, shows an example that produces the related cosine sum (moving right along the $x$ axis), the sine sum (moving down along the $y$ axis, *and* the trace of the position of the point generating these two plots.
|
||||
|
||||
::: {#fig-superposition-of-sines-cosines}
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
#| cache: true
|
||||
gr()
|
||||
@@ -390,11 +418,14 @@ end
|
||||
imgfile = tempname() * ".gif"
|
||||
gif(anim, imgfile, fps = 5)
|
||||
|
||||
caption = "Superposition of sines and cosines represented by an epicycle"
|
||||
caption = ""
|
||||
plotly()
|
||||
ImageFile(imgfile, caption)
|
||||
```
|
||||
|
||||
Superposition of sines and cosines represented by an epicycle.
|
||||
:::
|
||||
|
||||
As can be seen, even a somewhat simple combination can produce complicated graphs (a fact known to [Ptolemy](https://en.wikipedia.org/wiki/Deferent_and_epicycle)) . How complicated can such a graph get? This won't be answered here, but for fun enjoy this video produced by the same technique using more moving parts from the [`Javis.jl`](https://github.com/Wikunia/Javis.jl/blob/master/examples/fourier.jl) package:
|
||||
|
||||
|
||||
@@ -431,6 +462,7 @@ More generally, suppose we have two angles $\alpha$ and $\beta$, can we represen
|
||||
|
||||
Suppose both $\alpha$ and $\beta$ are positive with $\alpha + \beta \leq \pi/2$. Then using right triangle geometry we can associate the sine and cosine of $\alpha + \beta$ with distances in this figure:
|
||||
|
||||
::: {#fig-sin-cos-alpha-plus-beta-and-beta}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
gr()
|
||||
@@ -534,25 +566,25 @@ plot!(Shape([F,B]), fill=(:black, 0.35))
|
||||
annotate!(map(s ->getindex(txtpoints,s), collect(keys(txtpoints))))
|
||||
|
||||
|
||||
p1
|
||||
plot(p1, p2)
|
||||
```
|
||||
|
||||
Another right triangle with hypotenuse of length $1$ can be made by isolating the angle $\beta$, as below:
|
||||
The left figure labels the sides of a right triangle with angle $\alpha + \beta$, the right figure labels the sides of a right triangle with angle $\beta$.
|
||||
:::
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
p2
|
||||
```
|
||||
In @fig-cos-alpha-beta-sin-alpha-beta we make two more right triangles one with hypotenuse $\cos(\beta)$ and one with hypotenuse $\sin(\beta)$; each having an angle $\alpha$, the latter using some geometry, for which we can apply right-triangle trigonometry to find the length of their respective sides.
|
||||
|
||||
|
||||
We can make two more right triangles one with hypotenuse $\cos(\beta)$ and one with hypotenuse $\sin(\beta)$; each having an angle $\alpha$, the latter using some geometry, for which we can apply right-triangle trigonometry to find the length of their sides.
|
||||
::: {#fig-cos-alpha-beta-sin-alpha-beta}
|
||||
|
||||
```{julia}
|
||||
#| echo: false
|
||||
plot(p3, p4)
|
||||
```
|
||||
|
||||
From the left figure and the initial triangle, by comparing the lengths along the $x$ direction, we can see the decomposition:
|
||||
Two triangles with angle $\alpha$ and hypotenuses $\cos(\beta)$ and $\sin(\beta)$.
|
||||
:::
|
||||
|
||||
From the left side of #fig-cos-alpha-beta-sin-alpha-beta and the initial triangle, by comparing the lengths along the $x$ direction, we can see the decomposition:
|
||||
|
||||
$$
|
||||
\cos(\alpha)\cos(\beta) = \cos(\alpha + \beta) + \sin(\alpha)\sin(\beta)
|
||||
@@ -564,10 +596,9 @@ $$
|
||||
\sin(\alpha+\beta) = \sin(\alpha)\cos(\beta) + \cos(\alpha)\sin(\beta)
|
||||
$$
|
||||
|
||||
These lead to:
|
||||
All combined, these lead to:
|
||||
|
||||
::: {.callout-note icon=false}
|
||||
## The *sum* formulas for sine and cosine
|
||||
::: {.relationship title="The sum formulas for sine and cosine"}
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
@@ -575,12 +606,12 @@ $$
|
||||
\cos(\alpha + \beta) &= \cos(\alpha)\cos(\beta) - \sin(\alpha)\sin(\beta)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
:::
|
||||
|
||||
Taking $\alpha = \beta$ we immediately get
|
||||
|
||||
::: {.callout-note icon=false}
|
||||
## The "double-angle" formulas
|
||||
::: {.relationship title="The double-angle formulas"}
|
||||
$$
|
||||
\begin{align*}
|
||||
\sin(2\alpha) &= 2\sin(\alpha)\cos(\alpha)\\
|
||||
@@ -589,17 +620,16 @@ $$
|
||||
$$
|
||||
:::
|
||||
|
||||
The latter looks like the Pythagorean identify, but has a minus sign. In fact, the Pythagorean identify is often used to rewrite this, for example $\cos(2\alpha) = 2\cos^2(\alpha) - 1$ or $1 - 2\sin^2(\alpha)$.
|
||||
The latter looks like the Pythagorean identify, but has a minus sign. In fact, the Pythagorean identify is often used to rewrite this formula, for example $\cos(2\alpha) = 2\cos^2(\alpha) - 1$ or $1 - 2\sin^2(\alpha)$.
|
||||
|
||||
|
||||
Applying the above with $\alpha = \beta/2$, we get that $\cos(\beta) = 2\cos^2(\beta/2) -1$. Similarly, using the Pythagorean identity a formula for sine can be done; when rearranged these yield the "half-angle" formulas:
|
||||
Applying the above with $\alpha = \beta/2$, we get that $\cos(\beta) = 2\cos^2(\beta/2) -1$. Similarly, using the Pythagorean identity a formula for sine can be identified; when rearranged these yield the "half-angle" formulas:
|
||||
|
||||
::: {.callout-note icon=false}
|
||||
## The "half-angle" formula
|
||||
::: {.relationship title="The half-angle formulas"}
|
||||
$$
|
||||
\begin{align*}
|
||||
\sin^2(\frac{\beta}{2}) &= \frac{1 - \cos(\beta)}{2}\\
|
||||
\cos^2(\frac{\beta}{2}) &= \frac{1 + \cos(\beta)}{2}
|
||||
\sin^2\left(\frac{\beta}{2}\right) &= \frac{1 - \cos(\beta)}{2}\\
|
||||
\cos^2\left(\frac{\beta}{2}\right) &= \frac{1 + \cos(\beta)}{2}
|
||||
\end{align*}
|
||||
$$
|
||||
:::
|
||||
@@ -633,6 +663,13 @@ $$
|
||||
|
||||
That is the angle for a multiple of $n+1$ can be expressed in terms of the angle with a multiple of $n$ and $n-1$. This can be used recursively to find expressions for $\cos(n\theta)$ in terms of polynomials in $\cos(\theta)$.
|
||||
|
||||
For example,
|
||||
|
||||
```{julia}
|
||||
@syms θ
|
||||
sympy.expand_trig(cos(5 * θ))
|
||||
```
|
||||
|
||||
|
||||
## Inverse trigonometric functions
|
||||
|
||||
@@ -640,29 +677,31 @@ That is the angle for a multiple of $n+1$ can be expressed in terms of the angle
|
||||
The trigonometric functions are all periodic. In particular they are not monotonic over their entire domain. This means there is no *inverse* function applicable. However, by restricting the domain to where the functions are monotonic, inverse functions can be defined:
|
||||
|
||||
|
||||
* For $\sin(x)$, the restricted domain of $[-\pi/2, \pi/2]$ allows for the arcsine function to be defined. In `Julia` this is implemented with `asin`.
|
||||
* For $\cos(x)$, the restricted domain of $[0,\pi]$ allows for the arccosine function to be defined. In `Julia` this is implemented with `acos`.
|
||||
* For $\tan(x)$, the restricted domain of $(-\pi/2, \pi/2)$ allows for the arctangent function to be defined. In `Julia` this is implemented with `atan`.
|
||||
|
||||
* For $\sin(x)$, the restricted domain of $[-\pi/2, \pi/2]$ allows for the arcsine function to be defined. In `Julia` this is implemented with `asin`.
|
||||
|
||||
* For $\cos(x)$, the restricted domain of $[0,\pi]$ allows for the arccosine function to be defined. In `Julia` this is implemented with `acos`.
|
||||
|
||||
* For $\tan(x)$, the restricted domain of $(-\pi/2, \pi/2)$ allows for the arctangent function to be defined. In `Julia` this is implemented with `atan`.
|
||||
|
||||
|
||||
|
||||
|
||||
For example, the arcsine function is defined for $-1 \leq x \leq 1$ and has a range of $-\pi/2$ to $\pi/2$:
|
||||
|
||||
|
||||
::: {#fig-arcsin-arctan}
|
||||
```{julia}
|
||||
plot(asin, -1, 1)
|
||||
#| echo: false
|
||||
p1 = plot(asin, -1, 1; legend=false, title="arcsin")
|
||||
p2 = plot(atan, -15, 15; legend=false, title="arctan")
|
||||
plot(p1, p2)
|
||||
```
|
||||
|
||||
The arctangent has domain of all real $x$. It has shape given by:
|
||||
The function $f(x) = \arcsin(x)$ has domain $[-1,1]$, whereas the function $f(x) = \arctan(x)$ has domain $(-\infty, \infty)$.
|
||||
:::
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(atan, -10, 10)
|
||||
```
|
||||
|
||||
The horizontal asymptotes are $y=\pi/2$ and $y=-\pi/2$.
|
||||
|
||||
|
||||
### Implications of a restricted domain
|
||||
##### Example: Implications of a restricted domain
|
||||
|
||||
|
||||
Notice that $\sin(\arcsin(x)) = x$ for any $x$ in $[-1,1]$, but, of course, not for all $x$, as the output of the sine function can't be arbitrarily large.
|
||||
@@ -805,7 +844,7 @@ $$
|
||||
Both $\theta_0$ and $\theta_1$ are measured with respect to the coordinate system that looks like the $x-y$ plane. The red coordinate system is used to identify the angle of incidence for the second bending. Some right-triangle geometry relates the new angle $\theta'_1$ with $\theta_1$ through $\theta'_1 = \alpha - \theta_1$. With this new angle of incidence, the angle of refraction, $\theta'_2$, satisfies:
|
||||
|
||||
$$
|
||||
n1 \sin(\theta'_1) = n2 \sin(\theta'_2)
|
||||
n_1 \sin(\theta'_1) = n_2 \sin(\theta'_2)
|
||||
$$
|
||||
|
||||
Or
|
||||
@@ -868,9 +907,9 @@ $$
|
||||
d = \pi + 2i - 4 \arcsin(\frac{1}{n} \sin(i)).
|
||||
$$
|
||||
|
||||
Graphing this for incident angles between $0$ and $\pi/2$ we have:
|
||||
|
||||
@fig-plot-of-deflection-for-different-incident-angles shows the deflection for incident angles between $0$ and $\pi/2$
|
||||
|
||||
::: {#fig-plot-of-deflection-for-different-incident-angles}
|
||||
```{julia}
|
||||
#| hold: true
|
||||
n = 4/3
|
||||
@@ -878,6 +917,9 @@ d(i) = pi + 2i - 4 * asin(sin(i)/n)
|
||||
plot(d, 0, pi/2)
|
||||
```
|
||||
|
||||
Plot of deflection for different incident angles
|
||||
:::
|
||||
|
||||
Descartes was interested in the minimum value of this graph, as it relates to where the light concentrates. This is roughly at $1$ radian or about $57$ degrees:
|
||||
|
||||
|
||||
@@ -915,9 +957,9 @@ A few things become clear from the above two representations:
|
||||
* Using the initial definition, we see that the zeros of $T_n(x)$ all occur within $[-1,1]$ and happen when $n\arccos(x) = k\pi + \pi/2$, or $x=\cos((2k+1)/n \cdot \pi/2)$ for $k=0, 1, \dots, n-1$.
|
||||
|
||||
|
||||
Other properties of this polynomial family are not at all obvious. One is that amongst all polynomials of degree $n$ with roots in $[-1,1]$, $T_n(x)$ will be the smallest in magnitude (after we divide by the leading coefficient to make all polynomials considered to be monic). We check this for one case. Take $n=4$, then we have: $T_4(x) = 8x^4 - 8x^2 + 1$. Compare this with $q(x) = (x+3/5)(x+1/5)(x-1/5)(x-3/5)$ (evenly spaced zeros):
|
||||
|
||||
Other properties of this polynomial family are not at all obvious. One is that amongst all polynomials of degree $n$ with roots in $[-1,1]$, $T_n(x)$ will be the smallest in magnitude (after we divide by the leading coefficient to make all polynomials considered to be monic). We check this for one case. Take $n=4$, then we have: $T_4(x) = 8x^4 - 8x^2 + 1$. We compare this polynomial with $q(x) = (x+3/5)(x+1/5)(x-1/5)(x-3/5)$ (evenly spaced zeros) in @fig-plot-T4-q-showing-chebyshev-minimal.
|
||||
|
||||
::: {#fig-plot-T4-q-showing-chebyshev-minimal}
|
||||
```{julia}
|
||||
T4(x) = (8x^4 - 8x^2 + 1) / 8
|
||||
q(x) = (x+3/5)*(x+1/5)*(x-1/5)*(x-3/5)
|
||||
@@ -925,14 +967,17 @@ plot(abs ∘ T4, -1,1, label="|T₄|")
|
||||
plot!(abs ∘ q, -1,1, label="|q|")
|
||||
```
|
||||
|
||||
We will return to this family of polynomials in the section on Orthogonal Polynomials.
|
||||
The monic Chebyshev polynomial is has the smallest maximum value of $[-1,1]$ of all monic polynomials of the same degree
|
||||
:::
|
||||
|
||||
We will return to this family of polynomials in the section on orthogonal polynomials.
|
||||
|
||||
## Hyperbolic trigonometric functions
|
||||
|
||||
|
||||
Related to the trigonometric functions are the hyperbolic trigonometric functions. Instead of associating a point $(x,y)$ on the unit circle with an angle $\theta,$ we associate a point $(x,y)$ on the unit *hyperbola* ($x^2 - y^2 = 1$). We define the hyperbolic sine ($\sinh$) and hyperbolic cosine ($\cosh$) through $(\cosh(\theta), \sinh(\theta)) = (x,y)$.
|
||||
|
||||
|
||||
::: {#fig-hyperbolic-trig-functions-from-unit-hyperbola}
|
||||
```{julia}
|
||||
#| echo: false
|
||||
let
|
||||
@@ -940,7 +985,7 @@ let
|
||||
# y^2 = x^2 - 1
|
||||
top(x) = sqrt(x^2 - 1)
|
||||
|
||||
p = plot(; legend=false, aspect_ratio=:equal)
|
||||
p = plot(; legend=false, framestyle=:origin, aspect_ratio=:equal)
|
||||
|
||||
x₀ = 2
|
||||
xs = range(1, x₀, length=100)
|
||||
@@ -976,6 +1021,8 @@ let
|
||||
p
|
||||
end
|
||||
```
|
||||
Figure showing the definitions of $\cosh(x)$ and $\sinh(x)$ using the unit hyperbola $x^2 - y^2 = 1$
|
||||
:::
|
||||
|
||||
These values are more commonly expressed using the exponential function as:
|
||||
|
||||
@@ -1004,12 +1051,12 @@ What is bigger $\sin(1.23456)$ or $\cos(6.54321)$?
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
a = sin(1.23456) > cos(6.54321)
|
||||
choices = [raw"``\sin(1.23456)``", raw"``\cos(6.54321)``"]
|
||||
answ = a ? 1 : 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = a ? 1 : 2
|
||||
explanation = "Compare with `sin(1.23456) > cos(6.54321)`"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1019,13 +1066,13 @@ Let $x=\pi/4$. What is bigger $\cos(x)$ or $x$?
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
x = pi/4
|
||||
a = cos(x) > x
|
||||
choices = [raw"``\cos(x)``", "``x``"]
|
||||
answ = a ? 1 : 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = a ? 1 : 2
|
||||
explanation = "Compare with `cos(pi/4) > pi/4`"
|
||||
radioq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1041,8 +1088,8 @@ choices = [
|
||||
raw"``\cos(x) = \sin(x - \pi/2)``",
|
||||
raw"``\cos(x) = \sin(x + \pi/2)``",
|
||||
raw"``\cos(x) = \pi/2 \cdot \sin(x)``"]
|
||||
answ = 2
|
||||
radioq(choices, answ)
|
||||
answer = 2
|
||||
buttonq(choices, answer)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1058,8 +1105,9 @@ choices = [
|
||||
L"The values $k\pi$ for $k$ in $\dots, -2, -1, 0, 1, 2, \dots$",
|
||||
L"The values $\pi/2 + k\pi$ for $k$ in $\dots, -2, -1, 0, 1, 2, \dots$",
|
||||
L"The values $2k\pi$ for $k$ in $\dots, -2, -1, 0, 1, 2, \dots$"]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
explanation = "The secant is the reciprocal of the cosine function"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
@@ -1117,37 +1165,40 @@ numericq(val)
|
||||
The sine function is an *odd* function.
|
||||
|
||||
|
||||
* The hyperbolic sine is:
|
||||
* The hyperbolic sine is:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["odd", "even", "neither"]
|
||||
answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 1
|
||||
explanation = "subsitute `-x` into the exponential formula to see"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
* The hyperbolic cosine is:
|
||||
* The hyperbolic cosine is:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["odd", "even", "neither"]
|
||||
answ = 2
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 2
|
||||
explanation = L"The value of $\cosh(-x)$ is the $y$ position of the point $(x,y)$ refelected through the $y$ axis, so is unchanged."
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
* The hyperbolic tangent is:
|
||||
* The hyperbolic tangent is:
|
||||
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
choices = ["odd", "even", "neither"]
|
||||
answ = 1
|
||||
radioq(choices, answ, keep_order=true)
|
||||
answer = 1
|
||||
explanation = "A ratio of an odd function and an even function is *odd*"
|
||||
buttonq(choices, answer; explanation)
|
||||
```
|
||||
|
||||
###### Question
|
||||
|
||||
Reference in new issue
Block a user