我需要在周末进行模拟。我希望模拟足够大,以尽可能具有描述性,但我不希望它在我恢复工作时没有完成,我无法继续使用该软件。
算法是这样的,一旦启动,它必须完成或根本没有运行它的点。
模拟的各个元素以阶乘,n ^ 4和n ^ 2
运行 n:
<= 6 = 0ms
7 = 8ms
8 = 91ms
9 = 1,089ms
10 = 14,666ms
11 = 197,288ms
12 = 3,091,739ms
我在WolframAlpha here中对这些样本拟合曲线。与我有关的两件事首先是n ^ 4分量是负的,这没有任何意义,因为它肯定是运行时的一个因素。另一件事是,我曾尝试在类似的情况下估计过去的运行时间,而且我的推断通常都是偏离的。
您是否有过以这种方式根据输入大小猜测算法运行时的经验?
答案 0 :(得分:1)
为什么外推失败:
当你想要估算一个超出原始范围的值时,预计推断是偏离的。
在多项式外推中至少 - error是点到所有采样点之间距离与该范围内函数的n'导数的最大值的乘积的函数。如果这个距离很大 - 它(距离和最大导数的乘积)预计会很高。
n ^ 4分量给出负值,因为它显示了可以解释“观察”的最佳函数。
为了估算样品区的运行时间 - 我建议避免使用外推法。当走出已知样品的“舒适区”时,它们的定义并不是很好。
考虑替代方案: 我试着找到常数的粗略估计(通过静态分析代码) - 主要是 - 我想看看阶乘分量是否具有非常小的常数,其余部分具有非常大的常数。如果不是这样的话,可以忽略n ^ 2和n ^ 4个组件 - 与因子组件相比,它们可以忽略不计,这将更容易分析。
PS 从查看提供的动态数据 - 似乎是这种情况,运行时间之间的差异很快会收敛到阶乘因子,因此将函数分析为阶乘,并估算{{ 1}}等似乎是一个合理的假设。
如果您想要安全起见,可以使用f(12) ~= 12* f(11)
,其中d是预定义的正常数,例如f(n) = (n + d) * f(n-1)
。
<强> P.S.2:强>
由于阶乘的行为是如此激进,你可以尝试迭代地运行模拟,d = max{0,f(11)/f(10) - 11}
对于任何f(1) + f(2) + ... + f(n)
预计不会比f(n)
花费更长的时间。通过这样做,您将得到您有时间计算的结果,尽管您在回到办公室时会中止它。此行为称为any time。
答案 1 :(得分:1)
一般来说,当你有一些O(N 4 )时,你也必须引入O(N 3 ),O(N 2 < / sup>),O(N)和O(1)。换句话说,尝试将x 3 ,x 1 和x 0 添加到曲线拟合模型中。
对于这个特殊情况,你有一个O(N!),好吧,我会遵循amit建议,只考虑阶乘部分,因为它似乎收敛得非常快。
但无论如何,如果你真的有一个O(N!),你不需要估计,只需使用iterative deepening方法。让你的计算机迭代运行n = 1,2,3,4,5,6,7的情况...并尽可能地让它去。
似乎你在浪费你的计算机时间,但如果你分析它,你会发现浪费的时间是微不足道的。例如,你已经在n = 12,所以对于n = 13,所需的CPU C 13 将是13 * C 12 ,C 12 = 12 * C 11 ,依此类推。引入您的测量值,总和(C 13 .. C 0 )/ C 13 = 1.082,因此为0中的所有值运行函数13只比仅运行13只贵8%。当你想要更大的N值时,这个百分比会进一步缓解。
<强>更新强>:
为什么您需要为复杂程度以下的所有权力添加条款:
考虑一个复杂度为O(N 3 )的简单三级循环:
void foo(int n) {
int i, j, k;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
for (k = 0; k < n; k++)
do_something(i, j, k);
}
foo(3);
很明显,do_something(i, j, k)
被称为n 3 次。
但是我们从开始考虑每个执行的指令,我们可以看到进入和离开函数,设置堆栈和其他低级别任务一次完成; i=0
指令也执行一次。这些是与n 0 成本相对应的指令。
说明i < n
,i++
和j=0
被称为n次,它们对应于n 1 术语。
指令j < n
,j++
和k=0
称为n 2 次,它们对应于n 2 项。
好吧,等等。
更复杂的情况是相同的,你总是有指令运行的次数与复杂程度以下的所有权力成比例。
关于C(0) = 0
的测量,这只是你的时间不够准确的问题。它可能非常小但绝不是绝对的0。
最后,如果你的曲线拟合不起作用,那是因为N!在这种情况下你也会有运行指令(n-1)!时间和(n-2!)次等等。