Last active
December 5, 2019 05:32
-
-
Save nikhgupta/23113f0f6dba6dd8381a4a4382a74f55 to your computer and use it in GitHub Desktop.
Math Problem Generator - Sample
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
#!/usr/bin/env ruby | |
# | |
# Problem Statement | |
# ================= | |
# 1a) Two fractions with different denominators (Minuend and Subtrahend). Choose | |
# from the following pairs of denominators: 2 and 3, 2 and 4, 2 and 5, 3 and 6, | |
# 3 and 9, 4 and 8, 5 and 10, 6 and 12. Difference is greater than 0. (i.e. | |
# 1/2-2/5) | |
# ================= | |
require 'optparse' | |
DENOM_PAIRS = [[2,3], [2,4], [2,5], [3,6], [3,9], [4,8], [5,10], [6,12]] | |
module ProblemGenerator | |
# Utility module containing simple maths functions/expressions | |
module Utility | |
def gcd(a, b) | |
b == 0 ? a : gcd(b, a.modulo(b)) | |
end | |
def reduce_fraction(a, b) | |
gcd = gcd(a, b) | |
[a, b].map{|i| i / gcd} | |
end | |
def irreducible?(a, b) | |
gcd(a, b) == 1 | |
end | |
end | |
# Problem generator for the current problem statement. | |
# | |
# Generates two fractions (optionally, irreducible or mixed fractions) in order, | |
# such that their difference is positive. | |
class Fractions1A | |
include Utility | |
attr_accessor :reduce, :mixed | |
# Initialize problem generator. | |
# | |
# If `reduce` is set to true, only irreducible fractions will be generated. | |
# Irreducible fractions are fractions where numerator and denominator are | |
# co-prime. | |
# | |
# If `mixed` is set to a numeric value greater than 1, mixed fractions will | |
# be generated such that the rational number is less than `mixed` value. | |
# Setting `mixed` to `1` (default) will generate proper fractions. | |
# | |
def initialize(reduce: true, mixed: false) | |
self.mixed = mixed | |
self.reduce = reduce | |
end | |
def mixed=(mixed) | |
@mixed = mixed && mixed.to_i.positive? ? mixed : 1 | |
end | |
# Generate a problem statement. | |
# | |
# If `display` is true, problem statement will be printed. We might want to | |
# generate several problem statements at once, wherein setting `display` to | |
# false will help. | |
def generate(display: true) | |
pick_denominators | |
generate_minuend | |
generate_subtrahend | |
puts self if display | |
[[@n_min, @d_min], [@n_sub, @d_sub], @res] | |
end | |
def inspect | |
"#{@n_min}/#{@d_min} - #{@n_sub}/#{@d_sub} = #{@res[0]}/#{@res[1]}" | |
end | |
alias to_s inspect | |
protected | |
# Pick denominators, at random, from the given pairs. | |
def pick_denominators | |
@d_min, @d_sub = DENOM_PAIRS.sample | |
end | |
# Generate numerator for the minuend. We pick a numerator at random, | |
# such that either the minuend is irreducible, or we are not concerned with | |
# irreducible fractions (in which case, any numerator picked would work). | |
# | |
# Since, 2 consecutive numbers are always co-prime, numerator will be set | |
# via this method, always. | |
def generate_minuend | |
loop do | |
@n_min = 1 + rand(max_numerator_for(@d_min)) | |
break if !reduce || irreducible?(@n_min, @d_min) | |
end | |
end | |
# Generate the numerator for subtrahend. We shuffle available numerators, | |
# and stop at the first match which results in a valid value. | |
def generate_subtrahend | |
val = [] | |
@n_sub = (1..max_numerator_for(@d_sub)).to_a.shuffle.detect do |i| | |
next if @reduce && !irreducible?(i, @d_sub) | |
val = reduce_fraction(@n_min * @d_sub - @d_min * i, @d_min * @d_sub) | |
valid_resulting_fraction?(*val) | |
end | |
@res = val | |
end | |
private | |
def max_numerator_for(n) | |
@mixed * n - 1 | |
end | |
def valid_resulting_fraction?(a, b) | |
return false unless a.positive? && b.positive? | |
return false if @mixed == 1 && a >= b | |
return false if @reduce && !irreducible?(a, b) | |
return false if gcd(a, b) == b | |
true | |
end | |
end | |
end | |
options = {} | |
OptionParser.new do |opts| | |
opts.banner = "Usage: #{$0} [options]" | |
opts.on("-r", "--[no-]reduce", "Generate irreducible fractions only? [Default: True]") do |r| | |
options[:reduce] = r | |
end | |
opts.on("-mVAL", "--mixed=VAL", "Max value of generated fractions (Default: 1)") do |m| | |
options[:mixed] = m.to_i | |
end | |
opts.on("-nNUM", "--count=NUM", "Number of problem statements to generate (Default: 10)") do |n| | |
options[:count] = n.to_i | |
end | |
end.parse! | |
count = options.delete(:count) | |
count = 10 unless count.to_i.positive? | |
generator = ProblemGenerator::Fractions1A.new(options) | |
count.times.map { generator.generate } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment