อัลกอริธึมที่เร็วที่สุดในการนำผลิตภัณฑ์ของชุดย่อยทั้งหมด


23

ได้รับnตัวเลขในอาร์เรย์ (คุณไม่สามารถถือว่าพวกเขาเป็นจำนวนเต็ม) n-1ผมอยากจะคำนวณสินค้าย่อยทุกขนาด

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

หากคุณไม่อนุญาตให้มีการหารจำนวนการดำเนินการทางคณิตศาสตร์ขั้นต่ำ (เช่นการคูณและการเพิ่ม) จำเป็นต้องคำนวณผลิตภัณฑ์ของชุดย่อยทั้งหมดที่มีขนาด n-1 เป็นเท่าใด

เห็นได้ชัดว่าคุณสามารถทำได้(n-1)*nหลายวิธี

เพื่อชี้แจงผลลัพธ์ที่ได้คือnผลิตภัณฑ์ที่แตกต่างกันและการดำเนินการเฉพาะนอกเหนือจากการอ่านและการเขียนไปยังหน่วยความจำที่อนุญาตคือการคูณการบวกและการลบ

ตัวอย่าง

ถ้าใส่มีเลขสาม2,3,5แล้วออกเป็นสามตัวเลข15 = 3*5, และ10 = 2*56 = 2*3

เกณฑ์การชนะ

nคำตอบควรให้เป็นสูตรที่แน่นอนสำหรับจำนวนของการดำเนินการทางคณิตศาสตร์รหัสของพวกเขาจะใช้ในแง่ของ เพื่อให้ชีวิตง่ายขึ้นฉันแค่เสียบn = 1000สูตรของคุณเพื่อตัดสินคะแนน ยิ่งต่ำยิ่งดี

หากมันยากเกินไปที่จะสร้างสูตรที่แน่นอนสำหรับรหัสของคุณคุณสามารถเรียกใช้n = 1000และนับการดำเนินการทางคณิตศาสตร์ในรหัสได้ สูตรที่แน่นอนจะดีที่สุดอย่างไรก็ตาม

คุณควรเพิ่มคะแนนn=1000ของคุณเป็นคำตอบสำหรับการเปรียบเทียบง่าย ๆ


4
เราจะนับเป็น 1 ฟรีเมื่อคูณหรือไม่ มิฉะนั้นฉันจะกำหนดฟังก์ชั่นการคูณแบบกำหนดเองที่ทำสิ่งนี้
xnor

3
มันจะผิดกฎหรือไม่ที่จะทำทวีคูณทั้งคู่ในแบบคู่ขนานโดยการต่อหมายเลขเข้าด้วยกันกับตัวเว้นวรรคจำนวน 0 หลักอย่างเพียงพอหรือไม่?
xnor

1
จะดำเนินการเช่น+ในดัชนีนับ? หากเป็นกรณีนี้การจัดทำดัชนีอาร์เรย์จะนับด้วยหรือไม่ (เนื่องจากเป็นน้ำตาลที่อยู่หลัง syntactic ทั้งหมดสำหรับการบวกและการลดความเสี่ยง)
nore

2
@ ไม่เป็นไรฉันให้ :) เพียงนับการดำเนินการทางคณิตศาสตร์ที่เกี่ยวข้องกับการป้อนข้อมูลในทางใดทางหนึ่ง
Arthur

1
เห็นได้ชัดว่าคุณสามารถทำได้ในการ(n-1)*nคูณคุณหมายถึง(n-2)*nใช่มั้ย
Luis Mendo

คำตอบ:


25

Python, การดำเนินงาน 3 (n-2), คะแนน = 2994

l = list(map(float, input().split()))
n = len(l)

left = [0] * len(l)
right = [0] * len(l)
left[0] = l[0]
right[-1] = l[-1]
for i in range(1,len(l)-1):
  left[i] = l[i] * left[i - 1]
  right[-i-1] = l[-i-1] * right[-i]

result = [0] * len(l)
result[-1] = left[-2]
result[0] = right[1]
for i in range(1, len(l) - 1):
  result[i] = left[i - 1] * right[i+1]

print(result)

อาร์เรย์leftและrightมีผลิตภัณฑ์สะสมของอาร์เรย์จากด้านซ้ายและจากขวาตามลำดับ

