计算线性不等式的解

时间:2009-12-28 17:20:34

标签: algorithm math linear counting inequalities

我正在尝试解决一个问题,我已经减少了计算整数解决方案的数量到一些线性不等式。我需要能够计算任意数量的变量c_1,...,c_n的解的数量,但是对于n = 3,方程可以写成:

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

现在,我事先知道n和r的值,并希望找到存在的(c_1,...,c_n)解的数量。

这可以有效地完成(比枚举解决方案更快)吗? (如果是这样:怎么样?如果没有:为什么?)

4 个答案:

答案 0 :(得分:1)

为了解决这个问题,我可能会进入约束编程的领域。看起来你有一个经典的all different约束(有点像N-Queens问题)。请参阅下面列出的免费约束求解器之一。这将为您提供非常有效的解决方案。它基本上会生成整个搜索树,但是在那里有很好的All-Different约束实现,树最终会被修剪到几乎没有。

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

答案 1 :(得分:0)

这不是你问题的完全解决方案,但我认为它可能有所帮助,或者至少可以给你一些想法。

您对解决方案是整数的要求使这成为NP问题。如果我们首先考虑问题的放宽以使域是实数,则要求求解0 <= A * c <= 1的可满足性问题,其中A是矩阵,c是你的向量未知数。这是一个标准的线性程序(具有平凡目标的LP),并且可以有效地解决(在多项式时间内)。您可能希望将此作为首次通过测试来确定可行性,因为如果松弛LP没有解决方案,则您的整数LP当然没有解决方案。如果可能的话,一个好的LP求解器也将返回一个可行点,并且你可以对向量的条目进行舍入以找到整数解。

答案 2 :(得分:0)

假设您有一些代码来生成所有解决方案。

(对于参数 z ,请传递9.这是不等式右侧的数字。请注意,此代码仅在 r 为正时才有效。)

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)

一些可能的改进:

  • 如果确实 c 1 ... c n 总是≤ z ,然后检测到并立即返回0将对大型 n 有很大帮助。事实上,它会减少闪电般的O( nz )的运行时间。

  • 如果打算 c 1 ... c n 都是非否定,那就更好了。对min_ckmax_ck进行适当的更改会使此O( nz )具有较小的常量,并且缓存可以是平面2D数组而不是较慢的哈希表我有。

  • 您可以通过系统地构建缓存来做得更好,而不是按照此备忘录代码的方式“按需”填充它。首先为n = 1构建整个缓存,然后为n = 2构建整个缓存,依此类推。这样你可以避免递归,并且在每一步都可以丢弃你不再需要的缓存数据(在计算n = 2的结果之后,你不再需要n = 1的条目)。

答案 3 :(得分:0)

正如其他人所提到的,如果你想根据这些约束最大化线性目标函数,那么你会遇到integer linear programming问题,因为没有有效的通用解决方案。相反,你似乎要求feasible region中的点数,这是一个不同的问题,但它也因为必须有整数解决方案而变得复杂。

我能想到的最好的想法是找到可行区域边界上的点,然后用它们来确定内部的点数。这适用于加速较低维度的“计算格点”类型问题,但边界仍然只比所讨论的体积小一维。如果你的问题超过了几个维度,那么问题仍然是棘手的,即使它比枚举所有解决方案更快。