SlideShare a Scribd company logo
1 of 5
Download to read offline
Page 1 of 5
ლაბორატორიული მეცადინება 7
განსახილველი საკითხები:
 ამოცანა /ელემენტების ჩამატება წრფივი გადასინჯვის მეთოდით/
a. მოკლე ცნობები მეთოდის შესახებ
b. ღია მისამართებით ჰეშირების კლასი >>>
c. პროგრამა-დრაივერი >>>
 სავარჯიშოები >>>
ამოცანა: თავდაპირველად ცარიელ 11-ელემენტიან ჰეშ ცხრილში ჩაამატეთ გასაღებები
10, 22, 31, 4, 15, 28, 17, 88, 59
ღია მისამართები განსაზღვრეთ წრფივი გადასინჯვით. ჰეშ ფუნქცია გააკეთეთ გაყოფის
მეთოდით.
ამოხსნა:
მოკლე ცნობები მეთოდის შესახებ
რადგან მისამართებიან ჰეშ-ცხრილში ჩასასმელი არის ნატურალური რიცხვები, ამიტომ საქმე
მარტივდაა,- საჭირო აღარაა ყალიბების (ტემპლიტების) გაკეთება.
ცხრილის ზომა თუ არის m=11, ჰეშ-ფუნქციის ამოცანა არის ნატურალური რიცხვები
გარდაქმნას {0, 1, ..., 10} ელემენტებად, ანუ ამ ცხრილის ინდექსებად. ტრადიციულად, ჰეშ
ფუნქციის არგუმენტებს გასაღებებს, ხოლო კონკრეტულ გასაღებებზე ჰეშ ფუნქციის
მნიშვნელობებს ჰეშ-მნიშვნელობებს ვუწოდებთ. გასაღებები უნიკალურია, ანუ
განსხავებულია ერთმანეთისაგან, მაგრამ ზოგიერთი ჰეშ-მნიშვნელობა ერთმანეთის ტოლი
აღმოჩნდება (9 გასაღებია და მხოლოდ 11 ჰეშ-მნიშვნელობა), ამიტომ ადგილი ექნება
კოლიზიას, რასაც ღია მისამართის მოძებნის რომელიმე მეთოდი აგვარებს (ამ შემთხვევაში
წრფივი გადასინჯვა).
ჰეშ ფუნქცია ავიღოთ გაყოფის მეთოდით, ანუ
int hashByMod(int key, int M)
{
return key % M;
}
პროგრამა ისეა მოწყობილი, რომ მეორე არგუმენტს (m=11) ობიექტი ჰეშ-ცხრილი მიიღებს
კონსტრუირების მომენტში.
ღია მისამართები ნიშნავს, რომ ცხრილში ადგილების რაოდენობა ყოველთვის მეტი ან ტოლი
უნდა იყოს კონკრეტულ ამოცანაში აქტიური (ანუ გამოყენებული) გასაღებებისა, ანუ
ცარიელი ადგილი ცხრილში ყოველთვის უნდა იყოს. ცხრილში ჩასმა ხდება შემდეგი
ფსევდოკოდის გამოყენებით, რომელსაც ქვემოთ ფუნქციის სახე აქვს მიცემული:
ცხრილში გასაღების ჩამატება:
 მოცემული k გასაღებისთვის ვიპოვოთ ჰეშ-მნიშვნელობა h(k);
 დაწყებული i=0 -იდან, დავიწყოთ ცხრილის უჯრების გადასინჯვა მანამდე, ვიდრე არ
შეგვხვდება თავისუფალი უჯრა, ანუ უჯრა რომელშიც 0 ან -1 წერია. ყოველი i -ური
ცდისთვის მისამართს გამოვთვლით ფორმულით:   modh k i m , ანუ ჯერ
Page 2 of 5
გამოვთვლით h(k) მისამართს, მერე, თუ იგი დაკავებულია მის მარჯნივ პირველივე
თვისუფალს ვეძებთ.
 თუ რომელიმე i -ური ცდისთვის (h(k)+i)mod m მისამართზე წერია ნული ან -1, მაშინ
