Beefy Boxes and Bandwidth Generously Provided by pair Networks Bob
Keep It Simple, Stupid
 
PerlMonks  

Re: Monte Carlo - Coin Toss

by johngg (Abbot)
on Mar 11, 2011 at 15:05 UTC ( #892669=note: print w/ replies, xml ) Need Help??


in reply to Monte Carlo - Coin Toss

That is an interesting problem and ++ for your effective solution.

I hope you don't mind me modifying your script to make it a little more Perlish. It is more idiomatic (and often simpler) to use Perl-style rather than C-style loops. It is also, I think, preferable to declare variables inside the scope where they will be used rather than at the beginning of the script which, in effect, makes them global. I have renamed $toss to $tailsCt as I think that better describes its purpose. I have employed constants for number of runs and tosses just to illustrate their use. I also have added plenty of comments to clarify what I have done.

# Employ strictures and warnings. # use strict; use warnings; # Declare things we don't change as constants (by convention # use upper-case for these). # use constant { NUM_TOSSES => 20, RUNS => 10_000_000, }; # Initialise all possible elements of @collect to avoid # "uninitialised" warnings at the printing stage if, for # example, no "twenties" were thrown. # my @collect = map 0, 0 .. NUM_TOSSES; # Use Perl-style for or foreach loops rather than C-style # ones. The for and foreach are synonymous in Perl and can # be freely interchanged. The loop variable is, by default, # available in $_ or you can assign it to a lexically # scoped variable as I do further down when printing the # results. We don't need it in these nested loops. # for ( 1 .. RUNS ) { # Declare lexically scoped $tailsCt so a new variable is # brought into existence each time round the outer loop # and will go out of scope on loop exit, thus no need to # zeroise every time. # my $tailsCt; # Toss the coin NUM_TOSSES times. # for ( 1 .. NUM_TOSSES ) { # Give rand() an argument n and it will return a value # such that 0 <= value < n. Use int to truncate so # that $tailsCt is incremented by 0 or 1 (heads or # tails respectively). # $tailsCt += int rand 2; } $collect[ $tailsCt ] ++; } # Rather than trying to line things up with tabs it might be # easier to use printf and employ the same field widths in the # heading and data rows. The RUNS constant is, in effect, a # piece of code rather than a variable so we have to enclose # it in a ${ \ ... } or a @{ [ ... ] } construct to interpolate # it into a double-quoted string. # printf qq{%5s%25s%9s\n}, q{Tails}, qq{Count out of ${ \ RUNS }}, q{%ge}; # q{Tails}, qq{Count out of @{ [ RUNS ] }}, q{%ge}; also works # Loop over the possible results from all heads to all tails # using lexically scoped $tailsCt to access elements in the # @collect array. This $tailsCt is a different variable to the # $tailsCt in the for ( 1 .. RUNS ) { ... } loop above and is # only in existence within the scope of this loop. # foreach my $tailsCt ( 0 .. NUM_TOSSES ) { # Rather than using a temporary variable and sprintf you # can just use printf directly and do the calculation in # the argument list. # printf qq{%5d%25d%9.2f\n}, $tailsCt, $collect[ $tailsCt ], $collect[ $tailsCt ] / RUNS * 100; }

I hope these observations are useful.

Cheers,

JohnGG


Comment on Re: Monte Carlo - Coin Toss
Select or Download Code
Re^2: Monte Carlo - Coin Toss
by jwkrahn (Monsignor) on Mar 12, 2011 at 01:22 UTC
    It is more idiomatic (and often simpler) to use Perl-style rather than C-style loops.

    Yes, but it won't work if the numbers are too large for integers and have to use floating point.    Then only C style loops will work.

    my @collect = map 0, 0 .. NUM_TOSSES;

    Why are you initializing @collect with NUM_TOSSES + 1 elements?

    The idiomatic way to initialize @collect with NUM_TOSSES elements is usually:

    my @collect = ( 0 ) x NUM_TOSSES;
    for ($i = 0; $i < $numTosses+1; $i++) { foreach my $tailsCt ( 0 .. NUM_TOSSES )

    You have an off-by-one error, it should be:

    foreach my $tailsCt ( 0 .. NUM_TOSSES - 1 )

    Update: oops, I misread the original code.

      Yes, but it won't work if the numbers are too large for integers and have to use floating point.

      If 2 billion loop iterations are not enough for you, you can always nest loops to multiple the range.

      You have an off-by-one error, it should be: ...

      No, 0 .. NUM_TOSSES is correct here, because adding up NUM_TOSSES (with a single result being 0 or 1) can potentially produce values ranging from zero to NUM_TOSSES.

      Tossing a coin once can produce 0 or 1 tails;
      tossing a coin twice can produce 0, 1 or 2 tails;
      etc.

      Why are you initializing @collect with NUM_TOSSES + 1 elements?

      The idiomatic way to initialize @collect with NUM_TOSSES elements is usually:

      my @collect = ( 0 ) x NUM_TOSSES;

      As Eliya points out, 20 tosses could give 21 outcomes as regards the number of "tails" tossed, i.e. 0, 1, ... 19 or 20 so your way should be

      my @collect = ( 0 ) x ( NUM_TOSSES + 1 );

      and I would agree that your way is perhaps more idiomatic. However, I felt that

      my @collect = map 0, 0 .. NUM_TOSSES;

      better illustrated the relation between the numbers of tosses and outcomes. What do others think?

      Cheers,

      JohnGG

Re^2: Monte Carlo - Coin Toss
by James_H (Acolyte) on Mar 12, 2011 at 13:54 UTC

    Many thanks JohnGG!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://892669]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (7)
As of 2014-04-20 06:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (485 votes), past polls