2009-12-28 17 views
4

J'essaie de résoudre un problème que j'ai réduit en comptant le nombre de nombres entiers solutions à un certain nombre d'inégalités linéaires. Je dois pouvoir compter le nombre de solutions pour un certain nombre de variables C_1, ..., c_n, mais pour n = 3, les équations pourrait être écrit:Solutions de comptage aux inégalités linéaires

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

Maintenant, je sais que la les valeurs de n et r à l'avance et souhaitent trouver le nombre de solutions (c_1, ..., c_n) qui existent.

Est-ce que cela peut être fait efficacement (plus rapidement que l'énumération des solutions)? (Si oui: comment?; Sinon: pourquoi?)

+0

Vous montrez une matrice 4x3 en cours de multiplication par une matrice 3x1. Par conséquent, le résultat est une matrice 4x1. Qu'est-ce que cela signifie de dire qu'une matrice 4x1 est au moins zéro et au plus neuf? – jason

+0

Veuillez corriger le Q ... cela me rappelle un peu la programmation mixte, sauf ... | Maintenant, qu'en est-il de ceci: Traitez les inégalités comme des demi-plans, calculez l'intersection de toutes les régions - devriez vous donner un polygone. Si certaines de ses coordonnées sont +/- infini, alors vous avez soit un nombre infini, soit zéro solution (je pense). Sinon, trouvez un rectangle de délimitation. pour le polygone, faites deux boucles d'imbrication dedans et vérifiez si chaque point est à l'intérieur du polygone (vous devrez le trianguler). Le pire des cas est O (n^2) mais vous obtenez des solutions O (n). La géométrie computationnelle est votre ami. –

+0

Hm ... dans cet exemple particulier, il suffit d'énumérer (0,0,0) ... (9,9,9) (tant que la matrice a des entiers non négatifs) –

Répondre

0

Supposons que vous ayez du code pour produire toutes les solutions.

(Pour le paramètre z ici, passer 9. Il est le numéro sur le côté droit des inégalités. Notez que ce code ne fonctionne que lorsque r est positif.)

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) 

Vous pouvez convertir cela en code qui compte les solutions simplement en supprimant tout le code qui se réfère à c et en additionnant le nombre de solutions qui auraient été cédées. Enfin, vous pouvez améliorer les performances par mémo.

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) 

Quelques améliorations possibles:

  • S'il est vrai que c ... c n sont toujours ≤ z, puis en détectant immédiatement et que le retour 0 aiderait beaucoup pour grand n. En fait, cela réduirait le temps de fonctionnement à un O rapide comme l'éclair (nz).

  • S'il est prévu que c ... c n tous être non-négatif, qui est encore mieux. Apporter les modifications appropriées à min_ck et max_ck rendrait cette O (nz) avec une constante plus petite, et le cache pourrait être un tableau 2D plat au lieu de la table de hachage plus lente que j'ai.

  • Vous pourriez être en mesure de faire mieux en construisant le cache systématiquement, plutôt que de le remplir "à la demande" comme le fait ce code de mémo. Commencez par créer le cache entier pour n = 1, puis pour n = 2, et ainsi de suite. De cette façon, vous pouvez éviter la récursivité et, à chaque étape, vous pouvez jeter les données en cache dont vous n'avez plus besoin (après avoir calculé les résultats pour n = 2, vous n'avez plus besoin des entrées pour n = 1).

+0

Cela semble la meilleure solution, merci! –

1

Pour résoudre ce problème, j'irais probablement dans les domaines de la programmation par contraintes. Il semble que vous ayez une contrainte all different classique (un peu comme le problème N-Queens). Essayez l'un des solveurs libres de contrainte listés ci-dessous. Cela vous donnera une solution assez efficacement. Cela va générer l'arbre de recherche dans son ensemble mais avec les bonnes implémentations de contraintes All-Different, l'arbre finira par être élagué à presque rien.

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

est Voici la liste de wikipedia:
http://en.wikipedia.org/wiki/Constraint_programming#Constraint_programming_libraries_for_imperative_programming_languages

+0

Ma seule préoccupation est qu'il y aura trop de solutions pour compter pour de grandes valeurs de n. –

+0

True. Je dirais que pour 'N> 100' il deviendra assez lent à trouver toutes les solutions mais je ne peux pas voir une autre façon de faire face aux grandes valeurs de N très bien. :( – rui

0

Ce n'est pas vraiment une solution complète à votre problème, mais je pense qu'il peut aider ou au moins vous donner quelques idées

Votre exigence que les solutions soient entières en fait un problème NP. Si nous considérons d'abord la relaxation du problème pour que le domaine soit les nombres réels, vous demandez de résoudre le problème de satisfiabilité de 0 < = A * c < = 1, où A est une matrice et c est votre vecteur d'inconnues. Ceci est un programme linéaire standard (LP avec objectif trivial), et peut être résolu efficacement (en temps polynomial). Vous voudrez peut-être l'utiliser comme test de premier passage pour déterminer la faisabilité puisque si le LP détendu n'a pas de solution, votre LP entier n'a certainement pas de solution. Un bon solveur LP retournera aussi un point faisable si possible, et vous pourrez peut-être arrondir les entrées du vecteur pour trouver une solution entière.

0

Comme d'autres l'ont mentionné, si vous voulez maximiser une fonction objectif linéaire en fonction de ces contraintes alors vous avez un problème integer linear programming, qui ne possède pas de solution générale efficace. Au lieu de cela, vous semblez demander le nombre de points dans le feasible region, ce qui est un problème différent, mais il est également compliqué de devoir avoir des solutions entières.

La meilleure idée que je peux trouver consiste à trouver les points sur la limite de la région possible, et à les utiliser pour déterminer le nombre de points à l'intérieur. Cela fonctionne bien pour accélérer les problèmes de type "compter les points du réseau" dans les dimensions inférieures, mais la limite est toujours juste une dimension plus petite que le volume en question. Si votre problème dépasse quelques dimensions, le problème sera toujours impossible à résoudre, même s'il est plus rapide que d'énumérer toutes les solutions.