Algorithme Java pour trouver l'intersection entre les intervalles

j'ai un intervalle de temps comme ceci:

[5,10]

et j'ai plus de liste de point dans le temps, avec des longueurs différentes, par exemple:

t1=[3,6,9,10] 
t2=[2,4,5,6,10]
..

t1 [3,6] est le premier intervalle, [6,9] la deuxième et ainsi de suite.

Même chose pour t2 et autre liste.

maintenant je dois sauvegarder la liste, et l'intervalle spécifique, qui se croisent avec le premier intervalle de temps. Par exemple, dans t1 j'ai [3,6] qui se croisent sur [5,10], [6,9] que croisent [5,10], etc.

j'ai fait un algorithme mais je travail avec plus de données, et j'ai besoin d'un algorithme rapide. Par exemple, si je travaille avec 300.000 liste et chaque liste ont 200 points de temps mon algorithme 1 amende dans environ 5-10sec. Mais si j'ai 10.000 ou plus de temps l'algorithme est très lent.

Mon algorithme est comme ceci:

First time interval <- [t1,t2]
For each list
  For each time interval [s1,s2] in list
     if(s1>= t1 && s2 <= t2)
     {
         saveIntervall()
     }
     else if (s1<= t2 && s2 > t2)
     {
         saveIntervall()
     }
     else if(s1 <  t1 && s2 >= t1)
     {
        saveIntervall()
     }
     else if (s1 < t1 && s2 > t2)
     {
        saveIntervall()
     }

Edit1: Oui j'ai commandé liste.

Edit2: j'ai un autre problème, quand je trouve l'intersaction je dois calculer un score entre 0 et 1 de la façon dont l'intersection est grande. J'utilise ceci:

            double score = 0.0d;
        if(s1>= t1 && s2 <= t2)
        {
            score = (s2-s1) / (t2-t1);
        }
        else if(t2 >= s2 && t1 > s1)
        {
            score = (s2-t1) / (t2-t1);
        }
        else if(t2 < s2 && t1 <= s1)
        {
            score = (t2-s1) / (t2-t1);
        }
        else
        {
            score = 1;
        }

je peux accélérer ce trop?

8
demandé sur Neptune 2014-07-01 17:16:28

3 réponses

tout d'abord, votre structure de données est confuse - si vous essayez de parler d'intervalles de temps discrets, structurez vos données comme cela; par exemple int[][] où le tableau intérieur est toujours de longueur 2, donc votre t1 devient:

int[][] t1 = {{3,6}, {6,9}, {9,10}};

utiliser la bonne structure vous aidera probablement à simplifier votre algorithme et à le rendre plus facile à utiliser.


Mieux que bien structuré, tableaux, cependant, serait d'utiliser un type dédié pour représenter ces intervalles, de sorte que vous pourriez passer autour List<Interval> des objets et faire une sorte de contient vérifier sur eux. Mais ne pas réinventer la roue. Le génial bibliothèque de goyaves fournit unRange classe que vous pouvez utiliser. Même mieux, il fournit également RangeSet et RangeMap des cours qui vous permettent de faire facilement les choses dont vous parlez. Voir aussi leur Fourchettes Expliquées article qui couvre les notions de base.

notez que vous pouvez facilement transformer votre design actuel en Range les objets en interne, si vous ne pouvez pas refonte de la structure du tableau de l'extérieur.

ayant essayé à un moment donné de construire le mien IntervalSet class, laissez-moi vous dire que c'est un problème délicat pour obtenir le droit et vous vous éviterez beaucoup de maux de tête à l'aide de leur bien conçu et très testé la gamme des utilitaires.

Voici la façon dont je ferais ce que vous êtes décrire avec de la goyave-notez que nous évitons même besoin de penser à propos de mathématiques impliqués - Range fait la bonne chose pour nous:

/**
 * Given a Range and an group of other Ranges, identify the set of ranges in
 * the group which overlap with the first range.  Note this returns a Set<Range>
 * not a RangeSet, because we don't want to collapse connected ranges together. 
 */
public static <T extends Comparable<?>> Set<Range<T>>
        getIntersectingRanges(Range<T> intersects, Iterable<Range<T>> ranges) {
    ImmutableSet.Builder<Range<T>> builder = ImmutableSet.builder();
    for(Range<T> r : ranges) {
        if(r.isConnected(intersects) && !r.intersection(intersects).isEmpty()) {
            builder.add(r);
        }
    }
    return builder.build();
}

/**
 * Given a 2-length array representing a closed integer range, and an array of
 * discrete instances (each pair of which therefore represents a closed range)
 * return the set of ranges overlapping the first range.
 * Example: the instances array [1,2,3,4] maps to the ranges [1,2],[2,3],[3,4].
 */
public static Set<Range<Integer>> getIntersectingContinuousRanges(int[] intersects,
        int[] instances) {
    Preconditions.checkArgument(intersects.length == 2);
    Preconditions.checkArgument(instances.length >= 2);
    ImmutableList.Builder<Range<Integer>> builder = ImmutableList.builder();
    for(int i = 0; i < instances.length-1; i++) {
        builder.add(Range.closed(instances[i], instances[i+1]));
    }
    return getIntersectingRanges(Range.closed(intersects[0], intersects[1]),
                                 builder.build());
}

à l'Aide de votre exemples:

public static void main(String[] args)
{
    int[] interval = {5,10};
    int[] t1 = {3,6,9,10};
    int[] t2 = {2,4,5,6,10};

    System.out.println(getIntersectingContinuousRanges(interval, t1));
    System.out.println(getIntersectingContinuousRanges(interval, t2));
}

ci-dessus affiche:

[[3‥6], [6‥9], [9‥10]]
[[4‥5], [5‥6], [6‥10]]
2
répondu dimo414 2014-07-01 22:29:18

L'intersection de deux intervalles [s1, s2] et [t1, t2] est vide si et seulement si:

    t2 < s1 or s2 < t1

donc pour deux intervalles pour vérifier si les deux se croisent ou non, vous n'avez besoin de faire que le test ci-dessus.

aussi une fois que vous savez que s2 < t1 alors il n'y a pas de raison de continuer plus loin sur la liste qui a apporté t1 puisque les plus grands intervalles ne se croisent jamais, ce qui signifie que vous devez aller de l'avant.

Naïf Psuedo Algorithme:

   given [s1, s2]
   for each list [t1, t2, ... t(n)] in search_lists
        for each interval [t(x), t(x+1)] from [t1, t2, ... t(n] (x goes from 0 to n-1)
           if t(x+1) < s1
              continue
           if s2 < t(x)
              break
           saveInterval()

cela peut être un peu amélioré pour vraiment utiliser le fait que [t1, t2,.. , t(n)] est trié.

première remarque que [s1, s2] croisera [t(x), t(x+1)]le forumt(x+1) >= s1 et s2 >= t(x)

cependant

if t(x) >= s1 then for every h>0      `t(x+h) >= s1` 

if s2 >= t(x) then for every h>0  `s2 >= t(x-h)`

donc si nous trouvons le plus petit i de sorte que t (i+1)>=s1 alors tous les intervalles de [t(i), t(i+1)] les sections situées à l'intersection satisfont à la première condition d'intersection, c.-à-d. ([t(i+1), t(i+2)] , [t(i+2), t(i+3)] ...)

et si nous trouvons le plus grand j de sorte que s2 >= t (j-1) alors tous les intervalles de [t(j-1), t(j)] à l'envers satisfont à la deuxième condition . i.e. ([t(j-2), t(j-1)],[t(j-3), t(j-2)] ...)

tous les intervalles entre i et j répondent aux deux critères et seulement à eux.

donc l'algorithme final est:

given [s1, s2]
for each list [t1, t2, ... t(n)] in search_lists
    find the smallest i such that t(i+1)>=s1  
    find the biggest  j such that s2>= t(j-1)

    if j>i then all the intervals between `{t(i)... t(j)}` intersect with [s1, s2]
    otherwise there is no intersection.       

Depuis i et j efficacement

EDIT2:

L'intersection de [s1,s2] et [t1, t2] est:

[max(s1, t1), min(s2,t2)]

les tailles sont: L1 = s2-s1 L2 = t2-t1 L3 = min(s2,t2) - max(s1,t1)

La partition que vous cherchez est: L3/ min(L2, L1) un score entre 0 et 1.

(min(s2,t2) - max(s1,t1)) / ( min(s2-s1, t2-t1) )

le coût du calcul est de 3 tests, 3 moins les opérations et une opération à virgule flottante. Mais je suppose que les intervalles sont valables et que l'intersection existe autrement plus d'essais sont nécessaire. (s2>s2,t2>t1 et min(s2,t2) > max(s1,t1). Le test final est le même le forum condition pour l'intersection de la discussion ci-dessus.

5
répondu odedsh 2014-07-01 16:55:17

étant donné la limite gauche et la longueur de deux intervalles, l'intersection peut être testée par le code suivant.

protected boolean intervalOverlap( int pos1, int length1, int pos2, int length2 ){
  int pos_A_left  = pos1;
  int pos_A_right = pos1 + length1;
  int pos_B_left  = pos2;
  int pos_B_right = pos2 + length2;
  return pos_B_left < pos_A_right && pos_A_left < pos_B_right;
}

Il y a un court article dans laquelle cette approche est discutée. De plus, une autre représentation des intervalles (en utilisant le centre et la longueur) est présentée, pour laquelle le test d'intersection peut être mis en œuvre plus efficacement.

0
répondu Codor 2014-07-01 18:28:21