คุณไปได้สูงแค่ไหน? (ความท้าทายการเข้ารหัส + อัลกอริทึม)


34

ตอนนี้ทุกคนได้พัฒนาความเชี่ยวชาญระดับต่ำในการเขียนโค้ดสำหรับPython ช้าแค่ไหน? (หรือภาษาของคุณเร็วแค่ไหน)และPython ช้าแค่ไหน (ตอนที่ 2) จริงหรือ ถึงเวลาสำหรับความท้าทายที่จะยืดความสามารถของคุณในการปรับปรุงอัลกอริทึม

รหัสต่อไปนี้คำนวณรายการความยาว 9 ตำแหน่งiในรายการนับจำนวนครั้งอย่างน้อยiศูนย์ติดต่อกันของเขาถูกพบเมื่อคำนวณผลิตภัณฑ์ภายในระหว่างและF Sการทำเช่นนี้ว่ามัน iterates มากกว่าทุกรายการที่เป็นไปได้Fของความยาวnและรายชื่อของความยาวSn+m-1

#!/usr/bin/python
import itertools
import operator

n=8
m=n+1
leadingzerocounts = [0]*m
for S in itertools.product([-1,1], repeat = n+m-1):
    for F in itertools.product([-1,1], repeat = n):
        i = 0
        while (i<m and sum(map(operator.mul, F, S[i:i+n])) == 0):
            leadingzerocounts[i] +=1
            i+=1
print leadingzerocounts

ผลลัพธ์คือ

[4587520, 1254400, 347648, 95488, 27264, 9536, 4512, 2128, 1064]

หากคุณเพิ่ม n เป็น 10,12,14,16,18,20 โดยใช้รหัสนี้มันจะกลายเป็นช้าเกินไปอย่างรวดเร็ว

กฎระเบียบ

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

My Machineเวลาของฉันจะทำงานบนเครื่องของฉัน นี่คือการติดตั้ง Ubuntu มาตรฐานบนโปรเซสเซอร์ AMD FX-8350 Eight-Core นี่ก็หมายความว่าฉันต้องสามารถเรียกใช้รหัสของคุณได้ ดังนั้นโปรดใช้ซอฟต์แวร์ฟรีที่มีให้ง่ายและโปรดรวมคำแนะนำทั้งหมดในการรวบรวมและเรียกใช้รหัสของคุณ

สถานะ

  • . n = 12 ใน 49 วินาทีโดย @Fors
  • ชวา n = 16 ใน 3:07 โดย @PeterTaylor
  • C ++ n = 16 ใน 2:21 โดย @ilmale
  • Rpython n = 22 ใน 3:11 โดย @primo
  • ชวา n = 22 ใน 6:56 โดย @PeterTaylor
  • นิม n = 24 ใน 9:28 วินาทีโดย @ReimerBehrends

ผู้ชนะคือ Reimer Behrends พร้อมผลงานที่ Nimrod!

ขณะที่การตรวจสอบการส่งออกสำหรับ n = 22 [12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]ควรจะเป็น


การแข่งขันสิ้นสุดลงแล้ว แต่ ... ฉันจะเสนอ200 คะแนนสำหรับทุกการส่งที่เพิ่ม n โดย 2 (ภายใน 10 นาทีบนคอมพิวเตอร์ของฉัน) จนกว่าฉันจะหมดคะแนน ข้อเสนอนี้จะเปิดตลอดไป


1
"ฉันขอสงวนสิทธิ์ที่จะไม่ทดสอบโค้ดที่ใช้เวลามากกว่าสองสามนาที" > คุณควรระบุเวลาที่แน่นอนในเครื่องของคุณมิฉะนั้นคำถามนี้จะไม่มีเกณฑ์การชนะอย่างมีวัตถุประสงค์
ace_HongKong การพึ่งพากัน

14
ฉันชอบความท้าทายเหล่านี้ "เพิ่มความเร็ว" หากคุณกำลังสร้างผลิตภัณฑ์เชิงพาณิชย์ด้วยสิ่งเหล่านี้คุณจะต้องเป็นหนึ่งในผลิตภัณฑ์ที่รวดเร็วและคุณก็เป็นอัจฉริยะที่ชั่วร้ายด้วย
Rainbolt

1
บางทีชื่อเรื่องที่ให้ข้อมูลมากกว่าจะดึงดูดความสนใจได้
TheDoctor

8
หากเรายังคงเผชิญกับความท้าทายนี้ต่อไปฉันคิดว่าอย่างน้อยที่สุดเราควรพยายามแก้ปัญหาที่แตกต่างเพื่อให้มันน่าสนใจ
grovesNL

2
@Claudiu CPU ของเขามี 8 ฟิสิคัลคอร์ แต่การดึงข้อมูล / ถอดรหัสและหน่วย FPU จะถูกแชร์ระหว่างคอร์ ดังนั้นเมื่อคอขวดอยู่บนหนึ่งในพื้นที่เหล่านั้นมันจะทำงานเหมือน Quadcore ใช้ตรรกะจำนวนเต็มในทางที่ผิดและหลีกเลี่ยงรหัสขนาดใหญ่และมันก็เหมือนกับ 8-core มากกว่า
Stefan

คำตอบ:


20

นิมรอด (N = 22)

import math, locks

const
  N = 20
  M = N + 1
  FSize = (1 shl N)
  FMax = FSize - 1
  SStep = 1 shl (N-1)
  numThreads = 16

type
  ZeroCounter = array[0..M-1, int]
  ComputeThread = TThread[int]

var
  leadingZeros: ZeroCounter
  lock: TLock
  innerProductTable: array[0..FMax, int8]

proc initInnerProductTable =
  for i in 0..FMax:
    innerProductTable[i] = int8(countBits32(int32(i)) - N div 2)

initInnerProductTable()

proc zeroInnerProduct(i: int): bool =
  innerProductTable[i] == 0

proc search2(lz: var ZeroCounter, s, f, i: int) =
  if zeroInnerProduct(s xor f) and i < M:
    lz[i] += 1 shl (M - i - 1)
    search2(lz, (s shr 1) + 0, f, i+1)
    search2(lz, (s shr 1) + SStep, f, i+1)

when defined(gcc):
  const
    unrollDepth = 1
else:
  const
    unrollDepth = 4

template search(lz: var ZeroCounter, s, f, i: int) =
  when i < unrollDepth:
    if zeroInnerProduct(s xor f) and i < M:
      lz[i] += 1 shl (M - i - 1)
      search(lz, (s shr 1) + 0, f, i+1)
      search(lz, (s shr 1) + SStep, f, i+1)
  else:
    search2(lz, s, f, i)

