slanted W3C logo

Day 12 — Loops

A Puzzle

Think of a positive whole number, and apply the following rules:

  1. if the number is even, divide it by 2
  2. if the number is odd, multiply it by 3 and add 1
  3. if you reach 1, stop; otherwise go to Step 1

Do you always reach 1? Prove your answer.

The Collatz-Syracuse-Ulam Problem

The puzzle is known as the Collatz-Syracuse-Ulam problem (and has many other names).

No one knows if the sequence always reaches 1, but it is thought that it does.

Computer calculations show that every starting number up to at least 19 * 258 ≈ 5.48 * 1018 eventually hits 1. [Ian Stewart, Professor Stewart's Hoard of Mathematical Treasures].

The Halting Problem

In computability theory, the halting problem can be stated as:

Given a program and an input to the program, decide (yes or no) whether or not the program will eventually halt when run with that input.

In 1936, Alan Turing proved that the problem is undecidable: no general algorithm exists that can solve the problem for all possible program-input pairs.

See:
How Dr. Seuss would prove the halting problem undecidable
CSE2001 Introduction to the Theory of Computation

The Collatz Problem

In pseudocode:

while p is not equal to 1
   if p is even
      p = p / 2
   else
      p = 3 * p + 1

In Java:

while (p != 1)
{
   if (???)
   {
      
   }
   else
   {
      
   }
   output.println(p);
}

Loops

In Java, the code inside the block of a loop runs if a condition (logical expression) is true.

In while and for loops, the condition is checked once (and only once) before the loop runs the first time, and every time at the end of the loop body.

A   while (condition)
    {
       //
B      // loop body
       //
    }
C   // after loop
  1. On Line A, the condition is checked; if it is true the loop body B is run, otherwise control flows to C.
  2. After the B is run, the condition is checked again; if it is true the loop body B is run, otherwise control flows to C.

Gambler's Ruin

A gambler with finite wealth playing a fair game (equal odds of winning or losing) will eventually go broke against an opponent with infinite wealth (casino). However, it is possible for a disciplined gambler to make money if he stops gambling after he has accumulated a preset amount of wealth.

What are the odds of reaching the preset amount of wealth?

How many games will it take (on average)?

Gambler's Ruin Simulation

It is easy to simulate the gambler's ruin scenario. You need to know:

  1. how much money the gambler starts with
  2. how much money the gambler wants to leave with

You also need to know how much it costs to play a game. To keep things simple, assume that it costs $1 to play a game.

Gambler's Ruin Simulation

import java.util.Scanner;
import java.io.PrintStream;
import java.util.Random;

public class GamblersRuin
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      output.print("Starts with $ ");
      int dollars = input.nextInt();
      
      output.print("Finish with $ ");
      int target = input.nextInt();
      
      Random game = new Random();
      while (dollars > 0 && dollars < target)
      {
         if (game.nextBoolean())
         {
            dollars++;
         }
         else
         {
            dollars--;
         }
      }
      
      if (dollars == 0)
      {
         output.println("Busted!");
      }
      else
      {
         output.println("Winner!");
      }
   }
}

Sentinel-Based Input

Write a loop that repeatedly reads an integer number from the user until the user enters -1.

In pseudocode:

set number to zero
while number is not -1
   read number from user

In Java:

final int SENTINEL = -1;
int number = 0;
while (???)
{
   
}

Compute the Sum

Write a loop that repeatedly reads an integer number from the user until the user enters -1; the loop should compute and output the sum of the numbers entered so far.

In Java:

final int SENTINEL = -1;
int number = 0;
int sum = 0;
while (???)
{
   
}

Compute the Sum of n Numbers

Instead of using a sentinel, suppose you ask the user how many numbers they want to input. Write a loop that computes the total sum of the numbers input by the user.

output.print("How many numbers do you want to enter?: ");
int n = input.nextInt();

int sum = 0;

while (???)
{

}

for Loops

The idea of using a loop controlled by a counter is so common that it has its own kind of loop, the for loop.

The for loop is controlled by 3 expressions, all of which are optional.

      for (initial-expression; logical-expression; update-expression)
      {
         statements
      }

The loop executes as follows:

  1. The initial-expression executes; this only ever happens once.
  2. The logical-expression is evaluated. If it is true then:
    1. The body statements are executed.
    2. The update-expression is executed.
  3. Step 2 is repeated.

Compute the Sum on n Numbers

output.print("How many numbers do you want to enter?: ");
int n = input.nextInt();

int sum = 0;

for (int count = 0; count < n; count++)
{
   int number = input.nextInt();
   sum += number;
}

When written this way, the variable count is local to body of the loop; you cannot use count after the loop body.

Traditionally, loop counter variables are named i, j, and k.

Tracing a Loop

Suppose we have the following loop that computes the sum 0 + 1 + 2:

      int sum = 0;
      for (int i = 0; i < 3; i++)
      {
         sum = sum + i;
      }

You can think of this loop as being equivalent to:

      int sum = 0;
      {
         int i = 0;          // this only happens once

         if (i < 3)
         {
            sum = sum + i;   // sum is now 0
         }
         i++;                // i is now 1

         if (i < 3)
         {
            sum = sum + i;   // sum is now 1
         }
         i++;                // i is now 2

         if (i < 3)
         {
            sum = sum + i;   // sum is now 3
         }
         i++;                // i is now 3
         
         // i < 3 is false so the loop ends
      }

Nested Loops

You can put one loop inside another loop. For example, supposed you wanted to print this:

****
****
****
****
1. for each row
   a. print 4 stars
   b. print a newline
      final int N = 4;
      for (int row = 0; row < N; row++)
      {
         for (int col = 0; col < N; col++)
         {
            output.print("*");
         }
         output.println();
      }

Nested Loops

Supposed you wanted to print this:

    *
   **
  ***
 ****
*****
1. for each row
   a. print some spaces
   b. print some stars
   c. print a new line
      final int N = 5;
      for (int row = 0; row < N; row++)
      {
         // how many spaces?
         int spaces = ???;
         for (???; ???; ???)
         {
            output.print(" ");
         }
         // how many stars?
         int stars = ???;
         for (???; ???; ???)
         {
            output.print("*");
         }
         output.println();
      }

Nested Loops

Supposed you wanted to print this:

    *
   ***
  *****
 *******
*********
1. for each row
   a. print some spaces
   b. print some stars
   c. print a new line
      final int N = 5;
      for (int row = 0; row < N; row++)
      {
         // how many spaces?
         int spaces = ???;
         for (???; ???; ???)
         {
            output.print(" ");
         }
         // how many stars?
         int stars = ???;
         for (???; ???; ???)
         {
            output.print("*");
         }
         output.println();
      }

Revisiting Gambler's Ruin

Modify the GamblersRuin program so that it simulates the gambler's ruin scenario 10,000 times.

Count the number of times the gambler wins (reaches the target amount); this gives the odds of reaching the target amount.

Count the total number of games played (the number of times game.nextBoolean() for all of the winning scenarios; this gives the average number of games the gambler needs to play to reach the target amount.