簡體   English   中英

使用位移求奇數除數

[英]Finding odd divisors with bit-shifting

(這個問題指的是這個Codeforces 問題)我想知道是否有任何n (2 ≤ n ≤ 10¹⁴)有一個奇數除數。 使用 C++11,我認為可以通過迭代每個奇數直到 n 來強制解決方案,並檢查%是否可整除。 例如:

for(unsigned long long i=3;i<n;i+=2){
   if(n%i==0) return true; //It has an odd divisor
}
return false; //n%j==0 was never true so it doesn't have an odd divisor

如果給出任何大數字,這當然會變得非常緩慢。 我發現人們正在通過位移來解決它,並檢查最后一位是否為 1,類似於以下內容:

while(!(n&1))n>>=1;
if(n==1) return false; else return true;

如果我沒記錯的話,最后一段代碼是通過執行n=n/2ⁿ檢查最后一位(n&1)是否為奇數。 計算n=n/2ⁿ在解決問題方面如何具有與檢查每個奇數相同的准確性? (我怎么知道我不是“跳過”除數?)。

要理解這一點,您必須了解最右邊的奇數和偶數是如何工作的。
基本上任何奇數都有它的第 0 位(最右邊)為 1(記住位計數從右開始,即最右邊的位是第 0 位,然后你 go 一個接一個地離開,即第 1 位,第 2 位等等)和任何偶數它的第 0 位為 0。
所以當你這樣做時

int value = n & 1;

你在做什么是你在做n&1的'AND',所以如果n是奇數,那么它的第0位將為1,所以當你假設例如

int result = 5 & 1;

現在這實際上看起來像這樣

(101) & (001)

所以你得到1作為結果(因為1的所有其他位都是0,所以5和1的所有其他位的AND結果將為0,所以基本上你保持1),但如果n是偶數,那么例如,它的第 0 位將為 0

int result = 10 & 1;

看起來像這樣

1010 & 0001

現在基於前面的示例,您可以看到在偶數的情況下結果將始終為 0。 這就是為什么檢查一個數字是奇數還是偶數會更快,因為你在所有模數和其他更高級別的東西中都沒有 go,你在這里做的是低級別的東西。

所以來到重點,發生的事情是我們必須檢查一個數字是否包含任何奇數除數,讓我們一步一步看
例如,一個數字只不過是它的除數的倍數

30 = 10 * 3;
30 = 15 * 2;
30 = 5 * 6

但是如果你仔細觀察,你會發現它總是相同的唯一素數的組合,即

30 = 2 * 5 * 3;
30 = 3 * 5 * 2;
30 = 5 * 2 * 3;

所以現在讓我們看看素數中的一個事實,我們看到 2 是唯一不奇數的素數。
如果我們用這個事實 go ,我們可以確定如果 2 乘以任何其他素數,那么所得數字將始終是奇數(記住偶數 * 奇數 = 奇數)。 所以如果我們想要一個沒有任何奇數除數的數字,那么我們不希望在它的乘法組合中有任何奇數素數,那么該數字應該只是 2 的乘法組合,換句話說就是 2 的冪。(我們可以忽略 1 的大小寫,因為問題中提供了不將其視為輸入的情況)

所以如果我們只是檢查一個數字是否是2的冪,我們的問題就解決了,即如果它是2的冪,那么它不包含任何奇數除數,否則它包含一個奇數除數。
現在我們如何找到一個數字是否是 2 的冪。
所以我們看到的任何數字都可以是二進制表示中不同位的組合
例如

10 = (1010) (2^3 * 1 + 2^2 * 0 + 2^1 * 1 + 2^0 * 0)

如果任何數字是 2 的冪次方,那么它的二進制表示中只有一位為 1,所有其他位將為 0(對於 2^0 = 1 的情況,您可以看到我們已經給出了約束x > 1,因此我們可以忽略 1) 的大小寫處理

所以現在讓我們看看這段代碼

