我正在学习算法设计和分析课程,并且给出了编程问题,这是两个总和问题的变体:
输入是一个包含100万个整数的数组,包括正数和负数。 计算区间[-10000,10000](包括)中的目标值t的数量,使得输入文件中存在满足x + y = t的不同数字x,y。
我已经在目标C中编写了一个解决方案,可以针对较小的测试用例正确解决问题:
+ (BOOL)pairExistsForSum:(NSInteger)t dictionary:(NSDictionary *)dictionary
{
__block BOOL exists = NO;
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSNumber *x, BOOL *stop) {
NSInteger y = t - x.integerValue;
NSString *yKey = [NSString stringWithFormat:@"%li", y];
if (y != x.integerValue && dictionary[yKey]) {
exists = YES;
*stop = YES;
}
}];
return exists;
}
+ (NSInteger)twoSumProblem:(NSArray <NSNumber *> *)array interval:(NSInteger)min max:(NSInteger)max
{
NSDictionary *dictionary = [self generateDictionaryOfValuesWithNumbersArray:array];
NSInteger uniquePairs = 0;
for (NSInteger i = min; i <= max; i++) {
uniquePairs += [self pairExistsForSum:i dictionary:dictionary];
}
return uniquePairs;
}
问题是pairExistsForSum
的每次迭代都需要2秒多一点才能完成,这意味着整个过程需要数小时才能完成。
我尝试了一些替代方法,例如:
1)对输入进行排序并将其分为正负数组并使用二进制搜索找到免费加数
2)将外部for循环更改为仅遍历范围0 - 10000,然后同时搜索正负和值的加数
没有什么能够显着提高性能,甚至没有将其分解为子问题并在并发线程上运行每个问题。
我终于找到了某人的python解决方案,如下所示:
import time
import bisect
a = []
with open('2sum.txt', 'r') as f:
for line in f:
a.append(int(line.strip()))
a.sort()
ret = set()
for x in a:
lower = bisect.bisect_left(a, -10000 - x)
upper = bisect.bisect_right(a, 10000 - x)
for y in a[lower:upper]:
if x != y and x + y not in ret:
ret.add(x + y)
print len(ret)
此解决方案可在几秒或更短的时间内运行。我不熟悉Python,但我相信这是使用二进制搜索而不是利用输入数组的数据来提高速度。虽然我希望python代码比Objective C运行得更快,但这些解决方案之间的差异是巨大的。
我的问题如下:
(有人在这里问了同样的问题:Variant of the 2-sum algorithm with a range of sums但是没有给出答案,我相信我的答案更具体。)
非常感谢。
答案 0 :(得分:2)
我是否对这两种解决方案之间的差异缺失了什么能够解释如此巨大的性能差异?
你正在解决问题&#34;倒退&#34;。您从 t 开始,然后搜索与其相加的一对。想想输入中只包含两个数字的极端例子,您将执行200001次测试以查看总和是否为[-100000,100000]范围内的可能值之一。
选择 x 和 y 来驱动Python,因此只考虑数据可以生成的实际 t 值。此外,通过对数据进行排序,解决方案只能考虑那些 x , y 对,它们总和为范围内的值。
在目标c中,有什么我可以忽视的事情,以便在相当长的时间内(即一小时内)运行吗?
是的,只需实现与Python解决方案相同的算法。一个快速的谷歌将产生bisect函数的规范和他们的Python源。您会发现它们是简单的二进制搜索,您可以轻松实现。但是对于速度,您可能希望尝试使用标准的Objective-C方法。 DecimalFormatSymbols symbolsEN_US = DecimalFormatSymbols.getInstance(Locale.US);
DecimalFormat formatEN_US = new DecimalFormat("#,###.#", symbolsEN_US);
System.out.println(formatEN_US.format(-123456.789)); // prints -123,456.8
DecimalFormatSymbols symbolsDE_DE = DecimalFormatSymbols.getInstance(Locale.GERMANY);
DecimalFormat formatDE_DE = new DecimalFormat("#,###.#", symbolsDE_DE);
System.out.println(formatDE_DE.format(-123456.789)); // prints -123.456,8
DecimalFormatSymbols symbolsFR_FR = DecimalFormatSymbols.getInstance(Locale.FRANCE);
DecimalFormat formatFR_FR = new DecimalFormat("#,###.#", symbolsFR_FR);
System.out.println(formatFR_FR.format(-123456.789)); // prints -123 456,8
DecimalFormatSymbols symbolsIT_CH = DecimalFormatSymbols.getInstance(Locale.forLanguageTag("it-CH"));
DecimalFormat formatIT_CH = new DecimalFormat("#,###.#", symbolsIT_CH);
System.out.println(formatIT_CH.format(-123456.789)); // prints -123'456.8
DecimalFormatSymbols symbolsHI_IN = DecimalFormatSymbols.getInstance(Locale.forLanguageTag("hi-IN"));
DecimalFormat formatHI_IN = new DecimalFormat("#,###.#", symbolsHI_IN);
System.out.println(formatHI_IN.format(-123456.789)); // prints -१२३,४५६.८
DecimalFormatSymbols symbolsTHAI = DecimalFormatSymbols.getInstance(Locale.forLanguageTag("th-TH-u-nu-thai-x-lvariant-TH"));
DecimalFormat formatTHAI = new DecimalFormat("#,###.#", symbolsTHAI);
System.out.println(formatTHAI.format(-123456.789)); // prints -๑๒๓,๔๕๖.๘
没有直接的等价物,但请查看NSArray
并稍微考虑一下&#34;滥用&#34;比较器对等值的定义......
HTH