Синтезатор уравнений и тренировщик их решения



Программа сама генерирует линейные уравнения через аппарат случайных чисел, т.к. линейное уравнение A*x + B = C*x + D относительно х зависит только от 4 чисел: A, B, C, D. И также сделаны всякие приёмчики-дополнения чтобы это уравнение красивше выводилось. Например, если какой-то из этих коэффициентов равен 0, 1 или -1.

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

#include <iostream>

#include <cstdlib>

#include <ctime>

#include <cmath>

using namespace std;

 

// поле, которое выводится наверху- сколько уравнений ты решил и сколько из них правильных или неправильных

void info (int win, int lose) {

           system("CLS");

           cout << "Решено: " << win << "/" << win+lose << endl << endl << endl;

}

 

// функция, которая решает линейное уравнение- все 4 коэффициента мы храним в виде одного массива

template <typename Type>

Type solve (Type A[]) {

           return (A[3] - A[1]) / (A[0] - A[2]);

}

 

// функция синтезатора линура- по сути синтезируется массив коэффициентов

double* syntesis () {

           double *res= new double [4];

           int *eq= new int [4];

           do {

                           for(int i=0;i<4;i++) {

                                           eq[i]= -9 + rand()%19;

                                           res[i]= eq[i];

                           }

           } while ( (res[0]==res[2]) || (1000*solve(res)!=1000*solve(eq)) );

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

           return res;

}

 

// вывод сгенерированного уравнения

void print (double A[]) {

           cout << "Решите уравнение: " << endl;

           if(A[0]!=0) {

                           if(abs(A[0])!=1) cout << A[0] << "*x";

                           if(A[0]==1) cout << "x";

                           if(A[0]==-1) cout << "-x";

           }

           if((A[1]>0)&&(A[0]!=0)) cout << " + ";

           if(A[1]<0) {

                           if(A[0]!=0) cout << " ";

                           cout << "-";

                           if(A[0]!=0) cout << " ";

           }

           if(A[1]!=0) cout << abs(A[1]) << " ";

           if((A[0]==0)&&(A[1]==0)) cout << 0 << " ";

           if((A[1]==0)&&(A[0]!=0)) cout << " ";

           cout << "= ";

           if((A[2]==0)&&(A[3]==0)) cout << 0;

           if(A[2]!=0) {

                           if(abs(A[2])!=1) cout << A[2] << "*x";

                           if(A[2]==1) cout << "x";

                           if(A[2]==-1) cout << "-x";

           }

           if((A[3]>0)&&(A[2]!=0)) cout << " + ";

           if(A[3]<0) {

                           if(A[2]!=0) cout << " ";

                           cout << "-";

                           if(A[2]!=0) cout << " ";

           }

           if(A[3]!=0) cout << abs(A[3]);

           cout << endl;

           cout << "x= ";

}

 

// главная функция

int main() {

           srand(time(NULL)); setlocale(0,"");

           int win=0, lose=0; bool stop= false; int k=0;

           int t1= clock();

 

           // главный цикл

           while(!stop) {

                           info(win,lose);

                           double *A= syntesis(); print(A);

                           double x; cin >> x;

                           if(x==solve(A)) { cout << "Верно"; win++; }

                           else { cout << "Неверно"; lose++; }

                           cout << endl << endl;

                           system("pause");

                           info(win,lose);

                           cout << "(0) Продолжить\n(1) Закончить" << endl;

                           cin >> stop;

                           system("CLS");

           }

 

           // итоговая статистика

           info(win,lose);

           cout << "Решено " << win*100/(win+lose) << "%" << endl;

           double x= clock()-t1; x/=60000;

           cout << "Скорость: " << win/x << " уравнений в минуту" << endl;

           cout << endl;

           system("pause");

}

 

Обратные функции

Как мы можем сосчитать арифметический корень, если б у нас не было такой функции? Предположим, надо сосчитать корень из 7. Ну мы считаем 1*1=1. Если 1<7, то продолжаем. 2*2=4. 4<7? Продолжаем. 3*3=9. 9<7? Нет. Тогда теперь считаем 2.1*2.1= 4.41 и т.д. по такому принципу. Так же мы можем считать и другие обратные функции. Ниже программа на примере обратной функции от f(x)= x*(e^x), тут e- число "е" или 2.71828 примерно. Введём обратную функцию как kat(x), т.е. если f(x)= y, то x= kat(y). Например, kat(2.71828)= 1 (приблизительно). Тут считаются значения только больше нуля, т.к. только на этом промежутке функция монотонна и однозначна.

#include <iostream>

