1 # -*- coding: utf-8 -*-
3 # Compute upper bounds on min fragment.
6 from fractions import Fraction
9 """Floor function that can cope with Fraction. Bah, why does the
10 fractions module not contain one of these?"""
11 if q < 0: return -ceil(-q)
14 """Ceiling function that can cope with Fraction. Also bah."""
15 if q < 0: return -floor(-q)
16 if q == int(q): return int(q)
27 """Upper bound on the best solution by <lj user="writinghawk">.
29 We begin by ruling out the case where m | n, which is the only case in
30 which we can avoid cutting _any_ m-stick. With that constraint, we
31 know the min frag must be at most m/2, and we assume we will cut
32 _every_ m-stick at least once.
34 Now suppose the min frag is s, so that every piece is at least s and
35 at most m-s. We derive constraints on s by considering the total
36 number of pieces in the dissection.
38 Each stick of length n is cut into at least n/(m-s) and at most n/s
39 pieces. Because it must also have an integer number of pieces, that
40 means it's at least ⌈n/(m-s)⌉ and at most ⌊n/s⌋. Summing over all
41 n-sticks, the total number of pieces P in the dissection must satisfy
42 m⌈n/(m-s)⌉ ≤ P ≤ m⌊n/s⌋.
44 By similarly considering the number of pieces that a stick of length m
45 is cut into, we derive a second inequality n⌈m/(m-s)⌉ ≤ P ≤ n⌊m/s⌋
46 (identical to the first inequality, but with n,m are swapped
47 everywhere they appear except in the divisor m-s).
49 So when s becomes big enough that either of those inequalities is
50 self-inconsistent (with LHS > RHS) or when the two are incompatible
51 (because they define ranges for P that do not meet), there can be no
52 dissection with s that large."""
58 # See if s+ε is viable.
60 # lo_n = m * ⌈n/(m - s - ε)⌉
61 # = m * ⌈n/(m - s) + ε'⌉
62 lo_n = m * ceilpe(Fraction(n, m - s))
64 # hi_n = m * ⌊n/(s + ε)⌋
66 hi_n = m * floorme(Fraction(n, s))
68 # lo_m = n * ⌈m/(m - s - ε)⌉
69 # = n * ⌈m/(m - s) + ε'⌉
70 lo_m = n * ceilpe(Fraction(m, m - s))
72 # hi_m = n * ⌊m/(s + ε)⌋
74 hi_m = n * floorme(Fraction(m, s))
76 if max(lo_n, lo_m) > min(hi_n, hi_m):
79 # Find the next point where one of those floors changes.
80 s_new = min(m - Fraction(n, ceilpe(Fraction(n, m - s))),
81 Fraction(n, floorme(Fraction(n, s))),
82 m - Fraction(m, ceilpe(Fraction(m, m - s))),
83 Fraction(m, floorme(Fraction(m, s))))
87 """Upper bound on certain n,m pairs by Tom Womack.
89 If m/3 is an integer and also a multiple of n-m, then no solution can
92 Proof: suppose the shortest fragment is m/3 + ε for some ε > 0. Then
93 if any segment of stick less than _twice_ that length arises while
94 you're dissecting, it must remain whole. In particular, any fragment
95 shorter than 2m/3 + 2ε must be monolithic in this way.
97 So consider a short stick (of length m) from which we cut a piece of
98 length m/3+ε. The other piece of that stick has length 2m/3-ε and
99 hence is monolithic; so it must have been cut as a whole from a longer
100 stick of length n. Let n = m+k, because we'll want to talk about k a
101 lot. Now we reason as follows:
103 - a piece m/3+ε shares a short stick with 2m/3-ε
104 - that 2m/3-ε shares a long stick with m/3+k+ε
105 - that m/3+k+ε shares a short stick with 2m/3-k-ε
106 - that 2m/3-k-ε shares a long stick with m/3+2k+ε
108 ... and so on, so that we derive the existence of pieces of lengths
109 m/3+ε, m/3+k+ε, m/3+2k+ε, ... 2m/3+ε (which we must reach since m/3 is
110 an exact multiple of k). That 2m/3+ε is still monolithic (recall that
111 only a piece of at least 2m/3+2ε can avoid being so) and so it must
112 share a short stick with a piece of length m/3-ε, which is shorter
113 than our presumed minimum fragment. Contradiction."""
114 if m % (3*(n-m)) == 0:
119 (writinghawk, "writinghawk's piece-counting bound"),
120 (fivemack, "fivemack's conditional m/3 bound"),
123 def upper_bound(n, m):
125 best = m, "trivial bound of m"
126 for boundtype in bounds:
127 b = boundtype[0](n, m)
130 best = b, boundtype[1]
132 best = b, best[1] + ", " + boundtype[1]
139 b = upper_bound(n, m)
140 print "upper_bound(%d,%d) = %s (%s)" % (n, m, b[0], b[1])
142 if __name__ == "__main__":