PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » OpenSource » Radix Tree AI


Radix Tree AI

Сообщений 1 страница 5 из 5

1

gjrbyek ajhev

Отредактировано Webarion (13.06.2025 13:17:36)

0

2

Webarion
Mid медленная потому что создаёт строковое представление данных, то есть требует манипуляции, в то время как обращение непосредственно к бинарным данным не конвертируя в строку естественно будет быстрее. К примеру "apple" берём первую букву, но не "a" а 97, литеральная 'a' является бинарным числом, вот мы и получаем прямой доступ по указателю используя *c\c и в дереве также должен быть доступ к элементам в бинарном виде, то есть от корня ищем 97 что является "a". Вроде логически понимаю, но как это записать в код пока не ясно.
Посмотри описание "посимвольный разбор"

0

3

Webarion
За основу можно взять этот алгоритм. Это создаёт дерево файлов, то есть структурно это копия дерева "Radix Tree" с разницей, что запрашиваются файлы файловой системы. Если представить "apple" как "a\p\p\l\e", то это и есть дерево. Разделитель нам ставить не нужно, мы проверяем если в дереве есть папка "a" то входим в неё и ищем в ней следующий элемент и продолжаем до тех пор, когда буквы не будет в узле, в этот момент мы добавляем элемент целиком остаток. Это полностью описывает работу функции.

Экспериментируя столкнулся с ситуацией. Например есть 2 слова
аббв
аббг
можно построить легким и неэкономным способом:
а->б->б->в|г
или сложным и быстрым для доступа
а->бб->в|г

Первый способ на каждую букву свой бинарный элемент структуры, даже если он пустой и лишь ссылается на следующую букву. При этом имея в нагрузку два указателя на списки.

Второй способ экономнее, имеет поле строку любой ширины, но при добавлении слова сложнее перестраивать список. Часть слов, которые в узле придётся перестроить, потому что вводимое слово перестраивает узел. Например добавим слово "абв" и нам придется убрать поле "бб"
а->б
->б->в|г
->с
тут надо уметь приатачить узел в|г с "бб" на "б", удалить "бб" добавив "б" и "с". В общем вроде рассуждать понятно, а вот сделать работающий алгоритм голову сломаешь.

Вот мои потуги, просто добавить слово с учётом его разбора там ещё думать и думать

Код:
Structure Tree
	s.s ; строка и снизу привязанные к ней узлы и элементы
	size.i ; размер элемента узла, а нужен ли, разве что для чтение с PeekS(), но s.s нультерминированная, поэтому size не нужен
	List item.s()   ; список остатков строки
	List node.Tree()	 ; узел (папка)
EndStructure


Procedure.q AddWord(*s.Tree, *c.Character) ; передали пустое дерево (изначально пустое) и слово
	Protected *n.Character
	If *s=0 Or *c=0 Or *c\c=0 ; проверка, дерево существует, указатель на слово существует, слово в указателе существует.
    ProcedureReturn
	EndIf
	
	If ListSize(*s\node())
    ForEach *s\node()
    	*n = @*s\node()\s ; получаем указатель на строку элемента списка узла
    	While *c\c And *n\c ; если оба ещё валидны, то делаем шаг дальше
        If *c\c <> *n\c	; если очередной символ не одинаков, то
        	Break
        EndIf
        *n\c + 2
        *c\c + 2
    	Wend
    	If *c\c = 0 And *n\c = 0
        ProcedureReturn ; слово уже есть, выпрыгиваем
    	ElseIf *c\c = 0
        ; если слово короче чем узел, то надо перестроить узел, взяв короткое слово за узел
    	ElseIf *n\c = 0
        ; если узел короче чем	слово, то добавляем остаток в item указанного node
        AddWord(*s\node(), *c.Character)
    	Else
;         Если выход по Break тогда разделяем узел
    	EndIf
    Next
	Else
    If ListSize(*s\item())
