Задача идентификации серийного номера (СН) - это задача определения номенклатурной группы (НГ) изделия по его СН. Информация об изделиях (номенклатуре), их номенклатурных группах и серийных номерах хранится в базе серийных номеров и частично дублируется в базе гарантийного отдела. Обе базы созданы на платформе 1С:Предриятие 8.
Для определения номенклатурной группы изделия по его СН предварительно создается и обучается самоорганизующаяся карта Кохонена (СКК).
При приеме изделия на гарантийное обслуживание по СН изделия определяется узел СКК, отвечающий СН, а затем и НГ, ассоциируемая с найденным узлом.
Таким образом, решаются следующие задачи:
СКК создается средствами 1С:Предриятие 8, а графическое представление СКК выполняется в 3ds Max средствами MAXScript.
Самоорганизующаяся карта Кохонена - это вид нейронной сети, каждый узел (нейрон) которой связан с каждым элементом входного слоя (на рис. 1 входной слой показан зеленым цветом).
Рис. 1. Структура двухмерной 16-узловой СКК
Размерность СКК определяется числом измерений пространства, в котором располагаются узлы СКК.
Другим параметром СКК является ее число узлов.
Мы будем использовать двухмерную СКК с числом узлов, равным квадрату целого числа, которое будем называть размером СКК (рСКК).
Для наглядности будем отображать СКК на регулярную квадратную решетку с числом узлов рСКК * рСКК.
Связь узла СКК с входным слоем фиксируется вектором весовых коэффициентов (далее узловым вектором, УВ). Размер УВ равен числу элементов во входном слое.
Обучение СКК заключается в определение весовых коэффициентов УВ, таких, что схожим объектам отвечают компактно сгруппированные узлы СКК. Эти группы узлов СКК не пересекаются и различаются некоторыми характеристиками.
Так, при работе с СН каждая группа СКК отвечает некоторой НГ.
По своей сути СКК проецирует многомерное пространство (его размерность определяет число элементов входного слоя СКК) в пространство с более низкой размерностью.
Подавая на вход обученной СКК описание объекта, можно определить узел, наиболее полно отвечающий этому описанию. В случае СН после определения такого узла мы легко найдем и соответствующую НГ, то есть решим поставленную задачу.
Наглядно, например, 36-узловую сеть (рСКК = 6) можно представить в виде растровой карты (рис. 2), в которой узлы одной группы закрашиваются оттенками одного цвета.
Рис. 2. Представление двухмерной 36-узловой СКК в виде растровой карты
Такое представление получается непринужденно, если размер УВ равен 3. В этом случае каждый узел закрашивается цветом, RGB-компоненты которого определяются весовыми коэффициентами УВ.
Для получения рис. 1 в 3ds Max можно использовать следующий MAXScript-код (берется вид сверху):
delete $*
fn drawLine ss k p1 p2 = (
addNewSpline ss
addKnot ss k #corner #line p1
addKnot ss k #corner #line p2
updateShape ss
)
r = 6
n = 4
d = 3 * r
x = -0.5 * d * (n - 1)
y0 = -r * n + 0.5 * r
for k = 1 to n do (
y = y0
for m = 1 to n do (
p = [x, y, 0]
sphere radius:r pos:p wireColor:red
y += d
)
x += d
)
n2 = 4
r2 = 0.5 * r
d2 = 5 * r2
x2 = -0.5 * d2 * (n2 - 1)
y2 = y0 - 7 * r2
for k2 = 1 to n2 do (
p2 = [x2, y2, 0]
sphere radius:r2 pos:p2 wireColor:green
x2 += d2
x = -0.5 * d * (n - 1)
for k = 1 to n do (
y = y0
for m = 1 to n do (
ss = line render_renderable:on render_displayRenderMesh:on wireColor:black\
render_useViewportSettings:off render_displayRenderSettings:true render_thickness:0.5
p = [x, y, 0]
drawLine ss 1 p2 p
y += d
)
x += d
)
)
Рис. 2 поможет получить нижеприводимый MAXScript-код:
delete $*
d = 10
n = 6
x = -0.5 * d * (n - 1)
y0 = x
l = 0
n2 = n * n / 2
for k = 1 to n do (
y = y0
for m = 1 to n do (
p = [x, y, 0]
l += 1
clr = if l < n2 then random [0, 0, 0] [255, 255, 0] else random [0, 0, 0] [0, 255, 255]
box width:d length:d height:(random 1 5) pos:p wireColor:clr
y += d
)
x += d
)
Замечание. Вместо Box можно употребить Plane.
В базе гарантийного отдела изделие описывается в справочнике Номенклатура. Каждое реализованное изделие снабжается СН, который фиксируется на изделии в виде штрих-кодовой наклейки. Число СН изделия неограниченно.
СН хранится в справочнике серийных номеров, который подчинен справочнику Номенклатура. Кроме того, каждое изделие входит в одну НГ, что отражается в справочнике номенклатурных групп.
СН - это последовательность из не более 30 символов без ведущих пробелов, например (в скобках указана НГ, в которую входит изделие с текущим СН):
000-0009861 (DVD RW)
000001 09287131 (UPS)
9YP09DRB (Hard Drives)
A-204-G03-01679 (Media)
CDDHZBED (Hard Drives)
EW7722UTN15CB00101 (Netware)
IG31AM4S-03 70008G11403005 (Mother Boards)
S/N:17000114600372 (Cases)
U100-047RUK0908000322 (Platforms)
VN700M1W2N-A1112000843 (Cases)
YTD6TSAA42416D00211 (Cases)
В нашем случае на вход СКК подается информация о СН, представленная в виде вектора из трех элементов.
Входной вектор получается по следующей схеме:
Результат обеспечивает следующая 1С-функция, получающая на входе СН:
функция создатьВВ(снТ)
// Массив хранит компоненты ВВ и код НГ верхнего уровня
вв = новый массив(4);
// Простое число - делитель числового представления элемента входного вектора
пЧ = 109;
дВ = стрДлина(снТ) / 3;
д = цел(дВ);
если д < дВ тогда д = д + 1 конецЕсли;
н = 1;
для к = 0 по 2 цикл
вв[к] = сред(снТ, н, д);
н = н + д;
конецЦикла;
// Дополняем при необходимости последний элемент вектора хвостовыми пробелами
пока стрДлина(вв[2]) < д цикл вв[2] = вв[2] + " " конецЦикла;
для к = 0 по 2 цикл
в = "";
для к2 = 1 по д цикл в = в + кодСимвола(сред(вв[к], к2, 1)) конецЦикла;
вв[к] = число(в) % пЧ;
конецЦикла;
возврат вв;
конецФункции
Вызов функции и печать результата:
снТ = "9YP09DRB";
вв = создатьВВ(снТ);
для к = 0 по 2 цикл сообщить(вв[к]) конецЦикла;
Напечатает:
81
64
85
При работе с СН "9YP09DRB" получаем следующие три элемента: "9YP", "09D" и "RB " (последний элемент дополнен хвостовым пробелом).
Далее после замены символов на Unicode-коды имеем следующие три элемента: "578980", "485768" и "826632".
В качестве результата берем остаток от деления элемента на заданное простое число (в примере оно равно 109).
В тестовом примере задействованы шесть НГ верхнего уровня и все их дочерние НГ (для них в справочнике НГ значение реквизита Кластер = истина, рис. 3).
Рис. 3. Кластерная НГ
Кластерные НГ верхнего уровня (в скобках указан код НГ):
CD (263);
Coolers (1218).
Hard Drives (219);
Mother Boards (226);
UPS (387);
Video Cards (297)
Флажок Кластер устанавливается для шести НГ верхнего уровня интерактивно. В дочерних НГ этот флажок устанавливается следующим кодом:
пст = справочники.НоменклатурныеГруппы.ПустаяСсылка();
змн = истина;
пока змн цикл
змн = ложь;
в = справочники.НоменклатурныеГруппы.Выбрать();
пока в.Следующий() цикл
если в.Кластер тогда продолжить конецЕсли;
р = в.Родитель;
если р = пст тогда продолжить конецЕсли;
если р.Кластер тогда
во = в.ПолучитьОбъект();
во.Кластер = истина;
во.Записать();
змн = истина;
конецЕсли;
конецЦикла;
конецЦикла;
СН попадает в тестовую выборку, если его владелец (изделие) входит в тестовую (кластерную) НГ:
функция обучающаяВыборка()
з = новый запрос;
з.текст = "выбрать код из справочник.СерийныеНомера
| где владелец.НоменклатурнаяГруппа.Кластер и владелец.код <> код";
тз = з.Выполнить().Выгрузить();
тз.Колонки.Добавить("вв");
// Формируем входные векторы; код функции создатьВВ см. выше
для каждого с из тз цикл с.вв = создатьВВ(сокрЛП(с.код)) конецЦикла;
возврат тз;
конецФункции
Из обучающей выборки исключены фиктивные СН. Код такого СН равно коду владельца.
Вызов функции и печать одного ВВ.
тз = обучающаяВыборка();
вв = тз[300][1];
для к = 0 по 2 цикл сообщить(вв[к]) конецЦикла;
При описанном выше способе формирования обучающей выборки (ОВ) в нее попадет несколько десятков миллионов СН. Такой размер ОВ чрезмерен. Для его уменьшения можно включать в ОВ лишь часть СН каждой номенклатуры, что и делает приводимая ниже функция:
функция обучающаяВыборкаМалая()
пст = справочники.НоменклатурныеГруппы.ПустаяСсылка();
т = новый текстовыйДокумент;
ф = "c:\td.txt";
тзО = новый таблицаЗначений;
тзО.Колонки.Добавить("вв");
з = новый запрос;
з.текст = "выбрать ссылка, номенклатурнаяГруппа как нг из справочник.Номенклатура
| где НоменклатурнаяГруппа.Кластер";
в = з.Выполнить().Выбрать();
з.текст = "выбрать первые 5 код из справочник.СерийныеНомера
| где владелец = &влд и владелец.код <> код";
пока в.Следующий() цикл
з.УстановитьПараметр("влд", в.Ссылка);
в2 = з.Выполнить().Выбрать();
// Найдем код НГ верхнего уровня (код функции найтиНГ0 см. ниже)
кодНГ0 = найтиНГ0(в.нг, пст);
// Формируем входные векторы; код функции создатьВВ см. выше
пока в2.Следующий() цикл
с = тзО.Добавить();
вв = создатьВВ(сокрЛП(в2.код));
вв[3] = кодНГ0;
с.вв = вв;
т.ДобавитьСтроку("" + вв[0] + ", " + вв[1] + ", " + вв[2] + ", " + кодНГ0);
конецЦикла;
конецЦикла;
т.Записать(ф);
возврат тзО;
конецФункции
функция найтиНГ0(знач нг, пст)
пока истина цикл
р = нг.Родитель;
если р = пст тогда прервать конецЕсли;
нг = р;
конецЦикла;
возврат нг.код;
конецФункции
Вызов функции обучающаяВыборкаМалая и печать одного ВВ и кода НГ верхнего уровня.
тзО = обучающаяВыборкаМалая();
вв = тзО[200][0];
сообщить("" + вв[0] + ", " + вв[1] + ", " + вв[2] + ", " + вв[3]);
Программа обучения СКК берет данные из файла c:\td.txt, созданного функцией обучающаяВыборкаМалая.
Входные данные: обучающая выборка.
Выходные данные: веса узлов СКК (записываются в таблицу значений тзСКК).
Инициализация всех УВ обеспечивает следующая функция:
функция начальныеУВ(рСКК, чВ)
// рСКК - размер СКК (число узлов в СКК равно рСКК * рСКК)
// чВ - размер входного вектора (число элементов во входном слое)
гсч = новый генераторСлучайныхЧисел(1);
// Храним УВ в таблице значений размера рСКК * рСКК
// Последний элемент УВ резервируем под код НГ высшего уровня, ассоциируемой с узлом СКК
тзСКК = новый таблицаЗначений;
для к = 0 по рСКК - 1 цикл тзСКК.Колонки.Добавить("к" + к) конецЦикла;
для к = 0 по рСКК - 1 цикл
с = тзСКК.Добавить();
для к2 = 0 по рСКК - 1 цикл
ув = новый массив(чВ + 1);
для к3 = 0 по чВ цикл ув[к3] = гсч.СлучайноеЧисло(0, 255) конецЦикла;
с[к2] = ув;
конецЦикла;
конецЦикла;
возврат тзСКК;
конецФункции
Вызов функции и проверка результата:
// Размер СКК (число узлов в СКК равно рСКК * рСКК)
рСКК = 40;
// Размер входного вектора (число элементов во входном слое)
чВ = 3;
тзСКК = начальныеУВ(рСКК, чВ);
// Вывод одного случайно выбранного узлового вектора
гсч = новый генераторСлучайныхЧисел(3);
х = гсч.СлучайноеЧисло(0, рСКК);
у = гсч.СлучайноеЧисло(0, рСКК);
вв = тзСКК[х][у];
сообщить("ВВ: " + вв[0] + ", " + вв[1] + ", " + вв[2]);
Напечатает
ВВ: 42, 175, 67
Для текущего ВВ определяется лучший узел (ЛУ). Расстояние между ВВ и УВ определяется по евклидовой метрике:
Для ЛУ расстояние между его УВ и ВВ минимально.
Замечание. Для сокращения счета квадратный корень не извлекается.
Массив, содержащий индексы ЛУ в СКК, возвращается следующей функцией:
функция найтиЛУ(тзСКК, вв)
лу = новый массив(2);
дЛУ = 3 * 255 * 255;
лу[0] = -1;
лу[1] = -1;
рСКК = тзСКК.количество() - 1;
рВВ = вв.количество() - 2;
для к0 = 0 по рСКК цикл
с = тзСКК[к0];
для к1 = 0 по рСКК цикл
ув = с[к1];
д = 0;
для к2 = 0 по рВВ цикл
р = вв[к2] - ув[к2];
д = д + р * р;
конецЦикла;
если д < дЛУ тогда
дЛУ = д;
лу[0] = к0;
лу[1] = к1;
конецЕсли;
конецЦикла;
конецЦикла;
возврат лу;
конецФункции
Вызов функции и печать результата:
гсч = новый генераторСлучайныхЧисел(5);
тзО = обучающаяВыборкаМалая();
// Инициализации СКК
тзСКК = начальныеУВ(40, 3);
// Случайный выбор ВВ
кО = тзО.количество() - 1;
вв = тзО[гсч.СлучайноеЧисло(0, кО)][0];
лу = найтиЛУ(тзСКК, вв);
сообщить("Индексы ЛУ: " + лу[0] + " " + лу[1]);
Соседи ЛУ - это узлы, расположенные в окрестности ЛУ, то есть в пределах окружности радиуса рЛУ с центром в ЛУ.
Радиус рЛУ уменьшается с каждой итерацией алгоритма обучения по следующей зависимости:
рЛУ = рСКК * exp(-чои / (чои - к + 1)),
где
рСКК - размер СКК (так, в случае 36-узловой СКК рСКК = 6);
чои - число итераций алгоритма обучения;
к - номер итерации;
рЛУ - текущее значение радиуса окрестности ЛУ.
Поскольку СКК отображается на регулярную квадратную решетку, то для вычисления соседей ЛУ подойдет следующая функция:
функция соседиЛУ(рСКК, лу, рЛУ)
// Таблица значений, хранящая массивы с координатами соседей ЛУ
// Также содержит и массив с координатами самого ЛУ
тзСЛУ = новый таблицаЗначений;
тзСЛУ.колонки.добавить("сЛУ");
лу0 = лу[0];
лу1 = лу[1];
рЦ = цел(рЛУ);
если рЦ = 0 тогда
// В результирующей таблице значений только сам ЛУ
м = новый массив(3);
м[0] = лу0; м[1] = лу1; м[2] = 0;
с = тзСЛУ.добавить();
с.сЛУ = м
иначе
рЛУ2 = рЛУ * рЛУ;
// Границы минимальной области, охватывающей окрестность ЛУ
// Левая, правая, нижняя и верхняя границы охватывающей области
л = лу0 - рЦ; п = лу0 + рЦ;
н = лу1 - рЦ; в = лу1 + рЦ;
для х = л по п цикл
// Область может выходит за пределы решетки СКК
если х < 0 или х > рСКК - 1 тогда продолжить конецЕсли;
х2 = ?(х < 0, -х, х) - лу0;
х2 = х2 * х2;
для у = н по в цикл
если у < 0 или у > рСКК - 1 тогда продолжить конецЕсли;
у2 = ?(у < 0, -у, у) - лу1;
д2 = х2 + у2 * у2;
если д2 <= рЛУ2 тогда
м = новый массив(3);
м[0] = х; м[1] = у; м[2] = д2;
с = тзСЛУ.добавить();
с.сЛУ = м
конецЕсли;
конецЦикла;
конецЦикла;
конецЕсли;
возврат тзСЛУ;
конецФункции
Проверка функции:
рСКК = 40;
рЛУ = 5;
лу = новый массив(2);
лу[0] = 5;
лу[1] = 15;
тзСЛУ = соседиЛУ(рСКК, лу, рЛУ);
сообщить("Соседи ЛУ и квадрат расстояния соседа от ЛУ:");
для каждого с из тзСЛУ цикл сообщить("" + с.сЛУ[0] + ", " + с.сЛУ[1] + ", " + с.сЛУ[2]) конецЦикла;
Напечатает:
Соседи ЛУ и квадрат расстояния соседа от ЛУ:
0, 15, 5
1, 12, 5
...
После определения соседей ЛУ пересчитываются веса их УВ и вес УВ самого ЛУ по следующей формуле:
УВik = УВik - 1 + пК * (ВВik - 1 - УВik - 1),
где
k - номер итерации;
ВВ - входной вектор;
УВ - узловой вектор;
пК - поправочный коэффициент.
пК= рЛУ / рСКК * (1 - д / рСКК),
где
рСКК - размер СКК;
рЛУ - текущее значение радиуса окрестности ЛУ;
д - расстояние соседа от ЛУ.
Поправочный коэффициент снижается с каждой обучающей итерацией, поскольку зависит от радиуса окрестности ЛУ. Также пК уменьшается и при удалении узла от ЛУ.
Пересчет и изменение кода НГ, ассоциируемой с узлом, обеспечивает следующая процедура:
процедура пересчетУВ(тзСЛУ, вв, рЛУ, рСКК, тзСКК)
рВ = вв.количество() - 2;
пК1 = рЛУ / рСКК;
для каждого с из тзСЛУ цикл
х = с.сЛУ[0];
у = с.сЛУ[1];
пК2 = (1.0 - с.сЛУ[2] / рСКК);
ув = тзСКК[х][у];
для i = 0 по рВ цикл
ув[i] = ув[i] + пК1 * пК1 * (вв[i] - ув[i]);
конецЦикла;
ув[рВ + 1] = вв[рВ + 1];
тзСКК[х][у] = ув;
конецЦикла;
конецПроцедуры
// Возвращает массив с компонентами ВВ, считанного из текста тВВ
функция изСтрокиВВ(гсч, тВВ)
сч = гсч.СлучайноеЧисло(1, тВВ.КоличествоСтрок());
с = тВВ.ПолучитьСтроку(сч);
вв = новый массив(4);
п = найти(с, ",");
вв[0] = число(лев(с, п - 1)); с = сред(с, п + 2); п = найти(с, ",");
вв[1] = число(лев(с, п - 1)); с = сред(с, п + 2); п = найти(с, ",");
вв[2] = число(лев(с, п - 1));
вв[3] = число(сред(с, п + 2));
возврат вв;
конецФункции
Вызов процедуры ПересчетУВ и печать ВВ, УВ лучшего узла до и после пересчета:
гсч = новый генераторСлучайныхЧисел(7);
тВВ = новый текстовыйДокумент;
// Читаем данные из файла с обучающими ВВ
тВВ.Прочитать("c:\td.txt");
// Получаем случайный ВВ (текст функции ИзСтрокиВВ см. чуть выше)
вв = изСтрокиВВ(гсч, тВВ);
сообщить("ВВ:");
сообщить("" + вв[0] + " " + вв[1] + " " + вв[2] + " " + вв[3]);
// Размер СКК (число узлов в СКК равно рСКК * рСКК)
рСКК = 40;
// Размер входного вектора (число элементов во входном слое)
чВ = 3;
тзСКК = начальныеУВ(рСКК, чВ);
рЛУ = 5;
лу = новый массив(2);
лу[0] = 5;
лу[1] = 15;
тзСЛУ = соседиЛУ(рСКК, лу, рЛУ);
ув = тзСКК[лу[0]][лу[1]];
сообщить("УВ до:");
сообщить("" + ув[0] + " " + ув[1] + " " + ув[2] + " " + ув[3]);
пересчетУВ(тзСЛУ, вв, рЛУ, рСКК, тзСКК);
сообщить("УВ после:");
сообщить("" + ув[0] + " " + ув[1] + " " + ув[2] + " " + ув[3]);
Напечатает:
ВВ:
107 35 77 226
УВ до:
115 251 115 149
УВ после:
114,875 247,625 114,40625 226
Приводимая программа обучения СКК вызывает ранее описанные функции и процедуру. Результирующие УВ, хранимые таблицей значений тзСКК, выгружаются в текстовый файл c:\sm.txt.
// Размер СКК (число узлов в СКК равно рСКК * рСКК)
рСКК = 40;
// Размер входного вектора (число элементов во входном слое)
чВ = 3;
// Число обучающих итераций
чои = 1000;
// Имя файла с ВВ обучающей выборки
фов = "c:\td.txt";
// Имя файла с результирующими УВ
фув = "c:\sm.txt";
// Начальное значения радиуса в задаче поиска соседей ЛУ
рЛУ0 = рСКК / 2;
// ГСЧ для случайного выбора обучающего ВВ
гсч = новый генераторСлучайныхЧисел(7);
// Текст с обучающими ВВ (каждая строка текста содержит компоненты одного ВВ)
тВВ = новый текстовыйДокумент;
// Читаем данные из файла с обучающими ВВ
тВВ.Прочитать("c:\td.txt");
// Инициализация СКК (задание начальных весов УВ)
тзСКК = начальныеУВ(рСКК, чВ);
// Обучение
тН = текущаяДата();
для к = 1 по чои цикл
// Получаем обучающий вектор
вв = изСтрокиВВ(гсч, тВВ);
// Находим для него ЛУ
лу = найтиЛУ(тзСКК, вв);
// Радиус для поиска соседей ЛУ
рЛУ = окр(рСКК * exp(-чои / (чои - к + 1)), 2);
сообщить("к = " + к + "; рЛУ = " + рЛУ);
// Находим соседей ЛУ
тзСЛУ = соседиЛУ(рСКК, лу, рЛУ);
// Пересчет весовых коэффициентов соседей ЛУ и самого ЛУ
пересчетУВ(тзСЛУ, вв, рЛУ, рСКК, тзСКК);
конецЦикла;
сообщить("Время обучения (с): " + (текущаяДата() - тН));
// Вывод в файл результирующих УВ и кода ассоциируемого с узлом НГ. Используем имеющийся объект тВВ
тВВ.Очистить();
для каждого с из тзСКК цикл
для к = 0 по рСКК - 1 цикл
ув = с[к];
фС = "ЧЦ=10; ЧДЦ=2; ЧРД='.'; ЧН=";
ув0 = сокрЛ(формат(ув[0], фС));
ув1 = сокрЛ(формат(ув[1], фС));
ув2 = сокрЛ(формат(ув[2], фС));
тВВ.ДобавитьСтроку("" + ув0 + ", " + ув1 + ", " + ув2 + ", " + ув[3]);
конецЦикла;
конецЦикла;
тВВ.Записать(фув, кодировкаТекста.ANSI);
Выполнена в 3ds Max при помощи следующего кода:
delete $*
nSM = 40
w = 4
xyShft = -w * nSM / 2
// Открываем файл с УВ и кодами НГ, ассоциируемыми с узлами СКК
f = openFile "c:\\sm.txt"
for x = 1 to nSM do (
xP = xyShft + w * x
for y = 1 to nSM do (
yP = xyShft + w * y
nV = readLine f
arrNV = filterString nV ","
-- При выводе очередного узла его цвет определяем по коду НГ, ассоциируемой с узлом
clr = case arrNV[4] of (
" 219": [255, 0, 0] -- Hard Drives
" 226" : [0, 255, 0] -- Mother Boards
" 263" : [0, 0, 255] -- CD
" 297" : [255, 255, 0] -- Video Cards
" 387" : [255, 0, 255] -- UPS
" 218" : [0, 255, 255] -- Coolers
defaut: [0, 0, 0]
)
plane width:w length:w pos:[xP, yP, 0] wireColor:clr lengthSegs:1 widthSegs:1
)
)
close f
В полученном рисунке цвет узла СКК определяется по коду НГ, ассоциированной с выводимым узлом.
Рис. 4. Представление обученной СКК
НГ имеет подгруппы. Поэтому на рис. 4 имеются несвязанные области одного цвета: каждая область отвечает одной или нескольким подгруппам НГ верхнего уровня.
Задача идентификации СН при наличии обученной СКК решается следующим образом:
Приведенную схему реализует следующая функция:
функция найтиНГ(снТ, тзСКК)
вв = создатьВВ(снТ);
лу = найтиЛУ(тзСКК, вв);
ув = тзСКК[лу[0]][лу[1]];
кодНГ = ув[3];
возврат кодНГ;
конецФункции