Решения задач / Окружность и трапеция
.pdfОкружность и трапеция Далее в таблице приведены входные и выходные данные программы. Входные данные приведены для файла.
В отчете обязательно нужно привести определение трапеции:
Трапеция — выпуклый четырёхугольник, у которого две стороны параллельны, а две другие стороны не параллельны.
Таблица 1 — Входные и выходные данные
Изображение |
Входные данные |
Выходные данные |
|
|
|
|
|
|
point(3, 2) |
(3.000, 0.000) |
|
|
2 |
|
|
|
point(1, 0) |
|
|
|
point(2, -2) |
|
|
|
point(4, -2) |
|
|
|
point(5, 0) |
|
|
|
|
|
|
|
point(3, 2) |
(1.000, 2.000) |
|
|
2 |
(5.000, |
2.000) |
|
point(0, 2) |
|
|
|
point(2, -2) |
|
|
|
point(4, -2) |
|
|
|
point(6, 2) |
|
|
|
|
|
|
|
point(3, 2) |
(3.000, 4.000) |
|
|
2 |
|
|
|
point(0, 4) |
|
|
|
point(2, -2) |
|
|
|
point(4, -2) |
|
|
|
point(6, 4) |
|
|
|
|
|
|
point(3, 2) |
(1.000, 2.000) |
|
2 |
(2.059, |
3.765) |
point(1, 3.5) |
(3.000, 4.000) |
|
point(1, 0) |
(3.000, 0.000) |
|
point(5, 0) |
(5.000, 2.000) |
|
point(5, 4.5) |
|
|
point(3, |
2) |
(1.085, |
1.424) |
2 |
|
(1.268, |
1.000) |
point(1.5, 3.5) |
(1.454, |
3.268) |
|
point(1, |
1) |
(1.677, |
3.500) |
point(5, |
1) |
(4.323, |
3.500) |
point(4.5, 3.5) |
(4.546, |
3.268) |
|
|
|
(4.732, |
1.000) |
|
|
(4.915, |
1.424) |
|
|
*максимальное число |
|
|
|
пересечений |
|
|
|
|
|
point(0, |
0) |
(-1.664, |
-1.109) |
2 |
|
(0.000, -2.000) |
|
point(0, |
0) |
(2.000, |
0.000) |
point(-3, |
-2) |
|
|
point(2, -2) |
|
|
|
point(3, |
0) |
|
|
point(-2, -2) |
No roots! |
2 |
|
point(-3, -1) |
|
point(-3, -3) |
|
point(-2, -3) |
|
point(-1, -1) |
|
point(-2, -2) |
Not trapezoid! |
2 |
|
point(-5, -4) |
|
point(-4, -4) |
|
point(0, -4) |
|
point(0, 0) |
|
2
|
point(-2, |
-2) |
(-4.000, -2.000) |
|
2 |
|
(-3.200, -3.600) |
|
point(-5, 0) |
(-2.000, -4.000) |
|
|
point(-3, -4) |
(-2.000, 0.000) |
|
|
point(-1, -4) |
(-0.800, -3.600) |
|
|
point(1, |
0) |
(-0.000, -2.000) |
|
|
|
|
|
point(0, |
1) |
(-1.927, 1.537) |
|
2 |
|
(-1.823, 0.177) |
|
point(-5, 0) |
|
|
|
point(-1, 2) |
|
|
|
point(0, |
2) |
|
|
point(-2, 0) |
|
|
|
|
|
|
|
point(0, |
2) |
No roots |
|
2 |
|
|
|
point(-4, 1) |
|
|
|
point(-1, 6) |
|
|
|
point(5, |
2) |
|
|
point(-1, |
-1) |
|
|
|
|
|
Радиус окружности |
point(0, |
2) |
Circle radius <= 0! |
равен нулю |
0 |
|
|
point(-4, 1) |
|
||
|
point(-1, 6) |
|
|
|
point(5, |
2) |
|
|
point(-1, |
-1) |
|
|
point(0, |
2) |
(-2.000, 2.000) |
|
2 |
|
(-0.000, 4.000) |
|
point(-2, 2) |
(0.000, 0.000) |
|
|
point(0, |
0) |
|
|
point(1, |
1) |
*программа находит точку |
|
point(0, |
4) |
B дважды (для отрезка CB |
|
|
|
и отрезка AB), однако |
|
|
|
затем следует процесс |
|
|
|
удаления дубликатов |
|
|
|
точек |
|
|
|
|
3
Таблица 2 — Код программы на Turbo Prolog
Код
NOWARNINGS
%Раздел описания доменов
DOMAINS
file = datafile % Файл datafile
point = point(real, real) % Структура, описывающая точку points = point* % Список точек
%Раздел описания предикатов
PREDICATES
input(point, real, point, point, point, point) input_action(point, real, point, point, point, point, integer) exist_file(string)
read_data_from_console(point, real, point, point, point, point) read_data_from_file(point, real, point, point, point, point) main(point, real, point, point, point, point, points) is_circle(point, real)
is_trapezoid(point, point, point, point) is_trapezoid_aux(point, point, point, point) is_parallel(point, point, point, point)
find_intersections(point, real, point, point, point, point, point) find_intersection(point, real, point, point, point) check_boundaries(real, real, real)
get_roots(real, real, real, real, real, real, real, point, point) distance(point, point, real)
jarvis(points, points) jarvis_1(points, point, point, points)
jarvis_2(points, point, point, points, points, point) jarvis_3(symbol, points, point, point, point, points, points, point) min(points, point)
min_1(points, point, point) lt(point, point)
direction(point, point, point, symbol) direction_1(real, symbol) is_nearer(point, point, point) quicksort(points, points) partition(points, real, points, points) merge(points, points, points) distinct_p(points, points) point_equals(point, point) output(points)
output_action(points, integer) write_data(points)
% Раздел описания внутренней цели
GOAL
clearwindow(), % Очистка текущего окна
input(PointC, Radius, Point1, Point2, Point3, Point4), % Ввод исходных данных
main(PointC, Radius, Point1, Point2, Point3, Point4, Solutions), % Запуск основного алгоритма quicksort(Solutions, SortedSolutions), % Сортировка результирующего списка точек по X distinct_p(SortedSolutions, SortedSolutionsWithoutDuplicates), % Удаление дубликатов точек output(SortedSolutionsWithoutDuplicates), % Вывод результата
readchar(_).
CONSTANTS
epsilon = 1e-6
CLAUSES
% Ввод исходных данных
input(PC, Radius, P1, P2, P3, P4) :- write("-== MENU ==-"), nl, write("1. Read from console;"), nl, write("2. Read from file."), nl, write("Another button to exit"), nl,
readint(C), % Считывание целого числа input_action(PC, Radius, P1, P2, P3, P4, C), !; write("Error input."), fail.
% Ввод из диалогового окна
input_action(PC, Radius, P1, P2, P3, P4, 1) :- read_data_from_console(PC, Radius, P1, P2, P3, P4).
% Ввод из файла
input_action(PC, Radius, P1, P2, P3, P4, 2) :- write("File name: "),
readln(FileName), !, % Ввод названия файла exist_file(FileName), % Существует ли файл openread(datafile, FileName), % Открытие файла для чтения readdevice(datafile), % Перенаправление ввода на файл
read_data_from_file(PC, Radius, P1, P2, P3, P4), % Чтение данных из файла closefile(datafile), !; % Закрытие файла
write("Error reading file!"), nl, fail.
% Проверка существования файла и вывод ошибки, если отсутствует exist_file(FileName) :-
existfile(FileName), !;
write("File not found!"), nl, !, fail.
4
% Чтение данных с консоли
read_data_from_console(point(CX, CY), Radius, point(X1, Y1), point(X2, Y2), point(X3, Y3), point(X4, Y4)) :-
write("1. Input circle"), nl, write("- Center X: "), readreal(CX), write("- Center Y: "), readreal(CY), write("- Radius: "), readreal(Radius), write("2. Input trapezoid"), nl, write("- X1: "), readreal(X1), write("- Y1: "), readreal(Y1), write("- X2: "), readreal(X2), write("- Y2: "), readreal(Y2), write("- X3: "), readreal(X3), write("- Y3: "), readreal(Y3), write("- X4: "), readreal(X4), write("- Y4: "), readreal(Y4), nl.
% Чтение данных с файла
read_data_from_file(PointC, Radius, P1, P2, P3, P4) :- readterm(point, PointC),
readreal(Radius), readterm(point, P1), readterm(point, P2), readterm(point, P3), readterm(point, P4).
% Запуск основного алгоритма
main(PointC, R, P1, P2, P3, P4, Solutions) :- is_circle(PointC, R), % Проверка на круг is_trapezoid(P1, P2, P3, P4), % Проверка на трапецию
jarvis([P1, P2, P3, P4], [NP1, NP2, NP3, NP4, _]), % Алгоритм обхода Джарвиса
% Поиск всех решений-пересечений
findall(Point, find_intersections(PointC, R, NP1, NP2, NP3, NP4, Point), Solutions).
%Проверка на круг (радиус > 0) is_circle(_, R) :-
R > 0, !;
write("Circle radius <= 0!"), !, fail.
%Проверка на трапецию
%1. Две любые точки трапеции не могут совпадать.
%2. Две стороны параллельны и две другие стороны не параллельны. is_trapezoid(P1, P2, P3, P4) :-
not(point_equals(P1, P2)), not(point_equals(P1, P3)), not(point_equals(P1, P4)), not(point_equals(P2, P3)), not(point_equals(P2, P4)), not(point_equals(P3, P4)), is_trapezoid_aux(P1, P2, P3, P4), !;
write("Not trapezoid!"), !, fail.
%Проверка на трапецию (вспомогательный предикат)
is_trapezoid_aux(P1, P2, P3, P4) :- is_parallel(P1, P2, P3, P4), not(is_parallel(P1, P3, P2, P4)), not(is_parallel(P2, P3, P1, P4)), !; is_parallel(P1, P3, P2, P4), not(is_parallel(P1, P2, P3, P4)), not(is_parallel(P2, P3, P1, P4)), !; is_parallel(P2, P3, P1, P4), not(is_parallel(P1, P3, P2, P4)), not(is_parallel(P1, P2, P3, P4)).
% Проверка параллельности двух прямых, заданных точками P1-P2 и P3-P4 is_parallel(point(X1, Y1), point(X2, Y2), point(X3, Y3), point(X4, Y4)) :-
abs((Y2 - Y1) * (X4 - X3) - (Y4 - Y3) * (X2 - X1)) <= epsilon.
% Поиск всех пересечений
find_intersections(PC, R, P1, P2, P3, P4, Point) :- find_intersection(PC, R, P1, P2, Point); find_intersection(PC, R, P2, P3, Point); find_intersection(PC, R, P3, P4, Point); find_intersection(PC, R, P4, P1, Point).
% Поиск пересечения
find_intersection(point(CX, CY), R, point(X1, Y1), point(X2, Y2), point(RX, RY)) :- distance(point(X1, Y1), point(X2, Y2), Lab), abs(Lab) > epsilon,
DX = (X2 - X1) / Lab,
DY = (Y2 - Y1) / Lab,
T = DX * (CX - X1) + DY * (CY - Y1), EX = T * DX + X1,
EY = T * DY + Y1,
distance(point(EX, EY), point(CX, CY), LEC),
get_roots(LEC, R, DX, DY, T, X1, Y1, point(EX, EY), point(RX, RY)), check_boundaries(X1, X2, RX), check_boundaries(Y1, Y2, RY).
%Проверка границ решения
%(точка пересечения должна быть в пределах отрезка) check_boundaries(T1, T2, RT) :-
T1 <= RT, RT <= T2, !;
T2 <= RT, RT <= T1.
%Поиск всех корней
get_roots(LEC, R, DX, DY, T, X1, Y1, PointE, point(FX, FY)) :- LEC > R, !, fail; % Корней нет
5
LEC = R, !, PointE = point(FX, FY); % Один корень
DT = sqrt(R * R - LEC * LEC), % Два корня
FX = (T - DT) * DX + X1,
FY = (T - DT) * DY + Y1; % Также ещё один при помощи OR
DT = sqrt(R * R - LEC * LEC),
FX = (T + DT) * DX + X1,
FY = (T + DT) * DY + Y1.
%Расстояние Евклида между двумя точками distance(point(X1, Y1), point(X2, Y2), D) :-
D = sqrt((Y2 - Y1) * (Y2 - Y1) + (X2 - X1) * (X2 - X1)).
%Универсальный алгоритм Джарвиса
%Здесь используется для того, чтобы определить по четырем точкам трапецию
%Основная сложность определения трапеции — точки могут быть введены случайным образом
%Например, они могут быть введены диагональным образом
%Например, точки введены в порядке "A, C, B, D", но ребра: A-B-C-D-A
%Т. е. нужно как-то отделить диагонали от ребер
%Для этого алгоритм Джарвиса строит выпуклую оболочку так, будто точки были введены
%в порядке "A, B, C, D".
%Данный предикат всегда вставляет в конец начальную точку, что может быть лишним.
%Например, как в нашем случае. Однако это поведение стоит оставить таким.
jarvis(Points, [Pstart|ConvexHullVertices]) :- min(Points, Pstart),
jarvis_1([Pstart|Points], Pstart, Pstart, ConvexHullVertices). jarvis_1([P1|Ps], P0, Pstart, [Q|Qs]) :-
jarvis_2(Ps, P1, P0, [], Ps1, Q), not(Q = Pstart), !, jarvis_1(Ps1, Q, Pstart, Qs).
jarvis_1(_, _, Pstart, [Pstart]). jarvis_2([], P, _, Ps, Ps, P). jarvis_2([P0|Ps], P1, P0, Ps0, Ps1, Q) :- !,
jarvis_2(Ps, P1, P0, [P0|Ps0], Ps1, Q). jarvis_2([P2|Ps], P1, P0, Ps0, Ps1, Q) :-
direction(P2, P0, P1, Direction), jarvis_3(Direction, Ps, P2, P1, P0, Ps0, Ps1, Q).
jarvis_3(left, Ps, P2, P1, P0, Ps0, Ps1, Q) :- !, jarvis_2(Ps, P2, P0, [P1|Ps0], Ps1, Q).
jarvis_3(right, Ps, P2, P1, P0, Ps0, Ps1, Q) :- !, jarvis_2(Ps, P1, P0, [P2|Ps0], Ps1, Q).
jarvis_3(collinear, Ps, P2, P1, P0, Ps0, Ps1, Q):- is_nearer(P2, P1, P0), !,
% P2 = nearer to P0 than P1 jarvis_2(Ps, P1, P0, Ps0, Ps1, Q).
jarvis_3(collinear, Ps, P2, _, P0, Ps0, Ps1, Q):- % P1 = nearer to P0 than P2
jarvis_2(Ps, P2, P0, Ps0, Ps1, Q). min([X|Xs], Y) :- min_1(Xs, X, Y). min_1([], X, X).
min_1([Y|Ys], X, Z) :- lt(Y, X), !, min_1(Ys, Y, Z). min_1([_|Ys], X, Z) :- min_1(Ys, X, Z).
lt(point(X, _), point(X1, _)) :- X < X1, !. lt(point(X, Y), point(X, Y1)) :- Y < Y1.
direction(point(Xa, Ya), point(Xb, Yb), point(Xc, Yc), Dirn) :- Area = (Xb - Xa) * (Yc - Ya) - (Xc - Xa) * (Yb - Ya), direction_1(Area, Dirn).
direction_1(Area, left) :- Area > 0.0, !. direction_1(Area, right) :- Area < 0.0, !. direction_1(_, collinear).
is_nearer(point(Xa, Ya), point(Xb, Yb), point(Xc, Yc)) :- Xa_Xc = Xa - Xc,
Ya_Yc = Ya - Yc,
Xb_Xc = Xb - Xc,
Yb_Yc = Yb - Yc,
(Xa_Xc * Xa_Xc + Ya_Yc * Ya_Yc) < (Xb_Xc * Xb_Xc + Yb_Yc * Yb_Yc).
% Быстрая сортировка quicksort([point(X, Y)|Xs], Ys) :-
partition(Xs, X, Left, Right), quicksort(Left, Ls), quicksort(Right, Rs),
merge(Ls, [point(X, Y)|Rs], Ys), !. quicksort([], []).
% Разбиение списка
partition([point(X, Y)|Xs], Z, [point(X, Y)|Ls], Rs) :- X <= Z, !, partition(Xs, Z, Ls, Rs).
partition([point(X, Y)|Xs], Z, Ls, [point(X, Y)|Rs]) :- X > Z, partition(Xs, Z, Ls, Rs).
partition([], _, [], []).
% Объединение двух списков
merge([X|Xs], Ys, [X|Zs]) :- merge(Xs, Ys, Zs), !. merge([], Ys, Ys).
% Удаление дубликатов точек по X [O(N) - нужна предварительная сортировка] distinct_p([], []) :- !. % В пустом списке нет дубликатов distinct_p([Point], [Point]) :- !. % В списке из одной точки нет дубликатов distinct_p([Point1, Point2|Tail], Result) :- % Если точки примерно одинаковы
point_equals(Point1, Point2), !, distinct_p([Point1|Tail], Result).
distinct_p([Point1, Point2|Tail], [Point1|Result]) :- % Если точки разные distinct_p([Point2|Tail], Result).
6
point_equals(point(X1, Y1), point(X2, Y2)) :- % Проверка точек на примерное равенство abs(X1 - X2) <= epsilon, abs(Y1 - Y2) <= epsilon.
%Вывод результата output(Solutions) :-
not(Solutions = []),
write("1. Write to console;"), nl, write("2. Write to file."), nl, write("Another button to exit"), nl, readint(C), % Считывание числа output_action(Solutions, C), !; % Вывод
Solutions = [], !, write("No roots!"); write("Error output.").
%Вывод в диалоговое окно
output_action(Solutions, 1) :- write_data(Solutions).
% Вывод в файл output_action(Solutions, 2) :-
write("File name: "), readln(FileName), % Ввод названия файла
openwrite(datafile, FileName), % Открытие файла для записи writedevice(datafile), % Перенаправление вывода на файл write_data(Solutions), % Запись данных closefile(datafile). % Закрытие файла
% Вывод каждой точки пересечения write_data([]). write_data([point(X, Y)|Tail]) :-
writef("(%0.3f, %0.3f)", X, Y), nl, write_data(Tail).
7