this post was submitted on 01 May 2025
5 points (100.0% liked)

Learn Programming

1819 readers
1 users here now

Posting Etiquette

  1. Ask the main part of your question in the title. This should be concise but informative.

  2. Provide everything up front. Don't make people fish for more details in the comments. Provide background information and examples.

  3. Be present for follow up questions. Don't ask for help and run away. Stick around to answer questions and provide more details.

  4. Ask about the problem you're trying to solve. Don't focus too much on debugging your exact solution, as you may be going down the wrong path. Include as much information as you can about what you ultimately are trying to achieve. See more on this here: https://xyproblem.info/

Icon base by Delapouite under CC BY 3.0 with modifications to add a gradient

founded 2 years ago
MODERATORS
 

I'm going through the Programming With a Purpose course on Coursera, and trying to come up with my own implementation of some of the example programs before looking at the example. I just finished the example of a gambling situation. I was hoping to get some more eyes on my code and be told whether my version is going to behave differently from the example. Due to the nature of simulations, I can't just compare the output of the two, since it will vary.

This is the explanation of what the code is meant to represent:

Gambler (PROGRAM 1.3.8) is a simulation that can help answer these ques tions. It does a sequence of trials, using Math.random() to simulate the sequence of bets, continuing until the gambler is broke or the goal is reached, and keeping track of the number of wins and the number of bets.

Mine:

public class Gambler
{
    public static void main(String[] args)
    {
        int stake = Integer.parseInt(args[0]);
        int initialStake = stake;
        int goal = Integer.parseInt(args[1]);
        int desiredGain = goal - stake;
        int trials = Integer.parseInt(args[2]);
        int games = 0;
        int wins = 0;
        int bets = 0;

        while (games < trials)
        {
            bets++;
            if (Math.random() >= 0.5)
            {
                stake++;
                desiredGain = goal - stake;
                if (desiredGain <= 0)
                {
                    wins++;
                    games++;
                    stake = initialStake;
                }
            }
            else
            {
                stake--;
                if (stake < 1)
                {
                    games++;
                    stake = initialStake;
                }
            }
        }
        int averageBets = bets / trials;
        int percentWins = (100*wins / trials);
        System.out.println("Theoretical chance of winning: " + 100*initialStake/goal);
        System.out.println("Expected Bets: " + initialStake*(goal - initialStake));
        System.out.println(percentWins + "% wins");
        System.out.println("Average # bets: " + averageBets);
    }
}

Example:

public class Gambler2
{
    public static void main(String[] args)
    {
        int stake = Integer.parseInt(args[0]);
        int goal = Integer.parseInt(args[1]);
        int T = Integer.parseInt(args[2]);
        int bets = 0;
        int wins = 0;

        for (int t=0; t<T; t++)
        {
            int cash = stake;
            while (cash > 0 && cash < goal)
            {
                bets++;
                if (Math.random() < 0.5) cash++;
                else                     cash--;
            }
            if (cash == goal) wins++;
        }
    int averageBets = bets / T;
    int percentWins = (100*wins / T);
    System.out.println("Theoretical chance of winning: " + 100*stake/goal);
    System.out.println("Expected Bets: " + stake*(goal - stake));
    System.out.println(percentWins + "% wins");
    System.out.println("Average # bets: " + averageBets);
    }
}

The example is obviously much cleaner, but here is why I think mine should work the same:

  1. Each time the player's current stake exceeds the goal or reaches 0, the number of games played is incremented. When games == trials, the loop ends.
  2. When a game ends, stake is reset to its initial value for the next trial
  3. Bets is incremented each time the loop runs
  4. If the current stake meets or exceeds the goal, wins is incremented

I set up my conditions for interpreting the output of Math.random() opposite of the example. If that even makes a difference at all, it seems like it would take a lot more than 1000 trials before it became apparent.

I just want to make sure I'm not missing something.

you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 4 points 1 day ago (1 children)

TL;DR: You're getting different results for negative or otherwise invalid/unexpected input arguments.

A non-equivalence appears when passing a negative value as arg0 or arg1, which will always result in averageBets == 0 in the example, but >=0 with your code. The reason for this happening basically hides in your conditional incrementing of games, which allows for unintended "extra loops" if your conditions desiredGain <= 0 or stake < 1 are not met.

Also, the condition desiredGain = goal - stake; (desiredGain <= 0)(goal - stake <= 0)(goal <= stake) in your "win branch" is not equivalent to cash < goal, returning different results for all arg0 >= arg1.

The simpler example implementation handles these edge cases implicitly with cash > 0 and cash < goal, skipping the main work, but still running exactly all tries. It'd be fine, and actually preferable, IMHO, to add explicit input validation to your code to basically abort early, if the input values are out of bounds, but there's always a case to be made for keeping loops and conditions as simple, local, and immutable as possible, to avoid creating sneaky edge cases.

Unfortunately, it is very common for many examples to lack error checking and/or input validation altogether, just like here.

[–] Hammerheart 1 points 1 day ago

This is amazing, thank you! You've given me a lot to think about, not just with respect to this program, but what sort of things to consider when analyzing the behavior of any program. Describing the classes of input like you did was enlightening.