Обрамление неопределенного горизонта MDP, минимизация затрат с помощью обязательных к посещению состояний

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

Нам дан бюджет b и матрица затрат M, которая представляет вычет за путешествие между состояниями (Mij представляет стоимость путешествия из i в j), аналогично классической задаче коммивояжера. В этом случае время может быть отрицательным, представляя пополнение бюджета, Mij не обязательно может быть равно Mji. Также в этом случае M равно 4x4, но это может быть не так, обычно допустим, что 3 ‹= len(M) ‹= 5, поскольку я предполагаю, что это может потребовать некоторых методов грубой силы с большим количеством операций...

0 1 -3 1
6 0 2 2
4 2 0 3
1 2 3 0

Каждая строка представляет заданное состояние i, а каждый столбец — стоимость перехода к j.

Таким образом, проблема заключается в следующем: при заданных b и M, за исключением состояния = 0 и состояния = len (M), каково максимальное количество уникальных состояний, в которые мы можем перейти и оказаться в состоянии = len (M) с b> = 0.

Учитывая приведенное выше M и b = 0, я думаю, что вывод будет [2], путь — [0] -> [2] -> [3], а полное развертывание выглядит следующим образом:

state nextstate deduction time
 --       0        ---     0
 0        2         -3     3
 2        3         3      0

Я попытался решить эту проблему как проблему обучения с подкреплением с помощью электронного жадного решения и эвристической политики для выбора следующего состояния, я также думал об этом как о TSP и рассмотрел решение с использованием Флойда-Уоршалла, но я совершенно уверен, как соответствовать ограничениям в рамках проблемы.

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

Некоторые направления приветствуются в том, как это четко сформулировать и придумать прямое решение.


person Kory    schedule 17.10.2016    source источник


Ответы (1)


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

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

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

Здесь не уточненная реализация вышеизложенного:

M = [
    [0, 2, 2, 2, -1],
    [6, 0, 2, 2, -1],
    [6, 3, 0, 2, -1],
    [6, 3, 2, 0, -1],
    [6, 3, 2, 2, 0]
]

BUDGET = 1
SOURCE = 0
DEST = len(M) - 1

def all_paths_step(source, dest, visited, cost):
    for i in range(len(M)):
        if i in visited:
            continue
        new_cost = cost + M[source][i]
        new_set = visited | {i}
        if i == dest:
            yield new_set, new_cost
        elif i not in visited:
            yield from all_paths_step(i, dest, new_set, new_cost)

def all_paths(source, dest, cost=0):
    result = {}
    for states, cost in all_paths_step(source, dest, frozenset([source]), cost):
        result[states] = min(result.get(states, float('inf')), cost)

    return result

to_dest = all_paths(SOURCE, DEST)
loops = {}

for i in range(len(M)):
    if i == DEST:
        continue
    for states, cost in all_paths(i, len(M) - 1, M[len(M) - 1][i]).items():
        loops[states] = min(loops.get(states, float('inf')), cost)

possible = {visited: cost for visited, cost in to_dest.items() if cost <= BUDGET}

process = set(possible.keys())
while process:
    process_next = set()
    while process:
        states = process.pop()
        for path, cost in loops.items():
            cost += possible[states]
            new_states = states | path
            if path <= states or cost >= possible.get(new_states, BUDGET + 1):
                continue
            possible[new_states] = cost
            process_next.add(new_states)
    process = process_next

print('result: {}'.format(max(possible, key=len)) if possible else 'none')

Вывод, посещенные состояния в произвольном порядке:

result: frozenset({0, 2, 3, 4})
person niemmi    schedule 17.10.2016
comment
Спасибо вам за быстрый ответ. Это отличный способ поставить задачу и хорошее использование наборов. Кажется, он работает во многих лучших тестовых примерах, которые я пробовал... но, похоже, он не работает, если вам нужно перейти в конечное состояние до состояний коллекции... [[0, 2, 2, 2 , -1], [6, 0, 2, 2, -1], [6, 3, 0, 2, -1], [6, 3, 2, 0, -1], [6, 3, 2 , 2, 0]] с бюджетом, равным 1, даст результат frostset([1]), но есть более оптимальное решение, которое дало бы frostset([2,3]). Это будет путь (с бюджетом в скобках): 0 (b=1) -> 4 (b=2) -> 2 (b=0) -> 4 (b=1) -> 3 (b=-1) ) -> 4 (b=0) - person Kory; 17.10.2016
comment
Да, вы правы, я думал, что каждое состояние нужно посещать только один раз, хотя это явно не упоминалось в описании. Просто чтобы уточнить, может ли алгоритм посещать несколько раз в любом из состояний, включая начало и конец? - person niemmi; 17.10.2016
comment
Извините, это было не явно. Да, вы можете посетить любое состояние (в том числе state=len(M)) столько раз, сколько захотите, и матрица стоимости не изменится. - person Kory; 17.10.2016
comment
@Kory: Предпринял еще одну попытку, основываясь на дополнительной информации. Хотя код довольно беспорядочный, надеюсь, объяснение имеет смысл. - person niemmi; 17.10.2016
comment
Спасибо за помощь. Это выглядит великолепно и, похоже, работает в большинстве моих случаев. Я сделал это функцией, добавил средство проверки отрицательного цикла отрицательные циклы = {путь: стоимость пути, стоимость в loops.items(), если стоимость ‹ 0}, которая, кажется, работает, улавливая критический отрицательный цикл в простом тестовом примере. - person Kory; 18.10.2016
comment
@Kory Не могли бы вы предоставить ссылку на проблему или, если она не является общедоступной, скопируйте и вставьте исходное задание на вопрос? - person niemmi; 20.10.2016