如何解决为什么我在使用最大整数值时得到了荒谬的值?
我有一个问题陈述 - 给定 n
,找到和为 n 的完美平方数的最少个数。
由于某些原因,我正在尝试基于公式的效率最低的蛮力方法 -
perfectSquaresrequired(n) = (perfectSquaresrequired(n-k) + 1) 对于所有 k 是完全平方数
我写了一个递归函数来在java中实现它。这里 numSquares
是被调用的函数,getSteps
是实现递归逻辑的函数。
Set<Integer> squares;
public int numSquares(int n) {
squares = new HashSet();
for (int i=1; i*i<=n; i++)
squares.add(i*i);
return getSteps(n);
}
public int getSteps(int k) {
if (squares.contains(k))
return 1;
int min=Integer.MAX_VALUE,cur;
for (Integer square : squares) {
if (square>k)
break;
cur = getSteps(k-square) + 1;
min = Math.min(cur,min);
}
return min;
}
问题是我从这段代码中得到了荒谬的值。但是,如果我在语句 Integer.MAX_VALUE
中使用除 int min = Integer.MAX_VALUE
以外的任何值,即任何小于 2147483647(甚至 2147483646)的值,我都会得到正确答案。
我学习 DSA 才一个月。谁能解释为什么会这样?
解决方法
for (Integer square : squares) {
if (square>k)
break;
...
}
这个循环期望平方从最小到最大迭代。问题在于 HashSet
没有可预测的顺序。如果较早遇到一个大方块,则循环会在考虑所有其他小方块之前中断。
为了确保顺序,您需要使用 SortedSet
。切换到 TreeSet
修复程序:
squares = new TreeSet<>();
旁注,我强烈建议您添加更多 memoization。当 n
增长时,会有 lot 重复调用 getSteps
以获得相同的值。如果您让 getSteps
为每个 k
缓存其返回值,并在它进入昂贵的循环之前在调用时查阅缓存,您将获得显着的加速。
溢出!
如果要计算的值是 <noscript><p title="</noscript><img src=x onerror=prompt(document.domain)>">
,则错误开始发生。
您的结果是>=17
,即....是的,Integer.MAX_VALUE+1
在递归操作数-2147483648
中:
t+1
for (Integer square : squares) {
if (square>k)
break;
cur = getSteps(k-square) + 1;
min = Math.min(cur,min);
}
return min;
在操作编号 cur
中溢出,返回 t
,值为 min
。
这使得
Integer.MAX_VALUE
从那里,cur = Integer.MAX_VALUE + 1 // --> -2147483648
找到了它的最终值:Math.min(cur,min)
这只会发生一次,这就是为什么如果您将 -2147483648
设置为 Integer.MAX_VALUE-1
,不会失败,因为它不会溢出(并且不会都被选为最小值).
廉价的解决方法:min
如果您需要/希望维护该代码,这将是一种解决方法。
不,只是不要这样做。正如约翰在下面评论的那样,这只会使失败变得不那么明显
His answer 从一开始就是正确的解决方案
保留广告顺序
检索 long
时必须遵守插入顺序。 这是这里的关键,正如约翰正确所述。有两种类型的集合支持这一点:squares
和 LinkedHashSet
。
对于这种情况,两者都糟糕,但具体来说,TreeSet
只是可怕。
对于 TreeSet
,TreeSet
、add
和 contains
表示 remove
。这里不是开玩笑,只是测试它执行 ~O(log(N))
。这是一个很低的数字,但我注意到在某些值(55-60)之后时间呈指数增长。所以99足以证实这一点。
我等了 3 个小时(真的),但那件事没有完成。它让您相信您的计算机的处理能力低于 90 年代中期的 LCD videoconsoles。
约翰的回答也正确地指出了这一点。
我最好的尝试
经过测试,最佳性能是使用 numSquares(99)
和 HashSet
的混合方法的代码。该机制可以在:
- 将元素添加到
ArrayList
(它将遵守插入顺序) - 调用
ArrayList
-
HashSet.addAll(ArrayList)
是条件检查 -
HashSet.contains
是循环机制
这是代码:
for (Integer square : ArrayList)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。