การแตกบิตด้วยการคูณเดียว


301

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

เราได้รับจำนวนเต็ม 64- บิตที่ไม่ได้ลงชื่อและเราสนใจบิตต่อไปนี้:

1.......2.......3.......4.......5.......6.......7.......8.......

โดยเฉพาะเราต้องการย้ายพวกเขาไปยังตำแหน่งแปดอันดับแรกเช่น:

12345678........................................................

เราไม่สนใจเกี่ยวกับคุณค่าของบิตที่ระบุโดย.และพวกเขาไม่จำเป็นต้องเก็บรักษาไว้

วิธีการแก้ปัญหา0x2040810204081คือการหน้ากากออกบิตที่ไม่พึงประสงค์และคูณผลโดย นี่เป็นเคล็ดลับ

วิธีนี้เป็นวิธีทั่วไป? สามารถใช้เทคนิคนี้เพื่อแยกบิตย่อยใด ๆ ได้หรือไม่? ถ้าไม่วิธีหนึ่งคิดออกว่าวิธีการทำงานสำหรับชุดบิตเฉพาะหรือไม่

สุดท้ายเราจะหาตัวคูณที่ถูกต้องเพื่อแยกบิตที่กำหนดได้อย่างไร


29
หากคุณพบว่ามีสิ่งที่น่าสนใจลองดูที่รายการนี้: graphics.stanford.edu/~seander/bithacks.htmlหลายคน (ab) ใช้การคูณ / การหารที่กว้างขึ้นเพื่อให้ได้ผลลัพธ์ที่น่าสนใจ ( "ย้อนกลับบิตในไบต์ที่มีการดำเนินงาน 4" ส่วนหนึ่งที่แสดงให้เห็นวิธีการจัดการกับเคล็ดลับ bitshift / คูณเมื่อคุณไม่ได้มีพื้นที่เพียงพอและความจำเป็นที่จะสวมหน้ากาก / คูณสอง)
viraptor

@ viraptor: จุดที่ยอดเยี่ยม หากคุณเข้าใจถึงข้อ จำกัด ของวิธีการนี้คุณสามารถใช้การคูณเพื่อบรรลุผลอย่างมากเกี่ยวกับการจัดการบิต
Expedito

9
ที่น่าสนใจคือมีคำแนะนำใน AVX2 (ซึ่งน่าเสียดายที่ยังไม่พร้อมใช้งาน) ที่ทำงานตามที่คุณอธิบายไว้อย่างแน่นอน: software.intel.com/sites/products/documentation/studio/composer/
JPvdMerwe

3
อีกที่ที่จะมองหาอัลกอริธึมการบิด
Barmar

1
อืม livro que conheço sobre o assunto (e gosto bastante) é o "แฮกเกอร์ดีไลท์" ลิงก์
Salles

คำตอบ:


235

คำถามที่น่าสนใจมากและเคล็ดลับที่ชาญฉลาด

ลองดูตัวอย่างง่ายๆในการจัดการไบต์เดียว การใช้ 8 บิตที่ไม่ได้ลงนามเพื่อความเรียบง่าย ลองจินตนาการถึงหมายเลขของคุณและคุณต้องการxxaxxbxxab000000

การแก้ปัญหาประกอบด้วยสองขั้นตอน: การปิดบังเล็กน้อยตามด้วยการคูณ bit mask เป็นการทำงานแบบ AND อย่างง่ายที่เปลี่ยนบิตที่ไม่สนใจเป็นศูนย์ ในกรณีดังกล่าวข้างต้นหน้ากากของคุณจะเป็นและผลที่ได้0010010000a00b00

ab......ตอนนี้ส่วนที่ยากกลายเป็นว่า

การคูณคือการดำเนินการ shift-and-add กุญแจสำคัญคือการอนุญาตให้มีการไหลล้นเพื่อ "เลื่อนออกไป" บิตที่เราไม่ต้องการและใส่สิ่งที่เราต้องการในสถานที่ที่เหมาะสม

