Skip to Content

Encontrar las tangentes comunes a dos círculos

Se dan dos círculos. Se requiere encontrar todas sus tangentes comunes, es decir, todas las rectas que tocan ambos círculos a la vez.

El algoritmo descrito también funcionará en el caso en que uno (o ambos) círculos degeneren en puntos. Así, este algoritmo también se puede usar para encontrar tangentes a un círculo que pasan por un punto dado.

El número de tangentes comunes

El número de tangentes comunes a dos círculos puede ser 0,1,2,3,4 e infinito. Véanse las imágenes para los distintos casos.

Distintos casos de tangentes comunes a dos círculos

Aquí no consideraremos los casos degenerados, es decir, cuando los círculos coinciden (en este caso tienen infinitas tangentes comunes), o un círculo yace dentro del otro (en este caso no tienen tangentes comunes, o si los círculos son tangentes, hay una tangente común).

En la mayoría de los casos, dos círculos tienen cuatro tangentes comunes.

Si los círculos son tangentes, entonces tendrán tres tangentes comunes, pero esto se puede entender como un caso degenerado: como si las dos tangentes coincidieran.

Además, el algoritmo descrito a continuación funcionará en el caso en que uno o ambos círculos tengan radio cero: en este caso habrá, respectivamente, dos o una tangente común.

Resumiendo, siempre buscaremos cuatro tangentes para todos los casos excepto el de infinitas tangentes (el caso de infinitas tangentes hay que manejarlo por separado y no se discute aquí). En los casos degenerados, algunas de las tangentes coincidirán, pero no obstante estos casos también encajarán en el panorama general.

Algoritmo

Por simplicidad del algoritmo, asumiremos, sin pérdida de generalidad, que el centro del primer círculo tiene coordenadas (0,0)(0, 0). (Si no es así, esto se puede lograr simplemente trasladando toda la figura, y después de encontrar una solución, trasladando de vuelta las rectas obtenidas.)

Denotemos r1r_1 y r2r_2 los radios del primer y del segundo círculo, y por (vx,vy)(v_x,v_y) las coordenadas del centro del segundo círculo y el punto vv distinto del origen. (Nota: no estamos considerando el caso en el que ambos círculos son el mismo).

Para resolver el problema, lo abordamos de forma puramente algebraica. Necesitamos encontrar todas las rectas de la forma ax+by+c=0ax + by + c = 0 que yazcan a distancia r1r_1 del origen de coordenadas, y a distancia r2r_2 de un punto vv. Además, imponemos la condición de normalización de la recta: la suma de los cuadrados de los coeficientes debe ser igual a uno (esto es necesario, de lo contrario la misma recta correspondería a infinitas representaciones de la forma ax+by+c=0ax + by + c = 0). En total obtenemos el siguiente sistema de ecuaciones para los a,b,ca, b, c buscados:

a2+b2=1a0+b0+c=r1avx+bvy+c=r2a2+b2amp;=1a0+b0+camp;=r1avx+bvy+camp;=r2\begin{align} a^2 + b^2 &= 1 \ \mid a \cdot 0 + b \cdot 0 + c \mid &= r_1 \ \mid a \cdot v_x + b \cdot v_y + c \mid &= r_2 \end{align}

Para deshacernos del valor absoluto, nótese que solo hay cuatro formas de abrir el valor absoluto en este sistema. Todos estos métodos se pueden considerar por el caso general, si entendemos la apertura del valor absoluto como el hecho de que el coeficiente del lado derecho puede multiplicarse por -1. En otras palabras, pasamos a este sistema:

a2+b2=1c=±r1avx+bvy+c=±r2a2+b2amp;=1camp;=±r1avx+bvy+camp;=±r2\begin{align} a^2 + b^2 &= 1 \ c &= \pm r_1 \ a \cdot v_x + b \cdot v_y + c &= \pm r_2 \end{align}

Introduciendo la notación d1=±r1d_1 = \pm r_1 y d2=±r2d_2 = \pm r_2, llegamos a la conclusión de que el sistema debe tener cuatro soluciones:

a2+b2=1c=d1avx+bvy+c=d2a2+b2amp;=1camp;=d1avx+bvy+camp;=d2\begin{align} a^2 + b^2 &= 1 \ c &= d_1 \ a \cdot v_x + b \cdot v_y + c &= d_2 \end{align}

La solución de este sistema se reduce a resolver una ecuación cuadrática. Omitiremos todos los cálculos engorrosos y daremos de inmediato una respuesta lista:

a=(d2d1)vx±vyvx2+vy2(d2d1)2vx2+vy2b=(d2d1)vy±vxvx2+vy2(d2d1)2vx2+vy2c=d1aamp;=(d2d1)vx±vyvx2+vy2(d2d1)2vx2+vy2bamp;=(d2d1)vy±vxvx2+vy2(d2d1)2vx2+vy2camp;=d1\begin{align} a &= {( d_2 - d_1 ) v_x \pm v_y \sqrt{v_x^2 + v_y^2-(d_2-d_1)^2} \over {v_x^2 + v_y^2} } \ b &= {( d_2 - d_1 ) v_y \pm v_x \sqrt{v_x^2 + v_y^2-(d_2-d_1)^2} \over {v_x^2 + v_y^2} } \ c &= d_1 \end{align}

En total obtuvimos ocho soluciones en lugar de cuatro. Sin embargo, es fácil entender de dónde surgen las soluciones superfluas: de hecho, en el último sistema basta tomar solo una solución (por ejemplo, la primera). De hecho, el significado geométrico de tomar ±r1\pm r_1 y ±r2\pm r_2 es claro: en realidad estamos recorriendo de qué lado de cada círculo está la recta. Por tanto, los dos métodos que aparecen al resolver el último sistema son redundantes: basta elegir una de las dos soluciones (solo que, por supuesto, en los cuatro casos hay que elegir la misma familia de soluciones).

Lo último que aún no hemos considerado es cómo trasladar las rectas en el caso en que el primer círculo no estaba originalmente en el origen. Sin embargo, aquí todo es simple: de la linealidad de la ecuación de una recta se sigue que el valor ax0+by0a \cdot x_0 + b \cdot y_0 (donde x0x_0 e y0y_0 son las coordenadas del centro original del primer círculo) hay que restarlo del coeficiente cc.

Implementación

Primero describimos todas las estructuras de datos necesarias y otras definiciones auxiliares:

struct pt { double x, y; pt operator- (pt p) { pt res = { x-p.x, y-p.y }; return res; } }; struct circle : pt { double r; }; struct line { double a, b, c; }; const double EPS = 1E-9; double sqr (double a) { return a * a; }

Luego la solución misma se puede escribir así (donde la función principal a llamar es la segunda, y la primera función es auxiliar):

void tangents (pt c, double r1, double r2, vector<line> & ans) { double r = r2 - r1; double z = sqr(c.x) + sqr(c.y); double d = z - sqr(r); if (d < -EPS) return; d = sqrt (abs (d)); line l; l.a = (c.x * r + c.y * d) / z; l.b = (c.y * r - c.x * d) / z; l.c = r1; ans.push_back (l); } vector<line> tangents (circle a, circle b) { vector<line> ans; for (int i=-1; i<=1; i+=2) for (int j=-1; j<=1; j+=2) tangents (b-a, a.r*i, b.r*j, ans); for (size_t i=0; i<ans.size(); ++i) ans[i].c -= ans[i].a * a.x + ans[i].b * a.y; return ans; }

Problemas

TIMUS 1163 Chapaev