幂等散列关联

时间:2011-06-02 20:40:23

标签: python hash

我有一些名字:["James", "John", "Krieg"]和一些颜色:["Red", "Green", "Blue", "Yellow"]。我想使用一些散列函数将名称映射到颜色:f(name) -> color。这种关联是幂等的。例如,如果在原始列表中f(James) -> Red,则在我将名称或颜色添加到各自列表后,f(James)仍为Red

示例:

列出状态1:

["James", "John", "Krieg"] and ["Red", "Green", "Blue", "Yellow"]: 
    f(James) -> Red
    f(John) -> Yellow 
    f(Krieg) -> Yellow

列出状态2:

["James", "John", "Krieg", "Sarah"] and ["Red", "Green", "Blue", "Yellow", "Black"]: 
(added "Sarah" and "Black")
    f(James) -> Red
    f(John) -> Yellow 
    f(Krieg) -> Yellow
    f(Sarah) -> Green

只要尝试均匀性,哈希函数的细节就不重要了。我有这个问题,因为我有一个显示给用户的名字列表,随着该列表的增长,我希望以前输入的名称的颜色关联是相同的(因此用户保留名称/颜色关联)。我意识到,如果我事先指定颜色列表,这不会是一个问题。

所以现在只是出于好奇 - 是否存在散列函数,当输入/输出大小增加而没有持久性时,这些函数不会改变先前关联的值?对不起之前的混乱感到抱歉。

6 个答案:

答案 0 :(得分:2)

你是对的,你只需要将关联存储在内存中。你需要的是这样一个可变的关联集。在python中,这是一本字典:

>>> assocs = dict(zip(['James', 'John', 'Krieg', 'Sarah'], ['Red', 'Green', 'Blue', 'Yellow']))
>>> assocs['Sarah']
'Yellow'
>>> assocs['Sarah'] = 'Black'
>>> assocs['Sarah']
'Black'

修改

如果你总是有两个列表,并且它们总是有序,那么为什么不使用列表索引来“存储”映射:

>>> names = ['James', 'John', 'Krieg', 'Sarah']
>>> colors = ['Red', 'Green', 'Blue', 'Yellow']
>>> def finmap(name):
... i = names.index(name)
... if i < len(colors):
...     return colors[i]
... else:
...     print 'all the colors have been assigned'
...

希望有所帮助

答案 1 :(得分:2)

我并不完全确定你所说的“实时的方式”,但我认为幂等词典可能有所帮助。例如:

##!/usr/bin/env python
# coding: utf-8

class idempotent_dict(dict):
    def __setitem__(self, key, value):
        if key in self:
            return
        super(idempotent_dict, self).__setitem__(key, value)

if __name__ == '__main__':
    d = idempotent_dict()

    d['James'] = 'Red'
    d['John'] = 'Yellow'
    d['Krieg'] = 'Yellow'

    print d

    d['James'] = 'Black'
    d['John'] = 'Red'
    d['Sarah'] = 'Green'

    print d

打印:

{'James': 'Red', 'John': 'Yellow', 'Krieg': 'Yellow'}
{'Sarah': 'Green', 'James': 'Red', 'John': 'Yellow', 'Krieg': 'Yellow'}`

答案 2 :(得分:2)

如果我理解你的问题,你需要一个对象,它将获取一个键列表和一个值列表(任意长度或无长度),并返回一个总是具有相同值的字典,无论添加任何内容。

>>> class ListOMatic(object):
    def __init__(self):
        self.people = []
        self.colors = []

    def idpt(self, people=[], colors=[]):

        for p in people:
            if not p in self.people:
                self.people.append(p)

        for c in colors:
            if not c in self.colors:
                self.colors.append(c)

        return dict(zip(self.people, self.colors))

>>> lom = ListOMatic()
>>> people = ['James', 'John', 'Krieg']
>>> colors = ['Red', 'Green', 'Blue', 'Yellow']
>>> # populate it with our initial values and show that we can pull values out.
>>> print (lom.idpt(people, colors))
{'James': 'Red', 'John': 'Green', 'Krieg': 'Blue'}
>>> print (lom.idpt())
{'James': 'Red', 'John': 'Green', 'Krieg': 'Blue'}
>>> print (lom.idpt()["James"])
Red
>>> # add some colors but no names.
>>> print (lom.idpt([],["Purple", "Mauve"]))
{'James': 'Red', 'John': 'Green', 'Krieg': 'Blue'}
>>> print (lom.idpt())
{'James': 'Red', 'John': 'Green', 'Krieg': 'Blue'}
>>> # add a name and show that it "picks up" the first available color
>>> print (lom.idpt(["Sarah"],[]))
{'Sarah': 'Yellow', 'James': 'Red', 'John': 'Green', 'Krieg': 'Blue'}
>>> print (lom.idpt(["Victor", "Charlie"],["Puce"]))
{'Sarah': 'Yellow', 'James': 'Red', 'Charlie': 'Mauve', 'John': 'Green', 'Krieg': 'Blue', 'Victor': 'Purple'}
>>> print (lom.idpt())
{'Sarah': 'Yellow', 'James': 'Red', 'Charlie': 'Mauve', 'John': 'Green', 'Krieg': 'Blue', 'Victor': 'Purple'}
>>> print (lom.idpt()["Sarah"])
Yellow
>>> 

希望这就是你在“飞行中”的意思。

答案 3 :(得分:2)

我会说,如果不依赖持久性,这样的事情就不存在了。

我们可以根据列表位置排除任何映射。让我们从N个名称和1个颜色开始 - 这意味着所有名称都映射为单一颜色。如果我们以后有N个名字和M个颜色,除非我们可以存储哪个N名称映射到第一个颜色,否则无法使其工作。

同样,我们可以根据名称/颜色的值排除任何内容。假设我们有一些函数f(名称,颜色),它提供了决定名称最佳颜色的分数。如果F(bob,green)&gt; F(bob,红色)然后当我们的列表从[bob], [red]转到[bob], [green, red]时,我们会得到不同的映射。

你可以想出一些退化的解决方案,这些解决方案没有明确地“保存每个关联”,但仍然保持足够的状态来重新创建计算。充其量,这些将存储尽可能多的数据,只需存储您的映射。在最坏的情况下,他们将存储更多。

幂等的使用表明你的原始问题可能是抽象的好奇心。如果您正试图解决一个特定的,实际的问题,那么对该问题进行更具体的解释会有所帮助。

答案 4 :(得分:2)

在给予这个愚蠢的思考之后,我相信Sean McSomething是对的:如果没有“持久性”,你就不能完全你要求的东西(但见下文**) 。问题是如果你不允许“持久性”,那么要么你可以让多个键映射到相同的值,你可以拥有每个键,每个值只出现一次在每个列表中,但不是两个。

也就是说,这里有一个类似乎可以生成您想要的关联,只是与您的示例不同,它只允许多个相同的值,只要它们作为单个操作的一部分输入即可。我已经挪用了lysdexia's answer的基本设计,使用队列来管理未配对的新键映射到先前插入的值的情况,反之亦然。下面的代码存在很多问题,但它符合您的要求;这是一个有趣的运动:

import itertools

class IdempotentMap(object):
    def __init__(self, keys=[], vals=[]):
        self.keys = []
        self.vals = []
        self.keyqueue = []
        self.valqueue = []
        self.append(keys, vals)

    def has_key(self, key):
        return key in self.keys or key in self.keyqueue

    def has_val(self, val):
        return val in self.vals or val in self.valqueue

    def append(self, keys=[], vals=[]):
        len_diff = len(keys) - len(vals)
        if len_diff > 0:
            vals.extend([None] * len_diff)
        elif len_diff < 0:
            keys.extend([None] * abs(len_diff))
        seen = set()
        for i, k in enumerate(keys):
            if k in seen:
                keys[i] = None
            seen.add(k)
        keys = (None if self.has_key(k) else k for k in keys)
        vals = (None if self.has_val(v) else v for v in vals)
        for key, val in zip(keys, vals):
            if key and val:
                self.keys.append(key)
                self.vals.append(val)
            elif key and val is None:
                if self.valqueue:
                    self.keys.append(key)
                    self.vals.append(self.valqueue.pop(0))
                else:
                    self.keyqueue.append(key)
            elif val and key is None:
                if self.keyqueue:
                    self.vals.append(val)
                    self.keys.append(self.keyqueue.pop(0))
                else:
                    self.valqueue.append(val)

    def __iter__(self):
        return ((key, val) for key, val in itertools.izip(self.keys, self.vals))

以下是您如何使用它,最后稍微蓬勃发展,以表明按键始终是唯一的:

idem = IdempotentMap()
idem.append(['James'], ['Red', 'Green', 'Blue'])
print tuple((key, val) for key, val in idem)
idem.append(['John', 'Krieg'], ['Yellow', 'Yellow'])
print tuple((key, val) for key, val in idem)
idem.append(['Sarah', 'Sarah', 'Bo-barah'])
print tuple((key, val) for key, val in idem)

这是输出:

(('James', 'Red'),)
(('James', 'Red'), ('John', 'Yellow'), ('Krieg', 'Yellow'))
(('James', 'Red'), ('John', 'Yellow'), ('Krieg', 'Yellow'), ('Sarah', 'Green'), ('Bo-barah', 'Blue'))

**我将持久性置于恐慌引号中,因为我认为这里存在一些关于“持久性”实际意味着什么的问题,因为哈希值必须计算,并且哈希表中只有持久性是缓存的哈希值,如果有的话。我不确定dict中的关联是否比基于显式索引的映射中的关联更具“持久性”,例如这个或lydexia's。他们基于索引,最终,不是吗? 但是,我想上面对于大型地图使用的内存要少于相当大小的dict。

答案 5 :(得分:0)

假设您扩展了一组值,则f("Sarah") == "Black"。问题仍然存在,f("Sarah")的值在扩展值之前是什么?如果它是"Black",则您的哈希函数的结果不在可接受的值范围内。如果不是"Black",则在扩展集合时,您的哈希函数将不维护其映射。您的散列函数将需要记住它给任何给定键的值,并且此时您还停留在映射上。我认为这不是一个坏解决方案。但这实际上不再是哈希函数。