From 579d86d2674f7994d17253f9fa74c82497b28a35 Mon Sep 17 00:00:00 2001 From: Daniele Fucini Date: Mon, 30 Sep 2019 12:47:03 +0200 Subject: [PATCH] Add more solutions Added solutions for problems 76, 77, 78, 79 and 80 in C and python. --- C/keylog.txt | 50 +++++++++++ C/p076.c | 6 +- C/p077.c | 83 ++++++++++++++++++ C/p078.c | 44 ++++++++++ C/p079.c | 189 +++++++++++++++++++++++++++++++++++++++++ C/p080.c | 80 +++++++++++++++++ C/projecteuler.c | 19 ++++- C/projecteuler.h | 3 +- Python/keylog.txt | 50 +++++++++++ Python/p077.py | 72 ++++++++++++++++ Python/p078.py | 41 +++++++++ Python/p079.py | 105 +++++++++++++++++++++++ Python/p080.py | 51 +++++++++++ Python/projecteuler.py | 12 ++- 14 files changed, 793 insertions(+), 12 deletions(-) create mode 100644 C/keylog.txt create mode 100644 C/p077.c create mode 100644 C/p078.c create mode 100644 C/p079.c create mode 100644 C/p080.c create mode 100644 Python/keylog.txt create mode 100644 Python/p077.py create mode 100644 Python/p078.py create mode 100644 Python/p079.py create mode 100644 Python/p080.py diff --git a/C/keylog.txt b/C/keylog.txt new file mode 100644 index 0000000..41f1567 --- /dev/null +++ b/C/keylog.txt @@ -0,0 +1,50 @@ +319 +680 +180 +690 +129 +620 +762 +689 +762 +318 +368 +710 +720 +710 +629 +168 +160 +689 +716 +731 +736 +729 +316 +729 +729 +710 +769 +290 +719 +680 +318 +389 +162 +289 +162 +718 +729 +319 +790 +680 +890 +362 +319 +760 +316 +729 +380 +319 +728 +716 diff --git a/C/p076.c b/C/p076.c index f412820..cb9e18f 100644 --- a/C/p076.c +++ b/C/p076.c @@ -6,8 +6,8 @@ * 2 + 2 + 1 * 2 + 1 + 1 + 1 * 1 + 1 + 1 + 1 + 1 - -How many different ways can one hundred be written as a sum of at least two positive integers?*/ + * + * How many different ways can one hundred be written as a sum of at least two positive integers?*/ #include #include @@ -32,7 +32,7 @@ int main(int argc, char **argv) /* The number of ways a number can be written as a sum is given by the partition function * (-1 because the partition function includes also the number itself). * The function is implemented in projecteuler.c*/ - n = partition_fn(100, partitions) - 1; + n = partition_fn(100, partitions, -1) - 1; free(partitions); diff --git a/C/p077.c b/C/p077.c new file mode 100644 index 0000000..b5740ce --- /dev/null +++ b/C/p077.c @@ -0,0 +1,83 @@ +/* It is possible to write ten as the sum of primes in exactly five different ways: + * + * 7 + 3 + * 5 + 5 + * 5 + 3 + 2 + * 3 + 3 + 2 + 2 + * 2 + 2 + 2 + 2 + 2 + * + * What is the first value which can be written as the sum of primes in over five thousand different ways?*/ + +#include +#include +#include +#include +#include "projecteuler.h" + +int count(int value, int n, int i, int target); + +int primes[100]; + +int main(int argc, char **argv) +{ + int i, j, n; + double elapsed; + struct timespec start, end; + + clock_gettime(CLOCK_MONOTONIC, &start); + + /* Generate a list of the first 100 primes.*/ + for(i = 0, j = 0; j < 100; i++) + { + if(is_prime(i)) + { + primes[j++] = i; + } + } + + i = 1; + + /* Use a function to count the number of prime partitions for + * each number >= 2 until the one that can be written in over + * 5000 ways is found.*/ + while((n = count(0, 0, 0, ++i)) <= 5000); + + clock_gettime(CLOCK_MONOTONIC, &end); + + elapsed = (end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1000000000; + + printf("Project Euler, Problem 77\n"); + printf("Answer: %d\n", i); + + printf("Elapsed time: %.9lf seconds\n", elapsed); + + return 0; +} + +/* Function using a simple recursive brute force approach + * to find all the partitions.*/ +int count(int value, int n, int i, int target) +{ + int j; + + for(j = i; j < 100; j++) + { + value += primes[j]; + + if(value == target) + { + return n + 1; + } + else if(value > target) + { + return n; + } + else + { + n = count(value, n, j, target); + value -= primes[j]; + } + } + + return n; +} diff --git a/C/p078.c b/C/p078.c new file mode 100644 index 0000000..c7c7a3d --- /dev/null +++ b/C/p078.c @@ -0,0 +1,44 @@ +/* Let p(n) represent the number of different ways in which n coins can be separated into piles. + * For example, five coins can be separated into piles in exactly seven different ways, so p(5)=7. + * + * OOOOO + * OOOO O + * OOO OO + * OOO O O + * OO OO O + * OO O O O + * O O O O O + * + * Find the least value of n for which p(n) is divisible by one million.*/ + +#include +#include +#include +#include "projecteuler.h" + +#define N 1000000 + +int main(int argc, char **argv) +{ + int i = -1; + long int partitions[N] = {0}; + double elapsed; + struct timespec start, end; + + clock_gettime(CLOCK_MONOTONIC, &start); + + /* Using the partition function to calculate the number of partitions, + * giving the result modulo N.*/ + while(partition_fn(++i, partitions, N) != 0); + + clock_gettime(CLOCK_MONOTONIC, &end); + + elapsed = (end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1000000000; + + printf("Project Euler, Problem 78\n"); + printf("Answer: %d\n", i); + + printf("Elapsed time: %.9lf seconds\n", elapsed); + + return 0; +} diff --git a/C/p079.c b/C/p079.c new file mode 100644 index 0000000..78e6756 --- /dev/null +++ b/C/p079.c @@ -0,0 +1,189 @@ +/* A common security method used for online banking is to ask the user for three random characters from a passcode. + * For example, if the passcode was 531278, they may ask for the 2nd, 3rd, and 5th characters; the expected reply would be: 317. + * + * The text file, keylog.txt, contains fifty successful login attempts. + * + * Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible + * secret passcode of unknown length.*/ + +#include +#include +#include +#include "projecteuler.h" + +int compare(void *a, void *b); +int check_passcode(int **passcode, int len, int **logins, int n); + +int main(int argc, char **argv) +{ + int i, j, keylog, len = 4, found = 0, digits[10] = {0}, passcode_digits[10] = {0}, **passcode, **logins; + char line[5]; + FILE *fp; + double elapsed; + struct timespec start, end; + + clock_gettime(CLOCK_MONOTONIC, &start); + + if((fp = fopen("keylog.txt", "r")) == NULL) + { + fprintf(stderr, "Error while opening file %s\n", "keylog.txt"); + return 1; + } + + if((logins = (int **)malloc(50*sizeof(int*))) == NULL) + { + fprintf(stderr, "Error while allocating memory\n"); + return 1; + } + + for(i = 0; i < 50; i++) + { + if((logins[i] = (int *)malloc(3*sizeof(int))) == NULL) + { + fprintf(stderr, "Error while allocating memory\n"); + return 1; + } + } + + i = 0; + + while(fscanf(fp, "%s", line) != EOF) + { + j = 2; + keylog = atoi(line); + + while(keylog > 0) + { + logins[i][j--] = keylog % 10; + /* Check which digits are present in the login attempts.*/ + digits[keylog%10]++; + keylog /= 10; + } + + i++; + } + + fclose(fp); + + j = 0; + for(i = 0; i < 10; i++) + { + /* To generate the passcode, only use the digits present in the + * login attempts.*/ + if(digits[i] > 0) + { + passcode_digits[j++] = i; + } + } + + while(!found) + { + if((passcode = (int **)malloc(len*sizeof(int *))) == NULL) + { + fprintf(stderr, "Error while allocating memory\n"); + return 1; + } + + for(i = 0; i < len; i++) + { + if((passcode[i] = (int *)malloc(sizeof(int))) == NULL) + { + fprintf(stderr, "Error while allocating memory\n"); + return 1; + } + /* For the current length, generate the first passcode with the + * digits in order.*/ + *passcode[i] = passcode_digits[i]; + } + + /* Check if the passcode is compatible with the login attempts.*/ + if(check_passcode(passcode, len, logins, 50)) + { + found = 1; + break; + } + + /* For the given length, check every permutation until the correct + * passcode has been found, or all the permutations have been tried.*/ + while(next_permutation((void **)passcode, len, compare) != -1) + { + if(check_passcode(passcode, len, logins, 50)) + { + printf("Project Euler, Problem 79\n"); + printf("Answer: "); + for(i = 0; i < len; i++) + printf("%d", *passcode[i]); + printf("\n"); + + found = 1; + break; + } + } + + for(i = 0; i < len; i++) + { + free(passcode[i]); + } + free(passcode); + + /* If the passcode has not yet been found, try a longer passcode.*/ + len++; + } + + for(i = 0; i < 50; i++) + { + free(logins[i]); + } + + free(logins); + + clock_gettime(CLOCK_MONOTONIC, &end); + + elapsed = (end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1000000000; + + printf("Elapsed time: %.9lf seconds\n", elapsed); + + return 0; +} + +int compare(void *a, void *b) +{ + int *n1, *n2; + + n1 = (int *)a; + n2 = (int *)b; + + return *n1 - *n2; +} + +int check_passcode(int **passcode, int len, int **logins, int n) +{ + int i, j, k; + + /* For every login attempt, check if all the digits appear in the + * passcode in the correct order. Return 0 if a login attempt + * incompatible with the current passcode is found.*/ + for(i = 0; i < n; i++) + { + k = 0; + for(j = 0; j < len; j++) + { + if(*passcode[j] == logins[i][k]) + { + k++; + + if(k == 3) + { + break; + } + } + } + + if(k < 3) + { + return 0; + } + } + + return 1; +} diff --git a/C/p080.c b/C/p080.c new file mode 100644 index 0000000..1cad2e8 --- /dev/null +++ b/C/p080.c @@ -0,0 +1,80 @@ +/* It is well known that if the square root of a natural number is not an integer, then it is irrational. + * The decimal expansion of such square roots is infinite without any repeating pattern at all. + * + * The square root of two is 1.41421356237309504880..., and the digital sum of the first one hundred decimal digits is 475. + * + * For the first one hundred natural numbers, find the total of the digital sums of the first one hundred decimal digits + * for all the irrational square roots*/ + +#include +#include +#include +#include +#include + +int is_square(int n); + +int main(int argc, char **argv) +{ + int i, j, sum = 0; + char sqrt_digits[104]; + double elapsed; + struct timespec start, end; + mpf_t sqrt; + + clock_gettime(CLOCK_MONOTONIC, &start); + + /* Set the precision to 333 bits (should be enough for 100 decimal digits.*/ + mpf_set_default_prec(333); + mpf_init(sqrt); + + for(i = 2; i < 100; i++) + { + if(is_square(i)) + { + continue; + } + + /* Calculate the square root of the current number with the given precision + * and sum the digits to the total.*/ + mpf_sqrt_ui(sqrt, i); + gmp_sprintf(sqrt_digits, "%.101Ff\n", sqrt); + sum += (sqrt_digits[0] - '0'); + + for(j = 2; j < 101; j++) + { + sum += (sqrt_digits[j] - '0'); + } + } + + mpf_clear(sqrt); + + clock_gettime(CLOCK_MONOTONIC, &end); + + elapsed = (end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1000000000; + + printf("Project Euler, Problem 80\n"); + printf("Answer: %d\n", sum); + + printf("Elapsed time: %.9lf seconds\n", elapsed); + + return 0; +} + +int is_square(int n) +{ + int m; + double p; + + p = sqrt(n); + m = p; + + if(p == m) + { + return 1; + } + else + { + return 0; + } +} diff --git a/C/projecteuler.c b/C/projecteuler.c index fcf63d8..edcf467 100644 --- a/C/projecteuler.c +++ b/C/projecteuler.c @@ -785,7 +785,7 @@ int phi(int n, int *primes) } /* Function implementing the partition function.*/ -long int partition_fn(int n, long int *partitions) +long int partition_fn(int n, long int *partitions, int mod) { int k, limit; long int res = 0; @@ -800,6 +800,7 @@ long int partition_fn(int n, long int *partitions) if(n == 0) { partitions[n] = 1; + return 1; } @@ -816,12 +817,22 @@ long int partition_fn(int n, long int *partitions) { if(k != 0) { - res += pow(-1, k+1) * partition_fn(n-k*(3*k-1)/2, partitions); + res += pow(-1, k+1) * partition_fn(n-k*(3*k-1)/2, partitions, mod); } k++; } - partitions[n] = res; + /* Give the result modulo mod, if mod=!-1, otherwise give the full result.*/ + if(mod != -1) + { + partitions[n] = res % mod; - return res; + return res % mod; + } + else + { + partitions[n] = res; + + return res; + } } diff --git a/C/projecteuler.h b/C/projecteuler.h index ed1ef18..ae8ca4d 100644 --- a/C/projecteuler.h +++ b/C/projecteuler.h @@ -24,6 +24,7 @@ int pell_eq(int i, mpz_t x); int is_semiprime(int n, int *p, int *q, int *primes); int phi_semiprime(int n, int p, int q); int phi(int n, int *primes); -long int partition_fn(int n, long int *partitions); +long int partition_fn(int n, long int *partitions, int mod); +int partition_fn_mpz(int n, mpz_t res, mpz_t *partitions); #endif diff --git a/Python/keylog.txt b/Python/keylog.txt new file mode 100644 index 0000000..41f1567 --- /dev/null +++ b/Python/keylog.txt @@ -0,0 +1,50 @@ +319 +680 +180 +690 +129 +620 +762 +689 +762 +318 +368 +710 +720 +710 +629 +168 +160 +689 +716 +731 +736 +729 +316 +729 +729 +710 +769 +290 +719 +680 +318 +389 +162 +289 +162 +718 +729 +319 +790 +680 +890 +362 +319 +760 +316 +729 +380 +319 +728 +716 diff --git a/Python/p077.py b/Python/p077.py new file mode 100644 index 0000000..27f60cb --- /dev/null +++ b/Python/p077.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +# It is possible to write ten as the sum of primes in exactly five different ways: +# +# 7 + 3 +# 5 + 5 +# 5 + 3 + 2 +# 3 + 3 + 2 + 2 +# 2 + 2 + 2 + 2 + 2 +# +# What is the first value which can be written as the sum of primes in over five thousand different ways? + +from timeit import default_timer +from projecteuler import is_prime + +# Function using a simple recursive brute force approach +# to find all the partitions. +def count(value, n, i, target): + global primes + + for j in range(i, 100): + value = value + primes[j] + + if value == target: + return n + 1 + elif value > target: + return n + else: + n = count(value, n, j, target) + value = value - primes[j] + + return n + +def main(): + start = default_timer() + + global primes + + primes = [0] * 100 + +# Generate a list of the first 100 primes. + i = 0 + j = 0 + + while j < 100: + if is_prime(i): + primes[j] = i + j = j + 1 + i = i + 1 + + i = 2 + +# Use a function to count the number of prime partitions for +# each number >= 2 until the one that can be written in over +# 5000 ways is found. + while True: + n = count(0, 0, 0, i) + + if n > 5000: + break + + i = i + 1 + + end = default_timer() + + print('Project Euler, Problem 77') + print('Answer: {}'.format(i)) + + print('Elapsed time: {:.9f} seconds'.format(end - start)) + +if __name__ == '__main__': + main() diff --git a/Python/p078.py b/Python/p078.py new file mode 100644 index 0000000..ad8e421 --- /dev/null +++ b/Python/p078.py @@ -0,0 +1,41 @@ +#!/usr/bin/python + +# Let p(n) represent the number of different ways in which n coins can be separated into piles. +# For example, five coins can be separated into piles in exactly seven different ways, so p(5)=7. +# +# OOOOO +# OOOO O +# OOO OO +# OOO O O +# OO OO O +# OO O O O +# O O O O O +# +# Find the least value of n for which p(n) is divisible by one million. + +from timeit import default_timer +from projecteuler import partition_fn + +def main(): + start = default_timer() + + N = 1000000 + + partitions = [0] * N + + i = 0 + +# Using the partition function to calculate the number of partitions, +# giving the result modulo N.*/ + while partition_fn(i, partitions, N) != 0: + i = i + 1 + + end = default_timer() + + print('Project Euler, Problem 78') + print('Answer: {}'.format(i)) + + print('Elapsed time: {:.9f} seconds'.format(end - start)) + +if __name__ == '__main__': + main() diff --git a/Python/p079.py b/Python/p079.py new file mode 100644 index 0000000..888ed03 --- /dev/null +++ b/Python/p079.py @@ -0,0 +1,105 @@ +#!/usr/bin/python + +# A common security method used for online banking is to ask the user for three random characters from a passcode. +# For example, if the passcode was 531278, they may ask for the 2nd, 3rd, and 5th characters; the expected reply would be: 317. +# +# The text file, keylog.txt, contains fifty successful login attempts. +# +# Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible +# secret passcode of unknown length. + +from itertools import permutations + +from timeit import default_timer + +def check_passcode(passcode, len_, logins, n): +# For every login attempt, check if all the digits appear in the +# passcode in the correct order. Return 0 if a login attempt +# incompatible with the current passcode is found. + for i in range(n): + k = 0 + for j in range(len_): + if passcode[j] == int(logins[i][k]): + k = k + 1 + + if k == 3: + break + + if k < 3: + return 0 + + return 1 + +def main(): + start = default_timer() + + try: + fp = open('keylog.txt', 'r') + except: + print('Error while opening file {}'.format('keylog.txt')) + exit(1) + + logins = fp.readlines() + + fp.close() + + digits = [0] * 10 + passcode_digits = [0] * 10 + + for i in logins: + keylog = int(i) + +# Check which digits are present in the login attempts. + while True: + digits[keylog%10] = digits[keylog%10] + 1 + keylog = keylog // 10 + + if keylog == 0: + break + + j = 0 + for i in range(10): +# To generate the passcode, only use the digits present in the +# login attempts. + if digits[i] > 0: + passcode_digits[j] = i + j = j + 1 + + found = 0 + len_ = 4 + + while not found: + passcode = [0] * len_ + +# For the current length, generate the first passcode with the +# digits in order. + for i in range(len_): + passcode[i] = passcode_digits[i] + +# Check if the passcode is compatible with the login attempts. + if check_passcode(passcode, len_, logins, 50): + found = 1 + break + +# For the given length, check every permutation until the correct +# passcode has been found, or all the permutations have been tried. + passcodes = permutations(passcode, len_) + + for i in passcodes: + if check_passcode(i, len_, logins, 50): + found = 1 + res = ''.join(map(str, i)) + break + +# If the passcode has not yet been found, try a longer passcode. + len_ = len_ + 1 + + end = default_timer() + + print('Project Euler, Problem 79') + print('Answer: {}'.format(res)) + + print('Elapsed time: {:.9f} seconds'.format(end - start)) + +if __name__ == '__main__': + main() diff --git a/Python/p080.py b/Python/p080.py new file mode 100644 index 0000000..a67e7d2 --- /dev/null +++ b/Python/p080.py @@ -0,0 +1,51 @@ +#!/usr/bin/python + +# It is well known that if the square root of a natural number is not an integer, then it is irrational. +# The decimal expansion of such square roots is infinite without any repeating pattern at all. +# +# The square root of two is 1.41421356237309504880..., and the digital sum of the first one hundred decimal digits is 475. +# +# For the first one hundred natural numbers, find the total of the digital sums of the first one hundred decimal digits +# for all the irrational square roots + +from mpmath import sqrt, mp + +from timeit import default_timer + +def is_square(n): + p = sqrt(n) + m = int(p) + + if p == m: + return True + else: + return False + +def main(): + start = default_timer() + +# Set the precision to 100 digits + mp.dps = 102 + + sum_ = 0 + + for i in range(2, 100): + if not is_square(i): +# Calculate the square root of the current number with the given precision +# and sum the digits to the total. + root = str(sqrt(i)) + + sum_ = sum_ + int(root[0]) + + for j in range(2, 101): + sum_ = sum_ + int(root[j]) + + end = default_timer() + + print('Project Euler, Problem 80') + print('Answer: {}'.format(sum_)) + + print('Elapsed time: {:.9f} seconds'.format(end - start)) + +if __name__ == '__main__': + main() diff --git a/Python/projecteuler.py b/Python/projecteuler.py index 00c8461..ed92478 100644 --- a/Python/projecteuler.py +++ b/Python/projecteuler.py @@ -399,7 +399,7 @@ def phi(n, primes): return ph # Function implementing the partition function. -def partition_fn(n, partitions): +def partition_fn(n, partitions, mod=-1): # The partition function for negative numbers is 0 by definition. if n < 0: return 0 @@ -422,8 +422,12 @@ def partition_fn(n, partitions): res = res + pow(-1, k+1) * partition_fn(n-k*(3*k-1)//2, partitions) k = k + 1 - partitions[n] = res - - return int(res) +# Give the result modulo mod, if mod!=-1, otherwise give the full result. + if mod != -1: + partitions[n] = res % mod + return res % mod + else: + partitions[n] = int(res) + return int(res)