ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Створення ефективної реалізації сортованого списку з використанням generics
         

     

    Інформатика, програмування

    Створення ефективної реалізації сортованого списку з використанням generics

    Сергій Смирнов (Serginio1)

    Так сталося, що я став програмістом 1С. Всі чудово в цьому середовищі, за винятком швидкості. Цю проблему можна вирішити тільки одним способом: прямим доступом до файлів і обробкою результатів на компільовані мовою в пам'яті. Так

    , для групування даних потрібні алгоритми пошуку та вставки. І мою свідомість, обтяжене бухгалтерським обліком, не знайшло нічого кращого, ніж використовувати аналог TList (SortedList), що представляє собою динамічний масив з властивостями «ємність» та «кількість елементів».

    Упорядкованість в цьому масиві підтримується за допомогою компараторів, а при пошуку використовується алгоритм половинного поділу з пошуком потрібної позиції i по ключу з умовою (Items [i]> = Key) AND (Items [i-1]

    Але час йшов, і обсяг угруповань вийшов за 10000 записів. Мій AMD K6 200 (потужний на ті часи комп'ютер) почав працювати занадто мелений. І не дивно - кількість зрушуваної елементів у середньому стало одно N2/4, тобто 108.

    І от якось, після чергового навчання бухгалтерів бухгалтерії, прийшла думка. Навіщо тримати один великий масив, якщо можна його розбити на безліч маленьких? Сказано - зроблено. Протягом двох хвилин я створив дворівневий масив. Перший (верхній) рівень - це масив, елементами якого є посилання на масиви нижнього рівня. Другий з рівнів (нижній) по суті, складається з простих динамічних масивів. Під простими розуміється те, що пам'ять під них виділяється заздалегідь і згодом не перепозичати. Фактично цей масив являє собою структуру, що зберігає лічильник елементів і масив пар «ключ-значення». Надалі я буду називати ці динамічні масиви листовими сторінками (LeafPage).        

    PLeafPage = ^ TLeafPage;   

    TLeafPage = Record   

    // кількість задіяних елементів у   масиві KeyItems   

    Count: Integer;   

    // масив посилань на пари «ключ-значення»   

    KeyItems: Array [0 .. 63] of Tobject;   

    End;      

    TLeafPageArray = Array of PleafPage;   

    LeafPageArray: TLeafPageArray;     

    Пошук проходив в 2 етапи. Спочатку проводиться пошук масиву, в якому може знаходитися шуканий ключ. Для цього з потрібним значенням порівнюються значення ключів нульових елементів масивів KeyItems з таким умовою, щоб значення ключа нульового елемента масиву було менше або дорівнювало шуканого, а значення нульового елемента наступного масиву перевищувало шукане.

    Це можна виразити так:        

    (LeafPageArray [j]. KeyItems [0] <= Key)   

    AND   (LeafPageArray [j +1]. KeyItems [0]> Key)     

    Алгоритм пошуку на нижньому рівні аналогічний пошуку в одновимірному масиві. При повному заповненні KeyItems виділяється новий PLeafIPage, в який копіюється половина даних. Посилання на новий масив вставляється в масив LeafPageArray в позицію на 1 більше поточної. При цьому кількість коду було менше 100 рядків.

    Такий підхід дозволив різко скоротити обсяг копійований пам'яті - тому що кількість копійованих елементів ніколи не перевищує 64. Тим спосіб вдалося уникнути уповільнення роботи масиву при його зростанні.        

    ПРИМІТКА   

    І не дивно, бо кількість   переносите елементів стало одно (N/64) * 642/4 + (N/64) 2/4 = N * k   / 4 + (N/k) 2/4. Тут до - ємність сторінки, але враховуючи, що сторінки   заповнюються не повністю, сміливо можна скласти приблизну формулу   розрахунку загальної кількості операцій копіювання: N * k/2 + (N/k) 2/2,   оптимальне значення До буде K (N) = (2N) -3, і відповідно, 643 - цілком   прийнятний розмір сторінки для зберігання даних в цьому класі. Ставлення   кількості копійованих елементів в одновимірному масиві до дворівневої склало   N/(k + N/k2)/2. У будь-якому випадку це ставлення дуже велике. Єдиний   мінус цього алгоритму в уповільненні пошуку, так як доступ до ключа   проводиться через додаткову посилання. Для виправлення цього недоліку   досить включити нульовий елемент KeyItems в структуру батьківського   масиву.             

    TNodeItem = Record   

    Key: Tobject;   

    LeafPage: PLeafPage;   

    End;      

    TNodeArray = Array of TNodeItem;             

    ПРИМІТКА   

    Таким чином, при пошуку   потрібної листовий сторінки Вам не треба звертатися до її вмісту:   

    (NodeArray [j]. Key <= Key) AND (NodeArray [j + 1]. Key> Key)   

    Таким чином можна вбити   відразу двох зайців - зберегти швидкість пошуку і різко збільшити швидкість   вставки.       

    B +-дерева

    Коли обсяг угруповань почав підходити до мільйонів записів, цей алгоритм почав «гальмувати» через збільшення розміру масиву верхнього рівня. Проблеми з копіюванням великих обсягів даних повернулися. Щоб позбутися цієї проблеми, що можна застосувати той же самий механізм, і розбити масив верхнього рівня на кілька подмассівов. Це призведе до створення трирівневого масиву, а коли-небудь, можливо, і чотирьохрівневий. Так що в принципі є резон відразу створювати універсальний алгоритм, автоматично збільшує кількість рівнів і що будує дерево. Структура цього дерева включає сторінки двох типів - вузлові, що містять масиви посилань на нижележащие сторінки, і листові, що містять відсортовані списки даних. Таке дерево називається B +-деревом. Однак розбирати докладно реалізацію B +-дерев у цій статті я не буду.

    Реалізація дворівневого масиву

    На практиці в більшості випадків достатньо дворівневих масивів. До того ж, їх набагато простіше описувати. Вони використовують ті ж підходи, що й в Б +-деревах. Так що розглянемо реалізацію саме дворівневих масивів.

    Спочатку потрібно визначити структуру, яка буде зберігати пари ключ + значення (для листових сторінок) і ключ + посилання на сторінку (для вузлових сторінок). У принципі, посилання можна розглядати як приватний випадок даних. Так що за допомогою generic-ів можна описати єдину структуру. Ось ця структура:        

    internal struct KeyValuePair   

    (   

    internal K Key;      

    internal V Value;      

    public KeyValuePair (K key, V   value)   

    (   

    Key = key;   

    Value = value;   

    )   

    )     

    Визначимо клас PageBase, з єдиним полем Count.        

    internal   class PageBase   

    (   

    public int Count;   

    )     

    Опис сторінки, що знаходиться на нульовому рівні:        

    internal class LeafPage : PageBase   

    (   

    public KeyValuePair [] PageItems;// масив елементів   

    public LeafPage   PriorPage;// посилання на попередню сторінку   

    public LeafPage NextPage;// посилання на наступну сторінку      

    public LeafPage ()   

    (   

    Count = 0;   

    PageItems = new   KeyValuePair [BTConst.MaxCount];   

    )   

    )     

    PriorPage, NextPage потрібні для навігації по дереву.

    Основну функціональність дворівневого масиву реалізує клас TwoLevelSortedDictionary:        

    using Generic = System.Collections.Generic;      

    public class   TwoLevelSortedDictionary : Generic.IDictionary   

    (   

    internal LeafPage   CurrentLeafPage;// Поточна сторінка з даними   

    internal struct NodeItem// Структура елементів верхнього рівня   

    (   

    internal K Key;   

    internal   LeafPage ChildPage;   

    )   

    internal NodeItem []   NodeArray;// Масив елементів 2 рівня   

    internal int _pageCount;// Кількість сторінок 1   рівня   

    internal int _currentPageIndex;//   Поточний індекс елемента в масиві 2 рівня   

    internal int _currentElementIndex;// Поточний індекс у CurrentLeafPage   

    internal   Generic.IKeyComparer _comparer;// Користувацький компаратор   

    internal int _count;// Кількість елементів в об'єкті   

    bool _selected;// Обрано чи елемент   

    internal int version = 1;// Потрібен для перечіслітелей   

    public   TwoLevelSortedDictionary (Generic.IKeyComparer Comp)   

    (   

    this._comparer = Comp;   

    CurrentLeafPage = new   LeafPage ();// Виділяємо сторінку 1 рівня   

    _pageCount = 1;   

    )     

    Дворівневий масив дозволяє здійснювати навігацію по знаходилися в ньому елементів. При цьому виникає поняття поточного елемента, у якого можна зчитувати або встановлювати значення, і читати значення ключа. Для позиціонування на запис з деяким ключем використовується функція NavigateKey.

    Алгоритм роботи цієї функції такий. Оскільки інформація в масиві завжди упорядкована, то пошук можна здійснювати за допомогою алгоритму бінарного пошуку (тобто половинного поділу). Єдина проблема, що не дозволяє використовувати класичний алгоритм прямо - це те, що, що масив складається з двох рівнів. Тому алгоритм пошуку розділяється на два етапи. На першому етапі перевіряється, чи є масив верхнього рівня. Якщо є, то в ньому шукається сторінка, на якій може знаходитися шуканий елемент. Якщо масиву верхнього рівня немає, як сторінки, на якій буде проводитися подальший пошук, використовується єдина існуюча сторінка.

    На другому етапі проводиться класичний бінарний пошук по ключу в сортованого масиві.        

    public bool NavigateKey (K key)   

    (   

    // Встановлюємо індекс елемента в 0.   

    _currentElementIndex = 0;   

    // Якщо є другий рівень ...   

    if (_pageCount> 1)   

    (   

    // перебираємо межі   

    int hi = _pageCount - 1;      

    int lo = 0;      

    while (lo <= hi)   

    (   

    int i = (lo + hi)>> 1;   

    int result =   _comparer.Compare (NodeArray [i]. Key, key);      

    if (result <0)   

    lo = i + 1;   

    else   

    (   

    hi = i - 1;   

    if (result == 0)   

    (   

    // Ключ знайдений на 2 рівні.   Встановлюємо поточну   

    // сторінку CurrentLeafPage.   

    _currentPageIndex = i;   

    CurrentLeafPage =   NodeArray [_currentPageIndex]. ChildPage;   

    _selected = true;   

    return true;   

    )   

    )   

    )      

    // Ключ не знайдено на 2 рівні.   

    // Перевіряємо на можливість того, що   шуканий ключ -   

    // найменший з наявних в об'єкті.   

    if (hi <0)   

    (   

    // Даний ключ менше найменшого   зберігається ключа.   

    // Встаємо на самий перший елемент   дворівневого масиву   

    _currentPageIndex = 0;   

    CurrentLeafPage =   NodeArray [_currentPageIndex]. ChildPage;   

    _selected = false;   

    // Повертаємо інформацію про те, що   ключ не знайдено.   

    return false;   

    )   

    else   

    (   

    // Даний ключ потрапляє в діапазон   ключів нижележащий сторінки.   

    // Змінюємо поточну сторінку CurrentLeafPage   на знайдену дочірню   

    // сторінку   

    CurrentLeafPage =   NodeArray [_currentPageIndex]. ChildPage;   

    // Встановлюємо поточний індекс ключа на листовий   сторінці в 1,   

    // тому що 0 вже перевіряли.   

    _currentElementIndex = 1;   

    )   

    )      

    // Намагаємося знайти індекс шуканого ключа або   індекс, в якому він повинен   

    // був знаходитися.   

    hi = CurrentLeafPage.Count - 1;   

    lo = _currentElementIndex;      

    while (lo <= hi)   

    (   

    int i = (lo + hi)>>   1;   

    int result =   _comparer.Compare (CurrentLeafPage.PageItems [i]. Key, key);      

    if (result <0)   

    lo = i + 1;   

    else   

    (   

    hi = i - 1;   

    if (result == 0)   

    (   

    // Знайшли!   

    _currentElementIndex =   i;   

    _selected = true;   

    return true;   

    )   

    )   

    )      

    // Не знайшли ...   

    _selected = false;   

    // Поміщаємо в   _currentElementIndex позицію в яку   

    // можна додати елемент з потрібним ключем.   

    _currentElementIndex = lo;   

    return false;   

    )      

    // Процедура вставки в поточну   позицію   

    private void Insert (K Key)   

    (   

    // Вставляємо ключ в поточну позицію,   розширюючи тим самим масив на 1 елемент.      

    // Зрушуємо елементи, щоб звільнити   місце для вставляється.   

    Array.Copy (CurrentLeafPage.PageItems,   _currentElementIndex,   

    CurrentLeafPage.PageItems,   _currentElementIndex + 1,   

    CurrentLeafPage.Count --   _currentElementIndex);   

    // Збільшуємо кількість збережених елементів на сторінці та вставляємо   ключ.   

    CurrentLeafPage.Count ++;   

      CurrentLeafPage.PageItems [_currentElementIndex]. Key = Key;   

    if (_currentElementIndex == 0)   

      NodeArray [_currentPageIndex]. Key = key;      

    version + +;// Відбулися зміни, збільшуємо поточну версію.      

    //   

    // Якщо поточна сторінка листова повністю   заповнена,   

    // то є 2 варіанти. Можна або   перенести елемент з поточною   

    // сторінки на сусідню, або розбити   сторінку на 2.   

    //      

    if (CurrentLeafPage.Count ==   BTConst.MaxCount)   

    (   

    // Сторінку повністю заповнена.      

    // Якщо другого рівня немає ...   

    if (_pageCount == 1)   

    (   

    // ... то створюємо другий рівень.   

    //      

    // Для цього ділимо поточну сторінку   навпіл ...   

    LeafPage NewPage   = New LeafPage ();   

    // ... виправляємо посилання в полях NextPage і PriorPage   

    // щоб можна було здійснювати наскрізну   навігацію   

    // по листовим сторінок.   

    CurrentLeafPage.NextPage = NewPage;   

    NewPage.PriorPage = CurrentLeafPage;      

    // переміщаємо половину елементів   вихідного масиву в новий.   

    Array.Copy (CurrentLeafPage.PageItems,   BTConst.MidlCount,   

    NewPage.PageItems, 0,   BTConst.MidlCount);   

      Array.Clear (CurrentLeafPage.PageItems, BTConst.MidlCount,   BTConst.MidlCount);      

    // Створюємо масив другого рівня і поміщаємо в нього   посилання   

    // на листові сторінки.   

    NodeArray = new   NodeItem [BTConst.MaxCount];   

    _pageCount = 2;// Тепер сторінок два.   

    NodeArray [0]. Key =   CurrentLeafPage.PageItems [0]. Key;   

    NodeArray [0]. ChildPage =   CurrentLeafPage;   

    NodeArray [1]. Key =   NewPage.PageItems [0]. Key;   

    NodeArray [1]. ChildPage = NewPage;      

    // Задаємо кількість елементів на   сторінках.   

    CurrentLeafPage.Count =   BTConst.MidlCount;   

    NewPage.Count =   BTConst.MidlCount;      

    // Якщо поточний елемент перемістився на нову   сторінку ...   

    if (_currentElementIndex> =   BTConst.MidlCount)   

    (   

    // Змінюємо значення поточної сторінки   на нове ...   

    CurrentLeafPage = NewPage;   

    // ... та поточного індексу на ній.   

    _currentElementIndex -=   BTConst.MidlCount;   

    )   

    )   

    else   

    (   

    // Якщо другий рівень вже існує.      

    // Якщо є сторінка ліворуч від   поточної ...   

    LeafPage LeftPage   = CurrentLeafPage.PriorPage;   

    if (LeftPage! = null)   

    (   

    // ... і вона заповнена менш, ніж на   MaxFill (3/4 )...   

      if (LeftPage.Count <= BTConst.MaxFill)   

    (   

    // можна перекинути значення на   ліву сторінку.      

    // Знаходимо потрібне колічесво   елементів для перекидання.   

    int MoveCount =   (BTConst.MaxCount - LeftPage.Count)/2;      

    // переміщаємо початкові елементи з поточної сторінки      

    // в кінець лівої сторінки ...   

      Array.Copy (CurrentLeafPage.PageItems, 0,   

    LeftPage.PageItems,   LeftPage.Count, MoveCount);   

    // І Зрушуємо залишилися елементи сторінки в початок.   

    Array.Copy (CurrentLeafPage.PageItems,   MoveCount,   

      CurrentLeafPage.PageItems, 0, CurrentLeafPage.Count - MoveCount);   

    // затираємо переміщені елементи.   

      Array.Clear (CurrentLeafPage.PageItems,   

    CurrentLeafPage.Count - MoveCount, MoveCount);      

    // Так як нульовий елемент на   сторінці змінився, необхідно   

    // відкоригувати значення ключа,   що посилається на цю сторінку   

    // в масиві верхнього рівня.   

    // Виправляємо значення ключа у верхньому   рівні так, щоб його   

    // значення було рівним значенню   ключа нульового елементу   

    // відповідної листовий   сторінки.   

    NodeArray [_currentPageIndex]. Key =   CurrentLeafPage.PageItems [0]. Key;      

    // Поточний ключ був переміщений.   

    // Якщо він перемістився на ліву   сторінку, змінюємо значення   

    // поточної сторінки і поточного   індексу на неї так, щоб вони   

    // вказували на вставлений ключ.   

    if   (_currentElementIndex   

    (   

    _currentElementIndex   + = LeftPage.Count;   

      CurrentLeafPage.Count -= MoveCount;   

    LeftPage.Count + =   MoveCount;   

    CurrentLeafPage =   LeftPage;   

    )   

    else   

    (   

    _currentElementIndex   -= MoveCount;   

      CurrentLeafPage.Count -= MoveCount;   

    LeftPage.Count + =   MoveCount;   

    )      

    return;   

    )   

    )      

    // Якщо з лівого сторінкою не   вийшло, спробуємо з правого.   

    // Код цього кроку аналогічний   вищенаведеного.   

    // Його можна знайти у файлі,   супроводжує статтю.      

    ...      

    // Не вийшло перекинути елементи   на сусідні сторінки,   

    // так як вони заповнені.      

    // Виділяємо нову сторінку аналогічно   тому, як це робилося вище,   

    // При виділенні верхнього рівня, за   винятком того, що потрібно   

    // скорегувати посилання на   прилеглі листові сторінки.   

    // Цей код пропущений для стислості.      

    ...      

    // Вставляємо посилання на нову сторінку в   масив верхнього рівня      

    Array.Copy (NodeArray,   _currentPageIndex + 1, NodeArray,   

    _currentPageIndex + 2,   _pageCount - _currentPageIndex - 1);   

      NodeArray [_currentPageIndex + 1]. Key = NewPage.PageItems [0]. Key;   

      NodeArray [_currentPageIndex + 1]. ChildPage = NewPage;   

    CurrentLeafPage.Count =   BTConst.MidlCount;   

    NewPage.Count = BTConst.MidlCount;      

    // Перевіряємо поточну позицію   вставляється елементу (код пропущено)      

    ...      

    // Якщо масив верхнього рівня   повністю заповнений просто збільшуємо   

    // його ємність у 2 рази (в Б + деревах у   цьому випадку вибудовується   

    // новий рівень).   

    if (_pageCount ==   NodeArray.Length)   

    (   

    NodeItem [] NewNodeArray   = New NodeItem [2 * _pageCount];      

    Array.Copy (NodeArray, 0,   NewNodeArray, 0, _pageCount);   

    NodeArray = NewNodeArray;   

    )   

    )   

    )   

    )     

    Процедуру видалення елемента з дворівневого масиву я тут наводити не буду. Її код можна знайти в файлі, доданому до статті. Більший інтерес представляє функція пошуку індексу в дворівневому масиві.

    Існують ситуації, коли потрібно знайти значення, асоційоване з ключем, і якщо його немає, то вставити деяке значення, асоційоване з цим ключем. Якщо для вирішення цього завдання скористатися окремими процедурами пошуку і вставки, то загальний час подвоїться, так як процедура вставки (перед безпосереднім вставкою значення) здійснює пошук місця вставки, тобто має місце повторне (непродуктивний) виконання по суті однієї і тієї ж операції (пошуку). У принципі, якщо при пошуку запам'ятовувати знайдену позицію, то процедура вставки могла б скористатися результатами функції пошуку (за умови, що між пошуком і вставкою не було ніяких дій). Однак це призвело б до того, що користувача класу довелося допустити в нетрі реалізації даної колекції, і надійність роботи алгоритмів, побудованих за таким принципом, була б украй низка. З метою оптимізації я вирішив створити метод, який намагався б знайти ключ і, якщо не знайшов, вставляв б деяке значення «за замовчуванням» і позиціонувався на нього (а якщо знайшов, то просто позиціонувався). Це дозволяє позбутися від зайвої операції пошуку в описаних вище випадках, тому що після цієї операції користувач отримує можливість працювати з поточним значенням (значенням, на яке відбулося позиціонування). При цьому для читання чи модифікації значення не потрібно проводити повторний пошук. Функцію, що виробляє позиціонування (або вставку і позиціонування), я вирішив назвати NavigateOrInsertDefault. Ось її код:        

    public bool NavigateOrInsertDefault (K Key)   

    (      

    bool result =   this.NavigateKey (Key);      

    if (! result)   

    (   

    // Немає такого елемента. Вставляємо в   поточну позицію.   

    Insert (Key);      

    // Поміщаємо в поточну позицію значення по   замовчуванням.   

    CurrentLeafPage.PageItems [_currentElementIndex]. Value   = V.default;   

    _count ++;   

    )   

      

    // Стоїмо на потрібній позиції.   

    _selected = true;   

    return result;   

    )     

    Крім точного позиціонування, можна виробляти позиціювання на елемент, ключ якого більше, менше, більше або дорівнює і менше або дорівнює деякому значенню. Цим займається функція Navigate. У Як параметри вона отримує значення ключа і варіант пошуку. Тип пошуку задається наступним перерахуванням:        

    public   enum NavigateFlag: byte   

    (   

    Eqality,// ==   

    LessThan,// <   

      GreaterThan,//>   

      LessThanOrEqval,// <=   

      GreaterThanOrEqval//> =   

    )     

    А от реалізація цієї функції:        

    public bool Navigate (K Key, NavigateFlag flag)   

    (   

    bool result =   this.NavigateKey (Key);      

    switch (flag) (   

    case NavigateFlag.Eqality:   

    return result;   

    case NavigateFlag.GreaterThanOrEqval:   

    if (result)   

    return true;   

    goto case   NavigateFlag.GreaterThan;   

    case NavigateFlag.GreaterThan:   

    if (result)   

    _currentElementIndex ++;      

    if (CurrentLeafPage.Count   == _currentElementIndex)   

    (   

    if   (CurrentLeafPage.NextPage == null)   

    (   

    _selected = false;   

    return false;   

    )   

    else   

    (   

    CurrentLeafPage =   CurrentLeafPage.NextPage;   

    _currentElementIndex =   0;   

    )   

    )   

      

    _selected = true;   

    return true;   

    case   NavigateFlag.LessThanOrEqval:   

    if (result)   

    return true;   

    goto case   NavigateFlag.LessThan;   

    case NavigateFlag.LessThan:   

    return   this.GetPriorRecord ();   

    )      

    return result;   

    )     

    Ось, загалом-то, і все. За швидкістю пошуку дворівневий масив перевершує свого більш могутнього побратима (Б +-дерево), але за вставці починає програвати десь в районі мільйона елементів (а може, і раніше, якщо зберігаються значення або ключі мають великий розмір). Якщо порівнювати за тестами пошуку кількості однакових слів у тексті, то виходить така картина:        

    Кількість слів у тексті = 528124   

    Кількість слів 20359      

    Заповнення   SortedDictionary   1.53828656994115   

    Заповнення QuickDictionary   (через індексатор) 0.189289700227264   

    Заповнення Dictionary 0.309536826607851   

    Заповнення TLSD (через індексатор) 0.860960541074354   

    Заповнення QuickDictionary   (прямий доступ) 0.08363381379477   

    Заповнення TLSD (прямий   доступ)   0.368364694395517   

    Заповнення Б +-дерева   (прямий доступ)   0.461643030049909   

    Заповнення MySortedDictionary 0.984015566224199     

    «SortedDictionary» - це generic-реалізація абстракції Dictionary на базі масиву. Входить в стандартну бібліотеку. NET Framework. Більш докладно див статтю «Колекції ст. NET Framework Class Library ».

    «MySortedDictionary» - Аналог SortedDictionary, написаний мною. Відрізняється від оригіналу тим, що доступ до масиву здійснюється безпосередньо. Я привів її, щоб порівняти швидкість її роботи з тестами «TLSD (прямий доступ)» і «Б +-дерева (прямий доступ)», тому що в цих тестах також здійснюється прямий доступ до вмісту колекцій. Ці колекції відрізняються тільки використовуваними алгоритмами, і їх порівняння дозволить побачити чистий різницю між алгоритмами.

    «QuickDictionary (через індексатор)» - це моя generic-реалізація хеш-таблиці. Доступ до даних здійснюється через індексатор (реалізацію інтерфейсу IDictionary ).

    «QuickDictionary (прямий доступ)» - те ж, що й попереднє, але з прямим доступом до вмісту хеш-таблиці.

    «Dictionary» - це generic-реалізація абстракції Dictionary на базі хеш-таблиці. Входить в стандартну бібліотеку. NET Framework.

    «TLSD (через індексатор)» - TwoLevelSortedDictionary, generic-реалізація дворівневого сортованого масиву. Доступ до даних здійснюється через індексатор (реалізацію інтерфейсу IDictionary ). При вставці проводиться повторний пошук.

    «TLSD (прямий доступ)» - те ж, що і попереднє, але вставка проводиться у позицію. знайдену в процесі пошуку та доступ до вмісту колекції здійснюється прямо.

    «Б +-дерево (прямий доступ)» - generic-реалізація Б +-дерева.

    З цих тестів видно, що якщо у Влада Чистякова в статті «Колекції в. NET Framework Class Library »(з цього самого номера журналу) хеш-таблиця і Сортувати список розрізняються за швидкістю приблизно в 4 рази, то тут - аж у 16 разів. Якщо ж порівнювати Dictionary і TwoLevelSortedDictionary, то їх швидкість розрізняється менш ніж у 3 рази.

    Дерева виграють у MySortedDictionary тільки за рахунок часу вставки, тому що при вставці в MySortedDictionary здійснюється сдвіжка пам'яті, тоді як час, що витрачається на вставку в Б +-дерево і дворівневий масив, дуже мале.

    Алгоритм Б +-дерев цікавий ще й тим, що при великих обсягах швидкість пошуку в них стає навіть більше, ніж у масиві, за рахунок використання кешу процесора, куди потрапляють елементи вищих рівнів. Всі залежить від об'єму. Чим більше обсяг збережених даних, тим більші переваги дають Б +-дерева. Ну а явне їх перевага починається з обсягів, що перевищують мільйон елементів.

    Список літератури

    Для підготовки даної роботи були використані матеріали з сайту http://www.rsdn.ru/

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status