LINUX.ORG.RU

[python]Как лучше всего организовать структуру данных

 


0

1

В процессе участия в google ai contest в рамках увеличения скилла в программировании возник вопрос:
У меня для каждого объекта есть несколько списков, каждый элемент которых состоит из пар вида (ссылка на соседний объект, параметр1(численное значение)), (ссылка на соседний объект, параметр2(численное значение)) и.т.д.[например объект планета, а параметр расстояние до другой планеты или половина расстояния + константа] Где сами объекты повторяются(в каждом из листов), а параметры разные. Можно было бы сделать класс вида
class neighbour():
self.reference
self.parameter1
self.parameter2
self.parameter3
и потом формировать каждый раз нужный список из нужных атрибутов объектов, но проблема в том, что эти списки должны быть отсортированы по значению параметров(сортировать желательно 1 раз, тк параметры не меняются). Есть ли какой-нибудь способ их хранить красиво и эффективно или списки кортежей - оптимально?



Последнее исправление: gameover__ (всего исправлений: 2)

Сразу должен сказать, что полностью распарсить ваш вопрос мне не удалось.

Нужно связать объект с некоторыми другими объектами и задать некоторые параметры для этих связей, так? Если для связи с отдельным объектом имена параметров уникальны, делаем следующим образом:

from collections import defaultdict

class Foo(object):
    
    def __init__(self):
        self.neighbours = defaultdict(dict)
# Тогда

f = Foo()
f.neighbours['n1']['param1'] = 'val1'
f.neighbours['n1']['param2'] = 'val2'
f.neighbours['n2']['param1'] = 'val3'

f.neighbours будет таким:
{
    'n1': {
        'param1':'val1',
        'param2':'val2'
    },
    'n2': {
        'param1':'val3'
    }
}

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

shylent
()
Ответ на: комментарий от shylent

Спасибо за ответ. Пока не было времени хорошенько разобраться в том как это работает(имеется в виду defaultdict и двухуровневые dict). Чтобы было понятнее что мне надо - дам пример. У меня есть объект планета, где в каждом есть список пар типа (соседняя планета, расстояние до соседней планеты), мне надо отсортировать этот список по расстоянию до соседа и отослать корабли на планету(соседа) с минимальным расстоянием. Надо сказать, что эти расстояния не меняются со временем. Тут вроде все просто и списки работают успешно, если же мы будем хранить все в словарях, то каждый раз как мы захотим отослать корабли на ближайшую планету нам опять придется сортировать по расстояниям(или не так?). Так вот у меня таких параметров, типа как расстояние до соседа, много и в каждом случае надо сортировать(в рамках решения задачи оптимизации) и желательно это делать 1 раз. Я с виду не вижу другого решения, кроме как хранения все в отдельных списках, но смотрится оно ужасно.

gameover__
() автор топика
# encoding: utf-8

class Planet:
    def __init__(self, name):
        self.name = name
        self._neighbours = []
        self._sort_cache = {}

    def add_neighbour(self, planet, params):
        self._neighbours.append((planet, params))
        if self._sort_cache:
            self._sort_cache = {}

    def neighbours_sorted_by(self, attrib):
        if attrib not in self._sort_cache:
            sort_key = lambda x: x[1][attrib]
            self._sort_cache[attrib] = sorted(self._neighbours, key=sort_key)
        return self._sort_cache[attrib]

    def __repr__(self):
        return self.name

earth = Planet('Зелмя')
earth.add_neighbour(Planet('Луна'), {'distance': 1, 'relations': 100})
earth.add_neighbour(Planet('Марс'), {'distance': 100, 'relations': 0})
earth.add_neighbour(Planet('Юпитер'), {'distance': 200, 'relations': 50})

print earth.neighbours_sorted_by('distance')
print earth.neighbours_sorted_by('relations')
tarc
()
Ответ на: комментарий от gameover__

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

from collections import defaultdict

class Planet(object):
    
    def __init__(self):
        self.neighbours = defaultdict(dict)
    
    # Можно, в принципе, сделать, и как отдельно стоящую функцию
    def index_distance(self):
        self.by_distance = [s[0] for s in 
                sorted(self.neighbours.iteritems(),
                       key=lambda (k,v): v['distance'])
            ]

p = Planet()
p.neighbours['a']['distance'] = 10
p.neighbours['b']['distance'] = 5
p.neighbours['c']['distance'] = 7

p.index_distance()
print p.by_distance

# ['b', 'c', 'a']

for neighbour in p.by_distance:
    ... # p.neighbours[n] и т.д.

# Можно с lazy evaluation (т.е. чтобы не считать, если не надо)
class Planet(object):
    
    def __init__(self):
        self.neighbours = defaultdict(dict)
        self._by_distance = None
    
    @property
    def by_distance(self):
        if self._by_distance is not None:
            return self._by_distance
        self._by_distance = [s[0] for s in 
                sorted(self.neighbours.iteritems(),
                       key=lambda (k,v): v['distance'])
            ]
        return self._by_distance

shylent
()

А можно я чуть оффтопом позанимаюсь?

Насколько продвинутый бот получается, какое место? :)

gizzka ★★
()
Ответ на: комментарий от shylent

Не годится.

1. Слшиком велики издержки на добавление и переименование параметров
2. Соседи так же должны быть полноценными объектами класса Planet, в твоем коде это не отражено
3. Нет нормального интерфейса, добавление соседей осуществляется через прямой доступ а атрибуту, что плохо т.к. чревато переписыванием всего клиентского кода при изменении требований

tarc
()
Ответ на: комментарий от tarc

Все правильно. Ваш код лучше.

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

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

shylent
()
Ответ на: комментарий от shylent

Спасибо, очень полезно оказалось спросить. Кстати, киньте пожалуйста ссылку на то как тут вставлять код.

gameover__
() автор топика
Ответ на: комментарий от gizzka

Мой бот, которого я засабмитил - с очень тупой стратегией: он просто отсылает нужное количество кораблей на ближайшую не свою планету(так для каждой своей планеты). Сейчас болтается в районе 1600-1700 мест. После этого времени не было им заниматься, сейчас у меня есть более продвинутый алгоритм для экспанда, но он пока с ошибками. Плюс есть несколько интересных идей. С другой стороны с образовательной точки зрения это довольно полезно, тк я кое-как, но разобрался с логгированием, постоянно приходится изучать подходы к стандартным задачам.

gameover__
() автор топика
Ответ на: комментарий от gameover__

Ну здорово :) Я тоже нормального бота писать начал, но до сих пор руки не доходят доделать. А в рейтинге болтается стартер_бот с измененным числом одновременных кораблей в воздухе. Долго висел около 1200го места, сейчас около 1900го, что все равно довольно забавно.

Постараюсь все-таки найти время и доделать.

gizzka ★★
()
Ответ на: комментарий от shylent

Не помню, чтобы кто-то на ЛОРе признавал недочеты в своем коде :)

Ок, тогда для торжества справедливости скажу, что в этой теме я не использовал defaultdict в своем решении потому что не знал о его существовании, а про то, что все нормальные программы должны работать на версиях питона >= 2.3 придумал и сказал тебе чтобы выкрутиться :(

tarc
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.