12

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

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

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

有没有更好的技巧来做到这一点?

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

这里还有一个问题:

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

使用 Mathematica 在具有相同长度的 N 个列表中找到最大列表的任何有效简单方法?

4

5 回答 5

18

方法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


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

于 2012-01-16T20:07:19.460 回答
8

例如,

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:由于大卫没有可用的 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 在评论中建议的那样,我替换PartCompile`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

于 2012-01-16T20:02:36.157 回答
4

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

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来实现equaland unequal)来实现其他比较运算符(>, <, ==, !=)。请记住UnitStep[0]==1

于 2012-01-16T20:37:01.283 回答
3

可以通过多种方式将比较函数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 <= b1 && a2 <= b2 && a3 <= b3.

于 2012-01-16T20:49:27.507 回答
2

当使用打包数组和数字比较器时,>=很难击败 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's Method #2 这样的循环是一个不错的选择。可能写得更好:

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

False
于 2012-01-17T07:33:11.347 回答