คูณ 4 ( 00000100) จะเปลี่ยนทุกอย่างด้านซ้ายโดยที่ 2 a00b0000และได้รับคุณไป ในการที่bจะเลื่อนขึ้นเราจำเป็นต้องคูณด้วย 1 (เพื่อให้ a อยู่ในตำแหน่งที่ถูกต้อง) + 4 (เพื่อเลื่อน b ขึ้น) จำนวนนี้คือ 5 และรวมกับก่อนหน้านี้ 4 เราได้รับจำนวนมายากล 20 00010100หรือ ต้นฉบับคือ00a00b00หลังจากกำบัง; การคูณให้:

000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......

จากวิธีการนี้คุณสามารถขยายจำนวนมากขึ้นและบิตเพิ่มเติม

หนึ่งในคำถามที่คุณถามคือ "สามารถทำได้ด้วยบิตจำนวนเท่าใด?" ฉันคิดว่าคำตอบคือ "ไม่" ยกเว้นว่าคุณอนุญาตการดำเนินการปิดบังหลายรายการหรือหลายทวีคูณ ปัญหาคือปัญหาของ "การชน" - ตัวอย่างเช่น "หลงทางข" ในปัญหาข้างต้น xaxxbxxcxลองนึกภาพเราจำเป็นต้องทำเช่นนี้ไปยังหมายเลขเหมือน ทำตามวิธีก่อนหน้านี้คุณจะคิดว่าเราต้องการ {x 2, x {1 + 4 + 16}} = x 42 (oooh - คำตอบทุกอย่าง!) ผลลัพธ์:

00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......

อย่างที่คุณเห็นมันยังใช้งานได้ แต่ "เพียงแค่" พวกเขาสำคัญที่นี่คือมี "ช่องว่างเพียงพอ" ระหว่างบิตที่เราต้องการให้เราสามารถบีบทุกอย่างขึ้น ฉันไม่สามารถเพิ่มบิตที่สี่ d หลังจาก c เพราะฉันจะได้รับอินสแตนซ์ที่ฉันได้รับ c + d บิตอาจมีบิต ...

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

ข้อยกเว้นเดียวที่ฉันนึกได้สำหรับกฎ "ต้องมี (N-1) ระหว่างบิต" คือ: ถ้าคุณต้องการแยกสองบิตที่อยู่ติดกันในต้นฉบับและคุณต้องการเก็บไว้ใน คำสั่งเดียวกันจากนั้นคุณยังสามารถทำมันได้ และสำหรับจุดประสงค์ของกฎ (N-1) พวกเขานับเป็นสองบิต

มีข้อมูลเชิงลึกอื่น ๆ - ได้แรงบันดาลใจจากคำตอบของ @Ternary ด้านล่าง (ดูความคิดเห็นของฉันที่นั่น) สำหรับแต่ละบิตที่น่าสนใจคุณจะต้องมีศูนย์จำนวนมากทางด้านขวาของมันตามที่คุณต้องการพื้นที่สำหรับบิตที่ต้องไปที่นั่น แต่ยังต้องการบิตทางซ้ายมากเนื่องจากมีบิตผลลัพธ์ไปทางซ้าย ดังนั้นหากบิต b สิ้นสุดในตำแหน่ง m ของ n ดังนั้นมันต้องมีศูนย์ m-1 ทางด้านซ้ายและศูนย์ nm ทางด้านขวา โดยเฉพาะอย่างยิ่งเมื่อบิตไม่ได้อยู่ในลำดับเดียวกันในหมายเลขเดิมเหมือนเดิมหลังจากเรียงลำดับใหม่นี่เป็นการปรับปรุงที่สำคัญสำหรับเกณฑ์ดั้งเดิม ซึ่งหมายความว่าตัวอย่างเช่นนั่นคือคำ 16 บิต

a...e.b...d..c..

สามารถเลื่อนเป็น

abcde...........

แม้ว่าจะมีช่องว่างเดียวระหว่าง e และ b สองช่องว่างระหว่าง d และ c สามช่องว่างระหว่างพื้นที่อื่น เกิดอะไรขึ้นกับ N-1 ?? ในกรณีนี้a...eจะกลายเป็น "หนึ่งบล็อก" - พวกเขาจะถูกคูณด้วย 1 เพื่อสิ้นสุดในสถานที่ที่เหมาะสมและดังนั้น "เราได้รับ e ฟรี" สิ่งนี้เป็นจริงสำหรับ b และ d (b ต้องการช่องว่างสามช่องทางด้านขวาและ d ต้องการสามช่องทางด้านซ้ายเหมือนกัน) ดังนั้นเมื่อเราคำนวณจำนวนเวทย์มนตร์เราจะพบว่ามีการซ้ำซ้อน:

a: << 0  ( x 1    )
b: << 5  ( x 32   )
c: << 11 ( x 2048 )
d: << 5  ( x 32   )  !! duplicate
e: << 0  ( x 1    )  !! duplicate

เห็นได้ชัดว่าถ้าคุณต้องการตัวเลขเหล่านี้ในลำดับที่แตกต่างกันคุณจะต้องเว้นวรรคพวกเขาต่อไป เราสามารถจัด(N-1)ระเบียบกฎใหม่: "มันจะทำงานเสมอหากมีช่องว่างอย่างน้อย (N-1) ระหว่างบิตหรือถ้ารู้ลำดับของบิตในผลลัพธ์สุดท้ายแล้วถ้าบิต b สิ้นสุดในตำแหน่ง m ของ n, มันต้องมีศูนย์ m-1 ทางซ้าย, และศูนย์ nm อยู่ทางขวา "

@ เทอร์นารีชี้ให้เห็นว่ากฎนี้ใช้งานไม่ได้เนื่องจากอาจมีการเพิ่มบิตจาก "ทางด้านขวาของพื้นที่เป้าหมาย" - กล่าวคือเมื่อบิตที่เรากำลังค้นหาอยู่นั้นเป็นบิตทั้งหมด ดำเนินการต่อตัวอย่างที่ฉันให้ไว้ข้างต้นด้วยห้าบิตที่อัดแน่นในคำ 16 บิต: ถ้าเราเริ่มต้นด้วย

a...e.b...d..c..

เพื่อความง่ายฉันจะตั้งชื่อตำแหน่งบิต ABCDEFGHIJKLMNOP

คณิตศาสตร์ที่เรากำลังจะทำคือ

ABCDEFGHIJKLMNOP

a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00

จนถึงขณะนี้เราคิดอะไรด้านล่างabcde(ตำแหน่งABCDE) จะไม่เป็นเรื่อง แต่ในความเป็นจริงเป็น @Ternary ชี้ให้เห็นว่าถ้าb=1, c=1, d=1แล้ว(b+c)อยู่ในตำแหน่งที่Gจะทำให้บิตที่จะดำเนินการไปยังตำแหน่งFซึ่งหมายความว่า(d+1)อยู่ในตำแหน่งที่Fจะดำเนินการบิตเข้าE- และของเรา ผลที่ได้คือเสีย โปรดทราบว่าพื้นที่ทางด้านขวาของบิตที่มีความสำคัญน้อยที่สุด ( cในตัวอย่างนี้) ไม่สำคัญเนื่องจากการคูณจะทำให้เกิดช่องว่างภายในด้วยศูนย์จาก beyone บิตที่สำคัญน้อยที่สุด

ดังนั้นเราจำเป็นต้องแก้ไขกฎ (m-1) / (nm) ของเรา หากมีมากกว่าหนึ่งบิตที่มี "แน่นอน (nm) บิตที่ไม่ได้ใช้ไปทางขวา (ไม่นับบิตสุดท้ายในรูปแบบ -" c "ในตัวอย่างด้านบน) จากนั้นเราต้องเสริมความแข็งแกร่งของกฎ - และเราต้อง ทำซ้ำ!

