Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Сокровищница
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  15:10[Войти] | [Зарегистрироваться]

Модуль для расчета числовых и логических формул

Юрий Писарев
дата публикации 25-01-2002 11:38

Модуль для расчета числовых и логических формул

Модуль предназначен для расчета любых математических или логических выражений. В него уже включен набор стандартных математических и логических функций, но можно создавать свои функции любых типов. Можно также создавать свои типы данных. Логика работы с модулем такова, что сначала создается формула, которая затем преобразуется в цифровой вид, т.н. сценарий, по которому будет производиться расчет.
Это нужно для того, чтобы оптимизировать расчет формулы, при этом достигается огромный выигрыш в скорости. Для примера: у меня на компьютере (Athlon 1800XP, 512 MB DDR) расчет средней формулы 10000000 раз происходит за 1 - 1,5 секунды. Это при оптимизированном расчете. А при обычном (мои самые первые неудачные варианты) расчет той же формулы 10000 раз занимал около полминуты. Под словом "средней" я имею ввиду не очень длинную формулу, которая не перегруженна большим количеством функций. Но на саму формулу не накладывается вообще никаких ограничений, она может иметь любое количество операндов, она также может иметь любое количество вложенных выражений.
Вложеннное выражение - это с технической точки зрения отдельная формула, которая находится внутри другой формулы и может иметь еще любое количество вложенных формул. А с точки зрения пользователя это просто выражение, заключенное в скобки и обладающее приоритетом в вычислении.

Структура сценария

Как уже было сказано, формула переводится в цифровой вид - сценарий. Существуют два вида сценариев - математический и логический. Математический сценарий может содержать внутри себя только математические сценарии, в то время как логический сценарий может содержать внутри себя как логические, так и математическии сценарии. Они по своей структуре очень близки друг к другу. У каждого сценария есть заголовок, который содерит некоторую начальную информацию:
Заголовок сценария
Сценарий начинается с результата. Это нужно для расчета вложенных сценариев. Тип помещаемого туда результата - тип Double.
Кстати, в логическом сценарии результат занимает 4 байта и содержит логическое выражение. Это, конечно, очень нерационально тратить целых 4 байта на хранение логической переменной, но так проще. А основное внимание при создании этого модуля я уделял на достижении максимальной скорости вычисления формулы. Работа внутри модуля производится с вещественными числами, что, например, позволяет без проблем использовать тригонометрические функции. При расчете сценария сначала происходит расчет всех вложенных сценариев, в которых, в свою очередь, также происходит расчет вложенных сценариев и так далее. После расчета вложенного сценария в его начало записывается результат. Адреса вложенных сценариев задаются в байтах относительно начала содержащего их сценария. Сама формула при переводе в сценарий делится на части (единицы), которые определяются наличием положительго или отрицательного знака (в случае с логическим сценарием деление на единицы происходит несколько иначе, формула разбивается по логическим операндам xor, or, and).
Единицы содержат в себе 3 составляющие: функции, числа и вложенные сценарии. Это также справедливо и для логического сценария. Функции классифицируются между собой. Они отличаются тем, что некоторые требуют до себя параметр (например факториал: "10!"), некоторые требуют после себя параметр (например косинус: "cos 0"), некоторые требуют и до и после себя параметры (например умножение: "2 * 2"), некоторые не требуют вообще никаких параметров (например число пи: "pi"). У них также есть общее свойство - они все возвращают какой-то результат. Это также касается и логических функций. Каждая единица начинается с заголовка:
Заголовок единицы
Заголовки логических и математических единиц идентичны, и те и другие имеют знак и тип. Если знак отрицательный ("not" в логических или "-" в математических единицах), то значение единицы инвертируется. В логических выражениях допускается использование операнда "not" любое количество раз. Тип единицы соответствует одному из типов в Delphi. Более подробно - чуть ниже. Вычисления будут производится соотвественно описанному типу. В логических единицах тип тоже присутствует, так как хоть они и не могут содержать математические выражения, но могут содержать числа, тип которых можно уточнить. В общей части находятся функции, числа, вложенные сценарии. Чтобы из можно было отличать друг от друга, перед каждым составляющим общей части единицы ставится идентификатор. Еще одно отличие логического сценария от математического состоит в том, что в логическом сценарии перед вложенным сценарием ставится идентификатор, уточняющий его тип. Это необходимо, так как логическая формула может содержать в себе как логические формулы, так и математические и при вычисленни их нужно отличать друг от друга.

Структура формул

Любая формула должна быть составлена с учетов некоторых правил, а также при ее составления необходимо знать из чего она может состоять. Функции, типы, а также другие составляющие приведены ниже:
  • single: тип, означает вещественное 32 битное число
  • double: тип, означает вещественное 64 битное число
  • int64: тип, означает целое знаковое 64 битное число
  • integer: тип, означает целое знаковое 32 битное число
  • longword: тип, означает целое беззнаковое 32 битное число
  • smallint: тип, означает целое знаковое 16 битное число
  • word: тип, означает целое беззнаковое 16 битное число
  • shortint: тип, означает целое знаковое 8 битное число
  • byte: тип, означает целое беззнаковое 8 битное число
  • bool: зарезервированное слово, обозначает логическое выражение.
  • and : операнд, используется для связывания двух логических выражений. Аналогично логическому and в Delphi.
  • or : операнд, используется для связывания двух логических выражений. Аналогично логическому or в Delphi.
  • xor : операнд, используется для связывания двух логических выражений. Аналогично логическому xor в Delphi.
  • not : операнд, меняет логическое значение на противоположное.
  • > функция, если первое математическое выражение больше второго, то возвращает истину, в противном случае возвращает ложь.
  • <: функция, если первое математическое выражение меньше второго, то возвращает истину, в противном случае возвращает ложь.
  • <>: функция, если первое математическое выражение не равно второму, то возвращает истину, в противном случае возвращает ложь.
  • =>: функция, если первое математическое выражение больше или равно второму, то возвращает истину, в противном случае возвращает ложь.
  • <=: функция, если первое математическое выражение меньше или равно второму, то возвращает истину, в противном случае возвращает ложь.
  • =: функция, если первое математическое выражение равно второму, то возвращает истину, в противном случае возвращает ложь.
  • true: функция. Возвращает истину. Это величина может принимать значение 1
  • false: функция. Возвращает ложь. Это величина может принимать значение 0
  • +: операнд, сложение
  • -: операнд, вычитание
  • *: функция, вычитание
  • /: функция, деление
  • sqrt: функция, возвращает квадратный корень числа
  • div: функция, возвращает целочисленное деление
  • mod: функция, возвращает остаток от деления
  • int: функция, возвращает целая часть числа
  • frac: функция, возвращает дробная часть числа
  • random: функция, возвращает произвольное число в пределах от 0 до 1
  • trunc: функция, возвращает целую часть числа
  • round: функция, округляет число
  • arcsec: функция, возвращает арксеканс числа
  • sec: функция, возвращает секанс числа
  • arccsc: функция, возвращает арккосеканс числа
  • csc: функция, возвращает косеканс числа
  • arcsin: функция, возвращает арксинус числа
  • sin: функция, возвращает синус числа
  • arccos: функция, возвращает арккосинус числа
  • cos: функция, возвращает косинус числа
  • arctan: функция, возвращает арктангенс числа
  • tan: функция, возвращает тангенс числа
  • abs: функция, возвращает абсолютную величину числа
  • ln: функция, возвращает натуральный логарифм числа
  • lg: функция, возвращает десятичный логарифм числа
  • log: функция, возвращает логарифм двух числа
  • pi: функция, возвращает число Пи
  • !: функция, возвращает факториал числа
  • ^: функция, возвращает степень числа. Степень не может быть дробной.
В любом случае логическая формула должна начинаться с зарезервированного слова "bool". Оно означает, что текущее выражение является логическим. В формуле можно использовать любое количество вложенных формул, которые представляют собой содержимое пары скобок, а содержимое каждой из этих пар скобок может быть как логическим выражением, так и математическим. Соответственно внутри скобок при обозначения логического выражения нужно также ставить зарезервированное слово "bool". Если его нет, то считается, что выражение является математическим.
Например:
 "bool (2 log 4) = (4 sqrt 2) or (bool (2 * 2) = 4)". 
В формуле каждая функция должна быть заключена в круглые скобки. Что является функцией, а что нет можно узнать вышеприведенного списка. Как я уже сказал, логические выражения в некоторых случаях могут возвращать числовые значения. Это работает только в том случае, если выражение заключено в скобки, например: "bool (bool true) = 1". Логические выражения возвращающие истину принимают значение 1, а содержащие ложь - 0.

Функции для работы с модулем

