ทับทิมเร็วมาก แต่ก็ขึ้นอยู่กับอินพุตด้วย
ตอนนี้ความเร็วจะเพิ่มขึ้นเป็น 2 ถึง 2.5 โดยการเปลี่ยนจากสตริงเป็นจำนวนเต็ม
การใช้งาน:
cat <input> | ruby this.script.rb
เช่น.
mad_gaksha@madlab ~/tmp $ ruby c50138.rb < c50138.inp2
number of matches: 298208861472
took 0.05726237 s
จำนวนการจับคู่สำหรับหน้ากากเดียวที่คำนวณได้โดยสัมประสิทธิ์ทวินาม ดังนั้นสำหรับตัวอย่างเช่น122020
ต้องการ 3 2
s ที่เต็มไปด้วย 1 0
และ 1
2 ดังนั้นจึงมีnCr(3,2)=nCr(3,1)=3!/(2!*1!)=3
สตริงไบนารีที่แตกต่างกันตรงกับหน้ากากนี้
ทางแยกระหว่าง n มาสก์ m_1, m_2, ... m_n เป็นมาสก์ q ซึ่งเป็นสตริงไบนารี s จับคู่ q เฉพาะถ้ามันตรงกับมาสก์ทั้งหมด m_i
หากเราใช้สองมาสก์ m_1 และ m_2 จุดตัดของมันจะถูกคำนวณอย่างง่ายดาย เพียงตั้งค่า m_1 [i] = m_2 [i] ถ้า m_1 [i] == 2 จุดตัดระหว่าง122020
และ111222
คือ111020
:
122020 (matched by 3 strings, 111000 110010 101010)
111222 (matched by 1 string, 111000)
111020 (matched by 1 string, 111000)
มาสก์แต่ละตัวมีการจับคู่โดย 3 + 1 = 4 สตริงมาสก์ interesection จับคู่กับหนึ่งสายดังนั้นจึงมี 3 + 1-1 = 3 สายที่ไม่ซ้ำกันที่ตรงกับหนึ่งหรือทั้งสองมาสก์
ฉันจะโทรหา N (m_1, m_2, ... ) จำนวนสตริงที่ตรงกับ m_i ทั้งหมด การใช้ตรรกะเดียวกันกับข้างต้นเราสามารถคำนวณจำนวนของสตริงที่ไม่ซ้ำกันซึ่งจับคู่กับหน้ากากอย่างน้อยหนึ่งตัวที่กำหนดโดยหลักการการแยกแบบรวมและดูด้านล่างเช่นกัน:
N(m_1) + N(m_2) + ... + N(m_n) - N(m_1,m_2) - ... - N(m_n-1,m_n) + N(m_1,m_2,m_3) + N(m_1,m_2,m_4) + ... N(m_n-2,m_n-1,m_n) - N(m_1,m_2,m_3,m_4) -+ ...
มีหลายอย่างหลายอย่างรวมกันหลายอย่างพูด 30 หน้ากากจาก 200
ดังนั้นวิธีการแก้ปัญหานี้ทำให้ข้อสันนิษฐานว่ามีมาสก์อินพุทสูงมากที่มีอยู่ไม่มากเช่น n-tuples ส่วนใหญ่ของมาสก์ n> 2 จะไม่มีการจับคู่ทั่วไป
ใช้รหัสที่นี่รหัสที่ ideone อาจล้าสมัย
ฉันได้เพิ่มฟังก์ชั่นremove_duplicates
ที่สามารถใช้ในการประมวลผลอินพุตล่วงหน้าและลบมาสก์ได้m_i
เช่นกันซึ่งสตริงทั้งหมดที่ตรงกับมันก็ตรงกับมาสก์อื่นm_j
สำหรับอินพุตปัจจุบันสิ่งนี้ใช้เวลานานจริง ๆ เพราะไม่มีมาสก์ดังกล่าว ดังนั้นฟังก์ชั่นนี้ยังไม่ได้ใช้กับข้อมูลในรหัสด้านล่าง
รหัส:
# factorial table
FAC = [1]
def gen_fac(n)
n.times do |i|
FAC << FAC[i]*(i+1)
end
end
# generates a mask such that it is matched by each string that matches m and n
def diff_mask(m,n)
(0..m.size-1).map do |i|
c1 = m[i]
c2 = n[i]
c1^c2==1 ? break : c1&c2
end
end
# counts the number of possible balanced strings matching the mask
def count_mask(m)
n = m.size/2
c0 = n-m.count(0)
c1 = n-m.count(1)
if c0<0 || c1<0
0
else
FAC[c0+c1]/(FAC[c0]*FAC[c1])
end
end
# removes masks contained in another
def remove_duplicates(m)
m.each do |x|
s = x.join
m.delete_if do |y|
r = /\A#{s.gsub(?3,?.)}\Z/
(!x.equal?(y) && y =~ r) ? true : false
end
end
end
#intersection masks of cn masks from m.size masks
def mask_diff_combinations(m,n=1,s=m.size,diff1=[3]*m[0].size,j=-1,&b)
(j+1..s-1).each do |i|
diff2 = diff_mask(diff1,m[i])
if diff2
mask_diff_combinations(m,n+1,s,diff2,i,&b) if n<s
yield diff2,n
end
end
end
# counts the number of balanced strings matched by at least one mask
def count_n_masks(m)
sum = 0
mask_diff_combinations(m) do |mask,i|
sum += i%2==1 ? count_mask(mask) : -count_mask(mask)
end
sum
end
time = Time.now
# parse input
d = STDIN.each_line.map do |line|
line.chomp.strip.gsub('2','3')
end
d.delete_if(&:empty?)
d.shift
d.map!{|x|x.chars.map(&:to_i)}
# generate factorial table
gen_fac([d.size,d[0].size].max+1)
# count masks
puts "number of matches: #{count_n_masks(d)}"
puts "took #{Time.now-time} s"
สิ่งนี้เรียกว่าหลักการการแยกแบบรวม แต่ก่อนที่ใครบางคนจะชี้ให้ฉันเห็นฉันมีหลักฐานของตัวเองดังนั้นที่นี่มันไป การทำสิ่งที่ตัวเองรู้สึกดีนั้น
ให้เราพิจารณากรณีของมาสก์ 2 ตัวจากนั้นโทร0
และ1
ก่อน เราใช้สตริงไบนารี่ที่มีความสมดุลทุกตัวและจำแนกมันตามรูปแบบที่ตรงกัน c0
คือจำนวนของผู้ที่การแข่งขันเพียงหน้ากาก0
, c1
nunber ของผู้ที่ตรงกับเพียง1
, c01
ผู้ที่หน้ากากแข่งขันและ0
1
อนุญาตs0
เป็นจำนวนรวมของจำนวนการแข่งขันสำหรับหน้ากากแต่ละอัน (พวกเขาอาจทับซ้อนกัน) อนุญาตs1
เป็นผลรวมของจำนวนการแข่งขันสำหรับแต่ละคู่ (รวมกัน 2 คู่) ของมาสก์ อนุญาตs_i
เป็นผลรวมของจำนวนการแข่งขันสำหรับการรวมกันของ (i + 1) ของมาสก์ จำนวนการจับคู่ของ n-mask คือจำนวนของสตริงไบนารีที่ตรงกับมาสก์ทั้งหมด
หากมีมาสก์ n ตัวเอาต์พุตที่ต้องการคือผลรวมของทั้งหมดc
คือ c = c0+...+cn+c01+c02+...+c(n-2)(n-1)+c012+...+c(n-3)(n-2)(n-1)+...+c0123...(n-2)(n-1)
. สิ่งที่โปรแกรมคำนวณคือผลรวมสลับของทั้งหมดs
คือ s = s_0-s_1+s_2-+...+-s_(n-1)
. s==c
เราต้องการที่จะพิสูจน์ให้เห็นว่า
n = 1 ชัดเจน พิจารณา n = 2 นับการแข่งขันทั้งหมดของหน้ากาก0
ให้c0+c01
(จำนวนสตริงที่ตรงกันเพียง 0 + จับคู่เหล่านั้นทั้งใน0
และ1
) นับการแข่งขันทั้งหมดของให้1
c1+c02
เราสามารถอธิบายสิ่งนี้ได้ดังนี้:
0: c0 c01
1: c1 c10
ตามคำนิยาม, s0 = c0 + c1 + c12
. s1
คือจำนวนรวมของการแข่งขันของการรวมกันของ[0,1]
2 รายการ ทั้งหมด uniqye c_ij
s c01=c10
เก็บไว้ในใจว่า
s0 = c0 + c1 + 2 c01
s1 = c01
s = s0 - s1 = c0 + c1 + c01 = c
ดังนั้นs=c
สำหรับ n = 2
ตอนนี้ให้พิจารณา n = 3
0 : c0 + c01 + c02 + c012
1 : c1 + c01 + c12 + c012
2 : c2 + c12 + c02 + c012
01 : c01 + c012
02 : c02 + c012
12 : c12 + c012
012: c012
s0 = c0 + c1 + c2 + 2 (c01+c02+c03) + 3 c012
s1 = c01 + c02 + c12 + 3 c012
s2 = c012
s0 = c__0 + 2 c__1 + 3 c__2
s1 = c__1 + 3 c__2
s2 = c__2
s = s0 - s1 + s2 = ... = c0 + c1 + c2 + c01 + c02 + c03 + c012 = c__0 + c__1 + c__2 = c
ดังนั้นs=c
สำหรับ n = 3 c__i
แสดงถึงของทั้งหมดc
ที่มี (i + 1) ดัชนีเช่นc__1 = c01
สำหรับ n = 2 และc__1 = c01 + c02 + c12
สำหรับ n == 3
สำหรับ n = 4 รูปแบบเริ่มปรากฏ:
0: c0 + c01 + c02 + c03 + c012 + c013 + c023 + c0123
1: c1 + c01 + c12 + c13 + c102 + c103 + c123 + c0123
2: c2 + c02 + c12 + c23 + c201 + c203 + c213 + c0123
3: c3 + c03 + c13 + c23 + c301 + c302 + c312 + c0123
01: c01 + c012 + c013 + c0123
02: c02 + c012 + c023 + c0123
03: c03 + c013 + c023 + c0123
12: c11 + c012 + c123 + c0123
13: c13 + c013 + c123 + c0123
23: c23 + c023 + c123 + c0123
012: c012 + c0123
013: c013 + c0123
023: c023 + c0123
123: c123 + c0123
0123: c0123
s0 = c__0 + 2 c__1 + 3 c__2 + 4 c__3
s1 = c__1 + 3 c__2 + 6 c__3
s2 = c__2 + 4 c__3
s3 = c__3
s = s0 - s1 + s2 - s3 = c__0 + c__1 + c__2 + c__3 = c
ดังนั้นs==c
สำหรับ n = 4
โดยทั่วไปเราได้ค่าสัมประสิทธิ์ทวินามแบบนี้ (↓คือ i, →คือ j):
0 1 2 3 4 5 6 . . .
0 1 2 3 4 5 6 7 . . .
1 1 3 6 10 15 21 . . .
2 1 4 10 20 35 . . .
3 1 5 15 35 . . .
4 1 6 21 . . .
5 1 7 . . .
6 1 . . .
. .
. .
. .
หากต้องการดูสิ่งนี้ให้พิจารณาว่าสำหรับบางคนi
และj
มี:
- x = ncr (n, i + 1): ชุดค่าผสม C สำหรับจุดตัดของ (i + 1) ปิดบัง n
- y = ncr (ni-1, ji): สำหรับชุดค่าผสมแต่ละ C ด้านบนมีชุดค่าผสม y ที่แตกต่างกันสำหรับการแยกของมาสก์ (j + 2) ออกจากชุดที่มี C
- z = ncr (n, j + 1): ชุดค่าผสมที่แตกต่างกันสำหรับจุดตัดของ (j + 1) มาสก์ออกจาก n
เนื่องจากอาจทำให้เกิดความสับสนนี่คือข้อ จำกัด ที่ใช้กับตัวอย่าง สำหรับ i = 1, j = 2, n = 4, มันมีลักษณะเช่นนี้ (cf. ด้านบน):
01: c01 + c012 + c013 + c0123
02: c02 + c012 + c023 + c0123
03: c03 + c013 + c023 + c0123
12: c11 + c012 + c123 + c0123
13: c13 + c013 + c123 + c0123
23: c23 + c023 + c123 + c0123
ดังนั้นที่นี่ x = 6 (01, 02, 03, 12, 13, 23), y = 2 (สองคมีสามดัชนีสำหรับแต่ละชุด), z = 4 (c012, c013, c023, c123)
โดยรวมแล้วมีx*y
ค่าสัมประสิทธิ์c
กับ (ญ + 1) ดัชนีและมีz
คนที่แตกต่างกันเพื่อให้แต่ละเกิดขึ้นครั้งซึ่งเราเรียกว่าค่าสัมประสิทธิ์x*y/z
โดยพีชคณิตง่ายที่เราได้รับk_ij
k_ij = ncr(n,i+1) ncr(n-i-1,j-i) / ncr(n,j+1) = ncr(j+1,i+1)
ดังนั้นดัชนีจะได้รับจากk_ij = nCr(j+1,i+1)
หากคุณจำการขาดดุลทั้งหมดที่เราต้องแสดงก็คือผลรวมการสลับของแต่ละคอลัมน์ให้ 1
ผลรวมการสลับs0 - s1 + s2 - s3 +- ... +- s(n-1)
สามารถแสดงเป็น:
s_j = c__j * ∑[(-1)^(i+j) k_ij] for i=0..n-1
= c__j * ∑[(-1)^(i+j) nCr(j+1,i+1)] for i=0..n-1
= c__j * ∑[(-1)^(i+j) nCr(j+1,i)]{i=0..n} - (-1)^0 nCr(j+1,0)
= (-1)^j c__j
s = ∑[(-1)^j s_j] for j = 0..n-1
= ∑[(-1)^j (-1)^j c__j)] for j=0..n-1
= ∑[c__j] for j=0..n-1
= c
ดังนั้นs=c
ทั้งหมด n = 1,2,3, ...