วิธีตรวจสอบว่าตัวเลขนั้นมีค่าเป็น 2 หรือไม่


584

วันนี้ฉันต้องการอัลกอริทึมอย่างง่ายสำหรับการตรวจสอบว่าตัวเลขมีค่าเป็น 2 หรือไม่

อัลกอริทึมจะต้อง:

  1. ง่าย
  2. ถูกต้องสำหรับulongค่าใด ๆ

ฉันมากับอัลกอริทึมง่าย ๆ นี้:

private bool IsPowerOfTwo(ulong number)
{
    if (number == 0)
        return false;

    for (ulong power = 1; power > 0; power = power << 1)
    {
        // This for loop used shifting for powers of 2, meaning
        // that the value will become 0 after the last shift
        // (from binary 1000...0000 to 0000...0000) then, the 'for'
        // loop will break out.

        if (power == number)
            return true;
        if (power > number)
            return false;
    }
    return false;
}

แต่ฉันก็คิดว่าการตรวจสอบว่าเป็นจำนวนที่แน่นอน แต่เมื่อฉันตรวจสอบ 2 ^ 63 + 1 กลับได้ 63 ทั้งหมดเพราะการปัดเศษ ดังนั้นฉันจึงตรวจสอบว่า 2 ต่อกำลัง 63 เท่ากับจำนวนเดิม - และเป็นเพราะการคำนวณเสร็จในs และไม่ใช่ตัวเลขที่แน่นอน:log2 xMath.Logdouble

private bool IsPowerOfTwo_2(ulong number)
{
    double log = Math.Log(number, 2);
    double pow = Math.Pow(2, Math.Round(log));
    return pow == number;
}

นี้ถูกส่งกลับสำหรับค่าที่ไม่ถูกต้องได้รับ:true9223372036854775809

มีอัลกอริทึมที่ดีกว่าหรือไม่


1
ผมคิดว่าการแก้ปัญหา(x & (x - 1))อาจจะกลับมาบวกเท็จเมื่อเป็นผลรวมของอำนาจของทั้งสองเช่นX 8 + 16
Joe Brown

32
ตัวเลขทั้งหมดสามารถเขียนได้ว่าเป็นผลรวมของกำลังสองนั่นคือเหตุผลที่เราสามารถแสดงตัวเลขใด ๆ ในรูปของเลขฐานสอง นอกจากนี้ตัวอย่างของคุณจะไม่ส่งคืนค่าบวกที่ผิดพลาดเนื่องจาก 11000 & 10111 = 10,000! = 0.
vlsd

1
@JoeBrown มันไม่มีผลบวกที่ผิด ๆ ในความเป็นจริงการแสดงออกกลับมีขนาดใหญ่กว่าผลรวมของสองพลังของสองใด ๆ
Samy Bencherif

คำตอบ:


1219

มีเคล็ดลับง่ายๆสำหรับปัญหานี้:

bool IsPowerOfTwo(ulong x)
{
    return (x & (x - 1)) == 0;
}

หมายเหตุฟังก์ชันนี้จะรายงานtrueสำหรับซึ่งไม่ได้เป็นอำนาจของ0 2หากคุณต้องการยกเว้นสิ่งต่อไปนี้เป็นวิธี:

bool IsPowerOfTwo(ulong x)
{
    return (x != 0) && ((x & (x - 1)) == 0);
}

คำอธิบาย

อันดับแรกและสำคัญที่สุดสำหรับไบนารี & โอเปอร์เรเตอร์จากคำจำกัดความ MSDN:

ไบนารี & โอเปอเรเตอร์ถูกกำหนดไว้ล่วงหน้าสำหรับประเภทอินทิกรัลและบูล สำหรับประเภทอินทิกรัลคำนวณค่าตรรกะและ AND ของตัวถูกดำเนินการ สำหรับตัวถูกดำเนินการบูล & คำนวณตรรกะและของตัวถูกดำเนินการ; นั่นคือผลลัพธ์จะเป็นจริงถ้าตัวถูกดำเนินการทั้งสองเป็นจริง

ตอนนี้เรามาดูกันว่ามันเล่นอย่างไร:

ฟังก์ชันส่งคืนบูลีน (จริง / เท็จ) และยอมรับพารามิเตอร์ขาเข้าหนึ่งประเภทที่ไม่ได้ลงชื่อยาว (x ในกรณีนี้) ให้เราเพื่อความเรียบง่ายสมมติว่ามีคนผ่านค่า 4 และเรียกใช้ฟังก์ชันดังนี้:

