Programming 1
Lecture 2: Notes

To review much of the material from today's lecture, please read these sections of Introducing Python:

Here are some additional notes.

Removing all zeroes

This program uses a while loop to remove all zeroes at the end of a decimal number:

n = int(input('Enter n: '))

if n != 0:
  while n % 10 == 0:
    n = n // 10

print(n)

Sum of multiples of 3 or 5 below 1000

Project Euler's problem 1 asks us to find the sum of all the multiples of 3 or 5 below 1000. Here is a solution in Python:

sum = 0
for n in range(1000):
  if n % 3 == 0 or n % 5 == 0:
    sum += n
print(sum)

Nested loops

In programming it is common to use a nested loop, i.e. a loop inside a loop. For example, here's a program that prints out a rectangle of asterisks:

x = int(input('Enter x: '))
y = int(input('Enter y: '))


for i in range(y):
  for j in range(x):
    print('*', end = '')  # print '*' and stay on the same line
  print('')   # move to the next line

The output looks like this:

Enter x: 7
Enter y: 3
*******
*******
*******

Note that the inner loop ("for j in range(x)") runs in its entirety on every iteration of the outer loop ("for i in range(y)"). So the total number of asterisks printed is x ⋅ y.

We can also change the bounds of the inner loop on each iteration of the outer loop. For example, here is a program to print a triangle of asterisks:

n = int(input('Enter size: '))
  
for i in range(1, n + 1):
  for j in range(i):
    print('*', end = '')
  print('')

The output looks like this:

Enter size: 5
*
**
***
****
*****

In each of these examples we've used a doubly nested loop, i.e. a loop inside a loop. Of course, loops may be triply or even aribitrarily nested.

Math functions

For our first peek into Python's enormous standard library, we will see how to use Python's built-in math functions. To get access to these, write this at the top of your program:

import math

These functions include

and many others. You can see a full list in the Python library documentation.

To use any of these functions, write "math." followed by the name of the function. For example:

import math

print(math.sqrt(2))

prints

1.4142135623730951

A number-guessing game

We now have the tools we need to write a simple game. In this game, the computer chooses a random number between 1 and 1000 and the user has to guess it:

I am thinking of a number from 1 to 1000.
Your guess? 800 
Too low!
Your guess? 900
Too high:
Your guess? 850
Too low:
Your guess? 860
Too high:
Your guess? 864
You got it!

Here is the program:

import random

print('I am thinking of a number from 1 to 1000.')
n = random.randint(1, 1000)
while True:
  g = int(input('Your guess? '))
  if g == n:
    break
  if g < n:
    print('Too low!')
  else:
    print('Too high!')
    
print('You got it!')

If we are the user playing this game, what is our best strategy to minimize the number of guesses we may need to make? And how many guesses might be required?

As we play the game, at every moment we know that the target number falls in the range A...B for some integers A and B. As you might imagine, our best strategy is as follows. At each step, we guess a number G that divides this interval in half, i.e. G = (A + B) // 2. If G is too high, then we now know that the target number is in the range A … (G – 1). If it is too low, we now know that it's in the range (G + 1) … B.

This guessing strategy is called a binary search, which is actually an important algorithm that we'll see again later in this course. With this strategy, at each step the size of the interval containing the target value drops by a factor of 2. So after K guesses its size has dropped by a factor of 2K. In particular, in this case the interval originally contains 1000 numbers. After the first guess, its size is at most 1000 / 2 = 500. After the second guess, its size is at most 1000 / 22 = 250. And so on. After 10 guesses, the interval has dropped by a factor of 210 = 1024, and must now contain only a single number. In other words, 10 guesses are always sufficient to determine the number that the computer has chosen.

In the general case, if the computer chooses a random number from 1 to N then we might need log2(N) guesses in the worst case.

Standard input

On all major operating systems, as a program runs it can read from its standard input. Usually standard input comes from the terminal, but it is also possible to redirect it to come from a file instead.

In Python, we will often want to read lines from standard input. Fortunately this is easy. The sys.stdin object is a sequence of lines, and so we can loop over it using for. For example, here is a program that reads numbers from standard input, one per line, and computes their sum:

import sys
  
sum = 0
for line in sys.stdin:
  n = int(line)    # convert string to integer
  sum += n
  
print('The sum is', sum)

When we run the program and enter its input from a terminal, we need some way to signal that the input is complete. On Linux or macOS, we can do this by typing Ctrl+D. On Windows, type Ctrl+Z followed by Enter.

When we run the program, we see this:

3
4
5
The sum is 12

Above, we typed Ctrl+D or Ctrl+Z after the number 5 (though that was not visible in the terminal output).