Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Стек операций 
:(
    Опции темы
Mal Hack
Дата 23.8.2007, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Суть задачи. Идет разбор математического выражения, состоящего из математических функций и арифметических дейтсвий.
Предположим, что мы имеем стек операций, который надо вычислить. К примеру:
Функция: f(x)=(sin(x+2)-exp(2*x))*3
При разборе получаем следующий стек вида (даю не точный идпри работе програмы, а лишь схему (все данные стека - строки, работа с которыми ведется через регулярныет выражения и последующим рекурсивным вычислением)):
0 - х+2
1 - sin([0])
2 - 2*x
3 - exp([2])
4 - ([1]+[3])
5 - [4]*3

Так вот, вопрос в чем. Нашу ф(х) надо вычислить многократно (к примеру от 0 до 1 с шагом 0.01). Сделать это - не проблема, но, мне не хочется при каждом новом вычислении значении функции, производить рекурсивный разбор (точнее сбор) кусочков функции и их вычисление.

Можно ли как-то сделать стек вычислений, т.е. один раз показать программе, что sin - это sin из math.h и потом к нему и обращаться, просто передавая новое значение аргумента?

Работаю на C++, Qt4, Linux.
PM ICQ   Вверх
Lazin
Дата 23.8.2007, 14:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Я делал на первом курсе лабу, только там были ф-ии вложенные, небыло операторов - что-то вроде sin(tg(sqrt(x))), и по этой беде строился график. Для этого создавался список - односвязный - примерно такого вида:

Код

struct fnNode
{
double (*f_x)(double x);
fnNode* next;
};


затем, эта хрень вызывалась рекурсивно

Код

fnNode* list;
...
double call(fnNode* node, double arg)
{
    if (node->next != 0) return (*node->f_x)( call(node->next, arg) );
    return (*node->f_x)( arg );
}


Это сообщение отредактировал(а) Lazin - 23.8.2007, 14:15
PM MAIL Skype GTalk   Вверх
Mal Hack
Дата 23.8.2007, 14:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Lazin, это-то все мне понятно. Сам процесс вычисления - проблем не вызывает, тут вопрос в оптимальном решении при большом кол-ве вызовов этих самых вычислений.
PM ICQ   Вверх
Lazin
Дата 23.8.2007, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Что-бы разобрать сложное выражение наверное нужно использовать деревья а не списки

Добавлено через 5 минут и 58 секунд
Так-или иначе всё равно придется создавать структуру данных отражающую процесс вычислений, хотя возможно получится использовать итерацию для обхода, а это немного быстрее.
PM MAIL Skype GTalk   Вверх
JackYF
Дата 23.8.2007, 14:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 18
Всего: 162



Mal Hack, я тут подумал, подумал... может, помогут лямбда-функции? то есть при первом разборе при рекурсивном проходе мы "накапливаем" эту самую функцию как boost::function1<double, double>, в конце разбора сохраняем ее как

Код

...
boost::function1<double, double> end_func = func(); // чего сотворили...


А потом надо будет только один раз передать аргумент, что тебе и нужно. Это идея, сам подобным извратом я не занимался.


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
Mal Hack
Дата 23.8.2007, 14:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Lazin, я же вроде написал, что проблема НЕ В РАЗБОРЕ И НЕ В АЛГОРИТМЕ !!!!

JackYF, а можно что-ть более развернуто на тему лямбда функции почитать? Не имел с этим дело и мало представляю себе суть.
PM ICQ   Вверх
Sartorius
Дата 23.8.2007, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: 8
Всего: 37



 Есть такое предложение:
 Выражение парситься в ПОЛИЗ. и потом преобразуется во внутреннее представление.
 Ну например:
 sin(exp(x+2)) : ->  x 2 + exp sin 
 во внутреннем представление: 10 байт на число (double) и флажок вызова функции. если есть флажок - то содержимое стека трактуется как адрес этой функции

в стеке 

адрес функции возвращающей в стек x
2.0
адрес функции производящей сложение
адрес функции считающей экспоненту
адрес функции считающей синус

получаем что то типа виртуальной машины небольшой)))))
... дальше дело техники 


PS если используешь .NET - советую динамические сборки опробывать. Генерируешь в IL код метода. JITer его один раз скомпилирует. Дальше летать будет)))

ЗЗЫ
 вот ссылка на компилятор выражения в байт-код (правда на дельфи и какой то уцербный но может пригодиться) 


Это сообщение отредактировал(а) Sartorius - 23.8.2007, 15:17
PM MAIL ICQ   Вверх
Lazin
Дата 23.8.2007, 15:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



2JackYF Разве шаблоны не на этапе компиляции обрабатываются, boost::lamda не исключение.

PM MAIL Skype GTalk   Вверх
Mal Hack
Дата 23.8.2007, 15:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Sartorius, как я понимаю, все равно, придется парсить функцию при каждом новом значении аргумента.
А мне надо, отпарсить один раз, сделать что-то вроде макроса (запомнить последовательность действий), а затем вызывать эту последовательность действий для нового значения X.

.NET я не использую.
PM ICQ   Вверх
Sartorius
Дата 23.8.2007, 15:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: 8
Всего: 37



Цитата

Sartorius, как я понимаю, все равно, придется парсить функцию при каждом новом 

Mal Hack, нет. Функция парситься один раз. А вместо х - в стек записывается адрес функции , возвращающей x. или вообще можно ввести три типа элементов стека - константы, функции и переменные. 
 С константами все понятно. 
 Если следующим из стека достается адрес функции - то она вызвается  и ей передается адрес вершины стека, так , что б она могла брать аргументы и класть туда возвращаемое значение. 
 Если достается переменная - то в стек возвращается ее значение.

PS чего - то подзабыл как с польской записью работать поэтому путанно объяснил наверно. 
 Стоит различать стек - хранилище данных. И цепочку команд.
 Сейчас все покорректней сформулирую:
 sin(15 * x + exp(5))  - > в польской записи   15 x * 5 exp + sin  - эту цепочку переведем во внутреннее представление

 номер инструкции   тип (два бита или байт)                  значение
[0]   константа                                                15
[1]   переменная                                              адрес х
[2]   функция                                                   умножение
[3]   константа                                                 5
[4]   функция                                                    exp
[5]   функция                                                    сложение
[6]   функция                                                    sin


теперь когда эта небольшая программа заработает на нашей "виртуальной стековой машине" произойдет следующее
[0] встретили константу в цепочке  - кладем значение на стек
[1] встретили переменную - читаем ее адрес и кладем на стек
[2] встретили функцию - читаем ее адрес вызываем и передаем ей стек
...
уф... вроде теперь нормально объяснил





Это сообщение отредактировал(а) Sartorius - 23.8.2007, 15:44
PM MAIL ICQ   Вверх
powerfox
Дата 23.8.2007, 15:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I wanna fork()
****


Профиль
Группа: Комодератор
Сообщений: 3990
Регистрация: 1.10.2005
Где: Санкт-Петербург

Репутация: 2
Всего: 97



Гм...

При парсинге можно собирать указатели на необходимые функции и потом пользоваться ими.


--------------------
user posted image
PM WWW   Вверх
MAKCim
Дата 23.8.2007, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



1. Переводим выражение в обратную польскую запись
например (2 * x + 3) * 6 = 2 x * 3 + 6 *
2. Составляем связный список узлов элементарных операций
а) Каждый узел имеет структуру
Код

union args {
        char __array[sizeof(double) * 2];
        double operand;
        union {
            void * address;
            void * argument;
            double constant;
        } loperand, roperand;
};

struct node {
    double (*callback)(union args);
    int state;
    union args;
    struct node * previos;
};

функция вычисления по узлам выглядит следующим образом
Код

double calculate(struct node * top, double x) {
    union args args;
    switch (top -> state) {
        case 0: return (top -> callback)(top -> args); /* бинарная операция над двумя константами */
        case 1: {
            args.loperand.constant    = x;
            args.roperand.address    = &calculate;
            args.roperand.argument = top -> previos;
            return (top -> callback)(args); /* рекурсивная зависимость от нижележащей функции */
        }
        case 3: {
            args.operand = x;
            return (top -> callback)(args); /* вычисление синуса */
        ...
    }
}

/* вычисление синуса */
double sin_c(union args args) {
    return sin(args.operand);
}

/* бинарная операция над двумя константами */
double multiply_cc(union args args) {
    return args.loperand.constant * args.roperand.constant;
}

/* рекурсивная зависимость от нижележащей функции */
double multiple_vc(union args args) {
    return args.loperand.constant * ((double (*)(struct node*, double))args.roperand.address)(args.roperand.argument);
}

/* и так далее */


Это сообщение отредактировал(а) MAKCim - 23.8.2007, 15:51


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Mal Hack
Дата 23.8.2007, 15:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Sartorius, так так так... Мысля вроде бы появилась...
А вот как мне быть, если: 
Код

double clsFncParse::evalMathFnc(double a, double b, mathFncType type)
{
    switch(type)    
    {
        case SIN: return sin(a); break;
        case COS: return cos(a); break;
        case TAN: return tan(a); break;
        case CTAN: return 1/tan(a); break;
        case ASIN: return asin(a); break;
        case ACOS: return acos(a); break;
        case ATAN: return atan(a); break;
        case ACTAN: return 1/atan(a); break;
        case LN: return log(a); break;
        case LG: return log10(a); break;
        case LOG: return log10(a)/log10(b); break;
        case EXP: return exp(a); break;
        case POWER: return pow(a,b); break;
        case SQRT: return sqrt(a); break;
    }
}

т.е. я не делал отдельного переопределения каждой функции...
т.е. мне нужны не только указатели, но и параметры... Имеет смысл свою структурку создать тогда, наверное...
PM ICQ   Вверх
Sartorius
Дата 23.8.2007, 15:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: 8
Всего: 37



Mal Hack,  наверно если хочешь сохранить в таком виде вычисление математики - то в цепочке команд функцию надо задавать индексом mathFncType type а не адресом, передавать в качестве  паоамеьов - два значения с вершины стека. и после вызова evalMathFnc класть ее возврат в стек. только есть одна засада - неизвестно сколько значений 1 или 2 ей реально понадобилось для вычислений. Так что в этом случае придется возвращать не double а структуру
Код

struct {
 double value;
 char     num_of_params;
}


и после возврата корректно очищать сте
PM MAIL ICQ   Вверх
Mal Hack
Дата 23.8.2007, 16:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Я вот думаю, что тут проще вместо адреса на функцию сделать поле со значением типа mathFncType и в случае, если очередная запись в стеке этого типа, вызывать функию evalMathFnc и передавать ей аргумент и этот тип...

В любом случае, как я понял, надо переходить к польской записи и формировать в том или ином виде этит стек операций. Вроде бы это - оптимальное решение...
Для двух операндов, в структуре стека - два поля для констант...
PM ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1295 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.