Mẫu cho thuật toán đưa ra lời giải thích về cách giải pháp khi cần


14

Kịch bản sau đây đã xảy ra với tôi nhiều lần.

Tôi đã lập trình một thuật toán giải quyết một vấn đề nhất định. Nó hoạt động tốt và tìm ra giải pháp chính xác. Bây giờ, tôi muốn có một tùy chọn để nói với thuật toán "viết một lời giải thích đầy đủ về cách bạn có được giải pháp". Mục tiêu của tôi là có thể sử dụng thuật toán trong các cuộc biểu tình trực tuyến, các lớp hướng dẫn, v.v. Tôi vẫn muốn có một tùy chọn để chạy thuật toán trong thời gian thực mà không cần giải thích. Một mẫu thiết kế tốt để sử dụng là gì?

VÍ DỤ: Giả sử tôi thực hiện phương pháp này để tìm ước số chung lớn nhất . Phương thức thực hiện hiện tại trả về câu trả lời đúng, nhưng không có giải thích. Tôi muốn có một tùy chọn cho phương thức để giải thích các hành động của nó, như:

Initially, a=6 and b=4. The number of 2-factors, d, is initialized to 0.
a and b are both even, so we divide them by 2 and increment d by 1.
Now, a=3 and b=2.
a is odd but b is even, so we divide b by 2.
Now, a=3 and b=1.
a and b are both odd, so we replace a by (a-b)/2 = 1.
Now, a=1 and b=1.
a=b, so the GCD is a*2^d = 2.

Đầu ra phải được trả lại sao cho có thể dễ dàng hiển thị cả trong bảng điều khiển và trong các ứng dụng dựa trên web.

Một mô hình tốt để cung cấp giải thích khi cần thiết, trong khi không làm ảnh hưởng đến hiệu suất thời gian thực của thuật toán khi không cần giải thích là gì?

Câu trả lời:


50

"Mẫu" bạn đang tìm kiếm được gọi là "ghi nhật ký", chỉ cần thực hiện các báo cáo ghi nhật ký là dài dòng khi bạn cần chúng. Bằng cách sử dụng một khung ghi nhật ký tốt, bạn sẽ có thể bật và tắt nó trong thời gian chạy, cung cấp các mức độ chi tiết khác nhau hoặc điều chỉnh đầu ra cho các mục đích khác nhau (như web so với bảng điều khiển).

Nếu điều này có tác động hiệu suất đáng chú ý (ngay cả khi tắt đăng nhập) có thể sẽ phụ thuộc vào ngôn ngữ, khung và số lượng báo cáo ghi nhật ký bạn cần trong trường hợp cụ thể. Trong các ngôn ngữ được biên dịch, nếu điều này thực sự trở thành vấn đề, bạn có thể cung cấp một trình chuyển đổi trình biên dịch để xây dựng "biến thể ghi nhật ký" và "biến thể không ghi nhật ký" của mã của bạn. Tuy nhiên, tôi thực sự khuyên bạn không nên tối ưu hóa "chỉ trong trường hợp", mà không cần đo trước.


2
Mặc dù chúng không phải là thứ bạn bật và tắt như đăng nhập, nhưng cảm giác như các bình luậnmã tài liệu tự ít nhất nên có một đề cập đáng trân trọng trong một câu hỏi về một "thuật toán tự giải thích".
candied_orange

9
@CandiedOrange câu hỏi đặc biệt yêu cầu "giải thích" với các giá trị thời gian thực được bao gồm trong đó. Bình luận sẽ không giúp nhiều trong trường hợp đó.
metacubed

@metacubed ơi thôi nào. Tôi đã không nói rằng nó là một thay thế cho đăng nhập. Nhìn vào tiêu đề câu hỏi và suy nghĩ về lưu lượng truy cập đến đây.
candied_orange

4
@CandiedOrange: Tôi nghĩ tiêu đề câu hỏi là sai lệch, bạn đúng nó có thể được giải thích theo cách đó, nhưng đó không phải là những gì OP đang yêu cầu. Nhưng tôi để tôi sửa điều đó, tôi sẽ chỉnh sửa tiêu đề.
Doc Brown

1
Lưu ý rằng một cái gì đó như treelog được thiết kế đặc biệt để tạo đầu ra giải thích các tính toán phức tạp bằng cách tạo ra một bản ghi đầy đủ các lệnh gọi hàm.
nhện của Vladimir

7

Một mô hình tốt là Observer. https://en.wikipedia.org/wiki/Observer_potype

Trong thuật toán của bạn, tại mỗi điểm bạn muốn xuất một cái gì đó, bạn thông báo cho một số người quan sát. Sau đó, họ quyết định phải làm gì, để xuất văn bản của bạn trên bàn điều khiển hoặc gửi nó đến công cụ HTML / Apache, v.v.

Tùy thuộc vào ngôn ngữ lập trình của bạn, có thể có những cách khác nhau để làm cho nó nhanh. Ví dụ, trong Java (coi nó là mã giả, để đơn giản; làm cho nó "chính xác", với getters, setters, được để lại cho người đọc):

interface AlgoLogObserver {
   public void observe(String message);
}

class AlgorithmXyz {   
   AlgoLogObserver observer = null;
   void runCalculation() {   
       if (observer!=null) { oberserver.observe("Hello"); }
       ...
   }   
}

...
algo = new AlgorithmXyz();
algo.observer = new ConsoleLoggingObserver();  // yes, yes make a 
                                               // setter instead, or use Ruby :-)
algo.runCalculation();

