Шаблон алгоритму, який видає пояснення того, як він потрапляє до рішення, коли це необхідно


14

Такий сценарій траплявся зі мною кілька разів.

Я запрограмував алгоритм, який вирішує певну проблему. Це прекрасно працює і знаходить правильні рішення. Тепер я хочу мати можливість сказати алгоритму "написати повне пояснення того, як ти потрапив до рішення". Моя мета - мати можливість використовувати алгоритм в онлайн-демонстраціях, підручниках тощо. Я все ще хочу мати можливість запускати алгоритм в режимі реального часу, без пояснень. Яку хорошу модель дизайну використовувати?

ПРИКЛАД: Припустимо, я реалізую цей метод для пошуку найбільшого спільного дільника . Поточний реалізований метод повертає правильну відповідь, але без пояснень. Я хочу мати метод для пояснення своїх дій, наприклад:

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.

Вихід повинен бути повернутий таким чином, щоб його можна було легко відображати як в консолі, так і в веб-додатках.

Яка хороша схема надання пояснень при необхідності, не заважаючи виконувати алгоритм в реальному часі, коли пояснення не потрібні?

Відповіді:


50

"Шаблон", який ви шукаєте, називається "ведення журналів", просто зробіть заяви журналу настільки багатослівними, як вам потрібно. Використовуючи гідну рамку ведення журналу, ви повинні мати можливість увімкнути та вимкнути її під час виконання, забезпечити різні рівні багатослів’я або налаштувати вихід для різних цілей (наприклад, веб-консоль).

Якщо це має помітний вплив на продуктивність (навіть якщо журнал вимкнено), ймовірно, буде залежати від мови, рамки та кількості заяв журналу, які вам потрібні в конкретному випадку. Якщо в компільованих мовах, якщо це справді стає проблемою, ви можете надати компілятор для перетворення "варіанту журналу" та "варіанту без реєстрації" вашого коду. Однак я настійно рекомендую не оптимізувати "про всяк випадок", не вимірюючи спочатку.


2
Хоча вони не є тим, що ви включаєте і вимикаєте, як ведення журналів, схоже, що коментарі та самодокументування коду повинні принаймні отримати почесну згадку у запитанні про "алгоритм, який пояснює себе".
candied_orange

9
@CandiedOrange питання спеціально запитує "пояснення" з реальними значеннями часу роботи, що містяться в ньому. Коментарі в цьому випадку не дуже допоможуть.
metacubed

@metacubed ой давай. Я не сказав, що це альтернатива веденню журналів. Подивіться назву питання і подумайте про трафік, який проходить сюди.
candied_orange

4
@CandiedOrange: Я думаю, що питання питання вводить в оману, ви правильні, що це можна інтерпретувати таким чином, але це не те, що просить ОП. Але я дозволю це виправити, я відредагую заголовок.
Док Браун

1
Зауважте, що щось на зразок дерева записів розроблено спеціально для отримання результатів, що пояснює складні обчислення, створюючи повний запис функціональних викликів.
Борис Павук

7

Хороший зразок - спостерігач. https://en.wikipedia.org/wiki/Observer_pattern

У вашому алгоритмі в кожній точці, де ви хочете щось вивести, ви повідомляєте деяких спостерігачів. Потім вони вирішують, що робити, чи виводити ваш текст на консоль, або надсилати його до двигуна HTML / Apache тощо.

Залежно від вашої мови програмування можуть бути різні способи зробити це швидко. Наприклад, у Java (трактуйте це як псевдокод, для стислості; зробивши його "правильним", за допомогою геттерів, сеттерів, залишається читачеві):

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();

Це злегка багатослівно, але перевірка на це ==nullповинна бути якомога швидшою.

(Зверніть увагу, що в загальному випадку, observerмабуть, Vector observersзамість цього може бути призначено більше одного спостерігача; це, звичайно, також можливо і не призведе до більше накладних витрат; ви все одно можете вкласти оптимізацію, яку ви встановили, observers=nullзамість того, щоб мати порожній Vector.)

Звичайно, ви б реалізували різні види спостерігачів залежно від того, чого ви хочете досягти. Ви також можете вводити там статистику часу та ін., Або робити інші химерні речі.


5

Як невелике вдосконалення прямого ведення журналу, створіть якийсь об'єкт, який моделює одне виконання алгоритму. Додайте "крок" до цього об'єкта контейнера щоразу, коли ваш код робить щось цікаве. В кінці алгоритму записуйте накопичені кроки з контейнера.

У цього є кілька переваг:

  1. Ви можете зафіксувати повне виконання як один запис журналу, що часто корисно, коли є можливість реєструвати інші теми між вашими кроками algo.
  2. У моїй версії Java цього класу (називається просто "Налагодження") я не додаю рядки як записи журналу, а лямбда, що створюють рядки. Ці лямбдахи оцінюються лише в тому випадку, якщо відбудеться фактична реєстрація, тобто якщо об'єкт налагодження виявить, що його рівень журналу в даний час активований. Таким чином, немає необхідності накладних витрат на побудову рядків журналів без необхідності.

EDIT: Як зауважують інші, лямбдахи мають накладні витрати, тому вам доведеться орієнтуватися, щоб переконатися, що ця накладка менше, ніж зайва оцінка коду, необхідного для побудови рядка журналу (записи журналу часто не є простими літералами, але передбачають отримання контекстної інформації з об’єкти, що беруть участь).


2
Є, звичайно, накладні витрати на створення лямбд ...
Серхіо Туленцев

1
Серхіо робить світло, але не повністю пояснює дурість вашої логіки. Потужність накладних витрат на побудову рядків журналів - на порядок менший, ніж накладні витрати на побудову лямбда. Ви зробили тут дуже поганий компроміс
Kyeotic

2
@Tyrsius: Чи є у вас надійний орієнтир, що підтверджує це? (Еталон зв'язування глибоко зіпсований, ср stackoverflow.com/questions/504103 / ... )
Meriton

1
@Tyrsius все залежить від конкретної ситуації. Я можу також надати вам, мабуть, більш релевантний зразок зустрічі . Ви можете бачити, що версія String на порядок повільніше, ніж Runnable. Цей випадок є більш реалістичним, оскільки в контексті цього питання ви завжди хочете динамічно конструювати свої рядки. Це завжди потребує створення об'єктів Stringbuilder, тоді як з Lambda вони створюватимуться лише при потребі (тобто, коли вхід увімкнено).
Джиот

1
Лямбди мають накладні витрати, домовились. Однак розміщений орієнтир у цьому контексті абсолютно не має значення. Реєстрація алгоритмів часто передбачає оцінку іншого коду, який не був би оцінений, якби журнал був пропущений (отримання контекстної інформації з об'єктів-учасників тощо). Саме такої оцінки уникають лямбда. Але ти маєш рацію, моя відповідь вище припускає, що лямбда-накладні менше, ніж цей наклад, що я не перевіряв послідовно.
Корнел Массон

0

Я зазвичай шукаю розгалуження, тобто я шукаю if-заяви. Оскільки вони вказують на те, що я оцінюю значення, це контролюватиме потік алгоритму. У кожному такому випадку (у кожній умові) я можу записати вибраний шлях і чому він був обраний.

Отже, я б записував вхідні значення (початковий стан), кожну обрану гілку (умовні умови) та значення при введенні обраної гілки (темп-стан).


1
це навіть не намагається вирішити поставлене питання про те, щоб не зашкодити виконанню алгоритму в реальному часі, коли пояснення не потрібні
gnat

Я сприйняв це питання як більш загальне, ніж це, і я відповів на нього на рівні дизайну. Але якщо це викликає занепокоєння, додайте до умовного прапорця, який потрібно встановити, якщо ви хочете надрукувати для входу чи ні. Встановіть цей прапор як параметр при запуску.
Річард Тирегрім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.