Skip to content

Improve lower bound for C_3a#71

Open
sebastian-griego wants to merge 1 commit into
teorth:mainfrom
sebastian-griego:improve-3a-lower-bound
Open

Improve lower bound for C_3a#71
sebastian-griego wants to merge 1 commit into
teorth:mainfrom
sebastian-griego:improve-3a-lower-bound

Conversation

@sebastian-griego
Copy link
Copy Markdown

Summary

This updates the lower bound for the Gyamarti-Hennecart-Ruzsa sum-difference constant from

[
C_{3a} \geq 1.173077
]

to

[
C_{3a} \geq 1.1740744.
]

The bound comes from the finite digit construction

[
A={0,2,3,4,5,6,7,8,9,10},\qquad B=21,\qquad d=80,\qquad T=150,
]

and

[
U=\left{\sum_{i=0}^{79}a_i21^i:\ a_i\in A,\ \sum_{i=0}^{79}a_i\le150\right}.
]

For this set,

[
\max(U)=10\sum_{i=65}^{79}21^i
]

so

[
\max(U)=
2995805288150731620410662946668903948341032736664352641511848666717243994160370658179324073879212562136150.
]

The exact cardinalities are

[
\lvert U+U\rvert =
75448362167176243488362019935078206851619643198150854886920234689186981134888,
]

and

[
\lvert U-U\rvert =
195351744295266763842135520514417052287242446785296742323733058216909095059024572338564089814415.
]

Thus

[
1+\frac{\log(\lvert U-U\rvert/\lvert U+U\rvert)}
{\log(2\max(U)+1)}

1.174074447693521163363531806658755676155543896382679659744422686584815723921237399321826308369811589\ldots,
]

which certifies the stated conservative lower bound.

Certificate

The construction uses the standard finite-set lower-bound lemma already stated on the 3a.md page:

[
C_{3a}\geq
1+\frac{\log(\lvert U-U\rvert/\lvert U+U\rvert)}
{\log(2\max(U)+1)}.
]

Sums have no carries because every digit sum lies in ([0,20]), while the base is (21).

Differences are digitwise unique. If

[
\sum_{i=0}^{79}c_i21^i=0,\qquad c_i\in[-20,20],
]

then reducing modulo (21) gives (c_0=0), and induction gives every (c_i=0).

For sums, write (Y=A+A) and

[
P_y={a\in A:y-a\in A}.
]

A sum digit vector (y=(y_i)) is feasible exactly when one can choose (a_i\in P_{y_i}) such that

[
\sum_i a_i\le150,\qquad
\sum_i(y_i-a_i)\le150.
]

For differences, write (\Delta=A-A) and

[
q_\delta=\min{b\in A:b+\delta\in A}.
]

A difference digit vector (\delta=(\delta_i)) is feasible exactly when

[
\sum_i q_{\delta_i}\le150,\qquad
\sum_i(q_{\delta_i}+\delta_i)\le150.
]

The exact counting program below verifies the cardinalities and gives a rigorous logarithmic comparison.

Verification

from fractions import Fraction
from decimal import Decimal, getcontext
from functools import lru_cache

A = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]
B = 21
d = 80
T = 150

claimed_m = int(
    "2995805288150731620410662946668903948341032736664352641511848666717243994160370658179324073879212562136150"
)

claimed_S = int(
    "75448362167176243488362019935078206851619643198150854886920234689186981134888"
)

claimed_D = int(
    "195351744295266763842135520514417052287242446785296742323733058216909095059024572338564089814415"
)


def max_u():
    rem = T
    m = 0
    for i in range(d - 1, -1, -1):
        a = max(x for x in A if x <= rem)
        m += a * (B ** i)
        rem -= a
    return m


def sum_count():
    Y = sorted({a + b for a in A for b in A})
    P = {y: tuple(a for a in A if y - a in A) for y in Y}
    mask = (1 << (T + 1)) - 1

    @lru_cache(maxsize=None)
    def shift_possible(bitset, y):
        out = 0
        for p in P[y]:
            out |= bitset << p
        return out & mask

    states = {0: {1: 1}}

    for _ in range(d):
        new_states = {}
        for total_y, bitsets in states.items():
            for bitset, count in bitsets.items():
                for y in Y:
                    new_total_y = total_y + y
                    if new_total_y <= 2 * T:
                        new_bitset = shift_possible(bitset, y)
                        if new_bitset:
                            bucket = new_states.setdefault(new_total_y, {})
                            bucket[new_bitset] = bucket.get(new_bitset, 0) + count
        states = new_states

    total = 0
    full_mask = (1 << (T + 1)) - 1

    for total_y, bitsets in states.items():
        lower = max(0, total_y - T)
        feasible = full_mask ^ ((1 << lower) - 1) if lower else full_mask
        for bitset, count in bitsets.items():
            if bitset & feasible:
                total += count

    return total


