Можешь помочь с этим заданием? 
Задача: Для заданных погрешностей ε ∈ {0.1, 0.05, 0.01, 0.005, 0.001} постройте Count-Min Sketch, который ошибается более чем на ε ⋅ n с вероятностью ρ ∈ {2 ** (-1), 2 ** (-2), 2 ** (-5), 2 ** (-10)}. Оцените затраты памяти в зависимости от параметров.
Нужно для начала переписать код с java на python. И важное замечание: в новом коде hash функция будет использована специальная, а не какая-то из библиотек. А также код новый будет использован для чисел, не для строк как на java.
Вот код на java:
import java.util.Arrays;
import java.util.Random;

public class CountMinSketch {
    private final int[][] table;
    private final int width;
    private final int depth;
    private final int[] hashSeeds;

    // Constructor
    public CountMinSketch(double epsilon, double delta) {
        this.width = (int) Math.ceil(Math.E / epsilon); // Width based on error
        this.depth = (int) Math.ceil(Math.log(1 / delta)); // Depth based on confidence
        this.table = new int[depth][width];
        this.hashSeeds = new int[depth];
        Random random = new Random();

        // Generate random seeds for hash functions
        for (int i = 0; i < depth; i++) {
            hashSeeds[i] = random.nextInt();
        }
    }

    // Hash function
    private int hash(String item, int seed) {
        return (item.hashCode() ^ seed) % width;
    }

    // Add an element to the sketch
    public void add(String item) {
        for (int i = 0; i < depth; i++) {
            int hashValue = hash(item, hashSeeds[i]);
            table[i][Math.abs(hashValue)]++;
        }
    }

    // Query the count of an element
    public int estimate(String item) {
        int minCount = Integer.MAX_VALUE;

        for (int i = 0; i < depth; i++) {
            int hashValue = hash(item, hashSeeds[i]);
            minCount = Math.min(minCount, table[i][Math.abs(hashValue)]);
        }

        return minCount;
    }

    // Main method for testing
    public static void main(String[] args) {
        CountMinSketch cms = new CountMinSketch(0.01, 0.01); // 1% error, 99% confidence

        // Add elements
        cms.add("apple");
        cms.add("banana");
        cms.add("apple");
        cms.add("cherry");
        cms.add("apple");
        cms.add("banana");

        // Query counts
        System.out.println("Count of apple: " + cms.estimate("apple"));
        System.out.println("Count of banana: " + cms.estimate("banana"));
        System.out.println("Count of cherry: " + cms.estimate("cherry"));
        System.out.println("Count of mango: " + cms.estimate("mango"));
    }
}

А вот код на python:
class CountMinSketch:
    def __init__(self, epsilon, delta, A, b):
        self.width = int(np.ceil(np.e / epsilon))  # Width based on error
        self.depth = int(np.ceil(np.log(1 / delta)))  # Depth based on confidence
        self.table = np.zeros((self.depth, self.width), dtype=int)  # Initialize the sketch table
        self.hash_seeds = [random.getrandbits(32) for _ in range(self.depth)]  # Generate random seeds
        self.A = A
        self.b = b

    '''def hash(self, item, seed):
        return (hash(item) ^ seed) % self.width  # Hash function'''

    def _hash_function(self, x):
        hash_output = generate_hash(self.A, self.b, x)
        print(f"hash_output = {hash_output}")
        return int(''.join(map(str, hash_output)), 2) % self.width

    def add(self, item):
        for i in range(self.depth):
            print(f"self.depth = {self.depth}")
            #hash_value = self.hash(item, self.hash_seeds[i])
            hash_value = self._hash_function(item)
            self.table[i][hash_value] += 1  # Increment count in the appropriate row
            print(f"hash_value = {hash_value}")
            print(sum(self.table))

    def estimate(self, item):
        min_count = float('inf')  # Start with the maximum possible value

        for i in range(self.depth):
            #hash_value = self.hash(item, self.hash_seeds[i])
            hash_value = self._hash_function(item)
            print(f"hash_value = {hash_value}")
            min_count = min(min_count, self.table[i][hash_value])  # Get the minimum count from each row
            print(f"min_count = {min_count}")

        return min_count

И специальные функции hash:
def generate_data(distribution_name, size):
    if distribution_name == 'uniform':
        return generate_uniform_random_numbers(size)
    elif distribution_name == 'normal':
        return generate_normal_random_numbers(size, mean=500000, std_dev=100000)


    elif distribution_name == 'zipf':
        return generate_zipf_random_numbers(size, N=1000000, s=1.5)

def generate_matrix_and_vector(n, m=32):
    A = np.random.randint(0, 2, size=(n, m), dtype=np.int8)
    b = np.random.randint(0, 2, size=n, dtype=np.int8)
    return A, b

def generate_hash(A, b, x):

  # error handling
  n, m = A.shape
  if m != 32:
        raise ValueError("Matrix A must have 32 columns.")
  if len(b) != n:
        raise ValueError("Vector b must have the same number of elements as rows in matrix A.")

  # x to binary
  x = int(x)
  x_bits = np.array([(x >> i) & 1 for i in range(32)], dtype=np.int8)

  # r
  r = np.zeros(n, dtype=np.int8)
  for i in range(n):
    Ai = A[i]
    logic_AND_result = Ai & x_bits
    #print("A[i]:\n", Ai)
    #print("x:\n", x_bits)
    #print("A[i] & x:\n", logic_AND_result, "\nnumber of 1:\t", np.sum(logic_AND_result))

    # evenness counting (number of '1')
    parity = np.sum(logic_AND_result) % 2
    #print("parity:\t", parity)

    r[i] = parity  # parity -> i-th bit of r[]
    #print("r[i]:\t", r[i], "\n")

  # r XOR b
  logic_XOR_result = r ^ b
  #print("r:\n", r)
  #print("b:\n", b, "\n")

  return logic_XOR_result

Если чего-то не хватает, скажи. Давай пока проверим насколько хорошо получилось переписать код с java на python