2016-05-02 89 views
22

這最近code golfing post要求用C快速實現的(假定n是無符號整數)以下的可能性:如何快速評估零點集?

if (n==6 || n==8 || n==10 || n==12 || n==14 || n==16 || n==18 || n==20)

一個可能的簡化是觀察數字a[]={6,8,10,12,14,16,18,20}形成arithmetic progression,所以移位的範圍內,然後用一些bitwise tricks

if (((n - 6) & 14) + 6 == n)

導致更短(並且可能實際上更高效)的實現,如John Bollinger的answered

現在我問什麼是類似優雅(希望同樣有效)實現

if (n==3 || n==5 || n==11 || n==29 || n==83 || n==245 || n==731 || n==2189)

提示的:這一次的數字a[k]形成幾何級數a[k]=2+3^k

我想在一般情況下,不能比排序數字a[k]更好,然後做一個對數搜索來測試n是否是排序數組的成員。

+12

如果這是一個超過空間折衷的時間,那麼我會分配一個填充零的2189字緩衝區,除了特定的位置(3,5,11等),並簡單地執行數組查找(它因爲它是硬件實現的,所以速度非常快)。這不是非常優雅,但會以空間性能的成本爲您提供最佳性能。 –

+2

不應該在http://codereview.stackexchange.com上發佈此類問題嗎? – CinCout

+0

http://stackoverflow.com/questions/1804311/how-to-check-if-an-integer-is-a-power-of-3 – Dummy00001

回答

25
if ((n > 2) && (2187 % (n - 2) == 0)) 

檢查是否(n - 2)3功率並且小於或等於2187(3到7次冪)

作爲一般化,以檢查是否有任何的無符號整數n是一個功率素數k,您可以檢查n是否可以存儲在無符號整數中的最大功率k

+1

這是一個很好的答案,但您應該將「小於或等於2187」更改爲「小於或等於2189」。另外,你應該添加'n> 2'的初步驗證。 –

+0

@barakmanos,謝謝。我已經更新了檢查n> 2的答案。 –

+0

我認爲「小於或等於2187」是正確的,因爲它(n - 2)。 –

0

這就是我想出了:

bool b; 
if (n >= 3 && n <= 2189) 
{ 
    double d = std::log(n - 2)/std::log(3); // log₃(n - 2) 
    b = std::trunc(d) == d; // Checks to see if this is an integer 
    // If ‘b’ then ‘n == a[log₃(n - 2)]’ 
} 
else b = false; 

if (b) 
{ 
    // your code 
} 

由於數量n是小的整數,浮點不準確不應該是一個問題。

當然,它不會像整數運算一樣快,它不適合貼在表達式上,它可能比某種數組搜索更快,特別是如果你在哪裏增加最大值。

編輯我測試我的代碼(未啓用優化),平均(對於所有n小於或等於2189)我的版本了185.849納秒wheras的||版本了116.546納秒(運行我的程序產生多次類似的結果),所以這不是更快。 也出於一些離奇的原因,它設置bfalsen == 245,(它應該是真的)。

+1

如果'n <2'會怎麼樣?順便說一句,計算一個'日誌'可能需要比原始表達式更多。至少對'log(3)'來說,由於問題是關於性能的,你應該把它保存在一個'static'變量中(即只計算一次)。 –

+0

@barakmanos謝謝我解決了這個問題。嗯,我沒有想到,也許我會測試它... – Isaac

+0

@barakmanos我應該做一個靜態變量? – Isaac

3

發現在一個相關的帖子有類似的問題: 您可以使用std::find

bool found = (std::find(my_var.begin(), my_var.end(), my_variable) != my_var.end()); 
// my_var is a list of elements. 

(請確保您有<算法>)。

對於這種東西,在99%的情況下,有一個圖書館可以爲你做這項工作。

+2

'std :: find'具有線性運行時。我認爲這不會成爲可能的最快實現...... – Jens

+2

對於8個值,這可能會很快 - 許多CPU與計數器進行重複比較操作以限制搜索的數據量,並且它不是很多數據緩存。如果這個表現超過二分查找,我一點也不會感到驚訝。 –

+1

@TonyD我認爲在C++會議上有一個演示文稿,實際測量的是它,而小型文件集的線性搜索速度更快。然而,這個問題也要求一般情況下,他正在統計彙編指令。在這個假想的情況下,我認爲線性搜索沒有資格。 – Jens