proc worker(base: int) {.thread.} =
  var lz: ZeroCounter
  for f in countup(base, FMax div 2, numThreads):
    for s in 0..FMax:
      search(lz, s, f, 0)
  acquire(lock)
  for i in 0..M-1:
    leadingZeros[i] += lz[i]*2
  release(lock)

proc main =
  var threads: array[numThreads, ComputeThread]
  for i in 0 .. numThreads-1:
    createThread(threads[i], worker, i)
  for i in 0 .. numThreads-1:
    joinThread(threads[i])

initLock(lock)
main()
echo(@leadingZeros)

รวบรวมกับ

nimrod cc --threads:on -d:release count.nim

(Nimrod สามารถดาวน์โหลดได้ที่นี่ )

สิ่งนี้จะทำงานในเวลาที่กำหนดสำหรับ n = 20 (และสำหรับ n = 18 เมื่อใช้เพียงเธรดเดียวโดยใช้เวลาประมาณ 2 นาทีในกรณีหลัง)

อัลกอริทึมใช้การค้นหาแบบเรียกซ้ำการตัดทอนการค้นหาเมื่อใดก็ตามที่พบผลิตภัณฑ์ภายในที่ไม่เป็นศูนย์ นอกจากนี้เรายังลดพื้นที่การค้นหาลงครึ่งหนึ่งด้วยการสังเกตว่าสำหรับเวกเตอร์คู่ใด ๆ(F, -F)เราเพียงแค่ต้องพิจารณาอันใดอันหนึ่งเพราะอีกอันหนึ่งผลิตผลิตภัณฑ์ภายในชุดเดียวกันแน่นอน (โดยปฏิเสธSด้วย)

การติดตั้งใช้สิ่งอำนวยความสะดวก metaprogramming ของ Nimrod ในการเปิด / ปิดอินไลน์ระดับแรก ๆ ของการค้นหาแบบเรียกซ้ำ สิ่งนี้ช่วยประหยัดเวลาเล็กน้อยเมื่อใช้ gcc 4.8 และ 4.9 เป็นแบ็กเอนด์ของนิมโรดและจำนวนที่เหมาะสมสำหรับเสียงดังกราว

พื้นที่การค้นหาอาจถูกตัดทิ้งเพิ่มเติมโดยการสังเกตว่าเราเพียงแค่ต้องพิจารณาค่าของ S ที่แตกต่างกันในตำแหน่ง N แรกจำนวนเท่ากันจากทางเลือกของ F อย่างไรก็ตามความซับซ้อนหรือความต้องการหน่วยความจำของที่ไม่ขยายสำหรับค่าขนาดใหญ่ ของ N เนื่องจากว่าลูปบอดี้ถูกข้ามอย่างสมบูรณ์ในกรณีเหล่านั้น

การสร้างตารางที่ผลิตภัณฑ์ด้านในเป็นศูนย์ดูเหมือนจะเร็วกว่าการใช้ฟังก์ชันการนับบิตใด ๆ ในลูป เห็นได้ชัดว่าการเข้าถึงตารางมีสถานที่ที่ดีพอสมควร

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

ตัวอย่างผลลัพธ์:

N = 16:

@[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600]

N = 18:

@[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]

N = 20:

@[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]

สำหรับวัตถุประสงค์ในการเปรียบเทียบอัลกอริทึมกับการใช้งานอื่น N = 16 ใช้เวลาประมาณ 7.9 วินาทีในเครื่องของฉันเมื่อใช้เธรดเดี่ยวและ 2.3 วินาทีเมื่อใช้สี่คอร์

N = 22 ใช้เวลาประมาณ 15 นาทีบนเครื่อง 64-core กับ GCC 4.4.6 เป็นแบ็กเอนด์นิมและล้นจำนวนเต็ม 64 บิตในleadingZeros[0](อาจจะไม่ได้คนที่ไม่ได้ลงชื่อไม่ได้มองไปที่มัน)


อัปเดต: ฉันพบที่ว่างสำหรับการปรับปรุงเพิ่มเติมอีกสองสามข้อ อันดับแรกสำหรับค่าที่กำหนดFเราสามารถระบุ 16 รายการแรกของSเวกเตอร์ที่เกี่ยวข้องได้อย่างแม่นยำเพราะพวกมันต้องแตกต่างกันในN/2สถานที่ ดังนั้นเราจึง precompute รายการเวกเตอร์บิตของขนาดNที่มีN/2บิตชุดและใช้เหล่านี้จะได้รับส่วนเริ่มต้นของจากSF

ประการที่สองเราสามารถปรับปรุงการค้นหาแบบเรียกซ้ำโดยการสังเกตว่าเรารู้คุณค่าของเสมอF[N](เนื่องจาก MSB เป็นศูนย์ในการแทนค่าบิต) สิ่งนี้ช่วยให้เราสามารถทำนายได้อย่างแม่นยำว่าสาขาใดที่เรารับเงินคืนจากผลิตภัณฑ์ภายใน ในขณะที่จริงจะช่วยให้เราเปลี่ยนการค้นหาทั้งหมดเป็นวนซ้ำแบบซ้ำ ๆ ที่เกิดขึ้นจริงจะทำให้การคาดคะเนสาขาแตกหักเล็กน้อยดังนั้นเราจึงรักษาระดับสูงสุดไว้ในรูปแบบดั้งเดิม เรายังคงประหยัดเวลาส่วนใหญ่โดยการลดจำนวนสาขาที่เราทำ

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

การเร่งความเร็วโดยรวมอยู่ระหว่างปัจจัย x3 และ x4 N = 22 ยังคงต้องการมากกว่าแปดแกนที่จะทำงานในอายุต่ำกว่า 10 นาที แต่บนเครื่อง 64-core ก็ตอนนี้ลงไปประมาณสี่นาที (ด้วยการnumThreadsชนขึ้นตามลำดับ) ฉันไม่คิดว่าจะมีพื้นที่สำหรับการปรับปรุงมากขึ้นหากไม่มีอัลกอริทึมที่แตกต่างกัน

N = 22:

@[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]

อัปเดตอีกครั้งใช้ประโยชน์จากการลดพื้นที่การค้นหาที่เป็นไปได้เพิ่มเติม ทำงานในเวลาประมาณ 9:49 นาทีสำหรับ N = 22 บนเครื่อง quadcore ของฉัน

การปรับปรุงครั้งสุดท้าย (ฉันคิดว่า) คลาสความเท่าเทียมที่ดีกว่าสำหรับตัวเลือกของ F ตัด runtime สำหรับ N = 22 ลงไปที่3:19 นาที 57 วินาที (แก้ไข: ฉันรันโดยไม่ตั้งใจด้วยด้ายเพียงอันเดียว) บนเครื่องของฉัน

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

อีกหนึ่งการปรับปรุงเพื่อรองรับจำนวนเต็ม 128- บิตสำหรับข้อมูลที่สะสม เพื่อรวบรวมกับ 128 จำนวนเต็มบิตคุณจะต้องlongint.nimจากที่นี่-d:use128bitและจะรวบรวมกับ N = 24 ยังคงใช้เวลานานกว่า 10 นาที แต่ฉันได้รวมผลลัพธ์ด้านล่างสำหรับผู้ที่สนใจ

N = 24:

@[761152247121980686336, 122682715414070296576, 19793870419291799552, 3193295704340561920, 515628872377565184, 83289931274780672, 13484616786640896, 2191103969198080, 359662314586112, 60521536552960, 10893677035520, 2293940617216, 631498735616, 230983794688, 102068682752, 48748969984, 23993655296, 11932487680, 5955725312, 2975736832, 1487591936, 743737600, 371864192, 185931328, 92965664]

import math, locks, unsigned

when defined(use128bit):
  import longint
else:
  type int128 = uint64 # Fallback on unsupported architectures
  template toInt128(x: expr): expr = uint64(x)

const
  N = 22
  M = N + 1
  FSize = (1 shl N)
  FMax = FSize - 1
  SStep = 1 shl (N-1)
  numThreads = 16

type
  ZeroCounter = array[0..M-1, uint64]
  ZeroCounterLong = array[0..M-1, int128]
  ComputeThread = TThread[int]
  Pair = tuple[value, weight: int32]

var
  leadingZeros: ZeroCounterLong
  lock: TLock
  innerProductTable: array[0..FMax, int8]
  zeroInnerProductList = newSeq[int32]()
  equiv: array[0..FMax, int32]
  fTable = newSeq[Pair]()

proc initInnerProductTables =
  for i in 0..FMax:
    innerProductTable[i] = int8(countBits32(int32(i)) - N div 2)
    if innerProductTable[i] == 0:
      if (i and 1) == 0:
        add(zeroInnerProductList, int32(i))

initInnerProductTables()

proc ror1(x: int): int {.inline.} =
  ((x shr 1) or (x shl (N-1))) and FMax

