Time complexity for generic algorithms - math

I was wondering, if my thought process looks odd.
After digging for a bit on time complexity for general code, say:
Alg(x):
statements...
functions...
...
Can we say that T(Alg(x)) = T1(statement) + T2(functions) + T3(...) then break them apart and keep going in depth until we can go no further? This can lead to the Halting Problem which I can see if my previous statement is correct.
From (1) (assuming it holds) then any alg. such that it's non-recursive can we say T(Alg(x)) is:
Alg(x):
block 1 -> {
for (i = 0; i < N; i++) {
for (j = 0; j < M; j++) {
statements
}
}
}
block 2 -> {
for j -> k // T(K)
call_1() // T(N_1)
call_2() // T(N_2)
}
T(Alg(x)) = T1(b1) + T2(b2)
where T1(b1) = T(N) * T2(f2) = T(N) * (T(M) * T3(s3)) as general where N, M are input sizes, and s3 are more breakable parts.
As for T2(b2) we have T(K) + T(N1) + T(N2)

Related

Understanding Bezier derivation code that leads nowhere

I'm trying to convert Bezier.js implementation of calculating normals to a Shadertoy program, and the code appears to not use any of the calculated values. It needs to be for quadratic rationals as well.
I found the Javascript code a slight bit hard to follow, so I simplified it for my Shadertoy program:
vec2[3] derive(vec2[3] p)
{
vec2[3] dpoints;
int l_length = 0, j;
for (int i = 2; i > 0; --i) {
vec2[3] l;
for (j = 0; j < i; j++) {
vec2 dpt = vec2(
float(i) * (p[j + 1].x - p[j].x),
float(i) * (p[j + 1].y - p[j].y));
dpoints[l_length] = dpt;
l[l_length] = dpt; ++l_length;
}
p = l;
}
return dpoints;
}
The Bezier.js program continues to add functionality for 3d beziers, in case that has anything to do with rational beziers.
I need to make sense of the rest of the program, since I don't know the theory for calculating the normals.
To spell Pomax's answer out loud:
Only the last calculated value is used, to make a "curve" (line) from origin.
The weights are calculated as w'0 = 2(w1-w0), w'1 = 2(w2-w1).
The resulting bezier at t gives the tangent of the original bezier at t.
I hope I got this right I haven't tried this yet.

How to do it recursively if function depends on only one parameter

I need to do it with recursion, but the problem is that function depends on only ONE parameter and inside function it depends on two ( k and n ), also how to find minimum value if it returns only one value?
The function is :
I've already tried to make random k, but I don't think that is really good idea.
F1(int n) {
Random random = new Random();
int k = random.Next(1,10);
if (1 <= k && k <= n){
return Math.Min(F1(k - 1) + F1(n - k) + n);
} else {
return 0;
}
}
You need to make a loop traversing all k values in range 1..n. Something like this:
F1(int n) {
if (n == 0)
return ???? what is starting value?
minn = F1(0) + F1(n - 1) + n
for (int k = 2; k <= n; k++)
minn = Math.Min(minn, F1(k - 1) + F1(n - k) + n);
return minn;
}

Writing a loop as an mathematical Equation

For Documentation purposes I need to write a simple loop as a mathematical equation. But there is one thing I can't wrap my head around.
j = 1;
for(i = N; i > 1; --i){
j = 2*j+1;
}
scale = 1/j;
Sadly I can't write it as a simple ^N or at least I can't find the expression because of the +1. Do you know a good mathematical expression?
This is all far too obfuscated.
Let's rewrite to
j = 1;
for (int i = 1; i < N; ++i){
j = 2 * j + 1;
}
Then you can see that this loop can be rewritten as
j = 2N - 1
and you can hence change not only the documentation, but the code too.

Time complexity (in big-O notation) of the following recursive code?

What is the Big-O time complexity ( O ) of the following recursive code?
public static int abc(int n) {
if (n <= 2) {
return n;
}
int sum = 0;
for (int j = 1; j < n; j *= 2) {
sum += j;
}
for (int k = n; k > 1; k /= 2) {
sum += k;
}
return abc(n - 1) + sum;
}
My answer is O(n log(n)). Is it correct?
Where I'm sitting...I think the runtime is O(n log n). Here's why.
You are making n calls to the function. The function definitely depends on n for the number of times the following two operations are made:
You loop up to 2*log(n) values to increment a sum.
For a worst case, n is extremely large, but the overall runtime doesn't change. A best case would be that n <= 2, such that only one operation is done (the looping would not occur).

Handling large groups of numbers

Project Euler problem 14:
The following iterative sequence is
defined for the set of positive
integers:
n → n/2 (n is even) n → 3n + 1 (n is
odd)
Using the rule above and starting with
13, we generate the following
sequence: 13 → 40 → 20 → 10 → 5 → 16 →
8 → 4 → 2 → 1
It can be seen that this sequence
(starting at 13 and finishing at 1)
contains 10 terms. Although it has not
been proved yet (Collatz Problem), it
is thought that all starting numbers
finish at 1.
Which starting number, under one
million, produces the longest chain?
My first instinct is to create a function to calculate the chains, and run it with every number between 1 and 1 million. Obviously, that takes a long time. Way longer than solving this should take, according to Project Euler's "About" page. I've found several problems on Project Euler that involve large groups of numbers that a program running for hours didn't finish. Clearly, I'm doing something wrong.
How can I handle large groups of numbers quickly?
What am I missing here?
Have a read about memoization. The key insight is that if you've got a sequence starting A that has length 1001, and then you get a sequence B that produces an A, you don't to repeat all that work again.
This is the code in Mathematica, using memoization and recursion. Just four lines :)
f[x_] := f[x] = If[x == 1, 1, 1 + f[If[EvenQ[x], x/2, (3 x + 1)]]];
Block[{$RecursionLimit = 1000, a = 0, j},
Do[If[a < f[i], a = f[i]; j = i], {i, Reverse#Range#10^6}];
Print#a; Print[j];
]
Output .... chain length´525´ and the number is ... ohhhh ... font too small ! :)
BTW, here you can see a plot of the frequency for each chain length
Starting with 1,000,000, generate the chain. Keep track of each number that was generated in the chain, as you know for sure that their chain is smaller than the chain for the starting number. Once you reach 1, store the starting number along with its chain length. Take the next biggest number that has not being generated before, and repeat the process.
This will give you the list of numbers and chain length. Take the greatest chain length, and that's your answer.
I'll make some code to clarify.
public static long nextInChain(long n) {
if (n==1) return 1;
if (n%2==0) {
return n/2;
} else {
return (3 * n) + 1;
}
}
public static void main(String[] args) {
long iniTime=System.currentTimeMillis();
HashSet<Long> numbers=new HashSet<Long>();
HashMap<Long,Long> lenghts=new HashMap<Long, Long>();
long currentTry=1000000l;
int i=0;
do {
doTry(currentTry,numbers, lenghts);
currentTry=findNext(currentTry,numbers);
i++;
} while (currentTry!=0);
Set<Long> longs = lenghts.keySet();
long max=0;
long key=0;
for (Long aLong : longs) {
if (max < lenghts.get(aLong)) {
key = aLong;
max = lenghts.get(aLong);
}
}
System.out.println("number = " + key);
System.out.println("chain lenght = " + max);
System.out.println("Elapsed = " + ((System.currentTimeMillis()-iniTime)/1000));
}
private static long findNext(long currentTry, HashSet<Long> numbers) {
for(currentTry=currentTry-1;currentTry>=0;currentTry--) {
if (!numbers.contains(currentTry)) return currentTry;
}
return 0;
}
private static void doTry(Long tryNumber,HashSet<Long> numbers, HashMap<Long, Long> lenghts) {
long i=1;
long n=tryNumber;
do {
numbers.add(n);
n=nextInChain(n);
i++;
} while (n!=1);
lenghts.put(tryNumber,i);
}
Suppose you have a function CalcDistance(i) that calculates the "distance" to 1. For instance, CalcDistance(1) == 0 and CalcDistance(13) == 9. Here is a naive recursive implementation of this function (in C#):
public static int CalcDistance(long i)
{
if (i == 1)
return 0;
return (i % 2 == 0) ? CalcDistance(i / 2) + 1 : CalcDistance(3 * i + 1) + 1;
}
The problem is that this function has to calculate the distance of many numbers over and over again. You can make it a little bit smarter (and a lot faster) by giving it a memory. For instance, lets create a static array that can store the distance for the first million numbers:
static int[] list = new int[1000000];
We prefill each value in the list with -1 to indicate that the value for that position is not yet calculated. After this, we can optimize the CalcDistance() function:
public static int CalcDistance(long i)
{
if (i == 1)
return 0;
if (i >= 1000000)
return (i % 2 == 0) ? CalcDistance(i / 2) + 1 : CalcDistance(3 * i + 1) + 1;
if (list[i] == -1)
list[i] = (i % 2 == 0) ? CalcDistance(i / 2) + 1: CalcDistance(3 * i + 1) + 1;
return list[i];
}
If i >= 1000000, then we cannot use our list, so we must always calculate it. If i < 1000000, then we check if the value is in the list. If not, we calculate it first and store it in the list. Otherwise we just return the value from the list. With this code, it took about ~120ms to process all million numbers.
This is a very simple example of memoization. I use a simple list to store intermediate values in this example. You can use more advanced data structures like hashtables, vectors or graphs when appropriate.
Minimize how many levels deep your loops are, and use an efficient data structure such as IList or IDictionary, that can auto-resize itself when it needs to expand. If you use plain arrays they need to be copied to larger arrays as they expand - not nearly as efficient.
This variant doesn't use an HashMap but tries only to not repeat the first 1000000 numbers. I don't use an hashmap because the biggest number found is around 56 billions, and an hash map could crash.
I have already done some premature optimization. Instead of / I use >>, instead of % I use &. Instead of * I use some +.
void Main()
{
var elements = new bool[1000000];
int longestStart = -1;
int longestRun = -1;
long biggest = 0;
for (int i = elements.Length - 1; i >= 1; i--) {
if (elements[i]) {
continue;
}
elements[i] = true;
int currentStart = i;
int currentRun = 1;
long current = i;
while (current != 1) {
if (current > biggest) {
biggest = current;
}
if ((current & 1) == 0) {
current = current >> 1;
} else {
current = current + current + current + 1;
}
currentRun++;
if (current < elements.Length) {
elements[current] = true;
}
}
if (currentRun > longestRun) {
longestStart = i;
longestRun = currentRun;
}
}
Console.WriteLine("Longest Start: {0}, Run {1}", longestStart, longestRun);
Console.WriteLine("Biggest number: {0}", biggest);
}

Resources