Идемпотентные хэш-ассоциации - PullRequest
1 голос
/ 03 июня 2011

У меня есть несколько имен: ["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 ]

2 голосов
/ 03 июня 2011

После долгих размышлений я считаю, что Шон МакСоме прав: вы не можете иметь точно то, что вы просили, без "настойчивости" (но см. ** ниже).Проблема в том, что если вы запретите «постоянство», тогда или вы можете иметь несколько ключей, сопоставляемых одному и тому же значению, или , вы можете иметь каждый ключ, и каждое значение появляется только один раз в каждомсписок, но не оба.

Тем не менее, вот класс, который, по-видимому, генерирует именно те ассоциации, которые вам нужны, отличается только от вашего примера тем, что он допускает множественные идентичные значения, если они вводятся как часть одной операции.Я присвоил базовый дизайн ответа lysdexia , используя очереди для управления ситуациями, когда непарный новый ключ сопоставляется с ранее вставленным значением, или наоборот.Есть много проблем с приведенным ниже кодом, но он делает то, что вы просили;и это было забавное упражнение:

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'))

** Я поставил настойчивость в кавычки, потому что я думаю, что есть некоторый вопрос относительно того, что на самом деле означает «постоянство», учитывая, что хэши должны быть вычислены ,и единственное постоянство в хеш-таблице - это кэшированное хеш-значение, если оно есть.Я не уверен, что ассоциации в диктовке более "постоянны", чем ассоциации в явно основанном на индексе отображении, таком как это или лиздексия.Они оба на основе индекса, в конце концов, не так ли? Но , я полагаю, что выше будет использовать меньше памяти для больших карт, чем диктант сопоставимого размера.

2 голосов
/ 03 июня 2011

Я скажу, что такая вещь не существует, не полагаясь на постоянство.

Мы можем исключить любое отображение на основе позиции в списке.Давайте начнем с N имен и 1 цвета - это означает, что все имена отображаются в один цвет.Если позже у нас будет N имен и M цветов, если мы не сможем сохранить, какие имена N соответствуют этому первому цвету, мы не сможем сделать эту работу.

Аналогично, мы можем исключить что-либо, основываясь на значенияхимена / цвета.Предположим, у нас есть некоторая функция f (имя, цвет), которая предоставляет оценку, по которой можно выбрать лучший цвет для имени.Если F (боб, зеленый)> F (боб, красный), тогда мы получим другое отображение, когда наши списки перейдут от [bob], [red] до [bob], [green, red].

. Вы можете придумать некоторые вырожденные решения дляэто не явно "сохранить каждую ассоциацию", но все еще сохраняется достаточно состояние, чтобы воссоздать расчет.В лучшем случае это будет хранить столько же данных, сколько просто ваше отображение.В худшем случае они будут хранить гораздо больше.

Использование идемпотента предполагает, что ваш первоначальный вопрос может быть абстрактным любопытством.Если есть конкретная практическая проблема, которую вы пытаетесь решить, поможет более конкретное объяснение проблемы.

2 голосов
/ 03 июня 2011

Если я понимаю ваш вопрос, вам нужен объект, который будет принимать список ключей и список значений (любой или любой длины) и возвращать словарь, который всегда имеет одинаковые значения независимо от каких-либо дополнений.

>>> 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
>>> 

Надеюсь, это то, что вы имели в виду под «на лету».

2 голосов
/ 03 июня 2011

Я не совсем уверен, что вы подразумеваете под «оперативным способом сделать это», но я думаю, что идемпотентный словарь может помочь. Например:

##!/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 голосов
/ 03 июня 2011

Вы правы, что вам просто нужно хранить ассоциации в памяти.Вам нужен такой набор ассоциаций, который является изменяемым.В 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'
...

Надеюсь, что поможет

0 голосов
/ 07 августа 2018

Предположим, f("Sarah") == "Black", как только вы расширили свой набор значений.Остается вопрос: какое значение было f("Sarah") до того, как вы расширили свои значения?Если это было "Black", то результат вашей хеш-функции не попадает в набор допустимых значений.Если это не "Black", то ваша хеш-функция не поддерживает свое отображение при расширении наборов.Ваша хеш-функция должна была бы помнить значение, которое она дала любому данному ключу, и вы застряли с картой в этой точке.Что я не считаю плохим решением.Но это больше не хэш-функция.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...