Tysta dueller—Konstruera lösningen del 1 || Math ∩ Programmering

Tysta dueller—Konstruera lösningen del 1 || Math ∩ Programmering


Tidigare inlägg i den här serien:

Tysta dueller och ett gammalt papper från Restrepo Tysta dueller – analysera konstruktionen

Förra gången vadade vi in ​​på Restrepos tysta duellpapper. Du kan se originalet och min omformaterade version på Github tillsammans med all kod i den här serien. Vi smälte avsnitt 2 och lite längre och ritade några förenklade exempel på den symmetriska versionen av spelet.

Jag erkänner att den här uppsatsen inte är lätt att läsa. Avsnitt 3-6 presenterar de centrala definitionerna, lemman och bevis. De är ett slag av tät notskrift utan vägledande intuition. Symbolerna får inte ens catchy namn eller “tänk på det här som” förklaringar! Jag tror att denna skillnad i kommunikation har något att göra med den period då uppsatsen skrevs, och något att göra med datorernas barndom vid den tiden. Tidningen publicerades 1957, vilket markerade året IBM bytte från vakuumrör till transistorer, långt innan Moores lag ens var en glimt i Gordon Moores ögon. Vi kan inte klandra Restrepo för att inte vädja till våra moderna känslor.

Jag tillbringade pinsamt mycket tid med att kämpa mig igenom avsnitt 3-6 när jag fortfarande inte riktigt förstod formen av den optimala strategin. Det är inte förrän i allra högsta grad avsluta av papperet (avsnitt 8, beviset för sats 1) att vi får en konstruktion. Se sista inlägget för en detaljerad beskrivning av den data som utgör den optimala strategin. I korthet är det en partition av $ (0,1)$ i delintervall $ (a_i, a_{i+1})$ med en sannolikhetsfördelning $ F_i$ på varje intervall, och tiden du tar din $ i$-th åtgärd väljs slumpmässigt enligt $ F_i$. Alternativt kan den sista fördelningen inkludera en poängmassa vid tiden $ t=1$, dvs “vänta till sista ögonblicket för en garanterad träff.”

Avsnitt 8 beskriver hur man väljer $ a_i$ och $ b_j$, med fördelningarna $ F_i$ och $ G_j$ byggda enligt formlerna som byggts upp i föregående avsnitt.

Eftersom vårt mål fortfarande är att förstå hur man konstruerar lösningen – även om varför det fungerar är fortfarande ett mysterium – vi kommer att skriva ett program som implementerar den här algoritmen i två inlägg. Först kommer vi att arbeta med ett förenklat symmetriskt spel, där svaret tillhandahålls för oss som ett testfall. I ett uppföljande inlägg kommer vi att omarbeta koden för att konstruera den generiska lösningen och välja nits om kodkvalitet, och de finare punkterna i algoritmen Restrepo utelämnar.

I slutändan, om det som programmet producerar stämmer överens med Restrepos exempel (i stället för att förstå tillräckligt mycket av tidningen för att konstruera vårt eget), kommer vi att utropa seger – vi har framgångsrikt förnuftskontrollerat Restrepos konstruktion. Sedan kan vi gå vidare till att studera varför den här lösningen fungerar och vilka förbehåll som döljer sig under matematiken.

Följ formlerna

Ingången till spelet är ett val av $ n$ åtgärder för spelare 1, $ m$ åtgärder för spelare 2, och sannolikhetsfördelningar $ P, Q$ för de två spelarnas framgångsannolikheter (respektive). Här är algoritmen som anges av Restrepo, med de refererade ekvationerna som följer. Om du följer med min “arbeta genom tidningen organiskt” shtick, rekommenderar jag att du försöker analysera texten nedan innan du läser vidare. Minns $ \alpha$ är sannolikheten “vänta tills slutet” för spelare 1:s sista åtgärd, och $ \beta$ är den analoga sannolikheten för spelare 2.

Tysta dueller—Konstruera lösningen del 1 || Math ∩ Programmering

Restrepos beskrivning av algoritmen för att beräkna den optimala strategin.

Ekvationerna som refereras till ovan

