OcTree на Haskell
От: STilda  
Дата: 14.08.09 20:05
Оценка:
Доброго времени суток.
Взялся ради эксперимента сделать OcTree на haskell.
Но пока что не трехмерный случай а одномерный.
Идея в том, что если имеется список точек с координатами от 0 до 1, то мы его делим на две части, в первой части точки с координатой меньше 0.5, во второй — больше 0.5. Далее процесс повторяется, делим точки первой части еще на две (в одной координаты меньше 0.25 в другой больше 0.25), и вторую часть тоже делим на две (до 0.75 и после 0.75). И так впринципе "до бесконечности".

На Хаскеле удобно это сделать так как ленивые вычисления.

По дизайну естественно хочется разделить процесс генерации бесконечного дерева от процесса ограничения этого дерева. Ограничения могут быть разные: по глубине дерева, по количеству точек в интервале, по каким-то характеристикам этих точек, и так далее.

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

Задачу упростил до того что просто на этапе самой генерации дерева обрубаю пустые ветки.
Если без обрубания — память идет только на последний уровень дерева а промежуточные списки действительно удаляются походу.
Если ставлю проверку на пустой список — хранится все списки на всех уровнях.
( см строку с (*) в коде )
Почему так?

Помогите кто может

type Point a = a
type PointList a = [Point a]
data OcTree a = Node (PointList a,PointList a) [OcTree a] | Empty

leafs Empty = [[]]
leafs (Node (items1,items2) forest) = items1 : items2 : concat (map leafs forest)

main = do
    putStrLn $ "Begin"
    seed <- newStdGen
    let randomPoints = randomlist 1000000 seed `use` forceList
    let tree = chopByLevelNumber 15 . makeOcTree $ randomPoints
    let lastlevel = leafs $ tree
    let lastlevelCount = map length lastlevel
    putStrLn $ "  Last level len : " ++ show ( length lastlevel )
    putStrLn $ "  Last level lengths : " ++ show lastlevelCount    
    putStrLn $ "End"

randomlist :: Int -> StdGen -> [Double]
randomlist n = take n . randoms

--
-- utilites
--

forceList :: [a] -> ()
forceList [] = ()
forceList ((!a):as) = forceList as

use :: a -> (a -> ()) -> a
use expr strat = strat expr `seq` expr

--
-- procedures to create an OcTree
--
split2 param items = split2base items
  where split2base [] = ([],[])
        split2base (x:xs) | x < param = (x:s1,s2)
                          | otherwise = (s1,x:s2)
          where (s1,s2) = split2base xs 

makeOcTree :: RealFloat a => PointList a -> OcTree a
makeOcTree items = mkOcTree0 0.5 items
  where 
        mkOcTree0 :: RealFloat a => a -> PointList a -> OcTree a
        --(*) mkOcTree0 _ [] = Empty
        mkOcTree0 param items =  Node (items1,items2) [lt,rt] 
          where 
                (items1,items2) = split2 param items
                lt = mkOcTree0 (0.5*param) items1
                rt = mkOcTree0 (1.5*param) items2
                

--
-- procedures to chop OcTree
--

chopByLevelNumber :: Int -> OcTree a -> OcTree a
chopByLevelNumber _ Empty = Empty
chopByLevelNumber 0 (Node lst _) = Node lst [Empty,Empty]
chopByLevelNumber n (Node lst forest) = Node lst (map (chopByLevelNumber (n-1)) forest)
Re: OcTree на Haskell
От: STilda  
Дата: 15.08.09 10:46
Оценка:
вопрос снят.
Re: OcTree на Haskell
От: STilda  
Дата: 15.08.09 10:47
Оценка:
извиняюсь, вопрос снят. причина оказалась в другом.
Re[2]: OcTree на Haskell
От: Plague Россия  
Дата: 17.08.09 07:33
Оценка:
Здравствуйте, STilda, Вы писали:

ST>извиняюсь, вопрос снят. причина оказалась в другом.

Покажи правильно решение. Чисто в академических целях.
Re[3]: OcTree на Haskell
От: STilda  
Дата: 18.08.09 21:43
Оценка:
ST>>извиняюсь, вопрос снят. причина оказалась в другом.
P>Покажи правильно решение. Чисто в академических целях.
Ну было несколько ляпов. Расход памяти был изза того что функция leafs написана не правильно. Так как она написана действительно требуются все списки на всех уровнях. Вот оно и держало.

Сейчас это выглядит вот так:
Добавил функции для получения данных из дерева.

{-# LANGUAGE BangPatterns #-}
module Main where

import System.Random
import Data.List

type Point a = a
type Interval a = (Point a, Point a)
type BelongFunction a = Interval a -> Bool
data OcTree a = Node (Interval a) [Point a] [OcTree a]

main = do
    putStrLn $ "Begin"
    seed <- newStdGen
    let randomPoints = randomlist 5000000 seed
    let tree = chopByLevelNumber 15 . chopEmpty . makeOcTree split2 splitInterval2 (0,1) $ randomPoints
    let lastlevel = leafs tree
    let lastlevelCount = map length lastlevel
    let insphere = selector (inSphere 0.33 0.03) tree
    putStrLn $ "  Last level len : " ++ show ( length lastlevel )
    putStrLn $ "  Last level lengths : " ++ show lastlevelCount
    putStrLn $ "  In Sphere : " ++ show insphere    
    putStrLn $ "  In Sphere len : " ++ show (length insphere)
    putStrLn $ "End"

randomlist :: Int -> StdGen -> [Double]
randomlist n = take n . randoms

leafs :: OcTree a -> [[Point a]]
leafs (Node _ items []) = [items]
leafs (Node _ _ forest) = concat $ map leafs forest

--
-- procedure to create an OcTree
--
makeOcTree splitFunc splitIntervalFunc interval items = mkOcTree0 interval items
  where 
    mkOcTree0 int itms = Node int itms treeList
      where  
        itemsList = splitFunc int itms
        intervalList = splitIntervalFunc int        
        treeList = zipWith mkOcTree0 intervalList itemsList

--
-- procedures to split lists and parameters
--

split2 (x,y) items = split2base items 
  where split2base [] = [[],[]]
        split2base (i:is) | i < c     = [i:s1,s2]
                          | otherwise = [s1,i:s2]
          where [s1,s2] = split2base is
                c = 0.5*(x+y) 
          
splitInterval2 (x,y) = let c = 0.5*(x+y) in [(x,c),(c,y)]

--
-- procedures to chop OcTree
--

chopEmpty :: OcTree a -> OcTree a
chopEmpty (Node int items forest) 
    | null items  = Node int items []
    | otherwise   = Node int items (map chopEmpty forest) 

chopByLevelNumber :: Int -> OcTree a -> OcTree a
chopByLevelNumber 0 (Node int lst _) = Node int lst [] 
chopByLevelNumber n (Node int lst forest) = Node int lst (map (chopByLevelNumber (n-1)) forest)

--
-- procedures to make calls to tree
--

-- Return a Function to determine if Interval intersects with circle. Parameters : Center and radius
inSphere :: RealFloat a => Point a -> a -> BelongFunction a
inSphere pt r (x0,x1) = ((pt+r)>=x0 && (pt+r)<x1) || ((pt-r)>=x0 && (pt-r)<x1)

-- Return leafs' points. Belong function says which leafs we are interested in.
selector :: BelongFunction a -> OcTree a -> [Point a]
selector belong (Node interval items [])
    | belong interval   = items
    | otherwise         = []
selector belong (Node interval _ trees)
    | belong interval   = concat . map (selector belong) $ trees 
    | otherwise         = []
Re[4]: OcTree на Haskell
От: STilda  
Дата: 04.10.09 18:22
Оценка: 4 (1)
Привожу последнюю версию этого дела. Вдруг кому интересно.
Одномерный случай.

Основной модуль: создает дерево для 100000 случайных точек, делает запросы для 30000 случайных отрезков.
module Main where

import System.Random
import Data.List
import OcTree

main = do
    putStrLn $ "Begin"
    seed <- newStdGen
    let randPoints = take 100000 . rndPoints1D . rndList $ seed 
    let tree       = releaseNodes . chopByLevel 15 . chopEmpty . makeOcTree splitter1D (0,1) $ randPoints
    
    putStrLn $ "Calls to the tree"
    seed2 <- newStdGen
    let randspheres   = take 30000 . rndCircles1D . rndList $ seed2 
    let inspheres     = map (selector tree . uncurry intersectSphere1D) randspheres
    let spherescounts = map length inspheres 
    putStrLn $ "  In Spheres counts : " ++ show (spherescounts)
    putStrLn $ "End"

rndList :: StdGen -> [Double]
rndList = randoms

rndCircles1D :: [Double] -> [(Double,Double)]
rndCircles1D rnds = zip rnds (map (\x->x/10) (drop 1 rnds))

rndPoints1D :: [Double] -> [Double]
rndPoints1D rnds = rnds


Модуль реализации самого OcTree:
module OcTree where

data OcTree dom item = Node dom [item] [OcTree dom item] | Leaf dom [item] 
type BelongFunction dom = dom -> Bool
type Point1D = Double

makeOcTree :: (d -> [i] -> [(d,[i])]) -> d -> [i] -> OcTree d i 
makeOcTree splitter domain items = Node domain items $ map (uncurry $ makeOcTree splitter) $ splitter domain items

releaseNodes :: OcTree d i -> OcTree d i
releaseNodes (Node dom items forest) = Node dom [] (map releaseNodes forest)
releaseNodes leaf = leaf

chopEmpty :: OcTree d i -> OcTree d i
chopEmpty (Node dom [] _) = Leaf dom []
chopEmpty (Node dom items forest) = Node dom items (map chopEmpty forest)
chopEmpty leaf = leaf 

chopByLevel :: Int -> OcTree d i -> OcTree d i
chopByLevel 0 (Node dom items _) = Leaf dom items 
chopByLevel n (Node dom items forest) = Node dom items (map (chopByLevel (n-1)) forest)
chopByLevel _ leaf = leaf

selector :: OcTree dom item -> BelongFunction dom -> [item]
selector tree belong = concat $ selector0 tree
  where
    selector0 (Leaf domain items)    = if belong domain then [items] else []
    selector0 (Node domain _ forest) = if belong domain then concatMap selector0 forest else []
    
--
-- specific for 1D
--

splitter1D :: (Ord a, Fractional a) => (a,a) -> [a] -> [((a,a),[a])]
splitter1D (x0,x1) items = [ ((x0,c),is0), ((c,x1),is1) ] 
  where c = 0.5*(x0+x1)
        (is0,is1) = splitBase items
        splitBase [] = ([],[])
        splitBase (x:xs) | x < c     = (x:ys1,ys2)
                         | otherwise = (ys1,x:ys2)
          where (ys1,ys2) = splitBase xs

intersectSphere1D :: Point1D -> Double -> BelongFunction (Point1D,Point1D)
intersectSphere1D pt r dom = not $ (pt+r<b0) || (pt-r>b1)

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