Сибирский университет потребительской кооперации
Опубликован: 04.05.2005 | Доступ: свободный | Студентов: 4322 / 1347 | Оценка: 4.45 / 4.22 | Длительность: 12:28:00
ISBN: 978-5-9556-0034-5
Лекция 13:

Внутренние (динамические) базы данных

< Лекция 12 || Лекция 13: 1234 || Лекция 14 >

Пример. Другой распространенный вариант использования внутренних баз данных — это повышение эффективности программ за счет добавления уже вычисленных фактов в базу данных. При попытке вычислить предикат сначала проверяется, нет ли в базе данных уже вычисленного значения, и если оно там уже есть, то просто берется это значение. Если же ответа еще нет, он вычисляется обычным способом, после чего добавляется в базу данных для повторного использования. Эта техника еще называется мемоизация или табулирование.

Давайте разработаем табулированную версию предиката, вычисляющего число Фиббоначи по его номеру. В пятой лекции мы уже рассматривали предикат, вычисляющий числа Фиббоначи. Выглядел он следующим образом:

fib(0,1):–!. /* нулевое число Фиббоначи равно единице */
fib(1,1):–!. /* первое число Фиббоначи равно единице */
fib(N,F) :–
          N1=N–1, fib(N1,F1), /* F1 это N–1-е число 
                                 Фиббоначи */
          N2=N–2, fib(N2,F2), /* F2 это N–2-е число 
                                 Фиббоначи */
          F=F1+F2. /* N-е число Фиббоначи равно сумме 
                      N–1-го числа Фиббоначи и N–2-го 
                      числа Фиббоначи */

Чем плох этот вариант предиката, вычисляющего числа Фиббоначи? Получается, что при вычислении очередного числа происходит многократное перевычисление предыдущих чисел Фиббоначи, что не может не приводить к замедлению работы программы.

Изменим нашу программу следующим образом: добавим в нее раздел описания предикатов внутренней базы данных. В этот раздел добавим описание одного-единственного предиката, который будет иметь два аргумента. Первый аргумент — это номер числа Фиббоначи, а второй аргумент — само число.

Сам предикат, вычисляющий числа Фиббоначи, будет выглядеть следующим образом. Базис индукции для первых двух чисел Фиббоначи оставим без изменений. Для шага индукции добавим еще одно правило. Первым делом будем проверять внутреннюю базу данных на предмет наличия в ней уже вычисленного числа. Если оно там есть, то никаких дополнительных вычислений проводить не нужно. Если же числа в базе данных не окажется, вычислим его по обычной схеме как сумму двух предыдущих чисел, после чего добавим соответствующий факт в базу данных.

Попробуем придать этим рассуждениям некоторое материальное воплощение:

fib2(0,1):–!. /* нулевое число Фиббоначи равно единице */
fib2(1,1):–!. /* первое число Фиббоначи равно единице */
fib2(N,F):–
          fib_db(N,F),!. /* пытаемся найти N-е число 
                            Фиббоначи среди уже 
                            вычисленных чисел, хранящихся 
                            во внутренней базе данных */
fib2(N,F) :–
          N1=N–1, fib2(N1,F1), /* F1 это N–1-е число 
                                  Фиббоначи */
          N2=N–2, fib2(N2,F2), /* F2 это N–2-е число 
                                  Фиббоначи */
          F=F1+F2, /* N-е число Фиббоначи равно сумме 
                      N–1-го числа Фиббоначи и N–2-го 
                      числа Фиббоначи */
          asserta(fib_db(N,F)). 
                   /* добавляем вычисленное N-е число 
                      Фиббоначи в нашу внутреннюю базу 
                      данных*/

Заметьте, что при каждом вызове подцели fib2(N2,F2) используются значения, уже вычисленные при предыдущем вызове подцели fib2(N1,F1).

Попробуйте запустить два варианта предиката вычисляющего числа Фиббоначи для достаточно больших номеров (от 30 и выше) и почувствуйте разницу во времени работы. Минуты — работы первого варианта и доли секунды — работы его табулированной модификации.

Справедливости ради стоит заметить, что существует другой вариант ускорения работы предиката, вычисляющего числа Фиббоначи, без использования баз данных.

Будем искать сразу два числа Фиббоначи. То, которое нам нужно найти, и следующее за ним. Соответственно, предикат будет иметь третий дополнительный аргумент, в который и будет помещено следующее число. Базис рекурсии из двух предложений сожмется в одно, утверждающее, что первые два числа Фиббоначи равны единице.

Вот как будет выглядеть этот предикат:

fib_fast(0,1,1):–!.
fib_fast(N,FN,FN1):–
                   N1=N–1,fib_fast(N1,FN_1,FN),
                   FN1=FN+FN_1.

Если следующее число Фиббоначи искать не нужно, можно сделать последним аргументом анонимную переменную или добавить описанный ниже двухаргументный предикат:

fib_fast(N,FN):–
               fib_fast(N,FN,_).
< Лекция 12 || Лекция 13: 1234 || Лекция 14 >
Виктор Бондарь
Виктор Бондарь

После приведения формулы вида ПНФ к виду ССФ вы получаете формулу, в безквантовой матрице которой дизъюнкт содержит оба контранрных атома:. Как тогда проводить его унификацию, если в случае замены x на f(x) весь дизъюнкт обратится в единицу?

Ольга Потапенко
Ольга Потапенко

никак не могу увидеть тексты самих лекций.