Все операции с формулами обрабатывает объект класса TDataEditor. У этого класса есть ряд методов для регистрации новых функций, типов, превода формул в сценарии и выполнения этих сценариев. Объект класса TDataEditor может хранить в себе одну формулу и один сценарий. Если Вы не собираетесь создавать новые функции, то можно просмотреть только описание функций преобразования строки в сценарий, думаю этого будет вполне достаточно, в противном случае я советую особое внимание уделить описанию методов регистрации новых функций и событию OnIntFunction.
  • function RegisterIntFunction(const FunctionName: string; RequireValue1, RequireValue2: Boolean): Integer; Регистрирует математическую функцию с именем FunctionName. Параметры RequireValue1 и RequireValue2 означают, требуются ли этой функции параметры стоящие перед ней или после нее соответственно. Возвращает идентификатор зарегистрированной функции. Если Вы зарегистрировали новую функцию, то на Вас ложится ответственность за проведение расчета этой функции. Расчет функции придется делать каждый раз при расчете сценария. Этот расчет будет происходить в пределах события OnIntFunction (он описывается ниже), в котором Вам будет передан индекс Вашей новой функции и параметры, если необходимость их наличия была указана в функции, которую мы сейчас обсуждаем (RequireValue1 и RequireValue2).
  • function UnRegisterIntFunction; Удаляет ранее зарегистрированную функцию по ее имени или ее идентификатору.
  • function RegisterBoolFunction(const FunctionName: string; RequireValue1, RequireValue2: Boolean): Integer; По смыслу тоже что и функция RegisterIntFunction.
  • function RegisterType(const TypeName: string; TypeID: Integer): Integer; Регистрирует тип с именем TypeName и присваивает ему идентификатор TypeID. Возвращает идентификатор зарегистрированного типа.
  • function UnRegisterType: Удаляет ранее зарегистрированный тип по имени или по его идентификатору
  • function StringToIntScript(const S: string; out Script: TScript; OpenedBracket: Char = '('; ClosedBracket: Char = ')'); Переводит строку S в сценарий Script. В параметрах OpenedBracket и ClosedBracket содержится символы начала и конца вложенной формулы
  • function StringToBoolScript(const S: string; out Script: TScript; OpenedBracket: Char = '('; ClosedBracket: Char = ')'); По смыслу тоже, что и функция StringToIntScript.
  • function ExecuteIntScript(P: Pointer): Double; Выполняет математический сценарий с адресом P и возвращает результат сценария
  • function ExecuteInt: Double; Выполняет математический сценарий, содержащийся в свойстве Script.
  • function ExecuteBoolScript(P: Pointer): Boolean: По смыслу то же, что и функция ExecuteIntScript.
  • function ExecuteBool: Boolean; По смыслу то же, что и функция ExecuteInt.
  • property Script: TScript; содержит сценарий
  • property Formula: string; содержит формулу
  • property Accuracy: TRoundToRange; Точность вычисления операций. Проще говоря, это второй параметр функции RoundTo - см. справку Delphi
  • property OnIntFunction: TIntFunctionEvent; TIntFunctionEvent = function(FunctionID: Integer; TypeID: Integer; var Value1: Double; Value2, Value3: Double): Boolean; Эта событие должно обрабатываться в том случае, если зарегистрированы новые функции. Параметр FunctionID возвращает идентификатор функции, которая должна быть вычислена. Параметр TypeID указывает идентификатор типа (который, например, мог быть создан функцией RegisterType). Это событие будет возникать каждый раз при вычислении каждой функции в сценарии, даже если это стандартная функция. Если Вы хотите передать управление подпрограмме, которая обрабатывает стандартные функции в сценарии, то результат функции должен быть истина. Таким образом можно перекрыть стандартные функции, т.е. изменить их метематику. Результат выполнения функции нужно поместить в параметр Value1. Параметры Value2 и Value3 возвращают параметры функции, но только в том случае, если они требуются (это указывается при создании новой функции, см. RegisterIntFunction и RegisterBoolFunction).
  • property OnBoolFunction: TBoolFunctionEvent; TBoolFunctionEvent = function(FunctionID: Integer; TypeID: Integer; var Value1: Boolean; Value2, Value3: Double): Boolean of object; По смыслу то же, что и событие OnIntFunction.

Программа для тестирования скорости расчета

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

Практический пример

Также я сделал небольшой пример того, как можно использовать этот модуль на практике. Программа Drawing рисует картику по уже готовым математическим формулам, находящимся в файле Drawing.dat. Вы можете его открыть при помощи блокнота. Вы также можете вписать туда свои формулы. При создании этой программы мне пришлось зарегистрировать несколько новых функций. Первая из них (x) используется для расчета координаты X, вторая (y) используется для получения координыты Y, функция index возвращает индекс текущего пикселя. Напоминаю, что начало координат находится в верхнем левом углу. Кстати, если формула написана неправильно, то она будет проигнорирована, а для расчета картинки требуется как минимум три правильных формулы, по одной для кажного цвета: красного, зеленого и синего. Программа каждый раз при расчете картики выбирает случайные формулы. При стандартном разрешении 1024 : 768 получаем, что для вычисления одного составляющего всех пикселей картинки требуется произвести 1024 * 768 = 786432 операций, а всего 786432 * 3 = 2359296 операций. На моем компьютере расчет всей картинки занимает 1 - 3 секунды. На старых компьютерах расчет будет занимать намного больше времени, например, я был неприятно удивлен, когда запустил эту же программу на компьютере Celeron-400, там картинка расчитывалась 5 - 10 секунд.

Исходники

Скачать модуль DataEditor.zip
Скачать программу тестирования скорости SpeedTest.zip
Скачать практический пример Drawing.zip

Юрий Писарев




Смотрите также материалы по темам:
[Разбор и вычисление выражений]

 Обсуждение материала [ 02-02-2022 01:56 ] 14 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования