让我们定义eleven-non-free
个数字:
如果我们将数字视为一个字符串,那么如果内部的任何子字符串是11
的(非零)幂,那么这个数字就是eleven-non-free
个数字。
例如,1123
是eleven-non-free
个数字,11
里面是11^1
。 12154
也是121
11^2
12345
。但是11
不是,因为我们内部找不到任何非零幂eleven-non-free
。
所以给定k,找到第k个211
个数。例如,第13个这样的数字是{{1}}。
我不知道如何有效地做到这一点。蛮力的方法是将i从1增加并检查每个数字和计数直到第k个。
我想我们应该考虑不同长度的字符串(1,2,3,4 ......)。然后对于每个长度,我们尝试填写11,11 ^ 2,11 ^ 3等,并尝试获得所有组合。
但它似乎也很复杂。
任何?
答案 0 :(得分:3)
好的,这需要几天的时间来弄明白。要理解的第一个重要的事情是,这个问题的唯一具体要求是解决E(10 ^ 18)。 11个免费号码意味着什么的无关例子只是分散注意力。
由于两个原因,蛮力选项是不可能的。
它需要字面上的quintilian字符串比较,并且需要一周的时间来解决大多数家用电脑。
Leonhard Euler是一位数学家,因此必须在这种背景下解释问题的精神。
过了一会儿,我开始专注于10的幂的模式匹配,而不是在我的算法中不断包括11的幂。在计算出11的权力之后,你就完成了与之相关的任何事情。它们只代表字符串。它们甚至不包含在最终算法中,它们只用于侦探工作。
我开始尝试详细了解如何在10的幂之间引入新的包含11的数字以及是否存在可预测的模式。如果有,那么只需要使用该模式来推断出在任何10 ^停止点之前引入的所有包含11的数字。
对于到达的每个新的10 ^,先前的11 ^字符串匹配的数量与先前的10 ^范围相同。用图表解释起来比较容易。
这是一个10 ^ 2 - 10 ^ 6的列表,显示到目前为止引入的新的包含11的数字的数量,并按照匹配字符串的11 ^数字进行分组。
图1。) 10 ^范围内11 ^匹配的数量按11 ^字符串匹配分组
---------------------------
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...
制作此列表有两个要求。
当多个含11个数字顺序匹配时(如11000 - 11999),所有这些项目必须归属于第一个匹配的11 ^组。这意味着即使在该示例中将存在121和1331的匹配,该范围内的所有匹配都归因于11,因为它是第一个。
首先根据最短的可能性进行匹配。即)11,121,1331等......这是因为一些数字匹配多个11 ^字符串而其他数字出现在连续范围内。并且,将它们从高到低匹配并不会产生可预测的模式。在功能上,它都是一样的。这只是为图片带来了很多清晰度。
请注意,在每个新的10 ^范围内,只引入了1个新的11 ^匹配。并且,无论先前的功率如何,每个先前的11 ^数的匹配数是相同的。这里的困难在于,* 11字符串匹配的数量无法从之前的权力中直接推断出来。
图2。)区分其他11 ^模式与* 11匹配
****11
***110 pattern
**1100 pattern
*11000 pattern
110000 pattern
etc...
所有"模式"匹配在10 ^幂范围内是高度可预测的。只有* 11的数字在累积方式上并不明显。它不是真的没有模式,问题是您必须设定从右到左扫描的数字,因此,如果其他模式不再可预测,则不会产生任何数字。
事实证明,必须同时跟踪一个单独的累加器,这允许我们使用之前的* 11匹配来计算新* 11匹配的数量。对于你们中的任何一位数学天才,可能会有更优雅的解决方案。但是,这是我的。
您必须始终分别跟踪前2个权力中* 11个匹配项的数量。使用这些作为操作数,您可以计算当前功率的* 11匹配数。
示例:
10 ^ 3范围内的* 11场比赛的数量是8.在" 110"上还有10场比赛。但他们在这里并不重要,我们只是跟踪以" 11"结尾的数字。因此,我们首先从 2先前 10 ^范围中的前2个* 11匹配计数为8和0开始。
重要提示:对于任何小于10 ^ 2的数字,0是此计算的上一个匹配计数。这就是为什么0是使用10 ^ 3时的第二个回溯操作数。
图3。)如何使用反向跟踪计算每10 ^范围的* 11匹配计数
10^3 | 80 = 8 * 10 - 0;
10^4 | 792 = 80 * 10 - 8;
10^5 | 7840 = 792 * 10 - 80;
然而,这个数字只有从不同的角度来看它才有用。
图4。)匹配组如何按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
在这些示例中,只需要单独计算* 11数。看到这些数字堆叠起来就像这样让人觉得它比现在更复杂。理解这个概念很重要。在实践中,我们通过累积乘法抽象出除了* 11计算之外的所有东西。
现在,在我们进入一个工作算法之前,最后要理解的是如何使用这些模式来计算每个^ 10结尾的第N个11个数。
这是一个两步过程,所以我将演示一个例子。让我们看一下图1中的1000 - 10000范围.10000是10 ^ 4,这就是我们要解决的范围。
以下11 ^组都在该范围内。 18和1可以从先前的权力中得出(见图1)。
match #
---------------
11 260
121 18
1331 1
计算当前10 ^ 4范围内的总共11个匹配项。要做到这一点,我们首先要计算" 11"匹配类别。上面的260值可以使用两者以前的范围和单独的* 11跟踪操作数中的数字来计算:
计算10 ^ 4的* 11计数
260 = (18 * 10) + (8 * 10 - 0)
将10 ^ 4的结果与所有其他结果组合回10 ^ 2。 注意: 10 ^ 2的总匹配数为1,因为从0到100只有一个包含11的数字。
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
这是一个用C#编写的算法,它解决了10 ^ 1到10 ^ 18中的每一个。它几乎立即返回。出于对项目欧拉的尊重,我没有直接发布他们的谜题答案。但输出结果准确。现在#442问题只显示了163人已经解决了问题,相比之下,336260人解决了问题#1。如果该号码无缘无故地开始增长,我将删除此帖子。
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;
}
用法:
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());
这是第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
答案 1 :(得分:2)
有趣的是,人们很容易被“蛮力”一词吓唬,甚至没有试图量化它:)
其中R是结果(k
- 十一 - 非自由数)。您只需测试所有数字1..R,并为每个数字执行字符串搜索11,121,1331,14641等使用准备好的自动机(给定的位数)。 O(R * log R)
看起来不那么糟糕,如果以这种方式看待它,是吗? : - )
聪明的想法是尝试更直接地生成数字:
k
- 号码; k
11个非免费号码; 解决方案1必须非常非常聪明,如果不是不可能的话。解决方案2似乎比蛮力解决方案更好,因为它会跳过那些非十一 - 非自由的数字。解决方案3将是一个易于实现的替代方案,这将带给我们一个重要的问题:
简单的组合学(完全适用于n <= 10;对于更高的n,它是近似的,忽略11 2 是11 13 的子串,11是子串11 11 和11 19 ):
基本上,对于极限10 n ,你得到超过10个 n-2 解决方案!这意味着解决方案的数量是限制的固定比例,这意味着
暗示
可怕的蛮力解决方案现在好多了,不是吗?但它仍然是相同的:)
O(k)
,但您必须非常小心地实现这一目标。将会有许多并发症使得选择下一个最小的十一 - 非自由数字变得困难。例如。当生成所有11xxx,121xx,1331x时,例如。 13121介于两者之间,使任何有序数字的自动生成变得困难,更不用说由xxx数字等出现的模式引起的重复。你可能最终会得到一些复杂的数据结构,这会强制{{1}在每一步中,总共O(log k)
。O(k log k)
,这与查找O(k log k)
- 号码相同。答案 2 :(得分:2)
好的出发点是考虑计算有多少十一个非自由(ENF)整数有N个数字的相关问题。这与查找第n个ENF整数并不完全相同,但后一个问题可以很容易地减少到前者(请参阅本文末尾的java代码执行此操作)。
我们的想法是对前缀进行动态编程。对于任何数字字符串s
和任何整数k
,让N(k,s)
表示以k
开头的长度为s
的ENF字符串数。并且,对于任何字符串s
,让s_0
成为s
的最长后缀,该后缀也作为长度为k
或更少的任何11次幂(POE)的前缀出现。然后,假设s
本身不包含任何POE子串,我们有等式:
N(k,s) = N(k-length(s)+length(s_0),s_0)
这个等式背后的原因如下。由于s_0
是s
的后缀,因此我们将s
写为其他字符串q
和s_0
:s=[q,s_0]
的串联。现在,让r
成为以s
开头的任何数字字符串,然后让我们将其作为连接写为r=[s,r_0] = [q,s_0,r_0]
。
如果r
包含POE作为子字符串,则此POE包含在r_0
中,或者它跨越s
和r_0
之间的边界。在这种情况下,s
必须以POE前缀结尾,并且,因为我们知道作为后缀s
出现的最长POE前缀是s_0
,所以如果r
1}}包含一个POE子字符串,然后该子字符串必须包含在[s_0,r_0]
中。这使我们在以s=[q,s_0]
开头的ENF和以s_0
开头的ENF之间进行一对一的对应。
上述等式产生递归算法,用于计算具有给定前缀的k位ENF的数量。基本案例最终成为length(s_0)=length(s)
的实例,这意味着s
本身是长度为k或更短的POE的前缀。由于没有很多这样的POE前缀(最多k选择2是O(k ^ 2)),因此该公式导致有效的算法。这是伪代码:
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
使用动态编程,该算法的运行时间将是k的多项式,即位数。该算法仅对POE前缀进行递归,并且由于其中存在O(k ^ 2),因此运行时间将是每次迭代的运行时间的O(k ^ 2)倍。使用朴素O(k ^ 2)算法找到与POE前缀匹配的最长后缀将因此导致O(k ^ 4)算法,而使用基数树在线性时间中找到匹配将导致O(k ^ 3)。下面给出的java代码使用朴素算法,并且在实验上,对于大约k = 100的值似乎是Θ(k ^ 4.5),尽管这种差异很可能是由于实现细节或影响小输入大小的运行时的常数因素。这是代码:
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;
}
如上所述,该算法无法解决OP中的确切问题。但是,修改此代码以实际找到第n个ENF编号非常简单。制造
重复呼叫countENF
,我们首先计算出第n个ENF号码有多少位数,
然后,一次一个数字,我们从左到右计算出第n个ENF的数字。
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;
}
这是一些示例输出,给出了使用动态编程计算的第N个ENF,以及使用强力,功率为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
答案 3 :(得分:1)
让我们采取一种原则性的方法来构建解决方案,而不是试图一次性处理它。
设S为11的幂序列:
11,121,1331,14641,161051,......
设E(n)是包含n的字符串表示作为子字符串的数字集。您正在寻找的集合是S中的E(n)与n的结合。
我们如何为某些n生成E(n)的元素? (例如,我们以n = 121为例。)
制作有向无环图,其中n是根节点。将n指向以下所有19个数字:
追加0-9:n0 n1 n2 n3 n4 n5 n6 n7 n8 n9
前置1-9:1n 2n 3n 4n 5n 6n 7n 8n 9n
对每个新节点重复此过程。请注意,某些节点最终可能会有多个父节点,因此您需要从一个节点到节点指针维护一个散列映射。 (您可以通过121 - > 1217 - > 71217或121 - > 7121 - > 71217到达71217。)
这个图的特性是,对于从u到v的任何边,我们都有u&lt; v。这很重要,因为这意味着我们可以通过广度优先生成图表来按递增顺序生成E(n)元素,始终以最小数量扩展未扩展节点。
现在合并S中所有n的这些图。
你有一个大的有向非循环图(无限),你可以以广度优先的方式生成,总是用最小的数字扩展未扩展的节点,发出下一个(第k个)十一个非空闲的数字。
它有效,我测试了它。这是一个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))
}
}
要获得第k个十一 - 非自由数,只需执行类似generate_eleven_non_free()。element_at(k)的操作,其中应该选择第k个产生的数字。
答案 4 :(得分:0)
10^18
非常非常大。蛮力不是你的朋友。
但是,让我们注意一些便利:
s
代表十一个非空闲数字,那么d s
(其中d
是一串数字)也是如此。k
个数字是十一个非免费的,你可以根据多少k-1
个数字来计算11个非自由数字。最后,在顶部抛出一些二进制搜索样式逻辑,你应该能够确切地找到哪个数字是第N个十一 - 非自由数字。
答案 5 :(得分:0)
这听起来像是一种分而治之的算法。
此问题与http://www.geeksforgeeks.org/divide-and-conquer-maximum-sum-subarray/
非常相似并且:http://en.wikipedia.org/wiki/Maximum_subarray_problem
当你划分时,你将原来的String分成两个大约1/2的部分。在你的递归的底部,你很容易遇到小的1个字符串,这些字符串都是十一个(因为它们没有足够的字符,内部有11个幂)。
当您在递归中“加强”时,会出现“技巧”。在这一步中,您需要利用这样一个事实,即您正在寻找的任何“功率”(即121)必须桥接输入字符串的“中间”。
例如,当你从长度为1的字符串“升级”到长度为2的字符串时,你会知道你正在寻找的某些功能是在上半部分,有些是在下半部分输入字符串。
答案 6 :(得分:0)
这看起来像O(klog^2(k))
算法:
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)
答案 7 :(得分:0)
given a k, find the kth eleven-non-free number.
...
这是个大问题。
由于这是时间问题,我正在编写伪代码。
var eleven_powers = [];
for (var i=0; i<1000; i++)
eleven_powers.push(Math.pow(11,i));
non-eleven-free
个数字。这将不得不处理大整数。 for (var i=11; i<Integer.MAX_VALUE; i++){
if(string(i).substring(for each of eleven_powers) == true)
store it sequentially results_array
}
Take k, return results_array[k]