ამ უჯრაში ჩავწერთ k-ს.
 თუ i გახდა m-ის ტოლი მაგრამ თავისუფალი უჯრა ვერ ვნახეთ, მაშინ გვაქვს
ცხრილის გადავსების შეცდომა.
გასაღების წაშლისთვის, ჯერ ვეძებთ ამ გასაღებს, შემდეგ, თუ ვიპოვეთ, უჯრაში ჩავწერთ
1 -ს. შევნიშნოთ, რომ თავდაპირველად, ცარიელი ცხრილი ნულებითაა შევსილი. წაშლისას
-1-ს ვწერთ იმიტომ, რომ ძებნისას ხშირად შეიქმნება პრობლემები თუ წაშლილში 0 წერია
(ამის მაგალითებს ვნახავთ პრაქტიკულ მეცადინეობაზე და პროგრამა დრაივერში).
<<< ღია მისამართებით ჰეშირების კლასს შესაძლოა ჰქონდეს სახე:
#pragma once
#include<iostream>
using namespace std;
class OpenAddressHashTable
{
private:
int* hTable;
int (*fPtr)(int, int);
int size;
int capacity;
public:
OpenAddressHashTable(void);
~OpenAddressHashTable(void);
OpenAddressHashTable(int m, int hashFunc(int, int));
int getSize(void);
int getCapacity(void);
int* getTable(void);
int insert(int k);
int search(int k);
void erase(int k);
friend ostream& operator<<(ostream&, OpenAddressHashTable&);
}
მის იმპლემენტაციაა:
//h-შესაბამისი .cpp ფაილი
#include "OpenAddressHashTable.h"
#include<iterator>
OpenAddressHashTable::OpenAddressHashTable(void)
: capacity(0)
, size(0)
{
}
OpenAddressHashTable::~OpenAddressHashTable(void)
{
delete [] hTable;
hTable = NULL;
}
Page 3 of 5
OpenAddressHashTable::OpenAddressHashTable(int m, int hashFunc(int, int))
{
capacity = m;
hTable = new int[capacity];
size=0;
for(int i=0; i< capacity; i++)
hTable[i] = 0;
fPtr = hashFunc; ///
}
int OpenAddressHashTable::insert(int k)
{
for(int i=0; i < capacity; i++)
{
int j = ((*fPtr)(k, capacity)+i)%capacity;
if( hTable[j]<=0)
{
hTable[j] = k;
size++;
return j;
}
}
cout << "Hash Table Overflow!" << endl;
return capacity;
}
int OpenAddressHashTable::getSize(void)
{
return size;
}
int OpenAddressHashTable::getCapacity(void)
{
return capacity;
}
int* OpenAddressHashTable::getTable(void)
{
return hTable;
}
int OpenAddressHashTable::search(int k)
{
int i = 0;
while( true)
{
int j = ((*fPtr)(k, capacity)+i)%capacity;
if( hTable[j] == k)
return j;
if(hTable[j] == 0 || i == capacity)
return capacity;
i++;
}
}
void OpenAddressHashTable::erase(int k)
{
int j = search(k);
if(j != capacity)
{
hTable[j] = -1;
Page 4 of 5
size--;
}
}
ostream& operator<<(ostream& os, OpenAddressHashTable& T)
{
for(int i=0; i< T.getCapacity(); i++)
{
cout.width(7);
cout << T.getTable()[i];
}
cout << endl;
return os;
}
აქ არის რამდენიმე საინტერესო მომენტი. კერძოდ, კლასის ერთი კერძო ველი არის ფუნქციის
პოინტერი, რომელიც გამოიყენება კონსტრუქტორში კლასისთვის გადაცემული ჰეშ-
ფუნქციის დასამახსოვრებლად. ფუნქციის პოინტერზე განაცხადის გაკეთება მარტივია,
საჭიროა მხოლოდ დავაფიქსიროთ განსაზღვრისა და მნიშვნელობების არეები. თუ ამ ველის
დამატებას შევეცდებით კლასის შექმნის ინსტრუმენტით (ვიზუალ სტუდიოში), შეიქმნება
პრობლემა, რადგან თანამედროვე მიდგომით ფუნქციაზე განაცხადის ნაცვლად კეთდება
ფუნქტორი (ფუნქციის ტიპის ობიექტი), რომელიც შემდეგ ინლაინდება კომპილაციის დროს
და ამიტომ გაცილებით სწრაფად მუშაობს, რადგან სხვადასხვა ფუნქციების ძებნასა და
მიმართვაზე არ იკარგება დრო. თუმცა ეს შედარებით ახალი ტექნიკაა და ფუნქციებზე
მიმთითებლების გამოყენების ცოდნაც აუცილებელია.
კიდევ ერთი საკითხი არის ჰეშ-ცხრილის შესაქმნელად გამოყენებული კონტეინერი. რადგან
ცხრილის ზომა შექმნის შემდეგ აღარ იცვლება. დინამიკური მასივი საუკეთესო არჩევანია.
<<< ფუნქცია დრაივერი:
//.cpp
#include "OpenAddressHashTable.h"
#include <iostream>
using namespace std;
//hash functions
int hashByMod(int key, int M)
{
return key % M;
}
int hashByMultiply(int key, int M)
{
const double A=(sqrt(5.0)-1)/2;
return int(M*(A*key-floor(A*key)));
}
int main() {
OpenAddressHashTable ht(11,hashByMod);
ht.insert(10);
cout << "Afret inserting 10, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(22);
Page 5 of 5
cout << "Afret inserting 22, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(31);
cout << "Afret inserting 31, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(4);
cout << "Afret inserting 4, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(15);
cout << "Afret inserting 15, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(28);
cout << "Afret inserting 28, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(17);
cout << "Afret inserting 17, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(88);
cout << "Afret inserting 88, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.insert(59);
cout << "Afret inserting 59, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
ht.erase(17);
cout << "Afret inserting 17, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
int j = ht.search(59);
cout<< "Afret searching 59, index of " << 59 << " is "<< j << endl;
ht.insert(70);
cout << "Afret inserting 70, hash.size()=" <<ht.getSize() <<endl;
cout << ht;
cout << endl;
return (0);
}
<<< სავარჯიშოები
1. დინამიკურად შექმენით ჰეშ-ცხრილის ობიექტი, ჩაატარეთ მასზე გარკვეული
მანიპულაციები და შემდეგ გააუქმეთ იგი.
2. როგორ მოვიქცეთ, თუ გასაღებებს ახლავს თანამგზავნი ინფორმაცია?

