Why is the code throwing segmentation fault? - c

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;
}

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]);
}
}

Finding the longest Collatz Chain

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);
}

How to solve this C programming exercise?

This is my first time learning C. So I have to write a code that can find the second largest number from any inputted numbers.
The code is:
#include <stdio.h>
int main() {
int x, max, max2;
max = max2 = -1;
while (scanf("%d", &x) != EOF) {
if (x > max) {
max2 = max;
max = x;
} else if (x > max2) {
max2 = x;
}
}
printf("%d\n", max2);
return 0;
}
The problem is, if I enter 1 2 3 4 5 here, the answer should be 4, as 4 is the second max number. But the code should work on any inputted number. I just couldn't figure it out.
I have to use Linux terminal and vim to solve it. I made a input file in terminal using vim input and made a file and input 1 2 3 4 5 there. But when I run the code using ./a.out <input, it still shows 5. How do I solve this?
What do you mean by "any inputted number" ? Non sorted input ? Negative as well ?
Here is a function which takes a list of positive integers and return the second maximum, or -1 if an error is encountered (not enough arguments). To adapt to your needs.
int snd_max(int length, int* args) {
int max, max2;
max = -1;
max2 = -1;
for (int i = 0; i< length; i++) {
if (args[i] > max) {
max2 = max;
max = args[i];
} else if (args[i] > max2 {
max2 = args[i]
}
}
return max2;
I made it easy for myself and replaced your input with an array.
int main (void)
{
char lst [] = { 10, 11, 2, 33 }; // my input (must be char because
// I use sizeof to determine the number of inputs)
int max = -1; // biggest
int submax = -1; // 2nd biggest
if (sizeof (lst) < 2) // must be > 1 to find 2 biggest numbers
{
printf ("bad");
return 0;
}
for (int i = 0; i < sizeof (lst); i ++)
{
int x = lst [i];
if (x > max)
{
submax = max;
max = x; // new biggest makes the old biggest to 2nd biggest
}
else // its not biggest but maybe 2nd biggest
{
if (x > submax)
{
submax = x;
}
}
printf ("\n%d: check %d - max=%d, submax=%d\n", i, x, max, submax);
}
return 0;
}

C program - How to check array elements [duplicate]

This question already has answers here:
C sizeof a passed array [duplicate]
(7 answers)
Closed 6 years ago.
I have a function repsEqual that takes an array and integer and returns 1 if the array contains only digits of the number in the same order that appear in the same number. Otherwise it returns 0.
int repsEqual(int a[], int len, int n)
If a is {3,2,0,5,3} and n is 32053 return 1 because the array contains only the digits of the number in same order as they are in the number.
If a is {0,3,2,0,5,3} and n is 32053 return 1; we can ignore leading zeros.
I tried like this
int repsEqual(int a[], int len, int n)
{
int len = sizeof(a)/sizeof(a[0]);
//storing elements in array
for(int i=0;i<len;i++)
{
scanf("%d", &a[i]); //eg storing:3 2 0 5 3
}
//asking user integer number and storing in next array
scanf("%d",&a2[num]);//eg 32053
}
Now I need to check if a2 elements are in same order as a1, but do not know how to get started.
This is what you want
int repsEqual(int a[], int len, int n)
{
for (int i = 0; i < len; i++)
{
if (a[len - i - 1] == n % 10)
n /= 10;
else
return 0;
}
//For cases where your number-length is longer than your array length
if (n != 0) return 0;
return 1;
}
First you have your array, say like a[5] = { 5, 2, 3, 1, 4}
Basically what i do is looping the array from end to start, thats a[len - i - 1]
Then i check it with the last character of n thats n%10
So example with n = 52314, the first if statement check if (52314 % 10) which is 4 equal with a[4] which is also 4
if the 2 character match then the loop continue first by remove the last character of n: 52314 / 10 = 5231.
And the next loop will check for 5231 % 10 and a[3]
else the loop break mid-way and return 0 indicate that a mis-match is found
finally after all the character in array is checked and no mismatch is found, it will return 1, as the pattern match
Note: a function should only does what its name says
In your case, check if an array and an integer have the same pattern
User input should be put outside somewhere else, after you have the inputs (the array, the len, and n) you then pass-in to repsEqual for checking
Try matching the number (n) backwards against the array 'a'. To do this you'll want to modulus the smallest digit from 'n', by getting the remainder from dividing by 10. Then remove the smallest digit from 'n' by dividing by 10.
int repsEqual(int a[], int len, int n)
{
int i;
int temp;
if (0 == len || NULL == a)
return 0; // no array, or elements, doesn't match a real number (n).
temp = n;
for (i = len - 1; i >= 0; --i)
{
if (a[i] != (temp % 10))
return 0; // remainder mismatch against array element.
temp = temp / 10; // removes the smallest digit.
}
return 1;
}
By modulus 10 on your n you get the remainder of dividing by 10. IE 452 % 10 = 2. Then by dividing be ten we remove the smallest digit IE 452 / 10 = 45.
This seems to be some homework, haha. Anyway I gave u a quick/ugly sample to start with.
#include <stdio.h>
int repsEqual(int a[],int len , int n)
{
char str[100];
sprintf(str, "%d", n);
int i;
int nonzeroIndex;
for(i=0; i<len; i++){
if (a[i] != 0)
break;
}
nonzeroIndex = i;
printf("nonzeroIndex is %d\n", nonzeroIndex);
for(i= nonzeroIndex; i <len; i++){
if (a[i] != str[i - nonzeroIndex] - 48) {
printf("diff at %d\n", i);
return 0;
}
}
return 1;
}
int main()
{
int a[5];
a[0] = 0;
a[1] = 2;
a[2] = 0;
a[3] = 5;
a[4] = 3;
int output = repsEqual(a, 5, 2053);
printf("result: %d\n", output);
}

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