![](/img/trans.png)
[英]Increasing the number of records retrieved exponentially increases linq query duration
[英]Exponentially increasing number towards maximum
我正在尝试建立一种重试机制来发送电子邮件。 我希望它是可配置的,以便管理员可以同时指定两者。
另一个要求是每次重试之间要等待的秒数成倍增加(或遵循某些其他几何顺序),直到最大值。
描述问题的另一种方式是: 如何将最大秒数划分为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(tries),e = 2,则第一个间隔将是124/((2^5-1)/(2-1)) = 4s
。 以下间隔将是:
总等待时间为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,则可以将该方程重写为多项式方程的标准形式:
x n - r∙x + r -1 = 0
其中r = T / t是总时间与第一个间隔的长度之间的比率。
据我所知,该方程式没有通用解,但可以使用数值分析库中的算法求解。 我从NuGet上可用的Math.Net Numerics库中选择了Newton-Raphson算法。 对于此算法,需要多项式的一阶导数,这是
n∙x n -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%)
我需要在128秒内调整10个间隔。
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 / 4s的间隔开始并使用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秒。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.