代码之家  ›  专栏  ›  技术社区  ›  satoru

两表增量的快速算法

  •  11
  • satoru  · 技术社区  · 14 年前

    我有两张专辑名列表,是按一定的乐谱排列的。

    albums_today = ['album1', 'album2', 'album3']
    albums_yesterday = ['album2', 'album1', 'album3']
    

    我如何计算列表顺序的变化

    {'album1':1, 'album2':-1, 'album3':0}
    
    7 回复  |  直到 14 年前
        1
  •  6
  •   John La Rooy    14 年前
    >>> albums_today = ['album1', 'album2', 'album3']
    >>> albums_yesterday = ['album2', 'album1', 'album3']
    >>> D = dict((k,v) for v,k in enumerate(albums_yesterday))
    >>> dict((k,D[k]-v) for v,k in enumerate(albums_today))
    {'album1': 1, 'album3': 0, 'album2': -1}
    

    在Python2.7或Python3中,它可以写得更简单

    >>> albums_today = ['album1', 'album2', 'album3']
    >>> albums_yesterday = ['album2', 'album1', 'album3']
    >>> D = {k:v for v,k in enumerate(albums_yesterday)}
    >>> {k:D[k]-v for v,k in enumerate(albums_today)}
    {'album1': 1, 'album3': 0, 'album2': -1}
    
        2
  •  3
  •   Tyson    14 年前

    你也可以使用和我上面写的相同的算法,只需使用一个hashmap。

    def findDelta1(today,yesterday):
     results = {}
     ypos = 0
     for i,title in enumerate(today):
          if title in results:
               results[title] = results[title] - i
          else:
               for ypos in xrange(ypos,len(yesterday)):
                    if yesterday[ypos] == title:
                         results[title] = ypos - i
                         ypos = ypos + 1
                         break
                    else:
                         results[yesterday[ypos]] = ypos
     return results
    

    仍然是O(N),可能比我上面的版本更快,内存更少。

        3
  •  2
  •   SingleNegationElimination    14 年前

    这个怎么样:

    def delta(a, b):
        rank_a = dict((k, v) for v, k in enumerate(a))
        rank_b = enumerate(b)
        return dict((k, rank_a[k]-i) for i, k in rank_b)
    

    它只创建了一个用来查找事物的命令。

    好吧,只要这两个列表中的每个条目都只出现一次,那么我们就知道,一旦我们在rank_a集合中查找了一个键,我们就不再需要它了。我们可以删除它。此外,为了节省空间,在需要特定密钥之前,我们不必填充该集合。

    class LookupOnce:
        def __init__(self, seq):
            self.cache = {}
            self.seq = iter(seq)
        def get(self, key):
            if key in self.cache:
                value = self.cache[key]
                del self.cache[key]
                return value
            for v,k in self.seq:
                if k == key:
                    return v
                self.cache[k] = v
            raise KeyError
    
    
    def delta(a, b):
        rank_a = LookupOnce(enumerate(a))
        rank_b = enumerate(b)
        result = {}
        for i, k in rank_b:
            result[k] = i - rank_a.get(k)
        return result
    
        4
  •  1
  •   hughdbrown    14 年前
    >>> def transform(albums):
    ...     return dict((album, i) for i, album in enumerate(albums))
    ... 
    >>> def show_diffs(album1, album2):
    ...     album_dict1, album_dict2  = transform(album1), transform(album2)
    ...     for k, v in sorted(album_dict1.iteritems()):
    ...         print k, album_dict2[k] - v
    ... 
    >>> albums_today = ['album1', 'album2', 'album3']
    >>> albums_yesterday = ['album2', 'album1', 'album3']
    >>> show_diffs(albums_today, albums_yesterday)
    album1 1
    album2 -1
    album3 0
    
        5
  •  0
  •   Tyson    14 年前

    好吧,根据列表的大小,有很多不同的方法。在不知道数据集有多大的情况下,我建议最简单的方法(可能是不必要的优化)如下:

    albums_yesterday_lookup = new HashMap();
    differences = new HashMap();
    foreach(albums_yesterday as position => album_title)
        albums_yesterday_lookup.put(album_title,position);
    
    foreach(albums_today as position => album_title)
        differences.put(album_title, albums_yesterday_lookup.get(album_title) - position);
    

    它以O(N)的形式运行。

        6
  •  0
  •   satoru    14 年前
    D = dict((title, rank) for rank, title in enumerate(albums_yesterday))
    for rank, title in enumerate(albums_today):
        D[title] = D[title] - rank
    
        7
  •  0
  •   aaronasterling    14 年前

    新的和改进的,而不是O(n ) :但仍然比其他两个答案慢。

    此解决方案的唯一优点是节省内存。它避免了构建一个大型dict,而是只存储当时所需的内容。TokenMacGuy的第二个解决方案也能做到这一点,但速度稍快。

    def get_deltas_aas(today, yesterday):
        deltas = {}
        for (new_rank, new_album), (old_rank, old_album) in \
                itertools.izip(enumerate(today), enumerate(yesterday)):
            if old_album in deltas:
                #Believe it or not, this is faster than deltas.pop(old_album) + old_rank
                yield (old_album, deltas[old_album] + old_rank)
                del deltas[old_album]    
            else:
                deltas[old_album] = old_rank
    
            if new_album in deltas:
                yield (new_album, deltas[new_album] - new_rank)
                del deltas[new_album]
            else:
                deltas[new_album] = -new_rank
    

    这里有一些大多数答案的计时结果(在Python中所有的答案,除非我遗漏了什么)。 dict 命令生效了。如果有人想让我以任何方式修改他们的代码,只要打电话给我。

    get_deltas_token1: 1.08131885529 msecs
    get_deltas_gnibbler: 1.06443881989 msecs
    get_deltas_tyler: 1.61993408203 msecs
    get_deltas_token2: 1.52525019646 msecs
    get_deltas_hughdbrown: 3.27240777016 msecs
    get_deltas_aas: 1.39379096031 msecs
    

    我用来计时的代码是 here . 我很高兴我在时间的基础上为它设计的时间框架。在重构用于运行测试的代码之后,将来应该很有用。