วิธีที่ง่ายที่สุดในการทดสอบว่าตัวเลขเป็นเลขยกกำลัง 2 ใน C ++ อย่างไร


95

ฉันต้องการฟังก์ชั่นเช่นนี้:

// return true iff 'n' is a power of 2, e.g.
// is_power_of_2(16) => true  is_power_of_2(3) => false
bool is_power_of_2(int n);

ใครช่วยแนะนำว่าฉันจะเขียนสิ่งนี้ได้อย่างไร คุณช่วยบอกฉันว่ามีเว็บไซต์ดีๆที่สามารถหาอัลกอริทึมประเภทนี้ได้ไหม



@rootTraveller - อาจจะไม่ซ้ำกัน C ++ และ Java เป็นภาษาที่แตกต่างกันและแต่ละภาษามีสิ่งอำนวยความสะดวกที่แตกต่างกัน ตัวอย่างเช่นใน C / C ++ ตอนนี้เราสามารถใช้ Intrinsics กับโปรเซสเซอร์ที่เปิดใช้งาน BMI ซึ่งจะออกคำสั่งเครื่องให้ทำในนาฬิกาครั้งเดียว ฉันคิดว่า Java มีอย่างอื่นเช่นอาจจะมีบางอย่างจากกิจวัตรทางคณิตศาสตร์
jww

คำตอบ:


192

(n & (n - 1)) == 0ดีที่สุด อย่างไรก็ตามโปรดทราบว่าค่านี้จะคืนค่าจริงสำหรับ n = 0 อย่างไม่ถูกต้องดังนั้นหากเป็นไปได้คุณจะต้องตรวจสอบอย่างชัดเจน

http://www.graphics.stanford.edu/~seander/bithacks.htmlมีชุดอัลกอริทึมที่ชาญฉลาดจำนวนมากรวมถึงอัลกอริทึมนี้ด้วย


8
โดยทั่วไป(n>0 && ((n & (n-1)) == 0))
Saurabh Goyal

1
@SaurabhGoyal หรือn && !(n & (n - 1))ตามลิงก์ในคำตอบ
Carsten

ทำไมโอ้ทำไมสิ่งนี้ถึงไม่อยู่ด้านบนของคำตอบ? OP โปรดยอมรับ
donturner

@SaurabhGoyal การปรับปรุงเล็กน้อยคือ: n & !(n & (n - 1)). สังเกตบิต AND &(ไม่ใช่ตรรกะและ&&) ตัวดำเนินการ Bitwise ไม่ใช้การลัดวงจรดังนั้นรหัสจึงไม่แตกแขนง สิ่งนี้ดีกว่าในสถานการณ์ที่มีแนวโน้มว่าจะเกิดการคาดเดาผิดสาขาและเมื่อคำนวณ rhs ของนิพจน์ (นั่นคือ!(n & (n - 1))) มีราคาถูก
Cassio Neri

@cassio !เป็นตัวดำเนินการเชิงตรรกะและด้วยเหตุนี้ค่าของ!(n & (n - 1))จึงจะเป็นบูลีนคุณแน่ใจหรือไม่ว่าบูลีนและตัวเลขสามารถกำหนดให้กับตัวดำเนินการ AND แบบบิตได้? ถ้าใช่ก็ดูดี
Saurabh Goyal

81

กำลังของสองจะมีชุดบิตเพียงชุดเดียว (สำหรับตัวเลขที่ไม่ได้ลงชื่อ) สิ่งที่ต้องการ

bool powerOfTwo = !(x == 0) && !(x & (x - 1));

จะทำงานได้ดี; หนึ่งน้อยกว่ากำลังของสองคือ 1 ทั้งหมดในบิตที่มีนัยสำคัญน้อยกว่าดังนั้นต้อง AND ถึง 0 บิต

เนื่องจากฉันสมมติว่าเป็นตัวเลขที่ไม่ได้ลงชื่อการทดสอบ == 0 (ซึ่งตอนแรกฉันลืมไปแล้วขออภัย) ก็เพียงพอแล้ว คุณอาจต้องการการทดสอบ> 0 หากคุณใช้จำนวนเต็มที่ลงชื่อ


คุณไม่มี "!" หรือ '== 0'

คุณยังพลาดการทดสอบค่าลบของ x
Rob Wells

เรียบร้อยคุณแก้ไขได้อย่างไรโดยไม่ให้ 'แก้ไข x นาทีที่แล้ว' ปรากฏ

อย่างจริงจังคุณได้รับตัวแทน 120 คนสำหรับคำตอบที่ผิดอย่างเห็นได้ชัดได้อย่างไร?

@ Mike F: อันที่จริงดูเหมือนว่าผู้คนจะโหวตคำตอบโดยไม่ตรวจสอบพวกเขา ใคร ๆ ก็ทำผิดได้ฉันเดาว่าถ้าฉันทำผิดพลาดในอนาคตอย่าลังเลที่จะแก้ไข
Adam Wright

49

พลังของสองในไบนารีมีลักษณะดังนี้:

1: 0001
2: 0010
4: 0100
8: 1000

โปรดทราบว่าจะมีการตั้งค่า 1 บิตเสมอ ข้อยกเว้นเพียงประการเดียวคือจำนวนเต็มที่ลงนาม เช่นจำนวนเต็มที่ลงนาม 8 บิตที่มีค่า -128 ดูเหมือนว่า:

10000000

ดังนั้นหลังจากตรวจสอบว่าจำนวนมากกว่าศูนย์เราสามารถใช้การแฮ็กเล็กน้อยที่ชาญฉลาดเพื่อทดสอบว่ามีการตั้งค่าหนึ่งบิตเพียงหนึ่งบิต

bool is_power_of_2(int x) {
    return x > 0 && !(x & (x−1));
}

สำหรับ twiddling บิตเพิ่มเติมโปรดดูที่นี่


14

แนวทาง # 1:

หารตัวเลขด้วย 2 อย่างชัดเจนเพื่อตรวจสอบ

ความซับซ้อนของเวลา: O (log2n)

แนวทาง # 2:

Bitwise AND ตัวเลขที่มีตัวเลขก่อนหน้าควรเท่ากับศูนย์

ตัวอย่าง: Number = 8 Binary ของ 8: 1 0 0 0 Binary ของ 7: 0 1 1 1 และ bitwise AND ของตัวเลขทั้งสองคือ 0 0 0 0 = 0

ความซับซ้อนของเวลา: O (1)

แนวทาง # 3:

Bitwise XOR ตัวเลขที่มีหมายเลขก่อนหน้าควรเป็นผลรวมของตัวเลขทั้งสอง

ตัวอย่าง: Number = 8 Binary of 8: 1 0 0 0 Binary of 7: 0 1 1 1 และ bitwise XOR ของทั้งสองตัวเลขคือ 1 1 1 1 = 15

ความซับซ้อนของเวลา: O (1)

http://javaexplorer03.blogspot.in/2016/01/how-to-check-number-is-power-of-two.html



7

สำหรับอำนาจใด ๆ ของ 2 สิ่งต่อไปนี้จะถือด้วย

n & (- n) == น

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


2
ฉันหมายความว่าเงื่อนไขเป็นจริงสำหรับ n = 0 แม้ว่าจะไม่ใช่กำลังสอง
FReeze FRancis

สิ่งนี้ได้ผลกับการแปลงที่เกิดขึ้นหาก n ไม่ได้ลงนาม?
Joseph Garvin

6

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

if(1==__builtin_popcount(n))

ไม่ ฉันเพิ่งทดสอบสิ่งนี้ ฉันชอบ popcount แต่สำหรับการทดสอบ power-of-2 การทดสอบi && !(i & (i - 1)))จะเร็วขึ้นประมาณ 10% ในเครื่องของฉันแม้ว่าฉันจะแน่ใจว่าได้เปิดใช้งานคำสั่ง POPCNT แบบเนทีฟใน gcc
eraoul

อ๊ะฉันเอาคืน โปรแกรมทดสอบของฉันกำลังทำงานแบบวนซ้ำและการคาดคะเนสาขาคือ "การโกง" คุณพูดถูกถ้าคุณมีคำสั่ง POPCNT บน CPU ของคุณมันจะเร็วกว่า
eraoul

5

ใน C ++ 20 มีสิ่งstd::ispow2ที่คุณสามารถใช้เพื่อจุดประสงค์นี้หากคุณไม่จำเป็นต้องใช้งานด้วยตัวเอง:

#include <bit>
static_assert(std::ispow2(16));
static_assert(!std::ispow2(15));

3

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

int isPowerOfTwo(unsigned int x)
{
  return x && !(x & (x – 1));
}

ถ้าคุณรู้ว่า x ไม่สามารถเป็น 0 ได้

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x – 1));
}


3

วิธีที่ง่ายที่สุดในการทดสอบว่าตัวเลขเป็นเลขยกกำลัง 2 ใน C ++ อย่างไร

หากคุณมีโปรเซสเซอร์ Intel ที่ทันสมัยพร้อมคำแนะนำในการจัดการบิตคุณสามารถดำเนินการดังต่อไปนี้ มันละเว้นรหัส C / C ++ ตรงเพราะคนอื่นตอบไปแล้ว แต่คุณต้องใช้ถ้า BMI ไม่พร้อมใช้งานหรือเปิดใช้งาน

bool IsPowerOf2_32(uint32_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u32(x));
#endif
    // Fallback to C/C++ code
}

bool IsPowerOf2_64(uint64_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u64(x));
#endif
    // Fallback to C/C++ code
}

GCC, ICC และ Clang signal BMI รองรับด้วย__BMI__. มันมีอยู่ในคอมไพเลอร์ไมโครซอฟท์ใน Visual Studio 2015 และสูงกว่าเมื่อAVX2 สามารถใช้ได้และเปิดใช้งาน สำหรับส่วนหัวที่คุณต้องการโปรดดูที่ไฟล์ส่วนหัวสำหรับ SIMD ภายในintrinsics

ฉันมักจะป้องกัน_blsr_u64ด้วย_LP64_ในกรณีที่รวบรวมบน i686 เสียงดังต้องการวิธีแก้ปัญหาเล็กน้อยเนื่องจากใช้ชื่อสัญลักษณ์ภายในที่แตกต่างกันเล็กน้อย:

#if defined(__GNUC__) && defined(__BMI__)
# if defined(__clang__)
#  ifndef _tzcnt_u32
#   define _tzcnt_u32(x) __tzcnt_u32(x)
#  endif
#  ifndef _blsr_u32
#    define  _blsr_u32(x)  __blsr_u32(x)
#  endif
#  ifdef __x86_64__
#   ifndef _tzcnt_u64
#    define _tzcnt_u64(x) __tzcnt_u64(x)
#   endif
#   ifndef _blsr_u64
#     define  _blsr_u64(x)  __blsr_u64(x)
#   endif
#  endif  // x86_64
# endif  // Clang
#endif  // GNUC and BMI

คุณช่วยบอกฉันได้ไหมว่ามีเว็บไซต์ดีๆที่สามารถหาอัลกอริทึมประเภทนี้ได้

เว็บไซต์นี้มักจะอ้าง: Bit twiddling Hacks


นี่ไม่ใช่ "วิธีที่ง่ายที่สุด" ตามที่ร้องขอใน OP อย่างแน่นอน แต่เป็นวิธีที่เร็วที่สุดสำหรับสภาพแวดล้อมเฉพาะ การแสดงวิธีวางเงื่อนไขสำหรับสถาปัตยกรรมต่างๆมีประโยชน์อย่างมาก
fearless_fool

1

นี่ไม่ใช่วิธีที่เร็วที่สุดหรือสั้นที่สุด แต่ฉันคิดว่ามันอ่านง่ายมาก ดังนั้นฉันจะทำสิ่งนี้:

bool is_power_of_2(int n)
  int bitCounter=0;
  while(n) {
    if ((n & 1) == 1) {
      ++bitCounter;
    }
    n >>= 1;
  }
  return (bitCounter == 1);
}

สิ่งนี้ใช้ได้ผลเนื่องจากไบนารีขึ้นอยู่กับกำลังสอง ตัวเลขใด ๆ ที่มีเพียงชุดบิตเดียวจะต้องเป็นเลขยกกำลังสอง


อาจไม่เร็วหรือสั้น แต่ถูกต้องไม่เหมือนกับคำตอบด้านบน

2
ในเวลาแสดงความคิดเห็นพวกเขาทั้งหมดถูกดักฟัง ตั้งแต่นั้นมาได้รับการแก้ไขให้อยู่ในสถานะที่ยอมรับได้

0

นี่เป็นอีกวิธีหนึ่งในกรณีนี้โดยใช้|แทน&:

bool is_power_of_2(int x) {
    return x > 0 && (x<<1 == (x|(x-1)) +1));
}

0

เป็นไปได้ผ่าน c ++

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}

2
นั่นไม่ใช่เรื่องง่ายหรือรวดเร็วสำหรับฉัน
luk32

2
กล่าวคือมันไม่เร็วอย่างแน่นอนเนื่องจากlog2และการพิสูจน์ว่าการทำงานนั้นไม่ใช่เรื่องง่ายที่จะอธิบาย (อย่างแม่นยำคุณสามารถจับข้อผิดพลาดในการปัดเศษได้หรือไม่?) if..return..else..returnนอกจากนี้ยังไม่มีความจำเป็นที่ซับซ้อนด้วย เกิดอะไรขึ้นกับการยุบมันลงreturn x==(double)y;? มันควรจะคืนboolค่าใด ๆ IMO แม้ผู้ประกอบ ternary intจะชัดเจนถ้าใครต้องการจริงๆที่จะติด
luk32

0

ฉันรู้ว่านี่เป็นโพสต์เก่ามากแต่ฉันคิดว่ามันน่าสนใจที่จะโพสต์ที่นี่


จากCode-Golf SE (เพื่อให้เครดิตกับผู้ที่เขียนสิ่งนี้): Showcase of Languages

(ย่อหน้าเกี่ยวกับC , อนุวรรคยาว 36 ข้อมูลโค้ด )

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}

-1

อีกวิธีที่จะไป (อาจจะไม่เร็วที่สุด) คือการกำหนดว่า ln (x) / ln (2) เป็นจำนวนเต็มหรือไม่


2
ไม่มีอะไรเกี่ยวกับมัน :-)
paxdiablo

1
สิ่งนี้จะมีปัญหากับความไม่แม่นยำของจุดลอยตัว ln (1 << 29) / ln (2) ออกมาเป็น 29.000000000000004
ไม่ระบุชื่อ

-3

นี่คือวิธีการบิตกะใน T-SQL (SQL Server):

SELECT CASE WHEN @X>0 AND (@X) & (@X-1)=0 THEN 1 ELSE 0 END AS IsPowerOfTwo

เร็วกว่าการทำลอการิทึมสี่เท่า (ชุดแรกเพื่อให้ได้ผลลัพธ์ทศนิยมชุดที่ 2 เพื่อรับชุดจำนวนเต็มและเปรียบเทียบ)


5
เป็นการดีที่จะดูว่าคำตอบยอดนิยมสำหรับคำถามนี้สามารถนำไปใช้ใน T-SQL ได้อย่างไร แต่นั่นไม่เกี่ยวข้องกับคำถามที่ถามที่นี่ อีกทางเลือกหนึ่ง (หากคุณกำลังมองหาวิธีแก้ปัญหาใน T-SQL พบคำถามที่ตอบได้นี้นำไปใช้ใน T-SQL และคิดว่าน่าสนใจพอที่จะโพสต์คำตอบนี้) คือการโพสต์คำถามโดยอ้างอิงถึง T-SQL จากนั้น ตอบด้วยตัวเองโดยอ้างอิงถึงคำถามที่ตอบแล้วนี้ หวังว่าคำแนะนำนี้จะเป็นประโยชน์
Simon

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