È possibile risolvere il problema di arresto se si dispone di un input vincolato o prevedibile?


18

Il problema dell'arresto non può essere risolto nel caso generale. È possibile elaborare regole definite che limitano gli input consentiti e il problema di arresto può essere risolto per quel caso speciale?

Ad esempio, sembra probabile che un linguaggio che non consente i loop, per esempio, sarebbe molto facile dire se il programma si fermerebbe o meno.

Il problema che sto cercando di risolvere in questo momento è che sto cercando di creare un controllo script che controlli la validità del programma. È possibile risolvere il problema di arresto se so esattamente cosa aspettarmi dagli sceneggiatori, il che significa input molto prevedibili. Se ciò non può essere risolto esattamente, quali sono alcune buone tecniche di approssimazione per risolverlo?

Risposte:


10

La risposta intuitiva è che se non si dispone di loop illimitati e non si ha ricorsione e non si ha goto, i programmi terminano. Questo non è del tutto vero, ci sono altri modi per intrufolarsi nella non terminazione, ma è abbastanza buono per la maggior parte dei casi pratici. Ovviamente il contrario è sbagliato, ci sono lingue con questi costrutti che non consentono programmi non terminanti, ma usano altri tipi di restrizioni come sistemi di tipo sofisticati.

ricorsione

Una restrizione comune nei linguaggi di scripting è prevenire dinamicamente la ricorsione: se A chiama B chiama C chiama ... chiama A, l'interprete (o il controllore, nel tuo caso) rinuncia o segnala un errore, anche se la ricorsione potrebbe effettivamente terminare. Due esempi concreti:

  • Il preprocessore C lascia intatta una macro mentre sta espandendo quella macro. L'uso più comune è definire un wrapper attorno a una funzione:

    #define f(x) (printf("calling f(%d)\n", (x)), f(x))
    f(3);
    

    Questo si espande a

    (printf("calling f(%d)\n", (3)), f(3))
    

    Viene gestita anche la ricorsione reciproca. Una conseguenza è che il preprocessore C termina sempre, sebbene sia possibile creare macro con elevata complessità di runtime.

    #define f0(x) x(x)x(x)
    #define f1(x) f0(f0(x))
    #define f2(x) f1(f1(x))
    #define f3(x) f2(f2(x))
    f3(x)
    
  • Le shell Unix espandono ricorsivamente gli alias, ma solo fino a quando non incontrano un alias che è già in fase di espansione. Ancora una volta, lo scopo principale è definire un alias per un comando con nomi simili.

    alias ls='ls --color'
    alias ll='ls -l'
    

nn

Esistono tecniche più generali per dimostrare che le chiamate ricorsive terminano, come trovare un numero intero positivo che diminuisce sempre da una chiamata ricorsiva alla successiva, ma sono notevolmente più difficili da rilevare. Spesso sono difficili da verificare, figuriamoci inferire.

Loops

formn

In particolare, con for loop (oltre a costrutti di linguaggio ragionevoli come i condizionali), è possibile scrivere tutte le funzioni ricorsive primitive e viceversa. Puoi riconoscere sinteticamente le funzioni ricorsive primitive (se sono scritte in modo non offuscato), perché non usano il ciclo while o goto o ricorsione o altro trucco. Le funzioni ricorsive primitive sono garantite per terminare e la maggior parte dei compiti pratici non va oltre la ricorsione primitiva.


4

Vedi Terminator e AProVe . Tendono a fare affidamento sull'euristica e non sono sicuro che descrivano chiaramente la classe di programmi per cui lavorano. Tuttavia, sono considerati all'avanguardia, quindi dovrebbero essere un buon punto di partenza per te.


4

Sì, può essere possibile. Un modo comune per risolvere tali problemi è considerare un parametro extra (monotono) incomputabile che dipende dal codice come parte dell'input. La complessità del problema con quel parametro può essere notevolmente ridotta.

Non possiamo calcolare il parametro, ma se sai che le istanze di input con cui hai a che fare hanno valori di parametro piccoli, puoi fissarlo su un piccolo numero e utilizzare l'algoritmo.

Questo e altri trucchi simili sono usati nei metodi formali per affrontare l'indecidibilità dell'arresto e problemi simili. Ma se ciò che vuoi decidere è complicato, è improbabile che la complessità dei tuoi algoritmi sia migliore rispetto all'esecuzione dell'algoritmo in quelle istanze.

Per quanto riguarda l'altra domanda, se si limitano abbastanza gli input, il problema di arresto può essere facile. Ad esempio, se sai che gli input sono algoritmi temporali polinomiali, decidere il problema dell'arresto per loro è banale (poiché ogni algoritmo temporale polinomiale si interrompe).

I problemi che si presentano nei metodi formali sono in genere indecidibili, potresti voler controllare la letteratura su come affrontano questi problemi nella pratica.


4

Non una risposta formalmente rigida, ma qui va:

Il problema nel determinare se si ferma o si avvolge per sempre. Fare un ciclo su raccolte finite un elemento alla volta o tra un intervallo di numeri è ok. EDIT: Ovviamente, questo funzionerà solo se è vietato cambiare la raccolta o l'intervallo iterato (ad es. Per immutabilità) quando viene iterato (o almeno, è vietato crescere).

Probabilmente la ricorsione non va bene, a meno che se si imposta una regola artificiale per renderla finita, come consentire una profondità massima dello stack o forzare la diminuzione di un parametro non negativo in ogni iterazione.

I goto arbitrari sono generalmente cattivi. È molto probabile che le goto all'indietro conducano a loop che potrebbero essere infiniti.

Le dichiarazioni whiles e do-whiles sono un problema, perché dipendono da una condizione che non è garantita per cambiare o meno durante l'esecuzione. Un modo possibile (ma probabilmente molto insoddisfacente) di limitare ciò è quello di fornire un numero massimo di iterazioni possibili.


2

Devi fornire una definizione del tuo linguaggio di script e cosa intendi per "aspettarti" dagli autori degli script.

O(nω)

C'è un risultato simile per una classe di programma polinomiale di Aaron R. Bradley, Zohar Manna e Henny B. Sipma. Ma AFAIK (potrei sbagliarmi qui) il runtime è doppiamente esponenziale (essenzialmente il tempo necessario per calcolare una base di Groebner).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.