2016-07-05 3 views
4

Я хотел бы сгенерировать unboxed вектор рекурсивно. В качестве простого примера:Возможны ли рекурсивные определения с незанятыми векторами?

import qualified Data.Vector as V 

fib :: V.Vector Int 
fib = V.generate 10 f 
    where 
    f 0 = 0 
    f 1 = 1 
    f x = (fib V.! (x - 1)) + (fib V.! (x - 2)) 

Функция правильно генерирует последовательность фибоначчи. Однако, если вместо этого я использую Data.Vector.Unboxed, код будет висеть. Я понимаю причину, почему это так, но я все же хотел бы иметь возможность сделать рекурсивное определение и получить скорость распакованного вектора. Существуют ли какие-либо возможности для этого?

ответ

5

Одним из возможных вариантов является использование распакованный изменяемые вектор и заморозить когда-то сделано со строительством:

import Control.Monad.ST (runST) 
import Control.Monad (forM_, ap) 
import qualified Data.Vector.Unboxed as U 
import qualified Data.Vector.Unboxed.Mutable as M 

fib :: Int -> U.Vector Int 
fib s = runST $ M.new s >>= ap ((>>) . forM_ [0..s - 1] . f) U.unsafeFreeze 
    where 
    f v 0 = M.write v 0 0 
    f v 1 = M.write v 1 1 
    f v i = do 
     a <- M.read v (i - 1) 
     b <- M.read v (i - 2) 
     M.write v i (a + b) 
3

Единственный способ, которым я могу это сделать, - создать ленивый рекурсивный список, а затем преобразовать его в unboxed vector.

Стандарт озадачивает колдовство

fibs = 1 : 1 : zipWith (+) fibs (tail fibs) 

даст вам бесконечный список чисел Фибоначчи. Затем вы можете создать список из списка, который будет быстро индексироваться.

Несомненно, на самом деле он не использует вектор для вычисления номера Фибоначчи. Но он не делает индексацию O (n) по связанному списку, поэтому он должен быть достаточно быстрым.

+0

Спасибо. К сожалению, я на самом деле реализую алгоритм динамического программирования, так что, когда я завершаю вычисление вектора, я также получаю свой ответ и делаю с ним. Поэтому в моем фактическом случае это будет не очень полезно. Но в целом я бы сказал, что это хороший подход. – rityzmon

+0

@ritzymon В этом случае, вы уверены, что хотите распаковать вектор? Стандартные методы динамического программирования с использованием списков часто довольно эффективны - в частности, они вообще не будут хранить весь список в памяти в любой точке. –

4

constructN делает то, что вы хотите, даже в случае Unboxed. Ниже, v - это векторный префикс, построенный до сих пор, и f v возвращает следующий элемент.

Оптимальная сложность O (N).

import qualified Data.Vector.Unboxed as V 

fib :: V.Vector Int 
fib = V.constructN 10 f 
    where 
    f v = case V.length v of 
      0 -> 0 
      1 -> 1 
      n -> (v V.! (n - 1)) + (v V.! (n - 2)) 
+1

Что делать, если f (n) не является функцией от одних предыдущих индексов 0..n-1? скажем, вы просматриваете график, и в каждом узле интересующие индексы представляют собой индексы соседних узлов –

+0

@ behzad.nouri Конечно, в общем случае 'constructN' недостаточно, но, на мой взгляд, он охватывает многие случаи на практике. В самом общем случае я бы использовал вложенные векторы. Если бы я знал, сколько шагов требуется для достижения фиксированной точки (что может быть намного меньше длины вектора, в общем случае), я мог бы также рассмотреть изменяемые векторы, как в вашем ответе. – chi

Смежные вопросы