Constant time 2026-10-20
A time complexity bounded by independently of input size, under a stated computational cost model. Fixed-size machine arithmetic may be constant time, whereas arbitrary-precision arithmetic depends on operand length.
Greedy Egyptian fraction expansion 2026-10-25
For a positive proper residual fraction , with , choose , append the unit fraction , and replace the residual by in lowest terms. This is a greedy algorithm: the chosen unit fraction is the largest one not exceeding the residual.
The inequalities imply . Thus the numerator of the reduced residual strictly decreases while positive, proving algorithm termination by a nonnegative-integer loop variant. The loop invariant is that the accumulated sum plus the residual is the original rational number. Since and , the new residual is less than . If it remains positive, its next chosen denominator is therefore strictly greater than ; this expansion also satisfies the distinct-denominator convention. The proof does not bound the denominators by the input's fixed-width range. Arbitrary-precision arithmetic, checked conversion, or a different representable expansion can therefore be necessary in code.
In binary long division, first double the divisor until it exceeds
x, then halve it successively. Keep p = 2^j and d = y*p, so no exponentiation is needed inside a loop:q := 0; r := x;
j := 0; p := 1; d := y;
WHILE d <= x DO
d := 2*d;
p := 2*p;
j := j+1
END;
WHILE j > 0 DO
d := d DIV 2;
p := p DIV 2;
j := j-1;
IF r >= d THEN
r := r-d;
q := q+p
END
ENDThe first loop invariant is , , , and . Every doubling preserves it. For integer inputs, after at most iterations when ,
d > x; if , this loop has no iterations. The unbounded increase of proves termination. Equivalently, one can use the decreasing nonnegative loop variant while the guard is true.At the top of the second loop the loop invariant isIt holds on entry because
d > x = r. Suppose the old d is and the old p is . After the three halving assignments, and the old remainder satisfies . If , the desired new bound already holds. Otherwise subtracting leaves and adding to q preserves because . The divisions by two are exact: before each halving, j > 0 makes both p and d even.The loop variant
j decreases by one each iteration. At exit , so d = y and the loop invariant gives the required remainder bound. The work per iteration is constant in the unit-cost model, and the two loops have the same number of iterations. For ,The case , including , takes constant time. These are arithmetic-operation counts with sufficient range for the doubled values; arbitrary-precision arithmetic incurs additional bit complexity. If the inputs were arbitrary positive real numbers, the uniform bound would depend on rather than only ; the stated is the conventional integer division interpretation.Use schoolbook multiplication on the digit coefficients:
function Mul(a, b: number): number
new(result, len(a)+len(b))
for k := 0 to len(result)-1 do
result[k] := 0
for i := 0 to len(a)-1 do
for j := 0 to len(b)-1 do
result[i+j] := result[i+j] + a[i]*b[j]
Normal(result)
return resultThis is discrete convolution of the digit arrays. Expanding the product showsbefore normalization. Normalization preserves the value and establishes the digit bounds. Allocating fresh storage makes input aliasing harmless. The convolution uses coefficient products for input lengths . Intermediate products and sums need sufficiently wide or arbitrary-precision arithmetic; fitting each normalized digit into a small integer does not alone prevent accumulator overflow.
Choose the reciprocal-factorial summation
eb for asymptotically high accuracy, counting multiplications and divisions as arithmetic work. For tolerance , linear powering in part (a)(i) takes multiplications; binary exponentiation in part (a)(ii) reduces this to . Part (c) also has arithmetic work, since its required grows like , though its constants and cancellation error differ.By the reciprocal-factorial series remainder bound, the omitted positive tail satisfiesThus the factorial denominator grows much faster than an exponential in the number of iterations. For decimal places, setting of order gives by Stirling's approximation, rather than binary-power arithmetic steps. This is a comparison in an exact-arithmetic operation-count model; the stated Big O notation does not determine the exact winner for every small tolerance. Taken literally,
eb uses zero multiplications and only divisions. Fixed double-precision floating-point format cannot deliver arbitrarily many correct decimal places, and the precision-dependent cost of arbitrary-precision arithmetic is a separate issue.Source paper: released examination archive, member 2018/L/a10098w1.pdf. The cover identifies the Long Vacation sitting of Thursday 5 September 2019. That printed identity takes precedence over the archive member's directory name.
The implementations use Scala 2 syntax. Fixed-width results are checked when arbitrary-precision arithmetic is needed internally; the mathematical contracts do not make integer overflow harmless. All eight questions are covered.
An Egyptian fraction need not be proper: several positive unit fractions may sum to at least one. Accordingly, accumulate a general nonnegative reduced fraction, rather than calling the proper-input-only The loop invariant is , , , , andWriting , the fraction for the extended prefix has numerator and denominator . These are the
Reduce on every intermediate value. First define an exact helper:def toRatExact(e: Array[Int]): (BigInt, BigInt) = {
var n = BigInt(0)
var d = BigInt(1)
var i = 0
while (i < e.length) {
require(e(i) > 0)
val x = BigInt(e(i))
val g = d.gcd(x)
val nextN = n * (x / g) + d / g
val nextD = d * (x / g)
val h = nextN.gcd(nextD)
n = nextN / h
d = nextD / h
i += 1
}
(n, d)
}
def toRat(e: Array[Int]): (Int, Int) = {
val result = toRatExact(e)
require(result._1.isValidInt && result._2.isValidInt,
"Reduced result does not fit (Int, Int)")
(result._1.toInt, result._2.toInt)
}nextN and nextD in the code. Reducing them preserves the value and restores coprimality. The loop variant is . At exit the helper returns the exact sum in lowest terms, including for the empty array.There are three distinct ways to control integer overflow: cancel the common denominator factor before multiplication, reduce after each addition, and use arbitrary-precision arithmetic when the remaining values still exceed fixed-width bounds. Cancellation reduces intermediate size but cannot guarantee that an exact answer fits
(Int, Int). For example, the sum is in lowest terms, whose denominator exceeds Int.MaxValue. No implementation returning that fixed type can represent every permitted input exactly. The wrapper therefore checks this unavoidable result-type limitation instead of silently truncating; toRatExact supplies the unrestricted mathematical result.[ref]Scala 2.12 BigInt API documentation. Checked representability with isValidInt before the potentially truncating toInt conversion
Count each invocation before delegating, including duplicate insertions, and reset after clearing:
class IntegerSetWithCount extends IntegerSet() {
private var attempts = BigInt(0)
override def insert(x: Int): Unit = {
attempts += 1
super.insert(x)
}
override def clear: Unit = {
super.clear
attempts = BigInt(0)
}
def getCount: BigInt = attempts
}The inherited implementation already maintains
size; the new counter measures insertion attempts, not successful increases in cardinality. Deletion and popping do not subtract from it. Incrementing before delegation also counts an attempted insertion that subsequently fails a capacity check. Arbitrary-precision arithmetic avoids silently wrapping the historical count, though its arithmetic is not constant-cost for arbitrarily large counts.As the source supplies no object constructor signature for the concrete set, this code uses a fresh default-constructible
IntegerSet, with matching default capacity. Its correct behavior also assumes the base implementation does not internally invoke these overridable operations in a way that interferes with the counter. Part e exhibits that dependency.Source-signature qualification for the following set implementations: the intended membership query must be declared
def contains(x: Int): Boolean. The printed abstract declaration omits its result type; interpreted literally as Scala 2 procedure syntax, it returns Unit. The solutions use the explicit Boolean query contract, and Unit for the two mutators. Parameterless clear is overridden without adding parentheses.[ref]Scala 2.13 basic declarations and definitions specification. Procedures: a method declaration omitting its result type implicitly has Unit result