初始化

This commit is contained in:
2026-04-22 12:27:49 +08:00
commit 4857cb6e45
73 changed files with 20927 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
from typing import List, Union
import numpy as np
# 解决百分比相加不为100%
def fix_percentages(values: List[float], total: float) -> List[float]:
"""
修正百分比相加不为100%的问题。
Args:
values (list[float]): 百分比列表,元素个数与总和相同
total (float): 总和
Returns:
list[float]: 修正后的百分比列表
Examples:
>>> values = [0.2, 0.3, 0.5]
>>> total = 1
>>> fix_percentages(values, total)
[20.0, 30.0, 50.0]
>>> values = [0.2, 0.3, 0.5]
>>> total = 0.8
>>> fix_percentages(values, total)
[25.0, 37.5, 62.5]
"""
exact = [v / total * 100 for v in values]
floor = [np.floor(p * 100) / 100 for p in exact] # 向下取整到2位小数
remainders = [exact[i] - floor[i] for i in range(len(exact))]
# 需要分配的百分点数以0.01%为单位)
to_distribute = int(round(10000 - sum(floor) * 100))
# 按余数大小分配
indices = sorted(range(len(remainders)), key=lambda i: remainders[i], reverse=True)
fixed = floor.copy()
for i in range(to_distribute):
fixed[indices[i]] += 0.01
return [round(p, 2) for p in fixed]
# === 误差矫正 ===
def correct_rounding_error(target_total:Union[int,float], adjusted_areas:List[float], original_areas:List[float]) -> List[int]:
"""
健壮的数值舍入误差矫正函数:将浮点型面积值四舍五入后,调整至目标总和。
核心逻辑基于原始数值的小数部分优先级逐次增减1来抵消舍入误差确保最终总和匹配目标值
同时避免调整后数值出现负数,防止无限循环。
Args:
target_total (int/float): 目标总和(最终舍入后数值的合计值),函数内部会转为整型
adjusted_areas (list[float]): 经过比例调整后的浮点型面积列表(待舍入的原始数据)
original_areas (list[float]): 调整前的原始浮点型面积列表(用于计算小数部分优先级)
Returns:
list[int]: 矫正后的整型面积列表,总和尽可能接近/等于target_total
若无法完全矫正,返回尽可能接近的结果并打印警告
Raises:
无显式抛出异常,所有异常会被捕获并打印错误信息,返回保底的四舍五入结果
Notes:
1. 误差矫正规则:
- 误差>0当前总和 < 目标总和):优先给小数部分大的数值+1
- 误差<0当前总和 > 目标总和):优先给小数部分小的数值-1
2. 边界限制调整时确保数值≥0避免出现负数面积
3. 防无限循环:最大迭代次数为 len(adjusted_areas) * 10超出则终止并提示剩余误差
Examples:
>>> target = 10
>>> adjusted = [3.2, 2.8, 4.1] # 四舍五入后总和=3+3+4=10无误差
>>> original = [3.2, 2.8, 4.1]
>>> correct_rounding_error(target, adjusted, original)
[3, 3, 4]
>>> target = 10
>>> adjusted = [3.1, 2.1, 4.1] # 四舍五入后总和=3+2+4=9误差+1
>>> original = [3.1, 2.1, 4.1]
>>> correct_rounding_error(target, adjusted, original)
[3, 2, 5] # 优先给小数部分最大的4.1+1
>>> target = 8
>>> adjusted = [3.9, 2.9, 1.9] # 四舍五入后总和=4+3+2=9误差-1
>>> original = [3.9, 2.9, 1.9]
>>> correct_rounding_error(target, adjusted, original)
[3, 3, 2] # 优先给小数部分最小的1.9-1实际小数1.9>2.9>3.9故调整3.9
"""
try:
target_total = int(target_total)
rounded_areas = [int(round(area)) for area in adjusted_areas]
current_total = sum(rounded_areas)
error = target_total - current_total
if error == 0 or len(adjusted_areas) == 0:
return rounded_areas
# 使用循环分配直到误差为0或无法再分配
remaining_error = error
max_iterations = len(adjusted_areas) * 10 # 防止无限循环
for _ in range(max_iterations):
if remaining_error == 0:
break
# 每次迭代重新计算小数部分和排序
decimal_parts = [float(area - int(area)) for area in original_areas]
indices = list(range(len(adjusted_areas)))
if remaining_error > 0:
indices.sort(key=lambda i: decimal_parts[i], reverse=True)
adjustment = 1
else:
indices.sort(key=lambda i: decimal_parts[i])
adjustment = -1
# 尝试分配一次调整
adjusted = False
for idx in indices:
if (adjustment == 1 and rounded_areas[idx] >= 0) or (adjustment == -1 and rounded_areas[idx] > 0):
rounded_areas[idx] += adjustment
remaining_error -= adjustment
adjusted = True
break
if not adjusted: # 无法再调整
break
if remaining_error != 0:
print(f"警告:无法完全矫正误差,剩余: {remaining_error}")
return rounded_areas
except Exception as e:
print(f"误差矫正出错: {e}")
# 返回原始四舍五入结果作为保底
return [int(round(area)) for area in adjusted_areas]
if __name__ == '__main__':
target = 10
adjusted = [3.3, 3.9, 4.2] # 四舍五入后总和=3+3+4=10无误差
original = [3.25, 2.85, 4.15]
print(correct_rounding_error(target, adjusted, original))