More Related Content

Viewers also liked

Lab8 2014 chained_hashtable
Lab8 2014 chained_hashtableLab8 2014 chained_hashtable
Lab8 2014 chained_hashtableBeso Bancadze
 
Making the-future
Making the-futureMaking the-future
Making the-futuremohan_s
 
Beethoven-Moonlight Sonata-op27-no2
Beethoven-Moonlight Sonata-op27-no2Beethoven-Moonlight Sonata-op27-no2
Beethoven-Moonlight Sonata-op27-no2Beso Bancadze
 
სტრუქტურების თეორია 2014
სტრუქტურების თეორია 2014სტრუქტურების თეორია 2014
სტრუქტურების თეორია 2014Beso Bancadze
 
Digital Citizenship
Digital CitizenshipDigital Citizenship
Digital CitizenshipErica Reid
 
Branding and Social Media Overview: Mercy College G Pass
Branding and Social Media Overview: Mercy College G PassBranding and Social Media Overview: Mercy College G Pass
Branding and Social Media Overview: Mercy College G PassErica Reid
 
S360 Digital brand storytelling and internet culture
S360 Digital brand storytelling and internet cultureS360 Digital brand storytelling and internet culture
S360 Digital brand storytelling and internet cultureErica Reid
 

Viewers also liked (11)

