Finding the longest Collatz Chain - c

I am currently on my Uni homework and this is the last task. Its the 14th euler problem.
https://projecteuler.net/problem=14
So I am really new and i know that have a lot of crappy implementations. When executing this there is actually no output at all. I've been running this for 3 Minutes because i thought it needed to "load"..
My Task is to have a working function that calculates the Length and then take this length and compare it to have the longest chain as the "Final" output.
I tried to have a Loop that calculates the length for every i in 1000000 and then save this number into an array with the same size.
At the end of the Loop I want to compare the last length with the current and save the longer into the var if its longer.
I am stuck for like the past 2 hours
Here is my current Code:
#include <stdio.h>
#include <math.h>
int number = 1000000;
long sequence = 0;
int seqLen = 0;
int startingNum = 0;
int currLen = 0;
unsigned calculateCollatzLength(unsigned n){
int ans = 1;
while (n != 1) {
if (n & 1) {
n = 3 * n + 1;
} else {
n >>= 1;
}
ans ++;
}
currLen = ans;
return currLen;
}
int main() {
int cache[number];
for(int i = 0; i <= number; i++){
calculateCollatzLength(i);
cache[i] = currLen;
if (cache[i] > seqLen) {
seqLen = cache[i];
startingNum = i;
}
}
printf("The Longest Collatz Chain from 1 to 1000000 is %d long and has the starting number %d \n", seqLen, startingNum);
}
Hope that this is kind of understandable to ask in on this since this is my 3rd Question and it kind of feels like cheating asking but i dont know who to ask or cant find any answers :(

Here's a cleaned-up working version of your code. There's no need for a cache, and int64_t is a safer bet than unsigned (which is likely to be 32 bits) to avoid overflows. Your use of global variables was confusing and unnecessary – you can simply return the length of the sequence and find the maximum in main.
#include <stdio.h>
#include <stdint.h>
int collatz(int64_t n){
int len = 1;
while (n != 1) {
if (n & 1) {
n = 3 * n + 1;
} else {
n >>= 1;
}
len++;
}
return len;
}
#define N 1000000
int main(void) {
int longest_i = 0;
int longest = 0;
for(int i = 1; i <= N; i++){
int len = collatz(i);
if (len > longest) {
longest_i = i;
longest = len;
}
}
printf("**collatz(%d) = %d\n", longest_i, longest);
}

Related

Printing a Count in C Only Printing to 65

I am trying to make a very simple 64 bit operating system, and so far have done really well, but since I am not very familiar with C I have been having some problems with it, and this one stumped me. The output would count like normal (1 2 3 4 5 6 7 8 9 10 11 12 etc.) but it would stop printing at 65. I know that "print.h" works because I have tested it many times, but I am not so sure on my method of converting the numbers to a character array. Any help at all would be much appreciated. Here's my code:
#include "print.h"
int getLen(int x) {
unsigned int n = x;
int count = 0;
while(n!=0)
{
n=n/10;
count++;
}
return count;
}
char ITC(unsigned int x) {
char ret;
unsigned int n = x;
if(n==0){
ret='0';
}
if(n==1){
ret='1';
}
if(n==2){
ret='2';
}
if(n==3){
ret='3';
}
if(n==4){
ret='4';
}
if(n==5){
ret='5';
}
if(n==6){
ret='6';
}
if(n==7){
ret='7';
}
if(n==8){
ret='8';
}
if(n==9){
ret='9';
}
return ret;
}
void kernel_main(){
print_clear();
print_set_color(PRINT_COLOR_GREEN, PRINT_COLOR_BLACK);
char out[512];
int onOut = 0;
for (int i = 0; i < 100; i++)
{
onOut++;
unsigned int n = i;
while (n != 0) {
out[onOut + getLen(n)] = ITC(n%10);
n /= 10;
}
out[onOut + getLen(i) + 1] = '\n';
onOut += getLen(i) + 1;
}
for (int i = 0; i < 512; i++)
{
print_char(out[i]);
}
}
The issue is with the algorithm rather then perhaps familiarity with C. Your indexing into the unitialised out array was leaving gaps so outputting junk that happened to be in the array.
Consider the following - the parts I changed annotated - not all are part of the solution; just good practice:
char out[512] = {0}; // <<< Good idea to initialise
int onOut = 0;
for (int i = 1; i < 100; i++) // Start form 1 not zero
{
int n = i; // <<< Type agreement with i
int numlen = getLen(n) ; // <<< Get the length of the initial number
// Don't unnecessarily calculate in the loop
// when you know it decrements by 1 on each
// iteration
for( int j = onOut + numlen; // <<< Start from the end position
(n!=0) && j >= onOut; // <<< toward the start position
j-- ) // <<< backward
{
out[j - 1] = ITC(n % 10); // <<< Insert digit, starting from index zero
n /= 10;
}
onOut += numlen ; // <<< Move to end of newly inserted number
out[onOut++] = '\n'; // <<< Add the newline
}
Note that you have over-complicated this code somewhat; especially w.r.t. to ITC() if you code like that habitually your "operating system" will run very slowly. ITC() can be reduced to a simple look-up thus:
char ITC(unsigned int x)
{
static const char digits[] = "0123456789" ;
return digits[x] ;
}
Or in any likely character set where digits are contiguous and in order, arithmetically thus:
char ITC(unsigned int x)
{
return '0' + x ;
}
I'd give two pieces of advice for success in this project and programming in general.
Comment your code. If you have to explain it to yourself, you are more likely to find the flaws. But also later maintainers or people assisting you with debugging will have an idea of your intended semantics.
Use a debugger. I used a debugger to figure out were your code was going wrong because it was quicker and more direct that other methods. Certainly quicker than posting questions of StackOverflow!
I don't understand why you make that one function so complicated, what's wrong with this?
char ITC(unsigned int x) {
return (char)((int)'0' + n);
}
Some of the above comments are correct, plus you also need to terminate your string with a null character... and NOT print all 512 characters of a non-initialized array. Something like this should work much better (though you should also include the contents of print.h so we can see if there are any problems there):
#include "print.h"
int getLen(unsigned int x)
{
int count = 0;
while (n != 0) {
n = n / 10;
count++;
}
return count;
}
char ITC(unsigned int x)
{
return (x <= 9) ? x + '0' : '?';
}
void kernel_main()
{
char out[512];
int onOut = 0;
print_clear();
print_set_color(PRINT_COLOR_GREEN, PRINT_COLOR_BLACK);
for (int i = 0; i < 100; i++) {
unsigned int len = getLen(i);
unsigned int n = i;
unsigned int offs = len;
while (n != 0) {
out[onOut + offs--] = ITC(n % 10);
n /= 10;
}
out[onOut + len + 1] = '\n';
onOut += len + 1;
}
out[onOut + 1] = 0;
for (int i = 0; out[i] != 0; i++) {
print_char(out[i]);
}
}

Why is the code throwing segmentation fault?

The problem is to find the number i<=n, n<=500000 for which the longest collatz series exists.
Collatz series for a number n terminates at 1, and the conditions are
if n is even, next term = n/2
if n is odd, next term = 3*n + 1
Well as a matter of fact, the collatz series always terminates at 1 for all numbers.
Hence any number won't repeat in its collatz series. Using this fact, I have written the following code
LOGIC:
I start a while loop, that goes till n and for each iteration, I store the length of the series for that i.
If i occurs in the series of some n >= r > i, then i terminate the loop and add the length of i to r.
For example, say series of 3 is 3, 10, 5, 16, 8, 4, 2, 1. Now the length corresponding to 2 will already be stored in the series_length array, so I use that value.
Then the for loop next to that, finds the longest series and displays the answer.
The code works fine for n <= 1818 to be precise, but shows segmentation fault onwards (dunno why :(). Please help
CODE :
#include <stdio.h>
int length = 0, series_length[500000], maxlength = 0;
void store_length(int n) {
while(n > 1 && series_length[n] == 0) {
length++;
if(n%2 == 0) {
n = n/2;
}
else {
n = 3*n + 1;
}
}
length += series_length[n];
}
int main() {
int n, i = 1, result;
scanf("%d", &n);
series_length[1] = 1;//redundant statement
while(i <= n) {
store_length(i);
series_length[i] = length;
length = 0;
i++;
}
for(int i = 1;i <= n; i++) {
if(maxlength <= series_length[i]) {
maxlength = series_length[i];
result = i;
}
}
printf("%d %d\n", result, maxlength);
return 0;
}
INPUT-
10
OUTPUT-
9 20 (AS Expected)
INPUT-
100000
OUTPUT-
Segmentation Fault
Expected-
77031 351
Your value for n goes outside the range.
You have a line n = 3*n + 1; in the function store_length
Running this with the gdb with input as 100000 gives
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000000401545 in store_length (n=532060) at 29_01.c:6
6 while(n > 1 && series_length[n] == 0) {
(gdb) p n
$1 = 532060
only store it if it fits
... and use it if it already has been computed
avoid global variables
prefer unsigned values
[use descriptive variable names]
#include <stdio.h>
#define THE_SIZE 500000
unsigned series_length[THE_SIZE]= {0,};
unsigned get_length(unsigned val) {
unsigned steps;
for (steps=0; val > 1 ; steps++) {
if (val < THE_SIZE && series_length[val]) { steps += series_length[val]; break; }
if(val %2 ) val = 3*val + 1;
else val /= 2;
}
return steps;
}
int main( int argc, char **argv) {
unsigned top, val , result;
unsigned best,maxlength ;
sscanf(argv[1], "%u", &top);
series_length[1] = 1;//redundant statement
best = maxlength = 0;
for(val=1;val <= top; val++) {
result = get_length(val);
// store it if it fits;
if(val<THE_SIZE) series_length[val] = result;
if (result < maxlength) continue;
best = val; maxlength = result;
}
printf("%u %u\n", best, maxlength);
return 0;
}
Finally, just for fun, make the array smaller
#define THE_SIZE 500
, and the program should give the same result for a given value. (it does)
You get the maximum value 24,648,077,896 with n = 487039.
You must thus use the type long long int for n and you should use an array of 24,648,077,896 integers to avoid a segmentation fault. Unfortunately I never succeeded in allocating a block of 100GB. Your optimization is thus not viable.
Without the array optimization I can scan all 500000 n values in 265ms.
Here is my code:
#include <stdio.h>
int collatz_length(int n) {
int length = 0;
long long int v = (long long int)n;
while (v > 1) {
if ((v&1) == 0)
v = v / 2;
else
v = v*3 + 1;
length++;
}
return length;
}
int main() {
int max_i, max_l = 0;
for (int i = 500000; i > 0; i--) {
int l = collatz_length(i);
if (l > max_l){
max_l = l;
max_i = i;
}
}
printf("i: %d l: %d\n", max_i, max_l);
return 0;
}

Finding the Nth prime number in C language

The code runs just fine but instead of using "for loop" to iterate upto 200000 , I think there can be a better alternative and I am having trouble finding it. I need help to optimise this solution.The time taken by this solution currently is 56ms.
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
int isPrime(long long int number)
{
int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
int returnNPrime(int N)
{
int counter = 0;
int i ;
if(N == 1) return 2;
for(i=3;i<200000;i+=2)
{
if(isPrime(i))
{
counter++;
if(counter == (N-1))
return i;
}
}
return 0;
}
int main(int argc, char *argv[])
{
printf("%d",returnNPrime(10001));
return 0;
}
Don't put an arbitrary stop condition. You know that the list of primes is infinite and that the loop will eventually stop. Write it like this:
int returnNPrime (int N)
{
int counter = 0;
int i;
if (N == 1) return 2;
for (i = 3; ; i += 2)
{
if (isPrime(i))
{
counter++;
if (counter == (N - 1))
return i;
}
}
}
That being said, this solution is inefficient because you don't store previously found primes.
Try something like this:
#include <stdio.h>
#include <stdbool.h>
#define N 10001
int primes[N] = { 2, 3 };
int main ()
{
for (int n = 2; n < N; n++) {
for (int x = primes[n - 1] + 2; ; x += 2) {
bool prime = true;
for (int i = 0; i < n; i++) {
int p = primes[i];
if (p * p > x) {
break;
}
if (x % p == 0) {
prime = false;
break;
}
}
if (prime) {
primes[n] = x;
break;
}
}
}
printf ("%d\n", primes[N - 1]);
}
Read this paper http://cr.yp.to/bib/1996/deleglise.pdf which describes how to count the number of primes <= N in O (n^(2/3)) or so and implement the algorithm. It's substantially faster than the Eratosthenes sieve, because it doesn't actually find any primes but just counts how many there are.
Make an educated guess how large the n-th prime would be. Say the guess is x. Use the algorithm above to find out how many primes <= x there are, then use a sieve if you are close enough, or use a better guess with the information you just found and try again. Total time O (n^(2/3)).
With some decent hardware and a lot of patience this will let you find solutions up to n = 10^22 or so.
OP's method consumes a lot of time with as it does not take advantage that there is no need to determine the remainder if i is not a prime.
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
The Sieve_of_Eratosthenes is likely faster yet is a dramatic change from OP's code.
Suspect this code is still too slow for OP.
The follows adjust OP's code by only attempting to test against previously found primes. Also it uses pcandidate / plist[index] as part of a terminating condition. Optimized compilers often can provide this at a small cost once pcandidate % plist[index] is computed.
bool prime_test(const unsigned long *plist, unsigned long long pcandidate) {
if (pcandidate <= 2) return pcandidate == 2;
for (size_t index = 0; ; index++) {
unsigned long long remainder = pcandidate % plist[index];
if (remainder == 0) return false;
unsigned long long quotient = pcandidate / plist[index];
if (quotient < plist[index]) return true;
}
assert(0);
return true;
}
unsigned long long prime_nth(size_t n) {
unsigned long plist[n+1];
plist[0] = 2;
unsigned long long pcandidate = plist[0];
for (size_t index = 0; index <= n; index++) {
while (!prime_test(plist, pcandidate)) pcandidate++;
plist[index] = (unsigned long) pcandidate;
pcandidate++;
}
return plist[n];
}
A classic simplification involves only seeking new primes amongst odd numbers. Also change all math to unsigned. Left for OP.

A program that calculate the factorial of big number

It's a test in a website, here is the code
#include <stdio.h>
void Print_Factorial ( const int N );
int main()
{
int N;
scanf("%d",&N);
Print_Factorial(N--);
return 0;
}
/* your code will be put in here*/
#include <math.h>
int getFactLength(int N){
double length = 0;
while(N){
length += log10(N--);
}
return (int)length+1;
}
void printFact(int fact[], int length){
while(length--){
printf("%d",*fact++);
}
}
void initialNums(int nums[], int length, int num){
while(length--){
*nums++ = num;
}
}
void Print_Factorial( const int N ){
if(N < 0){
printf("Invalid input");
return ;
}
int NT = N;
if(NT>=0 && NT<15){
int fact = 1;
while(NT){
fact *= NT--;
}
printf("%d",fact);
return ;
}
int length = getFactLength(N);
int fact[length];
initialNums(fact, length, 0);
fact[length-1] = 1;
int lastNoneZeroIndex = length-1;
while(NT > 1){
int lengthT = length;
int carry = 0;
while(lengthT-- > lastNoneZeroIndex){
int result = NT*fact[lengthT] + carry;
fact[lengthT] = result % 10;
carry = result / 10;
}
while(carry){
fact[--lastNoneZeroIndex] = carry % 10;
carry /= 10;
}
NT--;
}
printFact(fact, length);
}
I use values from 0 to 20 to test it, all of them are right. But when I submit it in that website, a test case always do not pass. I don't know what that case is. But, there are 5 cases, all test cases are between 0 and 1000, and two of them are no more than 15, one of them is negative, one of them use most time to pass, so I think the case that didnt pass is a number that smaller than 1000. That's all I know, I can't imagine that 1000 was passed, but the number smaller than 1000 didn't pass. I don't know what is wrong with my lovely code. I hope you can watch my code, and find some bugs.
There create a overflow in the fact variable, here you use int type for fact variable.
For input 13,14, it gives wrong answer.
Solution:
long long int fact = 1;
or,
change the condition- if(NT>=0 && NT<13)

how do I convert an array to an long int and back to an array

You are given an integer N. Find the digits in this number that exactly divide N my code gives me a timed out message i.e. Terminated due to timeout or CPU time limit exceeded (core dumped). here is my codeenter code here
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main()
{
/* Enter your code here. Read input from STDIN. Print output to STDOUT */
int T, cnt, digits1, digits2, rem;
long long x;
char N[11];
scanf("%d", &T);
for (int i = 1; i <= T; i++)
{
scanf("%10c", N);
int x = (atoll(N));
digits1 = x;
while (digits1 != 0)
{
digits1 = x/10;
cnt++;
}
// char buffer[cnt + 1];
for(int j = 1; j <= cnt; j++)
{
rem = x % N[j];
if (rem == 0)
{
digits2++;
}
}
printf("%d", digits2);
}
return 0;
}
This looks like homework.
I'll give some pointers.
Once you have the input chars converted to an actual int or long (or long long in your case), do NOT muck around with digits - especially in base 10. If you're going to do anything like that then you may as well leave it in char array form. This bit is not needed:
while (digits1 != 0)
{
digits1 = x/10;
cnt++;
}
You have uninitialised values. Don't assume that everything is automatically initialised to zero. Sometimes you get lucky... but often you won't.
This looks good:
for(int j = 1; j <= cnt; j++)
{
rem = x % N[j]; //not quite right N is ASCII
if (rem == 0)
{
digits2++;
}
}

Resources