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%.

#### Your Task

Complete the bouncyRatio function.

The input will be the target ratio.

The output should be the smallest number such that the proportion of bouncy numbers reaches the target ratio.

You should throw an Error for a ratio less than 0% or greater than 99%.

**Source**

  - https://projecteuler.net/problem=112
  
**Updates**

  - 26/10/2015: Added a higher precision test case.

Source: codewars | Difficulty: EASY
Tags: Mathematics

Ad-hoc

Starter code: def bouncy_ratio(percent):
	

Solutions:
from operator import lt, gt
memo = {}
is_bouncy = lambda s: memo[s] if s in memo else memo.setdefault(s, any(map(lt, s[:-1], s[1:])) and any(map(gt, s[:-1], s[1:])))

def bouncy_ratio(percent):
	if not 0 < percent < 1:
		raise Exception('Wrong percentage: {}'.format(percent))
	(x, y) = (100, 0)
	while y < x * percent:
		(x, y) = (x + 1, y + is_bouncy(str(x + 1)))
	return x


def bouncy_ratio(percent):
	is_bouncy = lambda n: any((a < b for (a, b) in zip(n, n[1:]))) and any((a > b for (a, b) in zip(n, n[1:])))
	(n, bouncy) = (99, 0)
	while bouncy < percent * n:
		n += 1
		bouncy += is_bouncy(str(n))
	return n


def bouncy_ratio(percent):
	if percent <= 0 or percent >= 99:
		raise
	(bouncy, i) = (0, 99)
	while float(bouncy) / i < percent:
		i += 1
		bouncy += sum((any((f(a, b) for (a, b) in zip(str(i), str(i)[1:]))) for f in [lambda x, y: x > y, lambda x, y: x < y])) == 2
	return i


def bouncy_ratio(target):
	bouncy = 0.0
	n = 100
	while bouncy / n < target:
		n += 1
		s = list(str(n))
		s_ = sorted(s)
		if s != s_ and s != s_[::-1]:
			bouncy += 1
	return n


def bouncy_ratio(percent):
	ans = 0
	for i in range(1, 10 ** 10):
		s = str(i)
		res = ''.join(['=' if a == b else '>' if int(a) > int(b) else '<' for (a, b) in zip(s, s[1:])])
		ans += '<' in res and '>' in res
		if ans / i >= percent:
			return i


def bouncy_ratio(percent):
	b = 0
	n = 1
	while True:
		s = list(str(n))
		if s != sorted(s) and s != sorted(s)[::-1]:
			b += 1
		if float(b) / n >= percent:
			break
		n += 1
	return n


memo = {}

def bouncy_ratio(percent):
	if not 0 <= percent <= 0.99:
		raise ValueError
	(b, n) = (0.0, 100)
	while b / n < percent:
		n += 1
		if is_bouncy(n):
			b += 1
	return n

def is_bouncy(n):
	if n in memo:
		return memo[n]
	s = str(n)
	memo[n] = list(s) != sorted(s) and list(s) != sorted(s, reverse=True)
	return memo[n]


def bouncy_ratio(percent):
	if percent < 0 or percent > 0.99:
		raise Error
	n = 99
	bouncy = 0.0
	while True:
		n += 1
		ns = str(n)
		gaps = [int(ns[i]) - int(ns[i - 1]) for i in range(1, len(ns))]
		if not (all((gap >= 0 for gap in gaps)) or all((gap <= 0 for gap in gaps))):
			bouncy += 1.0
			if bouncy / n >= percent:
				return n


def bouncy_ratio(percent):

	def check(i):
		d = [int(x) for x in str(i)]
		(up, down) = (False, False)
		for k in range(1, len(d)):
			if d[k] > d[k - 1]:
				up = True
			if d[k] < d[k - 1]:
				down = True
		return up and down
	bouncy = 0.0
	for n in range(100, 100000):
		if check(n):
			bouncy += 1
		if bouncy / n >= percent:
			return n