Шаблон для алгоритма, который выводит объяснение того, как он попадает в решение при необходимости

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
Обратите внимание, что что-то вроде treelog специально разработано для получения выходных данных, объясняющих сложные вычисления путем создания полной записи вызовов функций.
Борис Паук
7

Хорошим примером является Observer. https://en.wikipedia.org/wiki/Observer_pattern

В вашем алгоритме в каждой точке, где вы хотите что-то вывести, вы уведомляете некоторых наблюдателей. Затем они решают, что делать: выводить текст на консоль или отправлять его на механизм HTML / Apache и т. Д.

В зависимости от вашего языка программирования могут быть разные способы сделать это быстро. Например, в Java (для краткости обращайтесь с ним как с псевдокодом; сделать его «правильным» с помощью getters, setters, оставьте читателю):

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.)

Конечно, вы будете реализовывать разные виды наблюдателей в зависимости от того, чего вы хотите достичь. Вы также можете добавить туда статистику по времени и т. Д. Или сделать что-то другое.

Anoe
источник
5

В качестве небольшого улучшения прямой регистрации, создайте некоторый объект, который моделирует одно выполнение алгоритма. Добавляйте «шаг» к этому объекту контейнера каждый раз, когда ваш код делает что-то интересное. В конце алгоритма запишите накопленные шаги из контейнера.

Это имеет несколько преимуществ:

  1. Вы можете зарегистрировать полное выполнение как одну запись в журнале, часто полезно, когда есть вероятность того, что другие потоки будут регистрировать вещи между вашими шагами.
  2. В моей версии Java этого класса (называемой просто «Отладка») я не добавляю строки как записи журнала, а лямбда-выражения, которые генерируют строки. Эти лямбды оцениваются только в том случае, если будет происходить фактическое ведение журнала, т. Е. Если объект отладки обнаружит, что его уровень ведения журнала в данный момент активирован. Таким образом, не возникает лишних затрат на создание ненужных строк журнала.

РЕДАКТИРОВАТЬ: Как прокомментировали другие, лямбды имеют накладные расходы, поэтому вам нужно будет сравнить, чтобы убедиться, что эти издержки меньше, чем ненужная оценка кода, необходимого для построения строки журнала (записи журнала часто не являются простыми литералами, но включают получение контекстной информации из участвующие объекты).

Кизил Массон
источник
2
Есть, конечно, накладные расходы на создание лямбд ...
Серхио Туленцев
1
Серхио освещает, но не полностью объясняет глупость вашей логики. Накладные расходы на создание строк журнала на порядок ниже накладных расходов на создание лямбд. Вы сделали очень плохой компромисс здесь
Kyeotic
2
@ Tyrsius: у вас есть надежный тест, подтверждающий это? (Эталон связывание глубоко испорчен, ср stackoverflow.com/questions/504103/... )
Meriton
1
@ Tyrsius все зависит от конкретной ситуации. Я также могу дать вам, возможно, более уместный контрпример . Вы можете видеть, что версия String на порядок медленнее, чем Runnable. Этот случай более реалистичен, потому что в контексте этого вопроса вы всегда захотите построить свои строки динамически. Это всегда требует создания объектов Stringbuilder, в то время как с помощью Lambda они будут создаваться только при необходимости (т. Е. При включенной регистрации).
Джиот
1
Лямбды над головой, согласились. Тем не менее, опубликованный тест совершенно не имеет значения в этом контексте. Регистрация алгоритмов часто включает оценку другого кода, который не был бы оценен, если бы регистрация была пропущена (получение контекстной информации из участвующих объектов и т. Д.). Именно этой оценки избегают лямбды. Но вы правы, мой ответ выше предполагает, что издержки лямбда меньше, чем эти издержки, что я не всегда проверял.
Корнел Массон
0

Я обычно ищу ответвления, то есть я ищу выражения if. Потому что они указывают, что я оцениваю значение, которое будет контролировать поток алгоритма. В каждом таком случае (каждое условие) я могу записать выбранный путь и узнать, почему он был выбран.

Таким образом, в основном я бы регистрировал значения входа (начальное состояние), каждую выбранную ветвь (условия) и значения при входе в выбранную ветвь (временное состояние).

Ричард Тирегрим
источник
1
это даже не попытка ответить на заданный вопрос о том, как не повредить производительности алгоритма в реальном времени, когда объяснения не нужны
комнат
Я взял вопрос как более общий, чем этот, и я ответил на него на уровне дизайна. Но если это вызывает озабоченность, добавьте к условному флажку, чтобы установить, хотите ли вы печатать в журнале или нет. Установите этот флаг в качестве параметра при запуске.
Ричард Тирегрим