proc initEquivClasses =
  add(fTable, (0'i32, 1'i32))
  for i in 1..FMax:
    var r = i
    var found = false
    block loop:
      for j in 0..N-1:
        for m in [0, FMax]:
          if equiv[r xor m] != 0:
            fTable[equiv[r xor m]-1].weight += 1
            found = true
            break loop
        r = ror1(r)
    if not found:
      equiv[i] = int32(len(fTable)+1)
      add(fTable, (int32(i), 1'i32))

initEquivClasses()

when defined(gcc):
  const unrollDepth = 4
else:
  const unrollDepth = 4

proc search2(lz: var ZeroCounter, s0, f, w: int) =
  var s = s0
  for i in unrollDepth..M-1:
    lz[i] = lz[i] + uint64(w)
    s = s shr 1
    case innerProductTable[s xor f]
    of 0:
      # s = s + 0
    of -1:
      s = s + SStep
    else:
      return

template search(lz: var ZeroCounter, s, f, w, i: int) =
  when i < unrollDepth:
    lz[i] = lz[i] + uint64(w)
    if i < M-1:
      let s2 = s shr 1
      case innerProductTable[s2 xor f]
      of 0:
        search(lz, s2 + 0, f, w, i+1)
      of -1:
        search(lz, s2 + SStep, f, w, i+1)
      else:
        discard
  else:
    search2(lz, s, f, w)

proc worker(base: int) {.thread.} =
  var lz: ZeroCounter
  for fi in countup(base, len(fTable)-1, numThreads):
    let (fp, w) = fTable[fi]
    let f = if (fp and (FSize div 2)) == 0: fp else: fp xor FMax
    for sp in zeroInnerProductList:
      let s = f xor sp
      search(lz, s, f, w, 0)
  acquire(lock)
  for i in 0..M-1:
    let t = lz[i].toInt128 shl (M-i).toInt128
    leadingZeros[i] = leadingZeros[i] + t
  release(lock)

proc main =
  var threads: array[numThreads, ComputeThread]
  for i in 0 .. numThreads-1:
    createThread(threads[i], worker, i)
  for i in 0 .. numThreads-1:
    joinThread(threads[i])

initLock(lock)
main()
echo(@leadingZeros)

ผลลัพธ์ที่มี N = 22 คือ 12410090985684467712 ซึ่งใช้เวลา 63.42 บิตจึงเหมาะกับบิต 64 ที่ไม่ได้ลงชื่อ
สเตฟาน

2
คุณยกระดับบาร์อย่างน่าประทับใจอย่างแน่นอน

1
ดีใจที่ได้เห็นคนที่ใช้นิมรอด :)
cjfaure

@Stefan ตัวช่วยสร้างการเข้ารหัสของคุณอาจใช้วิธีนี้ต่ำกว่า 10 นาทีสำหรับ N = 22 หรือไม่

ฉันลอง N = 22 ซึ่งยุติลงหลังจากสองสามชั่วโมง แต่มันทำให้ฉัน [-6036653088025083904, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680] ซึ่งดูเหมือนว่าจะเป็นข้อผิดพลาดมากเกินไป ฉันรู้ว่าไม่มีนิมรอด แต่เป็นไปได้หรือไม่ที่จะใช้ ints ที่ไม่ได้ลงชื่อเพื่อแก้ปัญหานี้

11

Java ( n=22?)

ฉันคิดว่าคำตอบส่วนใหญ่ที่ทำได้ดีกว่าn=16ใช้วิธีการที่คล้ายกันนี้แม้ว่าพวกเขาจะแตกต่างกันใน symmetries พวกเขาใช้ประโยชน์และวิธีที่พวกเขาแบ่งงานระหว่างหัวข้อ

เวกเตอร์ที่กำหนดไว้ในคำถามที่อาจถูกแทนที่ด้วยสตริงบิตและสินค้าภายในกับ XORing หน้าต่างที่ทับซ้อนกันและการตรวจสอบว่ามีตรงn/2บิตชุด (และด้วยเหตุนี้n/2บิตเคลียร์) มีn! / ((n/2)!)(สัมประสิทธิ์ทวินามกลาง) สายของnบิตกับn/2บิตตั้ง (ที่ฉันเรียกว่าสตริงที่สมดุล ) ดังนั้นสำหรับการใดก็ตามให้Fมีหน้าต่างจำนวนมากSที่ให้ผลิตภัณฑ์ภายในเป็นศูนย์ ยิ่งไปกว่านั้นการกระทำของการเลื่อนSไปตามทางหนึ่งและตรวจสอบว่าเรายังสามารถหาบิตที่เข้ามาซึ่งทำให้ผลิตภัณฑ์ภายในเป็นศูนย์สอดคล้องกับการค้นหาขอบในกราฟที่มีโหนดเป็นหน้าต่างและขอบที่เชื่อมโยงโหนดuกับโหนดvที่n-1บิตแรกเป็นคนสุดท้ายn-1uบิต

ตัวอย่างเช่นด้วยn=6และF=001001เราจะได้รับกราฟนี้:

กราฟสำหรับ F = 001001

และF=001011เราได้กราฟนี้มา:

กราฟสำหรับ F = 001011

จากนั้นเราก็ต้องนับสำหรับแต่ละiจาก0การnวิธีการหลายเส้นทางของความยาวมีข้อสรุปในช่วงกราฟสำหรับทุกi Fฉันคิดว่าพวกเราส่วนใหญ่ใช้การค้นหาในเชิงลึกก่อน

โปรดทราบว่ากราฟกระจัดกระจาย: ง่ายต่อการพิสูจน์ว่าแต่ละโหนดมีระดับไม่เกิน 1 และระดับไม่เกินหนึ่งโหนด นั่นก็หมายความว่าโครงสร้างที่เป็นไปได้เท่านั้นคือโซ่แบบเรียบง่ายและห่วงแบบง่าย สิ่งนี้ทำให้ DFS ง่ายขึ้นเล็กน้อย

ฉันใช้ประโยชน์จากสมมาตรสองสามอัน: สตริงที่สมดุลจะถูกปิดภายใต้การผกผันบิต (การ~ดำเนินการในหลายภาษาจากตระกูล ALGOL) และภายใต้การหมุนบิตดังนั้นเราจึงสามารถจัดกลุ่มค่าFที่เกี่ยวข้องกับการดำเนินการเหล่านี้ ครั้งหนึ่ง

public class CodeGolf26459v8D implements Runnable {
    private static final int NUM_THREADS = 8;

    public static void main(String[] args) {
        v8D(22);
    }

    private static void v8D(int n) {
        int[] bk = new int[1 << n];
        int off = 0;
        for (int i = 0; i < bk.length; i++) {
            bk[i] = Integer.bitCount(i) == n/2 ? off++ : -1;
        }

        int[] fwd = new int[off];
        for (int i = 0; i < bk.length; i++) {
            if (bk[i] >= 0) fwd[bk[i]] = i;
        }

        CodeGolf26459v8D[] runners = new CodeGolf26459v8D[NUM_THREADS];
        Thread[] threads = new Thread[runners.length];
        for (int i = 0; i < runners.length; i++) {
            runners[i] = new CodeGolf26459v8D(n, i, runners.length, bk, fwd);
            threads[i] = new Thread(runners[i]);
            threads[i].start();
        }

        try {
            for (int i = 0; i < threads.length; i++) threads[i].join();
        }
        catch (InterruptedException ie) {
            throw new RuntimeException("This shouldn't be reachable", ie);
        }

        long surviving = ((long)fwd.length) << (n - 1);
        for (int i = 0; i <= n; i++) {
            for (CodeGolf26459v8D runner : runners) surviving -= runner.survival[i];
            System.out.print(i == 0 ? "[" : ", ");
            java.math.BigInteger result = new java.math.BigInteger(Long.toString(surviving));
            System.out.print(result.shiftLeft(n + 1 - i));
        }
        System.out.println("]");
    }

    public final int n;
    protected final int id;
    protected final int numRunners;
    private final int[] bk;
    private final int[] fwd;

    public long[] survival;

    public CodeGolf26459v8D(int n, int id, int numRunners, int[] bk, int[] fwd) {
        this.n = n;
        this.id = id;
        this.numRunners = numRunners;

        this.bk = bk;
        this.fwd = fwd;
    }

    private int dfs2(int[] graphShape, int flip, int i) {
        if (graphShape[i] != 0) return graphShape[i];

        int succ = flip ^ (fwd[i] << 1);
        if (succ >= bk.length) succ ^= bk.length + 1;

        int j = bk[succ];
        if (j == -1) return graphShape[i] = 1;

        graphShape[i] = n + 1; // To detect cycles
        return graphShape[i] = dfs2(graphShape, flip, j) + 1;
    }

    @Override
    public void run() {
        int n = this.n;
        int[] bk = this.bk;
        int[] fwd = this.fwd;

        // NB The initial count is approx 2^(2n - 1.33 - 0.5 lg n)
        // For n=18 we overflow 32-bit
        // 64-bit is good up to n=32.
        long[] survival = new long[n + 1];
        boolean[] visited = new boolean[1 << (n - 1)];
        int th = 0;
        for (int f = 0; f < visited.length; f++) {
            if (visited[f]) continue;

            int m = 1, g = f;
            while (true) {
                visited[g] = true;
                int ng = g << 1;
                if ((ng >> (n - 1)) != 0) ng ^= (1 << n) - 1;
                if (ng == f) break;
                m++;
                g = ng;
            }

            if (th++ % numRunners != id) continue;

            int[] graphShape = new int[fwd.length];
            int flip = (f << 1) ^ f;
            for (int i = 0; i < graphShape.length; i++) {
                int life = dfs2(graphShape, flip, i);
                if (life <= n) survival[life] += m;
            }
        }

        this.survival = survival;
    }
}

ใน 2.5GHz Core 2 ของฉันฉันได้รับ

# n=18
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]

real    0m3.131s
user    0m10.133s
sys     0m0.380s

# n=20
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]

real    1m8.706s
user    4m20.980s
sys     0m0.564s

# n=22
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]

real    20m10.654s
user    76m53.880s
sys     0m6.852s

เนื่องจากคอมพิวเตอร์ของ Lembik มี 8 คอร์และดำเนินการโปรแกรมเธรดเดี่ยวของฉันก่อนหน้านี้สองครั้งเร็วเท่ากับของฉันฉันมองในแง่ดีว่ามันจะทำงานn=22ในเวลาน้อยกว่า 8 นาที


07:17! ดีมาก. คุณจะอธิบายวิธีการของคุณอีกเล็กน้อยหรือไม่?

6

C

มันเป็นเพียงการปรับใช้ขั้นตอนวิธีในคำถาม สามารถจัดการได้n=12ภายในเวลาที่กำหนด

#include <stdio.h>
#include <inttypes.h>

#define n 12
#define m (n + 1)

int main() {
    int i;
    uint64_t S, F, o[m] = {0};
    for (S = 0; S < (1LLU << (n + m - 1)); S += 2)
        for (F = 0; F < (1 << (n - 1)); F++)
            for (i = 0; i < m; i++)
                if (__builtin_popcount(((S >> i) & ((1 << n) - 1)) ^ F) == n >> 1)
                    o[i] += 4;
                else
                    break;
    for (i = 0; i < m; i++)
        printf("%" PRIu64 " ", o[i]);
    return 0;
}

