数量呈指数级增长,趋于最大值

本文关键字:最大值 指数 | 更新日期: 2023-09-27 18:34:17

我正在尝试构建一种发送电子邮件的重试机制。我希望它是可配置的,以便管理员可以同时指定两者;

  • 重试次数(间隔(
  • 放弃前的最大秒数

进一步的要求是,每次重试之间的等待秒数呈指数级增长(或遵循其他几何序列(,直至达到最大值。

描述问题的另一种方法是:如何将最大秒数划分为 X 个区间,其中每个区间都比其前一个间隔呈指数级大?

我不确定这是否可以用纯数学表示来表达,如果不是 C# 中的示例会受到欢迎。但是,我真的只是在这里寻找逻辑,所以只要解释得很好,我相信任何语言都可以轻松翻译。

数量呈指数级增长,趋于最大值

一些数学:让我们有可变T(总时间(,N(重试次数(,t(第一次尝试的时间(和e(指数(。每次尝试需要:t*1t*et*e*et*e*e*e等。

所以总时间可以写成T = t*e^0 + t*e^1 + t*e^2 +.. + t*e^N重写:T = t*(e^0+e^1+e^2 .. + e^N)。我们可以将幂的总和计算为: sum = (e^N-1)/(e-1)

所以给定TNe,我们可以计算t t = T/((e^N-1)/(e-1))为:

要计算i次迭代的时间,请使用:ti = t*e^i

例如,给定 T = 124(s(、N = 5(尝试(和 e = 2,第一个区间将为 124/((2^5-1)/(2-1)) = 4s 。以下间隔将是:

  • 第 0 个间隔:4 秒 (t*e^0(
  • 第一个间隔:4*2 = 8s (t*e^1(
  • 第 2 个间隔:8*2 = 4*2*2 = 16s (t*e^2(
  • 第 3 个间隔:16*2 = 4*2*2*2 = 32s (t*e^3(
  • 第 4 个间隔:32*2 = 4*2*2*2*2 = 64s (t*e^4(

等待时间总计 124 秒。

抱歉格式。这个问题对数学来说可能更好。

计算所有间隔的代码将是:

public static void TestFunction(int max, int numIntervals) {
    List<double> intervals = new List<double>();
    double exponent = 2;
    double n = Math.Pow(exponent, numIntervals) - 1;
    double d = exponent - 1;
    double t = max / (n / d);
    for (int x = 0; x < numIntervals; x++) {
        double interval = t * Math.Pow(exponent, x);
        intervals.Add(interval);
    }
}

我不确定这个答案有多大用处,但创建起来肯定很有趣。使这个答案有些不同的是,它计算了用于时间间隔指数计算的基数。

因此,输入是总时间以及除以此时间跨度的间隔数。指定第一个间隔的长度,挑战在于计算剩余间隔,以确保它们呈指数增长并加于总时间。

这可以表述为数学方程:

t∙x 0 + t∙x

1 + ... + t∙x n = T

t 是第一个间隔的长度,T 是总时间。n 是区间数,x 是未知基数。

假设 x 不是 1,那么这个方程可以重写为多项式方程的标准形式:

xn - r∙x + r - 1 = 0

其中 r = T/t 是总时间与第一个间隔长度之间的比率。

据我所知,这个方程没有通用的解,但它可以使用数值分析库中的算法来解决。我从 NuGet 上提供的 Math.Net Numerics 库中选择了牛顿-拉夫森算法。对于此算法,需要多项式的一阶导数,这是

n∙xn - 1 -r

将所有这些放在一起,以创建一系列呈指数增长的时间跨度来等待:

IEnumerable<TimeSpan> ExponentialTimeSpans(TimeSpan firstTimeSpan, TimeSpan totalTimeSpan, Int32 count) {
  var ratio = totalTimeSpan.TotalSeconds/firstTimeSpan.TotalSeconds;
  var @base = RobustNewtonRaphson.FindRoot(
    x => Math.Pow(x, count) - ratio*x + ratio - 1,
    x => count*Math.Pow(x, count - 1) - ratio,
    1d + 1E-8, // Assume that base is > 1
    100d // Arbitrary (but BIG) upper limit on base
  );
  for (var i = 0; i < count; i += 1)
    yield return TimeSpan.FromSeconds(firstTimeSpan.TotalSeconds*Math.Pow(@base, i));
}

请注意,您可以轻松地提供没有解决方案的输入,这将导致引发异常。但是,任何基于原始问题陈述的合理输入都应按预期工作。

也许是

下面这样?

var time_in_seconds = 10000; // usually done in milliseconds, so lets say 10 sec.
// if email fails:
time_in_seconds *= 10; 
// So, next will be 100, 1000, etc. & you get your exponential increment.

第一步 - 一个简单的解决方案 - 反向工作! (减少50%(

我需要将 10 个间隔放入 128 秒。

Interval :  Time (Of occurrence)
1        :  128
2        :  64
3        :  32
4        :  16
5        :  8
6        :  4
7        :  2
8        :  1
9        :  1/2
10       :  1/4

注意:以上工作正常(要保持在 X 以下,只需从 X/2 开始(。 下面,没有那么多。 不过,目前可以迭代应用此技术以找到"好"的解决方案。

很好,但是如果我们需要最低限度会发生什么? 开始时重试之间的 1/128 秒可能是不必要的。 所以我们需要做的,是改变指数。

现在以上可以写成result = 1/4 * 2^9或更一般的result = startingInterval * 2^(n-1). 但是我们知道我们想要的结果,所以我们需要重新排列这个公式。

(result/startingInterval)^(1/(n-1)) = base

使用上述值: (128/(1/4))^(1/(10-1)) = base = 2

或者换句话说,要获得 128 秒的总时间,从 1/4 秒的间隔开始并使用 10 次尝试(包括第一次(,那么我们需要将间隔之间的长度每次尝试增加 2 倍。

声明一个最大阈值以及要为每次重试添加的偏移量。

int maxRetryCount = 5;
int offSet = 10000;
int currRetryCount = 0;
int waitTime = 1000; //default value
while(currRetryCount < maxRetryCount)
{
    try
    {
        //Send Email 
        // break out once the email is send
        break;
    }
    catch
    {
        //Wait for a while
        Thread.Sleep(waitTime);
        //increase the wait time for next time
        waitTime += offSet;
        currRetryCount++;
    }
}

在这里,代码将尝试最多 5 次发送电子邮件,每次等待时间将增加 10 秒。