【发布时间】:2018-10-19 19:51:59
【问题描述】:
考虑以下场景:
一百万客户光顾一家商店并使用他们的信用卡支付了一笔款项。信用卡代码是使用 16 位数字生成的,并将其中的 4 位数字(随机)替换为字符 'A', 'B', 'C', 'D'。 16 位数字随机生成一次,用于每张信用卡,卡之间的唯一变化是上述字符的字符串中的位置(大约 40k 个可能的不同代码)。
我必须在哈希表中组织客户端,使用我选择的哈希函数并使用开放寻址(线性探测)来处理冲突。一旦在表格中组织好,我必须找到客户
- 在他的购买过程中支付了最多的钱。
- 光顾次数最多的商店。
我的哈希表实现如下,并且对于 1000 个客户端的测试似乎可以正常工作。但是,一旦我将客户端数量增加到 10000,页面就永远不会完成加载。这是一个大问题,因为“购物会话”的总数必须是一百万,而我什至没有接近这个数字。
class HashTable{
constructor(size){
this.size = size;
this.items = new Array(this.size);
this.collisions = 0;
}
put(k, v){
let hash = polynomial_evaluation(k);
//evaluating the index to the array
//using modulus a prime number (size of the array)
//This works well as long as the numbers are uniformly
//distributed and sparse.
let index = hash%this.size;
//if the array position is empty
//then fill it with the value v.
if(!this.items[index]){
this.items[index] = v;
}
//if not, search for the next available position
//and fill that with value v.
//if the card already is in the array,
//update the amount paid.
//also increment the collisions variable.
else{
this.collisions++;
let i=1, found = false;
//while the array at index is full
//check whether the card is the same,
//and if not then calculate the new index.
while(this.items[index]){
if(this.items[index] == v){
this.items[index].increaseAmount(v.getAmount());
found = true;
break;
}
index = (hash+i)%this.size;
i++;
}
if(!found){
this.items[index] = v;
}
found = false;
}
return index;
}
get(k){
let hash = polynomial_evaluation(k);
let index = hash%this.size, i=1;
while(this.items[index] != null){
if(this.items[index].getKey() == k){
return this.items[index];
}
else{
index = (hash+i)%this.size;
i++;
}
}
return null;
}
findBiggestSpender(){
let max = {getAmount: function () {
return 0;
}};
for(let item of this.items){
//checking whether the specific item is a client
//since many of the items will be null
if(item instanceof Client){
if(item.getAmount() > max.getAmount()){
max = item;
}
}
}
return max;
}
findMostFrequentBuyer(){
let max = {getTimes: function () {
return 0;
}};
for(let item of this.items){
//checking whether the specific item is a client
//since many of the items will be null
if(item instanceof Client){
if(item.getTimes() > max.getTimes()){
max = item;
}
}
}
return max;
}
}
我用来计算数组索引的键是一个从 0 到 15 的 4 个整数的列表,表示 'A', 'B', 'C', 'D' 在字符串中的位置
这是我正在使用的哈希函数:
function polynomial_evaluation(key, a=33){
//evaluates the expression:
// x1*a^(d-1) + x2*a^(d-2) + ... + xd
//for a given key in the form of a tuple (x1,x2,...,xd)
//and for a nonzero constant "a".
//for the evaluation of the expression horner's rule is used:
// x_d + a*(x_(d-1) + a(x_(d-2) + .... + a*(x_3 + a*(x_2 + a*x1))... ))
//defining a new key with the elements of the
//old times 2,3,4 or 5 depending on the position
//this helps for "spreading" the values of the keys
let nKey = [key[0]*2, key[1]*3, key[2]*4, key[3]*5];
let sum=0;
for(let i=0; i<nKey.length; i++){
sum*=a;
sum+=nKey[i];
}
return sum;
}
与哈希函数生成的键对应的值是 Client 类的实例,该类包含字段 amount(支付的金额)、times(此特定客户购物的时间)、@ 987654330@(上面提到的 4 个整数的数组),以及这些字段的 getter 函数。另外还有一种方法可以在同一个客户端出现多次时增加amount。
哈希表的大小为87383(质数),我的主文件中的代码如下所示:
//initializing the clients array
let clients = createClients(10000);
//creating a new hash table
let ht = new HashTable(N);
for(let client of clients){
ht.put(client.getKey(), client);
}
这会一直运行,直到谷歌浏览器给出“页面没有响应”错误。有什么办法可以让这更快吗?我在这个主题上的方法(甚至我选择的语言)是否正确?
提前致谢。
【问题讨论】:
-
@RandyCasburn 你有什么建议我应该解决我的问题?感谢您的反馈。
-
您是否调试过脚本以查看它一直挂起的位置?碰撞次数过多吗?
-
看起来像家庭作业...如果是,您应该首先披露它。无论如何,您在
put()方法中的while()循环不太可能解决零模数(== false)。 -
@VlassisFo 那么现在是学习它的好时机 :-) developers.google.com/web/tools/chrome-devtools/javascript(假设您使用基于 Chromium 的浏览器)
标签: javascript performance hashtable