เราต้องดูไม่เพียง แต่จำนวนบิตที่เป็นไปตามเกณฑ์ (nm) เท่านั้น แต่ต้องดูที่ (n-m + 1) และอื่น ๆ ด้วยเราจะเรียกหมายเลข Q0 (ตรงn-mกับบิตถัดไป), Q1 ( n-m + 1), สูงสุด Q (N-1) (n-1) ถ้าอย่างนั้นเราก็เสี่ยงที่จะพกพา

Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
... 

ถ้าคุณดูตรงนี้คุณจะเห็นว่าถ้าคุณเขียนนิพจน์ทางคณิตศาสตร์อย่างง่าย

W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)

และผลที่ได้คือW > 2 * Nแล้วคุณต้องเพิ่มเกณฑ์ RHS (n-m+1)โดยหนึ่งบิต ณ จุดนี้การดำเนินการมีความปลอดภัยตราบใดที่W < 4; หากวิธีนี้ใช้ไม่ได้ผลให้เพิ่มเกณฑ์อีกครั้งหนึ่งเป็นต้น

ฉันคิดว่าการทำตามข้างต้นจะทำให้คุณได้คำตอบที่ตรงไกลยิ่งขึ้น ...


1
ยิ่งใหญ่ ปัญหาที่ลึกซึ้งอีกข้อหนึ่ง: การทดสอบ m-1 / nm ล้มเหลวบางครั้งเนื่องจากการพกพาบิต ลอง a ... b..c ... d - คุณจบลงด้วย b + c ในบิตที่ห้าซึ่งหากพวกเขาทั้งคู่ 1 ทำให้บิตพกพาที่ clobbers d (!)
Ternary

1
upshot: n-1 บิตของพื้นที่ห้ามการกำหนดค่าที่ควรทำงาน (เช่น ... b..c ... d) และ m-1 / nm อนุญาตให้ใช้พื้นที่ที่ไม่ทำงาน (a ... b..c ... ง) ฉันไม่สามารถหาวิธีง่ายๆในการอธิบายลักษณะที่จะใช้งานได้และวิธีใดจะไม่เกิดขึ้น
Ternary

คุณทำได้ดี! ปัญหาการพกพาหมายความว่าเราต้องการพื้นที่เพิ่มขึ้นเล็กน้อยที่ด้านขวาของแต่ละบิตในฐานะ "การป้องกัน" จากภาพรวมแรกหากมีอย่างน้อยสองบิตที่มี nm ต่ำสุดทางขวาคุณจะต้องเพิ่มพื้นที่โดย 1 โดยทั่วไปหากมี P บิตดังกล่าวคุณต้อง log2 (P) บิตเพิ่มเติมไปที่ ด้านขวาของสิ่งที่มีค่าต่ำสุด (mn) ดูเหมือนจะใช่มั้ย
Floris

ความคิดเห็นล่าสุดนั้นง่ายเกินไป ฉันคิดว่าคำตอบที่แก้ไขล่าสุดของฉันแสดงว่า log2 (P) ไม่ใช่แนวทางที่ถูกต้อง @ คำตอบของ Ternary (ด้านล่าง) แสดงให้เห็นอย่างงดงามว่าคุณจะบอกได้อย่างไรว่ามีชุดค่าผสมบิตใดโดยเฉพาะถ้าคุณไม่มีวิธีแก้ปัญหาที่รับประกันได้ - ฉันเชื่อว่างานด้านบนจะมีรายละเอียดมากขึ้น
Floris

1
มันอาจจะเป็นเรื่องบังเอิญ แต่คำตอบนี้ได้รับการยอมรับเมื่อจำนวนผู้ติดตามถึง 127 ถ้าคุณอ่านมาไกลคุณจะยิ้มกับฉัน ...
Floris

154

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

