The first bug is as AudioDroid stated that the global variable i is being changed by two functions, namely main and prime_numbers. This can be fixed by defining a local variable let's say k and looping through with it:
int k;
for (k = 2; k <= sqrt(n); k++) {
if (n % k == 0) return 0;
}
The second bug is that you wrote
if (j > sqrt(a[i]) && count_divisors != 0) count1++;
inside the inner for loop body and thus it will never be executed. It has to be taken out of there. Fix these two bugs and you will get your 5 as an answer.
However in case you have q as big as let's say 10^6 or more this solution of yours will not be very effective because for every divisor d you are looping through the range [2; sqrt(d)] instead of generating all prime numbers up to a given limit in a lookup object and just checking whether the particular divisor is a prime number or not in O(1) time.
#include <memory>
#include <bitset>
#include <iostream>
#include <vector>
int const lim = 1000000;
bool is_future(int const num, std::unique_ptr<std::bitset<lim + 1>> const& is_prime) {
int div, cnt = 0;
for (div = 2; div * div <= num; ++div) {
if (num % div == 0) {
++cnt;
if (!is_prime->test(div)) {
return false;
}
if (div * div != num) {
++cnt;
if (!is_prime->test(num / div)) {
return false;
}
}
}
}
return cnt != 0;
}
int main() {
auto sieve = std::make_unique<std::bitset<lim + 1>>();
sieve->set();
sieve->set(0, false);
sieve->set(1, false);
int i, j;
for (i = 2; i * i <= lim; ++i) {
if (sieve->test(i)) {
for (j = i * i; j <= lim; j += i) {
sieve->set(j, false);
}
}
}
int q;
std::cin >> q;
std::vector<int> seq;
seq.reserve(q);
int k, num;
for (k = 0; k != q; ++k) {
std::cin >> num;
seq.emplace_back(num);
}
int ans = 0;
for (auto const& elem : seq) {
if (is_future(elem, sieve)) {
++ans;
}
}
std::cout << ans << '\n';
return 0;
}
Set your lim constant to be high enough according to the maximum value of the number in the input. Allocate bitset on the heap via the unique_ptr sieve. Run the sieve of Eratosthenes algorithm on sieve up to lim. Read q. Define the vector seq. Use reserve to allocate memory for q elements and thus avoid preallocation. Grow seq. Initialize the counter ans with 0. Loop through seq and for every element elem call is_future with elem and sieve. is_future uses is_prime (sieve) as a lookup object to check whether the divisor div or (num/div) is prime or not in O(1) time. The counter cnt counts the number of divisors of num in the range [2; sqrt(num)]. Pay attention that I don't use std::sqrt because it is expensive If called thousands of times. If cnt is 0 the number has no divisors in the specified range so it is not future otherwise it is. If is_future returns true increment ans. Output ans.
@Trương Quang Vinh
seq.reserve(q);
Imagine you have a vector seq that is empty. Its initial capacity is 0 which means that it has allocated a block of memory for 0 integers. If you start growing this vector let's say you want to read 10^6 integers and store them in it it has to do the following operations:
allocate a larger block of memory
copy the integers from the old block to the new one
call the destructors of the integers located on the old block
free the memory that the integers located on the old block used and return it back to the system
I made a test with 10^6 integers and under Microsoft's implementation the capacity of the vector seq changes this way:
0 -> 1 -> 2 -> 3 -> 4 -> 6 -> 9 -> 13 -> 19 -> 28 -> 42 -> 63 -> 94 -> 141 -> 211 -> 316 -> 474 -> 711 -> 1066 -> 1599 -> 2398 -> 3597 -> 5395 -> 8092 -> 12138 -> 18207 -> 27310 -> 40965 -> 61447 -> 92170 -> 138255 -> 207382 -> 311073 -> 466609 -> 699913 -> 1049869
At each of these steps where the vector seq changes its capacity the above-mentioned 4 operations will take place which of course take some time. In your case you know that you are going to store q integers in your vector and with the member function reserve you can directly allocate a block of memory that is large enough to keep them and thus avoid the above-mentioned 4 operations.
seq.emplace_back(num);
The member function emplace_back directly constructs an element from the arguments it is given instead of constructing an element from the copy created from the arguments it is given like push_back does and thus is more effective.
is_prime->test(num / div)
is_prime is an unique_ptr that points to an object of type std::bitset<lim + 1> located on the heap. The bitset class mimics the bits of a number. In your case you are going to run the sieve of Eratosthenes algorithm on this object. Let's say you want to generate all the prime numbers in the range [2; 100]. Your bitset object will initially look like that:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100
The last two bits representing the numbers 0 and 1 are set to 0 because 0 and 1 are not prime numbers. When the algorithm finishes your bitset object will look like that:
00010000000100000100010000010100010000010100000100000100010100010000010100000100010100010100010101100
The function test called via the pointer is_prime with operator-> which is the short form of (*is_prime).test will test whether the bit let's say x is 1 or 0. If it is 1 the number represented by the bit x is prime otherwise it is not.
std::unique_ptr<std::bitset<lim + 1>> const& is_prime
This is the object sieve passed as a reference to const to the function is_future which means that operations done on is_prime are done on sieve with the promise of not changing it because is_prime is a reference to const. If I don't write auto on this line
auto sieve = std::make_unique<std::bitset<lim + 1>>();
I have to write
std::unique_ptr<std::bitset<lim + 1>> sieve = std::make_unique<std::bitset<lim + 1>>();
which is where the type unique_ptr missing from main is coming from.