【问题标题】:Is there any efficient easy way to compare two lists with the same length with Mathematica?是否有任何有效的简单方法可以将两个具有相同长度的列表与 Mathematica 进行比较?
【发布时间】:2012-01-16 19:52:14
【问题描述】:

给定两个列表A={a1,a2,a3,...an}B={b1,b2,b3,...bn},我会说A>=B 当且仅当所有ai>=bi

有两个列表的内置逻辑比较A==B,但没有A>B。 我们是否需要像这样比较每个元素

And@@Table[A[[i]]>=B[[i]],{i,n}]

有更好的技巧吗?

编辑: 非常感谢你们所有人。

还有一个问题:

如何在 N 个列表中找到最大列表(如果存在)?

Any efficient easy way to find the maximum list among N lists with the same length using Mathematica?

【问题讨论】:

    标签: arrays list wolfram-mathematica mathematica-8


    【解决方案1】:

    方法一:我比较喜欢这种方法。

    NonNegative[Min[a - b]]
    

    方法2:这只是为了好玩。正如 Leonid 所指出的,对于我使用的数据,它被赋予了一些不公平的优势。 如果进行成对比较,并在适当的时候返回 False 和 Break, 那么循环可能更有效(尽管我通常在 mma 中避开循环):

    result = True;
    n = 1; While[n < 1001, If[a[[n]] < b[[n]], result = False; Break[]]; n++]; result
    

    对 10^6 个数字列表的一些时间比较:

    a = Table[RandomInteger[100], {10^6}];
    b = Table[RandomInteger[100], {10^6}];
    
    (* OP's method *)
    And @@ Table[a[[i]] >= b[[i]], {i, 10^6}] // Timing
    
    (* acl's uncompiled method *)
    And @@ Thread[a >= b] // Timing
    
    (* Leonid's method *)
    lessEqual[a, b] // Timing
    
    (* David's method #1 *)
    NonNegative[Min[a - b]] // Timing
    


    编辑:我删除了方法 #2 的时间安排,因为它们可能会产生误导。并且方法 #1 更适合作为通用方法。

    【讨论】:

    • 请注意,由于您生成随机列表,因此您的最后一个方法(其中有奇怪的硬编码常量,但我认为这些是您的开发代码的残余。无论如何,他们在这里弄错了,但是让我们暂时忽略这一点)获得了不公平的优势,因为从统计上讲,条件违规将在循环的早期发生。您还应该测试结果为True 的列表 - 在这种情况下,顶级列表保证会更慢。实际上,您的最后一个方法是 @acl 编译代码的顶级变体。
    • 这当然不减损你漂亮快速的第一种方法,我很佩服。
    • 嗯,最后一个要快得多,因为它会短路,只要任何两个元素不匹配就会断开。在最坏的情况下尝试(即,当它们都必须进行比较时,例如a=b),它是迄今为止最慢的。我认为最快的是编译方法cmp
    • 顺便说一下,在许多情况下,编译(到 C)循环比您可以做的任何其他事情都更有效。总体而言,未编译的顶级循环往往是最慢的(除非您使用的是效率更高的算法,就像您在这里一样)。
    • @Leonid。我也更喜欢方法#1。方法 #2 是检查 Th 早早脱离循环的速度优势。您对列表生成非常正确:如果将数字随机分配给 a、b,那么方法 #2 会获得不公平的优势。
    【解决方案2】:

    例如,

    And @@ Thread[A >= B]
    

    应该做的工作。

    编辑:另一方面,这个

    cmp = Compile[
      {
       {a, _Integer, 1},
       {b, _Integer, 1}
       },
      Module[
       {flag = True},
       Do[
        If[Not[a[[p]] >= b[[p]]], flag = False; Break[]],
        {p, 1, Length@a}];
       flag],
      CompilationTarget \[Rule] "C"
      ]
    

    快 20 倍。不过也丑了 20 倍。

    编辑 2:由于 David 没有可用的 C 编译器,这里是所有计时结果,有两个不同之处。首先,他的第二种方法已被固定为比较所有元素。其次,我将a 与自身进行比较,这是最坏的情况(否则,我上面的第二种方法只需将元素与第一个元素进行比较即可违反条件)。

    (*OP's method*)
    And @@ Table[a[[i]] >= b[[i]], {i, 10^6}] // Timing
    
    (*acl's uncompiled method*)
    And @@ Thread[a >= b] // Timing
    
    (*Leonid's method*)
    lessEqual[a, b] // Timing
    
    (*David's method #1*)
    NonNegative[Min[a - b]] // Timing
    
    (*David's method #2*)
    Timing[result = True;
     n = 1; While[n < Length[a], 
      If[a[[n]] < b[[n]], result = False; Break[]];
      n++]; result]
    
    (*acl's compiled method*)
    cmp[a, a] // Timing
    

    所以编译的方法要快很多(注意大卫的第二种方法和这里的编译方法是同一个算法,唯一的区别就是开销)。

    所有这些都是电池供电,因此可能会有一些随机波动,但我认为它们具有代表性。

    编辑 3:如果像 ruebenko 在评论中建议的那样,我将 Part 替换为 Compile`GetElement,就像这样

    cmp2 = Compile[{{a, _Integer, 1}, {b, _Integer, 1}}, 
      Module[{flag = True}, 
       Do[If[Not[Compile`GetElement[a, p] >= Compile`GetElement[b, p]], 
         flag = False; Break[]], {p, 1, Length@a}];
       flag], CompilationTarget -> "C"]
    

    那么cmp2 的速度是cmp 的两倍。

    【讨论】:

    • +1。你可能会更简单一点:cmp1 = Compile[{{a, _Integer, 1}, {b, _Integer, 1}}, Do[If[a[[p]] &lt; b[[p]], Return[False]], {p, 1, Length@a}] != False, CompilationTarget -&gt; "C"].
    • 编译速度明显更快。尽管如此,我很惊讶我的方法 #1 的效果和它一样好。
    • @David 也许是这么快,因为DeveloperPackedArrayQ[a - b]` 返回True
    • 为了好玩,试试 Compile`GetElement[a,p] 而不是 Part[a,p] 和第二个。
    • 当结果为 True 时,我的方法 #2 绝对不适合大型列表。
    【解决方案3】:

    由于您在问题中提到效率是一个因素,您可能会发现这些功能很有用:

    ClearAll[lessEqual, greaterEqual];
    lessEqual[lst1_, lst2_] :=
       SparseArray[1 - UnitStep[lst2 - lst1]]["NonzeroPositions"] === {};
    
    greaterEqual[lst1_, lst2_] :=
       SparseArray[1 - UnitStep[lst1 - lst2]]["NonzeroPositions"] === {};
    

    这些功能将相当有效。 @David 的解决方案仍然快两四倍,如果你想要极快的速度并且你的列表是数字的(由整数或实数组成),你可能应该使用编译到 C (@acl 的解决方案和其他类似的解决方案)运算符)。

    您可以使用相同的技术(使用Unitize 而不是UnitStep 来实现equalunequal),来实现其他比较运算符(&gt;&lt;==、@987654329 @)。请记住UnitStep[0]==1

    【讨论】:

    • 根据我的计时结果,你的函数比我的慢 4 倍以上。
    • @David 我认为这取决于数据。在我的测试中,我有 2 倍的差异。但是,是的,它们比你的第一个函数慢(至少比你的第一个函数)。不过我仍然会保留这个答案,因为它说明了如何使用SparseArray-s,但我同意你的解决方案是优越的。
    • 我提到了基于方法#1的计时比例(因为方法#2真的只是为了好玩)。
    • @David 我也指方法#1。我有:largeTst1 = RandomInteger[1000, 10000000];largeTst2 = RandomInteger[{999, 1990}, 10000000];In[95]:= NonNegative[Min[largeTst2-largeTst1]]//Timing Out[95]= {0.125,False}; In[96]:= greaterEqual[largeTst2,largeTst1]//Timing Out[96]= {0.234,False}.
    【解决方案4】:

    Greater, GreaterEqual, Equal, Less, LessEqual 之类的比较函数可以通过多种方式应用于列表(它们都是您问题中方法的变体)。

    有两个列表:

     a={a1,a2,a3};
     b={b1,b2,b3};
    

    和两个带有数字条目的实例

    na={2,3,4}; nb={1,3,2}; 
    

    你可以使用

    And@@NonNegative[na-nb]
    

    带有符号条目的列表

    And@@NonNegative[na-nb]
    

    给予

    NonNegative[a1 - b1] && NonNegative[a2 - b2] && NonNegative[a3 - b3]
    

    对于一般比较,可以创建一个一般比较函数,如

    listCompare[comp_ (_Greater | _GreaterEqual | _Equal | _Less | _LessEqual), 
             list1_List, list2_List] := And @@ MapThread[comp, {list1, list2}]
    

    用作

    listCompare[GreaterEqual,na,nb]
    

    给出True。带有符号条目

    listCompare[GreaterEqual,a,b]
    

    给出逻辑上等价的表达式a1 &lt;= b1 &amp;&amp; a2 &lt;= b2 &amp;&amp; a3 &lt;= b3

    【讨论】:

      【解决方案5】:

      在使用压缩数组和数值比较器(例如 &gt;=)时,很难击败 David 的方法 #1。

      但是,对于无法转换为简单算术的更复杂的测试,则需要另一种方法。

      一个很好的通用方法,特别是对于解压列表,是使用Inner

      Inner[test, a, b, And]
      

      这不会提前进行所有比较,因此在某些情况下可能比例如And @@ MapThread[test, {a, b}]。这说明了差异:

      test = (Print[#, " >= ", #2]; # >= #2) &;
      
      {a, b} = {{1, 2, 3, 4, 5}, {1, 3, 3, 4, 5}};
      
      Inner[test, a, b, And]
      
      1 >= 1
      2 >= 3
      
      False
      
      And @@ MapThread[test, {a, b}]
      
      1 >= 1
      2 >= 3
      3 >= 3
      4 >= 4
      5 >= 5
      
      False
      

      如果数组是打包的,特别是如果返回 False 的可能性很高,那么像 David 的方法 #2 这样的循环是一个不错的选择。可能写得更好:

      Null === Do[If[a[[i]] ~test~ b[[i]], , Return@False], {i, Length@a}]
      
      1 >= 1
      2 >= 3
      
      False
      

      【讨论】:

      • 其实一点都不难,在我的回答中有一个方法大约快一个数量级...(关于Inner的好提示,我不知道效率优势,+ 1)
      • @acl 我不算编译为 C。;-) (我知道我可能应该克服它,但在 v7 中没有它和需要以那种糟糕的程序风格编写之间我只是还没准备好接受它作为 Mathematica。)感谢您的投票。
      • 为什么说“如果数组被打包...”那么循环是一个不错的选择?如果我解压a 并设置b=a,解压列表版本会比打包版本慢一点。即,打包并不会真正影响Do 和公司的业绩(尽管它们似乎没有解包)。还是我误会了?
      • @acl 我的意思是在使用Inner 时,即使第一对没有通过test,也会有很大的解包开销。在Do 中使用Part 可以避免这种情况。如果你明白我的意思,并且能想出更好的表达方式,请这样做。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-04
      • 2016-05-17
      • 1970-01-01
      • 1970-01-01
      • 2016-02-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多