Readablility/pythonization
PEP8 is your friend
Use recommended practices, like using snake_case instead of camelCase for functions and variables.
Short-circuit evaluation
and
and or
operators evaluate the second argument only if the first can't tell the value - like False
for and
and True
for or
. So, if you move all multiplications in a condition, some of them will not be calculated.
if same_digits(i, x*2) and same_digits(i,x*3) and ...
Move repeating expressions into loops
Luckily, Python has functions to check several expressions for True
at once: any
for at least one True
and all
for all. They work with a short-circuit and can work with any iterable - like generator expression:
if all(same_digits(i, x*j) for j in range(1,7)):
There's a more pythonic way to have something like unlimited range: itertools.count()
from itertools import count
for i in count(2):
#no need to increment i
Using break
instead of found
variable
Though not a structured feature, it can be useful
for ...:
if ...:
break
Return the value from the function, not output it. return
statement works just like break, so we can omit it.
All together
from itertools import count
def same_digits(a, b):
return sorted(str(a))==sorted(str(b))
def main():
for i in count(2):
if all(same_digits(i, x*j) for j in range(1,7)):
return i
if __name__ == "__main__":
print(main())
Optimizations
I don't think you can change the complexity of an algorithm, but you can avoid unnecessary actions. Profile the code for everything below - Python is a very high-level programming language, and built-in functions can prove faster then better algorithms for small optimizations .
same_digits
Instead of using str, divide (with divmod) both numbers and count digits in a list - adding for a and subtracting for b. If at some point you reach negative value or lengths are different - return False
. Counting digits is slightly faster than sorting, and you avoid dividing after problem is found.
Multiples of 9
The number with this property should be a very specific. The sum of its digits remains the same after multiplication (because digits are the same). If the number is a multiple of 3, the sum of its digits also will be the multiple of 3, the same for 9. But \$3i\$ is a multiple of 3 and has the same digits, so \$i\$ will be the multiple of 3, \$i=3k\$. Once again, \$3i=9k\$ will be the multiple of 9, so i will be the multiple of 9. No sense to check not multiples of 9:
for i in count(9,9):
6*i
should have the same number of digits
The second idea is that 6*i
should have the same number of digits with i. You can refactor the loop into nested loops: outer for the number of digits (name it d
) and inner for numbers from 100...08 (d
digits) to 100..00 (d+1
digits)/6, everything bigger will make 6*i
have d+1
digit.