Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test if a list contains consecutive integers in Mathematica?

I want to test if a list contains consecutive integers.

 consQ[a_] := Module[
  {ret = True}, 
  Do[If[i > 1 && a[[i]] != a[[i - 1]] + 1, ret = False; Break[]], {i, 
  1, Length[a]}]; ret]

Although the function consQ does the job, I wonder if there is a better ( shorter, faster ) method of doing this, preferably using functional programming style.

EDIT: The function above maps lists with consecutive integers in decreasing sequence to False. I would like to change this to True.

like image 420
nilo de roock Avatar asked Oct 28 '11 15:10

nilo de roock


4 Answers

Szablics' solution is probably what I'd do, but here's an alternative:

consQ[a : {___Integer}] := Most[a] + 1 === Rest[a]
consQ[_] := False

Note that these approaches differ in how they handle the empty list.

like image 84
Brett Champion Avatar answered Oct 24 '22 01:10

Brett Champion


You could use

consQ[a_List ? (VectorQ[#, IntegerQ]&)] := Union@Differences[a] === {1}
consQ[_] = False

You may want to remove the test for integers if you know that every list you pass to it will only have integers.

EDIT: A little extra: if you use a very old version that doesn't have Differences, or wonder how to implement it,

differences[a_List] := Rest[a] - Most[a]

EDIT 2: The requested change:

consQ[a : {Integer___}] := MatchQ[Union@Differences[a], {1} | {-1} | {}]
consQ[_] = False

This works with both increasing and decreasing sequences, and gives True for a list of size 1 or 0 as well.

More generally, you can test if the list of numbers are equally spaced with something like equallySpacedQ[a_List] := Length@Union@Differences[a] == 1

like image 43
Szabolcs Avatar answered Oct 23 '22 23:10

Szabolcs


I think the following is also fast, and comparing reversed lists does not take longer:

a = Range[10^7];
f[a_] := Range[Sequence @@ ##, Sign[-#[[1]] + #[[2]]]] &@{a[[1]], a[[-1]]} == a;
Timing[f[a]]
b = Reverse@a;
Timing[f[b]]

Edit

A short test for the fastests solutions so far:

a = Range[2 10^7];
Timing@consQSzab@a
Timing@consQBret@a
Timing@consQBeli@a
(*
{6.5,True}
{0.703,True}
{0.203,True}
*)
like image 8
Dr. belisarius Avatar answered Oct 24 '22 00:10

Dr. belisarius


I like the solutions by the other two, but I'd be concerned about very long lists. Consider the data

d:dat[n_Integer?Positive]:= d = {1}~Join~Range[1, n]

which has its non-sequential point at the very beginning. Setting consQ1 for Brett's and consQ2 for Szabolcs, I get

AbsoluteTiming[ #[dat[ 10000 ] ]& /@ {consQ1, consQ2}
{ {0.000110, False}, {0.001091, False} }

Or, roughly a ten times difference between the two, which stays relatively consistent with multiple trials. This time can be cut in roughly half by short-circuiting the process using NestWhile:

Clear[consQ3]
consQ3[a : {__Integer}] := 
 Module[{l = Length[a], i = 1},
   NestWhile[# + 1 &, i, 
      (#2 <= l) && a[[#1]] + 1 == a[[#2]] &, 
   2] > l
 ]

which tests each pair and only continues if they return true. The timings

AbsoluteTiming[consQ3[dat[ 10000 ]]]
{0.000059, False}

with

{0.000076, False}

for consQ. So, Brett's answer is fairly close, but occasionally, it will take twice as long.

Edit: I moved the graphs of the timing data to a Community Wiki answer.

like image 8
rcollyer Avatar answered Oct 24 '22 01:10

rcollyer