Let's define eleven-non-free
numbers:
If we consider a number as a string, then if any substring inside is a (non-zero) power of 11
, then this number is an eleven-non-free
number.
For example, 1123
is an eleven-non-free
number as 11
inside is 11^1
. Also 12154
is one as 121
is 11^2
. But 12345
is not, because we can't find any non-zero power of 11
inside.
So given a k, find the kth eleven-non-free
number. For example the 13th such number is 211
.
I don't know how to efficiently do it. the brute-force way is to increase i from 1 and check every number and count until the kth.
I guess we should consider strings with different length (1, 2, 3, 4, ...). then for each length, we try to fill in 11, 11^2, 11^3, etc and try to get all the combinations.
But it seems quite complicated as well.
Anyone?
OK, this took several days to figure out. The first important thing to understand is that the only concrete requirement of the euler puzzle 442 that this question stems from is to solve for E(10^18). The unrelated examples of what an 11-free number means are just distractions.
The brute force option is out of the question, for 2 reasons.
It requires literally a quintilian string comparisons and would take a week to resolve on most home computers.
Leonhard Euler was a mathematician so the spirit of the question has to be interpreted in that context.
After a while, I started focusing on pattern matching for powers of 10, instead of continually including powers of 11 in my algorithms. After you calculate what the powers of 11 are, you're done with anything related to that. They only represent strings. They aren't even included in the final algorithm, they are just used in the detective work.
I started trying to get a detailed understanding of how new 11-containing numbers are introduced between powers of 10 and if there was a predictable pattern. If there was, then it would just be a matter of using that pattern to deduce all of the 11-containing numbers introduced prior to any 10^ stopping point.
For each new 10^ reached, the number of previous 11^ string matches are identical to the previous 10^ range. It's easier to explain with a chart.
Here is a list of 10^2 - 10^6 that shows the count of new 11-containing numbers introduced so far and grouped by the 11^ number that matched the string.
Fig 1.) Number of 11^ matches in 10^ ranges grouped by 11^ string match
---------------------------
10^2 range (10 - 100)
---------------------------
11 1
---------------------------
10^3 range (100 - 1000)
---------------------------
11 18
121 1
---------------------------
10^4 range (1000 - 10000)
---------------------------
11 260
121 18
1331 1
---------------------------
10^5 range (10000 - 100000)
---------------------------
11 3392
121 260
1331 18
14641 1
---------------------------
10^6 range (100000 - 1000000)
---------------------------
11 41760
121 3392
1331 260
14641 18
161051 1
etc...
There are two requirements for making this list.
When multiple 11-containing numbers are match sequentially (like 11000 - 11999) all of those items must be attributed to the 11^ group of the first match. Meaning that even though there will be matches for 121 and 1331 in that example, all matches in that range are attributed to 11, because it is first.
Matches are made based on the shortest possibility first. i.e.) 11, 121, 1331, etc... This is because some numbers match multiple 11^ strings while others appear inside of a contiguous range. And, matching them from high to low doesn't produce a predictable pattern. Functionally, it's all the same. This just brings a whole lot of clarity into the picture.
Notice that in each new 10^ range, only 1 new 11^ match is introduced. And, the number of matches for each previous 11^ number is identical, regardless of the previous power. The difficulty here is that the number of *11 string matches can't be directly inferred from the previous power.
Fig 2.) Differentiating other 11^ patterns from the *11 match
****11
***110 pattern
**1100 pattern
*11000 pattern
110000 pattern
etc...
All of the "pattern" matches are highly predictable within 10^ powers. Only the *11 numbers are not obvious in how they accumulate. It's not really that there isn't a pattern, the problem is that you would have to presume numbers that were scanned right to left and, as a result, none if the other patterns would be predictable anymore.
As it turns out, a separate accumulator has to be tracked concurrently that allows us to use the previous *11 matches to calculate the number of new *11 matches. For any of you math geniuses out there, there may be a more elegant solution. But, here is mine.
You have to always be separately keeping track of the number of *11 matches from the previous 2 powers. Using these as operands, you can calculate the number of *11 matches for the current power.
Example:
The number of *11 matches in the 10^3 range is 8. There are also 10 matches on "110" but they don't matter here, we are just tracking numbers that end with "11". So, we are starting with the 2 previous *11 match counts being 8 and 0 from the 2 previous 10^ ranges.
Important: For any number less than 10^2, 0 is the previous match count for this calculation. That is why 0 is the second look-back operand when working with 10^3.
Fig 3.) How to calculate *11 match counts per 10^ range using back-tracking
10^3 | 80 = 8 * 10 - 0;
10^4 | 792 = 80 * 10 - 8;
10^5 | 7840 = 792 * 10 - 80;
This number however is only useful by looking at it, again, from a different perspective.
Fig 4.) Illustration of how match groups scale by powers of 10
==================================
# Formulas for 10^4
==================================
80 **11 = 8 * 10 - 0
80 *110 pattern = previous power's *11 times 10
100 1100 pattern = previous power's 110 times 10
==================================
260 Total matches in range
==================================
# Formulas for 10^5
==================================
792 ***11 = 80 * 10 - 8
800 **110 pattern = previous power's **11 times 10
800 *1100 pattern = previous power's *110 times 10
1000 11000 pattern = previous power's 1100 times 10
==================================
3392 Total matches in range
In these examples, only the *11 number has to be calculated separately. Seeing these numbers stacked like this sort of makes it feel more complicated than it is. It's only important to understand the concept. In practice, we abstract out everything except the *11 calculation through cumulative multiplication.
Now, before we get into a working algorithm, the last thing to understand conceptually is how to use these patterns to calculate the Nth 11-free number ending at each ^10.
This is a two step process so I'll demonstrate using an example. Lets look at the 1000 - 10000 range from Fig 1. 10000 is 10^4 so that is the range we are solving for.
The following 11^ groups are all within that range. The 18 and the 1 can be derived from the previous powers (see Fig 1).
match #
---------------
11 260
121 18
1331 1
Calculate the total 11-containing matches in the current 10^4 range. To do this, we first have to calculate the value for the "11" match category. The 260 value above can be calculated using numbers from both the previous ranges and the separate *11 tracking operands like so:
Calculating *11 count for 10^4
260 = (18 * 10) + (8 * 10 - 0)
Combine the result of 10^4 with all the other results back to 10^2. Note: The total matches for 10^2 is 1 because there is only one 11-containing number from 0 through 100.
Total 11-containing numbers in 10^4 range: 279 = 260 + 18 + 1
Total 10^4 range plus previous totals : 299 = 279 + 19 + 1
---------------------
10^4 solved: 9701 = 10^4 - 299
Here is an algorithm written in C# that solves for each of 10^1 through 10^18. It returns almost instantly. Out of respect for project euler I didn't post the answer to their puzzle directly. The output is accurate though. Right now the #442 question only shows 163 people have solved the problem compared to 336260 people solving problem #1. If that number starts growing for no reason, I will delete this post.
private ulong GetNthElevenFreeForPow10(uint pow){
if(pow > 18)
throw new ArgumentException("Argument must be a positive integer between 1 and 18", "pow");
// All of the numbers up to 10^0 & 10^1 are 11-free
// if 1 is considered 11-free as the euler question
// states.
if (pow == 0 || pow == 1)
return (ulong)Math.Pow(10, pow);
// The sequence of 11s doesn't form a repeatable pattern
// until 10^4 because it requires back tracking to
// calculated running totals.
if (pow == 2)
return (ulong)Math.Pow(10, pow) - 1;
if (pow == 3)
return (ulong)Math.Pow(10, pow) - (18 + 1 + 1);
// At each new power the number of elevens created is
// easily predicted based on the previous power. But,
// the number of new entries ending with 11 i.e.) xxx11
// is a little trickier. It requires a lookback at the
// two previous powers. This array stores the lookback
// operands used to make that calculation.
ulong[] lookbackOperands = new ulong[] {
0, 8
};
// this number is used to calculate all of the new 11-containing
// numbers for each power. The next power is always a calculation
// on the previous.
ulong elevensConsumedCurrent = 18;
ulong elevensConsumedPrevious = 18 + 1;
// this number holds a running total all 11-containing
// numbers consumed so far.
ulong elevensConsumedTotal = 20;
for (int n = 4; n <= pow; n++) {
// Calculate the number of new items added that end with "11".
ulong endsWith11Current = lookbackOperands[1] * 10 - lookbackOperands[0];
lookbackOperands[0] = lookbackOperands[1];
lookbackOperands[1] = endsWith11Current;
elevensConsumedCurrent = elevensConsumedCurrent * 10 + endsWith11Current;
elevensConsumedPrevious += elevensConsumedCurrent;
elevensConsumedTotal += elevensConsumedPrevious;
}
return (ulong)Math.Pow(10, pow) - elevensConsumedTotal;
}
Usage:
StringBuilder sb = new StringBuilder();
for (uint n = 1; n <= 18; n++)
sb.AppendLine(String.Format(
"Nth 11-Free Number @ 10^{0}\t{1}",
n,
GetNthElevenFreeForPow10(n).ToString()
)
);
Console.WriteLine(sb.ToString());
Here are the first 15.
Nth 11-Free Number @ 10^1 10
Nth 11-Free Number @ 10^2 99
Nth 11-Free Number @ 10^3 980
Nth 11-Free Number @ 10^4 9701
Nth 11-Free Number @ 10^5 96030
Nth 11-Free Number @ 10^6 950599
Nth 11-Free Number @ 10^7 9409960
Nth 11-Free Number @ 10^8 93149001
Nth 11-Free Number @ 10^9 922080050
Nth 11-Free Number @ 10^10 9127651499
Nth 11-Free Number @ 10^11 90354434940
Nth 11-Free Number @ 10^12 894416697901
Nth 11-Free Number @ 10^13 8853812544070
Nth 11-Free Number @ 10^14 87643708742799
Nth 11-Free Number @ 10^15 867583274883920
It's interesting how easily people get scared by the term "brute force" without even trying to quantify it :)
where R is the result (k
-th eleven-non-free number). You just test all numbers 1..R and for each of them you perform string searches for 11, 121, 1331, 14641 etc. using prepared automaton (for given number of digits). O(R * log R)
doesn't look that bad, if you look at it this way, does it? :-)
Clever idea is to try to generate the number more directly:
k
-th number;k
eleven-non-free numbers;Solution 1 would have to be very clever, if not close to impossible. Solution 2 seems better than brute force solution because it skips those numbers which are not eleven-non-free. Solution 3 would be an easily implemented alternative, which brings us to important question:
Easy combinatorics (works exactly for n <= 10; for higher n it is close approximation, neglecting that 112 is substring of 1113 and 11 is substring of 1111 and 1119):
so basicly for the limit 10n you get more than 10n-2 solutions! This means that number of solutions is a constant proportion of the limit, which means that
which implies that
The dreaded brute force solution kooks much much better now, doesn't it? Yet it's still the same :)
O(k)
but you would have to be very careful to achieve this. There will be many complications that will make it uneasy to select next smallest eleven-non-free-number. E.g. when generating all 11xxx, 121xx, 1331x, eg. 13121 falls in between, making any automatic generation of ordered numbers difficult, let alone duplicities caused by pattern appearing in xxx digits etc. You will probably end up with some complicated data structure that will force O(log k)
in each step, and thus O(k log k)
in total.O(k log k)
, which is the same as to find k
-th number.As good starting point is to consider the related problem of counting how many eleven-non-free (ENF) integers there are of N digits. This is not exactly the same thing as finding the n'th ENF integer, but the latter problem can be reduced to the former pretty easily (see the end of this post for java code that does this).
The idea is to do dynamic programming on prefixes. For any string of digits s
, and any integer k
, let N(k,s)
denote the number of ENF strings of length k
which start with s
. And, for any string s
, let s_0
be the longest suffix of s
that also occurs as a prefix of any power of eleven (POE) of length k
or less. Then, assuming that s
itself does not contain any POE substrings, we have the equation:
N(k,s) = N(k-length(s)+length(s_0),s_0)
The reasoning behind this equation is as follows. Since s_0
is a suffix of s
, let us write s
as a concatenation of some other string q
and s_0
: s=[q,s_0]
. Now, let r
be any digit string which starts with s
and again let us write this as a concatenation, as r=[s,r_0] = [q,s_0,r_0]
.
If r
contains a POE as a substring, then either this POE is contained in r_0
, or else it crosses over the boundary between s
and r_0
. In this case, s
must end in a POE prefix, and, since we know that the longest POE prefix that occurs as a suffix of s
is s_0
, it follows that if r
contains a POE substring, then this substring must be contained in [s_0,r_0]
. This gives us a one-to-one correspondence between ENFs that begin with s=[q,s_0]
and ENFs that begin with s_0
.
This above equation gives rise to a recursive algorithm to count the number of k-digit ENFs with a given prefix. The base cases end up being instances where length(s_0)=length(s)
which means that s
itself is a prefix of a POE of length k or less. Since there aren't very many such POE prefixes (at most k choose 2 which is O(k^2)), this formula leads to an efficient algorithm. Here is pseudocode:
Given a prefix s and an integer k, this function computes N(k,s)
if s contains a POE then return 10^(k-length(s))
if length(s) = k return 0
let s0 = longest POE prefix which is a suffix of s
if(length(s0)<length(s))
return N(k-length(s)+length(s0),s0)
total = 0
for d = "0" to "9"
total += N(k,concatenate(s,d))
return total
Using dynamic programming, the running time of this algorithm will be polynomial in k, the number of digits. The algorithm only recurses on POE prefixes, and since there are O(k^2) of these, the running time will be O(k^2) times the running time of each iteration. Using a naive O(k^2) algorithm to find the longest suffix that matches a POE prefix would thus lead to an O(k^4) algorithm, while using a radix tree to find matches in linear time would result in O(k^3). The java code given below uses the naive algorithm, and experimentally, for values up to around k=100 seems to be Θ(k^4.5), though this discrepancy could well be due to implementation details or constant factors affecting runtimes for small input sizes. Here is the code:
public class Eleven {
public static final String[] digits =
{"0","1","2","3","4","5","6","7","8","9"};
public static BigInteger countENF(String prefix, int k){
return countENF(prefix,k,new HashMap(),getPowers(k));
}
public static BigInteger countENF(String prefix,
int k,
// This map contains stored results for dynamic programming
// Pair is an auxiliary class that does what you think it does
Map<Pair<String,Integer>,BigInteger> resultMap,
// precomputed list of powers of 11
List<String> powers
){
// Dynamic programming case
BigInteger res = resultMap.get(new Pair(prefix,k));
if(res != null)
return res;
// base cases
if(!isEFree(prefix, powers)){
res = new BigInteger("10").pow(k-prefix.length());
resultMap.put(new Pair<>(prefix,k), res);
return res;
}
if(prefix.length() >= k){
res = new BigInteger("0");
resultMap.put(new Pair<>(prefix,k), res);
return res;
}
String s0 = getMaxPrefix(prefix, powers);
if(s0.length() < prefix.length()){
return countENF(s0,k+s0.length()-prefix.length(),resultMap,powers);
}
// recursive summation
res = new BigInteger("0");
for(String d : digits){
String s1 = prefix + d;
res = res.add(countENF(s1,k,resultMap,powers));
}
resultMap.put(new Pair<>(prefix, k), res);
return res;
}
//
// helper functions
//
// returns all POEs of at most length digits
private static List<String> getPowers(int length) {
List<String> ret = new ArrayList<>();
BigInteger val = new BigInteger("11");
BigInteger eleven = new BigInteger("11");
while(val.toString().length() <= length){
ret.add(val.toString());
val = val.multiply(eleven);
}
return ret;
}
// finds the longest string that is both a prefix of s and a suffix of a POE
private static String getMaxSuffix(String s, List<String> powers){
for(int i=s.length(); i>0; i--){
String sub = s.substring(0,i);
for(String p : powers){
if(p.endsWith(sub))
return sub;
}
}
return "";
}
public static boolean isEFree(String s, List<String> powers){
for(String p : powers){
if(s.indexOf(p)>=0)
return false;
}
return true;
}
As mentioned above, this algorithm does not solve the exact problem in the OP. But, modifying this code to actually find the n'th ENF number is pretty simple. Making
repeated callse to countENF
, we first figure out how many digits the n'th ENF number has,
and then, one digit at a time, we figure out the digits of the n'th ENF from left to right.
public static String getNthENF(BigInteger i){
int k = i.toString().length();
HashMap results = new HashMap();
List<String> powers = getPowers(k);
while(countENF("",k,results,powers).compareTo(i) < 0){
k++;
powers = getPowers(k);
}
String solution = "";
BigInteger total = new BigInteger("0");
while(solution.length() < k){
for(String d : digits){
BigInteger newTotal = total.add(countENF(solution + d,k,results,powers));
int comp = newTotal.compareTo(i);
if(comp >= 0){
solution = solution + d;
break;
}
total = newTotal;
}
}
return solution;
}
Here is some sample output, giving the N'th ENF as computed using dynamic programming, as well as using brute force, for powers of 10.
Dynamic Programming:
10^1: 118
10^2: 1178
10^3: 11680
10^4: 115730
10^5: 1146628
10^6: 11360558
10^7: 112558960
10^8: 1115229050
10^9: 11049731548
10^10: 103258311161
10^11: 935443232311
10^12: 8576360477119
10^13: 79330786951511
10^14: 732117130575070
10^15: 6880811638385388
10^16: 64284911460844887
10^17: 610616803411054857
10^18: 5759459802926457113
10^19: 54555977711878792498
10^20: 518773721711219891634
Brute Force:
10^1: 118
10^2: 1178
10^3: 11680
10^4: 115730
10^5: 1146628
10^6: 11360558
10^7: 112558960
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With