7

I'm having some trouble in the following exercise:

Find which elements of the list are prime numbers or divisible by 3.

list = Table[i^2 - 6 i - 1, {i, 0, 15}]

my attempt: Cases[list, Divisible[_,3]]. Mathematica didn't like this and I don't know how to write it correctly :/

For prime numbers, I used Cases[list, _?PrimeQ] and it gave me the right answer, but I can't imagine a way to use incorporate the "or" thing, except by using a If. Is there a more imperative way of doing this?

bbgodfrey
  • 61,439
  • 17
  • 89
  • 156
hrmello
  • 117
  • 5

4 Answers4

16

Culling elements from lists may done several ways, such as with Cases, Select, and Pick. Cases and Select seem quite similar when culling elements from level 1. The documentation for Select shows that the following are equivalent:

Select[list, f]
Cases[list, x_ /; f[x]]

This, too, is equivalent:

Cases[list, _?f]

The functions Condition (/;) and PatternTest (?) are ways to restrict a pattern to match only expressions that both match the pattern and satisfy a (boolean) condition.

Let's turn to the exercise at hand: To use Cases to do the following.

Find which elements of the list are prime numbers or divisible by 3.

I like the expressiveness of Condition, if you read /; as "such that" or "that", so I'll try that. We can map the elements of the problem to elements of code if we mangle the order slightly:

(* Find            *)   Cases[
(* in the list     *)    list,
(* the elements    *)    x_ 
(* that are        *)     /;
(* either          *)     Or[
(*  prime or       *)      PrimeQ[x],
(*  divisible by 3 *)      Divisible[x, 3]
                         ]]

(*  {-6, -9, -9, -6, 6, 15, 39, 54, 71, 90, 111}  *)

Or more succinctly,

Cases[list, x_ /; PrimeQ[x] || Divisible[x, 3]]
Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • 2
    Addendum: for a sufficiently long list, exploiting the short-circuit property of Or[] can be helpful: test for divisibility by $3$ first, since it's the easier test, and invoke the primality test only if that fails. So, x_ /; Divisible[x, 3] || PrimeQ[x] – J. M.'s missing motivation Aug 30 '15 at 17:19
  • @Guesswhoitis. I'm not completely familiar with internals, but tests show PrimeQ is faster than Divisible[#, 3]& for small integers. (PrimeQ /@ Range@n vs. Divisible[#, 3] & /@ Range@n -- 30% faster when n < 10^6; 13% for n = 10^7, 10^8, though 10^8 seemed to exercise memory management.) – Michael E2 Aug 30 '15 at 17:54
  • Huh, that's very odd (yet another case of slow new functions?). What if the less idiomatic Mod[#, 3] == 0 & was used instead? – J. M.'s missing motivation Aug 30 '15 at 18:04
  • @Guesswhoitis. Divisible is 1/3 faster than Mod. – Michael E2 Aug 30 '15 at 18:18
  • @Guesswhoitis. A partial explanation is that PrimeQ is really fast on even integers. On odd integers PrimeQ is faster (average 1 <= x <= n) up to n = 10^7 and then Divisible starts to edge it out. – Michael E2 Aug 31 '15 at 01:52
  • Very interesting. Intuitively, I'd thought that Miller-Rabin is certainly more elaborate than computing a remainder, but I guess PrimeQ[] might actually be doing sonething else behind the scenes before Miller-Rabin kicks in, which might account for your observations. – J. M.'s missing motivation Aug 31 '15 at 02:46
4

Many possible approaches. One is

Union[Cases[list, _?PrimeQ], Cases[list, i_ /; Mod[i, 3] == 0]]
(* {-9, -6, 6, 15, 39, 54, 71, 90, 111} *)

Addendum

If, instead, you want the elements in order and with their corresponding i values

Flatten@Union[Position[list, _?PrimeQ], Position[list, i_ /; Mod[i, 3] == 0]]
list[[%]]
(* {2, 3, 5, 6, 8, 9, 11, 12, 13, 14, 15} 
   {-6, -9, -9, -6, 6, 15, 39, 54, 71, 90, 111} *)
bbgodfrey
  • 61,439
  • 17
  • 89
  • 156
2

Another way with the hint from @Guess who i is:

Select[list, Divisible[#, 3] &]~Union~Select[list, PrimeQ]
{-9, -6, 6, 15, 39, 54, 71, 90, 111}
  • I had Select[list, Divisible[#, 3] || PrimeQ[#] &] in mind, tho. – J. M.'s missing motivation Aug 30 '15 at 17:05
  • @Guess who it is My first attempt, but today is sunday and it would not correctly work. Farther, I'm sure, I have typed the "t" in your name. I believe, it is tipped over. Sorry :-) –  Aug 30 '15 at 17:29
0

Most near your attempt:

Cases[list, _?(Divisible[#, 3] &)]

no x_ :p

what you write is a pattern, use _? make boolean into a pattern.

put together:

Cases[list, _?((Divisible[#, 3] || PrimeQ[#]) &)]
AsukaMinato
  • 9,758
  • 1
  • 14
  • 40