Problem Statement

You are given a positive integer and a “magic number” which is a digit between and . Suppose we have a function that takes in a number and output the sum of the squares of that number’s base ten digits. Let us define the sequence as follows:

Your task is described below:

  1. Show that this sequence eventually has a cycle for all .
  2. Write an algorithm to find the first such that the last digit of is our magic number , or return if this never happens.
  3. Suppose the statement of the first part was not true. How does this impact your algorithm’s performance.
  4. What is the runtime of the algorithm.

For reference we have included some python starter code for you to use.

    # should return the first i such that the last digit of a_i is our magic number m given a_0 = n.
    # Return -1 if this never happens 
    def magic(n, m):
        # Your code here

Test Cases

Test Case 1

Input: 70, 2
Output: -1

Test Case 2

Input: 27, 3
Output: 1

Test Case 3

Input: 63, 3 
Output: 0

Click Below to see the Solution

Click Me

Part 1: Prove It!

Let us prove the following theorem:

Theorem: If ,

Proof: First, if is a power of , then , which is by assumption less than . Otherwise is simply equal to the sum of the digits of squared. Since each digit, by virtue of being a digit, is less than or equal to , each term in the sum is less than or equal to . As a result, since there are digits in (think about why), we see that is less than or equal to

Corollary: If , .

Proof Beyond 300, the function is less than the function . Sicne both function are increasing and the former grows slower than the latter (by virtue of being a logarthmic vs linear function), we see that for . Applying the above theorem, we see that for .

Corollary: If , .

Proof: This is trivial for . Since is an increasing function, we see that it is less than . Applying the theorem above, we see that .

Theorem: eventually has a cycle for all .

Proof We note that since this sequence is recursively defined, if for differing indices and , if , then there is a repeating cycle goes from up to and repeats.

So we only need prove that there exists two distinct indices and such that for all . First, by the first corollary, if , there exists an such that . This is because if we iteratively apply , then the result will be less than the original value of (and be an integer). This means that eventually, this integer will become less than 300.

Now, given that there is some , we see that it must stay less than or equal to . Since there are only a finite number of positive integers less than 300, by the pigeonhole principle, eventually an index must repeat. This will define our cycle.

Part 2: Code it Up

Algorithm

We will keep applying on the value until we find an index where the last digit is equal to the magic number. We also keep track of previously seen numbers in a hash-set. We know that since there will eventually be a cycle, if there is a number that we have seen before, and we have not found the magic number yet, we will never find such a number.

Code for the above algorithm can be seen below:


def magic(n, m):
    count = 0
    prev_seen = set()
    while n not in prev_seen:
        prev_seen.add(n)
        if(n % 10 == m):
            return count
        n = sum_square_digits(n)
    return -1

# return the sum of the square of the digits of n
def sum_square_digits(n):
    summ = 0
    while(n != 0):
        summ += (n % 10)**2
        n = n // 10
    return summ

Part 3: What if…

If the statement of the first theorem were not true, we could not guaruntee that there does not exist an index where is not the last digit of . Unless we had further information about the behavior of such a function, either we would have to continue our algorithm until it terminates (potentially taking an unbounded amount of computational time), or we would terminate after a set limit (which may impact the correctness of our algorithm).

Part 4: Runtime Analysis

The computational complexity of such an operation is determined by the number of function calls to mutliplied by the time it takes to compute . This time is simply . Since returns a number that is on the order of , the number of function calls to before gets small, is the same as the iterated logarithm or . Therefore the total runtime is at most .

Once this is less than 300, there are at most 300 function calls before a cycle is found which is constant and does not affect the runtime.

Note: In practice, decays so rapidly that it is effectively constant for any reasonable computation, so this algorithm is essentially a logararithmic runtime.