// it runs the while loop until all the right bits of first 1 bit is 0, that is it runs till we shift our rightmost 1 bit to 0th bit
while(!(n & 1)) { 
    n >>= 1; // if we are inside the loop, it means rightmost 1 is still not at 0th bit position, as the AND is done for 0th bit like I explained above, so doing this will right shift our rightmost 1, i.e if it was at 2nd position, it will now come at 1st position 
}

if(n == 1) {
   // if it is 1, it means only 1 bit is there in whole binary representation of the number which is at 0th position, it means it is a power of two(remember that this n is calculated based on sum of all the power of 2)
    return false; 
} else {
    return true;
}

這種更優化的解決方案有效地去除了 2 的任何冪,如果余數不是1的因數,則它必須涉及其他一些因素。

由於所有偶數因子本身都具有2作為因子,因此這只會將數字“歸結”到有余數因子或1為止。

比如,舉個例子:

0b11010 (26) -> 0b1101 (13)
0b11000 (24) -> 0b11 (3)
0b10000 (16) -> 0b1 (1)

您還可以有幾個主要因素,例如:

0b1111110000 (1008) -> (63)

那是 9 x 7,但你可以認為 63 是唯一相同的非偶數因子。

!(n & 1)等價於n & 1 == 0 ,並測試n是否為偶數,因為n & 1對於奇數為 1,對於偶數為 0。
n >>= 1將 integer 除以二。
(附帶說明一下,幾十年來,編譯器已經完全有能力對n % 2 == 0n /= 2進行這種優化,並且在確定何時真正有益方面比人類要好得多。)

接下來,數學:

最小的奇數除數也是最小的奇數素因數。

如果x是奇數,我們知道它至少有一個奇質因數,因此至少有一個奇數除數。

如果x是偶數,則可以寫成2 * y ,其中yx/2
我們還知道,我們可以將x寫為其質因數2 * f0 * f1 *... * fn的乘積。
因此, yf0 * f1 *... * fn ,並且x/2的奇數除數與x的奇數除數完全相同。

重復x/2直到達到 1 或大於 2 的奇數。

問題本身有一些模糊性。
這個問題的答案在所有情況下都是true的,因為奇數1是任何 integer 數n的除數。
因此,我懷疑其目的是找到一些與1不同的奇數除數。

根據算術基本定理,正數 integer 可以以獨特的方式分解,因為所有素數的乘積上升到某個非負 integer 冪。

例如,數字600被分解為2*2*2*3*5*5的乘積。
唯一的偶數引數是2 ,因此,在將n除以其素數分解的等於2的所有因子之后,將只剩下奇數素因子。

如果n有一些與1不同的奇數除數,則盡可能多次將n除以 2,我們將獲得除n的最大奇數。

另一方面,除以 2 相當於移位 1:
(n/2) == (n >> 1)

因此,通過盡可能多地移位來獲得這樣的最大奇數除數就足夠了。

int odd_divisor = n;
while (odd_divisor % 2 == 0)
  odd_divisor /= 2;
return odd_divisor;  // This number is odd,
                     // it is a divisor of n,
                     // and do with it
                     // whatever you want.

如果數字odd_divisor == 1這意味着n的唯一奇數除數是1 ,因此在這種情況下問題的答案似乎是false
否則,答案為true

您可以替換操作odd_divisor /= 2
通過odd_divisor >>= 1
你也可以替換odd_dividor % 2 == 0
通過條件odd_dividor & 1 == 0

正如其他人已經提到的,memory 中的 integer 數字的表示是在 base 2中完成的,因此檢查它的最后一位相當於檢查它的奇偶校驗。

沒有奇數除數的數字都是 2 的冪。使用 AND 位運算符,您可以執行以下操作:

    if ((n & (n - 1)) == 0) {
        cout << "NO" << endl;
    }
    else {
        cout << "YES" << endl;
    }

在二進制中,所有 2 的冪在其二進制表示中只有一個位為 1,所有其他位將為 0。此外,2 的冪之前的數字的所有位都為 1。所以與((n & (n - 1)) == 0)您可以輕松識別數字是否為 2 的冪。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM