This is a hearty “congratulations!” message. I often tackle a problem inspired by The On-Line Encyclopedia of Integer Sequences (OEIS). Here, sequence A378048: integers k such that k and k**2 together use at most 4 distinct decimal digits.
- 816 is in the sequence since 816**2 = 665856 and both together use at most 4 distinct digits.
- 149 is not in the sequence since 149**2 = 22201 and both together use 5 distinct digits.
I usually use PyPy for these, because it’s usually much faster than CPython, and if ints are “small enough” uses much less memory (“small enough” ints are apparently unboxed under PyPy) if stored in lists.
For this puzzle, it found all solutions with no more than 18 decimal digits in about 165 seconds (no, the first 5 programs you try for this will take very much longer - on the OEIS mailing list, one person coding in C said it took an hour to find the first 10,000 terms, and that was after they gave up waiting for a “pure brute force” program - this takes thought to make fast). There are 114,667 of them.
Just for fun, I ran it too under the released 64-bit Windows Python 3.13.0. Big surprise! It not only wasn’t much slower than under PyPy, it was faster, done in about 145 seconds. No real idea why. It’s overwhelmingly simple int arithmetic and int->str conversions, but rather involved logic. One factor is that for bigints, CPython has special code to compute i*i
(multiplying two ints that are the very same object), which builds only about half of the full “multiplication tree”.
In any case, I don’t believe that’s happened before for an OEIS program using pure Python. There are many such sequences I do use CPython for, but using “really really big ints”, where the gmpy2
extension module is a superpower pure PyPy speed can’t touch.
Would this be faster still in C? I doubt it, but don’t care enough to try. The ints here routinely overflow 64 bits, and when moving from one decade to the next it needs to sort ever-growing auxiliary helper lists (recording the suffixes that could still possibly be part of a solution), but those lists have structure of a kind CPython’s list.sort()
can exploit.
So, bravo! CPython’s speed greatly impressed me here. Keep it up .
Ah, I need to clarify a fine point: CPython was actually slower until I wrapped the code in a function. Accessing locals is very much faster than accessing globals under CPython, but PyPy doesn’t much care about that.