"มี 'mask' ค่าคงที่อยู่ 64 บิตและ 'multiplicand' เช่นนั้นสำหรับ bitvector 64 บิตทั้งหมดในนิพจน์ y = (x & mask) * multiplicand เรามี y.63 == x.63 , y.62 == x.55, y.61 == x.47, ฯลฯ "

หากประโยคนี้เป็นทฤษฎีบทจริง ๆ แล้วมันเป็นความจริงที่ค่าบางอย่างของค่าคงที่ 'mask' และ 'multiplicand' เป็นไปตามคุณสมบัตินี้ ดังนั้นวลีนี้ในแง่ของสิ่งที่นักทฤษฎีบทเข้าใจได้นั่นคืออินพุต SMT-LIB 2:

(set-logic BV)

(declare-const mask         (_ BitVec 64))
(declare-const multiplicand (_ BitVec 64))

(assert
  (forall ((x (_ BitVec 64)))
    (let ((y (bvmul (bvand mask x) multiplicand)))
      (and
        (= ((_ extract 63 63) x) ((_ extract 63 63) y))
        (= ((_ extract 55 55) x) ((_ extract 62 62) y))
        (= ((_ extract 47 47) x) ((_ extract 61 61) y))
        (= ((_ extract 39 39) x) ((_ extract 60 60) y))
        (= ((_ extract 31 31) x) ((_ extract 59 59) y))
        (= ((_ extract 23 23) x) ((_ extract 58 58) y))
        (= ((_ extract 15 15) x) ((_ extract 57 57) y))
        (= ((_ extract  7  7) x) ((_ extract 56 56) y))
      )
    )
  )
)

(check-sat)
(get-model)

และตอนนี้เราจะถามผู้พิสูจน์ทฤษฎีบท Z3 ว่าทฤษฎีนี้หรือไม่:

z3.exe /m /smt2 ExtractBitsThroughAndWithMultiplication.smt2

ผลลัพธ์คือ:

sat
(model
  (define-fun mask () (_ BitVec 64)
    #x8080808080808080)
  (define-fun multiplicand () (_ BitVec 64)
    #x0002040810204081)
)

บิงโก! มันทำซ้ำผลลัพธ์ที่ได้รับในโพสต์ต้นฉบับใน 0.06 วินาที

จากมุมมองที่กว้างกว่านี้เราสามารถมองว่านี่เป็นตัวอย่างของปัญหาการสังเคราะห์โปรแกรมอันดับหนึ่งซึ่งเป็นพื้นที่เริ่มต้นของการวิจัยเกี่ยวกับงานวิจัยที่ตีพิมพ์ไม่กี่ฉบับ การค้นหา"program synthesis" filetype:pdfควรเริ่มต้นให้คุณ


2
ผมประทับใจ! ฉันไม่รู้ว่า "ตรรกะอันดับหนึ่งเหนือทฤษฎีบิตเทียร์" เป็นเรื่องจริงที่ผู้คนศึกษา - นับประสาว่ามันจะให้ผลลัพธ์ที่น่าสนใจ ขอบคุณมากที่แบ่งปันสิ่งนี้
Floris

@AndrewBacker: ใครบางคนสามารถส่องฉันเป็นสิ่งที่มีจุดในสิ่งที่เรียกว่า "เป็นงาน" นี้? ฉันหมายความว่ามันไม่จ่ายอะไรเลย คุณไม่สามารถใช้ชีวิตบนตัวแทนคนเดียวได้ บางทีมันอาจให้คะแนนคุณในการสัมภาษณ์ อาจจะ. หากสถานที่ทำงานดีพอที่จะรับรู้คุณค่าของตัวแทน SO และนั่นไม่ใช่ ...
Reinstate Monica

3
แน่ใจ ดังนั้นยังเป็นเกม (ทุกสิ่งที่มีคะแนน) สำหรับผู้คนจำนวนมาก เพียงแค่ธรรมชาติของมนุษย์เช่นการล่าสัตว์ใน / r / ใหม่เพื่อให้คุณสามารถโพสต์ความคิดเห็นแรกและได้รับกรรม ไม่มีอะไรเลวร้ายเลยตราบใดที่คำตอบยังดีอยู่ ฉันแค่มีความสุขมากที่สามารถเอาชนะเวลาและความพยายามของใครบางคนเมื่อพวกเขามีแนวโน้มที่จะสังเกตเห็นว่ามีคนทำ การให้กำลังใจเป็นเรื่องดี :) และ ... นั่นเป็นความคิดเห็นที่เก่าแก่และยังคงเป็นความจริง ฉันไม่เห็นว่ามันไม่ชัดเจน
Andrew Backer

88

ทุกๆ 1 บิตในตัวคูณใช้เพื่อคัดลอกหนึ่งบิตไปยังตำแหน่งที่ถูกต้อง:

  • 10x0000000000000001มีอยู่แล้วในตำแหน่งที่ถูกต้องเพื่อให้คูณด้วย
  • 2ต้องเลื่อนตำแหน่ง 7 บิตไปทางซ้ายดังนั้นเราจึงคูณด้วย0x0000000000000080(ตั้งค่าบิต 7)
  • 3ต้องเลื่อนตำแหน่ง 14 บิตไปทางซ้ายดังนั้นเราจึงคูณด้วย0x0000000000000400(ตั้งค่าบิต 14)
  • เป็นต้นจนกระทั่ง
  • 8ต้องเลื่อนตำแหน่ง 49 บิตไปทางซ้ายดังนั้นเราจึงคูณด้วย0x0002000000000000(ตั้งค่าบิต 49)

ตัวคูณคือผลรวมของตัวคูณสำหรับแต่ละบิต

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

หมายเหตุว่าบิตอื่น ๆ 0ในจำนวนเดิมจะต้อง ซึ่งสามารถทำได้โดยการปิดบังด้วยการดำเนินการและ


2
คำอธิบายที่ดี! คำตอบสั้น ๆ ของคุณทำให้สามารถหาค่าของ "หมายเลขเวทมนตร์" ได้อย่างรวดเร็ว
Expedito

4
นี่เป็นคำตอบที่ดีที่สุด แต่จะไม่เป็นประโยชน์หากไม่ได้อ่าน (ครึ่งแรก) ของคำตอบของ @ floris ก่อน
Andrew Backer

29

(ฉันไม่เคยเห็นมาก่อนเคล็ดลับนี้ดีมาก!)

ฉันจะขยายบิตในการยืนยันของ Floris ว่าเมื่อแยกnบิตคุณต้องมีn-1ช่องว่างระหว่างบิตที่ไม่ต่อเนื่องกัน:

ความคิดเริ่มต้นของฉัน (เราจะเห็นว่ามันใช้งานไม่ได้ในหนึ่งนาที) ว่าคุณทำได้ดีกว่า: ถ้าคุณต้องการแยกnบิตคุณจะมีการชนกันเมื่อแยก / ขยับบิตiถ้าคุณมีใคร (ไม่ใช่ - ต่อเนื่องกันด้วยบิตi) ในi-1บิตก่อนหน้าหรือn-iบิตต่อมา

ฉันจะยกตัวอย่างเพื่ออธิบาย:

...a..b...c...ใช้งานได้ (ไม่มีใครอยู่ใน 2 บิตหลังจากaนั้นบิตก่อนและบิตหลังbและไม่มีใครอยู่ใน 2 บิตก่อนc):

  a00b000c
+ 0b000c00
+ 00c00000
= abc.....

...a.b....c...ล้มเหลวเพราะbอยู่ใน 2 บิตหลังa(และถูกดึงไปยังจุดอื่นเมื่อเราเปลี่ยนa):

  a0b0000c
+ 0b0000c0
+ 00c00000
= abX.....

...a...b.c...ล้มเหลวเพราะbอยู่ใน 2 บิตก่อนหน้าc(และถูกผลักไปยังจุดอื่นเมื่อเราเปลี่ยนc):

  a000b0c0
+ 0b0c0000
+ b0c00000
= Xbc.....

...a...bc...d... ทำงานได้เนื่องจากบิตต่อเนื่องกัน

  a000bc000d
+ 0bc000d000
+ 000d000000
= abcd000000

แต่เรามีปัญหา หากเราใช้n-iแทนn-1เราอาจมีสถานการณ์ต่อไปนี้: จะเกิดอะไรขึ้นถ้าเรามีการชนกันนอกส่วนที่เราสนใจบางสิ่งที่เราจะปกปิดในตอนท้าย แต่สิ่งที่มีบิตจบลงในช่วงที่ไม่มีหน้ากาก ? (และหมายเหตุ: n-1ความต้องการทำให้แน่ใจว่าสิ่งนี้จะไม่เกิดขึ้นโดยตรวจสอบให้แน่ใจว่าi-1บิตหลังจากช่วงที่ไม่มีหน้ากากของเราชัดเจนเมื่อเราเปลี่ยนiบิตที่สอง)

...a...b..c...d...ความล้มเหลวที่อาจเกิดขึ้นกับ carry-bits cอยู่ในn-1ภายหลังbแต่เป็นn-iไปตามเกณฑ์:

  a000b00c000d
+ 0b00c000d000
+ 00c000d00000
+ 000d00000000
= abcdX.......

แล้วทำไมเราไม่กลับไปที่n-1ข้อกำหนด" บิตของพื้นที่" นั่นล่ะ? เพราะเราทำได้ดีกว่า :

...a....b..c...d.. ล้มเหลวในการทดสอบ " n-1บิตของพื้นที่" แต่ใช้งานได้สำหรับเคล็ดลับการแตกไฟล์ของเรา:

+ a0000b00c000d00
+ 0b00c000d000000
+ 00c000d00000000
+ 000d00000000000
= abcd...0X......

ฉันไม่สามารถขึ้นมาเป็นวิธีที่ดีที่จะอธิบายลักษณะเขตข้อมูลเหล่านี้ที่ไม่ได้มีn-1ช่องว่างระหว่างชิ้นสำคัญ แต่ยังคงทำงานสำหรับการดำเนินงานของเรา อย่างไรก็ตามเนื่องจากเราทราบล่วงหน้าว่าบิตใดที่เราสนใจเราสามารถตรวจสอบตัวกรองของเราเพื่อให้แน่ใจว่าเราจะไม่พบการชนกันของบิต

เปรียบเทียบ(-1 AND mask) * shiftกับผลลัพธ์ทั้งหมดที่คาดไว้-1 << (64-n)(สำหรับผู้ที่ไม่ได้ลงชื่อแบบ 64 บิต)

การเปลี่ยนเวทย์ / การทวีคูณเพื่อแยกบิตของเราจะทำงานหากทั้งสองเท่ากัน


ฉันชอบมัน - คุณพูดถูกแล้วสำหรับแต่ละบิตคุณต้องการเลขศูนย์ทางด้านขวาของมันมากเท่าที่คุณต้องการพื้นที่สำหรับบิตที่ต้องไปที่นั่น แต่ยังต้องการบิตทางซ้ายมากเนื่องจากมีบิตผลลัพธ์ไปทางซ้าย ดังนั้นหากบิตbจบลงในตำแหน่งmของnมันก็ต้องมีm-1ศูนย์ทางซ้ายและn-m-1ศูนย์ทางขวา โดยเฉพาะอย่างยิ่งเมื่อบิตไม่ได้อยู่ในลำดับเดียวกันในหมายเลขเดิมเหมือนเดิมหลังจากเรียงลำดับใหม่นี่เป็นการปรับปรุงที่สำคัญสำหรับเกณฑ์ดั้งเดิม นี้คือความสนุก.
Floris

13

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

เอ็นจิ้นหมากรุกคอมพิวเตอร์จำนวนมากใช้จำนวนเต็ม 64- บิต (เรียกว่า bitboards) เพื่อแสดงชุดชิ้นส่วนต่าง ๆ (1 บิตต่อหนึ่งตารางที่ถูกครอบครอง) สมมติว่าชิ้นส่วนที่เลื่อน (โกง, บิชอป, ราชินี) ในตารางต้นกำเนิดที่แน่นอนสามารถย้ายไปที่Kสี่เหลี่ยมมากที่สุดหากไม่มีชิ้นส่วนที่ปิดกั้นอยู่ การใช้บิตบิต - และKบิตที่กระจัดกระจายเหล่านั้นด้วยบิตบอร์ดของสี่เหลี่ยมที่ถูกครอบครองจะให้Kคำ - บิตเฉพาะที่ฝังอยู่ภายในจำนวนเต็ม 64- บิต

การคูณด้วยเวทมนตร์สามารถใช้ในการแมปKบิตที่กระจัดกระจายเหล่านี้ไปยังKบิตล่างของจำนวนเต็ม 64- บิต Kบิตที่ต่ำกว่าเหล่านี้สามารถใช้ในการทำดัชนีตารางของ bitboards ที่คำนวณล่วงหน้าซึ่งแสดงถึงสแควร์สที่ได้รับอนุญาตที่ชิ้นส่วนบนสแควร์ดั้งเดิมสามารถย้ายไปที่จริงได้ (ดูแลการบล็อกส่วนเป็นต้น)

เอ็นจิ้นหมากรุกทั่วไปที่ใช้วิธีการนี้มี 2 ตาราง (หนึ่งสำหรับ rooks หนึ่งสำหรับ bishops, queens โดยใช้การรวมกันของทั้งสอง) ของ 64 รายการ (หนึ่งต่อตารางกำเนิด) ที่มีผลการคำนวณล่วงหน้าดังกล่าว ทั้งแหล่งที่ได้รับการจัดอันดับสูงสุด ( Houdini ) และเอ็นจิ้นหมากรุกโอเพนซอร์ซ ( Stockfish ) ปัจจุบันใช้วิธีนี้เพื่อประสิทธิภาพที่สูงมาก

การค้นหาตัวคูณวิเศษเหล่านี้ทำได้โดยใช้การค้นหาแบบละเอียด (ปรับให้เหมาะสมกับการตัดต้น) หรือด้วยการทดลองและ erorr (เช่นลองจำนวนเต็ม 64 บิตแบบสุ่มจำนวนมาก) ไม่มีการใช้รูปแบบบิตในระหว่างการเคลื่อนที่ซึ่งไม่สามารถหาค่าคงที่เวทย์มนตร์ได้ อย่างไรก็ตามโดยทั่วไปแล้วเอฟเฟกต์พกพาระดับบิตมีความจำเป็นเมื่อบิตที่ต้องแมปมีดัชนี (เกือบ) ติดกัน

AFAIK วิธีการแก้ปัญหา SAT ทั่วไปโดย @Syzygy ไม่ได้ถูกใช้ในคอมพิวเตอร์หมากรุกและไม่ปรากฏว่ามีทฤษฎีทางการใด ๆ เกี่ยวกับการดำรงอยู่และเอกลักษณ์ของค่าคงที่เวทย์มนตร์ดังกล่าว


ฉันคิดว่าทุกคนที่มีพื้นหลัง CS อย่างเป็นทางการเต็มตัวจะได้เข้าสู่วิธี SAT โดยตรงเมื่อเห็นปัญหานี้ บางทีคน CS จะพบว่าหมากรุกไม่น่าสนใจ? :(
Reinstate Monica

@ KubaOber ส่วนใหญ่เป็นวิธีอื่น ๆ : หมากรุกคอมพิวเตอร์ถูกครอบงำโดยผู้เล่นทวิบิตซึ่งตั้งโปรแกรมใน C หรือแอสเซมบลี ฉันคิดว่ามันทำให้กลัว CS คนจริง :-)
TemplateRex
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.