我对Python中的算法的this small example感兴趣,以便循环一个大的单词列表。我正在写一些“工具”,这将允许我以与Python类似的方式切片Objective-C字符串或数组。
具体来说,this elegant solution引起了我的注意,非常快速地执行,它使用字符串切片作为算法的关键元素。尝试解决这个没有切片!
我使用下面的Moby word list复制了我的本地版本。如果您不想下载Moby,可以使用/usr/share/dict/words
。源只是一个类似于字典的大型独特单词列表。
#!/usr/bin/env python
count=0
words = set(line.strip() for line in
open("/Users/andrew/Downloads/Moby/mwords/354984si.ngl"))
for w in words:
even, odd = w[::2], w[1::2]
if even in words and odd in words:
count+=1
print count
这个脚本将a)由Python解释; b)读取4.1 MB,354,983字的Moby字典文件; c)剥去线条; d)将线条放入一组,并且; e)并找到所有组合,其中平均值和给定单词的几率也是单词。这在MacBook Pro上执行约0.73秒。
我试图在Objective-C中重写相同的程序。我是这种语言的初学者,所以请轻松一下,但请指出错误。
#import <Foundation/Foundation.h>
NSString *sliceString(NSString *inString, NSUInteger start, NSUInteger stop,
NSUInteger step){
NSUInteger strLength = [inString length];
if(stop > strLength) {
stop = strLength;
}
if(start > strLength) {
start = strLength;
}
NSUInteger capacity = (stop-start)/step;
NSMutableString *rtr=[NSMutableString stringWithCapacity:capacity];
for(NSUInteger i=start; i < stop; i+=step){
[rtr appendFormat:@"%c",[inString characterAtIndex:i]];
}
return rtr;
}
NSSet * getDictWords(NSString *path){
NSError *error = nil;
NSString *words = [[NSString alloc] initWithContentsOfFile:path
encoding:NSUTF8StringEncoding error:&error];
NSCharacterSet *sep=[NSCharacterSet newlineCharacterSet];
NSPredicate *noEmptyStrings =
[NSPredicate predicateWithFormat:@"SELF != ''"];
if (words == nil) {
// deal with error ...
}
// ...
NSArray *temp=[words componentsSeparatedByCharactersInSet:sep];
NSArray *lines =
[temp filteredArrayUsingPredicate:noEmptyStrings];
NSSet *rtr=[NSSet setWithArray:lines];
NSLog(@"lines: %lul, word set: %lul",[lines count],[rtr count]);
[words release];
return rtr;
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int count=0;
NSSet *dict =
getDictWords(@"/Users/andrew/Downloads/Moby/mwords/354984si.ngl");
NSLog(@"Start");
for(NSString *element in dict){
NSString *odd_char=sliceString(element, 1,[element length], 2);
NSString *even_char=sliceString(element, 0, [element length], 2);
if([dict member:even_char] && [dict member:odd_char]){
count++;
}
}
NSLog(@"count=%i",count);
[pool drain];
return 0;
}
Objective-C版本产生相同的结果(13,341个单词),但需要将近3秒钟才能完成。我必须做一些非常错误的错误,因为编译语言比脚本语言慢了3倍,但如果我能理解为什么,我会感到很沮丧。
基本算法是相同的:读取线条,剥离它们,然后将它们放入一组中。
我对NSString元素处理速度慢的猜测,但我不知道另一种选择。
修改
我将Python编辑为:
#!/usr/bin/env python
import codecs
count=0
words = set(line.strip() for line in
codecs.open("/Users/andrew/Downloads/Moby/mwords/354984si.ngl",
encoding='utf-8'))
for w in words:
if w[::2] in words and w[1::2] in words:
count+=1
print count
使utf-8与utf-8 NSString在同一平面上。这使Python的速度降低到1.9秒。
对于Python和obj-c版本,我还将切片测试切换为suggested的短路类型。现在他们接近相同的速度。我也尝试使用C数组而不是NSStrings,这要快得多,但并不容易。你也可以放弃utf-8支持。
Python非常酷......
修改2
我发现了一个瓶颈,大大加快了速度。我没有使用[rtr appendFormat:@"%c",[inString characterAtIndex:i]];
方法将字符附加到返回字符串,而是使用了这个:
for(NSUInteger i=start; i < stop; i+=step){
buf[0]=[inString characterAtIndex:i];
[rtr appendString:[NSString stringWithCharacters:buf length:1]];
}
现在我可以最终声称Objective-C版本比Python版本更快 - 但不是很多。
答案 0 :(得分:9)
请记住,Python版本已被编写为在CPython上执行时将大量繁重工作转移到高度优化的C代码中(特别是文件输入缓冲,字符串切片和哈希表查找以检查是否{{ 1}}和even
位于odd
)。
也就是说,您似乎在Objective-C代码中将文件解码为UTF-8,但在Python代码中将文件保留为二进制文件。在Objective-C版本中使用Unicode NSString,但是Python版本中的8位字节字符串实际上并不是一个公平的比较 - 如果您使用words
打开,我会期望Python版本的性能显着下降声明编码为codecs.open()
的文件。
您还要进行完整的第二遍以剥离Objective-C中的空行,而Python代码中不存在此类步骤。
答案 1 :(得分:2)
在BOTH代码中,您构建偶数和奇数切片,然后根据单词测试它们。如果你在偶数切片成功后才构建奇数切片会更好。
当前的Python代码:
even, odd = w[::2], w[1::2]
if even in words and odd in words:
更好:
# even, odd = w[::2], w[1::2]
if w[::2] in words and w[1::2] in words:
顺便说一下,你没有提到一个指标:你需要多长时间来编写每个代码?
答案 2 :(得分:2)
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSSet_Class/Reference/Reference.html表示您可能希望将NSSet替换为CFSet,这可能会提高性能。
我无法通过快速Google搜索找到用于NSSet / CFSet的实现:如果他们使用基于树的实现(与stdc ++集类型相同),则查找和检查是O(log (N))而Python的集合查找O(1),这个可以说明速度差异。
[编辑] ncoghlan在下面的一条评论中指出,目标C中的集合使用哈希表,因此您也可以获得一个恒定的时间查找。因此,这归结为集合的实现在Objective C中的效率与在Objective C中的效率有关。正如其他人指出的那样,Python的集合和字典都经过了大量优化,尤其是。当字符串用作键时(Python字典用于实现名称空间并且需要非常快)。
答案 3 :(得分:1)
您的python代码主要在构建数据结构中运行,这些结构在C中实现.Python包含对这些数据类型的极其复杂的优化。有关更多详情,请查看Raymond Hettinger的演讲。它主要是关于对象的非常有效的散列,使用那些散列进行查找,特殊的内存分配策略,......
我在python中实现了一个网络搜索只是为了测试,我们从来没有能够用C ++,C#或类似语言加速它。不是C ++或C#的初学者! ; - )
答案 4 :(得分:0)
首先,CPython的库是用C语言编写的,并且经过高度优化,因此利用该库的程序比未经优化的Objective C运行得更快也就不足为奇了。
如果将Objective C程序逐行转换为Python,结果会有所不同。
我怀疑大部分结果来自于计数器不经常递增的事实,并且Python决定对象不在集合中是非常有效的。毕竟,如果你拿一个单词的每一个字母,你似乎不太可能最终得到一个有效的英文单词,更不用说同一个源文本中的一个。