6
$\begingroup$

I have noticed that there are a few examples that go into some detail regarding While loop alternatives. (See here for example: Alternatives to While Loops?) However, I wanted to provide a simpler example for clarity, and to help affirm something which I am currently unclear about.

Can someone give a simple, faster alternative to the following code containing a While loop? (It counts the number of iterations taken before a pseudorandom uniform number on [0,1] to be generated.)

 Module[{cSwitch, cCount, cRand}, cSwitch = 0; cCount = 0; cRand = 0.9999;
While[cSwitch == 0, aRand = RandomReal[]; cCount += 1; 
If[aRand > cRand, cSwitch = 1]]; cCount] // Timing

A corollary question is, when should one use a While loop for simplicity, and when is it really beneficial to use an alternative? I am highlighting really just to emphasise that there is a tradeoff between readability of code and speed.

Best,

Ben

$\endgroup$
  • 1
    $\begingroup$ Just for fun: Block[{$IterationLimit = Infinity}, If[RandomReal[] > 0.9999, #, #0[# + 1]] &[0]]. Another: NestWhile[# + 1 &, 0, RandomReal[] < 0.9999 &]. $\endgroup$ – Szabolcs Nov 14 '14 at 0:40
11
$\begingroup$

You are looking for a pattern suitable for loops which must terminate on a condition (rather than run a fixed number of times). While is a good solution for this, but the key is really Break[], which can be used in While, Do, For, etc. I would write this code as

count = 1;
While[RandomReal[] <= 0.9999, count++];
count

as here you can move the condition to the beginning of While. If you can't, then something like

While[True,
  ...
  If[condition, Break[]];
  ...
]

is an alternative.

(You can also use For, as in For[count = 1, RandomReal[] <= 0.9999, count++]; count, but I'm not a fan of for loops in Mathematica. I usually prefer While.)

Many problems that need to break on condition can be solved with NestWhile. Your example would be

NestWhile[# + 1 &, 1, RandomReal[] <= 0.9999 &]

Check the documentation to see all the possible syntaxes for NestWhile.

It's also possible to use recursive iteration, though it's more likely to be slower here, and we need to release the restriction on the iteration limit using $IterationLimit = Infinity:

f[n_] := If[RandomReal[] > 0.9999, n, f[n + 1]]
f[1]

A very concise, but arguably not too readable version of the same is If[RandomReal[] > 0.9999, #, #0[# + 1]] & [1]. Here #0 refers to the anonymous function itself.

If performance is key, then the While version has the advantage that it can Compiled. Not every function is compilable. A procedural style, such as the version using While, is the most suitable to be compiled.

$\endgroup$
  • $\begingroup$ a very instructional answer. +1 $\endgroup$ – bobthechemist Nov 14 '14 at 1:54
  • $\begingroup$ Your nasty unreadable last piece of code can be greatly improved as the If isn't really needed: Block[{$IterationLimit = Infinity}, (# + 1 // (#0^Boole[RandomReal[] < 0.9999])) &@1 // #[[1]] &] $\endgroup$ – Dr. belisarius Nov 14 '14 at 2:04
  • $\begingroup$ Hi, thanks for your reply - that is useful to see. I have done some timing on the various different approaches to the While loop, and all seem to be very similar in terms of their timing. Can I ask if anyone could provide examples of when there is a significant difference in timing between a While loop vs a functional construct? Best, Ben $\endgroup$ – ben18785 Nov 14 '14 at 12:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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