LoginSignup
2
1

More than 3 years have passed since last update.

F# Pipeline Operatorで末尾再帰最適化が効かない?

Last updated at Posted at 2019-09-18

AtCoderの過去問をF#で解いていてTLEに長時間悩まされたのでメモ。

int32では合算値が桁あふれしてしまう場合の対策として次のような自作関数を定義して使っていた。

let bigSum (arr : int []) =
    let rec loop (i : int) (sum : int64) =
        if i < 0 then sum
        else (sum + int64 arr.[i]) |> loop (i - 1)
    loop (arr.Length - 1) (int64 0)

しかし (sum + int64 arr.[i]) |> loop (i - 1) の書き方では10の5乗の要素があるような配列を与えた際に極端に速度が遅くなりTLEしてしまう。

let bigSum (arr : int []) =
    let rec loop (i : int) (sum : int64) =
        if i < 0 then sum
-       else (sum + int64 arr.[i]) |> loop (i - 1)
+       else loop (i - 1) (sum + int64 arr.[i])
    loop (arr.Length - 1) (int64 0)

このように書き直すと改善された。
それ以上の検証はしていないが、おそらくパイプライン演算子を用いた前者の書き方では末尾再帰最適化か効かないということだと推測している。

原因の突き止めにはコードテストが非常に役立った。

こちらに下のコードを貼って実行すると、

open System

let bigSum (arr : int []) =
    let rec loop (i : int) (sum : int64) =
        if i < 0 then sum
        else loop (i - 1) (sum + int64 arr.[i]) // 末尾再帰
    loop (arr.Length - 1) (int64 0)

[<EntryPoint>]
let main _argv =
    let n = 10.0 ** 5.0 |> int
    let aList =
        [ for _ in 0..n - 1 -> 10.0 ** 9.0 |> int ]
        |> List.toArray
    bigSum aList |> printfn "%d"
    0 // return an int exit code

終了コード 0
実行時間 60 ms

という結果になる。

一方、問題の書き方をすると、

open System

let bigSum (arr : int []) =
    let rec loop (i : int) (sum : int64) =
        if i < 0 then sum
        else sum + int64 arr.[i] |> loop (i - 1) // 末尾再帰最適化が効かない?
    loop (arr.Length - 1) (int64 0)

[<EntryPoint>]
let main _argv =
    let n = 10.0 ** 5.0 |> int
    let aList =
        [ for _ in 0..n - 1 -> 10.0 ** 9.0 |> int ]
        |> List.toArray
    bigSum aList |> printfn "%d"
    0 // return an int exit code

終了コード 9
実行時間 10508 ms

となる。

手元の端末だと後者のコードでも一瞬で処理が終わったのですぐ原因がわからなかった。
動作環境のスペックの違いによるものだと思うが、手元環境では .NetCore SDK 2.2.401 AtCoderでは Mono4.0 というランタイムの違いもあり、ひょっとしたらこちらということもあるかもしれない。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1