【问题标题】:Sort faster in racket using hash table使用哈希表在球拍中更快地排序
【发布时间】:2015-12-12 18:09:33
【问题描述】:

所以我有一个这样的元素示例列表

(define A (list 'a 'c 'd 'e 'f 'e 'a))

现在我想从这个样本中进行排名

(define (scan lst)
    (foldl (lambda (element a-hash) (hash-update a-hash element add1 0))
           (hash)
           lst))

结果应该是这样的:

> #(('a . 2) ('f . 1) ('e . 2) ....)

因为 `scan 函数会生成一个包含唯一键和该键重复次数的哈希表(如果它捕获到一个未索引的键,它将为该新键创建一个新位置,从 0 开始计数)。

然后我想对该哈希表进行排序,因为它未排序:

(define (rank A)
     (define ranking (scan A))         
     (sort ranking > #:key cdr)))

所以结果应该是这样的:

#(('a . 2) ('e . 2) ('f . 1) ...)

现在我想截断哈希表并在 n = 1 的阈值处丢弃底部(也就是只取重复次数超过 2 次的元素)。

(define (truncate lst n)
    (define l (length lst))
    (define how-many-to-take 
        (for/list
             ([i l]
               #:when (> (cdr (list-ref lst i))
                          n))
               i))
    (take lst (length how-many-to-take)))

所以结果可能如下所示:

(('a.2)('e.2))

但是,在大范围内,这个过程效率不高,耗时太长。您对提高性能有什么建议吗?

非常感谢,

第 2 部分:

我有这个数据结构:

(automaton x 
   (vector (state y (vector a b c))  
           (state y (vector a b c)) ...)) 

然后我随机生成 1000 个人口。然后我使用上述功能对它们进行扫描和排名。如果我只是按原样扫描它们,那已经需要很长时间了。如果我尝试将它们展平成这样的列表

(list x y a b c y a b c...)

这需要更多的时间。这是扁平化功能:

(define (flatten-au au)
  (match-define (automaton x states) au)
  (define l (vector-length states))
  (define body
    (for/list ([i (in-range l)])
      (match-define (state y z) (vector-ref states i))
      (list y (vector->list z))))
  (flatten (list x body)))

扫描功能看起来会有些不同:

(define (scan population)
    (foldl (lambda (auto a-hash) (hash-update a-hash (flatten-automaton auto) add1 0))
           (hash)
           population))

【问题讨论】:

    标签: sorting hash scheme racket fold


    【解决方案1】:

    是的,我相信我看到了问题所在。您的算法有 O(n^2) ("n-squared") 运行时间。这是因为您从 1 数到列表的长度,然后为每个索引执行list-ref,这需要与索引大小成正比的时间。

    这非常容易解决。

    事实上,如果这是您想要的,真的没有理由对其进行排序或将其转换为列表;直接过滤哈希表。像这样……

    #lang racket
    
    (define A (build-list 1000000 (λ (idx) (random 50))))
    
    (define (scan lst)
        (foldl (lambda (element a-hash) (hash-update a-hash element add1 0))
               (hash)
               lst))
    
    (define ht (scan A))
    
    (define only-repeated
      (time
       (for/hash ([(k v) (in-hash ht)]
                  #:when (< 1 v))
         (values k v))))
    

    我添加了对time 的调用以查看需要多长时间。对于大小为 100 万的列表,在我的计算机上,这需要 1 毫秒的测量时间。

    渐近复杂度很重要!

    【讨论】:

    • 谢谢,我是 Racket 的新手。 for/hash 不是也遍历整个哈希吗?现在计算机大部分时间都花在扫描功能上。你对此有什么建议吗?我为扫描函数编写了一个 for/fold 循环,但它的工作原理或多或少是一样的。
    • 回答您的问题:是的,for/hash 遍历了整个列表。不同之处在于您的原始解决方案遍历了整个列表,并且对于每个解决方案,都遍历了整个列表! (实际上,它只通过了列表的一部分,但我们不要陷入对渐近复杂性的讨论中)。为了解决您的第二个问题:您的列表有多长,扫描需要多长时间?
    • 它太长了,所以我将第 2 部分添加到我的问题中。
    • 您只包含部分代码;如果不能看到一切,真的很难帮助你。也许你应该只创建一个运行程序的要点(例如使用 gist.github.com)?
    • 真的是一团糟,涉及很多不同的东西。我还在努力。如果我能把它分解成一个更好的问题,我会把它贴在这里。谢谢你的帮助,Chi
    猜你喜欢
    • 2013-01-15
    • 2023-03-27
    • 2022-01-20
    • 2010-10-26
    • 2015-03-20
    • 2015-07-06
    • 2020-04-17
    • 2016-05-08
    • 2012-12-21
    相关资源
    最近更新 更多