pas de jeu! strong> Je ne peux pas utiliser des ensembles car: p> Utiliser uniquement les points d'extrémité des plages, existe-t-il un moyen optimal de soustraire deux listes de gammes? P>
Exemple: H3>
r1 = (1, 1000), (1100, 1200)
r2 = (30, 50), (60, 200), (1150, 1300)
r1 - r2 = (1, 29), (51, 59), (201, 1000), (1100, 1149)
7 Réponses :
Je pense que j'ai mal compris la question, mais ce code fonctionne si alors, vous faites. : p> Mise à jour forte>: Remarque Le dernier intervalle de R2 code> est un sous-ensemble de
R1 code>
R2 code> est différent de fonctionner comme je l'entends. P>
>>> r1 = RangeSet(((1, 1000), (1100, 1200)))
>>> r2 = RangeSet([(30, 50), (60, 200), (1150, 1200)])
>>> r1 - r2
RangeSet: [(1, 29), (51, 59), (201, 1000), (1100, 1149)]
>>> r1.sub(r2)
>>> r1
RangeSet: [(1, 29), (51, 59), (201, 1000), (1100, 1149)]
La réponse attendue n'inclut pas (1181, 1200) code>
@Dan c'est un ensemble différent. Il me semble étrange de ne pas accepter de se chevaucher au milieu, mais faites-le à la fin
Oh, vous avez utilisé un exemple qui était trop semblable à l'exemple donné et qui m'a confondu; J'ai considéré que c'était exactement la soustraction d'intervalle: (1100, 1200) - (1150, 1300) = (1100, 1149) code>.
Ceci est plutôt laid, mais cela fonctionne pour l'exemple donné impressions: p>
Une solution (en plus de toutes les autres solutions différentes présentées ici) consiste à utiliser un arbre d'intervalle / segment (ils sont vraiment la même chose):
http://fr.wikipedia.org/wiki/segment_tree p>
http://fr.wikipedia.org/wiki/interval_tree P>
Un grand avantage de le faire de cette façon est que c'est qu'il est Trivial pour faire des opérations booléennes arbitraires (pas seulement la soustraction) en utilisant le même morceau de code. Il existe un traitement standard de cette structure de données à De Berg. Pour effectuer une opération booléenne sur une paire d'arbres d'intervalle (y compris la soustraction), vous venez de les fusionner ensemble. Voici un code python (certes naïfs) pour le faire avec des arbres de plage déséquilibrés. Le fait qu'ils soient déséquilibrés n'a aucun effet sur le temps nécessaire pour fusionner les arbres, mais la construction arborescente ici est la partie vraiment idiote qui finit par être quadratique (à moins que la réduction ne soit exécutée par partitionnement, que je doute en quelque sorte). Quoi qu'il en soit, vous allez: P>
#Intersection merge(r1, r2, lambda a, b : a and b) #Union merge(r1, r2, lambda a, b : a or b) #Xor merge(r1, r2, lambda a, b : a != b)
@senderlele: merci! Techniquement, la structure de données est un arbre BSP 1D. Cet algorithme de fusion est en réalité basé sur celui que j'ai publié lorsque j'étais sous-traitant. Voici un lien PDF: Sal-CNC. me.wisc.edu/...
Le L'intervalle package peut fournir tout ce dont vous avez besoin.
from interval import Interval, IntervalSet r1 = IntervalSet([Interval(1, 1000), Interval(1100, 1200)]) r2 = IntervalSet([Interval(30, 50), Interval(60, 200), Interval(1150, 1300)]) print(r1 - r2) >>> [1..30),(50..60),(200..1000],[1100..1150)
Oui, même si je ne sais pas à quel point ce code semble être bien soutenu. Il dit que c'était la dernière mise à jour il y a 6 ans ...
Cela n'a peut-être pas été mis à jour en partie car il n'a pas besoin d'être: il semble être bien écrit avec de nombreux tests. Et, comme il est open source, vous pouvez le maintenir vous-même.
Merci! C'est génial et m'aurait sauvé environ deux jours de travailler à travers toutes les exceptions dans le script que j'ai écrit. Fyi à tous les autres, pour obtenir le nombre le plus bas ou le plus élevé dans un intervalle, il suffit de faire intervalle.Low_bound ou intervalle.upper_bound.
Qu'en est-il de l'exigence de non-série? Est-ce que Intervalet création O (n)?
C'est une belle solution mais malheureusement, ne fonctionne pas pour Python3 ...
Lors de l'essai de travailler avec cela dans Python 2, il donne l'erreur suivante: TypeError: '<< non pris en charge entre les instances d'intervalle "et" intervalle ". Une solution pour cela?
idem sur python3
Voici une fonction rapide Python qui fait la soustraction, que les listes initiales soient bien formées (c.-à-d. Tournez les listes dans la liste la plus petite des plages équivalentes, triées, avant de faire la soustraction):
def condense(l): l = sorted(l) temp = [l.pop(0)] for t in l: if t[0] <= temp[-1][1]: t2 = temp.pop() temp.append((t2[0], max(t[1], t2[1]))) else: temp.append(t) return temp def setSubtract(l1, l2): l1 = condense(l1) l2 = condense(l2) i = 0 for t in l2: while t[0] > l1[i][1]: i += 1 if i >= len(l1): break if t[1] < l1[i][1] and t[0] > l1[i][0]: #t cuts l1[i] in 2 pieces l1 = l1[:i] + [(l1[i][0], t[0] - 1), (t[1] + 1, l1[i][1])] + l1[i + 1:] elif t[1] >= l1[i][1] and t[0] <= l1[i][0]: #t eliminates l1[i] l1.pop(i) elif t[1] >= l1[i][1]: #t cuts off the top end of l1[i] l1[i] = (l1[i][0], t[0] - 1) elif t[0] <= l1[i][0]: #t cuts off the bottom end of l1[i] l1[i] = (t[1] + 1, l1[i][1]) else: print "This shouldn't happen..." exit() return l1 r1 = (1, 1000), (1100, 1200) r2 = (30, 50), (60, 200), (1150, 1300) setSubtract(r1, r2) #yields [(1, 29), (51, 59), (201, 1000), (1100, 1149)]
C'était un problème intéressant!
Je pense que cela a raison, et c'est assez compact. Il devrait fonctionner avec des gammes qui se chevauchent de toutes sortes, mais cela assume des gammes bien formées (c'est-à-dire [x, y) code> où
x code>). Il utilise
[x, y) code> gammes de style pour simplicité. Il est basé sur l'observation qu'il n'y a vraiment que six arrangements possibles (avec des résultats dans ()): p>
>>> r1_list = [(1, 1001), (1100, 1201)]
>>> r2_list = [(30, 51), (60, 201), (1150, 1301)]
>>> print multirange_diff(r1_list, r2_list)
[(1, 30), (51, 60), (201, 1001), (1100, 1150)]
Les limites de ces gammes ne sont pas correctes. Ils sont hors-tête en raison du fait que les intrants sont fermés i> gammes (je sais parce que j'ai fait cette erreur moi-même aussi :).
@Mikola: Eh bien, le texte de la réponse indique que les intervalles utilisés sont fermés à gauche, à droite. Il est assez simple de convertir l'entrée à l'avance à l'avance de ce que cette réponse nécessite, puis convertissez la sortie par la suite à ce que les besoins de l'OP.
Oui, @john, exactement. @Mikola, j'ai conclu qu'il était plus idiomatique (en python) d'utiliser une plage à moitié fermée, code> -style; Pour obtenir l'entrée et la sortie de l'OP, convertissez les gammes ci-dessus en soustraits en soustrayant un du dernier numéro dans chaque cas - une opération triviale "laissée au lecteur" :).
Je serais d'accord. En fait, c'est ainsi que j'ai initialement écrit ma solution. Honnêtement, en utilisant des gammes fermées est probablement une erreur désastreuse (d'une perspective de conception / de débogage), mais c'est ce que la puissance posée pour que je sentais que je sentais que je devrais obliger.
La représentation est absolument génie!
Question amusante! Une autre mise en œuvre, bien que vous ayez déjà beaucoup. C'était intéressant de faire! Implique une «décoration» supplémentaire pour faire ce que je vais plus explicite.
r1 = (1, 1000), (1100, 1200) r2 = (30, 50), (60, 200), (1150, 1300) >>> subtract_ranges(r1,r2) [(1, 29), (51, 59), (201, 1000), (1100, 1149)]
Et si quelque chose dans
r2 code> n'est pas dans
r1 code>?
Est-ce que nous traitons uniquement d'entiers? Et si nous avons
r1 = (1.5, 5.1) code> et
r2 = (2.5, 4.3) code>? Comment devrions-nous faire face exactement à cela?
@Jbernardo: R2 peut contenir des gammes qui n'ont aucun chevauchement avec R1
@JMLOPEZ: Personnellement, je n'uimerai que des entiers. J'ai un FXN qui le fait maintenant pour soustraire des séquences de génome. Je suis curieux s'il y a une façon optimale de le faire.
FWIW, je travaille sur (dans le cadre d'un projet plus vaste - et cette partie n'a pas été examinée dans un moment) une implémentation d'une classe qui semble faire ce que vous voulez ...