mirror of
https://github.com/kuhyx/testsAndMisc-archive.git
synced 2026-07-04 11:43:13 +02:00
feat: optimized isbn number calculations
This commit is contained in:
parent
5a8be96f94
commit
1d3e9750e7
@ -3,6 +3,9 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_ISBN_CPP
|
||||
#define CHECK_ISBN_CPP
|
||||
@ -68,35 +71,111 @@ bool checkISBN(const std::vector<int> isbn) {
|
||||
return !(sum % CHECK_NUMBER);
|
||||
}
|
||||
|
||||
std::vector<int> intToVector(unsigned long long int number) {
|
||||
std::vector<int> numbers;
|
||||
while (number > 0) {
|
||||
numbers.push_back(number % 10);
|
||||
number /= 10;
|
||||
}
|
||||
std::reverse(numbers.begin(), numbers.end());
|
||||
// Optimisation 1 – no heap allocation: intToVector() removed; checkAll()
|
||||
// uses only stack variables in the nested loops below.
|
||||
//
|
||||
// Optimisation 2 – algorithmic reduction (10^10 → 10^9 iterations):
|
||||
// ISBN-10 validity: 10*d0 + 9*d1 + ... + 2*d8 + 1*d9 ≡ 0 (mod 11)
|
||||
// Given a 9-digit prefix d0..d8 the check digit is determined:
|
||||
// d9 = (11 - S%11) % 11 where S = weighted sum of prefix
|
||||
// The number is valid iff d9 ≤ 9 (i.e. d9 ≠ 10).
|
||||
//
|
||||
// Optimisation 3 – incremental partial sums + OpenMP:
|
||||
// Each loop level accumulates its contribution once, not inside the
|
||||
// innermost body. The outermost loop is parallelised with OpenMP.
|
||||
//
|
||||
// Optimisation 4 – separated I/O:
|
||||
// File writing is done in a second serial pass with a large buffer so
|
||||
// it doesn't contend with (or race against) the parallel counting pass.
|
||||
long long countISBNs() {
|
||||
long long sum = 0;
|
||||
|
||||
return numbers;
|
||||
}
|
||||
|
||||
int checkAll() {
|
||||
int sum = 0;
|
||||
std::ofstream file;
|
||||
file.open("ISBN.txt");
|
||||
for (unsigned long long int i = HIGHEST_ISBN; i >= 1; i--) {
|
||||
// if(DEBUG) std::cout << i << std::endl;
|
||||
if (checkISBN(intToVector(i))) {
|
||||
++sum;
|
||||
file << std::to_string(i) << "\n";
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for reduction(+ : sum) schedule(static)
|
||||
#endif
|
||||
for (int d0 = 0; d0 <= 9; d0++) {
|
||||
int s0 = 10 * d0;
|
||||
for (int d1 = 0; d1 <= 9; d1++) {
|
||||
int s1 = s0 + 9 * d1;
|
||||
for (int d2 = 0; d2 <= 9; d2++) {
|
||||
int s2 = s1 + 8 * d2;
|
||||
for (int d3 = 0; d3 <= 9; d3++) {
|
||||
int s3 = s2 + 7 * d3;
|
||||
for (int d4 = 0; d4 <= 9; d4++) {
|
||||
int s4 = s3 + 6 * d4;
|
||||
for (int d5 = 0; d5 <= 9; d5++) {
|
||||
int s5 = s4 + 5 * d5;
|
||||
for (int d6 = 0; d6 <= 9; d6++) {
|
||||
int s6 = s5 + 4 * d6;
|
||||
for (int d7 = 0; d7 <= 9; d7++) {
|
||||
int s7 = s6 + 3 * d7;
|
||||
for (int d8 = 0; d8 <= 9; d8++) {
|
||||
int d9 = (11 - (s7 + 2 * d8) % 11) % 11;
|
||||
if (d9 <= 9)
|
||||
++sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file << "There are " << sum << " valid ISBN numbers\n";
|
||||
file.close();
|
||||
return sum;
|
||||
}
|
||||
|
||||
void writeISBNsToFile() {
|
||||
static const int BUF = 1 << 20; // 1 MB write buffer
|
||||
std::ofstream file;
|
||||
file.rdbuf()->pubsetbuf(nullptr, BUF);
|
||||
file.open("ISBN.txt");
|
||||
long long written = 0;
|
||||
|
||||
for (int d0 = 0; d0 <= 9; d0++) {
|
||||
int s0 = 10 * d0;
|
||||
for (int d1 = 0; d1 <= 9; d1++) {
|
||||
int s1 = s0 + 9 * d1;
|
||||
for (int d2 = 0; d2 <= 9; d2++) {
|
||||
int s2 = s1 + 8 * d2;
|
||||
for (int d3 = 0; d3 <= 9; d3++) {
|
||||
int s3 = s2 + 7 * d3;
|
||||
for (int d4 = 0; d4 <= 9; d4++) {
|
||||
int s4 = s3 + 6 * d4;
|
||||
for (int d5 = 0; d5 <= 9; d5++) {
|
||||
int s5 = s4 + 5 * d5;
|
||||
for (int d6 = 0; d6 <= 9; d6++) {
|
||||
int s6 = s5 + 4 * d6;
|
||||
for (int d7 = 0; d7 <= 9; d7++) {
|
||||
int s7 = s6 + 3 * d7;
|
||||
for (int d8 = 0; d8 <= 9; d8++) {
|
||||
int s = s7 + 2 * d8;
|
||||
int d9 = (11 - s % 11) % 11;
|
||||
if (d9 <= 9) {
|
||||
long long isbn = (long long)d0 * 1000000000LL +
|
||||
d1 * 100000000 + d2 * 10000000 +
|
||||
d3 * 1000000 + d4 * 100000 + d5 * 10000 +
|
||||
d6 * 1000 + d7 * 100 + d8 * 10 + d9;
|
||||
file << isbn << '\n';
|
||||
++written;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file << "There are " << written << " valid ISBN numbers\n";
|
||||
file.close();
|
||||
}
|
||||
|
||||
int main() {
|
||||
checkAll();
|
||||
long long count = countISBNs();
|
||||
std::cout << "There are " << count << " valid ISBN numbers\n";
|
||||
writeISBNsToFile();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BIN
CPP/miscelanious/howManyValidISBNNumbersAreThere/bench_opt
Executable file
BIN
CPP/miscelanious/howManyValidISBNNumbersAreThere/bench_opt
Executable file
Binary file not shown.
@ -0,0 +1,90 @@
|
||||
// Optimized ISBN-10 counter.
|
||||
//
|
||||
// Three improvements over the original:
|
||||
//
|
||||
// 1. NO HEAP ALLOCATION — original calls intToVector() on every iteration,
|
||||
// which does a heap alloc+free (std::vector) for each of 10^10 numbers.
|
||||
// Here we use only stack variables.
|
||||
//
|
||||
// 2. ALGORITHMIC REDUCTION — ISBN-10 validity condition:
|
||||
// 10*d0 + 9*d1 + ... + 2*d8 + 1*d9 ≡ 0 (mod 11)
|
||||
// Given any 9-digit prefix d0..d8 we can solve for d9 directly:
|
||||
// d9 = (11 - S mod 11) mod 11 where S = sum of weighted prefix
|
||||
// The number is valid iff 0 ≤ d9 ≤ 9 (i.e. d9 ≠ 10).
|
||||
// → Only 10^9 outer iterations instead of 10^10.
|
||||
//
|
||||
// 3. INCREMENTAL WEIGHTED SUM — instead of multiplying each digit by its
|
||||
// weight inside the innermost loop, partial sums are maintained
|
||||
// incrementally as digits change, so the innermost body is almost free.
|
||||
// The compiler can also vectorise the innermost digit loop.
|
||||
//
|
||||
// Compile: g++ -O2 -fopenmp bench_optimized.cpp -o bench_opt
|
||||
// (OpenMP is optional; remove -fopenmp if not available – it just runs
|
||||
// the outer loop on a single thread.)
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
long long count = 0;
|
||||
|
||||
// Nested loops over the 9 prefix digits.
|
||||
// s_k = partial weighted sum up through digit k.
|
||||
// weight for digit at position i (0-based, MSB first) = 10 - i
|
||||
// so outer digit d0 has weight 10, d1 has weight 9, ..., d8 has
|
||||
// weight 2.
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for reduction(+ : count) schedule(static)
|
||||
#endif
|
||||
for (int d0 = 0; d0 <= 9; d0++) {
|
||||
int s0 = 10 * d0;
|
||||
for (int d1 = 0; d1 <= 9; d1++) {
|
||||
int s1 = s0 + 9 * d1;
|
||||
for (int d2 = 0; d2 <= 9; d2++) {
|
||||
int s2 = s1 + 8 * d2;
|
||||
for (int d3 = 0; d3 <= 9; d3++) {
|
||||
int s3 = s2 + 7 * d3;
|
||||
for (int d4 = 0; d4 <= 9; d4++) {
|
||||
int s4 = s3 + 6 * d4;
|
||||
for (int d5 = 0; d5 <= 9; d5++) {
|
||||
int s5 = s4 + 5 * d5;
|
||||
for (int d6 = 0; d6 <= 9; d6++) {
|
||||
int s6 = s5 + 4 * d6;
|
||||
for (int d7 = 0; d7 <= 9; d7++) {
|
||||
int s7 = s6 + 3 * d7;
|
||||
// Innermost: vary d8, weight 2.
|
||||
for (int d8 = 0; d8 <= 9; d8++) {
|
||||
int s = s7 + 2 * d8;
|
||||
// Required check digit (weight 1):
|
||||
int d9 = (11 - s % 11) % 11;
|
||||
if (d9 <= 9)
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
double elapsed = std::chrono::duration<double>(end - start).count();
|
||||
|
||||
std::cout << "Valid ISBNs: " << count << "\n";
|
||||
std::cout << "Time: " << elapsed << " s\n";
|
||||
|
||||
#ifdef _OPENMP
|
||||
std::cout << "Threads: " << omp_get_max_threads() << "\n";
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
CPP/miscelanious/howManyValidISBNNumbersAreThere/bench_orig
Executable file
BIN
CPP/miscelanious/howManyValidISBNNumbersAreThere/bench_orig
Executable file
Binary file not shown.
@ -0,0 +1,69 @@
|
||||
// Benchmark version of the original algorithm.
|
||||
// File I/O removed so we measure pure computation time.
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
const int ISBN_LENGTH = 10;
|
||||
const int CHECK_NUMBER = 11;
|
||||
const unsigned long long int HIGHEST_ISBN = 9999999999ULL;
|
||||
|
||||
bool checkISBN(const std::vector<int> isbn) {
|
||||
int sum = 0, t = 0;
|
||||
for (int i = 0; i < ISBN_LENGTH; i++) {
|
||||
t += isbn[i];
|
||||
sum += t;
|
||||
}
|
||||
return !(sum % CHECK_NUMBER);
|
||||
}
|
||||
|
||||
std::vector<int> intToVector(unsigned long long int number) {
|
||||
std::vector<int> numbers;
|
||||
while (number > 0) {
|
||||
numbers.push_back(number % 10);
|
||||
number /= 10;
|
||||
}
|
||||
std::reverse(numbers.begin(), numbers.end());
|
||||
return numbers;
|
||||
}
|
||||
|
||||
// Run for at most SAMPLE seconds, then extrapolate total time.
|
||||
static constexpr double SAMPLE_SECS = 20.0;
|
||||
|
||||
long long checkAllTimed(double &elapsed_out) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto limit = start + std::chrono::duration<double>(SAMPLE_SECS);
|
||||
long long sum = 0;
|
||||
unsigned long long i;
|
||||
for (i = HIGHEST_ISBN; i >= 1; i--) {
|
||||
if (checkISBN(intToVector(i)))
|
||||
++sum;
|
||||
|
||||
// Check wall-clock every 1 million iterations to keep overhead low.
|
||||
if ((i & 0xFFFFF) == 0) {
|
||||
if (std::chrono::high_resolution_clock::now() >= limit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
elapsed_out = std::chrono::duration<double>(end - start).count();
|
||||
|
||||
unsigned long long done = HIGHEST_ISBN - i;
|
||||
double rate = (double)done / elapsed_out; // numbers/s
|
||||
double total_est = (double)HIGHEST_ISBN / rate;
|
||||
|
||||
std::cout << "Iterated: " << done << " numbers in " << elapsed_out
|
||||
<< " s\n";
|
||||
std::cout << "Rate: " << (long long)rate << " numbers/s\n";
|
||||
std::cout << "Estimated total time for full range: " << (long long)total_est
|
||||
<< " s (" << total_est / 60.0 << " min)\n";
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main() {
|
||||
double elapsed = 0.0;
|
||||
long long count = checkAllTimed(elapsed);
|
||||
std::cout << "Valid ISBNs in sampled range: " << count << "\n";
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user