2009-06-23 18 views
4

J'ai une fonction qui s'appelle récursivement, et je veux détecter et terminer si elle entre dans une boucle infinie, c'est-à-dire être à nouveau appelée pour le même problème. Quelle est la meilleure façon de faire cela?Comment détecter une boucle infinie dans un appel récursif?

EDIT: Ceci est la fonction, et elle sera appelée récursivement avec des valeurs différentes de x et y. Je veux terminer si dans un appel récursif, la valeur de la paire (x, y) est répétée.

int fromPos(int [] arr, int x, int y) 

Répondre

6

Si la fonction est purement fonctionnelle, à savoir qu'il n'a pas d'effets d'état ou sur le côté, vous pouvez garder un Set des arguments (modifier: voir votre édition, vous garderait un ensemble de paires de (x, y)) qu'il a été appelé avec, et chaque fois il suffit de vérifier si l'argument actuel est dans l'ensemble. De cette façon, vous pouvez détecter un cycle si vous le rencontrez rapidement. Mais si l'espace d'argument est grand et que la répétition prend du temps, vous risquez de manquer de mémoire avant de détecter un cycle. En général, bien sûr, vous ne pouvez pas le faire parce que c'est le problème d'arrêt.

+3

oui, vient de réaliser son problème d'arrêt. Peut-être que je devrais mettre une prime là-dessus. : D – Pranav

+0

Cette méthode ne détecte pas s'il est légal de la fonction de s'appeler parfois avec les mêmes valeurs - alors que la méthode de récursion peut fonctionner pour le cas général. –

+0

Oh, et cela nécessite le surdéveloppement de la construction de l'ensemble et des entrées qui s'y trouvent. –

15

Une façon est de passer une variable depth d'un appel à l'autre, incrémenter chaque fois que votre fonction elle-même appelle. Vérifiez que depth ne dépasse pas un certain seuil. Exemple:

int fromPos(int [] arr, int x, int y) 
{ 
    return fromPos(arr, x, y, 0); 
} 

int fromPos(int [] arr, int x, int y, int depth) 
{ 
    assert(depth < 10000); 

    // Do stuff 

    if (condition) 
     return fromPos(arr, x+1, y+1, depth + 1); 
    else 
     return 0; 
} 
+0

Bon sang! Tu me bats! –

+0

+1 Battez-moi aussi. – Tom

+0

Je préférerais que la signature de la méthode reste la même. – Pranav

2

Un moyen facile serait de mettre en œuvre une des opérations suivantes:

passer la valeur précédente et la nouvelle valeur à l'appel récursif et faire votre première étape, une vérification pour voir si elles sont la même chose - c'est peut-être votre cas récursif. Passez une variable pour indiquer le nombre de fois que la fonction a été appelée, et limitez arbitrairement le nombre de fois qu'elle peut être appelée.

0

Si vous souhaitez conserver la signature de votre méthode, vous pouvez conserver quelques ensembles pour enregistrer les anciennes valeurs de x et y.

static Set<Integer> xs; 
static Set<Integer> ys;//Initialize this! 
static int n=0;//keeps the count function calls. 

int fromPos(int [] arr, int x, int y){ 

int newX= getX(x); 
int newY= getY(y); 
n++; 
if ((!xs.add(Integer.valueOf(newX)) && !ys.add(Integer.valueOf(newY))){ 

    assert(n<threshold); //threshold defined elsewhere. 
    fromPos(arr,newx,newy); 
} 
} 
+0

Qu'en est-il du cas où il ne se répète pas lors de l'appel suivant, mais après quelques appels? – Pranav

+0

@Pranav. Tu as raison. édition – Tom

+0

Est-ce que le commentaire downvoter? – Tom

0

On dirait que vous travaillez sur un tableau 2D. Si vous avez un peu plus à perdre dans les valeurs du tableau, vous pouvez l'utiliser comme un drapeau. Vérifiez-le et terminez la récursion si le drapeau a été défini. Ensuite, réglez-le avant de continuer.

Si vous n'avez pas un peu de marge dans les valeurs, vous pouvez toujours en faire un tableau d'objets à la place.

+0

Cela a été utile, merci. – Pranav

2

Vous pouvez seulement détecter les plus triviaux en utilisant l'analyse de programme. Le mieux que vous pouvez faire est d'ajouter des gardes dans votre situation particulière et passer un contexte de niveau de profondeur. Il est presque impossible de détecter le cas général et de différencier l'utilisation légitime des algorithmes récursifs.

3

Vous devrez trouver une solution de rechange, car comme vous l'avez demandé, il n'y a pas de solution générale. Voir le Halting problem pour plus d'informations.

1

Vous pouvez utiliser la surcharge pour une signature cohérente (ce qui est la meilleure méthode), ou vous pouvez utiliser une variable statique:

int someFunc(int foo) 
{ 
    static recursionDepth = 0; 
    recursionDepth++; 
    if (recursionDepth > 10000) 
    { 
     recurisonDepth = 0; 
     return -1; 
    } 
    if (foo < 1000) 
     someFunc(foo + 3); 
    recursionDepth = 0; 
    return foo; 
} 

réponse de John Kugelman avec surcharge est mieux beacuse il est thread-safe, alors que statique les variables ne le sont pas.

Billy3

0

à mon humble avis boucles ne peut aller dans une boucle infinie.

Si votre méthode a trop de niveau de récursivité la machine virtuelle Java lancera une StackOverflowError. Vous pouvez intercepter cette erreur avec un bloc try/catch et faire ce que vous avez l'intention de faire lorsque cette condition se produit.

Questions connexes