Skip to content

Commit 76001b1

Browse files
committed
add day 05: Print Queue
1 parent dae191e commit 76001b1

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

05_print_queue.rb

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using(Module.new { refine(Array) {
2+
def middle
3+
self[size / 2]
4+
end
5+
}})
6+
7+
LSHIFT = 30
8+
seps = %w(| ,).map(&:freeze).freeze
9+
10+
detect_nontransitive = ARGV.delete('-t')
11+
verbose = ARGV.delete('-v')
12+
verbose1 = ARGV.delete('-v1')
13+
verbose2 = ARGV.delete('-v2')
14+
strict = !ARGV.delete('-n')
15+
16+
order, pages, empty = ARGF.each("\n\n", chomp: true).zip(seps).map { |section, sep|
17+
section.each_line.map { |l| l.split(sep).map(&method(:Integer)).freeze }.freeze
18+
}
19+
raise "bad #{empty}" if empty
20+
21+
uniq_page = pages.flatten.to_h { |k| [k, true] }.freeze
22+
23+
order = order.to_h { |a, b, *bad|
24+
raise "bad #{bad}" unless bad.empty?
25+
[a, b].each { |pg| raise "rule for unneeded page #{pg}" if strict && !uniq_page[pg] }
26+
[a << LSHIFT | b, -1]
27+
}.freeze
28+
29+
if detect_nontransitive
30+
non_transitive = uniq_page.keys.combination(3).filter_map { |a, b, c|
31+
if order[a << LSHIFT | b] && order[b << LSHIFT | c] && order[c << LSHIFT | a]
32+
[a, b, c].freeze
33+
elsif order[a << LSHIFT | c] && order[c << LSHIFT | b] && order[b << LSHIFT | a]
34+
[a, c, b].freeze
35+
end
36+
}.freeze
37+
non_transitive.each { |a, b, c| puts "#{a} < #{b} < #{c}" }
38+
puts non_transitive.size
39+
end
40+
41+
n = uniq_page.size
42+
all_pairs = order.size == n * (n - 1) / 2
43+
44+
if verbose
45+
puts n
46+
puts order.size
47+
puts n * (n - 1) / 2
48+
puts all_pairs
49+
end
50+
51+
good, bad = pages.partition { |ps|
52+
# no pair is in the *wrong* order.
53+
(all_pairs ? ps.each_cons(2) : ps.combination(2)).none? { |x, y|
54+
order[y << LSHIFT | x]
55+
}
56+
}.map(&:freeze)
57+
58+
good.each { |g| puts "#{g.middle} #{g}" } if verbose1
59+
p good.sum(&:middle)
60+
61+
bsort = bad.map { |b| b.sort { |x, y| order[x << LSHIFT | y] || 1 }.freeze }.freeze
62+
bad.zip(bsort) { |b, s| puts "#{s.middle} #{b} -> #{s}" } if verbose2
63+
p bsort.sum(&:middle)
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
require 'benchmark'
2+
3+
using(Module.new { refine(Array) {
4+
def middle
5+
self[size / 2]
6+
end
7+
}})
8+
9+
LSHIFT = 30
10+
11+
bench_candidates = []
12+
13+
bench_candidates << def sort_func(order, bad)
14+
bad.sum { |b| b.sort { |x, y| order[x << LSHIFT | y] || 1 }.middle }
15+
end
16+
17+
bench_candidates << def rand_pivot_quickselect(order, bad)
18+
quicksel = ->(a, i) {
19+
pivot = a.sample
20+
21+
low = a.select { |x| order[x << LSHIFT | pivot] }
22+
high = a.select { |x| order[pivot << LSHIFT | x] }
23+
k = low.size
24+
25+
if i < k
26+
quicksel[low, i]
27+
elsif i > k
28+
quicksel[high, i - k - 1]
29+
else
30+
pivot
31+
end
32+
}
33+
bad.sum { |b| quicksel[b, b.size / 2] }
34+
end
35+
36+
bench_candidates << def rand_pivot_quickselect_mut(order, bad)
37+
quicksel = ->(a, l, r, i) {
38+
pivot_i = rand(l...r)
39+
pivot = a[pivot_i]
40+
a[pivot_i] = a[r - 1]
41+
a[r - 1] = pivot
42+
43+
small = l
44+
45+
(l...r).each { |i|
46+
next unless order[a[i] << LSHIFT | pivot]
47+
a[small], a[i] = [a[i], a[small]]
48+
small += 1
49+
}
50+
51+
if i < small
52+
quicksel[a, l, small, i]
53+
elsif i > small
54+
# for my implementation I've decided not to swap the pivot back into place,
55+
# so i will decrease by 1 here.
56+
quicksel[a, small, r - 1, i - 1]
57+
else
58+
pivot
59+
end
60+
}
61+
bad.sum { |b|
62+
b = b.dup
63+
quicksel[b, 0, b.size, b.size / 2]
64+
}
65+
end
66+
67+
bench_candidates << def median_of_medians(order, bad)
68+
sort = ->a { a.sort { |x, y| order[x << LSHIFT | y] || 1 }.freeze }
69+
median_of_median = ->(a, i) {
70+
slices = a.each_slice(5).to_a.map(&:freeze).freeze
71+
medians = slices.map { |s| sort[s].middle }.freeze
72+
pivot = if medians.size <= 5
73+
sort[medians].middle
74+
else
75+
median_of_median[medians, medians.size / 2]
76+
end
77+
78+
low = a.select { |x| order[x << LSHIFT | pivot] }
79+
high = a.select { |x| order[pivot << LSHIFT | x] }
80+
k = low.size
81+
82+
if i < k
83+
median_of_median[low, i]
84+
elsif i > k
85+
median_of_median[high, i - k - 1]
86+
else
87+
pivot
88+
end
89+
}
90+
bad.sum { |b| median_of_median[b, b.size / 2] }
91+
end
92+
93+
seps = %w(| ,).map(&:freeze).freeze
94+
order, pages, empty = ARGF.each("\n\n", chomp: true).zip(seps).map { |section, sep|
95+
section.each_line.map { |l| l.split(sep).map(&method(:Integer)).freeze }.freeze
96+
}
97+
raise "bad #{empty}" if empty
98+
99+
strict = true
100+
uniq_page = pages.flatten.to_h { |k| [k, true] }.freeze
101+
102+
order = order.to_h { |a, b, *bad|
103+
raise "bad #{bad}" unless bad.empty?
104+
[a, b].each { |pg| raise "rule for unneeded page #{pg}" if strict && !uniq_page[pg] }
105+
[a << LSHIFT | b, -1]
106+
}.freeze
107+
108+
n = uniq_page.size
109+
all_pairs = order.size == n * (n - 1) / 2
110+
111+
results = {}
112+
113+
bad = pages.select { |ps|
114+
# any pair is in the *wrong* order.
115+
(all_pairs ? ps.each_cons(2) : ps.combination(2)).any? { |x, y|
116+
order[y << LSHIFT | x]
117+
}
118+
}.freeze
119+
120+
Benchmark.bmbm { |bm|
121+
bench_candidates.each { |f|
122+
bm.report(f) { 100.times { results[f] = send(f, order, bad) } }
123+
}
124+
}
125+
126+
# Obviously the benchmark would be useless if they got different answers.
127+
if results.values.uniq.size != 1
128+
results.each { |k, v| puts "#{k} #{v}" }
129+
raise 'differing answers'
130+
end

0 commit comments

Comments
 (0)