Ekvationerna som refereras till ovan

Låt oss reda ut den här röran.

Först den breda bilden. Hela konstruktionen beror på $ \alpha, \beta$, dessa poängmassor för spelarnas slutliga handlingar. Det finns dock detta villkor att $ \alpha \beta = 0$, dvs högst en kan vara lik noll. Detta ger en vagt intuitiv mening: en spelare med fler åtgärder kommer att ha extra “tillskott” och därför kan det vara vettigt för dem att vänta till slutet för att få en garanterad träff. Men bara en spelare kan ha en sådan fördel gentemot den andra, så bara en av de två parametrarna kan vara lik noll. Det är min informella motivering för $ \alpha \beta = 0$.

Om vi ​​inte vet $ \alpha, \beta$ i början, är Restrepos konstruktion (valet av $ a_i$s och $ b_j$s) en deterministisk funktion av $ \alpha, \beta$ och de andra fasta ingångarna.

$$\displaystyle (\alpha, \beta) \mapsto (a_1, \dots, a_n, b_1, \dots, b_m)$$

Konstruktionen hävdar att den optimala lösningen har $ a_1 = b_1$ och vi måste hitta en ingång $ (\alpha, \beta) \in (0,1) \times (0,1)$ så att $ \alpha \beta = 0$ och de producerar $ a_1 = b_1 = 1$ som utdata. Vi gör en sökning efter de “rätta” utdataparametrarna och använder kunskap om det kedjade sambandet mellan ekvationer för att titta på utdata, och använder det för att justera indata för att få utdata närmare det vi vill ha. Det är inte gradientnedstigning, men det skulle förmodligen kunna omformuleras på det sättet.

Tänk särskilt på fallet när vi får $ b_1 omvänd utdata, dvs kommer att göra $ a_1 mellanvärdessats från kalkyl, men till hitta det värdet det enklaste alternativet är binär sökning. Vi har de övre och nedre gränserna, $ \beta = 0$ och $ \beta = 1 – \varepsilon$, och vi vet när vi hittade vårt mål: när utgången har $ a_1 = b_1$.

Denna binära sökning kommer tillbaka i full gång i nästa inlägg, eftersom vi redan vet att den symmetriska tysta duellen börjar med $ a_1 = b_1$. Ingen sökning efter $ \alpha, \beta$ behövs, och vi kan fixa dem båda till noll för tillfället – eller snarare, anta att de rätta värdena är kända.

Det som återstår är att bestämma hur man beräknar $ a_i$ och $ b_j$ från en start $ \alpha, \beta$. Vi kommer att gå igenom algoritmen steg för steg med hjälp av det symmetriska spelet där $ P=Q$ (samma åtgärdsframgångsannolikhet) och $ n=m$ (samma åtgärdsantal) för att grunda vår studie. Ett uppföljningsinlägg kommer att se över dessa formler i full allmänhet.

Det symmetriska spelet

Grundidén med konstruktionen är att vi utgår från en beräkning av de sista handlingsparametrarna $ a_n, b_m$, och använder dem induktivt för att beräkna parametrarna för tidigare åtgärder via några integraler och substitutioner. Konstruktionen är med andra ord en rekursion, och intervallet i vilket spelarna gör sin sista åtgärd $ (a_n, 1), (b_m, 1)$ är basfallet. När jag började skriva programmen nedan, ville jag ge ett namn till dessa $ a_i, b_j$ värden. Restrepo verkar hänvisa till dem som “parametrar” i tidningen. Jag ringer dem övergångstidereftersom markera de ögonblick då en spelare “övergår” från ett åtgärdsintervall $ (a_2, a_3)$ till nästa $ (a_3, a_4)$.

För en enkel sannolikhetsfunktion $ P$ resulterar slutet av algoritmen i ekvationer som liknar: välj $ a_t$ så att $ P(a_{nt}) = 1/(2t + 3)$.

Kom ihåg att spelare 1 har en speciell funktion som används i varje steg för att konstruera sin optimala strategi, kallad $ f^*$ av Restrepo. Det definieras för icke-symmetriskt spel enligt följande, där återkallelse $ Q$ är motståndarens handlingssannolikhet:

$$\displaystyle f^*

def f_star(prob_fun, prob_fun_var, larger_transition_times):
”’Compute f* as in Restrepo ’57.

In this implementation, we’re working in the simplified example
where P = Q (both players probabilities of success are the same).
”’
x = prob_fun_var
P = prob_fun

product = 1
for a in larger_transition_times:
product *= (1 – P(a))

return product * diff(P(x), x) / P(x)**3

I denna symmetriska instans av problemet vet vi redan att $ \alpha = \beta = 0$ är den optimala lösningen (Restrepo anger det i avsnitt 2), så vi kan fixa $ \alpha=0$, och beräkna $ a_n$, vilket vi gör härnäst.

I tidningen säger Restrepo “beräkna utan parametrar i definitionen av $ f^*$” och det här menar jag, eftersom det inte finns några större åtgärdsögonblick, är produkten $ \prod_{b_j > t}1-P(b_j)$ tom, dvs. vi skickar en tom lista med larger_transition_times. Restrepo bryter mot detta genom att ibland hänvisa till $ a_{n+1} = 1$ och $ b_{m+1} = 1$, men om vi inkluderade någon av dessa $ P(a_{n+1}) = P(1) = 0$, och detta skulle göra definitionen av $ f^*$ noll, vilket skulle ge en icke-fördelning, så det kan inte vara rätt. Detta är ett av de fall där man, när man läser en matematikuppsats, måste sluta sig till den tolkning som är mest förnuftig och ge författaren fördelen av tvivel.

Att följa resten av ekvationerna är trivialt, förutom att vi löser för $ a_n$ som är en gräns för integration. Eftersom SciPy fungerar symboliskt kan vi dock helt enkelt säga åt det att integrera utan att berätta för det $ a_n$, och be det lösa för $ a_n$.

from sympy import Integral
from sympy import Symbol
from sympy.solvers import solve
from sympy.functions.elementary.miscellaneous import Max

