优化哈希表实现以容纳大量元素

时间:2018-05-09 16:06:45

标签: javascript performance hashtable

考虑以下情况:

100万客户访问商店并使用信用卡支付一定数额的钱。信用卡代码使用16位数字生成,并用字符'A', 'B', 'C', 'D'替换其4位(随机)。 16位数字随机生成一次,用于每张信用卡,卡片之间唯一的变化就是上述字符串中的位置(即可能的不同代码)。

我必须使用我选择的哈希函数在哈希表中组织客户端,并使用开放寻址(线性探测)来处理冲突。一旦在表格中组织,我必须找到

的客户
  • 在购买期间支付了最多的钱。
  • 访问商店的时间最多。

我对哈希表的实现如下,似乎可以正常运行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;
}
}

to key我用来计算数组的索引是一个4个整数的列表,范围从0到15,表示'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(此时间为特定客户购物),key(上面提到的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);
}

这种情况一直持续到谷歌浏览器提供的页面没有响应&#34;错误。有什么方法可以让它更快吗?我对这个主题的态度(甚至是我选择的语言)是否正确?

提前致谢。

1 个答案:

答案 0 :(得分:0)

由于主(UI)线程被锁定,页面没有响应。使用WebWorker或ServiceWorker处理计算,并将它们作为消息发布到主线程。

关于优化代码,我看到的一件事是findBiggestSpender。我会逐行打破它。

let max = {getAmount: function () {
  return 0;
}};

这是一种浪费。只需分配一个局部变量,无需在每次迭代中调用max.getAmount()

for(let item of this.items){ 在Javascript中迭代列表的最快方法是使用缓存的循环长度:for (let item, len = this.items.length; i < len; i ++)

if(item instanceof Client){

这比硬空检查慢,只需使用item !== null