แก้ไข: พิสูจน์ว่า 3 (n-2) เป็นจำนวนการดำเนินการที่เหมาะสมที่สุดสำหรับ n> = 2 ถ้าเราใช้การคูณเท่านั้น

เราจะทำสิ่งนั้นโดยอุปนัย โดยอัลกอริทึมด้านบนเราแค่ต้องพิสูจน์ว่าสำหรับ n> = 2, 3 (n-2) คือขอบเขตล่างของจำนวนการคูณที่จำเป็น

สำหรับ n = 2 เราต้องการการคูณอย่างน้อย 0 = 3 (2-2) ดังนั้นผลลัพธ์จึงเป็นเรื่องเล็กน้อย

ให้ n> 2 และสมมติว่าองค์ประกอบ n - 1 เราต้องการการคูณอย่างน้อย 3 (n-3) พิจารณาวิธีแก้ปัญหาสำหรับองค์ประกอบ n ที่มีการคูณ k ตอนนี้เราลบองค์ประกอบสุดท้ายเหล่านี้ราวกับว่ามันเป็น 1 และทำให้การคูณทั้งหมดนั้นง่ายขึ้น นอกจากนี้เรายังลบการคูณที่นำไปสู่ผลิตภัณฑ์ขององค์ประกอบอื่น ๆ ทั้งหมดเนื่องจากไม่จำเป็นต้องใช้เนื่องจากไม่สามารถใช้เป็นค่ากลางเพื่อรับผลิตภัณฑ์ที่ n-2 ขององค์ประกอบอื่น ๆ เนื่องจากไม่อนุญาตให้มีการหาร สิ่งนี้ทำให้เรามีการคูณ l และการแก้ปัญหาสำหรับองค์ประกอบ n - 1

โดยสมมติฐานการเหนี่ยวนำเรามี l> = 3 (n-3)

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

เราจึงมี k> = l + 3> = 3 (n-2) เพื่อพิสูจน์ทฤษฎีบทที่อ้างสิทธิ์


8
นี้จะเปิดออกสะอาดมากใน Haskellf l = zipWith (*) (scanl (*) 1 l) (scanr (*) 1 $ tail l) :
xnor

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Dennis

12

Haskellคะแนน 2994

group :: Num a => [a] -> [[a]]
group (a:b:t) = [a,b] : group t
group [a] = [[a]]
group [] = []

(%) :: (Num a, Eq a) => a -> a -> a
a % 1 = a
1 % b = b
a % b = a * b

prod_one_or_two :: (Num a, Eq a) => [a] -> a
prod_one_or_two [a, b] = a % b
prod_one_or_two [x] = x

insert_new_value :: (Num a, Eq a) => ([a], a) -> [a]
insert_new_value ([a, b], c) = [c % b, c % a]
insert_new_value ([x], c) = [c]

products_but_one :: (Num a, Eq a) => [a] -> [a]
products_but_one [a] = [1]
products_but_one l = 
    do combination <- combinations ; insert_new_value combination
    where 
        pairs = group l
        subresults = products_but_one $ map prod_one_or_two pairs
        combinations = zip pairs subresults

ลองออนไลน์!

[a,b,c,d,e,f,g,h]พูดว่าเราได้รับรายชื่อ [[a,b],[c,d],[e,f],[g,h]]เราเป็นกลุ่มแรกที่มันเป็นคู่ จากนั้นเราจะเรียกคืนรายการขนาดครึ่งหนึ่งpairsของผลิตภัณฑ์เพื่อรับsubresults

[a*b, c*d, e*f, g*h] -> [(c*d)*(e*f)*(g*h), (a*b)*(e*f)*(g*h), (a*b)*(c*d)*(g*h), (a*b)*(c*d)*(e*f)]

ถ้าเราใช้องค์ประกอบแรก(c*d)*(e*f)*(g*h)และคูณด้วยbและaตามลำดับเราได้รับผลิตภัณฑ์ของทั้งหมด แต่aและ bแต่ การทำเช่นนี้กับทุกคู่และผลการเรียกซ้ำโดยที่คู่นั้นหายไปเราจะได้ผลลัพธ์สุดท้าย กรณีความยาวคี่ได้รับการจัดการเป็นพิเศษโดยการให้องค์ประกอบคี่ผ่านไปไม่ได้จับคู่ไปยังขั้นตอนซ้ำและผลิตภัณฑ์ขององค์ประกอบที่เหลือกลับมาเป็นผลิตภัณฑ์ที่ไม่มีมัน

จำนวนคูณt(n)เป็นn/2สินค้าจับคู่t(n/2)สำหรับโทร recursive และอื่น ๆnสำหรับผลิตภัณฑ์ที่มีองค์ประกอบของแต่ละบุคคล นี้จะช่วยให้สำหรับคี่t(n) = 1.5 * n + t(n/2) nใช้นับที่แม่นยำมากขึ้นสำหรับการที่แปลกnและไม่สนใจคูณด้วย1สำหรับกรณีฐานให้คะแนนสำหรับ2997n=1000


นี่เป็นสิ่งที่ดีมาก
Arthur

ฉันคิดว่าเหตุผลที่ทำให้คะแนนคือ 2995 และไม่ใช่ 2994 ตามคำตอบของฉันคือมันคำนวณผลคูณของตัวเลขทั้งหมดรวมทั้งในกรณีที่ไม่มีอำนาจของสองกรณีซึ่งถูกตัดทอนในภายหลัง บางทีการจัดการอย่างระมัดระวังproducts_but_one'อาจหลีกเลี่ยงได้โดยส่งคืนความยาวที่ถูกต้อง
nore

@ ไม่มีฉันพบว่าฉันมีการเพิ่มทวีคูณในการนับของฉันเพราะฉันลืมกรณีฐานมีตัว1ที่เป็นอิสระที่จะคูณ ฉันคิดว่าช่องว่างภายใน 1 ไม่ได้ส่งผลกระทบต่อสิ่งต่าง ๆ แต่ฉันล้างอัลกอริทึมของฉันเพื่อไม่ใช้
xnor

รหัสนี้สมมติว่าอินพุตเป็นจำนวนเต็มหรือไม่?

@Lembik มันทำ แต่เฉพาะในคำอธิบายประกอบประเภทตัวเลือก floatฉันจะเปลี่ยนพวกเขาทั้งหมด
xnor

9

Haskellคะแนน 9974

partition :: [Float] -> ([Float], [Float])
partition = foldr (\a (l1,l2) -> (l2, a:l1)) ([],[])

(%) :: Float -> Float -> Float
a % 1 = a
1 % b = b
a % b = a*b

merge :: (Float, [Float]) -> (Float, [Float]) -> (Float, [Float])
merge (p1,r1) (p2, r2) = (p1%p2, map(%p1)r2 ++ map(%p2)r1)

missing_products' :: [Float] -> (Float, [Float])
missing_products' [a] = (a,[1])
missing_products' l = merge res1 res2
    where
        (l1, l2) = partition l
        res1 = missing_products' l1
        res2 = missing_products' l2

missing_products :: [Float] -> [Float]
missing_products = snd . missing_products'

ลองออนไลน์!

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

ฟังก์ชั่นpartitionแยกรายการออกเป็นครึ่งเท่าของเท่าที่จะทำได้โดยใส่องค์ประกอบสลับที่ด้านตรงข้ามของพาร์ติชัน เรารวมผลลัพธ์ซ้ำ ๆ ของ(p,r)แต่ละส่วนซ้ำด้วยrรายการผลิตภัณฑ์ที่ขาดหายไปหนึ่งรายการและpผลิตภัณฑ์โดยรวม

สำหรับเอาต์พุตสำหรับรายการทั้งหมดองค์ประกอบที่หายไปจะต้องอยู่ในครึ่งหนึ่ง ผลิตภัณฑ์ที่มีองค์ประกอบนั้นหายไปนั้นเป็นผลิตภัณฑ์ที่ขาดหายไปหนึ่งรายการในครึ่งหนึ่งและคูณด้วยผลิตภัณฑ์เต็มรูปแบบสำหรับอีกครึ่งหนึ่ง ดังนั้นเราจึงคูณผลิตภัณฑ์ที่ขาดหายไปหนึ่งตัวโดยผลิตภัณฑ์เต็มรูปแบบของอีกครึ่งหนึ่งและทำรายการผลลัพธ์map(*p1)r2 ++ map(*p2)r1)ดังนี้ การnคูณnนี้ใช้ความยาว เราจำเป็นต้องสร้างผลิตภัณฑ์ใหม่เต็มรูปแบบp1*p2สำหรับใช้ในอนาคตโดยคิดราคาเพิ่มอีก 1 เท่า