def compute_a_n(prob_fun, alpha=0):
    P = prob_fun
    t = Symbol('t0', positive=True)
    a_n = Symbol('a_n', positive=True)

    a_n_integral = Integral(
        ((1 + alpha) - (1 - alpha) * P
    a_n_integrated = a_n_integral.doit()   # yes now "do it"
    P_a_n_solutions = solve(a_n_integrated - 2 * (1 - alpha), P(a_n))
    P_a_n = Max(*P_a_n_solutions)
    print("P(a_n) = %s" % P_a_n)

    a_n_solutions = solve(P(a_n) - P_a_n, a_n)
    a_n_solutions_in_range = (soln for soln in a_n_solutions if 0  soln  1)
    assert len(a_n_solutions_in_range) == 1
    a_n = a_n_solutions_in_range(0)
    print("a_n = %s" % a_n)

    h_n_integral = Integral(f_star(P, t, ()), (t, a_n, 1))
    h_n_integrated = h_n_integral.doit()
    h_n = (1 - alpha) / h_n_integrated
    print("h_n = %s" % h_n)

    return (a_n, h_n)

Det finns tre faser här. Först integrerar vi och löser $ a_n$ (blint enligt ekvation 27 i tidningen). Om du räknar ut denna integral för hand (expanderar $ f^*$), kommer du att märka att den ser ut som $ P’

def compute_as_and_bs(prob_fun, n, alpha=0):
”’Compute the a’s and b’s for the symmetric silent duel.”’
P = prob_fun
t = Symbol(‘t0’, positive=True)

a_n, h_n = compute_a_n(prob_fun, alpha=alpha)

normalizing_constants = deque((h_n))
transitions = deque((a_n))
f_star_products = deque((1, 1 – P(a_n)))

for step in range(n):
# prepending new a’s and h’s to the front of the list
last_a = transitions(0)
last_h = normalizing_constants(0)
next_a = Symbol(‘a’, positive=True)

next_a_integral = Integral(
(1 – P
next_a_integrated = next_a_integral.doit()
# print(“%s” % next_a_integrated)
P_next_a_solutions = solve(next_a_integrated – 1 / last_h, P(next_a))
print(“P(a_{n-%d}) is one of %s“ % (step + 1, P_next_a_solutions))
P_next_a = Max(*P_next_a_solutions)

next_a_solutions = solve(P(next_a) – P_next_a, next_a)
next_a_solutions_in_range = (
soln for soln in next_a_solutions if 0 soln 1)
assert len(next_a_solutions_in_range) == 1
next_a_soln = next_a_solutions_in_range(0)
print(“a_{n-%d} = %s“ % (step + 1, next_a_soln))

next_h_integral = Integral(
f_star(P, t, transitions), (t, next_a_soln, last_a))
next_h = 1 / next_h_integral.doit()
print(“h_{n-%d} = %s“ % (step + 1, next_h))

print(“dF_{n-%d} coeff = %s“ % (step + 1, next_h * f_star_products(–1)))

f_star_products.append(f_star_products(–1) * (1 – P_next_a))
transitions.appendleft(next_a_soln)
normalizing_constants.appendleft(next_h)

return transitions

Slutligen kan vi köra den med enklast möjliga sannolikhetsfunktion: $ P
compute_as_and_bs(Lambda((x,), x), 3)

Utgången är

P(a_n) = 1/3
a_n = 1/3
h_n = 1/4
P(a_{n-1}) is one of (1/5)
a_{n-1} = 1/5
h_{n-1} = 3/16
dF_{n-1} coeff = 1/8
P(a_{n-2}) is one of (1/7)
a_{n-2} = 1/7
h_{n-2} = 5/32
dF_{n-2} coeff = 1/12
P(a_{n-3}) is one of (1/9)
a_{n-3} = 1/9
h_{n-3} = 35/256
dF_{n-3} coeff = 1/16

This matches up so far with Restrepo’s example, since $ P(a_{n-k}) = a_{n-k} = 1/(2k+3)$ gives $ 1/3, 1/5, 1/7, 1/9, \dots$. Since we have the normalizing constants $ h_i$, we can also verify the probability distribution for each action aligns with Restrepo’s example. The constant in the point mass function is supposed to be $ h_i \prod_{j={i+1}}^n (1-P(a_j))$. This is what I printed out as dF_{n-k}. I Restrepos exempel förväntas detta vara $ \frac{1}{4(k+1)}$, vilket är exakt vad som skrivs ut.

Ett annat exempel med $ P



Source link

Postagens Similares

  • Exclusive Cover Reveal: For the Love of the Quest by Alexandra Ammon Parthun

    Today on the site I’m delighted to reveal the cover of For the Love of the Quest by Alexandra Ammon Parthun, a debut Sapphic Historical Romance releasing August 11, 2026 from Alcove Press! Here’s the story: The search for King Arthur’s Excalibur invites danger… and potentially love in this Sapphic historical romance, perfect for fans…

  • ਵੱਡੇ ਸ਼ਹਿਰਾਂ ਵਿੱਚ ਜਾਣਾ: ਲੁਗ ਬਨਾਮ ਕਲਟਰ

    2026 ਅੱਪਡੇਟ: ਇੱਕ ਸੈਕਸ਼ਨਲ ਸੋਫਾਬੈੱਡ ਸੋਫੇ ਨੂੰ ਮੂਵ ਕਰਨ ਲਈ Lugg ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਗਈ ਅਤੇ ਇਸ ‘ਤੇ ਪਛਤਾਵਾ ਹੋਇਆ – ਇਸਦੀ ਕੀਮਤ $222.51 ਇੱਕ ਤਰਫਾ (44 ਮਿੰਟ ਲੇਬਰ ਪਿਕਅਪ 55 ਮਿੰਟ ਲੇਬਰ ਡਰਾਪਆਫ, ਜੋ ਕਿ ਮੇਰੇ ਖਿਆਲ ਵਿੱਚ ਧੋਖਾਧੜੀ ਸੀ, ਪਿਕਅਪ ਤੋਂ ਵੱਧ ਕਿਸੇ ਵੀ ਤਰੀਕੇ ਨਾਲ ਡਰਾਪਆਫ ਦੀ ਲਾਗਤ ਨਹੀਂ) ਅਤੇ ਕੁੱਲ ਬੇਸ…

  • एलएलएम-ओपनराउटर 0.6

    मुक्त करना: एलएलएम-ओपनराउटर 0.6 llm openrouter refresh कैश समाप्त होने की प्रतीक्षा किए बिना उपलब्ध मॉडलों की सूची को ताज़ा करने का आदेश। मैंने यह सुविधा जोड़ी ताकि मैं किमी 2.6 को ओपनराउटर पर आज़मा सकूं जैसे ही यह वहां उपलब्ध हो। यहां इसका पेलिकन है – इस बार एक HTML पेज के रूप में…

  • 缩放、拉伸和移动正弦曲线

    2026 年 5 月 2 日 07:17 标签 数学 这是关于如何调整标准正弦波以改变其幅度、频率和相移的简要说明。更准确地说,给出一般函数: 我们将看看如何调整参数 , 和 影响形状。下面的每个部分都从数学角度涵盖了这些方面之一,您可以使用底部的演示来直观地试验该主题。 缩放 从概念上来说,缩放是最简单的改变。我们调整 增加或减少 的幅度(最大高度)。设置将使 值是原始函数的两倍(正方向和负方向)。 伸展运动 拉伸会改变 的频率,与其周期成反比。基线函数的周期为 ,这意味着它每隔 重复一次。换句话说,对于任何 。 如果我们设置,我们就会得到。该函数的重复速度是 的两倍,因为 在送入正弦曲线之前乘以 2。如果 变化 ,正弦曲线的输入变化 。因此, is 的周期、 is 的周期等等。 更一般地, 的周期是 。玩下面的演示,通过更改来查看其实际效果 并观察波形如何变化。 如果我们知道我们想要的周期,我们可以很容易地计算出 这给了我们这个时期: 变速 我们讨论的最后一个参数是 ;它被称为 阶段 的正弦曲线。在基线中, 。正弦波在 处为 0,在 处达到其正峰值,在 处再次穿过 0,在 处达到负峰值,并返回到重复开始处的原始位置。 通过添加一个非零…

  • 在 Go 中使用 TCP keepalive · Felix Geisendörfer

    发表: 2014 年 8 月 26 日 如果您曾经编写过一些 TCP 套接字代码,您可能会想:“如果拔掉网线或远程计算机崩溃,我的连接会发生什么情况?”。 简短的回答是:什么也没有。连接的远端将无法发送 FIN 数据包,本地操作系统也不会检测到连接丢失。因此,由您作为开发人员来解决这种情况。 在 Go 中,您可以使用多种方法来帮助解决此问题。也许第一个要考虑的是 SetReadDeadline net.Conn 接口的方法。假设您的连接预计会定期接收数据,您可以简单地将超时读取视为相当于 io.EOF 错误和 Close 连接。许多现有的 TCP 协议通过定义某种心跳机制来支持这种错误处理方式,该机制要求每个端点定期发送 PING/PONG 探测,以便检测网络问题以及服务运行状况1。此外,此类心跳还可以帮助处理寻找网络活动以确定连接健康状况的代理服务器。 因此,如果您的协议支持心跳,或者您有能力将心跳添加到自己的协议中,那么这应该是解决未插入网线场景的首选。 但是,如果您无法控制协议并且不支持心跳,会发生什么情况? 现在是时候了解 TCP keepalive 以及如何在 Go 中使用它了。 TCP keepalive 在 RFC 1122 中定义,并不是 TCP 规范本身的一部分。可以为单个连接启用它,但默认情况下必须关闭。启用它将导致网络堆栈在默认持续时间(必须不少于两个小时)后探测空闲连接的运行状况。探测数据包将不包含任何数据2,并且未能回复单个探测不能被解释为死连接,因为单个探测数据包没有可靠地传输。 Go 允许您使用以下命令启用 TCP keepalive net.TCPConn的 SetKeepAlive。在 OSX 和 Linux 上,这将导致在连接空闲 2 小时后以…

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *