Помоги, пожалуйста, разобраться подробно с этим классом для построения экспоненциальной гистограммы (разобраться в каждом методе и почему он устроен именно таким образом):
class ExponentialHistogram:
    def __init__(self, alpha=1.0009765625):  # alpha = 1 + 2^(-10)
        self.alpha = alpha
        self.counts = []
        self.size = 0

    def _index(self, x):
        """Находит индекс для элемента x."""
        if x < 1:
            raise ValueError("Element must be >= 1")
        return int(np.floor(np.log(x) / np.log(self.alpha)))

    def insert(self, x):
        """Вставляет элемент x в гистограмму."""
        index = self._index(x)
        while index >= len(self.counts):
            self.counts.append(0)
        self.counts[index] += 1
        self.size += 1

    def remove(self, x):
        """Удаляет элемент x из гистограммы."""
        index = self._index(x)
        if index < len(self.counts) and self.counts[index] > 0:
            self.counts[index] -= 1
            self.size -= 1

    def get_min(self):
        """Возвращает нижнюю оценку минимума."""
        for i, count in enumerate(self.counts):
            if count > 0:
                return self.alpha ** i
        return None

    def get_max(self):
        """Возвращает верхнюю оценку максимума."""
        for i in reversed(range(len(self.counts))):
            if self.counts[i] > 0:
                return self.alpha ** (i + 1)
        return None

    def get_median(self):
        """Возвращает нижнюю оценку медианы."""
        half_n = self.size / 2
        cumulative_sum = 0
        for i, count in enumerate(self.counts):
            cumulative_sum += count
            if cumulative_sum >= half_n:
                return self.alpha ** i
        return None

    def get_mean(self):
        """Возвращает оценку среднего значения."""
        total_sum = 0
        count_sum = 0
        for i, count in enumerate(self.counts):
            if count > 0:
                lower_bound = self.alpha ** i
                upper_bound = self.alpha ** (i + 1)
                # Среднее значение на интервале
                mean_in_bin = (lower_bound + upper_bound) / 2
                total_sum += mean_in_bin * count
                count_sum += count
        return total_sum / count_sum if count_sum > 0 else None

Использование:
data = generate_data('uniform', 1000)  # моя функция генерации случайных чисел
histogram = ExponentialHistogram()
for number in data:
    histogram.insert(number)

epsilon_values = [0.5, 0.1, 0.05, 0.01, 0.001, 0.0001]
memory_usage = []
time_taken = []

for epsilon in epsilon_values:
    alpha = 1 + 2 ** (-int(-np.log2(epsilon)))
    histogram = ExponentialHistogram(alpha)

    start_time = time.time()

    for number in data:
        histogram.insert(number)

    elapsed_time = time.time() - start_time
    time_taken.append(elapsed_time)

    memory_usage.append(len(histogram.counts) * 8)  # предполагаем 64-битовые счётчики

# Построение графиков
plt.figure(figsize=(12, 6))

plt.subplot(1, 2, 1)
plt.plot(epsilon_values, memory_usage, marker='o')
plt.xscale('log')
plt.title('Зависимость размера памяти от ε')
plt.xlabel('ε')
plt.ylabel('Размер памяти (байты)')

plt.subplot(1, 2, 2)
plt.plot(epsilon_values, time_taken, marker='o', color='red')
plt.xscale('log')
plt.title('Зависимость времени выполнения от ε')
plt.xlabel('ε')
plt.ylabel('Время (секунды)')

plt.tight_layout()
plt.show()