# interval.py (2.0) # Split a value x into approximately equal intervals with the remainder # distributed so the sum of the result sequence is equal to x. # Increment while remainder is non-zero. # @note Remainder is always distributed at the front. def split_1(value, interval): assert interval > 0 result = [] q, r = divmod(value, interval) for _ in range(interval): if r == 0: result.append(q) else: result.append(q + 1) r -= 1 return result # Recursive split. # @note One value tends to repeat followed by alternation. def split_2(value, interval): if interval <= 0: return [] x = round(value / interval) return [x] + split_2(value - x, interval - 1) def split_2(value, interval): assert interval > 0 result = [] v = value i = interval for _ in range(interval): x = round(v / i) v = v - x i = i - 1 result.append(x) return result # Minimize error on r/t (Bresenham). # @note Uniform distribution of remainder (rear biased). def split_3(value, interval): assert interval > 0 result = [] q, r = divmod(value, interval) t = interval - r e = 0 for _ in range(interval): if e < t: result.append(q) e += r else: result.append(q + 1) e -= t return result # Minimize error on r/t (Bresenham). # @note Uniform distribution of remainder (symmetric). def split_4(value, interval): assert interval > 0 result = [] q, r = divmod(value, interval) t = interval e = t-r for _ in range(interval): e2 = 2*e if e2 + r > 0: e -= r if e2 - t < 0: result.append(q + 1) e += t else: result.append(q) return result # Main. from timeit import timeit def _test(n, f): def check(v, i): a = f(v, i) ok = len(a) == i ok = ok and sum(a) == v t = v // i ok = ok and all(x == t or x == t+1 for x in a) return ok for v in range(-2*n, 2*n+1): for i in range(1, n+1): assert check(v, i), f'bad match at {v},{i}' def _show(n, f): for i in range(n+1): a = f(i, n) print(a, sum(a) == i) def _time(n, f): def g(): for v in range(-n, n+1): for i in range(1, n+1): f(v, i) print('Elapsed: {:.6f}s'.format(timeit(g, number=1))) def _main(): fs = [ split_1, split_2, split_3, split_4 ] n = 10 for f in fs: _test(n, f) n = 5 m = 100 for f in fs: _show(n, f) _time(m, f) n = 100 m = 15 for f in fs: print(f(n, m)) if __name__ == '__main__': _main()
Standard input is empty
[0, 0, 0, 0, 0] True [1, 0, 0, 0, 0] True [1, 1, 0, 0, 0] True [1, 1, 1, 0, 0] True [1, 1, 1, 1, 0] True [1, 1, 1, 1, 1] True Elapsed: 0.057807s [0, 0, 0, 0, 0] True [0, 0, 0, 0, 1] True [0, 0, 1, 0, 1] True [1, 0, 1, 0, 1] True [1, 1, 1, 0, 1] True [1, 1, 1, 1, 1] True Elapsed: 0.306156s [0, 0, 0, 0, 0] True [0, 0, 0, 0, 1] True [0, 0, 1, 0, 1] True [0, 1, 0, 1, 1] True [0, 1, 1, 1, 1] True [1, 1, 1, 1, 1] True Elapsed: 0.071120s [0, 0, 0, 0, 0] True [0, 0, 1, 0, 0] True [0, 1, 0, 1, 0] True [1, 0, 1, 0, 1] True [1, 1, 0, 1, 1] True [1, 1, 1, 1, 1] True Elapsed: 0.165472s [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6] [7, 7, 7, 7, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7] [6, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7, 7] [7, 6, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7]