From b4c85f8f4a76d2780df55487451187519e304511 Mon Sep 17 00:00:00 2001 From: Daniele Fucini Date: Mon, 23 Sep 2019 20:13:32 +0200 Subject: [PATCH] Improve solutions for problems 18, 24 and 25 --- C/p018.c | 1 + C/p024.c | 136 +++++++++++++++++++++++++---------------- C/p025.c | 61 ++++++++++-------- C/projecteuler.c | 60 ++++++++++++++++-- C/projecteuler.h | 1 + Python/p018.py | 17 +----- Python/projecteuler.py | 10 +++ 7 files changed, 187 insertions(+), 99 deletions(-) diff --git a/C/p018.c b/C/p018.c index c89bf0b..3ca501c 100644 --- a/C/p018.c +++ b/C/p018.c @@ -74,6 +74,7 @@ int main(int argc, char **argv) fclose(fp); + /* Use function implemented in projecteuler.c to find the maximum path.*/ max = find_max_path(triang, 15); clock_gettime(CLOCK_MONOTONIC, &end); diff --git a/C/p024.c b/C/p024.c index e622745..7c1bd8a 100644 --- a/C/p024.c +++ b/C/p024.c @@ -1,24 +1,59 @@ +/* A permutation is an ordered arrangement of objects. For example, 3124 is one possible permutation of the digits 1, 2, 3 and 4. + * If all of the permutations are listed numerically or alphabetically, we call it lexicographic order. The lexicographic permutations of 0, 1 and 2 are: + * + * 012 021 102 120 201 210 + * + * What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?*/ + #include #include #include +#include "projecteuler.h" -void next_perm(int *perm, int n); -void swap(int *vet, int i, int j); -void sort(int *vet, int i, int n); +void next_perm(int **perm, int n); +void swap(int **vet, int i, int j); +int compare(void *a, void *b); int main(int argc, char **argv) { - int i, perm[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int i, res[10]; + int **perm; double elapsed; struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); + if((perm = (int **)malloc(10*sizeof(int *))) == NULL) + { + fprintf(stderr, "Error while allocating memory\n"); + return 1; + } + + for(i = 0; i < 10; i++) + { + if((perm[i] = (int *)malloc(sizeof(int))) == NULL) + { + fprintf(stderr, "Error while allocating memory\n"); + return 1; + } + *perm[i] = i; + } + for(i = 0; i < 999999; i++) { + /* Function that generates permutations in lexicographic order. + * Finish when the 1000000th is found.*/ next_perm(perm, 10); } + for(i = 0; i < 10; i++) + { + res[i] = *perm[i]; + free(perm[i]); + } + + free(perm); + clock_gettime(CLOCK_MONOTONIC, &end); printf("Project Euler, Problem 24\n"); @@ -26,7 +61,7 @@ int main(int argc, char **argv) for(i = 0; i < 10; i++) { - printf("%d", perm[i]); + printf("%d", res[i]); } printf("\n"); @@ -38,61 +73,56 @@ int main(int argc, char **argv) return 0; } -void next_perm(int *perm, int n) +void swap(int **vet, int i, int j) { - int i, j, min = n, min_idx, flag = 0; - - for(i = 0; i < n - 1; i++) - { - if(perm[i] < perm[i+1]) - { - flag=1; - break; - } - } - - if(!flag) - { - return; - } - - for(i = n - 2; perm[i] > perm[i+1]; i--); - - for(j = i + 1; j < n; j++) - { - if(perm[j] > perm[i] && perm[j] < min) - { - min = perm[j]; - min_idx = j; - } - } - - swap(perm, i, min_idx); - sort(perm, i+1, n); -} - -void swap(int *vet, int i, int j) -{ - int tmp; + int *tmp; tmp = vet[i]; vet[i] = vet[j]; vet[j] = tmp; } -void sort(int *vet, int i, int j) +int compare(void *a, void *b) { - int a, b, tmp; + int *n1, *n2; - for(a=i+1; a=i && vet[b]>tmp) - { - vet[b+1]=vet[b]; - b--; - } - vet[b+1]=tmp; - } + n1 = (int *)a; + n2 = (int *)b; + + return *n1 - *n2; +} + +/* Implements SEPA (Simple, Efficient Permutation Algorithm) + * to find the next permutation.*/ +void next_perm(int **perm, int n) +{ + int i, key; + + /* Starting from the right of the array, for each pair of values + * if the left one is smaller than the right, that value is the key.*/ + for(i = n - 2; i >= 0; i--) + { + if(compare((void *)perm[i], (void *)perm[i+1]) < 0) + { + key = i; + break; + } + } + + /* If no left value is smaller than its right value, the + * array is in reverse order, i.e. it's the last permutation.*/ + if(i == -1) + { + return; + } + + /* Find the smallest value on the right of the key which is bigger than the key itself, + * considering that the values at the right of the key are in reverse order.*/ + for(i = key + 1; i < n && compare((void *)perm[i], (void *)perm[key]) > 0; i++); + + /* Swap the value found and the key.*/ + swap(perm, key, i-1); + /* Sort the values at the right of the key. This is + * the next permutation.*/ + insertion_sort((void **)perm, key+1, n-1, compare); } diff --git a/C/p025.c b/C/p025.c index d227d4d..9e63c1c 100644 --- a/C/p025.c +++ b/C/p025.c @@ -1,3 +1,24 @@ +/* The Fibonacci sequence is defined by the recurrence relation: + * + * Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + * Hence the first 12 terms will be: + * F1 = 1 + * F2 = 1 + * F3 = 2 + * F4 = 3 + * F5 = 5 + * F6 = 8 + * F7 = 13 + * F8 = 21 + * F9 = 34 + * F10 = 55 + * F11 = 89 + * F12 = 144 + * + * The 12th term, F12, is the first term to contain three digits. + * + * What is the index of the first term in the Fibonacci sequence to contain 1000 digits?*/ + #include #include #include @@ -9,30 +30,36 @@ int main(int argc, char **argv) int i; double elapsed; struct timespec start, end; - mpz_t a, b; + mpz_t f1, f2, fn; char *num; size_t size; clock_gettime(CLOCK_MONOTONIC, &start); - mpz_init_set_ui(a, 1); - mpz_init_set_ui(b, 1); + mpz_init_set_ui(f1, 1); + mpz_init_set_ui(f2, 1); + mpz_init(fn); i = 2; while(1) { - mpz_add(a, a, b); + /* Use the GMP Library to calculate the Fibonacci numbers.*/ + mpz_add(fn, f1, f2); i++; - if((size = mpz_sizeinbase(a, 10)) == 1000) + /* The function mpz_sizeinbase gives the number of digits of + * the number in the given base, but the result is either exact + * or one too big. To check the exact size, the number is + * converted to string and the strlen function is used.*/ + if((size = mpz_sizeinbase(fn, 10)) >= 1000) { if((num = (char *)malloc((2+size)*sizeof(char))) == NULL) { fprintf(stderr, "Error while allocating memory\n"); return 1; } - gmp_sprintf(num, "%Zd", a); + gmp_sprintf(num, "%Zd", fn); size = strlen(num); free(num); if(size == 1000) @@ -41,27 +68,11 @@ int main(int argc, char **argv) } } - mpz_add(b, a, b); - i++; - - if((size = mpz_sizeinbase(b, 10)) == 1000) - { - if((num = (char *)malloc((2+size)*sizeof(char))) == NULL) - { - fprintf(stderr, "Error while allocating memory\n"); - return 1; - } - gmp_sprintf(num, "%Zd", b); - size = strlen(num); - free(num); - if(size == 1000) - { - break; - } - } + mpz_set(f1, f2); + mpz_set(f2, fn); } - mpz_clears(a, b, NULL); + mpz_clears(f1, f2, fn, NULL); clock_gettime(CLOCK_MONOTONIC, &end); diff --git a/C/projecteuler.c b/C/projecteuler.c index 40074e7..28fa6b0 100644 --- a/C/projecteuler.c +++ b/C/projecteuler.c @@ -50,7 +50,7 @@ int is_palindrome(int num, int base) /* Start with reverse=0, get the rightmost digit of the number using * modulo operation (num modulo base), add it to reverse. Remove the - * rightmost digit dividing num by the base, shift the reverse left + * rightmost digit from num dividing num by the base, shift the reverse left * multiplying by the base, repeat until all digits have been inserted * in reverse order.*/ while(tmp > 0) @@ -192,8 +192,12 @@ int find_max_path(int **triang, int n) { int i, j; + /* Start from the second to last row and go up.*/ for(i = n - 2; i >= 0; i--) { + /* For each element in the row, check the two adjacent elements + * in the row below and sum the larger one to it. At the end, + * the element at the top will contain the value of the maximum path.*/ for(j = 0; j <= i; j++) { if(triang[i+1][j] > triang[i+1][j+1]) @@ -206,11 +210,40 @@ int find_max_path(int **triang, int n) return triang[0][0]; } +int sum_of_divisors(int n) +{ + int i, sum = 1, limit; + + /* For each divisor of n smaller than the square root of n, + * there is another one larger than the square root. If i is + * a divisor of n, so is n/i. Checking divisors i up to square + * root of n and adding both i and n/i is sufficient to sum + * all divisors.*/ + limit = floor(sqrt(n)); + + for(i = 2; i <= limit; i++) + { + if(n % i == 0) + { + sum += i; + /* If n is a perfect square, i=limit is a divisor and + * has to be counted only once.*/ + if(n != i * i) + { + sum += (n / i); + } + } + } + + return sum; +} + void insertion_sort(void **array, int l, int r, int (*cmp)(void *lv, void *rv)) { int i, j; void *tmp; - + + /* After this cycle the smallest element will be in the first position of the array.*/ for(i = r; i > l; i--) { if(cmp(array[i], array[i-1]) < 0) @@ -220,6 +253,9 @@ void insertion_sort(void **array, int l, int r, int (*cmp)(void *lv, void *rv)) array[i-1] = tmp; } } + + /* For each element in the array (starting from i=2), move it to the left until a + * smaller element on its left is found.*/ for(i = l + 2; i <= r; i++) { tmp = array[i]; @@ -239,12 +275,16 @@ int partition(void **array, int l, int r, int (*cmp)(void *lv, void *rv)) { int i = l -1, j = r; void *pivot, *tmp; - + + /* Arbitrarily selecting the rightmost element as pivot.*/ pivot = array[r]; while(1) { + /* From the left, loop until an element greater than the pivot is found.*/ while(cmp(array[++i], pivot) < 0); + /* From the right, loop until an element smaller than the pivot is found + * or the beginning of the array is reached.*/ while(cmp(array[--j], pivot) > 0) { if(j == l) @@ -253,16 +293,22 @@ int partition(void **array, int l, int r, int (*cmp)(void *lv, void *rv)) } } + /* If j<=i, array[j], which is smaller than pivot, is already on the left + * of array[i], which is larger than pivot, so they don't need to be swapped.*/ if(j <= i) { break; } + /* If j>i, swap array[i] and array[j].*/ tmp = array[i]; array[i] = array[j]; array[j] = tmp; } + /* Swap array[i] with pivot. All elements on the left of pivot are smaller, all + * the elements on the right are bigger, so pivot is in the correct position + * in the sorted array.*/ tmp = array[i]; array[i] = array[r]; array[r] = tmp; @@ -273,13 +319,15 @@ int partition(void **array, int l, int r, int (*cmp)(void *lv, void *rv)) void quick_sort(void **array, int l, int r, int (*cmp)(void *lv, void *rv)) { int i; - - if(r - l <= 10) + + /* If the array is small, it's better to just use a simple insertion_sort algorithm.*/ + if(r - l <= 20) { insertion_sort(array, l, r, cmp); return; } - + + /* Partition the array and recursively run quick_sort on the two partitions.*/ i = partition(array, l, r, cmp); quick_sort(array, l, i-1, cmp); quick_sort(array, i+1, r, cmp); diff --git a/C/projecteuler.h b/C/projecteuler.h index af9f2f5..c1e6636 100644 --- a/C/projecteuler.h +++ b/C/projecteuler.h @@ -9,6 +9,7 @@ long int lcmm(long int *values, int n); int *sieve(int n); int count_divisors(int n); int find_max_path(int **triang, int n); +int sum_of_divisors(int n); void insertion_sort(void **array, int l, int r, int (*cmp)(void *lv, void *rv)); void quick_sort(void **array, int l, int r, int (*cmp)(void *lv, void *rv)); int is_pandigital(int value, int n); diff --git a/Python/p018.py b/Python/p018.py index 97fdd47..d66de38 100644 --- a/Python/p018.py +++ b/Python/p018.py @@ -1,21 +1,9 @@ #!/usr/bin/python3 from timeit import default_timer - -def sum_triangle(triang, n, i, j, sum_): - global max_ - - if i == n: - if sum_ > max_: - max_ = sum_ - return max_ - - sum_triangle(triang, n, i+1, j, sum_+triang[i][j]) - sum_triangle(triang, n, i+1, j+1, sum_+triang[i][j]) +from projecteuler import find_max_path def main(): - global max_ - start = default_timer() try: @@ -36,8 +24,7 @@ def main(): for i in range(l): triang[i] = list(map(int, triang[i])) - max_ = 0 - sum_triangle(triang, 15, 0, 0, 0) + max_ = find_max_path(triang, 15) end = default_timer() diff --git a/Python/projecteuler.py b/Python/projecteuler.py index fc599e3..0c36d57 100644 --- a/Python/projecteuler.py +++ b/Python/projecteuler.py @@ -80,6 +80,16 @@ def count_divisors(n): return count +def find_max_path(triang, n): + for i in range(n-2, -1, -1): + for j in range(0, i+1): + if triang[i+1][j] > triang[i+1][j+1]: + triang[i][j] = triang[i][j] + triang[i+1][j] + else: + triang[i][j] = triang[i][j] + triang[i+1][j+1] + + return triang[0][0] + def is_pandigital(value, n): i = 0 digits = zeros(n + 1, int)