3 import sys, os, re, fractions
4 from fractions import Fraction
8 header_re = re.compile(r'^(\d+) into (\d+).*:.* ([\d/]+)')
9 dissect_re = re.compile(r'\s+(\d+)\s+x\s+\((([\d/]+ \+ )*[\d/]+)\)')
13 def find_min_frag(dissection):
15 for side in dissection:
16 for count, pieces in side:
19 if ret is None or ret > piece:
21 assert ret is not None
24 def verify(n, m, dissection):
25 collections = [{}, {}]
27 for count, pieces in dissection[0]:
28 assert sum(pieces) == n
31 collections[0].setdefault(piece, 0)
32 collections[0][piece] += count
35 for count, pieces in dissection[1]:
36 assert sum(pieces) == m
39 collections[1].setdefault(piece, 0)
40 collections[1][piece] += count
42 assert collections[0] == collections[1]
44 def add_m_dissector(n,m):
47 prev = dissections[(n-m, m)]
48 ret = [prev[0][:], prev[1][:]]
49 for i in range(len(ret[0])):
50 ret[0][i] = (ret[0][i][0], ret[0][i][1] + [m])
51 ret[1].append((m, (m,)))
54 def gcd_dissector(n,m):
55 gcd = fractions.gcd(n,m)
56 return [[(m, (gcd,) * (n/gcd))],
57 [(n, (gcd,) * (m/gcd))]]
59 def invent_dissection(n,m):
60 # Try to come up with a dissection by assorted simple methods.
62 for dissector, this_reason in [
63 (gcd_dissector, "trivial gcd solution"),
64 (add_m_dissector, "extend solution for (%d,%d)" % (n-m, m)),
66 dissection = dissector(n, m)
67 if dissection is None: continue
68 verify(n, m, dissection)
69 this_best = find_min_frag(dissection)
71 best, best_reason = this_best, this_reason
73 return dissection, this_reason
78 # Look for appropriate file(s).
79 for filename, nature in [("data/main.%d.%d" % (n,m), "e"),
80 ("data/partition.%d.%d" % (n,m), "p"),
81 ("data/manual.%d.%d" % (n,m), "m")]:
82 if os.path.exists(filename):
83 with open(filename) as f:
85 match = header_re.match(header)
86 assert match is not None
87 assert int(match.group(1)) == n
88 assert int(match.group(2)) == m
89 this_best = Fraction(match.group(3))
91 # Now read the actual dissection.
93 for line in f.readlines():
94 match = dissect_re.match(line)
96 count = int(match.group(1))
97 pieces = map(Fraction, match.group(2).split(" + "))
100 dissection[0].append((count, pieces))
102 dissection[1].append((count, pieces))
104 assert False, "wrong length"
105 verify(n, m, dissection)
106 assert find_min_frag(dissection) == this_best
107 dissections[(n,m)] = dissection
114 # We don't have a stored solution at all, so make up something
115 # plausible if we can.
116 dissection, best_nature = invent_dissection(n, m)
117 best = find_min_frag(dissection)
118 dissections[(n,m)] = dissection
120 bound, bound_type = bounds.upper_bound(n, m)
125 elif best_nature == 'e':
128 elif best_nature == 'p':
133 show_bound = " – %s"
134 sys.stdout.write("<td class=\"%s\">" % tdclass)
135 sys.stdout.write(str(best))
137 sys.stdout.write(show_bound % str(bound))
138 sys.stdout.write("</td>\n")
141 limit = 24 # FIXME: configurable
142 sys.stdout.write("""\
145 <title>Known bounds for stick-dissecting problem</title>
146 <style type="text/css">
148 border: 1px solid black;
149 border-collapse: collapse;
152 background-color: #00ff00;
155 background-color: #aaff00;
158 background-color: #ffff00;
161 background-color: #ff8888;
168 sys.stdout.write("<tr>\n")
169 sys.stdout.write("<th>n \\ m</th>\n")
170 for m in range(2,limit-1):
171 sys.stdout.write("<th>%d</th>\n" % m)
172 sys.stdout.write("</tr>\n")
173 for n in range(2,limit):
174 sys.stdout.write("<tr>\n")
175 sys.stdout.write("<th>%d</th>\n" % n)
178 sys.stdout.write("</tr>\n")
179 sys.stdout.write("</table>\n")
180 sys.stdout.write("</body></html>\n")
182 if __name__ == "__main__":