#include <cmath>

using namespace std;

const double e= exp(1);

double f (double x) { return x * pow (e,x) ; }

double kat (double x) {

           if(x==0) return 0;

           double y=0; double k=0;

           while(k>(-7)) {

                           if((f(y)<x)&&(f(y+pow(10,k))>x)) k--;

                           else y+=pow(10,k);

           }

           return y;

}

int main() {

           setlocale(0,""); double x;

           while(true) {

                           system("CLS"); cout << "x= "; cin >> x;

                      if(x>=0)           cout << "Подбор со степенным шагом: kat(x)= " << kat(x) << endl;

                           system("pause");

           }

}

 

Теория вероятности

Ниже представлены функции работы с вероятностями по формуле Бернулли, Гаусса и Лапласа. А также все вспомогательные.

 

// библиотеки, которые могут пригодиться

#include <iostream>

using namespace std;

#include <cmath>

 

// факториал

long long int fact (int x) {

           if(x==0) return 1;

           else return x*fact(x-1);

}

 

// число сочетаний

long long int cnk (int n, int k) { return fact(n)/(fact(k)*fact(n-k)); }

////////////////////////////////////////////

 

// функция, считающая вероятность по формуле Бернулли (ниже будет краткая теория)

double Bernulli (int n, int k, double p=0.5) {

           double res= 1;

           for (double i= 1; i<=(n-k); i++) res*= ((1-p)*(k+i)/i);

           res*= pow(p,k);

           return res;         

}

 

// функция Гаусса- для приблизительного расчёта по формуле Бернулли

double GaussFunc (double x) { return pow(2*M_PI,-0.5) * exp ( -x*x/2 ); }

 

// вывод таблицы функции Гаусса

void GaussTable(double step= 0.01) {

           for (double i=0; i<=4; i+=step) cout << "f(" << i << ")= " << GaussFunc(i) << endl;

}

 

// вероятность по формуле Гаусса (как Бернулли- только для приближённых расчётов)

double Gauss (int n, int k, double p) {

           double R= pow(n*p*(1-p),-0.5);

           return R * GaussFunc((k-n*p)*R);

}

 

// Распределение- тоже как Бернулли, но начальные числа немного другие

double Raspred (int luck, int fail, double p=0.5) {

           double res= 1;

           for (double i= 1; i<=fail; i++) res*= ((1-p)*(luck+i)/i);

           res*= pow(p,luck);

           return res;

}

 

////////////////////////////////////////////

 

// функция расчёта интеграла

double integral (double A, double B, int r=5) {

           double res=0;

           int n= pow(10,r);

           double h= (B-A)/n;

           for(int i=0; i<n; i++) res+= h * ( GaussFunc(A+i*h) + GaussFunc(A+(i+1)*h) ) / 2 ;

           return res;

}

 

// функция Лапласа- интеграл от функции Гаусса- нужна для расчёта вероятности на промежутке

double LaplasFunc (double x) { return integral(0,x); }

 

// вывод таблицы Лапласа

void LaplasTable(double step= 0.1) {

           for (double i=0; i<=4; i+=step) cout << "F(" << i << ")= " << LaplasFunc(i) << endl;

}

 

// вероятность по функции Лапласа

double Laplas (int n, int k1, int k2, double p) {

           double x1,x2;

           x1= (k1-n*p)/sqrt(n*p*(1-p));

           x2= (k2-n*p)/sqrt(n*p*(1-p));

           return LaplasFunc(x2) - LaplasFunc(x1);

}

 

////////////////////////////////////////////

 

// вероятность успеха при n попыток если вероятность 1 попытки равна p

double Deneyim (int n, double p=-1) {

           if(p==-1) p= 1.0/n;

           return 1 - pow(1-p,n);   

}

 

Теперь некий пример как это всё работает. Например, мы стреляем в мишень. Вероятность попасть в цель равна 0.8. И нужно сосчитать вероятность того что за 10 попыток мы попадём 3 раза- тут 3 это число успехов (k) , 10- всего попыток, 0.8- вероятность успеха. И по формуле Бернулли сосчитаем: Bernulli(10,3,0.8).

Вероятность по функции Гаусса делает то же самое- проблема формулы Бернулли в расчётах когда у нас большие числа. Но тем не менее сделан алгоритм удачного домножения.

Есть нюанс- программа вылетает, когда мы работаем с большими факториалами. Поэтому мы домножаем частично: for (double i= 1; i<=(n-k); i++) res*= ((1-p)*(k+i)/i);

Функция распределение- то же по Бернулли, но вместо попыток всего мы вбиваем число неудач n-k.

