Минимальная факторизация стоимости в абелевых группах

У меня есть определенная проблема оптимизации, и мне интересно, есть ли разумный подход для ее решения. (Это, возможно, хорошо изучено, и я просто не знаю, какое имя искать в нем.)

У меня есть (EDIT: free) конечно порожденная абелева группа, G, на n генераторах. У меня также есть набор P элементов G, каждый из которых помечен строго положительной стоимостью. Все генераторы G появляются в P, поэтому всегда можно выразить любой элемент G как произведение элементов P или их обратных. Стоимость любого такого продукта представляет собой сумму затрат на элементы P, которые появляются в нем, с учетом того, как часто они появляются. Стоимость нулевого произведения, которая выражает элемент идентификации G, равна нулю.

Учитывая элемент группы, я хотел бы найти продукт с минимальной стоимостью, который выражает его в терминах элементов P.

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

Может быть, один из этих переводов - это путь? Или добавляет ли дополнительная структура этой проблемы более простой способ сделать это? В моих реальных проблемах 5 <= n <= 10 и элементах, которые меня интересуют, никогда не бывает кратностей ни одного из генераторов, больших примерно +/- 20.

Я работаю в Haskell, поэтому функциональные подходы будут предпочтительнее для состояния, но с точки зрения состояния тоже все в порядке.

Ответ 1

Предупреждение: неподтвержденный псевдокод

This is pseudocode.  It isn't finished and probably won't even compile.

minCost :: element -> [generator] -> number -> Maybe [generator]
minCost _ [] _ = Nothing
minCost x _ c | (elemCost x) + c > cutoff = Nothing
minCost e _ _ = Just [] -- The factorization of the identity is the nullary product.
minCost x gs _ | elem x gs = Just [x]
minCost x gs _ | elem x ps = Nothing -- in P but not in gs.
minCost x gs c  =
  argmin
    pathCost
    [maybeCons g (minCost (x-g) [h| h <- gs, h <= g, h /= -g] (c+(elemCost g))) | g<-gs]

maybeCons :: a -> Maybe [a] -> Maybe [a]
maybeCons _ Nothing = Nothing
maybeCons x (Just xs) = Just (x:xs)

elemCost :: element -> number
pathCost :: [element] -> number
pathCost = sum . map elemCost

argmin :: (a -> n) -> [Maybe a] -> Maybe a
-- Return the item with the lowest cost, or Nothing if there isn't one.

Здесь немного рутины, но логика должна, я надеюсь, быть ясной. Мы должны наложить произвольное полное упорядочение на P и argmin должно обрабатывать результаты Nothing, представляя, что нет способа генерировать x из этого подмножества P. Мой псевдокод не имеет достаточно правильного синтаксиса для этого, для удобства чтения.

Исключение h > g из разрешенных генераторов безопасно, потому что любое решение, содержащее h, будет найдено ветвью minCost (x-h), вплоть до перестановки (и G является абелевой, поэтому любые решения, которые являются перестановками, эквивалентны). Исключение -g является безопасным, потому что g + (-g + a) = a, но при более высокой стоимости, поэтому такое решение не может быть оптимальным.

Алгоритм нуждается в способе обрезания ветвей, например, когда P = {1, -1, i, -i}, тестирование (2 + i) {1, -1, -i}, (2 + 2i) {1, -1, -i}, ad infinitum. Это, вероятно, требует обрезки поиска, когда стоимость превышает отсечку. С этим исправлением он заканчивается, потому что каждая рекурсия уменьшает размер gs или количество шагов до тех пор, пока х не уменьшится до генератора, пока не достигнет одного из базовых случаев, или стоимость не будет превышать порог. (Это может быть улучшено путем переноса наименьшей стоимости, рассчитанной на любую параллельную ветвь до сих пор.) Он не может повторить вычисление, потому что мы исключили инверсии всех предыдущих шагов в пути.

запоздалые мысли

Говоря, что e генерирует себя, даже если не в P неверно по требованиям и ненужно для правильности: алгоритм никогда не добавляет элемент к его собственному обратному, поэтому это может произойти только в том случае, если мы явно спросим, ​​как сгенерировать идентификатор. И что действительный запрос: сложные корни единства?

О дальнейшем размышлении, спасибо за предложение представить идентичность как нулевой продукт. В противном случае мы потерпим неудачу, потому что мы никогда не будем проверять генераторы против их инверсий! Он тоже имеет нужный тип!

Вот пример, чтобы сделать возвращаемый тип [[generator]] вместо Maybe [generator] и вернуть все оптимальные производные, представляющие Nothing как []. Определение maybeCons станет просто map ((:)g). Тем не менее, это рискует экспоненциальным раздутием, если есть много одинаково дешевых путей.

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

Конкретная структура решетки вашей группы может предложить больше способов обрезать, хотя я не думаю о других вообще. Например, для комплексных целых чисел при добавлении мы можем легко обнаружить, что два (не более) генератора должны быть как раз от реального и мнимого коэффициентов. Во многих группах мы можем легко обнаружить, что что-то не является продуктом конкретного генератора, из которого подмножество G оно находится. Это могут быть дополнительные охранники, которые возвращают хвост с соответствующим подмножеством gs.

Тип generator должен быть таким же, как element или его экземпляр из-за функции стоимости. Отношение упорядочения может быть определено только для генераторов, или их структура может быть проще. Это может иметь другое имя, если группа имеет естественный порядок, который оказывается менее эффективным для алгоритма.

Я оставлю в примечании, что код не должен компилироваться, потому что я уверен, что написал хотя бы одну ошибку.