Intro to Algorithms, 2020-1
Lecture 2 – Notes

Decimal representation

By longstanding convention, basically every society on Earth writes numbers in base 10, otherwise known as decimal. When we write a number such as 1985, we actually mean a sum of powers of 10:

1985 = 1 · 103 + 9 · 102 + 8 · 10 + 5

We will now discuss simple algorithms that can split an integer (e.g. 1985) into a series of decimal digits (1, 9, 8, 5), and can combine a series of decimal digits into an integer.

Splitting into digits

To split into digits, we first notice that for any integer i,

We can see this mathematically by factoring the expansion of 1985 above:

1985 = 1 · 103 + 9 · 102 + 8 · 10 + 5 = 10 (1 · 102 + 9 · 10 + 8) + 5

From this factoring it is evident that 1985 mod 10 = 5 and 1985 div 10 = (1 · 102 + 9 · 10 + 8) = 198.

So we can write a simple loop that extracts digits from an integer in succession. In Python:

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

while n > 0:
    d = n % 10
    n = n // 10
    print('digit: ', d)

For example:

Enter n: 1985
digit:  5
digit:  8
digit:  9
digit:  1

Combining digits

To combine a series of digits into a number, we first notice that we can append a decimal digit d to an integer n using this formula:

n' = 10 · n + d

For example, if n = 198 and d = 5, then n' = 10 · 198 + 5 = 1985.

So we can write a simple loop that reads a series of digits, one per line, and combines them into an integer. The user will enter '-1' when there are no more digits. In Python:

n = 0
while True:
    d = int(input('Digit: '))
    if d < 0:
        break   # end of input
    n = 10 * n + d
print('n is', n)

The program behaves like this:

$ py joinDigits.py
Digit: 1
Digit: 9
Digit: 8
Digit: 5
Digit: -1
n is 1985
$

numbers in different bases

We use base 10 by convention, but of course the number 10 is arbitrary. We would now like to work with numbers written in non-decimal bases, i.e. bases other than 10. For example, consider base 5. In the base 5 system, every digit has a value from 0 to 4. Consider the number

20315

Here, the subscript 5 means that the number is written in base 5. (In this course, you may assume that any number written without a subscript is in base 10.)

The decimal value of the base-5 number above is

2 · 53 + 0 · 52 + 3 · 5 + 1 = 26610

Base 2 (binary) is especially common in computer programming. Here are the first natural numbers in base 2:

We also often use base 16 (hexadecimal), in which we have extra digits A = 10, B = 11, C = 12, D = 13, E = 14, F = 15. For example,

FF16 = 25510 because 15 · 161 + 15 · 160 = 240 + 15 = 255

Internally, computers usually do not store numbers in base 10. (Instead, they are actually stored as a sequence of bytes, though you don't need to worry about that too much for the moment). Python functions such as print() internally perform arithmetic operations to produce decimal digits from a number. Similarly, functions such as int() use arithmetic to join decimal digits into a number.

splitting into digits in different bases

Previously we saw algorithms that can split an integer into decimal digits, and join a series of decimal digits to form an integer.

Let's now generalize those algorithms to work with non-decimal bases. Actually the change is trivial: we simply change the constant 10 in our code!

For example, previously we saw this program to print a number's decimal digits:

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

while n > 0:
    d = n % 10
    n = n // 10
    print('digit:', d)

Let's modify it to print a number's binary digits. We only need to change the constant 10 to 2:

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

while n > 0:
    d = n % 2
    n = n // 2
    print('digit:', d)

Let's run the program:

Enter n: 43
digit:  1
digit:  1
digit:  0
digit:  1
digit:  0
digit:  1

The digits are generated in reverse order. The program's output means that

1010112 = 4310

This is because

1 · 25 + 0 · 24 + 1 · 23 + 0 · 22 + 1 · 2 + 1 = 32 + 8 + 2 + 1 = 43

We can modify the program to combine the digits into a string. Because the digits are generated in reverse order, we must prepend to the string at each step to end up with the digits in the correct order.

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

s = ''
while n > 0:
    d = n % 2
    n = n // 2
    s = str(d) + s    # prepend digit to string
print('in base 2:', s)

Let's run it:

Enter n: 43
in base 2: 101011

combining digits in different bases

Similarly, above we saw a program that reads a series of decimal digits and joins them into an integer:

n = 0
while True:
    d = int(input('Digit: '))
    if d < 0:
        break   # end of input
    n = 10 * n + d
print('n is', n)

We can easily modify this program to work another base. Once again, let's change 10 to 2:

n = 0
while True:
    d = int(input('Digit: '))
    if d < 0:
        break   # end of input
    n = 2 * n + d
print('n is', n)

Now we run the program:

$ py hello.py
Digit: 1
Digit: 0
Digit: 1
Digit: 0
Digit: 1
Digit: 1
Digit: -1
n is 43

The program has read the binary digits of 1010112 and combined them into the number 43.

converting between bases

To convert from base B to base C, you can

  1. Combine the digits in base B into an integer. (By the way, in this step students sometimes think that they are "converting to base 10", but that's not true. An integer such as a Python int is not stored internally in base 10. It's best to think of it as a mathematical integer that's not in any base.)

  2. Split the integer into digits in base C.

primality testing

As we know from mathematics, a prime number is an integer greater than 1 whose only factors are 1 and itself. For example, 2, 7, 47 and 101 are all prime.

We would now like to write a program that tests whether a given number is prime. To do this, we will use a simple algorithm called trial division, which means dividing by each possible factor in turn. (By the way, there also exist more efficient algorithms for primality testing, though they are more complex. You may encounter these in more advanced courses.)

Here is a naive implementation of trial division:

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

prime = True

i = 2
while i < n:  # loop from 2 .. (n - 1)
  if n % i == 0:
    prime = False
    break
  i += 1

if prime:
  print('n is prime')
else
  print('not prime')

This works fine, but is inefficient because it may need to test all integers from 2 up to (n – 1). When n is large, this can take a long time.

Actually for a given n we need test only the values up to √n, i.e. the square root of n. To see this, consider the following fact. If ab = n for integers a and b, then either a ≤ √n or b ≤ √n. Proof: Suppose that a > √n and b > √n. Then ab > √n ⋅ √n = n, a contradiction. So either a ≤ √n or b ≤ √n.

It follows that if we have tested all the values from 2 through √n and none of them divide n, then if ab = n we must have a = 1 or b = 1. And so n is prime.

So we can make our program far more efficient by replacing the statement

while i < n:

with

while i * i <= n:

Now the loop will stop as soon as i > √n.