Функция Лапласа- считает вероятность на промежутке. Например, мы делаем 1000 выстрелов. И найти вероятность того что мы попадём в цель 307-415 раз. Т.е. тут задаётся промежуток. Ну и вероятность одного выстрела p тоже вводится.

Использовать Гаусса и Лапласа рекомендуется когда n*p*(1-p) >= 10. Тогда погрешность будет незначительной.

 

Статистика

Ниже функции работы со статистикой. Все данные у нас будут одного типа и лежать в неком исходном массиве.

 

// библиотеки и функции, которые могут пригодиться

#include <cmath>

template <typename Type>

double q2 (Type x) { return x*x; }

///////////////////////////////////////////////////////////////

 

// среднее арифметическое

template <typename Type>

double Average (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= m[i];

           return sum/size;

}

 

// среднее геометрическое

template <typename Type>

double AverageG (Type m[], int size) {

           double prod= 1;

           for(int i=0; i<size; i++) prod*= m[i];

           return pow (prod, 1.0/size);

}

 

// среднее квадратическое

template <typename Type>

double AverageQ (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= q2(m[i]);

           return sqrt(sum);

}

 

// среднее гармоническое (нули только не вводим сюда во избежание ошибок)

template <typename Type>

double AverageH (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= 1.0/m[i];

           return 1.0/sum;

}

 

///////////////////////////////////////////////////////////////

 

// амплитуда- разница между максимальным и минимальным

template <typename Type>

Type Amplitude (Type m[], int size) {

           Type min= m[0], max= m[0];

           for(int i=0; i<size; i++) if (m[i]<min) min= m[i];

           for(int i=0; i<size; i++) if (m[i]>max) max= m[i];

           return abs(min-max);

}

 

// медиана- элемент с серединным порядковым номером

template <typename Type>

Type Mediana (Type m[], int size) {

           if(size%2) return m[size/2];

           else return ( m[size/2] + m[(size-1)/2] ) / 2;        

}

 

// мода- самый часто встречающийся элемент

template <typename Type>

Type Moda (Type m[], int size) {

           int *md= new int[size]; for(int i=0; i<size; i++) md[i]= 0;

           for(int i=0; i<size; i++) for(int j=0; j<size; j++) if(m[j]==m[i]) md[j]++;

           int poz=0;

           for(int i=0; i<size; i++) if(md[i]>md[poz]) poz=i;

           return m[poz];

}

 

///////////////////////////////////////////////////////////////

 

// линейное отклонение от среднего арифметического

template <typename Type>

double DispL (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= abs(Average(m,size) - m[i]);

           return sum;

}

 

// среднеквадратичное отклонение от среднего арифметического

template <typename Type>

double DispQ (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= q2( Average(m,size) - m[i] );

           return sqrt(sum);

}

 

// процент отклонения от среднего

template <typename Type>

double ProcDispL (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= abs(Average(m,size) - m[i]);

           return sum / Average(m,size);

}

 

template <typename Type>

double ProcDispQ (Type m[], int size) {

           double sum= 0;

           for(int i=0; i<size; i++) sum+= abs(Average(m,size) - m[i]);

           return sqrt(sum) / Average(m,size);

}

 

///////////////////////////////////////////////////////////////

 

// вспомогательные функции работы с векторами-массивами

template <typename Type>

Type ArrayScalar (Type m[], Type n[], int size) {

           Type res= 0;

           for (int i=0; i<size; i++) res+= m[i]*n[i];

           return res;

}

 

template <typename Type>

Type *ArrayOf (Type value, int size) {

           Type *res= new Type[size];

           for (int i=0; i<size; i++) res[i]= value;

           return res;

}

 

template <typename Type>

Type* SubstOfArrays (Type m[], Type n[], int size) {

           Type *M= new Type [size];

           for(int i=0; i<size; i++) M[i]= m[i]-n[i];

           return M;

}

 

template <typename Type>

Type* ProdOfArrays (Type m[], Type n[], int size) {

           Type *M= new Type [size];

           for(int i=0; i<size; i++) M[i]= m[i]*n[i];

           return M;

}

 

// математическое ожидание

double MathWait(double data[], double p[], int size) { return ArrayScalar(data,p,size); }

 

// дисперсия

double Dispersion (double data[], double p[], int size) {

           double *MathWait= ArrayOf (ArrayScalar(data,p,size), size);

           double *Razn= SubstOfArrays(data,MathWait,size);

           double *Quadr= ProdOfArrays(Razn,Razn,size);

           return ArrayScalar(Quadr,p,size);

}

 

