Генерация всех деревьев на haskell
От: pigman  
Дата: 10.11.11 00:46
Оценка:
Здравствуйте!

Который день не могу родить решение переборной задачи.
Суть ее такова: есть числа от 1 до 5 (пока что), есть арифметические знаки — "+", "-", "*" и скобочки "(", ")".
Нужно сгенерировать всевозможные комбинации расстановки скобочек и знаков, т.е. к примеру (1 + (2 — (3 * (4 + 5)))) или (1+((2*3)-(4+5))) — да, вариантов здесь множество.

Для пяти цифр, например, это количество K = 14 всех бинарных деревьев разбора выражения умноженные на количество размещений с повторениями трех знаков по четырем местам (между 1 и 5 четыре арифм. операции). Итого, получаем 14*3^4 = 1134.

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

13.11.11 13:04: Перенесено из 'Этюды для программистов'
haskell деревья переборные алгоритмы
Re: Генерация всех деревьев на haskell
От: MBo  
Дата: 10.11.11 01:54
Оценка:
Здравствуйте, pigman, Вы писали:


P>Прошу помощи в генерации всевозможных пар скобок, которые будут являться бинарными деревьями со арифметическими операциями в узлах и цифрами в листьях.


Все наборы из 2N скобок (их количества — числа Каталана) нетрудно сгенерировать рекурсивно. Нужно вести подсчет количества левых и правых скобок.
Если их разность нулевая, то на данном шаге рекурсии можно добавить только левую скобку.
Если количество левых = N — то только правую.
Иначе можно любую.

Кстати, у Кнута недавно вышли кусочки 4-го тома, среди них есть и глава по генерация всяких деревьев.
(pre-fascicle 4a, Section 7.2.1.6)
Re: Генерация всех деревьев на haskell
От: Буравчик Россия  
Дата: 10.11.11 17:55
Оценка:
Здравствуйте, pigman, Вы писали:

P>Прошу помощи в генерации всевозможных пар скобок, которые будут являться бинарными деревьями со арифметическими операциями в узлах и цифрами в листьях.


Задача решается с помощью рекурсии.
Рассмотрим список значений, например от [1,2,3,4,5]. Разобьем список на две части (левую и правую), например [1,2] и [3,4,5]. Для каждого подсписка сформируем все возможные выражения. Затем объединим все полученные выражения из одного списка со всеми выражениями другого списка, вставляя между ними какой-нибудь знак операции. Выполняя такую операцию для различных разбиений начального списка, получим все возможные выражения.

Надеюсь, что это не учебная задача (по-моему haskell в России не изучают в ВУЗах), поэтому выложу полный код. Если задача учебная, сюда заглядывать запрещено
  Код
import Data.List

-- Все возможные бинарные операции (для удобства, представим как Char)
type Operation = Char
operations :: [Operation]
operations = ['+', '-', '*'] 

-- Делим список на две части (левую и правую) всеми возможными способами, например
-- splits [1,2,3,4] = [
--  ([1], [2,3,4]),
--  ([1,2], [3,4]),
--  ([1,2,3],[4])
-- ]
splits :: Eq a => [a] -> [([a], [a])]
splits xs = [ (as,bs) | (as,bs) <- zip (inits xs) (tails xs), as /= [], bs /= [] ]

-- Наше дерево выражений, в котором будут храниться значения и операции между ними
data Node = 
    Value Int  -- лист дерева с некоторым числовым значением 
  | Op Operation Node Node -- узел дерева: операция и два поддерева
  deriving Show


-----
-- Генерация всех выражений из заданного списка значений.
-- На выходе получаем список всех деревьев
gen :: [Int] -> [Node]

-- Нет входных значений, значит нет и выражений
gen [] = []

-- Из одного значения, можем сформировать только одно выражение (лист дерева)
gen [x] = [Value x]

-- Если значений несколько, то можем сформировать несколько узлов. Для этого:
-- 1. Разобъем список значений на две части (левую и правую)
-- 2. Для левой и правой части сформируем рекурсивно выражения
-- 3. Склеим выражение левой и правой части, вставляя между ними операции 
gen xs = [ Op op leftNode rightNode | 
    (leftList,rightList) <- splits xs, 
    leftNode <- gen leftList, 
    rightNode <- gen rightList,
    op <- operations ]


-----
-- Вывод дерева в скобочной нотации
showNode :: Node -> String

-- Лист дерева: просто выводим значение
showNode (Value a) = show a

-- Узел дерева: выводим левое поддерево, знак между ними, правое поддерево
-- Все выражение заключаем в скобки
showNode (Op op left right) = "(" ++ showNode left ++ [op] ++ showNode right ++ ")"

  Результат

mapM_ (putStrLn.showNode) $ gen [1..3]
(1+(2+3))
(1-(2+3))
(1*(2+3))
(1+(2-3))
(1-(2-3))
(1*(2-3))
(1+(2*3))
(1-(2*3))
(1*(2*3))
((1+2)+3)
((1+2)-3)
((1+2)*3)
((1-2)+3)
((1-2)-3)
((1-2)*3)
((1*2)+3)
((1*2)-3)
((1*2)*3)
— — — — — — — — — — — — — — — — — — — — —
length $ gen [1..5]
1134
— — — — — — — — — — — — — — — — — — — — -


Вообще, такая постановка задачи не совсем правильна. Например выражения (1+(2+3)) и ((1+2)+3) в задаче считаются различными, хотя по сути являются одинаковыми.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Best regards, Буравчик
Re[2]: Генерация всех деревьев на haskell
От: deniok Россия  
Дата: 13.11.11 12:02
Оценка:
Здравствуйте, Буравчик, Вы писали:


Б>Надеюсь, что это не учебная задача (по-моему haskell в России не изучают в ВУЗах),


Изучают, изучают.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.