|
ru.algorithms- RU.ALGORITHMS ---------------------------------------------------------------- From : Nick Gorev 2:5020/400 25 Jan 2002 07:06:42 To : Nick Gorev Subject : Реализация с помощью метода Штурма -------------------------------------------------------------------------------- Hello, All! Меня попросили подробнее рассказать решение. Hачну с постановки задачи: Имеется тор, стандартно вложенный в R^3: Окружность радиуса r c центром в точке (0,p,0), лежащая в плоскости XOZ вращается вдоль окружности с центром в точке (0,0,0) радиуса p, лежащей в плоскости XOY. Получается тор (поверхность бублика). Алгебраическое уравнение этой поверхности: (x^2 + y^2)^2 + 2*(z^2 - p^2 - r^2)*(x^2 + y^2) + (z^2 + p^2 - r^2)^2 = 0 Имеется отрезок с концами (x0,y0,z0) и (x1,y1,z1). Требуется определить, пересекаются ли отрезок с тором. Параметрическое уравнение отрезка x(t) = x0 + t * vx y(t) = y0 + t * vy z(t) = z0 + t * vz где vx = x1 - x0 vy = y1 - y0 vz = z1 - z0 параметр t пробегает отрезок [0..1]. Подставляем выражения для x(t), y(t), z(t) в уравнение тора и получаем алгебраическое уравнение 4-й степени относительно переменной t. f(t) = a0 + a1*t + a2*t^2 + a3*t^3 + a4*t^4 = 0 где a0 = (x0^2 + y0^2 + z0^2 - 2*r^2)*(x0^2 + y0^2 + z0^2) + 2*p^2*(z0^2 - y0^2 - x0^2) + (p^2 + r^2)^2 a1 = 4*p^2*(z0*vz - x0*vx - y0*vy) + 4*(x0^2 + y0^2 + z0^2 - r^2)*(x0*vx + y0*vy + z0*vz) a2 = 2*(x0^2 + y0^2 + z0^2 - r^2)*(vx^2 + vy^2 + vz^2) + 4*(x0*vx + y0*vy + z0*vz)*(x0*vx + y0*vy + z0*vz) + 2*p^2*(vz^2 - vx^2 - vy^2) a3 = 4*(vx^2 + vy^2 + vz^2)*(x0*vx + y0*vy + z0*vz) a4 = (vx^2 + vy^2 + vz^2)^2 Случай когда отрезок имеет нулевую длину можно сразу исключить. Поэтому можно считать, что a4=/=0. Теперь надо узнать, есть ли у этого уравнения корни на отрезке [0..1]. Для этого используется метод Штурма. Hапомню его. Строится последовательность многочленов вида: f_0(t) = f(t) = g_0(t) * f_1(t) - f_2(t), f_1(t) = f'(t) = g_1(t) * f_2(t) - f_3(t), f_2(t) = g_2(t) * f_3(t) - f_4(t), ..., где при i>1 каждый многочлен f_i есть взятый с коэффициентом -1 остаток, получаемый при делении f_(i-2) на f_(i-1). Далее, N(t) есть число перемен знака в последовательности значений этих многочленов (члены, обращающиеся в нуль, не учитываются). Тогда число N(a) - N(b) есть число корней между a и b, причем каждый кратный корень считается только один раз. В нашем случае имеем: f_1(t) = b0 + b1*t + b2*t^2 + b3*t^3 = f'(t) = a1 + 2*a2*t + 3*a3*t^2 + 4*a4*t^3 Остальные многочлены последовательности находим делением многочленов. Это легко сделать вручную, а ленивым (вроде меня) на помощь приходит функция rem пакета Maple. f_2(t) = c0 + c1*t + c2*t^2 f_3(t) = d0 + d1*t f_4(t) = e0 Hу я уж не буду выписывать здесь значения коэффициентов c0, c1,... (см. ниже листинг программы), замечу только, что остатки от деления многочленов могут получаться с нулевыми старшими коэффициентами. В этом случае последовательность получается короче, и это тоже надо учитывать. Реализация: /* Функция, определяющая пересекается ли отрезок с тором x0,y0,z0,x1,y1,z1 - координаты концов отрезка r - радиус образующей тора p - радиус направляющей тора (образующая - это маленькая окружность, вращая ее вдоль большой направляющей окружности, мы и получаем тор) Функция возвращает ноль, если пересечения нет */ #include <math.h> #define eps 1.e-20 // типа ноль int CrossTor(double x0, double y0, double z0, double x1, double y1, double z1, double r, double p) { double vx, vy, vz, lv2, xv, lx2, a0, a1, a2, a3, a4, b0, b1, b2, b3, c0, c1, c2, d0, d1, e0, v0[5], v1[5], w0[5], w1[5]; int s1, s0, I, N0, N1; vx = x1 - x0; vy = y1 - y0; vz = z1 - z0; lv2 = vx * vx + vy * vy + vz * vz; if(lv2 < eps) return 0; xv = x0 * vx + y0 * vy + z0 * vz; lx2 = x0 * x0 + y0 * y0 + z0 * z0; for(I = 0; I < 5; I++) { v0[I] = 0.; v1[I] = 0.; } // Hулевой многочлен последовательности Штурма - 4-й степени a4 = lv2 * lv2; a3 = 4. * lv2 * xv; a2 = 2. * (lx2 - r * r) * lv2 - 2. * p * p * (lv2 - 2. * vz * vz) + 4. * xv * xv; a1 = -4. * p * p * (xv - 2. * z0 * vz) + 4. * (lx2 - r * r) * xv; a0 = (lx2 - 2. * r * r) * lx2 + (r * r - p * p) * (r * r - p * p) - 2. * p * p * (lx2 - 2. * z0 * z0); v0[0] = a0; v1[0] = a0 + a1 + a2 + a3 + a4; // Первый многочлен последовательности - производная нулевого - // 3-й степени b0 = a1; b1 = 2. * a2; b2 = 3. * a3; b3 = 4. * a4; v0[1] = b0; v1[1] = b0 + b1 + b2 + b3; // Второй многочлен - взятый со знаком минус остаток // от деления нулевого многочлена на первый c0 = a1 * a3 / (a4 * 16.) - a0; c1 = a2 * a3 / (a4 * 8.) - 0.75 * a1; c2 = 0.1875 * a3 * a3 / a4 - 0.5 * a2; v0[2] = c0; v1[2] = c0 + c1 + c2; if(fabs(c2) <= eps) { if(fabs(c1) > eps) { d0 = c0 * b1 / c1 - c0 * c0 * b2 / (c1 * c1) + c0 * c0 * c0 * b3 / (c1 * c1 * c1) - b0; v0[3] = d0; v1[3] = d0; } } else { d0 = c0 * b2 / c2 - b0 - c0 * b3 * c1 / (c2 * c2); d1 = b3 * c0 / c2 - b1 + c1 * b2 / c2 - b3 * c1 * c1 / (c2 * c2); v0[3] = d0; v1[3] = d0 + d1; if(fabs(d1) > eps) { e0 = d0 * c1 / d1 - c0 - d0 * d0 * c2 / (d1 * d1); v0[4] = e0; v1[4] = e0; } } // Избавляемся от нулей в последовательности Штурма s0 = 0; s1 = 0; for(I = 0; I < 5; I++) { if(v0[I] != 0.) {w0[s0] = v0[I]; s0++;} if(v1[I] != 0.) {w1[s1] = v1[I]; s1++;} } // Вычисление числа перемен знака N0 = 0; N1 = 0; for(I = 1; I <= s0; I++) if(w0[I] * w0[I - 1] < 0.) N0++; for(I = 1; I <= s1; I++) if(w1[I] * w1[I - 1] < 0.) N1++; // Возвращаем число корней return N0 - N1; } With best regards, Nick Gorev. E-mail: NickGorev@mtu-net.ru --- ifmail v.2.15dev5 * Origin: MTU-Intel ISP (2:5020/400) Вернуться к списку тем, сортированных по: возрастание даты уменьшение даты тема автор
Архивное /ru.algorithms/9104947a37fe.html, оценка из 5, голосов 10
|