如果整数从1到999,999,999 被写成单词,排序 按字母顺序,连接,什么 是第510亿封信?
准确地说:如果整数来自1 以999,999,999表示 (省略空格,'和',和 标点符号 - 请参阅下面的注释格式),并排序 按字母顺序排列前六位 整数是
- 8
- 18
- eighteenmillion
- eighteenmillioneight
- eighteenmillioneighteen
- eighteenmillioneighteenthousand
,最后一个是
- twothousandtwohundredtwo
然后从上到下阅读,向左 对,第28封信完成了 拼写整数 “eighteenmillion”。
第51亿封信也完成了 整数的拼写。哪一个, 什么是所有的总和 整数到那个点?
注意:例如,911,610,034是 书面 “ninehundredelevenmillionsixhundredtenthousandthirtyfour”; 写了500,000,000 “fivehundredmillion”;编写了1,709 “onethousandsevenhundrednine”。
我在编程博客'Occasionally Sane'上偶然发现了这一点,并且无法想到这样做的简洁方法,relevant post的作者说他最初的尝试是通过1.5GB的内存来进行的。 10分钟,他只赚了20,000,000(“二十亿”)。
任何人都能想到 想出 与小组分享一个新颖/聪明的方法吗?
答案 0 :(得分:22)
修改:已解决!
您可以创建一个按排序顺序输出数字的生成器。有一些规则用于比较连接字符串,我认为我们大多数人都隐含地知道:
如果您从前1000个数字的排序列表开始,您可以通过附加“千”或“百万”并连接另一组1000来轻松生成其余数据。
以下是Python中的完整代码:
import heapq
first_thousand=[('', 0), ('one', 1), ('two', 2), ('three', 3), ('four', 4),
('five', 5), ('six', 6), ('seven', 7), ('eight', 8),
('nine', 9), ('ten', 10), ('eleven', 11), ('twelve', 12),
('thirteen', 13), ('fourteen', 14), ('fifteen', 15),
('sixteen', 16), ('seventeen', 17), ('eighteen', 18),
('nineteen', 19)]
tens_name = (None, 'ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty',
'seventy','eighty','ninety')
for number in range(20, 100):
name = tens_name[number/10] + first_thousand[number%10][0]
first_thousand.append((name, number))
for number in range(100, 1000):
name = first_thousand[number/100][0] + 'hundred' + first_thousand[number%100][0]
first_thousand.append((name, number))
first_thousand.sort()
def make_sequence(base_generator, suffix, multiplier):
prefix_list = [(name+suffix, number*multiplier)
for name, number in first_thousand[1:]]
prefix_list.sort()
for prefix_name, base_number in prefix_list:
for name, number in base_generator():
yield prefix_name + name, base_number + number
return
def thousand_sequence():
for name, number in first_thousand:
yield name, number
return
def million_sequence():
return heapq.merge(first_thousand,
make_sequence(thousand_sequence, 'thousand', 1000))
def billion_sequence():
return heapq.merge(million_sequence(),
make_sequence(million_sequence, 'million', 1000000))
def solve(stopping_size = 51000000000):
total_chars = 0
total_sum = 0
for name, number in billion_sequence():
total_chars += len(name)
total_sum += number
if total_chars >= stopping_size:
break
return total_chars, total_sum, name, number
运行需要一段时间,大约一个小时。第五十五个字是最后一个字符,六百七十六百七十万五千五十五十五,并且这一点的整数之和为413,540,008,163,475,743。
答案 1 :(得分:15)
我会对前20个整数的名称和数十,数百和数千的名称进行排序,计算出每个数字的起始数,并从那里开始。
例如,前几个是[ eight, eighteen, eighthundred, eightmillion, eightthousand, eighty, eleven, ...
。
以“8”开头的数字是8.使用“eighthundred”,800-899,800,000-899,999,800,000,000-899,999,999。等等。
可以找到并总计0(由空字符串表示)到99的单词串联中的字母数;这可以乘以“千”= 8或“百万”= 7加入更高的范围。 800-899的值将是“eighthundred”长度的100倍加上0-99的长度。等等。
答案 2 :(得分:10)
This guy解决了Haskell编写的难题。显然Michael Borgwardt使用Trie找到解决方案是正确的。
答案 3 :(得分:9)
这些字符串将有很多很多共同的前缀 - trie的完美用例,这将大大减少内存使用量,也可能还有运行时间。
答案 4 :(得分:3)
这是我的python解决方案,可在几分之一秒内打印出正确的答案。我一般不是python程序员,所以对任何令人发指的代码样式错误表示道歉。
#!/usr/bin/env python
import sys
ONES=[
"", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve", "thirteen", "fourteen",
"fifteen", "sixteen", "seventeen","eighteen", "nineteen",
]
TENS=[
"zero", "ten", "twenty", "thirty", "forty",
"fifty", "sixty", "seventy", "eighty", "ninety",
]
def to_s_h(i):
if(i<20):
return(ONES[i])
return(TENS[i/10] + ONES[i%10])
def to_s_t(i):
if(i<100):
return(to_s_h(i))
return(ONES[i/100] + "hundred" + to_s_h(i%100))
def to_s_m(i):
if(i<1000):
return(to_s_t(i))
return(to_s_t(i/1000) + "thousand" + to_s_t(i%1000))
def to_s_b(i):
if(i<1000000):
return(to_s_m(i))
return(to_s_m(i/1000000) + "million" + to_s_m(i%1000000))
def try_string(s,t):
global letters_to_go,word_sum
l=len(s)
letters_to_go -= l
word_sum += t
if(letters_to_go == 0):
print "solved: " + s
print "sum is: " + str(word_sum)
sys.exit(0)
elif(letters_to_go < 0):
print "failed: " + s + " " + str(letters_to_go)
sys.exit(-1)
def solve(depth,prefix,prefix_num):
global millions,thousands,ones,letters_to_go,onelen,thousandlen,word_sum
src=[ millions,thousands,ones ][depth]
for x in src:
num=prefix + x[2]
nn=prefix_num+x[1]
try_string(num,nn)
if(x[0] == 0):
continue
if(x[0] == 1):
stl=(len(num) * 999) + onelen
ss=(nn*999) + onesum
else:
stl=(len(num) * 999999) + thousandlen + onelen*999
ss=(nn*999999) + thousandsum
if(stl < letters_to_go):
letters_to_go -= stl
word_sum += ss
else:
solve(depth+1,num,nn)
ones=[]
thousands=[]
millions=[]
onelen=0
thousandlen=0
onesum=(999*1000)/2
thousandsum=(999999*1000000)/2
for x in range(1,1000):
s=to_s_b(x)
l=len(s)
ones.append( (0,x,s) )
onelen += l
thousands.append( (0,x,s) )
thousands.append( (1,x*1000,s + "thousand") )
thousandlen += l + (l+len("thousand"))*1000
millions.append( (0,x,s) )
millions.append( (1,x*1000,s + "thousand") )
millions.append( (2,x*1000000,s + "million") )
ones.sort(key=lambda x: x[2])
thousands.sort(key=lambda x: x[2])
millions.sort(key=lambda x: x[2])
letters_to_go=51000000000
word_sum=0
solve(0,"",0)
它的工作原理是预先计算1..999和1..999999中数字的长度,这样它就可以跳过整个子树,除非它知道答案位于其中的某个位置。
答案 5 :(得分:3)
(对此的第一次尝试是错误的,但我会放弃它,因为在解决问题的方式上看错会更有用,而不仅仅是最终答案。)
我首先生成0到999之间的字符串,然后将它们存储到名为thousandsStrings的数组中。 0元素为“”,“”表示下面列表中的空白。
thousandsString设置使用以下内容:
Units: "" one two three ... nine
Teens: ten eleven twelve ... nineteen
Tens: "" "" twenty thirty forty ... ninety
千万字符串设置是这样的:
thousandsString[0] = ""
for (i in 1..10)
thousandsString[i] = Units[i]
end
for (i in 10..19)
thousandsString[i] = Teens[i]
end
for (i in 20..99)
thousandsString[i] = Tens[i/10] + Units[i%10]
end
for (i in 100..999)
thousandsString[i] = Units[i/100] + "hundred" + thousandsString[i%100]
end
然后,我会按字母顺序对该数组进行排序。
然后,假设t1 t2 t3是从thousandsString中取出的字符串,所有字符串都具有
形式T1
OR
t1 +百万+ t2 +千+ t3
OR
t1 +千+ t2
要以正确的顺序输出它们,我会处理单个字符串,然后是数百万个字符串,后跟字符串+数千个字符串。
foreach (t1 in thousandsStrings)
if (t1 == "")
continue;
process(t1)
foreach (t2 in thousandsStrings)
foreach (t3 in thousandsStrings)
process (t1 + "million" + t2 + "thousand" + t3)
end
end
foreach (t2 in thousandsStrings)
process (t1 + "thousand" + t2)
end
end
其中,过程表示存储前一个总和长度,然后将新的字符串长度添加到总和中,如果新总和> =您的目标总和,则吐出结果,并可能返回或中断循环,无论什么让你开心
=============================================== ======================
第二次尝试,其他答案是正确的,你需要使用3k字符串而不是1k字符串作为基础。
从上面开始使用thousandsString,但是将空白“”删除为零。这留下了999个元素,并将其称为uStr(单位字符串)。
再创建两组:
tStr = the set of all uStr + "thousand"
mStr = the set of all uStr + "million"
现在再创建两个设置联合:
mtuStr = mStr union tStr union uStr
tuStr = tStr union uStr
订购uStr,tuStr,mtuStr
现在这里的循环和逻辑与以前有点不同。
foreach (s1 in mtuStr)
process(s1)
// If this is a millions or thousands string, add the extra strings that can
// go after the millions or thousands parts.
if (s1.contains("million"))
foreach (s2 in tuStr)
process (s1+s2)
if (s2.contains("thousand"))
foreach (s3 in uStr)
process (s1+s2+s3)
end
end
end
end
if (s1.contains("thousand"))
foreach (s2 in uStr)
process (s1+s2)
end
end
end
答案 6 :(得分:2)
我做了什么: 1)迭代1 - 999并为每个单词生成单词。 正如我们生成: 2)创建3个数据结构,其中每个节点都有一个指向子节点的指针,每个节点都有一个字符值,以及一个指向兄弟姐妹的指针。 (事实上,这是一棵二叉树,但是我们不想这么想它 - 对我而言,更容易将其概念化为一个兄弟姐妹的列表,其中有一些儿童名单悬而未决,但是如果你想到这一点{画一张照片你会意识到它实际上是一棵二叉树)。 这3个数据结构并行创建如下: a)第一个生成单词(即1-999按字母顺序排序) b)第一个+所有值中的所有值,附加“千”(即1-999和1,000 - 999,000(步骤1000)(1998年总值) c)B +中的所有值以及附加百万的所有值(总共2997个值) 3)对于(b)中的每个叶节点,将一个Child添加为(a)。对于(c)中的每个叶节点,将子节点添加为(b)。 4)遍历树,计算我们通过的字符数和停止在51亿字。
注意:这不会对这些值求和(我最初做的时候没有读过那个位),并且运行时间超过3分钟(通常使用c ++约为192秒)。 注2 :(如果不是很明显),只存储了5,994个值,但它们的存储方式是树中有十亿个路径
我在大约一年或两年前做过这件事,当我偶然发现它时,已经意识到有很多优化(最耗时的一点是穿越树 - 通过一条长路)。我认为有一些优化可以显着改善这种方法,但除了稍微优化树中的冗余节点之外,我永远不会费心去做,所以它们存储字符串而不是字符
我看到人们在网上声称他们已经在不到5秒的时间内解决了它......
答案 7 :(得分:1)
是的,我再次,但是一种完全不同的方法。
简单地说,不是存储“onethousandeleventyseven”字样,而是在比较时编写要使用的排序。
Crude java POC:
public class BillionsBillions implements Comparator {
public int compare(Object a, Object b) {
String s1 = (String)a; // "1234";
String s2 = (String)b; // "1235";
int n1 = Integer.valueOf(s1);
int n2 = Integer.valueOf(s2);
String w1 = numberToWords(n1);
String w2 = numberToWords(n2);
return w1.compare(w2);
}
public static void main(String args[]) {
long numbers[] = new long[1000000000]; // Bring your 64 bit JVMs
for(int i = 0; i < 1000000000; i++) {
numbers[i] = i;
}
Arrays.sort(numbers, 0, numbers.length, new BillionsBillions());
long total = 0;
for(int i : numbers) {
String l = numberToWords(i);
long offset = total + l - 51000000000;
if (offset >= 0) {
String c = l.substring(l - offset, l - offset + 1);
System.out.println(c);
break;
}
}
}
}
答案 8 :(得分:1)
奇怪但有趣的想法。
建立一个稀疏的数字长度列表,从0到9,然后是10-90乘几十,然后是100,1000等,到十亿,索引是存储长度的整数部分的值。 / p>
使用表格编写一个函数来计算数字作为字符串长度。 (将数字分成它的部分,并查看aprts的长度,从不动作创建字符串。)
然后你只是在遍历数字时进行数学计算,从中计算长度 表后来总结你的总和。
用和,以及最终整数的值,计算出拼写的整数,以及volia,你已经完成了。
答案 9 :(得分:0)
代码获胜......
#!/bin/bash
n=0
while [ $n -lt 1000000000 ]; do
number -l $n | sed -e 's/[^a-z]//g'
let n=n+1
done | sort > /tmp/bignumbers
awk '
BEGIN {
total = 0;
}
{
l = length($0);
offset = total + l - 51000000000;
print total " " offset
if (offset >= 0) {
c = substr($0, l - offset, 1);
print c;
exit;
}
total = total + l;
}' /tmp/bignumbers
测试范围小得多;-)。需要大量的磁盘空间,压缩的文件系统,嗯,有价值,但没有那么多内存。
Sort也有压缩工作文件的选项,你可以投入gzip来直接压缩数据。
不是最简单的解决方案。
但确实有效。
答案 10 :(得分:0)
你有10亿个数字和510亿个字符 - 很有可能这是一个技巧问题,因为每个数字平均有51个字符。总结所有数字的转换,看看它是否总计达到510亿。
编辑:它最多可添加70,305,000,000个字符,因此这是错误的答案。
答案 11 :(得分:0)
计算1-999的长度,并将0的长度包括为0。
所以现在你有一个0-999的数组,即uint32 sizes999 [1000]; (不打算详细介绍这个) 还需要一个最后一千个字母的数组last_letters [1000] (再次没有深入了解产生这一点的细节,因为它甚至更容易甚至数百甚至数十年,除了10,其他n个循环虽然最后一个 e 通过nin e 强>零是无关紧要的)
uint32 sizes999[1000];
uint64 totallen = 0;
strlen_million = strlen("million");
strlen_thousand = strlen("thousand");
for (uint32 i = 0; i<1000;++i){
for (uint32 j = 0; j<1000;++j){
for (uint32 j = 0; j<1000;++j){
total_len += sizes999[i]+strlen_million +
sizes999[j]+strlen_thousand +
sizes999[k];
if totallen == 51000000000 goto done;
ASSERT(totallen <51000000000);//he claimed 51000000000 was not intermediate
}
}
}
done:
//现在使用i j k通过last_letters999获取最后一个字母
//将i,j,k视为数字基数1000
//如果k = 0&amp; j == 0那么字母是n millio n
//如果只有k = 0那么这封信就是d thousan d
//其他方面使用last_letters数组,因为
//单位数字基数1000,即k,不为零
//对于数字i的总和,i,j,k是数字基数1000的数字,所以
n = i*1000000 + j*1000 + k;
//代表数字并使用
sum = n*(n+1)/2;
如果您需要为51000000000以外的数字执行此操作,那么还要计算sums_sizes999并以自然方式使用它。
总记忆:0(1000);
总时间:0(n)其中n是数字
答案 12 :(得分:0)
这就是我要做的事情:
创建此数组后,可以根据以下观察结果按字母顺序查找所有999,999,999个字符串:
构造单词主要涉及基于这些2,997个标记创建一到三个字母的“单词”,确保标记的顺序根据上述规则生成有效数字。给定一个特定的“单词”,下一个“单词”就像这样找到:
在每一步中,您只需保留两个运行总计即可计算字符串的总长度和数字的总和。
答案 13 :(得分:0)
重要的是要注意,如果迭代所有1000亿个可能的数字,会有很多重叠和重复计算。重要的是要认识到以“8”开头的字符串数量与以“nin”或“7”或“6”等开头的数字相同......
对我而言,这需要一个动态编程解决方案,其中计算数十,数百,数千等字符串的数量并存储在某种类型的查找表中。当然,对于一对十一,二对十二等特殊情况
如果我能获得快速运行的解决方案,我会更新。
答案 14 :(得分:0)
WRONG !!!!!!!!!我读错了。我认为这意味着“字母顺序最后一个数字的最后一个字母是什么”
出了什么问题:
public class Nums {
// if overflows happen, switch to an BigDecimal or something
// with arbitrary precision
public static void main(String[] args) {
System.out.println("last letter: " + lastLetter(1L, 51000000L);
System.out.println("sum: " + sum(1L, 51000000L);
}
static char lastLetter(long start, long end) {
String last = toWord(start);
for(long i = start; i < end; i++)
String current = toWord(i);
if(current.compareTo(last) > 1)
last = current;
return last.charAt(last.length()-1);
}
static String toWord(long num) {
// should be relatively easy, but for now ...
return "one";
}
static long sum(long first, long n) {
return (n * first + n*n) / 2;
}
}
实际上没有尝试过:/ LOL
答案 15 :(得分:0)
老实说,我会让像SQL Server或Oracle这样的RDBMS为我工作。
可能需要花费一段时间才能打开服务器,因为它需要执行大量的磁盘IO,但总的来说,我认为我能找到一个比编写程序的人更快的答案。
有时只需完成它就是客户真正想要的东西,并且可以更少关注您使用的花哨的设计模式或数据结构。
答案 16 :(得分:0)
问题是有效的数据存储而非字符串操作。创建一个枚举来表示单词。单词应该按排序顺序出现,以便在排序时,它是一个简单的比较。现在生成列表并排序。使用这样一个事实,即您知道每个单词与枚举结合多长时间,以便加起来所需的字符。
答案 17 :(得分:0)
所有字符串都将从一个,十个,两个,二十,三个,三十个,四个等开始,所以我首先要弄清楚每个桶中有多少个字符串。那你至少应该知道你需要仔细观察哪个桶。
然后我会根据可能的前缀进一步查看细分桶。例如,在九百之内,您将拥有所有相同的桶,只需要从900开始的数字。
答案 18 :(得分:0)
我在2008年的某个时候用Java解决了这个问题,作为在ITA Software工作的应用程序的一部分。
代码很长,现在已经三年了,我看起来有点恐怖......所以我不会发布它。
但是我会在应用程序中包含的一些注释中发布引用。
这个难题的问题当然是大小。天真的方法是按字数顺序对列表进行排序,然后迭代计算字符和求和的排序列表。对于大小为999,999,999的列表,这当然需要相当长的时间,并且可能无法在内存中进行排序。
但是在排序中有自然模式允许快捷方式。
在以“百万”结尾的任何条目(比如数字为X)之后,将立即以相同文本开头的999,999个条目,表示来自X +1的所有数字 到X + 10 ^ 6 -1。
所有这些数字的总和可以通过经典公式(“算术系列”)计算,并且字符计数可以通过基于前缀(上面的X)和一次计算的字符的类似简单公式来计算计算从1到999,999的数字。两者都只依赖于范围底部数字的“数百万”部分。因此,如果整个范围的字符数将使整个计数保持在搜索目标之下,则无需遍历各个条目。
类似的快捷方式适用于“千”,实际上可以应用于“百”或“十亿”,虽然我没有在数百级别的快捷方式,数十亿水平超出这个问题的范围。
为了应用这些快捷方式,我的代码会创建并排序表示数字的2997个对象的列表:
1到999步进1 1000到999000步进1000 1000000到999000000步进1000000
代码遍历此列表,累积总和和字符数,根据需要递归创建,排序和遍历类似但更小的列表。
只需在结束时进行显式计数和添加。
我没有得到这份工作,但后来将这些代码用作另一份工作的“代码示例”,我确实得到了。
使用这些技术的Java代码可以在大约8秒内跳过大部分显式计数和添加运行。
答案 19 :(得分:0)
您需要将整个字符串保存在内存中吗?
如果没有,只需保存到目前为止已添加的字符数。对于每次迭代,您检查下一个数字的文本表示的长度。如果它超过了您要查找的第n个字母,则该字母必须在该字符串中,因此请通过索引提取,打印并停止执行。否则,将字符串长度添加到字符数,然后移动到下一个数字。