数量呈指数级增长,趋于最大值
本文关键字:最大值 指数 | 更新日期: 2023-09-27 18:34:17
我正在尝试构建一种发送电子邮件的重试机制。我希望它是可配置的,以便管理员可以同时指定两者;
- 重试次数(间隔(
- 放弃前的最大秒数
进一步的要求是,每次重试之间的等待秒数呈指数级增长(或遵循其他几何序列(,直至达到最大值。
描述问题的另一种方法是:如何将最大秒数划分为 X 个区间,其中每个区间都比其前一个间隔呈指数级大?
我不确定这是否可以用纯数学表示来表达,如果不是 C# 中的示例会受到欢迎。但是,我真的只是在这里寻找逻辑,所以只要解释得很好,我相信任何语言都可以轻松翻译。
一些数学:让我们有可变T
(总时间(,N
(重试次数(,t
(第一次尝试的时间(和e
(指数(。每次尝试需要:t*1
、t*e
、t*e*e
、t*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)
。
所以给定T
、N
和e
,我们可以计算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∙x1 + ... + 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 秒。