Martin Thoma 11 lat temu
rodzic
commit
1bcec68935

+ 2 - 1
documents/math-minimal-distance-to-cubic-function/Makefile

@@ -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

+ 58 - 16
documents/math-minimal-distance-to-cubic-function/math-minimal-distance-to-cubic-function.tex

@@ -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}

+ 46 - 0
documents/math-minimal-distance-to-cubic-function/quadratic-min-visualization/quadratic-vis.html

@@ -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>

+ 257 - 0
documents/math-minimal-distance-to-cubic-function/quadratic-min-visualization/quadratic-vis.js

@@ -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);