Foto novela dengue
Foto novela   dengueFoto novela   dengue
Foto novela dengue
 
Lab8 2014 chained_hashtable
Lab8 2014 chained_hashtableLab8 2014 chained_hashtable
Lab8 2014 chained_hashtable
 
Making the-future
Making the-futureMaking the-future
Making the-future
 
Maths_2
Maths_2Maths_2
Maths_2
 
Beethoven-Moonlight Sonata-op27-no2
Beethoven-Moonlight Sonata-op27-no2Beethoven-Moonlight Sonata-op27-no2
Beethoven-Moonlight Sonata-op27-no2
 
სტრუქტურების თეორია 2014
სტრუქტურების თეორია 2014სტრუქტურების თეორია 2014
სტრუქტურების თეორია 2014
 
Usability
UsabilityUsability
Usability
 
Digital Citizenship
Digital CitizenshipDigital Citizenship
Digital Citizenship
 
Branding and Social Media Overview: Mercy College G Pass
Branding and Social Media Overview: Mercy College G PassBranding and Social Media Overview: Mercy College G Pass
Branding and Social Media Overview: Mercy College G Pass
 
S360 Digital brand storytelling and internet culture
S360 Digital brand storytelling and internet cultureS360 Digital brand storytelling and internet culture
S360 Digital brand storytelling and internet culture
 
Qimia
QimiaQimia
Qimia
 

