VikeStep
VikeStep

Reputation: 43

How to Speed up Computation Time

I have written the following code:

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()

I need to find all the sets of data that fit the following equation

(((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

each variable (a-g) is a real integer. So I calculated the maximum values for each of the 7 (the maximum for each variable will be when all the other values are 0). These maximum's are 65, 52, 40, 28, 19, 11 and 4 respectfully (62 = a, 52 = b and so on)

So I created 7 nested for loops (as shown in the code above) and in the middle block, i tested the 7 values to see if they fit the criteria, if they did, they were added onto a string. At the end of the code, the program would write over a file and put that final string in containing all the possible combinations.

The program is working fine, however there are 3.1 billion computations carried out over the course of this simulation and from some testing, I found my computer to be averaging 3000 computations per second. This means that the total simulation time is about 12 days and 5 hours. I don't have this time whatsoever, so I had spent all morning simplifying the equation to be tested for, removing unnecessary code and this was my final result.

Is this method I have done using the nested for loops the most optimal method here? If it is, are there any other ways I can speed this up, if not, can you tell me another way?

P.S. I am using Lua because it's the language I am the most familiar with, but if you have other suggestions/examples, use it in your language and I can try optimise it for this program.

Upvotes: 1

Views: 259

Answers (4)

Dmitry Ledentsov
Dmitry Ledentsov

Reputation: 3660

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

runs in 30s on my machine and fills around 1 gb of memory. You can't put 66 times that in the 32 bit Lua VM, and in 64 bit LuaVM still the array part of tables is limited to 32 bit integer keys.

I've commented the outermost loop, so you'll need around 30s*66=33min. I'd write that to 66 different files perhaps. The results are held in a table first, which can then be concatenated. Check out:

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)

printing

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

Upvotes: 0

Since you also asked for solutions in different languages, here is a quick and dirty program in C++, also incorporating the suggestions by @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;
}

Upvotes: 0

Steven Lu
Steven Lu

Reputation: 43547

I would recommend a static language for brute-forcing things of this nature. I had a problem (this one) that I was having trouble with using python but the C++ brute force 8-for-loop approach computes the solution in 30 seconds.

Upvotes: 2

tmyklebu
tmyklebu

Reputation: 14225

I don't speak lua, but here are a few suggestions:

  • Before starting the loop on b, compute and store 1.15^a-1; maybe call it fooa.
  • Likewise, before starting the loop on c, compute fooa+(20/3)*(1.15^b-1); maybe call it foob.
  • Do similar things before starting each loop.
  • If foob, for instance, is at least 10000, break out of the loop; the stuff inside can only make the result bigger.
  • This might be useless or worse in lua, but do you really need to accumulate the result in a string? I don't know how lua represents strings and does concatenation, but the concatenation might be hurting you badly. Try using a list or array data structure instead.

I'd also add that the nested loops are a perfectly sensible solution and, with the above modifications, exactly what I would do.

Upvotes: 3

Related Questions