Консультация № 189331
09.05.2016, 14:53
0.00 руб.
0 12 1
Здравствуйте, уважаемые эксперты! Помогите, пожалуйста!
Реализовать решение задачи на Си: выбрать три различных точки из заданного множества точек на плоскости так, чтобы была минимальной разность между количествами точек, лежащих внутри и вне треугольника с вершинами в выбранных точках.

Обсуждение

давно
Посетитель
7438
7205
10.05.2016, 02:41
общий
это ответ
Здравствуйте, YarLam125!
Посмотрите, как я сделал:
1) Задаются случайно 100 точек (можете переделать на ручной ввод или изменить число точек)
2) Перебираем все тройки чисел, для каждой пробегаем по всем остальным точкам и считаем
количество попаданий внутрь треугольника.
3) Ищем ту тройку, у которой число попаданий во внутрь ближе к половине количества точек.
4) Попадание вовнутрь определяем по тому, находится ли точка по одну сторону с каждой вершиной
треугольника относительно прямой, соединяющей две оставшиеся вершины. (см. аналитическую геометрию)
5) выводится количество внутренних точек. Можете самостоятельно вывести координаты найденных точек, они сохраняются в a, b, c
[code h=200]
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>

#define N 100

typedef struct point
{
double x;
double y;
};

// Вычисляет положение точки D(x,y) относительно прямой AB
double g(point a, point b, point d)
{
return (d.x - a.x) * (b.y - a.y) - (d.y - a.y) * (b.x - a.x);
}

// Лежат ли точки C и D с одной стороны прямой (AB)?
bool f(point a, point b, point c, point d)
{
return g(a, b, c) * g(a, b, d) >= 0;
}

int main()
{
point* p = (point*)malloc(N*sizeof(point));;
int i,j,k,m;
int ci;
int ci_curr;
point a,b,c;

srand(time(NULL));

for(i=0; i<N; i++)
{
p[i].x = (rand()%200) - 100;
p[i].y = (rand()%200) - 100;
}

ci = 0;
for(i=0; i<N-2; i++)
{
for(j=i+1; j<N-1; j++)
{
for(k=j+1; k<N; k++)
{
ci_curr = 0;
for(m=0; m<N; m++)
{
if ((m != i) && (m != j) && (m != k))
ci_curr += f(p[i],p[j],p[k],p[m]) && f(p[j],p[k],p[i],p[m]) && f(p[k],p[i],p[j],p[m]);
}
if (abs(ci_curr - N/2) < abs(ci - N/2))
{
ci = ci_curr;
a = p[i];
b = p[j];
c = p[k];
}
}
}
}
printf("ci = %d\n", ci);

free(p);
return 0;
}
[/code]
5
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
399292
22
10.05.2016, 18:06
общий
Адресаты:
Спасибо большое! Только вот можете пояснить 3 пункт? Почему именно к половине имеющихся точек?
давно
Посетитель
7438
7205
11.05.2016, 10:20
общий
Адресаты:
А чтобы выполнить:
чтобы была минимальной разность между количествами точек, лежащих внутри и вне треугольника с вершинами в выбранных точках
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
11.05.2016, 10:25
общий
Адресаты:
Строго говоря, это неправильно.
Переделайте самостоятельно:
Нашли внутренние, их ci штук, внешних co=N-3-ci
И ищем минимум |co-ci|
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
399292
22
19.05.2016, 22:38
общий
Адресаты:
Здравствуйте! Прошу прощения, что пишу по данному вопросу так поздно, но не могли бы вы все-таки откомментировать строки в вашей программе? Пытался самостоятельно разобраться, но пока получается неважно.
давно
Посетитель
7438
7205
20.05.2016, 11:04
общий
20.05.2016, 11:06
Адресаты:
Немного подправил. Сделал, как говорил
Нашли внутренние, их ci штук, внешних co=N-3-ci
И ищем минимум |co-ci|

Вывожу минимальную разность
[code h=200]
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>

#define N 100
typedef struct _point
{
double x;
double y;
}point;

// вычисляет положение точки D(x,y) относительно AB
// важен знак
double g(point a, point b, point d)
{
return (d.x - a.x) * (b.y - a.y) - (d.y - a.y) * (b.x - a.x);
}

// лежат ли точки C и D с одной стороны прямой (AB)
// если результат g() одного знака, то с одной стороны!
bool f(point a, point b, point c, point d)
{
return g(a, b, c) * g(a, b, d) >= 0;
}

int main()
{
point* p = (point*)malloc(N*sizeof(point)); //выделим память в куче
int i,j,k,m; //переменные цикла
int ci, co; //количество внутри и снаружи
int cdelta; //их минимальная разность
point a,b,c; //для сохранения оптимальных точек
srand(time(NULL)); //инициализация генератора псевдослучайных чисел
for(i=0; i<N; i++) //заполним случайными числами [-100,99]
{
p[i].x = (rand()%200) - 100;
p[i].y = (rand()%200) - 100;
}
cdelta = N; //сначала считаем, что минимальная разность = N
for(i=0; i<N-2; i++) //перебираем все точки в трех циклах
{
for(j=i+1; j<N-1; j++)
{
for(k=j+1; k<N; k++)
{ //считаем точки, которые внутри
ci = 0; //обнуляем счетчик
for(m=0; m<N; m++) //по всем остальным точкам
{
if ((m != i) && (m != j) && (m != k))//обходим текущие
//проверяем, чтобы точка p[m] была по одну сторону
//со всеми вершинами относительно оставшейся стороны
//только тогда точка p[m] будет внутри
//если все три = true, только тогда инкрементируем счетчик внутренних
ci += f(p[i],p[j],p[k],p[m]) && f(p[j],p[k],p[i],p[m]) && f(p[k],p[i],p[j],p[m]);
}
co = N-3-ci; //количество внешних точек
if (abs(co-ci) < cdelta) //ищем минимальную разность
{
cdelta = abs(co-ci); //новая минимальная разность
a = p[i]; //сохраняем точки
b = p[j];
c = p[k];
}
}
}
}
printf("cdelta = %d\n", cdelta); //выведем минимальную разность

free(p); //освободим память
return 0;
}
[/code]
А вообще-то, математику надо знать, молодой человек
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
399292
22
20.05.2016, 15:12
общий
Адресаты:
Теперь все понятно! Большое спасибо! Вы очень помогли!
давно
Посетитель
402105
8
30.05.2018, 17:52
общий
Адресаты:
Пожалуйста, объясните, каким образом эта строка
ci += f(p[i], p[j], p[k], p[m]) && f(p[j], p[k], p[i], p[m]) && f(p[k], p[i], p[j], p[m]);
И подпрограммы помогают понять местоположение точек относительно друг друга! Какие правила/теоремы используются? И еще
a = p[i]; //сохраняем точки
b = p[j];
c = p[k];
Как вывести abc на кран? cout не помогает
давно
Посетитель
7438
7205
30.05.2018, 18:12
общий
Адресаты:
Вы думаете, я держу в голове программы двухлетней давности?
Сначала отвечу на последний вопрос: g вызывается из функции f
Далее, функции f возвращает true, если точка p[m] будет внутри треугольника, заданного точками p[i],p[j],p[k] в любой последовательности.
Если получим три true, то точка точно внутри, и добавляем единицу к количеству внутренних точек. Если будет false, то количество внутренних не изменится.
Теория - аналитическая геометрия. Смотрите самостоятельно.
Вывести на экран (у нас С, а не С++) можно при помощи printf, ну или (для С++) добавьте соответствующую h-ку. Тоже разберетесь самостоятельно
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
402105
8
30.05.2018, 18:17
общий
30.05.2018, 18:18
Адресаты:
Что-то забыл подумать о дате)) Вроде понял. Но все же, если вдруг вспомните правила, по которым вы разобрались, лежит точка внутри или нет, то дайте знать) и еще: я учу с++, мне просто мало что показалось здесь отличным от с++ в вашем коде, как можно осознать что это си?
bool f(point a, point b, point c, point d)
{
return g(a, b, c) * g(a, b, d) >= 0;
}
В с++ разрешено так условия записать или нет?
давно
Посетитель
7438
7205
30.05.2018, 18:30
общий
Адресаты:
Простое отличие Си от С++: используемые include-ы. Да и в тексте вопроса было сказано...
В с++ разрешено так условия записать или нет?
Конечно Никто не мешает считать код, как С++. Замените соответствующие include-ы, некоторые функции и вуаля...
Правила - векторная алгебра и аналитическая геометрия. Мне некогда Вам их рассказывать. Найдите самостоятельно.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
30.05.2018, 18:34
общий
Адресаты:
Код:
return g(a, b, c) * g(a, b, d) >= 0;
Кстати, это не условие, а возврат true или false, в зависимости от того, одного ли знака результат указанных функций.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа