Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find the kth eleven-non-free number

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?

like image 272
Jackson Tale Avatar asked Dec 18 '13 15:12

Jackson Tale


3 Answers

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.

  1. It requires literally a quintilian string comparisons and would take a week to resolve on most home computers.

  2. 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.

Understanding the Main Concepts

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.

  1. 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.

  2. 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
  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)        
    
    • Where 18 is the total of all "11" matches (not necessarily *11) from the 10^3 range (see Fig 1).
    • The 10's are constants.
    • And, 8 and 0 are our starting points, mentioned previously, for separately calculating *11 changes.
  2. 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
    

A Working Algorithm

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
like image 64
drankin2112 Avatar answered Nov 02 '22 04:11

drankin2112


It's interesting how easily people get scared by the term "brute force" without even trying to quantify it :)

Brute force solution is actually O(R * log R)

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? :-)

Generation solution?

Clever idea is to try to generate the number more directly:

  1. either by generating directly the k-th number;
  2. or by sequentially generating all 1-k eleven-non-free numbers;
  3. or by generating all eleven-non-free numbers < 10^n at once and sort them up.

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:

How many eleven-non-free numbers < 10n exist?

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):

enter image description here

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

O(R) = O(k)

which implies that

The brute force solution is actually O(k * log k), as well as the generate-all solution!

The dreaded brute force solution kooks much much better now, doesn't it? Yet it's still the same :)

Can we get better than this?

  • Solution 1 - would be a chance, but close to magic.
  • Solution 2 - the best you can hope for is 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.
  • Solution 3 - this we already know is O(k log k), which is the same as to find k-th number.
like image 2
Tomas Avatar answered Nov 02 '22 05:11

Tomas


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
like image 2
15 revs Avatar answered Nov 02 '22 05:11

15 revs