2
0
Fork 0
mirror of https://github.com/MartinThoma/LaTeX-examples.git synced 2025-04-19 11:38:05 +02:00
This commit is contained in:
Martin Thoma 2013-11-22 21:40:15 +01:00
parent 145238524e
commit 1bcec68935
4 changed files with 363 additions and 17 deletions

View file

@ -1,7 +1,8 @@
SOURCE = math-minimal-distance-to-cubic-function
make:
pdflatex $(SOURCE).tex -output-format=pdf
pdflatex $(SOURCE).tex -output-format=pdf
make clean
clean:
rm -rf $(TARGET) *.class *.html *.log *.aux *.out
rm -rf $(TARGET) *.class *.log *.aux *.out

View file

@ -30,21 +30,38 @@
\begin{document}
\maketitle
\begin{abstract}
In this paper I want to discuss how to find all points on a a cubic
When you have a selfdriving car, you have to plan which path you
want to take. A reasonable choice for the representation of this
path is a cubic spline. But you also have to be able to calculate
how to steer to get or to remain on this path. A way to do this
is applying the \href{https://en.wikipedia.org/wiki/PID_algorithm}{PID algorithm}.
But this algorithm needs to know the current error. So you need to
be able to get the minimal distance of a point to a cubic spline.
As you need to get the signed error (and one steering direction might
be prefered), it is not only necessary to
get the minimal absolute distance, but also to get all points
on the spline with minimal distance.
In this paper I want to discuss how to find all points on a cubic
function with minimal distance to a given point.
As other representations of paths might be easier to understand and
to implement, I will also cover the problem of finding the minimal
distance of a point to a polynomial of degree 0, 1 and 2.
\end{abstract}
\section{Description of the Problem}
Let $f: \mdr \rightarrow \mdr$ be a polynomial function and $P \in \mdr^2$
be a point. Let $d: \mdr^2 \times \mdr^2 \rightarrow \mdr_0^+$
be the euklidean distance of two points:
be the Euklidean distance of two points:
\[d \left ((x_1, y_1), (x_2, y_2) \right) := \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}\]
Now there is finite set of points $x_1, \dots, x_n$ such that
Now there is \todo{Should I proof this?}{finite set} $x_1, \dots, x_n$ such that
\[\forall \tilde x \in \mathbb{R} \setminus \{x_1, \dots, x_n\}: d(P, (x_1, f(x_1))) = \dots = d(P, (x_n, f(x_n))) < d(P, (\tilde x, f(\tilde x)))\]
The task is now to find those $x_1, \dots, x_n$ for given $f, P$.
\section{Minimal distance to a constant function}
Let $f(x) = c$ with $c \in \mdr$ be a function.
Let $f(x) = c$ with $c \in \mdr$ be a constant function.
\begin{figure}[htp]
\centering
@ -72,21 +89,26 @@ Let $f(x) = c$ with $c \in \mdr$ be a function.
\addplot[domain=-5:5, thick,samples=50, green] {2};
\addplot[domain=-5:5, thick,samples=50, blue] {3};
\addplot[black, mark = *, nodes near coords=$P$,every node near coord/.style={anchor=225}] coordinates {(2, 2)};
\addplot[blue, mark = *, nodes near coords=$P_{h,\text{min}}$,every node near coord/.style={anchor=225}] coordinates {(2, 3)};
\addplot[green, mark = x, nodes near coords=$P_{g,\text{min}}$,every node near coord/.style={anchor=120}] coordinates {(2, 2)};
\addplot[red, mark = *, nodes near coords=$P_{f,\text{min}}$,every node near coord/.style={anchor=225}] coordinates {(2, 1)};
\draw[thick, dashed] (axis cs:2,0) -- (axis cs:2,3);
\addlegendentry{$f(x)=1$}
\addlegendentry{$g(x)=2$}
\addlegendentry{$h(x)=3$}
\end{axis}
\end{tikzpicture}
\caption{3 constant functions}
\caption{3 constant functions and their points with minimal distance}
\label{fig:constant-min-distance}
\end{figure}
Then $(x_P,f(x_P))$ has
minimal distance to $P$. Every other point has higher distance.
See Figure~\ref{fig:constant-min-distance}.
\section{Minimal distance to a linear function}
Let $f(x) = m \cdot x + t$ with $m \in \mdr \setminus \Set{0}$ and
$t \in \mdr$ be a function.
$t \in \mdr$ be a linear function.
\begin{figure}[htp]
\centering
@ -118,27 +140,33 @@ $t \in \mdr$ be a function.
\end{axis}
\end{tikzpicture}
\caption{The shortest distance of $P$ to $f$ can be calculated by using the perpendicular}
\label{fig:linear-min-distance}
\end{figure}
Now you can drop a perpendicular through $P$ on $f(x)$. The slope $f_\bot$
of the perpendicular is $- \frac{1}{m}$. Then:
Now you can drop a perpendicular $f_\bot$ through $P$ on $f(x)$. The slope of $f_\bot$
is $- \frac{1}{m}$. Now you can calculate $f_\bot$:\nobreak
\begin{align}
f_\bot(x) &= - \frac{1}{m} \cdot x + t_\bot\\
\Rightarrow y_P &= - \frac{1}{m} \cdot x_P + t_\bot\\
\Leftrightarrow t_\bot &= y_P + \frac{1}{m} \cdot x_P\\
\Leftrightarrow t_\bot &= y_P + \frac{1}{m} \cdot x_P
\end{align}
Now find the point $(x, f(x))$ where the perpendicular crosses the function:
\begin{align}
f(x) &= f_\bot(x)\\
\Leftrightarrow m \cdot x + t &= - \frac{1}{m} \cdot x + \left(y_P + \frac{1}{m} \cdot x_P \right)\\
\Leftrightarrow \left (m + \frac{1}{m} \right ) \cdot x &= y_P + \frac{1}{m} \cdot x_P - t\\
\Leftrightarrow x &= \frac{m}{m^2+1} \left ( y_P + \frac{1}{m} \cdot x_P - t \right )
\end{align}
There is only one point with minimal distance.
There is only one point with minimal distance. See Figure~\ref{fig:linear-min-distance}.
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Quadratic functions %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Minimal distance to a quadratic function}
Let $f(x) = a \cdot x^2 + b \cdot x + c$ with $a \in \mdr \setminus \Set{0}$ and
$b, c \in \mdr$ be a function.
$b, c \in \mdr$ be a quadratic function.
\begin{figure}[htp]
\centering
@ -239,7 +267,7 @@ Minimizing $d$ is the same as minimizing $d^2$:
\begin{align}
d(x)^2 &= x_p^2 - 2x_p x + x^2 + y_p^2 - 2y_p f(x) + f(x)^2\\
(d(x)^2)' &= -2 x_p + 2x -2y_p(f(x))' + (f(x)^2)'\\
0 &\stackrel{!}{=} -2 x_p + 2x -2y_p(f(x))' + (f(x)^2)'
0 &\stackrel{!}{=} -2 x_p + 2x -2y_p(f(x))' + (f(x)^2)' \label{eq:minimizing}
\end{align}
Now we use thet $f(x) = ax^2 + bx + c$:
@ -274,9 +302,12 @@ So the minimum for $a=1, b=c=d=0$ is:
\subsection{Calculate points with minimal distance}
\todo[inline]{Write this}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Cubic %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Minimal distance to a cubic function}
Let $f(x) = a \cdot x^3 + b \cdot x^2 + c \cdot x + d$ with $a \in \mdr \setminus \Set{0}$ and
Let $f(x) = a \cdot x^3 + b \cdot x^2 + c \cdot x + d$ be a cubic function
with $a \in \mdr \setminus \Set{0}$ and
$b, c, d \in \mdr$ be a function.
\subsection{Number of points with minimal distance}
@ -292,5 +323,16 @@ For $b^2 \geq 3ac$
\todo[inline]{Write this}
\subsection{Calculate points with minimal distance}
When you want to calculate points with minimal distance, you can
take the same approach as in Equation \ref{eq:minimizing}:
\begin{align}
0 &\stackrel{!}{=} -2 x_p + 2x -2y_p(f(x))' + (f(x)^2)'\\
\Leftrightarrow 0 &\stackrel{!}{=} 2 f(x) \cdot f'(x) - 2 y_p f'(x) + 2x - 2 x_p\\
\Leftrightarrow 0 &\stackrel{!}{=} \underbrace{\left (2 f(x) - 2 y_p \right ) \cdot f'(x)}_{\text{Polynomial of degree 5}} + \underbrace{2x - 2 x_p}_{\text{:-(}}
\end{align}
\todo[inline]{Write this}
\end{document}

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- saved from url=(0112)file:///home/moose/Downloads/LaTeX-examples/documents/math-minimal-distance-to-cubic-function/quadratic-vis.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<title>Quadratic visualization</title>
<style type="text/css">
span.hint {
border-bottom:1px dotted #666;
}
</style>
</head>
<body>
<table>
<tbody><tr>
<td><span class="hint" title="constat for x*x">a</span></td>
<td><input type="number" step="1" value="1" id="a" min="1" onchange="updateBoard()"></td>
<td><span class="hint" title="constant for x">b</span></td>
<td><input type="number" step="1" value="1" id="b" min="1" onchange="updateBoard()"></td>
<td><span class="hint" title="constant">c</span></td>
<td><input type="number" step="1" value="1" id="c" min="1" onchange="updateBoard()"></td>
</tr>
<tr>
<td><span class="hint" title="">STRETCH_X</span></td>
<td><input type="number" step="1" value="3" id="STRETCH_X" min="1" onchange="updateBoard()"></td>
<td><span class="hint" title="">STRETCH_Y</span></td>
<td><input type="number" step="0.25" value="0.5" id="STRETCH_Y" min="0.1" onchange="updateBoard()"></td>
<td><span class="hint" title="">X_OFFSET</span></td>
<td><input type="number" step="16" value="-128" id="X_OFFSET" onchange="updateBoard()"></td>
<td><span class="hint" title="">Y_OFFSET</span></td>
<td><input type="number" step="16" value="256" id="Y_OFFSET" onchange="updateBoard()"></td>
</tr>
<tr>
<td><span class="hint" title="visualize distance">pDistance</span></td>
<td><input type="checkbox" id="pDistance" onchange="updateBoard()"></td>
<td><span class="hint" title="How much will points be spread for voronoi? USE 1 WITH CAUTION! The bigger the value, the quicker the computation.">spread</span></td>
<td><input type="number" step="1" value="1" id="density" min="1" onchange="updateBoard()"></td>
<td><a href="./quadratic-vis_files/quadratic-vis.html">clear board</a></td>
</tr>
</tbody></table>
<canvas id="myCanvas" width="1316" height="535" style="border: 1px solid rgb(0, 0, 0); cursor: crosshair;"> </canvas>
<script type="text/javascript" src="quadratic-vis.js">
</script>
</body></html>

View file

@ -0,0 +1,257 @@
'use strict';
/* global variables */
var STRETCH_X = 3;
var STRETCH_Y = 0.5;
var X_MIN = -10;
var X_MAX = +10;
var Y_MIN = -10;
var Y_MAX = +10;
var X_OFFSET = -128;
var Y_OFFSET = 256;
var INITIAL_RADIUS = 20;
var POINT_RADIUS = 5;
/*******************************************************************/
/* Graphics */
/*******************************************************************/
/**
* Calculates coordinates from worldspace to screenspace
* @param {Number} x the coordinate you want to transform
* @param {bool} isX true iff x is a x-coordinate, otherwise false
* @return {Number} transformed coordinate
*/
function c(x, isX) {
if (isX) {
return STRETCH_X * (x - X_OFFSET);
}
return STRETCH_Y * (-x + Y_OFFSET);
}
/**
* Calculates coordinates from screenspace to worldspace
* @param {Number} x the coordinate you want to transform
* @param {bool} isX true iff x is a x-coordinate, otherwise false
* @return {Number} transformed coordinate
*/
function r(x, isX) {
if (isX) {
return (x / STRETCH_X) + X_OFFSET;
}
return (-x / STRETCH_Y) + Y_OFFSET;
}
function setCursorByID(id,cursorStyle) {
var elem;
if (document.getElementById &&
(elem=document.getElementById(id)) ) {
if (elem.style) elem.style.cursor=cursorStyle;
}
}
function drawEllipse(centerX, centerY, width, height) {
context.beginPath() ;
var x = centerX;
var y = centerY;
var rx = width;
var ry = height;
var rotation = 0; // The rotation of the ellipse (in radians)
var start = 0; // The start angle (in radians)
var end = 2 * Math.PI; // The end angle (in radians)
var anticlockwise = false;
context.ellipse(x, y, rx, ry, rotation, start, end, anticlockwise);
context.fillStyle = "rgba(255, 0, 0, 0.5)";
context.fill();
}
function getColor(i, transparency) {
//var t = (i+1)*(360/k);
//var color = 'hsla('+t+', 100%, 50%, '+transparency+')';
var x = i / 256;
if (x > 1) {x = 1.0;}
x = parseInt(x*255);
var color = 'rgba('+x+','+x+','+x+','+transparency+')';
return color;
}
function drawQuadraticFunction(canvas) {
var add = parseInt(document.getElementById("density").value);
context.beginPath();
context.fillStyle = 'red';
context.moveTo(0, c(getValue(r(0))), false);
for (var xS=0; xS < canvas.width; xS+=add) {
var x = r(xS);
var y = getValue(x);
//context.fillRect(c(x), c(y, false), add/2, add/2);
context.lineTo(c(x, true), c(y, false));
}
context.closePath();
context.stroke();
}
/*******************************************************************/
/* Math */
/*******************************************************************/
function euklideanDist(p1, p2) {
return Math.sqrt(
Math.pow(p1["x"]-p2["x"], 2)
+ Math.pow(p1["y"]-p2["y"], 2));
}
/**
* Calculates the value of a cubic function at x
* @param {Number} x
* @return {Number} f(x)
*/
function getValue(x) {
var a = parseFloat(document.getElementById("a").value);
var b = parseFloat(document.getElementById("b").value);
var c1 = parseFloat(document.getElementById("c").value);
return a*x*x+b*x+c1;
}
/**
* Calculates the drivate f'(x)
* @param {Number} x
* @return {Number} f'(x)
*/
function getDValue(x) {
var a = parseFloat(document.getElementById("a").value);
var b = parseFloat(document.getElementById("b").value);
return 2*a*x+b;
}
/**
* Calculates the drivate f''(x)
* @param {Number} x
* @return {Number} f''(x)
*/
function getDDValue(x) {
var a = parseFloat(document.getElementById("a").value);
return 2*a;
}
/**
* Calculates (f(x)^2)' = ((a*x*x+b*x+c)^2)' = 2 (b + 2 a x) (c + x (b + a x))
* @param {Number} x
* @return {Number} (f(x)^2)'
*/
function gedSquaredValueD(x) {
var a = parseFloat(document.getElementById("a").value);
var b = parseFloat(document.getElementById("b").value);
var c1 = parseFloat(document.getElementById("c").value);
return 2*(2*a*x+b)*(x*(a*x+b)+c1);
}
/**
* Calculates (f(x)^2)'' = ((a*x*x+b*x+c)^2)'' = 2 (b^2 + 6 a b x + 2 a (c + 3 a x^2))
* @param {Number} x
* @return {Number} (f(x)^2)''
*/
function gedSquaredValueDD(x) {
var a = parseFloat(document.getElementById("a").value);
var b = parseFloat(document.getElementById("b").value);
var c1 = parseFloat(document.getElementById("c").value);
return 2*(b*b+6*a*b*x+2*a*(c1+3*a*x*x));
}
function findMin(p) {
var a = parseFloat(document.getElementById("a").value);
var b = parseFloat(document.getElementById("b").value);
var c1 = parseFloat(document.getElementById("c").value);
var currentMinX = p.x;
for (var i=0; i < 10; i++) {
// Funktionswert
var fx = -2.0*p.x+2.0*currentMinX-2.0*p.y*getDValue(currentMinX) +gedSquaredValueD(currentMinX);
var fxd = 2.0 -2.0*p.y*getDDValue(currentMinX)+gedSquaredValueDD(currentMinX);
if (Math.abs(fxd) < 0.0001) {
return currentMinX;
}
// x_{n+1} = x_n - f(x_n)/f'(x_n) wenn x gesucht, sodass f(x) = 0 gilt
currentMinX -= fx / fxd;
}
return currentMinX;
}
function getDist(p, minX) {
var minY = getValue(minX);
return euklideanDist({"x":minX, "y":minY}, {"x":p.x, "y":p.y});
}
/*******************************************************************/
/* Start / Update */
/*******************************************************************/
function drawBoard(canvas, mouseCoords, radius) {
var context = canvas.getContext('2d');
context.canvas.width = window.innerWidth - 50;
context.canvas.height = window.innerHeight - 120;
context.clearRect(0, 0, canvas.width, canvas.height);
drawQuadraticFunction(canvas);
if (document.getElementById("pDistance").checked) {
var add = parseInt(document.getElementById("density").value)+10;
for (var x=0; x < canvas.width; x+=add) {
for (var y=0; y < canvas.height; y+=add) {
var dist = getDist({"x":r(x,true), "y":r(y,false)}, findMin({"x":r(x,true), "y":r(y,false)}));
context.fillStyle = getColor(dist,0.5);
context.fillRect(x, y, add/2, add/2);
}
}
}
}
function updateBoard(){
var canvas = document.getElementById("myCanvas");
STRETCH_X = parseFloat(document.getElementById("STRETCH_X").value);
STRETCH_Y = parseFloat(document.getElementById("STRETCH_Y").value);
X_OFFSET = parseFloat(document.getElementById("X_OFFSET").value);
Y_OFFSET = parseFloat(document.getElementById("Y_OFFSET").value);
drawBoard(canvas, {"x":0,"y":0}, INITIAL_RADIUS);
}
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
drawBoard(canvas, {"x":0,"y":0}, INITIAL_RADIUS);
setCursorByID("myCanvas", "crosshair");
/** get the current position of the mouse */
function getMouseCoords(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
"x": evt.clientX - rect.left,
"y": evt.clientY - rect.top
};
}
/** event listeners */
canvas.addEventListener('mousemove',
function (evt) {
var mouseCoords = getMouseCoords(canvas, evt);
drawBoard(canvas, mouseCoords, 10);
// draw coordinates next to mouse
context.fillStyle = "blue";
context.font = "bold 16px Arial";
var x = r(mouseCoords.x, true).toFixed(3);
var y = r(mouseCoords.y, false).toFixed(3);
context.fillText("(" + x + ", " + y + ")", mouseCoords.x + 5, mouseCoords.y - 5);
var minX = findMin({"x":x, "y":y});
var minY = getValue(minX);
context.beginPath();
context.moveTo(c(minX, true), c(minY, false), false);
context.lineTo(mouseCoords.x, mouseCoords.y, false);
context.stroke();
var minRadius = getDist({"x":x, "y":y}, minX);
/* Draw circle */
drawEllipse(mouseCoords.x, mouseCoords.y, minRadius*STRETCH_X, minRadius*STRETCH_Y);
}, false);