Целью данного проекта было изучение графического интерфейса. В частности, считывание введенной пользователем информации и вывод полученного результата в другой элемент управления. Еще одной особенностью, на мой взгляд, является то, что на кнопки нагружен дополнительный функционал. Математическая часть этого проекта, хоть и является ядром проекта, не самая большая его часть.
Проанализировав исходный код, можно понять, как переплетаются графическая и вычислительная части проекта.
Решение кубического уравнения происходит в отдельной функции, принимающей на входе коэффициенты уравнения.
// Функция cubic(...) предназначена для решения кубического уравнения // // a3*x^3 + a2*x^2 + a1*x + a0 = 0 // int cubic( // НА ВХОДЕ: double a0,double a1,double a2,double a3, // коэффициенты уравнения // НА ВЫХОДЕ: double &x1, // Всегда первый действительный корень double &x2, // Если уравнение имеет 3 действительных корня - // это второй корень уравнения; // в противном случае x2 - это действительная часть // комплексно-сопряженной пары корней double &x3, // Если уравнение имеет 3 действительных корня - // это третий корень уравнения; // в противном случае x3 - это модуль мнимой части // комплексно-сопряженной пары корней double eps ) // ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ: // * * * // 0 - корни действительные (x1 = x1, x2 = x2, x3 = x3); // 1 - один действительный и два комплексно сопряженных корня // * * * // (x1 = x1, x2 = x2 + x3*i, x3 = x2 - x3*i); // -1 - это не кубическое уравнение, т.к. a3 == 0. { double z, f, A, B, C, D, x_prev; int k_iter; if (a3 == 0) return -1; if (a3 < 0) { a3 = -a3; a2 = -a2; a1 = -a1; a0 = -a0; } x_prev = x1 = a0/3; k_iter = 0; // Находим действительный корень x1 методом Ньютона: for (;;) { f = (x1*(x1*(a3*x1 + a2)+a1)+a0); if (fabs(f) < eps) break; z = x1*(3*a3*x1+2*a2)+a1; if (fabs(z) <= 2.2e-16) { x1 *= 2; if (x1 == 0) x1 += 1; } x1 = x1 - f/z; if (x_prev == x1) { k_iter++; if (k_iter > 5) { k_iter = 0; eps *= 5; } } x_prev = x1; } A = a3; B = a2+A*x1; C = a1+B*x1; // A,B,C - коэффициенты квадратного уравнения D = B*B-4*A*C; // его дискриминант A *= 2; if (D >= 0) { // два остальных корня кубического уравнения - действительные D = sqrt(D); x2 = (-B-D)/A; x3 = (-B+D)/A; return 0; } D = sqrt(fabs(D)); x2 = -B/A; x3 = fabs(D/A); return 1; }
Отличительной особенностью в данном проекте является работа со шрифтом: задание его параметров и дальнейшее использование для вывода текста в многострочное текстовое поле ListBox
font.CreateFont( -10, // nHeight 0, // nWidth 0, // nEscapement 0, // nOrientation FW_NORMAL, // nWeight FALSE, // bItalic FALSE, // bUnderline 0, // cStrikeOut RUSSIAN_CHARSET, // nCharSet OUT_DEFAULT_PRECIS, // nOutPrecision CLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQuality DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily "Condensed"); // lpszFacename ((CListBox *)GetDlgItem(IDC_LIST1))->SetFont(&font);
Также интересна функция rfp(). Эта функция работает с входными данным, проверяет их достоверность, преобразует из строк в числа. После получения решения в строке int ier = cubic(a0,a1,a2,a3,x1,x2,x3,1e-6); формирует строку нужного формата и преобразует числа в строку. Указатель типа void * необходим для указания на определенную область памяти, в которой размещены данные неопределенного типа. Проще говоря, можно только запомнить область памяти. Но через подобный указатель нельзя вызывать никакие методы и члены класса.
UINT rfp(void *) { CString s; double a3,a2,a1,a0; double x1,x2,x3; double pr; unsigned long kp, i; stopper = FALSE; // Осуществляем проверку: int ier9 = _stscanf(o->m_pr,_T("%le"),&pr); int ier10= _stscanf(o->m_kp,_T("%lu"),&kp); int ier3 = _stscanf(o->m_a3,_T("%le"),&a3); int ier2 = _stscanf(o->m_a2,_T("%le"),&a2); int ier1 = _stscanf(o->m_a1,_T("%le"),&a1); int ier0 = _stscanf(o->m_a0,_T("%le"),&a0); int ier = ((CListBox *)o->GetDlgItem(IDC_LIST1))->InitStorage(100000, 100); //ASSERT(n != LB_ERRSPACE); ((CListBox *)o->GetDlgItem(IDC_LIST1))->ResetContent(); // В ier0, ier1 , ... - коды возврата функции _stscanf(...) // Если ier0 == 0 => чтение прошло неудачно. if ( !ier0 || !ier1 || !ier2 || !ier3 || !ier9 || !ier10 ) { AfxMessageBox(_T("Проверьте поля ввода !")); return 0; } //s.Format("%lu",kp); //AfxMessageBox(s); for (i=0;i<kp;i++) { if (stopper) break; a0=(1+(pr/100)*rand()/(double)RAND_MAX)*a0; a1=(1+(pr/100)*rand()/(double)RAND_MAX)*a1; a2=(1+(pr/100)*rand()/(double)RAND_MAX)*a2; a3=(1+(pr/100)*rand()/(double)RAND_MAX)*a3; // Решаем уравнение int ier = cubic(a0,a1,a2,a3,x1,x2,x3,1e-6); if (ier == 0) // Корни все действительные { s.Format(_T("%lg %lg %lg"),x1,x2,x3); } else // Один корень дейстивтельный, два других - комплексно-сопряженные { s.Format(_T("%lg %lg+(%lg)*i %lg+(%lg)*i"),x1,x2,x3,x2,-x3); } // %6.3lf ier = ((CListBox *)o->GetDlgItem(IDC_LIST1))->AddString(s); if (ier == LB_ERR || ier == LB_ERRSPACE) { AfxMessageBox("Error"); break; } s.Format("%lu",kp); o->SetDlgItemText(IDC_STATIC_TMP,s); } s.Format("%lu",i); AfxMessageBox(s); return 0; }
При нажатии кнопки Button5 меняется только заголовок кнопки.
void CZ1Dlg::OnButton5() { if (m_switch) { GetDlgItem(IDC_BUTTON5)->SetWindowText(_T("Пауза")); } else { GetDlgItem(IDC_BUTTON5)->SetWindowText(_T("Продолжить")); } m_switch = !m_switch; }
Перед закрытием, если поток существует, его надо приостановить:
if (m_potok) m_potok->SuspendThread();
Запись результатов итераций в текстовый файл.
void CZ1Dlg::OnButton4() { FILE *f = fopen("tmp.txt","a"); int ier = 10000, i; CString s; int n; n = ((CListBox *)GetDlgItem(IDC_LIST1))->GetCount(); for (i = 0; i < n; i++) { // Получили очередную строку из listbox'а: ((CListBox *)GetDlgItem(IDC_LIST1))->GetText(i,s); fprintf(f,"%s\n",(const char *)s); } fclose(f); }