Project Euler problem #112 Problem statement:


  Working from left-to-right if no digit is exceeded by the digit to its
  left it is called an increasing number; for example, 134468.
  
  Similarly if no digit is exceeded by the digit to its right it is
  called a decreasing number; for example, 66420.
  
  We shall call a positive integer that is neither increasing nor
  decreasing a "bouncy" number; for example, 155349.
  
  Clearly there cannot be any bouncy numbers below one-hundred, but just
  over half of the numbers below one-thousand (525) are bouncy. In fact,
  the least number for which the proportion of bouncy numbers first
  reaches 50% is 538.
  
  Surprisingly, bouncy numbers become more and more common and by the
  time we reach 21780 the proportion of bouncy numbers is equal to 90%.
  
  Find the least number for which the proportion of bouncy numbers is
  exactly 99%.


My code:

import time
start = time.time()


def bouncy(N):
    '''Function determines whether arbitrary number N is a bouncy number.'''
    number = list(str(N))
    n1 = list(str(N))
    n2 = list(str(N))
    n1.sort()
    n2.sort(reverse=True)
    if (number != n1 and number != n2):
        return True
    else:
        return False


def find():
    start = 1
    total = 0
    bou = 0 
    while True:
        total += 1
        if bouncy(start):
            bou += 1
        if bou/total &gt;= 0.99:
            break
        start += 1
    return start
print(find())
print(time.time() - start)




What can be improved?
Code structure

The most common and widely accepted structure found in Python scripts from a high-level point of view looks something like:

# imports
import
...

# functions and classes, aka "library code"
class Foo:
    ...

def bar(batz):
    ...

if __name__ == "__main__":
   # part that is supposed to be run as script
   ...



Your code on the other hand does not follow this structure since there is some script code at the top (start = time.time()), then come your functions, followed by another block of script code. I highly recommend to follow that structure.

Further reading: Explanation of if __name__ == "__main__": from the official Python documentation.

The code itself

bouncy

The most striking aspect of bouncy from a stylistic point is the capital N as parameter name that goes against the official Style Guide for Python Code (often just called PEP8). Usually parameter and variable names are written in snake_case, e.g. small letters, possibly separated by underscores. 

With that said, let's look at the actual algorithm: 


number = list(str(N))
n1 = list(str(N))
n2 = list(str(N))



Lots of repeated code here. An easy way out would be to do the conversion once, and copy the results afterwards. This can be done using slicing like n1 = number[:], the list constructor n1 = list(number) or the copy module. Also, sorted would create a copy indirectly, so no need to do this manually (see the next point).

The next thing you do in your code is sorting those lists. Once ascending, then descending. Since the second one should by definition be the mirror of the previous one, you can use slicing again to avoid that second sort.

sorted_number = sorted(number)  # &lt;- also does a copy


Oh, and did you know that sorted does work on strings as well and simply returns a new list? (sorted_number = sorted(N))

You could also use the result of the condition directly as return value of your function.

With some other minor changes you might end up with something like

def is_bouncy(number):
    '''Function determines whether arbitrary number N is a bouncy number.'''
    digits = list(str(number))
    sorted_digits = sorted(digits)
    return digits != sorted_digits and digits != sorted_digits[::-1]


find

find is almost as straightforward as it gets. I would advise to change the variable name bou to bouncy or n_bouncy now that the checking function is called is_bouncy. IMHO current would also likely be a better name for start. Also find_bouncy_boundary, maybe even with an optional "expected bounciness percentage" might be worth a thought.

From an optimization point of view, you could also start from 1000 if you're only looking for that 99%, since the task description tells you that there are exactly 525 bouncy numbers below 1000 ;-) But there is likely not much to gain from that. 

The rest

Now all there is left to do is to take the spread out script code, wrap it up in if __name__ == "__main__":, and collect it at the bottom of the script:

if __name__ == "__main__":
    start = time.time()
    print(find())
    print(time.time() - start)


If the script part becomes more complex, this part is often put into a separate main() method.