Even without deducing a general algorithm, here's how I would have written it, in Python as well:
def largest_decent_number_of_length(N):
for num_threes in xrange(0, N, 5):
num_fives = length - num_threes
if num_fives % 3 == 0:
return "5" * num_fives + "3" * num_threes
else:
return "-1"
if __name__ == "__main__":
T = raw_input('')
for _ in range(0, T):
N = raw_input('')
print largest_decent_number_of_length(N)
This solution to me is more clear because it doesn't do magic with modulo to determine the case we are in, and incorporates some points which I feel are important for clear code.
First of all, as jonrsharpe also mentioned: if you want to do code executing at the top level, do it in an if __name__ == "__main__":
section. This will only execute when directly run, but not when imported. However, this is something python specific, and in another language you would have to do this in a different fashion.
Second: The variable names are quite specific, num_threes
and num_fives
are more clear than i
and j
. I could have gone overboard, and have written number_of_threes
but that would have made the names overly long, and thus less readable for such a short piece of code. The T
and N
however are quite short, but they are a direct translation from the document. I could have named them num_challenges
and challenge
, but that isn't necessary---assuming the reader also read the challenge itself. Another thing I always try to do is make function names read like descriptions, especially when the variables are filled in. This doesn't work often, but in the cases where you can, I believe it makes code more readable. "print largest decent number of length N" sounds like a clear task to me.
Third: After reading this, you should be able to follow my logic---I hope. The largest number will start with 5
s, followed by 3
s --- interchanging a 5
and a 3
would make the number smaller. Also, less 3
s means more 5
s, so we start with as few 3
s as possible, and add as few as possible while still satisfying the second requirement (num_threes % 5 == 0
). For each of these options, we check if the number of fives also satisfies the requirements, and if so, we print the number.
Fourth: we don't print the number, but return it, making the function reusable. (Not that important in this case, but I would just like to mention it). The main loop is concerned with reading numbers, and printing results. The function is concerned with finding the answer. The responsibilities are separated.
Fifth: The value returned is in both cases of the same type (a string, twice). Returning a string instead of a number is an arbitrary choice, however one that followed quite easily from the manipulations involved. One could decide to make it an integer instead, by writing return int("5" * num_fives + "3" * num_threes)
and return -1
. Both are a viable solution, and if I had to extend on this I would probably choose to go for wrapping the int(...)
around it. However, the most important thing is that the function returns items of only one type.
Sixth: One of the nice things of python is the for
...else
construct. The else
portion is executed when break
/return
is not called from within the loop. Using this, you don't have to use a variable flag
that gets set on each point where you break from the loop. I admit it is a weird feature and it took me quite some time to get used to it, but it is ideally suited for the loops where you try to find an item satisfying a condition. If you found it, you can just break. If you haven't found it, the else
will be executed instead.