Created
September 7, 2021 11:31
-
-
Save AgentAntelope/c3f7aa6acdf7bf8f14da0f0cdcdcc1af to your computer and use it in GitHub Desktop.
A quick hack to calculate rational frations where digits can be cancelled to correctly reduce the fraction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Array | |
# Subtract each passed value once: | |
# %w(1 2 3 1).subtract_once %w(1 1 2) # => ["3"] | |
# [ 1, 1, 2, 2, 3, 3, 4, 5 ].subtract_once([ 1, 2, 4 ]) => [1, 2, 3, 3, 5] | |
# Time complexity of O(n + m) | |
def subtract_once(values) | |
counts = values.inject(Hash.new(0)) { |h, v| h[v] += 1; h } | |
reject { |e| counts[e] -= 1 unless counts[e].zero? } | |
end | |
end | |
class Fractionator | |
attr_reader :max_denominator, :digits | |
def initialize(max_denominator = 10000) | |
@max_denominator = max_denominator | |
end | |
def leftover_digits(num_1, num_2) | |
num_1_digits = num_1.to_s.chars | |
num_2_digits = num_2.to_s.chars | |
digit_diff = num_1_digits.subtract_once(num_2_digits) | |
end | |
def share_some_digits?(numerator, denominator) | |
digit_diff = leftover_digits(numerator, denominator) | |
digit_diff.count > 0 && digit_diff.count < numerator.to_s.chars.count | |
end | |
def same_value?(numerator:, denominator:, reduced_fraction:) | |
Rational(numerator, denominator) == reduced_fraction | |
end | |
def calculate | |
denominator = 0 | |
while denominator < max_denominator | |
denominator += 1 | |
possible_numerators(denominator).each do |numerator| | |
possible_bad_reductions(denominator, numerator).each do |bad_denominator| | |
possible_bad_reductions(numerator, denominator).filter{|bn| bn < bad_denominator}.each do |bad_numerator| | |
# These are less impressive in base 10 | |
next if (numerator/bad_numerator % 10) == 0 && (denominator/bad_denominator % 10) == 0 | |
next if (bad_numerator % 11) == 0 || (bad_denominator % 11) == 0 | |
# puts "trying #{numerator}/#{denominator} => #{bad_numerator}/#{bad_denominator}" | |
next unless same_value?(numerator: numerator, denominator: denominator, reduced_fraction: Rational(bad_numerator, bad_denominator)) | |
next unless bad_numerator == leftover_digits(numerator, denominator).join.to_i | |
next unless bad_denominator == leftover_digits(denominator, numerator).join.to_i | |
puts "#{numerator}/#{denominator} cancels to #{bad_numerator}/#{bad_denominator}" | |
end | |
end | |
end | |
end | |
end | |
def possible_numerators(denominator) | |
(1..(denominator - 1)).filter do |numerator| | |
share_some_digits?(numerator, denominator) | |
end | |
end | |
def possible_bad_reductions(num_1, num_2) | |
[leftover_digits(num_1, num_2).join.to_i].reject(&:zero?) | |
end | |
end | |
Fractionator.new.calculate |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment