6
$\begingroup$

I have an array arr with n entries. Now I want to check whether

arr[[1]]==arr[[2]]&&arr[[2]]==arr[[3]]&&...arr[[n-1]]==arr[[n]]

Of course I could use a for-loop, like

arr = {0, 1, 0, 0};
IsConst = True;
For[i = 1, i <= Length[arr], i++,
  If[arr[[i]] != arr[[1]],
    IsConst = False;
    i = Length[arr];
  ];
];
Print[IsConst];

But that doesn't look too fast, so I wonder whether there is a faster way.

$\endgroup$
1
  • $\begingroup$ Union or DeleteDuplicates and then if the resulting list has length greater than 1 then the elements are not all the same (not constant in your terminology) $\endgroup$ Commented Jan 12, 2015 at 8:43

5 Answers 5

7
$\begingroup$

You can Apply (@@) Equal to your list:

constantQ = Equal@@#&;

constantQ @ {0,1,0,0}
(* False *)

or

constantQ2 = Max@#==Min@# &;
constantQ3 = Variance@#==0;
constantQ4 = Length@DeleteDuplicates@# == 1 &;

See also: SameQ

constantQ5 = SameQ @@ #&

Equal @@ {1, 1., 1}
(* True *)
SameQ @@ {1, 1., 1}
(* False *)

...

$\endgroup$
2
  • $\begingroup$ That looks nice, very interesting solutions (I should, at some point, really understand that kind of instructions you show). Thanks! $\endgroup$ Commented Jan 12, 2015 at 9:27
  • $\begingroup$ @NicoDean, my pleasure.. $\endgroup$
    – kglr
    Commented Jan 12, 2015 at 9:32
4
$\begingroup$

Not sure why people don't like DeleteDuplicates :)

Length@DeleteDuplicates@arr > 1

for me this is faster than other methods (using V9 OS X tonight)

$\endgroup$
3
$\begingroup$
If[Length[Tally[arr]] == 1, IsConst = True, IsConst = False]
$\endgroup$
1
  • 3
    $\begingroup$ If is redundant here: IsConst = Length@Tally@arr == 1 $\endgroup$
    – ybeltukov
    Commented Jan 12, 2015 at 9:37
3
$\begingroup$

Maybe the fastest is... just to compile the OP code

sameQ = Compile[{{arr, _Integer, 1}},
   Module[{IsConst = True, i},
    For[i = 1, i <= Length[arr], i++,
     If[arr[[i]] != arr[[1]],
      IsConst = False;
      Break[];
      ]];
    IsConst], CompilationTarget -> "C", "RuntimeOptions" -> "Speed"];

arr = ConstantArray[1, 10000000];
arr[[-1]] = 0;

sameQ[arr] // AbsoluteTiming
(* {0.027877, False} *)

constantQ4@arr // AbsoluteTiming (* fastest kguler's test *)
(* {0.062203, False} *)
$\endgroup$
2
  • $\begingroup$ Oh wow. That's exactly why I love SE :D $\endgroup$ Commented Jan 12, 2015 at 10:25
  • 2
    $\begingroup$ One of these days I really should install a C compiler... $\endgroup$
    – Mr.Wizard
    Commented Jan 12, 2015 at 10:57
2
$\begingroup$

This problem is in a manner an converse of How do you check if there are any equal arguments(even sublist) in a list? and is related to How do I check if any element in a list is positive? As with both of those there is a choice in approach of either scanning the entire light with a fast, vectorized operation, or providing for an early-exit behavior. Which one is desirable will depend on the most commonly tested expression form. Please review my answer to the second linked question for examples.

Using Tally or Max and Min are examples of methods that scan the entire list without the possibility of an early exist.

The length of the list can also affect the performance of the methods used. For example, as noted here UnsameQ works well on short lists (few arguments) but not on long ones. That doesn't appear to be the case with Equal and SameQ however so both of those are good general methods for your problem, and provide early exist behavior. You can choose between numeric and structural equivalence. kguler already showed these but repeated for completeness:

Equal @@ {1, 1, 1, 1}
SameQ @@ {1, 1, 1, 1}
True
True
Equal @@ {1, 1, 2, 1}
SameQ @@ {1, 1, 2, 1}
False
False
Equal @@ {1, 1, 1.0, 1}
SameQ @@ {1, 1, 1.0, 1}
True
False

To bring a unique method to this answer we can also use pattern matching for structural equivalence:

MatchQ[{1, 1, 1, 1}, {x_ ..}]
MatchQ[{1, 1, 2, 1}, {x_ ..}]
True

False

By naming the Pattern (x_) we restrict it to matching a particular expression. See:

$\endgroup$
3
  • 1
    $\begingroup$ w.r.t. to timings, users should be reminded that Apply will unpack. Therefore MatchQ or checking the length of a deleted duplicates list may well be better options for packed arrays of larger length $\endgroup$ Commented Jan 12, 2015 at 10:04
  • $\begingroup$ @Mike Indeed. The issue of unpacking is mentioned in my answer to "How do I check if any element in a list is positive?" which I intend for people to read. I chose not to repeat myself here but instead direct readers to the earlier questions. $\endgroup$
    – Mr.Wizard
    Commented Jan 12, 2015 at 10:10
  • $\begingroup$ no worries. Am watching the cricket, having a beer, and reading this ...am not a multi tasker! $\endgroup$ Commented Jan 12, 2015 at 10:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.