Как использовать векторы, чтобы найти среднее и стандартное отклонение - PullRequest
0 голосов
/ 24 марта 2019

Это задание:

Напишите две функции, которые рассчитывают среднее значение и стандартное отклонение. Ваши функции должны реализовывать следующие прототипы:

double mean(vector<double>x);
double sd(vector<double>x);

б. Поместите эти функции в файл с именем «statfun.cpp».

с. Поместите определения их функций в файл с именем «statfun.h».

  1. Написать функцию main () в файле с именем «lab1.cpp».

  2. Предложите пользователю ввести 10 значений с плавающей запятой и сохранить их в векторе v.

  3. Вывести вектор v на одной строке, каждый элемент которого разделен пробелом.

  4. Назовите ваши функции означают (v) и sd (v) ...

Я знаю, как кодировать формулу для среднего, но я не уверен, как кодировать формулу для стандартного отклонения, используя векторы. Я даже менее уверен, как это сделать с различными файлами. Я полностью осознаю, что мой код - мусор, но я не уверен в том, что есть много вещей, которые я не знаю в первую очередь.

Редактировать: Обновлен код

//statfun.h
#include <iostream> 
#include <vector>
#ifndef STATFUN_H
#define STATFUN_H
using namespace std;

double mean(vector<double> v);

double sd(vector<double> v);

#endif

//statfun.cpp
#include <iostream>
#include <cmath>
#include <vector>
#include "statfun.h"
#ifndef STATFUN_CPP
#define STATFUN_CPP
using namespace std;

double mean(const vector<double> v) {

    double result;

    double sumVal = 0.0;            //Calculating sum of all values
for (int i = 0; i < v.size(); ++i) {
    sumVal = sumVal + v.at(i);
}

result = sumVal / v.size(); //Calculating mean

return result; 
}

double sd(const vector<double> v) {
    double total = 0;
    for (int i = 0; i < 10; ++i) {      //Calcuating standard deviation
        double mean_value = mean(v);
        int length = v.size()
        total = total + (val - mean_value)*(val - mean_value);
}

    return sqrt(total / length);
}

#endif 

//lab1.cpp
#include "statfun.cpp"
#include <iomanip>
using namespace std;

vector<double> v;

int main() {

    cout << "Enter 10 numbers: " << endl;

    float userInput = 0;
    for (int i = 0; i < 10; ++i) {
        cin >> userInput;
        v.push_back(userInput);
    }

    for (int i = 0; i < 10; ++i) {
        cout << v.at(i) << " ";
    }

    cout << endl;

cout.precision(3);
cout << mean(v) << " " << sd(v) << endl;

cout.precision(5);
cout << scientific << mean(v) << " " << sd(v) << endl;

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 24 марта 2019

Вы сделали много ошибок, и ваш код может многое улучшить.

Позвольте мне показать вам меня.

  1. Заголовок

Поскольку один файл может включать заголовок несколько раз, для предотвращения любого побочного эффекта для каждого заголовочного файла требуется include guard .

// statfun.h
#ifndef  __statfun_H__
# define __statfun_H__

# include <vector>

double mean(const std::vector<double>&);
double sd(const std::vector<double>&);

#endif

Кстати, объявление функции может сокращать имя аргумента.

  1. Ссылки

Вторая ошибка, которую вы сделали, заключается в том, что вы не использовали ссылку . В c++ объект по умолчанию передается по значению.

Примечание: Это похоже на R, за исключением того, что у него нет семантики copy-on-write уровня языка, но пользовательский класс может реализовать это, классы, определенные в пространстве имен std, могут также реализовать это.

Таким образом, чтобы предотвратить дорогостоящее копирование, сделана ссылка .

double mean(const std::vector<double>&);

Здесь я использовал const left-value reference (const &), поскольку mean не изменит переданный вектор.

  1. Функциональные блоки.

В c++ функция определяется следующим образом:

return_value func_name(type1 arg1 /* , type2 arg2, ... */)
{
    // The function body goes here:
}

Итак

// statfun.cpp
// c++11
#include "statfun.h"
#include <cmath>

double mean(const std::vector<double> &v)
{
    double sum = 0;

    for (auto &each: v)
        sum += each;

    return sum / v.size();
}

double sd(const std::vector<double> &v)
{
    double square_sum_of_difference = 0;
    double mean_var = mean(v);
    auto len = v.size();

    double tmp;
    for (auto &each: v) {
        tmp = each - mean_var;
        square_sum_of_difference += tmp * tmp;
    }

    return std::sqrt(square_sum_of_difference / (len - 1));
}
  1. Вычет типа переменной времени компиляции

Как вы могли заметить в приведенном выше коде, я использовал auto len = v.size(), который является языковой функцией c ++ 11 - auto .

Поскольку c++11, c++ может определить тип возвращаемых вызовов функций во время компиляции. Таким образом, вместо определения переменной, такой как typename std::vector<double>::size_type len = v.size(), теперь у нас есть auto len = v.size().

  1. диапазон для петля

Если вы выучили python, то вы должны знать range-for. Так как c++11, c++ также может сделать это:

for (auto &each: v) {
    // Loop body
}

, где v может быть std::vector или любой другой контейнер в c++.

  1. Проверка ошибок ввода-вывода

И последнее, но не менее важное: вы не проверяли, успешно ли выполнено какое-либо из этих IO, выполненных вами на std::cout или std::cin!

Используя std::cout или std::cin, вы должны проверять состояние потока по std::cout.fail() каждый раз после выполнения ввода-вывода или использовать следующий код:

std::cout.exceptions(std::ios_base::failbit | std::ios_base::badbit);
std::cin.exceptions(std::ios_base::failbit | std::ios_base::badbit);

Для выполнения std::cout и std::cin бросков при сбое ввода-вывода.

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

Ниже приведен последний фрагмент кода:

// lab1.cpp
// c++11
#include "statfun.h"
#include <iostream>

auto get_use_input() -> std::vector<double>
{
    std::vector<double> v;
    v.reserve(10);

    double userInput;
    for (int i = 0; i != 10; ++i) {
        std::cout << "Please enter the " << i + 1 << " number: ";
        std::cin >> userInput;
        std::cout << std::endl;

        v.push_back(userInput);
    }

    return v;
}

void print_vec(const std::vector<double> &v)
{
    std::cout << "Vector: ";

    for (auto &each: v)
        std::cout << each << " ";

    std::cout << std::endl;
}

int main() {
    // Configure std::cout and std::cin to throw if io fails.
    std::cout.exceptions(std::ios_base::failbit | std::ios_base::badbit);
    std::cin.exceptions(std::ios_base::failbit | std::ios_base::badbit);

    /*
     * With "-O3" or [c++17, copy elision](https://en.cppreference.com/w/cpp/language/copy_elision), 
     * the cost of initializing an object using the return value of anther function is nearly zero.
     */
    std::vector<double> v = get_use_input();

    print_vec(v);

    std::cout.precision(3);
    std::cout << "mean: " << mean(v) << " sd: " << sd(v) << std::endl;

    std::cout.precision(5);
    std::cout <<std::scientific << "mean: " << mean(v) << " sd: " << sd(v) << std::endl;

    return 0;
}

Для сборки этой программы необходим компилятор c++, который поддерживает c++11 и передает -std=c++11 компилятору.

PS: Вы также можете использовать -std=c++14 или -std=c++17.

Простой Makefile для сборки программы:

cxx = ${CXX}
# The flags 
CPPFLAGS := -std=c++11

# The two line below is the flags I used for clang++-8
# CPPFLAGS := -std=c++17 -Ofast -pipe -flto
# LDFLAGS := -flto -pipe -Wl,--icf=all,-O2,--as-needed,--strip-all,--plugin-opt=O3

lab1: lab1.o statfun.o
    $(CXX) $(LDFLAGS) $^ -o $@

statfun.o: statfun.h
lab1.o: statfun.h

.PHONY: clean
    rm -f lab1.o statfun.o lab
0 голосов
/ 24 марта 2019

Я считаю, что ваша первая проблема заключается в понимании файловой структуры вашего назначения статистики.Решить это первым. Понимание заголовков. Подробнее о заголовках и вызовах функций из других файлов.

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

Пример:

statfun.h

double mean(vector<double> v);
// other **declaration** stuff....

lab1.cpp вверху файла

#include "statfun.h" // equivalent to copy/pasting 'double mean(vector<double> v); and other declarations' into your lab1.cpp
// This is to help with cleanliness of your file structure. 
// You'll thank yourself when projects become bigger.

Примечание: lab1.cpp включает statfun.cpp, который включает statfun.час;неявно, lab1.cpp включает statfun.h, что означает, что вам не нужно включать statfun.h в lab1, хотя обычно включается заголовок, а не файл cpp.Вы должны избегать циклических зависимостей, которые вы делаете с помощью ifndef.

b.statfun.cpp должен быть местом, где вы кодируете всю свою логику для среднего и стандартного отклонения.Например:

statfun.cpp

double mean(vector<double> v) {
    // Your mean calculation logic here.
    return mean;
}

double sd(vector<double> x) {
    // Your standard deviation calculation logic here.
    return sd;
}

c.

Итак, у вас есть lab1.cpp, который будет скомпилирован для создания исполняемого двоичного файла.Как точка входа в вашу программу, она должна включать функцию int main ().Эта основная функция должна запрашивать ввод данных пользователем (поиск в Интернете о том, как принимать ввод std). Сохраните стандартный ввод в виде вектора (это все еще входит в вашу основную функцию). Используйте cout для печати на стандартном выходе.'cout << name_of_variable_with_vector_input_from_user;'(все еще в вашем int main ()) </li> Вызовите / используйте функции, которые вы написали в statfun.cpp (в частности, mean () и sd ()).Может быть, сохранить их возвращаемые значения в переменной, чтобы использовать позже.Так как здесь вам нужно вызывать функции statfun, входной файл lab1.cpp должен включать statfun.h, чтобы он знал, какой код выполнять при вызове этих функций.

Теперь, когда эта логика структуры файлазавершено.Простой способ вычислить отклонение стандартного отклонения в псевдокоде:

statfun.madeuplanguage

type sd(vector<type> values) {
    type total = 0;
    type mean_value = mean(values);
    for val in values {
        total += (val - mean_value)^2;
    }
    total /= values.length;
    return sqrt(total);
}

Имея это в виду, я бы структурировал lab1.cpp следующим образом.

lab1.cpp

int main() {
    vector<double> v;
    // take input and store in v.
    // std out - v

    double mean_val = mean(v);    
    double std_dev = sd(v);

    // std out - mean_val and std_dev
}

Если у вас есть какие-либо вопросы по поводу реализации вышеуказанного псевдокода в C ++, отлично!Это ваше задание / класс, поэтому позаботьтесь о поиске в сети, выполняя крайне специфические вещи в C ++ (например, итерации по вектору, возведение в квадрат, квадратный корень и т. Д.).Удачи в обучении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...