2025-11-30 14:45:55 +01:00
|
|
|
"""Distribute values symmetrically across N parts."""
|
|
|
|
|
|
|
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
def calculate_symmetric_weights(
|
|
|
|
|
n: int, middle_weight: float, factors: list[float] | None = None
|
|
|
|
|
) -> list[float]:
|
2025-11-30 13:42:16 +01:00
|
|
|
"""Calculate symmetric weights for both even and odd N.
|
2024-09-11 15:01:33 +02:00
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
Args:
|
|
|
|
|
n: Number of parts to split into.
|
|
|
|
|
middle_weight: The middle value for symmetry.
|
|
|
|
|
factors: If provided, controls the difference in weights.
|
|
|
|
|
Must have length n // 2 or n // 2 - 1 depending on n.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
List of symmetric weights.
|
2024-09-11 14:59:57 +02:00
|
|
|
"""
|
2025-11-30 15:49:40 +01:00
|
|
|
half_n = n // 2
|
|
|
|
|
weights_left: list[float] = [middle_weight]
|
2024-09-11 15:01:33 +02:00
|
|
|
|
|
|
|
|
if factors:
|
2024-09-11 14:59:57 +02:00
|
|
|
for factor in factors:
|
|
|
|
|
next_weight = weights_left[-1] + factor
|
|
|
|
|
weights_left.append(next_weight)
|
2024-09-11 15:01:33 +02:00
|
|
|
else:
|
2025-11-30 20:47:38 +01:00
|
|
|
weights_left.extend(middle_weight - (i + 1) for i in range(half_n - 1))
|
2024-09-11 15:01:33 +02:00
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
if n % 2 == 0:
|
2024-09-11 14:59:57 +02:00
|
|
|
weights = weights_left[::-1] + weights_left
|
2024-09-11 15:01:33 +02:00
|
|
|
else:
|
2025-11-30 13:59:21 +01:00
|
|
|
weights = [*weights_left[::-1], middle_weight, *weights_left]
|
2025-11-30 13:42:16 +01:00
|
|
|
|
2024-09-11 15:01:33 +02:00
|
|
|
return weights
|
2024-09-11 14:59:57 +02:00
|
|
|
|
2025-11-30 13:42:16 +01:00
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
def scale_to_total(x: float, weights: list[float]) -> list[float]:
|
2025-11-30 13:42:16 +01:00
|
|
|
"""Scale the weights so that their sum is proportional to X.
|
|
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
Args:
|
|
|
|
|
x: Total value to distribute.
|
|
|
|
|
weights: The list of weights to be scaled.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
List of scaled values summing to x.
|
2024-09-11 15:01:33 +02:00
|
|
|
"""
|
2024-09-11 14:59:57 +02:00
|
|
|
total_weight = sum(weights)
|
2025-11-30 15:49:40 +01:00
|
|
|
base_unit = x / total_weight
|
2025-11-30 13:59:21 +01:00
|
|
|
return [base_unit * weight for weight in weights]
|
2024-09-11 14:59:57 +02:00
|
|
|
|
2025-11-30 13:42:16 +01:00
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
def split_x_into_n_symmetrically(x: float, n: int, factors: list[float]) -> list[float]:
|
2025-11-30 14:53:09 +01:00
|
|
|
"""Split X into N parts with symmetric weights controlled by factors."""
|
2025-11-30 15:49:40 +01:00
|
|
|
weights = calculate_symmetric_weights(n, middle_weight=1, factors=factors)
|
|
|
|
|
return scale_to_total(x, weights)
|
2024-09-11 14:59:57 +02:00
|
|
|
|
2025-11-30 13:42:16 +01:00
|
|
|
|
2025-11-30 15:49:40 +01:00
|
|
|
def split_x_into_n_middle(x: float, n: int, middle_value: float) -> list[float]:
|
2025-11-30 14:53:09 +01:00
|
|
|
"""Split X into N parts with symmetric weights using middle_value as peak."""
|
2025-11-30 15:49:40 +01:00
|
|
|
weights = calculate_symmetric_weights(n, middle_weight=middle_value)
|
|
|
|
|
return scale_to_total(x, weights)
|