【问题标题】:How to Speed up Computation Time如何加快计算时间
【发布时间】:2013-09-23 01:14:22
【问题描述】:

我写了以下代码:

combinationsstring = "List of Combinations"
for a = 0, 65 do
    for b = 0, 52 do
        for c = 0, 40 do
            for d = 0, 28 do
                for e = 0, 19 do
                    for f = 0, 11 do
                        for g = 0, 4 do
                            if (((1.15^a)-1)+((20/3)*((1.15^b)-1))
                               +((100/3)*((1.15^c)-1))+(200*((1.15^d)-1))
                               +((2000/3)*((1.15^e)-1))+((8000/3)*((1.15^f)-1))
                               +((40000/3)*((1.15^g)-1))) < 10000 then
                                combinationsstring = combinationsstring
                                    .."\n"..a..", "..b..", "..c..", "..d
                                    ..", "..e..", "..f..", "..g
                            end
                        end
                    end
                end
            end
        end
    end
end

local file = io.open("listOfCombinations.txt", "w")
file:write(combinationsstring)
file:close()

我需要找到符合以下等式的所有数据集

(((1.15^a)-1)+((20/3)*((1.15^b)-1))+
((100/3)*((1.15^c)-1))+(200*((1.15^d)-1))+
((2000/3)*((1.15^e)-1))+((8000/3)*((1.15^f)-1))+
((40000/3)*((1.15^g)-1))) < 10000

每个变量 (a-g) 都是一个实整数。所以我计算了 7 个中的每一个的最大值(每个变量的最大值将是当所有其他值都为 0 时)。这些最大值分别为 65、52、40、28、19、11 和 4(62 = a、52 = b 等等)

所以我创建了 7 个嵌套的 for 循环(如上面的代码所示),在中间块中,我测试了 7 个值以查看它们是否符合标准,如果符合,则将它们添加到字符串中。在代码的最后,程序会覆盖一个文件,并将最后的字符串放入包含所有可能的组合。

该程序运行良好,但是在此模拟过程中执行了 31 亿次计算,并且通过一些测试,我发现我的计算机平均每秒进行 3000 次计算。这意味着总的模拟时间约为 12 天零 5 小时。我没有任何时间,所以我花了整个上午来简化要测试的方程式,删除不必要的代码,这就是我的最终结果。

我使用嵌套 for 循环完成的这个方法是不是这里最优化的方法?如果是,有没有其他方法可以加快速度,如果没有,您能告诉我另一种方法吗?

附:我使用 Lua 是因为它是我最熟悉的语言,但如果您有其他建议/示例,请用您的语言使用它,我可以尝试针对该程序进行优化。

【问题讨论】:

  • 我还不够清醒,无法为算法改进提供建议,但你可以做的一件简单的事情就是从普通 Lua 切换到 LuaJIT,从而大幅提升性能。一件更难的事情是并行化你的代码。这就是所谓的“令人尴尬的并行”问题,其中结果都是相互独立的,因此使用诸如 Lanes 之类的东西将您的计算分成多个线程应该可以让您几乎线性加速(直到您用完内核,无论如何) .
  • 致反对者:您介意解释一下为什么您认为这是一个低质量的问题吗?即使 OP 显然不是专家级程序员,这也是一个非常真实和具体的问题,可以使用程序来解决,并且尝试的解决方案显示了努力和学习的意愿。 (为我 +1)

标签: performance math lua nested-loops


【解决方案1】:

我不会说lua,但这里有一些建议:

  • b 上开始循环之前,计算并存储1.15^a-1;也许叫它fooa
  • 同样,在c 上开始循环之前,计算fooa+(20/3)*(1.15^b-1);也许叫它foob
  • 在开始每个循环之前执行类似的操作。
  • 例如,如果foob 至少是10000,则跳出循环;里面的东西 只能让结果更大。
  • 这在 lua 中可能没用或更糟,但你真的需要将结果累积到一个字符串中吗?我不知道 lua 如何表示字符串并进行连接,但连接可能会严重伤害您。尝试改用列表或数组数据结构。

我还要补充一点,嵌套循环是一个非常明智的解决方案,并且通过上述修改,这正是我要做的。

【讨论】:

  • (+1) 我要补充一点:(a) 内部循环中的串联造成严重伤害,因为它占用了垃圾收集器,并带有要回收的死字符串; (b) 使用数组可能不够,因为所需的内存太大(我尝试了一种天真的方法,但出现内存不足错误); OP 最好的选择是在找到后立即将 [a,b,c,d,e,f,g] 元组写入文件,尽管速度很慢。在表中缓存一小组解决方案然后将它们写出来可能会更好(c)Lua 不会优化表达式,因此您建议手动将公共子表达式从内部循环中提升出来是好的。
  • 谢谢,我会试试这个:)
  • 好的,下面是更新后的代码:pastebin.com/raw.php?i=U1qmCXQc 我现在每秒可以进行大约 20000 次计算,这相当于总共 1 天 19 小时。我现在将尝试在 Lua JIT 上运行它
  • 我试了一下,使用 LuaJIT(在 1.8GHz 办公机器上)可以在大约 15 分钟内计算出整个事情。我没有将每个结果都写入文件,而是将 ~500 存储在一个表中并将其清除,似乎要快一些。这是我使用的代码:pastebin.com/raw.php?i=dTLA5V3J
  • 另一个尝试的技巧是使用 table.concat({a,b,c,d,e,f,g},',') 来连接字符串。
【解决方案2】:

我会推荐一种静态语言来暴力破解这种性质的事情。我遇到了一个问题 (this one),我在使用 python 时遇到了问题,但是 C++ 蛮力 8-for-loop 方法可以在 30 秒内计算出解决方案。

【讨论】:

  • C/C++ 速度确实更快,但我认为将 Lua 的速度与 Python 相比是不公平的。实验表明 Lua(JIT) 是最快的脚本语言。
  • 我同意。除了作为他/她更了解的语言之外,用 Lua 编写的非常具体的 OP 问题并没有带来太多好处。用 C 或 C++ 重新实现它并使用完全优化对其进行编译可以在几乎没有额外麻烦的情况下获得更好的结果。
  • @YuHao 虽然 LuaJIT 确实是最快的动态运行时之一,但很明显这一事实并没有被利用。当涉及到暴力破解时,您无法逃避静态编译程序在性能上的数量级改进。如果我们在谈论一些用 lua 更容易编写的花哨算法,那么也许吧。我说也许吧,因为 C++11 有闭包,而且当你有一个 Clang 集成 IDE 来编写 C++11 时,编写高级程序实际上非常快。
【解决方案3】:

由于您还要求使用不同语言的解决方案,这里是一个快速而肮脏的 C++ 程序,也包含了@tmyklebu 的建议。

#include <iostream>
#include <fstream>
#include <cmath>

int main()
{
    std::ofstream os( "listOfCombinations.txt" );
    using std::pow;
    for( double a = 0; a <= 65; ++a ) {
        double aa = (pow(1.15, a) - 1);
        if ( aa > 10000 ) break;
        for( double b = 0; b <= 52; ++b ) {
            double bb = aa + (20/3) * (pow(1.15, b) - 1);
            if ( bb > 10000 ) break;
            for( double c = 0; c <= 40; ++c ) {
                double cc = bb + (100/3) * (pow(1.15, c) - 1);
                if ( cc > 10000 ) break;
                // The following line provides some visual feedback for the
                // user about the progress (it prints current a, b, and c
                // values).
                std::cout << a << "   " << b << "   " << c << std::endl;
                for( double d = 0; d <= 28; ++d ) {
                    double dd = cc + 200 * ( pow(1.15, d) - 1);
                    if ( dd > 10000 ) break;
                    for( double e = 0; e <= 19; ++e ) {
                        double ee = dd + (2000/3) * (pow(1.15, e) - 1);
                        if ( ee > 10000 ) break;
                        for( double f = 0; f <= 11; ++f ) {
                            double ff = ee + (8000/3) * (pow(1.15, f) - 1);
                            if ( ff > 10000 ) break;
                            for( double g = 0; g <= 4; ++g ) {
                                double gg = ff + (40000/3) * (pow(1.15, g) - 1);
                                if ( gg >= 10000 ) break;
                                os << a << ", " << b << ", " 
                                    << c << ", " << d << ", " 
                                    << e << ", " << f << ", " 
                                    << g << "\n";
                            }
                        }
                    }
                }
            }
        }
    }

    return 0;
}

【讨论】:

    【解决方案4】:
    local res={}
    combinationsstring = "List of Combinations"
    --for a = 0, 65 do
            a=0
        for b = 0, 52 do
            for c = 0, 40 do
                for d = 0, 28 do
                    for e = 0, 19 do
                        for f = 0, 11 do
                            for g = 0, 4 do
                                if (((1.15^a)-1)+((20/3)*((1.15^b)-1))
                                   +((100/3)*((1.15^c)-1))+(200*((1.15^d)-1))
                                   +((2000/3)*((1.15^e)-1))+((8000/3)*((1.15^f)-1))
                                   +((40000/3)*((1.15^g)-1))) < 10000 then
                                            res[#res+1]={a,b,c,d,e,f,g}
                                end
                            end
                        end
                    end
                end
            end
        end
    --end
    

    在我的机器上运行 30 秒,并填充了大约 1 GB 的内存。你不能在 32 位 Lua VM 中放 66 倍,在 64 位 LuaVM 中,表的数组部分仍然限制为 32 位整数键。

    我已经注释了最外层的循环,因此您需要大约 30s*66=33min。我可能会将其写入 66 个不同的文件。结果首先保存在一个表中,然后可以将其连接起来。签出:

    local res={
        {1,2,3,4,5,6,7},
        {8,9,10,11,12,13,14}
    }
    
    for k,v in ipairs(res) do
        -- either concatenate each line and produce a huge string
        res[k]=table.concat(v,", ")
      -- or write each line to a file in this loop
    end
    
    local text=table.concat(res,"\n")
    print(text)
    

    打印

    1, 2, 3, 4, 5, 6, 7
    8, 9, 10, 11, 12, 13, 14
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-21
      • 2018-11-28
      • 1970-01-01
      • 1970-01-01
      • 2018-04-23
      • 1970-01-01
      • 2020-02-08
      • 2013-04-23
      相关资源
      最近更新 更多