Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование с нуля для инженера - выпуск 9


Информационный Канал Subscribe.Ru

Программирование с нуля для инженера - математические функции, генерация случайных чисел (выпуск 9)
  • Архив рассылки: ssdg.h15.ru/resources.php?internalpart=maillist
  • Автор рассылки: Седлярский Илья
  • Периодичность выхода: раз в две недели
  • Содержание
    Оператор выбора Case
    Оператор Case реализует выполнение одного из нескольких вложенных в него блоков в зависимости от значения выражения. Оператор имеет такую форму:
    Case <выражение> Of
      <совокупность значений 1>:
      Begin
        <блок операторов 1>;
      End;
      
      <совокупность значений 2>:
      Begin
        <блок операторов 2>;
      End;
      
      ...
      <совокупность значений N>:
      Begin
        <блок операторов N>;
      End;
      
      Else
      Begin
        <блок операторов, выполняющийся в том случае, если
        ни один из вышестоящих блоков не выполнился>
      End;
    End;
    Оператор вычисляет выражение, затем по очереди проверяет принадлежность полученного значения совокупностям, и в случае попадания значения в какую-либо совокупность выполняет блок, соответствующий этой совокупности. Принадлежность к последующим совокупностям проверяться не будет, и соответствующие им блоки выполняться не будут. Проверяемое выражение может быть целочисленного или логического типа, но не вещественного. Совокупность значений выражения записывается в виде объединения промежутков. Промежуток может состоять из одного значения. В наиболее частном случае совокупность состоит из одного значения.
    Пример:
    Var
      X: Integer;
    
    Begin
      X := 100;
      Case X Of
        0..20, 40..50, 124, 280:   ShowMessage('Случай 1');
        30..35, 61..100, 121, 144: ShowMessage('Случай 2');
      End;
    End;
    
    В этом примере совокупностями являются "0..20, 40..50, 124, 280" и "30..35, 61..100, 121, 144". У оператора Case может не быть ветки Else, как и у условного оператора If. Перед Else может стоять точка с запятой, в отличие от условного оператора. Начинается оператор с ключевого слова Case, а заканчивается ключевым словом End. Совокупности значений выражения в операторе выбора не должны пересекаться. В случае пересечения компилятор выдаст соответствующую ошибку.
    Пример перевода оценки из стобалльной системы в пятибалльную с использованием оператора Case:
    Procedure TMainForm.CalculateBtnClick(Sender: TObject);
    Var
      Points: Integer;
    
    Begin
      Points := StrToInt(PointsET.Text);
    
      Case Points Of
        0..49:
        Begin
          MarkLB.Caption := 'Оценка: 2';
        End;
        50..74:
        Begin
          MarkLB.Caption := 'Оценка: 3';
        End;
        75..89:
        Begin
          MarkLB.Caption := 'Оценка: 4';
        End;
        90..100:
        Begin
          MarkLB.Caption := 'Оценка: 5';
        End;
        Else
        Begin
          MarkLB.Caption := 'Оценка: ???';
          ShowMessage('Как вы умудрились?');
        End;
      End;
    
      // этот же оператор мог быть записан иначе
      Case Points Of
        0..49:   MarkLB.Caption := 'Оценка: 2';
        50..74:  MarkLB.Caption := 'Оценка: 3';
        75..89:  MarkLB.Caption := 'Оценка: 4';
        90..100: MarkLB.Caption := 'Оценка: 5';
        Else
        Begin
          MarkLB.Caption := 'Оценка: ???';
          ShowMessage('Как вы умудрились?');
        End;
      End;
    End;
    Этот пример находится в архиве примеров, папка Example1.
    Математические функции
    Математические функции очень важны при написании расчётных программ. Мы рассмотрим многие стандартные функции, а также создадим свой модуль математических функций, которые не реализованы в стандартных модулях Delphi, но могут быть полезны в работе.
    Рассмотрим сначала арифметические функции:
    • Function Abs(X) - функция возвращает модуль числа X, где X - число любого целого или вещественного типа. Тип возвращаемого значения равен типу переданного в функцию значению. Функция является встроенной в компилятор, но, судя по справке, объявлена в модуле System.
      Вы, наверное, заметили, что модуль System нигде не подключается к проекту. Это его исключительная особенность. Этот модуль всегда автоматически подключается к проекту, поэтому его не подключают вручную. Он содержит функции, без использования которых программа не способна работать. Некоторые функции, встроенные в компилятор, ссылаются в начало модуля System. То есть, они якобы объявлены там, а на самом деле являются встроенными в компилятор.

    • Function Ceil(X: Extended): Integer - уже известная вам функция округления вещественного числа в верхнюю сторону. Находится в модуле Math, который по умолчанию не подключается к проекту.

    • Function Exp(X: Real): Real - возвращает число eX, где e - основание натурального логарифма. Функция встроена в компилятор.

    • Function Floor(X: Extended): Integer - округляет число в меньшую сторону. Находится в модуле Math.

    • Function Frac(X: Extended): Extended - возвращает дробную часть числа. Встроена в компилятор.

    • Procedure Frexp(X: Extended; Var Mantissa: Extended; Var Exponent: Integer) - разбивает число X на мантиссу и экспоненту. Переменные Mantissa и Exponent передаются в процедуру по ссылке. Типы передачи параметров в подпрограммы будут рассмотрены позже. Сейчас просто запомните, что в качестве второго и третьего параметра в процедуру должны быть переданы переменные, а не конкретные числовые значения. После выполнения процедуры в этих переменных окажутся мантисса и экспонента числа X. Процедура находится в модуле Math.

    • Function Int(X: Extended): Extended - возвращает целую часть числа в вещественном представлении. Функция встроена в компилятор.

    • Function IntPower(Base: Extended; Exponent: Integer): Extended - возвращает число BaseExponent. Степень, в которую возводится число - целая. Находится в модуле Math.

    • Function Ldexp(X: Extended; P: Integer): Extended - возвращает число X * 2P. Функция модуля Math.

    • Function Ln(X: Real): Real - возвращает натуральный логарифм числа X. Встроена в компилятор.

    • Function Log10(X: Extended): Extended - возвращает десятичный логарифм числа. Находится в модуле Math.

    • Function Log2(X: Extended): Extended - возвращает двоичный логарифм числа. Находится в модуле Math.

    • Function LogN(N, X: Extended): Extended - возвращает логарифм по основанию N числа X (LogNX). Функция находится в модуле Math.

    • Function Max(A,B: Integer): Integer; overload;
      Function Max(A,B: Int64): Int64; overload;
      Function Max(A,B: Single): Single; overload;
      Function Max(A,B: Double): Double; overload;
      Function Max(A,B: Extended): Extended; overload;

      Возвращает максимальное из двух переданных значений. Функция имеет пять перегруженных реализаций, отличающихся по типу принимаемых параметров и по типу возвращаемого параметра. При вызове этой функции не нужно задумываться о том, какая реализация вызывается. Компилятор сам решит, исходя из типов передаваемых параметров. Функция модуля Math.

    • Function Min(A,B) - возвращает минимальное из двух значений. Остальное - аналогично функции Max. Функция модуля Math.

    • Function Pi: Extended - возвращает число Пи, равное в данном случае 3.1415926535897932385. Функция встроена в компилятор.

    • Function Poly(X: Extended; Const Coefficients: Array Of Double): Extended - функция вычисляет значение однородного многочлена в точке X. Однородный многочлен имеет вид Anxn + An-1xn-1 + ... + A0, где x - переменная, а A0..An - коэффициенты при соответствующих степенях x. Массив Coefficients содержит коэффициенты в порядке увеличения степени x. Функция Poly может пригодиться, например, для подбора корней однородного уравнения. Массивы будут рассмотрены нами позже. Функция находится в модуле Math.

    • Function Power(Base, Exponent: Extended): Extended - возвращает число BaseExponent. В отличие от функции IntPower степень, в которую возводится число, дробная. Находится в модуле Math.

    • Function Round(X: Extended): Int64 - округляет вещественное число до ближайшего целого. Если дробная часть числа равна 0.5, число округлится до ближайшего чётного, а не в верхнюю сторону. Функция встроена в компилятор.

    • Function Sqr(X: Extended): Extended - возвращает квадрат числа X. Встроена в компилятор.

    • Function Sqrt(X: Extended): Extended - возвращает корень числа X. Встроена в компилятор.

    • Function Trunc(X: Extended): Int64 - возвращает целую часть вещественного числа X. Если число положительно, результат действия функции эквивалентен округлению в меньшую сторону. Для округления чисел любого знака в меньшую сторону следует использовать функцию Floor. Trunc встроена в компилятор.
    Тригонометрические функции:
    • Function ArcCos(X: Extended): Extended - возвращает арккосинус числа X. Для X должно выполняться условие -1 <= X <= 1. Находится в модуле Math.

    • Function ArcCosh(X: Extended): Extended - возвращает обратный гиперболический косинус числа X. Для X должно выполняться условие X >= 1. Находится в модуле Math.

    • Function ArcSin(X: Extended): Extended - возвращает арксинус числа. Для X должно выполняться условие -1 <= X <= 1. Функция модуля Math.

    • Function ArcSinh(X: Extended): Extended - возвращает обратный гиперболический синус числа X. Функция модуля Math.

    • Function ArcTan(X: Extended): Extended - возвращает арктангенс числа X. Находится в модуле Math.

    • Function ArcTanh(X: Extended): Extended - возвращает обратный гиперболический тангенс числа X. Для X должно выполняться условие -1 <= X <= 1. Находится в модуле Math.

    • Function Cos(X: Extended): Extended - возвращает косинус числа X. Встроена в компилятор.

    • Function Cosh(X: Extended): Extended - возвращает гиперболический косинус числа X. Находится в модуле Math.

    • Function Cotan(X: Extended): Extended - возвращает котангенс числа X. Для X должно выполняться условие X <> 0. Функция находится в модуле Math.

    • Function Hypot(X, Y: Extended): Extended - возвращает гипотенузу прямоугольного треугольника со сторонами X и Y. Находится в модуле Math.

    • Function Sin(X: Extended): Extended - возвращает синус числа X. Функция встроена в компилятор.

    • Function Sinh(X: Extended): Extended - возвращает гиперболический синус числа X. Функция модуля Math.

    • Function Tan(X: Extended): Extended - возвращает тангенс числа. X не должно быть равно Pi/2. Объявлена в модуле Math.

    • Function Tanh(X: Extended): Extended - возвращает гиперболический тангенс числа. Функция объявлена в модуле Math.
    Тригонометрические функции принимают аргументы в радианах, а обратные тригонометрические функции возвращают значения также в радианах. Для преобразования единиц измерения служат следующие функции, находящиеся в модуле Math.
    • Function CycleToRad(Cycles: Extended): Extended - принимает число периодов угла, возвращает радианную меру угла.

    • Function DegToRad(Degrees: Extended): Extended - возвращает радианную меру угла, заданного в градусах.

    • Function GradToRad(Grads: Extended): Extended - возвращает радианную меру угла, заданного в градах.

    • Function RadToCycle(Radians: Extended): Extended - возвращает число периодов угла, заданного в радианах.

    • Function RadToDeg(Radians: Extended): Extended - возвращает градусную меру угла, заданного в радианах.

    • Function RadToGrad(Radians: Extended): Extended - возвращает градовую меру угла, заданного в радианах.
    Рассмотрим некоторые статистические функции, которые также находятся в модуле Math:
    • Function MaxIntValue(const Data: array of Integer): Integer - возвращает максимальное значение из массива целых чисел.

    • Function MaxValue(const Data: array of Double): Double - возвращает максимальное значение из массива вещественных чисел.

    • Function Mean(const Data: array of Double): Extended - возвращает среднее арифметическое всех чисел массива.

    • Function MinIntValue(const Data: array of Integer): Integer - возвращает минимальное значение из массива целых чисел.

    • Function MinValue(const Data: array of Double): Double - возвращает минимальное значение из массива вещественных чисел.

    • Function Sum(const Data: array of Double): Extended - возвращает сумму элементов массива вещественных чисел.

    • Function SumInt(const Data: array of Integer): Integer - возвращает сумму элементов массива целых чисел.

    • Function SumOfSquares(const Data: array of Double): Extended - возвращает сумму квадратов чисел массива.

    • Procedure SumsAndSquares(const Data: array of Double; var Sum, SumOfSquares: Extended) - в переменные Sum и SumOfSquares, переданные по ссылкам, возвращает сумму и сумму квадратов чисел массива.

    Отмечу ещё пару полезных процедур:
    • Procedure Inc(var X [ ; N: Longint ] ) - процедура увеличивает значение переданной по ссылке переменной на N. Запись в квадратных скобках означает, что второй параметр процедуры необязателен. Если не передавать второй параметр, значение переменной увеличится на единицу. Например, запись X := X + 10; можно переписать так: Inc(X, 10);. Оператор Inc(X); увеличивает X на 1. Оба передаваемых в процедуру Inc параметра должны быть целого типа. Процедура встроена в компилятор.

    • Procedure Dec(var X [ ; N: Longint]) - процедура, противоположная процедуре Inc. Она уменьшает значение переданной переменной. X := X - 10; переписывается как Dec(X, 10);.

    Модуль математических функций
    На практике часто требуется округлять вещественные числа не до целых значений, а до определённого знака. Первой функцией нашего модуля математических функций будет функция, округляющая число до заданного количества знаков после запятой.
    Function FloatRound(X: Extended; Precision: Byte): Extended;
    Var
      Multiplier: Extended;   // коэффициент умножения числа
      FracX:      Extended;   // дробная часть числа
    
    Begin
      // Сдвиг запятой на "Precision" разрядов вправо
      Multiplier := IntPower(10, Precision);
      X := X * Multiplier;
    
      // Целая часть числа меньше или равна числу, если число положительно
      // и больше числа, если число отрицательно
      // Нужно рассмотреть два случая - когда число положительно, и когда
      // оно отрицательно. Условия округления у этих случаев различаются  
      FracX := Frac(X);
      X := Int(X);
      If FracX >= 0 Then
      Begin
        If FracX >= 0.5 Then
          X := X + 1;
      End
      Else
      Begin
        If FracX < -0.5 Then
          X := X - 1;
      End;
    
      // Обратно сдвигаем запятую - на "Precision" разрядов влево
      // Получаем округлённое число
      FloatRound := X / Multiplier;
    End;
    Алгоритм округления числа будет такой:
    1. Получаем коэффициент, на который нужно умножить число, чтобы разряд, который после округления станет последним значимым, оказался слева от запятой.
      Например, нужно округлить -10.455 до 2 знаков после запятой. Коэффициент умножения будет равен 102, поскольку округляем число до 2 знаков. В общем случае коэффициент равен десяти в степени количества разрядов, до которого округляется число.
    2. Умножаем число на полученный коэффициент - сдвигаем запятую вправо. Получаем -1045.5.
    3. Далее нужно округлить получившееся число в вещественном представлении (то есть не переходя к целочисленному представлению, чтобы не потерять точность и не вызвать переполнения). Для этого сначала получаем дробную часть числа с помощью функции Frac, затем округляем число до целого (в вещественном представлении) с помощью функции Int. То есть на этом этапе мы получили целую и дробную части числа. Получаем целую часть -1045 и дробную часть -0.5.
    4. Целая часть числа меньше или равна числу, если число положительно, и больше числа, если число отрицательно. Учитывая этот момент, производим округление. Нужно рассмотреть два случая - когда число положительно и когда число отрицательно.
      Если число положительно, целая часть является его округлением в меньшую сторону. В этом случае сравниваем дробную часть с 0.5. Если она больше или равна 0.5, прибавляем к числу 1 - округляем в большую сторону.
      Если число отрицательно, целая часть является его округлением в большую сторону, а дробная часть отрицательна (или равна нулю). Сравниваем дробную часть с -0.5. Если она строго меньше -0.5, число нужно округлить в меньшую сторону, то есть вычесть из рассчитанной целой части единицу. Если дробная часть равна -0.5, округляем, по правилу, в большую сторону. Но целая часть и есть округление в большую сторону, поэтому ничего делать не надо. По этой причине в выражении "FracX < -0.5" используется знак "меньше";
      В нашем случае дробная часть равна -0.5, и число будет округлено в большую сторону.
    5. Полученное число уже округлено, осталось обратно сдвинуть запятую делением числа на коэффициент, полученный в первом пункте алгоритма. Получаем -10.45.
    Надеюсь, я никого не запутал этим алгоритмом.

    Пример программы, которая использует эту функцию округления (пример и модуль MathFunctions.pas находятся в папке Example2 архива примеров):
    Program FloatRoundDemo;
    {$APPTYPE CONSOLE}
    
    Uses
      SysUtils, MathFunctions;
    
    Var
      X:    Extended;
      Prec: Byte;
      S:    String;
    
    Begin
      Write('Enter X > ');
      ReadLn(X);
      Write('Enter precision > ');
      ReadLn(Prec);
    
      X := FloatRound(X, Prec);
      S := FloatToStrF(X, ffFixed, 10, Prec);
      WriteLn(S);
      ReadLn;
    End.
    Целиком посвящать выпуски математическим и расчётным функциям мы не будем, но если у вас есть свои функции, которые могут быть полезны в работе, пусть даже в какой-нибудь специфической области - присылайте мне их с описанием. Они будут опубликованы в выпусках и добавлены в модуль MathFunctions.pas. Этот модуль будет доступен на сайте в архиве рассылок.
    Генерация случайных чисел
    Для генерации случайных чисел в компилятор Delphi встроен генератор случайных чисел. Рассмотрим процедуры и функции, использующиеся для генерации случайных чисел.
    • Procedure Randomize - инициализирует генератор случайных чисел. Если не инициализировать генератор, программа при каждом запуске будет выдавать одну и ту же последовательность случайных чисел. Перед использованием функций, возвращающих случайные числа, обязательно надо вызвать процедуру Randomize. Обычно это делают 1 раз, при запуске программы или перед выполнением расчётов. Процедура встроена в компилятор.

    • Function Random [ ( Range: Integer) ] - функция возвращает случайное число. Она может вызываться с одним параметром или без параметров. В случае вызова Random с одним параметром целого типа (Range) функция возвращает целое число в промежутке от 0 до Range - 1. При вызове без параметров Random возвращает вещественное число X, для которого выполняется условие 0 <= X < 1.

    • Function RandG(Mean, StdDev: Extended): Extended - возвращает случайное число с гауссовым распределением около значения Mean. Второй параметр функции - стандартное отклонение. Функция может быть полезна для имитирования данных с ошибками выборки и с ожидаемым отклонением от заданного значения Mean. Находится в модуле Math.
    Рассмотрим небольшой пример. Функция Random при вызове с одним параметром возвращает целое число, левая граница которого равна нулю. Реализуем функцию, которая возвращает целое число в заданном ей промежутке.
    Program IntRandomDemo;
    {$APPTYPE CONSOLE}
    
    // Возвращает случайное целое число в промежутке от
    // LowBorder до UpBorder
    Function IntRandom(LowBorder, UpBorder: Integer): Integer;
    Begin
      IntRandom := LowBorder + Random(UpBorder - LowBorder + 1);
    End;
    
    Var
      X: Integer;
    
    Begin
      Randomize;
      X := IntRandom(-20, 50);
      WriteLn(X);
      ReadLn;
    End.
    Чтобы получить число в промежутке от LowBorder до UpBorder, необходимо взять нижнюю границу LowBorder и прибавить к ней случайное число, лежащее в промежутке от нуля до длины всего диапазона. Длина диапазона равна UpBorder - LowBorder. Чтобы Random выдала случайное число от 0 до UpBorder - LowBorder, нужно вызвать её с параметром UpBorder - LowBorder + 1.
    Этот пример находится в папке Example3 архива примеров.
    Заключение
    Ссылка на примеры. В заключение предлагаю вам одно маленькое задание. Есть две переменных целого типа, например, A, B: Integer. Чтобы поменять местами значения, которые содержат переменные нужно ввести дополнительную переменную C и записать так: C := A; A := B; B := C. Это несложно. Задача состоит в том, что нужно поменять местами значения, не используя третью переменную.
    Отдельно от примеров выкладываю свой вариант решения задания с квадратным уравнением, но только исполняемый файл, без исходного кода. Следующий выпуск будет посвящён разбору этого задания. Внимание будет уделяться не столько расчёту корней, сколько описанию свойств, методов, событий использованных компонентов. До следующего выпуска присылайте свои решения.
    Ссылка на программу.

    Subscribe.Ru
    Поддержка подписчиков
    Другие рассылки этой тематики
    Другие рассылки этого автора
    Подписан адрес:
    Код этой рассылки: comp.soft.others.prog2eng
    Архив рассылки
    Отписаться
    Вспомнить пароль

    В избранное