Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2011-kl-lab9.doc
Скачиваний:
16
Добавлен:
12.11.2019
Размер:
1.65 Mб
Скачать

3.2 Динамічне програмування

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

Пінгала – давній індійський поет і математик близько 5-го століття до н.е. написав трактат на санскриті про просодії Чандас-шастра (Chandas Shastra) th century B.C., and wrote a treatise on Sanskrit prosody called the. Віраханка розширив цю роботу в 6-му столітті н.е. вивчаючи способи комбінування коротких і довгих складів для одержання метрики розміру n. Коротким складам S, присвоювалась одна одиниця довжини а довгим L, дві одиниці довжини. Пінгала визначив, наприклад, що існують п’ять способів одержання метрики довжиною 4: V4 = {LL, SSL, SLS, LSS, SSSS}. V4 можна поділити на дві підмножини, що починаються з L і починаються з S, як показано в (1).

(1)

V4 =

LL, LSS

i.e. L prefixed to each item of V2 = {L, SS}

SSL, SLS, SSSS

i.e. S prefixed to each item of V3 = {SL, LS, SSS}

V3 =

LS

i.e. L prefixed to each item of V1 = {S}

SL, SSS

i.e. S prefixed to each item of V2 = {L, SS}}

V2 =

L

i.e. L prefixed to each item of V0 = {}

SS

i.e. S prefixed to each item of V1 = {S}

V1 = S

V0 = {}

 

def virahanka1(n):

if n == 0:

return [""]

elif n == 1:

return ["S"]

else:

s = ["S" + prosody for prosody in virahanka1(n-1)]

l = ["L" + prosody for prosody in virahanka1(n-2)]

return s + l

def virahanka2(n):

lookup = [[""], ["S"]]

for i in range(n-1):

s = ["S" + prosody for prosody in lookup[i+1]]

l = ["L" + prosody for prosody in lookup[i]]

lookup.append(s + l)

return lookup[n]

def virahanka3(n, lookup={0:[""], 1:["S"]}):

if n not in lookup:

s = ["S" + prosody for prosody in virahanka3(n-1)]

l = ["L" + prosody for prosody in virahanka3(n-2)]

lookup[n] = s + l

return lookup[n]

from nltk import memoize

@memoize

def virahanka4(n):

if n == 0:

return [""]

elif n == 1:

return ["S"]

else:

s = ["S" + prosody for prosody in virahanka4(n-1)]

l = ["L" + prosody for prosody in virahanka4(n-2)]

return s + l

 

>>> virahanka1(4)

['SSSS', 'SSL', 'SLS', 'LSS', 'LL']

>>> virahanka2(4)

['SSSS', 'SSL', 'SLS', 'LSS', 'LL']

>>> virahanka3(4)

['SSSS', 'SSL', 'SLS', 'LSS', 'LL']

>>> virahanka4(4)

['SSSS', 'SSL', 'SLS', 'LSS', 'LL']

Виходячи з (1) можна написати просту рекурсивну функцію virahanka1() для обчислення цієї метрики. Для обчислення V4 потрібно обчислити спочатку V3 та V2. Але для обчислення V3, потрібно спочатку обчислити V2 та V1. Структуру обчислено показано в (2).

(2)

Для обчислення V4 потрібно двічі обчислити V2 . Такі обчислення не повинні викликати труднощів, але при великих значеннях n виконувати ці обчислення стає не раціонально і складно. Для обчислення V20 використовуючи рекурсію V2 потрібно обчислити 4,181 раз; а для V40 потрібно V2 обчислити 63,245,986 разів! Альтернативою може бути збереження значення V2 в таблиці та використання цього значення при потребі. Так само потрібно зробити і для обчислення V3 та інших. Функція virahanka2() реалізовує підхід динамічного програмування до вирішення цієї задачі. Робота функції полягає в заповненні таблиці (lookup) проміжними рішеннями задачі і її виконання припиняється при досягненні потібного n . Кожна підзадача вирішується тільки один раз.

Підхід запропонований в virahanka2() полягає в рішенні малих задач для вирішення великої задачі. Відповідно, це підхід знизу – вверх до динамічного. На жаль, такий підхід має низьку ефективність при вирішення деяких задач, оскільки, від передбачає рішення під задач, які ніколи потім не використовуються для рішення основної задачі. Уникнути таких обчислень дозволяє підхід зверху-вниз до динамічного програмування, який ілюструє функція virahanka3. Ця функція уникає непотрібних обчислень, як в virahanka1(), перевіряючи чи є вже збережений такий результат. Якщо ні функція рекурсивно його обчислює і зберігає у таблиці. Останній крок роботи це повернення збережених результатів. Остатосне рішення – функція virahanka4(), яка використовує “декоратор” Python memoize, який дозволяє зберігати допоміжні результати виконані virahanka3(). Процес "memoization" запам’ятовування полягає в збереженні результатів всіх попередніх викликів функції разом з параметрами, які використовувались. Якщо функція викликається з тими самими параметрами то повертається збережений результат , замість повторного обчислення функції.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]