นี้จะช่วยให้การเรียกซ้ำทั่วไปสำหรับจำนวนของการดำเนินงานt(n)ที่มีแม้กระทั่ง:n t(n) = n + 1 + 2 * t(n/2)ส่วนคี่นั้นคล้ายกัน แต่หนึ่งใน sublists นั้น1ใหญ่กว่า ทำการสอบถามซ้ำเราได้รับการn*(log_2(n) + 1)คูณแม้ว่าความแตกต่างคี่ / คู่มีผลต่อค่าที่แน่นอนนั้น ค่าที่สูงถึงt(3)ได้รับการปรับปรุงโดยไม่คูณ1ด้วยการกำหนดตัวแปร(%)ของ(*)ทางลัด_*1หรือ1*_กรณีต่างๆ

นี้จะช่วยให้คูณสำหรับ9975 n=1000ผมเชื่อว่าความเกียจคร้านของ Haskell หมายความว่าผลิตภัณฑ์โดยรวมไม่ได้ใช้ในชั้นนอกไม่ได้คำนวณ9974; หากฉันเข้าใจผิดฉันสามารถละเว้นได้อย่างชัดเจน


คุณเอาชนะฉันด้วยการประทับเวลาหนึ่งนาทีก่อนหน้านี้
nore

ถ้ามันยากที่จะn = 1000คำนวณสูตรอย่างแน่นอนคุณสามารถเรียกใช้และนับการดำเนินการทางคณิตศาสตร์ในรหัสได้
Arthur

เนื่องจากรหัสของเราเหมือนกันฉันไม่เข้าใจว่าคุณจะไปถึง9974และไม่9975คูณสำหรับn = 1000(ในกรณีของการคำนวณผลิตภัณฑ์โดยรวมในเลเยอร์ด้านนอก) คุณรวม a 1ในอินพุตที่คุณใช้ทดสอบหรือไม่?
nore

@ อีกต่อไปคุณถูกต้องฉันถูกปิดโดยหนึ่ง ฉันเขียนโค้ดเพื่อทำการสอบถามซ้ำสำหรับจำนวนการเรียกฟังก์ชันการคูณ การนับการโทรโดยตรงจะเชื่อถือได้มากขึ้น - ไม่มีใครรู้ว่าฉันจะทำอย่างไรใน Haskell
xnor

1
@xnor คุณสามารถใช้traceจากDebug.Traceที่มีการจับทุก| trace "call!" False = undefinedยามที่ผมคิดว่า แต่สิ่งนี้ใช้unsafePerformIOภายใต้ประทุนดังนั้นจึงไม่ได้เป็นการปรับปรุงมากนัก
Soham Chowdhury

6

Haskellคะแนน 2994

group :: [a] -> Either [(a, a)] (a, [(a, a)])
group [] = Left []
group (a : l) = case group l of
  Left pairs -> Right (a, pairs)
  Right (b, pairs) -> Left ((a, b) : pairs)

products_but_one :: Num a => [a] -> [a]
products_but_one [_] = [1]
products_but_one [a, b] = [b, a]
products_but_one l = case group l of
  Left pairs ->
    let subresults =
          products_but_one [a * b | (a, b) <- pairs]
    in do ((a, b), c) <- zip pairs subresults; [c * b, c * a]
  Right (extra, pairs) ->
    let subresult : subresults =
          products_but_one (extra : [a * b | (a, b) <- pairs])
    in subresult : do ((a, b), c) <- zip pairs subresults; [c * b, c * a]

ลองออนไลน์!

มันทำงานอย่างไร

นี่เป็นอัลกอริทึมของ xnor ที่ได้รับการทำความสะอาดซึ่งเกี่ยวข้องกับคดีแปลก ๆ ในทางที่ตรงไปตรงมามากขึ้น (แก้ไข: ดูเหมือนว่า xnor ได้ล้างมันด้วยวิธีเดียวกัน):

[a, b, c, d, e, f, g] ↦
[a, bc, de, fg] ↦
[(bc) (de) (fg), (de) (fg), a (bc) ( fg), a (bc) (de)] โดยเรียกซ้ำ↦
[(bc) (de) (fg), a (de) (fg) c, a (de) (fg) b, a (bc) (fg) e, a (bc) (fg) d, a (bc) (de) g, a (bc) (de) f]

[a, b, c, d, e, f, g, h] ↦
[ab, cd, ef, gh] ↦
[(cd) (ef) (gh), (ab) (ef) (gh), ( ab) (cd) (gh), (ab) (cd) (ef)] โดยการเรียกซ้ำ↦
[(cd) (ef) (gh) b, (cd) (ef) (gh) a, (ab) (ef) ) (gh) d, (ab) (ef) (gh) c, (ab) (cd) (gh) f, (ab) (cd) (cd) (gh) e, (ab) (cd) (ef) h, (AB) (CD) (EF) g]


"รับตัวเลข n ในอาเรย์ (คุณไม่สามารถคิดว่ามันเป็นจำนวนเต็ม)" เราไม่สามารถสรุปได้ว่าพวกเขาเป็นจำนวนเต็ม

5

การดำเนินงาน O (n log n) คะแนน = 9974

ทำงานร่วมกับต้นไม้ไบนารี

หลาม

l = list(map(int, input().split()))
n = len(l)

p = [0] * n + l
for i in range(n - 1, 1, -1):
  p[i] = p[i + i] * p[i + i+1]

def mul(x, y):
  if y == None:
    return x
  return x * y

r = [None] * n + [[None]] * n
for i in range(n - 1, 0, -1):
  r[i] = [mul(p[i + i + 1], x) for x in r[i + i]] + [mul(p[i + i], x) for x in r[i + i + 1]]

u = r[1]
j = 1
while j <= n:
  j += j
print(u[n+n-j:] + u[:n+n-j])

สิ่งนี้ยังต้องการการดำเนินการเพิ่มเติมรายการและเลขคณิตบางอย่างของตัวเลขที่ไม่ใช่ค่าอินพุต ไม่แน่ใจว่านับหรือไม่ mulฟังก์ชั่นจะมีการบันทึก n การดำเนินงานสำหรับกรณีฐานเพื่อหลีกเลี่ยงการสูญเสียพวกเขาโดยคูณด้วย 1. ในกรณีนี้คือ O (n log n) การดำเนินงาน สูตรที่แน่นอนคือถ้านับเฉพาะดำเนินการทางคณิตศาสตร์เกี่ยวกับตัวเลขการป้อนข้อมูลด้วย:j = floor(log_2(n)) j * (2^(j + 1) - n) + (j + 1) * (2 * n - 2^(j + 1)) - 2

ขอบคุณ @xnor สำหรับการบันทึกหนึ่งการดำเนินการด้วยแนวคิดที่จะไม่คำนวณผลิตภัณฑ์ด้านนอก!

ส่วนสุดท้ายคือการส่งออกผลิตภัณฑ์ตามลำดับของคำที่หายไป


ถ้ามันยากที่จะn = 1000คำนวณสูตรอย่างแน่นอนคุณสามารถเรียกใช้และนับการดำเนินการทางคณิตศาสตร์ในรหัสได้
Arthur

ฉันนับการดำเนินงาน 10975 ... ?
HyperNeutrino

p[i] = p[i + i] * p[i + i+1]ไม่ถูกนับ
HyperNeutrino

นี่คือn log2 n + nการดำเนินการ (ซึ่งเป็น O (nlogn) btw
HyperNeutrino

@HyperNeutrino การดำเนินการที่p[i] = p[i + i] * p[i + i + 1]ควรได้รับการบันทึกโดยการเพิ่มประสิทธิภาพการคูณ อย่างไรก็ตามฉันอาจนับหนึ่งมากเกินไป
nore

3

O ((n-2) * n) = O (n 2 ): วิธีแก้ปัญหาเล็กน้อย

นี่เป็นเพียงวิธีแก้ปัญหาเล็กน้อยที่คูณกันแต่ละชุดย่อย:

หลาม

def product(array): # Requires len(array) - 1 multiplication operations
    if not array: return 1
    result = array[0]
    for value in array[1:]:
        result *= value
    return result

def getSubsetProducts(array):
    products = []
    for index in range(len(array)): # calls product len(array) times, each time calling on an array of size len(array) - 1, which means len(array) - 2 multiplication operations called len(array) times
        products.append(product(array[:index] + array[index + 1:]))
    return products

โปรดทราบว่าสิ่งนี้ยังต้องใช้nการดำเนินการเพิ่มรายการ ไม่แน่ใจว่านับหรือไม่ หากไม่ได้รับอนุญาตแล้วproduct(array[:index] + array[index + 1:])สามารถเปลี่ยนไปซึ่งการเปลี่ยนแปลงสูตรการproduct(array[:index]) * product(array[index + 1:])O((n-1)*n)


คุณสามารถเพิ่มคะแนนของคุณเองในคำตอบ 998 * 1,000 ในกรณีนี้
Arthur

ไม่ต้องการproductฟังก์ชั่นO(n)การทำงานของคุณ? หนึ่งรายการสำหรับแต่ละองค์ประกอบในอาร์เรย์ (แม้ว่าจะสามารถเปลี่ยนได้อย่างง่ายดายO(n-1))
Roman Gräf

@ RomanGräf True ฉันจะเปลี่ยนเป็น O (n-1) แต่ขอบคุณที่ชี้ให้เห็น
HyperNeutrino

สิ่งนี้ได้ถูกเปลี่ยนเป็นatomic-code-golf ...
Erik the Outgolfer

@EriktheOutgolfer อะไรที่ทำให้ฉันได้คะแนนตอนนี้? นอกเสียจากว่าฉันจะโง่อย่างโจ๋งครึ่มแล้วแท็กและรายละเอียดก็ไม่ขัดแย้งกันเลยเหรอ?
HyperNeutrino

3

Python, 7540

กลยุทธ์การรวมไตรภาคี ฉันคิดว่าฉันสามารถทำได้ดีกว่านี้ด้วยการรวมที่ใหญ่มาก มันคือ O (n log n)

แก้ไข: แก้ไขความผิดพลาด

count = 0
def prod(a, b):
    if a == 1: return b
    if b == 1: return a
    global count
    count += 1
    return a * b

def tri_merge(subs1, subs2, subs3):
    total1, missing1 = subs1
    total2, missing2 = subs2
    total3, missing3 = subs3

    prod12 = prod(total1, total2)
    prod13 = prod(total1, total3)
    prod23 = prod(total2, total3)

    new_missing1 = [prod(m1, prod23) for m1 in missing1]
    new_missing2 = [prod(m2, prod13) for m2 in missing2]
    new_missing3 = [prod(m3, prod12) for m3 in missing3]

    return prod(prod12, total3), new_missing1 + new_missing2 + new_missing3

def tri_partition(nums):
    split_size = len(nums) // 3
    a = nums[:split_size]
    second_split_length = split_size + (len(nums) % 3 == 2)
    b = nums[split_size:split_size + second_split_length]
    c = nums[split_size + second_split_length:]
    return a, b, c

def missing_products(nums):
    if len(nums) == 1: return nums[0], [1]
    if len(nums) == 0: return 1, []
    subs = [missing_products(part) for part in tri_partition(nums)]
    return tri_merge(*subs)

def verify(nums, res):
    actual_product = 1
    for num in nums:
        actual_product *= num
    actual_missing = [actual_product // num for num in nums]
    return actual_missing == res[1] and actual_product == res[0]

nums = range(2, int(input()) + 2)
res = missing_products(nums)

print("Verified?", verify(nums, res))
if max(res[1]) <= 10**10: print(res[1])

print(len(nums), count)

ฟังก์ชั่นที่เกี่ยวข้องคือการmissing_productsให้ผลิตภัณฑ์โดยรวมและองค์ประกอบทั้งหมดที่มีองค์ประกอบที่ขาดหายไป


คุณนับการคูณด้วยtri_mergeหรือไม่ นอกจากนี้คุณสามารถแทนที่2 * split_size + ...ในด้วยtri_partition split_size + split_size + ...
Roman Gräf

@ RomanGräfฉันปรับโครงสร้างมันตามคำแนะนำของคุณ
isaacg

1

dc, คะแนน 2994

#!/usr/bin/dc -f

# How it works:
# The required products are
#
#   (b × c × d × e × ... × x × y × z)
# (a) × (c × d × e × ... × x × y × z)
# (a × b) × (d × e × ... × x × y × z)
# ...
# (a × b × c × d × e × ... × x) × (z)
# (a × b × c × d × e × ... × x × y)
#
# We calculate each parenthesised term by
# multiplying the one above (on the left) or below
# (on the right), for 2(n-2) calculations, followed
# by the n-2 non-parenthesised multiplications
# giving a total of 3(n-2) operations.

# Read input from stdin
?

# We will store input values into stack 'a' and
# accumulated product into stack 'b'.  Initialise
# stack b with the last value read.
sb

# Turnaround function at limit of recursion: print
# accumulated 'b' value (containing b..z above).
[Lbn[ ]nq]sG

# Recursive function - on the way in, we stack up
# 'a' values and multiply up the 'b' values.  On
# the way out, we multiply up the 'a' values and
# multiply each by the corresponding 'b' value.
[dSalb*Sb
z1=G
lFx
dLb*n[ ]n
La*]dsFx

# Do the last a*b multiplication
dLb*n[ ]n

# And we have one final 'a' value that doesn't have a
# corresponding 'b':
La*n

ฉันสมมติว่าการเปรียบเทียบจำนวนเต็มz1=(ซึ่งยุติการเรียกซ้ำเมื่อเราไปถึงค่าสุดท้าย) ฟรี นี่เทียบเท่ากับการกดforeachไลค์ในภาษาอื่น

การสาธิต

for i in '2 3 5' '2 3 5 7' '0 2 3 5' '0 0 1 2 3 4'
do printf '%s => ' "$i"; ./127147.dc <<<"$i"; echo
done
2 3 5 => 15 10 6
2 3 5 7 => 105 70 42 30
0 2 3 5 => 30 0 0 0
0 0 1 2 3 4 => 0 0 0 0 0 0

ตัวอย่างที่มีอินพุตขนาดใหญ่และขนาดเล็ก:

./127147.dc <<<'.0000000000000000000542101086242752217003726400434970855712890625 1 18446744073709551616'
18446744073709551616 1.0000000000000000000000000000000000000000000000000000000000000000 .0000000000000000000542101086242752217003726400434970855712890625

1

C ++, คะแนน: 5990, O ([2NlogN] / 3)

การใช้งานนี้ใช้ตารางค้นหาต้นไม้แบบไบนารี การใช้งานครั้งแรกของฉันคือ O (NlogN) แต่การปรับให้เหมาะสมในนาทีสุดท้ายซึ่งค้นหาผลิตภัณฑ์ขององค์ประกอบอาเรย์ทั้งหมดลบด้วยหนึ่งคู่การคูณ + 2 จะบันทึกวัน ฉันคิดว่าสิ่งนี้ยังสามารถเพิ่มประสิทธิภาพได้อีกเล็กน้อยอาจเพิ่มอีก 16% ...

ฉันทิ้งร่องรอยการแก้จุดบกพร่องไว้เพียงเพราะมันง่ายกว่าที่จะลบทิ้งเพื่อเขียนใหม่ :)

[แก้ไข] ความซับซ้อนที่แท้จริงถูกวัดที่ O ([2NlogN] / 3) เป็น 100 จริง ๆ แล้วมันแย่กว่า O (NlogN) เล็กน้อยสำหรับเซตเล็ก ๆ แต่มีแนวโน้มไปที่ O ([NlogN] / 2) เมื่ออาร์เรย์เติบโตขึ้น O ที่มีขนาดใหญ่มาก (0.57.NlogN) สำหรับชุดขององค์ประกอบ 1 ล้านรายการ

#include "stdafx.h"
#include <vector>
#include <iostream>
#include <random>
#include <cstdlib>

using DataType = long double;

using DataVector = std::vector<DataType>;

struct ProductTree
{
    std::vector<DataVector> tree_;
    size_t ops_{ 0 };

    ProductTree(const DataVector& v) : ProductTree(v.begin(), v.end()) {}
    ProductTree(DataVector::const_iterator first, DataVector::const_iterator last)
    {
        Build(first, last);
    }

    void Build(DataVector::const_iterator first, DataVector::const_iterator last)
    {
        tree_.emplace_back(DataVector(first, last));

        auto size = std::distance(first, last);
        for (auto n = size; n >= 2; n >>= 1)
        {
            first = tree_.back().begin();
            last = tree_.back().end();

            DataVector v;
            v.reserve(n);
            while (first != last) // steps in pairs
            {
                auto x = *(first++);
                if (first != last)
                {
                    ++ops_;
                    x *= *(first++); // could optimize this out,small gain
                }
                v.push_back(x);
            }
            tree_.emplace_back(v);
        }
    }

    // O(NlogN) implementation... 
    DataVector Prod()
    {
        DataVector result(tree_[0].size());
        for (size_t i = 0; i < tree_[0].size(); ++i)
        {
            auto depth = tree_.size() - 1;
            auto k = i >> depth;
            result[i] = ProductAtDepth(i, depth);
        }
        return result;
    }

    DataType ProductAtDepth(size_t index, size_t depth) 
    {
        if (depth == 0)
        {
            return ((index ^ 1) < tree_[depth].size())
                ? tree_[depth][index ^ 1]
                : 1;
        }
        auto k = (index >> depth) ^ 1;

        if ((k < tree_[depth].size()))
        {
            ++ops_;
            return tree_[depth][k] * ProductAtDepth(index, depth - 1);
        }
        return ProductAtDepth(index, depth - 1);
    }    

    // O([3NlogN]/2) implementation... 
    DataVector Prod2()
    {
        DataVector result(tree_[0].size());
        for (size_t i = 0; i < tree_[0].size(); ++i)    // steps in pairs
        {
            auto depth = tree_.size() - 1;
            auto k = i >> depth;
            auto x = ProductAtDepth2(i, depth);
            if (i + 1 < tree_[0].size())
            {
                ops_ += 2;
                result[i + 1] = tree_[0][i] * x;
                result[i] = tree_[0][i + 1] * x;
                ++i;
            }
            else
            {
                result[i] = x;
            }
        }
        return result;
    }

    DataType ProductAtDepth2(size_t index, size_t depth)
    {
        if (depth == 1)
        {
            index = (index >> 1) ^ 1;
            return (index < tree_[depth].size())
                ? tree_[depth][index]
                : 1;
        }
        auto k = (index >> depth) ^ 1;

        if ((k < tree_[depth].size()))
        {
            ++ops_;
            return tree_[depth][k] * ProductAtDepth2(index, depth - 1);
        }
        return ProductAtDepth2(index, depth - 1);
    }

};


int main()
{
    //srand(time());

    DataVector data;
    for (int i = 0; i < 1000; ++i)
    {
        auto x = rand() & 0x3;          // avoiding overflow and zero vaolues for testing
        data.push_back((x) ? x : 1);
    }

    //for (int i = 0; i < 6; ++i)
    //{
    //  data.push_back(i + 1);
    //}

    //std::cout << "data:[";
    //for (auto val : data)
    //{
    //  std::cout << val << ",";
    //}
    //std::cout << "]\n";

    ProductTree pt(data);
    DataVector result = pt.Prod2();

    //std::cout << "result:[";
    //for (auto val : result)
    //{
    //  std::cout << val << ",";
    //}
    //std::cout << "]\n";
    std::cout << "N = " << data.size() << " Operations :" << pt.ops_ << '\n';

    pt.ops_ = 0;
    result = pt.Prod();

    //std::cout << "result:[";
    //for (auto val : result)
    //{
    //  std::cout << val << ",";
    //}
    //std::cout << "]\n";

    std::cout << "N = " << data.size() << " Operations :" << pt.ops_ << '\n';

    return 0;
}

ฉันกำลังเพิ่มอัลกอริทึมของ @ nore เพื่อความสมบูรณ์ มันเยี่ยมมากและเร็วที่สุด

class ProductFlat
{
private:
    size_t ops_{ 0 };

    void InitTables(const DataVector& v, DataVector& left, DataVector& right)
    {
        if (v.size() < 2)
        {
            return;
        }

        left.resize(v.size() - 1);
        right.resize(v.size() - 1);

        auto l = left.begin();
        auto r = right.rbegin();
        auto ol = v.begin();
        auto or = v.rbegin();

        *l = *ol++;
        *r = *or++;
        if (ol == v.end())
        {
            return;
        }

        while (ol + 1 != v.end())
        {
            ops_ += 2;
            *l = *l++ * *ol++;
            *r = *r++ * *or++;
        }
    }

public:
    DataVector Prod(const DataVector& v)
    {
        if (v.size() < 2)
        {
            return v;
        }

        DataVector result, left, right;
        InitTables(v, left, right);

        auto l = left.begin();
        auto r = right.begin();
        result.push_back(*r++);
        while (r != right.end())
        {
            ++ops_;
            result.push_back(*l++ * *r++);
        }
        result.push_back(*l++);
        return result;
    }

    auto Ops() const
    {
        return ops_;
    }
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.