首页 > 解决方案 > 当 float 数据类型太小时,如何在 SQL Server 中计算 Erlang C 值?

问题描述

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

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

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

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

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

Excel 网格

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

这里的问题是公式中间的幂和阶乘项。
] 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 Server 会产生错误,因为该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-serverfloating-pointprecisionbigintegerphone-call

解决方案


很抱歉,我对 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 都会变大,所以我认为最好引入另一个函数

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

推荐阅读