Моделирование падающего шара

Я сделал простую симуляцию падающего шара. Можно ли как-то улучшить или оптимизировать этот код? Какие советы вы можете дать для развития?

#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;

int timeFallingBall() {
    static int time(0);
    return ++time;
}
void fallingBall() {
    cout << "From what height do we drop the ball? (In meters): ";
    double h;                                           
    cin >> h;
    double v = 0, high = 0, s = 0, maxv = 0, g = 9.8;   
    int t = 0;
    double hi = h;
    while (h) {
        t = timeFallingBall();
        v = static_cast<double>
        high = (v * static_cast<double>
        h = hi - high;
        if (h <= 0) {
            switch 
            case 1:
                cout << "After " << t << " second the ball has reached the ground! Maximum speed: " << maxv << " m/s";
                break;      
            default:
                cout << "After " << t << " seconds the ball has reached the ground! Maximum speed: " << maxv << " m/s";
            }
            break;
        }
        switch 
        case 1:
            cout << "After " << t << " second, the ball is at a distance of " << h << " m from the ground at a speed: " << v << " m/s" << endl;
            break;
        default:
            cout << "After " << t << " seconds, the ball is at a distance of " << h << " m from the ground at a speed: " << v << " m/s" << endl;
            break;
        }
        if (v > maxv) {
            maxv = v;
        }
    }
}
int main() {
    system("chcp 1251>nul");
    fallingBall();
    return 0;
}

4 ответа
4

  • using namespace std; это плохая привычка, поскольку она может привести к конфликтам имен и другим проблемам. Лучше явно указать нужные нам имена там, где это необходимо, например std::cout.

  • Было бы полезно в коде сделать еще несколько переносов строки. Думайте о коде с определенной целью как о абзаце при написании текста. Например, это можно рассматривать как один «абзац», поэтому после него должна быть пустая строка.

     std::cout << "From what height do we drop the ball? (In meters): ";
     double h;                                           
     std::cin >> h;
    
  • У нас есть несколько переменных с одинаковыми названиями h, high а также hi. Это затрудняет понимание кода.

  • Переменные, которые не изменяются, должны быть отмечены const. (например g).

  • Переменные, как правило, следует объявлять как можно ближе к точке использования, устанавливать сразу же полезные значения и не использовать повторно (если они не потребляют значительные ресурсы). Например, t, v, high а также h все могут быть объявлены там, где они установлены внутри цикла.

  • s похоже, не используется.

  • У нас нет возможности сбросить статическую переменную времени в timeFallingBall(). Это означает, что мы не можем позвонить fallingBall во второй раз, чтобы сбросить еще один мяч. Мы легко можем просто увеличить t в fallingBall функция вместо этого.

  • while (h) переводится на while (h != 0). С h это double с расчетным значением, сравнивать его с точным числом – плохая идея. Мы должны использовать while (h > 0.0) вместо этого, или while (true) поскольку мы обрабатываем условие выхода внутри цикла.

  • В C ++ числовые литералы имеют определенные типы: 0 является int, 0.f это float, 0.0 это дубль. При сравнении или назначении переменных лучше использовать буквальный стиль правильного типа переменной, а не полагаться на преобразования.

  • std::endl выводит новую строку, но также без необходимости очищает выходной поток. Мы можем вывести ” n”, чтобы получить новую строку без сброса.

  • Обратите внимание, что при постоянном ускорении вниз максимальная скорость всегда будет скоростью, когда мяч ударяется о землю. Так что нам действительно не нужно это отслеживать.

  • (Очевидно, что разрешение в 1 секунду делает вывод максимальной скорости довольно некорректным).


Применяя вышеизложенное, я бы, вероятно, выбрал что-то вроде:

void fallingBall()
{
    std::cout << "From what height do we drop the ball? (In meters): ";
    double h_start;
    std::cin >> h_start;

    if (h_start <= 0.0)
    {
        std::cout << "The ball is already on the ground!n";
        return;
    }

    const double g = 9.81;

    double t = 0.0;

    while (true)
    {
        t += 1.0;

        const double h_current = h_start - 0.5 * g * t * t;
        const double v_current = g * t;

        if (h_current <= 0.0)
        {
            std::cout << "After second " << t << ", the ball has reached the ground! Maximum speed: " << v_current << " m/sn";
            break;
        }

        std::cout << "After second " << t << ", the ball is at a distance of " << h_current << " m from the ground at a speed: " << v_current << " m/sn";
    }
}

  • 1

    Спасибо за совет. Для меня это действительно очень важно. За счет использования пространства имен std, как говорится в материале, в котором я изучаю язык, почему не стоит этого делать. Я узнал об этом только после написания этого кода и не мог заметить это до публикации. Остальные советы я постараюсь применить и улучшить понимание кода. Спасибо за помощь в улучшении кода стиля.

    – ТулеКр1с

  • 3

    Этот вопрос о переполнении стека подробно объясняет, почему using namespace std плохо.

    – Роланд Иллиг

  • 2

    Почему бы не вложить логику внутрь if (h_current <= 0.0) после цикла while и вместо этого измените условие на while (h_current > 0)?

    – JansthcirlU

Комментарии @ user673679 о стиле, именах переменных и странностях наличия static int внутри специальной функции все в порядке, поэтому я не буду повторять большую часть этого.

Вы переоцениваете конечную скорость на 0,9999 … секунды дополнительного времени падения над уровнем земли., т. е. наихудшая ошибка почти 9,8 м / с, если на предыдущем временном шаге объект находился над землей на наименьшее возможное double что может произойти из-за ошибки округления в hi - high когда вы вычитаете два соседних числа1. Меньший временной шаг моделирования или какая-то интерполяция при обнаружении столкновения будет намного лучше. (Физика здесь проста, так что вы мог интерполировать точно обратно с высоты под землей, но даже линейная интерполяция, т. е. предположение, что объект движется с постоянной скоростью с последнего шага, позволит вам более точно оценить время, когда он ударится о землю, и, таким образом, скорректировать полное падение время и, следовательно, скорость.)

Или просто решите уравнение и рассчитайте правильную конечную скорость для этого простого случая; как @G. Слипен прокомментировал: t = std::sqrt(2 * h / g), и из этого t с постоянным ускорением вы можете просто рассчитать скорость. Так v = sqrt(2 * h / g) * g, или принимая g внутри sqrt и упрощая: v = std::sqrt(2 * h * g);

Примечание 1: вычитание двух ближайших чисел FP плохо для числовой точности в целом; ошибка округления называется «катастрофической отменой». Но в этом случае, похоже, вы хотите знать, когда остановиться, поэтому hi <= high избежал бы этого. Либо это high <= hi? Не возвращаясь к проверке вашего кода, я забываю, какой именно из них, потому что их имена совершенно не различают их.

switch с повторяющимся кодом, за исключением одной буквы, сильно раздувает ваш код.

Распространенная ленивая техника ... second(s) чтобы указать, что читатель должен вывести единственное или множественное число на основе числа. Это кажется уместным и достаточно хорошим, когда вас больше всего интересует симуляция физики. @ user673679 указывает, что в этом случае вы можете сформулировать его порядковыми числами, используя число в качестве метки для интервала, чтобы избежать необходимости множественного числа. “после второй 2” – нормально; немного неудобно читать.

switch определенно неправильный подход к выбору между двумя вещами; это то что if/else для. Если у вас когда-нибудь будет switch с одним case и один default:, вместо этого должно быть if / else. будет более компактным, но вы все равно будете повторяться.

   if (t == 1)
       std::cout << ... << 'n';
   else
       std::cout << ...s << "blah blahn";

