幂等性是程序设计的一种重要原则。
举个最简单的例子,去ATM取钱,确认好数字,按下确认之后,机器会开始吐钱。但这个过程中如果再按下“确认”, 而ATM还会接着吐钱,那么这个设计就是不幂等的。
当然现实没有那么好的事情,按下确认到收钱的这个时间段,在程序设计中属于一个“事务锁”的阶段。这期间按100次确认也是徒劳。
不知不觉之间,ATM也成为了即将被淘汰的消费电子类产品。但幂等原则和数据库事务这类基本原理没有消失。
不幂等电梯
比如我最近遇到一种“不幂等电梯”。在传统商业建筑中,运营为了方便一般会设置“1对N幂等”——按下下行之后,所有下行灯一起亮,然后调度中心分配一个最近的电梯。这种我称为“幂等电梯”。
不幂等电梯,是把面板上下行按钮跟调度中心分离。比如一栋楼有3部挨着的电梯。按下面板上的上下行,只是控制“1对1”关联的那部电梯。如果想要另外一部电梯,那就按另外一个面板。这种我称为“不幂等电梯”。 本文只讨论不幂等电梯场景。
个体自私导致群体延误
结合过往的统计规律,应用时间序列编程对“不幂等电梯”场景建模,假设工作时间是 9:00 ~ 18:30 ,那么在上下班临界点10分钟内,对于楼层<10 的写字楼,按照我的经验,基本上是走步梯比较快。
而假设场景中的人只有1个,实际上选取电梯的策略非常简单,选择“离自己最近的那部同向电梯”便是最佳策略:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
type Work struct {
Floor int16 //工作楼层
WorkHours time.Time //上班时间
ClosingTime time.Time //下班时间
}
// Person 表示人
type Person struct {
Time time.Time // 时间
Floor int16 //当前楼层
TargetFloor int16 //目标楼层
work Work
}
func (p *Person) MVP(elevators []Elevator) (int, *Elevator) {
t := p.Time
if p.work.Floor < 10 && (t.Sub(p.work.ClosingTime).Abs() < 10*time.Minute || t.Sub(p.work.WorkHours).Abs() < 10*time.Minute) {
//walking
return -1, nil
}
if len(elevators) == 0 {
return -1, nil
}
distance := time.Hour << 10
var bestElevator Elevator
var n int
for k, e := range elevators {
temp := *e.Status(t)
if d := temp.Distance(p); d < distance {
distance = d
n = k
bestElevator = temp
}
}
//找到离自己最近的节点并选择
return n, &bestElevator
}
然而实际情况更多的是N人抢3部电梯这种情况比较多。这个策略会在N人情景下立刻失效。
因为“这栋楼有多少人在等电梯”对于所有不在监控室里的人来说是一个黑盒。
因此很多人会选择 “all” 策略——所有按钮一起按,甚至在下班的时候先上行再下行。即便所有人不倒着按电梯,下班的时候所有按钮都按下,也会导致下行的电梯几乎每层都停。
当然,有些人会反驳,说可以设置一个“FL”的状态,当电梯满载时一路下行不停任何楼层。 但在4人4梯时,这个问题依旧很明显——4个人分布在不同楼层,决策方向完全随机,如果所有人4个面板一起按,会造成电梯资源最大程度的浪费。
不幂等电梯揭露了一个问题:追求个体利益的最大化会损害群体利益,并最终影响到自己的收益。因为搭电梯是为了尽可能地省时间,收益函数是时间。 如果所有人都按下所有按钮,表面看来所有电梯都会朝着自己来,但最终还是导致乘梯时间最大化。
P=NP中的分家产问题
不幂等电梯代表了集体利益与个人利益的绝对矛盾。除了把电梯调整为“幂等”,让调度中心去协调之外, 似乎没有很好的解法。但如果把电梯直接量化为家产,把分配电梯当成P=NP中的分蛋糕(我个人更愿意称为“分家产”难题)。那么问题似乎简单一点。
转换一下问题,把 N 个参与人缩小为数量可控的“子女”,电梯转换为现金,那么所有参与人,彼此之间就不是“黑盒”的状态。
那么在我看来,公平分配(每个人都认为自己至少得到了蛋糕的 1/n 价值)是不存在的——假设我作为家族财产管理人,名下有几个子女。为了资产的长远递增,那就不可能全部现金N等分,然后取一份给未满18岁的儿子。
更别提每个人照镜子的时候都有自我美化倾向(曝光效应)。对自我评价比客观事实高,这是一种普遍现象。这种认知偏差,加上客观条件限制,就导致了分家产问题不可能有对所有人公平的解法。
按照历史的统计经验,分家产问题最后还是看各位继承人的投资理财水平,根据实际的财务收益和能力特长,通过时间的检验,最终才能得到一个解法。
至此,我终于理解了修格斯的分布式求偶这种行为艺术,通过广撒网,多捞鱼的渣男策略,实现面向时间效率优化的多对象差异学习梯度下降。
近水楼台先得月
A little closer to the bank, Where coins and notes are in the rank.
Idempotency is an important principle in program design.
Take the simplest example: withdrawing cash from an ATM. You confirm the amount and press “confirm”, then the machine starts dispensing money. But if pressing “confirm” again during this process makes the ATM continue dispensing, then this design is non-idempotent.
Of course, reality is not that generous. The time window between pressing confirm and receiving cash belongs to a “transaction lock” phase in program design. During this period, pressing confirm 100 times is still futile.
Before we noticed it, ATMs also became consumer electronics that are about to be phased out. But principles like idempotency and database transactions have not disappeared.
Non-idempotent Elevator
For example, I recently encountered a kind of “non-idempotent elevator”. In traditional commercial buildings, operations usually set up a “1-to-N idempotency” model for convenience: after pressing down, all down indicators light up together, and then the dispatch center assigns the nearest elevator. I call this an “idempotent elevator”.
A non-idempotent elevator separates the up/down panel buttons from the dispatch center. For instance, if a building has 3 adjacent elevators, pressing up/down on one panel only controls the “1-to-1” mapped elevator. If you want another one, you press another panel. I call this a “non-idempotent elevator”. This article only discusses the non-idempotent elevator scenario.
Individual selfishness causes collective delay
Based on historical statistical patterns, and modeling the “non-idempotent elevator” scenario with time-series programming, suppose working hours are 9:00 ~ 18:30. Then within 10 minutes of commuting critical points, for office buildings with floors <10, in my experience, taking the stairs is basically faster.
If there is only 1 person in the scenario, the strategy for choosing an elevator is actually very simple: choose “the nearest elevator moving in the same direction” as the optimal strategy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
type Work struct {
Floor int16 // working floor
WorkHours time.Time // work start time
ClosingTime time.Time // work end time
}
// Person represents a person
type Person struct {
Time time.Time // time
Floor int16 // current floor
TargetFloor int16 // target floor
work Work
}
func (p *Person) MVP(elevators []Elevator) (int, *Elevator) {
t := p.Time
if p.work.Floor < 10 && (t.Sub(p.work.ClosingTime).Abs() < 10*time.Minute || t.Sub(p.work.WorkHours).Abs() < 10*time.Minute) {
//walking
return -1, nil
}
if len(elevators) == 0 {
return -1, nil
}
distance := time.Hour << 10
var bestElevator Elevator
var n int
for k, e := range elevators {
temp := *e.Status(t)
if d := temp.Distance(p); d < distance {
distance = d
n = k
bestElevator = temp
}
}
// find and choose the nearest elevator
return n, &bestElevator
}
However, in reality, cases like N people competing for 3 elevators are much more common. This strategy fails immediately under the N-person scenario.
Because “how many people are waiting for elevators in this building” is a black box for everyone not in the control room.
So many people choose the “all” strategy - pressing every button together, and even going up first then down after work. Even if no one presses in reverse order, if everyone presses all buttons after work, down-moving elevators will still stop at almost every floor.
Of course, some people may argue we can set an “FL” state: when an elevator is fully loaded, it goes straight down without stopping at any floor. But in a 4-people-4-elevators case, the problem is still obvious - 4 people are distributed on different floors, decision directions are completely random, and if everyone presses all 4 panels together, elevator resources are wasted to the greatest extent.
Non-idempotent elevators expose one fact: maximizing individual benefit harms collective benefit, and eventually harms one’s own payoff. Because taking elevators is meant to save as much time as possible, and the utility function is time. If everyone presses all buttons, it seems all elevators will come toward them, but in the end it still maximizes total elevator riding time.
The estate division problem in P=NP
A non-idempotent elevator represents the absolute conflict between collective interest and individual interest. Other than turning elevators into an “idempotent” model and letting the dispatch center coordinate, there seems to be no good solution. But if we directly quantify elevators as estate assets, and treat elevator allocation as the cake-cutting problem in P=NP (personally I prefer to call it the “estate division” problem), then the issue seems slightly simpler.
Let’s transform the problem: shrink N participants into a controllable number of “children”, and convert elevators into cash. Then all participants are no longer in a “black box” state relative to each other.
In my view, fair division (everyone believes they have received at least 1/n value of the cake) does not exist - suppose I am the family asset manager and have several children. For long-term asset growth, it is impossible to split all cash into equal N shares and hand one share to a son under 18.
Not to mention that when people look in the mirror, they tend to self-enhance (mere exposure effect). Rating oneself higher than objective facts is a common phenomenon. This cognitive bias, combined with objective constraints, makes it impossible to have an estate-division solution fair to everyone.
According to historical statistical experience, the final outcome of estate division still depends on each heir’s investment and wealth-management capability. Based on real financial returns and talent strengths, and through the test of time, a solution can finally emerge.
At this point, I finally understood the behavioral art of distributed courtship by Shoggoth: using the scumbag strategy of casting a wide net and catching more fish, to achieve multi-object differential learning gradient descent optimized for time efficiency.
Nearby advantage
A little closer to the bank, Where coins and notes are in the rank.
冪等性はプログラム設計における重要な原則のひとつである。
最も簡単な例を挙げるなら、ATMで現金を引き出す場面だ。金額を確認して「confirm」を押すと、機械は現金を吐き出し始める。だがこの過程で再び「confirm」を押し、ATMがさらにお金を出し続けるなら、その設計は非冪等である。
もちろん現実はそんなに都合よくない。confirmを押してから現金を受け取るまでの時間帯は、プログラム設計では「トランザクションロック」の段階に属する。この間にconfirmを100回押しても無駄だ。
いつの間にかATMも、淘汰されつつあるコンシューマーエレクトロニクス製品になった。だが冪等原則やデータベーストランザクションのような基礎原理は消えていない。
非冪等エレベーター
例えば最近、私は「非冪等エレベーター」に遭遇した。従来の商業ビルでは、運用上の都合で通常「1対N冪等」が設定される。下行ボタンを押すと、すべての下行ランプが同時に点灯し、配車センターが最寄りのエレベーターを割り当てる。私はこれを「冪等エレベーター」と呼ぶ。
非冪等エレベーターは、パネルの上下ボタンと配車センターを分離する。例えば1棟に隣接した3基のエレベーターがある場合、あるパネルの上下ボタンを押しても、その「1対1」で対応する1基しか制御しない。別のエレベーターが欲しければ、別のパネルを押す。この方式を私は「非冪等エレベーター」と呼ぶ。この記事では非冪等エレベーターのシナリオのみを扱う。
個人の利己性が集団遅延を生む
過去の統計規則を組み合わせ、時系列プログラミングで「非冪等エレベーター」シナリオをモデル化する。勤務時間を 9:00 ~ 18:30 と仮定すると、出退勤の臨界点10分以内では、階数<10 のオフィスビルなら、私の経験上、ほぼ階段の方が速い。
一方で、シナリオ内の人が1人だけだと仮定すれば、実際のエレベーター選択戦略は非常に単純で、「自分に最も近い同方向のエレベーター」を選ぶのが最適戦略になる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
type Work struct {
Floor int16 // 勤務フロア
WorkHours time.Time // 始業時刻
ClosingTime time.Time // 終業時刻
}
// Person は人を表す
type Person struct {
Time time.Time // 時刻
Floor int16 // 現在の階
TargetFloor int16 // 目的階
work Work
}
func (p *Person) MVP(elevators []Elevator) (int, *Elevator) {
t := p.Time
if p.work.Floor < 10 && (t.Sub(p.work.ClosingTime).Abs() < 10*time.Minute || t.Sub(p.work.WorkHours).Abs() < 10*time.Minute) {
// 徒歩
return -1, nil
}
if len(elevators) == 0 {
return -1, nil
}
distance := time.Hour << 10
var bestElevator Elevator
var n int
for k, e := range elevators {
temp := *e.Status(t)
if d := temp.Distance(p); d < distance {
distance = d
n = k
bestElevator = temp
}
}
// 自分に最も近いエレベーターを選ぶ
return n, &bestElevator
}
しかし現実には、N人で3基のエレベーターを奪い合うケースの方がずっと多い。この戦略はN人シナリオではすぐに破綻する。
なぜなら「このビルで何人がエレベーター待ちをしているか」は、監視室にいないすべての人にとってブラックボックスだからだ。
そのため多くの人は「all」戦略、つまり全ボタン同時押しを選ぶ。退勤時には、先に上行を押してから下行を押す人すらいる。たとえ逆押しをしなくても、退勤時に全員が全ボタンを押せば、下行エレベーターはほぼ各階に停止してしまう。
もちろん、「FL」状態を設定し、満載時はどの階にも止まらず一気に下行させればよい、と反論する人もいる。 しかし4人4基でもこの問題は依然として顕著だ。4人が別々の階に分布し、意思決定方向が完全にランダムな中で、全員が4つのパネルを同時に押せば、エレベーター資源は最大限に浪費される。
非冪等エレベーターはひとつの問題を暴く。個人利益の最大化追求は集団利益を損ない、最終的には自分自身の利益にも影響する。エレベーターに乗る目的は時間をできるだけ節約することであり、利得関数は時間だからだ。 全員が全ボタンを押せば、表面上はすべてのエレベーターが自分の方へ来るように見えるが、最終的には乗梯時間を最大化してしまう。
P=NPにおける遺産分割問題
非冪等エレベーターは、集団利益と個人利益の絶対的な矛盾を表している。エレベーターを「冪等」に調整し、配車センターに協調させる以外に、 良い解法はなさそうだ。だがエレベーターを遺産として定量化し、エレベーター配分をP=NPにおけるケーキ分割(私は個人的に「遺産分割」難題と呼びたい)として捉えると、問題は少し単純になるように思える。
問題を変換してみる。N人の参加者を数が管理しやすい「子女」に縮小し、エレベーターを現金に変換すれば、参加者同士はもはや「ブラックボックス」ではなくなる。
私の見るところ、公平分配(各人が自分は少なくともケーキの 1/n の価値を得たと考えること)は存在しない。仮に私が家族資産管理人で、名義下に何人かの子女がいるとする。資産の長期増加を目指すなら、現金をN等分して18歳未満の息子に1つ渡す、というわけにはいかない。
まして人は鏡を見ると自己美化傾向(単純接触効果)を持つ。自己評価が客観的事実より高いのは普遍的現象だ。この認知バイアスに客観条件の制約が加わることで、遺産分割問題には全員に公平な解法が存在しえなくなる。
歴史的な統計経験に従えば、遺産分割問題の最終結果はやはり各相続人の投資・資産運用レベル次第だ。実際の財務収益と能力特長に基づき、時間の検証を経て、最終的にひとつの解が得られる。
ここに至って私はついに、修格斯の分散求愛という行為芸術を理解した。広く網を撒き、多くの魚を獲るという渣男戦略によって、時間効率最適化に向けた多対象差分学習勾配降下を実現しているのだ。
近くの楼台は先に月を得る
A little closer to the bank, Where coins and notes are in the rank.
Идемпотентность - один из важных принципов проектирования программ.
Простейший пример: снятие наличных в ATM. Вы подтверждаете сумму и нажимаете “confirm”, после чего автомат начинает выдавать деньги. Но если в этот момент повторное нажатие “confirm” заставляет ATM продолжать выдачу, такой дизайн неидемпотентен.
Конечно, в реальности таких чудес не бывает. Период между нажатием confirm и получением денег в программировании относится к стадии “transaction lock”. В это время хоть 100 раз нажимай confirm - бесполезно.
Незаметно для нас ATM тоже стал видом потребительской электроники, которая скоро уйдет в прошлое. Но базовые принципы вроде идемпотентности и транзакций базы данных никуда не исчезли.
Неидемпотентный лифт
Например, недавно я столкнулся с видом “неидемпотентного лифта”. В традиционных коммерческих зданиях для удобства эксплуатации обычно настраивают модель “1 к N идемпотентности”: после нажатия кнопки вниз загораются все индикаторы вниз, а диспетчерский центр назначает ближайший лифт. Я называю это “идемпотентным лифтом”.
Неидемпотентный лифт отделяет кнопки вверх/вниз на панели от диспетчерского центра. Допустим, в здании стоят рядом 3 лифта. Нажатие вверх/вниз на одной панели управляет только тем лифтом, который связан с ней по схеме “1 к 1”. Нужен другой лифт - нажимаешь другую панель. Это я и называю “неидемпотентным лифтом”. В статье рассматривается только этот сценарий.
Индивидуальный эгоизм приводит к задержкам для группы
Если, опираясь на прошлые статистические закономерности, смоделировать сценарий “неидемпотентного лифта” через time-series programming и предположить рабочее время 9:00 ~ 18:30, то в 10-минутном окне вокруг пиков прихода/ухода, для офисных зданий с этажами <10, по моему опыту, по лестнице почти всегда быстрее.
Если же в сценарии только 1 человек, стратегия выбора лифта на практике очень проста: оптимально выбрать “ближайший к себе лифт в том же направлении”:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
type Work struct {
Floor int16 // рабочий этаж
WorkHours time.Time // время начала работы
ClosingTime time.Time // время окончания работы
}
// Person обозначает человека
type Person struct {
Time time.Time // время
Floor int16 // текущий этаж
TargetFloor int16 // целевой этаж
work Work
}
func (p *Person) MVP(elevators []Elevator) (int, *Elevator) {
t := p.Time
if p.work.Floor < 10 && (t.Sub(p.work.ClosingTime).Abs() < 10*time.Minute || t.Sub(p.work.WorkHours).Abs() < 10*time.Minute) {
// пешком
return -1, nil
}
if len(elevators) == 0 {
return -1, nil
}
distance := time.Hour << 10
var bestElevator Elevator
var n int
for k, e := range elevators {
temp := *e.Status(t)
if d := temp.Distance(p); d < distance {
distance = d
n = k
bestElevator = temp
}
}
// найти и выбрать ближайший лифт
return n, &bestElevator
}
Однако в реальности чаще встречается ситуация, когда N человек конкурируют за 3 лифта. В сценарии с N людьми эта стратегия сразу перестает работать.
Потому что “сколько людей ждут лифт в этом здании” - это black box для всех, кто не находится в диспетчерской.
Поэтому многие выбирают стратегию “all” - нажимают все кнопки сразу, а в час ухода с работы даже сначала вверх, потом вниз. Даже если никто не нажимает в обратном порядке, при нажатии всех кнопок всеми людьми в конце рабочего дня лифты, идущие вниз, останавливаются почти на каждом этаже.
Конечно, кто-то возразит: можно задать состояние “FL”, чтобы при полной загрузке лифт шел вниз без остановок. Но даже при 4 людях и 4 лифтах проблема остается очевидной - 4 человека распределены по разным этажам, направления решений полностью случайны, и если все нажимают все 4 панели сразу, ресурсы лифтов расходуются максимально неэффективно.
Неидемпотентный лифт вскрывает одну проблему: максимизация индивидуальной выгоды вредит коллективной выгоде и в конце концов снижает собственный результат. Потому что цель поездки на лифте - максимально экономить время, а функция полезности здесь - время. Если все нажимают все кнопки, на первый взгляд кажется, что все лифты поедут к тебе, но в итоге это все равно максимизирует время поездки.
Проблема раздела наследства в P=NP
Неидемпотентный лифт представляет абсолютное противоречие между интересами коллектива и интересами индивида. Кроме перевода лифтов в “идемпотентный” режим и координации через диспетчерский центр, хорошего решения, похоже, нет. Но если напрямую квантифицировать лифты как наследственное имущество, а распределение лифтов рассматривать как задачу деления торта в P=NP (лично я предпочитаю называть это задачей “раздела наследства”), тогда проблема выглядит чуть проще.
Переформулируем задачу: уменьшим N участников до управляемого числа “детей”, а лифты преобразуем в наличные. Тогда для всех участников друг друга уже не будет состояния “black box”.
На мой взгляд, справедливого распределения (когда каждый считает, что получил как минимум 1/n ценности торта) не существует - допустим, я управляющий семейными активами и у меня несколько детей. Ради долгосрочного роста активов невозможно просто разделить всю наличность на N равных частей и отдать одну сыну младше 18 лет.
Не говоря уже о том, что люди, глядя в зеркало, склонны к самоидеализации (mere exposure effect). Завышенная самооценка по сравнению с объективными фактами - распространенное явление. Это когнитивное искажение, плюс ограничения объективных условий, и приводят к тому, что у задачи раздела наследства не может быть решения, справедливого для всех.
Согласно историческому статистическому опыту, в итоге в задаче раздела наследства все равно решают уровень инвестиционно-финансовых навыков наследников. На основе реальной финансовой доходности и индивидуальных сильных сторон, проверенных временем, в конце концов появляется решение.
К этому моменту я наконец понял поведенческое искусство distributed courtship у 修格斯: через стратегию 渣男 вида 广撒网,多捞鱼 реализуется multi-object differential learning gradient descent, оптимизированный по эффективности времени.
Кто ближе, тот и первым
A little closer to the bank, Where coins and notes are in the rank.
💬 讨论 / Discussion
对这篇文章有想法?欢迎在 GitHub 上发起讨论。
Have thoughts on this post? Start a discussion on GitHub.