2

Some of my programs are required to have a strict time limit for completing given tasks.

If I am correct, converting certain loops to mathematical equations should reduce the time complexity for my program, yes? I can get the same result the loop finds, in just one operation?

I've checked many other solutions regarding this problem, sadly they all focus on solving the loop itself rather than the general steps that should be taken to convert loops into mathematical equations, I could not understand much.

I need to be able to convert loops myself, and I can't find anywhere on the internet to help with this one issue. References would be appreciated.

For example, this loop takes more than 7 seconds at some cases:

for (int j = 0; j < N * M; j++){
            S += V;
            V = (A * V + B) % (10007);
        }

And this one as well takes more than one second:

for (int i = 1; i <= product; i++){
    if (product % i == 0)
        sum += i;
}

Please notice that my problem does not lie within these two loops, I need to know how to approach any convertible loop. Answers are also not limited to equations, any performance tips are appreciated.

EDIT: Here is an example of what I mean.

Community
  • 1
  • 1
Remon Ramy
  • 167
  • 1
  • 11
  • 2
    This is more of a mathematics problem than programming. You need to use induction to determine a closed function that's equivalent to the loop. Note that note all loops have an equivalent function. – Barmar Oct 08 '15 at 08:19
  • See https://en.wikipedia.org/wiki/Mathematical_induction – Barmar Oct 08 '15 at 08:20
  • 1
    Did you take algebra in high school? Did you learn how to prove that `1+2+3+...+n` is equal to `n*(n+1)/2`? – Barmar Oct 08 '15 at 08:21
  • 2
    An other alternative may be to find an other algorithm. I assume that your example loops are in another loop and that in fact, it is the whole which can be rewritten to lower complexity. (For the example of sum of divisor of product, if done in an loop increasing `product`, an algorithm like a sieve may be better). – Jarod42 Oct 08 '15 at 08:25
  • Let's just say I live in a terrible country, everything I know is self-taught. I'll take a close look at `mathematical induction`, but I still don't see how this can help me convert the first loop. – Remon Ramy Oct 08 '15 at 08:25
  • 1
    @Barmar Alexander Stepanov dedicates half (3 quarters) of his last book to this kind of mathematical subject, so yes, this kind of question is a legitimate programming question. http://www.amazon.fr/Mathematics-Generic-Programming-Alexander-Stepanov/dp/0321942043 – fjardon Oct 08 '15 at 11:58

2 Answers2

2

You can indeed use mathematical induction to deduce that a loop is equivalent to a simple formula. The loop in the question that you linked is a simple sum of a series and easy to reason about. However, not all loops can be reduced to a formula.

It's quite difficult to reason about either of your loops using induction. The first has a remainder operation, which makes the output periodic. Since it's periodic, a single inductive step does not apply to every increment of j the same way. It is beyond my mathematical skill to prove that a simple formula exists for the loop let alone find that formula.

I recognize the condition in your second loop. It's a test for whether i is a factor of product. So, essentially, you're adding together all factors of a number. I can intuitively guess that there is no easy way to find a simple formula for that, because I know that there is no known fast algorithm for integer factorization (i.e. finding all factors of an integer).

There are faster algorithms than testing every number less than product but for small numbers like int, it's not bad.

As an optimization, you can reduce the iterations to half because we know that no greater number than that (besides product itself) is a factor. 1 is always a factor, so we can add that during initialization. Even further, we can add the pair factor and only iterate until the square root of product:

int sum = 1 + product;
int root = sqrt(product);
for (int i = 2; i < root; i++){
    if (product % i == 0)
        sum += (i + product / i);
}
// add square root separately, because it doesn't have a pair
if (root*root == product)
    sum += root;
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • You can probably do even better in most cases by finding the prime factors and summing all unique products of subsets of the prime factors. If there are any small prime factors, the trial-division loop doesn't have to check all the way to sqrt(product). – Peter Cordes Oct 08 '15 at 19:10
2

I don't have time to fully expand the solution into code but you'll find usefull ideas.

First Loop

First I change N*M to only N because it will simplify the writing of the equations (you can then substitute back in the final equation to find the correct formula). I will also assume that S is equal to 0 when entering the loop. I will also work in the field Z/10007Z (10007 being a prime)

   for (int j = 0; j < N; j++){
        S += V;
        V = (A * V + B) % (10007);
    }

Basically you have a sequence of numbers v_i and a sum S_i defined as follows:

v_0     = ? // not important in the following equations
S_0     = 0
v_{i+1} = (A*v_i+B)
S_{i+1} = S_i + v_{i}

You can rewrite the recurrence formula for v_i as a matrix operation:

|v_{i+1}|   | A  B | |v_i|
|       | = |      | |   |
|      1|   | 0  1 | |  1|

Let's call M the matrix. You can now easily compute any value v_i by the following formula:

|v_i|    i |v_0|
|   | = M  |   |
|  1|      |  1|

Then by summing over i from 0 to N you get:

|S|   / N    i \ |v_0|
| | = |SUM  M  | |   |
|1|   \i=0     / |  1|

Let's call the sum of powers of the matrix M: Q

You can easily prove that the ith power of M is:

 i  | A^i  B(A^i+A^(i-1)+...+1) |
M = |                           |
    | 0                       1 |

Which turns out to be:

 i  | A^i  B(1-A^(i+1))/(1-A) |
M = |                         |
    | 0                     1 |

(see: https://en.wikipedia.org/wiki/Geometric_series#Sum)

So we can rewrite Q as:

    | (1-A^(N+1))/(1-A)  B*SUM(i=1,N, B(1-A^(i+1))/(1-A) ) |
Q = |                                                      |
    | 0                                                  1 |

Eventually we get:

     1   | 1-A^(N+1)  B*( N - (1-A^(N+1))/(1-A) ) |
Q = ---  |                                        |
    1-A  | 0                                    1 |

You can easily compute A^(N+1) in O(log(N)).

Computing 1/(1-A) is done by computing (1-A)^(10007-1-1) as per Fermat's little theorem.

If A is known in advance you can even precompute it.

Obviously, everything is done in the field of number modulo 10007 as already explained.

Second Loop

Basically you're computing the number of divisors of a number. I don't know any better way of doing it. BUT if you have to do it for many contiguous numbers there may be an optimization.

fjardon
  • 7,921
  • 22
  • 31