ทดสอบการทำงานn=12รวมถึงการรวบรวม:

$ clang -O3 -march=native -fstrict-aliasing -ftree-vectorize -Wall fast.c
$ time ./a.out 
15502147584 3497066496 792854528 179535872 41181184 9826304 2603008 883712 381952 177920 85504 42560 21280 
real    0m53.266s
user    0m53.042s
sys     0m0.068s
$

หมายเหตุ: ผมเพิ่งเปิดสมองของฉันและใช้ combinatorics n! / ((n / 2)!)^2 * 2^(n + m - 1)บางอย่างง่ายในการคำนวณว่าค่าแรกจะเป็น ดูเหมือนว่าฉันจะต้องมีวิธีแก้ปัญหาพีชคณิตอย่างสมบูรณ์กับปัญหานี้


ฉันได้รับคำเตือนมากมายเมื่อรวบรวม ลอง gcc -Wall -Wextra Fors.c -o Fors

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

มันบ่นเกี่ยวกับ Fors.c: 13: 17: คำเตือน: แนะนำวงเล็บรอบ '-' ในตัวถูกดำเนินการของ '&' [- วงเล็บ] (สองครั้ง) และเตือน: รูปแบบ '% llu' คาดว่าอาร์กิวเมนต์ประเภท 'int long unsigned ยาว 'แต่อาร์กิวเมนต์ 2 มีประเภท' uint64_t '[-Wformat =] ในความเป็นจริงเสียงดังบ่นเกี่ยวกับคำสั่ง printf เกินไปสำหรับฉัน

ด้วยการเปลี่ยนแปลงล่าสุด GCC ไม่ควรทิ้งข้อความเตือนใด ๆ
Fors

มันยังคงบ่นเกี่ยวกับ Fors.c: 13: 49: คำเตือน: แนะนำวงเล็บรอบเลขคณิตในตัวถูกดำเนินการของ '^' [- วงเล็บ] แต่ในข่าวที่เลวร้ายยิ่ง ... ใช้เวลานานกว่า 10 นาทีในเครื่องของฉัน

5

ชวา n=16

สำหรับค่าใดก็ตามที่ระบุFมี\binom{n}{n/2}เวกเตอร์ซึ่งมีผลคูณภายในเป็นศูนย์ ดังนั้นเราสามารถสร้างกราฟที่มีจุดยอดเป็นเวกเตอร์ที่เข้าคู่กันและมีขอบตรงกับการเลื่อนของSแล้วเราก็ต้องนับเส้นทางที่มีความยาวจนถึงnกราฟ

ฉันไม่ได้ลองแบบ microoptimising สิ่งนี้ด้วยการแทนที่ conditionals ด้วยการทำงานแบบ bitwise แต่การเพิ่มขึ้นสองเท่าของnเวลาทำงานเพิ่มขึ้นประมาณ 16 เท่าดังนั้นมันจะไม่สร้างความแตกต่างมากนักยกเว้นว่าฉันค่อนข้างใกล้เคียงกับเกณฑ์ บนเครื่องของฉันฉันไม่ได้

public class CodeGolf26459 {

    public static void main(String[] args) {
        v3(16);
    }

    // Order of 2^(2n-1) * n ops
    private static void v3(int n) {
        long[] counts = new long[n+1];
        int mask = (1 << n) - 1;
        for (int f = 0; f < (1 << (n-1)); f++) {
            // Find adjacencies
            long[] subcounts = new long[1 << n];
            for (int g = 0; g < (1 << n); g++) {
                subcounts[g] = Integer.bitCount(f ^ g) == n/2 ? 2 : -1;
            }

            for (int round = 0; round <= n; round++) {
                long count = 0;
                // Extend one bit.
                long[] next = new long[1 << n];
                for (int i = 0; i < (1 << n); i++) {
                    long s = subcounts[i];
                    if (s == -1) next[i] = -1;
                    else {
                        count += s;
                        int j = (i << 1) & mask;
                        if (subcounts[j] >= 0) next[j] += s;
                        if (subcounts[j + 1] >= 0) next[j + 1] += s;
                    }
                }
                counts[round] += count << (n - round);
                subcounts = next;
            }
        }

        System.out.print("[");
        for (long count : counts) System.out.print(count+", ");
        System.out.println("]");
    }
}

ใน 2.5GHz Core 2 ของฉันฉันได้รับ

$ javac CodeGolf26459.java && time java -server CodeGolf26459 
[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600, ]

real    6m2.663s
user    6m4.631s
sys     0m1.580s

Piggybacking เนื่องจากฉันไม่ต้องการใช้โซลูชันของตัวเองในตอนนี้ แต่ละจุดสุดยอดจะมีตัวตายตัวแทนไม่เกินหนึ่งตัวดังนั้นคุณจึงไม่จำเป็นต้องใช้อาร์เรย์ ในการวนซ้ำอย่างมีประสิทธิภาพกับการรวมกันของfจุดเริ่มต้นให้วนซ้ำทั้งหมดf_xor_gด้วยn/2บิตที่ตั้งไว้อย่างแน่นอน สำหรับแต่ละเหล่านี้ย้ำไปทั้งหมดและใช้เวลาf g = f ^ f_xor_g
David Eisenstat

@ David ฉันรู้และเวอร์ชัน 7 ของฉันทำ n = 18 ในหนึ่งนาทีบนเน็ตบุ๊ก Atom ของฉัน แต่ฉันไม่สามารถโพสต์ได้จนกว่าฉันจะกลับจากวันหยุด
Peter Taylor

4

RPython, N = 22 ~ 3: 23

แบบมัลติเธรดโดยใช้โคตรแบบเรียกซ้ำแบบไม่มีสแต็ก โปรแกรมยอมรับอาร์กิวเมนต์บรรทัดคำสั่งสองข้อ: N และจำนวนเธรดผู้ทำงาน

from time import sleep

from rpython.rlib.rthread import start_new_thread, allocate_lock
from rpython.rlib.rarithmetic import r_int64, build_int, widen
from rpython.rlib.rbigint import rbigint

r_int8 = build_int('r_char', True, 8)

class ThreadEnv:
  __slots__ = ['n', 'counts', 'num_threads',
               'v_range', 'v_num', 'running', 'lock']

  def __init__(self):
    self.n = 0
    self.counts = [rbigint.fromint(0)]
    self.num_threads = 0
    self.v_range = [0]
    self.v_num = 0
    self.running = 0
    self.lock = None

env = ThreadEnv()

bt_bits = 12
bt_mask = (1<<bt_bits)-1
# computed compile time
bit_table = [r_int8(0)]
for i in xrange(1,1<<bt_bits):
  bit_table += [((i&1)<<1) + bit_table[i>>1]]

def main(argv):
  argc = len(argv)
  if argc < 2 or argc > 3:
    print 'Usage: %s N [NUM_THREADS=2]'%argv[0]
    return 1

  if argc == 3:
    env.num_threads = int(argv[2])
  else:
    env.num_threads = 2

  env.n = int(argv[1])
  env.counts = [rbigint.fromint(0)]*env.n
  env.lock = allocate_lock()

  v_range = []
  v_max = 1<<(env.n-1)
  v_num = 0
  v = (1<<(env.n>>1))-1
  while v < v_max:
    v_num += 1
    v_range += [v]
    if v&1:
      # special case odd v
      s = (v+1)&-v
      v ^= s|(s>>1)
    else:
      s = v&-v
      r = v+s
      # s is at least 2, skip two iterations
      i = 3
      s >>= 2
      while s:
        i += 1
        s >>= 1
      v = r|((v^r)>>i)
  env.v_range = v_range
  env.v_num = v_num

  for i in xrange(env.num_threads-1):
    start_new_thread(run,())

  # use the main process as a worker
  run()

  # wait for any laggers
  while env.running:
    sleep(0.05)

  result = []
  for i in range(env.n):
    result += [env.counts[i].lshift(env.n-i+3).str()]
  result += [env.counts[env.n-1].lshift(3).str()]
  print result
  return 0

def run():
  with env.lock:
    v_start = env.running
    env.running += 1

  n = env.n
  counts = [r_int64(0)]*n
  mask = (1<<n)-1
  v_range = env.v_range
  v_num = env.v_num
  z_count = 1<<(n-2)

  for i in xrange(v_start, v_num, env.num_threads):
    v = v_range[i]
    counts[0] += z_count
    counts[1] += v_num
    r = v^(v<<1)
    for w in v_range:
      # unroll counts[2] for speed
      # ideally, we could loop over x directly,
      # rather than over all v, only to throw the majority away
      # there's a 2x-3x speed improvement to be had here...
      x = w^r
      if widen(bit_table[x>>bt_bits]) + widen(bit_table[x&bt_mask]) == n:
        counts[2] += 1
        x, y = v, x
        o, k = 2, 3
        while k < n:
          # x = F ^ S
          # y = F ^ (S<<1)
          o = k
          z = (((x^y)<<1)^y)&mask
          # z is now F ^ (S<<2), possibly xor 1
          # what S and F actually are is of no consequence

          # the compiler hint `widen` let's the translator know
          # to store the result as a native int, rather than a signed char
          bt_high = widen(bit_table[z>>bt_bits])
          if bt_high + widen(bit_table[z&bt_mask]) == n:
            counts[k] += 1
            x, y = y, z
            k += 1
          elif bt_high + widen(bit_table[(z^1)&bt_mask]) == n:
            counts[k] += 1
            x, y = y, z^1
            k += 1
          else: k = n

  with env.lock:
    for i in xrange(n):
      env.counts[i] = env.counts[i].add(rbigint.fromrarith_int(counts[i]))
    env.running -= 1

def target(*args):
  return main, None

เพื่อรวบรวม

สร้างโคลนในพื้นที่เก็บข้อมูล PyPyโดยใช้ Mercurial คอมไพล์หรืออะไรก็ตามที่คุณต้องการ พิมพ์คาถาต่อไปนี้ (สมมติว่ามีชื่อสคริปต์ด้านบนconvolution-high.py):

$ pypy %PYPY_REPO%/rpython/bin/rpython --thread convolution-high.py

โดยที่%PYPY_REPO%แทนตัวแปรสภาพแวดล้อมที่ชี้ไปยังที่เก็บที่คุณเพิ่งโคลน การรวบรวมใช้เวลาประมาณหนึ่งนาที


ตัวอย่างกำหนดเวลา

N = 16, 4 กระทู้:

$ timeit convolution-high-c 16 4
[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600]
Elapsed Time:     0:00:00.109
Process Time:     0:00:00.390

N = 18, 4 กระทู้:

$ timeit convolution-high-c 18 4
[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]
Elapsed Time:     0:00:01.250
Process Time:     0:00:04.937

N = 20, 4 กระทู้:

$ timeit convolution-high-c 20 4
[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]
Elapsed Time:     0:00:15.531
Process Time:     0:01:01.328

N = 22, 4 กระทู้:

$ timeit convolution-high-c 22 4
[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]
Elapsed Time:     0:03:23.156
Process Time:     0:13:25.437

09:26 ยินดีต้อนรับสู่ 22 ลูกเรือ :)

ฉันไม่แน่ใจว่าทำไม แต่รุ่นใหม่ของคุณไม่เร็วสำหรับฉัน ยังประมาณ 9:30 เมื่อฉันทำเวลา. / primo-c 22 8

@ Lembik ที่เข้าใจได้ง่ายหากการแบ่งโดยเร็วประมาณ 3 กะขวา (3 = ผลรวม {(n + 1) / (2 ^ n)}, n = 1..infty) สำหรับสถาปัตยกรรมรับรองฉันคิดว่าอาจเป็นกรณี แต่ในการแบ่งของฉันจะช้าลงอย่างเห็นได้ชัด ขอขอบคุณที่สละเวลาในการทดสอบ :)
พรีโม่

3

Python 3.3, N = 20, 3.5 นาที

Disclaimer: ความตั้งใจของผมคือไม่โพสต์นี้เป็นคำตอบของตัวเองตั้งแต่ขั้นตอนวิธีที่ฉันใช้เป็นเพียงพอร์ตไร้ยางอายจากการแก้ปัญหาของพรีโม่ RPython จุดประสงค์ของฉันที่นี่เพียงเพื่อแสดงสิ่งที่คุณสามารถทำได้ใน Python หากคุณรวมความมหัศจรรย์ของโมดูลNumpyและNumba

Numba อธิบายโดยย่อ:

Numba เป็นคอมไพเลอร์ที่เชี่ยวชาญในเวลาที่รวบรวมโค้ด Python และ NumPy เป็น LLVM (ผ่านตัวตกแต่ง) http://numba.pydata.org/

อัปเดต 1 : ฉันสังเกตเห็นหลังจากการกดตัวเลขรอบ ๆ ที่เราสามารถข้ามตัวเลขบางส่วนได้อย่างสมบูรณ์ ดังนั้นmaxfจะกลายเป็น(1 << n) // 2และmaxsกลายเป็นmaxf 2 ** นี่จะทำให้กระบวนการเร็วขึ้นเล็กน้อย n = 16 ใช้เวลาเพียง 48 ชั่วโมง (ลดลงจาก 4,5 นาที) ฉันมีความคิดอื่นที่ฉันจะลองดูว่าฉันสามารถทำให้มันเร็วขึ้นได้ไหม

อัปเดต 2:อัลกอริทึมที่เปลี่ยนแปลง (โซลูชันของพรีโม่) ในขณะที่พอร์ตของฉันยังไม่รองรับมัลติเธรดมันค่อนข้างง่ายที่จะเพิ่ม เป็นไปได้ที่จะปล่อย CPython GIL ด้วย Numba และ ctypes อย่างไรก็ตามโซลูชันนี้ทำงานบนคอร์เดียวอย่างรวดเร็วเช่นกัน!

import numpy as np
import numba as nb

bt_bits = 11
bt_mask = (1 << bt_bits) - 1
bit_table = np.zeros(1 << bt_bits, np.int32)

for i in range(0, 1 << bt_bits):
    bit_table[i] = ((i & 1) << 1) + bit_table[i >> 1]

@nb.njit("void(int32, int32, int32, int32, int64[:], int64[:])")
def run(n, m, start, re, counts, result):
    mask = (1 << n) - 1

    v_max = (1 << n) // 2
    rr = v_max // 2

    v = (1 << (n >> 1)) - 1
    while v < v_max:
        s = start

        while s < rr:
            f = v ^ s
            counts[0] += 8
            t = s << 1
            o, j = 0, 1

            while o < j and j < m:
                o = j
                w = (t ^ f) & mask
                bt_high = bit_table[w >> bt_bits]

                if bt_high + bit_table[w & bt_mask] == n:
                    counts[j] += 8
                    t <<= 1
                    j += 1
                elif bt_high + bit_table[(w ^ 1) & bt_mask] == n:
                    counts[j] += 8
                    t = (t | 1) << 1
                    j += 1
                    s += re

            s = v & -v
            r = v + s
            o = v ^ r
            o = (o >> 2) // s
            v = r | o

    for e in range(m):
        result[e] += counts[e] << (n - e)

และในที่สุดก็:

if __name__ == "__main__":
    n = 20
    m = n + 1

    result = np.zeros(m, np.int64)
    counts = np.zeros(m, np.int64)

    s1 = time.time() * 1000
    run(n, m, 0, 1, counts, result)
    s2 = time.time() * 1000

    print(result)
    print("{0}ms".format(s2 - s1))

สิ่งนี้ทำงานบนเครื่องของฉันใน 212688ms หรือ ~ 3.5 นาที


ขอบคุณ ทีนี้แล้ว n = 18 ล่ะ? :)

มันเกือบ 20 นาทีแล้วตั้งแต่ฉันเริ่มโปรแกรมโดยใช้ n = 18 ฉันคิดว่ามันปลอดภัยที่จะบอกว่า Python ไม่สามารถแก้ปัญหานี้ได้แม้กับ Numba ตรงเวลาโดยใช้อัลกอริทึมเฉพาะนี้
Anna Jokela

ฉันมองโลกในแง่ดีว่ามีอัลกอริทึมที่ดีกว่าอยู่

ฉันลอง pip pip ติดตั้ง numba แต่มันบอกว่าหา llvmpy ไม่ได้ ฉันลอง sudo pip ติดตั้ง llvmpy แต่มันบอกว่าหารุ่นไม่เจอ ฉันลอง sudo pip install versioneer แล้ว แต่มันบอกว่าฉันมีอยู่แล้ว

แม้ว่าฉันจะยังไม่ได้ใช้งาน numba (ฉันคิดว่าฉันจะต้องติดตั้งแอนนาคอนดาในที่สุด) ฉันประทับใจในสิ่งนี้ คำถามคือคุณจะได้รับมันเพื่อแก้ปัญหา N = 22 โดยใช้วิธีการที่คล้ายกับนิมรอดหรือไม่?

2

C ++ N = 16

ฉันกำลังทดสอบกับ EEEPC ด้วยอะตอม .. เวลาของฉันไม่สมเหตุสมผล : D
อะตอมแก้ปัญหา n = 14 ใน 34 วินาที และ n = 16 ใน 20 นาที ฉันต้องการทดสอบ n = 16 บนพีซี OP ฉันมองโลกในแง่ดี

ความคิดคือทุกครั้งที่เราพบทางออกสำหรับ F ที่กำหนดเราพบโซลูชัน 2 ^ i เพราะเราสามารถเปลี่ยนส่วนล่างของ S ที่นำไปสู่ผลลัพธ์เดียวกัน

#include <stdio.h>
#include <cinttypes>
#include <cstring>

int main()
{
   const int n = 16;
   const int m = n + 1;
   const uint64_t maxS = 1ULL << (2*n);
   const uint64_t maxF = 1ULL << n;
   const uint64_t mask = (1ULL << n)-1;
   uint64_t out[m]={0};
   uint64_t temp[m] = {0};
   for( uint64_t F = 0; F < maxF; ++F )
   {
      for( uint64_t S = 0; S < maxS; ++S )
      {
         int numSolution = 1;
         for( int i = n; i >= 0; --i )
         {
            const uint64_t window = S >> i;
            if( __builtin_popcount( mask & (window ^ F) ) == (n / 2) )
            {
               temp[i] += 1;
            } else {
               numSolution = 1 << i;
               S += numSolution - 1;
               break;
            }
         }
         for( int i = n; i >= 0; --i )
         {
            out[i] += temp[i]*numSolution;
            temp[i] = 0;
         }
      }
   }
   for( int i = n; i >= 0; --i )
   {
      uint64_t x = out[i];
      printf( "%lu ", x );
   }
   return 0;
}

เพื่อรวบรวม:

gcc 26459.cpp -std = c ++ 11 -O3 -march = เนทิฟ -fstrict-aliasing -ftree-vectorize -Wall -pedantic -o 26459


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

2

JAVASCRIPT n: 12

ในคอมพิวเตอร์ของฉันใช้เวลา 231.242 วินาที ในการสาธิตฉันใช้เว็บเวิร์คเกอร์เพื่อป้องกันการแช่แข็งเบราว์เซอร์ แน่นอนว่าสามารถปรับปรุงให้ดีขึ้นได้เมื่อใช้แรงงานคู่ขนาน ฉันรู้ว่า JS ไม่ได้มีโอกาสในความท้าทายนี้ แต่ฉันทำเพื่อความสนุก!

คลิกเพื่อเรียกใช้การสาธิตออนไลน์

