【问题标题】:Optimizing hash table implementation to accommodate large amount of elements优化哈希表实现以容纳大量元素
【发布时间】: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


【解决方案1】:

由于主 (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 &lt; len; i ++)

if(item instanceof Client){

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

【讨论】:

  • 这些是一些不错的建议,我会立即尝试并尽快给您反馈。谢谢你:)
  • 愚蠢的我没有意识到打开页面时不会调用findBiggestSpender 和findMostFrequentBuyer 函数。那是因为我希望在哈希表完成自身构建后在控制台中测试它们。修复 for 循环似乎也对性能没有太大影响。不过还是感谢您的建议。
猜你喜欢
  • 1970-01-01
  • 2017-12-22
  • 2012-09-02
  • 2016-03-25
  • 2011-10-14
  • 2011-09-15
  • 2012-06-03
  • 2021-09-14
相关资源
最近更新 更多