হাসকেলে স্মৃতিচারণ?


136

বিপুল সংখ্যক লোকের জন্য হাস্কেল-এ নিম্নলিখিত ফাংশনটি কীভাবে দক্ষতার সাথে সমাধান করতে হবে সে সম্পর্কে কোনও পয়েন্টার (n > 108)

f(n) = max(n, f(n/2) + f(n/3) + f(n/4))

আমি ফ্যাসোনাচি সংখ্যাগুলি সমাধান করতে হাস্কেলের স্মৃতিচারণের উদাহরণগুলি দেখেছি, যার মধ্যে সমস্ত ফাইবোনাচি সংখ্যা প্রয়োজনীয় এন পর্যন্ত কম্পিউটিং (অলসভাবে) জড়িত। তবে এই ক্ষেত্রে, একটি প্রদত্ত এন এর জন্য আমাদের খুব কম মধ্যবর্তী ফলাফল গণনা করা দরকার।

ধন্যবাদ


110
কেবলমাত্র এই অর্থে যে এটি আমি ঘরে বসে কাজ করছি :-)
অ্যাঞ্জেল ডি ভিসেন্ট

উত্তর:


256

আমরা উপ-লিনিয়ার সময়ে সূচি দিতে পারি এমন একটি কাঠামো তৈরি করে খুব দক্ষতার সাথে এটি করতে পারি।

কিন্তু প্রথম,

{-# LANGUAGE BangPatterns #-}

import Data.Function (fix)

আসুন সংজ্ঞায়িত করা যাক f, তবে সরাসরি এটিকে কল করার পরিবর্তে এটিকে 'ওপেন পুনরাবৃত্তি' ব্যবহার করুন।

f :: (Int -> Int) -> Int -> Int
f mf 0 = 0
f mf n = max n $ mf (n `div` 2) +
                 mf (n `div` 3) +
                 mf (n `div` 4)

আপনি fব্যবহার করে একটি স্মরণীয় পেতে পারেনfix f

এটি আপনাকে পরীক্ষা করতে fদেয় যা fকল করে ক্ষুদ্র মূল্যবোধগুলির জন্য আপনার অর্থ কি তা বোঝায় , উদাহরণস্বরূপ:fix f 123 = 144

আমরা এটি সংজ্ঞায়িত করে স্মরণ করতে পারি:

f_list :: [Int]
f_list = map (f faster_f) [0..]

faster_f :: Int -> Int
faster_f n = f_list !! n

এটি উত্তমরূপে ভাল সম্পাদন করে এবং ও কী নিতে চলেছে তা প্রতিস্থাপন করে (n ^ 3) সময় ফলাফলগুলিকে স্মরণ করিয়ে দেয় time

তবে এর জন্য স্মৃতিযুক্ত উত্তর খুঁজে পেতে কেবল সূচকে লিনিয়ার সময় লাগে mf। এর অর্থ এই যে ফলাফলগুলি:

*Main Data.List> faster_f 123801
248604

সহনীয়, তবে ফলাফলটি এর চেয়ে ভাল স্কেল করে না। আমরা আরও ভাল করতে পারি!

প্রথমে আসুন একটি অসীম গাছ নির্ধারণ করুন:

data Tree a = Tree (Tree a) a (Tree a)
instance Functor Tree where
    fmap f (Tree l m r) = Tree (fmap f l) (f m) (fmap f r)

এবং তারপর আমরা এটি মধ্যে সূচক একটি উপায় নির্ধারণ করব, তাই আমরা সূচকের সাথে একটি নোড জানতে পারেন nমধ্যে হে (লগ ঢ) সময় পরিবর্তে:

index :: Tree a -> Int -> a
index (Tree _ m _) 0 = m
index (Tree l _ r) n = case (n - 1) `divMod` 2 of
    (q,0) -> index l q
    (q,1) -> index r q

... এবং আমরা সুবিধাজনক হওয়ার জন্য প্রাকৃতিক সংখ্যায় পূর্ণ একটি গাছ পেতে পারি যাতে আমাদের সেই সূচকগুলি নিয়ে ঘুরতে হবে না:

nats :: Tree Int
nats = go 0 1
    where
        go !n !s = Tree (go l s') n (go r s')
            where
                l = n + s
                r = l + s
                s' = s * 2

যেহেতু আমরা সূচক করতে পারি, আপনি কেবল একটি গাছকে তালিকায় রূপান্তর করতে পারেন:

toList :: Tree a -> [a]
toList as = map (index as) [0..]

যা toList natsদেয় তা যাচাই করে আপনি এতক্ষণ কাজটি পরীক্ষা করতে পারেন[0..]

এখন,

f_tree :: Tree Int
f_tree = fmap (f fastest_f) nats

fastest_f :: Int -> Int
fastest_f = index f_tree

উপরের তালিকার মতো ঠিক কাজ করে, তবে প্রতিটি নোডের সন্ধান করার জন্য লিনিয়ার সময় না দিয়ে লোগারিথমিক সময়ে এটি তাড়া করতে পারে।

ফলাফলটি যথেষ্ট দ্রুত:

*Main> fastest_f 12380192300
67652175206

*Main> fastest_f 12793129379123
120695231674999

আসলে এটি এত দ্রুত যে আপনি উপরের Intসাথে যেতে পারেন এবং প্রতিস্থাপন করতে পারেন এবং Integerপ্রায় তাত্ক্ষণিকভাবে হাস্যকর আকারে বড় উত্তরগুলি পেতে পারেন

*Main> fastest_f' 1230891823091823018203123
93721573993600178112200489

*Main> fastest_f' 12308918230918230182031231231293810923
11097012733777002208302545289166620866358

3
আমি এই কোডটি চেষ্টা করেছি এবং মজার বিষয় হল f_faster এফ এর চেয়ে ধীর বলে মনে হচ্ছে। আমি অনুমান করি যে তালিকার উল্লেখগুলি সত্যিই জিনিসগুলিকে ধীর করে দিয়েছে। নেটস এবং ইনডেক্সের সংজ্ঞাটি আমার কাছে বেশ রহস্যময় মনে হয়েছিল, তাই আমি নিজের উত্তর যুক্ত করেছি যা জিনিসগুলি আরও পরিষ্কার করে দিতে পারে।
পিটারো

5
অসীম তালিকার কেসটি 111111111 আইটেম দীর্ঘ লিংকযুক্ত তালিকার সাথে ডিল করতে হয়। ট্রি কেস লগ এন নিয়ে কাজ করছে * নোডের সংখ্যা পৌঁছেছে।
এডওয়ার্ড কেএমইটিটি

2
অর্থাত্ তালিকার সংস্করণটিকে তালিকার সমস্ত নোডের জন্য থঙ্কস তৈরি করতে হবে, অন্যদিকে গাছের সংস্করণ তাদের অনেকগুলি তৈরি করা এড়িয়ে চলে।
টম এলিস

7
আমি জানি এটি একটি বরং পুরাতন পোস্ট, তবে কলগুলিতে গাছের অনিচ্ছাকৃত পথগুলি এড়ানোর জন্য f_treeকোনও whereধারাতে সংজ্ঞায়িত করা উচিত নয় ?
dfeuer

17
সিএএফ-এ এটি স্টাফ করার কারণটি হ'ল আপনি কলগুলিতে মেমোয়েজেশন পেতে পারেন। যদি আমার একটি ব্যয়বহুল ফোন থাকে তবে আমি স্মৃতিচারণ করছিলাম, তবে আমি সম্ভবত এটি সিএএফ-এ রেখে যাব, সুতরাং প্রযুক্তিটি এখানে প্রদর্শিত। সত্যিকারের প্রয়োগে অবশ্যই স্থায়ীভাবে স্মৃতিচারণের সুবিধাগুলি এবং ব্যয়ের মধ্যে একটি বাণিজ্য বন্ধ রয়েছে trade যদিও, প্রদত্ত প্রশ্নটি কীভাবে স্মৃতিচারণ করতে হবে সে সম্পর্কে আমি মনে করি যে এটি এমন একটি কৌশল দিয়ে উত্তর দেওয়া বিভ্রান্তিকর হবে যা ইচ্ছাকৃতভাবে কলগুলিতে স্মৃতিচারণকে এড়িয়ে চলে, এবং অন্য কিছু না হলে এখানে এই ভাষ্যটি সূক্ষ্মতা রয়েছে বলে লোকেরা নির্দেশ করবে। ;)
এডওয়ার্ড কেএমইটিটি

17

এডওয়ার্ডের উত্তরটি এমন একটি দুর্দান্ত রত্ন যে আমি এটির সদৃশ করেছি এবং এর প্রয়োগ memoListএবং memoTreeসংযুক্তকারীগুলি সরবরাহ করেছি যা খোলার পুনরাবৃত্ত আকারে একটি ফাংশন স্মরণ করে।

{-# LANGUAGE BangPatterns #-}

import Data.Function (fix)

f :: (Integer -> Integer) -> Integer -> Integer
f mf 0 = 0
f mf n = max n $ mf (div n 2) +
                 mf (div n 3) +
                 mf (div n 4)


-- Memoizing using a list

-- The memoizing functionality depends on this being in eta reduced form!
memoList :: ((Integer -> Integer) -> Integer -> Integer) -> Integer -> Integer
memoList f = memoList_f
  where memoList_f = (memo !!) . fromInteger
        memo = map (f memoList_f) [0..]

faster_f :: Integer -> Integer
faster_f = memoList f


-- Memoizing using a tree

data Tree a = Tree (Tree a) a (Tree a)
instance Functor Tree where
    fmap f (Tree l m r) = Tree (fmap f l) (f m) (fmap f r)

index :: Tree a -> Integer -> a
index (Tree _ m _) 0 = m
index (Tree l _ r) n = case (n - 1) `divMod` 2 of
    (q,0) -> index l q
    (q,1) -> index r q

nats :: Tree Integer
nats = go 0 1
    where
        go !n !s = Tree (go l s') n (go r s')
            where
                l = n + s
                r = l + s
                s' = s * 2

toList :: Tree a -> [a]
toList as = map (index as) [0..]

-- The memoizing functionality depends on this being in eta reduced form!
memoTree :: ((Integer -> Integer) -> Integer -> Integer) -> Integer -> Integer
memoTree f = memoTree_f
  where memoTree_f = index memo
        memo = fmap (f memoTree_f) nats

fastest_f :: Integer -> Integer
fastest_f = memoTree f

12

সবচেয়ে কার্যকর উপায় নয়, তবে স্মৃতিচারণ করতে পারেন:

f = 0 : [ g n | n <- [1..] ]
    where g n = max n $ f!!(n `div` 2) + f!!(n `div` 3) + f!!(n `div` 4)

অনুরোধ করার সময় f !! 144, এটি পরীক্ষা করা হয়f !! 143 উপস্থিত রয়েছে , তবে এর সঠিক মান গণনা করা হয় না। এটি এখনও গণনার কিছু অজানা ফলাফল হিসাবে সেট করা আছে। একমাত্র সঠিক মানগুলি হ'ল প্রয়োজনীয়গুলি।

সুতরাং প্রাথমিকভাবে, যতদূর গণনা করা হয়েছে, প্রোগ্রাম কিছুই জানে না।

f = .... 

আমরা যখন অনুরোধটি করি তখন f !! 12এটি কিছু প্যাটার্ন মেলানো শুরু করে:

f = 0 : g 1 : g 2 : g 3 : g 4 : g 5 : g 6 : g 7 : g 8 : g 9 : g 10 : g 11 : g 12 : ...

এখন এটি গণনা শুরু হয়

f !! 12 = g 12 = max 12 $ f!!6 + f!!4 + f!!3

এটি পুনরাবৃত্তভাবে চ-তে আরেকটি দাবি করে, তাই আমরা গণনা করি

f !! 6 = g 6 = max 6 $ f !! 3 + f !! 2 + f !! 1
f !! 3 = g 3 = max 3 $ f !! 1 + f !! 1 + f !! 0
f !! 1 = g 1 = max 1 $ f !! 0 + f !! 0 + f !! 0
f !! 0 = 0

এখন আমরা কিছু ব্যাক আপ করতে পারেন

f !! 1 = g 1 = max 1 $ 0 + 0 + 0 = 1

যার অর্থ প্রোগ্রামটি এখন জানে:

f = 0 : 1 : g 2 : g 3 : g 4 : g 5 : g 6 : g 7 : g 8 : g 9 : g 10 : g 11 : g 12 : ...

চালিয়ে যাওয়া চালিয়ে যাওয়া:

f !! 3 = g 3 = max 3 $ 1 + 1 + 0 = 3

যার অর্থ প্রোগ্রামটি এখন জানে:

f = 0 : 1 : g 2 : 3 : g 4 : g 5 : g 6 : g 7 : g 8 : g 9 : g 10 : g 11 : g 12 : ...

এখন আমরা আমাদের গণনাটি চালিয়ে যাচ্ছি f!!6:

f !! 6 = g 6 = max 6 $ 3 + f !! 2 + 1
f !! 2 = g 2 = max 2 $ f !! 1 + f !! 0 + f !! 0 = max 2 $ 1 + 0 + 0 = 2
f !! 6 = g 6 = max 6 $ 3 + 2 + 1 = 6

যার অর্থ প্রোগ্রামটি এখন জানে:

f = 0 : 1 : 2 : 3 : g 4 : g 5 : 6 : g 7 : g 8 : g 9 : g 10 : g 11 : g 12 : ...

এখন আমরা আমাদের গণনাটি চালিয়ে যাচ্ছি f!!12:

f !! 12 = g 12 = max 12 $ 6 + f!!4 + 3
f !! 4 = g 4 = max 4 $ f !! 2 + f !! 1 + f !! 1 = max 4 $ 2 + 1 + 1 = 4
f !! 12 = g 12 = max 12 $ 6 + 4 + 3 = 13

যার অর্থ প্রোগ্রামটি এখন জানে:

f = 0 : 1 : 2 : 3 : 4 : g 5 : 6 : g 7 : g 8 : g 9 : g 10 : g 11 : 13 : ...

সুতরাং গণনা মোটামুটি অলসভাবে করা হয়। প্রোগ্রামটি জানে যে এর জন্য কিছু মান f !! 8বিদ্যমান, এটি এর সমান g 8, তবে এটি কী তা কোন ধারণা নেই g 8


এই এক জন্য আপনাকে ধন্যবাদ। আপনি কীভাবে 2 মাত্রিক সমাধানের স্থান তৈরি এবং ব্যবহার করবেন? তা কি তালিকার একটি তালিকা হতে পারে? এবংg n m = (something with) f!!a!!b
ভাইকিংস্টিভ

1
অবশ্যই, আপনি করতে পারেন। সত্যিকারের সমাধানের জন্য, যদিও আমি সম্ভবত মেমোকেবিনেটরগুলির
রামপিন

দুর্ভাগ্যক্রমে এটি ও (এন ^ 2)।
সংখ্যা

8

এটি এডওয়ার্ড কেমেটের দুর্দান্ত উত্তরের একটি সংযোজন।

আমি যখন তার কোডটি চেষ্টা করেছিলাম তখন তার সংজ্ঞা ছিল natsএবং indexবেশ রহস্যময় মনে হয়েছিল, তাই আমি একটি বিকল্প সংস্করণ লিখি যা আমি বুঝতে সহজ পেয়েছি।

আমি সংজ্ঞায়িত indexএবং natsপদ index'এবং nats'

index' t nপরিসীমা উপর সংজ্ঞায়িত করা হয় [1..]। (মনে রাখবেন যে index tএটি পরিসীমাটির উপরে সংজ্ঞায়িত করা হয়েছে [0..])) এটি nবিটগুলির একটি স্ট্রিং হিসাবে গণ্য করে এবং বিটগুলি বিপরীতে পড়ার মাধ্যমে গাছটি অনুসন্ধান করে । যদি বিটটি হয় 1তবে ডান হাতের শাখাটি লাগবে। যদি বিট হয় 0তবে এটি বাম হাতের শাখা নেয়। এটি শেষ বিটে পৌঁছালে এটি থামবে (যা অবশ্যই একটি 1)।

index' (Tree l m r) 1 = m
index' (Tree l m r) n = case n `divMod` 2 of
                          (n', 0) -> index' l n'
                          (n', 1) -> index' r n'

ঠিক যেমন natsজন্য সংজ্ঞায়িত করা হয় index, যাতে index nats n == nসবসময় সত্য, nats'জন্য সংজ্ঞায়িত করা হয় index'

nats' = Tree l 1 r
  where
    l = fmap (\n -> n*2)     nats'
    r = fmap (\n -> n*2 + 1) nats'
    nats' = Tree l 1 r

এখন, natsএবং indexকেবল হয় nats'এবং index'কিন্তু মান 1 দ্বারা স্থানান্তরিত:

index t n = index' t (n+1)
nats = fmap (\n -> n-1) nats'

ধন্যবাদ। আমি একটি মাল্টিভারিয়েট ফাংশন স্মরণ করছি এবং এটি আমাকে সূচক এবং নাটগুলি সত্যই কী করছে তা কার্যকর করতে সহায়তা করেছে।
কিটসিল

8

এডওয়ার্ড কেমেটের উত্তরে যেমন বলা হয়েছে, জিনিসগুলি গতি বাড়ানোর জন্য আপনাকে ব্যয়বহুল গণনাগুলি ক্যাশে করতে হবে এবং সেগুলি দ্রুত অ্যাক্সেস করতে সক্ষম হতে হবে।

ফাংশনটিকে অ-মোনাডিক রাখতে, একটি সূচিত করার উপযুক্ত উপায় সহ একটি অসীম অলস গাছ তৈরির সমাধান (পূর্ববর্তী পোস্টগুলিতে দেখানো হয়েছে) সেই লক্ষ্যটি পূরণ করে। যদি আপনি এই ফাংশনটির অ-সাম্প্রদায়িক প্রকৃতি ছেড়ে দেন তবে আপনি হাসকেলে পাওয়া স্ট্যান্ডার্ড এসোসিয়েটিভ পাত্রে ব্যবহার করতে পারেন "রাষ্ট্র-জাতীয়" মনাদগুলির (রাজ্য বা এসটি-এর মতো) মিশ্রণে।

মূল অসুবিধাটি হ'ল আপনি একটি নন-মোনাডিক ফাংশন পেয়েছেন, আপনাকে আর কাঠামোটি নিজেই সূচী করতে হবে না, এবং কেবল এসোসিয়েটিভ পাত্রে স্ট্যান্ডার্ড বাস্তবায়ন ব্যবহার করতে পারেন।

এটি করার জন্য, আপনাকে প্রথমে পুনরায় লিখতে হবে যে কোনও ধরণের মোনাড গ্রহণ করার জন্য:

fm :: (Integral a, Monad m) => (a -> m a) -> a -> m a
fm _    0 = return 0
fm recf n = do
   recs <- mapM recf $ div n <$> [2, 3, 4]
   return $ max n (sum recs)

আপনার পরীক্ষাগুলির জন্য, আপনি এখনও এমন একটি ফাংশন সংজ্ঞায়িত করতে পারেন যা ডেটা.ফানশন.ফিক্স ব্যবহার করে কোনও স্মৃতিচারণ করে না, যদিও এটি কিছুটা আরও ভার্ভোজ:

noMemoF :: (Integral n) => n -> n
noMemoF = runIdentity . fix fm

এরপরে আপনি স্টেট মোনাদকে ডেটা এর সাথে মিলিয়ে ব্যবহার করতে পারেন things

import qualified Data.Map.Strict as MS

withMemoStMap :: (Integral n) => n -> n
withMemoStMap n = evalState (fm recF n) MS.empty
   where
      recF i = do
         v <- MS.lookup i <$> get
         case v of
            Just v' -> return v' 
            Nothing -> do
               v' <- fm recF i
               modify $ MS.insert i v'
               return v'

ছোটখাটো পরিবর্তন সহ আপনি কোডটি ডেটা দিয়ে কাজ করতে পারবেন instead পরিবর্তে হ্যাশম্যাপ:

import qualified Data.HashMap.Strict as HMS

withMemoStHMap :: (Integral n, Hashable n) => n -> n
withMemoStHMap n = evalState (fm recF n) HMS.empty
   where
      recF i = do
         v <- HMS.lookup i <$> get
         case v of
            Just v' -> return v' 
            Nothing -> do
               v' <- fm recF i
               modify $ HMS.insert i v'
               return v'

অবিরাম ডেটা স্ট্রাকচারের পরিবর্তে, আপনি এসটি মোনাডের সাথে মিলিয়ে মিউটটেবল ডেটা স্ট্রাকচার (যেমন ডেটা.হ্যাশ টেবিল) ব্যবহার করতে পারেন:

import qualified Data.HashTable.ST.Linear as MHM

withMemoMutMap :: (Integral n, Hashable n) => n -> n
withMemoMutMap n = runST $
   do ht <- MHM.new
      recF ht n
   where
      recF ht i = do
         k <- MHM.lookup ht i
         case k of
            Just k' -> return k'
            Nothing -> do 
               k' <- fm (recF ht) i
               MHM.insert ht i k'
               return k'

কোনও স্মৃতিচারণ ছাড়াই বাস্তবায়নের তুলনায় এগুলির যে কোনও বাস্তবায়ন আপনাকে কয়েক সেকেন্ড অপেক্ষা না করে মাইক্রো সেকেন্ডে ফলাফল পেতে বিশাল ইনপুটগুলির জন্য অনুমতি দেয়।

মানদণ্ড হিসাবে মানদণ্ড ব্যবহার করে, আমি পর্যবেক্ষণ করতে পারি যে ডেটা.হ্যাশম্যাপের সাথে বাস্তবায়নটি ডেটা.ম্যাপ এবং ডেটা.হ্যাশ টেবিলের চেয়ে কিছুটা ভাল (প্রায় 20%) সঞ্চালিত হয়েছে যার জন্য সময়গুলি খুব মিল ছিল।

আমি বেঞ্চমার্কের ফলাফলগুলি কিছুটা অবাক করেছিলাম। আমার প্রাথমিক অনুভূতিটি হ্যাশ টেবিল হ্যাশম্যাপ বাস্তবায়নকে ছাড়িয়ে দেবে কারণ এটি পরিবর্তনীয়। এই শেষ বাস্তবায়নে কিছু পারফরম্যান্স ত্রুটি লুকিয়ে থাকতে পারে।


2
GHC অপরিবর্তনীয় কাঠামোর আশেপাশে অনুকূলকরণের জন্য খুব ভাল কাজ করে। সি থেকে অন্তর্দৃষ্টি সবসময় প্যান আউট হয় না।
জন টাই

3

কয়েক বছর পরে, আমি এটি দেখেছিলাম এবং বুঝতে পারি যে লিনিয়ার সময় zipWithএবং একটি সহায়ক ফাংশন ব্যবহার করে এটি স্মরণ করার একটি সহজ উপায় রয়েছে :

dilate :: Int -> [x] -> [x]
dilate n xs = replicate n =<< xs

dilate সুবিধাজনক সম্পত্তি আছে dilate n xs !! i == xs !! div i n

সুতরাং, ধরুন আমাদের এফ (0) দেওয়া হয়েছে, এটির সাথে গণনা সহজ হয় l

fs = f0 : zipWith max [1..] (tail $ fs#/2 .+. fs#/3 .+. fs#/4)
  where (.+.) = zipWith (+)
        infixl 6 .+.
        (#/) = flip dilate
        infixl 7 #/

আমাদের মূল সমস্যার বর্ণনার মতো অনেকটা অনুসন্ধান করা, এবং একটি লিনিয়ার সমাধান দেওয়া ( sum $ take n fsও (এন) নেবে)।


2
সুতরাং এটি একটি জেনারেটর (কোরক্রেসিভ?), বা গতিশীল প্রোগ্রামিং, সমাধান। প্রতিটি উত্পন্ন মান অনুযায়ী ও (1) সময় নেওয়ার মতো স্বাভাবিক ফিবোনাচি করছে is গ্রেট! এবং ইকেমেটের সমাধান হ'ল লোগারিথমিক বিগ-ফাইবোনাকির মতো, বড় সংখ্যায় দ্রুত পৌঁছে যাওয়া, অনেকগুলি ইন-বিটওয়েইনকে বাদ দিয়ে। এটা কি ঠিক?
নেস

অথবা এটি হ্যামিং সংখ্যার জন্য একের কাছাকাছি, তিনটি ব্যাক-পয়েন্টার সহ যে ক্রমটি তৈরি হচ্ছে, এবং এর প্রতিটিটির জন্য বিভিন্ন গতি এটির সাথে এগিয়েছে। সত্যি সুন্দর.
নেস

2

এডওয়ার্ড কেমেটের উত্তরের আরও একটি সংযোজন: একটি স্ব-নিরপেক্ষ উদাহরণ:

data NatTrie v = NatTrie (NatTrie v) v (NatTrie v)

memo1 arg_to_index index_to_arg f = (\n -> index nats (arg_to_index n))
  where nats = go 0 1
        go i s = NatTrie (go (i+s) s') (f (index_to_arg i)) (go (i+s') s')
          where s' = 2*s
        index (NatTrie l v r) i
          | i <  0    = f (index_to_arg i)
          | i == 0    = v
          | otherwise = case (i-1) `divMod` 2 of
             (i',0) -> index l i'
             (i',1) -> index r i'

memoNat = memo1 id id 

একটি একক পূর্ণসংখ্যার আর্গ (উদাহরণস্বরূপ ফাইবোনাকি) দিয়ে কোনও ফাংশন স্মরণে রাখতে নিম্নলিখিত হিসাবে এটি ব্যবহার করুন:

fib = memoNat f
  where f 0 = 0
        f 1 = 1
        f n = fib (n-1) + fib (n-2)

অ-নেতিবাচক আর্গুমেন্টের জন্য কেবল মানগুলি ক্যাশে করা হবে।

নেতিবাচক আর্গুমেন্টের জন্য মানগুলি ক্যাশে করতে memoInt, নীচে সংজ্ঞায়িতভাবে ব্যবহার করুন use

memoInt = memo1 arg_to_index index_to_arg
  where arg_to_index n
         | n < 0     = -2*n
         | otherwise =  2*n + 1
        index_to_arg i = case i `divMod` 2 of
           (n,0) -> -n
           (n,1) ->  n

দুটি পূর্ণসংখ্যক যুক্তিযুক্ত ফাংশনগুলির জন্য মানগুলি ক্যাশে করতে নীচে memoIntIntসংজ্ঞায়িত করা হয়েছে:

memoIntInt f = memoInt (\n -> memoInt (f n))

2

সূচি ছাড়াই সমাধান এবং এডওয়ার্ড কেএমইটিটির ভিত্তিতে নয়।

একটি সাধারণ পিতা বা মাতা সাধারণ সাব-ট্রি আউট আমি ফ্যাক্টর ( f(n/4)মধ্যে ভাগ করা আছে f(n/2)এবং f(n/4), এবং f(n/6)মধ্যে ভাগ করা আছে f(2)এবং f(3))। প্যারেন্টে একক ভেরিয়েবল হিসাবে তাদের সংরক্ষণ করে, সাবট্রির গণনা একবার করা হয়।

data Tree a =
  Node {datum :: a, child2 :: Tree a, child3 :: Tree a}

f :: Int -> Int
f n = datum root
  where root = f' n Nothing Nothing


-- Pass in the arg
  -- and this node's lifted children (if any).
f' :: Integral a => a -> Maybe (Tree a) -> Maybe (Tree a)-> a
f' 0 _ _ = leaf
    where leaf = Node 0 leaf leaf
f' n m2 m3 = Node d c2 c3
  where
    d = if n < 12 then n
            else max n (d2 + d3 + d4)
    [n2,n3,n4,n6] = map (n `div`) [2,3,4,6]
    [d2,d3,d4,d6] = map datum [c2,c3,c4,c6]
    c2 = case m2 of    -- Check for a passed-in subtree before recursing.
      Just c2' -> c2'
      Nothing -> f' n2 Nothing (Just c6)
    c3 = case m3 of
      Just c3' -> c3'
      Nothing -> f' n3 (Just c6) Nothing
    c4 = child2 c2
    c6 = f' n6 Nothing Nothing

    main =
      print (f 123801)
      -- Should print 248604.

কোডটি কোনও সাধারণ স্মৃতি ফাংশনে সহজেই প্রসারিত হয় না (কমপক্ষে, আমি কীভাবে এটি করব তা জানতাম না) এবং আপনাকে কীভাবে সাব-প্রব্লেমগুলি ওভারল্যাপ করে তা ভাবতে হবে, তবে কৌশলটি সাধারণ একাধিক অ-পূর্ণসংখ্যার পরামিতিগুলির জন্য কাজ করা উচিত । (আমি এটি দুটি স্ট্রিং প্যারামিটারের জন্য ভেবেছিলাম))

মেমো প্রতিটি গণনার পরে বাতিল করা হয়। (আবার আমি দুটি স্ট্রিং প্যারামিটার সম্পর্কে ভাবছিলাম was)

আমি জানি না এটি অন্যান্য উত্তরের চেয়ে বেশি দক্ষ কিনা। প্রতিটি অনুসন্ধান প্রযুক্তিগতভাবে কেবল এক বা দুটি পদক্ষেপ ("আপনার সন্তানের বা আপনার সন্তানের সন্তানের দিকে তাকান"), তবে অতিরিক্ত মেমরি ব্যবহারের প্রচুর পরিমাণ থাকতে পারে।

সম্পাদনা করুন: এই সমাধানটি এখনও সঠিক নয়। ভাগ করা অসম্পূর্ণ।

সম্পাদনা: এটি এখন সাবচিল্ডেনগুলিকে সঠিকভাবে ভাগ করা উচিত, তবে আমি বুঝতে পেরেছিলাম যে এই সমস্যাটিতে প্রচুর পরিমাণে অনানুষ্ঠানিক শেয়ারিং রয়েছে: n/2/2/2এবং n/3/3এটি একই রকম হতে পারে। সমস্যাটি আমার কৌশলটির জন্য উপযুক্ত নয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.