As I make my way through these Project Euler problems, I’m noticing some trends in calculations and questions I keep needing to ask about a particular number (is this number even? is this number prime?). It’s time to work smarter and simply save these functions to a package so I can repeatedly use them moving forward, which will also give me some motivation to spend some time heavily optimizing the code as I know it will be used again in the future. I’ll keep this post updated with the functions I use in solving Euler problems.
CREATE OR REPLACE PACKAGE EULER.EULER_PKG AS TYPE int_array IS VARRAY(2000) OF INTEGER; FUNCTION is_even(num_in NUMBER) RETURN BOOLEAN; FUNCTION is_prime(num_in NUMBER) RETURN BOOLEAN; FUNCTION get_divisors(num_in NUMBER) RETURN int_array; FUNCTION get_divisors_count(num_in NUMBER) RETURN PLS_INTEGER; FUNCTION get_num_from_list(line_in VARCHAR2, num_in INTEGER) RETURN VARCHAR2; PROCEDURE set_num_from_list(line_in IN OUT VARCHAR2, num_in INTEGER, new_val VARCHAR2); END EULER_PKG; / CREATE OR REPLACE PACKAGE BODY EULER.EULER_PKG AS FUNCTION is_even(num_in NUMBER) RETURN BOOLEAN IS BEGIN IF REMAINDER(num_in, 2) = 0 THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END is_even; FUNCTION is_prime(num_in NUMBER) RETURN BOOLEAN IS i PLS_INTEGER := 5; BEGIN IF num_in <= 3 THEN RETURN num_in > 1; ELSIF REMAINDER(num_in,2) = 0 OR REMAINDER(num_in,3) = 0 THEN RETURN FALSE; ELSE WHILE i * i <= num_in LOOP IF REMAINDER(num_in,i) = 0 OR REMAINDER(num_in, i+2) = 0 THEN RETURN FALSE; ELSE i := i + 6; END IF; END LOOP; RETURN TRUE; END IF; END is_prime; FUNCTION get_divisors(num_in NUMBER) RETURN int_array IS divisors_array int_array := int_array(); BEGIN IF num_in > 1 THEN FOR i IN 1..ROUND(num_in/2,0) LOOP IF REMAINDER(num_in,i) = 0 THEN divisors_array.extend; divisors_array(divisors_array.count) := i; --dbms_output.put_line('num_in: '||num_in||' i: '||i); END IF; END LOOP; END IF; divisors_array.extend; divisors_array(divisors_array.last) := num_in; return divisors_array; END get_divisors; FUNCTION get_divisors_count(num_in NUMBER) RETURN PLS_INTEGER IS divisors PLS_INTEGER := 0; BEGIN IF num_in > 1 THEN FOR i IN 1..ROUND(SQRT(num_in),0) LOOP IF REMAINDER(num_in,i) = 0 THEN divisors := divisors + 2; END IF; END LOOP; END IF; return divisors; END get_divisors_count; FUNCTION get_num_from_list(line_in VARCHAR2, num_in INTEGER) RETURN VARCHAR2 AS --grabs a number from a line of numbers delimited by spaces BEGIN RETURN regexp_substr(line_in, '\S+', 1, num_in); END; PROCEDURE set_num_from_list(line_in IN OUT VARCHAR2, num_in INTEGER, new_val VARCHAR2) AS BEGIN line_in := REGEXP_REPLACE(line_in, '\S+', new_val, 1, num_in); END; END EULER_PKG; /
The is_even
logic is easy enough to understand, and will now add some clarity as to why we’re diving by 2 and checking for a remainder. The is_prime
logic bears some explaining. I found the formula from Wikipedia’s Primality test page and converted the code to PL/SQL. I don’t fully understand the math behind why it works, but I understand the logic it goes through and tested many prime and non-prime numbers to confirm it’s fast and correct.
get_num_from_list
and set_num_from_list
help manage the space-delimited lines Project Euler often uses. get_divisors
and get_divisors_count
do what you’d expect, these come up fairly often in the problems and are now easy to re-use.