Страница: 1 | 2 | 3 | 4 |
|
Вопрос: Рус-яп словарь. Что выбрать для БД?
|
Добавлено: 23.06.05 10:43
|
|
Номер ответа: 18 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #18
|
Добавлено: 26.06.05 18:54
|
А для чего все эти навороты: хэш-таблицы, методы BinarySearch и Sort какого-то левого класса?
Если статей в словаре 500000, то просто держать в файлах указателей сами слова и соответствующие номера статей, разбитые по алфавиту: одна буква (первая) - один файл. Так снижается требования к памяти (грубо говоря, в 33 раза меньше).
Допустим абрикос: взяли первую букву - а, открыли файл "a.txt", бинарным поиском нашли слово и открыли статью, соответствующего номера.
Какие тут могут быть проблемы? Чаще всего в русском языке встречается буква "о", ~20%, значит максимум в одном файле будет порядка 100000 записей -> элементарным поиском это всего 16 сравнений, и запись найдена.
Добавление записей производить сразу в нужное место - в алфавитном порядке, и никаких Sort не нужно будет. При добавлении пользоваться тем же методом, что и при поиске строки. Изначально всё будет сортировано.
А хэш - это типа навороты для верблюда
Сделать нормальную хэш-функцию, без коллизий, сложно, да и вряд ли с ней будет работать быстрее, чем простое и банальное сравнение десятка строк.
Можно несколько "заголовочных" файлов объединить в один, если пугает 33 файла Например а - е, ж - м, н - о, п - с, т - я. Пять файлов - немного.
Имхо, никакая суперпупер база и никакой суперпупер класс не сработают быстрее обычного текстового файла, обслуживаемого простым и эффективным алгоритмом. И памяти жрать меньше будет.
Ответить
|
Номер ответа: 19 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #19
|
Добавлено: 26.06.05 19:01
|
Что касается памяти, то просто посчитать, приняв среднюю длину строки за 10 букв + 10 на номер статьи и разные побочные дела, получается если пять файлов то грубо говоря, 20 * 100000 = 2 Мб
Ответить
|
Номер ответа: 23 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #23
|
Добавлено: 27.06.05 11:34
|
Ты запускать свой сервер будешь дольше, чем я найду нужный файл и нужную строку в файле и покажу соответствующую статью
Ответить
|
Номер ответа: 25 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #25
|
Добавлено: 27.06.05 17:58
|
Предпологается, что серверу тут не место. Читай ответ номер пять.
Ответить
|
Номер ответа: 29 Автор ответа: Ruslan_x
Вопросов: 7 Ответов: 41
|
Профиль | | #29
|
Добавлено: 27.06.05 19:55
|
Уважаемые участники форума, огромное аригато из далекой Японии за ваши ответы.
Чувствую, что нужно прояснить несколько моментов.
Сделай индексы так: две таблицы типа хэш-статья (для русского и японского), отсортируй по хэшам, затем binary search (aka b-tree) хэшируй запрошенное слово и сравнивай полученный хэш с серединой списка хэшей. Если больше, рассматривай вторую половину, меньше - первую, угадал - радуйся и так, пока не найдешь нужную статью. Нашел - выводишь. А 50-60 метров очень жирно. Пусть 16 байт на хэш, 4 байта на ид статьи, 500 тыс. записей, две таблицы, максимум 20 метров.
Момент 1.
------------------
При тестировании я так и делал. Использовал NameValueCollection для хранения индекса. Однако, в конечном итоге не хотелось бы хранить индекс в памяти. Потому что таблицы нужно будет не две, а три. Третья таблица - это поиск по японскому чтению, т.е. уже получается 30 Мб.
Не хочется занимать оперативку и замедлять загрузку программы. Таким образом остается одно - хранить индекс на диске.
-------------------
Используйте класс HashTable, он полностью подходит под вашу задачу.
Момент 2.
-------------------
Мне кажется, что HashTable в виде индекса для этого проекта не совсем подходит. Потому что ключи не являются уникальными. Другими словами, на слово "абрикос" может быть не одна статья, а две, три или вообще десять.
Насколько мне известно, в VB.NET единственный вариант хэш-таблицы, позволяющий иметь несколько одинаковых ключей - это NameValueCollection.
А если очень хочется использовать бинарный поиск, то используйте для хранения ключей класс ArrayList: его методы Sort и BinarySearch позволят отсортировать элементы и провести бинарный поиск.
Проблема не в том, что я не могу сделать поиск. Поиск я могу сделать - если индекс находится в памяти в виде того же ArrayList. Проблема в том, как сделать быстрый поиск, если индекс хранится в виде файла на диске. И загружать весь индекс в память я не хочу.
Если статей в словаре 500000, то просто держать в файлах указателей сами слова и соответствующие номера статей, разбитые по алфавиту: одна буква (первая) - один файл. Так снижается требования к памяти (грубо говоря, в 33 раза меньше).
To HOOLIGAN:
Очень интересная идея, но все же не хотелось бы плодить кучу файлов. Даже если их будет 5-6, то для это ж для каждого из 3-х индексов...
Допустим абрикос: взяли первую букву - а, открыли файл "a.txt", бинарным поиском нашли слово и открыли статью, соответствующего номера.
"Открыли файл" - это значит полностью загрузили его в память? Если загружать файлы в память, то можно просто тот же индекс в виде NameValueCollection десериализовать с диска.
Заковыка в том, что не хочется ничего лишнего в оперативку грузить, хочется все сделать с помощью десятка-другого обращений к файлам на диске.
Хотя по большому счету, меня волнуют вовсе не эти технические детали, а основные требования:
- чтобы программа быстро грузилась
- чтобы быстро находила нужную запись
- чтобы не занимала много места в оперативке
Sharp:
Ты файл с первой буквой будешь дольше открывать, чем я проделаю все операции: и хэширование, и бинарный поиск, и нахождение статьи
-----------
HOOLIGAN:
Ты запускать свой сервер будешь дольше, чем я найду нужный файл и нужную строку в файле и покажу соответствующую статью
Похоже, это один из тех случаев, когда в споре рождается истина.
Честно говоря, очень хотелось бы протестировать оба подхода, чтобы докопаться, чья точка зрения вернее. Но это, к сожалению, нереально. Наверняка, каждый подход имеет свои плюсы и минусы.
Мне так кажется, что если вести речь о сервере, на котором крутится программа, то подход Sharp, возможно, лучше. Но для самостоятельной программы мне больше по душе подход HOOLIGAN.
Сказать по правде, после того, как я сделаю программу, то хотелось бы также сделать доступ к словарной базе через Web-сайт.
Нагрузка там вряд ли будет большой. Я так думаю, что максимум где-то 1-2 клиента в секунду.
Было бы неплохо, конечно, использовать один и тот же механизм и на сайте и в программе. Но пока что главый приоритет - это отдельная программа.
**********************
Пока что я пришел к вот к какому решению, которое мне кажется далеко не идеальным. Буду признателен за замечания и критику.
Итак, в отдельном файле хранятся записи.
Назовем его "IC.DAT".
Для простоты возьмем только один индекс по русским словам.
Вот он:
--------------
абрикос - 0030
арбуз - 0045
...
яблоко - 8794
---------------
Цифра - это местоположение записи в основном файле "IC.DAT".
Как я уже говорил выше, одному ключу-слову может соответствовать не одна, а несколько записей. Другими словами:
---------------
абрикос - 0030
арбуз - 0045
арбуз - 0048
арбуз - 0053
арбуз - 0129
....
или
абрикос - 0030
арбуз - 0045,0048,0053,0129
...
---------------
В качестве индексного файла "IC.IDX" я хочу использовать Random Access файл, который предварительно отсортирован по русскому слову.
В предыдущем посте я спрашивал, как конкретно реализовать Binary search. Долгие поиски в инете меня привели к решению. Привожу его здесь - может кому пригодится. Думаю, для большинства тут нет ничего нового, но для нас, начинающих, такие куски кода очень нужны.
Вообще-то это binary search для массива, а не для файла, но переделать его не сложно.
++++++++++++++++++++++++++++++
Sample List:
1. Alex
2. Dave
3. Dan
4. Dylan
5. Jason
6. Kyle
7. Matt
8. Saroja
To code a user-defined function in VB that returned the index of a name in an array of names, we could write:
private function findName(byref names() as string, byval seekName as string) as integer
dim found as boolean
dim lowIndex as integer
dim highIndex as integer
dim middleIndex as integer
found = false
lowIndex = 0
highIndex = Ubound(names)
middleIndex = int((highIndex + lowIndex)/2)
do while (NOT found and lowIndex < highIndex)
if seekName = names(middleIndex) then
found = true
elseif seekName > names(middleIndex) then
lowIndex = middleIndex + 1
middleIndex = int((highIndex + lowIndex)/2)
else
highIndex = middleIndex - 1
middleIndex = int((highIndex + lowIndex)/2)
end if
loop
if found = true then
findName = middleIndex
else
findName = -1
end if
end sub
++++++++++++++++++++++++++++++
Проблема с Random Access файлом в том, что все записи должны быть одинаковой длинны.
Скажем, 20 символов на слово и 4 - на смещение. Т.е. мы не сможем сделать:
-------------
арбуз - 0045,0048,0053,0129
-------------
Таким образом наш арбуз будет многократно повторяться:
-------------
арбуз - 0045
арбуз - 0048
арбуз - 0053
арбуз - 0129
-------------
Вот это "повторение арбуза" мне не нравится. И вот почему. С помощью binary search я найду первый арбуз в списке. А как мне найти остальные, которые могут быть выше или ниже его? Нет, конечно, можно написать дополнительный код, который это все сделает, но это - дополнительные тормоза.
Единственное, что мне пришло на ум - это ввести дополнительный индекс. В виде Binary Access файла "IC.ID2".
И в основном индексе "IC.IDX" указывать на то место в этом дополнительном индексе "IC.ID2", где хранятся смещения основного файла "IC.DAT".
Таким образом наши несколько арбузов становится одним:
-------------
абрикос - 0204
арбуз - 0218
банан - 0267
-------------
Смещение арбуза "0218" показывает на то место в "IC.ID2", где хранятся все его смещения в основном файле "0045,0048,0053,0129".
Думаю, нужно давать медаль тем, кто дочитал до этого места.
Короче, этот подход плодит два индексных файла вместо одного и это, похоже, единственный недостаток, который мне бросается в глаза.
Как это все сделать лучше - я не знаю. Буду признателен за любые комментарии, замечания и критику.
----------------
Вопросы к HOOLIGAN:
Ну дык а я о чём говорю С той лишь разницей, что не упомянутые таблицы хранить, а просто саму строку, благо они короткие. И не морочиться с хэшами/таблицами.
"...а просто саму строку"
Какую "строку" вы имеете в виду?
1) Саму запись из словаря типа "вкусный арбуз - OISHII SUIKA"
2) Индексную информацию "арбуз - 0045"
Более того, вообще не грузить его в память ReadFile'ом, а сделать MapViewю
Не могли бы вы в двух словах объяснить про MapView?
Ответить
|
Номер ответа: 30 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #30
|
Добавлено: 28.06.05 00:44
|
Начнем с начала, нет, лучше с конца
На конце у нас MapView: Это такой способ получения информации из файла, при котором файл целиком не грузится в оперативную память, только одна запись, которую считали в данный момент. Плюс - не надо памяти для открытого файла, каких бы он размеров не был. Обычно чем больше файл, тем эффективней MapViewOfFile и менее эффективен способ открытия через ReadFile. Минус MapViewOfFile - скорость ограничена вращением винта. Минус ReadFile'а - нужна память.
Поэтому можно посоветовать такую схему:
Если у тебя несколько индексов, описывающих статьи про арбуз по такому принципу:
а -> арбуз -> красный -> сладкий -> 00001
незрелый -> 00002
сочный -> 00003
круглый -> зеленый -> 00004
полосатый -> 00005
с хвостиком -> 00006
абрикос -> оранжевый -> и т.д.
б -> банан ->
барабан -> и т.д.
баран ->
то лучше всего держать в самой быстрой части, т.е. считанной в память, только те индексы, которые старшие в схеме:<а-я> + <арбуз-яблоко>. Чтобы можно было быстро отсечь максимум ненужного. Если условно принять, что буквы равновероятно встечаются в алфавите, то взяв индекс <а>, отсекли от зоны поиска 97% объёма. Если у тебя всего миллион статей, то уже после первого индекса осталось всего 30 000. Но и эта цифра пусть не пугает, т.к. индексов, следующих за головными, не так уж и много: Если 30 000 статей на одну букву <а>, и каждое слово может иметь к примеру три статьи, то 10 000 слов для одной буквы. Это уже совсем по-детски. И так далее.
Насколько позволит память, желательно побольше индексов старшего ранга держать в оперативе. Если держишь всего один индекс - головной, то оперативы нужно всего 33 строки, указывающих на 33 файла со списками слов. Т.е. 33 строки вида
"a<a.txt>" - "я<я.txt>" Или всего 264 байта. Это вся оперативная память, что тебе нужна. И не надо пугаться 33 файлов. Наоборот, это твой козырь в погоне за скоростью - отсечь от зоны поиска как можно больше файлов. Если файл один - выбора нет - придется рыскать по всему файлу одним объёмом, если файлов 5 - будешь искать только в 20% общего объёма. Если 33 - в 3%.
В общем схема такая:
Один файл с 33 строками, зачитал его в память и держишь, чтобы не вырвался и не убежал.
На запрос <арбуз красный сладкий> реагируешь так: получаешь первую букву "а", и находишь среди 33 строк такую, которая начинается с этой буквы.
Открываешь файл, соответствующий этой букве.
В открытом файле ищешь строки начинающиеся на "арбуз", их в нашем случае 6. 50 байт в среднем на строку хватит с запасом. Если для каждого слова взять по 6 определений, то 10000х6х50= 3 Мб на файл для одной буквы. Или 100 Мб места на винте под 33 файла. Думаю, что в твоём случае ни миллиона статей, ни по 6 статей на каждое слово не будет, поэтому можно принять это как максимальное значение, которого тебе хватит на все случаи и с запасом.
Найдя в файле строку "арбуз красный сладкий 0001", ты имеешь номер статьи - 00001. Дальше делай с ним что хочешь, статья найдена.
Имеется один головной файл размером 264 байта, 33 файла по 3 Мб максимум и огромный файл с твоими собственно конечными статьями (или несколько поменьше, непринципиально). Причём, вся эта масса не грузится в память, а спокойно валяется на винте.
Сколько нужно времени на то, чтобы найти конкретную статью?
Первый файл - 33 строки - максимум 6 сравнений строк (2^6>33)
Второй файл - 10000 строк - 14 сравнений (2^14>10000).
Время на открытие 2 файла размером 3 Мб - миллисекунды. Даже ReadFile'ом.
Время на открытие последнего огромного файла со статьями - открывать при помощи MapViewOfFile, не читая в памяти - миллисекунды.
А номер статьи 00001 пусть будет смещением от начала файла, на котором начинается твоя конкретная статья. Вызов одной апи SetFilePointer сразу установит указатель чтения на нужный адрес в файле. Зачитал статью с этого адреса и всё.
Много ли времени надо, чтобы сравнить 20 строк? Хватит ли на это одной секунды? Могу сказать, что за 5 секунд можно не просто сравнить, а считать в память ReadFile'ом, отсортировать и сохранить в сортированном виде на винт файл размером 50 Мб из 200 000 строк. Не особо напрягаясь. А количество сравнений строк, и перестановок, которое при этом производится, измеряется многими миллионами.
Думаю за 1 секунду можно найти статью.
Главное - это не использование какого-нибудь ультрамодного и разрекламированного средства типа .net или скулосервера. Главное чётко представлять себе организацию данных, которые предстоит обрабатывать. И тогда простейший алгоритм будет работать быстрее любого другого, навороченного и не заточенного под конкретную задачу.
Теперь о шести арбузах: нечего их бояться Записи будут добавляться по одной, поэтому при добавлении производишь точно такой же поиск, как при нахождении статьи, и найдя соседнюю пару строк, одна из которых меньше (по алфавиту), а другая больше, чем добавляемая, впихиваешь добавляемую между ними. Добавление не будет происходить ежесекундно, тут тем более некритична скорость. И твои данные будут автоматом в сортированном порядке, позволяющем вести обычный бинарный поиск, как самый эффективный в данном случае.
Последовательность маппирования файла это вызов следующих апи:
CreateFile->CreateFileMapping->MapViewOfFile.
В msdn всё это расписывается, наверняка и на vb найдёшь кучу примеров как использовать.
Приведенная схема весьма проста, если есть желание, можно её усложнить, чтобы оптимизировать работу программы.
И везде, где есть смысл, используй указатели как смещение от начала файлов, в котором предстоит искать.
Те части, на которые ссылка идёт не абсолютная (как смещение), а просто как на имя файла, где искать, должны быть сортированы. Например если ищем арбуз, то в головном файле нет указателя на строку, содержащую арбуз, есть только имя файла. Поэтому, чтобы к этому файлу можно было применить бинарный поиск, этот файл должен быть сортирован, что достигается либо сразу добавлением записи в положенное ему по алфавиту место, либо отдельной сортировкой файла после добавления каждой новой строки. Первый вариант намного проще.
Какую "строку" вы имеете в виду?
1) Саму запись из словаря типа "вкусный арбуз - OISHII SUIKA"
2) Индексную информацию "арбуз - 0045"
Вторую строку.
По-моему, я написал ещё больше, интересно, дочитает кто-нибудь до конца или нет )
Ответить
|
Страница: 1 | 2 | 3 | 4 |
Поиск по форуму