Lab7 2014 ჰეშირება ღია მისამართებით

  • 1. Page 1 of 5 ლაბორატორიული მეცადინება 7 განსახილველი საკითხები:  ამოცანა /ელემენტების ჩამატება წრფივი გადასინჯვის მეთოდით/ a. მოკლე ცნობები მეთოდის შესახებ b. ღია მისამართებით ჰეშირების კლასი >>> c. პროგრამა-დრაივერი >>>  სავარჯიშოები >>> ამოცანა: თავდაპირველად ცარიელ 11-ელემენტიან ჰეშ ცხრილში ჩაამატეთ გასაღებები 10, 22, 31, 4, 15, 28, 17, 88, 59 ღია მისამართები განსაზღვრეთ წრფივი გადასინჯვით. ჰეშ ფუნქცია გააკეთეთ გაყოფის მეთოდით. ამოხსნა: მოკლე ცნობები მეთოდის შესახებ რადგან მისამართებიან ჰეშ-ცხრილში ჩასასმელი არის ნატურალური რიცხვები, ამიტომ საქმე მარტივდაა,- საჭირო აღარაა ყალიბების (ტემპლიტების) გაკეთება. ცხრილის ზომა თუ არის m=11, ჰეშ-ფუნქციის ამოცანა არის ნატურალური რიცხვები გარდაქმნას {0, 1, ..., 10} ელემენტებად, ანუ ამ ცხრილის ინდექსებად. ტრადიციულად, ჰეშ ფუნქციის არგუმენტებს გასაღებებს, ხოლო კონკრეტულ გასაღებებზე ჰეშ ფუნქციის მნიშვნელობებს ჰეშ-მნიშვნელობებს ვუწოდებთ. გასაღებები უნიკალურია, ანუ განსხავებულია ერთმანეთისაგან, მაგრამ ზოგიერთი ჰეშ-მნიშვნელობა ერთმანეთის ტოლი აღმოჩნდება (9 გასაღებია და მხოლოდ 11 ჰეშ-მნიშვნელობა), ამიტომ ადგილი ექნება კოლიზიას, რასაც ღია მისამართის მოძებნის რომელიმე მეთოდი აგვარებს (ამ შემთხვევაში წრფივი გადასინჯვა). ჰეშ ფუნქცია ავიღოთ გაყოფის მეთოდით, ანუ int hashByMod(int key, int M) { return key % M; } პროგრამა ისეა მოწყობილი, რომ მეორე არგუმენტს (m=11) ობიექტი ჰეშ-ცხრილი მიიღებს კონსტრუირების მომენტში. ღია მისამართები ნიშნავს, რომ ცხრილში ადგილების რაოდენობა ყოველთვის მეტი ან ტოლი უნდა იყოს კონკრეტულ ამოცანაში აქტიური (ანუ გამოყენებული) გასაღებებისა, ანუ ცარიელი ადგილი ცხრილში ყოველთვის უნდა იყოს. ცხრილში ჩასმა ხდება შემდეგი ფსევდოკოდის გამოყენებით, რომელსაც ქვემოთ ფუნქციის სახე აქვს მიცემული: ცხრილში გასაღების ჩამატება:  მოცემული k გასაღებისთვის ვიპოვოთ ჰეშ-მნიშვნელობა h(k);  დაწყებული i=0 -იდან, დავიწყოთ ცხრილის უჯრების გადასინჯვა მანამდე, ვიდრე არ შეგვხვდება თავისუფალი უჯრა, ანუ უჯრა რომელშიც 0 ან -1 წერია. ყოველი i -ური ცდისთვის მისამართს გამოვთვლით ფორმულით:   modh k i m , ანუ ჯერ
  • 2. Page 2 of 5 გამოვთვლით h(k) მისამართს, მერე, თუ იგი დაკავებულია მის მარჯნივ პირველივე თვისუფალს ვეძებთ.  თუ რომელიმე i -ური ცდისთვის (h(k)+i)mod m მისამართზე წერია ნული ან -1, მაშინ ამ უჯრაში ჩავწერთ k-ს.  თუ i გახდა m-ის ტოლი მაგრამ თავისუფალი უჯრა ვერ ვნახეთ, მაშინ გვაქვს ცხრილის გადავსების შეცდომა. გასაღების წაშლისთვის, ჯერ ვეძებთ ამ გასაღებს, შემდეგ, თუ ვიპოვეთ, უჯრაში ჩავწერთ 1 -ს. შევნიშნოთ, რომ თავდაპირველად, ცარიელი ცხრილი ნულებითაა შევსილი. წაშლისას -1-ს ვწერთ იმიტომ, რომ ძებნისას ხშირად შეიქმნება პრობლემები თუ წაშლილში 0 წერია (ამის მაგალითებს ვნახავთ პრაქტიკულ მეცადინეობაზე და პროგრამა დრაივერში). <<< ღია მისამართებით ჰეშირების კლასს შესაძლოა ჰქონდეს სახე: #pragma once #include<iostream> using namespace std; class OpenAddressHashTable { private: int* hTable; int (*fPtr)(int, int); int size; int capacity; public: OpenAddressHashTable(void); ~OpenAddressHashTable(void); OpenAddressHashTable(int m, int hashFunc(int, int)); int getSize(void); int getCapacity(void); int* getTable(void); int insert(int k); int search(int k); void erase(int k); friend ostream& operator<<(ostream&, OpenAddressHashTable&); } მის იმპლემენტაციაა: //h-შესაბამისი .cpp ფაილი #include "OpenAddressHashTable.h" #include<iterator> OpenAddressHashTable::OpenAddressHashTable(void) : capacity(0) , size(0) { } OpenAddressHashTable::~OpenAddressHashTable(void) { delete [] hTable; hTable = NULL; }
  • 3. Page 3 of 5 OpenAddressHashTable::OpenAddressHashTable(int m, int hashFunc(int, int)) { capacity = m; hTable = new int[capacity]; size=0; for(int i=0; i< capacity; i++) hTable[i] = 0; fPtr = hashFunc; /// } int OpenAddressHashTable::insert(int k) { for(int i=0; i < capacity; i++) { int j = ((*fPtr)(k, capacity)+i)%capacity; if( hTable[j]<=0) { hTable[j] = k; size++; return j; } } cout << "Hash Table Overflow!" << endl; return capacity; } int OpenAddressHashTable::getSize(void) { return size; } int OpenAddressHashTable::getCapacity(void) { return capacity; } int* OpenAddressHashTable::getTable(void) { return hTable; } int OpenAddressHashTable::search(int k) { int i = 0; while( true) { int j = ((*fPtr)(k, capacity)+i)%capacity; if( hTable[j] == k) return j; if(hTable[j] == 0 || i == capacity) return capacity; i++; } } void OpenAddressHashTable::erase(int k) { int j = search(k); if(j != capacity) { hTable[j] = -1;
  • 4. Page 4 of 5 size--; } } ostream& operator<<(ostream& os, OpenAddressHashTable& T) { for(int i=0; i< T.getCapacity(); i++) { cout.width(7); cout << T.getTable()[i]; } cout << endl; return os; } აქ არის რამდენიმე საინტერესო მომენტი. კერძოდ, კლასის ერთი კერძო ველი არის ფუნქციის პოინტერი, რომელიც გამოიყენება კონსტრუქტორში კლასისთვის გადაცემული ჰეშ- ფუნქციის დასამახსოვრებლად. ფუნქციის პოინტერზე განაცხადის გაკეთება მარტივია, საჭიროა მხოლოდ დავაფიქსიროთ განსაზღვრისა და მნიშვნელობების არეები. თუ ამ ველის დამატებას შევეცდებით კლასის შექმნის ინსტრუმენტით (ვიზუალ სტუდიოში), შეიქმნება პრობლემა, რადგან თანამედროვე მიდგომით ფუნქციაზე განაცხადის ნაცვლად კეთდება ფუნქტორი (ფუნქციის ტიპის ობიექტი), რომელიც შემდეგ ინლაინდება კომპილაციის დროს და ამიტომ გაცილებით სწრაფად მუშაობს, რადგან სხვადასხვა ფუნქციების ძებნასა და მიმართვაზე არ იკარგება დრო. თუმცა ეს შედარებით ახალი ტექნიკაა და ფუნქციებზე მიმთითებლების გამოყენების ცოდნაც აუცილებელია. კიდევ ერთი საკითხი არის ჰეშ-ცხრილის შესაქმნელად გამოყენებული კონტეინერი. რადგან ცხრილის ზომა შექმნის შემდეგ აღარ იცვლება. დინამიკური მასივი საუკეთესო არჩევანია. <<< ფუნქცია დრაივერი: //.cpp #include "OpenAddressHashTable.h" #include <iostream> using namespace std; //hash functions int hashByMod(int key, int M) { return key % M; } int hashByMultiply(int key, int M) { const double A=(sqrt(5.0)-1)/2; return int(M*(A*key-floor(A*key))); } int main() { OpenAddressHashTable ht(11,hashByMod); ht.insert(10); cout << "Afret inserting 10, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(22);
  • 5. Page 5 of 5 cout << "Afret inserting 22, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(31); cout << "Afret inserting 31, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(4); cout << "Afret inserting 4, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(15); cout << "Afret inserting 15, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(28); cout << "Afret inserting 28, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(17); cout << "Afret inserting 17, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(88); cout << "Afret inserting 88, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.insert(59); cout << "Afret inserting 59, hash.size()=" <<ht.getSize() <<endl; cout << ht; ht.erase(17); cout << "Afret inserting 17, hash.size()=" <<ht.getSize() <<endl; cout << ht; int j = ht.search(59); cout<< "Afret searching 59, index of " << 59 << " is "<< j << endl; ht.insert(70); cout << "Afret inserting 70, hash.size()=" <<ht.getSize() <<endl; cout << ht; cout << endl; return (0); } <<< სავარჯიშოები 1. დინამიკურად შექმენით ჰეშ-ცხრილის ობიექტი, ჩაატარეთ მასზე გარკვეული მანიპულაციები და შემდეგ გააუქმეთ იგი. 2. როგორ მოვიქცეთ, თუ გასაღებებს ახლავს თანამგზავნი ინფორმაცია?