def is_non_bouncy(n: int): digits = list(map(int, list(str(n)))) decreasing = True increasing = True for i in range(len(digits) - 1): increasing = increasing and digits[i] <= digits[i + 1] decreasing = decreasing and digits[i] >= digits[i + 1] return increasing or decreasing def non_bouncy_below_naiv(digits: int): threshold = 10 ** digits non_bouncy = sum([1 for i in range(1, threshold) if is_non_bouncy(i)]) return non_bouncy def non_bouncy_below(digits: int): # increasing s = [1 for _ in range(1, 10)] result = sum(s) for _ in range(digits - 1): s = [sum(s[i:]) for i in range(9)] result += sum(s) - 9 # The -9 is to avoid double counting 11, 22, 33 for increasing and # decreasing. # decreasing decreasing_count = 0 s = [i + 1 for i in range(1, 10)] for _ in range(digits - 1): # print(s) decreasing_count += sum(s) s = [sum(s[:i]) + 1 for i in range(1, 10)] result += decreasing_count return result def euler_113(): assert non_bouncy_below_naiv(6) == 12951 assert non_bouncy_below(6) == 12951 assert non_bouncy_below(10) == 277032 assert is_non_bouncy(134468) assert is_non_bouncy(66420) assert (not is_non_bouncy(155349)) return non_bouncy_below(100) if __name__ == "__main__": solution = euler_113() print("e113.py: " + str(solution)) assert(solution == 51161058134250)