2009-12-28 7 views
4

Я пытаюсь решить проблему, которую я сведя до подсчета числа integer решений ряда линейных неравенств. Мне нужно, чтобы иметь возможность подсчитать количество решений для любого числа переменных c_1, ..., c_n, но при п = 3 уравнения можно записать в виде:Подсчет решений линейных неравенств

The equations. http://silicon.appspot.com/readdoc?id=155604

Теперь, я знаю, значения n и r заранее и хотите найти количество существующих (c_1, ..., c_n) решений.

Можно ли это сделать эффективно (быстрее, чем перечисление решений)? (Если так: как ?; если нет: почему?)

+0

Вы показываете матрицу 4x3, умноженную на матрицу 3x1. Поэтому результатом является матрица 4x1. Что значит сказать, что матрица 4x1 имеет хотя бы ноль и не более девяти? – jason

+0

Пожалуйста, исправьте Q ... это напоминает мне смешанное целочисленное программирование, за исключением ... | Теперь, как насчет этого: лечить неравенства как полуплоскости, вычислить пересечение всех регионов - должен дать вам многоугольник. Если некоторые из его координат +/- бесконечность, то у вас либо есть бесконечное число, либо нулевые решения (я думаю). В противном случае найдите ограничительный прямоугольник. для многоугольника, сделайте два вложенных для него петель и проверьте, находится ли каждая точка внутри многоугольника (вам нужно будет триангулировать ее). Наихудший случай - ограничивающий прямоугольник O (n^2), но вы получаете O (n) решения. Вычислительная геометрия - ваш друг. –

+0

Hm ... в этом конкретном примере вам нужно только перечислить (0,0,0) ... (9,9,9) (если матрица имеет неотрицательные целые числа) –

ответ

0

Предположим, у вас есть код для создания всех решений.

(Для параметра г здесь, проходят 9. Это число на правой части неравенств. Обратите внимание, что этот код работает только тогда, когда г положительна.)

from math import floor, ceil 

def iter_solutions(r, n, z): 
    c = [None] * n 
    def iter_solutions_bounded(k, pick): 
     # pick is the last pick, if any, and 0 otherwise 
     assert (1 <= k < n and pick == c[k]) or (k == n and pick == 0) 

     min_ck = int(ceil(-pick/r)) 
     max_ck = int(floor((z - pick)/r)) 
     if k == 1: 
      for ck in range(max(min_ck, 0), min(max_ck, z) + 1): 
       c[0] = ck 
       yield c 
     else: 
      for ck in range(min_ck, max_ck + 1): 
       c[k - 1] = ck 
       for soln in iter_solutions_bounded(k - 1, ck): 
        yield soln 
    return iter_solutions_bounded(n, 0) 

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

from math import floor, ceil 

def memoize(f): 
    cache = {} 
    def g(*args): 
     if args in cache: 
      return cache[args] 
     tmp = cache[args] = f(*args) 
     return tmp 
    return g 

def len_range(a, b): 
    if a <= b: 
     return b - a 
    return 0 

def count_solutions(r, n, z): 
    @memoize 
    def count_solutions_bounded(k, pick): 
     min_ck = int(ceil(-pick/r)) 
     max_ck = int(floor((z - pick)/r)) 
     if k == 1: 
      return len_range(max(min_ck, 0), min(max_ck, z) + 1) 
     else: 
      return sum(count_solutions_bounded(k - 1, ck) for ck in range(min_ck, max_ck + 1)) 
    return count_solutions_bounded(n, 0) 

Некоторые возможные улучшения:

  • Если это правда, что с ... с п всегда ≤ г, а затем обнаружения, что и сразу же возвращаются 0 поможет много для больших n. Фактически это сократило бы время работы до молниеносного O (nz).

  • Если предполагается, что с ... с п все неотрицательными, что даже лучше. Внесение соответствующих изменений в min_ck и max_ck сделает этот O (nz) с меньшей константой, а кеш может быть плоской 2D-матрицей вместо более медленной хеш-таблицы, которую я получил.

  • Возможно, вам удастся добиться большего, создав кэш систематически, вместо того, чтобы заполнять его «по требованию», как это делает код memoization. Сначала постройте весь кеш для n = 1, затем для n = 2 и т. Д. Таким образом, вы можете избежать рекурсии, и на каждом шаге вы можете выбросить кэшированные данные, которые вам больше не нужны (после вычисления результатов для n = 2 вам больше не нужны записи для n = 1).

+0

Это кажется лучшим решением. Спасибо! – 2009-12-29 01:38:24

1

Чтобы решить эту проблему, я, вероятно, перейду в сферу программирования ограничений. Похоже, у вас есть классическое ограничение all different (немного похожее на проблему N-Queens). Пойдите в один из решателей свободного ограничения, перечисленных ниже. Это даст вам решение достаточно эффективно. В основном это генерирует все дерево поиска, но с красивыми реализациями All-Different constraint там, дерево будет обрезано почти до нуля.

http://www.gecode.org/ http://minion.sourceforge.net/ http://jacop.osolpro.com/ http://research.microsoft.com/apps/pubs/default.aspx?id=64335

Вот список Википедии:
http://en.wikipedia.org/wiki/Constraint_programming#Constraint_programming_libraries_for_imperative_programming_languages

+0

Моя единственная проблема заключается в том, что будет слишком много решений для подсчета больших значений n. – 2009-12-28 17:54:33

+0

Правда. Я бы сказал, что для 'N> 100' он будет довольно медленным, чтобы найти все решения, но я не вижу другого способа, который будет очень хорошо справляться с большими значениями N. :( – rui

0

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

Ваше требование, чтобы решения были целыми, делает эту проблему NP. Если мы сначала рассмотрим релаксацию задачи, так что область является вещественным числом, вы просите решить задачу выполнимости 0 < = A * c < = 1, где A - матрица, а c - ваш вектор неизвестных. Это стандартная линейная программа (LP с тривиальной целью) и может быть эффективно решена (в полиномиальное время). Вы можете использовать это как тест первого прохода, чтобы определить выполнимость, поскольку, если у расслабленного LP нет решений, у вашего целочисленного LP нет никаких решений. Хороший ресивер LP также вернет возможную точку, если это возможно, и вы сможете округлить записи вектора, чтобы найти целочисленное решение.

0

Как уже упоминалось выше, если вы хотите, чтобы максимально линейной целевой функции на основе этих ограничений, то вы имели бы integer linear programming проблему, для которой не существует эффективное общее решение. Вместо этого вы, похоже, запрашиваете количество баллов в feasible region, что представляет собой другую проблему, но это тоже сложно, если у вас есть целочисленные решения.

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