1, 2, 3, … , 26, 27, 28, … for a, b, c, …, z, aa, ab, … , z, aa, ab, … to a, b, c, …, z, aa, ab, … https://atcoder.jp/contests/abc171/tasks/abc171_c
- Since there is a space character other than a-z, of course it is not in 26th decimal, and since there is no space character in the middle of the number, it is not in 27th decimal, either.
- I developed it based on the “fact” of test cases and their success or failure, rather than based on a vague “interpretation” that “it must be 26 decimal digits”.
- I think this test-driven story is rather interesting, so let’s summarize it before I forget
complete form python
def solve(N):
q = N
ret = ""
while True:
q, r = divmod(q - 1, 26)
ret = ascii_lowercase[r] + ret
if q == 0:
print(ret)
return
I didn’t write this code from the beginning.
At first I thought, “This problem is easy to test, so let’s make it test-driven,” so I put up a snippet that calls doctest. python
from string import ascii_lowercase
def solve(N):
"""
"""
def main():
N = int(input())
solve(N)
def _test():
import doctest
doctest.testmod()
if __name__ == "__main__":
import sys
argv = sys.argv
if len(sys.argv) == 1:
# no option
main()
elif sys.argv[1] == "-t":
_test()
else:
input = open(sys.argv[1]).buffer.readline
First, write a test case python
def solve(N):
"""
>>> solve(1)
a
"""
Naturally, the test will fail. python
$ python3 c2.py -t
**********************************************************************
File "c2.py", line 6, in __main__.solve
Failed example:
solve(1)
Expected:
a
Got nothing
**********************************************************************
1 items had failures:
1 of 1 in __main__.solve
***Test Failed*** 1 failures.
Write code that passes tests python
def solve(N):
"""
>>> solve(1)
a
"""
print(ascii_lowercase[N - 1])
Run the test to confirm it is OK (doctest does not display anything when it is OK).
Add a test case python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
"""
print(ascii_lowercase[N - 1])
Naturally, the test will fail.
See what kind of failures you have.
I’m getting IndexError: string index out of range
after reading the 27th of only 26 strings.
python
$ python3 c2.py -t
**********************************************************************
File "c2.py", line 8, in __main__.solve
Failed example:
solve(27)
Exception raised:
Traceback (most recent call last):
File ".../doctest.py", line 1329, in __run
compileflags, 1), test.globs)
File "<doctest __main__.solve[1]>", line 1, in <module>
solve(27)
File "c2.py", line 11, in solve
print(ascii_lowercase[N - 1])
IndexError: string index out of range
**********************************************************************
1 items had failures:
1 of 2 in __main__.solve
***Test Failed*** 1 failures.
Then let’s just calculate the quotient divided by 26 and the remainder. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
"""
q, r = divmod(N - 1, 26)
print(ascii_lowercase[r])
The error message changed, and it was not a runtime error, but rather that the value output was wrong. Let’s fix this. python
$ python3 c2.py -t
**********************************************************************
File "c2.py", line 8, in __main__.solve
Failed example:
solve(27)
Expected:
aa
Got:
a
**********************************************************************
1 items had failures:
1 of 2 in __main__.solve
***Test Failed*** 1 failures.
I haven’t used the quotient I calculated earlier, so I’ll use it anyway. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
"""
q, r = divmod(N - 1, 26)
ret = ascii_lowercase[r]
if q == 0:
print(ret)
else:
print(ascii_lowercase[q] + ret)
Failure that it should be aa but it is ba (foreshadowing 1) python
$ python3 c2.py -t
**********************************************************************
File "c2.py", line 8, in __main__.solve
Failed example:
solve(27)
Expected:
aa
Got:
ba
**********************************************************************
1 items had failures:
1 of 2 in __main__.solve
***Test Failed*** 1 failures.
The number was reduced by 1 to make it more consistent. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
"""
q, r = divmod(N - 1, 26)
ret = ascii_lowercase[r]
if q == 0:
print(ret)
else:
print(ascii_lowercase[q - 1] + ret)
Test OK Add a new test case. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
>>> solve(703)
aaa
"""
q, r = divmod(N - 1, 26)
ret = ascii_lowercase[r]
if q == 0:
print(ret)
else:
print(ascii_lowercase[q - 1] + ret)
Naturally, it will fail.
This error message IndexError: string index out of range
is the one I saw earlier, let’s fix it the same way.
python
$ python3 c2.py -t
**********************************************************************
File "c2.py", line 10, in __main__.solve
Failed example:
solve(703)
Exception raised:
Traceback (most recent call last):
File ".../doctest.py", line 1329, in __run
compileflags, 1), test.globs)
File "<doctest __main__.solve[2]>", line 1, in <module>
solve(703)
File "c2.py", line 18, in solve
print(ascii_lowercase[q - 1] + ret)
IndexError: string index out of range
**********************************************************************
1 items had failures:
1 of 3 in __main__.solve
***Test Failed*** 1 failures.
I copied and pasted the code I wrote earlier and modified it the same way. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
>>> solve(703)
aaa
"""
q, r = divmod(N - 1, 26)
ret = ascii_lowercase[r]
if q == 0:
print(ret)
else:
q, r = divmod(q - 1, 26)
ret = ascii_lowercase[r] + ret
if q == 0:
print(ret)
else:
q, r = divmod(q - 1, 26)
ret = ascii_lowercase[r] + ret
print(ret)
Test OK The same thing continues after the fourth digit, so let’s rewrite it using a loop. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
>>> solve(703)
aaa
"""
q = N
while True:
q, r = divmod(q - 1, 26)
ret = ascii_lowercase[r]
if q == 0:
print(ret)
break
Fail when rewritten and tested. python
$ python3 c2.py -t
**********************************************************************
File "c2.py", line 8, in __main__.solve
Failed example:
solve(27)
Expected:
aa
Got:
a
**********************************************************************
File "c2.py", line 10, in __main__.solve
Failed example:
solve(703)
Expected:
aaa
Got:
a
**********************************************************************
1 items had failures:
2 of 3 in __main__.solve
***Test Failed*** 2 failures.
Forgot to extend the string. Correction. python
def solve(N):
"""
>>> solve(1)
a
>>> solve(27)
aa
>>> solve(703)
aaa
"""
q = N
ret = ""
while True:
q, r = divmod(q - 1, 26)
ret = ascii_lowercase[r] + ret
if q == 0:
print(ret)
break
Test OK Now submitted.
digression
- I got ba in the middle (foreshadowing 1), but that was the correct 26th decimal.
- Even in decimal notation, the first number to have two digits is 10, not 00.
- This problem is different from the 26th decimal because it has to be aa, not ba.
- I see people on Twitter trying to make an N-decimal library and lamenting that they couldn’t implement the 26th decimal system correctly, but that’s not what this problem is asking for in the first place.
- Interpretation is skewed.
- If you do test-driven fact-based development, it’s hard to get distorted by interpretation.
This page is auto-translated from /nishio/テストドリブンで変形26進法の実装 using DeepL. If you looks something interesting but the auto-translated English is not good enough to understand it, feel free to let me know at @nishio_en. I’m very happy to spread my thought to non-Japanese readers.