-
Notifications
You must be signed in to change notification settings - Fork 0
/
Hash.cpp
284 lines (255 loc) · 5.51 KB
/
Hash.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#include <iostream>
#include <string>
#include <cmath>
#include <windows.h>
#include <iomanip>
#include <time.h>
#include <fstream>
using namespace std;
typedef unsigned int uint;
void RandCreat()
{
string str;
cout<<"请输入要创建的文件名"<<endl;
getline(cin,str);
string comd="RandCreat.exe ";
time_t at = clock();
cout << "正在创建中,请稍等\n";
system(comd.append(str).c_str());
cout<<"创建成功"<<endl;
cout << endl << fixed << setprecision(0)<< "耗时" << (double)(clock() - at) * 1000 / CLOCKS_PER_SEC << "ms" << endl;
}
void PrintInf()
{
cout << "*************************" << endl;
cout << "请选择测试模式" << endl;
cout << "*************************" << endl;
cout << "1:\t通过文件" << endl;
cout << "2:\t键盘输入" << endl;
cout << "3:\t随机创建数据" << endl;
cout << "4:\t退出程序" << endl;
cout << "*************************" << endl;
}
class Hash
{
private:
string * base;
int Amount = 100000;
double rate = 0.6233;
uint M; //开放地址的数量
uint P; //离M最近的素数
int NextPrime();
bool Isprime(int n);
public:
Hash(double rate);
Hash();
uint HashCode(string str);
bool Find(string e, int ×);
void CreatBaseFile(string address);
void FileTest(void);
void KeyBoardTest();
};
int main(void)
{
double rate;
cout << "请输入填充因子:";
cin >> rate;
cin.get();
if (rate>0.99) // 避免错误输入
{
cout<<"填充因子太大,程序将退出"<<endl;
return 0;
}
Hash hash(rate);
time_t at = clock();
hash.CreatBaseFile("input.txt");
cout << endl << "耗时" << (int)((double)(clock() - at)*1000/CLOCKS_PER_SEC )<<"ms"<< endl;
while (true)
{
cin.clear();
PrintInf();
string cmd;
getline(cin,cmd);
char n=cmd[0];
switch (n)
{
case '1': hash.FileTest();
break;
case '2': hash.KeyBoardTest();
break;
case '3': RandCreat();
break;
case '4': return 0;
break;
default: cout << "输入非法请重新输入" << endl;
break;
}
//system("cls");
cout << endl;
}
return 0;
}
//构造函数 初始化变量
Hash::Hash(double rate)
{
this->rate = rate;
M = (int)(Amount / rate);//计算开放地址的数量
base = new string[M];//创建以及初始化内存
P = NextPrime();
}
Hash::Hash()
{
M = (int)(Amount / rate);
base = new string[M];
P = NextPrime();
}
//哈希查找关键函数
bool Hash::Find(string e, int ×)
{
bool flag = false;
uint n = HashCode(e);
times = 1;
while (base[n] != "")
{
if (e == base[n])
{
flag = true;
break;
}
n = (n + 1) % M;
times++;
}
return flag;
}
//寻找最近的一个素数
int Hash::NextPrime()
{
for (int i = M; i >= 1; i--)
{
if (Isprime(i))
return i;
}
return 1;
}
//计算字符串的哈希值并返回位置
uint Hash::HashCode(string str)
{
uint seed = 131; // 31 131 1313 13131 131313 etc..
uint hash = 0;
for (uint i = 0; i < str.size(); i++)
{
hash = hash * seed + str[i];
}
return (hash & 0x7FFFFFFF)%P;
}
// 比较高效率的判断这个数是否是素数
bool Hash::Isprime(int num)
{
if(num ==2|| num==3 )
return true ;
if(num %6!= 1&&num %6!= 5)
return false ;
int tmp =(int)sqrt( num);
for(int i= 5;i <=tmp; i+=6 )
if(num %i== 0||num %(i+ 2)==0 )
return false ;
return true ;
}
void Hash::CreatBaseFile(string address)
{
cout << "正在构造哈希表,请稍等......" << endl << endl;
cout << "填充因子为 " << this->rate << endl << endl;
ifstream input(address.c_str()); // 类似于cin对控制台的操纵
if (!input)
{
cout << "文件不存在,请检查" << endl;
system("RandCreat input.txt");
return;
}
string str;
uint cnt = 0; //用于记录总共的成功查找次数
while (!input.eof()&&getline(input, str))
{
if (str == "0")
break;
uint location = HashCode(str);
int time = 0;
Find(str, time);
cnt += time;
base[(location + time - 1)%M] = str; //加上取余操作,避免下标越界导致程序崩溃
}
input.close();
cout << "哈希表构造完毕!" << endl << endl;;
cout << "平均成功查找长度 " << 1.0*cnt / Amount << endl << endl;
}
void Hash::FileTest(void)
{
string address;
cout << endl<<"请输入要测试文件的地址 "<<endl;
getline(cin, address);
ifstream input(address.c_str());
if (!input)
{
cout << "文件不存在,请检查输入" << endl;
return ;
}
cout << "正在处理中请稍等" << endl;
//通过freopen的方法可以减少代码量 ,直接先两个freopen 然后调用keyboardtest 就可以
/*
freopen(address.c_str(),"r",stdin);
freopen("out.txt","w",stdout);
this->KeyBoardTest();
freopen("con","w",stdout);
freopen("con","r",stdin);
*/
time_t at = clock();
ofstream out("out.txt");
string name;
uint amount = 0, cnt = 0;
while (!input.eof() && getline(input, name))
{
if (name == "0") break;
cnt++;
out << cnt << ": ";
int times;
if (Find(name, times))
{
out << "Pos " << HashCode(name) + times;
}
else
{
out << "Sorry! ";
}
amount += times;
out << " ; cur: " << times << ", Avg: ";
out << fixed << setprecision(6) << 1.0*amount / cnt << endl;
}
cout << "平均查找长度为" << fixed << setprecision(6) << 1.0*amount / cnt << endl;
input.close();
out.close();
cout << endl << fixed << setprecision(0)<< "耗时" << (double)(clock() - at) * 1000 / CLOCKS_PER_SEC << "ms" << endl;
}
void Hash::KeyBoardTest()
{
string name;
uint amount = 0, cnt = 0;
while (!cin.eof()&&getline(cin, name))
{
if (name == "0") break;
cnt++;
cout << cnt << ": ";
int times;
if (Find(name, times))
{
cout << "Pos " << HashCode(name) + times;
}
else
{
cout << "Sorry! ";
}
amount += times;
cout << " ; cur: " << times << ", Avg: ";
cout << fixed << setprecision(6) << 1.0*amount / cnt << endl;
}
cout << "平均查找长度为" << fixed << setprecision(6) << 1.0*amount / cnt << endl;
}