bool b = IsPowerOfTwo(4)

ตอนนี้เราแทนที่แต่ละการเกิดขึ้นของ x ด้วย 4:

return (4 != 0) && ((4 & (4-1)) == 0);

ทีนี้เราก็รู้แล้วว่า 4! = 0 เปลี่ยนไปเป็นจริงจนถึงตอนนี้ก็ดีมาก แต่สิ่งที่เกี่ยวกับ:

((4 & (4-1)) == 0)

สิ่งนี้แปลถึงสิ่งนี้แน่นอน:

((4 & 3) == 0)

แต่สิ่งที่แน่นอนคือ4&3?

การแทนเลขฐานสองของ 4 คือ 100 และการแทนเลขฐานสองของ 3 คือ 011 (โปรดจำไว้ว่า & ใช้การแทนเลขฐานสองของตัวเลขเหล่านี้) ดังนั้นเราจึงมี:

100 = 4
011 = 3

ลองนึกภาพค่าเหล่านี้จะถูกซ้อนกันเหมือนการเพิ่มระดับประถม &ผู้ประกอบการบอกว่าถ้าทั้งสองค่าเท่ากับ 1 แล้วผลที่ได้คือ 1 มิฉะนั้นจะเป็น 0 ดังนั้น1 & 1 = 1, 1 & 0 = 0, และ0 & 0 = 0 0 & 1 = 0ดังนั้นเราจึงทำคณิตศาสตร์:

100
011
----
000

ผลลัพธ์คือ 0 ดังนั้นเราจึงย้อนกลับไปดูสิ่งที่คำชี้แจงการคืนสินค้าของเราแปลเป็น:

return (4 != 0) && ((4 & 3) == 0);

ซึ่งแปลตอนนี้เป็น:

return true && (0 == 0);
return true && true;

เราทุกคนรู้ว่าtrue && trueเป็นเรื่องง่ายtrueและสิ่งนี้แสดงให้เห็นว่าสำหรับตัวอย่างของเรา 4 คือพลังของ 2


56
@Kripp: จำนวนจะเป็นรูปแบบไบนารี 1000 ... 000 เมื่อคุณ -1 มันจะอยู่ในรูปแบบ 0111 ... 111 ดังนั้นเลขฐานสองของทั้งสองและจะได้ผลลัพธ์เป็น 000000 ซึ่งจะไม่เกิดขึ้นสำหรับการไม่ใช้กำลังของสองตั้งแต่ 1010100 เช่นจะกลายเป็น 1010011 ทำให้เกิด (ต่อ ... )
กำหนดค่า

47
... ส่งผลให้ 1,010000 หลังจากไบนารีและ ข้อผิดพลาดที่เป็นบวกเท่านั้นจะเป็น 0 ซึ่งเป็นเหตุผลที่ฉันจะใช้: return (x! = 0) && ((x & (x - 1)) == 0);
กำหนดค่า

6
คริปป์พิจารณา (2: 1, 10: 1) (4: 3, 100: 11) (8: 7, 1000: 111) (16:15, 10000: 1111) ดูรูปแบบไหม?
Thomas L Holaday

13
@ShuggyCoUk: ส่วนประกอบสองอย่างคือการแสดงจำนวนลบ เนื่องจากนี่เป็นเลขจำนวนเต็มที่ไม่ได้ลงนามการแทนค่าจำนวนลบจะไม่เกี่ยวข้อง เทคนิคนี้อาศัยการแทนเลขฐานสองของจำนวนเต็มที่ไม่ใช่ค่าลบเท่านั้น
Greg Hewgill

4
@SapapBox - อะไรคือสามัญมากขึ้น? เลขศูนย์หรือตัวเลขที่ไม่ใช่ศูนย์ซึ่งไม่ใช่พลังของทั้งสอง? นี่เป็นคำถามที่คุณไม่สามารถตอบได้หากไม่มีบริบทเพิ่มเติม และมันจริงๆมันไม่สำคัญแล้วล่ะค่ะ
กำหนดค่า

97

ไซต์บางแห่งที่มีเอกสารและอธิบายสิ่งนี้และแฮ็กแบบทวิปอื่น ๆ ได้แก่ :

และหลานของพวกเขาหนังสือ "แฮ็คเกอร์ดีไลท์" โดยเฮนรีวอร์เรนจูเนียร์ :

ดังที่หน้าของ Sean Andersonอธิบายการแสดงออกที่((x & (x - 1)) == 0)ไม่ถูกต้องระบุว่า 0 คือพลังของ 2 เขาแนะนำให้ใช้:

(!(x & (x - 1)) && x)

เพื่อแก้ไขปัญหานั้น


4
0 คือพลังของ 2 ... 2 ^ -inf = 0. ;););)
Michael Bray

4
เนื่องจากนี่คือเธรดC # ที่ติดแท็กจึงควรชี้ให้เห็นว่าการแสดงออกครั้งสุดท้าย (ของ Sean Anderson) นั้นผิดกฎหมายใน C # เนื่องจาก!สามารถใช้กับประเภทบูลีนได้เท่านั้นและ&&ยังต้องการตัวถูกดำเนินการทั้งสองเป็นบูลีน - ทำให้สิ่งอื่น ๆ ที่เป็นไปได้ แต่ที่ไม่เกี่ยวข้องกับการulong).
Jeppe เพอร์นีลเซ่น

40

return (i & -i) == i


2
คำใบ้ว่าทำไมสิ่งนี้จะหรือไม่ทำงาน ฉันตรวจสอบความถูกต้องในภาษาจาวาเท่านั้นโดยที่มีการเซ็นชื่อ int / longs ถ้ามันถูกต้องนี่จะเป็นคำตอบที่ดีกว่า เร็วขึ้น + เล็กลง
Andreas Petersson

7
มันใช้ประโยชน์จากคุณสมบัติอย่างใดอย่างหนึ่งของสัญกรณ์เสริมสองอย่าง: เพื่อคำนวณค่าลบของตัวเลขที่คุณทำการลบล้างระดับบิตและเพิ่ม 1 เข้ากับผลลัพธ์ บิตอย่างมีนัยสำคัญน้อยที่สุดซึ่งเป็นชุดก็จะได้รับการตั้งค่าในi -iบิตด้านล่างที่จะเป็น 0 (ในค่าทั้งสอง) ในขณะที่บิตด้านบนนั้นจะถูกคว่ำด้วยความเคารพซึ่งกันและกัน ค่าของi & -iจะเป็นบิตที่มีนัยสำคัญน้อยที่สุดi(ซึ่งก็คือพลังของสอง) หากiมีค่าเท่ากันแสดงว่าเป็นชุดบิตเท่านั้น มันล้มเหลวเมื่อiเป็น 0 ด้วยเหตุผลเดียวกันกับที่i & (i - 1) == 0ทำ
Michael Carman

6
ถ้าiเป็นประเภทที่ไม่ได้ลงนามส่วนประกอบสองอย่างจะไม่เกี่ยวข้องกับมัน คุณเพียงแค่ได้รับประโยชน์จากคุณสมบัติของเลขคณิตแบบแยกส่วนและระดับบิตและ
.. GitHub หยุดช่วยน้ำแข็ง

2
สิ่งนี้จะไม่ทำงานหากi==0(ส่งคืน(0&0==0)ซึ่งเป็นtrue) ควรเป็นreturn i && ( (i&-i)==i )
bobobobo

22
bool IsPowerOfTwo(ulong x)
{
    return x > 0 && (x & (x - 1)) == 0;
}

3
การแก้ปัญหานี้จะดีกว่าเพราะมันยังสามารถจัดการกับจำนวนลบถ้าเชิงลบก็สามารถที่จะผ่านใน (ถ้ายาวแทนอู่ล่อง).
สตีเว่น

ทำไมทศนิยมผ่านเป็นกำลังสองในกรณีนี้
Chris Frisina

21

ผมเขียนบทความเกี่ยวกับเรื่องนี้เมื่อเร็ว ๆ นี้ที่http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/ ครอบคลุมการนับบิตวิธีใช้ลอการิทึมอย่างถูกต้องการตรวจสอบ "x &&! (x & (x - 1))" แบบคลาสสิคและอื่น ๆ


17

นี่คือโซลูชันC ++ ที่ง่าย

bool IsPowerOfTwo( unsigned int i )
{
    return std::bitset<32>(i).count() == 1;
}

8
ใน GCC คอมไพล์นี้ลงไป GCC __builtin_popcountเดียวในตัวเรียกว่า น่าเสียดายที่โปรเซสเซอร์ตระกูลหนึ่งยังไม่มีคำสั่งแอสเซมบลีเดี่ยวให้ทำสิ่งนี้ (x86) ดังนั้นจึงเป็นวิธีที่เร็วที่สุดสำหรับการนับจำนวนบิต ในสถาปัตยกรรมอื่น ๆ นี่คือคำสั่งการประกอบเดียว
deft_code

3
@deft_code ใหม่รองรับ x86 microarchitecturespopcnt
phuclv

13

ภาคผนวกต่อไปนี้สำหรับคำตอบที่ยอมรับอาจมีประโยชน์สำหรับบางคน:

กำลังสองเมื่อแสดงเป็นไบนารี่จะดูเหมือน1 ตามด้วย n zeroesเสมอโดยที่ n มากกว่าหรือเท่ากับ 0 Ex:

Decimal  Binary
1        1     (1 followed by 0 zero)
2        10    (1 followed by 1 zero)
4        100   (1 followed by 2 zeroes)
8        1000  (1 followed by 3 zeroes)
.        .
.        .
.        .

และอื่น ๆ

เมื่อเราลบ1ตัวเลขเหล่านี้ออกมาพวกเขาจะกลายเป็น0 แล้วตามด้วย nและอีก n ก็เหมือนกับข้างบน Ex:

Decimal    Binary
1 - 1 = 0  0    (0 followed by 0 one)
2 - 1 = 1  01   (0 followed by 1 one)
4 - 1 = 3  011  (0 followed by 2 ones)
8 - 1 = 7  0111 (0 followed by 3 ones)
.          .
.          .
.          .

และอื่น ๆ

มาถึงปม

จะเกิดอะไรขึ้นเมื่อเราทำ bitwise และจำนวนxซึ่งเป็นพลังของ 2 และx - 1?

หนึ่งในxได้รับการจัดตำแหน่งกับศูนย์ของx - 1และศูนย์ทั้งหมดของการxได้รับการจัดตำแหน่งด้วยx - 1ทำให้ก่อให้เกิดค่าบิตและเพื่อให้เป็น 0และนั่นคือวิธีที่เรามีคำตอบบรรทัดเดียวดังกล่าวข้างต้นถูกต้อง


เพิ่มเติมเพื่อความงามของคำตอบที่ยอมรับข้างต้น -

ดังนั้นเรามีสถานที่ให้บริการของเราในขณะนี้:

เมื่อเราลบ 1 จากจำนวนใด ๆ จากนั้นในการแทนเลขฐานสองขวาสุด 1 จะกลายเป็น 0 และศูนย์ทั้งหมดก่อนที่ขวาสุด 1 จะกลายเป็น 1

การใช้คุณสมบัตินี้อย่างยอดเยี่ยมอย่างหนึ่งคือการค้นหาว่ามี 1s กี่ตัวในการแทนเลขฐานสองของจำนวนที่กำหนด รหัสสั้นและหวานที่จะทำเช่นนั้นสำหรับจำนวนเต็มที่กำหนดxคือ:

byte count = 0;
for ( ; x != 0; x &= (x - 1)) count++;
Console.Write("Total ones in the binary representation of x = {0}", count);

อีกแง่มุมของตัวเลขที่สามารถพิสูจน์ได้จากแนวคิดที่อธิบายข้างต้นคือ"ทุกจำนวนบวกสามารถแสดงเป็นผลรวมของพลังของ 2 หรือไม่" .

ใช่ทุกจำนวนบวกสามารถแสดงเป็นผลรวมของพลังของ 2 สำหรับจำนวนใด ๆ ใช้การเป็นตัวแทนไบนารีของมัน Ex: 117หมายเลขพา

The binary representation of 117 is 1110101

Because  1110101 = 1000000 + 100000 + 10000 + 0000 + 100 + 00 + 1
we have  117     = 64      + 32     + 16    + 0    + 4   + 0  + 1

@Michi: ฉันอ้างว่ามีค่าเป็นบวกหรือไม่? หรือพลังของ 2?
displayName

ใช่โดยการใส่0เป็นตัวอย่างและทำให้คณิตศาสตร์นั้นอยู่ในการเป็นตัวแทนไบนารีนั้น มันสร้างความสับสน
Michi

1
หากเพิ่มตัวเลขสองตัวทำให้คุณสับสนเพราะเชื่อว่าพวกเขาต้องเป็นบวกฉันจะทำอะไรกับมันไม่ได้ ยิ่งกว่านั้นมีการแสดง 0 ในการเป็นตัวแทนเพื่อบ่งบอกว่ากำลังของ 2 ถูกข้ามในจำนวนนี้ ทุกคนที่รู้คณิตศาสตร์พื้นฐานทราบดีว่าการเพิ่ม 0 หมายถึงไม่เพิ่มอะไรเลย
displayName

10

หลังจากโพสต์คำถามที่ฉันคิดของการแก้ปัญหาต่อไปนี้:

เราจำเป็นต้องตรวจสอบว่าหนึ่งในเลขฐานสองเป็นหนึ่งหรือไม่ ดังนั้นเราก็เปลี่ยนจำนวนหนึ่งที่เหมาะสมหลักในเวลาและผลตอบแทนtrueถ้ามันเท่ากับ 1. หากจุดใด ๆ ที่เรานำมาโดยเป็นเลขคี่ ( (number & 1) == 1) falseเรารู้ว่าผลที่ได้คือ สิ่งนี้ได้รับการพิสูจน์แล้ว (ใช้มาตรฐาน) เร็วกว่าวิธีดั้งเดิมสำหรับค่าจริง (ใหญ่) เล็กน้อยและเร็วกว่าสำหรับค่าเท็จหรือเล็กมาก

private static bool IsPowerOfTwo(ulong number)
{
    while (number != 0)
    {
        if (number == 1)
            return true;

        if ((number & 1) == 1)
            // number is an odd number and not 1 - so it's not a power of two.
            return false;

        number = number >> 1;
    }
    return false;
}

แน่นอนว่าทางออกของ Greg นั้นดีกว่ามาก


10
    bool IsPowerOfTwo(int n)
    {
        if (n > 1)
        {
            while (n%2 == 0)
            {
                n >>= 1;
            }
        }
        return n == 1;
    }

และนี่คืออัลกอริธึมทั่วไปสำหรับการค้นหาว่าตัวเลขนั้นเป็นกำลังของตัวเลขอื่น

    bool IsPowerOf(int n,int b)
    {
        if (n > 1)
        {
            while (n % b == 0)
            {
                n /= b;
            }
        }
        return n == 1;
    }

6
bool isPow2 = ((x & ~(x-1))==x)? !!x : 0;

1
นี่คือc#อะไร ฉันเดาว่านี่c++เป็นxคืนที่เป็นบูล
Mariano Desanze

1
ฉันเขียนมันเป็น C ++ วิธีทำให้ C # เป็นเรื่องสำคัญ: bool isPow2 = ((x & ~ (x-1)) == x)? x! = 0: false;
abelenky

4

ค้นหาว่าจำนวนที่กำหนดคือพลังของ 2

#include <math.h>

int main(void)
{
    int n,logval,powval;
    printf("Enter a number to find whether it is s power of 2\n");
    scanf("%d",&n);
    logval=log(n)/log(2);
    powval=pow(2,logval);

    if(powval==n)
        printf("The number is a power of 2");
    else
        printf("The number is not a power of 2");

    getch();
    return 0;
}

หรือใน C #: return x == Math.Pow (2, Math.Log (x, 2));
กำหนดค่า

4
เสีย ทนทุกข์ทรมานจากปัญหาการปัดเศษทศนิยมที่สำคัญ ใช้สิ่งที่frexpน่ารังเกียจมากกว่าlogถ้าคุณต้องการใช้จุดลอย
.. GitHub หยุดช่วยน้ำแข็ง


4
int isPowerOfTwo(unsigned int x)
{
    return ((x != 0) && ((x & (~x + 1)) == x));
}

มันเร็วมาก ใช้เวลาประมาณ 6 นาที 43 วินาทีในการตรวจสอบจำนวนเต็ม 2 ^ 32 ทั้งหมด


4
return ((x != 0) && !(x & (x - 1)));

ถ้าxเป็นอำนาจของทั้งสองคนเดียว 1 nบิตของมันอยู่ในตำแหน่ง ซึ่งหมายความว่าx – 1มี 0 nอยู่ในตำแหน่ง เพื่อดูว่าทำไมจำวิธีการลบแบบไบนารีทำงาน เมื่อลบ 1 จากxยืมแพร่กระจายทางไปยังตำแหน่งที่ทุกคนn; บิตnจะกลายเป็น 0 และทุกบิตที่ต่ำกว่ากลายเป็นที่ 1 ตอนนี้เนื่องจากxไม่มี 1 บิตในการร่วมกันกับx – 1, x & (x – 1)เป็น 0 และ!(x & (x – 1))เป็นความจริง


3

ตัวเลขคือกำลังของ 2 ถ้ามันมีบิตเพียง 1 ชุด เราสามารถใช้คุณสมบัตินี้และฟังก์ชั่นทั่วไปcountSetBitsเพื่อค้นหาว่าตัวเลขมีค่าเป็น 2 หรือไม่

นี่คือโปรแกรม C ++:

int countSetBits(int n)
{
        int c = 0;
        while(n)
        {
                c += 1;
                n  = n & (n-1);
        }
        return c;
}

bool isPowerOfTwo(int n)
{        
        return (countSetBits(n)==1);
}
int main()
{
    int i, val[] = {0,1,2,3,4,5,15,16,22,32,38,64,70};
    for(i=0; i<sizeof(val)/sizeof(val[0]); i++)
        printf("Num:%d\tSet Bits:%d\t is power of two: %d\n",val[i], countSetBits(val[i]), isPowerOfTwo(val[i]));
    return 0;
}

เราไม่จำเป็นต้องตรวจสอบอย่างชัดเจนว่า 0 เป็นพลังของ 2 เพราะมันส่งกลับค่าเท็จสำหรับ 0 เช่นกัน

เอาท์พุท

Num:0   Set Bits:0   is power of two: 0
Num:1   Set Bits:1   is power of two: 1
Num:2   Set Bits:1   is power of two: 1
Num:3   Set Bits:2   is power of two: 0
Num:4   Set Bits:1   is power of two: 1
Num:5   Set Bits:2   is power of two: 0
Num:15  Set Bits:4   is power of two: 0
Num:16  Set Bits:1   is power of two: 1
Num:22  Set Bits:3   is power of two: 0
Num:32  Set Bits:1   is power of two: 1
Num:38  Set Bits:3   is power of two: 0
Num:64  Set Bits:1   is power of two: 1
Num:70  Set Bits:3   is power of two: 0

กลับ c เป็น 'int' เมื่อฟังก์ชั่นมีประเภทคืน 'ulong'? ใช้whileแทนif? โดยส่วนตัวฉันไม่เห็นเหตุผล แต่ดูเหมือนว่าจะทำงาน แก้ไข: - ไม่ ... มันจะส่งคืน 1 สำหรับสิ่งที่มากกว่า0!
James Khoury

@JamesKhoury ฉันกำลังเขียนโปรแกรม c ++ ดังนั้นฉันจึงส่งคืน int ผิดพลาด อย่างไรก็ตามนั่นเป็นความผิดพลาดเล็กน้อยและไม่สมควรได้รับการโหวต แต่ฉันไม่เข้าใจเหตุผลที่เหลือในการแสดงความคิดเห็นของคุณ "ใช้ในขณะที่แทนที่จะ" ถ้า "และ" มันจะกลับ 1 สำหรับสิ่งที่มากกว่า 0 " ฉันเพิ่มต้นขั้วหลักเพื่อตรวจสอบผลลัพธ์ AFAIK เป็นผลผลิตที่คาดหวัง ถูกต้องฉันถ้าฉันผิด
jerrymouse

3

นี่เป็นอีกวิธีที่ฉันคิดในกรณีนี้ใช้|แทน&:

bool is_power_of_2(ulong x) {
    if(x ==  (1 << (sizeof(ulong)*8 -1) ) return true;
    return (x > 0) && (x<<1 == (x|(x-1)) +1));
}

คุณต้องการ(x > 0)บิตที่นี่หรือไม่
กำหนดค่า

@configurator ใช่มิฉะนั้น is_power_of_2 (0) จะกลับมาจริง
Chethan

3

สำหรับพลังใด ๆ ของ 2 ต่อไปนี้ยังถือ

n (- n) n ==

