当 float 数据类型太小时,如何计算 SQL 服务器中的 Erlang C 值?

How can I calculate Erlang C values in SQL Server when the float datatype is too small?

我们正在我们的数据中心实施 Erlang C 计算,但我遇到了障碍:
SQL 服务器的 float 数据类型的溢出错误。

如何在 SQL 服务器中以我仍然可以执行算术的方式表示超过 10^308 的值?

Wikipedia Article: Erlang C
Better explanation of Erlang C calculation

Erlang C 计算回答了以下问题:“给定预测的呼叫量和估计的处理时间,我们应该安排多少座席以确保在指定时间内接听足够多的呼叫者?”

举个例子:我们的服务水平是 75% 的电话必须在 60 秒内接听。下表探讨了与我们的运营相关的各种数量和处理时间的人员配置。

您可以看到,当所需的代理数量超过 140 时,SQL 服务器无法再处理所需数量的大小。

这里的问题是公式中间的求幂和阶乘项。
]2
例如,导致第一个错误的计算有 V=425 和 AHT=600:
(425 calls/30m @ 10m/call -> 141 call hours/hour -> 141 erlangs) A=141
在 n=142

开始评估
  1. 对于 N=142,A^N 是 3.02002e+305 和 N!是 2.69536e+245。完成计算得出大约 6% 的服务水平,这是不够的。
  2. 对于 N=143,A^N 是 4.27836e+307 和 N!是 3.85437e+247。完成计算得出大约 24% 的服务水平,这仍然不够。
  3. 对于 N=144,A^N 是 6.06101e+309 并且 SQL 服务器在我尝试计算它时产生错误,因为 float 类型最多只能处理大约1e308.

编辑:

@chtz 和@dmuir 给出了我需要的提示。
而不是累积 A^i 和 i!分开,我按照建议把它们累积在一起,新版本完美运行。

SELECT 
    @acc_ai_if = @acc_ai_if * @intensity / cast(@agentcount as float)
--  @acc_if = @acc_if * @agentcount
--, @acc_ai = @acc_ai * @intensity  -- this overflows for N>143
;

很抱歉,我对 SQL 服务器一无所知,但这里有一个避免大数的算法,以及一个 C 程序来演示它。

正如 chtz 在他们的评论中指出的那样,关键是要避免计算大幂和大阶乘。 引入一些任意名称,让

a(N) = pow( A, N)/factorial(N)
b(N) = Sum{ 0<=i<N | a(i)}

那么我们可以写出 N>A 的概率为

P = N/(N-A) * a(N) / (b(N) + N/(N-A) * a(N))

请注意,我们有

b(N+1) = b(N) + a(N)
a(N+1) = (A/(N+1))*a(N)

因此我们可以继续基于这些递归编写代码。但是随着 N 的增加,a 和 b 都会变大,所以我认为最好引入另一个函数 as

beta( N) = a(N)/b(N)

然后 beta(1) = A,当 N 增加超过 A 时 beta(N) 减小(至零)。就 beta 而言,我们有

P = N/(N-A) * beta(N) / (1 + N/(N-A) * beta(N))

一点点代数给出了 beta 的递归:

beta(N) = (A/N) * beta(N-1)/(1+beta(N-1))

如上所述

beta(1) = A

这是一个基于这些想法的 C 程序:

#include    <stdio.h>
#include    <stdlib.h>

    // compute beta(toN) from A and previous value of beta
    static  double  beta_step( int A, int toN, double beta)
    {
    double  f = A/(double)toN;
        return f*beta/(1.0+beta);
    }
    int main( void)
    {
    int A = 140;
    int np = 20;    // number of probabilities to compute
        // compute values for beta at N=1..A    
    double  beta = A;
        for( int N=2; N<=A; ++N)
        {   beta = beta_step( A, N, beta);
        }
        // compute probabilities at N=A+1..A+np
        for( int i=1; i<=np; ++i)
        {
        int N = A+i;
            beta = beta_step( A, N, beta);
        double  f = (double)N/(double)i;    // == N/(N-A)
        double  prob = f*beta/(f*beta + 1.0);   
            printf( "%d\t%f\n", N, prob);
        }   
        return EXIT_SUCCESS; 
    }

我用

编译了这个(使用相当古老的 gcc (4.8.5))
gcc -o erl -std=gnu99 -Wall erl.c

在SQL 服务器

中为 A=341 和 N=1000 计算 ErlangC
DECLARE @startnum float=1
DECLARE @X float = 1

DECLARE @A float= 341
DECLARE @N float= 1000
;
WITH comp AS (
    SELECT @startnum as num,@A as res
    UNION ALL
    SELECT num+1, res*@A/(num+1) FROM comp WHERE num<@N 
)
SELECT res*(@N/(@N-@A))/((SELECT SUM(res) FROM comp where num<@N)+res*(@N/(@N-@A))) FROM comp where num=@N
option (maxrecursion 32000)