簡體   English   中英

F#或Ocaml優化

[英]F# or Ocaml optimization

我試圖理解為什么以下 Python 代碼比 F# 和 Ocaml 版本運行得更快:

from sys import stdin, stdout

def read():
    return stdin.readline().rstrip()
    
def readints():
    return [int(x) for x in read().split()]

_, _, q = readints()
A = readints()
B = readints()

for _ in range(q):
    arr = [0]*4001
    l1, r1, l2, r2 = readints()
    for i in range(l1-1, r1):
        arr[A[i]] ^= 1
    for i in range(l2-1, r2):
        arr[B[i]] ^= 1
    result = sum(arr)
    stdout.write(str(result)+"\n")

這是我花了幾個小時學習該語言的 Ocaml 版本:

let open Printf in
let parse_line () =
    read_line()
    |> String.trim
    |> Str.split (Str.regexp_string " ")
    |> Array.of_list
    |> Array.map int_of_string in
let nmq = parse_line() in
let n = nmq.(0) in
let m = nmq.(1) in
let q = nmq.(2) in
let a = parse_line() in
let b = parse_line() in
for i = 1 to q do
    let arr = Array.create 4001 0 in
    let llrr = parse_line() in
    let l1 = llrr.(0) in
    let r1 = llrr.(1) in
    let l2 = llrr.(2) in
    let r2 = llrr.(3) in
    for i = l1-1 to r1-1 do
        arr.(a.(i)) <- arr.(a.(i)) lxor 1
    done;
    for i = l2-1 to r2-1 do
        arr.(b.(i)) <- arr.(b.(i)) lxor 1
    done;
    printf "%d\n" (Array.fold_left (+) 0 arr)
done;

這是相同的,但在 F#

open System
open System.IO
let out = new StreamWriter(Console.OpenStandardOutput())
let readints () = [ for x in stdin.ReadLine().TrimEnd().Split() -> int(x) ]
let mutable [ _; _; q ] = readints()
let a = readints()
let b = readints()
for _ in 1..q do
  let arr = Array.create 4001 0
  let [ l1; r1; l2; r2 ] = readints()
  for i in l1-1 .. r1-1 do
    arr.[a.[i]] <- arr.[a.[i]] ^^^ 1
  for i in l2-1 .. r2-1 do
    arr.[b.[i]] <- arr.[b.[i]] ^^^ 1
  out.WriteLine(Array.sum(arr))
out.Close()

示例輸入:

5 5 2     
76 56 34 52 12
10 91 86 10 91    
1 5 2 5
2 2 2 5

Output:

7
3

關於如何提高 Ocaml 或 F# 程序的性能的任何想法? 謝謝!

更新

實現@FyodorSoikin 和@JL0PD F# 代碼現在通過了 5 個測試用例中的 2 個:

open System
open System.IO

let out = new StreamWriter(Console.OpenStandardOutput())

let readints () = [| for x in stdin.ReadLine().TrimEnd().Split() -> int(x) |]

let mutable q = readints().[2]
let a = readints()
let b = readints()

for t=1 to q do
  let arr = Array.zeroCreate 4001
  let llrr = readints()
  let l1 = llrr.[0]
  let r1 = llrr.[1]
  let l2 = llrr.[2]
  let r2 = llrr.[3]
  for i = l1-1 to r1-1 do
    arr.[a.[i]] <- arr.[a.[i]] ^^^ 1
  for i = l2-1 to r2-1 do
    arr.[b.[i]] <- arr.[b.[i]] ^^^ 1
  out.WriteLine(Array.sum(arr))

out.Close()

這是一個更慣用的 OCaml 代碼,雖然你沒有告訴任務是什么,所以實現只是盡可能接近你的

open Printf

let parse_line () =
  read_line () |> String.trim |>
  String.split_on_char ' ' |>
  List.map int_of_string |>
  Array.of_list


let main () =
  match parse_line () with
  | [|n;m;q|] ->
    let a = parse_line () in
    let b = parse_line () in
    let arr = Array.make 4001 0 in
    for i = 1 to q do
      match parse_line () with
      | [|l1; r1; l2; r2 |] ->
        Array.fill arr 0 (Array.length arr) 0;
        for i = l1-1 to r1-1 do
          arr.(a.(i)) <- arr.(a.(i)) lxor 1
        done;
        for i = l2-1 to r2-1 do
          arr.(b.(i)) <- arr.(b.(i)) lxor 1
        done;
        printf "%d\n" (Array.fold_left (+) 0 arr)
      | _ -> failwith "wrong input: expects four numbers"
    done
  | _ -> failwith "wrong input on line 1: expects three numbers"

let () = main ()

它使用更高效的parse_line function 不依賴正則表達式進行字符串拆分,但我認為這並不重要,因為它不經常被調用(假設q很小)。 另一個小的優化是我將數組創建移出循環並用零重新填充它,而不是在每次迭代時創建一個新的(也是一個小的優化,考慮到 OCaml 的速度有多快,我不希望它產生很大的影響) s GC 正在工作)。

更好的實現將完全避開中間arr值。

另外,我真的不認為 Python 實現在絕對數字上比 OCaml 或 F# 快,我認為它沒有通過測試,因為它比其他 OCaml 實現慢。

PS 找到實際任務后,我可以看到q可能非常大, parse_line將成為瓶頸,這里有很大的改進空間。

此外,您的方法的最弱點是您正在執行 4k 迭代並創建一個 4k 條目數組,兩者都是完全不必要的並且對性能至關重要。 您只需要計算主題的數量,因此為每個可能的主題編號分配一個包含 4k 個條目的數組並不是最優的。 您可以為此使用哈希表。

這是最終通過 F# 中 5 個測試用例中的 5 個的版本。 我不相信這個問題今天可以在 Python 作為平台中的當前約束來解決。

open System
open System.Collections

// Required manually implementation since the old .NET didn't support BitOperations.PopCount
let cardinality (bitArray: BitArray) =
    let arr: int32[] = Array.create ((bitArray.Length >>> 5) + 1) 0
    bitArray.CopyTo(arr, 0)
    let mutable count = 0
    let n = arr.Length-1
    arr.[n] <- arr.[n] &&& (~~~(-1 <<< (bitArray.Count % 32)))
    for i = 0 to n do
        let mutable c = arr.[i]
        c <- c - ((c >>> 1) &&& 0x55555555)
        c <- (c &&& 0x33333333) + ((c >>> 2) &&& 0x33333333)
        c <- ((c + (c >>> 4) &&& 0xF0F0F0F) * 0x1010101) >>> 24
        count <- count + c
    count

let readints () = [| for x in stdin.ReadLine().TrimEnd().Split() -> int(x) |]

let [| n; m; q |] = readints()
let a = readints()
let b = readints()

let abits = Array.init (n+5) (fun _ -> BitArray(4005))
let bbits = Array.init (m+5) (fun _ -> BitArray(4005))

for i = 1 to n do
    let k = a.[i-1]
    abits.[i] <- BitArray(abits.[i-1])
    abits.[i].[k] <- not abits.[i].[k]

for i = 1 to m do
    let k = b.[i-1]
    bbits.[i] <- BitArray(bbits.[i-1])
    bbits.[i].[k] <- not bbits.[i].[k]

for i = 1 to q do
  let [| l1; r1; l2; r2 |] = readints()
  let result = BitArray(abits.[l1-1]).Xor(BitArray(abits.[r1]))
  result.Xor(BitArray(bbits.[l2-1]).Xor(BitArray(bbits.[r2])))
  printfn "%d" (cardinality result)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM