برنامه نویسی

فاخته هشویی: یک تکنیک وضوح کارآمد برخورد با O (1) زمان جستجو

نمای کلی

Cuckoo Hashing یک تکنیک پیشرفته است که برای حل و فصل برخوردهای هش استفاده می شود. بر خلاف روشهای هشویی سنتی ، که ممکن است در بدترین حالت به O (n) تخریب شود ، فاخته هشویی ، پیچیدگی زمانی بدترین حالت O (1) را برای جستجوی ، درج و حذف تضمین می کند.


مفهوم فاخته

Cuckoo Hashing هنگامی که یک تصادف در هنگام درج رخ می دهد ، از یک استراتژی “شروع به کار” استفاده می کند. از دو جدول هش جداگانه (H1 و H2) و دو عملکرد هش (F1 (X) و F2 (x)) استفاده می کند. در صورت قرار دادن یک عنصر داده D1 به H1 ، در صورت بروز برخورد ، عنصر موجود در H1 جابجا می شود (بیرون رانده می شود) و بر اساس عملکرد هش دوم به H2 منتقل می شود. این فرآیند بین H1 و H2 تکرار می شود تا زمانی که موقعیت خالی پیدا شود.

برای جلوگیری از حلقه های نامتناهی در هنگام درج ، از ثابت از پیش تعریف شده (max_hash_loop) برای محدود کردن مجدد مجدد استفاده می شود. در صورت دستیابی به این حد ، جداول هش تغییر اندازه داده می شود ، و یک عملیات مجدد انجام می شود تا ضمن حفظ پیچیدگی زمان O (1) درج ، عناصر جدید را در خود جای دهد.

ویژگی های کلیدی:

  • از دو جدول هش برای وضوح برخورد کارآمد استفاده می کند.
  • برای تعیین قرار دادن داده ها از دو کارکرد هش مجزا استفاده می کند.
  • از عملیات درج سریع ، جستجو و حذف پشتیبانی می کند.
  • مکانیسم تجدید نظر را برای رسیدگی به حلقه های درج بی نهایت پیاده سازی می کند.

اجرای فاخته هش در C ++

#include 
#include 
#include 
#include 

using namespace std;

#define TABLE_SIZE 11  // Initial hash table size
#define MAX_REHASH 10  // Maximum reinsertion attempts before rehashing

class CuckooHashTable {
private:
    vector table1, table2;
    vector occupied1, occupied2;

    int hash1(int key) { return key % TABLE_SIZE; }
    int hash2(int key) { return (key / TABLE_SIZE) % TABLE_SIZE; }

    void rehash() {
        cout << "Rehashing required!" << endl;
        vector oldTable1 = table1, oldTable2 = table2;
        vector oldOccupied1 = occupied1, oldOccupied2 = occupied2;

        int newSize = TABLE_SIZE * 2;
        table1.assign(newSize, -1);
        table2.assign(newSize, -1);
        occupied1.assign(newSize, false);
        occupied2.assign(newSize, false);

        for (int i = 0; i < oldTable1.size(); i++) {
            if (oldOccupied1[i]) insert(oldTable1[i]);
            if (oldOccupied2[i]) insert(oldTable2[i]);
        }
    }

public:
    CuckooHashTable() {
        table1.assign(TABLE_SIZE, -1);
        table2.assign(TABLE_SIZE, -1);
        occupied1.assign(TABLE_SIZE, false);
        occupied2.assign(TABLE_SIZE, false);
    }

    void insert(int key) {
        int pos1 = hash1(key);
        int pos2 = hash2(key);
        int loopCounter = 0;
        int currKey = key;

        while (loopCounter < MAX_REHASH) {
            if (!occupied1[pos1]) {
                table1[pos1] = currKey;
                occupied1[pos1] = true;
                return;
            }
            swap(currKey, table1[pos1]);

            if (!occupied2[pos2]) {
                table2[pos2] = currKey;
                occupied2[pos2] = true;
                return;
            }
            swap(currKey, table2[pos2]);

            pos1 = hash1(currKey);
            pos2 = hash2(currKey);
            loopCounter++;
        }

        rehash();
        insert(currKey);
    }

    bool search(int key) {
        return (occupied1[hash1(key)] && table1[hash1(key)] == key) ||
               (occupied2[hash2(key)] && table2[hash2(key)] == key);
    }

    void remove(int key) {
        if (occupied1[hash1(key)] && table1[hash1(key)] == key) {
            occupied1[hash1(key)] = false;
            table1[hash1(key)] = -1;
            return;
        }
        if (occupied2[hash2(key)] && table2[hash2(key)] == key) {
            occupied2[hash2(key)] = false;
            table2[hash2(key)] = -1;
            return;
        }
        cout << "Key not found!" << endl;
    }

    void display() {
        cout << "Table 1: ";
        for (int i = 0; i < TABLE_SIZE; i++)
            cout << (occupied1[i] ? to_string(table1[i]) : "-") << " ";
        cout << "\nTable 2: ";
        for (int i = 0; i < TABLE_SIZE; i++)
            cout << (occupied2[i] ? to_string(table2[i]) : "-") << " ";
        cout << "\n";
    }
};

int main() {
    CuckooHashTable hashTable;
    hashTable.insert(10);
    hashTable.insert(20);
    hashTable.insert(30);
    hashTable.insert(25);
    hashTable.insert(35);
    hashTable.insert(40);
    hashTable.insert(50);

    hashTable.display();

    cout << "Searching 25: " << (hashTable.search(25) ? "Found" : "Not Found") << endl;
    cout << "Searching 100: " << (hashTable.search(100) ? "Found" : "Not Found") << endl;

    hashTable.remove(30);
    hashTable.display();
    return 0;
}

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


توضیح:

نمای کلی ساختار داده ها:

  • دو میز هش جداگانه (جدول 1 و جدول 2).
  • بردارهای بولی (اشغال 1 و اشغال 2) شکاف های اشغال شده را دنبال می کنند.

توابع هش:

  • HASH1 (کلید) = کلید ٪ table_size
  • hash2 (کلید) = (key / table_size) ٪ table_size

فرآیند درج:

  • سعی کنید در جدول 1 قرار دهید. در صورت اشغال ، با عنصر موجود مبادله کنید.
  • سعی کنید عنصر جابجایی شده را در جدول 2 وارد کنید. در صورت اشغال ، به تعویض ادامه دهید.
  • اگر یک حلقه مجدد تشخیص داده شود (MAX_REHASH به آن رسیده است) ، دوباره شروع می شود.

جستجو و حذف:

  • جستجو هر دو جدول را بررسی می کند.
  • حذف شکاف را به عنوان بدون استفاده نشان می دهد.

پایان

فاخته هشویی یک راه حل کارآمد برای حل برخورد هش با یک زمان جستجوی بدترین حالت O (1) ارائه می دهد. با استفاده از دو میز هش و مکانیسم ضربه زدن ، عملکرد بالایی را تضمین می کند و از توالی های طولانی کاوشی جلوگیری می کند. مکانیسم تجدید نظر در هنگام وقوع حلقه های درج ، تغییر اندازه پویا را امکان پذیر می کند و حتی در بارهای کاری پویا حفظ می کند. این روش به ویژه در برنامه هایی که زمان دسترسی سریع بسیار مهم است ، مانند حافظه پنهان ، برنامه های شبکه و سیستم های زمان واقعی مفید است.


منابع:

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا