Comment detail

分数を小数に展開 (Nested Flatten)

This comment is reply for 6148 [1..100]>>=pen: ウサギとカメによる循環検出アルゴリズム ...(分数を小数に展開). Go to thread root.

http://en.wikipedia.org/wiki/Floyd's_cycle-finding_algorithm
に書いてあったもう一つの循環検出アルゴリズム
「Brent のアルゴリズム」を使ったバージョン。

Brent によるとこの循環検出方法は Floyd の方法より平均で約36%速いそうです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import List (unfoldr,findIndex)
import Monad (msum)

{-
余り列からμ(循環部より前の長さ)とλ(循環部の長さ)を求める

(A) xs を1個、2個、4個、8個・・・からなる区間に分ける。
  例えば xs = [1..] だったら [[1],[2,3],[4,5,6,7],..] に分けるように。
  (本当はさらにもうちょっと加工している)
(B) 各区間で先頭の値と同じものが区間中にあるか検索する
(C) 最初に見つかった検索成功情報を取ってくる。
  成功情報は Just i の形で i は λ-1 に等しい。
(D) インデックスを Just の中から取り出し1加える。
(E) λステップ進んだ位置とスタート位置から同時に進む(カウンタ付き)。
(F) 循環して出会うまで待つ。
-}

muLam xs = (mu, if (d==0) then 0 else lam) where
  ys = unfoldr f (1,xs)                                         -- (A)
  f (m,zs) = Just (splitAt 1 zs1, (2*m,zs2)) where
    (zs1,zs2) = splitAt m zs
  lam = maybe 0 (+1)                                            -- (D)
    $ msum                                                      -- (C)
    $ zipWith (\([a],as) ([b],_) -> findIndex (==a) (as++[b]))  -- (B)
        ys (tail ys)
  (_,d,mu) = head $ dropWhile (\(a,b,_) -> a/=b)                -- (F)
    $ zip3 xs (drop lam xs) [0..]                               -- (E)

toDecimal m n = show q0 ++ "." ++ (qs1 >>= show) ++ cyc  where
  (q0,m') = divMod m n
  (_:qs,rs) = unzip $ iterate ((`divMod` n).(10*).snd) (0,m')
  (mu,lam) = muLam rs
  (qs1,qs2) = splitAt mu qs
  cyc | lam == 0  = ""
      | otherwise = "{" ++ (take lam qs2 >>= show) ++ "}"

Index

Feed

Other

Link

Pathtraq

loading...