Which function grows faster, exponential or factorial? [closed] - factorial

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
Which function grows faster, exponential (like 2^n, n^n, e^n etc) or factorial (n!)?
Ps: I just read somewhere, n! grows faster than 2^n.

n! eventually grows faster than an exponential with a constant base (2^n and e^n), but n^n grows faster than n! since the base grows as n increases.

n! = n * (n-1) * (n-2) * ...
n^n = n * n * n * ...
Every term after the first one in n^n is larger, so n^n will grow faster.

n^n grows larger than n! -- for an excellent explanation, see the answer by #AlexQueue.
For the other cases, read on:
Factorial functions do asymptotically grow larger than exponential functions, but it isn't immediately clear when the difference begins. For example, for n=5 and k=10, the factorial 5!=120 is still smaller than 10^5=10000. To find when factorial functions begin to grow larger, we have to do some quick mathematical analysis.
We use Stirling's formula and basic logarithm manipulation:
log_k(n!) ~ n*log_k(n) - n*log_k(e)
k^n = n!
log_k(k^n) = log_k(n!)
n*log_k(k) = log_k(n!)
n = log_k(n!)
n ~ n*log_k(n) - n*log_k(e)
1 ~ log_k(n) - log_k(e)
log_k(n) - log_k(e) - 1 ~ 0
log_k(n) - log_k(e) - log_k(k) ~ 0
log_k(n/(e*k)) ~ 0
n/(e*k) ~ 1
n ~ e*k
Thus, once n reaches almost 3 times the size of k, factorial functions will begin to grow larger than exponential functions. For most real-world scenarios, we will be using large values of n and small values of k, so in practice, we can assume that factorial functions are strictly larger than exponential functions.

I want to show you a more graphical method to very easily prove this. We're going to use division to graph a function, and it will show us this very easily.
Let's use a basic and boring division function to explain a property of division.
As a increases, the evaluation of that expression also increases. As a decreases, the evaluation of that expression also decreases.
Using this idea, we can plot a graph based on what we expect to increase, and expect to decrease, and make a comparision as to which increases faster.
In our case, we want to know whether exponential functions will grow faster than factorials, or vice versa. We have two cases, a constant to a variable exponent vs. a variable factorial, and a variable to a variable exponent vs a variable factorial.
Graphing these tools with Desmos (no affiliation, it's just a nice tool), shows us this:
Graph of a constant to variable exponent, vs variable factorial
Although it initially seems that the exponential expression increases faster, it hits a point where it no longer increases as fast, and instead, the factorial expression is increasing faster.
Graph of a variable to variable exponent, vs variable factorial
Although it initially seems to be slower, it begins to rise rapidly past that point, therefore we can conclude that the exponential must be increasing faster than the factorial.

Related

What is the answer for this computational analysis problem?

Two algorithms have the same function, while algorithm A has computational complexity O(2^N) and algorithm B has computational complexity O(N^10). Suppose a real computer can continuously run 10^7seconds, performing 10^3 basic operations per second.
In this computer environment, please answer the following questions.
A) What is the approximate range of N for algorithms A and B, respectively?
B) Which algorithm is more suitable in the environment? Why?
The question is defective.
The fact that A has complexity O(2N) means the number of basic operations (presumably modeled as each basic operation taking the same amount of time) means A takes at most some constant times 2N steps for N at least some threshold N0. Similarly, the fact B has complexity O(N10) means B takes at most some constant times N10 steps for N at least some threshold N1. However, they may be different constants; the number of steps for A is at most C02N and the number of steps for B is at most C1N10, and they may have different thresholds N0 and N1.
In asking about a computer that can perform 103 basic operations for 107 seconds, the question asks for which N is the number of steps of A or B known to be at most 1010. In other words, it asks to solve for N in C02N ≤ 1010 and in C1N10 ≤ 1010.
These are clearly unsolvable without knowing C0 and C1, about which the question gives no information.
Further, we do not know the thresholds N0 and N1 where these bounds are known to apply. So even if we knew C0 and C1, we would not know any bound on how many steps the algorithms take for any particular N.
The question is also defective in that it neglects that the O notation puts only an upper bound on the algorithm. The algorithm may run in fewer steps than the values of the formulae. So it may be that, even with N for which C02N ≤ C1N10, algorithm B is better, or vice-versa.
Possibly it is intended that some simplifying assumptions are intended, such as C0 = C1 = 1, N0 = N1 = 0, and each algorithm takes exactly the number of steps of its formula. Then it is easy to solve 2N ≤ 1010 (N is at most about 33.22) and N10 = 1010 (N ≤ 10). However, if these assumptions are intended, then the author has missed the point of O notation; it characterizes a fundamental nature of an algorithm; it does not quantify its actual number of steps.

Finding first duplicated element in linear time [duplicate]

There is an array of size n and the elements contained in the array are between 1 and n-1 such that each element occurs once and just one element occurs more than once. We need to find this element.
Though this is a very FAQ, I still haven't found a proper answer. Most suggestions are that I should add up all the elements in the array and then subtract from it the sum of all the indices, but this won't work if the number of elements is very large. It will overflow. There have also been suggestions regarding the use of XOR gate dup = dup ^ arr[i] ^ i, which are not clear to me.
I have come up with this algorithm which is an enhancement of the addition algorithm and will reduce the chances of overflow to a great extent!
for i=0 to n-1
begin :
diff = A[i] - i;
sum = sum + diff;
end
diff contains the duplicate element, but using this method I am unable to find out the index of the duplicate element. For that I need to traverse the array once more which is not desirable. Can anyone come up with a better solution that does not involve the addition method or the XOR method works in O(n)?
There are many ways that you can think about this problem, depending on the constraints of your problem description.
If you know for a fact that exactly one element is duplicated, then there are many ways to solve this problem. One particularly clever solution is to use the bitwise XOR operator. XOR has the following interesting properties:
XOR is associative, so (x ^ y) ^ z = x ^ (y ^ z)
XOR is commutative: x ^ y = y ^ x
XOR is its own inverse: x ^ y = 0 iff x = y
XOR has zero as an identity: x ^ 0 = x
Properties (1) and (2) here mean that when taking the XOR of a group of values, it doesn't matter what order you apply the XORs to the elements. You can reorder the elements or group them as you see fit. Property (3) means that if you XOR the same value together multiple times, you get back zero, and property (4) means that if you XOR anything with 0 you get back your original number. Taking all these properties together, you get an interesting result: if you take the XOR of a group of numbers, the result is the XOR of all numbers in the group that appear an odd number of times. The reason for this is that when you XOR together numbers that appear an even number of times, you can break the XOR of those numbers up into a set of pairs. Each pair XORs to 0 by (3), and th combined XOR of all these zeros gives back zero by (4). Consequently, all the numbers of even multiplicity cancel out.
To use this to solve the original problem, do the following. First, XOR together all the numbers in the list. This gives the XOR of all numbers that appear an odd number of times, which ends up being all the numbers from 1 to (n-1) except the duplicate. Now, XOR this value with the XOR of all the numbers from 1 to (n-1). This then makes all numbers in the range 1 to (n-1) that were not previously canceled out cancel out, leaving behind just the duplicated value. Moreover, this runs in O(n) time and only uses O(1) space, since the XOR of all the values fits into a single integer.
In your original post you considered an alternative approach that works by using the fact that the sum of the integers from 1 to n-1 is n(n-1)/2. You were concerned, however, that this would lead to integer overflow and cause a problem. On most machines you are right that this would cause an overflow, but (on most machines) this is not a problem because arithmetic is done using fixed-precision integers, commonly 32-bit integers. When an integer overflow occurs, the resulting number is not meaningless. Rather, it's just the value that you would get if you computed the actual result, then dropped off everything but the lowest 32 bits. Mathematically speaking, this is known as modular arithmetic, and the operations in the computer are done modulo 232. More generally, though, let's say that integers are stored modulo k for some fixed k.
Fortunately, many of the arithmetical laws you know and love from normal arithmetic still hold in modular arithmetic. We just need to be more precise with our terminology. We say that x is congruent to y modulo k (denoted x ≡k y) if x and y leave the same remainder when divided by k. This is important when working on a physical machine, because when an integer overflow occurs on most hardware, the resulting value is congruent to the true value modulo k, where k depends on the word size. Fortunately, the following laws hold true in modular arithmetic:
For example:
If x ≡k y and w ≡k z, then x + w ≡k y + z
If x ≡k y and w ≡k z, then xw ≡k yz.
This means that if you want to compute the duplicate value by finding the total sum of the elements of the array and subtracting out the expected total, everything will work out fine even if there is an integer overflow because standard arithmetic will still produce the same values (modulo k) in the hardware. That said, you could also use the XOR-based approach, which doesn't need to consider overflow at all. :-)
If you are not guaranteed that exactly one element is duplicated, but you can modify the array of elements, then there is a beautiful algorithm for finding the duplicated value. This earlier SO question describes how to accomplish this. Intuitively, the idea is that you can try to sort the sequence using a bucket sort, where the array of elements itself is recycled to hold the space for the buckets as well.
If you are not guaranteed that exactly one element is duplicated, and you cannot modify the array of elements, then the problem is much harder. This is a classic (and hard!) interview problem that reportedly took Don Knuth 24 hours to solve. The trick is to reduce the problem to an instance of cycle-finding by treating the array as a function from the numbers 1-n onto 1-(n-1) and then looking for two inputs to that function. However, the resulting algorithm, called Floyd's cycle-finding algorithm, is extremely beautiful and simple. Interestingly, it's the same algorithm you would use to detect a cycle in a linked list in linear time and constant space. I'd recommend looking it up, since it periodically comes up in software interviews.
For a complete description of the algorithm along with an analysis, correctness proof, and Python implementation, check out this implementation that solves the problem.
Hope this helps!
Adding the elements is perfectly fine you just have to take mod(%) of the intermediate aggregate when calculating the sum of the elements and the expected sum. For the mod operation you can use something like 2n. You also have to fix the value after substraction.

Is it correct to use a table of interpolated prime-counting function `pi(x)` values as an upper bound for an array of primes?

Suppose I want to allocate an array of integers to store all the prime numbers less than some N. I would then need an estimate for the array size, E(N). There is mathematical function that gives the exact number of primes below N, it's the Prime-counting function - pi(n). However, it looks impossible to define the function in terms of elementary functions.
There exist some approximations to the function, but all of them are asymptotic approximations, so they can be either above or below the true number of primes and cannot in general be used as the estimate E(N).
I've tried to use tabulated values of pi(n) for certain n like power-of-two and interpolate between them. However I noticed that the function pi(n) is convex, so the interpolation between sparse table points may accidentally yield values of E(n) below true pi(n) that may result in buffer overflow.
I then decided to exploit the monotonic nature of pi(n) and use the table values of pi(2^(n+1)) as an far upper estimate for E(2^n) an interpolate between them this time.
I still feel not completely sure that for some 2^n < X < 2^(n+1) an interpolation between pi(2^(n+1)) and pi(2^(n+2)) would be the safe upper estimate. Is it correct? How do I prove it?
You are overthinking this. In C, you just use malloc and realloc. I'd 100 times prefer an algorithm that just obviously works instead of one that requires a deep mathematical proof.
Use an upper bound. There are a number to choose from, each more complicated but tighter. I call this prime_count_upper(n) since you want a value guaranteed to be greater than or equal to the number of primes under n. See Chebyshev, Rosser and Schoenfeld, Dusart 1999, Dusart 2010, Axler 2014, and Büthe 2015. R&S is simple and not terrible: π(x) <= x/(log(x)-3/2) for x >= 67 but Dusart gives better ones for larger values. Either way, no tables or original research needed.
The prime number theorem guarantees the nth prime P(n) is on the range n log n < P(n) < n log n + n log log n for n > 5. As DanaJ suggests, tighter bounds can be computed.
If you want to store the primes in an array, you can't be talking about anything too big. As you suggest, there is no direct computation of pi(n) in terms of elementary arithmetic functions, but there are several methods for computing pi(n) exactly that aren't too hard, as long as n isn't too big. See this, for instance.

Exponential Computation by Approximation in Linux Kernel's source code

I have to modify a little bit of TCP algorithm in Linux Kernel's source code.
In it, I have to compute Congestion Window as following:
cwnd = cwnd (1-x^alpha) where alpha and x are float type and satisfy 0<x<1, alpha >0.
Normally, one has to use #include Math.h and pow() function, right?
But I don't know if exponential computation can make operations slower.
So, I think of Bernoulli's inequality
(1-a)^x < 1-ax where 0<a<1
So in my case, am I allowed to approximate the computation by using Bernoulli's.
x^alpha = (1-(1-x))^alpha ~ 1 - alpha(1-x)
If this is not OK then is there any way out.
Please help me with that.
Apart from the question of using or not using floating point numbers in kernel mode, the approximation is not that stellar since it does not preserve positivity. For that use
x^a = 1/(1/x)^a = 1/(1+u)^a approx 1/(1+a*u)
where 1/x is greater or equal 1 and thus all terms in the approximation positive. Reinserting u=1/x-1 gives
x^a approx x/(x+a*(1-x))
which is 0 for x=0, 1 for x=1 and monotonically increasing in between. If x=p/q is rational and a integer, the resulting rational expression is
p/(p+a*(q-p)).

Finding out the duplicate element in an array

There is an array of size n and the elements contained in the array are between 1 and n-1 such that each element occurs once and just one element occurs more than once. We need to find this element.
Though this is a very FAQ, I still haven't found a proper answer. Most suggestions are that I should add up all the elements in the array and then subtract from it the sum of all the indices, but this won't work if the number of elements is very large. It will overflow. There have also been suggestions regarding the use of XOR gate dup = dup ^ arr[i] ^ i, which are not clear to me.
I have come up with this algorithm which is an enhancement of the addition algorithm and will reduce the chances of overflow to a great extent!
for i=0 to n-1
begin :
diff = A[i] - i;
sum = sum + diff;
end
diff contains the duplicate element, but using this method I am unable to find out the index of the duplicate element. For that I need to traverse the array once more which is not desirable. Can anyone come up with a better solution that does not involve the addition method or the XOR method works in O(n)?
There are many ways that you can think about this problem, depending on the constraints of your problem description.
If you know for a fact that exactly one element is duplicated, then there are many ways to solve this problem. One particularly clever solution is to use the bitwise XOR operator. XOR has the following interesting properties:
XOR is associative, so (x ^ y) ^ z = x ^ (y ^ z)
XOR is commutative: x ^ y = y ^ x
XOR is its own inverse: x ^ y = 0 iff x = y
XOR has zero as an identity: x ^ 0 = x
Properties (1) and (2) here mean that when taking the XOR of a group of values, it doesn't matter what order you apply the XORs to the elements. You can reorder the elements or group them as you see fit. Property (3) means that if you XOR the same value together multiple times, you get back zero, and property (4) means that if you XOR anything with 0 you get back your original number. Taking all these properties together, you get an interesting result: if you take the XOR of a group of numbers, the result is the XOR of all numbers in the group that appear an odd number of times. The reason for this is that when you XOR together numbers that appear an even number of times, you can break the XOR of those numbers up into a set of pairs. Each pair XORs to 0 by (3), and th combined XOR of all these zeros gives back zero by (4). Consequently, all the numbers of even multiplicity cancel out.
To use this to solve the original problem, do the following. First, XOR together all the numbers in the list. This gives the XOR of all numbers that appear an odd number of times, which ends up being all the numbers from 1 to (n-1) except the duplicate. Now, XOR this value with the XOR of all the numbers from 1 to (n-1). This then makes all numbers in the range 1 to (n-1) that were not previously canceled out cancel out, leaving behind just the duplicated value. Moreover, this runs in O(n) time and only uses O(1) space, since the XOR of all the values fits into a single integer.
In your original post you considered an alternative approach that works by using the fact that the sum of the integers from 1 to n-1 is n(n-1)/2. You were concerned, however, that this would lead to integer overflow and cause a problem. On most machines you are right that this would cause an overflow, but (on most machines) this is not a problem because arithmetic is done using fixed-precision integers, commonly 32-bit integers. When an integer overflow occurs, the resulting number is not meaningless. Rather, it's just the value that you would get if you computed the actual result, then dropped off everything but the lowest 32 bits. Mathematically speaking, this is known as modular arithmetic, and the operations in the computer are done modulo 232. More generally, though, let's say that integers are stored modulo k for some fixed k.
Fortunately, many of the arithmetical laws you know and love from normal arithmetic still hold in modular arithmetic. We just need to be more precise with our terminology. We say that x is congruent to y modulo k (denoted x ≡k y) if x and y leave the same remainder when divided by k. This is important when working on a physical machine, because when an integer overflow occurs on most hardware, the resulting value is congruent to the true value modulo k, where k depends on the word size. Fortunately, the following laws hold true in modular arithmetic:
For example:
If x ≡k y and w ≡k z, then x + w ≡k y + z
If x ≡k y and w ≡k z, then xw ≡k yz.
This means that if you want to compute the duplicate value by finding the total sum of the elements of the array and subtracting out the expected total, everything will work out fine even if there is an integer overflow because standard arithmetic will still produce the same values (modulo k) in the hardware. That said, you could also use the XOR-based approach, which doesn't need to consider overflow at all. :-)
If you are not guaranteed that exactly one element is duplicated, but you can modify the array of elements, then there is a beautiful algorithm for finding the duplicated value. This earlier SO question describes how to accomplish this. Intuitively, the idea is that you can try to sort the sequence using a bucket sort, where the array of elements itself is recycled to hold the space for the buckets as well.
If you are not guaranteed that exactly one element is duplicated, and you cannot modify the array of elements, then the problem is much harder. This is a classic (and hard!) interview problem that reportedly took Don Knuth 24 hours to solve. The trick is to reduce the problem to an instance of cycle-finding by treating the array as a function from the numbers 1-n onto 1-(n-1) and then looking for two inputs to that function. However, the resulting algorithm, called Floyd's cycle-finding algorithm, is extremely beautiful and simple. Interestingly, it's the same algorithm you would use to detect a cycle in a linked list in linear time and constant space. I'd recommend looking it up, since it periodically comes up in software interviews.
For a complete description of the algorithm along with an analysis, correctness proof, and Python implementation, check out this implementation that solves the problem.
Hope this helps!
Adding the elements is perfectly fine you just have to take mod(%) of the intermediate aggregate when calculating the sum of the elements and the expected sum. For the mod operation you can use something like 2n. You also have to fix the value after substraction.

Resources