Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
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 scrutinizing the Monastery: (4)
As of 2015-07-05 23:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (68 votes), past polls