Apakah mungkin untuk menyelesaikan masalah penghentian jika Anda memiliki input yang terbatas atau dapat diprediksi?

18

Masalah penghentian tidak dapat diselesaikan dalam kasus umum. Dimungkinkan untuk membuat aturan yang ditetapkan yang membatasi input yang diizinkan dan dapatkah masalah penghentian diselesaikan untuk kasus khusus itu?

Misalnya, sepertinya bahasa yang tidak memungkinkan loop misalnya, akan sangat mudah untuk mengetahui apakah program akan berhenti atau tidak.

Masalah yang saya coba selesaikan sekarang adalah saya mencoba membuat pemeriksa skrip yang memeriksa validitas program. Dapat menghentikan masalah diselesaikan jika saya tahu persis apa yang diharapkan dari penulis skrip, yang berarti input yang sangat dapat diprediksi. Jika ini tidak dapat diselesaikan dengan tepat, apa sajakah teknik perkiraan yang baik untuk menyelesaikan ini?

Ken Li
sumber

Jawaban:

10

Jawaban intuitifnya adalah bahwa jika Anda tidak memiliki loop yang tidak terikat dan Anda tidak memiliki rekursi dan Anda tidak punya kebagian, program Anda berakhir. Ini tidak sepenuhnya benar, ada cara lain untuk menyelundupkan non-terminasi, tetapi cukup baik untuk sebagian besar kasus praktis. Tentu saja kebalikannya salah, ada bahasa dengan konstruksi ini yang tidak memungkinkan program non-terminating, tetapi mereka menggunakan jenis pembatasan lain seperti sistem tipe canggih.

Pengulangan

Pembatasan umum dalam bahasa skrip adalah secara dinamis mencegah rekursi: jika A memanggil B memanggil C memanggil ... panggilan A, maka penerjemah (atau pemeriksa, dalam kasus Anda) menyerah atau memberi sinyal kesalahan, bahkan jika rekursi itu mungkin benar-benar berakhir. Dua contoh nyata:

  • Preprosesor C membiarkan makro tetap utuh saat memperluas makro itu. Penggunaan yang paling umum adalah mendefinisikan wrapper di sekitar fungsi:

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

    Ini berkembang menjadi

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

    Rekursi timbal balik juga ditangani. Konsekuensinya adalah bahwa preprosesor C selalu berakhir, meskipun mungkin untuk membangun makro dengan kompleksitas run-time yang tinggi.

    #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)
    
  • Kerang Unix memperluas alias secara rekursif, tetapi hanya sampai mereka menemukan alias yang sudah diperluas. Sekali lagi, tujuan utamanya adalah untuk menetapkan alias untuk perintah yang bernama sama.

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

nn

Ada teknik yang lebih umum untuk membuktikan bahwa panggilan rekursif berakhir, seperti menemukan beberapa bilangan bulat positif yang selalu berkurang dari satu panggilan rekursif ke yang berikutnya, tetapi ini jauh lebih sulit untuk dideteksi. Mereka seringkali sulit diverifikasi, apalagi disimpulkan.

Loop

formn

Secara khusus, dengan for loop (ditambah konstruksi bahasa yang masuk akal seperti conditional), Anda dapat menulis semua fungsi rekursif primitif , dan sebaliknya. Anda dapat mengenali fungsi rekursif primitif secara sintaksis (jika mereka ditulis dengan cara yang tidak dikobarkan), karena mereka tidak menggunakan while atau goto atau rekursi atau trik lainnya. Fungsi rekursif primitif dijamin akan berakhir, dan sebagian besar tugas praktis tidak melampaui rekursi primitif.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
4

Lihat Terminator dan APVVe . Mereka cenderung mengandalkan heuristik, dan saya tidak yakin apakah mereka menggambarkan dengan jelas kelas program tempat mereka bekerja. Namun, mereka dianggap sebagai yang mutakhir, jadi mereka harus menjadi titik awal yang baik untuk Anda.

rgrig
sumber
4

Ya, itu mungkin. Salah satu cara umum untuk memecahkan masalah tersebut adalah mempertimbangkan parameter yang tidak dapat dihitung ekstra (monoton) tergantung dari kode sebagai bagian dari input. Kompleksitas masalah memiliki parameter tersebut dapat sangat dikurangi.

Kami tidak dapat menghitung parameter, tetapi jika Anda tahu bahwa instance input yang Anda hadapi memiliki nilai parameter yang kecil, Anda dapat memperbaikinya ke sejumlah kecil dan menggunakan algoritma.

Trik ini dan yang serupa digunakan dalam metode formal untuk mengatasi keraguan yang terputus-putus dan masalah serupa. Tetapi jika apa yang ingin Anda putuskan itu rumit, kompleksitas algoritme Anda tidak mungkin lebih baik daripada menjalankan algoritme pada contoh-contoh itu.

Mengenai pertanyaan lain, jika Anda cukup membatasi input, maka masalah penghentian bisa menjadi mudah. Misalnya, jika Anda tahu bahwa inputnya adalah algoritma waktu polinomial, memutuskan masalah penghentian untuk mereka adalah sepele (karena setiap algoritma waktu polinomial berhenti).

Masalah yang muncul dalam metode formal biasanya tidak dapat dipastikan, Anda mungkin ingin memeriksa literatur tentang bagaimana mereka menangani masalah ini dalam praktiknya.

Kaveh
sumber
4

Bukan jawaban yang kaku secara formal, tapi begini saja:

Masalah dalam menentukan apakah itu berhenti atau berulang selamanya. Looping pada koleksi hingga satu elemen pada satu waktu atau antara interval angka adalah ok. EDIT: Jelas, ini hanya akan berfungsi jika pengumpulan atau interval yang di-iterasi dilarang untuk berubah (misalnya, dengan kekekalan) ketika sedang di-iterasi (atau setidaknya, dilarang untuk tumbuh).

Rekursi mungkin tidak ok, kecuali jika Anda menetapkan aturan buatan untuk membuatnya terbatas, seperti mengizinkan kedalaman tumpukan maksimum, atau memaksa agar parameter non-negatif berkurang di setiap iterasi.

Foto-foto sewenang-wenang umumnya buruk. Mundur-gotos sangat mungkin menyebabkan loop yang mungkin tak terbatas.

Pernyataan whiles dan do-whiles adalah masalah, karena mereka bergantung pada kondisi yang tidak dijamin untuk berubah atau tidak selama eksekusi. Cara yang mungkin (tapi mungkin sangat tidak memuaskan) untuk membatasi yaitu memberikan jumlah iterasi maksimum yang mungkin.

Victor Stafusa
sumber
2

Anda perlu memberikan definisi bahasa skrip Anda, dan apa yang Anda maksud dengan "harapkan" dari para penulis skrip.

HAI(nω)

Ada hasil yang serupa untuk kelas program polinomial oleh Aaron R. Bradley, Zohar Manna dan Henny B. Sipma. Tapi AFAIK (saya mungkin salah di sini) runtime adalah dua kali lipat eksponensial (pada dasarnya waktu yang diperlukan untuk menghitung basis Groebner).


sumber