Другой подход – просто выбрать сообщение и оставить тот же код. (Переключатель или, если мы надеемся, компиляция в asm работает таким образом, если ваш компилятор умен и замечает, что одни и те же функции передаются просто разными данными.)

    auto seconds_unit = (t == 1) ? "second" : "seconds";
    std::cout << "After " << t << seconds_unit << ", the ball is at a distance of " << h << " m from the ground at a speed: " << v << " m/sn";

std::endl полезен только в том случае, если вы хотите принудительно сбросить, даже если вывод полностью буферизован (например, перенаправлен в файл). Вывод на терминал уже будет автоматически сброшен при выводе новой строки, поэтому вы можете просто включить "n" в строковом литерале, если ваш вывод в любом случае заканчивается постоянной строкой вместо числа. Даже если вы делаете что-то медленно между отпечатками, не нужно std::endl чтобы убедиться, что интерактивные «обновления хода выполнения» отображаются тогда, когда вы хотите. Вот почему cout / stdout буферизуется по строке, когда он подключен к терминалу, а не к файлу.

  • Даже не знаю, почему я не придумал такой простой способ, спасибо, что напомнили мне об этом! А также спасибо за комментарии, для меня это очень важно.

    – ТулеКр1с


В дополнение к тому, что было опубликовано ранее, я хотел бы подчеркнуть, что такие проблемы должны разделять ввод, «настоящую проблему» и вывод в отдельные функции.

В вашем случае печать по ходу дела сложнее, чем ее стоит изолировать, но вы можете легко разделить подсказку пользователя и заставить функцию принимать параметр. Это упрощает многократный вызов с фиксированными входами для облегчения тестирования.

void falling_ball (double h_start);

int main()
{
    std::cout << "From what height do we drop the ball? (In meters): ";
    double h_start;
    std::cin >> h_start;
    falling_ball (h_start);
}

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

много слов, которые я не понимаю. пока я только начинаю и не могу понять, почему все это так, но когда я научусь и стану более умным разработчиком, я обязательно вернусь к этому посту и пойму. Спасибо!

– ТулеКр1с

Другие дали вам хороший совет по поводу вашего стиля. Я далек от С ++, чтобы комментировать в любом случае. Я предлагаю описанный ниже подход для реального решения представленной проблемы, а затем применяю советы других, чтобы сделать его хорошим C ++.

#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;

void falling_ball(double height)
{
    const double g = 9.81;
    double h = height;
    /* report progress of object falling under gravity 
       from height h every second until it reaches the ground
       Use the equations of motion: 
         v = u+at
         s = ut + 1/2 at^2  // assume u = 0
         => at^2 = 2s
         => t^2 = 2s/a
         => t = sqrt(2s/a)
         
     */
         
    double v = 0.0;
    double t = 0.0;
    double s = 0.0;

    do 
    {
        cout << "After " << 
             t << " seconds, the ball is at a distance of " << 
             h << " m from the ground at a speed: " << 
             v << " m/s" << endl;
        t ++; 
        v = (g*t);
        s = (0.5*(g*(t*t)));
        h = height - s;
    } while (h > 0.0); 
    t = std::sqrt(height*2/g);  //  calculate exact time of impact

    cout << "The ball reached the ground after " <<
         t << " seconds, at a speed of " <<
         (g*t) << " m/s" << endl;
}   

int main()
{
    std::cout << "From what height do we drop the ball? (In meters): ";
    double h_start;
    std::cin >> h_start;
    falling_ball (h_start);
}

Результат выглядит так:

From what height do we drop the ball? (In meters): 30
After 0 seconds, the ball is at a distance of 30 m from the ground at a speed: 0 m/s
After 1 seconds, the ball is at a distance of 25.095 m from the ground at a speed: 9.81 m/s
After 2 seconds, the ball is at a distance of 10.38 m from the ground at a speed: 19.62 m/s
The ball reached the ground after 2.4731 seconds, at a speed of 24.2611 m/s

  • не использовать using namespace std;

    – JDłuosz

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *