C++ Collatz Conjecture Optimization - c++

In ProjectEuler problem #14, one needs to find the longest Collatz chain, up to 1 million. I found a halfway decent way to do so, however, it feels like I'm just being stupid because I can't find a way to make this code more efficient (the code is supposed to only print out the solution, after it tests 1 to 1 million, but hasn't printed anyting out after 10 minutes). Am I tackling this problem the wrong way, or is there a way to optimize my existing code?
#include <iostream>
using namespace std;
int main()
{
int i;
int x;
int n;
int superN;
int superI;
superN = 0;
superI = 0;
for (i = 1; i <= 1000000; i++) {
x = i;
n = 1;
do {
if (x % 2 == 0) {
x = x / 2;
}
if (x % 2 == 1 && x != 1) {
x = 3 * x + 1;
}
n++;
if (n > superN) {
superN = n;
superI = i;
}
} while (x != 1);
}
cout << "The number " << superI << " ran for " << superN << " terms.";
system("pause");
return 0;
}

You've got a few small problems:
I'm fairly sure that you are overflowing the int data type. Use a uint64_t to make this far less likely.
You should only update superI and superN outside of the while loop. This shouldn't matter, but it hurts performance.
On each iteration you should only modify x once. You currently might modify it twice, which might cause you to fall into an infinite loop. And your calculation of n will be off as well.
Use memoization to improve performance by caching old results.
Applying this, you could come up with some code like this:
#include <cstdint>
#include <iostream>
#include <map>
using namespace std;
int main()
{
uint64_t i;
uint64_t x;
uint64_t n;
uint64_t superN;
uint64_t superI;
std::map<uint64_t, uint64_t> memory;
superN = 0;
superI = 0;
for (i = 1; i <= 1000000; i++) {
x = i;
n = 1;
do {
if (memory.find(x) != memory.end()) {
n += memory[x];
break;
}
if (x % 2 == 0) {
x = x / 2;
} else {
x = 3 * x + 1;
}
n++;
} while (x != 1);
if (n > superN) {
superN = n;
superI = i;
}
memory[i] = n;
}
cout << "The number " << superI << " ran for " << superN << " terms.\n";
system("pause");
return 0;
}
Which takes 4 seconds to output:
The number 837799 ran for 556 terms.

I would suggest not to use memoization as for me it run slower; in my case (up to 10,000,000) the code below is faster without memoization.
the main changes are:
only testing if the current number is even once (not need for an else-if).
using a bitwise operation instead of the modulo operation (slightly faster)
Apart from that I don't know why your code is so long (mine is below 200 milliseconds) maybe you compile as debug ?
bool isEven(uint64_t value)
{
return (!(value & 1));
}
uint64_t solveCollatz(uint64_t start)
{
uint64_t counter = 0;
while (start != 1)
{
if(isEven(start))
{
start /= 2;
}
else
{
start = (3 * start) + 1;
}
counter++;
}
return counter;
}

If you can use compiler intrinsics, particularly with counting and removing trailing zeros, you'll recognize you don't need to branch in the main loop, you'll always alternate odd and even. The memoization techniques that have been previously presented will rarely short-circuit around the math you're doing, since we're dealing with hailstone numbers - additionally, the majority of numbers only have one entry point, so if you see them once, you'll never see them again.
In GCC it'll look something like this:
#include <cstdint>
#include <iostream>
#include <unordered_map>
#include <map>
using namespace std;
using n_iPair = std::pair<uint32_t, uint64_t>;
auto custComp = [](n_iPair a, n_iPair b){
return a.first < b.first;
};
int main()
{
uint64_t x;
uint64_t n;
n_iPair Super = {0,0};
for (auto i = 1; i <= 1000000; i++){
x = i;
n = 0;
if (x % 2 == 0) {
n += __builtin_ctz(x); // account for all evens
x >>= __builtin_ctz(x); // always returns an odd
}
do{ //when we enter we're always working on an odd number
x = 3 * x + 1; // always increases an odd to an even
n += __builtin_ctz(x)+1; // account for both odd and even transfer
x >>= __builtin_ctz(x); // always returns odd
}while (x != 1);
Super = max(Super, {n,i}, custComp);
}
cout << "The number " << Super.second << " ran for " << Super.first << " terms.\n";
return 0;
}

If performance is critical, but memory isn't, you can use caching to improve the speed.
#include <iostream>
#include <chrono>
#include <vector>
#include <sstream>
std::pair<uint32_t, uint32_t> longestCollatz(std::vector<uint64_t> &cache)
{
uint64_t length = 0;
uint64_t number = 0;
for (uint64_t current = 2; current < cache.size(); current++)
{
uint64_t collatz = current;
uint64_t steps = 0;
while (collatz != 1 && collatz >= current)
{
if (collatz % 2)
{
// if a number is odd, then ((collatz * 3) + 1) would result in
// even number, but even number can have even or odd result, so
// we can combine two steps for even number, and increment twice.
collatz = ((collatz * 3) + 1) / 2;
steps += 2;
}
else
{
collatz = collatz / 2;
steps++;
}
}
cache[current] = steps + cache[collatz];
if (cache[current] > length)
{
length = cache[current];
number = current;
}
}
return std::make_pair(number, length);
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();;
uint64_t input = 1000000;
std::vector<uint64_t> cache(input + 1);
auto longest = longestCollatz(cache);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Longest Collatz (index : value) --> " << longest.first << " : " << longest.second;
std::cout << "\nExecution time: " << duration << " milliseconds\n";
return EXIT_SUCCESS;
}

Related

Generate Partition of integer into k different integers (C++)

I have problem with the constraint
I have to partition it to "exact" k "different" integers
e.g. 10 = 1+2+7 -> valid
10 = 2+2+6 -> invalid
I don't want to print them, I want to store them in to vectors or something
I am thinking of recursive solution, but still can't come up with a efficient way to store them...(the solution I found can only print it)
And I think it should store in vector<vector>
the struct should be like this?
Partition(){
....
....
}
Partition_main(){
...
Partition()
....
}
In practice, the problem is to enumerate all the solutions.
This can be done by a DFS.
In the following code, the DFS is implemented with a help of a FIFO (i.e. a std::queue).
The little trick is, for a given candidate at a given step, to calculate the minimum
sum that can be obtained in the next steps.
If this minimum sum is larger than n, then we can stop the research in this direction.
#include <iostream>
#include <vector>
#include <queue>
#include <cassert>
struct Parti {
std::vector<int> v;
int sum = 0;
};
std::vector<std::vector<int>> get_partitions (int n, int k) {
std::vector<std::vector<int>> result;
std::queue<Parti> fifo;
Parti start;
fifo.push(start);
while (!fifo.empty()) {
Parti elt = fifo.front();
fifo.pop();
int sum = elt.sum;
int remain = k-elt.v.size();
if (remain == 1) {
int last = n - sum;
if (k > 1) assert (last > elt.v.back());
elt.v.push_back(last);
result.push_back (elt.v);
continue;
}
int i;
if (elt.v.size() == 0) i = 1;
else i = elt.v.back() + 1;
while (true) {
int min_sum = sum + remain*(2*i + remain - 1)/2;
if (min_sum > n) break;
Parti new_elt = elt;
new_elt.v.push_back(i);
new_elt.sum += i;
fifo.push (new_elt);
++i;
};
}
return result;
}
int main() {
int n = 15;
int k = 4;
auto res = get_partitions (n, k);
if (res.size() == 0) {
std::cout << "no partition possible\n";
return 0;
}
for (const auto& v: res) {
std::cout << n << " = ";
for (int i = 0; i < v.size(); ++i) {
std::cout << v[i];
if (i == v.size()-1) {
std::cout << "\n";
} else {
std::cout << " + ";
}
}
}
return 0;
}

What does "not all control paths return a value" mean and how to troubleshoot. (C++)

I'm trying to create a function for an assignment that finds the two prime numbers that add up to the given sum. The instructions ask
"Write a C++ program to investigate the conjecture by listing all the even numbers from 4 to 100,000 along
with two primes which add to the same number.
Br sure you program the case where you find an even number that cannot be expressed as the sum of two
primes (even though this should not occur!). An appropriate message to display would be “Conjecture
fails!” You can test this code by seeing if all integers between 4 and 100,000 can be expressed as the sum
of two primes. There should be lots of failures."
I have created and tested the "showPrimePair" function before modifying it to integrate it into the main program, but now I run into this specific error
"C4715 'showPrimePair': not all control paths return a value"
I have already done my research to try to fix the error but it still
remains.
#include <iostream>
#include <stdio.h>
//#include <string> // new
//#include <vector> //new
//#include <algorithm>
using namespace std;
bool isPrime(int n);
//bool showPrimePair(int x);
//vector <int> primes; //new
const int MAX = 100000;
//// Sieve Sundaram function // new
//
//void sieveSundaram()
//{
// bool marked[MAX / 2 + 100] = { 0 };
// for (int i = 1; i <= (sqrt(MAX) - 1) / 2; i++)
// for (int j = (i * (i + 1)) << 1; j <= MAX / 2; j = j + 2 * i + 1)
// marked[j] = true;
//
// primes.push_back(2);
// for (int i = 1; i <= MAX / 2; i++)
// if (marked[i] == false)
// primes.push_back(2 * i + 1);
//}
// Function checks if number is prime //links to showPrimePair
bool isPrime(int n) {
bool prime = true;
for (int i = 2; i <= n / 2; i++)
{
if (n % i == 0) // condition for nonprime number
{
prime = false;
break;
}
}
return prime;
}
// Function for showing prime pairs ( in progress) Integer as a Sum of Two Prime Numbers
bool showPrimePair(int n) {
bool foundPair = true;
for (int i = 2; i <= n / 2; ++i)
// condition for i to be a prime number
{
if (isPrime(i) == 1)
{
// condition for n-i to be a prime number
if (isPrime(n - i) == 1)
{
// n = primeNumber1 + primeNumber2
printf("%d = %d + %d\n", n, i, n - i);
foundPair = true;
break;
}
}
}
if (foundPair == false) {
cout << " Conjecture fails!" << endl;
return 0;
}
}
// Main program in listing conjectures for all even numbers from 4-100,000 along q/ 2 primes that add up to same number.
int main()
{
//sieveSundaram();
cout << "Goldbach's Conjecture by Tony Pham " << endl;
for (int x = 2; x <= MAX; x++) {
/*if (isPrime(x) == true) { //works
cout << x << " is a prime number " << endl;
}
else {
cout << x << " is not a prime number " << endl;
}*/
showPrimePair(x);
}
cout << "Enter any character to quit: ";
cin.get();
}
First you can find all prime numbers in the desired range using the Sieve of Eratosthenes algorithm. Next, you can insert all found primes into a hash set. Finally for each number n in the range you can try all primes p that don't exceed n/2, and probe if the n-p is also a prime (as long as you have a hash set this operation is very fast).
Here is an implementation of Dmitry Kuzminov's answer. It takes a minute to run but it does finish within a reasonable time period. (Also, my implementation skips to the next number if a solution is found, but there are multiple solutions for each number. Finding every solution for each number simply takes WAAAAY too long.)
#include <iostream>
#include <vector>
#include <unordered_set>
std::unordered_set<long long> sieve(long long max) {
auto arr = new long long[max];
std::unordered_set<long long> ret;
for (long long i = 2; i < max; i++) {
for (long long j = i * i; j < max; j+=i) {
*(arr + (j - 1)) = 1;
}
}
for (long long i = 1; i < max; i++) {
if (*(arr + (i - 1)) == 0)
ret.emplace(i);
}
delete[] arr;
return ret;
}
bool is_prime(long long n) {
for(long long i = 2; i <= n / 2; ++i) {
if(n % i == 0) {
return false;
}
}
return true;
}
int main() {
auto primes = sieve(100000);
for (long long n = 4; n <= 100000; n+=2) {
bool found = false;
for (auto prime : primes) {
if (prime <= n / 2) {
if (is_prime(n - prime)) {
std::cout << prime << " + " << n - prime << " = " << n << std::endl;
found = true;
break; // Will move onto the next number after it finds a result
}
}
}
if (!found) { // Replace with whatever code you'd like.
std::terminate();
}
}
}
EDIT: Remember to use delete[] and clean up after ourselves.

Program that checks whether another number is contained within it

I've just recently started to dabble in coding, and I ran into a problem that I haven't been able to solve for days, and the closest thing I've been able to find online is a program checking whether a number contains a specific digit, but that doesn't really apply in my case, I don't think. The problem is to let the user enter two positive numbers and check whether the reverse of the second number is contained within the first one. For example if you enter 654321 and 345, it would say say that it contains it because the reverse of 345 is 543 and 654321 contains that. Here's what I've been trying, but it has been a disaster.
P.S: The variables should stay integer through the program.
#include <iostream>
using namespace std;
bool check(int longer, int shorter)
{
int i = 1;
int rev=0;
int digit;
while (shorter > 0)
{
digit = shorter%10;
rev = rev*10 + digit;
shorter = shorter/10;
}
cout << rev << endl;
bool win=0;
int left = longer / 10; //54321
int right = longer % 10; // 65432
int middle = (longer /10)%10; // 5432
int middle1;
int middle2;
int trueorfalse = 0;
while (left > 0 && right > 0 && middle1 > 0 && middle2 >0)
{
left = longer / 10; //4321 //321
right = longer % 10; //6543 //654
middle1 = middle%10; //543
middle2= middle/10; //432
if (rev == left || rev == right || rev == middle1 || rev == middle2 || rev == middle)
{
win = true;
}
else
{
win = false;
}
}
return win;
}
int main ()
{
int longer;
int shorter;
int winorno;
cout << "Please enter two numbers, first of which is longer: ";
cin >> longer;
cin >> shorter;
winorno = check(longer,shorter);
if (winorno==true)
{
cout << "It works.";
}
else
{
cout << "It doesn't work.";
}
return 0;
}
The more you overthink the plumbing, the easier it is to
stop up the drain. -- Scotty, Star Trek III.
This becomes much easier if you divide this task in two parts:
Reverse the digits in an integer.
Search the second integer for the reversed integer calculated by the first part.
For the first part, assume that n contains the number to reverse.
int modulo=1;
int reversed_n=0;
do
{
reversed_n = reversed_n * 10 + (n % 10);
modulo *= 10;
} while ( (n /= 10) != 0);
The end result is if n contained 345, reversed_n will end up with 543, and modulo will be 1000. We'll need modulo for the second part.
The reason the loop is structured this way is intentional. If the original number is 0, we want to wind up with reversed_n also 0, and modulo as 10.
And now, we can take a similar approach to search the second number, called search, whether it contains reversed_n:
for (;;)
{
if ((search % modulo) == reversed_n)
{
std::cout << "Yes" << std::endl;
return 0;
}
if (search < modulo)
break;
search /= 10;
}
std::cout << "No" << std::endl;
Complete program:
#include <iostream>
int main()
{
int search=654321;
int n=345;
int modulo=1;
int reversed_n=0;
do
{
reversed_n = reversed_n * 10 + (n % 10);
modulo *= 10;
} while ( (n /= 10) != 0);
for (;;)
{
if ((search % modulo) == reversed_n)
{
std::cout << "Yes" << std::endl;
return 0;
}
if (search < modulo)
break;
search /= 10;
}
std::cout << "No" << std::endl;
return 0;
}
#include <iostream>
#include <cmath>
using namespace std;
int calculateNumLength(int num){
int length = 0;
while (num > 0) {
num = num / 10;
length++;
}
return length;
}
bool check(int longer, int shorter){
int reversed = 0;
int digit;
int shortLength = calculateNumLength(shorter);
int longLength = calculateNumLength(longer);
int diffrence = longLength - shortLength;
int possibleValues = diffrence + 1;
int possibleNums[possibleValues];
while ( shorter > 0 ) {
digit = shorter % 10;
rev = ( rev * 10 ) + digit;
shorter = shorter / 10;
}
int backstrip = pow(10, diffrence);
int frontstrip = pow(10, longLength-1);
int arrayCounter = 0;
while ( longer > 0 ){
possibleNums[arrayCounter++] = longer/backstrip;
if ( backstrip >= 10 ){
backstrip = backstrip / 10;
}else{
break;
}
longer = longer % frontstrip;
frontstrip = frontstrip / 10;
}
for (int i=0;i<possibleValues;i++){
if (possibleNums[i] == rev ){
return true;
}
}
return false;
}
int main() {
std::cout << check(654321,123) << std::endl;
return 0;
}

UVA online judge 3n+1: Wrong Answer

Ive been working on this problem for a while but every time I submit it I get the wrong answer, however when I input sample cases I seem to produce the right answer. Could anyone help me out here?
The problem can be found on this site: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&page=show_problem&problem=36
The code itself:
#include <vector>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
long find_cycle_length(long b)
{
// Finds the max cycle of b
long max_cycle = 1;
while (b != 1)
{
if (b % 2 != 0)
{
b = (3 * b) + 1;
++max_cycle;
}
else if (b % 2 == 0)
{
b /= 2;
++max_cycle;
}
}
return max_cycle;
}
long find_max_cycle(vector <long>& b)
{
vector <long> temp;
for (int i = 0; i < b.size(); ++i)
{
long buffer = b[i];
temp.push_back(find_cycle_length(buffer));
}
long max_cycle = *max_element(temp.begin(), temp.end());
return max_cycle;
}
int main()
{
long i = 0; // First number
long j = 0; // Second number
long size = 0; // Determines the size of the vector buffer
long counter = 0; // Used to fill buffer
cin >> i >> j;
if (j > i) {
size = (j - i) + 1;
counter = i;
}
else if (i > j) {
size = (i - j) + 1;
counter = j;
}
else if (i == j)
{
size = 1;
counter = i;
}
vector<long> buffer(size); // Used to store all numbers i to j
for (int x = 0; x < buffer.size(); ++x) // fill buffer
{
buffer[x] = counter;
++counter;
}
cout << i << " " << j << " " << find_max_cycle(buffer) << endl;
return 0;
}
I think you may be misunderstanding the instructions. The sample input isn't
1 10
but
1 10
100 200
201 210
900 1000
You don't have a while loop that lets the user give you more than one line of input -- your program quits after one. Why don't you make that change -- let the user keep putting in new lines of input till giving you an end-of-file (or -- it works out the same for coding -- accept all lines of input from an input file redirected to standard input) -- and see if you get an OK?
Oh, I see molbdnilo suggested this in the first comment. Anyway: he's right.

C++, Math exponents

I have this problem in making a program that helps me with this.
For n (n <= 25). Make a program that calculates and shows on the screen the value of the sum:
S= 1+ 2+ 2(pow 2)+ 2(pow 3)+...+2(pow n).
what i managed to do is this :
#include <iostream>
#include <math.h>
using namespace std;
int i;
int n;
long s;
long f() {
if (n=0) {
return 1;
}else if (n=1) {
return 2;
}else {
return 2* (n-1);
}
}
int main() {
for (i=0; n<=2;++n){
s=s+f();
cout << s <<endl;
}
}
The main code is wrong i know that for sure but i do not know how to do it..please help me, im just a c++ begginer and trying to learn the language on my own.
The specific things you're doing wrong...
int i;
int n;
long s;
Don't use globals like this. You should need no globals at all for this program.
long f() {
if (n=0) {
return 1;
}else if (n=1) {
return 2;
}else {
return 2* (n-1);
}
}
Here you're using recursion where you should use a loop instead. Also, n should be a passed-in parameter:
long f(int n) {
long result = 1;
for(int i = 0; i < n; ++i)
result *= 2;
return result;
}
Or even better, don't reinvent the wheel and use pow(2, n) instead of f(n).
for (i=0; n<=2;++n){
You set i but never do anything with it.
You never initialize n or s so they could have random values (though these days compilers are nicer to people and set all the uninitialized globals to 0, but you really shouldn't depend on that).
Ergo, you should have written n=0 instead of i=0.
How it could have looked if you didn't use globals:
int main() {
long s = 0;
for (int n = 0; n <= 2; ++n){
s += f(n);
cout << s <<endl;
}
}
This is just a geometric series. Sum of n terms of geometric series is given by:-
S(n) = a ( r^n - 1 )/ (r - 1 )
n = no. of terms.
r = common ratio.
a = first term.
So, for your example...
a = 1.
r = 2.
n = no of terms you want to take sum.
2(pow n) may be written 1 << n
or if you want to compute yourself the power of two:
// compute manually (1 << n)
int power2(int n)
{
int res = 1;
for (int i = 0; i != n; ++i) {
res *= 2
}
return res;
}
Your sum is in fact power2(n+1) - 1, so you may simply write:
std::cout << ((1 << n + 1) - 1) << std::endl;
or
std::cout << power2(n + 1) - 1 << std::endl;
if you want to do that in loop:
unsigned int res = 0;
for (int i = 0; i != n; ++i) {
res += power2(i);
}
std::cout << res << std::endl;
All you need is a variable to hold the current sum and another variable to hold the power of 2:
int main()
{
const int n = 25;
int pow2 = 1;
int sum = 1;
for (int i = 1; i <= n; i++)
{
pow2 *= 2;
sum += pow2;
}
cout << sum << endl;
}