2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3  След.
 
 Две задачи по Си (1 курс)
Сообщение14.09.2009, 21:27 
Аватара пользователя


05/12/06
126
Нижний Новгород
Прошу помощи. Помогите разобраться с несколькими заданиями, и разъяснить некоторые детали.
Задача 1: Выяснить взаимное расположение окружностей. Заданы их координаты и радиусы.

Код:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
#include <conio.h>
#include <stdio.h>
#include <math.h>
#define EPS 1e-7

int main()
{
    float x01, x02, y01, y02;
    float r1, r2, dl;
    char ag;
    printf("\nThis program will determine the relative positions of the circles.");

    do
    {
// Input ON
        printf("\nPlease input x-coord of the first circle: ");
        scanf("%f", &x01);
        printf("Please input y-coord of the first circle: ");
        scanf("%f", &y01);
        do
        {
             printf("Please input radius of the first circle: ");
             scanf("%f", &r1);
        } while (r1<=0);
        printf("Please input x-coord of the second circle: ");
        scanf("%f", &x02);
        printf("Please input y-coord of the second circle: ");
        scanf("%f", &y02);
        do
        {
                printf("Please input radius of the second circle: ");
                scanf("%f", &r2);
        } while (r2<=0);
// Input OFF

//        dl=sqrt(((x01-x02)*(x01-x02))+((y01-y02)*(y01-y02)));
        dl=hypot(x01-x02,y01-y02);
        if (dl==0) {
            if (r1>r2) printf("Second circle is embedded in the first.");
            if (r2>r1) printf("First circle is embedded in the second.");
            if (r1==r2) printf("Circles coincide with each other.");
            } else
        if (dl>(r1+r2)) printf("The circles do not interact."); else
        if (dl==(r1+r2)) printf("The circles have one common point. External touch."); else
        if ((dl<fabs(r1-r2))  /*&&(dl!=0)*/ &&(r1>r2)) printf("The second circle inside the first."); else
        if ((dl<fabs(r1-r2))  /*&&(dl!=0)*/ &&(r2>r1)) printf("The fisrt circle inside the second."); else
        if (fabs(dl-(r1+r2))<EPS) printf("The circles have one common point. Internal touch. The second circle inside the first."); else
        if (fabs(dl-(r1+r2))<EPS) printf("The circles have one common point. Internal touch. The second circle inside the first."); else
        if (((r1-r2)<dl)&&(dl<(r1+r2))) printf("The circles intersect.");
        printf("\nAgain? ('n' - no, anykey - yes).");
        ag=getch();
    } while (ag!='n');
    printf("\nThanks for using.\n");
    return 0;
}
 


У меня получился такой вот код. А теперь, если можно, попридирайтесь ко всему, что заметите.

Задача 2: Даны координаты двух векторов, исходящих из одной точки. Необходимо сосчитать градусную меру угла, составленного из этих векторов, при условии, что считать будем именно от вектора 1 до вектора 2. То есть, против часовой стрелки, с конкретно определенными векторами (может получиться угол > 180 и пр.).

Задачу эту наверняка можно решить, определя перпендикуляр, вычислить проэкцию одного из векторов, если проэкция будет положительна, значит второй вектор находится в той же полуплоскости что и первый. Если отрицательна, значит при вычислении угла нужно вычесть все это из 180 градусов.

Я попробовал предложить такую идею. То есть, нужно как-то програмно поворачивать первый вектор (или ось, которая будет совпадать с первым вектором), допустим, на один градус, в цикле, до тех пор пока он не совпадет со вторым вектором. Из количества итераций можно будет найти градусную меру. Можно ли реализовать данную идею? Возможно придется поворачивать вектор не на один градус, а на полградуса (нужно как-то осторожно определить шаг, чтобы ось не перескочила через вектор и не зациклилась). И вообще, как крутануть на один градус именно против часовой стрелки? Можно ли это сделать и насколько это оправдано?

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 21:54 


21/03/06
1545
Москва
Вместо
Код:
dl=sqrt(((x01-x02)*(x01-x02))+((y01-y02)*(y01-y02)));

Лучше использовать
Код:
dl = hypot (x01 - x02, y01 - y02);


Целесообразнее использовать тип переменных double, как основной в современных процессорах и компиляторах. float все равно скорее всего приведется к double, а это - лишняя операция.

А в целом код неплохой.

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 21:58 
Заслуженный участник
Аватара пользователя


18/05/06
13438
с Территории
В первой задаче не отмечен случай внутреннего касания.
Во второй - допустим, можно. Ещё можно написать программу, которая будет искать произведение двух целых чисел путём перебора всех чисел. Примерно столь же - - -

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 22:16 
Заслуженный участник


26/07/09
1559
Алматы
2int13
Цитата:
Даны координаты двух векторов, исходящих из одной точки. Необходимо сосчитать градусную меру угла, составленного из этих векторов

Попробуйте воспользоваться скалярным произведением, т.е. для $a,\ b\in\mathbb{R}^2$, из $\left<a|b\right>=|a|\cdot|b|\cdot\cos\alpha$ можно найти искомый угол $\alpha$. Примерно так.

Цитата:
И вообще, как крутануть на один градус именно против часовой стрелки? Можно ли это сделать и насколько это оправдано?

Конечно можно (умножением на матрицу поворота), только не оправдано.

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 22:34 
Аватара пользователя


05/12/06
126
Нижний Новгород
Эм.. Случай внутреннего касания..
Можно его описать так? dl+r2=r1 - значит вторая окружность внутри первой и касается изнутри
И dl+r1=r2 - первая внутри второй? Это будет достаточно полно?
Еще, изредка, в некоторых случаях у меня, бывает, срабатывают сразу несколько условий, например
Код:
        if ((dl<fabs(r1-r2))  /*&&(dl!=0)*/ &&(r1>r2)) printf("The second circle inside the first.");

и
Код:
            if (r1>r2){ printf("Second circle is embedded in the first.");

Можно как-нибудь этого избежать, чтобы после первого срабатывания, все остальные проверки пропускались?
Для этого обязательно нужно вводить новую переменную, и перед каждой проверкой, сперва проверять переменную?

Код:
dl = hypot (x01 - x02, y01 - y02);

Это функция из math.h? (Мне просто нужно уметь обьяснить любой отрезок программы и быть готовым к любой самой мелочной придирке)

Circiter в сообщении #243496 писал(а):
Попробуйте воспользоваться скалярным произведением

А как в таком случае определить, где вектор один и где вектор два? В этом случае ведь будет искаться наименьший угол между векторами, а нам нужно найти строго от первого до второго (против часовой стрелки)

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 22:46 
Заслуженный участник


26/07/09
1559
Алматы
2int13
Цитата:
Можно как-нибудь этого избежать, чтобы после первого срабатывания, все остальные проверки пропускались?

Используйте break при выполнении условия. Попробуйте также применить конструкцию switch.

-- Вт сен 15, 2009 02:28:28 --

Цитата:
А как в таком случае определить, где вектор один и где вектор два?

Порядок можно детектировать например по знаку определителя матрицы, составленной из этих векторов (для $\mathbb{R}^2$).
То есть, если $a=(a_x,a_y)$, $b=(b_x,b_y)$, то порядок определяется как $sgn(a_x b_y-a_y b_x)$. В одном из случаев нужно будет вычитать найденный угол из $2\pi$, а потом переводить результат в градусы (умножением на $180/\pi$). Не, лучше сначала переведите в градусы, а потом, если нужно, отнимите от 360. :)

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 23:32 
Аватара пользователя


05/12/06
126
Нижний Новгород
Хм, если этот способ сработает - не нужно будет возиться ни с какими проэкциями, можно облегчить себе работу чуть ли не в два раза.
Сейчас попробую написать.
P.S. Всем спасибо :)

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение14.09.2009, 23:49 
Заслуженный участник


26/07/09
1559
Алматы
Цитата:
У меня получился такой вот код. А теперь, если можно, попридирайтесь ко всему, что заметите.

Ужас. Ну да ладно. Вот конкретные замечания:
  • Зачем вам переменная result? Выбросьте.
  • Инициализировать радиусы не надо (не обязательно присваивать ноль при объявлении).
  • Чего вы хотите добиться первыми двумя строчками вашей программки?

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 00:17 
Аватара пользователя


05/12/06
126
Нижний Новгород
Код:
        do
        {
             printf("Please input radius of the first circle: ");
             scanf("%f", &r1);
        } while (r1<=0);


Инициализировал, на всякий случай. Не может быть такого, что при обьявлении, там окажется положительное значение и проверка не сработает?
А, тьфу, хотя я все равно эту переменную перепишу.
Result уже выбросил, я просто хотел сделать проверку еще одну, но после сделал все через switch.
Насчет первых двух строчек, оно уже было автоматически при создании проэкта, это все можно ведь выбросить без последствий?

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 00:33 
Заслуженный участник


26/07/09
1559
Алматы
Цитата:
это все можно ведь выбросить без последствий?

Ну я же не знаю, что у вас за компилятор, и знать не хочу. :) Просто по стандарту этого не надо. Еще по стандарту надо писать int main(void).

-- Вт сен 15, 2009 05:43:28 --

На самом деле считать угол через скалярное произведение, а потом проверять знак через внешнее -- излишество.

Набросал пример решения вашей задачи сразу с использованием псевдоскалярного умножения (внешнее произведение на плоскости a.k.a. косое произведение). Угол находится из формулы $a\wedge b=|a|\cdot|b|\cdot\sin\alpha$. Вот исходничек:

код: [ скачать ] [ спрятать ]
Используется синтаксис C
#include <stdio.h>
#include <math.h>

#define sqr(x) x*x

typedef struct{float x,y;} Vector;

void ReadPoint(Vector *Point)
{
    printf("> ");
    scanf("%f, %f",&Point->x,&Point->y);
}

float Norm(Vector *Point){return sqr(Point->x)+sqr(Point->y);}

/*
**  Calculates determinant of a matrix with given rows.
*/

float ExteriorProduct(Vector *First, Vector *Second)
{
    return
        First->x*Second->y
        -First->y*Second->x;
}

/*
**  Evaluates angle (in degrees) using relation
**  between sine of this angle and exterior
**  product on Cartesian plane.
*/

float Angle(Vector *First, Vector *Second)
{
    float Length, Result=0;

    if(Length=Norm(First)*Norm(Second)) /* Avoid division by zero. */
        Result=asin(ExteriorProduct(First,Second)/sqrt(Length));

    Result*=180/M_PI; /* Convert radians to degrees. */

    return Result<0?360+Result:Result; /* Invert angle if needed. */
}

int main(void)
{
    Vector First, Second;

    ReadPoint(&First);
    ReadPoint(&Second);

    printf
        (
            "\nAngle between given points is %3.3f degrees.",
            Angle(&First,&Second)
        );

    return 0;
}
 


Основными здесь являются функция ExtreriorProduct(), вычисляющая псевдоскалярное произведение и Angle(), вычисляющая угол, поворот первого из векторов на который переводит этот вектор в коллинеарный второму.

Протокол проверки:
Код:
$ ./test
> 3, 2
> -1, 2

Angle between given points is 82.875 degrees.


$ ./test
> 3, 2
> -1, -4

Angle between given points is 317.726 degrees.


Вроде бы работает. :)

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 08:23 
Заслуженный участник
Аватара пользователя


18/05/06
13438
с Территории
Дай-ка докопаюсь до комментов. :lol: Не Invert angle, а wrap around; и не угол между точками (как это?), а...

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 08:54 


21/03/06
1545
Москва
Цитата:
Можно как-нибудь этого избежать, чтобы после первого срабатывания, все остальные проверки пропускались?

Естественно можно, используйте конструкцию
Код:
if (...) {}
else if (...) {}
else if (...) {}
...
else {}

Оператор switch Вам не поможет (во всяком случае, исходя из реализованной Вами логики условий), ибо для него необходимо сравнение с целой константой(за исключением экзотических компиляторов).

Цитата:
Это функция из math.h? (Мне просто нужно уметь обьяснить любой отрезок программы и быть готовым к любой самой мелочной придирке)

Да, функция hypot входит в math.h по стандарту ANSI C, ANSI C++.

-- Вт сен 15, 2009 09:01:10 --

int13 в сообщении #243524 писал(а):
Код:
        do
        {
             printf("Please input radius of the first circle: ");
             scanf("%f", &r1);
        } while (r1<=0);


Инициализировал, на всякий случай. Не может быть такого, что при обьявлении, там окажется положительное значение и проверка не сработает?

Не может, т.к. примененная Вами конструкция do {} while (...); подразумевает обязательное исполнение кода в do {} хотя бы один раз, только потом происходит проверка. Однако, явная инициализация переменных - хороший тон, вместо того, чтобы запоминать, в каких случаях переменные автоматически инициализируются нулем (глобальные и static), а какие нет (локальные).

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 09:36 
Заслуженный участник


26/07/09
1559
Алматы
2ИСН
Цитата:
и не угол между точками (как это?)

Ну, в афинном пространстве разница между точками и векторами конечно есть. Но если взять именно плоскость $\mathbb{R}\times\mathbb{R}$, то точки на ней (суть пары вещественных чисел) одновременно и векторы евклидова лин. пространства, ведь так? Значит в этом случае углы можно измерять и между точками. :)

Хотя лучше говорить именно о радиусах-векторах этих точек, согласен. :)

Цитата:
Не Invert angle, а wrap around

Кстати, как это по-русски то правильно хоть сказать. :) Угол, дополняющий данный до $2\pi$?

2e2e4
Цитата:
Да, функция hypot входит в math.h по стандарту ANSI C, ANSI C++.

Хм, это наверное в новых редакциях, я по крайней мере почему-то не встречал. Посмотрю ещё раз.

-- Вт сен 15, 2009 13:23:42 --

Почему никто не заметил, что мои идея и код бессмысленны и не работают?

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 12:11 
Аватара пользователя


05/12/06
126
Нижний Новгород
Отредактировал первый пост, теперь это вроде окончательный вариант. (Надеюсь никаких модулей и проверок на строгость\нестрогость не упустил), но пока вроде работает. Работаю в C++Builder 6, если что :)
Насчет второго, что-то не совсем понял - сейчас буду разбираться..

 Профиль  
                  
 
 Re: Две задачи по Си (1 курс)
Сообщение15.09.2009, 12:34 
Заслуженный участник
Аватара пользователя


06/10/08
6422
Circiter в сообщении #243557 писал(а):
Хм, это наверное в новых редакциях, я по крайней мере почему-то не встречал. Посмотрю ещё раз.

Это в C'99 добавили, но во многих реализациях уже была до этого.
Кстати, int main() вместо int main(void) тоже можно писать по стандарту.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 42 ]  На страницу 1, 2, 3  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group