Этот параграф посвящен, прежде всего, решению одной конкретной задачи
средней
степени сложности — нахождению
В нем также рассматриваются аплеты, которые используются в качестве основы для создания простейшего графического интерфейса (GUI — graphics user interface), что дает возможность решать задачи на изображение графиков функций и различных графических объектов. Вопросы создания аплетов обсуждаются практически во всех изданиях, посвященных языку Java, в том числе и в книгах [10], [11] и [13].
Напомним сначала два определения, связанные с важнейшим математическим понятием — свойством выпуклости множеств.
Определение 11.1.
Множество $$M$$ называется
Примеры выпуклых множеств: отрезок прямой, прямоугольник и круг на плоскости, куб, шар и пирамида в пространстве. Выпуклыми множествами не являются окружность, граница квадрата и тор (прямое произведение двух окружностей).
Определение 11.2.
Теперь можно сформулировать основную задачу.
Задача 11.1. Напишите программу, находящую
Если представить себе точки конечного множества
в виде вбитых в доску гвоздей, то
(рис 11.1) Выпуклая оболочка точек плоскостиМы будем трактовать поставленную задачу следующим образом: реализовать
класс Convex с методами
add — добавить новую точку и построить выпуклую
оболочку;perimeter — вычислить периметр;area — вычислить площадь.Договоримся придерживаться стратегии вычисления всех характеристик оболочки
сразу при добавлении новой точки (в методе add ); выполнение методов perimeter и area при этом будет сводиться просто к выдаче уже
вычисленных ранее значений.
Если обозначить множество $$\mathbb{R}^2$$ точек плоскости через $$X$$, а
совокупность всех выпуклых фигур на плоскости через $$\mathcal{P}$$,
то
тройка функций $$f\colon X^* \rightarrow \mathcal{P}$$
Функцию перевычисления $$G$$ для нее более точно мы опишем чуть
позже, а пока
ограничимся следующим предварительным рассуждением. Пусть для некоторой
последовательности точек плоскости
В первом случае
(рис 11.2) Добавление новой точкиХорошей моделью для описания изменяющейся оболочки является следующая:
предположим, что во вновь добавляемой точке $$x$$ расположена
лампочка,
освещающая часть ребер старой оболочки, которые мы так и будем называть —
Если добавляемая точка лежит на продолжении одного из ребер, то оболочка должна измениться так, как показано на рис. 11.3. Поэтому ребро, на продолжении которого лежит точка $$x$$, мы также будем считать освещенным.
До сих пор рассматривался только общий случай, когда
(рис 11.3) Добавление точки — специальный случайДля разработки необходимой иерархии классов в данном случае достаточно
воспользоваться давно известным методом — проектированием сверху вниз.
Уточненная постановка задачи требует создания класса Convex, необходим
еще и класс, содержащий метод main с тестирующей программой. Можно,
конечно, добавить этот метод в класс Convex, но мы для этих целей
создадим отдельный класс ConvexTest.
Интерфейс Figure должен содержать общие для всех возможных случаев
выпуклой
оболочки методы add, perimeter и area, а его реализациями
будут являться классы "нульугольник" ( Void ),
"одноугольник"
( Point ), "двуугольник" ( Segment ) и многоугольник ( Polygon ).
Каждый из этих классов обязан обеспечивать реализацию всех методов интерфейса Figure, при этом метод add должен в качестве аргумента получать
добавляемую точку, а возвращать
Следовательно, необходим класс, который мы назовем R2Point,
представляющий точку на плоскости и обеспечивающий все необходимые действия
над объектами этого типа. Его вполне достаточно для реализации
классов Void, Point и Segment, ибо в первом случае никакой
информации в фигуре хранить вообще не нужно, а в двух последних нужно хранить
только один (для "одноугольника") или два (для
"двуугольника") экземпляра
класса R2Point, представляющие соответственно единственную или две
концевых точки этих выпуклых оболочек.
Для класса Polygon, однако, необходимо нечто большее — вершины
многоугольника необходимо хранить в каком-то контейнере. Для этих целей
особенно хорошо в данном случае подходит дек (это будет понятно уже после
того, как программа будет полностью написана), хотя, вообще говоря, можно
воспользоваться и другим контейнером.
Итак, класс Deq будет представлять собой непрерывную реализацию R2Point, а класс Polygon можно просто сделать
выведенным (дочерним) для него. При этом класс Polygon оказывается
одновременно реализующим ( implements ) интерфейс Figure и
расширяющим ( extends ) класс Deq.
(рис 11.4) Иерархия классов проекта "Выпуклая оболочка"Это завершает проектирование иерархии классов, необходимой для решения поставленной задачи. Результат представлен на рис. 11.4.
В заключение данной секции разберемся с тем, как должны быть устроены
реализации классов Void, Point и Segment. Для
"нульугольника" все очевидно — методы perimeter и area
возвращают нулевые значения, а возвращаемым значением метода add
должен быть объект типа Point.
Класс "одноугольник" уже должен иметь конструктор с аргументом
типа R2Point, сохраняющий эту точку в своей private -компоненте.
Периметр и площадь здесь также являются нулевыми, а метод add
может возвратить как объект типа Segment, так и неизмененный
объект Point. Последнее должно иметь место в случае совпадения
вновь добавляемой точки с уже имеющейся. Таким образом, реализация класса Point требует наличия в классе R2Point метода equal,
позволяющего сравнивать точки плоскости на равенство. Напомним, что оператор == для объектов ссылочных типов возвращает true только в случае
равенства ссылок, а не внутреннего содержания объектов.
Класс "двуугольник" обязан иметь две компоненты типа R2Point,
в которые конструктором класса заносятся его концевые точки. Площадь
любого "двуугольника" нулевая, а периметром естественно считать удвоенную длину отрезка (сравните "двуугольник" с
треугольником).
Добавление точки в данном случае может
привести к трем различным ситуациям: "двуугольник" может
превратиться в
треугольник, он может остаться "двуугольником", но изменить одну
из своих
концевых точек, и, наконец, он может совсем не измениться.
Класс R2Point должен обеспечивать методы, которые позволят различать
эти ситуации, а итоговый вариант реализации классов Void, Point и Segment приведен в конце лекции.
В предыдущей секции мы столкнулись с необходимостью реализовать в классе R2Point ряд методов, которые позволяли бы вычислять расстояния
между точками, сравнивать их на совпадение, выяснять, лежат ли три точки
на одной прямой и не находится ли некоторая точка на прямой между
двумя другими. Для реализации класса Polygon необходимо также уметь
вычислять площадь треугольника и находить все освещенные ребра
многоугольника. Решение задач на модификацию эталонного проекта потребует
реализации еще целого ряда методов, связанных с различными геометрическими
характеристиками.
Ряд фактов из аналитической геометрии и векторной алгебры
являются
необходимой базой для решения сформулированных задач, однако только знание
элементов вычислительной геометрии позволяет получить достаточно
эффективные решения.
Простейшим примером может служить задача вычисления площади треугольника по
известным координатам его вершин. Известная из средней школы формула Герона$$S = \sqrt{p (p-a) (p-b) (p-c)}$$
требует большого числа умножений и четырех относительно
медленно выполняемых операций извлечения квадратного корня. Основанная на
связи площади с
Большую часть методов, о которых будет идти речь в текущей секции,
целесообразно реализовать в виде методов класса (статических методов).
Напомним, что такой метод не получает в качестве неявного аргумента
конкретный экземпляр класса, и не может использовать ключевое слово this.
Расстояние $$d$$ между двумя точками на плоскости можно посчитать
по известной
формуле $$d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$$, которая и
используется в
реализации метода dist. Два объекта типа R2Point соответствуют
совпадающим точкам плоскости тогда и только тогда, когда попарно равны их
координаты, что позволяет реализовать метод equal.
public static double dist(R2Point a, R2Point b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
public static boolean equal(R2Point a, R2Point b) {
return a.x==b.x a.y==b.y;
}
public static double area(R2Point a, R2Point b, R2Point c) {
return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}
Для вычисления площади треугольника в методе area применена уже
цитированная выше формула, основанная на следующем факте из векторной алгебры:
модуль векторного произведения двух векторов равен площади параллелограмма,
натянутого на эти вектора. Напомним, что
Часто полезно использовать запись этого определения в координатах. Если $$\vec a = (a_x, a_y, a_z)$$, а $$\vec b = (b_x, b_y, b_z)$$, то координаты вектора $$\vec c$$ находятся по формуле$$\vec c = \left| \begin{array}{ccc} \vec i \vec j \vec k\\ a_x a_y a_z \\ b_x b_y b_z \end{array} \right| = (a_y b_z - a_z b_y) \vec i + (a_z b_x - a_x b_z) \vec j + (a_x b_y - a_y b_x) \vec k.$$
В том случае, когда вектора $$\vec a$$ и $$\vec b$$
расположены на плоскости, их
area. Обратите внимание на то, что так
вычисленная площадь является ориентированной, то есть может быть
отрицательной.
Выяснение того факта, лежат ли три точки на одной прямой, сводится к вычислению площади треугольника и сравнению ее с нулем, а для того, чтобы точка $$t$$ прямой располагалась на отрезке $$[a,b]$$ этой же прямой, необходимо и достаточно принадлежности проекций этой точки проекциям на оси координат отрезка $$[a,b]$$. Вот как выглядит программная реализация этих идей.
public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
return area(a, b, c) != 0.0;
}
public boolean inside(R2Point a, R2Point b) {
return (a.x <= x x <= b.x || a.x >= x x >= b.x)
(a.y <= y y <= b.y || a.y >= y y >= b.y);
}
(рис 11.5) Освещенность ребра из точкиПеред тем, как реализовать метод light, позволяющий выяснить,
освещено ли
ребро $$[a,b]$$
Определение 11.3.
Ребро $$[a,b]$$ называется
Заметим, что при таком формальном определении понятия освещенности никакое ребро многоугольника не освещено ни из одной точки внутри или на границе многоугольника, что хорошо видно на рисунке рис. 11.5.
public boolean light(R2Point a, R2Point b) {
double s = area(a, b, this);
return s < 0.0 || ( s == 0.0 ! inside(a, b));
}
В заключение этой секции рассмотрим более сложный вопрос — покажем, как можно наиболее эффективно выяснить, пересекаются ли два отрезка на плоскости, заданные координатами четырех их граничных точек.
На первом шаге решения этой задачи необходимо выяснить, пересекаются ли
Пусть координаты граничных точек первого отрезка $$\overrightarrow{P_1
P_2}$$
равны $$(x_1, y_1)$$ и $$(x_2, y_2)$$, а второго $$\overrightarrow{P_3 P_4}$$ — $$(x_3, y_3)$$ и $$(x_4, y_4)$$. Тогда координаты
левого нижнего угла
Если
Можно доказать (это рекомендуется сделать самостоятельно), что два отрезка пересекаются тогда и только тогда, когда пересекаются их ограничивающие прямоугольники и, кроме того, каждый из них пересекается с прямой, содержащей другой отрезок.
Проверка факта пересечения отрезка с прямой осуществляется с помощью
Таким образом, отрезки $$\overrightarrow{P_1 P_2}$$ и $$\overrightarrow{P_3 P_4}$$ пересекаются тогда и только тогда, когда одновременно выполняются следующие три условия:
1) пересекаются ограничивающие их прямоугольники;
2) $$(\overrightarrow{P_1 P_3} \times \overrightarrow{P_1 P_2}) \cdot (\overrightarrow{P_1 P_4} \times \overrightarrow{P_1 P_2}) \leqslant 0;$$
3) $$(\overrightarrow{P_3 P_1} \times \overrightarrow{P_3 P_4}) \cdot (\overrightarrow{P_3 P_2} \times \overrightarrow{P_3 P_4}) \leqslant 0$$.
Более подробное изложение решения этой и ряда других подобных задач может быть найдено в разделе "Вычислительная геометрия" книги [8].
Нам осталось реализовать методы perimeter, area и add
класса Polygon. Как это уже отмечалось при уточнении постановки задачи,
вся работа по модификации perimeter и area будет сводиться просто к выдаче уже вычисленных
ранее значений, хранящихся в private -компонентах.
class Polygon extends Deq implements Figure {
private double s, p;
public double perimeter() {
return p;
}
public double area() {
return s;
}
...
}
Для того чтобы реализовать метод add и конструктор класса Polygon,
необходимо аккуратно разобраться с тем, как именно вершины
Это должно учитываться в конструкторе, который создает простейший
из многоугольников, — треугольник, размещая заданные ему вершины в Math.abs, находящего
абсолютное значение своего аргумента.
public Polygon(R2Point a, R2Point b, R2Point c) {
pushFront(b);
if (b.light(a, c)) {
pushFront(a); pushBack(c);
} else {
pushFront(c); pushBack(a);
}
p = R2Point.dist(a, b) + R2Point.dist(b, c)
+ R2Point.dist(c, a);
s = Math.abs(R2Point.area(a, b, c));
}
Назовем ребро $$AB$$ (см. рис. рис. 11.6), соединяющее конец
(рис 11.6) Добавление новой точкиПри добавлении новой точки возможны два случая. Если в многоугольнике
освещенных ребер нет, то это означает, что новая точка попала внутрь или на
границу старой
При реализации метода add в этом случае необходимо
для всех освещенных ребер
Пересчет периметра и площади, который необходимо производить для каждого из
удаляемых ребер, выделен из соображений оптимизации в отдельный private -метод grow.
private void grow(R2Point a, R2Point b, R2Point t) {
p -= R2Point.dist(a, b);
s += Math.abs(R2Point.area(a, b, t));
}
Полный текст построенной программы приведен ниже, в последней секции текущего параграфа.
До сих пор мы не использовали богатейших возможностей, предоставляемых
различными библиотеками языка Java, которые позволяют создавать удобные
графические интерфейсы (GUI), ограничиваясь вводом/выводом текстовой
информации с помощью класса . Вопросы создания полноценных
графических интерфейсов выходят за рамки нашего курса, однако с простейшими
методами вывода графической информации познакомиться весьма полезно.
Наилучший способ для этих целей — использование main и не может быть запущен самостоятельно.
Аплеты запускаются с помощью браузера или специализированной программы для
просмотра аплетов , а для создания аплетов необходимо
реализовывать подкласс класса Applet, определенного в пакете java.applet, являющимся составной частью библиотеки
Для целей вывода графической информации достаточно в классе, выведенном из
класса Applet, переопределить метод paint. Мы познакомимся только
с несколькими простейшими графическими примитивами:
setColor — установить цвет, которым будут
изображаться выводимые объекты;drawLine — изобразить отрезок прямой линии;drawRect — изобразить границу прямоугольника;drawOval — изобразить границу овала;fillRect — изобразить заполненный прямоугольник;fillOval — изобразить заполненный овал.В качестве первого примера рассмотрим решение следующей задачи.
Задача 11.2. Создайте аплет,изображающий в окне размером 200х200 заполненный черный и синий прямоугольники, заполненный синий круг, красный отрезок, прямоугольник и овал.
Вот одно из возможных решений этой задачи.
Текст аплета
import java.awt.*;
import java.applet.*;
public class Primitives extends Applet {
public void paint (Graphics g) {
g.fillRect(20, 20, 40, 60);
g.setColor(Color.red);
g.drawLine(2, 2, 80, 80);
g.drawOval(120, 120, 30, 40);
g.drawRect(170, 170, 10, 15);
g.setColor(Color.blue);
g.fillOval(20, 150, 30, 30);
}
}
Операторы import обеспечивают подключение необходимых классов
библиотеки , аплет
можно запустить только создав дополнительно еще один файл — . Именно он должен быть указан в качестве аргумента
программе или браузеру. Содержимое этого файла может быть
примерно таким.
HTML-файл
<HTML><BODY> <APPLET code="Primitives.class" width=200 height=200></APPLET> </BODY></HTML>
Содержательная информация здесь сводится к указанию имени запускаемого аплета и размеру окна, в котором этот аплет будет работать. После создания данного файла можно, наконец, запустить аплет, выполнив команду
appletviewer Primitives.html
Заметим, что начало координат размещается в левом верхнем углу окна, а ось ординат направлена вниз. Других комментариев созданный нами аплет не требует.
Рассмотрим более интересную задачу на построение графика функции.
Задача 11.3. Создайте аплет,изображающий в окне размером 200х200 оси
координат и график функции y=x2-x на промежутке [-2,2].
Будем рисовать график этой функции путем изображения отдельных точек,
применяя метод drawOval с равными единице третьим и четвертым
аргументами, определяющими размер овала. Коэффициент $$k$$ позволяет
изобразить
график в нужном масштабе.
Текст аплета
import java.awt.*;
import java.applet.*;
public class FuncGraph extends Applet {
private static final int SIZE = 200;
private static final double k = 50.;
private int f(double x) {
return SIZE/2 - (int)(k * (x*(x*x-1)));
}
public void paint (Graphics g) {
g.drawLine(0, SIZE/2, SIZE-1, SIZE/2);
g.drawLine(SIZE/2, 0, SIZE/2, SIZE-1);
g.setColor(Color.red);
for (int i=0; i<SIZE; i++) {
double x = (i - SIZE/2) / k;
g.drawOval(i, f(x), 1, 1);
}
}
}
Файл для запуска аплета вполне аналогичен предыдущему случаю:
HTML-файл
<HTML><BODY> <APPLET code="FuncGraph.class" width=200 height=200></APPLET> </BODY></HTML>
Построим теперь график функции, заданной в полярных координатах.
Задача 11.4. Создайте аплет,изображающий в окне размером 200х200 график функции, задаваемой в полярных координатах $$(r,\varphi)$$ соотношением $$r = \sin 3\varphi$$
Для решения задачи нужно знать, что константа $$\pi$$ доступна
программам на
языке Java, как величина Math.PI, а функции $$\sin$$ и $$\cos$$ —
как методы Math.sin и Math.cos соответственно. На сей раз будем
рисовать график, изображая отрезки прямых, соединяющие последовательно
вычисляемые точки графика.
(рис 11.7) Результат работы аплетаТекст аплета
import java.awt.*;
import java.applet.*;
public class FuncPolGraph extends Applet {
private static final int SIZE = 200;
private static final double k = 95.;
private int x(double fi) {
return SIZE/2 + (int)(k * Math.sin(3.*fi) * Math.cos(fi));
}
private int y(double fi) {
return SIZE/2 - (int)(k * Math.sin(3.*fi) * Math.sin(fi));
}
public void paint (Graphics g) {
setBackground(Color.white);
g.setColor(Color.blue);
int xp = x(0.), yp = y(0.);
for (double fi=0.; fi<2.*Math.PI; fi+=Math.PI/180.) {
if (Math.sin(3.*fi) < 0)
continue;
g.drawLine(xp, yp, x(fi), y(fi));
xp = x(fi); yp = y(fi);
}
}
}
HTML-файл
<HTML><BODY> <APPLET code="FuncPolGraph.class" width=200 height=200></APPLET> </BODY></HTML>
Результат работы этой программы приведен на рисунке рис. 11.7.
Задача 11.5. Создайте аплет, изображающий в окне стандартный прямоугольник (со сторонами, параллельными осям координат), заштрихованный под углом $$45^\circ$$.
Задача 11.6. Создайте аплет, изображающий в окне границу объединения двух стандартных
прямоугольников (со сторонами, параллельными осям координат), координаты
углов которых предварительно вводятся с помощью методов класса
Задача 11.7. Создайте аплет, изображающий в окне размером 600x600 график функции, заданной параметрически: $$x = \sin 2\varphi$$, $$y = \cos 3 \varphi$$.
Задача 11.8. Создайте аплет, изображающий в окне размером 600x600 линии уровня функции $$z = xy$$.
Задача 11.9. Создайте аплет, изображающий в окне размером 600x600 траекторию движения шара с начальными координатами $$(x_1, x_2)$$ и скоростью $$(v_1, v_2)$$ в квадратном билльярде со стороной единичной длины.
Задача 11.10. Модифицируйте текст эталонного проекта "
a) среднее арифметическое значений функции $$\sin (xy)$$ в вершинах выпуклой оболочки;
b) максимальное значение функции $$\sin (xy)$$ в серединах сторон выпуклой оболочки;
c) мощность множества пересечения границы
d) площадь части
e) периметр части
f) количество ребер
g) количество ребер выпуклой оболочке, параллельных осям координат;
h) количество ребер выпуклой оболочке, параллельных сторонам заданного треугольника;
i) угол между максимальным и минимальным ребрами
j) лежит ли заданная точка плоскости внутри
Задача 11.11. Модифицируйте текст эталонного проекта "
a) расстояние от
b) расстояние от
c) расстояние от
d) расстояние от
e) расстояние от
f) среднее арифметическое расстояний от заданной точки до вершин выпуклой оболочки;
g) сумму квадратов расстояний от заданной точки до вершин
h) сумму квадратов расстояний от начала координат до середин сторон выпуклой оболочки;
i) количество пар вершин
j) количество пар сторон
Задача 11.12. Модифицируйте текст эталонного проекта "
a) находится ли единичная окружность с центром в начале координат внутри
b) количество вершин
c) количество вершин
d) минимальный стандартный прямоугольник, содержащий
e) максимальный стандартный прямоугольник, содержащийся в выпуклой оболочке;
f) радиус минимального круга с центром в заданной точке, содержащего
g) радиус максимального круга с центром в заданной точке, содержащегося в выпуклой оболочке;
h) диаметр
i) длину минимальной диагонали
j) координаты центра тяжести
Задача 11.13. Модифицируйте текст эталонного проекта "
a) мощность множества точек пересечения границы
b) мощность множества точек пересечения границы
c) мощность множества точек пересечения границы
d) мощность множества точек пересечения границы
e) мощность множества точек пересечения границы
f) мощность множества точек пересечения границы
g) мощность множества точек пересечения границы
h) мощность множества точек пересечения границы
i) мощность множества точек пересечения границы
j) мощность множества точек пересечения границы
Задача 11.14. Модифицируйте текст эталонного проекта "
a) площадь части
b) периметр части
c) площадь части
d) периметр части
e) площадь части
f) периметр части
g) площадь части
h) периметр части
i) площадь части
j) периметр части
Задача 11.15. Модифицируйте текст эталонного проекта "
a) количество вершин
b) количество вершин
c) количество вершин
d) количество вершин
e) количество вершин
f) количество вершин
g) количество вершин
h) количество вершин
i) количество вершин
j) количество вершин
Задача 11.16. Модифицируйте текст эталонного проекта "
a) количество ребер
b) количество ребер
c) количество ребер
d) количество ребер
e) количество ребер
f) количество ребер
g) количество ребер
h) количество ребер
i) количество ребер
j) количество ребер
Задача 11.17. Модифицируйте текст эталонного проекта "
a) угол, под которым
b) угол, под которым видно из начала координат самое длинное ребро
c) количество всех острых внутренних углов
d) количество внутренних острых углов
e) сумму всех внутренних углов
f) сумму внутренних углов
g) сумму углов, под которыми ребра
h) сумму углов, под которыми ребра
i) сумму углов, под которыми ребра
j) сумму углов, под которыми ребра
Задача 11.18. Модифицируйте текст эталонного проекта "
a) пересечение границы
b) часть
c)
d)
e)
f)
g) множество точек пересечения границы
h) множество точек пересечения границы
i) множество точек пересечения границы
j) множество точек пересечения границы
Задача 11.19. Модифицируйте текст эталонного проекта "
a) часть
b) часть
c) часть
d) часть
e) часть
f) часть
g) часть
h) часть
i) часть
j) часть
Данная секция содержит исходный текст программы, являющейся итогом работы
над проектом "
Эталонный проект
//Класс, описывающий точку (Point) на плоскости (R2).
class R2Point {
private double x, y;
public R2Point(double x, double y) {
this.x = x; this.y = y;
}
public R2Point() throws Exception {
x = Xterm.inputDouble("x -> ");
y = Xterm.inputDouble("y -> ");
}
public static double dist(R2Point a, R2Point b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
public static double area(R2Point a, R2Point b, R2Point c) {
return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}
public static boolean equal(R2Point a, R2Point b) {
return a.x==b.x a.y==b.y;
}
public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
return area(a, b, c) != 0.0;
}
public boolean inside(R2Point a, R2Point b) {
return (a.x <= x x <= b.x || a.x >= x x >= b.x)
(a.y <= y y <= b.y || a.y >= y y >= b.y);
}
public boolean light(R2Point a, R2Point b) {
double s = area(a, b, this);
return s < 0.0 || ( s == 0.0 ! inside(a, b));
}
}
// Непрерывная реализация дека.
class Deq {
private final static int DEFSIZE = 16;
private R2Point[] array;
private int size, head, tail;
private int forward(int index) {
return ++index < array.length ? index : 0;
}
private int backward(int index) {
return --index >= 0 ? index : array.length - 1;
}
public Deq(int size) {
array = new R2Point[size];
this.size = head = 0;
tail = array.length - 1;
}
public Deq() {
this(DEFSIZE);
}
public int length() {
return size;
}
public void pushFront(R2Point p) {
array[head=backward(head)] = p;
size += 1;
}
public void pushBack(R2Point p) {
array[tail=forward(tail)] = p;
size += 1;
}
public R2Point popFront() {
R2Point p = front();
head = forward(head);
size -= 1;
return p;
}
public R2Point popBack() {
R2Point p = back();
tail = backward(tail);
size -= 1;
return p;
}
public R2Point front() {
return array[head];
}
public R2Point back() {
return array[tail];
}
}
// Интерфейс, задающий новый тип - фигуру.
interface Figure {
public double perimeter();
public double area();
public Figure add(R2Point p);
}
// Класс "нульугольник", реализующий интерфейс фигуры.
class Void implements Figure {
public double perimeter() {
return 0.0;
}
public double area() {
return 0.0;
}
public Figure add(R2Point p) {
return new Point(p);
}
}
// Класс "одноугольник", реализующий интерфейс фигуры.
class Point implements Figure {
private R2Point p;
public Point(R2Point p) {
this.p = p;
}
public double perimeter() {
return 0.0;
}
public double area() {
return 0.0;
}
public Figure add(R2Point q) {
if (!R2Point.equal(p,q)) return new Segment(p, q);
else return this;
}
}
// Класс "двуугольник", реализующий интерфейс фигуры.
class Segment implements Figure {
private R2Point p, q;
public Segment(R2Point p, R2Point q) {
this.p = p; this.q = q;
}
public double perimeter() {
return 2.0 * R2Point.dist(p, q);
}
public double area() {
return 0.0;
}
public Figure add(R2Point r) {
if (R2Point.isTriangle(p, q, r))
return new Polygon(p, q, r);
if (q.inside(p, r)) q = r;
if (p.inside(r, q)) p = r;
return this;
}
}
// Класс "многоугольник", реализующий интерфейс фигуры.
class Polygon extends Deq implements Figure {
private double s, p;
private void grow(R2Point a, R2Point b, R2Point t) {
p -= R2Point.dist(a, b);
s += Math.abs(R2Point.area(a, b, t));
}
public Polygon(R2Point a, R2Point b, R2Point c) {
pushFront(b);
if (b.light(a, c)) {
pushFront(a); pushBack(c);
} else {
pushFront(c); pushBack(a);
}
p = R2Point.dist(a, b) + R2Point.dist(b, c)
+ R2Point.dist(c, a);
s = Math.abs(R2Point.area(a, b, c));
}
public double perimeter() {
return p;
}
public double area() {
return s;
}
public Figure add(R2Point t) {
int i;
// Ищем освещенные ребра, просматривая их одно за другим.
for (i=length(); i>0 !t.light(back(),front()); i--)
pushBack(popFront());
// УТВЕРЖДЕНИЕ: либо ребро [back(),front()] освещено из t,
// либо освещенных ребер нет совсем.
if (i>0) {
R2Point x;
grow(back(), front(), t);
// Удаляем все освещенные ребра из начала дека.
for (x = popFront(); t.light(x, front()); x = popFront())
grow(x, front(), t );
pushFront(x);
// Удаляем все освещенные ребра из конца дека.
for (x = popBack(); t.light(back(), x); x = popBack())
grow(back(), x, t);
pushBack(x);
// Завершаем обработку добавляемой точки.
p += R2Point.dist(back(), t) + R2Point.dist(t, front());
pushFront(t);
}
return this;
}
}
// Класс "выпуклая оболочка".
class Convex {
private Figure fig;
public Convex() {
fig = new Void();
}
public void add(R2Point p) {
fig = fig.add(p);
}
public double area() {
return fig.area();
}
public double perimeter() {
return fig.perimeter();
}
}
// Тест для выпуклой оболочки.
class ConvexTest {
public static void main(String[] args) throws Exception {
Convex convex = new Convex();
while (true) {
convex.add(new R2Point());
Xterm.println("S = " + convex.area() + " , P = "
+ convex.perimeter());
}
}
}
Этот параграф посвящен, прежде всего, решению одной конкретной задачи
средней
степени сложности — нахождению
В нем также рассматриваются аплеты, которые используются в качестве основы для создания простейшего графического интерфейса (GUI — graphics user interface), что дает возможность решать задачи на изображение графиков функций и различных графических объектов. Вопросы создания аплетов обсуждаются практически во всех изданиях, посвященных языку Java, в том числе и в книгах [10], [11] и [13].
Напомним сначала два определения, связанные с важнейшим математическим понятием — свойством выпуклости множеств.
Определение 11.1.
Множество $$M$$ называется
Примеры выпуклых множеств: отрезок прямой, прямоугольник и круг на плоскости, куб, шар и пирамида в пространстве. Выпуклыми множествами не являются окружность, граница квадрата и тор (прямое произведение двух окружностей).
Определение 11.2.
Теперь можно сформулировать основную задачу.
Задача 11.1. Напишите программу, находящую
Если представить себе точки конечного множества
в виде вбитых в доску гвоздей, то
(рис 11.1) Выпуклая оболочка точек плоскостиМы будем трактовать поставленную задачу следующим образом: реализовать
класс Convex с методами
add — добавить новую точку и построить выпуклую
оболочку;perimeter — вычислить периметр;area — вычислить площадь.Договоримся придерживаться стратегии вычисления всех характеристик оболочки
сразу при добавлении новой точки (в методе add ); выполнение методов perimeter и area при этом будет сводиться просто к выдаче уже
вычисленных ранее значений.
Если обозначить множество $$\mathbb{R}^2$$ точек плоскости через $$X$$, а
совокупность всех выпуклых фигур на плоскости через $$\mathcal{P}$$,
то
тройка функций $$f\colon X^* \rightarrow \mathcal{P}$$
Функцию перевычисления $$G$$ для нее более точно мы опишем чуть
позже, а пока
ограничимся следующим предварительным рассуждением. Пусть для некоторой
последовательности точек плоскости
В первом случае
(рис 11.2) Добавление новой точкиХорошей моделью для описания изменяющейся оболочки является следующая:
предположим, что во вновь добавляемой точке $$x$$ расположена
лампочка,
освещающая часть ребер старой оболочки, которые мы так и будем называть —
Если добавляемая точка лежит на продолжении одного из ребер, то оболочка должна измениться так, как показано на рис. 11.3. Поэтому ребро, на продолжении которого лежит точка $$x$$, мы также будем считать освещенным.
До сих пор рассматривался только общий случай, когда
(рис 11.3) Добавление точки — специальный случайДля разработки необходимой иерархии классов в данном случае достаточно
воспользоваться давно известным методом — проектированием сверху вниз.
Уточненная постановка задачи требует создания класса Convex, необходим
еще и класс, содержащий метод main с тестирующей программой. Можно,
конечно, добавить этот метод в класс Convex, но мы для этих целей
создадим отдельный класс ConvexTest.
Интерфейс Figure должен содержать общие для всех возможных случаев
выпуклой
оболочки методы add, perimeter и area, а его реализациями
будут являться классы "нульугольник" ( Void ),
"одноугольник"
( Point ), "двуугольник" ( Segment ) и многоугольник ( Polygon ).
Каждый из этих классов обязан обеспечивать реализацию всех методов интерфейса Figure, при этом метод add должен в качестве аргумента получать
добавляемую точку, а возвращать
Следовательно, необходим класс, который мы назовем R2Point,
представляющий точку на плоскости и обеспечивающий все необходимые действия
над объектами этого типа. Его вполне достаточно для реализации
классов Void, Point и Segment, ибо в первом случае никакой
информации в фигуре хранить вообще не нужно, а в двух последних нужно хранить
только один (для "одноугольника") или два (для
"двуугольника") экземпляра
класса R2Point, представляющие соответственно единственную или две
концевых точки этих выпуклых оболочек.
Для класса Polygon, однако, необходимо нечто большее — вершины
многоугольника необходимо хранить в каком-то контейнере. Для этих целей
особенно хорошо в данном случае подходит дек (это будет понятно уже после
того, как программа будет полностью написана), хотя, вообще говоря, можно
воспользоваться и другим контейнером.
Итак, класс Deq будет представлять собой непрерывную реализацию R2Point, а класс Polygon можно просто сделать
выведенным (дочерним) для него. При этом класс Polygon оказывается
одновременно реализующим ( implements ) интерфейс Figure и
расширяющим ( extends ) класс Deq.
(рис 11.4) Иерархия классов проекта "Выпуклая оболочка"Это завершает проектирование иерархии классов, необходимой для решения поставленной задачи. Результат представлен на рис. 11.4.
В заключение данной секции разберемся с тем, как должны быть устроены
реализации классов Void, Point и Segment. Для
"нульугольника" все очевидно — методы perimeter и area
возвращают нулевые значения, а возвращаемым значением метода add
должен быть объект типа Point.
Класс "одноугольник" уже должен иметь конструктор с аргументом
типа R2Point, сохраняющий эту точку в своей private -компоненте.
Периметр и площадь здесь также являются нулевыми, а метод add
может возвратить как объект типа Segment, так и неизмененный
объект Point. Последнее должно иметь место в случае совпадения
вновь добавляемой точки с уже имеющейся. Таким образом, реализация класса Point требует наличия в классе R2Point метода equal,
позволяющего сравнивать точки плоскости на равенство. Напомним, что оператор == для объектов ссылочных типов возвращает true только в случае
равенства ссылок, а не внутреннего содержания объектов.
Класс "двуугольник" обязан иметь две компоненты типа R2Point,
в которые конструктором класса заносятся его концевые точки. Площадь
любого "двуугольника" нулевая, а периметром естественно считать удвоенную длину отрезка (сравните "двуугольник" с
треугольником).
Добавление точки в данном случае может
привести к трем различным ситуациям: "двуугольник" может
превратиться в
треугольник, он может остаться "двуугольником", но изменить одну
из своих
концевых точек, и, наконец, он может совсем не измениться.
Класс R2Point должен обеспечивать методы, которые позволят различать
эти ситуации, а итоговый вариант реализации классов Void, Point и Segment приведен в конце лекции.
В предыдущей секции мы столкнулись с необходимостью реализовать в классе R2Point ряд методов, которые позволяли бы вычислять расстояния
между точками, сравнивать их на совпадение, выяснять, лежат ли три точки
на одной прямой и не находится ли некоторая точка на прямой между
двумя другими. Для реализации класса Polygon необходимо также уметь
вычислять площадь треугольника и находить все освещенные ребра
многоугольника. Решение задач на модификацию эталонного проекта потребует
реализации еще целого ряда методов, связанных с различными геометрическими
характеристиками.
Ряд фактов из аналитической геометрии и векторной алгебры
являются
необходимой базой для решения сформулированных задач, однако только знание
элементов вычислительной геометрии позволяет получить достаточно
эффективные решения.
Простейшим примером может служить задача вычисления площади треугольника по
известным координатам его вершин. Известная из средней школы формула Герона$$S = \sqrt{p (p-a) (p-b) (p-c)}$$
требует большого числа умножений и четырех относительно
медленно выполняемых операций извлечения квадратного корня. Основанная на
связи площади с
Большую часть методов, о которых будет идти речь в текущей секции,
целесообразно реализовать в виде методов класса (статических методов).
Напомним, что такой метод не получает в качестве неявного аргумента
конкретный экземпляр класса, и не может использовать ключевое слово this.
Расстояние $$d$$ между двумя точками на плоскости можно посчитать
по известной
формуле $$d = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$$, которая и
используется в
реализации метода dist. Два объекта типа R2Point соответствуют
совпадающим точкам плоскости тогда и только тогда, когда попарно равны их
координаты, что позволяет реализовать метод equal.
public static double dist(R2Point a, R2Point b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
public static boolean equal(R2Point a, R2Point b) {
return a.x==b.x a.y==b.y;
}
public static double area(R2Point a, R2Point b, R2Point c) {
return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}
Для вычисления площади треугольника в методе area применена уже
цитированная выше формула, основанная на следующем факте из векторной алгебры:
модуль векторного произведения двух векторов равен площади параллелограмма,
натянутого на эти вектора. Напомним, что
Часто полезно использовать запись этого определения в координатах. Если $$\vec a = (a_x, a_y, a_z)$$, а $$\vec b = (b_x, b_y, b_z)$$, то координаты вектора $$\vec c$$ находятся по формуле$$\vec c = \left| \begin{array}{ccc} \vec i \vec j \vec k\\ a_x a_y a_z \\ b_x b_y b_z \end{array} \right| = (a_y b_z - a_z b_y) \vec i + (a_z b_x - a_x b_z) \vec j + (a_x b_y - a_y b_x) \vec k.$$
В том случае, когда вектора $$\vec a$$ и $$\vec b$$
расположены на плоскости, их
area. Обратите внимание на то, что так
вычисленная площадь является ориентированной, то есть может быть
отрицательной.
Выяснение того факта, лежат ли три точки на одной прямой, сводится к вычислению площади треугольника и сравнению ее с нулем, а для того, чтобы точка $$t$$ прямой располагалась на отрезке $$[a,b]$$ этой же прямой, необходимо и достаточно принадлежности проекций этой точки проекциям на оси координат отрезка $$[a,b]$$. Вот как выглядит программная реализация этих идей.
public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
return area(a, b, c) != 0.0;
}
public boolean inside(R2Point a, R2Point b) {
return (a.x <= x x <= b.x || a.x >= x x >= b.x)
(a.y <= y y <= b.y || a.y >= y y >= b.y);
}
(рис 11.5) Освещенность ребра из точкиПеред тем, как реализовать метод light, позволяющий выяснить,
освещено ли
ребро $$[a,b]$$
Определение 11.3.
Ребро $$[a,b]$$ называется
Заметим, что при таком формальном определении понятия освещенности никакое ребро многоугольника не освещено ни из одной точки внутри или на границе многоугольника, что хорошо видно на рисунке рис. 11.5.
public boolean light(R2Point a, R2Point b) {
double s = area(a, b, this);
return s < 0.0 || ( s == 0.0 ! inside(a, b));
}
В заключение этой секции рассмотрим более сложный вопрос — покажем, как можно наиболее эффективно выяснить, пересекаются ли два отрезка на плоскости, заданные координатами четырех их граничных точек.
На первом шаге решения этой задачи необходимо выяснить, пересекаются ли
Пусть координаты граничных точек первого отрезка $$\overrightarrow{P_1
P_2}$$
равны $$(x_1, y_1)$$ и $$(x_2, y_2)$$, а второго $$\overrightarrow{P_3 P_4}$$ — $$(x_3, y_3)$$ и $$(x_4, y_4)$$. Тогда координаты
левого нижнего угла
Если
Можно доказать (это рекомендуется сделать самостоятельно), что два отрезка пересекаются тогда и только тогда, когда пересекаются их ограничивающие прямоугольники и, кроме того, каждый из них пересекается с прямой, содержащей другой отрезок.
Проверка факта пересечения отрезка с прямой осуществляется с помощью
Таким образом, отрезки $$\overrightarrow{P_1 P_2}$$ и $$\overrightarrow{P_3 P_4}$$ пересекаются тогда и только тогда, когда одновременно выполняются следующие три условия:
1) пересекаются ограничивающие их прямоугольники;
2) $$(\overrightarrow{P_1 P_3} \times \overrightarrow{P_1 P_2}) \cdot (\overrightarrow{P_1 P_4} \times \overrightarrow{P_1 P_2}) \leqslant 0;$$
3) $$(\overrightarrow{P_3 P_1} \times \overrightarrow{P_3 P_4}) \cdot (\overrightarrow{P_3 P_2} \times \overrightarrow{P_3 P_4}) \leqslant 0$$.
Более подробное изложение решения этой и ряда других подобных задач может быть найдено в разделе "Вычислительная геометрия" книги [8].
Нам осталось реализовать методы perimeter, area и add
класса Polygon. Как это уже отмечалось при уточнении постановки задачи,
вся работа по модификации perimeter и area будет сводиться просто к выдаче уже вычисленных
ранее значений, хранящихся в private -компонентах.
class Polygon extends Deq implements Figure {
private double s, p;
public double perimeter() {
return p;
}
public double area() {
return s;
}
...
}
Для того чтобы реализовать метод add и конструктор класса Polygon,
необходимо аккуратно разобраться с тем, как именно вершины
Это должно учитываться в конструкторе, который создает простейший
из многоугольников, — треугольник, размещая заданные ему вершины в Math.abs, находящего
абсолютное значение своего аргумента.
public Polygon(R2Point a, R2Point b, R2Point c) {
pushFront(b);
if (b.light(a, c)) {
pushFront(a); pushBack(c);
} else {
pushFront(c); pushBack(a);
}
p = R2Point.dist(a, b) + R2Point.dist(b, c)
+ R2Point.dist(c, a);
s = Math.abs(R2Point.area(a, b, c));
}
Назовем ребро $$AB$$ (см. рис. рис. 11.6), соединяющее конец
(рис 11.6) Добавление новой точкиПри добавлении новой точки возможны два случая. Если в многоугольнике
освещенных ребер нет, то это означает, что новая точка попала внутрь или на
границу старой
При реализации метода add в этом случае необходимо
для всех освещенных ребер
Пересчет периметра и площади, который необходимо производить для каждого из
удаляемых ребер, выделен из соображений оптимизации в отдельный private -метод grow.
private void grow(R2Point a, R2Point b, R2Point t) {
p -= R2Point.dist(a, b);
s += Math.abs(R2Point.area(a, b, t));
}
Полный текст построенной программы приведен ниже, в последней секции текущего параграфа.
До сих пор мы не использовали богатейших возможностей, предоставляемых
различными библиотеками языка Java, которые позволяют создавать удобные
графические интерфейсы (GUI), ограничиваясь вводом/выводом текстовой
информации с помощью класса . Вопросы создания полноценных
графических интерфейсов выходят за рамки нашего курса, однако с простейшими
методами вывода графической информации познакомиться весьма полезно.
Наилучший способ для этих целей — использование main и не может быть запущен самостоятельно.
Аплеты запускаются с помощью браузера или специализированной программы для
просмотра аплетов , а для создания аплетов необходимо
реализовывать подкласс класса Applet, определенного в пакете java.applet, являющимся составной частью библиотеки
Для целей вывода графической информации достаточно в классе, выведенном из
класса Applet, переопределить метод paint. Мы познакомимся только
с несколькими простейшими графическими примитивами:
setColor — установить цвет, которым будут
изображаться выводимые объекты;drawLine — изобразить отрезок прямой линии;drawRect — изобразить границу прямоугольника;drawOval — изобразить границу овала;fillRect — изобразить заполненный прямоугольник;fillOval — изобразить заполненный овал.В качестве первого примера рассмотрим решение следующей задачи.
Задача 11.2. Создайте аплет,изображающий в окне размером 200х200 заполненный черный и синий прямоугольники, заполненный синий круг, красный отрезок, прямоугольник и овал.
Вот одно из возможных решений этой задачи.
Текст аплета
import java.awt.*;
import java.applet.*;
public class Primitives extends Applet {
public void paint (Graphics g) {
g.fillRect(20, 20, 40, 60);
g.setColor(Color.red);
g.drawLine(2, 2, 80, 80);
g.drawOval(120, 120, 30, 40);
g.drawRect(170, 170, 10, 15);
g.setColor(Color.blue);
g.fillOval(20, 150, 30, 30);
}
}
Операторы import обеспечивают подключение необходимых классов
библиотеки , аплет
можно запустить только создав дополнительно еще один файл — . Именно он должен быть указан в качестве аргумента
программе или браузеру. Содержимое этого файла может быть
примерно таким.
HTML-файл
<HTML><BODY> <APPLET code="Primitives.class" width=200 height=200></APPLET> </BODY></HTML>
Содержательная информация здесь сводится к указанию имени запускаемого аплета и размеру окна, в котором этот аплет будет работать. После создания данного файла можно, наконец, запустить аплет, выполнив команду
appletviewer Primitives.html
Заметим, что начало координат размещается в левом верхнем углу окна, а ось ординат направлена вниз. Других комментариев созданный нами аплет не требует.
Рассмотрим более интересную задачу на построение графика функции.
Задача 11.3. Создайте аплет,изображающий в окне размером 200х200 оси
координат и график функции y=x2-x на промежутке [-2,2].
Будем рисовать график этой функции путем изображения отдельных точек,
применяя метод drawOval с равными единице третьим и четвертым
аргументами, определяющими размер овала. Коэффициент $$k$$ позволяет
изобразить
график в нужном масштабе.
Текст аплета
import java.awt.*;
import java.applet.*;
public class FuncGraph extends Applet {
private static final int SIZE = 200;
private static final double k = 50.;
private int f(double x) {
return SIZE/2 - (int)(k * (x*(x*x-1)));
}
public void paint (Graphics g) {
g.drawLine(0, SIZE/2, SIZE-1, SIZE/2);
g.drawLine(SIZE/2, 0, SIZE/2, SIZE-1);
g.setColor(Color.red);
for (int i=0; i<SIZE; i++) {
double x = (i - SIZE/2) / k;
g.drawOval(i, f(x), 1, 1);
}
}
}
Файл для запуска аплета вполне аналогичен предыдущему случаю:
HTML-файл
<HTML><BODY> <APPLET code="FuncGraph.class" width=200 height=200></APPLET> </BODY></HTML>
Построим теперь график функции, заданной в полярных координатах.
Задача 11.4. Создайте аплет,изображающий в окне размером 200х200 график функции, задаваемой в полярных координатах $$(r,\varphi)$$ соотношением $$r = \sin 3\varphi$$
Для решения задачи нужно знать, что константа $$\pi$$ доступна
программам на
языке Java, как величина Math.PI, а функции $$\sin$$ и $$\cos$$ —
как методы Math.sin и Math.cos соответственно. На сей раз будем
рисовать график, изображая отрезки прямых, соединяющие последовательно
вычисляемые точки графика.
(рис 11.7) Результат работы аплетаТекст аплета
import java.awt.*;
import java.applet.*;
public class FuncPolGraph extends Applet {
private static final int SIZE = 200;
private static final double k = 95.;
private int x(double fi) {
return SIZE/2 + (int)(k * Math.sin(3.*fi) * Math.cos(fi));
}
private int y(double fi) {
return SIZE/2 - (int)(k * Math.sin(3.*fi) * Math.sin(fi));
}
public void paint (Graphics g) {
setBackground(Color.white);
g.setColor(Color.blue);
int xp = x(0.), yp = y(0.);
for (double fi=0.; fi<2.*Math.PI; fi+=Math.PI/180.) {
if (Math.sin(3.*fi) < 0)
continue;
g.drawLine(xp, yp, x(fi), y(fi));
xp = x(fi); yp = y(fi);
}
}
}
HTML-файл
<HTML><BODY> <APPLET code="FuncPolGraph.class" width=200 height=200></APPLET> </BODY></HTML>
Результат работы этой программы приведен на рисунке рис. 11.7.
Задача 11.5. Создайте аплет, изображающий в окне стандартный прямоугольник (со сторонами, параллельными осям координат), заштрихованный под углом $$45^\circ$$.
Задача 11.6. Создайте аплет, изображающий в окне границу объединения двух стандартных
прямоугольников (со сторонами, параллельными осям координат), координаты
углов которых предварительно вводятся с помощью методов класса
Задача 11.7. Создайте аплет, изображающий в окне размером 600x600 график функции, заданной параметрически: $$x = \sin 2\varphi$$, $$y = \cos 3 \varphi$$.
Задача 11.8. Создайте аплет, изображающий в окне размером 600x600 линии уровня функции $$z = xy$$.
Задача 11.9. Создайте аплет, изображающий в окне размером 600x600 траекторию движения шара с начальными координатами $$(x_1, x_2)$$ и скоростью $$(v_1, v_2)$$ в квадратном билльярде со стороной единичной длины.
Задача 11.10. Модифицируйте текст эталонного проекта "
a) среднее арифметическое значений функции $$\sin (xy)$$ в вершинах выпуклой оболочки;
b) максимальное значение функции $$\sin (xy)$$ в серединах сторон выпуклой оболочки;
c) мощность множества пересечения границы
d) площадь части
e) периметр части
f) количество ребер
g) количество ребер выпуклой оболочке, параллельных осям координат;
h) количество ребер выпуклой оболочке, параллельных сторонам заданного треугольника;
i) угол между максимальным и минимальным ребрами
j) лежит ли заданная точка плоскости внутри
Задача 11.11. Модифицируйте текст эталонного проекта "
a) расстояние от
b) расстояние от
c) расстояние от
d) расстояние от
e) расстояние от
f) среднее арифметическое расстояний от заданной точки до вершин выпуклой оболочки;
g) сумму квадратов расстояний от заданной точки до вершин
h) сумму квадратов расстояний от начала координат до середин сторон выпуклой оболочки;
i) количество пар вершин
j) количество пар сторон
Задача 11.12. Модифицируйте текст эталонного проекта "
a) находится ли единичная окружность с центром в начале координат внутри
b) количество вершин
c) количество вершин
d) минимальный стандартный прямоугольник, содержащий
e) максимальный стандартный прямоугольник, содержащийся в выпуклой оболочке;
f) радиус минимального круга с центром в заданной точке, содержащего
g) радиус максимального круга с центром в заданной точке, содержащегося в выпуклой оболочке;
h) диаметр
i) длину минимальной диагонали
j) координаты центра тяжести
Задача 11.13. Модифицируйте текст эталонного проекта "
a) мощность множества точек пересечения границы
b) мощность множества точек пересечения границы
c) мощность множества точек пересечения границы
d) мощность множества точек пересечения границы
e) мощность множества точек пересечения границы
f) мощность множества точек пересечения границы
g) мощность множества точек пересечения границы
h) мощность множества точек пересечения границы
i) мощность множества точек пересечения границы
j) мощность множества точек пересечения границы
Задача 11.14. Модифицируйте текст эталонного проекта "
a) площадь части
b) периметр части
c) площадь части
d) периметр части
e) площадь части
f) периметр части
g) площадь части
h) периметр части
i) площадь части
j) периметр части
Задача 11.15. Модифицируйте текст эталонного проекта "
a) количество вершин
b) количество вершин
c) количество вершин
d) количество вершин
e) количество вершин
f) количество вершин
g) количество вершин
h) количество вершин
i) количество вершин
j) количество вершин
Задача 11.16. Модифицируйте текст эталонного проекта "
a) количество ребер
b) количество ребер
c) количество ребер
d) количество ребер
e) количество ребер
f) количество ребер
g) количество ребер
h) количество ребер
i) количество ребер
j) количество ребер
Задача 11.17. Модифицируйте текст эталонного проекта "
a) угол, под которым
b) угол, под которым видно из начала координат самое длинное ребро
c) количество всех острых внутренних углов
d) количество внутренних острых углов
e) сумму всех внутренних углов
f) сумму внутренних углов
g) сумму углов, под которыми ребра
h) сумму углов, под которыми ребра
i) сумму углов, под которыми ребра
j) сумму углов, под которыми ребра
Задача 11.18. Модифицируйте текст эталонного проекта "
a) пересечение границы
b) часть
c)
d)
e)
f)
g) множество точек пересечения границы
h) множество точек пересечения границы
i) множество точек пересечения границы
j) множество точек пересечения границы
Задача 11.19. Модифицируйте текст эталонного проекта "
a) часть
b) часть
c) часть
d) часть
e) часть
f) часть
g) часть
h) часть
i) часть
j) часть
Данная секция содержит исходный текст программы, являющейся итогом работы
над проектом "
Эталонный проект
//Класс, описывающий точку (Point) на плоскости (R2).
class R2Point {
private double x, y;
public R2Point(double x, double y) {
this.x = x; this.y = y;
}
public R2Point() throws Exception {
x = Xterm.inputDouble("x -> ");
y = Xterm.inputDouble("y -> ");
}
public static double dist(R2Point a, R2Point b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
public static double area(R2Point a, R2Point b, R2Point c) {
return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}
public static boolean equal(R2Point a, R2Point b) {
return a.x==b.x a.y==b.y;
}
public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
return area(a, b, c) != 0.0;
}
public boolean inside(R2Point a, R2Point b) {
return (a.x <= x x <= b.x || a.x >= x x >= b.x)
(a.y <= y y <= b.y || a.y >= y y >= b.y);
}
public boolean light(R2Point a, R2Point b) {
double s = area(a, b, this);
return s < 0.0 || ( s == 0.0 ! inside(a, b));
}
}
// Непрерывная реализация дека.
class Deq {
private final static int DEFSIZE = 16;
private R2Point[] array;
private int size, head, tail;
private int forward(int index) {
return ++index < array.length ? index : 0;
}
private int backward(int index) {
return --index >= 0 ? index : array.length - 1;
}
public Deq(int size) {
array = new R2Point[size];
this.size = head = 0;
tail = array.length - 1;
}
public Deq() {
this(DEFSIZE);
}
public int length() {
return size;
}
public void pushFront(R2Point p) {
array[head=backward(head)] = p;
size += 1;
}
public void pushBack(R2Point p) {
array[tail=forward(tail)] = p;
size += 1;
}
public R2Point popFront() {
R2Point p = front();
head = forward(head);
size -= 1;
return p;
}
public R2Point popBack() {
R2Point p = back();
tail = backward(tail);
size -= 1;
return p;
}
public R2Point front() {
return array[head];
}
public R2Point back() {
return array[tail];
}
}
// Интерфейс, задающий новый тип - фигуру.
interface Figure {
public double perimeter();
public double area();
public Figure add(R2Point p);
}
// Класс "нульугольник", реализующий интерфейс фигуры.
class Void implements Figure {
public double perimeter() {
return 0.0;
}
public double area() {
return 0.0;
}
public Figure add(R2Point p) {
return new Point(p);
}
}
// Класс "одноугольник", реализующий интерфейс фигуры.
class Point implements Figure {
private R2Point p;
public Point(R2Point p) {
this.p = p;
}
public double perimeter() {
return 0.0;
}
public double area() {
return 0.0;
}
public Figure add(R2Point q) {
if (!R2Point.equal(p,q)) return new Segment(p, q);
else return this;
}
}
// Класс "двуугольник", реализующий интерфейс фигуры.
class Segment implements Figure {
private R2Point p, q;
public Segment(R2Point p, R2Point q) {
this.p = p; this.q = q;
}
public double perimeter() {
return 2.0 * R2Point.dist(p, q);
}
public double area() {
return 0.0;
}
public Figure add(R2Point r) {
if (R2Point.isTriangle(p, q, r))
return new Polygon(p, q, r);
if (q.inside(p, r)) q = r;
if (p.inside(r, q)) p = r;
return this;
}
}
// Класс "многоугольник", реализующий интерфейс фигуры.
class Polygon extends Deq implements Figure {
private double s, p;
private void grow(R2Point a, R2Point b, R2Point t) {
p -= R2Point.dist(a, b);
s += Math.abs(R2Point.area(a, b, t));
}
public Polygon(R2Point a, R2Point b, R2Point c) {
pushFront(b);
if (b.light(a, c)) {
pushFront(a); pushBack(c);
} else {
pushFront(c); pushBack(a);
}
p = R2Point.dist(a, b) + R2Point.dist(b, c)
+ R2Point.dist(c, a);
s = Math.abs(R2Point.area(a, b, c));
}
public double perimeter() {
return p;
}
public double area() {
return s;
}
public Figure add(R2Point t) {
int i;
// Ищем освещенные ребра, просматривая их одно за другим.
for (i=length(); i>0 !t.light(back(),front()); i--)
pushBack(popFront());
// УТВЕРЖДЕНИЕ: либо ребро [back(),front()] освещено из t,
// либо освещенных ребер нет совсем.
if (i>0) {
R2Point x;
grow(back(), front(), t);
// Удаляем все освещенные ребра из начала дека.
for (x = popFront(); t.light(x, front()); x = popFront())
grow(x, front(), t );
pushFront(x);
// Удаляем все освещенные ребра из конца дека.
for (x = popBack(); t.light(back(), x); x = popBack())
grow(back(), x, t);
pushBack(x);
// Завершаем обработку добавляемой точки.
p += R2Point.dist(back(), t) + R2Point.dist(t, front());
pushFront(t);
}
return this;
}
}
// Класс "выпуклая оболочка".
class Convex {
private Figure fig;
public Convex() {
fig = new Void();
}
public void add(R2Point p) {
fig = fig.add(p);
}
public double area() {
return fig.area();
}
public double perimeter() {
return fig.perimeter();
}
}
// Тест для выпуклой оболочки.
class ConvexTest {
public static void main(String[] args) throws Exception {
Convex convex = new Convex();
while (true) {
convex.add(new R2Point());
Xterm.println("S = " + convex.area() + " , P = "
+ convex.perimeter());
}
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.