Текст книги "Интернет-журнал "Домашняя лаборатория", 2007 №1"
Автор книги: Журнал «Домашняя лаборатория»
Жанры:
Хобби и ремесла
,сообщить о нарушении
Текущая страница: 37 (всего у книги 39 страниц)
• Следующий оператор CString UpperValue; создает новую переменную типа CString.
• Затем переменной UpperValue приравнивается значение переменной m_TestEdit, это можно сделать, так как они имеют одинаковый тип.
• Оператор UpperValue.MakeUpper(); переводит все символы переменной UpperValue в верхний регистр.
• Оператор if(UpperValue=="PAINT"); проверяет, если введено ли слово PAINT, то выполняются следующие три оператора:
Первый system(«pbrush.exe»); запускает графический редактор, так как не указан явный путь к файлу, то программа будет искать его в каталоге C: WINDOWS.
Второй оператор m_TestEdit=""; присваивает переменной окна редактирования значение NULL, а следующий оператор UpadteData(FALSE) – обновляет экран. Вследствие этого, весь текст из окна редактирования удалится.
• Заметьте, что мы перевели символы переменной UpperValue в верхний регистр – это нужно потому, что в языке C++ символы нижнего и верхнего регистра имеют разные значения, поэтому при переводе на верхний регистр, мы 100 % будем уверены, что написанное нами слово Paint совпадет с PAINT.
• Следующая конструкция операторов полностью идентична предыдущей, только проверяется равно ли значение переменной m_TestEdit слову CALCULATOR, если да, то запускается калькулятор, и окно редактирования очищается.
Вы закончили связывание событий с элементами управления
Резюме и Упражнение
Упражнение
Сейчас вы выполните упражнение. Вам необходимо включить в свою программу еще код, чтобы при вводе слова Веер в окне редактирования, компьютер подавал звуковой сигнал. Подсказка: Код для генерации сигнала MessageBeep((WORD)-2);
Ответ к упражнению
Для того, чтобы выполнить упражнение вам необходимо включить в конец функции OnChangeTestEdit следующий код:
if(UpperVatoe== «ВЕЕР»)
{
MessageBeep((WORD)-2);
m_ TestEdit=
UpdateData(FALSE);
}
Резюме
В этой главе вы получили некоторой опыт использования элементов управления и написали программу, которая имеет такие элементы управления, как окно редактирования, флажок и кнопка.
Вы также увидели процесс написания программы, использующей различные средства управления, который состоит в размещении элемента управления в диалоговой панели, настройки его свойств и связывания кода с событием данного элемента.
Теперь выполняем программу, для этого заходим в меню Build->Build Test.exe, а потом в меню Build выбираем Execute Test.exe
Вы закончили второй урок!
LESSON 3
Сейчас мы создадим программу DRAW.EXE Посмотрим на ее спецификацию и что она должна делать.
• Программа DRAW предназначена для рисования с помощью мыши линий установленного цвета и размера, программа будет рисовать линию в соответствии с движениями мыши.
• При нажатии на кнопку Exit программа завершается.
При запуске вашей программы, главное окно должно будет выглядеть так:
План урока:
1) Создание проекта программы
2) Визуальное проектирование диалоговой панели
3) Связывание элементов управления с событиями
4) Резюме, Упражнение и ответ к упражнению.
Теперь рассмотрим все пункты по порядку.
• Вначале запустим Visual C++. Зайдем в меню FILE —> NEW…, выберем там Project. Ваша панель должна выглядеть теперь так:
• Выберем там MFC AppWizard(exe), это значит вы хотите создать запускаемый файл.
• В Project Name задайте имя программы Draw. И щелкните ОК.
• В раскрывшемся окне (SТЕР1) выберете Dialog Based. Это означает, что ваша программа будет основана на диалоге. Щелкните Next.
• В STEP2задайте в Please enter title of your dialog имя программы The Draw Program.
Остальное оставьте без изменения и нажмите NEXT.
• В STEP3Оставьте все без изменения и щелкните NEXT. Здесь вы указали, что ваша программа будет иметь библиотеку с динамической компоновкой (DLL), а не статической.
• В STEP4 Оставьте все без изменения. Посмотрите, чтобы в Class Name было выбрано CTestApp и щелкните FINISH.
Вы закончили создание проекта программы!
Все каркасные файлы для вашей программы написал Visual C++, с помощью мастера настроек MFC AppWizard.
MFC – это вещь очень полезная, с помощью нее можно быстро создавать программы, так как все дежурные файлы он пишет сам, что сильно облегчает работу программиста, ведь не писать стандартный набор текста каждый раз!
А теперь мы спроектируем диалоговую панель.
Проектирование диалоговой панели
Сейчас мы визуально спроектируем диалоговую панель. Для этого вначале выберете закладку "Resourse View" и раскройте пункт Draw Resource, как показано на рис. 1.5.
Выполните следующие действия
• Щелкните два раза по надписи IDD_DRAW_DIALOG. Справа появится окно проектирования диалоговой панели.
• С помощью панели Controls расставьте элементы, управления как показано на рис. 2.0 и согласно таблице 2.0.
Теперь рассмотрим все пункты по порядку.
• В объекте Dialog Box вы задаете следующие данные:
Caption – название диалогового окна.
Font – здесь вы установили шрифт — System и его размер 10
• Static Text – это элемент текста, он вставляется как обычный элемент управления, но не выполняет никаких функций, так как служит для показа текста, который вы зададите в Captions. Так же мы задали некоторые графические установки для статистического текста – это Client Edge и Static Edge. Поздравляю, вы закончили визуальное проектирование диалоговой панели!
Связывание событий с элементами управления
Связывание кода с событием BN_CLICKED кнопки Exit
При нажатии на кнопку Exit программа Draw.Exe завершится.
• Чтобы связать код с событием BN_CLICKED кнопки EXIT, выполните следующие действия:
• Выберете ClassWizard в меню View
• Выберете закладку Message Maps в панели ClassWizard
• Используйте диалоговую панель ClassWizard для выбора следующего события:
Class Name: CDrawDIg
Object ID: IDC_EXIT_BUTTON
Messages: BN_CLICKED
• Щелкните на кнопку Add Fucntion и в раскрывшемся окне нажмите кнопку ОК.
• Сейчас ваша панель MFC ClassWizard должна будет выглядеть так:
• Нажмите кнопку Edit Code и напишите следующий код в функции OnExitButton():
void CDrawDlg::OnExitButton()
{
// TODO: Add your control notification handler code here
////////Мой код начинается здесь///////////
OnOK();
////////Мой код заканчивается здесь///////////
}
• Функция ОnОК(), которую вы ввели завершает программу, при нажатии на кнопку Exit.
Связывание кода с событием WM_MOUSEMOVE диалоговой панели
Сейчас мы свяжем код с событием WM_MOUSEMOVE, которое будет автоматически выполняться при передвижении мыши.
• Чтобы связать код с событием WM_MOUSEMOVE диалоговой панели, выполните следующие действия:
• Выберете ClassWizard в меню View
• Выберете закладку Message Maps в панели ClassWizard
• Используйте диалоговую панель ClassWizard для выбора следующего события:
Class Name: CDrawDIg
Object ID: CDrawDIg
Messages: WM_MOUSEMOVE
• Щелкните на кнопку Add Fucntion.
Нажмите кнопку Edit Code и напишите следующий код в функции OnMouseMove():
void CDrawDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
////////Мой код начинается здесь///////////
if((nFlags 8l MK_LBUTTON)==MK_LBUTTON)
{
CCIientDC dc(this);
dc.SetPixel(point.x, point.y, RGB(0,0,0));
}
//конец структуры IF
////////Мой код заканчивается здесь///////////
///…
• Код который вы ввели содержит единственный оператор IF:
• Оператор IF содержит в себе функции, которые будут выполняться, если условие оператора IF верно. Условие таково, если левая кнопка мыши нажата и произошло перемещение, то функции внутри оператора IF выполняются.
• Функция OnMouseMove выполняется при любом передвижении мыши, ее параметр nFlags показывает была ли нажата какая-нибудь клавиша клавиатуры(типа ALT или Shift) и кнопка мыши. Операция & проверяет прижата ли левая кнопка мыши при ее перемещении. Если это так, то выполняется код ниже оператора IF.
• Код в блоке оператора IF CCIientDC dc(this); создает объект контексного устройства, с помощью него вы сможете рисовать, его можно назвать воображаемым экраном в памяти компьютера. Научно говоря, dc(this) – это экземпляр класса CCIientDC с параметром конструктора this, с помощью экземпляра класса вы можете обращаться к функциям этого класса.
• Следующий оператор, который вы напечатали рисует точку в заданном месте (место щелчка мыши), используя параметры point.x и point.y, которые ему передает функция OnMouseMove, и заданного цвета, с помощью функции RGB();.
Сейчас у нас установлен черный цвет, чтобы установить красный, синий или зеленый, надо заменить параметры функции RGB() соответственно на 255, 0, 0, 0, 255, 0, 0, 0,255.
Рисование изображения точка за точкой
Чтобы увидеть в действии свой код, выполните следующие действия:
• Скомпонуйте и скомпилируйте программу.
• Запустите ее.
• Удерживая нажатой левую кнопку мыши, претащите ее в сторону.
• Вы видите, что точки рисуются не слитно друг с другом, это происходит по тому, что WINDOWS должен выполнять и другие задачи и не может полностью следить за Draw. Поэтому мы модифицируем нашу программу, так чтобы эти точки соединялись линиями.
• При выполнении функции OnMouseMove параметр point передает положение курсора во время последней проверки его положения.
Расширение возможностей программы DRAW
Сейчас вы расширите возможности программы Draw. Вы сделаете так, чтобы при передвижении мыши, рисовалась сплошная линия.
• Модифицируем программу следующим образом.
• Чтобы соединить точки линией функция OnMouseMove должна знать предыдущие координаты мыши, для этого нам нужно объявить две переменные m_PrevX и m_PrevY, для хранения координат по X и Y.
• Выведем на экран файл DrawDIg.h, для этого откроем Project Workspace, щелкнем на закладке File View и выполним двойной щелчек на пункте DrawDIg.h.
• Включите объявления переменных m_PrevX и m_PrevY следующим образом:
// DrawDIg.h: header file
//
////////////////////////////////////////////////////
// CDrawDIg dialog c
lass CDrawDIg: public CDialog
{
// Construction
public:
CDrawDlg(CWnd* pParent = NULL); // standard constructor
////////Мой код начинается здесь///////////
int m_PrevX;
int m_PrevY;
////////Мой код заканчивается здесь///////////
//…. //….
}
• Вы объявили две переменные целого типа для хранения предыдущих координат мыши. Заметьте, что вы их включили после слова public: – это означает, что эти переменные доступны всем функциям класса.
• Теперь модифицируем функцию OnMouseMove в файле DrawDIg.cpp
void CDrawDIg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
////////Мой код начинается здесь///////////
if((nFlags & MK_LBUTTON)==MK_LBUTTON)
{
CCIientDC dc(this);
// dc.SetPixel(point.x, point.y, RGB(123,211,98));
CPen NewPen(PS_SOLID, 10, RGB(255,0,0);
dc.SelectObject(&NewPen);
dc.MoveTo(m_PrevX, m_PrevY);
dc.LineTo(point.x, point.y);
m_PrevX=poi nt.x;
m_PrevY=poi nt.y;
}
////////Мой код заканчивается здесь///////////
CDialog::OnMouseMove(nFlags, point);
}
• Рассмотрим код приведенный выше:
• Код вызова функции SetPixel(), помещен в комментарий, так как он нам больше не понадобиться.
• Следующий код CPen NewPen(PS_SOLID, 10, RGB(255,0,0)) создает новое перо с именем NewPen класса СРеn с заданным размером шрифта 10 пикселов и красным цветом. Параметр PC_SOLID говорит, что будет рисоваться сплошная линия.
• После этого выполняется функция SelectObject, выбирающая новое перо:
dc.SelectObject(&NewPen);
Этой функцией вы включаете перо в работу.
• Следующие два оператора:
dc.MoveTo(m_PrevX, m_PrevY);
dc.LineTo(point.x, point.y);
рисуют линию, используя координаты начала(это определяет функция MoveTo()) и координат конца (LineTo).
• И последний два оператора:
m_PrevX=point.x;
m_P revY=point.y;
сохраняют в переменных m_PrevX и m_PrevY, текущие координаты, которые в следующий раз будут использоваться, как предыдущие(начальная позиция).
• Теперь запустите программу и проверьте правильность ее работы.
• Вы должны заметить, что при первом нажатии на кнопку мышки в любой области диалоговой панели, есть лишняя линия, которая выходит из краев окна. Дело в том, что вы при первом рисовании линии, т. е после первого щелчка мыши и ее передвижении, начальные координаты не определены, а будут известны только текущие.
• Это выглядит примерно так:
• Для решения этой проблемы, вы должны связать код событием WM_LBUTTONDOWN, которое происходит при нажатии на левую кнопку мыши.
• Выберете ClassWizard в меню View
• Выберете закладку Message Maps в панели ClassWizard
• Используйте диалоговую панель ClassWizard для выбора следующего события:
Class Name: CDrawDIg
Object ID: CDrawDIg
Messages: WM_LBUTTONDOWN
• Щелкните на кнопку Add Fucntion.
• Нажмите кнопку Edit Code и напишите следующий код в функции OnLButtonDown():
void CDrawDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
////////Мой код начинается здесь///////////
m_PrevX=point.x;
m_PrevY=point.y;
////////Мой код заканчивается здесь///////////
…
}
• Код, который вы напечатали, обновляет значения переменных m_PrevX и m_PrevY, тем местоположением мыши, где был совершен щелчок по ее левой кнопке, соответственно при первом и последующих нажатиях кнопки, линия будет начинаться из данной точки нажатия.
• Поэкспериментируйте с программой Draw и щелкните на кнопку Exit для ее прекращения.
Резюме и Упражнение
Упражнение
Сделайте так, чтобы линия, которую вы рисуете была шириной 5 пикселов.
Ответ к упражнению
Для того, чтобы выполнить упражнение вам необходимо изменить код функции OnMouseMove следующим образом:
void CDrawDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
////////Мой код начинается здесь///////////
if((nFlags 8l MK_LBUTTON)==MK_LBUTTON)
{
CCIientDC dc(this);
// dc.SetPixel(point.x, poi nt.y, RGB(123,211,98));
CPen NewPen(PS_SOLID, 5, RGB(255,0,0);
dc.SelectObject(&NewPen);
dc.MoveTo(m_PrevX, m_PrevY);
dc.LineTo(point.x, point.y);
m_PrevX=poi nt.x;
m_PrevY=poi nt.y;
}
////////Мой код заканчивается здесь///////////
CDialog::OnMouseMove(nFlags, point);
}
Резюме
Вы создали и выполнили программу Draw.exe, с помощью которой вы можете рисовать, путем передвижения мыши, при нажатой ее левой кнопки. Также вы познакомились с функцией OnMouseMove, которая выполняется при передвижении мыши, и с функцией OnLButtonDown – при нажатии на левую кнопку мыши.
Вы закончили третий урок!
МАТЕМАТИЧЕСКИЙ ПРАКТИКУМ
Задачи с решениями
Ведет Данила Мастер
1. Положим, что мы хотим приблизить вещественное число α € £(0,1) с точностью ε т. е. подобрать с помощью некоторого изображающего аппарата рациональное число – так чтобы p/q
| α – (p/q)|< ε
Воспользуемся для этого двумя изображающими аппаратами, именно систематическими (двоичными) дробями с одной стороны и цепными (непрерывными) дробями – с другой, и оценим потребные для хранения числа а количества информации при каждом из способов представления.
Случай систематических дробей хорошо известен. При разложении числа в двоичную дробь длины m, т. е. при использовании m значащих бит для мантиссы погрешность приближения числа α составит – (1/2)∙2-m = 2-m-1. Поэтому для достижения потребной точности ε мы должны иметь
2-m-1 < ε => -m – 1 < log2 ε => m > -log2 ε – 1 = log2(1/ε) – 1.
Таким образом для достижения точности ε мы должны затратить по меньшей мере
I2(ε) = log2 (1/ε) – 1 (1)
бит информации.
2. Разложим теперь наше число α в цепную дробь:
и возьмем в качестве представления числа α последовательность {а1, а2…., аn}, обрезая цепную дробь на n-м члене, т. е. беря n-ю подходящую дробь (поскольку α € (0,1), то, очевидно, а0 = 0).
Известно, что для записи числа αi, – требуется в среднем log2 αi – бит, и значит для хранения последовательности {а1, а2…., аn} этих бит потребуется по меньшей мере
Iс = Σni=1 log2 ai = log2 Пni=1ai
(2)
Остается связать данное количество информации с потребной точностью представления числа α. Этим мы и займемся.
Известно (см. [1], стр. 40, формула (30)), что подходящая дробь
построенная по числу а, приближает его с точностью 1/q2n:
|α – (pn/qn)| < 1/q2n
Поэтому для приближения α с точностью ε достаточно соблюдения условия
1/q2n < ε => qn > 1/√ε
Остается получить оценку сверху для qn, причем требуется оценить qn величиной, связанной с Ic. Сделаем это.
Для знаменателей qn подходящих дробей справедливо рекуррентное соотношение (см. [1], стр. 11, формула (7))
qк = aкqк-1 + aкqк-2,
причем по определению полагается q-1 = 0, q0 = 1. Тогда q1 = а1, q2 = a2a1 + 1, q3 = a3a2a1 + a3 + a1 и т. д.
Лемма. Для знаменателей qn верна оценка qn <= 2n-1πn, где πn= Пn i=1= ai.
Доказательство (методом мат. индукции). Для n = 1 утверждение верно, ибо q1 = а1. Предположим, что оценка верна для всех k и n. Тогда
поскольку выражение в круглых скобках не превосходит единицы ибо аn >= 1, аn+1 >= 1. Лемма доказана.
Опираясь теперь на лемму, заключаем, что для аппроксимации числа α с точностью ε должно быть:
1/√ε < qn =< 2n-1Пni-1 ai
Следовательно, используя (2), находим:
Таким образом, учитывая (1), получаем соотношение:
Ic(ε) >= (1/2)∙I2(ε) – n + 3/2
Впрочем, данная оценка довольно груба. Ее можно немного усилить.
С другой стороны, очевидно, qn >= Пn i=1 ai (доказывается тоже индукцией), поэтому если
1/q2n < ε < 1/q2n-1
то
qn-1 < 1/√ε < qn
(5)
и, значит
Поэтому,
Ic(ε) <= 1/2∙I2(ε) + 1/2 + log2 an
Если принять, что 1/q2n < ε, то оценка упрощается:
Ic(ε) <= 1/2∙I2(ε) + 1/2
Таким образом видим, что теоретическая нижняя граница для Ic(ε) – объема данных, потребных для хранения вещественного числа а, оценена нами как сверху (формулы (6) и (6’)), так и снизу (формула (4)). Поэтому в случае больших объемов информации (Ic(ε) —> оо) представление числа в виде цепной дроби требует примерно вдвое меньшего количества бит, т. е. достигаемое сжатие – порядка 50 %.
3. Оценку (6) можно несколько улучшить. Для этого достаточно вместо (3) использовать более точную оценку приближения произвольного числа а подходящими дробями. Именно, ([1]. стр. 30) имеют место неравенства:
(7)
Поэтому если дробь pn/qn – первая из последовательности подходящих дробей, которая приближает число а с точностью ε, то, очевидно, имеют место неравенства:
и в тоже время
Из последнего неравенства вытекает
qn-1qn <= 1/ε (8)
Но
qn-1qn = qn-1(anqn-1 + qn-2) >= anq2n-1
Поэтому (8) превращается в неравенство
каковая оценка является просто уточнением первого из неравенств (5). Поэтому точно так же, как и раньше получаем уточненную оценку для Ic(ε):
Список литературы
[1] А. Я. Хинчин. Цепные дроби. Государственное изд-во физ. – мат. лит-ры. М., 1961.
Задача: Три рыбака ловили рыбу и после ловли заночевали на берегу. Двое рыбаков заснули, а третий решил уехать домой со своей частью улова. Он разделил рыбу на 3 равные части, но при этом одна рыбина оказалась лишней. Он швырнул ее в воду, забрал свою треть и ушел.
Среди ночи проснулся второй рыбак и тоже решил уехать. Не зная, что первый рыбак уже ушел, он тоже поделил всю рыбу на 3 равные части, одна рыбина снова оказалась лишней, он ее тоже выкинул и ушел.
То же произошло и с последним рыбаком: проснувшись, он тоже разделил оставшийся улов на 3 равные части, выкинул одну рыбину и ушел.
Вопрос: какое наименьшее количество рыб могло быть у рыбаков?
Решение П. А. М. Дирака: Рыб было (-2). После того, как первый рыбак выкинул одну рыбину в воду, их осталось (-2) – 1 = -3. Потом он ушел, унося (-1) рыбу. Рыб стало (-3) – (-1) = -2. Второй и третий рыбаки просто повторили поступок своего товарища.
Из книги «Физики продолжают шутить».
На самом деле решение Дирака, хоть и остроумно, но, строго говоря, неверно, и во всяком случае неполно. Приведем полное, "математическое" решение, т. е. найдем ВСЕ решения.
Решение.
Пусть в начале рыб было n0 (количество, после того, как уехало 0 рыбаков). Первый рыбак выбросил одну (лишнюю) рыбину (n0 – > n0 – 1), после чего количество рыбин стало делиться на 3, и взял 1/3 оставшегося улова, и после себя он оставил n1 = (2/3)∙(n0 – 1) рыбин. Аналогичным образом действовали и остальные рыбаки, так что после отъезда 2-го рыбака рыб осталось n2 = (2/3)∙(n1 – 1), а после отъезда 3-го – n3 = (2/3)∙(n2 – 1). Подставляя в последнее равенство выражения для n2 и n1, получаем:
В итоге получаем уравнение, требующее разрешения в целых числах:
8n0 – 27n3 = 38.
Для упрощения расчетов имеет смысл уменьшить входящие в уравнение числа перейдя к новым переменным n0 = n0 + k, n3 = n3. Если взять k = 5, то уравнение превратится в
27n3 – 8n0 = 2. (1)
Это уравнение имеет вид
ах – by = с (1')
т. е. является диофантовым уравнением, и нужно найти все решения данного уравнения. Как это сделать?
Взглянем на уравнение (1') с точки зрения теории сравнений. В самом деле, требуется найти такое число х, что ах отличается от заданного числа с на слагаемое, кратное Ь. Иными словами, разность ах – с делится на Ь, т. е. (ах – с) = ()(mod b) или
ах = c(mod Ь). (1")
То же самое можно выразить словами: "ах сравнимо с с по модулю b" или "ах принадлежит тому же классу вычетов по модулю Ь, что и с". Т. е. мы свели уравнение с двумя неизвестными (х и у) к уравнению с одним неизвестным, поскольку ясно, что из (1'), зная х, сразу же можно найти у. Однако х все же не произволен, а именно – удовлетворяет (1").
Кроме того, можно усмотреть следующее. Уравнение (1') (или (1")) – это неоднородное сравнение первой степени (т. е. линейное сравнение). Согласно общей теории, общее решение неоднородного сравнения есть сумма частного решения неоднородного сравнения и общего решения однородного сравнения ах = ()(mod b). Таким образом, задача разбилась на две.
Займемся сначала неоднородным сравнением (1") – Рассматривая эквивалентное уравнение (1'), замечаем (стандартное рассуждение – см. [1]), что если числа а и b делятся на число k, то на это же число должно делиться и с. Поскольку это верно для любого общего делителя а и Ь, то это верно и для их наибольшего общего делителя (а, Ь) =< 1. Таким образом, делимость с на <1 – необходимое условие разрешимости сравнения (1").
В нашем случае а = 27, b = 8, (а, Ь) = 1, т. е. числа а и b взаимно просты, поэтому сравнение (1") разрешимо при любом с.
Из приведенного рассуждения следует и способ решения сравнения (1") – Если мы умеем решить уравнение ах0 – by0 =< d, то умножив его на целое число c/d (поскольку необходимо с делится на d), мы получим решение уравнения (1').
В нашем случае d = 1, и кратчайший способ решения уравнения ах0 – by0 = 1 дается в [2]. Именно, надо разложить число a/b в цепную дробь, и если а = рn, b = qn то положить х = (-1)n-1qn-1, y = (-1)n-1pn-1.Это следует просто из того, что qn_1pn – qnpn-1 = (-1)n-1.
В нашем случае
Поэтому
И в самом деле: 27∙3–8∙10 = 81–80 = 1, поэтому берем x0 = 3, у0 = 10. Значит частным решением уравнения аx1 – by1 = с будет х1 =3∙2, y1 = 10∙2.
Что касается однородного уравнения ах – by = 0, то очевидным семейством решений его будет х = b∙k, у = a∙k, k – произвольное целое число. То, что это общее решение однородного уравнения следует из того, что данное уравнение эквивалентно сравнению ах = ()(mod b) и в силу взаимной простоты а и b это сравнение можно поделить на а (см. [3]), после чего сравнение превращается в х = ()(mod b), т. е. х должно делиться на Ь.
В итоге, получаем решение
уравнения (1). Поэтому в исходных переменных получаем:
Если здесь положить k = —1, то получаем дираковское решение: n0 = n3 = —2. Однако видно, что оно вовсе не наименьшее, и существует множество других, еще меньше. Впрочем, в каком-то смысле дираковский ответ действительно наименьший из возможных: именно, если искать наименьшее по абсолютной величине возможное количество рыб, то таким в самом деле окажется (-2).
Список литературы
[1] Энциклопедия элементарной математики. Государственное изд-во технико-теоретической лит-ры. М.-Л., 1951, стр. 285.
[2] Энциклопедия элементарной математики. Государственное изд-во технико-теоретической лит-ры. М.-Л., 1951, стр. 303.
[3] Энциклопедия элементарной математики. Государственное изд-во технико-теоретической лит-ры. М.-Л., 1951, стр. 275–276.
Рассмотрим вопрос о количестве решений уравнения
ax = logax (1)
на полуоси х > 0 при 0 < a < 1. Именно, нас интересует вопрос о том, при каких a количество решений равно трем.
Если ψ(х) = ах, то loga х = ψ-1(х), и наше уравнение (1) принимает вид ψ(х) = v-1(х), что равносильно ψ(ψ(х)) = x или
(2)
Для удобства дальнейшего введем новую переменную t = х∙In а и функцию
Тогда
(3)
и уравнение (2) превращается в
(4)
Найдем количество решений данного уравнения. Для этого прежде всего исследуем функцию F(t).
Поскольку исходная функция ψ(х) определена на интервале х > 0 и 0 < а < 1, то In а < 0 и t = х In а < 0, т. е. функция F(t) определена на интервале t € (—оо,0).
Асимптотики в предельных точках: limt->-ooF(t) = 0–0, limt->0–0F(t) = —oo. Т. е. функция F имеет горизонтальную и вертикальную асимптоты.
Далее,
Рис. 1: График функции F(t)
Для нахождения экстремумов функции F рассмотрим функцию φ(t) = tet и найдем корни уравнения φ(t) = 1/ln a. Видно, что на интервале t € (—оо,0) имеют место соотношения: limt->oo φ(t) = 0–0, φ(0) = 0. Далее, φ'(t) = et(t + 1), φ"(t) = et(t + 2) и вообще φ(n)(t) = et(t + n). Поэтому minimum функции φ находится в точке tmin – 1 и равен φmin = – e-1
Рис. 2: График функции φ(t) и определение положения точек t1, и t2.
Значит:
1) При 1/ln a <= – e-1 <=> a >= e-e экстремумов у функции F нет.
2) При а < е-e функция F имеет один minimum в точке t1, равный Fmin = aet1/t1 и один maximum в точке t2 > t1, равный Fmax = aet2/t2; при этом t1 < = tmin= -1 и t2 > tmin = -1.
Таким образом уравнение (4) имеет три решения только в случае 2) и лишь в том случае если
Fmin > 1/ln a < Fmax. (5)
При этом в случае 2) условие (5) является не только необходимым, но и достаточным для наличия у уравнения (4) трех решений. Точки t1 и t2 определяются условиями φ(t1) = t1et1 = φ(t2) = t2et2 = 1/ln a. Т. е. необходимое и достаточное условие наличия трех решений принимает вид
Левые части уравнений в условиях (6) не зависят от а, и потому эти уравнения имеют вид f(t) = g(a), в то время как неравенства (6) данным свойством не обладают (обе их части зависят от а), что неудобно. Выразим из первого уравнения et1= 1/t1lna и подставим это в соответствующее неравенство. Тогда получим
Аналогично, Fmax = e1/t2/t2. Тогда условия (6) превращаются в
Вспоминая определение функции φ, перепишем условия в форме:
Данные условия удобны тем, что левые части их не зависят уже от а (т. к. функция φ не зависит от а) и имеют вид f(t) = g(а) (т. е. переменные t и а разделены).
Рис. 3: Графики функций φ(t) (красный) и φ(1/t) (синий) и определение точек t1 и t2 (зеленая прямая – на уровне 1/ln a).
Проверку условий (9) проведем в два этапа: сначала докажем выполнение усиленного варианта второго из условий (9), а затем увидим, что первое условие (9) следует отсюда уже автоматически.
Поскольку точки t1 и t2 определяются как точки пересечения графика функции φ(t) с горизонтальной прямой на высоте 1/ln a, функция φ(t) имеет единственный minimum в точке tmin = —1, то ясно, что t1 < —1 < t2.
Покажем, что Vt € (—1,0) φ(1/t) > φ(t). Для этого рассмотрим функцию ξ(t) = φ(t)/φ(1/t) = t2еt – 1/t'. Ясно, что ξ(-1) = 1, а поскольку
Дальше все просто. Т. к. φ(t) < φ(1/t) Vt € (—1,0), то (обозначив 1/t через τ):
Поскольку t1 < —1 < t2, то соединяя (11) и (12), мы получим оба условия (9). Что и требовалось.
Коль скоро при a < е-e оба условия (9) выполнены, то действительно функция F имеет 1 minimum и 1 maximum, выполняется условие (5), и уравнение (4) в самом деле имеет три решения. Значит, и эквивалентное ему исходное уравнение (1) имеет три решения. Указанное положение дел иллюстрируется Рис. 4.
Рис. 4: Графики функций у = ax (красный), у = loga х (синий) и у = х (зеленый) – случай трех точек пересечения.
Одна из точек пересечения графиков функций у = ax (красный) и у = loga x; (синий) лежит на прямой у = х, т. е. является еще и решением уравнений аx = х и loga х = х, а остальные две симметричны относительно этой прямой. При а —> е-e данные точки «слипаются» на прямой у = х, при а = е~е имеет место касание графиков функций у = ах и у = loga х, а в дальнейшем, т. е. при a > е-e точка пересечения будет уже одна, и находиться она будет, конечно же, снова на прямой у = х (Рис. 5).
Рис. 5: Графики функций у = ах (красный), у = loga х (синий) и у = х (зеленый) – случай одной точки пересечения.
Рассмотрим уравнение линейного одномерного классического осциллятора с трением (уравнение затухающих колебаний):
х∙∙ + 2δх∙ + w20х = 0. (1)
Соответствующее характеристическое уравнение
λ2 + 2δλ + w20 = 0 имеет корни
λ1,2 = – δ ± √(δ2 – w20) – δ ± ip,
где
p = √(w20 – δ2)
Поэтому общее решение уравнения (1) есть:
x(t) = e-δt(Ae-ipt + Beipt). (2)
Уравнение второго порядка – две произвольные постоянные для того, чтобы удовлетворить любым начальным условиям.
Однако, здесь возникает трудность. Вот что говорит по этому поводу Л. И. Мандельштам («Лекции по теории колебаний», стр. 138):
«Рассмотрим последний случай, когда
δ = w0, λ1 =λ2
При этом решение (2) принимает вид:
х = Ае-λt. (3)
Если мы захотим приспособить такое решение к начальным условиям, то нам не хватит одной постоянной интегрирования. Нетрудно, однако, показать, что в этом специальном случае наряду с решением вида (3) имеет решение вида tе-λt и общее решение таково:
х = Ае-λt + Btе-λt. (4)
В нем опять имеются две независимые константы, и его можно приспособить к любым начальным условиям.
Случай, когда λ1 и λ2 почти равны друг другу, и случай, когда они в точности равны, физически близки друг другу. Замечу, что этот случай важен в теории измерительных приборов. Часто требуется, чтобы измерительный прибор как можно быстрее приходил в положение равновесия. Оказывается, это требование выполняется как раз тогда, когда характеристическое уравнение имеет равные корни.»
В самом деле, физически ситуацию λ1 и λ2 от ситуации λ1 ~= λ2 мы отличить не можем из-за конечной точности измерения любых величин и, в частности, коэффициентов уравнения (1) (в какой-то момент δ станет неотличимым от w0, не будучи равным ему в точности), в то время как решения (2) и (4) уравнения (1), отвечающие этим различным ситуациям, различаются весьма существенно. Перепишем решение (4) в виде, схожем с видом решения (2):
x(t) = e-δt(A + Bt). (5)
Таким образом видно, что асимптотики решений (2) и (5) существенно различны: в первом случае затухающая экспонента, умноженная на осциллирующие (и, стало быть, ограниченные) синус и косинус, а во втором – такая же экспонента (δ уже неотличимо), умноженная на растущую линейную функцию, и никаких осцилляций. Получается как бы парадокс: физически неразличимые ситуации можно различить…








