当浮点数据类型太小时,如何在 SQL Server 中计算 Erlang C 值?

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

提问人:Einstein X. Mystery 提问时间:6/20/2020 最后编辑:Einstein X. Mystery 更新时间:3/16/2021 访问量:1001

问:

我们正在我们的数据中心实现 Erlang C 计算,我遇到了一个障碍:
SQL Server 数据类型的溢出错误。
float

如何在 SQL Server 中表示超过 10^308 的值,以便我仍然可以执行算术运算?

维基百科文章:Erlang C 对 Erlang C
计算的更好解释

Erlang C 计算回答了这个问题,“给定预测的呼叫量和估计的处理时间,我们应该安排多少座席来确保在指定时间内有足够比例的呼叫者得到答复?

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

Excel grid

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

这里的问题是公式中间的幂和阶乘项。
]2
例如,导致第一个错误的计算具有 V=425 和 AHT=600:
(425 次调用/30 米 @ 10 米/次调用 -> 141 次调用小时/小时 -> 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 Server 会生成一个错误,因为该类型最多只能处理大约 1e308 的数字。float

编辑:

@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-server 浮点 精度 biginteger 电话呼叫

评论

0赞 chtz 6/20/2020
提示:和 .A^0/0! = 1A^k/k! = (A^(k-1)/(k-1)!)*A/k

答:

1赞 dmuir 6/21/2020 #1

很抱歉,我对 SQL Server 一无所知,但这里有一个避免大数字的算法,以及一个 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)

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

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

然后 beta(1) = A,并且 beta(N) 随着 N 的增加超过 A 而减小(至零)。在测试版方面,我们有

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
0赞 Rosenfeld Eugen 3/16/2021 #2

在 SQL Server 中计算 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)