2017-09-29 78 views
2

从STDIN读取数千行时,我偶然发现了一个问题。这将是一个假想的边缘情况,直到我发现this problem的一些测试需要读取STDIN中的数千行。起初我认为我的算法不是最优的,只是偶然发现只有没有任何计算的读线才能使测试的一半时间结束。如何在Erlang中有效地读取STDIN中的数千行代码?

下面是部分代码超时:

process_queries(0, _) -> ok; 
process_queries(N, A) -> 
    case io:fread("", "~s~d~d") of 
     {ok, _} -> process_queries(N - 1, A) 
     %{ok, ["Q", L, R]} -> process_queries(N - 1, apply_q(L, R, A)); 
     %{ok, ["U", Idx, Val]} -> process_queries(N - 1, apply_u(Idx, Val, A)) 
    end 
. 

我故意留下评论表明,所有的计算被禁用。所以这个代码超时给出N=7984

有没有更好的方式来读取和处理Erlang中的数千行STDIN?

  • io:get_line一次只得到一行。
  • io:get_chars要求你知道要获得多少个字符。
+1

我怀疑在Erlang问题的实际世界中不存在的hackerrank相关问题会激发很多回应。如果您在这里没有找到答案,请尝试IRC或真实世界的问题。 – zxq9

回答

3

我建议将stdio切换为二进制,然后使用io:get_line。通过分割空白并将两个值转换为整数,您的数据格式非常易于解析。下面的代码在简单的基准测试中比我的代码运行速度快了10倍。我使用escript进行基准测试,这意味着它很可能差不多是10次以上,因为escript在运行时解析和编译代码。

process_queries_2(0, _) -> ok; 
process_queries_2(N, A) -> 
    Line = io:get_line(""), 
    [X, Y0, Z0, _] = binary:split(Line, [<<$\s>>, <<$\n>>], [global]), 
    Y = binary_to_integer(Y0), 
    Z = binary_to_integer(Z0), 
    % use X, Y, Z 
    process_queries_2(N - 1, A). 

下面是我用的基准代码:

main(["1"]) -> 
    ok = io:setopts(standard_io, [binary]), 
    process_queries(10000, {}); 
main(["2"]) -> 
    ok = io:setopts(standard_io, [binary]), 
    process_queries_2(10000, {}).% 
$ time yes 'Q 100 200' | escript a.erl 1 
yes 'Q 100 200' 4.64s user 0.11s system 93% cpu 5.063 total 
escript a.erl 1 4.67s user 1.44s system 120% cpu 5.062 total 
$ time yes 'Q 100 200' | escript a.erl 2 
yes 'Q 100 200' 0.36s user 0.01s system 77% cpu 0.466 total 
escript a.erl 2 0.40s user 0.10s system 106% cpu 0.464 total 

的原因加速是Erlang的字符串是链表,这是非常低效既为CPU时间和内存与二进制文件相比,这是一个连续的内存块。

3

从我的解决方案摘录。有几个技巧如何做到真正有效。

read_command(CP) -> 
    {ok, Line} = file:read_line(standard_io), 
    [C, A, B] = binary:split(Line, CP, [global, trim_all]), 
    {case C of <<"Q">> -> 'Q'; <<"U">> -> 'U' end, 
    binary_to_integer(A), 
    binary_to_integer(B)}. 

read_commands(N, CP) -> 
    [ read_command(CP) || _ <- lists:seq(1, N) ]. 

execute(Array, L) -> 
    lists:foldl(fun({'Q', F, T}, A) -> 
         {Val, A2} = query(A, F, T), 
         file:write(standard_io, [integer_to_binary(Val), $\n]), 
         A2; 
        ({'U', I, V}, A) -> 
         update(A, I, V) 
       end, Array, L). 

read_int_line(CP) -> 
    {ok, Line} = file:read_line(standard_io), 
    [binary_to_integer(X) || X <- binary:split(Line, CP, [global, trim_all])]. 

main() -> 
    ok = io:setopts([binary]), 
    CP = binary:compile_pattern([<<" ">>, <<$\n>>]), 
    [N] = read_int_line(CP), 
    L = read_int_line(CP), 
    N = length(L), 
    [K] = read_int_line(CP), 
    execute(init(L), read_commands(K, CP)). 

你必须写自己init/1update/3当然query/3

+0

谢谢,我有几个问题。为什么需要'init/1'功能?它看起来像整个数组已经在'L'中。 – m3nthal

+1

@ m3nthal:因为我使用不同的内部表示。 'L'是一个列表,而不是一个数组。它具有不良的访问特征。而且我还使用数字的不同表示法以及更快的最小公倍数计算。 –

+0

我做了一些研究,发现访问和随机更新时,字典和地图会更快。你如何表示数字?在二进制格式?也许你也使用二进制gcd算法? – m3nthal

相关问题