Find the kth eleven-non-free number

前端 未结 8 2126
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-04 10:10

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

相关标签:
8条回答
  • 2021-02-04 10:21

    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
    
    0 讨论(0)
  • 2021-02-04 10:36

    Edit: This approach misses the eleven-non-free number 1011 for example. I know what's wrong and will fix it, but can't do it now.

    Let's take a principled approach that builds up to the solution rather than trying to handle it all at once.

    Let S be the sequence of powers of 11:

    11, 121, 1331, 14641, 161051, ...

    Let E(n) be the set of numbers that contain the string representation of n as a substring. The set you are looking for is the union of E(n) over n in S.

    How do we generate the elements of E(n) for some n? (Let's take n = 121 for example.)

    Make a directed acyclic graph where n is the root node. Make n point to all 19 of the following numbers:

    Append 0-9: n0 n1 n2 n3 n4 n5 n6 n7 n8 n9
    Prepend 1-9: 1n 2n 3n 4n 5n 6n 7n 8n 9n

    Repeat the process for each of these new nodes. Notice that certain nodes may end up with more than one parent, so you'll want to maintain a hashmap from number to node pointer. (You can get to 71217 by 121 -> 1217 -> 71217 or 121 -> 7121 -> 71217.)

    This graph has the property that, for any edge from u to v, we have u < v. This is important because it means we can generate the elements of E(n) in increasing order by doing a breadth-first generation of the graph, always expanding the unexpanded node with smallest number.

    Now merge these graphs for all n in S.

    You have one large directed acyclic graph (that is infinite) that you can generate in a breadth-first manner, always expanding the unexpanded node with smallest number, emitting the next (kth) eleven-non-free number.

    Pseudocode

    It works, I tested it. Here's a C# gist: https://gist.github.com/timothy-shields/8026924

    procedure generate_eleven_non_free
    i = 1
    n = 11^i
    //frontier nodes; F.insert(x) does nothing if x already in F
    F = empty-min-heap-set
    while (true)
    {
        if (F.empty() or F.min() > n)
        {
            F.insert(n)
            i = i + 1
            n = 11^i
        }
        else
        {
            m = F.extractmin()
            yield m
            for j = 0:9        
                F.insert(append(m,j))
            for j = 1:9
                F.insert(append(j,m))
        }
    }
    

    To get the kth eleven-non-free number, simply do something like generate_eleven_non_free().element_at(k), where this is supposed to pick out the kth yielded number.

    0 讨论(0)
  • 2021-02-04 10:36

    This looks like an O(klog^2(k)) algorithm:

    def add(vals, new_vals, m, val):
        if val < m and val not in vals and val not in new_vals:
            new_vals.append(val)
    
    def kth11(k):
        vals = [ 11**i for i in range(1, k+1) ]
        new_vals = [ ]
        while True:
            vals = sorted(vals + new_vals)
            vals = vals[:k]
            m = vals[-1]
            new_vals = [ ] 
            for v in vals:
                for s in range(1, 10):
                    val = int(str(s) + str(v))
                    add(vals, new_vals, m, val)
                for s in range(0, 10):
                    val = int(str(v) + str(s))
                    add(vals, new_vals, m, val)
            if len(new_vals) == 0: break
        return vals[-1]
    
    print kth11(130)
    
    0 讨论(0)
  • 2021-02-04 10:37

    This sounds like a divide and conquer algorithm to me.

    This problem feels very similar to: http://www.geeksforgeeks.org/divide-and-conquer-maximum-sum-subarray/

    and this: http://en.wikipedia.org/wiki/Maximum_subarray_problem

    When you divide you split the original String s into 2 parts that are roughly 1/2 as big. At the bottom of your recursion you have easy problems with small 1 char Strings that are all eleven-free (because they don't contain enough chars to have any powers of 11 inside).

    The "trick" comes in when you "step up" in the recursion. In this step you need to take advantage of the fact that any "power" (i.e. 121) you are looking for MUST bridge the "middle" of the input string.

    For example, when you "step up" from Strings of length 1 to Strings of length 2 you'll know that some of the power you're looking for is in the first half, and some is in the second half of the input string.

    0 讨论(0)
  • 2021-02-04 10:39

    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.
    0 讨论(0)
  • 2021-02-04 10:39

    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
    
    0 讨论(0)
提交回复
热议问题