Điều này hơi dài dòng, nhưng việc kiểm tra ==nullphải nhanh nhất có thể.

(Lưu ý rằng trong trường hợp chung, observercó thể Vector observersthay vào đó sẽ cho phép nhiều hơn một người quan sát; điều này tất nhiên là có thể và sẽ không dẫn đến nhiều chi phí hơn; bạn vẫn có thể đưa vào tối ưu hóa mà bạn đặt observers=nullthay vì có trống rỗng Vector.)

Tất nhiên, bạn sẽ triển khai các loại quan sát viên khác nhau tùy thuộc vào những gì bạn muốn đạt được. Bạn cũng có thể đưa vào thống kê thời gian, vv, hoặc làm những thứ ưa thích khác.


5

Là một cải tiến nhỏ để ghi nhật ký thẳng, tạo một số loại đối tượng mô hình hóa một thực thi thuật toán. Thêm một "bước" cho đối tượng chứa này mỗi khi mã của bạn làm điều gì đó thú vị. Khi kết thúc thuật toán, ghi nhật ký các bước tích lũy từ container.

Điều này có một vài lợi thế:

  1. Bạn có thể ghi nhật ký thực thi đầy đủ dưới dạng một mục nhật ký, thường hữu ích khi có khả năng các luồng ghi nhật ký khác ở giữa các bước của bạn
  2. Trong phiên bản Java của lớp này (được gọi đơn giản là "Gỡ lỗi"), tôi không thêm chuỗi dưới dạng mục nhật ký, nhưng lambdas tạo chuỗi. Các lambdas này chỉ được đánh giá NẾU việc ghi nhật ký thực tế sẽ diễn ra, tức là nếu đối tượng Debug thấy rằng mức ghi nhật ký của nó hiện đang được kích hoạt. Bằng cách này, không có chi phí hiệu năng của việc xây dựng các chuỗi nhật ký không cần thiết.

EDIT: Như nhận xét của người khác, lambdas có chi phí hoạt động, do đó bạn sẽ phải đo điểm chuẩn để đảm bảo chi phí này thấp hơn so với đánh giá không cần thiết của mã để xây dựng chuỗi nhật ký (các mục nhật ký thường không đơn giản bằng chữ, nhưng liên quan đến việc lấy thông tin theo ngữ cảnh từ đối tượng tham gia).


2
Tất nhiên, có quá trình tạo ra lambdas ...
Sergio Tulentsev

1
Sergio làm sáng tỏ, nhưng không giải thích đầy đủ sự logic của bạn. Chi phí hoạt động của việc xây dựng các chuỗi nhật ký là một thứ tự có độ lớn thấp hơn chi phí hiệu năng của việc xây dựng lambdas. Bạn đã thực hiện một sự đánh đổi rất kém ở đây
Kyeotic 11/05/2016

2
@Tyrsius: Bạn có điểm chuẩn đáng tin cậy chứng minh điều đó không? (Điểm chuẩn bạn liên kết đến rất thiếu sót, cf stackoverflow.com/questions/504103/NH )
meriton - đình công vào

1
@Tyrsius tất cả phụ thuộc vào tình hình cụ thể. Tôi cũng có thể cung cấp cho bạn một ví dụ , có thể tranh luận, có liên quan hơn . Bạn có thể thấy rằng phiên bản String chậm hơn so với Runnable. Trường hợp này là thực tế hơn, bởi vì trong bối cảnh của câu hỏi này, bạn sẽ luôn muốn xây dựng chuỗi của bạn một cách linh hoạt. Điều này luôn đòi hỏi phải tạo các đối tượng Stringbuilder, trong khi với Lambda, chúng sẽ chỉ được tạo khi cần thiết (tức là khi đăng nhập được bật).
jhyot

1
Lambdas có trên đầu, đồng ý. Tuy nhiên, điểm chuẩn được đăng là hoàn toàn không liên quan trong bối cảnh này. Ghi nhật ký thuật toán thường liên quan đến việc đánh giá mã khác sẽ không được đánh giá nếu việc ghi nhật ký bị bỏ qua (lấy thông tin theo ngữ cảnh từ các đối tượng tham gia, v.v.). Đó là đánh giá này mà lambdas tránh. Nhưng bạn đã đúng, câu trả lời của tôi ở trên không cho rằng chi phí lambda thấp hơn chi phí này , điều mà tôi chưa kiểm tra một cách nhất quán.
Cornel Masson

0

Tôi thường tìm phân nhánh, nghĩa là tôi tìm if-statement. Bởi vì những điều này chỉ ra rằng tôi đánh giá một giá trị, điều đó sẽ kiểm soát dòng chảy của thuật toán. Trong mỗi lần xuất hiện như vậy (mỗi điều kiện) tôi có thể đăng nhập đường dẫn đã chọn và tại sao nó được chọn.

Vì vậy, về cơ bản tôi sẽ ghi lại các giá trị mục nhập (trạng thái ban đầu), mỗi nhánh được chọn (điều kiện) và các giá trị khi vào nhánh được chọn (trạng thái tạm thời).


1
điều này thậm chí không cố gắng giải quyết câu hỏi được hỏi, về việc không làm ảnh hưởng đến hiệu suất thời gian thực của thuật toán khi không cần giải thích
gnat

Tôi đã đặt câu hỏi chung chung hơn thế và tôi đã trả lời nó ở mức độ thiết kế. Nhưng nếu đó là một mối quan tâm, hãy thêm vào cờ có điều kiện để đặt nếu bạn muốn in để đăng nhập hay không. Đặt cờ này làm tham số khi khởi chạy.
Richard Tyregrim
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.