def diff_count():
    Delta = sorted({a - b for a in A for b in A})
    features = []

    for delta in Delta:
        q = min(b for b in A if b + delta in A)
        features.append((q, q + delta))

    states = {(0, 0): 1}

    for _ in range(d):
        new_states = {}
        for (left, right), count in states.items():
            for x, y in features:
                new_left = left + x
                new_right = right + y
                if new_left <= T and new_right <= T:
                    key = (new_left, new_right)
                    new_states[key] = new_states.get(key, 0) + count
        states = new_states

    return sum(states.values())


def atanh_log_unit(q, M):
    z = (q - Fraction(1)) / (q + Fraction(1))
    s = Fraction(0)
    zpow = z
    z2 = z * z

    for j in range(M):
        s += zpow / (2 * j + 1)
        zpow *= z2

    approx = 2 * s
    remainder = 2 * abs(zpow) / ((2 * M + 1) * (1 - z2))

    return approx - remainder, approx + remainder


def floor_log2_fraction(x):
    n = x.numerator
    q = x.denominator
    k = n.bit_length() - q.bit_length()

    if k >= 0:
        if Fraction(1 << k, 1) > x:
            k -= 1
    else:
        if Fraction(1, 1 << (-k)) > x:
            k -= 1

    return k


def ln_bounds(x, M=120):
    if x == 1:
        return Fraction(0), Fraction(0)

    if x < 1:
        lo, hi = ln_bounds(1 / x, M)
        return -hi, -lo

    k = floor_log2_fraction(x)
    q = x / (Fraction(2) ** k)

    lo2, hi2 = atanh_log_unit(Fraction(2), M)
    loq, hiq = atanh_log_unit(q, M)

    return k * lo2 + loq, k * hi2 + hiq


m = max_u()
S = sum_count()
D = diff_count()

assert m == claimed_m
assert S == claimed_S
assert D == claimed_D

c = Fraction(1740744, 10000000)
N = Fraction(2 * m + 1, 1)

lo_ratio, hi_ratio = ln_bounds(Fraction(D, S), 120)
lo_N, hi_N = ln_bounds(N, 120)

margin_lower = lo_ratio - c * hi_N
interval_width = (hi_ratio - lo_ratio) + c * (hi_N - lo_N)

getcontext().prec = 100

print("m =", m)
print("|U+U| =", S)
print("|U-U| =", D)

print("certified value =")
print(
    Decimal(1)
    + (Decimal(D) / Decimal(S)).ln()
    / Decimal(2 * m + 1).ln()
)

print("lower bound for log comparison =")
print(Decimal(margin_lower.numerator) / Decimal(margin_lower.denominator))

print("log interval width <")
print(Decimal(interval_width.numerator) / Decimal(interval_width.denominator))

assert margin_lower > 0

Expected output includes:

m = 2995805288150731620410662946668903948341032736664352641511848666717243994160370658179324073879212562136150
|U+U| = 75448362167176243488362019935078206851619643198150854886920234689186981134888
|U-U| = 195351744295266763842135520514417052287242446785296742323733058216909095059024572338564089814415
certified value =
1.174074447693521163363531806658755676155543896382679659744422686584815723921237399321826308369811589...
lower bound for log comparison =
0.000011616319625271776397327368655753599431094491184039815851890822283355139391844978...
log interval width <
2.35e-115

Therefore

[
\log\frac{\lvert U-U\rvert}{\lvert U+U\rvert}

0.1740744\log(2\max(U)+1)

0,
]

so

[
C_{3a}\geq 1.1740744.
]

AI assistance disclosure

The construction, patch, PR text, and verification script were prepared with assistance from ChatGPT 5.5 Pro. The submitter rechecked the construction and arithmetic before submission.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant