Problem 290: Digital Signature

How many integers 0 <= n < 10^18 have the property that the sum of the digits of n equals the sum of digits of 137n?

My Algorithm

A brute-force algorithm can be written without much thinking (and just stealing the digitSum() function from problem 119).
It works pretty well for n <= 10^8.
There is a small optimization which I added based on the observation:
if n is not a multiple of 9 then the sum of digits of n never matches the sum of digits of 137n.

Coming up with a faster algorithm took me two months - well, I was working on other problems in the meantime, but this problem was a tough nut to crack -
escpecially considering the short solution that I finally found. Note: apparently almost everyone found the same algorithm, the fastest solver needed just 7 minutes (!) according to the forum.

While the brute-force algorithm works for every n, the faster algorithm requires n to be a power of ten, that is n = 10^k, e.g. n = 10^18.

Let's say S(n) returns the sum of digits of n. Then I have to find all n < 10^18 where(1)S(n) = S(137n)

If I partition n into n = 10x + y then y is the last digit of n and x everything else.(2)S(n) = S(10x + y) = S(x) + y

If n < 10, that means the number of digits is 1, then x = 0 and (8) simplifies accordingly.
If n >= 10 then x has one digit less than n (it's missing the right-most digit which is y) and(9)S(137n) = S(137x + u) + v
So that the recursion pattern is(10)u_{next} = \lfloor dfrac{137u}{10} \rfloor(11)v_{next} = 137u mod 10 + v - digit

Note

I didn't find the final steps not by sitting in front of a piece of paper - instead it was laborous work fitting the equations to the results of my brute-force search.
As mentioned before, several solvers went the straight way and immediately found the correct algorithm. Not me, I was stupid ...

Usually I rely on std::map for caching because it's simple:

no need to pre-allocate memory and fill it with an Unknown indicator

works great when the ids have huge gaps inbetween

It solves the current problem in 0.415 seconds.

When I have the correct solution, I often try to speed up the program because std::map involves lots of calls to new and delete.
An std::unordered_map is a plug-in replacement. Since most hash maps are based on a big blocks of continuous memory, there are less new and delete.
That small difference (combined with better locality / CPU caching) is enough to solve the current problem almost three times faster: in only 0.120 seconds.

There are no huge gaps between ids so that I converted the cache container to std::vector and finish in just 0.027 seconds, again 4 times faster than std::unordered_map.
Even more important, each new consumes a few bytes on its own. Therefore the faster std::vector version needs 50% less memory, too.
Feel free to experiment with #define FAST.

Interactive test

You can submit your own input to my program and it will be instantly processed at my server:

Input data (separated by spaces or newlines):

This is equivalent toecho 4 | ./290

Output:

(please click 'Go !')

Note: the original problem's input 18cannot be enteredbecause just copying results is a soft skill reserved for idiots.

(this interactive test is still under development, computations will be aborted after one second)

My code

… was written in C++11 and can be compiled with G++, Clang++, Visual C++. You can download it, too.

Those links are just an unordered selection of source code I found with a semi-automatic search script on Google/Bing/GitHub/whatever.
You will probably stumble upon better solutions when searching on your own.
Maybe not all linked resources produce the correct result and/or exceed time/memory limits.

Heatmap

Please click on a problem's number to open my solution to that problem:

green

solutions solve the original Project Euler problem and have a perfect score of 100% at Hackerrank, too

yellow

solutions score less than 100% at Hackerrank (but still solve the original problem easily)

gray

problems are already solved but I haven't published my solution yet

blue

solutions are relevant for Project Euler only: there wasn't a Hackerrank version of it (at the time I solved it) or it differed too much

orange

problems are solved but exceed the time limit of one minute or the memory limit of 256 MByte

red

problems are not solved yet but I wrote a simulation to approximate the result or verified at least the given example - usually I sketched a few ideas, too

black

problems are solved but access to the solution is blocked for a few days until the next problem is published

[new]

the flashing problem is the one I solved most recently

I stopped working on Project Euler problems around the time they released 617.

The 310 solved problems (that's level 12) had an average difficulty of 32.6&percnt; at Project Euler and
I scored 13526 points (out of 15700 possible points, top rank was 17 out of &approx;60000 in August 2017)
at Hackerrank's Project Euler+.

My username at Project Euler is stephanbrumme while it's stbrumme at Hackerrank.

Copyright

I hope you enjoy my code and learn something - or give me feedback how I can improve my solutions.All of my solutions can be used for any purpose and I am in no way liable for any damages caused.You can even remove my name and claim it's yours. But then you shall burn in hell.

The problems and most of the problems' images were created by Project Euler.Thanks for all their endless effort !!!

more about me can be found on my homepage,
especially in my coding blog.
some names mentioned on this site may be trademarks of their respective owners.
thanks to the KaTeX team for their great typesetting library !