5

提示:這次數字a[k]形成幾何級數:a[k]=2+3^k

n = 2 + 3^k 
n - 2 = 3^k 

(n - 2)/3^k = 1 
(n - 2) % 3^k = 0 

k = 0 ~ n-2 = 3^0 = 1, n = 3 
k = 1 ~ n-2 = 3^1 = 3, n = 5 
k = 2 ~ n-2 = 3^3 = 9, n = 11 

if (n > 2 && isPow(n-2, 3)) 

與功能isPow(x,y)

bool isPow(unsigned long x, unsigned int y) 
{ 
    while (x % y == 0) 
    { 
     x /= y; 
    } 

    return x == 1; 
} 

n = n ~ n-2 = 3^k/3 = 3^(k-1)/3 = .. = 3^1/3 = 1%3 != 0 
n = 11 ~ n-2 = 9/3 = 3/3 = 1%3 != 0 
n = 5 ~ n-2 = 3/3 = 1%3 != 0 
n = 3 ~ n-2 = 1%3 != 0 

Similiarly我們可以扣除k定義..

int k = findK(n-2, 3); 

int findK(unsigned long x, unsigned int y) 
{ 
    unsigned int k = 0; 

    while (x % y == 0) 
    { 
     x /= y; 
     k++; 
    } 

    if (x == 1) return k; 
    else return (-1); 
} 

n - 2 = 3 * 3^(k-1)  for k > 0 
(n - 2)/3 = 3^(k-1) 
(n - 2)/3/3 = 3^(k-2) 
(n - 2)/3/3/3 = 3^(k-3) 
(n - 2)/3/3/3/3 = 3^(k-4) 
.. 
(n - 2)/3/3/..i times = 3^(k-i) 
.. 
(n - 2)/3/3/..k times = 3^0 = 1 
+1

@thiru現在修好了 –

+1

與線性搜索具有相同的時間複雜度。 –

+0

@ n.m。我正在尋找'類似優雅的' –

3

的一個非常有效的方式做到這樣的事情是與一組,尤其是unordered_set。作爲散列表,搜索是平均常數,最差的線性。它也比一系列條件更加優雅,並且規模良好。

只需插入要比較的值和count該值中的值。如果找到它,這是您的測試值之一。

std::unordered_set<int> values; 
values.insert(3); 
values.insert(5); 
values.insert(11); 
values.insert(29); 
values.insert(83); 
values.insert(245); 
values.insert(731); 
values.insert(2189); 

... 

if(values.count(input)) 
    std::cout << "Value is in set.\n"; 
else 
    std::cout << "Value is NOT in set.\n"; 
+2

我喜歡這個方法來解決這個問題。讓我感到驚訝的是,在這裏你不會利用數字的結構,也就是說,它們不僅僅是一些隨機數,而且它們形成了幾何級數。你在這裏聲稱,這些巨大的信息對性能沒有任何影響,當然這可能是真實的,但它對我來說看起來是不直觀的。 – Matsmath

+0

@Matsmath好吧,隱含的另一個關於這些數字的同樣重要的信息片段是利用數學進程忽略它們:它們如何影響散列函數,以及它們如何在集合中被處理。也就是說,你可能可以利用系列級數編寫自定義哈希函數;這可能會使碰撞徹底消除,從而迫使不斷的搜尋。然而,這主要是一個學術觀察,我認爲任何人都不會這樣做。 –

+0

@WilliamKappler我不同意你最後的評論句子:https://en.wikipedia.org/wiki/Perfect_hash_function。在極少數情況下,這是必要的。 – maaartinus

8

這非常類似於recognizing a power of three,並且可以例如this solution適應:

bool test(unsigned x) { 
    x -= 2; 
    if (x > 2187) 
     return 0; 
    if (x > 243) 
     x *= 0xd2b3183b; 
    return x <= 243 && ((x * 0x71c5) & 0x5145) == 0x5145; 
} 

在給定的範圍內,這可以進一步簡化(通過強力實測值):

bool test2(unsigned x) { 
    x -= 2; 
    return x <= 2187 && (((x * 0x4be55) & 0xd2514105) == 5); 
} 
+2

哎呀,這裏有些好東西。您能否詳細說明您從「基本」版本到「高級」版本的過程?這裏有什麼大想法? – Matsmath