3 回答

TA貢獻1827條經驗 獲得超4個贊
請注意,使用BigInteger意味著數字以及兩個數字之間的差異可能會很大。從字面上看,從下邊界到上邊界循環可能需要很長時間。相反,您可以使用封閉形式的變體sum(1..N) = (N*(N+1))/2。1使用它對 from toupper和 from 1to 的數字求和lower,然后將兩者結合起來以獲得您想要的結果。
BigInteger lower = new BigInteger("5");
BigInteger upper = new BigInteger("8");
BigInteger one = BigInteger.ONE, two = BigInteger.TWO;
BigInteger oneToUpper = upper.multiply(upper.add(one)).divide(two);
BigInteger oneToLower = lower.multiply(lower.add(one)).divide(two);
BigInteger lowertoUpperInc = oneToUpper.subtract(oneToLower).add(lower);
System.out.println(lowertoUpperInc); // 5 + 6 + 7 + 8 = 26
BigInteger lowertoUpperExc = oneToUpper.subtract(oneToLower).subtract(upper);
System.out.println(lowertoUpperExc); // 6 + 7 = 13
(請注意,您的循環似乎也返回18了這個示例,這似乎是您真正想要的5+6+7,因此不是您真正想要的。)
除了循環之外,這也適用于true BigInteger,例如 和 的總和(包含和排除)分別為lower = 123456789123456789和。upper = 987654321987654321480109740480109740075445815075445815480109740480109738964334703964334705

TA貢獻1859條經驗 獲得超6個贊
正如另一個答案中已經提到的:像這樣的電話
total.add(startingBoundary);
沒有任何可觀察到的影響。該add方法不會修改對象total。相反,它返回一個新 BigInteger對象。更一般地說,其原因是BigInteger不可變的。這意味著對象的值BigInteger在創建后就無法更改。至于原因,可以看一下為什么java中的BigInteger被設計成不可變的?
將行更改為
total = total.add(startingBoundary);
將解決這個問題(同樣,對于其他行 - 對于固定實現,請參見下面的示例)。
旁注:您通常應該使用and來代替new BigInteger("0")and 。沒有理由為這些常用值創建新對象。new BigInteger("1")BigInteger.ZEROBigInteger.ONE
不過,可能的改進是:
除非作業明確指出必須用循環來解決這個問題,否則有一個更高效、更優雅的解決方案。您可以使用Gau?'sche Summenformel(抱歉,沒有英文版本),它基本上指出:
1到n的自然數之和等于(n*(n+1))/2
因此,您可以在范圍的限制下直接計算這些總和,然后返回兩者之間的差值。
以下包含原始代碼的固定版本和替代實現,以及(非?;镜模拔⒒鶞省保?/p>
import java.math.BigInteger;
import java.util.Locale;
public class SumFromRange
{
public static void main(String[] args)
{
simpleExample();
simpleBenchmark();
}
private static void simpleExample()
{
System.out.println(addNumbers("5", "8"));
System.out.println(addNumbersFast("5", "8"));
System.out.println(addNumbers("15", "78"));
System.out.println(addNumbersFast("15", "78"));
}
private static void simpleBenchmark()
{
int blackHole = 0;
for (long min = 10000; min <= 20000; min += 10000)
{
for (long max = 10000000; max <= 20000000; max += 10000000)
{
String from = String.valueOf(min);
String to = String.valueOf(max);
long before = 0;
long after = 0;
before = System.nanoTime();
BigInteger slow = addNumbers(from, to);
after = System.nanoTime();
blackHole += slow.hashCode();
System.out.printf("Compute %10d to %10d slow took %8.3f ms\n",
min, max, (after - before) / 1e6);
before = System.nanoTime();
BigInteger fast = addNumbersFast(from, to);
after = System.nanoTime();
blackHole += fast.hashCode();
System.out.printf(Locale.ENGLISH,
"Compute %10d to %10d fast took %8.3f ms\n", min, max,
(after - before) / 1e6);
}
}
System.out.println("blackHole " + blackHole);
}
public static BigInteger addNumbers(String from, String to)
{
BigInteger total = BigInteger.ZERO;
BigInteger startingBoundary = new BigInteger(from);
BigInteger finishingBoundary = new BigInteger(to);
if (startingBoundary.compareTo(finishingBoundary) < 0)
{
startingBoundary = new BigInteger(from);
finishingBoundary = new BigInteger(to);
}
else
{
finishingBoundary = new BigInteger(from);
startingBoundary = new BigInteger(to);
}
startingBoundary = startingBoundary.add(BigInteger.ONE);
while (startingBoundary.compareTo(finishingBoundary) != 0)
{
total = total.add(startingBoundary);
startingBoundary = startingBoundary.add(BigInteger.ONE);
}
return total;
}
public static BigInteger addNumbersFast(String from, String to)
{
BigInteger f = new BigInteger(from);
BigInteger t = new BigInteger(to);
BigInteger sf = computeSum(f);
BigInteger st = computeSum(t.subtract(BigInteger.ONE));
return st.subtract(sf);
}
// Compute the sum of 1...n
public static BigInteger computeSum(BigInteger n)
{
BigInteger n1 = n.add(BigInteger.ONE);
return n.multiply(n1).divide(BigInteger.valueOf(2));
}
}
較大值的基準結果是顯而易見的:
Compute 10000 to 10000000 slow took 635,506 ms
Compute 10000 to 10000000 fast took 0.089 ms
Compute 10000 to 20000000 slow took 1016,381 ms
Compute 10000 to 20000000 fast took 0.037 ms
Compute 20000 to 10000000 slow took 477,258 ms
Compute 20000 to 10000000 fast took 0.038 ms
Compute 20000 to 20000000 slow took 987,400 ms
Compute 20000 to 20000000 fast took 0.040 ms
這些根本就不是一個檔次的……

TA貢獻1835條經驗 獲得超7個贊
使用
total = total.add(startingBoundary);
和
startingBoundary = startingBoundary.add(new BigInteger("1"));
因為add并不與第一個操作數相加,而是返回總和。
另外,在開始循環之前,執行以下操作
startingBoundary = startingBoundary.add(new BigInteger("1"));
以滿足必須排除起始邊界的條件。
作為防御性編碼,不要使用等于零,而是使用
startingBoundary.compareTo(finishingBoundary) < 0
添加回答
舉報