[英]Fast way to check if long integer is a cube (in Java)
我正在編寫一個程序,在該程序中,我需要檢查某些大數(立方體的排列)是否為三次方(某些 n 等於 n^3)。
目前我只是使用該方法
static boolean isCube(long input) {
double cubeRoot = Math.pow(input,1.0/3.0);
return Math.round(cubeRoot) == cubeRoot;
}
但這在處理大數字(10 位以上)時非常慢。 有沒有更快的方法來確定整數是否是立方體?
只有 2^21 個立方體不會溢出很長時間(如果允許負數,則為 2^22 - 1),因此您可以只使用 HashSet 查找。
Hacker's Delight書有一個簡短而快速的整數立方根函數,值得移植到 64 位長整數,見下文。
似乎測試一個數字是否是完美立方體比實際計算立方根更快。 Burningmath 有一種使用“ 數字根”的技術(將數字相加。重復直到它是一個數字)。 如果數字根是 0、1 或 8,則您的數字可能是一個完美的立方體。
這種方法對於您排列(數字?)數字的情況可能非常有價值。 如果您可以通過數字根排除一個數字,則所有排列也將被排除。
他們還描述了一種基於檢驗完美立方體的主要因素的技術。 這看起來最適合心算,因為我認為因子分解比計算機上的立方根運算慢。
無論如何,數字根可以快速計算機化,您甚至可以將您的數字作為一串數字開始。 您仍然需要一個除以 10 的循環,但您的起點是輸入數字的總和,而不是整數,因此不會有很多除法。 (整數除法比當前 CPU 上的乘法慢大約一個數量級,但是可以優化除法以編譯時間常數, 以使用定點逆乘法+移位。希望 Java JIT 編譯器也使用它,也許甚至將它用於運行時常量。)
這加上A. Webb 的測試( input % 819
-> 搜索 45 個條目的表)將排除很多輸入,因為它不可能是完美的立方體。 IDK 如果二分搜索、線性搜索或散列/集合將是最好的。
這些測試可能是David Eisenstat 想法的前端,即在允許快速檢查是否存在的數據結構中存儲作為完美多維數據集的long
s 集。 (例如HashSet )。 是的,緩存未命中足夠昂貴,至少在進行 HashSet 查找之前進行數字根測試可能是值得的,也許兩者兼而有之。
您可以通過將它用於布隆過濾器而不是精確集( David Ehrman 的建議)來減少這個想法的內存。 這將為完整計算提供另一個候選拒絕前端。 guavac BloomFilter
實現需要一個“漏斗”函數來將對象轉換為字節,在這種情況下應該是 f(x)=x)。
我懷疑布隆過濾不會比精確的 HashSet 檢查大勝,因為它需要多次內存訪問。 當您真的負擔不起完整表的空間時,這是合適的,並且您要過濾掉的東西非常昂貴,例如磁盤訪問。
整數立方根函數(如下)可能比單個緩存未命中更快。 如果 cbrt 檢查導致緩存未命中,那么當其數據被逐出時,您的代碼的其余部分可能也會遭受更多的緩存未命中。
Math.SE 有一個關於完美正方形的問題,但那是關於正方形,而不是立方體,所以這些都沒有出現。 不過,那里的答案確實討論並避免了您方法中的問題。 >.<
你的方法有幾個問題:
使用pow(x, 1./3)
的問題在於 1/3 沒有精確的浮點表示,因此您並沒有“真正”獲得立方根。 所以使用cbrt
。 它不太可能變慢,除非它具有更高的准確性,但需要時間成本。
您假設Math.pow
或Math.cbrt
總是返回一個恰好是整數的值,而不是 41.999999 或其他值。 Java 文檔說:
計算結果必須在精確結果的 1 ulp 以內。
這意味着您的代碼可能不適用於符合標准的 Java 實現。 比較完全相等的浮點數是一件棘手的事情。 每位計算機科學家都應該知道的關於浮點運算的內容 關於浮點數,有很多話要說,但它真的很長。 (有充分的理由。使用浮點數很容易讓自己陷入困境。)另請參閱比較浮點數,2012 年版,Bruce Dawson 的 FP 文章系列。
我認為它不適用於所有long
價值。 double
只能精確表示高達 2^53 的整數(64 位 IEEE double 中的尾數大小)。 不能精確表示的整數的Math.cbrt
更不可能是精確的整數。
FP 立方根,然后測試得到的整數,避免了 FP 比較引入的所有問題:
static boolean isCube(long input) {
double cubeRoot = Math.cbrt(input);
long intRoot = Math.round(cubeRoot);
return (intRoot*intRoot*intRoot) == input;
}
(四處搜索后,我在其他 stackoverflow / stackexchange 答案中看到其他人也建議使用整數比較方法。)
如果您需要高性能,並且您不介意擁有更多源代碼的更復雜功能,那么就有可能。 例如,使用帶有整數數學的立方根逐次逼近算法。 如果您最終達到n^3 < input <
(n+1)^3 的點, then
input` 不是立方體。
在這個 math.SE 問題上有一些關於方法的討論。
我不會花時間詳細研究整數立方根算法,因為cbrt
部分可能不是主要瓶頸。 輸入解析和 string->long 轉換可能是瓶頸的主要部分。
其實我很好奇事實證明,在Hacker's Delight 中已經有一個整數立方根實現可用(即使沒有署名也允許使用/復制/分發。AFAICT,它本質上是公共域代碼。):
// Hacker's delight integer cube-root (for 32-bit integers, I think)
int icbrt1(unsigned x) {
int s;
unsigned y, b;
y = 0;
for (s = 30; s >= 0; s = s - 3) {
y = 2*y;
b = (3*y*(y + 1) + 1) << s;
if (x >= b) {
x = x - b;
y = y + 1;
}
}
return y;
}
根據int
的位數, 30
看起來像是一個神奇的數字。 將其移植到long
需要測試。 (還要注意,這是 C,但看起來它也應該用 Java 編譯!)
IDK 如果這是 Java 人的常識,但 32 位 Windows JVM 不使用server
JIT 引擎,也不優化您的代碼。
您可以首先通過測試給定數字的模數來消除大量候選者。 例如,以數字819
為模的立方體只能取以下45
值。
0 125 181 818 720 811 532 755 476
1 216 90 307 377 694 350 567 442
8 343 559 629 658 351 190 91 469
27 512 287 252 638 118 603 161 441
64 729 99 701 792 378 260 468 728
因此,在幾乎 95% 的均勻分布情況下,您實際上無需計算三次方根。
如果您只是將 int 更改為 long 並將 30 更改為 60,那么黑客喜悅例程似乎可以處理長數。如果您將 30 更改為 61,它似乎不起作用。
我並沒有真正理解該程序,所以我制作了另一個似乎可以在 Java 中運行的版本。
private static int cubeRoot(long n) {
final int MAX_POWER = 21;
int power = MAX_POWER;
long factor;
long root = 0;
long next, square, cube;
while (power >= 0) {
factor = 1 << power;
next = root + factor;
while (true) {
if (next > n) {
break;
}
if (n / next < next) {
break;
}
square = next * next;
if (n / square < next) {
break;
}
cube = square * next;
if (cube > n) {
break;
}
root = next;
next += factor;
}
--power;
}
return (int) root;
}
請定義非常顯示。 這是一個測試程序:
public static void main(String[] args) {
for (long v = 1; v > 0; v = v * 10) {
long start = System.nanoTime();
for (int i = 0; i < 100; i++)
isCube(v);
long end = System.nanoTime();
System.out.println(v + ": " + (end - start) + "ns");
}
}
static boolean isCube(long input) {
double cubeRoot = Math.pow(input,1.0/3.0);
return Math.round(cubeRoot) == cubeRoot;
}
輸出是:
1: 290528ns
10: 46188ns
100: 45332ns
1000: 46188ns
10000: 46188ns
100000: 46473ns
1000000: 46188ns
10000000: 45048ns
100000000: 45048ns
1000000000: 44763ns
10000000000: 45048ns
100000000000: 44477ns
1000000000000: 45047ns
10000000000000: 46473ns
100000000000000: 47044ns
1000000000000000: 46188ns
10000000000000000: 65291ns
100000000000000000: 45047ns
1000000000000000000: 44477ns
我沒有看到“大”數字的性能影響。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.