You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

百分比序列凑整算法咨询:实现调整后总和为100,适配Pl/PgSql

匹配需求的成熟算法

你要实现的百分比凑整逻辑属于最大余数法(Hamilton Method,也叫汉密尔顿席位分配法),该算法原本用于选举场景下的席位分配,适配后可完美满足你要求的总和固定、优先调整非整数、小值优先分配差值的规则,也能解决你当前遇到的49.99无法凑整到50.00的问题。

适配你业务规则的算法步骤

调整后的算法完全兼容你现有逻辑,同时覆盖所有约束:

  • 前置校验:先计算所有输入百分比的总和,若总和<99.9或>100.1,直接返回原数值不调整
  • 数值转换:将所有百分比乘以100转为整数,记总和为sum_int,总差值diff = 10000 - sum_int(100%对应10000个0.01的最小单位)
  • 权重计算:沿用你现有权重规则,权重越低调整优先级越高:
    • (int mod 1000)=0 → 权重30
    • (int mod 100)=0 → 权重20
    • (int mod 10)=0 → 权重10
    • (int mod 5)=0 → 权重5
    • 其余情况 → 权重0
  • 排序规则:先按权重升序排序,同权重按数值升序排序,保证非整数、小数值优先调整
  • 差值分配:
    • 若diff>0(缺额):按排序顺序依次给每个项加1,每加1次diff减1,直到diff=0
    • 若diff<0(溢出):按排序顺序依次给每个项减1,每减1次diff加1,直到diff=0
  • 结果转换:将所有整数除以100转回两位小数的百分比数值

该逻辑天然支持你提到的49.99转50.00的场景:49.99转整后为4999,权重为0(调整优先级最高),只要存在正差值会优先对其调整,一步即可凑整为50.00。

Pl/PgSQL 实现参考
CREATE OR REPLACE FUNCTION adjust_percentages(input_nums numeric[])
RETURNS numeric[] AS $$
DECLARE
    i integer;
    total numeric;
    sum_int integer;
    diff integer;
    item_int integer;
    weight integer;
    items RECORD;
    item_list RECORD[];
    result numeric[];
BEGIN
    -- 1. 前置校验总和
    SELECT sum(val) INTO total FROM unnest(input_nums) val;
    IF total < 99.9 OR total > 100.1 THEN
        RETURN input_nums;
    END IF;

    -- 2. 转整数并计算权重、保留原始下标保证输出顺序和输入一致
    FOR i IN 1..array_length(input_nums, 1) LOOP
        item_int := round(input_nums[i] * 100)::integer;
        -- 按规则计算权重
        IF item_int % 1000 = 0 THEN
            weight := 30;
        ELSIF item_int % 100 = 0 THEN
            weight := 20;
        ELSIF item_int % 10 = 0 THEN
            weight := 10;
        ELSIF item_int % 5 = 0 THEN
            weight := 5;
        ELSE
            weight := 0;
        END IF;
        item_list[i] := (i, item_int, weight)::RECORD(idx integer, val integer, weight integer);
    END LOOP;

    -- 3. 计算总差值
    SELECT sum((item).val) INTO sum_int FROM unnest(item_list) item;
    diff := 10000 - sum_int;

    -- 4. 循环分配差值
    WHILE diff != 0 LOOP
        -- 取优先级最高的待调整项
        SELECT * INTO items
        FROM unnest(item_list) item
        ORDER BY (item).weight ASC, (item).val ASC
        LIMIT 1;

        -- 更新数值
        IF diff > 0 THEN
            item_list[items.idx].val := items.val + 1;
            diff := diff - 1;
        ELSE
            item_list[items.idx].val := items.val - 1;
            diff := diff + 1;
        END IF;
    END LOOP;

    -- 5. 组装返回结果,保留两位小数
    FOR i IN 1..array_length(input_nums, 1) LOOP
        result[i] := round(item_list[i].val::numeric / 100, 2);
    END LOOP;

    RETURN result;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

内容的提问来源于stack exchange,提问作者JC Boggio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 03:45:01