หมายเหตุ: ล้มเหลวสำหรับ n = 0 ดังนั้นจำเป็นต้องตรวจสอบ
เหตุผลว่าทำไมงานนี้คือ:
-n เป็นส่วนประกอบ 2s ของ n -n จะมีทุกบิตไปทางซ้ายของบิตเซตที่ตั้งขวาสุดของ n พลิกเมื่อเทียบกับ n สำหรับกำลังของ 2 มีเพียงบิตเดียวเท่านั้น


2

ตัวอย่าง

0000 0001    Yes
0001 0001    No

ขั้นตอนวิธี

  1. ใช้หน้ากากบิตแบ่งNUMตัวแปรในไบนารี

  2. IF R > 0 AND L > 0: Return FALSE

  3. มิฉะนั้นNUMจะกลายเป็นคนที่ไม่ใช่ศูนย์

  4. IF NUM = 1: Return TRUE

  5. มิฉะนั้นไปที่ขั้นตอนที่ 1

ความซับซ้อน

เวลา ~ O(log(d))โดยที่dเป็นจำนวนเลขฐานสอง


1

การปรับปรุงคำตอบของ @ user134548 โดยไม่ใช้เลขคณิตบิต:

public static bool IsPowerOfTwo(ulong n)
{
    if (n % 2 != 0) return false;  // is odd (can't be power of 2)

    double exp = Math.Log(n, 2);
    if (exp != Math.Floor(exp)) return false;  // if exp is not integer, n can't be power
    return Math.Pow(2, exp) == n;
}

วิธีนี้ใช้ได้ผลดีสำหรับ:

IsPowerOfTwo(9223372036854775809)

การดำเนินการจุดลอยตัวนั้นช้ากว่านิพจน์ระดับบิตอย่างง่าย
phuclv

1

ทำเครื่องหมาย gravell แนะนำสิ่งนี้ถ้าคุณมี. NET Core 3, System.Runtime.Intrinsics.X86.Popcnt.PopCount

public bool IsPowerOfTwo(uint i)
{
    return Popcnt.PopCount(i) == 1
}

คำสั่งเดียวเร็วกว่า(x != 0) && ((x & (x - 1)) == 0)แต่พกพาน้อย


คุณแน่ใจว่ามันเร็วกว่า(x != 0) && ((x & (x - 1)) == 0)? ฉันสงสัยว่าเ ในระบบเก่าที่ popcnt ไม่สามารถใช้ได้
phuclv

มันไม่เร็ว ฉันเพิ่งทดสอบสิ่งนี้กับ CPU ของ Intel ที่ทันสมัยและตรวจสอบ POPCNT ที่ใช้ในการถอดแยกชิ้นส่วน (ได้รับในรหัส C ไม่ใช่ .NET) POPCNT เร็วขึ้นสำหรับการนับบิตโดยทั่วไป แต่สำหรับบิตเดียวในกรณีที่เคล็ดลับการเล่นการพนันบิตยังคงเร็วขึ้น 10%
eraoul

โอ๊ะฉันเอามันกลับมา ฉันกำลังทดสอบเป็นวงวนฉันคิดว่าการทำนายสาขาคือ "การโกง" POPCNT เป็นคำสั่งเดียวที่ทำงานในรอบสัญญาณนาฬิกาเดียวและจะเร็วขึ้นหากคุณมี
eraoul

0

ใน C ฉันทดสอบi && !(i & (i - 1)เคล็ดลับและเปรียบเทียบกับ__builtin_popcount(i)ใช้ gcc บน Linux พร้อมกับแฟล็ก -mpopcnt เพื่อให้แน่ใจว่าใช้คำสั่ง POPCNT ของ CPU โปรแกรมทดสอบของฉันนับจำนวน # ของจำนวนเต็มตั้งแต่ 0 ถึง 2 ^ 31 ที่มีกำลังสอง

ในตอนแรกที่ผมคิดว่าi && !(i & (i - 1)เป็น 10% เร็วแม้ว่าฉันสอบว่า POPCNT __builtin_popcountถูกใช้ในการถอดชิ้นส่วนที่ผมใช้

อย่างไรก็ตามฉันตระหนักว่าฉันได้รวมข้อความสั่ง if และการคาดคะเนสาขาอาจจะทำได้ดีกว่าในเวอร์ชันที่มีปัญหาเล็กน้อย ฉันลบ if และ POPCNT ลงเอยเร็วกว่าที่คาดไว้

ผล:

Intel (R) Core (TM) i7-4771 CPU สูงสุด 3.90GHz

Timing (i & !(i & (i - 1))) trick
30

real    0m13.804s
user    0m13.799s
sys     0m0.000s

Timing POPCNT
30

real    0m11.916s
user    0m11.916s
sys     0m0.000s

โปรเซสเซอร์ AMD Ryzen Threadripper 2950X 16-Core สูงสุด 3.50GHz

Timing (i && !(i & (i - 1))) trick
30

real    0m13.675s
user    0m13.673s
sys 0m0.000s

Timing POPCNT
30

real    0m13.156s
user    0m13.153s
sys 0m0.000s

โปรดทราบว่าที่นี่ Intel CPU ดูเหมือนจะช้ากว่า AMD เล็กน้อยโดยมีการกระตุกเล็กน้อย แต่มี POPCNT ที่เร็วกว่ามาก POPCNT ของ AMD ไม่ได้ให้การสนับสนุนมากนัก

popcnt_test.c:

#include "stdio.h"

// Count # of integers that are powers of 2 up to 2^31;
int main() {
  int n;
  for (int z = 0; z < 20; z++){
      n = 0;
      for (unsigned long i = 0; i < 1<<30; i++) {
       #ifdef USE_POPCNT
        n += (__builtin_popcount(i)==1); // Was: if (__builtin_popcount(i) == 1) n++;
       #else
        n += (i && !(i & (i - 1)));  // Was: if (i && !(i & (i - 1))) n++;
       #endif
      }
  }

  printf("%d\n", n);
  return 0;
}

ทำการทดสอบ:

gcc popcnt_test.c -O3 -o test.exe
gcc popcnt_test.c -O3 -DUSE_POPCNT -mpopcnt -o test-popcnt.exe

echo "Timing (i && !(i & (i - 1))) trick"
time ./test.exe

echo
echo "Timing POPCNT"
time ./test-opt.exe

0

ฉันเห็นคำตอบมากมายที่แนะนำให้คืน n &&! (n & (n - 1)) แต่สำหรับประสบการณ์ของฉันหากค่าอินพุตเป็นลบมันจะคืนค่าเท็จ ฉันจะแบ่งปันวิธีการง่ายๆที่นี่เนื่องจากเรารู้ว่ากำลังสองจำนวนมีเพียงชุดเดียวดังนั้นเราจะนับจำนวนชุดบิตนี้จะใช้เวลา O (log N)

while (n > 0) {
    int count = 0;
    n = n & (n - 1);
    count++;
}
return count == 1;

ตรวจสอบบทความนี้เพื่อนับไม่ ของบิตตั้ง


-1
private static bool IsPowerOfTwo(ulong x)
{
    var l = Math.Log(x, 2);
    return (l == Math.Floor(l));
}

ลองใช้สำหรับหมายเลข 9223372036854775809 มันใช้งานได้หรือไม่ ฉันไม่คิดว่าเพราะข้อผิดพลาดในการปัดเศษ
กำหนดค่า

1
@configurator 922337203685477580_9_ ดูไม่เหมือนพลังของ 2 ค่ะ;)
Kirschstein

1
@ Kirschstein: หมายเลขนั้นให้เขาบวกเท็จ
Erich Mirabal

7
Kirschstein: มันไม่เหมือนกันกับฉัน มันดูเหมือนหนึ่งในฟังก์ชั่นแม้ว่า ...
กำหนดค่า

-2

โปรแกรมใน java นี้ส่งคืน "true" ถ้า number เป็นกำลังของ 2 และส่งคืน "false" หากไม่ใช่พลังของ 2

// To check if the given number is power of 2

import java.util.Scanner;

public class PowerOfTwo {
    int n;
    void solve() {
        while(true) {
//          To eleminate the odd numbers
            if((n%2)!= 0){
                System.out.println("false");
                break;
            }
//  Tracing the number back till 2
            n = n/2;
//  2/2 gives one so condition should be 1
            if(n == 1) {
                System.out.println("true");
                break;
            }
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner in = new Scanner(System.in);
        PowerOfTwo obj = new PowerOfTwo();
        obj.n = in.nextInt();
        obj.solve();
    }

}

OUTPUT : 
34
false

16
true

1
คำถามนี้ถูกแท็ก C # และโซลูชันของคุณช้ามากเมื่อเทียบกับโซลูชันก่อนหน้านี้ [
phuclv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.