Saya membaca "Pengantar Algoritma" oleh CLRS. Dalam bab 2, penulis menyebutkan "loop invariants". Apa itu invarian loop?
Saya membaca "Pengantar Algoritma" oleh CLRS. Dalam bab 2, penulis menyebutkan "loop invariants". Apa itu invarian loop?
Jawaban:
Dengan kata sederhana, invarian loop adalah beberapa predikat (kondisi) yang berlaku untuk setiap iterasi loop. Sebagai contoh, mari kita lihat for
loop sederhana yang terlihat seperti ini:
int j = 9;
for(int i=0; i<10; i++)
j--;
Dalam contoh ini benar (untuk setiap iterasi) itu i + j == 9
. Invarian lemah yang juga benar adalah itu
i >= 0 && i <= 10
.
Saya suka definisi yang sangat sederhana ini: ( sumber )
Loop invarian adalah suatu kondisi [di antara variabel-variabel program] yang harus benar segera sebelum dan segera setelah setiap iterasi loop. (Perhatikan bahwa ini tidak mengatakan apa-apa tentang kebenaran atau kepalsuan sebagian jalan melalui iterasi.)
Dengan sendirinya, loop invarian tidak banyak membantu. Namun, diberikan invarian yang sesuai, dapat digunakan untuk membantu membuktikan kebenaran suatu algoritma. Contoh sederhana dalam CLRS mungkin ada hubungannya dengan pengurutan. Sebagai contoh, biarkan invarian loop Anda menjadi sesuatu seperti, pada awal loop, i
entri pertama array ini diurutkan. Jika Anda dapat membuktikan bahwa ini memang invarian loop (yaitu bahwa ia berlaku sebelum dan sesudah setiap iterasi loop), Anda dapat menggunakan ini untuk membuktikan kebenaran dari algoritma pengurutan: pada penghentian loop, loop invarian masih puas , dan penghitung i
adalah panjang dari array. Oleh karena itu, i
entri pertama diurutkan berarti seluruh array diurutkan.
Contoh yang lebih sederhana: Loops Invariants, Correctness, dan Derivation Program .
Cara saya memahami invarian loop adalah sebagai alat formal, sistematis untuk alasan tentang program. Kami membuat satu pernyataan yang kami fokuskan untuk membuktikan kebenaran, dan kami menyebutnya loop invariant. Ini mengatur logika kita. Meskipun kita dapat berdebat secara informal tentang kebenaran beberapa algoritma, menggunakan loop invarian memaksa kita untuk berpikir dengan sangat hati-hati dan memastikan alasan kita kedap udara.
Ada satu hal yang banyak orang tidak sadari ketika berhadapan dengan loop dan invarian. Mereka menjadi bingung antara loop invarian, dan loop kondisional (kondisi yang mengontrol terminasi loop).
Seperti yang ditunjukkan orang, loop invarian harus benar
(meskipun sementara itu bisa salah selama tubuh loop). Di sisi lain loop kondisional harus salah setelah loop berakhir, jika loop tidak akan pernah berakhir.
Jadi loop invarian dan kondisi loop harus kondisi yang berbeda.
Contoh yang bagus dari invarian loop kompleks adalah untuk pencarian biner.
bsearch(type A[], type a) {
start = 1, end = length(A)
while ( start <= end ) {
mid = floor(start + end / 2)
if ( A[mid] == a ) return mid
if ( A[mid] > a ) end = mid - 1
if ( A[mid] < a ) start = mid + 1
}
return -1
}
Jadi pengulangan loop sepertinya cukup lurus ke depan - ketika mulai> mengakhiri pengakhiran berakhir. Tetapi mengapa loop itu benar? Apa loop invarian yang membuktikan kebenarannya?
Yang invarian adalah pernyataan logis:
if ( A[mid] == a ) then ( start <= mid <= end )
Pernyataan ini adalah tautologi logis - selalu benar dalam konteks loop / algoritma spesifik yang kami coba buktikan . Dan itu memberikan informasi berguna tentang kebenaran loop setelah itu berakhir.
Jika kita kembali karena kita menemukan elemen dalam array maka pernyataannya benar, karena jika A[mid] == a
kemudian a
ada dalam array dan mid
harus antara awal dan akhir. Dan jika loop berakhir karena start > end
tidak ada angka seperti itu start <= mid
dan mid <= end
dan karena itu kita tahu bahwa pernyataan itu A[mid] == a
pasti salah. Namun, sebagai akibatnya pernyataan logis keseluruhan masih benar dalam arti nol. (Dalam logika pernyataan if (false) then (something) selalu benar.)
Sekarang bagaimana dengan apa yang saya katakan tentang loop kondisional selalu salah ketika loop berakhir? Sepertinya ketika elemen ditemukan dalam array maka loop kondisional benar ketika loop berakhir !? Sebenarnya tidak, karena kondisi loop tersirat benar-benar while ( A[mid] != a && start <= end )
tetapi kami mempersingkat tes yang sebenarnya sejak bagian pertama tersirat. Persyaratan ini jelas salah setelah loop terlepas dari bagaimana loop berakhir.
a
hadir dalam A
. Secara informal, "jika kunci a
ada dalam array, itu harus terjadi di antara start
dan end
inklusif". Maka itu berarti bahwa jika A[start..end]
kosong, itu a
tidak ada dalam A.
Jawaban sebelumnya telah mendefinisikan loop invarian dengan cara yang sangat baik.
Berikut ini adalah cara penulis CLRS menggunakan invarian loop untuk membuktikan kebenaran Penyisipan Sortir.
Algoritma Penyisipan Sortir (seperti yang diberikan dalam Buku):
INSERTION-SORT(A)
for j ← 2 to length[A]
do key ← A[j]
// Insert A[j] into the sorted sequence A[1..j-1].
i ← j - 1
while i > 0 and A[i] > key
do A[i + 1] ← A[i]
i ← i - 1
A[i + 1] ← key
Loop Invariant dalam hal ini: Sub-array [1 ke j-1] selalu diurutkan.
Sekarang mari kita periksa ini dan buktikan algoritma itu benar.
Inisialisasi : Sebelum iterasi pertama j = 2. Jadi sub-array [1: 1] adalah array yang akan diuji. Karena hanya memiliki satu elemen sehingga diurutkan. Dengan demikian invarian puas.
Pemeliharaan : Ini dapat dengan mudah diverifikasi dengan memeriksa invarian setelah setiap iterasi. Dalam hal ini puas.
Pengakhiran : Ini adalah langkah di mana kami akan membuktikan kebenaran dari algoritma.
Ketika loop berakhir maka nilai j = n + 1. Lagi-lagi loop invarian puas. Ini berarti Sub-array [1 ke n] harus disortir.
Inilah yang ingin kami lakukan dengan algoritma kami. Jadi algoritma kami benar.
Di samping semua jawaban yang baik, saya kira contoh yang bagus dari How to Think About Algorithms, oleh Jeff Edmonds dapat menggambarkan konsep dengan sangat baik:
CONTOH 1.2.1 "Algoritma Dua Jari Find-Max"
1) Spesifikasi: Sebuah instance input terdiri dari daftar L (1..n) elemen. Output terdiri dari indeks i sedemikian rupa sehingga L (i) memiliki nilai maksimum. Jika ada beberapa entri dengan nilai yang sama, maka salah satu dari mereka dikembalikan.
2) Langkah-langkah Dasar: Anda memutuskan metode dua jari. Jari kanan Anda berada di bawah daftar.
3) Ukuran Kemajuan: Ukuran kemajuan adalah seberapa jauh sepanjang daftar jari kanan Anda.
4) Loop Invariant: Loop invariant menyatakan bahwa jari kiri Anda menunjuk ke salah satu entri terbesar yang sejauh ini ditemui oleh jari kanan Anda.
5) Langkah Utama: Setiap iterasi, Anda gerakkan jari kanan Anda ke bawah satu entri dalam daftar. Jika jari kanan Anda sekarang menunjuk pada entri yang lebih besar dari entri jari kiri, maka gerakkan jari kiri Anda untuk berada dengan jari kanan Anda.
6) Lakukan Kemajuan: Anda membuat kemajuan karena jari kanan Anda bergerak satu entri.
7) Pertahankan Loop Invariant: Anda tahu bahwa loop invarian telah dipertahankan sebagai berikut. Untuk setiap langkah, elemen jari kiri baru adalah Max (elemen jari kiri lama, elemen baru). Dengan loop invarian, ini adalah Max (Max (daftar pendek), elemen baru). Secara matematis, ini Max (daftar lagi).
8) Membuat Loop Invariant: Anda awalnya membuat loop invarian dengan mengarahkan kedua jari ke elemen pertama.
9) Kondisi Keluar: Anda selesai ketika jari kanan Anda selesai melintasi daftar.
10) Berakhir: Pada akhirnya, kita tahu masalahnya diselesaikan sebagai berikut. Dengan kondisi keluar, jari kanan Anda telah menjumpai semua entri. Dengan loop invarian, jari kiri Anda menunjuk pada maksimumnya. Kembalikan entri ini.
11) Pemutusan dan Waktu Berlari: Waktu yang diperlukan adalah beberapa kali konstan dari panjang daftar.
12) Kasus Khusus: Periksa apa yang terjadi ketika ada beberapa entri dengan nilai yang sama atau ketika n = 0 atau n = 1.
13) Detail Pengkodean dan Implementasi: ...
14) Bukti Resmi: Kebenaran algoritma mengikuti dari langkah-langkah di atas.
Perlu dicatat bahwa Loop Invariant dapat membantu dalam desain algoritma iteratif ketika dianggap sebagai pernyataan yang menyatakan hubungan penting antara variabel yang harus benar pada awal setiap iterasi dan ketika loop berakhir. Jika ini berlaku, perhitungan sedang menuju ke efektifitas. Jika salah, maka algoritme telah gagal.
Invarian dalam hal ini berarti suatu kondisi yang harus benar pada titik tertentu dalam setiap iterasi loop.
Dalam pemrograman kontrak, invarian adalah suatu kondisi yang harus benar (berdasarkan kontrak) sebelum dan sesudah metode publik apa pun dipanggil.
Properti Loop Invariant adalah kondisi yang berlaku untuk setiap langkah dari eksekusi loop (mis. Untuk loop, saat loop, dll.)
Ini sangat penting untuk Bukti Loop Invarian, di mana orang dapat menunjukkan bahwa suatu algoritma dieksekusi dengan benar jika pada setiap langkah pelaksanaannya properti invarian loop ini berlaku.
Agar suatu algoritma menjadi benar, Loop Invariant harus berlaku di:
Inisialisasi (awal)
Pemeliharaan (setiap langkah setelah)
Pengakhiran (saat selesai)
Ini digunakan untuk mengevaluasi banyak hal, tetapi contoh terbaik adalah algoritma serakah untuk grafik traversal berbobot. Untuk algoritma serakah untuk menghasilkan solusi optimal (jalur melintasi grafik), ia harus mencapai menghubungkan semua node di jalur bobot terendah yang mungkin.
Dengan demikian, properti loop invarian adalah bahwa jalur yang diambil memiliki bobot paling sedikit. Pada awalnya kami belum menambahkan tepi, jadi properti ini benar (dalam hal ini tidak salah). Pada setiap langkah , kita mengikuti batas bobot terendah (langkah serakah), jadi sekali lagi kita mengambil jalur bobot terendah. Pada akhirnya , kami telah menemukan jalur berbobot terendah, jadi properti kami juga benar.
Jika suatu algoritma tidak melakukan ini, kami dapat membuktikan bahwa itu tidak optimal.
Sulit untuk melacak apa yang terjadi dengan loop. Loop yang tidak berakhir atau berakhir tanpa mencapai perilaku tujuan mereka adalah masalah umum dalam pemrograman komputer. Loop invarian membantu. Loop invarian adalah pernyataan formal tentang hubungan antara variabel dalam program Anda yang berlaku tepat sebelum loop dijalankan (membuat invarian) dan benar lagi di bagian bawah loop, setiap kali melalui loop (mempertahankan invarian) ). Berikut adalah pola umum penggunaan Loop Invariants dalam kode Anda:
... // Loop Invariant harus benar di sini
while (TEST CONDITION) {
// bagian atas loop
...
// bagian bawah loop
// Loop Invariant harus benar di sini
}
// Termination + Loop Invariant = Sasaran
...
Di antara bagian atas dan bawah loop, kemungkinan headway dibuat untuk mencapai tujuan loop. Ini mungkin mengganggu (membuat salah) yang invarian. Inti dari Loop Invariants adalah janji bahwa invarian akan dipulihkan sebelum mengulangi loop body setiap kali. Ada dua keuntungan untuk ini:
Pekerjaan tidak diteruskan ke langkah selanjutnya dengan cara yang rumit dan tergantung data. Masing-masing melewati loop secara independen dari semua yang lain, dengan invarian melayani untuk mengikat pass bersama menjadi satu kesatuan yang berfungsi. Alasan bahwa loop Anda berfungsi dikurangi menjadi alasan bahwa loop invarian dikembalikan dengan setiap melewati loop. Ini memecah perilaku keseluruhan yang rumit dari loop menjadi langkah-langkah kecil yang sederhana, masing-masing yang dapat dianggap secara terpisah. Kondisi pengujian loop bukan bagian dari invarian. Inilah yang membuat loop berakhir. Anda mempertimbangkan secara terpisah dua hal: mengapa loop harus diakhiri, dan mengapa loop mencapai tujuannya ketika itu berakhir. Loop akan berakhir jika setiap kali melalui loop Anda bergerak lebih dekat untuk memenuhi kondisi terminasi. Seringkali mudah untuk memastikan ini: misalnya melangkah variabel counter dengan satu sampai mencapai batas atas yang tetap. Terkadang alasan di balik pemutusan hubungan kerja lebih sulit.
Loop invarian harus dibuat sehingga ketika kondisi terminasi tercapai, dan invarian benar, maka tujuannya tercapai:
invarian + termination => goal
Dibutuhkan latihan untuk membuat invarian yang sederhana dan berhubungan yang menangkap semua pencapaian tujuan kecuali untuk terminasi. Cara terbaik adalah menggunakan simbol matematika untuk mengekspresikan invarian loop, tetapi ketika ini mengarah pada situasi yang terlalu rumit, kita mengandalkan prosa yang jelas dan akal sehat.
Maaf saya tidak punya izin komentar.
@ Thomas Petricek seperti yang Anda sebutkan
Invarian lemah yang juga benar adalah bahwa i> = 0 && i <10 (karena ini adalah kondisi kelanjutan!) "
Bagaimana itu invarian loop?
Saya harap saya tidak salah, sejauh yang saya mengerti [1] , Loop invarian akan benar pada awal loop (Inisialisasi), itu akan benar sebelum dan setelah setiap iterasi (Pemeliharaan) dan itu juga akan menjadi benar setelah terminasi loop (Pengakhiran) . Tetapi setelah iterasi terakhir saya menjadi 10. Jadi, kondisi i> = 0 && i <10 menjadi salah dan mengakhiri loop. Itu melanggar properti ketiga (Pemutusan) dari invarian loop.
[1] http://www.win.tue.nl/~kbuchin/teaching/JBP030/notebooks/loop-invariants.html
Loop invarian adalah rumus matematika seperti (x=y+1)
. Dalam contoh itu, x
dan y
mewakili dua variabel dalam satu lingkaran. Mempertimbangkan perubahan perilaku variabel-variabel tersebut selama eksekusi kode, hampir tidak mungkin untuk menguji semua kemungkinan x
dan y
nilai-nilai dan melihat apakah mereka menghasilkan bug. Katakanlah x
adalah bilangan bulat. Integer dapat menampung ruang 32 bit dalam memori. Jika jumlah itu melebihi, terjadi buffer overflow. Jadi kita perlu memastikan bahwa selama eksekusi kode, tidak pernah melebihi ruang itu. untuk itu, kita perlu memahami formula umum yang menunjukkan hubungan antar variabel. Bagaimanapun, kami hanya mencoba memahami perilaku program.
Loop invarian adalah pernyataan yang benar sebelum dan sesudah eksekusi loop.
Dalam Pencarian Linier (sesuai dengan latihan yang diberikan dalam buku), kita perlu menemukan nilai V dalam array yang diberikan.
Ini sederhana seperti memindai array dari 0 <= k <panjang dan membandingkan setiap elemen. Jika V ditemukan, atau jika pemindaian mencapai panjang array, hentikan saja loopnya.
Sesuai pemahaman saya dalam masalah di atas-
Loop Invariants (Inisialisasi): V tidak ditemukan dalam iterasi k - 1. Iterasi pertama, ini adalah -1 maka kita dapat mengatakan V tidak ditemukan pada posisi -1
Maintainance: Dalam iterasi berikutnya, V tidak ditemukan dalam k-1 berlaku
Pengakhiran: Jika V ditemukan dalam posisi k atau k mencapai panjang array, akhiri loop.