双向平衡份额转移算法

9 阅读3分钟

一、核心问题

在多面板布局中,每个面板的尺寸(如高度、宽度等)常需动态调整。设计此算法旨在实现以下目标:

  • 面板间可相互转移份额(如尺寸比例);
  • 无论经过多少次操作,系统始终能够回到初始状态;
  • 所有面板的份额总和始终保持不变。

二、面板数据结构

每个面板仅需记录两个数值:

名称含义是否可变
基础份额面板初始状态下应有的份额固定不变
当前变化当前份额相对于基础值的增减量每次操作更新

面板的当前份额计算公式为:

当前份额 = 基础份额 + 当前变化

例如:基础份额为 3,当前变化为 +1,则该面板当前实际占有 4 份。

三、转移规则(三步判断法)

当从面板 A 向面板 B 转移份额时,按以下顺序判断转移量:

  1. 优先填补亏空

    检查面板 B 是否当前处于“亏损”状态(即当前变化 < 0)。

    若是,则转移量 = min(A 当前可转出的份额, B 所缺的份额)。

  2. 其次归还盈余

    若 B 不亏损,则检查 A 是否当前处于“盈余”状态(即当前变化 > 0)。

    若是,则转移量 = min(A 当前可转出的份额, A 多出的份额)。

  3. 最后正常转移

    若以上均不满足,则 A 将当前持有的可转份额全部转给 B。

完成判断后更新数据:

  • A 的当前变化 减去 转移量;
  • B 的当前变化 加上 转移量。

四、示例推演

假设三个面板初始状态为:

A(基础 2)、B(基础 3)、C(基础 1),所有当前变化均为 0。

  • 操作 1:B → C

    B 可转出 3 份,C 不亏损,B 无盈余 → 进入第 3 步,B 全转 3 份给 C。

    结果:B 变化 = -3,C 变化 = +3。

  • 操作 2:C → B

    C 可转出 4 份,B 变化 = -3(亏损 3 份)→ 进入第 1 步,C 归还 3 份给 B。

    结果:所有面板变化归零,恢复初始状态。

五、代码实现(JavaScript 示例)

function transfer(from, to) {
  const available = from.base + from.change;
  if (available <= 0) return; // 无可转移份额
​
  let amount = 0;
​
  // 1. 若目标面板有亏空,优先填补
  if (to.change < 0) {
    amount = Math.min(available, -to.change);
  }
  // 2. 若来源面板有盈余,优先归还盈余部分
  else if (from.change > 0) {
    amount = Math.min(available, from.change);
  }
  // 3. 一般情况,全部转出
  else {
    amount = available;
  }
​
  // 更新变化值
  from.change -= amount;
  to.change += amount;
}

六、算法为何总能恢复初始状态?

该规则本质上建立了一个“状态记忆”机制:

  1. 始终优先补足当前份额不足的面板(填坑);
  2. 其次减少当前份额过剩的面板(还债);
  3. 仅在前两者都不满足时,才动用面板的基础份额进行操作。

通过这一优先顺序,所有的转移操作实际上都是在逐步“抵消”系统中临时产生的不平衡。无论经过多少次转移,只要按相反顺序或适当路径操作,这些临时变化最终都能被完全对冲,使每个面板回归其基础值。

七、完整案例

总结

该算法的核心是:每个面板仅维护基础份额当前变化两个值,转移时严格按照 “补亏空 → 还多余 → 全给” 的优先级决定转移量。这一机制既保证了操作的灵活性与可预测性,又使系统始终具备回到初始状态的能力,非常适合需要平衡自由调整与状态复原的交互设计。