├── README.md ├── Top_Coding_Interview_Algorithms.pdf ├── algo ├── fizzbuzz.rb ├── merge.rb ├── shipping.yaml ├── sort.rb └── yaml_parsing.rb ├── codeeval ├── 001fizzbuzz.rb ├── 001text.txt ├── 003prime_palindrome.rb ├── 004sum_of_primes.rb ├── 020lowercase.rb ├── 020text.txt ├── 025odd_numbers.rb ├── 035email_validation.rb ├── 035text.txt ├── 042text.txt ├── 042ugly_numbers.rb ├── 045reverse_and_add.rb ├── 045text.txt ├── 046prime_numbers.rb ├── 046text.txt ├── 062n_mod_m.rb ├── 062text.txt ├── 067hex_to_decimal.rb ├── 089pass_triangle.rb ├── 089triangle.txt ├── 091simple_sorting.rb ├── 091text.txt └── 096swap_case.rb ├── euler ├── 001_multiples_3_and_5.rb ├── 002_fibonacci.rb ├── 003_prime_factor.rb ├── 004_palindrome.rb ├── 005_smallest_multiple.rb ├── 006_square_sum_difference.rb ├── 007_10001st_prime.rb ├── 008_largest_product.rb ├── 009_Pythagorean_triplet.rb ├── 010_summation_of_primes.rb ├── 011_largest_product_in_grid.rb ├── 012_triangular_fastest-way.rb ├── 012_triangular_num-brute-force.rb ├── 012_triangular_num-print-factors.rb ├── 013_large_sum.rb ├── 014_longest_collatz_sequence.rb ├── 015_lattice_paths.rb ├── 016_power_digit_sum.rb ├── 017_number_letter_counts.rb ├── 018_maximum_path_sum1.rb ├── 019_counting_sundays.rb ├── 020_factorial_digit_sum.rb ├── 021_amicable_numbers.rb ├── 022_names_scores.rb ├── 023_non-abundant_sums.rb ├── 024_lexicographic_permutations.rb ├── 025_1000-digit_fibonacci.rb ├── 026_reciprocal_cycles.rb ├── 027_quadratic_primes.rb ├── 028_number_spiral_diagonals.rb ├── 029_distinct_powers.rb ├── 030_digit_fifth_powers.rb ├── 031_coin_sums.rb ├── 031_test.rb ├── 032_pandigital_products.rb ├── 033_digit_canceling_fractions.rb ├── 034_digit_factorials.rb ├── 035_circular_primes.rb ├── 036_double_base_palindromes.rb ├── 040_champernowne_constant.rb ├── 045_triangular_pentagonal_hexagonal.rb ├── 047_distinct_primes_factors.rb ├── 048_self_powers.rb ├── 049_prime_permutations.rb ├── 050_consecutive_prime_sum.rb ├── 052_permuted_multiples.rb ├── 053_combinatoric_selections.rb ├── 067_maximum_path_sum2.rb ├── 079_passcode_derivation.rb ├── 081_data.txt ├── 081_matrix.txt ├── 081_path_sum_2_ways.rb ├── 082_path_sum_3_ways.rb ├── 083_path_sum_4_ways.rb ├── 092_square_digit_chains.rb ├── 421_prime_factors.rb ├── Permutation Generation Methods - Sedgewick.pdf ├── Permutation Powerpoint - Sedgewick.pdf ├── bsearch.rb ├── change_to_different_base.rb ├── factors.dat ├── factors.output ├── factors_caching.rb ├── factors_compare_speeds.rb ├── gcd_lcm.rb ├── is_prime.rb ├── jsonprimes.txt ├── keylog.txt ├── names.txt ├── perm2.rb ├── permutation.rb ├── priority_queue.rb ├── rename.rb ├── rspec_maker.rb ├── rspec_maker_input.txt ├── sieve_of_eratosthenes.rb ├── sieveprimes-serialized.txt ├── spec │ ├── factors.dat │ ├── factors_caching_spec.rb │ ├── roman_numbers_spec.rb │ └── spec_helper.rb ├── tower_of_hanoi.rb ├── triangle.txt └── triangle2.txt └── graph_algorithms ├── data ├── directed.txt ├── directed2.txt ├── directed3.txt ├── directed4.txt ├── directed_matrix.txt ├── directed_p1.txt ├── directed_p2.txt ├── directed_p3.txt ├── directed_scc.txt ├── kosaraju.txt ├── rspec_screen_output.txt ├── tinyDAG.txt ├── tinyDG.txt ├── tinyG.txt ├── undirected.txt ├── undirected_p21.txt ├── undirected_p22.txt └── undirected_p23.txt ├── lib ├── graph.rb ├── neighbor.rb ├── priority_queue.rb └── vertex.rb ├── rspec_screen_output.txt └── spec ├── graph_spec.rb ├── priority_queue_spec.rb └── spec_helper.rb /README.md: -------------------------------------------------------------------------------- 1 | Finished solving 50 Project Euler problems!!! (http://projecteuler.net/problems) (5/6/2014) Only 5.88%, 40759 out of 693691 software people worldwide, have done this. 2 | 3 | Graph algorithms (see my 900 lines of code, with full RSpec tests): https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/graph_algorithms 4 | RSpec output: https://github.com/rayning0/ProjectEuler-and-Algorithms/blob/master/graph_algorithms/data/rspec_screen_output.txt 5 | 6 | CodeEval (https://www.codeeval.com/open_challenges/), and algorithm problems. See /euler, /codeeval, and /algo directories. Please do NOT see my solution for a problem unless you have first tried it yourself. 7 | 8 | Also, see my repo on RubyQuiz, InterviewCake, and other challenge questions: https://github.com/rayning0/rubyquiz. 9 | 10 | Test-First Ruby problems: https://github.com/rayning0/Test-First-Ruby 11 | 12 | App Academy problems: https://github.com/rayning0/App-Academy-Practice-Problems 13 | -------------------------------------------------------------------------------- /Top_Coding_Interview_Algorithms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayning0/ProjectEuler-and-Algorithms/0a9fbbc1b5c8fd51ec2d33b8a7915c12128f5bd1/Top_Coding_Interview_Algorithms.pdf -------------------------------------------------------------------------------- /algo/fizzbuzz.rb: -------------------------------------------------------------------------------- 1 | # The infamous FizzBuzz question 2 | # http://www.codinghorror.com/blog/2007/02/why-cant-programmers-program.html 3 | # "Write a program that prints the numbers from 1 to 100. But for multiples of 3 print "Fizz" instead of the number and for multiples of 5 print "Buzz". For multiples of both 3 and 5 print "FizzBuzz". 4 | 5 | (1..100).each do |n| 6 | output = '' 7 | output << "Fizz" if n % 3 == 0 8 | output << "Buzz" if n % 5 == 0 9 | output << n.to_s if output.empty? 10 | puts output 11 | end 12 | -------------------------------------------------------------------------------- /algo/merge.rb: -------------------------------------------------------------------------------- 1 | def merge_sort(list) 2 | return list if list.length <= 1 3 | 4 | middle = list.length / 2 5 | left = list[0..middle - 1] 6 | right = list[middle..-1] 7 | left = merge_sort(left) 8 | right = merge_sort(right) 9 | merge(left, right) 10 | end 11 | 12 | def merge(left, right) 13 | merged = [] 14 | 15 | while !left.empty? && !right.empty? 16 | if left.first <= right.first 17 | merged << left.shift 18 | else 19 | merged << right.shift 20 | end 21 | end 22 | 23 | # if right empty, add remaining left part to merged 24 | unless left.empty? 25 | merged += left 26 | end 27 | 28 | # if left empty, add remaining right part to merged 29 | unless right.empty? 30 | merged += right 31 | end 32 | merged 33 | end 34 | 35 | p merge_sort([11, 15, 4, 2, 19,13, 4, 10, 7, 19]) -------------------------------------------------------------------------------- /algo/shipping.yaml: -------------------------------------------------------------------------------- 1 | invoice: 34843 2 | date : 2001-01-23 3 | billto: &id001 4 | given : Chris 5 | family : Dumars 6 | address: 7 | lines: | 8 | 458 Walkman Dr. 9 | Suite #292 10 | city : Royal Oak 11 | state : MI 12 | postal : 48046 13 | shipto: *id001 14 | product: 15 | - sku : BL394D 16 | quantity : 4 17 | description : Basketball 18 | price : 450.00 19 | - sku : BL4438H 20 | quantity : 1 21 | description : Super Hoop 22 | price : 2392.00 23 | tax : 251.42 24 | total: 4443.52 25 | comments: > 26 | Late afternoon is best. 27 | Backup contact is Nancy 28 | Billsmer @ 338-4338. -------------------------------------------------------------------------------- /algo/sort.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan - My own Ruby versions of different sorting algorithms. I read descriptions in Wikipedia and coded these independently 2 | # http://en.wikipedia.org/wiki/Sorting_algorithm 3 | # and http://www.sorting-algorithms.com/ are good sorting algorithm references 4 | 5 | require 'benchmark' 6 | 7 | # Keeps comparing/swapping adjacent elements from left to right, repeatedly 8 | def bubble_sort(list) 9 | loop do 10 | swapped = false 11 | 12 | (list.size - 1).times do |i| 13 | if list[i] > list[i+1] # if current > next 14 | list[i], list[i+1] = list[i+1], list[i] # swap current and next values 15 | swapped = true 16 | end 17 | end 18 | 19 | # if swap happened, return to start and scans whole list again from left to right 20 | # else we're done 21 | break unless swapped 22 | end 23 | list 24 | end 25 | 26 | # Starts from 2nd value and moves left, comparing it until it's < anything to the right. Repeats. 27 | def insertion_sort(list) 28 | for i in 1..list.size - 1 29 | j = i - 1 30 | while j >=0 and list[j+1] < list[j] # if current < previous 31 | list[j], list[j+1] = list[j+1], list[j] # swap previous and current values. faster. 32 | 33 | # list[j+1] = list[j] --- typical book way. slower. 34 | j -= 1 35 | end 36 | # list[j+1] = current --- typical book way. slower. 37 | end 38 | list 39 | end 40 | 41 | # usually the slowest sort of all. does only n swaps. useful if swapping is expensive. 42 | def selection_sort(list) 43 | (list.size - 1).times do |i| 44 | min = i # starting min value 45 | for j in i+1..list.size - 1 # searches all values to right to find min 46 | min = j if list[j] < list[min] # resets min 47 | end 48 | list[min], list[i] = list[i], list[min] # swaps min val with past min val, ignoring already sorted part 49 | end 50 | list 51 | end 52 | 53 | # same as insertion sort, but with a gap, which keeps getting cut in half 54 | def shell_sort(list) 55 | gap = list.size / 2 56 | while gap > 0 do 57 | for i in gap..list.size - 1 58 | j = i 59 | while j >= gap and list[j] < list[j-gap] # if current < previous. previous = gap values to the left 60 | list[j], list[j-gap] = list[j-gap], list[j] # swap current and previous 61 | j -= gap # keep swapping current and previous until previous > current 62 | end 63 | end 64 | gap = gap / 2 65 | end 66 | list 67 | end 68 | 69 | # divide-and-conquer. recursive. 70 | # divides whole array into 1-element arrays, then merges them back (while sorting), 71 | # doubling size each time 72 | def merge_sort(list) 73 | return list if list.length <= 1 74 | 75 | middle = list.length / 2 76 | left = list[0..middle - 1] 77 | right = list[middle..-1] 78 | left = merge_sort(left) 79 | right = merge_sort(right) 80 | merge(left, right) 81 | end 82 | 83 | def merge(left, right) 84 | merged = [] 85 | 86 | while !left.empty? && !right.empty? 87 | if left.first <= right.first 88 | merged << left.shift 89 | else 90 | merged << right.shift 91 | end 92 | end 93 | 94 | # if right empty, add remaining left part to merged 95 | unless left.empty? 96 | merged += left 97 | end 98 | 99 | # if left empty, add remaining right part to merged 100 | unless right.empty? 101 | merged += right 102 | end 103 | merged 104 | end 105 | 106 | =begin ---- shell sort ---- 107 | a = [15, 8, 4, 12, 4, 3, 10, 6, 2, 0] 108 | gap = 5 109 | i = 5 110 | j = 5. new list = [3, 8, 4, 12, 4, 15, 10, 6, 2, 0] 111 | i = 6 112 | i = 7 113 | i = 8 114 | j = 8. new list = [3, 8, 4, 2, 4, 15, 10, 6, 12, 0] 115 | i = 9 116 | j = 9. new list = [3, 8, 4, 2, 0, 15, 10, 6, 12, 4] 117 | gap = 2 118 | i = 2 119 | i = 3 120 | j = 3. new list = [3, 2, 4, 8, 0, 15, 10, 6, 12, 4] 121 | i = 4 122 | j = 4. new list = [3, 2, 0, 8, 4, 15, 10, 6, 12, 4] 123 | j = 2. new list = [0, 2, 3, 8, 4, 15, 10, 6, 12, 4] 124 | i = 5 125 | i = 6 126 | i = 7 127 | j = 7. new list = [0, 2, 3, 8, 4, 6, 10, 15, 12, 4] 128 | j = 5. new list = [0, 2, 3, 6, 4, 8, 10, 15, 12, 4] 129 | i = 8 130 | i = 9 131 | j = 9. new list = [0, 2, 3, 6, 4, 8, 10, 4, 12, 15] 132 | j = 7. new list = [0, 2, 3, 6, 4, 4, 10, 8, 12, 15] 133 | j = 5. new list = [0, 2, 3, 4, 4, 6, 10, 8, 12, 15] 134 | gap = 1 135 | i = 1 136 | i = 2 137 | i = 3 138 | i = 4 139 | i = 5 140 | i = 6 141 | i = 7 142 | j = 7. new list = [0, 2, 3, 4, 4, 6, 8, 10, 12, 15] 143 | i = 8 144 | i = 9 145 | Sorted 10 values. Your sort matches result of Ruby 'sort' method. 146 | s = [0, 2, 3, 4, 4, 6, 8, 10, 12, 15] 147 | =end 148 | 149 | #puts "Enter list of numbers to sort, separated by spaces:" 150 | #array = gets.chomp.split.map {|v| v.to_i } # makes array of integers 151 | 152 | n = 10000 153 | a = Array.new(n) { rand(n) } # make array of random numbers 154 | 155 | sorted = merge_sort(a) 156 | if sorted == a.sort 157 | puts "Sorted #{n} values. Your sort matches result of Ruby 'sort' method." 158 | # puts "s = #{sorted}" 159 | else 160 | puts "Sort is wrong. Check your code." 161 | end 162 | 163 | Benchmark.bm(7) do |x| 164 | x.report("shell sort: ") {shell_sort(a)} 165 | x.report("bubble sort: ") {bubble_sort(a)} 166 | x.report("insert sort: ") {insertion_sort(a)} 167 | x.report("select sort: ") {selection_sort(a)} 168 | x.report("merge sort: ") {merge_sort(a)} 169 | x.report("ruby sort: ") {a.sort} 170 | end 171 | 172 | =begin 173 | Ruby 2.0.0 174 | Comparing speed of all sorting techniques. Output. The last column tells # of seconds. Select sort is the slowest. 175 | 176 | Sorted 10000 values. Your sort matches result of Ruby 'sort' method. 177 | user system total real 178 | shell sort: 0.120000 0.000000 0.120000 ( 0.128072) 179 | bubble sort: 0.000000 0.000000 0.000000 ( 0.001883) 180 | insert sort: 0.000000 0.000000 0.000000 ( 0.002288) 181 | select sort: 10.300000 0.080000 10.380000 ( 11.138458) 182 | merge sort: 0.040000 0.000000 0.040000 ( 0.045420) 183 | ruby sort: 0.000000 0.000000 0.000000 ( 0.000173) 184 | 185 | =end 186 | -------------------------------------------------------------------------------- /algo/yaml_parsing.rb: -------------------------------------------------------------------------------- 1 | # Interview question: 2 | # Given the a Yaml file of the format found in shipping.yaml parse the file so that we can access it in Ruby. 3 | # 4 | # Your parsing code should return an object that allows you to access attributes using the [] operator: 5 | # 6 | # data["product"].first["sku"] => "BL394D" 7 | # The returned object should also allow you to access attributes using method calls: 8 | # 9 | # data.product.first.sku => "BL394D" 10 | 11 | -------------------------------------------------------------------------------- /codeeval/001fizzbuzz.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan, rayning@yahoo.com 2 | # https://www.codeeval.com/open_challenges/1/ 3 | # My Project Euler solutions on GitHub: 4 | # https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/euler 5 | # http://www.linkedin.com/pub/raymond-gan/1/801/ba/ 6 | 7 | File.open(ARGV[0]).each_line do |line| 8 | a, b, c = line.split.map(&:to_i) 9 | str = '' 10 | 11 | (1..c).each do |n| 12 | str += "F" if n % a == 0 13 | str += "B" if n % b == 0 14 | str += n.to_s if !(n % a == 0 || n % b == 0) 15 | str += " " 16 | end 17 | 18 | puts str.strip 19 | end 20 | 21 | 22 | -------------------------------------------------------------------------------- /codeeval/001text.txt: -------------------------------------------------------------------------------- 1 | 3 5 10 2 | 2 7 15 -------------------------------------------------------------------------------- /codeeval/003prime_palindrome.rb: -------------------------------------------------------------------------------- 1 | def is_prime?(n) 2 | return true if n.between?(2, 3) 3 | return false if (n.even? || n % 3 == 0 || n <= 1) 4 | 5 | j, w = 5, 2 6 | 7 | while j * j <= n 8 | return false if n % j == 0 9 | j += w 10 | w = 6 - w 11 | end 12 | 13 | true 14 | end 15 | 16 | 997.step(2, -2) do |p| 17 | if is_prime?(p) && p.to_s == p.to_s.reverse 18 | puts p 19 | break 20 | end 21 | end -------------------------------------------------------------------------------- /codeeval/004sum_of_primes.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan, rayning@yahoo.com 2 | # https://www.codeeval.com/open_challenges/4/ 3 | # My Project Euler solutions on GitHub: 4 | # https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/euler 5 | # http://www.linkedin.com/pub/raymond-gan/1/801/ba/ 6 | 7 | # Sieve of Eratosthenes 8 | def sieve(n) # quickly makes array of all primes from 2 to n 9 | s = 3.step(n, 2).to_a # make array of odd integers from 3 to n. Skip evens. 10 | s.each do |p| 11 | next if p.nil? # go to next element if p has been marked empty 12 | break if p * p > n # p only needs to go up to sqrt(n) 13 | 14 | k, pval = 0, 0 15 | 16 | while pval < n 17 | pval = p * (p + k) # jump forward 2p at a time: p*p, p*p + 2p, p*p + 4p, etc. 18 | 19 | # Set all those multiples to nil. i = (pval - 3)/2 translates pvals to index i 20 | 21 | s[(pval - 3) / 2] = nil 22 | k += 2 23 | end 24 | 25 | end 26 | s.compact! # removes all nil elements from array 27 | s.unshift(2).sort # adds 2 as 1st element 28 | end 29 | 30 | s = sieve(8000) 31 | 32 | puts s[0..999].inject(:+) 33 | -------------------------------------------------------------------------------- /codeeval/020lowercase.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan, rayning@yahoo.com 2 | # https://www.codeeval.com/open_challenges/20/ 3 | # My Project Euler solutions on GitHub: 4 | # https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/euler 5 | # http://www.linkedin.com/pub/raymond-gan/1/801/ba/ 6 | 7 | File.open(ARGV[0]).each_line do |line| 8 | puts line.downcase 9 | end -------------------------------------------------------------------------------- /codeeval/020text.txt: -------------------------------------------------------------------------------- 1 | HELLO CODEEVAL 2 | This is some text 3 | Blah Blah BLAH -------------------------------------------------------------------------------- /codeeval/025odd_numbers.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan, rayning@yahoo.com 2 | # https://www.codeeval.com/open_challenges/25/ 3 | # My Project Euler solutions on GitHub: 4 | # https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/euler 5 | 6 | (1..99).each do |n| 7 | puts n if !n.even? 8 | end -------------------------------------------------------------------------------- /codeeval/035email_validation.rb: -------------------------------------------------------------------------------- 1 | email = IO.readlines(ARGV[0]).map(&:strip) 2 | email.each do |e| 3 | if e =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 4 | puts 'true' 5 | else 6 | puts 'false' 7 | end 8 | end -------------------------------------------------------------------------------- /codeeval/035text.txt: -------------------------------------------------------------------------------- 1 | foo@bar.com 2 | this is not an email id 3 | admin#codeeval.com 4 | good123@bad.com -------------------------------------------------------------------------------- /codeeval/042text.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 9 3 | 011 4 | 12345 -------------------------------------------------------------------------------- /codeeval/042ugly_numbers.rb: -------------------------------------------------------------------------------- 1 | def ugly?(n) 2 | for d in [2, 3, 5, 7] 3 | return true if n % d == 0 4 | end 5 | false 6 | end 7 | 8 | File.open(ARGV[0]).each_line do |n| 9 | c = 0 10 | n = n.strip.split('') 11 | len = n.size 12 | if len == 1 13 | if ugly?(n[0].to_i) 14 | puts 1 15 | else 16 | puts 0 17 | end 18 | next 19 | end 20 | 21 | expr, expr2 = [], [] 22 | sign = ["+", "-", ""] 23 | # makes nested array of all permutations of sign, for len - 1 spots 24 | s = sign.repeated_permutation(len - 1).to_a 25 | 26 | (0..(3**(len - 1) - 1)).each do |e| 27 | expr[e] = n[0] 28 | 29 | (1..len - 1).each do |i| 30 | # n[0] + s[0][0] + n[1] + s[0][1] + n[2] + ... 31 | expr[e] += s[e][i - 1] + n[i] 32 | end 33 | 34 | if expr[e].split(/[+-]/).any?{|x| x =~ /^0+/} # if expr has leading 0's in any term 35 | e2 = expr[e].split(/[+-]/).map{|x| x = Integer(x, 10).to_s} # Turns everything into base 10 number 36 | expr2[e] = e2[0] 37 | badjust = 0 38 | 39 | (1..e2.size - 1).each do |i| 40 | badjust += 1 if s[e][i - 1] == '' 41 | expr2[e] += s[e][i - 1 + badjust] + e2[i] # adjusts symbol location by # of blanks. 42 | end 43 | 44 | else 45 | expr2[e] = expr[e] 46 | end 47 | 48 | c += 1 if ugly?(eval(expr2[e])) 49 | end 50 | 51 | puts c 52 | end -------------------------------------------------------------------------------- /codeeval/045reverse_and_add.rb: -------------------------------------------------------------------------------- 1 | # https://www.codeeval.com/open_challenges/45/ 2 | 3 | num = IO.readlines(ARGV[0]).map(&:strip) 4 | 5 | num.each do |n| 6 | c = 1 7 | while true 8 | sum = n.to_i + n.reverse.to_i 9 | break if sum.to_s == sum.to_s.reverse 10 | c += 1 11 | n = sum.to_s 12 | end 13 | puts "#{c} #{sum}" 14 | end 15 | -------------------------------------------------------------------------------- /codeeval/045text.txt: -------------------------------------------------------------------------------- 1 | 195 2 | 50 3 | 2342 4 | 3737 5 | 353 6 | 7 7 | 128 -------------------------------------------------------------------------------- /codeeval/046prime_numbers.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan, rayning@yahoo.com 2 | # My Project Euler solutions: 3 | # https://github.com/rayning0/ProjectEuler-and-Algorithms/tree/master/euler 4 | # http://www.linkedin.com/pub/raymond-gan/1/801/ba/ 5 | 6 | # Sieve of Eratosthenes 7 | def sieve(n) 8 | s = 3.step(n, 2).to_a 9 | s.each do |p| 10 | next if p.nil? 11 | break if p * p > n 12 | 13 | k, pval = 0, 0 14 | 15 | while pval < n 16 | pval = p * (p + k) 17 | s[(pval - 3) / 2] = nil 18 | k += 2 19 | end 20 | 21 | end 22 | s.compact! 23 | s.unshift(2).sort 24 | end 25 | 26 | num = IO.readlines(ARGV[0]).map(&:strip) 27 | num.each do |n| 28 | s = sieve(n.to_i) 29 | puts "#{s.to_s[1..-2].delete(' ')}" 30 | end 31 | 32 | 33 | -------------------------------------------------------------------------------- /codeeval/046text.txt: -------------------------------------------------------------------------------- 1 | 10 2 | 20 3 | 100 -------------------------------------------------------------------------------- /codeeval/062n_mod_m.rb: -------------------------------------------------------------------------------- 1 | File.open(ARGV[0]).each_line do |line| 2 | spl = line.split(',') 3 | n = spl[0].to_i 4 | m = spl[1].to_i 5 | puts n - (n / m) * m 6 | end -------------------------------------------------------------------------------- /codeeval/062text.txt: -------------------------------------------------------------------------------- 1 | 20,6 2 | 2,3 3 | 60,7 4 | 103,29 -------------------------------------------------------------------------------- /codeeval/067hex_to_decimal.rb: -------------------------------------------------------------------------------- 1 | File.open(ARGV[0]).each_line do |n| 2 | puts n.strip.to_i(16) 3 | end -------------------------------------------------------------------------------- /codeeval/089pass_triangle.rb: -------------------------------------------------------------------------------- 1 | # https://www.codeeval.com/open_challenges/89/ 2 | 3 | t = IO.readlines(ARGV[0]) 4 | 5 | t.map!(&:chomp).map!(&:split) 6 | 7 | t.each do |a| 8 | a.map!(&:to_i) 9 | end 10 | 11 | sum, sumup, sumdiag = 0, 0, 0 12 | 13 | # Add upwards, from bottom to top of triangle. Dynamic programming. 14 | (t.size - 1).downto(1).each do |row| 15 | (0..row - 1).each do |col| 16 | sumup = t[row][col] + t[row - 1][col] 17 | sumdiag = t[row][col + 1] + t[row - 1][col] 18 | sumup > sumdiag ? t[row - 1][col] = sumup : t[row - 1][col] = sumdiag 19 | end 20 | end 21 | 22 | puts "#{t[0][0]}" -------------------------------------------------------------------------------- /codeeval/091simple_sorting.rb: -------------------------------------------------------------------------------- 1 | # https://www.codeeval.com/open_challenges/91/ 2 | 3 | File.open(ARGV[0]).each_line do |n| 4 | n = n.strip.split.map(&:to_f).sort 5 | puts n.map{|x| sprintf "%.3f" % x}.join(' ') 6 | end 7 | 8 | =begin 9 | 10 | -38.797 7.581 14.354 70.920 90.374 99.323 11 | -55.552 -37.507 -3.263 27.999 40.079 65.213 12 | 13 | =end -------------------------------------------------------------------------------- /codeeval/091text.txt: -------------------------------------------------------------------------------- 1 | 70.920 -38.797 14.354 99.323 90.374 7.581 2 | -37.507 -3.263 40.079 27.999 65.213 -55.552 -------------------------------------------------------------------------------- /codeeval/096swap_case.rb: -------------------------------------------------------------------------------- 1 | File.open(ARGV[0]).each_line do |line| 2 | puts line.swapcase 3 | end -------------------------------------------------------------------------------- /euler/001_multiples_3_and_5.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=1 2 | # Write sum of all integers < 1000 that are multiples of 3 and/or 5 3 | 4 | sum = 0 5 | i = 3 6 | while i < 1000 7 | sum += i if (i % 3 == 0) || (i % 5 == 0) 8 | i += 1 9 | end 10 | puts sum -------------------------------------------------------------------------------- /euler/002_fibonacci.rb: -------------------------------------------------------------------------------- 1 | # Fibonacci sequence: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... 2 | # Find sum of even-valued terms in Fibonacci sequence whose values < 4 million 3 | # http://projecteuler.net/problem=2 4 | 5 | sum, f0, f1 = 0, 1, 1 6 | 7 | while true 8 | f2 = f0 + f1 9 | sum += f2 if f2.even? 10 | 11 | if f2 > 4e6 12 | puts "Sum of all even-valued terms in Fibonacci sequence < 4 million = #{sum}" 13 | exit 14 | end 15 | 16 | f0 = f1 17 | f1 = f2 18 | end 19 | 20 | =begin ---- Using arrays ------ 21 | sum = 0 22 | i = 2 23 | f = [] 24 | f[0], f[1] = 1, 1 25 | 26 | while true 27 | f[i] = f[i - 1] + f[i - 2] 28 | sum += f[i] if f[i].even? 29 | puts "i = #{i}, f[i] = #{f[i]}, sum = #{sum}" 30 | if f[i] > 4e6 31 | print f 32 | puts 33 | puts "Sum of all even-valued terms in Fibonacci sequence < 4 million = #{sum}" 34 | exit 35 | end 36 | i += 1 37 | end 38 | =end 39 | -------------------------------------------------------------------------------- /euler/003_prime_factor.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=3 2 | 3 | def largest_prime_factor(n) 4 | #factors = [] 5 | f = 2 6 | while f * f <= n 7 | if n % f == 0 8 | #factors << f 9 | n /= f 10 | else 11 | f += 1 12 | end 13 | end 14 | # factors << n 15 | # factors 16 | n 17 | end 18 | 19 | =begin 20 | # This method takes < 1 second! 21 | # ------Fast way to check if number is prime------------ 22 | 23 | def is_prime?(num) 24 | return false if num <= 1 25 | return true if num.between?(2, 3) # true if 2 <= num <= 3 26 | 27 | return false if num.even? # goodbye even numbers 28 | return false if num % 3 == 0 # goodbye multiples of 3 29 | 30 | j = 5 31 | w = 2 32 | 33 | while j * j <= num # only check j up to sqrt(num), since factors invert order afterwards 34 | return false if num % j == 0 35 | j += w # Only checks odd numbers. First checks j + 2 36 | w = 6 - w # Don't check multiples of 3. So then checks j + 4. 37 | # Alternates + 2, + 4, + 2, etc. 38 | end 39 | 40 | true 41 | end 42 | 43 | =end 44 | -------------------------------------------------------------------------------- /euler/004_palindrome.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=4 2 | # Find the largest palindrome made from the product of two 3-digit numbers. 3 | 4 | def is_palindrome?(n) 5 | n.to_s == n.to_s.reverse 6 | end 7 | 8 | palindromes = [] 9 | 10 | start = Time.now 11 | 999.downto(900) do |x| # don't need to look at numbers below 900 12 | 999.downto(900) do |y| 13 | if is_palindrome?(x * y) 14 | puts "#{x * y} = #{x} * #{y}" 15 | palindromes << x * y 16 | end 17 | end 18 | end 19 | 20 | puts "Largest palindrome = #{palindromes.max}" 21 | puts "It took #{Time.now - start} secs." -------------------------------------------------------------------------------- /euler/005_smallest_multiple.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=5 2 | # 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. 3 | # What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? 4 | 5 | test = 20 6 | divisor = 0 7 | 8 | while true 9 | 10 | # Don't check factors from 1-10, since all included in 11-20. Plus check backwards from 20 down, to save time. 11 | 20.downto(11) do |d| 12 | divisor = d 13 | # puts "test = #{test}, d = #{d}" 14 | break if test % d != 0 15 | end 16 | if divisor == 11 && test % divisor == 0 17 | puts "Answer = #{test}" 18 | exit 19 | end 20 | test += 20 # only need to check multiples of 20 21 | end 22 | -------------------------------------------------------------------------------- /euler/006_square_sum_difference.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=6 2 | # Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum. 3 | 4 | def sum_1_to_n(n) # sum of numbers from 1 to n 5 | n * (n + 1) / 2 # from high school algebra 6 | end 7 | 8 | n = 100 9 | sqsum = 0 10 | 11 | (1..n).each { |i| sqsum += i * i } 12 | 13 | diff = sum_1_to_n(n)**2 - sqsum 14 | 15 | puts "Diff = #{diff}" 16 | 17 | -------------------------------------------------------------------------------- /euler/007_10001st_prime.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=7 2 | # What is the 10001st prime number? 3 | 4 | # This method takes < 1 second! 5 | # ------Fast way to check if number is prime------------ 6 | 7 | def is_prime?(num) 8 | return false if num <= 1 9 | return true if num.between?(2, 3) # true if 2 <= num <= 3 10 | 11 | return false if num.even? # goodbye even numbers 12 | return false if num % 3 == 0 # goodbye multiples of 3 13 | 14 | j = 5 15 | w = 2 16 | 17 | while j * j <= num # only check j up to sqrt(num), since factors invert order afterwards 18 | return false if num % j == 0 19 | j += w # Only checks odd numbers. First checks j + 2 20 | w = 6 - w # Don't check multiples of 3. So then checks j + 4. 21 | # Alternates + 2, + 4, + 2, etc. 22 | end 23 | 24 | true 25 | end 26 | 27 | i = 2 28 | primecount = 0 29 | 30 | while true 31 | primecount += 1 if is_prime?(i) 32 | if primecount == 10001 33 | puts "10,001th prime = #{i}" 34 | exit 35 | end 36 | i += 1 37 | end 38 | 39 | # Useful link: 40 | # http://stackoverflow.com/questions/1801391/what-is-the-best-algorithm-for-checking-if-a-number-is-prime 41 | 42 | =begin 43 | 44 | # Alternate slower way: Takes 12 seconds. 45 | # Build array of primes. 46 | # All primes, besides 2 or 3, are of form 6k + 1 or 6k - 1. This excludes all multiples of 2 or 3. 47 | 48 | primes = [2, 3] # start with 1st 2 primes 49 | k = 1 50 | 51 | while true 52 | prime = true 53 | 54 | primes.each do |p| # is 6k - 1 divisible by each prime found so far? 55 | if (6 * k - 1) % p == 0 56 | prime = false 57 | break 58 | end 59 | end 60 | 61 | primes << 6 * k - 1 if prime # we found a new prime 62 | 63 | break if primes.count == 10001 64 | 65 | prime = true 66 | 67 | primes.each do |p| 68 | if (6 * k + 1) % p == 0 # is 6k + 1 divisible by each prime found so far? 69 | prime = false 70 | break 71 | end 72 | end 73 | 74 | primes << 6 * k + 1 if prime # we found a new prime 75 | 76 | break if primes.count == 10001 77 | k += 1 78 | end 79 | 80 | puts "10,001st prime = #{primes[-1]}" 81 | 82 | =end 83 | -------------------------------------------------------------------------------- /euler/008_largest_product.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=8 2 | # Find the greatest product of five consecutive digits in this 1000-digit number. 3 | 4 | num = '73167176531330624919225119674426574742355349194934\ 5 | 96983520312774506326239578318016984801869478851843\ 6 | 85861560789112949495459501737958331952853208805511\ 7 | 12540698747158523863050715693290963295227443043557\ 8 | 66896648950445244523161731856403098711121722383113\ 9 | 62229893423380308135336276614282806444486645238749\ 10 | 30358907296290491560440772390713810515859307960866\ 11 | 70172427121883998797908792274921901699720888093776\ 12 | 65727333001053367881220235421809751254540594752243\ 13 | 52584907711670556013604839586446706324415722155397\ 14 | 53697817977846174064955149290862569321978468622482\ 15 | 83972241375657056057490261407972968652414535100474\ 16 | 82166370484403199890008895243450658541227588666881\ 17 | 16427171479924442928230863465674813919123162824586\ 18 | 17866458359124566529476545682848912883142607690042\ 19 | 24219022671055626321111109370544217506941658960408\ 20 | 07198403850962455444362981230987879927244284909188\ 21 | 84580156166097919133875499200524063689912560717606\ 22 | 05886116467109405077541002256983155200055935729725\ 23 | 71636269561882670428252483600823257530420752963450' 24 | 25 | n = num.split('').map { |x| x.to_i } # splits number into array of single integers 26 | product = [] 27 | 28 | (0..n.length - 5).each do |i| 29 | product << n[i..i + 4].inject(:*) # multiplies 5 array elements, from n[i] to n[i + 4] 30 | end 31 | 32 | puts "Greatest product = #{product.max}" -------------------------------------------------------------------------------- /euler/009_Pythagorean_triplet.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=9 2 | # There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find product abc. 3 | 4 | (1..998).each do |a| 5 | (2..999).each do |b| 6 | c = 1000 - a - b 7 | if a < b && b < c 8 | if a * a + b * b == c * c 9 | puts "Product of Pythagorean triplet = #{a * b * c}" 10 | exit 11 | end 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /euler/010_summation_of_primes.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=10 2 | # Find the sum of all the primes below 2 million. 3 | # Use Sieve of Eratosthenes to generate array of primes: 4 | # http://www.mathblog.dk/sum-of-all-primes-below-2000000-problem-10/ 5 | 6 | # Also, see my times for checking if number is prime, in is_prime.rb 7 | 8 | def sieve(n) # makes array of all primes from 2 to n 9 | s = 3.step(n, 2).to_a # make array of odd integers from 3 to n. Skip evens. 10 | s.each do |p| 11 | next if p.nil? # go to next element if p has been marked empty 12 | break if p * p > n # p only needs to go up to sqrt(n) 13 | 14 | k, pval = 0, 0 15 | 16 | while pval < n 17 | pval = p * (p + k) # jump forward 2p at a time: p*p, p*p + 2p, p*p + 4p, etc. 18 | 19 | # Set all those multiples to nil. i = (pval - 3)/2 translates pvals to index i 20 | 21 | s[(pval - 3) / 2] = nil 22 | k += 2 23 | end 24 | 25 | end 26 | s.compact! # removes all nil elements from array 27 | s.unshift(2) # adds 2 as 1st element 28 | end 29 | 30 | def is_prime?(n) 31 | return true if n.between?(2, 3) 32 | return false if (n.even? || n % 3 == 0 || n <= 1) 33 | 34 | j, w = 5, 2 35 | 36 | while j * j <= n 37 | return false if n % j == 0 38 | j += w 39 | w = 6 - w 40 | end 41 | 42 | true 43 | end 44 | 45 | def is_prime_sieve?(s, n) # trial division, only dividing by primes from sieve file 46 | return true if n.between?(2, 3) 47 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 48 | 49 | s.each do |d| 50 | return true if d * d > n # only must check up till sqrt(n)! 51 | return false if n % d == 0 # if n is divisible by a number in prime file 52 | end 53 | 54 | true 55 | end 56 | 57 | start = Time.now 58 | s = Marshal.load(File.read('sieveprimes-serialized.txt')) # loads previously calculated primes < 2 million 59 | puts "Took #{Time.now - start} seconds to load in previously calculated primes < 2 million." 60 | 61 | sum = 5 # includes 2 + 3 62 | 63 | start = Time.now 64 | 5.step(2e6.to_i, 2) do |i| # step method increments from 5 to 2 million by 2's 65 | sum += i if is_prime?(i) 66 | end 67 | 68 | puts "Sum of all primes < 2 million = #{sum}" 69 | puts "is_prime method, by trial division, takes #{Time.now - start} seconds. Slow." 70 | 71 | sum = 5 72 | 73 | start = Time.now 74 | 5.step(2e6.to_i, 2) do |i| # step method increments from 5 to 2 million by 2's 75 | sum += i if is_prime_sieve?(s, i) # checks if prime by trial division, only using prime divisors 76 | end 77 | 78 | puts "Sum of all primes < 2 million = #{sum}" 79 | puts "is_prime_sieve method, by trial division (only by primes), takes #{Time.now - start} seconds." 80 | 81 | start = Time.now 82 | sum = sieve(2_000_000).inject(:+) 83 | puts "Sum of all primes < 2 million = #{sum}" 84 | puts "by newly generating Sieve of Eratosthenes. Freakin' fast! Took #{Time.now - start} seconds." 85 | 86 | start = Time.now 87 | sum = s.inject(:+) 88 | puts "Sum of all primes < 2 million = #{sum}" 89 | puts "Summed all primes in preloaded sieve prime file. Fastest of all! Took #{Time.now - start} seconds." 90 | 91 | =begin 92 | 93 | Took 0.019628 seconds to load in previously calculated primes < 2 million. 94 | 95 | Sum of all primes < 2 million = 142913828922 96 | is_prime method, by trial division, takes 9.323758 seconds. Slow. 97 | 98 | Sum of all primes < 2 million = 142913828922 99 | is_prime_sieve method, by trial division (only by primes), takes 8.739961 seconds. 100 | 101 | Sum of all primes < 2 million = 142913828922 102 | by newly generating Sieve of Eratosthenes. Freakin' fast! Took 0.582711 seconds. 103 | 104 | Sum of all primes < 2 million = 142913828922 105 | Summed all primes in preloaded sieve prime file. Fastest of all! Took 0.017074 seconds. 106 | 107 | =end -------------------------------------------------------------------------------- /euler/011_largest_product_in_grid.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=11 2 | # What is the greatest product of 4 adjacent numbers in the same direction 3 | # (up, down, left, right, or diagonally) in the 20 × 20 grid? 4 | 5 | require 'matrix' # The Matrix class will save your butt!!! 6 | 7 | grid = 8 | 9 | Matrix[%w[08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08], 10 | %w[49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00], 11 | %w[81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65], 12 | %w[52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91], 13 | %w[22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80], 14 | %w[24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50], 15 | %w[32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70], 16 | %w[67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21], 17 | %w[24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72], 18 | %w[21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95], 19 | %w[78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92], 20 | %w[16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57], 21 | %w[86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58], 22 | %w[19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40], 23 | %w[04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66], 24 | %w[88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69], 25 | %w[04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36], 26 | %w[20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16], 27 | %w[20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54], 28 | %w[01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48]] 29 | 30 | hpmax, vpmax, dp1max, dp2max = 0, 0, 0, 0 31 | answer = [] 32 | 33 | rows = grid.row_size 34 | cols = grid.column_size 35 | 36 | def horiz_product(mat, rows, cols) 37 | product = [] 38 | 39 | (0..rows - 1).each do |r| 40 | (0..cols - 4).each do |c| 41 | product << mat[r, c] * mat[r, c+1] * mat[r, c+2] * mat[r, c+3] 42 | end 43 | end 44 | 45 | product 46 | end 47 | 48 | g = grid.map { |x| x.to_i } # Turn 20 x 20 matrix of strings into integers 49 | 50 | hpmax = horiz_product(g, rows, cols).max 51 | puts "horiz prod max = #{hpmax}" 52 | 53 | gvert = g.transpose # Turn all columns into rows! 54 | vpmax = horiz_product(gvert, rows, cols).max # Use horiz_product on transpose of grid to find vertical product 55 | puts "vert prod max = #{vpmax}" 56 | 57 | # Calculate diagonal product (top left to bottom right) 58 | dproduct = [] 59 | (0..rows - 4).each do |r| 60 | (0..cols - 4).each do |c| 61 | dproduct << g[r, c] * g[r+1, c+1] * g[r+2, c+2] * g[r+3, c+3] 62 | end 63 | end 64 | 65 | dp1max = dproduct.max 66 | puts "diag1 prod max = #{dp1max}" 67 | 68 | # Calculate diagonal product (top right to bottom left) 69 | dproduct = [] 70 | (0..rows - 4).each do |r| 71 | (cols - 1).downto(3) do |c| 72 | dproduct << g[r, c] * g[r+1, c-1] * g[r+2, c-2] * g[r+3, c-3] 73 | end 74 | end 75 | 76 | dp2max = dproduct.max 77 | puts "diag2 prod max = #{dp2max}" 78 | 79 | answer = [hpmax, vpmax, dp1max, dp2max] 80 | 81 | puts "Max of all 4 products = #{answer.max}" 82 | 83 | 84 | -------------------------------------------------------------------------------- /euler/012_triangular_fastest-way.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=12 2 | # What is the first triangle number to have > 500 factors? 3 | 4 | # Fastest method, thanks to this blog: 5 | # http://www.mathblog.dk/triangle-number-with-more-than-500-divisors/ 6 | # http://www.mathblog.dk/files/euler/Problem12.cs 7 | # Use Sieve of Eratosthenes to build prime # list: 8 | # http://www.mathblog.dk/sum-of-all-primes-below-2000000-problem-10/ 9 | =begin 10 | 11 | Any num is uniquely described by its prime factorization: 12 | 13 | N = p1^a1 * p2^a2 * p3^a3 ..., where pn = distinct prime. 14 | Each prime may be chosen 0,1,2,...an ("a sub n") times. 15 | So we may choose pn in (an + 1) different ways. 16 | 17 | Total divisors of N = 18 | D(N) = (a1 + 1)(a2 + 1)(a3 + 1)* * * 19 | 20 | =end 21 | 22 | def div(noriginal, primelist) 23 | divisors = 1 24 | exponent = 0 25 | n = noriginal 26 | 27 | (0..primelist.length - 1).each do |i| 28 | 29 | # In case there's a remainder, this is a prime factor as well. 30 | # That factor's exponent is 1. (Ex: a1 = 1) We have 2 possibilities 31 | # fot that prime factor---we use it or we dont. That's why we multiply by 2. 32 | 33 | return 2 * divisors if primelist[i] * primelist[i] > noriginal 34 | 35 | exponent = 1 36 | while n % primelist[i] == 0 37 | exponent += 1 # each time we can divide noriginal by the same prime, 38 | # we raise exponent, keeping track of its multiplicity (a1, a2, a3, etc) 39 | n /= primelist[i] # keep dividing n by prime, until we can't 40 | end 41 | 42 | divisors *= exponent # include new multiplicity of that prime in divisors 43 | return divisors if n == 1 # if no remainder, return the count 44 | end 45 | 46 | divisors 47 | end -------------------------------------------------------------------------------- /euler/012_triangular_num-brute-force.rb: -------------------------------------------------------------------------------- 1 | def div(n) 2 | divisors = 0 3 | f = 1 4 | while f * f <= n 5 | divisors += 2 if n % f == 0 # add both factors 6 | f += 1 7 | end 8 | # reduce 1 for duplicate if n is perfect square 9 | divisors -= 1 if Math.sqrt(n) == Math.sqrt(n).to_i 10 | divisors 11 | end 12 | 13 | i = 2 14 | 15 | while true 16 | triangle = i * (i + 1) / 2 # triangle number = 1 + 2 + 3 + ... + i 17 | if div(triangle) > 500 18 | puts "Triangle # with > 500 factors = #{triangle}" 19 | exit 20 | end 21 | i += 1 22 | end -------------------------------------------------------------------------------- /euler/012_triangular_num-print-factors.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=12 2 | # What is the first triangle number to have > 500 factors? 3 | 4 | # Prints out all factors. Slow. Takes 11 seconds. 5 | 6 | def factors(noriginal) # gives n's factors 7 | factors = [1, noriginal] # We know 1 and n are always factors 8 | f = 2 9 | 10 | begin 11 | n = noriginal 12 | 13 | while f * f <= n # Only check up to sqrt(n) 14 | if n % f == 0 15 | factors << f << n / f 16 | n /= f 17 | else 18 | 19 | f += 1 20 | if noriginal % f == 0 # catches divisors you missed with n < noriginal 21 | factors << f << noriginal / f 22 | end 23 | end 24 | end 25 | 26 | f += 1 27 | end until f * f > noriginal # Overall, only check up to sqrt(noriginal) 28 | 29 | factors.uniq.sort # sort and remove duplicates 30 | end 31 | 32 | i = 2 33 | 34 | while true 35 | triangle = i * (i + 1) / 2 # triangle number = 1 + 2 + 3 + ... + i 36 | fact = factors(triangle) 37 | puts "triangle: #{triangle}" 38 | print "#{fact}, size: #{fact.size}\n" 39 | 40 | if fact.size > 500 41 | puts "Triangle # with > 500 factors = #{triangle}" 42 | exit 43 | end 44 | i += 1 45 | end 46 | 47 | =begin [This algorithm, step by step, for number 36] 48 | 49 | Answer = [1, 2, 3, 4, 6, 9, 12, 18, 36] 50 | 51 | n = 36 52 | f = 2 53 | 54 | 36 % 2 = 0 is f*f <= n? 55 | => 2, 36/2 = 18 56 | n = 18 57 | 18 % 2 = 0 is f*f <= n? 58 | => 2, 18/2 = 9 59 | n = 9 60 | 9 % 2 != 0 F or is f*f <= n? 61 | 62 | f: 2 += 1 = 3 63 | noriginal % f = 0? (36 % 3) if n < noriginal 64 | => 3, 36/3 = 12 65 | 66 | 9 % 3 = 0 is f*f <= n? 67 | => 3, 9/3 = 3 68 | n = 3 69 | 3 % 2 is f*f <= n? F 70 | 71 | f: 3 += 1 = 4 72 | 73 | n = noriginal = 36 74 | 36 % 4 = 0 is f*f <= n? 75 | => 4, 36/4 = 9 76 | n = 9 77 | 9 % 4 is f*f <= n? F 78 | 79 | f: 4 += 1 = 5 80 | 81 | n = original 36 82 | 36 % 5 !=0 F or is f*f <= n? 83 | 84 | f: 5 += 1 = 6 85 | 86 | 36 % 6 = 0 is f*f <= n? 87 | => 6, 36/6 = 6 88 | n = 6 89 | 6 % 6 = 0 or is f*f <= n? F 90 | 91 | f: 6 += 1 = 7 92 | n = original 36 93 | is f * f <= noriginal? F 94 | 95 | STOP 96 | 97 | =end 98 | -------------------------------------------------------------------------------- /euler/013_large_sum.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=13 2 | # Find first 10 digits of the sum of the following one-hundred 50-digit numbers. 3 | 4 | nums = 5 | 6 | [37107287533902102798797998220837590246510135740250, 7 | 46376937677490009712648124896970078050417018260538, 8 | 74324986199524741059474233309513058123726617309629, 9 | 91942213363574161572522430563301811072406154908250, 10 | 23067588207539346171171980310421047513778063246676, 11 | 89261670696623633820136378418383684178734361726757, 12 | 28112879812849979408065481931592621691275889832738, 13 | 44274228917432520321923589422876796487670272189318, 14 | 47451445736001306439091167216856844588711603153276, 15 | 70386486105843025439939619828917593665686757934951, 16 | 62176457141856560629502157223196586755079324193331, 17 | 64906352462741904929101432445813822663347944758178, 18 | 92575867718337217661963751590579239728245598838407, 19 | 58203565325359399008402633568948830189458628227828, 20 | 80181199384826282014278194139940567587151170094390, 21 | 35398664372827112653829987240784473053190104293586, 22 | 86515506006295864861532075273371959191420517255829, 23 | 71693888707715466499115593487603532921714970056938, 24 | 54370070576826684624621495650076471787294438377604, 25 | 53282654108756828443191190634694037855217779295145, 26 | 36123272525000296071075082563815656710885258350721, 27 | 45876576172410976447339110607218265236877223636045, 28 | 17423706905851860660448207621209813287860733969412, 29 | 81142660418086830619328460811191061556940512689692, 30 | 51934325451728388641918047049293215058642563049483, 31 | 62467221648435076201727918039944693004732956340691, 32 | 15732444386908125794514089057706229429197107928209, 33 | 55037687525678773091862540744969844508330393682126, 34 | 18336384825330154686196124348767681297534375946515, 35 | 80386287592878490201521685554828717201219257766954, 36 | 78182833757993103614740356856449095527097864797581, 37 | 16726320100436897842553539920931837441497806860984, 38 | 48403098129077791799088218795327364475675590848030, 39 | 87086987551392711854517078544161852424320693150332, 40 | 59959406895756536782107074926966537676326235447210, 41 | 69793950679652694742597709739166693763042633987085, 42 | 41052684708299085211399427365734116182760315001271, 43 | 65378607361501080857009149939512557028198746004375, 44 | 35829035317434717326932123578154982629742552737307, 45 | 94953759765105305946966067683156574377167401875275, 46 | 88902802571733229619176668713819931811048770190271, 47 | 25267680276078003013678680992525463401061632866526, 48 | 36270218540497705585629946580636237993140746255962, 49 | 24074486908231174977792365466257246923322810917141, 50 | 91430288197103288597806669760892938638285025333403, 51 | 34413065578016127815921815005561868836468420090470, 52 | 23053081172816430487623791969842487255036638784583, 53 | 11487696932154902810424020138335124462181441773470, 54 | 63783299490636259666498587618221225225512486764533, 55 | 67720186971698544312419572409913959008952310058822, 56 | 95548255300263520781532296796249481641953868218774, 57 | 76085327132285723110424803456124867697064507995236, 58 | 37774242535411291684276865538926205024910326572967, 59 | 23701913275725675285653248258265463092207058596522, 60 | 29798860272258331913126375147341994889534765745501, 61 | 18495701454879288984856827726077713721403798879715, 62 | 38298203783031473527721580348144513491373226651381, 63 | 34829543829199918180278916522431027392251122869539, 64 | 40957953066405232632538044100059654939159879593635, 65 | 29746152185502371307642255121183693803580388584903, 66 | 41698116222072977186158236678424689157993532961922, 67 | 62467957194401269043877107275048102390895523597457, 68 | 23189706772547915061505504953922979530901129967519, 69 | 86188088225875314529584099251203829009407770775672, 70 | 11306739708304724483816533873502340845647058077308, 71 | 82959174767140363198008187129011875491310547126581, 72 | 97623331044818386269515456334926366572897563400500, 73 | 42846280183517070527831839425882145521227251250327, 74 | 55121603546981200581762165212827652751691296897789, 75 | 32238195734329339946437501907836945765883352399886, 76 | 75506164965184775180738168837861091527357929701337, 77 | 62177842752192623401942399639168044983993173312731, 78 | 32924185707147349566916674687634660915035914677504, 79 | 99518671430235219628894890102423325116913619626622, 80 | 73267460800591547471830798392868535206946944540724, 81 | 76841822524674417161514036427982273348055556214818, 82 | 97142617910342598647204516893989422179826088076852, 83 | 87783646182799346313767754307809363333018982642090, 84 | 10848802521674670883215120185883543223812876952786, 85 | 71329612474782464538636993009049310363619763878039, 86 | 62184073572399794223406235393808339651327408011116, 87 | 66627891981488087797941876876144230030984490851411, 88 | 60661826293682836764744779239180335110989069790714, 89 | 85786944089552990653640447425576083659976645795096, 90 | 66024396409905389607120198219976047599490197230297, 91 | 64913982680032973156037120041377903785566085089252, 92 | 16730939319872750275468906903707539413042652315011, 93 | 94809377245048795150954100921645863754710598436791, 94 | 78639167021187492431995700641917969777599028300699, 95 | 15368713711936614952811305876380278410754449733078, 96 | 40789923115535562561142322423255033685442488917353, 97 | 44889911501440648020369068063960672322193204149535, 98 | 41503128880339536053299340368006977710650566631954, 99 | 81234880673210146739058568557934581403627822703280, 100 | 82616570773948327592232845941706525094512325230608, 101 | 22918802058777319719839450180888072429661980811197, 102 | 77158542502016545090413245809786882778948721859617, 103 | 72107838435069186155435662884062257473692284509516, 104 | 20849603980134001723930671666823555245252804609722, 105 | 53503534226472524250874054075591789781264330331690] 106 | 107 | sum = nums.inject(:+) 108 | 109 | puts "Sum of these 50-digit numbers = #{sum}" 110 | puts "1st 10 digits of this sum = #{sum.to_s[0..9]}" -------------------------------------------------------------------------------- /euler/014_longest_collatz_sequence.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=14 2 | # The following iterative sequence is defined for the set of positive integers: 3 | 4 | # n → n/2 (n is even) 5 | # n → 3n + 1 (n is odd) 6 | # 7 | # Using the rule above and starting with 13, we generate the following sequence: 8 | # 9 | # 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 10 | # It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. 11 | # It is thought that all starting numbers finish at 1. 12 | # 13 | # Which starting number, < 1 million, produces the longest chain? 14 | # ================================================================== 15 | require 'rspec/autorun' 16 | 17 | # Brute force way: 18 | def slow_way 19 | longest = 0 20 | (2..999_999).each do |n| 21 | length = 1 22 | 23 | begin 24 | n = if n.even? 25 | n / 2 26 | else 27 | 3 * n + 1 28 | end 29 | length += 1 30 | end until n == 1 31 | 32 | longest = length if length > longest 33 | end 34 | longest 35 | end 36 | 37 | # start = Time.now 38 | # puts "Longest chain was #{slow_way} terms. Took #{Time.now - start} secs." # 18.2 secs 39 | 40 | # Faster way, using memoization. Makes hash of prior Collatz lengths. 41 | # If it sees old_length already recorded, adds old_length to length 42 | 43 | class Collatz 44 | def longest_chain(starting_num) 45 | old_length = {} 46 | longest = 1 47 | longest_n = starting_num 48 | length = 1 49 | 50 | (2..starting_num).each do |n| 51 | initial_n = n 52 | 53 | until n == 1 || old_length[n] 54 | n = if n.even? 55 | n / 2 56 | else 57 | 3 * n + 1 58 | end 59 | length += 1 60 | end 61 | 62 | length += old_length[n] if old_length[n] 63 | old_length[initial_n] = length 64 | 65 | if length > longest 66 | longest = length 67 | longest_n = initial_n 68 | end 69 | length = 0 70 | end 71 | return longest, longest_n 72 | end 73 | end 74 | 75 | # start = Time.now 76 | # longest, starting_num = Collatz.new.longest_chain(999_999) 77 | # puts "Longest chain of #{longest} terms was made by #{starting_num}. Took #{Time.now - start} secs." 78 | # Longest chain of 525 terms was made by 837799. Took 2.75 secs. (Brute force way took 18.2 secs.) 79 | 80 | describe Collatz do 81 | let(:c) { Collatz.new } 82 | it '#longest_chain' do 83 | expect(c.longest_chain(1)).to eq [1, 1] 84 | expect(c.longest_chain(2)).to eq [2, 2] 85 | expect(c.longest_chain(3)).to eq [8, 3] 86 | expect(c.longest_chain(13)).to eq [20, 9] 87 | expect(c.longest_chain(999_999)).to eq [525, 837799] 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /euler/015_lattice_paths.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=15 2 | # Starting in the top left corner of a 2×2 grid, and only being able to move to the right and down, there 3 | # are exactly 6 routes to the bottom right corner. How many such routes are there through a 20×20 grid? 4 | 5 | =begin 6 | Best way: dynamic programming. As you move from upper left to bottom right, keep track of # of paths 7 | to reach any point. Put that # in array. 8 | 9 | # of paths to reach any point = # of paths to reach square ABOVE it + # of paths to reach square to its LEFT. 10 | 11 | When you reach lower right corner, tbe final total of paths will be the answer! 12 | 13 | Here's a "pretty picture" of different paths: http://mathforum.org/advanced/robertd/manhattan.html 14 | =end 15 | 16 | start = Time.now 17 | 18 | n = 20 19 | grid = Array.new(n + 1) {Array.new(n + 1, 0)} # makes (n + 1) x (n + 1) array filled with 0's. 20 | 21 | (0..n).each do |i| # Fill row 0 and col 0 all with 1's. Only 1 way to get to each square on top/left edges. 22 | grid[0][i] = 1 23 | grid[i][0] = 1 24 | end 25 | 26 | (1..n).each do |row| 27 | (1..n).each do |col| 28 | # each element = # of paths in square above + # of paths in square to left 29 | grid[row][col] = grid[row - 1][col] + grid[row][col - 1] 30 | end 31 | end 32 | 33 | puts "Number of paths to get from upper left to bottom right of a #{n} x #{n} grid," 34 | puts "if you may only move right or down each step, is #{grid[n][n]}. Time: #{Time.now - start} secs." 35 | 36 | =begin 37 | 38 | Output: "Number of paths to get from upper left to bottom right of a 20 x 20 grid, 39 | if you may only move right or down each step, is 137846528820. Time: 0.000422 secs." 40 | 41 | (0..n).each do |row| # prints whole matrix 42 | p grid[row] 43 | end 44 | 45 | Math way. The simplest answer, from combinatorics, is 40 choose 20 = 40! / (20! * 20!) = 137846528820. 46 | Why? To get to our destination, in all cases, we must always move 20 times right and 20 times down. 47 | The question is WHEN we move right vs. WHEN we move down. We always move 40 steps total. 48 | 49 | Once we choose which steps we move right, all other steps MUST be moves down. No choice. 50 | Thus our answer is just the # of ways we can pick 20 times moving right, out of 40 steps. 51 | 52 | Let's see a 3x3 grid. It has 6 steps total, 3 right and 3 down. Here's 1 path: 53 | 54 | R _ R _ _ R. Let's say we pick to move right in steps 1, 3, and 6. Steps 2, 4, and 5 MUST be down! 55 | R D R D D R. We have no other choice. Thus # of paths for a 3x3 grid = 6 choose 3 = 6! (3! * 3!) 56 | =end 57 | 58 | -------------------------------------------------------------------------------- /euler/016_power_digit_sum.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=16 2 | # What is the sum of the digits of the number 2**1000? 3 | 4 | # 1. Convert 2^1000 to string. 5 | # 2. Split it into array of single digit strings. 6 | # 3. Map each element of array to integer form. 7 | # 4. Add all elements together with "inject" 8 | 9 | sum = (2**1000).to_s.split('').map {|x| x.to_i}.inject(:+) 10 | puts "Sum of digits of 2^1000 = #{sum}" -------------------------------------------------------------------------------- /euler/017_number_letter_counts.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=17 2 | # If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, 3 | # how many letters would be used? 4 | 5 | # Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 6 | # 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of "and" when 7 | # writing out numbers is in compliance with British usage. 8 | 9 | def in_words(n) 10 | # n / power of 10 = leftmost digit. n % power of 10 = remaining right digits. 11 | 12 | words = [] 13 | 14 | case n 15 | when 0..19 16 | words.unshift(teen(n)) 17 | when 20..99 18 | words.unshift(tens(n)) 19 | when 100..999 20 | words.unshift(hundreds(n)) 21 | when 1000..999_999 22 | words.unshift(thousands(n)) 23 | end 24 | 25 | words.join(' ') 26 | end 27 | 28 | def thousands(n) 29 | return hundreds(n) if n < 1000 30 | 31 | if n % 10**(n.to_s.length - 1) != 0 32 | hundreds(n / 1000) + ' thousand ' + hundreds(n % 1000) 33 | else 34 | hundreds(n / 1000) + ' thousand' 35 | end 36 | end 37 | 38 | def hundreds(n) 39 | return tens(n) if n < 100 40 | 41 | if n % 100 != 0 # if number is not 100, 200, 300, etc. 42 | teen(n / 100) + ' hundred and ' + tens(n % 100) 43 | else 44 | teen(n / 100) + ' hundred' 45 | end 46 | end 47 | 48 | def tens(n) 49 | return teen(n) if n < 20 50 | 51 | d = %w[twenty thirty forty fifty sixty seventy eighty ninety] 52 | 53 | if n % 10 != 0 # if last digit isn't 0 54 | d[n / 10 - 2] + ' ' + teen(n % 10) 55 | else 56 | d[n / 10 - 2] 57 | end 58 | end 59 | 60 | def teen(n) 61 | d = %w[zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen] 62 | d[n] 63 | end 64 | 65 | total = 0 66 | 67 | (1..1000).each do |n| 68 | total += in_words(n).split.join.length # makes string with no spaces. finds length. 69 | end 70 | 71 | puts "# of letters in all numbers from 1 to 1000, written out: #{total}" 72 | 73 | =begin 74 | puts "Enter number. I'll repeat it in words and say # of letters:" 75 | n = gets.chomp.to_i 76 | w = in_words(n) 77 | puts "In words: #{w}" 78 | puts "Letters: #{w.split.join.length}" 79 | =end -------------------------------------------------------------------------------- /euler/018_maximum_path_sum1.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=18 2 | # Find the maximum total from top to bottom of the triangle below: 3 | # As there are only 2**(rows - 1) = 16384 routes, it is possible to solve this problem 4 | # by trying every route. However, Problem 67, is the same challenge with a 5 | # triangle containing one-hundred rows; it cannot be solved by brute force, 6 | # and requires a clever method! ;o) 7 | 8 | =begin 9 | reads in file of triangle numbers. Each line is separate element in array. 10 | Makes array like this: 11 | 12 | ["75\n", 13 | "95 64\n", 14 | "17 47 82\n", 15 | "18 35 87 10\n"] 16 | 17 | =end 18 | 19 | $t = IO.readlines('triangle.txt') 20 | $t.map!(&:chomp) # removes all trailing "\n" from each line 21 | $t.map!(&:split) # splits each line with commas. Makes each line a separate nested array 22 | 23 | =begin 24 | 25 | [["75"], 26 | ["95", "64"], 27 | ["17", "47", "82"], 28 | 29 | =end 30 | 31 | $t.each do |a| 32 | a.map!(&:to_i) # finally, we convert all elements in nested array to integers! 33 | end 34 | 35 | =begin 36 | 37 | [[75], 38 | [95, 64], 39 | [17, 47, 82], 40 | [18, 35, 87, 10]] 41 | 42 | =end 43 | 44 | def solve(row, col) # recursive solution is so much easier! But it's too slow for problem 67, super big triangle. 45 | return 0 if row == $t.length 46 | return $t[row][col] + [solve(row + 1, col), solve(row + 1, col + 1)].max 47 | end 48 | 49 | puts "Max sum of path down triangle = #{solve(0, 0)}" 50 | 51 | =begin -------- My original random trial solution. Not reliable. --------- 52 | sums = [] 53 | 54 | def routesum(t, sums) 55 | sum = 0 56 | col = 0 57 | # colold = -1 58 | 59 | (0..t.size - 1).each do |row| 60 | 61 | # if col == colold # only want cols in row that are adjacent to col from last row 62 | # 63 | # if t[row][col + 1] > t[row][col] # we can only move to 2 spots: col or col+1 64 | # col += 1 # make current col = col with bigger number 65 | # end 66 | # end 67 | 68 | if row != 0 69 | col += 1 if rand(2) == 1 # make a random decision whether to pick L or R number 70 | end 71 | #print "Row = #{row}, Col = #{col}, " 72 | sum += t[row][col] 73 | #puts "Sum = #{sum}" 74 | # colold = col # column # of previous row 75 | end 76 | 77 | sums << sum 78 | puts "Sum of route = #{sum}" 79 | sums 80 | end 81 | 82 | 10000.times do # doesn't always give right answer 83 | routesum(t, sums) 84 | end 85 | 86 | print sums 87 | puts 88 | puts "Max sum of path after 10000 random trials = #{sums.max}" 89 | =end 90 | -------------------------------------------------------------------------------- /euler/019_counting_sundays.rb: -------------------------------------------------------------------------------- 1 | =begin http://projecteuler.net/problem=19 2 | 3 | 1 Jan 1900 was a Monday. 4 | 5 | Thirty days has September, 6 | April, June and November. 7 | All the rest have thirty-one, 8 | Saving February alone, 9 | Which has twenty-eight, rain or shine. 10 | And on leap years, twenty-nine. 11 | 12 | A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400. 13 | How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)? 14 | 15 | =end 16 | 17 | require 'date' 18 | 19 | DAYS = %w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday] 20 | MONTHS = %w[January February March April May June July August September October November December] 21 | MONTHDAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] # Days in each month 22 | 23 | # start values -------- 24 | year = 1901 25 | dow = DAYS[2] # Day of week = Tuesday 26 | day = 1 # Day of month 27 | month = 0 # 0 = January, 1 = February, etc. 28 | 29 | # end values ---------- 30 | endyear = 2000 31 | endday = 31 32 | endmonth = 11 33 | 34 | sundays = 0 # number of Sundays on 1st of month (where day = 1) 35 | 36 | def leapyear?(y) 37 | return true if y % 400 == 0 # a leap century 38 | return true if (y % 100 != 0) && (y % 4 == 0) # normal leap year 39 | false 40 | end 41 | 42 | def nextmonthyear(m, d, y) 43 | if leapyear?(y) && m == 1 # if it's Feb. in a leap year 44 | if d > 29 45 | d -= 29 46 | m = 2 47 | return m, d, y 48 | elsif d == 29 # not checking for this caused me to be short by 4! 49 | return m, d, y # it added 1 extra day every Feb. 29! 50 | end 51 | end 52 | 53 | if d > MONTHDAYS[m] # if days > # of days in that month, 54 | d -= MONTHDAYS[m] # change d to days for next month 55 | m += 1 # increase month 56 | if m > 11 # if now December, 57 | m = 0 # change to January 58 | y += 1 # increase year 59 | end 60 | end 61 | 62 | return m, d, y 63 | end 64 | 65 | while true 66 | print "#{MONTHS[month]} #{day}, #{year} is #{dow} " 67 | 68 | dater = Date.new(year, month+1, day) # Ruby class to find day of the week, given a date 69 | print "**FALSE**" if Date::DAYNAMES[dater.wday] != dow # Check your calcs match Ruby day of the week 70 | 71 | if day == 1 && dow == DAYS[0] 72 | sundays += 1 73 | puts "* Sundays: #{sundays}" 74 | else 75 | puts 76 | end 77 | 78 | if dow != DAYS[0] # if it's not Sunday, force next loop to move to Sunday 79 | days_till_Sun = 7 - DAYS.index(dow) 80 | day += days_till_Sun 81 | dow = DAYS[0] 82 | else 83 | day += 7 84 | end 85 | 86 | break if (year == endyear) && (month == endmonth) && (day >= endday) 87 | month, day, year = nextmonthyear(month, day, year) # if we've moved to next month, adjust month/day 88 | end 89 | 90 | puts "#{MONTHS[month]} #{day}, #{year} is #{dow} " 91 | sundays += 1 if day == 1 && dow = DAYS[0] 92 | puts "# of Sundays that fell on 1st of month = #{sundays}" 93 | -------------------------------------------------------------------------------- /euler/020_factorial_digit_sum.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=20 2 | # Find the sum of the digits in the number 100! 3 | 4 | factorial = (1..100).inject(:*) # 100! 5 | sum = factorial.to_s.split('').map {|x| x.to_i}.inject(:+) 6 | puts "Sum of digits in 100! = #{sum}" -------------------------------------------------------------------------------- /euler/021_amicable_numbers.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=21 2 | # Amicable numbers 3 | 4 | def factors(noriginal) # gives n's factors 5 | factors = [1] # We know 1 is always a factors. Don't include n. 6 | f = 2 7 | 8 | begin 9 | n = noriginal 10 | 11 | while f * f <= n # Only check up to sqrt(n) 12 | if n % f == 0 13 | factors << f << n / f 14 | n /= f 15 | else 16 | 17 | f += 1 18 | if noriginal % f == 0 # catches divisors you missed with n < noriginal 19 | factors << f << noriginal / f 20 | end 21 | end 22 | end 23 | 24 | f += 1 25 | end until f * f > noriginal # Overall, only check up to sqrt(noriginal) 26 | 27 | factors.uniq.sort # sort and remove duplicates 28 | end 29 | 30 | amicable = [] 31 | 32 | (1..9999).each do |n| 33 | s1 = factors(n).inject(:+) # sum of all "proper divisors" (factors, excluding n) 34 | s2 = factors(s1).inject(:+) 35 | 36 | # s1 = sum of n's p. divisors. if n = sum of s1's p. divisors, they are "amicable #'s" 37 | if s2 == n && s1 != s2 # they must both be DIFFERENT 38 | amicable << s1 + s2 39 | puts "Sum = #{s1 + s2} ***** amicable: #{s2} and #{s1} *****" 40 | end 41 | 42 | end 43 | 44 | # remove duplicates from all answers, then take sum of all 45 | puts "Sum of all amicable nums < 10000 = #{amicable.uniq.inject(:+)}" 46 | -------------------------------------------------------------------------------- /euler/022_names_scores.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=22 2 | # What is the total of all the name scores in the file? 3 | 4 | require 'csv' 5 | 6 | start = Time.now 7 | 8 | names = CSV.read("names.txt").shift # Remember this! Reads CSV file into array! 9 | 10 | names.sort! # sort names in alphabetical order 11 | nscore = 0 12 | i = 1 # first name on list is position 1 13 | 14 | names.each do |name| 15 | nsum = 0 16 | 17 | name.split('').each do |letter| 18 | nsum += letter.ord - 64 # ord gives ASCII value of character, minus 64 gives 1 for "A" 19 | end 20 | 21 | nscore += nsum * i # name score = alphabetical value of each name * position on list 22 | i += 1 23 | end 24 | 25 | puts "Total name scores for file = #{nscore} in #{Time.now - start} seconds." 26 | 27 | 28 | -------------------------------------------------------------------------------- /euler/023_non-abundant_sums.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=23 2 | # A number n is "abundant" if the sum of all its "proper divisors" exceeds n. 3 | # All integers > 20161 can be written as sum of two abundant numbers. (According to Wolfram Mathworld) 4 | # Find sum of all positive integers which CANNOT be written as sum of 2 abundant numbers. 5 | 6 | def factors(noriginal) # gives n's proper divisors 7 | factors = [1] # 1 is always a factor. Exclude n to get "proper divisors" 8 | f = 2 9 | 10 | begin 11 | n = noriginal 12 | 13 | while f * f <= n # Only check up to sqrt(n) 14 | if n % f == 0 15 | factors << f << n / f 16 | n /= f 17 | else 18 | 19 | f += 1 20 | if noriginal % f == 0 # catches divisors you missed with n < noriginal 21 | factors << f << noriginal / f 22 | end 23 | end 24 | end 25 | 26 | f += 1 27 | end until f * f > noriginal # Overall, only check up to sqrt(noriginal) 28 | 29 | factors.uniq.sort # sort and remove duplicates 30 | end 31 | 32 | abundant = [] # all abundant #'s <= 28123 (in original problem). Dropped it to 20161 for speed. 33 | 34 | start = Time.now 35 | 36 | (1..20161).each do |n| 37 | # if sum of n's proper divisors > n, n = "abundant" 38 | abundant << n if factors(n).inject(:+) > n 39 | end 40 | 41 | # Make array of all sums of 2 abundant nums, where sums <= 20161. 42 | sum_abundant = [] 43 | 44 | abundant.each_with_index do |a1, index| 45 | 46 | while index < abundant.size 47 | a2 = abundant[index] # a2 only needs to range from a1 to last abundant number! 48 | sum = a1 + a2 49 | 50 | sum > 20161 ? break : sum_abundant << sum 51 | 52 | index += 1 53 | end 54 | end 55 | 56 | sum_all = (1..20161).inject(:+) # sum of all ints from 1 to 20161 57 | sum_ab = sum_abundant.uniq!.inject(:+) # add all ints that CAN be written as sum of 2 abundant nums. No duplicates. 58 | 59 | # sum of all ints who CAN'T be written = sum of ALL ints - sum of all who CAN be written 60 | sum_nonabundant = sum_all - sum_ab 61 | 62 | puts "Sum of all integers which are NOT sums of 2 abundant #'s = #{sum_nonabundant}" 63 | puts "Total time: #{Time.now - start} seconds" -------------------------------------------------------------------------------- /euler/024_lexicographic_permutations.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=24 2 | # What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9? 3 | 4 | # 1 line solution (but slow). It makes all 1 million permutations, then picks 1 millionth entry of string array 5 | 6 | # [*0..9].permutation.map(&:join)[999_999] = answer! 7 | 8 | # [*0..9] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 9 | 10 | # Best non-recursive routine. Took only 1.4 secs to solve Project Euler 24. 11 | # Taken from http://www.mathblog.dk/project-euler-24-millionth-lexicographic-permutation/ 12 | 13 | a = [*0..9] 14 | count = 1 15 | numPerm = 1_000_000 # get 1 millionth permutation 16 | i, j = 0, 0 17 | n = a.length 18 | 19 | start = Time.now 20 | 21 | while (count < numPerm) 22 | i = n-1 23 | 24 | while (a[i - 1] >= a[i]) 25 | i -= 1 26 | end 27 | 28 | j = n 29 | 30 | while (a[j - 1] <= a[i - 1]) 31 | j -= 1 32 | end 33 | 34 | # start with a[i-1] = 8, a[j-1] = 9 35 | 36 | # swap values at position i-1 and j-1 37 | # puts "i-1 = #{i-1}, j-1 = #{j-1}" 38 | a[i - 1], a[j - 1] = a[j - 1], a[i - 1] 39 | 40 | # p a 41 | 42 | i += 1 43 | j = n 44 | 45 | while (i < j) 46 | # puts "i-1 = #{i-1}, j-1 = #{j-1} (while i= 4 && newnum[-4..-1] == oldnum[-4..-1] 57 | cycle[d] = 1 58 | 59 | # digit is probably something like 0.166666... 60 | # puts "1 / #{d} repeats for (probably) 1 digit" 61 | break 62 | end 63 | 64 | i += 1 65 | oldnum = num[0..i] 66 | end 67 | end 68 | 69 | max = cycle.values.max 70 | 71 | puts "Longest repeating cycle = 1 / #{cycle.key(max)}, with #{max} digits. It took #{Time.now - start} secs." 72 | 73 | # "Longest repeating cycle = 1 / 983, with 982 digits. It took 0.632792 secs." 74 | 75 | =begin 76 | 77 | On Project Euler forum, zetetic said: 78 | 79 | For all primes p valued 7 to 997, compute the sequence: 80 | 81 | n(1) = 9 82 | n(i+1) = mod (10 * n(i) + 9 , p) 83 | 84 | When n(i) = 0 then i is the length of the reciprocal cycle of 1/p. 85 | 86 | Why it works: 87 | 88 | Only primes need be considered since any reciprocal of a composite either terminates or has the same cycle length as one of its prime factors. What the repeated decimal of the reciprocal when p is prime represents is a fraction with a denominator composed of all 9's (I will call them 9-numbers for brevity's sake). For example: 89 | 90 | 1/7 = .142857142857 ... = 142857 / 999999 91 | 92 | So all we have to do to find the reciprocal cycle length is keep figuring out the remainder of successive 9-numbers when divided by our prime p until the remainder hits 0. Since only the remainder matters we can take the modulus to base p each time so the numbers being considered will stay low. I also use the fact that if m is a 9-number, 10m + 9 is the next 9-number. Note that this algorithm only works for prime numbers greater than or equal to 7. You can't simplify it by not computing primes and just running it on all numbers. 93 | 94 | =end -------------------------------------------------------------------------------- /euler/027_quadratic_primes.rb: -------------------------------------------------------------------------------- 1 | # https://projecteuler.net/problem=27 2 | # Considering quadratics of the form: 3 | # n² + an + b, where |a| < 1000 and |b| < 1000 4 | # Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n = 0. 5 | require 'pry' 6 | require './sieve_of_eratosthenes' 7 | 8 | # if n = 0, and f = 0*0 + a*0 + b = b must be prime, then b must be prime! 9 | b_primes = sieve(999) # b = all primes from 2-999 10 | 11 | old_max_n = 0 12 | max_n = {} 13 | [*-999..999].each do |a| 14 | b_primes.each do |b| 15 | n = 0 16 | loop do 17 | f = n * (n + a) + b 18 | if is_prime?(f) 19 | n += 1 20 | else 21 | mn = [n - 1, old_max_n].max 22 | if mn != old_max_n 23 | # puts "new max_n: #{mn}, a: #{a}, b: #{b}" 24 | max_n[:max] = mn 25 | max_n[:a] = a 26 | max_n[:b] = b 27 | end 28 | old_max_n = mn 29 | break 30 | end 31 | end 32 | end 33 | end 34 | 35 | puts "max n: #{max_n[:max]}, a: #{max_n[:a]}, b: #{max_n[:b]}, ab: #{max_n[:a] * max_n[:b]}" 36 | puts "Saved lots of time! If b = -999 to 999, problem took 12 secs. Now it takes 2.9 secs." -------------------------------------------------------------------------------- /euler/028_number_spiral_diagonals.rb: -------------------------------------------------------------------------------- 1 | sum = 1 2 | lcv = 1 # last corner value 3 | ring = 1 4 | while true 5 | sum += (lcv + 2 * ring) + (lcv + 2 * ring * 2) + (lcv + 2 * ring * 3) + (lcv + 2 * ring * 4) 6 | break if 2 * ring + 1 == 1001 # 2 * ring + 1 = side length of each ring 7 | lcv += 8 * ring # last corner value of ring, before going to next ring 8 | ring += 1 9 | end 10 | 11 | puts "Sum of diagonal #'s for 1001 x 1001 spiral = #{sum}" 12 | 13 | =begin 14 | 15 | Analyzing it mathematically: 16 | 17 | Ring Side length Increment for corners Increment to next ring Total digits for Ring 18 | 1 3 2 4 8 19 | 2 5 4 6 16 20 | 3 7 6 8 24 21 | 22 | n 2n+1 2n 2n+2 8n 23 | 24 | Ring Corners 25 | 1 3,5,7,9 26 | 2 13,17,21,25 27 | 3 31,37,43,49 28 | 29 | 1 + 4*2 + 4*4 + 4*6 + ...4* 2n = last corner 30 | 31 | since side length = 1001 32 | 1001 = 2n+1 => n = 500 33 | 34 | since n = 500, increment for corners = 2(500) = 1000 35 | 36 | last corner = 1 + 4*2 (1 + 2 + 3 + ... + n) 37 | 38 | = 1 + 8 (1+2+3+...500) 39 | = 1 + 8 (500) 501 /2 40 | = 1002001 = last corner 41 | 42 | other 3 final corners, with difference of 2n = 1000, are 43 | 44 | 1001001 45 | 1000001 46 | 999001 47 | 48 | 1 + (1+2*1 + 1+2*2 + 1+2*3 + 1+2*4) 49 | + (last corner val+4(1) + lcv+4*2 + lcv+4*3 + lcv+4*4) 50 | + (lcv+6*1 + etc...)... 51 | 52 | Ring n=500: (lcv+2n*1 + lcv+2n*2 + lcv+2n*3 + lcv+2n*4) 53 | 54 | After mathematical analysis, the fast code is: 55 | 56 | sum = 1 57 | lcv = 1 # last corner value 58 | (1..500).each do |ring| 59 | sum += 4 * lcv + 20 * ring 60 | lcv += 8 * ring 61 | end 62 | 63 | =end -------------------------------------------------------------------------------- /euler/029_distinct_powers.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=29 2 | # How many distinct terms are in the sequence a^b for 2 ≤ a ≤ 100 and 2 ≤ b ≤ 100? 3 | 4 | answer = [] 5 | (2..100).each do |a| 6 | (2..100).each do |b| 7 | answer << a**b 8 | end 9 | end 10 | 11 | puts "# of distinct a^b terms = #{answer.uniq.size}" 12 | -------------------------------------------------------------------------------- /euler/030_digit_fifth_powers.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=30 2 | # Find the sum of all the numbers that can be written as the sum of 5th powers of their digits. 3 | 4 | start = Time.now 5 | answer = [] 6 | (11..200_000).each do |i| 7 | istring = i.to_s 8 | sum = 0 9 | 10 | (0..istring.size - 1).each do |j| 11 | sum += istring[j].to_i ** 5 12 | end 13 | 14 | answer << i if i == sum 15 | end 16 | 17 | puts "Sum of all #'s that can be written as sum of 5th powers of their digits = #{answer.inject(:+)}" 18 | p answer 19 | puts "Took #{Time.now - start} secs." 20 | 21 | =begin 22 | 23 | Sum of all #'s that can be written as sum of 5th powers of their digits = 443839 24 | [4150, 4151, 54748, 92727, 93084, 194979] 25 | Took 0.910941 secs. 26 | 27 | =end -------------------------------------------------------------------------------- /euler/031_coin_sums.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | http://projecteuler.net/problem=31 3 | 4 | Dynamic programming solution is much better. Read it: 5 | http://www.mathblog.dk/project-euler-31-combinations-english-currency-denominations/ 6 | 7 | In England the currency is made up of pound, £, and pence, p, and there are eight coins in general circulation: 8 | 9 | 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p). 10 | It is possible to make £2 in the following way: 11 | 12 | 1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p 13 | How many different ways can £2 be made using any number of coins? 14 | =end 15 | 16 | # Brute Force Solution 17 | 18 | n = 200 19 | v = [200, 100, 50, 20, 10, 5, 2, 1] 20 | c = [] # final array of all coin permutations that add up to n 21 | d = [] # each different permutation 22 | d7 = 0 23 | 24 | dmax = [] # max each coin may loop up to 25 | 26 | start = Time.now 27 | 28 | (0..v.size - 1).each do |i| 29 | dmax[i] = n / v[i] 30 | end 31 | 32 | (0..dmax[0]).each do |d0| 33 | dval = n - d0 * v[0] 34 | break if dval < 0 35 | (0..dmax[1]).each do |d1| 36 | dval -= d1 * v[1] 37 | break if dval < 0 38 | (0..dmax[2]).each do |d2| 39 | dval -= d2 * v[2] 40 | break if dval < 0 41 | (0..dmax[3]).each do |d3| 42 | dval -= d3 * v[3] 43 | break if dval < 0 44 | (0..dmax[4]).each do |d4| 45 | dval -= d4 * v[4] 46 | break if dval < 0 47 | (0..dmax[5]).each do |d5| 48 | dval -= d5 * v[5] 49 | break if dval < 0 50 | (0..dmax[6]).each do |d6| 51 | dval -= d6 * v[6] 52 | break if dval < 0 53 | d7 = dval / v[7] 54 | 55 | c << [d0, d1, d2, d3, d4, d5, d6, d7] 56 | #p [d0, d1, d2, d3, d4, d5, d6, d7] 57 | dval += d6 * v[6] 58 | end 59 | dval = n - d0 * v[0] - d1 * v[1] - d2 * v[2] - d3 * v[3] - d4 * v[4] 60 | end 61 | dval = n - d0 * v[0] - d1 * v[1] - d2 * v[2] - d3 * v[3] 62 | end 63 | dval = n - d0 * v[0] - d1 * v[1] - d2 * v[2] 64 | end 65 | dval = n - d0 * v[0] - d1 * v[1] 66 | end 67 | dval = n - d0 * v[0] 68 | end 69 | dval = n 70 | end 71 | 72 | puts "Brute force solution: For #{n}, we have #{c.size} ways to make it. They are:" 73 | #p c 74 | puts "It took #{Time.now - start} secs." 75 | 76 | # "For 200, we have 73682 ways to make it. They are: (lists them all). It took 0.094477 secs." 77 | 78 | # Optimal Solution 79 | 80 | start = Time.now 81 | 82 | def coin_sum(target) 83 | coin_sizes = [1, 2, 5, 10, 20, 50, 100, 200] 84 | ways = Array.new(201, 0) 85 | ways[0] = 1 86 | 87 | coin_sizes.each do |coin| 88 | #puts "coin: #{coin}" 89 | (coin..target).each do |sum| 90 | #puts "sum: #{sum}. sum-coin: #{sum-coin}" 91 | ways[sum] += ways[sum - coin] 92 | #puts "ways[#{sum}]: #{ways[sum]}" 93 | end 94 | end 95 | ways[target] 96 | end 97 | 98 | puts "Optimal solution: #{coin_sum(200)} ways to make the sum. It took #{Time.now - start} secs." 99 | 100 | # Optimal solution: 73682 ways to make the sum. It took 0.000282 secs. -------------------------------------------------------------------------------- /euler/031_test.rb: -------------------------------------------------------------------------------- 1 | 2 | Solution by counting down each denomination: 3 | 4 | count = 0 5 | 200.step(0, -200) {|a| 6 | a.step(0, -100) {|b| 7 | b.step(0, -50){|c| 8 | c.step(0, -20){|d| 9 | d.step(0, -10){|e| 10 | e.step(0, -5){|f| 11 | f.step(0, -2){|g| 12 | count += 1}}}}}}} 13 | p count 14 | 15 | 16 | Recursive solution in C/C++ 17 | 18 | #include 19 | 20 | int coins[8] = {200, 100, 50, 20, 10, 5,2,1}; 21 | 22 | int findposs(int money, int maxcoin) 23 | { 24 | int sum = 0; 25 | if(maxcoin == 7) return 1; 26 | for(int i = maxcoin; i<8;i++) 27 | { 28 | if (money-coins[i] == 0) sum+=1; 29 | if (money-coins[i] > 0) sum+=findposs(money-coins[i], i); 30 | } 31 | return sum; 32 | } 33 | 34 | int main() 35 | { 36 | cout<= c and c >= maxCoin 46 | end 47 | count 48 | end 49 | 50 | Dynamic Programming (Ruby). Runs in .06 secs 51 | 52 | coins = [200, 100, 50, 20, 10, 5, 2, 1] 53 | def ways sum, head, *rest 54 | return 1 if rest.empty? 55 | ws = 0 56 | 0.upto sum / head do |i| 57 | ws += ways(sum - head * i, *rest) 58 | end 59 | ws 60 | end 61 | p ways(200, *coins) -------------------------------------------------------------------------------- /euler/032_pandigital_products.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=32 2 | # Find the sum of all products whose multiplicand/multiplier/product 3 | # identity can be written as a 1 through 9 pandigital. 4 | # You may NOT have duplicate products! 5 | 6 | # Ex: 39 * 186 = 7254 (uses all digits 1-9) 7 | 8 | # Note: both factors can only be maximum 3 digit numbers! 9 | # Otherwise, we go over our 9 digit limit. 10 | 11 | # We have 920 nums from 2 to 987 with up to 4 unique digits and no 0's. 12 | t = Time.now 13 | @factors = [*2..1987].map(&:to_s).reject { |n| n =~ /0/ }.select { |n| n.chars.uniq == n.chars } 14 | 15 | def prod(factor1, factor2) 16 | factor1.to_i * factor2.to_i 17 | end 18 | 19 | def pandigital?(factor1, factor2) 20 | p = prod(factor1, factor2).to_s 21 | return false if p =~ /0/ # product has '0' in it 22 | concat = factor1 + factor2 + p 23 | return false if concat.size != 9 24 | return false if concat.chars.uniq != concat.chars # remove duplicate digits 25 | true 26 | end 27 | 28 | fpairs = [] 29 | @factors.each do |f1| 30 | f1_index = @factors.index(f1) 31 | @factors[f1_index + 1..-1].each do |f2| 32 | fpairs << [f1, f2] 33 | end 34 | end 35 | 36 | products = [] 37 | # nested array of factor pairs: [['39', '186'], ['41', '52'], ...] 38 | fpairs.each do |fpair| 39 | if pandigital?(fpair[0], fpair[1]) 40 | p = prod(fpair[0], fpair[1]) 41 | unless products.include?(p) 42 | products << p 43 | #puts "ppair: #{fpair[0]} #{fpair[1]}, prod: #{p}" 44 | end 45 | end 46 | end 47 | 48 | puts "Answer: #{products.inject(&:+)}, Time: #{Time.now - t} secs" 49 | -------------------------------------------------------------------------------- /euler/033_digit_canceling_fractions.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | http://projecteuler.net/problem=33 3 | The fraction 49/98 is a curious fraction, as an inexperienced mathematician in attempting to simplify it may incorrectly believe that 49/98 = 4/8, which is correct, is obtained by cancelling the 9s. 4 | 5 | There are exactly four non-trivial examples of this type of fraction, less than one in value, 6 | and containing two digits in the numerator and denominator. 7 | 8 | If the product of these four fractions is given in its lowest common terms, find the value of the denominator. 9 | =end 10 | start = Time.now 11 | 12 | fractions = {} 13 | (12..98).each do |t| 14 | 15 | (t + 1..99).each do |b| # top/bottom < 1 16 | # reject if t, b are both same or both multiples of 10 or 11. trivial. 17 | next if b == t || (b % 10 == 0 && t % 10 == 0) || (b % 11 == 0 && t % 11 == 0) 18 | t0 = t.to_s[0].to_f # first digit of top number 19 | t1 = t.to_s[1].to_f # second digit of top number 20 | b0 = b.to_s[0].to_f 21 | b1 = b.to_s[1].to_f 22 | 23 | f = t.to_f / b.to_f # we need floating point decimal. otherwise, integer divisions round to integer 24 | fractions["#{t}/#{b}"] = f if (t0 == b0 && f == t1/b1) || (t1 == b1 && f == t0/b0) \ 25 | || (t0 == b1 && f == t1/b0) || (t1 == b0 && f == t0/b1) 26 | end 27 | end 28 | 29 | p fractions 30 | frac = fractions.keys.map(&:to_r) # array of only fractions. to_r reduces fractions to simplest terms! 31 | product = frac.inject(:*).to_r 32 | denom = product.to_s.split('/')[-1] 33 | 34 | p "Product of #{frac} = #{product}. Denominator = #{denom}. Took #{Time.now - start} secs." 35 | 36 | =begin 37 | 38 | {"16/64"=>0.25, "19/95"=>0.2, "26/65"=>0.4, "49/98"=>0.5} 39 | "Product of [(1/4), (1/5), (2/5), (1/2)] = 1/100. Denominator = 100. Took 0.014906 secs." 40 | 41 | =end -------------------------------------------------------------------------------- /euler/034_digit_factorials.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=34 2 | # Find sum of all numbers which equal the sum of the factorial of their digits. 3 | 4 | fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] # factorials from 0-9 5 | total = 0 6 | 7 | start = Time.now 8 | 9 | (3..50000).each do |i| 10 | sum = 0 # sum of digit factorials for each number 11 | num = i.to_s.split('') 12 | num.map! {|x| x.to_i} # split i into array of single digit integers 13 | 14 | num.each do |n| 15 | sum += fact[n] 16 | end 17 | if i == sum 18 | total += sum 19 | print i 20 | print ' ' 21 | end 22 | end 23 | 24 | puts "Total = #{total}. Took #{Time.now - start} secs." 25 | 26 | # "Output: 145 40585 Total = 40730. Took 0.575524 secs. " -------------------------------------------------------------------------------- /euler/035_circular_primes.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=35 2 | # The number, 197, is called a circular prime because all rotations of the digits: 3 | # 197, 971, and 719, are themselves prime. How many circular primes are there < 1 million? 4 | 5 | def sieve(n) # makes array of all primes from 2 to n 6 | s = 3.step(n, 2).to_a # make array of odd integers from 3 to n. Skip evens. 7 | s.each do |p| 8 | next if p.nil? # go to next element if p has been marked empty 9 | break if p * p > n # p only needs to go up to sqrt(n) 10 | 11 | k, pval = 0, 0 12 | 13 | while pval < n 14 | pval = p * (p + k) # jump forward 2p at a time: p*p, p*p + 2p, p*p + 4p, etc. 15 | 16 | # Set all those multiples to nil. i = (pval - 3)/2 translates pvals to index i 17 | 18 | s[(pval - 3) / 2] = nil 19 | k += 2 20 | end 21 | 22 | end 23 | s.compact! # removes all nil elements from array 24 | s.unshift(2).sort # adds 2 as 1st element 25 | end 26 | 27 | def is_prime_sieve?(s, n) # trial division, only dividing by primes from sieve array 28 | return true if n.between?(2, 3) 29 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 30 | 31 | s.each do |d| 32 | return true if d * d > n # only must check up till sqrt(n)! 33 | return false if n % d == 0 # if n is divisible by a number in prime file 34 | end 35 | 36 | true 37 | end 38 | 39 | #------------------------------------------ 40 | start = Time.now 41 | 42 | s = sieve(1_000_000) # makes array of all primes < 1 million 43 | circular = 0 # number of circular primes 44 | 45 | 46 | s.each do |p| 47 | num = p.to_s.split('') # splits p into array of single digits 48 | primecheck = 0 49 | 50 | (num.size - 1).times do 51 | n = num.rotate!.join.to_i 52 | primecheck += 1 if is_prime_sieve?(s, n) 53 | end 54 | 55 | circular += 1 if primecheck == num.size - 1 56 | end 57 | 58 | puts "Below 1 million, we have #{circular} circular primes. Took #{Time.now - start} secs." -------------------------------------------------------------------------------- /euler/036_double_base_palindromes.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/index.php?section=problems&id=36 2 | # Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2. 3 | 4 | sum = 0 5 | (1..999_999).each do |n| 6 | nbin = n.to_s(2) 7 | sum += n if (n.to_s == n.to_s.reverse) && (nbin == nbin.reverse) 8 | end 9 | puts "Sum of all #'s that are palindromes in both base 10 and 2 = #{sum}" -------------------------------------------------------------------------------- /euler/040_champernowne_constant.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=40 2 | # If dn represents the nth digit of the fractional part, find the value of the following expression. 3 | # d1 × d10 × d100 × d1000 × d10000 × d100000 × d1000000 4 | 5 | def cconstant(n) 6 | count = 0 7 | digit = -1 8 | 9 | (1..n).each do |i| 10 | count += i.to_s.size 11 | if count >= n # we've hit our goal! 12 | puts i 13 | count -= i.to_s.size # go backwards 14 | 15 | istring = i.to_s 16 | 17 | j = 1 18 | while j <= istring.size # find where in i that count = n exactly 19 | count += j 20 | if count == n # increment in istring by 1 till we find goal 21 | digit = istring[j - 1].to_i 22 | break 23 | end 24 | j += 1 25 | end 26 | end 27 | 28 | return digit if digit >= 0 29 | end 30 | 31 | digit 32 | end 33 | 34 | start = Time.now 35 | 36 | product = 1 37 | 38 | (0..6).each do |power| 39 | d = cconstant(10**power) 40 | puts "The 10^#{power}th digit is #{d}." 41 | product *= d 42 | end 43 | 44 | puts "Product of these digits = #{product}. It took #{Time.now - start} secs." 45 | 46 | =begin 47 | 48 | 1 49 | The 10^0th digit is 1. 50 | 10 51 | The 10^1th digit is 1. 52 | 55 53 | The 10^2th digit is 5. 54 | 370 55 | The 10^3th digit is 3. 56 | 2777 57 | The 10^4th digit is 7. 58 | 22222 59 | The 10^5th digit is 2. 60 | 185185 61 | The 10^6th digit is 1. 62 | Product of these digits = 210. It took 0.106682 secs. 63 | 64 | =end 65 | -------------------------------------------------------------------------------- /euler/045_triangular_pentagonal_hexagonal.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=45 2 | # It can be verified that T285 = P165 = H143 = 40755. 3 | # Find the next triangle number that is also pentagonal and hexagonal. 4 | 5 | start = Time.now 6 | tnum = [*286..40_000].map{|t| t * (t + 1) / 2} 7 | pnum = [*166..40_000].map{|p| p * (3 * p - 1) / 2} 8 | hnum = [*144..40_000].map{|h| h * (2 * h - 1)} 9 | 10 | tpintersect = tnum & pnum 11 | phintersect = pnum & hnum 12 | 13 | puts "#{tpintersect} in #{Time.now-start} secs." if tpintersect = phintersect 14 | 15 | =begin 16 | "[1533776805] in 0.126874 secs." 17 | 18 | Alternate answers: 19 | 20 | It seemed superflous to have the triangle numbers since all hexagonal numbers are also triangle number. 21 | 22 | H = h*(2h-1) 23 | T = t*(t+1)/2 24 | generating t by t=2h-1, 25 | 26 | T = (2h-1)(2h-1+1)/2 = (2h-1)(2h)/2 = h(2h-1), 27 | To make calculation faster I ingored triangle aspect. 28 | 29 | 30 | You're absolutley right, but that is part of the process in solving these problems: stripping them down to the bare essentials. 31 | 32 | To solve this problem I ran through the hexagon numbers and, rather than compare with a separate array/list of pentagon numbers, I used a test. 33 | 34 | Given that P=n(3n-1)/2, we get the quadratic, 3n2-n-2P=0, and using the quadratic formula, n=(1+√(1+24P))/6 (only taking positive root). In other words, if the number being test, P, produces an integer in the formula, then it is a pentagon number. 35 | 36 | This allows the problem to be solving in a few lines of code and in the fraction of a second. For example, using BASIC: 37 | 38 | n=1 39 | do 40 | n=n+1 41 | h=n*(2*n-1) 42 | k=(1+math.sqrt(1+24*h))/6 43 | if k=int(k) then msgbox(h) 44 | loop 45 | =end -------------------------------------------------------------------------------- /euler/047_distinct_primes_factors.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=47 2 | # Find the first 4 consecutive integers to have 4 distinct prime factors. What is the first of these numbers? 3 | 4 | # Finds all prime factors (Problem #3) 5 | def primefact(n) 6 | f = 2 7 | factors = [] 8 | while f * f <= n # only need to check up to sqrt(n) 9 | if n % f == 0 10 | factors << f 11 | n = n / f # After you find a factor, only check quotient after that! 12 | else 13 | f += 1 14 | end 15 | end 16 | factors << n 17 | factors 18 | end 19 | 20 | i = 647 21 | c = 0 22 | previous = false # did you previously get # with 4 distinct prime factors? 23 | 24 | start = Time.now 25 | while true 26 | if primefact(i).uniq.size == 4 27 | if previous == true 28 | c += 1 29 | else 30 | c = 1 # reset count, to ensure these are CONSECUTIVE #'s 31 | end 32 | 33 | previous = true 34 | 35 | if c == 4 36 | puts "#{i - 3} is 1st of 4 consecutive #'s with 4 distinct prime factors." 37 | puts "Took #{Time.now - start} secs." 38 | exit 39 | end 40 | else 41 | previous = false 42 | end 43 | i += 1 44 | end -------------------------------------------------------------------------------- /euler/048_self_powers.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=48 2 | # Find the last ten digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000. 3 | 4 | # I didn't even need the BigDecimal class. Ruby handles large digits fine by itself. 5 | 6 | sum = 0 7 | (1..999).each do |i| # ignore 1000^1000, since its last digits are all 0's. 8 | sum += i**i 9 | end 10 | 11 | puts "Last 10 digits: #{sum.to_s[-10..-1]}" -------------------------------------------------------------------------------- /euler/049_prime_permutations.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | http://projecteuler.net/problem=49 3 | 4 | The arithmetic sequence, 1487, 4817, 8147, in which each of the terms increases by 3330, is unusual in two ways: (i) each of the three terms are prime, and, (ii) each of the 4-digit numbers are permutations of one another. 5 | 6 | There is one other 4-digit increasing sequence. What 12-digit number do you form by concatenating the three terms in this sequence? 7 | =end 8 | 9 | def sieve(n) # makes array of all primes from 2 to n 10 | s = 3.step(n, 2).to_a # make array of odd integers from 3 to n. Skip evens. 11 | s.each do |p| 12 | next if p.nil? # go to next element if p has been marked empty 13 | break if p * p > n # p only needs to go up to sqrt(n) 14 | 15 | k, pval = 0, 0 16 | 17 | while pval < n 18 | pval = p * (p + k) # jump forward 2p at a time: p*p, p*p + 2p, p*p + 4p, etc. 19 | 20 | # Set all those multiples to nil. i = (pval - 3)/2 translates pvals to index i 21 | 22 | s[(pval - 3) / 2] = nil 23 | k += 2 24 | end 25 | 26 | end 27 | s.compact! # removes all nil elements from array 28 | s.unshift(2).sort # adds 2 as 1st element 29 | end 30 | 31 | def is_prime_sieve?(s, n) # trial division, only dividing by primes from sieve array 32 | return true if n.between?(2, 3) 33 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 34 | 35 | s.each do |d| 36 | return true if d * d > n # only must check up till sqrt(n)! 37 | return false if n % d == 0 # if n is divisible by a number in prime file 38 | end 39 | 40 | true 41 | end 42 | 43 | #------------------------------------------------------- 44 | start = Time.now 45 | primes = sieve(9973) # make array of primes < 10,000 46 | 47 | primes.each do |p| 48 | next if p < 1009 # we only want 4-digit numbers 49 | break if p > 3313 # biggest prime < 10,000 is 9973. Minus 6660 = 3313. 50 | p2 = p + 3330 51 | p3 = p + 6660 52 | next if !is_prime_sieve?(primes, p2) || !is_prime_sieve?(primes, p3) # only want other primes 53 | 54 | # makes sorted permutations array like this: ["1234", "1243", "1324", "1342", "1423",...] 55 | pperm = p.to_s.split('').permutation.map(&:join).sort 56 | p2perm = p2.to_s.split('').permutation.map(&:join).sort 57 | p3perm = p3.to_s.split('').permutation.map(&:join).sort 58 | 59 | if pperm == p2perm && pperm == p3perm # if all 3 are permutations of each other 60 | puts "12 digit answer: #{p.to_s + p2.to_s + p3.to_s}" 61 | puts "It took #{Time.now - start} secs." 62 | end 63 | end -------------------------------------------------------------------------------- /euler/050_consecutive_prime_sum.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=50 2 | # Which prime, below one-million, can be written as the sum of the most consecutive 3 | # primes? The longest sum of consecutive primes < 1000 that adds to a prime, 4 | # contains 21 terms, and is equal to 953. 5 | require 'pry' 6 | require 'pry-debugger' 7 | 8 | # Sieve of Eratosthenes 9 | def sieve(n) # makes array of all primes from 2 to n 10 | s = 3.step(n, 2).to_a # make array of odd integers from 3 to n. Skip evens. 11 | s.each do |p| 12 | next if p.nil? # go to next element if p has been marked empty 13 | break if p * p > n # p only needs to go up to sqrt(n) 14 | 15 | k, pval = 0, 0 16 | 17 | while pval < n 18 | pval = p * (p + k) # jump forward 2p at a time: p*p, p*p + 2p, p*p + 4p, etc. 19 | 20 | # Set all those multiples to nil. i = (pval - 3)/2 translates pvals to index i 21 | 22 | s[(pval - 3) / 2] = nil 23 | k += 2 24 | end 25 | 26 | end 27 | s.compact! # removes all nil elements from array 28 | s.unshift(2).sort # adds 2 as 1st element 29 | end 30 | 31 | def is_prime_sieve?(s, n) # trial division, only dividing by primes from sieve array 32 | return true if n.between?(2, 3) 33 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 34 | 35 | s.each do |d| 36 | return true if d * d > n # only must check up till sqrt(n)! 37 | return false if n % d == 0 # if n is divisible by a number in prime array 38 | end 39 | 40 | true 41 | end 42 | #------------------------------------------------------- 43 | start = Time.now 44 | primes = sieve(1_000_000) # quickly makes array of all primes < 1 million 45 | sums = {} 46 | 47 | # Copying trick we did in Problem 23, instead of subtracting each prime from test prime, 48 | # we create all possible sums from consecutive primes, then check if sums are prime. 49 | primes.each_with_index do |p, i| 50 | break if i > primes.size / 21 - 1 51 | c = 1 # number of consecutive primes added to get sum 52 | sum = p 53 | j = i + 1 54 | 55 | # I doubt we'll have answers > 48,000 (1 million/21) that could top 21 consecutive primes 56 | while j < primes.size / 21 57 | p2 = primes[j] 58 | c += 1 59 | sum += p2 60 | break if sum >= 1_000_000 61 | if is_prime_sieve?(primes, sum) # if sum = prime 62 | 63 | # Put sum in hash, with count. But DON'T OVERWRITE KEY 64 | # WITH NEW VALUE IF IT ALREADY HAS ONE! Original count is the biggest for the key. 65 | 66 | sums[sum] = c if !sums.has_key?(sum) 67 | end 68 | j += 1 69 | end 70 | end 71 | 72 | max = sums.values.max 73 | puts "Longest sum of consecutive primes is for #{sums.key(max)}, with #{max} terms. It took #{Time.now - start} secs." 74 | 75 | # "Longest sum of consecutive primes is for 997651, with 543 terms. It took 1.856201 secs." 76 | 77 | =begin 78 | - Different strategy: create array of all consecutive sums, sumarray. 79 | - With nested loop, take later sum - earlier sum. If that's a prime, call that maxprime. With next loop, look ahead in primes only the same as max consecutive terms now. 80 | 81 | sumarray = [] 82 | 83 | sum = 0 84 | maxprime = 0 85 | sumprimes = 0 86 | count = 0 87 | 88 | while sum < 1_000_000 89 | sum += primes[count] 90 | sumarray << sum 91 | count += 1 92 | end 93 | 94 | terms = 1 95 | (0..count - 2).each do |i| 96 | (i + terms..count - 1).each do |j| 97 | sumprimes = sumarray[j] - sumarray[i] 98 | break if sumprimes > 1_000_000 99 | #puts "i = #{i}, j = #{j}" 100 | if j > i && is_prime_sieve?(primes, sumprimes) 101 | terms, maxprime = j - i, sumprimes 102 | end 103 | end 104 | end 105 | 106 | puts "Longest sum of consecutive primes is for #{maxprime}, with #{terms} terms. It took #{Time.now - start} secs." 107 | 108 | =end 109 | -------------------------------------------------------------------------------- /euler/052_permuted_multiples.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=52 2 | # Find the smallest positive integer, x, such that 2x, 3x, 4x, 5x, and 6x, contain the same digits. 3 | 4 | start = Time.now 5 | (10..200_000).each do |x| 6 | p = [] 7 | (1..6).each do |i| 8 | p[i] = (i * x).to_s.split('').sort # turns x into ['1', '2', '3', '4'] 9 | end 10 | 11 | if [p[2], p[3], p[4], p[5], p[6]].all? {|y| y == p[1]} # if they're all equal 12 | 13 | puts "Smallest x such that 2x, 3x, 4x, 5x, and 6x, contain same digits = #{x}.\nMultiples: #{2*x}, #{3*x}, #{4*x}, #{5*x}, #{6*x}. Took #{Time.now - start} secs." 14 | end 15 | end 16 | 17 | =begin 18 | Smallest x such that 2x, 3x, 4x, 5x, and 6x, contain same digits = 142857. 19 | Multiples: 285714, 428571, 571428, 714285, 857142. Took 11.35892 secs. 20 | =end -------------------------------------------------------------------------------- /euler/053_combinatoric_selections.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=53 2 | # How many, not necessarily distinct, values of nCr, for 1 ≤ n ≤ 100, are > 1 million? 3 | 4 | def comb(n, r) 5 | # n(n - 1)(n - 2)...(r + 1) / (n - r)! 6 | answer = (r + 1..n).inject(:*) / (2..n - r).inject(:*) 7 | end 8 | 9 | r = 3 10 | count = 0 11 | 12 | (6..100).each do |n| 13 | while r < n - 1 14 | count += 1 if comb(n, r) > 1_000_000 15 | r += 1 16 | end 17 | r = 2 18 | end 19 | 20 | puts "# of nCr combos > 1 million = #{count}" -------------------------------------------------------------------------------- /euler/067_maximum_path_sum2.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=67 2 | # Find the maximum total from top to bottom in triangle2.txt, a text file containing a 100-row triangle. 3 | # This is a much more difficult version of Problem 18. It is not possible to try every route to solve this problem, as there are 299 altogether! If you could check 1 trillion routes every second it would take over 20 billion years to check them all. There is an efficient algorithm to solve it. 4 | 5 | t = IO.readlines('triangle2.txt') 6 | t.map!(&:chomp).map!(&:split) # removes whitespace and splits each line with commas. Makes each line separate nested array 7 | 8 | t.each do |a| 9 | a.map!(&:to_i) # convert all elements in nested array to integers 10 | end 11 | 12 | =begin # triangle looks like this, with 100 rows 13 | [[75], 14 | [95, 64], 15 | [17, 47, 82], 16 | [18, 35, 87, 10]] 17 | =end 18 | 19 | sum, sumup, sumdiag = 0, 0, 0 20 | 21 | (t.size - 1).downto(1).each do |row| # start from bottom of triangle and move upwards 22 | (0..row - 1).each do |col| 23 | # puts "row = #{row}, col = #{col}, t(row,col) = #{t[row][col]}" 24 | 25 | sumup = t[row][col] + t[row - 1][col] # sum vertically up triangle 26 | sumdiag = t[row][col + 1] + t[row - 1][col] # sum from lower right to upper left 27 | if sumup > sumdiag 28 | t[row - 1][col] = sumup # replace value in row above with the greater sum, from bottom up 29 | else 30 | t[row - 1][col] = sumdiag 31 | end 32 | end 33 | end 34 | 35 | puts "Maximum sum from top to bottom = #{t[0][0]}. (I really added this from bottom to top.)" -------------------------------------------------------------------------------- /euler/079_passcode_derivation.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | http://projecteuler.net/problem=79 3 | 4 | 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. 5 | 6 | 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. 7 | =end 8 | 9 | start = Time.now 10 | 11 | # read file into array. strip out '\n' characters after number. 12 | passcode = IO.readlines("keylog.txt").map(&:strip) 13 | code, tcommon, notcommon = [], [], [] 14 | tcommon = [-1, -1, -1] # array to track the location of the digits code has in common with each t 15 | 16 | passcode.each do |n| 17 | t = n.split('') # Ex: ['1','2','3'] 18 | puts "t = #{t}, code = #{code}" 19 | if (code & t).empty? # if t has no digits in common with code (intersection) 20 | code = code | t # append all t to code (union) 21 | next 22 | end 23 | 24 | code.each do |c| # tracks location of digits code and t have in common 25 | if t.include?(c) 26 | tcommon[t.index(c)] = code.index(c) # common[1] = 3 means the digit in t[1] is at code[3] 27 | end 28 | end 29 | 30 | print "tcommon = #{tcommon}, " 31 | 32 | # array of all indices of tcommon NOT in common with code. Intersection. 33 | notcommon = tcommon.each_index.select{|i| tcommon[i] == -1} 34 | puts "notcommon = #{notcommon}" 35 | 36 | if notcommon.size == 2 # t has only 1 digit in common with code 37 | code = code | t # only append 2 digits who are NOT in common. Union. 38 | tcommon = [-1, -1, -1] 39 | next 40 | end 41 | 42 | if notcommon.size == 1 # t has 2 digits in common with code 43 | case notcommon[0] 44 | when 0 # tcommon[0] = -1 45 | if tcommon[1] > tcommon[2] # if order of these digits in code is reversed 46 | # swap tcommon[1] and tcommon[2] digits in code 47 | code[tcommon[1]], code[tcommon[2]] = code[tcommon[2]], code[tcommon[1]] 48 | end 49 | # at tcommon[1] position in code, INSERT number tcommon[0]. Preserve correct order. 50 | code.insert(tcommon[1], t[0]) 51 | when 1 # tcommon[1] = -1 52 | if tcommon[0] > tcommon[2] 53 | code[tcommon[0]], code[tcommon[2]] = code[tcommon[2]], code[tcommon[0]] 54 | end 55 | code.insert(tcommon[2], t[1]) 56 | when 2 # tcommon[2] = -1 57 | if tcommon[0] > tcommon[1] 58 | code[tcommon[0]], code[tcommon[1]] = code[tcommon[1]], code[tcommon[0]] 59 | end 60 | code << t[2] 61 | end 62 | 63 | end 64 | 65 | if notcommon.size == 0 # t has ALL digits in common with code 66 | if tcommon[0] < tcommon[1] && tcommon[1] < tcommon[2] # all ok. don't change code. 67 | tcommon = [-1, -1, -1] 68 | next 69 | elsif tcommon[0] > tcommon[1] && tcommon[0] < tcommon[2] 70 | code[tcommon[0]], code[tcommon[1]] = code[tcommon[1]], code[tcommon[0]] 71 | elsif tcommon[1] > tcommon[2] && tcommon[0] < tcommon[2] 72 | code[tcommon[1]], code[tcommon[2]] = code[tcommon[2]], code[tcommon[1]] 73 | else 74 | code[tcommon[0]], code[tcommon[2]] = code[tcommon[2]], code[tcommon[0]] 75 | end 76 | end 77 | 78 | tcommon = [-1, -1, -1] # reset 79 | end 80 | 81 | puts "Shortest secret passcode = #{code.join}. Took #{Time.now - start} secs." 82 | 83 | =begin 84 | 85 | t = ["3", "1", "9"], code = [] 86 | t = ["6", "8", "0"], code = ["3", "1", "9"] 87 | t = ["1", "8", "0"], code = ["3", "1", "9", "6", "8", "0"] 88 | tcommon = [1, 4, 5], notcommon = [] 89 | t = ["6", "9", "0"], code = ["3", "1", "9", "6", "8", "0"] 90 | tcommon = [3, 2, 5], notcommon = [] 91 | t = ["1", "2", "9"], code = ["3", "1", "6", "9", "8", "0"] 92 | tcommon = [1, -1, 3], notcommon = [1] 93 | t = ["6", "2", "0"], code = ["3", "1", "6", "2", "9", "8", "0"] 94 | tcommon = [2, 3, 6], notcommon = [] 95 | t = ["7", "6", "2"], code = ["3", "1", "6", "2", "9", "8", "0"] 96 | tcommon = [-1, 2, 3], notcommon = [0] 97 | t = ["6", "8", "9"], code = ["3", "1", "7", "6", "2", "9", "8", "0"] 98 | tcommon = [3, 6, 5], notcommon = [] 99 | t = ["7", "6", "2"], code = ["3", "1", "7", "6", "2", "8", "9", "0"] 100 | tcommon = [2, 3, 4], notcommon = [] 101 | t = ["3", "1", "8"], code = ["3", "1", "7", "6", "2", "8", "9", "0"] 102 | tcommon = [0, 1, 5], notcommon = [] 103 | t = ["3", "6", "8"], code = ["3", "1", "7", "6", "2", "8", "9", "0"] 104 | tcommon = [0, 3, 5], notcommon = [] 105 | t = ["7", "1", "0"], code = ["3", "1", "7", "6", "2", "8", "9", "0"] 106 | tcommon = [2, 1, 7], notcommon = [] 107 | t = ["7", "2", "0"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 108 | tcommon = [1, 4, 7], notcommon = [] 109 | t = ["7", "1", "0"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 110 | tcommon = [1, 2, 7], notcommon = [] 111 | t = ["6", "2", "9"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 112 | tcommon = [3, 4, 6], notcommon = [] 113 | t = ["1", "6", "8"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 114 | tcommon = [2, 3, 5], notcommon = [] 115 | t = ["1", "6", "0"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 116 | tcommon = [2, 3, 7], notcommon = [] 117 | t = ["6", "8", "9"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 118 | tcommon = [3, 5, 6], notcommon = [] 119 | t = ["7", "1", "6"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 120 | tcommon = [1, 2, 3], notcommon = [] 121 | t = ["7", "3", "1"], code = ["3", "7", "1", "6", "2", "8", "9", "0"] 122 | tcommon = [1, 0, 2], notcommon = [] 123 | t = ["7", "3", "6"], code = ["7", "3", "1", "6", "2", "8", "9", "0"] 124 | tcommon = [0, 1, 3], notcommon = [] 125 | Shortest secret passcode = 73162890 Took 0.004095 secs. 126 | ----------------------------------------- 127 | Lots of great strategies here. I like the heuristics and statistical ideas! Anyway I used a LOT of if statements. My code ran in 4 ms. 128 | 129 | For each new 3-digit data, I used an array, tcommon, to track the positions that its digits had in common with the passcode I was building. The elements of tcommon were positions in passcode that had values the same as the new 3-digit value. The key point: if the indexes in tcommon were in numerical order, passcode was ok and didn't need change. If parts were out of order, I swapped all the corresponding elements of passcode to match the order in the 3-digit data. 130 | 131 | If it had nothing in common, I added the 3-digit data to the end of passcode. 132 | 133 | If it had 1 digit in common, I used a set union and added the 2 non-common digits to the end of passcode. 134 | 135 | If it had 2 digits in common, I checked the numerical order of the passcode indexes in tcommon. If out of order, I swapped them until they were in numerical order. 136 | 137 | If it had all 3 digits in common, again I checked numerical order of passcode indexes in tcommon. Swapped again if needed. 138 | 139 | Took many if statements. Looks ugly. But it worked in 4 ms and got the answer with just half the data. 140 | 141 | =end -------------------------------------------------------------------------------- /euler/081_data.txt: -------------------------------------------------------------------------------- 1 | 131,673,234,103,18 2 | 201,96,342,965,150 3 | 630,803,746,422,111 4 | 537,699,497,121,956 5 | 805,732,524,37,331 -------------------------------------------------------------------------------- /euler/081_path_sum_2_ways.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=81 2 | # Find the minimal path sum, in matrix.txt, containing a 80 by 80 matrix, from the top 3 | # left to the bottom right by only moving right and down. 4 | 5 | # Dijkstra's algorithm with priority queue 6 | require 'pry' 7 | class PathSum 8 | INPUT_FILE = './081_matrix.txt' 9 | ROWS, COLS = 80, 80 10 | INFINITY = (2 ** (0.size * 8 - 2) - 1) # max Fixnum integer value 11 | 12 | attr_accessor :matrix, :input, :start, :finish, :dist, :pqueue 13 | 14 | def initialize(start = [0, 0], finish = [ROWS - 1, COLS - 1]) 15 | @start, @finish = start, finish 16 | @input = IO.readlines(INPUT_FILE).map(&:chomp) # reads file as array 17 | 18 | @matrix = Array.new(ROWS) do |row| 19 | Array.new(COLS) do |col| 20 | input[row].split(',').map(&:to_i)[col] 21 | end 22 | end 23 | @dist = Array.new(ROWS) { |row| Array.new(COLS) { |col| INFINITY } } 24 | 25 | # dist = min distance to that point, that we know so far 26 | @dist[start[0]][start[1]] = matrix[start[0]][start[1]] 27 | @pqueue = { [start[0], start[1]] => matrix[start[0]][start[1]] } # priority queue 28 | end 29 | 30 | def min_path 31 | s = Time.now 32 | while pqueue 33 | # Dequeue. Takes closest node (lowest total distance) first. 34 | minvalue = pqueue.values.min 35 | minkey = pqueue.key(minvalue) 36 | pqueue.delete(minkey) 37 | current = { minkey => minvalue } 38 | 39 | if current.keys.first == [finish[0], finish[1]] 40 | puts "It took #{Time.now - s} secs." 41 | print_path 42 | break 43 | end 44 | row, col = current.keys.first[0], current.keys.first[1] 45 | 46 | downrow = [row + 1, col] if row + 1 <= finish[0] 47 | rightcol = [row, col + 1] if col + 1 <= finish[1] 48 | 49 | # Go through each adjacent node 50 | [downrow, rightcol].each do |node| 51 | next if node.nil? 52 | alt_distance = dist[row][col] + matrix[node[0]][node[1]] 53 | 54 | # if total dist to each adj node < min dist to there now, replace min dist at node 55 | if alt_distance < dist[node[0]][node[1]] 56 | dist[node[0]][node[1]] = alt_distance 57 | # Put each adjacent node on priority queue 58 | pqueue[node] = alt_distance 59 | end 60 | end 61 | end 62 | 63 | puts "Min distance: #{dist[finish[0]][finish[1]]}" 64 | end 65 | 66 | def print_path 67 | row, col = finish[0], finish[1] 68 | final_path = [] 69 | # find path by going backwards, from finish point to start. 70 | # pick each node that has LOWER total distance, to left and up. 71 | until row == start[0] && col == start[1] 72 | final_path.unshift([row, col]) 73 | dist[row][col - 1] < dist[row - 1][col] ? col -= 1 : row -= 1 74 | end 75 | final_path.unshift([row, col]) 76 | print "Final path: #{final_path}\n" 77 | end 78 | end 79 | 80 | PathSum.new.min_path 81 | -------------------------------------------------------------------------------- /euler/082_path_sum_3_ways.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=82 2 | # Find the minimal path sum, in matrix.txt, containing a 80 by 80 matrix, from the 3 | # left column to right column by moving 3 ways (up, down, and right). 4 | 5 | # Dijkstra's algorithm with priority queue 6 | class PathSum 7 | INPUT_FILE = './081_matrix.txt' 8 | ROWS, COLS = 80, 80 9 | INFINITY = (2 ** (0.size * 8 - 2) - 1) # max Fixnum integer value 10 | 11 | attr_accessor :matrix, :input, :start, :finish, :dist, :pqueue 12 | 13 | def initialize(startcol = 0, finishcol = COLS - 1) 14 | @start, @finish = [0, startcol], [0, finishcol] 15 | @input = IO.readlines(INPUT_FILE).map(&:chomp) # reads file as array 16 | 17 | @matrix = Array.new(ROWS) do |row| 18 | Array.new(COLS) do |col| 19 | input[row].split(',').map(&:to_i)[col] 20 | end 21 | end 22 | setup 23 | end 24 | 25 | def setup 26 | @dist = Array.new(ROWS) { |row| Array.new(COLS) { |col| INFINITY } } 27 | 28 | # dist = min distance to that point, that we know so far 29 | @dist[start[0]][start[1]] = matrix[start[0]][start[1]] 30 | @pqueue = { [start[0], start[1]] => matrix[start[0]][start[1]] } # priority queue 31 | end 32 | 33 | # Takes closest node (lowest total distance) first. 34 | def dequeue 35 | minvalue = pqueue.values.min 36 | minkey = pqueue.key(minvalue) 37 | @pqueue.delete(minkey) 38 | { minkey => minvalue } 39 | end 40 | 41 | def min_path 42 | min_dist = INFINITY 43 | min_path = [] 44 | ROWS.times do |startrow| 45 | @start[0] = startrow 46 | setup 47 | while pqueue 48 | node = dequeue 49 | row, col = node.keys.first[0], node.keys.first[1] 50 | 51 | if col == finish[1] # we're done! 52 | path = print_path(row, col) 53 | dist_across = path.inject(0) { |length, n| length + matrix[n[0]][n[1]] } 54 | puts "Distance: #{dist_across}" 55 | if dist_across < min_dist 56 | min_dist = dist_across 57 | min_path = path 58 | end 59 | break 60 | end 61 | 62 | uprow = [row - 1, col] if row - 1 >= 0 63 | downrow = [row + 1, col] if row + 1 <= ROWS - 1 64 | rightcol = [row, col + 1] if col + 1 <= finish[1] 65 | 66 | # Go through each adjacent node 67 | [uprow, downrow, rightcol].each do |n| 68 | next if n.nil? 69 | alt_distance = dist[row][col] + matrix[n[0]][n[1]] 70 | 71 | # if total dist to each adj node < min dist to there now, replace min dist at node 72 | if alt_distance < dist[n[0]][n[1]] 73 | dist[n[0]][n[1]] = alt_distance 74 | # Put each adjacent node on priority queue 75 | pqueue[n] = alt_distance 76 | end 77 | end 78 | end 79 | end 80 | 81 | puts "\nSmallest Path Distance: #{min_dist}" 82 | puts "Shortest Path: #{min_path}" 83 | end 84 | 85 | def dist_3_ways(row, col) 86 | return INFINITY unless row.between?(0, ROWS - 1) && col.between?(start[1], finish[1]) 87 | dist[row][col] 88 | end 89 | 90 | def print_path(row, col) 91 | final_path = [] 92 | # find path by going backwards, from finish point to start. 93 | # pick each node that has LOWER total distance, to left and up. 94 | until row == start[0] && col == start[1] 95 | final_path.unshift([row, col]) 96 | 97 | left = dist_3_ways(row, col - 1) 98 | up = dist_3_ways(row - 1, col) 99 | down = dist_3_ways(row + 1, col) 100 | 101 | # min total distance value, in LEFT, UP, and DOWN directions 102 | min = [left, up, down].each_with_index.min.last 103 | case min 104 | when 0 then col -= 1 105 | when 1 then row -= 1 106 | else row += 1 107 | end 108 | end 109 | final_path.unshift([row, col]) 110 | print "Path: #{final_path} " 111 | final_path 112 | end 113 | end 114 | 115 | PathSum.new.min_path 116 | -------------------------------------------------------------------------------- /euler/083_path_sum_4_ways.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=83 2 | # Find the minimal path sum, in matrix.txt, containing an 80 x 80 matrix, from 3 | # top left to bottom right, by moving 4 directions (left, right, up, and down). 4 | 5 | # Dijkstra's algorithm with priority queue 6 | class PathSum 7 | INPUT_FILE = './081_matrix.txt' 8 | ROWS, COLS = 80, 80 9 | INFINITY = (2 ** (0.size * 8 - 2) - 1) # max Fixnum integer value 10 | 11 | attr_accessor :matrix, :input, :start, :finish, :dist, :pqueue 12 | 13 | def initialize(start = [0, 0], finish = [ROWS - 1, COLS - 1]) 14 | @start, @finish = start, finish 15 | @input = IO.readlines(INPUT_FILE).map(&:chomp) # reads file as array 16 | 17 | @matrix = Array.new(ROWS) do |row| 18 | Array.new(COLS) do |col| 19 | input[row].split(',').map(&:to_i)[col] 20 | end 21 | end 22 | setup 23 | end 24 | 25 | def setup 26 | @dist = Array.new(ROWS) { |row| Array.new(COLS) { |col| INFINITY } } 27 | 28 | # dist = min distance to that point, that we know so far 29 | @dist[start[0]][start[1]] = matrix[start[0]][start[1]] 30 | @pqueue = { [start[0], start[1]] => matrix[start[0]][start[1]] } # priority queue 31 | end 32 | 33 | # Takes closest node (lowest total distance) first. 34 | def dequeue 35 | minvalue = pqueue.values.min 36 | minkey = pqueue.key(minvalue) 37 | @pqueue.delete(minkey) 38 | { minkey => minvalue } 39 | end 40 | 41 | def min_path 42 | t = Time.now 43 | while pqueue 44 | node = dequeue 45 | row, col = node.keys.first[0], node.keys.first[1] 46 | 47 | if [row, col] == [finish[0], finish[1]] # we're done! 48 | puts "Time: #{Time.now - t} secs" 49 | path = print_path(row, col) 50 | min_dist = path.inject(0) { |length, n| length + matrix[n[0]][n[1]] } 51 | break 52 | end 53 | 54 | uprow = [row - 1, col] if row - 1 >= 0 55 | downrow = [row + 1, col] if row + 1 <= finish[0] 56 | leftcol = [row, col - 1] if col - 1 >= 0 57 | rightcol = [row, col + 1] if col + 1 <= finish[1] 58 | 59 | # Go through each adjacent node 60 | [uprow, downrow, leftcol, rightcol].each do |n| 61 | next if n.nil? 62 | alt_distance = dist[row][col] + matrix[n[0]][n[1]] 63 | 64 | # if total dist to each adj node < min dist to there now, replace min dist at node 65 | if alt_distance < dist[n[0]][n[1]] 66 | dist[n[0]][n[1]] = alt_distance 67 | # Put each adjacent node on priority queue 68 | pqueue[n] = alt_distance 69 | end 70 | end 71 | end 72 | 73 | puts "\nSmallest Path Distance: #{min_dist}" 74 | puts "Shortest Path: #{path}" 75 | end 76 | 77 | def dist_4_ways(row, col) 78 | return INFINITY unless row.between?(start[0], finish[0]) && col.between?(start[1], finish[1]) 79 | dist[row][col] 80 | end 81 | 82 | def print_path(row, col) 83 | final_path = [] 84 | # find path by going backwards, from finish point to start. 85 | # pick each node that has LOWER total distance, to left and up. 86 | until row == start[0] && col == start[1] 87 | final_path.unshift([row, col]) 88 | 89 | up = dist_4_ways(row - 1, col) 90 | down = dist_4_ways(row + 1, col) 91 | left = dist_4_ways(row, col - 1) 92 | right = dist_4_ways(row, col + 1) 93 | 94 | # min total distance value, in UP, DOWN, LEFT, RIGHT directions 95 | min = [up, down, left, right].each_with_index.min.last 96 | case min 97 | when 0 then row -= 1 98 | when 1 then row += 1 99 | when 2 then col -= 1 100 | else col += 1 101 | end 102 | end 103 | final_path.unshift([row, col]) 104 | end 105 | end 106 | 107 | PathSum.new.min_path 108 | -------------------------------------------------------------------------------- /euler/092_square_digit_chains.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=92 2 | 3 | class SquareDigit 4 | SQ = [*1..9].map{|i| i*i}.unshift(0) # array of squares from 1..81 5 | 6 | attr_reader :max, :results, :table, :table89 7 | def initialize(max) 8 | @max = max 9 | @results = [] 10 | # out of 10 million, only get 567 possible 1st sums = (9 * 9) * 7 11 | @table = [*1..567].map {|n| sum_digits(n)}.unshift(0) # sums of all 567 1st sums 12 | 13 | # maps each sum to T/F, to avoid recursion later 14 | @table89 = {} 15 | table[1..-1].each {|sum| @table89[sum] = sum_digits_89(sum) } 16 | end 17 | 18 | def sum_digits(num) 19 | num.to_s.chars.inject(0) {|s, digit| s + SQ[digit.to_i]} 20 | end 21 | 22 | # tell (through recursion) if ultimate sum = 89 or not 23 | def sum_digits_89(num) 24 | r = results[num] 25 | return r unless r.nil? 26 | sum = sum_digits(num) 27 | results[num] = true if sum == 89 28 | results[num] = false if sum == 1 29 | sum_digits_89(sum) 30 | end 31 | 32 | def run 33 | sum_to_one = [] 34 | (1..max - 1).each do |num| 35 | 36 | # MUCH FASTER way to sum digits in number than .to_s.chars.map(&:to_i).inject!!! 37 | first_sum = 0 38 | begin 39 | first_sum += SQ[num % 10] # adds units digit squared 40 | num /= 10 # num is shifted to right 41 | end until num == 0 42 | 43 | # SLOW WAY!!! Don't use. 44 | # first_sum = num.to_s.chars.map(&:to_i).inject(0) {|fs, v| fs + SQ[v]} 45 | 46 | next if table89[table[first_sum]] # skip num if its sum = 89 47 | sum_to_one << num 48 | end 49 | puts "nums < 10,000,000 whose square digits sum to 89: #{max - 1 - sum_to_one.count}" 50 | end 51 | 52 | end 53 | 54 | time = Time.now 55 | s = SquareDigit.new(10_000_000) 56 | s.run 57 | 58 | puts "Total time: #{Time.now - time} secs" 59 | 60 | # 1st draft of my program took 216.3 secs for 10 million numbers. I did recursion to sum each number. 61 | 62 | # 2nd draft used a lookup table for 567 different 1st sums, then another lookup table to 63 | # map 1st sum to true/false values. It took 114.5 secs. 64 | 65 | # After removing "sort" from num.to_s.sort.chars, I cut it dramatically to 84 secs. 66 | 67 | # Finally, after avoiding cumbersome to_s.chars.map(&:to_i).inject to make 1st sums of digits, 68 | # I dramatically dropped my time to only 13.5 secs for 10 million numbers!!! 69 | # That's an 84% drop in time from using to_s => to_i! 70 | 71 | # I cut my starting time by 94%, using this better algorithm!!! It's 16 times as fast as before!!! -------------------------------------------------------------------------------- /euler/421_prime_factors.rb: -------------------------------------------------------------------------------- 1 | # http://projecteuler.net/problem=421 2 | # Find sum of the distinct prime factors of n^15+1 not exceeding 10^8 3 | # 1 <= n <= 10^11 4 | require 'pry' 5 | def primef(n) 6 | f = 2 7 | factors = [] 8 | while f * f <= n # only need to check up to sqrt(n) 9 | if n % f == 0 10 | factors << f 11 | n = n / f # key line! After you find a factor, only check quotient after that! 12 | else 13 | f += 1 14 | end 15 | end 16 | factors << n 17 | factors 18 | end 19 | 20 | start = Time.now 21 | sum = 0 22 | (2..10**11).each do |n| 23 | factors = primef(n**15 + 1).select{|i| i < 1e8.to_i} 24 | sum += factors.uniq.inject(:+) 25 | print "Sum = #{sum}. Factors = #{factors.uniq}" 26 | puts 27 | #binding.pry 28 | end 29 | 30 | puts "Sum of distinct prime factors of n^15+1 that are < 10^8 = #{sum}. \ 31 | Took #{Time.now - start} secs." -------------------------------------------------------------------------------- /euler/Permutation Generation Methods - Sedgewick.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayning0/ProjectEuler-and-Algorithms/0a9fbbc1b5c8fd51ec2d33b8a7915c12128f5bd1/euler/Permutation Generation Methods - Sedgewick.pdf -------------------------------------------------------------------------------- /euler/Permutation Powerpoint - Sedgewick.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayning0/ProjectEuler-and-Algorithms/0a9fbbc1b5c8fd51ec2d33b8a7915c12128f5bd1/euler/Permutation Powerpoint - Sedgewick.pdf -------------------------------------------------------------------------------- /euler/bsearch.rb: -------------------------------------------------------------------------------- 1 | # Binary search. Input array must be sorted in ascending order. 2 | require 'benchmark' 3 | 4 | class BSearch 5 | def self.recursive(arr, val, low = 0, high = arr.length - 1) 6 | return nil if low > high 7 | mid = (low + high) / 2 8 | case 9 | when arr[mid] > val then recursive(arr, val, low, mid - 1) 10 | when arr[mid] < val then recursive(arr, val, mid + 1, high) 11 | else return mid 12 | end 13 | end 14 | 15 | def self.iterative(arr, val) 16 | low, high = 0, arr.length - 1 17 | while low <= high 18 | mid = (low + high) / 2 19 | case 20 | when arr[mid] > val then high = mid - 1 21 | when arr[mid] < val then low = mid + 1 22 | else return mid 23 | end 24 | end 25 | nil 26 | end 27 | 28 | def self.bsearch(arr, val) 29 | arr.bsearch { |v| val - v } # Ruby's own bsearch method 30 | end 31 | end 32 | 33 | # Benchmark.bm do |run| 34 | # run.report { BSearch.recursive([1,5,10,19,24], 24) } 35 | # run.report { BSearch.iterative([1,5,10,19,24], 24) } 36 | # run.report { BSearch.bsearch([1,5,10,19,24], 24) } 37 | # end 38 | 39 | # Output: 40 | # user system total real 41 | # 0.000000 0.000000 0.000000 ( 0.000012) 42 | # 0.000000 0.000000 0.000000 ( 0.000009) 43 | # 0.000000 0.000000 0.000000 ( 0.000020) 44 | 45 | # Iterative binary search is faster than both recursive 46 | # and Ruby's own bsearch method! 47 | -------------------------------------------------------------------------------- /euler/change_to_different_base.rb: -------------------------------------------------------------------------------- 1 | puts "Enter integer" 2 | num = gets.chomp.to_i 3 | puts "Convert to what base?" 4 | base = gets.chomp.to_i 5 | 6 | def dec2base(num, base) # converts base 10 to a different base (<= 10) 7 | # easiest solution: num.to_s(base) --- but this is cheating! 8 | answer = [] 9 | while num != 0 10 | answer << num % base # num mod base = remainder, which is rightmost digit 11 | num /= base # num / base = left part of num, without rightmost digit 12 | end 13 | answer.reverse!.join.to_i # Answer is backwards. Reverse, join into string, change back to integer. 14 | end 15 | 16 | puts "Answer = #{dec2base(num, base)}. A2 = #{num.to_s(base)}" -------------------------------------------------------------------------------- /euler/factors_caching.rb: -------------------------------------------------------------------------------- 1 | # By Raymond Gan 2 | 3 | require 'yaml' 4 | 5 | class FactorsCaching 6 | attr_reader :factors_file 7 | attr_accessor :integers, :factor_hash 8 | 9 | def initialize(integers = []) 10 | @integers = integers.sort.uniq 11 | @factors_file = Dir.pwd + '/factors.dat' 12 | @factor_hash = File.file?(factors_file) ? YAML.load_file(factors_file) : {} 13 | end 14 | 15 | # Big-O complexity = O(log n) 16 | def factors(num) 17 | (2..Math.sqrt(num)).inject([]) do |ans, f| 18 | if num % f == 0 19 | ans << f 20 | ans << num / f unless f == num / f 21 | end 22 | ans 23 | end.sort 24 | end 25 | 26 | # If num exists in factor_hash, hash lookup is just O(1) 27 | # Usually faster than factors method 28 | def factors_cache(num) 29 | (2..Math.sqrt(num)).inject([]) do |ans, f| 30 | # Union of 2 arrays has O(m + n) cost that 31 | # may make this slower than factors method 32 | return ans | factor_hash[num] if factor_hash[num] 33 | if num % f == 0 34 | ans << f 35 | ans << num / f unless f == num / f 36 | end 37 | ans 38 | end.sort 39 | end 40 | 41 | # No caching at all 42 | def get_factors 43 | return {} if integers.empty? 44 | integers.inject({}) do |ans, num| 45 | ans[num] = factors(num) 46 | ans 47 | end 48 | end 49 | 50 | # Check for cache in 2 places: 51 | # 1. Initially for num 52 | # 2. If needed, check again for a factor of num 53 | # This is fastest overall way 54 | def get_factors_cache 55 | return {} if integers.empty? 56 | integers.inject({}) do |ans, num| 57 | ans[num] = factor_hash[num] ? factor_hash[num] : factors_cache(num) 58 | ans 59 | end 60 | end 61 | 62 | def cache_in_file 63 | File.open(factors_file, 'w') { |file| YAML.dump(factor_hash, file) } 64 | end 65 | 66 | def compare_speed_factors 67 | cache_count, no_cache_count = 0, 0 68 | puts "Compare speed of factors vs. factors_cache methods:" 69 | 70 | 100.times do 71 | num = rand(1000) 72 | start = Time.now 73 | factors(num) 74 | time_no_cache = Time.now - start 75 | print "Time (no cache): #{time_no_cache}. " 76 | 77 | start = Time.now 78 | factors_cache(num) 79 | time_cache = Time.now - start 80 | puts "Time (cache): #{time_cache}." 81 | 82 | if time_cache < time_no_cache 83 | puts "cache wins" 84 | cache_count += 1 85 | else 86 | puts "no cache wins" 87 | no_cache_count += 1 88 | end 89 | puts 90 | end 91 | 92 | puts "#{cache_count} cache wins. #{no_cache_count} no cache wins." 93 | end 94 | 95 | def compare_speed_get_factors 96 | start = Time.now 97 | get_factors 98 | time_no_cache = Time.now - start 99 | print "Time (no cache): #{time_no_cache}. " 100 | 101 | start = Time.now 102 | new_factor_hash_cache = get_factors_cache 103 | time_cache = Time.now - start 104 | puts "Time (cache): #{time_cache}." 105 | 106 | if time_cache < time_no_cache 107 | puts "cache wins" 108 | else 109 | puts "no cache wins" 110 | end 111 | 112 | factor_hash.merge!(new_factor_hash_cache) 113 | cache_in_file 114 | return time_cache, time_no_cache 115 | end 116 | 117 | #------------------------------------------------------ 118 | 119 | # Faster than O(n * n), since inner loop size shrinks 120 | def multiples 121 | return {} if integers.empty? 122 | length = integers.size 123 | ans = {} 124 | if length > 1 125 | (0..length - 2).each do |i| 126 | num = integers[i] 127 | ans[num] = [] 128 | 129 | integers[i + 1..-1].each do |multiple| 130 | ans[num] << multiple if multiple % num == 0 131 | end 132 | 133 | end 134 | end 135 | ans[integers[-1]] = [] 136 | ans 137 | end 138 | 139 | # Using Sieve of Eratosthenes-like way. Takes O(n * n) 140 | # Slower than multiples method 141 | def multiples_sieve 142 | return {} if integers.empty? 143 | length = integers.size 144 | last = integers[-1] 145 | ans = {} 146 | if length > 1 147 | (0..length - 2).each do |i| 148 | num = integers[i] 149 | # Creating multiples of num take O(n) 150 | multiples_of_num = num.step(last, num).to_a.drop(1) 151 | # Intersecting 2 arrays takes O(m+n) 152 | ans[num] = integers & multiples_of_num 153 | end 154 | end 155 | ans[last] = [] 156 | ans 157 | end 158 | 159 | def compare_speed_multiples 160 | start = Time.now 161 | multiples 162 | time_no_sieve = Time.now - start 163 | print "Time (no sieve): #{time_no_sieve}. " 164 | 165 | start = Time.now 166 | multiples_sieve 167 | time_sieve = Time.now - start 168 | puts "Time (sieve): #{time_sieve}." 169 | 170 | if time_sieve < time_no_sieve 171 | puts "sieve wins" 172 | else 173 | puts "no sieve wins" 174 | end 175 | puts 176 | return time_sieve, time_no_sieve 177 | end 178 | end 179 | -------------------------------------------------------------------------------- /euler/factors_compare_speeds.rb: -------------------------------------------------------------------------------- 1 | require './factors_caching' 2 | 3 | # Results of sample run are in file "factors.output" 4 | 5 | # Compare speed of factors vs. factors_cache methods: 6 | FactorsCaching.new.compare_speed_factors 7 | 8 | # From "factors.output": "65 cache wins. 35 no cache wins." 9 | 10 | puts "\nCompare speed of get_factors vs. get_factors_cache methods:" 11 | @cache_count, @no_cache_count = 0, 0 12 | 13 | 100.times do 14 | integers = Array.new(5) { |num| rand(1000) } 15 | cache, no_cache = FactorsCaching.new(integers).compare_speed_get_factors 16 | if cache < no_cache 17 | @cache_count += 1 18 | else 19 | @no_cache_count += 1 20 | end 21 | end 22 | 23 | puts "#{@cache_count} cache wins. #{@no_cache_count} no cache wins." 24 | 25 | # From "factors.output": "98 cache wins. 2 no cache wins." 26 | 27 | puts "\nCompare speed of multiples vs. multiples_sieve methods:" 28 | @sieve_count, @no_sieve_count = 0, 0 29 | 30 | 100.times do 31 | integers = Array.new(8) { |num| rand(100) + 2 } 32 | sieve, no_sieve = FactorsCaching.new(integers).compare_speed_multiples 33 | if sieve < no_sieve 34 | @sieve_count += 1 35 | else 36 | @no_sieve_count += 1 37 | end 38 | end 39 | 40 | puts "#{@sieve_count} sieve wins. #{@no_sieve_count} no sieve wins." 41 | 42 | # From "factors.output": 2 sieve wins. 98 no sieve wins. 43 | -------------------------------------------------------------------------------- /euler/gcd_lcm.rb: -------------------------------------------------------------------------------- 1 | # Find Greatest Common Divisor (Euclid's Algorithm) 2 | # http://people.cis.ksu.edu/~schmidt/301s12/Exercises/euclid_alg.html 3 | # Based on Euclid's insight that GCD(x, y) = GCD(x-y, y) 4 | 5 | def gcd(k, m) 6 | k, m = m, k if k < m # swap k and m so k is greater number 7 | 8 | while m != 0 9 | r = k % m 10 | k = m 11 | m = r 12 | end 13 | 14 | k 15 | end 16 | 17 | # recursive way 18 | 19 | def gcd_recursive(k, m) 20 | return k if m == 0 21 | r = k % m 22 | gcd_recursive(m, r) 23 | end 24 | 25 | # Least Common Multiple: LCM(a, b) = a * b / GCD(a, b) 26 | def lcm(a, b) 27 | a / gcd(a, b) * b 28 | end 29 | 30 | puts "Give me 2 non-zero numbers. I'll tell you GCD and LCM:" 31 | print "Number 1: " 32 | a = gets.chomp.to_i 33 | print "Number 2: " 34 | b = gets.chomp.to_i 35 | puts "LCM: #{lcm(a, b)}." 36 | 37 | start = Time.now 38 | puts "GCD (iterative way): #{gcd(a, b)}. Took #{Time.now - start} secs." 39 | start = Time.now 40 | puts "GCD (recursive way): #{gcd_recursive(a, b)}. Took #{Time.now - start} secs." -------------------------------------------------------------------------------- /euler/is_prime.rb: -------------------------------------------------------------------------------- 1 | # Fast way to check if number is prime 2 | # Compared several methods and timed with Benchmark class. See results below. 3 | # Also see times for summing all primes < 2 million, in 010summation_of_primes.rb 4 | 5 | require 'benchmark' 6 | require 'json' 7 | 8 | def is_prime?(n) # optimized trial division 9 | return true if n.between?(2, 3) 10 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 11 | 12 | j, w = 5, 2 13 | 14 | while j * j <= n # Only must check j up to sqrt(n) 15 | return false if n % j == 0 16 | j += w # Only check odd #'s. j + 2 17 | w = 6 - w # Don't check multiples of 3. j + 4. 18 | end # Alternate +2, +4, +2, +4. Ex: 5+2 = 7+4 = 11+2 = 13+4 = 17 ... 19 | 20 | true 21 | end 22 | 23 | 24 | def is_prime_sieve?(s, n) # trial division, only dividing by primes from sieve file 25 | return true if n.between?(2, 3) 26 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 27 | 28 | s.each do |d| 29 | return true if d * d > n # only must check up till sqrt(n)! 30 | return false if n % d == 0 # if n is divisible by a number in prime file 31 | end 32 | 33 | true 34 | end 35 | 36 | puts "Enter number (< 2 million). I'll tell you if it's prime." 37 | n = gets.chomp.to_i 38 | 39 | 40 | Benchmark.bm do |bm| 41 | bm.report("With is_prime? method. Prime? ") do 42 | puts "#{is_prime?(n)}." 43 | end 44 | 45 | # load in previously created arrays of primes < 2 million 46 | bm.report("Time to load serialized sieve file of primes") do 47 | puts 48 | s = Marshal.load(File.read('sieveprimes-serialized.txt')) 49 | end 50 | 51 | bm.report("Time to load JSON sieve file of primes") do 52 | puts 53 | j = JSON.parse(File.read('jsonprimes.txt')) 54 | end 55 | end 56 | 57 | # Program does not remember s, j from the Benchmark block. Must load again. 58 | s = Marshal.load(File.read('sieveprimes-serialized.txt')) 59 | j = JSON.parse(File.read('jsonprimes.txt')) 60 | 61 | Benchmark.bm do |bm| 62 | bm.report("is_prime_sieve? method. Only dividing by primes from sieveprimes-serialized.txt. Prime? ") do 63 | puts "#{is_prime_sieve?(s, n)}" 64 | end 65 | 66 | bm.report("is_prime_sieve? method. Only dividing by primes from jsonprimes.txt. Prime? ") do 67 | puts "#{is_prime_sieve?(j, n)}" 68 | end 69 | 70 | bm.report("Search for prime in serialized file with include? method. ") do 71 | #t = Marshal.load File.read('sieveprimes-serialized.txt') 72 | 73 | # uses Sieve of Eratosthenes file to check if prime. Slower. 74 | puts "Prime? #{s.include?(n)}" 75 | end 76 | 77 | bm.report("Search for prime in JSON file with include? method. ") do # Slowest! 78 | #j = JSON.parse(File.read('jsonprimes.txt')) 79 | puts "Prime? #{j.include?(n)}" 80 | end 81 | end 82 | 83 | =begin -----Output of Benchmarks------ 84 | 85 | in_prime? method is still fastest, if you count time to load sieve prime files into memory. 86 | Serialized file loads faster and but processes slower than JSON. 87 | 88 | However, if you load file in ADVANCE, before asking for a number, trial division by sieve prime 89 | file is 100 times FASTER than in_prime? method! See times in parentheses below. 90 | 91 | 92 | Enter number (< 2 million). I'll tell you if it's prime. 93 | 1999993 (This is biggest prime < 2 million) 94 | 95 | user system total real 96 | With is_prime? method. Prime? true. 97 | 0.000000 0.000000 0.000000 ( 0.000889) 98 | 99 | Time to load serialized sieve file of primes 100 | 0.020000 0.000000 0.020000 ( 0.021665) 101 | Time to load JSON sieve file of primes 102 | 0.050000 0.000000 0.050000 ( 0.051689) 103 | 104 | is_prime_sieve? method. Only dividing by primes from sieveprimes-serialized.txt. Prime? true 105 | 0.000000 0.000000 0.000000 ( 0.000083) 106 | is_prime_sieve? method. Only dividing by primes from jsonprimes.txt. Prime? true 107 | 0.000000 0.000000 0.000000 ( 0.000073) 108 | 109 | Search for prime in serialized file, with include? method. Prime? true 110 | 0.010000 0.000000 0.010000 ( 0.011237) 111 | Search for prime in JSON file, with include? method. Prime? true 112 | 0.010000 0.000000 0.010000 ( 0.011146) 113 | 114 | =end 115 | -------------------------------------------------------------------------------- /euler/keylog.txt: -------------------------------------------------------------------------------- 1 | 319 2 | 680 3 | 180 4 | 690 5 | 129 6 | 620 7 | 762 8 | 689 9 | 762 10 | 318 11 | 368 12 | 710 13 | 720 14 | 710 15 | 629 16 | 168 17 | 160 18 | 689 19 | 716 20 | 731 21 | 736 22 | 729 23 | 316 24 | 729 25 | 729 26 | 710 27 | 769 28 | 290 29 | 719 30 | 680 31 | 318 32 | 389 33 | 162 34 | 289 35 | 162 36 | 718 37 | 729 38 | 319 39 | 790 40 | 680 41 | 890 42 | 362 43 | 319 44 | 760 45 | 316 46 | 729 47 | 380 48 | 319 49 | 728 50 | 716 51 | -------------------------------------------------------------------------------- /euler/perm2.rb: -------------------------------------------------------------------------------- 1 | # http:#permute.tchs.info/01example.php 2 | 3 | start = Time.now 4 | a = [*0..3] 5 | 6 | =begin 7 | def QuickPerm(a) 8 | perms = [] 9 | n = a.length 10 | p = [*0..n] # p = index control array.p[n] > 0 controls iteration and the index boundary for i 11 | i, j = 0, 0 # Upper Index i, Lower Index j 12 | b = a.dup 13 | perms << b 14 | i = 1 # setup first swap points to be 1 and 0 respectively (i & j) 15 | 16 | while i < n 17 | p[i] -= 1 # decrease index "weight" for i by one 18 | i.even? ? j = 0 : j = p[i] # If i is odd then j = p[i] otherwise j = 0 19 | a[i], a[j] = a[j], a[i] 20 | b = a.dup 21 | perms << b 22 | i = 1 # reset index i to 1 (assumed) 23 | 24 | while (p[i] == 0) 25 | p[i] = i # reset p[i] zero value 26 | i += 1 # set new index value for i (increase by one) 27 | end 28 | end 29 | 30 | perms 31 | end 32 | 33 | QuickPerm(a).map(&:join).sort 34 | puts "It took #{Time.now - start} secs." # It took 1 min for 10 digit number. 35 | =end 36 | 37 | 38 | # -------------------------------------- 39 | # http:#www.freewebs.com/permute/soda_submit.html (SEPA algorithm) 40 | 41 | def permute(str, len) 42 | 43 | key = len - 1 44 | newkey = len - 1 45 | 46 | # The key value is the first value from the end which is smaller 47 | # than the value to its immediate right 48 | 49 | while (key > 0) && (str[key] <= str[key-1]) 50 | key -= 1 51 | end 52 | 53 | key -= 1 54 | 55 | # If key < 0 the data is in reverse sorted order, which is the last permutation. 56 | 57 | return 0 if key < 0 58 | 59 | #str[key+1] is greater than str[key] because of how key 60 | # was found. If no other is greater, str[key+1] is used 61 | 62 | newkey = len - 1 63 | while (newkey > key) && (str[newkey] <= str[key]) 64 | newkey -= 1 65 | end 66 | 67 | str[key], str[newkey] = str[newkey], str[key] 68 | 69 | # variables len and key are used to walk through the tail, 70 | # exchanging pairs from both ends of the tail. len and 71 | # key are reused to save memory 72 | 73 | len -= 1 74 | key += 1 75 | 76 | # The tail must end in sorted order to produce the next permutation. 77 | 78 | while len > key 79 | str[key], str[newkey] = str[newkey], str[key] 80 | key += 1 81 | len -= 1 82 | end 83 | 84 | return 1 85 | end 86 | 87 | 88 | -------------------------------------------------------------------------------- /euler/permutation.rb: -------------------------------------------------------------------------------- 1 | # Taken from http://www.mathblog.dk/project-euler-24-millionth-lexicographic-permutation/ 2 | # http://stackoverflow.com/questions/18557813/how-to-remember-swapped-array-elements-in-another-array 3 | 4 | a = [*0..9] 5 | 6 | start = Time.now 7 | 8 | =begin Best recursive routine. Output is in numerical (lexicographic) order! 9 | def permute(a) 10 | perms = [] 11 | return [a] if a.size < 2 12 | 13 | a.each do |e| 14 | permute(a - [e]).each do |p| # a - |e| is subarray of all elements to right of 1st one 15 | perms << ([e] + p) # add 1st element back to left of array, for EACH permutation of subarray 16 | end 17 | end 18 | 19 | perms 20 | end 21 | 22 | p permute(a).map(&:join) # prints array of strings of each permutation 23 | puts "Took #{Time.now - start} secs." # 9 items takes 7.5 secs. Can't handle >= 10 items. 24 | 25 | =begin ------------------------------------------------ 26 | Output not in lexicographic order. Easy to remember. Can output by print, not by array. 27 | 28 | def permute(p, k, n) 29 | p p if k == n 30 | 31 | (k..n).each do |i| 32 | 33 | p[k], p[i] = p[i], p[k] 34 | 35 | permute(p, k+1, n) 36 | 37 | p[k], p[i] = p[i], p[k] 38 | end 39 | end 40 | 41 | permute(list, 0, 3) 42 | 43 | 44 | =end --------------------------------- 45 | 46 | # Best non-recursive routine. Took only 1.4 secs to solve Project Euler 24. 47 | 48 | count = 1 49 | numPerm = 1_000_000 # get 1 millionth permutation 50 | i, j = 0, 0 51 | n = a.length 52 | 53 | while (count < numPerm) 54 | i = n-1 55 | 56 | while (a[i - 1] >= a[i]) 57 | i -= 1 58 | end 59 | 60 | j = n 61 | 62 | while (a[j - 1] <= a[i - 1]) 63 | j -= 1 64 | end 65 | 66 | # start with a[i-1] = 8, a[j-1] = 9 67 | 68 | # swap values at position i-1 and j-1 69 | # puts "i-1 = #{i-1}, j-1 = #{j-1}" 70 | a[i - 1], a[j - 1] = a[j - 1], a[i - 1] 71 | 72 | # p a 73 | 74 | i += 1 75 | j = n 76 | 77 | while (i < j) 78 | # puts "i-1 = #{i-1}, j-1 = #{j-1} (while i minvalue} 20 | # Hash[*queue.shift] <- use if want normal (not priority) queue 21 | end 22 | end 23 | 24 | def pop 25 | if queue.empty? 26 | return nil 27 | else 28 | last_key = queue.keys.last 29 | last_value = queue[last_key] 30 | queue.delete(last_key) 31 | {last_key => last_value} 32 | end 33 | end 34 | 35 | def contains?(key) 36 | queue.has_key?(key) 37 | end 38 | 39 | def size 40 | queue.size 41 | end 42 | 43 | def empty? 44 | queue.empty? 45 | end 46 | end -------------------------------------------------------------------------------- /euler/rename.rb: -------------------------------------------------------------------------------- 1 | # renames files in directory from "021roman..." to "021_roman..." 2 | files = Dir['*rb'].select {|f| f =~ /^[0-9]{3}[a-zA-Z]+/} 3 | 4 | files.each do |f| 5 | f1 = f.clone 6 | f2 = f.insert(3, '_') 7 | system("mv #{f1} #{f2}") 8 | end 9 | -------------------------------------------------------------------------------- /euler/rspec_maker.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | By Raymond Gan, 4/30/14 3 | Automatically makes RSpec files! 4 | 5 | Reads in data text file in this format below (on separate lines) 6 | See '/data/rspec_maker_input.txt' for sample format: 7 | 8 | File name (assume 1 file per class. Your Ruby code in /lib.) 9 | Class name 10 | * (1 star) 11 | Method name 1 12 | All inputs (on separate lines) 13 | (blank line) 14 | All expected outputs (separate lines) 15 | * (1 star) 16 | Method name 2 17 | etc........ 18 | = (end of file) 19 | 20 | =end 21 | 22 | INPUT_FILE = 'rspec_maker_input.txt' 23 | OUTPUT_DIR = './spec/' 24 | 25 | class RSpecMaker 26 | attr_accessor :filename, :classname, :method, :data_in, :data_out, :line 27 | def initialize 28 | @line = read_file 29 | @filename = line[0] 30 | @classname = line[1] 31 | @method = [] 32 | @data_in = [] 33 | @data_out = [] 34 | end 35 | 36 | def read_file 37 | IO.readlines(INPUT_FILE).map(&:chomp) # reads file as array 38 | end 39 | 40 | def read_file_input 41 | i = 2 42 | m = -1 43 | inc, outc = 0, 0 44 | loop do 45 | # Start new method. Read data in. 46 | if line[i][0] == '*' 47 | m += 1 48 | @method[m] = line[i + 1] 49 | i += 2 50 | 51 | begin 52 | @data_in[inc] = line[i] 53 | i += 1 54 | inc += 1 55 | end until line[i] == '' 56 | 57 | @data_in << '*' # mark end of data in 58 | inc += 1 59 | end 60 | 61 | # Read data out 62 | if line[i] == '' 63 | i += 1 64 | 65 | begin 66 | @data_out[outc] = line[i] 67 | i += 1 68 | outc += 1 69 | end until ['*', '='].include?(line[i]) 70 | 71 | @data_out << '*' # mark end of data out 72 | outc += 1 73 | i -= 1 74 | end 75 | break if line[i] == '=' 76 | i += 1 77 | end 78 | end 79 | 80 | def write_spec_file 81 | inc = 0 82 | output_file = "#{OUTPUT_DIR}#{filename}_spec.rb" 83 | File.open(output_file, 'w') # overwrites old file 84 | File.open(output_file, 'a') do |f| 85 | f.puts "require 'spec_helper'" 86 | f.puts "require '#{filename}'" 87 | f.puts 88 | f.puts "describe #{classname} do" 89 | f.puts " let(:#{classname[0].downcase}) { #{classname}.new }" 90 | 91 | method.size.times do |m| 92 | f.puts 93 | f.puts " describe '##{method[m]}' do" 94 | f.puts " it '' do" 95 | 96 | loop do 97 | if data_in[inc] == '*' # hit end of data for this method 98 | inc += 1 99 | break 100 | end 101 | f.puts " expect(#{classname[0].downcase}.#{method[m]}(#{data_in[inc]})).to eq(#{data_out[inc]})" 102 | inc += 1 103 | end 104 | f.puts " end" 105 | f.puts " end" 106 | end 107 | f.puts "end" 108 | end 109 | end 110 | 111 | def run 112 | read_file_input 113 | write_spec_file 114 | end 115 | end 116 | 117 | RSpecMaker.new.run -------------------------------------------------------------------------------- /euler/rspec_maker_input.txt: -------------------------------------------------------------------------------- 1 | roman_numbers 2 | RomanNumbers 3 | * 4 | to_roman 5 | 29 6 | 1999 7 | 38 8 | 9 | 'XXIX' 10 | 'MCMXCIX' 11 | 'XXXVIII' 12 | * 13 | from_roman 14 | 'XXIX' 15 | 'MCMXCIX' 16 | 'XXXVIII' 17 | 18 | 29 19 | 1999 20 | 38 21 | = -------------------------------------------------------------------------------- /euler/sieve_of_eratosthenes.rb: -------------------------------------------------------------------------------- 1 | # Sieve of Eratosthenes: quickly makes big array of primes. May hog all your memory. 2 | # http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes 3 | # http://www.mathblog.dk/sum-of-all-primes-below-2000000-problem-10/ 4 | 5 | # Raymond Gan: Coded my own version, from scratch! 6 | require 'benchmark' 7 | require 'json' 8 | 9 | def sieve(n) # makes array of all primes from 2 to n 10 | s = 3.step(n, 2).to_a # make array of odd integers from 3 to n. Skip evens. 11 | s.each do |p| 12 | next if p.nil? # go to next element if p has been marked empty 13 | break if p * p > n # p only needs to go up to sqrt(n) 14 | 15 | k, pval = 0, 0 16 | 17 | while pval < n 18 | pval = p * (p + k) # jump forward 2p at a time: p*p, p*p + 2p, p*p + 4p, etc. 19 | 20 | # Set all those multiples to nil. i = (pval - 3)/2 translates pvals to index i 21 | 22 | s[(pval - 3) / 2] = nil 23 | k += 2 24 | end 25 | 26 | end 27 | s.compact! # removes all nil elements from array 28 | s.unshift(2) # adds 2 as 1st element 29 | end 30 | 31 | def is_prime?(n) 32 | return true if n.between?(2, 3) 33 | return false if (n.even? || n % 3 == 0 || n <= 1) # no evens, multiples of 3, or nums <= 1 34 | 35 | j, w = 5, 2 36 | 37 | while j * j <= n # Only must check j up to sqrt(n) 38 | return false if n % j == 0 39 | j += w # Only check odd #'s. j + 2 40 | w = 6 - w # Don't check multiples of 3. j + 4. 41 | end # Alternate +2, +4, +2, +4. Ex: 5+2 = 7+4 = 11+2 = 13+4 = 17 ... 42 | 43 | true 44 | end 45 | 46 | def benchmark_primes 47 | puts "Enter n. I'll make an array of primes up till n:" 48 | n = gets.chomp.to_i 49 | puts 50 | s = sieve(n) 51 | puts "Array size = #{s.size}" 52 | puts "Putting it in text file...(WARNING: this may create a HUGE file that uses up all your memory! May need to reboot.)" 53 | File.open('sieveprimes-serialized.txt', 'w') {|f| f.write(Marshal.dump(s))} 54 | File.open('jsonprimes.txt', 'w') {|f| f.write(s.to_json)} 55 | 56 | puts "Enter n. I'll tell you if it's prime" 57 | n = gets.chomp.to_i 58 | 59 | Benchmark.bm(27) do |bm| 60 | bm.report("Using is_prime method. ") do # Fastest! 61 | puts "Prime? #{is_prime?(n)}" 62 | end 63 | 64 | bm.report("Read in primes from serialized file. ") do 65 | t = Marshal.load File.read('sieveprimes-serialized.txt') 66 | 67 | # uses Sieve of Eratosthenes file to check if prime. Slower. 68 | prime = t.any? {|i| i == n} 69 | puts "Prime? #{prime}" 70 | end 71 | 72 | bm.report("Read in primes from JSON file. ") do # Slowest! 73 | j = JSON.parse(File.read('jsonprimes.txt')) 74 | prime = j.any? {|i| i == n} 75 | puts "Prime? #{prime}" 76 | end 77 | end 78 | end 79 | 80 | =begin 81 | # sum all primes 82 | # puts "Sum of all primes: #{s.inject(:+)}" 83 | 84 | =begin 85 | 86 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 = i 87 | 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43 = 2i + 3 88 | 89 | p*p, p*p + 2p, p*p + 4p, p*p + 6p, etc... = p(p + k), k = even numbers 90 | 91 | i=p, 2p, , 3p , 4p 92 | 93 | (25-3)/2, (35-3)/2 94 | =end -------------------------------------------------------------------------------- /euler/sieveprimes-serialized.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayning0/ProjectEuler-and-Algorithms/0a9fbbc1b5c8fd51ec2d33b8a7915c12128f5bd1/euler/sieveprimes-serialized.txt -------------------------------------------------------------------------------- /euler/spec/factors.dat: -------------------------------------------------------------------------------- 1 | --- 2 | 44: 3 | - 2 4 | - 4 5 | - 11 6 | - 22 7 | 290: 8 | - 2 9 | - 5 10 | - 10 11 | - 29 12 | - 58 13 | - 145 14 | 719: [] 15 | 764: 16 | - 2 17 | - 4 18 | - 191 19 | - 382 20 | 790: 21 | - 2 22 | - 5 23 | - 10 24 | - 79 25 | - 158 26 | - 395 27 | 11: [] 28 | 262: 29 | - 2 30 | - 131 31 | 524: 32 | - 2 33 | - 4 34 | - 131 35 | - 262 36 | 536: 37 | - 2 38 | - 4 39 | - 8 40 | - 67 41 | - 134 42 | - 268 43 | 540: 44 | - 2 45 | - 3 46 | - 4 47 | - 5 48 | - 6 49 | - 9 50 | - 10 51 | - 12 52 | - 15 53 | - 18 54 | - 20 55 | - 27 56 | - 30 57 | - 36 58 | - 45 59 | - 54 60 | - 60 61 | - 90 62 | - 108 63 | - 135 64 | - 180 65 | - 270 66 | 189: 67 | - 3 68 | - 7 69 | - 9 70 | - 21 71 | - 27 72 | - 63 73 | 332: 74 | - 2 75 | - 4 76 | - 83 77 | - 166 78 | 626: 79 | - 2 80 | - 313 81 | 700: 82 | - 2 83 | - 4 84 | - 5 85 | - 7 86 | - 10 87 | - 14 88 | - 20 89 | - 25 90 | - 28 91 | - 35 92 | - 50 93 | - 70 94 | - 100 95 | - 140 96 | - 175 97 | - 350 98 | 99: 99 | - 3 100 | - 9 101 | - 11 102 | - 33 103 | 220: 104 | - 2 105 | - 4 106 | - 5 107 | - 10 108 | - 11 109 | - 20 110 | - 22 111 | - 44 112 | - 55 113 | - 110 114 | 711: 115 | - 3 116 | - 9 117 | - 79 118 | - 237 119 | 871: 120 | - 13 121 | - 67 122 | 926: 123 | - 2 124 | - 463 125 | 335: 126 | - 5 127 | - 67 128 | 339: 129 | - 3 130 | - 113 131 | 363: 132 | - 3 133 | - 11 134 | - 33 135 | - 121 136 | 458: 137 | - 2 138 | - 229 139 | 916: 140 | - 2 141 | - 4 142 | - 229 143 | - 458 144 | 106: 145 | - 2 146 | - 53 147 | 235: 148 | - 5 149 | - 47 150 | 367: [] 151 | 710: 152 | - 2 153 | - 5 154 | - 10 155 | - 71 156 | - 142 157 | - 355 158 | 983: [] 159 | 410: 160 | - 2 161 | - 5 162 | - 10 163 | - 41 164 | - 82 165 | - 205 166 | 484: 167 | - 2 168 | - 4 169 | - 11 170 | - 22 171 | - 44 172 | - 121 173 | - 242 174 | 886: 175 | - 2 176 | - 443 177 | 889: 178 | - 7 179 | - 127 180 | 927: 181 | - 3 182 | - 9 183 | - 103 184 | - 309 185 | 29: [] 186 | 377: 187 | - 13 188 | - 29 189 | 566: 190 | - 2 191 | - 283 192 | 590: 193 | - 2 194 | - 5 195 | - 10 196 | - 59 197 | - 118 198 | - 295 199 | 752: 200 | - 2 201 | - 4 202 | - 8 203 | - 16 204 | - 47 205 | - 94 206 | - 188 207 | - 376 208 | 71: [] 209 | 478: 210 | - 2 211 | - 239 212 | 603: 213 | - 3 214 | - 9 215 | - 67 216 | - 201 217 | 788: 218 | - 2 219 | - 4 220 | - 197 221 | - 394 222 | 918: 223 | - 2 224 | - 3 225 | - 6 226 | - 9 227 | - 17 228 | - 18 229 | - 27 230 | - 34 231 | - 51 232 | - 54 233 | - 102 234 | - 153 235 | - 306 236 | - 459 237 | 34: 238 | - 2 239 | - 17 240 | 432: 241 | - 2 242 | - 3 243 | - 4 244 | - 6 245 | - 8 246 | - 9 247 | - 12 248 | - 16 249 | - 18 250 | - 24 251 | - 27 252 | - 36 253 | - 48 254 | - 54 255 | - 72 256 | - 108 257 | - 144 258 | - 216 259 | 482: 260 | - 2 261 | - 241 262 | 767: 263 | - 13 264 | - 59 265 | 832: 266 | - 2 267 | - 4 268 | - 8 269 | - 13 270 | - 16 271 | - 26 272 | - 32 273 | - 52 274 | - 64 275 | - 104 276 | - 208 277 | - 416 278 | 247: 279 | - 13 280 | - 19 281 | 289: 282 | - 17 283 | 400: 284 | - 2 285 | - 4 286 | - 5 287 | - 8 288 | - 10 289 | - 16 290 | - 20 291 | - 25 292 | - 40 293 | - 50 294 | - 80 295 | - 100 296 | - 200 297 | 601: [] 298 | 30: 299 | - 2 300 | - 3 301 | - 5 302 | - 6 303 | - 10 304 | - 15 305 | 121: 306 | - 11 307 | 124: 308 | - 2 309 | - 4 310 | - 31 311 | - 62 312 | 427: 313 | - 7 314 | - 61 315 | 200: 316 | - 2 317 | - 4 318 | - 5 319 | - 8 320 | - 10 321 | - 20 322 | - 25 323 | - 40 324 | - 50 325 | - 100 326 | 272: 327 | - 2 328 | - 4 329 | - 8 330 | - 16 331 | - 17 332 | - 34 333 | - 68 334 | - 136 335 | 401: [] 336 | 413: 337 | - 7 338 | - 59 339 | 675: 340 | - 3 341 | - 5 342 | - 9 343 | - 15 344 | - 25 345 | - 27 346 | - 45 347 | - 75 348 | - 135 349 | - 225 350 | 345: 351 | - 3 352 | - 5 353 | - 15 354 | - 23 355 | - 69 356 | - 115 357 | 485: 358 | - 5 359 | - 97 360 | 750: 361 | - 2 362 | - 3 363 | - 5 364 | - 6 365 | - 10 366 | - 15 367 | - 25 368 | - 30 369 | - 50 370 | - 75 371 | - 125 372 | - 150 373 | - 250 374 | - 375 375 | 969: 376 | - 3 377 | - 17 378 | - 19 379 | - 51 380 | - 57 381 | - 323 382 | 3: [] 383 | 279: 384 | - 3 385 | - 9 386 | - 31 387 | - 93 388 | 465: 389 | - 3 390 | - 5 391 | - 15 392 | - 31 393 | - 93 394 | - 155 395 | 703: 396 | - 19 397 | - 37 398 | 899: 399 | - 29 400 | - 31 401 | 267: 402 | - 3 403 | - 89 404 | 638: 405 | - 2 406 | - 11 407 | - 22 408 | - 29 409 | - 58 410 | - 319 411 | 903: 412 | - 3 413 | - 7 414 | - 21 415 | - 43 416 | - 129 417 | - 301 418 | 102: 419 | - 2 420 | - 3 421 | - 6 422 | - 17 423 | - 34 424 | - 51 425 | 562: 426 | - 2 427 | - 281 428 | 793: 429 | - 13 430 | - 61 431 | 838: 432 | - 2 433 | - 419 434 | 848: 435 | - 2 436 | - 4 437 | - 8 438 | - 16 439 | - 53 440 | - 106 441 | - 212 442 | - 424 443 | 174: 444 | - 2 445 | - 3 446 | - 6 447 | - 29 448 | - 58 449 | - 87 450 | 210: 451 | - 2 452 | - 3 453 | - 5 454 | - 6 455 | - 7 456 | - 10 457 | - 14 458 | - 15 459 | - 21 460 | - 30 461 | - 35 462 | - 42 463 | - 70 464 | - 105 465 | 361: 466 | - 19 467 | 365: 468 | - 5 469 | - 73 470 | 973: 471 | - 7 472 | - 139 473 | 91: 474 | - 7 475 | - 13 476 | 274: 477 | - 2 478 | - 137 479 | 383: [] 480 | 704: 481 | - 2 482 | - 4 483 | - 8 484 | - 11 485 | - 16 486 | - 22 487 | - 32 488 | - 44 489 | - 64 490 | - 88 491 | - 176 492 | - 352 493 | 168: 494 | - 2 495 | - 3 496 | - 4 497 | - 6 498 | - 7 499 | - 8 500 | - 12 501 | - 14 502 | - 21 503 | - 24 504 | - 28 505 | - 42 506 | - 56 507 | - 84 508 | 194: 509 | - 2 510 | - 97 511 | 586: 512 | - 2 513 | - 293 514 | 749: 515 | - 7 516 | - 107 517 | -------------------------------------------------------------------------------- /euler/spec/factors_caching_spec.rb: -------------------------------------------------------------------------------- 1 | require './spec_helper' 2 | require '../factors_caching' 3 | 4 | describe FactorsCaching do 5 | describe '#factors_cache' do 6 | it 'takes 0' do 7 | expect(subject.factors_cache(0)).to eq([]) 8 | end 9 | it 'takes prime number' do 10 | expect(subject.factors_cache(47)).to eq([]) 11 | end 12 | it 'takes number w/ multiple factors' do 13 | expect(subject.factors_cache(1000)).to eq([2, 4, 5, 8, 10, 20, 14 | 25, 40, 50, 100, 125, 200, 250, 500]) 15 | end 16 | end 17 | 18 | describe '#get_factors_cache' do 19 | it 'takes empty array' do 20 | expect(subject.get_factors_cache).to eq({}) 21 | end 22 | 23 | it 'takes prime number' do 24 | subject.integers = [13] 25 | expect(subject.get_factors_cache).to eq(13 => []) 26 | end 27 | 28 | it 'takes number w/ multiple factors' do 29 | subject.integers = [20] 30 | expect(subject.get_factors_cache).to eq(20 => [2, 4, 5, 10]) 31 | end 32 | 33 | it 'shows factors for multiple integers' do 34 | subject.integers = [10, 5, 2, 20] 35 | expect(subject.get_factors_cache).to eq(10 => [2, 5], 36 | 5 => [], 2 => [], 20 => [2, 4, 5, 10]) 37 | end 38 | end 39 | 40 | describe '#multiples' do 41 | it 'takes empty array' do 42 | expect(subject.multiples).to eq({}) 43 | end 44 | 45 | it 'takes single integer' do 46 | subject.integers = [20] 47 | expect(subject.multiples).to eq(20 => []) 48 | end 49 | 50 | it 'shows multiples for multiple integers' do 51 | subject.integers = [2, 5, 10, 20, 25, 50, 100, 125] 52 | expect(subject.multiples).to eq(2 => [10, 20, 50, 100], 53 | 5 => [10, 20, 25, 50, 100, 125], 10 => [20, 50, 100], 20 => [100], 54 | 25 => [50, 100, 125], 50 => [100], 100 => [], 125 => []) 55 | end 56 | end 57 | end -------------------------------------------------------------------------------- /euler/spec/roman_numbers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'roman_numbers' 3 | 4 | describe RomanNumbers do 5 | let(:r) { RomanNumbers.new } 6 | 7 | describe '#to_roman' do 8 | it '' do 9 | expect(r.to_roman(29)).to eq('XXIX') 10 | expect(r.to_roman(1999)).to eq('MCMXCIX') 11 | expect(r.to_roman(38)).to eq('XXXVIII') 12 | end 13 | end 14 | 15 | describe '#from_roman' do 16 | it '' do 17 | expect(r.from_roman('XXIX')).to eq(29) 18 | expect(r.from_roman('MCMXCIX')).to eq(1999) 19 | expect(r.from_roman('XXXVIII')).to eq(38) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /euler/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.run_all_when_everything_filtered = true 3 | config.color = true 4 | config.order = 'default' 5 | config.formatter = 'documentation' 6 | end -------------------------------------------------------------------------------- /euler/tower_of_hanoi.rb: -------------------------------------------------------------------------------- 1 | # Raymond Gan-------Tower of Hanoi problem------------- 2 | # http://en.wikipedia.org/wiki/Tower_of_Hanoi 3 | # http://www.skorks.com/2010/03/solving-the-towers-of-hanoi-mathematically-and-programmatically-the-value-of-recursion/ 4 | 5 | # Recursive solution: 6 | # http://www.sparknotes.com/cs/recursion/examples/section6.rhtml 7 | 8 | # Iterative solution: 9 | # http://apcentral.collegeboard.com/apc/members/courses/teachers_corner/45418.html 10 | # http://www.ecse.rpi.edu/~wrf/p/28-sigplan84-hanoi.pdf 11 | 12 | # No disk may be moved twice in a row. May not move bigger disk on top of smaller one. 13 | # Must move smallest possible disk (as long as not last one moved). 14 | # A = source, B = temp, C = destination 15 | 16 | class Array # prints multidimensional array in pretty format 17 | def to_table 18 | l = [] 19 | self.each{|r| r.each_with_index{|f,i| l[i] = [l[i]||0, f.to_s.length].max}} 20 | self.each{|r| r.each_with_index{|f,i| print "#{f.to_s.ljust l[i]} "}; puts} 21 | end 22 | end 23 | 24 | # Recursive solution 25 | def toh(n, source = 'A', temp = 'B', dest = 'C') 26 | toh(n - 1, source, dest, temp) if n > 1 # move all n - 1 disks to temp pole 27 | puts "Move top disc #{n}: #{source} => #{dest}." # move bottom disk to destination pole 28 | 29 | toh(n - 1, temp, source, dest) if n > 1 # move all n - 1 disks to destination pole 30 | end 31 | 32 | # Iterative solution: 33 | # If n = odd: Move all odd disks to left. All even disks to right. 34 | # If n = even: Move all odd disks to right. All even disks to left. 35 | 36 | def toh_iterative(n) 37 | t = [['A', 'B', 'C']] 38 | col = 0 # starting column 39 | top = 1 # starting top disk 40 | n.downto(1) do |i| 41 | t.unshift([i, '', '']) # build starting tower array 42 | end 43 | t.to_table # print it 44 | moves = 2 ** n - 1 45 | 46 | moves.times do 47 | !n.even? ? left = 1 : left = -1 # if n odd, move odd disks left, else move right. 48 | startcol = col 49 | startrow = t.transpose[col].index(top) 50 | 51 | if !top.even? 52 | # move left if disk odd and n odd. move right if disk odd and n even. 53 | col -= left 54 | else 55 | # move right if disk even and n odd. move left if disk even and n odd. 56 | col += left 57 | end 58 | 59 | col = 2 if col < 0 # If moves off edge, swing it back around. 60 | col = 0 if col > 2 61 | 62 | endtop = t.transpose[col].detect {|d| d != ''} # top disk in new column 63 | row = t.transpose[col].index(endtop) - 1 # 1st empty row in new column 64 | 65 | t[startrow][startcol] = '' # erase entry in disk's previous location 66 | t[row][col] = top # write disk in new location 67 | t.to_table 68 | 69 | top_maybe = [] 70 | 71 | (0..2).each do |c| # What disk do we move next? 72 | top_maybe[c] = t.transpose[c].detect {|d| d != ''} # top disk in column c 73 | 74 | # May not move current disk in next move. Make it high so it's not picked. 75 | top_maybe[c] = 100 if c == col 76 | top_maybe[c] = 100 if top_maybe[c].is_a?(String) # Column is empty 77 | end 78 | 79 | top = top_maybe.min # Next disk is smallest possible one. 80 | col = top_maybe.index(top) 81 | end 82 | end 83 | 84 | puts "Enter number of disks for Tower of Hanoi:" 85 | n = gets.chomp.to_i 86 | 87 | start = Time.now 88 | toh(n) 89 | puts "Recursive way took #{Time.now - start} secs." 90 | 91 | start = Time.now 92 | toh_iterative(n) 93 | puts "Iterative way took #{Time.now - start} secs." 94 | 95 | =begin Output: 96 | 97 | Enter number of disks for Tower of Hanoi: 98 | 3 99 | Move top disc 1: A => C. 100 | Move top disc 2: A => B. 101 | Move top disc 1: C => B. 102 | Move top disc 3: A => C. 103 | Move top disc 1: B => A. 104 | Move top disc 2: B => C. 105 | Move top disc 1: A => C. 106 | Recursive way took 0.000271 secs. 107 | 1 108 | 2 109 | 3 110 | A B C 111 | 112 | 2 113 | 3 1 114 | A B C 115 | 116 | 117 | 3 2 1 118 | A B C 119 | 120 | 1 121 | 3 2 122 | A B C 123 | 124 | 1 125 | 2 3 126 | A B C 127 | 128 | 129 | 1 2 3 130 | A B C 131 | 132 | 2 133 | 1 3 134 | A B C 135 | 1 136 | 2 137 | 3 138 | A B C 139 | Iterative way took 0.003831 secs. 140 | 141 | =end -------------------------------------------------------------------------------- /euler/triangle.txt: -------------------------------------------------------------------------------- 1 | 75 2 | 95 64 3 | 17 47 82 4 | 18 35 87 10 5 | 20 04 82 47 65 6 | 19 01 23 75 03 34 7 | 88 02 77 73 07 63 67 8 | 99 65 04 28 06 16 70 92 9 | 41 41 26 56 83 40 80 70 33 10 | 41 48 72 33 47 32 37 16 94 29 11 | 53 71 44 65 25 43 91 52 97 51 14 12 | 70 11 33 28 77 73 17 78 39 68 17 57 13 | 91 71 52 38 17 14 91 43 58 50 27 29 48 14 | 63 66 04 68 89 53 67 30 73 16 69 87 40 31 15 | 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 -------------------------------------------------------------------------------- /euler/triangle2.txt: -------------------------------------------------------------------------------- 1 | 59 2 | 73 41 3 | 52 40 09 4 | 26 53 06 34 5 | 10 51 87 86 81 6 | 61 95 66 57 25 68 7 | 90 81 80 38 92 67 73 8 | 30 28 51 76 81 18 75 44 9 | 84 14 95 87 62 81 17 78 58 10 | 21 46 71 58 02 79 62 39 31 09 11 | 56 34 35 53 78 31 81 18 90 93 15 12 | 78 53 04 21 84 93 32 13 97 11 37 51 13 | 45 03 81 79 05 18 78 86 13 30 63 99 95 14 | 39 87 96 28 03 38 42 17 82 87 58 07 22 57 15 | 06 17 51 17 07 93 09 07 75 97 95 78 87 08 53 16 | 67 66 59 60 88 99 94 65 55 77 55 34 27 53 78 28 17 | 76 40 41 04 87 16 09 42 75 69 23 97 30 60 10 79 87 18 | 12 10 44 26 21 36 32 84 98 60 13 12 36 16 63 31 91 35 19 | 70 39 06 05 55 27 38 48 28 22 34 35 62 62 15 14 94 89 86 20 | 66 56 68 84 96 21 34 34 34 81 62 40 65 54 62 05 98 03 02 60 21 | 38 89 46 37 99 54 34 53 36 14 70 26 02 90 45 13 31 61 83 73 47 22 | 36 10 63 96 60 49 41 05 37 42 14 58 84 93 96 17 09 43 05 43 06 59 23 | 66 57 87 57 61 28 37 51 84 73 79 15 39 95 88 87 43 39 11 86 77 74 18 24 | 54 42 05 79 30 49 99 73 46 37 50 02 45 09 54 52 27 95 27 65 19 45 26 45 25 | 71 39 17 78 76 29 52 90 18 99 78 19 35 62 71 19 23 65 93 85 49 33 75 09 02 26 | 33 24 47 61 60 55 32 88 57 55 91 54 46 57 07 77 98 52 80 99 24 25 46 78 79 05 27 | 92 09 13 55 10 67 26 78 76 82 63 49 51 31 24 68 05 57 07 54 69 21 67 43 17 63 12 28 | 24 59 06 08 98 74 66 26 61 60 13 03 09 09 24 30 71 08 88 70 72 70 29 90 11 82 41 34 29 | 66 82 67 04 36 60 92 77 91 85 62 49 59 61 30 90 29 94 26 41 89 04 53 22 83 41 09 74 90 30 | 48 28 26 37 28 52 77 26 51 32 18 98 79 36 62 13 17 08 19 54 89 29 73 68 42 14 08 16 70 37 31 | 37 60 69 70 72 71 09 59 13 60 38 13 57 36 09 30 43 89 30 39 15 02 44 73 05 73 26 63 56 86 12 32 | 55 55 85 50 62 99 84 77 28 85 03 21 27 22 19 26 82 69 54 04 13 07 85 14 01 15 70 59 89 95 10 19 33 | 04 09 31 92 91 38 92 86 98 75 21 05 64 42 62 84 36 20 73 42 21 23 22 51 51 79 25 45 85 53 03 43 22 34 | 75 63 02 49 14 12 89 14 60 78 92 16 44 82 38 30 72 11 46 52 90 27 08 65 78 03 85 41 57 79 39 52 33 48 35 | 78 27 56 56 39 13 19 43 86 72 58 95 39 07 04 34 21 98 39 15 39 84 89 69 84 46 37 57 59 35 59 50 26 15 93 36 | 42 89 36 27 78 91 24 11 17 41 05 94 07 69 51 96 03 96 47 90 90 45 91 20 50 56 10 32 36 49 04 53 85 92 25 65 37 | 52 09 61 30 61 97 66 21 96 92 98 90 06 34 96 60 32 69 68 33 75 84 18 31 71 50 84 63 03 03 19 11 28 42 75 45 45 38 | 61 31 61 68 96 34 49 39 05 71 76 59 62 67 06 47 96 99 34 21 32 47 52 07 71 60 42 72 94 56 82 83 84 40 94 87 82 46 39 | 01 20 60 14 17 38 26 78 66 81 45 95 18 51 98 81 48 16 53 88 37 52 69 95 72 93 22 34 98 20 54 27 73 61 56 63 60 34 63 40 | 93 42 94 83 47 61 27 51 79 79 45 01 44 73 31 70 83 42 88 25 53 51 30 15 65 94 80 44 61 84 12 77 02 62 02 65 94 42 14 94 41 | 32 73 09 67 68 29 74 98 10 19 85 48 38 31 85 67 53 93 93 77 47 67 39 72 94 53 18 43 77 40 78 32 29 59 24 06 02 83 50 60 66 42 | 32 01 44 30 16 51 15 81 98 15 10 62 86 79 50 62 45 60 70 38 31 85 65 61 64 06 69 84 14 22 56 43 09 48 66 69 83 91 60 40 36 61 43 | 92 48 22 99 15 95 64 43 01 16 94 02 99 19 17 69 11 58 97 56 89 31 77 45 67 96 12 73 08 20 36 47 81 44 50 64 68 85 40 81 85 52 09 44 | 91 35 92 45 32 84 62 15 19 64 21 66 06 01 52 80 62 59 12 25 88 28 91 50 40 16 22 99 92 79 87 51 21 77 74 77 07 42 38 42 74 83 02 05 45 | 46 19 77 66 24 18 05 32 02 84 31 99 92 58 96 72 91 36 62 99 55 29 53 42 12 37 26 58 89 50 66 19 82 75 12 48 24 87 91 85 02 07 03 76 86 46 | 99 98 84 93 07 17 33 61 92 20 66 60 24 66 40 30 67 05 37 29 24 96 03 27 70 62 13 04 45 47 59 88 43 20 66 15 46 92 30 04 71 66 78 70 53 99 47 | 67 60 38 06 88 04 17 72 10 99 71 07 42 25 54 05 26 64 91 50 45 71 06 30 67 48 69 82 08 56 80 67 18 46 66 63 01 20 08 80 47 07 91 16 03 79 87 48 | 18 54 78 49 80 48 77 40 68 23 60 88 58 80 33 57 11 69 55 53 64 02 94 49 60 92 16 35 81 21 82 96 25 24 96 18 02 05 49 03 50 77 06 32 84 27 18 38 49 | 68 01 50 04 03 21 42 94 53 24 89 05 92 26 52 36 68 11 85 01 04 42 02 45 15 06 50 04 53 73 25 74 81 88 98 21 67 84 79 97 99 20 95 04 40 46 02 58 87 50 | 94 10 02 78 88 52 21 03 88 60 06 53 49 71 20 91 12 65 07 49 21 22 11 41 58 99 36 16 09 48 17 24 52 36 23 15 72 16 84 56 02 99 43 76 81 71 29 39 49 17 51 | 64 39 59 84 86 16 17 66 03 09 43 06 64 18 63 29 68 06 23 07 87 14 26 35 17 12 98 41 53 64 78 18 98 27 28 84 80 67 75 62 10 11 76 90 54 10 05 54 41 39 66 52 | 43 83 18 37 32 31 52 29 95 47 08 76 35 11 04 53 35 43 34 10 52 57 12 36 20 39 40 55 78 44 07 31 38 26 08 15 56 88 86 01 52 62 10 24 32 05 60 65 53 28 57 99 53 | 03 50 03 52 07 73 49 92 66 80 01 46 08 67 25 36 73 93 07 42 25 53 13 96 76 83 87 90 54 89 78 22 78 91 73 51 69 09 79 94 83 53 09 40 69 62 10 79 49 47 03 81 30 54 | 71 54 73 33 51 76 59 54 79 37 56 45 84 17 62 21 98 69 41 95 65 24 39 37 62 03 24 48 54 64 46 82 71 78 33 67 09 16 96 68 52 74 79 68 32 21 13 78 96 60 09 69 20 36 55 | 73 26 21 44 46 38 17 83 65 98 07 23 52 46 61 97 33 13 60 31 70 15 36 77 31 58 56 93 75 68 21 36 69 53 90 75 25 82 39 50 65 94 29 30 11 33 11 13 96 02 56 47 07 49 02 56 | 76 46 73 30 10 20 60 70 14 56 34 26 37 39 48 24 55 76 84 91 39 86 95 61 50 14 53 93 64 67 37 31 10 84 42 70 48 20 10 72 60 61 84 79 69 65 99 73 89 25 85 48 92 56 97 16 57 | 03 14 80 27 22 30 44 27 67 75 79 32 51 54 81 29 65 14 19 04 13 82 04 91 43 40 12 52 29 99 07 76 60 25 01 07 61 71 37 92 40 47 99 66 57 01 43 44 22 40 53 53 09 69 26 81 07 58 | 49 80 56 90 93 87 47 13 75 28 87 23 72 79 32 18 27 20 28 10 37 59 21 18 70 04 79 96 03 31 45 71 81 06 14 18 17 05 31 50 92 79 23 47 09 39 47 91 43 54 69 47 42 95 62 46 32 85 59 | 37 18 62 85 87 28 64 05 77 51 47 26 30 65 05 70 65 75 59 80 42 52 25 20 44 10 92 17 71 95 52 14 77 13 24 55 11 65 26 91 01 30 63 15 49 48 41 17 67 47 03 68 20 90 98 32 04 40 68 60 | 90 51 58 60 06 55 23 68 05 19 76 94 82 36 96 43 38 90 87 28 33 83 05 17 70 83 96 93 06 04 78 47 80 06 23 84 75 23 87 72 99 14 50 98 92 38 90 64 61 58 76 94 36 66 87 80 51 35 61 38 61 | 57 95 64 06 53 36 82 51 40 33 47 14 07 98 78 65 39 58 53 06 50 53 04 69 40 68 36 69 75 78 75 60 03 32 39 24 74 47 26 90 13 40 44 71 90 76 51 24 36 50 25 45 70 80 61 80 61 43 90 64 11 62 | 18 29 86 56 68 42 79 10 42 44 30 12 96 18 23 18 52 59 02 99 67 46 60 86 43 38 55 17 44 93 42 21 55 14 47 34 55 16 49 24 23 29 96 51 55 10 46 53 27 92 27 46 63 57 30 65 43 27 21 20 24 83 63 | 81 72 93 19 69 52 48 01 13 83 92 69 20 48 69 59 20 62 05 42 28 89 90 99 32 72 84 17 08 87 36 03 60 31 36 36 81 26 97 36 48 54 56 56 27 16 91 08 23 11 87 99 33 47 02 14 44 73 70 99 43 35 33 64 | 90 56 61 86 56 12 70 59 63 32 01 15 81 47 71 76 95 32 65 80 54 70 34 51 40 45 33 04 64 55 78 68 88 47 31 47 68 87 03 84 23 44 89 72 35 08 31 76 63 26 90 85 96 67 65 91 19 14 17 86 04 71 32 95 65 | 37 13 04 22 64 37 37 28 56 62 86 33 07 37 10 44 52 82 52 06 19 52 57 75 90 26 91 24 06 21 14 67 76 30 46 14 35 89 89 41 03 64 56 97 87 63 22 34 03 79 17 45 11 53 25 56 96 61 23 18 63 31 37 37 47 66 | 77 23 26 70 72 76 77 04 28 64 71 69 14 85 96 54 95 48 06 62 99 83 86 77 97 75 71 66 30 19 57 90 33 01 60 61 14 12 90 99 32 77 56 41 18 14 87 49 10 14 90 64 18 50 21 74 14 16 88 05 45 73 82 47 74 44 67 | 22 97 41 13 34 31 54 61 56 94 03 24 59 27 98 77 04 09 37 40 12 26 87 09 71 70 07 18 64 57 80 21 12 71 83 94 60 39 73 79 73 19 97 32 64 29 41 07 48 84 85 67 12 74 95 20 24 52 41 67 56 61 29 93 35 72 69 68 | 72 23 63 66 01 11 07 30 52 56 95 16 65 26 83 90 50 74 60 18 16 48 43 77 37 11 99 98 30 94 91 26 62 73 45 12 87 73 47 27 01 88 66 99 21 41 95 80 02 53 23 32 61 48 32 43 43 83 14 66 95 91 19 81 80 67 25 88 69 | 08 62 32 18 92 14 83 71 37 96 11 83 39 99 05 16 23 27 10 67 02 25 44 11 55 31 46 64 41 56 44 74 26 81 51 31 45 85 87 09 81 95 22 28 76 69 46 48 64 87 67 76 27 89 31 11 74 16 62 03 60 94 42 47 09 34 94 93 72 70 | 56 18 90 18 42 17 42 32 14 86 06 53 33 95 99 35 29 15 44 20 49 59 25 54 34 59 84 21 23 54 35 90 78 16 93 13 37 88 54 19 86 67 68 55 66 84 65 42 98 37 87 56 33 28 58 38 28 38 66 27 52 21 81 15 08 22 97 32 85 27 71 | 91 53 40 28 13 34 91 25 01 63 50 37 22 49 71 58 32 28 30 18 68 94 23 83 63 62 94 76 80 41 90 22 82 52 29 12 18 56 10 08 35 14 37 57 23 65 67 40 72 39 93 39 70 89 40 34 07 46 94 22 20 05 53 64 56 30 05 56 61 88 27 72 | 23 95 11 12 37 69 68 24 66 10 87 70 43 50 75 07 62 41 83 58 95 93 89 79 45 39 02 22 05 22 95 43 62 11 68 29 17 40 26 44 25 71 87 16 70 85 19 25 59 94 90 41 41 80 61 70 55 60 84 33 95 76 42 63 15 09 03 40 38 12 03 32 73 | 09 84 56 80 61 55 85 97 16 94 82 94 98 57 84 30 84 48 93 90 71 05 95 90 73 17 30 98 40 64 65 89 07 79 09 19 56 36 42 30 23 69 73 72 07 05 27 61 24 31 43 48 71 84 21 28 26 65 65 59 65 74 77 20 10 81 61 84 95 08 52 23 70 74 | 47 81 28 09 98 51 67 64 35 51 59 36 92 82 77 65 80 24 72 53 22 07 27 10 21 28 30 22 48 82 80 48 56 20 14 43 18 25 50 95 90 31 77 08 09 48 44 80 90 22 93 45 82 17 13 96 25 26 08 73 34 99 06 49 24 06 83 51 40 14 15 10 25 01 75 | 54 25 10 81 30 64 24 74 75 80 36 75 82 60 22 69 72 91 45 67 03 62 79 54 89 74 44 83 64 96 66 73 44 30 74 50 37 05 09 97 70 01 60 46 37 91 39 75 75 18 58 52 72 78 51 81 86 52 08 97 01 46 43 66 98 62 81 18 70 93 73 08 32 46 34 76 | 96 80 82 07 59 71 92 53 19 20 88 66 03 26 26 10 24 27 50 82 94 73 63 08 51 33 22 45 19 13 58 33 90 15 22 50 36 13 55 06 35 47 82 52 33 61 36 27 28 46 98 14 73 20 73 32 16 26 80 53 47 66 76 38 94 45 02 01 22 52 47 96 64 58 52 39 77 | 88 46 23 39 74 63 81 64 20 90 33 33 76 55 58 26 10 46 42 26 74 74 12 83 32 43 09 02 73 55 86 54 85 34 28 23 29 79 91 62 47 41 82 87 99 22 48 90 20 05 96 75 95 04 43 28 81 39 81 01 28 42 78 25 39 77 90 57 58 98 17 36 73 22 63 74 51 78 | 29 39 74 94 95 78 64 24 38 86 63 87 93 06 70 92 22 16 80 64 29 52 20 27 23 50 14 13 87 15 72 96 81 22 08 49 72 30 70 24 79 31 16 64 59 21 89 34 96 91 48 76 43 53 88 01 57 80 23 81 90 79 58 01 80 87 17 99 86 90 72 63 32 69 14 28 88 69 79 | 37 17 71 95 56 93 71 35 43 45 04 98 92 94 84 96 11 30 31 27 31 60 92 03 48 05 98 91 86 94 35 90 90 08 48 19 33 28 68 37 59 26 65 96 50 68 22 07 09 49 34 31 77 49 43 06 75 17 81 87 61 79 52 26 27 72 29 50 07 98 86 01 17 10 46 64 24 18 56 80 | 51 30 25 94 88 85 79 91 40 33 63 84 49 67 98 92 15 26 75 19 82 05 18 78 65 93 61 48 91 43 59 41 70 51 22 15 92 81 67 91 46 98 11 11 65 31 66 10 98 65 83 21 05 56 05 98 73 67 46 74 69 34 08 30 05 52 07 98 32 95 30 94 65 50 24 63 28 81 99 57 81 | 19 23 61 36 09 89 71 98 65 17 30 29 89 26 79 74 94 11 44 48 97 54 81 55 39 66 69 45 28 47 13 86 15 76 74 70 84 32 36 33 79 20 78 14 41 47 89 28 81 05 99 66 81 86 38 26 06 25 13 60 54 55 23 53 27 05 89 25 23 11 13 54 59 54 56 34 16 24 53 44 06 82 | 13 40 57 72 21 15 60 08 04 19 11 98 34 45 09 97 86 71 03 15 56 19 15 44 97 31 90 04 87 87 76 08 12 30 24 62 84 28 12 85 82 53 99 52 13 94 06 65 97 86 09 50 94 68 69 74 30 67 87 94 63 07 78 27 80 36 69 41 06 92 32 78 37 82 30 05 18 87 99 72 19 99 83 | 44 20 55 77 69 91 27 31 28 81 80 27 02 07 97 23 95 98 12 25 75 29 47 71 07 47 78 39 41 59 27 76 13 15 66 61 68 35 69 86 16 53 67 63 99 85 41 56 08 28 33 40 94 76 90 85 31 70 24 65 84 65 99 82 19 25 54 37 21 46 33 02 52 99 51 33 26 04 87 02 08 18 96 84 | 54 42 61 45 91 06 64 79 80 82 32 16 83 63 42 49 19 78 65 97 40 42 14 61 49 34 04 18 25 98 59 30 82 72 26 88 54 36 21 75 03 88 99 53 46 51 55 78 22 94 34 40 68 87 84 25 30 76 25 08 92 84 42 61 40 38 09 99 40 23 29 39 46 55 10 90 35 84 56 70 63 23 91 39 85 | 52 92 03 71 89 07 09 37 68 66 58 20 44 92 51 56 13 71 79 99 26 37 02 06 16 67 36 52 58 16 79 73 56 60 59 27 44 77 94 82 20 50 98 33 09 87 94 37 40 83 64 83 58 85 17 76 53 02 83 52 22 27 39 20 48 92 45 21 09 42 24 23 12 37 52 28 50 78 79 20 86 62 73 20 59 86 | 54 96 80 15 91 90 99 70 10 09 58 90 93 50 81 99 54 38 36 10 30 11 35 84 16 45 82 18 11 97 36 43 96 79 97 65 40 48 23 19 17 31 64 52 65 65 37 32 65 76 99 79 34 65 79 27 55 33 03 01 33 27 61 28 66 08 04 70 49 46 48 83 01 45 19 96 13 81 14 21 31 79 93 85 50 05 87 | 92 92 48 84 59 98 31 53 23 27 15 22 79 95 24 76 05 79 16 93 97 89 38 89 42 83 02 88 94 95 82 21 01 97 48 39 31 78 09 65 50 56 97 61 01 07 65 27 21 23 14 15 80 97 44 78 49 35 33 45 81 74 34 05 31 57 09 38 94 07 69 54 69 32 65 68 46 68 78 90 24 28 49 51 45 86 35 88 | 41 63 89 76 87 31 86 09 46 14 87 82 22 29 47 16 13 10 70 72 82 95 48 64 58 43 13 75 42 69 21 12 67 13 64 85 58 23 98 09 37 76 05 22 31 12 66 50 29 99 86 72 45 25 10 28 19 06 90 43 29 31 67 79 46 25 74 14 97 35 76 37 65 46 23 82 06 22 30 76 93 66 94 17 96 13 20 72 89 | 63 40 78 08 52 09 90 41 70 28 36 14 46 44 85 96 24 52 58 15 87 37 05 98 99 39 13 61 76 38 44 99 83 74 90 22 53 80 56 98 30 51 63 39 44 30 91 91 04 22 27 73 17 35 53 18 35 45 54 56 27 78 48 13 69 36 44 38 71 25 30 56 15 22 73 43 32 69 59 25 93 83 45 11 34 94 44 39 92 90 | 12 36 56 88 13 96 16 12 55 54 11 47 19 78 17 17 68 81 77 51 42 55 99 85 66 27 81 79 93 42 65 61 69 74 14 01 18 56 12 01 58 37 91 22 42 66 83 25 19 04 96 41 25 45 18 69 96 88 36 93 10 12 98 32 44 83 83 04 72 91 04 27 73 07 34 37 71 60 59 31 01 54 54 44 96 93 83 36 04 45 91 | 30 18 22 20 42 96 65 79 17 41 55 69 94 81 29 80 91 31 85 25 47 26 43 49 02 99 34 67 99 76 16 14 15 93 08 32 99 44 61 77 67 50 43 55 87 55 53 72 17 46 62 25 50 99 73 05 93 48 17 31 70 80 59 09 44 59 45 13 74 66 58 94 87 73 16 14 85 38 74 99 64 23 79 28 71 42 20 37 82 31 23 92 | 51 96 39 65 46 71 56 13 29 68 53 86 45 33 51 49 12 91 21 21 76 85 02 17 98 15 46 12 60 21 88 30 92 83 44 59 42 50 27 88 46 86 94 73 45 54 23 24 14 10 94 21 20 34 23 51 04 83 99 75 90 63 60 16 22 33 83 70 11 32 10 50 29 30 83 46 11 05 31 17 86 42 49 01 44 63 28 60 07 78 95 40 93 | 44 61 89 59 04 49 51 27 69 71 46 76 44 04 09 34 56 39 15 06 94 91 75 90 65 27 56 23 74 06 23 33 36 69 14 39 05 34 35 57 33 22 76 46 56 10 61 65 98 09 16 69 04 62 65 18 99 76 49 18 72 66 73 83 82 40 76 31 89 91 27 88 17 35 41 35 32 51 32 67 52 68 74 85 80 57 07 11 62 66 47 22 67 94 | 65 37 19 97 26 17 16 24 24 17 50 37 64 82 24 36 32 11 68 34 69 31 32 89 79 93 96 68 49 90 14 23 04 04 67 99 81 74 70 74 36 96 68 09 64 39 88 35 54 89 96 58 66 27 88 97 32 14 06 35 78 20 71 06 85 66 57 02 58 91 72 05 29 56 73 48 86 52 09 93 22 57 79 42 12 01 31 68 17 59 63 76 07 77 95 | 73 81 14 13 17 20 11 09 01 83 08 85 91 70 84 63 62 77 37 07 47 01 59 95 39 69 39 21 99 09 87 02 97 16 92 36 74 71 90 66 33 73 73 75 52 91 11 12 26 53 05 26 26 48 61 50 90 65 01 87 42 47 74 35 22 73 24 26 56 70 52 05 48 41 31 18 83 27 21 39 80 85 26 08 44 02 71 07 63 22 05 52 19 08 20 96 | 17 25 21 11 72 93 33 49 64 23 53 82 03 13 91 65 85 02 40 05 42 31 77 42 05 36 06 54 04 58 07 76 87 83 25 57 66 12 74 33 85 37 74 32 20 69 03 97 91 68 82 44 19 14 89 28 85 85 80 53 34 87 58 98 88 78 48 65 98 40 11 57 10 67 70 81 60 79 74 72 97 59 79 47 30 20 54 80 89 91 14 05 33 36 79 39 97 | 60 85 59 39 60 07 57 76 77 92 06 35 15 72 23 41 45 52 95 18 64 79 86 53 56 31 69 11 91 31 84 50 44 82 22 81 41 40 30 42 30 91 48 94 74 76 64 58 74 25 96 57 14 19 03 99 28 83 15 75 99 01 89 85 79 50 03 95 32 67 44 08 07 41 62 64 29 20 14 76 26 55 48 71 69 66 19 72 44 25 14 01 48 74 12 98 07 98 | 64 66 84 24 18 16 27 48 20 14 47 69 30 86 48 40 23 16 61 21 51 50 26 47 35 33 91 28 78 64 43 68 04 79 51 08 19 60 52 95 06 68 46 86 35 97 27 58 04 65 30 58 99 12 12 75 91 39 50 31 42 64 70 04 46 07 98 73 98 93 37 89 77 91 64 71 64 65 66 21 78 62 81 74 42 20 83 70 73 95 78 45 92 27 34 53 71 15 99 | 30 11 85 31 34 71 13 48 05 14 44 03 19 67 23 73 19 57 06 90 94 72 57 69 81 62 59 68 88 57 55 69 49 13 07 87 97 80 89 05 71 05 05 26 38 40 16 62 45 99 18 38 98 24 21 26 62 74 69 04 85 57 77 35 58 67 91 79 79 57 86 28 66 34 72 51 76 78 36 95 63 90 08 78 47 63 45 31 22 70 52 48 79 94 15 77 61 67 68 100 | 23 33 44 81 80 92 93 75 94 88 23 61 39 76 22 03 28 94 32 06 49 65 41 34 18 23 08 47 62 60 03 63 33 13 80 52 31 54 73 43 70 26 16 69 57 87 83 31 03 93 70 81 47 95 77 44 29 68 39 51 56 59 63 07 25 70 07 77 43 53 64 03 94 42 95 39 18 01 66 21 16 97 20 50 90 16 70 10 95 69 29 06 25 61 41 26 15 59 63 35 101 | -------------------------------------------------------------------------------- /graph_algorithms/data/directed.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 5 3 | A 4 | B 5 | C 6 | D 7 | E 8 | A B 5 9 | B C 4 10 | C D 8 11 | D C 8 12 | D E 6 13 | A D 5 14 | C E 2 15 | E B 3 16 | A E 7 -------------------------------------------------------------------------------- /graph_algorithms/data/directed2.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 16 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | I 12 | J 13 | K 14 | L 15 | M 16 | N 17 | O 18 | P 19 | A B 1 20 | A C 1 21 | B D 1 22 | B E 1 23 | C F 1 24 | C G 1 25 | D H 1 26 | D I 1 27 | E J 1 28 | E K 1 29 | F L 1 30 | F M 1 31 | G N 1 32 | G O 1 33 | L P 1 34 | E I 1 35 | I P 1 -------------------------------------------------------------------------------- /graph_algorithms/data/directed3.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 5 3 | A 4 | B 5 | C 6 | D 7 | E 8 | A B 1 9 | B C 1 10 | C D 1 11 | D C 1 12 | D E 1 13 | A D 1 14 | C E 1 15 | E B 1 16 | A E 1 -------------------------------------------------------------------------------- /graph_algorithms/data/directed4.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 8 3 | 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 0 1 12 | 1 2 13 | 2 0 14 | 2 3 15 | 2 5 16 | 3 4 17 | 3 6 18 | 4 6 19 | 6 3 20 | 5 7 -------------------------------------------------------------------------------- /graph_algorithms/data/directed_matrix.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 5 3 | P1 4 | P2 5 | P3 6 | P4 7 | P5 8 | P1 P2 1 9 | P1 P4 1 10 | P1 P5 1 11 | P2 P1 1 12 | P2 P3 1 13 | P2 P4 1 14 | P3 P4 1 15 | P3 P5 1 16 | P4 P1 1 17 | P4 P3 1 18 | P4 P5 1 -------------------------------------------------------------------------------- /graph_algorithms/data/directed_p1.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 8 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | A E 12 | A F 13 | B C 14 | B A 15 | C H 16 | D H 17 | D C 18 | F E 19 | F B 20 | F G 21 | G B 22 | G C 23 | H G -------------------------------------------------------------------------------- /graph_algorithms/data/directed_p2.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 8 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | B A 1 12 | C B 1 13 | C D 1 14 | E F 1 15 | E A 1 16 | F G 1 17 | F B 1 18 | F A 1 19 | G C 1 20 | G H 1 21 | G B 1 22 | H C 1 23 | H D 1 -------------------------------------------------------------------------------- /graph_algorithms/data/directed_p3.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 10 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | I 12 | J 13 | A B 1 14 | B G 1 15 | C H 1 16 | C I 1 17 | C B 1 18 | C D 1 19 | D E 1 20 | E J 1 21 | F B 1 22 | F A 1 23 | G C 1 24 | G H 1 25 | G F 1 26 | H I 1 27 | I D 1 28 | J I 1 29 | J D 1 -------------------------------------------------------------------------------- /graph_algorithms/data/directed_scc.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 8 3 | a 4 | b 5 | c 6 | d 7 | e 8 | f 9 | g 10 | h 11 | a b 1 12 | e a 1 13 | b c 1 14 | b e 1 15 | b f 1 16 | c d 1 17 | c g 1 18 | d c 1 19 | d h 1 20 | h d 1 21 | h g 1 22 | e f 1 23 | f g 1 24 | g f 1 -------------------------------------------------------------------------------- /graph_algorithms/data/kosaraju.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 13 3 | 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 3 2 17 | 4 3 18 | 4 2 19 | 2 0 20 | 0 1 21 | 6 0 22 | 10 12 23 | 7 9 24 | 9 10 25 | 9 11 26 | 12 9 27 | 11 12 28 | 5 4 29 | 2 3 30 | 0 5 31 | 6 8 32 | 7 6 33 | 11 4 34 | 3 5 35 | 6 4 36 | 6 9 37 | 8 6 -------------------------------------------------------------------------------- /graph_algorithms/data/tinyDAG.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 13 3 | 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 2 3 17 | 0 6 18 | 0 1 19 | 2 0 20 | 11 12 21 | 9 12 22 | 9 10 23 | 9 11 24 | 3 5 25 | 8 7 26 | 5 4 27 | 0 5 28 | 6 4 29 | 6 9 30 | 7 6 -------------------------------------------------------------------------------- /graph_algorithms/data/tinyDG.txt: -------------------------------------------------------------------------------- 1 | directed 2 | 13 3 | 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 4 2 17 | 2 3 18 | 3 2 19 | 6 0 20 | 0 1 21 | 2 0 22 | 11 12 23 | 12 9 24 | 9 10 25 | 9 11 26 | 7 9 27 | 10 12 28 | 11 4 29 | 4 3 30 | 3 5 31 | 6 8 32 | 8 6 33 | 5 4 34 | 0 5 35 | 6 4 36 | 6 9 37 | 7 6 -------------------------------------------------------------------------------- /graph_algorithms/data/tinyG.txt: -------------------------------------------------------------------------------- 1 | undirected 2 | 13 3 | 0 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | 10 14 | 11 15 | 12 16 | 0 5 17 | 4 3 18 | 0 1 19 | 9 12 20 | 6 4 21 | 5 4 22 | 0 2 23 | 11 12 24 | 9 10 25 | 0 6 26 | 7 8 27 | 9 11 28 | 5 3 -------------------------------------------------------------------------------- /graph_algorithms/data/undirected.txt: -------------------------------------------------------------------------------- 1 | undirected 2 | 8 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | A B 7 12 | A C 8 13 | B F 2 14 | C F 6 15 | C G 4 16 | E H 1 17 | F D 8 18 | F G 9 19 | F H 3 -------------------------------------------------------------------------------- /graph_algorithms/data/undirected_p21.txt: -------------------------------------------------------------------------------- 1 | undirected 2 | 8 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | A F 12 | A E 13 | B F 14 | B C 15 | C F 16 | C G 17 | C H 18 | D H 19 | G H -------------------------------------------------------------------------------- /graph_algorithms/data/undirected_p22.txt: -------------------------------------------------------------------------------- 1 | undirected 2 | 8 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | A B 12 | B C 13 | B F 14 | B E 15 | C F 16 | C G 17 | C D 18 | D H 19 | E F 20 | G H -------------------------------------------------------------------------------- /graph_algorithms/data/undirected_p23.txt: -------------------------------------------------------------------------------- 1 | undirected 2 | 10 3 | A 4 | B 5 | C 6 | D 7 | E 8 | F 9 | G 10 | H 11 | I 12 | J 13 | A B 14 | A F 15 | B A 16 | B G 17 | B F 18 | C H 19 | D J 20 | D I 21 | D E 22 | E J 23 | F G 24 | I J -------------------------------------------------------------------------------- /graph_algorithms/lib/neighbor.rb: -------------------------------------------------------------------------------- 1 | class Neighbor 2 | attr_reader :name, :weight, :next 3 | 4 | def initialize(vnum, neighbor, weight, name) 5 | @vnum = vnum 6 | @next = neighbor 7 | @weight = weight 8 | @name = name 9 | end 10 | end -------------------------------------------------------------------------------- /graph_algorithms/lib/priority_queue.rb: -------------------------------------------------------------------------------- 1 | # See http://www.ruby-doc.org/stdlib-2.0/libdoc/forwardable/rdoc/Forwardable.html 2 | require 'forwardable' 3 | 4 | class PriorityQueue 5 | extend Forwardable 6 | attr_reader :queue 7 | 8 | def initialize 9 | @queue = {} 10 | end 11 | 12 | def_delegators :queue, :size, :empty?, :[], :[]= 13 | def_delegator :queue, :has_key?, :contains? 14 | 15 | def enq(key, value = nil) 16 | queue[key] = value # key = vertex name, value = total distance 17 | end 18 | 19 | def deq 20 | return nil if queue.empty? 21 | minvalue = queue.values.min 22 | minkey = queue.key(minvalue) 23 | queue.delete(minkey) 24 | {minkey => minvalue} 25 | # Hash[*queue.shift] <- use if want normal (not priority) queue 26 | end 27 | 28 | def pop 29 | return nil if queue.empty? 30 | last_key = queue.keys.last 31 | last_value = queue[last_key] 32 | queue.delete(last_key) 33 | {last_key => last_value} 34 | end 35 | end -------------------------------------------------------------------------------- /graph_algorithms/lib/vertex.rb: -------------------------------------------------------------------------------- 1 | class Vertex 2 | attr_reader :name 3 | attr_accessor :adj_list 4 | 5 | def initialize(name, neighbors) 6 | @name = name 7 | @adj_list = neighbors 8 | end 9 | end -------------------------------------------------------------------------------- /graph_algorithms/spec/graph_spec.rb: -------------------------------------------------------------------------------- 1 | # By Raymond Gan 2 | # See "rspec_screen_output.txt" for RSpec results 3 | 4 | require 'spec_helper' 5 | 6 | describe Graph do 7 | let(:g) {Graph.new("./data/directed.txt")} 8 | let(:h) {Graph.new("./data/undirected.txt")} 9 | let(:j) {Graph.new("./data/directed2.txt")} 10 | let(:k) {Graph.new("./data/directed_matrix.txt")} 11 | let(:scc) {Graph.new("./data/directed_scc.txt")} 12 | let(:m) {Graph.new("./data/directed3.txt")} 13 | let(:n) {Graph.new("./data/directed4.txt")} 14 | let(:p1) {Graph.new("./data/directed_p1.txt")} 15 | let(:p2) {Graph.new("./data/directed_p2.txt")} 16 | let(:p3) {Graph.new("./data/directed_p3.txt")} 17 | let(:ks) {Graph.new("./data/kosaraju.txt")} 18 | let(:ks2) {Graph.new("./data/tinyDAG.txt")} 19 | let(:ks3) {Graph.new("./data/tinyDG.txt")} 20 | let(:p23) {Graph.new("./data/undirected_p23.txt")} 21 | let(:cc1) {Graph.new("./data/tinyG.txt")} 22 | let(:p22) {Graph.new("./data/undirected_p22.txt")} 23 | let(:p21) {Graph.new("./data/undirected_p21.txt")} 24 | 25 | let(:graphfile_g) {["directed", "5", "A", "B", "C", "D", "E", 26 | "A B 5", 27 | "B C 4", 28 | "C D 8", 29 | "D C 8", 30 | "D E 6", 31 | "A D 5", 32 | "C E 2", 33 | "E B 3", 34 | "A E 7"]} 35 | 36 | let(:graphfile_h) {["undirected", "8", "A", "B", "C", "D", "E", "F", "G", "H", 37 | "A B 7", 38 | "A C 8", 39 | "B F 2", 40 | "C F 6", 41 | "C G 4", 42 | "E H 1", 43 | "F D 8", 44 | "F G 9", 45 | "F H 3"]} 46 | 47 | # checks both directed and undirected graphs 48 | describe "#read_file" do 49 | it "reads graph input file as an array" do 50 | expect(g.read_file("./data/directed.txt")).to eq(graphfile_g) 51 | expect(h.read_file("./data/undirected.txt")).to eq(graphfile_h) 52 | end 53 | end 54 | 55 | describe "#adj_matrix" do 56 | it "creates adjacency matrix from graph input file" do 57 | matrix_g = Matrix[[0, 1, 0, 1, 1], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1], [0, 0, 1, 0, 1], [0, 1, 0, 0, 0]] 58 | matrix_k = Matrix[[0, 1, 0, 1, 1], [1, 0, 1, 1, 0], [0, 0, 0, 1, 1], [1, 0, 1, 0, 1], [0, 0, 0, 0, 0]] 59 | expect(g.adj_matrix).to eq(matrix_g) 60 | expect(k.adj_matrix).to eq(matrix_k) 61 | end 62 | end 63 | 64 | describe "#read_vertices" do 65 | it "returns array of Vertex objects---adjacency list" do 66 | adj_lists = g.read_vertices 67 | expect(adj_lists[3].instance_of?(Vertex)).to eq(true) 68 | expect(adj_lists[3].name).to eq('D') 69 | expect(adj_lists[3].adj_list).to be_nil 70 | expect(adj_lists.size).to eq(5) 71 | end 72 | end 73 | 74 | describe "#adj_vertices" do 75 | it "returns array of vertices pointing out from given vertex" do 76 | expect(g.adj_vertices('A', g.adj_lists)).to eq(['E', 'D', 'B']) 77 | expect(g.adj_vertices('B', g.adj_lists)).to eq(['C']) 78 | expect(g.adj_vertices('C', g.adj_lists)).to eq(['E', 'D']) 79 | expect(g.adj_vertices('D', g.adj_lists)).to eq(['E', 'C']) 80 | expect(g.adj_vertices('E', g.adj_lists)).to eq(['B']) 81 | end 82 | end 83 | 84 | describe "#inward_vertices" do 85 | it "returns array of vertices pointing into given vertex" do 86 | expect(g.inward_vertices('A', g.adj_lists)).to eq([]) 87 | expect(g.inward_vertices('B', g.adj_lists)).to eq(['A', 'E']) 88 | expect(g.inward_vertices('C', g.adj_lists)).to eq(['B', 'D']) 89 | expect(g.inward_vertices('D', g.adj_lists)).to eq(['A', 'C']) 90 | expect(g.inward_vertices('E', g.adj_lists)).to eq(['A', 'C', 'D']) 91 | end 92 | end 93 | 94 | describe "#read_edges" do 95 | it "returns final version of adjacency lists, containing Neighbor objects connected as linked lists" do 96 | adj_lists = g.read_edges(0, 1) 97 | # from input file, vertex A is connected to E, D, and B 98 | expect(adj_lists[0].adj_list.instance_of?(Neighbor)).to eq(true) 99 | expect(adj_lists[0].adj_list.name).to eq('E') 100 | expect(adj_lists[0].adj_list.weight).to eq(7) 101 | expect(adj_lists[0].adj_list.next.instance_of?(Neighbor)).to eq(true) 102 | expect(adj_lists[0].adj_list.next.name).to eq('D') 103 | expect(adj_lists[0].adj_list.next.weight).to eq(5) 104 | expect(adj_lists[0].adj_list.next.next.name).to eq('B') 105 | expect(adj_lists[0].adj_list.next.next.weight).to eq(5) 106 | end 107 | end 108 | 109 | describe "#vertex_num_for" do 110 | it "finds vertex number, given vertex name" do 111 | expect(g.vertex_num_for('A')).to eq(0) 112 | expect(g.vertex_num_for('B')).to eq(1) 113 | expect(g.vertex_num_for('C')).to eq(2) 114 | expect(g.vertex_num_for('D')).to eq(3) 115 | expect(g.vertex_num_for('E')).to eq(4) 116 | end 117 | end 118 | 119 | describe "#copy_graph" do 120 | it "clones graph into cadj_lists, ca_matrix" do 121 | g.copy_graph 122 | expect(g.cadj_lists == g.adj_lists).to eq(true) 123 | expect(g.ca_matrix == g.a_matrix).to eq(true) 124 | end 125 | 126 | it "but object_ids are different" do 127 | expect(g.cadj_lists.object_id == g.adj_lists.object_id).to eq(false) 128 | expect(g.ca_matrix.object_id == g.a_matrix.object_id).to eq(false) 129 | end 130 | end 131 | 132 | describe "#reverse_graph" do 133 | it "reverses all edges in graph and makes copy of old graph" do 134 | g.reverse_graph 135 | expect(g.a_matrix).to eq(Matrix[[0, 0, 0, 0, 0], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [1, 0, 1, 0, 0], [1, 0, 1, 1, 0]]) 136 | expect(g.adj_lists[0].adj_list).to eq(nil) 137 | expect(g.adj_lists[4].adj_list.name).to eq('A') 138 | expect(g.adj_lists[4].adj_list.weight).to eq(7) 139 | expect(g.adj_lists[4].adj_list.next.instance_of?(Neighbor)).to eq(true) 140 | expect(g.adj_lists[4].adj_list.next.name).to eq('C') 141 | expect(g.adj_lists[4].adj_list.next.weight).to eq(2) 142 | expect(g.adj_lists[4].adj_list.next.next.name).to eq('D') 143 | expect(g.adj_lists[4].adj_list.next.next.weight).to eq(6) 144 | 145 | # makes copy of old graph 146 | expect(g.ca_matrix).to eq(Matrix[[0, 1, 0, 1, 1], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1], [0, 0, 1, 0, 1], [0, 1, 0, 0, 0]]) 147 | expect(g.cadj_lists[0].adj_list.name).to eq('E') 148 | expect(g.cadj_lists[0].adj_list.weight).to eq(7) 149 | expect(g.cadj_lists[0].adj_list.next.instance_of?(Neighbor)).to eq(true) 150 | expect(g.cadj_lists[0].adj_list.next.name).to eq('D') 151 | expect(g.cadj_lists[0].adj_list.next.weight).to eq(5) 152 | expect(g.cadj_lists[0].adj_list.next.next.name).to eq('B') 153 | expect(g.cadj_lists[0].adj_list.next.next.weight).to eq(5) 154 | end 155 | end 156 | 157 | describe "#rereverse_graph" do 158 | it "changes graph back to original, after reverse_graph" do 159 | expect(g.a_matrix).to eq(Matrix[[0, 1, 0, 1, 1], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1], [0, 0, 1, 0, 1], [0, 1, 0, 0, 0]]) 160 | expect(g.adj_lists[0].adj_list.name).to eq('E') 161 | 162 | g.reverse_graph 163 | expect(g.a_matrix).to eq(Matrix[[0, 0, 0, 0, 0], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [1, 0, 1, 0, 0], [1, 0, 1, 1, 0]]) 164 | expect(g.adj_lists[0].adj_list).to eq(nil) 165 | expect(g.adj_lists[4].adj_list.name).to eq('A') 166 | 167 | g.rereverse_graph 168 | expect(g.a_matrix).to eq(Matrix[[0, 1, 0, 1, 1], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1], [0, 0, 1, 0, 1], [0, 1, 0, 0, 0]]) 169 | expect(g.adj_lists[0].adj_list.name).to eq('E') 170 | end 171 | end 172 | 173 | describe "#print_graph" do 174 | context "for directed graph" do 175 | it "prints graph as an adjacency list" do 176 | output_g = capture_stdout do 177 | g.print_graph 178 | end 179 | 180 | output_g.should include("A --> E (7) --> D (5) --> B (5)") 181 | output_g.should include("B --> C (4)") 182 | output_g.should include("C --> E (2) --> D (8)") 183 | output_g.should include("D --> E (6) --> C (8)") 184 | output_g.should include("E --> B (3)") 185 | end 186 | end 187 | 188 | context "for undirected graph" do 189 | it "prints graph as an adjacency list" do 190 | output_h = capture_stdout do 191 | h.print_graph 192 | end 193 | 194 | output_h.should include("A --> C (8) --> B (7)") 195 | output_h.should include("B --> F (2) --> A (7)") 196 | output_h.should include("C --> G (4) --> F (6) --> A (8)") 197 | output_h.should include("D --> F (8)") 198 | output_h.should include("E --> H (1)") 199 | output_h.should include("F --> H (3) --> G (9) --> D (8) --> C (6) --> B (2)") 200 | output_h.should include("G --> F (9) --> C (4)") 201 | output_h.should include("H --> F (3) --> E (1)") 202 | end 203 | end 204 | end 205 | 206 | describe "#dist" do 207 | it "finds distances between vertices" do 208 | expect(g.dist('A', 'B', 'C')).to eq(9) 209 | expect(g.dist('A', 'D')).to eq(5) 210 | expect(g.dist('A', 'D', 'C')).to eq(13) 211 | expect(g.dist('A', 'E', 'B', 'C', 'D')).to eq(22) 212 | expect(g.dist('A', 'E', 'D')).to eq("NO SUCH ROUTE") 213 | end 214 | end 215 | 216 | describe "#path_length" do 217 | it "finds total distance of a path of vertices" do 218 | expect(g.path_length(%w[C D C E B C])).to eq(25) 219 | expect(g.path_length(%w[C D C])).to eq(16) 220 | expect(g.path_length(%w[C E B C])).to eq(9) 221 | expect(g.path_length(%w[C E B C D C])).to eq(25) 222 | expect(g.path_length(%w[C D C E B C])).to eq(25) 223 | expect(g.path_length(%w[C D E B C])).to eq(21) 224 | expect(g.path_length(%w[C E B C E B C])).to eq(18) 225 | expect(g.path_length(%w[C E B C E B C E B C])).to eq(27) 226 | end 227 | end 228 | 229 | # uses adjacency matrix way 230 | # k is graph (Fig. 15) from here: https://www.math.ucdavis.edu/~daddel/linear_algebra_appl/Applications/GraphTheory/GraphTheory_9_17/node9.html 231 | describe "#maxtrips" do 232 | it "finds number of trips between 2 vertices with given range of stops" do 233 | expect(g.maxtrips('C', 'C', 1, 3)).to eq(2) # for max of 3 stops 234 | expect(g.maxtrips('A', 'C', 4, 4)).to eq(3) # for exactly 4 stops 235 | expect(k.maxtrips('P2', 'P5', 3, 3)).to eq(4) # for example 3 stops 236 | expect(k.maxtrips('P3', 'P2', 1, 3)).to eq(1) # for max of 3 stops. UC Davis page has mistake at bottom. Answer is 1, not 2. 237 | expect(j.maxtrips('A', 'P', 4, 4)).to eq(3) # for exactly 3 stops 238 | end 239 | end 240 | 241 | describe "#shortest_length" do 242 | it "finds length of shortest distance between 2 vertices" do 243 | expect(g.shortest_length('A', 'C')).to eq(9) 244 | expect(g.shortest_length('B', 'B')).to eq(9) 245 | expect(g.shortest_length('C', 'C')).to eq(9) 246 | expect(h.shortest_length('A', 'E')).to eq(13) # undirected graph 247 | expect(m.shortest_length('C', 'C')).to eq(2) 248 | end 249 | end 250 | 251 | # finds shortest path between 2 vertices (fewest edges), using breadth-first search (BFS) 252 | describe "#fewest_edges" do 253 | it "finds shortest path between 2 vertices (fewest edges)" do 254 | expect(g.fewest_edges('A', 'C')).to eq(2) 255 | expect(g.fewest_edges('B', 'B')).to eq(3) 256 | expect(g.fewest_edges('C', 'C')).to eq(2) 257 | expect(p1.fewest_edges('A', 'H')).to eq(4) 258 | end 259 | end 260 | 261 | describe "#all_cycles" do 262 | # excludes self-loops (like A->A) 263 | it "finds nested array of all cycles in graph" do 264 | expect(g.all_cycles).to eq([["B", "C", "E"], ["B", "C", "D", "E"], ["D", "C"]]) 265 | expect(j.all_cycles).to eq("NO CYCLES EXIST") 266 | expect(n.all_cycles).to eq([["6", "3"], ["4", "6", "3"], ["1", "2", "0"]]) 267 | end 268 | end 269 | 270 | describe "#trips" do 271 | it "finds number of trips between 2 vertices with given range of stops" do 272 | expect(g.trips('C', 'C', 1, 3)).to eq(2) # original question 273 | expect(g.trips('A', 'C', 4, 4)).to eq(3) # original question 274 | 275 | expect(g.trips('A', 'C', 3, 3)).to eq(1) 276 | expect(g.trips('A', 'C', 2, 2)).to eq(2) 277 | expect(g.trips('A', 'C', 1, 1)).to eq(0) 278 | expect(g.trips('A', 'C', 1, 4)).to eq(6) # note 6 = 1 + 2 + 0 279 | 280 | expect(g.trips('A', 'B', 1, 1)).to eq(1) 281 | expect(j.trips('A', 'P', 4, 4)).to eq(3) 282 | expect(g.trips('B', 'B', 3, 4)).to eq(2) 283 | expect(g.trips('D', 'D', 2, 4)).to eq(3) 284 | expect(g.trips('E', 'E', 3, 4)).to eq(2) 285 | 286 | expect(g.trips('C', 'C', 9, 9)).to eq(2) 287 | expect(g.trips('C', 'C', 1, 9)).to eq(19) 288 | end 289 | end 290 | 291 | describe "#lesstrips" do 292 | it "finds number of trips between 2 vertices with distance < amount" do 293 | expect(g.lesstrips('C', 'C', 30)).to eq(7) 294 | end 295 | end 296 | 297 | describe "#dfs" do 298 | context "for normal graph" do 299 | it "shows preorder and reverse postorder of vertices visited by recursive DFS" do 300 | # preorder 301 | expect(ks2.dfs(ks2.adj_lists)[0]).to eq(["0", "5", "4", "1", "6", "9", "11", "12", "10", "2", "3", "7", "8"]) 302 | # reverse postorder 303 | expect(ks2.dfs(ks2.adj_lists)[1]).to eq(["8", "7", "2", "3", "0", "6", "9", "10", "11", "12", "1", "5", "4"]) 304 | end 305 | end 306 | 307 | context "for reversed graph" do 308 | it "shows preorder and reverse postorder of vertices visited by recursive DFS" do 309 | ks.reverse_graph 310 | # preorder (in Coursera lecture) 311 | expect(ks.dfs(ks.adj_lists)[0]).to eq(["0", "6", "8", "7", "2", "4", "11", "9", "12", "10", "5", "3", "1"]) 312 | # reverse 313 | expect(ks.dfs(ks.adj_lists)[1]).to eq(["1", "0", "2", "4", "5", "3", "11", "9", "12", "10", "6", "7", "8"]) 314 | end 315 | end 316 | end 317 | 318 | describe "#scc_tarjan" do 319 | it "finds nested array of all strongly connected components (SCCs) in graph" do 320 | # graph g has >= 1 cycle for [B, C, D, E] 321 | expect(g.scc_tarjan).to eq([["B", "C", "D", "E"], ["A"]]) 322 | # graph h is an undirected graph and thus full of cycles 323 | expect(h.scc_tarjan).to eq([["A", "B", "C", "D", "E", "F", "G", "H"]]) 324 | # tree j has 0 cycles: there's no component > size 1 325 | expect(j.scc_tarjan).to eq([["O"], ["N"], ["G"], ["M"], ["P"], ["L"], ["F"], ["C"], ["I"], ["K"], ["J"], ["E"], ["H"], ["D"], ["B"], ["A"]]) 326 | # graph k has >= 1 cycle for [P1, P2, P3, P4] 327 | expect(k.scc_tarjan).to eq([["P5"], ["P1", "P2", "P3", "P4"]]) 328 | 329 | # graph scc has 3 SCCs (and thus >= 3 cycles), shown here: 330 | # http://iwillgetthatjobatgoogle.tumblr.com/post/19060679724/finding-strongly-connected-components-of-graph 331 | # NOTE: all SCCs are found in REVERSE topological order. 332 | # If we reverse nested array, we get topological sort of the SCCs! 333 | expect(scc.scc_tarjan).to eq([["f", "g"], ["c", "d", "h"], ["a", "b", "e"]]) 334 | 335 | expect(m.scc_tarjan).to eq([["B", "C", "D", "E"], ["A"]]) 336 | expect(n.scc_tarjan).to eq([["7"], ["5"], ["3", "4", "6"], ["0", "1", "2"]]) 337 | expect(p1.scc_tarjan).to eq([["E"], ["A", "B", "C", "F", "G", "H"], ["D"]]) 338 | expect(p2.scc_tarjan).to eq([["A"], ["B"], ["D"], ["C"], ["H"], ["G"], ["F"], ["E"]]) 339 | expect(p3.scc_tarjan).to eq([["D", "E", "I", "J"], ["H"], ["A", "B", "C", "F", "G"]]) 340 | expect(ks.scc_tarjan).to eq([["1"], ["0", "2", "3", "4", "5"], ["10", "11", "12", "9"], ["6", "8"], ["7"]]) 341 | expect(ks2.scc_tarjan).to eq([["4"], ["5"], ["1"], ["12"], ["11"], ["10"], ["9"], ["6"], ["0"], ["3"], ["2"], ["7"], ["8"]]) 342 | expect(ks3.scc_tarjan).to eq([["1"], ["0", "2", "3", "4", "5"], ["10", "11", "12", "9"], ["6", "8"], ["7"]]) 343 | end 344 | end 345 | 346 | describe "#scc_kosaraju" do 347 | it "returns SCCs using Kosaraju 2-pass algorithm" do 348 | expect(ks.scc_kosaraju).to eq([["1"], ["0", "2", "3", "4", "5"], ["10", "11", "12", "9"], ["6", "8"], ["7"]]) 349 | expect(ks3.scc_kosaraju).to eq([["1"], ["0", "2", "3", "4", "5"], ["10", "11", "12", "9"], ["6", "8"], ["7"]]) 350 | expect(g.scc_kosaraju).to eq([["B", "C", "D", "E"], ["A"]]) 351 | expect(h.scc_kosaraju).to eq([["A", "B", "C", "D", "E", "F", "G", "H"]]) 352 | expect(j.scc_kosaraju).to eq([["P"], ["O"], ["N"], ["M"], ["L"], ["K"], ["J"], ["I"], ["H"], ["G"], ["F"], ["E"], ["D"], ["C"], ["B"], ["A"]]) 353 | expect(k.scc_kosaraju).to eq([["P5"], ["P1", "P2", "P3", "P4"]]) 354 | expect(scc.scc_kosaraju).to eq([["f", "g"], ["c", "d", "h"], ["a", "b", "e"]]) 355 | expect(m.scc_kosaraju).to eq([["B", "C", "D", "E"], ["A"]]) 356 | expect(n.scc_kosaraju).to eq([["7"], ["5"], ["3", "4", "6"], ["0", "1", "2"]]) 357 | expect(p1.scc_kosaraju).to eq([["E"], ["A", "B", "C", "F", "G", "H"], ["D"]]) 358 | expect(p2.scc_kosaraju).to eq([["D"], ["A"], ["B"], ["C"], ["H"], ["G"], ["F"], ["E"]]) 359 | expect(p3.scc_kosaraju).to eq([["D", "E", "I", "J"], ["H"], ["A", "B", "C", "F", "G"]]) 360 | expect(ks2.scc_kosaraju).to eq([["12"], ["11"], ["10"], ["9"], ["4"], ["5"], ["6"], ["7"], ["8"], ["3"], ["1"], ["0"], ["2"]]) 361 | end 362 | end 363 | 364 | describe "#cc" do 365 | it "finds all connected components (CCs) of a graph" do 366 | expect(p23.cc).to eq([["A", "B", "F", "G"], ["C", "H"], ["D", "E", "I", "J"]]) 367 | expect(cc1.cc).to eq([["0", "1", "2", "3", "4", "5", "6"], ["7", "8"], ["10", "11", "12", "9"]]) 368 | end 369 | end 370 | end 371 | -------------------------------------------------------------------------------- /graph_algorithms/spec/priority_queue_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe PriorityQueue do 4 | before do 5 | @p = PriorityQueue.new 6 | @p.enq('C', 10) 7 | @p.enq('Z', 5) 8 | @p.enq('D', 6) 9 | end 10 | 11 | describe '#enq' do 12 | it 'adds new item to queue' do 13 | @p.enq('B', 9) 14 | expect(@p.queue).to eq({'C' => 10, 'Z' => 5, 'D' => 6, 'B' => 9}) 15 | end 16 | end 17 | 18 | describe '#deq' do 19 | it 'returns item with smallest value from queue and deletes it from queue' do 20 | expect(@p.deq).to eq({'Z' => 5}) 21 | expect(@p.queue).to eq({'C' => 10, 'D' => 6}) 22 | expect(@p.deq).to eq({'D' => 6}) 23 | end 24 | 25 | it 'returns nil if queue is empty' do 26 | 3.times {@p.deq} 27 | expect(@p.deq).to be_nil 28 | end 29 | end 30 | 31 | describe '#pop' do 32 | it 'returns last item added to queue' do 33 | expect(@p.pop).to eq({'D' => 6}) 34 | end 35 | 36 | it 'returns nil if queue is empty' do 37 | 3.times {@p.deq} 38 | expect(@p.pop).to be_nil 39 | end 40 | end 41 | end -------------------------------------------------------------------------------- /graph_algorithms/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'neighbor' 2 | require 'vertex' 3 | require 'priority_queue' 4 | require 'matrix' 5 | require 'graph' 6 | require 'stringio' 7 | require 'pry' 8 | 9 | RSpec.configure do |config| 10 | config.treat_symbols_as_metadata_keys_with_true_values = true 11 | config.run_all_when_everything_filtered = true 12 | config.color_enabled = true 13 | config.order = 'default' 14 | end 15 | 16 | def capture_stdout(&blk) 17 | old = $stdout 18 | $stdout = fake = StringIO.new 19 | blk.call 20 | fake.string 21 | ensure 22 | $stdout = old 23 | end 24 | --------------------------------------------------------------------------------