Tôi đang đọc "Giới thiệu về thuật toán" của CLRS. Trong chương 2, các tác giả đã đề cập đến "bất biến vòng lặp". Một vòng lặp bất biến là gì?
Tôi đang đọc "Giới thiệu về thuật toán" của CLRS. Trong chương 2, các tác giả đã đề cập đến "bất biến vòng lặp". Một vòng lặp bất biến là gì?
Câu trả lời:
Nói một cách đơn giản, bất biến vòng lặp là một số vị ngữ (điều kiện) giữ cho mỗi lần lặp của vòng lặp. Ví dụ: chúng ta hãy nhìn vào một for
vòng lặp đơn giản trông như thế này:
int j = 9;
for(int i=0; i<10; i++)
j--;
Trong ví dụ này, điều đó đúng (với mỗi lần lặp) đó i + j == 9
. Một bất biến yếu hơn mà cũng đúng là như vậy
i >= 0 && i <= 10
.
Tôi thích định nghĩa rất đơn giản này: ( nguồn )
Bất biến vòng lặp là một điều kiện [trong số các biến chương trình] nhất thiết phải đúng ngay trước và ngay sau mỗi lần lặp của vòng lặp. (Lưu ý rằng điều này không nói gì về sự thật hoặc giả dối của nó thông qua một lần lặp.)
Chính nó, một bất biến vòng lặp không làm được gì nhiều. Tuy nhiên, với một bất biến thích hợp, nó có thể được sử dụng để giúp chứng minh tính đúng đắn của thuật toán. Ví dụ đơn giản trong CLRS có lẽ phải làm với việc sắp xếp. Ví dụ, hãy để bất biến vòng lặp của bạn giống như ở đầu vòng lặp, các i
mục đầu tiên của mảng này được sắp xếp. Nếu bạn có thể chứng minh rằng đây thực sự là một bất biến vòng lặp (nghĩa là nó giữ trước và sau mỗi lần lặp vòng lặp), bạn có thể sử dụng điều này để chứng minh tính đúng đắn của thuật toán sắp xếp: khi kết thúc vòng lặp, bất biến vòng lặp vẫn được thỏa mãn và bộ đếm i
là chiều dài của mảng. Do đó, các i
mục đầu tiên được sắp xếp có nghĩa là toàn bộ mảng được sắp xếp.
Một ví dụ đơn giản hơn: Loops bất biến, tính chính xác và dẫn xuất chương trình .
Cách tôi hiểu một bất biến vòng lặp là một công cụ chính thức, có hệ thống để lý luận về các chương trình. Chúng tôi đưa ra một tuyên bố duy nhất mà chúng tôi tập trung vào việc chứng minh sự thật và chúng tôi gọi đó là bất biến vòng lặp. Điều này tổ chức logic của chúng tôi. Mặc dù chúng ta cũng có thể tranh luận một cách không chính thức về tính đúng đắn của một số thuật toán, sử dụng một bất biến vòng lặp buộc chúng ta phải suy nghĩ rất cẩn thận và đảm bảo lý luận của chúng ta là kín gió.
Có một điều mà nhiều người không nhận ra ngay lập tức khi xử lý các vòng lặp và bất biến. Chúng bị lẫn lộn giữa bất biến vòng lặp và vòng lặp có điều kiện (điều kiện kiểm soát sự kết thúc của vòng lặp).
Như mọi người chỉ ra, bất biến vòng lặp phải đúng
(mặc dù nó có thể tạm thời là sai trong phần thân của vòng lặp). Mặt khác, điều kiện vòng lặp phải là sai sau khi vòng lặp kết thúc, nếu không thì vòng lặp sẽ không bao giờ chấm dứt.
Do đó, bất biến vòng lặp và điều kiện vòng lặp phải là các điều kiện khác nhau.
Một ví dụ điển hình về bất biến vòng lặp phức tạp là tìm kiếm nhị phân.
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
}
Vì vậy, điều kiện vòng lặp có vẻ khá thẳng về phía trước - khi bắt đầu> kết thúc vòng lặp chấm dứt. Nhưng tại sao vòng lặp đúng? Vòng lặp bất biến chứng tỏ tính đúng đắn của nó là gì?
Bất biến là câu lệnh logic:
if ( A[mid] == a ) then ( start <= mid <= end )
Tuyên bố này là một tautology logic - nó luôn luôn đúng trong bối cảnh của vòng lặp / thuật toán cụ thể mà chúng tôi đang cố gắng chứng minh . Và nó cung cấp thông tin hữu ích về tính đúng đắn của vòng lặp sau khi nó kết thúc.
Nếu chúng ta trả về vì chúng ta đã tìm thấy phần tử trong mảng thì câu lệnh rõ ràng là đúng, vì nếu A[mid] == a
sau đó a
nằm trong mảng và mid
phải nằm giữa bắt đầu và kết thúc. Và nếu chấm dứt vòng lặp vì start > end
sau đó không thể có số lượng như vậy start <= mid
và mid <= end
và do đó chúng ta biết rằng báo cáo kết quả A[mid] == a
phải là sai lầm. Tuy nhiên, kết quả là câu lệnh logic tổng thể vẫn đúng theo nghĩa rỗng. (Trong logic, câu lệnh if (false) thì (cái gì đó) luôn luôn đúng.)
Bây giờ những gì tôi đã nói về vòng lặp có điều kiện nhất thiết là sai khi vòng lặp kết thúc? Có vẻ như khi phần tử được tìm thấy trong mảng thì vòng lặp có điều kiện là đúng khi vòng lặp kết thúc!? Đó thực sự là không, bởi vì điều kiện vòng lặp ngụ ý là thực sự while ( A[mid] != a && start <= end )
nhưng chúng tôi rút ngắn thử nghiệm thực tế vì phần đầu tiên được ngụ ý. Điều kiện này rõ ràng là sai sau vòng lặp bất kể vòng lặp kết thúc như thế nào.
a
mặt trong đó A
. Một cách không chính thức, "nếu khóa a
có mặt trong mảng, nó phải xảy ra giữa start
và end
bao gồm". Sau đó, nếu A[start..end]
nó trống, điều đó a
không có trong A.
Các câu trả lời trước đã xác định một bất biến vòng lặp theo cách rất tốt.
Sau đây là cách các tác giả của CLRS sử dụng bất biến vòng lặp để chứng minh tính đúng đắn của Sắp xếp chèn.
Thuật toán sắp xếp chèn (như được đưa ra trong Sách):
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
Vòng lặp bất biến trong trường hợp này: Mảng con [1 đến j-1] luôn được sắp xếp.
Bây giờ hãy để chúng tôi kiểm tra điều này và chứng minh rằng thuật toán là chính xác.
Khởi tạo : Trước lần lặp đầu tiên j = 2. Vì vậy, mảng con [1: 1] là mảng cần kiểm tra. Vì nó chỉ có một yếu tố nên nó được sắp xếp. Như vậy bất biến được thỏa mãn.
Bảo trì : Điều này có thể được xác nhận dễ dàng bằng cách kiểm tra bất biến sau mỗi lần lặp. Trong trường hợp này, nó được thỏa mãn.
Chấm dứt : Đây là bước mà chúng tôi sẽ chứng minh tính đúng đắn của thuật toán.
Khi vòng lặp kết thúc thì giá trị của j = n + 1. Một lần nữa vòng lặp bất biến được thỏa mãn. Điều này có nghĩa là mảng con [1 đến n] nên được sắp xếp.
Đây là những gì chúng tôi muốn làm với thuật toán của chúng tôi. Do đó, thuật toán của chúng tôi là chính xác.
Bên cạnh tất cả các câu trả lời hay, tôi đoán một ví dụ tuyệt vời từ Cách nghĩ về thuật toán, bởi Jeff Edmonds có thể minh họa rất rõ khái niệm này:
VÍ DỤ 1.2.1 "Thuật toán hai ngón tay Find-Max"
1) Thông số kỹ thuật: Một thể hiện đầu vào bao gồm danh sách L (1..n) của các phần tử. Đầu ra bao gồm một chỉ số i sao cho L (i) có giá trị tối đa. Nếu có nhiều mục có cùng giá trị này, thì bất kỳ mục nào trong số chúng được trả về.
2) Các bước cơ bản: Bạn quyết định phương pháp hai ngón tay. Ngón tay phải của bạn chạy xuống danh sách.
3) Thước đo tiến độ: Thước đo tiến độ là khoảng cách dọc theo danh sách ngón tay phải của bạn.
4) Vòng lặp bất biến: vòng lặp vòng lặp nói rằng ngón tay trái của bạn trỏ đến một trong những mục lớn nhất mà ngón tay phải của bạn gặp phải cho đến nay.
5) Các bước chính: Mỗi lần lặp, bạn di chuyển ngón tay phải xuống một mục trong danh sách. Nếu ngón tay phải của bạn bây giờ đang chỉ vào một mục lớn hơn thì mục nhập của ngón tay trái, sau đó di chuyển ngón tay trái của bạn để bằng ngón tay phải của bạn.
6) Tiến bộ: Bạn tiến bộ vì ngón tay phải của bạn di chuyển một mục.
7) Duy trì vòng lặp bất biến: Bạn biết rằng bất biến vòng lặp đã được duy trì như sau. Đối với mỗi bước, phần tử ngón tay trái mới là Max (phần tử ngón tay trái cũ, phần tử mới). Theo bất biến vòng lặp, đây là Max (Max (danh sách ngắn hơn), phần tử mới). Về mặt toán học, đây là Max (danh sách dài hơn).
8) Thiết lập bất biến vòng lặp: Ban đầu, bạn thiết lập bất biến vòng lặp bằng cách trỏ cả hai ngón tay vào phần tử đầu tiên.
9) Điều kiện thoát: Bạn đã hoàn thành khi ngón tay phải của bạn kết thúc việc duyệt qua danh sách.
10) Kết thúc: Cuối cùng, chúng tôi biết vấn đề được giải quyết như sau. Theo điều kiện xuất cảnh, ngón tay phải của bạn đã gặp phải tất cả các mục. Bằng bất biến vòng lặp, ngón tay trái của bạn chỉ điểm tối đa. Trả lại mục này.
11) Thời gian kết thúc và thời gian chạy: Thời gian cần thiết là một số lần không đổi độ dài của danh sách.
12) Các trường hợp đặc biệt: Kiểm tra xem điều gì xảy ra khi có nhiều mục có cùng giá trị hoặc khi n = 0 hoặc n = 1.
13) Chi tiết mã hóa và triển khai: ...
14) Bằng chứng chính thức: Tính chính xác của thuật toán tuân theo các bước trên.
Cần lưu ý rằng Bất biến vòng lặp có thể giúp thiết kế các thuật toán lặp khi được coi là một khẳng định thể hiện mối quan hệ quan trọng giữa các biến phải đúng khi bắt đầu mỗi lần lặp và khi vòng lặp kết thúc. Nếu điều này giữ, tính toán đang trên đường đến hiệu quả. Nếu sai thì thuật toán đã thất bại.
Bất biến trong trường hợp này có nghĩa là một điều kiện phải đúng tại một điểm nhất định trong mỗi lần lặp.
Trong lập trình hợp đồng, bất biến là một điều kiện phải đúng (theo hợp đồng) trước và sau khi bất kỳ phương thức công khai nào được gọi.
Thuộc tính vòng lặp bất biến là một điều kiện giữ cho mỗi bước thực hiện vòng lặp (nghĩa là đối với các vòng lặp, trong khi các vòng lặp, v.v.)
Điều này rất cần thiết cho một Bằng chứng bất biến vòng lặp, trong đó người ta có thể chỉ ra rằng một thuật toán thực thi chính xác nếu ở mỗi bước thực hiện của nó, thuộc tính bất biến vòng lặp này giữ.
Để thuật toán được chính xác, Vòng lặp bất biến phải giữ tại:
Khởi tạo (bắt đầu)
Bảo trì (mỗi bước sau)
Chấm dứt (khi nó kết thúc)
Điều này được sử dụng để đánh giá một loạt các thứ, nhưng ví dụ tốt nhất là các thuật toán tham lam cho biểu đồ có trọng số. Để một thuật toán tham lam mang lại một giải pháp tối ưu (một đường dẫn trên biểu đồ), nó phải đạt được kết nối tất cả các nút trong đường dẫn trọng lượng thấp nhất có thể.
Do đó, thuộc tính bất biến vòng lặp là đường dẫn được thực hiện có trọng số nhỏ nhất. Lúc đầu, chúng tôi chưa thêm bất kỳ cạnh nào, vì vậy thuộc tính này là đúng (trong trường hợp này không sai). Ở mỗi bước , chúng tôi đi theo cạnh trọng lượng thấp nhất (bước tham lam), vì vậy một lần nữa chúng tôi đang đi theo con đường trọng lượng thấp nhất. Cuối cùng , chúng tôi đã tìm thấy con đường có trọng số thấp nhất, vì vậy tài sản của chúng tôi cũng đúng.
Nếu một thuật toán không làm điều này, chúng tôi có thể chứng minh rằng nó không tối ưu.
Thật khó để theo dõi những gì đang xảy ra với các vòng lặp. Các vòng lặp không chấm dứt hoặc chấm dứt mà không đạt được hành vi mục tiêu của họ là một vấn đề phổ biến trong lập trình máy tính. Vòng lặp bất biến giúp. Bất biến vòng lặp là một tuyên bố chính thức về mối quan hệ giữa các biến trong chương trình của bạn, điều này đúng ngay trước khi vòng lặp được chạy (thiết lập bất biến) và lại đúng ở cuối vòng lặp, mỗi lần qua vòng lặp (duy trì bất biến ). Dưới đây là mô hình chung về việc sử dụng Vòng lặp bất biến trong mã của bạn:
... // Bất biến vòng lặp phải đúng ở đây
trong khi (ĐIỀU KIỆN KIỂM TRA) {
// đỉnh của vòng lặp
...
// dưới cùng của vòng lặp
// Bất biến vòng lặp phải đúng ở đây
}
// Chấm dứt + Vòng lặp bất biến = Mục tiêu
...
Giữa đỉnh và đáy của vòng lặp, phần đầu có lẽ được thực hiện để đạt được mục tiêu của vòng lặp. Điều này có thể làm phiền (làm sai) sự bất biến. Điểm của Vòng lặp bất biến là lời hứa rằng bất biến sẽ được khôi phục trước khi lặp lại thân vòng lặp mỗi lần. Có hai ưu điểm này:
Công việc không được chuyển sang đường tiếp theo theo những cách phụ thuộc dữ liệu phức tạp. Mỗi lần đi qua vòng lặp độc lập với tất cả những người khác, với sự bất biến phục vụ để liên kết các đường chuyền lại với nhau thành một tổng thể hoạt động. Lý do rằng vòng lặp của bạn hoạt động được giảm xuống thành lý do rằng bất biến vòng lặp được khôi phục với mỗi lần đi qua vòng lặp. Điều này phá vỡ hành vi tổng thể phức tạp của vòng lặp thành các bước đơn giản nhỏ, mỗi bước có thể được xem xét riêng. Điều kiện kiểm tra của vòng lặp không phải là một phần của bất biến. Nó là những gì làm cho vòng lặp chấm dứt. Bạn xem xét riêng hai điều: tại sao vòng lặp nên chấm dứt và tại sao vòng lặp đạt được mục tiêu khi nó chấm dứt. Vòng lặp sẽ chấm dứt nếu mỗi lần qua vòng lặp bạn di chuyển lại gần để thỏa mãn điều kiện kết thúc. Nó thường dễ dàng để đảm bảo điều này: vd bước một biến đếm cho đến khi nó đạt đến giới hạn trên cố định. Đôi khi lý do đằng sau chấm dứt là khó khăn hơn.
Bất biến vòng lặp phải được tạo để khi đạt được điều kiện chấm dứt và bất biến là đúng, thì mục tiêu đã đạt được:
bất biến + chấm dứt => mục tiêu
Cần thực hành để tạo ra các bất biến đơn giản và liên quan đến việc nắm bắt tất cả các mục tiêu đạt được ngoại trừ chấm dứt. Tốt nhất là sử dụng các ký hiệu toán học để diễn tả các bất biến vòng lặp, nhưng khi điều này dẫn đến các tình huống quá phức tạp, chúng ta dựa vào văn xuôi rõ ràng và lẽ thường.
Xin lỗi tôi không có quyền bình luận.
@Tomas Petricek như bạn đã đề cập
Một bất biến yếu hơn cũng đúng là i> = 0 && i <10 (vì đây là điều kiện tiếp tục!) "
Làm thế nào nó là một vòng lặp bất biến?
Tôi hy vọng tôi không sai, theo như tôi hiểu [1] , bất biến vòng lặp sẽ đúng ở đầu vòng lặp (Khởi tạo), nó sẽ đúng trước và sau mỗi lần lặp (Bảo trì) và nó cũng sẽ đúng sau chấm dứt vòng lặp (Chấm dứt) . Nhưng sau lần lặp cuối i trở thành 10. Vì vậy, điều kiện i> = 0 && i <10 trở thành sai và chấm dứt vòng lặp. Nó vi phạm thuộc tính thứ ba (Chấm dứt) của bất biến vòng lặp.
[1] http://www.win.tue.nl/~kbuchin/teaching/JBP030/notebooks/loop-invariants.html
Bất biến vòng lặp là một công thức toán học như (x=y+1)
. Trong ví dụ đó, x
và y
biểu diễn hai biến trong một vòng lặp. Xét hành vi thay đổi của các biến trong suốt hành của mã này, nó gần như không thể kiểm tra tất cả các khả năng x
và y
giá trị và xem họ tạo ra bất kỳ lỗi. Hãy nói x
là một số nguyên. Số nguyên có thể giữ không gian 32 bit trong bộ nhớ. Nếu số lượng vượt quá, tràn bộ đệm xảy ra. Vì vậy, chúng ta cần chắc chắn rằng trong suốt quá trình thực thi mã, nó không bao giờ vượt quá không gian đó. để làm được điều đó, chúng ta cần hiểu một công thức chung cho thấy mối quan hệ giữa các biến. Rốt cuộc, chúng tôi chỉ cố gắng để hiểu hành vi của chương trình.
Bất biến vòng lặp là một xác nhận đúng trước và sau khi thực hiện vòng lặp.
Trong Tìm kiếm tuyến tính (theo bài tập được đưa ra trong sách), chúng ta cần tìm giá trị V trong mảng đã cho.
Nó đơn giản như quét mảng từ 0 <= k <length và so sánh từng phần tử. Nếu tìm thấy V hoặc nếu quét đạt đến độ dài của mảng, chỉ cần chấm dứt vòng lặp.
Theo sự hiểu biết của tôi trong vấn đề trên-
Vòng lặp bất biến (Khởi tạo): Không tìm thấy V trong lần lặp k - 1. Lần lặp đầu tiên, đây sẽ là -1 do đó chúng ta có thể nói V không tìm thấy ở vị trí -1
Bảo trì: Trong lần lặp lại tiếp theo, V không tìm thấy trong k-1 đúng
Chấm dứt: Nếu V tìm thấy ở vị trí k hoặc k đạt đến độ dài của mảng, chấm dứt vòng lặp.