【问题标题】:Cartesian Power of a list in ErlangErlang中列表的笛卡尔幂
【发布时间】:2012-11-05 22:47:17
【问题描述】:

我正在尝试编写一个 erlang Mastermind 求解器作为练习(我是一个完全的新手,但我认为这是一个函数式语言的有趣练习)

我希望它尽可能通用,所以我觉得我需要一个笛卡尔幂函数。比如:

cart_pow([a,b],2) -> [[a,a],[a,b],[b,a],[b,b]]
cart_pow([a,b],3) -> [[a,a,a],[a,a,b],[a,b,a],[a,b,b],[b,a,a],[b,a,b],[b,b,a],[b,b,b]]

我想不出一个纯粹的函数式(递归、映射、折叠...)解决方案。 有什么线索吗?如果它是懒惰的奖励。

【问题讨论】:

    标签: functional-programming erlang


    【解决方案1】:

    @Ed'ka 提供的解决方案简洁而漂亮,但尽管如此,它的复杂性却是O(N)

    我建议您考虑Exponentiation by squaring method,它提供了O(log(N)) 计算功率的复杂性。使用这种技术,笛卡尔幂可以这样实现:

    %% Entry point
    cart(List, N) ->
            Tmp = [[X] || X <- List],
            cart(Tmp, Tmp, N).
    
    cart(_InitialList, CurrList, 1) ->
            CurrList;
    cart(_InitialList, CurrList, N) when N rem 2 == 0 ->
            Tmp = mul(CurrList, CurrList),
            cart(Tmp, Tmp, N div 2);
    cart(InitialList, CurrList, N) ->
            Tmp = cart(InitialList, CurrList, N - 1),
            mul(InitialList, Tmp).
    
    mul(L1, L2) ->
            [X++Y || X <- L1, Y <- L2].
    

    附: shell的使用示例(我已经将函数cart打包成mudulemy_module):

    1> c(my_module).
    {ok,my_module}
    2> 
    2> my_module:cart([0,1], 2).
    [[0,0],[0,1],[1,0],[1,1]]
    3> 
    3> my_module:cart([0,1], 3).
    [[0,0,0],
     [0,0,1],
     [0,1,0],
     [0,1,1],
     [1,0,0],
     [1,0,1],
     [1,1,0],
     [1,1,1]]
    

    【讨论】:

    • mul 是丢失的部分。我无法让它返回正确的结构,主要是因为我不明白正确的输入不是 ([a,b],[a,b]) 而是 ([[a],[b]] ,[[a],[b]])
    • 在函数mul 计算两个列表的笛卡尔积。您可能会注意到那里使用了列表连接语法(运算符++)。因此,这意味着 - 该函数仅适用于列表列表。这就是为什么你必须打电话给mul([[a],[b]], [[a],[b]]) 而不是mul([a,b], [a,b])。如果您查看入口点(函数cart/2) - 您可能会注意到,我将输入列表转换为列表列表(通过将每个元素包装在其自己的列表中:[[X] || X &lt;- List],所以列出@ 987654334@ 转换为 [[a],[b]])。
    • 如果++ 运算符为 O(1) 则为 O(log(N)) 但不幸的是它为 O(N) (其​​中 N 是左参数的长度)如此有效只是用单个++ 替换多个[|]。您的解决方案确实比我的第一个版本运行得更快(恒定差异),但我怀疑这是由于第二种情况下的尾调用。例如,如果我使我的解决方案尾递归(请参阅我的更新),它会变得比你的更快(同样,一个恒定的差异)。
    【解决方案2】:

    来自 Haskell 实现:

    cart_pow(Xs, N) -> 
        sequence(lists:duplicate(N, Xs)).
    
    sequence([]) ->
        [[]];
    sequence([Xs|Xss]) ->
        [[X|Xs1] || X <- Xs, Xs1 <- sequence(Xss)].
    

    不确定如何让 Erlang 的列表变得懒惰。

    更新: 这个版本可以通过简单地使其尾递归来提高性能(尽管我相信这三个版本之间没有渐近差异)

    cart_pow(Xs, N) -> 
        sequence(lists:duplicate(N, Xs)).
    
    sequence(Xss) ->
        sequence(Xss, [[]]).
    
    sequence([], Acc) ->
        Acc;
    sequence([Xs|Xss], Acc) ->
        sequence(Xss, [[X|Xs1] || X <- Xs, Xs1 <- Acc]).
    

    与@stemm 的版本相比:

    1> timer:tc(fun() -> length(tmp1:cart([0,1], 20)) end).
    {383939,1048576}
    2> timer:tc(fun() -> length(tmp1:cart_pow([0,1], 20)) end).
    {163932,1048576}
    

    PS:甚至更好:

    sequence(Xss) ->
        lists:foldl(fun(Xs, A) -> [[X|Xs1] || X <- Xs, Xs1 <- A] end, [[]], Xss).
    

    【讨论】:

      【解决方案3】:

      您可能会发现这个 Stack Overflow 问题很有帮助,它涉及在函数式语言中生成列表的笛卡尔幂。该问题针对 F#,但 cmets 中也有一个 Haskell 示例:F#: how to find Cartesian power

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-15
        • 1970-01-01
        • 2019-03-31
        • 1970-01-01
        • 2011-02-08
        • 2012-09-07
        • 1970-01-01
        • 2012-03-24
        相关资源
        最近更新 更多