Jarvis323
- 1,247
- 988
Here's my solution. I also used the Miller-Rabin prime tester from Rosseta Code (which still turns out to be the bottleneck). The times I am getting are,
getting primes took: 1 ms
getting primes pairs took: 286 ms
computing all solution sums took: 5 ms
26033
"computing all solution sums" could potentially be optimized a bit more, i.e. in testing that a newly added prime is also in a pair with each other prime in the current solution. I tried binary search, and unorderd_set (also uses more memory), but it seams the brute force is faster for this part anyway.
Seams like a pretty efficient solution. Most of the time is in testing primes, to construct the list of prime pairs.
1) list prime pairs ##P## in sorted order with no duplicates.
2) given the ##i^{th}## prime. You have pairs in the list (from indices ##start(x_i)##, to ##start(x_j)## ), with ##(x_i, x_j)## the last pair with ##x_i## in the list.
3) pick ##x_i##' = ##P[ start[ x_i ] ] + 1##, as the first candidate to include in a solution using ##x_i##, and recurse, with ##x_i = x_i##'.
4) repeat step 3 until you have a solution (while making sure that each prime picked is in a pair with each previous pick), record it as the new min if it is, go back one level and continue where you left off.
getting primes took: 1 ms
getting primes pairs took: 286 ms
computing all solution sums took: 5 ms
26033
"computing all solution sums" could potentially be optimized a bit more, i.e. in testing that a newly added prime is also in a pair with each other prime in the current solution. I tried binary search, and unorderd_set (also uses more memory), but it seams the brute force is faster for this part anyway.
C:
struct Pair
{
IntType a;
IntType b;
Pair( IntType _a, IntType _b ) : a( _a ), b( _b ) {}
};inline void fillPrimes( vector< IntType > & primes, const IntType n )
{
for( IntType i = 3, j = 0; j < n; ++i )
{
if( prime( i ) )
{
primes[ j++ ] = i;
}
}
}
inline IntType cat(
const IntType a,
const IntType b )
{
return a*( std::pow( static_cast< IntType >( 10 ),
( IntType( std::log10( b ) ) + 1 ) ) ) + b;
}
inline bool catTest( IntType a, IntType b )
{
return prime( cat( a, b ) )
&& prime( cat( b, a ) );
}
inline bool binarySearch(
const IntType v,
const vector< Pair > & pairs,
IntType start,
IntType last )
{
while( start <= last )
{
int64_t m = ( start + last ) / 2;
if( pairs[ m ].b < v )
{
start = m + 1;
}
else if( pairs[ m ].b > v )
{
last = m - 1;
}
else
{
return true;
}
}
return false;
}
pair< bool, IntType > solutionFrom(
vector< IntType > & roots,
IntType & minSum,
const IntType & sum,
const IntType & NPP,
const vector< Pair > & pairs,
const vector< IntType > & starts,
const vector< IntType > & counts,
const vector< IntType > & primePosition,
const vector< IntType > & primes )
{
const IntType root = roots.back();
const IntType level = roots.size();
const IntType NPL = counts[ root ] - ( NPP - level );
for( IntType i = 0; i <= NPL; ++i )
{
IntType subPrime = pairs[ starts[ root ] + i ].b;
IntType tempSum = sum + subPrime;
if( tempSum + subPrime >= minSum )
{
return { false, 0 };
}
bool intersectsAll = true;
for( IntType k = level - 2; k >= 0; --k )
{
bool intersectsSubTree = false;
for( IntType j = 0; j < counts[ roots[ k ] ]
&& subPrime >= pairs[ starts[ roots[ k ] ] + j ].b; ++j )
{
if( pairs[ starts[ roots[ k ] ] + j ].b == subPrime )
{
intersectsSubTree = true;
break;
}
}
if( ! intersectsSubTree )
{
intersectsAll = false;
break;
}
// if( binarySearch(
// subPrime,
// pairs,
// starts[ roots[ k ] ],
// starts[ roots[ k ] ] + counts[ roots[ k ] ] - 1 ) )
// {
// continue;
// }
// intersectsAll = false;
// break;
}
if( ! intersectsAll )
{
continue;
}
if( level == NPP - 1 )
{
return { true, tempSum };
}
else
{
roots.push_back( primePosition[ subPrime / 2 ] );
auto s = solutionFrom(
roots,
minSum,
tempSum,
NPP,
pairs,
starts,
counts,
primePosition,
primes );
if( s.first )
{
minSum = s.second;
}
roots.pop_back();
}
}
return { false, 0 };
}
int main()
{
const IntType MAX_N_PRIMES = 1200;
const IntType NPP = 5;
vector< IntType > primes( MAX_N_PRIMES );
auto t1 = high_resolution_clock::now();
fillPrimes( primes, MAX_N_PRIMES );
cout << "getting primes took: " <<
duration_cast< milliseconds >( high_resolution_clock::now() - t1 ).count()
<< " ms" << endl;
vector< Pair > pairs;
pairs.reserve( MAX_N_PRIMES );
vector< IntType > primePairCounts( MAX_N_PRIMES, 0 );
vector< IntType > primePairStarts( MAX_N_PRIMES, 0 );
vector< IntType > validPairs;
vector< IntType > primePosition( primes.back() / 2, 0 );
t1 = high_resolution_clock::now();
for( IntType i = 0; i < MAX_N_PRIMES; ++i )
{
primePosition[ primes[ i ] / 2 ] = i;
primePairStarts[ i ] = pairs.size();
for( IntType j = i+1; j < MAX_N_PRIMES; ++j )
{
if( catTest( primes[ i ], primes[ j ] ) )
{
pairs.push_back( { primes[ i ], primes[ j ] } );
++primePairCounts[ i ];
}
}
if( primePairCounts[ i ] >= NPP )
{
validPairs.push_back( i );
}
}
cout << "getting primes pairs took: " <<
duration_cast< milliseconds >( high_resolution_clock::now() - t1 ).count()
<< " ms" << endl;
t1 = high_resolution_clock::now();
IntType minSolution = numeric_limits<IntType>::max();
vector< IntType > roots( NPP );
roots.shrink_to_fit();
for( auto root : validPairs )
{
IntType sum = primes[ root ];
roots.clear();
roots.push_back( root );
pair< bool, IntType > p
= solutionFrom(
roots,
minSolution,
sum,
NPP,
pairs,
primePairStarts,
primePairCounts,
primePosition,
primes );
if( p.first )
{
minSolution = min( minSolution, p.second );
}
else
{
continue;
}
}
cout << "computing all solution sums took: " <<
duration_cast< milliseconds >( high_resolution_clock::now() - t1 ).count()
<< " ms" << endl;
cout << minSolution << endl;
}
Seams like a pretty efficient solution. Most of the time is in testing primes, to construct the list of prime pairs.
1) list prime pairs ##P## in sorted order with no duplicates.
2) given the ##i^{th}## prime. You have pairs in the list (from indices ##start(x_i)##, to ##start(x_j)## ), with ##(x_i, x_j)## the last pair with ##x_i## in the list.
3) pick ##x_i##' = ##P[ start[ x_i ] ] + 1##, as the first candidate to include in a solution using ##x_i##, and recurse, with ##x_i = x_i##'.
4) repeat step 3 until you have a solution (while making sure that each prime picked is in a pair with each previous pick), record it as the new min if it is, go back one level and continue where you left off.
Last edited: