Making a factorial program faster? - c++

I've been trying to submit this to a website with programming lessons, but the judge keeps telling me that this program takes too long to execute :/
Problem Statement:
Write a program that reads a non-negative integer n from the standard input, will count the digit of tens and the digit of ones in the decimal notation of n!, and will write the result to the standard output. In the first line of input there is one integer D (1≤D≤30), denoting the number of cases to be considered. For each case of entry. your program should print exactly two digits on a separate line (separated by a single space): the tens digit and the ones digit of n! in the decimal system.
Input/Output:
Input
Output
2
1
0 1
4
2 4
#include <iostream>
using namespace std;
int d,n;
int main()
{
cin>>d;
for(int i=0; i<d; i++)
{
cin>>n;
int silnia = 1;
for(int j=n; j>1; j--)
{
silnia=silnia*j;
}
if(silnia == 1) cout<<0<<" "<<silnia<<"\n";
else cout<<(silnia/10)%10<<" "<<silnia%10<<"\n";
}
return 0;
}

You can get rid of inner loop since n! == (n - 1)! * n:
cin >> d;
int factorial = 1;
cout << 0 << " " << 1 << "\n";
for (int i = 1; i < d; ++i) {
/* we operate with last two disgits: % 100 */
factorial = (factorial * i) % 100;
cout << factorial / 10 << " " << factorial % 10 << "\n";
}
Edit: Another issue is with
silnia=silnia*j;
line. Factorial grows fast:
13! = 6227020800 > LONG_MAX (2147483647)
that's why we should use modulo arithmetics: we keep not factorial itself (which can be very large), but its two last digits (note % 100), which is garanteed to be in 00..99 range:
factorial = (factorial * i) % 100;
Or even (if i can be large)
factorial = (factorial * (i % 100)) % 100;

Since only the last 2 digits of n! are needed, any n >= 10** will have a n! with 00 as the last 2 digits.
A short-cut is to test n: This takes the problem from O(n) to O(1).
int factorial = 0;
if (n < 10) {
int factorial = 1;
for(int j=n; j>1; j--)
{
factorial *= j;
}
factorial %= 100;
}
Or use a look-up table for n in the [0...10) range to drop the for loop.
---
** 10_or_more! has a 2 * 5 * 10 * other factors in it. All these factorials then end with 00.

Related

Best way to find 'quite good' numbers up to 1 million?

I am working on an assignment involving 'quite good' numbers. The task describes them as:
"A "quite good" number is an integer whose "badness" – the size of the difference between the sum of its divisors and the number itself – is not greater than a specified value. For example, if the maximum badness is set at 3, there are 12 "quite good" numbers less than 100: 2, 3, 4, 6, 8, 10, 16, 18, 20, 28, 32, and 64; Your task is to write a C++ program, quitegood, that determines numbers of a specified maximum badness that are less than a specified value. The limiting value and maximum badness are specified as command-line arguments when the program is executed."
The task asks me to write a program that prints perfect numbers with a specified badness limit up to a million. So, the command line argument of quitegood 1000000
1 should print 2 4 6 8 16 28 32 64 128 256 496 512 1024 2048 4096 8128 8192 16384 32768 65536 131072
262144 524288.
I have gotten this to work with the following code
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
const int limit = argc > 1 ? atoi(argv[1]) : 1000000;
const int badness = argc > 2 ? atoi(argv[2]) : 10;
for(int number = 2; number < limit; number++) {
int sum = 1;
for (int factor = 2; factor < number; factor++){
if (number % factor == 0) {
sum += factor;
}
}
if (number >= (sum - badness) && number <= (sum + badness)) {
cout << number << " ";
}
}
return 0;
}
The only issue is that this code is far too slow finding the 'quite good' numbers up to 1 million. Is there any way of optimising this?
Thank you
If f is a factor of n then so is n/f (although when f is the square-root of n, f and n/f are the same factor). So you can make the code a lot faster by counting factors only up to sqrt(number), and then when you find one also include the matching factor number/factor (except for the square-root case).
for (int factor = 2; factor * factor <= number; factor++){
if (number % factor == 0) {
sum += factor;
if (factor * factor != number) {
sum += number / factor;
}
}
}
This code runs in 1.554s on my machine in the case of limit being 1 million, and badness 1. I got bored after several minutes waiting for the original code to complete.
To make the code even faster, you can find the prime factorization of the number, and use the formula for the sum of the divisors based on the prime factorization.
Even without pre-computing the primes, using this method runs in 0.713s on my machine. Here's my code to compute sum from number:
int n = number;
int i = 2;
while (n > 1) {
if (i * i > n) {
sum *= (n + 1);
break;
}
int pp = i;
while (n % i == 0) {
pp *= i;
n /= i;
}
sum *= (pp - 1) / (i - 1);
i += 1;
}
sum -= number;
It finds all prime powers that divide number, and for each p^m multiplies sum by (p^(m+1) - 1) / (p - 1). Like the first solution, it stops early, when i*i > n, which at that point means n is a prime.
It's a lot faster than the first solution in the average case, because although we're still doing trial division, n gets smaller as prime factors are found.
If you have precomputed a large enough list of primes (that is, it includes at least one larger than the square root of limit), you can be a little more efficient again in computing sum:
int n = number;
for (int i = 0; primes[i] * primes[i] <= n; ++i) {
int pp = primes[i];
while (n % primes[i] == 0) {
pp *= primes[i];
n /= primes[i];
}
sum *= (pp - 1) / (primes[i] - 1);
}
if (n > 1) sum *= (n + 1);
sum -= number;
The code with this way of computing sum runs in 0.189s on my machine.

Speed problem for summation (sum of divisors)

I should implement this summation in C ++. I have tried with this code, but with very high numbers up to 10 ^ 12 it takes too long.
The summation is:
For any positive integer k, let d(k) denote the number of positive divisors of k (including 1 and k itself).
For example, for the number 4: 1 has 1 divisor, 2 has two divisors, 3 has two divisors, and 4 has three divisors. So the result would be 8.
This is my code:
#include <iostream>
#include <algorithm>
using namespace std;
int findDivisors(long long n)
{
int c=0;
for(int j=1;j*j<=n;j++)
{
if(n%j==0)
{
c++;
if(j!=(n/j))
{
c++;
}
}
}
return c;
}
long long compute(long long n)
{
long long sum=0;
for(int i=1; i<=n; i++)
{
sum += (findDivisors(i));
}
return sum;
}
int main()
{
int n, divisors;
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
cin >> n;
cout << compute(n);
}
I think it's not just a simple optimization problem, but maybe I should change the algorithm entirely.
Would anyone have any ideas to speed it up? Thank you.
largest_prime_is_463035818's answer shows an O(N) solution, but the OP is trying to solve this problem
with very high numbers up to 1012.
The following is an O(N1/2) algorithm, based on some observations about the sum
n/1 + n/2 + n/3 + ... + n/n
In particular, we can count the number of terms with a specific value.
Consider all the terms n/k where k > n/2. There are n/2 of those and all are equal to 1 (integer division), so that their sum is n/2.
Similar considerations hold for the other dividends, so that we can write the following function
long long count_divisors(long long n)
{
auto sum{ n };
for (auto i{ 1ll }, k_old{ n }, k{ n }; i < k ; ++i, k_old = k)
{ // ^^^^^ it goes up to sqrt(n)
k = n / (i + 1);
sum += (k_old - k) * i;
if (i == k)
break;
sum += k;
}
return sum;
}
Here it is tested against the O(N) algorithm, the only difference in the results beeing the corner cases n = 0 and n = 1.
Edit
Thanks again to largest_prime_is_463035818, who linked the Wikipedia page about the divisor summatory function, where both an O(N) and an O(sqrt(N)) algorithm are mentioned.
An implementation of the latter may look like this
auto divisor_summatory(long long n)
{
auto sum{ 0ll };
auto k{ 1ll };
for ( ; k <= n / k; ++k )
{
sum += n / k;
}
--k;
return 2 * sum - k * k;
}
They also add this statement:
Finding a closed form for this summed expression seems to be beyond the techniques available, but it is possible to give approximations. The leading behavior of the series is given by
D(x) = xlogx + x(2γ - 1) + Δ(x)
where γ is the Euler–Mascheroni constant, and the error term is Δ(x) = O(sqrt(x)).
I used your brute force approach as reference to have test cases. The ones I used are
compute(12) == 35
cpmpute(100) == 482
Don't get confused by computing factorizations. There are some tricks one can play when factorizing numbers, but you actually don't need any of that. The solution is a plain simple O(N) loop:
#include <iostream>
#include <limits>
long long compute(long long n){
long long sum = n+1;
for (long long i=2; i < n ; ++i){
sum += n/i;
}
return sum;
}
int main()
{
std::cout << compute(12) << "\n";
std::cout << compute(100) << "\n";
}
Output:
35
482
Why does this work?
The key is in Marc Glisse's comment:
As often with this kind of problem, this sum actually counts pairs x,
y where x divides y, and the sum is arranged to count first all x
corresponding to a fixed y, but nothing says you have to keep it that
way.
I could stop here, because the comment already explains it all. Though, if it didn't click yet...
The trick is to realize that it is much simpler to count divisors of all numbers up to n rather than n-times counting divisors of individual numbers and take the sum.
You don't need to care about factorizations of eg 123123123 or 52323423 to count all divisors up to 10000000000. All you need is a change of perspective. Instead of trying to factorize numbers, consider the divisors. How often does the divisor 1 appear up to n? Simple: n-times. How often does the divisor 2 appear? Still simple: n/2 times, because every second number is divisible by 2. Divisor 3? Every 3rd number is divisible by 3. I hope you can see the pattern already.
You could even reduce the loop to only loop till n/2, because bigger numbers obviously appear only once as divisor. Though I didn't bother to go further, because the biggest change is from your O(N * sqrt(N)) to O(N).
Let's start off with some math and reduce the O(n * sq(n)) factorization to O(n * log(log(n))) and for counting the sum of divisors the overall complexity is O(n * log(log(n)) + n * n^(1/3)).
For instance:
In Codeforces himanshujaju explains how we can optimize the solution of finding divisors of a number.
I am simplifying it a little bit.
Let, n as the product of three numbers p, q, and r.
so assume p * q * r = n, where p <= q <= r.
The maximum value of p = n^(1/3).
Now we can loop over all prime numbers in a range [2, n^(1/3)]
and try to reduce the time complexity of prime factorization.
We will split our number n into two numbers x and y => x * y = n.
And x contains prime factors up to n^(1/3) and y deals with higher prime factors greater than n^(1/3).
Thus gcd(x, y) = 1.
Now define F(n) as the number of prime factors of n.
From multiplicative rules, we can say that
F(x * y) = F(x) * F(y), if gcd(x, y) = 1.
For finding F(n) => F(x * y) = F(x) * F(y)
So first find F(x) then F(y) will F(n/x)
And there will 3 cases to cover for y:
1. y is a prime number: F(y) = 2.
2. y is the square of a prime number: F(y) = 3.
3. y is a product of two distinct prime numbers: F(y) = 4.
So once we are done with finding F(x) and F(y), we are also done with finding F(x * y) or F(n).
In Cp-Algorithm there is also a nice explanation of how to count the number of divisors on a number. And also in GeeksForGeeks a nice coding example of how to count the number of divisors of a number in an efficient way. One can check the articles and can generate a nice solution to this problem.
C++ implementation
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 11;
bool prime[maxn];
bool primesquare[maxn];
int table[maxn]; // for storing primes
void SieveOfEratosthenes()
{
for(int i = 2; i < maxn; i++){
prime[i] = true;
}
for(int i = 0; i < maxn; i++){
primesquare[i] = false;
}
// 1 is not a prime number
prime[1] = false;
for(int p = 2; p * p < maxn; p++){
// If prime[p] is not changed, then
// it is a prime
if(prime[p] == true){
// Update all multiples of p
for(int i = p * 2; i < maxn; i += p){
prime[i] = false;
}
}
}
int j = 0;
for(int p = 2; p < maxn; p++) {
if (prime[p]) {
// Storing primes in an array
table[j] = p;
// Update value in primesquare[p * p],
// if p is prime.
if(p < maxn / p) primesquare[p * p] = true;
j++;
}
}
}
// Function to count divisors
int countDivisors(int n)
{
// If number is 1, then it will have only 1
// as a factor. So, total factors will be 1.
if (n == 1)
return 1;
// ans will contain total number of distinct
// divisors
int ans = 1;
// Loop for counting factors of n
for(int i = 0;; i++){
// table[i] is not less than cube root n
if(table[i] * table[i] * table[i] > n)
break;
// Calculating power of table[i] in n.
int cnt = 1; // cnt is power of prime table[i] in n.
while (n % table[i] == 0){ // if table[i] is a factor of n
n = n / table[i];
cnt = cnt + 1; // incrementing power
}
// Calculating the number of divisors
// If n = a^p * b^q then total divisors of n
// are (p+1)*(q+1)
ans = ans * cnt;
}
// if table[i] is greater than cube root of n
// First case
if (prime[n])
ans = ans * 2;
// Second case
else if (primesquare[n])
ans = ans * 3;
// Third case
else if (n != 1)
ans = ans * 4;
return ans; // Total divisors
}
int main()
{
SieveOfEratosthenes();
int sum = 0;
int n = 5;
for(int i = 1; i <= n; i++){
sum += countDivisors(i);
}
cout << sum << endl;
return 0;
}
Output
n = 4 => 8
n = 5 => 10
Complexity
Time complexity: O(n * log(log(n)) + n * n^(1/3))
Space complexity: O(n)
Thanks, #largest_prime_is_463035818 for pointing out my mistake.

How can we find big O notation for trailing zeros in factorial?

I know how to calculate big O notation for factorial but I am having difficulty combining both notations.
this is the code for calculating trailing zeros.
using namespace std;
// Function to return trailing
// 0s in factorial of n
int findTrailingZeros(int n)
{
// Initialize result
int count = 0;
// Keep dividing n by powers of
// 5 and update count
for (int i = 5; n / i >= 1; i *= 5)
count += n / i;
return count;
}
// Driver Code
int main()
{
int n = 100;
cout << "Count of trailing 0s in " << 100
<< "! is " << findTrailingZeros(n);
return 0;
}
The complexity is O(log(n)). Its easy to see if you plot the number of iterations for each n:
n iterations
------ -----------
< 5 0
< 25 1
< 125 2
< 625 3
< 3125 4
To be precise it should be
O(1 - log5(n)) = O(log5(n)) where n is the number whose factorial is to be determined.
5,5^2,5^3....5^k.
at last
5^k<=n(given in for loop)
so k<=log5(n)
so time complexity is thetha(logn)

How to find the Nth number in a fractal sequence?

The assignment is to write a C++ program which takes the input number n and outputs the nth number in the sequence:
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6 ...
This is what I've come up with so far:
#include <iostream>
using namespace std;
int main()
{
long long n,k=1,result;
cin >> n;
if(n==1){
result=1;
}else{
for(int i=1,j=1;;i=j,j=j+k){
if(n>i&&n<=j){
result=n-i;
break;
}else{
k++;
}
}
}
cout << result << endl;
}
This is also what I've written before:
#include <iostream>
using namespace std;
int main()
{
long long n,count=0,result;
cin >> n;
for(int i=1;;i++){
for(int j=1;j<=i;j++){
count=count+1;
if(count==n){
result=j;
break;
}
}
if(count>=n){
break;
}
}
cout << result << endl;
}
Both of these work properly for smaller numbers, but the problem is I have to follow the constraint:
1 <= n <= 10^12
So when bigger numbers are inputted, the programs both take too long to output the solution and exceed the time limit, which is 2 seconds. I've been working on this for 5 hours now and I don't know how to improve these programs so they are faster. I also thought about a certain formula that could help determine the nth number in such a sequence, but I can't seem to find anything about it on the internet or in my math books. Could somebody point me to the solution? I would be very grateful.
We can group numbers in your sequence:
(1) (1, 2) (1, 2, 3) ...
The overall amount of numbers is
1 + 2 + 3 + ...
The latter is an arithmetic progression, its sum equals to x*(x+1)/2.
We'll find the number of full groups k that go before n+1-th number in the sequence. k equals to the maximal integer such that k*(k+1)/2 <= n. To find it we'll solve the quadratic equation:
x*(x+1)/2 = n
x^2 + x - 2*n = 0
Let's assume that positive root of this equation is x'. We round it down to the nearest integer k. If x' == k (x' is a whole number) it is the answer. Otherwise, the answer is n - k*(k+1)/2.
Exemplary c++ implementation:
double d = 1 + 8.0 * n;
double x = (-1 + sqrt(d)) / 2;
long long k = floor(x);
long long m = k*(k+1) / 2;
if (m == n) {
return k;
} else {
return n - m;
}
The solution has O(1) time complexity.
The first job is to write out the sequence like this:
1
2 3
4 5 6
7 8 9 10
And note that we want to map this to
1
1 2
1 2 3
1 2 3 4
The row position of a number is given by rearranging the formula for an arithmetic progression, solving the resultant quadratic, discarding the negative root, and removing any fractional part of the answer. A number t appears in the row r given by the whole number part
r = R(1/2 + (1/4 + 2 * (t - 1))1/2)
Where R() is a function that rounds a number downwards to the whole number.
But you are after the column c. That is obtained subtracting the value of the first term in that row from t:
c = t - 1/2 * r * (r - 1)
Reference: https://en.wikipedia.org/wiki/Arithmetic_progression
A solution using loop. It will out the number at nth.
x = 0 ;
i = 1 ;
do {
x += i ;
if( x == n ) {
cout<< i ;
break ;
}
else if( x > n ) {
cout<< (n - (x-i)) ;
break ;
}
i ++ ;
}while( 1) ;

Print sum to n numbers

Here is my question...
Input a number n from the user. The program should output the sum of all numbers from 1 to n NOT including the multiples of 5.
For example if the user inputs 13 then the program should compute and print the sum of the numbers: 1 2 3 4 6 7 8 9 11 12 13 (note 5,10 are not included in the sum)
i have made the following program but it is not working..
can any one help me THANK YOU in advance...
#include <iostream>
using namespace std;
int main()
{
int inputnumber = 0;
int sum = 0;
int count= 1;
cout<<"Enter the number to print the SUM : ";
cin>>inputnumber;
while(count<=inputnumber)
{
if (count % 5!=0)
{
sum = sum + count;
}
} count = count +1;
cout<<"the sum of the numbers are : "<<sum;
}
You should increment count inside the loop, not outside it:
while(count<=inputnumber)
{
if (count % 5!=0)
{
sum = sum + count;
}
count = count +1; // here
}
Note, by the way, that using a for loop would be much more convenient here. Additionally, sum = sum + count could be shorthanded to sum += count.
for (int count = 1; count <= inputnumber; ++count)
{
if (count % 5 != 0)
{
sum += count;
}
}
You need to put the count+1 inside your while loop. also add !=0 tou your if statement.
while(count<=inputnumber)
{
if (count % 5!=0)
{
sum = sum + count;
}
count = count +1;
}
No need to use a loop at all:
The sum 1..n is
n * (n+1) / 2;
the sum of the multiples of 5 not above n is
5 * m * (m+1) / 2
where m = n/5 (integer devision). The result is therefore
n * (n+1) / 2 - 5 * m * (m+1) / 2
Try this..
In my condition,checks n value is not equal to zero and % logic
int sum = 0;
int n = 16;
for(int i=0 ; i < n ;i++) {
if( i%5 != 0){
sum += i;
}
}
System.out.println(sum);
Let's apply some maths. We'll use a formula that allows us to sum an arithmetic progression. This will make the program way more efficient with bigger numbers.
sum = n(a1+an)/2
Where sum is the result, n is the inpnum, a1 is the first number of the progression and an is the place that ocuppies n (the inpnum) in the progression.
So what I have done is calculate the sum of all the numbers from 1 to inpnum and then substract the sum of all the multiples of 5 from 5 to n.
#include <iostream>
using namespace std;
int main (void)
{
int inpnum, quotient, sum;
cout << "Enter the number to print the SUM : ";
cin >> inpnum;
// Finds the amount of multiples of 5 from 5 to n
quotient = inpnum/5;
// Sum from 1 to n // Sum from 5 to n of multiples of 5
sum = (inpnum*(1+inpnum))/2 - (quotient*(5+(quotient)*5))/2;
cout << "The sum of the numbers is: " << sum;
}
thanks every one but the problem is solved . the mistake was very small. i forget to write "()" in if condition.
#include <iostream>
using namespace std;
int main()
{
int inputnumber = 0;
int sum = 0;
int count= 1;
cout<<"Enter the number to print the SUM : ";
cin>>inputnumber;
while(count<=inputnumber)
{
if ((count % 5)!=0)//here the ()..
{
sum = sum + count;
}
count = count +1;
}
cout<<"the sum of the numbers are : "<<sum;
}