var n = 8;        
var m = n + 1;
var o = [];
var popCount = function(bits) {
  var SK5  = 0x55555555,
      SK3  = 0x33333333,
      SKF0 = 0x0f0f0f0f,
      SKFF = 0xff00ff;

  bits -= (bits >> 1) & SK5;
  bits  = (bits & SK3) + ((bits >> 2) & SK3);
  bits  = (bits & SKF0) + ((bits >> 4) & SKF0);
  bits += bits >> 8;

  return (bits + (bits >> 15)) & 63;
};
for(var S = 0; S < (1 << n + m - 1); S += 2){
  for(var F = 0; F < (1 << n - 1); F += 1){
    for (var i = 0; i < m; i++){
      var c = popCount(((S >> i) & ((1 << n) - 1)) ^ F);
      if(c == n >> 1){
        if(!o[i]) o[i] = 0;
        o[i] += 4;
      } else break;
    }
  }
}
return o;

แล้วหนึ่งในเครื่องมือจาวาสคริปต์เร็ว (ish) ใหม่นั้นคืออะไร? สามารถใช้งานได้หรือไม่

คุณหมายถึงอะไรเช่นปาลูกดอก ?
rafaelcastrocouto

1
ที่จริงฉันผิด คุณอาจลองทั้ง Firefox และโครเมียม นอกจากว่าคุณต้องการเขียนใน asm.js แน่นอน :)

1
ความท้าทายได้รับการยอมรับ ... จะทำมัน!
rafaelcastrocouto

1
พยายามทำสิ่งนี้และใช้เวลาในคอมพิวเตอร์ของฉัน 5.4 วินาทีในการทำn=22 [235388928,86292480,19031048,5020640,1657928,783920,545408,481256,463832,460256,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744] i.imgur.com/FIJa2Ch.png
Spedwards

1

Fortran: n = 12

ฉันเพิ่งสร้างเวอร์ชั่นด่วนใน Fortran ไม่มีการปรับให้เหมาะสมยกเว้น OpenMP ควรบีบในเวลาต่ำกว่า 10 นาทีสำหรับ n = 12 ในเครื่อง OPs ใช้เวลา 10:39 ในเครื่องของฉันซึ่งช้ากว่า sligthly

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

program golf
use iso_fortran_env
implicit none
integer, parameter ::  n=12
integer :: F(n), S(2*n)
integer(int64) :: leadingzerocounts(n+1)
integer :: k
integer(int64) :: i,j,bindec,enc

leadingzerocounts=0

!$OMP parallel do private(i,enc,j,bindec,S,F,k) reduction(+:leadingzerocounts) schedule(dynamic)
do i=0,2**(2*n)-1
  enc=i
  ! Short loop to convert i into the array S with -1s and 1s
  do j=2*n,1,-1
    bindec=2**(j-1)
    if (enc-bindec .ge. 0) then
      S(j)=1
      enc=enc-bindec
    else
      S(j)=-1
    endif
  end do
  do j=0,2**(n)-1
    ! Convert j into the array F with -1s and 1s
    enc=j
    do k=n,1,-1
      bindec=2**(k-1)
      if (enc-bindec .ge. 0) then
        F(k)=1
        enc=enc-bindec
      else
        F(k)=-1
      endif
    end do
    ! Compute dot product   
    do k=1,n+1
      if (dot_product(F,S(k:k+n-1)) /= 0) exit
      leadingzerocounts(k)=leadingzerocounts(k)+1
    end do
  end do
end do
!$OMP end parallel do

print *, leadingzerocounts

end

1

Lua: n = 16

Disclaimer: ความตั้งใจของผมคือไม่โพสต์นี้เป็นคำตอบของตัวเองตั้งแต่ขั้นตอนวิธีฉันใช้ถูกขโมยลงคอจากคำตอบที่ฉลาดแอนนา Jokela ของ ซึ่งถูกขโมยอย่างไร้ยางอายจากคำตอบที่ชาญฉลาดของ ilmaleคำตอบที่ฉลาด

นอกจากนี้มันไม่ถูกต้อง - มันมีความไม่ถูกต้องที่เกิดจากตัวเลขจุดลอยตัว (มันจะดีกว่าถ้า Lua จะรองรับจำนวนเต็ม 64 บิต) อย่างไรก็ตามฉันยังคงอัปโหลดเพื่อแสดงว่าโซลูชันนี้รวดเร็วแค่ไหน เป็นภาษาการเขียนโปรแกรมแบบไดนามิก แต่ฉันสามารถคำนวณ n = 16 ในเวลาที่เหมาะสม (1 นาทีสำหรับ CPU 800MHz)

ทำงานกับ LuaJIT ล่ามมาตรฐานช้าเกินไป

local bit = require "bit"
local band = bit.band
local bor = bit.bor
local bxor = bit.bxor
local lshift = bit.lshift
local rshift = bit.rshift

-- http://stackoverflow.com/a/11283689/736054
local function pop_count(w)
    local b1 = 1431655765
    local b2 = 858993459
    local b3 = 252645135
    local b7 = 63

    w = band(rshift(w, 1), b1) + band(w, b1)
    w = band(rshift(w, 2), b2) + band(w, b2)
    w = band(w + rshift(w, 4), b3)
    return band(rshift(w, 24) + rshift(w, 16) + rshift(w, 8) + w, b7)
end

local function gen_array(n, value)
    value = value or 0
    array = {}
    for i = 1, n do
        array[i] = value
    end
    return array
end

local n = 16
local u = math.floor(n / 2)
local m = n + 1
local maxf = math.floor(lshift(1, n) / 2)
local maxs = maxf ^ 2
local mask = lshift(1, n) - 1

local out = gen_array(m, 0)
local temp = gen_array(m, 0)


for f = 0, maxf do
    local s = 0
    while s <= maxs do
        local num_solution = 1

        for i = n, 0, -1 do
            if pop_count(band(mask, bxor(rshift(s, i), f))) == u then
                temp[i + 1] = temp[i + 1] + 8
            else
                num_solution = lshift(1, i)
                s = s + num_solution - 1
                break
            end
        end

        for i = 1, m do
            out[i] = out[i] + temp[i] * num_solution
            temp[i] = 0
        end

        s = s + 1
    end
end

for i = m, 1, -1 do
    print(out[i])
end

ขอขอบคุณ. ฉันคิดว่ารุ่น lua รุ่นล่าสุดใช้ long long int ซึ่งควรเป็น 64 บิตในระบบ 64 บิต โปรดดูที่ "lua_integer" ที่lua.org/work/doc/manual.html

@Lembik: น่าสนใจ ทั้งสองวิธีเป็น Lua มาตรฐาน (ซึ่งรองรับแล้วlong longแทนที่จะdoubleใช้การตั้งค่าการคอมไพล์) ไม่ใช่ LuaJIT
Konrad Borowski

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