import math
# Константа округления (Unit Roundoff) для типа данных double (IEEE 754)
# Равна половине машинного эпсилон: 2^(-53) ≈ 1.11e-16
UNIT_ROUNDOFF = 2**(-53)
def ceil_log2(x):
"""Возвращает округленное вверх двоичное логарифмическое значение."""
return math.ceil(math.log2(x)) if x > 0 else 0
def pairwise_summation(x):
"""Рекурсивное попарное суммирование для уменьшения погрешности."""
n = len(x)
if n <= 1:
return x[0] if n == 1 else 0.0
elif n <= 128: # Базовый случай для повышения производительности
return sum(x)
else:
m = n // 2
return pairwise_summation(x[:m]) + pairwise_summation(x[m:])
def test1():
harmonic_numbers = [1 / k for k in range(1, 10**6 + 1, 1)]
n = ceil_log2(len(harmonic_numbers))
pairwise_sum_result = pairwise_summation(harmonic_numbers)
pairwise_sum_result_with_abs = pairwise_summation(
[abs(x) for x in harmonic_numbers]
)
apriori_abs_error = (
pairwise_sum_result_with_abs
* (n * UNIT_ROUNDOFF)
/ (1.0 - 2 * n * UNIT_ROUNDOFF)
)
print("--- Test 1 (Harmonic Numbers) ---")
print("Pairwise Summation Result:", pairwise_sum_result)
print("Pairwise Summation Result with Absolute Values:", pairwise_sum_result_with_abs)
print(
"A Priori Absolute Error Estimate:",
apriori_abs_error,
pairwise_sum_result_with_abs * (n * UNIT_ROUNDOFF) / (1.0 - n * UNIT_ROUNDOFF),
)
def test2():
# Дополненный код для квадратов гармонических чисел
squared_harmonic_numbers = [1 / (k * k) for k in range(1, 10**3 + 1, 1)]
n = ceil_log2(len(squared_harmonic_numbers))
pairwise_sum_result = pairwise_summation(squared_harmonic_numbers)
pairwise_sum_result_with_abs = pairwise_summation(
[abs(x) for x in squared_harmonic_numbers]
)
apriori_abs_error = (
pairwise_sum_result_with_abs
* (n * UNIT_ROUNDOFF)
/ (1.0 - 2 * n * UNIT_ROUNDOFF)
)
print("\n--- Test 2 (Squared Harmonic Numbers) ---")
print("Pairwise Summation Result:", pairwise_sum_result)
print("Pairwise Summation Result with Absolute Values:", pairwise_sum_result_with_abs)
print(
"A Priori Absolute Error Estimate:",
apriori_abs_error,
pairwise_sum_result_with_abs * (n * UNIT_ROUNDOFF) / (1.0 - n * UNIT_ROUNDOFF),
)
if __name__ == "__main__":
test1()
test2()