9

Imagine you have a list of pairs of numbers, such as

{{2,-2}, {1,5}, {1,-3},{-3,3}} 

The task is to reduce the given list to a list of pairs of type {a,-a}, where a may be any real number, i.e. our initial list should be reduced to {{2,-2},{-3,3}}.

I have come up with a somewhat cumbersome solution

DeleteCases[{{2, -2}, {1, 5}, {1, -3}, {-3, 3}}, {a_, b_} /; a + b != 0]

which is, on top of that, not particularly fast. The following code

Ta = Flatten[Table[{i, j}, {i, -1000, 1000}, {j, -1000, 1000}], 1];
Timing[DeleteCases[Ta, {a_, b_} /; a != -b]]`

yields

4.945, {{-1000, 1000}, {-999, 999}, (*...I have cut out the rest*)}

Now the question is:

  • Is this an overkill?
  • Can I perform the task in a more efficient manner?
  • Can I maybe define the pattern in a more natural way, which happens to be more efficient as well?

Thanks a lot!

P.S.:

The question might be a bit ambiguous but I'd like to hear all your suggestions. :)

xyz
  • 605
  • 4
  • 38
  • 117
Mischa
  • 135
  • 6
  • Welcome to Mathematica.SE! I suggest that: 1) You take the introductory Tour now! 2) When you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. Also, please remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign! 3) As you receive help, try to give it too, by answering questions in your area of expertise. – bbgodfrey Apr 08 '15 at 16:20
  • What's the distribution of pair signs? Any of the answers so far s/b easy to better for non-uniform. For that matter, how large are lists you care about? For up to midsized ones, anything other than deletecases should suffice. – ciao Apr 08 '15 at 19:51

2 Answers2

14
lst = {{2, -2}, {1, 5}, {1, -3}, {-3, 3}};
Pick[lst, Total[lst, {2}], 0]

{{2,-2}, {-3,3}}

First @ Timing[res0 = Pick[Ta, Total[Ta, {2}], 0];]

0.093750

First @ Timing[res0a = Pick[Ta, Total /@ Ta, 0];]

0.187500

First @ Timing[res0b = Pick[Ta, Plus @@ Transpose[Ta], 0];] (*thanks: 2012rcampion *)

0.062500

First @ Timing[res1 = DeleteCases[Ta, {a_, b_} /; a != -b];]

4.093750

First @ Timing[res2 = Cases[Ta, {a_, b_} /; a + b == 0];]

3.656250

Equal @@ {res0, res0a, res0b, res1, res2}

True

Update: for the case where a is any real number you can Unitize the selector array (thanks: @MrWizard):

First @ Timing[Pick[Ta, Unitize@Total[Ta, {2}], 0];]

0.125000

First @ Timing[Pick[Ta, Unitize[Total /@ Ta], 0];]

0.156250

First @ Timing[Pick[Ta, Unitize[Plus @@ Transpose[Ta]], 0];] 

0.046875

kglr
  • 394,356
  • 18
  • 477
  • 896
  • Much faster! Why is that? – bbgodfrey Apr 08 '15 at 16:57
  • 2
    You can get a 2-3x speedup by using Plus @@ Transpose[Ta] instead of Total[Ta, {2}]. – 2012rcampion Apr 08 '15 at 17:01
  • Most of the time goes into computing the Pick selection criterion, and Plus @@ Transpose[Ta] indeed is nearly twice as fast as Total[Ta, {2}]. Since all these approaches involve the same simple calculations, some pipelining must be involved in the faster ones to compensate for the extra memory accesses. – bbgodfrey Apr 08 '15 at 17:11
  • @bbgodfrey, Pick usually is faster than its siblings Select, Cases, DeleteCases. Re why, I will try to find references in this site. – kglr Apr 08 '15 at 17:12
  • @2012rcampion, thank you; updated with your suggested selector array. – kglr Apr 08 '15 at 17:19
  • 2
    @bbgodfrey The keyword is packed arrays. Working with packed arrays is very fast, but many functions don't support them. Since version 8 only, Pick will work with packed arrays when used in certain ways. – Szabolcs Apr 08 '15 at 17:42
  • 4
    @bbgodfrey You touched on a big annoyance of Mathematica. Performance is really hard to predict and unintuitive. How could you have known that Pick would be so fast? There's no way to guess it and no documentation on this. I know it due to experience and having spent too much time on this site. Regarding faster alternatives to things like Select or Cases, check out my BoolEval package, which only works in special situations, but when it does, it can be much faster. – Szabolcs Apr 08 '15 at 17:44
  • Mischa says any real number so you will need to add Unitize to your Pick code to catch inexact zeros. +1 nevertheless – Mr.Wizard Apr 08 '15 at 17:48
1
Cases[{{2, -2}, {1, 5}, {1, -3}, {-3, 3}}, {a_, b_} /; a + b == 0]

produces the same result and is modestly faster.

Ta = Flatten[Table[{i, j}, {i, -1000, 1000}, {j, -1000, 1000}], 1];
Timing[DeleteCases[Ta, {a_, b_} /; a != -b];]
(* {4.34375, Null} *)

Timing[Cases[Ta, {a_, b_} /; a + b == 0];]
(* {3.92188, Null} *)

An irreducible amount of time goes to loading each pair, comparing the elements, and writing the answers to a new List. Probably, we are near that irreducible limit. In fact, I am uncertain why Cases would work faster than DeleteCases in this instance, since they both do the same thing.

bbgodfrey
  • 61,439
  • 17
  • 89
  • 156
  • The :> {a, b} is unnecessary in your Cases statement and might squeeze a few milliseconds off the timing! – MikeLimaOscar Apr 08 '15 at 16:39
  • You are correct, and I shall make the change, but it saves no time. Thanks. – bbgodfrey Apr 08 '15 at 16:42
  • I found that a conditional pattern takes about twice as long to evaluate as a construct such as Cases[{#[[1]], -#[[2]]} & /@ ta, {a_, a_}]. Mr.Wizard's answer vastly outperforms us though. – LLlAMnYP Apr 09 '15 at 10:20