Chaque fois que les gens posent des questions sur le problème de l'arrêt lorsqu'il se rapporte à la programmation, les gens répondent à "si vous avez simplement ajouté une boucle, vous avez le programme d'arrêt et vous ne pouvez donc pas automatiser tâche"
Logique. Si votre programme a une boucle infinie, alors lorsque votre programme est en cours d'exécution, vous n'avez aucun moyen de savoir si le programme est toujours en train de croquer, ou s'il s'agit simplement de boucler infiniment.
Mais une partie de cela semble contre-intuitive. Et si j'écrivais un solveur de problèmes d'arrêt, qui prend le code source comme entrée. rascher@localhost$ ./haltingSolver source.c
Si mon code (source.c) ressemble à ceci:
for (;;) { /* infinite loop */ }
Il semble que ce soit assez facile pour mon programme de voir cela. "Regardez la boucle et regardez la condition. Si la condition est basée sur les littéraux, et pas de variables, vous connaissez toujours le résultat de la boucle. S'il y a des variables (par exemple, (x <10)), voir si Ces variables sont jamais modifiées. Sinon, vous connaissez toujours le résultat de la boucle. "
Certes, ces chèques ne seraient pas triviaux (calculer les arithmétiques du pointeur, etc.), mais cela ne semble pas impossible. par exemple:
int x = 0
while (x < 10) {}
pourrait être détecté. avec - même pas trivialement:
int x = 0
while (x < 10)
{
x++;
if (x == 10)
{
x = 0
}
}
Maintenant, qu'en est-il de l'utilisateur? C'est le kicker, c'est ce qui fait un programme imprévisible.
int x = 0;
while (x < 10)
{
scanf("%d", &x); /* ignoring infinite scanf loop oddities */
}
Maintenant, mon programme peut dire: "Si l'utilisateur entre dans un 10 ou plus, le programme s'arrêtera. Sur toutes les autres entrées, elle se bougera à nouveau."
Ce qui signifie que, même avec des centaines d'entrées, un doit Pouvoir énumérer les conditions sur lesquelles le programme s'arrête. En effet, lorsque j'écris un programme, je m'assure toujours que quelqu'un a la capacité de le terminer! Je ne dis pas que la liste des conditions résultante est trivial Pour créer, mais cela ne me semble pas impossible. Vous pouvez prendre des informations à partir de l'utilisateur, les utiliser pour calculer les index du pointeur, etc. mais cela ajoute simplement au nombre de conditions pour que le programme se termine, ne rend pas impossible de les énumérer.
Alors, quel est exactement le problème d'arrêt? Qu'est-ce que je ne comprends pas l'idée que nous ne pouvons pas écrire de problème pour détecter des boucles infinies? Ou pourquoi "boucles" sont-elles un exemple aussi citées?
[~ # ~ ~] Mise à jour [~ # ~]
Donc, laissez-moi changer la question un peu: quel est le problème d'arrêt comme il s'applique aux ordinateurs ? Et puis je répondrai à certains des commentaires:
Beaucoup de gens ont déclaré que le programme devait être en mesure de traiter de "toute entrée arbitraire". Mais dans les ordinateurs, il n'y a jamais d'entrée arbitraire. Si je saisis seulement un seul octet de données, que je n'ai que 2 ^ 8 entrées possibles. Ainsi, comme exemple:
int c = getchar()
switch (c) {
case 'q':
/* quit the program */
}
Tout à coup, je viens de comptabiliser toutes les possibilités. Si c
a le motif de bits 0x71, il fait une chose. Pour tous les autres modèles, il fait autre chose. Même un programme qui accepte l'entrée de chaîne arbitraire n'est jamais vraiment "arbitraire", car les ressources sont finies, ce qui signifie que, alors que la théorie de "arbitraire" s'applique ... ce n'est pas exactement un à un avec la pratique.
L'autre exemple de personnes citées est la suivante:
while (n != 1)
if (n & 1 == 1)
n = 3 * n + 1;
else
n /= 2;
Si N est un entier 32 bits ... alors je peux vous dire visuellement si cela s'arrêtera ou non.
Je suppose que cette modification ne demande rien, mais l'exemple le plus convaincant que j'ai vu est celui-ci :
Supposons que vous avez votre programme/méthode magique pour déterminer qu'un programme s'arrête.
public bool DeterminesHalt(string filename, string[] args){
//runs whatever program you tell it do, passing any args
//returns true if the program halts, false if it doesn't
}
Disons maintenant que nous écrivons un petit morceau de code tel que ...
public static void Main(string[] args){
string filename = Console.ReadLine(); //read in file to run from user
if(DeterminesHalt(filename, args))
for(;;);
else
return;
}
Donc, pour cet exemple, nous pouvons écrire un programme pour faire exactement le contraire de notre méthode de halte magique. Si nous déterminons en quelque sorte qu'un programme donné s'arrêtera, nous saurons simplement dans une boucle infinie; Sinon, si nous déterminons que le programme est dans une boucle infinie, nous terminons le programme.
Encore une fois, si vous écrivez intentionnellement un programme contenant une boucle infinie ... "Résoudre le problème d'arrêt" est un peu discutable, n'est-ce pas?
Modifier (beaucoup plus tard que la réponse originale): Markcc de Bon calcul, mauvais mathématiques a récemment écrit une excellente discussion du problème de halte avec des exemples concrets avec des exemples concrets.
Le problème d'arrêt est fondamentalement une façon formelle de demander si vous pouvez dire si un programme arbitraire sera éventuellement arrêté.
En d'autres termes, pouvez-vous écrire un programme appelé Oracle, hallingoracle (programme, entrée), qui retourne true si le programme (entrée) serait éventuellement arrêté et qui retourne faux si ce ne serait pas?
La réponse est: Non, vous ne pouvez pas.
Suivi des questions sur la question de savoir si l'entrée du problème d'arrêt est pertinente ou un hareng rouge: Oui, l'entrée est importante. De plus, il semble y avoir une certaine confusion en ce que je vois "infini" utilisé où "arbitraire" est plus correct.
Exemple pratique : Imaginez que vous travaillez dans une position d'assurance qualité et que vous devez écrire un programme de découpage (alias Oracle) qui confirmera que pour tout arbitraire programme écrit par l'équipe de développement (d) et tout arbitraire Entrée fournie par l'utilisateur final (i), le programme D finira éventuellement s'arrêter lors de l'entrée. JE.
Cue Manager Voice: "Ho Ho, ces utilisateurs de Goofy, veillons à ce que, quelle que soit la poubelle qu'ils tapent, nos tâches de serveur ne se retrouveront jamais dans une boucle sans fin. Faites-le donc, codez le singe ! "
Cela semble être une bonne idée, non? Vous ne voulez pas que votre serveur accroche, non?
Ce que le problème de halte vous dit est que vous êtes remis une tâche insoluble. Au lieu de cela, dans ce cas particulier, vous devez planifier des tâches qui passent après un temps de seuil et être prêtes à les annuler.
Mark utilise le code au lieu d'une entrée pour illustrer le problème:
def Deciever(i):
Oracle = i[0]
in = i[1]
if Oracle(Deceiver, i):
while True:
continue
else:
return i
Dans ma discussion dans les commentaires, je suis allé la route de la manipulation des intrants malveillantes pour forcer un problème insoluble. L'exemple de Mark est beaucoup plus élégant, à l'aide de l'arrêt Oracle pour se défaire:
Donc, l'entrée de trompeurs est en fait une liste de deux éléments: le premier est un organisme d'arrêt proposé. La seconde est une autre entrée. Ce que le tueur arrêté est de demander à l'oracle: "Pensez-vous que je vais arrêter de saisir je?". Si l'Oracle dit: "Oui, tu feras arrêter", puis le programme passe dans une boucle infinie. Si l'oracle dit "Non, tu n'arrases pas", alors il s'arrête. Donc, peu importe ce que dit l'oracle, c'est faux.
Dit une autre façon, sans tricher, reformater des intrants, des infinités comptables/indénombrables ou autre chose d'autres distractions, Mark a écrit un morceau de code qui peut défaite tout Programme d'Oracle. Vous ne pouvez pas écrire un Oracle
qui répond à la question de savoir si Deceiver
arrête jamais.
Réponse originale :
De la grande Wikipedia :
Dans la théorie de la calculabilité, le problème d'arrêt est un problème de décision qui peut être indiqué comme suit: Étant donné une description d'un programme et une entrée finie, décidez si le programme se termine en cours d'exécution ou fonctionnera à jamais, étant donné cette entrée.
Alan Turing s'est révélé en 1936 qu'un algorithme général pour résoudre le problème d'arrêt pour toutes les paires d'intrants de programme possibles ne peut exister. Nous disons que le problème d'arrêt est indéciable sur les machines de Turing. Copeland (2004) attribue le problème de halte de terme réel à Martin Davis.
L'un des points critiques est que vous n'avez aucun contrôle sur le programme ni l'entrée. Vous êtes remis ceux-ci et c'est à vous de répondre à la question.
Notez également que les machines Turing sont la base pour des modèles de calcul efficaces. Dit une autre manière, tout ce que vous faites dans les langues informatiques modernes peut être ramé sur ces machines turices archétypiques. En conséquence, le problème d'arrêt est indécitable dans une langue moderne utile.
Pour résoudre le problème d'arrêt, vous devez développer un algorithme qui pourrait déterminer si tout programme arbitraire s'arrête pour Toute entrée arbitraire , pas seulement les cas relativement simples de vos exemples.
Voici une simple explication de la preuve que le problème d'arrêt est indécitable.
Supposons que vous avez un programme, H, qui calcule si un programme s'arrête ou non. H prend deux paramètres, la première est une description d'un programme, P et la seconde est une entrée, I. H retourne true si P s'arrête sur l'entrée I et Faux sinon.
Maintenant, écrivez un programme, P2, qui prend en entrée la description d'un autre programme, P3. P2 appelle H (P3, P3), puis boucles si H retourne vrai et s'arrête autrement.
Que se passe-t-il lorsque nous courons P2 (P2)?
Il doit boucler et mettre d'arrêt en même temps, provoquant l'explosion de l'univers.
Cela a été aussi battu à la mort si bien qu'il existe en fait un poétique preuve , écrit dans le style de Lewis Carroll Dr. Seuss par Geoffrey Pullum (il de journal de langue Fame).
Truc drôle. Voici un goût:
Voici l'astuce que je vais utiliser - et c'est simple à faire.
[.____] Je définirai une procédure que j'appellerai q,
[.____] qui utilisera les prédictions de P d'arrêt du succès
[.____] Pour remuer un terrible gâchis logique....
Peu importe comment P pourrait jouer, q va le scooper:
Q utilise la sortie de P pour faire de p look stupide.
[.____] Quel que soit ce que P dit, il ne peut pas prédire q:
[.____] p est juste quand c'est faux et est faux quand c'est vrai!
Il y a une preuve ok le HALDS Problème sur Wikipedia.
Pour illustrer, exactement, pourquoi simplement appliquer une technique à des boucles est insuffisant, considérez le programme suivant (pseudocode):
int main()
{
//Unbounded length integer
Number i = 3;
while(true)
{
//example: GetUniquePositiveDivisiors(6) = [1, 2, 3], ...(5) = 1, ...(10) = 1, 2, 5, etc.
Number[] divisiors = GetUniquePositiveDivisiors(i);
Number sum = 0;
foreach(Number divisor in divisiors) sum += divisor;
if(sum == i) break;
i+=2;
}
}
Pouvez-vous penser à une approche qui retournera vrai si ce code s'arrête, et faux sinon?
Si, par hasard, vous avez une conflit sérieuse pour une médaille de champs, imaginez un code pour ces problèmes à la place de ce qui précède.
"Si vous ajoutez juste une boucle, vous avez le programme d'arrêt et vous ne pouvez donc pas automatiser la tâche"
Cela ressemble à quelqu'un de généraliser l'application du problème d'arrêt. Il y a beaucoup de boucles particulières que vous pouvez prouver se terminer. Il existe des recherches susceptibles d'effectuer une vérification de la résiliation pour de grandes catégories de programmes. Par exemple, dans Coq, vous êtes limité aux programmes que vous pouvez prouver de terminer. Microsoft dispose d'un projet de recherche appelé Terminator qui utilise diverses approximations pour prouver que les programmes seront terminés.
Mais rappelez-vous que le problème d'arrêt n'est pas seulement des exemples de jouets. Aucune de ces personnes ne résout le "problème d'arrêt général", car ils ne travaillent pas pour chaque programme.
Le problème est que le problème d'arrêt indique qu'il existe des programmes que vous n'avez aucun moyen de savoir s'ils se termineront sans les exécuter, ce qui signifie que vous risquez de ne jamais décider de décider s'ils s'arrêtent.
Un exemple de programme qui peut ou non arrêter (à Haskell):
collatz 1 = ()
collatz !n | odd n = collatz (3 * n + 1)
| otherwise = collatz (n `div` 2)
ou dans quelque chose de plus accessible:
while (n != 1)
if (n & 1 == 1)
n = 3 * n + 1;
else
n /= 2;
Compte tenu de tous les entiers> = 1, ce programme arrête-t-il? Eh bien, cela a travaillé jusqu'à présent, mais il n'y a pas de théorème qui dit qu'il sera arrêté pour tous les nombres entiers. Nous avons une conjecture due à Lothar Collatz qui remonte à 1937 qu'il tient, mais aucune preuve.
Le grand exemple de Turing était auto-référentiel - supposons-y IS Un programme qui peut examiner une autre et déterminer s'il s'arrête ou non. Alimonez le vérificateur d'arrêt de l'arrêt - Checker - Que devrait-il faire?
En référence au sous-point "Les gens répondent avec" si vous avez simplement ajouté une boucle, vous avez le programme d'arrêt et vous ne pouvez donc pas automatiser la tâche "", j'ajouterai ce détail:
Les messages qui disent que vous ne pouvez pas calculer algorithmiquement si un programme arbitraire arrêté est absolument correct pour une machine de Turing.
Ce n'est que tous les programmes ne nécessitent pas de machines de Turing. Ce sont des programmes pouvant être calculés avec une machine conceptuellement "plus faible" -, par exemple, des expressions régulières peuvent être entièrement incarnées par une machine d'état fini, qui toujours s'arrête sur l'entrée. N'est-ce pas gentil?
Je mie que lorsque les gens disent "ajouter une boucle", ils essaient d'exprimer l'idée que, lorsqu'un programme est suffisamment complexe, il nécessite une machine de Turing, et donc le problème d'arrêt (comme une idée) s'applique.
Cela peut être légèrement tangentiel à la question, mais je crois, étant donné ce détail dans la question, cela mérite d'être souligné. :)
Voici un programme que le problème d'arrêt ne sera jamais capable de résoudre.
Supposons que vous avez votre programme/méthode magique pour déterminer qu'un programme s'arrête.
public bool DeterminesHalt(string filename, string[] args){
//runs whatever program you tell it do, passing any args
//returns true if the program halts, false if it doesn't
}
Disons maintenant que nous écrivons un petit morceau de code tel que ...
public static void Main(string[] args){
string filename = Console.ReadLine(); //read in file to run from user
if(DeterminesHalt(filename, args))
for(;;);
else
return;
}
Donc, pour cet exemple, nous pouvons écrire un programme pour faire exactement le contraire de notre méthode de halte magique. Si nous déterminons en quelque sorte qu'un programme donné s'arrêtera, nous saurons simplement dans une boucle infinie; Sinon, si nous déterminons que le programme est dans une boucle infinie, nous terminons le programme.
Peu importe le nombre de vérifications d'entrée que vous effectuez, il n'y a pas de solution possible pour déterminer si chaque programme écrit s'arrête ou non.
Beaucoup d'exemples/analogies spécifiques intéressants jusqu'à présent. Si vous voulez lire plus profondément dans l'arrière-plan, il y a un bon livre sur le papier original de Turing, Turing annoté , par Charles Petzold.
Dans une Vein, la Sideways-Sorta, la veine, il y a un essai vraiment soigné sur le Web, qui peut nommer le plus grand nombre? qui brosses sur les machines Turing et Ackermann Fonctions.
C'est une variante du problème de chien d'arrêt , sauf avec des programmes au lieu des chiens et d'arrêter au lieu d'aboyer.
Il y a déjà beaucoup de bonnes réponses, mais je n'ai vu personne de faire face au fait que, dans une sorte de mélange sélectif de la théorie et de la praticité, le problème d'arrêt est vraiment soluble.
Donc, tout d'abord, le problème d'arrêt est essentiellement la tâche de rédiger un programme qui prend tout programme secondaire arbitraire et détermine si le programme secondaire s'arrêtera sur une contribution arbitraire. Donc, vous dites "oui ce programme arrêtera de cette entrée" ou "non, ça ne va pas". Et en fait, il est insoluble dans le cas général (d'autres personnes semblent avoir des preuves de cela déjà) sur une machine de Turing. Le vrai problème est que vous pouvez trouver un peu de savoir si quelque chose va s'arrêter en l'exécutant (attendez simplement que cela s'arrête), mais vous ne pouvez pas vraiment savoir si quelque chose ne va pas arrêter de l'exécuter (vous continuez à attendre pour toujours).
Ceci est un problème sur une machine de Turing qui, par définition, a une quantité infinie de mémoire et donc d'infiniment de nombreux États. Cependant, nos ordinateurs n'ont qu'une quantité finie de mémoire. Il n'y a que tant de bits sur l'ordinateur. Donc, si vous pouviez en quelque sorte garder une trace de tous les états précédents (configurations de bits) que vous avez vus lors de l'exécution du programme, vous pouvez garantir que votre vérificateur n'ira jamais dans une boucle infinie. Si le programme secondaire s'arrête éventuellement, vous dites "Oui, ce programme arrête de cette entrée". Si vous voyez la même configuration de bit deux fois avant que cela ne s'arrête, vous savez "non ce n'est pas". Probablement pas d'une grande importance technique, mais il est bon de savoir que beaucoup de fois les problèmes vraiment "difficiles" que nous sommes confrontés sont plus difficiles en théorie que dans la pratique.
ne preuve d'une autre perspective
Supposons que nous ayons une CPU avec des instructions telles que MOV, ajouter, JMP, mais sans entrer ni dehors. Et nous avons eu la mémoire. Pas comme d'autres processeurs, celui-ci a un autre registre, appelé Parag. Ce registre est comme un fichier, nous pouvons transformer un contenu illimité, en obtenir la taille, chercher au milieu de cela, supprimez une partie du contenu de celui-ci, qui se déroule à travers des instructions supplémentaires.
Avant de commencer, définissez quelques mots. A -Programme est un tas d'instructions, qui est une chaîne. Avant de gérer un programme, nous effacons tous les registres et tous les registres et à zéro à zéro, à l'exception Parareg, qui détient le paramètre (une chaîne), puis mettez le programme dans l'emplacement de mémoire zéro et définissez le registre IP à zéro. A -processus est quand un programme est en cours d'exécution.
Maintenant, le problème d'arrêt peut être indiqué comme suit: étant donné n'importe quel programme, appelé proobj (s'il faut un paramètre para0, nous ajoutons une instruction à la première ligne de celui-ci: MOV Parag, para0), existe-t-il un programme qui prend ProObJ en tant que Paramètre et peut décider si PROOBJ s'arrêtera quand PROOBJ commence à fonctionner sur Paragj à zéro?
Supposons que nous ayons eu un tel programme, appelé P1. Ensuite, nous pouvons créer un autre programme appelé P2 qui prend un paramètre para0. Grâce à P1, nous pouvons dire si un programme dont le contenu est para0, dont le paramètre est para0, sera arrêté ou non. (Nous le faisons de cette façon. Construire un programme dont la première ligne est [MOV Parag, para0], le reste est para0. Nommez ce programme Pro0. Ensuite, nous avons défini Parag to Pro0 et appelez P1.) Si cela s'arrêtera, nous laissons P2 entrer dans une boucle infinie, sinon nous laissons HALT P2.
Si nous mettons P2 dans ParaG et Run P2, le processus sera-t-il arrêté ou non? Si cela s'arrête, de la définition de P2, nous savons que lorsque nous mettons P2 dans ParaRare et exécutez P2, il ne devrait pas arrêter de s'arrêter; De même, si cela ne fait pas arrêter, nous savons quand mettre P2 dans Parareg et gérer P2, il devrait arrêter. Ensuite, on peut dire qu'il n'y a pas de P2, et il n'y a pas de P1.
Vous avez énuméré quelques-uns des cas simples.
Pensez maintenant à penser à tout le reste des cas.
Il y a un nombre infini de scénarios possibles, vous devrez tous les énumérer.
Sauf si bien sûr, vous pourriez le généraliser.
C'est là que le problème d'arrêt entre en place. Comment le généralisez-vous?
Comment votre programme résout-il la Collatz Conjecture ?
De - Programmation des perles , par Jon Bentley
[.____] 4.6 Problèmes
[.____] 5. Prouvez que ce programme se termine lorsque son entrée X est un entier positif.
while x != 1 do
if even(x)
x = x/2
else
x = 3*x +1
L'importance du problème de l'arrêt ne réside pas dans l'importance du problème lui-même; au contraire, les tests automatisés serait peu pratique en génie logiciel (prouvant qu'un programme arrête ne prouve pas qu'il est correct, et en tout cas l'algorithme hypothétique fournit la preuve que pour un fini donné entrée, alors qu'un développeur de logiciels de la vie réelle serait plus intéressé par un test tous possible entrées finis).
Au contraire, le problème a été l'un des arrêt de la première à prouver indécidable, ce qui signifie qu'aucun algorithme existe qui fonctionne dans le cas général. En d'autres termes, plus de Turing prouvé il y a 70 ans qu'il ya des problèmes qui les ordinateurs ne peuvent pas résoudre - pas seulement parce que l'algorithme droit n'a pas encore été trouvé, mais parce qu'un tel algorithme ne peut pas exister logiquement.
Encore un autre exemple. J'ai récemment rencontré quelque chose appelé Nombres Haillstone. Ces chiffres forment une séquence avec ces règles
f(n) is odd - f(n+1) = 3*f(n)+1
f(n) is even - f(n+1) = f(n)/2
Actuellement, il est supposé que tous les points de départ arriveront éventuellement à 1, puis répétez 4,2,1,4,2,1,4,2,1...
Cependant, il n'y a aucune preuve pour cela. Donc, à l'heure actuelle, le seul moyen de déterminer si un numéro se termine lorsqu'il est introduit dans la séquence Haillstone, c'est réellement le calculer jusqu'à ce que vous arriviez à 1.
C'est la clé de savoir comment [~ # ~] i [~ # ~] Comprenez le problème d'arrêt. Comment puis-je comprendre que c'est que vous ne pouvez pas à coup sûr Savoir un programme/ne s'arrêtera pas, sauf si vous exécutez effectivement le programme. Donc, tout programme que vous écrivez, cela pourrait vous donner une réponse à coup sûr au problème d'arrêt, aurait à exécuter le programme.
Je suggérerais de lire ceci: http://fr.wikipedia.org/wiki/halting_problem , surtout http://fr.wikipedia.org/wiki/halting_problem#sketch_of_rophé = Afin de comprendre pourquoi ce problème ne peut pas être résolu sous forme algorithmique.
Supposons que vous écriviez un algorithme qui peut vérifier n'importe quel morceau de code arbitraire et dire s'il s'arrête.
Donnez maintenant à votre algoritme lui-même pour vérifier.
La définition précise du problème est que vous devez rédiger un programme qui suit: - prend un programme arbitraire - détermine si le programme s'arrête de toute entrée finie arbitraire dans le programme.
Cependant, c'est un barreau vraiment élevé. Il existe de nombreuses solutions partielles au problème d'arrêt, mais aucune solution générale. Pire encore, même trouver des programmes qui résolvent partiellement le problème d'arrêt sont connus pour être difficiles:
Article BBC H2G2 sur le problème d'arrêt
Si vous avez vraiment résolu le problème d'arrêt, il travaille sur des sites comme Rentacoder.com pour vous. Il y a quelques mois, il y avait un poste sur l'un d'entre eux d'un utilisateur nommé During qui a offert un contrat pour résoudre le problème d'arrêt. :)
Vous trouverez peut-être utile de considérer l'histoire du gars qui tondre la pelouse de quiconque ne tondre pas leur propre pelouse et de lui demander s'il tondait ou non sa propre pelouse.