Рисунок 29 - отображение таблицы принятия решений
В третьей вкладке приводится таблица принятия решений для текущей заполняемой формы (рисунок 29). Верхняя часть таблицы - условия и комбинации условий, нижняя - действия и комбинации действий. Данные запрашиваются из временной таблицы, созданной процедурой PRC_TRANS().
По завершении работы открывается страница с результатами: поля step, tablename, actions, value пройденных таблиц принятия решения, загруженные из истории клиента (рисунок 30).
В
рамочке отображаются действия с наибольшим значением продукции.
Рисунок 30 - страница с результатами
3. Экспериментальные исследования
Для экспериментального исследования разработанных инструментов была выбрана часто встречающаяся в повседневной жизни задача выбора мобильного телефона, в частности смартфона.
В настоящее время многие сайты предлагают помощь в решении данного вопроса, но большинство из них основывается на предположении, что человек хоть немного разбирается в технических особенностях устройств. В разработанной схеме выбор основывается на необходимых качествах без уточнения технических параметров. Однако, в связи с довольно широким спектром устройств, система не предлагает выбор между конкретными моделями, а лишь помогает сузить диапазон поиска устройства.
Основными запрашиваемыми характеристиками служат тип операционной системы, предназначение, качество камеры, ценовой диапазон. Отдельным, частным случаем можно рассматривать выбор конкретной модели фирмы Apple.
База знаний для решения данной задачи реализована, как система таблиц
принятия решения, переходы между которыми осуществляются за счет использования
введения последействий. Структура таблиц решения приведена на рисунке 31.
Рисунок 31 - схема тестовой базы
Время работы в Oracle процедуры prc_go, включая вызов процедуры prc_add_history и функций fnc_get_value и fnc_calculation для одной из таблиц учебной базы: 00.233 секунд.
Создана база данных для инструментального средства, предназначенного для работы с таблицами принятия решений, встроенными в СУБД Oracle.
Создан пакет процедур и функций, реализующий процессы, обеспечивающие создание, редактирование и работу с таблицами принятия решений,
Доработаны интерфейсы, разработанные в курсовой работе Дербеневой Е. «Адаптивный интерфейс для работы с таблицами принятия решений», выполнить экспериментальную проверку инструментального средства. В частности, созданы страницы редактирования истории и таблицы ответов пользователей, реализован расширенный ввод данных.
Проведены экспериментальные исследования быстродействия.
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1 Бессмертный И.А., Применение реляционных операций для логического вывода в продукционных системах. Изв. вузов. Приборостроение, 2010 - с.34-38.
2 Бессарабов Н.В., Муса-Оглы Е.С. Универсальная модель данных. RSDN, 2011, №3 - с. 51-55.
Братко И., Алгоритмы искусственного интеллекта на языке PROLOG. Мск.: Вильямс, 2004 - с. 331-334.
В. Г. Рубанов, А. Г. Филатов, И. А. Рыбин. Интеллектуальные системы автоматического управления. Нечеткое управление в технических системах [электронное пособие]. URL: http://nrsu.bstu.ru/
5 Бессарабов Н.В., Семенютина Л.В. Таблицы принятия решений встроенные в базы данных. - INTELS, 2014.
Поспелов
Д. А. Моделирование рассуждений. Опыт анализа мыслительных актов. М.: Радио и
связь, 1989, 184 с.
ПРИЛОЖЕНИЕ А
Спецификация пакета pkg_get_answer
or replace PACKAGE pkg_get_answer ASprc_go( p_sysname decision.sysname%TYPE,_domain decision.domain%TYPE,_subdomain decision.subdomain%TYPE,_table_id decision.table_id%TYPE,_client_id data.client_id%TYPE);prc_add_history( p_sid history.sid%TYPE,_user history.user_id%TYPE,_client history.client_id%TYPE,_prod_id NUMBER,_position NUMBER,_prod_value history.prod_value%TYPE
);prc_translate( p_sysname decision.sysname%TYPE,_domain decision.domain%TYPE,_subdomain decision.subdomain%TYPE,_table_id decision.table_id%TYPE
);fnc_get_value(p_sid metadata.sid%TYPE,_client_id data_clients.client_id%TYPE,_prod_id decision.prod_id%TYPE)NUMBER;
fnc_calculation( p_x1 VARCHAR2
, p_x2 VARCHAR2
, p_operation VARCHAR2
, p_basis_id data_clients.basis_id%TYPE)NUMBER;
pkg_get_answer;
Тело пакета pkg_get_answer
CREATE OR REPLACE PACKAGE BODY pkg_get_answer AS
arr_type IS TABLE OF VARCHAR2(2000)INDEX BY BINARY_INTEGER;
prc_go( p_sysname decision.sysname%TYPE,_domain decision.domain%TYPE,_subdomain decision.subdomain%TYPE,_table_id decision.table_id%TYPE,_client_id data.client_id%TYPE)_sid NUMBER;_user_id NUMBER;_condition NUMBER; --число условий_min_condition NUMBER;_action NUMBER; --число действий_product NUMBER; --число продукций_select VARCHAR(2000);_where VARCHAR(2000);NUMBER; --чёрная рабочая сила)NUMBER;_in_str NUMBER;_substr_condition VARCHAR2(2000);_char_left VARCHAR(5);_char_right VARCHAR(5);_connection VARCHAR(5);
m_answer arr_type; -- масс в ответов пользователя_connection arr_type; -- массив логических операций_char arr_type; -- массив со скобками_product arr_type; -- массив, в который помещаются все распасенные продукци_productuctno NUMBER; -- массив с номерами эквивалентных продукций
v_prod_value history.prod_value%TYPE := 0;_condition decision.conditions%TYPE;
--m_condition arr_type; -- массив с продукциями
v_equals_cnt NUMBER;_count NUMBER := 0;
- c_prc_name VARCHAR2(50) := 'PKG_GET_ANSWER_NEW.PRC_GO';
-v_prc_step NUMBER := 0;_right_bkt NUMBER;_left_bkt NUMBER;NUMBER;_type metadata.type%TYPE;_START TIMESTAMP;_END TIMESTAMP;_START := SYSTIMESTAMP;
DISTINCT sid INTO c_sid FROM metadatasysname = p_sysnamedomain = p_domainsubdomain = p_subdomaintable_id = p_table_id;COUNT(condition_id),COUNT(action_id) INTO c_condition, c_action --находим количество условий и действийmetadatasid = c_sid;
c_condition < 1 THEN raise_application_error(-20050,'Количество условий должно быть не меньше 1.'); END IF;c_action < 1 THEN raise_application_error(-20050,'Количество действий должно быть не меньше 1.'); END IF;
COUNT(brackets) INTO v_right_bktmetadatasid = c_sidtable_id = p_table_idbrackets = '(';
COUNT(brackets) INTO v_left_bktmetadatasid = c_sidbrackets = ')';
v_left_bkt <> v_right_bkt THEN raise_application_error(-20050,'Количество открывающих и закрывающих скобок в таблице metadata не совпадает. Обратитесь к разработчику.'); END IF;
user_id INTO c_user_id FROM data_clientsclient_id =
p_client_id;
SELECT COUNT(prod_id) INTO c_product --находим количество продукций для данной таблицы
FROM decisionsid = c_sid;
i IN 1..c_condition LOOP --записываем логические операции в массивconnection, brackets INTO m_connection(i),m_char(i)metadatasid = c_sidcondition_id = i;LOOP;
MIN(condition_id) INTO c_min_condition
FROM data --берём номер условия, с которого есть ответы пользователя
WHERE sid = c_sidclient_id = p_client_id;
i IN c_min_condition..c_condition LOOPtype INTO v_typemetadatasid = c_sidcondition_id = i;
v_type'checkbox' THENanswer INTO m_answer(i)data --берём ответы пользователяsid = c_sidclient_id =
p_client_idcondition_id = i;'text' THENCASETO_NUMBER(answer, '999D9999') > 0
THEN 'yes''no'INTO m_answer(i)data --берём ответы пользователяsid = c_sidclient_id = p_client_idcondition_id = i;CASE;
END LOOP;
c_min_condition > 1 THEN -- если в начале ответов не хватает, то заполняем пробелы '_'
FOR i IN 1..c_min_condition LOOP_answer(i):='_';
END LOOP;IF;
i IN 1..c_product LOOP --=== режем все комбинации ответов и записываем в один массив
SELECT conditions INTO v_condition--m_condition(i)decisionsid = c_sidprod_id = i;
j IN 1..c_condition-1 LOOP
-IF j <> c_condition THEN
-BEGINregexp_substr(v_condition, '[^'||m_connection(j)||']+', 1, 1),(v_condition,m_connection(j))m_product((i-1)*c_condition+j),
v_in_strdual; --режем исходную строку с комбинацией условий в один
большой массив
--SELECT INSTR(v_condition,m_connection(j)) INTO v_in_str --ищем номер первого вхождения условия соединения
- FROM dual;
SUBSTR(v_condition,v_in_str+1,LENGTH(v_condition)) INTO v_substr_condition --вырезаем строку, начиная с этой позицииdual;
_condition := v_substr_condition;
-END;
-ELSE m_product((i-1)*c_condition+j):=v_substr_condition;
-END IF;LOOP;
_product((i-1)*c_condition+c_condition):=v_substr_condition;LOOP;
i IN 1..c_product LOOP_equals_cnt := 0;_select:= 'SELECT COUNT(*) FROM dual WHERE ';_where:='';j IN 1..c_condition LOOPm_connection(j)='&' THEN v_connection :=' AND ';m_connection(j)='||' THEN v_connection :=' OR ';v_connection :='';IF;
SUBSTR(m_char(j),1,1)='(' THEN v_char_left:=m_char(j); v_char_right:=''; --чтобы правильно расставить скобкиSUBSTR(m_char(j),1,1)=')' THEN v_char_left:=''; v_char_right:=m_char(j);v_char_left:=''; v_char_right:='';IF;
_where:= q_where||v_char_left||'(('''||m_answer(j)||'''='''||m_product((i-1)*c_condition+j)||''')OR('''||m_product((i-1)*c_condition+j)||'''=''_''))'||v_char_right;j <> c_condition THEN q_where:=q_where||v_connection; END IF;-- если есть ещё условия, добавляем соединениеLOOP;
_select:=q_select||q_where;
IMMEDIATE q_select INTO v_equals_cnt; -- есть ли совпадения, если есть - запоминаемv_equals_cnt > 0 THEN v_count := v_count+1;
v_productuctno:=i; -- если нашлось совпадение, считаем значение полученной продукции
v_prod_value := pkg_get_answer.fnc_get_value(c_sid, p_client_id, v_productuctno);
-dbms_output.put_line('Продукция = '||v_prod_value);_ADD_HISTORY(c_sid, c_user_id, p_client_id, v_productuctno, v_count, v_prod_value);IF;
LOOP;MIN(step) INTO xhistoryclient_id = p_client_idstatus =
'nostarted'user_id = c_user_id;
IF x = 0 THEN_output.put_line('Работа с системой завершена. Проверьте результаты');IF;
_END := SYSTIMESTAMP;
- TIME_WORK := TIME_END - TIME_START;_output.put_line('Времы работы: '||(TIME_end-TIME_START));prc_go;
PRC_ADD_HISTORY( p_sid history.sid%TYPE,_user history.user_id%TYPE,_client history.client_id%TYPE,_prod_id NUMBER,_position NUMBER,_prod_value history.prod_value%TYPE )_table_name metadata.tablename%TYPE;
c_table_id NUMBER; -- номер таблицы, для которой вызвали процедуру
c_action decision.actions%TYPE; -- нераспарсенные строки_aftereffect decision.aftereffect%TYPE;_condition decision.conditions%TYPE;
c_condition NUMBER; --количество условий_step NUMBER :=0; -- номер последнего шага_step NUMBER :=0; -- номер шага, который запишем_query VARCHAR2(2000):=''; -- для динамического запроса
v_action NUMBER; -- число действий_aftereffect NUMBER; -- число последействий_table_id NUMBER;_sid NUMBER;_progress VARCHAR2(50);_in_str NUMBER;_substr_condition VARCHAR2(2000);_basis_name history.basis_name%TYPE := '';
m_action arr_type; -- массив рспарсенных действий
m_aftereffect arr_type;
-m_condition_num arr_type;_condition_num VARCHAR2(5);_product_part metadata.condition%TYPE;_reason_part metadata.condition%TYPE;_limit metadata.prod_limit%TYPE;_connection VARCHAR2(5);
- чёрная рабочая силаNUMBER;NUMBER;NUMBER;BOOLEAN;VARCHAR2(2000);_reason VARCHAR2(2000) := '';_in_array NUMBER :=0;_sysname decision.sysname%TYPE;_domain decision.domain%TYPE;_subdomain decision.subdomain%TYPE;_min_condition_id NUMBER := 1;DISTINCT table_id, tablename, sysname, domain, subdomainc_table_id, c_table_name, c_sysname, c_domain, c_subdomain
FROM metadata --ищем номер таблицы по sid
WHERE sid = p_sid;
COUNT(*) INTO c
FROM history --смотрим были ли уже в этой таблице и прошли ли её полностью
WHERE client_id = p_clientuser_id = p_usersid = p_sidcondition_id = 1status = 'complete';
c>0 THEN
--здесь проверить список условий_application_error(-20050, 'Таблица, для которой была вызвана процедура уже была использована.');
END IF;
NVL(MAX(step),0) INTO c_stephistoryuser_id = p_userclient_id
= p_client;
IF p_position = 1 THEN v_step := c_step + 1; --если это первая продукция для данной таблицы, то шаг=МАХ+1
ELSE v_step := c_step;
END IF; -- иначе шаг=МАХ
- если в этой таблице не работали, или прошли её не полностью
IF c_step>0 THEN_query := 'UPDATE history SET status = ''complete'' WHERE user_id='||p_user||
' AND client_id='||p_client||
' AND step='||c_step;IMMEDIATE V_QUERY ; COMMIT;
-dbms_output.put_line(v_query);IF;
basis_name INTO v_basis_name --смотрим базис, в котором рабоает
клиентdata_clientsclient_id =
p_client;
-порогMAX(prod_limit) INTO
v_limitmetadatasid = p_sid;
-формируем обоснованиеCOUNT(condition_id) INTO c_condition --находим количество условий и действийmetadatasid = p_sid;
conditions INTO v_condition --берём продукцтюdecisionsid = p_sidprod_id = p_prod_id;j IN 1..c_condition-1 LOOPconnection INTO v_connection --m_connection(i)metadatasid = p_sidcondition_id = j;
regexp_substr(v_condition, '[^'||v_connection||']+', 1, 1),(v_condition,v_connection)v_product_part, --m_product(j),
v_in_strdual; --режем исходную строку с комбинацией условий в один
большой массив
SELECT condition INTO v_reason_partmetadatasid = p_sidcondition_id = j;
v_product_part = 'yes' THEN v_reason := v_reason || v_reason_part || ', ';v_product_part = 'no' THEN v_reason := v_reason || 'not ' || v_reason_part || ', ';v_product_part = '_' THEN v_reason := v_reason || 'no matter ' || v_reason_part || ', ';IF;
SUBSTR(v_condition,v_in_str+1,LENGTH(v_condition)) INTO v_substr_condition --вырезаем строку, начиная с этой позицииdual;
_condition := v_substr_condition;LOOP;
condition INTO v_reason_partmetadatasid = p_sidcondition_id = c_condition;
v_substr_condition = 'yes' THEN v_reason := v_reason || v_reason_part;v_substr_condition = 'no' THEN v_reason := v_reason || 'not ' || v_reason_part;v_substr_condition = '_' THEN v_reason := v_reason || 'no matter ' || v_reason_part || ', ';IF;
p_prod_value < v_limit THEN --если продукция не проходит порог_reason := v_reason || ' (value = '||p_prod_value||'< lower limit = '||v_limit||')';
_query := 'INSERT INTO history(sid, tablename, user_id, client_id, status, step, prod_id, prod_value, basis_name, condition_id, reason)('||p_sid||', '''||c_table_name||''', '||p_user||', '||p_client||', ''abort'', '||v_step||', '||p_prod_id||', '||p_prod_value||', '''||v_basis_name||''', '||c_min_condition_id||', '''||v_reason||''')'; --m_condition_num(i)IMMEDIATE v_query; COMMIT;_reason := v_reason || ' (value = '||p_prod_value||'> lower limit = '||v_limit||')';
actions, aftereffect INTO c_action, c_aftereffectdecisionsid = p_sidprod_id = p_prod_id;
LENGTH(c_action)-LENGTH(REPLACE(c_action, '&')) INTO v_action FROM dual;
v_action := v_action + 1; --считаем количество действий в строке (кол-во
разделителей+1)
FOR i IN 1..v_action LOOPREGEXP_SUBSTR(c_action,'[^&]+',1,i) INTO m_action(i) --режем эту строку в массивdual;--(SELECT c_action FROM dual);LOOP;
c_aftereffect IS NULL THEN
i IN 1..v_action LOOP --записываем список действий_query := 'INSERT INTO history (sid, tablename, user_id, client_id, action, aftereffect, status, step, prod_value, reason, basis_name)('||p_sid||', '''||c_table_name||''', '||p_user||', '||p_client||', '''||m_action(i)||''', ''null'', ''end'', '||v_step||', '||p_prod_value||', '''||v_reason||''', '''||v_basis_name||''')';IMMEDIATE v_query;
-dbms_output.put_line(v_query);
COMMIT;LOOP;
-проверяем, есть ли непройденные ветки
/* SELECT MIN(step) INTO xhistoryclient_id = p_clientstatus = 'nostarted'user_id = p_user;
x > 0 THENMIN(prod_id) INTO yhistoryclient_id = p_clientstatus = 'nostarted'user_id = p_userstep = x;
aftereffect INTO zhistoryclient_id = p_clientstatus = 'nostarted'user_id = p_userstep = xprod_id = y;
_query := 'UPDATE data_clients SET current_progress = '||SUBSTR(z,7,3)||' WHERE client_id='||p_client;IMMEDIATE v_query;;
dbms_output.put_line('Работа с одной из веток завершена. Проверьте результаты');*/
-т.к. дошли до конца, ставим current_progress = 0
v_query := 'UPDATE data_clients SET current_progress = 0 WHERE client_id='||p_client;IMMEDIATE v_query;
-dbms_output.put_line(v_query);
COMMIT;_output.put_line('Работа с веткой решений завершена.');--('Работа с системой завершена. Проверьте результаты');
-END IF;
- Если последействие IS NOT NULL --MIN(condition_id) INTO c_min_condition_iddatasid = p_sidclient_id = p_client;
LENGTH(c_aftereffect)-LENGTH(REPLACE(c_aftereffect, '|')) INTO v_aftereffect FROM dual;
v_aftereffect := v_aftereffect +1; --считаем количество последействий
(кол-во разделителей +1)
FOR i IN 1..v_aftereffect LOOP
--f := false;-- повторений не было
REGEXP_SUBSTR(c_aftereffect ,'[^|]+',1,i) INTO s--распарсиваем список последействий по разделителям
FROM dual;
-SELECT REGEXP_SUBSTR(s,'[^"]+',1,2) INTO v_tablename --берём название таблицы из кавычек
- FROM dual;
DISTINCT table_id INTO v_table_id
FROM metadata --ищем номер таблицы по названию
WHERE UPPER(tablename) = UPPER((SELECT REGEXP_SUBSTR(s,'[^"]+',1,2) --берём название таблицы из кавычекdual))sysname = c_sysnamedomain = c_domainsubdomain = c_subdomain;
DISTINCT sid INTO v_sid
FROM metadata -- ищем sid по номеру
WHERE table_id = v_table_idsysname = c_sysnamedomain = c_domainsubdomain = c_subdomain;
- !!!!!!!!!!!!!!!!COUNT(*) INTO c FROM history --смотрим были ли уже в таблице, на которую хотим перейтиclient_id = p_clientuser_id =
p_usersid = v_sidcondition_id = 1status = 'complete';
IF c = 0 THEN -- если нет, то запишем последействия
-IF v_in_array > 0 THEN -- если в массив уже что-то записали, то ищем повторения
-- FOR j IN 1..v_in_array LOOP
- IF 'table_'||v_table_id||'_1' = m_aftereffect(j) THEN f := true; END IF; --нашли повторение
-- END LOOP;
-END IF;
-
-IF f = false THEN -- если повторений нет, то записываем новый элемент
v_in_array := v_in_array + 1;NVL(REGEXP_SUBSTR(s,'[0-9]*$'),1) INTO v_condition_num--m_condition_num(v_in_array )
FROM dual; --(SELECT s FROM dual); --вытаскиваем номер условия, к
которому нужно перейти
IF v_condition_num IS NULL THEN m_aftereffect(v_in_array):='table_'||v_table_id||'_1';m_aftereffect(v_in_array):='table_'||v_table_id||'_'||v_condition_num;IF;
-END IF;IF;LOOP;
v_in_array = 0 THEN raise_application_error(-20050, 'Все таблицы из списка последействий уже пройдены.'); END IF;
_aftereffect := v_in_array ;v_aftereffect > v_action THEN
--== дополняем массивыi IN v_action+1..v_aftereffect LOOP_action(i):='';LOOP;:=
v_aftereffect;i IN v_aftereffect+1..v_action
LOOP_aftereffect(i):='';LOOP;:=v_action;IF;
FOR i IN 1..c LOOP -- записываем следующие шаги
v_query := 'INSERT INTO history(sid, tablename, user_id, client_id, action, aftereffect, status, step, prod_id, prod_value, basis_name, condition_id, reason)('||p_sid||', '''||c_table_name||''', '||p_user||', '||p_client||',
'''||m_action(i)||''', '''||m_aftereffect(i)||''',
''nostarted'', '||v_step||', '||p_prod_id||', '||p_prod_value||', '''||v_basis_name||''', '||c_min_condition_id||', '''||v_reason||''')'; --m_condition_num(i)IMMEDIATE v_query; COMMIT;
-dbms_output.put_line(v_query);
END LOOP;
-== берём номер таблицы, на которую перешли, записываем в progress в client_id ==
v_progress := SUBSTR(m_aftereffect(1),7,3);_query := 'UPDATE data_clients SET current_progress ='''||v_progress||''' WHERE client_id='||p_client;IMMEDIATE v_query; COMMIT;
-dbms_output.put_line(v_query);
IF; -- close if aftereffect is not nullIF; --close limitprc_add_history;
PRC_TRANSLATE( p_sysname decision.sysname%TYPE,_domain decision.domain%TYPE,_subdomain decision.subdomain%TYPE,_table_id decision.table_id%TYPE )_action arr_type; --массив действий_connection arr_type;_product arr_type;_sid NUMBER;_condition NUMBER; --чиcло условий_action NUMBER; --число действий_product NUMBER;
v_in_str NUMBER;
-для динамических запросов
q VARCHAR(2000);_execute VARCHAR(2000);
--чёрная рабочая силаNUMBER;
c NUMBER;NUMBER;decision.actions%TYPE;decision.actions%TYPE;_CONDITION DECISION.CONDITIONS%TYPE;_START TIMESTAMP;_end TIMESTAMP;_START := SYSTIMESTAMP;
DISTINCT sid INTO c_sid FROM metadatasysname = p_sysnamedomain = p_domainsubdomain = p_subdomaintable_id = p_table_id;COUNT(condition_id),COUNT(action_id) INTO v_condition, v_action --находим количество условий и действийmetadatasid = c_sid;