;     	Здесь должен быть алгоритм поиска возможности узла, но пока просто добавим слово
    	If AddElement(*s\item())
        *s\item() = PeekS(*c) ; считываем остаток слова
    	EndIf
    	
    	ForEach *s\item()
        *n = @*s\item() ; получаем указатель на строку элемента списка узла
        While *c\c And *n\c ; если оба ещё валидны, то делаем шаг дальше
        	If *c\c <> *n\c	; если очередной символ не одинаков, то
            Break
        	EndIf
        	*n\c + 2
        	*c\c + 2
        Wend
        If *c\c = 0 And *n\c = 0
        	ProcedureReturn ; слово уже есть, выпрыгиваем
        ElseIf *c\c = 0
        	; если слово короче чем узел, то надо перестроить узел, взяв короткое слово за узел
        ElseIf *n\c = 0
        	; если item короче чем слово, то добавляем остаток в item указанного item
        	If AddElement(*s\node())
            *s\node()\s = *s\item()
            If AddElement(*s\item())
            	*s\item() = PeekS(*c) ; считываем остаток слова
            EndIf
        	EndIf
        Else
        	;         Если выход по Break тогда разделяем узел
        EndIf
    	Next
    Else
    	If AddElement(*s\item())
        *s\item() = PeekS(*c) ; считываем остаток слова
    	EndIf
    EndIf
	EndIf
EndProcedure


Procedure View(*s.Tree, Pos) ; Отображение содержимого дерева.
	
	AddGadgetItem(0, -1, *s\s, 0, Pos)
	
	Pos+1
	
	ForEach *s\node() ; Отображение всех подузлов текущего узла
    View(*s\node(), Pos) ; Рекурсивный вызов процедуры
	Next
	
	ForEach *s\item() ; Отображение списка элементов текущего узла
    AddGadgetItem(0, -1, *s\item(), 0, Pos)
	Next
EndProcedure


If OpenWindow(0, 0, 0, 400, 400, "", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
	TreeGadget(0, 0, 0, 400, 400)
	Dictionary.Tree ; Создание пустого дерева как одного экземпляра структуры
	Dictionary\s = "узел"
	AddWord(Dictionary, @"apple")
	AddWord(Dictionary, @"banana")
	View(Dictionary, 0)
	Repeat
    Event = WaitWindowEvent()
	Until Event = #PB_Event_CloseWindow
EndIf

Отредактировано AZJIO (02.03.2025 11:05:51)

0

4

Webarion
С сортировкой дольше добавляется, так надо сортировать дерево. Это очевидно. А доступ не угадаешь, запрашиваемый символ может оказаться в начале одноуровнего списка, а может в конце. К примеру z был в начале, отсортировал и стал в конце, запрашиваешь z и он идут до конца списка, так что у тебя не обязательно узлы будут начинаться на "a" поэтому нет приоритета в какой-то сортировке, они там равнозначные. Искать ты всё равно будешь до первого попавшегося.

Webarion написал(а):

AZJIO написал(а):

    можно построить легким и неэкономным способом:
    а->б->б->в|г

Это похоже на алгоритм Trie:
а
  \
   б
     \
      б
     /  \
   в     г
В итоге, в этом варианте у дерева, 5 узлов.
AZJIO написал(а):

    или сложным и быстрым для доступа
    а->бб->в|г

Думаю, что в этом варианте, ты про Radix Tree:
  абб
  /  \
в     г

Так суть то у них одинакова, ты всё равно также добавляешь и также извлекаешь слово, а алгоритм разный. И последний выигрывает по скорости, по меньшему объёму памяти, но по более сложному алгоритму. В первом иди в цикле по одной букве и генерируй очередную стркутуру, а во втором надо перестраивать дерево (в пером нет).

0

5

Webarion написал(а):

запишет ключи на "а", в самый конец последовательности

Какова вероятность что "а" имеет приоритет? Что если отсортируешь, а у тебя постоянно нужен "z"? Он был первый, а ты его сделал последним и начинаешь при каждом запросе проходить весь список? У тебя "а" котируется только при условии что ты всегда просишь "а", но в реальности "а" не является приоритетом. У тебя ощущение что если ты отсортировал, то будет быстрее, на самом деле нет. Будет быстрее при условии что начальные буквы алфавита будут встречаться чаще, но получается как если бы ты говорил собеседнику "строй свои фразы так чтобы все слова начинались на "а", у меня так мозг быстрее работает". Максимум можешь взять какую нибудь книгу и сделать подсчёт встречаемости букв, и сортировать с этим условием. Хотя я думаю выгадаешь не много, так как гласные чаще во второй букве слова, поэтому если в первом уровне использовать один алгоритм сортировка, то во втором уровне должен уже другой. Получается надо создавать частоту встречаемости буквы в слове учитывая ещё и позицию буквы в слове.

0


Вы здесь » PureBasic - форум » OpenSource » Radix Tree AI