## Session 7

### Exercise 1: Code Trace

Consider this program:

```    number = 24
count  = 0

while number > 0:
if number % 3 == 0:
number = number // 3
elif number % 5 == 0:
number = number // 5
else:
number = number - 1
count = count + 1

print(number)
print(count)
```

What is the program's output?

What is the program's output if we change the 24 to 25?

What is the program's output if we take out the else clause?

What is the program's output if we change the elif clause to an if?

Is there any starting value for number that results in the program printing something other than 0 on its first print statement?

### Statement Interlude: while

... a while statement works a lot like an if statement, with one big change:

```    while {condition}:    |    [1] if {condition}:
{statement 1}      |            {statement 1}
...                |            ...
{statement k}      |            {statement k}
|            {go back to [1]}
```

This is why we have to do something in the body of the loop with the potential to change the value in the condition. Otherwise, it just keeps going back!

### Exercise 2: Hey, Nineteen

How many three-digit numbers are divisible by 19?

Write a program to answer the question. Have your program print them all, too.

```    multiples_of_19 = 0
for n in range(100,1000):
if n % 19 == 0:
print(n)
multiples_of_19 += 1

print("Total", multiples_of_19)
```

Why do we use 1000 in the range expression?

### Statement Interlude: for

... any for statement can be written as an equivalent while statement:

```    for obj in collection:    |    obj = {first item in collection}
{statement 1}          |    while obj exists
...                    |        {statement 1}
{statement k}          |        ...
|        {statement k}
|        obj = {next item in collection}
```

Your textbook gives a simpler translation for "counting" loops.

If possible, let a for statement do the work for you.

### Exercise 3: Prime Check

Is a number prime? The answer is 'no' if any number smaller than itself divides into it evenly, and 'yes' otherwise.

Write a program that prints 'no' if the given number has a divisor.

Hint: look divisors in a fixed range, and break as soon as you find one.

We could use this program, which uses a while loop. But this is a good case for a program that uses a for loop.

```    for i in range(2,n):
if n % i == 0:
print("Divisible by ", i)
break
```

Can we stop looking sooner than n-1?

How can we change the program so that it also prints 'yes' for the prime numbers?

This program uses a flag variable to record that we have seen a divisor:

```    saw_a_divisor = False
divisor = 2
while divisor <= n // 2:
if n % divisor == 0:
saw_a_divisor = True
print("no", divisor)
break
divisor += 1

if not saw_a_divisor:
print("prime")
```

This is a handy technique to keep in mind...

(As you read in Chapter 2, the for statement can include an else: clause. This program uses that feature to accomplish the same thing. Notice how important the indentation is here!)

### Exercise 4: Factors

Note: This example is a bit more complex. Only dive in after you feel pretty comfortable with the first three exercises. If you don't, save this section for later.

In high school math, teachers sometimes asked us to find all of the factors of a number. For example, the factors of 36 are 2*2*3*3, and the factors of 360 are 2*2*2*3*3*5. Some numbers have larger factors; the factors of 194 are 2 and 97.

Once we understand the process and know how to program, we don't have to solve these problems by hand anymore!

How can we turn our previous program into a program that prints all the prime factors of a given number, one per line?

```    for i in range(2, n//2):
if n % i == 0:
print(i)
n = n // i
```

Something goes wrong... What? Run it for 6, 16.

With n // 2 as the upper bound, the for loop does not consider that value as a possible factor. The stopping condition is < n // 2.

More importantly, A for loop looks at each value in its collection exactly once. But a number may have the same factor multiple times.

Both of these tell us: We need a while loop.

Write a program that prints all the prime factors of a given number, one per line. Use a while loop.

```    i = 2
upper_bound = n // 2
while i <= upper_bound:
if n % i == 0:
print(i)
n = n // i
else:
i += 1
```

Why do we need the upper_bound variable? Why didn't we need it in the earlier example?

Can we stop looking sooner than n // 2?

This program continues looping long after it has found the last factor. Run it for 7776, then with a tracer print. Can we write a while loop that stops when as soon as it is done finding factors?

We don't have to control our loop with the possible factor; we can use the falling value of n! One simple change gives us this this:

```    i = 2
while n > 1:
# print("Entering", i)     # useful for seeing the speedup
if n % i == 0:
print(i)
n = n // i
else:
i += 1
```

Run it with the tracer print on: 6 16 36 49 7776.

There are almost always many ways to solve a problem. Different algorithms lead to different programs, and different behavior. With practice and experience, you will begin to see more alternatives. Try them out. Eventually, you will become a more effective designer of algorithms and writer of programs.

### Wrap Up

• Code -- today's zip file

• Reading -- Re-read Section 2.2, pages 103-137. Play with the code in a Python shell as you do. Write down a list of items that puzzle you, or about which you'd like to learn more.

• Homework -- Homework 3 is available and is due next session.

Eugene Wallingford ..... wallingf@cs.uni.edu ..... September 16, 2014