// среднеквадратичное отклонение- корень из дисперсии

double AQD (double data[], double p[], int size) { return sqrt(Dispersion(data,p,size)); }

 

Комплексные числа

Работу с комплексными числами я выделил в отдельный заголовочный файл, где представлен класс комплексных чисел, перегрузки и прочие методы работы с ними. Комплексное число состоит только из двух элементов- действительная часть и мнимая. Эти 2 числа просто типа double. И далее просто прописываются правила работы с этой парой чисел.

#ifndef KATMUR_COMPLEX

#define KATMUR_COMPLEX

 

// вспомогательные функции и библиотеки, которые могут пригодиться

#include <iostream>

using namespace std;

 

#include <cmath>

double q2(double x) { return x*x; }

 

// знак числа плюс или минус

double sign(double x) {

           if(x>=0) return 1;

           else return -1;

}

char Sign(double x) {

           if(x>0) return '+';

           if(x<0) return '-';

}

 

///////////////////////////////////////////////////////////////

 

// объявление нового типа данных- комплексное число

class complex {

           // вспомогательная функция работы с аргументом- в блоке private

           double purearg() { return atan(Re/Im); }

public:

           double Re, Im; // вещественная и мнимая часть

           complex (double r1=0, double i1=0) { Re=r1; Im=i1; } // конструктор

           double mdl() { return sqrt(q2(Re)+q2(Im)); } // модуль

           //

           complex sopr() { complex c; c.Re= Re; c.Im= -Im; return c; } // сопряжённое число

           //

           // аргумент комплексного числа

           double arg() {

                           if(Re>0) return purearg();

                           if(Re<0) return purearg() + M_PI*sign(Im);

                           if(Re==0) return M_PI*sign(Im)/2;

           }

           //

           // арифметика

           complex operator+(complex z) {

                           complex c;

                           c.Re= Re + z.Re;

                           c.Im= Im + z.Im;

                           return c;

           }

           complex operator-(complex z) {

                           complex c;

                           c.Re= Re - z.Re;

                           c.Im= Im - z.Im;

                           return c;

           }

           complex operator*(complex z) {

                           complex c;

                           c.Re= Re*z.Re - Im*z.Im;

                           c.Im= Re*z.Im + Im*z.Re;

                           return c;

           }

           complex operator/(complex z) {

                           complex c;

                           c.Re= (Re*z.Re+Im*z.Im)/q2(z.mdl());

                           c.Im= (z.Re*Im-z.Im*Re)/q2(z.mdl());

                           return c;

           }

           //

           // тригонометрическая и алгебраическая записи числа

           void trigon() {

                           cout << mdl() << " * " << "( cos(" << arg() << ") + sin(" << arg() << ") )" << endl;

           }

           void algebr() {

                           cout << Re << " " << Sign(Im) << " " << abs(Im) << " * i" << endl;

           }

           //

           // возведение в натуральную степень (по формуле Муавра)

           complex operator^(int n) {

                           complex c;

                           c.Re= pow(mdl(),n)*cos(n*arg());

                           c.Im= pow(mdl(),n)*sin(n*arg());

                           return c;

           }

};

 

// перегрузки вывода и ввода

ostream& operator<<(ostream& str, complex& c) {

           cout << "(" << c.Re << ", " << c.Im << ")";

           return str;

}

istream& operator>>(istream& str, complex& c) {

           cout << "Ввод в формате <Re> <Im>";

           cout << "—> "; cin >> c.Re >> c.Im;

           cout << endl;

           return str;

}

 

// корень натуральной степени из комплексного числа- по той же формуле Муавра

void root (complex c, int n) {

           complex *res= new complex[n];

           for(int k=0; k<n; k++) {

                           res[k].Re= pow(c.mdl(),1.0/n)*cos((c.arg()+2*M_PI*k)/n);

                           res[k].Im= pow(c.mdl(),1.0/n)*sin((c.arg()+2*M_PI*k)/n);

                           cout << "Корень " << k+1 << ": " << res[k] << endl;

           }

}

complex *roots (complex c, int n) {

           complex *res= new complex[n];

           for(int k=0; k<n; k++) {

                           res[k].Re= pow(c.mdl(),1.0/n)*cos((c.arg()+2*M_PI*k)/n);

                           res[k].Im= pow(c.mdl(),1.0/n)*sin((c.arg()+2*M_PI*k)/n);

           }

           return res;

}

 

// завершение заголовочного файла

#endif

 


Дата добавления: 2018-09-22; просмотров: 253; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!