Синтезатор уравнений и тренировщик их решения
Программа сама генерирует линейные уравнения через аппарат случайных чисел, т.к. линейное уравнение 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; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!