As a challenge I decided to do the following problem:
How many different numbers with n digits are there whose digits add up to k?
As this is an embarrassingly parallel problem I decided to use a bit of GPGPU programming (partly for learning purposes) using C++AMP and came up with the following solution:
#include <iostream>
#include <vector>
#include <assert.h>
#include <amp.h>
#include <chrono>
#include <ctime>
#include <numeric>
inline unsigned int AddDigits( unsigned int n ) restrict(amp, cpu)
{
unsigned int sum = 0;
while( n > 0 )
{
sum += n % 10;
n /= 10;
}
return sum;
}
int main()
{
std::chrono::steady_clock clock;
unsigned int iSize, iSumRequired;
std::cout << "Please enter the number of digits: ";
std::cin >> iSize;
std::cout << std::endl << "Please enter the sum required: ";
std::cin >> iSumRequired;
std::cout << std::endl;
unsigned long long iMaxNum = std::pow( 10, iSize );
assert( vecData.max_size() > iMaxNum );
std::vector<unsigned int> vecData( iMaxNum );
std::vector<int> vecNumValid( iMaxNum );
auto tpBegin = clock.now();
std::iota( vecData.begin(), vecData.end(), 1 );
concurrency::array_view<const unsigned int, 1> arrayView( iMaxNum, vecData );
concurrency::array_view<int, 1> numValid( iMaxNum, vecNumValid );
concurrency::parallel_for_each( numValid.extent, [=]( concurrency::index<1> idx ) restrict( amp ) {
numValid[idx] = (AddDigits( arrayView[idx] ) == iSumRequired ? 1 : 0);
} );
numValid.synchronize();
int iNumValid = concurrency::parallel_reduce( vecNumValid.begin(), vecNumValid.end(), 0 );
std::cout << "The number of valid numbers are: " << iNumValid << std::endl;
auto tpTimeTaken = clock.now() - tpBegin;
std::cout << "Time Taken: " << std::chrono::duration_cast<std::chrono::milliseconds>(tpTimeTaken).count() << std::endl;
return 0;
}
This offers a significant improvement over parallel brute-forcing on the CPU (2x speedup versus 8 threads on an 8-core CPU), however out of curiosity I was wondering whether any further speedup could be gained, perhaps through somehow running the parallel_reduce
on the GPU rather than on the CPU; removing the necessity to synchronize the array_view
?
Moreover, this only allows for up to 8-digits (because of the restriction of the maximum value being less than 231-1) because the GPU only supports types of float
, double
, int
, unsigned int
and bool
; is there a way to circumvent this and improve the maximum number of digits allowed?
How many different numbers with n digits are there that add up to k
Do you mean:How many different numbers with n digits are there whose digits add up to k
? Sorry probably my fault for not understanding. \$\endgroup\$