Concepts / Working with Loops

Working with Loops

random.random() generates a pseudorandom float in the range [0.0, 1.0), inclusive of 0.0 but exclusive of 1.0

  • Programming

A New Value on Every Pass

A loop can call random.random() repeatedly so that each iteration receives a new pseudorandom float. The loop controls how many calls occur; random.random() supplies the value for each call. The values may look unpredictable, but they come from a deterministic sequence controlled by the random module's internal state.

python

This loop makes three calls to random.random(), one during each iteration. It therefore prints three floating-point values. The exact values are not specified in advance because they depend on the random module's current internal state.

Tracing the Loop State

What do you think happens?

The loop calls random.random() three times. What should happen across the iterations?

  • The same float is printed three times
  • Three new floats are printed, one per iteration
  • Only the final iteration produces a float
Reveal answer

Answer: Three new floats are printed, one per iteration

Each call to random.random() returns the next value in the sequence. The random module's internal state changes with each call, so the next iteration receives a new pseudorandom value.

calls random.random()internal state advancescalls random.random()internal state advancescalls random.random()Iteration 1first callValue 1float in [0.0, 1.0)Iteration 2second callValue 2float in [0.0, 1.0)Iteration 3third callValue 3float in [0.0, 1.0)
What changes as the loop reaches each iteration and calls random.random()?

The important state change is not a variable shown in the loop body. The random module maintains internal state. Each call advances that state, and the changed state determines the next value. The function does not remember previous calls itself; the module's internal state controls the sequence.

The Half-Open Output Range

random.random() generates a pseudorandom float in the range [0.0, 1.0). The square bracket means that 0.0 is included. The parenthesis means that 1.0 is excluded.

through the rangeapproaches but does not include0.0includedValues betweenpossible floats1.0excluded
Which boundary values belong to the output range of random.random()?

The upper bound is exclusive: random.random() will not return exactly 1.0. This half-open range is useful when scaling the result. Multiplying by 100 produces a float in [0.0, 100.0). Multiplying by 6 and converting to an integer produces one of 0, 1, 2, 3, 4, or 5.

NotationMeaningFor random.random()
[Include the boundary0.0 can occur
)Exclude the boundary1.0 cannot occur

Reading the interval [0.0, 1.0)

A Three-Iteration Trace

Following Three Calls

Trace a loop that calls random.random() once per iteration for three iterations.

Iteration 1: The first call returns the next pseudorandom float in the current sequence. That value is printed.

Iteration 2: The loop repeats. A second call returns a new float, because the random module's internal state has advanced after the first call.

Iteration 3: The loop repeats once more. A third call returns another float from the sequence, and that value is printed.

The loop prints three floats. Every printed value is in [0.0, 1.0), and the exact sequence depends on the random module's internal state.

A useful trace records two things for every iteration: the iteration number and the value returned by that iteration's call. Do not expect a fixed list of literal values unless the seed and the number of earlier random calls are controlled.

Finding the Divergence Point

first call differssequence continues from current stateExpected 0.1Actual 0.7Expected later valuesActual later values
Which iteration first differs when an expected sequence is compared with the generated sequence?

Suppose you expected the five-value sequence [0.1, 0.2, 0.3, 0.4, 0.5], but the loop produced [0.7, 0.8, 0.9, 0.6, 0.5]. The first divergence is at the first call: the generated value is 0.7 instead of 0.1. The divergence occurs at random.random(), where the pseudorandom value is generated, not elsewhere in the loop.

Reproducible Loop Runs

Use random.seed() before the loop when testing or debugging requires the same sequence to be reproduced. Setting the same seed before the same sequence of calls produces identical sequences. Without resetting the seed, repeated runs can produce different sequences.

python

The important behavior in this example is that the seed is set before the loop begins. If the same seed is set again before the same three calls, the sequence is reproducible. The code still generates pseudorandom floats, but the starting internal state is controlled.

Common Loop Mistakes

  • Expecting random.random() to return the same fixed sequence on every run without setting a seed.

    Different runs can begin with different internal states, so they can produce different pseudorandom sequences.

    Fix: Call random.seed() before the loop when you need reproducible output.

  • Treating 1.0 as a possible result.

    The interval is [0.0, 1.0): 0.0 is included, but 1.0 is excluded.

    Fix: Read the square bracket and parenthesis separately before reasoning about possible values.

  • Looking for the divergence somewhere after the random call.

    The divergence begins at the random.random() call that generated the unexpected value.

    Fix: Compare expected and actual values starting with the first call, then check the seed and the number of earlier calls.

  • Assuming a loop reuses one random value automatically.

    Each iteration calls random.random() again, and each call returns the next value in the sequence.

    Fix: Count the calls: one call per iteration produces one new value per iteration.

Check Your Prediction

EASY

A loop calls random.random() five times. Before running it, predict how many floats will be printed, whether 0.0 is allowed, whether 1.0 is allowed, and what must be controlled if you want to reproduce the same sequence in a second run.

Hints
  • Count one random.random() call for each loop iteration.
  • Use the notation [0.0, 1.0) to determine the boundary behavior.
  • Reproducibility depends on setting the seed before the calls.

Practice Answer

Determine the behavior of a five-iteration loop that calls random.random() once per iteration.

Number of outputs: There is one call during each of five iterations, so the loop prints five floats.

Range: Each float is in [0.0, 1.0). Therefore 0.0 is included and 1.0 is excluded.

Reproduction: Set the same seed before the same sequence of calls if the second run must produce the same sequence.

Five pseudorandom floats are printed. Each is at least 0.0 and less than 1.0, and the sequence can be reproduced by controlling the seed before the loop.

Key Takeaways

  1. Each call to random.random() returns a pseudorandom float in [0.0, 1.0).
  2. The range includes 0.0 but excludes 1.0.
  3. A loop calling random.random() once per iteration produces one new value per iteration.
  4. The random module's internal state advances with each call and determines the next value.
  5. Use random.seed() before the loop to reproduce a sequence for testing or debugging.

Key Takeaways

  • random.random() generates pseudorandom floats in the half-open interval [0.0, 1.0).
  • A repeated call advances the random module's internal state and produces the next value in its sequence.
  • A loop makes the number of calls easy to count: one call per iteration means one generated float per iteration.
  • When actual output differs from an expected sequence, the first divergence occurs at a random.random() call.
  • Setting the same seed before the same calls makes the sequence reproducible.